mirror of https://github.com/JorjBauer/aiie.git
playing with audio output
This commit is contained in:
parent
5c8e1de195
commit
9b21e88296
|
@ -1,11 +1,13 @@
|
|||
#include <Arduino.h>
|
||||
#include <softspi.h>
|
||||
#include <TeensyThreads.h>
|
||||
#include "teensy-speaker.h"
|
||||
#include "teensy-println.h"
|
||||
#include <i2c_device.h>
|
||||
|
||||
#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()
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "physicalspeaker.h"
|
||||
#include <MCP492X.h>
|
||||
|
||||
#define SAMPLERATE 8000
|
||||
#define SAMPLERATE 4000
|
||||
|
||||
class TeensySpeaker : public PhysicalSpeaker {
|
||||
public:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue