speaker fixes; committed to removing threads

This commit is contained in:
Jorj Bauer 2020-07-14 20:43:41 -04:00
parent eff51b4100
commit 70885ab240
6 changed files with 99 additions and 96 deletions

View File

@ -11,6 +11,7 @@
#include <Bounce2.h> #include <Bounce2.h>
#include "teensy-paddles.h" #include "teensy-paddles.h"
extern Bounce resetButtonDebouncer; extern Bounce resetButtonDebouncer;
extern void runDebouncer();
#endif #endif
// Experimenting with using EXTMEM to cache all the filenames in a directory // Experimenting with using EXTMEM to cache all the filenames in a directory
@ -302,6 +303,7 @@ uint8_t BIOS::GetAction(int8_t selection)
#endif #endif
) { ) {
#ifdef TEENSYDUINO #ifdef TEENSYDUINO
runDebouncer();
delay(10); delay(10);
#else #else
usleep(100); usleep(100);
@ -313,7 +315,7 @@ uint8_t BIOS::GetAction(int8_t selection)
if (resetButtonDebouncer.read() == LOW) { if (resetButtonDebouncer.read() == LOW) {
// wait until it's no longer pressed // wait until it's no longer pressed
while (resetButtonDebouncer.read() == HIGH) while (resetButtonDebouncer.read() == HIGH)
; runDebouncer();
delay(100); // wait long enough for it to debounce delay(100); // wait long enough for it to debounce
// then return an exit code // then return an exit code
return ACT_EXIT; return ACT_EXIT;

View File

@ -10,8 +10,8 @@ extern "C"
}; };
// What values do we use for logical speaker-high and speaker-low? // What values do we use for logical speaker-high and speaker-low?
#define HIGHVAL 0xC0 #define HIGHVAL (0x1FFF)
#define LOWVAL 0x40 #define LOWVAL (-(0x1FFF))
#include "globals.h" #include "globals.h"
@ -21,11 +21,13 @@ extern "C"
// FIXME: Globals; ick. // FIXME: Globals; ick.
static volatile uint32_t bufIdx = 0; 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 pthread_mutex_t togmutex = PTHREAD_MUTEX_INITIALIZER;
static volatile uint32_t skippedSamples = 0; static volatile uint32_t skippedSamples = 0;
#define SAMPLEBYTES sizeof(short)
volatile uint8_t audioRunning = 0; volatile uint8_t audioRunning = 0;
volatile uint32_t lastFilledTime = 0;
// Debugging by writing a wav file with the sound output... // 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 // While the BIOS is running, we don't put samples in the audio
// queue. // queue.
audioRunning = 0; audioRunning = 0;
memset(stream, 0x80, len); memset(stream, 0, SDLSIZE*SAMPLEBYTES);
pthread_mutex_unlock(&togmutex); pthread_mutex_unlock(&togmutex);
return; return;
} }
if (audioRunning==1 && bufIdx >= len) { if (audioRunning==1 && bufIdx >= SDLSIZE) {
// Fully up and running now; we got a full cache // Fully up and running now; we got a full cache
audioRunning = 2; audioRunning = 2;
} else if (audioRunning==1) { } else if (audioRunning==1) {
// waiting for first fill; return an empty buffer. // waiting for first fill; return an empty buffer.
memset(stream, 0x80, len); memset(stream, 0, SDLSIZE*SAMPLEBYTES);
return; 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) { if (bufIdx >= SDLSIZE) {
memcpy(stream, (void *)soundBuf, len); memcpy(stream, (void *)soundBuf, SDLSIZE*SAMPLEBYTES);
lastKnownSample = stream[len-1]; lastKnownSample = stream[SDLSIZE-1];
if (bufIdx > len) { if (bufIdx > SDLSIZE) {
// move the remaining data down // move the remaining data down
memcpy((void *)soundBuf, (void *)&soundBuf[len], bufIdx - len + 1); memcpy((void *)soundBuf, (void *)&soundBuf[SDLSIZE], (bufIdx - SDLSIZE + 1)*SAMPLEBYTES);
bufIdx -= len; bufIdx -= SDLSIZE;
} }
} else { } else {
if (bufIdx) { if (bufIdx) {
// partial buffer exists // 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 // and it's a partial underrun. Track the number of samples we skipped
// so we can keep the audio buffer in sync. // so we can keep the audio buffer in sync.
skippedSamples += len-bufIdx; skippedSamples += SDLSIZE-bufIdx;
memset(&stream[bufIdx], lastKnownSample, len-bufIdx); for (long i=0; i<SDLSIZE-bufIdx; i++) {
stream[bufIdx+i] = lastKnownSample;
}
bufIdx = 0; bufIdx = 0;
} else { } else {
// No big deal - buffer underrun might just mean nothing // No big deal - buffer underrun might just mean nothing
// is trying to play audio right now. // is trying to play audio right now.
skippedSamples += len; skippedSamples += SDLSIZE;
memset(stream, 0x80, len); memset(stream, 0, SDLSIZE*SAMPLEBYTES);
// memset(stream, lastKnownSample, len); // memset(stream, lastKnownSample, SDLSIZE);
// Trend toward DC voltage = 0v // Trend toward DC voltage = 0v
// if (lastKnownSample < 0x7F) lastKnownSample++; // if (lastKnownSample < 0x7F) lastKnownSample++;
// if (lastKnownSample >= 0x80) lastKnownSample--; // if (lastKnownSample >= 0x80) lastKnownSample--;
@ -109,7 +113,7 @@ static void audioCallback(void *unused, Uint8 *stream, int len)
write(outputFD, buf, sizeof(buf)); write(outputFD, buf, sizeof(buf));
} }
write(outputFD, (void *)(stream), len); write(outputFD, (void *)(stream), SDLSIZE*SAMPLEBYTES);
#endif #endif
pthread_mutex_unlock(&togmutex); pthread_mutex_unlock(&togmutex);
@ -132,14 +136,14 @@ void SDLSpeaker::begin()
SDL_AudioSpec audioDevice; SDL_AudioSpec audioDevice;
SDL_AudioSpec audioActual; SDL_AudioSpec audioActual;
SDL_memset(&audioDevice, 0, sizeof(audioDevice)); SDL_memset(&audioDevice, 0, sizeof(audioDevice));
audioDevice.freq = 44100; // count of 8-bit samples audioDevice.freq = 44100; // count of 16-bit samples
audioDevice.format = AUDIO_U8; audioDevice.format = AUDIO_S16;
audioDevice.channels = 1; 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.callback = audioCallback;
audioDevice.userdata = NULL; audioDevice.userdata = NULL;
memset((void *)&soundBuf[0], 0, CACHEMULTIPLIER*SDLSIZE); memset((void *)&soundBuf[0], 0, CACHEMULTIPLIER*SDLSIZE*SAMPLEBYTES);
bufIdx = 0; bufIdx = 0;
skippedSamples = 0; skippedSamples = 0;
audioRunning = 0; audioRunning = 0;
@ -151,7 +155,6 @@ void SDLSpeaker::begin()
SDL_PauseAudio(0); SDL_PauseAudio(0);
} }
uint32_t lastFilledTime = 0;
void SDLSpeaker::toggle(uint32_t c) void SDLSpeaker::toggle(uint32_t c)
{ {
pthread_mutex_lock(&togmutex); pthread_mutex_lock(&togmutex);
@ -191,9 +194,9 @@ void SDLSpeaker::toggle(uint32_t c)
return; return;
} }
if (newIdx >= sizeof(soundBuf)) { if (newIdx >= sizeof(soundBuf)/SAMPLEBYTES) {
printf("ERROR: buffer overrun: size %lu idx %d\n", sizeof(soundBuf), newIdx); printf("ERROR: buffer overrun: size %lu idx %d\n", sizeof(soundBuf)/SAMPLEBYTES, newIdx);
newIdx = sizeof(soundBuf)-1; newIdx = (sizeof(soundBuf)/SAMPLEBYTES)-1;
} }
lastFilledTime = expectedCycleNumber; lastFilledTime = expectedCycleNumber;
@ -203,7 +206,9 @@ void SDLSpeaker::toggle(uint32_t c)
// Fill from bufIdx .. newIdx and set bufIdx to newIdx when done. // Fill from bufIdx .. newIdx and set bufIdx to newIdx when done.
if (newIdx > bufIdx) { if (newIdx > bufIdx) {
long count = (long)newIdx - bufIdx; long count = (long)newIdx - bufIdx;
memset((void *)&soundBuf[bufIdx], toggleState ? HIGHVAL : LOWVAL, count); for (long i=0; i<count; i++) {
soundBuf[bufIdx+i] = toggleState ? HIGHVAL : LOWVAL;
}
bufIdx = newIdx; bufIdx = newIdx;
} }

View File

@ -5,8 +5,6 @@
#include <stdint.h> #include <stdint.h>
#include "physicalspeaker.h" #include "physicalspeaker.h"
#define SPEAKERQUEUESIZE 1024
class SDLSpeaker : public PhysicalSpeaker { class SDLSpeaker : public PhysicalSpeaker {
public: public:
SDLSpeaker(); SDLSpeaker();

View File

@ -20,7 +20,8 @@ AudioConnection patchCord4(mixer1, 0, i2s, 0);
#define LOWVAL (-0x4FFF) #define LOWVAL (-0x4FFF)
// Ring buffer that we fill with 44.1kHz data // 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 bufIdx; // 0 .. BUFSIZE-1
static volatile uint32_t skippedSamples; // Who knows where this will static volatile uint32_t skippedSamples; // Who knows where this will
// wind up (FIXME: eventual // 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 uint8_t audioRunning = 0; // FIXME: needs constants abstracted
static volatile uint32_t lastFilledTime = 0; 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) #define SAMPLEBYTES sizeof(short)
EXTMEM short soundBuf[BUFSIZE]; EXTMEM short soundBuf[BUFSIZE*CACHEMULTIPLIER];
static bool toggleState = false; static bool toggleState = false;
@ -52,6 +56,8 @@ void TeensySpeaker::begin()
{ {
mixer1.gain(0, 0.1f); // left channel mixer1.gain(0, 0.1f); // left channel
memset(soundBuf, 0, sizeof(soundBuf));
toggleState = false; toggleState = false;
bufIdx = 0; bufIdx = 0;
skippedSamples = 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; // 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 // then figure out how many audio buffer cycles we have to fill from that
// CPU time to this one. // CPU time to this one.
#if 1
__disable_irq(); __disable_irq();
// We expect to have filled to this cycle number... // 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 // toggling the speaker fast enough that our 44k audio can't keep
// up with the individual changes is likely to toggle again in a // up with the individual changes is likely to toggle again in a
// moment without significant distortion? // moment without significant distortion?
__enable_irq();
return; return;
} }
if (newIdx >= BUFSIZE) { if (newIdx >= sizeof(soundBuf)/SAMPLEBYTES) {
// Buffer overrun error. Shouldn't happen? // Buffer overrun error. Shouldn't happen?
newIdx = BUFSIZE - 1; println("OVERRUN");
newIdx = (sizeof(soundBuf)/SAMPLEBYTES) - 1;
} }
lastFilledTime = expectedCycleNumber; lastFilledTime = expectedCycleNumber;
@ -122,7 +129,6 @@ void TeensySpeaker::toggle(uint32_t c)
bufIdx = newIdx; bufIdx = newIdx;
} }
__enable_irq(); __enable_irq();
#endif
} }
void TeensySpeaker::maintainSpeaker(uint32_t c, uint64_t microseconds) void TeensySpeaker::maintainSpeaker(uint32_t c, uint64_t microseconds)
@ -147,71 +153,70 @@ void TeensySpeaker::mixOutput(uint8_t v)
void TeensyAudio::update(void) void TeensyAudio::update(void)
{ {
audio_block_t *block; audio_block_t *block;
short *bp; short *stream;
if (audioRunning == 0) if (audioRunning == 0)
audioRunning = 1; audioRunning = 1;
block = allocate();
if (!block) {
return;
}
stream = block->data;
if (g_biosInterrupt) { if (g_biosInterrupt) {
// While the BIOS is running, we don't put samples in the audio queue. // While the BIOS is running, we don't put samples in the audio queue.
audioRunning = 0; audioRunning = 0;
block = allocate(); memset(stream, 0, AUDIO_BLOCK_SAMPLES * SAMPLEBYTES);
if (block) { goto done;
bp = block->data;
memset(bp, 0, AUDIO_BLOCK_SAMPLES * SAMPLEBYTES);
transmit(block, 0);
release(block);
}
return;
} }
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 // We have enough samples in the buffer to fill it, so we're fully
// up and running. // up and running.
audioRunning = 2; audioRunning = 2;
} else if (audioRunning == 1) { } else if (audioRunning == 1) {
// Still waiting for the first fill; return an empty buffer. // Still waiting for the first fill; return an empty buffer.
block = allocate(); memset(stream, 0, AUDIO_BLOCK_SAMPLES * SAMPLEBYTES);
if (block) { goto done;
bp = block->data;
memset(bp, 0, AUDIO_BLOCK_SAMPLES * SAMPLEBYTES);
transmit(block, 0);
release(block);
}
return;
} }
// 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(); static short lastKnownSample = 0;
if (block) {
bp = block->data; if (bufIdx >= AUDIO_BLOCK_SAMPLES) {
static short lastKnownSample = 0; memcpy(stream, (void *)soundBuf, AUDIO_BLOCK_SAMPLES * SAMPLEBYTES);
if (bufIdx >= AUDIO_BLOCK_SAMPLES) { lastKnownSample = stream[AUDIO_BLOCK_SAMPLES-1];
memcpy(bp, (void *)soundBuf, AUDIO_BLOCK_SAMPLES * SAMPLEBYTES);
lastKnownSample = bp[AUDIO_BLOCK_SAMPLES-1];
if (bufIdx > AUDIO_BLOCK_SAMPLES) { if (bufIdx > AUDIO_BLOCK_SAMPLES) {
// move the remaining data down // move the remaining data down
memcpy((void *)soundBuf, (void *)&soundBuf[AUDIO_BLOCK_SAMPLES], (bufIdx - AUDIO_BLOCK_SAMPLES + 1)*SAMPLEBYTES); memcpy((void *)soundBuf, (void *)&soundBuf[AUDIO_BLOCK_SAMPLES], (bufIdx - AUDIO_BLOCK_SAMPLES + 1)*SAMPLEBYTES);
bufIdx -= AUDIO_BLOCK_SAMPLES; 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<AUDIO_BLOCK_SAMPLES-bufIdx; i++) {
stream[i+bufIdx] = lastKnownSample;
} }
} else { } else {
if (bufIdx) { // No big deal - buffer underrun might just mean nothing is
// partial buffer exists // trying to play audio right now.
memcpy(bp, (void *)soundBuf, bufIdx * SAMPLEBYTES); skippedSamples += AUDIO_BLOCK_SAMPLES;
// and it's a partial underrun. Track the number of samples we skipped for (int32_t i=0; i<AUDIO_BLOCK_SAMPLES; i++) {
// so we can keep the audio buffer in sync. stream[i] = 0;
skippedSamples += AUDIO_BLOCK_SAMPLES - bufIdx;
for (int32_t i=0; i<AUDIO_BLOCK_SAMPLES-bufIdx; i++) {
bp[i+bufIdx] = lastKnownSample;
}
} else {
// No big deal - buffer underrun might just mean nothing is
// trying to play audio right now.
skippedSamples += AUDIO_BLOCK_SAMPLES;
memset(bp, 0, AUDIO_BLOCK_SAMPLES * SAMPLEBYTES);
} }
// memset(stream, 0, AUDIO_BLOCK_SAMPLES * SAMPLEBYTES);
} }
transmit(block, 0);
release(block);
} }
done:
transmit(block, 0);
release(block);
} }

