chiark / gitweb /
OSS backend for playrtp
[disorder] / clients / playrtp-oss.c
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) */
47 static int playrtp_oss_fd = -1;
48
49 /** @brief Open and configure the OSS audio device */
50 static 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 */
76 static 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  */
93 static 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  */
106 static 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  */
129 static 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  */
138 static 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 */
148 void 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 /*
187 Local Variables:
188 c-basic-offset:2
189 comment-column:40
190 fill-column:79
191 indent-tabs-mode:nil
192 End:
193 */