MCUXpresso_MIMXRT1052xxxxB/boards/evkbimxrt1050/audio_examples/maestro_playback/app_streamer.c

458 lines
12 KiB
C

/*
* Copyright 2020-2022 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "fsl_common.h"
#include "fsl_debug_console.h"
#include "fsl_codec_common.h"
#include "app_data.h"
#include "app_streamer.h"
#include "streamer_pcm_app.h"
#include "main.h"
#include "logging.h"
#ifdef EAP_PROC
#include "eap_proc.h"
#endif
#define APP_STREAMER_MSG_QUEUE "app_queue"
#define STREAMER_TASK_NAME "Streamer"
#define STREAMER_MESSAGE_TASK_NAME "StreamerMessage"
#define STREAMER_TASK_STACK_SIZE 20 * 1024
#define STREAMER_MESSAGE_TASK_STACK_SIZE 1024
static STREAMER_T *streamer;
static ringbuf_t *audioBuffer;
OSA_MUTEX_HANDLE_DEFINE(audioMutex);
OSA_TASK_HANDLE_DEFINE(msg_thread);
static void (*progress_handler)(int current, int total);
extern codec_handle_t codecHandle;
extern app_handle_t app;
/*!
* @brief Streamer task for communicating messages
*
* This function is the entry point of a task that is manually created by
* STREAMER_Create. It listens on a message queue and receives status updates
* about errors, audio playback state and position. The application can make
* use of this data.
*
* @param arg Data to be passed to the task
*/
static void STREAMER_MessageTask(void *arg)
{
STREAMER_MSG_T msg;
bool exit_thread = false;
osa_status_t ret;
if (get_app_data()->logEnabled)
{
PRINTF("[EAP STREAMER] Message Task started\r\n");
}
while (!streamer)
{
OSA_TimeDelay(1);
}
if (streamer->mq_out == NULL)
{
PRINTF("[EAP STREAMER] osa_mq_open failed: %d\r\n");
return;
}
int trackLength = 0;
progress_handler(0, trackLength);
do
{
ret = OSA_MsgQGet(&streamer->mq_out, (void *)&msg, osaWaitForever_c);
if (ret != KOSA_StatusSuccess)
{
PRINTF("[EAP STREAMER] OSA_MsgQGet error: %d\r\n", ret);
continue;
}
switch (msg.id)
{
case STREAM_MSG_DESTROY_PIPELINE:
progress_handler(0, trackLength);
break;
case STREAM_MSG_ERROR:
PRINTF("[EAP STREAMER] STREAM_MSG_ERROR\r\n");
exit_thread = true;
progress_handler(0, 0);
/* propagate stop request to main application state machine */
get_eap_att_control()->command = kAttCmdStop;
break;
case STREAM_MSG_EOS:
if (get_app_data()->logEnabled)
{
PRINTF("\n[EAP STREAMER] STREAM_MSG_EOS\r\n");
}
progress_handler(0, trackLength);
exit_thread = true;
/* propagate stop request to main application state machine */
get_eap_att_control()->command = kAttCmdStop;
break;
case STREAM_MSG_UPDATE_POSITION:
if (get_app_data()->logEnabled)
{
PRINTF("[EAP STREAMER] STREAM_MSG_UPDATE_POSITION..");
PRINTF(" position: %d ms\r", msg.event_data);
}
progress_handler(msg.event_data, trackLength);
break;
case STREAM_MSG_UPDATE_DURATION:
if (get_app_data()->logEnabled)
{
PRINTF("[EAP STREAMER] STREAM_MSG_UPDATE_DURATION: %d\r\n", msg.event_data);
}
trackLength = msg.event_data;
break;
case STREAM_MSG_CLOSE_TASK:
if (get_app_data()->logEnabled)
{
PRINTF("[EAP STREAMER] STREAM_MSG_CLOSE_TASK\r\n");
}
exit_thread = true;
break;
default:
break;
}
} while (!exit_thread);
OSA_MsgQDestroy(&streamer->mq_out);
streamer->mq_out = NULL;
OSA_TaskDestroy(msg_thread);
}
int STREAMER_Read(uint8_t *data, uint32_t size)
{
uint32_t bytes_read;
OSA_MutexLock(&audioMutex, osaWaitForever_c);
bytes_read = ringbuf_read(audioBuffer, data, size);
OSA_MutexUnlock(&audioMutex);
if (bytes_read != size)
{
PRINTF("[STREAMER WARN] read underrun: size: %d, read: %d\r\n", size, bytes_read);
}
return bytes_read;
}
int STREAMER_Write(uint8_t *data, uint32_t size)
{
uint32_t written;
OSA_MutexLock(&audioMutex, osaWaitForever_c);
written = ringbuf_write(audioBuffer, data, size);
OSA_MutexUnlock(&audioMutex);
if (written != size)
{
PRINTF("[STREAMER ERR] write overflow: size %d, written %d\r\n", size, written);
}
return written;
}
bool STREAMER_IsPlaying(streamer_handle_t *handle)
{
return handle->audioPlaying;
}
void STREAMER_Start(streamer_handle_t *handle)
{
PRINTF("[STREAMER] start playback\r\n");
handle->audioPlaying = true;
streamer_set_state(handle->streamer, 0, STATE_PLAYING, true);
}
void STREAMER_Stop(streamer_handle_t *handle)
{
PRINTF("[STREAMER] stop playback\r\n");
handle->audioPlaying = false;
streamer_set_state(handle->streamer, 0, STATE_NULL, true);
/* Empty input ringbuffer. */
if (audioBuffer)
{
ringbuf_clear(audioBuffer);
}
}
status_t STREAMER_file_Create(char *filename, int volume)
{
STREAMER_CREATE_PARAM params;
ELEMENT_PROPERTY_T prop;
osa_task_def_t thread_attr;
int ret;
eap_att_control_t *control = get_eap_att_control();
/* Create message process thread */
thread_attr.tpriority = OSA_PRIORITY_HIGH;
thread_attr.tname = (uint8_t *)STREAMER_MESSAGE_TASK_NAME;
thread_attr.pthread = &STREAMER_MessageTask;
thread_attr.stacksize = STREAMER_MESSAGE_TASK_STACK_SIZE;
ret = OSA_TaskCreate(&msg_thread, &thread_attr, (void *)control);
if (KOSA_StatusSuccess != ret)
{
return kStatus_Fail;
}
/* Create streamer */
strcpy(params.out_mq_name, APP_STREAMER_MSG_QUEUE);
params.stack_size = STREAMER_TASK_STACK_SIZE;
#ifdef EAP_PROC
params.pipeline_type = STREAM_PIPELINE_EAP;
#else
params.pipeline_type = STREAM_PIPELINE_FILESYSTEM;
#endif
params.task_name = STREAMER_TASK_NAME;
params.in_dev_name = "";
params.out_dev_name = "";
streamer = streamer_create(&params);
if (!streamer)
{
return kStatus_Fail;
}
streamer_set_file(streamer, 0, filename, STATE_NULL, true);
#ifdef EAP_PROC
register_post_process(streamer);
#endif
prop.prop = PROP_AUDIOSINK_SET_VOLUME;
prop.val = volume;
streamer_set_property(streamer, prop, true);
return kStatus_Success;
}
/* EAP Audio Tuning Tool control integration - START */
/* this functions should not be called internally to prevent possible issues caused by broken state machine */
eap_att_code_t play()
{
init_logging();
/* Uncomment below to turn on full debug logging for the streamer. */
// set_debug_module(0xffffffff);
// set_debug_level(LOGLVL_DEBUG);
// get_debug_state();
streamer_pcm_init();
if (STREAMER_file_Create((char *)get_eap_att_control()->input, (int)get_eap_att_control()->volume) ==
kStatus_Success)
{
if (streamer_set_state(streamer, 0, STATE_PLAYING, true) == 0)
{
PRINTF("[EAP STREAMER] Starting playback\r\n");
return kEapAttCodeOk;
}
else
{
PRINTF("[EAP STREAMER] Playback start failed\r\n");
return kEapAttCodeStreamControlFailure;
}
}
else
{
PRINTF("[EAP STREAMER] create_stream failed\r\n");
}
return kEapAttCodeStreamCreateFailed;
}
eap_att_code_t resume()
{
if (streamer_set_state(streamer, 0, STATE_PLAYING, true) == 0)
{
return kEapAttCodeOk;
}
return kEapAttCodeStreamControlFailure;
}
eap_att_code_t pause()
{
if (streamer_set_state(streamer, 0, STATE_PAUSED, true) == 0)
{
return kEapAttCodeOk;
}
return kEapAttCodeStreamControlFailure;
}
eap_att_code_t seek(int32_t seek_time)
{
StreamData query1;
if (streamer_query_info(streamer, 0, INFO_DURATION, &query1, true) != 0)
{
return kEapAttCodeStreamControlFailure;
}
if (query1.value32u > 0U)
{
if ((uint32_t)seek_time > query1.value32u)
{
PRINTF(
"[SEEK STREAMER] No seek was performed because the seek time is longer than the duration of the audio "
"track.\r\n");
return kEapAttCodeOk;
}
if (streamer_seek_pipeline(streamer, 0, seek_time, true) == 0)
{
PRINTF("[SEEK STREAMER] The seek audio track to %u milliseconds was performed successfully.\r\n",
seek_time);
return kEapAttCodeOk;
}
}
return kEapAttCodeStreamControlFailure;
}
eap_att_code_t set_volume(int volume)
{
ELEMENT_PROPERTY_T prop;
prop.prop = PROP_AUDIOSINK_SET_VOLUME;
prop.val = volume;
if (streamer_set_property(streamer, prop, true) == 0)
{
return kEapAttCodeOk;
}
return kEapAttCodeStreamControlFailure;
}
eap_att_code_t reset()
{
return kEapAttCodeOk;
}
eap_att_code_t destroy()
{
if (streamer != NULL)
{
streamer_destroy(streamer);
streamer = 0;
/* Suspend the tasks for memory cleanup */
vTaskSuspend(app.shell_task_handle);
vTaskDelay(100);
vTaskResume(app.shell_task_handle);
if (audioBuffer != NULL)
{
ringbuf_destroy(audioBuffer);
audioBuffer = NULL;
}
deinit_logging();
#ifdef EAP_PROC
eap_att_control_t *control = get_eap_att_control();
control->handle = 0;
#endif
PRINTF("[EAP STREAMER] destroyed\r\n");
}
return kEapAttCodeOk;
}
eap_att_code_t stop()
{
if (streamer_set_state(streamer, 0, STATE_NULL, true) == 0)
{
/* Empty input ringbuffer. */
if (audioBuffer)
{
ringbuf_clear(audioBuffer);
}
destroy();
return kEapAttCodeOk;
}
return kEapAttCodeStreamControlFailure;
}
#ifdef EAP_PROC
static eap_att_code_t update()
{
eap_att_code_t status = kEapAttCodeOk;
PRINTF("[EAP_STREAMER] update notified\r\n");
eap_att_control_t *control = get_eap_att_control();
if (((get_app_data()->lastPreset != control->eapPreset)
#if (ALGORITHM_XO == 1)
|| (get_eap_att_control()->controlParam->XO_OperatingMode != get_app_data()->lastXOOperatingMode)
#endif
) &&
control->handle != 0)
{
if (get_app_data()->lastPreset != control->eapPreset)
{
PRINTF("[EAP_STREAMER] preset changed, forcing update\r\n");
get_app_data()->lastPreset = control->eapPreset;
}
else
{
// reset presets to bypass preset reload during eap init
get_app_data()->lastPreset = control->eapPreset = 0;
PRINTF("[EAP_STREAMER] force update is requested\r\n");
}
if (control->status == kAttRunning)
{
status = pause();
if (status == kEapAttCodeOk)
{
EAP_Deinit();
EAP_Init(&get_app_data()->eap_args);
status = resume();
}
}
else
{
EAP_Deinit();
EAP_Init(&get_app_data()->eap_args);
}
}
return status;
}
#endif
void STREAMER_Init(void)
{
eap_att_control_t *att_control = get_eap_att_control();
progress_handler = att_control->progress;
att_control->play = &play;
att_control->pause = &pause;
att_control->reset = &reset;
att_control->stop = &stop;
att_control->seek = &seek;
att_control->destroy = &destroy;
att_control->resume = &resume;
att_control->set_volume = &set_volume;
att_control->logme = PRINTF;
att_control->volume = 75;
#ifdef EAP_PROC
att_control->update = &update;
// explicitly specify platform during initialization
att_control->instParams->Platform = LVM_IMXRT1060;
#endif
}