2 * This file is part of DisOrder.
3 * Copyright (C) 2007 Richard Kettlewell
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.
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.
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
20 /** @file clients/playrtp-alsa.c
21 * @brief RTP player - ALSA support
26 #if HAVE_ALSA_ASOUNDLIB_H
30 #include <alsa/asoundlib.h>
33 #include <arpa/inet.h>
41 /** @brief PCM handle */
42 static snd_pcm_t
*pcm
;
44 /** @brief True when @ref pcm is up and running */
45 static int playrtp_alsa_prepared
= 1;
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;
61 if((err
= snd_pcm_open(&pcm
,
62 device ? device
: "default",
63 SND_PCM_STREAM_PLAYBACK
,
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
,
76 fatal(0, "error from snd_pcm_hw_params_set_format (%d): %d",
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",
81 if((err
= snd_pcm_hw_params_set_channels(pcm
, hwparams
,
83 fatal(0, "error from snd_pcm_hw_params_set_channels (%d): %d",
85 if((err
= snd_pcm_hw_params_set_buffer_size_near(pcm
, hwparams
,
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",
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",
104 if((err
= snd_pcm_sw_params(pcm
, swparams
)) < 0)
105 fatal(0, "error calling snd_pcm_sw_params: %d", err
);
108 /** @brief Wait until ALSA wants some audio */
109 static void wait_alsa(void) {
110 struct pollfd fds
[64];
112 unsigned short events
;
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
);
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
132 static int playrtp_alsa_writei(const void *s
, size_t n
) {
134 snd_pcm_sframes_t frames_written
;
137 frames_written
= snd_pcm_writei(pcm
, s
, n
/ 2);
138 if(frames_written
< 0) {
139 /* Something went wrong */
140 switch(frames_written
) {
144 error(0, "error calling snd_pcm_writei: %ld",
145 (long)frames_written
);
146 if((err
= snd_pcm_prepare(pcm
)) < 0) {
147 error(0, "error calling snd_pcm_prepare: %d", err
);
150 frames_written
= snd_pcm_writei(pcm
, s
, n
/ 2);
151 if(frames_written
== -EAGAIN
)
153 else if(frames_written
< 0) {
154 error(0, "error calling snd_pcm_writei: %ld",
155 (long)frames_written
);
160 fatal(0, "error calling snd_pcm_writei: %ld",
161 (long)frames_written
);
165 next_timestamp
+= frames_written
* 2;
167 snd_pcm_sframes_t count
;
168 const int16_t *sp
= s
;
170 for(count
= 0; count
< frames_written
* 2; ++count
) {
171 dump_buffer
[dump_index
++] = (int16_t)ntohs(*sp
++);
172 dump_index
%= dump_size
;
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
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
);
187 /** @brief Play some silence
188 * @param p Next packet or NULL
189 * @return 0 on success, -1 on non-fatal error
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
;
195 if(p
&& samples_available
> p
->timestamp
- next_timestamp
)
196 samples_available
= p
->timestamp
- next_timestamp
;
197 return playrtp_alsa_writei(zeros
, samples_available
);
200 static void playrtp_alsa_enable(void){
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;
210 /** @brief Reset ALSA state after we lost synchronization */
211 static void playrtp_alsa_disable(int hard_reset
) {
214 if((err
= snd_pcm_nonblock(pcm
, 0)))
215 fatal(0, "error calling snd_pcm_nonblock: %d", err
);
217 if((err
= snd_pcm_drop(pcm
)))
218 fatal(0, "error calling snd_pcm_drop: %d", err
);
220 if((err
= snd_pcm_drain(pcm
)))
221 fatal(0, "error calling snd_pcm_drain: %d", err
);
222 if((err
= snd_pcm_nonblock(pcm
, 1)))
223 fatal(0, "error calling snd_pcm_nonblock: %d", err
);
224 playrtp_alsa_prepared
= 0;
227 void playrtp_alsa(void) {
229 const struct packet
*p
;
232 pthread_mutex_lock(&lock
);
234 /* Wait for the buffer to fill up a bit */
235 playrtp_fill_buffer();
236 playrtp_alsa_enable();
239 /* Keep playing until the buffer empties out, or ALSA tells us to get
241 while((nsamples
>= minbuffer
243 && contains(pheap_first(&packets
), next_timestamp
)))
245 /* Wait for ALSA to ask us for more data */
246 pthread_mutex_unlock(&lock
);
248 pthread_mutex_lock(&lock
);
249 /* ALSA is ready for more data, find something to play */
250 p
= playrtp_next_packet();
251 /* Play it or play some silence */
252 if(contains(p
, next_timestamp
))
253 escape
= playrtp_alsa_play(p
);
255 escape
= playrtp_alsa_infill(p
);
258 /* We stop playing for a bit until the buffer re-fills */
259 pthread_mutex_unlock(&lock
);
260 playrtp_alsa_disable(escape
);
261 pthread_mutex_lock(&lock
);