ret = hp4570_set_iindpm_mA(1000);\r
printf("hp4570_set_iindpm_mA(1000) ret:%d\n",ret);\r
\r
+#if 0\r
+ uint8_t value = 0;\r
+ hp4570_read_reg(HP4570_REG_DPM_SET, &value);\r
+ printf("HP4570_REG_DPM_SET : 0x%x\n",value);\r
+\r
+ value = 0;\r
+ hp4570_read_reg(HP4570_REG_SYS_CTRL1, &value);\r
+ printf("HP4570_REG_SYS_CTRL1 : 0x%x\n",value);\r
+\r
+ value = 0;\r
+ hp4570_read_reg(HP4570_REG_SYS_CTRL0, &value);\r
+ printf("HP4570_REG_SYS_CTRL0 : 0x%x\n",value);\r
+\r
+ value = 0;\r
+ hp4570_read_reg(HP4570_REG_IDSG_ITERM_SET, &value);\r
+ printf("HP4570_REG_IDSG_ITERM_SET : 0x%x\n",value);\r
+\r
+ value = 0;\r
+ hp4570_read_reg(HP4570_REG_BAT_VOL_SET, &value);\r
+ printf("HP4570_REG_BAT_VOL_SET : 0x%x\n",value);\r
+\r
+ value = 0;\r
+ hp4570_read_reg(HP4570_REG_SYS_CTRL3, &value);\r
+ printf("HP4570_REG_SYS_CTRL3 : 0x%x\n",value);\r
+\r
+ value = 0;\r
+ hp4570_read_reg(HP4570_REG_SYS_CTRL4, &value);\r
+ printf("HP4570_REG_SYS_CTRL4 : 0x%x\n",value);\r
+\r
+ value = 0;\r
+ hp4570_read_reg(HP4570_REG_CHIP_STAT0, &value);\r
+ printf("HP4570_REG_CHIP_STAT0 : 0x%x\n",value);\r
+\r
+ value = 0;\r
+ hp4570_read_reg(HP4570_REG_CHIP_STAT1, &value);\r
+ printf("HP4570_REG_CHIP_STAT1 : 0x%x\n",value);\r
+\r
+ value = 0;\r
+ hp4570_read_reg(HP4570_REG_CHIP_STAT2, &value);\r
+ printf("HP4570_REG_CHIP_STAT2 : 0x%x\n",value);\r
+\r
+ value = 0;\r
+ hp4570_read_reg(HP4570_REG_VIPRE, &value);\r
+ printf("HP4570_REG_VIPRE : 0x%x\n",value);\r
+\r
+ value = 0;\r
+ hp4570_read_reg(HP4570_REG_EXT_CTRL, &value);\r
+ printf("HP4570_REG_EXT_CTRL : 0x%x\n",value);\r
+\r
+ value = 0;\r
+ hp4570_read_reg(HP4570_REG_SYS_CTRL5, &value);\r
+ printf("HP4570_REG_SYS_CTRL5 : 0x%x\n",value);\r
+\r
+ value = 0;\r
+ hp4570_read_reg(HP4570_REG_SYS_CTRL6, &value);\r
+ printf("HP4570_REG_SYS_CTRL6 : 0x%x\n",value);\r
+\r
+ value = 0;\r
+ hp4570_read_reg(HP4570_REG_SYS_CTRL7, &value);\r
+ printf("HP4570_REG_SYS_CTRL7 : 0x%x\n",value);\r
+\r
+ value = 0;\r
+ hp4570_read_reg(HP4570_REG_SYS_WAKE_UP_SRC, &value);\r
+ printf("HP4570_REG_SYS_WAKE_UP_SRC : 0x%x\n",value);\r
+\r
+ value = 0;\r
+ hp4570_read_reg(HP4570_REG_INT0_ENABLE, &value);\r
+ printf("HP4570_REG_INT0_ENABLE : 0x%x\n",value);\r
+\r
+ value = 0;\r
+ hp4570_read_reg(HP4570_REG_INT0_STAT, &value);\r
+ printf("HP4570_REG_INT0_STAT : 0x%x\n",value);\r
+\r
+ value = 0;\r
+ hp4570_read_reg(HP4570_REG_VID_LSB, &value);\r
+ printf("HP4570_REG_VID_LSB : 0x%x\n",value);\r
+\r
+ value = 0;\r
+ hp4570_read_reg(HP4570_REG_VID_MSB, &value);\r
+ printf("HP4570_REG_VID_MSB : 0x%x\n",value);\r
+\r
+ value = 0;\r
+ hp4570_read_reg(HP4570_REG_PID_LSB, &value);\r
+ printf("HP4570_REG_PID_LSB : 0x%x\n",value);\r
+\r
+ value = 0;\r
+ hp4570_read_reg(HP4570_REG_PID_MSB, &value);\r
+ printf("HP4570_REG_PID_MSB : 0x%x\n",value);\r
+\r
+ value = 0;\r
+ hp4570_read_reg(HP4570_REG_CHIP_VER, &value);\r
+ printf("HP4570_REG_CHIP_VER : 0x%x\n",value);\r
+\r
+#endif\r
+\r
// if( hp4570_set_chrg_vbat_mV(4012.5) &&\r
// hp4570_set_chrg_ipre_mA(34) &&\r
// hp4570_set_chrg_ichg_mA(200) &&\r
--- /dev/null
+/**
+ * @file audio_test.cpp
+ * @brief 音频测试程序 - 播放1kHz PCM信号并实时计算录音音量
+ *
+ * 功能:
+ * 1. 启动AI语音服务(使用old_Start_AIVoice)
+ * 2. 在独立线程中循环播放1kHz PCM信号(使用AI_WriteAudioData)
+ * 3. 通过录音回调收集数据,每0.5秒计算并打印一次音量
+ * 4. 支持开始/停止测试指令(使用StopAIVoice)
+ */
+
+#include "custom_utilscpp.h"
+#include "debug_print.h"
+#include "securec.h"
+#include "media_thread_adapt.h"
+#include "cmsis_os2.h"
+#include "los_base.h"
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <unistd.h>
+#include <time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// 包含AI媒体服务
+#include "../../ai/include/ai_media.h"
+
+// 包含PCM数据和音量计算函数
+#include "pcm_1khz_period.h"
+#include "pcm_volume_calculator.h"
+
+// 测试状态
+typedef struct {
+ bool is_running; // 测试是否正在运行
+ MediaThreadIdHandle play_thread; // 播放线程句柄
+ MediaMutexHandle mutex; // 互斥锁
+
+ // 播放音量调节(0.0-2.0,1.0为原始音量)
+ float play_volume; // 播放音量增益
+ MediaMutexHandle volume_mutex; // 音量互斥锁
+
+ // 录音数据缓冲区(用于音量计算)
+ int16_t* record_buffer; // 录音数据缓冲区
+ uint32_t buffer_size; // 缓冲区大小
+ uint32_t buffer_write_pos; // 写入位置
+ MediaMutexHandle buffer_mutex; // 缓冲区互斥锁
+
+ // 音量计算相关
+ uint32_t last_volume_check_time; // 上次音量检查时间(毫秒)
+ uint32_t volume_check_interval_ms; // 音量检查间隔(500ms)
+} AudioTestContext;
+
+static AudioTestContext g_audio_test_ctx = {0};
+
+// 前向声明
+static void* AudioPlayThread(void* arg);
+static void AudioTestDataCallback(const uint8_t* data, uint32_t size, void* userData);
+static uint32_t GetCurrentTimeMs(void);
+
+/**
+ * @brief 获取当前时间(毫秒)
+ */
+static uint32_t GetCurrentTimeMs(void)
+{
+ // 使用CMSIS OS2的tick计数
+ uint32_t tick_count = osKernelGetTickCount();
+ uint32_t tick_freq = osKernelGetTickFreq();
+ return (tick_count * 1000) / tick_freq;
+}
+
+/**
+ * @brief AI语音数据回调函数 - 收集录音数据用于音量计算
+ * @param data 音频数据
+ * @param size 数据大小
+ * @param userData 用户数据
+ */
+static void AudioTestDataCallback(const uint8_t* data, uint32_t size, void* userData)
+{
+ (void)userData;
+
+ if (data == nullptr || size == 0 || !g_audio_test_ctx.is_running) {
+ return;
+ }
+
+ // 将uint8_t数据转换为int16_t PCM数据
+ uint32_t sample_count = size / 2; // 16位PCM,每样本2字节
+
+ MediaMutexLock(g_audio_test_ctx.buffer_mutex);
+
+ // 确保缓冲区足够大
+ if (g_audio_test_ctx.buffer_size < sample_count * 2) {
+ // 重新分配缓冲区(保留2倍空间)
+ if (g_audio_test_ctx.record_buffer != nullptr) {
+ free(g_audio_test_ctx.record_buffer);
+ }
+ g_audio_test_ctx.buffer_size = sample_count * 2;
+ g_audio_test_ctx.record_buffer = (int16_t*)malloc(g_audio_test_ctx.buffer_size * sizeof(int16_t));
+ if (g_audio_test_ctx.record_buffer == nullptr) {
+ PRINT("[AudioTest] Failed to allocate record buffer\r\n");
+ MediaMutexUnLock(g_audio_test_ctx.buffer_mutex);
+ return;
+ }
+ g_audio_test_ctx.buffer_write_pos = 0;
+ }
+
+ // 复制数据到缓冲区(循环缓冲区)
+ const int16_t* pcm_data = (const int16_t*)data;
+ for (uint32_t i = 0; i < sample_count; i++) {
+ g_audio_test_ctx.record_buffer[g_audio_test_ctx.buffer_write_pos] = pcm_data[i];
+ g_audio_test_ctx.buffer_write_pos = (g_audio_test_ctx.buffer_write_pos + 1) % g_audio_test_ctx.buffer_size;
+ }
+
+ // 检查是否需要计算音量(每0.5秒)
+ uint32_t current_time = GetCurrentTimeMs();
+ bool should_calculate = (current_time - g_audio_test_ctx.last_volume_check_time >= g_audio_test_ctx.volume_check_interval_ms);
+
+ if (should_calculate) {
+ g_audio_test_ctx.last_volume_check_time = current_time;
+
+ // 计算音量(使用最近0.5秒的数据,约8000样本@16kHz)
+ const uint32_t samples_for_volume = 8000; // 0.5秒数据@16kHz
+ uint32_t samples_to_use = (g_audio_test_ctx.buffer_write_pos > samples_for_volume) ?
+ samples_for_volume : g_audio_test_ctx.buffer_write_pos;
+
+ if (samples_to_use > 0) {
+ // 分配临时缓冲区用于计算(简化循环缓冲区处理)
+ int16_t* temp_buffer = (int16_t*)malloc(samples_to_use * sizeof(int16_t));
+ if (temp_buffer != nullptr) {
+ // 从循环缓冲区复制最近的数据
+ if (g_audio_test_ctx.buffer_write_pos >= samples_to_use) {
+ // 直接从buffer_write_pos往前取
+ memcpy(temp_buffer,
+ g_audio_test_ctx.record_buffer + (g_audio_test_ctx.buffer_write_pos - samples_to_use),
+ samples_to_use * sizeof(int16_t));
+ } else {
+ // 需要从缓冲区末尾开始取(循环缓冲区)
+ uint32_t part1 = g_audio_test_ctx.buffer_write_pos;
+ uint32_t part2 = samples_to_use - part1;
+ memcpy(temp_buffer,
+ g_audio_test_ctx.record_buffer + (g_audio_test_ctx.buffer_size - part2),
+ part2 * sizeof(int16_t));
+ memcpy(temp_buffer + part2,
+ g_audio_test_ctx.record_buffer,
+ part1 * sizeof(int16_t));
+ }
+
+ pcm_volume_result_t result;
+ if (pcm_calculate_volume(temp_buffer, samples_to_use, &result) == 0) {
+ PRINT("[AudioTest] Volume - RMS: %.2f%% (%.2f dB), Peak: %.2f%% (%.2f dB), Level: %s\r\n",
+ result.rms * 100.0, result.rms_db,
+ result.peak * 100.0, result.peak_db,
+ pcm_get_volume_level(result.rms_db));
+ printf("fac:mic level:%s\n",pcm_get_volume_level(result.rms_db));
+ }
+
+ free(temp_buffer);
+ }
+ }
+ }
+ MediaMutexUnLock(g_audio_test_ctx.buffer_mutex);
+}
+
+/**
+ * @brief 播放线程 - 循环播放1kHz PCM信号
+ */
+static void* AudioPlayThread(void* arg)
+{
+ (void)arg;
+
+ PRINT("[AudioTest] Play thread started\r\n");
+
+ // 16kHz采样率,16样本 = 1ms,所以每1ms发送一个周期
+ // 为了最大程度减少卡顿,采用最小的数据包和最高的发送频率
+ // 每次发送5个周期(5ms数据,160字节),usleep(40*160) = 6.4ms,非常接近实时
+ const uint32_t periods_per_send = 5; // 每次发送5个周期(5ms数据,160字节)
+
+ // 预先准备数据缓冲区(避免在循环中重复分配)
+ uint32_t buffer_size = PCM_1KHZ_DATA_BYTES * periods_per_send;
+ uint8_t* pcm_buffer = (uint8_t*)malloc(buffer_size);
+ if (pcm_buffer == nullptr) {
+ PRINT("[AudioTest] Failed to allocate play buffer\r\n");
+ return nullptr;
+ }
+
+ int16_t* pcm_samples = (int16_t*)pcm_buffer;
+
+ // 预先填充原始数据缓冲区(不应用音量)
+ int16_t* raw_pcm_samples = (int16_t*)malloc(buffer_size);
+ if (raw_pcm_samples == nullptr) {
+ PRINT("[AudioTest] Failed to allocate raw buffer\r\n");
+ free(pcm_buffer);
+ return nullptr;
+ }
+
+ for (uint32_t period = 0; period < periods_per_send; period++) {
+ for (uint32_t i = 0; i < PCM_1KHZ_DATA_SIZE; i++) {
+ raw_pcm_samples[period * PCM_1KHZ_DATA_SIZE + i] = pcm_1khz_data[i];
+ }
+ }
+
+ PRINT("[AudioTest] Pre-filled %u periods (%u bytes, %.1f ms) of PCM data\r\n",
+ periods_per_send, buffer_size, (float)periods_per_send * PCM_1KHZ_DATA_PERIOD_DURATION_MS);
+
+ // 计算发送间隔:5ms数据,但AI_WriteAudioData内部会sleep(40*size) = 6.4ms
+ // 所以实际间隔约为6.4ms,非常接近实时播放速度
+ // 采用连续发送策略,不等待,让AI_WriteAudioData内部的sleep自然控制节奏
+
+ // 获取初始音量并应用(确保pcm_buffer被正确初始化)
+ MediaMutexLock(g_audio_test_ctx.volume_mutex);
+ float initial_volume = g_audio_test_ctx.play_volume;
+ printf("initial_volume : %.3f \n",initial_volume);
+ printf("g_audio_test_ctx.play_volume : %.3f \n",g_audio_test_ctx.play_volume);
+ MediaMutexUnLock(g_audio_test_ctx.volume_mutex);
+
+ // 初始化pcm_buffer:应用音量增益
+ uint32_t sample_count = buffer_size / sizeof(int16_t);
+ for (uint32_t i = 0; i < sample_count; i++) {
+ int32_t sample = (int32_t)raw_pcm_samples[i];
+ printf("initial_volume : %.3f \n",initial_volume);
+ sample = (int32_t)(sample * initial_volume);
+ // 限制在int16_t范围内,避免削波
+ if (sample > 32767) sample = 32767;
+ if (sample < -32768) sample = -32768;
+ pcm_samples[i] = (int16_t)sample;
+ }
+
+ float last_volume = initial_volume; // 记录上次音量,避免重复计算
+
+ while (true) {
+ // 检查是否需要停止(在循环开始就检查,避免不必要的处理)
+ MediaMutexLock(g_audio_test_ctx.mutex);
+ bool should_stop = !g_audio_test_ctx.is_running;
+ MediaMutexUnLock(g_audio_test_ctx.mutex);
+
+ if (should_stop) {
+ break;
+ }
+
+ // 获取当前音量设置(只在音量改变时重新计算)
+ MediaMutexLock(g_audio_test_ctx.volume_mutex);
+ float volume = g_audio_test_ctx.play_volume;
+ MediaMutexUnLock(g_audio_test_ctx.volume_mutex);
+
+ // 如果音量改变,重新应用音量增益到数据
+ if (volume != last_volume) {
+ for (uint32_t i = 0; i < sample_count; i++) {
+ int32_t sample = (int32_t)raw_pcm_samples[i];
+ sample = (int32_t)(sample * volume);
+ // 限制在int16_t范围内,避免削波
+ if (sample > 32767) sample = 32767;
+ if (sample < -32768) sample = -32768;
+ pcm_samples[i] = (int16_t)sample;
+ }
+ last_volume = volume;
+ }
+
+ // 连续发送数据,保持缓冲区充足
+ // 每次循环发送40批,总共200ms的数据
+ // 由于AI_WriteAudioData内部有usleep(40*size),每批会等待约6.4ms
+ // 40批总共约256ms,这样可以保持缓冲区有足够数据
+ // 不额外等待,让AI_WriteAudioData内部的sleep自然控制节奏
+ for (int batch = 0; batch < 40; batch++) {
+ // 检查停止标志(每批检查一次,保持响应性)
+ MediaMutexLock(g_audio_test_ctx.mutex);
+ bool should_stop_now = !g_audio_test_ctx.is_running;
+ MediaMutexUnLock(g_audio_test_ctx.mutex);
+
+ if (should_stop_now) {
+ goto exit_loop;
+ }
+
+ // 使用AI_WriteAudioData发送到播放服务
+ // 注意:AI_WriteAudioData内部有usleep(40*size),5个周期(160字节)会sleep约6.4ms
+ // 这个sleep会自然控制播放节奏,我们不需要额外等待
+ int32_t ret = AI_WriteAudioData(pcm_buffer, buffer_size);
+ if (ret != 0) {
+ // 如果发送失败,短暂等待后继续
+ usleep(500);
+ }
+ }
+ }
+
+exit_loop:
+ // 释放缓冲区
+ free(pcm_buffer);
+ free(raw_pcm_samples);
+
+ PRINT("[AudioTest] Play thread stopped\r\n");
+ return nullptr;
+}
+
+/**
+ * @brief 启动音频测试
+ * @return 0 成功, -1 失败
+ */
+int custom_audio_test_start(void)
+{
+ PRINT("[AudioTest] Starting audio test...\r\n");
+
+ // 检查是否已经在运行
+ MediaMutexLock(g_audio_test_ctx.mutex);
+ if (g_audio_test_ctx.is_running) {
+ PRINT("[AudioTest] Test already running\r\n");
+ MediaMutexUnLock(g_audio_test_ctx.mutex);
+ return -1;
+ }
+
+ // 初始化互斥锁
+ if (g_audio_test_ctx.mutex == nullptr) {
+ g_audio_test_ctx.mutex = MediaMutexCreate(nullptr);
+ if (g_audio_test_ctx.mutex == nullptr) {
+ PRINT("[AudioTest] Failed to create mutex\r\n");
+ return -1;
+ }
+ }
+
+ if (g_audio_test_ctx.buffer_mutex == nullptr) {
+ g_audio_test_ctx.buffer_mutex = MediaMutexCreate(nullptr);
+ if (g_audio_test_ctx.buffer_mutex == nullptr) {
+ PRINT("[AudioTest] Failed to create buffer mutex\r\n");
+ MediaMutexUnLock(g_audio_test_ctx.mutex);
+ return -1;
+ }
+ }
+
+ if (g_audio_test_ctx.volume_mutex == nullptr) {
+ g_audio_test_ctx.volume_mutex = MediaMutexCreate(nullptr);
+ if (g_audio_test_ctx.volume_mutex == nullptr) {
+ PRINT("[AudioTest] Failed to create volume mutex\r\n");
+ MediaMutexUnLock(g_audio_test_ctx.mutex);
+ return -1;
+ }
+ }
+
+ // 初始化播放音量(默认1.0,即原始音量)
+ g_audio_test_ctx.play_volume = 1.0f;
+
+ // 初始化AI媒体服务
+ int32_t ret = ai_media_service_init();
+ if (ret != 0) {
+ PRINT("[AudioTest] Failed to init AI media service: %d\r\n", ret);
+ MediaMutexUnLock(g_audio_test_ctx.mutex);
+ return -1;
+ }
+
+ // 设置输入和输出音频参数(PCM格式,16kHz,单声道)
+ // PCM = 1 (根据audio_base_type.h定义)
+ AIVoiceAudioParams inputParams = {
+ .audioFormat = 1, // PCM格式
+ .channelCount = 1,
+ .sampleRate = 16000
+ };
+
+ AIVoiceAudioParams outputParams = {
+ .audioFormat = 1, // PCM格式
+ .channelCount = 1,
+ .sampleRate = 16000
+ };
+
+ // 启动AI语音服务(使用old_Start_AIVoice)
+ // 不使用OGG封装
+ ret = old_Start_AIVoice(AudioTestDataCallback, nullptr, &inputParams, &outputParams, nullptr,false);
+ if (ret != 0) {
+ PRINT("[AudioTest] Failed to start AI voice service: %d\r\n", ret);
+ MediaMutexUnLock(g_audio_test_ctx.mutex);
+ return -1;
+ }
+
+ // 初始化缓冲区
+ g_audio_test_ctx.buffer_size = 16000; // 16kHz采样率,1秒数据
+ g_audio_test_ctx.record_buffer = (int16_t*)malloc(g_audio_test_ctx.buffer_size * sizeof(int16_t));
+ if (g_audio_test_ctx.record_buffer == nullptr) {
+ PRINT("[AudioTest] Failed to allocate record buffer\r\n");
+ StopAIVoice();
+ MediaMutexUnLock(g_audio_test_ctx.mutex);
+ return -1;
+ }
+ g_audio_test_ctx.buffer_write_pos = 0;
+ g_audio_test_ctx.last_volume_check_time = GetCurrentTimeMs();
+ g_audio_test_ctx.volume_check_interval_ms = 500; // 0.5秒
+
+ // 启动播放线程
+ g_audio_test_ctx.is_running = true;
+ MediaMutexUnLock(g_audio_test_ctx.mutex);
+
+ MediaThreadattr attr = {
+ "AudioTestPlay",
+ 0x2000, // 8KB栈
+ THREAD_SCHED_INVALID,
+ 0,
+ false
+ };
+
+ g_audio_test_ctx.play_thread = MediaThreadCreate(AudioPlayThread, nullptr, &attr);
+ if (g_audio_test_ctx.play_thread == nullptr) {
+ PRINT("[AudioTest] Failed to create play thread\r\n");
+ g_audio_test_ctx.is_running = false;
+ StopAIVoice();
+ free(g_audio_test_ctx.record_buffer);
+ g_audio_test_ctx.record_buffer = nullptr;
+ return -1;
+ }
+
+ PRINT("[AudioTest] Audio test started successfully\r\n");
+ PRINT("[AudioTest] Playing 1kHz tone, monitoring volume every 0.5s...\r\n");
+
+ return 0;
+}
+
+/**
+ * @brief 停止音频测试
+ * @return 0 成功, -1 失败
+ */
+int custom_audio_test_stop(void)
+{
+ PRINT("[AudioTest] Stopping audio test...\r\n");
+
+ MediaMutexLock(g_audio_test_ctx.mutex);
+
+ if (!g_audio_test_ctx.is_running) {
+ PRINT("[AudioTest] Test not running\r\n");
+ MediaMutexUnLock(g_audio_test_ctx.mutex);
+ return 0; // 已经停止,返回成功
+ }
+
+ // 设置停止标志,让回调和播放线程知道要停止
+ g_audio_test_ctx.is_running = false;
+ MediaMutexUnLock(g_audio_test_ctx.mutex);
+
+ // 给播放线程一点时间退出(最多等待500ms)
+ MediaThreadIdHandle thread_to_join = nullptr;
+ for (int i = 0; i < 5; i++) { // 最多等待500ms
+ MediaMutexLock(g_audio_test_ctx.mutex);
+ thread_to_join = g_audio_test_ctx.play_thread;
+ bool has_thread = (thread_to_join != nullptr);
+ MediaMutexUnLock(g_audio_test_ctx.mutex);
+
+ if (!has_thread) {
+ break; // 线程已经退出
+ }
+
+ usleep(100000); // 等待100ms
+ }
+
+ // 清理播放线程句柄(如果还在)
+ if (thread_to_join != nullptr) {
+ MediaMutexLock(g_audio_test_ctx.mutex);
+ if (g_audio_test_ctx.play_thread == thread_to_join) {
+ g_audio_test_ctx.play_thread = nullptr; // 清空句柄,避免重复join
+ }
+ MediaMutexUnLock(g_audio_test_ctx.mutex);
+ // 不等待线程结束,避免阻塞
+ }
+
+ // 清理缓冲区
+ MediaMutexLock(g_audio_test_ctx.buffer_mutex);
+ if (g_audio_test_ctx.record_buffer != nullptr) {
+ free(g_audio_test_ctx.record_buffer);
+ g_audio_test_ctx.record_buffer = nullptr;
+ }
+ g_audio_test_ctx.buffer_size = 0;
+ g_audio_test_ctx.buffer_write_pos = 0;
+ MediaMutexUnLock(g_audio_test_ctx.buffer_mutex);
+
+ // 停止AI语音服务(使用StopAIVoice)
+ PRINT("[AudioTest] Calling StopAIVoice()...\r\n");
+ StopAIVoice();
+
+ PRINT("[AudioTest] Audio test stopped\r\n");
+
+ return 0;
+}
+
+/**
+ * @brief 获取测试状态
+ * @return true 正在运行, false 未运行
+ */
+bool custom_audio_test_is_running(void)
+{
+ MediaMutexLock(g_audio_test_ctx.mutex);
+ bool is_running = g_audio_test_ctx.is_running;
+ MediaMutexUnLock(g_audio_test_ctx.mutex);
+ return is_running;
+}
+
+/**
+ * @brief 设置播放音量
+ * @param volume 音量值,范围0.0-2.0,1.0为原始音量
+ * @return 0 成功, -1 失败
+ */
+int custom_audio_test_set_volume(float volume)
+{
+ if (volume < 0.0f || volume > 2.0f) {
+ PRINT("[AudioTest] Invalid volume: %.2f (range: 0.0-2.0)\r\n", volume);
+ return -1;
+ }
+
+ MediaMutexLock(g_audio_test_ctx.volume_mutex);
+ g_audio_test_ctx.play_volume = volume;
+ MediaMutexUnLock(g_audio_test_ctx.volume_mutex);
+
+ PRINT("[AudioTest] Play volume set to %.2f (%.0f%%)\r\n", volume, volume * 100.0f);
+ return 0;
+}
+
+/**
+ * @brief 获取播放音量
+ * @return 当前音量值(0.0-2.0)
+ */
+float custom_audio_test_get_volume(void)
+{
+ MediaMutexLock(g_audio_test_ctx.volume_mutex);
+ float volume = g_audio_test_ctx.play_volume;
+ MediaMutexUnLock(g_audio_test_ctx.volume_mutex);
+ return volume;
+}
+
+
+
+#ifdef __cplusplus
+}
+#endif
--- /dev/null
+/**
+ * @file pcm_volume_calculator.c
+ * @brief PCM音频数据音量计算函数实现
+ */
+
+#include "pcm_volume_calculator.h"
+#include <string.h>
+#include <stdlib.h>
+
+#define PCM_MAX_VALUE 32767.0
+#define PCM_MIN_VALUE -32768.0
+#define DB_MIN_VALUE -120.0 // 最小分贝值(接近静音)
+
+int pcm_calculate_volume(const int16_t *pcm_data, size_t sample_count, pcm_volume_result_t *result)
+{
+ if (pcm_data == NULL || sample_count == 0 || result == NULL) {
+ return -1;
+ }
+
+ // 初始化结果结构
+ memset(result, 0, sizeof(pcm_volume_result_t));
+ result->sample_count = sample_count;
+
+ // 计算RMS和峰值
+ double sum_squares = 0.0;
+ int16_t max_val = PCM_MIN_VALUE;
+ int16_t min_val = PCM_MAX_VALUE;
+
+ for (size_t i = 0; i < sample_count; i++) {
+ int16_t sample = pcm_data[i];
+
+ // 计算平方和(用于RMS)
+ double normalized = (double)sample / PCM_MAX_VALUE;
+ sum_squares += normalized * normalized;
+
+ // 查找最大值和最小值
+ if (sample > max_val) {
+ max_val = sample;
+ }
+ if (sample < min_val) {
+ min_val = sample;
+ }
+ }
+
+ // 计算RMS
+ result->rms = sqrt(sum_squares / sample_count);
+
+ // 计算峰值(取绝对值较大的)
+ double max_abs = (abs(max_val) > abs(min_val)) ? abs(max_val) : abs(min_val);
+ result->peak = max_abs / PCM_MAX_VALUE;
+
+ // 保存最大最小样本值
+ result->max_sample = max_val;
+ result->min_sample = min_val;
+
+ // 计算分贝值
+ result->rms_db = linear_to_db(result->rms, 1.0);
+ result->peak_db = linear_to_db(result->peak, 1.0);
+
+ return 0;
+}
+
+double pcm_calculate_rms(const int16_t *pcm_data, size_t sample_count)
+{
+ if (pcm_data == NULL || sample_count == 0) {
+ return -1.0;
+ }
+
+ double sum_squares = 0.0;
+
+ for (size_t i = 0; i < sample_count; i++) {
+ double normalized = (double)pcm_data[i] / PCM_MAX_VALUE;
+ sum_squares += normalized * normalized;
+ }
+
+ return sqrt(sum_squares / sample_count);
+}
+
+double pcm_calculate_peak(const int16_t *pcm_data, size_t sample_count)
+{
+ if (pcm_data == NULL || sample_count == 0) {
+ return -1.0;
+ }
+
+ int16_t max_abs = 0;
+
+ for (size_t i = 0; i < sample_count; i++) {
+ int16_t abs_val = abs(pcm_data[i]);
+ if (abs_val > max_abs) {
+ max_abs = abs_val;
+ }
+ }
+
+ return (double)max_abs / PCM_MAX_VALUE;
+}
+
+double linear_to_db(double linear_value, double reference)
+{
+ if (linear_value <= 0.0) {
+ return DB_MIN_VALUE;
+ }
+
+ if (reference <= 0.0) {
+ reference = 1.0;
+ }
+
+ double ratio = linear_value / reference;
+ double db = 20.0 * log10(ratio);
+
+ // 限制最小分贝值
+ if (db < DB_MIN_VALUE) {
+ db = DB_MIN_VALUE;
+ }
+
+ return db;
+}
+
+double db_to_linear(double db_value, double reference)
+{
+ if (reference <= 0.0) {
+ reference = 1.0;
+ }
+
+ if (db_value <= DB_MIN_VALUE) {
+ return 0.0;
+ }
+
+ return reference * pow(10.0, db_value / 20.0);
+}
+
+const char* pcm_get_volume_level(double rms_db)
+{
+ if (rms_db < -60.0) {
+ return "静音";
+ } else if (rms_db < -40.0) {
+ return "极低";
+ } else if (rms_db < -20.0) {
+ return "低";
+ } else if (rms_db < -6.0) {
+ return "中";
+ } else if (rms_db < 0.0) {
+ return "高";
+ } else {
+ return "最大";
+ }
+}
+