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>
40 /** @brief PCM handle */
41 static snd_pcm_t
*pcm
;
43 /** @brief True when @ref pcm is up and running */
44 static int playrtp_alsa_prepared
= 1;
46 static void playrtp_alsa_init(void) {
47 snd_pcm_hw_params_t
*hwparams
;
48 snd_pcm_sw_params_t
*swparams
;
49 /* Only support one format for now */
50 const int sample_format
= SND_PCM_FORMAT_S16_BE
;
51 unsigned rate
= 44100;
52 const int channels
= 2;
53 const int samplesize
= channels
* sizeof(uint16_t);
54 snd_pcm_uframes_t pcm_bufsize
= MAXSAMPLES
* samplesize
* 3;
55 /* If we can write more than this many samples we'll get a wakeup */
56 const int avail_min
= 256;
60 if((err
= snd_pcm_open(&pcm
,
61 device ? device
: "default",
62 SND_PCM_STREAM_PLAYBACK
,
64 fatal(0, "error from snd_pcm_open: %d", err
);
65 /* Set up 'hardware' parameters */
66 snd_pcm_hw_params_alloca(&hwparams
);
67 if((err
= snd_pcm_hw_params_any(pcm
, hwparams
)) < 0)
68 fatal(0, "error from snd_pcm_hw_params_any: %d", err
);
69 if((err
= snd_pcm_hw_params_set_access(pcm
, hwparams
,
70 SND_PCM_ACCESS_RW_INTERLEAVED
)) < 0)
71 fatal(0, "error from snd_pcm_hw_params_set_access: %d", err
);
72 if((err
= snd_pcm_hw_params_set_format(pcm
, hwparams
,
75 fatal(0, "error from snd_pcm_hw_params_set_format (%d): %d",
77 if((err
= snd_pcm_hw_params_set_rate_near(pcm
, hwparams
, &rate
, 0)) < 0)
78 fatal(0, "error from snd_pcm_hw_params_set_rate (%d): %d",
80 if((err
= snd_pcm_hw_params_set_channels(pcm
, hwparams
,
82 fatal(0, "error from snd_pcm_hw_params_set_channels (%d): %d",
84 if((err
= snd_pcm_hw_params_set_buffer_size_near(pcm
, hwparams
,
86 fatal(0, "error from snd_pcm_hw_params_set_buffer_size (%d): %d",
87 MAXSAMPLES
* samplesize
* 3, err
);
88 if((err
= snd_pcm_hw_params(pcm
, hwparams
)) < 0)
89 fatal(0, "error calling snd_pcm_hw_params: %d", err
);
90 /* Set up 'software' parameters */
91 snd_pcm_sw_params_alloca(&swparams
);
92 if((err
= snd_pcm_sw_params_current(pcm
, swparams
)) < 0)
93 fatal(0, "error calling snd_pcm_sw_params_current: %d", err
);
94 if((err
= snd_pcm_sw_params_set_avail_min(pcm
, swparams
, avail_min
)) < 0)
95 fatal(0, "error calling snd_pcm_sw_params_set_avail_min %d: %d",
97 if((err
= snd_pcm_sw_params(pcm
, swparams
)) < 0)
98 fatal(0, "error calling snd_pcm_sw_params: %d", err
);
101 /** @brief Wait until ALSA wants some audio */
102 static void wait_alsa(void) {
103 struct pollfd fds
[64];
105 unsigned short events
;
109 if((nfds
= snd_pcm_poll_descriptors(pcm
,
110 fds
, sizeof fds
/ sizeof *fds
)) < 0)
111 fatal(0, "error calling snd_pcm_poll_descriptors: %d", nfds
);
112 } while(poll(fds
, nfds
, -1) < 0 && errno
== EINTR
);
113 if((err
= snd_pcm_poll_descriptors_revents(pcm
, fds
, nfds
, &events
)))
114 fatal(0, "error calling snd_pcm_poll_descriptors_revents: %d", err
);
120 /** @brief Play some sound via ALSA
121 * @param s Pointer to sample data
122 * @param n Number of samples
123 * @return 0 on success, -1 on non-fatal error
125 static int playrtp_alsa_writei(const void *s
, size_t n
) {
127 const snd_pcm_sframes_t frames_written
= snd_pcm_writei(pcm
, s
, n
/ 2);
128 if(frames_written
< 0) {
129 /* Something went wrong */
130 switch(frames_written
) {
134 error(0, "error calling snd_pcm_writei: %ld",
135 (long)frames_written
);
138 fatal(0, "error calling snd_pcm_writei: %ld",
139 (long)frames_written
);
143 next_timestamp
+= frames_written
* 2;
148 /** @brief Play the relevant part of a packet
149 * @param p Packet to play
150 * @return 0 on success, -1 on non-fatal error
152 static int playrtp_alsa_play(const struct packet
*p
) {
153 return playrtp_alsa_writei(p
->samples_raw
+ next_timestamp
- p
->timestamp
,
154 (p
->timestamp
+ p
->nsamples
) - next_timestamp
);
157 /** @brief Play some silence
158 * @param p Next packet or NULL
159 * @return 0 on success, -1 on non-fatal error
161 static int playrtp_alsa_infill(const struct packet
*p
) {
162 static const uint16_t zeros
[INFILL_SAMPLES
];
163 size_t samples_available
= INFILL_SAMPLES
;
165 if(p
&& samples_available
> p
->timestamp
- next_timestamp
)
166 samples_available
= p
->timestamp
- next_timestamp
;
167 return playrtp_alsa_writei(zeros
, samples_available
);
170 static void playrtp_alsa_enable(void){
173 if(!playrtp_alsa_prepared
) {
174 if((err
= snd_pcm_prepare(pcm
)))
175 fatal(0, "error calling snd_pcm_prepare: %d", err
);
176 playrtp_alsa_prepared
= 1;
180 /** @brief Reset ALSA state after we lost synchronization */
181 static void playrtp_alsa_disable(int hard_reset
) {
184 if((err
= snd_pcm_nonblock(pcm
, 0)))
185 fatal(0, "error calling snd_pcm_nonblock: %d", err
);
187 if((err
= snd_pcm_drop(pcm
)))
188 fatal(0, "error calling snd_pcm_drop: %d", err
);
190 if((err
= snd_pcm_drain(pcm
)))
191 fatal(0, "error calling snd_pcm_drain: %d", err
);
192 if((err
= snd_pcm_nonblock(pcm
, 1)))
193 fatal(0, "error calling snd_pcm_nonblock: %d", err
);
194 playrtp_alsa_prepared
= 0;
197 void playrtp_alsa(void) {
199 const struct packet
*p
;
202 pthread_mutex_lock(&lock
);
204 /* Wait for the buffer to fill up a bit */
205 playrtp_fill_buffer();
206 playrtp_alsa_enable();
209 /* Keep playing until the buffer empties out, or ALSA tells us to get
211 while((nsamples
>= minbuffer
213 && contains(pheap_first(&packets
), next_timestamp
)))
215 /* Wait for ALSA to ask us for more data */
216 pthread_mutex_unlock(&lock
);
218 pthread_mutex_lock(&lock
);
219 /* ALSA is ready for more data, find something to play */
220 p
= playrtp_next_packet();
221 /* Play it or play some silence */
222 if(contains(p
, next_timestamp
))
223 escape
= playrtp_alsa_play(p
);
225 escape
= playrtp_alsa_infill(p
);
228 /* We stop playing for a bit until the buffer re-fills */
229 pthread_mutex_unlock(&lock
);
230 playrtp_alsa_disable(escape
);
231 pthread_mutex_lock(&lock
);