From 70885ab24052056bed0098e1ee5a9bba43374d72 Mon Sep 17 00:00:00 2001 From: Jorj Bauer Date: Tue, 14 Jul 2020 20:43:41 -0400 Subject: [PATCH] speaker fixes; committed to removing threads --- bios.cpp | 4 +- sdl/sdl-speaker.cpp | 63 +++++++++++----------- sdl/sdl-speaker.h | 2 - teensy/teensy-speaker.cpp | 107 ++++++++++++++++++++------------------ teensy/teensy-speaker.h | 4 -- teensy/teensy.ino | 15 +++--- 6 files changed, 99 insertions(+), 96 deletions(-) diff --git a/bios.cpp b/bios.cpp index d3dbdab..2eecb64 100644 --- a/bios.cpp +++ b/bios.cpp @@ -11,6 +11,7 @@ #include #include "teensy-paddles.h" extern Bounce resetButtonDebouncer; +extern void runDebouncer(); #endif // Experimenting with using EXTMEM to cache all the filenames in a directory @@ -302,6 +303,7 @@ uint8_t BIOS::GetAction(int8_t selection) #endif ) { #ifdef TEENSYDUINO + runDebouncer(); delay(10); #else usleep(100); @@ -313,7 +315,7 @@ uint8_t BIOS::GetAction(int8_t selection) if (resetButtonDebouncer.read() == LOW) { // wait until it's no longer pressed while (resetButtonDebouncer.read() == HIGH) - ; + runDebouncer(); delay(100); // wait long enough for it to debounce // then return an exit code return ACT_EXIT; diff --git a/sdl/sdl-speaker.cpp b/sdl/sdl-speaker.cpp index 625b3fd..a585f60 100644 --- a/sdl/sdl-speaker.cpp +++ b/sdl/sdl-speaker.cpp @@ -10,8 +10,8 @@ extern "C" }; // What values do we use for logical speaker-high and speaker-low? -#define HIGHVAL 0xC0 -#define LOWVAL 0x40 +#define HIGHVAL (0x1FFF) +#define LOWVAL (-(0x1FFF)) #include "globals.h" @@ -21,11 +21,13 @@ extern "C" // FIXME: Globals; ick. static volatile uint32_t bufIdx = 0; -static volatile uint8_t soundBuf[CACHEMULTIPLIER*SDLSIZE]; +static volatile short soundBuf[CACHEMULTIPLIER*SDLSIZE]; static pthread_mutex_t togmutex = PTHREAD_MUTEX_INITIALIZER; static volatile uint32_t skippedSamples = 0; +#define SAMPLEBYTES sizeof(short) volatile uint8_t audioRunning = 0; +volatile uint32_t lastFilledTime = 0; // Debugging by writing a wav file with the sound output... @@ -43,47 +45,49 @@ static void audioCallback(void *unused, Uint8 *stream, int len) // While the BIOS is running, we don't put samples in the audio // queue. audioRunning = 0; - memset(stream, 0x80, len); + memset(stream, 0, SDLSIZE*SAMPLEBYTES); pthread_mutex_unlock(&togmutex); return; } - if (audioRunning==1 && bufIdx >= len) { + if (audioRunning==1 && bufIdx >= SDLSIZE) { // Fully up and running now; we got a full cache audioRunning = 2; } else if (audioRunning==1) { // waiting for first fill; return an empty buffer. - memset(stream, 0x80, len); + memset(stream, 0, SDLSIZE*SAMPLEBYTES); return; } - static uint8_t lastKnownSample = 0; // saved for when the apple is quiescent + static short lastKnownSample = 0; // saved for when the apple is quiescent - if (bufIdx >= len) { - memcpy(stream, (void *)soundBuf, len); - lastKnownSample = stream[len-1]; + if (bufIdx >= SDLSIZE) { + memcpy(stream, (void *)soundBuf, SDLSIZE*SAMPLEBYTES); + lastKnownSample = stream[SDLSIZE-1]; - if (bufIdx > len) { + if (bufIdx > SDLSIZE) { // move the remaining data down - memcpy((void *)soundBuf, (void *)&soundBuf[len], bufIdx - len + 1); - bufIdx -= len; + memcpy((void *)soundBuf, (void *)&soundBuf[SDLSIZE], (bufIdx - SDLSIZE + 1)*SAMPLEBYTES); + bufIdx -= SDLSIZE; } } else { if (bufIdx) { // partial buffer exists - memcpy(stream, (void *)soundBuf, bufIdx); + memcpy(stream, (void *)soundBuf, bufIdx*SAMPLEBYTES); // and it's a partial underrun. Track the number of samples we skipped // so we can keep the audio buffer in sync. - skippedSamples += len-bufIdx; - memset(&stream[bufIdx], lastKnownSample, len-bufIdx); + skippedSamples += SDLSIZE-bufIdx; + for (long i=0; i= 0x80) lastKnownSample--; @@ -109,7 +113,7 @@ static void audioCallback(void *unused, Uint8 *stream, int len) write(outputFD, buf, sizeof(buf)); } - write(outputFD, (void *)(stream), len); + write(outputFD, (void *)(stream), SDLSIZE*SAMPLEBYTES); #endif pthread_mutex_unlock(&togmutex); @@ -132,14 +136,14 @@ void SDLSpeaker::begin() SDL_AudioSpec audioDevice; SDL_AudioSpec audioActual; SDL_memset(&audioDevice, 0, sizeof(audioDevice)); - audioDevice.freq = 44100; // count of 8-bit samples - audioDevice.format = AUDIO_U8; + audioDevice.freq = 44100; // count of 16-bit samples + audioDevice.format = AUDIO_S16; audioDevice.channels = 1; - audioDevice.samples = SDLSIZE; // SDLSIZE 8-bit samples @ 44100Hz: 4096 is about 1/10th second out of sync + audioDevice.samples = SDLSIZE; // SDLSIZE 16-bit samples @ 44100Hz: 4096 is about 1/10th second out of sync audioDevice.callback = audioCallback; audioDevice.userdata = NULL; - memset((void *)&soundBuf[0], 0, CACHEMULTIPLIER*SDLSIZE); + memset((void *)&soundBuf[0], 0, CACHEMULTIPLIER*SDLSIZE*SAMPLEBYTES); bufIdx = 0; skippedSamples = 0; audioRunning = 0; @@ -151,7 +155,6 @@ void SDLSpeaker::begin() SDL_PauseAudio(0); } -uint32_t lastFilledTime = 0; void SDLSpeaker::toggle(uint32_t c) { pthread_mutex_lock(&togmutex); @@ -191,9 +194,9 @@ void SDLSpeaker::toggle(uint32_t c) return; } - if (newIdx >= sizeof(soundBuf)) { - printf("ERROR: buffer overrun: size %lu idx %d\n", sizeof(soundBuf), newIdx); - newIdx = sizeof(soundBuf)-1; + if (newIdx >= sizeof(soundBuf)/SAMPLEBYTES) { + printf("ERROR: buffer overrun: size %lu idx %d\n", sizeof(soundBuf)/SAMPLEBYTES, newIdx); + newIdx = (sizeof(soundBuf)/SAMPLEBYTES)-1; } lastFilledTime = expectedCycleNumber; @@ -203,7 +206,9 @@ void SDLSpeaker::toggle(uint32_t c) // Fill from bufIdx .. newIdx and set bufIdx to newIdx when done. if (newIdx > bufIdx) { long count = (long)newIdx - bufIdx; - memset((void *)&soundBuf[bufIdx], toggleState ? HIGHVAL : LOWVAL, count); + for (long i=0; i #include "physicalspeaker.h" -#define SPEAKERQUEUESIZE 1024 - class SDLSpeaker : public PhysicalSpeaker { public: SDLSpeaker(); diff --git a/teensy/teensy-speaker.cpp b/teensy/teensy-speaker.cpp index 8a3b80f..52e0453 100644 --- a/teensy/teensy-speaker.cpp +++ b/teensy/teensy-speaker.cpp @@ -20,7 +20,8 @@ AudioConnection patchCord4(mixer1, 0, i2s, 0); #define LOWVAL (-0x4FFF) // Ring buffer that we fill with 44.1kHz data -#define BUFSIZE 4096 +#define BUFSIZE (4096) +#define CACHEMULTIPLIER 2 static volatile uint32_t bufIdx; // 0 .. BUFSIZE-1 static volatile uint32_t skippedSamples; // Who knows where this will // wind up (FIXME: eventual @@ -32,8 +33,11 @@ static volatile uint32_t skippedSamples; // Who knows where this will static volatile uint8_t audioRunning = 0; // FIXME: needs constants abstracted static volatile uint32_t lastFilledTime = 0; +// how full do we want the audio buffer before we start it playing? +#define AUDIO_WATERLEVEL 4096 + #define SAMPLEBYTES sizeof(short) -EXTMEM short soundBuf[BUFSIZE]; +EXTMEM short soundBuf[BUFSIZE*CACHEMULTIPLIER]; static bool toggleState = false; @@ -52,6 +56,8 @@ void TeensySpeaker::begin() { mixer1.gain(0, 0.1f); // left channel + memset(soundBuf, 0, sizeof(soundBuf)); + toggleState = false; bufIdx = 0; skippedSamples = 0; @@ -63,7 +69,6 @@ void TeensySpeaker::toggle(uint32_t c) // Figure out when the last time was that we put data in the audio buffer; // then figure out how many audio buffer cycles we have to fill from that // CPU time to this one. -#if 1 __disable_irq(); // We expect to have filled to this cycle number... @@ -101,12 +106,14 @@ void TeensySpeaker::toggle(uint32_t c) // toggling the speaker fast enough that our 44k audio can't keep // up with the individual changes is likely to toggle again in a // moment without significant distortion? + __enable_irq(); return; } - if (newIdx >= BUFSIZE) { + if (newIdx >= sizeof(soundBuf)/SAMPLEBYTES) { // Buffer overrun error. Shouldn't happen? - newIdx = BUFSIZE - 1; + println("OVERRUN"); + newIdx = (sizeof(soundBuf)/SAMPLEBYTES) - 1; } lastFilledTime = expectedCycleNumber; @@ -122,7 +129,6 @@ void TeensySpeaker::toggle(uint32_t c) bufIdx = newIdx; } __enable_irq(); -#endif } void TeensySpeaker::maintainSpeaker(uint32_t c, uint64_t microseconds) @@ -147,71 +153,70 @@ void TeensySpeaker::mixOutput(uint8_t v) void TeensyAudio::update(void) { audio_block_t *block; - short *bp; + short *stream; if (audioRunning == 0) audioRunning = 1; - + + block = allocate(); + if (!block) { + return; + } + + stream = block->data; + if (g_biosInterrupt) { // While the BIOS is running, we don't put samples in the audio queue. audioRunning = 0; - block = allocate(); - if (block) { - bp = block->data; - memset(bp, 0, AUDIO_BLOCK_SAMPLES * SAMPLEBYTES); - transmit(block, 0); - release(block); - } - return; + memset(stream, 0, AUDIO_BLOCK_SAMPLES * SAMPLEBYTES); + goto done; } - if (audioRunning == 1 && bufIdx >= AUDIO_BLOCK_SAMPLES) { + if (audioRunning == 1 && bufIdx >= AUDIO_WATERLEVEL) { // We have enough samples in the buffer to fill it, so we're fully // up and running. audioRunning = 2; } else if (audioRunning == 1) { // Still waiting for the first fill; return an empty buffer. - block = allocate(); - if (block) { - bp = block->data; - memset(bp, 0, AUDIO_BLOCK_SAMPLES * SAMPLEBYTES); - transmit(block, 0); - release(block); - } - return; + memset(stream, 0, AUDIO_BLOCK_SAMPLES * SAMPLEBYTES); + goto done; } + + // FROM THE SOUND OF IT, something below this line isn't filling buffers + // completely; or something isn't filling soundBuf completely in toggle(). - block = allocate(); - if (block) { - bp = block->data; - static short lastKnownSample = 0; - if (bufIdx >= AUDIO_BLOCK_SAMPLES) { - memcpy(bp, (void *)soundBuf, AUDIO_BLOCK_SAMPLES * SAMPLEBYTES); - lastKnownSample = bp[AUDIO_BLOCK_SAMPLES-1]; + static short lastKnownSample = 0; + + if (bufIdx >= AUDIO_BLOCK_SAMPLES) { + memcpy(stream, (void *)soundBuf, AUDIO_BLOCK_SAMPLES * SAMPLEBYTES); + lastKnownSample = stream[AUDIO_BLOCK_SAMPLES-1]; - if (bufIdx > AUDIO_BLOCK_SAMPLES) { + if (bufIdx > AUDIO_BLOCK_SAMPLES) { // move the remaining data down - memcpy((void *)soundBuf, (void *)&soundBuf[AUDIO_BLOCK_SAMPLES], (bufIdx - AUDIO_BLOCK_SAMPLES + 1)*SAMPLEBYTES); - bufIdx -= AUDIO_BLOCK_SAMPLES; + memcpy((void *)soundBuf, (void *)&soundBuf[AUDIO_BLOCK_SAMPLES], (bufIdx - AUDIO_BLOCK_SAMPLES + 1)*SAMPLEBYTES); + bufIdx -= AUDIO_BLOCK_SAMPLES; + } + } else { + if (bufIdx) { + // partial buffer exists + memcpy(stream, (void *)soundBuf, bufIdx * SAMPLEBYTES); + // and it's a partial underrun. Track the number of samples we skipped + // so we can keep the audio buffer in sync. + skippedSamples += AUDIO_BLOCK_SAMPLES - bufIdx; + for (int32_t i=0; i #include "physicalspeaker.h" -#include - -#define SAMPLERATE 44100 class TeensyAudio : public AudioStream { public: @@ -29,7 +26,6 @@ class TeensySpeaker : public PhysicalSpeaker { virtual void mixOutput(uint8_t v); private: - // bool toggleState; uint32_t mixerValue; uint8_t numMixed; diff --git a/teensy/teensy.ino b/teensy/teensy.ino index 9490792..9e0b5a7 100644 --- a/teensy/teensy.ino +++ b/teensy/teensy.ino @@ -69,7 +69,7 @@ void onKeyrelease(int unicode) void setup() { Serial.begin(230400); -#if 1 +#if 0 // Wait for USB serial connection before booting while debugging while (!Serial) { yield(); @@ -164,10 +164,6 @@ void setup() println("free-running"); Serial.flush(); - -// threads.setMicroTimer(); // use a 100uS timer instead of a 1mS timer - // threads.setSliceMicros(5); -// threads.addThread(runDebouncer); } // FIXME: move these memory-related functions elsewhere... @@ -200,9 +196,9 @@ void biosInterrupt() { // wait for the interrupt button to be released while (!resetButtonDebouncer.read()) - ; + resetButtonDebouncer.update(); - // invoke the BIOS + // Invoke the BIOS if (bios.runUntilDone()) { // if it returned true, we have something to store persistently in EEPROM. writePrefs(); @@ -299,7 +295,7 @@ void runDisplay(uint32_t now) void runDebouncer() { static uint32_t nextRuntime = 0; - while (1) { + // while (1) { if (millis() >= nextRuntime) { nextRuntime = millis() + 10; resetButtonDebouncer.update(); @@ -307,7 +303,7 @@ void runDebouncer() yield(); // threads.yield(); } - } + // } } void runCPU(uint32_t now) @@ -345,6 +341,7 @@ void loop() runCPU(now); runDisplay(now); runMaintenance(now); + runDebouncer(); } void doDebugging(uint32_t lastFps)