View Single Post
Old 07-09-2012, 08:56 PM   #58
twobob
( ͡° ͜ʖ ͡°){ʇlnɐɟ ƃǝs}Týr
twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.
 
twobob's Avatar
 
Posts: 6,586
Karma: 6299993
Join Date: Jun 2012
Location: uti gratia usura (Yao ying da ying; Mo ying da yieng)
Device: PW-WIFI|K5-3G+WIFI| K4|K3-3G|DXG|K2| Rooted Nook Touch
Post microphone: a much more robust sampling venture

THIS IS ONLY TESTED ON A K3 - PLEASE USE THE TONES DEMO FOR OTHER MODELS

I have included here a compiled version of the incredibly obscure

http://alumnos.elo.utfsm.cl/~yanez/a...mple-programs/ "microphone"

For several reasons, not least the slavish attention to debugging potential.

Again, this may have limited uses for the normal folken but give the README a browse.

Basically spits out to STDIO again, but with lot's of potential parameters, handy for testing.

I made it good for the kindle and managed to remove most of the style warnings.
It's not built optimised.

the entire code (It's lengthy)
Spoiler:

Code:
/*************************************************************************************
This sample program was made on Mar 2005 by:

Aquiles Yáńez C.

Under the design guidance of:

Agustin González V.

Edited by twobob to work on the kindle

This one grabs from the mic and spits out to stdout like:

./microphone -d hw:0 -r 16000 -c2 -m 0  > ouput.wav

*************************************************************************************/
//Needed libraries
#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
#include <getopt.h>

//Enum needed to choose the type of I/O loop
typedef enum {
    METHOD_DIRECT_RW,   //method with direct use of read/write functions
    METHOD_DIRECT_MMAP, //method with direct use of memory mapping
    METHOD_ASYNC_MMAP,  //method with async use of memory mapping
    METHOD_ASYNC_RW,    //method with async use of read/write functions
    METHOD_RW_AND_POLL, //method with use of read/write functions and pool
    METHOD_DIRECT_RW_NI //method with direct use of read/write and noninterleaved format (not implemented)
} enum_io_method;

//struct that defines one I/O method
struct io_method {
    enum_io_method method;   //I/O loop type
    snd_pcm_access_t access; //PCM access type
    int open_mode;           //open function flags
};

//array of the available I/O methods defined for capture
static struct io_method methods[] = {
    { METHOD_DIRECT_RW, SND_PCM_ACCESS_RW_INTERLEAVED, 0 },
    { METHOD_DIRECT_MMAP, SND_PCM_ACCESS_MMAP_INTERLEAVED, 0 },
    { METHOD_ASYNC_RW,SND_PCM_ACCESS_RW_INTERLEAVED, 0 },
    { METHOD_ASYNC_MMAP,SND_PCM_ACCESS_MMAP_INTERLEAVED, 0 },
    { METHOD_RW_AND_POLL,SND_PCM_ACCESS_RW_INTERLEAVED, 0 },
    { METHOD_DIRECT_RW_NI,SND_PCM_ACCESS_RW_NONINTERLEAVED, 0 }//not implemented
    //SND_PCM_ACCESS_RW_NONINTERLEAVED not supported by the most kind of cards
};

//general configuration parameters of the device
struct device_parameters {
    snd_pcm_sframes_t buffer_size;      //buffer size in frames
    snd_pcm_sframes_t period_size;      //period size in frames
    unsigned int buffer_time;           //length of the circular buffer in usec
    unsigned int period_time;           //length of one period in usec
    int n_channels;                     //number of channels
    unsigned int sample_rate;           //frame rate
    snd_pcm_format_t sample_format;     //format of the samples
    snd_pcm_access_t access_type;       //PCM access type
};




//recovery callback in case of error
static int xrun_recovery(snd_pcm_t *handle, int error)
{
    switch(error)
    {
        case -EPIPE:    // Buffer Over-run
            fprintf(stderr,"microphone: \"Buffer Overrun\" \n");
            if ((error = snd_pcm_prepare(handle))< 0)
                fprintf(stderr,"microphone: Buffer overrrun cannot be recovered, snd_pcm_prepare fail: %s\n", snd_strerror(error));
            return 0;
            break;

        case -ESTRPIPE: //suspend event occurred
            fprintf(stderr,"microphone: Error ESTRPIPE\n");
			//EAGAIN means that the request cannot be processed immediately
            while ((error = snd_pcm_resume(handle)) == -EAGAIN)
                sleep(1);// wait until the suspend flag is clear

            if (error < 0) // error case
            {
                if ((error = snd_pcm_prepare(handle)) < 0)
                    fprintf(stderr,"microphone: Suspend cannot be recovered, snd_pcm_prepare fail: %s\n", snd_strerror(error));
            }
            return 0;
            break;

        case -EBADFD://Error PCM descriptor is wrong
            fprintf(stderr,"microphone: Error EBADFD\n");
            break;

        default:
            fprintf(stderr,"microphone: Error unknown, error = %d\n",error);
            break;
    }
    return error;
}



//shows the help when is needed
static void help(void)
{
    int k;
    printf(
"Usage: microphone [OPTIONS]\n"
"-h,--help      show this usage help\n"
"-d,--device    device of capture\n"
"-r,--rate      sample rate in hz\n"
"-c,--channels  channels number\n"
"-m,--method    I/O method\n"
"-p,--period    period size in samples\n"
"-b,--buffer    circular buffer size in samples\n"
"\n");
    printf("The I/O methods are:\n");
    printf("(0) DIRECT_RW\n");
    printf("(1) DIRECT_MMAP\n");
    printf("(2) ASYNC_RW\n");
    printf("(3) ASYNC_MMAP\n");
    printf("(4) RW_AND_POLL\n");
}

/*******************************************************************************/
/********************* case: direct with r/w functions *************************/
/*******************************************************************************/
//This case only uses a main loop
static int direct_rw(snd_pcm_t *device, struct device_parameters cap_dev_params)
{
    int error;
    snd_pcm_sframes_t period_size = cap_dev_params.period_size;
    int n_channels = cap_dev_params.n_channels;
    short buf[n_channels*period_size];//audio samples buffer
    signed short *ptr;
    int cptr;

    while(1)
    {
        ptr = buf; //aux pointer in buff
        cptr = period_size; //aux ptr needed to calculate remained frames
        //in the most cases readi only uses one bucle
        while(cptr > 0) {//wait until buf is full with period_size frames
            error = snd_pcm_readi (device, ptr, cptr);
            if (error < 0)
            {
                if (xrun_recovery(device, error) < 0) {
                    fprintf(stderr,"microphone: Write error: %s\n", snd_strerror(error));
                    exit(EXIT_FAILURE);
                }
                break; //discard current period
            }
            ptr += error * n_channels;
            cptr -= error;
        }
        //write to standard output

        int looper = 2;

        while (looper){
        looper--;





        write(STDOUT_FILENO, buf, sizeof(short)*period_size*n_channels);



        }

    }
}

/*******************************************************************************/
/********************** case: direct with memory mapping ***********************/
/*******************************************************************************/
//this case also uses one main loop
static int direct_mmap(snd_pcm_t *device, struct device_parameters cap_dev_params)
{
    int error, state;
    snd_pcm_sframes_t period_size = cap_dev_params.period_size;//period size in frames
    int n_channels = cap_dev_params.n_channels;//number of channels
    const snd_pcm_channel_area_t *my_areas;//mapped memory area info
    snd_pcm_uframes_t offset, frames, size;//aux for frames count
    snd_pcm_sframes_t avail, commitres;//aux for frames count
    int first=1; //first == 1  => first period of the stream is processed now

    while(1) //main loop
    {
        state = snd_pcm_state(device); //needed for descriptor check
        switch(state)
        {
            case SND_PCM_STATE_XRUN://buffer over-run
                //fprintf(stderr,"microphone: SND_PCM_STATE_XRUN\n");
                if ((error = xrun_recovery(device, -EPIPE)) < 0)
                {
                    fprintf(stderr,"microphone: XRUN recovery failed: %s\n", snd_strerror(error));
                    return error;
                }
				//stream is restarted
                first = 1;
                break;

            case SND_PCM_STATE_SUSPENDED://PCM is suspended
            //fprintf(stderr,"microphone: SND_PCM_STATE_SUSPENDED\n");
                if ((error = xrun_recovery(device, -ESTRPIPE)) < 0)
                {
                    fprintf(stderr,"microphone: SUSPEND recovery failed: %s\n", snd_strerror(error));
                    return error;
                }
                break;
        }

		//checks how many frames are ready to read or write
        avail = snd_pcm_avail_update(device);
        if (avail < 0)
        {
            if ((error = xrun_recovery(device, avail)) < 0) {
                fprintf(stderr,"microphone: SUSPEND recovery failed: %s\n", snd_strerror(error));
                return error;
            }
            first = 1;
            continue;
        }
        if (avail < period_size)//checks if one period is ready to process
        {
            switch(first)
            {
                case 1:
					//if the capture from PCM is started (first=1) and one period is ready to process,
					//the stream must start
                    first = 0;
                    if ((error = snd_pcm_start(device)) < 0) {
                        fprintf(stderr,"microphone: Start error: %s\n", snd_strerror(error));
                        exit(EXIT_FAILURE);
                    }
                    break;

                case 0:
                    //wait for pcm to become ready
                    if ((error = snd_pcm_wait(device, -1))< 0) {
                        if ((error = xrun_recovery(device, error)) < 0) {
                            fprintf(stderr,"microphone: snd_pcm_wait error: %s\n", snd_strerror(error));
                            exit(EXIT_FAILURE);
                        }
                        first = 1;
                    }
                 //   break; ??? This seems to be missing?
            }
            continue;
        }
        size = period_size;
        while (size > 0) //wait until we have period_size frames (in the most cases only one loop is needed)
        {
            frames = size;//expected frames number to be processed
			//frames is a bidirectional variable, this means that the real number of frames processed is written
			//to this variable by the function.
            if ((error = snd_pcm_mmap_begin (device, &my_areas, &offset, &frames)) < 0) {
                if ((error = xrun_recovery(device, error)) < 0) {
                    fprintf(stderr,"microphone: MMAP begin avail error: %s\n", snd_strerror(error));
                    exit(EXIT_FAILURE);
                }
                first = 1;
            }
            //write to standard output
            write(STDOUT_FILENO, (my_areas[0].addr)+(offset*sizeof(short)*n_channels), frames*sizeof(short)*n_channels);
            commitres = snd_pcm_mmap_commit(device, offset, frames);
            if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) {
                if ((error = xrun_recovery(device, commitres >= 0 ? commitres : -EPIPE)) < 0) {
                    fprintf(stderr,"microphone: MMAP commit error: %s\n", snd_strerror(error));
                    exit(EXIT_FAILURE);
                }
                first = 1;
            }
            size -= frames;//needed in the condition of the while loop to check if period is filled
        }
    }
}

