Enhanced, added async and direct (mmap) mode

This commit is contained in:
Jaroslav Kysela 2001-10-18 17:10:06 +00:00
parent 67cc9dbc75
commit 0f67a428be

View file

@ -24,20 +24,40 @@ snd_pcm_sframes_t buffer_size;
snd_pcm_sframes_t period_size; snd_pcm_sframes_t period_size;
snd_output_t *output = NULL; snd_output_t *output = NULL;
static void generate_sine(signed short *samples, int count, double *_phase) static void generate_sine(const snd_pcm_channel_area_t *areas,
snd_pcm_uframes_t offset,
int count, double *_phase)
{ {
double phase = *_phase; double phase = *_phase;
double max_phase = 1.0 / freq; double max_phase = 1.0 / freq;
double step = 1.0 / (double)rate; double step = 1.0 / (double)rate;
double res; double res;
signed short *samples[channels];
int steps[channels];
int chn, ires; int chn, ires;
/* 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 / 16;
samples[chn] += offset * steps[chn];
}
/* fill the channel areas */
while (count-- > 0) { while (count-- > 0) {
res = sin((phase * 2 * M_PI) / max_phase - M_PI) * 32767; res = sin((phase * 2 * M_PI) / max_phase - M_PI) * 32767;
ires = res; ires = res;
for (chn = 0; chn < channels; chn++) for (chn = 0; chn < channels; chn++) {
*samples++ = ires; *samples[chn] = ires;
// printf("phase: %.8f, max_phase: %.8f, res: %.8f, smp = %i\n", phase, max_phase, res, *(samples-1)); samples[chn] += steps[chn];
}
phase += step; phase += step;
if (phase >= max_phase) if (phase >= max_phase)
phase -= max_phase; phase -= max_phase;
@ -46,7 +66,8 @@ static void generate_sine(signed short *samples, int count, double *_phase)
} }
static int set_hwparams(snd_pcm_t *handle, static int set_hwparams(snd_pcm_t *handle,
snd_pcm_hw_params_t *params) snd_pcm_hw_params_t *params,
snd_pcm_access_t access)
{ {
int err, dir; int err, dir;
@ -57,7 +78,7 @@ static int set_hwparams(snd_pcm_t *handle,
return err; return err;
} }
/* set the interleaved read/write format */ /* set the interleaved read/write format */
err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); err = snd_pcm_hw_params_set_access(handle, params, access);
if (err < 0) { if (err < 0) {
printf("Access type not available for playback: %s\n", snd_strerror(err)); printf("Access type not available for playback: %s\n", snd_strerror(err));
return err; return err;
@ -150,12 +171,12 @@ static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams)
static int xrun_recovery(snd_pcm_t *handle, int err) static int xrun_recovery(snd_pcm_t *handle, int err)
{ {
if (err = -EPIPE) { /* underrun */ if (err == -EPIPE) { /* underrun */
err = snd_pcm_prepare(handle); err = snd_pcm_prepare(handle);
if (err < 0) if (err < 0)
printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err)); printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err));
return 0; return 0;
} else if (err = -ESTRPIPE) { } else if (err == -ESTRPIPE) {
while ((err = snd_pcm_resume(handle)) == -EAGAIN) while ((err = snd_pcm_resume(handle)) == -EAGAIN)
sleep(1); /* wait until suspend flag is released */ sleep(1); /* wait until suspend flag is released */
if (err < 0) { if (err < 0) {
@ -172,16 +193,18 @@ static int xrun_recovery(snd_pcm_t *handle, int err)
* Transfer method - write only * Transfer method - write only
*/ */
static int write_loop(snd_pcm_t *handle, signed short *samples) static int write_loop(snd_pcm_t *handle,
signed short *samples,
snd_pcm_channel_area_t *areas)
{ {
int ufds_count;
struct pollfd *ufds;
double phase = 0; double phase = 0;
signed short *ptr; signed short *ptr;
int err, count, cptr; int err, cptr;
while (1) { while (1) {
generate_sine(ptr = samples, cptr = period_size, &phase); generate_sine(areas, 0, period_size, &phase);
ptr = samples;
cptr = period_size;
while (cptr > 0) { while (cptr > 0) {
err = snd_pcm_writei(handle, ptr, cptr); err = snd_pcm_writei(handle, ptr, cptr);
if (err == -EAGAIN) if (err == -EAGAIN)
@ -222,9 +245,10 @@ static int wait_for_poll(struct pollfd *ufds, int count)
} }
} }
static int write_and_poll_loop(snd_pcm_t *handle, signed short *samples) static int write_and_poll_loop(snd_pcm_t *handle,
signed short *samples,
snd_pcm_channel_area_t *areas)
{ {
int ufds_count;
struct pollfd *ufds; struct pollfd *ufds;
double phase = 0; double phase = 0;
signed short *ptr; signed short *ptr;
@ -253,7 +277,9 @@ static int write_and_poll_loop(snd_pcm_t *handle, signed short *samples)
return err; return err;
} }
generate_sine(ptr = samples, cptr = period_size, &phase); generate_sine(areas, 0, period_size, &phase);
ptr = samples;
cptr = period_size;
while (cptr > 0) { while (cptr > 0) {
err = snd_pcm_writei(handle, ptr, cptr); err = snd_pcm_writei(handle, ptr, cptr);
if (err < 0) { if (err < 0) {
@ -278,19 +304,140 @@ static int write_and_poll_loop(snd_pcm_t *handle, signed short *samples)
} }
} }
/*
* 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("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);
}
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);
}
}
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,
snd_pcm_channel_area_t *areas)
{
double phase = 0;
const snd_pcm_channel_area_t *my_areas;
snd_pcm_uframes_t offset, frames;
int err, first = 1;
while (1) {
frames = period_size;
err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames);
if (err < 0) {
printf("MMAP begin error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if (frames > period_size)
frames = period_size;
generate_sine(my_areas, offset, frames, &phase);
err = snd_pcm_mmap_commit(handle, offset, frames);
if (err < 0) {
printf("MMAP commit error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if (frames == 0 && first) { /* trigger playback */
first = 0;
err = snd_pcm_start(handle);
if (err < 0) {
printf("Start error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
}
}
}
/* /*
* *
*/ */
struct transfer_method { struct transfer_method {
const char *name; const char *name;
int (*transfer_loop)(snd_pcm_t *handle, signed short *samples); 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[] = { static struct transfer_method transfer_methods[] = {
{ "write", write_loop }, { "write", SND_PCM_ACCESS_RW_INTERLEAVED, write_loop },
{ "write_and_poll", write_and_poll_loop }, { "write_and_poll", SND_PCM_ACCESS_RW_INTERLEAVED, write_and_poll_loop },
{ NULL, NULL } { "async", SND_PCM_ACCESS_RW_INTERLEAVED, async_loop },
{ "direct_interleaved", SND_PCM_ACCESS_MMAP_INTERLEAVED, direct_loop },
{ "direct_noninterleaved", SND_PCM_ACCESS_MMAP_NONINTERLEAVED, direct_loop },
{ NULL, SND_PCM_ACCESS_RW_INTERLEAVED, NULL }
}; };
static void help(void) static void help(void)
@ -336,13 +483,13 @@ int main(int argc, char *argv[])
{NULL, 0, NULL, 0}, {NULL, 0, NULL, 0},
}; };
snd_pcm_t *handle; snd_pcm_t *handle;
char *buffer;
int err, morehelp; int err, morehelp;
snd_pcm_hw_params_t *hwparams; snd_pcm_hw_params_t *hwparams;
snd_pcm_sw_params_t *swparams; snd_pcm_sw_params_t *swparams;
double phase;
int method = 0; int method = 0;
signed short *samples; signed short *samples;
int chn;
snd_pcm_channel_area_t *areas;
snd_pcm_hw_params_alloca(&hwparams); snd_pcm_hw_params_alloca(&hwparams);
snd_pcm_sw_params_alloca(&swparams); snd_pcm_sw_params_alloca(&swparams);
@ -410,12 +557,12 @@ int main(int argc, char *argv[])
printf("Sine wave rate is %.4fHz\n", freq); printf("Sine wave rate is %.4fHz\n", freq);
printf("Using transfer method: %s\n", transfer_methods[method].name); printf("Using transfer method: %s\n", transfer_methods[method].name);
if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) { if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
printf("Playback open error: %s\n", snd_strerror(err)); printf("Playback open error: %s\n", snd_strerror(err));
return 0; return 0;
} }
if ((err = set_hwparams(handle, hwparams)) < 0) { if ((err = set_hwparams(handle, hwparams, transfer_methods[method].access)) < 0) {
printf("Setting of hwparams failed: %s\n", snd_strerror(err)); printf("Setting of hwparams failed: %s\n", snd_strerror(err));
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@ -429,11 +576,24 @@ int main(int argc, char *argv[])
printf("No enough memory\n"); printf("No enough memory\n");
exit(EXIT_FAILURE); 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 * 16;
areas[chn].step = channels * 16;
}
err = transfer_methods[method].transfer_loop(handle, samples); err = transfer_methods[method].transfer_loop(handle, samples, areas);
if (err < 0) if (err < 0)
printf("Transfer failed: %s\n", snd_strerror(err)); printf("Transfer failed: %s\n", snd_strerror(err));
free(areas);
free(samples);
snd_pcm_close(handle); snd_pcm_close(handle);
return 0; return 0;
} }