sdk-hwV1.3/lichee/xr806/appos/project/common/cmd/cmd_audio.c

408 lines
11 KiB
C
Executable File

/*
* Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
* 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef CONFIG_FILESYSTEMS
#include "cmd_util.h"
#include "cmd_audio.h"
#include "audio/pcm/audio_pcm.h"
#include "audio/manager/audio_manager.h"
#include "driver/chip/hal_snd_card.h"
#include "fs/vfs.h"
#define CMD_AUDIO_SND_CARD AUDIO_SND_CARD_DEFAULT
#define AUDIO_RECORD_TIME (20)//s
#define AUDIO_THREAD_STACK_SIZE (2 * 1024)
#define AUDIO_DELETE_THREAD(THREAD) OS_ThreadDelete(&THREAD)
#define AUDIO_CREAT_THREAD(THREAD, TASK, ARG) \
{ \
if (OS_ThreadIsValid(&THREAD)) { \
CMD_ERR("audio task is running\n"); \
return CMD_STATUS_FAIL; \
} \
if (OS_ThreadCreate(&THREAD, \
"", \
TASK, \
(void *)ARG, \
OS_THREAD_PRIO_APP, \
AUDIO_THREAD_STACK_SIZE) != OS_OK) { \
CMD_ERR("audio task create failed\n"); \
return CMD_STATUS_FAIL; \
} \
}
static OS_Thread_t g_audio_record_thread;
static OS_Thread_t g_audio_play_thread;
static OS_Thread_t g_audio_control_thread;
static uint8_t g_audio_task_end;
static uint32_t sampleRate[] = {8000, 11025, 12000, 16000, 22050, 24000,
32000, 44100, 48000, 96000, 192000};
static int AudioConfigIsValid(int samplerate, int channels)
{
int i;
int sr_num;
//if ((channels != 1) && (channels != 2))
// return 0;
sr_num = sizeof(sampleRate) / sizeof(sampleRate[0]);
for (i = 0; i < sr_num; i++) {
if (sampleRate[i] == samplerate) {
return 1;
}
}
return 0;
}
static int AudioSetConfig(int samplerate, int channels, struct pcm_config *config)
{
if (!AudioConfigIsValid(samplerate, channels)) {
return -1;
}
config->channels = channels;
config->rate = samplerate;
config->period_size = 2048;
config->period_count = 2;
config->format = PCM_FORMAT_S16_LE;
return 0;
}
static void cap_exec(void *cmd)
{
int argc, ret = 0;
unsigned int writenum = 0;
int samplerate;
int channels;
char *argv[5];
vfs_file_t *vfs;
char *file_path;
unsigned int pcm_buf_size;
char *pcm_data;
uint32_t stopTime;
struct pcm_config config;
argc = cmd_parse_argv(cmd, argv, 5);
if (argc < 3) {
CMD_ERR("invalid audio capture cmd, argc %d\n", argc);
goto exit_thread;
}
samplerate = cmd_atoi(argv[0]);
channels = cmd_atoi(argv[1]);
file_path = argv[2];
CMD_DBG("CMD:drv audio cap (samplerate)%d (channel)%d (file)%s\n",
samplerate, channels, file_path);
if (AudioSetConfig(samplerate, channels, &config)) {
CMD_ERR("invalid audio cap param.\n");
goto exit_thread;
}
vfs_unlink(file_path);
vfs = vfs_open(file_path, VFS_WRONLY | VFS_CREAT);
if (vfs == NULL) {
CMD_ERR("open file fail\n");
goto exit_thread;
}
pcm_buf_size = (config.channels) * 2 * (config.period_size);
pcm_data = malloc(pcm_buf_size);
if (pcm_data == NULL) {
CMD_ERR("malloc buf failed\n");
vfs_close(vfs);
goto exit_thread;
}
memset(pcm_data, 0, pcm_buf_size);
if (snd_pcm_open(CMD_AUDIO_SND_CARD, PCM_IN, &config) != 0) {
CMD_ERR("sound card open err\n");
goto exit_fs;
}
stopTime = OS_TicksToSecs(OS_GetTicks()) + AUDIO_RECORD_TIME;
if (stopTime < AUDIO_RECORD_TIME) {
CMD_ERR("stopTime overflow\n");
goto exit_snd;
}
g_audio_task_end = 0;
CMD_DBG("Capture run.\n");
while (!g_audio_task_end/* && OS_TicksToSecs(OS_GetTicks()) <= stopTime */) {
ret = snd_pcm_read(CMD_AUDIO_SND_CARD, pcm_data, pcm_buf_size);
if (ret != pcm_buf_size) {
CMD_ERR("read data failed(%d), line:%d\n", ret, __LINE__);
break;
}
writenum = vfs_write(vfs, pcm_data, pcm_buf_size);
if (writenum != pcm_buf_size) {
CMD_ERR("write failed %d, %d\n", writenum, __LINE__);
break;
}
}
exit_snd:
snd_pcm_close(CMD_AUDIO_SND_CARD, PCM_IN);
exit_fs:
free(pcm_data);
vfs_close(vfs);
CMD_DBG("Capture end.\n");
exit_thread:
AUDIO_DELETE_THREAD(g_audio_record_thread);
}
static void play_exec(void *cmd)
{
int argc;
unsigned int readnum = 0;
int samplerate;
int channels;
char *argv[5];
vfs_file_t *vfs;
char *file_path;
unsigned int pcm_buf_size;
char *pcm_data;
struct pcm_config config;
argc = cmd_parse_argv(cmd, argv, 5);
if (argc < 3) {
CMD_ERR("invalid audio capture cmd, argc %d\n", argc);
goto exit_thread;
}
samplerate = cmd_atoi(argv[0]);
channels = cmd_atoi(argv[1]);
file_path = argv[2];
CMD_DBG("CMD:drv audio play (samplerate)%d (channel)%d (file)%s\n",
samplerate, channels, file_path);
if (AudioSetConfig(samplerate, channels, &config)) {
CMD_ERR("invalid audio cap param.\n");
goto exit_thread;
}
vfs = vfs_open(file_path, VFS_RDONLY);
if (vfs == NULL) {
CMD_ERR("open file fail\n");
goto exit_thread;
}
pcm_buf_size = (config.channels)*2*(config.period_size);
pcm_data = malloc(pcm_buf_size);
if (pcm_data == NULL) {
CMD_ERR("malloc buf failed\n");
vfs_close(vfs);
goto exit_thread;;
}
if (snd_pcm_open(CMD_AUDIO_SND_CARD, PCM_OUT, &config) != 0) {
CMD_ERR("sound card open err\n");
goto exit_fs;
}
CMD_DBG("Play on.\n");
g_audio_task_end = 0;
while (!g_audio_task_end) {
readnum = vfs_read(vfs, pcm_data, pcm_buf_size);
if (readnum < 0) {
CMD_ERR("read failed.\n");
break;
}
snd_pcm_write(CMD_AUDIO_SND_CARD, pcm_data, readnum);
if (readnum != pcm_buf_size) {
CMD_DBG("file end: file size = %d\n", readnum);
break;
}
}
snd_pcm_flush(CMD_AUDIO_SND_CARD);
snd_pcm_close(CMD_AUDIO_SND_CARD, PCM_OUT);
exit_fs:
vfs_close(vfs);
free(pcm_data);
CMD_DBG("Play end.\n");
exit_thread:
AUDIO_DELETE_THREAD(g_audio_play_thread);
}
static void vol_exec(void *cmd)
{
int argc;
char *argv[3];
argc = cmd_parse_argv(cmd, argv, 3);
if (argc < 2) {
CMD_ERR("invalid audio set vol cmd, argc %d\n", argc);
goto exit;
}
uint16_t dev = cmd_atoi(argv[0]);
int vol = cmd_atoi(argv[1]);
CMD_DBG("CMD:drv audio vol (level)%d\n", vol);
if (vol > VOLUME_MAX_LEVEL) {
CMD_ERR("invalid audio vol.Range(0-%d)\n", VOLUME_MAX_LEVEL);
goto exit;
}
audio_manager_handler(CMD_AUDIO_SND_CARD, AUDIO_MANAGER_SET_VOLUME_LEVEL, dev, vol);
exit:
AUDIO_DELETE_THREAD(g_audio_control_thread);
}
static void path_exec(void *cmd)
{
int argc;
char *argv[2];
argc = cmd_parse_argv(cmd, argv, 2);
if (argc < 1) {
CMD_ERR("invalid audio set path cmd, argc %d\n", argc);
goto exit;
}
int path = cmd_atoi(argv[0]);
CMD_DBG("CMD:drv audio out-path %d\n", path);
if (path > AUDIO_OUT_DEV_SPK) {
CMD_ERR("invalid audio out-path.Range(1-2)\n");
goto exit;
}
audio_manager_handler(CMD_AUDIO_SND_CARD, AUDIO_MANAGER_SET_ROUTE, path, cmd_atoi(argv[1]));
exit:
AUDIO_DELETE_THREAD(g_audio_control_thread);
}
static enum cmd_status audio_play_task(char *arg)
{
char *cmd = (char *)arg;
AUDIO_CREAT_THREAD(g_audio_play_thread, play_exec, cmd);
return CMD_STATUS_OK;
}
static enum cmd_status audio_cap_task(char *arg)
{
char *cmd = (char *)arg;
AUDIO_CREAT_THREAD(g_audio_record_thread, cap_exec, cmd);
return CMD_STATUS_OK;
}
static enum cmd_status audio_vol_task(char *arg)
{
char *cmd = (char *)arg;
AUDIO_CREAT_THREAD(g_audio_control_thread, vol_exec, cmd);
return CMD_STATUS_OK;
}
static enum cmd_status audio_path_task(char *arg)
{
char *cmd = (char *)arg;
AUDIO_CREAT_THREAD(g_audio_control_thread, path_exec, cmd);
return CMD_STATUS_OK;
}
static enum cmd_status audio_end_task(char *arg)
{
char *cmd = (char *)arg;
(void)cmd;
g_audio_task_end = 1;
CMD_DBG("audio task end\n");
return CMD_STATUS_OK;
}
/*
* brief audio Test Command
* command
* 1)cap: $ audio cap [samplerate] [channels] [file-path]
* 2)play: $ audio play [samplerate] [channels] [file-path]
* 3)vol: $ audio vol [dev] [vol]
* 4)path: $ audio path [dev] [en]
* 5)end : $ audio end
* samplerate: [8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000]
* channels: [1~2]
* vol: [0~31]
* dev: device mask
* en: [0~1]
* example
* audio cap 16000 1 record.pcm
* audio play 44100 2 music.pcm
* audio vol 12
* audio path 1 1
* audio end
*/
#if CMD_DESCRIBE
#define audio_cap_help_info \
"audio cap [samplerate] [channels] [file-path]\n" \
"\t\t\teg. audio cap 16000 1 record.pcm"
#define audio_play_help_info \
"audio play [samplerate] [channels] [file-path]\n" \
"\t\t\teg. audio play 44100 2 music.pcm"
#define audio_vol_help_info \
"audio vol [dev] [vol]\n" \
"\t\t\teg. audio vol 12"
#define audio_path_help_info \
"audio path [dev] [en]\n" \
"\t\t\teg. audio path 1 1"
#define audio_end_help_info "audio end"
#endif
static enum cmd_status cmd_audio_help_exec(char *cmd);
static const struct cmd_data g_audio_cmds[] = {
{ "cap", audio_cap_task, CMD_DESC(audio_cap_help_info) },
{ "play", audio_play_task, CMD_DESC(audio_play_help_info) },
{ "vol", audio_vol_task, CMD_DESC(audio_vol_help_info) },
{ "path", audio_path_task, CMD_DESC(audio_path_help_info) },
{ "end", audio_end_task, CMD_DESC(audio_end_help_info) },
{ "help", cmd_audio_help_exec, CMD_DESC(CMD_HELP_DESC) },
};
static enum cmd_status cmd_audio_help_exec(char *cmd)
{
return cmd_help_exec(g_audio_cmds, cmd_nitems(g_audio_cmds), 8);
}
enum cmd_status cmd_audio_exec(char *cmd)
{
return cmd_exec(cmd, g_audio_cmds, cmd_nitems(g_audio_cmds));
}
#endif