1d5ac70f0Sopenharmony_ci/* 2d5ac70f0Sopenharmony_ci * This small demo sends a simple sinusoidal wave to your speakers. 3d5ac70f0Sopenharmony_ci */ 4d5ac70f0Sopenharmony_ci 5d5ac70f0Sopenharmony_ci#include "config.h" 6d5ac70f0Sopenharmony_ci 7d5ac70f0Sopenharmony_ci#include <stdio.h> 8d5ac70f0Sopenharmony_ci#include <stdlib.h> 9d5ac70f0Sopenharmony_ci#include <string.h> 10d5ac70f0Sopenharmony_ci#include <sched.h> 11d5ac70f0Sopenharmony_ci#include <errno.h> 12d5ac70f0Sopenharmony_ci#include <getopt.h> 13d5ac70f0Sopenharmony_ci#include "../include/asoundlib.h" 14d5ac70f0Sopenharmony_ci#include <sys/time.h> 15d5ac70f0Sopenharmony_ci#include <math.h> 16d5ac70f0Sopenharmony_ci 17d5ac70f0Sopenharmony_ci#ifndef ESTRPIPE 18d5ac70f0Sopenharmony_ci#define ESTRPIPE ESPIPE 19d5ac70f0Sopenharmony_ci#endif 20d5ac70f0Sopenharmony_ci 21d5ac70f0Sopenharmony_cistatic char *device = "plughw:0,0"; /* playback device */ 22d5ac70f0Sopenharmony_cistatic snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */ 23d5ac70f0Sopenharmony_cistatic unsigned int rate = 44100; /* stream rate */ 24d5ac70f0Sopenharmony_cistatic unsigned int channels = 1; /* count of channels */ 25d5ac70f0Sopenharmony_cistatic unsigned int buffer_time = 500000; /* ring buffer length in us */ 26d5ac70f0Sopenharmony_cistatic unsigned int period_time = 100000; /* period time in us */ 27d5ac70f0Sopenharmony_cistatic double freq = 440; /* sinusoidal wave frequency in Hz */ 28d5ac70f0Sopenharmony_cistatic int verbose = 0; /* verbose flag */ 29d5ac70f0Sopenharmony_cistatic int resample = 1; /* enable alsa-lib resampling */ 30d5ac70f0Sopenharmony_cistatic int period_event = 0; /* produce poll event after each period */ 31d5ac70f0Sopenharmony_ci 32d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t buffer_size; 33d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t period_size; 34d5ac70f0Sopenharmony_cistatic snd_output_t *output = NULL; 35d5ac70f0Sopenharmony_ci 36d5ac70f0Sopenharmony_cistatic void generate_sine(const snd_pcm_channel_area_t *areas, 37d5ac70f0Sopenharmony_ci snd_pcm_uframes_t offset, 38d5ac70f0Sopenharmony_ci int count, double *_phase) 39d5ac70f0Sopenharmony_ci{ 40d5ac70f0Sopenharmony_ci static double max_phase = 2. * M_PI; 41d5ac70f0Sopenharmony_ci double phase = *_phase; 42d5ac70f0Sopenharmony_ci double step = max_phase*freq/(double)rate; 43d5ac70f0Sopenharmony_ci unsigned char *samples[channels]; 44d5ac70f0Sopenharmony_ci int steps[channels]; 45d5ac70f0Sopenharmony_ci unsigned int chn; 46d5ac70f0Sopenharmony_ci int format_bits = snd_pcm_format_width(format); 47d5ac70f0Sopenharmony_ci unsigned int maxval = (1 << (format_bits - 1)) - 1; 48d5ac70f0Sopenharmony_ci int bps = format_bits / 8; /* bytes per sample */ 49d5ac70f0Sopenharmony_ci int phys_bps = snd_pcm_format_physical_width(format) / 8; 50d5ac70f0Sopenharmony_ci int big_endian = snd_pcm_format_big_endian(format) == 1; 51d5ac70f0Sopenharmony_ci int to_unsigned = snd_pcm_format_unsigned(format) == 1; 52d5ac70f0Sopenharmony_ci int is_float = (format == SND_PCM_FORMAT_FLOAT_LE || 53d5ac70f0Sopenharmony_ci format == SND_PCM_FORMAT_FLOAT_BE); 54d5ac70f0Sopenharmony_ci 55d5ac70f0Sopenharmony_ci /* verify and prepare the contents of areas */ 56d5ac70f0Sopenharmony_ci for (chn = 0; chn < channels; chn++) { 57d5ac70f0Sopenharmony_ci if ((areas[chn].first % 8) != 0) { 58d5ac70f0Sopenharmony_ci printf("areas[%u].first == %u, aborting...\n", chn, areas[chn].first); 59d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 60d5ac70f0Sopenharmony_ci } 61d5ac70f0Sopenharmony_ci samples[chn] = /*(signed short *)*/(((unsigned char *)areas[chn].addr) + (areas[chn].first / 8)); 62d5ac70f0Sopenharmony_ci if ((areas[chn].step % 16) != 0) { 63d5ac70f0Sopenharmony_ci printf("areas[%u].step == %u, aborting...\n", chn, areas[chn].step); 64d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 65d5ac70f0Sopenharmony_ci } 66d5ac70f0Sopenharmony_ci steps[chn] = areas[chn].step / 8; 67d5ac70f0Sopenharmony_ci samples[chn] += offset * steps[chn]; 68d5ac70f0Sopenharmony_ci } 69d5ac70f0Sopenharmony_ci /* fill the channel areas */ 70d5ac70f0Sopenharmony_ci while (count-- > 0) { 71d5ac70f0Sopenharmony_ci union { 72d5ac70f0Sopenharmony_ci float f; 73d5ac70f0Sopenharmony_ci int i; 74d5ac70f0Sopenharmony_ci } fval; 75d5ac70f0Sopenharmony_ci int res, i; 76d5ac70f0Sopenharmony_ci if (is_float) { 77d5ac70f0Sopenharmony_ci fval.f = sin(phase); 78d5ac70f0Sopenharmony_ci res = fval.i; 79d5ac70f0Sopenharmony_ci } else 80d5ac70f0Sopenharmony_ci res = sin(phase) * maxval; 81d5ac70f0Sopenharmony_ci if (to_unsigned) 82d5ac70f0Sopenharmony_ci res ^= 1U << (format_bits - 1); 83d5ac70f0Sopenharmony_ci for (chn = 0; chn < channels; chn++) { 84d5ac70f0Sopenharmony_ci /* Generate data in native endian format */ 85d5ac70f0Sopenharmony_ci if (big_endian) { 86d5ac70f0Sopenharmony_ci for (i = 0; i < bps; i++) 87d5ac70f0Sopenharmony_ci *(samples[chn] + phys_bps - 1 - i) = (res >> i * 8) & 0xff; 88d5ac70f0Sopenharmony_ci } else { 89d5ac70f0Sopenharmony_ci for (i = 0; i < bps; i++) 90d5ac70f0Sopenharmony_ci *(samples[chn] + i) = (res >> i * 8) & 0xff; 91d5ac70f0Sopenharmony_ci } 92d5ac70f0Sopenharmony_ci samples[chn] += steps[chn]; 93d5ac70f0Sopenharmony_ci } 94d5ac70f0Sopenharmony_ci phase += step; 95d5ac70f0Sopenharmony_ci if (phase >= max_phase) 96d5ac70f0Sopenharmony_ci phase -= max_phase; 97d5ac70f0Sopenharmony_ci } 98d5ac70f0Sopenharmony_ci *_phase = phase; 99d5ac70f0Sopenharmony_ci} 100d5ac70f0Sopenharmony_ci 101d5ac70f0Sopenharmony_cistatic int set_hwparams(snd_pcm_t *handle, 102d5ac70f0Sopenharmony_ci snd_pcm_hw_params_t *params, 103d5ac70f0Sopenharmony_ci snd_pcm_access_t access) 104d5ac70f0Sopenharmony_ci{ 105d5ac70f0Sopenharmony_ci unsigned int rrate; 106d5ac70f0Sopenharmony_ci snd_pcm_uframes_t size; 107d5ac70f0Sopenharmony_ci int err, dir; 108d5ac70f0Sopenharmony_ci 109d5ac70f0Sopenharmony_ci /* choose all parameters */ 110d5ac70f0Sopenharmony_ci err = snd_pcm_hw_params_any(handle, params); 111d5ac70f0Sopenharmony_ci if (err < 0) { 112d5ac70f0Sopenharmony_ci printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err)); 113d5ac70f0Sopenharmony_ci return err; 114d5ac70f0Sopenharmony_ci } 115d5ac70f0Sopenharmony_ci /* set hardware resampling */ 116d5ac70f0Sopenharmony_ci err = snd_pcm_hw_params_set_rate_resample(handle, params, resample); 117d5ac70f0Sopenharmony_ci if (err < 0) { 118d5ac70f0Sopenharmony_ci printf("Resampling setup failed for playback: %s\n", snd_strerror(err)); 119d5ac70f0Sopenharmony_ci return err; 120d5ac70f0Sopenharmony_ci } 121d5ac70f0Sopenharmony_ci /* set the interleaved read/write format */ 122d5ac70f0Sopenharmony_ci err = snd_pcm_hw_params_set_access(handle, params, access); 123d5ac70f0Sopenharmony_ci if (err < 0) { 124d5ac70f0Sopenharmony_ci printf("Access type not available for playback: %s\n", snd_strerror(err)); 125d5ac70f0Sopenharmony_ci return err; 126d5ac70f0Sopenharmony_ci } 127d5ac70f0Sopenharmony_ci /* set the sample format */ 128d5ac70f0Sopenharmony_ci err = snd_pcm_hw_params_set_format(handle, params, format); 129d5ac70f0Sopenharmony_ci if (err < 0) { 130d5ac70f0Sopenharmony_ci printf("Sample format not available for playback: %s\n", snd_strerror(err)); 131d5ac70f0Sopenharmony_ci return err; 132d5ac70f0Sopenharmony_ci } 133d5ac70f0Sopenharmony_ci /* set the count of channels */ 134d5ac70f0Sopenharmony_ci err = snd_pcm_hw_params_set_channels(handle, params, channels); 135d5ac70f0Sopenharmony_ci if (err < 0) { 136d5ac70f0Sopenharmony_ci printf("Channels count (%u) not available for playbacks: %s\n", channels, snd_strerror(err)); 137d5ac70f0Sopenharmony_ci return err; 138d5ac70f0Sopenharmony_ci } 139d5ac70f0Sopenharmony_ci /* set the stream rate */ 140d5ac70f0Sopenharmony_ci rrate = rate; 141d5ac70f0Sopenharmony_ci err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0); 142d5ac70f0Sopenharmony_ci if (err < 0) { 143d5ac70f0Sopenharmony_ci printf("Rate %uHz not available for playback: %s\n", rate, snd_strerror(err)); 144d5ac70f0Sopenharmony_ci return err; 145d5ac70f0Sopenharmony_ci } 146d5ac70f0Sopenharmony_ci if (rrate != rate) { 147d5ac70f0Sopenharmony_ci printf("Rate doesn't match (requested %uHz, get %iHz)\n", rate, err); 148d5ac70f0Sopenharmony_ci return -EINVAL; 149d5ac70f0Sopenharmony_ci } 150d5ac70f0Sopenharmony_ci /* set the buffer time */ 151d5ac70f0Sopenharmony_ci err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir); 152d5ac70f0Sopenharmony_ci if (err < 0) { 153d5ac70f0Sopenharmony_ci printf("Unable to set buffer time %u for playback: %s\n", buffer_time, snd_strerror(err)); 154d5ac70f0Sopenharmony_ci return err; 155d5ac70f0Sopenharmony_ci } 156d5ac70f0Sopenharmony_ci err = snd_pcm_hw_params_get_buffer_size(params, &size); 157d5ac70f0Sopenharmony_ci if (err < 0) { 158d5ac70f0Sopenharmony_ci printf("Unable to get buffer size for playback: %s\n", snd_strerror(err)); 159d5ac70f0Sopenharmony_ci return err; 160d5ac70f0Sopenharmony_ci } 161d5ac70f0Sopenharmony_ci buffer_size = size; 162d5ac70f0Sopenharmony_ci /* set the period time */ 163d5ac70f0Sopenharmony_ci err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir); 164d5ac70f0Sopenharmony_ci if (err < 0) { 165d5ac70f0Sopenharmony_ci printf("Unable to set period time %u for playback: %s\n", period_time, snd_strerror(err)); 166d5ac70f0Sopenharmony_ci return err; 167d5ac70f0Sopenharmony_ci } 168d5ac70f0Sopenharmony_ci err = snd_pcm_hw_params_get_period_size(params, &size, &dir); 169d5ac70f0Sopenharmony_ci if (err < 0) { 170d5ac70f0Sopenharmony_ci printf("Unable to get period size for playback: %s\n", snd_strerror(err)); 171d5ac70f0Sopenharmony_ci return err; 172d5ac70f0Sopenharmony_ci } 173d5ac70f0Sopenharmony_ci period_size = size; 174d5ac70f0Sopenharmony_ci /* write the parameters to device */ 175d5ac70f0Sopenharmony_ci err = snd_pcm_hw_params(handle, params); 176d5ac70f0Sopenharmony_ci if (err < 0) { 177d5ac70f0Sopenharmony_ci printf("Unable to set hw params for playback: %s\n", snd_strerror(err)); 178d5ac70f0Sopenharmony_ci return err; 179d5ac70f0Sopenharmony_ci } 180d5ac70f0Sopenharmony_ci return 0; 181d5ac70f0Sopenharmony_ci} 182d5ac70f0Sopenharmony_ci 183d5ac70f0Sopenharmony_cistatic int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams) 184d5ac70f0Sopenharmony_ci{ 185d5ac70f0Sopenharmony_ci int err; 186d5ac70f0Sopenharmony_ci 187d5ac70f0Sopenharmony_ci /* get the current swparams */ 188d5ac70f0Sopenharmony_ci err = snd_pcm_sw_params_current(handle, swparams); 189d5ac70f0Sopenharmony_ci if (err < 0) { 190d5ac70f0Sopenharmony_ci printf("Unable to determine current swparams for playback: %s\n", snd_strerror(err)); 191d5ac70f0Sopenharmony_ci return err; 192d5ac70f0Sopenharmony_ci } 193d5ac70f0Sopenharmony_ci /* start the transfer when the buffer is almost full: */ 194d5ac70f0Sopenharmony_ci /* (buffer_size / avail_min) * avail_min */ 195d5ac70f0Sopenharmony_ci err = snd_pcm_sw_params_set_start_threshold(handle, swparams, (buffer_size / period_size) * period_size); 196d5ac70f0Sopenharmony_ci if (err < 0) { 197d5ac70f0Sopenharmony_ci printf("Unable to set start threshold mode for playback: %s\n", snd_strerror(err)); 198d5ac70f0Sopenharmony_ci return err; 199d5ac70f0Sopenharmony_ci } 200d5ac70f0Sopenharmony_ci /* allow the transfer when at least period_size samples can be processed */ 201d5ac70f0Sopenharmony_ci /* or disable this mechanism when period event is enabled (aka interrupt like style processing) */ 202d5ac70f0Sopenharmony_ci err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_event ? buffer_size : period_size); 203d5ac70f0Sopenharmony_ci if (err < 0) { 204d5ac70f0Sopenharmony_ci printf("Unable to set avail min for playback: %s\n", snd_strerror(err)); 205d5ac70f0Sopenharmony_ci return err; 206d5ac70f0Sopenharmony_ci } 207d5ac70f0Sopenharmony_ci /* enable period events when requested */ 208d5ac70f0Sopenharmony_ci if (period_event) { 209d5ac70f0Sopenharmony_ci err = snd_pcm_sw_params_set_period_event(handle, swparams, 1); 210d5ac70f0Sopenharmony_ci if (err < 0) { 211d5ac70f0Sopenharmony_ci printf("Unable to set period event: %s\n", snd_strerror(err)); 212d5ac70f0Sopenharmony_ci return err; 213d5ac70f0Sopenharmony_ci } 214d5ac70f0Sopenharmony_ci } 215d5ac70f0Sopenharmony_ci /* write the parameters to the playback device */ 216d5ac70f0Sopenharmony_ci err = snd_pcm_sw_params(handle, swparams); 217d5ac70f0Sopenharmony_ci if (err < 0) { 218d5ac70f0Sopenharmony_ci printf("Unable to set sw params for playback: %s\n", snd_strerror(err)); 219d5ac70f0Sopenharmony_ci return err; 220d5ac70f0Sopenharmony_ci } 221d5ac70f0Sopenharmony_ci return 0; 222d5ac70f0Sopenharmony_ci} 223d5ac70f0Sopenharmony_ci 224d5ac70f0Sopenharmony_ci/* 225d5ac70f0Sopenharmony_ci * Underrun and suspend recovery 226d5ac70f0Sopenharmony_ci */ 227d5ac70f0Sopenharmony_ci 228d5ac70f0Sopenharmony_cistatic int xrun_recovery(snd_pcm_t *handle, int err) 229d5ac70f0Sopenharmony_ci{ 230d5ac70f0Sopenharmony_ci if (verbose) 231d5ac70f0Sopenharmony_ci printf("stream recovery\n"); 232d5ac70f0Sopenharmony_ci if (err == -EPIPE) { /* under-run */ 233d5ac70f0Sopenharmony_ci err = snd_pcm_prepare(handle); 234d5ac70f0Sopenharmony_ci if (err < 0) 235d5ac70f0Sopenharmony_ci printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err)); 236d5ac70f0Sopenharmony_ci return 0; 237d5ac70f0Sopenharmony_ci } else if (err == -ESTRPIPE) { 238d5ac70f0Sopenharmony_ci while ((err = snd_pcm_resume(handle)) == -EAGAIN) 239d5ac70f0Sopenharmony_ci sleep(1); /* wait until the suspend flag is released */ 240d5ac70f0Sopenharmony_ci if (err < 0) { 241d5ac70f0Sopenharmony_ci err = snd_pcm_prepare(handle); 242d5ac70f0Sopenharmony_ci if (err < 0) 243d5ac70f0Sopenharmony_ci printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err)); 244d5ac70f0Sopenharmony_ci } 245d5ac70f0Sopenharmony_ci return 0; 246d5ac70f0Sopenharmony_ci } 247d5ac70f0Sopenharmony_ci return err; 248d5ac70f0Sopenharmony_ci} 249d5ac70f0Sopenharmony_ci 250d5ac70f0Sopenharmony_ci/* 251d5ac70f0Sopenharmony_ci * Transfer method - write only 252d5ac70f0Sopenharmony_ci */ 253d5ac70f0Sopenharmony_ci 254d5ac70f0Sopenharmony_cistatic int write_loop(snd_pcm_t *handle, 255d5ac70f0Sopenharmony_ci signed short *samples, 256d5ac70f0Sopenharmony_ci snd_pcm_channel_area_t *areas) 257d5ac70f0Sopenharmony_ci{ 258d5ac70f0Sopenharmony_ci double phase = 0; 259d5ac70f0Sopenharmony_ci signed short *ptr; 260d5ac70f0Sopenharmony_ci int err, cptr; 261d5ac70f0Sopenharmony_ci 262d5ac70f0Sopenharmony_ci while (1) { 263d5ac70f0Sopenharmony_ci generate_sine(areas, 0, period_size, &phase); 264d5ac70f0Sopenharmony_ci ptr = samples; 265d5ac70f0Sopenharmony_ci cptr = period_size; 266d5ac70f0Sopenharmony_ci while (cptr > 0) { 267d5ac70f0Sopenharmony_ci err = snd_pcm_writei(handle, ptr, cptr); 268d5ac70f0Sopenharmony_ci if (err == -EAGAIN) 269d5ac70f0Sopenharmony_ci continue; 270d5ac70f0Sopenharmony_ci if (err < 0) { 271d5ac70f0Sopenharmony_ci if (xrun_recovery(handle, err) < 0) { 272d5ac70f0Sopenharmony_ci printf("Write error: %s\n", snd_strerror(err)); 273d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 274d5ac70f0Sopenharmony_ci } 275d5ac70f0Sopenharmony_ci break; /* skip one period */ 276d5ac70f0Sopenharmony_ci } 277d5ac70f0Sopenharmony_ci ptr += err * channels; 278d5ac70f0Sopenharmony_ci cptr -= err; 279d5ac70f0Sopenharmony_ci } 280d5ac70f0Sopenharmony_ci } 281d5ac70f0Sopenharmony_ci} 282d5ac70f0Sopenharmony_ci 283d5ac70f0Sopenharmony_ci/* 284d5ac70f0Sopenharmony_ci * Transfer method - write and wait for room in buffer using poll 285d5ac70f0Sopenharmony_ci */ 286d5ac70f0Sopenharmony_ci 287d5ac70f0Sopenharmony_cistatic int wait_for_poll(snd_pcm_t *handle, struct pollfd *ufds, unsigned int count) 288d5ac70f0Sopenharmony_ci{ 289d5ac70f0Sopenharmony_ci unsigned short revents; 290d5ac70f0Sopenharmony_ci 291d5ac70f0Sopenharmony_ci while (1) { 292d5ac70f0Sopenharmony_ci poll(ufds, count, -1); 293d5ac70f0Sopenharmony_ci snd_pcm_poll_descriptors_revents(handle, ufds, count, &revents); 294d5ac70f0Sopenharmony_ci if (revents & POLLERR) 295d5ac70f0Sopenharmony_ci return -EIO; 296d5ac70f0Sopenharmony_ci if (revents & POLLOUT) 297d5ac70f0Sopenharmony_ci return 0; 298d5ac70f0Sopenharmony_ci } 299d5ac70f0Sopenharmony_ci} 300d5ac70f0Sopenharmony_ci 301d5ac70f0Sopenharmony_cistatic int write_and_poll_loop(snd_pcm_t *handle, 302d5ac70f0Sopenharmony_ci signed short *samples, 303d5ac70f0Sopenharmony_ci snd_pcm_channel_area_t *areas) 304d5ac70f0Sopenharmony_ci{ 305d5ac70f0Sopenharmony_ci struct pollfd *ufds; 306d5ac70f0Sopenharmony_ci double phase = 0; 307d5ac70f0Sopenharmony_ci signed short *ptr; 308d5ac70f0Sopenharmony_ci int err, count, cptr, init; 309d5ac70f0Sopenharmony_ci 310d5ac70f0Sopenharmony_ci count = snd_pcm_poll_descriptors_count (handle); 311d5ac70f0Sopenharmony_ci if (count <= 0) { 312d5ac70f0Sopenharmony_ci printf("Invalid poll descriptors count\n"); 313d5ac70f0Sopenharmony_ci return count; 314d5ac70f0Sopenharmony_ci } 315d5ac70f0Sopenharmony_ci 316d5ac70f0Sopenharmony_ci ufds = malloc(sizeof(struct pollfd) * count); 317d5ac70f0Sopenharmony_ci if (ufds == NULL) { 318d5ac70f0Sopenharmony_ci printf("No enough memory\n"); 319d5ac70f0Sopenharmony_ci return -ENOMEM; 320d5ac70f0Sopenharmony_ci } 321d5ac70f0Sopenharmony_ci if ((err = snd_pcm_poll_descriptors(handle, ufds, count)) < 0) { 322d5ac70f0Sopenharmony_ci printf("Unable to obtain poll descriptors for playback: %s\n", snd_strerror(err)); 323d5ac70f0Sopenharmony_ci return err; 324d5ac70f0Sopenharmony_ci } 325d5ac70f0Sopenharmony_ci 326d5ac70f0Sopenharmony_ci init = 1; 327d5ac70f0Sopenharmony_ci while (1) { 328d5ac70f0Sopenharmony_ci if (!init) { 329d5ac70f0Sopenharmony_ci err = wait_for_poll(handle, ufds, count); 330d5ac70f0Sopenharmony_ci if (err < 0) { 331d5ac70f0Sopenharmony_ci if (snd_pcm_state(handle) == SND_PCM_STATE_XRUN || 332d5ac70f0Sopenharmony_ci snd_pcm_state(handle) == SND_PCM_STATE_SUSPENDED) { 333d5ac70f0Sopenharmony_ci err = snd_pcm_state(handle) == SND_PCM_STATE_XRUN ? -EPIPE : -ESTRPIPE; 334d5ac70f0Sopenharmony_ci if (xrun_recovery(handle, err) < 0) { 335d5ac70f0Sopenharmony_ci printf("Write error: %s\n", snd_strerror(err)); 336d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 337d5ac70f0Sopenharmony_ci } 338d5ac70f0Sopenharmony_ci init = 1; 339d5ac70f0Sopenharmony_ci } else { 340d5ac70f0Sopenharmony_ci printf("Wait for poll failed\n"); 341d5ac70f0Sopenharmony_ci return err; 342d5ac70f0Sopenharmony_ci } 343d5ac70f0Sopenharmony_ci } 344d5ac70f0Sopenharmony_ci } 345d5ac70f0Sopenharmony_ci 346d5ac70f0Sopenharmony_ci generate_sine(areas, 0, period_size, &phase); 347d5ac70f0Sopenharmony_ci ptr = samples; 348d5ac70f0Sopenharmony_ci cptr = period_size; 349d5ac70f0Sopenharmony_ci while (cptr > 0) { 350d5ac70f0Sopenharmony_ci err = snd_pcm_writei(handle, ptr, cptr); 351d5ac70f0Sopenharmony_ci if (err < 0) { 352d5ac70f0Sopenharmony_ci if (xrun_recovery(handle, err) < 0) { 353d5ac70f0Sopenharmony_ci printf("Write error: %s\n", snd_strerror(err)); 354d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 355d5ac70f0Sopenharmony_ci } 356d5ac70f0Sopenharmony_ci init = 1; 357d5ac70f0Sopenharmony_ci break; /* skip one period */ 358d5ac70f0Sopenharmony_ci } 359d5ac70f0Sopenharmony_ci if (snd_pcm_state(handle) == SND_PCM_STATE_RUNNING) 360d5ac70f0Sopenharmony_ci init = 0; 361d5ac70f0Sopenharmony_ci ptr += err * channels; 362d5ac70f0Sopenharmony_ci cptr -= err; 363d5ac70f0Sopenharmony_ci if (cptr == 0) 364d5ac70f0Sopenharmony_ci break; 365d5ac70f0Sopenharmony_ci /* it is possible, that the initial buffer cannot store */ 366d5ac70f0Sopenharmony_ci /* all data from the last period, so wait awhile */ 367d5ac70f0Sopenharmony_ci err = wait_for_poll(handle, ufds, count); 368d5ac70f0Sopenharmony_ci if (err < 0) { 369d5ac70f0Sopenharmony_ci if (snd_pcm_state(handle) == SND_PCM_STATE_XRUN || 370d5ac70f0Sopenharmony_ci snd_pcm_state(handle) == SND_PCM_STATE_SUSPENDED) { 371d5ac70f0Sopenharmony_ci err = snd_pcm_state(handle) == SND_PCM_STATE_XRUN ? -EPIPE : -ESTRPIPE; 372d5ac70f0Sopenharmony_ci if (xrun_recovery(handle, err) < 0) { 373d5ac70f0Sopenharmony_ci printf("Write error: %s\n", snd_strerror(err)); 374d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 375d5ac70f0Sopenharmony_ci } 376d5ac70f0Sopenharmony_ci init = 1; 377d5ac70f0Sopenharmony_ci } else { 378d5ac70f0Sopenharmony_ci printf("Wait for poll failed\n"); 379d5ac70f0Sopenharmony_ci return err; 380d5ac70f0Sopenharmony_ci } 381d5ac70f0Sopenharmony_ci } 382d5ac70f0Sopenharmony_ci } 383d5ac70f0Sopenharmony_ci } 384d5ac70f0Sopenharmony_ci} 385d5ac70f0Sopenharmony_ci 386d5ac70f0Sopenharmony_ci/* 387d5ac70f0Sopenharmony_ci * Transfer method - asynchronous notification 388d5ac70f0Sopenharmony_ci */ 389d5ac70f0Sopenharmony_ci 390d5ac70f0Sopenharmony_cistruct async_private_data { 391d5ac70f0Sopenharmony_ci signed short *samples; 392d5ac70f0Sopenharmony_ci snd_pcm_channel_area_t *areas; 393d5ac70f0Sopenharmony_ci double phase; 394d5ac70f0Sopenharmony_ci}; 395d5ac70f0Sopenharmony_ci 396d5ac70f0Sopenharmony_cistatic void async_callback(snd_async_handler_t *ahandler) 397d5ac70f0Sopenharmony_ci{ 398d5ac70f0Sopenharmony_ci snd_pcm_t *handle = snd_async_handler_get_pcm(ahandler); 399d5ac70f0Sopenharmony_ci struct async_private_data *data = snd_async_handler_get_callback_private(ahandler); 400d5ac70f0Sopenharmony_ci signed short *samples = data->samples; 401d5ac70f0Sopenharmony_ci snd_pcm_channel_area_t *areas = data->areas; 402d5ac70f0Sopenharmony_ci snd_pcm_sframes_t avail; 403d5ac70f0Sopenharmony_ci int err; 404d5ac70f0Sopenharmony_ci 405d5ac70f0Sopenharmony_ci avail = snd_pcm_avail_update(handle); 406d5ac70f0Sopenharmony_ci while (avail >= period_size) { 407d5ac70f0Sopenharmony_ci generate_sine(areas, 0, period_size, &data->phase); 408d5ac70f0Sopenharmony_ci err = snd_pcm_writei(handle, samples, period_size); 409d5ac70f0Sopenharmony_ci if (err < 0) { 410d5ac70f0Sopenharmony_ci printf("Write error: %s\n", snd_strerror(err)); 411d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 412d5ac70f0Sopenharmony_ci } 413d5ac70f0Sopenharmony_ci if (err != period_size) { 414d5ac70f0Sopenharmony_ci printf("Write error: written %i expected %li\n", err, period_size); 415d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 416d5ac70f0Sopenharmony_ci } 417d5ac70f0Sopenharmony_ci avail = snd_pcm_avail_update(handle); 418d5ac70f0Sopenharmony_ci } 419d5ac70f0Sopenharmony_ci} 420d5ac70f0Sopenharmony_ci 421d5ac70f0Sopenharmony_cistatic int async_loop(snd_pcm_t *handle, 422d5ac70f0Sopenharmony_ci signed short *samples, 423d5ac70f0Sopenharmony_ci snd_pcm_channel_area_t *areas) 424d5ac70f0Sopenharmony_ci{ 425d5ac70f0Sopenharmony_ci struct async_private_data data; 426d5ac70f0Sopenharmony_ci snd_async_handler_t *ahandler; 427d5ac70f0Sopenharmony_ci int err, count; 428d5ac70f0Sopenharmony_ci 429d5ac70f0Sopenharmony_ci data.samples = samples; 430d5ac70f0Sopenharmony_ci data.areas = areas; 431d5ac70f0Sopenharmony_ci data.phase = 0; 432d5ac70f0Sopenharmony_ci err = snd_async_add_pcm_handler(&ahandler, handle, async_callback, &data); 433d5ac70f0Sopenharmony_ci if (err < 0) { 434d5ac70f0Sopenharmony_ci printf("Unable to register async handler\n"); 435d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 436d5ac70f0Sopenharmony_ci } 437d5ac70f0Sopenharmony_ci for (count = 0; count < 2; count++) { 438d5ac70f0Sopenharmony_ci generate_sine(areas, 0, period_size, &data.phase); 439d5ac70f0Sopenharmony_ci err = snd_pcm_writei(handle, samples, period_size); 440d5ac70f0Sopenharmony_ci if (err < 0) { 441d5ac70f0Sopenharmony_ci printf("Initial write error: %s\n", snd_strerror(err)); 442d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 443d5ac70f0Sopenharmony_ci } 444d5ac70f0Sopenharmony_ci if (err != period_size) { 445d5ac70f0Sopenharmony_ci printf("Initial write error: written %i expected %li\n", err, period_size); 446d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 447d5ac70f0Sopenharmony_ci } 448d5ac70f0Sopenharmony_ci } 449d5ac70f0Sopenharmony_ci if (snd_pcm_state(handle) == SND_PCM_STATE_PREPARED) { 450d5ac70f0Sopenharmony_ci err = snd_pcm_start(handle); 451d5ac70f0Sopenharmony_ci if (err < 0) { 452d5ac70f0Sopenharmony_ci printf("Start error: %s\n", snd_strerror(err)); 453d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 454d5ac70f0Sopenharmony_ci } 455d5ac70f0Sopenharmony_ci } 456d5ac70f0Sopenharmony_ci 457d5ac70f0Sopenharmony_ci /* because all other work is done in the signal handler, 458d5ac70f0Sopenharmony_ci suspend the process */ 459d5ac70f0Sopenharmony_ci while (1) { 460d5ac70f0Sopenharmony_ci sleep(1); 461d5ac70f0Sopenharmony_ci } 462d5ac70f0Sopenharmony_ci} 463d5ac70f0Sopenharmony_ci 464d5ac70f0Sopenharmony_ci/* 465d5ac70f0Sopenharmony_ci * Transfer method - asynchronous notification + direct write 466d5ac70f0Sopenharmony_ci */ 467d5ac70f0Sopenharmony_ci 468d5ac70f0Sopenharmony_cistatic void async_direct_callback(snd_async_handler_t *ahandler) 469d5ac70f0Sopenharmony_ci{ 470d5ac70f0Sopenharmony_ci snd_pcm_t *handle = snd_async_handler_get_pcm(ahandler); 471d5ac70f0Sopenharmony_ci struct async_private_data *data = snd_async_handler_get_callback_private(ahandler); 472d5ac70f0Sopenharmony_ci const snd_pcm_channel_area_t *my_areas; 473d5ac70f0Sopenharmony_ci snd_pcm_uframes_t offset, frames, size; 474d5ac70f0Sopenharmony_ci snd_pcm_sframes_t avail, commitres; 475d5ac70f0Sopenharmony_ci snd_pcm_state_t state; 476d5ac70f0Sopenharmony_ci int first = 0, err; 477d5ac70f0Sopenharmony_ci 478d5ac70f0Sopenharmony_ci while (1) { 479d5ac70f0Sopenharmony_ci state = snd_pcm_state(handle); 480d5ac70f0Sopenharmony_ci if (state == SND_PCM_STATE_XRUN) { 481d5ac70f0Sopenharmony_ci err = xrun_recovery(handle, -EPIPE); 482d5ac70f0Sopenharmony_ci if (err < 0) { 483d5ac70f0Sopenharmony_ci printf("XRUN recovery failed: %s\n", snd_strerror(err)); 484d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 485d5ac70f0Sopenharmony_ci } 486d5ac70f0Sopenharmony_ci first = 1; 487d5ac70f0Sopenharmony_ci } else if (state == SND_PCM_STATE_SUSPENDED) { 488d5ac70f0Sopenharmony_ci err = xrun_recovery(handle, -ESTRPIPE); 489d5ac70f0Sopenharmony_ci if (err < 0) { 490d5ac70f0Sopenharmony_ci printf("SUSPEND recovery failed: %s\n", snd_strerror(err)); 491d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 492d5ac70f0Sopenharmony_ci } 493d5ac70f0Sopenharmony_ci } 494d5ac70f0Sopenharmony_ci avail = snd_pcm_avail_update(handle); 495d5ac70f0Sopenharmony_ci if (avail < 0) { 496d5ac70f0Sopenharmony_ci err = xrun_recovery(handle, avail); 497d5ac70f0Sopenharmony_ci if (err < 0) { 498d5ac70f0Sopenharmony_ci printf("avail update failed: %s\n", snd_strerror(err)); 499d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 500d5ac70f0Sopenharmony_ci } 501d5ac70f0Sopenharmony_ci first = 1; 502d5ac70f0Sopenharmony_ci continue; 503d5ac70f0Sopenharmony_ci } 504d5ac70f0Sopenharmony_ci if (avail < period_size) { 505d5ac70f0Sopenharmony_ci if (first) { 506d5ac70f0Sopenharmony_ci first = 0; 507d5ac70f0Sopenharmony_ci err = snd_pcm_start(handle); 508d5ac70f0Sopenharmony_ci if (err < 0) { 509d5ac70f0Sopenharmony_ci printf("Start error: %s\n", snd_strerror(err)); 510d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 511d5ac70f0Sopenharmony_ci } 512d5ac70f0Sopenharmony_ci } else { 513d5ac70f0Sopenharmony_ci break; 514d5ac70f0Sopenharmony_ci } 515d5ac70f0Sopenharmony_ci continue; 516d5ac70f0Sopenharmony_ci } 517d5ac70f0Sopenharmony_ci size = period_size; 518d5ac70f0Sopenharmony_ci while (size > 0) { 519d5ac70f0Sopenharmony_ci frames = size; 520d5ac70f0Sopenharmony_ci err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames); 521d5ac70f0Sopenharmony_ci if (err < 0) { 522d5ac70f0Sopenharmony_ci if ((err = xrun_recovery(handle, err)) < 0) { 523d5ac70f0Sopenharmony_ci printf("MMAP begin avail error: %s\n", snd_strerror(err)); 524d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 525d5ac70f0Sopenharmony_ci } 526d5ac70f0Sopenharmony_ci first = 1; 527d5ac70f0Sopenharmony_ci } 528d5ac70f0Sopenharmony_ci generate_sine(my_areas, offset, frames, &data->phase); 529d5ac70f0Sopenharmony_ci commitres = snd_pcm_mmap_commit(handle, offset, frames); 530d5ac70f0Sopenharmony_ci if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) { 531d5ac70f0Sopenharmony_ci if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) { 532d5ac70f0Sopenharmony_ci printf("MMAP commit error: %s\n", snd_strerror(err)); 533d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 534d5ac70f0Sopenharmony_ci } 535d5ac70f0Sopenharmony_ci first = 1; 536d5ac70f0Sopenharmony_ci } 537d5ac70f0Sopenharmony_ci size -= frames; 538d5ac70f0Sopenharmony_ci } 539d5ac70f0Sopenharmony_ci } 540d5ac70f0Sopenharmony_ci} 541d5ac70f0Sopenharmony_ci 542d5ac70f0Sopenharmony_cistatic int async_direct_loop(snd_pcm_t *handle, 543d5ac70f0Sopenharmony_ci signed short *samples ATTRIBUTE_UNUSED, 544d5ac70f0Sopenharmony_ci snd_pcm_channel_area_t *areas ATTRIBUTE_UNUSED) 545d5ac70f0Sopenharmony_ci{ 546d5ac70f0Sopenharmony_ci struct async_private_data data; 547d5ac70f0Sopenharmony_ci snd_async_handler_t *ahandler; 548d5ac70f0Sopenharmony_ci const snd_pcm_channel_area_t *my_areas; 549d5ac70f0Sopenharmony_ci snd_pcm_uframes_t offset, frames, size; 550d5ac70f0Sopenharmony_ci snd_pcm_sframes_t commitres; 551d5ac70f0Sopenharmony_ci int err, count; 552d5ac70f0Sopenharmony_ci 553d5ac70f0Sopenharmony_ci data.samples = NULL; /* we do not require the global sample area for direct write */ 554d5ac70f0Sopenharmony_ci data.areas = NULL; /* we do not require the global areas for direct write */ 555d5ac70f0Sopenharmony_ci data.phase = 0; 556d5ac70f0Sopenharmony_ci err = snd_async_add_pcm_handler(&ahandler, handle, async_direct_callback, &data); 557d5ac70f0Sopenharmony_ci if (err < 0) { 558d5ac70f0Sopenharmony_ci printf("Unable to register async handler\n"); 559d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 560d5ac70f0Sopenharmony_ci } 561d5ac70f0Sopenharmony_ci for (count = 0; count < 2; count++) { 562d5ac70f0Sopenharmony_ci size = period_size; 563d5ac70f0Sopenharmony_ci while (size > 0) { 564d5ac70f0Sopenharmony_ci frames = size; 565d5ac70f0Sopenharmony_ci err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames); 566d5ac70f0Sopenharmony_ci if (err < 0) { 567d5ac70f0Sopenharmony_ci if ((err = xrun_recovery(handle, err)) < 0) { 568d5ac70f0Sopenharmony_ci printf("MMAP begin avail error: %s\n", snd_strerror(err)); 569d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 570d5ac70f0Sopenharmony_ci } 571d5ac70f0Sopenharmony_ci } 572d5ac70f0Sopenharmony_ci generate_sine(my_areas, offset, frames, &data.phase); 573d5ac70f0Sopenharmony_ci commitres = snd_pcm_mmap_commit(handle, offset, frames); 574d5ac70f0Sopenharmony_ci if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) { 575d5ac70f0Sopenharmony_ci if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) { 576d5ac70f0Sopenharmony_ci printf("MMAP commit error: %s\n", snd_strerror(err)); 577d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 578d5ac70f0Sopenharmony_ci } 579d5ac70f0Sopenharmony_ci } 580d5ac70f0Sopenharmony_ci size -= frames; 581d5ac70f0Sopenharmony_ci } 582d5ac70f0Sopenharmony_ci } 583d5ac70f0Sopenharmony_ci err = snd_pcm_start(handle); 584d5ac70f0Sopenharmony_ci if (err < 0) { 585d5ac70f0Sopenharmony_ci printf("Start error: %s\n", snd_strerror(err)); 586d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 587d5ac70f0Sopenharmony_ci } 588d5ac70f0Sopenharmony_ci 589d5ac70f0Sopenharmony_ci /* because all other work is done in the signal handler, 590d5ac70f0Sopenharmony_ci suspend the process */ 591d5ac70f0Sopenharmony_ci while (1) { 592d5ac70f0Sopenharmony_ci sleep(1); 593d5ac70f0Sopenharmony_ci } 594d5ac70f0Sopenharmony_ci} 595d5ac70f0Sopenharmony_ci 596d5ac70f0Sopenharmony_ci/* 597d5ac70f0Sopenharmony_ci * Transfer method - direct write only 598d5ac70f0Sopenharmony_ci */ 599d5ac70f0Sopenharmony_ci 600d5ac70f0Sopenharmony_cistatic int direct_loop(snd_pcm_t *handle, 601d5ac70f0Sopenharmony_ci signed short *samples ATTRIBUTE_UNUSED, 602d5ac70f0Sopenharmony_ci snd_pcm_channel_area_t *areas ATTRIBUTE_UNUSED) 603d5ac70f0Sopenharmony_ci{ 604d5ac70f0Sopenharmony_ci double phase = 0; 605d5ac70f0Sopenharmony_ci const snd_pcm_channel_area_t *my_areas; 606d5ac70f0Sopenharmony_ci snd_pcm_uframes_t offset, frames, size; 607d5ac70f0Sopenharmony_ci snd_pcm_sframes_t avail, commitres; 608d5ac70f0Sopenharmony_ci snd_pcm_state_t state; 609d5ac70f0Sopenharmony_ci int err, first = 1; 610d5ac70f0Sopenharmony_ci 611d5ac70f0Sopenharmony_ci while (1) { 612d5ac70f0Sopenharmony_ci state = snd_pcm_state(handle); 613d5ac70f0Sopenharmony_ci if (state == SND_PCM_STATE_XRUN) { 614d5ac70f0Sopenharmony_ci err = xrun_recovery(handle, -EPIPE); 615d5ac70f0Sopenharmony_ci if (err < 0) { 616d5ac70f0Sopenharmony_ci printf("XRUN recovery failed: %s\n", snd_strerror(err)); 617d5ac70f0Sopenharmony_ci return err; 618d5ac70f0Sopenharmony_ci } 619d5ac70f0Sopenharmony_ci first = 1; 620d5ac70f0Sopenharmony_ci } else if (state == SND_PCM_STATE_SUSPENDED) { 621d5ac70f0Sopenharmony_ci err = xrun_recovery(handle, -ESTRPIPE); 622d5ac70f0Sopenharmony_ci if (err < 0) { 623d5ac70f0Sopenharmony_ci printf("SUSPEND recovery failed: %s\n", snd_strerror(err)); 624d5ac70f0Sopenharmony_ci return err; 625d5ac70f0Sopenharmony_ci } 626d5ac70f0Sopenharmony_ci } 627d5ac70f0Sopenharmony_ci avail = snd_pcm_avail_update(handle); 628d5ac70f0Sopenharmony_ci if (avail < 0) { 629d5ac70f0Sopenharmony_ci err = xrun_recovery(handle, avail); 630d5ac70f0Sopenharmony_ci if (err < 0) { 631d5ac70f0Sopenharmony_ci printf("avail update failed: %s\n", snd_strerror(err)); 632d5ac70f0Sopenharmony_ci return err; 633d5ac70f0Sopenharmony_ci } 634d5ac70f0Sopenharmony_ci first = 1; 635d5ac70f0Sopenharmony_ci continue; 636d5ac70f0Sopenharmony_ci } 637d5ac70f0Sopenharmony_ci if (avail < period_size) { 638d5ac70f0Sopenharmony_ci if (first) { 639d5ac70f0Sopenharmony_ci first = 0; 640d5ac70f0Sopenharmony_ci err = snd_pcm_start(handle); 641d5ac70f0Sopenharmony_ci if (err < 0) { 642d5ac70f0Sopenharmony_ci printf("Start error: %s\n", snd_strerror(err)); 643d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 644d5ac70f0Sopenharmony_ci } 645d5ac70f0Sopenharmony_ci } else { 646d5ac70f0Sopenharmony_ci err = snd_pcm_wait(handle, -1); 647d5ac70f0Sopenharmony_ci if (err < 0) { 648d5ac70f0Sopenharmony_ci if ((err = xrun_recovery(handle, err)) < 0) { 649d5ac70f0Sopenharmony_ci printf("snd_pcm_wait error: %s\n", snd_strerror(err)); 650d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 651d5ac70f0Sopenharmony_ci } 652d5ac70f0Sopenharmony_ci first = 1; 653d5ac70f0Sopenharmony_ci } 654d5ac70f0Sopenharmony_ci } 655d5ac70f0Sopenharmony_ci continue; 656d5ac70f0Sopenharmony_ci } 657d5ac70f0Sopenharmony_ci size = period_size; 658d5ac70f0Sopenharmony_ci while (size > 0) { 659d5ac70f0Sopenharmony_ci frames = size; 660d5ac70f0Sopenharmony_ci err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames); 661d5ac70f0Sopenharmony_ci if (err < 0) { 662d5ac70f0Sopenharmony_ci if ((err = xrun_recovery(handle, err)) < 0) { 663d5ac70f0Sopenharmony_ci printf("MMAP begin avail error: %s\n", snd_strerror(err)); 664d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 665d5ac70f0Sopenharmony_ci } 666d5ac70f0Sopenharmony_ci first = 1; 667d5ac70f0Sopenharmony_ci } 668d5ac70f0Sopenharmony_ci generate_sine(my_areas, offset, frames, &phase); 669d5ac70f0Sopenharmony_ci commitres = snd_pcm_mmap_commit(handle, offset, frames); 670d5ac70f0Sopenharmony_ci if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) { 671d5ac70f0Sopenharmony_ci if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) { 672d5ac70f0Sopenharmony_ci printf("MMAP commit error: %s\n", snd_strerror(err)); 673d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 674d5ac70f0Sopenharmony_ci } 675d5ac70f0Sopenharmony_ci first = 1; 676d5ac70f0Sopenharmony_ci } 677d5ac70f0Sopenharmony_ci size -= frames; 678d5ac70f0Sopenharmony_ci } 679d5ac70f0Sopenharmony_ci } 680d5ac70f0Sopenharmony_ci} 681d5ac70f0Sopenharmony_ci 682d5ac70f0Sopenharmony_ci/* 683d5ac70f0Sopenharmony_ci * Transfer method - direct write only using mmap_write functions 684d5ac70f0Sopenharmony_ci */ 685d5ac70f0Sopenharmony_ci 686d5ac70f0Sopenharmony_cistatic int direct_write_loop(snd_pcm_t *handle, 687d5ac70f0Sopenharmony_ci signed short *samples, 688d5ac70f0Sopenharmony_ci snd_pcm_channel_area_t *areas) 689d5ac70f0Sopenharmony_ci{ 690d5ac70f0Sopenharmony_ci double phase = 0; 691d5ac70f0Sopenharmony_ci signed short *ptr; 692d5ac70f0Sopenharmony_ci int err, cptr; 693d5ac70f0Sopenharmony_ci 694d5ac70f0Sopenharmony_ci while (1) { 695d5ac70f0Sopenharmony_ci generate_sine(areas, 0, period_size, &phase); 696d5ac70f0Sopenharmony_ci ptr = samples; 697d5ac70f0Sopenharmony_ci cptr = period_size; 698d5ac70f0Sopenharmony_ci while (cptr > 0) { 699d5ac70f0Sopenharmony_ci err = snd_pcm_mmap_writei(handle, ptr, cptr); 700d5ac70f0Sopenharmony_ci if (err == -EAGAIN) 701d5ac70f0Sopenharmony_ci continue; 702d5ac70f0Sopenharmony_ci if (err < 0) { 703d5ac70f0Sopenharmony_ci if (xrun_recovery(handle, err) < 0) { 704d5ac70f0Sopenharmony_ci printf("Write error: %s\n", snd_strerror(err)); 705d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 706d5ac70f0Sopenharmony_ci } 707d5ac70f0Sopenharmony_ci break; /* skip one period */ 708d5ac70f0Sopenharmony_ci } 709d5ac70f0Sopenharmony_ci ptr += err * channels; 710d5ac70f0Sopenharmony_ci cptr -= err; 711d5ac70f0Sopenharmony_ci } 712d5ac70f0Sopenharmony_ci } 713d5ac70f0Sopenharmony_ci} 714d5ac70f0Sopenharmony_ci 715d5ac70f0Sopenharmony_ci/* 716d5ac70f0Sopenharmony_ci * 717d5ac70f0Sopenharmony_ci */ 718d5ac70f0Sopenharmony_ci 719d5ac70f0Sopenharmony_cistruct transfer_method { 720d5ac70f0Sopenharmony_ci const char *name; 721d5ac70f0Sopenharmony_ci snd_pcm_access_t access; 722d5ac70f0Sopenharmony_ci int (*transfer_loop)(snd_pcm_t *handle, 723d5ac70f0Sopenharmony_ci signed short *samples, 724d5ac70f0Sopenharmony_ci snd_pcm_channel_area_t *areas); 725d5ac70f0Sopenharmony_ci}; 726d5ac70f0Sopenharmony_ci 727d5ac70f0Sopenharmony_cistatic struct transfer_method transfer_methods[] = { 728d5ac70f0Sopenharmony_ci { "write", SND_PCM_ACCESS_RW_INTERLEAVED, write_loop }, 729d5ac70f0Sopenharmony_ci { "write_and_poll", SND_PCM_ACCESS_RW_INTERLEAVED, write_and_poll_loop }, 730d5ac70f0Sopenharmony_ci { "async", SND_PCM_ACCESS_RW_INTERLEAVED, async_loop }, 731d5ac70f0Sopenharmony_ci { "async_direct", SND_PCM_ACCESS_MMAP_INTERLEAVED, async_direct_loop }, 732d5ac70f0Sopenharmony_ci { "direct_interleaved", SND_PCM_ACCESS_MMAP_INTERLEAVED, direct_loop }, 733d5ac70f0Sopenharmony_ci { "direct_noninterleaved", SND_PCM_ACCESS_MMAP_NONINTERLEAVED, direct_loop }, 734d5ac70f0Sopenharmony_ci { "direct_write", SND_PCM_ACCESS_MMAP_INTERLEAVED, direct_write_loop }, 735d5ac70f0Sopenharmony_ci { NULL, SND_PCM_ACCESS_RW_INTERLEAVED, NULL } 736d5ac70f0Sopenharmony_ci}; 737d5ac70f0Sopenharmony_ci 738d5ac70f0Sopenharmony_cistatic void help(void) 739d5ac70f0Sopenharmony_ci{ 740d5ac70f0Sopenharmony_ci int k; 741d5ac70f0Sopenharmony_ci printf( 742d5ac70f0Sopenharmony_ci"Usage: pcm [OPTION]... [FILE]...\n" 743d5ac70f0Sopenharmony_ci"-h,--help help\n" 744d5ac70f0Sopenharmony_ci"-D,--device playback device\n" 745d5ac70f0Sopenharmony_ci"-r,--rate stream rate in Hz\n" 746d5ac70f0Sopenharmony_ci"-c,--channels count of channels in stream\n" 747d5ac70f0Sopenharmony_ci"-f,--frequency sine wave frequency in Hz\n" 748d5ac70f0Sopenharmony_ci"-b,--buffer ring buffer size in us\n" 749d5ac70f0Sopenharmony_ci"-p,--period period size in us\n" 750d5ac70f0Sopenharmony_ci"-m,--method transfer method\n" 751d5ac70f0Sopenharmony_ci"-o,--format sample format\n" 752d5ac70f0Sopenharmony_ci"-v,--verbose show the PCM setup parameters\n" 753d5ac70f0Sopenharmony_ci"-n,--noresample do not resample\n" 754d5ac70f0Sopenharmony_ci"-e,--pevent enable poll event after each period\n" 755d5ac70f0Sopenharmony_ci"\n"); 756d5ac70f0Sopenharmony_ci printf("Recognized sample formats are:"); 757d5ac70f0Sopenharmony_ci for (k = 0; k < SND_PCM_FORMAT_LAST; ++k) { 758d5ac70f0Sopenharmony_ci const char *s = snd_pcm_format_name(k); 759d5ac70f0Sopenharmony_ci if (s) 760d5ac70f0Sopenharmony_ci printf(" %s", s); 761d5ac70f0Sopenharmony_ci } 762d5ac70f0Sopenharmony_ci printf("\n"); 763d5ac70f0Sopenharmony_ci printf("Recognized transfer methods are:"); 764d5ac70f0Sopenharmony_ci for (k = 0; transfer_methods[k].name; k++) 765d5ac70f0Sopenharmony_ci printf(" %s", transfer_methods[k].name); 766d5ac70f0Sopenharmony_ci printf("\n"); 767d5ac70f0Sopenharmony_ci} 768d5ac70f0Sopenharmony_ci 769d5ac70f0Sopenharmony_ciint main(int argc, char *argv[]) 770d5ac70f0Sopenharmony_ci{ 771d5ac70f0Sopenharmony_ci struct option long_option[] = 772d5ac70f0Sopenharmony_ci { 773d5ac70f0Sopenharmony_ci {"help", 0, NULL, 'h'}, 774d5ac70f0Sopenharmony_ci {"device", 1, NULL, 'D'}, 775d5ac70f0Sopenharmony_ci {"rate", 1, NULL, 'r'}, 776d5ac70f0Sopenharmony_ci {"channels", 1, NULL, 'c'}, 777d5ac70f0Sopenharmony_ci {"frequency", 1, NULL, 'f'}, 778d5ac70f0Sopenharmony_ci {"buffer", 1, NULL, 'b'}, 779d5ac70f0Sopenharmony_ci {"period", 1, NULL, 'p'}, 780d5ac70f0Sopenharmony_ci {"method", 1, NULL, 'm'}, 781d5ac70f0Sopenharmony_ci {"format", 1, NULL, 'o'}, 782d5ac70f0Sopenharmony_ci {"verbose", 1, NULL, 'v'}, 783d5ac70f0Sopenharmony_ci {"noresample", 1, NULL, 'n'}, 784d5ac70f0Sopenharmony_ci {"pevent", 1, NULL, 'e'}, 785d5ac70f0Sopenharmony_ci {NULL, 0, NULL, 0}, 786d5ac70f0Sopenharmony_ci }; 787d5ac70f0Sopenharmony_ci snd_pcm_t *handle; 788d5ac70f0Sopenharmony_ci int err, morehelp; 789d5ac70f0Sopenharmony_ci snd_pcm_hw_params_t *hwparams; 790d5ac70f0Sopenharmony_ci snd_pcm_sw_params_t *swparams; 791d5ac70f0Sopenharmony_ci int method = 0; 792d5ac70f0Sopenharmony_ci signed short *samples; 793d5ac70f0Sopenharmony_ci unsigned int chn; 794d5ac70f0Sopenharmony_ci snd_pcm_channel_area_t *areas; 795d5ac70f0Sopenharmony_ci 796d5ac70f0Sopenharmony_ci snd_pcm_hw_params_alloca(&hwparams); 797d5ac70f0Sopenharmony_ci snd_pcm_sw_params_alloca(&swparams); 798d5ac70f0Sopenharmony_ci 799d5ac70f0Sopenharmony_ci morehelp = 0; 800d5ac70f0Sopenharmony_ci while (1) { 801d5ac70f0Sopenharmony_ci int c; 802d5ac70f0Sopenharmony_ci if ((c = getopt_long(argc, argv, "hD:r:c:f:b:p:m:o:vne", long_option, NULL)) < 0) 803d5ac70f0Sopenharmony_ci break; 804d5ac70f0Sopenharmony_ci switch (c) { 805d5ac70f0Sopenharmony_ci case 'h': 806d5ac70f0Sopenharmony_ci morehelp++; 807d5ac70f0Sopenharmony_ci break; 808d5ac70f0Sopenharmony_ci case 'D': 809d5ac70f0Sopenharmony_ci device = strdup(optarg); 810d5ac70f0Sopenharmony_ci break; 811d5ac70f0Sopenharmony_ci case 'r': 812d5ac70f0Sopenharmony_ci rate = atoi(optarg); 813d5ac70f0Sopenharmony_ci rate = rate < 4000 ? 4000 : rate; 814d5ac70f0Sopenharmony_ci rate = rate > 196000 ? 196000 : rate; 815d5ac70f0Sopenharmony_ci break; 816d5ac70f0Sopenharmony_ci case 'c': 817d5ac70f0Sopenharmony_ci channels = atoi(optarg); 818d5ac70f0Sopenharmony_ci channels = channels < 1 ? 1 : channels; 819d5ac70f0Sopenharmony_ci channels = channels > 1024 ? 1024 : channels; 820d5ac70f0Sopenharmony_ci break; 821d5ac70f0Sopenharmony_ci case 'f': 822d5ac70f0Sopenharmony_ci freq = atoi(optarg); 823d5ac70f0Sopenharmony_ci freq = freq < 50 ? 50 : freq; 824d5ac70f0Sopenharmony_ci freq = freq > 5000 ? 5000 : freq; 825d5ac70f0Sopenharmony_ci break; 826d5ac70f0Sopenharmony_ci case 'b': 827d5ac70f0Sopenharmony_ci buffer_time = atoi(optarg); 828d5ac70f0Sopenharmony_ci buffer_time = buffer_time < 1000 ? 1000 : buffer_time; 829d5ac70f0Sopenharmony_ci buffer_time = buffer_time > 1000000 ? 1000000 : buffer_time; 830d5ac70f0Sopenharmony_ci break; 831d5ac70f0Sopenharmony_ci case 'p': 832d5ac70f0Sopenharmony_ci period_time = atoi(optarg); 833d5ac70f0Sopenharmony_ci period_time = period_time < 1000 ? 1000 : period_time; 834d5ac70f0Sopenharmony_ci period_time = period_time > 1000000 ? 1000000 : period_time; 835d5ac70f0Sopenharmony_ci break; 836d5ac70f0Sopenharmony_ci case 'm': 837d5ac70f0Sopenharmony_ci for (method = 0; transfer_methods[method].name; method++) 838d5ac70f0Sopenharmony_ci if (!strcasecmp(transfer_methods[method].name, optarg)) 839d5ac70f0Sopenharmony_ci break; 840d5ac70f0Sopenharmony_ci if (transfer_methods[method].name == NULL) 841d5ac70f0Sopenharmony_ci method = 0; 842d5ac70f0Sopenharmony_ci break; 843d5ac70f0Sopenharmony_ci case 'o': 844d5ac70f0Sopenharmony_ci for (format = 0; format < SND_PCM_FORMAT_LAST; format++) { 845d5ac70f0Sopenharmony_ci const char *format_name = snd_pcm_format_name(format); 846d5ac70f0Sopenharmony_ci if (format_name) 847d5ac70f0Sopenharmony_ci if (!strcasecmp(format_name, optarg)) 848d5ac70f0Sopenharmony_ci break; 849d5ac70f0Sopenharmony_ci } 850d5ac70f0Sopenharmony_ci if (format == SND_PCM_FORMAT_LAST) 851d5ac70f0Sopenharmony_ci format = SND_PCM_FORMAT_S16; 852d5ac70f0Sopenharmony_ci if (!snd_pcm_format_linear(format) && 853d5ac70f0Sopenharmony_ci !(format == SND_PCM_FORMAT_FLOAT_LE || 854d5ac70f0Sopenharmony_ci format == SND_PCM_FORMAT_FLOAT_BE)) { 855d5ac70f0Sopenharmony_ci printf("Invalid (non-linear/float) format %s\n", 856d5ac70f0Sopenharmony_ci optarg); 857d5ac70f0Sopenharmony_ci return 1; 858d5ac70f0Sopenharmony_ci } 859d5ac70f0Sopenharmony_ci break; 860d5ac70f0Sopenharmony_ci case 'v': 861d5ac70f0Sopenharmony_ci verbose = 1; 862d5ac70f0Sopenharmony_ci break; 863d5ac70f0Sopenharmony_ci case 'n': 864d5ac70f0Sopenharmony_ci resample = 0; 865d5ac70f0Sopenharmony_ci break; 866d5ac70f0Sopenharmony_ci case 'e': 867d5ac70f0Sopenharmony_ci period_event = 1; 868d5ac70f0Sopenharmony_ci break; 869d5ac70f0Sopenharmony_ci } 870d5ac70f0Sopenharmony_ci } 871d5ac70f0Sopenharmony_ci 872d5ac70f0Sopenharmony_ci if (morehelp) { 873d5ac70f0Sopenharmony_ci help(); 874d5ac70f0Sopenharmony_ci return 0; 875d5ac70f0Sopenharmony_ci } 876d5ac70f0Sopenharmony_ci 877d5ac70f0Sopenharmony_ci err = snd_output_stdio_attach(&output, stdout, 0); 878d5ac70f0Sopenharmony_ci if (err < 0) { 879d5ac70f0Sopenharmony_ci printf("Output failed: %s\n", snd_strerror(err)); 880d5ac70f0Sopenharmony_ci return 0; 881d5ac70f0Sopenharmony_ci } 882d5ac70f0Sopenharmony_ci 883d5ac70f0Sopenharmony_ci printf("Playback device is %s\n", device); 884d5ac70f0Sopenharmony_ci printf("Stream parameters are %uHz, %s, %u channels\n", rate, snd_pcm_format_name(format), channels); 885d5ac70f0Sopenharmony_ci printf("Sine wave rate is %.4fHz\n", freq); 886d5ac70f0Sopenharmony_ci printf("Using transfer method: %s\n", transfer_methods[method].name); 887d5ac70f0Sopenharmony_ci 888d5ac70f0Sopenharmony_ci if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { 889d5ac70f0Sopenharmony_ci printf("Playback open error: %s\n", snd_strerror(err)); 890d5ac70f0Sopenharmony_ci return 0; 891d5ac70f0Sopenharmony_ci } 892d5ac70f0Sopenharmony_ci 893d5ac70f0Sopenharmony_ci if ((err = set_hwparams(handle, hwparams, transfer_methods[method].access)) < 0) { 894d5ac70f0Sopenharmony_ci printf("Setting of hwparams failed: %s\n", snd_strerror(err)); 895d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 896d5ac70f0Sopenharmony_ci } 897d5ac70f0Sopenharmony_ci if ((err = set_swparams(handle, swparams)) < 0) { 898d5ac70f0Sopenharmony_ci printf("Setting of swparams failed: %s\n", snd_strerror(err)); 899d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 900d5ac70f0Sopenharmony_ci } 901d5ac70f0Sopenharmony_ci 902d5ac70f0Sopenharmony_ci if (verbose > 0) 903d5ac70f0Sopenharmony_ci snd_pcm_dump(handle, output); 904d5ac70f0Sopenharmony_ci 905d5ac70f0Sopenharmony_ci samples = malloc((period_size * channels * snd_pcm_format_physical_width(format)) / 8); 906d5ac70f0Sopenharmony_ci if (samples == NULL) { 907d5ac70f0Sopenharmony_ci printf("No enough memory\n"); 908d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 909d5ac70f0Sopenharmony_ci } 910d5ac70f0Sopenharmony_ci 911d5ac70f0Sopenharmony_ci areas = calloc(channels, sizeof(snd_pcm_channel_area_t)); 912d5ac70f0Sopenharmony_ci if (areas == NULL) { 913d5ac70f0Sopenharmony_ci printf("No enough memory\n"); 914d5ac70f0Sopenharmony_ci exit(EXIT_FAILURE); 915d5ac70f0Sopenharmony_ci } 916d5ac70f0Sopenharmony_ci for (chn = 0; chn < channels; chn++) { 917d5ac70f0Sopenharmony_ci areas[chn].addr = samples; 918d5ac70f0Sopenharmony_ci areas[chn].first = chn * snd_pcm_format_physical_width(format); 919d5ac70f0Sopenharmony_ci areas[chn].step = channels * snd_pcm_format_physical_width(format); 920d5ac70f0Sopenharmony_ci } 921d5ac70f0Sopenharmony_ci 922d5ac70f0Sopenharmony_ci err = transfer_methods[method].transfer_loop(handle, samples, areas); 923d5ac70f0Sopenharmony_ci if (err < 0) 924d5ac70f0Sopenharmony_ci printf("Transfer failed: %s\n", snd_strerror(err)); 925d5ac70f0Sopenharmony_ci 926d5ac70f0Sopenharmony_ci free(areas); 927d5ac70f0Sopenharmony_ci free(samples); 928d5ac70f0Sopenharmony_ci snd_pcm_close(handle); 929d5ac70f0Sopenharmony_ci return 0; 930d5ac70f0Sopenharmony_ci} 931d5ac70f0Sopenharmony_ci 932