@@ -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
|
||||
|
||||
- [](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
|
||||
|
||||
- [](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;
|
||||
}
|
||||
+517
@@ -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;
|
||||
}
|
||||
+701
@@ -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;
|
||||
}
|
||||
+60
@@ -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;
|
||||
}
|
||||
+102
@@ -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();
|
||||
}
|
||||
+280
@@ -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;
|
||||
}
|
||||
+304
@@ -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;
|
||||
}
|
||||
+274
@@ -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;
|
||||
}
|
||||
+270
@@ -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;
|
||||
}
|
||||
+437
@@ -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;
|
||||
}
|
||||
+276
@@ -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;
|
||||
}
|
||||
+435
@@ -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;
|
||||
}
|
||||
+19
@@ -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"
|
||||
+72
@@ -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 */
|
||||
+75
@@ -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 */
|
||||
+113
@@ -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 */
|
||||
+30
@@ -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 */
|
||||
+217
@@ -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 */
|
||||
+128
@@ -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
|
||||
+53
@@ -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 */
|
||||
+55
@@ -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 */
|
||||
+47
@@ -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 */
|
||||
+87
@@ -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 */
|
||||
+81
@@ -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 */
|
||||
+47
@@ -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 */
|
||||
+76
@@ -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 */
|
||||
+55
@@ -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 */
|
||||
+92
@@ -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 */
|
||||
+35
@@ -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 */
|
||||
Reference in New Issue
Block a user