loader/win32.c: misc cleanup
[mplayer/glamo.git] / libao2 / ao_nas.c
blobfb49c5e60e8bb533d1c7a5391010bb86cc18c3c8
1 /*
2 * NAS audio output driver
4 * copyright (c) 2001 Tobias Diedrich <ranma@gmx.at>
6 * Based on the libaudiooss parts rewritten by me, which were
7 * originally based on the NAS output plugin for XMMS.
9 * XMMS plugin by Willem Monsuwe
10 * adapted for libaudiooss by Jon Trulson
11 * further modified by Erik Inge Bolsø
12 * largely rewritten and used for this ao driver by Tobias Diedrich
14 * This file is part of MPlayer.
16 * MPlayer is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
21 * MPlayer is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
26 * You should have received a copy of the GNU General Public License along
27 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
28 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
32 * Theory of operation:
34 * The NAS consists of two parts, a server daemon and a client.
35 * We setup the server to use a buffer of size bytes_per_second
36 * with a low watermark of buffer_size - NAS_FRAG_SIZE.
37 * Upon starting the flow the server will generate a buffer underrun
38 * event and the event handler will fill the buffer for the first time.
39 * Now the server will generate a lowwater event when the server buffer
40 * falls below the low watermark value. The event handler gets called
41 * again and refills the buffer by the number of bytes requested by the
42 * server (usually a multiple of 4096). To prevent stuttering on
43 * startup (start of playing, seeks, unpausing) the client buffer should
44 * be bigger than the server buffer. (For debugging we also do some
45 * accounting of what we think how much of the server buffer is filled)
48 #include <unistd.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <pthread.h>
53 #include <limits.h>
54 #include <audio/audiolib.h>
56 #include "config.h"
57 #include "mp_msg.h"
59 #include "audio_out.h"
60 #include "audio_out_internal.h"
61 #include "libaf/af_format.h"
63 /* NAS_FRAG_SIZE must be a power-of-two value */
64 #define NAS_FRAG_SIZE 4096
66 static const char * const nas_event_types[] = {
67 "Undefined",
68 "Undefined",
69 "ElementNotify",
70 "GrabNotify",
71 "MonitorNotify",
72 "BucketNotify",
73 "DeviceNotify"
76 static const char * const nas_elementnotify_kinds[] = {
77 "LowWater",
78 "HighWater",
79 "State",
80 "Unknown"
83 static const char * const nas_states[] = {
84 "Stop",
85 "Start",
86 "Pause",
87 "Any"
90 static const char * const nas_reasons[] = {
91 "User",
92 "Underrun",
93 "Overrun",
94 "EOF",
95 "Watermark",
96 "Hardware",
97 "Any"
100 static const char* nas_reason(unsigned int reason)
102 if (reason > 6) reason = 6;
103 return nas_reasons[reason];
106 static const char* nas_elementnotify_kind(unsigned int kind)
108 if (kind > 2) kind = 3;
109 return nas_elementnotify_kinds[kind];
112 static const char* nas_event_type(unsigned int type) {
113 if (type > 6) type = 0;
114 return nas_event_types[type];
117 static const char* nas_state(unsigned int state) {
118 if (state>3) state = 3;
119 return nas_states[state];
122 static const ao_info_t info =
124 "NAS audio output",
125 "nas",
126 "Tobias Diedrich <ranma+mplayer@tdiedrich.de>",
130 struct ao_nas_data {
131 AuServer *aud;
132 AuFlowID flow;
133 AuDeviceID dev;
134 AuFixedPoint gain;
136 unsigned int state;
137 int expect_underrun;
139 char *client_buffer;
140 char *server_buffer;
141 unsigned int client_buffer_size;
142 unsigned int client_buffer_used;
143 unsigned int server_buffer_size;
144 unsigned int server_buffer_used;
145 pthread_mutex_t buffer_mutex;
147 pthread_t event_thread;
148 int stop_thread;
151 static struct ao_nas_data *nas_data;
153 LIBAO_EXTERN(nas)
155 static void nas_print_error(AuServer *aud, const char *prefix, AuStatus as)
157 char s[100];
158 AuGetErrorText(aud, as, s, 100);
159 mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: %s: returned status %d (%s)\n", prefix, as, s);
162 static int nas_readBuffer(struct ao_nas_data *nas_data, unsigned int num)
164 AuStatus as;
166 pthread_mutex_lock(&nas_data->buffer_mutex);
167 mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: nas_readBuffer(): num=%d client=%d/%d server=%d/%d\n",
168 num,
169 nas_data->client_buffer_used, nas_data->client_buffer_size,
170 nas_data->server_buffer_used, nas_data->server_buffer_size);
172 if (nas_data->client_buffer_used == 0) {
173 mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: buffer is empty, nothing read.\n");
174 pthread_mutex_unlock(&nas_data->buffer_mutex);
175 return 0;
177 if (num > nas_data->client_buffer_used)
178 num = nas_data->client_buffer_used;
181 * It is not appropriate to call AuWriteElement() here because the
182 * buffer is locked and delays writing to the network will cause
183 * other threads to block waiting for buffer_mutex. Instead the
184 * data is copied to "server_buffer" and written to the network
185 * outside of the locked section of code.
187 * (Note: Rather than these two buffers, a single circular buffer
188 * could eliminate the memcpy/memmove steps.)
190 /* make sure we don't overflow the buffer */
191 if (num > nas_data->server_buffer_size)
192 num = nas_data->server_buffer_size;
193 memcpy(nas_data->server_buffer, nas_data->client_buffer, num);
195 nas_data->client_buffer_used -= num;
196 nas_data->server_buffer_used += num;
197 memmove(nas_data->client_buffer, nas_data->client_buffer + num, nas_data->client_buffer_used);
198 pthread_mutex_unlock(&nas_data->buffer_mutex);
201 * Now write the new buffer to the network.
203 AuWriteElement(nas_data->aud, nas_data->flow, 0, num, nas_data->server_buffer, AuFalse, &as);
204 if (as != AuSuccess)
205 nas_print_error(nas_data->aud, "nas_readBuffer(): AuWriteElement", as);
207 return num;
210 static int nas_writeBuffer(struct ao_nas_data *nas_data, void *data, unsigned int len)
212 pthread_mutex_lock(&nas_data->buffer_mutex);
213 mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: nas_writeBuffer(): len=%d client=%d/%d server=%d/%d\n",
214 len, nas_data->client_buffer_used, nas_data->client_buffer_size,
215 nas_data->server_buffer_used, nas_data->server_buffer_size);
217 /* make sure we don't overflow the buffer */
218 if (len > nas_data->client_buffer_size - nas_data->client_buffer_used)
219 len = nas_data->client_buffer_size - nas_data->client_buffer_used;
220 memcpy(nas_data->client_buffer + nas_data->client_buffer_used, data, len);
221 nas_data->client_buffer_used += len;
223 pthread_mutex_unlock(&nas_data->buffer_mutex);
225 return len;
228 static int nas_empty_event_queue(struct ao_nas_data *nas_data)
230 AuEvent ev;
231 int result = 0;
233 while (AuScanForTypedEvent(nas_data->aud, AuEventsQueuedAfterFlush,
234 AuTrue, AuEventTypeElementNotify, &ev)) {
235 AuDispatchEvent(nas_data->aud, &ev);
236 result = 1;
238 return result;
241 static void *nas_event_thread_start(void *data)
243 struct ao_nas_data *nas_data = data;
245 do {
246 mp_msg(MSGT_AO, MSGL_DBG2,
247 "ao_nas: event thread heartbeat (state=%s)\n",
248 nas_state(nas_data->state));
249 nas_empty_event_queue(nas_data);
250 usleep(1000);
251 } while (!nas_data->stop_thread);
253 return NULL;
256 static AuBool nas_error_handler(AuServer* aud, AuErrorEvent* ev)
258 char s[100];
259 AuGetErrorText(aud, ev->error_code, s, 100);
260 mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: error [%s]\n"
261 "error_code: %d\n"
262 "request_code: %d\n"
263 "minor_code: %d\n",
265 ev->error_code,
266 ev->request_code,
267 ev->minor_code);
269 return AuTrue;
272 static AuBool nas_event_handler(AuServer *aud, AuEvent *ev, AuEventHandlerRec *hnd)
274 AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
275 struct ao_nas_data *nas_data = hnd->data;
277 mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: event_handler(): type %s kind %s state %s->%s reason %s numbytes %d expect_underrun %d\n",
278 nas_event_type(event->type),
279 nas_elementnotify_kind(event->kind),
280 nas_state(event->prev_state),
281 nas_state(event->cur_state),
282 nas_reason(event->reason),
283 (int)event->num_bytes,
284 nas_data->expect_underrun);
286 if (event->num_bytes > INT_MAX) {
287 mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: num_bytes > 2GB, server buggy?\n");
290 if (event->num_bytes > nas_data->server_buffer_used)
291 event->num_bytes = nas_data->server_buffer_used;
292 nas_data->server_buffer_used -= event->num_bytes;
294 switch (event->reason) {
295 case AuReasonWatermark:
296 nas_readBuffer(nas_data, event->num_bytes);
297 break;
298 case AuReasonUnderrun:
299 // buffer underrun -> refill buffer
300 nas_data->server_buffer_used = 0;
301 if (nas_data->expect_underrun) {
302 nas_data->expect_underrun = 0;
303 } else {
304 static int hint = 1;
305 mp_msg(MSGT_AO, MSGL_WARN,
306 "ao_nas: Buffer underrun.\n");
307 if (hint) {
308 hint = 0;
309 mp_msg(MSGT_AO, MSGL_HINT,
310 "Possible reasons are:\n"
311 "1) Network congestion.\n"
312 "2) Your NAS server is too slow.\n"
313 "Try renicing your nasd to e.g. -15.\n");
316 if (nas_readBuffer(nas_data,
317 nas_data->server_buffer_size -
318 nas_data->server_buffer_used) != 0) {
319 event->cur_state = AuStateStart;
320 break;
322 mp_msg(MSGT_AO, MSGL_DBG2,
323 "ao_nas: Can't refill buffer, stopping flow.\n");
324 AuStopFlow(aud, nas_data->flow, NULL);
325 break;
326 default:
327 break;
329 nas_data->state=event->cur_state;
330 return AuTrue;
333 static AuDeviceID nas_find_device(AuServer *aud, int nch)
335 int i;
336 for (i = 0; i < AuServerNumDevices(aud); i++) {
337 AuDeviceAttributes *dev = AuServerDevice(aud, i);
338 if ((AuDeviceKind(dev) == AuComponentKindPhysicalOutput) &&
339 AuDeviceNumTracks(dev) == nch) {
340 return AuDeviceIdentifier(dev);
343 return AuNone;
346 static unsigned int nas_aformat_to_auformat(unsigned int *format)
348 switch (*format) {
349 case AF_FORMAT_U8:
350 return AuFormatLinearUnsigned8;
351 case AF_FORMAT_S8:
352 return AuFormatLinearSigned8;
353 case AF_FORMAT_U16_LE:
354 return AuFormatLinearUnsigned16LSB;
355 case AF_FORMAT_U16_BE:
356 return AuFormatLinearUnsigned16MSB;
357 case AF_FORMAT_S16_LE:
358 return AuFormatLinearSigned16LSB;
359 case AF_FORMAT_S16_BE:
360 return AuFormatLinearSigned16MSB;
361 case AF_FORMAT_MU_LAW:
362 return AuFormatULAW8;
363 default:
364 *format=AF_FORMAT_S16_NE;
365 return nas_aformat_to_auformat(format);
369 // to set/get/query special features/parameters
370 static int control(int cmd, void *arg)
372 AuElementParameters aep;
373 AuStatus as;
374 int retval = CONTROL_UNKNOWN;
376 ao_control_vol_t *vol = (ao_control_vol_t *)arg;
378 switch (cmd) {
379 case AOCONTROL_GET_VOLUME:
381 vol->right = (float)nas_data->gain/AU_FIXED_POINT_SCALE*50;
382 vol->left = vol->right;
384 mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: AOCONTROL_GET_VOLUME: %.2f\n", vol->right);
385 retval = CONTROL_OK;
386 break;
388 case AOCONTROL_SET_VOLUME:
390 * kn: we should have vol->left == vol->right but i don't
391 * know if something can change it outside of ao_nas
392 * so i take the mean of both values.
394 nas_data->gain = AU_FIXED_POINT_SCALE*((vol->left+vol->right)/2)/50;
395 mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: AOCONTROL_SET_VOLUME: %.2f\n", (vol->left+vol->right)/2);
397 aep.parameters[AuParmsMultiplyConstantConstant]=nas_data->gain;
398 aep.flow = nas_data->flow;
399 aep.element_num = 1;
400 aep.num_parameters = AuParmsMultiplyConstant;
402 AuSetElementParameters(nas_data->aud, 1, &aep, &as);
403 if (as != AuSuccess) {
404 nas_print_error(nas_data->aud,
405 "control(): AuSetElementParameters", as);
406 retval = CONTROL_ERROR;
407 } else retval = CONTROL_OK;
408 break;
411 return retval;
414 // open & setup audio device
415 // return: 1=success 0=fail
416 static int init(int rate,int channels,int format,int flags)
418 AuElement elms[3];
419 AuStatus as;
420 unsigned char auformat = nas_aformat_to_auformat(&format);
421 int bytes_per_sample = channels * AuSizeofFormat(auformat);
422 int buffer_size;
423 char *server;
425 (void)flags; /* shut up 'unused parameter' warning */
427 nas_data=malloc(sizeof(struct ao_nas_data));
428 memset(nas_data, 0, sizeof(struct ao_nas_data));
430 mp_msg(MSGT_AO, MSGL_V, "ao2: %d Hz %d chans %s\n",rate,channels,
431 af_fmt2str_short(format));
433 ao_data.format = format;
434 ao_data.samplerate = rate;
435 ao_data.channels = channels;
436 ao_data.outburst = NAS_FRAG_SIZE;
437 ao_data.bps = rate * bytes_per_sample;
438 buffer_size = ao_data.bps; /* buffer 1 second */
440 * round up to multiple of NAS_FRAG_SIZE
441 * divide by 3 first because of 2:1 split
443 buffer_size = (buffer_size/3 + NAS_FRAG_SIZE-1) & ~(NAS_FRAG_SIZE-1);
444 ao_data.buffersize = buffer_size*3;
446 nas_data->client_buffer_size = buffer_size*2;
447 nas_data->client_buffer = malloc(nas_data->client_buffer_size);
448 nas_data->server_buffer_size = buffer_size;
449 nas_data->server_buffer = malloc(nas_data->server_buffer_size);
451 if (!bytes_per_sample) {
452 mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: init(): Zero bytes per sample -> nosound\n");
453 return 0;
456 if (!(server = getenv("AUDIOSERVER")) &&
457 !(server = getenv("DISPLAY"))) {
458 mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: init(): AUDIOSERVER environment variable not set -> nosound\n");
459 return 0;
462 mp_msg(MSGT_AO, MSGL_V, "ao_nas: init(): Using audioserver %s\n", server);
464 nas_data->aud = AuOpenServer(server, 0, NULL, 0, NULL, NULL);
465 if (!nas_data->aud) {
466 mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: init(): Can't open nas audio server -> nosound\n");
467 return 0;
470 while (channels>0) {
471 nas_data->dev = nas_find_device(nas_data->aud, channels);
472 if (nas_data->dev != AuNone &&
473 ((nas_data->flow = AuCreateFlow(nas_data->aud, NULL)) != 0))
474 break;
475 channels--;
478 if (nas_data->flow == 0) {
479 mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: init(): Can't find a suitable output device -> nosound\n");
480 AuCloseServer(nas_data->aud);
481 nas_data->aud = 0;
482 return 0;
485 AuMakeElementImportClient(elms, rate, auformat, channels, AuTrue,
486 buffer_size / bytes_per_sample,
487 (buffer_size - NAS_FRAG_SIZE) /
488 bytes_per_sample, 0, NULL);
489 nas_data->gain = AuFixedPointFromFraction(1, 1);
490 AuMakeElementMultiplyConstant(elms+1, 0, nas_data->gain);
491 AuMakeElementExportDevice(elms+2, 1, nas_data->dev, rate,
492 AuUnlimitedSamples, 0, NULL);
493 AuSetElements(nas_data->aud, nas_data->flow, AuTrue, sizeof(elms)/sizeof(*elms), elms, &as);
494 if (as != AuSuccess) {
495 nas_print_error(nas_data->aud, "init(): AuSetElements", as);
496 AuCloseServer(nas_data->aud);
497 nas_data->aud = 0;
498 return 0;
500 AuRegisterEventHandler(nas_data->aud, AuEventHandlerIDMask |
501 AuEventHandlerTypeMask,
502 AuEventTypeElementNotify, nas_data->flow,
503 nas_event_handler, (AuPointer) nas_data);
504 AuSetErrorHandler(nas_data->aud, nas_error_handler);
505 nas_data->state=AuStateStop;
506 nas_data->expect_underrun=0;
508 pthread_mutex_init(&nas_data->buffer_mutex, NULL);
509 pthread_create(&nas_data->event_thread, NULL, &nas_event_thread_start, nas_data);
511 return 1;
514 // close audio device
515 static void uninit(int immed){
517 mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: uninit()\n");
519 nas_data->expect_underrun = 1;
520 if (!immed)
521 while (nas_data->state != AuStateStop) usleep(1000);
522 nas_data->stop_thread = 1;
523 pthread_join(nas_data->event_thread, NULL);
524 AuCloseServer(nas_data->aud);
525 nas_data->aud = 0;
526 free(nas_data->client_buffer);
527 free(nas_data->server_buffer);
530 // stop playing and empty buffers (for seeking/pause)
531 static void reset(void){
532 AuStatus as;
534 mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: reset()\n");
536 pthread_mutex_lock(&nas_data->buffer_mutex);
537 nas_data->client_buffer_used = 0;
538 pthread_mutex_unlock(&nas_data->buffer_mutex);
539 while (nas_data->state != AuStateStop) {
540 AuStopFlow(nas_data->aud, nas_data->flow, &as);
541 if (as != AuSuccess)
542 nas_print_error(nas_data->aud, "reset(): AuStopFlow", as);
543 usleep(1000);
547 // stop playing, keep buffers (for pause)
548 static void audio_pause(void)
550 AuStatus as;
551 mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: audio_pause()\n");
553 AuStopFlow(nas_data->aud, nas_data->flow, &as);
556 // resume playing, after audio_pause()
557 static void audio_resume(void)
559 AuStatus as;
561 mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: audio_resume()\n");
563 AuStartFlow(nas_data->aud, nas_data->flow, &as);
564 if (as != AuSuccess)
565 nas_print_error(nas_data->aud,
566 "play(): AuStartFlow", as);
570 // return: how many bytes can be played without blocking
571 static int get_space(void)
573 int result;
575 mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: get_space()\n");
577 pthread_mutex_lock(&nas_data->buffer_mutex);
578 result = nas_data->client_buffer_size - nas_data->client_buffer_used;
579 pthread_mutex_unlock(&nas_data->buffer_mutex);
581 return result;
584 // plays 'len' bytes of 'data'
585 // it should round it down to outburst*n
586 // return: number of bytes played
587 static int play(void* data,int len,int flags)
589 int written, maxbursts = 0, playbursts = 0;
590 AuStatus as;
592 mp_msg(MSGT_AO, MSGL_DBG3,
593 "ao_nas: play(%p, %d, %d)\n",
594 data, len, flags);
596 if (len == 0)
597 return 0;
599 if (!(flags & AOPLAY_FINAL_CHUNK)) {
600 pthread_mutex_lock(&nas_data->buffer_mutex);
601 maxbursts = (nas_data->client_buffer_size -
602 nas_data->client_buffer_used) / ao_data.outburst;
603 playbursts = len / ao_data.outburst;
604 len = (playbursts > maxbursts ? maxbursts : playbursts) *
605 ao_data.outburst;
606 pthread_mutex_unlock(&nas_data->buffer_mutex);
610 * If AOPLAY_FINAL_CHUNK is set, we did not actually check len fits
611 * into the available buffer space, but mplayer.c shouldn't give us
612 * more to play than we report to it by get_space(), so this should be
613 * fine.
615 written = nas_writeBuffer(nas_data, data, len);
617 if (nas_data->state != AuStateStart &&
618 (maxbursts == playbursts ||
619 flags & AOPLAY_FINAL_CHUNK)) {
620 mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: play(): Starting flow.\n");
621 nas_data->expect_underrun = 1;
622 AuStartFlow(nas_data->aud, nas_data->flow, &as);
623 if (as != AuSuccess)
624 nas_print_error(nas_data->aud, "play(): AuStartFlow", as);
627 return written;
630 // return: delay in seconds between first and last sample in buffer
631 static float get_delay(void)
633 float result;
635 mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: get_delay()\n");
637 pthread_mutex_lock(&nas_data->buffer_mutex);
638 result = ((float)(nas_data->client_buffer_used +
639 nas_data->server_buffer_used)) /
640 (float)ao_data.bps;
641 pthread_mutex_unlock(&nas_data->buffer_mutex);
643 return result;