Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add all esp adf decoders #10

Merged
merged 1 commit into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
10 changes: 9 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
47 changes: 41 additions & 6 deletions Kconfig
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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

Expand Down
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
20 changes: 19 additions & 1 deletion nifs/atomvm_esp_adf_aac_decoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <term.h>

#include "atomvm_esp_adf_audio_element.h"
#include "atomvm_esp_adf_common.h"

// #define ENABLE_TRACE
#include <trace.h>
Expand Down Expand Up @@ -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);
}

Expand Down
102 changes: 102 additions & 0 deletions nifs/atomvm_esp_adf_amr_decoder.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/* SPDX-License-Identifier: MIT */
#include <sdkconfig.h>

#ifdef CONFIG_AVM_ESP_ADF_AMR_DECODER_ENABLE

#include <stdlib.h>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"

#include <esp_attr.h>
#include <esp_log.h>
#include <esp_system.h>

#include <amr_decoder.h>

#pragma GCC diagnostic pop

#include <context.h>
#include <defaultatoms.h>
#include <erl_nif.h>
#include <erl_nif_priv.h>
#include <esp32_sys.h>
#include <interop.h>
#include <memory.h>
#include <nifs.h>
#include <resources.h>
#include <term.h>

#include "atomvm_esp_adf_audio_element.h"
#include "atomvm_esp_adf_common.h"

// #define ENABLE_TRACE
#include <trace.h>

#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
62 changes: 62 additions & 0 deletions nifs/atomvm_esp_adf_common.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/* SPDX-License-Identifier: MIT */
#include <sdkconfig.h>

#include <stdbool.h>

#include <context.h>
#include <defaultatoms.h>
#include <interop.h>
#include <nifs.h>
#include <term.h>

#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;
}
Loading
Loading