chiark / gitweb /
dump silence too
[disorder] / clients / playrtp-oss.c
... / ...
CommitLineData
1/*
2 * This file is part of DisOrder.
3 * Copyright (C) 2007 Richard Kettlewell
4 * Portions copyright (C) 2007 Ross Younger
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 * USA
20 */
21/** @file clients/playrtp-oss.c
22 * @brief RTP player - OSS and empeg support
23 */
24
25#include <config.h>
26
27#if HAVE_SYS_SOUNDCARD_H || EMPEG_HOST
28#include "types.h"
29
30#include <poll.h>
31#include <sys/ioctl.h>
32#if !EMPEG_HOST
33#include <sys/soundcard.h>
34#endif
35#include <assert.h>
36#include <pthread.h>
37#include <string.h>
38#include <fcntl.h>
39#include <unistd.h>
40#include <errno.h>
41
42#include "mem.h"
43#include "log.h"
44#include "vector.h"
45#include "heap.h"
46#include "syscalls.h"
47#include "playrtp.h"
48
49/** @brief /dev/dsp (or whatever) */
50static int playrtp_oss_fd = -1;
51
52/** @brief Audio buffer */
53static char *playrtp_oss_buffer;
54
55/** @brief Size of @ref playrtp_oss_buffer in bytes */
56static int playrtp_oss_bufsize;
57
58/** @brief Number of bytes used in @ref playrtp_oss_buffer */
59static int playrtp_oss_bufused;
60
61/** @brief Open and configure the OSS audio device */
62static void playrtp_oss_enable(void) {
63 if(playrtp_oss_fd == -1) {
64#if EMPEG_HOST
65 /* empeg audio driver only knows /dev/audio, only supports the equivalent
66 * of AFMT_S16_NE, has a fixed buffer size, and does not support the
67 * SNDCTL_ ioctls. */
68 if(!device)
69 device = "/dev/audio";
70 if((playrtp_oss_fd = open(device, O_WRONLY)) < 0)
71 fatal(errno, "error opening %s", device);
72 playrtp_oss_bufsize = 4608;
73#else
74 int rate = 44100, stereo = 1, format = AFMT_S16_BE;
75 if(!device) {
76 if(access("/dev/dsp", W_OK) == 0)
77 device = "/dev/dsp";
78 else if(access("/dev/audio", W_OK) == 0)
79 device = "/dev/audio";
80 else
81 fatal(0, "cannot determine default audio device");
82 }
83 if((playrtp_oss_fd = open(device, O_WRONLY)) < 0)
84 fatal(errno, "error opening %s", device);
85 if(ioctl(playrtp_oss_fd, SNDCTL_DSP_SETFMT, &format) < 0)
86 fatal(errno, "ioctl SNDCTL_DSP_SETFMT");
87 if(ioctl(playrtp_oss_fd, SNDCTL_DSP_STEREO, &stereo) < 0)
88 fatal(errno, "ioctl SNDCTL_DSP_STEREO");
89 if(ioctl(playrtp_oss_fd, SNDCTL_DSP_SPEED, &rate) < 0)
90 fatal(errno, "ioctl SNDCTL_DSP_SPEED");
91 if(rate != 44100)
92 error(0, "asking for 44100Hz, got %dHz", rate);
93 if(ioctl(playrtp_oss_fd, SNDCTL_DSP_GETBLKSIZE, &playrtp_oss_bufsize) < 0)
94 fatal(errno, "ioctl SNDCTL_DSP_GETBLKSIZE");
95 info("OSS buffer size %d", playrtp_oss_bufsize);
96#endif
97 playrtp_oss_buffer = xmalloc(playrtp_oss_bufsize);
98 playrtp_oss_bufused = 0;
99 nonblock(playrtp_oss_fd);
100 }
101}
102
103/** @brief Flush the OSS output buffer
104 * @return 0 on success, non-0 on error
105 */
106static int playrtp_oss_flush(void) {
107 int nbyteswritten;
108
109 if(!playrtp_oss_bufused)
110 return 0; /* nothing to do */
111 /* 0 out the unused portion of the buffer */
112 memset(playrtp_oss_buffer + playrtp_oss_bufused, 0,
113 playrtp_oss_bufsize - playrtp_oss_bufused);
114#if EMPEG_HOST
115 /* empeg audio driver insists on native-endian samples */
116 {
117 uint16_t *ptr,
118 *const limit = (uint16_t *)(playrtp_oss_buffer + playrtp_oss_bufused);
119
120 for(ptr = (uint16_t *)playrtp_oss_buffer; ptr < limit; ++ptr)
121 *ptr = ntohs(*ptr);
122 }
123#endif
124 for(;;) {
125 nbyteswritten = write(playrtp_oss_fd,
126 playrtp_oss_buffer, playrtp_oss_bufsize);
127 if(nbyteswritten < 0) {
128 switch(errno) {
129 case EINTR:
130 break; /* try again */
131 case EAGAIN:
132 return 0; /* try later */
133 default:
134 error(errno, "error writing to %s", device);
135 return -1;
136 }
137 } else {
138 if(nbyteswritten < playrtp_oss_bufsize)
139 error(0, "%s: short write (%d/%d)",
140 device, nbyteswritten, playrtp_oss_bufsize);
141 playrtp_oss_bufused = 0;
142 return 0;
143 }
144 }
145}
146
147/** @brief Wait until the audio device can accept more data */
148static void playrtp_oss_wait(void) {
149 struct pollfd fds[1];
150 int n;
151
152 do {
153 fds[0].fd = playrtp_oss_fd;
154 fds[0].events = POLLOUT;
155 while((n = poll(fds, 1, -1)) < 0 && errno == EINTR)
156 ;
157 if(n < 0)
158 fatal(errno, "calling poll");
159 } while(!(fds[0].revents & (POLLOUT|POLLERR)));
160}
161
162/** @brief Close the OSS output device
163 * @param hard If nonzero, drop pending data
164 */
165static void playrtp_oss_disable(int hard) {
166 if(hard) {
167#if !EMPEG_HOST
168 /* No SNDCTL_DSP_ ioctls on empeg */
169 if(ioctl(playrtp_oss_fd, SNDCTL_DSP_RESET, 0) < 0)
170 error(errno, "ioctl SNDCTL_DSP_RESET");
171#endif
172 } else
173 playrtp_oss_flush();
174 xclose(playrtp_oss_fd);
175 playrtp_oss_fd = -1;
176 free(playrtp_oss_buffer);
177 playrtp_oss_buffer = 0;
178}
179
180/** @brief Write samples to OSS output device
181 * @param data Pointer to sample data
182 * @param nsamples Number of samples
183 * @return 0 on success, non-0 on error
184 */
185static int playrtp_oss_write(const char *data, size_t samples) {
186 long bytes = samples * sizeof(int16_t);
187 while(bytes > 0) {
188 int n = playrtp_oss_bufsize - playrtp_oss_bufused;
189
190 if(n > bytes)
191 n = bytes;
192 memcpy(playrtp_oss_buffer + playrtp_oss_bufused, data, n);
193 bytes -= n;
194 data += n;
195 playrtp_oss_bufused += n;
196 if(playrtp_oss_bufused == playrtp_oss_bufsize)
197 if(playrtp_oss_flush())
198 return -1;
199 }
200 next_timestamp += samples;
201 return 0;
202}
203
204/** @brief Play some data from packet @p p
205 *
206 * @p p is assumed to contain @ref next_timestamp.
207 */
208static int playrtp_oss_play(const struct packet *p) {
209 return playrtp_oss_write
210 ((const char *)(p->samples_raw + next_timestamp - p->timestamp),
211 (p->timestamp + p->nsamples) - next_timestamp);
212}
213
214/** @brief Play some silence before packet @p p
215 *
216 * @p p is assumed to be entirely before @ref next_timestamp.
217 */
218static int playrtp_oss_infill(const struct packet *p) {
219 static const char zeros[INFILL_SAMPLES * sizeof(int16_t)];
220 size_t samples_available = INFILL_SAMPLES;
221
222 if(p && samples_available > p->timestamp - next_timestamp)
223 samples_available = p->timestamp - next_timestamp;
224 return playrtp_oss_write(zeros, samples_available);
225}
226
227/** @brief OSS backend for playrtp */
228void playrtp_oss(void) {
229 int escape;
230 const struct packet *p;
231
232 pthread_mutex_lock(&lock);
233 for(;;) {
234 /* Wait for the buffer to fill up a bit */
235 playrtp_fill_buffer();
236 playrtp_oss_enable();
237 escape = 0;
238 info("Playing...");
239 /* Keep playing until the buffer empties out, we get an error */
240 while((nsamples >= minbuffer
241 || (nsamples > 0
242 && contains(pheap_first(&packets), next_timestamp)))
243 && !escape) {
244 /* Wait until we can play more */
245 pthread_mutex_unlock(&lock);
246 playrtp_oss_wait();
247 pthread_mutex_lock(&lock);
248 /* Device is ready for more data, find something to play */
249 p = playrtp_next_packet();
250 /* Play it or play some silence */
251 if(contains(p, next_timestamp))
252 escape = playrtp_oss_play(p);
253 else
254 escape = playrtp_oss_infill(p);
255 }
256 active = 0;
257 /* We stop playing for a bit until the buffer re-fills */
258 pthread_mutex_unlock(&lock);
259 playrtp_oss_disable(escape);
260 pthread_mutex_lock(&lock);
261 }
262}
263
264#endif
265
266/*
267Local Variables:
268c-basic-offset:2
269comment-column:40
270fill-column:79
271indent-tabs-mode:nil
272End:
273*/