mirror of
https://github.com/arabine/open-story-teller.git
synced 2025-12-06 17:09:06 +01:00
(wip) mono and dynamic sample rate support
This commit is contained in:
parent
620f06bf6c
commit
d9b1edadfe
8 changed files with 93 additions and 100 deletions
|
|
@ -77,12 +77,18 @@ static uint32_t ButtonsStatePrev = 0;
|
|||
|
||||
// Rotary encoder
|
||||
// pio 0 is used
|
||||
PIO pio = pio1;
|
||||
static PIO pio = pio1;
|
||||
// state machine 0
|
||||
uint8_t sm = 0;
|
||||
static uint8_t sm = 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
|
||||
// ===========================================================================================================
|
||||
|
|
@ -261,14 +267,7 @@ void ost_system_initialize()
|
|||
gpio_set_function(SDCARD_MISO, GPIO_FUNC_SPI);
|
||||
|
||||
//------------------- 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);
|
||||
|
||||
audio_init(&audio_ctx);
|
||||
|
||||
// ------------ 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)
|
||||
{
|
||||
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;
|
||||
|
||||
|
|
|
|||
|
|
@ -18,50 +18,13 @@
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
#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)
|
||||
|
|
|
|||
|
|
@ -20,13 +20,11 @@ extern "C"
|
|||
{
|
||||
#endif
|
||||
|
||||
#define AUDIO_BUFFER_FRAMES SIZE_OF_SAMPLES
|
||||
#define STEREO_BUFFER_SIZE AUDIO_BUFFER_FRAMES * 2 // for left + right
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t freq;
|
||||
uint32_t bps;
|
||||
uint16_t channels;
|
||||
uint8_t data_pin;
|
||||
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 pico_i2s_set_frequency(const pio_i2s *i2s, const audio_i2s_config_t *config);
|
||||
void i2s_start(pio_i2s *i2s);
|
||||
void i2s_stop(pio_i2s *i2s);
|
||||
|
||||
|
|
|
|||
|
|
@ -10,11 +10,10 @@
|
|||
#include "ost_hal.h"
|
||||
#include "serializers.h"
|
||||
|
||||
// Audio Double Buffer for DMA transfer
|
||||
int32_t audio_buf[SIZE_OF_SAMPLES * 2]; // x2 because we store L+R
|
||||
int32_t audio_buf[STEREO_BUFFER_SIZE]; // x2 because we store L+R
|
||||
|
||||
// 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; //
|
||||
|
||||
|
|
@ -59,12 +58,12 @@ void audio_init(audio_ctx_t *ctx)
|
|||
ctx->volume = 65;
|
||||
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;
|
||||
}
|
||||
|
||||
ctx->transfer_size = SIZE_OF_SAMPLES / 4;
|
||||
ctx->transfer_size = STEREO_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
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);
|
||||
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++;
|
||||
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;
|
||||
while (1)
|
||||
{
|
||||
|
|
@ -116,17 +117,29 @@ static int load_next_file(audio_ctx_t *ctx, const char *fname_ptr)
|
|||
}
|
||||
else
|
||||
{
|
||||
debug_printf("[AUDIO_PLAYER] Data not found, invalid file\r\n");
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ctx->audio_info.data_size = size;
|
||||
// printf("Audio data size = %d\n\r", (int) audio_info.data_size);
|
||||
ctx->audio_info.data_start = offset;
|
||||
ctx->audio_info.data_offset = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
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;
|
||||
// printf("Audio data size = %d\n\r", (int) audio_info.data_size);
|
||||
ctx->audio_info.data_start = offset;
|
||||
ctx->audio_info.data_offset = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
number = 0; // number to transfer (en octets)
|
||||
|
||||
while (number < sizeof(raw_buf))
|
||||
{
|
||||
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)
|
||||
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 + 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
|
||||
|
||||
buf_32b[i * 2] = ((int32_t)((int16_t)leu16_get(&raw_buf[i * 4]))) << 16;
|
||||
buf_32b[i * 2 + 1] = ((int32_t)((int16_t)leu16_get(&raw_buf[i * 4 + 2]))) << 16;
|
||||
buf_32b[i * 2] = ((int32_t)((int16_t)leu16_get(&raw_buf[i * index]))) << 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] = 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_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_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;
|
||||
}
|
||||
|
||||
|
|
@ -245,11 +260,11 @@ int audio_process(audio_ctx_t *ctx)
|
|||
}
|
||||
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;
|
||||
}
|
||||
ctx->transfer_size = SIZE_OF_SAMPLES / 4;
|
||||
ctx->transfer_size = STEREO_BUFFER_SIZE;
|
||||
}
|
||||
ctx->count++;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@
|
|||
|
||||
#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
|
||||
|
||||
typedef struct
|
||||
|
|
@ -18,6 +19,8 @@ typedef struct
|
|||
uint32_t data_start;
|
||||
uint32_t data_size;
|
||||
uint32_t data_offset;
|
||||
uint32_t sample_rate;
|
||||
uint16_t channels;
|
||||
char artist[256];
|
||||
char title[256];
|
||||
char album[256];
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ void filesystem_mount()
|
|||
}
|
||||
|
||||
scan_files("");
|
||||
scan_files("/ba869e4b-03d6-4249-9202-85b4cec767a7/assets");
|
||||
}
|
||||
|
||||
uint32_t filesystem_get_capacity()
|
||||
|
|
|
|||
|
|
@ -50,7 +50,8 @@ typedef struct
|
|||
fs_result_cb_t cb;
|
||||
} 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
|
||||
|
|
@ -68,9 +69,6 @@ static int PacketCounter = 0;
|
|||
|
||||
static char ScratchFile[260];
|
||||
|
||||
static const char *ImagesDir = "/images/";
|
||||
static const char *SoundsDir = "/sounds/";
|
||||
|
||||
static uint8_t LedState = 0;
|
||||
|
||||
// ===========================================================================================================
|
||||
|
|
@ -119,7 +117,7 @@ void FsTask(void *args)
|
|||
if (OstContext.sound != NULL)
|
||||
{
|
||||
ScratchFile[STORY_DIR_OFFSET] = 0;
|
||||
strcat(ScratchFile, SoundsDir);
|
||||
strcat(ScratchFile, ASSETS_DIR);
|
||||
strcat(ScratchFile, message->sound);
|
||||
|
||||
debug_printf("\r\n-------------------------------------------------------\r\nPlaying: %s\r\n", ScratchFile);
|
||||
|
|
@ -151,7 +149,7 @@ void FsTask(void *args)
|
|||
if (OstContext.image != NULL)
|
||||
{
|
||||
ScratchFile[STORY_DIR_OFFSET] = 0;
|
||||
strcat(ScratchFile, ImagesDir);
|
||||
strcat(ScratchFile, ASSETS_DIR);
|
||||
strcat(ScratchFile, message->image);
|
||||
|
||||
filesystem_display_image(ScratchFile);
|
||||
|
|
@ -168,7 +166,7 @@ void FsTask(void *args)
|
|||
// Init current directory
|
||||
ScratchFile[0] = '/';
|
||||
memcpy(&ScratchFile[1], OstContext.uuid, UUID_SIZE);
|
||||
ScratchFile[1 + UUID_SIZE] = 0;
|
||||
ScratchFile[STORY_DIR_OFFSET] = 0;
|
||||
success = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -164,6 +164,7 @@ void VmTask(void *args)
|
|||
ost_vm_state_t VmState = OST_VM_STATE_HOME;
|
||||
fs_task_scan_index(read_index_callback);
|
||||
bool run_script = false;
|
||||
bool block_keys = false;
|
||||
|
||||
while (1)
|
||||
{
|
||||
|
|
@ -176,17 +177,28 @@ void VmTask(void *args)
|
|||
case OST_VM_STATE_HOME:
|
||||
switch (message->ev)
|
||||
{
|
||||
case VM_EV_END_OF_SOUND:
|
||||
block_keys = false;
|
||||
break;
|
||||
|
||||
case VM_EV_EXEC_HOME_INDEX:
|
||||
// La lecture de l'index est terminée, on demande l'affichage des médias
|
||||
fs_task_play_index();
|
||||
if (!block_keys)
|
||||
{
|
||||
block_keys = true;
|
||||
fs_task_play_index();
|
||||
}
|
||||
break;
|
||||
case VM_EV_BUTTON_EVENT:
|
||||
// debug_printf("B: %x", message->button_mask);
|
||||
if ((message->button_mask & OST_BUTTON_OK) == OST_BUTTON_OK)
|
||||
if (!block_keys)
|
||||
{
|
||||
VmState = OST_VM_STATE_HOME_WAIT_LOAD_STORY;
|
||||
debug_printf("OK\r\n");
|
||||
fs_task_load_story(m_rom_data);
|
||||
// debug_printf("B: %x", message->button_mask);
|
||||
if ((message->button_mask & OST_BUTTON_OK) == OST_BUTTON_OK)
|
||||
{
|
||||
VmState = OST_VM_STATE_HOME_WAIT_LOAD_STORY;
|
||||
debug_printf("OK\r\n");
|
||||
fs_task_load_story(m_rom_data);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case VM_EV_ERROR:
|
||||
|
|
|
|||
Loading…
Reference in a new issue