mirror of
https://github.com/JorjBauer/aiie.git
synced 2024-12-26 23:29:21 +00:00
removed threads; bones for audio in place
This commit is contained in:
parent
fb7a64a22f
commit
1dcbee91ba
5
bios.cpp
5
bios.cpp
@ -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;
|
||||
}
|
||||
|
@ -55,9 +55,4 @@ extern bool g_invertPaddleY;
|
||||
|
||||
extern char debugBuf[255];
|
||||
|
||||
#ifdef TEENSYDUINO
|
||||
#include <TeensyThreads.h>
|
||||
extern Threads::Mutex spi_lock;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -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();}
|
||||
|
||||
|
@ -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];
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user