Register Guidelines E-Books Today's Posts Search

Go Back   MobileRead Forums > E-Book Readers > Amazon Kindle > Kindle Developer's Corner

Notices

Reply
 
Thread Tools Search this Thread
Old 07-09-2012, 11:58 AM   #46
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: 6299991
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
snd_pcm_hw_params_set_rate_near not supported

THIS PAGE CONTAINS A COUPLE OF WORKING AUDIO DEMOS, TECHNICAL WAVE DETAILS AND FURTHER ALSA INTIALISATION DETAILS.

===============================================
Threads continue:

Code:
/*    CAUSES SEGGIE
 *
if ((err = snd_pcm_hw_params_set_rate_near 
        (capture_handle, hw_params, 48000, 0)) < 0) {
	fprintf (stderr, "cannot set sample rate (%s)\n",
	 snd_strerror (err));
	exit (1);
}


*/
so either approximate yourself or go for the known hw rates [re-Listed here from previous post]

Device: hw (type: HW)
Access types: MMAP_INTERLEAVED RW_INTERLEAVED
Formats: S16_LE S24_LE S20_3LE
Channels: 2
Sample rates: 8000 11025 16000 22050 32000 44100 48000
Interrupt interval: 166-512000 us
Buffer size: 333-2048000 us

Making the right choices should be trivial since we have known hardware
(This assumes that all kindles with sound support will support the same alsa rates,
This is untested on anything but a 3.)

Instead use snd_pcm_hw_params_set_rate

For example

Code:
if ((err = snd_pcm_hw_params_set_rate 
                (capture_handle, hw_params, 48000, 0)) < 0) {
		fprintf (stderr, "cannot set sample rate (%s)\n",
		 snd_strerror (err));
		exit (1);
		}
should be fine

Last edited by twobob; 07-11-2012 at 05:55 PM. Reason: added solution - added page precis
twobob is offline   Reply With Quote
Old 07-09-2012, 12:28 PM   #47
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: 6299991
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
just enough to get you going with mic input.

Here is a simple test program that pulls some data in from the mic and splats it to stderr.

By itself it is, again, almost pointless and feature free but it's enough to get you going with access to mic data.

Spoiler:

