#include "sdl-speaker.h" #include #include extern "C" { #include #include }; #include "globals.h" #include "timeutil.h" // FIXME: Globals; ick. static volatile uint32_t bufIdx = 0; static uint8_t soundBuf[44100]; // 1 second of audio static pthread_mutex_t sndmutex = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t togmutex = PTHREAD_MUTEX_INITIALIZER; static void audioCallback(void *unused, Uint8 *stream, int len) { pthread_mutex_lock(&sndmutex); if (g_biosInterrupt) { // While the BIOS is running, we don't put samples in the audio // queue. memset(stream, 0, len); pthread_mutex_unlock(&sndmutex); return; } if (bufIdx >= len) { memcpy(stream, soundBuf, len); if (bufIdx > len) { // move the remaining data down memcpy(soundBuf, &soundBuf[len], bufIdx - len + 1); bufIdx -= len; } } else { // Audio underrun static uint8_t occurrenceCount = 0; if (++occurrenceCount < 10) { printf("Audio underrun!\n"); if (occurrenceCount == 9) { printf(" (Suppressing further audio errors)\n"); } } memset(stream, 0, len); } pthread_mutex_unlock(&sndmutex); } void ResetDCFilter(); // FIXME: remove SDLSpeaker::SDLSpeaker() { toggleState = false; mixerValue = 0x80; toggleCount = toggleReadPtr = toggleWritePtr = 0; pthread_mutex_init(&togmutex, NULL); pthread_mutex_init(&sndmutex, NULL); _init_darwin_shim(); ResetDCFilter(); lastCycleCount = 0; lastSampleCount = 0; SDL_AudioSpec audioDevice; SDL_AudioSpec audioActual; SDL_memset(&audioDevice, 0, sizeof(audioDevice)); audioDevice.freq = 44100; audioDevice.format = AUDIO_U8; audioDevice.channels = 1; audioDevice.samples = 4096; // 4096 bytes @ 44100Hz is about 1/10th second out of sync - should be okay for this testing audioDevice.callback = audioCallback; audioDevice.userdata = NULL; SDL_OpenAudio(&audioDevice, &audioActual); // FIXME retval printf("Actual: freq %d channels %d samples %d\n", audioActual.freq, audioActual.channels, audioActual.samples); SDL_PauseAudio(0); } SDLSpeaker::~SDLSpeaker() { } void SDLSpeaker::toggle(uint32_t c) { pthread_mutex_lock(&togmutex); toggleTimes[toggleWritePtr] = c; if (toggleCount < SPEAKERQUEUESIZE-1) { toggleWritePtr++; if (toggleWritePtr >= SPEAKERQUEUESIZE) toggleWritePtr = 0; toggleCount++; } else { printf("speaker overflow @ cycle %d\n", c); for (int i=0; i= 32768) { dcFilterState--; return in; } return ( (int32_t)in * (int32_t)dcFilterState-- ) / (int32_t)32768; } void SDLSpeaker::maintainSpeaker(uint32_t c, uint64_t microseconds) { bool didChange = false; pthread_mutex_lock(&togmutex); if (c == -1 && microseconds == -1) { // flushing printf("Flush sound output\n"); toggleReadPtr = toggleWritePtr = 0; toggleCount = 0; } else { while (toggleCount && c >= toggleTimes[toggleReadPtr]) { // Override the mixer with a 1-bit "Terribad" audio sample change toggleState = !toggleState; toggleCount--; toggleReadPtr++; if (toggleReadPtr >= SPEAKERQUEUESIZE) toggleReadPtr = 0; didChange = true; } } pthread_mutex_unlock(&togmutex); // FIXME: removed all the mixing code // Add samples from the last time to this time // mixerValue = (toggleState ? 0x1FF : 0x00); mixerValue = (toggleState ? 0x00 : ~0x80); // FIXME: DC filter isn't correct yet // mixerValue = DCFilter(mixerValue); uint64_t sampleCount = (microseconds * 44100) / 1000000; uint64_t numSamples = sampleCount - lastSampleCount; if (numSamples) { lastSampleCount = sampleCount; pthread_mutex_lock(&sndmutex); if (bufIdx + numSamples >= sizeof(soundBuf)) { static uint8_t errcnt = 0; if (++errcnt <= 10) { printf("Sound overrun!\n"); } numSamples = sizeof(soundBuf) - bufIdx - 1; } mixerValue >>= (8-(g_volume/2)); memset(&soundBuf[bufIdx], mixerValue, numSamples); bufIdx += numSamples; pthread_mutex_unlock(&sndmutex); } } void SDLSpeaker::beginMixing() { } void SDLSpeaker::mixOutput(uint8_t v) { }