/*****************************************************************************************/
/*********************  Case: Async with read/write functions  ***************************/
/*****************************************************************************************/
//this case uses a sync callback and the read function
struct async_private_data {
        int n_channels;
        snd_pcm_sframes_t period_size;
};

//In every call to async_rw_callback one period is processed

//Size of one period (bytes) = n_channels * sizeof(short) * period_size
void async_rw_callback(snd_async_handler_t *ahandler)
{
    int error;
    snd_pcm_t *device = snd_async_handler_get_pcm(ahandler);
    struct async_private_data *data = snd_async_handler_get_callback_private(ahandler);
    int n_channels = data->n_channels;
    snd_pcm_sframes_t period_size = data->period_size;
    short buf[n_channels*period_size];//audio frames buffer
    signed short *ptr;
    int cptr;

    ptr = buf; //aux pointer in buff
    cptr = period_size; //indicates how many frames are still unread

	//in the most cases one loop is enough to fill the buffer
    while(cptr > 0) {//wait until the buffer have period_size frames
		//trying to read one entire period
        if ((error = snd_pcm_readi (device, ptr, cptr)) < 0)
        {
            if (xrun_recovery(device, error) < 0) {
                fprintf(stderr,"microphone: Write error: %s\n", snd_strerror(error));
                exit(EXIT_FAILURE);
            }
            continue;
        }
        ptr += error * n_channels;
        cptr -= error;
    }
    //writing to standard output
    write(STDOUT_FILENO, buf, sizeof(short)*period_size*n_channels);
}

