[alsa-devel] Any standard ALSA application for simultaneous capture and playabck ?????

Nobin Mathew nobin.mathew at gmail.com
Tue May 22 10:53:54 CEST 2007


I have a single threaded ALSA application which does simultaneous
capture and playback. But i am getting overflow error XRUN from
capture side.

 I am attaching the code to this mail.

Is there any problem with single threaded approach?

Is there any global configuration for ALSA lib?

When i run this app, ALSA state is getting corrupted, even aplay and
arecord is not working properly after that.

Any comments on this issue?

Thanks in advance
Nobin Mathew

On 5/22/07, Ciaccia <ciacciax at yahoo.com> wrote:
> > What are the complications in implementing simultaneous playback and
> > capture in ALSA???
>
>
> I would like to add something to this post: what is the "best" way to
> program a full duplex application? I would like to have an application
> that does full-duplex, with the lowest possible latency and using less
> CPU resource as possible. I tried different approaches, but I was not
> completely satisfied with none of them....
>
>
> Here is what I tried:
> -having two threads, one for playback and one for capture
> -defining 2 callbacks with async_handles and do all the processing in the callback functions
> (the last attempt)
> -using poll() to wait for one of the streams to be ready (see attached file fullduplex.c)
>
> Could some of you look at the attached code (it is not generic and contains lots of asserts, but it's supposed to work only on one single machine) and tell me if the last approach using poll is well structured?
>
> The main loop looks as following:
>
> >    while (1) {
> >        err = poll(ufds, 2, -1);
> >
> >        for (i = 0; i < 2; i++)
> >        {
> >            if (snd_pcm_poll_descriptors_revents(handles[i], &ufds[i], 1, &revents) < 0)
> >            {
> >                printf("Error getting revents for %s\n", descriptors[i].name);
> >                exit(1);
> >            }
> >
> >            if (revents & descriptors[i].poll_flag)
> >            {
> >                if (transfer_loop(handles[i], &first[i], descriptors[i].func) < 0)
> >                {
> >                    printf("transfer_loop error for %s\n", descriptors[i].name);
> >                    // TODO exit?!?
> >                }
> >            }
> >        }
> >    }
>
> and the transfer_loop is a modified version of the loop found in the ALSA examples (pcm.c and latency.c).
> Is this strategy optimal in respect to low latency and low CPU usage?
>
> Every comment is greatly appreciated
> Andrea
>
>
>
>
>
> ____________________________________________________________________________________Luggage? GPS? Comic books?
> Check out fitting gifts for grads at Yahoo! Search
> http://search.yahoo.com/search?fr=oni_on_mail&p=graduation+gifts&cs=bz
>
-------------- next part --------------
/********************************************************
 * 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 <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <ctype.h>
#include <errno.h>

#include <sys/ioctl.h>
#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(&params);
    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(&params);
	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 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Audio.h
Type: application/octet-stream
Size: 1481 bytes
Desc: not available
Url : http://mailman.alsa-project.org/pipermail/alsa-devel/attachments/20070522/f193bc34/attachment-0001.dll 
-------------- next part --------------
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <termio.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
//#include "ipc-char-ioctls.h"
#include <sys/signal.h>
#include <signal.h>
#include <alsa/asoundlib.h>
#include <sys/time.h>
#include <time.h>

typedef int int32;
typedef unsigned int uint32;
typedef char int8;
typedef unsigned char uint8;

#define SCO_CHUNK_SIZE 256
#define PRINT
#define CAPTURE_ENABLED
int uninit(int iIpcFd);

int StartAudio(int iFragment, int iFormat);
int StopAudio();
int ReadFromAlsa(void *buf, int len);
int WriteToAlsa(void *buf, int len);

//GSC3A_CREATE_MODULEID(OSA);

int giIpcFd = -1;
int giAudFd = -1;
int ghUpstreamThread = 0;

void usage()
{
    printf("USAGE:\n\t<exe> </dev/ipc...> <file to be sent>");    
    printf("Example:\tipcsco-tester /dev/ipcttysco sine.wav");    
}
int iAudioFd = -1;
void signal_handler(int sig)
{
    switch(sig)
	{
	case SIGHUP:
	    {
		}break;
	case SIGTERM:
		{
            if(iAudioFd>=0)
            {
                close(iAudioFd);
            }
            printf("Calling Uninit");
            uninit(giIpcFd);
            printf("Exiting \n");
            exit(0);
        }break;
    }
}


int StartPlaybackAndCapture(int ipcFD)
{
    unsigned char acData[SCO_CHUNK_SIZE];
    int iCount;
    int iCount123;
    
    printf("Start the while looop\n");
    //iCount123 = WriteToAlsa(acData,SCO_CHUNK_SIZE);
    while(1)
    {
#ifdef CAPTURE_ENABLED	    
        //gettimeofday(&start, &tz);
        //iCount = ReadFromAlsa(acData, SCO_CHUNK_SIZE);
        iCount = ReadFromAlsa(acData, SCO_CHUNK_SIZE);
	//printf("Written iCount\t%d\n", iCount);
#else
	iCount = read(giAudFd,acData,SCO_CHUNK_SIZE);
#endif
        if(iCount < 0)
        {
            printf("Read Error. %d\n", iCount);
            break;
        }
        else if(iCount == 0)
        {
            printf("Overflow state\n");
#ifndef CAPTURE_ENABLED
	    lseek(giAudFd, 0, SEEK_SET);
#endif	    
            continue;
        }
        {
	//	iCount = 0;
//printf("");	
            iCount = WriteToAlsa(acData,iCount);
            if (iCount < 0)
            {
                printf("Write Error : %d\n",iCount);
                break;
            }
        }
        
    }

    return 0;
}

int main(int argc, char *argv[])
{
    int iIpcFd = -1;
    if(argc < 2)
    {
        usage();
        return -1;
    }
    else
    {
        printf("In Execution  - [%s %s]\n", argv[0], argv[1]);
    }
   
    signal(SIGHUP,signal_handler); /* catch hangup signal */
	signal(SIGTERM,signal_handler); /* catch kill signal */

    StartAudio(SCO_CHUNK_SIZE, SND_PCM_FORMAT_S16_LE);
#ifndef CAPTURE_ENABLED
    giAudFd = open(argv[2],O_RDONLY);
    if(giAudFd < 0)
    {
        printf("OPeing Audio file failed \n");
	return -1;
    }
#endif 
    StartPlaybackAndCapture(iIpcFd);
    uninit(iIpcFd);
    return 0;
}

int uninit(int iIpcFd)
{
    StopAudio();
#ifndef CAPTURE_ENABLED
    close(giAudFd);
#endif 
    return 0;
}


More information about the Alsa-devel mailing list