178 lines
4.5 KiB
C++
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();
|
|
}
|
|
|