463 lines
16 KiB
C
Executable File
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;
|
|
}
|