Skip to content

Commit cb4ed10

Browse files
committed
bugfix(esp_audio_render): Fixed mixer failed to restart
1 parent d42e012 commit cb4ed10

File tree

12 files changed

+375
-64
lines changed

12 files changed

+375
-64
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
## v0.7.1
2+
3+
### Features
4+
5+
- Added API `esp_audio_render_task_reconfigure` for reconfiguration of render task
6+
- Added `process_buf_align` to allow use special aligned buffer for audio processor
7+
- Added API `esp_audio_render_stream_set_mixer_gain` for changing of stream mixer gain
8+
- Added API `esp_audio_render_stream_set_fade` to do fade in/out when stream is mixing
9+
- Changed `ESP_AUDIO_RENDER_MIXER_THREAD_PRIORITY` default value to 20
10+
11+
### Bug Fixes
12+
13+
- Fixed double free caused by pipeline failed to create
14+
- Fixed mixer thread can not restart when all stream close and reopen
15+
- Warning for writing to ringfifo if timeout to prevent from resisting for further write
16+
17+
18+
## v0.7.0
19+
20+
### Features
21+
22+
- Initial version of `esp_audio_render`

packages/esp_audio_render/Kconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ menu "Espressif Audio Render Configuration"
99

1010
config ESP_AUDIO_RENDER_MIXER_THREAD_PRIORITY
1111
int "Audio render mixer thread priority"
12-
default 5
12+
default 20
1313
endmenu

packages/esp_audio_render/idf_component.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
version: "0.7.0"
1+
version: "0.7.1"
22
description: Espressif Audio Render is a module for render PCM audio through output device
33
url: https://github.com/espressif/esp-gmf/tree/main/packages/esp_audio_render
44
documentation: "https://github.com/espressif/esp-gmf/blob/main/packages/esp_audio_render/README.md"

packages/esp_audio_render/include/esp_audio_render.h

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include "esp_audio_render_types.h"
1111
#include "esp_gmf_element.h"
12+
#include "esp_gmf_task.h"
1213

