Skip to content

Commit

Permalink
Add all esp adf decoders
Browse files Browse the repository at this point in the history
Signed-off-by: Paul Guyot <pguyot@kallisys.net>
  • Loading branch information
pguyot committed May 13, 2024
1 parent c8d3afc commit b8184cd
Show file tree
Hide file tree
Showing 27 changed files with 1,231 additions and 138 deletions.
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_opts: [""]

include:
- additional_opts: "FLAC_DECODER OGG_DECODER"
soc: "esp32c3"
idf-version: "v5.2.1"

- additional_opts: "G711_DECODER OPUS_DECODER"
soc: "esp32c3"
idf-version: "v5.2.1"

- additional_opts: "AMR_DECODER"
soc: "esp32c3"
idf-version: "v5.2.1"

- additional_opts: "PCM_DECODER WAV_DECODER"
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_opts }}" ; do
echo AVM_ESP_ADF_${codec}_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_opts == ''
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_opts == ''
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

0 comments on commit b8184cd

Please sign in to comment.