mirror of
https://github.com/polpo/picogus.git
synced 2026-10-25 21:40:16 +00:00
Convert audio FIFOs to stereo sample pairs
This commit is contained in:
1 parent
db355ec4d6
commit
6afc44b2ad
4 files changed
+97
-80
No files matched your search
+13
-8
@@ -26,20 +26,25 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Configuration
|
||||
#define AUDIO_FIFO_SIZE 4096 // Must be power of 2
|
||||
// Needs to be > 2*SAMPLES_PER_SECTOR (1176) to absorb
|
||||
// slow FAT/USB sector reads without underrun (~93ms at 44100Hz)
|
||||
// Packed stereo pair: left in low 16 bits, right in high 16 bits.
|
||||
typedef union {
|
||||
uint32_t data32;
|
||||
int16_t data16[2];
|
||||
} sample_pair;
|
||||
|
||||
// Each FIFO entry is a packed stereo pair.
|
||||
typedef sample_pair audio_sample_t;
|
||||
|
||||
// CD audio FIFO size in stereo pairs — must be > SAMPLES_PER_SECTOR/2 (588
|
||||
// stereo frames per CD sector) to absorb slow USB/FAT reads.
|
||||
#define AUDIO_FIFO_SIZE 2048
|
||||
#define AUDIO_FIFO_BITS (AUDIO_FIFO_SIZE - 1)
|
||||
#define AUDIO_FIFO_START_THRESHOLD 512 // Start playback after 512 samples (~11ms); lower = faster post-seek response
|
||||
#define AUDIO_FIFO_START_THRESHOLD 256
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Audio sample type - adjust as needed for your audio format
|
||||
typedef int16_t audio_sample_t;
|
||||
|
||||
// FIFO state
|
||||
typedef enum {
|
||||
FIFO_STATE_STOPPED,
|
||||
|
||||
+46
-46
@@ -281,11 +281,11 @@ bool cdrom_audio_callback(cdrom_t *dev, uint32_t len) {
|
||||
}
|
||||
|
||||
// --- Fill audio_fifo using data from audio_sector_buffer, reading new sectors as needed ---
|
||||
// Loop until FIFO has 'len' samples, or is full, or an error/EoT occurs.
|
||||
// Loop until FIFO has 'len' stereo pairs, or is full, or an error/EoT occurs.
|
||||
while (fifo_level(&dev->audio_fifo) < len && ret) {
|
||||
uint32_t samples_remaining_in_sector = SAMPLES_PER_SECTOR - dev->audio_sector_consumed_samples;
|
||||
uint32_t pairs_remaining_in_sector = STEREO_SAMPLES_PER_SECTOR - dev->audio_sector_consumed_pairs;
|
||||
|
||||
if (samples_remaining_in_sector == 0) { // Need to read a new sector
|
||||
if (pairs_remaining_in_sector == 0) { // Need to read a new sector
|
||||
if (dev->seek_pos < dev->cd_end) {
|
||||
bool read_successful;
|
||||
if (dev->audio_muted_soft) {
|
||||
@@ -300,9 +300,8 @@ bool cdrom_audio_callback(cdrom_t *dev, uint32_t len) {
|
||||
/* putchar('r'); */
|
||||
cdrom_log("CD-ROM %i: Read LBA %08X successful\n", dev->id, dev->seek_pos);
|
||||
dev->seek_pos++;
|
||||
/* dev->audio_sector_total_samples = SAMPLES_PER_SECTOR; */
|
||||
dev->audio_sector_consumed_samples = 0;
|
||||
samples_remaining_in_sector = SAMPLES_PER_SECTOR; // Update for current iteration
|
||||
dev->audio_sector_consumed_pairs = 0;
|
||||
pairs_remaining_in_sector = STEREO_SAMPLES_PER_SECTOR; // Update for current iteration
|
||||
} else {
|
||||
cdrom_log("CD-ROM %i: Read LBA %08X failed\n", dev->id, dev->seek_pos);
|
||||
dev->cd_status = CD_STATUS_STOPPED;
|
||||
@@ -317,36 +316,35 @@ bool cdrom_audio_callback(cdrom_t *dev, uint32_t len) {
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, if ret is still true, samples_remaining_in_sector should be > 0.
|
||||
// At this point, if ret is still true, pairs_remaining_in_sector should be > 0.
|
||||
uint32_t space_in_fifo = fifo_free_space(&dev->audio_fifo);
|
||||
|
||||
// Determine how many samples to transfer from staging to FIFO
|
||||
uint32_t samples_to_transfer = (samples_remaining_in_sector < space_in_fifo) ?
|
||||
samples_remaining_in_sector : space_in_fifo;
|
||||
// Determine how many stereo pairs to transfer from staging to FIFO
|
||||
uint32_t pairs_to_transfer = (pairs_remaining_in_sector < space_in_fifo) ?
|
||||
pairs_remaining_in_sector : space_in_fifo;
|
||||
|
||||
/* printf("%u\n", samples_to_transfer); */
|
||||
// Add samples from sector buffer to FIFO
|
||||
/* printf("%u\n", pairs_to_transfer); */
|
||||
// Add stereo pairs from sector buffer to FIFO
|
||||
bool added_ok = fifo_add_samples(&dev->audio_fifo,
|
||||
&dev->current_sector_samples[dev->audio_sector_consumed_samples],
|
||||
/* &dev->current_sector_samples[dev->audio_sector_consumed_samples], */
|
||||
samples_to_transfer);
|
||||
&dev->current_sector_samples[dev->audio_sector_consumed_pairs],
|
||||
pairs_to_transfer);
|
||||
if (added_ok) {
|
||||
/* putchar('y'); */
|
||||
dev->audio_sector_consumed_samples += samples_to_transfer;
|
||||
dev->audio_sector_consumed_pairs += pairs_to_transfer;
|
||||
} else {
|
||||
putchar('n');
|
||||
// This implies fifo_add_samples failed even though we thought there was space.
|
||||
// Could be a bug in fifo_free_space or fifo_add_samples, or a race condition in a threaded env (not here).
|
||||
cdrom_log("CD-ROM %i: fifo_add_samples failed unexpectedly! Space was %u, tried to add %u.\n",
|
||||
dev->id, space_in_fifo, samples_to_transfer);
|
||||
dev->id, space_in_fifo, pairs_to_transfer);
|
||||
ret = false; // Treat as an error
|
||||
break;
|
||||
}
|
||||
} // End while (fifo_level < len && ret)
|
||||
|
||||
cdrom_log("CD-ROM %i: Audio cb. FIFO level: %u. Staging: %d/%d. Ret %d\n",
|
||||
cdrom_log("CD-ROM %i: Audio cb. FIFO level: %u. Staging consumed: %d. Ret %d\n",
|
||||
dev->id, fifo_level(&dev->audio_fifo),
|
||||
dev->audio_sector_consumed_samples, dev->audio_sector_total_samples, ret);
|
||||
dev->audio_sector_consumed_pairs, ret);
|
||||
return ret;
|
||||
}
|
||||
#endif // USE_CD_AUDIO_FIFO
|
||||
@@ -356,13 +354,14 @@ uint32_t cdrom_audio_callback_simple(cdrom_t *dev, int16_t *buffer, uint32_t len
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t samples_produced = 0;
|
||||
uint32_t pairs_produced = 0;
|
||||
uint32_t pairs_requested = len / 2;
|
||||
// --- Fill buffer using data from audio_sector_buffer, reading new sectors as needed ---
|
||||
// Loop until FIFO has 'len' samples, or is full, or an error/EoT occurs.
|
||||
while (samples_produced < len) {
|
||||
uint32_t samples_remaining_in_sector = SAMPLES_PER_SECTOR - dev->audio_sector_consumed_samples;
|
||||
// Loop until buffer has 'len' samples, or an error/EoT occurs.
|
||||
while (pairs_produced < pairs_requested) {
|
||||
uint32_t pairs_remaining_in_sector = STEREO_SAMPLES_PER_SECTOR - dev->audio_sector_consumed_pairs;
|
||||
|
||||
if (samples_remaining_in_sector == 0) { // Need to read a new sector
|
||||
if (pairs_remaining_in_sector == 0) { // Need to read a new sector
|
||||
if (dev->seek_pos < dev->cd_end) {
|
||||
bool read_successful;
|
||||
if (dev->audio_muted_soft) {
|
||||
@@ -377,9 +376,8 @@ uint32_t cdrom_audio_callback_simple(cdrom_t *dev, int16_t *buffer, uint32_t len
|
||||
/* putchar('r'); */
|
||||
cdrom_log("CD-ROM %i: Read LBA %08X successful\n", dev->id, dev->seek_pos);
|
||||
dev->seek_pos++;
|
||||
/* dev->audio_sector_total_samples = SAMPLES_PER_SECTOR; */
|
||||
dev->audio_sector_consumed_samples = 0;
|
||||
samples_remaining_in_sector = SAMPLES_PER_SECTOR; // Update for current iteration
|
||||
dev->audio_sector_consumed_pairs = 0;
|
||||
pairs_remaining_in_sector = STEREO_SAMPLES_PER_SECTOR; // Update for current iteration
|
||||
} else {
|
||||
cdrom_log("CD-ROM %i: Read LBA %08X failed\n", dev->id, dev->seek_pos);
|
||||
dev->cd_status = CD_STATUS_STOPPED;
|
||||
@@ -392,25 +390,27 @@ uint32_t cdrom_audio_callback_simple(cdrom_t *dev, int16_t *buffer, uint32_t len
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, if ret is still true, samples_remaining_in_sector should be > 0.
|
||||
uint32_t space_in_buffer = len - samples_produced;
|
||||
// Determine how many samples to transfer from staging to buffer
|
||||
uint32_t samples_to_transfer = (samples_remaining_in_sector < space_in_buffer) ?
|
||||
samples_remaining_in_sector : space_in_buffer;
|
||||
// At this point, pairs_remaining_in_sector should be > 0.
|
||||
uint32_t space_in_buffer = pairs_requested - pairs_produced;
|
||||
// Determine how many stereo pairs to transfer from staging to buffer
|
||||
uint32_t pairs_to_transfer = (pairs_remaining_in_sector < space_in_buffer) ?
|
||||
pairs_remaining_in_sector : space_in_buffer;
|
||||
|
||||
/* printf("%u\n", samples_to_transfer); */
|
||||
// Add samples from sector buffer to FIFO
|
||||
//memcpy(buffer + samples_produced, &dev->current_sector_samples[dev->audio_sector_consumed_samples], samples_to_transfer * sizeof(int16_t));
|
||||
for (uint32_t i = 0; i < samples_to_transfer; i++)
|
||||
/* printf("%u\n", pairs_to_transfer); */
|
||||
// Add samples from sector buffer to output buffer
|
||||
//memcpy(buffer + pairs_produced * 2, &dev->current_sector_samples[dev->audio_sector_consumed_pairs], pairs_to_transfer * sizeof(sample_pair));
|
||||
for (uint32_t i = 0; i < pairs_to_transfer; i++)
|
||||
{
|
||||
int32_t sample = dev->current_sector_samples[dev->audio_sector_consumed_samples + i];
|
||||
buffer[samples_produced + i] = (int16_t)scale_sample(sample, cd_audio_volume, 0);
|
||||
sample_pair sp = dev->current_sector_samples[dev->audio_sector_consumed_pairs + i];
|
||||
buffer[(pairs_produced + i) * 2] = (int16_t)scale_sample(sp.data16[0], cd_audio_volume, 0);
|
||||
buffer[(pairs_produced + i) * 2 + 1] = (int16_t)scale_sample(sp.data16[1], cd_audio_volume, 0);
|
||||
}
|
||||
|
||||
samples_produced += samples_to_transfer;
|
||||
dev->audio_sector_consumed_samples += samples_to_transfer;
|
||||
} // End while (fifo_level < len && ret)
|
||||
pairs_produced += pairs_to_transfer;
|
||||
dev->audio_sector_consumed_pairs += pairs_to_transfer;
|
||||
}
|
||||
|
||||
uint32_t samples_produced = pairs_produced * 2;
|
||||
if (!pad) {
|
||||
return samples_produced;
|
||||
} else {
|
||||
@@ -551,12 +551,12 @@ uint8_t cdrom_get_subq(cdrom_t *dev,uint8_t *b) {
|
||||
/* seek_pos points to the next sector to be READ, not the one currently
|
||||
* audible. Subtract the number of sectors already buffered in the FIFO
|
||||
* so the reported position matches what the listener actually hears.
|
||||
* Each sector holds SAMPLES_PER_SECTOR stereo pairs = 2*SAMPLES_PER_SECTOR
|
||||
* individual int16 values. Guard against underflow when the FIFO is
|
||||
* empty or seek_pos is at the start of the track. */
|
||||
* Each sector holds STEREO_SAMPLES_PER_SECTOR stereo pairs.
|
||||
* Guard against underflow when the FIFO is empty or seek_pos is at the
|
||||
* start of the track. */
|
||||
#if USE_CD_AUDIO_FIFO
|
||||
{
|
||||
uint32_t buffered_sectors = (dev->audio_fifo.write_idx - dev->audio_fifo.read_idx) / (SAMPLES_PER_SECTOR * 2);
|
||||
uint32_t buffered_sectors = fifo_level(&dev->audio_fifo) / STEREO_SAMPLES_PER_SECTOR;
|
||||
uint32_t report_pos = (buffered_sectors < dev->seek_pos) ?
|
||||
(dev->seek_pos - buffered_sectors) : 0;
|
||||
dev->ops->get_subchannel(dev, report_pos, &subc);
|
||||
@@ -633,7 +633,7 @@ cdrom_global_init(void)
|
||||
/* Clear the global data. */
|
||||
memset(&cdrom, 0x00, sizeof(cdrom));
|
||||
cdrom.error_str = cdrom_errorstr_get();
|
||||
cdrom.current_sector_samples = (int16_t*)cdrom.audio_sector_buffer;
|
||||
cdrom.current_sector_samples = (sample_pair*)cdrom.audio_sector_buffer;
|
||||
set_volume(CMD_CDVOL);
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -55,6 +55,7 @@
|
||||
|
||||
#define RAW_SECTOR_SIZE 2352
|
||||
#define SAMPLES_PER_SECTOR 1176
|
||||
#define STEREO_SAMPLES_PER_SECTOR 588
|
||||
|
||||
#define STAT_READY 0x01
|
||||
#define STAT_PLAY 0x08
|
||||
@@ -198,9 +199,8 @@ typedef struct cdrom {
|
||||
|
||||
// int16_t cd_buffer[BUF_SIZE];
|
||||
uint8_t audio_sector_buffer[RAW_SECTOR_SIZE];
|
||||
int16_t *current_sector_samples; // Convenience pointer: (int16_t*)audio_sector_buffer
|
||||
int audio_sector_total_samples; // Samples available in current_sector_samples after a read
|
||||
int audio_sector_consumed_samples; // Samples consumed from current_sector_samples
|
||||
sample_pair *current_sector_samples; // Convenience pointer: (sample_pair*)audio_sector_buffer
|
||||
int audio_sector_consumed_pairs; // Stereo pairs consumed from current_sector_samples
|
||||
|
||||
#if USE_CD_AUDIO_FIFO
|
||||
audio_fifo_t audio_fifo;
|
||||
|
||||
+35
-23
@@ -98,7 +98,26 @@ static constexpr uint32_t fixed_ratio(uint16_t a, uint16_t b) {
|
||||
}
|
||||
|
||||
static constexpr uint32_t opl_ratio = fixed_ratio(49716, 44100);
|
||||
static audio_fifo_t opl_out_fifo;
|
||||
|
||||
// OPL FIFO — smaller than CD audio FIFO since producer and consumer are on
|
||||
// the same core. 256 stereo pairs ~= 5.8ms at 44100Hz.
|
||||
#define OPL_FIFO_SIZE 256
|
||||
#define OPL_FIFO_BITS (OPL_FIFO_SIZE - 1)
|
||||
static struct {
|
||||
sample_pair buffer[OPL_FIFO_SIZE];
|
||||
volatile uint32_t write_idx;
|
||||
volatile uint32_t read_idx;
|
||||
volatile fifo_state_t state;
|
||||
} opl_out_fifo;
|
||||
|
||||
static inline uint32_t opl_fifo_free_space() {
|
||||
return OPL_FIFO_SIZE - (opl_out_fifo.write_idx - opl_out_fifo.read_idx);
|
||||
}
|
||||
|
||||
static inline void opl_fifo_add_sample(sample_pair sample) {
|
||||
opl_out_fifo.buffer[opl_out_fifo.write_idx & OPL_FIFO_BITS] = sample;
|
||||
opl_out_fifo.write_idx++;
|
||||
}
|
||||
|
||||
// Stereo OPL resampler state.
|
||||
// For stereo backends (dbopl, ymfm), OPL_Pico_stereo provides true stereo.
|
||||
@@ -137,11 +156,6 @@ static void opl_resample_tick()
|
||||
}
|
||||
}
|
||||
|
||||
typedef union {
|
||||
uint32_t data32;
|
||||
int16_t data16[2];
|
||||
} sample_pair;
|
||||
|
||||
// Setup values for audio sample clock
|
||||
// 8390 clock cycles per sample (370MHz / 8390 ~= 44100Hz)
|
||||
static constexpr uint32_t clocks_per_sample_minus_one = (SYS_CLK_HZ / 44100) - 1;
|
||||
@@ -175,22 +189,21 @@ void audio_sample_handler(void) {
|
||||
#endif
|
||||
|
||||
#ifdef CDROM
|
||||
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 & AUDIO_FIFO_BITS], volume.cd_audio[0], 0);
|
||||
sample_r += scale_sample(cd_fifo->buffer[(idx + 1) & AUDIO_FIFO_BITS], volume.cd_audio[1], 0);
|
||||
cd_fifo->read_idx = idx + 2;
|
||||
if (cd_fifo->state != FIFO_STATE_STOPPED && cd_fifo->write_idx - cd_fifo->read_idx >= 1) {
|
||||
sample_pair cd = cd_fifo->buffer[cd_fifo->read_idx & AUDIO_FIFO_BITS];
|
||||
cd_fifo->read_idx++;
|
||||
if (cd_fifo->write_idx == cd_fifo->read_idx) cd_fifo->state = FIFO_STATE_STOPPED;
|
||||
sample_l += scale_sample(cd.data16[0], volume.cd_audio[0], 0);
|
||||
sample_r += scale_sample(cd.data16[1], volume.cd_audio[1], 0);
|
||||
}
|
||||
#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.write_idx - opl_out_fifo.read_idx >= 2) {
|
||||
uint32_t idx = opl_out_fifo.read_idx;
|
||||
sample_l += scale_sample(opl_out_fifo.buffer[idx & AUDIO_FIFO_BITS], volume.opl[0], 0);
|
||||
sample_r += scale_sample(opl_out_fifo.buffer[(idx + 1) & AUDIO_FIFO_BITS], volume.opl[1], 0);
|
||||
opl_out_fifo.read_idx = idx + 2;
|
||||
if (opl_out_fifo.state != FIFO_STATE_STOPPED && opl_out_fifo.write_idx - opl_out_fifo.read_idx >= 1) {
|
||||
sample_pair opl = opl_out_fifo.buffer[opl_out_fifo.read_idx & OPL_FIFO_BITS];
|
||||
opl_out_fifo.read_idx++;
|
||||
if (opl_out_fifo.write_idx == opl_out_fifo.read_idx) opl_out_fifo.state = FIFO_STATE_STOPPED;
|
||||
sample_l += scale_sample(opl.data16[0], volume.opl[0], 0);
|
||||
sample_r += scale_sample(opl.data16[1], volume.opl[1], 0);
|
||||
}
|
||||
|
||||
#ifdef SOUND_SB
|
||||
@@ -266,7 +279,7 @@ void play_adlib() {
|
||||
|
||||
for (;;) {
|
||||
#if CDROM
|
||||
cdrom_audio_callback(&cdrom, AUDIO_FIFO_SIZE - SAMPLES_PER_SECTOR);
|
||||
cdrom_audio_callback(&cdrom, AUDIO_FIFO_SIZE - STEREO_SAMPLES_PER_SECTOR);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
@@ -283,14 +296,13 @@ void play_adlib() {
|
||||
// Process DSP commands
|
||||
sbdsp_process();
|
||||
|
||||
// Generate OPL stereo pairs and add to output FIFO (interleaved L, R).
|
||||
// Need 2 free slots per output sample.
|
||||
// Generate OPL stereo pairs and add to output FIFO.
|
||||
for (uint32_t opl_i = 0;
|
||||
opl_i < OPL_FILL_PER_ITER && fifo_free_space(&opl_out_fifo) >= 2;
|
||||
opl_i < OPL_FILL_PER_ITER && opl_fifo_free_space() >= 1;
|
||||
opl_i++) {
|
||||
opl_resample_tick();
|
||||
fifo_add_sample(&opl_out_fifo, (int16_t)opl_resamp_l);
|
||||
fifo_add_sample(&opl_out_fifo, (int16_t)opl_resamp_r);
|
||||
sample_pair p = {.data16 = {(int16_t)opl_resamp_l, (int16_t)opl_resamp_r}};
|
||||
opl_fifo_add_sample(p);
|
||||
}
|
||||
#ifdef USB_STACK
|
||||
// Service TinyUSB events
|
||||
|
||||
Reference in new issue
Block a user