From 9eb1726fd94fb66c1c5f72cbb3588a5d2fad77e5 Mon Sep 17 00:00:00 2001 From: elehobica Date: Mon, 11 Mar 2024 00:10:01 +0900 Subject: [PATCH] use submodule for pico_audio_i2s_32b --- .gitmodules | 3 + CMakeLists.txt | 2 +- lib/pico_audio_32b/CMakeLists.txt | 16 - lib/pico_audio_32b/audio.cpp | 264 ------- lib/pico_audio_32b/audio_utils.S | 256 ------- lib/pico_audio_32b/include/pico/audio.h | 328 -------- .../include/pico/sample_conversion.h | 295 ------- lib/pico_audio_i2s_32b | 1 + lib/pico_audio_i2s_32b/CMakeLists.txt | 19 - lib/pico_audio_i2s_32b/audio_i2s.c | 723 ------------------ lib/pico_audio_i2s_32b/audio_i2s.pio | 81 -- .../include/pico/audio_i2s.h | 189 ----- 12 files changed, 5 insertions(+), 2172 deletions(-) delete mode 100644 lib/pico_audio_32b/CMakeLists.txt delete mode 100644 lib/pico_audio_32b/audio.cpp delete mode 100644 lib/pico_audio_32b/audio_utils.S delete mode 100644 lib/pico_audio_32b/include/pico/audio.h delete mode 100644 lib/pico_audio_32b/include/pico/sample_conversion.h create mode 160000 lib/pico_audio_i2s_32b delete mode 100644 lib/pico_audio_i2s_32b/CMakeLists.txt delete mode 100644 lib/pico_audio_i2s_32b/audio_i2s.c delete mode 100644 lib/pico_audio_i2s_32b/audio_i2s.pio delete mode 100644 lib/pico_audio_i2s_32b/include/pico/audio_i2s.h diff --git a/.gitmodules b/.gitmodules index 25b7899..257c188 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "lib/pico_spdif_rx"] path = lib/pico_spdif_rx url = git@github.com:elehobica/pico_spdif_rx.git +[submodule "lib/pico_audio_i2s_32b"] + path = lib/pico_audio_i2s_32b + url = git@github.com:elehobica/pico_audio_i2s_32b.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 06de3c4..4ad0647 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ set(CMAKE_CXX_STANDARD 17) pico_sdk_init() add_subdirectory(lib/pico_spdif_rx pico_spdif_rx) -add_subdirectory(lib/pico_audio_32b pico_audio_32b) # for extended audio.h +add_subdirectory(lib/pico_audio_i2s_32b/src/pico_audio_32b pico_audio_32b) # for extended audio.h add_subdirectory(lib/pico_audio_i2s_32b pico_audio_i2s_32b) # select sample application here (choose only one) diff --git a/lib/pico_audio_32b/CMakeLists.txt b/lib/pico_audio_32b/CMakeLists.txt deleted file mode 100644 index f8a8c35..0000000 --- a/lib/pico_audio_32b/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -if (NOT TARGET pico_audio_32b_headers) - add_library(pico_audio_32b_headers INTERFACE) - target_include_directories(pico_audio_32b_headers INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) - target_link_libraries(pico_audio_32b_headers INTERFACE pico_util_buffer) -endif() - -if (NOT TARGET pico_audio_32b) - add_library(pico_audio_32b INTERFACE) - - target_sources(pico_audio_32b INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/audio.cpp - $<$>:${CMAKE_CURRENT_LIST_DIR}/audio_utils.S> - ) - - target_link_libraries(pico_audio_32b INTERFACE pico_audio_32b_headers pico_sync pico_util_buffer) -endif() diff --git a/lib/pico_audio_32b/audio.cpp b/lib/pico_audio_32b/audio.cpp deleted file mode 100644 index 273591d..0000000 --- a/lib/pico_audio_32b/audio.cpp +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -// Modified by Elehobica, 2021 - -#include -#include "pico/audio.h" -#include "pico/sample_conversion.h" - -// ====================== -// == DEBUGGING ========= - -#define ENABLE_AUDIO_ASSERTIONS - -#ifdef ENABLE_AUDIO_ASSERTIONS -#define audio_assert(x) assert(x) -#else -#define audio_assert(x) (void)0 -#endif - -inline static audio_buffer_t *list_remove_head(audio_buffer_t **phead) { - audio_buffer_t *ab = *phead; - - if (ab) { - *phead = ab->next; - ab->next = NULL; - } - - return ab; -} - -inline static audio_buffer_t *list_remove_head_with_tail(audio_buffer_t **phead, - audio_buffer_t **ptail) { - audio_buffer_t *ab = *phead; - - if (ab) { - *phead = ab->next; - - if (!ab->next) { - audio_assert(*ptail == ab); - *ptail = NULL; - } else { - ab->next = NULL; - } - } - - return ab; -} - -inline static void list_prepend(audio_buffer_t **phead, audio_buffer_t *ab) { - audio_assert(ab->next == NULL); - audio_assert(ab != *phead); - ab->next = *phead; - *phead = ab; -} - -// todo add a tail for these already sorted lists as we generally insert on the end -inline static void list_append_with_tail(audio_buffer_t **phead, audio_buffer_t **ptail, - audio_buffer_t *ab) { - audio_assert(ab->next == NULL); - audio_assert(ab != *phead); - audio_assert(ab != *ptail); - - if (!*phead) { - audio_assert(!*ptail); - *ptail = ab; - // insert at the beginning - list_prepend(phead, ab); - } else { - // insert at end - (*ptail)->next = ab; - *ptail = ab; - } -} - -audio_buffer_t *get_free_audio_buffer(audio_buffer_pool_t *context, bool block) { - audio_buffer_t *ab; - - do { - uint32_t save = spin_lock_blocking(context->free_list_spin_lock); - ab = list_remove_head(&context->free_list); - spin_unlock(context->free_list_spin_lock, save); - if (ab || !block) break; - __wfe(); - } while (true); - return ab; -} - -void queue_free_audio_buffer(audio_buffer_pool_t *context, audio_buffer_t *ab) { - assert(!ab->next); - uint32_t save = spin_lock_blocking(context->free_list_spin_lock); - list_prepend(&context->free_list, ab); - spin_unlock(context->free_list_spin_lock, save); - __sev(); -} - -audio_buffer_t *get_full_audio_buffer(audio_buffer_pool_t *context, bool block) { - audio_buffer_t *ab; - - do { - uint32_t save = spin_lock_blocking(context->prepared_list_spin_lock); - ab = list_remove_head_with_tail(&context->prepared_list, &context->prepared_list_tail); - spin_unlock(context->prepared_list_spin_lock, save); - if (ab || !block) break; - __wfe(); - } while (true); - return ab; -} - -void queue_full_audio_buffer(audio_buffer_pool_t *context, audio_buffer_t *ab) { - assert(!ab->next); - uint32_t save = spin_lock_blocking(context->prepared_list_spin_lock); - list_append_with_tail(&context->prepared_list, &context->prepared_list_tail, ab); - spin_unlock(context->prepared_list_spin_lock, save); - __sev(); -} - -void producer_pool_give_buffer_default(audio_connection_t *connection, audio_buffer_t *buffer) { - queue_full_audio_buffer(connection->producer_pool, buffer); -} - -audio_buffer_t *producer_pool_take_buffer_default(audio_connection_t *connection, bool block) { - return get_free_audio_buffer(connection->producer_pool, block); -} - -void consumer_pool_give_buffer_default(audio_connection_t *connection, audio_buffer_t *buffer) { - queue_free_audio_buffer(connection->consumer_pool, buffer); -} - -audio_buffer_t *consumer_pool_take_buffer_default(audio_connection_t *connection, bool block) { - return get_full_audio_buffer(connection->consumer_pool, block); -} - -static audio_connection_t connection_default = { - .producer_pool_take = producer_pool_take_buffer_default, - .producer_pool_give = producer_pool_give_buffer_default, - .consumer_pool_take = consumer_pool_take_buffer_default, - .consumer_pool_give = consumer_pool_give_buffer_default, -}; - -audio_buffer_t *audio_new_buffer(audio_buffer_format_t *format, int buffer_sample_count) { - audio_buffer_t *buffer = (audio_buffer_t *) calloc(1, sizeof(audio_buffer_t)); - audio_init_buffer(buffer, format, buffer_sample_count); - return buffer; -} - -void audio_init_buffer(audio_buffer_t *audio_buffer, audio_buffer_format_t *format, int buffer_sample_count) { - audio_buffer->format = format; - audio_buffer->buffer = pico_buffer_alloc(buffer_sample_count * format->sample_stride); - audio_buffer->max_sample_count = buffer_sample_count; - audio_buffer->sample_count = 0; -} - -audio_buffer_pool_t * -audio_new_buffer_pool(audio_buffer_format_t *format, int buffer_count, int buffer_sample_count) { - audio_buffer_pool_t *ac = (audio_buffer_pool_t *) calloc(1, sizeof(audio_buffer_pool_t)); - audio_buffer_t *audio_buffers = buffer_count ? (audio_buffer_t *) calloc(buffer_count, - sizeof(audio_buffer_t)) : 0; - ac->format = format->format; - for (int i = 0; i < buffer_count; i++) { - audio_init_buffer(audio_buffers + i, format, buffer_sample_count); - audio_buffers[i].next = i != buffer_count - 1 ? &audio_buffers[i + 1] : NULL; - } - // todo one per channel? - ac->free_list_spin_lock = spin_lock_init(SPINLOCK_ID_AUDIO_FREE_LIST_LOCK); - ac->free_list = audio_buffers; - ac->prepared_list_spin_lock = spin_lock_init(SPINLOCK_ID_AUDIO_PREPARED_LISTS_LOCK); - ac->prepared_list = NULL; - ac->prepared_list_tail = NULL; - ac->connection = &connection_default; - return ac; -} - -audio_buffer_t *audio_new_wrapping_buffer(audio_buffer_format_t *format, mem_buffer_t *buffer) { - audio_buffer_t *audio_buffer = (audio_buffer_t *) calloc(1, sizeof(audio_buffer_t)); - if (audio_buffer) { - audio_buffer->format = format; - audio_buffer->buffer = buffer; - audio_buffer->max_sample_count = buffer->size / format->sample_stride; - audio_buffer->sample_count = 0; - audio_buffer->next = 0; - } - return audio_buffer; - -} - -audio_buffer_pool_t * -audio_new_producer_pool(audio_buffer_format_t *format, int buffer_count, int buffer_sample_count) { - audio_buffer_pool_t *ac = audio_new_buffer_pool(format, buffer_count, buffer_sample_count); - ac->type = audio_buffer_pool::ac_producer; - return ac; -} - -audio_buffer_pool_t * -audio_new_consumer_pool(audio_buffer_format_t *format, int buffer_count, int buffer_sample_count) { - audio_buffer_pool_t *ac = audio_new_buffer_pool(format, buffer_count, buffer_sample_count); - ac->type = audio_buffer_pool::ac_consumer; - return ac; -} - -void audio_complete_connection(audio_connection_t *connection, audio_buffer_pool_t *producer_pool, - audio_buffer_pool_t *consumer_pool) { - assert(producer_pool->type == audio_buffer_pool::ac_producer); - assert(consumer_pool->type == audio_buffer_pool::ac_consumer); - producer_pool->connection = connection; - consumer_pool->connection = connection; - connection->producer_pool = producer_pool; - connection->consumer_pool = consumer_pool; -} - -void give_audio_buffer(audio_buffer_pool_t *ac, audio_buffer_t *buffer) { - buffer->user_data = 0; - assert(ac->connection); - if (ac->type == audio_buffer_pool::ac_producer) - ac->connection->producer_pool_give(ac->connection, buffer); - else - ac->connection->consumer_pool_give(ac->connection, buffer); -} - -audio_buffer_t *take_audio_buffer(audio_buffer_pool_t *ac, bool block) { - assert(ac->connection); - if (ac->type == audio_buffer_pool::ac_producer) - return ac->connection->producer_pool_take(ac->connection, block); - else - return ac->connection->consumer_pool_take(ac->connection, block); -} - -// todo rename this - this is s16 to s16 -audio_buffer_t *mono_to_mono_consumer_take(audio_connection_t *connection, bool block) { - return consumer_pool_take, Mono>(connection, block); -} - -// todo rename this - this is s16 to s16 -audio_buffer_t *stereo_s16_to_stereo_s16_consumer_take(audio_connection_t *connection, bool block) { - return consumer_pool_take, Stereo>(connection, block); -} - -audio_buffer_t *stereo_s32_to_stereo_s32_consumer_take(audio_connection_t *connection, bool block) { - return consumer_pool_take, Stereo>(connection, block); -} - -// todo rename this - this is s16 to s16 -audio_buffer_t *mono_to_stereo_consumer_take(audio_connection_t *connection, bool block) { - return consumer_pool_take, Mono>(connection, block); -} - -audio_buffer_t *mono_s8_to_mono_consumer_take(audio_connection_t *connection, bool block) { - return consumer_pool_take, Mono>(connection, block); -} - -audio_buffer_t *mono_s8_to_stereo_consumer_take(audio_connection_t *connection, bool block) { - return consumer_pool_take, Mono>(connection, block); -} - -void stereo_s16_to_stereo_s16_producer_give(audio_connection_t *connection, audio_buffer_t *buffer) { - return producer_pool_blocking_give, Stereo>(connection, buffer); -} - -void stereo_s32_to_stereo_s32_producer_give(audio_connection_t *connection, audio_buffer_t *buffer) { - return producer_pool_blocking_give, Stereo>(connection, buffer); -} \ No newline at end of file diff --git a/lib/pico_audio_32b/audio_utils.S b/lib/pico_audio_32b/audio_utils.S deleted file mode 100644 index e46ec5e..0000000 --- a/lib/pico_audio_32b/audio_utils.S +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include "hardware/regs/addressmap.h" -#include "hardware/regs/sio.h" - -.syntax unified -.cpu cortex-m0plus -.thumb - -#define AUDIO_UPSAMPLE_SCALE_BITS 12 -.align 2 -.section .time_critical.audio_upsample -.global audio_upsample -.type audio_upsample,%function -// step is fraction of an input sample per output sample * (1 << AUDIO_UPSAMPLE_SCALE_BITS) and should be < (1 << AUDIO_UPSAMPLE_SCALE_BITS) ... i.e. we we are upsampling (otherwise results are undefined) -// void audio_upsample(int16_t *input, int16_t *output, int count, uint32_t step) -.thumb_func -audio_upsample: - push {r4, r5, r6, r7, lr} - lsls r2, #1 - mov ip, r1 - add ip, r2 - ldr r6, =#SIO_BASE + SIO_INTERP0_ACCUM0_OFFSET - // interp_configure_with_signed_and_blend - ldr r4, =# ((AUDIO_UPSAMPLE_SCALE_BITS - 1) << SIO_INTERP0_CTRL_LANE0_SHIFT_LSB) | (1 << SIO_INTERP0_CTRL_LANE0_MASK_LSB_LSB) | ((24 - AUDIO_UPSAMPLE_SCALE_BITS) << SIO_INTERP0_CTRL_LANE0_MASK_MSB_LSB) | SIO_INTERP0_CTRL_LANE0_BLEND_BITS - str r4, [r6, #SIO_INTERP0_CTRL_LANE0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - // interp_configure_with_signed_and_cross_input - ldr r4, =# ((AUDIO_UPSAMPLE_SCALE_BITS - 8) << SIO_INTERP0_CTRL_LANE1_SHIFT_LSB) | (0 << SIO_INTERP0_CTRL_LANE1_MASK_LSB_LSB) | (7 << SIO_INTERP0_CTRL_LANE1_MASK_MSB_LSB) | SIO_INTERP0_CTRL_LANE1_SIGNED_BITS | SIO_INTERP0_CTRL_LANE1_CROSS_INPUT_BITS - str r4, [r6, #SIO_INTERP0_CTRL_LANE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - str r0, [r6, #SIO_INTERP0_BASE2_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - movs r0, #0 - str r0, [r6, #SIO_INTERP0_ACCUM0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - mov r7, r0 // last_offset = 0 (invalid) - movs r2, #2 - - // r0 0 - // r1 output - // r2 2 - // r3 step - // r4 temp - // r5 temp - // r6 interp_hw - // r7 last_offset - // ip end - b 4f - -1: // aligned - ldr r5, [r4] - str r5, [r6, #SIO_INTERP0_BASE_1AND0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] -2: // unchanged sample ptr - ldr r4, [r6, #SIO_INTERP0_PEEK_LANE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - strh r4, [r1] - str r3, [r6, #SIO_INTERP0_ACCUM0_ADD_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - add r1, r2 - cmp r1, ip - beq 5f -3: // next sample - ldr r4, [r6, #SIO_INTERP0_PEEK_FULL_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - cmp r4, r7 - beq 2b - mov r7, r4 - tst r4, r2 - beq 1b - ldrsh r5, [r4, r0] - str r5, [r6, #SIO_INTERP0_BASE0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - ldrsh r4, [r4, r2] - str r4, [r6, #SIO_INTERP0_BASE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - ldr r4, [r6, #SIO_INTERP0_PEEK_LANE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - strh r4, [r1] - str r3, [r6, #SIO_INTERP0_ACCUM0_ADD_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - add r1, r2 -4: - cmp r1, ip - bne 3b -5: - pop {r4, r5, r6, r7, pc} - -.align 2 -.section .time_critical.audio_upsample_words -.global audio_upsample_words -.type audio_upsample_words,%function -// step is fraction of an input sample per output sample * (1 << AUDIO_UPSAMPLE_SCALE_BITS) and should be < (1 << AUDIO_UPSAMPLE_SCALE_BITS) ... i.e. we we are upsampling (otherwise results are undefined) -// void audio_upsample(int16_t *input, int16_t *output_aligned, int output_word_count, uint32_t step) -.thumb_func -audio_upsample_words: - push {r4, r5, r6, r7, lr} - lsls r2, #2 - mov ip, r1 - add ip, r2 - ldr r6, =#SIO_BASE + SIO_INTERP0_ACCUM0_OFFSET - // interp_configure_with_blend - ldr r4, =# ((AUDIO_UPSAMPLE_SCALE_BITS - 1) << SIO_INTERP0_CTRL_LANE0_SHIFT_LSB) | (1 << SIO_INTERP0_CTRL_LANE0_MASK_LSB_LSB) | ((24 -AUDIO_UPSAMPLE_SCALE_BITS) << SIO_INTERP0_CTRL_LANE0_MASK_MSB_LSB) | SIO_INTERP0_CTRL_LANE0_BLEND_BITS - str r4, [r6, #SIO_INTERP0_CTRL_LANE0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - // interp_configure_with_signed_and_cross_input - ldr r4, =# ((AUDIO_UPSAMPLE_SCALE_BITS - 8) << SIO_INTERP0_CTRL_LANE1_SHIFT_LSB) | (0 << SIO_INTERP0_CTRL_LANE1_MASK_LSB_LSB) | (7 << SIO_INTERP0_CTRL_LANE1_MASK_MSB_LSB) | SIO_INTERP0_CTRL_LANE1_SIGNED_BITS | SIO_INTERP0_CTRL_LANE1_CROSS_INPUT_BITS - str r4, [r6, #SIO_INTERP0_CTRL_LANE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - str r0, [r6, #SIO_INTERP0_BASE2_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - movs r0, #0 - str r0, [r6, #SIO_INTERP0_ACCUM0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - mov r7, r0 // last_offset = 0 (invalid) - movs r2, #2 - - // r0 0 - // r1 output - // r2 2 - // r3 step - // r4 temp - // r5 temp - // r6 interp_hw - // r7 last_offset - // ip end - b 4f - -1: // aligned A - ldr r5, [r4] - str r5, [r6, #SIO_INTERP0_BASE_1AND0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] -2: // unchanged sample ptr A - ldr r4, [r6, #SIO_INTERP0_PEEK_LANE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - // output A - strh r4, [r1] - str r3, [r6, #SIO_INTERP0_ACCUM0_ADD_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - - // next sample B - ldr r4, [r6, #SIO_INTERP0_PEEK_FULL_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - cmp r4, r7 - beq 6f - - mov r7, r4 - tst r4, r2 - bne 7f - -8: - // aligned B - ldr r5, [r4] - str r5, [r6, #SIO_INTERP0_BASE_1AND0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - -6: // unchanged sample ptr B - ldr r4, [r6, #SIO_INTERP0_PEEK_LANE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - strh r4, [r1, r2] - str r3, [r6, #SIO_INTERP0_ACCUM0_ADD_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - adds r1, #4 - cmp r1, ip - beq 5f - -3: // next sample A - ldr r4, [r6, #SIO_INTERP0_PEEK_FULL_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - cmp r4, r7 - beq 2b - mov r7, r4 - tst r4, r2 - beq 1b - ldrsh r5, [r4, r0] - str r5, [r6, #SIO_INTERP0_BASE0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - ldrsh r4, [r4, r2] - str r4, [r6, #SIO_INTERP0_BASE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - ldr r4, [r6, #SIO_INTERP0_PEEK_LANE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - strh r4, [r1] - - str r3, [r6, #SIO_INTERP0_ACCUM0_ADD_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - - // next sample B - ldr r4, [r6, #SIO_INTERP0_PEEK_FULL_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - cmp r4, r7 - beq 6b - mov r7, r4 - tst r4, r2 - beq 8b -7: // unalignedb - ldrsh r5, [r4, r0] - str r5, [r6, #SIO_INTERP0_BASE0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - ldrsh r4, [r4, r2] - str r4, [r6, #SIO_INTERP0_BASE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - ldr r4, [r6, #SIO_INTERP0_PEEK_LANE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - strh r4, [r1, r2] - str r3, [r6, #SIO_INTERP0_ACCUM0_ADD_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - adds r1, #4 - -4: - cmp r1, ip - bne 3b - -5: - pop {r4, r5, r6, r7, pc} - -.global audio_upsample_double -.type audio_upsample_double,%function -// step is fraction of an input sample per output sample * (1 << AUDIO_UPSAMPLE_SCALE_BITS) and should be < (1 << AUDIO_UPSAMPLE_SCALE_BITS) ... i.e. we we are upsampling (otherwise results are undefined) -// void audio_upsample(int16_t *input, int16_t *output, int count, uint32_t step) -.thumb_func -audio_upsample_double: - push {r4, r5, r6, r7, lr} - lsls r2, #2 - mov ip, r1 - add ip, r2 - ldr r6, =#SIO_BASE + SIO_INTERP0_ACCUM0_OFFSET - // interp_configure_with_signed_and_blend - ldr r4, =# ((AUDIO_UPSAMPLE_SCALE_BITS - 1) << SIO_INTERP0_CTRL_LANE0_SHIFT_LSB) | (1 << SIO_INTERP0_CTRL_LANE0_MASK_LSB_LSB) | ((24 - AUDIO_UPSAMPLE_SCALE_BITS) << SIO_INTERP0_CTRL_LANE0_MASK_MSB_LSB) | SIO_INTERP0_CTRL_LANE0_BLEND_BITS - str r4, [r6, #SIO_INTERP0_CTRL_LANE0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - // interp_configure_with_signed_and_cross_input - ldr r4, =# ((AUDIO_UPSAMPLE_SCALE_BITS - 8) << SIO_INTERP0_CTRL_LANE1_SHIFT_LSB) | (0 << SIO_INTERP0_CTRL_LANE1_MASK_LSB_LSB) | (7 << SIO_INTERP0_CTRL_LANE1_MASK_MSB_LSB) | SIO_INTERP0_CTRL_LANE1_SIGNED_BITS | SIO_INTERP0_CTRL_LANE1_CROSS_INPUT_BITS - str r4, [r6, #SIO_INTERP0_CTRL_LANE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - str r0, [r6, #SIO_INTERP0_BASE2_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - movs r0, #0 - str r0, [r6, #SIO_INTERP0_ACCUM0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - mov r7, r0 // last_offset = 0 (invalid) - movs r2, #2 - - // r0 0 - // r1 output - // r2 2 - // r3 step - // r4 temp - // r5 temp - // r6 interp_hw - // r7 last_offset - // ip end - b 4f - -1: // aligned - ldr r5, [r4] - str r5, [r6, #SIO_INTERP0_BASE_1AND0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] -2: // unchanged sample ptr - ldr r4, [r6, #SIO_INTERP0_PEEK_LANE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - strh r4, [r1] - str r3, [r6, #SIO_INTERP0_ACCUM0_ADD_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - strh r4, [r1, #2] - add r1, r2 - add r1, r2 - cmp r1, ip - beq 5f -3: // next sample - ldr r4, [r6, #SIO_INTERP0_PEEK_FULL_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - cmp r4, r7 - beq 2b - mov r7, r4 - tst r4, r2 - beq 1b - ldrsh r5, [r4, r0] - str r5, [r6, #SIO_INTERP0_BASE0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - ldrsh r4, [r4, r2] - str r4, [r6, #SIO_INTERP0_BASE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - ldr r4, [r6, #SIO_INTERP0_PEEK_LANE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - strh r4, [r1] - str r3, [r6, #SIO_INTERP0_ACCUM0_ADD_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] - strh r4, [r1, #2] - add r1, r2 - add r1, r2 -4: - cmp r1, ip - bne 3b -5: - pop {r4, r5, r6, r7, pc} diff --git a/lib/pico_audio_32b/include/pico/audio.h b/lib/pico_audio_32b/include/pico/audio.h deleted file mode 100644 index 273ad44..0000000 --- a/lib/pico_audio_32b/include/pico/audio.h +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -// Modified by Elehobica, 2021 - -#ifndef _PICO_AUDIO_H -#define _PICO_AUDIO_H - -#include "pico.h" -#include "pico/util/buffer.h" -#include "hardware/sync.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** \file audio.h -* \defgroup pico_audio pico_audio - * - * Common API for audio output - * - */ - -// PICO_CONFIG: SPINLOCK_ID_AUDIO_FREE_LIST_LOCK, Spinlock number for the audio free list, min=0, max=31, default=6, group=audio -#ifndef SPINLOCK_ID_AUDIO_FREE_LIST_LOCK -#define SPINLOCK_ID_AUDIO_FREE_LIST_LOCK 6 -#endif - -// PICO_CONFIG: SPINLOCK_ID_AUDIO_PREPARED_LISTS_LOCK, Spinlock number for the audio prepared list, min=0, max=31, default=7, group=audio -#ifndef SPINLOCK_ID_AUDIO_PREPARED_LISTS_LOCK -#define SPINLOCK_ID_AUDIO_PREPARED_LISTS_LOCK 7 -#endif - -// PICO_CONFIG: PICO_AUDIO_NOOP, Enable/disable audio by forcing NOOPS, type=bool, default=0, group=audio -#ifndef PICO_AUDIO_NOOP -#define PICO_AUDIO_NOOP 0 -#endif - -typedef enum { - AUDIO_PCM_FORMAT_S32 = 0, ///< signed 32bit PCM - AUDIO_PCM_FORMAT_S16, ///< signed 16bit PCM - AUDIO_PCM_FORMAT_S8, ///< signed 8bit PCM - AUDIO_PCM_FORMAT_U32, ///< unsigned 16bit PCM - AUDIO_PCM_FORMAT_U16, ///< unsigned 16bit PCM - AUDIO_PCM_FORMAT_U8 ///< unsigned 16bit PCM -} audio_pcm_format_t; - -typedef enum { - AUDIO_CHANNEL_MONO = 1, - AUDIO_CHANNEL_STEREO = 2 -} audio_channel_t; - -/** \brief Audio format definition - */ -typedef struct audio_format { - uint32_t sample_freq; ///< Sample frequency in Hz - audio_pcm_format_t pcm_format; ///< Audio format \ref audio_formats - audio_channel_t channel_count; ///< Number of channels -} audio_format_t; - -/** \brief Audio buffer format definition - */ -typedef struct audio_buffer_format { - const audio_format_t *format; ///< Audio format - uint16_t sample_stride; ///< Sample stride -} audio_buffer_format_t; - -/** \brief Audio buffer definition - */ -typedef struct audio_buffer { - mem_buffer_t *buffer; - const audio_buffer_format_t *format; - uint32_t sample_count; - uint32_t max_sample_count; - uint32_t user_data; // only valid while the user has the buffer - // private - todo make an internal version - struct audio_buffer *next; -} audio_buffer_t; - -typedef struct audio_connection audio_connection_t; - -typedef struct audio_buffer_pool { - enum { - ac_producer, ac_consumer - } type; - const audio_format_t *format; - // private - audio_connection_t *connection; - spin_lock_t *free_list_spin_lock; - // ----- begin protected by free_list_spin_lock ----- - audio_buffer_t *free_list; - spin_lock_t *prepared_list_spin_lock; - audio_buffer_t *prepared_list; - audio_buffer_t *prepared_list_tail; -} audio_buffer_pool_t; - -typedef struct audio_connection audio_connection_t; - -struct audio_connection { - audio_buffer_t *(*producer_pool_take)(audio_connection_t *connection, bool block); - - void (*producer_pool_give)(audio_connection_t *connection, audio_buffer_t *buffer); - - audio_buffer_t *(*consumer_pool_take)(audio_connection_t *connection, bool block); - - void (*consumer_pool_give)(audio_connection_t *connection, audio_buffer_t *buffer); - - audio_buffer_pool_t *producer_pool; - audio_buffer_pool_t *consumer_pool; -}; - -/*! \brief Allocate and initialise an audio producer pool - * \ingroup pico_audio - * - * \param format Format of the audio buffer - * \param buffer_count \todo - * \param buffer_sample_count \todo - * \return Pointer to an audio_buffer_pool - */ -audio_buffer_pool_t *audio_new_producer_pool(audio_buffer_format_t *format, int buffer_count, - int buffer_sample_count); - -/*! \brief Allocate and initialise an audio consumer pool - * \ingroup pico_audio - * - * \param format Format of the audio buffer - * \param buffer_count - * \param buffer_sample_count - * \return Pointer to an audio_buffer_pool - */ -audio_buffer_pool_t *audio_new_consumer_pool(audio_buffer_format_t *format, int buffer_count, - int buffer_sample_count); - -/*! \brief Allocate and initialise an audio wrapping buffer - * \ingroup pico_audio - * - * \param format Format of the audio buffer - * \param buffer \todo - * \return Pointer to an audio_buffer - */ -audio_buffer_t *audio_new_wrapping_buffer(audio_buffer_format_t *format, mem_buffer_t *buffer); - -/*! \brief Allocate and initialise an new audio buffer - * \ingroup pico_audio - * - * \param format Format of the audio buffer - * \param buffer_sample_count \todo - * \return Pointer to an audio_buffer - */ -audio_buffer_t *audio_new_buffer(audio_buffer_format_t *format, int buffer_sample_count); - -/*! \brief Initialise an audio buffer - * \ingroup pico_audio - * - * \param audio_buffer Pointer to an audio_buffer - * \param format Format of the audio buffer - * \param buffer_sample_count \todo - */ -void audio_init_buffer(audio_buffer_t *audio_buffer, audio_buffer_format_t *format, int buffer_sample_count); - -/*! \brief \todo - * \ingroup pico_audio - * - * \param ac \todo - * \param buffer \todo - * \return Pointer to an audio_buffer - */ -void give_audio_buffer(audio_buffer_pool_t *ac, audio_buffer_t *buffer); - -/*! \brief \todo - * \ingroup pico_audio - * - * \return Pointer to an audio_buffer - */ -audio_buffer_t *take_audio_buffer(audio_buffer_pool_t *ac, bool block); - -/*! \brief \todo - * \ingroup pico_audio - * - */ -static inline void release_audio_buffer(audio_buffer_pool_t *ac, audio_buffer_t *buffer) { - buffer->sample_count = 0; - give_audio_buffer(ac, buffer); -} - -/*! \brief \todo - * \ingroup pico_audio - * - * todo we are currently limited to 4095+1 input samples - * step is fraction of an input sample per output sample * 0x1000 and should be < 0x1000 i.e. we we are up-sampling (otherwise results are undefined) - */ -void audio_upsample(int16_t *input, int16_t *output, uint output_count, uint32_t step); - -/*! \brief \todo - * \ingroup pico_audio - * similar but the output buffer is word aligned, and we output an even number of samples.. this is slightly faster than the above - * todo we are currently limited to 4095+1 input samples - * step is fraction of an input sample per output sample * 0x1000 and should be < 0x1000 i.e. we we are up-sampling (otherwise results are undefined) - */ -void audio_upsample_words(int16_t *input, int16_t *output_aligned, uint output_word_count, uint32_t step); - -/*! \brief \todo - * \ingroup pico_audio - */ -void audio_upsample_double(int16_t *input, int16_t *output, uint output_count, uint32_t step); - -/*! \brief \todo - * \ingroup pico_audio - */ -void audio_complete_connection(audio_connection_t *connection, audio_buffer_pool_t *producer, - audio_buffer_pool_t *consumer); - -/*! \brief \todo - * \ingroup pico_audio - */ -audio_buffer_t *get_free_audio_buffer(audio_buffer_pool_t *context, bool block); - -/*! \brief \todo - * \ingroup pico_audio - */ -void queue_free_audio_buffer(audio_buffer_pool_t *context, audio_buffer_t *ab); - -/*! \brief \todo - * \ingroup pico_audio - */ -audio_buffer_t *get_full_audio_buffer(audio_buffer_pool_t *context, bool block); - -/*! \brief \todo - * \ingroup pico_audio - */ -void queue_full_audio_buffer(audio_buffer_pool_t *context, audio_buffer_t *ab); - -/*! \brief \todo - * \ingroup pico_audio - * - * generally an pico_audio connection uses 3 of the defaults and does the hard work in one of them - */ -void consumer_pool_give_buffer_default(audio_connection_t *connection, audio_buffer_t *buffer); - -/*! \brief \todo - * \ingroup pico_audio - */ -audio_buffer_t *consumer_pool_take_buffer_default(audio_connection_t *connection, bool block); - -/*! \brief \todo - * \ingroup pico_audio - */ -void producer_pool_give_buffer_default(audio_connection_t *connection, audio_buffer_t *buffer); - -/*! \brief \todo - * \ingroup pico_audio - */ -audio_buffer_t *producer_pool_take_buffer_default(audio_connection_t *connection, bool block); - -enum audio_correction_mode { - none, - fixed_dither, - dither, - noise_shaped_dither, -}; - -struct buffer_copying_on_consumer_take_connection { - struct audio_connection core; - audio_buffer_t *current_producer_buffer; - uint32_t current_producer_buffer_pos; -}; - -struct producer_pool_blocking_give_connection { - audio_connection_t core; - audio_buffer_t *current_consumer_buffer; - uint32_t current_consumer_buffer_pos; -}; - -/*! \brief \todo - * \ingroup pico_audio - */ -audio_buffer_t *mono_to_mono_consumer_take(audio_connection_t *connection, bool block); - -/*! \brief \todo - * \ingroup pico_audio - */ -audio_buffer_t *mono_s8_to_mono_consumer_take(audio_connection_t *connection, bool block); - -/*! \brief \todo - * \ingroup pico_audio - */ -audio_buffer_t *stereo_s16_to_stereo_s16_consumer_take(audio_connection_t *connection, bool block); - -/*! \brief \todo - * \ingroup pico_audio - */ -audio_buffer_t *stereo_s32_to_stereo_s32_consumer_take(audio_connection_t *connection, bool block); - -/*! \brief \todo - * \ingroup pico_audio - */ -audio_buffer_t *mono_to_stereo_consumer_take(audio_connection_t *connection, bool block); - -/*! \brief \todo - * \ingroup pico_audio - */ -audio_buffer_t *mono_s8_to_stereo_consumer_take(audio_connection_t *connection, bool block); - -/*! \brief \todo - * \ingroup pico_audio - */ -void stereo_s16_to_stereo_s16_producer_give(audio_connection_t *connection, audio_buffer_t *buffer); - -/*! \brief \todo - * \ingroup pico_audio - */ -void stereo_s32_to_stereo_s32_producer_give(audio_connection_t *connection, audio_buffer_t *buffer); - -// not worth a separate header for now -typedef struct __packed pio_audio_channel_config { - uint8_t base_pin; - uint8_t dma_channel; - uint8_t pio_sm; -} pio_audio_channel_config_t; - -#ifdef __cplusplus -} -#endif - -#endif //_AUDIO_H diff --git a/lib/pico_audio_32b/include/pico/sample_conversion.h b/lib/pico_audio_32b/include/pico/sample_conversion.h deleted file mode 100644 index f337ef6..0000000 --- a/lib/pico_audio_32b/include/pico/sample_conversion.h +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -// Modified by Elehobica, 2021 - -#ifndef SOFTWARE_SAMPLE_CONVERSION_H -#define SOFTWARE_SAMPLE_CONVERSION_H - -#include -#include -#include "pico/audio.h" -#include "pico/util/buffer.h" - -template -struct FmtDetails { -public: - static const uint channel_count = 1; - static const uint frame_stride = channel_count * sizeof(_sample_t); - typedef _sample_t sample_t; -}; - -typedef struct : public FmtDetails { -} FmtU8; - -typedef struct : public FmtDetails { -} FmtS8; - -typedef struct : public FmtDetails { -} FmtU16; - -typedef struct : public FmtDetails { -} FmtS16; - -typedef struct : public FmtDetails { -} FmtU32; - -typedef struct : public FmtDetails { -} FmtS32; - -// Multi channel is just N samples back to back -template -struct MultiChannelFmt { - static const uint channel_count = ChannelCount; - static const uint frame_stride = ChannelCount * Fmt::frame_stride; - typedef typename Fmt::sample_t sample_t; -}; - -// define Mono details as one channel -template using Mono = MultiChannelFmt; - -// define Stereo details as two channels -template using Stereo = MultiChannelFmt; - -template -struct sample_converter { - static typename ToFmt::sample_t convert_sample(const typename FromFmt::sample_t &sample); -}; - -// noop conversion - -template -struct sample_converter { - static typename Fmt::sample_t convert_sample(const typename Fmt::sample_t &sample) { - return sample; - } -}; - -// converters to S16 -template<> -struct sample_converter { - static int16_t convert_sample(const uint16_t &sample) { - return sample ^ 0x8000u; - } -}; - -template<> -struct sample_converter { - static int16_t convert_sample(const int8_t &sample) { - return sample << 8u; - } -}; - -template<> -struct sample_converter { - static int16_t convert_sample(const uint8_t &sample) { - return (sample << 8u) ^ 0x8000u; - } -}; - -// converters to U16 - -template<> -struct sample_converter { - static uint16_t convert_sample(const int8_t &sample) { - return (sample << 8u) ^ 0x8000u; - } -}; - -template<> -struct sample_converter { - static uint16_t convert_sample(const uint8_t &sample) { - return sample << 8u; - } -}; - -template<> -struct sample_converter { - static uint16_t convert_sample(const int16_t &sample) { - return sample ^ 0x8000u; - } -}; - -// converters to S8 - -template<> -struct sample_converter { - static int8_t convert_sample(const uint16_t &sample) { - return (sample ^ 0x8000u) >> 8u; - } -}; - -template<> -struct sample_converter { - static int8_t convert_sample(const uint8_t &sample) { - return sample ^ 0x80; - } -}; - -template<> -struct sample_converter { - static int8_t convert_sample(const int16_t &sample) { - return sample >> 8u; - } -}; - -// converters to U8 - -template<> -struct sample_converter { - static uint8_t convert_sample(const uint16_t &sample) { - return sample >> 8u; - } -}; - -template<> -struct sample_converter { - static uint8_t convert_sample(const int8_t &sample) { - return sample ^ 0x80; - } -}; - -template<> -struct sample_converter { - static uint8_t convert_sample(const int16_t &sample) { - return (sample ^ 0x8000u) >> 8u; - } -}; - -// template type for doing sample conversion -template -struct converting_copy { - static void copy(typename ToFmt::sample_t *dest, const typename FromFmt::sample_t *src, uint sample_count); -}; - -// Efficient copies of same sample type - -template -struct converting_copy, MultiChannelFmt> { - static void copy(typename MultiChannelFmt::sample_t *dest, - const typename MultiChannelFmt::sample_t *src, - uint sample_count) { - memcpy((void *) dest, (const void *) src, sample_count * MultiChannelFmt::frame_stride); - } -}; - -// N channel to N channel -template -struct converting_copy, MultiChannelFmt> { - static void copy(typename ToFmt::sample_t *dest, const typename FromFmt::sample_t *src, uint sample_count) { - for (uint i = 0; i < sample_count * NumChannels; i++) { - *dest++ = sample_converter::convert_sample(*src++); - } - } -}; - - -// mono->stereo conversion -template -struct converting_copy, Mono> { - static void copy(typename ToFmt::sample_t *dest, const typename FromFmt::sample_t *src, uint sample_count) { - for (; sample_count; sample_count--) { - typename ToFmt::sample_t mono_sample = sample_converter::convert_sample(*src++); - *dest++ = mono_sample; - *dest++ = mono_sample; - } - } -}; - -// stereo->mono conversion -template -struct converting_copy, Stereo> { - static void copy(typename ToFmt::sample_t *dest, const typename FromFmt::sample_t *src, uint sample_count) { - for (; sample_count; sample_count--) { - // average first in case precision is better in source - typename FromFmt::sample_t averaged_sample = (src[0] + src[1]) / 2; - src += 2; - *dest++ = sample_converter::convert_sample(averaged_sample); - } - } -}; - -template -audio_buffer_t *consumer_pool_take(audio_connection_t *connection, bool block) { - struct buffer_copying_on_consumer_take_connection *cc = (struct buffer_copying_on_consumer_take_connection *) connection; - // for now we block until we have all the data in consumer buffers - audio_buffer_t *buffer = get_free_audio_buffer(cc->core.consumer_pool, block); - if (!buffer) return NULL; - assert(buffer->format->sample_stride == ToFmt::frame_stride); - - uint32_t pos = 0; - while (pos < buffer->max_sample_count) { - if (!cc->current_producer_buffer) { - cc->current_producer_buffer = get_full_audio_buffer(cc->core.producer_pool, block); - if (!cc->current_producer_buffer) { - assert(!block); - if (!pos) { - queue_free_audio_buffer(cc->core.consumer_pool, buffer); - return NULL; - } - break; - } - assert(cc->current_producer_buffer->format->format->channel_count == FromFmt::channel_count); - assert(cc->current_producer_buffer->format->sample_stride == FromFmt::frame_stride); - cc->current_producer_buffer_pos = 0; - } - uint sample_count = std::min(buffer->max_sample_count - pos, - cc->current_producer_buffer->sample_count - cc->current_producer_buffer_pos); - converting_copy::copy( - ((typename ToFmt::sample_t *) buffer->buffer->bytes) + pos * ToFmt::channel_count, - ((typename FromFmt::sample_t *) cc->current_producer_buffer->buffer->bytes) + - cc->current_producer_buffer_pos * FromFmt::channel_count, - sample_count); - pos += sample_count; - cc->current_producer_buffer_pos += sample_count; - if (cc->current_producer_buffer_pos == cc->current_producer_buffer->sample_count) { - queue_free_audio_buffer(cc->core.producer_pool, cc->current_producer_buffer); - cc->current_producer_buffer = NULL; - } - } - buffer->sample_count = pos; - return buffer; -} - -template -void producer_pool_blocking_give(audio_connection_t *connection, audio_buffer_t *buffer) { - struct producer_pool_blocking_give_connection *pbc = (struct producer_pool_blocking_give_connection *) connection; - // for now we block until we have all the data in consumer buffers - uint32_t pos = 0; - while (pos < buffer->sample_count) { - if (!pbc->current_consumer_buffer) { - pbc->current_consumer_buffer = get_free_audio_buffer(pbc->core.consumer_pool, true); - pbc->current_consumer_buffer_pos = 0; - } - uint sample_count = std::min(buffer->sample_count - pos, - pbc->current_consumer_buffer->max_sample_count - pbc->current_consumer_buffer_pos); - assert(buffer->format->sample_stride == FromFmt::frame_stride); - assert(buffer->format->format->channel_count == FromFmt::channel_count); - converting_copy::copy( - ((typename ToFmt::sample_t *) pbc->current_consumer_buffer->buffer->bytes) + - pbc->current_consumer_buffer_pos * ToFmt::channel_count, - ((typename FromFmt::sample_t *) buffer->buffer->bytes) + pos * FromFmt::channel_count, sample_count); - pos += sample_count; - pbc->current_consumer_buffer_pos += sample_count; - if (pbc->current_consumer_buffer_pos == pbc->current_consumer_buffer->max_sample_count) { - pbc->current_consumer_buffer->sample_count = pbc->current_consumer_buffer->max_sample_count; - queue_full_audio_buffer(pbc->core.consumer_pool, pbc->current_consumer_buffer); - pbc->current_consumer_buffer = NULL; - } - } - // todo this should be a connection configuration (or a seaparate connection type) -#ifdef BLOCKING_GIVE_SYNCHRONIZE_BUFFERS - if (pbc->current_consumer_buffer) { - pbc->current_consumer_buffer->sample_count = pbc->current_consumer_buffer_pos; - queue_full_audio_buffer(pbc->core.consumer_pool, pbc->current_consumer_buffer); - pbc->current_consumer_buffer = NULL; - } -#endif - assert(pos == buffer->sample_count); - queue_free_audio_buffer(pbc->core.producer_pool, buffer); -} - -#endif //SOFTWARE_SAMPLE_CONVERSION_H diff --git a/lib/pico_audio_i2s_32b b/lib/pico_audio_i2s_32b new file mode 160000 index 0000000..1152153 --- /dev/null +++ b/lib/pico_audio_i2s_32b @@ -0,0 +1 @@ +Subproject commit 11521536d07274c10d8b89d83762af776a46bc46 diff --git a/lib/pico_audio_i2s_32b/CMakeLists.txt b/lib/pico_audio_i2s_32b/CMakeLists.txt deleted file mode 100644 index 3d3e0ba..0000000 --- a/lib/pico_audio_i2s_32b/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -if (NOT TARGET pico_audio_i2s_32b) - add_library(pico_audio_i2s_32b INTERFACE) - - pico_generate_pio_header(pico_audio_i2s_32b ${CMAKE_CURRENT_LIST_DIR}/audio_i2s.pio) - - target_sources(pico_audio_i2s_32b INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/audio_i2s.c - ) - - target_include_directories(pico_audio_i2s_32b INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) - target_link_libraries(pico_audio_i2s_32b INTERFACE - pico_stdlib - pico_multicore - hardware_dma - hardware_pio - hardware_irq - pico_audio_32b - ) -endif() \ No newline at end of file diff --git a/lib/pico_audio_i2s_32b/audio_i2s.c b/lib/pico_audio_i2s_32b/audio_i2s.c deleted file mode 100644 index a5beb21..0000000 --- a/lib/pico_audio_i2s_32b/audio_i2s.c +++ /dev/null @@ -1,723 +0,0 @@ -/* - * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -// Modified by Elehobica, 2021 - -#include - -#include "pico/stdlib.h" -#include "pico/multicore.h" -#include "hardware/pio.h" -#include "hardware/gpio.h" -#include "hardware/dma.h" -#include "hardware/irq.h" -#include "hardware/clocks.h" -#include "hardware/structs/dma.h" -#include "hardware/regs/dreq.h" - -#include "audio_i2s.pio.h" -#include "pico/audio_i2s.h" - -//#define CORE1_PROCESS_I2S_CALLBACK // Multi-Core Processing Mode (Experimentally Single-Core seems better) -//#define WATCH_DMA_TRANSFER_INTERVAL // Activate only for analysis because of watch overhead -//#define WATCH_PIO_SM_TX_FIFO_LEVEL // Activate only for analysis because of watch overhead - -CU_REGISTER_DEBUG_PINS(audio_timing) - -// ---- select at most one --- -//CU_SELECT_DEBUG_PINS(audio_timing) - - -#define audio_pio __CONCAT(pio, PICO_AUDIO_I2S_PIO) -#define GPIO_FUNC_PIOx __CONCAT(GPIO_FUNC_PIO, PICO_AUDIO_I2S_PIO) -#define DREQ_PIOx_TX0 __CONCAT(__CONCAT(DREQ_PIO, PICO_AUDIO_I2S_PIO), _TX0) - -#define DMA_IRQ_x __CONCAT(DMA_IRQ_, PICO_AUDIO_I2S_DMA_IRQ) - -static uint loaded_offset = 0; -static const audio_format_t *_i2s_input_audio_format; -static const audio_format_t *_i2s_output_audio_format; -struct { - audio_buffer_t *playing_buffer0; - audio_buffer_t *playing_buffer1; - uint32_t freq; - uint8_t pio_sm; - uint8_t dma_channel0; - uint8_t dma_channel1; -} shared_state; -static dma_channel_config dma_config0; -static dma_channel_config dma_config1; - -audio_format_t pio_i2s_consumer_format; -audio_buffer_format_t pio_i2s_consumer_buffer_format = { - .format = &pio_i2s_consumer_format, -}; - -static audio_buffer_pool_t *audio_i2s_consumer; -static audio_buffer_t silence_buffer; - -static void __isr __time_critical_func(audio_i2s_dma_irq_handler)(); - -#ifdef WATCH_PIO_SM_TX_FIFO_LEVEL -static inline uint32_t _millis(void) -{ - return to_ms_since_boot(get_absolute_time()); -} -#endif // WATCH_PIO_SM_TX_FIFO_LEVEL - -#ifdef WATCH_DMA_TRANSFER_INTERVAL -static inline uint32_t _micros(void) -{ - return to_us_since_boot(get_absolute_time()); -} -#endif // WATCH_DMA_TRANSFER_INTERVAL - -// i2s callback function to be defined at external -__attribute__((weak)) -void i2s_callback_func() -{ - /* - uint32_t time = to_ms_since_boot(get_absolute_time()); - printf("i2s_callback_func %d\n", time); - */ - return; -} - -#ifdef CORE1_PROCESS_I2S_CALLBACK - -enum FifoMessage { - RESPONSE_CORE1_THREAD_STARTED = 0, - RESPONSE_CORE1_THREAD_TERMINATED = 0, - EVENT_I2S_DMA_TRANSFER_STARTED, - NOTIFY_I2S_DISABLED -}; - -static const uint64_t FIFO_TIMEOUT = 10 * 1000; // us - -void i2s_callback_loop() -{ - multicore_fifo_push_blocking(RESPONSE_CORE1_THREAD_STARTED); -#ifndef NDEBUG - printf("i2s_callback_loop started (on core %d)\n", get_core_num()); -#endif // NDEBUG - multicore_fifo_drain(); - while (true) { - uint32_t msg = multicore_fifo_pop_blocking(); - if (msg == EVENT_I2S_DMA_TRANSFER_STARTED) { - i2s_callback_func(); - } else if (msg == NOTIFY_I2S_DISABLED) { - break; - } else { - panic("Unexpected message from Core 0\n"); - } - tight_loop_contents(); - } - multicore_fifo_push_blocking(RESPONSE_CORE1_THREAD_TERMINATED); -#ifndef NDEBUG - printf("i2s_callback_loop terminated (on core %d)\n", get_core_num()); -#endif // NDEBUG - - while (true) { tight_loop_contents(); } // infinite loop - return; -} -#endif // CORE1_PROCESS_I2S_CALLBACK - -void audio_i2s_end() { - audio_buffer_t *ab; - ab = take_audio_buffer(audio_i2s_consumer, false); - while (ab != NULL) { - free(ab->buffer->bytes); - free(ab->buffer); - ab = take_audio_buffer(audio_i2s_consumer, false); - } - ab = get_free_audio_buffer(audio_i2s_consumer, false); - while (ab != NULL) { - free(ab->buffer->bytes); - free(ab->buffer); - ab = get_free_audio_buffer(audio_i2s_consumer, false); - } - ab = get_full_audio_buffer(audio_i2s_consumer, false); - while (ab != NULL) { - free(ab->buffer->bytes); - free(ab->buffer); - ab = get_full_audio_buffer(audio_i2s_consumer, false); - } - if (shared_state.playing_buffer0 != NULL) { - free(shared_state.playing_buffer0->buffer->bytes); - free(shared_state.playing_buffer0->buffer); - } - if (shared_state.playing_buffer1 != NULL) { - free(shared_state.playing_buffer1->buffer->bytes); - free(shared_state.playing_buffer1->buffer); - } - free(audio_i2s_consumer); - free(silence_buffer.buffer->bytes); - free(silence_buffer.buffer); - shared_state.playing_buffer0 = NULL; - shared_state.playing_buffer1 = NULL; - uint8_t sm = shared_state.pio_sm; - pio_sm_clear_fifos(audio_pio, sm); - pio_sm_drain_tx_fifo(audio_pio, sm); - pio_remove_program(audio_pio, &audio_i2s_program, loaded_offset); - pio_clear_instruction_memory(audio_pio); - pio_sm_unclaim(audio_pio, sm); -} - -const audio_format_t *audio_i2s_setup(const audio_format_t *i2s_input_audio_format, const audio_format_t *i2s_output_audio_format, - const audio_i2s_config_t *config) { - _i2s_input_audio_format = i2s_input_audio_format; - _i2s_output_audio_format = i2s_output_audio_format; - uint func = GPIO_FUNC_PIOx; - gpio_set_function(config->data_pin, func); - gpio_set_function(config->clock_pin_base, func); - gpio_set_function(config->clock_pin_base + 1, func); - - uint8_t sm = shared_state.pio_sm = config->pio_sm; - pio_sm_claim(audio_pio, sm); - - loaded_offset = pio_add_program(audio_pio, &audio_i2s_program); - - assert(_i2s_output_audio_format->channel_count == AUDIO_CHANNEL_STEREO); - assert(_i2s_output_audio_format->pcm_format == AUDIO_PCM_FORMAT_S16 || _i2s_output_audio_format->pcm_format == AUDIO_PCM_FORMAT_S32); - uint res_bits = (_i2s_output_audio_format->pcm_format == AUDIO_PCM_FORMAT_S32) ? 32 : 16; - audio_i2s_program_init(audio_pio, sm, loaded_offset, config->data_pin, config->clock_pin_base, res_bits); - - silence_buffer.buffer = pico_buffer_alloc(PICO_AUDIO_I2S_BUFFER_SAMPLE_LENGTH * 4); - silence_buffer.sample_count = PICO_AUDIO_I2S_BUFFER_SAMPLE_LENGTH; - silence_buffer.format = &pio_i2s_consumer_buffer_format; - - __mem_fence_release(); - uint8_t dma_channel0 = config->dma_channel0; - uint8_t dma_channel1 = config->dma_channel1; - - shared_state.dma_channel0 = dma_channel0; - shared_state.dma_channel1 = dma_channel1; - - enum dma_channel_transfer_size i2s_dma_configure_size; - if (_i2s_output_audio_format->channel_count == AUDIO_CHANNEL_MONO) { - switch (_i2s_output_audio_format->pcm_format) { - case AUDIO_PCM_FORMAT_S8: - case AUDIO_PCM_FORMAT_U8: - i2s_dma_configure_size = DMA_SIZE_8; - assert(false); - break; - case AUDIO_PCM_FORMAT_S16: - case AUDIO_PCM_FORMAT_U16: - i2s_dma_configure_size = DMA_SIZE_16; - assert(false); - break; - case AUDIO_PCM_FORMAT_S32: - case AUDIO_PCM_FORMAT_U32: - i2s_dma_configure_size = DMA_SIZE_32; - assert(false); - break; - default: - assert(false); - break; - } - } else { - switch (_i2s_output_audio_format->pcm_format) { - case AUDIO_PCM_FORMAT_S8: - case AUDIO_PCM_FORMAT_U8: - i2s_dma_configure_size = DMA_SIZE_16; - break; - case AUDIO_PCM_FORMAT_S16: - case AUDIO_PCM_FORMAT_U16: - i2s_dma_configure_size = DMA_SIZE_32; - break; - case AUDIO_PCM_FORMAT_S32: - case AUDIO_PCM_FORMAT_U32: - i2s_dma_configure_size = DMA_SIZE_32; // Need after-treatment because of no 64bit transfer - break; - default: - assert(false); - break; - } - } - // DMA0 (configuration only) - dma_config0 = dma_channel_get_default_config(dma_channel0); - channel_config_set_transfer_data_size(&dma_config0, i2s_dma_configure_size); - channel_config_set_read_increment(&dma_config0, true); - channel_config_set_write_increment(&dma_config0, false); - channel_config_set_dreq(&dma_config0, DREQ_PIOx_TX0 + sm); - channel_config_set_chain_to(&dma_config0, dma_channel1); - // DMA1 (configuration only) - dma_config1 = dma_channel_get_default_config(dma_channel1); - channel_config_set_transfer_data_size(&dma_config1, i2s_dma_configure_size); - channel_config_set_read_increment(&dma_config1, true); - channel_config_set_write_increment(&dma_config1, false); - channel_config_set_dreq(&dma_config1, DREQ_PIOx_TX0 + sm); - channel_config_set_chain_to(&dma_config1, dma_channel0); - - return _i2s_output_audio_format; -} - -static void update_pio_frequency(uint32_t sample_freq, audio_pcm_format_t pcm_format, audio_channel_t channel_count) { - printf("setting PIO freq for target sampling freq = %d Hz\n", (int) sample_freq); - uint32_t system_clock_frequency = clock_get_hz(clk_sys); - assert(system_clock_frequency < 0x40000000); - //uint32_t divider = system_clock_frequency * 4 / sample_freq; // avoid arithmetic overflow - uint32_t divider; - uint32_t bits; - switch (pcm_format) { - case AUDIO_PCM_FORMAT_S8: - case AUDIO_PCM_FORMAT_U8: - divider = system_clock_frequency * 4 * channel_count / sample_freq; - bits = 8; - break; - case AUDIO_PCM_FORMAT_S16: - case AUDIO_PCM_FORMAT_U16: - divider = system_clock_frequency * 2 * channel_count / sample_freq; - bits = 16; - break; - case AUDIO_PCM_FORMAT_S32: - case AUDIO_PCM_FORMAT_U32: - divider = system_clock_frequency * 1 * channel_count / sample_freq; - bits = 32; - break; - default: - divider = system_clock_frequency * 2 * channel_count / sample_freq; - bits = 16; - assert(false); - break; - } - assert(divider < 0x1000000); -#if 1 // PIO_CLK_DIV_FRAC - float pio_freq = (float) system_clock_frequency * 256 / divider; // frac - printf("System clock at %u Hz, I2S clock divider %d/256: PIO freq %7.4f Hz\n", (uint) system_clock_frequency, (uint) divider, pio_freq); - pio_sm_set_clkdiv_int_frac(audio_pio, shared_state.pio_sm, divider >> 8u, divider & 0xffu); // This scheme includes clock Jitter -#else - divider >>= 8u; - float pio_freq = (float) system_clock_frequency / divider; // no frac - float samp_freq = pio_freq / ((float) bits * 2.0 * 2.0); - printf("System clock at %u Hz, I2S clock divider %d: PIO freq %7.4f Hz: sampling freq %7.4f Hz\n", (uint) system_clock_frequency, (uint) divider, pio_freq, samp_freq); - pio_sm_set_clkdiv(audio_pio, shared_state.pio_sm, divider); // No Jitter. but clock freq accuracy depends on PIO source clock freq -#endif - - shared_state.freq = sample_freq; -} - -static audio_buffer_t *wrap_consumer_take(audio_connection_t *connection, bool block) { - // support dynamic frequency shifting - if (connection->producer_pool->format->sample_freq != shared_state.freq) { - update_pio_frequency(connection->producer_pool->format->sample_freq, connection->producer_pool->format->pcm_format, connection->producer_pool->format->channel_count); - } - if (_i2s_input_audio_format->pcm_format == _i2s_output_audio_format->pcm_format) { - if (_i2s_input_audio_format->channel_count == AUDIO_CHANNEL_MONO && _i2s_input_audio_format->channel_count == AUDIO_CHANNEL_MONO) { - return mono_to_mono_consumer_take(connection, block); - } else if (_i2s_input_audio_format->channel_count == AUDIO_CHANNEL_MONO && _i2s_input_audio_format->channel_count == AUDIO_CHANNEL_STEREO) { - return mono_to_stereo_consumer_take(connection, block); - } else if (_i2s_input_audio_format->channel_count == AUDIO_CHANNEL_STEREO && _i2s_input_audio_format->channel_count == AUDIO_CHANNEL_STEREO) { - switch (_i2s_input_audio_format->pcm_format) { - case AUDIO_PCM_FORMAT_S16: - return stereo_s16_to_stereo_s16_consumer_take(connection, block); - break; - case AUDIO_PCM_FORMAT_S32: - return stereo_s32_to_stereo_s32_consumer_take(connection, block); - break; - default: - assert(false); - } - } else { - assert(false); // unsupported - } - } else { - assert(false); // unsupported - } -} - -static void wrap_producer_give(audio_connection_t *connection, audio_buffer_t *buffer) { - // support dynamic frequency shifting - if (connection->producer_pool->format->sample_freq != shared_state.freq) { - update_pio_frequency(connection->producer_pool->format->sample_freq, connection->producer_pool->format->pcm_format, connection->producer_pool->format->channel_count); - } - if (_i2s_input_audio_format->pcm_format == _i2s_output_audio_format->pcm_format) { - if (_i2s_input_audio_format->channel_count == AUDIO_CHANNEL_MONO && _i2s_input_audio_format->channel_count == AUDIO_CHANNEL_MONO) { - assert(false); - //return mono_to_mono_producer_give(connection, block); - } else if (_i2s_input_audio_format->channel_count == AUDIO_CHANNEL_MONO && _i2s_input_audio_format->channel_count == AUDIO_CHANNEL_STEREO) { - assert(false); - //return mono_to_stereo_producer_give(connection, buffer); - } else if (_i2s_input_audio_format->channel_count == AUDIO_CHANNEL_STEREO && _i2s_input_audio_format->channel_count == AUDIO_CHANNEL_STEREO) { - switch (_i2s_input_audio_format->pcm_format) { - case AUDIO_PCM_FORMAT_S16: - return stereo_s16_to_stereo_s16_producer_give(connection, buffer); - break; - case AUDIO_PCM_FORMAT_S32: - return stereo_s32_to_stereo_s32_producer_give(connection, buffer); - break; - default: - assert(false); - } - } else { - assert(false); // unsupported - } - } else { - assert(false); // unsupported - } -} - -static struct buffer_copying_on_consumer_take_connection m2s_audio_i2s_ct_connection = { - .core = { - .consumer_pool_take = wrap_consumer_take, - .consumer_pool_give = consumer_pool_give_buffer_default, - .producer_pool_take = producer_pool_take_buffer_default, - .producer_pool_give = producer_pool_give_buffer_default, - } -}; - -static struct producer_pool_blocking_give_connection m2s_audio_i2s_pg_connection = { - .core = { - .consumer_pool_take = consumer_pool_take_buffer_default, - .consumer_pool_give = consumer_pool_give_buffer_default, - .producer_pool_take = producer_pool_take_buffer_default, - .producer_pool_give = wrap_producer_give, - } -}; - -bool audio_i2s_connect_thru(audio_buffer_pool_t *producer, audio_connection_t *connection) { - return audio_i2s_connect_extra(producer, false, 2, 256, connection); -} - -bool audio_i2s_connect(audio_buffer_pool_t *producer) { - return audio_i2s_connect_thru(producer, NULL); -} - -bool audio_i2s_connect_extra(audio_buffer_pool_t *producer, bool buffer_on_give, uint buffer_count, - uint samples_per_buffer, audio_connection_t *connection) { - printf("Connecting PIO I2S audio\n"); - - // todo we need to pick a connection based on the frequency - e.g. 22050 can be more simply upsampled to 44100 - assert(producer->format->pcm_format == AUDIO_PCM_FORMAT_S16 || producer->format->pcm_format == AUDIO_PCM_FORMAT_S32); - pio_i2s_consumer_format.pcm_format = _i2s_output_audio_format->pcm_format; - // todo we could do mono - // todo we can't match exact, so we should return what we can do - pio_i2s_consumer_format.sample_freq = producer->format->sample_freq; - pio_i2s_consumer_format.channel_count = _i2s_output_audio_format->channel_count; - switch (_i2s_output_audio_format->pcm_format) { - case AUDIO_PCM_FORMAT_S8: - case AUDIO_PCM_FORMAT_U8: - pio_i2s_consumer_buffer_format.sample_stride = 1 * pio_i2s_consumer_format.channel_count; - break; - case AUDIO_PCM_FORMAT_S16: - case AUDIO_PCM_FORMAT_U16: - pio_i2s_consumer_buffer_format.sample_stride = 2 * pio_i2s_consumer_format.channel_count; - break; - case AUDIO_PCM_FORMAT_S32: - case AUDIO_PCM_FORMAT_U32: - pio_i2s_consumer_buffer_format.sample_stride = 4 * pio_i2s_consumer_format.channel_count; - break; - default: - assert(false); - break; - } - - audio_i2s_consumer = audio_new_consumer_pool(&pio_i2s_consumer_buffer_format, buffer_count, samples_per_buffer); - - update_pio_frequency(producer->format->sample_freq, producer->format->pcm_format, producer->format->channel_count); - - // todo cleanup threading - __mem_fence_release(); - - if (!connection) { - if (producer->format->channel_count == AUDIO_CHANNEL_STEREO) { - if (_i2s_input_audio_format->channel_count == AUDIO_CHANNEL_MONO) { - panic("need to merge channels down\n"); - } else if (_i2s_output_audio_format->channel_count == AUDIO_CHANNEL_MONO) { - panic("trying to play stereo thru mono not yet supported"); - } else { - printf("Copying stereo to stereo at %d Hz\n", (int) producer->format->sample_freq); - } - // todo we should support pass thru option anyway - //printf("TODO... not completing stereo audio connection properly!\n"); - } else { - if (_i2s_output_audio_format->channel_count == AUDIO_CHANNEL_MONO) { - printf("Copying mono to mono at %d Hz\n", (int) producer->format->sample_freq); - } else { - printf("Converting mono to stereo at %d Hz\n", (int) producer->format->sample_freq); - } - } - connection = buffer_on_give ? &m2s_audio_i2s_pg_connection.core : &m2s_audio_i2s_ct_connection.core; - } - audio_complete_connection(connection, producer, audio_i2s_consumer); - return true; -} - -static struct buffer_copying_on_consumer_take_connection m2s_audio_i2s_connection_s8_mono = { - .core = { - .consumer_pool_take = mono_s8_to_mono_consumer_take, - .consumer_pool_give = consumer_pool_give_buffer_default, - .producer_pool_take = producer_pool_take_buffer_default, - .producer_pool_give = producer_pool_give_buffer_default, - } -}; - -static struct buffer_copying_on_consumer_take_connection m2s_audio_i2s_connection_s8_stereo = { - .core = { - .consumer_pool_take = mono_s8_to_stereo_consumer_take, - .consumer_pool_give = consumer_pool_give_buffer_default, - .producer_pool_take = producer_pool_take_buffer_default, - .producer_pool_give = producer_pool_give_buffer_default, - } -}; - -bool audio_i2s_connect_s8(audio_buffer_pool_t *producer) { - printf("Connecting PIO I2S audio (U8)\n"); - - // todo we need to pick a connection based on the frequency - e.g. 22050 can be more simply upsampled to 44100 - assert(producer->format->pcm_format == AUDIO_PCM_FORMAT_S8); - pio_i2s_consumer_format.pcm_format = AUDIO_PCM_FORMAT_S16; - // todo we could do mono - // todo we can't match exact, so we should return what we can do - pio_i2s_consumer_format.sample_freq = producer->format->sample_freq; - pio_i2s_consumer_format.channel_count = _i2s_output_audio_format->channel_count; - switch (_i2s_output_audio_format->pcm_format) { - case AUDIO_PCM_FORMAT_S8: - case AUDIO_PCM_FORMAT_U8: - pio_i2s_consumer_buffer_format.sample_stride = 1 * pio_i2s_consumer_format.channel_count; - break; - case AUDIO_PCM_FORMAT_S16: - case AUDIO_PCM_FORMAT_U16: - pio_i2s_consumer_buffer_format.sample_stride = 2 * pio_i2s_consumer_format.channel_count; - break; - case AUDIO_PCM_FORMAT_S32: - case AUDIO_PCM_FORMAT_U32: - pio_i2s_consumer_buffer_format.sample_stride = 4 * pio_i2s_consumer_format.channel_count; - break; - default: - assert(false); - break; - } - - // we do this on take so should do it quickly... - uint samples_per_buffer = 256; - // todo with take we really only need 1 buffer - audio_i2s_consumer = audio_new_consumer_pool(&pio_i2s_consumer_buffer_format, 2, samples_per_buffer); - - // todo we need a method to calculate this in clocks - uint32_t system_clock_frequency = 48000000; - //uint32_t divider = system_clock_frequency * 4 / producer->format->sample_freq; // avoid arithmetic overflow - //uint32_t divider = system_clock_frequency * 256 / producer->format->sample_freq * 16 * 4; - uint32_t divider; - switch (producer->format->pcm_format) { - case AUDIO_PCM_FORMAT_S8: - case AUDIO_PCM_FORMAT_U8: - divider = system_clock_frequency * 4 * producer->format->channel_count * 2 / producer->format->sample_freq; - break; - case AUDIO_PCM_FORMAT_S16: - case AUDIO_PCM_FORMAT_U16: - divider = system_clock_frequency * 2 * producer->format->channel_count * 2 / producer->format->sample_freq; - break; - case AUDIO_PCM_FORMAT_S32: - case AUDIO_PCM_FORMAT_U32: - divider = system_clock_frequency * 1 * producer->format->channel_count * 2 / producer->format->sample_freq; - break; - default: - divider = system_clock_frequency * 2 * producer->format->channel_count * 2 / producer->format->sample_freq; - assert(false); - break; - } - pio_sm_set_clkdiv_int_frac(audio_pio, shared_state.pio_sm, divider >> 8u, divider & 0xffu); - - // todo cleanup threading - __mem_fence_release(); - - audio_connection_t *connection; - if (producer->format->channel_count == AUDIO_CHANNEL_STEREO) { - if (_i2s_output_audio_format->channel_count == AUDIO_CHANNEL_MONO) { - panic("trying to play stereo thru mono not yet supported"); - } - // todo we should support pass thru option anyway - printf("TODO... not completing stereo audio connection properly!\n"); - connection = &m2s_audio_i2s_connection_s8_stereo.core; - } else { - if (_i2s_output_audio_format->channel_count == AUDIO_CHANNEL_MONO) { - printf("Copying mono to mono at %d Hz\n", (int) producer->format->sample_freq); - connection = &m2s_audio_i2s_connection_s8_mono.core; - } else { - printf("Converting mono to stereo at %d Hz\n", (int) producer->format->sample_freq); - connection = &m2s_audio_i2s_connection_s8_stereo.core; - } - } - audio_complete_connection(connection, producer, audio_i2s_consumer); - return true; -} - -static inline void audio_start_dma_transfer(uint8_t dma_channel, dma_channel_config *dma_config, audio_buffer_t **playing_buffer) { - assert(!*playing_buffer); - - #ifdef WATCH_DMA_TRANSFER_INTERVAL - static uint32_t latest = 0; - static uint32_t max_interval = 0; - uint32_t now = _micros(); - uint32_t interval = now - latest; - if (latest != 0 && max_interval < interval) { - printf("dma_transfer interval %d\n", interval); - max_interval = interval; - } - latest = now; - #endif // WATCH_DMA_TRANSFER_INTERVAL - #ifdef WATCH_PIO_SM_TX_FIFO_LEVEL - uint tx_fifo_level = pio_sm_get_tx_fifo_level(audio_pio, shared_state.pio_sm); - if (tx_fifo_level < 4) { - printf("PIO TX FIFO too low: %d at %d ms\n", (int) tx_fifo_level, (int) _millis()); - } - #endif // WATCH_PIO_SM_TX_FIFO_LEVEL - - audio_buffer_t *ab = take_audio_buffer(audio_i2s_consumer, false); - - *playing_buffer = ab; - if (!ab) { - DEBUG_PINS_XOR(audio_timing, 1); - DEBUG_PINS_XOR(audio_timing, 2); - DEBUG_PINS_XOR(audio_timing, 1); - //DEBUG_PINS_XOR(audio_timing, 2); - // just play some silence - ab = &silence_buffer; - } - assert(ab->sample_count); - // todo better naming of format->format->format!! - assert(ab->format->format->pcm_format == AUDIO_PCM_FORMAT_S16 || ab->format->format->pcm_format == AUDIO_PCM_FORMAT_S32); - if (_i2s_output_audio_format->channel_count == AUDIO_CHANNEL_MONO) { - assert(ab->format->format->channel_count == AUDIO_CHANNEL_MONO); - //assert(ab->format->sample_stride == 2); - } else { - assert(ab->format->format->channel_count == AUDIO_CHANNEL_STEREO); - //assert(ab->format->sample_stride == 4); - } - uint transfer_size; - if (ab->format->format->pcm_format == AUDIO_PCM_FORMAT_S32 && ab->format->format->channel_count == AUDIO_CHANNEL_STEREO) { - transfer_size = ab->sample_count * 2; - //dma_channel_transfer_from_buffer_now(dma_channel, ab->buffer->bytes, ab->sample_count*2); // DMA_SIZE_32 * 2 times; - } else { - transfer_size = ab->sample_count; - //dma_channel_transfer_from_buffer_now(dma_channel, ab->buffer->bytes, ab->sample_count); - } - dma_channel_configure( - dma_channel, - dma_config, - &audio_pio->txf[shared_state.pio_sm], // dest - ab->buffer->bytes, // src - transfer_size, // count - false // trigger - ); -} - -// irq handler for DMA -void __isr __time_critical_func(audio_i2s_dma_irq_handler)() { -#if PICO_AUDIO_I2S_NOOP - assert(false); -#else - uint dma_channel0 = shared_state.dma_channel0; - uint dma_channel1 = shared_state.dma_channel1; - if (dma_irqn_get_channel_status(PICO_AUDIO_I2S_DMA_IRQ, dma_channel0)) { - dma_irqn_acknowledge_channel(PICO_AUDIO_I2S_DMA_IRQ, dma_channel0); - DEBUG_PINS_SET(audio_timing, 4); - // free the buffer we just finished - if (shared_state.playing_buffer0) { - give_audio_buffer(audio_i2s_consumer, shared_state.playing_buffer0); -#ifndef NDEBUG - shared_state.playing_buffer0 = NULL; -#endif - } - audio_start_dma_transfer(dma_channel0, &dma_config0, &shared_state.playing_buffer0); - DEBUG_PINS_CLR(audio_timing, 4); -#ifdef CORE1_PROCESS_I2S_CALLBACK - bool flg = multicore_fifo_push_timeout_us(EVENT_I2S_DMA_TRANSFER_STARTED, FIFO_TIMEOUT); - if (!flg) { printf("Core0 -> Core1 FIFO Full\n"); } -#else - i2s_callback_func(); -#endif // CORE1_PROCESS_I2S_CALLBACK - } else if (dma_irqn_get_channel_status(PICO_AUDIO_I2S_DMA_IRQ, dma_channel1)) { - dma_irqn_acknowledge_channel(PICO_AUDIO_I2S_DMA_IRQ, dma_channel1); - DEBUG_PINS_SET(audio_timing, 4); - // free the buffer we just finished - if (shared_state.playing_buffer1) { - give_audio_buffer(audio_i2s_consumer, shared_state.playing_buffer1); -#ifndef NDEBUG - shared_state.playing_buffer1 = NULL; -#endif - } - audio_start_dma_transfer(dma_channel1, &dma_config1, &shared_state.playing_buffer1); - DEBUG_PINS_CLR(audio_timing, 4); -#ifdef CORE1_PROCESS_I2S_CALLBACK - bool flg = multicore_fifo_push_timeout_us(EVENT_I2S_DMA_TRANSFER_STARTED, FIFO_TIMEOUT); - if (!flg) { printf("Core0 -> Core1 FIFO Full\n"); } -#else - i2s_callback_func(); -#endif // CORE1_PROCESS_I2S_CALLBACK - } -#endif -} - -void audio_i2s_set_enabled(bool enabled) { -#ifndef NDEBUG - if (enabled) { - printf("Enabling PIO I2S audio (on core %d)\n", get_core_num()); - } else { - printf("Disabling PIO I2S audio (on core %d)\n", get_core_num()); - } -#endif - uint dma_channel0 = shared_state.dma_channel0; - uint dma_channel1 = shared_state.dma_channel1; - - if (enabled) { - dma_channel_claim(dma_channel0); - dma_channel_claim(dma_channel1); - audio_start_dma_transfer(dma_channel0, &dma_config0, &shared_state.playing_buffer0); - audio_start_dma_transfer(dma_channel1, &dma_config1, &shared_state.playing_buffer1); - if (!irq_has_shared_handler(DMA_IRQ_x)) { - irq_add_shared_handler(DMA_IRQ_x, audio_i2s_dma_irq_handler, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY); - } - dma_irqn_set_channel_enabled(PICO_AUDIO_I2S_DMA_IRQ, dma_channel0, true); - dma_irqn_set_channel_enabled(PICO_AUDIO_I2S_DMA_IRQ, dma_channel1, true); - irq_set_enabled(DMA_IRQ_x, true); - dma_channel_start(dma_channel0); -#ifdef CORE1_PROCESS_I2S_CALLBACK - { - bool flg; - uint32_t msg; - multicore_reset_core1(); - multicore_launch_core1(i2s_callback_loop); - flg = multicore_fifo_pop_timeout_us(FIFO_TIMEOUT, &msg); - if (!flg || msg != RESPONSE_CORE1_THREAD_STARTED) { - panic("Core1 is not respond\n"); - } - pio_sm_set_enabled(audio_pio, shared_state.pio_sm, enabled); - } -#endif // CORE1_PROCESS_I2S_CALLBACK - } else { -#ifdef CORE1_PROCESS_I2S_CALLBACK - { - bool flg; - uint32_t msg; - pio_sm_set_enabled(audio_pio, shared_state.pio_sm, false); - flg = multicore_fifo_push_timeout_us(NOTIFY_I2S_DISABLED, FIFO_TIMEOUT); - if (!flg) { printf("Core0 -> Core1 FIFO Full\n"); } - flg = multicore_fifo_pop_timeout_us(FIFO_TIMEOUT, &msg); - if (!flg || msg != RESPONSE_CORE1_THREAD_TERMINATED) { - panic("Core1 is not respond\n"); - } - } -#endif // CORE1_PROCESS_I2S_CALLBACK - dma_irqn_set_channel_enabled(PICO_AUDIO_I2S_DMA_IRQ, dma_channel0, false); - dma_irqn_set_channel_enabled(PICO_AUDIO_I2S_DMA_IRQ, dma_channel1, false); - irq_set_enabled(DMA_IRQ_x, false); - dma_channel_abort(dma_channel0); - dma_channel_wait_for_finish_blocking(dma_channel0); - dma_irqn_acknowledge_channel(PICO_AUDIO_I2S_DMA_IRQ, dma_channel0); - dma_channel_cleanup(dma_channel0); - dma_channel_unclaim(dma_channel0); - dma_channel_abort(dma_channel1); - dma_channel_wait_for_finish_blocking(dma_channel1); - dma_irqn_acknowledge_channel(PICO_AUDIO_I2S_DMA_IRQ, dma_channel1); - dma_channel_cleanup(dma_channel1); - dma_channel_unclaim(dma_channel1); - if (!irq_has_shared_handler(DMA_IRQ_x)) { - irq_remove_handler(DMA_IRQ_x, audio_i2s_dma_irq_handler); - } - } -} diff --git a/lib/pico_audio_i2s_32b/audio_i2s.pio b/lib/pico_audio_i2s_32b/audio_i2s.pio deleted file mode 100644 index 59d86ad..0000000 --- a/lib/pico_audio_i2s_32b/audio_i2s.pio +++ /dev/null @@ -1,81 +0,0 @@ -; -; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. -; -; SPDX-License-Identifier: BSD-3-Clause -; - -; Modified by Elehobica, 2021 - -; Transmit a mono or stereo I2S audio stream as stereo -; This is 16 bits per sample; can be altered by modifying the "set" params, -; or made programmable by replacing "set x" with "mov x, y" and using Y as a config register. -; -; Autopull must be enabled, with threshold set to 32. -; Since I2S is MSB-first, shift direction should be to left. -; Hence the format of the FIFO word is: -; -; S16 case -; | 31 : 16 | 15 : 0 | -; | sample ws=0 | sample ws=1 | -; -; S32 case -; | 31 : 0 | 31 : 0 | -; | sample ws=0 | sample ws=1 | -; -; Data is output at 1 bit per clock. Use clock divider to adjust frequency. -; Fractional divider will probably be needed to get correct bit clock period, -; but for common syslck freqs this should still give a constant word select period. -; -; One output pin is used for the data output. -; Two side-set pins are used. Bit 0 is clock, bit 1 is word select. - -; Send 16 bit words to the PIO for mono, 32 bit words for stereo (S16) -; Send 32 bit words to the PIO for mono, 32 bit words * 2 for stereo (S32) - -.program audio_i2s -.side_set 2 - - ; /--- LRCLK - ; |/-- BCLK -bitloop1: ; || - out pins, 1 side 0b00 - jmp x-- bitloop1 side 0b01 - out pins, 1 side 0b10 - mov x, isr side 0b11 - -bitloop0: - out pins, 1 side 0b10 - jmp x-- bitloop0 side 0b11 - out pins, 1 side 0b00 -public entry_point: - mov x, isr side 0b01 - -% c-sdk { - -static inline void audio_i2s_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint res_bits) { - pio_sm_config sm_config = audio_i2s_program_get_default_config(offset); - - sm_config_set_out_pins(&sm_config, data_pin, 1); - sm_config_set_sideset_pins(&sm_config, clock_pin_base); - sm_config_set_out_shift(&sm_config, false, true, 32); - sm_config_set_fifo_join(&sm_config, PIO_FIFO_JOIN_TX); - - pio_sm_init(pio, sm, offset, &sm_config); - - uint pin_mask = (1u << data_pin) | (3u << clock_pin_base); - pio_sm_set_pindirs_with_mask(pio, sm, pin_mask, pin_mask); - pio_sm_set_pins(pio, sm, 0); // clear pins - pio_sm_clear_fifos(pio, sm); - pio_sm_drain_tx_fifo(pio, sm); - - // set resolution to ISR (use as config value) - pio_sm_set_enabled(pio, sm, false); - pio_sm_put_blocking(pio, sm, res_bits - 2); // res_bits should be 32, 16 or 8 - pio_sm_exec(pio, sm, pio_encode_pull(false, false)); - pio_sm_exec(pio, sm, pio_encode_out(pio_isr, 32)); - pio_sm_set_enabled(pio, sm, true); - - pio_sm_exec(pio, sm, pio_encode_jmp(offset + audio_i2s_offset_entry_point)); -} - -%} \ No newline at end of file diff --git a/lib/pico_audio_i2s_32b/include/pico/audio_i2s.h b/lib/pico_audio_i2s_32b/include/pico/audio_i2s.h deleted file mode 100644 index 30cd113..0000000 --- a/lib/pico_audio_i2s_32b/include/pico/audio_i2s.h +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -// Modified by Elehobica, 2021 - -#ifndef _PICO_AUDIO_I2S_H -#define _PICO_AUDIO_I2S_H - -#include "pico/audio.h" - -/** \file audio_i2s.h - * \defgroup pico_audio_i2s pico_audio_i2s - * I2S audio output using the PIO - * - * This library uses the \ref hardware_pio system to implement a I2S audio interface - * - * \todo Must be more we need to say here. - * \todo certainly need an example - * - */ - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef PICO_AUDIO_I2S_DMA_IRQ -#ifdef PICO_AUDIO_DMA_IRQ -#define PICO_AUDIO_I2S_DMA_IRQ PICO_AUDIO_DMA_IRQ -#else -#define PICO_AUDIO_I2S_DMA_IRQ 0 -#endif -#endif - -#ifndef PICO_AUDIO_I2S_PIO -#ifdef PICO_AUDIO_PIO -#define PICO_AUDIO_I2S_PIO PICO_AUDIO_PIO -#else -#define PICO_AUDIO_I2S_PIO 0 -#endif -#endif - -#if !(PICO_AUDIO_I2S_DMA_IRQ == 0 || PICO_AUDIO_I2S_DMA_IRQ == 1) -#error PICO_AUDIO_I2S_DMA_IRQ must be 0 or 1 -#endif - -#if !(PICO_AUDIO_I2S_PIO == 0 || PICO_AUDIO_I2S_PIO == 1) -#error PICO_AUDIO_I2S_PIO ust be 0 or 1 -#endif - -#ifndef PICO_AUDIO_I2S_MAX_CHANNELS -#ifdef PICO_AUDIO_MAX_CHANNELS -#define PICO_AUDIO_I2S_MAX_CHANNELS PICO_AUDIO_MAX_CHANNELS -#else -#define PICO_AUDIO_I2S_MAX_CHANNELS 2u -#endif -#endif - -#ifndef PICO_AUDIO_I2S_BUFFERS_PER_CHANNEL -#ifdef PICO_AUDIO_BUFFERS_PER_CHANNEL -#define PICO_AUDIO_I2S_BUFFERS_PER_CHANNEL PICO_AUDIO_BUFFERS_PER_CHANNEL -#else -#define PICO_AUDIO_I2S_BUFFERS_PER_CHANNEL 3u -#endif -#endif - -#ifndef PICO_AUDIO_I2S_BUFFER_SAMPLE_LENGTH -#ifdef PICO_AUDIO_BUFFER_SAMPLE_LENGTH -#define PICO_AUDIO_I2S_BUFFER_SAMPLE_LENGTH PICO_AUDIO_BUFFER_SAMPLE_LENGTH -#else -#define PICO_AUDIO_I2S_BUFFER_SAMPLE_LENGTH 576u -#endif -#endif - -#ifndef PICO_AUDIO_I2S_SILENCE_BUFFER_SAMPLE_LENGTH -#ifdef PICO_AUDIO_I2S_SILENCE_BUFFER_SAMPLE_LENGTH -#define PICO_AUDIO_I2S_SILENCE_BUFFER_SAMPLE_LENGTH PICO_AUDIO_SILENCE_BUFFER_SAMPLE_LENGTH -#else -#define PICO_AUDIO_I2S_SILENCE_BUFFER_SAMPLE_LENGTH 256u -#endif -#endif - -// Allow use of pico_audio driver without actually doing anything much -#ifndef PICO_AUDIO_I2S_NOOP -#ifdef PICO_AUDIO_NOOP -#define PICO_AUDIO_I2S_NOOP PICO_AUDIO_NOOP -#else -#define PICO_AUDIO_I2S_NOOP 0 -#endif -#endif - -#ifndef PICO_AUDIO_I2S_DATA_PIN -//#warning PICO_AUDIO_I2S_DATA_PIN should be defined when using AUDIO_I2S -#define PICO_AUDIO_I2S_DATA_PIN 18 -#endif - -#ifndef PICO_AUDIO_I2S_CLOCK_PIN_BASE -//#warning PICO_AUDIO_I2S_CLOCK_PIN_BASE should be defined when using AUDIO_I2S -#define PICO_AUDIO_I2S_CLOCK_PIN_BASE 16 -#endif - -// todo this needs to come from a build config -/** \brief Base configuration structure used when setting up - * \ingroup pico_audio_i2s - */ -typedef struct audio_i2s_config { - uint8_t data_pin; - uint8_t clock_pin_base; - uint8_t dma_channel0; - uint8_t dma_channel1; - uint8_t pio_sm; -} audio_i2s_config_t; - -/** \brief Set up system to output I2S audio - * \ingroup pico_audio_i2s - * - * \param i2s_audio_format \todo - * \param config The configuration to apply. - */ -const audio_format_t *audio_i2s_setup(const audio_format_t *i2s_input_audio_format, const audio_format_t *i2s_output_audio_format, - const audio_i2s_config_t *config); - - -/** \brief End up system to output I2S audio - * \ingroup pico_audio_i2s - * - */ -void audio_i2s_end(); -/* - * \param config The configuration to apply. -void audio_i2s_end(const audio_i2s_config_t *config); -*/ - - -/** \brief \todo - * \ingroup pico_audio_i2s - * - * \param producer - * \param connection - */ -bool audio_i2s_connect_thru(audio_buffer_pool_t *producer, audio_connection_t *connection); - - -/** \brief \todo - * \ingroup pico_audio_i2s - * - * \param producer - * - * todo make a common version (or a macro) .. we don't want to pull in unnecessary code by default - */ -bool audio_i2s_connect(audio_buffer_pool_t *producer); - - -/** \brief \todo - * \ingroup pico_audio_i2s - * - * \param producer - */ -bool audio_i2s_connect_s8(audio_buffer_pool_t *producer); -bool audio_i2s_connect_extra(audio_buffer_pool_t *producer, bool buffer_on_give, uint buffer_count, uint samples_per_buffer, audio_connection_t *connection); - -/** \brief \todo - * \ingroup pico_audio_i2s - * - * \param producer - * \param buffer_on_give - * \param buffer_count - * \param samples_per_buffer - * \param connection - * \return - */ -bool audio_i2s_connect_extra(audio_buffer_pool_t *producer, bool buffer_on_give, uint buffer_count, - uint samples_per_buffer, audio_connection_t *connection); - - -/** \brief Set up system to output I2S audio - * \ingroup pico_audio_i2s - * - * \param enable true to enable I2S audio, false to disable. - */ -void audio_i2s_set_enabled(bool enabled); - -#ifdef __cplusplus -} -#endif - -#endif //_AUDIO_I2S_H