1 /*
2 *
3 * ao_arts.c
4 *
5 * Copyright (C) Rik Hemsley (rikkus) <rik@kde.org> 2000
6 * Modifications Copyright (C) 2010 Monty <monty@xiph.org>
7 *
8 * This file is part of libao, a cross-platform library. See
9 * README for a history of this source code.
10 *
11 * libao is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2, or (at your option)
14 * any later version.
15 *
16 * libao is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with GNU Make; see the file COPYING. If not, write to
23 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
24 *
25 ********************************************************************
26
27 last mod: $Id: ao_arts.c 17718 2010-12-06 20:09:29Z xiphmont $
28
29 ********************************************************************/
30
31 #include <stdio.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <pthread.h>
35
36 #include <glib.h>
37 #include <artsc.h>
38 #include <ao/ao.h>
39 #include <ao/plugin.h>
40
41 /* we must serialize all aRtsc library access as virtually every
42 operation accesses global state */
43 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
44 static int server_open_count = 0;
45
46 static char *ao_arts_options[] = {"matrix","verbose","quiet","debug","multi"};
47 static ao_info ao_arts_info =
48 {
49 AO_TYPE_LIVE,
50 "aRts output",
51 "arts",
52 "Monty <monty@xiph.org>",
53 "Outputs to the aRts soundserver.",
54 AO_FMT_NATIVE,
55 #ifdef HAVE_ARTS_SUSPENDED
56 45,
57 #else
58 15,
59 #endif
60 ao_arts_options,
61 sizeof(ao_arts_options)/sizeof(*ao_arts_options)
62 };
63
64 typedef struct ao_arts_internal
65 {
66 arts_stream_t stream;
67 int allow_multi;
68 int buffersize;
69 } ao_arts_internal;
70
71
ao_plugin_test()72 int ao_plugin_test()
73 {
74 pthread_mutex_lock(&mutex);
75
76 if (server_open_count || arts_init() == 0) {
77 server_open_count++;
78
79 #ifdef HAVE_ARTS_SUSPENDED
80 if (arts_suspended() == 1) {
81 server_open_count--;
82 if(!server_open_count)arts_free();
83 pthread_mutex_unlock(&mutex);
84 return 0;
85 }
86 #endif
87 server_open_count--;
88 if(!server_open_count)arts_free();
89 arts_free();
90 pthread_mutex_unlock(&mutex);
91 return 1;
92 }
93 pthread_mutex_unlock(&mutex);
94 return 0;
95 }
96
ao_plugin_driver_info(void)97 ao_info *ao_plugin_driver_info(void)
98 {
99 /* this is a dirty but necessary trick. aRts's C library for
100 clients calls g_thread_init() internally in arts_init() whether
101 your app uses glib or not. This call sets several
102 thread-specific keys and stashes glib static data state in the
103 calling thread. Later, ao_close() calls arts_free(), glib is
104 dlclose()d, but the keys aren't deleted and this will cause a
105 segfault when the thread that originally called arts_init() exits
106 and pthreads tries to clean up. In addition, g_thread_init() must
107 be called outside of mutextes, and access to arts_init() below is
108 and must be locked.
109
110 So we tackle this problem in two ways; one, call g_thread_init()
111 here, which will be during ao_initialize(). It is documented
112 that ao_initialize() must be called in the app's main
113 thread. Second, be sure to link with glib-2.0, which means that
114 the glib static context is never unloaded by aRts (this alone is
115 currently enough in practice to avoid problems, but that's partly
116 by accident. The g_thread_init() here avoids it randomly breaking
117 again in the future by following documentation exactly). */
118
119 if (!g_thread_supported ())
120 g_thread_init(0);
121
122 return &ao_arts_info;
123 }
124
125
ao_plugin_device_init(ao_device * device)126 int ao_plugin_device_init(ao_device *device)
127 {
128 ao_arts_internal *internal;
129
130 internal = (ao_arts_internal *) calloc(1,sizeof(ao_arts_internal));
131
132 if (internal == NULL)
133 return 0; /* Could not initialize device memory */
134
135 device->internal = internal;
136 device->output_matrix_order = AO_OUTPUT_MATRIX_FIXED;
137 device->output_matrix=strdup("L,R");
138
139 return 1; /* Memory alloc successful */
140 }
141
142
ao_plugin_set_option(ao_device * device,const char * key,const char * value)143 int ao_plugin_set_option(ao_device *device, const char *key, const char *value)
144 {
145 ao_arts_internal *internal = (ao_arts_internal *) device->internal;
146
147 if (!strcmp(key, "multi")) {
148 if(!strcmp(value,"yes") || !strcmp(value,"y") ||
149 !strcmp(value,"true") || !strcmp(value,"t") ||
150 !strcmp(value,"1"))
151 {
152 internal->allow_multi = 1;
153 return 1;
154 }
155 if(!strcmp(value,"no") || !strcmp(value,"n") ||
156 !strcmp(value,"false") || !strcmp(value,"f") ||
157 !strcmp(value,"0"))
158 {
159 internal->allow_multi = 0;
160 return 1;
161 }
162 return 0;
163 }
164 return 1;
165 }
166
ao_plugin_open(ao_device * device,ao_sample_format * format)167 int ao_plugin_open(ao_device *device, ao_sample_format *format)
168 {
169 ao_arts_internal *internal = (ao_arts_internal *) device->internal;
170 int errorcode=0;
171
172 if(device->output_channels<1 || device->output_channels>2){
173 /* the docs aren't kidding here--- feed it more than 2
174 channels and the server simply stops answering; the
175 connection freezes. */
176 aerror("Cannot handle more than 2 channels\n");
177 return 0;
178 }
179
180 pthread_mutex_lock(&mutex);
181 if(!server_open_count)
182 errorcode = arts_init();
183 else{
184 if(!internal->allow_multi){
185 /* multiple-playback access disallowed; it's disallowed by
186 default as it tends to crash the aRts server. */
187 adebug("Multiple-open access disallowed and playback already in progress.\n");
188 pthread_mutex_unlock(&mutex);
189 return 0;
190 }
191 }
192
193 if (0 != errorcode){
194 pthread_mutex_unlock(&mutex);
195 aerror("Could not connect to server => %s.\n",arts_error_text(errorcode));
196 return 0; /* Could not connect to server */
197 }
198
199 device->driver_byte_format = AO_FMT_NATIVE;
200 internal->stream = arts_play_stream(format->rate,
201 format->bits,
202 device->output_channels,
203 "libao stream");
204
205 if(!internal->stream){
206 if(!server_open_count)arts_free();
207 pthread_mutex_unlock(&mutex);
208
209 aerror("Could not open audio stream.\n");
210 return 0;
211 }
212
213 if(arts_stream_set(internal->stream, ARTS_P_BLOCKING, 0)){
214 arts_close_stream(internal->stream);
215 internal->stream=NULL;
216 if(!server_open_count)arts_free();
217 pthread_mutex_unlock(&mutex);
218
219 aerror("Could not set audio stream to nonblocking.\n");
220 return 0;
221 }
222
223 if((internal->buffersize = arts_stream_get(internal->stream, ARTS_P_BUFFER_SIZE))<=0){
224 arts_close_stream(internal->stream);
225 internal->stream=NULL;
226 if(!server_open_count)arts_free();
227 pthread_mutex_unlock(&mutex);
228
229 aerror("Could not get audio buffer size.\n");
230 return 0;
231 }
232
233 server_open_count++;
234 pthread_mutex_unlock(&mutex);
235
236 return 1;
237 }
238
239
ao_plugin_play(ao_device * device,const char * output_samples,uint_32 num_bytes)240 int ao_plugin_play(ao_device *device, const char *output_samples,
241 uint_32 num_bytes)
242 {
243 ao_arts_internal *internal = (ao_arts_internal *) device->internal;
244 int spindetect=0;
245 int i;
246
247 pthread_mutex_lock(&mutex);
248
249 /* the while loop below is another dirty but servicable hack needed
250 for two reasons:
251
252 1) for multiple-stream playback, there is no way to block on
253 more than one stream object at a time. One can neither
254 select/poll, nor can we block on multiple writes at a time as
255 access to arts_write must be locked globally. So we run in
256 nonblocking mode and write to the server based on audio timing.
257
258 2) Although aRts allegedly delivers errors on write failure, I've
259 never observed it actually do so in practice. Most of the time
260 when something goes wrong, it returns a short count or zero, but
261 there are also cases where the write simply blocks forever
262 because the server logged an error and stopped answering without
263 informing the client or dropping the connection. Again, we have
264 to run in nonblocking moda and look for an output pattern that
265 indicates the server disappeared out from under us (no successful
266 writes over a period that should certainly have starved
267 playback) */
268
269 while(1){
270 int accwrote=0;
271
272 /* why the multiple rapid-fire writes below?
273
274 aRts in nonblocking mode does not service internal buffering
275 state outside of the write call. Further, the internal buffer
276 state appears to be pipelined; although the server may be
277 waiting or even starved for data, a non blocking write call
278 will often return immediately without actually writing
279 anything, regardless of internal buffer fullness. Several more
280 calls (all returning 0, due to the full internal buffer) will
281 suddenly cause the internal state to actually flush data to the
282 server. Thus the multiple writes in sequence are a way of
283 having the aRts internal state step through the sequence
284 necessary to actually submit data to the server.
285 */
286
287 for(i=0;i<5;i++){
288 int wrote = arts_write(internal->stream, output_samples, num_bytes);
289 if(wrote < 0){
290 /* although it's vanishingly unlikely that aRtsc will actually
291 bother reporting any errors, we might as well be ready for
292 one. */
293 pthread_mutex_unlock(&mutex);
294 aerror("Write error\n");
295 return 0;
296 }
297 accwrote+=wrote;
298 num_bytes -= wrote;
299 output_samples += wrote;
300 }
301
302 if(accwrote)
303 spindetect=0;
304 else
305 spindetect++;
306
307 if(spindetect==100){
308 pthread_mutex_unlock(&mutex);
309 aerror("Write thread spinning; has the aRts server crashed?\n");
310 return 0;
311 }
312
313 if(num_bytes>0){
314 long wait = internal->buffersize*1000/(device->output_channels*device->bytewidth*device->rate);
315 pthread_mutex_unlock(&mutex);
316 wait = (wait/8)*1000;
317 if(wait<1)wait=1;
318 if(wait>500000)wait=500000;
319 usleep(wait);
320 pthread_mutex_lock(&mutex);
321 }else{
322 pthread_mutex_unlock(&mutex);
323 break;
324 }
325 }
326
327 return 1;
328 }
329
330
ao_plugin_close(ao_device * device)331 int ao_plugin_close(ao_device *device)
332 {
333 ao_arts_internal *internal = (ao_arts_internal *) device->internal;
334 pthread_mutex_lock(&mutex);
335 if(internal->stream)
336 arts_close_stream(internal->stream);
337 internal->stream = NULL;
338
339 server_open_count--;
340 if(!server_open_count)arts_free();
341 pthread_mutex_unlock(&mutex);
342
343 return 1;
344 }
345
346
ao_plugin_device_clear(ao_device * device)347 void ao_plugin_device_clear(ao_device *device)
348 {
349 ao_arts_internal *internal = (ao_arts_internal *) device->internal;
350
351 if(internal)
352 free(internal);
353 device->internal=NULL;
354 }
355