On Mon, 2001-10-15 at 02:46, Jaroslav Kysela wrote:
> On 15 Oct 2001, Josh Green wrote:
>
> > I finally got around to doing a full duplex test with my SB Live card.
> > At first I was experiencing the same problems I was having a while back
> > (although to a lesser degree) with clicks increasing/decreasing in
> > intensity over time. I was searching the ALSA archives (with google as
>
> There shouldn't be any clicks (only on xruns). Could you show me your
> code?
>
Sure, I'll attach it to this email. I've been testing it for a while now
and I'm realizing that the problem hasn't completely gone away. Since
the start/prepare operations happen in sync, each time an xrun occurs
the two devices are resynced. If I start it as root (I'm setting
SCHED_FIFO currently) the test will rarely get xruns. For the most part
the test runs smooth, but every once in a while little pops and clicks
will be heard in series. They increase in # and then decrease and
dissapear again (in a periodic fashion, sometimes sounds like a
scratched vinyl record). This is the same problem, just a lot harder to
reproduce, since it seems the two buffers are much more syncronized.
> > geocrawler is useless) when alsa-lib/test/latency.c was brought to my
> > attention. This program works fine with full duplex, no clicks (except
> > on overruns of course). I did notice that it likes to take 100% CPU
> > which isn't so nice when running SCHED_RR. It looks like "snd_pcm_link"
> > is what does the magic. Putting this in my test program makes things
> > work all nice and sweet :) Is this function call required in a program
> > to synchronize PCMs or can this be done externally (asound.conf?).
>
> This call is a part of API and it changes the behaviour of some functions
> (it means that all operations are synced and you need to start/stop only
> one stream from the linked group).
>
Yeah. But does a program need to have special support for full duplex
mode or can a program that has a configurable input and output device be
used without problems? (ecasound in particular)
> Jaroslav
>
--
Josh Green
Smurf Sound Font Editor (http://smurf.sourceforge.net)
#include <stdio.h>
#include <alsa/asoundlib.h>
#include <string.h>
#include <sched.h>
#define OK 0
#define FAIL 1
char *audiobuf = NULL;
int audiobuf_bytes = 0;
snd_pcm_t *cards[2] = { NULL };
int fdcount = 0;
struct pollfd *ufds = NULL;
int
setup_device (int index, char *device, int read)
{
int err;
snd_pcm_t *handle;
snd_pcm_hw_params_t *params; /* Hardware parameters */
snd_pcm_sw_params_t *swparams; /* Software parameters */
int period_size; /* Number of frames in period */
int period_bytes; /* size of period */
int buffer_size; /* size of entire audio buffer */
unsigned int rate;
snd_pcm_uframes_t start_threshold, stop_threshold;
int count;
/* Open output sound card */
if (err = snd_pcm_open (&handle, device,
read ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE,
SND_PCM_NONBLOCK) < 0)
{
fprintf (stderr, "snd_pcm_open failed: %s\n", snd_strerror (err));
return (FAIL);
}
cards[index] = handle;
snd_pcm_hw_params_alloca (¶ms);
snd_pcm_sw_params_alloca (&swparams);
snd_pcm_hw_params_any (handle, params);
snd_pcm_hw_params_set_access(handle, params,
SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format (handle, params,
SND_PCM_FORMAT_S16_LE);
snd_pcm_hw_params_set_channels (handle, params, 1);
rate = snd_pcm_hw_params_set_rate_near (handle, params, 44100, 0);
period_size = 384;
if (snd_pcm_hw_params_set_period_size (handle, params, period_size, 0) < 0)
{
fprintf (stderr, "Failed to set period size!\n");
return (FAIL);
}
buffer_size = period_size * 2;
if (snd_pcm_hw_params_set_buffer_size (handle, params, buffer_size) < 0)
{
fprintf (stderr, "Failed to set buffer size!\n");
return (FAIL);
}
err = snd_pcm_hw_params (handle, params);
/* set software parameters */
snd_pcm_sw_params_current (handle, swparams);
snd_pcm_sw_params_set_sleep_min (handle, swparams, 0);
snd_pcm_sw_params_set_avail_min (handle, swparams, period_size);
start_threshold = (double) rate * (1 / 1000000);
snd_pcm_sw_params_set_start_threshold(handle, swparams, start_threshold);
stop_threshold = buffer_size;
snd_pcm_sw_params_set_stop_threshold(handle, swparams, stop_threshold);
snd_pcm_sw_params (handle, swparams);
/* Prepare our buffer */
period_bytes = period_size * 2; /* 2 bytes for 16 bit */
if (audiobuf_bytes == 0)
{
audiobuf = malloc (period_bytes);
audiobuf_bytes = period_bytes;
}
else if (audiobuf_bytes != period_bytes)
{
fprintf (stderr, "audio buffers not same size (%d != %d)!\n",
audiobuf_bytes, period_bytes);
return (FAIL);
}
count = snd_pcm_poll_descriptors_count (handle);
if (count <= 0)
{
fprintf (stderr, "invalid poll descriptors count!\n");
return (FAIL);
}
if (fdcount == 0)
ufds = malloc (sizeof (struct pollfd) * count);
else ufds = realloc (ufds, sizeof (struct pollfd) * (fdcount + count));
snd_pcm_poll_descriptors (handle, &ufds[fdcount], count);
fdcount += count;
return (OK);
}
int
main (void)
{
int i;
short revents;
int err;
struct sched_param schp;
memset (&schp, 0, sizeof (schp));
schp.sched_priority = sched_get_priority_max (SCHED_FIFO);
if (sched_setscheduler (0, SCHED_FIFO, &schp) != 0)
fprintf (stderr, "Failed to run SCHED_FIFO\n");
/* Open output sound card */
if (setup_device (0, "plughw:0,0", 1) != OK)
{
fprintf (stderr, "Failed to setup output device\n");
exit (1);
}
/* Open input sound card */
if (setup_device (1, "plughw:0,0", 0) != OK)
{
fprintf (stderr, "Failed to setup input device\n");
exit (1);
}
if (snd_pcm_link (cards[1], cards[0]) < 0)
{
fprintf (stderr, "Failed to link devices\n");
exit (1);
}
snd_pcm_start (cards[1]);
do
{
poll (ufds, fdcount, -1);
for (i=0; i < fdcount; i++)
{
revents = ufds[i].revents;
if (revents & POLLERR)
{
snd_pcm_t *handle;
snd_pcm_state_t state;
if (revents & POLLOUT) handle = cards[0];
else handle = cards[1];
switch ((state = snd_pcm_state (handle)))
{
case SND_PCM_STATE_XRUN:
fprintf (stderr, "XRUN on %s\n",
(revents & POLLOUT != 0) ? "output" : "input");
snd_pcm_prepare (handle);
snd_pcm_start (handle);
break;
default:
fprintf (stderr, "Bad state %d on %s\n", state,
(revents & POLLOUT != 0) ? "output" : "input");
}
}
else if (revents & POLLOUT)
{
if (err = snd_pcm_writei (cards[0], audiobuf,
audiobuf_bytes) < 0)
{
if (err == -EPIPE)
{
fprintf (stderr, "writei XRUN on output\n");
snd_pcm_prepare (cards[0]);
// snd_pcm_start (cards[0]);
}
}
}
else if (revents & POLLIN)
{
if (err = snd_pcm_readi (cards[1], audiobuf, audiobuf_bytes) < 0)
{
if (err == -EPIPE)
{
fprintf (stderr, "readi XRUN on input\n");
snd_pcm_prepare (cards[1]);
// snd_pcm_start (cards[1]);
}
}
}
}
}
while (1);
exit (0);
}