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-02-2012, 09:36 AM   #1
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
Audio (video) - Kindle. Getting it up and running, Code level.

Latest Update:
Spoiler:

The supported device is hw for now. YMMV using other alsa interface devices.

In the spirit of usefulness in the style on MobileRead Dev Corner I'll continue to edit the top post to make reading all the rest of this stuff - if you don't actually need to - unnecessary.

The reality of video and sound in one application is a great deal nearer now and simple init code is demoed below

"Audio - Kindle - Linux. Getting it up and running, Code level."

This thread is about the quest to get some audio action on the Kindle - direct via code.
If this is not something you are interested in. Stop reading now. Thanks

OUTPUT OF THE PROJECT

There are now a wealth of links on these threads to pretty much everything I could find about alsa as it stands today. It's not an impressive list of resources all in out there and hopefully this guide will serve to AT LEAST get sound in your applications in a singular direction without too much grind.

There is a useful overview of the PCM interface that is a bit scary but not a terrible read here http://www.alsa-project.org/alsa-doc/alsa-lib/pcm.html

STEP ONE: INIT MY ALSA

There are a few simple demos freely available from the Alsa project. We have used these as part of the basis of the explanations here. Hopefully in a clear enough manner to get you going.

BARE MINIMUM:

Spoiler:
Code:
 #include <asoundlib.h>
 static char *device = "hw:0";                        /* playback device */
 
int main(void)
 {       
         // setup the references.
         snd_pcm_t *handle;
         snd_pcm_sframes_t frames;

         // open it.
         snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0);
         
         // init it.
         snd_pcm_set_params(handle,
                                                 SND_PCM_FORMAT_S16_LE,
                                                 SND_PCM_ACCESS_RW_INTERLEAVED,
                                                 2,  // channels
                                                 44100,  // rate
                                                 0,  // soft resample
                                                 500000);  // latency
         // write to it.
         frames = snd_pcm_writei(handle, buffer, sizeof(buffer));
         
         // close it.
         snd_pcm_close(handle);


That's an absolute bare minimum example. The further examples demonstrate better error handling and alternate setup methods. All are succinct.

One of the more succinct demonstrations for initialising a sound device is here but the actual how and why of what's happening is not clear, plus much of the code can be elided for use on the kindle, so to this end here are two reworked versions of that demo. It's enough to get you going in about 5 minutes flat.

Here is the (Astlye compliant) discursive version of the code with additional notes and highlighting of important areas: (In the preferred style of Knc1)
Spoiler:

Code:
#include <alsa/asoundlib.h>

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

    // The err value being setup here would ordinarily 
    //  be populated by error handling code that is "wrapped around" 
    //  each of the calls  to the alsa methods (in bold). 

    //  I have left in one example of this construct on the snd_pcm_writei
    //  method to show how this works in practice. (At the end.)
    //  This repetitive construct is elided here for brevity, further examples
    //  can be found on previous posts on this thread
 
    // i = index for looping over buffer at the end - It's a superfluous 
    // variable used only to illustrate buffer iteration and can be ignored

    // err = alsa method return codes / frame counts etc...
    int i, err;

    // buf = An example data store to splat into buffer at the end
    // You would fill a similar construct with something 
    // useful in the real world.
    short buf[128];
 
    // Declare an instance (or occurence if you prefer)
    // of the special "Type" defined by Alsa 
    // This tells Alsa the card is an available sound device
    // We use this name to reference the device in other calls

    snd_pcm_t *playback_handle;

    // Similarly we create a new "parameters" construct
    // Simply creating the reference here is enough to create it
    // You can use this to setup the card to your liking
    // We do the bare minimum here to get us going
    snd_pcm_hw_params_t *hw_params

    // This is the 1st Alsa method we have called (directly anyway)
    // note we reference the card via it's handle we just created

    // Then we grab the first argument passed to the command line
    // argv[1] gives us this value - an e.g is below. 
    // (like ./TestProgram hw:0 would return hw:0)

    // At this stage we link the handle to the device hw:0 in this case 
    // (Further details of how devices are configed via conf files in earlier posts)

    // We would get back an error code (into the err construct) at this 
    // stage if something was amiss in accessing the card (bad conf for e.g.)

   // The full details of the enum SND_PCM_STREAM_ETC... can be
   // viewed here the complete PCM command set here

    snd_pcm_open (&playback_handle, argv[1], SND_PCM_STREAM_PLAYBACK, 0);

    // This defines some space for our params constuct
    // c is fussy about memory handling
    // Make some space - remember to tidy it up after
    // Malloc = Think "Memory Allocate"
    snd_pcm_hw_params_malloc (&hw_params);

    // This is defined in the ALSA Hardware section here
    // Fill our params space with a full configuration construct for a PCM. 
    // Making use of the space we just made and created a skeleton for us
    // Note we still reference the card via the handle from now on in
     snd_pcm_hw_params_any (playback_handle, hw_params);

    //  Restrict a configuration space to contain only one access type.   
    // The full reference for the method is here
    // I seemed to need to set this to just one type to get the card going.

    // Perhaps setting one val is absolute minimum for configuration
    // I am unsure but this got it working for me without going on to set 
    // the sample rate, access format etc. the full gory details are in the 
    // original demo, this is about getting the kindle going with minimum code

    // the full tech ref for the enum SND_PCM_ACCESS_BLAH is here
    // In essence it is pcm_read/write interleaved access 

    // Pcm means it's not done in memory it's (kinda) direct to card

    // read/write means duplexing possible, Interleaving means that we 
    // alternate samples for the left and right channel (LR,LR,LR...).

    // It's a very generic choice. You could "refine" this decision to your needs
    snd_pcm_hw_params_set_access (playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);

    // The next utility method puts our params decisions in motion 
    // "Install one PCM hardware configuration chosen from a configuration 
    // space and snd_pcm_prepare  it." say the docs here
    // so as you can see this runs another method internally for us. Happy days.
    snd_pcm_hw_params (playback_handle, hw_params);

    // This simply tidies up the memory space from the param construct 
    // as we are done with it, 

    // it's always good practice in c to destruct your disused objects. 
    snd_pcm_hw_params_free (hw_params);

    // Showtime, as shown here this "Prepare[s] PCM for use." 
    // All of these methods could have been wrapped in error handlers, 
    // only some of them have real value though, this is maybe one of them 
    snd_pcm_prepare (playback_handle);

    // We are now ready to so some stuff so let's write some interleaved data.
    // Everything after this point is "Program", we have done our job really.

    // buf is just some 0 data 128 long, 
    // 128 is length of buf, effectively buf.length
    // 10 is just a value to show looping - has no instrinsic meaning
    // write 10 * 128 shorties via the handle using interleaved writing

     // This says - in English-Psuedo-Code
     // Try to write 128 shorts to the PCM
     // If it fails a) return an error value for the process and Then 
     // b) exit the application with a code indicating Failure 
     // Do this ten times 

    for (i = 0; i < 10; ++i)
    {   if ((err = snd_pcm_writei (playback_handle, buf, 128)) != 128) {
            fprintf (stderr, "write to audio interface failed (%s)\n",snd_strerror (err));
            exit (1);
        }
    }

    //Tidy up. Ref here 
    snd_pcm_close (playback_handle);

    // Exit the application with a code indicating Success. 
    exit (0);
}


INIT ALSA - CHOP FU
And here is that demo again but this time squished: (In GM's preferred compressed style)
Spoiler:
Code:
#include <alsa/asoundlib.h>

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

//INIT BLOCK
int err; short buf[128]; snd_pcm_t *playback_handle; 	
snd_pcm_hw_params_t *hw_params; 
snd_pcm_open (&playback_handle, argv[1], SND_PCM_STREAM_PLAYBACK, 0);
snd_pcm_hw_params_malloc (&hw_params);
snd_pcm_hw_params_any (playback_handle, hw_params);
snd_pcm_hw_params_set_access (playback_handle, hw_params, 
SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params (playback_handle, hw_params);
snd_pcm_hw_params_free (hw_params);
snd_pcm_prepare (playback_handle);
// Write something
snd_pcm_writei (playback_handle, buf, 128) ;
//Tidy up.
snd_pcm_close (playback_handle); 		exit (0);}
//Done.


ACKNOWLEDGMENTS
Many thanks to: The excellent assistance of GeekMaster, Knc1, NiLuJe and others
and notably the KeK project, without which this project would still be stuck in no-x-compile-land.
All of the work regarding video is completely down to the work of Geekmaster and you can find the thread dedicated to that here geekmaster kindle video player , currently it is without audio. Perfect for the thrust of this thread: "Providing support and documentation to enable sound for the kindle." Testing continues but I can report decent beta results on a 3.

WORK DONE SO FAR
Spoiler:

Identified the audio card, drivers and some ancillary support mechanisms in place on the device. These are documented in the further posts.

We have working card init code in cpp. c,
A Tones demo cross-compiled for all audio enabled kindles.

TODO:
Spoiler:

1) Fixed the broken library lookup at the expense of losing plughw: operation,
Todo: restore this functionality and study more closely aplays operational straces on the kindle