void async_rw(snd_pcm_t *device, struct device_parameters cap_dev_params)
{
    snd_pcm_sframes_t period_size = cap_dev_params.period_size;
    int n_channels = cap_dev_params.n_channels;
    struct async_private_data data;//private data passed to the async routine
    data.n_channels = n_channels;
    data.period_size = cap_dev_params.period_size;//period size
    snd_async_handler_t *ahandler;//async handler
    int error;
    short buf[n_channels*period_size];//audio frames buffer

	//adding async handler for PCM with private data data and callback async_rw_callback
    if ((error = snd_async_add_pcm_handler(&ahandler, device, async_rw_callback, &data)) < 0)
    {
        fprintf(stderr,"microphone: Unable to register async handler\n");
        exit(EXIT_FAILURE);
    }
	//async_rw_callback is called every time that the period is filled

	//starting to read data from PCM
    if ((error = snd_pcm_readi (device, buf, period_size)) < 0)
    {
        if (xrun_recovery(device, error)) {
            fprintf(stderr,"microphone: Write error: %s\n", snd_strerror(error));
            exit(EXIT_FAILURE);
        }
    }

	//writing one period to standard output
    write(STDOUT_FILENO, buf, sizeof(short)*period_size*n_channels);

	//the remainder work is made by the handler and the callback
    while (1) {
        sleep(1);
    }
}

