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

522 lines
16 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.
*/
#include "fft/fft.h"
#include "cmd_util.h"
#include "cmd_codec.h"
#include "cmd_codec_dat.h"
#include "audio/pcm/audio_pcm.h"
#include "audio/manager/audio_manager.h"
#define CMD_CODEC_PLAY_SND_CARD SND_CARD_0
#define CMD_CODEC_REC_SND_CARD SND_CARD_1
#define FFT_DEBUG_EN 0
#define RECORD_PCM_BUF_SIZE_48K (768) //max: 48*2*4 * 2 = 768
#define RECORD_PCM_BUF_SIZE_44K (176400/2) //max: 11025*2*4 * 2 = 176400
struct Cmd_Codec_Priv {
u32 play_sample_rate;
u8 play_channels;
u8 play_sample_res;
u32 rec_sample_rate;
u8 rec_channels;
u8 rec_sample_res;
} cmd_codec_priv ;
FFT_RESULT Cooley_Tukey_FFT(__s32 *data, __u32 fs);
/* sample_rate, channels, sample_resolution */
const unsigned char *play_dat[9][2][2] = {
{ {play_8k16bit1ch, play_8k32bit1ch}, {play_8k16bit2ch, play_8k32bit2ch} },
{ {play_11k16bit1ch, play_11k32bit1ch}, {play_11k16bit2ch, play_11k32bit2ch} },
{ {play_12k16bit1ch, play_12k32bit1ch}, {play_12k16bit2ch, play_12k32bit2ch} },
{ {play_16k16bit1ch, play_16k32bit1ch}, {play_16k16bit2ch, play_16k32bit2ch} },
{ {play_22k16bit1ch, play_22k32bit1ch}, {play_22k16bit2ch, play_22k32bit2ch} },
{ {play_24k16bit1ch, play_24k32bit1ch}, {play_24k16bit2ch, play_24k32bit2ch} },
{ {play_32k16bit1ch, play_32k32bit1ch}, {play_32k16bit2ch, play_32k32bit2ch} },
{ {play_44k16bit1ch, play_44k32bit1ch}, {play_44k16bit2ch, play_44k32bit2ch} },
{ {play_48k16bit1ch, play_48k32bit1ch}, {play_48k16bit2ch, play_48k32bit2ch} },
};
/* sample_rate, channels, sample_resolution */
const uint16_t play_dat_size[9][2][2] = {
{ {sizeof(play_8k16bit1ch), sizeof(play_8k32bit1ch)}, {sizeof(play_8k16bit2ch), sizeof(play_8k32bit2ch)} },
{ {sizeof(play_11k16bit1ch), sizeof(play_11k32bit1ch)}, {sizeof(play_11k16bit2ch), sizeof(play_11k32bit2ch)} },
{ {sizeof(play_12k16bit1ch), sizeof(play_12k32bit1ch)}, {sizeof(play_12k16bit2ch), sizeof(play_12k32bit2ch)} },
{ {sizeof(play_16k16bit1ch), sizeof(play_16k32bit1ch)}, {sizeof(play_16k16bit2ch), sizeof(play_16k32bit2ch)} },
{ {sizeof(play_22k16bit1ch), sizeof(play_22k32bit1ch)}, {sizeof(play_22k16bit2ch), sizeof(play_22k32bit2ch)} },
{ {sizeof(play_24k16bit1ch), sizeof(play_24k32bit1ch)}, {sizeof(play_24k16bit2ch), sizeof(play_24k32bit2ch)} },
{ {sizeof(play_32k16bit1ch), sizeof(play_32k32bit1ch)}, {sizeof(play_32k16bit2ch), sizeof(play_32k32bit2ch)} },
{ {sizeof(play_44k16bit1ch), sizeof(play_44k32bit1ch)}, {sizeof(play_44k16bit2ch), sizeof(play_44k32bit2ch)} },
{ {sizeof(play_48k16bit1ch), sizeof(play_48k32bit1ch)}, {sizeof(play_48k16bit2ch), sizeof(play_48k32bit2ch)} },
};
static uint8_t sample_rate_to_num(uint16_t sample_rate)
{
switch (sample_rate) {
case 8000: return 0;
case 11025: return 1;
case 12000: return 2;
case 16000: return 3;
case 22050: return 4;
case 24000: return 5;
case 32000: return 6;
case 44100: return 7;
case 48000: return 8;
default:
CMD_ERR("invalid sample rate %u\n", sample_rate);
return 0;
}
}
static enum cmd_status cmd_codec_open_exec(char *cmd)
{
int cnt, snd_card_num;
uint32_t direction, channels, resolution, sampleRate, cmd_param[3];
cnt = cmd_sscanf(cmd, "d=%u c=%u r=%u s=%u", &direction, &channels,
&resolution, &sampleRate);
if (cnt != 4) {
CMD_ERR("cmd_sscanf return: cnt = %d\n", cnt);
return CMD_STATUS_INVALID_ARG;
}
if (direction != 0 && direction != 1) {
CMD_ERR("invalid direction %u\n", direction);
return CMD_STATUS_INVALID_ARG;
}
if (channels != 1 && channels != 2) {
CMD_ERR("invalid channels %u\n", channels);
return CMD_STATUS_INVALID_ARG;
}
if (resolution != 16 && resolution != 24) {
CMD_ERR("invalid resolution %u\n", resolution);
return CMD_STATUS_INVALID_ARG;
}
switch (sampleRate) {
case 8000:
case 16000:
case 32000:
case 12000:
case 24000:
case 48000:
case 11025:
case 22050:
case 44100:
break;
default:
CMD_ERR("invalid sample rate %u\n", sampleRate);
return CMD_STATUS_INVALID_ARG;
}
struct pcm_config pcm_cfg;
pcm_cfg.rate = sampleRate;
pcm_cfg.channels = channels;
pcm_cfg.format = (resolution == 16) ? PCM_FORMAT_S16_LE : PCM_FORMAT_S24_LE;
pcm_cfg.period_count = 1;
if (direction == PCM_IN) {
snd_card_num = CMD_CODEC_REC_SND_CARD;
pcm_cfg.period_size =
(sampleRate % 1000 ? RECORD_PCM_BUF_SIZE_44K : RECORD_PCM_BUF_SIZE_48K)
/ (pcm_format_to_bits(pcm_cfg.format) / 8 * channels)
/ pcm_cfg.period_count;
audio_manager_handler(snd_card_num, AUDIO_MANAGER_SET_VOLUME_GAIN, AUDIO_IN_DEV_AMIC, VOLUME_GAIN_0dB);
audio_manager_handler(snd_card_num, AUDIO_MANAGER_SET_ROUTE, AUDIO_IN_DEV_AMIC, AUDIO_DEV_EN);
if (snd_card_num == SND_CARD_2) {
audio_manager_handler(snd_card_num, AUDIO_MANAGER_SET_VOLUME_GAIN, AUDIO_IN_DEV_LINEIN, VOLUME_GAIN_0dB);
audio_manager_handler(snd_card_num, AUDIO_MANAGER_SET_ROUTE, AUDIO_IN_DEV_LINEIN, AUDIO_DEV_EN);
}
cmd_codec_priv.rec_sample_rate = sampleRate;
cmd_codec_priv.rec_channels = channels;
cmd_codec_priv.rec_sample_res = resolution;
cmd_param[0] = 256 << 16 | 256;
audio_maneger_ioctl(snd_card_num, PLATFORM_IOCTL_SW_CONFIG,
&cmd_param[0], 1);
if (snd_card_num == SND_CARD_2) {
cmd_param[0] = 0 << 24 | 0x0 << 16 | 0x20 << 8 | 0x1;
cmd_param[1] = (channels + 1) / 2 * 32;
cmd_param[2] = 24576000;
audio_maneger_ioctl(snd_card_num, PLATFORM_IOCTL_HW_CONFIG,
cmd_param, 3);
}
} else {
snd_card_num = CMD_CODEC_PLAY_SND_CARD;
pcm_cfg.period_size =
play_dat_size[sample_rate_to_num(sampleRate)][channels-1][resolution/8-2]
/ (pcm_format_to_bits(pcm_cfg.format) / 8 * channels)
/ pcm_cfg.period_count;
//audio_manager_handler(snd_card_num, AUDIO_MANAGER_SET_VOLUME_GAIN, AUDIO_OUT_DEV_SPK, VOLUME_GAIN_0dB);
audio_manager_handler(snd_card_num, AUDIO_MANAGER_SET_ROUTE, AUDIO_OUT_DEV_SPK, AUDIO_DEV_EN);
cmd_codec_priv.play_sample_rate = sampleRate;
cmd_codec_priv.play_channels = channels;
cmd_codec_priv.play_sample_res = resolution;
if (snd_card_num == SND_CARD_0) {
cmd_param[0] = 256;
audio_maneger_ioctl(snd_card_num, CODEC_IOCTL_SW_CONFIG, &cmd_param[0], 1);
} else if (snd_card_num == SND_CARD_2) {
cmd_param[0] = 256 << 16 | 256;
audio_maneger_ioctl(snd_card_num, PLATFORM_IOCTL_SW_CONFIG, &cmd_param[0], 1);
cmd_param[0] = 0 << 24 | 0x0 << 16 | 0x20 << 8 | 0x1;
cmd_param[1] = (channels + 1) / 2 * 32;
cmd_param[2] = 24576000;
audio_maneger_ioctl(snd_card_num, PLATFORM_IOCTL_HW_CONFIG, cmd_param, 3);
}
}
if (snd_pcm_open(snd_card_num, (Audio_Stream_Dir)direction, &pcm_cfg)) {
CMD_ERR("snd pcm open Fail..\n");
return CMD_STATUS_FAIL;
}
return CMD_STATUS_OK;
}
static enum cmd_status cmd_codec_close_exec(char *cmd)
{
int cnt, snd_card_num;
uint32_t direction;
cnt = cmd_sscanf(cmd, "d=%u", &direction);
if (cnt != 1) {
CMD_ERR("cmd_sscanf return: cnt = %d\n", cnt);
return CMD_STATUS_INVALID_ARG;
}
if (direction != 0 && direction != 1) {
CMD_ERR("invalid direction %u\n", direction);
return CMD_STATUS_INVALID_ARG;
}
if (direction == PCM_IN) {
snd_card_num = CMD_CODEC_REC_SND_CARD;
audio_manager_handler(snd_card_num, AUDIO_MANAGER_SET_ROUTE,
AUDIO_IN_DEV_AMIC, AUDIO_DEV_DIS);
} else {
snd_card_num = CMD_CODEC_PLAY_SND_CARD;
audio_manager_handler(snd_card_num, AUDIO_MANAGER_SET_ROUTE,
AUDIO_OUT_DEV_SPK, AUDIO_DEV_DIS);
}
if (snd_pcm_close(snd_card_num, (Audio_Stream_Dir)direction)) {
CMD_ERR("Snd pcm close Fail..\n");
return CMD_STATUS_FAIL;
}
return CMD_STATUS_OK;
}
static enum cmd_status cmd_codec_pcm_write_exec(char *cmd)
{
uint8_t *buf;
int32_t size;
uint32_t len = play_dat_size
[sample_rate_to_num(cmd_codec_priv.play_sample_rate)]
[cmd_codec_priv.play_channels-1]
[cmd_codec_priv.play_sample_res/8-2];
buf = (uint8_t *)cmd_malloc(len);
if (buf == NULL) {
CMD_ERR("cmd_malloc return NULL.\n");
return CMD_STATUS_FAIL;
}
cmd_write_respond(CMD_STATUS_OK, "OK");
cmd_memcpy(buf,
play_dat[sample_rate_to_num(cmd_codec_priv.play_sample_rate)]
[cmd_codec_priv.play_channels-1][cmd_codec_priv.play_sample_res/8-2],
len);
size = snd_pcm_write(CMD_CODEC_PLAY_SND_CARD, buf, len);
if (size != len) {
CMD_ERR("len = %u, but snd pcm write size = %d\n", len, size);
cmd_free(buf);
cmd_write_respond(CMD_STATUS_FAIL, "FAIL");
return CMD_STATUS_ACKED;
}
cmd_free(buf);
cmd_write_respond(CMD_STATUS_OK, "OK");
return CMD_STATUS_ACKED;
}
static enum cmd_status cmd_codec_pcm_read_exec(char *cmd)
{
int i;
uint8_t *buf;
int32_t size;
uint32_t len = (cmd_codec_priv.rec_sample_rate % 1000)
? RECORD_PCM_BUF_SIZE_44K : RECORD_PCM_BUF_SIZE_48K;
buf = (uint8_t *)cmd_malloc(len);
if (buf == NULL) {
CMD_ERR("cmd malloc return NULL.\n");
return CMD_STATUS_FAIL;
}
cmd_memset(buf, 0, len);
size = snd_pcm_read(CMD_CODEC_REC_SND_CARD, buf, len);
if (size != len) {
CMD_ERR("len = %u, but snd pcm read size = %d\n", len, size);
cmd_free(buf);
return CMD_STATUS_FAIL;
}
cmd_write_respond(CMD_STATUS_OK, "OK");
/********************************** FFT Analyse **********************************/
#define FFT_POINTS 1024
FFT_RESULT fft_result;
uint16_t *buf_u16 = (uint16_t *)buf;
uint32_t *buf_u32 = (uint32_t *)buf;
uint32_t frame_cnt = len
/ (cmd_codec_priv.rec_sample_res == 16 ? 2 : 4)
/ cmd_codec_priv.rec_channels;
s32 *fft_dat = (s32 *)cmd_malloc(FFT_POINTS * 4);
#if FFT_DEBUG_EN
printf("read original data:\n");
for (i = 0; i < len / 4; i++)
printf("0x%08x ", buf_u32[i]);
printf("\n");
#endif
//cmd_memcpy(buf, buf+len/2, len/2);
#if FFT_DEBUG_EN
printf("\n");
for (i = 0; i < len; i++) {
printf("%02x ", buf[i]);
}
printf("\n");
#endif
if (fft_dat == NULL) {
CMD_ERR("\nMalloc FFT DAT buffer Fail\n\n");
} else {
/* AMIC FFT analyse */
cmd_memset(fft_dat, 0, FFT_POINTS * 4);
//printf("AMIC FFT buf:\n");
if (cmd_codec_priv.rec_sample_res == 16) {
if (cmd_codec_priv.rec_channels == 1) {
for (i = 0; i < FFT_POINTS; i++) {
fft_dat[i] = buf_u16[i % frame_cnt];
//printf("0x%08x\n",fft_dat[i]);
}
} else if (cmd_codec_priv.rec_channels == 2) {
for (i = 0; i < FFT_POINTS; i++) {
fft_dat[i] = buf_u16[2 * (i % frame_cnt)];
//printf("0x%08x\n",fft_dat[i]);
}
}
} else if (cmd_codec_priv.rec_sample_res == 24) {
if (cmd_codec_priv.rec_channels == 1) {
for (i = 0; i < FFT_POINTS; i++) {
fft_dat[i] = buf_u32[i % frame_cnt] >> 16;
//printf("0x%08x\n",fft_dat[i]);
}
} else if (cmd_codec_priv.rec_channels == 2) {
for (i = 0; i < FFT_POINTS; i++) {
fft_dat[i] = buf_u32[2 * (i % frame_cnt)] >> 16;
//printf("0x%08x\n",fft_dat[i]);
}
}
}
fft_result = Cooley_Tukey_FFT(fft_dat, cmd_codec_priv.rec_sample_rate);
printf("\nAMIC FFT Frequency: %f KHz, SNR: %f dB\n\n",
fft_result.sig_freq / 1000,
fft_result.sig_power - fft_result.noise_power);
/* LINEIN FFT analyse */
if (cmd_codec_priv.rec_channels == 2) {
cmd_memset(fft_dat, 0, FFT_POINTS * 4);
//printf("LINEIN FFT buf:\n");
if (cmd_codec_priv.rec_sample_res == 16) {
for (i = 0; i < FFT_POINTS; i++) {
fft_dat[i] = buf_u16[2 * (i % frame_cnt) + 1];
//printf("0x%08x\n",fft_dat[i]);
}
} else if (cmd_codec_priv.rec_sample_res == 24) {
for (i = 0; i < FFT_POINTS; i++) {
fft_dat[i] = buf_u32[2 * (i % frame_cnt) + 1] >> 16;
//printf("0x%08x\n",fft_dat[i]);
}
}
fft_result = Cooley_Tukey_FFT(fft_dat, cmd_codec_priv.rec_sample_rate);
printf("\nLINEIN FFT Frequency: %f KHz, SNR: %f dB\n\n",
fft_result.sig_freq / 1000,
fft_result.sig_power - fft_result.noise_power);
}
cmd_free(fft_dat);
}
/*********************************************************************************/
cmd_free(buf);
return CMD_STATUS_ACKED;
}
static enum cmd_status cmd_codec_set_route_exec(char *cmd)
{
int cnt;
uint32_t route, enable;
HAL_Status hal_status;
Audio_Device audio_device;
cnt = cmd_sscanf(cmd, "r=%u e=%u", &route, &enable);
if (cnt != 2) {
CMD_ERR("cmd_sscanf return: cnt = %d\n", cnt);
return CMD_STATUS_INVALID_ARG;
}
switch (route) {
case 0:
audio_device = AUDIO_IN_DEV_AMIC;
break;
case 1:
audio_device = AUDIO_IN_DEV_LINEIN;
break;
case 2:
audio_device = AUDIO_IN_DEV_DMIC;
break;
case 3:
audio_device = AUDIO_OUT_DEV_SPK;
break;
default:
CMD_ERR("invalid route %u\n", route);
return CMD_STATUS_INVALID_ARG;
}
if (enable != 0 && enable != 1) {
CMD_ERR("invalid enable %u\n", enable);
return CMD_STATUS_INVALID_ARG;
}
hal_status = audio_manager_handler(CMD_CODEC_PLAY_SND_CARD, AUDIO_MANAGER_SET_ROUTE,
audio_device, (Audio_Dev_State)enable);
if (hal_status == HAL_OK) {
return CMD_STATUS_OK;
} else {
CMD_ERR("Codec set route Fail, return: hal_status = %d\n", hal_status);
return CMD_STATUS_FAIL;
}
}
static enum cmd_status cmd_codec_set_gain_exec(char *cmd)
{
int cnt;
uint32_t route, vol_level;
HAL_Status hal_status;
Audio_Device audio_device;
cnt = cmd_sscanf(cmd, "r=%u g=%u", &route, &vol_level);
if (cnt != 2) {
CMD_ERR("cmd_sscanf return: cnt = %d\n", cnt);
return CMD_STATUS_INVALID_ARG;
}
switch (route) {
case 0:
audio_device = AUDIO_IN_DEV_AMIC;
break;
case 1:
audio_device = AUDIO_IN_DEV_LINEIN;
break;
case 3:
audio_device = AUDIO_OUT_DEV_SPK;
break;
case 2:
default:
CMD_ERR("invalid route %u\n", route);
return CMD_STATUS_INVALID_ARG;
}
if (route == 3) {
if (vol_level < 0 || vol_level > 31) {
CMD_ERR("invalid vol_level %u\n", vol_level);
return CMD_STATUS_INVALID_ARG;
}
} else {
if (vol_level < 0 || vol_level > 7) {
CMD_ERR("invalid vol_level %u\n", vol_level);
return CMD_STATUS_INVALID_ARG;
}
}
hal_status = audio_manager_handler(CMD_CODEC_PLAY_SND_CARD, AUDIO_MANAGER_SET_VOLUME_LEVEL,
audio_device, (Volume_Level)vol_level);
if (hal_status == HAL_OK) {
return CMD_STATUS_OK;
} else {
CMD_ERR("Codec set vol_level Fail, return: hal_status = %d\n",
hal_status);
return CMD_STATUS_FAIL;
}
}
static const struct cmd_data g_codec_cmds[] = {
{ "open", cmd_codec_open_exec },
{ "close", cmd_codec_close_exec },
{ "pcm_read", cmd_codec_pcm_read_exec },
{ "pcm_write", cmd_codec_pcm_write_exec },
{ "set_route", cmd_codec_set_route_exec},
{ "set_gain", cmd_codec_set_gain_exec},
};
enum cmd_status cmd_codec_exec(char *cmd)
{
return cmd_exec(cmd, g_codec_cmds, cmd_nitems(g_codec_cmds));
}