2) Obviously this audio cradle will not solely be playing static files, streaming data from stdin, or w/e.
Ideally it will provide enough information to cater for developers individual needs. In the interim a simple Proof Of Concept side - by - side of audio with video would be a great start.

3) See if we can make the extremely low CPU usage mode we have identified as a potential candidate for realtime playback can carry audio data (don't see why not, just needs implementing within the parameters of the tones demo in special circumstances.)

4) Get a working practice together for creating usable files for AV work in association with GM.
Further to this - develop a format that works - ideally retrospectively - with the GM player and audio.

5) Implement a core - including short code snippets - with documented features - to enable quick initialisation of the kindle card - with reference to implementation on all devices audio capable - and possibly extend the none audio capable ones into at least raw streaming support to a null / file device.

6) Create some cool demos that show off GM's front end and the audio enabled backend.


The application outlined below is a simple tone generator demonstrating access the kindles alsa layer.
It's a more fun demo than the previous top-post and people like fun.

A more general purpose initialisation codeset - possibly called "Kradle" - is in the wings, somewhere, one day.

DEMO APPLICATION: tones

Frankly I'm amazed it works at all. so if it blows up your kindle? Don't blame me.

Source:
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)
{

	// this is to store the value of direction

	static int direction = 0; // default direction;

	static int pace_count = 30; // default speedup;

	static int multi = 1; // for reference really

	static int d_count = 0; // to reduce screen clutter;

	static int inc_dec = 1; // to inc or dec the multi;

	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;

		//result and index

		int res, i;
		if (is_float) {

			// Do the maths

			fval.f = sin(phase) * maxval;
			res = fval.i;
		} else

			//Do the maths

			res = sin(phase) * maxval;

		// remove sign
		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];
		}

		//increment phase

		phase += (step);

		//  >= 2 PI
		if (phase >= max_phase)

			// loop back to beginning
			phase -= max_phase;

	}
	*_phase = phase;

	// We need to stop the number range going to far out
	// The number range should not be more than half of the sample rate - like 8k for 16000



	if (freq <= -3000)
	{

		direction = 1;
	}

	else if (freq >= 3000)
	{
		direction = 0;

	}

	// make it circle round some helpful edges

	if (multi > 1000)
	{ inc_dec = -20;}
	else if (multi < 20)
	{ inc_dec = 2;}


	pace_count+= inc_dec;

	 multi= pace_count * M_PI;

	if (direction)
	{

		//* M_PI added just for a laugh
		freq +=max_phase*multi; //added just for a laugh;
	}
	else
	{//* M_PI added just for a laugh
		freq -=max_phase*multi;

	}

	if (d_count == 10)
			{

				printf("freak: %f multiplier %i\n",freq, multi);
				d_count = 0;
			}


	d_count++;

}

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 is in essence the demo from Alsa with a few bits of musicality chucked in.
Notably the use of numeric constraints to limit to pleasant range, multi-step offsets linked an overall counter to give us motion and loops, etc.

It's basic stuff, chuck in a Pi here and a > there and you're off. You want to be looking at the second half of the function generate_sine, Feel free to hack, I added notes.

FEEDBACK

I'm interested in any random output it may have on your kindles that I can fix up.

INSTALL
Copy it to your device somewhere.

RUNNING THE APPLICATION
Launch it via SSH, Launchpad or whatever.

EXAMPLE simple usage: ./tones

EXAMPLE advanced usage: ./tones -f 4000 -D hw -c 2 -v -r16000 -mdirect_write

Usage: tones [OPTION]...
-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

UNINSTALL
Just delete the file.

OTHER THOUGHTS

This tones application is just a simple demo, fiddling about with sines via the alsa interface to show we now can do direct access, and how to have a play, don't expect "Kuitar Hero", "Kubase" or "Kuake" just yet. It's a silly demo.

The final overall aim of the "Kradle" project is simple:
Give us enough information to implement sound support in other applications,

Thanks.

MANY THANKS TO GM FOR BUILDING A VERSION THAT WORKS ON ALL SOUND ENABLED KINDLES: (ATTACHED)
Attached Files
File Type: gz tones.gz (13.3 KB, 275 views)

Last edited by twobob; 01-30-2013 at 08:12 AM. Reason: Added some content information and more up to date info on the thread.
twobob is offline   Reply With Quote
Old 07-02-2012, 09:49 AM   #2
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
Borrowed from Lupines:
https://www.mobileread.com/forums/sho...d.php?t=204275

