Buffering of audio
This commit is contained in:
parent
ad0fb039fc
commit
82bab0698b
@ -26,6 +26,12 @@ double phase = 0.0;
|
|||||||
unsigned long lastNoteChangeTime = 0;
|
unsigned long lastNoteChangeTime = 0;
|
||||||
// ---
|
// ---
|
||||||
|
|
||||||
|
// Ring Buffer
|
||||||
|
int16_t audioBuffer[AUDIO_BUFFER_SIZE];
|
||||||
|
int audioHead = 0;
|
||||||
|
int audioTail = 0;
|
||||||
|
bool audioBuffering = true;
|
||||||
|
|
||||||
void setupAudio() {
|
void setupAudio() {
|
||||||
// Configure I2S pins
|
// Configure I2S pins
|
||||||
i2s.setBCLK(I2S_BCLK_PIN);
|
i2s.setBCLK(I2S_BCLK_PIN);
|
||||||
@ -43,8 +49,10 @@ void setupAudio() {
|
|||||||
|
|
||||||
// Initialize the portable synth engine
|
// Initialize the portable synth engine
|
||||||
globalSynth = new SynthEngine(SAMPLE_RATE);
|
globalSynth = new SynthEngine(SAMPLE_RATE);
|
||||||
|
if (globalSynth) {
|
||||||
globalSynth->loadPreset(2);
|
globalSynth->loadPreset(2);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void loopAudio() {
|
void loopAudio() {
|
||||||
unsigned long now = millis();
|
unsigned long now = millis();
|
||||||
@ -71,38 +79,43 @@ void loopAudio() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const int BATCH_SIZE = 16;
|
// Produce samples in a cyclic buffer
|
||||||
|
int nextHead = (audioHead + 1) % AUDIO_BUFFER_SIZE;
|
||||||
// Ensure we don't generate samples faster than the I2S can consume them.
|
if (nextHead != audioTail) {
|
||||||
// We write 2 samples (Left + Right) for every 1 synth sample.
|
if (audioHead == audioTail) {
|
||||||
if (i2s.availableForWrite() < BATCH_SIZE * 2) {
|
audioBuffering = true;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
int16_t sample = 0;
|
||||||
// Process a small batch of samples
|
|
||||||
static int16_t samples[BATCH_SIZE];
|
|
||||||
|
|
||||||
// Generate sound samples
|
|
||||||
if (globalSynth) {
|
if (globalSynth) {
|
||||||
// using synth engine
|
globalSynth->process(&sample, 1);
|
||||||
globalSynth->process(samples, BATCH_SIZE);
|
|
||||||
} else {
|
} else {
|
||||||
// using fallback sawtooth
|
|
||||||
for (int i = 0; i < BATCH_SIZE; ++i) {
|
|
||||||
if (currentFrequency > 0) {
|
if (currentFrequency > 0) {
|
||||||
//samples[i] = (int16_t)(sin(phase) * AMPLITUDE);
|
|
||||||
phase += 2.0 * M_PI * currentFrequency / SAMPLE_RATE;
|
phase += 2.0 * M_PI * currentFrequency / SAMPLE_RATE;
|
||||||
if (phase >= 2.0 * M_PI) phase -= 2.0 * M_PI;
|
if (phase >= 2.0 * M_PI) phase -= 2.0 * M_PI;
|
||||||
samples[i] = phase * 0.1f * AMPLITUDE;
|
sample = phase * 0.1f * AMPLITUDE;
|
||||||
} else {
|
} else {
|
||||||
samples[i] = 0;
|
sample = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
audioBuffer[audioHead] = sample;
|
||||||
|
audioHead = nextHead;
|
||||||
|
} else {
|
||||||
|
audioBuffering = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// write out stereo samples
|
// Consume samples from this buffer whenever there is capacity in i2s
|
||||||
for (int i = 0; i < BATCH_SIZE; ++i) {
|
while (!audioBuffering && audioHead != audioTail) {
|
||||||
i2s.write(samples[i]);
|
if (i2s.availableForWrite() < 2) {
|
||||||
i2s.write(samples[i]);
|
break;
|
||||||
}
|
}
|
||||||
|
int16_t s = audioBuffer[audioTail];
|
||||||
|
i2s.write(s);
|
||||||
|
i2s.write(s);
|
||||||
|
audioTail = (audioTail + 1) % AUDIO_BUFFER_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update usage stats
|
||||||
|
int usage = audioHead - audioTail;
|
||||||
|
if (usage < 0) usage += AUDIO_BUFFER_SIZE;
|
||||||
|
audioBufferUsage = usage;
|
||||||
}
|
}
|
||||||
@ -2,6 +2,8 @@
|
|||||||
#include "SharedState.h"
|
#include "SharedState.h"
|
||||||
#include "synth_engine.h"
|
#include "synth_engine.h"
|
||||||
|
|
||||||
|
volatile int audioBufferUsage = 0;
|
||||||
|
|
||||||
volatile unsigned long lastLoop0Time = 0;
|
volatile unsigned long lastLoop0Time = 0;
|
||||||
volatile unsigned long lastLoop1Time = 0;
|
volatile unsigned long lastLoop1Time = 0;
|
||||||
volatile bool watchdogActive = false;
|
volatile bool watchdogActive = false;
|
||||||
|
|||||||
@ -3,6 +3,9 @@
|
|||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#define AUDIO_BUFFER_SIZE 512
|
||||||
|
extern volatile int audioBufferUsage;
|
||||||
|
|
||||||
extern volatile unsigned long lastLoop0Time;
|
extern volatile unsigned long lastLoop0Time;
|
||||||
extern volatile unsigned long lastLoop1Time;
|
extern volatile unsigned long lastLoop1Time;
|
||||||
extern volatile bool watchdogActive;
|
extern volatile bool watchdogActive;
|
||||||
|
|||||||
26
UIThread.cpp
26
UIThread.cpp
@ -182,7 +182,7 @@ void handleInput() {
|
|||||||
void drawUI() {
|
void drawUI() {
|
||||||
display.clearDisplay();
|
display.clearDisplay();
|
||||||
|
|
||||||
if (globalSynth) {
|
{
|
||||||
// Copy grid state to local buffer to minimize lock time
|
// Copy grid state to local buffer to minimize lock time
|
||||||
struct MiniCell {
|
struct MiniCell {
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
@ -191,7 +191,7 @@ void drawUI() {
|
|||||||
};
|
};
|
||||||
MiniCell gridCopy[SynthEngine::GRID_W][SynthEngine::GRID_H];
|
MiniCell gridCopy[SynthEngine::GRID_W][SynthEngine::GRID_H];
|
||||||
|
|
||||||
{
|
if (globalSynth) {
|
||||||
SynthLockGuard<SynthMutex> lock(globalSynth->gridMutex);
|
SynthLockGuard<SynthMutex> lock(globalSynth->gridMutex);
|
||||||
for(int x=0; x<SynthEngine::GRID_W; ++x) {
|
for(int x=0; x<SynthEngine::GRID_W; ++x) {
|
||||||
for(int y=0; y<SynthEngine::GRID_H; ++y) {
|
for(int y=0; y<SynthEngine::GRID_H; ++y) {
|
||||||
@ -202,9 +202,9 @@ void drawUI() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int cellW = 10;
|
int cellW = 8;
|
||||||
int cellH = 5;
|
int cellH = 5;
|
||||||
int marginX = (SCREEN_WIDTH - (SynthEngine::GRID_W * cellW)) / 2;
|
int marginX = 2;
|
||||||
int marginY = (SCREEN_HEIGHT - (SynthEngine::GRID_H * cellH)) / 2;
|
int marginY = (SCREEN_HEIGHT - (SynthEngine::GRID_H * cellH)) / 2;
|
||||||
|
|
||||||
for(int x=0; x<SynthEngine::GRID_W; ++x) {
|
for(int x=0; x<SynthEngine::GRID_W; ++x) {
|
||||||
@ -245,6 +245,24 @@ void drawUI() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw Buffer Stats
|
||||||
|
int barX = 110;
|
||||||
|
int barY = 10;
|
||||||
|
int barW = 8;
|
||||||
|
int barH = 30;
|
||||||
|
|
||||||
|
display.drawRect(barX, barY, barW, barH, SSD1306_WHITE);
|
||||||
|
|
||||||
|
int usage = audioBufferUsage;
|
||||||
|
int fillH = (usage * (barH - 2)) / AUDIO_BUFFER_SIZE;
|
||||||
|
if (fillH > barH - 2) fillH = barH - 2;
|
||||||
|
if (fillH < 0) fillH = 0;
|
||||||
|
|
||||||
|
display.fillRect(barX + 1, barY + (barH - 1) - fillH, barW - 2, fillH, SSD1306_WHITE);
|
||||||
|
|
||||||
|
// display.setCursor(barX - 4, barY + barH + 4);
|
||||||
|
// display.print(usage);
|
||||||
}
|
}
|
||||||
|
|
||||||
display.display();
|
display.display();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user