View Single Post
Old 07-10-2012, 07:26 PM   #79
twobob
( ͡° ͜ʖ ͡°){ʇlnɐɟ ƃǝs}Týr
twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.
 
twobob's Avatar
 
Posts: 6,586
Karma: 6299993
Join Date: Jun 2012
Location: uti gratia usura (Yao ying da ying; Mo ying da yieng)
Device: PW-WIFI|K5-3G+WIFI| K4|K3-3G|DXG|K2| Rooted Nook Touch
Post hmm.. Full duplex with polling is not playing nice. better slow it down

Well. I managed to get my hands on some fairly heavyweight code for full duplex.

Sadly though the initialisation period of the card is making it choke

THE FAIL: straced.
Spoiler:
Code:
// Doing the prep and sync

ioctl(4, SNDRV_PCM_IOCTL_PREPARE, 0xddd18) = 0
ioctl(4, SNDRV_PCM_IOCTL_SYNC_PTR, 0xe8240) = 0
ioctl(5, SNDRV_PCM_IOCTL_PREPARE, 0xe8240) = 0
ioctl(5, SNDRV_PCM_IOCTL_SYNC_PTR, 0xe8170) = 0
ioctl(4, SNDRV_PCM_IOCTL_SYNC_PTR, 0xe8240) = 0
ioctl(4, SNDRV_PCM_IOCTL_START, 0xe8240) = 0

// Run a poll

poll([{fd=4, events=POLLIN|POLLERR|POLLNVAL}, {fd=5, events=POLLOUT|POLLERR|POLLNVAL}], 2, -1) = 1 ([{fd=5, revents=POLLOUT}])
write(1, "demangled poll:  on capture devi"..., 35) = 35
write(1, "demangled poll: POLLOUT  on play"..., 44) = 44
ioctl(5, SNDRV_PCM_IOCTL_WRITEI_FRAMES, 0xbedb6a2c) = 0
ioctl(5, SNDRV_PCM_IOCTL_SYNC_PTR, 0xe8170) = 0
write(1, "wrote 160 frames\n", 17)      = 17
poll([{fd=4, events=POLLIN|POLLERR|POLLNVAL}, {fd=5, events=POLLOUT|POLLERR|POLLNVAL}], 2, -1) = 1 ([{fd=5, revents=POLLOUT}])
write(1, "demangled poll:  on capture devi"..., 35) = 35
write(1, "demangled poll: POLLOUT  on play"..., 44) = 44

// And then boom...

ioctl(5, SNDRV_PCM_IOCTL_WRITEI_FRAMES, 0xbedb6a2c) = -1 EPIPE (Broken pipe)
write(2, "XRUN while writing to playback d"..., 51) = 51
ioctl(4, SNDRV_PCM_IOCTL_PREPARE, 0xddd0c) = -1 EBUSY (Device or resource busy)
ioctl(4, SNDRV_PCM_IOCTL_SYNC_PTR, 0xe8240) = 0
ioctl(4, SNDRV_PCM_IOCTL_START, 0xe8240) = -1 EBADFD (File descriptor in bad state)
ioctl(4, SNDRV_PCM_IOCTL_PREPARE, 0x1f) = -1 EBUSY (Device or resource busy)
ioctl(4, SNDRV_PCM_IOCTL_SYNC_PTR, 0xe8240) = 0
ioctl(4, SNDRV_PCM_IOCTL_START, 0xe8240) = -1 EBADFD (File descriptor in bad state)
write(1, "wrote -32 frames\n", 17)      = 17


This smells to me like unhandled underrun so I'll see if I can get it to double up on the buffer times.

Nice to get some fresh code code to rummage around in.
http://pastebin.com/fCicqctq

EDIT: Even with an enormous period of pcm_period_size=4096 it still dies off after just a few seconds... Perhaps some more rugged error handling is in order.
Spoiler:
Code:
#include <stdio.h>
#include <alsa/asoundlib.h>
#include <string.h>
#include <sched.h>
#include <alloca.h>
#include <stdint.h>

void hexdump(uint8_t *buffer, int len)
{
	int i;
	int addr=0;
	const int bytecount=16;

	fprintf(stderr, "hexdump(): %i bytes\n", len);
	fflush(NULL);

	for (i=0; i<len; i++)
	{
		if ((i % bytecount) == 0)
			fprintf(stderr, "%04x\t", addr);

		fprintf(stderr, "%02x ", buffer[i]);

		if (((i+1) % bytecount) == 0)
		{
			addr += bytecount;
			fprintf(stderr, "\n");
		}
	}

	if (((i+1) % bytecount) != 0)
		fprintf(stderr, "\n");
}