Now, if you are REALLY brave and want to compile it from scratch
here are the instructions on how to build it from source.

INSTRUCTIONS FOR THE CROSS COMPILER

cross compiler instructions for ARM from here:
http://wiki.debian.org/BuildingCross...Cross_Compiler

----------------------------------------------------------

COMPILING INSTRUCTIONS FOR alsa-lib

Since the Kindle works under ALSA for audio, you must have a cross compiled
version of alsa-lib-1.0.13. I believe a newer version of the ALSA libs
could be used. I just tried this one and it works (Other than an init lag, which seems to affect all version of alsa with this card)

# wget http://gd.tuwien.ac.at/opsys/linux/a...1.0.13.tar.bz2

# bunzip2 alsa-lib-1.0.13.tar.bz2 ; tar -xvf alsa-lib-1.0.13.tar

# cd alsa-lib-1.0.13

# ./configure --with-audio=alsa --prefix=/usr/local/arm-linux --host=arm-linux CC=arm-linux-gnueabi-gcc \
GCC=arm-linux-gnueabi-g++ CFLAGS=-DEMBEDDED CFLAGS="-I/usr/local/arm-linux/include" \
LDFLAGS="-L/usr/local/arm-linux/lib" LD_LIBRARY_PATH="/usr/local/arm-linux/lib"

# make

# make install


-------------------------------------------------
Notes about the card

For the interested: cat /proc/asound/cards

Spoiler:

[root@kindle /dev]# cat /proc/asound/cards
0 [mx35luigi ]: WM8960 - mx35luigi
mx35luigi (WM8960)


[root@kindle /dev]# amixer
Simple mixer control 'Headphone',0
Capabilities: pvolume
Playback channels: Front Left - Front Right
Limits: Playback 0 - 127
Mono:
Front Left: Playback 0 [0%]
Front Right: Playback 16 [13%]

...ELIDED... repeated later in threads



So its an Alsa-sound installation with some interesting bits and bats of settable params.

Specifically: Access to some basic 3D audio emulation and potentially some filtering
(By all accounts this often does nothing)

Automated Volume leveling and some fancy ADSR enums around it
Also uses some sort of target function which looks intriguing
(which could serve as a compressor limiter expander possibly mis-used correctly)

Settings for ADC / DAC inversion with single side inversion
(that may allow for phase effect tricks such as cancellation to be done without code)
Mic Boost is included which may actually come in handy. no doubt horrible.

A mono volume-joined noise gate - which is a nice bonus.

If anyone has any experience with these on the kindle wade in here.
The last time I played with this stuff was a decade ago.

Looks like there are some ways to make what is there do some cool stuff with a bit of thought.
would be nice to have something that provide for direct DSP pumping in place.

http://tldp.org/HOWTO/Alsa-sound-6.html has a run down of much of info I have re-covered here.

the alsamixer and alsactl apps are also present. as yet untested them. Assuming the alsamixer operates "as well" as it does on my main Slackware system: It will be flaky and have odd muting behavior. Have to see.

Comforting to find an alsa setup under the hood.

Last edited by twobob; 01-30-2013 at 08:17 AM. Reason: Legibility and some notes about alsa.
twobob is offline   Reply With Quote
Advert
Old 07-02-2012, 10:14 AM   #3
knc1
Going Viral
knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.
 
knc1's Avatar
 
Posts: 17,212
Karma: 18210809
Join Date: Feb 2012
Location: Central Texas
Device: No K1, PW2, KV, KOA
And the manufacturer's repository tree for that device:
http://opensource.wolfsonmicro.com/c...ed10c4;hb=HEAD
knc1 is offline   Reply With Quote
Old 07-02-2012, 11:04 AM   #4
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
ls of /dev/snd

[root@kindle snd]# ls

controlC0 pcmC0D0c pcmC0D0p timer

p is playback
c is capture

And from Knc1's docs:

Code:
#define WM8960_RATES SNDRV_PCM_RATE_8000_48000

#define WM8960_FORMATS \
	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
	SNDRV_PCM_FMTBIT_S24_LE)
and
Code:
static struct snd_soc_dai_driver wm8960_dai = {
	.name = "wm8960-hifi",
	.playback = {
		.stream_name = "Playback",
		.channels_min = 1,
		.channels_max = 2,
		.rates = WM8960_RATES,
		.formats = WM8960_FORMATS,},
	.capture = {
		.stream_name = "Capture",
		.channels_min = 1,
		.channels_max = 2,
		.rates = WM8960_RATES,
		.formats = WM8960_FORMATS,},
	.ops = &wm8960_dai_ops,
	.symmetric_rates = 1,
};
Attached Thumbnails
Click image for larger version

