Main Page | Modules | Data Structures | File List | Data Fields | Globals | Related Pages | Examples

/test/pcm.c

/* * 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 "../include/asoundlib.h" #include <sys/time.h> #include <math.h> char *device = "plughw:0,0"; /* playback device */ snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */ unsigned int rate = 44100; /* stream rate */ unsigned int channels = 1; /* count of channels */ unsigned int buffer_time = 500000; /* ring buffer length in us */ unsigned int period_time = 100000; /* period time in us */ double freq = 440; /* sinusoidal wave frequency in Hz */ int verbose = 0; /* verbose flag */ snd_pcm_sframes_t buffer_size; snd_pcm_sframes_t period_size; 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) { static double max_phase = 2. * M_PI; double phase = *_phase; double step = max_phase*freq/(double)rate; double res; signed short *samples[channels]; int steps[channels]; unsigned int chn; int 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) { res = sin(phase) * 32767; ires = res; for (chn = 0; chn < channels; chn++) { *samples[chn] = ires; samples[chn] += steps[chn]; } phase += step; if (phase >= max_phase) phase -= max_phase; } *_phase = phase; } static int set_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params, snd_pcm_access_t access) { unsigned int rrate; 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 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, &buffer_size); if (err < 0) { printf("Unable to get buffer size for playback: %s\n", snd_strerror(err)); return err; } /* 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, &period_size, &dir); if (err < 0) { printf("Unable to get period size for playback: %s\n", snd_strerror(err)); return err; } /* 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 */ err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_size); if (err < 0) { printf("Unable to set avail min for playback: %s\n", snd_strerror(err)); return err; } /* align all transfers to 1 sample */ err = snd_pcm_sw_params_set_xfer_align(handle, swparams, 1); if (err < 0) { printf("Unable to set transfer align for playback: %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 (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("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 - 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, snd_pcm_channel_area_t *areas) { 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, snd_pcm_channel_area_t *areas) { 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" "-v,--verbose show the PCM setup parameters\n" "\n"); printf("Recognized sample formats are:"); for (k = 0; k < SND_PCM_FORMAT_LAST; ++(unsigned long) 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'}, {"verbose", 1, NULL, 'v'}, {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:v", 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 'v': verbose = 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_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 * 16; areas[chn].step = channels * 16; } 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; }
00001 /* 00002 * This small demo sends a simple sinusoidal wave to your speakers. 00003 */ 00004 00005 #include <stdio.h> 00006 #include <stdlib.h> 00007 #include <string.h> 00008 #include <sched.h> 00009 #include <errno.h> 00010 #include <getopt.h> 00011 #include "../include/asoundlib.h" 00012 #include <sys/time.h> 00013 #include <math.h> 00014 00015 char *device = "plughw:0,0"; /* playback device */ 00016 snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */ 00017 unsigned int rate = 44100; /* stream rate */ 00018 unsigned int channels = 1; /* count of channels */ 00019 unsigned int buffer_time = 500000; /* ring buffer length in us */ 00020 unsigned int period_time = 100000; /* period time in us */ 00021 double freq = 440; /* sinusoidal wave frequency in Hz */ 00022 int verbose = 0; /* verbose flag */ 00023 00024 snd_pcm_sframes_t buffer_size; 00025 snd_pcm_sframes_t period_size; 00026 snd_output_t *output = NULL; 00027 00028 static void generate_sine(const snd_pcm_channel_area_t *areas, 00029 snd_pcm_uframes_t offset, 00030 int count, double *_phase) 00031 { 00032 static double max_phase = 2. * M_PI; 00033 double phase = *_phase; 00034 double step = max_phase*freq/(double)rate; 00035 double res; 00036 signed short *samples[channels]; 00037 int steps[channels]; 00038 unsigned int chn; 00039 int ires; 00040 00041 /* verify and prepare the contents of areas */ 00042 for (chn = 0; chn < channels; chn++) { 00043 if ((areas[chn].first % 8) != 0) { 00044 printf("areas[%i].first == %i, aborting...\n", chn, areas[chn].first); 00045 exit(EXIT_FAILURE); 00046 } 00047 samples[chn] = (signed short *)(((unsigned char *)areas[chn].addr) + (areas[chn].first / 8)); 00048 if ((areas[chn].step % 16) != 0) { 00049 printf("areas[%i].step == %i, aborting...\n", chn, areas[chn].step); 00050 exit(EXIT_FAILURE); 00051 } 00052 steps[chn] = areas[chn].step / 16; 00053 samples[chn] += offset * steps[chn]; 00054 } 00055 /* fill the channel areas */ 00056 while (count-- > 0) { 00057 res = sin(phase) * 32767; 00058 ires = res; 00059 for (chn = 0; chn < channels; chn++) { 00060 *samples[chn] = ires; 00061 samples[chn] += steps[chn]; 00062 } 00063 phase += step; 00064 if (phase >= max_phase) 00065 phase -= max_phase; 00066 } 00067 *_phase = phase; 00068 } 00069 00070 static int set_hwparams(snd_pcm_t *handle, 00071 snd_pcm_hw_params_t *params, 00072 snd_pcm_access_t access) 00073 { 00074 unsigned int rrate; 00075 int err, dir; 00076 00077 /* choose all parameters */ 00078 err = snd_pcm_hw_params_any(handle, params); 00079 if (err < 0) { 00080 printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err)); 00081 return err; 00082 } 00083 /* set the interleaved read/write format */ 00084 err = snd_pcm_hw_params_set_access(handle, params, access); 00085 if (err < 0) { 00086 printf("Access type not available for playback: %s\n", snd_strerror(err)); 00087 return err; 00088 } 00089 /* set the sample format */ 00090 err = snd_pcm_hw_params_set_format(handle, params, format); 00091 if (err < 0) { 00092 printf("Sample format not available for playback: %s\n", snd_strerror(err)); 00093 return err; 00094 } 00095 /* set the count of channels */ 00096 err = snd_pcm_hw_params_set_channels(handle, params, channels); 00097 if (err < 0) { 00098 printf("Channels count (%i) not available for playbacks: %s\n", channels, snd_strerror(err)); 00099 return err; 00100 } 00101 /* set the stream rate */ 00102 rrate = rate; 00103 err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0); 00104 if (err < 0) { 00105 printf("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err)); 00106 return err; 00107 } 00108 if (rrate != rate) { 00109 printf("Rate doesn't match (requested %iHz, get %iHz)\n", rate, err); 00110 return -EINVAL; 00111 } 00112 /* set the buffer time */ 00113 err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir); 00114 if (err < 0) { 00115 printf("Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err)); 00116 return err; 00117 } 00118 err = snd_pcm_hw_params_get_buffer_size(params, &buffer_size); 00119 if (err < 0) { 00120 printf("Unable to get buffer size for playback: %s\n", snd_strerror(err)); 00121 return err; 00122 } 00123 /* set the period time */ 00124 err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir); 00125 if (err < 0) { 00126 printf("Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err)); 00127 return err; 00128 } 00129 err = snd_pcm_hw_params_get_period_size(params, &period_size, &dir); 00130 if (err < 0) { 00131 printf("Unable to get period size for playback: %s\n", snd_strerror(err)); 00132 return err; 00133 } 00134 /* write the parameters to device */ 00135 err = snd_pcm_hw_params(handle, params); 00136 if (err < 0) { 00137 printf("Unable to set hw params for playback: %s\n", snd_strerror(err)); 00138 return err; 00139 } 00140 return 0; 00141 } 00142 00143 static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams) 00144 { 00145 int err; 00146 00147 /* get the current swparams */ 00148 err = snd_pcm_sw_params_current(handle, swparams); 00149 if (err < 0) { 00150 printf("Unable to determine current swparams for playback: %s\n", snd_strerror(err)); 00151 return err; 00152 } 00153 /* start the transfer when the buffer is almost full: */ 00154 /* (buffer_size / avail_min) * avail_min */ 00155 err = snd_pcm_sw_params_set_start_threshold(handle, swparams, (buffer_size / period_size) * period_size); 00156 if (err < 0) { 00157 printf("Unable to set start threshold mode for playback: %s\n", snd_strerror(err)); 00158 return err; 00159 } 00160 /* allow the transfer when at least period_size samples can be processed */ 00161 err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_size); 00162 if (err < 0) { 00163 printf("Unable to set avail min for playback: %s\n", snd_strerror(err)); 00164 return err; 00165 } 00166 /* align all transfers to 1 sample */ 00167 err = snd_pcm_sw_params_set_xfer_align(handle, swparams, 1); 00168 if (err < 0) { 00169 printf("Unable to set transfer align for playback: %s\n", snd_strerror(err)); 00170 return err; 00171 } 00172 /* write the parameters to the playback device */ 00173 err = snd_pcm_sw_params(handle, swparams); 00174 if (err < 0) { 00175 printf("Unable to set sw params for playback: %s\n", snd_strerror(err)); 00176 return err; 00177 } 00178 return 0; 00179 } 00180 00181 /* 00182 * Underrun and suspend recovery 00183 */ 00184 00185 static int xrun_recovery(snd_pcm_t *handle, int err) 00186 { 00187 if (err == -EPIPE) { /* under-run */ 00188 err = snd_pcm_prepare(handle); 00189 if (err < 0) 00190 printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err)); 00191 return 0; 00192 } else if (err == -ESTRPIPE) { 00193 while ((err = snd_pcm_resume(handle)) == -EAGAIN) 00194 sleep(1); /* wait until the suspend flag is released */ 00195 if (err < 0) { 00196 err = snd_pcm_prepare(handle); 00197 if (err < 0) 00198 printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err)); 00199 } 00200 return 0; 00201 } 00202 return err; 00203 } 00204 00205 /* 00206 * Transfer method - write only 00207 */ 00208 00209 static int write_loop(snd_pcm_t *handle, 00210 signed short *samples, 00211 snd_pcm_channel_area_t *areas) 00212 { 00213 double phase = 0; 00214 signed short *ptr; 00215 int err, cptr; 00216 00217 while (1) { 00218 generate_sine(areas, 0, period_size, &phase); 00219 ptr = samples; 00220 cptr = period_size; 00221 while (cptr > 0) { 00222 err = snd_pcm_writei(handle, ptr, cptr); 00223 if (err == -EAGAIN) 00224 continue; 00225 if (err < 0) { 00226 if (xrun_recovery(handle, err) < 0) { 00227 printf("Write error: %s\n", snd_strerror(err)); 00228 exit(EXIT_FAILURE); 00229 } 00230 break; /* skip one period */ 00231 } 00232 ptr += err * channels; 00233 cptr -= err; 00234 } 00235 } 00236 } 00237 00238 /* 00239 * Transfer method - write and wait for room in buffer using poll 00240 */ 00241 00242 static int wait_for_poll(snd_pcm_t *handle, struct pollfd *ufds, unsigned int count) 00243 { 00244 unsigned short revents; 00245 00246 while (1) { 00247 poll(ufds, count, -1); 00248 snd_pcm_poll_descriptors_revents(handle, ufds, count, &revents); 00249 if (revents & POLLERR) 00250 return -EIO; 00251 if (revents & POLLOUT) 00252 return 0; 00253 } 00254 } 00255 00256 static int write_and_poll_loop(snd_pcm_t *handle, 00257 signed short *samples, 00258 snd_pcm_channel_area_t *areas) 00259 { 00260 struct pollfd *ufds; 00261 double phase = 0; 00262 signed short *ptr; 00263 int err, count, cptr, init; 00264 00265 count = snd_pcm_poll_descriptors_count (handle); 00266 if (count <= 0) { 00267 printf("Invalid poll descriptors count\n"); 00268 return count; 00269 } 00270 00271 ufds = malloc(sizeof(struct pollfd) * count); 00272 if (ufds == NULL) { 00273 printf("No enough memory\n"); 00274 return -ENOMEM; 00275 } 00276 if ((err = snd_pcm_poll_descriptors(handle, ufds, count)) < 0) { 00277 printf("Unable to obtain poll descriptors for playback: %s\n", snd_strerror(err)); 00278 return err; 00279 } 00280 00281 init = 1; 00282 while (1) { 00283 if (!init) { 00284 err = wait_for_poll(handle, ufds, count); 00285 if (err < 0) { 00286 if (snd_pcm_state(handle) == SND_PCM_STATE_XRUN || 00287 snd_pcm_state(handle) == SND_PCM_STATE_SUSPENDED) { 00288 err = snd_pcm_state(handle) == SND_PCM_STATE_XRUN ? -EPIPE : -ESTRPIPE; 00289 if (xrun_recovery(handle, err) < 0) { 00290 printf("Write error: %s\n", snd_strerror(err)); 00291 exit(EXIT_FAILURE); 00292 } 00293 init = 1; 00294 } else { 00295 printf("Wait for poll failed\n"); 00296 return err; 00297 } 00298 } 00299 } 00300 00301 generate_sine(areas, 0, period_size, &phase); 00302 ptr = samples; 00303 cptr = period_size; 00304 while (cptr > 0) { 00305 err = snd_pcm_writei(handle, ptr, cptr); 00306 if (err < 0) { 00307 if (xrun_recovery(handle, err) < 0) { 00308 printf("Write error: %s\n", snd_strerror(err)); 00309 exit(EXIT_FAILURE); 00310 } 00311 init = 1; 00312 break; /* skip one period */ 00313 } 00314 if (snd_pcm_state(handle) == SND_PCM_STATE_RUNNING) 00315 init = 0; 00316 ptr += err * channels; 00317 cptr -= err; 00318 if (cptr == 0) 00319 break; 00320 /* it is possible, that the initial buffer cannot store */ 00321 /* all data from the last period, so wait awhile */ 00322 err = wait_for_poll(handle, ufds, count); 00323 if (err < 0) { 00324 if (snd_pcm_state(handle) == SND_PCM_STATE_XRUN || 00325 snd_pcm_state(handle) == SND_PCM_STATE_SUSPENDED) { 00326 err = snd_pcm_state(handle) == SND_PCM_STATE_XRUN ? -EPIPE : -ESTRPIPE; 00327 if (xrun_recovery(handle, err) < 0) { 00328 printf("Write error: %s\n", snd_strerror(err)); 00329 exit(EXIT_FAILURE); 00330 } 00331 init = 1; 00332 } else { 00333 printf("Wait for poll failed\n"); 00334 return err; 00335 } 00336 } 00337 } 00338 } 00339 } 00340 00341 /* 00342 * Transfer method - asynchronous notification 00343 */ 00344 00345 struct async_private_data { 00346 signed short *samples; 00347 snd_pcm_channel_area_t *areas; 00348 double phase; 00349 }; 00350 00351 static void async_callback(snd_async_handler_t *ahandler) 00352 { 00353 snd_pcm_t *handle = snd_async_handler_get_pcm(ahandler); 00354 struct async_private_data *data = snd_async_handler_get_callback_private(ahandler); 00355 signed short *samples = data->samples; 00356 snd_pcm_channel_area_t *areas = data->areas; 00357 snd_pcm_sframes_t avail; 00358 int err; 00359 00360 avail = snd_pcm_avail_update(handle); 00361 while (avail >= period_size) { 00362 generate_sine(areas, 0, period_size, &data->phase); 00363 err = snd_pcm_writei(handle, samples, period_size); 00364 if (err < 0) { 00365 printf("Initial write error: %s\n", snd_strerror(err)); 00366 exit(EXIT_FAILURE); 00367 } 00368 if (err != period_size) { 00369 printf("Initial write error: written %i expected %li\n", err, period_size); 00370 exit(EXIT_FAILURE); 00371 } 00372 avail = snd_pcm_avail_update(handle); 00373 } 00374 } 00375 00376 static int async_loop(snd_pcm_t *handle, 00377 signed short *samples, 00378 snd_pcm_channel_area_t *areas) 00379 { 00380 struct async_private_data data; 00381 snd_async_handler_t *ahandler; 00382 int err, count; 00383 00384 data.samples = samples; 00385 data.areas = areas; 00386 data.phase = 0; 00387 err = snd_async_add_pcm_handler(&ahandler, handle, async_callback, &data); 00388 if (err < 0) { 00389 printf("Unable to register async handler\n"); 00390 exit(EXIT_FAILURE); 00391 } 00392 for (count = 0; count < 2; count++) { 00393 generate_sine(areas, 0, period_size, &data.phase); 00394 err = snd_pcm_writei(handle, samples, period_size); 00395 if (err < 0) { 00396 printf("Initial write error: %s\n", snd_strerror(err)); 00397 exit(EXIT_FAILURE); 00398 } 00399 if (err != period_size) { 00400 printf("Initial write error: written %i expected %li\n", err, period_size); 00401 exit(EXIT_FAILURE); 00402 } 00403 } 00404 err = snd_pcm_start(handle); 00405 if (err < 0) { 00406 printf("Start error: %s\n", snd_strerror(err)); 00407 exit(EXIT_FAILURE); 00408 } 00409 00410 /* because all other work is done in the signal handler, 00411 suspend the process */ 00412 while (1) { 00413 sleep(1); 00414 } 00415 } 00416 00417 /* 00418 * Transfer method - asynchronous notification + direct write 00419 */ 00420 00421 static void async_direct_callback(snd_async_handler_t *ahandler) 00422 { 00423 snd_pcm_t *handle = snd_async_handler_get_pcm(ahandler); 00424 struct async_private_data *data = snd_async_handler_get_callback_private(ahandler); 00425 const snd_pcm_channel_area_t *my_areas; 00426 snd_pcm_uframes_t offset, frames, size; 00427 snd_pcm_sframes_t avail, commitres; 00428 snd_pcm_state_t state; 00429 int first = 0, err; 00430 00431 while (1) { 00432 state = snd_pcm_state(handle); 00433 if (state == SND_PCM_STATE_XRUN) { 00434 err = xrun_recovery(handle, -EPIPE); 00435 if (err < 0) { 00436 printf("XRUN recovery failed: %s\n", snd_strerror(err)); 00437 exit(EXIT_FAILURE); 00438 } 00439 first = 1; 00440 } else if (state == SND_PCM_STATE_SUSPENDED) { 00441 err = xrun_recovery(handle, -ESTRPIPE); 00442 if (err < 0) { 00443 printf("SUSPEND recovery failed: %s\n", snd_strerror(err)); 00444 exit(EXIT_FAILURE); 00445 } 00446 } 00447 avail = snd_pcm_avail_update(handle); 00448 if (avail < 0) { 00449 err = xrun_recovery(handle, avail); 00450 if (err < 0) { 00451 printf("avail update failed: %s\n", snd_strerror(err)); 00452 exit(EXIT_FAILURE); 00453 } 00454 first = 1; 00455 continue; 00456 } 00457 if (avail < period_size) { 00458 if (first) { 00459 first = 0; 00460 err = snd_pcm_start(handle); 00461 if (err < 0) { 00462 printf("Start error: %s\n", snd_strerror(err)); 00463 exit(EXIT_FAILURE); 00464 } 00465 } else { 00466 break; 00467 } 00468 continue; 00469 } 00470 size = period_size; 00471 while (size > 0) { 00472 frames = size; 00473 err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames); 00474 if (err < 0) { 00475 if ((err = xrun_recovery(handle, err)) < 0) { 00476 printf("MMAP begin avail error: %s\n", snd_strerror(err)); 00477 exit(EXIT_FAILURE); 00478 } 00479 first = 1; 00480 } 00481 generate_sine(my_areas, offset, frames, &data->phase); 00482 commitres = snd_pcm_mmap_commit(handle, offset, frames); 00483 if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) { 00484 if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) { 00485 printf("MMAP commit error: %s\n", snd_strerror(err)); 00486 exit(EXIT_FAILURE); 00487 } 00488 first = 1; 00489 } 00490 size -= frames; 00491 } 00492 } 00493 } 00494 00495 static int async_direct_loop(snd_pcm_t *handle, 00496 signed short *samples, 00497 snd_pcm_channel_area_t *areas) 00498 { 00499 struct async_private_data data; 00500 snd_async_handler_t *ahandler; 00501 const snd_pcm_channel_area_t *my_areas; 00502 snd_pcm_uframes_t offset, frames, size; 00503 snd_pcm_sframes_t commitres; 00504 int err, count; 00505 00506 data.samples = NULL; /* we do not require the global sample area for direct write */ 00507 data.areas = NULL; /* we do not require the global areas for direct write */ 00508 data.phase = 0; 00509 err = snd_async_add_pcm_handler(&ahandler, handle, async_direct_callback, &data); 00510 if (err < 0) { 00511 printf("Unable to register async handler\n"); 00512 exit(EXIT_FAILURE); 00513 } 00514 for (count = 0; count < 2; count++) { 00515 size = period_size; 00516 while (size > 0) { 00517 frames = size; 00518 err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames); 00519 if (err < 0) { 00520 if ((err = xrun_recovery(handle, err)) < 0) { 00521 printf("MMAP begin avail error: %s\n", snd_strerror(err)); 00522 exit(EXIT_FAILURE); 00523 } 00524 } 00525 generate_sine(my_areas, offset, frames, &data.phase); 00526 commitres = snd_pcm_mmap_commit(handle, offset, frames); 00527 if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) { 00528 if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) { 00529 printf("MMAP commit error: %s\n", snd_strerror(err)); 00530 exit(EXIT_FAILURE); 00531 } 00532 } 00533 size -= frames; 00534 } 00535 } 00536 err = snd_pcm_start(handle); 00537 if (err < 0) { 00538 printf("Start error: %s\n", snd_strerror(err)); 00539 exit(EXIT_FAILURE); 00540 } 00541 00542 /* because all other work is done in the signal handler, 00543 suspend the process */ 00544 while (1) { 00545 sleep(1); 00546 } 00547 } 00548 00549 /* 00550 * Transfer method - direct write only 00551 */ 00552 00553 static int direct_loop(snd_pcm_t *handle, 00554 signed short *samples, 00555 snd_pcm_channel_area_t *areas) 00556 { 00557 double phase = 0; 00558 const snd_pcm_channel_area_t *my_areas; 00559 snd_pcm_uframes_t offset, frames, size; 00560 snd_pcm_sframes_t avail, commitres; 00561 snd_pcm_state_t state; 00562 int err, first = 1; 00563 00564 while (1) { 00565 state = snd_pcm_state(handle); 00566 if (state == SND_PCM_STATE_XRUN) { 00567 err = xrun_recovery(handle, -EPIPE); 00568 if (err < 0) { 00569 printf("XRUN recovery failed: %s\n", snd_strerror(err)); 00570 return err; 00571 } 00572 first = 1; 00573 } else if (state == SND_PCM_STATE_SUSPENDED) { 00574 err = xrun_recovery(handle, -ESTRPIPE); 00575 if (err < 0) { 00576 printf("SUSPEND recovery failed: %s\n", snd_strerror(err)); 00577 return err; 00578 } 00579 } 00580 avail = snd_pcm_avail_update(handle); 00581 if (avail < 0) { 00582 err = xrun_recovery(handle, avail); 00583 if (err < 0) { 00584 printf("avail update failed: %s\n", snd_strerror(err)); 00585 return err; 00586 } 00587 first = 1; 00588 continue; 00589 } 00590 if (avail < period_size) { 00591 if (first) { 00592 first = 0; 00593 err = snd_pcm_start(handle); 00594 if (err < 0) { 00595 printf("Start error: %s\n", snd_strerror(err)); 00596 exit(EXIT_FAILURE); 00597 } 00598 } else { 00599 err = snd_pcm_wait(handle, -1); 00600 if (err < 0) { 00601 if ((err = xrun_recovery(handle, err)) < 0) { 00602 printf("snd_pcm_wait error: %s\n", snd_strerror(err)); 00603 exit(EXIT_FAILURE); 00604 } 00605 first = 1; 00606 } 00607 } 00608 continue; 00609 } 00610 size = period_size; 00611 while (size > 0) { 00612 frames = size; 00613 err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames); 00614 if (err < 0) { 00615 if ((err = xrun_recovery(handle, err)) < 0) { 00616 printf("MMAP begin avail error: %s\n", snd_strerror(err)); 00617 exit(EXIT_FAILURE); 00618 } 00619 first = 1; 00620 } 00621 generate_sine(my_areas, offset, frames, &phase); 00622 commitres = snd_pcm_mmap_commit(handle, offset, frames); 00623 if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) { 00624 if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) { 00625 printf("MMAP commit error: %s\n", snd_strerror(err)); 00626 exit(EXIT_FAILURE); 00627 } 00628 first = 1; 00629 } 00630 size -= frames; 00631 } 00632 } 00633 } 00634 00635 /* 00636 * Transfer method - direct write only using mmap_write functions 00637 */ 00638 00639 static int direct_write_loop(snd_pcm_t *handle, 00640 signed short *samples, 00641 snd_pcm_channel_area_t *areas) 00642 { 00643 double phase = 0; 00644 signed short *ptr; 00645 int err, cptr; 00646 00647 while (1) { 00648 generate_sine(areas, 0, period_size, &phase); 00649 ptr = samples; 00650 cptr = period_size; 00651 while (cptr > 0) { 00652 err = snd_pcm_mmap_writei(handle, ptr, cptr); 00653 if (err == -EAGAIN) 00654 continue; 00655 if (err < 0) { 00656 if (xrun_recovery(handle, err) < 0) { 00657 printf("Write error: %s\n", snd_strerror(err)); 00658 exit(EXIT_FAILURE); 00659 } 00660 break; /* skip one period */ 00661 } 00662 ptr += err * channels; 00663 cptr -= err; 00664 } 00665 } 00666 } 00667 00668 /* 00669 * 00670 */ 00671 00672 struct transfer_method { 00673 const char *name; 00674 snd_pcm_access_t access; 00675 int (*transfer_loop)(snd_pcm_t *handle, 00676 signed short *samples, 00677 snd_pcm_channel_area_t *areas); 00678 }; 00679 00680 static struct transfer_method transfer_methods[] = { 00681 { "write", SND_PCM_ACCESS_RW_INTERLEAVED, write_loop }, 00682 { "write_and_poll", SND_PCM_ACCESS_RW_INTERLEAVED, write_and_poll_loop }, 00683 { "async", SND_PCM_ACCESS_RW_INTERLEAVED, async_loop }, 00684 { "async_direct", SND_PCM_ACCESS_MMAP_INTERLEAVED, async_direct_loop }, 00685 { "direct_interleaved", SND_PCM_ACCESS_MMAP_INTERLEAVED, direct_loop }, 00686 { "direct_noninterleaved", SND_PCM_ACCESS_MMAP_NONINTERLEAVED, direct_loop }, 00687 { "direct_write", SND_PCM_ACCESS_MMAP_INTERLEAVED, direct_write_loop }, 00688 { NULL, SND_PCM_ACCESS_RW_INTERLEAVED, NULL } 00689 }; 00690 00691 static void help(void) 00692 { 00693 int k; 00694 printf( 00695 "Usage: pcm [OPTION]... [FILE]...\n" 00696 "-h,--help help\n" 00697 "-D,--device playback device\n" 00698 "-r,--rate stream rate in Hz\n" 00699 "-c,--channels count of channels in stream\n" 00700 "-f,--frequency sine wave frequency in Hz\n" 00701 "-b,--buffer ring buffer size in us\n" 00702 "-p,--period period size in us\n" 00703 "-m,--method transfer method\n" 00704 "-v,--verbose show the PCM setup parameters\n" 00705 "\n"); 00706 printf("Recognized sample formats are:"); 00707 for (k = 0; k < SND_PCM_FORMAT_LAST; ++(unsigned long) k) { 00708 const char *s = snd_pcm_format_name(k); 00709 if (s) 00710 printf(" %s", s); 00711 } 00712 printf("\n"); 00713 printf("Recognized transfer methods are:"); 00714 for (k = 0; transfer_methods[k].name; k++) 00715 printf(" %s", transfer_methods[k].name); 00716 printf("\n"); 00717 } 00718 00719 int main(int argc, char *argv[]) 00720 { 00721 struct option long_option[] = 00722 { 00723 {"help", 0, NULL, 'h'}, 00724 {"device", 1, NULL, 'D'}, 00725 {"rate", 1, NULL, 'r'}, 00726 {"channels", 1, NULL, 'c'}, 00727 {"frequency", 1, NULL, 'f'}, 00728 {"buffer", 1, NULL, 'b'}, 00729 {"period", 1, NULL, 'p'}, 00730 {"method", 1, NULL, 'm'}, 00731 {"verbose", 1, NULL, 'v'}, 00732 {NULL, 0, NULL, 0}, 00733 }; 00734 snd_pcm_t *handle; 00735 int err, morehelp; 00736 snd_pcm_hw_params_t *hwparams; 00737 snd_pcm_sw_params_t *swparams; 00738 int method = 0; 00739 signed short *samples; 00740 unsigned int chn; 00741 snd_pcm_channel_area_t *areas; 00742 00743 snd_pcm_hw_params_alloca(&hwparams); 00744 snd_pcm_sw_params_alloca(&swparams); 00745 00746 morehelp = 0; 00747 while (1) { 00748 int c; 00749 if ((c = getopt_long(argc, argv, "hD:r:c:f:b:p:m:v", long_option, NULL)) < 0) 00750 break; 00751 switch (c) { 00752 case 'h': 00753 morehelp++; 00754 break; 00755 case 'D': 00756 device = strdup(optarg); 00757 break; 00758 case 'r': 00759 rate = atoi(optarg); 00760 rate = rate < 4000 ? 4000 : rate; 00761 rate = rate > 196000 ? 196000 : rate; 00762 break; 00763 case 'c': 00764 channels = atoi(optarg); 00765 channels = channels < 1 ? 1 : channels; 00766 channels = channels > 1024 ? 1024 : channels; 00767 break; 00768 case 'f': 00769 freq = atoi(optarg); 00770 freq = freq < 50 ? 50 : freq; 00771 freq = freq > 5000 ? 5000 : freq; 00772 break; 00773 case 'b': 00774 buffer_time = atoi(optarg); 00775 buffer_time = buffer_time < 1000 ? 1000 : buffer_time; 00776 buffer_time = buffer_time > 1000000 ? 1000000 : buffer_time; 00777 break; 00778 case 'p': 00779 period_time = atoi(optarg); 00780 period_time = period_time < 1000 ? 1000 : period_time; 00781 period_time = period_time > 1000000 ? 1000000 : period_time; 00782 break; 00783 case 'm': 00784 for (method = 0; transfer_methods[method].name; method++) 00785 if (!strcasecmp(transfer_methods[method].name, optarg)) 00786 break; 00787 if (transfer_methods[method].name == NULL) 00788 method = 0; 00789 break; 00790 case 'v': 00791 verbose = 1; 00792 break; 00793 } 00794 } 00795 00796 if (morehelp) { 00797 help(); 00798 return 0; 00799 } 00800 00801 err = snd_output_stdio_attach(&output, stdout, 0); 00802 if (err < 0) { 00803 printf("Output failed: %s\n", snd_strerror(err)); 00804 return 0; 00805 } 00806 00807 printf("Playback device is %s\n", device); 00808 printf("Stream parameters are %iHz, %s, %i channels\n", rate, snd_pcm_format_name(format), channels); 00809 printf("Sine wave rate is %.4fHz\n", freq); 00810 printf("Using transfer method: %s\n", transfer_methods[method].name); 00811 00812 if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { 00813 printf("Playback open error: %s\n", snd_strerror(err)); 00814 return 0; 00815 } 00816 00817 if ((err = set_hwparams(handle, hwparams, transfer_methods[method].access)) < 0) { 00818 printf("Setting of hwparams failed: %s\n", snd_strerror(err)); 00819 exit(EXIT_FAILURE); 00820 } 00821 if ((err = set_swparams(handle, swparams)) < 0) { 00822 printf("Setting of swparams failed: %s\n", snd_strerror(err)); 00823 exit(EXIT_FAILURE); 00824 } 00825 00826 if (verbose > 0) 00827 snd_pcm_dump(handle, output); 00828 00829 samples = malloc((period_size * channels * snd_pcm_format_width(format)) / 8); 00830 if (samples == NULL) { 00831 printf("No enough memory\n"); 00832 exit(EXIT_FAILURE); 00833 } 00834 00835 areas = calloc(channels, sizeof(snd_pcm_channel_area_t)); 00836 if (areas == NULL) { 00837 printf("No enough memory\n"); 00838 exit(EXIT_FAILURE); 00839 } 00840 for (chn = 0; chn < channels; chn++) { 00841 areas[chn].addr = samples; 00842 areas[chn].first = chn * 16; 00843 areas[chn].step = channels * 16; 00844 } 00845 00846 err = transfer_methods[method].transfer_loop(handle, samples, areas); 00847 if (err < 0) 00848 printf("Transfer failed: %s\n", snd_strerror(err)); 00849 00850 free(areas); 00851 free(samples); 00852 snd_pcm_close(handle); 00853 return 0; 00854 } 00855

Generated on Thu Sep 30 04:13:33 2004 for ALSA project - the C library reference by doxygen 1.3.7