avs-device-sdk/extension/mic-asp-sdk/srcs/tdmin_interface.c

463 lines
16 KiB
C
Executable File

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <alsa/asoundlib.h>
#include "tdmin_interface.h"
#define ALSA_OK 0
#define ALSA_FAIL -1
#if 0
#define PB_ALSA_ERR(x) printf x
#define PB_ALSA_INFO(x) printf x
#else
#define PB_ALSA_ERR(x)
#define PB_ALSA_INFO(x)
#endif
#define PCM_HANDLE_CHECK(handle) \
do { \
if (NULL == handle) { \
PB_ALSA_ERR((PB_ALSA_DEBUG_TAG "%s -- Invail pcm handle fail\n", __FUNCTION__)); \
return ALSA_FAIL; \
} \
} while (0)
static int cset(int argc, char argv1[], char argv2[], int roflag, int keep_handle);
static long long gdatalen = 0;
/*---------------------------------------------------------------------------
* Name
* Playback_Alsa_ReadPCM
* Description -
* Input arguments -
* Output arguments -
* Returns -ok:write pcm size fail or pause:-1
*---------------------------------------------------------------------------*/
int gLineOutDetected = 0;
static int _alsa_read_pcm(PCMContainer_t* sndpcm, size_t rcount) {
ssize_t r;
size_t result = 0;
size_t count = rcount;
uint8_t* data = sndpcm->data_buf_16;
size_t i;
if (count != sndpcm->chunk_size) {
count = sndpcm->chunk_size;
}
while (count > 0) {
r = snd_pcm_readi(sndpcm->handle, data, count);
if (r == -EAGAIN || (r >= 0 && (size_t)r < count)) {
snd_pcm_wait(sndpcm->handle, 1000);
} else if (r == -EPIPE) {
snd_pcm_prepare(sndpcm->handle);
PB_ALSA_ERR((PB_ALSA_DEBUG_TAG "<<<<<<<<<<<<<<< Buffer Underrun >>>>>>>>>>>>>>>\n"));
} else if (r == -ESTRPIPE) {
PB_ALSA_ERR((PB_ALSA_DEBUG_TAG "<<<<<<<<<<<<<<< Need suspend >>>>>>>>>>>>>>>\n"));
} else if (r < 0) {
PB_ALSA_ERR((PB_ALSA_DEBUG_TAG "Error snd_pcm_readi: [%s]\n", snd_strerror(r)));
return -1;
}
if (r > 0) {
result += r;
count -= r;
data += r * sndpcm->bits_per_frame / 8;
}
}
#if 1
unsigned char* data_raw = sndpcm->data_buf_16;
unsigned char* data_32 = sndpcm->data_buf;
// rcount = chunk_bytes_16
memset(sndpcm->data_buf, 0, sndpcm->chunk_bytes);
for (i = 0; i < rcount; i += 32) {
memcpy((data_32 + 24), (data_raw), 4);
memcpy((data_32 + 28), (data_raw + 4), 4);
memcpy((data_32), (data_raw + 8), 4);
memcpy((data_32 + 12), (data_raw + 12), 4);
memcpy((data_32 + 8), (data_raw + 16), 4);
memcpy((data_32 + 4), (data_raw + 20), 4);
memcpy((data_32 + 16), (data_raw + 24), 4);
memcpy((data_32 + 20), (data_raw + 28), 4);
data_raw += 32;
data_32 += 32;
}
#endif
return rcount;
}
/*---------------------------------------------------------------------------
* Name
* Playback_Alsa_SetHWParams
* Description -
* Input arguments -
* Output arguments -
* Returns - OK(0) PARAMS ERR(-1)
*---------------------------------------------------------------------------*/
static int _alsa_set_hw_params(PCMContainer_t* pcm_params, uint32_t ui4_max_buffer_time) {
snd_pcm_hw_params_t* hwparams;
uint32_t exact_rate;
uint32_t buffer_time, period_time;
int err;
PCM_HANDLE_CHECK(pcm_params->handle);
/* Allocate the snd_pcm_hw_params_t structure on the stack. */
snd_pcm_hw_params_alloca(&hwparams);
/* Fill it with default values */
err = snd_pcm_hw_params_any(pcm_params->handle, hwparams);
if (err < 0) {
PB_ALSA_ERR((PB_ALSA_DEBUG_TAG "Error snd_pcm_hw_params_any : %s\n", snd_strerror(err)));
goto ERR_SET_PARAMS;
}
/* Interleaved mode */
err = snd_pcm_hw_params_set_access(pcm_params->handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
if (err < 0) {
PB_ALSA_ERR((PB_ALSA_DEBUG_TAG "Error snd_pcm_hw_params_set_access : %s\n", snd_strerror(err)));
goto ERR_SET_PARAMS;
}
/* Set sample format */
err = snd_pcm_hw_params_set_format(pcm_params->handle, hwparams, pcm_params->format);
if (err < 0) {
PB_ALSA_ERR((PB_ALSA_DEBUG_TAG "Error snd_pcm_hw_params_set_format : %s\n", snd_strerror(err)));
goto ERR_SET_PARAMS;
}
/* Set number of channels */
err = snd_pcm_hw_params_set_channels(pcm_params->handle, hwparams, LE_SHORT(pcm_params->channels));
if (err < 0) {
PB_ALSA_ERR((PB_ALSA_DEBUG_TAG "Error snd_pcm_hw_params_set_channels : %s\n", snd_strerror(err)));
goto ERR_SET_PARAMS;
}
/* Set sample rate. If the exact rate is not supported */
/* by the hardware, use nearest possible rate. */
exact_rate = LE_INT(pcm_params->sample_rate);
err = snd_pcm_hw_params_set_rate_near(pcm_params->handle, hwparams, &exact_rate, 0);
if (err < 0) {
PB_ALSA_ERR((PB_ALSA_DEBUG_TAG "Error snd_pcm_hw_params_set_rate_near : %s\n", snd_strerror(err)));
goto ERR_SET_PARAMS;
}
if (LE_INT(pcm_params->sample_rate) != exact_rate) {
PB_ALSA_ERR(
(PB_ALSA_DEBUG_TAG "The rate %d Hz is not supported by your hardware.\n ==> Using %d Hz instead.\n",
LE_INT(pcm_params->sample_rate),
exact_rate));
}
err = snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time, 0);
if (err < 0) {
PB_ALSA_ERR((PB_ALSA_DEBUG_TAG "Error snd_pcm_hw_params_get_buffer_time_max : %s\n", snd_strerror(err)));
goto ERR_SET_PARAMS;
}
PB_ALSA_ERR((PB_ALSA_DEBUG_TAG "snd_pcm_hw_params_get_buffer_time_max : %ul (us)\n", buffer_time));
if (buffer_time > ui4_max_buffer_time) buffer_time = ui4_max_buffer_time; /*200000us = 200ms*/
if (buffer_time > 0) period_time = buffer_time / 4;
err = snd_pcm_hw_params_set_buffer_time_near(pcm_params->handle, hwparams, &buffer_time, 0);
if (err < 0) {
PB_ALSA_ERR((PB_ALSA_DEBUG_TAG "Error snd_pcm_hw_params_set_buffer_time_near : %s\n", snd_strerror(err)));
goto ERR_SET_PARAMS;
}
err = snd_pcm_hw_params_set_period_time_near(pcm_params->handle, hwparams, &period_time, 0);
if (err < 0) {
PB_ALSA_ERR((PB_ALSA_DEBUG_TAG "Error snd_pcm_hw_params_set_period_time_near : %s\n", snd_strerror(err)));
goto ERR_SET_PARAMS;
}
/* Set hw params */
err = snd_pcm_hw_params(pcm_params->handle, hwparams);
if (err < 0) {
PB_ALSA_ERR((PB_ALSA_DEBUG_TAG "Error snd_pcm_hw_params: %s at line->%d\n", snd_strerror(err), __LINE__));
goto ERR_SET_PARAMS;
}
snd_pcm_hw_params_get_period_size(hwparams, &pcm_params->chunk_size, 0);
snd_pcm_hw_params_get_buffer_size(hwparams, &pcm_params->buffer_size);
if (pcm_params->chunk_size == pcm_params->buffer_size) {
PB_ALSA_ERR(
(PB_ALSA_DEBUG_TAG "Can't use period equal to buffer size (%lu == %lu)\n",
pcm_params->chunk_size,
pcm_params->buffer_size));
goto ERR_SET_PARAMS;
}
printf(
">> chunk_size = %d, buffer_size = %d\n",
(unsigned int)pcm_params->chunk_size,
(unsigned int)pcm_params->buffer_size);
PB_ALSA_ERR(
(PB_ALSA_DEBUG_TAG "chunk_size is %lu, buffer size is %lu\n", pcm_params->chunk_size, pcm_params->buffer_size));
/*bits per sample = bits depth*/
pcm_params->bits_per_sample = snd_pcm_format_physical_width(pcm_params->format);
/*bits per frame = bits depth * channels*/
pcm_params->bits_per_frame = pcm_params->bits_per_sample * LE_SHORT(pcm_params->channels);
/*chunk byte is a better size for each write or read for alsa*/
pcm_params->chunk_bytes_16 = pcm_params->chunk_size * pcm_params->bits_per_frame / 8;
pcm_params->chunk_bytes = pcm_params->chunk_bytes_16 * 2;
printf(
">>> bps = %d, chunk_bytes = %d\n",
(unsigned int)pcm_params->bits_per_frame,
(unsigned int)pcm_params->chunk_bytes_16);
/* Allocate audio data buffer */
pcm_params->data_buf = (uint8_t*)malloc(pcm_params->chunk_bytes);
if (!pcm_params->data_buf) {
PB_ALSA_ERR((PB_ALSA_DEBUG_TAG "Error malloc: [data_buf] at line-> %d\n", __LINE__));
goto ERR_SET_PARAMS;
}
pcm_params->data_buf_16 = (uint8_t*)malloc(pcm_params->chunk_bytes_16);
if (!pcm_params->data_buf_16) {
PB_ALSA_ERR((PB_ALSA_DEBUG_TAG "Error malloc: [data_buf] at line-> %d\n", __LINE__));
goto ERR_SET_PARAMS;
}
return 0;
ERR_SET_PARAMS:
if (NULL != pcm_params->data_buf) {
free(pcm_params->data_buf);
pcm_params->data_buf = NULL;
}
if (NULL != pcm_params->data_buf_16) {
free(pcm_params->data_buf_16);
pcm_params->data_buf_16 = NULL;
}
snd_pcm_close(pcm_params->handle);
pcm_params->handle = NULL;
return -1;
}
int _pcm_32_to_16(char* dst_buf, char* ori_buf, int ori_len, int fseek_bit) {
char* p = ori_buf;
char* q = dst_buf;
unsigned int* temp;
if (dst_buf == NULL || ori_buf == NULL) {
printf("Err: u_aispeech_pcm_32_to_16() buf is null!");
return -1;
}
while (ori_len >= 4) {
temp = (unsigned int*)p;
*temp = (*temp << fseek_bit);
*q = *(p + 2);
*(q + 1) = *(p + 3);
q += 2;
p += 4;
ori_len -= 4;
}
return 0;
}
int alsa_tdmin_init(PCMContainer_t* pcm_params, int samplerate, int channels, int max_buffer_time) {
int i4_ret = 0;
if (pcm_params == NULL) {
printf("PCMContainer handle == NULL!\n");
return TDMIN_INV_ARG;
}
pcm_params->format = SND_PCM_FORMAT_S32_LE;
pcm_params->sample_rate = samplerate;
pcm_params->channels = channels;
// i4_ret = snd_pcm_open(&pcm_params->handle, AISPEECH_TDMIC_PCM_RECORD_DEVICE_NAME, SND_PCM_STREAM_CAPTURE, 0);
i4_ret = snd_pcm_open(&pcm_params->handle, TDM_IN_DEVICE_NAME, SND_PCM_STREAM_CAPTURE, 0);
if (0 != i4_ret) {
printf("snd_pcm_open failed %d!\n", i4_ret);
return TDMIN_FAIL;
}
i4_ret = _alsa_set_hw_params(pcm_params, max_buffer_time);
if (i4_ret != 0) {
printf("u_alsa_set_hw_params failed!\n");
return TDMIN_FAIL;
}
printf("Set alsa param OK, start read pcm!!!\n");
gdatalen = 0;
return TDMIN_OK;
}
void alsa_tdmin_uninit(PCMContainer_t* pcm_params) {
if (pcm_params == NULL) {
printf("PCMContainer handle == NULL!\n");
return;
}
if (NULL != pcm_params->data_buf) {
free(pcm_params->data_buf);
pcm_params->data_buf = NULL;
}
if (NULL != pcm_params->data_buf_16) {
free(pcm_params->data_buf_16);
pcm_params->data_buf_16 = NULL;
}
snd_pcm_close(pcm_params->handle);
pcm_params->handle = NULL;
}
int asla_tdmin_init_bias() {
cset(2, "name='ADC Gain Switch'", "0", 0, 0);
cset(2, "name='ADC Bais'", "19", 0, 0);
cset(2, "name='ADC Gain Switch'", "1", 0, 0);
cset(2, "name='ADC Bais'", "19", 0, 0);
return 1;
}
int asla_tdmin_set_bias(int on) {
cset(2, "name='ADC Gain Switch'", "0", 0, 0);
for (int i = 1; i <= 4; i++) {
char cmd[128];
sprintf(cmd, "name='Channel%d enable'", i);
cset(2, cmd, on ? "1" : "0", 0, 0);
}
cset(2, "name='ADC Gain Switch'", "1", 0, 0);
for (int i = 1; i <= 4; i++) {
char cmd[128];
sprintf(cmd, "name='Channel%d enable'", i);
cset(2, cmd, on ? "1" : "0", 0, 0);
}
return 1;
}
int alsa_read_tdmin_pcm(PCMContainer_t* pcm_params) {
int i4_ret = 0;
if (pcm_params == NULL) {
printf("PCMContainer handle == NULL!\n");
return TDMIN_INV_ARG;
}
i4_ret = _alsa_read_pcm(pcm_params, pcm_params->chunk_bytes_16);
return i4_ret;
}
static int cset(int argc, char argv1[], char argv2[], int roflag, int keep_handle) {
#define LEVEL_BASIC (1 << 0)
#define LEVEL_INACTIVE (1 << 1)
#define LEVEL_ID (1 << 2)
int err;
static snd_ctl_t* handle = NULL;
snd_ctl_elem_info_t* info;
snd_ctl_elem_id_t* id;
snd_ctl_elem_value_t* control;
snd_ctl_elem_info_alloca(&info);
snd_ctl_elem_id_alloca(&id);
snd_ctl_elem_value_alloca(&control);
char card[64] = "default";
if (argc < 1) {
fprintf(
stderr,
"Specify a full control identifier: "
"[[iface=<iface>,][name='name',][index=<index>,][device=<device>,][subdevice=<subdevice>]]|[numid=<numid>]"
"\n");
return -EINVAL;
}
if (snd_ctl_ascii_elem_id_parse(id, argv1)) {
fprintf(stderr, "Wrong control identifier: %s\n", argv1);
return -EINVAL;
}
if (handle == NULL && (err = snd_ctl_open(&handle, card, 0)) < 0) {
printf("Control %s open error: %s\n", card, snd_strerror(err));
return err;
}
snd_ctl_elem_info_set_id(info, id);
if ((err = snd_ctl_elem_info(handle, info)) < 0) {
printf("Cannot find the given element from control %s\n", card);
if (!keep_handle) {
snd_ctl_close(handle);
handle = NULL;
}
return err;
}
snd_ctl_elem_info_get_id(info, id); /* FIXME: Remove it when hctl find works ok !!! */
if (!roflag) {
snd_ctl_elem_value_set_id(control, id);
if ((err = snd_ctl_elem_read(handle, control)) < 0) {
printf("Cannot read the given element from control %s\n", card);
if (!keep_handle) {
snd_ctl_close(handle);
handle = NULL;
}
return err;
}
err = snd_ctl_ascii_value_parse(handle, control, info, argv2);
if (err < 0) {
printf("Control %s parse error: %s\n", card, snd_strerror(err));
if (!keep_handle) {
snd_ctl_close(handle);
handle = NULL;
}
return err;
}
if ((err = snd_ctl_elem_write(handle, control)) < 0) {
printf("Control %s element write error: %s\n", card, snd_strerror(err));
if (!keep_handle) {
snd_ctl_close(handle);
handle = NULL;
}
return err;
}
}
if (!keep_handle) {
snd_ctl_close(handle);
handle = NULL;
}
return 0;
}
// amixer cset name='ADC1 PGA gain' 1
int alsa_mixer_set_gain(int mic_gain, int dsp_gain) {
char mic_gain_buf[4];
char dsp_gain_buf[4];
sprintf(mic_gain_buf, "%d", mic_gain);
sprintf(dsp_gain_buf, "%d", dsp_gain);
#if 0
ret = cset(2, "name='ADC Gain Switch'", "0", 0, 0);
ret = cset(2, "name='ADC1 PGA gain'", mic_gain_buf, 0, 0);
ret = cset(2, "name='ADC2 PGA gain'", mic_gain_buf, 0, 0);
ret = cset(2, "name='ADC3 PGA gain'", mic_gain_buf, 0, 0);
ret = cset(2, "name='ADC4 PGA gain'", mic_gain_buf, 0, 0);
//ADC2
ret = cset(2, "name='ADC Gain Switch'", "1", 0, 0);
ret = cset(2, "name='ADC1 PGA gain'", mic_gain_buf, 0, 0);
ret = cset(2, "name='ADC2 PGA gain'", mic_gain_buf, 0, 0);
ret = cset(2, "name='ADC3 PGA gain'", dsp_gain_buf, 0, 0);
ret = cset(2, "name='ADC4 PGA gain'", dsp_gain_buf, 0, 0);
#endif
cset(2, "name='PGA1_setting'", mic_gain_buf, 0, 0);
cset(2, "name='PGA2_setting'", mic_gain_buf, 0, 0);
cset(2, "name='PGA3_setting'", mic_gain_buf, 0, 0);
cset(2, "name='PGA4_setting'", mic_gain_buf, 0, 0);
cset(2, "name='PGA5_setting'", dsp_gain_buf, 0, 0);
cset(2, "name='PGA6_setting'", dsp_gain_buf, 0, 0);
cset(2, "name='PGA7_setting'", mic_gain_buf, 0, 0);
cset(2, "name='PGA8_setting'", mic_gain_buf, 0, 0);
return 0;
}
int alsa_mixer_switch_mode(int mode) {
if (mode == 0) { // default
cset(2, "name='Channel Switch'", "0", 0, 0);
} else {
cset(2, "name='Channel Switch'", "1", 0, 0);
}
return 0;
}