/*******************************************************************************/
/************************ case async with memory mapping ***********************/
/*******************************************************************************/
//This case uses an async callback and memory mapping
void async_mmap_callback(snd_async_handler_t *ahandler)//async callback
{
    int error, state;
    snd_pcm_t *device = snd_async_handler_get_pcm(ahandler);
    struct async_private_data *data = snd_async_handler_get_callback_private(ahandler);
    int n_channels = data->n_channels;
    snd_pcm_sframes_t period_size = data->period_size;
    const snd_pcm_channel_area_t *my_areas;//memory area info
    snd_pcm_uframes_t offset, frames, size;
    snd_pcm_sframes_t avail, commitres;
    int first = 0;

    while(1)
    {
        state = snd_pcm_state(device);//checks the PCM descriptor state
        switch(state)
        {
            case SND_PCM_STATE_XRUN://checks if the buffer is in a wrong state
                //fprintf(stderr,"microphone: SND_PCM_STATE_XRUN\n");
                if ((error = xrun_recovery(device, -EPIPE)) < 0)
                {
                    fprintf(stderr,"microphone: XRUN recovery failed: %s\n", snd_strerror(error));
                    exit(EXIT_FAILURE);
                }
                first = 1;
                break;

            case SND_PCM_STATE_SUSPENDED://checks if PCM is in a suspend state
                //fprintf(stderr,"microphone: SND_PCM_STATE_SUSPENDED\n");
                if ((error = xrun_recovery(device, -ESTRPIPE)) < 0)
                {
                    fprintf(stderr,"microphone: SUSPEND recovery failed: %s\n", snd_strerror(error));
                    exit(EXIT_FAILURE);
                }
                break;
        }
        avail = snd_pcm_avail_update(device);
        if (avail < 0) //error
        {
            if ((error = xrun_recovery(device, avail)) < 0) {
                fprintf(stderr,"microphone: Recovery fail: %s\n", snd_strerror(error));
                exit(error);
            }
            first = 1;
            continue;
        }
        if (avail < period_size)
        {
            switch(first)
            {
                case 1://initializing PCM
                    fprintf(stderr,"microphone: Restarting PCM \n");
                    first = 0;
                    if ((error = snd_pcm_start(device)) < 0) {
                        fprintf(stderr,"microphone: Start error: %s\n", snd_strerror(error));
                        exit(EXIT_FAILURE);
                    }
                    break;

                case 0:
                    return;
            }
            continue;//we don't have enough data for one period
        }
        size = period_size;
        while (size > 0)//wait until we have period_size frames
        {
            frames = size;//expected frames number to be processed
			//frames is a bidirectional variable, that means the real number of frames processed is written
			//to this variable by the function.

			//sending request for the start of the data reading by the application
            if ((error = snd_pcm_mmap_begin (device, &my_areas, &offset, &frames)) < 0) {
                if ((error = xrun_recovery(device, error)) < 0) {
                    fprintf(stderr,"microphone: MMAP begin avail error: %s\n", snd_strerror(error));
                    exit(EXIT_FAILURE);
                }
                first = 1;
            }
            //writing data to standard output
            write(STDOUT_FILENO, (my_areas[0].addr)+(offset*sizeof(short)*n_channels), frames*sizeof(short)*n_channels);

			//sending signal for the end of the data reading by the application
            commitres = snd_pcm_mmap_commit(device, offset, frames);
            if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) {
                if ((error = xrun_recovery(device, commitres >= 0 ? commitres : -EPIPE)) < 0) {
                    fprintf(stderr,"microphone: MMAP commit error: %s\n", snd_strerror(error));
                    exit(EXIT_FAILURE);
                }
                first = 1;
            }
            size -= frames;//needed for the condition of the while loop (size == 0 means end of reading)
        }
    }
}

void async_mmap(snd_pcm_t *device, struct device_parameters cap_dev_params)
{
    snd_async_handler_t *ahandler;// async handler
    struct async_private_data data;// private data passed to the async callback
    snd_pcm_sframes_t period_size = cap_dev_params.period_size;
    int n_channels = cap_dev_params.n_channels;
    data.n_channels = n_channels;
    data.period_size = cap_dev_params.period_size;
    int error;
    snd_pcm_uframes_t offset, frames;
    snd_pcm_sframes_t avail, commitres;
    const snd_pcm_channel_area_t *my_areas;//memory area info

	//adding async handler for PCM
    if ((error = snd_async_add_pcm_handler(&ahandler, device, async_mmap_callback, &data)) < 0)
    {
        fprintf(stderr,"microphone: Unable to register async handler\n");
        exit(EXIT_FAILURE);
    }
    //starting PCM
    if ((error = snd_pcm_start(device)) < 0) {
        fprintf (stderr, "microphone: Unable to start PCM (%s)\n",snd_strerror (error));
        exit (1);
    }

	//sending request for the start of the data reading by the application
    if ((error = snd_pcm_mmap_begin (device, &my_areas, &offset, &frames))<0) {
        fprintf (stderr, "microphone: Memory mapping cannot be started (%s)\n",snd_strerror (error));
        exit (1);
    }

	//writing data to standard output
    write(STDOUT_FILENO, (my_areas[0].addr)+(offset*sizeof(short)*n_channels), frames*sizeof(short)*n_channels);
	//sending signal for the end of the data reading by the application
    commitres = snd_pcm_mmap_commit(device, offset, frames);
            if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) {
                if ((error = xrun_recovery(device, commitres >= 0 ? commitres : -EPIPE)) < 0) {
                    fprintf(stderr,"microphone: MMAP commit error: %s\n", snd_strerror(error));
                    exit(EXIT_FAILURE);
                }
            }
	//the remainder work is made by the handler and the callback
    while (1) {
        sleep(1);
    }
}

