(wip) mono and dynamic sample rate support

This commit is contained in:
Anthony Rabine 2023-08-10 09:00:45 +02:00
parent 620f06bf6c
commit d9b1edadfe
8 changed files with 93 additions and 100 deletions

View file

@ -77,12 +77,18 @@ static uint32_t ButtonsStatePrev = 0;
// Rotary encoder // Rotary encoder
// pio 0 is used // pio 0 is used
PIO pio = pio1; static PIO pio = pio1;
// state machine 0 // state machine 0
uint8_t sm = 0; static uint8_t sm = 0;
static int new_value, delta, old_value = 0; static int new_value, delta, old_value = 0;
static audio_i2s_config_t config = {
.freq = 44100,
.bps = 32,
.data_pin = 28,
.clock_pin_base = 26};
// =========================================================================================================== // ===========================================================================================================
// PROTOTYPES // PROTOTYPES
// =========================================================================================================== // ===========================================================================================================
@ -261,14 +267,7 @@ void ost_system_initialize()
gpio_set_function(SDCARD_MISO, GPIO_FUNC_SPI); gpio_set_function(SDCARD_MISO, GPIO_FUNC_SPI);
//------------------- Init Sound //------------------- Init Sound
static const audio_i2s_config_t config = {
.freq = 44100,
.bps = 32,
.data_pin = 28,
.clock_pin_base = 26};
i2s_program_setup(pio0, audio_i2s_dma_irq_handler, &i2s, &config); i2s_program_setup(pio0, audio_i2s_dma_irq_handler, &i2s, &config);
audio_init(&audio_ctx); audio_init(&audio_ctx);
// ------------ Everything is initialized, print stuff here // ------------ Everything is initialized, print stuff here
@ -425,6 +424,9 @@ void ost_display_transfer_multi(uint8_t *buff, uint32_t btr)
void ost_audio_play(const char *filename) void ost_audio_play(const char *filename)
{ {
audio_play(&audio_ctx, filename); audio_play(&audio_ctx, filename);
config.freq = audio_ctx.audio_info.sample_rate;
config.channels = audio_ctx.audio_info.channels;
pico_i2s_set_frequency(&i2s, &config);
i2s.buffer_index = 0; i2s.buffer_index = 0;

View file

@ -18,50 +18,13 @@
void pico_i2s_set_frequency(const pio_i2s *i2s, const audio_i2s_config_t *config) void pico_i2s_set_frequency(const pio_i2s *i2s, const audio_i2s_config_t *config)
{ {
float bitClk = config->freq * config->bps * 2.0 /* channels */ * 2.0 /* edges per clock */; // Pour le calcul de la fréquence, le nombre de canaux est toujours fixé à 2
// car c'est notre format de sortie I2S
// Dans le cas du mono, on l'a détecté en amont et on a copié l'échantillon dans la voie de droite
float bitClk = config->freq * config->bps * config->channels /* channels */ * 2.0 /* edges per clock */;
pio_sm_set_clkdiv(i2s->pio, i2s->sm_dout, (float)clock_get_hz(clk_sys) / bitClk); pio_sm_set_clkdiv(i2s->pio, i2s->sm_dout, (float)clock_get_hz(clk_sys) / bitClk);
} }
#if 0
static bool audio_enabled;
void audio_i2s_set_enabled(bool enabled)
{
if (enabled != audio_enabled)
{
#ifndef NDEBUG
if (enabled)
{
puts("Enabling PIO I2S audio\n");
printf("(on core %d\n", get_core_num());
}
#endif
irq_set_enabled(DMA_IRQ_0 + PICO_AUDIO_I2S_DMA_IRQ, enabled);
if (enabled)
{
// audio_start_dma_transfer();
}
else
{
/*
// if there was a buffer in flight, it will not be freed by DMA IRQ, let's do it manually
if (shared_state.playing_buffer)
{
give_audio_buffer(audio_i2s_consumer, shared_state.playing_buffer);
shared_state.playing_buffer = NULL;
}
*/
}
pio_sm_set_enabled(audio_pio, shared_state.pio_sm, enabled);
audio_enabled = enabled;
}
}
#endif
//--------------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------------
static void dma_double_buffer_init(pio_i2s *i2s) static void dma_double_buffer_init(pio_i2s *i2s)

View file

@ -20,13 +20,11 @@ extern "C"
{ {
#endif #endif
#define AUDIO_BUFFER_FRAMES SIZE_OF_SAMPLES
#define STEREO_BUFFER_SIZE AUDIO_BUFFER_FRAMES * 2 // for left + right
typedef struct typedef struct
{ {
uint32_t freq; uint32_t freq;
uint32_t bps; uint32_t bps;
uint16_t channels;
uint8_t data_pin; uint8_t data_pin;
uint8_t clock_pin_base; uint8_t clock_pin_base;
@ -48,6 +46,7 @@ extern "C"
void i2s_program_setup(PIO pio, void (*dma_handler)(void), pio_i2s *i2s, const audio_i2s_config_t *config); void i2s_program_setup(PIO pio, void (*dma_handler)(void), pio_i2s *i2s, const audio_i2s_config_t *config);
void pico_i2s_set_frequency(const pio_i2s *i2s, const audio_i2s_config_t *config);
void i2s_start(pio_i2s *i2s); void i2s_start(pio_i2s *i2s);
void i2s_stop(pio_i2s *i2s); void i2s_stop(pio_i2s *i2s);

View file

@ -10,11 +10,10 @@
#include "ost_hal.h" #include "ost_hal.h"
#include "serializers.h" #include "serializers.h"
// Audio Double Buffer for DMA transfer int32_t audio_buf[STEREO_BUFFER_SIZE]; // x2 because we store L+R
int32_t audio_buf[SIZE_OF_SAMPLES * 2]; // x2 because we store L+R
// Audio Buffer for File Read // Audio Buffer for File Read
uint8_t raw_buf[SIZE_OF_SAMPLES * 2 * 2]; // x2 for 16-bit, and x2 for L+R uint8_t raw_buf[AUDIO_BUFFER_FRAMES * 2 * 2]; // x2 for 16-bit, and x2 for L+R
int32_t DAC_ZERO_VALUE = 1; // int32_t DAC_ZERO_VALUE = 1; //
@ -59,12 +58,12 @@ void audio_init(audio_ctx_t *ctx)
ctx->volume = 65; ctx->volume = 65;
ctx->count = 0; ctx->count = 0;
for (int i = 0; i < SIZE_OF_SAMPLES; i++) for (int i = 0; i < STEREO_BUFFER_SIZE; i++)
{ {
audio_buf[i] = DAC_ZERO_VALUE; audio_buf[i] = DAC_ZERO_VALUE;
} }
ctx->transfer_size = SIZE_OF_SAMPLES / 4; ctx->transfer_size = STEREO_BUFFER_SIZE;
} }
static int list_chunk_is_info_type(audio_ctx_t *ctx) static int list_chunk_is_info_type(audio_ctx_t *ctx)
@ -95,12 +94,14 @@ static int load_next_file(audio_ctx_t *ctx, const char *fname_ptr)
fr = f_open(&ctx->fil, ctx->audio_info.filename, FA_READ); fr = f_open(&ctx->fil, ctx->audio_info.filename, FA_READ);
if (fr != FR_OK) if (fr != FR_OK)
{ {
debug_printf("ERROR: f_open %d\n\r", (int)fr); debug_printf("ERROR: f_open %d for file: %s\n\r", (int)fr, fname_ptr);
} }
ctx->idx_play++; ctx->idx_play++;
FRESULT res = f_read(&ctx->fil, ctx->header, sizeof(ctx->header), &br); FRESULT res = f_read(&ctx->fil, ctx->header, sizeof(ctx->header), &br);
// Find 'data' chunk
ctx->audio_info.channels = leu16_get(&ctx->header[22]);
ctx->audio_info.sample_rate = leu32_get(&ctx->header[24]);
// Find 'data' chunk
offset = 0; offset = 0;
while (1) while (1)
{ {
@ -116,9 +117,20 @@ static int load_next_file(audio_ctx_t *ctx, const char *fname_ptr)
} }
else else
{ {
debug_printf("[AUDIO_PLAYER] Data not found, invalid file\r\n");
return 0;
break; break;
} }
} }
if (size == 0)
{
debug_printf("[AUDIO_PLAYER] Empty audio file\r\n");
return 0;
}
else
{
debug_printf("[AUDIO_PLAYER] Load WAV: \r\n - Channels: %d\r\n - Sample rate: %d\r\n", ctx->audio_info.channels, ctx->audio_info.sample_rate);
ctx->audio_info.data_size = size; ctx->audio_info.data_size = size;
// printf("Audio data size = %d\n\r", (int) audio_info.data_size); // printf("Audio data size = %d\n\r", (int) audio_info.data_size);
ctx->audio_info.data_start = offset; ctx->audio_info.data_start = offset;
@ -128,6 +140,7 @@ static int load_next_file(audio_ctx_t *ctx, const char *fname_ptr)
return 0; return 0;
} }
}
static int get_level(uint32_t val) static int get_level(uint32_t val)
{ {
@ -164,6 +177,7 @@ static int get_audio_buf(audio_ctx_t *ctx, int32_t *buf_32b)
uint32_t lvl_r = 0; uint32_t lvl_r = 0;
number = 0; // number to transfer (en octets) number = 0; // number to transfer (en octets)
while (number < sizeof(raw_buf)) while (number < sizeof(raw_buf))
{ {
file_rest = ctx->audio_info.data_size - ctx->audio_info.data_offset; file_rest = ctx->audio_info.data_size - ctx->audio_info.data_offset;
@ -192,18 +206,31 @@ static int get_audio_buf(audio_ctx_t *ctx, int32_t *buf_32b)
} }
} }
static bool onetime = true; bool mono = ctx->audio_info.channels == 1;
uint32_t index = 4;
if (mono)
{
index = 2;
}
// samples : total bytes devided by 2 (16 bits) and by two again (2 channels) // samples : total bytes devided by 2 (16 bits) and by two again (2 channels)
for (i = 0; i < number / 4; i++) for (i = 0; i < number / index; i++)
{ {
// buf_32b[i * 2 + 0] = (int32_t)swap16b((int32_t)buf_16b[i * 2 + 0] * vol_table[ctx->volume]) + DAC_ZERO_VALUE; // L // buf_32b[i * 2 + 0] = (int32_t)swap16b((int32_t)buf_16b[i * 2 + 0] * vol_table[ctx->volume]) + DAC_ZERO_VALUE; // L
// buf_32b[i * 2 + 1] = (int32_t)swap16b((int32_t)buf_16b[i * 2 + 1] * vol_table[ctx->volume]) + DAC_ZERO_VALUE; // R // buf_32b[i * 2 + 1] = (int32_t)swap16b((int32_t)buf_16b[i * 2 + 1] * vol_table[ctx->volume]) + DAC_ZERO_VALUE; // R
// Avec le AUDIO PICO de waveshare, on entend un truc // Avec le AUDIO PICO de waveshare, on entend un truc
buf_32b[i * 2] = ((int32_t)((int16_t)leu16_get(&raw_buf[i * 4]))) << 16; buf_32b[i * 2] = ((int32_t)((int16_t)leu16_get(&raw_buf[i * index]))) << 16;
buf_32b[i * 2 + 1] = ((int32_t)((int16_t)leu16_get(&raw_buf[i * 4 + 2]))) << 16;
if (mono)
{
buf_32b[i * 2 + 1] = buf_32b[i * 2];
}
else
{
buf_32b[i * 2 + 1] = ((int32_t)((int16_t)leu16_get(&raw_buf[i * index + 2]))) << 16;
}
// buf_32b[i * 2] = 1; // buf_32b[i * 2] = 1;
// buf_32b[i * 2 + 1] = 4; // buf_32b[i * 2 + 1] = 4;
@ -211,21 +238,9 @@ static int get_audio_buf(audio_ctx_t *ctx, int32_t *buf_32b)
// lvl_l += ((int32_t)buf_16b[i * 2 + 0] * buf_16b[i * 2 + 0]) / 32768; // lvl_l += ((int32_t)buf_16b[i * 2 + 0] * buf_16b[i * 2 + 0]) / 32768;
// lvl_r += ((int32_t)buf_16b[i * 2 + 1] * buf_16b[i * 2 + 1]) / 32768; // lvl_r += ((int32_t)buf_16b[i * 2 + 1] * buf_16b[i * 2 + 1]) / 32768;
} }
/*
if (onetime)
{
onetime = false;
for (int i = 0; i < 10; i++)
{
debug_printf("Sample left: %d\r\n", buf_32b[i * 2]);
debug_printf("Sample right: %d\r\n", buf_32b[i * 2 + 1]);
}
}
*/
// ctx->audio_info.lvl_l = get_level(lvl_l / (number / 4)); // ctx->audio_info.lvl_l = get_level(lvl_l / (number / 4));
// ctx->audio_info.lvl_r = get_level(lvl_r / (number / 4)); // ctx->audio_info.lvl_r = get_level(lvl_r / (number / 4));
ctx->transfer_size = (number / 2); // 32 bytes tranfers ctx->transfer_size = number / 2; // 32 bytes tranfers
return _next_is_end; return _next_is_end;
} }
@ -245,11 +260,11 @@ int audio_process(audio_ctx_t *ctx)
} }
else else
{ {
for (int i = 0; i < SIZE_OF_SAMPLES; i++) for (int i = 0; i < STEREO_BUFFER_SIZE; i++)
{ {
audio_buf[i] = DAC_ZERO_VALUE; audio_buf[i] = DAC_ZERO_VALUE;
} }
ctx->transfer_size = SIZE_OF_SAMPLES / 4; ctx->transfer_size = STEREO_BUFFER_SIZE;
} }
ctx->count++; ctx->count++;

