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 (%d): %d",
88 MAXSAMPLES
* samplesize
* 3, 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 if((err
= snd_pcm_sw_params(pcm
, swparams
)) < 0)
99 fatal(0, "error calling snd_pcm_sw_params: %d", err
);
102 /** @brief Wait until ALSA wants some audio */
103 static void wait_alsa(void) {
104 struct pollfd fds
[64];
106 unsigned short events
;
110 if((nfds
= snd_pcm_poll_descriptors(pcm
,
111 fds
, sizeof fds
/ sizeof *fds
)) < 0)
112 fatal(0, "error calling snd_pcm_poll_descriptors: %d", nfds
);
113 } while(poll(fds
, nfds
, -1) < 0 && errno
== EINTR
);
114 if((err
= snd_pcm_poll_descriptors_revents(pcm
, fds
, nfds
, &events
)))
115 fatal(0, "error calling snd_pcm_poll_descriptors_revents: %d", err
);
121 /** @brief Play some sound via ALSA
122 * @param s Pointer to sample data
123 * @param n Number of samples
124 * @return 0 on success, -1 on non-fatal error
126 static int playrtp_alsa_writei(const void *s
, size_t n
) {
128 const snd_pcm_sframes_t frames_written
= snd_pcm_writei(pcm
, s
, n
/ 2);
129 if(frames_written
< 0) {
130 /* Something went wrong */
131 switch(frames_written
) {
135 error(0, "error calling snd_pcm_writei: %ld",
136 (long)frames_written
);
139 fatal(0, "error calling snd_pcm_writei: %ld",
140 (long)frames_written
);
144 next_timestamp
+= frames_written
* 2;
146 snd_pcm_sframes_t count
;
147 const int16_t *sp
= s
;
149 for(count
= 0; count
< frames_written
* 2; ++count
) {
150 dump_buffer
[dump_index
++] = (int16_t)ntohs(*sp
++);
151 dump_index
%= dump_size
;
158 /** @brief Play the relevant part of a packet
159 * @param p Packet to play
160 * @return 0 on success, -1 on non-fatal error
162 static int playrtp_alsa_play(const struct packet
*p
) {
163 return playrtp_alsa_writei(p
->samples_raw
+ next_timestamp
- p
->timestamp
,
164 (p
->timestamp
+ p
->nsamples
) - next_timestamp
);
167 /** @brief Play some silence
168 * @param p Next packet or NULL
169 * @return 0 on success, -1 on non-fatal error
171 static int playrtp_alsa_infill(const struct packet
*p
) {
172 static const uint16_t zeros
[INFILL_SAMPLES
];
173 size_t samples_available
= INFILL_SAMPLES
;
175 if(p
&& samples_available
> p
->timestamp
- next_timestamp
)
176 samples_available
= p
->timestamp
- next_timestamp
;
177 return playrtp_alsa_writei(zeros
, samples_available
);
180 static void playrtp_alsa_enable(void){
183 if(!playrtp_alsa_prepared
) {
184 if((err
= snd_pcm_prepare(pcm
)))
185 fatal(0, "error calling snd_pcm_prepare: %d", err
);
186 playrtp_alsa_prepared
= 1;
190 /** @brief Reset ALSA state after we lost synchronization */
191 static void playrtp_alsa_disable(int hard_reset
) {
194 if((err
= snd_pcm_nonblock(pcm
, 0)))
195 fatal(0, "error calling snd_pcm_nonblock: %d", err
);
197 if((err
= snd_pcm_drop(pcm
)))
198 fatal(0, "error calling snd_pcm_drop: %d", err
);
200 if((err
= snd_pcm_drain(pcm
)))
201 fatal(0, "error calling snd_pcm_drain: %d", err
);
202 if((err
= snd_pcm_nonblock(pcm
, 1)))
203 fatal(0, "error calling snd_pcm_nonblock: %d", err
);
204 playrtp_alsa_prepared
= 0;
207 void playrtp_alsa(void) {
209 const struct packet
*p
;
212 pthread_mutex_lock(&lock
);
214 /* Wait for the buffer to fill up a bit */
215 playrtp_fill_buffer();
216 playrtp_alsa_enable();
219 /* Keep playing until the buffer empties out, or ALSA tells us to get
221 while((nsamples
>= minbuffer
223 && contains(pheap_first(&packets
), next_timestamp
)))
225 /* Wait for ALSA to ask us for more data */
226 pthread_mutex_unlock(&lock
);
228 pthread_mutex_lock(&lock
);
229 /* ALSA is ready for more data, find something to play */
230 p
= playrtp_next_packet();
231 /* Play it or play some silence */
232 if(contains(p
, next_timestamp
))
233 escape
= playrtp_alsa_play(p
);
235 escape
= playrtp_alsa_infill(p
);
238 /* We stop playing for a bit until the buffer re-fills */
239 pthread_mutex_unlock(&lock
);
240 playrtp_alsa_disable(escape
);
241 pthread_mutex_lock(&lock
);