View File

@ -3,9 +3,6 @@
#include <AudioStream.h> #include <AudioStream.h>
#include "physicalspeaker.h" #include "physicalspeaker.h"
#include <MCP492X.h>
#define SAMPLERATE 44100
class TeensyAudio : public AudioStream { class TeensyAudio : public AudioStream {
public: public:
@ -29,7 +26,6 @@ class TeensySpeaker : public PhysicalSpeaker {
virtual void mixOutput(uint8_t v); virtual void mixOutput(uint8_t v);
private: private:
// bool toggleState;
uint32_t mixerValue; uint32_t mixerValue;
uint8_t numMixed; uint8_t numMixed;

View File

@ -69,7 +69,7 @@ void onKeyrelease(int unicode)
void setup() void setup()
{ {
Serial.begin(230400); Serial.begin(230400);
#if 1 #if 0
// Wait for USB serial connection before booting while debugging // Wait for USB serial connection before booting while debugging
while (!Serial) { while (!Serial) {
yield(); yield();
@ -164,10 +164,6 @@ void setup()
println("free-running"); println("free-running");
Serial.flush(); 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... // FIXME: move these memory-related functions elsewhere...
@ -200,9 +196,9 @@ void biosInterrupt()
{ {
// wait for the interrupt button to be released // wait for the interrupt button to be released
while (!resetButtonDebouncer.read()) while (!resetButtonDebouncer.read())
; resetButtonDebouncer.update();
// invoke the BIOS // Invoke the BIOS
if (bios.runUntilDone()) { if (bios.runUntilDone()) {
// if it returned true, we have something to store persistently in EEPROM. // if it returned true, we have something to store persistently in EEPROM.
writePrefs(); writePrefs();
@ -299,7 +295,7 @@ void runDisplay(uint32_t now)
void runDebouncer() void runDebouncer()
{ {
static uint32_t nextRuntime = 0; static uint32_t nextRuntime = 0;
while (1) { // while (1) {
if (millis() >= nextRuntime) { if (millis() >= nextRuntime) {
nextRuntime = millis() + 10; nextRuntime = millis() + 10;
resetButtonDebouncer.update(); resetButtonDebouncer.update();
@ -307,7 +303,7 @@ void runDebouncer()
yield(); yield();
// threads.yield(); // threads.yield();
} }
} // }
} }
void runCPU(uint32_t now) void runCPU(uint32_t now)
@ -345,6 +341,7 @@ void loop()
runCPU(now); runCPU(now);
runDisplay(now); runDisplay(now);
runMaintenance(now); runMaintenance(now);
runDebouncer();
} }
void doDebugging(uint32_t lastFps) void doDebugging(uint32_t lastFps)