/*******************************************************************************/
/************************ case: async with poll function ***********************/
/*******************************************************************************/
//uses the pool function to read the data when is available

//waits for data in buffer using poll
static int wait_for_poll(snd_pcm_t *device, struct pollfd *ufds, unsigned int count)
{
    unsigned short revents;

    while (1) {
        //checking file descriptors activity
        poll(ufds, count, -1);//-1 means block
        //processing data returned by pool
        snd_pcm_poll_descriptors_revents(device, ufds, count, &revents);
        if (revents & POLLERR)
            return -EIO;
        if (revents & POLLIN)//we have data waiting for read
            return 0;
        }
}

static int rw_and_poll_loop(snd_pcm_t *device, struct device_parameters cap_dev_params)
{
    int count;
    int error;
    struct pollfd *ufds;//file descriptor array used by pool
    snd_pcm_sframes_t period_size = cap_dev_params.period_size;
    int n_channels = cap_dev_params.n_channels;
    signed short *ptr;//pointer in buffer
    int cptr;//captured frames counter
    short buf[n_channels*period_size];//audio frames buffer
    int init;
    count = snd_pcm_poll_descriptors_count (device);
    if (count <= 0) {
                fprintf(stderr,"microphone: Invalid poll descriptors count\n");
                return count;
    }
    ufds = malloc(sizeof(struct pollfd) * count);
    if ((error = snd_pcm_poll_descriptors(device, ufds, count)) < 0) {
                fprintf(stderr,"microphone: Unable to obtain poll descriptors for capture: %s\n", snd_strerror(error));
                return error;
    }
    init = 1;
    while(1)
    {
        if (!init)
        {
            error = wait_for_poll(device, ufds, count);
            if (error < 0) { //try to recover from error
                if (snd_pcm_state(device) == SND_PCM_STATE_XRUN ||snd_pcm_state(device) == SND_PCM_STATE_SUSPENDED)
                {
                    error = snd_pcm_state(device) == SND_PCM_STATE_XRUN ? -EPIPE : -ESTRPIPE;
                    if (xrun_recovery(device, error) < 0) {
                        fprintf(stderr,"microphone: Write error: %s\n", snd_strerror(error));
                        exit(EXIT_FAILURE);
                    }
                    init = 1;
                } else {
                    fprintf(stderr,"microphone: Wait for poll failed\n");
                    return error;
                }
            }
        }
        ptr = buf;
        cptr = period_size;

        while(cptr > 0) //waits until buff is filled with period_size frames
        {
            if ((error = snd_pcm_readi (device, buf, period_size)) < 0)
            {
                if (xrun_recovery(device, error)) {
                    fprintf(stderr,"microphone: Write error: %s\n", snd_strerror(error));
                    exit(EXIT_FAILURE);
                }
                init=1;
                continue;
            }
            if (snd_pcm_state(device) == SND_PCM_STATE_RUNNING)
                init = 0;
            ptr += error * n_channels;
            cptr -= error;
            if (cptr == 0)//exits if the read of the period is done
                break;

			//if period is not totally filled, another while loop is needed
            error = wait_for_poll(device, ufds, count);
            if (error < 0) { //tries to recover from error
                if (snd_pcm_state(device) == SND_PCM_STATE_XRUN ||snd_pcm_state(device) == SND_PCM_STATE_SUSPENDED)
                {
                    error = snd_pcm_state(device) == SND_PCM_STATE_XRUN ? -EPIPE : -ESTRPIPE;
                    if (xrun_recovery(device, error) < 0) {
                        fprintf(stderr,"microphone: Write error: %s\n", snd_strerror(error));
                        exit(EXIT_FAILURE);
                    }
                    init = 1;
                } else {
                    fprintf(stderr,"microphone: Wait for poll failed\n");
                    return error;
                }
            }
        }
        //writes to standard output
        write(STDOUT_FILENO, buf, sizeof(short)*period_size*n_channels);
    }
}

