From eff51b41002a13a4c685a40e36780b92b8f799c0 Mon Sep 17 00:00:00 2001 From: Jorj Bauer Date: Mon, 13 Jul 2020 21:10:26 -0400 Subject: [PATCH] working on teensy audio --- teensy/teensy-speaker.cpp | 216 ++++++++++++++++++++++---------------- 1 file changed, 128 insertions(+), 88 deletions(-) diff --git a/teensy/teensy-speaker.cpp b/teensy/teensy-speaker.cpp index c66a4ec..8a3b80f 100644 --- a/teensy/teensy-speaker.cpp +++ b/teensy/teensy-speaker.cpp @@ -13,31 +13,29 @@ AudioConnection patchCord1(audioDriver, 0, mixer1, 0); //AudioConnection patchCord2(audioDriver, 0, mixer2, 0); //AudioConnection patchCord3(mixer2, 0, i2s, 1); AudioConnection patchCord4(mixer1, 0, i2s, 0); -//const float t_ampx = 0.8; -//const int t_lox = 10; -//const int t_hix = 22000; -//const float t_timex = 10; // Length of time for the sweep in seconds #include "globals.h" -//#define BUFSIZE 4096 -//EXTMEM uint32_t toggleBuffer[BUFSIZE]; // cycle counts at which state toggles -//uint16_t headptr, tailptr; +#define HIGHVAL (0x4FFF) +#define LOWVAL (-0x4FFF) // Ring buffer that we fill with 44.1kHz data -#define RINGBUFSIZE 4096 -EXTMEM short sampleRingBuffer[RINGBUFSIZE]; -volatile uint16_t sampleHeadPtr = 0; -volatile uint16_t sampleTailPtr = 0; -volatile uint32_t lastFilledTime = 0; +#define BUFSIZE 4096 +static volatile uint32_t bufIdx; // 0 .. BUFSIZE-1 +static volatile uint32_t skippedSamples; // Who knows where this will + // wind up (FIXME: eventual + // rollover means we need a + // way to purge the queue + // when it's quiescent for + // too long & restart all the + // constants) +static volatile uint8_t audioRunning = 0; // FIXME: needs constants abstracted +static volatile uint32_t lastFilledTime = 0; -volatile uint32_t lastSampleNum = 0; +#define SAMPLEBYTES sizeof(short) +EXTMEM short soundBuf[BUFSIZE]; -bool toggleState = false; - -// How many cycles do we run the audio behind? Needs to be more than our bulk -// cycle count. -//#define CYCLEDELAY 100 +static bool toggleState = false; TeensySpeaker::TeensySpeaker(uint8_t sda, uint8_t scl) : PhysicalSpeaker() { @@ -52,14 +50,12 @@ TeensySpeaker::~TeensySpeaker() void TeensySpeaker::begin() { - mixer1.gain(0, 0.5f); // left channel - - lastFilledTime = g_cpu->cycles; - sampleHeadPtr = sampleTailPtr = 0; + mixer1.gain(0, 0.1f); // left channel + toggleState = false; - // memset(toggleBuffer, 0, sizeof(toggleBuffer)); - // headptr = tailptr = 0; - lastSampleNum = 0; + bufIdx = 0; + skippedSamples = 0; + audioRunning = 0; } void TeensySpeaker::toggle(uint32_t c) @@ -72,18 +68,59 @@ void TeensySpeaker::toggle(uint32_t c) // We expect to have filled to this cycle number... uint32_t expectedCycleNumber = (float)c * (float)AUDIO_SAMPLE_RATE_EXACT / (float)g_speed; + // Dynamically initialize the lastFilledTime based on the start time of the + // audio channel. + if (lastFilledTime == 0) + lastFilledTime = expectedCycleNumber; - // and we have filled to cycle number lastFilledTime. So how many do we need? - uint32_t audioBufferSamples = expectedCycleNumber - lastFilledTime; - - if (audioBufferSamples > RINGBUFSIZE) - audioBufferSamples = RINGBUFSIZE; - for (int i=0; i= BUFSIZE) { + // Buffer overrun error. Shouldn't happen? + newIdx = BUFSIZE - 1; } - toggleState = !toggleState; lastFilledTime = expectedCycleNumber; + + // Flip the toggle state + toggleState = !toggleState; + + // Fill from bufIdx .. newIdx and set bufIdx to newIdx when done. + if (newIdx > bufIdx) { + long count = (long)newIdx - bufIdx; + for (long i=0; icycles - CYCLEDELAY; - while (headptr != tailptr) { - if (curTime >= toggleBuffer[headptr]) { - toggleState = !toggleState; - headptr++; headptr %= BUFSIZE; - } else { - // The time to deal with this one has not come yet, so we're done for now - break; - } - } -#endif } void TeensySpeaker::beginMixing() @@ -129,49 +149,69 @@ void TeensyAudio::update(void) audio_block_t *block; short *bp; - // Grab a block and we'll fill it up. It needs AUDIO_BLOCK_SAMPLES short values - // (which is 128 on the Teensy 4). + if (audioRunning == 0) + audioRunning = 1; + + 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; + } + + if (audioRunning == 1 && bufIdx >= AUDIO_BLOCK_SAMPLES) { + // 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; + } + block = allocate(); if (block) { bp = block->data; -#if 1 - uint32_t underflow = 0; - for (int i=0; i= AUDIO_BLOCK_SAMPLES) { + memcpy(bp, (void *)soundBuf, AUDIO_BLOCK_SAMPLES * SAMPLEBYTES); + lastKnownSample = bp[AUDIO_BLOCK_SAMPLES-1]; + + 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; + } + } else { + if (bufIdx) { + // partial buffer exists + memcpy(bp, (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; icycles; - } else { - lastFilledTime = 0; - } - // FIXME: - // lastSampleNum = 0; - } -#endif }