Name:	Selection_479.png
Views:	377
Size:	125.6 KB
ID:	104612  

Last edited by twobob; 04-19-2013 at 09:30 PM. Reason: added documentary citation
twobob is offline   Reply With Quote
Old 07-02-2012, 11:27 AM   #5
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
ls of /dev/snd

[root@kindle snd]# ls

controlC0 pcmC0D0c pcmC0D0p timer

p is playback
c is capture
So we can pipe sound directly to a device inside of a player? I like that.

It would be nice to know the default format for the raw sound stream (e.g. 8-bit mono 44.1KHz, or whatever). I suppose that is in the other settings you dumped. No time to check right now...
geekmaster is offline   Reply With Quote
Advert
Old 07-02-2012, 08:50 PM   #6
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
etc/asound.conf

The device is a Kindle 3.

Again for the interested:

vi /etc/asound.conf

Spoiler:

Code:
pcm.dmix0 {
type dmix
ipc_key 5155511
slave {
    pcm {
        type hw
        card 0
        device 0
        subdevice 0
        rate 44100
    }
    period_time 100000
    buffer_size 200000
}
}

also

[root@kindle /]# cat /proc/asound/pcm

00-00: WM8960 HiFi WM8960-I2S-0 : : playback 1 : capture 1

and

[root@kindle /etc]# arecord --list-devices (or) aplay --list-devices
Spoiler:

**** List of CAPTURE Hardware Devices ****
card 0: mx35luigi [mx35luigi], device 0: WM8960 HiFi WM8960-I2S-0 []
Subdevices: 1/1
Subdevice #0: subdevice #0

[root@kindle /etc]# aplay --list-pcms
Spoiler:

Code:
PCM list:
hw {
...elided...
			}
			default {
                                     ...elided...

dmix0 {
	type dmix
	ipc_key 5155511
	slave {
		pcm {
			type hw
			card 0
			device 0
			subdevice 0
			rate 44100
		}
		period_time 100000
		buffer_size 200000
	}
}

Perhaps a buffer size there. perhaps not.

so some interesting info. Standard 44.1k 16bit setup in the main.

Version of Alsa:
http://www.linuxfromscratch.org/blfs.../alsa-lib.html
offers the exact version of ALSA used: ALSA Library-1.0.13

Writing direct to /dev/snd/pcmxx0
It's not possible to write directly to the interface as that operation is deprecated, AFAIK on some systems this can be bypassed through the use of an application.

DirectfB
usr/share/directfb-1.2.0 contains cursor.dat perhaps an indication that the box runs http://directfb.org/downloads/Core/D...2.0-rc1.tar.gz

DirectFB offers some interesting options according to their site. I'll have to look it over in depth, this may or may not indicate audio extensions AFAIK, which isn't far.

I don't know haven't got it to compile yet.
stumbled on the lib support. not available for my system easily. ho hum

SDL:
SDL support is not planned in the near future for kindles. Go figure. People have run it embedded AIUI though.

More info on Alsa
There are various excellent notes - and various reasonable ones - littering the net.

http://www.sabi.co.uk/Notes/linuxSoundALSA.html seems fairly decent for entry-concept level stuff.

and the ubiquitous http://www.alsa-project.org/alsa-doc...lib/index.html

Examples of code
I am no code genius, but I can expand on the work of others.

I am aiming to provide decentish, full duplex support one day, and in the short term enough documentation that people can get the sound working.

Last edited by twobob; 07-10-2012 at 07:20 PM. Reason: added some sense, code tags etc
twobob is offline   Reply With Quote
Old 07-02-2012, 09:57 PM   #7
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
it's a start

EDIT: You can find examples on the ASLA ORG page

Last edited by twobob; 07-05-2012 at 03:09 PM. Reason: I like editing, added ref
twobob is offline   Reply With Quote
Old 07-03-2012, 03:15 AM   #8
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
I have used SDL on my K3. I even built it to use a default library path of /mnt/us/SDL, so I did not need to set a library search path. I did have to add an eink update call to the SDL page flipping code, and I also removed sound support (because of my never having succeeded in getting ALSA support working -- why I am interested in your stuff. ).

We are lacking in simple sound support for native mode apps around here. I like SIMPLE monolithic (single .c file) demo programs so that absolute beginners can compile them with tcc, with a minimum of fuss and bother.

Thanks a lot for being the local "sound guy". I am trying to be the "animation guy". Together we can make a complete team (except for other game-design stuff, like artists, map makers, 3D modellers, script writers, and all that other stuff that indy gamers do themselves). We need you.

P.S. I consider SDL to be a bit heavy for a kindle (for the simple demos at least). But it is great for cross-platform stuff.

Last edited by geekmaster; 07-03-2012 at 03:17 AM.
geekmaster is offline   Reply With Quote
Old 07-03-2012, 02:23 PM   #9
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
I am considering other access layer methods as using the alsa headers is very linux specific. I imagine VS users may hate me. but in the interim alsa it is. Perhaps I could post the project file when it's complete.

I did fiddle about going pure C. I'll go back to style considerations after the meat and veg. I fiddled with getting aplay and arecord to do "things" today. back the raw I think.

Build the tools and they will come.
Well, some of them anyways
twobob is offline   Reply With Quote
Old 07-03-2012, 03:24 PM   #10
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
It has taken a relatively long time (months) to get the tools we have together where they can accelerate the development effort. The more tools we get the faster the progress should be (if amazon would quite throwing sand in the gears).
geekmaster is offline   Reply With Quote
Old 07-03-2012, 03:37 PM   #11
knc1
Going Viral
knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.
 
knc1's Avatar
 
Posts: 17,212
Karma: 18210809
Join Date: Feb 2012
Location: Central Texas
Device: No K1, PW2, KV, KOA
Quote:
Originally Posted by geekmaster View Post
It has taken a relatively long time (months) to get the tools we have together where they can accelerate the development effort. The more tools we get the faster the progress should be (if amazon would quite throwing sand in the gears).
“Never ascribe to malice that which can adequately be explained by incompetence.”
Napoleon Bonaparte
knc1 is offline   Reply With Quote
Old 07-03-2012, 06:54 PM   #12
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
Murphy's Law Book Two, More Reasons Why Things Go Wrong. (Directors cut)

1) Because they can.
2) Goto 1
twobob is offline   Reply With Quote
Old 07-04-2012, 09:07 AM   #13
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 knc1 View Post
“Never ascribe to malice that which can adequately be explained by incompetence.”
Napoleon Bonaparte
Ahh.. so they ACCIDENTALLY begain obfuscating the JVM code starting with 5.1.x? Or are you implying that they made an incompetent decision to do that?
geekmaster is offline   Reply With Quote
Old 07-04-2012, 11:08 AM   #14
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
For My Ref. Basically "aplay" implemented in C

