Thursday, May 31, 2012

[SKYPEKIT][DM368][IPNC] Audio PCM/ALSA Experiment on DM368-IPNC,

The previous trial on skypekit audio ([SKYPEKIT][DM368][IPNC] Audio PCM Experiment on DM368-IPNC) was using OSS (/dev/dsp) for Audio PCM host input and output voice. The major cons are that it's lack of powerful helper functions, i.e. API, which might lead to delay.

After trying to implement Voice RTP host (a nightmare since DM368 is lack of hardware G.729 encode/decode support. Even if using G.711, the CPU usage is not significant less compared to deploying Audio PCM host), we decided to use ALSA instead of OSS.


The code to start with is still PCMHost loopback. The steps are:

1. Remove buffer manipulation codes

Similar to what we did in OSS, we don't need loopback_buf anymore, as well as it's Pull() and Push() methods. It's safe to remove those codes in Run()@PCMHostLoopback.c. It can save you some CPU time.

2. Open ALSA device for voice input and output

I decided to put the initialization in Start():PCMLoopback. If the deviceType is INPUT_DEVICE, the code would be something like:

int rc, dir = 0;
unsigned int val;
snd_pcm_hw_params_t *hw_params = NULL;
snd_pcm_uframes_t uframe;
if(capture_handle == 0)
{
rc = snd_pcm_open(&capture_handle, "default", SND_PCM_STREAM_CAPTURE, 0);
if (rc < 0)
{
SID_ERROR("unable to open pcm device: %s\n", snd_strerror(rc));
return PCMIF_ERROR;
}
/* Allocate a hardware parameters object. */
snd_pcm_hw_params_alloca(&hw_params);
/* Fill it in with default values. */
snd_pcm_hw_params_any(capture_handle, hw_params);
/* Sampling rate*/
val = input_sampleRate;
snd_pcm_hw_params_set_rate_near(capture_handle, hw_params, &val, 0);
/* Signed 16-bit little-endian format */
snd_pcm_hw_params_set_format(capture_handle, hw_params, SND_PCM_FORMAT_S16_LE);
/* Interleaved mode */
snd_pcm_hw_params_set_access(capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
/* Num channels */
snd_pcm_hw_params_set_channels(capture_handle, hw_params, 1);
uframe = (output_sampleRate / 100);
rc = snd_pcm_hw_params_set_period_size_near(capture_handle, hw_params, &uframe, 0);
if (rc < 0)
{
SID_ERROR("snd_pcm_hw_params_set_period_size_near %s\n", snd_strerror(rc));
return PCMIF_ERROR;
}
rc = snd_pcm_hw_params(capture_handle, hw_params);
if (rc < 0)
{
SID_ERROR("snd_pcm_hw_params %s\n", snd_strerror(rc));
return PCMIF_ERROR;
}

Similarly, if deviceType == OUTPUT_DEVICE, the code would be


int rc, dir = 0;
unsigned int val;
snd_pcm_hw_params_t *hw_params = NULL;
snd_pcm_uframes_t uframe;
if(playback_handle == 0)
{
rc = snd_pcm_open(&playback_handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
if (rc < 0)
{
SID_ERROR("unable to open pcm device: %s\n", snd_strerror(rc));
return PCMIF_ERROR;
}
/* Allocate a hardware parameters object. */
snd_pcm_hw_params_alloca(&hw_params);
/* Fill it in with default values. */
snd_pcm_hw_params_any(playback_handle, hw_params);
/* Sampling rate*/
val = output_sampleRate;
snd_pcm_hw_params_set_rate_near(playback_handle, hw_params, &val, 0);
/* Signed 16-bit little-endian format */
snd_pcm_hw_params_set_format(playback_handle, hw_params, SND_PCM_FORMAT_S16_LE);
/* Interleaved mode */
snd_pcm_hw_params_set_access(playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
/* Num channels */
snd_pcm_hw_params_set_channels(playback_handle, hw_params, 1);
uframe = (output_sampleRate / 100);
rc = snd_pcm_hw_params_set_period_size_near(playback_handle, hw_params, &uframe, 0);
if (rc < 0)
{
SID_ERROR("snd_pcm_hw_params_set_period_size_near %s\n", snd_strerror(rc));
return PCMIF_ERROR;
}
rc = snd_pcm_hw_params(playback_handle, hw_params);
if (rc < 0)
{
SID_ERROR("snd_pcm_hw_params %s\n", snd_strerror(rc));
return PCMIF_ERROR;
}


3. Close ALSA device when necessary
I put them in Stop()::PCMLoopback, maybe there is good idea to put it at other place like ~PCMLoopback() or Uninit().


snd_pcm_close(playback_handle);
snd_pcm_close(capture_handle);

4. Feed data to ALSA playback and Deliver ALSA captured data to Skypekit Runtime
in Run()::PCMLoopback

if output_started is true, using 
len = snd_pcm_writei(playback_handle, output_buf.m_data.data(), output_buf.m_data.size()>>1);
to send voice out.

if input_started is true, using
len = snd_pcm_readi(capture_handle, input_buf.m_data.data(), input_sampleRate / 100); //input_buf.m_data.size());
to get captured data and send it to Skypekit Runtime by invoking InputDeviceReady().

Do remember to call snd_pcm_recover() if snd_pcm_readi or snd_pcm_writei returns error.

The CPU usage of using ALSA is almost the same as we are using OSS, but we can get more control on  audio I/O.

Easy job, right?





No comments:

Post a Comment