-
Notifications
You must be signed in to change notification settings - Fork 15
Open
Description
Checklist
- Checked the issue tracker for similar issues to ensure this is not a duplicate.
- Provided a clear description of your suggestion.
- Included any relevant context or examples.
Issue or Suggestion Description
Description:
Hi, I’m using ESP-GMF to develop an interactive game. My goal is to stitch together a story from multiple MP3 files.
The challenge is that each file has a different runtime, so they may need to start/stop while another file is still playing. I also need to reuse pipelines to play different files once the current one is done (e.g., in a conversation).
Here’s a diagram illustrating my approach:

My current GMF architecture:

- Each track and the mixer have their own GMF-Task assigned.
- They are connected via a ring buffer at the moment.
Problems encountered:
- If I start the GMF-Task and one of the track pipelines is not started or has no input URI, there is no sound output at all.
- If one track pipeline finishes and I try to restart it with a different URI, I get a Task Timeout when attempting to restart the pipeline.
Question:
What is the correct way to:
- Keep pipelines functional even if some don’t have a URI yet?
- Properly restart a finished pipeline with a new URI without hitting a timeout?
This is my current Soucecode (Work in Progress, Skratch State)
#include "game_audio.h"
#include "utils/pinMap.h"
#include "sdkconfig.h"
#include "esp_gmf_io_codec_dev.h"
#include "esp_gmf_port.h"
#include "esp_gmf_pool.h"
#include "esp_gmf_pool.h"
#include "esp_gmf_err.h"
const char *TAG = "GAME_AUDIO";
#ifndef AUDIO_MANAGER_DEBUG_INFO
#define AUDIO_MANAGER_DEBUG_INFO(...)
#endif
#ifndef AUDIO_MANAGER_DEBUG_ERROR
#define AUDIO_MANAGER_DEBUG_ERROR(...)
#endif
#define GAME_AUDIO_EXPECT(func, expect, retVar) \
do { \
retVar = func; \
if (retVar != expect) { \
AUDIO_MANAGER_DEBUG_ERROR("'%s' failed at %s:%d", #func, __FILE__, __LINE__); \
goto _exit; \
} \
} while (0)
//*-----------------------------GMF Audio IO Init Functions------------------------*/
#include "esp_gmf_io.h"
#include "esp_gmf_io_file.h"
#include "esp_gmf_io_codec_dev.h"
static esp_gmf_err_t initGmfIo_fileReader(esp_gmf_pool_handle_t pool)
{
ESP_GMF_NULL_CHECK(TAG, pool, return ESP_GMF_ERR_INVALID_ARG);
esp_gmf_err_t ret = ESP_GMF_ERR_OK;
file_io_cfg_t fs_cfg = FILE_IO_CFG_DEFAULT();
esp_gmf_io_handle_t hd = NULL;
fs_cfg.dir = ESP_GMF_IO_DIR_READER;
ret = esp_gmf_io_file_init(&fs_cfg, &hd);
ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to init file io");
ret = esp_gmf_pool_register_io(pool, hd, "fileReader");
ESP_GMF_RET_ON_ERROR(TAG, ret, {esp_gmf_io_deinit(hd); return ret;}, "Failed to register file io");
return ret;
}
static esp_gmf_err_t initGmfIo_codecWriter(esp_gmf_pool_handle_t pool)
{
ESP_GMF_NULL_CHECK(TAG, pool, return ESP_GMF_ERR_INVALID_ARG);
esp_gmf_err_t ret = ESP_GMF_ERR_OK;
esp_gmf_io_handle_t dev = NULL;
codec_dev_io_cfg_t tx_codec_dev_cfg = ESP_GMF_IO_CODEC_DEV_CFG_DEFAULT();
tx_codec_dev_cfg.dir = ESP_GMF_IO_DIR_WRITER;
tx_codec_dev_cfg.dev = NULL;
ret = esp_gmf_io_codec_dev_init(&tx_codec_dev_cfg, &dev);
ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to init codec dev io");
ret = esp_gmf_pool_register_io(pool, dev, "codecWriter");
ESP_GMF_RET_ON_ERROR(TAG, ret, {esp_gmf_io_deinit(dev); return ret;}, "Failed to register codec dev io");
return ret;
}
//*--------------------------GMF Audio Decoder Init Functions----------------------*/
#include "esp_audio_dec_default.h"
#include "esp_audio_enc_default.h"
#include "esp_audio_simple_dec_default.h"
#include "esp_gmf_audio_dec.h"
static esp_gmf_err_t initGmfElement_mp3Decoder(esp_gmf_pool_handle_t pool)
{
ESP_GMF_NULL_CHECK(TAG, pool, return ESP_GMF_ERR_INVALID_ARG);
esp_gmf_err_t ret = ESP_GMF_ERR_OK;
ret = (esp_gmf_err_t) esp_mp3_dec_register();
ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to register mp3 decoder");
esp_audio_simple_dec_cfg_t dec_cfg = DEFAULT_ESP_GMF_AUDIO_DEC_CONFIG();
esp_gmf_element_handle_t hd = NULL;
dec_cfg.dec_type = ESP_AUDIO_SIMPLE_DEC_TYPE_MP3;
ret = esp_gmf_audio_dec_init(&dec_cfg, &hd);
ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to init audio dec");
ret = esp_gmf_pool_register_element(pool, hd, "mp3Decoder");
ESP_GMF_RET_ON_ERROR(TAG, ret, {esp_gmf_element_deinit(hd); return ret;}, "Failed to register element in pool");
return ret;
}
//*--------------------------GMF Audio Elements Init Functions---------------------*/
#include "esp_gmf_mixer.h"
#include "esp_gmf_rate_cvt.h"
#include "esp_gmf_bit_cvt.h"
#include "esp_gmf_ch_cvt.h"
static esp_gmf_err_t initGmfElement_mixer(esp_gmf_pool_handle_t pool, uint8_t numAudioTracks)
{
ESP_GMF_NULL_CHECK(TAG, pool, return ESP_GMF_ERR_INVALID_ARG);
esp_gmf_err_t ret = ESP_GMF_ERR_OK;
esp_gmf_element_handle_t hd = NULL;
// Configure Track Info for Mixer
esp_ae_mixer_info_t *mixerInputConfig = (esp_ae_mixer_info_t *)malloc(sizeof(esp_ae_mixer_info_t) * numAudioTracks);
if (mixerInputConfig == NULL) {
return ESP_GMF_ERR_MEMORY_LACK;
}
for(int i = 0; i < numAudioTracks; i++) {
mixerInputConfig[i].weight1 = 1.0f; // Default weight for each track
mixerInputConfig[i].weight2 = 1.0f; // Default weight for each track
mixerInputConfig[i].transit_time = 100; // Default transition time of 100 ms
}
// Configure Mixer
esp_ae_mixer_cfg_t mixer_cfg =
{
.channel = 1,
.sample_rate = 44100,
.bits_per_sample = 16,
.src_num = numAudioTracks,
.src_info = mixerInputConfig
};
// Initialize Mixer Element
ret = esp_gmf_mixer_init(&mixer_cfg, &hd);
free(mixerInputConfig); // Free the mixer input config after use
ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to init audio mixer");
// Register Mixer Element
ret = esp_gmf_pool_register_element(pool, hd, "mixer");
ESP_GMF_RET_ON_ERROR(TAG, ret, {esp_gmf_element_deinit(hd); return ret;}, "Failed to register element in pool");
return ret;
}
static esp_gmf_err_t initGmfElement_sampleRateConverter(esp_gmf_pool_handle_t pool)
{
ESP_GMF_NULL_CHECK(TAG, pool, return ESP_GMF_ERR_INVALID_ARG);
esp_gmf_err_t ret = ESP_GMF_ERR_OK;
esp_gmf_element_handle_t hd = NULL;
esp_ae_rate_cvt_cfg_t es_rate_cvt_cfg = DEFAULT_ESP_GMF_RATE_CVT_CONFIG();
es_rate_cvt_cfg.dest_rate = 44100;
es_rate_cvt_cfg.complexity = 2; // CPU vs Quality tradeoff (1=fast/low, 3=slow/high)
es_rate_cvt_cfg.perf_type = ESP_AE_RATE_CVT_PERF_TYPE_SPEED;
ret = esp_gmf_rate_cvt_init(&es_rate_cvt_cfg, &hd);
ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to init sampleRateConverter");
ret = esp_gmf_pool_register_element(pool, hd, "sampleRateConverter");
ESP_GMF_RET_ON_ERROR(TAG, ret, {esp_gmf_element_deinit(hd); return ret;}, "Failed to register sampleRateConverter element in pool");
return ret;
}
static esp_gmf_err_t initGmfElement_bitDepthConverter(esp_gmf_pool_handle_t pool)
{
ESP_GMF_NULL_CHECK(TAG, pool, return ESP_GMF_ERR_INVALID_ARG);
esp_gmf_err_t ret = ESP_GMF_ERR_OK;
esp_gmf_element_handle_t hd = NULL;
esp_ae_bit_cvt_cfg_t es_bit_cvt_cfg = DEFAULT_ESP_GMF_BIT_CVT_CONFIG();
es_bit_cvt_cfg.dest_bits = 16;
ret = esp_gmf_bit_cvt_init(&es_bit_cvt_cfg, &hd);
ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to init audio bit depth converter");
ret = esp_gmf_pool_register_element(pool, hd, "bitDepthConverter");
ESP_GMF_RET_ON_ERROR(TAG, ret, {esp_gmf_element_deinit(hd); return ret;}, "Failed to register audio bit depth converter element in pool");
return ret;
}
static esp_gmf_err_t initGmfElement_channelConverter(esp_gmf_pool_handle_t pool)
{
//There are 2 chCvt needed:
// 1. Stereo to Mono for the input tracks (always mono output)
// 2. Mono to Stereo for the final output to the DAC (always stereo output
ESP_GMF_NULL_CHECK(TAG, pool, return ESP_GMF_ERR_INVALID_ARG);
esp_gmf_err_t ret = ESP_GMF_ERR_OK;
esp_gmf_element_handle_t handleIn = NULL;
// 1. Stereo to Mono for the input tracks (always mono output)
esp_ae_ch_cvt_cfg_t es_ch_cvt_cfg = DEFAULT_ESP_GMF_CH_CVT_CONFIG();
es_ch_cvt_cfg.dest_ch = 1; // Always mono
ret = esp_gmf_ch_cvt_init(&es_ch_cvt_cfg, &handleIn);
ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to init channelConverter_in");
ret = esp_gmf_pool_register_element(pool, handleIn, "channelConverter_in");
ESP_GMF_RET_ON_ERROR(TAG, ret, {esp_gmf_element_deinit(handleIn); return ret;}, "Failed to register channelConverter_in element in pool");
// 2. Mono to Stereo for the final output to the DAC (always stereo output)
esp_gmf_element_handle_t handleOut = NULL; // Reset handle for new element
es_ch_cvt_cfg.dest_ch = 2; // Always stereo
ret = esp_gmf_ch_cvt_init(&es_ch_cvt_cfg, &handleOut);
ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to init channelConverter_out");
ret = esp_gmf_pool_register_element(pool, handleOut, "channelConverter_out");
ESP_GMF_RET_ON_ERROR(TAG, ret, {esp_gmf_element_deinit(handleOut); return ret;}, "Failed to register channelConverter_out element in pool");
return ret;
}
//*-------------------------GMF Audio Track Pipeline Functions-----------------------*//
#include "esp_gmf_pipeline.h"
#include "esp_gmf_task.h"
#include "esp_gmf_data_bus.h"
#include "esp_gmf_new_databus.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
typedef struct game_audio_track_s{
esp_gmf_task_handle_t task;
esp_gmf_pipeline_handle_t pipeline;
esp_gmf_db_handle_t db;
} game_audio_track_t;
esp_gmf_err_t trackEvent_callback(esp_gmf_event_pkt_t *pkt, void *ctx)
{
return ESP_GMF_ERR_OK;
}
static esp_gmf_err_t initGmfPipeline_track(esp_gmf_pool_handle_t pool, game_audio_track_t *track, int trackIndex)
{
ESP_GMF_NULL_CHECK(TAG, pool, return ESP_GMF_ERR_INVALID_ARG);
esp_gmf_err_t ret = ESP_GMF_ERR_OK;
// Create unique task name for each track
char trackTaskName[8] = "track_0"; // Base name for track tasks
trackTaskName[6] = '0' + trackIndex; // Set the track number in the task name
//Create Ring Buffer for each track
if(esp_gmf_db_new_ringbuf(4, 1024, &track->db) < 0) {
return ESP_GMF_ERR_MEMORY_LACK; // GMF Data Bus creation failed
}
// Initialize each track's pipeline
const char *elements[] = {"mp3Decoder", "channelConverter_in", "bitDepthConverter", "sampleRateConverter"};
const uint8_t num_elements = sizeof(elements) / sizeof(elements[0]);
if(esp_gmf_pool_new_pipeline(pool, "fileReader", elements, num_elements, NULL, &track->pipeline) != ESP_GMF_ERR_OK) {
AUDIO_MANAGER_DEBUG_ERROR("GMF Pool new pipeline failed for track %d", trackIndex);
ret = ESP_GMF_ERR_FAIL;
goto exit; // GMF Pool new pipeline failed
}
esp_gmf_task_cfg_t task_cfg = {
.thread = {
.stack = 4096, // Stack size for the track task
.prio = 6, // Priority of the track task
.core = 0, // Run on core 0
.stack_in_ext = 1 // Stack is in external memory (PSRAM)
},
.name = trackTaskName, // Name of the track task
.ctx = NULL, // No user context needed
.cb = NULL // No callback function needed
};
if(esp_gmf_task_init(&task_cfg, &track->task) != ESP_GMF_ERR_OK) {
AUDIO_MANAGER_DEBUG_ERROR("GMF Task initialization failed for track %d", trackIndex);
ret = ESP_GMF_ERR_FAIL;
goto exit; // GMF Task initialization failed
}
if(esp_gmf_pipeline_bind_task(track->pipeline, track->task) != ESP_GMF_ERR_OK) {
AUDIO_MANAGER_DEBUG_ERROR("GMF Pipeline bind task failed for track %d", trackIndex);
ret = ESP_GMF_ERR_FAIL;
goto exit; // GMF Pipeline bind task failed
}
if(esp_gmf_pipeline_loading_jobs(track->pipeline) != ESP_GMF_ERR_OK) {
AUDIO_MANAGER_DEBUG_ERROR("GMF Pipeline loading jobs failed for track %d", trackIndex);
ret = ESP_GMF_ERR_FAIL;
goto exit; // GMF Pipeline loading jobs failed
}
exit:
if(ret != ESP_GMF_ERR_OK) {
if(track->task) {
esp_gmf_task_deinit(track->task);
track->task = NULL;
}
if(track->pipeline) {
esp_gmf_pipeline_stop(track->pipeline);
track->pipeline = NULL;
}
if(track->db) {
esp_gmf_db_deinit(track->db);
track->db = NULL;
}
}
return ret;
}
typedef struct game_audio_mixer_s{
esp_gmf_task_handle_t task;
esp_gmf_pipeline_handle_t pipeline;
} game_audio_mixer_t;
static esp_gmf_err_t initGmfPipeline_mixer(esp_gmf_pool_handle_t pool, game_audio_mixer_t *mixer, game_audio_track_t *tracks, int numTracks)
{
ESP_GMF_NULL_CHECK(TAG, pool, return ESP_GMF_ERR_INVALID_ARG);
esp_gmf_err_t ret = ESP_GMF_ERR_OK;
// Create Mixer Pipeline
const char *elements[] = {"mixer", "channelConverter_out"};
const uint8_t numElements = sizeof(elements) / sizeof(elements[0]);
if(esp_gmf_pool_new_pipeline(pool, NULL, elements, numElements, "codecWriter", &mixer->pipeline) != ESP_GMF_ERR_OK) {
AUDIO_MANAGER_DEBUG_ERROR("GMF Pool new pipeline failed on mixer");
return ESP_GMF_ERR_FAIL; // GMF Pool new pipeline failed
}
// Connect each track's output to the mixer input
for(int i = 0; i < numTracks; i++) {
if(tracks[i].pipeline == NULL) {
AUDIO_MANAGER_DEBUG_ERROR("Track %d pipeline is NULL", i);
ret = ESP_GMF_ERR_FAIL;
goto exit; // Track pipeline is NULL
}
esp_gmf_port_handle_t out_port =
NEW_ESP_GMF_PORT_OUT_BYTE(
esp_gmf_db_acquire_write,
esp_gmf_db_release_write,
esp_gmf_db_deinit,
tracks[i].db,
4096, 300
)
;
esp_gmf_port_handle_t in_port =
NEW_ESP_GMF_PORT_IN_BYTE(
esp_gmf_db_acquire_read,
esp_gmf_db_release_read,
esp_gmf_db_deinit,
tracks[i].db,
4096, 300
)
;
if(esp_gmf_pipeline_connect_pipe(
tracks[i].pipeline, "sampleRateConverter", out_port,
mixer->pipeline, "mixer", in_port) != ESP_GMF_ERR_OK) {
AUDIO_MANAGER_DEBUG_ERROR("GMF Pipeline connect pipe failed for track %d", i);
ret = ESP_GMF_ERR_FAIL;
goto exit; // GMF Pipeline connect pipe failed
}
}
esp_gmf_task_cfg_t task_cfg = {
.thread = {
.stack = 4096, // Stack size for the track task
.prio = 5, // Priority of the track task
.core = 0, // Run on core 0
.stack_in_ext = 1 // Stack is in external memory (PSRAM)
},
.name = "mixer", // Name of the track task
.ctx = NULL, // No user context needed
.cb = NULL // No callback function needed
};
if(esp_gmf_task_init(&task_cfg, &mixer->task) != ESP_GMF_ERR_OK) {
AUDIO_MANAGER_DEBUG_ERROR("GMF Task initialization failed for mixer");
ret = ESP_GMF_ERR_FAIL;
goto exit; // GMF Task initialization failed
}
if(esp_gmf_pipeline_bind_task(mixer->pipeline, mixer->task) != ESP_GMF_ERR_OK) {
AUDIO_MANAGER_DEBUG_ERROR("GMF Pipeline bind task failed for mixer");
ret = ESP_GMF_ERR_FAIL;
goto exit; // GMF Pipeline bind task failed
}
if(esp_gmf_pipeline_loading_jobs(mixer->pipeline) != ESP_GMF_ERR_OK) {
AUDIO_MANAGER_DEBUG_ERROR("GMF Pipeline loading jobs failed for mixer");
ret = ESP_GMF_ERR_FAIL;
goto exit; // GMF Pipeline loading jobs failed
}
exit:
if(ret != ESP_GMF_ERR_OK) {
if(mixer->task) {
esp_gmf_task_deinit(mixer->task);
mixer->task = NULL;
}
if(mixer->pipeline) {
esp_gmf_pipeline_stop(mixer->pipeline);
mixer->pipeline = NULL;
}
}
return ret;
}
//*-------------------------ESP Codec Device Functions-----------------------*//
#include "tlv320dac3100.h"
static esp_codec_dev_handle_t s_codecDev = NULL;
esp_codec_dev_handle_t codecDev_init(void)
{
if(s_codecDev != NULL) {
return s_codecDev; // Already initialized
}
//Init I2C
i2c_master_bus_config_t i2c_cfg = {
.sda_io_num = RFID_I2C_SDA_PIN,
.scl_io_num = RFID_I2C_SCL_PIN,
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = I2C_NUM_0,
.flags.enable_internal_pullup = 1,
.glitch_ignore_cnt = 7,
};
i2c_master_bus_handle_t i2cHandle = NULL;
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_cfg, &i2cHandle));
tlv320dac3100_codec_cfg_t codec_cfg = {
.ctrl_if = tlv320dac3100_codec_ctrl_if_new(i2cHandle,i2c_cfg.i2c_port),
.enableHeadphoneDetection = true,
.monitoringPollIntervalMs = 300, //ms
.pll = {
.enable = true,
.clkIn = 0b01, // BCLK
//BCLK = Fin = 44.1kHz * 2 * 16 = 1.4112MHz
.p = 1,
.r = 8,
.j = 8,
.d = 0,
//PLL Fout = (Fin * R * (J + D/16384)) / P) = (1.4112MHz * 8 * (8 + 0/16384)) / 1) = 90.3168MHz
//(I) 512 KHz <= Fin/P <= 20 MHz
//(II) 80MHz <= Fout <= 110MHz
//(III) 4 <= R*J <= 259
//(see condition (7) TLV320DAC3100 datasheet (page 55))
},
.clk =
{
.clkIn = 0b11, // PLL_CLK
.nDacDivider = 4, // DAC_CLK = 90.3168MHz / 4 = 22.5792MHz
.mDacDivider = 4, // DAC_MOD_CLK = 22.5792MHz / 4 = 5.6448MHz
.dOsr = 128, // Fs = 5.6448MHz / 128 = 44.1kHz
// (I) DAC_CLK <= 48 MHz
// (II) DAC_MOD_CLK <= 6.758 MHz
// (III) Fs == 44.1kHz
// (IV) 2.8 MHz < (DOSR * DAC_fS) < 6.2 MHz
// (V) (MDAC * DOSR / 32) ≥ RC_FILTER (max 12)
},
.timer =
{
.useMClk = false
},
};
esp_codec_dev_cfg_t codec_dev_cfg = {
.dev_type = ESP_CODEC_DEV_TYPE_OUT,
};
codec_dev_cfg.data_if = tlv320dac3100_codec_data_if_new(
AUDIO_AMP_I2S_MCLK_PIN,
AUDIO_AMP_I2S_BCLK_PIN,
AUDIO_AMP_I2S_WS_PIN,
AUDIO_AMP_I2S_DOUT_PIN
);
codec_dev_cfg.codec_if = tlv320dac3100_codec_if_new(&codec_cfg);
s_codecDev = esp_codec_dev_new(&codec_dev_cfg);
return s_codecDev;
}
static esp_gmf_pool_handle_t s_pool = NULL;
static game_audio_mixer_t s_mixer = {0};
static game_audio_track_t *s_tracks = NULL;
static int s_numAudioTracks = 0;
static bool s_mixerRunning = false;
int game_audio_init(int numAudioTracks)
{
esp_gmf_err_t ret = ESP_GMF_ERR_OK;
esp_gmf_pool_init(&s_pool);
GAME_AUDIO_EXPECT(initGmfElement_bitDepthConverter(s_pool), ESP_GMF_ERR_OK, ret);
GAME_AUDIO_EXPECT(initGmfElement_channelConverter(s_pool), ESP_GMF_ERR_OK, ret);
GAME_AUDIO_EXPECT(initGmfElement_sampleRateConverter(s_pool), ESP_GMF_ERR_OK, ret);
GAME_AUDIO_EXPECT(initGmfElement_mp3Decoder(s_pool), ESP_GMF_ERR_OK, ret);
GAME_AUDIO_EXPECT(initGmfElement_mixer(s_pool, numAudioTracks), ESP_GMF_ERR_OK, ret);
GAME_AUDIO_EXPECT(initGmfIo_fileReader(s_pool), ESP_GMF_ERR_OK, ret);
GAME_AUDIO_EXPECT(initGmfIo_codecWriter(s_pool), ESP_GMF_ERR_OK, ret);
s_tracks = (game_audio_track_t *)malloc(sizeof(game_audio_track_t) * numAudioTracks);
if(s_tracks == NULL) {
ret = ESP_GMF_ERR_MEMORY_LACK;
goto _exit;
}
s_numAudioTracks = numAudioTracks;
for(int i = 0; i < numAudioTracks; i++) {
memset(&s_tracks[i], 0, sizeof(game_audio_track_t));
GAME_AUDIO_EXPECT(initGmfPipeline_track(s_pool, &s_tracks[i], i), ESP_GMF_ERR_OK, ret);
}
GAME_AUDIO_EXPECT(initGmfPipeline_mixer(s_pool, &s_mixer, s_tracks, numAudioTracks), ESP_GMF_ERR_OK, ret);
codecDev_init();
esp_codec_dev_sample_info_t fs = {
.bits_per_sample = 16,
.sample_rate = 44100,
.channel = 2,
};
esp_codec_dev_open(s_codecDev, &fs);
esp_codec_dev_set_out_vol(s_codecDev, 100);
GAME_AUDIO_EXPECT(esp_gmf_io_codec_dev_set_dev(ESP_GMF_PIPELINE_GET_OUT_INSTANCE(s_mixer.pipeline), s_codecDev), ESP_GMF_ERR_OK, ret);
// esp_gmf_pipeline_run(s_mixer.pipeline);
// for (size_t i = 0; i < numAudioTracks; i++)
// {
// esp_gmf_pipeline_run(s_tracks[i].pipeline);
// }
return ESP_GMF_ERR_OK;
_exit:
if(ret != ESP_GMF_ERR_OK) {
game_audio_deinit();
}
return ret;
}
int game_audio_deinit()
{
if(s_tracks) {
for(int i = 0; i < s_numAudioTracks; i++) {
if(s_tracks[i].task) {
esp_gmf_task_deinit(s_tracks[i].task);
s_tracks[i].task = NULL;
}
if(s_tracks[i].pipeline) {
esp_gmf_pipeline_stop(s_tracks[i].pipeline);
s_tracks[i].pipeline = NULL;
}
if(s_tracks[i].db) {
esp_gmf_db_deinit(s_tracks[i].db);
s_tracks[i].db = NULL;
}
}
free(s_tracks);
s_tracks = NULL;
}
if(s_mixer.task) {
esp_gmf_task_deinit(s_mixer.task);
s_mixer.task = NULL;
}
if(s_mixer.pipeline) {
esp_gmf_pipeline_stop(s_mixer.pipeline);
esp_gmf_pipeline_destroy(s_mixer.pipeline);
s_mixer.pipeline = NULL;
}
esp_gmf_pool_deinit(s_pool);
return ESP_GMF_ERR_OK;
}
int game_audio_playTrack(int trackIndex, const char *filePath)
{
ESP_LOGI(TAG, "Playing track %d: %s", trackIndex, filePath);
esp_gmf_pipeline_reset(s_tracks[trackIndex].pipeline);
esp_gmf_pipeline_set_in_uri(s_tracks[trackIndex].pipeline, filePath);
esp_gmf_pipeline_run(s_tracks[trackIndex].pipeline);
if(!s_mixerRunning) {
esp_gmf_pipeline_run(s_mixer.pipeline);
s_mixerRunning = true;
}
return ESP_GMF_ERR_OK;
}
used like this:
game_audio_init(2);
game_audio_playTrack(1, "/sdcard/B.mp3"); //(B.mp3 is 29.8 secs)
game_audio_playTrack(0, "/sdcard/A.mp3"); //(A.mp3 is 14.5 secs)
vTaskDelay(pdMS_TO_TICKS(15000)); // Wait for a 15 second (A.mp3 is 14.5 secs)
game_audio_playTrack(0, "/sdcard/A.mp3"); //Restart just to test
vTaskDelay(pdMS_TO_TICKS(15000)); // Let Audio Play
Metadata
Metadata
Assignees
Labels
No labels