Code:
	#include <stdio.h>
	#include <stdlib.h>
	#include <alsa/asoundlib.h>

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

		//
		int i;
		int err;


		// Just a tiny bit of space
		short buf[128];

		// and an index
		int counter = sizeof(buf);


		// The card
		snd_pcm_t *capture_handle;

		// the cards params
		snd_pcm_hw_params_t *hw_params;


		// This takes the device as the first argument for testing purposes.
		//hw is a sensible choice here.

		if ((err = snd_pcm_open (&capture_handle, argv[1], SND_PCM_STREAM_CAPTURE, 0)) < 0) {
			fprintf (stderr, "cannot open audio device %s (%s)\n",
				 argv[1],
				 snd_strerror (err));
			exit (1);
		}

		// make a little space
		if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) {
			fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n",
				 snd_strerror (err));
			exit (1);
		}

		// set it up
		if ((err = snd_pcm_hw_params_any (capture_handle, hw_params)) < 0) {
			fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n",
				 snd_strerror (err));
			exit (1);
		}


		// go for the INTERLEAVED type of access.
		if ((err = snd_pcm_hw_params_set_access (capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
			fprintf (stderr, "cannot set access type (%s)\n",
				 snd_strerror (err));
			exit (1);
		}

		// Signed 16 Bit Little Endian sampling
		if ((err = snd_pcm_hw_params_set_format (capture_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) {
			fprintf (stderr, "cannot set sample format (%s)\n",
				 snd_strerror (err));
			exit (1);
		}

		// We are going to hard set this to 48k for now
		if ((err = snd_pcm_hw_params_set_rate (capture_handle, hw_params, 48000, 0)) < 0) {
					fprintf (stderr, "cannot set sample rate (%s)\n",
						 snd_strerror (err));
					exit (1);
				}

		// Make it stereo
		if ((err = snd_pcm_hw_params_set_channels (capture_handle, hw_params, 2)) < 0) {
			fprintf (stderr, "cannot set channel count (%s)\n",
				 snd_strerror (err));
			exit (1);
		}

		// Make it so
		if ((err = snd_pcm_hw_params (capture_handle, hw_params)) < 0) {
			fprintf (stderr, "cannot set parameters (%s)\n",
				 snd_strerror (err));
			exit (1);
		}

               // I forgot to mention the nasty mem leak if you forget to do this...
		snd_pcm_hw_params_free (hw_params);

		// Prepare for recording
		if ((err = snd_pcm_prepare (capture_handle)) < 0) {
			fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
				 snd_strerror (err));
			exit (1);
		}

		
		// It would probably be pertinent in the real world to loop this or do via callback
		
		// Let's actually grab something
		for (i = 0; i < 10; ++i) {
			if ((err = snd_pcm_readi (capture_handle, buf, 128)) != 128) {
				fprintf (stderr, "read from audio interface failed (%s)\n",
					 snd_strerror (err));
				exit (1);
			}
		}


		// Assuming we got this far we actually have something in our buffer.
		// We could now shovel this out somewhere. Let's go for stderr at this point.

		while(counter){
			counter--;

				fprintf (stderr, "buf: (%d)\n",buf[counter]);

			}

		// The cycle is complete.
		
		snd_pcm_close (capture_handle);
		
		//Live long and prosper
		
		exit (0);
	}


remember to compile it -lasound and --static against x-compiled alsa-libs with -o example or whatever

call it like example hw

This program is incredibly trivial - it simply output a short buffer of numbers to stderr.
The principle in what is important.

screenie of the output:

Last edited by twobob; 07-10-2012 at 11:54 AM. Reason: added call info: added mem leak
twobob is offline   Reply With Quote
Advert
Old 07-09-2012, 12:32 PM   #48
geekmaster
Carpe diem, c'est la vie.
geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.
 
geekmaster's Avatar
 
Posts: 6,433
Karma: 10773668
Join Date: Nov 2011
Location: Multiverse 6627A
Device: K1 to PW3
Quote:
Originally Posted by twobob View Post
Here is a simple test program that pulls some data in from the mic and splats it to stderr.

Enough to get you going with access to mic data.
...
remember to compile it -lasound and static against x-compiled alsa-libs with -o example or whatever

call it like example hw

This program is so trivial I shan't bother posting a compiled version.
I'll post a screenie of the output perhaps.
Thanks! I am looking forward to playing with this when I get caught up around here...
geekmaster is offline   Reply With Quote
Old 07-09-2012, 01:06 PM   #49
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: 6299991
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
Sound Buffers and Data Transfer

as outlined at http://www.linuxjournal.com/article/6735

A sound card has a hardware buffer that stores recorded samples. When the buffer is sufficiently full, it generates an interrupt. The kernel sound driver then uses direct memory access (DMA) to transfer samples to an application buffer in memory. Similarly, for playback, another application buffer is transferred from memory to the sound card's hardware buffer using DMA.

These hardware buffers are ring buffers, meaning the data wraps back to the start when the end of the buffer is reached. A pointer is maintained to keep track of the current positions in both the hardware buffer and the application buffer. Outside of the kernel, only the application buffer is of interest, so from here on we discuss only the application buffer.

The size of the buffer can be programmed by ALSA library calls. The buffer can be quite large, and transferring it in one operation could result in unacceptable delays, called latency. To solve this, ALSA splits the buffer up into a series of periods (called fragments in OSS/Free) and transfers the data in units of a period.

A period stores frames, each of which contains the samples captured at one point in time. For a stereo device, the frame would contain samples for two channels. Figure 1 illustrates the breakdown of a buffer into periods, frames and samples with some hypothetical values. Here, left and right channel information is stored alternately within a frame; this is called interleaved mode. A non-interleaved mode, where all the sample data for one channel is stored followed by the data for the next channel, also is supported.

.... ELIDED


FIGURE 1

This gives us a tidy overview of the way to do our frame maths.
We generally use the interleaved format as this is the default it would see, on our setup.

There is a wordy explanation of this and corner cases here:
http://www.alsa-project.org/main/ind.../FramesPeriods

Last edited by twobob; 07-09-2012 at 03:47 PM. Reason: added extra - somewhat wordy - reference
twobob is offline   Reply With Quote
Old 07-09-2012, 01:43 PM   #50
NiLuJe
BLAM!
NiLuJe ought to be getting tired of karma fortunes by now.NiLuJe ought to be getting tired of karma fortunes by now.NiLuJe ought to be getting tired of karma fortunes by now.NiLuJe ought to be getting tired of karma fortunes by now.NiLuJe ought to be getting tired of karma fortunes by now.NiLuJe ought to be getting tired of karma fortunes by now.NiLuJe ought to be getting tired of karma fortunes by now.NiLuJe ought to be getting tired of karma fortunes by now.NiLuJe ought to be getting tired of karma fortunes by now.NiLuJe ought to be getting tired of karma fortunes by now.NiLuJe ought to be getting tired of karma fortunes by now.
 
NiLuJe's Avatar
 
Posts: 13,506
Karma: 26047190
Join Date: Jun 2010
Location: Paris, France
Device: Kindle 2i, 3g, 4, 5w, PW, PW2, PW5; Kobo H2O, Forma, Elipsa, Sage, C2E
@twobob: The input twin of dmix is dsnoop, FWIW. Probably not that useful on the Kindle, but, hey . (You can glue a dmix output + dsnoop input together in a single pcm via an asym plug (See my desktop asoundrc as an example)).

Last edited by NiLuJe; 07-09-2012 at 03:19 PM.
NiLuJe is offline   Reply With Quote
Advert
Old 07-09-2012, 03:04 PM   #51
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: 6299991
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
Glueing inputs together

Quote:
Originally Posted by NiLuJe View Post
@twobob: The input twin of dmix in dsnoop, FWIW. Probably not that useful on the Kindle, but, hey . (You can glue a dmix output + dsnoop input together in a single pcm via an asym plug (See my desktop asoundrc as an example)).
Thanks NiLuJe, this is exactly the type of welding I have in mind to implement, eventually, via alsactl so it can be swapped in and out
twobob is offline   Reply With Quote
Old 07-09-2012, 03:07 PM   #52
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: 6299991
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
A simple walkthough

For those looking for a quick, decent short example of a simple blow-by-blow of soundcard through-alsa initialisation on a code level, then you could do worse than this:

http://stackoverflow.com/questions/3...fers-weird-api

The final "Answer" is fairly elucidating for those looking for a quick and dirty.

Last edited by twobob; 07-09-2012 at 03:49 PM. Reason: added the fact that this is the programmatic detail
twobob is offline   Reply With Quote
Old 07-09-2012, 04:22 PM   #53
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: 6299991
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
Wave file information



This is called the RIFF structure, built from different 'chunks'. The first 'chunk' is the DESCRIPTOR explaining the RIFF file type.

Next the FORMAT 'chunk' which explains the data format. The .WAV sound data can be 8-bit or 16-bit, mono or stereo, can have a different sampling rate, can be compressed or not, etc.

The next 'chunk', called the DATA 'chunk' holds the sound data, please see below:


Useful information for strapping together the audio cradle
twobob is offline   Reply With Quote
Old 07-09-2012, 04:24 PM   #54
MaPePeR
Connoisseur
MaPePeR can shake the floor when laughingMaPePeR can shake the floor when laughingMaPePeR can shake the floor when laughingMaPePeR can shake the floor when laughingMaPePeR can shake the floor when laughingMaPePeR can shake the floor when laughingMaPePeR can shake the floor when laughingMaPePeR can shake the floor when laughingMaPePeR can shake the floor when laughingMaPePeR can shake the floor when laughingMaPePeR can shake the floor when laughing
 
Posts: 58
Karma: 63518
Join Date: Apr 2012
Device: KT
just for the records
twobob: you are awesome.
MaPePeR is offline   Reply With Quote
Old 07-09-2012, 04:37 PM   #55
geekmaster
Carpe diem, c'est la vie.
geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.
 
geekmaster's Avatar
 
Posts: 6,433
Karma: 10773668
Join Date: Nov 2011
Location: Multiverse 6627A
Device: K1 to PW3
@twobob: Okay, time to stop hanging out in the "2600 neighborhood". I gave you an extra karma digit (and a palindrome at that).

I like where this is heading.

@MaPePer: I am still looking for a useful contribution from you so you too can join the "2600 club". Got anything to show me?
geekmaster is offline   Reply With Quote
Old 07-09-2012, 04:48 PM   #56
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: 6299991
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
appreciation

Quote:
Originally Posted by MaPePeR View Post
just for the records
twobob: you are awesome.
I'm not sure what for for, but I'll take it
Thanks.

If you mean the "Twobob" music the entire back catalogue of freely-available tracks is here: http://soundcloud.com/twobob/sets/fr...-the-clouders/

twobob is offline   Reply With Quote
Old 07-09-2012, 07:44 PM   #57
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: 6299991
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
This both amuses and educates
Spoiler:



Last edited by twobob; 07-09-2012 at 07:45 PM. Reason: its HUGE
twobob is offline   Reply With Quote
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: 6299991
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, 223 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
Old 07-09-2012, 09:10 PM   #59
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: 6299991
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
Cool cat /proc/asound/card0/BLAH

One interesting thing to note is you can monitor devices while the are running:

This output is from running the command whilst the microphone application above is running

(Tip: the p in pcm0c is capture, p would be playback)

Code:
[root@kindle us]# cat /proc/asound/card0/pcm0c/sub0/hw_params

access: MMAP_INTERLEAVED
format: S16_LE
subformat: STD
channels: 2
rate: 44100 (44100/1)
period_size: 16
buffer_size: 2048
period_size: 16
Cool!

Last edited by twobob; 07-09-2012 at 09:14 PM. Reason: added blackness, coolness and rightness
twobob is offline   Reply With Quote
Old 07-09-2012, 09:34 PM   #60
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: 6299991
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 Next up. something cool.

Okay, enough initialising. Let's hear something.

First off TURN YOUR VOLUME DOWN.
This demo is LOUD. I hope I SHOUTED that enough. I warned you.

Next up is the built version of the demo at
http://www.alsa-project.org/alsa-doc...c-example.html

It plays a simple sine wave to our speaker - pretty dull huh?
But it paves the wave for all kind of coolness so here's the blurb

Spoiler:

Code:
 /*
  *  This small demo sends a simple sinusoidal wave to your speakers.
  */

 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sched.h>
 #include <errno.h>
 #include <getopt.h>
 #include <asoundlib.h>
 #include <sys/time.h>
 #include <math.h>

 static char *device = "hw:0,0";                     /* playback device */
 static snd_pcm_format_t format = SND_PCM_FORMAT_S16;    /* sample format */
 static unsigned int rate = 44100;                       /* stream rate */
 static unsigned int channels = 2;                       /* count of channels */
 static unsigned int buffer_time = 500000;               /* ring buffer length in us */
 static unsigned int period_time = 100000;               /* period time in us */
 static double freq = 440;                               /* sinusoidal wave frequency in Hz */
 static int verbose = 0;                                 /* verbose flag */
 static int resample = 1;                                /* enable alsa-lib resampling */
 static int period_event = 0;                            /* produce poll event after each period */

 static snd_pcm_sframes_t buffer_size;
 static snd_pcm_sframes_t period_size;
 static snd_output_t *output = NULL;

 static void generate_sine(const snd_pcm_channel_area_t *areas,
                           snd_pcm_uframes_t offset,
                           int count, double *_phase)
 {
         static double max_phase = 2. * M_PI;
         double phase = *_phase;
         double step = max_phase*freq/(double)rate;
         unsigned char *samples[channels];
         int steps[channels];
         unsigned int chn;
         int format_bits = snd_pcm_format_width(format);
         unsigned int maxval = (1 << (format_bits - 1)) - 1;
         int bps = format_bits / 8;  /* bytes per sample */
         int phys_bps = snd_pcm_format_physical_width(format) / 8;
         int big_endian = snd_pcm_format_big_endian(format) == 1;
         int to_unsigned = snd_pcm_format_unsigned(format) == 1;
         int is_float = (format == SND_PCM_FORMAT_FLOAT_LE ||
                         format == SND_PCM_FORMAT_FLOAT_BE);

         /* verify and prepare the contents of areas */
         for (chn = 0; chn < channels; chn++) {
                 if ((areas[chn].first % 8) != 0) {
                         printf("areas[%i].first == %i, aborting...\n", chn, areas[chn].first);
                         exit(EXIT_FAILURE);
                 }
                 samples[chn] = /*(signed short *)*/(((unsigned char *)areas[chn].addr) + (areas[chn].first / 8));
                 if ((areas[chn].step % 16) != 0) {
                         printf("areas[%i].step == %i, aborting...\n", chn, areas[chn].step);
                         exit(EXIT_FAILURE);
                 }
                 steps[chn] = areas[chn].step / 8;
                 samples[chn] += offset * steps[chn];
         }
         /* fill the channel areas */
         while (count-- > 0) {
                 union {
                         float f;
                         int i;
                 } fval;
                 int res, i;
                 if (is_float) {
                         fval.f = sin(phase) * maxval;
                         res = fval.i;
                 } else
                         res = sin(phase) * maxval;
                 if (to_unsigned)
                         res ^= 1U << (format_bits - 1);
                 for (chn = 0; chn < channels; chn++) {
                         /* Generate data in native endian format */
                         if (big_endian) {
                                 for (i = 0; i < bps; i++)
                                         *(samples[chn] + phys_bps - 1 - i) = (res >> i * 8) & 0xff;
                         } else {
                                 for (i = 0; i < bps; i++)
                                         *(samples[chn] + i) = (res >>  i * 8) & 0xff;
                         }
                         samples[chn] += steps[chn];
                 }
                 phase += step;
                 if (phase >= max_phase)
                         phase -= max_phase;
         }
         *_phase = phase;
 }

 static int set_hwparams(snd_pcm_t *handle,
                         snd_pcm_hw_params_t *params,
                         snd_pcm_access_t access)
 {
         unsigned int rrate;
         snd_pcm_uframes_t size;
         int err, dir;

         /* choose all parameters */
         err = snd_pcm_hw_params_any(handle, params);
         if (err < 0) {
                 printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
                 return err;
         }
         /* set hardware resampling */
         err = snd_pcm_hw_params_set_rate_resample(handle, params, resample);
         if (err < 0) {
                 printf("Resampling setup failed for playback: %s\n", snd_strerror(err));
                 return err;
         }
         /* set the interleaved read/write format */
         err = snd_pcm_hw_params_set_access(handle, params, access);
         if (err < 0) {
                 printf("Access type not available for playback: %s\n", snd_strerror(err));
                 return err;
         }
         /* set the sample format */
         err = snd_pcm_hw_params_set_format(handle, params, format);
         if (err < 0) {
                 printf("Sample format not available for playback: %s\n", snd_strerror(err));
                 return err;
         }
         /* set the count of channels */
         err = snd_pcm_hw_params_set_channels(handle, params, channels);
         if (err < 0) {
                 printf("Channels count (%i) not available for playbacks: %s\n", channels, snd_strerror(err));
                 return err;
         }
         /* set the stream rate */
         rrate = rate;
         err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0);
         if (err < 0) {
                 printf("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err));
                 return err;
         }
         if (rrate != rate) {
                 printf("Rate doesn't match (requested %iHz, get %iHz)\n", rate, err);
                 return -EINVAL;
         }
         /* set the buffer time */
         err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir);
         if (err < 0) {
                 printf("Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err));
                 return err;
         }
         err = snd_pcm_hw_params_get_buffer_size(params, &size);
         if (err < 0) {
                 printf("Unable to get buffer size for playback: %s\n", snd_strerror(err));
                 return err;
         }
         buffer_size = size;
         /* set the period time */
         err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir);
         if (err < 0) {
                 printf("Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err));
                 return err;
         }
         err = snd_pcm_hw_params_get_period_size(params, &size, &dir);
         if (err < 0) {
                 printf("Unable to get period size for playback: %s\n", snd_strerror(err));
                 return err;
         }
         period_size = size;
         /* write the parameters to device */
         err = snd_pcm_hw_params(handle, params);
         if (err < 0) {
                 printf("Unable to set hw params for playback: %s\n", snd_strerror(err));
                 return err;
         }
         return 0;
 }

 static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams)
 {
         int err;

         /* get the current swparams */
         err = snd_pcm_sw_params_current(handle, swparams);
         if (err < 0) {
                 printf("Unable to determine current swparams for playback: %s\n", snd_strerror(err));
                 return err;
         }
         /* start the transfer when the buffer is almost full: */
         /* (buffer_size / avail_min) * avail_min */
         err = snd_pcm_sw_params_set_start_threshold(handle, swparams, (buffer_size / period_size) * period_size);
         if (err < 0) {
                 printf("Unable to set start threshold mode for playback: %s\n", snd_strerror(err));
                 return err;
         }
         /* allow the transfer when at least period_size samples can be processed */
         /* or disable this mechanism when period event is enabled (aka interrupt like style processing) */
         err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_event ? buffer_size : period_size);
         if (err < 0) {
                 printf("Unable to set avail min for playback: %s\n", snd_strerror(err));
                 return err;
         }
         /* enable period events when requested */
         if (period_event) {
               //  err = snd_pcm_sw_params_set_period_event(handle, swparams, 1);
               //  if (err < 0) {
               //          printf("Unable to set period event: %s\n", snd_strerror(err));
               //          return err;
               //  }
         }
         /* write the parameters to the playback device */
         err = snd_pcm_sw_params(handle, swparams);
         if (err < 0) {
                 printf("Unable to set sw params for playback: %s\n", snd_strerror(err));
                 return err;
         }
         return 0;
 }

 /*
  *   Underrun and suspend recovery
  */

 static int xrun_recovery(snd_pcm_t *handle, int err)
 {
         if (verbose)
                 printf("stream recovery\n");
         if (err == -EPIPE) {    /* under-run */
                 err = snd_pcm_prepare(handle);
                 if (err < 0)
                         printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err));
                 return 0;
         } else if (err == -ESTRPIPE) {
                 while ((err = snd_pcm_resume(handle)) == -EAGAIN)
                         sleep(1);       /* wait until the suspend flag is released */
                 if (err < 0) {
                         err = snd_pcm_prepare(handle);
                         if (err < 0)
                                 printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err));
                 }
                 return 0;
         }
         return err;
 }

 /*
  *   Transfer method - write only
  */

 static int write_loop(snd_pcm_t *handle,
                       signed short *samples,
                       snd_pcm_channel_area_t *areas)
 {
         double phase = 0;
         signed short *ptr;
         int err, cptr;

         while (1) {
                 generate_sine(areas, 0, period_size, &phase);
                 ptr = samples;
                 cptr = period_size;
                 while (cptr > 0) {
                         err = snd_pcm_writei(handle, ptr, cptr);
                         if (err == -EAGAIN)
                                 continue;
                         if (err < 0) {
                                 if (xrun_recovery(handle, err) < 0) {
                                         printf("Write error: %s\n", snd_strerror(err));
                                         exit(EXIT_FAILURE);
                                 }
                                 break;  /* skip one period */
                         }
                         ptr += err * channels;
                         cptr -= err;
                 }
         }
 }

 /*
  *   Transfer method - write and wait for room in buffer using poll
  */

 static int wait_for_poll(snd_pcm_t *handle, struct pollfd *ufds, unsigned int count)
 {
         unsigned short revents;

         while (1) {
                 poll(ufds, count, -1);
                 snd_pcm_poll_descriptors_revents(handle, ufds, count, &revents);
                 if (revents & POLLERR)
                         return -EIO;
                 if (revents & POLLOUT)
                         return 0;
         }
 }

 static int write_and_poll_loop(snd_pcm_t *handle,
                                signed short *samples,
                                snd_pcm_channel_area_t *areas)
 {
         struct pollfd *ufds;
         double phase = 0;
         signed short *ptr;
         int err, count, cptr, init;

         count = snd_pcm_poll_descriptors_count (handle);
         if (count <= 0) {
                 printf("Invalid poll descriptors count\n");
                 return count;
         }

         ufds = malloc(sizeof(struct pollfd) * count);
         if (ufds == NULL) {
                 printf("No enough memory\n");
                 return -ENOMEM;
         }
         if ((err = snd_pcm_poll_descriptors(handle, ufds, count)) < 0) {
                 printf("Unable to obtain poll descriptors for playback: %s\n", snd_strerror(err));
                 return err;
         }

         init = 1;
         while (1) {
                 if (!init) {
                         err = wait_for_poll(handle, ufds, count);
                         if (err < 0) {
                                 if (snd_pcm_state(handle) == SND_PCM_STATE_XRUN ||
                                     snd_pcm_state(handle) == SND_PCM_STATE_SUSPENDED) {
                                         err = snd_pcm_state(handle) == SND_PCM_STATE_XRUN ? -EPIPE : -ESTRPIPE;
                                         if (xrun_recovery(handle, err) < 0) {
                                                 printf("Write error: %s\n", snd_strerror(err));
                                                 exit(EXIT_FAILURE);
                                         }
                                         init = 1;
                                 } else {
                                         printf("Wait for poll failed\n");
                                         return err;
                                 }
                         }
                 }

                 generate_sine(areas, 0, period_size, &phase);
                 ptr = samples;
                 cptr = period_size;
                 while (cptr > 0) {
                         err = snd_pcm_writei(handle, ptr, cptr);
                         if (err < 0) {
                                 if (xrun_recovery(handle, err) < 0) {
                                         printf("Write error: %s\n", snd_strerror(err));
                                         exit(EXIT_FAILURE);
                                 }
                                 init = 1;
                                 break;  /* skip one period */
                         }
                         if (snd_pcm_state(handle) == SND_PCM_STATE_RUNNING)
                                 init = 0;
                         ptr += err * channels;
                         cptr -= err;
                         if (cptr == 0)
                                 break;
                         /* it is possible, that the initial buffer cannot store */
                         /* all data from the last period, so wait awhile */
                         err = wait_for_poll(handle, ufds, count);
                         if (err < 0) {
                                 if (snd_pcm_state(handle) == SND_PCM_STATE_XRUN ||
                                     snd_pcm_state(handle) == SND_PCM_STATE_SUSPENDED) {
                                         err = snd_pcm_state(handle) == SND_PCM_STATE_XRUN ? -EPIPE : -ESTRPIPE;
                                         if (xrun_recovery(handle, err) < 0) {
                                                 printf("Write error: %s\n", snd_strerror(err));
                                                 exit(EXIT_FAILURE);
                                         }
                                         init = 1;
                                 } else {
                                         printf("Wait for poll failed\n");
                                         return err;
                                 }
                         }
                 }
         }
 }

 /*
  *   Transfer method - asynchronous notification
  */

 struct async_private_data {
         signed short *samples;
         snd_pcm_channel_area_t *areas;
         double phase;
 };

 static void async_callback(snd_async_handler_t *ahandler)
 {
         snd_pcm_t *handle = snd_async_handler_get_pcm(ahandler);
         struct async_private_data *data = snd_async_handler_get_callback_private(ahandler);
         signed short *samples = data->samples;
         snd_pcm_channel_area_t *areas = data->areas;
         snd_pcm_sframes_t avail;
         int err;

         avail = snd_pcm_avail_update(handle);
         while (avail >= period_size) {
                 generate_sine(areas, 0, period_size, &data->phase);
                 err = snd_pcm_writei(handle, samples, period_size);
                 if (err < 0) {
                         printf("Write error: %s\n", snd_strerror(err));
                         exit(EXIT_FAILURE);
                 }
                 if (err != period_size) {
                         printf("Write error: written %i expected %li\n", err, period_size);
                         exit(EXIT_FAILURE);
                 }
                 avail = snd_pcm_avail_update(handle);
         }
 }

 static int async_loop(snd_pcm_t *handle,
                       signed short *samples,
                       snd_pcm_channel_area_t *areas)
 {
         struct async_private_data data;
         snd_async_handler_t *ahandler;
         int err, count;

         data.samples = samples;
         data.areas = areas;
         data.phase = 0;
         err = snd_async_add_pcm_handler(&ahandler, handle, async_callback, &data);
         if (err < 0) {
                 printf("Unable to register async handler\n");
                 exit(EXIT_FAILURE);
         }
         for (count = 0; count < 2; count++) {
                 generate_sine(areas, 0, period_size, &data.phase);
                 err = snd_pcm_writei(handle, samples, period_size);
                 if (err < 0) {
                         printf("Initial write error: %s\n", snd_strerror(err));
                         exit(EXIT_FAILURE);
                 }
                 if (err != period_size) {
                         printf("Initial write error: written %i expected %li\n", err, period_size);
                         exit(EXIT_FAILURE);
                 }
         }
         if (snd_pcm_state(handle) == SND_PCM_STATE_PREPARED) {
                 err = snd_pcm_start(handle);
                 if (err < 0) {
                         printf("Start error: %s\n", snd_strerror(err));
                         exit(EXIT_FAILURE);
                 }
         }

         /* because all other work is done in the signal handler,
            suspend the process */
         while (1) {
                 sleep(1);
         }
 }

 /*
  *   Transfer method - asynchronous notification + direct write
  */

 static void async_direct_callback(snd_async_handler_t *ahandler)
 {
         snd_pcm_t *handle = snd_async_handler_get_pcm(ahandler);
         struct async_private_data *data = snd_async_handler_get_callback_private(ahandler);
         const snd_pcm_channel_area_t *my_areas;
         snd_pcm_uframes_t offset, frames, size;
         snd_pcm_sframes_t avail, commitres;
         snd_pcm_state_t state;
         int first = 0, err;

         while (1) {
                 state = snd_pcm_state(handle);
                 if (state == SND_PCM_STATE_XRUN) {
                         err = xrun_recovery(handle, -EPIPE);
                         if (err < 0) {
                                 printf("XRUN recovery failed: %s\n", snd_strerror(err));
                                 exit(EXIT_FAILURE);
                         }
                         first = 1;
                 } else if (state == SND_PCM_STATE_SUSPENDED) {
                         err = xrun_recovery(handle, -ESTRPIPE);
                         if (err < 0) {
                                 printf("SUSPEND recovery failed: %s\n", snd_strerror(err));
                                 exit(EXIT_FAILURE);
                         }
                 }
                 avail = snd_pcm_avail_update(handle);
                 if (avail < 0) {
                         err = xrun_recovery(handle, avail);
                         if (err < 0) {
                                 printf("avail update failed: %s\n", snd_strerror(err));
                                 exit(EXIT_FAILURE);
                         }
                         first = 1;
                         continue;
                 }
                 if (avail < period_size) {
                         if (first) {
                                 first = 0;
                                 err = snd_pcm_start(handle);
                                 if (err < 0) {
                                         printf("Start error: %s\n", snd_strerror(err));
                                         exit(EXIT_FAILURE);
                                 }
                         } else {
                                 break;
                         }
                         continue;
                 }
                 size = period_size;
                 while (size > 0) {
                         frames = size;
                         err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames);
                         if (err < 0) {
                                 if ((err = xrun_recovery(handle, err)) < 0) {
                                         printf("MMAP begin avail error: %s\n", snd_strerror(err));
                                         exit(EXIT_FAILURE);
                                 }
                                 first = 1;
                         }
                         generate_sine(my_areas, offset, frames, &data->phase);
                         commitres = snd_pcm_mmap_commit(handle, offset, frames);
                         if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) {
                                 if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) {
                                         printf("MMAP commit error: %s\n", snd_strerror(err));
                                         exit(EXIT_FAILURE);
                                 }
                                 first = 1;
                         }
                         size -= frames;
                 }
         }
 }

 static int async_direct_loop(snd_pcm_t *handle,
                              signed short *samples ATTRIBUTE_UNUSED,
                              snd_pcm_channel_area_t *areas ATTRIBUTE_UNUSED)
 {
         struct async_private_data data;
         snd_async_handler_t *ahandler;
         const snd_pcm_channel_area_t *my_areas;
         snd_pcm_uframes_t offset, frames, size;
         snd_pcm_sframes_t commitres;
         int err, count;

         data.samples = NULL;    /* we do not require the global sample area for direct write */
         data.areas = NULL;      /* we do not require the global areas for direct write */
         data.phase = 0;
         err = snd_async_add_pcm_handler(&ahandler, handle, async_direct_callback, &data);
         if (err < 0) {
                 printf("Unable to register async handler\n");
                 exit(EXIT_FAILURE);
         }
         for (count = 0; count < 2; count++) {
                 size = period_size;
                 while (size > 0) {
                         frames = size;
                         err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames);
                         if (err < 0) {
                                 if ((err = xrun_recovery(handle, err)) < 0) {
                                         printf("MMAP begin avail error: %s\n", snd_strerror(err));
                                         exit(EXIT_FAILURE);
                                 }
                         }
                         generate_sine(my_areas, offset, frames, &data.phase);
                         commitres = snd_pcm_mmap_commit(handle, offset, frames);
                         if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) {
                                 if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) {
                                         printf("MMAP commit error: %s\n", snd_strerror(err));
                                         exit(EXIT_FAILURE);
                                 }
                         }
                         size -= frames;
                 }
         }
         err = snd_pcm_start(handle);
         if (err < 0) {
                 printf("Start error: %s\n", snd_strerror(err));
                 exit(EXIT_FAILURE);
         }

         /* because all other work is done in the signal handler,
            suspend the process */
         while (1) {
                 sleep(1);
         }
 }

 /*
  *   Transfer method - direct write only
  */

 static int direct_loop(snd_pcm_t *handle,
                        signed short *samples ATTRIBUTE_UNUSED,
                        snd_pcm_channel_area_t *areas ATTRIBUTE_UNUSED)
 {
         double phase = 0;
         const snd_pcm_channel_area_t *my_areas;
         snd_pcm_uframes_t offset, frames, size;
         snd_pcm_sframes_t avail, commitres;
         snd_pcm_state_t state;
         int err, first = 1;

         while (1) {
                 state = snd_pcm_state(handle);
                 if (state == SND_PCM_STATE_XRUN) {
                         err = xrun_recovery(handle, -EPIPE);
                         if (err < 0) {
                                 printf("XRUN recovery failed: %s\n", snd_strerror(err));
                                 return err;
                         }
                         first = 1;
                 } else if (state == SND_PCM_STATE_SUSPENDED) {
                         err = xrun_recovery(handle, -ESTRPIPE);
                         if (err < 0) {
                                 printf("SUSPEND recovery failed: %s\n", snd_strerror(err));
                                 return err;
                         }
                 }
                 avail = snd_pcm_avail_update(handle);
                 if (avail < 0) {
                         err = xrun_recovery(handle, avail);
                         if (err < 0) {
                                 printf("avail update failed: %s\n", snd_strerror(err));
                                 return err;
                         }
                         first = 1;
                         continue;
                 }
                 if (avail < period_size) {
                         if (first) {
                                 first = 0;
                                 err = snd_pcm_start(handle);
                                 if (err < 0) {
                                         printf("Start error: %s\n", snd_strerror(err));
                                         exit(EXIT_FAILURE);
                                 }
                         } else {
                                 err = snd_pcm_wait(handle, -1);
                                 if (err < 0) {
                                         if ((err = xrun_recovery(handle, err)) < 0) {
                                                 printf("snd_pcm_wait error: %s\n", snd_strerror(err));
                                                 exit(EXIT_FAILURE);
                                         }
                                         first = 1;
                                 }
                         }
                         continue;
                 }
                 size = period_size;
                 while (size > 0) {
                         frames = size;
                         err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames);
                         if (err < 0) {
                                 if ((err = xrun_recovery(handle, err)) < 0) {
                                         printf("MMAP begin avail error: %s\n", snd_strerror(err));
                                         exit(EXIT_FAILURE);
                                 }
                                 first = 1;
                         }
                         generate_sine(my_areas, offset, frames, &phase);
                         commitres = snd_pcm_mmap_commit(handle, offset, frames);
                         if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) {
                                 if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) {
                                         printf("MMAP commit error: %s\n", snd_strerror(err));
                                         exit(EXIT_FAILURE);
                                 }
                                 first = 1;
                         }
                         size -= frames;
                 }
         }
 }

 /*
  *   Transfer method - direct write only using mmap_write functions
  */

 static int direct_write_loop(snd_pcm_t *handle,
                              signed short *samples,
                              snd_pcm_channel_area_t *areas)
 {
         double phase = 0;
         signed short *ptr;
         int err, cptr;

         while (1) {
                 generate_sine(areas, 0, period_size, &phase);
                 ptr = samples;
                 cptr = period_size;
                 while (cptr > 0) {
                         err = snd_pcm_mmap_writei(handle, ptr, cptr);
                         if (err == -EAGAIN)
                                 continue;
                         if (err < 0) {
                                 if (xrun_recovery(handle, err) < 0) {
                                         printf("Write error: %s\n", snd_strerror(err));
                                         exit(EXIT_FAILURE);
                                 }
                                 break;  /* skip one period */
                         }
                         ptr += err * channels;
                         cptr -= err;
                 }
         }
 }

 /*
  *
  */

 struct transfer_method {
         const char *name;
         snd_pcm_access_t access;
         int (*transfer_loop)(snd_pcm_t *handle,
                              signed short *samples,
                              snd_pcm_channel_area_t *areas);
 };

 static struct transfer_method transfer_methods[] = {
         { "write", SND_PCM_ACCESS_RW_INTERLEAVED, write_loop },
         { "write_and_poll", SND_PCM_ACCESS_RW_INTERLEAVED, write_and_poll_loop },
         { "async", SND_PCM_ACCESS_RW_INTERLEAVED, async_loop },
         { "async_direct", SND_PCM_ACCESS_MMAP_INTERLEAVED, async_direct_loop },
         { "direct_interleaved", SND_PCM_ACCESS_MMAP_INTERLEAVED, direct_loop },
         { "direct_noninterleaved", SND_PCM_ACCESS_MMAP_NONINTERLEAVED, direct_loop },
         { "direct_write", SND_PCM_ACCESS_MMAP_INTERLEAVED, direct_write_loop },
         { NULL, SND_PCM_ACCESS_RW_INTERLEAVED, NULL }
 };

 static void help(void)
 {
         int k;
         printf(
 "Usage: pcm [OPTION]... [FILE]...\n"
 "-h,--help      help\n"
 "-D,--device    playback device\n"
 "-r,--rate      stream rate in Hz\n"
 "-c,--channels  count of channels in stream\n"
 "-f,--frequency sine wave frequency in Hz\n"
 "-b,--buffer    ring buffer size in us\n"
 "-p,--period    period size in us\n"
 "-m,--method    transfer method\n"
 "-o,--format    sample format\n"
 "-v,--verbose   show the PCM setup parameters\n"
 "-n,--noresample  do not resample\n"
 "-e,--pevent    enable poll event after each period\n"
 "\n");
         printf("Recognized sample formats are:");
         for (k = 0; k < SND_PCM_FORMAT_LAST; ++k) {
                 const char *s = snd_pcm_format_name(k);
                 if (s)
                         printf(" %s", s);
         }
         printf("\n");
         printf("Recognized transfer methods are:");
         for (k = 0; transfer_methods[k].name; k++)
                 printf(" %s", transfer_methods[k].name);
         printf("\n");
 }

 int main(int argc, char *argv[])
 {
         struct option long_option[] =
         {
                 {"help", 0, NULL, 'h'},
                 {"device", 1, NULL, 'D'},
                 {"rate", 1, NULL, 'r'},
                 {"channels", 1, NULL, 'c'},
                 {"frequency", 1, NULL, 'f'},
                 {"buffer", 1, NULL, 'b'},
                 {"period", 1, NULL, 'p'},
                 {"method", 1, NULL, 'm'},
                 {"format", 1, NULL, 'o'},
                 {"verbose", 1, NULL, 'v'},
                 {"noresample", 1, NULL, 'n'},
                 {"pevent", 1, NULL, 'e'},
                 {NULL, 0, NULL, 0},
         };
         snd_pcm_t *handle;
         int err, morehelp;
         snd_pcm_hw_params_t *hwparams;
         snd_pcm_sw_params_t *swparams;
         int method = 0;
         signed short *samples;
         unsigned int chn;
         snd_pcm_channel_area_t *areas;

         snd_pcm_hw_params_alloca(&hwparams);
         snd_pcm_sw_params_alloca(&swparams);

         morehelp = 0;
         while (1) {
                 int c;
                 if ((c = getopt_long(argc, argv, "hD:r:c:f:b:p:m:o:vne", long_option, NULL)) < 0)
                         break;
                 switch (c) {
                 case 'h':
                         morehelp++;
                         break;
                 case 'D':
                         device = strdup(optarg);
                         break;
                 case 'r':
                         rate = atoi(optarg);
                         rate = rate < 4000 ? 4000 : rate;
                         rate = rate > 196000 ? 196000 : rate;
                         break;
                 case 'c':
                         channels = atoi(optarg);
                         channels = channels < 1 ? 1 : channels;
                         channels = channels > 1024 ? 1024 : channels;
                         break;
                 case 'f':
                         freq = atoi(optarg);
                         freq = freq < 50 ? 50 : freq;
                         freq = freq > 5000 ? 5000 : freq;
                         break;
                 case 'b':
                         buffer_time = atoi(optarg);
                         buffer_time = buffer_time < 1000 ? 1000 : buffer_time;
                         buffer_time = buffer_time > 1000000 ? 1000000 : buffer_time;
                         break;
                 case 'p':
                         period_time = atoi(optarg);
                         period_time = period_time < 1000 ? 1000 : period_time;
                         period_time = period_time > 1000000 ? 1000000 : period_time;
                         break;
                 case 'm':
                         for (method = 0; transfer_methods[method].name; method++)
                                         if (!strcasecmp(transfer_methods[method].name, optarg))
                                         break;
                         if (transfer_methods[method].name == NULL)
                                 method = 0;
                         break;
                 case 'o':
                         for (format = 0; format < SND_PCM_FORMAT_LAST; format++) {
                                 const char *format_name = snd_pcm_format_name(format);
                                 if (format_name)
                                         if (!strcasecmp(format_name, optarg))
                                         break;
                         }
                         if (format == SND_PCM_FORMAT_LAST)
                                 format = SND_PCM_FORMAT_S16;
                         if (!snd_pcm_format_linear(format) &&
                             !(format == SND_PCM_FORMAT_FLOAT_LE ||
                               format == SND_PCM_FORMAT_FLOAT_BE)) {
                                 printf("Invalid (non-linear/float) format %s\n",
                                        optarg);
                                 return 1;
                         }
                         break;
                 case 'v':
                         verbose = 1;
                         break;
                 case 'n':
                         resample = 0;
                         break;
                 case 'e':
                         period_event = 1;
                         break;
                 }
         }

         if (morehelp) {
                 help();
                 return 0;
         }

         err = snd_output_stdio_attach(&output, stdout, 0);
         if (err < 0) {
                 printf("Output failed: %s\n", snd_strerror(err));
                 return 0;
         }

         printf("Playback device is %s\n", device);
         printf("Stream parameters are %iHz, %s, %i channels\n", rate, snd_pcm_format_name(format), channels);
         printf("Sine wave rate is %.4fHz\n", freq);
         printf("Using transfer method: %s\n", transfer_methods[method].name);

         if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
                 printf("Playback open error: %s\n", snd_strerror(err));
                 return 0;
         }

         if ((err = set_hwparams(handle, hwparams, transfer_methods[method].access)) < 0) {
                 printf("Setting of hwparams failed: %s\n", snd_strerror(err));
                 exit(EXIT_FAILURE);
         }
         if ((err = set_swparams(handle, swparams)) < 0) {
                 printf("Setting of swparams failed: %s\n", snd_strerror(err));
                 exit(EXIT_FAILURE);
         }

         if (verbose > 0)
                 snd_pcm_dump(handle, output);

         samples = malloc((period_size * channels * snd_pcm_format_physical_width(format)) / 8);
         if (samples == NULL) {
                 printf("No enough memory\n");
                 exit(EXIT_FAILURE);
         }

         areas = calloc(channels, sizeof(snd_pcm_channel_area_t));
         if (areas == NULL) {
                 printf("No enough memory\n");
                 exit(EXIT_FAILURE);
         }
         for (chn = 0; chn < channels; chn++) {
                 areas[chn].addr = samples;
                 areas[chn].first = chn * snd_pcm_format_physical_width(format);
                 areas[chn].step = channels * snd_pcm_format_physical_width(format);
         }

         err = transfer_methods[method].transfer_loop(handle, samples, areas);
         if (err < 0)
                 printf("Transfer failed: %s\n", snd_strerror(err));

         free(areas);
         free(samples);
         snd_pcm_close(handle);
         return 0;
         }


