removed threads; bones for audio in place

This commit is contained in:
Jorj Bauer 2020-07-12 22:13:33 -04:00
parent fb7a64a22f
commit 1dcbee91ba
9 changed files with 146 additions and 108 deletions

View File

@ -8,7 +8,6 @@
#include "cpu.h" #include "cpu.h"
#ifdef TEENSYDUINO #ifdef TEENSYDUINO
#include <TeensyThreads.h>
#include <Bounce2.h> #include <Bounce2.h>
#include "teensy-paddles.h" #include "teensy-paddles.h"
extern Bounce resetButtonDebouncer; extern Bounce resetButtonDebouncer;
@ -303,7 +302,7 @@ uint8_t BIOS::GetAction(int8_t selection)
#endif #endif
) { ) {
#ifdef TEENSYDUINO #ifdef TEENSYDUINO
threads.delay(10); delay(10);
#else #else
usleep(100); usleep(100);
#endif #endif
@ -315,7 +314,7 @@ uint8_t BIOS::GetAction(int8_t selection)
// wait until it's no longer pressed // wait until it's no longer pressed
while (resetButtonDebouncer.read() == HIGH) while (resetButtonDebouncer.read() == HIGH)
; ;
threads.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

@ -55,9 +55,4 @@ extern bool g_invertPaddleY;
extern char debugBuf[255]; extern char debugBuf[255];
#ifdef TEENSYDUINO
#include <TeensyThreads.h>
extern Threads::Mutex spi_lock;
#endif
#endif #endif

View File

@ -9,7 +9,6 @@
#include "globals.h" #include "globals.h"
#include <Arduino.h> #include <Arduino.h>
#include <TeensyThreads.h>
#define open(path, flags, perms) g_filemanager->openFile(path) #define open(path, flags, perms) g_filemanager->openFile(path)
#define close(filedes) g_filemanager->closeFile(filedes) #define close(filedes) g_filemanager->closeFile(filedes)
@ -17,9 +16,8 @@
#define read(filedes,buf,nbyte) g_filemanager->read(filedes,buf,nbyte) #define read(filedes,buf,nbyte) g_filemanager->read(filedes,buf,nbyte)
#define lseek(filedes,offset,whence) g_filemanager->lseek(filedes,offset,whence) #define lseek(filedes,offset,whence) g_filemanager->lseek(filedes,offset,whence)
Threads::Mutex serlock;
static char fsbuf[200]; static char fsbuf[200];
#define printf(x, ...) {sprintf(fsbuf, x, ##__VA_ARGS__); serlock.lock(); Serial.println(fsbuf); Serial.flush(); Serial.send_now(); serlock.unlock();} #define printf(x, ...) {sprintf(fsbuf, x, ##__VA_ARGS__); Serial.println(fsbuf); Serial.flush(); Serial.send_now();}
#define fprintf(f, x, ...) {sprintf(fsbuf, x, ##__VA_ARGS__); serlock.lock(); Serial.println(fsbuf); Serial.flush(); Serial.send_now(); serlock.lock();} #define fprintf(f, x, ...) {sprintf(fsbuf, x, ##__VA_ARGS__); Serial.println(fsbuf); Serial.flush(); Serial.send_now();}
#define perror(x) {serlock.lock();Serial.println(x);Serial.flush(); Serial.send_now(); serlock.unlock();} #define perror(x) {Serial.println(x);Serial.flush(); Serial.send_now();}

View File

@ -2,11 +2,8 @@
#include <wchar.h> #include <wchar.h>
#include "teensy-filemanager.h" #include "teensy-filemanager.h"
#include <string.h> // strcpy #include <string.h> // strcpy
#include <TeensyThreads.h>
#include "teensy-println.h" #include "teensy-println.h"
Threads::Mutex fslock;
TeensyFileManager::TeensyFileManager() TeensyFileManager::TeensyFileManager()
{ {
numCached = 0; numCached = 0;
@ -23,8 +20,6 @@ TeensyFileManager::~TeensyFileManager()
int8_t TeensyFileManager::openFile(const char *name) int8_t TeensyFileManager::openFile(const char *name)
{ {
Threads::Scope locker(fslock);
if (cacheFd != -1) { if (cacheFd != -1) {
cacheFile.close(); cacheFile.close();
cacheFd = -1; cacheFd = -1;
@ -56,7 +51,6 @@ int8_t TeensyFileManager::openFile(const char *name)
void TeensyFileManager::closeFile(int8_t fd) void TeensyFileManager::closeFile(int8_t fd)
{ {
Threads::Scope locker(fslock);
if (cacheFd != -1) { if (cacheFd != -1) {
cacheFile.close(); cacheFile.close();
cacheFd = -1; cacheFd = -1;
@ -92,8 +86,6 @@ void TeensyFileManager::closeDir()
// suffix may be comma-separated // suffix may be comma-separated
int16_t TeensyFileManager::readDir(const char *where, const char *suffix, char *outputFN, int16_t startIdx, uint16_t maxlen) int16_t TeensyFileManager::readDir(const char *where, const char *suffix, char *outputFN, int16_t startIdx, uint16_t maxlen)
{ {
Threads::Scope locker(fslock);
// First entry is always "../" if we're in a subdir of the root // First entry is always "../" if we're in a subdir of the root
if (startIdx == 0 || !outerDir) { if (startIdx == 0 || !outerDir) {
if (outerDir) if (outerDir)
@ -220,7 +212,6 @@ void TeensyFileManager::seekToEnd(int8_t fd)
int TeensyFileManager::write(int8_t fd, const void *buf, int nbyte) int TeensyFileManager::write(int8_t fd, const void *buf, int nbyte)
{ {
Threads::Scope locker(fslock);
// open, seek, write, close. // open, seek, write, close.
if (fd < 0 || fd >= numCached) { if (fd < 0 || fd >= numCached) {
return -1; return -1;
@ -249,7 +240,6 @@ int TeensyFileManager::write(int8_t fd, const void *buf, int nbyte)
int TeensyFileManager::read(int8_t fd, void *buf, int nbyte) int TeensyFileManager::read(int8_t fd, void *buf, int nbyte)
{ {
Threads::Scope locker(fslock);
// open, seek, read, close. // open, seek, read, close.
if (fd < 0 || fd >= numCached) { if (fd < 0 || fd >= numCached) {
return -1; return -1;
@ -276,7 +266,6 @@ int TeensyFileManager::read(int8_t fd, void *buf, int nbyte)
int TeensyFileManager::lseek(int8_t fd, int offset, int whence) int TeensyFileManager::lseek(int8_t fd, int offset, int whence)
{ {
Threads::Scope locker(fslock);
if (whence == SEEK_CUR && offset == 0) { if (whence == SEEK_CUR && offset == 0) {
return fileSeekPositions[fd]; return fileSeekPositions[fd];
} }

View File

@ -3,12 +3,12 @@
namespace arduino_preprocessor_is_buggy { namespace arduino_preprocessor_is_buggy {
bool serialavailable() { bool serialavailable() {
Threads::Scope locker(getSerialLock()); // Threads::Scope locker(getSerialLock());
return Serial.available(); return Serial.available();
} }
char serialgetch() { char serialgetch() {
Threads::Scope locker(getSerialLock()); // Threads::Scope locker(getSerialLock());
return Serial.read(); return Serial.read();
} }

View File

@ -1,6 +1,5 @@
#include <Arduino.h> #include <Arduino.h>
#include <utility> #include <utility>
#include <TeensyThreads.h>
// cf. https://forum.pjrc.com/threads/41504-Teensy-3-x-multithreading-library-first-release/page6 // cf. https://forum.pjrc.com/threads/41504-Teensy-3-x-multithreading-library-first-release/page6
@ -12,10 +11,12 @@ namespace arduino_preprocessor_is_buggy {
// used for suppressing compiler warnings // used for suppressing compiler warnings
template<class T> void silence(T&&) {}; template<class T> void silence(T&&) {};
/*
inline Threads::Mutex& getSerialLock() { inline Threads::Mutex& getSerialLock() {
static Threads::Mutex serial_lock; static Threads::Mutex serial_lock;
return serial_lock; return serial_lock;
} }
*/
bool serialavailable(); bool serialavailable();
char serialgetch(); char serialgetch();
@ -29,12 +30,12 @@ namespace arduino_preprocessor_is_buggy {
} }
template<class... args_t> void print(args_t... params) { template<class... args_t> void print(args_t... params) {
Threads::Scope locker(getSerialLock()); // Threads::Scope locker(getSerialLock());
silence(expand{ (print_fwd(params), 42)... }); silence(expand{ (print_fwd(params), 42)... });
} }
template<class... args_t> void println(args_t... params) { template<class... args_t> void println(args_t... params) {
Threads::Scope locker(getSerialLock()); // Threads::Scope locker(getSerialLock());
silence(expand{ (print_fwd(params), 42)... }); silence(expand{ (print_fwd(params), 42)... });
Serial.println(); Serial.println();
Serial.flush(); Serial.flush();

View File

@ -1,28 +1,49 @@
#include <Arduino.h> #include <Arduino.h>
#include <TeensyThreads.h>
#include "teensy-speaker.h" #include "teensy-speaker.h"
#include "teensy-println.h" #include "teensy-println.h"
#include <i2c_device.h> #include <Audio.h>
#include <SPI.h>
TeensyAudio audioDriver;
//AudioMixer4 mixer2; //xy=280,253
AudioMixer4 mixer1; //xy=280,175
AudioOutputI2S i2s; //xy=452,189
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" #include "globals.h"
I2CDevice dac = I2CDevice(Master, 0x60, _BIG_ENDIAN); //#define BUFSIZE 4096
//EXTMEM uint32_t toggleBuffer[BUFSIZE]; // cycle counts at which state toggles
//uint16_t headptr, tailptr;
Threads::Mutex togmutex; // Ring buffer that we fill with 44.1kHz data
#define BUFSIZE 4096 #define RINGBUFSIZE 4096
EXTMEM uint32_t toggleBuffer[BUFSIZE]; // cycle counts at which state toggles EXTMEM short sampleRingBuffer[RINGBUFSIZE];
uint16_t headptr, tailptr; volatile uint16_t sampleHeadPtr = 0;
uint32_t lastCycleCount, toggleAtCycle; volatile uint16_t sampleTailPtr = 0;
volatile uint32_t lastFilledTime = 0;
volatile uint32_t lastSampleNum = 0;
bool toggleState = false;
// How many cycles do we run the audio behind? Needs to be more than our bulk // How many cycles do we run the audio behind? Needs to be more than our bulk
// cycle count. // cycle count.
#define CYCLEDELAY 100 //#define CYCLEDELAY 100
TeensySpeaker::TeensySpeaker(uint8_t sda, uint8_t scl) : PhysicalSpeaker() TeensySpeaker::TeensySpeaker(uint8_t sda, uint8_t scl) : PhysicalSpeaker()
{ {
toggleState = false; toggleState = false;
mixerValue = numMixed = 0; mixerValue = numMixed = 0;
Master.begin(1000000); // 100000 or 400000 or 1000000 AudioMemory(8);
} }
TeensySpeaker::~TeensySpeaker() TeensySpeaker::~TeensySpeaker()
@ -31,18 +52,40 @@ TeensySpeaker::~TeensySpeaker()
void TeensySpeaker::begin() void TeensySpeaker::begin()
{ {
lastCycleCount = g_cpu->cycles; mixer1.gain(0, 0.5f); // left channel
lastFilledTime = g_cpu->cycles;
sampleHeadPtr = sampleTailPtr = 0;
toggleState = false; toggleState = false;
memset(toggleBuffer, 0, sizeof(toggleBuffer)); // memset(toggleBuffer, 0, sizeof(toggleBuffer));
headptr = tailptr = 0; // headptr = tailptr = 0;
lastSampleNum = 0;
} }
void TeensySpeaker::toggle(uint32_t c) void TeensySpeaker::toggle(uint32_t c)
{ {
Threads::Scope lock(togmutex); // Figure out when the last time was that we put data in the audio buffer;
// Queue the speaker toggle time; maintainSpeaker will pick it up // then figure out how many audio buffer cycles we have to fill from that
toggleBuffer[tailptr++] = c; tailptr %= BUFSIZE; // CPU time to this one.
#if 1
__disable_irq();
// We expect to have filled to this cycle number...
uint32_t expectedCycleNumber = (float)c * (float)AUDIO_SAMPLE_RATE_EXACT / (float)g_speed;
// 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<audioBufferSamples; i++) {
sampleRingBuffer[sampleTailPtr++] = toggleState ? (32767/2) : (-32767/2); // FIXME: appropriate value?
sampleTailPtr %= RINGBUFSIZE;
}
toggleState = !toggleState;
lastFilledTime = expectedCycleNumber;
__enable_irq();
#endif
} }
void TeensySpeaker::maintainSpeaker(uint32_t c, uint64_t microseconds) void TeensySpeaker::maintainSpeaker(uint32_t c, uint64_t microseconds)
@ -52,25 +95,13 @@ void TeensySpeaker::maintainSpeaker(uint32_t c, uint64_t microseconds)
void TeensySpeaker::maintainSpeaker() void TeensySpeaker::maintainSpeaker()
{ {
// This is called @ SAMPLERATE (8k, as of this writing) and looks for // This is called @ 44100Hz, which is the sample rate for the
// any transitions that have passed before sending data to the DAC. // Teensy4 (#define AUDIO_SAMPLE_RATE_EXACT 44100.0f). We fill a FIFO
// The idea is that this will be called fast enough for the given number // that is then drained by update(). In theory, as long as we don't fall
// of cycles that we run behind, so that the buffer can't overflow. // 128 cycles behind, it should be okay, I think (b/c AUDIO_BLOCK_SAMPLES
// // is 128 on the Teensy 4).
// at 8kHz, that means that .000125 seconds have passed since our last #if 0
// call; where the CPU has executed about 128 instructions in the same
// time. Therefore, it can't have toggled more than 128 times. We're
// also trying to stay 100 cycles "behind", so it's possible that we have
// 228 cycles difference between an event that just fired-and-queued,
// and where we need to catch up to in this loop.
// First, reconcile the "correct" toggleState. We pretend it's currently
// some time in the past, based on our cycle delay. In theory (as above),
// this shouldn't be more than about 228 cycles off (maybe slightly more,
// since we actually run cycles in batches).
uint32_t curTime = g_cpu->cycles - CYCLEDELAY; uint32_t curTime = g_cpu->cycles - CYCLEDELAY;
// And then find any events that should have happened, accounting for them:
togmutex.lock();
while (headptr != tailptr) { while (headptr != tailptr) {
if (curTime >= toggleBuffer[headptr]) { if (curTime >= toggleBuffer[headptr]) {
toggleState = !toggleState; toggleState = !toggleState;
@ -80,11 +111,7 @@ void TeensySpeaker::maintainSpeaker()
break; break;
} }
} }
togmutex.unlock(); #endif
// Now we can safely update the DAC based on the current toggleState
uint16_t v = (toggleState ? 0xFFF : 0x000);
// dac.write((uint8_t) ((v >> 8) & 0xFF), (uint8_t) (v & 0xFF), true);
} }
void TeensySpeaker::beginMixing() void TeensySpeaker::beginMixing()
@ -97,3 +124,54 @@ void TeensySpeaker::mixOutput(uint8_t v)
// unused // unused
} }
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).
block = allocate();
if (block) {
bp = block->data;
#if 1
uint32_t underflow = 0;
for (int i=0; i<AUDIO_BLOCK_SAMPLES; i++) {
static short lastValue = 0;
if (sampleHeadPtr == sampleTailPtr) {
// bp[i] = lastValue; // underflow: just repeat whatever old data we have
// FIXME: trend toward zero, maybe?
bp[i] = lastValue;
underflow++;
} else {
lastValue = sampleRingBuffer[sampleHeadPtr++];
bp[i] = lastValue;
sampleHeadPtr %= RINGBUFSIZE;
}
}
#else
// Fill in the AUDIO_BLOCK_SAMPLES samples of data, pull them from the FIFO
memset(bp, 0, AUDIO_BLOCK_SAMPLES * sizeof(short));
#endif
if (underflow) {
println("U ", underflow);
}
transmit(block, 0);
release(block);
}
#if 0
if (sampleHeadPtr == sampleTailPtr) {
// The FIFO is empty, so reset...
if (g_cpu) {
lastFilledTime = g_cpu->cycles;
} else {
lastFilledTime = 0;
}
// FIXME:
// lastSampleNum = 0;
}
#endif
}

View File

@ -1,11 +1,19 @@
#ifndef __TEENSY_SPEAKER_H #ifndef __TEENSY_SPEAKER_H
#define __TEENSY_SPEAKER_H #define __TEENSY_SPEAKER_H
#include <AudioStream.h>
#include "physicalspeaker.h" #include "physicalspeaker.h"
#include <MCP492X.h> #include <MCP492X.h>
#define SAMPLERATE 44100 #define SAMPLERATE 44100
class TeensyAudio : public AudioStream {
public:
TeensyAudio(void) : AudioStream(0, NULL) {}
virtual void update(void);
};
class TeensySpeaker : public PhysicalSpeaker { class TeensySpeaker : public PhysicalSpeaker {
public: public:
TeensySpeaker(uint8_t sda, uint8_t scl); TeensySpeaker(uint8_t sda, uint8_t scl);
@ -21,7 +29,7 @@ class TeensySpeaker : public PhysicalSpeaker {
virtual void mixOutput(uint8_t v); virtual void mixOutput(uint8_t v);
private: private:
bool toggleState; // bool toggleState;
uint32_t mixerValue; uint32_t mixerValue;
uint8_t numMixed; uint8_t numMixed;

View File

@ -1,7 +1,7 @@
#include <Arduino.h> #include <Arduino.h>
#include <Audio.h>
#include <SPI.h> #include <SPI.h>
#include <TimeLib.h> #include <TimeLib.h>
#include <TeensyThreads.h>
#include <Bounce2.h> #include <Bounce2.h>
#include "bios.h" #include "bios.h"
#include "cpu.h" #include "cpu.h"
@ -16,7 +16,7 @@
#include "teensy-prefs.h" #include "teensy-prefs.h"
#include "teensy-println.h" #include "teensy-println.h"
#define DEBUG_TIMING //#define DEBUG_TIMING
#define THREADED if (1) #define THREADED if (1)
@ -69,7 +69,7 @@ void onKeyrelease(int unicode)
void setup() void setup()
{ {
Serial.begin(230400); Serial.begin(230400);
#if 0 #if 1
// Wait for USB serial connection before booting while debugging // Wait for USB serial connection before booting while debugging
while (!Serial) { while (!Serial) {
yield(); yield();
@ -165,9 +165,9 @@ void setup()
println("free-running"); println("free-running");
Serial.flush(); Serial.flush();
threads.setMicroTimer(); // use a 100uS timer instead of a 1mS timer // threads.setMicroTimer(); // use a 100uS timer instead of a 1mS timer
// threads.setSliceMicros(5); // threads.setSliceMicros(5);
threads.addThread(runDebouncer); // threads.addThread(runDebouncer);
} }
// FIXME: move these memory-related functions elsewhere... // FIXME: move these memory-related functions elsewhere...
@ -228,43 +228,13 @@ void biosInterrupt()
g_keyboard->maintainKeyboard(); g_keyboard->maintainKeyboard();
} }
void runSpeaker(uint32_t now)
{
static uint32_t spk_nextResetMicros = 0;
static uint32_t spk_refreshCount = 0;
static uint32_t spk_microsAtStart = micros();
static uint32_t spk_microsForNext = 0;
THREADED {
if (now >= spk_microsForNext) {
spk_refreshCount++;
spk_microsForNext = spk_microsAtStart + ((float)1000000.0*((float)spk_refreshCount/(float)SAMPLERATE));
((TeensySpeaker *)g_speaker)->maintainSpeaker();
}
if (now >= spk_nextResetMicros) {
#ifdef DEBUG_TIMING
float pct = (100.0 * (float)spk_refreshCount) / (float)SAMPLERATE;
sprintf(debugBuf, "Speaker running at %f%% [%lu]", pct, spk_refreshCount);
println(debugBuf);
#endif
spk_microsAtStart = now;
spk_refreshCount = 0;
spk_nextResetMicros = spk_microsAtStart + 1000000;
spk_microsForNext = spk_microsAtStart + ((float)1000000.0*((float)spk_refreshCount/(float)SAMPLERATE));
}
}
}
void runMaintenance(uint32_t now) void runMaintenance(uint32_t now)
{ {
static uint32_t nextRuntime = 0; static uint32_t nextRuntime = 0;
THREADED { THREADED {
if (now >= nextRuntime) { if (now >= nextRuntime) {
nextRuntime = now + 100000000; // FIXME: what's a good time here nextRuntime = now + 100000; // FIXME: what's a good time here? 1/10 sec?
if (!resetButtonDebouncer.read()) { if (!resetButtonDebouncer.read()) {
// This is the BIOS interrupt. We immediately act on it. // This is the BIOS interrupt. We immediately act on it.
@ -334,7 +304,8 @@ void runDebouncer()
nextRuntime = millis() + 10; nextRuntime = millis() + 10;
resetButtonDebouncer.update(); resetButtonDebouncer.update();
} else { } else {
threads.yield(); yield();
// threads.yield();
} }
} }
} }
@ -373,7 +344,6 @@ void loop()
uint32_t now = micros(); uint32_t now = micros();
runCPU(now); runCPU(now);
runDisplay(now); runDisplay(now);
runSpeaker(now);
runMaintenance(now); runMaintenance(now);
} }