( ͡° ͜ʖ ͡°){ʇlnɐɟ ƃǝs}Týr
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
|
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 :)
|