mirror of
https://github.com/polpo/picogus.git
synced 2026-10-25 21:40:16 +00:00
Implement audio_fifo as lock-free SPSC queue to not require disabling interrupts
This commit is contained in:
1 parent
d0510ad50c
commit
cee1081a03
4 files changed
+36
-45
No files matched your search
+12
-14
@@ -38,14 +38,12 @@ void fifo_init(audio_fifo_t *fifo) {
|
||||
fifo->state = FIFO_STATE_STOPPED;
|
||||
fifo->write_idx = 0;
|
||||
fifo->read_idx = 0;
|
||||
fifo->samples_in_fifo = 0;
|
||||
}
|
||||
|
||||
// Reset the FIFO to empty state
|
||||
void fifo_reset(audio_fifo_t *fifo) {
|
||||
fifo->state = FIFO_STATE_STOPPED;
|
||||
fifo->write_idx = fifo->read_idx;
|
||||
fifo->samples_in_fifo = 0;
|
||||
}
|
||||
|
||||
// Add multiple samples to the FIFO
|
||||
@@ -56,22 +54,23 @@ bool fifo_add_samples(audio_fifo_t *fifo, const audio_sample_t *samples_buffer,
|
||||
}
|
||||
|
||||
// Check if there's enough space in the FIFO
|
||||
uint32_t available_space = AUDIO_FIFO_SIZE - fifo->samples_in_fifo;
|
||||
uint32_t level = fifo->write_idx - fifo->read_idx;
|
||||
uint32_t available_space = AUDIO_FIFO_SIZE - level;
|
||||
if (num_samples_to_add > available_space) {
|
||||
return false; // Not enough space for all requested samples
|
||||
}
|
||||
|
||||
// Determine how many samples can be written before wrap-around
|
||||
// AUDIO_FIFO_SIZE is the total capacity. write_idx is 0 to AUDIO_FIFO_SIZE-1.
|
||||
uint32_t samples_to_end = AUDIO_FIFO_SIZE - fifo->write_idx;
|
||||
uint32_t phy_write = fifo->write_idx & AUDIO_FIFO_BITS;
|
||||
uint32_t samples_to_end = AUDIO_FIFO_SIZE - phy_write;
|
||||
|
||||
if (num_samples_to_add <= samples_to_end) {
|
||||
// All samples fit without wrapping around the physical end of the buffer
|
||||
memcpy(&fifo->buffer[fifo->write_idx], samples_buffer, num_samples_to_add * sizeof(audio_sample_t));
|
||||
memcpy(&fifo->buffer[phy_write], samples_buffer, num_samples_to_add * sizeof(audio_sample_t));
|
||||
} else {
|
||||
// Samples will wrap around the physical end of the buffer
|
||||
// Part 1: Copy samples up to the end of the physical buffer
|
||||
memcpy(&fifo->buffer[fifo->write_idx], samples_buffer, samples_to_end * sizeof(audio_sample_t));
|
||||
memcpy(&fifo->buffer[phy_write], samples_buffer, samples_to_end * sizeof(audio_sample_t));
|
||||
|
||||
// Part 2: Copy remaining samples from the beginning of the physical buffer
|
||||
uint32_t remaining_samples = num_samples_to_add - samples_to_end;
|
||||
@@ -79,9 +78,8 @@ bool fifo_add_samples(audio_fifo_t *fifo, const audio_sample_t *samples_buffer,
|
||||
}
|
||||
|
||||
// Update FIFO state
|
||||
fifo->write_idx = (fifo->write_idx + num_samples_to_add) & (AUDIO_FIFO_BITS);
|
||||
fifo->samples_in_fifo += num_samples_to_add;
|
||||
if (fifo->samples_in_fifo >= AUDIO_FIFO_START_THRESHOLD) {
|
||||
fifo->write_idx += num_samples_to_add;
|
||||
if (fifo->write_idx - fifo->read_idx >= AUDIO_FIFO_START_THRESHOLD) {
|
||||
fifo->state = FIFO_STATE_RUNNING;
|
||||
}
|
||||
return true;
|
||||
@@ -93,11 +91,11 @@ uint32_t fifo_take_samples(audio_fifo_t *fifo, uint32_t num_samples) {
|
||||
if (fifo->state == FIFO_STATE_STOPPED) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t samples_returned = MIN(fifo->samples_in_fifo, num_samples);
|
||||
fifo->read_idx = (fifo->read_idx + samples_returned) & (AUDIO_FIFO_BITS);
|
||||
fifo->samples_in_fifo -= samples_returned;
|
||||
uint32_t level = fifo->write_idx - fifo->read_idx;
|
||||
uint32_t samples_returned = MIN(level, num_samples);
|
||||
fifo->read_idx += samples_returned;
|
||||
// Only go back to stopped once fifo is fully exhausted
|
||||
if (fifo->samples_in_fifo == 0) {
|
||||
if (fifo->write_idx == fifo->read_idx) {
|
||||
fifo->state = FIFO_STATE_STOPPED;
|
||||
}
|
||||
return samples_returned;
|
||||
|
||||
+15
-20
@@ -25,7 +25,6 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "hardware/sync.h"
|
||||
|
||||
// Configuration
|
||||
#define AUDIO_FIFO_SIZE 4096 // Must be power of 2
|
||||
@@ -48,14 +47,15 @@ typedef enum {
|
||||
} fifo_state_t;
|
||||
|
||||
// FIFO structure
|
||||
// Lock-free SPSC: write_idx is only modified by the producer, read_idx only
|
||||
// by the consumer. Both are unmasked uint32_t counters — mask with
|
||||
// AUDIO_FIFO_BITS when indexing into buffer[]. Level = write_idx - read_idx
|
||||
// (unsigned subtraction handles wrap correctly).
|
||||
typedef struct {
|
||||
audio_sample_t buffer[AUDIO_FIFO_SIZE];
|
||||
volatile uint32_t write_idx;
|
||||
volatile uint32_t read_idx;
|
||||
volatile fifo_state_t state;
|
||||
// mutex_t mutex;
|
||||
// semaphore_t data_available;
|
||||
volatile uint32_t samples_in_fifo;
|
||||
} audio_fifo_t;
|
||||
|
||||
// FIFO management functions (implemented in audio_fifo.c)
|
||||
@@ -66,31 +66,26 @@ bool fifo_add_samples(audio_fifo_t *fifo, const audio_sample_t *samples_buffer,
|
||||
|
||||
// Inline functions for performance
|
||||
static inline uint32_t fifo_level(audio_fifo_t *fifo) {
|
||||
return fifo->samples_in_fifo;
|
||||
return fifo->write_idx - fifo->read_idx;
|
||||
}
|
||||
|
||||
static inline bool fifo_add_sample(audio_fifo_t *fifo, audio_sample_t sample) {
|
||||
if (fifo->samples_in_fifo == AUDIO_FIFO_SIZE) {
|
||||
uint32_t level = fifo->write_idx - fifo->read_idx;
|
||||
if (level == AUDIO_FIFO_SIZE) {
|
||||
return false;
|
||||
}
|
||||
fifo->buffer[fifo->write_idx] = sample;
|
||||
fifo->write_idx = (fifo->write_idx + 1) & AUDIO_FIFO_BITS;
|
||||
// Disable interrupts around the counter increment: the PWM ISR (consumer)
|
||||
// runs on the same core and its samples_in_fifo-- can preempt the
|
||||
// load-add-store here, causing a lost decrement and an inflated count.
|
||||
uint32_t irq_save = save_and_disable_interrupts();
|
||||
fifo->samples_in_fifo++;
|
||||
if (fifo->samples_in_fifo >= AUDIO_FIFO_START_THRESHOLD) {
|
||||
fifo->buffer[fifo->write_idx & AUDIO_FIFO_BITS] = sample;
|
||||
fifo->write_idx++;
|
||||
if (level + 1 >= AUDIO_FIFO_START_THRESHOLD) {
|
||||
fifo->state = FIFO_STATE_RUNNING;
|
||||
}
|
||||
restore_interrupts(irq_save);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t fifo_take_samples(audio_fifo_t *fifo, uint32_t num_samples);
|
||||
|
||||
static inline uint32_t fifo_free_space(audio_fifo_t *fifo) {
|
||||
return AUDIO_FIFO_SIZE - fifo->samples_in_fifo;
|
||||
return AUDIO_FIFO_SIZE - (fifo->write_idx - fifo->read_idx);
|
||||
}
|
||||
|
||||
// Inline version of fifo_take_samples for performance-critical code
|
||||
@@ -98,10 +93,10 @@ static inline uint32_t fifo_take_samples_inline(audio_fifo_t *fifo, uint32_t num
|
||||
if (fifo->state == FIFO_STATE_STOPPED) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t samples_returned = fifo->samples_in_fifo < num_samples ? fifo->samples_in_fifo : num_samples;
|
||||
fifo->read_idx = (fifo->read_idx + samples_returned) & AUDIO_FIFO_BITS;
|
||||
fifo->samples_in_fifo -= samples_returned;
|
||||
if (fifo->samples_in_fifo == 0) {
|
||||
uint32_t level = fifo->write_idx - fifo->read_idx;
|
||||
uint32_t samples_returned = level < num_samples ? level : num_samples;
|
||||
fifo->read_idx += samples_returned;
|
||||
if (fifo->write_idx == fifo->read_idx) {
|
||||
fifo->state = FIFO_STATE_STOPPED;
|
||||
}
|
||||
return samples_returned;
|
||||
|
||||
+1
-1
@@ -556,7 +556,7 @@ uint8_t cdrom_get_subq(cdrom_t *dev,uint8_t *b) {
|
||||
* empty or seek_pos is at the start of the track. */
|
||||
#if USE_CD_AUDIO_FIFO
|
||||
{
|
||||
uint32_t buffered_sectors = dev->audio_fifo.samples_in_fifo / (SAMPLES_PER_SECTOR * 2);
|
||||
uint32_t buffered_sectors = (dev->audio_fifo.write_idx - dev->audio_fifo.read_idx) / (SAMPLES_PER_SECTOR * 2);
|
||||
uint32_t report_pos = (buffered_sectors < dev->seek_pos) ?
|
||||
(dev->seek_pos - buffered_sectors) : 0;
|
||||
dev->ops->get_subchannel(dev, report_pos, &subc);
|
||||
|
||||
+8
-10
@@ -164,24 +164,22 @@ void audio_sample_handler(void) {
|
||||
#ifdef CDROM
|
||||
// Read L+R sample pair directly from FIFO using read_idx before advancing.
|
||||
// Must request exactly 2 (stereo pair) or skip — never read partial pairs.
|
||||
if (cd_fifo->state != FIFO_STATE_STOPPED && cd_fifo->samples_in_fifo >= 2) {
|
||||
if (cd_fifo->state != FIFO_STATE_STOPPED && cd_fifo->write_idx - cd_fifo->read_idx >= 2) {
|
||||
uint32_t idx = cd_fifo->read_idx;
|
||||
sample_l += scale_sample(cd_fifo->buffer[idx], cd_audio_volume, 0);
|
||||
sample_l += scale_sample(cd_fifo->buffer[idx & AUDIO_FIFO_BITS], cd_audio_volume, 0);
|
||||
sample_r += scale_sample(cd_fifo->buffer[(idx + 1) & AUDIO_FIFO_BITS], cd_audio_volume, 0);
|
||||
cd_fifo->read_idx = (idx + 2) & AUDIO_FIFO_BITS;
|
||||
cd_fifo->samples_in_fifo -= 2;
|
||||
if (cd_fifo->samples_in_fifo == 0) cd_fifo->state = FIFO_STATE_STOPPED;
|
||||
cd_fifo->read_idx = idx + 2;
|
||||
if (cd_fifo->write_idx == cd_fifo->read_idx) cd_fifo->state = FIFO_STATE_STOPPED;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Read OPL stereo pair from FIFO — always consume 2 samples (L then R).
|
||||
if (opl_out_fifo.state != FIFO_STATE_STOPPED && opl_out_fifo.samples_in_fifo >= 2) {
|
||||
if (opl_out_fifo.state != FIFO_STATE_STOPPED && opl_out_fifo.write_idx - opl_out_fifo.read_idx >= 2) {
|
||||
uint32_t idx = opl_out_fifo.read_idx;
|
||||
sample_l += opl_out_fifo.buffer[idx];
|
||||
sample_l += opl_out_fifo.buffer[idx & AUDIO_FIFO_BITS];
|
||||
sample_r += opl_out_fifo.buffer[(idx + 1) & AUDIO_FIFO_BITS];
|
||||
opl_out_fifo.read_idx = (idx + 2) & AUDIO_FIFO_BITS;
|
||||
opl_out_fifo.samples_in_fifo -= 2;
|
||||
if (opl_out_fifo.samples_in_fifo == 0) opl_out_fifo.state = FIFO_STATE_STOPPED;
|
||||
opl_out_fifo.read_idx = idx + 2;
|
||||
if (opl_out_fifo.write_idx == opl_out_fifo.read_idx) opl_out_fifo.state = FIFO_STATE_STOPPED;
|
||||
}
|
||||
|
||||
const sample_pair clamped = {.data16 = {
|
||||
|
||||
Reference in new issue
Block a user