dosgame1/audio/AudioPlayer.cpp

178 lines
4.5 KiB
C++

#include "AudioPlayer.h"
#include "../system/Opl.h"
#include "../system/Timer.h"
#include "../util/Log.h"
AudioPlayer audioPlayer;
void radPlayerWriteReg(void *p, uint16_t reg, uint8_t data) {
auto player = static_cast<AudioPlayer *>(p);
player->writeOpl(reg, data);
}
void audioPlayerOnTimer() {
audioPlayer.onTimer();
}
void audioPlayerOnTimerDelayed() {
static int idle = 10;
if (--idle) return;
Timer::instance().setCallback(audioPlayerOnTimer);
}
AudioPlayer::AudioPlayer() {
Timer::instance().setCallback(audioPlayerOnTimerDelayed);
}
AudioPlayer::~AudioPlayer() {
Timer::instance().setCallback(nullptr);
}
void AudioPlayer::playMusic(Music &music) {
stopMusic();
_currentMusic = &music;
_musicPlayer.Init(_currentMusic->getData(), radPlayerWriteReg, this);
auto hz = _musicPlayer.GetHertz();
if (hz < 0) {
stopMusic();
return;
}
// Start music playback
Timer::instance().setFrequency(hz);
resumeMusic();
}
void AudioPlayer::stopMusic() {
if (!_currentMusic) return;
_musicPlayer.Stop();
_paused = true;
_currentMusic = nullptr;
}
void AudioPlayer::pauseMusic() {
_paused = true;
}
void AudioPlayer::resumeMusic() {
if (!_currentMusic) return;
_paused = false;
}
void AudioPlayer::playAudio(Audio &audio, unsigned priority, int flags) {
if (priority > AUDIO_MAX_PRIORITY) priority = AUDIO_MAX_PRIORITY;
_channelMutex.lock();
if (flags & AUDIO_PLAY_FIXED_PRIORITY) {
if (_channels[priority].audio) {
_channels[priority].audio = 0;
}
_channels[priority].audio = &audio;
_channels[priority].loop = flags & AUDIO_PLAY_LOOP;
_channels[priority].pos = 0;
} else {
size_t shortestPriority = 0;
size_t remainingShortest = -1;
for (auto i = 0; i < priority; i++) {
if (!_channels[i].audio) {
_channels[i].audio = &audio;
_channels[i].loop = flags & AUDIO_PLAY_LOOP;
_channels[i].pos = 0;
_channelMutex.unlock();
return;
}
if (!_channels[i].loop) {
auto remaining = _channels[i].audio->_length - _channels[i].pos;
if (remaining < remainingShortest) {
shortestPriority = i;
remainingShortest = remaining;
}
}
}
if (_channels[shortestPriority].audio) {
_channels[shortestPriority].audio = 0;
}
_channels[shortestPriority].audio = &audio;
_channels[shortestPriority].loop = flags & AUDIO_PLAY_LOOP;
_channels[shortestPriority].pos = 0;
}
_channelMutex.unlock();
}
bool AudioPlayer::isPlaying(const Audio &audio) {
for (auto &channel : _channels) {
if (channel.audio == &audio) return true;
}
return false;
}
void AudioPlayer::stopAudio(Audio &audio) {
_channelMutex.lock();
for (auto &channel : _channels) {
if (channel.audio == &audio) {
channel.audio = nullptr;
channel.loop = false;
}
}
_channelMutex.unlock();
}
void AudioPlayer::stopAllAudio() {
_channelMutex.lock();
for (auto &channel : _channels) {
channel.audio = nullptr;
channel.loop = false;
}
_channelMutex.unlock();
}
void AudioPlayer::generateSamples(int8_t *buffer, size_t size) {
_channelMutex.lock();
for (size_t i = 0; i < size; i++) {
int16_t accum = 0;
for (auto &channel : _channels) {
if (channel.audio) {
auto sample = channel.audio->_data[channel.pos++];
if (channel.pos >= channel.audio->_length) {
if (channel.loop) {
channel.pos = 0;
} else {
channel.audio = nullptr;
}
}
accum += sample;
}
}
buffer[i] = (accum < 0) ? -((-accum) >> 3) : (accum >> 3);
}
_channelMutex.unlock();
}
bool AudioPlayer::isPlaying(const Music &music) const {
return _currentMusic == &music;
}
void AudioPlayer::writeOpl(uint16_t reg, uint8_t data) const {
Opl::write(reg, data);
}
void AudioPlayer::onTimer() {
if (_paused) return;
if (!_currentMusic) return;
_musicPlayer.Update();
}