除了显示驱动,其余全部测试通过
This commit is contained in:
@@ -0,0 +1,362 @@
|
||||
#include "app_config.h"
|
||||
#include "app_log.h"
|
||||
#include "system/includes.h"
|
||||
#include "os/os_api.h"
|
||||
#include "asm/includes.h"
|
||||
#include "server/audio_server.h"
|
||||
#include "generic/printf.h"
|
||||
#include "audio_test.h"
|
||||
|
||||
#define AUDIO_TEST_BYTES_PER_SEC \
|
||||
(AUDIO_TEST_SAMPLE_RATE * AUDIO_TEST_RECORD_CHANNEL * 2)
|
||||
|
||||
#define AUDIO_TEST_RECORD_BUF_SIZE \
|
||||
(AUDIO_TEST_BYTES_PER_SEC * AUDIO_TEST_RECORD_MS / 1000)
|
||||
|
||||
#define AUDIO_TEST_FRAME_SIZE \
|
||||
(AUDIO_TEST_SAMPLE_RATE / 100 * 2 * AUDIO_TEST_RECORD_CHANNEL)
|
||||
|
||||
#define AUDIO_TEST_TASK_NAME "audio_test"
|
||||
#define AUDIO_TEST_TASK_PRIO 6
|
||||
#define AUDIO_TEST_TASK_STK 1024
|
||||
#define AUDIO_TEST_MS_TO_TICKS(ms) ((ms) / (1000 / OS_TICKS_PER_SEC))
|
||||
|
||||
struct audio_test_hdl {
|
||||
struct server *enc_server;
|
||||
struct server *dec_server;
|
||||
u8 *record_buf;
|
||||
volatile u32 record_len; /* 已录音字节数(单声道) */
|
||||
volatile u32 play_pos; /* 已回放字节数(单声道计) */
|
||||
volatile u8 running;
|
||||
volatile u8 recording;
|
||||
volatile u8 playing;
|
||||
u32 peak;
|
||||
u32 sum_abs;
|
||||
u32 samples;
|
||||
int task_pid;
|
||||
};
|
||||
|
||||
static struct audio_test_hdl audio_test_hdl;
|
||||
|
||||
/* MIC录音数据回调: 拷贝进内存缓冲并统计峰值/均值 */
|
||||
static int audio_test_rec_fwrite(void *file, void *data, u32 len)
|
||||
{
|
||||
struct audio_test_hdl *p = (struct audio_test_hdl *)file;
|
||||
u32 remain, copy_len, i, n;
|
||||
s16 *s;
|
||||
u32 a;
|
||||
|
||||
if (!p->recording || !p->record_buf) {
|
||||
return len;
|
||||
}
|
||||
|
||||
remain = AUDIO_TEST_RECORD_BUF_SIZE - p->record_len;
|
||||
if (!remain) {
|
||||
return len;
|
||||
}
|
||||
copy_len = (len > remain) ? remain : len;
|
||||
memcpy(p->record_buf + p->record_len, data, copy_len);
|
||||
p->record_len += copy_len;
|
||||
|
||||
s = (s16 *)data;
|
||||
n = copy_len / 2;
|
||||
for (i = 0; i < n; i++) {
|
||||
a = (s[i] < 0) ? (u32)(-s[i]) : (u32)s[i];
|
||||
p->sum_abs += a;
|
||||
if (a > p->peak) {
|
||||
p->peak = a;
|
||||
}
|
||||
}
|
||||
p->samples += n;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* DAC回放数据回调: 单声道->双声道 */
|
||||
static int audio_test_play_fread(void *file, void *data, u32 len)
|
||||
{
|
||||
struct audio_test_hdl *p = (struct audio_test_hdl *)file;
|
||||
u32 mono_remain, stereo_remain, frames, i;
|
||||
s16 *src, *dst;
|
||||
|
||||
if (!p->playing || !p->record_buf || p->play_pos >= p->record_len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
mono_remain = p->record_len - p->play_pos;
|
||||
stereo_remain = mono_remain * 2;
|
||||
if (len > stereo_remain) {
|
||||
len = stereo_remain;
|
||||
}
|
||||
frames = len / (2 * 2);
|
||||
if (!frames) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
src = (s16 *)(p->record_buf + p->play_pos);
|
||||
dst = (s16 *)data;
|
||||
for (i = 0; i < frames; i++) {
|
||||
s16 v = src[i] * AUDIO_TEST_PLAYBACK_GAIN;
|
||||
dst[i * 2] = v;
|
||||
dst[i * 2 + 1] = v;
|
||||
}
|
||||
p->play_pos += frames * 2;
|
||||
|
||||
return frames * 2 * 2;
|
||||
}
|
||||
|
||||
static int audio_test_play_flen(void *file)
|
||||
{
|
||||
struct audio_test_hdl *p = (struct audio_test_hdl *)file;
|
||||
return p->record_len * 2;
|
||||
}
|
||||
|
||||
static int audio_test_vfs_fclose(void *file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct audio_vfs_ops audio_test_rec_vfs = {
|
||||
.fwrite = audio_test_rec_fwrite,
|
||||
.fclose = audio_test_vfs_fclose,
|
||||
};
|
||||
|
||||
static const struct audio_vfs_ops audio_test_play_vfs = {
|
||||
.fread = audio_test_play_fread,
|
||||
.fclose = audio_test_vfs_fclose,
|
||||
.flen = audio_test_play_flen,
|
||||
};
|
||||
|
||||
static int audio_test_record_start(void)
|
||||
{
|
||||
struct audio_test_hdl *p = &audio_test_hdl;
|
||||
union audio_req req = {0};
|
||||
|
||||
if (!p->record_buf) {
|
||||
p->record_buf = malloc(AUDIO_TEST_RECORD_BUF_SIZE);
|
||||
if (!p->record_buf) {
|
||||
APP_LOG("[AUDIO_TEST] malloc record buf fail: %d\n",
|
||||
AUDIO_TEST_RECORD_BUF_SIZE);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
p->record_len = 0;
|
||||
p->peak = 0;
|
||||
p->sum_abs = 0;
|
||||
p->samples = 0;
|
||||
|
||||
p->enc_server = server_open("audio_server", "enc");
|
||||
if (!p->enc_server) {
|
||||
APP_LOG("[AUDIO_TEST] enc server open fail\n");
|
||||
return -1;
|
||||
}
|
||||
p->recording = 1;
|
||||
|
||||
req.enc.cmd = AUDIO_ENC_OPEN;
|
||||
req.enc.channel = AUDIO_TEST_RECORD_CHANNEL;
|
||||
req.enc.volume = AUDIO_TEST_ADC_GAIN;
|
||||
req.enc.output_buf = NULL;
|
||||
req.enc.output_buf_len = AUDIO_TEST_DMA_BUF_SIZE;
|
||||
req.enc.sample_rate = AUDIO_TEST_SAMPLE_RATE;
|
||||
req.enc.format = "pcm";
|
||||
req.enc.frame_size = AUDIO_TEST_FRAME_SIZE;
|
||||
req.enc.sample_source = "mic";
|
||||
req.enc.channel_bit_map = BIT(AUDIO_TEST_ADC_CHANNEL_L);
|
||||
req.enc.vfs_ops = &audio_test_rec_vfs;
|
||||
req.enc.file = (FILE *)p;
|
||||
req.enc.msec = 0;
|
||||
|
||||
if (server_request(p->enc_server, AUDIO_REQ_ENC, &req)) {
|
||||
APP_LOG("[AUDIO_TEST] enc open fail\n");
|
||||
goto __err;
|
||||
}
|
||||
req.enc.cmd = AUDIO_ENC_START;
|
||||
if (server_request(p->enc_server, AUDIO_REQ_ENC, &req)) {
|
||||
APP_LOG("[AUDIO_TEST] enc start fail\n");
|
||||
goto __err;
|
||||
}
|
||||
return 0;
|
||||
|
||||
__err:
|
||||
p->recording = 0;
|
||||
server_close(p->enc_server);
|
||||
p->enc_server = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void audio_test_record_stop(void)
|
||||
{
|
||||
struct audio_test_hdl *p = &audio_test_hdl;
|
||||
union audio_req req = {0};
|
||||
|
||||
if (p->enc_server) {
|
||||
req.enc.cmd = AUDIO_ENC_STOP;
|
||||
server_request(p->enc_server, AUDIO_REQ_ENC, &req);
|
||||
server_close(p->enc_server);
|
||||
p->enc_server = NULL;
|
||||
}
|
||||
p->recording = 0;
|
||||
}
|
||||
|
||||
static int audio_test_play_start(void)
|
||||
{
|
||||
struct audio_test_hdl *p = &audio_test_hdl;
|
||||
union audio_req req = {0};
|
||||
|
||||
p->play_pos = 0;
|
||||
p->dec_server = server_open("audio_server", "dec");
|
||||
if (!p->dec_server) {
|
||||
APP_LOG("[AUDIO_TEST] dec server open fail\n");
|
||||
return -1;
|
||||
}
|
||||
p->playing = 1;
|
||||
|
||||
req.dec.cmd = AUDIO_DEC_OPEN;
|
||||
req.dec.channel = AUDIO_TEST_PLAYBACK_CHANNEL;
|
||||
req.dec.volume = AUDIO_TEST_DAC_VOLUME;
|
||||
req.dec.output_buf = NULL;
|
||||
req.dec.output_buf_len = AUDIO_TEST_DMA_BUF_SIZE;
|
||||
req.dec.sample_rate = AUDIO_TEST_SAMPLE_RATE;
|
||||
req.dec.dec_type = "pcm";
|
||||
req.dec.sample_source = "dac";
|
||||
req.dec.vfs_ops = &audio_test_play_vfs;
|
||||
req.dec.file = (FILE *)p;
|
||||
req.dec.priority = 1;
|
||||
|
||||
if (server_request(p->dec_server, AUDIO_REQ_DEC, &req)) {
|
||||
APP_LOG("[AUDIO_TEST] dec open fail\n");
|
||||
goto __err;
|
||||
}
|
||||
req.dec.cmd = AUDIO_DEC_START;
|
||||
if (server_request(p->dec_server, AUDIO_REQ_DEC, &req)) {
|
||||
APP_LOG("[AUDIO_TEST] dec start fail\n");
|
||||
goto __err;
|
||||
}
|
||||
return 0;
|
||||
|
||||
__err:
|
||||
p->playing = 0;
|
||||
server_close(p->dec_server);
|
||||
p->dec_server = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void audio_test_play_stop(void)
|
||||
{
|
||||
struct audio_test_hdl *p = &audio_test_hdl;
|
||||
union audio_req req = {0};
|
||||
|
||||
if (p->dec_server) {
|
||||
req.dec.cmd = AUDIO_DEC_STOP;
|
||||
server_request(p->dec_server, AUDIO_REQ_DEC, &req);
|
||||
server_close(p->dec_server);
|
||||
p->dec_server = NULL;
|
||||
}
|
||||
p->playing = 0;
|
||||
}
|
||||
|
||||
/* 显式驱动功放SD脚 */
|
||||
static void audio_test_amp_set(u8 enable)
|
||||
{
|
||||
u8 level = enable ? AUDIO_TEST_AMP_ENABLE_LEVEL : !AUDIO_TEST_AMP_ENABLE_LEVEL;
|
||||
gpio_direction_output(AUDIO_TEST_AMP_PORT, level);
|
||||
APP_LOG("[AUDIO_TEST] amp SD=PA0 -> %s\n", level ? "HIGH" : "LOW");
|
||||
}
|
||||
|
||||
static void audio_test_task(void *priv)
|
||||
{
|
||||
struct audio_test_hdl *p = (struct audio_test_hdl *)priv;
|
||||
u32 timeout;
|
||||
|
||||
APP_LOG("[AUDIO_TEST] ===== audio test start =====\n");
|
||||
APP_LOG("[AUDIO_TEST] sample=%dHz record=%dms adc_gain=%d dac_vol=%d ch=%d\n",
|
||||
AUDIO_TEST_SAMPLE_RATE, AUDIO_TEST_RECORD_MS,
|
||||
AUDIO_TEST_ADC_GAIN, AUDIO_TEST_DAC_VOLUME,
|
||||
AUDIO_TEST_ADC_CHANNEL_L);
|
||||
|
||||
audio_test_amp_set(0); /* 测试开始先静音功放, 防止上电杂音 */
|
||||
|
||||
/* 1. MIC 录音 */
|
||||
if (audio_test_record_start()) {
|
||||
goto __exit;
|
||||
}
|
||||
APP_LOG("[AUDIO_TEST] recording, please speak to MIC...\n");
|
||||
timeout = AUDIO_TEST_MS_TO_TICKS(AUDIO_TEST_RECORD_MS);
|
||||
while (timeout--) {
|
||||
os_time_dly(1);
|
||||
if (p->record_len >= AUDIO_TEST_RECORD_BUF_SIZE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
audio_test_record_stop();
|
||||
|
||||
APP_LOG("[AUDIO_TEST] record done: %d bytes, peak=%d, avg=%d\n",
|
||||
p->record_len, p->peak,
|
||||
p->samples ? (u32)(p->sum_abs / p->samples) : 0);
|
||||
if (p->peak >= AUDIO_TEST_PEAK_PASS_THRESHOLD) {
|
||||
APP_LOG("[AUDIO_TEST] MIC result: PASS (peak=%d)\n", p->peak);
|
||||
} else {
|
||||
APP_LOG("[AUDIO_TEST] MIC result: WEAK/FAIL (peak=%d < %d), check mic circuit\n",
|
||||
p->peak, AUDIO_TEST_PEAK_PASS_THRESHOLD);
|
||||
}
|
||||
|
||||
/* 2. 回放录音到功放 */
|
||||
if (p->record_len < AUDIO_TEST_FRAME_SIZE * 2) {
|
||||
APP_LOG("[AUDIO_TEST] record too short, skip playback\n");
|
||||
goto __exit;
|
||||
}
|
||||
if (audio_test_play_start()) {
|
||||
goto __exit;
|
||||
}
|
||||
audio_test_amp_set(1); /* 使能功放 */
|
||||
APP_LOG("[AUDIO_TEST] playing, listen speaker...\n");
|
||||
timeout = AUDIO_TEST_MS_TO_TICKS(AUDIO_TEST_RECORD_MS) + AUDIO_TEST_MS_TO_TICKS(2000);
|
||||
while (timeout--) {
|
||||
os_time_dly(1);
|
||||
if (p->play_pos >= p->record_len) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
audio_test_play_stop();
|
||||
audio_test_amp_set(0); /* 回放结束静音功放 */
|
||||
APP_LOG("[AUDIO_TEST] playback done: %d bytes\n", p->play_pos);
|
||||
APP_LOG("[AUDIO_TEST] ===== audio test finish =====\n");
|
||||
|
||||
__exit:
|
||||
p->running = 0;
|
||||
p->task_pid = 0;
|
||||
}
|
||||
|
||||
int audio_test_start(void)
|
||||
{
|
||||
struct audio_test_hdl *p = &audio_test_hdl;
|
||||
|
||||
if (p->running || p->task_pid) {
|
||||
APP_LOG("[AUDIO_TEST] already running\n");
|
||||
return -1;
|
||||
}
|
||||
p->running = 1;
|
||||
if (thread_fork(AUDIO_TEST_TASK_NAME, AUDIO_TEST_TASK_PRIO,
|
||||
AUDIO_TEST_TASK_STK, 0, &p->task_pid,
|
||||
audio_test_task, p) != OS_NO_ERR) {
|
||||
p->running = 0;
|
||||
p->task_pid = 0;
|
||||
APP_LOG("[AUDIO_TEST] task fork fail\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int audio_test_stop(void)
|
||||
{
|
||||
struct audio_test_hdl *p = &audio_test_hdl;
|
||||
|
||||
audio_test_play_stop();
|
||||
audio_test_record_stop();
|
||||
if (p->task_pid) {
|
||||
thread_kill(&p->task_pid, KILL_WAIT);
|
||||
p->task_pid = 0;
|
||||
}
|
||||
p->running = 0;
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user