This was the first demo to work out-of-the-box on cross-compile. Happy days.

usage: ./sine

(it goes up to 5Khz), device hw ( hw:0 and synonyms ) is supported currently

install / uninstall: just copy / remove the file

Full listing of Usage: sine [OPTION]... [FILE]...
-h,--help help
-D,--device playback device
-r,--rate stream rate in Hz
-c,--channels count of channels in stream
-f,--frequency sine wave frequency in Hz
-b,--buffer ring buffer size in us
-p,--period period size in us
-m,--method transfer method
-o,--format sample format
-v,--verbose show the PCM setup parameters
-n,--noresample do not resample
-e,--pevent enable poll event after each period

Recognized sample formats are: S8 U8 S16_LE S16_BE U16_LE U16_BE S24_LE S24_BE U24_LE U24_BE S32_LE S32_BE U32_LE U32_BE FLOAT_LE FLOAT_BE FLOAT64_LE FLOAT64_BE IEC958_SUBFRAME_LE IEC958_SUBFRAME_BE MU_LAW A_LAW IMA_ADPCM MPEG GSM SPECIAL S24_3LE S24_3BE U24_3LE U24_3BE S20_3LE S20_3BE U20_3LE U20_3BE S18_3LE S18_3BE U18_3LE
Recognized transfer methods are: write write_and_poll async async_direct direct_interleaved direct_noninterleaved direct_write

WHAT IS ACTUALLY ATTACHED IS tones, THIS IS SIMPLY A MORE INTERESTING VARIATION OF THIS CODE. THANKS

TESTED ON ALL AUDIO CAPABLE KINDLES
Attached Files
File Type: gz tones.gz (13.3 KB, 165 views)

Last edited by twobob; 07-12-2012 at 11:03 PM. Reason: I updated the static Alsalib to stop to broken alsa.conf request
twobob is offline   Reply With Quote
Reply

Tags
stupid root mistakes


Forum Jump


All times are GMT -4. The time now is 06:32 AM.


MobileRead.com is a privately owned, operated and funded community.