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

View File

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

View File

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

View File

@ -2,11 +2,8 @@
#include <wchar.h>
#include "teensy-filemanager.h"
#include <string.h> // strcpy
#include <TeensyThreads.h>
#include "teensy-println.h"
Threads::Mutex fslock;
TeensyFileManager::TeensyFileManager()
{
numCached = 0;
@ -23,8 +20,6 @@ TeensyFileManager::~TeensyFileManager()
int8_t TeensyFileManager::openFile(const char *name)
{
Threads::Scope locker(fslock);
if (cacheFd != -1) {
cacheFile.close();
cacheFd = -1;
@ -56,7 +51,6 @@ int8_t TeensyFileManager::openFile(const char *name)
void TeensyFileManager::closeFile(int8_t fd)
{
Threads::Scope locker(fslock);
if (cacheFd != -1) {
cacheFile.close();
cacheFd = -1;
@ -92,8 +86,6 @@ void TeensyFileManager::closeDir()
// suffix may be comma-separated
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
if (startIdx == 0 || !outerDir) {
if (outerDir)
@ -220,7 +212,6 @@ void TeensyFileManager::seekToEnd(int8_t fd)
int TeensyFileManager::write(int8_t fd, const void *buf, int nbyte)
{
Threads::Scope locker(fslock);
// open, seek, write, close.
if (fd < 0 || fd >= numCached) {
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)
{
Threads::Scope locker(fslock);
// open, seek, read, close.
if (fd < 0 || fd >= numCached) {
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)
{
Threads::Scope locker(fslock);
if (whence == SEEK_CUR && offset == 0) {
return fileSeekPositions[fd];
}

View File

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

View File

@ -1,6 +1,5 @@
#include <Arduino.h>
#include <utility>
#include <TeensyThreads.h>
// 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
template<class T> void silence(T&&) {};
/*
inline Threads::Mutex& getSerialLock() {
static Threads::Mutex serial_lock;
return serial_lock;
}
*/
bool serialavailable();
char serialgetch();
@ -29,12 +30,12 @@ namespace arduino_preprocessor_is_buggy {
}
template<class... args_t> void print(args_t... params) {
Threads::Scope locker(getSerialLock());
// Threads::Scope locker(getSerialLock());
silence(expand{ (print_fwd(params), 42)... });
}
template<class... args_t> void println(args_t... params) {
Threads::Scope locker(getSerialLock());
// Threads::Scope locker(getSerialLock());
silence(expand{ (print_fwd(params), 42)... });
Serial.println();
Serial.flush();

View File

@ -1,28 +1,49 @@
#include <Arduino.h>
#include <TeensyThreads.h>
#include "teensy-speaker.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"
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;
#define BUFSIZE 4096
EXTMEM uint32_t toggleBuffer[BUFSIZE]; // cycle counts at which state toggles
uint16_t headptr, tailptr;
uint32_t lastCycleCount, toggleAtCycle;
// 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;
volatile uint32_t lastSampleNum = 0;
bool toggleState = false;
// How many cycles do we run the audio behind? Needs to be more than our bulk
// cycle count.
#define CYCLEDELAY 100
//#define CYCLEDELAY 100
TeensySpeaker::TeensySpeaker(uint8_t sda, uint8_t scl) : PhysicalSpeaker()
{
toggleState = false;
mixerValue = numMixed = 0;
Master.begin(1000000); // 100000 or 400000 or 1000000
AudioMemory(8);
}
TeensySpeaker::~TeensySpeaker()
@ -31,18 +52,40 @@ TeensySpeaker::~TeensySpeaker()
void TeensySpeaker::begin()
{
lastCycleCount = g_cpu->cycles;
mixer1.gain(0, 0.5f); // left channel
lastFilledTime = g_cpu->cycles;
sampleHeadPtr = sampleTailPtr = 0;
toggleState = false;
memset(toggleBuffer, 0, sizeof(toggleBuffer));
headptr = tailptr = 0;
// memset(toggleBuffer, 0, sizeof(toggleBuffer));
// headptr = tailptr = 0;
lastSampleNum = 0;
}
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;
// 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...
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)
@ -52,25 +95,13 @@ void TeensySpeaker::maintainSpeaker(uint32_t c, uint64_t microseconds)
void TeensySpeaker::maintainSpeaker()
{
// 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
// of cycles that we run behind, so that the buffer can't overflow.
//
// at 8kHz, that means that .000125 seconds have passed since our last
// 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).
// This is called @ 44100Hz, which is the sample rate for the
// Teensy4 (#define AUDIO_SAMPLE_RATE_EXACT 44100.0f). We fill a FIFO
// that is then drained by update(). In theory, as long as we don't fall
// 128 cycles behind, it should be okay, I think (b/c AUDIO_BLOCK_SAMPLES
// is 128 on the Teensy 4).
#if 0
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;
@ -80,11 +111,7 @@ void TeensySpeaker::maintainSpeaker()
break;
}
}
togmutex.unlock();
// 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);
#endif
}
void TeensySpeaker::beginMixing()
@ -97,3 +124,54 @@ void TeensySpeaker::mixOutput(uint8_t v)
// 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
#define __TEENSY_SPEAKER_H
#include <AudioStream.h>
#include "physicalspeaker.h"
#include <MCP492X.h>
#define SAMPLERATE 44100
class TeensyAudio : public AudioStream {
public:
TeensyAudio(void) : AudioStream(0, NULL) {}
virtual void update(void);
};
class TeensySpeaker : public PhysicalSpeaker {
public:
TeensySpeaker(uint8_t sda, uint8_t scl);
@ -21,7 +29,7 @@ class TeensySpeaker : public PhysicalSpeaker {
virtual void mixOutput(uint8_t v);
private:
bool toggleState;
// bool toggleState;
uint32_t mixerValue;
uint8_t numMixed;

View File

@ -1,7 +1,7 @@
#include <Arduino.h>
#include <Audio.h>
#include <SPI.h>
#include <TimeLib.h>
#include <TeensyThreads.h>
#include <Bounce2.h>
#include "bios.h"
#include "cpu.h"
@ -16,7 +16,7 @@
#include "teensy-prefs.h"
#include "teensy-println.h"
#define DEBUG_TIMING
//#define DEBUG_TIMING
#define THREADED if (1)
@ -69,7 +69,7 @@ void onKeyrelease(int unicode)
void setup()
{
Serial.begin(230400);
#if 0
#if 1
// Wait for USB serial connection before booting while debugging
while (!Serial) {
yield();
@ -165,9 +165,9 @@ void setup()
println("free-running");
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.addThread(runDebouncer);
// threads.addThread(runDebouncer);
}
// FIXME: move these memory-related functions elsewhere...
@ -228,43 +228,13 @@ void biosInterrupt()
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)
{
static uint32_t nextRuntime = 0;
THREADED {
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()) {
// This is the BIOS interrupt. We immediately act on it.
@ -334,7 +304,8 @@ void runDebouncer()
nextRuntime = millis() + 10;
resetButtonDebouncer.update();
} else {
threads.yield();
yield();
// threads.yield();
}
}
}
@ -373,7 +344,6 @@ void loop()
uint32_t now = micros();
runCPU(now);
runDisplay(now);
runSpeaker(now);
runMaintenance(now);
}