#include #include #include #include #include #include #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=,][name='name',][index=,][device=,][subdevice=]]|[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; }