759 lines
27 KiB
C
759 lines
27 KiB
C
/**
|
||
********************************************************************
|
||
* @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 <stdlib.h>
|
||
#include <errno.h>
|
||
#include <string.h>
|
||
#include <stdio.h>
|
||
#include "utils/util_misc.h"
|
||
#include "utils/util_md5.h"
|
||
#include <dji_aircraft_info.h>
|
||
|
||
#ifdef OPUS_INSTALLED
|
||
|
||
#include <opus/opus.h>
|
||
|
||
#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_8KBPS (40)
|
||
#define WIDGET_SPEAKER_AUDIO_OPUS_DECODE_BITRATE_8KBPS (8000)
|
||
|
||
/* 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;
|
||
static uint16_t s_decodeBitrate = 0;
|
||
|
||
/* 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 SetPlayMode(E_DjiWidgetSpeakerPlayMode playMode);
|
||
static T_DjiReturnCode StartPlay(void);
|
||
static T_DjiReturnCode StopPlay(void);
|
||
static T_DjiReturnCode SetVolume(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.StartPlay = StartPlay;
|
||
s_speakerHandler.StopPlay = StopPlay;
|
||
s_speakerHandler.SetPlayMode = SetPlayMode;
|
||
s_speakerHandler.SetVolume = SetVolume;
|
||
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;
|
||
}
|
||
|
||
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, s_decodeBitrate / WIDGET_SPEAKER_AUDIO_OPUS_DECODE_BITRATE_8KBPS *
|
||
WIDGET_SPEAKER_AUDIO_OPUS_DECODE_FRAME_SIZE_8KBPS, 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];
|
||
T_DjiAircraftInfoBaseInfo aircraftInfoBaseInfo;
|
||
T_DjiReturnCode returnCode;
|
||
|
||
returnCode = DjiAircraftInfo_GetBaseInfo(&aircraftInfoBaseInfo);
|
||
if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) {
|
||
USER_LOG_ERROR("get aircraft base info error");
|
||
return DJI_ERROR_SYSTEM_MODULE_CODE_SYSTEM_ERROR;
|
||
}
|
||
|
||
if (aircraftInfoBaseInfo.aircraftType == DJI_AIRCRAFT_TYPE_M3E ||
|
||
aircraftInfoBaseInfo.aircraftType == DJI_AIRCRAFT_TYPE_M3T) {
|
||
return DjiTest_PlayAudioData();
|
||
} else {
|
||
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);
|
||
|
||
#if 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 -o %s", data,
|
||
WIDGET_SPEAKER_TTS_OUTPUT_FILE_NAME);
|
||
#else
|
||
USER_LOG_WARN(
|
||
"Ekho is not installed, please visit https://www.eguidedog.net/ekho.php to install it or use other TTS tools to convert audio");
|
||
#endif
|
||
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);
|
||
|
||
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 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 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;
|
||
s_speakerState.volume = volume;
|
||
|
||
USER_LOG_INFO("Set widget speaker volume: %d", volume);
|
||
|
||
#ifdef PLATFORM_ARCH_x86_64
|
||
snprintf(cmdStr, sizeof(cmdStr), "pactl list | grep %s -q", WIDGET_SPEAKER_USB_AUDIO_DEVICE_NAME);
|
||
ret = system(cmdStr);
|
||
if (ret == DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) {
|
||
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);
|
||
}
|
||
} else {
|
||
USER_LOG_WARN("No audio device found, please add audio device and init speaker volume here!!!");
|
||
}
|
||
#else
|
||
USER_LOG_WARN("No audio device found, please add audio device and init speaker volume here!!!");
|
||
#endif
|
||
|
||
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;
|
||
T_DjiWidgetTransDataContent transDataContent = {0};
|
||
|
||
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);
|
||
}
|
||
|
||
memcpy(&transDataContent, buf, size);
|
||
s_decodeBitrate = transDataContent.transDataStartContent.fileDecodeBitrate;
|
||
USER_LOG_INFO("Create voice file: %s, decoder bitrate: %d.", transDataContent.transDataStartContent.fileName,
|
||
transDataContent.transDataStartContent.fileDecodeBitrate);
|
||
} 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_DEBUG("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_DEBUG("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****/
|