// returns handle of PCM on success, NULL otherwise
snd_pcm_t *setup_pcm(char *device_name, int stream)
{
	int ret;
	snd_pcm_t *handle=NULL;
	snd_pcm_hw_params_t *hwparams=NULL;
	snd_pcm_sw_params_t *swparams=NULL;
	snd_pcm_format_t pcm_format;
	int dir;
	unsigned int pcm_rate_min, pcm_rate_max, pcm_rate, pcm_rate_real;
	snd_pcm_uframes_t pcm_period_size_min, pcm_period_size_max, pcm_period_size;
	snd_pcm_uframes_t pcm_buffer_size_min, pcm_buffer_size_max, pcm_buffer_size, pcm_buffer_size_real;
	snd_pcm_uframes_t start_threshold, stop_threshold;
	snd_pcm_uframes_t avail_min;


	if ((stream != SND_PCM_STREAM_PLAYBACK) && (stream != SND_PCM_STREAM_CAPTURE))
	{
		fprintf(stderr, "invalid stream\n");
		return NULL;
	}

	assert(device_name);

	// open sound card
	if ((ret=snd_pcm_open(&handle, device_name, stream, SND_PCM_NONBLOCK))<0)
	//if ((ret=snd_pcm_open(&handle, device_name, stream, 0))<0)
	{
		fprintf(stderr, "could not open PCM: %s\n", snd_strerror(ret));
		return NULL;
	}

	// auto-alloc and fill hardware params
	snd_pcm_hw_params_alloca(&hwparams);
	snd_pcm_hw_params_any(handle, hwparams);

	// set access format
	snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);

	// set audio format
	pcm_format=SND_PCM_FORMAT_S16_LE;
	snd_pcm_hw_params_set_format (handle, hwparams, pcm_format);

	printf("PCM format is %s, %s, %s with %i bits\n",
		snd_pcm_format_signed(pcm_format)? "signed":"unsigned",
		snd_pcm_format_linear(pcm_format)? "linear":"not linear",
		snd_pcm_format_little_endian (pcm_format)? "LE":"BE",
		snd_pcm_format_width(pcm_format));

	// set channels
	snd_pcm_hw_params_set_channels (handle, hwparams, 1);

	// get minimum and maximum supported rates
	dir=0;
	if ((ret=snd_pcm_hw_params_get_rate_min(hwparams, &pcm_rate_min, &dir))<0)
	{
		fprintf(stderr, "could not get minimum supported rate: %s\n", snd_strerror(ret));
		return NULL;
	}
	dir=0;
	if ((ret=snd_pcm_hw_params_get_rate_max(hwparams, &pcm_rate_max, &dir))<0)
	{
		fprintf(stderr, "could not get maximum supported rate: %s\n", snd_strerror(ret));
		return NULL;
	}

	printf("PCM rate %u - %u Hz\n", pcm_rate_min, pcm_rate_max);

	// check rate
	pcm_rate=8000;
	if ((pcm_rate<pcm_rate_min || pcm_rate>pcm_rate_max))
	{
		fprintf(stderr, "PCM rate out of range\n");
		return NULL;
	}

	// check if exact rate is available and set up rate
	dir=0;
	if ((ret=snd_pcm_hw_params_test_rate(handle, hwparams, pcm_rate, dir))<0)
	{
		dir=0;
		pcm_rate_real=pcm_rate;

		// almost always fails - so pick a sensible one.
		if ((ret=snd_pcm_hw_params_set_rate_near(handle, hwparams, &pcm_rate_real, &dir))<0)
		{
			fprintf(stderr, "could not set nearest PCM rate: %s\n", snd_strerror(ret));
			return NULL;
		}

		fprintf(stderr, "exact rate of %u Hz not possible, using nearest possible rate of %u Hz instead\n", pcm_rate, pcm_rate_real);
		pcm_rate=pcm_rate_real;
	}
	else
	{
		dir=0;
		if ((ret=snd_pcm_hw_params_set_rate(handle, hwparams, pcm_rate, dir))<0)
		{
			fprintf(stderr, "could not set up PCM rate: %s\n", snd_strerror(ret));
			return NULL;
		}
	}

	// get minimum and maximum supported period size
	dir=0;
	if ((ret=snd_pcm_hw_params_get_period_size_min(hwparams, &pcm_period_size_min, &dir))<0)
	{
		fprintf(stderr, "could not get minimum supported period size: %s\n", snd_strerror(ret));
		return NULL;
	}
	dir=0;
	if ((ret=snd_pcm_hw_params_get_period_size_max(hwparams, &pcm_period_size_max, &dir))<0)
	{
		fprintf(stderr, "could not get maximum supported period size: %s\n", snd_strerror(ret));
		return NULL;
	}

	printf("PCM period size: %lu - %lu\n", pcm_period_size_min, pcm_period_size_max);

	// check period size
