OSS backend for playrtp
[disorder] / clients / playrtp-oss.c
CommitLineData
cfa7dda1 1/*
2 * This file is part of DisOrder.
3 * Copyright (C) 2007 Richard Kettlewell
4 *
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.
9 *
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.
14 *
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
18 * USA
19 */
20/** @file clients/playrtp-oss.c
21 * @brief RTP player - OSS support
22 */
23
24#include <config.h>
25
26#if HAVE_SYS_SOUNDCARD_H
27#include "types.h"
28
29#include <poll.h>
30#include <sys/ioctl.h>
31#include <sys/soundcard.h>
32#include <assert.h>
33#include <pthread.h>
34#include <string.h>
35#include <fcntl.h>
36#include <unistd.h>
37#include <errno.h>
38
39#include "mem.h"
40#include "log.h"
41#include "vector.h"
42#include "heap.h"
43#include "syscalls.h"
44#include "playrtp.h"
45
46/** @brief /dev/dsp (or whatever) */
47static int playrtp_oss_fd = -1;
48
49/** @brief Open and configure the OSS audio device */
50static void playrtp_oss_enable(void) {
51 if(playrtp_oss_fd == -1) {
52 int rate = 44100, stereo = 1, format = AFMT_S16_BE;
53 if(!device) {
54 if(access("/dev/dsp", W_OK) == 0)
55 device = "/dev/dsp";
56 else if(access("/dev/audio", W_OK) == 0)
57 device = "/dev/audio";
58 else
59 fatal(0, "cannot determine default audio device");
60 }
61 if((playrtp_oss_fd = open(device, O_WRONLY)) < 0)
62 fatal(errno, "error opening %s", device);
63 if(ioctl(playrtp_oss_fd, SNDCTL_DSP_SETFMT, &format) < 0)
64 fatal(errno, "ioctl SNDCTL_DSP_SETFMT");
65 if(ioctl(playrtp_oss_fd, SNDCTL_DSP_STEREO, &stereo) < 0)
66 fatal(errno, "ioctl SNDCTL_DSP_STEREO");
67 if(ioctl(playrtp_oss_fd, SNDCTL_DSP_SPEED, &rate) < 0)
68 fatal(errno, "ioctl SNDCTL_DSP_SPEED");
69 if(rate != 44100)
70 error(0, "asking for 44100Hz, got %dHz", rate);
71 nonblock(playrtp_oss_fd);
72 }
73}
74
75/** @brief Wait until the audio device can accept more data */
76static void playrtp_oss_wait(void) {
77 struct pollfd fds[1];
78 int n;
79
80 do {
81 fds[0].fd = playrtp_oss_fd;
82 fds[0].events = POLLOUT;
83 while((n = poll(fds, 1, -1)) < 0 && errno == EINTR)
84 ;
85 if(n < 0)
86 fatal(errno, "calling poll");
87 } while(!(fds[0].revents & (POLLOUT|POLLERR)));
88}
89
90/** @brief Close the OSS output device
91 * @param hard If nonzero, drop pending data
92 */
93static void playrtp_oss_disable(int hard) {
94 if(hard)
95 if(ioctl(playrtp_oss_fd, SNDCTL_DSP_RESET, 0) < 0)
96 error(errno, "ioctl SNDCTL_DSP_RESET");
97 xclose(playrtp_oss_fd);
98 playrtp_oss_fd = -1;
99}
100
101/** @brief Write samples to OSS output device
102 * @param data Pointer to sample data
103 * @param nsamples Number of samples
104 * @return 0 on success, non-0 on error
105 */
106static int playrtp_oss_write(const void *data, size_t samples) {
107 const ssize_t nbyteswritten = write(playrtp_oss_fd, data,
108 samples * sizeof (int16_t));
109
110 if(nbyteswritten < 0) {
111 switch(errno) {
112 case EAGAIN:
113 case EINTR:
114 return 0;
115 default:
116 error(errno, "error writing to %s", device);
117 return -1;
118 }
119 } else {
120 next_timestamp += nbyteswritten / 2;
121 return 0;
122 }
123}
124
125/** @brief Play some data from packet @p p
126 *
127 * @p p is assumed to contain @ref next_timestamp.
128 */
129static int playrtp_oss_play(const struct packet *p) {
130 return playrtp_oss_write(p->samples_raw + next_timestamp - p->timestamp,
131 (p->timestamp + p->nsamples) - next_timestamp);
132}
133
134/** @brief Play some silence before packet @p p
135 *
136 * @p p is assumed to be entirely before @ref next_timestamp.
137 */
138static int playrtp_oss_infill(const struct packet *p) {
139 static const uint16_t zeros[INFILL_SAMPLES];
140 size_t samples_available = INFILL_SAMPLES;
141
142 if(p && samples_available > p->timestamp - next_timestamp)
143 samples_available = p->timestamp - next_timestamp;
144 return playrtp_oss_write(zeros, samples_available);
145}
146
147/** @brief OSS backend for playrtp */
148void playrtp_oss(void) {
149 int escape;
150 const struct packet *p;
151
152 pthread_mutex_lock(&lock);
153 for(;;) {
154 /* Wait for the buffer to fill up a bit */
155 playrtp_fill_buffer();
156 playrtp_oss_enable();
157 escape = 0;
158 info("Playing...");
159 /* Keep playing until the buffer empties out, we get an error */
160 while((nsamples >= minbuffer
161 || (nsamples > 0
162 && contains(pheap_first(&packets), next_timestamp)))
163 && !escape) {
164 /* Wait until we can play more */
165 pthread_mutex_unlock(&lock);
166 playrtp_oss_wait();
167 pthread_mutex_lock(&lock);
168 /* Device is ready for more data, find something to play */
169 p = playrtp_next_packet();
170 /* Play it or play some silence */
171 if(contains(p, next_timestamp))
172 escape = playrtp_oss_play(p);
173 else
174 escape = playrtp_oss_infill(p);
175 }
176 active = 0;
177 /* We stop playing for a bit until the buffer re-fills */
178 pthread_mutex_unlock(&lock);
179 playrtp_oss_disable(escape);
180 pthread_mutex_lock(&lock);
181 }
182}
183
184#endif
185
186/*
187Local Variables:
188c-basic-offset:2
189comment-column:40
190fill-column:79
191indent-tabs-mode:nil
192End:
193*/