/*****************************************************************************************/
/********************************** main function ****************************************/
/*****************************************************************************************/
int main (int argc, char *argv[])
{

    int error,dir;
    unsigned int sample_rate = 48000;//expected frame rate
    unsigned int real_sample_rate;//real frame rate
    int n_channels = 2;//expected number of channels
    unsigned int real_n_channels;//real number of channels
    char * device_name = "hw:0,0";
    snd_pcm_t *device;//capture device
    snd_pcm_hw_params_t *hw_params;//hardware configuration structure
    int access = 1;
    snd_pcm_sframes_t buffer_size = 2048;//expected buffer size in frames
    snd_pcm_uframes_t period_size = 8;//expected period size in frames
    snd_pcm_sframes_t real_buffer_size;//real buffer size in frames
    snd_pcm_uframes_t real_period_size;//real period size in frames
    unsigned int buffer_time;//length of the circular buffer in us
    unsigned int period_time;//length of one period in us
    snd_pcm_format_t real_sample_format;
    snd_pcm_access_t real_access;
	struct device_parameters capture_device_params;
    struct option long_option[] =
    {

        {"device", 1, NULL, 'd'},
        {"rate", 1, NULL, 'r'},
        {"channels", 1, NULL, 'c'},
        {"method", 1, NULL, 'm'},
        {"buffer", 1, NULL, 'b'},
        {"period", 1, NULL, 'p'},
        {"help", 0, NULL, 'h'},
        {NULL, 0, NULL, 0},
    };//needed for getopt_long

 /************************** processing command line parameters ******************************/
    while (1) {
        int c;
        if ((c = getopt_long(argc, argv, "d:r:c:m:b:p:h", long_option, NULL)) < 0)
            break;
        switch (c)
        {
            case 'd':
                device_name = strdup(optarg);
                break;
            case 'r':
                sample_rate = atoi(optarg);
                sample_rate = sample_rate < 4000 ? 4000 : sample_rate;
                sample_rate = sample_rate > 196000 ? 196000 : sample_rate;
                break;
            case 'c':
                n_channels = atoi(optarg);
                n_channels = n_channels < 1 ? 1 : n_channels;
                n_channels = n_channels > 1024 ? 1024 : n_channels;
                break;
            case 'm':
                access = atoi(optarg);
                break;
            case 'b':
                buffer_size = atoi(optarg);
                break;
                        //  buffer_time(us) = 0.001 * buffer_size(frames) / rate(khz)
            case 'p':
                period_size = atoi(optarg);
                break;
            case 'h':
                help();
                exit(1);
                break;
        }
    }

/************************************** opens the device *****************************************/

    if ((error = snd_pcm_open (&device, device_name, SND_PCM_STREAM_CAPTURE, methods[access].open_mode)) < 0) {
        fprintf (stderr, "microphone: Device cannot be opened  %s (%s)\n",
             argv[1],
             snd_strerror (error));
        exit (1);
    }
    fprintf (stderr, "microphone: Device: %s open_mode = %d\n", device_name, methods[access].open_mode);

	//allocating the hardware configuration structure
    if ((error = snd_pcm_hw_params_malloc (&hw_params)) < 0) {
        fprintf (stderr, "microphone: Hardware configuration structure cannot be allocated (%s)\n",
             snd_strerror (error));
        exit (1);
    }

    //assigning the hw configuration structure to the device
    if ((error = snd_pcm_hw_params_any (device, hw_params)) < 0) {
        fprintf (stderr, "microphone: Hardware configuration structure cannot be assigned to device (%s)\n",
             snd_strerror (error));
        exit (1);
    }

/*********************************** shows the audio capture method ****************************/

    switch(methods[access].method)
    {
        case METHOD_DIRECT_RW:
            fprintf (stderr, "microphone: capture method: METHOD_DIRECT_RW (m = 0) \n");
            break;
        case METHOD_DIRECT_MMAP:
            fprintf (stderr, "microphone: capture method: METHOD_DIRECT_MMAP (m = 1)\n");
            break;
        case METHOD_ASYNC_MMAP:
            fprintf (stderr, "microphone: capture method: METHOD_ASYNC_MMAP (m = 2)\n");
            break;
        case METHOD_ASYNC_RW:
            fprintf (stderr, "microphone: capture method: METHOD_ASYNC_RW (m = 3)\n");
            break;
        case METHOD_RW_AND_POLL:
            fprintf (stderr, "microphone: capture method: METHOD_RW_AND_POLL (m = 4)\n");
            break;
        case METHOD_DIRECT_RW_NI://not implemented
            fprintf (stderr, "microphone: capture method: METHOD_DIRECT_RW_NI (m = 5)\n");
            break;
    }

/*************************** configures access method ******************************************/
    //sets the configuration method
    fprintf (stderr, "microphone: Access method: %d\n",methods[access].access);
    if ((error = snd_pcm_hw_params_set_access (device, hw_params, methods[access].access)) < 0) {
        fprintf (stderr, "microphone: Access method cannot be configured (%s)\n",
             snd_strerror (error));
        exit (1);
    }
    //checks the access method
    if ((error = snd_pcm_hw_params_get_access (hw_params, &real_access)) < 0) {
        fprintf (stderr, "microphone: Access method cannot be obtained (%s)\n",
             snd_strerror (error));
        exit (1);
    }
    //shows the access method
    switch(real_access)
    {
    case SND_PCM_ACCESS_MMAP_INTERLEAVED:
        fprintf (stderr, "microphone: PCM access method: SND_PCM_ACCESS_MMAP_INTERLEAVED \n");
        break;
    case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
        fprintf (stderr, "microphone: PCM access method: SND_PCM_ACCESS_MMAP_NONINTERLEAVED \n");
        break;
    case SND_PCM_ACCESS_MMAP_COMPLEX:
        fprintf (stderr, "microphone: PCM access method: SND_PCM_ACCESS_MMAP_COMPLEX \n");
        break;
    case SND_PCM_ACCESS_RW_INTERLEAVED:
        fprintf (stderr, "microphone: PCM access method: SND_PCM_ACCESS_RW_INTERLEAVED \n");
        break;
    case SND_PCM_ACCESS_RW_NONINTERLEAVED:
        fprintf (stderr, "microphone: PCM access method: SND_PCM_ACCESS_RW_NONINTERLEAVED \n");
        break;
    }
/****************************  configures the capture format *******************************/
    //SND_PCM_FORMAT_S16_LE => 16 bit signed little endian
    if ((error = snd_pcm_hw_params_set_format (device, hw_params, SND_PCM_FORMAT_S16_LE)) < 0)
    {
        fprintf (stderr, "microphone: Capture format cannot be configured (%s)\n",
             snd_strerror (error));
        exit (1);
    }
    //checks capture format
    if ((error = snd_pcm_hw_params_get_format (hw_params, &real_sample_format)) < 0)
    {
        fprintf (stderr, "microphone: Capture sample format cannot be obtained (%s)\n",
             snd_strerror (error));
        exit (1);
    }
    //just shows the capture format in a human readable way
    switch(real_sample_format)
    {
    case SND_PCM_FORMAT_S16_LE:
        fprintf (stderr, "microphone: PCM capture sample format: SND_PCM_FORMAT_S16_LE \n");
        break;
    default:
        fprintf (stderr, "microphone: PCM capture sample format = %d\n", real_sample_format);
        break;
    }
/*************************** configures the sample rate  ***************************/
	//sets the sample rate
    if ((error = snd_pcm_hw_params_set_rate (device, hw_params, sample_rate, 0)) < 0) {
        fprintf (stderr, "microphone: Sample rate cannot be configured (%s)\n",
             snd_strerror (error));
        exit (1);
    }
    //checks sample rate
    if ((error = snd_pcm_hw_params_get_rate (hw_params, &real_sample_rate, 0)) < 0) {
        fprintf (stderr, "microphone: Sample rate cannot be obtained (%s)\n",
             snd_strerror (error));
        exit (1);
    }
    fprintf (stderr, "microphone: Sample_rate_real = %d\n", real_sample_rate);

/**************************** configures the number of channels ********************************/
    //sets the number of channels
    if ((error = snd_pcm_hw_params_set_channels (device, hw_params, n_channels)) < 0) {
        fprintf (stderr, "microphone: Number of channels cannot be configured (%s)\n",
             snd_strerror (error));
        exit (1);
    }
    //checks the number of channels
    if ((error = snd_pcm_hw_params_get_channels (hw_params,& real_n_channels)) < 0) {
        fprintf (stderr, "microphone: Number of channels cannot be obtained (%s)\n",
             snd_strerror (error));
        exit (1);
    }
    fprintf (stderr, "microphone: real_n_channels = %d\n", real_n_channels);

/***************************** configures the buffer size *************************************/
    //sets the buffer size
    if ((error = snd_pcm_hw_params_set_buffer_size(device, hw_params, buffer_size)) < 0) {
        fprintf (stderr, "microphone: Buffer size cannot be configured (%s)\n",
             snd_strerror (error));
        exit (1);
    }
    //checks the value of the buffer size
    if ((error = snd_pcm_hw_params_get_buffer_size(hw_params, &real_buffer_size)) < 0) {
    fprintf (stderr, "microphone: Buffer size cannot be obtained (%s)\n",
             snd_strerror (error));
        exit (1);
    }
    fprintf (stderr, "microphone: Buffer size = %d [frames]\n", (int)real_buffer_size);
/***************************** configures period size *************************************/
    dir=0; //dir=0  =>  period size must be equal to period_size
    //sets the period size
 //   if ((error = snd_pcm_hw_params_set_period_size(device, hw_params, period_size, dir)) < 0) {
 //       fprintf (stderr, "microphone: Period size cannot be configured (%s)\n",
 //            snd_strerror (error));
 //       exit (1);
 //   }
    //checks the value of period size
//    if ((error = snd_pcm_hw_params_get_period_size(hw_params, &real_period_size, &dir)) < 0) {
 //   fprintf (stderr, "microphone: Period size cannot be obtained (%s)\n",
 //            snd_strerror (error));
 //       exit (1);
//    }
 //   fprintf (stderr, "microphone: Period size = %d [frames]\n", (int)real_period_size);
/************************* applies the hardware configuration  ******************************/

    if ((error = snd_pcm_hw_params (device, hw_params)) < 0) {
        fprintf (stderr, "microphone: Hardware parameters cannot be configured (%s)\n",
             snd_strerror (error));
        exit (1);
    }
    //frees the structure of hardware configuration
    snd_pcm_hw_params_free (hw_params);

/*********************************** filling capture_device_params *************************************/

    capture_device_params.access_type = real_access;           //real access method
    capture_device_params.buffer_size = real_buffer_size;      //real buffer size
  //  capture_device_params.period_size = real_period_size;      //real period size
    capture_device_params.buffer_time = buffer_time;           //real buffer time
    capture_device_params.period_time = period_time;           //real period time
    capture_device_params.sample_format = real_sample_format;  //real samples format
    capture_device_params.sample_rate = real_sample_rate;      //real sample rate
    capture_device_params.n_channels = n_channels;             //real number of channels

/********************** selects the appropriate access method for audio capture *******************/

	switch(methods[access].method)
	{
		case METHOD_DIRECT_RW:
			direct_rw(device, capture_device_params);
		break;

		case METHOD_DIRECT_MMAP:
			direct_mmap(device, capture_device_params);
		break;

		case METHOD_ASYNC_MMAP:
			async_mmap(device, capture_device_params);
		break;

		case METHOD_ASYNC_RW:
			async_rw(device, capture_device_params);
		break;

		case METHOD_RW_AND_POLL:
			rw_and_poll_loop(device, capture_device_params);
		break;

		case METHOD_DIRECT_RW_NI://not implemented
			//direct_rw_ni(dev_captura, parm_dev_captura);
			fprintf (stderr, "microphone: Method not supported\n");
		break;
	}


    // Given the programs usage this almost never happens neatly
    fprintf (stderr, "microphone: BYE BYE\n");
    //closes the device
    snd_pcm_close (device);

    exit (0);
}


