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