I think the original code was named "alsa wave".
If I could find out the author I would credit it. I just tweaked it to use the right devices (and a seggie)
It's public domain stuff anyways. just basic setup and header stuff.

Code:
// A simple C example to play a mono or stereo, 16-bit 44KHz
// WAVE file using ALSA. This goes directly to the first
// audio card (ie, its first set of audio out jacks). It
// uses the snd_pcm_writei() mode of outputting waveform data,
// blocking.
//
// Compile as so to create "salsa":
// gcc -lasound -B /usr/include -o salsa  salsa.c
//
// Run it from a terminal, specifying the name of a WAVE file to play:
// ./salsa wave.wav
// Tweaked by Twobob to work on the kindle

//#include <stdio.h>  or tcclib perhaps (however its not required)
#include <stdlib.h>
#include <unistd.h>

// Include the ALSA .H file that defines ALSA functions/data
#include <alsa/asoundlib.h>

#pragma pack (1)
/////////////////////// WAVE File Stuff /////////////////////
// An IFF file header looks like this
typedef struct _FILE_head
{
	unsigned char	ID[4];	
	// could be {'R', 'I', 'F', 'F'} or {'F', 'O', 'R', 'M'}
	unsigned int	Length;	
	// Length of subsequent file (including remainder of header). 
	//This is in Intel reverse byte order if RIFF, Motorola format if FORM.
	unsigned char	Type[4];	
	// {'W', 'A', 'V', 'E'} or {'A', 'I', 'F', 'F'}
} FILE_head;


// An IFF chunk header looks like this
typedef struct _CHUNK_head
{
	unsigned char ID[4];	// 4 ascii chars that is the chunk ID
	unsigned int	Length;	// Length of subsequent data within this chunk. This is in Intel reverse byte
							// order if RIFF, Motorola format if FORM. Note: this doesn't include any
							// extra byte needed to pad the chunk out to an even size.
} CHUNK_head;

// WAVE fmt chunk
typedef struct _FORMAT {
	short				wFormatTag;
	unsigned short	wChannels;
	unsigned int	dwSamplesPerSec;
	unsigned int	dwAvgBytesPerSec;
	unsigned short	wBlockAlign;
	unsigned short	wBitsPerSample;
  // Note: there may be additional fields here, depending upon wFormatTag
} FORMAT;
#pragma pack()