//	pcm_period_size=160;  This is tiny for a kindle
	pcm_period_size=4096;
	if ((pcm_period_size<pcm_period_size_min || pcm_period_size>pcm_period_size_max))
	{
		fprintf(stderr, "PCM period size out of range\n");
		return NULL;
	}

	// check if exact period size is available
	dir=0;
	if ((ret=snd_pcm_hw_params_test_period_size(handle, hwparams, pcm_period_size, dir))<0)
	{
		fprintf(stderr, "requested period size not available: %s\n", snd_strerror(ret));
		return NULL;
	}

	// set up period size
	dir=0;
	if ((ret=snd_pcm_hw_params_set_period_size(handle, hwparams, pcm_period_size, dir))<0)
	{
		fprintf(stderr, "could not set up PCM rate: %s\n", snd_strerror(ret));
		return NULL;
	}

	// get minimum and maximum supported buffer size
	if ((ret=snd_pcm_hw_params_get_buffer_size_min(hwparams, &pcm_buffer_size_min))<0)
	{
		fprintf(stderr, "could not get minimum supported buffer size: %s\n", snd_strerror(ret));
		return NULL;
	}
	if ((ret=snd_pcm_hw_params_get_buffer_size_max(hwparams, &pcm_buffer_size_max))<0)
	{
		fprintf(stderr, "could not get maximum supported buffer size: %s\n", snd_strerror(ret));
		return NULL;
	}

	printf("PCM buffer size: %lu - %lu\n", pcm_buffer_size_min, pcm_buffer_size_max);

	// check buffer size
	// buffer size: 2*period_size with given format and 1 channel (mono)
	pcm_buffer_size=pcm_period_size*snd_pcm_format_size(pcm_format, 1)*2;
	printf("calculated buffer size: %lu\n", pcm_buffer_size);
	if ((pcm_buffer_size<pcm_buffer_size_min || pcm_buffer_size>pcm_buffer_size_max))
	{
		fprintf(stderr, "PCM period size out of range\n");
		return NULL;
	}

	// check if exact buffer size is available and set up buffer size
	if ((ret=snd_pcm_hw_params_test_buffer_size(handle, hwparams, pcm_buffer_size))<0)
	{
		pcm_buffer_size_real=pcm_buffer_size;
		if ((ret=snd_pcm_hw_params_set_buffer_size_near(handle, hwparams, &pcm_buffer_size_real))<0)
		{
			fprintf(stderr, "could not set nearest buffer size: %s\n", snd_strerror(ret));
			return NULL;
		}

		fprintf(stderr, "exact buffer size of %lu bytes not possible, using nearest possible buffer size %lu instead\n",
			pcm_buffer_size, pcm_buffer_size_real);
		pcm_buffer_size=pcm_buffer_size_real;
	}
	else
	{
		if ((ret=snd_pcm_hw_params_set_buffer_size(handle, hwparams, pcm_buffer_size))<0)
		{
			fprintf(stderr, "could not set PCM buffer size: %s\n", snd_strerror(ret));
			return NULL;
		}
	}

	// set up configured hw params
	if ((ret=snd_pcm_hw_params(handle, hwparams))<0)
	{
		fprintf(stderr, "could not set up hardware params: %s\n", snd_strerror(ret));
		return NULL;
	}

	// alloc and fill software params
	snd_pcm_sw_params_alloca (&swparams);
	snd_pcm_sw_params_current (handle, swparams);

	if ((ret=snd_pcm_sw_params_get_avail_min(swparams, &avail_min))<0)
	{
		fprintf(stderr, "could not get current avail_min setting: %s\n", snd_strerror(ret));
		return NULL;
	}

	printf("avail min: %lu\n", avail_min);

	// when to consider the device to be ready for the next data transfer operation
	if ((ret=snd_pcm_sw_params_set_avail_min(handle, swparams, pcm_period_size))<0)
	{
		fprintf(stderr, "could not set avail_min: %s\n", snd_strerror(ret));
		return NULL;
	}

	// start threshold
	//start_threshold = (double)pcm_rate * (1/1000000);
	start_threshold = pcm_period_size;
	printf("start threshold: %lu frames\n", start_threshold);
	if ((ret=snd_pcm_sw_params_set_start_threshold(handle, swparams, start_threshold))<0)
	{
		fprintf(stderr, "could not set start threshold: %s\n", snd_strerror(ret));
		return NULL;
	}


	// stop threshold
	stop_threshold=2*pcm_period_size;
	printf("stop threshold: %lu frames\n", stop_threshold);
	if ((ret=snd_pcm_sw_params_set_stop_threshold(handle, swparams, stop_threshold))<0)
	{
		fprintf(stderr, "could not set stop threshold: %s\n", snd_strerror(ret));
		return NULL;
	}

	// set software params
	if ((ret=snd_pcm_sw_params(handle, swparams))<0)
	{
		fprintf(stderr, "could not set sw params: %s\n", snd_strerror(ret));
		return NULL;
	}


	return handle;
}

