/* * 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