作者 ookk303

speex参数调整 t40s功率调整

... ... @@ -209,7 +209,7 @@ void *JZ_T40_SubConnectTask(void *arg)
{
HalSend_type1Send_SearchLight_SetLumenPowerLimit(UART_DEV_2, 0x00, 0x64, 0x20, 0x00);
SearchLight_SetExtrenLimit(90);
SearchLight_SetExtrenLimit(85);
}
else
{
... ... @@ -237,7 +237,7 @@ void *JZ_T40_SubConnectTask(void *arg)
{
HalSend_type1Send_SearchLight_SetLumenPowerLimit(UART_DEV_2, 0x00, 0x64, 0x20, 0x00);
SearchLight_SetExtrenLimit(95);
SearchLight_SetExtrenLimit(90);
}
else
{
... ...
... ... @@ -260,7 +260,8 @@ T_JZsdkReturnCode AudioDeal_PcmDataInput_RealTimeSteam(int In_Bitrate, short *bu
#ifdef SPEEX_STATUS_ON
//回音消除+噪声抑制
Speex_ProcessMic(buffer, buffer);
//Speex_ProcessMic(buffer, buffer);
Speex_DenoiseOnly_Process(buffer, buffer);
#endif
... ...
... ... @@ -10,9 +10,9 @@
// ========== 可配置参数 ==========
#define TAIL_MS 300 // 回声尾长(毫秒)
#define FRAME_SAMPLES 640 // 每帧样本数 640个short
#define FRAME_SAMPLES 640 // 每帧样本数(640个short)
#define SAMPLE_RATE 16000 // 采样率
#define PLAYBACK_DELAY_FRAMES 1 // 播放延迟(帧数),1 表示用上一帧作为参考
#define PLAYBACK_DELAY_FRAMES 19 // 播放延迟(帧数),软件时间0.75秒约19帧
// =================================
typedef struct JZ_SpeexInfo {
... ... @@ -26,12 +26,12 @@ typedef struct JZ_SpeexInfo {
// 环形缓冲区,存储历史输出帧(即播放过的数据)
short* HistoryBuffer;
int HistorySize; // 缓冲区总长度(样本数)
int WritePos; // 下一个写入位置
int TotalWritten; // 累计写入样本数(用于判断缓冲区是否足够)
int HistorySize;
int WritePos;
int TotalWritten;
SpeexPreprocessState* DenoiseOnlyState;
int DenoiseOnlyFlag; // 是否启用独立降噪模式
int DenoiseOnlyFlag;
int Flag;
} JZ_SpeexInfo;
... ... @@ -39,11 +39,14 @@ typedef struct JZ_SpeexInfo {
static JZ_SpeexInfo g_SpeexInfo = { 0 };
// 初始化历史缓冲区
static int InitHistoryBuffer(int size_samples) {
if (g_SpeexInfo.HistoryBuffer) {
static int InitHistoryBuffer(int size_samples)
{
if (g_SpeexInfo.HistoryBuffer)
{
free(g_SpeexInfo.HistoryBuffer);
g_SpeexInfo.HistoryBuffer = NULL;
}
g_SpeexInfo.HistoryBuffer = (short*)malloc(size_samples * sizeof(short));
if (!g_SpeexInfo.HistoryBuffer) return -1;
memset(g_SpeexInfo.HistoryBuffer, 0, size_samples * sizeof(short));
... ... @@ -54,7 +57,8 @@ static int InitHistoryBuffer(int size_samples) {
}
// 写入一帧到历史缓冲区(播放过的帧)
static void WriteHistoryFrame(short* frame) {
static void WriteHistoryFrame(short* frame)
{
int fs = g_SpeexInfo.FrameSize;
int hist_size = g_SpeexInfo.HistorySize;
int write_pos = g_SpeexInfo.WritePos;
... ... @@ -72,7 +76,8 @@ static void WriteHistoryFrame(short* frame) {
}
// 从历史缓冲区读取参考帧(对齐到当前麦克风时间)
static int ReadRefFrame(short* out_ref) {
static int ReadRefFrame(short* out_ref)
{
int fs = g_SpeexInfo.FrameSize;
int hist_size = g_SpeexInfo.HistorySize;
int write_pos = g_SpeexInfo.WritePos;
... ... @@ -122,8 +127,7 @@ T_JZsdkReturnCode Speex_Deinit()
g_SpeexInfo.Flag = JZ_FLAGCODE_OFF;
}
//降噪注销
if (g_SpeexInfo.DenoiseOnlyState)
if (g_SpeexInfo.DenoiseOnlyState)
{
speex_preprocess_state_destroy(g_SpeexInfo.DenoiseOnlyState);
g_SpeexInfo.DenoiseOnlyState = NULL;
... ... @@ -133,8 +137,6 @@ T_JZsdkReturnCode Speex_Deinit()
return JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS;
}
// 初始化
// sample_rate: 采样率(如 16000)
T_JZsdkReturnCode Speex_Init(int sample_rate)
{
if (g_SpeexInfo.Flag == JZ_FLAGCODE_ON)
... ... @@ -153,7 +155,8 @@ T_JZsdkReturnCode Speex_Init(int sample_rate)
int hist_size = g_SpeexInfo.TailLen + g_SpeexInfo.PlaybackDelaySamples + frame_samples * 2;
g_SpeexInfo.EchoState = speex_echo_state_init(frame_samples, g_SpeexInfo.TailLen);
if (!g_SpeexInfo.EchoState) {
if (!g_SpeexInfo.EchoState)
{
JZSDK_LOG_DEBUG("Speex_Init: speex_echo_state_init failed\n");
return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;
}
... ... @@ -169,7 +172,8 @@ T_JZsdkReturnCode Speex_Init(int sample_rate)
speex_preprocess_ctl(g_SpeexInfo.PreprocessState, SPEEX_PREPROCESS_SET_ECHO_STATE, g_SpeexInfo.EchoState);
if (InitHistoryBuffer(hist_size) != 0) {
if (InitHistoryBuffer(hist_size) != 0)
{
speex_echo_state_destroy(g_SpeexInfo.EchoState);
speex_preprocess_state_destroy(g_SpeexInfo.PreprocessState);
JZSDK_LOG_DEBUG("Speex_Init: history buffer allocation failed\n");
... ... @@ -181,66 +185,66 @@ T_JZsdkReturnCode Speex_Init(int sample_rate)
sample_rate, frame_samples, g_SpeexInfo.TailLen,
g_SpeexInfo.PlaybackDelaySamples, hist_size);
/***********************************
降噪配置
*************************************/
// 独立降噪初始化
g_SpeexInfo.DenoiseOnlyState = speex_preprocess_state_init(FRAME_SAMPLES, sample_rate);
if (!g_SpeexInfo.DenoiseOnlyState) {
if (!g_SpeexInfo.DenoiseOnlyState)
{
return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;
}
// 在 Speex_Init 中,创建 DenoiseOnlyState 后添加:
//语音活动检测
int vad = 0;
speex_preprocess_ctl(g_SpeexInfo.DenoiseOnlyState, SPEEX_PREPROCESS_SET_VAD, &vad);
//自动增益
int agc = 0;
speex_preprocess_ctl(g_SpeexInfo.DenoiseOnlyState, SPEEX_PREPROCESS_SET_AGC, &agc);
int denoise = 1; // 保持开启
//启用降噪
int denoise = 1;
speex_preprocess_ctl(g_SpeexInfo.DenoiseOnlyState, SPEEX_PREPROCESS_SET_DENOISE, &denoise);
// 可选:关闭降噪的自动增益补偿
int noise_suppress = 0; // 或者尝试 1,2...
int dereverb = 0; // 关闭去混响
speex_preprocess_ctl(g_SpeexInfo.DenoiseOnlyState, SPEEX_PREPROCESS_SET_DEREVERB, &dereverb);
/*
设置噪声抑制最大衰减量 越低越激进,可以为负数 -40基本没有原噪音了,但是偶尔会有一点打印机一样的噪音
-15 没什么效果
-80 也是没有原噪音,但是引入的噪音没改善
-30 没什么效果
*/
int noise_suppress = -40;
speex_preprocess_ctl(g_SpeexInfo.DenoiseOnlyState, SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, &noise_suppress);
return JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS;
}
// 独立降噪处理(char* 版本,原地处理)
// 独立降噪处理
T_JZsdkReturnCode Speex_DenoiseOnly_Process(short* mic, short* out)
{
if (!g_SpeexInfo.DenoiseOnlyState)
if (!g_SpeexInfo.DenoiseOnlyState)
{
if (out != mic) memcpy(out, mic, FRAME_SAMPLES * sizeof(short));
return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;
}
if (out != mic) memcpy(out, mic, FRAME_SAMPLES * sizeof(short));
speex_preprocess_run(g_SpeexInfo.DenoiseOnlyState, out);
return JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS;
}
// 处理麦克风数据(char* 版本)
// mic: 麦克风采集的原始 PCM 字节流(长度 = frame_samples * sizeof(short) = 640 字节)
// out: 处理后的干净 PCM 字节流(长度相同,可与 mic 共用内存)
// 注意:out 同时会被保存到历史缓冲区,作为下一帧的参考信号(播放数据)
// 回声消除处理(使用历史输出作为参考信号)
T_JZsdkReturnCode Speex_ProcessMic(short* mic, short* out)
{
if (g_SpeexInfo.Flag == JZ_FLAGCODE_OFF)
if (g_SpeexInfo.Flag == JZ_FLAGCODE_OFF)
{
return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;
}
short ref_frame[FRAME_SAMPLES]; // 用于存放参考帧
if (ReadRefFrame(ref_frame) != 0)
short ref_frame[FRAME_SAMPLES];
if (ReadRefFrame(ref_frame) != 0)
{
// 历史数据不足,直接拷贝输出
// 历史数据不足,直接拷贝输出并写入历史缓冲区
if (out != mic) memcpy(out, mic, g_SpeexInfo.FrameSize * sizeof(short));
WriteHistoryFrame(out);
return JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS;
... ... @@ -249,11 +253,11 @@ T_JZsdkReturnCode Speex_ProcessMic(short* mic, short* out)
// 执行回声消除
speex_echo_cancellation(g_SpeexInfo.EchoState, mic, ref_frame, out);
// 执行后处理(噪声抑制等)
speex_preprocess_run(g_SpeexInfo.PreprocessState, out);
// 后处理降噪
speex_preprocess_run(g_SpeexInfo.DenoiseOnlyState, out);
// 将处理后的帧写入历史缓冲区(因为它即将被播放)
WriteHistoryFrame(out);
// 将输出帧写入历史缓冲区(供后续帧作为参考)
//WriteHistoryFrame(out);
return JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS;
}
... ...
... ... @@ -308,10 +308,15 @@ static void *DecodeAudioData_task(void *arg)
DecodeFinsh:
USER_LOG_INFO("Decode Finished...");
JZSDK_LOG_INFO("Decode Finished...");
s_isDecodeFinished = true;
//录音喊话解锁
SpeakerWidgetUseLock = JZ_FLAGCODE_OFF;
//新加入
StartPlay();
#endif
}
... ... @@ -320,7 +325,9 @@ DecodeFinsh:
static T_DjiReturnCode DjiTest_PlayAudioData(void)
{
JZSDK_LOG_INFO("播放固定位置的录音DjiTest_PlayAudioData ");
USER_LOG_INFO("播放固定位置的录音DjiTest_PlayAudioData ");
JZSDK_LOG_INFO("播放固定位置的录音DjiTest_PlayAudioData ");
#ifdef MEGAPHONE_CONFIG_STATUS_ON
do
{
... ... @@ -467,7 +474,9 @@ static void SetSpeakerState(E_DjiWidgetSpeakerState speakerState)
if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) {
USER_LOG_ERROR("lock mutex error: 0x%08llX.", returnCode);
}
USER_LOG_INFO("s_speakerState.state = %x", speakerState);
JZSDK_LOG_INFO("s_speakerState.state = %x", speakerState);
s_speakerState.state = speakerState;
returnCode = osalHandler->MutexUnlock(s_speakerMutex);
... ... @@ -572,16 +581,17 @@ static T_DjiReturnCode StartPlay(void)
return DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS;
}
//检查当前是否正在播放中
if (s_speakerState.state == DJI_WIDGET_SPEAKER_STATE_PLAYING)
{
//关闭上次播放
StopPlay();
////检查当前是否正在播放中
//if (s_speakerState.state == DJI_WIDGET_SPEAKER_STATE_PLAYING)
//{
// //关闭上次播放
// StopPlay();
delayMs(100);
}
// delayMs(100);
//}
USER_LOG_INFO("Start widget speaker play");
JZSDK_LOG_INFO("Start widget speaker play");
SetSpeakerState(DJI_WIDGET_SPEAKER_STATE_PLAYING);
//播放
... ... @@ -717,6 +727,7 @@ static T_DjiReturnCode ReceiveAudioData(E_DjiWidgetTransmitDataEvent event,
{
s_isDecodeFinished = false;
USER_LOG_INFO("Create voice file.");
JZSDK_LOG_INFO("开始录音喊话");
//a、创建一个保存录音数据的文件
s_audioFile = fopen(WIDGET_SPEAKER_AUDIO_OPUS_FILE_NAME, "wb");
... ... @@ -738,6 +749,8 @@ static T_DjiReturnCode ReceiveAudioData(E_DjiWidgetTransmitDataEvent event,
//e、上锁
SpeakerWidgetUseLock = JZ_FLAGCODE_ON;
}
//2、传输中
else if (event == DJI_WIDGET_TRANSMIT_DATA_EVENT_TRANSMIT)
... ... @@ -762,6 +775,7 @@ static T_DjiReturnCode ReceiveAudioData(E_DjiWidgetTransmitDataEvent event,
else if (event == DJI_WIDGET_TRANSMIT_DATA_EVENT_FINISH)
{
USER_LOG_INFO("Close voice file.");
JZSDK_LOG_INFO("结束录音喊话");
if (s_audioFile != NULL) {
fclose(s_audioFile);
}
... ... @@ -770,6 +784,7 @@ static T_DjiReturnCode ReceiveAudioData(E_DjiWidgetTransmitDataEvent event,
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");
JZSDK_LOG_ERROR("File md5 sum check failed");
return DJI_ERROR_SYSTEM_MODULE_CODE_SYSTEM_ERROR;
}
... ... @@ -782,11 +797,12 @@ static T_DjiReturnCode ReceiveAudioData(E_DjiWidgetTransmitDataEvent event,
int task_ret = pthread_create(&V_DecodeAudioData_task,&task_attribute,DecodeAudioData_task,NULL); //TTS线程
if(task_ret != 0)
{
printf("创建DecodeAudioData_task线程失败!\n");
JZSDK_LOG_ERROR("创建DecodeAudioData_task线程失败!");
//录音喊话解锁
SpeakerWidgetUseLock = JZ_FLAGCODE_OFF;
//pthread_exit(NULL);
}
}
}
... ... @@ -817,6 +833,14 @@ static T_DjiReturnCode ReceiveAudioData(E_DjiWidgetTransmitDataEvent event,
//e、上锁
SpeakerWidgetUseLock = JZ_FLAGCODE_ON;
JZSDK_LOG_INFO("开始实时喊话");
//Temp、创建一个保存录音数据的文件
s_audioFile = fopen(WIDGET_SPEAKER_AUDIO_OPUS_FILE_NAME, "wb");
if (s_audioFile == NULL) {
USER_LOG_ERROR("Create tts file error.");
}
}
//2、传输过程
else if (event == DJI_WIDGET_TRANSMIT_DATA_EVENT_TRANSMIT)
... ... @@ -831,6 +855,15 @@ static T_DjiReturnCode ReceiveAudioData(E_DjiWidgetTransmitDataEvent event,
{
SetSpeakerState(DJI_WIDGET_SPEAKER_STATE_TRANSMITTING);
}
//Temp如果创建的保存文件不为空
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);
}
}
}
//3、传输结束
else if(event == DJI_WIDGET_TRANSMIT_DATA_EVENT_FINISH)
... ... @@ -844,6 +877,13 @@ static T_DjiReturnCode ReceiveAudioData(E_DjiWidgetTransmitDataEvent event,
//解锁
SpeakerWidgetUseLock = JZ_FLAGCODE_OFF;
JZSDK_LOG_INFO("结束实时喊话");
//Temp结束
if (s_audioFile != NULL) {
fclose(s_audioFile);
}
}
}
... ... @@ -926,7 +966,7 @@ T_JZsdkReturnCode Speaker_Set_SpeakerState_state(int value)
s_speakerState.state = value;
}
//循环模式90
//循环模式
T_JZsdkReturnCode Speaker_Set_SpeakerPlaymode(int value)
{
s_speakerState.playMode = value;
... ...