source: vital-to8-sdk/music/mod/alsa_playback.c @ 1

Last change on this file since 1 was 1, checked in by svn, 5 years ago

Import initial

File size: 13.5 KB
Line 
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <sched.h>
5#include <errno.h>
6#include <getopt.h>
7#include <alsa/asoundlib.h>
8#include <sys/time.h>
9#include <math.h>
10
11static char *device = "plughw:0,0";                     /* playback device */
12static snd_pcm_format_t format = SND_PCM_FORMAT_S16;    /* sample format */
13static unsigned int rate = 44100;                       /* stream rate */
14static unsigned int channels = 1;                       /* count of channels */
15static unsigned int buffer_time = 500000;               /* ring buffer length in us */
16static unsigned int period_time = 100000;               /* period time in us */
17static int resample = 1;                                /* enable alsa-lib resampling */
18static int period_event = 0;                            /* produce poll event after each period */
19static snd_pcm_sframes_t buffer_size;
20static snd_pcm_sframes_t period_size;
21static snd_output_t *output = NULL;
22
23static void generate_sine(const snd_pcm_channel_area_t *areas,
24                          snd_pcm_uframes_t offset,
25                          int count, double *_phase)
26{
27        unsigned char *samples[channels];
28        int steps[channels];
29        unsigned int chn;
30        int format_bits = snd_pcm_format_width(format);
31        unsigned int maxval = (1 << (format_bits - 1)) - 1;
32        int bps = format_bits / 8;  /* bytes per sample */
33        int phys_bps = snd_pcm_format_physical_width(format) / 8;
34        int big_endian = snd_pcm_format_big_endian(format) == 1;
35        int to_unsigned = snd_pcm_format_unsigned(format) == 1;
36
37        /* verify and prepare the contents of areas */
38        for (chn = 0; chn < channels; chn++) {
39                if ((areas[chn].first % 8) != 0) {
40                        printf("areas[%i].first == %i, aborting...\n", chn, areas[chn].first);
41                        exit(EXIT_FAILURE);
42                }
43                samples[chn] = (((unsigned char *)areas[chn].addr) + (areas[chn].first / 8));
44                if ((areas[chn].step % 16) != 0) {
45                        printf("areas[%i].step == %i, aborting...\n", chn, areas[chn].step);
46                        exit(EXIT_FAILURE);
47                }
48                steps[chn] = areas[chn].step / 8;
49                samples[chn] += offset * steps[chn];
50        }
51        /* fill the channel areas */
52        while (count-- > 0) {
53                int res, i;
54                int tab[1];
55
56                build_chunk(1,&tab);
57                res=tab[0]*127;
58               
59                if (to_unsigned)
60                        res ^= 1U << (format_bits - 1);
61                for (chn = 0; chn < channels; chn++) {
62                        /* Generate data in native endian format */
63                        if (big_endian) {
64                                for (i = 0; i < bps; i++)
65                                        *(samples[chn] + phys_bps - 1 - i) = (res >> i * 8) & 0xff;
66                        } else {
67                                for (i = 0; i < bps; i++)
68                                        *(samples[chn] + i) = (res >>  i * 8) & 0xff;
69                        }
70                        samples[chn] += steps[chn];
71                }
72        }
73}
74
75
76static int set_hwparams(snd_pcm_t *handle,
77                        snd_pcm_hw_params_t *params,
78                        snd_pcm_access_t access)
79{
80        unsigned int rrate;
81        snd_pcm_uframes_t size;
82        int err, dir;
83        /* choose all parameters */
84        err = snd_pcm_hw_params_any(handle, params);
85        if (err < 0) {
86                printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
87                return err;
88        }
89        /* set hardware resampling */
90        err = snd_pcm_hw_params_set_rate_resample(handle, params, resample);
91        if (err < 0) {
92                printf("Resampling setup failed for playback: %s\n", snd_strerror(err));
93                return err;
94        }
95        /* set the interleaved read/write format */
96        err = snd_pcm_hw_params_set_access(handle, params, access);
97        if (err < 0) {
98                printf("Access type not available for playback: %s\n", snd_strerror(err));
99                return err;
100        }
101        /* set the sample format */
102        err = snd_pcm_hw_params_set_format(handle, params, format);
103        if (err < 0) {
104                printf("Sample format not available for playback: %s\n", snd_strerror(err));
105                return err;
106        }
107        /* set the count of channels */
108        err = snd_pcm_hw_params_set_channels(handle, params, channels);
109        if (err < 0) {
110                printf("Channels count (%i) not available for playbacks: %s\n", channels, snd_strerror(err));
111                return err;
112        }
113        /* set the stream rate */
114        rrate = rate;
115        err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0);
116        if (err < 0) {
117                printf("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err));
118                return err;
119        }
120        if (rrate != rate) {
121                printf("Rate doesn't match (requested %iHz, get %iHz)\n", rate, err);
122                return -EINVAL;
123        }
124        /* set the buffer time */
125        err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir);
126        if (err < 0) {
127                printf("Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err));
128                return err;
129        }
130        err = snd_pcm_hw_params_get_buffer_size(params, &size);
131        if (err < 0) {
132                printf("Unable to get buffer size for playback: %s\n", snd_strerror(err));
133                return err;
134        }
135        buffer_size = size;
136        /* set the period time */
137        err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir);
138        if (err < 0) {
139                printf("Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err));
140                return err;
141        }
142        err = snd_pcm_hw_params_get_period_size(params, &size, &dir);
143        if (err < 0) {
144                printf("Unable to get period size for playback: %s\n", snd_strerror(err));
145                return err;
146        }
147        period_size = size;
148        /* write the parameters to device */
149        err = snd_pcm_hw_params(handle, params);
150        if (err < 0) {
151                printf("Unable to set hw params for playback: %s\n", snd_strerror(err));
152                return err;
153        }
154        return 0;
155}
156static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams)
157{
158        int err;
159        /* get the current swparams */
160        err = snd_pcm_sw_params_current(handle, swparams);
161        if (err < 0) {
162                printf("Unable to determine current swparams for playback: %s\n", snd_strerror(err));
163                return err;
164        }
165        /* start the transfer when the buffer is almost full: */
166        /* (buffer_size / avail_min) * avail_min */
167        err = snd_pcm_sw_params_set_start_threshold(handle, swparams, (buffer_size / period_size) * period_size);
168        if (err < 0) {
169                printf("Unable to set start threshold mode for playback: %s\n", snd_strerror(err));
170                return err;
171        }
172        /* allow the transfer when at least period_size samples can be processed */
173        /* or disable this mechanism when period event is enabled (aka interrupt like style processing) */
174        err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_event ? buffer_size : period_size);
175        if (err < 0) {
176                printf("Unable to set avail min for playback: %s\n", snd_strerror(err));
177                return err;
178        }
179        /* enable period events when requested */
180        if (period_event) {
181                err = snd_pcm_sw_params_set_period_event(handle, swparams, 1);
182                if (err < 0) {
183                        printf("Unable to set period event: %s\n", snd_strerror(err));
184                        return err;
185                }
186        }
187        /* write the parameters to the playback device */
188        err = snd_pcm_sw_params(handle, swparams);
189        if (err < 0) {
190                printf("Unable to set sw params for playback: %s\n", snd_strerror(err));
191                return err;
192        }
193        return 0;
194}
195/*
196 *   Underrun and suspend recovery
197 */
198 
199static int xrun_recovery(snd_pcm_t *handle, int err)
200{
201        if (err == -EPIPE) {    /* under-run */
202                err = snd_pcm_prepare(handle);
203                if (err < 0)
204                        printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err));
205                return 0;
206        } else if (err == -ESTRPIPE) {
207                while ((err = snd_pcm_resume(handle)) == -EAGAIN)
208                        sleep(1);       /* wait until the suspend flag is released */
209                if (err < 0) {
210                        err = snd_pcm_prepare(handle);
211                        if (err < 0)
212                                printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err));
213                }
214                return 0;
215        }
216        return err;
217}
218
219struct async_private_data {
220        signed short *samples;
221        snd_pcm_channel_area_t *areas;
222        double phase;
223};
224
225static void async_callback(snd_async_handler_t *ahandler)
226{
227        snd_pcm_t *handle = snd_async_handler_get_pcm(ahandler);
228        struct async_private_data *data = snd_async_handler_get_callback_private(ahandler);
229        signed short *samples = data->samples;
230        snd_pcm_channel_area_t *areas = data->areas;
231        snd_pcm_sframes_t avail;
232        int err;
233       
234        avail = snd_pcm_avail_update(handle);
235        while (avail >= period_size) {
236                generate_sine(areas, 0, period_size, &data->phase);
237                err = snd_pcm_writei(handle, samples, period_size);
238                if (err < 0) {
239                        printf("Write error: %s\n", snd_strerror(err));
240                        exit(EXIT_FAILURE);
241                }
242                if (err != period_size) {
243                        printf("Write error: written %i expected %li\n", err, period_size);
244                        exit(EXIT_FAILURE);
245                }
246                avail = snd_pcm_avail_update(handle);
247        }
248}
249
250struct async_private_data data;
251
252static int async_loop(snd_pcm_t *handle,
253                      signed short *samples,
254                      snd_pcm_channel_area_t *areas)
255{
256        snd_async_handler_t *ahandler;
257        int err, count;
258        data.samples = samples;
259        data.areas = areas;
260        data.phase = 0;
261       
262        snd_pcm_prepare (handle);
263       
264        err = snd_async_add_pcm_handler(&ahandler, handle, async_callback, &data);
265        if (err < 0) {
266                printf("Unable to register async handler\n");
267                exit(EXIT_FAILURE);
268        }
269        for (count = 0; count < 2; count++) {
270                generate_sine(areas, 0, period_size, &data.phase);
271                err = snd_pcm_writei(handle, samples, period_size);
272                if (err < 0) {
273                        printf("Initial write error: %s\n", snd_strerror(err));
274                        exit(EXIT_FAILURE);
275                }
276                if (err != period_size) {
277                        printf("Initial write error: written %i expected %li\n", err, period_size);
278                        exit(EXIT_FAILURE);
279                }
280        }
281        if (snd_pcm_state(handle) == SND_PCM_STATE_PREPARED) {
282                err = snd_pcm_start(handle);
283                if (err < 0) {
284                        printf("Start error: %s\n", snd_strerror(err));
285                        exit(EXIT_FAILURE);
286                }
287        }
288}
289
290static snd_pcm_t *handle=NULL;
291static snd_pcm_channel_area_t *areas=NULL;
292signed short *samples;
293
294
295int
296start_playback(void) {
297        int err, morehelp;
298        snd_pcm_hw_params_t *hwparams;
299        snd_pcm_sw_params_t *swparams;
300        int method = 0;
301        unsigned int chn;
302        snd_pcm_hw_params_alloca(&hwparams);
303        snd_pcm_sw_params_alloca(&swparams);
304       
305         if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
306                printf("Playback open error: %s\n", snd_strerror(err));
307                return 0;
308        }
309       
310        if ((err = set_hwparams(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
311                printf("Setting of hwparams failed: %s\n", snd_strerror(err));
312                exit(EXIT_FAILURE);
313        }
314        if ((err = set_swparams(handle, swparams)) < 0) {
315                printf("Setting of swparams failed: %s\n", snd_strerror(err));
316                exit(EXIT_FAILURE);
317        }
318
319        samples = malloc((period_size * channels * snd_pcm_format_physical_width(format)) / 8);
320        if (samples == NULL) {
321                printf("No enough memory\n");
322                exit(EXIT_FAILURE);
323        }
324       
325        areas = calloc(channels, sizeof(snd_pcm_channel_area_t));
326        if (areas == NULL) {
327                printf("No enough memory\n");
328                exit(EXIT_FAILURE);
329        }
330        for (chn = 0; chn < channels; chn++) {
331                areas[chn].addr = samples;
332                areas[chn].first = chn * snd_pcm_format_physical_width(format);
333                areas[chn].step = channels * snd_pcm_format_physical_width(format);
334        }
335        err = async_loop(handle, samples, areas);
336        if (err < 0)
337                printf("Transfer failed: %s\n", snd_strerror(err));
338}
339
340 void stop_playback() {
341         if (handle!=NULL) {
342                snd_pcm_drop(handle);
343        free(areas);
344        free(samples);
345        snd_pcm_close(handle);
346        handle=NULL;
347        }
348}       
349
Note: See TracBrowser for help on using the repository browser.