1314
#ifdef __cplusplus
1415
extern "C" {
@@ -18,17 +19,19 @@ extern "C" {
1819
* @brief Audio render configuration
1920
*/
2021
typedef struct {
21-
uint8_t max_stream_num; /*!< Maximum supported stream number */
22-
esp_audio_render_write_cb_t out_writer; /*!< Audio render write callback */
23-
void *out_ctx; /*!< Audio render write context */
24-
esp_audio_render_sample_info_t out_sample_info; /*!< Output sample information, needs to be aligned with actual device setting */
25-
void *pool; /*!< GMF pool handle */
22+
uint8_t max_stream_num; /*!< Maximum supported stream number */
23+
esp_audio_render_write_cb_t out_writer; /*!< Audio render write callback */
24+
void *out_ctx; /*!< Audio render write context */
25+
esp_audio_render_sample_info_t out_sample_info; /*!< Output sample information, needs to be aligned with actual device setting */
26+
void *pool; /*!< GMF pool handle */
2627
uint16_t process_period; /*!< Audio processing interval in milliseconds (default: 20ms)
2728
This determines how frequently the audio mixer processes input streams
2829
Only valid for multiple streams which need mix processor
2930
- Shorter periods: Faster audio response but require more precise timing
3031
- Longer periods: More tolerant to buffer variations but increase latency
3132
This value controls the ring_fifo size used by `esp_audio_render_stream_write` also */
33+
uint8_t process_buf_align; /*!< When use hardware or optimized processor may need special buffer alignment
34+
If set to 0, the default value is 16 */
3235
} esp_audio_render_cfg_t;
3336

3437
/**
@@ -90,6 +93,25 @@ typedef int (*esp_audio_render_event_cb_t)(esp_audio_render_event_type_t event_t
9093
*/
9194
esp_audio_render_err_t esp_audio_render_create(esp_audio_render_cfg_t *cfg, esp_audio_render_handle_t *render);
9295

96+
/**
97+
* @brief Reconfigures the audio render task parameters
98+
*
99+
* @note This function must be called when no audio stream is active (no stream is opened yet)
100+
*
101+
* @note Default configuration (applied if never called):
102+
* - `stack_in_ext` = true (task stack allocated in external SPI-RAM)
103+
* - Other parameters use values from Kconfig defaults
104+
*
105+
* @param[in] render Audio render handle
106+
* @param[in] cfg Pointer to task configuration
107+
*
108+
* @return
109+
* - ESP_AUDIO_RENDER_ERR_OK On success
110+
* - ESP_AUDIO_RENDER_ERR_INVALID_ARG Invalid input argument
111+
* - ESP_AUDIO_RENDER_ERR_INVALID_STATE Called when any stream is opened
112+
*/
113+
esp_audio_render_err_t esp_audio_render_task_reconfigure(esp_audio_render_handle_t render, esp_gmf_task_config_t *cfg);
114+
93115
/**
94116
* @brief Set event callback for audio render
95117
*
@@ -182,6 +204,24 @@ esp_audio_render_err_t esp_audio_render_stream_get(esp_audio_render_handle_t ren
182204
esp_audio_render_stream_id_t stream_id,
183205
esp_audio_render_stream_handle_t *stream_handle);
184206

207+
/**
208+
* @brief Set mixer gain for audio render stream (optional)
209+
*
210+
* @note Currently only support to set when none stream is opened yet
211+
* When this API not call it will use default mixer gain [0, sqrt(1.0 / max_stream_num)]
212+
* Target gain should be limited to avoid clipping after mixed
213+
*
214+
* @param[in] stream Audio render handle
215+
* @param[in] mixer_gain Mixer gain to set
216+
*
217+
* @return
218+
* - ESP_AUDIO_RENDER_ERR_OK On success
219+
* - ESP_AUDIO_RENDER_ERR_INVALID_ARG Invalid input argument
220+
* - ESP_AUDIO_RENDER_ERR_INVALID_STATE Mixer already running
221+
*/
222+
esp_audio_render_err_t esp_audio_render_stream_set_mixer_gain(esp_audio_render_stream_handle_t stream,
223+
esp_audio_render_mixer_gain_t *mixer_gain);
224+
185225
/**
186226
* @brief Open audio render stream
187227
*
@@ -254,6 +294,24 @@ esp_audio_render_err_t esp_audio_render_stream_get_element(esp_audio_render_stre
254294
esp_audio_render_err_t esp_audio_render_stream_write(esp_audio_render_stream_handle_t stream,
255295
uint8_t *pcm_data, uint32_t pcm_size);
256296

297+
/**
298+
* @brief Fade in/out for audio render stream
299+
*
300+
* @note Fade in/out will only take effect in multiple stream cases
301+
* When fade in, mixer gain for this stream will goes from current to target gain
302+
* When fade out, mixer gain for this stream will goes from current to initial gain
303+
* Allow to set during runtime
304+
*
305+
* @param[in] stream Stream handle
306+
* @param[in] fade_in Fade operation (true: fade in, false: fade out)
307+
*
308+
* @return
309+
* - ESP_AUDIO_RENDER_ERR_OK On success
310+
* - ESP_AUDIO_RENDER_ERR_INVALID_ARG Invalid input argument
311+
*/
312+
esp_audio_render_err_t esp_audio_render_stream_set_fade(esp_audio_render_stream_handle_t stream,
313+
bool fade_in);
314+
257315
/**
258316
* @brief Pause audio render stream
259317
*

packages/esp_audio_render/include/esp_audio_render_types.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@ typedef struct {
5555
uint8_t channel; /*!< Audio channel */
5656
} esp_audio_render_sample_info_t;
5757

58+
/**
59+
* @brief Audio render mixer gain
60+
*/
61+
typedef struct {
62+
float initial_gain; /*!< Initial gain for mixer stream */
63+
float target_gain; /*!< Target gain for mixer stream */
64+
uint32_t transition_time; /*!< Transition time to smooth from initial to target or versa (unit ms) */
65+
} esp_audio_render_mixer_gain_t;
66+
5867
/**
5968
* @brief Audio render writer callback
6069
*

packages/esp_audio_render/private_inc/audio_render_proc.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
extern "C" {
1616
#endif /* __cplusplus */
1717

18+
#define SAME_SAMPLE_INFO(from, to) \
19+
((from).sample_rate == (to).sample_rate && (from).bits_per_sample == (to).bits_per_sample && (from).channel == (to).channel)
20+
1821
/**
1922
* @brief Audio render processor handle
2023
*/
@@ -33,6 +36,19 @@ typedef void *audio_render_proc_handle_t;
3336
*/
3437
esp_audio_render_err_t audio_render_proc_create(esp_gmf_pool_handle_t pool, audio_render_proc_handle_t *proc);
3538

39+
/**
40+
* @brief Set buffer alignment for audio render processor
41+
*
42+
* @param[in] proc Audio processor handle
43+
* @param[in] buf_align Audio render processor handle to store
44+
*
45+
* @return
46+
* - ESP_AUDIO_RENDER_ERR_OK On success
47+
* - ESP_AUDIO_RENDER_ERR_INVALID_ARG Invalid input argument
48+
* - ESP_AUDIO_RENDER_ERR_NO_MEM Not enough memory
49+
*/
50+
esp_audio_render_err_t audio_render_proc_set_buf_align(audio_render_proc_handle_t proc, uint8_t buf_align);
51+
3652
/**
3753
* @brief Add audio processor
3854
*

packages/esp_audio_render/src/audio_render_pipeline.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ static inline void add_proc(const char *pipeline_tag[], const char *proc_tag, ui
2525
uint8_t n = *element_num;
2626
// Not add duplicate one
2727
for (int i = 0; i < n; i++) {
28-
if (pipeline_tag[i] == proc_tag) {
28+
if (strcmp(pipeline_tag[i], proc_tag) == 0) {
2929
return;
3030
}
3131
}
@@ -312,6 +312,12 @@ esp_audio_render_err_t audio_render_pipeline_open(audio_render_pipeline_cfg_t *p
312312
pipeline);
313313
if (ret != ESP_GMF_ERR_OK) {
314314
ESP_LOGE(TAG, "Failed to new pipeline ret %d", ret);
315+
if (proc_cfg->in_port) {
316+
esp_gmf_port_deinit(proc_cfg->in_port);
317+
}
318+
if (proc_cfg->out_port) {
319+
esp_gmf_port_deinit(proc_cfg->out_port);
320+
}
315321
return ESP_AUDIO_RENDER_ERR_NO_RESOURCE;
316322
}
317323
esp_gmf_element_handle_t head_element = ESP_GMF_PIPELINE_GET_FIRST_ELEMENT((*pipeline));

packages/esp_audio_render/src/audio_render_proc.c

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,12 @@
1414

1515
#define TAG "AUD_RENDER_PROC"
1616

17-
#define SAME_SAMPLE_INFO(from, to) \
18-
((from).sample_rate == (to).sample_rate && (from).bits_per_sample == (to).bits_per_sample && (from).channel == (to).channel)
19-
2017
typedef struct {
2118
esp_gmf_pool_handle_t pool;
2219
esp_audio_render_proc_type_t *procs;
2320
esp_gmf_element_handle_t *proc_elements;
2421
uint8_t proc_num;
22+
uint8_t buf_align;
2523
esp_gmf_pipeline_handle_t pipeline;
2624
bool is_opened;
2725
bool is_error;
@@ -80,6 +78,18 @@ static esp_gmf_err_io_t sink_acquire(void *handle, esp_gmf_payload_t *load, uint
8078
return ESP_GMF_IO_OK;
8179
}
8280
if (proc->out_pcm_size < wanted_size) {
81+
if (proc->buf_align) {
82+
if (proc->out_pcm) {
83+
audio_render_free(proc->out_pcm);
84+
proc->out_pcm_size = 0;
85+
}
86+
proc->out_pcm = audio_render_malloc_align(wanted_size, proc->buf_align);
87+
if (proc->out_pcm == NULL) {
88+
// Not enough memory
89+
return ESP_GMF_IO_FAIL;
90+
}
91+
proc->out_pcm_size = wanted_size;
92+
}
8393
uint8_t *out_pcm = audio_render_realloc(proc->out_pcm, wanted_size);
8494
if (out_pcm == NULL) {
8595
// Not enough memory
@@ -167,6 +177,17 @@ esp_audio_render_err_t audio_render_proc_create(esp_gmf_pool_handle_t pool, audi
167177
return ESP_AUDIO_RENDER_ERR_OK;
168178
}
169179

180+
esp_audio_render_err_t audio_render_proc_set_buf_align(audio_render_proc_handle_t handle, uint8_t buf_align)
181+
{
182+
if (handle == NULL) {
183+
ESP_LOGE(TAG, "Invalid arg for handle:%p", handle);
184+
return ESP_AUDIO_RENDER_ERR_INVALID_ARG;
185+
}
186+
audio_proc_t *proc = (audio_proc_t*)handle;
187+
proc->buf_align = buf_align;
188+
return ESP_AUDIO_RENDER_ERR_OK;
189+
}
190+
170191
esp_audio_render_err_t audio_render_proc_add(audio_render_proc_handle_t handle, esp_audio_render_proc_type_t *procs,
171192
uint8_t proc_num)
172193
{
@@ -235,6 +256,12 @@ esp_audio_render_err_t audio_render_proc_open(audio_render_proc_handle_t handle,
235256
out_port = NEW_ESP_GMF_PORT_OUT_BLOCK(sink_acquire, sink_release, NULL, proc, 0, ESP_GMF_MAX_DELAY);
236257
if (in_port == NULL || out_port == NULL) {
237258
ESP_LOGE(TAG, "Fail to create port");
259+
if (in_port) {
260+
esp_gmf_port_deinit(in_port);
261+
}
262+
if (out_port) {
263+
esp_gmf_port_deinit(out_port);
264+
}
238265
break;
239266
}
240267
audio_render_pipeline_cfg_t pipeline_cfg = {
@@ -246,6 +273,7 @@ esp_audio_render_err_t audio_render_proc_open(audio_render_proc_handle_t handle,
246273
.proc_elements = proc->proc_elements,
247274
.proc_num = proc->proc_num,
248275
};
276+
// When create pipeline success port will takeover by pipeline
249277
ret = audio_render_pipeline_open(&pipeline_cfg, &proc->pipeline);
250278
if (ret != ESP_AUDIO_RENDER_ERR_OK) {
251279
ESP_LOGE(TAG, "Fail to create pipeline");
@@ -258,12 +286,6 @@ esp_audio_render_err_t audio_render_proc_open(audio_render_proc_handle_t handle,
258286
proc->is_opened = true;
259287
return ret;
260288
} while (0);
261-
if (in_port) {
262-
esp_gmf_port_deinit(in_port);
263-
}
264-
if (out_port) {
265-
esp_gmf_port_deinit(out_port);
266-
}
267289
audio_render_proc_close((audio_render_proc_handle_t) proc);
268290
return ESP_AUDIO_RENDER_ERR_NO_RESOURCE;
269291
}

0 commit comments

Comments
 (0)