From f64791d603206aa72617f39b7396a228a832ccb8 Mon Sep 17 00:00:00 2001 From: Paul Guyot Date: Mon, 13 May 2024 11:31:36 +0200 Subject: [PATCH] Add all esp adf decoders Signed-off-by: Paul Guyot --- .github/workflows/build.yml | 23 +++++ CMakeLists.txt | 10 +- Kconfig | 47 +++++++-- README.md | 21 ++++ nifs/atomvm_esp_adf_aac_decoder.c | 20 +++- nifs/atomvm_esp_adf_amr_decoder.c | 102 +++++++++++++++++++ nifs/atomvm_esp_adf_common.c | 62 ++++++++++++ nifs/atomvm_esp_adf_flac_decoder.c | 102 +++++++++++++++++++ nifs/atomvm_esp_adf_g711_decoder.c | 113 +++++++++++++++++++++ nifs/atomvm_esp_adf_i2s_output.c | 141 +++++++++++++++------------ nifs/atomvm_esp_adf_mp3_decoder.c | 20 ++++ nifs/atomvm_esp_adf_ogg_decoder.c | 102 +++++++++++++++++++ nifs/atomvm_esp_adf_opus_decoder.c | 102 +++++++++++++++++++ nifs/atomvm_esp_adf_pcm_decoder.c | 111 +++++++++++++++++++++ nifs/atomvm_esp_adf_rsp_filter.c | 80 +++------------ nifs/atomvm_esp_adf_wav_decoder.c | 102 +++++++++++++++++++ nifs/include/atomvm_esp_adf_common.h | 12 +++ src/esp_adf_aac_decoder.erl | 11 ++- src/esp_adf_amr_decoder.erl | 19 ++++ src/esp_adf_flac_decoder.erl | 19 ++++ src/esp_adf_g711_decoder.erl | 22 +++++ src/esp_adf_mp3_decoder.erl | 11 ++- src/esp_adf_ogg_decoder.erl | 19 ++++ src/esp_adf_opus_decoder.erl | 19 ++++ src/esp_adf_pcm_decoder.erl | 22 +++++ src/esp_adf_rsp_filter.erl | 38 ++++++++ src/esp_adf_wav_decoder.erl | 19 ++++ 27 files changed, 1231 insertions(+), 138 deletions(-) create mode 100644 nifs/atomvm_esp_adf_amr_decoder.c create mode 100644 nifs/atomvm_esp_adf_common.c create mode 100644 nifs/atomvm_esp_adf_flac_decoder.c create mode 100644 nifs/atomvm_esp_adf_g711_decoder.c create mode 100644 nifs/atomvm_esp_adf_ogg_decoder.c create mode 100644 nifs/atomvm_esp_adf_opus_decoder.c create mode 100644 nifs/atomvm_esp_adf_pcm_decoder.c create mode 100644 nifs/atomvm_esp_adf_wav_decoder.c create mode 100644 nifs/include/atomvm_esp_adf_common.h create mode 100644 src/esp_adf_amr_decoder.erl create mode 100644 src/esp_adf_flac_decoder.erl create mode 100644 src/esp_adf_g711_decoder.erl create mode 100644 src/esp_adf_ogg_decoder.erl create mode 100644 src/esp_adf_opus_decoder.erl create mode 100644 src/esp_adf_pcm_decoder.erl create mode 100644 src/esp_adf_rsp_filter.erl create mode 100644 src/esp_adf_wav_decoder.erl diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7b8a473..140da5e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,6 +16,24 @@ jobs: idf-version: - 'v5.1.3' - 'v5.2.1' + additional_decoders: [""] + + include: + - additional_decoders: "FLAC OGG" + soc: "esp32c3" + idf-version: "v5.2.1" + + - additional_decoders: "G711 OPUS" + soc: "esp32c3" + idf-version: "v5.2.1" + + - additional_decoders: "AMR" + soc: "esp32c3" + idf-version: "v5.2.1" + + - additional_decoders: "PCM WAV" + soc: "esp32c3" + idf-version: "v5.2.1" container: espressif/idf:${{ matrix.idf-version }} steps: @@ -65,6 +83,9 @@ jobs: . $IDF_PATH/export.sh idf.py set-target ${{ matrix.soc }} echo CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY=y >> sdkconfig + for codec in "${{ matrix.additional_decoders }}" ; do + echo AVM_ESP_ADF_${codec}_DECODER_ENABLE=y >> sdkconfig + done idf.py build -DCONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY=y - name: Run tests using qemu @@ -83,12 +104,14 @@ jobs: - name: "Create a ${{ matrix.soc }} image" working-directory: AtomVM/src/platforms/esp32/build + if: matrix.additional_decoders == '' run: | ./mkimage.sh mv atomvm-${{ matrix.soc }}.img atomvm-esp-adf-${{ matrix.soc }}-${{ matrix.idf-version }}.img - name: "Upload ${{ matrix.soc }} artifacts" uses: actions/upload-artifact@v4 + if: matrix.additional_decoders == '' with: name: atomvm-esp-adf-${{ matrix.soc }}-${{ matrix.idf-version }}-image path: AtomVM/src/platforms/esp32/build/atomvm-esp-adf-${{ matrix.soc }}-${{ matrix.idf-version }}.img diff --git a/CMakeLists.txt b/CMakeLists.txt index 806cce7..c70a99c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,12 +19,20 @@ # set(ATOMVM_ESP_AUDIO_COMPONENT_SRCS + "nifs/atomvm_esp_adf_aac_decoder.c" + "nifs/atomvm_esp_adf_amr_decoder.c" "nifs/atomvm_esp_adf_audio_element.c" "nifs/atomvm_esp_adf_audio_pipeline.c" - "nifs/atomvm_esp_adf_aac_decoder.c" + "nifs/atomvm_esp_adf_common.c" + "nifs/atomvm_esp_adf_flac_decoder.c" + "nifs/atomvm_esp_adf_g711_decoder.c" "nifs/atomvm_esp_adf_i2s_output.c" "nifs/atomvm_esp_adf_mp3_decoder.c" + "nifs/atomvm_esp_adf_ogg_decoder.c" + "nifs/atomvm_esp_adf_opus_decoder.c" + "nifs/atomvm_esp_adf_pcm_decoder.c" "nifs/atomvm_esp_adf_rsp_filter.c" + "nifs/atomvm_esp_adf_wav_decoder.c" ) # WHOLE_ARCHIVE option is supported only with esp-idf 5.x diff --git a/Kconfig b/Kconfig index a859a76..644ba87 100644 --- a/Kconfig +++ b/Kconfig @@ -1,9 +1,9 @@ # SPDX-License-Identifier: MIT -menu "ATOMVM_ESP_ADF Configuration" +menu "ATOMVM ESP-ADF Configuration" config AVM_ESP_ADF_AUDIO_ELEMENT_ENABLE - bool "Enable AtomVM esp adf audio element driver" + bool "Enable AtomVM esp adf audio element" default y config AVM_ESP_ADF_AUDIO_PIPELINE_ENABLE @@ -12,22 +12,57 @@ config AVM_ESP_ADF_AUDIO_PIPELINE_ENABLE depends on AVM_ESP_ADF_AUDIO_ELEMENT_ENABLE config AVM_ESP_ADF_I2S_OUTPUT_ENABLE - bool "Enable AtomVM esp adf i2s output" + bool "Enable AtomVM esp adf I2S output stream" default y depends on AVM_ESP_ADF_AUDIO_ELEMENT_ENABLE config AVM_ESP_ADF_AAC_DECODER_ENABLE - bool "Enable AtomVM esp adf aac decoder" + bool "Enable AtomVM esp adf AAC decoder" default y depends on AVM_ESP_ADF_AUDIO_ELEMENT_ENABLE +config AVM_ESP_ADF_AMR_DECODER_ENABLE + bool "Enable AtomVM esp adf AMR decoder" + default n + depends on AVM_ESP_ADF_AUDIO_ELEMENT_ENABLE + +config AVM_ESP_ADF_FLAC_DECODER_ENABLE + bool "Enable AtomVM esp adf FLAC decoder" + default n + depends on AVM_ESP_ADF_AUDIO_ELEMENT_ENABLE + +config AVM_ESP_ADF_G711_DECODER_ENABLE + bool "Enable AtomVM esp adf G711 decoder" + default n + depends on AVM_ESP_ADF_AUDIO_ELEMENT_ENABLE + config AVM_ESP_ADF_MP3_DECODER_ENABLE - bool "Enable AtomVM esp adf mp3 decoder" + bool "Enable AtomVM esp adf MP3 decoder" default y depends on AVM_ESP_ADF_AUDIO_ELEMENT_ENABLE +config AVM_ESP_ADF_OGG_DECODER_ENABLE + bool "Enable AtomVM esp adf OGG decoder" + default n + depends on AVM_ESP_ADF_AUDIO_ELEMENT_ENABLE + +config AVM_ESP_ADF_OPUS_DECODER_ENABLE + bool "Enable AtomVM esp adf OPUS decoder" + default n + depends on AVM_ESP_ADF_AUDIO_ELEMENT_ENABLE + +config AVM_ESP_ADF_PCM_DECODER_ENABLE + bool "Enable AtomVM esp adf PCM decoder" + default n + depends on AVM_ESP_ADF_AUDIO_ELEMENT_ENABLE + +config AVM_ESP_ADF_WAV_DECODER_ENABLE + bool "Enable AtomVM esp adf WAV decoder" + default n + depends on AVM_ESP_ADF_AUDIO_ELEMENT_ENABLE + config AVM_ESP_ADF_RSP_FILTER_ENABLE - bool "Enable AtomVM esp adf rsp filter" + bool "Enable AtomVM esp adf resample filter" default y depends on AVM_ESP_ADF_AUDIO_ELEMENT_ENABLE diff --git a/README.md b/README.md index 4fd5285..2ec4ff0 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,27 @@ You will also need to flash AtomVM core libraries. Alternatively, you can flash an image generated by the [CI of this project](https://github.com/pguyot/atomvm_esp_adf/actions/workflows/build.yml). +3. Custom builds + +You can finely select the ESP ADF elements you need using the configuration menu, under `Component config` -> `ATOMVM ESP-ADF Configuration`). + +Default builds include: +- audio element driver +- audio pipeline +- i2s output stream +- MP3 and AAC decoders +- resample filter + +Enabling all available decoders currently yields an image that is too large for AtomVM's default partition map. + +```bash +cd AtomVM/src/platforms/esp32/ +idf.py menuconfig +idf.py build +``` + +Do not forget FreeRTOS `configENABLE_BACKWARD_COMPATIBILITY` (under `Component config` -> `FreeRTOS` -> `Kernel`). + ## Usage This project defines `esp_adf_*` modules. diff --git a/nifs/atomvm_esp_adf_aac_decoder.c b/nifs/atomvm_esp_adf_aac_decoder.c index 5a58804..c480d78 100644 --- a/nifs/atomvm_esp_adf_aac_decoder.c +++ b/nifs/atomvm_esp_adf_aac_decoder.c @@ -28,6 +28,7 @@ #include #include "atomvm_esp_adf_audio_element.h" +#include "atomvm_esp_adf_common.h" // #define ENABLE_TRACE #include @@ -55,10 +56,27 @@ static term nif_init(Context *ctx, int argc, term argv[]) } aac_decoder_cfg_t aac_cfg = DEFAULT_AAC_DECODER_CONFIG(); + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xB", "out_rb_size"), &aac_cfg.out_rb_size, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xA", "task_stack"), &aac_cfg.task_stack, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "task_core"), &aac_cfg.task_core, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "task_prio"), &aac_cfg.task_prio, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_bool_parameter(ctx, argv, ATOM_STR("\xC", "stack_in_ext"), &aac_cfg.stack_in_ext))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_bool_parameter(ctx, argv, ATOM_STR("\xB", "plus_enable"), &aac_cfg.plus_enable))) { + return term_invalid_term(); + } audio_element_handle_t audio_element = aac_decoder_init(&aac_cfg); atomvm_esp_adf_audio_element_init_resource(rsrc_obj, audio_element, active_term == TRUE_ATOM, ctx); - return atomvm_esp_adf_audio_element_resource_to_opaque(rsrc_obj, ctx); } diff --git a/nifs/atomvm_esp_adf_amr_decoder.c b/nifs/atomvm_esp_adf_amr_decoder.c new file mode 100644 index 0000000..74292ac --- /dev/null +++ b/nifs/atomvm_esp_adf_amr_decoder.c @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: MIT */ +#include + +#ifdef CONFIG_AVM_ESP_ADF_AMR_DECODER_ENABLE + +#include + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + +#include +#include +#include + +#include + +#pragma GCC diagnostic pop + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "atomvm_esp_adf_audio_element.h" +#include "atomvm_esp_adf_common.h" + +// #define ENABLE_TRACE +#include + +#define MODULE_PREFIX "esp_adf_amr_decoder:" +#define TAG "esp_adf_amr_decoder" + +static term nif_init(Context *ctx, int argc, term argv[]) +{ + TRACE("%s\n", __func__); + UNUSED(argc); + + VALIDATE_VALUE(argv[0], term_is_list); + term active_term = interop_kv_get_value_default(argv[0], ATOM_STR("\x6", "active"), TRUE_ATOM, ctx->global); + + if (UNLIKELY(memory_ensure_free(ctx, AUDIO_ELEMENT_OPAQUE_TERM_SIZE) != MEMORY_GC_OK)) { + ESP_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__); + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + + struct AudioElementResource *rsrc_obj = enif_alloc_resource(audio_element_resource_type, sizeof(struct AudioElementResource)); + if (IS_NULL_PTR(rsrc_obj)) { + ESP_LOGW(TAG, "Failed to allocate memory: %s:%i.\n", __FILE__, __LINE__); + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + + amr_decoder_cfg_t amr_cfg = DEFAULT_AMR_DECODER_CONFIG(); + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xB", "out_rb_size"), &amr_cfg.out_rb_size, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xA", "task_stack"), &amr_cfg.task_stack, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "task_core"), &amr_cfg.task_core, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "task_prio"), &amr_cfg.task_prio, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_bool_parameter(ctx, argv, ATOM_STR("\xC", "stack_in_ext"), &amr_cfg.stack_in_ext))) { + return term_invalid_term(); + } + audio_element_handle_t audio_element = amr_decoder_init(&amr_cfg); + + atomvm_esp_adf_audio_element_init_resource(rsrc_obj, audio_element, active_term == TRUE_ATOM, ctx); + return atomvm_esp_adf_audio_element_resource_to_opaque(rsrc_obj, ctx); +} + +static const struct Nif init_nif = { + .base.type = NIFFunctionType, + .nif_ptr = nif_init +}; + +// +// Component Nif Entrypoints +// + +static const struct Nif *get_nif(const char *nifname) +{ + if (memcmp(nifname, MODULE_PREFIX, strlen(MODULE_PREFIX))) { + return NULL; + } + if (strcmp(nifname + strlen(MODULE_PREFIX), "init/1") == 0) { + return &init_nif; + } + return NULL; +} + +REGISTER_NIF_COLLECTION(esp_adf_amr_decoder, NULL, NULL, get_nif) + +#endif diff --git a/nifs/atomvm_esp_adf_common.c b/nifs/atomvm_esp_adf_common.c new file mode 100644 index 0000000..3d17229 --- /dev/null +++ b/nifs/atomvm_esp_adf_common.c @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: MIT */ +#include + +#include + +#include +#include +#include +#include +#include + +#include "atomvm_esp_adf_common.h" + +bool get_integer_parameter(Context *ctx, term argv[], AtomString key, int *value, bool required) +{ + term parameter_term = interop_kv_get_value(argv[0], key, ctx->global); + if (term_is_invalid_term(parameter_term)) { + if (required) { + RAISE_ERROR(BADARG_ATOM); + return false; + } + return true; + } + if (UNLIKELY(!term_is_integer(parameter_term))) { + RAISE_ERROR(BADARG_ATOM); + return false; + } + *value = term_to_int(parameter_term); + return true; +} + +bool get_bool_parameter(Context *ctx, term argv[], AtomString key, bool *value) +{ + term parameter_term = interop_kv_get_value(argv[0], key, ctx->global); + if (term_is_invalid_term(parameter_term)) { + return true; + } + if (UNLIKELY(!term_is_atom(parameter_term) || (parameter_term != TRUE_ATOM && parameter_term != FALSE_ATOM))) { + RAISE_ERROR(BADARG_ATOM); + return false; + } + *value = parameter_term == TRUE_ATOM; + return true; +} + +bool get_enum_parameter(Context *ctx, term argv[], AtomString key, const AtomStringIntPair *table, int *value) +{ + term parameter_term = interop_kv_get_value(argv[0], key, ctx->global); + if (term_is_invalid_term(parameter_term)) { + return true; + } + if (UNLIKELY(!term_is_atom(parameter_term))) { + RAISE_ERROR(BADARG_ATOM); + return false; + } + int enum_value = interop_atom_term_select_int(table, parameter_term, ctx->global); + if (enum_value < 0) { + return false; + } + *value = enum_value; + return true; +} diff --git a/nifs/atomvm_esp_adf_flac_decoder.c b/nifs/atomvm_esp_adf_flac_decoder.c new file mode 100644 index 0000000..e75b84c --- /dev/null +++ b/nifs/atomvm_esp_adf_flac_decoder.c @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: MIT */ +#include + +#ifdef CONFIG_AVM_ESP_ADF_FLAC_DECODER_ENABLE + +#include + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + +#include +#include +#include + +#include + +#pragma GCC diagnostic pop + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "atomvm_esp_adf_audio_element.h" +#include "atomvm_esp_adf_common.h" + +// #define ENABLE_TRACE +#include + +#define MODULE_PREFIX "esp_adf_flac_decoder:" +#define TAG "esp_adf_flac_decoder" + +static term nif_init(Context *ctx, int argc, term argv[]) +{ + TRACE("%s\n", __func__); + UNUSED(argc); + + VALIDATE_VALUE(argv[0], term_is_list); + term active_term = interop_kv_get_value_default(argv[0], ATOM_STR("\x6", "active"), TRUE_ATOM, ctx->global); + + if (UNLIKELY(memory_ensure_free(ctx, AUDIO_ELEMENT_OPAQUE_TERM_SIZE) != MEMORY_GC_OK)) { + ESP_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__); + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + + struct AudioElementResource *rsrc_obj = enif_alloc_resource(audio_element_resource_type, sizeof(struct AudioElementResource)); + if (IS_NULL_PTR(rsrc_obj)) { + ESP_LOGW(TAG, "Failed to allocate memory: %s:%i.\n", __FILE__, __LINE__); + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + + flac_decoder_cfg_t flac_cfg = DEFAULT_FLAC_DECODER_CONFIG(); + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xB", "out_rb_size"), &flac_cfg.out_rb_size, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xA", "task_stack"), &flac_cfg.task_stack, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "task_core"), &flac_cfg.task_core, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "task_prio"), &flac_cfg.task_prio, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_bool_parameter(ctx, argv, ATOM_STR("\xC", "stack_in_ext"), &flac_cfg.stack_in_ext))) { + return term_invalid_term(); + } + audio_element_handle_t audio_element = flac_decoder_init(&flac_cfg); + + atomvm_esp_adf_audio_element_init_resource(rsrc_obj, audio_element, active_term == TRUE_ATOM, ctx); + return atomvm_esp_adf_audio_element_resource_to_opaque(rsrc_obj, ctx); +} + +static const struct Nif init_nif = { + .base.type = NIFFunctionType, + .nif_ptr = nif_init +}; + +// +// Component Nif Entrypoints +// + +static const struct Nif *get_nif(const char *nifname) +{ + if (memcmp(nifname, MODULE_PREFIX, strlen(MODULE_PREFIX))) { + return NULL; + } + if (strcmp(nifname + strlen(MODULE_PREFIX), "init/1") == 0) { + return &init_nif; + } + return NULL; +} + +REGISTER_NIF_COLLECTION(esp_adf_flac_decoder, NULL, NULL, get_nif) + +#endif diff --git a/nifs/atomvm_esp_adf_g711_decoder.c b/nifs/atomvm_esp_adf_g711_decoder.c new file mode 100644 index 0000000..03480ab --- /dev/null +++ b/nifs/atomvm_esp_adf_g711_decoder.c @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: MIT */ +#include + +#ifdef CONFIG_AVM_ESP_ADF_G711_DECODER_ENABLE + +#include + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + +#include +#include +#include + +#include + +#pragma GCC diagnostic pop + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "atomvm_esp_adf_audio_element.h" +#include "atomvm_esp_adf_common.h" + +// #define ENABLE_TRACE +#include + +#define MODULE_PREFIX "esp_adf_g711_decoder:" +#define TAG "esp_adf_g711_decoder" + +static const AtomStringIntPair g711_dec_modes[] = { + { ATOM_STR("\x4", "alaw"), false }, + { ATOM_STR("\x4", "ulaw"), true }, + SELECT_INT_DEFAULT(-1) +}; + +static term nif_init(Context *ctx, int argc, term argv[]) +{ + TRACE("%s\n", __func__); + UNUSED(argc); + + VALIDATE_VALUE(argv[0], term_is_list); + term active_term = interop_kv_get_value_default(argv[0], ATOM_STR("\x6", "active"), TRUE_ATOM, ctx->global); + + if (UNLIKELY(memory_ensure_free(ctx, AUDIO_ELEMENT_OPAQUE_TERM_SIZE) != MEMORY_GC_OK)) { + ESP_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__); + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + + struct AudioElementResource *rsrc_obj = enif_alloc_resource(audio_element_resource_type, sizeof(struct AudioElementResource)); + if (IS_NULL_PTR(rsrc_obj)) { + ESP_LOGW(TAG, "Failed to allocate memory: %s:%i.\n", __FILE__, __LINE__); + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + + g711_decoder_cfg_t g711_cfg = DEFAULT_G711_DECODER_CONFIG(); + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xB", "out_rb_size"), &g711_cfg.out_rb_size, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xA", "task_stack"), &g711_cfg.task_stack, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "task_core"), &g711_cfg.task_core, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "task_prio"), &g711_cfg.task_prio, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_bool_parameter(ctx, argv, ATOM_STR("\xC", "stack_in_ext"), &g711_cfg.stack_in_ext))) { + return term_invalid_term(); + } + int dec_mode = g711_cfg.dec_mode; + if (UNLIKELY(!get_enum_parameter(ctx, argv, ATOM_STR("\x8", "dec_mode"), g711_dec_modes, &dec_mode))) { + return term_invalid_term(); + } + g711_cfg.dec_mode = (bool) dec_mode; + audio_element_handle_t audio_element = g711_decoder_init(&g711_cfg); + + atomvm_esp_adf_audio_element_init_resource(rsrc_obj, audio_element, active_term == TRUE_ATOM, ctx); + return atomvm_esp_adf_audio_element_resource_to_opaque(rsrc_obj, ctx); +} + +static const struct Nif init_nif = { + .base.type = NIFFunctionType, + .nif_ptr = nif_init +}; + +// +// Component Nif Entrypoints +// + +static const struct Nif *get_nif(const char *nifname) +{ + if (memcmp(nifname, MODULE_PREFIX, strlen(MODULE_PREFIX))) { + return NULL; + } + if (strcmp(nifname + strlen(MODULE_PREFIX), "init/1") == 0) { + return &init_nif; + } + return NULL; +} + +REGISTER_NIF_COLLECTION(esp_adf_g711_decoder, NULL, NULL, get_nif) + +#endif diff --git a/nifs/atomvm_esp_adf_i2s_output.c b/nifs/atomvm_esp_adf_i2s_output.c index 71834e6..0bb102e 100644 --- a/nifs/atomvm_esp_adf_i2s_output.c +++ b/nifs/atomvm_esp_adf_i2s_output.c @@ -30,6 +30,7 @@ #include #include "atomvm_esp_adf_audio_element.h" +#include "atomvm_esp_adf_common.h" // #define ENABLE_TRACE #include @@ -37,6 +38,30 @@ #define MODULE_PREFIX "esp_adf_i2s_output:" #define TAG "esp_adf_i2s_output" +typedef struct +{ + bool active; + int gpio_mclk; + int gpio_bclk; + int gpio_lrclk; + int gpio_dout; + int rate; + int bits; + int channels; +} i2s_output_cfg_t; + +#define DEFAULT_I2S_OUTPUT_CONFIG() \ + { \ + .active = true, \ + .gpio_mclk = I2S_GPIO_UNUSED, \ + .gpio_bclk = 0, \ + .gpio_lrclk = 0, \ + .gpio_dout = 0, \ + .rate = 48000, \ + .bits = 16, \ + .channels = 2 \ + } + struct I2SData { i2s_chan_handle_t tx_handle; @@ -117,7 +142,7 @@ static int i2s_write(audio_element_handle_t self, char *buffer, int len, TickTyp return bytes_written; } -static void i2s_data_init(struct I2SData *driver_data, const struct MusicInfo *music_info, int gpio_mclk, int gpio_bclk, int gpio_lrclk, int gpio_dout) +static void i2s_data_init(struct I2SData *driver_data, const i2s_output_cfg_t *cfg) { TRACE("%s\n", __func__); @@ -125,13 +150,13 @@ static void i2s_data_init(struct I2SData *driver_data, const struct MusicInfo *m i2s_new_channel(&driver_data->chan_cfg, &driver_data->tx_handle, NULL); driver_data->std_config = (i2s_std_config_t){ - .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(music_info->rate), - .slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(music_info->bits, music_info->channels), + .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(cfg->rate), + .slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(cfg->bits, cfg->channels), .gpio_cfg = { - .mclk = gpio_mclk, - .bclk = gpio_bclk, - .ws = gpio_lrclk, - .dout = gpio_dout, + .mclk = cfg->gpio_mclk, + .bclk = cfg->gpio_bclk, + .ws = cfg->gpio_lrclk, + .dout = cfg->gpio_dout, .din = I2S_GPIO_UNUSED, .invert_flags = { .mclk_inv = false, @@ -143,7 +168,7 @@ static void i2s_data_init(struct I2SData *driver_data, const struct MusicInfo *m i2s_channel_init_std_mode(driver_data->tx_handle, &driver_data->std_config); } -static term i2s_output_new(Context *ctx, term argv[], bool active, const struct MusicInfo *music_info, int gpio_mclk, int gpio_bclk, int gpio_lrclk, int gpio_dout) +static term i2s_output_new(Context *ctx, term argv[], const i2s_output_cfg_t *cfg) { TRACE("%s\n", __func__); @@ -158,13 +183,13 @@ static term i2s_output_new(Context *ctx, term argv[], bool active, const struct RAISE_ERROR(OUT_OF_MEMORY_ATOM); } - audio_element_cfg_t cfg = DEFAULT_AUDIO_ELEMENT_CONFIG(); - cfg.open = i2s_open; - cfg.close = i2s_close; - cfg.destroy = i2s_destroy; - cfg.process = i2s_process; - cfg.write = i2s_write; - cfg.tag = "i2s"; + audio_element_cfg_t ae_cfg = DEFAULT_AUDIO_ELEMENT_CONFIG(); + ae_cfg.open = i2s_open; + ae_cfg.close = i2s_close; + ae_cfg.destroy = i2s_destroy; + ae_cfg.process = i2s_process; + ae_cfg.write = i2s_write; + ae_cfg.tag = "i2s"; struct I2SData *driver_data = malloc(sizeof(struct I2SData)); if (IS_NULL_PTR(driver_data)) { @@ -172,9 +197,9 @@ static term i2s_output_new(Context *ctx, term argv[], bool active, const struct RAISE_ERROR(OUT_OF_MEMORY_ATOM); } - i2s_data_init(driver_data, music_info, gpio_mclk, gpio_bclk, gpio_lrclk, gpio_dout); + i2s_data_init(driver_data, cfg); - audio_element_handle_t audio_element = audio_element_init(&cfg); + audio_element_handle_t audio_element = audio_element_init(&ae_cfg); if (IS_NULL_PTR(audio_element)) { ESP_LOGW(TAG, "Failed to allocate memory: %s:%i.\n", __FILE__, __LINE__); free(driver_data); @@ -182,9 +207,9 @@ static term i2s_output_new(Context *ctx, term argv[], bool active, const struct } audio_element_setdata(audio_element, driver_data); - audio_element_set_music_info(audio_element, music_info->rate, music_info->channels, music_info->bits); + audio_element_set_music_info(audio_element, cfg->rate, cfg->channels, cfg->bits); - atomvm_esp_adf_audio_element_init_resource(rsrc_obj, audio_element, active, ctx); + atomvm_esp_adf_audio_element_init_resource(rsrc_obj, audio_element, cfg->active, ctx); return atomvm_esp_adf_audio_element_resource_to_opaque(rsrc_obj, ctx); } @@ -193,56 +218,53 @@ static term nif_init(Context *ctx, int argc, term argv[]) TRACE("%s\n", __func__); UNUSED(argc); - term cfg = argv[0]; - - VALIDATE_VALUE(cfg, term_is_list); - - term gpio_mclk_term = interop_kv_get_value_default(cfg, ATOM_STR("\x9", "gpio_mclk"), term_from_int(I2S_GPIO_UNUSED), ctx->global); - term gpio_bclk_term = interop_kv_get_value(cfg, ATOM_STR("\x9", "gpio_bclk"), ctx->global); - term gpio_lrclk_term = interop_kv_get_value(cfg, ATOM_STR("\xA", "gpio_lrclk"), ctx->global); - term gpio_dout_term = interop_kv_get_value(cfg, ATOM_STR("\x9", "gpio_dout"), ctx->global); + VALIDATE_VALUE(argv[0], term_is_list); - VALIDATE_VALUE(gpio_mclk_term, term_is_integer); - VALIDATE_VALUE(gpio_bclk_term, term_is_integer); - VALIDATE_VALUE(gpio_lrclk_term, term_is_integer); - VALIDATE_VALUE(gpio_dout_term, term_is_integer); - - avm_int_t gpio_mclk = term_to_int(gpio_mclk_term); - avm_int_t gpio_bclk = term_to_int(gpio_bclk_term); - avm_int_t gpio_lrclk = term_to_int(gpio_lrclk_term); - avm_int_t gpio_dout = term_to_int(gpio_dout_term); + i2s_output_cfg_t cfg = DEFAULT_I2S_OUTPUT_CONFIG(); + if (UNLIKELY(!get_bool_parameter(ctx, argv, ATOM_STR("\x6", "active"), &cfg.active))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "gpio_mclk"), &cfg.gpio_mclk, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "gpio_bclk"), &cfg.gpio_bclk, true))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xA", "gpio_lrclk"), &cfg.gpio_lrclk, true))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "gpio_dout"), &cfg.gpio_dout, true))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x4", "rate"), &cfg.rate, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x4", "bits"), &cfg.bits, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x8", "channels"), &cfg.channels, false))) { + return term_invalid_term(); + } - if (UNLIKELY(gpio_mclk != I2S_GPIO_UNUSED && !GPIO_IS_VALID_GPIO(gpio_mclk))) { + if (UNLIKELY(cfg.gpio_mclk != I2S_GPIO_UNUSED && !GPIO_IS_VALID_GPIO(cfg.gpio_mclk))) { RAISE_ERROR(BADARG_ATOM); } - if (UNLIKELY(!GPIO_IS_VALID_GPIO(gpio_bclk))) { + if (UNLIKELY(!GPIO_IS_VALID_GPIO(cfg.gpio_bclk))) { RAISE_ERROR(BADARG_ATOM); } - if (UNLIKELY(!GPIO_IS_VALID_GPIO(gpio_lrclk))) { + if (UNLIKELY(!GPIO_IS_VALID_GPIO(cfg.gpio_lrclk))) { RAISE_ERROR(BADARG_ATOM); } - if (UNLIKELY(!GPIO_IS_VALID_GPIO(gpio_dout))) { + if (UNLIKELY(!GPIO_IS_VALID_GPIO(cfg.gpio_dout))) { RAISE_ERROR(BADARG_ATOM); } - term rate_term = interop_kv_get_value_default(cfg, ATOM_STR("\x4", "rate"), term_from_int(48000), ctx->global); - term bits_term = interop_kv_get_value_default(cfg, ATOM_STR("\x4", "bits"), term_from_int(32), ctx->global); - term channels_term = interop_kv_get_value_default(cfg, ATOM_STR("\x8", "channels"), term_from_int(2), ctx->global); - - VALIDATE_VALUE(rate_term, term_is_integer); - VALIDATE_VALUE(bits_term, term_is_integer); - VALIDATE_VALUE(channels_term, term_is_integer); - - avm_int_t rate = term_to_int(rate_term); - avm_int_t bits = term_to_int(bits_term); - avm_int_t channels = term_to_int(channels_term); - // In standard mode, there are always 2 channels - if (UNLIKELY(channels != 2)) { + if (UNLIKELY(cfg.channels != 2)) { RAISE_ERROR(BADARG_ATOM); } // In standard mode, bits can be 8/16/24 or 32 - if (UNLIKELY(bits != 8 && bits != 16 && bits != 24 && bits != 32)) { + if (UNLIKELY(cfg.bits != 8 && cfg.bits != 16 && cfg.bits != 24 && cfg.bits != 32)) { RAISE_ERROR(BADARG_ATOM); } @@ -254,16 +276,7 @@ static term nif_init(Context *ctx, int argc, term argv[]) _Static_assert(I2S_DATA_BIT_WIDTH_32BIT == 32, "expected I2S_DATA_BIT_WIDTH_32BIT to be equal to 32"); #endif - struct MusicInfo music_info = { - .rate = rate, - .bits = bits, - .channels = channels - }; - - term active_term = interop_kv_get_value_default(cfg, ATOM_STR("\x6", "active"), TRUE_ATOM, ctx->global); - VALIDATE_VALUE(active_term, term_is_atom); - - return i2s_output_new(ctx, argv, active_term == TRUE_ATOM, &music_info, gpio_mclk, gpio_bclk, gpio_lrclk, gpio_dout); + return i2s_output_new(ctx, argv, &cfg); } static const struct Nif init_nif = { diff --git a/nifs/atomvm_esp_adf_mp3_decoder.c b/nifs/atomvm_esp_adf_mp3_decoder.c index 61850e3..904fda8 100644 --- a/nifs/atomvm_esp_adf_mp3_decoder.c +++ b/nifs/atomvm_esp_adf_mp3_decoder.c @@ -11,6 +11,7 @@ #include #include #include + #include #pragma GCC diagnostic pop @@ -27,6 +28,7 @@ #include #include "atomvm_esp_adf_audio_element.h" +#include "atomvm_esp_adf_common.h" // #define ENABLE_TRACE #include @@ -54,6 +56,24 @@ static term nif_init(Context *ctx, int argc, term argv[]) } mp3_decoder_cfg_t mp3_cfg = DEFAULT_MP3_DECODER_CONFIG(); + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xB", "out_rb_size"), &mp3_cfg.out_rb_size, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xA", "task_stack"), &mp3_cfg.task_stack, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "task_core"), &mp3_cfg.task_core, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "task_prio"), &mp3_cfg.task_prio, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_bool_parameter(ctx, argv, ATOM_STR("\xC", "stack_in_ext"), &mp3_cfg.stack_in_ext))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_bool_parameter(ctx, argv, ATOM_STR("\x10", "id3_parse_enable"), &mp3_cfg.id3_parse_enable))) { + return term_invalid_term(); + } audio_element_handle_t audio_element = mp3_decoder_init(&mp3_cfg); atomvm_esp_adf_audio_element_init_resource(rsrc_obj, audio_element, active_term == TRUE_ATOM, ctx); diff --git a/nifs/atomvm_esp_adf_ogg_decoder.c b/nifs/atomvm_esp_adf_ogg_decoder.c new file mode 100644 index 0000000..53d9eae --- /dev/null +++ b/nifs/atomvm_esp_adf_ogg_decoder.c @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: MIT */ +#include + +#ifdef CONFIG_AVM_ESP_ADF_OGG_DECODER_ENABLE + +#include + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + +#include +#include +#include + +#include + +#pragma GCC diagnostic pop + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "atomvm_esp_adf_audio_element.h" +#include "atomvm_esp_adf_common.h" + +// #define ENABLE_TRACE +#include + +#define MODULE_PREFIX "esp_adf_ogg_decoder:" +#define TAG "esp_adf_ogg_decoder" + +static term nif_init(Context *ctx, int argc, term argv[]) +{ + TRACE("%s\n", __func__); + UNUSED(argc); + + VALIDATE_VALUE(argv[0], term_is_list); + term active_term = interop_kv_get_value_default(argv[0], ATOM_STR("\x6", "active"), TRUE_ATOM, ctx->global); + + if (UNLIKELY(memory_ensure_free(ctx, AUDIO_ELEMENT_OPAQUE_TERM_SIZE) != MEMORY_GC_OK)) { + ESP_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__); + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + + struct AudioElementResource *rsrc_obj = enif_alloc_resource(audio_element_resource_type, sizeof(struct AudioElementResource)); + if (IS_NULL_PTR(rsrc_obj)) { + ESP_LOGW(TAG, "Failed to allocate memory: %s:%i.\n", __FILE__, __LINE__); + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + + ogg_decoder_cfg_t ogg_cfg = DEFAULT_OGG_DECODER_CONFIG(); + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xB", "out_rb_size"), &ogg_cfg.out_rb_size, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xA", "task_stack"), &ogg_cfg.task_stack, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "task_core"), &ogg_cfg.task_core, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "task_prio"), &ogg_cfg.task_prio, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_bool_parameter(ctx, argv, ATOM_STR("\xC", "stack_in_ext"), &ogg_cfg.stack_in_ext))) { + return term_invalid_term(); + } + audio_element_handle_t audio_element = ogg_decoder_init(&ogg_cfg); + + atomvm_esp_adf_audio_element_init_resource(rsrc_obj, audio_element, active_term == TRUE_ATOM, ctx); + return atomvm_esp_adf_audio_element_resource_to_opaque(rsrc_obj, ctx); +} + +static const struct Nif init_nif = { + .base.type = NIFFunctionType, + .nif_ptr = nif_init +}; + +// +// Component Nif Entrypoints +// + +static const struct Nif *get_nif(const char *nifname) +{ + if (memcmp(nifname, MODULE_PREFIX, strlen(MODULE_PREFIX))) { + return NULL; + } + if (strcmp(nifname + strlen(MODULE_PREFIX), "init/1") == 0) { + return &init_nif; + } + return NULL; +} + +REGISTER_NIF_COLLECTION(esp_adf_ogg_decoder, NULL, NULL, get_nif) + +#endif diff --git a/nifs/atomvm_esp_adf_opus_decoder.c b/nifs/atomvm_esp_adf_opus_decoder.c new file mode 100644 index 0000000..df90240 --- /dev/null +++ b/nifs/atomvm_esp_adf_opus_decoder.c @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: MIT */ +#include + +#ifdef CONFIG_AVM_ESP_ADF_OPUS_DECODER_ENABLE + +#include + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + +#include +#include +#include + +#include + +#pragma GCC diagnostic pop + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "atomvm_esp_adf_audio_element.h" +#include "atomvm_esp_adf_common.h" + +// #define ENABLE_TRACE +#include + +#define MODULE_PREFIX "esp_adf_opus_decoder:" +#define TAG "esp_adf_opus_decoder" + +static term nif_init(Context *ctx, int argc, term argv[]) +{ + TRACE("%s\n", __func__); + UNUSED(argc); + + VALIDATE_VALUE(argv[0], term_is_list); + term active_term = interop_kv_get_value_default(argv[0], ATOM_STR("\x6", "active"), TRUE_ATOM, ctx->global); + + if (UNLIKELY(memory_ensure_free(ctx, AUDIO_ELEMENT_OPAQUE_TERM_SIZE) != MEMORY_GC_OK)) { + ESP_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__); + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + + struct AudioElementResource *rsrc_obj = enif_alloc_resource(audio_element_resource_type, sizeof(struct AudioElementResource)); + if (IS_NULL_PTR(rsrc_obj)) { + ESP_LOGW(TAG, "Failed to allocate memory: %s:%i.\n", __FILE__, __LINE__); + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + + opus_decoder_cfg_t opus_cfg = DEFAULT_OPUS_DECODER_CONFIG(); + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xB", "out_rb_size"), &opus_cfg.out_rb_size, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xA", "task_stack"), &opus_cfg.task_stack, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "task_core"), &opus_cfg.task_core, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "task_prio"), &opus_cfg.task_prio, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_bool_parameter(ctx, argv, ATOM_STR("\xC", "stack_in_ext"), &opus_cfg.stack_in_ext))) { + return term_invalid_term(); + } + audio_element_handle_t audio_element = decoder_opus_init(&opus_cfg); + + atomvm_esp_adf_audio_element_init_resource(rsrc_obj, audio_element, active_term == TRUE_ATOM, ctx); + return atomvm_esp_adf_audio_element_resource_to_opaque(rsrc_obj, ctx); +} + +static const struct Nif init_nif = { + .base.type = NIFFunctionType, + .nif_ptr = nif_init +}; + +// +// Component Nif Entrypoints +// + +static const struct Nif *get_nif(const char *nifname) +{ + if (memcmp(nifname, MODULE_PREFIX, strlen(MODULE_PREFIX))) { + return NULL; + } + if (strcmp(nifname + strlen(MODULE_PREFIX), "init/1") == 0) { + return &init_nif; + } + return NULL; +} + +REGISTER_NIF_COLLECTION(esp_adf_opus_decoder, NULL, NULL, get_nif) + +#endif diff --git a/nifs/atomvm_esp_adf_pcm_decoder.c b/nifs/atomvm_esp_adf_pcm_decoder.c new file mode 100644 index 0000000..7007679 --- /dev/null +++ b/nifs/atomvm_esp_adf_pcm_decoder.c @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: MIT */ +#include + +#ifdef CONFIG_AVM_ESP_ADF_PCM_DECODER_ENABLE + +#include + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + +#include +#include +#include + +#include + +#pragma GCC diagnostic pop + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "atomvm_esp_adf_audio_element.h" +#include "atomvm_esp_adf_common.h" + +// #define ENABLE_TRACE +#include + +#define MODULE_PREFIX "esp_adf_pcm_decoder:" +#define TAG "esp_adf_pcm_decoder" + +static term nif_init(Context *ctx, int argc, term argv[]) +{ + TRACE("%s\n", __func__); + UNUSED(argc); + + VALIDATE_VALUE(argv[0], term_is_list); + term active_term = interop_kv_get_value_default(argv[0], ATOM_STR("\x6", "active"), TRUE_ATOM, ctx->global); + + if (UNLIKELY(memory_ensure_free(ctx, AUDIO_ELEMENT_OPAQUE_TERM_SIZE) != MEMORY_GC_OK)) { + ESP_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__); + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + + struct AudioElementResource *rsrc_obj = enif_alloc_resource(audio_element_resource_type, sizeof(struct AudioElementResource)); + if (IS_NULL_PTR(rsrc_obj)) { + ESP_LOGW(TAG, "Failed to allocate memory: %s:%i.\n", __FILE__, __LINE__); + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + + pcm_decoder_cfg_t pcm_cfg = DEFAULT_PCM_DECODER_CONFIG(); + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xB", "out_rb_size"), &pcm_cfg.out_rb_size, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xA", "task_stack"), &pcm_cfg.task_stack, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "task_core"), &pcm_cfg.task_core, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "task_prio"), &pcm_cfg.task_prio, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_bool_parameter(ctx, argv, ATOM_STR("\xC", "stack_in_ext"), &pcm_cfg.stack_in_ext))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x4", "rate"), &pcm_cfg.rate, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x4", "bits"), &pcm_cfg.bits, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x8", "channels"), &pcm_cfg.channels, false))) { + return term_invalid_term(); + } + audio_element_handle_t audio_element = pcm_decoder_init(&pcm_cfg); + + atomvm_esp_adf_audio_element_init_resource(rsrc_obj, audio_element, active_term == TRUE_ATOM, ctx); + return atomvm_esp_adf_audio_element_resource_to_opaque(rsrc_obj, ctx); +} + +static const struct Nif init_nif = { + .base.type = NIFFunctionType, + .nif_ptr = nif_init +}; + +// +// Component Nif Entrypoints +// + +static const struct Nif *get_nif(const char *nifname) +{ + if (memcmp(nifname, MODULE_PREFIX, strlen(MODULE_PREFIX))) { + return NULL; + } + if (strcmp(nifname + strlen(MODULE_PREFIX), "init/1") == 0) { + return &init_nif; + } + return NULL; +} + +REGISTER_NIF_COLLECTION(esp_adf_pcm_decoder, NULL, NULL, get_nif) + +#endif diff --git a/nifs/atomvm_esp_adf_rsp_filter.c b/nifs/atomvm_esp_adf_rsp_filter.c index 5ea5390..9370b98 100644 --- a/nifs/atomvm_esp_adf_rsp_filter.c +++ b/nifs/atomvm_esp_adf_rsp_filter.c @@ -29,6 +29,7 @@ #include #include "atomvm_esp_adf_audio_element.h" +#include "atomvm_esp_adf_common.h" // #define ENABLE_TRACE #include @@ -58,113 +59,64 @@ static const AtomStringIntPair esp_rsp_prefer_types[] = { SELECT_INT_DEFAULT(-1) }; -static bool get_integer_parameter(Context *ctx, term argv[], AtomString key, int *value) -{ - term parameter_term = interop_kv_get_value(argv[0], key, ctx->global); - if (term_is_invalid_term(parameter_term)) { - return true; - } - if (UNLIKELY(!term_is_integer(parameter_term))) { - RAISE_ERROR(BADARG_ATOM); - return false; - } - *value = term_to_int(parameter_term); - return true; -} - -static bool get_bool_parameter(Context *ctx, term argv[], AtomString key, bool *value) -{ - term parameter_term = interop_kv_get_value(argv[0], key, ctx->global); - if (term_is_invalid_term(parameter_term)) { - return true; - } - if (UNLIKELY(!term_is_atom(parameter_term) || (parameter_term != TRUE_ATOM && parameter_term != FALSE_ATOM))) { - RAISE_ERROR(BADARG_ATOM); - return false; - } - *value = parameter_term == TRUE_ATOM; - return true; -} - -static bool get_enum_parameter(Context *ctx, term argv[], AtomString key, const AtomStringIntPair *table, int *value) -{ - term parameter_term = interop_kv_get_value(argv[0], key, ctx->global); - if (term_is_invalid_term(parameter_term)) { - return true; - } - if (UNLIKELY(!term_is_atom(parameter_term))) { - RAISE_ERROR(BADARG_ATOM); - return false; - } - int enum_value = interop_atom_term_select_int(table, parameter_term, ctx->global); - if (enum_value < 0) { - return false; - } - *value = enum_value; - return true; -} - static term nif_init(Context *ctx, int argc, term argv[]) { TRACE("%s\n", __func__); UNUSED(argc); - term cfg = argv[0]; - - VALIDATE_VALUE(cfg, term_is_list); + VALIDATE_VALUE(argv[0], term_is_list); term active_term = interop_kv_get_value_default(argv[0], ATOM_STR("\x6", "active"), TRUE_ATOM, ctx->global); rsp_filter_cfg_t filter_cfg = DEFAULT_RESAMPLE_FILTER_CONFIG(); - - if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x8", "src_bits"), &filter_cfg.src_bits))) { + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x8", "src_bits"), &filter_cfg.src_bits, false))) { return term_invalid_term(); } - if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x6", "src_ch"), &filter_cfg.src_ch))) { + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x6", "src_ch"), &filter_cfg.src_ch, false))) { return term_invalid_term(); } - if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x8", "src_rate"), &filter_cfg.src_rate))) { + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x8", "src_rate"), &filter_cfg.src_rate, false))) { return term_invalid_term(); } - if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "dest_bits"), &filter_cfg.dest_bits))) { + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "dest_bits"), &filter_cfg.dest_bits, false))) { return term_invalid_term(); } - if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x7", "dest_ch"), &filter_cfg.dest_ch))) { + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x7", "dest_ch"), &filter_cfg.dest_ch, false))) { return term_invalid_term(); } - if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "dest_rate"), &filter_cfg.dest_rate))) { + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "dest_rate"), &filter_cfg.dest_rate, false))) { return term_invalid_term(); } if (UNLIKELY(!get_enum_parameter(ctx, argv, ATOM_STR("\x4", "mode"), esp_resample_modes, (int *) &filter_cfg.mode))) { return term_invalid_term(); } - if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x10", "max_indata_bytes"), &filter_cfg.max_indata_bytes))) { + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x10", "max_indata_bytes"), &filter_cfg.max_indata_bytes, false))) { return term_invalid_term(); } - if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xD", "out_len_bytes"), &filter_cfg.out_len_bytes))) { + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xD", "out_len_bytes"), &filter_cfg.out_len_bytes, false))) { return term_invalid_term(); } if (UNLIKELY(!get_enum_parameter(ctx, argv, ATOM_STR("\x4", "type"), esp_resample_types, &filter_cfg.type))) { return term_invalid_term(); } - if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xA", "complexity"), &filter_cfg.complexity))) { + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xA", "complexity"), &filter_cfg.complexity, false))) { return term_invalid_term(); } - if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xB", "down_ch_idx"), &filter_cfg.down_ch_idx))) { + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xB", "down_ch_idx"), &filter_cfg.down_ch_idx, false))) { return term_invalid_term(); } if (UNLIKELY(!get_enum_parameter(ctx, argv, ATOM_STR("\xB", "prefer_flag"), esp_rsp_prefer_types, (int *) &filter_cfg.prefer_flag))) { return term_invalid_term(); } - if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xB", "out_rb_size"), &filter_cfg.out_rb_size))) { + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xB", "out_rb_size"), &filter_cfg.out_rb_size, false))) { return term_invalid_term(); } - if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xA", "task_stack"), &filter_cfg.task_stack))) { + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xA", "task_stack"), &filter_cfg.task_stack, false))) { return term_invalid_term(); } - if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "task_core"), &filter_cfg.task_core))) { + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "task_core"), &filter_cfg.task_core, false))) { return term_invalid_term(); } - if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "task_prio"), &filter_cfg.task_prio))) { + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "task_prio"), &filter_cfg.task_prio, false))) { return term_invalid_term(); } if (UNLIKELY(!get_bool_parameter(ctx, argv, ATOM_STR("\xC", "stack_in_ext"), &filter_cfg.stack_in_ext))) { diff --git a/nifs/atomvm_esp_adf_wav_decoder.c b/nifs/atomvm_esp_adf_wav_decoder.c new file mode 100644 index 0000000..a0bd992 --- /dev/null +++ b/nifs/atomvm_esp_adf_wav_decoder.c @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: MIT */ +#include + +#ifdef CONFIG_AVM_ESP_ADF_WAV_DECODER_ENABLE + +#include + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + +#include +#include +#include + +#include + +#pragma GCC diagnostic pop + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "atomvm_esp_adf_audio_element.h" +#include "atomvm_esp_adf_common.h" + +// #define ENABLE_TRACE +#include + +#define MODULE_PREFIX "esp_adf_wav_decoder:" +#define TAG "esp_adf_wav_decoder" + +static term nif_init(Context *ctx, int argc, term argv[]) +{ + TRACE("%s\n", __func__); + UNUSED(argc); + + VALIDATE_VALUE(argv[0], term_is_list); + term active_term = interop_kv_get_value_default(argv[0], ATOM_STR("\x6", "active"), TRUE_ATOM, ctx->global); + + if (UNLIKELY(memory_ensure_free(ctx, AUDIO_ELEMENT_OPAQUE_TERM_SIZE) != MEMORY_GC_OK)) { + ESP_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__); + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + + struct AudioElementResource *rsrc_obj = enif_alloc_resource(audio_element_resource_type, sizeof(struct AudioElementResource)); + if (IS_NULL_PTR(rsrc_obj)) { + ESP_LOGW(TAG, "Failed to allocate memory: %s:%i.\n", __FILE__, __LINE__); + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + + wav_decoder_cfg_t wav_cfg = DEFAULT_WAV_DECODER_CONFIG(); + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xB", "out_rb_size"), &wav_cfg.out_rb_size, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\xA", "task_stack"), &wav_cfg.task_stack, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "task_core"), &wav_cfg.task_core, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_integer_parameter(ctx, argv, ATOM_STR("\x9", "task_prio"), &wav_cfg.task_prio, false))) { + return term_invalid_term(); + } + if (UNLIKELY(!get_bool_parameter(ctx, argv, ATOM_STR("\xC", "stack_in_ext"), &wav_cfg.stack_in_ext))) { + return term_invalid_term(); + } + audio_element_handle_t audio_element = wav_decoder_init(&wav_cfg); + + atomvm_esp_adf_audio_element_init_resource(rsrc_obj, audio_element, active_term == TRUE_ATOM, ctx); + return atomvm_esp_adf_audio_element_resource_to_opaque(rsrc_obj, ctx); +} + +static const struct Nif init_nif = { + .base.type = NIFFunctionType, + .nif_ptr = nif_init +}; + +// +// Component Nif Entrypoints +// + +static const struct Nif *get_nif(const char *nifname) +{ + if (memcmp(nifname, MODULE_PREFIX, strlen(MODULE_PREFIX))) { + return NULL; + } + if (strcmp(nifname + strlen(MODULE_PREFIX), "init/1") == 0) { + return &init_nif; + } + return NULL; +} + +REGISTER_NIF_COLLECTION(esp_adf_wav_decoder, NULL, NULL, get_nif) + +#endif diff --git a/nifs/include/atomvm_esp_adf_common.h b/nifs/include/atomvm_esp_adf_common.h new file mode 100644 index 0000000..efeba51 --- /dev/null +++ b/nifs/include/atomvm_esp_adf_common.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: MIT */ +#include + +#include + +#include +#include +#include + +bool get_integer_parameter(Context *ctx, term argv[], AtomString key, int *value, bool required); +bool get_bool_parameter(Context *ctx, term argv[], AtomString key, bool *value); +bool get_enum_parameter(Context *ctx, term argv[], AtomString key, const AtomStringIntPair *table, int *value); diff --git a/src/esp_adf_aac_decoder.erl b/src/esp_adf_aac_decoder.erl index fe960db..2143f04 100644 --- a/src/esp_adf_aac_decoder.erl +++ b/src/esp_adf_aac_decoder.erl @@ -3,9 +3,18 @@ -export([init/1]). +-type aac_decoder_option() :: + esp_adf_audio_element:audio_element_opt() + | {out_rb_size, pos_integer()} + | {task_stack, pos_integer()} + | {task_core, non_neg_integer()} + | {task_prio, non_neg_integer()} + | {stack_in_ext, boolean()} + | {plus_enable, boolean()}. + %% @doc Create a handle to an AAC decoder audio element. %% @param Cfg configuration %% @return an audio element resource --spec init([esp_adf_audio_element:audio_element_opt()]) -> esp_adf_audio_element:audio_element(). +-spec init([aac_decoder_option()]) -> esp_adf_audio_element:audio_element(). init(_Cfg) -> erlang:nif_error(undefined). diff --git a/src/esp_adf_amr_decoder.erl b/src/esp_adf_amr_decoder.erl new file mode 100644 index 0000000..742b43a --- /dev/null +++ b/src/esp_adf_amr_decoder.erl @@ -0,0 +1,19 @@ +% SPDX-License-Identifier: MIT +-module(esp_adf_amr_decoder). + +-export([init/1]). + +-type amr_decoder_option() :: + esp_adf_audio_element:audio_element_opt() + | {out_rb_size, pos_integer()} + | {task_stack, pos_integer()} + | {task_core, non_neg_integer()} + | {task_prio, non_neg_integer()} + | {stack_in_ext, boolean()}. + +%% @doc Create a handle to an AMR decoder audio element. +%% @param Cfg configuration +%% @return an audio element resource +-spec init([amr_decoder_option()]) -> esp_adf_audio_element:audio_element(). +init(_Cfg) -> + erlang:nif_error(undefined). diff --git a/src/esp_adf_flac_decoder.erl b/src/esp_adf_flac_decoder.erl new file mode 100644 index 0000000..bf775bd --- /dev/null +++ b/src/esp_adf_flac_decoder.erl @@ -0,0 +1,19 @@ +% SPDX-License-Identifier: MIT +-module(esp_adf_flac_decoder). + +-export([init/1]). + +-type flac_decoder_option() :: + esp_adf_audio_element:audio_element_opt() + | {out_rb_size, pos_integer()} + | {task_stack, pos_integer()} + | {task_core, non_neg_integer()} + | {task_prio, non_neg_integer()} + | {stack_in_ext, boolean()}. + +%% @doc Create a handle to an FLAC decoder audio element. +%% @param Cfg configuration +%% @return an audio element resource +-spec init([flac_decoder_option()]) -> esp_adf_audio_element:audio_element(). +init(_Cfg) -> + erlang:nif_error(undefined). diff --git a/src/esp_adf_g711_decoder.erl b/src/esp_adf_g711_decoder.erl new file mode 100644 index 0000000..bd3feb3 --- /dev/null +++ b/src/esp_adf_g711_decoder.erl @@ -0,0 +1,22 @@ +% SPDX-License-Identifier: MIT +-module(esp_adf_g711_decoder). + +-export([init/1]). + +-type g711_dec_mode() :: alaw | ulaw. + +-type g711_decoder_option() :: + esp_adf_audio_element:audio_element_opt() + | {out_rb_size, pos_integer()} + | {task_stack, pos_integer()} + | {task_core, non_neg_integer()} + | {task_prio, non_neg_integer()} + | {stack_in_ext, boolean()} + | {dec_mode, g711_dec_mode()}. + +%% @doc Create a handle to an G711 decoder audio element. +%% @param Cfg configuration +%% @return an audio element resource +-spec init([g711_decoder_option()]) -> esp_adf_audio_element:audio_element(). +init(_Cfg) -> + erlang:nif_error(undefined). diff --git a/src/esp_adf_mp3_decoder.erl b/src/esp_adf_mp3_decoder.erl index e7a2dc3..f2bf97a 100644 --- a/src/esp_adf_mp3_decoder.erl +++ b/src/esp_adf_mp3_decoder.erl @@ -3,9 +3,18 @@ -export([init/1]). +-type mp3_decoder_option() :: + esp_adf_audio_element:audio_element_opt() + | {out_rb_size, pos_integer()} + | {task_stack, pos_integer()} + | {task_core, non_neg_integer()} + | {task_prio, non_neg_integer()} + | {stack_in_ext, boolean()} + | {id3_parse_enable, boolean()}. + %% @doc Create a handle to an MP3 decoder audio element. %% @param Cfg configuration %% @return an audio element resource --spec init([esp_adf_audio_element:audio_element_opt()]) -> esp_adf_audio_element:audio_element(). +-spec init([mp3_decoder_option()]) -> esp_adf_audio_element:audio_element(). init(_Cfg) -> erlang:nif_error(undefined). diff --git a/src/esp_adf_ogg_decoder.erl b/src/esp_adf_ogg_decoder.erl new file mode 100644 index 0000000..b1a4c5a --- /dev/null +++ b/src/esp_adf_ogg_decoder.erl @@ -0,0 +1,19 @@ +% SPDX-License-Identifier: MIT +-module(esp_adf_ogg_decoder). + +-export([init/1]). + +-type ogg_decoder_option() :: + esp_adf_audio_element:audio_element_opt() + | {out_rb_size, pos_integer()} + | {task_stack, pos_integer()} + | {task_core, non_neg_integer()} + | {task_prio, non_neg_integer()} + | {stack_in_ext, boolean()}. + +%% @doc Create a handle to an OGG decoder audio element. +%% @param Cfg configuration +%% @return an audio element resource +-spec init([ogg_decoder_option()]) -> esp_adf_audio_element:audio_element(). +init(_Cfg) -> + erlang:nif_error(undefined). diff --git a/src/esp_adf_opus_decoder.erl b/src/esp_adf_opus_decoder.erl new file mode 100644 index 0000000..c64e7b7 --- /dev/null +++ b/src/esp_adf_opus_decoder.erl @@ -0,0 +1,19 @@ +% SPDX-License-Identifier: MIT +-module(esp_adf_opus_decoder). + +-export([init/1]). + +-type opus_decoder_option() :: + esp_adf_audio_element:audio_element_opt() + | {out_rb_size, pos_integer()} + | {task_stack, pos_integer()} + | {task_core, non_neg_integer()} + | {task_prio, non_neg_integer()} + | {stack_in_ext, boolean()}. + +%% @doc Create a handle to an OPUS decoder audio element. +%% @param Cfg configuration +%% @return an audio element resource +-spec init([opus_decoder_option()]) -> esp_adf_audio_element:audio_element(). +init(_Cfg) -> + erlang:nif_error(undefined). diff --git a/src/esp_adf_pcm_decoder.erl b/src/esp_adf_pcm_decoder.erl new file mode 100644 index 0000000..5b02fd0 --- /dev/null +++ b/src/esp_adf_pcm_decoder.erl @@ -0,0 +1,22 @@ +% SPDX-License-Identifier: MIT +-module(esp_adf_pcm_decoder). + +-export([init/1]). + +-type pcm_decoder_option() :: + esp_adf_audio_element:audio_element_opt() + | {out_rb_size, pos_integer()} + | {task_stack, pos_integer()} + | {task_core, non_neg_integer()} + | {task_prio, non_neg_integer()} + | {stack_in_ext, boolean()} + | {rate, pos_integer()} + | {bits, 8 | 16 | 24 | 32} + | {channels, 1..2}. + +%% @doc Create a handle to an PCM decoder audio element. +%% @param Cfg configuration +%% @return an audio element resource +-spec init([pcm_decoder_option()]) -> esp_adf_audio_element:audio_element(). +init(_Cfg) -> + erlang:nif_error(undefined). diff --git a/src/esp_adf_rsp_filter.erl b/src/esp_adf_rsp_filter.erl new file mode 100644 index 0000000..9b77c6a --- /dev/null +++ b/src/esp_adf_rsp_filter.erl @@ -0,0 +1,38 @@ +% SPDX-License-Identifier: MIT +-module(esp_adf_rsp_filter). + +-export([init/1]). + +-type esp_resample_mode() :: decode | encode | uncross. + +-type esp_resample_type() :: auto | decimate | interp | resample | bypass. + +-type esp_rsp_prefer_type() :: memory | speed. + +-type rsp_filter_option() :: + esp_adf_audio_element:audio_element_opt() + | {src_bits, 8 | 16 | 24 | 32} + | {src_ch, 1 | 2} + | {src_rate, pos_integer()} + | {dest_bits, 16} + | {dest_ch, 1 | 2} + | {dest_rate, pos_integer()} + | {mode, esp_resample_mode()} + | {max_indata_bytes, pos_integer()} + | {out_len_bytes, pos_integer()} + | {type, esp_resample_type()} + | {complexity, 0 | 1..5} + | {down_ch_idx, non_neg_integer()} + | {prefer_flag, esp_rsp_prefer_type()} + | {out_rb_size, pos_integer()} + | {task_stack, pos_integer()} + | {task_core, non_neg_integer()} + | {task_prio, non_neg_integer()} + | {stack_in_ext, boolean()}. + +%% @doc Create a handle to an MP3 decoder audio element. +%% @param Cfg configuration +%% @return an audio element resource +-spec init([rsp_filter_option()]) -> esp_adf_audio_element:audio_element(). +init(_Cfg) -> + erlang:nif_error(undefined). diff --git a/src/esp_adf_wav_decoder.erl b/src/esp_adf_wav_decoder.erl new file mode 100644 index 0000000..38dc375 --- /dev/null +++ b/src/esp_adf_wav_decoder.erl @@ -0,0 +1,19 @@ +% SPDX-License-Identifier: MIT +-module(esp_adf_wav_decoder). + +-export([init/1]). + +-type wav_decoder_option() :: + esp_adf_audio_element:audio_element_opt() + | {out_rb_size, pos_integer()} + | {task_stack, pos_integer()} + | {task_core, non_neg_integer()} + | {task_prio, non_neg_integer()} + | {stack_in_ext, boolean()}. + +%% @doc Create a handle to an WAV decoder audio element. +%% @param Cfg configuration +%% @return an audio element resource +-spec init([wav_decoder_option()]) -> esp_adf_audio_element:audio_element(). +init(_Cfg) -> + erlang:nif_error(undefined).