/** ******************************************************************** * @file test_widget_speaker.c * @brief * * @copyright (c) 2018 DJI. All rights reserved. * * All information contained herein is, and remains, the property of DJI. * The intellectual and technical concepts contained herein are proprietary * to DJI and may be covered by U.S. and foreign patents, patents in process, * and protected by trade secret or copyright law. Dissemination of this * information, including but not limited to data and other proprietary * material(s) incorporated within the information, in any form, is strictly * prohibited without the express written consent of DJI. * * If you receive this source code without DJI’s authorization, you may not * further disseminate the information, and you must immediately remove the * source code and notify DJI of its removal. DJI reserves the right to pursue * legal actions against you for any loss(es) or damage(s) caused by your * failure to do so. * ********************************************************************* */ /* Includes ------------------------------------------------------------------*/ #ifdef SYSTEM_ARCH_LINUX #include "test_widget_speaker.h" #include "dji_logger.h" #include #include #include #include #include "utils/util_misc.h" #include "utils/util_md5.h" #ifdef OPUS_INSTALLED #include #endif /* Private constants ---------------------------------------------------------*/ #define WIDGET_SPEAKER_TASK_STACK_SIZE (2048) /*! Attention: replace your audio device name here. */ #define WIDGET_SPEAKER_USB_AUDIO_DEVICE_NAME "alsa_output.usb-C-Media_Electronics_Inc._USB_Audio_Device-00.analog-stereo" #define WIDGET_SPEAKER_AUDIO_OPUS_FILE_NAME "test_audio.opus" #define WIDGET_SPEAKER_AUDIO_PCM_FILE_NAME "test_audio.pcm" #define WIDGET_SPEAKER_TTS_FILE_NAME "test_tts.txt" #define WIDGET_SPEAKER_TTS_OUTPUT_FILE_NAME "tts_audio.wav" #define WIDGET_SPEAKER_TTS_FILE_MAX_SIZE (3000) /* The frame size is hardcoded for this sample code but it doesn't have to be */ #define WIDGET_SPEAKER_AUDIO_OPUS_MAX_PACKET_SIZE (3 * 1276) #define WIDGET_SPEAKER_AUDIO_OPUS_MAX_FRAME_SIZE (6 * 960) #define WIDGET_SPEAKER_AUDIO_OPUS_SAMPLE_RATE (16000) #define WIDGET_SPEAKER_AUDIO_OPUS_CHANNELS (1) #define WIDGET_SPEAKER_AUDIO_OPUS_DECODE_FRAME_SIZE (160) /* The speaker initialization parameters */ #define WIDGET_SPEAKER_DEFAULT_VOLUME (30) #define EKHO_INSTALLED (1) /* Private types -------------------------------------------------------------*/ /* Private values -------------------------------------------------------------*/ static T_DjiWidgetSpeakerHandler s_speakerHandler = {0}; static T_DjiMutexHandle s_speakerMutex = {0}; static T_DjiWidgetSpeakerState s_speakerState = {0}; static T_DjiTaskHandle s_widgetSpeakerTestThread; static FILE *s_audioFile = NULL; static FILE *s_ttsFile = NULL; static bool s_isDecodeFinished = true; /* Private functions declaration ---------------------------------------------*/ static void SetSpeakerState(E_DjiWidgetSpeakerState speakerState); static T_DjiReturnCode GetSpeakerState(T_DjiWidgetSpeakerState *speakerState); static T_DjiReturnCode SetWorkMode(E_DjiWidgetSpeakerWorkMode workMode); static T_DjiReturnCode GetWorkMode(E_DjiWidgetSpeakerWorkMode *workMode); static T_DjiReturnCode SetPlayMode(E_DjiWidgetSpeakerPlayMode playMode); static T_DjiReturnCode GetPlayMode(E_DjiWidgetSpeakerPlayMode *playMode); static T_DjiReturnCode StartPlay(void); static T_DjiReturnCode StopPlay(void); static T_DjiReturnCode SetVolume(uint8_t volume); static T_DjiReturnCode GetVolume(uint8_t *volume); static T_DjiReturnCode ReceiveTtsData(E_DjiWidgetTransmitDataEvent event, uint32_t offset, uint8_t *buf, uint16_t size); static T_DjiReturnCode ReceiveAudioData(E_DjiWidgetTransmitDataEvent event, uint32_t offset, uint8_t *buf, uint16_t size); static void *DjiTest_WidgetSpeakerTask(void *arg); static uint32_t DjiTest_GetVoicePlayProcessId(void); static uint32_t DjiTest_KillVoicePlayProcess(uint32_t pid); static T_DjiReturnCode DjiTest_DecodeAudioData(void); static T_DjiReturnCode DjiTest_PlayAudioData(void); static T_DjiReturnCode DjiTest_PlayTtsData(void); static T_DjiReturnCode DjiTest_CheckFileMd5Sum(const char *path, uint8_t *buf, uint16_t size); /* Exported functions definition ---------------------------------------------*/ T_DjiReturnCode DjiTest_WidgetSpeakerStartService(void) { T_DjiReturnCode returnCode; T_DjiOsalHandler *osalHandler = DjiPlatform_GetOsalHandler(); s_speakerHandler.GetSpeakerState = GetSpeakerState; s_speakerHandler.SetWorkMode = SetWorkMode; s_speakerHandler.GetWorkMode = GetWorkMode; s_speakerHandler.StartPlay = StartPlay; s_speakerHandler.StopPlay = StopPlay; s_speakerHandler.SetPlayMode = SetPlayMode; s_speakerHandler.GetPlayMode = GetPlayMode; s_speakerHandler.SetVolume = SetVolume; s_speakerHandler.GetVolume = GetVolume; s_speakerHandler.ReceiveTtsData = ReceiveTtsData; s_speakerHandler.ReceiveVoiceData = ReceiveAudioData; returnCode = osalHandler->MutexCreate(&s_speakerMutex); if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("Create speaker mutex error: 0x%08llX", returnCode); return returnCode; } returnCode = DjiWidget_RegSpeakerHandler(&s_speakerHandler); if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("Register speaker handler error: 0x%08llX", returnCode); return returnCode; } returnCode = osalHandler->MutexLock(s_speakerMutex); if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("lock mutex error: 0x%08llX.", returnCode); return returnCode; } s_speakerState.state = DJI_WIDGET_SPEAKER_STATE_IDEL; s_speakerState.workMode = DJI_WIDGET_SPEAKER_WORK_MODE_VOICE; s_speakerState.playMode = DJI_WIDGET_SPEAKER_PLAY_MODE_SINGLE_PLAY; returnCode = osalHandler->MutexUnlock(s_speakerMutex); if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("unlock mutex error: 0x%08llX.", returnCode); return returnCode; } returnCode = SetVolume(WIDGET_SPEAKER_DEFAULT_VOLUME); if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("Set speaker volume error: 0x%08llX", returnCode); return returnCode; } if (osalHandler->TaskCreate("user_widget_speaker_task", DjiTest_WidgetSpeakerTask, WIDGET_SPEAKER_TASK_STACK_SIZE, NULL, &s_widgetSpeakerTestThread) != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("Dji widget speaker test task create error."); return DJI_ERROR_SYSTEM_MODULE_CODE_UNKNOWN; } return DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS; } /* Private functions definition-----------------------------------------------*/ static uint32_t DjiTest_GetVoicePlayProcessId(void) { FILE *fp; char cmdStr[128]; uint32_t pid; int ret; snprintf(cmdStr, 128, "pgrep ffplay"); fp = popen(cmdStr, "r"); if (fp == NULL) { USER_LOG_ERROR("fp is null."); return 0; } ret = fscanf(fp, "%u", &pid); if (ret <= 0) { pid = 0; goto out; } out: pclose(fp); return pid; } static uint32_t DjiTest_KillVoicePlayProcess(uint32_t pid) { FILE *fp; char cmdStr[128]; snprintf(cmdStr, 128, "kill %d", pid); fp = popen(cmdStr, "r"); if (fp == NULL) { USER_LOG_ERROR("fp is null."); return 0; } pclose(fp); return pid; } static T_DjiReturnCode DjiTest_DecodeAudioData(void) { #ifdef OPUS_INSTALLED FILE *fin; FILE *fout; OpusDecoder *decoder; opus_int16 out[WIDGET_SPEAKER_AUDIO_OPUS_MAX_FRAME_SIZE * WIDGET_SPEAKER_AUDIO_OPUS_CHANNELS]; uint8_t cbits[WIDGET_SPEAKER_AUDIO_OPUS_MAX_PACKET_SIZE]; int32_t nbBytes; int32_t err; /*! Attention: you can use "ffmpeg -i xxx.mp3 -ar 16000 -ac 1 out.wav" and use opus-tools to generate opus file for test */ fin = fopen(WIDGET_SPEAKER_AUDIO_OPUS_FILE_NAME, "r"); if (fin == NULL) { fprintf(stderr, "failed to open input file: %s\n", strerror(errno)); return EXIT_FAILURE; } /* Create a new decoder state. */ decoder = opus_decoder_create(WIDGET_SPEAKER_AUDIO_OPUS_SAMPLE_RATE, WIDGET_SPEAKER_AUDIO_OPUS_CHANNELS, &err); if (err < 0) { fprintf(stderr, "failed to create decoder: %s\n", opus_strerror(err)); return EXIT_FAILURE; } fout = fopen(WIDGET_SPEAKER_AUDIO_PCM_FILE_NAME, "w"); if (fout == NULL) { fprintf(stderr, "failed to open output file: %s\n", strerror(errno)); return EXIT_FAILURE; } USER_LOG_INFO("Decode Start..."); while (1) { int i; unsigned char pcm_bytes[WIDGET_SPEAKER_AUDIO_OPUS_MAX_FRAME_SIZE * WIDGET_SPEAKER_AUDIO_OPUS_CHANNELS * 2]; int frame_size; /* Read a 16 bits/sample audio frame. */ nbBytes = fread(cbits, 1, WIDGET_SPEAKER_AUDIO_OPUS_DECODE_FRAME_SIZE, fin); if (feof(fin)) break; /* Decode the data. In this example, frame_size will be constant because the encoder is using a constant frame size. However, that may not be the case for all encoders, so the decoder must always check the frame size returned. */ frame_size = opus_decode(decoder, cbits, nbBytes, out, WIDGET_SPEAKER_AUDIO_OPUS_MAX_FRAME_SIZE, 0); if (frame_size < 0) { fprintf(stderr, "decoder failed: %s\n", opus_strerror(frame_size)); return EXIT_FAILURE; } USER_LOG_DEBUG("decode data to file: %d\r\n", frame_size * WIDGET_SPEAKER_AUDIO_OPUS_CHANNELS); /* Convert to little-endian ordering. */ for (i = 0; i < WIDGET_SPEAKER_AUDIO_OPUS_CHANNELS * frame_size; i++) { pcm_bytes[2 * i] = out[i] & 0xFF; pcm_bytes[2 * i + 1] = (out[i] >> 8) & 0xFF; } /* Write the decoded audio to file. */ fwrite(pcm_bytes, sizeof(short), frame_size * WIDGET_SPEAKER_AUDIO_OPUS_CHANNELS, fout); } /*Destroy the encoder state*/ opus_decoder_destroy(decoder); fclose(fin); fclose(fout); USER_LOG_INFO("Decode Finished..."); s_isDecodeFinished = true; #endif return EXIT_SUCCESS; } static T_DjiReturnCode DjiTest_PlayAudioData(void) { char cmdStr[128]; memset(cmdStr, 0, sizeof(cmdStr)); USER_LOG_INFO("Start Playing..."); snprintf(cmdStr, sizeof(cmdStr), "ffplay -nodisp -autoexit -ar 16000 -ac 1 -f s16le -i %s 2>/dev/null", WIDGET_SPEAKER_AUDIO_PCM_FILE_NAME); return DjiUserUtil_RunSystemCmd(cmdStr); } static T_DjiReturnCode DjiTest_PlayTtsData(void) { FILE *txtFile; uint8_t data[WIDGET_SPEAKER_TTS_FILE_MAX_SIZE] = {0}; int32_t readLen; char cmdStr[WIDGET_SPEAKER_TTS_FILE_MAX_SIZE + 128]; txtFile = fopen(WIDGET_SPEAKER_TTS_FILE_NAME, "r"); if (txtFile == NULL) { fprintf(stderr, "failed to open input file: %s\n", strerror(errno)); return EXIT_FAILURE; } readLen = fread(data, 1, WIDGET_SPEAKER_TTS_FILE_MAX_SIZE, txtFile); if (readLen <= 0) { USER_LOG_ERROR("Read tts file failed, error code: %d", readLen); fclose(txtFile); return DJI_ERROR_SYSTEM_MODULE_CODE_NOT_FOUND; } fclose(txtFile); USER_LOG_INFO("Read tts file success, len: %d", readLen); USER_LOG_INFO("Content: %s", data); memset(cmdStr, 0, sizeof(cmdStr)); SetSpeakerState(DJI_WIDGET_SPEAKER_STATE_IN_TTS_CONVERSION); #ifdef EKHO_INSTALLED /*! Attention: you can use other tts opensource function to convert txt to speech, example used ekho v7.5 */ snprintf(cmdStr, sizeof(cmdStr), " ekho %s -s 20 -p 20 -a 100", data); #else snprintf(cmdStr, sizeof(cmdStr), "tts_offline_sample '%s' %s", data, WIDGET_SPEAKER_TTS_OUTPUT_FILE_NAME); DjiUserUtil_RunSystemCmd(cmdStr); SetSpeakerState(DJI_WIDGET_SPEAKER_STATE_PLAYING); USER_LOG_INFO("Start TTS Playing..."); memset(cmdStr, 0, sizeof(cmdStr)); snprintf(cmdStr, sizeof(cmdStr), "ffplay -nodisp -autoexit -ar 16000 -ac 1 -f s16le -i %s 2>/dev/null", WIDGET_SPEAKER_TTS_OUTPUT_FILE_NAME); #endif return DjiUserUtil_RunSystemCmd(cmdStr); } static T_DjiReturnCode DjiTest_CheckFileMd5Sum(const char *path, uint8_t *buf, uint16_t size) { MD5_CTX md5Ctx; uint32_t readFileTotalSize = 0; uint16_t readLen; T_DjiReturnCode returnCode; uint8_t readBuf[1024]; uint8_t md5Sum[16]; FILE *file = NULL;; UtilMd5_Init(&md5Ctx); file = fopen(path, "rb"); if (file == NULL) { USER_LOG_ERROR("Open tts file error."); } while (1) { returnCode = fseek(file, readFileTotalSize, SEEK_SET); if (returnCode != 0) { USER_LOG_INFO("fseek file error"); } readLen = fread(readBuf, 1, sizeof(readBuf), file); if (readLen > 0) { readFileTotalSize += readLen; UtilMd5_Update(&md5Ctx, readBuf, readLen); } if (feof(file)) break; } UtilMd5_Final(&md5Ctx, md5Sum); fclose(file); if (size == sizeof(md5Sum)) { if (memcmp(md5Sum, buf, sizeof(md5Sum)) == 0) { USER_LOG_INFO("MD5 sum check success"); return DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS; } else { return DJI_ERROR_SYSTEM_MODULE_CODE_SYSTEM_ERROR; } } else { USER_LOG_ERROR("MD5 sum length error"); } return DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS; } static void SetSpeakerState(E_DjiWidgetSpeakerState speakerState) { T_DjiReturnCode returnCode; T_DjiOsalHandler *osalHandler = DjiPlatform_GetOsalHandler(); returnCode = osalHandler->MutexLock(s_speakerMutex); if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("lock mutex error: 0x%08llX.", returnCode); } s_speakerState.state = speakerState; returnCode = osalHandler->MutexUnlock(s_speakerMutex); if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("unlock mutex error: 0x%08llX.", returnCode); } } static T_DjiReturnCode GetSpeakerState(T_DjiWidgetSpeakerState *speakerState) { T_DjiReturnCode returnCode; T_DjiOsalHandler *osalHandler = DjiPlatform_GetOsalHandler(); returnCode = osalHandler->MutexLock(s_speakerMutex); if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("lock mutex error: 0x%08llX.", returnCode); return returnCode; } *speakerState = s_speakerState; returnCode = osalHandler->MutexUnlock(s_speakerMutex); if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("unlock mutex error: 0x%08llX.", returnCode); return returnCode; } return DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS; } static T_DjiReturnCode SetWorkMode(E_DjiWidgetSpeakerWorkMode workMode) { T_DjiReturnCode returnCode; T_DjiOsalHandler *osalHandler = DjiPlatform_GetOsalHandler(); returnCode = osalHandler->MutexLock(s_speakerMutex); if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("lock mutex error: 0x%08llX.", returnCode); return returnCode; } USER_LOG_INFO("Set widget speaker work mode: %d", workMode); s_speakerState.workMode = workMode; returnCode = osalHandler->MutexUnlock(s_speakerMutex); if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("unlock mutex error: 0x%08llX.", returnCode); return returnCode; } return DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS; } static T_DjiReturnCode GetWorkMode(E_DjiWidgetSpeakerWorkMode *workMode) { T_DjiReturnCode returnCode; T_DjiOsalHandler *osalHandler = DjiPlatform_GetOsalHandler(); returnCode = osalHandler->MutexLock(s_speakerMutex); if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("lock mutex error: 0x%08llX.", returnCode); return returnCode; } *workMode = s_speakerState.workMode; returnCode = osalHandler->MutexUnlock(s_speakerMutex); if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("unlock mutex error: 0x%08llX.", returnCode); return returnCode; } return DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS; } static T_DjiReturnCode SetPlayMode(E_DjiWidgetSpeakerPlayMode playMode) { T_DjiReturnCode returnCode; T_DjiOsalHandler *osalHandler = DjiPlatform_GetOsalHandler(); returnCode = osalHandler->MutexLock(s_speakerMutex); if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("lock mutex error: 0x%08llX.", returnCode); return returnCode; } USER_LOG_INFO("Set widget speaker play mode: %d", playMode); s_speakerState.playMode = playMode; returnCode = osalHandler->MutexUnlock(s_speakerMutex); if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("unlock mutex error: 0x%08llX.", returnCode); return returnCode; } return DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS; } static T_DjiReturnCode GetPlayMode(E_DjiWidgetSpeakerPlayMode *playMode) { T_DjiReturnCode returnCode; T_DjiOsalHandler *osalHandler = DjiPlatform_GetOsalHandler(); returnCode = osalHandler->MutexLock(s_speakerMutex); if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("lock mutex error: 0x%08llX.", returnCode); return returnCode; } *playMode = s_speakerState.playMode; returnCode = osalHandler->MutexUnlock(s_speakerMutex); if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("unlock mutex error: 0x%08llX.", returnCode); return returnCode; } return DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS; } static T_DjiReturnCode StartPlay(void) { uint32_t pid; T_DjiOsalHandler *osalHandler = DjiPlatform_GetOsalHandler(); pid = DjiTest_GetVoicePlayProcessId(); if (pid != 0) { DjiTest_KillVoicePlayProcess(pid); } osalHandler->TaskSleepMs(5); USER_LOG_INFO("Start widget speaker play"); SetSpeakerState(DJI_WIDGET_SPEAKER_STATE_PLAYING); return DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS; } static T_DjiReturnCode StopPlay(void) { T_DjiReturnCode returnCode; T_DjiOsalHandler *osalHandler = DjiPlatform_GetOsalHandler(); uint32_t pid; returnCode = osalHandler->MutexLock(s_speakerMutex); if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("lock mutex error: 0x%08llX.", returnCode); return returnCode; } USER_LOG_INFO("Stop widget speaker play"); s_speakerState.state = DJI_WIDGET_SPEAKER_STATE_IDEL; pid = DjiTest_GetVoicePlayProcessId(); if (pid != 0) { DjiTest_KillVoicePlayProcess(pid); } returnCode = osalHandler->MutexUnlock(s_speakerMutex); if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("unlock mutex error: 0x%08llX.", returnCode); return returnCode; } return DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS; } static T_DjiReturnCode SetVolume(uint8_t volume) { T_DjiReturnCode returnCode; T_DjiOsalHandler *osalHandler = DjiPlatform_GetOsalHandler(); char cmdStr[128]; int32_t ret = 0; float realVolume; returnCode = osalHandler->MutexLock(s_speakerMutex); if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("lock mutex error: 0x%08llX.", returnCode); return returnCode; } realVolume = 1.5f * (float) volume; USER_LOG_INFO("Set widget speaker volume: %d", volume); s_speakerState.volume = volume; memset(cmdStr, 0, sizeof(cmdStr)); snprintf(cmdStr, sizeof(cmdStr), "pactl set-sink-volume %s %d%%", WIDGET_SPEAKER_USB_AUDIO_DEVICE_NAME, (int32_t) realVolume); returnCode = DjiUserUtil_RunSystemCmd(cmdStr); if (returnCode < 0) { USER_LOG_ERROR("Set widget speaker volume error: %d", ret); } returnCode = osalHandler->MutexUnlock(s_speakerMutex); if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("unlock mutex error: 0x%08llX.", returnCode); return returnCode; } return DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS; } static T_DjiReturnCode GetVolume(uint8_t *volume) { T_DjiReturnCode returnCode; T_DjiOsalHandler *osalHandler = DjiPlatform_GetOsalHandler(); returnCode = osalHandler->MutexLock(s_speakerMutex); if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("lock mutex error: 0x%08llX.", returnCode); return returnCode; } *volume = s_speakerState.volume; returnCode = osalHandler->MutexUnlock(s_speakerMutex); if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("unlock mutex error: 0x%08llX.", returnCode); return returnCode; } return DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS; } static T_DjiReturnCode ReceiveTtsData(E_DjiWidgetTransmitDataEvent event, uint32_t offset, uint8_t *buf, uint16_t size) { uint16_t writeLen; T_DjiReturnCode returnCode; if (event == DJI_WIDGET_TRANSMIT_DATA_EVENT_START) { USER_LOG_INFO("Create tts file."); s_ttsFile = fopen(WIDGET_SPEAKER_TTS_FILE_NAME, "wb"); if (s_ttsFile == NULL) { USER_LOG_ERROR("Open tts file error."); } if (s_speakerState.state != DJI_WIDGET_SPEAKER_STATE_PLAYING) { SetSpeakerState(DJI_WIDGET_SPEAKER_STATE_TRANSMITTING); } } else if (event == DJI_WIDGET_TRANSMIT_DATA_EVENT_TRANSMIT) { USER_LOG_INFO("Transmit tts file, offset: %d, size: %d", offset, size); if (s_ttsFile != NULL) { fseek(s_ttsFile, offset, SEEK_SET); writeLen = fwrite(buf, 1, size, s_ttsFile); if (writeLen != size) { USER_LOG_ERROR("Write tts file error %d", writeLen); } } if (s_speakerState.state != DJI_WIDGET_SPEAKER_STATE_PLAYING) { SetSpeakerState(DJI_WIDGET_SPEAKER_STATE_TRANSMITTING); } } else if (event == DJI_WIDGET_TRANSMIT_DATA_EVENT_FINISH) { USER_LOG_INFO("Close tts file."); if (s_ttsFile != NULL) { fclose(s_ttsFile); } returnCode = DjiTest_CheckFileMd5Sum(WIDGET_SPEAKER_TTS_FILE_NAME, buf, size); if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("File md5 sum check failed"); } if (s_speakerState.state != DJI_WIDGET_SPEAKER_STATE_PLAYING) { SetSpeakerState(DJI_WIDGET_SPEAKER_STATE_IDEL); } } return DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS; } static T_DjiReturnCode ReceiveAudioData(E_DjiWidgetTransmitDataEvent event, uint32_t offset, uint8_t *buf, uint16_t size) { uint16_t writeLen; T_DjiReturnCode returnCode; if (event == DJI_WIDGET_TRANSMIT_DATA_EVENT_START) { s_isDecodeFinished = false; USER_LOG_INFO("Create voice file."); s_audioFile = fopen(WIDGET_SPEAKER_AUDIO_OPUS_FILE_NAME, "wb"); if (s_audioFile == NULL) { USER_LOG_ERROR("Create tts file error."); } if (s_speakerState.state != DJI_WIDGET_SPEAKER_STATE_PLAYING) { SetSpeakerState(DJI_WIDGET_SPEAKER_STATE_TRANSMITTING); } } else if (event == DJI_WIDGET_TRANSMIT_DATA_EVENT_TRANSMIT) { USER_LOG_INFO("Transmit voice file, offset: %d, size: %d", offset, size); if (s_audioFile != NULL) { fseek(s_audioFile, offset, SEEK_SET); writeLen = fwrite(buf, 1, size, s_audioFile); if (writeLen != size) { USER_LOG_ERROR("Write tts file error %d", writeLen); } } if (s_speakerState.state != DJI_WIDGET_SPEAKER_STATE_PLAYING) { SetSpeakerState(DJI_WIDGET_SPEAKER_STATE_TRANSMITTING); } } else if (event == DJI_WIDGET_TRANSMIT_DATA_EVENT_FINISH) { USER_LOG_INFO("Close voice file."); if (s_audioFile != NULL) { fclose(s_audioFile); } returnCode = DjiTest_CheckFileMd5Sum(WIDGET_SPEAKER_AUDIO_OPUS_FILE_NAME, buf, size); if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("File md5 sum check failed"); return DJI_ERROR_SYSTEM_MODULE_CODE_SYSTEM_ERROR; } if (s_speakerState.state != DJI_WIDGET_SPEAKER_STATE_PLAYING) { SetSpeakerState(DJI_WIDGET_SPEAKER_STATE_IDEL); } DjiTest_DecodeAudioData(); } return DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS; } #ifndef __CC_ARM #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmissing-noreturn" #pragma GCC diagnostic ignored "-Wreturn-type" #endif static void *DjiTest_WidgetSpeakerTask(void *arg) { T_DjiReturnCode djiReturnCode; T_DjiOsalHandler *osalHandler = DjiPlatform_GetOsalHandler(); USER_UTIL_UNUSED(arg); while (1) { osalHandler->TaskSleepMs(10); if (s_speakerState.state == DJI_WIDGET_SPEAKER_STATE_PLAYING) { if (s_speakerState.playMode == DJI_WIDGET_SPEAKER_PLAY_MODE_LOOP_PLAYBACK) { if (s_speakerState.workMode == DJI_WIDGET_SPEAKER_WORK_MODE_VOICE) { USER_LOG_WARN("Waiting opus decoder finished..."); while (s_isDecodeFinished == false) { osalHandler->TaskSleepMs(1); } djiReturnCode = DjiTest_PlayAudioData(); if (djiReturnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("Play audio data failed, error: 0x%08llX.", djiReturnCode); } } else { djiReturnCode = DjiTest_PlayTtsData(); if (djiReturnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("Play tts data failed, error: 0x%08llX.", djiReturnCode); } } osalHandler->TaskSleepMs(1000); } else { if (s_speakerState.workMode == DJI_WIDGET_SPEAKER_WORK_MODE_VOICE) { USER_LOG_WARN("Waiting opus decoder finished..."); while (s_isDecodeFinished == false) { osalHandler->TaskSleepMs(1); } djiReturnCode = DjiTest_PlayAudioData(); if (djiReturnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("Play audio data failed, error: 0x%08llX.", djiReturnCode); } } else { djiReturnCode = DjiTest_PlayTtsData(); if (djiReturnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("Play tts data failed, error: 0x%08llX.", djiReturnCode); } } djiReturnCode = osalHandler->MutexLock(s_speakerMutex); if (djiReturnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("lock mutex error: 0x%08llX.", djiReturnCode); } if (s_speakerState.playMode == DJI_WIDGET_SPEAKER_PLAY_MODE_SINGLE_PLAY) { s_speakerState.state = DJI_WIDGET_SPEAKER_STATE_IDEL; } djiReturnCode = osalHandler->MutexUnlock(s_speakerMutex); if (djiReturnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) { USER_LOG_ERROR("unlock mutex error: 0x%08llX.", djiReturnCode); } } } } } #ifndef __CC_ARM #pragma GCC diagnostic pop #endif #endif /****************** (C) COPYRIGHT DJI Innovations *****END OF FILE****/