chiark / gitweb /
Further ALSA underrun fiddling.
[disorder] / clients / playrtp-alsa.c
1 /*
2  * This file is part of DisOrder.
3  * Copyright (C) 2007 Richard Kettlewell
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18  * USA
19  */
20 /** @file clients/playrtp-alsa.c
21  * @brief RTP player - ALSA support
22  */
23
24 #include <config.h>
25
26 #if HAVE_ALSA_ASOUNDLIB_H 
27 #include "types.h"
28
29 #include <poll.h>
30 #include <alsa/asoundlib.h>
31 #include <assert.h>
32 #include <pthread.h>
33 #include <arpa/inet.h>
34
35 #include "mem.h"
36 #include "log.h"
37 #include "vector.h"
38 #include "heap.h"
39 #include "playrtp.h"
40
41 /** @brief PCM handle */
42 static snd_pcm_t *pcm;
43
44 /** @brief True when @ref pcm is up and running */
45 static int playrtp_alsa_prepared = 1;
46
47 static void playrtp_alsa_init(void) {
48   snd_pcm_hw_params_t *hwparams;
49   snd_pcm_sw_params_t *swparams;
50   /* Only support one format for now */
51   const int sample_format = SND_PCM_FORMAT_S16_BE;
52   unsigned rate = 44100;
53   const int channels = 2;
54   const int samplesize = channels * sizeof(uint16_t);
55   snd_pcm_uframes_t pcm_bufsize = MAXSAMPLES * samplesize * 3;
56   /* If we can write more than this many samples we'll get a wakeup */
57   const int avail_min = 256;
58   int err;
59   
60   /* Open ALSA */
61   if((err = snd_pcm_open(&pcm,
62                          device ? device : "default",
63                          SND_PCM_STREAM_PLAYBACK,
64                          SND_PCM_NONBLOCK)))
65     fatal(0, "error from snd_pcm_open: %d", err);
66   /* Set up 'hardware' parameters */
67   snd_pcm_hw_params_alloca(&hwparams);
68   if((err = snd_pcm_hw_params_any(pcm, hwparams)) < 0)
69     fatal(0, "error from snd_pcm_hw_params_any: %d", err);
70   if((err = snd_pcm_hw_params_set_access(pcm, hwparams,
71                                          SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
72     fatal(0, "error from snd_pcm_hw_params_set_access: %d", err);
73   if((err = snd_pcm_hw_params_set_format(pcm, hwparams,
74                                          sample_format)) < 0)
75     
76     fatal(0, "error from snd_pcm_hw_params_set_format (%d): %d",
77           sample_format, err);
78   if((err = snd_pcm_hw_params_set_rate_near(pcm, hwparams, &rate, 0)) < 0)
79     fatal(0, "error from snd_pcm_hw_params_set_rate (%d): %d",
80           rate, err);
81   if((err = snd_pcm_hw_params_set_channels(pcm, hwparams,
82                                            channels)) < 0)
83     fatal(0, "error from snd_pcm_hw_params_set_channels (%d): %d",
84           channels, err);
85   if((err = snd_pcm_hw_params_set_buffer_size_near(pcm, hwparams,
86                                                    &pcm_bufsize)) < 0)
87     fatal(0, "error from snd_pcm_hw_params_set_buffer_size (%ld): %d",
88           (long)pcm_bufsize, err);
89   if((err = snd_pcm_hw_params(pcm, hwparams)) < 0)
90     fatal(0, "error calling snd_pcm_hw_params: %d", err);
91   /* Set up 'software' parameters */
92   snd_pcm_sw_params_alloca(&swparams);
93   if((err = snd_pcm_sw_params_current(pcm, swparams)) < 0)
94     fatal(0, "error calling snd_pcm_sw_params_current: %d", err);
95   if((err = snd_pcm_sw_params_set_avail_min(pcm, swparams, avail_min)) < 0)
96     fatal(0, "error calling snd_pcm_sw_params_set_avail_min %d: %d",
97           avail_min, err);
98   /* Default start threshold is 1, which means that PCM starts as soon as we've
99    * written anything.  Setting it to pcm_bufsize (around 15000) produces
100    * -EINVAL.  1024 is a guess... */
101   if((err = snd_pcm_sw_params_set_start_threshold(pcm, swparams, 1024)) < 0)
102     fatal(0, "error calling snd_pcm_sw_params_set_start_threshold %d: %d",
103           1024, err);
104   if((err = snd_pcm_sw_params(pcm, swparams)) < 0)
105     fatal(0, "error calling snd_pcm_sw_params: %d", err);
106 }
107
108 /** @brief Wait until ALSA wants some audio */
109 static void wait_alsa(void) {
110   struct pollfd fds[64];
111   int nfds, err;
112   unsigned short events;
113
114   for(;;) {
115     do {
116       if((nfds = snd_pcm_poll_descriptors(pcm,
117                                           fds, sizeof fds / sizeof *fds)) < 0)
118         fatal(0, "error calling snd_pcm_poll_descriptors: %d", nfds);
119     } while(poll(fds, nfds, -1) < 0 && errno == EINTR);
120     if((err = snd_pcm_poll_descriptors_revents(pcm, fds, nfds, &events)))
121       fatal(0, "error calling snd_pcm_poll_descriptors_revents: %d", err);
122     if(events & POLLOUT)
123       return;
124   }
125 }
126
127 /** @brief Play some sound via ALSA
128  * @param s Pointer to sample data
129  * @param n Number of samples
130  * @return 0 on success, -1 on non-fatal error
131  */
132 static int playrtp_alsa_writei(const void *s, size_t n) {
133   int err;
134   snd_pcm_sframes_t frames_written;
135   
136   /* Do the write */
137   frames_written = snd_pcm_writei(pcm, s, n / 2);
138   if(frames_written < 0) {
139     /* Something went wrong */
140     switch(frames_written) {
141     case -EAGAIN:
142       return 0;
143     case -EPIPE:
144       error(0, "error calling snd_pcm_writei: %ld",
145             (long)frames_written);
146       if((err = snd_pcm_recover(pcm, -EPIPE, 0)) < 0) {
147         error(0, "error calling snd_pcm_recover: %d", err);
148         return -1;
149       }
150       frames_written = snd_pcm_writei(pcm, s, n / 2);
151       if(frames_written == -EAGAIN)
152         return 0;
153       else if(frames_written < 0) {
154         error(0, "error retrying snd_pcm_writei: %ld",
155               (long)frames_written);
156         return -1;
157       }
158       break;
159     default:
160       fatal(0, "error calling snd_pcm_writei: %ld",
161             (long)frames_written);
162     }
163   }
164   /* Success */
165   next_timestamp += frames_written * 2;
166   if(dump_buffer) {
167     snd_pcm_sframes_t count;
168     const int16_t *sp = s;
169     
170     for(count = 0; count < frames_written * 2; ++count) {
171       dump_buffer[dump_index++] = (int16_t)ntohs(*sp++);
172       dump_index %= dump_size;
173     }
174   }
175   return 0;
176 }
177
178 /** @brief Play the relevant part of a packet
179  * @param p Packet to play
180  * @return 0 on success, -1 on non-fatal error
181  */
182 static int playrtp_alsa_play(const struct packet *p) {
183   return playrtp_alsa_writei(p->samples_raw + next_timestamp - p->timestamp,
184                              (p->timestamp + p->nsamples) - next_timestamp);
185 }
186
187 /** @brief Play some silence
188  * @param p Next packet or NULL
189  * @return 0 on success, -1 on non-fatal error
190  */
191 static int playrtp_alsa_infill(const struct packet *p) {
192   static const uint16_t zeros[INFILL_SAMPLES];
193   size_t samples_available = INFILL_SAMPLES;
194
195   if(p && samples_available > p->timestamp - next_timestamp)
196     samples_available = p->timestamp - next_timestamp;
197   return playrtp_alsa_writei(zeros, samples_available);
198 }
199
200 static void playrtp_alsa_enable(void){
201   int err;
202
203   if(!playrtp_alsa_prepared) {
204     if((err = snd_pcm_prepare(pcm)))
205       fatal(0, "error calling snd_pcm_prepare: %d", err);
206     playrtp_alsa_prepared = 1;
207   }
208 }
209
210 /** @brief Reset ALSA state after we lost synchronization */
211 static void playrtp_alsa_disable(int hard_reset) {
212   int err;
213
214   if((err = snd_pcm_nonblock(pcm, 0)))
215     fatal(0, "error calling snd_pcm_nonblock: %d", err);
216   if(hard_reset) {
217     if((err = snd_pcm_drop(pcm)))
218       fatal(0, "error calling snd_pcm_drop: %d", err);
219   } else {
220     if((err = snd_pcm_drain(pcm))) {
221       error(0, "error calling snd_pcm_drain: %d", err);
222       if((err = snd_pcm_drop(pcm)))
223         fatal(0, "error calling snd_pcm_drop: %d", err);
224     }
225   }
226   if((err = snd_pcm_nonblock(pcm, 1)))
227     fatal(0, "error calling snd_pcm_nonblock: %d", err);
228   playrtp_alsa_prepared = 0;
229 }
230
231 void playrtp_alsa(void) {
232   int escape;
233   const struct packet *p;
234
235   playrtp_alsa_init();
236   pthread_mutex_lock(&lock);
237   for(;;) {
238     /* Wait for the buffer to fill up a bit */
239     playrtp_fill_buffer();
240     playrtp_alsa_enable();
241     escape = 0;
242     info("Playing...");
243     /* Keep playing until the buffer empties out, or ALSA tells us to get
244      * lost */
245     while((nsamples >= minbuffer
246            || (nsamples > 0
247                && contains(pheap_first(&packets), next_timestamp)))
248           && !escape) {
249       /* Wait for ALSA to ask us for more data */
250       pthread_mutex_unlock(&lock);
251       wait_alsa();
252       pthread_mutex_lock(&lock);
253       /* ALSA is ready for more data, find something to play */
254       p = playrtp_next_packet();
255       /* Play it or play some silence */
256       if(contains(p, next_timestamp))
257         escape = playrtp_alsa_play(p);
258       else
259         escape = playrtp_alsa_infill(p);
260     }
261     active = 0;
262     /* We stop playing for a bit until the buffer re-fills */
263     pthread_mutex_unlock(&lock);
264     playrtp_alsa_disable(escape);
265     pthread_mutex_lock(&lock);
266   }
267 }
268
269 #endif
270
271 /*
272 Local Variables:
273 c-basic-offset:2
274 comment-column:40
275 fill-column:79
276 indent-tabs-mode:nil
277 End:
278 */