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