To get around the horrific kernal module crash that appears to rear it's head during piping I currently call my stuff like:

(WITH NO LINE BREAKS OBVIOUSLY, I have fixed them up here with \ for visual clarity but you can just call it one one line)

Code:
 [root@kindle sound]# aplay -D dmix0 ImNotReal.wav;\
 ./microphone -d hw:0 -r 16000 -c2 | aplay -f cd -D hw:0 -r 16000 
Where the aplay -D dmix0 ImNotReal.wav; is a throwaway call to a non-existant file (dev/null didn't seem so robust)

To be clear once again: This extra initial call it to get around a nasty bug that makes the full-duplex hang.

This complete call gives us:

BE READY TO TURN THE VOLUME DOWN


Code:
[root@kindle sound]# aplay -D dmix0 ImNotReal.wav; ./microphone -d hw:0 -r 16000 -c2 | aplay -f cd -D hw:0 -r 16000 

ImNotReal.wav: No such file or directory
microphone: Device: hw:0 open_mode = 0
microphone: capture method: METHOD_DIRECT_MMAP (m = 1)
microphone: Access method: 0
microphone: PCM access method: SND_PCM_ACCESS_MMAP_INTERLEAVED 
microphone: PCM capture sample format: SND_PCM_FORMAT_S16_LE 
microphone: Sample_rate_real = 16000
microphone: real_n_channels = 2
microphone: Buffer size = 2048 [frames]
Playing raw data 'stdin' : Signed 16 bit Little Endian, Rate 16000 Hz, Stereo
And a very cool little echo machine!

But more importantly you can play with the -d,-r and other things you can read about in the README to test capabilities of the device.

At the very least this is an exercise in very complete code.
As with all my stuff - simply copy it to the device and run it via SSH.
The top post covers this in detail I believe.
Uninstall - Just delete the file.
Attached Files
File Type: gz microphone-FIXED-HW-VERSION.tar.gz (703.6 KB, 460 views)

Last edited by twobob; 07-12-2012 at 11:01 PM. Reason: tidied up the calls. added d/l - added standard install instructions - added point of throwaway call
twobob is offline   Reply With Quote