// Size of the audio card hardware buffer. Here we want it
// set to 1024 16-bit sample points. This is relatively
// small in order to minimize latency. If you have trouble
// with underruns, you may need to increase this, and PERIODSIZE
// (trading off lower latency for more stability)
#define BUFFERSIZE	(2*1024)

// How many sample points the ALSA card plays before it calls
// our callback to fill some more of the audio card's hardware
// buffer. Here we want ALSA to call our callback after every
// 64 sample points have been played
#define PERIODSIZE	(2*64)

// Handle to ALSA (audio card's) playback port
snd_pcm_t				*PlaybackHandle;

// Handle to our callback thread
snd_async_handler_t	*CallbackHandle;

// Points to loaded WAVE file's data
unsigned char			*WavePtr;

// Size (in frames) of loaded WAVE file's data
snd_pcm_uframes_t		WaveSize;

// Sample rate
unsigned short			WaveRate;

// Bit resolution
unsigned char			WaveBits;

// Number of channels in the wave file
unsigned char			WaveChannels;

// The name of the ALSA port we output to. In this case, we're
// directly writing to hardware card 0,0 (ie, first set of audio
// outputs on the first audio card)
static const char		SoundCardPortName[] = "hw";


// For WAVE file loading
static const unsigned char Riff[4]	= { 'R', 'I', 'F', 'F' };
static const unsigned char Wave[4] = { 'W', 'A', 'V', 'E' };
static const unsigned char Fmt[4] = { 'f', 'm', 't', ' ' };
static const unsigned char Data[4] = { 'd', 'a', 't', 'a' };

/********************** compareID() *********************
 * Compares the passed ID str (ie, a ptr to 4 Ascii
 * bytes) with the ID at the passed ptr. Returns TRUE if
 * a match, FALSE if not.
 */

static unsigned char compareID(const unsigned char * id, unsigned char * ptr)
{
	register unsigned char i = 4;

	while (i--)
	{
		if ( *(id)++ != *(ptr)++ ) return(0);
	}
	return(1);
}


/********************** waveLoad() *********************
 * Loads a WAVE file.
 *
 * fn =			Filename to load.
 *
 * RETURNS: 0 if success, non-zero if not.
 *
 * NOTE: Sets the global "WavePtr" to an allocated buffer
 * containing the wave data, and "WaveSize" to the size
 * in sample points.
 */

static unsigned char waveLoad(const char *fn)
{
	const char				*message;
	FILE_head				head;
	register int			inHandle;

	if ((inHandle = open(fn, O_RDONLY)) == -1)
		message = "didn't open";

	// Read in IFF File header
	else
	{
		if (read(inHandle, &head, sizeof(FILE_head)) == sizeof(FILE_head))
		{
			// Is it a RIFF and WAVE?
			if (!compareID(&Riff[0], &head.ID[0]) || !compareID(&Wave[0], &head.Type[0]))
			{
				message = "is not a WAVE file";
				goto bad;
			}

			// Read in next chunk header
			while (read(inHandle, &head, sizeof(CHUNK_head)) == sizeof(CHUNK_head))
			{
				// ============================ Is it a fmt chunk? ===============================
				if (compareID(&Fmt[0], &head.ID[0]))
				{
					FORMAT	format;

					// Read in the remainder of chunk
					if (read(inHandle, &format.wFormatTag, sizeof(FORMAT)) != sizeof(FORMAT)) break;

					// Can't handle compressed WAVE files
					if (format.wFormatTag != 1)
					{
						message = "compressed WAVE not supported";
						goto bad;
					}

					WaveBits = (unsigned char)format.wBitsPerSample;
					WaveRate = (unsigned short)format.dwSamplesPerSec;
					WaveChannels = format.wChannels;
				}

				// ============================ Is it a data chunk? ===============================
				else if (compareID(&Data[0], &head.ID[0]))
				{
					// Size of wave data is head.Length. Allocate a buffer and read in the wave data
					if (!(WavePtr = (unsigned char *)malloc(head.Length)))
					{
						message = "won't fit in RAM";
						goto bad;
					}

					if (read(inHandle, WavePtr, head.Length) != head.Length)
					{
						free(WavePtr);
						break;
					}

					// Store size (in frames)
					WaveSize = (head.Length * 8) / ((unsigned int)WaveBits * (unsigned int)WaveChannels);

					close(inHandle);
					return(0);
				}

				// ============================ Skip this chunk ===============================
				else
				{
					if (head.Length & 1) ++head.Length;  // If odd, round it up to account for pad byte
					lseek(inHandle, head.Length, SEEK_CUR);
				}
			}
		}

		message = "is a bad WAVE file";
bad:	close(inHandle);
	}

	printf("%s %s\n", fn, message);
	return(1);
}