int main(int argc, char **argv)
{
	struct sched_param schp;
	char *device_name=NULL;
	snd_pcm_t *capture_device=NULL, *playback_device=NULL;
	int capture_pollfd_count, playback_pollfd_count, pollfd_count;
	int ret;
	struct pollfd poll_fds[2];
	uint8_t buffer[160*2];
	int i;
	snd_pcm_state_t state;
	snd_pcm_t *device_handle;
	unsigned short revents;

	if (argc != 2)
	{
		printf("Usage: %s <audio device name>\n", argv[0]);
		exit(EXIT_FAILURE);
	}

	device_name=argv[1];

	// try to get high scheduler priority
	memset(&schp, 0, sizeof (schp));
	schp.sched_priority = sched_get_priority_max(SCHED_FIFO);

	if (sched_setscheduler (0, SCHED_FIFO, &schp)<0)
	{
		perror("could not sched_setscheduler");
	}

	// open and configure capture device
	if ((capture_device=setup_pcm(device_name, SND_PCM_STREAM_CAPTURE))==NULL)
	{
		fprintf(stderr, "could not open and set up capture device %s\n", device_name);
		exit(EXIT_FAILURE);
	}

	if ((playback_device=setup_pcm(device_name, SND_PCM_STREAM_PLAYBACK))==NULL)
	{
		fprintf(stderr, "could not open and set up capture device %s\n", device_name);
		exit(EXIT_FAILURE);
	}

	/*
	// try to link capture and playback device so that they start/stop/prepare in sync
	if ((linked=snd_pcm_link(capture_device, playback_device))<0)
	{
		fprintf(stderr, "could not link capture and playback device: %s\n", snd_strerror(linked));
	}
	*/

	// get poll info
	capture_pollfd_count=snd_pcm_poll_descriptors_count(capture_device);
	playback_pollfd_count=snd_pcm_poll_descriptors_count(playback_device);
	pollfd_count=capture_pollfd_count+playback_pollfd_count;

	printf("capture fds: %i, playback fds: %i\n", capture_pollfd_count, playback_pollfd_count);"A Minimal Interrupt-Driven Program"

	assert(capture_pollfd_count==1 && playback_pollfd_count==1);

	// set up poll struct
	memset(poll_fds, 0x00, sizeof(poll_fds));
	snd_pcm_poll_descriptors(capture_device, &poll_fds[0], capture_pollfd_count);
	snd_pcm_poll_descriptors(playback_device, &poll_fds[1], playback_pollfd_count);

	printf("capture poll fd: %i, playback poll fd: %i\n", poll_fds[0].fd, poll_fds[1].fd);
	printf("capture struct: fd: %i, events: %s%s%s, revents: %i\n",
		poll_fds[0].fd,
		(poll_fds[0].events & POLLIN) ? "POLLIN ":"",
		(poll_fds[0].events & POLLOUT) ? "POLLOUT ":"",
		(poll_fds[0].events & POLLERR) ? "POLLERR ":"",
		poll_fds[0].revents);
	printf("playback struct: fd: %i, events: %s%s%s, revents: %i\n",
		poll_fds[1].fd,
		(poll_fds[1].events & POLLIN) ? "POLLIN ":"",
		(poll_fds[1].events & POLLOUT) ? "POLLOUT ":"",
		(poll_fds[1].events & POLLERR) ? "POLLERR ":"",
		poll_fds[1].revents);

	// prepare capture PCM
	if ((ret=snd_pcm_prepare(capture_device))<0)
	{
		fprintf(stderr, "could not prepare capture device: %s\n", snd_strerror(ret));
		exit(EXIT_FAILURE);
	}

	// prepare playback PCM
	if ((ret=snd_pcm_prepare(playback_device))<0)
	{
		fprintf(stderr, "could not prepare playback device: %s\n", snd_strerror(ret));
		exit(EXIT_FAILURE);
	}

	// start capture device
	if ((ret=snd_pcm_start(capture_device))<0)
	{
		fprintf(stderr, "could not start capture device: %s\n", snd_strerror(ret));
		exit(EXIT_FAILURE);
	}

	memset(buffer, 0x00, sizeof(buffer));

	// read samples from capture device and write them to the playback device
	while (1)
	{
		// poll on both devices
		poll(poll_fds, pollfd_count, -1);

		for (i=0; i<pollfd_count; i++)
		{

			if (i==0)
				device_handle=capture_device;
			else
				device_handle=playback_device;

			// get demangled revent
			if ((ret=snd_pcm_poll_descriptors_revents(device_handle, &poll_fds[i], 1, &revents))<0)
			{
				fprintf(stderr, "could not snd_pcm_poll_descriptors_revents: %s\n", snd_strerror(ret));
				exit(EXIT_FAILURE);
			}

			printf("demangled poll: %s%s%s on %s device\n",
				(revents & POLLERR)? "POLLERR ":"",
				(revents & POLLIN)? "POLLIN ":"",
				(revents & POLLOUT)? "POLLOUT ":"",
				(i==0)? "capture":"playback");

			// check if there was an error
			if (revents & POLLERR)
			{
				state=snd_pcm_state(device_handle);

				switch(state)
				{
					case	SND_PCM_STATE_XRUN:
													fprintf(stderr, "XRUN on %s device\n",
														(poll_fds[i].revents & POLLOUT) ? "playback" : "capture");

													// restart
													snd_pcm_prepare(device_handle);
													snd_pcm_start(device_handle);
													break;
					default:
													fprintf(stderr, "unknown state (%i) on %s device\n",
														state, (poll_fds[i].revents & POLLOUT) ? "playback" : "capture");
				}
			}

			// check for new input data
			if (revents & POLLIN)
			{
				if ((ret=snd_pcm_readi(capture_device, buffer, 160))<0)
				{
					if (ret == -EPIPE)
					{
						fprintf(stderr, "XRUN while reading from capture device: %s\n", snd_strerror(ret));
						snd_pcm_prepare(capture_device);
						snd_pcm_start(capture_device);
					}
					else
					{
						fprintf(stderr, "could not read from capture device: %s\n", snd_strerror(ret));
						exit(EXIT_FAILURE);
					}
				}

				printf("read %i frames\n", ret);
				assert(ret==160);

				hexdump(buffer, ret*2);
			}

			// check if we can write the buffer to the playback device
			if (revents & POLLOUT)
			{
				if ((ret=snd_pcm_writei(playback_device, buffer, 160))<0)
				{
					if (ret == -EPIPE)
					{
						fprintf(stderr, "XRUN while writing to playback device: %s\n", snd_strerror(ret));
						snd_pcm_prepare(capture_device);
						snd_pcm_start(capture_device);

					}
					else
					{
						fprintf(stderr, "could not write to playback device: %s\n", snd_strerror(ret));
						exit(EXIT_FAILURE);
					}
				}

				printf("wrote %i frames\n", ret);
				assert(ret==160);

			//	hexdump(buffer, ret*2);
			}
		}
	} // while(1)

	// never reached
	exit(EXIT_SUCCESS);
}

Last edited by twobob; 07-10-2012 at 08:03 PM. Reason: Added further failure :)
twobob is offline   Reply With Quote