chore: checkpoint 第一版
ci / verify (push) Canceled after 0s

This commit is contained in:
Codex Local
2026-09-06 17:04:11 +08:00
parent a8cdec98d1
commit 0c2151ace9
659 changed files with 185855 additions and 554 deletions
@@ -0,0 +1,105 @@
# Changelog
## v0.7.2
### Features
- Support C++ build
## v0.7.1
### Bug Fixes
- Fixed sonic bypass failed for first payload
### Features
- Added audio param setting for `aud_alc`, `aud_fade`, `aud_sonic`
## v0.7.0
### Features
- Updated `esp_audio_codec` to version v2.3.0
- Updated `esp_audio_effects` to version v1.1.0
- Added `esp_gmf_audio_enc_get_frame_size`, `esp_gmf_audio_enc_set_bitrate`, `esp_gmf_audio_enc_get_bitrate`, `esp_gmf_audio_enc_reconfig` and `esp_gmf_audio_enc_reconfig_by_sound_info` functions to `gmf_audio_enc`
- Added `SBC` and `LC3` encoders
- Added `esp_gmf_audio_dec_reconfig` and `esp_gmf_audio_dec_reconfig_by_sound_info` functions to `gmf_audio_dec`
- Added `SBC` and `LC3` decoders
- Added `meta_flag` in `gmf_audio_dec` to support audio decoder recovery status tracking
- Added common audio parameter setting interface
- Redefined audio methods name
- Removed the audio encoder and decoder reconfig interface in `esp_gmf_audio_helper.c`
- Used the `esp_gmf_element_handle_t` type handle in the `gmf_audio` module
- Use default configuration for `esp_gmf_audio_xxx_init` internally if configuration set to NULL
- Added TRUNCATE support for SONIC element to enable its flexible placement at any position within the pipeline
- Supported scenarios where the input data for audio encoding is not a complete frame
- Added PTS (Presentation Time Stamp) calculation and propagation for the `aud_alc`, `aud_eq`, `aud_fade`, `aud_enc`
- Supported user-configurable input frame length and load acquisition timeout for the mixer
### Bug Fixes
- Fixed `gmf_audio_enc` process blocked due to forget release of in_load when truncate is returned
- Fixed parameter mismatch in `audio_dec_reconfig_dec_by_sound_info`
- Standardize TAG identifier format across all audio elements with `aud` prefix
- Fixed a crash caused by passing a NULL encoder config to the init function and subsequently reporting info
- Fixed bugs in the implementation of methods for sonic, fade, and mixer
- Fixed the issue where the encoder modified the configuration during runtime
- Fixed the issue where the mixer did not forward the out_load parameter to downstream components when all input stream were no data
- Fixed bugs that not check validation of in and out buffer for mixer which will cause crash
- Fixed bugs where setting parameters before opening had no effect for `aud_fade` and `aud_mixer`
## v0.6.3
### Bug Fixes
- Fixed decoder not releasing input port on failure
- Fixed missing input size check in gmf_audio_enc when input is insufficient to encode a frame and stream has ended
- Fixed missing zero size check for acquire_in in gmf_audio_dec
- Fixed unmatched sub_cfg when reconfigured by esp_gmf_audio_helper_reconfig_dec_by_uri
## v0.6.2
### Features
- Limit the version of `esp_audio_codec` and `esp_audio_effects`
## v0.6.1
- Added missing #include "esp_gmf_audio_element.h" across all audio elements
- Fixed out-of-range parameter handling for mixer and EQ elements
- Resolved rate/bit/channel converter bypass errors caused by asynchronous modification of obj->cfg between set/event callbacks and process functions
- Deleted one unreasonable log from esp_gmf_audio_helper.c
## v0.6.0
### Features
- Updated the logic for GMF capabilities and method handling
- Added support for NULL object configuration
- Added GMF port attribution for elements
- Added gmf_cache and optimized the loop path for the audio encoder and decoder
- Renamed component to `gmf_audio`
- Updated the License
## v0.5.2
### Bug Fixes
- Fixed an issue where the decoder's output done flag was repeatedly set after the input was marked as done
- Fixed an issue where the input port was not released when the input data size was 0
## v0.5.1
### Bug Fixes
- Fixed the component requirements
## v0.5.0
### Features
- Initial version of `esp-gmf-audio`
@@ -0,0 +1,4 @@
idf_component_register(SRC_DIRS "./"
INCLUDE_DIRS "./include"
PRIV_INCLUDE_DIRS "./private_include"
REQUIRES esp_timer)
@@ -0,0 +1,22 @@
Espressif Modified MIT License
Copyright (c) 2025 Espressif Systems (Shanghai) CO., LTD
Permission is hereby granted for use EXCLUSIVELY with Espressif Systems products.
This includes the right to use, copy, modify, merge, publish, distribute, and sublicense
the Software, subject to the following conditions:
1. This Software MUST BE USED IN CONJUNCTION WITH ESPRESSIF SYSTEMS PRODUCTS.
2. The above copyright notice and this permission notice shall be included in all copies
or substantial portions of the Software.
3. Redistribution of the Software in source or binary form FOR USE WITH NON-ESPRESSIF PRODUCTS
is strictly prohibited.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
@@ -0,0 +1,43 @@
# ESP-GMF-Audio
- [![Component Registry](https://components.espressif.com/components/espressif/gmf_audio/badge.svg)](https://components.espressif.com/components/espressif/gmf_audio)
- [中文版](./README_CN.md)
ESP GMF Audio is a collection of GMF elements related to audio processing, including audio encoding, decoding, and audio effect processing algorithms. The currently supported audio modules are listed in the table below.
|Name|TAG|Function|Method|Input Port|Output Port|Input blocking time|Output blocking time|Dependent on Audio Information|
|:----:|:----:|:-----:|:----:|:----:|:----:|:----:|:----:|:----|
| AUDIO_DEC |aud_dec |Audio decoder: MP3,AAC,AMRNB,<br>AMRWB,FLAC,WAV,M4A,TS|Nil|Single|Single|User configurable, default value is maximum delay|User configurable, default value is maximum delay|No|
| AUDIO_ENC |aud_enc |Audio encoder: AAC,AMRNB,AMRWB,<br>ADPCM,OPUS,PCM|Nil|Single|Single|User configurable, default value is maximum delay|User configurable, default value is maximum delay|Yes|
| RATE_CVT|aud_rate_cvt |Audio sampling rate adjustment|`set_dest_rate`|Single|Single|Maximum delay|Maximum delay|Yes|
| BIT_CVT |aud_bit_cvt |Audio bit-depth conversion|`set_dest_bits`|Single|Single|Maximum delay|Maximum delay|Yes|
| CH_CVT |aud_ch_cvt |Audio channel conversion|`set_dest_ch`|Single|Single|Maximum delay|Maximum delay|Yes|
| ALC |aud_alc |Audio volume adjustment|`set_gain`<br>`get_gain`|Single|Single|Maximum delay|Maximum delay|Yes|
| EQ |aud_eq |Audio equalizer adjustment|`set_para`<br>`get_para`<br>`enable_filter`<br>`disable_filter`|Single|Single|Maximum delay|Maximum delay|Yes|
| FADE |aud_fade |Audio fade-in and fade-out effects|`set_mode`<br>`get_mode`<br>`reset_weight`|Single|Single|Maximum delay|Maximum delay|Yes|
| SONIC |aud_sonic |Audio pitch and speed shifting effects|`set_speed`<br>`get_speed`<br>`set_pitch`<br>`get_pitch`|Single|Single|Maximum delay|Maximum delay|Yes|
| MIXER |aud_mixer |Audio mixing effects|`set_info`<br>`set_mode`|Multiple|Single|The blocking time for the first channel is 0, while the blocking time for other channels is maximum delay|Maximum delay|No|
|INTERLEAVE|aud_intlv|Data interleaving|Nil|Multiple|Single|User configurable, default value is maximum delay|Maximum delay|Yes|
|DEINTERLEAVE|aud_deintlv|Data de-interleaving|Nil|Single|Multiple|Maximum delay|User configurable, default value is maximum delay|Yes|
## Usage
The ESP GMF Audio is often used in combination to form a pipeline. For example code, please refer to [test_app](../test_apps/main/elements/gmf_audio_play_el_test.c)。
You can also create and compile a project using the following commands, taking the `pipeline_play_embed_music` project as an example. Before starting, make sure you have a working [ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/get-started/index.html) environment.
### 1. Create the Example Project
Create the `pipeline_play_embed_music` example project based on the `gmf_examples` component (using version v0.7.0 as an example; update the version as needed):
```shell
idf.py create-project-from-example "espressif/gmf_examples=0.7.0:pipeline_play_embed_music"
```
### 2. Build and Flash the Project Using an ESP32-S3 Board
```shell
cd pipeline_play_embed_music
idf.py set-target esp32s3
idf.py -p YOUR_PORT flash monitor
```
@@ -0,0 +1,43 @@
# ESP-GMF-Audio
- [![Component Registry](https://components.espressif.com/components/espressif/gmf_audio/badge.svg)](https://components.espressif.com/components/espressif/gmf_audio)
- [English](./README.md)
ESP GMF Audio 是 GMF 音频处理相关元素的集合,包括音频编码,解码和音频效果处理算法。目前已支持的 Audio 模块参见下表。
| 名称 | 标签 | 功能 | 函数方法 | 输入端口 | 输出端口 | 输入阻塞时间 | 输出阻塞时间 |依赖音频信息 |
|:----:| :-----: | :----: | :----: |:----: |:----: |:----: |:----: |:---- |
| AUDIO_DEC | aud_dec | 音频解码:MP3,AAC,AMRNB,<br>AMRWB,FLAC,WAV,M4A,TS | 无 | 单个 | 单个 | 可用户配置,默认是最大延迟 |可用户配置,默认是最大延迟 |否 |
| AUDIO_ENC | aud_enc | 音频编码:AAC,AMRNB,AMRWB,<br>ADPCM,OPUS,PCM | 无 | 单个 | 单个 | 可用户配置,默认是最大延迟 |可用户配置,默认是最大延迟 |是 |
| RATE_CVT| aud_rate_cvt | 音频采样率调节 | `set_dest_rate` | 单个 | 单个 |最大延迟 |最大延迟| 是 |
| BIT_CVT | aud_bit_cvt | 音频比特位转换 | `set_dest_bits`| 单个 | 单个 |最大延迟 |最大延迟| 是 |
| CH_CVT | aud_ch_cvt | 音频声道数转换 | `set_dest_ch`| 单个 | 单个 |最大延迟 |最大延迟| 是 |
| ALC | aud_alc | 音频音量调节 | `set_gain`<br>`get_gain`| 单个 | 单个 |最大延迟 |最大延迟| 是 |
| EQ | aud_eq | 音频均衡器调节 |`set_para`<br>`get_para`<br>`enable_filter`<br>`disable_filter` |单个 |单个|最大延迟 |最大延迟|是 |
| FADE | aud_fade | 音频淡入淡出效果 |`set_mode`<br>`get_mode`<br>`reset_weight` | 单个 | 单个 |最大延迟 |最大延迟 |是 |
| SONIC | aud_sonic | 音频变速变调效果 |`set_speed`<br>`get_speed`<br>`set_pitch`<br>`get_pitch`| 单个 | 单个 |最大延迟 |最大延迟| 是 |
| MIXER | aud_mixer | 音频混音效果 |`set_info`<br>`set_mode`| 多个 | 单个 | 第一路阻塞时间为0,其他路阻塞时间为最大延迟 |最大延迟| 否 |
|INTERLEAVE| aud_intlv | 数据交织 | 无 | 多个 | 单个 | 可用户配置,默认是最大延迟 |最大延迟| 是 |
|DEINTERLEAVE| aud_deintlv | 数据解交织 | 无| 单个 | 多个 |最大延迟|可用户配置,默认是最大延迟 |是 |
## 示例
ESP GMF Audio 常常组合成管道使用,示例代码请参考 [test_app](../test_apps/main/elements/gmf_audio_play_el_test.c)。
此外,也可通过以下命令创建并编译工程,以 `pipeline_play_embed_music` 示例工程为例。开始之前需要有可运行的 [ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/get-started/index.html) 环境。
### 1. 创建示例工程
基于 `gmf_examples` 组件创建 `pipeline_play_embed_music` 的示例(以 v0.7.0 版本为例, 请根据实际使用更新版本参数)
``` shell
idf.py create-project-from-example "espressif/gmf_examples=0.7.0:pipeline_play_embed_music"
```
### 2. 基于 ESP32S3 编译和下载
```shell
cd pipeline_play_embed_music
idf.py set-target esp32s3`
idf.py -p YOUR_PORT flash monitor
```
@@ -0,0 +1,353 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#include <string.h>
#include "esp_log.h"
#include "esp_gmf_oal_mem.h"
#include "esp_gmf_oal_mutex.h"
#include "esp_gmf_node.h"
#include "esp_gmf_alc.h"
#include "esp_gmf_args_desc.h"
#include "gmf_audio_common.h"
#include "esp_gmf_audio_methods_def.h"
#include "esp_gmf_cap.h"
#include "esp_gmf_caps_def.h"
#include "esp_gmf_audio_element.h"
#define GMF_ALC_DEFAULT_MAX_CHANNEL 2
/**
* @brief Audio ALC context in GMF
*/
typedef struct {
esp_gmf_audio_element_t parent; /*!< The GMF alc handle */
esp_ae_alc_handle_t alc_hd; /*!< The audio effects alc handle */
uint8_t bytes_per_sample; /*!< Bytes number of per sampling point */
int8_t *gain; /*!< The modified gain value of a certain channel number */
int8_t max_ch; /*!< The maximum channel number */
bool need_reopen : 1; /*!< Whether need to reopen.
True: Execute the close function first, then execute the open function
False: Do nothing */
} esp_gmf_alc_t;
static const char *TAG = "ESP_GMF_ALC";
static esp_gmf_err_t __alc_set_gain(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc,
uint8_t *buf, int buf_len)
{
ESP_GMF_NULL_CHECK(TAG, arg_desc, { return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, buf, { return ESP_GMF_ERR_INVALID_ARG;});
esp_gmf_args_desc_t *alc_desc = arg_desc;
uint8_t idx = (uint8_t)(*buf);
alc_desc = alc_desc->next;
int8_t gain = (int8_t)(*(buf + alc_desc->offset));
esp_gmf_err_t ret = ESP_GMF_ERR_OK;
if (idx == 0xFF) {
// Apply gain to all channels
esp_ae_alc_cfg_t *config = (esp_ae_alc_cfg_t *)OBJ_GET_CFG(handle);
if (config) {
for (uint8_t i = 0; i < config->channel; i++) {
ret |= esp_gmf_alc_set_gain(handle, i, gain);
}
}
} else {
ret = esp_gmf_alc_set_gain(handle, idx, gain);
}
return ret;
}
static esp_gmf_err_t __alc_get_gain(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc,
uint8_t *buf, int buf_len)
{
ESP_GMF_NULL_CHECK(TAG, arg_desc, {return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;});
esp_gmf_args_desc_t *alc_desc = arg_desc;
uint8_t idx = (uint8_t)(*buf);
alc_desc = alc_desc->next;
int8_t *gain = (int8_t *)(buf + alc_desc->offset);
return esp_gmf_alc_get_gain(handle, idx, gain);
}
static esp_gmf_err_t esp_gmf_alc_new(void *cfg, esp_gmf_obj_handle_t *handle)
{
return esp_gmf_alc_init(cfg, (esp_gmf_element_handle_t *)handle);
}
static esp_gmf_job_err_t esp_gmf_alc_open(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_alc_t *alc = (esp_gmf_alc_t *)self;
esp_ae_alc_cfg_t *config = (esp_ae_alc_cfg_t *)OBJ_GET_CFG(self);
ESP_GMF_NULL_CHECK(TAG, config, { return ESP_GMF_JOB_ERR_FAIL;});
alc->bytes_per_sample = (config->bits_per_sample >> 3) * config->channel;
esp_ae_alc_open(config, &alc->alc_hd);
ESP_GMF_CHECK(TAG, alc->alc_hd, { return ESP_GMF_JOB_ERR_FAIL;}, "Failed to create alc handle");
GMF_AUDIO_UPDATE_SND_INFO(self, config->sample_rate, config->bits_per_sample, config->channel);
for (size_t i = 0; i < config->channel; i++) {
esp_ae_err_t ret = esp_ae_alc_set_gain(alc->alc_hd, i, alc->gain[i]);
if (ret != ESP_AE_ERR_OK) {
return ESP_GMF_JOB_ERR_FAIL;
}
}
alc->need_reopen = false;
ESP_LOGD(TAG, "Open, %p", self);
return ESP_GMF_JOB_ERR_OK;
}
static esp_gmf_job_err_t esp_gmf_alc_close(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_alc_t *alc = (esp_gmf_alc_t *)self;
ESP_LOGD(TAG, "Closed, %p", self);
if (alc->alc_hd != NULL) {
esp_ae_alc_close(alc->alc_hd);
alc->alc_hd = NULL;
}
return ESP_GMF_ERR_OK;
}
static esp_gmf_job_err_t esp_gmf_alc_process(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_alc_t *alc = (esp_gmf_alc_t *)self;
esp_gmf_job_err_t out_len = ESP_GMF_JOB_ERR_OK;
if (alc->need_reopen) {
esp_gmf_alc_close(self, NULL);
out_len = esp_gmf_alc_open(self, NULL);
if (out_len != ESP_GMF_JOB_ERR_OK) {
ESP_LOGE(TAG, "ALC reopen failed");
return out_len;
}
}
esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in;
esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out;
esp_gmf_payload_t *in_load = NULL;
esp_gmf_payload_t *out_load = NULL;
int samples_num = ESP_GMF_ELEMENT_GET(alc)->in_attr.data_size / (alc->bytes_per_sample);
int bytes = samples_num * alc->bytes_per_sample;
esp_gmf_err_io_t load_ret = esp_gmf_port_acquire_in(in_port, &in_load, bytes, ESP_GMF_MAX_DELAY);
samples_num = in_load->valid_size / (alc->bytes_per_sample);
bytes = samples_num * alc->bytes_per_sample;
if ((bytes != in_load->valid_size) || (load_ret < ESP_GMF_IO_OK)) {
ESP_LOGE(TAG, "Invalid in load size %d, ret %d", in_load->valid_size, load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
goto __alc_release;
}
if (in_port->is_shared == 1) {
out_load = in_load;
}
load_ret = esp_gmf_port_acquire_out(out_port, &out_load, samples_num ? bytes : in_load->buf_length, ESP_GMF_MAX_DELAY);
ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, out_len, { goto __alc_release;});
if (samples_num) {
esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)self)->lock);
esp_ae_err_t ret = esp_ae_alc_process(alc->alc_hd, samples_num, in_load->buf, out_load->buf);
esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)self)->lock);
ESP_GMF_RET_ON_ERROR(TAG, ret, {out_len = ESP_GMF_JOB_ERR_FAIL; goto __alc_release;}, "ALC process error %d", ret);
}
ESP_LOGV(TAG, "Samples: %d, IN-PLD: %p-%p-%d-%d-%d, OUT-PLD: %p-%p-%d-%d-%d",
samples_num, in_load, in_load->buf, in_load->valid_size, in_load->buf_length, in_load->is_done,
out_load, out_load->buf, out_load->valid_size, out_load->buf_length, out_load->is_done);
out_load->valid_size = samples_num * alc->bytes_per_sample;
out_load->is_done = in_load->is_done;
out_load->pts = in_load->pts;
esp_gmf_audio_el_update_file_pos((esp_gmf_element_handle_t)self, out_load->valid_size);
if (in_load->is_done) {
out_len = ESP_GMF_JOB_ERR_DONE;
ESP_LOGD(TAG, "ALC done, out len: %d", out_load->valid_size);
}
__alc_release:
// Release in and out port
if (out_load != NULL) {
load_ret = esp_gmf_port_release_out(out_port, out_load, ESP_GMF_MAX_DELAY);
if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) {
ESP_LOGE(TAG, "OUT port release error, ret:%d", load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
}
}
if (in_load != NULL) {
load_ret = esp_gmf_port_release_in(in_port, in_load, ESP_GMF_MAX_DELAY);
if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) {
ESP_LOGE(TAG, "IN port release error, ret:%d", load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
}
}
return out_len;
}
static esp_gmf_err_t alc_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx)
{
ESP_GMF_NULL_CHECK(TAG, ctx, {return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, evt, {return ESP_GMF_ERR_INVALID_ARG;});
if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO)
|| (evt->sub != ESP_GMF_INFO_SOUND)
|| (evt->payload == NULL)) {
return ESP_GMF_ERR_OK;
}
esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx;
esp_gmf_element_handle_t el = evt->from;
esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE;
esp_gmf_element_get_state(self, &state);
esp_gmf_info_sound_t *info = (esp_gmf_info_sound_t *)evt->payload;
esp_ae_alc_cfg_t *config = (esp_ae_alc_cfg_t *)OBJ_GET_CFG(self);
ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_FAIL);
esp_gmf_alc_t *alc = (esp_gmf_alc_t *)self;
if (info->channels > alc->max_ch) {
alc->gain = esp_gmf_oal_realloc(alc->gain, config->channel * sizeof(*alc->gain));
ESP_GMF_MEM_VERIFY(TAG, alc->gain, return ESP_GMF_ERR_MEMORY_LACK, "alc gain", config->channel * sizeof(*alc->gain));
alc->max_ch = info->channels;
}
alc->need_reopen = (config->sample_rate != info->sample_rates) || (info->channels != config->channel) || (config->bits_per_sample != info->bits);
config->sample_rate = info->sample_rates;
config->channel = info->channels;
config->bits_per_sample = info->bits;
ESP_LOGD(TAG, "RECV element info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, rate: %d, ch: %d, bits: %d",
OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type,
esp_gmf_event_get_state_str(state), info->sample_rates, info->channels, info->bits);
if (state == ESP_GMF_EVENT_STATE_NONE) {
esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED);
}
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t esp_gmf_alc_destroy(esp_gmf_element_handle_t self)
{
esp_gmf_alc_t *alc = (esp_gmf_alc_t *)self;
ESP_LOGD(TAG, "Destroyed, %p", self);
void *cfg = OBJ_GET_CFG(self);
if (cfg) {
esp_gmf_oal_free(cfg);
}
if (alc->gain) {
esp_gmf_oal_free(alc->gain);
alc->gain = NULL;
}
esp_gmf_audio_el_deinit(self);
esp_gmf_oal_free(alc);
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t _load_alc_caps_func(esp_gmf_element_handle_t handle)
{
esp_gmf_cap_t *caps = NULL;
esp_gmf_cap_t dec_caps = {0};
dec_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_ALC;
dec_caps.attr_fun = NULL;
int ret = esp_gmf_cap_append(&caps, &dec_caps);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to create capability");
esp_gmf_element_t *el = (esp_gmf_element_t *)handle;
el->caps = caps;
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t _load_alc_methods_func(esp_gmf_element_handle_t handle)
{
esp_gmf_method_t *method = NULL;
esp_gmf_args_desc_t *set_args = NULL;
esp_gmf_args_desc_t *get_args = NULL;
esp_gmf_err_t ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(ALC, SET_GAIN, IDX),
ESP_GMF_ARGS_TYPE_UINT8, sizeof(uint8_t), 0);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append index argument");
ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(ALC, SET_GAIN, GAIN), ESP_GMF_ARGS_TYPE_INT8,
sizeof(int8_t), sizeof(uint8_t));
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append gain argument");
ret = esp_gmf_method_append(&method, AMETHOD(ALC, SET_GAIN), __alc_set_gain, set_args);
ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(ALC, SET_GAIN));
ret = esp_gmf_args_desc_copy(set_args, &get_args);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to copy argument");
ret = esp_gmf_method_append(&method, AMETHOD(ALC, GET_GAIN), __alc_get_gain, get_args);
ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(ALC, GET_GAIN));
esp_gmf_element_t *el = (esp_gmf_element_t *)handle;
el->method = method;
return ESP_GMF_ERR_OK;
}
esp_gmf_err_t esp_gmf_alc_set_gain(esp_gmf_element_handle_t handle, uint8_t idx, int8_t gain)
{
ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;});
esp_gmf_alc_t *alc = (esp_gmf_alc_t *)handle;
if (idx >= alc->max_ch) {
ESP_LOGE(TAG, "Gain index %d is out of range", idx);
return ESP_GMF_ERR_INVALID_ARG;
}
if (alc->alc_hd) {
esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)handle)->lock);
esp_ae_err_t ret = esp_ae_alc_set_gain(alc->alc_hd, idx, gain);
esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)handle)->lock);
if (ret != ESP_AE_ERR_OK) {
return ESP_GMF_JOB_ERR_FAIL;
}
}
alc->gain[idx] = gain;
return ESP_GMF_JOB_ERR_OK;
}
esp_gmf_err_t esp_gmf_alc_get_gain(esp_gmf_element_handle_t handle, uint8_t idx, int8_t *gain)
{
ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, gain, { return ESP_GMF_ERR_INVALID_ARG;});
esp_gmf_alc_t *alc = (esp_gmf_alc_t *)handle;
if(idx > alc->max_ch) {
ESP_LOGE(TAG, "Gain index %d is out of range", idx);
return ESP_GMF_ERR_INVALID_ARG;
}
if (alc->alc_hd) {
esp_ae_err_t ret = esp_ae_alc_get_gain(alc->alc_hd, idx, gain);
if (ret != ESP_AE_ERR_OK) {
return ESP_GMF_JOB_ERR_FAIL;
}
return ESP_GMF_JOB_ERR_OK;
}
*gain = alc->gain[idx];
return ESP_GMF_JOB_ERR_OK;
}
esp_gmf_err_t esp_gmf_alc_init(esp_ae_alc_cfg_t *config, esp_gmf_element_handle_t *handle)
{
ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;});
*handle = NULL;
esp_gmf_err_t ret = ESP_GMF_ERR_OK;
esp_gmf_alc_t *alc = esp_gmf_oal_calloc(1, sizeof(esp_gmf_alc_t));
ESP_GMF_MEM_VERIFY(TAG, alc, {return ESP_GMF_ERR_MEMORY_LACK;}, "ALC", sizeof(esp_gmf_alc_t));
esp_gmf_obj_t *obj = (esp_gmf_obj_t *)alc;
obj->new_obj = esp_gmf_alc_new;
obj->del_obj = esp_gmf_alc_destroy;
alc->max_ch = GMF_ALC_DEFAULT_MAX_CHANNEL;
esp_ae_alc_cfg_t *cfg = esp_gmf_oal_calloc(1, sizeof(esp_ae_alc_cfg_t));
ESP_GMF_MEM_VERIFY(TAG, cfg, ret = ESP_GMF_ERR_MEMORY_LACK; goto ALC_INIT_FAIL;, "alc configuration", sizeof(esp_ae_alc_cfg_t));
esp_gmf_obj_set_config(obj, cfg, sizeof(esp_ae_alc_cfg_t));
if (config) {
alc->max_ch = config->channel > 0 ? config->channel : GMF_ALC_DEFAULT_MAX_CHANNEL;
memcpy(cfg, config, sizeof(esp_ae_alc_cfg_t));
} else {
esp_ae_alc_cfg_t dcfg = DEFAULT_ESP_GMF_ALC_CONFIG();
alc->max_ch = dcfg.channel;
memcpy(cfg, &dcfg, sizeof(esp_ae_alc_cfg_t));
}
alc->gain = esp_gmf_oal_calloc(1, alc->max_ch * sizeof(int8_t));
ESP_GMF_MEM_VERIFY(TAG, alc->gain, goto ALC_INIT_FAIL, "alc gain", alc->max_ch * sizeof(int8_t));
ret = esp_gmf_obj_set_tag(obj, "aud_alc");
ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto ALC_INIT_FAIL, "Failed to set obj tag");
esp_gmf_element_cfg_t el_cfg = {0};
ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0,
ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT);
ESP_GMF_ELEMENT_OUT_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0,
ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT);
el_cfg.dependency = true;
ret = esp_gmf_audio_el_init(alc, &el_cfg);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto ALC_INIT_FAIL, "Failed to initialize alc element");
ESP_GMF_ELEMENT_GET(alc)->ops.open = esp_gmf_alc_open;
ESP_GMF_ELEMENT_GET(alc)->ops.process = esp_gmf_alc_process;
ESP_GMF_ELEMENT_GET(alc)->ops.close = esp_gmf_alc_close;
ESP_GMF_ELEMENT_GET(alc)->ops.event_receiver = alc_received_event_handler;
ESP_GMF_ELEMENT_GET(alc)->ops.load_caps = _load_alc_caps_func;
ESP_GMF_ELEMENT_GET(alc)->ops.load_methods = _load_alc_methods_func;
*handle = obj;
ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj);
return ESP_GMF_ERR_OK;
ALC_INIT_FAIL:
esp_gmf_alc_destroy(obj);
return ret;
}
@@ -0,0 +1,517 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#include <string.h>
#include "esp_log.h"
#include "esp_gmf_oal_mem.h"
#include "esp_gmf_err.h"
#include "esp_gmf_audio_dec.h"
#include "esp_audio_types.h"
#include "esp_audio_simple_dec_default.h"
#include "gmf_audio_common.h"
#include "esp_gmf_cap.h"
#include "esp_fourcc.h"
#include "esp_gmf_caps_def.h"
#include "esp_gmf_audio_methods_def.h"
#include "esp_gmf_audio_element.h"
#define DEFAULT_DEC_OUTPUT_BUFFER_SIZE 1024
/**
* @brief Audio simple decoder context in GMF
*/
typedef struct {
esp_gmf_audio_element_t parent; /*!< The GMF audio decoder handle */
esp_audio_simple_dec_handle_t dec_hd; /*!< The audio simple decoder handle */
esp_audio_simple_dec_raw_t in_data; /*!< The audio simple decoder input data handle */
esp_audio_simple_dec_out_t out_data; /*!< The audio simple decoder output data handle */
int32_t buf_size; /*!< The size of decoder out buffer */
esp_gmf_payload_t *in_load; /*!< The input payload */
uint64_t pts; /*!< Audio pts */
} esp_gmf_audio_dec_t;
static const char *TAG = "ESP_GMF_ASMP_DEC";
static esp_gmf_err_t _dec_caps_iter_fun(uint32_t attr_index, esp_gmf_cap_attr_t *attr)
{
switch (attr_index) {
case 0: {
const static uint32_t support_dec_type[] = {ESP_FOURCC_MP3, ESP_FOURCC_AAC, ESP_FOURCC_OPUS, ESP_FOURCC_FLAC, ESP_FOURCC_AMRNB, ESP_FOURCC_AMRWB, ESP_FOURCC_ALAC,
ESP_FOURCC_M4A, ESP_FOURCC_ALAW, ESP_FOURCC_ULAW, ESP_FOURCC_LC3, ESP_FOURCC_SBC, ESP_FOURCC_PCM};
ESP_GMF_CAP_ATTR_SET_DISCRETE(attr, ESP_FOURCC_TO_INT('T', 'Y', 'P', 'E'), (uint32_t *) &support_dec_type,
sizeof(support_dec_type) / sizeof(uint32_t), sizeof(uint32_t));
break;
}
default:
attr->prop_type = ESP_GMF_PROP_TYPE_NONE;
return ESP_GMF_ERR_NOT_SUPPORT;
}
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t __audio_dec_reconfig(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc,
uint8_t *buf, int buf_len)
{
ESP_GMF_NULL_CHECK(TAG, buf, return ESP_GMF_ERR_INVALID_ARG);
esp_audio_simple_dec_cfg_t *config = (esp_audio_simple_dec_cfg_t *)buf;
return esp_gmf_audio_dec_reconfig(handle, config);
}
static esp_gmf_err_t __audio_dec_reconfig_by_sound_info(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc,
uint8_t *buf, int buf_len)
{
ESP_GMF_NULL_CHECK(TAG, buf, return ESP_GMF_ERR_INVALID_ARG);
esp_gmf_info_sound_t *snd_info = (esp_gmf_info_sound_t *)buf;
return esp_gmf_audio_dec_reconfig_by_sound_info(handle, snd_info);
}
static inline esp_gmf_err_t dupl_esp_audio_simple_cfg(esp_audio_simple_dec_cfg_t *config, esp_audio_simple_dec_cfg_t **new_config)
{
void *sub_cfg = NULL;
*new_config = esp_gmf_oal_calloc(1, sizeof(*config));
ESP_GMF_MEM_VERIFY(TAG, *new_config, {return ESP_GMF_ERR_MEMORY_LACK;}, "audio simple decoder configuration", sizeof(*config));
memcpy(*new_config, config, sizeof(*config));
if (config->dec_cfg && (config->cfg_size > 0)) {
sub_cfg = esp_gmf_oal_calloc(1, config->cfg_size);
ESP_GMF_MEM_VERIFY(TAG, sub_cfg, {esp_gmf_oal_free(*new_config); return ESP_GMF_ERR_MEMORY_LACK;},
"decoder configuration", config->cfg_size);
memcpy(sub_cfg, config->dec_cfg, config->cfg_size);
(*new_config)->dec_cfg = sub_cfg;
}
return ESP_GMF_JOB_ERR_OK;
}
static esp_gmf_err_t audio_dec_set_subcfg(esp_audio_simple_dec_cfg_t *dec_cfg, void *sub_cfg, int32_t sub_cfg_sz)
{
dec_cfg->dec_cfg = esp_gmf_oal_calloc(1, sub_cfg_sz);
ESP_GMF_MEM_CHECK(TAG, dec_cfg->dec_cfg, return ESP_GMF_ERR_MEMORY_LACK;);
dec_cfg->cfg_size = sub_cfg_sz;
memcpy(dec_cfg->dec_cfg, sub_cfg, sub_cfg_sz);
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t audio_dec_reconfig_dec_by_sound_info(esp_gmf_element_handle_t handle, esp_gmf_info_sound_t *info)
{
esp_audio_simple_dec_cfg_t *dec_cfg = (esp_audio_simple_dec_cfg_t *)OBJ_GET_CFG(handle);
if (dec_cfg == NULL) {
dec_cfg = esp_gmf_oal_calloc(1, sizeof(esp_audio_simple_dec_cfg_t));
ESP_GMF_MEM_VERIFY(TAG, dec_cfg, return ESP_GMF_ERR_MEMORY_LACK, "audio simple decoder configuration", sizeof(esp_audio_simple_dec_cfg_t));
esp_gmf_obj_set_config(handle, dec_cfg, sizeof(esp_audio_simple_dec_cfg_t));
}
esp_gmf_err_t ret = ESP_GMF_ERR_OK;
bool same_type = (info->format_id == dec_cfg->dec_type) ? (true) : (false);
dec_cfg->dec_type = info->format_id;
// free sub cfg first
if (dec_cfg->dec_cfg && (same_type == false)) {
esp_gmf_oal_free(dec_cfg->dec_cfg);
dec_cfg->dec_cfg = NULL;
dec_cfg->cfg_size = 0;
}
switch (info->format_id) {
case ESP_AUDIO_SIMPLE_DEC_TYPE_MP3:
case ESP_AUDIO_SIMPLE_DEC_TYPE_AMRWB:
case ESP_AUDIO_SIMPLE_DEC_TYPE_AMRNB:
case ESP_AUDIO_SIMPLE_DEC_TYPE_FLAC:
case ESP_AUDIO_SIMPLE_DEC_TYPE_WAV:
case ESP_AUDIO_SIMPLE_DEC_TYPE_M4A:
case ESP_AUDIO_SIMPLE_DEC_TYPE_TS:
break;
case ESP_AUDIO_SIMPLE_DEC_TYPE_AAC:
if (dec_cfg->dec_cfg == NULL) {
esp_aac_dec_cfg_t aac_cfg = {
.no_adts_header = false,
.aac_plus_enable = true,
};
ret = audio_dec_set_subcfg(dec_cfg, &aac_cfg, sizeof(esp_aac_dec_cfg_t));
}
esp_aac_dec_cfg_t *aac_cfg = (esp_aac_dec_cfg_t *)dec_cfg->dec_cfg;
aac_cfg->sample_rate = info->sample_rates;
aac_cfg->channel = info->channels;
aac_cfg->bits_per_sample = info->bits;
break;
case ESP_AUDIO_SIMPLE_DEC_TYPE_RAW_OPUS:
if (dec_cfg->dec_cfg == NULL) {
dec_cfg->dec_type = ESP_AUDIO_SIMPLE_DEC_TYPE_RAW_OPUS;
esp_opus_dec_cfg_t opus_cfg = ESP_OPUS_DEC_CONFIG_DEFAULT();
opus_cfg.frame_duration = ESP_OPUS_DEC_FRAME_DURATION_20_MS;
opus_cfg.channel = info->channels;
opus_cfg.sample_rate = info->sample_rates;
ret = audio_dec_set_subcfg(dec_cfg, &opus_cfg, sizeof(esp_opus_dec_cfg_t));
} else {
esp_opus_dec_cfg_t *opus_cfg = (esp_opus_dec_cfg_t *)dec_cfg->dec_cfg;
opus_cfg->channel = info->channels;
opus_cfg->sample_rate = info->sample_rates;
}
break;
case ESP_AUDIO_SIMPLE_DEC_TYPE_PCM:
esp_pcm_dec_cfg_t pcm_cfg = {
.sample_rate = info->sample_rates,
.channel = info->channels,
.bits_per_sample = info->bits,
};
ret = audio_dec_set_subcfg(dec_cfg, &pcm_cfg, sizeof(esp_pcm_dec_cfg_t));
break;
case ESP_AUDIO_SIMPLE_DEC_TYPE_G711A:
case ESP_AUDIO_SIMPLE_DEC_TYPE_G711U:
esp_g711_dec_cfg_t g711_cfg = {
.channel = info->channels,
};
ret = audio_dec_set_subcfg(dec_cfg, &g711_cfg, sizeof(esp_g711_dec_cfg_t));
break;
case ESP_AUDIO_SIMPLE_DEC_TYPE_ADPCM:
dec_cfg->dec_type = ESP_AUDIO_SIMPLE_DEC_TYPE_ADPCM;
esp_adpcm_dec_cfg_t adpcm_cfg = {
.bits_per_sample = 4,
.channel = info->channels,
.sample_rate = info->sample_rates,
};
ret = audio_dec_set_subcfg(dec_cfg, &adpcm_cfg, sizeof(esp_adpcm_dec_cfg_t));
break;
case ESP_AUDIO_SIMPLE_DEC_TYPE_SBC:
if (dec_cfg->dec_cfg == NULL) {
dec_cfg->dec_type = ESP_AUDIO_SIMPLE_DEC_TYPE_SBC;
esp_sbc_dec_cfg_t sbc_cfg = {
.sbc_mode = ESP_SBC_MODE_STD,
.ch_num = 2,
.enable_plc = false,
};
ret = audio_dec_set_subcfg(dec_cfg, &sbc_cfg, sizeof(esp_sbc_dec_cfg_t));
}
break;
case ESP_AUDIO_SIMPLE_DEC_TYPE_LC3:
if (dec_cfg->dec_cfg == NULL) {
dec_cfg->dec_type = ESP_AUDIO_SIMPLE_DEC_TYPE_LC3;
esp_lc3_dec_cfg_t lc3_cfg = ESP_LC3_DEC_CONFIG_DEFAULT();
lc3_cfg.channel = info->channels;
lc3_cfg.sample_rate = info->sample_rates;
lc3_cfg.bits_per_sample = info->bits;
ret = audio_dec_set_subcfg(dec_cfg, &lc3_cfg, sizeof(esp_lc3_dec_cfg_t));
} else {
esp_lc3_dec_cfg_t *lc3_cfg = (esp_lc3_dec_cfg_t *)dec_cfg->dec_cfg;
lc3_cfg->channel = info->channels;
lc3_cfg->sample_rate = info->sample_rates;
lc3_cfg->bits_per_sample = info->bits;
}
break;
default:
dec_cfg->dec_type = ESP_AUDIO_SIMPLE_DEC_TYPE_NONE;
ESP_LOGW(TAG, "Not support for simple decoder type %ld", info->format_id);
return ESP_GMF_ERR_NOT_SUPPORT;
}
ESP_LOGD(TAG, "The new dec type is %d", dec_cfg->dec_type);
return ((ret == ESP_GMF_ERR_OK) ? (ESP_GMF_ERR_OK) : (ret));
}
static inline void free_esp_audio_simple_cfg(esp_audio_simple_dec_cfg_t *config)
{
if (config) {
if (config->dec_cfg) {
esp_gmf_oal_free(config->dec_cfg);
config->cfg_size = 0;
}
esp_gmf_oal_free(config);
}
}
static esp_gmf_err_t esp_gmf_audio_dec_new(void *cfg, esp_gmf_obj_handle_t *handle)
{
return esp_gmf_audio_dec_init((esp_audio_simple_dec_cfg_t *)cfg, (esp_gmf_element_handle_t *)handle);
}
static esp_gmf_job_err_t esp_gmf_audio_dec_open(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_audio_dec_t *audio_dec = (esp_gmf_audio_dec_t *)self;
esp_audio_simple_dec_cfg_t *dec_cfg = (esp_audio_simple_dec_cfg_t *)OBJ_GET_CFG(self);
ESP_GMF_NULL_CHECK(TAG, dec_cfg, return ESP_GMF_ERR_FAIL);
esp_audio_simple_dec_open(dec_cfg, &audio_dec->dec_hd);
ESP_GMF_CHECK(TAG, audio_dec->dec_hd, {return ESP_GMF_JOB_ERR_FAIL;}, "Failed to create simple decoder handle");
esp_gmf_port_enable_payload_share(ESP_GMF_ELEMENT_GET(self)->in, false);
audio_dec->buf_size = DEFAULT_DEC_OUTPUT_BUFFER_SIZE;
ESP_LOGD(TAG, "Open, el: %p, cfg: %p, type: %d", self, dec_cfg, dec_cfg->dec_type);
return ESP_GMF_JOB_ERR_OK;
}
static esp_gmf_job_err_t esp_gmf_audio_dec_process(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_port_t *in_port = ESP_GMF_ELEMENT_GET(self)->in;
esp_gmf_port_t *out = ESP_GMF_ELEMENT_GET(self)->out;
esp_audio_err_t ret = ESP_AUDIO_ERR_OK;
esp_gmf_job_err_t out_len = ESP_GMF_JOB_ERR_OK;
esp_gmf_err_io_t load_ret = ESP_GMF_IO_OK;
esp_gmf_audio_dec_t *audio_dec = (esp_gmf_audio_dec_t *)self;
esp_gmf_payload_t *out_load = NULL;
esp_audio_simple_dec_info_t dec_info = {0};
esp_gmf_info_sound_t snd_info = {0};
if (audio_dec->in_data.len == 0) {
load_ret = esp_gmf_port_acquire_in(in_port, &audio_dec->in_load, ESP_GMF_ELEMENT_GET(audio_dec)->in_attr.data_size, in_port->wait_ticks);
ESP_GMF_PORT_ACQUIRE_IN_CHECK(TAG, load_ret, out_len, {goto __aud_proc_release;});
audio_dec->in_data.buffer = audio_dec->in_load->buf;
audio_dec->in_data.len = audio_dec->in_load->valid_size;
audio_dec->in_data.consumed = 0;
audio_dec->in_data.eos = audio_dec->in_load->is_done;
audio_dec->in_data.frame_recover = (audio_dec->in_load->meta_flag & ESP_GMF_META_FLAG_AUD_RECOVERY_PLC) ? 1 : 0;
}
ESP_LOGV(TAG, "Read, in_len: %ld, done: %d\r\n", audio_dec->in_data.len, audio_dec->in_load ? audio_dec->in_load->is_done : -1);
load_ret = esp_gmf_port_acquire_out(out, &out_load, audio_dec->buf_size, ESP_GMF_MAX_DELAY);
ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, out_len, {goto __aud_proc_release;});
out_load->valid_size = 0;
audio_dec->out_data.buffer = out_load->buf;
audio_dec->out_data.len = out_load->buf_length;
if (audio_dec->in_data.len == 0) {
if (audio_dec->in_load->is_done == true) {
out_len = ESP_GMF_JOB_ERR_DONE;
out_load->is_done = audio_dec->in_load->is_done;
ESP_LOGD(TAG, "Return done, line:%d", __LINE__);
goto __aud_proc_release;
} else {
out_len = ESP_GMF_JOB_ERR_CONTINUE;
ESP_LOGD(TAG, "Return Continue, size:%d", audio_dec->in_load->valid_size);
goto __aud_proc_release;
}
}
while (1) {
ret = esp_audio_simple_dec_process(audio_dec->dec_hd, &audio_dec->in_data, &audio_dec->out_data);
if (ret != ESP_AUDIO_ERR_OK && ret != ESP_AUDIO_ERR_BUFF_NOT_ENOUGH) {
ESP_LOGE(TAG, "Failed to decode data, ret: %d", ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
goto __aud_proc_release;
}
if (ret == ESP_AUDIO_ERR_BUFF_NOT_ENOUGH) {
load_ret = esp_gmf_port_release_out(out, out_load, out->wait_ticks);
ESP_GMF_PORT_RELEASE_OUT_CHECK(TAG, load_ret, out_len, {goto __aud_proc_release;});
load_ret = esp_gmf_port_acquire_out(out, &out_load, audio_dec->out_data.needed_size, ESP_GMF_MAX_DELAY);
ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, out_len, {goto __aud_proc_release;});
ESP_LOGW(TAG, "Not enough memory for out, need:%d, old: %ld, new: %d", (int)audio_dec->out_data.needed_size,
audio_dec->out_data.len, out_load->buf_length);
audio_dec->out_data.buffer = out_load->buf;
audio_dec->out_data.len = out_load->buf_length;
audio_dec->buf_size = audio_dec->out_data.needed_size;
continue;
}
if (audio_dec->in_data.consumed <= audio_dec->in_data.len) {
audio_dec->in_data.buffer += audio_dec->in_data.consumed;
audio_dec->in_data.len -= audio_dec->in_data.consumed;
}
ESP_LOGV(TAG, "Dec, out len: %ld, need: %ld, in len: %ld, consumed: %ld, dec: %ld",
audio_dec->out_data.len, audio_dec->out_data.needed_size,
audio_dec->in_data.len, audio_dec->in_data.consumed, audio_dec->out_data.decoded_size);
ESP_LOGV(TAG, "buf: %p, sz: %d, dec: %ld", out_load->buf, out_load->valid_size, audio_dec->out_data.decoded_size);
if (audio_dec->out_data.decoded_size > 0) {
esp_audio_simple_dec_get_info(audio_dec->dec_hd, &dec_info);
esp_gmf_audio_el_get_snd_info(self, &snd_info);
if (snd_info.sample_rates != dec_info.sample_rate
|| snd_info.channels != dec_info.channel
|| snd_info.bits != dec_info.bits_per_sample) {
ESP_LOGD(TAG, "NOTIFY Info, rate: %d, bits: %d, ch: %d --> rate: %ld, bits: %d, ch: %d",
snd_info.sample_rates, snd_info.bits, snd_info.channels, dec_info.sample_rate, dec_info.bits_per_sample, dec_info.channel);
GMF_AUDIO_UPDATE_SND_INFO(self, dec_info.sample_rate, dec_info.bits_per_sample, dec_info.channel);
}
out_load->valid_size = audio_dec->out_data.decoded_size;
out_load->pts = audio_dec->pts;
audio_dec->pts += GMF_AUDIO_CALC_PTS(out_load->valid_size, dec_info.sample_rate, dec_info.channel, dec_info.bits_per_sample);
esp_gmf_audio_el_update_file_pos(self, out_load->valid_size);
if (audio_dec->in_load != NULL && audio_dec->in_data.len > 0) {
ESP_LOGD(TAG, "Return truncate, in len:%ld", audio_dec->in_data.len);
out_len = ESP_GMF_JOB_ERR_TRUNCATE;
}
} else {
if (audio_dec->in_data.len > 0) {
continue;
}
if (audio_dec->in_load && audio_dec->in_load->is_done) {
out_load->is_done = audio_dec->in_load->is_done;
ESP_LOGD(TAG, "Return done, line:%d", __LINE__);
out_len = ESP_GMF_JOB_ERR_DONE;
} else {
ESP_LOGD(TAG, "Return Continue, in len:%ld", audio_dec->in_data.len);
out_len = ESP_GMF_JOB_ERR_CONTINUE;
}
}
ESP_LOGV(TAG, "Release IN, in_len: %ld, done: %d, decoded_size: %ld",
audio_dec->in_data.len, audio_dec->in_load ? audio_dec->in_load->is_done : -1, audio_dec->out_data.decoded_size);
break;
}
__aud_proc_release:
if (out_load != NULL) {
load_ret = esp_gmf_port_release_out(out, out_load, out->wait_ticks);
if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) {
ESP_LOGE(TAG, "OUT port release error, ret:%d", load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
}
}
// If decoding fails or there is no data on input side, release input port
if ((out_len == ESP_GMF_JOB_ERR_FAIL) || (audio_dec->in_load && (audio_dec->in_data.len == 0))) {
load_ret = esp_gmf_port_release_in(in_port, audio_dec->in_load, ESP_GMF_MAX_DELAY);
if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) {
ESP_LOGE(TAG, "IN port release error, ret:%d", load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
}
audio_dec->in_load = NULL;
}
return out_len;
}
static esp_gmf_job_err_t esp_gmf_audio_dec_close(esp_gmf_element_handle_t self, void *para)
{
ESP_LOGD(TAG, "Closed, %p", self);
esp_gmf_audio_dec_t *audio_dec = (esp_gmf_audio_dec_t *)self;
if (audio_dec->dec_hd != NULL) {
esp_audio_simple_dec_close(audio_dec->dec_hd);
audio_dec->dec_hd = NULL;
}
audio_dec->pts = 0;
esp_gmf_info_sound_t snd_info = {0};
audio_dec->in_load = NULL;
audio_dec->in_data.len = 0;
esp_gmf_audio_el_set_snd_info(self, &snd_info);
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t esp_gmf_audio_dec_destroy(esp_gmf_element_handle_t self)
{
ESP_LOGD(TAG, "Destroyed, %p", self);
free_esp_audio_simple_cfg(OBJ_GET_CFG(self));
esp_gmf_audio_el_deinit(self);
esp_gmf_oal_free(self);
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t _load_dec_caps_func(esp_gmf_element_handle_t handle)
{
esp_gmf_cap_t *caps = NULL;
esp_gmf_cap_t dec_caps = {0};
dec_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_DECODER;
dec_caps.attr_fun = _dec_caps_iter_fun;
int ret = esp_gmf_cap_append(&caps, &dec_caps);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to create capability");
esp_gmf_element_t *el = (esp_gmf_element_t *)handle;
el->caps = caps;
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t _load_dec_methods_func(esp_gmf_element_handle_t handle)
{
esp_gmf_method_t *method = NULL;
esp_gmf_args_desc_t *set_args = NULL;
esp_gmf_args_desc_t *reconfig_args = NULL;
esp_gmf_args_desc_t *sndinfo_args = NULL;
esp_gmf_err_t ret = esp_gmf_args_desc_append(&sndinfo_args, AMETHOD_ARG(DECODER, RECONFIG_BY_SND_INFO, INFO_TYPE), ESP_GMF_ARGS_TYPE_UINT32,
sizeof(uint32_t), offsetof(esp_gmf_info_sound_t, format_id));
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append type argument");
ret = esp_gmf_args_desc_append(&sndinfo_args, AMETHOD_ARG(DECODER, RECONFIG_BY_SND_INFO, INFO_SAMPLERATE), ESP_GMF_ARGS_TYPE_INT32,
sizeof(int32_t), offsetof(esp_gmf_info_sound_t, sample_rates));
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append sample_rates argument");
ret = esp_gmf_args_desc_append(&sndinfo_args, AMETHOD_ARG(DECODER, RECONFIG_BY_SND_INFO, INFO_CHANNEL), ESP_GMF_ARGS_TYPE_INT8,
sizeof(int8_t), 12);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append channels argument");
ret = esp_gmf_args_desc_append(&sndinfo_args, AMETHOD_ARG(DECODER, RECONFIG_BY_SND_INFO, INFO_BITS), ESP_GMF_ARGS_TYPE_INT8,
sizeof(int8_t), 13);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append bits argument");
ret = esp_gmf_args_desc_append_array(&set_args, AMETHOD_ARG(DECODER, RECONFIG_BY_SND_INFO, INFO), sndinfo_args,
sizeof(esp_gmf_info_sound_t), 0);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append sound info argument");
ret = esp_gmf_method_append(&method, AMETHOD(DECODER, RECONFIG_BY_SND_INFO), __audio_dec_reconfig_by_sound_info, set_args);
ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to register %s method", AMETHOD(DECODER, RECONFIG_BY_SND_INFO));
set_args = NULL;
ret = esp_gmf_args_desc_append(&reconfig_args, AMETHOD_ARG(DECODER, RECONFIG, CFG_TYPE), ESP_GMF_ARGS_TYPE_INT32,
sizeof(int32_t), offsetof(esp_audio_simple_dec_cfg_t, dec_type));
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append type argument");
ret = esp_gmf_args_desc_append(&reconfig_args, AMETHOD_ARG(DECODER, RECONFIG, CFG_SUBCFGPTR), ESP_GMF_ARGS_TYPE_INT32,
sizeof(int32_t), offsetof(esp_audio_simple_dec_cfg_t, dec_cfg));
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append cfg argument");
ret = esp_gmf_args_desc_append(&reconfig_args, AMETHOD_ARG(DECODER, RECONFIG, CFG_SUBCFGSZ), ESP_GMF_ARGS_TYPE_UINT32,
sizeof(uint32_t), offsetof(esp_audio_simple_dec_cfg_t, cfg_size));
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append cfg_sz argument");
ret = esp_gmf_args_desc_append_array(&set_args, AMETHOD_ARG(DECODER, RECONFIG, CFG), reconfig_args,
sizeof(esp_audio_simple_dec_cfg_t), 0);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append config argument");
ret = esp_gmf_method_append(&method, AMETHOD(DECODER, RECONFIG), __audio_dec_reconfig, set_args);
ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to register %s method", AMETHOD(DECODER, RECONFIG));
esp_gmf_element_t *el = (esp_gmf_element_t *)handle;
el->method = method;
return ESP_GMF_ERR_OK;
}
esp_gmf_err_t esp_gmf_audio_dec_reconfig(esp_gmf_element_handle_t handle, esp_audio_simple_dec_cfg_t *config)
{
ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG);
ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_INVALID_ARG);
esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE;
esp_gmf_element_get_state(handle, &state);
if (state < ESP_GMF_EVENT_STATE_OPENING) {
esp_audio_simple_dec_cfg_t *new_config = NULL;
esp_gmf_err_t ret = dupl_esp_audio_simple_cfg(config, &new_config);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to duplicate audio decoder configuration");
free_esp_audio_simple_cfg(OBJ_GET_CFG(handle));
esp_gmf_obj_set_config(handle, new_config, sizeof(*config));
return ESP_GMF_ERR_OK;
} else {
ESP_LOGE(TAG, "Failed to reconfig decoder due to invalid state: %s", esp_gmf_event_get_state_str(state));
return ESP_GMF_ERR_FAIL;
}
}
esp_gmf_err_t esp_gmf_audio_dec_reconfig_by_sound_info(esp_gmf_element_handle_t handle, esp_gmf_info_sound_t *info)
{
ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG);
esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE;
esp_gmf_element_get_state(handle, &state);
if (state < ESP_GMF_EVENT_STATE_OPENING) {
esp_gmf_err_t ret = audio_dec_reconfig_dec_by_sound_info(handle, info);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to reconfig simple decoder by sound information");
return ESP_GMF_ERR_OK;
} else {
ESP_LOGE(TAG, "Failed to reconfig decoder due to invalid state: %s", esp_gmf_event_get_state_str(state));
return ESP_GMF_ERR_FAIL;
}
}
esp_gmf_err_t esp_gmf_audio_dec_init(esp_audio_simple_dec_cfg_t *config, esp_gmf_element_handle_t *handle)
{
ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;});
*handle = NULL;
esp_gmf_err_t ret = ESP_GMF_ERR_OK;
esp_gmf_audio_dec_t *dec_hd = esp_gmf_oal_calloc(1, sizeof(esp_gmf_audio_dec_t));
ESP_GMF_MEM_VERIFY(TAG, dec_hd, {return ESP_GMF_ERR_MEMORY_LACK;}, "audio decoder", sizeof(esp_gmf_audio_dec_t));
esp_gmf_obj_t *obj = (esp_gmf_obj_t *)dec_hd;
obj->new_obj = esp_gmf_audio_dec_new;
obj->del_obj = esp_gmf_audio_dec_destroy;
esp_audio_simple_dec_cfg_t *cfg = NULL;
if (config) {
dupl_esp_audio_simple_cfg(config, &cfg);
} else {
esp_audio_simple_dec_cfg_t dcfg = DEFAULT_ESP_GMF_AUDIO_DEC_CONFIG();
dupl_esp_audio_simple_cfg(&dcfg, &cfg);
}
ESP_GMF_CHECK(TAG, cfg, {ret = ESP_GMF_ERR_MEMORY_LACK; goto ES_DEC_FAIL;}, "Failed to allocate audio decoder configuration");
esp_gmf_obj_set_config(obj, cfg, sizeof(esp_audio_simple_dec_cfg_t));
ret = esp_gmf_obj_set_tag(obj, "aud_dec");
ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto ES_DEC_FAIL, "Failed to set obj tag");
esp_gmf_element_cfg_t el_cfg = {0};
ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0,
ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, 512);
ESP_GMF_ELEMENT_OUT_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0,
ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, 512);
el_cfg.dependency = false;
ret = esp_gmf_audio_el_init(dec_hd, &el_cfg);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto ES_DEC_FAIL, "Failed to initialize audio decoder element");
ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj);
ESP_GMF_ELEMENT_GET(dec_hd)->ops.open = esp_gmf_audio_dec_open;
ESP_GMF_ELEMENT_GET(dec_hd)->ops.process = esp_gmf_audio_dec_process;
ESP_GMF_ELEMENT_GET(dec_hd)->ops.close = esp_gmf_audio_dec_close;
ESP_GMF_ELEMENT_GET(dec_hd)->ops.load_caps = _load_dec_caps_func;
ESP_GMF_ELEMENT_GET(dec_hd)->ops.load_methods = _load_dec_methods_func;
*handle = obj;
return ESP_GMF_ERR_OK;
ES_DEC_FAIL:
esp_gmf_audio_dec_destroy(obj);
return ret;
}
@@ -0,0 +1,701 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#include <string.h>
#include "esp_log.h"
#include "esp_gmf_node.h"
#include "esp_gmf_oal_mem.h"
#include "esp_gmf_oal_mutex.h"
#include "esp_gmf_audio_enc.h"
#include "esp_audio_enc_default.h"
#include "esp_gmf_cache.h"
#include "esp_gmf_cap.h"
#include "esp_gmf_caps_def.h"
#include "esp_gmf_audio_methods_def.h"
#include "esp_gmf_audio_element.h"
#include "gmf_audio_common.h"
#define AUD_ENC_DEFAULT_INPUT_TIME_MS (20)
#define SET_ENC_BASIC_INFO(cfg, info) do { \
(cfg)->sample_rate = (info)->sample_rates; \
(cfg)->channel = (info)->channels; \
(cfg)->bits_per_sample = (info)->bits; \
} while (0)
/**
* @brief Audio encoder context in GMF
*/
typedef struct {
esp_gmf_audio_element_t parent; /*!< The GMF audio encoder handle */
esp_audio_enc_handle_t audio_enc_hd; /*!< The audio encoder handle */
esp_gmf_cache_t *cached_payload; /*!< A Cached payload for data concatenation */
uint32_t bitrate; /*!< The bitrate of the encoded data */
esp_gmf_payload_t *origin_in_load; /*!< The original input payload */
int64_t cur_pts; /*!< The audio Presentation Time Stamp(pts) */
} esp_gmf_audio_enc_t;
static const char *TAG = "ESP_GMF_AENC";
static esp_gmf_err_t __audio_enc_get_frame_size(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc,
uint8_t *buf, int buf_len)
{
ESP_GMF_NULL_CHECK(TAG, arg_desc, return ESP_GMF_ERR_INVALID_ARG);
ESP_GMF_NULL_CHECK(TAG, buf, return ESP_GMF_ERR_INVALID_ARG);
esp_gmf_args_desc_t *enc_desc = arg_desc;
uint32_t *in_size = (uint32_t *)(buf);
enc_desc = enc_desc->next;
uint32_t *out_size = (uint32_t *)(buf + enc_desc->offset);
return esp_gmf_audio_enc_get_frame_size(handle, in_size, out_size);
}
static esp_gmf_err_t __audio_enc_set_bitrate(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc,
uint8_t *buf, int buf_len)
{
ESP_GMF_NULL_CHECK(TAG, arg_desc, return ESP_GMF_ERR_INVALID_ARG);
ESP_GMF_NULL_CHECK(TAG, buf, return ESP_GMF_ERR_INVALID_ARG);
uint32_t bitrate = *((uint32_t *)buf);
return esp_gmf_audio_enc_set_bitrate(handle, bitrate);
}
static esp_gmf_err_t __audio_enc_get_bitrate(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc,
uint8_t *buf, int buf_len)
{
ESP_GMF_NULL_CHECK(TAG, arg_desc, return ESP_GMF_ERR_INVALID_ARG);
ESP_GMF_NULL_CHECK(TAG, buf, return ESP_GMF_ERR_INVALID_ARG);
uint32_t *bitrate = (uint32_t *)buf;
return esp_gmf_audio_enc_get_bitrate(handle, bitrate);
}
static esp_gmf_err_t __audio_enc_reconfig(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc,
uint8_t *buf, int buf_len)
{
ESP_GMF_NULL_CHECK(TAG, arg_desc, return ESP_GMF_ERR_INVALID_ARG);
ESP_GMF_NULL_CHECK(TAG, buf, return ESP_GMF_ERR_INVALID_ARG);
esp_audio_enc_config_t *config = (esp_audio_enc_config_t *)buf;
return esp_gmf_audio_enc_reconfig(handle, config);
}
static esp_gmf_err_t __audio_enc_reconfig_by_sound_info(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc,
uint8_t *buf, int buf_len)
{
ESP_GMF_NULL_CHECK(TAG, arg_desc, return ESP_GMF_ERR_INVALID_ARG);
ESP_GMF_NULL_CHECK(TAG, buf, return ESP_GMF_ERR_INVALID_ARG);
esp_gmf_info_sound_t *snd_info = (esp_gmf_info_sound_t *)buf;
return esp_gmf_audio_enc_reconfig_by_sound_info(handle, snd_info);
}
static inline esp_gmf_err_t dupl_esp_gmf_audio_enc_cfg(esp_audio_enc_config_t *config, esp_audio_enc_config_t **new_config)
{
void *sub_cfg = NULL;
*new_config = esp_gmf_oal_calloc(1, sizeof(*config));
ESP_GMF_MEM_VERIFY(TAG, *new_config, {return ESP_GMF_ERR_MEMORY_LACK;}, "audio encoder handle configuration", sizeof(*config));
memcpy(*new_config, config, sizeof(*config));
if (config->cfg && (config->cfg_sz > 0)) {
sub_cfg = esp_gmf_oal_calloc(1, config->cfg_sz);
ESP_GMF_MEM_VERIFY(TAG, sub_cfg, {esp_gmf_oal_free(*new_config); return ESP_GMF_ERR_MEMORY_LACK;},
"audio encoder configuration", (int)config->cfg_sz);
memcpy(sub_cfg, config->cfg, config->cfg_sz);
(*new_config)->cfg = sub_cfg;
}
return ESP_GMF_JOB_ERR_OK;
}
static inline void free_esp_gmf_audio_enc_cfg(esp_audio_enc_config_t *config)
{
if (config) {
if (config->cfg) {
esp_gmf_oal_free(config->cfg);
config->cfg = NULL;
config->cfg_sz = 0;
}
esp_gmf_oal_free(config);
}
}
static inline void audio_enc_change_audio_info(esp_audio_enc_config_t *enc_cfg, esp_gmf_info_sound_t *info)
{
switch (enc_cfg->type) {
case ESP_AUDIO_TYPE_AAC: {
esp_aac_enc_config_t *aac_enc_cfg = (esp_aac_enc_config_t *)enc_cfg->cfg;
SET_ENC_BASIC_INFO(aac_enc_cfg, info);
break;
}
case ESP_AUDIO_TYPE_AMRNB: {
esp_amrnb_enc_config_t *amr_enc_cfg = (esp_amrnb_enc_config_t *)enc_cfg->cfg;
SET_ENC_BASIC_INFO(amr_enc_cfg, info);
break;
}
case ESP_AUDIO_TYPE_G711U:
case ESP_AUDIO_TYPE_G711A: {
esp_g711_enc_config_t *g711_enc_cfg = (esp_g711_enc_config_t *)enc_cfg->cfg;
SET_ENC_BASIC_INFO(g711_enc_cfg, info);
break;
}
case ESP_AUDIO_TYPE_AMRWB: {
esp_amrnb_enc_config_t *amr_enc_cfg = (esp_amrnb_enc_config_t *)enc_cfg->cfg;
SET_ENC_BASIC_INFO(amr_enc_cfg, info);
break;
}
case ESP_AUDIO_TYPE_ALAC: {
esp_alac_enc_config_t *alac_enc_cfg = (esp_alac_enc_config_t *)enc_cfg->cfg;
SET_ENC_BASIC_INFO(alac_enc_cfg, info);
break;
}
case ESP_AUDIO_TYPE_PCM: {
esp_pcm_enc_config_t *pcm_enc_cfg = (esp_pcm_enc_config_t *)enc_cfg->cfg;
SET_ENC_BASIC_INFO(pcm_enc_cfg, info);
break;
}
case ESP_AUDIO_TYPE_OPUS: {
esp_opus_enc_config_t *opus_enc_cfg = (esp_opus_enc_config_t *)enc_cfg->cfg;
SET_ENC_BASIC_INFO(opus_enc_cfg, info);
break;
}
case ESP_AUDIO_TYPE_ADPCM: {
esp_adpcm_enc_config_t *adpcm_enc_cfg = (esp_adpcm_enc_config_t *)enc_cfg->cfg;
SET_ENC_BASIC_INFO(adpcm_enc_cfg, info);
break;
}
case ESP_AUDIO_TYPE_LC3: {
esp_lc3_enc_config_t *lc3_enc_cfg = (esp_lc3_enc_config_t *)enc_cfg->cfg;
SET_ENC_BASIC_INFO(lc3_enc_cfg, info);
break;
}
case ESP_AUDIO_TYPE_SBC: {
esp_sbc_enc_config_t *sbc_enc_cfg = (esp_sbc_enc_config_t *)enc_cfg->cfg;
sbc_enc_cfg->sample_rate = info->sample_rates;
sbc_enc_cfg->bits_per_sample = info->bits;
if (info->channels == 1) {
sbc_enc_cfg->ch_mode = ESP_SBC_CH_MODE_MONO;
} else if (info->channels == 2) {
sbc_enc_cfg->ch_mode = ((sbc_enc_cfg->ch_mode > ESP_SBC_CH_MODE_MONO && sbc_enc_cfg->ch_mode <= ESP_SBC_CH_MODE_JOINT_STEREO) ? (sbc_enc_cfg->ch_mode) : (ESP_SBC_CH_MODE_DUAL));
} else {
sbc_enc_cfg->ch_mode = ESP_SBC_CH_MODE_INVALID;
}
break;
}
default:
break;
}
}
static esp_gmf_err_t audio_enc_set_subcfg(esp_audio_enc_config_t *enc_cfg, void *sub_cfg, int32_t sub_cfg_sz)
{
enc_cfg->cfg = esp_gmf_oal_calloc(1, sub_cfg_sz);
ESP_GMF_MEM_CHECK(TAG, enc_cfg->cfg, return ESP_GMF_ERR_MEMORY_LACK;);
enc_cfg->cfg_sz = sub_cfg_sz;
memcpy(enc_cfg->cfg, sub_cfg, sub_cfg_sz);
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t audio_enc_reconfig_enc_by_sound_info(esp_gmf_element_handle_t handle, esp_gmf_info_sound_t *info)
{
esp_audio_enc_config_t *cfg = (esp_audio_enc_config_t *)OBJ_GET_CFG(handle);
if (cfg == NULL) {
cfg = esp_gmf_oal_calloc(1, sizeof(esp_audio_enc_config_t));
ESP_GMF_MEM_VERIFY(TAG, cfg, return ESP_GMF_ERR_MEMORY_LACK, "audio encoder configuration", sizeof(esp_audio_enc_config_t));
esp_gmf_obj_set_config(handle, cfg, sizeof(esp_audio_enc_config_t));
}
esp_gmf_err_t ret = ESP_GMF_ERR_OK;
bool same_type = (cfg->type == info->format_id) ? true : false;
// free sub cfg first
if (cfg->cfg && (same_type == false)) {
esp_gmf_oal_free(cfg->cfg);
cfg->cfg = NULL;
cfg->cfg_sz = 0;
}
cfg->type = info->format_id;
if (same_type == true && (cfg->cfg != NULL)) {
audio_enc_change_audio_info(cfg, info);
return ESP_GMF_ERR_OK;
}
switch (info->format_id) {
case ESP_AUDIO_TYPE_AAC: {
esp_aac_enc_config_t aac_enc_cfg = ESP_AAC_ENC_CONFIG_DEFAULT();
SET_ENC_BASIC_INFO(&aac_enc_cfg, info);
aac_enc_cfg.bitrate = info->bitrate;
ret = audio_enc_set_subcfg(cfg, &aac_enc_cfg, sizeof(esp_aac_enc_config_t));
break;
}
case ESP_AUDIO_TYPE_AMRNB: {
esp_amrnb_enc_config_t amr_enc_cfg = ESP_AMRNB_ENC_CONFIG_DEFAULT();
SET_ENC_BASIC_INFO(&amr_enc_cfg, info);
amr_enc_cfg.bitrate_mode = info->bitrate;
ret = audio_enc_set_subcfg(cfg, &amr_enc_cfg, sizeof(esp_amrnb_enc_config_t));
break;
}
case ESP_AUDIO_TYPE_AMRWB: {
esp_amrnb_enc_config_t amr_enc_cfg = ESP_AMRNB_ENC_CONFIG_DEFAULT();
SET_ENC_BASIC_INFO(&amr_enc_cfg, info);
amr_enc_cfg.bitrate_mode = info->bitrate;
ret = audio_enc_set_subcfg(cfg, &amr_enc_cfg, sizeof(esp_amrnb_enc_config_t));
break;
}
case ESP_AUDIO_TYPE_G711A: {
esp_g711_enc_config_t g711_enc_cfg = ESP_G711_ENC_CONFIG_DEFAULT();
SET_ENC_BASIC_INFO(&g711_enc_cfg, info);
ret = audio_enc_set_subcfg(cfg, &g711_enc_cfg, sizeof(esp_g711_enc_config_t));
break;
}
case ESP_AUDIO_TYPE_G711U: {
esp_g711_enc_config_t g711_enc_cfg = ESP_G711_ENC_CONFIG_DEFAULT();
SET_ENC_BASIC_INFO(&g711_enc_cfg, info);
ret = audio_enc_set_subcfg(cfg, &g711_enc_cfg, sizeof(esp_g711_enc_config_t));
break;
}
case ESP_AUDIO_TYPE_ALAC: {
esp_alac_enc_config_t alac_enc_cfg = ESP_ALAC_ENC_CONFIG_DEFAULT();
SET_ENC_BASIC_INFO(&alac_enc_cfg, info);
ret = audio_enc_set_subcfg(cfg, &alac_enc_cfg, sizeof(esp_alac_enc_config_t));
break;
}
case ESP_AUDIO_TYPE_PCM: {
esp_pcm_enc_config_t pcm_enc_cfg = ESP_PCM_ENC_CONFIG_DEFAULT();
SET_ENC_BASIC_INFO(&pcm_enc_cfg, info);
ret = audio_enc_set_subcfg(cfg, &pcm_enc_cfg, sizeof(esp_pcm_enc_config_t));
break;
}
case ESP_AUDIO_TYPE_OPUS: {
esp_opus_enc_config_t opus_enc_cfg = ESP_OPUS_ENC_CONFIG_DEFAULT();
SET_ENC_BASIC_INFO(&opus_enc_cfg, info);
opus_enc_cfg.bitrate = info->bitrate;
ret = audio_enc_set_subcfg(cfg, &opus_enc_cfg, sizeof(esp_opus_enc_config_t));
break;
}
case ESP_AUDIO_TYPE_ADPCM: {
esp_adpcm_enc_config_t adpcm_enc_cfg = ESP_ADPCM_ENC_CONFIG_DEFAULT();
SET_ENC_BASIC_INFO(&adpcm_enc_cfg, info);
ret = audio_enc_set_subcfg(cfg, &adpcm_enc_cfg, sizeof(esp_adpcm_enc_config_t));
break;
}
case ESP_AUDIO_TYPE_LC3: {
esp_lc3_enc_config_t lc3_enc_cfg = ESP_LC3_ENC_CONFIG_DEFAULT();
SET_ENC_BASIC_INFO(&lc3_enc_cfg, info);
ret = audio_enc_set_subcfg(cfg, &lc3_enc_cfg, sizeof(esp_lc3_enc_config_t));
break;
}
case ESP_AUDIO_TYPE_SBC: {
esp_sbc_enc_config_t sbc_enc_cfg = ESP_SBC_STD_ENC_CONFIG_DEFAULT();
sbc_enc_cfg.sample_rate = info->sample_rates;
sbc_enc_cfg.bits_per_sample = info->bits;
if (info->channels == 1) {
sbc_enc_cfg.ch_mode = ESP_SBC_CH_MODE_MONO;
} else if (info->channels == 2) {
sbc_enc_cfg.ch_mode = ESP_SBC_CH_MODE_DUAL;
} else {
sbc_enc_cfg.ch_mode = ESP_SBC_CH_MODE_INVALID;
}
ret = audio_enc_set_subcfg(cfg, &sbc_enc_cfg, sizeof(esp_sbc_enc_config_t));
break;
}
default:
ESP_LOGE(TAG, "Not support for encoder, %ld", info->format_id);
cfg->type = ESP_AUDIO_TYPE_UNSUPPORT;
return ESP_GMF_ERR_NOT_SUPPORT;
}
return ((ret == ESP_GMF_ERR_OK) ? (ESP_GMF_ERR_OK) : (ret));
}
static esp_gmf_job_err_t gmf_audio_enc_acquire_in(esp_gmf_audio_enc_t *audio_enc, esp_gmf_port_handle_t in_port, esp_gmf_payload_t **in_load)
{
bool needed_load = false;
esp_gmf_job_err_t job_ret = ESP_GMF_JOB_ERR_OK;
esp_gmf_cache_ready_for_load(audio_enc->cached_payload, &needed_load);
if (needed_load) {
esp_gmf_err_io_t load_ret = esp_gmf_port_acquire_in(in_port, &audio_enc->origin_in_load, ESP_GMF_ELEMENT_GET(audio_enc)->in_attr.data_size, in_port->wait_ticks);
ESP_GMF_PORT_ACQUIRE_IN_CHECK(TAG, load_ret, job_ret, return job_ret);
int cache_size = 0;
esp_gmf_cache_get_cached_size(audio_enc->cached_payload, &cache_size);
audio_enc->cur_pts = audio_enc->origin_in_load->pts - GMF_AUDIO_CALC_PTS(cache_size, audio_enc->parent.snd_info.sample_rates, audio_enc->parent.snd_info.channels, audio_enc->parent.snd_info.bits);
esp_gmf_cache_load(audio_enc->cached_payload, audio_enc->origin_in_load);
}
esp_gmf_err_t ret = esp_gmf_cache_acquire(audio_enc->cached_payload, ESP_GMF_ELEMENT_GET(audio_enc)->in_attr.data_size, in_load);
if (ret != ESP_GMF_ERR_OK) {
job_ret = ((ret == ESP_GMF_ERR_NOT_ENOUGH) ? (ESP_GMF_JOB_ERR_CONTINUE) : (ESP_GMF_JOB_ERR_FAIL));
}
return job_ret;
}
static esp_gmf_err_t esp_gmf_audio_enc_new(void *cfg, esp_gmf_obj_handle_t *handle)
{
return esp_gmf_audio_enc_init(cfg, (esp_gmf_element_handle_t *)handle);
}
static esp_gmf_job_err_t esp_gmf_audio_enc_open(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_audio_enc_t *enc = (esp_gmf_audio_enc_t *)self;
esp_audio_enc_config_t *enc_cfg = (esp_audio_enc_config_t *)OBJ_GET_CFG(enc);
ESP_GMF_CHECK(TAG, enc_cfg, {return ESP_GMF_JOB_ERR_FAIL;}, "There is no encoder configuration");
esp_audio_err_t ret = esp_audio_enc_open(enc_cfg, &enc->audio_enc_hd);
ESP_GMF_CHECK(TAG, enc->audio_enc_hd, {return ESP_GMF_JOB_ERR_FAIL;}, "Failed to create audio encoder handle");
if (esp_audio_enc_get_frame_size(enc->audio_enc_hd, &ESP_GMF_ELEMENT_GET(enc)->in_attr.data_size, &ESP_GMF_ELEMENT_GET(enc)->out_attr.data_size) != ESP_AUDIO_ERR_OK) {
ESP_LOGE(TAG, "Failed to obtain frame size, ret: %d", ret);
return ESP_GMF_JOB_ERR_FAIL;
}
esp_gmf_port_enable_payload_share(ESP_GMF_ELEMENT_GET(self)->in, false);
esp_gmf_cache_new(ESP_GMF_ELEMENT_GET(enc)->in_attr.data_size, &enc->cached_payload);
ESP_GMF_CHECK(TAG, enc->cached_payload, {return ESP_GMF_JOB_ERR_FAIL;}, "Failed to new a cached payload on open");
esp_audio_enc_info_t enc_info = {0};
esp_audio_enc_get_info(enc->audio_enc_hd, &enc_info);
GMF_AUDIO_UPDATE_SND_INFO(self, enc_info.sample_rate, enc_info.bits_per_sample, enc_info.channel);
ESP_LOGI(TAG, "Open, type:%s, acquire in frame: %d, out frame: %d", esp_audio_codec_get_name(enc_cfg->type), ESP_GMF_ELEMENT_GET(enc)->in_attr.data_size, ESP_GMF_ELEMENT_GET(enc)->out_attr.data_size);
return ESP_GMF_JOB_ERR_OK;
}
static esp_gmf_job_err_t esp_gmf_audio_enc_process(esp_gmf_element_handle_t self, void *para)
{
// Get audio encoder instance and initialize variables
esp_gmf_audio_enc_t *audio_enc = (esp_gmf_audio_enc_t *)self;
esp_gmf_job_err_t out_len = ESP_GMF_JOB_ERR_OK;
esp_audio_err_t ret = ESP_AUDIO_ERR_OK;
esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in;
esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out;
esp_gmf_payload_t *out_load = NULL;
esp_audio_enc_in_frame_t enc_in_frame = {0};
esp_audio_enc_out_frame_t enc_out_frame = {0};
esp_gmf_err_io_t load_ret = 0;
bool needed_load = false;
esp_gmf_payload_t *in_load = NULL;
out_len = gmf_audio_enc_acquire_in(audio_enc, in_port, &in_load);
if (out_len != ESP_GMF_JOB_ERR_OK) {
goto __audio_enc_release;
}
ESP_LOGD(TAG, "Acq cache, buf:%p, vld:%d, len:%d, done:%d", in_load->buf, in_load->valid_size, in_load->buf_length, in_load->is_done);
// Acquire output buffer for encoded data and verify output buffer size
load_ret = esp_gmf_port_acquire_out(out_port, &out_load, ESP_GMF_ELEMENT_GET(audio_enc)->out_attr.data_size, ESP_GMF_MAX_DELAY);
ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, out_len, {goto __audio_enc_release;});
if (out_load->buf_length < (ESP_GMF_ELEMENT_GET(audio_enc)->out_attr.data_size)) {
ESP_LOGE(TAG, "The out payload valid size(%d) is smaller than wanted size(%d)",
out_load->buf_length, (ESP_GMF_ELEMENT_GET(audio_enc)->out_attr.data_size));
out_len = ESP_GMF_JOB_ERR_FAIL;
goto __audio_enc_release;
}
// Check if have enough input data for processing
if (in_load->valid_size != ESP_GMF_ELEMENT_GET(audio_enc)->in_attr.data_size) {
if (in_load->is_done == true) {
out_len = ESP_GMF_JOB_ERR_DONE;
out_load->valid_size = 0;
out_load->is_done = in_load->is_done;
ESP_LOGD(TAG, "Return done, line:%d", __LINE__);
goto __audio_enc_release;
} else {
out_len = ESP_GMF_JOB_ERR_CONTINUE;
ESP_LOGD(TAG, "Return Continue, line:%d", __LINE__);
goto __audio_enc_release;
}
}
// Perform audio encoding with mutex protection
enc_in_frame.buffer = in_load->buf;
enc_in_frame.len = in_load->valid_size;
enc_out_frame.buffer = out_load->buf;
enc_out_frame.len = ESP_GMF_ELEMENT_GET(audio_enc)->out_attr.data_size;
esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)self)->lock);
ret = esp_audio_enc_process(audio_enc->audio_enc_hd, &enc_in_frame, &enc_out_frame);
esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)self)->lock);
ESP_GMF_RET_ON_ERROR(TAG, ret, {out_len = ESP_GMF_JOB_ERR_FAIL; goto __audio_enc_release;}, "Audio encoder process error %d", ret);
out_load->valid_size = enc_out_frame.encoded_bytes;
out_load->is_done = in_load->is_done;
out_load->pts = audio_enc->cur_pts;
audio_enc->cur_pts += GMF_AUDIO_CALC_PTS(enc_in_frame.len, audio_enc->parent.snd_info.sample_rates, audio_enc->parent.snd_info.channels, audio_enc->parent.snd_info.bits);
// Handle end of stream
if (in_load->is_done) {
ESP_LOGW(TAG, "Got done, out size: %d", out_load->valid_size);
out_len = ESP_GMF_JOB_ERR_DONE;
}
// Check if need to truncate
esp_gmf_cache_ready_for_load(audio_enc->cached_payload, &needed_load);
if (needed_load == false) {
out_len = ESP_GMF_JOB_ERR_TRUNCATE;
int cached_size = 0;
esp_gmf_cache_get_cached_size(audio_enc->cached_payload, &cached_size);
ESP_LOGD(TAG, "Return TRUNCATE, reminder in size: %d", cached_size);
}
__audio_enc_release:
// Cleanup resources
esp_gmf_cache_release(audio_enc->cached_payload, in_load);
if (out_load != NULL) {
load_ret = esp_gmf_port_release_out(out_port, out_load, out_port->wait_ticks);
if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) {
ESP_LOGE(TAG, "OUT port release error, ret:%d", load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
}
}
if ((audio_enc->origin_in_load != NULL) && (out_len != ESP_GMF_JOB_ERR_TRUNCATE)) {
load_ret = esp_gmf_port_release_in(in_port, audio_enc->origin_in_load, in_port->wait_ticks);
if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) {
ESP_LOGE(TAG, "IN port release error, ret:%d", load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
}
audio_enc->origin_in_load = NULL;
}
return out_len;
}
static esp_gmf_job_err_t esp_gmf_audio_enc_close(esp_gmf_element_handle_t self, void *para)
{
ESP_LOGD(TAG, "Closed, %p", self);
esp_gmf_audio_enc_t *enc = (esp_gmf_audio_enc_t *)self;
if (enc->cached_payload) {
esp_gmf_cache_delete(enc->cached_payload);
enc->cached_payload = NULL;
}
if (enc->audio_enc_hd != NULL) {
esp_audio_enc_close(enc->audio_enc_hd);
enc->audio_enc_hd = NULL;
}
return ESP_GMF_JOB_ERR_OK;
}
static esp_gmf_err_t audio_enc_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx)
{
ESP_GMF_NULL_CHECK(TAG, ctx, { return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, evt, { return ESP_GMF_ERR_INVALID_ARG;});
if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO)
|| (evt->sub != ESP_GMF_INFO_SOUND)
|| (evt->payload == NULL)) {
return ESP_GMF_ERR_OK;
}
esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx;
esp_gmf_element_handle_t el = evt->from;
esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE;
esp_gmf_element_get_state(self, &state);
if (state < ESP_GMF_EVENT_STATE_OPENING) {
esp_audio_enc_config_t *enc_cfg = (esp_audio_enc_config_t *)OBJ_GET_CFG(self);
ESP_GMF_NULL_CHECK(TAG, enc_cfg, return ESP_GMF_ERR_FAIL);
esp_gmf_info_sound_t *info = (esp_gmf_info_sound_t *)evt->payload;
audio_enc_change_audio_info(enc_cfg, info);
ESP_LOGD(TAG, "RECV info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, rate: %d, ch: %d, bits: %d",
OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type,
esp_gmf_event_get_state_str(state), info->sample_rates, info->channels, info->bits);
if (state == ESP_GMF_EVENT_STATE_NONE) {
esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED);
}
}
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t esp_gmf_audio_enc_destroy(esp_gmf_element_handle_t self)
{
ESP_LOGD(TAG, "Destroyed, %p", self);
esp_gmf_audio_enc_t *enc = (esp_gmf_audio_enc_t *)self;
free_esp_gmf_audio_enc_cfg(OBJ_GET_CFG(self));
esp_gmf_audio_el_deinit(self);
esp_gmf_oal_free(enc);
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t _load_enc_caps_func(esp_gmf_element_handle_t handle)
{
esp_gmf_cap_t *caps = NULL;
esp_gmf_cap_t dec_caps = {0};
dec_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_ENCODER;
dec_caps.attr_fun = NULL;
int ret = esp_gmf_cap_append(&caps, &dec_caps);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to create capability");
esp_gmf_element_t *el = (esp_gmf_element_t *)handle;
el->caps = caps;
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t _load_enc_methods_func(esp_gmf_element_handle_t handle)
{
esp_gmf_method_t *method = NULL;
esp_gmf_args_desc_t *set_args = NULL;
esp_gmf_args_desc_t *get_args = NULL;
esp_gmf_err_t ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(ENCODER, SET_BITRATE, BITRATE),
ESP_GMF_ARGS_TYPE_INT32, sizeof(uint32_t), 0);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append bitrate argument");
ret = esp_gmf_method_append(&method, AMETHOD(ENCODER, SET_BITRATE), __audio_enc_set_bitrate, set_args);
ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to register %s method", AMETHOD(ENCODER, SET_BITRATE));
ret = esp_gmf_args_desc_copy(set_args, &get_args);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to copy argument");
ret = esp_gmf_method_append(&method, AMETHOD(ENCODER, GET_BITRATE), __audio_enc_get_bitrate, get_args);
ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to register %s method", AMETHOD(ENCODER, GET_BITRATE));
get_args = NULL;
set_args = NULL;
ret = esp_gmf_args_desc_append(&get_args, AMETHOD_ARG(ENCODER, GET_FRAME_SZ, INSIZE),
ESP_GMF_ARGS_TYPE_INT32, sizeof(uint32_t), 0);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append insize argument");
ret = esp_gmf_args_desc_append(&get_args, AMETHOD_ARG(ENCODER, GET_FRAME_SZ, OUTSIZE), ESP_GMF_ARGS_TYPE_INT32,
sizeof(uint32_t), sizeof(uint32_t));
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append outsize argument");
ret = esp_gmf_method_append(&method, AMETHOD(ENCODER, GET_FRAME_SZ), __audio_enc_get_frame_size, get_args);
ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to register %s method", AMETHOD(ENCODER, GET_FRAME_SZ));
set_args = NULL;
esp_gmf_args_desc_t *sndinfo_args = NULL;
ret = esp_gmf_args_desc_append(&sndinfo_args, AMETHOD_ARG(ENCODER, RECONFIG_BY_SND_INFO, INFO_TYPE), ESP_GMF_ARGS_TYPE_UINT32,
sizeof(uint32_t), offsetof(esp_gmf_info_sound_t, format_id));
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append type argument");
ret = esp_gmf_args_desc_append(&sndinfo_args, AMETHOD_ARG(ENCODER, RECONFIG_BY_SND_INFO, INFO_SAMPLERATE), ESP_GMF_ARGS_TYPE_INT32,
sizeof(int32_t), offsetof(esp_gmf_info_sound_t, sample_rates));
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append sample_rates argument");
ret = esp_gmf_args_desc_append(&sndinfo_args, AMETHOD_ARG(ENCODER, RECONFIG_BY_SND_INFO, INFO_BITRATE), ESP_GMF_ARGS_TYPE_INT32,
sizeof(int32_t), offsetof(esp_gmf_info_sound_t, bitrate));
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append bitrate argument");
ret = esp_gmf_args_desc_append(&sndinfo_args, AMETHOD_ARG(ENCODER, RECONFIG_BY_SND_INFO, INFO_CHANNEL), ESP_GMF_ARGS_TYPE_INT8,
sizeof(int8_t), 12);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append channels argument");
ret = esp_gmf_args_desc_append(&sndinfo_args, AMETHOD_ARG(ENCODER, RECONFIG_BY_SND_INFO, INFO_BITS), ESP_GMF_ARGS_TYPE_INT8,
sizeof(int8_t), 13);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append bits argument");
ret = esp_gmf_args_desc_append_array(&set_args, AMETHOD_ARG(ENCODER, RECONFIG_BY_SND_INFO, INFO), sndinfo_args,
sizeof(esp_gmf_info_sound_t), 0);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append sound info argument");
ret = esp_gmf_method_append(&method, AMETHOD(ENCODER, RECONFIG_BY_SND_INFO), __audio_enc_reconfig_by_sound_info, set_args);
ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to register %s method", AMETHOD(ENCODER, RECONFIG_BY_SND_INFO));
set_args = NULL;
esp_gmf_args_desc_t *reconfig_args = NULL;
ret = esp_gmf_args_desc_append(&reconfig_args, AMETHOD_ARG(ENCODER, RECONFIG, CFG_TYPE), ESP_GMF_ARGS_TYPE_INT32,
sizeof(int32_t), offsetof(esp_audio_enc_config_t, type));
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append type argument");
ret = esp_gmf_args_desc_append(&reconfig_args, AMETHOD_ARG(ENCODER, RECONFIG, CFG_SUBCFGPTR), ESP_GMF_ARGS_TYPE_INT32,
sizeof(int32_t), offsetof(esp_audio_enc_config_t, cfg));
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append cfg argument");
ret = esp_gmf_args_desc_append(&reconfig_args, AMETHOD_ARG(ENCODER, RECONFIG, CFG_SUBCFGSZ), ESP_GMF_ARGS_TYPE_UINT32,
sizeof(uint32_t), offsetof(esp_audio_enc_config_t, cfg_sz));
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append cfg_sz argument");
ret = esp_gmf_args_desc_append_array(&set_args, AMETHOD_ARG(ENCODER, RECONFIG, CFG), reconfig_args,
sizeof(esp_audio_enc_config_t), 0);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append argument");
ret = esp_gmf_method_append(&method, AMETHOD(ENCODER, RECONFIG), __audio_enc_reconfig, set_args);
ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to register %s method", AMETHOD(ENCODER, RECONFIG));
esp_gmf_element_t *el = (esp_gmf_element_t *)handle;
el->method = method;
return ESP_GMF_ERR_OK;
}
esp_gmf_err_t esp_gmf_audio_enc_get_frame_size(esp_gmf_element_handle_t handle, uint32_t *in_size, uint32_t *out_size)
{
ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG);
esp_audio_enc_config_t *cfg = (esp_audio_enc_config_t *)OBJ_GET_CFG(handle);
ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_ERR_FAIL);
esp_gmf_audio_enc_t *enc = (esp_gmf_audio_enc_t *)handle;
esp_audio_err_t ret = -1;
if (enc->audio_enc_hd == NULL) {
esp_audio_enc_frame_info_t frame_info = {0};
ret = esp_audio_enc_get_frame_info_by_cfg(cfg, &frame_info);
if (ret != ESP_AUDIO_ERR_OK) {
return ESP_GMF_ERR_FAIL;
}
*in_size = (uint32_t)frame_info.in_frame_size;
*out_size = (uint32_t)frame_info.out_frame_size;
} else {
ret = esp_audio_enc_get_frame_size(enc->audio_enc_hd, (int *)in_size, (int *)out_size);
}
return ((ret == ESP_AUDIO_ERR_OK) ? (ESP_GMF_ERR_OK) : (ESP_GMF_ERR_FAIL));
}
esp_gmf_err_t esp_gmf_audio_enc_set_bitrate(esp_gmf_element_handle_t handle, uint32_t bitrate)
{
ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG);
esp_gmf_audio_enc_t *enc = (esp_gmf_audio_enc_t *)handle;
if (enc->audio_enc_hd) {
esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)handle)->lock);
esp_audio_err_t ret = esp_audio_enc_set_bitrate(enc->audio_enc_hd, (int)bitrate);
esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)handle)->lock);
return ((ret == ESP_AUDIO_ERR_OK) ? (ESP_GMF_ERR_OK) : (ESP_GMF_ERR_FAIL));
}
enc->bitrate = bitrate;
return ESP_GMF_JOB_ERR_OK;
}
esp_gmf_err_t esp_gmf_audio_enc_get_bitrate(esp_gmf_element_handle_t handle, uint32_t *bitrate)
{
ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG);
esp_gmf_audio_enc_t *enc = (esp_gmf_audio_enc_t *)handle;
if (enc->audio_enc_hd) {
esp_audio_enc_info_t enc_info = {0};
esp_audio_err_t ret = esp_audio_enc_get_info(enc->audio_enc_hd, &enc_info);
*bitrate = enc_info.bitrate;
return ((ret == ESP_AUDIO_ERR_OK) ? (ESP_GMF_ERR_OK) : (ESP_GMF_ERR_FAIL));
}
*bitrate = enc->bitrate;
return ESP_GMF_JOB_ERR_OK;
}
esp_gmf_err_t esp_gmf_audio_enc_reconfig(esp_gmf_element_handle_t handle, esp_audio_enc_config_t *config)
{
ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG);
ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_INVALID_ARG);
esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE;
esp_gmf_element_get_state(handle, &state);
if (state < ESP_GMF_EVENT_STATE_OPENING) {
esp_audio_enc_config_t *new_config = NULL;
esp_gmf_err_t ret = dupl_esp_gmf_audio_enc_cfg(config, &new_config);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to duplicate config");
free_esp_gmf_audio_enc_cfg(OBJ_GET_CFG(handle));
esp_gmf_obj_set_config(handle, new_config, sizeof(esp_audio_enc_config_t));
return ESP_GMF_ERR_OK;
} else {
ESP_LOGE(TAG, "Failed to reconfig encoder due to invalid state: %s", esp_gmf_event_get_state_str(state));
return ESP_GMF_ERR_FAIL;
}
}
esp_gmf_err_t esp_gmf_audio_enc_reconfig_by_sound_info(esp_gmf_element_handle_t handle, esp_gmf_info_sound_t *info)
{
ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG);
esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE;
esp_gmf_element_get_state(handle, &state);
if (state < ESP_GMF_EVENT_STATE_OPENING) {
esp_gmf_err_t ret = audio_enc_reconfig_enc_by_sound_info(handle, info);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to reconfig encoder by sound information");
return ESP_GMF_ERR_OK;
} else {
ESP_LOGE(TAG, "Failed to reconfig encoder due to invalid state: %s", esp_gmf_event_get_state_str(state));
return ESP_GMF_ERR_FAIL;
}
}
esp_gmf_err_t esp_gmf_audio_enc_init(esp_audio_enc_config_t *config, esp_gmf_element_handle_t *handle)
{
ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;});
*handle = NULL;
esp_gmf_err_t ret = ESP_GMF_ERR_OK;
esp_gmf_audio_enc_t *audio_enc = esp_gmf_oal_calloc(1, sizeof(esp_gmf_audio_enc_t));
ESP_GMF_MEM_VERIFY(TAG, audio_enc, {return ESP_GMF_ERR_MEMORY_LACK;}, "audio encoder", sizeof(esp_gmf_audio_enc_t));
esp_gmf_obj_t *obj = (esp_gmf_obj_t *)audio_enc;
obj->new_obj = esp_gmf_audio_enc_new;
obj->del_obj = esp_gmf_audio_enc_destroy;
esp_audio_enc_config_t *cfg = NULL;
if (config) {
dupl_esp_gmf_audio_enc_cfg(config, &cfg);
} else {
esp_audio_enc_config_t dcfg = DEFAULT_ESP_GMF_AUDIO_ENC_CONFIG();
dupl_esp_gmf_audio_enc_cfg(&dcfg, &cfg);
}
ESP_GMF_CHECK(TAG, cfg, ret = ESP_GMF_ERR_MEMORY_LACK; goto ES_ENC_FAIL;, "Failed to allocate audio encoder configuration");
esp_gmf_obj_set_config(obj, cfg, sizeof(esp_audio_enc_config_t));
ret = esp_gmf_obj_set_tag(obj, "aud_enc");
ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto ES_ENC_FAIL, "Failed to set obj tag");
esp_gmf_element_cfg_t el_cfg = {0};
ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0,
ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT);
ESP_GMF_ELEMENT_OUT_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0,
ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT);
el_cfg.dependency = true;
ret = esp_gmf_audio_el_init(audio_enc, &el_cfg);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto ES_ENC_FAIL, "Failed to initialize audio encoder element");
audio_enc->parent.base.ops.open = esp_gmf_audio_enc_open;
audio_enc->parent.base.ops.process = esp_gmf_audio_enc_process;
audio_enc->parent.base.ops.close = esp_gmf_audio_enc_close;
audio_enc->parent.base.ops.event_receiver = audio_enc_received_event_handler;
audio_enc->parent.base.ops.load_caps = _load_enc_caps_func;
audio_enc->parent.base.ops.load_methods = _load_enc_methods_func;
*handle = obj;
ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj);
return ret;
ES_ENC_FAIL:
esp_gmf_audio_enc_destroy(obj);
return ret;
}
@@ -0,0 +1,60 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#include <string.h>
#include "esp_log.h"
#include "esp_gmf_audio_helper.h"
#include "esp_fourcc.h"
static const char *TAG = "ESP_GMF_AUDIO_HELPER";
esp_gmf_err_t esp_gmf_audio_helper_get_audio_type_by_uri(const char *uri, uint32_t *format_id)
{
const char *ext = strrchr(uri, '.');
if (ext == NULL) {
return ESP_GMF_ERR_NOT_SUPPORT;
}
ext++;
if (strncasecmp(ext, "aac", 3) == 0) {
*format_id = ESP_FOURCC_AAC;
} else if (strncasecmp(ext, "g711a", 5) == 0) {
*format_id = ESP_FOURCC_ALAW;
} else if (strncasecmp(ext, "g711u", 5) == 0) {
*format_id = ESP_FOURCC_ULAW;
} else if (strncasecmp(ext, "amr", 3) == 0) {
*format_id = ESP_FOURCC_AMRNB;
} else if (strncasecmp(ext, "awb", 3) == 0) {
*format_id = ESP_FOURCC_AMRWB;
} else if (strncasecmp(ext, "alac", 4) == 0) {
*format_id = ESP_FOURCC_ALAC;
} else if (strncasecmp(ext, "pcm", 3) == 0) {
*format_id = ESP_FOURCC_PCM;
} else if (strncasecmp(ext, "opus", 4) == 0) {
*format_id = ESP_FOURCC_OPUS;
} else if (strncasecmp(ext, "adpcm", 5) == 0) {
*format_id = ESP_FOURCC_ADPCM;
} else if (strncasecmp(ext, "sbc", 3) == 0) {
*format_id = ESP_FOURCC_SBC;
} else if (strncasecmp(ext, "lc3", 3) == 0) {
*format_id = ESP_FOURCC_LC3;
} else if (strncasecmp(ext, "mp3", 3) == 0) {
*format_id = ESP_FOURCC_MP3;
} else if (strncasecmp(ext, "m4a", 3) == 0) {
*format_id = ESP_FOURCC_M4A;
} else if (strncasecmp(ext, "wav", 3) == 0) {
*format_id = ESP_FOURCC_WAV;
} else if (strncasecmp(ext, "ts", 2) == 0) {
*format_id = ESP_FOURCC_M2TS;
} else if (strncasecmp(ext, "flac", 4) == 0) {
*format_id = ESP_FOURCC_FLAC;
} else {
*format_id = 0;
ESP_LOGE(TAG, "Not support audio codec type, %s", uri);
return ESP_GMF_ERR_NOT_SUPPORT;
}
return ESP_GMF_ERR_OK;
}
@@ -0,0 +1,102 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#include "esp_gmf_method_helper.h"
#include "esp_gmf_audio_param.h"
#include "esp_gmf_audio_methods_def.h"
#define PREPARE_AMETHOD_SETTING() \
esp_gmf_method_exec_ctx_t exec_ctx = {}; \
const esp_gmf_method_t* method_head = NULL; \
esp_gmf_element_get_method(self, &method_head); \
esp_gmf_err_t ret = esp_gmf_method_prepare_exec_ctx(method_head, method_name, &exec_ctx); \
if (ret != ESP_GMF_ERR_OK) { \
return ret; \
}
#define SET_AMETHOD_ARG(arg_name, value) \
esp_gmf_args_set_value(exec_ctx.method->args_desc, arg_name, exec_ctx.exec_buf, (uint8_t*) &value, sizeof(value));
#define RELEASE_AMETHOD_SETTING() \
ret = exec_ctx.method->func(self, exec_ctx.method->args_desc, exec_ctx.exec_buf, exec_ctx.buf_size); \
esp_gmf_method_release_exec_ctx(&exec_ctx); \
return ret;
esp_gmf_err_t esp_gmf_audio_param_set_dest_rate(esp_gmf_element_handle_t self, uint32_t dest_rate)
{
const char *method_name = AMETHOD(RATE_CVT, SET_DEST_RATE);
PREPARE_AMETHOD_SETTING();
SET_AMETHOD_ARG(AMETHOD_ARG(RATE_CVT, SET_DEST_RATE, RATE), dest_rate);
RELEASE_AMETHOD_SETTING();
}
esp_gmf_err_t esp_gmf_audio_param_set_dest_bits(esp_gmf_element_handle_t self, uint8_t dest_bits)
{
const char *method_name = AMETHOD(BIT_CVT, SET_DEST_BITS);
PREPARE_AMETHOD_SETTING();
SET_AMETHOD_ARG(AMETHOD_ARG(BIT_CVT, SET_DEST_BITS, BITS), dest_bits);
RELEASE_AMETHOD_SETTING();
}
esp_gmf_err_t esp_gmf_audio_param_set_dest_ch(esp_gmf_element_handle_t self, uint8_t dest_ch)
{
const char *method_name = AMETHOD(CH_CVT, SET_DEST_CH);
PREPARE_AMETHOD_SETTING();
SET_AMETHOD_ARG(AMETHOD_ARG(CH_CVT, SET_DEST_CH, CH), dest_ch);
RELEASE_AMETHOD_SETTING();
}
esp_gmf_err_t esp_gmf_audio_param_set_speed(esp_gmf_element_handle_t self, float speed)
{
const char *method_name = AMETHOD(SONIC, SET_SPEED);
PREPARE_AMETHOD_SETTING();
SET_AMETHOD_ARG(AMETHOD_ARG(SONIC, SET_SPEED, SPEED), speed);
RELEASE_AMETHOD_SETTING();
}
esp_gmf_err_t esp_gmf_audio_param_set_pitch(esp_gmf_element_handle_t self, float pitch)
{
const char *method_name = AMETHOD(SONIC, SET_PITCH);
PREPARE_AMETHOD_SETTING();
SET_AMETHOD_ARG(AMETHOD_ARG(SONIC, SET_PITCH, PITCH), pitch);
RELEASE_AMETHOD_SETTING();
}
esp_gmf_err_t esp_gmf_audio_param_set_alc_channel_gain(esp_gmf_element_handle_t self, uint8_t ch_idx, float db)
{
const char *method_name = AMETHOD(ALC, SET_GAIN);
// ALC currently only support int8 db
int8_t gain = (int8_t) db;
PREPARE_AMETHOD_SETTING();
SET_AMETHOD_ARG(AMETHOD_ARG(ALC, SET_GAIN, IDX), ch_idx);
SET_AMETHOD_ARG(AMETHOD_ARG(ALC, SET_GAIN, GAIN), gain);
RELEASE_AMETHOD_SETTING();
}
esp_gmf_err_t esp_gmf_audio_param_set_fade(esp_gmf_element_handle_t self, bool is_fade_in)
{
const char *method_name = AMETHOD(FADE, SET_MODE);
// Aligned with ESP_AE_FADE_MODE_FADE_IN = 1, ESP_AE_FADE_MODE_FADE_OUT = 2
int32_t mode = is_fade_in ? 1 : 2;
PREPARE_AMETHOD_SETTING();
SET_AMETHOD_ARG(AMETHOD_ARG(FADE, SET_MODE, MODE), mode);
RELEASE_AMETHOD_SETTING();
}
@@ -0,0 +1,280 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#include <string.h>
#include "esp_log.h"
#include "esp_gmf_node.h"
#include "esp_gmf_oal_mem.h"
#include "esp_gmf_oal_mutex.h"
#include "esp_gmf_bit_cvt.h"
#include "gmf_audio_common.h"
#include "esp_gmf_audio_methods_def.h"
#include "esp_gmf_cap.h"
#include "esp_gmf_caps_def.h"
#include "esp_gmf_audio_element.h"
/**
* @brief Audio bit conversion context in GMF
*/
typedef struct {
esp_gmf_audio_element_t parent; /*!< The GMF bit cvt handle */
esp_ae_bit_cvt_handle_t bit_hd; /*!< The audio effects bit cvt handle */
uint8_t in_bytes_per_sample; /*!< Source bytes number of per sampling point */
uint8_t out_bytes_per_sample; /*!< Dest bytes number of per sampling point */
bool need_reopen : 1; /*!< Whether need to reopen.
True: Execute the close function first, then execute the open function
False: Do nothing */
bool bypass : 1; /*!< Whether bypass. True: need bypass. False: needn't bypass */
} esp_gmf_bit_cvt_t;
static const char *TAG = "ESP_GMF_BIT_CVT";
static esp_gmf_err_t __set_dest_bits(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc,
uint8_t *buf, int buf_len)
{
ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;});
uint8_t dest_bits = (uint8_t)(*buf);
return esp_gmf_bit_cvt_set_dest_bits(handle, dest_bits);
}
static esp_gmf_err_t esp_gmf_bit_cvt_new(void *cfg, esp_gmf_obj_handle_t *handle)
{
return esp_gmf_bit_cvt_init(cfg, (esp_gmf_element_handle_t *)handle);
}
static esp_gmf_job_err_t esp_gmf_bit_cvt_open(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_bit_cvt_t *bit_cvt = (esp_gmf_bit_cvt_t *)self;
esp_ae_bit_cvt_cfg_t *bit_info = (esp_ae_bit_cvt_cfg_t *)OBJ_GET_CFG(self);
ESP_GMF_NULL_CHECK(TAG, bit_info, {return ESP_GMF_JOB_ERR_FAIL;});
esp_ae_bit_cvt_open(bit_info, &bit_cvt->bit_hd);
ESP_GMF_CHECK(TAG, bit_cvt->bit_hd, {return ESP_GMF_JOB_ERR_FAIL;}, "Failed to create bit conversion handle");
bit_cvt->in_bytes_per_sample = (bit_info->src_bits >> 3) * bit_info->channel;
bit_cvt->out_bytes_per_sample = (bit_info->dest_bits >> 3) * bit_info->channel;
GMF_AUDIO_UPDATE_SND_INFO(self, bit_info->sample_rate, bit_info->dest_bits, bit_info->channel);
ESP_LOGD(TAG, "Open, rate: %ld, channel: %d, src_bits: %d, dest_bits: %d",
bit_info->sample_rate, bit_info->channel, bit_info->src_bits, bit_info->dest_bits);
bit_cvt->need_reopen = false;
bit_cvt->bypass = (bit_info->src_bits == bit_info->dest_bits);
return ESP_GMF_JOB_ERR_OK;
}
static esp_gmf_job_err_t esp_gmf_bit_cvt_close(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_bit_cvt_t *bit_cvt = (esp_gmf_bit_cvt_t *)self;
ESP_LOGD(TAG, "Closed, %p", self);
if (bit_cvt->bit_hd != NULL) {
esp_ae_bit_cvt_close(bit_cvt->bit_hd);
bit_cvt->bit_hd = NULL;
}
return ESP_GMF_ERR_OK;
}
static esp_gmf_job_err_t esp_gmf_bit_cvt_process(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_bit_cvt_t *bit_cvt = (esp_gmf_bit_cvt_t *)self;
esp_gmf_job_err_t out_len = ESP_GMF_JOB_ERR_OK;
if (bit_cvt->need_reopen) {
esp_gmf_bit_cvt_close(self, NULL);
out_len = esp_gmf_bit_cvt_open(self, NULL);
if (out_len != ESP_GMF_JOB_ERR_OK) {
ESP_LOGE(TAG, "Bit conversion reopen failed");
return out_len;
}
}
esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in;
esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out;
esp_gmf_payload_t *in_load = NULL;
esp_gmf_payload_t *out_load = NULL;
int samples_num = ESP_GMF_ELEMENT_GET(bit_cvt)->in_attr.data_size / (bit_cvt->in_bytes_per_sample);
int bytes = samples_num * bit_cvt->in_bytes_per_sample;
esp_gmf_err_io_t load_ret = esp_gmf_port_acquire_in(in_port, &in_load, bytes, ESP_GMF_MAX_DELAY);
if (load_ret < ESP_GMF_IO_OK) {
ESP_LOGE(TAG, "ACQ IN, ret: %d", load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
goto __bit_release;
}
samples_num = in_load->valid_size / (bit_cvt->in_bytes_per_sample);
bytes = samples_num * bit_cvt->out_bytes_per_sample;
if ((bit_cvt->in_bytes_per_sample * samples_num != in_load->valid_size) || (load_ret < ESP_GMF_IO_OK)) {
ESP_LOGE(TAG, "Invalid in load size %d, ret %d", in_load->valid_size, load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
goto __bit_release;
}
if ((bit_cvt->bypass) && (in_port->is_shared == 1)) {
// This case bit conversion is do bypass
out_load = in_load;
}
load_ret = esp_gmf_port_acquire_out(out_port, &out_load, samples_num ? bytes : in_load->buf_length, ESP_GMF_MAX_DELAY);
ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, out_len, { goto __bit_release;});
if (samples_num) {
esp_ae_err_t ret = esp_ae_bit_cvt_process(bit_cvt->bit_hd, samples_num, (unsigned char *)in_load->buf, (unsigned char *)out_load->buf);
ESP_GMF_RET_ON_ERROR(TAG, ret, {out_len = ESP_GMF_JOB_ERR_FAIL; goto __bit_release;}, "Bit conversion process error, ret: %d", ret);
}
ESP_LOGV(TAG, "Samples: %d, IN-PLD: %p-%p-%d-%d-%d, OUT-PLD: %p-%p-%d-%d-%d",
samples_num, in_load, in_load->buf, in_load->valid_size, in_load->buf_length, in_load->is_done,
out_load, out_load->buf, out_load->valid_size, out_load->buf_length, out_load->is_done);
out_load->valid_size = samples_num * bit_cvt->out_bytes_per_sample;
out_load->pts = in_load->pts;
out_load->is_done = in_load->is_done;
if (out_load->valid_size > 0) {
esp_gmf_audio_el_update_file_pos((esp_gmf_element_handle_t)self, out_load->valid_size);
}
if (in_load->is_done) {
out_len = ESP_GMF_JOB_ERR_DONE;
ESP_LOGD(TAG, "Bit conversion is done, out len: %d", out_load->valid_size);
}
__bit_release:
// Release in and out port
if (out_load != NULL) {
load_ret = esp_gmf_port_release_out(out_port, out_load, ESP_GMF_MAX_DELAY);
if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) {
ESP_LOGE(TAG, "OUT port release error, ret:%d", load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
}
}
if (in_load != NULL) {
load_ret = esp_gmf_port_release_in(in_port, in_load, ESP_GMF_MAX_DELAY);
if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) {
ESP_LOGE(TAG, "IN port release error, ret:%d", load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
}
}
return out_len;
}
static esp_gmf_err_t bit_cvt_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx)
{
ESP_GMF_NULL_CHECK(TAG, ctx, {return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, evt, {return ESP_GMF_ERR_INVALID_ARG;});
if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO)
|| (evt->sub != ESP_GMF_INFO_SOUND)
|| (evt->payload == NULL)) {
return ESP_GMF_ERR_OK;
}
esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx;
esp_gmf_element_handle_t el = evt->from;
esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE;
esp_gmf_element_get_state(self, &state);
esp_gmf_info_sound_t *info = (esp_gmf_info_sound_t *)evt->payload;
esp_ae_bit_cvt_cfg_t *config = (esp_ae_bit_cvt_cfg_t *)OBJ_GET_CFG(self);
ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_FAIL);
esp_gmf_bit_cvt_t *bit_cvt = (esp_gmf_bit_cvt_t *)self;
bit_cvt->need_reopen = (config->sample_rate != info->sample_rates) || (info->channels != config->channel) || (config->src_bits != info->bits);
config->sample_rate = info->sample_rates;
config->channel = info->channels;
config->src_bits = info->bits;
ESP_LOGD(TAG, "RECV info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, rate: %d, ch: %d, bits: %d",
OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type,
esp_gmf_event_get_state_str(state), info->sample_rates, info->channels, info->bits);
if (state == ESP_GMF_EVENT_STATE_NONE) {
esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED);
}
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t esp_gmf_bit_cvt_destroy(esp_gmf_element_handle_t self)
{
esp_gmf_bit_cvt_t *bit_cvt = (esp_gmf_bit_cvt_t *)self;
ESP_LOGD(TAG, "Destroyed, %p", self);
void *cfg = OBJ_GET_CFG(self);
if (cfg) {
esp_gmf_oal_free(cfg);
}
esp_gmf_audio_el_deinit(self);
esp_gmf_oal_free(bit_cvt);
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t _load_bit_cvt_caps_func(esp_gmf_element_handle_t handle)
{
esp_gmf_cap_t *caps = NULL;
esp_gmf_cap_t dec_caps = {0};
dec_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_BIT_CONVERT;
dec_caps.attr_fun = NULL;
int ret = esp_gmf_cap_append(&caps, &dec_caps);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to create capability");
esp_gmf_element_t *el = (esp_gmf_element_t *)handle;
el->caps = caps;
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t _load_bit_cvt_methods_func(esp_gmf_element_handle_t handle)
{
esp_gmf_method_t *method = NULL;
esp_gmf_args_desc_t *set_args = NULL;
esp_gmf_err_t ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(BIT_CVT, SET_DEST_BITS, BITS),
ESP_GMF_ARGS_TYPE_UINT8, sizeof(uint8_t), 0);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append argument");
ret = esp_gmf_method_append(&method, AMETHOD(BIT_CVT, SET_DEST_BITS), __set_dest_bits, set_args);
ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(BIT_CVT, SET_DEST_BITS));
esp_gmf_element_t *el = (esp_gmf_element_t *)handle;
el->method = method;
return ESP_GMF_ERR_OK;
}
esp_gmf_err_t esp_gmf_bit_cvt_set_dest_bits(esp_gmf_element_handle_t handle, uint8_t dest_bits)
{
ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;});
esp_ae_bit_cvt_cfg_t *cfg = (esp_ae_bit_cvt_cfg_t *)OBJ_GET_CFG(handle);
if (cfg) {
if (cfg->dest_bits == dest_bits) {
return ESP_GMF_ERR_OK;
}
cfg->dest_bits = dest_bits;
esp_gmf_bit_cvt_t *bit_cvt = (esp_gmf_bit_cvt_t *)handle;
bit_cvt->need_reopen = true;
return ESP_GMF_ERR_OK;
}
ESP_LOGE(TAG, "Failed to set dest bits, cfg is NULL");
return ESP_GMF_ERR_FAIL;
}
esp_gmf_err_t esp_gmf_bit_cvt_init(esp_ae_bit_cvt_cfg_t *config, esp_gmf_element_handle_t *handle)
{
ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;});
*handle = NULL;
esp_gmf_err_t ret = ESP_GMF_ERR_OK;
esp_gmf_bit_cvt_t *bit_cvt = esp_gmf_oal_calloc(1, sizeof(esp_gmf_bit_cvt_t));
ESP_GMF_MEM_VERIFY(TAG, bit_cvt, {return ESP_GMF_ERR_MEMORY_LACK;}, "bit conversion", sizeof(esp_gmf_bit_cvt_t));
esp_gmf_obj_t *obj = (esp_gmf_obj_t *)bit_cvt;
obj->new_obj = esp_gmf_bit_cvt_new;
obj->del_obj = esp_gmf_bit_cvt_destroy;
esp_ae_bit_cvt_cfg_t *cfg = esp_gmf_oal_calloc(1, sizeof(esp_ae_bit_cvt_cfg_t));
ESP_GMF_MEM_VERIFY(TAG, cfg, {ret = ESP_GMF_ERR_MEMORY_LACK; goto BIT_CVT_INIT_FAIL;}, "bit conversion configuration", sizeof(esp_ae_bit_cvt_cfg_t));
esp_gmf_obj_set_config(obj, cfg, sizeof(esp_ae_bit_cvt_cfg_t));
if (config) {
memcpy(cfg, config, sizeof(esp_ae_bit_cvt_cfg_t));
} else {
esp_ae_bit_cvt_cfg_t dcfg = DEFAULT_ESP_GMF_BIT_CVT_CONFIG();
memcpy(cfg, &dcfg, sizeof(esp_ae_bit_cvt_cfg_t));
}
ret = esp_gmf_obj_set_tag(obj, "aud_bit_cvt");
ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto BIT_CVT_INIT_FAIL, "Failed to set obj tag");
esp_gmf_element_cfg_t el_cfg = {0};
ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0,
ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT);
ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0,
ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT);
el_cfg.dependency = true;
ret = esp_gmf_audio_el_init(bit_cvt, &el_cfg);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto BIT_CVT_INIT_FAIL, "Failed to initialize bit conversion element");
ESP_GMF_ELEMENT_GET(bit_cvt)->ops.open = esp_gmf_bit_cvt_open;
ESP_GMF_ELEMENT_GET(bit_cvt)->ops.process = esp_gmf_bit_cvt_process;
ESP_GMF_ELEMENT_GET(bit_cvt)->ops.close = esp_gmf_bit_cvt_close;
ESP_GMF_ELEMENT_GET(bit_cvt)->ops.event_receiver = bit_cvt_received_event_handler;
ESP_GMF_ELEMENT_GET(bit_cvt)->ops.load_caps = _load_bit_cvt_caps_func;
ESP_GMF_ELEMENT_GET(bit_cvt)->ops.load_methods = _load_bit_cvt_methods_func;
*handle = obj;
ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj);
return ESP_GMF_ERR_OK;
BIT_CVT_INIT_FAIL:
esp_gmf_bit_cvt_destroy(obj);
return ret;
}
@@ -0,0 +1,304 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#include <string.h>
#include "esp_log.h"
#include "esp_gmf_node.h"
#include "esp_gmf_oal_mem.h"
#include "esp_gmf_oal_mutex.h"
#include "esp_gmf_ch_cvt.h"
#include "gmf_audio_common.h"
#include "esp_gmf_audio_methods_def.h"
#include "esp_gmf_cap.h"
#include "esp_gmf_caps_def.h"
#include "esp_gmf_audio_element.h"
/**
* @brief Audio channel conversion context in GMF
*/
typedef struct {
esp_gmf_audio_element_t parent; /*!< The GMF channel cvt handle */
esp_ae_ch_cvt_handle_t ch_hd; /*!< The audio effects channel cvt handle */
uint8_t in_bytes_per_sample; /*!< Source bytes number of per sampling point */
uint8_t out_bytes_per_sample; /*!< Dest bytes number of per sampling point */
bool need_reopen : 1; /*!< Whether need to reopen.
True: Execute the close function first, then execute the open function
False: Do nothing */
bool bypass : 1; /*!< Whether bypass. True: need bypass. False: needn't bypass */
} esp_gmf_ch_cvt_t;
static const char *TAG = "ESP_GMF_CH_CVT";
static inline esp_gmf_err_t dupl_esp_ae_ch_cvt_cfg(esp_ae_ch_cvt_cfg_t *config, esp_ae_ch_cvt_cfg_t **new_config)
{
void *sub_cfg = NULL;
*new_config = esp_gmf_oal_calloc(1, sizeof(*config));
ESP_GMF_MEM_VERIFY(TAG, *new_config, {return ESP_GMF_ERR_MEMORY_LACK;}, "channel conversion configuration", sizeof(*config));
memcpy(*new_config, config, sizeof(*config));
if (config->weight && (config->weight_len > 0)) {
sub_cfg = esp_gmf_oal_calloc(1, config->weight_len);
ESP_GMF_MEM_VERIFY(TAG, sub_cfg, {esp_gmf_oal_free(*new_config); return ESP_GMF_ERR_MEMORY_LACK;},
"weight array", (int)config->weight_len);
memcpy(sub_cfg, config->weight, config->weight_len);
(*new_config)->weight = sub_cfg;
}
return ESP_GMF_JOB_ERR_OK;
}
static inline void free_esp_ae_ch_cvt_cfg(esp_ae_ch_cvt_cfg_t *config)
{
if (config) {
if (config->weight) {
esp_gmf_oal_free(config->weight);
config->weight = NULL;
config->weight_len = 0;
}
esp_gmf_oal_free(config);
}
}
static esp_gmf_err_t __set_dest_ch(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc,
uint8_t *buf, int buf_len)
{
ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;});
uint8_t dest_ch = (uint8_t)(*buf);
return esp_gmf_ch_cvt_set_dest_channel(handle, dest_ch);
}
static esp_gmf_err_t esp_gmf_ch_cvt_new(void *cfg, esp_gmf_obj_handle_t *handle)
{
return esp_gmf_ch_cvt_init(cfg, (esp_gmf_element_handle_t *)handle);
}
static esp_gmf_job_err_t esp_gmf_ch_cvt_open(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_ch_cvt_t *ch_cvt = (esp_gmf_ch_cvt_t *)self;
esp_ae_ch_cvt_cfg_t *ch_info = (esp_ae_ch_cvt_cfg_t *)OBJ_GET_CFG(self);
ESP_GMF_NULL_CHECK(TAG, ch_info, {return ESP_GMF_JOB_ERR_FAIL;});
esp_ae_ch_cvt_open(ch_info, &ch_cvt->ch_hd);
ESP_GMF_CHECK(TAG, ch_cvt->ch_hd, {return ESP_GMF_JOB_ERR_FAIL;}, "Failed to create channel conversion handle");
ch_cvt->in_bytes_per_sample = (ch_info->bits_per_sample >> 3) * ch_info->src_ch;
ch_cvt->out_bytes_per_sample = (ch_info->bits_per_sample >> 3) * ch_info->dest_ch;
GMF_AUDIO_UPDATE_SND_INFO(self, ch_info->sample_rate, ch_info->bits_per_sample, ch_info->dest_ch);
ESP_LOGD(TAG, "Open, rate: %ld, bits: %d, src_channel: %d, dest_channel: %d",
ch_info->sample_rate, ch_info->bits_per_sample, ch_info->src_ch, ch_info->dest_ch);
ch_cvt->need_reopen = false;
ch_cvt->bypass = ch_info->src_ch == ch_info->dest_ch;
return ESP_GMF_JOB_ERR_OK;
}
static esp_gmf_job_err_t esp_gmf_ch_cvt_close(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_ch_cvt_t *ch_cvt = (esp_gmf_ch_cvt_t *)self;
ESP_LOGD(TAG, "Closed, %p", self);
if (ch_cvt->ch_hd != NULL) {
esp_ae_ch_cvt_close(ch_cvt->ch_hd);
ch_cvt->ch_hd = NULL;
}
return ESP_GMF_JOB_ERR_OK;
}
static esp_gmf_job_err_t esp_gmf_ch_cvt_process(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_ch_cvt_t *ch_cvt = (esp_gmf_ch_cvt_t *)self;
esp_gmf_job_err_t out_len = ESP_GMF_JOB_ERR_OK;
if (ch_cvt->need_reopen) {
esp_gmf_ch_cvt_close(self, NULL);
out_len = esp_gmf_ch_cvt_open(self, NULL);
if (out_len != ESP_GMF_JOB_ERR_OK) {
ESP_LOGE(TAG, "Channel conversion reopen failed");
return out_len;
}
}
esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in;
esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out;
esp_gmf_payload_t *in_load = NULL;
esp_gmf_payload_t *out_load = NULL;
int samples_num = ESP_GMF_ELEMENT_GET(ch_cvt)->in_attr.data_size / (ch_cvt->in_bytes_per_sample);
int bytes = samples_num * ch_cvt->in_bytes_per_sample;
esp_gmf_err_io_t load_ret = esp_gmf_port_acquire_in(in_port, &in_load, bytes, ESP_GMF_MAX_DELAY);
if (load_ret < ESP_GMF_IO_OK) {
ESP_LOGE(TAG, "ACQ IN, ret: %d", load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
goto __ch_release;
}
samples_num = in_load->valid_size / (ch_cvt->in_bytes_per_sample);
bytes = samples_num * ch_cvt->out_bytes_per_sample;
if ((ch_cvt->in_bytes_per_sample * samples_num != in_load->valid_size) || (load_ret < ESP_GMF_IO_OK)) {
ESP_LOGE(TAG, "Invalid in load size %d, ret %d", in_load->valid_size, load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
goto __ch_release;
}
if (ch_cvt->bypass && (in_port->is_shared == true)) {
// This case channel conversion is do bypass
out_load = in_load;
}
load_ret = esp_gmf_port_acquire_out(out_port, &out_load, samples_num ? bytes : in_load->buf_length, ESP_GMF_MAX_DELAY);
ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, out_len, { goto __ch_release;});
if (samples_num) {
esp_ae_err_t ret = esp_ae_ch_cvt_process(ch_cvt->ch_hd, samples_num, (unsigned char *)in_load->buf, (unsigned char *)out_load->buf);
ESP_GMF_RET_ON_ERROR(TAG, ret, {out_len = ESP_GMF_JOB_ERR_FAIL; goto __ch_release;}, "Channel conversion process error, ret: %d", ret);
}
out_load->valid_size = samples_num * ch_cvt->out_bytes_per_sample;
out_load->pts = in_load->pts;
out_load->is_done = in_load->is_done;
ESP_LOGV(TAG, "Samples: %d, IN-PLD: %p-%p-%d-%d-%d, OUT-PLD: %p-%p-%d-%d-%d",
samples_num, in_load, in_load->buf, in_load->valid_size, in_load->buf_length, in_load->is_done,
out_load, out_load->buf, out_load->valid_size, out_load->buf_length, out_load->is_done);
if (out_load->valid_size > 0) {
esp_gmf_audio_el_update_file_pos((esp_gmf_element_handle_t)self, out_load->valid_size);
}
if (in_load->is_done) {
out_len = ESP_GMF_JOB_ERR_DONE;
ESP_LOGD(TAG, "The channel cvt done, out len: %d", out_load->valid_size);
}
__ch_release:
// Release in and out port
if (out_load != NULL) {
load_ret = esp_gmf_port_release_out(out_port, out_load, ESP_GMF_MAX_DELAY);
if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) {
ESP_LOGE(TAG, "OUT port release error, ret:%d", load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
}
}
if (in_load != NULL) {
load_ret = esp_gmf_port_release_in(in_port, in_load, ESP_GMF_MAX_DELAY);
if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) {
ESP_LOGE(TAG, "IN port release error, ret:%d", load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
}
}
return out_len;
}
static esp_gmf_err_t ch_cvt_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx)
{
ESP_GMF_NULL_CHECK(TAG, ctx, {return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, evt, {return ESP_GMF_ERR_INVALID_ARG;});
if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO)
|| (evt->sub != ESP_GMF_INFO_SOUND)
|| (evt->payload == NULL)) {
return ESP_GMF_ERR_OK;
}
esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx;
esp_gmf_element_handle_t el = evt->from;
esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE;
esp_gmf_element_get_state(self, &state);
esp_gmf_info_sound_t *info = (esp_gmf_info_sound_t *)evt->payload;
esp_ae_ch_cvt_cfg_t *config = (esp_ae_ch_cvt_cfg_t *)OBJ_GET_CFG(self);
ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_FAIL);
esp_gmf_ch_cvt_t *ch_cvt = (esp_gmf_ch_cvt_t *)self;
ch_cvt->need_reopen = (config->sample_rate != info->sample_rates) || (info->channels != config->src_ch) || (config->bits_per_sample != info->bits);
config->sample_rate = info->sample_rates;
config->src_ch = info->channels;
config->bits_per_sample = info->bits;
ESP_LOGD(TAG, "RECV info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, rate: %d, ch: %d, bits: %d",
OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type,
esp_gmf_event_get_state_str(state), info->sample_rates, info->channels, info->bits);
if (state == ESP_GMF_EVENT_STATE_NONE) {
esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED);
}
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t esp_gmf_ch_cvt_destroy(esp_gmf_element_handle_t self)
{
esp_gmf_ch_cvt_t *ch_cvt = (esp_gmf_ch_cvt_t *)self;
ESP_LOGD(TAG, "Destroyed, %p", self);
free_esp_ae_ch_cvt_cfg(OBJ_GET_CFG(self));
esp_gmf_audio_el_deinit(self);
esp_gmf_oal_free(ch_cvt);
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t _load_channel_cvt_caps_func(esp_gmf_element_handle_t handle)
{
esp_gmf_cap_t *caps = NULL;
esp_gmf_cap_t dec_caps = {0};
dec_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_CHANNEL_CONVERT;
dec_caps.attr_fun = NULL;
int ret = esp_gmf_cap_append(&caps, &dec_caps);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to create capability");
esp_gmf_element_t *el = (esp_gmf_element_t *)handle;
el->caps = caps;
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t _load_channel_cvt_methods_func(esp_gmf_element_handle_t handle)
{
esp_gmf_method_t *method = NULL;
esp_gmf_args_desc_t *set_args = NULL;
esp_gmf_err_t ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(CH_CVT, SET_DEST_CH, CH), ESP_GMF_ARGS_TYPE_UINT8, sizeof(uint8_t), 0);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append argument");
ret = esp_gmf_method_append(&method, AMETHOD(CH_CVT, SET_DEST_CH), __set_dest_ch, set_args);
ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(CH_CVT, SET_DEST_CH));
esp_gmf_element_t *el = (esp_gmf_element_t *)handle;
el->method = method;
return ESP_GMF_ERR_OK;
}
esp_gmf_err_t esp_gmf_ch_cvt_set_dest_channel(esp_gmf_element_handle_t handle, uint8_t dest_ch)
{
ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;});
esp_ae_ch_cvt_cfg_t *cfg = (esp_ae_ch_cvt_cfg_t *)OBJ_GET_CFG(handle);
if (cfg) {
if (cfg->dest_ch == dest_ch) {
return ESP_GMF_ERR_OK;
}
cfg->dest_ch = dest_ch;
esp_gmf_ch_cvt_t *ch_cvt = (esp_gmf_ch_cvt_t *)handle;
ch_cvt->need_reopen = true;
return ESP_GMF_ERR_OK;
}
ESP_LOGE(TAG, "Failed to set dest channel, cfg is NULL");
return ESP_GMF_ERR_FAIL;
}
esp_gmf_err_t esp_gmf_ch_cvt_init(esp_ae_ch_cvt_cfg_t *config, esp_gmf_element_handle_t *handle)
{
ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;});
*handle = NULL;
esp_gmf_err_t ret = ESP_GMF_ERR_OK;
esp_gmf_ch_cvt_t *ch_cvt = esp_gmf_oal_calloc(1, sizeof(esp_gmf_ch_cvt_t));
ESP_GMF_MEM_VERIFY(TAG, ch_cvt, {return ESP_GMF_ERR_MEMORY_LACK;}, "channel conversion", sizeof(esp_gmf_ch_cvt_t));
esp_gmf_obj_t *obj = (esp_gmf_obj_t *)ch_cvt;
obj->new_obj = esp_gmf_ch_cvt_new;
obj->del_obj = esp_gmf_ch_cvt_destroy;
esp_ae_ch_cvt_cfg_t *cfg = NULL;
if (config) {
dupl_esp_ae_ch_cvt_cfg(config, &cfg);
} else {
esp_ae_ch_cvt_cfg_t dcfg = DEFAULT_ESP_GMF_CH_CVT_CONFIG();
dupl_esp_ae_ch_cvt_cfg(&dcfg, &cfg);
}
ESP_GMF_CHECK(TAG, cfg, {ret = ESP_GMF_ERR_MEMORY_LACK; goto CH_CVT_INIT_FAIL;}, "Failed to allocate channel conversion configuration");
esp_gmf_obj_set_config(obj, cfg, sizeof(esp_ae_ch_cvt_cfg_t));
ret = esp_gmf_obj_set_tag(obj, "aud_ch_cvt");
ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto CH_CVT_INIT_FAIL, "Failed to set obj tag");
esp_gmf_element_cfg_t el_cfg = {0};
ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0,
ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT);
ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0,
ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT);
el_cfg.dependency = true;
ret = esp_gmf_audio_el_init(ch_cvt, &el_cfg);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto CH_CVT_INIT_FAIL, "Failed to initialize channel conversion element");
ESP_GMF_ELEMENT_GET(ch_cvt)->ops.open = esp_gmf_ch_cvt_open;
ESP_GMF_ELEMENT_GET(ch_cvt)->ops.process = esp_gmf_ch_cvt_process;
ESP_GMF_ELEMENT_GET(ch_cvt)->ops.close = esp_gmf_ch_cvt_close;
ESP_GMF_ELEMENT_GET(ch_cvt)->ops.event_receiver = ch_cvt_received_event_handler;
ESP_GMF_ELEMENT_GET(ch_cvt)->ops.load_caps = _load_channel_cvt_caps_func;
ESP_GMF_ELEMENT_GET(ch_cvt)->ops.load_methods = _load_channel_cvt_methods_func;
*handle = obj;
ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj);
return ESP_GMF_ERR_OK;
CH_CVT_INIT_FAIL:
esp_gmf_ch_cvt_destroy(obj);
return ret;
}
@@ -0,0 +1,274 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#include <string.h>
#include "esp_log.h"
#include "esp_gmf_oal_mem.h"
#include "esp_gmf_oal_mutex.h"
#include "esp_gmf_node.h"
#include "esp_gmf_deinterleave.h"
#include "esp_ae_data_weaver.h"
#include "esp_heap_caps.h"
#include "gmf_audio_common.h"
#include "esp_gmf_cap.h"
#include "esp_gmf_caps_def.h"
#include "esp_gmf_audio_element.h"
#define ESP_GMF_PROCESS_SAMPLE (256)
/**
* @brief Audio deinterleave context in GMF
*/
typedef struct {
esp_gmf_audio_element_t parent; /*!< The GMF deinterleave handle */
uint8_t bytes_per_sample; /*!< Bytes number of per sampling point */
esp_gmf_payload_t *in_load; /*!< The input payload */
esp_gmf_payload_t **out_load; /*!< The array of output payload */
uint8_t **out_arr; /*!< The array of output buffer pointer */
uint8_t channel; /*!< The audio channel */
uint8_t bits_per_sample; /*!< Bits number of per sampling point */
bool need_reopen : 1; /*!< Whether need to reopen.
True: Execute the close function first, then execute the open function
False: Do nothing */
} esp_gmf_deinterleave_t;
static const char *TAG = "ESP_GMF_DEINTLV";
static inline esp_gmf_err_t dupl_esp_ae_deinterleave_cfg(esp_gmf_deinterleave_cfg *config, esp_gmf_deinterleave_cfg **new_config)
{
*new_config = esp_gmf_oal_calloc(1, sizeof(*config));
ESP_GMF_MEM_VERIFY(TAG, *new_config, {return ESP_GMF_ERR_MEMORY_LACK;}, "deinterleave configuration", sizeof(*config));
memcpy(*new_config, config, sizeof(*config));
return ESP_GMF_JOB_ERR_OK;
}
static inline void free_esp_ae_deinterleave_cfg(esp_gmf_deinterleave_cfg *config)
{
if (config) {
esp_gmf_oal_free(config);
}
}
static esp_gmf_err_t esp_gmf_deinterleave_new(void *cfg, esp_gmf_obj_handle_t *handle)
{
return esp_gmf_deinterleave_init(cfg, (esp_gmf_element_handle_t *)handle);
}
static esp_gmf_job_err_t esp_gmf_deinterleave_open(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_deinterleave_t *deinterleave = (esp_gmf_deinterleave_t *)self;
esp_gmf_deinterleave_cfg *deinterleave_info = (esp_gmf_deinterleave_cfg *)OBJ_GET_CFG(self);
ESP_GMF_NULL_CHECK(TAG, deinterleave_info, {return ESP_GMF_JOB_ERR_FAIL;})
deinterleave->bytes_per_sample = (deinterleave_info->bits_per_sample >> 3);
deinterleave->out_load = esp_gmf_oal_calloc(1, sizeof(esp_gmf_payload_t *) * deinterleave_info->channel);
ESP_GMF_MEM_VERIFY(TAG, deinterleave->out_load, {return ESP_GMF_JOB_ERR_FAIL;},
"out load", sizeof(esp_gmf_payload_t *) * deinterleave_info->channel);
deinterleave->out_arr = esp_gmf_oal_calloc(1, sizeof(uint8_t *) * deinterleave_info->channel);
ESP_GMF_MEM_VERIFY(TAG, deinterleave->out_arr, {return ESP_GMF_JOB_ERR_FAIL;},
"out buffer array", sizeof(uint8_t *) * deinterleave_info->channel);
GMF_AUDIO_UPDATE_SND_INFO(self, deinterleave_info->sample_rate, deinterleave_info->bits_per_sample, 1);
deinterleave->channel = deinterleave_info->channel;
deinterleave->bits_per_sample = deinterleave_info->bits_per_sample;
deinterleave->need_reopen = false;
ESP_LOGD(TAG, "Open, %p", self);
return ESP_GMF_ERR_OK;
}
static esp_gmf_job_err_t esp_gmf_deinterleave_close(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_deinterleave_t *deinterleave = (esp_gmf_deinterleave_t *)self;
ESP_LOGD(TAG, "Closed, %p", self);
if (deinterleave->out_arr != NULL) {
esp_gmf_oal_free(deinterleave->out_arr);
deinterleave->out_arr = NULL;
}
if (deinterleave->out_load != NULL) {
esp_gmf_oal_free(deinterleave->out_load);
deinterleave->out_load = NULL;
}
return ESP_GMF_ERR_OK;
}
static esp_gmf_job_err_t esp_gmf_deinterleave_process(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_deinterleave_t *deinterleave = (esp_gmf_deinterleave_t *)self;
esp_gmf_job_err_t out_len = ESP_GMF_JOB_ERR_OK;
int i = 0;
if (deinterleave->need_reopen) {
esp_gmf_deinterleave_close(self, NULL);
out_len = esp_gmf_deinterleave_open(self, NULL);
if (out_len != ESP_GMF_JOB_ERR_OK) {
ESP_LOGE(TAG, "deinterleave reopen failed");
return out_len;
}
}
esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in;
esp_gmf_port_handle_t out = ESP_GMF_ELEMENT_GET(self)->out;
esp_gmf_port_handle_t out_port = out;
deinterleave->in_load = NULL;
memset(deinterleave->out_load, 0, sizeof(esp_gmf_payload_t *) * deinterleave->channel);
int samples_num = ESP_GMF_ELEMENT_GET(deinterleave)->in_attr.data_size / (deinterleave->bytes_per_sample * deinterleave->channel);
int bytes = samples_num * deinterleave->bytes_per_sample * deinterleave->channel;
esp_gmf_err_io_t load_ret = esp_gmf_port_acquire_in(in_port, &deinterleave->in_load, bytes, ESP_GMF_MAX_DELAY);
samples_num = deinterleave->in_load->valid_size / (deinterleave->bytes_per_sample * deinterleave->channel);
bytes = samples_num * deinterleave->bytes_per_sample;
if (((bytes * deinterleave->channel) != deinterleave->in_load->valid_size) || (load_ret < ESP_GMF_IO_OK)) {
ESP_LOGE(TAG, "Invalid in load size %d, ret %d", deinterleave->in_load->valid_size, load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
goto __deintlv_release;
}
ESP_LOGV(TAG, "IN: load: %p, buf: %p, valid size: %d, buf length: %d, done: %d",
deinterleave->in_load, deinterleave->in_load->buf, deinterleave->in_load->valid_size,
deinterleave->in_load->buf_length, deinterleave->in_load->is_done);
// Not do deinterleave anymore if one channel failed
while (out_port != NULL) {
load_ret = esp_gmf_port_acquire_out(out_port, &(deinterleave->out_load[i]),
samples_num ? bytes : deinterleave->in_load->buf_length, ESP_GMF_MAX_DELAY);
ESP_GMF_PORT_CHECK(TAG, load_ret, out_len, {out_len = ESP_GMF_JOB_ERR_FAIL; goto __deintlv_release;}, "Failed to acquire out, idx:%d, ret: %d", i, load_ret);
deinterleave->out_arr[i] = deinterleave->out_load[i]->buf;
out_port = out_port->next;
i++;
}
if (samples_num > 0) {
esp_ae_err_t proc_ret = esp_ae_deintlv_process(deinterleave->channel, deinterleave->bits_per_sample, samples_num,
deinterleave->in_load->buf, (void **)deinterleave->out_arr);
ESP_GMF_RET_ON_ERROR(TAG, proc_ret, {out_len = ESP_GMF_JOB_ERR_FAIL; goto __deintlv_release;}, "Deinterleave process error, ret: %d", proc_ret);
}
out_port = out;
i = 0;
while (out_port != NULL) {
deinterleave->out_load[i]->pts = deinterleave->in_load->pts;
deinterleave->out_load[i]->is_done = deinterleave->in_load->is_done;
deinterleave->out_load[i]->valid_size = samples_num * deinterleave->bytes_per_sample;
ESP_LOGV(TAG, "OUT: idx: %d load: %p, buf: %p, valid size: %d, buf length: %d, done: %d",
i, deinterleave->out_load[i], deinterleave->out_load[i]->buf, deinterleave->out_load[i]->valid_size,
deinterleave->out_load[i]->buf_length, deinterleave->out_load[i]->is_done);
out_port = out_port->next;
i++;
}
if (deinterleave->in_load->is_done) {
ESP_LOGD(TAG, "Deinterleave is done");
out_len = ESP_GMF_JOB_ERR_DONE;
}
__deintlv_release:
out_port = out;
i = 0;
while (out_port != NULL && deinterleave->out_load[i] != NULL) {
load_ret = esp_gmf_port_release_out(out_port, deinterleave->out_load[i], out_port->wait_ticks);
if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) {
ESP_LOGE(TAG, "OUT port release error, ret:%d", load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
}
out_port = out_port->next;
i++;
}
if (deinterleave->in_load != NULL) {
load_ret = esp_gmf_port_release_in(in_port, deinterleave->in_load, ESP_GMF_MAX_DELAY);
if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) {
ESP_LOGE(TAG, "IN port release error, ret:%d", load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
}
}
return out_len;
}
static esp_gmf_err_t deinterleave_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx)
{
ESP_GMF_NULL_CHECK(TAG, ctx, {return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, evt, {return ESP_GMF_ERR_INVALID_ARG;});
if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO)
|| (evt->sub != ESP_GMF_INFO_SOUND)
|| (evt->payload == NULL)) {
return ESP_GMF_ERR_OK;
}
esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx;
esp_gmf_element_handle_t el = evt->from;
esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE;
esp_gmf_element_get_state(self, &state);
esp_gmf_info_sound_t *info = (esp_gmf_info_sound_t *)evt->payload;
ESP_GMF_NULL_CHECK(TAG, info, { return ESP_GMF_ERR_FAIL;});
esp_gmf_deinterleave_cfg *config = (esp_gmf_deinterleave_cfg *)OBJ_GET_CFG(self);
ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_FAIL);
esp_gmf_deinterleave_t *deinterleave = (esp_gmf_deinterleave_t *)self;
deinterleave->need_reopen = (config->sample_rate != info->sample_rates) || (info->channels != config->channel) || (config->bits_per_sample != info->bits);
config->sample_rate = info->sample_rates;
config->channel = info->channels;
config->bits_per_sample = info->bits;
ESP_LOGD(TAG, "RECV element info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, rate: %d, ch: %d, bits: %d",
OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type,
esp_gmf_event_get_state_str(state), info->sample_rates, info->channels, info->bits);
if (state == ESP_GMF_EVENT_STATE_NONE) {
esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED);
}
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t esp_gmf_deinterleave_destroy(esp_gmf_element_handle_t self)
{
esp_gmf_deinterleave_t *deinterleave = (esp_gmf_deinterleave_t *)self;
ESP_LOGD(TAG, "Destroyed, %p", self);
free_esp_ae_deinterleave_cfg(OBJ_GET_CFG(self));
esp_gmf_audio_el_deinit(self);
esp_gmf_oal_free(deinterleave);
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t _load_deinterleave_caps_func(esp_gmf_element_handle_t handle)
{
esp_gmf_cap_t *caps = NULL;
esp_gmf_cap_t dec_caps = {0};
dec_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_DEINTERLEAVE;
dec_caps.attr_fun = NULL;
int ret = esp_gmf_cap_append(&caps, &dec_caps);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to create capability");
esp_gmf_element_t *el = (esp_gmf_element_t *)handle;
el->caps = caps;
return ESP_GMF_ERR_OK;
}
esp_gmf_err_t esp_gmf_deinterleave_init(esp_gmf_deinterleave_cfg *config, esp_gmf_element_handle_t *handle)
{
ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;});
*handle = NULL;
esp_gmf_err_t ret = ESP_GMF_ERR_OK;
esp_gmf_deinterleave_t *deinterleave = esp_gmf_oal_calloc(1, sizeof(esp_gmf_deinterleave_t));
ESP_GMF_MEM_VERIFY(TAG, deinterleave, {return ESP_GMF_ERR_MEMORY_LACK;}, "deinterleave", sizeof(esp_gmf_deinterleave_t));
esp_gmf_obj_t *obj = (esp_gmf_obj_t *)deinterleave;
obj->new_obj = esp_gmf_deinterleave_new;
obj->del_obj = esp_gmf_deinterleave_destroy;
esp_gmf_deinterleave_cfg *cfg = NULL;
if (config) {
dupl_esp_ae_deinterleave_cfg(config, &cfg);
} else {
esp_gmf_deinterleave_cfg dcfg = DEFAULT_ESP_GMF_DEINTERLEAVE_CONFIG();
dupl_esp_ae_deinterleave_cfg(&dcfg, &cfg);
}
ESP_GMF_CHECK(TAG, cfg, ret = ESP_GMF_ERR_MEMORY_LACK; goto DEINTLV_INIT_FAIL;, "Failed to allocate deinterleave configuration");
esp_gmf_obj_set_config(obj, cfg, sizeof(esp_gmf_deinterleave_cfg));
ret = esp_gmf_obj_set_tag(obj, "aud_deintlv");
ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto DEINTLV_INIT_FAIL, "Failed to set obj tag");
esp_gmf_element_cfg_t el_cfg = {0};
ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0,
ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT);
ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_MULTI, 0, 0,
ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT);
el_cfg.dependency = true;
ret = esp_gmf_audio_el_init(deinterleave, &el_cfg);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto DEINTLV_INIT_FAIL, "Failed to initialize deinterleave element");
ESP_GMF_ELEMENT_GET(deinterleave)->ops.open = esp_gmf_deinterleave_open;
ESP_GMF_ELEMENT_GET(deinterleave)->ops.process = esp_gmf_deinterleave_process;
ESP_GMF_ELEMENT_GET(deinterleave)->ops.close = esp_gmf_deinterleave_close;
ESP_GMF_ELEMENT_GET(deinterleave)->ops.event_receiver = deinterleave_received_event_handler;
ESP_GMF_ELEMENT_GET(deinterleave)->ops.load_caps = _load_deinterleave_caps_func;
*handle = obj;
ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj);
return ESP_GMF_ERR_OK;
DEINTLV_INIT_FAIL:
esp_gmf_deinterleave_destroy(obj);
return ret;
}
@@ -0,0 +1,446 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#include <string.h>
#include "esp_log.h"
#include "esp_gmf_oal_mem.h"
#include "esp_gmf_oal_mutex.h"
#include "esp_gmf_node.h"
#include "esp_gmf_eq.h"
#include "esp_gmf_args_desc.h"
#include "gmf_audio_common.h"
#include "esp_gmf_audio_methods_def.h"
#include "esp_gmf_cap.h"
#include "esp_gmf_caps_def.h"
#include "esp_gmf_audio_element.h"
/**
* @brief Audio equalizer context in GMF
*/
typedef struct {
esp_gmf_audio_element_t parent; /*!< The GMF eq handle */
esp_ae_eq_handle_t eq_hd; /*!< The audio effects eq handle */
uint8_t bytes_per_sample; /*!< Bytes number of per sampling point */
bool *is_filter_enabled; /*!< The flag of whether eq filter is enabled */
bool need_reopen : 1; /*!< Whether need to reopen.
True: Execute the close function first, then execute the open function
False: Do nothing */
} esp_gmf_eq_t;
static const char *TAG = "ESP_GMF_EQ";
const esp_ae_eq_filter_para_t esp_gmf_default_eq_paras[10] = {
{ESP_AE_EQ_FILTER_PEAK, 31, 1.0, 0.0},
{ESP_AE_EQ_FILTER_PEAK, 62, 1.0, 0.0},
{ESP_AE_EQ_FILTER_PEAK, 125, 1.0, 0.0},
{ESP_AE_EQ_FILTER_PEAK, 250, 1.0, 1.0},
{ESP_AE_EQ_FILTER_PEAK, 500, 1.0, 2.0},
{ESP_AE_EQ_FILTER_PEAK, 1000, 1.0, 3.0},
{ESP_AE_EQ_FILTER_PEAK, 2000, 1.0, 3.0},
{ESP_AE_EQ_FILTER_PEAK, 4000, 1.0, 2.0},
{ESP_AE_EQ_FILTER_PEAK, 8000, 1.0, 1.0},
{ESP_AE_EQ_FILTER_PEAK, 16000, 1.0, 0.0},
};
static inline esp_gmf_err_t dupl_esp_ae_eq_cfg(esp_ae_eq_cfg_t *config, esp_ae_eq_cfg_t **new_config)
{
void *sub_cfg = NULL;
*new_config = esp_gmf_oal_calloc(1, sizeof(*config));
ESP_GMF_MEM_VERIFY(TAG, *new_config, {return ESP_GMF_ERR_MEMORY_LACK;}, "eq configuration", sizeof(*config));
memcpy(*new_config, config, sizeof(*config));
if (config->para && (config->filter_num > 0)) {
sub_cfg = esp_gmf_oal_calloc(1, config->filter_num * sizeof(esp_ae_eq_filter_para_t));
ESP_GMF_MEM_VERIFY(TAG, sub_cfg, {esp_gmf_oal_free(*new_config); return ESP_GMF_ERR_MEMORY_LACK;},
"filter parameter", config->filter_num * sizeof(esp_ae_eq_filter_para_t));
memcpy(sub_cfg, config->para, config->filter_num * sizeof(esp_ae_eq_filter_para_t));
(*new_config)->para = sub_cfg;
}
return ESP_GMF_JOB_ERR_OK;
}
static inline void free_esp_ae_eq_cfg(esp_ae_eq_cfg_t *config)
{
if (config) {
if (config->para && (config->para != esp_gmf_default_eq_paras)) {
esp_gmf_oal_free(config->para);
}
esp_gmf_oal_free(config);
}
}
static inline void eq_change_src_info(esp_gmf_element_handle_t self, uint32_t src_rate, uint8_t src_ch, uint8_t src_bits)
{
esp_ae_eq_cfg_t *eq_info = (esp_ae_eq_cfg_t *)OBJ_GET_CFG(self);
eq_info->channel = src_ch;
eq_info->sample_rate = src_rate;
eq_info->bits_per_sample = src_bits;
}
static esp_gmf_err_t __eq_set_para(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc,
uint8_t *buf, int buf_len)
{
ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, arg_desc, {return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;});
uint8_t idx = (uint8_t)(*buf);
return esp_gmf_eq_set_para(handle, idx, (esp_ae_eq_filter_para_t *)(buf + arg_desc->next->offset));
}
static esp_gmf_err_t __eq_get_para(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc,
uint8_t *buf, int buf_len)
{
ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, arg_desc, {return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;});
uint8_t idx = (uint8_t)(*buf);
esp_ae_eq_filter_para_t *para = (esp_ae_eq_filter_para_t *)(buf + arg_desc->next->offset);
return esp_gmf_eq_get_para(handle, idx, para);
}
static esp_gmf_err_t __eq_enable_filter(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc,
uint8_t *buf, int buf_len)
{
ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, arg_desc, {return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;});
esp_gmf_args_desc_t *filter_desc = arg_desc;
uint8_t idx = (uint8_t)(*buf);
filter_desc = filter_desc->next;
uint8_t is_enable = (uint8_t)(*(buf + filter_desc->offset));
return esp_gmf_eq_enable_filter(handle, idx,is_enable);
}
static esp_gmf_err_t esp_gmf_eq_new(void *cfg, esp_gmf_obj_handle_t *handle)
{
return esp_gmf_eq_init(cfg, (esp_gmf_element_handle_t *)handle);
}
static esp_gmf_job_err_t esp_gmf_eq_open(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_eq_t *eq = (esp_gmf_eq_t *)self;
esp_ae_eq_cfg_t *eq_info = (esp_ae_eq_cfg_t *)OBJ_GET_CFG(self);
ESP_GMF_NULL_CHECK(TAG, eq_info, {return ESP_GMF_JOB_ERR_FAIL;});
eq->bytes_per_sample = (eq_info->bits_per_sample >> 3) * eq_info->channel;
esp_ae_eq_open(eq_info, &eq->eq_hd);
ESP_GMF_CHECK(TAG, eq->eq_hd, {return ESP_GMF_JOB_ERR_FAIL;}, "Failed to create eq handle");
GMF_AUDIO_UPDATE_SND_INFO(self, eq_info->sample_rate, eq_info->bits_per_sample, eq_info->channel);
for (int i = 0; i < eq_info->filter_num; i++) {
if (eq->is_filter_enabled[i]) {
esp_ae_eq_enable_filter(eq->eq_hd, i);
} else {
esp_ae_eq_disable_filter(eq->eq_hd, i);
}
}
eq->need_reopen = false;
ESP_LOGD(TAG, "Open, %p", eq);
return ESP_GMF_ERR_OK;
}
static esp_gmf_job_err_t esp_gmf_eq_close(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_eq_t *eq = (esp_gmf_eq_t *)self;
ESP_LOGD(TAG, "Closed, %p", self);
if (eq->eq_hd != NULL) {
esp_ae_eq_close(eq->eq_hd);
eq->eq_hd = NULL;
}
return ESP_GMF_ERR_OK;
}
static esp_gmf_job_err_t esp_gmf_eq_process(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_eq_t *eq = (esp_gmf_eq_t *)self;
esp_gmf_job_err_t out_len = ESP_GMF_JOB_ERR_OK;
if (eq->need_reopen) {
esp_gmf_eq_close(self, NULL);
out_len = esp_gmf_eq_open(self, NULL);
if (out_len != ESP_GMF_JOB_ERR_OK) {
ESP_LOGE(TAG, "EQ reopen failed");
return out_len;
}
}
esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in;
esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out;
esp_gmf_payload_t *in_load = NULL;
esp_gmf_payload_t *out_load = NULL;
int samples_num = ESP_GMF_ELEMENT_GET(eq)->in_attr.data_size / (eq->bytes_per_sample);
int bytes = samples_num * eq->bytes_per_sample;
esp_gmf_err_io_t load_ret = esp_gmf_port_acquire_in(in_port, &in_load, bytes, ESP_GMF_MAX_DELAY);
samples_num = in_load->valid_size / (eq->bytes_per_sample);
bytes = samples_num * eq->bytes_per_sample;
if ((bytes != in_load->valid_size) || (load_ret < ESP_GMF_IO_OK)) {
ESP_LOGE(TAG, "Invalid in load size %d, ret %d", in_load->valid_size, load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
goto __eq_release;
}
if (in_port->is_shared == 1) {
out_load = in_load;
}
load_ret = esp_gmf_port_acquire_out(out_port, &out_load, samples_num ? bytes : in_load->buf_length, ESP_GMF_MAX_DELAY);
ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, out_len, { goto __eq_release;});
if (samples_num > 0) {
esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)self)->lock);
esp_ae_err_t ret = esp_ae_eq_process(eq->eq_hd, samples_num, in_load->buf, out_load->buf);
esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)self)->lock);
ESP_GMF_RET_ON_ERROR(TAG, ret, {out_len = ESP_GMF_JOB_ERR_FAIL; goto __eq_release;}, "Equalize process error %d", ret);
}
ESP_LOGV(TAG, "Samples: %d, IN-PLD: %p-%p-%d-%d-%d, OUT-PLD: %p-%p-%d-%d-%d",
samples_num, in_load, in_load->buf, in_load->valid_size, in_load->buf_length, in_load->is_done,
out_load, out_load->buf, out_load->valid_size, out_load->buf_length, out_load->is_done);
out_load->valid_size = samples_num * eq->bytes_per_sample;
out_load->is_done = in_load->is_done;
out_load->pts = in_load->pts;
if (out_load->valid_size > 0) {
esp_gmf_audio_el_update_file_pos((esp_gmf_element_handle_t)self, out_load->valid_size);
}
if (in_load->is_done) {
out_len = ESP_GMF_JOB_ERR_DONE;
ESP_LOGD(TAG, "Equalize done, out len: %d", out_load->valid_size);
}
__eq_release:
// Release in and out port
if (out_load != NULL) {
load_ret = esp_gmf_port_release_out(out_port, out_load, ESP_GMF_MAX_DELAY);
if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) {
ESP_LOGE(TAG, "OUT port release error, ret:%d", load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
}
}
if (in_load != NULL) {
load_ret = esp_gmf_port_release_in(in_port, in_load, ESP_GMF_MAX_DELAY);
if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) {
ESP_LOGE(TAG, "IN port release error, ret:%d", load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
}
}
return out_len;
}
static esp_gmf_err_t eq_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx)
{
ESP_GMF_NULL_CHECK(TAG, ctx, {return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, evt, {return ESP_GMF_ERR_INVALID_ARG;});
if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO)
|| (evt->sub != ESP_GMF_INFO_SOUND)
|| (evt->payload == NULL)) {
return ESP_GMF_ERR_OK;
}
esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx;
esp_gmf_element_handle_t el = evt->from;
esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE;
esp_gmf_element_get_state(self, &state);
esp_gmf_info_sound_t *info = (esp_gmf_info_sound_t *)evt->payload;
esp_ae_eq_cfg_t *config = (esp_ae_eq_cfg_t *)OBJ_GET_CFG(self);
ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_FAIL);
esp_gmf_eq_t *eq = (esp_gmf_eq_t *)self;
eq->need_reopen = (config->sample_rate != info->sample_rates) || (info->channels != config->channel) || (config->bits_per_sample != info->bits);
config->sample_rate = info->sample_rates;
config->channel = info->channels;
config->bits_per_sample = info->bits;
ESP_LOGD(TAG, "RECV element info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, rate: %d, ch: %d, bits: %d",
OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type,
esp_gmf_event_get_state_str(state), info->sample_rates, info->channels, info->bits);
if (state == ESP_GMF_EVENT_STATE_NONE) {
esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED);
}
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t esp_gmf_eq_destroy(esp_gmf_element_handle_t self)
{
esp_gmf_eq_t *eq = (esp_gmf_eq_t *)self;
ESP_LOGD(TAG, "Destroyed, %p", self);
free_esp_ae_eq_cfg(OBJ_GET_CFG(self));
if(eq->is_filter_enabled){
esp_gmf_oal_free(eq->is_filter_enabled);
}
esp_gmf_audio_el_deinit(self);
esp_gmf_oal_free(eq);
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t _load_eq_caps_func(esp_gmf_element_handle_t handle)
{
esp_gmf_cap_t *caps = NULL;
esp_gmf_cap_t dec_caps = {0};
dec_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_EQUALIZER;
dec_caps.attr_fun = NULL;
int ret = esp_gmf_cap_append(&caps, &dec_caps);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to create capability");
esp_gmf_element_t *el = (esp_gmf_element_t *)handle;
el->caps = caps;
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t _load_eq_methods_func(esp_gmf_element_handle_t handle)
{
esp_gmf_method_t *method = NULL;
esp_gmf_args_desc_t *set_args = NULL;
esp_gmf_args_desc_t *get_args = NULL;
esp_gmf_args_desc_t *pointer_args = NULL;
esp_gmf_err_t ret = esp_gmf_args_desc_append(&pointer_args, AMETHOD_ARG(EQ, SET_PARA, PARA_FT), ESP_GMF_ARGS_TYPE_UINT32,
sizeof(uint32_t), offsetof(esp_ae_eq_filter_para_t, filter_type));
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append FILTER argument");
ret = esp_gmf_args_desc_append(&pointer_args, AMETHOD_ARG(EQ, SET_PARA, PARA_FC), ESP_GMF_ARGS_TYPE_UINT32,
sizeof(uint32_t), offsetof(esp_ae_eq_filter_para_t, fc));
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append FC argument");
ret = esp_gmf_args_desc_append(&pointer_args, AMETHOD_ARG(EQ, SET_PARA, PARA_Q), ESP_GMF_ARGS_TYPE_FLOAT,
sizeof(float), offsetof(esp_ae_eq_filter_para_t, q));
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append Q argument");
ret = esp_gmf_args_desc_append(&pointer_args, AMETHOD_ARG(EQ, SET_PARA, PARA_GAIN), ESP_GMF_ARGS_TYPE_FLOAT,
sizeof(float), offsetof(esp_ae_eq_filter_para_t, gain));
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append GAIN argument");
ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(EQ, SET_PARA, IDX), ESP_GMF_ARGS_TYPE_UINT8, sizeof(uint8_t), 0);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append INDEX argument");
ret = esp_gmf_args_desc_append_array(&set_args, AMETHOD_ARG(EQ, SET_PARA, PARA), pointer_args,
sizeof(esp_ae_eq_filter_para_t), sizeof(uint8_t));
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append PARA argument");
ret = esp_gmf_method_append(&method, AMETHOD(EQ, SET_PARA), __eq_set_para, set_args);
ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(EQ, SET_PARA));
ret = esp_gmf_args_desc_copy(set_args, &get_args);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to copy PARA argument");
ret = esp_gmf_method_append(&method, AMETHOD(EQ, GET_PARA), __eq_get_para, get_args);
ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(EQ, GET_PARA));
set_args = NULL;
get_args = NULL;
ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(EQ, ENABLE_FILTER, IDX), ESP_GMF_ARGS_TYPE_UINT8, sizeof(uint8_t), 0);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append INDEX argument");
ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(EQ, ENABLE_FILTER, ENABLE), ESP_GMF_ARGS_TYPE_UINT8,
sizeof(uint8_t), sizeof(uint8_t));
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append PARA argument");
ret = esp_gmf_method_append(&method, AMETHOD(EQ, ENABLE_FILTER), __eq_enable_filter, set_args);
ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(EQ, ENABLE_FILTER));
esp_gmf_element_t *el = (esp_gmf_element_t *)handle;
el->method = method;
return ESP_GMF_ERR_OK;
}
esp_gmf_err_t esp_gmf_eq_set_para(esp_gmf_element_handle_t handle, uint8_t idx, esp_ae_eq_filter_para_t *para)
{
ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, para, { return ESP_GMF_ERR_INVALID_ARG;});
esp_gmf_eq_t *eq = (esp_gmf_eq_t *)handle;
if (eq->eq_hd) {
esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)handle)->lock);
esp_ae_err_t ret = esp_ae_eq_set_filter_para(eq->eq_hd, idx, para);
esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)handle)->lock);
ESP_GMF_RET_ON_ERROR(TAG, ret, return ESP_GMF_JOB_ERR_FAIL;, "Equalize set error %d", ret);
}
esp_ae_eq_cfg_t *cfg = (esp_ae_eq_cfg_t *)OBJ_GET_CFG(handle);
ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_ERR_FAIL);
if (cfg->para) {
if (idx >= cfg->filter_num) {
ESP_LOGE(TAG, "Invalid idx %d", idx);
return ESP_GMF_ERR_FAIL;
}
memcpy(&cfg->para[idx], para, sizeof(esp_ae_eq_filter_para_t));
return ESP_GMF_ERR_OK;
}
ESP_LOGE(TAG, "Failed to set EQ para, no para allocated");
return ESP_GMF_ERR_FAIL;
}
esp_gmf_err_t esp_gmf_eq_get_para(esp_gmf_element_handle_t handle, uint8_t idx, esp_ae_eq_filter_para_t *para)
{
ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;});
esp_gmf_eq_t *eq = (esp_gmf_eq_t *)handle;
if (eq->eq_hd) {
esp_ae_err_t ret = esp_ae_eq_get_filter_para(eq->eq_hd, idx, para);
ESP_GMF_RET_ON_ERROR(TAG, ret, return ESP_GMF_JOB_ERR_FAIL;, "Equalize set error %d", ret);
} else {
esp_ae_eq_cfg_t *cfg = (esp_ae_eq_cfg_t *)OBJ_GET_CFG(handle);
ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_ERR_FAIL);
if (cfg->para) {
if (idx >= cfg->filter_num) {
ESP_LOGE(TAG, "Invalid idx %d", idx);
return ESP_GMF_ERR_FAIL;
}
memcpy(para, &cfg->para[idx], sizeof(esp_ae_eq_filter_para_t));
}
}
return ESP_GMF_ERR_OK;
}
esp_gmf_err_t esp_gmf_eq_enable_filter(esp_gmf_element_handle_t handle, uint8_t idx, bool is_enable)
{
ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;});
esp_gmf_eq_t *eq = (esp_gmf_eq_t *)handle;
esp_ae_eq_cfg_t *cfg = (esp_ae_eq_cfg_t *)OBJ_GET_CFG(handle);
ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_ERR_FAIL);
if (idx >= cfg->filter_num) {
ESP_LOGE(TAG, "Filter index %d overlimit %d hd:%p", idx, cfg->filter_num, eq);
return ESP_GMF_ERR_INVALID_ARG;
}
if (eq->eq_hd) {
esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)handle)->lock);
esp_ae_err_t ret = 0;
if (is_enable) {
ret = esp_ae_eq_enable_filter(eq->eq_hd, idx);
} else {
ret = esp_ae_eq_disable_filter(eq->eq_hd, idx);
}
esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)handle)->lock);
ESP_GMF_RET_ON_ERROR(TAG, ret, return ESP_GMF_JOB_ERR_FAIL;, "Equalize set error %d", ret);
}
eq->is_filter_enabled[idx] = is_enable;
return ESP_GMF_ERR_OK;
}
esp_gmf_err_t esp_gmf_eq_init(esp_ae_eq_cfg_t *config, esp_gmf_element_handle_t *handle)
{
ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;});
*handle = NULL;
esp_gmf_err_t ret = ESP_GMF_ERR_OK;
esp_gmf_eq_t *eq = esp_gmf_oal_calloc(1, sizeof(esp_gmf_eq_t));
ESP_GMF_MEM_VERIFY(TAG, eq, {return ESP_GMF_ERR_MEMORY_LACK;}, "eq", sizeof(esp_gmf_eq_t));
esp_gmf_obj_t *obj = (esp_gmf_obj_t *)eq;
obj->new_obj = esp_gmf_eq_new;
obj->del_obj = esp_gmf_eq_destroy;
esp_ae_eq_cfg_t *cfg = NULL;
if (config) {
if (config->para == NULL) {
config->para = (esp_ae_eq_filter_para_t *)esp_gmf_default_eq_paras;
config->filter_num = sizeof(esp_gmf_default_eq_paras) / sizeof(esp_ae_eq_filter_para_t);
}
dupl_esp_ae_eq_cfg(config, &cfg);
} else {
esp_ae_eq_cfg_t dcfg = DEFAULT_ESP_GMF_EQ_CONFIG();
dcfg.para = (esp_ae_eq_filter_para_t *)esp_gmf_default_eq_paras;
dcfg.filter_num = sizeof(esp_gmf_default_eq_paras) / sizeof(esp_ae_eq_filter_para_t);
dupl_esp_ae_eq_cfg(&dcfg, &cfg);
}
ESP_GMF_CHECK(TAG, cfg, {ret = ESP_GMF_ERR_MEMORY_LACK; goto EQ_INI_FAIL;}, "Failed to allocate eq configuration");
esp_gmf_obj_set_config(obj, cfg, sizeof(esp_ae_eq_cfg_t));
eq->is_filter_enabled = esp_gmf_oal_calloc(cfg->filter_num, sizeof(bool));
ESP_GMF_MEM_VERIFY(TAG, eq->is_filter_enabled, ret = ESP_GMF_ERR_MEMORY_LACK; goto EQ_INI_FAIL, "Rellocation failed", cfg->filter_num);
ret = esp_gmf_obj_set_tag(obj, "aud_eq");
ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto EQ_INI_FAIL, "Failed to set obj tag");
esp_gmf_element_cfg_t el_cfg = {0};
ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0,
ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT);
ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0,
ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT);
el_cfg.dependency = true;
ret = esp_gmf_audio_el_init(eq, &el_cfg);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto EQ_INI_FAIL, "Failed to initialize eq element");
*handle = obj;
ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj);
ESP_GMF_ELEMENT_GET(eq)->ops.open = esp_gmf_eq_open;
ESP_GMF_ELEMENT_GET(eq)->ops.process = esp_gmf_eq_process;
ESP_GMF_ELEMENT_GET(eq)->ops.close = esp_gmf_eq_close;
ESP_GMF_ELEMENT_GET(eq)->ops.event_receiver = eq_received_event_handler;
ESP_GMF_ELEMENT_GET(eq)->ops.load_caps = _load_eq_caps_func;
ESP_GMF_ELEMENT_GET(eq)->ops.load_methods = _load_eq_methods_func;
return ESP_GMF_ERR_OK;
EQ_INI_FAIL:
esp_gmf_eq_destroy(obj);
return ret;
}
@@ -0,0 +1,329 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#include <string.h>
#include "esp_log.h"
#include "esp_gmf_oal_mem.h"
#include "esp_gmf_oal_mutex.h"
#include "esp_gmf_node.h"
#include "esp_gmf_fade.h"
#include "gmf_audio_common.h"
#include "esp_gmf_audio_methods_def.h"
#include "esp_gmf_cap.h"
#include "esp_gmf_caps_def.h"
#include "esp_gmf_audio_element.h"
/**
* @brief Audio fade context in GMF
*/
typedef struct {
esp_gmf_audio_element_t parent; /*!< The GMF fade handle */
esp_ae_fade_handle_t fade_hd; /*!< The audio effects fade handle */
uint8_t bytes_per_sample; /*!< Bytes number of per sampling point */
bool need_reopen : 1; /*!< Whether need to reopen.
True: Execute the close function first, then execute the open function
False: Do nothing */
} esp_gmf_fade_t;
static const char *TAG = "ESP_GMF_FADE";
static esp_gmf_err_t __fade_set_mode(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc,
uint8_t *buf, int buf_len)
{
ESP_GMF_NULL_CHECK(TAG, arg_desc, {return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;});
return esp_gmf_fade_set_mode(handle, *((esp_ae_fade_mode_t *)buf));
}
static esp_gmf_err_t __fade_get_mode(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc,
uint8_t *buf, int buf_len)
{
ESP_GMF_NULL_CHECK(TAG, arg_desc, {return ESP_GMF_ERR_INVALID_ARG;});
return esp_gmf_fade_get_mode(handle, (esp_ae_fade_mode_t *)buf);
}
static esp_gmf_err_t __fade_reset(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc,
uint8_t *buf, int buf_len)
{
return esp_gmf_fade_reset_weight(handle);
}
static esp_gmf_err_t esp_gmf_fade_new(void *cfg, esp_gmf_obj_handle_t *handle)
{
return esp_gmf_fade_init(cfg, (esp_gmf_element_handle_t *)handle);
}
static esp_gmf_job_err_t esp_gmf_fade_open(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_fade_t *fade = (esp_gmf_fade_t *)self;
esp_ae_fade_cfg_t *fade_info = (esp_ae_fade_cfg_t *)OBJ_GET_CFG(self);
ESP_GMF_NULL_CHECK(TAG, fade_info, {return ESP_GMF_JOB_ERR_FAIL;});
fade->bytes_per_sample = (fade_info->bits_per_sample >> 3) * fade_info->channel;
esp_ae_fade_open(fade_info, &fade->fade_hd);
ESP_GMF_CHECK(TAG, fade->fade_hd, {return ESP_GMF_JOB_ERR_FAIL;}, "Failed to create fade handle");
GMF_AUDIO_UPDATE_SND_INFO(self, fade_info->sample_rate, fade_info->bits_per_sample, fade_info->channel);
fade->need_reopen = false;
ESP_LOGD(TAG, "Open, %p", self);
return ESP_GMF_ERR_OK;
}
static esp_gmf_job_err_t esp_gmf_fade_close(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_fade_t *fade = (esp_gmf_fade_t *)self;
ESP_LOGD(TAG, "Closed, %p", self);
if (fade->fade_hd != NULL) {
esp_ae_fade_close(fade->fade_hd);
fade->fade_hd = NULL;
}
return ESP_GMF_ERR_OK;
}
static esp_gmf_job_err_t esp_gmf_fade_process(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_fade_t *fade = (esp_gmf_fade_t *)self;
esp_gmf_job_err_t out_len = ESP_GMF_JOB_ERR_OK;
if (fade->need_reopen) {
esp_gmf_fade_close(self, NULL);
out_len = esp_gmf_fade_open(self, NULL);
if (out_len != ESP_GMF_JOB_ERR_OK) {
ESP_LOGE(TAG, "Fade reopen failed");
return out_len;
}
}
esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in;
esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out;
esp_gmf_payload_t *in_load = NULL;
esp_gmf_payload_t *out_load = NULL;
int samples_num = ESP_GMF_ELEMENT_GET(fade)->in_attr.data_size / (fade->bytes_per_sample);
int bytes = samples_num * fade->bytes_per_sample;
esp_gmf_err_io_t load_ret = esp_gmf_port_acquire_in(in_port, &in_load, bytes, ESP_GMF_MAX_DELAY);
samples_num = in_load->valid_size / (fade->bytes_per_sample);
bytes = samples_num * fade->bytes_per_sample;
if ((bytes != in_load->valid_size) || (load_ret < ESP_GMF_IO_OK)) {
ESP_LOGE(TAG, "Invalid in load size %d, ret %d", in_load->valid_size, load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
goto __fade_release;
}
if (in_port->is_shared == 1) {
out_load = in_load;
}
load_ret = esp_gmf_port_acquire_out(out_port, &out_load, samples_num ? bytes : in_load->buf_length, ESP_GMF_MAX_DELAY);
ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, out_len, { goto __fade_release;});
if (samples_num > 0) {
esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)self)->lock);
esp_ae_err_t ret = esp_ae_fade_process(fade->fade_hd, samples_num, in_load->buf, out_load->buf);
esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)self)->lock);
ESP_GMF_RET_ON_ERROR(TAG, ret, {out_len = ESP_GMF_JOB_ERR_FAIL; goto __fade_release;}, "Fade process error %d", ret);
}
ESP_LOGV(TAG, "Samples: %d, IN-PLD: %p-%p-%d-%d-%d, OUT-PLD: %p-%p-%d-%d-%d",
samples_num, in_load, in_load->buf, in_load->valid_size, in_load->buf_length, in_load->is_done,
out_load, out_load->buf, out_load->valid_size, out_load->buf_length, out_load->is_done);
out_load->valid_size = samples_num * fade->bytes_per_sample;
out_load->is_done = in_load->is_done;
out_load->pts = in_load->pts;
if (out_load->valid_size > 0) {
esp_gmf_audio_el_update_file_pos((esp_gmf_element_handle_t)self, out_load->valid_size);
}
if (in_load->is_done) {
out_len = ESP_GMF_JOB_ERR_DONE;
ESP_LOGD(TAG, "Fade done, out len: %d", out_load->valid_size);
}
__fade_release:
// Release in and out port
if (out_load != NULL) {
load_ret = esp_gmf_port_release_out(out_port, out_load, ESP_GMF_MAX_DELAY);
if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) {
ESP_LOGE(TAG, "OUT port release error, ret:%d", load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
}
}
if (in_load != NULL) {
load_ret = esp_gmf_port_release_in(in_port, in_load, ESP_GMF_MAX_DELAY);
if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) {
ESP_LOGE(TAG, "IN port release error, ret:%d", load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
}
}
return out_len;
}
static esp_gmf_err_t fade_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx)
{
ESP_GMF_NULL_CHECK(TAG, ctx, {return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, evt, {return ESP_GMF_ERR_INVALID_ARG;});
if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO)
|| (evt->sub != ESP_GMF_INFO_SOUND)
|| (evt->payload == NULL)) {
return ESP_GMF_ERR_OK;
}
esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx;
esp_gmf_element_handle_t el = evt->from;
esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE;
esp_gmf_element_get_state(self, &state);
esp_gmf_info_sound_t *info = (esp_gmf_info_sound_t *)evt->payload;
esp_ae_fade_cfg_t *config = (esp_ae_fade_cfg_t *)OBJ_GET_CFG(self);
ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_FAIL);
esp_gmf_fade_t *fade = (esp_gmf_fade_t *)self;
fade->need_reopen = (config->sample_rate != info->sample_rates) || (info->channels != config->channel) || (config->bits_per_sample != info->bits);
config->sample_rate = info->sample_rates;
config->channel = info->channels;
config->bits_per_sample = info->bits;
ESP_LOGD(TAG, "RECV element info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, rate: %d, ch: %d, bits: %d",
OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type,
esp_gmf_event_get_state_str(state), info->sample_rates, info->channels, info->bits);
if (state == ESP_GMF_EVENT_STATE_NONE) {
esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED);
}
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t esp_gmf_fade_destroy(esp_gmf_element_handle_t self)
{
esp_gmf_fade_t *fade = (esp_gmf_fade_t *)self;
ESP_LOGD(TAG, "Destroyed, %p", self);
void *cfg = OBJ_GET_CFG(self);
if (cfg) {
esp_gmf_oal_free(cfg);
}
esp_gmf_audio_el_deinit(self);
esp_gmf_oal_free(fade);
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t _load_fade_caps_func(esp_gmf_element_handle_t handle)
{
esp_gmf_cap_t *caps = NULL;
esp_gmf_cap_t dec_caps = {0};
dec_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_FADE;
dec_caps.attr_fun = NULL;
int ret = esp_gmf_cap_append(&caps, &dec_caps);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to create capability");
esp_gmf_element_t *el = (esp_gmf_element_t *)handle;
el->caps = caps;
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t _load_fade_methods_func(esp_gmf_element_handle_t handle)
{
esp_gmf_method_t *method = NULL;
esp_gmf_args_desc_t *set_args = NULL;
esp_gmf_args_desc_t *get_args = NULL;
esp_gmf_err_t ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(FADE, SET_MODE, MODE), ESP_GMF_ARGS_TYPE_INT32, sizeof(int32_t), 0);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append MODE argument");
ret = esp_gmf_method_append(&method, AMETHOD(FADE, SET_MODE), __fade_set_mode, set_args);
ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(FADE, SET_MODE));
ret = esp_gmf_args_desc_copy(set_args, &get_args);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to copy argument");
ret = esp_gmf_method_append(&method, AMETHOD(FADE, GET_MODE), __fade_get_mode, get_args);
ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(FADE, GET_MODE));
ret = esp_gmf_method_append(&method, AMETHOD(FADE, RESET_WEIGHT), __fade_reset, NULL);
ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(FADE, RESET_WEIGHT));
esp_gmf_element_t *el = (esp_gmf_element_t *)handle;
el->method = method;
return ESP_GMF_ERR_OK;
}
esp_gmf_err_t esp_gmf_fade_set_mode(esp_gmf_element_handle_t handle, esp_ae_fade_mode_t mode)
{
ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;});
esp_gmf_fade_t *fade = (esp_gmf_fade_t *)handle;
esp_ae_fade_cfg_t *cfg = (esp_ae_fade_cfg_t *)OBJ_GET_CFG(handle);
ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_ERR_FAIL);
if (fade->fade_hd) {
esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)handle)->lock);
esp_ae_err_t ret = esp_ae_fade_set_mode(fade->fade_hd, mode);
esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)handle)->lock);
if (ret != ESP_AE_ERR_OK) {
return ESP_GMF_JOB_ERR_FAIL;
}
}
cfg->mode = mode;
return ESP_GMF_JOB_ERR_OK;
}
esp_gmf_err_t esp_gmf_fade_get_mode(esp_gmf_element_handle_t handle, esp_ae_fade_mode_t *mode)
{
ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, mode, { return ESP_GMF_ERR_INVALID_ARG;});
esp_ae_fade_cfg_t *cfg = (esp_ae_fade_cfg_t *)OBJ_GET_CFG(handle);
ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_ERR_FAIL);
esp_gmf_fade_t *fade = (esp_gmf_fade_t *)handle;
if (fade->fade_hd) {
esp_ae_err_t ret = esp_ae_fade_get_mode(fade->fade_hd, mode);
if (ret != ESP_AE_ERR_OK) {
return ESP_GMF_JOB_ERR_FAIL;
}
} else {
*mode = cfg->mode;
}
return ESP_GMF_JOB_ERR_OK;
}
esp_gmf_err_t esp_gmf_fade_reset_weight(esp_gmf_element_handle_t handle)
{
ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;});
esp_ae_fade_cfg_t *cfg = (esp_ae_fade_cfg_t *)OBJ_GET_CFG(handle);
ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_ERR_FAIL);
esp_gmf_fade_t *fade = (esp_gmf_fade_t *)handle;
if(fade->fade_hd) {
esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)handle)->lock);
esp_ae_err_t ret = esp_ae_fade_reset_weight(fade->fade_hd);
esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)handle)->lock);
if (ret != ESP_AE_ERR_OK) {
return ESP_GMF_JOB_ERR_FAIL;
}
esp_ae_fade_get_mode(fade->fade_hd, &cfg->mode);
}
return ESP_GMF_JOB_ERR_OK;
}
esp_gmf_err_t esp_gmf_fade_init(esp_ae_fade_cfg_t *config, esp_gmf_element_handle_t *handle)
{
ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;});
*handle = NULL;
esp_gmf_err_t ret = ESP_GMF_ERR_OK;
esp_gmf_fade_t *fade = esp_gmf_oal_calloc(1, sizeof(esp_gmf_fade_t));
ESP_GMF_MEM_VERIFY(TAG, fade, {return ESP_GMF_ERR_MEMORY_LACK;}, "fade", sizeof(esp_gmf_fade_t));
esp_gmf_obj_t *obj = (esp_gmf_obj_t *)fade;
obj->new_obj = esp_gmf_fade_new;
obj->del_obj = esp_gmf_fade_destroy;
esp_ae_fade_cfg_t *cfg = esp_gmf_oal_calloc(1, sizeof(esp_ae_fade_cfg_t));
ESP_GMF_MEM_VERIFY(TAG, cfg, {ret = ESP_GMF_ERR_MEMORY_LACK; goto FADE_INIT_FAIL;}, "fade configuration", sizeof(esp_ae_fade_cfg_t));
esp_gmf_obj_set_config(obj, cfg, sizeof(esp_ae_fade_cfg_t));
if (config) {
memcpy(cfg, config, sizeof(esp_ae_fade_cfg_t));
} else {
esp_ae_fade_cfg_t dcfg = DEFAULT_ESP_GMF_FADE_CONFIG();
memcpy(cfg, &dcfg, sizeof(esp_ae_fade_cfg_t));
}
ret = esp_gmf_obj_set_tag(obj, "aud_fade");
ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto FADE_INIT_FAIL, "Failed to set obj tag");
esp_gmf_element_cfg_t el_cfg = {0};
ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0,
ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT);
ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0,
ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT);
el_cfg.dependency = true;
ret = esp_gmf_audio_el_init(fade, &el_cfg);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto FADE_INIT_FAIL, "Failed to initialize fade element");
ESP_GMF_ELEMENT_GET(fade)->ops.open = esp_gmf_fade_open;
ESP_GMF_ELEMENT_GET(fade)->ops.process = esp_gmf_fade_process;
ESP_GMF_ELEMENT_GET(fade)->ops.close = esp_gmf_fade_close;
ESP_GMF_ELEMENT_GET(fade)->ops.event_receiver = fade_received_event_handler;
ESP_GMF_ELEMENT_GET(fade)->ops.load_caps = _load_fade_caps_func;
ESP_GMF_ELEMENT_GET(fade)->ops.load_methods = _load_fade_methods_func;
*handle = obj;
ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj);
return ESP_GMF_ERR_OK;
FADE_INIT_FAIL:
esp_gmf_fade_destroy(obj);
return ret;
}
@@ -0,0 +1,270 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#include <string.h>
#include "esp_log.h"
#include "esp_gmf_oal_mem.h"
#include "esp_gmf_oal_mutex.h"
#include "esp_gmf_node.h"
#include "esp_gmf_interleave.h"
#include "esp_ae_data_weaver.h"
#include "esp_heap_caps.h"
#include "gmf_audio_common.h"
#include "esp_gmf_cap.h"
#include "esp_gmf_caps_def.h"
#include "esp_gmf_audio_element.h"
#define ESP_GMF_PROCESS_SAMPLE (256)
/**
* @brief Audio interleave context in GMF
*/
typedef struct {
esp_gmf_audio_element_t parent; /*!< The GMF interleave handle */
uint8_t bytes_per_sample; /*!< Bytes number of per sampling point */
esp_gmf_payload_t **in_load; /*!< The array of input payload */
esp_gmf_payload_t *out_load; /*!< The output payload */
uint8_t **in_arr; /*!< The array of input buffer pointer */
uint8_t src_num; /*!< The number of input stream source */
uint8_t bits_per_sample; /*!< Bits number of per sampling point */
bool need_reopen : 1; /*!< Whether need to reopen.
True: Execute the close function first, then execute the open function
False: Do nothing */
} esp_gmf_interleave_t;
static const char *TAG = "ESP_GMF_INTLV";
static inline esp_gmf_err_t dupl_esp_ae_interleave_cfg(esp_gmf_interleave_cfg *config, esp_gmf_interleave_cfg **new_config)
{
*new_config = esp_gmf_oal_calloc(1, sizeof(*config));
ESP_GMF_MEM_VERIFY(TAG, *new_config, {return ESP_GMF_ERR_MEMORY_LACK;}, "interleave configuration", sizeof(*config));
memcpy(*new_config, config, sizeof(*config));
return ESP_GMF_JOB_ERR_OK;
}
static inline void free_esp_ae_interleave_cfg(esp_gmf_interleave_cfg *config)
{
if (config) {
esp_gmf_oal_free(config);
}
}
static esp_gmf_err_t esp_gmf_interleave_new(void *cfg, esp_gmf_obj_handle_t *handle)
{
return esp_gmf_interleave_init(cfg, (esp_gmf_element_handle_t *)handle);
}
static esp_gmf_job_err_t esp_gmf_interleave_open(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_interleave_t *interleave = (esp_gmf_interleave_t *)self;
esp_gmf_interleave_cfg *interleave_info = (esp_gmf_interleave_cfg *)OBJ_GET_CFG(self);
ESP_GMF_NULL_CHECK(TAG, interleave_info, {return ESP_GMF_JOB_ERR_FAIL;})
interleave->bytes_per_sample = (interleave_info->bits_per_sample >> 3);
interleave->in_load = esp_gmf_oal_calloc(1, sizeof(esp_gmf_payload_t *) * interleave_info->src_num);
ESP_GMF_MEM_VERIFY(TAG, interleave->in_load, {return ESP_GMF_JOB_ERR_FAIL;},
"in load", sizeof(esp_gmf_payload_t *) * interleave_info->src_num);
interleave->in_arr = esp_gmf_oal_calloc(1, sizeof(int *) * interleave_info->src_num);
ESP_GMF_MEM_VERIFY(TAG, interleave->in_arr, {return ESP_GMF_JOB_ERR_FAIL;},
"in buffer array", sizeof(int *) * interleave_info->src_num);
GMF_AUDIO_UPDATE_SND_INFO(self, interleave_info->sample_rate, interleave_info->bits_per_sample, interleave_info->src_num);
interleave->src_num = interleave_info->src_num;
interleave->bits_per_sample = interleave_info->bits_per_sample;
interleave->need_reopen = false;
ESP_LOGD(TAG, "Open, %p", self);
return ESP_GMF_JOB_ERR_OK;
}
static esp_gmf_job_err_t esp_gmf_interleave_close(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_interleave_t *interleave = (esp_gmf_interleave_t *)self;
ESP_LOGD(TAG, "Closed, %p", self);
if (interleave->in_arr != NULL) {
esp_gmf_oal_free(interleave->in_arr);
interleave->in_arr = NULL;
}
if (interleave->in_load != NULL) {
esp_gmf_oal_free(interleave->in_load);
interleave->in_load = NULL;
}
return ESP_GMF_ERR_OK;
}
static esp_gmf_job_err_t esp_gmf_interleave_process(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_interleave_t *interleave = (esp_gmf_interleave_t *)self;
esp_gmf_job_err_t out_len = ESP_GMF_JOB_ERR_OK;
if (interleave->need_reopen) {
esp_gmf_interleave_close(self, NULL);
out_len = esp_gmf_interleave_open(self, NULL);
if (out_len != ESP_GMF_JOB_ERR_OK) {
ESP_LOGE(TAG, "Interleave reopen failed");
return out_len;
}
}
esp_gmf_port_handle_t in = ESP_GMF_ELEMENT_GET(self)->in;
esp_gmf_port_handle_t in_port = in;
esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out;
esp_gmf_interleave_cfg *interleave_info = (esp_gmf_interleave_cfg *)OBJ_GET_CFG(self);
ESP_GMF_NULL_CHECK(TAG, interleave_info, return ESP_GMF_JOB_ERR_FAIL);
int index = interleave_info->src_num;
int i = 0;
bool is_done = false;
esp_gmf_err_io_t load_ret = ESP_GMF_IO_OK;
memset(interleave->in_load, 0, sizeof(esp_gmf_payload_t *) * index);
interleave->out_load = NULL;
int samples_num = ESP_GMF_ELEMENT_GET(interleave)->in_attr.data_size / (interleave->bytes_per_sample);
int bytes = samples_num * interleave->bytes_per_sample;
while (in_port != NULL) {
load_ret = esp_gmf_port_acquire_in(in_port, &(interleave->in_load[i]), bytes, in_port->wait_ticks);
ESP_GMF_PORT_CHECK(TAG, load_ret, out_len, {out_len = ESP_GMF_JOB_ERR_FAIL; goto __intlv_release;}, "Failed to acquire in, idx:%d, ret: %d", i, load_ret);
interleave->in_arr[i] = interleave->in_load[i]->buf;
in_port = in_port->next;
// if one load is done means interleave need to done
is_done = (((is_done == 1) || (interleave->in_load[i]->is_done == 1)) ? true : false);
ESP_LOGV(TAG, "IN: idx: %d load: %p, buf: %p, valid size: %d, buf length: %d, done: %d",
i, interleave->in_load[i], interleave->in_load[i]->buf, interleave->in_load[i]->valid_size,
interleave->in_load[i]->buf_length, interleave->in_load[i]->is_done);
i++;
}
samples_num = interleave->in_load[0]->valid_size / (interleave->bytes_per_sample);
bytes = samples_num * interleave->bytes_per_sample;
load_ret = esp_gmf_port_acquire_out(out_port, &interleave->out_load,
samples_num ? bytes * interleave->src_num : interleave->in_load[0]->buf_length, ESP_GMF_MAX_DELAY);
ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, out_len, { goto __intlv_release;});
if (samples_num > 0) {
esp_ae_err_t ret = esp_ae_intlv_process(interleave->src_num, interleave->bits_per_sample, samples_num,
(void **)interleave->in_arr, interleave->out_load->buf);
ESP_GMF_RET_ON_ERROR(TAG, ret, {out_len = ESP_GMF_JOB_ERR_FAIL; goto __intlv_release;}, "Interleave process error, ret: %d", ret);
}
ESP_LOGV(TAG, "OUT: load: %p, buf: %p, valid size: %d, buf length: %d",
interleave->out_load, interleave->out_load->buf, interleave->out_load->valid_size, interleave->out_load->buf_length);
interleave->out_load->valid_size = samples_num * interleave->bytes_per_sample * interleave->src_num;
if (interleave->out_load->valid_size > 0) {
esp_gmf_audio_el_update_file_pos((esp_gmf_element_handle_t)self, interleave->out_load->valid_size);
}
// Interleave finished
if (is_done == true) {
ESP_LOGD(TAG, "Interleave is done.");
interleave->out_load->is_done = true;
out_len = ESP_GMF_JOB_ERR_DONE;
}
__intlv_release:
if (interleave->out_load != NULL) {
load_ret = esp_gmf_port_release_out(out_port, interleave->out_load, ESP_GMF_MAX_DELAY);
if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) {
ESP_LOGE(TAG, "OUT port release error, ret:%d", load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
}
}
in_port = in;
i = 0;
while (in_port != NULL && interleave->in_load[i] != NULL) {
load_ret = esp_gmf_port_release_in(in_port, interleave->in_load[i], ESP_GMF_MAX_DELAY);
if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) {
ESP_LOGE(TAG, "IN port release error, ret:%d", load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
}
in_port = in_port->next;
i++;
}
return out_len;
}
static esp_gmf_err_t interleave_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx)
{
ESP_GMF_NULL_CHECK(TAG, ctx, {return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, evt, {return ESP_GMF_ERR_INVALID_ARG;});
if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO)
|| (evt->sub != ESP_GMF_INFO_SOUND)
|| (evt->payload == NULL)) {
return ESP_GMF_ERR_OK;
}
esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx;
esp_gmf_element_handle_t el = evt->from;
esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE;
esp_gmf_element_get_state(self, &state);
esp_gmf_info_sound_t *info = (esp_gmf_info_sound_t *)evt->payload;
esp_gmf_interleave_cfg *config = (esp_gmf_interleave_cfg *)OBJ_GET_CFG(self);
ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_FAIL);
esp_gmf_interleave_t *interleave = (esp_gmf_interleave_t *)self;
interleave->need_reopen = (config->sample_rate != info->sample_rates) || (config->bits_per_sample != info->bits);
config->sample_rate = info->sample_rates;
config->bits_per_sample = info->bits;
ESP_LOGD(TAG, "RECV element info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, rate: %d, ch: %d, bits: %d",
OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type,
esp_gmf_event_get_state_str(state), info->sample_rates, info->channels, info->bits);
if (state == ESP_GMF_EVENT_STATE_NONE) {
esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED);
}
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t esp_gmf_interleave_destroy(esp_gmf_element_handle_t self)
{
esp_gmf_interleave_t *interleave = (esp_gmf_interleave_t *)self;
ESP_LOGD(TAG, "Destroyed, %p", self);
free_esp_ae_interleave_cfg(OBJ_GET_CFG(self));
esp_gmf_audio_el_deinit(self);
esp_gmf_oal_free(interleave);
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t _load_interleave_caps_func(esp_gmf_element_handle_t handle)
{
esp_gmf_cap_t *caps = NULL;
esp_gmf_cap_t dec_caps = {0};
dec_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_INTERLEAVE;
dec_caps.attr_fun = NULL;
int ret = esp_gmf_cap_append(&caps, &dec_caps);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to create capability");
esp_gmf_element_t *el = (esp_gmf_element_t *)handle;
el->caps = caps;
return ESP_GMF_ERR_OK;
}
esp_gmf_err_t esp_gmf_interleave_init(esp_gmf_interleave_cfg *config, esp_gmf_element_handle_t *handle)
{
ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;});
*handle = NULL;
esp_gmf_err_t ret = ESP_GMF_ERR_OK;
esp_gmf_interleave_t *interleave = esp_gmf_oal_calloc(1, sizeof(esp_gmf_interleave_t));
ESP_GMF_MEM_VERIFY(TAG, interleave, {return ESP_GMF_ERR_MEMORY_LACK;}, "interleave", sizeof(esp_gmf_interleave_t));
esp_gmf_obj_t *obj = (esp_gmf_obj_t *)interleave;
obj->new_obj = esp_gmf_interleave_new;
obj->del_obj = esp_gmf_interleave_destroy;
esp_gmf_interleave_cfg *cfg = esp_gmf_oal_calloc(1, sizeof(esp_gmf_interleave_cfg));
ESP_GMF_MEM_VERIFY(TAG, cfg, {ret = ESP_GMF_ERR_MEMORY_LACK; goto INTLV_INIT_FAIL;}, "interleave configuration", sizeof(esp_gmf_interleave_cfg));
esp_gmf_obj_set_config(obj, cfg, sizeof(esp_gmf_interleave_cfg));
if (config) {
memcpy(cfg, config, sizeof(esp_gmf_interleave_cfg));
} else {
esp_gmf_interleave_cfg dcfg = DEFAULT_ESP_GMF_INTERLEAVE_CONFIG();
memcpy(cfg, &dcfg, sizeof(esp_gmf_interleave_cfg));
}
ret = esp_gmf_obj_set_tag(obj, "aud_intlv");
ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto INTLV_INIT_FAIL, "Failed to set obj tag");
esp_gmf_element_cfg_t el_cfg = {0};
ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_MULTI, 0, 0,
ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT);
ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0,
ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT);
el_cfg.dependency = true;
ret = esp_gmf_audio_el_init(interleave, &el_cfg);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto INTLV_INIT_FAIL, "Failed to initialize interleave element");
ESP_GMF_ELEMENT_GET(interleave)->ops.open = esp_gmf_interleave_open;
ESP_GMF_ELEMENT_GET(interleave)->ops.process = esp_gmf_interleave_process;
ESP_GMF_ELEMENT_GET(interleave)->ops.close = esp_gmf_interleave_close;
ESP_GMF_ELEMENT_GET(interleave)->ops.event_receiver = interleave_received_event_handler;
ESP_GMF_ELEMENT_GET(interleave)->ops.load_caps = _load_interleave_caps_func;
*handle = obj;
ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj);
return ESP_GMF_ERR_OK;
INTLV_INIT_FAIL:
esp_gmf_interleave_destroy(obj);
return ret;
}
@@ -0,0 +1,437 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#include <string.h>
#include "esp_log.h"
#include "esp_gmf_oal_mem.h"
#include "esp_gmf_oal_mutex.h"
#include "esp_gmf_node.h"
#include "esp_gmf_mixer.h"
#include "esp_heap_caps.h"
#include "gmf_audio_common.h"
#include "esp_gmf_audio_methods_def.h"
#include "esp_gmf_cap.h"
#include "esp_gmf_caps_def.h"
#include "esp_gmf_audio_element.h"
#ifdef MIXER_DEBUG
#include "esp_timer.h"
#endif
#define MIXER_DEFAULT_PROC_TIME_MS (10)
#define MIXER_GET_FRAME_BYTE_SIZE(info) (MIXER_DEFAULT_PROC_TIME_MS * info->sample_rate * info->channel * info->bits_per_sample / 8000)
typedef struct {
esp_gmf_audio_element_t parent; /*!< The GMF mixer handle */
esp_ae_mixer_handle_t mixer_hd; /*!< The audio effects mixer handle */
uint8_t bytes_per_sample; /*!< Bytes number of per sampling point */
uint32_t process_num; /*!< The data number of mixer processing */
uint32_t frame_time; /*!< The time of one frame, unit: ms */
esp_gmf_payload_t **in_load; /*!< The array of input payload */
esp_gmf_payload_t *out_load; /*!< The output payload */
uint8_t **in_arr; /*!< The input buffer pointer array of mixer */
esp_ae_mixer_mode_t *mode; /*!< The mixer mode */
uint8_t src_num; /*!< The number of source */
bool need_reopen : 1; /*!< Whether need to reopen.
True: Execute the close function first, then execute the open function
False: Do nothing */
} esp_gmf_mixer_t;
static const char *TAG = "ESP_GMF_MIXER";
const esp_ae_mixer_info_t esp_gmf_default_mixer_src_info[] = {
{1.0, 0.5, 500},
{0.5, 0.0, 500},
};
static inline esp_gmf_err_t dupl_esp_ae_mixer_cfg(esp_ae_mixer_cfg_t *config, esp_ae_mixer_cfg_t **new_config)
{
void *sub_cfg = NULL;
*new_config = esp_gmf_oal_calloc(1, sizeof(*config));
ESP_GMF_MEM_VERIFY(TAG, *new_config, {return ESP_GMF_ERR_MEMORY_LACK;}, "mixer configuration", sizeof(*config));
memcpy(*new_config, config, sizeof(*config));
if (config->src_info && (config->src_num > 0)) {
sub_cfg = esp_gmf_oal_calloc(1, config->src_num * sizeof(esp_ae_mixer_info_t));
ESP_GMF_MEM_VERIFY(TAG, sub_cfg, {esp_gmf_oal_free(*new_config); return ESP_GMF_ERR_MEMORY_LACK;},
"source information", config->src_num * sizeof(esp_ae_mixer_info_t));
memcpy(sub_cfg, config->src_info, config->src_num * sizeof(esp_ae_mixer_info_t));
(*new_config)->src_info = sub_cfg;
}
return ESP_GMF_JOB_ERR_OK;
}
static inline void free_esp_ae_mixer_cfg(esp_ae_mixer_cfg_t *config)
{
if (config) {
if (config->src_info && (config->src_info != esp_gmf_default_mixer_src_info)) {
esp_gmf_oal_free(config->src_info);
}
esp_gmf_oal_free(config);
}
}
static esp_gmf_err_t __mixer_set_mode(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc,
uint8_t *buf, int buf_len)
{
ESP_GMF_NULL_CHECK(TAG, arg_desc, {return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;});
uint8_t src_idx = (uint8_t)(*buf);
return esp_gmf_mixer_set_mode(handle, src_idx, *((esp_ae_mixer_mode_t *)(buf + arg_desc->next->offset)));
}
static esp_gmf_err_t __mixer_set_audio_info(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc,
uint8_t *buf, int buf_len)
{
ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, arg_desc, {return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;});
esp_gmf_args_desc_t *mix_desc = arg_desc;
uint32_t rate = *((uint32_t *)buf);
mix_desc = mix_desc->next;
uint8_t ch = (uint8_t)(*(buf + mix_desc->offset));
mix_desc = mix_desc->next;
uint8_t bits = (uint8_t)(*(buf + mix_desc->offset));
return esp_gmf_mixer_set_audio_info(handle, rate, bits, ch);
}
static esp_gmf_err_t esp_gmf_mixer_new(void *cfg, esp_gmf_obj_handle_t *handle)
{
return esp_gmf_mixer_init(cfg, (esp_gmf_element_handle_t *)handle);
}
static esp_gmf_job_err_t esp_gmf_mixer_open(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_mixer_t *mixer = (esp_gmf_mixer_t *)self;
esp_ae_mixer_cfg_t *mixer_info = (esp_ae_mixer_cfg_t *)OBJ_GET_CFG(self);
ESP_GMF_NULL_CHECK(TAG, mixer_info, {return ESP_GMF_JOB_ERR_FAIL;})
mixer->bytes_per_sample = (mixer_info->bits_per_sample >> 3) * mixer_info->channel;
esp_ae_mixer_open(mixer_info, &mixer->mixer_hd);
ESP_GMF_CHECK(TAG, mixer->mixer_hd, {return ESP_GMF_JOB_ERR_FAIL;}, "Failed to create mixer handle");
for (int i = 0; i < mixer_info->src_num; i++) {
esp_ae_mixer_set_mode(mixer->mixer_hd, i, mixer->mode[i]);
}
uint32_t process_num = MIXER_GET_FRAME_BYTE_SIZE(mixer_info);
mixer->process_num = (ESP_GMF_ELEMENT_GET(mixer)->in_attr.data_size == 0) ? (process_num) : (ESP_GMF_ELEMENT_GET(mixer)->in_attr.data_size);
mixer->frame_time = GMF_AUDIO_CALC_PTS(mixer->process_num, mixer_info->sample_rate, mixer_info->channel, mixer_info->bits_per_sample);
mixer->src_num = mixer_info->src_num;
mixer->in_load = esp_gmf_oal_calloc(1, sizeof(esp_gmf_payload_t *) * mixer_info->src_num);
ESP_GMF_MEM_VERIFY(TAG, mixer->in_load, {return ESP_GMF_JOB_ERR_FAIL;},
"in load", sizeof(esp_gmf_payload_t *) * mixer_info->src_num);
mixer->in_arr = esp_gmf_oal_calloc(1, sizeof(int *) * mixer_info->src_num);
ESP_GMF_MEM_VERIFY(TAG, mixer->in_arr, {return ESP_GMF_JOB_ERR_FAIL;},
"in buffer array", sizeof(int *) * mixer_info->src_num);
GMF_AUDIO_UPDATE_SND_INFO(self, mixer_info->sample_rate, mixer_info->bits_per_sample, mixer_info->channel);
mixer->need_reopen = false;
ESP_LOGD(TAG, "Open, %p", self);
return ESP_GMF_JOB_ERR_OK;
}
static esp_gmf_job_err_t esp_gmf_mixer_close(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_mixer_t *mixer = (esp_gmf_mixer_t *)self;
ESP_LOGD(TAG, "Closed, %p", self);
if (mixer->mixer_hd != NULL) {
esp_ae_mixer_close(mixer->mixer_hd);
mixer->mixer_hd = NULL;
}
if (mixer->in_arr != NULL) {
esp_gmf_oal_free(mixer->in_arr);
mixer->in_arr = NULL;
}
if (mixer->in_load != NULL) {
esp_gmf_oal_free(mixer->in_load);
mixer->in_load = NULL;
}
return ESP_GMF_ERR_OK;
}
static esp_gmf_job_err_t esp_gmf_mixer_process(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_mixer_t *mixer = (esp_gmf_mixer_t *)self;
esp_gmf_job_err_t out_len = ESP_GMF_JOB_ERR_OK;
if (mixer->need_reopen) {
esp_gmf_mixer_close(self, NULL);
out_len = esp_gmf_mixer_open(self, NULL);
if (out_len != ESP_GMF_JOB_ERR_OK) {
ESP_LOGE(TAG, "Mixer reopen failed");
return out_len;
}
}
int read_len = 0;
esp_gmf_err_io_t ret = ESP_GMF_IO_OK;
int status_end = 0;
ESP_GMF_RET_ON_NOT_OK(TAG, ret, { return ESP_GMF_JOB_ERR_FAIL;}, "Failed to apply mixer setting");
esp_gmf_port_handle_t in = ESP_GMF_ELEMENT_GET(self)->in;
esp_gmf_port_handle_t in_port = in;
esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out;
int index = mixer->src_num;
memset(mixer->in_load, 0, sizeof(esp_gmf_payload_t *) * index);
mixer->out_load = NULL;
int i = 0;
#ifdef MIXER_DEBUG
uint64_t start = 0;
uint64_t end = 0;
uint64_t acquire_in_time = 0;
uint32_t frame_time = mixer->frame_time;
#endif
while (in_port != NULL) {
#ifdef MIXER_DEBUG
start = esp_timer_get_time();
#endif
int wait_ticks = ((in_port->wait_ticks < (mixer->frame_time / index)) ? (mixer->frame_time / index) : (in_port->wait_ticks));
ret = esp_gmf_port_acquire_in(in_port, &(mixer->in_load[i]), mixer->process_num, wait_ticks);
#ifdef MIXER_DEBUG
end = esp_timer_get_time();
acquire_in_time += ((end - start) / 1000);
ESP_LOGD(TAG, "Port %d acquire in time: %lld ms, wait ticks: %d", i, (end - start) / 1000, wait_ticks);
#endif
if (ret == ESP_GMF_IO_FAIL || mixer->in_load[i]->buf == NULL) {
ESP_LOGE(TAG, "Acquire in failed, idx:%d, ret: %d", i, ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
goto __mixer_release;
}
if (ret == ESP_GMF_IO_TIMEOUT || ret == ESP_GMF_IO_ABORT || mixer->in_load[i]->is_done) {
status_end++;
}
read_len = mixer->in_load[i]->valid_size;
mixer->in_arr[i] = mixer->in_load[i]->buf;
if (read_len < mixer->process_num) {
memset(mixer->in_arr[i] + read_len, 0, mixer->process_num - read_len);
}
in_port = in_port->next;
ESP_LOGV(TAG, "IN: idx: %d load: %p, buf: %p, valid size: %d, buf length: %d, done: %d",
i, mixer->in_load[i], mixer->in_load[i]->buf, mixer->in_load[i]->valid_size,
mixer->in_load[i]->buf_length, mixer->in_load[i]->is_done);
i++;
}
#ifdef MIXER_DEBUG
if (acquire_in_time > frame_time) {
ESP_LOGW(TAG, "Total acquire in time: %lld ms, frame time: %ld ms", acquire_in_time, frame_time);
}
#endif
ret = esp_gmf_port_acquire_out(out_port, &mixer->out_load, mixer->process_num, ESP_GMF_MAX_DELAY);
ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, ret, out_len, { goto __mixer_release;});
// Down-mixer never stop in gmf, only user can set to stop
if (status_end == index) {
memset(mixer->out_load->buf, 0, mixer->process_num);
out_len = ESP_GMF_JOB_ERR_OK;
goto __mixer_release;
}
esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)self)->lock);
esp_ae_err_t porc_ret = esp_ae_mixer_process(mixer->mixer_hd, mixer->process_num / mixer->bytes_per_sample,
(void *)mixer->in_arr, mixer->out_load->buf);
esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)self)->lock);
if (porc_ret != ESP_AE_ERR_OK) {
ESP_LOGE(TAG, "Mix process error %d.", porc_ret);
return ESP_GMF_JOB_ERR_FAIL;
}
ESP_LOGV(TAG, "OUT: load: %p, buf: %p, valid size: %d, buf length: %d",
mixer->out_load, mixer->out_load->buf, mixer->out_load->valid_size, mixer->out_load->buf_length);
mixer->out_load->valid_size = mixer->process_num;
if (mixer->out_load->valid_size > 0) {
esp_gmf_audio_el_update_file_pos((esp_gmf_element_handle_t)self, mixer->out_load->valid_size);
}
__mixer_release:
// Release in and out port
if (mixer->out_load != NULL) {
ret = esp_gmf_port_release_out(out_port, mixer->out_load, ESP_GMF_MAX_DELAY);
if ((ret < ESP_GMF_IO_OK) && (ret != ESP_GMF_IO_ABORT)) {
ESP_LOGE(TAG, "OUT port release error, ret:%d", ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
}
}
in_port = in;
i = 0;
while (in_port != NULL && mixer->in_load[i] != NULL) {
ret = esp_gmf_port_release_in(in_port, mixer->in_load[i], ESP_GMF_MAX_DELAY);
if ((ret < ESP_GMF_IO_OK) && (ret != ESP_GMF_IO_ABORT)) {
ESP_LOGE(TAG, "IN port release error, ret:%d", ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
}
in_port = in_port->next;
i++;
}
return out_len;
}
static esp_gmf_err_t mixer_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx)
{
ESP_GMF_NULL_CHECK(TAG, ctx, {return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, evt, {return ESP_GMF_ERR_INVALID_ARG;});
if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO)
|| (evt->sub != ESP_GMF_INFO_SOUND)
|| (evt->payload == NULL)) {
return ESP_GMF_ERR_OK;
}
esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx;
esp_gmf_element_handle_t el = evt->from;
esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE;
esp_gmf_element_get_state(self, &state);
esp_gmf_info_sound_t *info = (esp_gmf_info_sound_t *)evt->payload;
esp_ae_mixer_cfg_t *config = (esp_ae_mixer_cfg_t *)OBJ_GET_CFG(self);
ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_FAIL);
esp_gmf_mixer_t *mixer = (esp_gmf_mixer_t *)self;
mixer->need_reopen = (config->sample_rate != info->sample_rates) || (info->channels != config->channel) || (config->bits_per_sample != info->bits);
config->sample_rate = info->sample_rates;
config->channel = info->channels;
config->bits_per_sample = info->bits;
ESP_LOGD(TAG, "RECV element info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, rate: %d, ch: %d, bits: %d",
OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type,
esp_gmf_event_get_state_str(state), info->sample_rates, info->channels, info->bits);
if (state == ESP_GMF_EVENT_STATE_NONE) {
esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED);
}
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t esp_gmf_mixer_destroy(esp_gmf_element_handle_t self)
{
esp_gmf_mixer_t *mixer = (esp_gmf_mixer_t *)self;
ESP_LOGD(TAG, "Destroyed, %p", self);
free_esp_ae_mixer_cfg(OBJ_GET_CFG(self));
if (mixer->mode) {
esp_gmf_oal_free(mixer->mode);
}
esp_gmf_audio_el_deinit(self);
esp_gmf_oal_free(mixer);
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t _load_mixer_caps_func(esp_gmf_element_handle_t handle)
{
esp_gmf_cap_t *caps = NULL;
esp_gmf_cap_t dec_caps = {0};
dec_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_MIXER;
dec_caps.attr_fun = NULL;
int ret = esp_gmf_cap_append(&caps, &dec_caps);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to create capability");
esp_gmf_element_t *el = (esp_gmf_element_t *)handle;
el->caps = caps;
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t _load_mixer_methods_func(esp_gmf_element_handle_t handle)
{
esp_gmf_method_t *method = NULL;
esp_gmf_args_desc_t *set_args = NULL;
esp_gmf_err_t ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(MIXER, SET_INFO, RATE), ESP_GMF_ARGS_TYPE_UINT32, sizeof(uint32_t), 0);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append RATE argument");
ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(MIXER, SET_INFO, CH), ESP_GMF_ARGS_TYPE_UINT8,
sizeof(uint8_t), sizeof(uint32_t));
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append CHANNEL argument");
ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(MIXER, SET_INFO, BITS), ESP_GMF_ARGS_TYPE_UINT8,
sizeof(uint8_t), sizeof(uint8_t) + sizeof(uint32_t));
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append BITS argument");
ret = esp_gmf_method_append(&method, AMETHOD(MIXER, SET_INFO), __mixer_set_audio_info, set_args);
ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(MIXER, SET_INFO));
set_args = NULL;
ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(MIXER, SET_MODE, IDX), ESP_GMF_ARGS_TYPE_UINT8, sizeof(uint8_t), 0);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append INDEX argument");
ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(MIXER, SET_MODE, MODE), ESP_GMF_ARGS_TYPE_INT32,
sizeof(int32_t), sizeof(uint8_t));
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append MODE argument");
ret = esp_gmf_method_append(&method, AMETHOD(MIXER, SET_MODE), __mixer_set_mode, set_args);
ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(MIXER, SET_MODE));
esp_gmf_element_t *el = (esp_gmf_element_t *)handle;
el->method = method;
return ESP_GMF_ERR_OK;
}
esp_gmf_err_t esp_gmf_mixer_set_mode(esp_gmf_element_handle_t handle, uint8_t src_idx, esp_ae_mixer_mode_t mode)
{
ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;});
esp_gmf_mixer_t *mixer = (esp_gmf_mixer_t *)handle;
esp_ae_mixer_cfg_t *cfg = (esp_ae_mixer_cfg_t *)OBJ_GET_CFG(handle);
ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_ERR_FAIL);
if (src_idx >= cfg->src_num) {
ESP_LOGE(TAG, "Source index %d overlimit %d hd:%p", src_idx, cfg->src_num, mixer);
return ESP_GMF_ERR_INVALID_ARG;
}
if (mixer->mixer_hd) {
esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)handle)->lock);
esp_ae_err_t ret = esp_ae_mixer_set_mode(mixer->mixer_hd, src_idx, mode);
esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)handle)->lock);
ESP_GMF_RET_ON_ERROR(TAG, ret, return ESP_GMF_JOB_ERR_FAIL;, "mixerualize set error %d", ret);
}
mixer->mode[src_idx] = mode;
return ESP_GMF_ERR_OK;
}
esp_gmf_err_t esp_gmf_mixer_set_audio_info(esp_gmf_element_handle_t handle, uint32_t sample_rate,
uint8_t bits, uint8_t channel)
{
ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;});
esp_ae_mixer_cfg_t *cfg = (esp_ae_mixer_cfg_t *)OBJ_GET_CFG(handle);
ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_ERR_FAIL);
if (cfg->sample_rate == sample_rate && cfg->bits_per_sample == bits && cfg->channel == channel) {
return ESP_GMF_ERR_OK;
}
cfg->sample_rate = sample_rate;
cfg->bits_per_sample = bits;
cfg->channel = channel;
esp_gmf_mixer_t *mixer = (esp_gmf_mixer_t *)handle;
mixer->need_reopen = true;
return ESP_GMF_ERR_OK;
}
esp_gmf_err_t esp_gmf_mixer_init(esp_ae_mixer_cfg_t *config, esp_gmf_element_handle_t *handle)
{
ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;});
*handle = NULL;
esp_gmf_err_t ret = ESP_GMF_ERR_OK;
esp_gmf_mixer_t *mixer = esp_gmf_oal_calloc(1, sizeof(esp_gmf_mixer_t));
ESP_GMF_MEM_VERIFY(TAG, mixer, {return ESP_GMF_ERR_MEMORY_LACK;}, "mixer", sizeof(esp_gmf_mixer_t));
esp_gmf_obj_t *obj = (esp_gmf_obj_t *)mixer;
obj->new_obj = esp_gmf_mixer_new;
obj->del_obj = esp_gmf_mixer_destroy;
esp_ae_mixer_cfg_t *cfg = NULL;
if (config) {
if (config->src_info == NULL) {
config->src_info = (esp_ae_mixer_info_t *)esp_gmf_default_mixer_src_info;
config->src_num = sizeof(esp_gmf_default_mixer_src_info) / sizeof(esp_ae_mixer_info_t);
}
dupl_esp_ae_mixer_cfg(config, &cfg);
} else {
esp_ae_mixer_cfg_t dcfg = DEFAULT_ESP_GMF_MIXER_CONFIG();
dcfg.src_info = (esp_ae_mixer_info_t *)esp_gmf_default_mixer_src_info;
dcfg.src_num = sizeof(esp_gmf_default_mixer_src_info) / sizeof(esp_ae_mixer_info_t);
dupl_esp_ae_mixer_cfg(&dcfg, &cfg);
}
ESP_GMF_CHECK(TAG, cfg, ret = ESP_GMF_ERR_MEMORY_LACK; goto MIXER_INIT_FAIL;, "Failed to allocate mixer configuration");
esp_gmf_obj_set_config(obj, cfg, sizeof(esp_ae_mixer_cfg_t));
mixer->mode = esp_gmf_oal_calloc(cfg->src_num, sizeof(esp_ae_mixer_mode_t));
ESP_GMF_MEM_VERIFY(TAG, mixer->mode, {ret = ESP_GMF_ERR_MEMORY_LACK; goto MIXER_INIT_FAIL;}, "Allocate(%d) failed", cfg->src_num * sizeof(esp_ae_mixer_mode_t));
for (int i = 0; i < cfg->src_num; i++) {
mixer->mode[i] = ESP_AE_MIXER_MODE_FADE_UPWARD;
}
ret = esp_gmf_obj_set_tag(obj, "aud_mixer");
ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto MIXER_INIT_FAIL, "Failed to set obj tag");
esp_gmf_element_cfg_t el_cfg = {0};
mixer->process_num = MIXER_GET_FRAME_BYTE_SIZE(cfg);
ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_MULTI, 0, 0,
ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, mixer->process_num);
ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0,
ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, mixer->process_num);
el_cfg.dependency = true;
ret = esp_gmf_audio_el_init(mixer, &el_cfg);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto MIXER_INIT_FAIL, "Failed to initialize mixer element");
ESP_GMF_ELEMENT_GET(mixer)->ops.open = esp_gmf_mixer_open;
ESP_GMF_ELEMENT_GET(mixer)->ops.process = esp_gmf_mixer_process;
ESP_GMF_ELEMENT_GET(mixer)->ops.close = esp_gmf_mixer_close;
ESP_GMF_ELEMENT_GET(mixer)->ops.event_receiver = mixer_received_event_handler;
ESP_GMF_ELEMENT_GET(mixer)->ops.load_caps = _load_mixer_caps_func;
ESP_GMF_ELEMENT_GET(mixer)->ops.load_methods = _load_mixer_methods_func;
*handle = obj;
ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj);
return ESP_GMF_ERR_OK;
MIXER_INIT_FAIL:
esp_gmf_mixer_destroy(obj);
return ret;
}
@@ -0,0 +1,276 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#include <string.h>
#include "esp_log.h"
#include "esp_gmf_oal_mem.h"
#include "esp_gmf_oal_mutex.h"
#include "esp_gmf_node.h"
#include "esp_gmf_rate_cvt.h"
#include "gmf_audio_common.h"
#include "esp_gmf_audio_methods_def.h"
#include "esp_gmf_cap.h"
#include "esp_gmf_caps_def.h"
#include "esp_gmf_audio_element.h"
/**
* @brief Audio rate conversion context in GMF
*/
typedef struct {
esp_gmf_audio_element_t parent; /*!< The GMF rate cvt handle */
esp_ae_rate_cvt_handle_t rate_hd; /*!< The audio effects rate cvt handle */
uint8_t bytes_per_sample; /*!< Bytes number of per sampling point */
bool need_reopen : 1; /*!< Whether need to reopen.
True: Execute the close function first, then execute the open function
False: Do nothing */
bool bypass : 1; /*!< Whether bypass. True: need bypass. False: needn't bypass */
} esp_gmf_rate_cvt_t;
static const char *TAG = "ESP_GMF_RATE_CVT";
static esp_gmf_err_t __rate_cvt_set_dest_rate(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc,
uint8_t *buf, int buf_len)
{
ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;});
uint32_t dest_rate = *((uint32_t *)buf);
return esp_gmf_rate_cvt_set_dest_rate(handle, dest_rate);
}
static esp_gmf_err_t esp_gmf_rate_cvt_new(void *cfg, esp_gmf_obj_handle_t *handle)
{
return esp_gmf_rate_cvt_init(cfg, (esp_gmf_element_handle_t *)handle);
}
static esp_gmf_job_err_t esp_gmf_rate_cvt_open(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_rate_cvt_t *rate_cvt = (esp_gmf_rate_cvt_t *)self;
esp_ae_rate_cvt_cfg_t *rate_info = (esp_ae_rate_cvt_cfg_t *)OBJ_GET_CFG(self);
ESP_GMF_NULL_CHECK(TAG, rate_info, {return ESP_GMF_JOB_ERR_FAIL;});
rate_cvt->bytes_per_sample = (rate_info->bits_per_sample >> 3) * rate_info->channel;
esp_ae_rate_cvt_open(rate_info, &rate_cvt->rate_hd);
ESP_GMF_CHECK(TAG, rate_cvt->rate_hd, {return ESP_GMF_JOB_ERR_FAIL;}, "Failed to create rate conversion handle");
GMF_AUDIO_UPDATE_SND_INFO(self, rate_info->dest_rate, rate_info->bits_per_sample, rate_info->channel);
ESP_LOGD(TAG, "Open, src: %"PRIu32", dest: %"PRIu32", ch: %d, bits: %d",
rate_info->src_rate, rate_info->dest_rate, rate_info->channel, rate_info->bits_per_sample);
rate_cvt->need_reopen = false;
rate_cvt->bypass = rate_info->src_rate == rate_info->dest_rate;
return ESP_GMF_JOB_ERR_OK;
}
static esp_gmf_job_err_t esp_gmf_rate_cvt_close(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_rate_cvt_t *rate_cvt = (esp_gmf_rate_cvt_t *)self;
ESP_LOGD(TAG, "Closed, %p", self);
if (rate_cvt->rate_hd != NULL) {
esp_ae_rate_cvt_close(rate_cvt->rate_hd);
rate_cvt->rate_hd = NULL;
}
return ESP_GMF_JOB_ERR_OK;
}
static esp_gmf_job_err_t esp_gmf_rate_cvt_process(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_rate_cvt_t *rate_cvt = (esp_gmf_rate_cvt_t *)self;
esp_gmf_job_err_t out_len = ESP_GMF_JOB_ERR_OK;
if (rate_cvt->need_reopen) {
esp_gmf_rate_cvt_close(self, NULL);
out_len = esp_gmf_rate_cvt_open(self, NULL);
if (out_len != ESP_GMF_JOB_ERR_OK) {
ESP_LOGE(TAG, "Rate conversion reopen failed");
return out_len;
}
}
esp_ae_err_t ret = ESP_AE_ERR_OK;
esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in;
esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out;
esp_gmf_payload_t *in_load = NULL;
esp_gmf_payload_t *out_load = NULL;
int samples_num = ESP_GMF_ELEMENT_GET(rate_cvt)->in_attr.data_size / (rate_cvt->bytes_per_sample);
int bytes = samples_num * rate_cvt->bytes_per_sample;
esp_gmf_err_io_t load_ret = esp_gmf_port_acquire_in(in_port, &in_load, bytes, ESP_GMF_MAX_DELAY);
samples_num = in_load->valid_size / (rate_cvt->bytes_per_sample);
bytes = samples_num * rate_cvt->bytes_per_sample;
if((bytes != in_load->valid_size) || (load_ret < ESP_GMF_IO_OK)) {
ESP_LOGE(TAG, "Invalid in load size %d, ret %d", in_load->valid_size, load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
goto __rate_release;
}
uint32_t out_samples_num = 0;
if (samples_num) {
ret = esp_ae_rate_cvt_get_max_out_sample_num(rate_cvt->rate_hd, samples_num, &out_samples_num);
ESP_GMF_RET_ON_ERROR(TAG, ret, {out_len = ESP_GMF_JOB_ERR_FAIL; goto __rate_release;}, "Failed to get resample out size, ret: %d", ret);
}
int acq_out_size = out_samples_num == 0 ? in_load->buf_length : out_samples_num * rate_cvt->bytes_per_sample;
if (rate_cvt->bypass && (in_port->is_shared == true)) {
// This case rate conversion is do bypass
out_load = in_load;
}
load_ret = esp_gmf_port_acquire_out(out_port, &out_load, acq_out_size, ESP_GMF_MAX_DELAY);
ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, out_len, {goto __rate_release;});
if (samples_num) {
ret = esp_ae_rate_cvt_process(rate_cvt->rate_hd, (unsigned char *)in_load->buf, samples_num,
(unsigned char *)out_load->buf, &out_samples_num);
ESP_GMF_RET_ON_ERROR(TAG, ret, {out_len = ESP_GMF_JOB_ERR_FAIL; goto __rate_release;}, "Rate conversion process error, ret: %d", ret);
}
out_load->valid_size = out_samples_num * rate_cvt->bytes_per_sample;
out_load->pts = in_load->pts;
out_load->is_done = in_load->is_done;
ESP_LOGV(TAG, "Out Samples: %ld, IN-PLD: %p-%p-%d-%d-%d, OUT-PLD: %p-%p-%d-%d-%d", out_samples_num, in_load, in_load->buf,
in_load->valid_size, in_load->buf_length, in_load->is_done, out_load,
out_load->buf, out_load->valid_size, out_load->buf_length, out_load->is_done);
esp_gmf_audio_el_update_file_pos((esp_gmf_element_handle_t)self, out_load->valid_size);
if (in_load->is_done) {
out_len = ESP_GMF_JOB_ERR_DONE;
ESP_LOGD(TAG, "Rate convert done, out len: %d", out_load->valid_size);
}
__rate_release:
// Release in and out port
if (out_load != NULL) {
load_ret = esp_gmf_port_release_out(out_port, out_load, ESP_GMF_MAX_DELAY);
if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) {
ESP_LOGE(TAG, "OUT port release error, ret:%d", load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
}
}
if (in_load != NULL) {
load_ret = esp_gmf_port_release_in(in_port, in_load, ESP_GMF_MAX_DELAY);
if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) {
ESP_LOGE(TAG, "IN port release error, ret:%d", load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
}
}
return out_len;
}
static esp_gmf_err_t rate_cvt_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx)
{
ESP_GMF_NULL_CHECK(TAG, ctx, {return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, evt, {return ESP_GMF_ERR_INVALID_ARG;});
if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO)
|| (evt->sub != ESP_GMF_INFO_SOUND)
|| (evt->payload == NULL)) {
return ESP_GMF_ERR_OK;
}
esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx;
esp_gmf_element_handle_t el = evt->from;
esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE;
esp_gmf_element_get_state(self, &state);
esp_gmf_info_sound_t *info = (esp_gmf_info_sound_t *)evt->payload;
esp_ae_rate_cvt_cfg_t *config = (esp_ae_rate_cvt_cfg_t *)OBJ_GET_CFG(self);
ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_FAIL);
esp_gmf_rate_cvt_t *rate_cvt = (esp_gmf_rate_cvt_t *)self;
rate_cvt->need_reopen = (config->src_rate != info->sample_rates) || (info->channels != config->channel) || (config->bits_per_sample != info->bits);
config->src_rate = info->sample_rates;
config->channel = info->channels;
config->bits_per_sample = info->bits;
ESP_LOGD(TAG, "RECV element info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, rate: %d, ch: %d, bits: %d",
OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type,
esp_gmf_event_get_state_str(state), info->sample_rates, info->channels, info->bits);
if (state == ESP_GMF_EVENT_STATE_NONE) {
esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED);
}
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t esp_gmf_rate_cvt_destroy(esp_gmf_element_handle_t self)
{
esp_gmf_rate_cvt_t *rate_cvt = (esp_gmf_rate_cvt_t *)self;
ESP_LOGD(TAG, "Destroyed, %p", self);
void *cfg = OBJ_GET_CFG(self);
if (cfg) {
esp_gmf_oal_free(cfg);
}
esp_gmf_audio_el_deinit(self);
esp_gmf_oal_free(rate_cvt);
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t _load_rate_cvt_caps_func(esp_gmf_element_handle_t handle)
{
esp_gmf_cap_t *caps = NULL;
esp_gmf_cap_t dec_caps = {0};
dec_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_RATE_CONVERT;
dec_caps.attr_fun = NULL;
int ret = esp_gmf_cap_append(&caps, &dec_caps);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to create capability");
esp_gmf_element_t *el = (esp_gmf_element_t *)handle;
el->caps = caps;
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t _load_rate_cvt_methods_func(esp_gmf_element_handle_t handle)
{
esp_gmf_method_t *method = NULL;
esp_gmf_args_desc_t *set_args = NULL;
esp_gmf_err_t ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(RATE_CVT, SET_DEST_RATE, RATE),
ESP_GMF_ARGS_TYPE_UINT32, sizeof(uint32_t), 0);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append RATE argument");
ret = esp_gmf_method_append(&method, AMETHOD(RATE_CVT, SET_DEST_RATE), __rate_cvt_set_dest_rate, set_args);
ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(RATE_CVT, SET_DEST_RATE));
esp_gmf_element_t *el = (esp_gmf_element_t *)handle;
el->method = method;
return ESP_GMF_ERR_OK;
}
esp_gmf_err_t esp_gmf_rate_cvt_set_dest_rate(esp_gmf_element_handle_t handle, uint32_t dest_rate)
{
ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;});
esp_ae_rate_cvt_cfg_t *cfg = (esp_ae_rate_cvt_cfg_t *)OBJ_GET_CFG(handle);
ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_ERR_FAIL);
if (cfg->dest_rate == dest_rate) {
return ESP_GMF_ERR_OK;
}
cfg->dest_rate = dest_rate;
esp_gmf_rate_cvt_t *rate_cvt = (esp_gmf_rate_cvt_t *)handle;
rate_cvt->need_reopen = true;
return ESP_GMF_ERR_OK;
}
esp_gmf_err_t esp_gmf_rate_cvt_init(esp_ae_rate_cvt_cfg_t *config, esp_gmf_element_handle_t *handle)
{
ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;});
*handle = NULL;
esp_gmf_err_t ret = ESP_GMF_ERR_OK;
esp_gmf_rate_cvt_t *rate_cvt = esp_gmf_oal_calloc(1, sizeof(esp_gmf_rate_cvt_t));
ESP_GMF_MEM_VERIFY(TAG, rate_cvt, {return ESP_GMF_ERR_MEMORY_LACK;}, "rate conversion", sizeof(esp_gmf_rate_cvt_t));
esp_gmf_obj_t *obj = (esp_gmf_obj_t *)rate_cvt;
obj->new_obj = esp_gmf_rate_cvt_new;
obj->del_obj = esp_gmf_rate_cvt_destroy;
esp_ae_rate_cvt_cfg_t *cfg = esp_gmf_oal_calloc(1, sizeof(esp_ae_rate_cvt_cfg_t));
ESP_GMF_MEM_VERIFY(TAG, cfg, {ret = ESP_GMF_ERR_MEMORY_LACK; goto RATE_CVT_INIT_FAIL;}, "Rate conversion configuration", sizeof(esp_ae_rate_cvt_cfg_t));
esp_gmf_obj_set_config(obj, cfg, sizeof(esp_ae_rate_cvt_cfg_t));
if (config) {
memcpy(cfg, config, sizeof(esp_ae_rate_cvt_cfg_t));
} else {
esp_ae_rate_cvt_cfg_t dcfg = DEFAULT_ESP_GMF_RATE_CVT_CONFIG();
memcpy(cfg, &dcfg, sizeof(esp_ae_rate_cvt_cfg_t));
}
ret = esp_gmf_obj_set_tag(obj, "aud_rate_cvt");
ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto RATE_CVT_INIT_FAIL, "Failed to set obj tag");
esp_gmf_element_cfg_t el_cfg = {0};
ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0,
ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT);
ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0,
ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT);
el_cfg.dependency = true;
ret = esp_gmf_audio_el_init(rate_cvt, &el_cfg);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto RATE_CVT_INIT_FAIL, "Failed to initialize rate conversion element");
ESP_GMF_ELEMENT_GET(rate_cvt)->ops.open = esp_gmf_rate_cvt_open;
ESP_GMF_ELEMENT_GET(rate_cvt)->ops.process = esp_gmf_rate_cvt_process;
ESP_GMF_ELEMENT_GET(rate_cvt)->ops.close = esp_gmf_rate_cvt_close;
ESP_GMF_ELEMENT_GET(rate_cvt)->ops.event_receiver = rate_cvt_received_event_handler;
ESP_GMF_ELEMENT_GET(rate_cvt)->ops.load_caps = _load_rate_cvt_caps_func;
ESP_GMF_ELEMENT_GET(rate_cvt)->ops.load_methods = _load_rate_cvt_methods_func;
*handle = obj;
ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj);
return ESP_GMF_ERR_OK;
RATE_CVT_INIT_FAIL:
esp_gmf_rate_cvt_destroy(obj);
return ret;
}
@@ -0,0 +1,435 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#include <string.h>
#include "esp_log.h"
#include "esp_gmf_oal_mem.h"
#include "esp_gmf_oal_mutex.h"
#include "esp_gmf_node.h"
#include "esp_gmf_sonic.h"
#include "gmf_audio_common.h"
#include "esp_gmf_audio_methods_def.h"
#include "esp_gmf_cap.h"
#include "esp_gmf_caps_def.h"
#include "esp_gmf_audio_element.h"
#define SONIC_DEFAULT_OUTPUT_TIME_MS (10)
/**
* @brief Audio sonic context in GMF
*/
typedef struct {
esp_gmf_audio_element_t parent; /*!< The GMF sonic handle */
esp_ae_sonic_handle_t sonic_hd; /*!< The audio effects sonic handle */
uint8_t bytes_per_sample; /*!< Bytes number of per sampling point */
uint32_t sample_rate; /*!< The audio sample rate */
uint8_t bits_per_sample; /*!< Bits number of per sampling point */
uint8_t channel; /*!< The audio channel */
esp_ae_sonic_in_data_t in_data_hd; /*!< The sonic input data handle */
esp_ae_sonic_out_data_t out_data_hd; /*!< The sonic output data handle */
float speed; /*!< The audio speed */
float pitch; /*!< The audio pitch */
int32_t out_size; /*!< The acquired out size */
int64_t cur_pts; /*!< The audio Presentation Time Stamp(pts) */
bool need_reopen : 1; /*!< Whether need to reopen.
True: Execute the close function first, then execute the open function
False: Do nothing */
bool is_done : 1; /*!< Whether the input data is done */
} esp_gmf_sonic_t;
static const char *TAG = "ESP_GMF_SONIC";
static esp_gmf_err_t __sonic_set_speed(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc,
uint8_t *buf, int buf_len)
{
ESP_GMF_NULL_CHECK(TAG, arg_desc, {return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;});
float *speed = (float *)buf;
return esp_gmf_sonic_set_speed(handle, *speed);
}
static esp_gmf_err_t __sonic_get_speed(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc,
uint8_t *buf, int buf_len)
{
ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;});
float *speed = (float *)buf;
return esp_gmf_sonic_get_speed(handle, speed);
}
static esp_gmf_err_t __sonic_set_pitch(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc,
uint8_t *buf, int buf_len)
{
ESP_GMF_NULL_CHECK(TAG, arg_desc, {return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;});
float *pitch = (float *)buf;
return esp_gmf_sonic_set_pitch(handle, *pitch);
}
static esp_gmf_err_t __sonic_get_pitch(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc,
uint8_t *buf, int buf_len)
{
ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;});
float *pitch = (float *)buf;
return esp_gmf_sonic_get_pitch(handle, pitch);
}
static esp_gmf_err_t esp_gmf_sonic_new(void *cfg, esp_gmf_obj_handle_t *handle)
{
return esp_gmf_sonic_init(cfg, (esp_gmf_element_handle_t *)handle);
}
static esp_gmf_job_err_t esp_gmf_sonic_open(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_sonic_t *sonic = (esp_gmf_sonic_t *)self;
esp_ae_sonic_cfg_t *sonic_info = (esp_ae_sonic_cfg_t *)OBJ_GET_CFG(self);
ESP_GMF_NULL_CHECK(TAG, sonic_info, {return ESP_GMF_JOB_ERR_FAIL;});
sonic->sample_rate = sonic_info->sample_rate;
sonic->channel = sonic_info->channel;
sonic->bits_per_sample = sonic_info->bits_per_sample;
sonic->bytes_per_sample = (sonic_info->bits_per_sample >> 3) * sonic_info->channel;
GMF_AUDIO_UPDATE_SND_INFO(self, sonic_info->sample_rate, sonic_info->bits_per_sample, sonic_info->channel);
sonic->out_size = SONIC_DEFAULT_OUTPUT_TIME_MS * sonic->sample_rate * sonic->bytes_per_sample / 1000;
esp_ae_sonic_open(sonic_info, &sonic->sonic_hd);
ESP_GMF_CHECK(TAG, sonic->sonic_hd, {return ESP_GMF_JOB_ERR_FAIL;}, "Failed to create sonic handle");
esp_ae_sonic_set_speed(sonic->sonic_hd, sonic->speed);
esp_ae_sonic_set_pitch(sonic->sonic_hd, sonic->pitch);
sonic->need_reopen = false;
ESP_LOGD(TAG, "Open, %p", self);
return ESP_GMF_JOB_ERR_OK;
}
static esp_gmf_job_err_t esp_gmf_sonic_close(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_sonic_t *sonic = (esp_gmf_sonic_t *)self;
ESP_LOGD(TAG, "Closed, %p", self);
if (sonic->sonic_hd != NULL) {
esp_ae_sonic_close(sonic->sonic_hd);
sonic->sonic_hd = NULL;
}
sonic->cur_pts = 0;
return ESP_GMF_ERR_OK;
}
static esp_gmf_job_err_t gmf_sonic_bypass_process(esp_gmf_sonic_t *sonic, esp_gmf_port_handle_t in_port, esp_gmf_port_handle_t out_port,
esp_gmf_payload_t **in_load, esp_gmf_payload_t **out_load)
{
esp_gmf_job_err_t out_len = ESP_GMF_JOB_ERR_OK;
int load_ret = esp_gmf_port_acquire_in(in_port, in_load, ESP_GMF_ELEMENT_GET(sonic)->in_attr.data_size, ESP_GMF_MAX_DELAY);
ESP_GMF_PORT_ACQUIRE_IN_CHECK(TAG, load_ret, out_len, return out_len);
sonic->is_done = (*in_load)->is_done;
if (!sonic->is_done && (*in_load)->valid_size == 0) {
return ESP_GMF_JOB_ERR_CONTINUE;
}
if (in_port->is_shared == 1) {
*out_load = *in_load;
}
size_t out_size = (*in_load)->valid_size ? (*in_load)->valid_size : ESP_GMF_ELEMENT_GET(sonic)->in_attr.data_size;
load_ret = esp_gmf_port_acquire_out(out_port, out_load, out_size, ESP_GMF_MAX_DELAY);
ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, out_len, return out_len);
if (in_port->is_shared != 1) {
memcpy((*out_load)->buf, (*in_load)->buf, (*in_load)->valid_size);
(*out_load)->valid_size = (*in_load)->valid_size;
(*out_load)->is_done = (*in_load)->is_done;
(*out_load)->pts = (*in_load)->pts;
}
return sonic->is_done ? ESP_GMF_JOB_ERR_DONE : out_len;
}
static esp_gmf_job_err_t esp_gmf_sonic_process(esp_gmf_element_handle_t self, void *para)
{
esp_gmf_sonic_t *sonic = (esp_gmf_sonic_t *)self;
esp_gmf_job_err_t out_len = ESP_GMF_JOB_ERR_OK;
esp_gmf_err_io_t load_ret = ESP_GMF_IO_OK;
if (sonic->need_reopen) {
esp_gmf_sonic_close(self, NULL);
out_len = esp_gmf_sonic_open(self, NULL);
if (out_len != ESP_GMF_JOB_ERR_OK) {
ESP_LOGE(TAG, "Sonic reopen failed");
return out_len;
}
}
esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in;
esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out;
esp_gmf_payload_t *in_load = NULL;
esp_gmf_payload_t *out_load = NULL;
bool is_done = false;
sonic->out_data_hd.needed_num = sonic->out_size / sonic->bytes_per_sample;
if (sonic->speed == 1.0f && sonic->pitch == 1.0f && (sonic->out_data_hd.out_num < sonic->out_data_hd.needed_num)) {
out_len = gmf_sonic_bypass_process(sonic, in_port, out_port, &in_load, &out_load);
goto __sonic_release;
} else {
if ((sonic->out_data_hd.out_num < sonic->out_data_hd.needed_num) && sonic->is_done == false) {
int samples_num = ESP_GMF_ELEMENT_GET(sonic)->in_attr.data_size / (sonic->bytes_per_sample);
int bytes = samples_num * sonic->bytes_per_sample;
load_ret = esp_gmf_port_acquire_in(in_port, &in_load, bytes, ESP_GMF_MAX_DELAY);
samples_num = in_load->valid_size / (sonic->bytes_per_sample);
bytes = samples_num * sonic->bytes_per_sample;
sonic->is_done = in_load->is_done;
if ((bytes != in_load->valid_size) || (load_ret < ESP_GMF_IO_OK)) {
ESP_LOGE(TAG, "Invalid in load size %d, ret %d", in_load->valid_size, load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
goto __sonic_release;
}
sonic->in_data_hd.samples = in_load->buf;
sonic->in_data_hd.num = samples_num;
sonic->cur_pts = in_load->pts;
if (sonic->in_data_hd.num == 0) {
if (sonic->is_done == true) {
out_len = ESP_GMF_JOB_ERR_DONE;
is_done = true;
} else {
out_len = ESP_GMF_JOB_ERR_CONTINUE;
goto __sonic_release;
}
}
}
load_ret = esp_gmf_port_acquire_out(out_port, &out_load, sonic->out_size, ESP_GMF_MAX_DELAY);
ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, out_len, goto __sonic_release);
sonic->out_data_hd.samples = out_load->buf;
out_load->valid_size = 0;
out_load->pts = sonic->cur_pts;
if (!is_done) {
esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)self)->lock);
esp_ae_err_t ret = esp_ae_sonic_process(sonic->sonic_hd, &sonic->in_data_hd, &sonic->out_data_hd);
esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)self)->lock);
ESP_GMF_RET_ON_ERROR(TAG, ret, {out_len = ESP_GMF_JOB_ERR_FAIL; goto __sonic_release;}, "Sonic process error %d", ret);
sonic->in_data_hd.samples = ((uint8_t *)sonic->in_data_hd.samples) + sonic->in_data_hd.consume_num * sonic->bytes_per_sample;
sonic->in_data_hd.num -= sonic->in_data_hd.consume_num;
sonic->in_data_hd.consume_num = 0;
out_load->valid_size = sonic->out_data_hd.out_num * sonic->bytes_per_sample;
if (out_load->valid_size > 0) {
esp_gmf_audio_el_update_file_pos((esp_gmf_element_handle_t)self, out_load->valid_size);
}
out_load->is_done = false;
out_load->pts = sonic->cur_pts;
sonic->cur_pts += (uint64_t)((float)(GMF_AUDIO_CALC_PTS(out_load->valid_size, sonic->sample_rate, sonic->channel, sonic->bits_per_sample)) * sonic->speed);
ESP_LOGV(TAG, "%s, I: %p-buf: %p-sz: %d, O: %p-buf: %p-sz: %d, ret: %d", __func__, in_port,
in_load->buf, in_load->valid_size, out_port,
out_load->buf, out_load->buf_length, ret);
if (sonic->out_data_hd.out_num == sonic->out_data_hd.needed_num) {
out_len = ESP_GMF_JOB_ERR_TRUNCATE;
goto __sonic_release;
}
}
if (sonic->is_done == true) {
out_load->is_done = true;
out_len = ESP_GMF_JOB_ERR_DONE;
goto __sonic_release;
}
}
__sonic_release:
if (out_load != NULL) {
load_ret = esp_gmf_port_release_out(out_port, out_load, ESP_GMF_MAX_DELAY);
if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) {
ESP_LOGE(TAG, "OUT port release error, ret:%d", load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
}
}
if (in_load != NULL) {
load_ret = esp_gmf_port_release_in(in_port, in_load, ESP_GMF_MAX_DELAY);
if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) {
ESP_LOGE(TAG, "IN port release error, ret:%d", load_ret);
out_len = ESP_GMF_JOB_ERR_FAIL;
}
}
return out_len;
}
static esp_gmf_err_t sonic_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx)
{
ESP_GMF_NULL_CHECK(TAG, ctx, {return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, evt, {return ESP_GMF_ERR_INVALID_ARG;});
if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO)
|| (evt->sub != ESP_GMF_INFO_SOUND)
|| (evt->payload == NULL)) {
return ESP_GMF_ERR_OK;
}
esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx;
esp_gmf_element_handle_t el = evt->from;
esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE;
esp_gmf_element_get_state(self, &state);
esp_gmf_info_sound_t *info = (esp_gmf_info_sound_t *)evt->payload;
esp_ae_sonic_cfg_t *config = (esp_ae_sonic_cfg_t *)OBJ_GET_CFG(self);
ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_FAIL);
esp_gmf_sonic_t *sonic = (esp_gmf_sonic_t *)self;
sonic->need_reopen = (config->sample_rate != info->sample_rates) || (info->channels != config->channel) || (config->bits_per_sample != info->bits);
config->sample_rate = info->sample_rates;
config->channel = info->channels;
config->bits_per_sample = info->bits;
ESP_LOGD(TAG, "RECV element info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, rate: %d, ch: %d, bits: %d",
OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type,
esp_gmf_event_get_state_str(state), info->sample_rates, info->channels, info->bits);
if (state == ESP_GMF_EVENT_STATE_NONE) {
esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED);
}
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t esp_gmf_sonic_destroy(esp_gmf_element_handle_t self)
{
esp_gmf_sonic_t *sonic = (esp_gmf_sonic_t *)self;
ESP_LOGD(TAG, "Destroyed, %p", self);
void *cfg = OBJ_GET_CFG(self);
if (cfg) {
esp_gmf_oal_free(cfg);
}
esp_gmf_audio_el_deinit(self);
esp_gmf_oal_free(sonic);
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t _load_sonic_caps_func(esp_gmf_element_handle_t handle)
{
esp_gmf_cap_t *caps = NULL;
esp_gmf_cap_t dec_caps = {0};
dec_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_SONIC;
dec_caps.attr_fun = NULL;
int ret = esp_gmf_cap_append(&caps, &dec_caps);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to create capability");
esp_gmf_element_t *el = (esp_gmf_element_t *)handle;
el->caps = caps;
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t _load_sonic_methods_func(esp_gmf_element_handle_t handle)
{
esp_gmf_method_t *method = NULL;
esp_gmf_args_desc_t *set_args = NULL;
esp_gmf_args_desc_t *get_args = NULL;
esp_gmf_err_t ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(SONIC, SET_SPEED, SPEED),
ESP_GMF_ARGS_TYPE_FLOAT, sizeof(float), 0);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append argument");
ret = esp_gmf_method_append(&method, AMETHOD(SONIC, SET_SPEED), __sonic_set_speed, set_args);
ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(SONIC, SET_SPEED));
ret = esp_gmf_args_desc_copy(set_args, &get_args);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to copy argument");
ret = esp_gmf_method_append(&method, AMETHOD(SONIC, GET_SPEED), __sonic_get_speed, get_args);
ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(SONIC, GET_SPEED));
set_args = NULL;
get_args = NULL;
ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(SONIC, SET_PITCH, PITCH), ESP_GMF_ARGS_TYPE_FLOAT, sizeof(float), 0);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append argument");
ret = esp_gmf_method_append(&method, AMETHOD(SONIC, SET_PITCH), __sonic_set_pitch, set_args);
ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(SONIC, SET_PITCH));
ret = esp_gmf_args_desc_copy(set_args, &get_args);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to copy argument");
ret = esp_gmf_method_append(&method, AMETHOD(SONIC, GET_PITCH), __sonic_get_pitch, get_args);
ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(SONIC, GET_PITCH));
esp_gmf_element_t *el = (esp_gmf_element_t *)handle;
el->method = method;
return ESP_GMF_ERR_OK;
}
esp_gmf_err_t esp_gmf_sonic_set_speed(esp_gmf_element_handle_t handle, float speed)
{
ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;});
esp_gmf_sonic_t *sonic = (esp_gmf_sonic_t *)handle;
if (sonic->sonic_hd) {
esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)handle)->lock);
esp_ae_err_t ret = esp_ae_sonic_set_speed(sonic->sonic_hd, speed);
esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)handle)->lock);
ESP_GMF_RET_ON_ERROR(TAG, ret, return ESP_GMF_JOB_ERR_FAIL;, "sonicualize set error %d", ret);
}
sonic->speed = speed;
return ESP_GMF_ERR_OK;
}
esp_gmf_err_t esp_gmf_sonic_get_speed(esp_gmf_element_handle_t handle, float *speed)
{
ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, speed, { return ESP_GMF_ERR_INVALID_ARG;});
esp_gmf_sonic_t *sonic = (esp_gmf_sonic_t *)handle;
if (sonic->sonic_hd) {
esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)handle)->lock);
esp_ae_err_t ret = esp_ae_sonic_get_speed(sonic->sonic_hd, speed);
esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)handle)->lock);
ESP_GMF_RET_ON_ERROR(TAG, ret, return ESP_GMF_JOB_ERR_FAIL;, "sonicualize set error %d", ret);
} else {
*speed = sonic->speed;
}
return ESP_GMF_ERR_OK;
}
esp_gmf_err_t esp_gmf_sonic_set_pitch(esp_gmf_element_handle_t handle, float pitch)
{
ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;});
esp_gmf_sonic_t *sonic = (esp_gmf_sonic_t *)handle;
if (sonic->sonic_hd) {
esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)handle)->lock);
esp_ae_err_t ret = esp_ae_sonic_set_pitch(sonic->sonic_hd, pitch);
esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)handle)->lock);
ESP_GMF_RET_ON_ERROR(TAG, ret, return ESP_GMF_JOB_ERR_FAIL;, "sonicualize set error %d", ret);
}
sonic->pitch = pitch;
return ESP_GMF_ERR_OK;
}
esp_gmf_err_t esp_gmf_sonic_get_pitch(esp_gmf_element_handle_t handle, float *pitch)
{
ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;});
ESP_GMF_NULL_CHECK(TAG, pitch, { return ESP_GMF_ERR_INVALID_ARG;});
esp_gmf_sonic_t *sonic = (esp_gmf_sonic_t *)handle;
if (sonic->sonic_hd) {
esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)handle)->lock);
esp_ae_err_t ret = esp_ae_sonic_get_pitch(sonic->sonic_hd, pitch);
esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)handle)->lock);
ESP_GMF_RET_ON_ERROR(TAG, ret, return ESP_GMF_JOB_ERR_FAIL;, "sonicualize set error %d", ret);
} else {
*pitch = sonic->pitch;
}
return ESP_GMF_ERR_OK;
}
esp_gmf_err_t esp_gmf_sonic_init(esp_ae_sonic_cfg_t *config, esp_gmf_element_handle_t *handle)
{
ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;});
*handle = NULL;
esp_gmf_err_t ret = ESP_GMF_ERR_OK;
esp_gmf_sonic_t *sonic = esp_gmf_oal_calloc(1, sizeof(esp_gmf_sonic_t));
ESP_GMF_MEM_VERIFY(TAG, sonic, {return ESP_GMF_ERR_MEMORY_LACK;}, "sonic", sizeof(esp_gmf_sonic_t));
esp_gmf_obj_t *obj = (esp_gmf_obj_t *)sonic;
obj->new_obj = esp_gmf_sonic_new;
obj->del_obj = esp_gmf_sonic_destroy;
esp_ae_sonic_cfg_t *cfg = esp_gmf_oal_calloc(1, sizeof(esp_ae_sonic_cfg_t));
ESP_GMF_MEM_VERIFY(TAG, cfg, ret = ESP_GMF_ERR_MEMORY_LACK; goto SONIC_INIT_FAIL;, "sonic configuration", sizeof(esp_ae_sonic_cfg_t));
esp_gmf_obj_set_config(obj, cfg, sizeof(esp_ae_sonic_cfg_t));
if (config) {
memcpy(cfg, config, sizeof(esp_ae_sonic_cfg_t));
} else {
esp_ae_sonic_cfg_t dcfg = DEFAULT_ESP_GMF_SONIC_CONFIG();
memcpy(cfg, &dcfg, sizeof(esp_ae_sonic_cfg_t));
}
ret = esp_gmf_obj_set_tag(obj, "aud_sonic");
ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto SONIC_INIT_FAIL, "Failed to set obj tag");
sonic->speed = 1.0f;
sonic->pitch = 1.0f;
esp_gmf_element_cfg_t el_cfg = {0};
ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0,
ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT);
ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0,
ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT);
el_cfg.dependency = true;
ret = esp_gmf_audio_el_init(sonic, &el_cfg);
ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto SONIC_INIT_FAIL, "Failed to initialize sonic element");
ESP_GMF_ELEMENT_GET(sonic)->ops.open = esp_gmf_sonic_open;
ESP_GMF_ELEMENT_GET(sonic)->ops.process = esp_gmf_sonic_process;
ESP_GMF_ELEMENT_GET(sonic)->ops.close = esp_gmf_sonic_close;
ESP_GMF_ELEMENT_GET(sonic)->ops.event_receiver = sonic_received_event_handler;
ESP_GMF_ELEMENT_GET(sonic)->ops.load_caps = _load_sonic_caps_func;
ESP_GMF_ELEMENT_GET(sonic)->ops.load_methods = _load_sonic_methods_func;
*handle = obj;
ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj);
return ESP_GMF_ERR_OK;
SONIC_INIT_FAIL:
esp_gmf_sonic_destroy(obj);
return ret;
}
@@ -0,0 +1,19 @@
version: "0.7.2"
description: Espressif GMF Audio Elements Module
url: https://github.com/espressif/esp-gmf/tree/main/elements/gmf_audio
documentation: "https://github.com/espressif/esp-gmf/blob/main/elements/gmf_audio/README.md"
issues: "https://github.com/espressif/esp-gmf/issues"
tags:
- "gmf"
dependencies:
espressif/esp_audio_codec:
require: public
version: "~2.3"
espressif/esp_audio_effects:
require: public
version: "~1.1"
espressif/gmf_core:
require: public
version: "^0.7"
@@ -0,0 +1,72 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#pragma once
#include "esp_gmf_err.h"
#include "esp_gmf_element.h"
#include "esp_ae_alc.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define DEFAULT_ESP_GMF_ALC_CONFIG() { \
.sample_rate = 48000, \
.channel = 2, \
.bits_per_sample = 16, \
}
/**
* @brief Initializes the GMF ALC with the provided configuration
*
* @param[in] config Pointer to the ALC configuration
* @param[out] handle Pointer to the ALC handle to be initialized
*
* @return
* - ESP_GMF_ERR_OK Success
* - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided
* - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory
*/
esp_gmf_err_t esp_gmf_alc_init(esp_ae_alc_cfg_t *config, esp_gmf_element_handle_t *handle);
/**
* @brief Set specific channel gain of the ALC handle.
* Positive gain indicates an increase in volume,
* negative gain indicates a decrease in volume.
* 0 gain indicates the volume level remains unchanged.
*
* @param[in] handle The ALC handle
* @param[in] idx The channel index of the gain to retrieve. eg: 0 refers to the first channel
* @param[in] gain The gain value needs to conform to the following conditions:
* - Supported range [-64, 63]
* - Below -64 will set to mute
* - Higher than 63 not supported
* Unit: dB
*
* @return
* - ESP_GMF_ERR_OK Operation succeeded
* - ESP_GMF_ERR_INVALID_ARG Invalid input parameter
*/
esp_gmf_err_t esp_gmf_alc_set_gain(esp_gmf_element_handle_t handle, uint8_t idx, int8_t gain);
/**
* @brief Get the gain for a specific channel from the ALC handle
*
* @param[in] handle The ALC handle
* @param[in] ch_idx The channel index of the gain to retrieve. eg: 0 refers to the first channel
* @param[out] gain Pointer to store the retrieved gain. Unit: dB
*
* @return
* - ESP_GMF_ERR_OK Operation succeeded
* - ESP_GMF_ERR_INVALID_ARG Invalid input parameter
*/
esp_gmf_err_t esp_gmf_alc_get_gain(esp_gmf_element_handle_t handle, uint8_t idx, int8_t *gain);
#ifdef __cplusplus
}
#endif /* __cplusplus */
@@ -0,0 +1,75 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#pragma once
#include "esp_audio_simple_dec.h"
#include "esp_gmf_element.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define DEFAULT_ESP_GMF_AUDIO_DEC_CONFIG() { \
.dec_type = ESP_AUDIO_SIMPLE_DEC_TYPE_NONE, \
.dec_cfg = NULL, \
.cfg_size = 0, \
}
/**
* @brief Initializes the GMF audio decoder with the provided configuration
*
* @param[in] config Pointer to the audio decoder configuration
* @param[out] handle Pointer to the audio decoder handle to be initialized
*
* @return
* - ESP_GMF_ERR_OK Success
* - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided
* - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory
*/
esp_gmf_err_t esp_gmf_audio_dec_init(esp_audio_simple_dec_cfg_t *config, esp_gmf_element_handle_t *handle);
/**
* @brief Reconfigure the GMF audio decoder and filled into provided configuration
*
* @note Only allowed when not running, i.e., in the `ESP_GMF_EVENT_STATE_NONE` or `ESP_GMF_EVENT_STATE_INITIALIZED` state
*
* @param[in] handle Audio decoder handle to be reconfigured
* @param[in] config Pointer to the new simple decoder configuration
*
* @return
* - ESP_GMF_ERR_OK Success
* - ESP_GMF_ERR_INVALID_ARG Invalid handle or configuration provided
* - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory
*/
esp_gmf_err_t esp_gmf_audio_dec_reconfig(esp_gmf_element_handle_t handle, esp_audio_simple_dec_cfg_t *config);
/**
* @brief Reconfigures the GMF audio decoder with default configuration based on provided sound information
*
* @note 1. Only allowed when not running, i.e., in the `ESP_GMF_EVENT_STATE_NONE` or `ESP_GMF_EVENT_STATE_INITIALIZED` state
* 2. The actual reconfiguration behavior depends on the relationship between the provided `sound info` and
* the current decoder configuration (`cfg`):
* - If the `format_id` in `sound info` matches the decoder's current `type`, and the decoder's sub-config (`sub cfg`) is not NULL,
* only the basic fields in the sub-config will be updated with information from `sound info` (e.g., sample rate, channel count).
* - If the `format_id` differs from the current `type`, or if the sub-config is NULL,
* the decoder will be reconfigured using the **default configuration** for the specified `format_id`,
* and the current decoder `type` will be updated accordingly.
*
* @param[in] handle Audio decoder handle to be reconfigured
* @param[in] info Sound information to be configured
*
* @return
* - ESP_GMF_ERR_OK Success
* - ESP_GMF_ERR_INVALID_ARG Invalid handle or decoder type provided
* - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory
*/
esp_gmf_err_t esp_gmf_audio_dec_reconfig_by_sound_info(esp_gmf_element_handle_t handle, esp_gmf_info_sound_t *info);
#ifdef __cplusplus
}
#endif /* __cplusplus */
@@ -0,0 +1,113 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#pragma once
#include "esp_gmf_err.h"
#include "esp_gmf_element.h"
#include "encoder/esp_audio_enc.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define DEFAULT_ESP_GMF_AUDIO_ENC_CONFIG() { \
.type = ESP_AUDIO_TYPE_UNSUPPORT, \
.cfg = NULL, \
.cfg_sz = 0, \
}
/**
* @brief Initializes the GMF audio encoder with the provided configuration
*
* @param[in] config Pointer to the audio encoder configuration
* @param[out] handle Pointer to the audio encoder handle to be initialized
*
* @return
* - ESP_GMF_ERR_OK Success
* - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided
* - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory
*/
esp_gmf_err_t esp_gmf_audio_enc_init(esp_audio_enc_config_t *config, esp_gmf_element_handle_t *handle);
/**
* @brief Get the input and output frame size information of the GMF audio encoder
*
* @param[in] handle Audio encoder handle
* @param[out] in_size Pointer to store input frame size
* @param[out] out_size Pointer to store suggested output frame size
*
* @return
* - ESP_GMF_ERR_OK Success
* - ESP_GMF_ERR_INVALID_ARG Invalid handle or size pointers provided
*/
esp_gmf_err_t esp_gmf_audio_enc_get_frame_size(esp_gmf_element_handle_t handle, uint32_t *in_size, uint32_t *out_size);
/**
* @brief Set the bitrate for the GMF audio encoder
*
* @param[in] handle Audio encoder handle
* @param[in] bitrate The bitrate of the encoder
*
* @return
* - ESP_GMF_ERR_OK Success
* - ESP_GMF_ERR_INVALID_ARG Invalid handle or bitrate value provided
*/
esp_gmf_err_t esp_gmf_audio_enc_set_bitrate(esp_gmf_element_handle_t handle, uint32_t bitrate);
/**
* @brief Get the current bitrate of the GMF audio encoder
*
* @param[in] handle Audio encoder handle
* @param[out] bitrate Pointer to store the current bitrate
*
* @return
* - ESP_GMF_ERR_OK Success
* - ESP_GMF_ERR_INVALID_ARG Invalid handle or bitrate pointer provided
*/
esp_gmf_err_t esp_gmf_audio_enc_get_bitrate(esp_gmf_element_handle_t handle, uint32_t *bitrate);
/**
* @brief Reconfigure the GMF audio encoder and filled into provided configuration
*
* @note Only allowed when not running, i.e., in the `ESP_GMF_EVENT_STATE_NONE` or `ESP_GMF_EVENT_STATE_INITIALIZED` state
*
* @param[in] handle Audio encoder handle to be reconfigured
* @param[in] config Pointer to the new audio encoder configuration
*
* @return
* - ESP_GMF_ERR_OK Success
* - ESP_GMF_ERR_INVALID_ARG Invalid handle or configuration provided
* - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory
*/
esp_gmf_err_t esp_gmf_audio_enc_reconfig(esp_gmf_element_handle_t handle, esp_audio_enc_config_t *config);
/**
* @brief Reconfigures the GMF audio encoder with default configuration for the sound information
*
* @note 1. Only allowed when not running, i.e., in the `ESP_GMF_EVENT_STATE_NONE` or `ESP_GMF_EVENT_STATE_INITIALIZED` state
* 2. The actual reconfiguration behavior depends on the relationship between the provided `sound info` and
* the current encoder configuration (`cfg`):
* - If the `format_id` in `sound info` matches the encoder's current `type`, and the encoder's sub-config (`sub cfg`) is not NULL,
* only the basic fields in the sub-config will be updated with information from `sound info` (e.g., sample rate, channel count).
* - If the `format_id` differs from the current `type`, or if the sub-config is NULL,
* the encoder will be reconfigured using the **default configuration** for the specified `format_id`,
* and the current encoder `type` will be updated accordingly.
*
* @param[in] handle Audio encoder handle to be reconfigured
* @param[in] info Sound information to be configured
*
* @return
* - ESP_GMF_ERR_OK Success
* - ESP_GMF_ERR_INVALID_ARG Invalid handle or encoder type provided
* - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory
*/
esp_gmf_err_t esp_gmf_audio_enc_reconfig_by_sound_info(esp_gmf_element_handle_t handle, esp_gmf_info_sound_t *info);
#ifdef __cplusplus
}
#endif /* __cplusplus */
@@ -0,0 +1,30 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#pragma once
#include "esp_gmf_err.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/**
* @brief Get audio codec type by uri
*
* @param[in] uri URI of audio codec
* @param[out] format_id Type of audio codec(ESP_FourCC type)
*
* @return
* - ESP_GMF_ERR_OK Success
* - ESP_GMF_ERR_NOT_SUPPORT Not supported audio codec type
*/
esp_gmf_err_t esp_gmf_audio_helper_get_audio_type_by_uri(const char *uri, uint32_t *format_id);
#ifdef __cplusplus
}
#endif /* __cplusplus */
@@ -0,0 +1,217 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#pragma once
#include "esp_gmf_method_helper.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/**
* @brief Audio general method argument definition
*/
#define ESP_GMF_AUDIO_INDEX_ARG "index"
#define ESP_GMF_AUDIO_GAIN_ARG "gain"
#define ESP_GMF_AUDIO_BITS_ARG "bits"
#define ESP_GMF_AUDIO_CHANNEL_ARG "channel"
#define ESP_GMF_AUDIO_SAMPLE_RATE_ARG "sample_rate"
#define ESP_GMF_AUDIO_BITRATE_ARG "bitrate"
#define ESP_GMF_AUDIO_ENABLE_ARG "enable"
#define ESP_GMF_AUDIO_TYPE_ARG "type"
#define ESP_GMF_AUDIO_PARA_ARG "para"
#define ESP_GMF_AUDIO_MODE_ARG "mode"
#define ESP_GMF_AUDIO_SPEED_ARG "speed"
#define ESP_GMF_AUDIO_PITCH_ARG "pitch"
#define ESP_GMF_AUDIO_INSIZE_ARG "in_size"
#define ESP_GMF_AUDIO_OUTSIZE_ARG "out_size"
#define ESP_GMF_AUDIO_CFG_ARG "cfg"
#define ESP_GMF_AUDIO_INFO_ARG "info"
/**
* @brief Audio EQ parameter argument definition
*/
#define ESP_GMF_AUDIO_EQ_PARA_ARG ESP_GMF_AUDIO_PARA_ARG
#define ESP_GMF_AUDIO_EQ_PARA_FT_ARG ESP_GMF_AUDIO_TYPE_ARG
#define ESP_GMF_AUDIO_EQ_PARA_FC_ARG "fc"
#define ESP_GMF_AUDIO_EQ_PARA_Q_ARG "q"
#define ESP_GMF_AUDIO_EQ_PARA_GAIN_ARG ESP_GMF_AUDIO_GAIN_ARG
/**
* @brief Audio general config parameter argument definition
*/
#define ESP_GMF_AUDIO_CFG_TYPE_ARG ESP_GMF_AUDIO_TYPE_ARG
#define ESP_GMF_AUDIO_CFG_SUBCFGPTR_ARG "sub_cfg"
#define ESP_GMF_AUDIO_CFG_SUBCFGSZ_ARG "sub_cfg_sz"
/**
* @brief Audio sound information parameter argument definition
*/
#define ESP_GMF_AUDIO_SND_INFO_ARG ESP_GMF_AUDIO_INFO_ARG
#define ESP_GMF_AUDIO_SND_INFO_TYPE_ARG ESP_GMF_AUDIO_TYPE_ARG
#define ESP_GMF_AUDIO_SND_INFO_SAMPLERATE_ARG ESP_GMF_AUDIO_SAMPLE_RATE_ARG
#define ESP_GMF_AUDIO_SND_INFO_BITRATE_ARG ESP_GMF_AUDIO_BITRATE_ARG
#define ESP_GMF_AUDIO_SND_INFO_CHANNEL_ARG ESP_GMF_AUDIO_CHANNEL_ARG
#define ESP_GMF_AUDIO_SND_INFO_BITS_ARG ESP_GMF_AUDIO_BITS_ARG
/**
* @brief GMF audio method definition
*/
#define AMETHOD_DEF(module, method, str) ESP_GMF_METHOD_DEF(AUDIO, module, method, str)
/**
* @brief Get GMF audio method string
*/
#define AMETHOD(module, method) ESP_GMF_METHOD_STR(AUDIO, module, method)
/**
* @brief GMF audio method argument definition
*/
#define AMETHOD_ARG_DEF(module, method, arg, str) ESP_GMF_METHOD_ARG_DEF(AUDIO, module, method, arg, str)
/**
* @brief Get GMF audio method argument string
*/
#define AMETHOD_ARG(module, method, arg) ESP_GMF_METHOD_ARG_STR(AUDIO, module, method, arg)
/**
* @brief ALC method
*/
AMETHOD_DEF(ALC, SET_GAIN, "set_gain");
AMETHOD_ARG_DEF(ALC, SET_GAIN, IDX, ESP_GMF_AUDIO_INDEX_ARG);
AMETHOD_ARG_DEF(ALC, SET_GAIN, GAIN, ESP_GMF_AUDIO_GAIN_ARG);
AMETHOD_DEF(ALC, GET_GAIN, "get_gain");
AMETHOD_ARG_DEF(ALC, GET_GAIN, IDX, ESP_GMF_AUDIO_INDEX_ARG);
AMETHOD_ARG_DEF(ALC, GET_GAIN, GAIN, ESP_GMF_AUDIO_GAIN_ARG);
/**
* @brief BIT CVT method
*/
AMETHOD_DEF(BIT_CVT, SET_DEST_BITS, "set_dest_bits");
AMETHOD_ARG_DEF(BIT_CVT, SET_DEST_BITS, BITS, ESP_GMF_AUDIO_BITS_ARG);
/**
* @brief CH CVT method
*/
AMETHOD_DEF(CH_CVT, SET_DEST_CH, "set_dest_ch");
AMETHOD_ARG_DEF(CH_CVT, SET_DEST_CH, CH, ESP_GMF_AUDIO_CHANNEL_ARG);
/**
* @brief RATE CVT method
*/
AMETHOD_DEF(RATE_CVT, SET_DEST_RATE, "set_dest_rate");
AMETHOD_ARG_DEF(RATE_CVT, SET_DEST_RATE, RATE, ESP_GMF_AUDIO_SAMPLE_RATE_ARG);
/**
* @brief EQ method
*/
AMETHOD_DEF(EQ, SET_PARA, "set_para");
AMETHOD_ARG_DEF(EQ, SET_PARA, IDX, ESP_GMF_AUDIO_INDEX_ARG);
AMETHOD_ARG_DEF(EQ, SET_PARA, PARA, ESP_GMF_AUDIO_EQ_PARA_ARG);
AMETHOD_ARG_DEF(EQ, SET_PARA, PARA_FT, ESP_GMF_AUDIO_EQ_PARA_FT_ARG);
AMETHOD_ARG_DEF(EQ, SET_PARA, PARA_FC, ESP_GMF_AUDIO_EQ_PARA_FC_ARG);
AMETHOD_ARG_DEF(EQ, SET_PARA, PARA_Q, ESP_GMF_AUDIO_EQ_PARA_Q_ARG);
AMETHOD_ARG_DEF(EQ, SET_PARA, PARA_GAIN, ESP_GMF_AUDIO_EQ_PARA_GAIN_ARG);
AMETHOD_DEF(EQ, GET_PARA, "get_para");
AMETHOD_ARG_DEF(EQ, GET_PARA, IDX, ESP_GMF_AUDIO_INDEX_ARG);
AMETHOD_ARG_DEF(EQ, GET_PARA, PARA, ESP_GMF_AUDIO_EQ_PARA_ARG);
AMETHOD_ARG_DEF(EQ, GET_PARA, PARA_FT, ESP_GMF_AUDIO_EQ_PARA_FT_ARG);
AMETHOD_ARG_DEF(EQ, GET_PARA, PARA_FC, ESP_GMF_AUDIO_EQ_PARA_FC_ARG);
AMETHOD_ARG_DEF(EQ, GET_PARA, PARA_Q, ESP_GMF_AUDIO_EQ_PARA_Q_ARG);
AMETHOD_ARG_DEF(EQ, GET_PARA, PARA_GAIN, ESP_GMF_AUDIO_EQ_PARA_GAIN_ARG);
AMETHOD_DEF(EQ, ENABLE_FILTER, "enable_filter");
AMETHOD_ARG_DEF(EQ, ENABLE_FILTER, IDX, ESP_GMF_AUDIO_INDEX_ARG);
AMETHOD_ARG_DEF(EQ, ENABLE_FILTER, ENABLE, ESP_GMF_AUDIO_ENABLE_ARG);
/**
* @brief FADE method
*/
AMETHOD_DEF(FADE, SET_MODE, "set_mode");
AMETHOD_ARG_DEF(FADE, SET_MODE, MODE, ESP_GMF_AUDIO_MODE_ARG);
AMETHOD_DEF(FADE, GET_MODE, "get_mode");
AMETHOD_ARG_DEF(FADE, GET_MODE, MODE, ESP_GMF_AUDIO_MODE_ARG);
AMETHOD_DEF(FADE, RESET_WEIGHT, "reset_weight");
/**
* @brief MIXER method
*/
AMETHOD_DEF(MIXER, SET_MODE, "set_mode");
AMETHOD_ARG_DEF(MIXER, SET_MODE, IDX, ESP_GMF_AUDIO_INDEX_ARG);
AMETHOD_ARG_DEF(MIXER, SET_MODE, MODE, ESP_GMF_AUDIO_MODE_ARG);
AMETHOD_DEF(MIXER, SET_INFO, "set_info");
AMETHOD_ARG_DEF(MIXER, SET_INFO, RATE, ESP_GMF_AUDIO_SAMPLE_RATE_ARG);
AMETHOD_ARG_DEF(MIXER, SET_INFO, CH, ESP_GMF_AUDIO_CHANNEL_ARG);
AMETHOD_ARG_DEF(MIXER, SET_INFO, BITS, ESP_GMF_AUDIO_BITS_ARG);
/**
* @brief SONIC method
*/
AMETHOD_DEF(SONIC, SET_SPEED, "set_speed");
AMETHOD_ARG_DEF(SONIC, SET_SPEED, SPEED, ESP_GMF_AUDIO_SPEED_ARG);
AMETHOD_DEF(SONIC, GET_SPEED, "get_speed");
AMETHOD_ARG_DEF(SONIC, GET_SPEED, SPEED, ESP_GMF_AUDIO_SPEED_ARG);
AMETHOD_DEF(SONIC, SET_PITCH, "set_pitch");
AMETHOD_ARG_DEF(SONIC, SET_PITCH, PITCH, ESP_GMF_AUDIO_PITCH_ARG);
AMETHOD_DEF(SONIC, GET_PITCH, "get_pitch");
AMETHOD_ARG_DEF(SONIC, GET_PITCH, PITCH, ESP_GMF_AUDIO_PITCH_ARG);
/**
* @brief ENCODER method
*/
AMETHOD_DEF(ENCODER, GET_FRAME_SZ, "get_frame_sz");
AMETHOD_ARG_DEF(ENCODER, GET_FRAME_SZ, INSIZE, ESP_GMF_AUDIO_INSIZE_ARG);
AMETHOD_ARG_DEF(ENCODER, GET_FRAME_SZ, OUTSIZE, ESP_GMF_AUDIO_OUTSIZE_ARG);
AMETHOD_DEF(ENCODER, SET_BITRATE, "set_bitrate");
AMETHOD_ARG_DEF(ENCODER, SET_BITRATE, BITRATE, ESP_GMF_AUDIO_BITRATE_ARG);
AMETHOD_DEF(ENCODER, GET_BITRATE, "get_bitrate");
AMETHOD_ARG_DEF(ENCODER, GET_BITRATE, BITRATE, ESP_GMF_AUDIO_BITRATE_ARG);
AMETHOD_DEF(ENCODER, RECONFIG, "recfg");
AMETHOD_ARG_DEF(ENCODER, RECONFIG, CFG, ESP_GMF_AUDIO_CFG_ARG);
AMETHOD_ARG_DEF(ENCODER, RECONFIG, CFG_TYPE, ESP_GMF_AUDIO_CFG_TYPE_ARG);
AMETHOD_ARG_DEF(ENCODER, RECONFIG, CFG_SUBCFGPTR, ESP_GMF_AUDIO_CFG_SUBCFGPTR_ARG);
AMETHOD_ARG_DEF(ENCODER, RECONFIG, CFG_SUBCFGSZ, ESP_GMF_AUDIO_CFG_SUBCFGSZ_ARG);
AMETHOD_DEF(ENCODER, RECONFIG_BY_SND_INFO, "recfg_by_sndinfo");
AMETHOD_ARG_DEF(ENCODER, RECONFIG_BY_SND_INFO, INFO, ESP_GMF_AUDIO_SND_INFO_ARG);
AMETHOD_ARG_DEF(ENCODER, RECONFIG_BY_SND_INFO, INFO_TYPE, ESP_GMF_AUDIO_SND_INFO_TYPE_ARG);
AMETHOD_ARG_DEF(ENCODER, RECONFIG_BY_SND_INFO, INFO_SAMPLERATE, ESP_GMF_AUDIO_SND_INFO_SAMPLERATE_ARG);
AMETHOD_ARG_DEF(ENCODER, RECONFIG_BY_SND_INFO, INFO_BITRATE, ESP_GMF_AUDIO_SND_INFO_BITRATE_ARG);
AMETHOD_ARG_DEF(ENCODER, RECONFIG_BY_SND_INFO, INFO_CHANNEL, ESP_GMF_AUDIO_SND_INFO_CHANNEL_ARG);
AMETHOD_ARG_DEF(ENCODER, RECONFIG_BY_SND_INFO, INFO_BITS, ESP_GMF_AUDIO_SND_INFO_BITS_ARG);
/**
* @brief DECODER method
*/
AMETHOD_DEF(DECODER, RECONFIG, "recfg");
AMETHOD_ARG_DEF(DECODER, RECONFIG, CFG, ESP_GMF_AUDIO_CFG_ARG);
AMETHOD_ARG_DEF(DECODER, RECONFIG, CFG_TYPE, ESP_GMF_AUDIO_CFG_TYPE_ARG);
AMETHOD_ARG_DEF(DECODER, RECONFIG, CFG_SUBCFGPTR, ESP_GMF_AUDIO_CFG_SUBCFGPTR_ARG);
AMETHOD_ARG_DEF(DECODER, RECONFIG, CFG_SUBCFGSZ, ESP_GMF_AUDIO_CFG_SUBCFGSZ_ARG);
AMETHOD_DEF(DECODER, RECONFIG_BY_SND_INFO, "recfg_by_sndinfo");
AMETHOD_ARG_DEF(DECODER, RECONFIG_BY_SND_INFO, INFO, ESP_GMF_AUDIO_SND_INFO_ARG);
AMETHOD_ARG_DEF(DECODER, RECONFIG_BY_SND_INFO, INFO_TYPE, ESP_GMF_AUDIO_SND_INFO_TYPE_ARG);
AMETHOD_ARG_DEF(DECODER, RECONFIG_BY_SND_INFO, INFO_SAMPLERATE, ESP_GMF_AUDIO_SND_INFO_SAMPLERATE_ARG);
AMETHOD_ARG_DEF(DECODER, RECONFIG_BY_SND_INFO, INFO_CHANNEL, ESP_GMF_AUDIO_SND_INFO_CHANNEL_ARG);
AMETHOD_ARG_DEF(DECODER, RECONFIG_BY_SND_INFO, INFO_BITS, ESP_GMF_AUDIO_SND_INFO_BITS_ARG);
#ifdef __cplusplus
}
#endif /* __cplusplus */
@@ -0,0 +1,128 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#pragma once
#include "esp_gmf_element.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Set frame destination sample rate for audio element
*
* @param[in] handle Audio element handle
* @param[in] dest_rate Destination sample rate
*
* @return
* - ESP_GMF_ERR_OK On success
* - ESP_GMF_ERR_NOT_FOUND Not found the method
* - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory
* - Others Failed to apply method
*/
esp_gmf_err_t esp_gmf_audio_param_set_dest_rate(esp_gmf_element_handle_t self, uint32_t dest_rate);
/**
* @brief Set frame destination bits for audio element
*
* @param[in] handle Audio element handle
* @param[in] dest_bits Destination bits
*
* @return
* - ESP_GMF_ERR_OK On success
* - ESP_GMF_ERR_NOT_FOUND Not found the method
* - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory
* - Others Failed to apply method
*/
esp_gmf_err_t esp_gmf_audio_param_set_dest_bits(esp_gmf_element_handle_t self, uint8_t dest_bits);
/**
* @brief Set frame destination channel for audio element
*
* @param[in] handle Audio element handle
* @param[in] dest_ch Destination channel
*
* @return
* - ESP_GMF_ERR_OK On success
* - ESP_GMF_ERR_NOT_FOUND Not found the method
* - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory
* - Others Failed to apply method
*/
esp_gmf_err_t esp_gmf_audio_param_set_dest_ch(esp_gmf_element_handle_t self, uint8_t dest_ch);
/**
* @brief Set speed for audio element
*
* @param[in] handle Audio element handle
* @param[in] speed Audio playback speed
*
* @return
* - ESP_GMF_ERR_OK On success
* - ESP_GMF_ERR_NOT_FOUND Not found the method
* - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory
* - Others Failed to apply method
*/
esp_gmf_err_t esp_gmf_audio_param_set_speed(esp_gmf_element_handle_t self, float speed);
/**
* @brief Set pitch for audio element
*
* @param[in] handle Audio element handle
* @param[in] pitch Audio pitch to set
*
* @return
* - ESP_GMF_ERR_OK On success
* - ESP_GMF_ERR_NOT_FOUND Not found the method
* - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory
* - Others Failed to apply method
*/
esp_gmf_err_t esp_gmf_audio_param_set_pitch(esp_gmf_element_handle_t self, float pitch);
/**
* @brief Set ALC certain channel gain for audio element
*
* @param[in] handle Audio element handle
* @param[in] ch_idx Channel index (0: Left channel 1: Right Channel 0xFF: All channels)
* @param[in] gain_db ALC gain (unit decibel)
*
* @return
* - ESP_GMF_ERR_OK On success
* - ESP_GMF_ERR_NOT_FOUND Not found the method
* - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory
* - Others Failed to apply method
*/
esp_gmf_err_t esp_gmf_audio_param_set_alc_channel_gain(esp_gmf_element_handle_t self, uint8_t ch_idx, float gain_db);
/**
* @brief Set ALC gain for all channels
*/
#define esp_gmf_audio_param_set_alc_gain(self, gain_db) esp_gmf_audio_param_set_alc_channel_gain(self, 0xFF, gain_db)
/**
* @brief Set fade in/out direction for audio element
*
* @param[in] handle Audio element handle
* @param[in] is_fade_in Whether fade in or out
*
* @return
* - ESP_GMF_ERR_OK On success
* - ESP_GMF_ERR_NOT_FOUND Not found the method
* - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory
* - Others Failed to apply method
*/
esp_gmf_err_t esp_gmf_audio_param_set_fade(esp_gmf_element_handle_t self, bool is_fade_in);
/**
* @brief Helper wrapper for fade in and fade out setting
*/
#define esp_gmf_audio_param_set_fade_in(self) esp_gmf_audio_param_set_fade(self, true);
#define esp_gmf_audio_param_set_fade_out(self) esp_gmf_audio_param_set_fade(self, false);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,53 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#pragma once
#include "esp_gmf_err.h"
#include "esp_ae_bit_cvt.h"
#include "esp_gmf_element.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define DEFAULT_ESP_GMF_BIT_CVT_CONFIG() { \
.sample_rate = 48000, \
.channel = 2, \
.src_bits = 16, \
.dest_bits = 16, \
}
/**
* @brief Initializes the GMF bit conversion with the provided configuration
*
* @param[in] config Pointer to the bit conversion configuration
* @param[out] handle Pointer to the bit conversion handle to be initialized
*
* @return
* - ESP_GMF_ERR_OK Success
* - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided
* - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory
*/
esp_gmf_err_t esp_gmf_bit_cvt_init(esp_ae_bit_cvt_cfg_t *config, esp_gmf_element_handle_t *handle);
/**
* @brief Set destination bits for the bit conversion handle
*
* @param[in] handle The channel bit handle
* @param[in] dest_bits The destination bits
*
* @return
* - ESP_GMF_ERR_OK Operation succeeded
* - ESP_GMF_ERR_INVALID_ARG Invalid input parameter
* - ESP_GMF_ERR_FAIL Failed to set configuration
*/
esp_gmf_err_t esp_gmf_bit_cvt_set_dest_bits(esp_gmf_element_handle_t handle, uint8_t dest_bits);
#ifdef __cplusplus
}
#endif /* __cplusplus */
@@ -0,0 +1,55 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#pragma once
#include "esp_gmf_err.h"
#include "esp_ae_ch_cvt.h"
#include "esp_gmf_element.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define DEFAULT_ESP_GMF_CH_CVT_CONFIG() { \
.sample_rate = 48000, \
.bits_per_sample = 16, \
.src_ch = 2, \
.dest_ch = 2, \
.weight = NULL, \
.weight_len = 0, \
}
/**
* @brief Initializes the GMF channel conversion with the provided configuration
*
* @param[in] config Pointer to the channel conversion configuration
* @param[out] handle Pointer to the channel conversion handle to be initialized
*
* @return
* - ESP_GMF_ERR_OK Success
* - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided
* - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory
*/
esp_gmf_err_t esp_gmf_ch_cvt_init(esp_ae_ch_cvt_cfg_t *config, esp_gmf_element_handle_t *handle);
/**
* @brief Set dest channel in the channel conversion handle
*
* @param[in] handle The channel conversion handle
* @param[in] dest_ch The dest channel
*
* @return
* - ESP_GMF_ERR_OK Operation succeeded
* - ESP_GMF_ERR_INVALID_ARG Invalid input parameter
* - ESP_GMF_ERR_FAIL Failed to set configuration
*/
esp_gmf_err_t esp_gmf_ch_cvt_set_dest_channel(esp_gmf_element_handle_t handle, uint8_t dest_ch);
#ifdef __cplusplus
}
#endif /* __cplusplus */
@@ -0,0 +1,47 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#pragma once
#include "esp_gmf_err.h"
#include "esp_gmf_element.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define DEFAULT_ESP_GMF_DEINTERLEAVE_CONFIG() { \
.sample_rate = 48000, \
.bits_per_sample = 16, \
.channel = 2, \
}
/**
* @brief Configuration structure for deinterleave
*/
typedef struct {
uint32_t sample_rate; /*!< The audio sample rate */
uint8_t bits_per_sample; /*!< The audio bits per sample, supports 16, 24, 32 bits */
uint8_t channel; /*!< The audio channel */
} esp_gmf_deinterleave_cfg;
/**
* @brief Initializes the GMF deinterleave with the provided configuration
*
* @param[in] config Pointer to the deinterleave configuration
* @param[out] handle Pointer to the deinterleave handle to be initialized
*
* @return
* - ESP_GMF_ERR_OK Success
* - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided
* - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory
*/
esp_gmf_err_t esp_gmf_deinterleave_init(esp_gmf_deinterleave_cfg *config, esp_gmf_element_handle_t *handle);
#ifdef __cplusplus
}
#endif /* __cplusplus */
@@ -0,0 +1,87 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#pragma once
#include "esp_gmf_err.h"
#include "esp_ae_eq.h"
#include "esp_gmf_element.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/**
* @brief Default EQ parameters
* If `para` is NULL in the `esp_ae_eq_cfg_t` , the EQ will use `esp_gmf_default_eq_paras` as default
* And the `filter_num` will be set to sizeof(esp_gmf_default_eq_paras) / sizeof(esp_ae_eq_filter_para_t)
*/
#define DEFAULT_ESP_GMF_EQ_CONFIG() { \
.sample_rate = 48000, \
.channel = 2, \
.bits_per_sample = 16, \
.filter_num = 0, \
.para = NULL, \
}
/**
* @brief Initializes the GMF EQ with the provided configuration
*
* @param[in] config Pointer to the EQ configuration
* @param[out] handle Pointer to the EQ handle to be initialized
*
* @return
* - ESP_GMF_ERR_OK Success
* - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided
* - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory
*/
esp_gmf_err_t esp_gmf_eq_init(esp_ae_eq_cfg_t *config, esp_gmf_element_handle_t *handle);
/**
* @brief Set the filter parameters for a specific filter identified by 'idx'
*
* @param[in] handle The EQ handle
* @param[in] idx The index of a specific filter for which the parameters are to be set.
* eg: 0 refers to the first filter
* @param[in] para The filter setup parameter
*
* @return
* - ESP_GMF_ERR_OK Operation succeeded
* - ESP_GMF_ERR_INVALID_ARG Invalid input parameter
*/
esp_gmf_err_t esp_gmf_eq_set_para(esp_gmf_element_handle_t handle, uint8_t idx, esp_ae_eq_filter_para_t *para);
/**
* @brief Get the filter parameters for a specific filter identified by 'idx'
*
* @param[in] handle The EQ handle
* @param[in] idx The index of a specific filter for which the parameters are to be retrieved.
* eg: 0 refers to first filter
* @param[out] para The filter setup parameter
*
* @return
* - ESP_GMF_ERR_OK Operation succeeded
* - ESP_GMF_ERR_INVALID_ARG Invalid input parameter
*/
esp_gmf_err_t esp_gmf_eq_get_para(esp_gmf_element_handle_t handle, uint8_t idx, esp_ae_eq_filter_para_t *para);
/**
* @brief Choose to enable or disable filter processing for a specific filter identified by 'idx' in the equalizer
*
* @param[in] handle The EQ handle
* @param[in] idx The index of a specific filter to be enabled
* @param[in] is_enable The flag of whether to enable band filter processing
*
* @return
* - ESP_GMF_ERR_OK Operation succeeded
* - ESP_GMF_ERR_INVALID_ARG Invalid input parameter
*/
esp_gmf_err_t esp_gmf_eq_enable_filter(esp_gmf_element_handle_t handle, uint8_t idx, bool is_enable);
#ifdef __cplusplus
}
#endif /* __cplusplus */
@@ -0,0 +1,81 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#pragma once
#include "esp_gmf_err.h"
#include "esp_ae_fade.h"
#include "esp_gmf_element.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define DEFAULT_ESP_GMF_FADE_CONFIG() { \
.mode = ESP_AE_FADE_MODE_FADE_IN, \
.curve = ESP_AE_FADE_CURVE_LINE, \
.transit_time = 500, \
.sample_rate = 48000, \
.channel = 2, \
.bits_per_sample = 16, \
}
/**
* @brief Initializes the GMF fade with the provided configuration
*
* @param[in] config Pointer to the fade configuration
* @param[out] handle Pointer to the fade handle to be initialized
*
* @return
* - ESP_GMF_ERR_OK Success
* - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided
* - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory
*/
esp_gmf_err_t esp_gmf_fade_init(esp_ae_fade_cfg_t *config, esp_gmf_element_handle_t *handle);
/**
* @brief Set the fade process mode
*
* @param[in] handle The fade handle
* @param[in] mode The mode of fade
*
* @return
* - ESP_GMF_ERR_OK Operation succeeded
* - ESP_GMF_ERR_INVALID_ARG Invalid input parameter
*/
esp_gmf_err_t esp_gmf_fade_set_mode(esp_gmf_element_handle_t handle, esp_ae_fade_mode_t mode);
/**
* @brief Get the fade process mode
*
* @param[in] handle The fade handle
* @param[out] mode The mode of fade
*
* @return
* - ESP_GMF_ERR_OK Operation succeeded
* - ESP_GMF_ERR_INVALID_ARG Invalid input parameter
*/
esp_gmf_err_t esp_gmf_fade_get_mode(esp_gmf_element_handle_t handle, esp_ae_fade_mode_t *mode);
/**
* @brief Reset the fade process to the initial configuration state.
* If the initial configuration mode is fade in, the current weight is set to 0.
* If the initial configuration mode is fade out, the current weight is set to 1.
* This function helps the user to restart fade in or fade out without
* reopening and closing.
*
* @param[in] handle The fade handle
*
* @return
* - ESP_GMF_ERR_OK Operation succeeded
* - ESP_GMF_ERR_INVALID_ARG Invalid input parameter
*/
esp_gmf_err_t esp_gmf_fade_reset_weight(esp_gmf_element_handle_t handle);
#ifdef __cplusplus
}
#endif /* __cplusplus */
@@ -0,0 +1,47 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#pragma once
#include "esp_gmf_err.h"
#include "esp_gmf_element.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define DEFAULT_ESP_GMF_INTERLEAVE_CONFIG() { \
.sample_rate = 48000, \
.bits_per_sample = 16, \
.src_num = 2, \
}
/**
* @brief Configuration structure for interleave
*/
typedef struct {
uint32_t sample_rate; /*!< The audio sample rate */
uint8_t bits_per_sample; /*!< The audio bits per sample, supports 16, 24, 32 bits */
uint8_t src_num; /*!< The number of input source num */
} esp_gmf_interleave_cfg;
/**
* @brief Initializes the GMF interleave with the provided configuration
*
* @param[in] config Pointer to the interleave configuration
* @param[out] handle Pointer to the interleave handle to be initialized
*
* @return
* - ESP_GMF_ERR_OK Success
* - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided
* - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory
*/
esp_gmf_err_t esp_gmf_interleave_init(esp_gmf_interleave_cfg *config, esp_gmf_element_handle_t *handle);
#ifdef __cplusplus
}
#endif /* __cplusplus */
@@ -0,0 +1,76 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#pragma once
#include "esp_gmf_err.h"
#include "esp_ae_mixer.h"
#include "esp_gmf_element.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/**
* @brief Default mixer source info
* If `src_info` is NULL in `esp_ae_mixer_cfg_t`, the mixer will use esp_gmf_default_mixer_src_info as default
* And `src_num` will set to `sizeof(esp_gmf_default_mixer_src_info) / sizeof(esp_ae_mixer_info_t)`;
*/
#define DEFAULT_ESP_GMF_MIXER_CONFIG() { \
.sample_rate = 48000, \
.channel = 2, \
.bits_per_sample = 16, \
.src_num = 0, \
.src_info = NULL, \
}
/**
* @brief Initializes the GMF mixer with the provided configuration
*
* @param[in] config Pointer to the mixer configuration
* @param[out] handle Pointer to the mixer handle to be initialized
*
* @return
* - ESP_GMF_ERR_OK Success
* - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided
* - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory
*/
esp_gmf_err_t esp_gmf_mixer_init(esp_ae_mixer_cfg_t *config, esp_gmf_element_handle_t *handle);
/**
* @brief Set the transit mode of a certain stream according to src_idx
*
* @param[in] handle The mixer handle
* @param[in] src_idx The index of a certain source stream which want to set transit mode.
* eg: 0 refer to first source stream
* @param[in] mode The transit mode of source stream
*
* @return
* - ESP_GMF_ERR_OK Operation succeeded
* - ESP_GMF_ERR_INVALID_ARG Invalid input parameter
*/
esp_gmf_err_t esp_gmf_mixer_set_mode(esp_gmf_element_handle_t handle, uint8_t src_idx, esp_ae_mixer_mode_t mode);
/**
* @brief Set audio information to the mixer handle
*
* @param[in] handle The mixer handle
* @param[in] sample_rate The audio sample rate
* @param[in] bits The audio bits per sample
* @param[in] channel The audio channel
*
* @return
* - ESP_GMF_ERR_OK Operation succeeded
* - ESP_GMF_ERR_INVALID_ARG Invalid input parameter
* - ESP_GMF_ERR_FAIL Failed to set configuration
*/
esp_gmf_err_t esp_gmf_mixer_set_audio_info(esp_gmf_element_handle_t handle, uint32_t sample_rate,
uint8_t bits, uint8_t channel);
#ifdef __cplusplus
}
#endif /* __cplusplus */
@@ -0,0 +1,55 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#pragma once
#include "esp_gmf_err.h"
#include "esp_ae_rate_cvt.h"
#include "esp_gmf_element.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define DEFAULT_ESP_GMF_RATE_CVT_CONFIG() { \
.src_rate = 44100, \
.dest_rate = 48000, \
.channel = 2, \
.bits_per_sample = 16, \
.complexity = 2, \
.perf_type = ESP_AE_RATE_CVT_PERF_TYPE_SPEED, \
}
/**
* @brief Initializes the GMF rate conversion with the provided configuration
*
* @param[in] config Pointer to the rate conversion configuration
* @param[out] handle Pointer to the rate conversion handle to be initialized
*
* @return
* - ESP_GMF_ERR_OK Success
* - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided
* - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory
*/
esp_gmf_err_t esp_gmf_rate_cvt_init(esp_ae_rate_cvt_cfg_t *config, esp_gmf_element_handle_t *handle);
/**
* @brief Set dest rate in the rate conversion handle
*
* @param[in] handle The rate conversion handle
* @param[in] dest_ch The dest rate
*
* @return
* - ESP_GMF_ERR_OK Operation succeeded
* - ESP_GMF_ERR_INVALID_ARG Invalid input parameter
* - ESP_GMF_ERR_FAIL Failed to set configuration
*/
esp_gmf_err_t esp_gmf_rate_cvt_set_dest_rate(esp_gmf_element_handle_t handle, uint32_t dest_rate);
#ifdef __cplusplus
}
#endif /* __cplusplus */
@@ -0,0 +1,92 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#pragma once
#include "esp_gmf_err.h"
#include "esp_ae_sonic.h"
#include "esp_gmf_element.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define DEFAULT_ESP_GMF_SONIC_CONFIG() { \
.sample_rate = 48000, \
.channel = 2, \
.bits_per_sample = 16, \
}
/**
* @brief Initializes the GMF sonic with the provided configuration
*
* @param[in] config Pointer to the sonic configuration
* @param[out] handle Pointer to the sonic handle to be initialized
*
* @return
* - ESP_GMF_ERR_OK Success
* - ESP_ERR_INVALID_ARG Invalid configuration provided
* - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory
*/
esp_gmf_err_t esp_gmf_sonic_init(esp_ae_sonic_cfg_t *config, esp_gmf_element_handle_t *handle);
/**
* @brief Set the audio speed
*
* @param[in] handle The handle of the sonic
* @param[in] speed The scaling factor of audio speed.
* The range of speed is [0.5, 2.0]
*
* @return
* - ESP_GMF_ERR_OK Operation succeeded
* - ESP_GMF_ERR_INVALID_ARG Invalid input parameter
*/
esp_gmf_err_t esp_gmf_sonic_set_speed(esp_gmf_element_handle_t handle, float speed);
/**
* @brief Get the audio speed
*
* @param[in] handle The handle of the sonic
* @param[out] speed The scaling factor of audio speed
*
* @return
* - ESP_GMF_ERR_OK Operation succeeded
* - ESP_GMF_ERR_INVALID_ARG Invalid input parameter
*/
esp_gmf_err_t esp_gmf_sonic_get_speed(esp_gmf_element_handle_t handle, float *speed);
/**
* @brief Set the audio pitch
*
* @param[in] handle The handle of the sonic
* @param[in] pitch The scaling factor of audio pitch.
* The range of pitch is [0.5, 2.0].
* If the pitch value is smaller than 1.0, the sound is deep voice;
* if the pitch value is equal to 1.0, the sound is no change;
* if the pitch value is gather than 1.0, the sound is sharp voice;
*
* @return
* - ESP_GMF_ERR_OK Operation succeeded
* - ESP_GMF_ERR_INVALID_ARG Invalid input parameter
*/
esp_gmf_err_t esp_gmf_sonic_set_pitch(esp_gmf_element_handle_t handle, float pitch);
/**
* @brief Get the audio pitch
*
* @param[in] handle The handle of the sonic
* @param[out] pitch The scaling factor of audio pitch
*
* @return
* - ESP_GMF_ERR_OK Operation succeeded
* - ESP_GMF_ERR_INVALID_ARG Invalid input parameter
*/
esp_gmf_err_t esp_gmf_sonic_get_pitch(esp_gmf_element_handle_t handle, float *pitch);
#ifdef __cplusplus
}
#endif /* __cplusplus */
@@ -0,0 +1,35 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
*
* See LICENSE file for details.
*/
#pragma once
#include "esp_err.h"
#include "esp_gmf_info.h"
#include "esp_gmf_audio_element.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define GMF_AUDIO_UPDATE_SND_INFO(self, sample_rate, bits, channel) gmf_audio_update_snd_info(self, sample_rate, bits, channel)
#define GMF_AUDIO_INPUT_SAMPLE_NUM (256)
#define GMF_AUDIO_CALC_PTS(out_len, sample_rate, ch, bits) ((out_len) * 8000 / ((sample_rate) * (ch) * (bits)))
static inline void gmf_audio_update_snd_info(void *self, uint32_t sample_rate, uint8_t bits, uint8_t channel)
{
esp_gmf_info_sound_t snd_info = {0};
esp_gmf_audio_el_get_snd_info(self, &snd_info);
snd_info.sample_rates = sample_rate;
snd_info.channels = channel;
snd_info.bits = bits;
esp_gmf_audio_el_set_snd_info(self, &snd_info);
esp_gmf_element_notify_snd_info(self, &snd_info);
}
#ifdef __cplusplus
}
#endif /* __cplusplus */