From 9b21e882967cd64b4f962eb9887c58a8831b6d4c Mon Sep 17 00:00:00 2001 From: Jorj Bauer Date: Sat, 11 Jul 2020 19:14:30 -0400 Subject: [PATCH] playing with audio output --- teensy/teensy-speaker.cpp | 23 ++++++++++------------- teensy/teensy-speaker.h | 2 +- teensy/teensy.ino | 31 ++++++++++++++++++++++--------- 3 files changed, 33 insertions(+), 23 deletions(-) diff --git a/teensy/teensy-speaker.cpp b/teensy/teensy-speaker.cpp index d4e49b8..940ea7c 100644 --- a/teensy/teensy-speaker.cpp +++ b/teensy/teensy-speaker.cpp @@ -1,11 +1,13 @@ #include -#include #include #include "teensy-speaker.h" #include "teensy-println.h" +#include #include "globals.h" +I2CDevice dac = I2CDevice(Master, 0x60, _BIG_ENDIAN); + Threads::Mutex togmutex; #define BUFSIZE 4096 EXTMEM uint32_t toggleBuffer[BUFSIZE]; // cycle counts at which state toggles @@ -20,6 +22,7 @@ TeensySpeaker::TeensySpeaker(uint8_t sda, uint8_t scl) : PhysicalSpeaker() { toggleState = false; mixerValue = numMixed = 0; + Master.begin(1000000); // 100000 or 400000 or 1000000 } TeensySpeaker::~TeensySpeaker() @@ -39,6 +42,7 @@ void TeensySpeaker::toggle(uint32_t c) Threads::Scope lock(togmutex); // Queue the speaker toggle time; maintainSpeaker will pick it up toggleBuffer[tailptr++] = c; tailptr %= BUFSIZE; + } void TeensySpeaker::maintainSpeaker(uint32_t c, uint64_t microseconds) @@ -48,8 +52,6 @@ void TeensySpeaker::maintainSpeaker(uint32_t c, uint64_t microseconds) void TeensySpeaker::maintainSpeaker() { - Threads::Scope lock(togmutex); - // This is called @ SAMPLERATE (8k, as of this writing) and looks for // any transitions that have passed before sending data to the DAC. // The idea is that this will be called fast enough for the given number @@ -68,6 +70,7 @@ void TeensySpeaker::maintainSpeaker() // since we actually run cycles in batches). uint32_t curTime = g_cpu->cycles - CYCLEDELAY; // And then find any events that should have happened, accounting for them: + togmutex.lock(); while (headptr != tailptr) { if (curTime >= toggleBuffer[headptr]) { toggleState = !toggleState; @@ -77,18 +80,12 @@ void TeensySpeaker::maintainSpeaker() break; } } + togmutex.unlock(); // Now we can safely update the DAC based on the current toggleState - uint16_t v = (toggleState ? 0x1FF : 0x0); - uint8_t configbits = - (0 << 3) | // channel (A/B; A=0) - (0 << 2) | // buffered (no) - (1 << 1) | // gain (1 = 1x; 0 = 2x) - 1; // keep channel active - uint8_t b = ((configbits << 4) | (v & 0xF00)) >> 8; - uint8_t b2 = (v & 0xFF); - spi_send(b); - spi_send(b2); + uint16_t v = (toggleState ? 0xFFF : 0x000); + dac.write((uint8_t) ((v >> 8) & 0xFF), (uint8_t) (v & 0xFF), true); + } void TeensySpeaker::beginMixing() diff --git a/teensy/teensy-speaker.h b/teensy/teensy-speaker.h index 58b83e3..f1a16c3 100644 --- a/teensy/teensy-speaker.h +++ b/teensy/teensy-speaker.h @@ -4,7 +4,7 @@ #include "physicalspeaker.h" #include -#define SAMPLERATE 8000 +#define SAMPLERATE 4000 class TeensySpeaker : public PhysicalSpeaker { public: diff --git a/teensy/teensy.ino b/teensy/teensy.ino index 29013c1..5fe08de 100644 --- a/teensy/teensy.ino +++ b/teensy/teensy.ino @@ -21,7 +21,7 @@ #endif #define RESETPIN 38 -#define SPEAKERPIN A16 // aka digital 40 +#define DEBUGPIN 23 #include "globals.h" #include "teensy-crash.h" @@ -44,6 +44,7 @@ int biosThreadId = -1; Bounce resetButtonDebouncer = Bounce(); Threads::Mutex cpulock; // For the BIOS to suspend CPU cleanly Threads::Mutex displaylock; // For the BIOS to shut down the display cleanly +Threads::Mutex speakerlock; volatile bool g_writePrefsFromMainLoop = false; @@ -83,6 +84,8 @@ void setup() #endif delay(120); // let the power settle + pinMode(DEBUGPIN, OUTPUT); // for debugging + // enableFaultHandler(); SCB_SHCSR |= SCB_SHCSR_BUSFAULTENA | SCB_SHCSR_USGFAULTENA | SCB_SHCSR_MEMFAULTENA; @@ -102,8 +105,6 @@ void setup() analogReadRes(8); // We only need 8 bits of resolution (0-255) for paddles analogReadAveraging(4); // ?? dunno if we need this or not. - pinMode(SPEAKERPIN, OUTPUT); // analog speaker output, used as digital volume control - println("creating virtual hardware"); g_speaker = new TeensySpeaker(18, 19); // FIXME abstract constants @@ -169,17 +170,22 @@ void setup() Serial.flush(); threads.setMicroTimer(); // use a 100uS timer instead of a 1mS timer + threads.setSliceMicros(5); cpuThreadId = threads.addThread(runCPU); displayThreadId = threads.addThread(runDisplay); maintenanceThreadId = threads.addThread(runMaintenance); speakerThreadId = threads.addThread(runSpeaker); // Set the relative priorities of the threads by defining how long a "slice" // is for each (in 100uS "ticks") - // At a ratio of 50:10:1, we get about 30FPS and 100% CPU speed. - threads.setTimeSlice(displayThreadId, 100); - threads.setTimeSlice(cpuThreadId, 20); + // At a ratio of 50:10:1, we get about 30FPS and 100% CPU speed using 100uS ticks. + // After adding an I2C DAC (what a terrible idea!) - 40:10:1:10 gets us about 70% + // CPU during disk activity, and 22 FPS, with a speaker that, well, makes a good deal + // of noise. It's not ideal, but it proves that it's possible; using a real SPI + // DAC here would probably work. + threads.setTimeSlice(displayThreadId, 40); + threads.setTimeSlice(cpuThreadId, 10); threads.setTimeSlice(maintenanceThreadId, 1); - threads.setTimeSlice(speakerThreadId, 3); // guessing at a good value + threads.setTimeSlice(speakerThreadId, 10); // guessing at a good value } // FIXME: move these memory-related functions elsewhere... @@ -213,6 +219,7 @@ void biosInterrupt() // Make sure the CPU and display don't run while we're in interrupt. Threads::Scope lock1(cpulock); Threads::Scope lock2(displaylock); + Threads::Scope lock3(speakerlock); // wait for the interrupt button to be released while (!resetButtonDebouncer.read()) @@ -260,8 +267,14 @@ void runSpeaker() uint32_t nextRuntime = 0; while (1) { if (micros() > nextRuntime) { + Threads::Scope lock(speakerlock); + digitalWrite(DEBUGPIN, HIGH); + digitalWrite(DEBUGPIN, LOW); + nextRuntime = micros() + ((float)1000000/(float)SAMPLERATE); // 125 uS per cycle @ 8k sample rate ((TeensySpeaker *)g_speaker)->maintainSpeaker(); + } else { + threads.yield(); } } } @@ -376,8 +389,8 @@ void runCPU() ((AppleVM *)g_vm)->cpuMaintenance(g_cpu->cycles); } else { - // threads.yield(); - threads.delay(1); + threads.yield(); + // threads.delay(1); } } }