View file

@ -6,7 +6,8 @@
#include <ff.h> #include <ff.h>
#define SIZE_OF_SAMPLES (128) // in bytes #define AUDIO_BUFFER_FRAMES (128) // in bytes
#define STEREO_BUFFER_SIZE AUDIO_BUFFER_FRAMES * 2 // for left + right
#define FILENAME_MAX_SIZE 260 #define FILENAME_MAX_SIZE 260
typedef struct typedef struct
@ -18,6 +19,8 @@ typedef struct
uint32_t data_start; uint32_t data_start;
uint32_t data_size; uint32_t data_size;
uint32_t data_offset; uint32_t data_offset;
uint32_t sample_rate;
uint16_t channels;
char artist[256]; char artist[256];
char title[256]; char title[256];
char album[256]; char album[256];

View file

@ -109,6 +109,7 @@ void filesystem_mount()
} }
scan_files(""); scan_files("");
scan_files("/ba869e4b-03d6-4249-9202-85b4cec767a7/assets");
} }
uint32_t filesystem_get_capacity() uint32_t filesystem_get_capacity()

View file

@ -50,7 +50,8 @@ typedef struct
fs_result_cb_t cb; fs_result_cb_t cb;
} ost_fs_event_t; } ost_fs_event_t;
#define STORY_DIR_OFFSET (UUID_SIZE + 1) #define ASSETS_DIR "/assets/"
#define STORY_DIR_OFFSET (UUID_SIZE + 1) // +1 for the first '/' (filesystem root)
// =========================================================================================================== // ===========================================================================================================
// PRIVATE GLOBAL VARIABLES // PRIVATE GLOBAL VARIABLES
@ -68,9 +69,6 @@ static int PacketCounter = 0;
static char ScratchFile[260]; static char ScratchFile[260];
static const char *ImagesDir = "/images/";
static const char *SoundsDir = "/sounds/";
static uint8_t LedState = 0; static uint8_t LedState = 0;
// =========================================================================================================== // ===========================================================================================================
@ -119,7 +117,7 @@ void FsTask(void *args)
if (OstContext.sound != NULL) if (OstContext.sound != NULL)
{ {
ScratchFile[STORY_DIR_OFFSET] = 0; ScratchFile[STORY_DIR_OFFSET] = 0;
strcat(ScratchFile, SoundsDir); strcat(ScratchFile, ASSETS_DIR);
strcat(ScratchFile, message->sound); strcat(ScratchFile, message->sound);
debug_printf("\r\n-------------------------------------------------------\r\nPlaying: %s\r\n", ScratchFile); debug_printf("\r\n-------------------------------------------------------\r\nPlaying: %s\r\n", ScratchFile);
@ -151,7 +149,7 @@ void FsTask(void *args)
if (OstContext.image != NULL) if (OstContext.image != NULL)
{ {
ScratchFile[STORY_DIR_OFFSET] = 0; ScratchFile[STORY_DIR_OFFSET] = 0;
strcat(ScratchFile, ImagesDir); strcat(ScratchFile, ASSETS_DIR);
strcat(ScratchFile, message->image); strcat(ScratchFile, message->image);
filesystem_display_image(ScratchFile); filesystem_display_image(ScratchFile);
@ -168,7 +166,7 @@ void FsTask(void *args)
// Init current directory // Init current directory
ScratchFile[0] = '/'; ScratchFile[0] = '/';
memcpy(&ScratchFile[1], OstContext.uuid, UUID_SIZE); memcpy(&ScratchFile[1], OstContext.uuid, UUID_SIZE);
ScratchFile[1 + UUID_SIZE] = 0; ScratchFile[STORY_DIR_OFFSET] = 0;
success = true; success = true;
} }

View file

@ -164,6 +164,7 @@ void VmTask(void *args)
ost_vm_state_t VmState = OST_VM_STATE_HOME; ost_vm_state_t VmState = OST_VM_STATE_HOME;
fs_task_scan_index(read_index_callback); fs_task_scan_index(read_index_callback);
bool run_script = false; bool run_script = false;
bool block_keys = false;
while (1) while (1)
{ {
@ -176,11 +177,21 @@ void VmTask(void *args)
case OST_VM_STATE_HOME: case OST_VM_STATE_HOME:
switch (message->ev) switch (message->ev)
{ {
case VM_EV_END_OF_SOUND:
block_keys = false;
break;
case VM_EV_EXEC_HOME_INDEX: case VM_EV_EXEC_HOME_INDEX:
// La lecture de l'index est terminée, on demande l'affichage des médias // La lecture de l'index est terminée, on demande l'affichage des médias
if (!block_keys)
{
block_keys = true;
fs_task_play_index(); fs_task_play_index();
}
break; break;
case VM_EV_BUTTON_EVENT: case VM_EV_BUTTON_EVENT:
if (!block_keys)
{
// debug_printf("B: %x", message->button_mask); // debug_printf("B: %x", message->button_mask);
if ((message->button_mask & OST_BUTTON_OK) == OST_BUTTON_OK) if ((message->button_mask & OST_BUTTON_OK) == OST_BUTTON_OK)
{ {
@ -188,6 +199,7 @@ void VmTask(void *args)
debug_printf("OK\r\n"); debug_printf("OK\r\n");
fs_task_load_story(m_rom_data); fs_task_load_story(m_rom_data);
} }
}
break; break;
case VM_EV_ERROR: case VM_EV_ERROR:
default: default: