/******************************************************** * Copyright (c) 2002 Impulsesoft Pvt. Ltd. * * All rights reserved * *******************************************************/ /******************************************************* * Name : Audio.c * Description : Wrapper function to access Audio data * over Bluetooth module * * Module : PhoneGW * Author : Mayank * * Revision No : $Revision: 1.11 $ * Changed On : $Date: 2003/10/10 05:17:39 $ * Changed By : $Author: kausik $ *******************************************************/ #include #include #include #include #include #include #include #include #include "Audio.h" #define DEBUG_LOG printf #define DEBUG_ERR printf #define SND_PCM_NAME_DEFAULT "default" #define MONO 1 #define SAMPLE_8000HZ 8000 static int gbPcmOpen = 0 ; static my_snd_pcm uPcm; int start_delay = 0; static size_t g_bits_per_sample; //#define USE_NEWAPI #define CAPTURE_ENABLED #ifndef USE_NEWAPI void set_params(snd_pcm_t *puHandle, int iFormat); #endif static int MySndPcmOpen(my_snd_pcm *puPcm,int iCard) { int iResult; if ((iResult = snd_pcm_open(&puPcm->playback, SND_PCM_NAME_DEFAULT, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { DEBUG_ERR("Error: audio open error: %s\n", snd_strerror(iResult)); return -1; } snd_pcm_info_alloca(&puPcm->playback_info); if (snd_pcm_info(puPcm->playback,puPcm->playback_info) < 0) { DEBUG_ERR("Error in info\n"); return -1; } #ifdef CAPTURE_ENABLED if ((iResult = snd_pcm_open(&puPcm->capture, SND_PCM_NAME_DEFAULT, SND_PCM_STREAM_CAPTURE, 0)) < 0) { DEBUG_ERR("Error: audio open error: %s\n", snd_strerror(iResult)); return -1; } snd_pcm_info_alloca(&puPcm->capture_info); if (snd_pcm_info(puPcm->capture,puPcm->capture_info) < 0) { DEBUG_ERR("Error in info\n"); return -1; } //snd_pcm_drain(puPcm->capture); #endif snd_pcm_drain(puPcm->playback); return(0); } static int MySetPcmParam(my_snd_pcm *puPcm, int iFragment,int iFormat) { #ifdef USE_NEWAPI int iRet = -1; iRet = snd_pcm_set_params(puPcm->playback, iFormat, SND_PCM_ACCESS_RW_INTERLEAVED, MONO, SAMPLE_8000HZ, 0/*Resampling*/, 0/*Latency*/); if(iRet < 0) { printf("snd_pcm_set_params(playback) returned error. %d\n", iRet); return -1; } g_bits_per_sample = snd_pcm_format_physical_width(iFormat); #ifdef CAPTURE_ENABLED iRet = snd_pcm_set_params(puPcm->capture, iFormat, SND_PCM_ACCESS_RW_INTERLEAVED, MONO, SAMPLE_8000HZ, 0/*Resampling*/, 0/*Latency*/); if(iRet < 0) { printf("snd_pcm_set_params(capture) returned error. %d\n", iRet); return -1; } #endif snd_pcm_drain(puPcm->playback); printf("MySetPcmParam():Before PrepareState:%d\n",snd_pcm_state(puPcm->playback)); if (snd_pcm_prepare(puPcm->playback) < 0) { DEBUG_ERR("unable to prepare channel\n"); return(-1); } printf("MySetPcmParam():after prepare-State:%d\n",snd_pcm_state(puPcm->playback)); #ifdef CAPTURE_ENABLED snd_pcm_drain(puPcm->capture); if (snd_pcm_prepare(puPcm->capture)<0) { DEBUG_ERR("unable to set recording channel params\n"); return(-1); } #endif alsa_print_hw_info(); #else set_params(puPcm->playback, iFormat); start_delay = 1; set_params(puPcm->capture, iFormat); #endif return(0); } void alsa_print_hw_info() { my_snd_pcm *puPcm = &uPcm; snd_pcm_uframes_t uBufferSize = 0; snd_pcm_uframes_t uPeriodSize = 0; unsigned int iBufferTime = 0, iPeriodTime = 0; unsigned int iPeriods = 0; int iErr = 0; int dir = 0; snd_pcm_hw_params_t *params; snd_pcm_hw_params_t *cparams; snd_pcm_hw_params_alloca(¶ms); snd_pcm_hw_params_alloca(&cparams); snd_pcm_get_params(puPcm->playback,&uBufferSize, &uPeriodSize); printf("snd_pcm_get_params for playback returned the values as 0x%x, 0x%x\n",uBufferSize,uPeriodSize); snd_pcm_get_params(puPcm->capture,&uBufferSize, &uPeriodSize); printf("snd_pcm_get_params for capture returned the values as 0x%x, 0x%x\n",uBufferSize,uPeriodSize); iErr = snd_pcm_hw_params_any(puPcm->playback, params); if (iErr < 0) { printf("Broken configuration for this PCM: no configurations available\n"); return; } snd_pcm_hw_params_get_period_size(params, &uPeriodSize, &dir); snd_pcm_hw_params_get_period_time(params, &iPeriodTime, &dir); snd_pcm_hw_params_get_periods(params, &iPeriods, &dir); snd_pcm_hw_params_get_buffer_size(params, &uBufferSize); snd_pcm_hw_params_get_buffer_time(params, &iBufferTime, &dir); printf("alsa_print_hw_info(): buffersize:0x%x periodsize:0x%x\n" "buffertime:0%x periodtime:%x periods:0x%x\n", uBufferSize, uPeriodSize,iBufferTime, iPeriodTime, iPeriods); iErr = snd_pcm_hw_params_any(puPcm->capture, cparams); if (iErr < 0) { printf("Broken configuration for this PCM: no configurations available\n"); return; } snd_pcm_hw_params_get_period_size(cparams, &uPeriodSize, &dir); snd_pcm_hw_params_get_period_time(cparams, &iPeriodTime, &dir); snd_pcm_hw_params_get_periods(cparams, &iPeriods, &dir); snd_pcm_hw_params_get_buffer_size(cparams, &uBufferSize); snd_pcm_hw_params_get_buffer_time(cparams, &iBufferTime, &dir); printf("alsa_print_hw_info(): buffersize:0x%x periodsize:0x%x\n" "buffertime:0%x periodtime:%x periods:0x%x\n", uBufferSize, uPeriodSize,iBufferTime, iPeriodTime, iPeriods); } int wait_for_pcm_ready() { my_snd_pcm *puPcm = &uPcm; snd_pcm_wait(puPcm->playback, 10); snd_pcm_wait(puPcm->capture, 10); } int ReadFromAlsa(void *buf, int len) { my_snd_pcm *puPcm = &uPcm; snd_pcm_uframes_t uNumFrames = (len * 8)/(g_bits_per_sample*1); if (gbPcmOpen) { int iLen; static iiLen = 0; iLen = snd_pcm_mmap_readi(puPcm->capture, buf, uNumFrames); if(iLen == -EAGAIN) { /* Wait for a max of 10 ms for pcm to get ready/be in a sane state*/ printf("ReadFromAlsa(): read error EAGAIN\n"); printf("WriteToAlsa():State:%d\n",snd_pcm_state(puPcm->capture)); snd_pcm_wait(puPcm->playback, 10); return iLen; } else if(iLen == -EPIPE) { printf("ReadFromAlsa():State:%d, iiLen=%d\n",snd_pcm_state(puPcm->capture),iiLen); iiLen = 0; snd_pcm_drain(puPcm->capture); if (snd_pcm_prepare(puPcm->capture)<0) { DEBUG_ERR("unable to set recording channel params\n"); return 0; } printf("ReadFromAlsa():State:%d\n",snd_pcm_state(puPcm->capture)); return 0; } else if(iLen <= 0) { printf("ReadFromAlsa():State:%d\n",snd_pcm_state(puPcm->capture)); printf("ReadFromAlsa(): read error %d\n", iLen); return iLen; } iiLen += iLen; return iLen*((g_bits_per_sample*1)/8); } else return -EBADF; } int WriteToAlsa(void *buf, int len) { my_snd_pcm *puPcm = &uPcm; snd_pcm_uframes_t uNumFrames = (len * 8)/(g_bits_per_sample*1); //printf("WriteToAlsa():State:%d\n",snd_pcm_state(puPcm->playback)); if (gbPcmOpen) { int iLen; static iiLen = 0; iLen = snd_pcm_mmap_writei(puPcm->playback, buf, uNumFrames); if(iLen == -EPIPE) { printf("WriteToAlsa():State:%d, iiLen=%d\n",snd_pcm_state(puPcm->playback),iiLen); iiLen = 0; snd_pcm_drain(puPcm->playback); if (snd_pcm_prepare(puPcm->playback)<0) { //return 0; } printf("WriteToAlsa():State:%d\n",snd_pcm_state(puPcm->playback)); return 0; } else if(iLen < 0) { return iLen; } iiLen += iLen; return iLen*((g_bits_per_sample*1)/8); } else return -EBADF; } static snd_output_t *log; int StartAudio(int iFragment, int iFormat) { my_snd_pcm *puPcm = &uPcm; int err; err = snd_output_stdio_attach(&log, stderr, 0); assert(err >= 0); if (!gbPcmOpen) { if (MySndPcmOpen(puPcm,0) < 0) return -1; if (MySetPcmParam(puPcm, iFragment,iFormat) < 0) { printf("MySetPcmParam(): Failed\n"); return -1; } gbPcmOpen = 1 ; } else DEBUG_ERR("Audio Device is already opened\n"); return 0; } int StopAudio() { my_snd_pcm *puPcm = &uPcm; DEBUG_LOG("Closing Audio device\n"); if (gbPcmOpen) { gbPcmOpen = 0 ; snd_pcm_drain(puPcm->playback); snd_pcm_drain(puPcm->capture); snd_pcm_close(puPcm->playback); snd_pcm_close(puPcm->capture); } snd_output_close(log); snd_config_update_free_global(); return 0; } #ifndef USE_NEWAPI /* Aplay set params.. storing for adversity :) */ void set_params(snd_pcm_t *puHandle, int iFormat) { snd_pcm_uframes_t chunk_size = 0; unsigned period_time = 0; unsigned buffer_time = 0; snd_pcm_uframes_t period_frames = 0; snd_pcm_uframes_t buffer_frames = 0; unsigned int sleep_min = 0; int avail_min = -1; int mmap_flag = 1; int stop_delay = 0; size_t bits_per_frame; size_t chunk_bytes; snd_pcm_hw_params_t *params; snd_pcm_sw_params_t *swparams; snd_pcm_uframes_t buffer_size; int err; size_t n; snd_pcm_uframes_t xfer_align; unsigned int rate; snd_pcm_uframes_t start_threshold, stop_threshold; snd_pcm_hw_params_alloca(¶ms); snd_pcm_sw_params_alloca(&swparams); err = snd_pcm_hw_params_any(puHandle, params); if (err < 0) { printf("Broken configuration for this PCM: no configurations available"); return; } if(mmap_flag) { snd_pcm_access_mask_t *mask = alloca(snd_pcm_access_mask_sizeof()); snd_pcm_access_mask_none(mask); snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED); snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED); snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_COMPLEX); err = snd_pcm_hw_params_set_access_mask(puHandle, params, mask); } // err = snd_pcm_hw_params_set_access(puHandle, params, // SND_PCM_ACCESS_RW_INTERLEAVED); if (err < 0) { printf("Access type not available\n"); return; } err = snd_pcm_hw_params_set_format(puHandle, params, iFormat); if (err < 0) { printf("Sample format non available\n"); return; } err = snd_pcm_hw_params_set_channels(puHandle, params, 1); if (err < 0) { printf("Channels count non available"); return; } rate = SAMPLE_8000HZ; err = snd_pcm_hw_params_set_rate_near(puHandle, params, &rate, 0); if (buffer_time == 0 && buffer_frames == 0) { err = snd_pcm_hw_params_get_buffer_time_max(params, &buffer_time, 0); assert(err >= 0); if (buffer_time > 500000) buffer_time = 500000; } printf("ALSA : BufferTime = %d\n",buffer_time); if (period_time == 0 && period_frames == 0) { if (buffer_time > 0) period_time = buffer_time / 4; else period_frames = buffer_frames / 4; } printf("ALSA : period_time=%x, period_frames=%x, buffer_time=%x, buffer_frames=%x\n", period_time,period_frames,buffer_time,buffer_frames); if (period_time > 0) err = snd_pcm_hw_params_set_period_time_near(puHandle, params, &period_time, 0); else err = snd_pcm_hw_params_set_period_size_near(puHandle, params, &period_frames, 0); assert(err >= 0); if (buffer_time > 0) { err = snd_pcm_hw_params_set_buffer_time_near(puHandle, params, &buffer_time, 0); } else { err = snd_pcm_hw_params_set_buffer_size_near(puHandle, params, &buffer_frames); } assert(err >= 0); err = snd_pcm_hw_params(puHandle, params); if (err < 0) { printf("Unable to install hw params:"); return; } snd_pcm_hw_params_get_period_size(params, &chunk_size, 0); snd_pcm_hw_params_get_buffer_size(params, &buffer_size); if (chunk_size == buffer_size) { printf("Can't use period equal to buffer size (%lu == %lu)", chunk_size, buffer_size); return; } snd_pcm_sw_params_current(puHandle, swparams); err = snd_pcm_sw_params_get_xfer_align(swparams, &xfer_align); if (err < 0) { printf("Unable to obtain xfer align\n"); return; } if (sleep_min) xfer_align = 1; err = snd_pcm_sw_params_set_sleep_min(puHandle, swparams, sleep_min); assert(err >= 0); if(avail_min < 0) n = chunk_size; else n = (double) rate * avail_min/1000000; err = snd_pcm_sw_params_set_avail_min(puHandle, swparams, n); /* round up to closest transfer boundary */ n = (buffer_size / xfer_align) * xfer_align; if (start_delay <= 0) { start_threshold = n + (double) rate * start_delay / 1000000; } else start_threshold = (double) rate * start_delay / 1000000; if (start_threshold < 1) start_threshold = 1; if (start_threshold > n) start_threshold = n; err = snd_pcm_sw_params_set_start_threshold(puHandle, swparams, start_threshold); assert(err >= 0); if (stop_delay <= 0) stop_threshold = buffer_size + (double) rate * stop_delay / 1000000; else stop_threshold = (double) rate * stop_delay / 1000000; err = snd_pcm_sw_params_set_stop_threshold(puHandle, swparams, stop_threshold); assert(err >= 0); err = snd_pcm_sw_params_set_xfer_align(puHandle, swparams, xfer_align); assert(err >= 0); if (snd_pcm_sw_params(puHandle, swparams) < 0) { printf("unable to install sw params:"); return; } g_bits_per_sample = snd_pcm_format_physical_width(iFormat); bits_per_frame = g_bits_per_sample * 1; chunk_bytes = chunk_size * bits_per_frame / 8; snd_pcm_dump(puHandle, log); printf("ALSA : period_time=0x%x, period_frames=0x%x, buffer_time=0x%x, buffer_frames=0x%x\n", period_time,period_frames,buffer_time,buffer_frames); printf("chunk_size:%d chunk_bytes:%d bits/frame:%d b/s:%d\n", chunk_size, chunk_bytes, bits_per_frame, g_bits_per_sample); return; } #endif