/********************** play_audio() **********************
 * Plays the loaded waveform.
 *
 * NOTE: ALSA sound card's handle must be in the global
 * "PlaybackHandle". A pointer to the wave data must be in
 * the global "WavePtr", and its size of "WaveSize".
 */

static void play_audio(void)
{
	register snd_pcm_uframes_t		count, frames;

	// Output the wave data
	count = 0;
	do
	{
		frames = snd_pcm_writei(PlaybackHandle, WavePtr + count, WaveSize - count);

		// If an error, try to recover from it
		if (frames < 0)
			frames = snd_pcm_recover(PlaybackHandle, frames, 0);
		if (frames < 0)
		{
			printf("Error playing wave: %s\n", snd_strerror(frames));
			break;
		}

		// Update our pointer
		count += frames;

	} while (count < WaveSize);

	// Wait for playback to completely finish
	if (count == WaveSize)
		snd_pcm_drain(PlaybackHandle);
}

/*********************** free_wave_data() *********************
 * Frees any wave data we loaded.
 *
 * NOTE: A pointer to the wave data be in the global
 * "WavePtr".
 */

static void free_wave_data(void)
{
	if (WavePtr) free(WavePtr);
	WavePtr = 0;
}

int main(int argc, char **argv)
{
	// No wave data loaded yet
	WavePtr = 0;

	if (argc < 2)
		printf("You must supply the name of a 16-bit mono WAVE file to play\n");

	// Load the wave file
	else if (!waveLoad(argv[1]))
	{
		register int		err;

		// Open audio card we wish to use for playback
		if ((err = snd_pcm_open(&PlaybackHandle, &SoundCardPortName[0], SND_PCM_STREAM_PLAYBACK, 0)) < 0)
			printf("Can't open audio %s: %s\n", &SoundCardPortName[0], snd_strerror(err));
		else
		{
			switch (WaveBits)
			{
				case 8:
					err = SND_PCM_FORMAT_U8;
					break;
			
				case 16:
					err = SND_PCM_FORMAT_S16;
					break;
			
				case 24:
					err = SND_PCM_FORMAT_S24;
					break;
			
				case 32:
					err = SND_PCM_FORMAT_S32;
					break;
			}
		
			// Set the audio card's hardware parameters (sample rate, bit resolution, etc)
			if ((err = snd_pcm_set_params(PlaybackHandle, err, SND_PCM_ACCESS_RW_INTERLEAVED, WaveChannels, WaveRate, 1, 500000)) < 0)
				printf("Can't set sound parameters: %s\n", snd_strerror(err));

			// Play the waveform
			else
				play_audio();

			// Close sound card
			snd_pcm_close(PlaybackHandle);
		}
	}

	// Free the WAVE data
	free_wave_data();

	return(0);
}
so

[root@kindle us]# aplay -D plughw:0,0 mono.wav
Playing WAVE 'mono.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Mono

and

bash-4.1$ ./salsa ~/mono.wav provide audio functionality, via alsa, except this we can control the guts of. (the port used to play through is "hw")

Many thanks. Heartening however to get something, functional, in c, that emulates the current access to sound (aplay) in a programmable way.

Your thoughts so far. thanks

Last edited by twobob; 07-09-2012 at 04:01 PM. Reason: updating stuff, added updated pcm name details
twobob is offline   Reply With Quote
Old 07-04-2012, 11:14 AM   #15
knc1
Going Viral
knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.knc1 ought to be getting tired of karma fortunes by now.
 
knc1's Avatar
 
Posts: 17,212
Karma: 18210809
Join Date: Feb 2012
Location: Central Texas
Device: No K1, PW2, KV, KOA
There is a project here:
https://www.mobileread.com/forums/sho...d.php?t=174066
that stalled over problems with the Kindle alsa implementation.

Until they hit that snag - they were making good progress in getting VoIP working on the Kindle.

Myself - I don't need a book that will make a telephone call.
But since that mountain was there, the members of that thread where climbing it.

Last edited by knc1; 07-04-2012 at 11:19 AM.
knc1 is offline   Reply With Quote
Reply

Tags
stupid root mistakes


Forum Jump


All times are GMT -4. The time now is 04:41 AM.


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