Partially sync files.el from XEmacs 21.5 for wildcard support.
[sxemacs] / src / media / sound-nas.c
1 /* nas.c --- SXEmacs support for the Network Audio System server.
2  *
3  * Author: Sebastian Freundt <hroptatyr@sxemacs.org>
4  *
5  * Copyright 2006 Sebastian Freundt
6  *
7  * Permission to use, copy, modify, distribute, and sell this software and
8  * its documentation for any purpose is hereby granted without fee, provided
9  * that the above copyright notice appear in all copies and that both that
10  * copyright notice and this permission notice appear in supporting
11  * documentation, and that the name Network Computing Devices, Inc. not be
12  * used in advertising or publicity pertaining to distribution of this
13  * software without specific, written prior permission.
14  *
15  * THIS SOFTWARE IS PROVIDED 'AS-IS'.  NETWORK COMPUTING DEVICES, INC.,
16  * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT
17  * LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
18  * PARTICULAR PURPOSE, OR NONINFRINGEMENT.  IN NO EVENT SHALL NETWORK
19  * COMPUTING DEVICES, INC., BE LIABLE FOR ANY DAMAGES WHATSOEVER, INCLUDING
20  * SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, INCLUDING LOSS OF USE, DATA,
21  * OR PROFITS, EVEN IF ADVISED OF THE POSSIBILITY THEREOF, AND REGARDLESS OF
22  * WHETHER IN AN ACTION IN CONTRACT, TORT OR NEGLIGENCE, ARISING OUT OF OR IN
23  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24  */
25
26 /* Synched up with: Not in FSF. */
27
28 #include <config.h>
29 #include "lisp.h"
30 #include "sysdep.h"
31 #include "syssignal.h"
32 #include <stdlib.h>
33 #include <stdarg.h>
34 #include <string.h>
35 #include <stdio.h>
36 #include <setjmp.h>
37
38 #ifdef HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41
42 #include "media.h"
43 #include "sound-nas.h"
44 #include "ui/device.h"
45
46 static JMP_BUF nas_server_sig;
47 static AuBool nas_error_handler(AuServer* aud, AuErrorEvent* ev);
48 static AuBool nas_IOerror_handler(AuServer* aud);
49
50 DECLARE_AUDIO_DEVICE_SIMPLE_METHS(sound_nas);
51 DEFINE_AUDIO_DEVICE_SIMPLE(sound_nas);
52
53 Lisp_Object Qnas;
54 #define MYSELF ADRIVER_NAS
55
56 #define __NAS_DEBUG__(args...)          fprintf(stderr, "NAS " args)
57 #ifndef NAS_DEBUG_FLAG
58 #define NAS_DEBUG(args...)
59 #else
60 #define NAS_DEBUG(args...)              __NAS_DEBUG__(args)
61 #endif
62 #define NAS_DEBUG_C(args...)            NAS_DEBUG("[connection]: " args)
63 #define NAS_DEBUG_S(args...)            NAS_DEBUG("[stream]: " args)
64 #define NAS_DEBUG_COE(args...)          NAS_DEBUG("[coerce]: " args)
65 #define NAS_DEBUG_EV(args...)           NAS_DEBUG("[event]: " args)
66 #define NAS_DEBUG_AJ(args...)           NAS_DEBUG("[audio-job]: " args)
67 #define NAS_CRITICAL(args...)           __NAS_DEBUG__("CRITICAL: " args)
68
69 \f
70 static char *nas_event_types[] = {
71         "Undefined",
72         "Undefined",
73         "ElementNotify",
74         "GrabNotify",
75         "MonitorNotify",
76         "BucketNotify",
77         "DeviceNotify"
78 };
79
80 static char *nas_elementnotify_kinds[] = {
81         "LowWater",
82         "HighWater",
83         "State",
84         "Unknown"
85 };
86
87 static char *nas_states[] = {
88         "Stop",
89         "Start",
90         "Pause",
91         "Any"
92 };
93
94 static char *nas_reasons[] = {
95         "User",
96         "Underrun",
97         "Overrun",
98         "EOF",
99         "Watermark",
100         "Hardware",
101         "Any"
102 };
103
104 #if defined __GNUC__
105 static char* nas_reason(unsigned int reason) __attribute__((unused));
106 #endif
107 static char*
108 nas_reason(unsigned int reason)
109 {
110         if (reason > 6){
111                 reason = 6;
112         }
113         return nas_reasons[reason];
114 }
115
116 #if defined __GNUC__
117 static char* nas_elementnotify_kind(unsigned int kind) __attribute__((unused));
118 #endif
119 static char* nas_elementnotify_kind(unsigned int kind)
120 {
121         if (kind > 2) kind = 3;
122         return nas_elementnotify_kinds[kind];
123 }
124
125 #if defined __GNUC__
126 static char* nas_event_type(unsigned int type) __attribute__((unused));
127 #endif
128 static char* nas_event_type(unsigned int type)
129 {
130         if (type > 6) type = 0;
131         return nas_event_types[type];
132 }
133
134 #if defined __GNUC__
135 static char* nas_state(unsigned int state) __attribute__((unused));
136 #endif
137 static char* nas_state(unsigned int state)
138 {
139         if (state>3) state = 3;
140         return nas_states[state];
141 }
142
143 \f
144 static Lisp_Object
145 sound_nas_mark(ad_device_data *devdata)
146 {
147         sound_nas_data_t *snd = devdata;
148
149         snd = NULL;;
150
151         return Qnil;
152 }
153
154 static void
155 sound_nas_print(Lisp_Object device, Lisp_Object pcfun, int ef)
156 {
157         sound_nas_data_t *snd = NULL;
158
159         snd = get_audio_device_data(device);
160
161         /* cannot use incomplete or corrupt audio devices */
162         if (XAUDIO_DEVICE_DRIVER(device) != MYSELF || snd == NULL) {
163                 write_c_string(" VOID", pcfun);
164                 /* now that we are here, mark AO device as dead */
165                 XAUDIO_DEVICE_STATE(device) = ASTATE_DEAD;
166                 return;
167         }
168
169         /* info about the connected output plugin */
170         write_c_string(" :server ", pcfun);
171         if (snd->aud && snd->aud->server_name)
172                 write_c_string(snd->aud->server_name, pcfun);
173         else
174                 write_c_string("#unknown", pcfun);
175
176         write_c_string(" :server-handle ", pcfun);
177         if (snd->aud == NULL)
178                 write_c_string("#b0rked", pcfun);
179         else
180                 write_fmt_str(pcfun, "0x%x", (unsigned int)snd->aud);
181
182         return;
183 }
184
185 \f
186 static AuDeviceID
187 nas_find_device(AuServer *aud, int channels)
188 {
189         int i;
190         for (i = 0; i < AuServerNumDevices(aud); i++) {
191                 AuDeviceAttributes *dev = AuServerDevice(aud, i);
192                 if ((AuDeviceKind(dev) == AuComponentKindPhysicalOutput) &&
193                      AuDeviceNumTracks(dev) == channels) {
194                         return AuDeviceIdentifier(dev);
195                 }
196         }
197         return AuNone;
198 }
199
200 static void
201 nas_setup_defaults(char **server, int *cnt)
202 {
203         int i;
204         Lisp_Object tmp;
205
206         /* considering some defaults */
207         NAS_DEBUG("trying to find some defaults\n");
208
209 #ifdef HAVE_X_WINDOWS
210         /* check for the device connection of the currently active frame */
211         tmp = Fselected_device(Qnil);
212         if (DEVICEP(tmp) && DEVICE_X_P(XDEVICE(tmp)))
213                 server[(*cnt)++] =
214                         (char*)XSTRING_DATA(
215                                 DEVICE_CONNECTION(XDEVICE(tmp)));
216
217         /* tbd: check for conn of the initial frame */
218 #endif
219
220         /* try to look for $AUDIOSERVER */
221         if ((server[(*cnt)] = getenv("AUDIOSERVER"))) {
222                 /* only add the stuff, if not already in the try queue */
223                 for (i=0; i < (*cnt); i++)
224                         if (strcmp(server[i], server[(*cnt)]) == 0)
225                                 break;
226                 if (i == (*cnt))
227                         (*cnt)++;
228         }
229         /* try to look for $DISPLAY */
230         if ((server[(*cnt)] = getenv("DISPLAY"))){
231                 /* only add the stuff, if not already in the try queue */
232                 for (i=0; i < (*cnt); i++)
233                         if (strcmp(server[i], server[(*cnt)]) == 0)
234                                 break;
235                 if (i == (*cnt))
236                         (*cnt)++;
237         }
238
239         /* oh, let's try localhost:0.0, if not already there of course */
240         for (i=0; i < (*cnt); i++)
241                 if (strcmp(server[i], "localhost:0.0") == 0)
242                         break;
243         if (i == (*cnt))
244                 server[(*cnt)++] = "localhost:0.0";
245
246         /* finally we try NULL, too */
247         server[(*cnt)++] = NULL;
248
249         return;
250 }
251
252 static AuServer *
253 nas_try_connection(char *server)
254 {
255         AuServer *result = NULL;
256         char *err_message = NULL;
257
258         /* open server */
259         NAS_DEBUG_C("trying to contact NAS server: %s\n", server);
260         message(GETTEXT("trying to contact NAS server at %s..."),
261                 server);
262         result = AuOpenServer(server, 0, NULL, 0, NULL, &err_message);
263
264         if (!result) {
265                 NAS_DEBUG_C("cannot contact NAS server: %s\n",
266                              (err_message ? err_message : ":("));
267         }
268
269         return result;
270 }
271
272 static ad_device_data *
273 sound_nas_create(Lisp_Object nas_options)
274 {
275         sound_nas_data_t *snd;
276         char *server[6] = {NULL, NULL, NULL, NULL, NULL, NULL};
277         int i, server_cnt = 0;
278         AuServer *aud = NULL;
279         Lisp_Object opt_server = Qnil;
280
281         /* parse options */
282         opt_server = Fplist_get(nas_options, intern(":server"), Qnil);
283         if (!NILP(opt_server) && !STRINGP(opt_server) && !DEVICEP(opt_server)) {
284                 wrong_type_argument(Qstringp, opt_server);
285                 return NULL;
286         }
287
288         if (NILP(opt_server))
289                 nas_setup_defaults(server, &server_cnt);
290         else if (STRINGP(opt_server))
291                 server[server_cnt++] = (char*)XSTRING_DATA(opt_server);
292 #ifdef HAVE_X_WINDOWS
293         else if (DEVICEP(opt_server) && DEVICE_X_P(XDEVICE(opt_server)))
294                 server[server_cnt++] =
295                         (char*)XSTRING_DATA(
296                                 DEVICE_CONNECTION(XDEVICE(opt_server)));
297 #endif
298
299         NAS_DEBUG("trying %d connections\n", server_cnt);
300         for (i = 0; i<server_cnt; i++)
301                 if ((aud = nas_try_connection(server[i])))
302                         break;
303
304         if (!aud) {
305                 NAS_DEBUG_C("cannot contact any NAS server\n");
306                 warn_when_safe(Qnas, Qwarning,
307                                GETTEXT("No NAS servers in sight.\n"));
308                 return NULL; /* Could not contact NAS server */
309         }
310
311
312         /* -- initialise -- */
313         snd = xnew_and_zero(sound_nas_data_t);
314         snd->aud = aud;
315
316         /* round up SOUND_MAX_AUDIO_FRAME_SIZE to multiple of NAS_FRAG_SIZE
317          * divide by 3 first because of 2:1 split */
318         snd->proposed_buffer_size =
319                 (SOUND_MAX_AUDIO_FRAME_SIZE/3 + NAS_FRAG_SIZE-1)
320                 & ~(NAS_FRAG_SIZE-1);
321         NAS_DEBUG_C("proposed buffer size: %u\n", snd->proposed_buffer_size);
322
323         NAS_DEBUG_C("created: 0x%x\n", (unsigned int)snd);
324
325         return snd;
326 }
327
328 static void
329 sound_nas_finish(ad_device_data *data)
330 {
331         sound_nas_data_t *snd = data;
332
333         NAS_DEBUG("finishing NAS\n");
334
335         if (snd->aud) {
336                 NAS_DEBUG("closing 0x%x\n", (unsigned int)snd->aud);
337                 AuCloseServer(snd->aud);
338         }
339         snd->aud = NULL;
340
341         NAS_DEBUG("audio-device finished.\n");
342
343         return;
344 }
345
346 \f
347 #define snsd_t  sound_nas_aj_data_t
348 static int
349 nas_fill(audio_job_t aj, int nframes)
350 {
351         size_t len = 0, tmplen = 0;
352         sxe_media_sample_t *tmpbuf;
353         snsd_t *snsd = audio_job_device_data(aj);
354         int i;
355
356         tmpbuf = (sxe_media_sample_t*)(aj->buffer + snsd->writepos);
357         len = media_stream_meth(aj->substream->up, read)(
358                 aj->substream, aj->buffer + snsd->writepos,
359                 nframes);
360
361         /* set up the volume args */
362         snsd->volargs->volume[0] = snsd->volargs->volume[1] =
363                 aj->volume;
364         /* set up the rerate args */
365         snsd->rrargs->tweak = aj->ratetrafo;
366
367         /* coerce the stuff */
368         tmplen = snsd->channels*len;
369         for (i = 0; i < snsd->coe_ch_cnt; i++) {
370                 NAS_DEBUG_COE("calling coerce "
371                               "%d on b:0x%x l:%d\n",
372                               i, (unsigned int)tmpbuf, tmplen);
373                 tmplen = CALL_MEDIA_SAMPLE_EFFECT(
374                         snsd->coe_chain, i, tmpbuf, tmpbuf, tmplen);
375         }
376         /* bring back to S16 or U8 */
377         MEDIA_SAMPLE_FORMAT_DOWNSAMPLE(snsd->msf)(
378                 aj->buffer + snsd->writepos,
379                 aj->buffer + snsd->writepos,
380                 tmplen);
381
382         /* convert tmplen (#samples) to number of bytes */
383         tmplen = tmplen * snsd->framesize/snsd->channels;
384         snsd->overfill += tmplen;
385         snsd->writepos += tmplen;
386
387         /* increase underrun counter to detect the end */
388         if (len == 0)
389                 snsd->underrun++;
390
391         return tmplen;
392 }
393
394 static int
395 nas_read(audio_job_t aj, size_t nbytes)
396 {
397         AuStatus as;
398         size_t tmplen = 0;
399         snsd_t *snsd = audio_job_device_data(aj);
400
401         NAS_DEBUG_EV("want to read: %d\n", nbytes);
402
403         if (snsd->writepos < aj->buffer_alloc_size >> 2 &&
404             snsd->underrun < 4) {
405                 tmplen = nas_fill(aj, nbytes*snsd->channels/snsd->framesize);
406         } else if ((size_t)snsd->overfill > nbytes * 8 || snsd->underrun >= 4) {
407                 NAS_DEBUG_S("having a rest\n");
408         } else {
409                 NAS_DEBUG_S("resetting write position.\n");
410                 memcpy(aj->buffer,
411                        aj->buffer+snsd->readpos,
412                        snsd->writepos - snsd->readpos);
413                 snsd->writepos -= snsd->readpos;
414                 snsd->readpos = 0;
415         }
416
417         NAS_DEBUG_S("req:%d p, got:%d p, readpos %d, writepos %d, "
418                     "overfill: %d, framesize: %d\n",
419                     nbytes, tmplen*snsd->framesize,
420                     snsd->readpos, snsd->writepos,
421                     snsd->overfill, snsd->framesize);
422
423         /* care for buffer underruns */
424         if (nbytes > snsd->overfill)
425                 nbytes = snsd->overfill;
426
427         /*
428          * Now write the new buffer to the network.
429          */
430         NAS_DEBUG_C("writing %d bytes\n", nbytes);
431         AuWriteElement(snsd->snd->aud, snsd->flow, 0, nbytes,
432                        aj->buffer+snsd->readpos, AuFalse, &as);
433         if (as != AuSuccess)
434                 NAS_DEBUG_C("nas_read(): AuWriteElement\n");
435         snsd->readpos += nbytes;
436         snsd->overfill -= nbytes;
437
438         /* detect end of track */
439         if (snsd->underrun >= 4 && snsd->overfill < 64)
440                 aj->play_state = MTPSTATE_STOP;
441
442         return nbytes;
443 }
444
445 static AuBool
446 nas_event_handler(AuServer *aud, AuEvent *ev, AuEventHandlerRec *hnd)
447 {
448         AuElementNotifyEvent *event = (AuElementNotifyEvent*)ev;
449         audio_job_t aj = hnd->data;
450         snsd_t *snsd = audio_job_device_data(aj);
451
452         NAS_DEBUG_EV("event_handler(): "
453                      "type %s kind %s state %s->%s reason %s "
454                      "numbytes %u\n",
455                      nas_event_type(event->type),
456                      nas_elementnotify_kind(event->kind),
457                      nas_state(event->prev_state),
458                      nas_state(event->cur_state),
459                      nas_reason(event->reason),
460                      (uint32_t)event->num_bytes);
461
462         if (event->num_bytes > INT_MAX) {
463                 NAS_CRITICAL("num_bytes > 2GB, server buggy?\n");
464         }
465
466         switch (event->reason) {
467         case AuReasonWatermark:
468                 nas_read(aj, event->num_bytes);
469                 break;
470         case AuReasonUnderrun:
471                 /* buffer underrun -> refill buffer */
472                 if (nas_read(aj, event->num_bytes) != 0) {
473                         event->cur_state = AuStateStart;
474                         NAS_DEBUG_EV("restarting\n");
475                         break;
476                 }
477                 NAS_DEBUG_S("Can't refill buffer, stopping flow.\n");
478                 AuStopFlow(aud, snsd->flow, NULL);
479                 aj->play_state = MTPSTATE_STOP;
480                 break;
481         default:
482                 break;
483         }
484
485         return AuTrue;
486 }
487
488 static AuBool
489 nas_error_handler(AuServer* aud, AuErrorEvent* ev)
490 {
491         char s[100];
492         AuGetErrorText(aud, ev->error_code, s, 100);
493         NAS_CRITICAL("error [%s]\n"
494                      "error_code: %d\n"
495                      "request_code: %d\n"
496                      "minor_code: %d\n",
497                      s, ev->error_code, ev->request_code, ev->minor_code);
498
499         LONGJMP(nas_server_sig, 1);
500         return AuTrue;
501 }
502
503 static AuBool
504 nas_IOerror_handler(AuServer *aud)
505 {
506         NAS_CRITICAL("Strange things happened.\n");
507         if (aud) {
508                 NAS_CRITICAL("Connection seems to be broken.\n");
509         } else {
510                 NAS_CRITICAL("Server raised SIGPIPE.\n");
511         }
512
513         LONGJMP(nas_server_sig, 1);
514         return AuTrue;
515 }
516
517 static int
518 nas_empty_event_queue(snsd_t *snsd)
519 {
520         AuEvent ev;
521         int result = 0;
522
523         while (AuScanForTypedEvent(
524                        snsd->snd->aud, AuEventsQueuedAfterFlush,
525                        AuTrue, AuEventTypeElementNotify, &ev)) {
526                 AuDispatchEvent(snsd->snd->aud, &ev);
527                 result = 1;
528         }
529         return result;
530 }
531 \f
532 #ifdef EF_USE_ASYNEQ
533 static inline void
534 sound_nas_change_volume(audio_job_t aj, audio_job_event_args_t args)
535 {
536         SXE_MUTEX_LOCK(&aj->mtx);
537         aj->volume = args->volume_args;
538         SXE_MUTEX_UNLOCK(&aj->mtx);
539 }
540
541 static inline void
542 sound_nas_change_rate(audio_job_t aj, audio_job_event_args_t args)
543 {
544         SXE_MUTEX_LOCK(&aj->mtx);
545         aj->ratetrafo = args->rate_args;
546         SXE_MUTEX_UNLOCK(&aj->mtx);
547 }
548
549 static inline void
550 sound_nas_change_state(audio_job_t aj, audio_job_event_args_t args)
551 {
552         SXE_MUTEX_LOCK(&aj->mtx);
553         switch (args->state_args) {
554         case aj_pause:
555                 NAS_DEBUG_AJ("->pause state\n");
556                 aj->play_state = MTPSTATE_PAUSE;
557                 break;
558         case aj_resume:
559                 NAS_DEBUG_AJ("->resume state\n");
560                 aj->play_state = MTPSTATE_RUN;
561                 break;
562         case aj_start:
563                 NAS_DEBUG_AJ("->start state\n");
564                 break;
565         case aj_stop:
566                 NAS_DEBUG_AJ("->stop state\n");
567                 aj->play_state = MTPSTATE_STOP;
568                 break;
569         case no_audio_job_change_states:
570         default:
571                 NAS_DEBUG_AJ("->unknown state\n");
572                 break;
573         }
574         SXE_MUTEX_UNLOCK(&aj->mtx);
575 }
576
577 static inline void
578 sound_nas_handle_aj_events(audio_job_t aj)
579         __attribute__((always_inline));
580 static inline void
581 sound_nas_handle_aj_events(audio_job_t aj)
582 {
583         sound_nas_aj_data_t *sasd;
584         audio_job_event_t ev = NULL;
585
586 #if 0
587         assert(audio_job_queue(aj));
588 #endif
589
590         SXE_MUTEX_LOCK(&aj->mtx);
591         sasd = audio_job_device_data(aj);
592         if ((ev = eq_noseeum_dequeue(audio_job_queue(aj))) == NULL) {
593                 SXE_MUTEX_UNLOCK(&aj->mtx);
594                 return;
595         }
596         SXE_MUTEX_UNLOCK(&aj->mtx);
597
598         NAS_DEBUG_AJ("Event 0x%lx\n", (long unsigned int)ev);
599         switch (audio_job_event_kind(ev)) {
600         case aj_change_state:
601                 NAS_DEBUG_AJ("change state event\n");
602                 sound_nas_change_state(aj, &audio_job_event_args(ev));
603                 break;
604         case aj_change_volume:
605                 NAS_DEBUG_AJ("change volume event\n");
606                 sound_nas_change_volume(aj, &audio_job_event_args(ev));
607                 break;
608         case aj_change_rate:
609                 NAS_DEBUG_AJ("change rate event\n");
610                 sound_nas_change_rate(aj, &audio_job_event_args(ev));
611                 break;
612         case no_audio_job_event_kinds:
613         default:
614                 NAS_CRITICAL("unknown event\n");
615                 break;
616         }
617         free_audio_job_event(ev);
618 }
619 #endif  /* EF_USE_ASYNEQ */
620 \f
621 static int
622 sound_nas_play(audio_job_t aj)
623 {
624         /* stream stuff */
625         Lisp_Media_Stream *ms;
626         media_substream *mss;
627         /* thread stuff */
628         media_thread_play_state mtp;
629         /* device stuff */
630         Lisp_Object device;
631         Lisp_Audio_Device *lad = NULL;
632         sound_nas_data_t *snd = NULL;
633         /* nas stuff */
634         AuElement elms[3];
635         AuStatus as;
636         int bsize = 0;
637         /* subthread stuff */
638         sound_nas_aj_data_t _snsd, *snsd = &_snsd;
639         sxe_mse_volume_args _volargs, *volargs = &_volargs;
640         sxe_mse_rerate_args _rrargs, *rrargs = &_rrargs;
641         /* cache stuff */
642         int alloced_myself = 0;
643
644         SOUND_UNPACK_MT(aj, device, ms, mss, lad, snd, snsd->mtap);
645
646         /* refuse to do anything if the AuServer pointer is not set */
647         if (snd->aud == NULL) {
648                 NAS_DEBUG_C("b0rked connection, gute Nacht!\n");
649                 return 0;
650         }
651
652         /* install error handlers before anything else */
653         AuSetErrorHandler(snd->aud, nas_error_handler);
654         AuSetIOErrorHandler(snd->aud, nas_IOerror_handler);
655
656         /* find physical output device */
657         snsd->dev = nas_find_device(snd->aud, snsd->mtap->channels);
658
659         if (snsd->dev == AuNone ||
660             !(snsd->flow = AuCreateFlow(snd->aud, NULL))) {
661                 /* No physical output device found or flow creation failed. */
662                 NAS_DEBUG_C("no physical devices for this stream\n");
663                 return 0;
664         }
665
666         /* A system call interrupted with a SIGALRM or SIGIO
667            comes back here */
668         if (SETJMP(nas_server_sig)) {
669                 NAS_CRITICAL("Caught the lethal signal.\n");
670                 snd->aud = NULL;
671                 sound_nas_finish(snd);
672                 goto uhoh;
673         }
674
675         /* init the snsd */
676         snsd->samplerate = 0;
677         snsd->framesize = (snsd->channels = snsd->mtap->channels)
678                 * sizeof(int16_t);
679         snsd->msf = MEDIA_SAMPLE_FORMAT(sxe_msf_S16);
680         snsd->coe_ch_cnt = 0;
681         snsd->resolution =
682                 (snsd->mtap->samplerate * MTPSTATE_REACT_TIME) / 1000000;
683         bsize = (snsd->resolution + NAS_FRAG_SIZE - 1) & ~(NAS_FRAG_SIZE-1);
684
685         snsd->snd = snd;
686         snsd->buffer_size = bsize;
687         snsd->writepos = snsd->readpos = 0;
688         snsd->overfill = snsd->underrun = 0;
689
690         /* set up flow */
691         AuMakeElementImportClient(elms+0, snsd->mtap->samplerate,
692                                   AuFormatLinearSigned16LSB,
693                                   snsd->mtap->channels,
694                                   AuTrue,
695                                   bsize, bsize / 2,
696                                   0, NULL);
697         snsd->gain = AuFixedPointFromFraction(1, 1);
698         AuMakeElementMultiplyConstant(elms+1, 0, snsd->gain);
699         AuMakeElementExportDevice(elms+2, 0, snsd->dev,
700                                   snsd->mtap->samplerate,
701                                   AuUnlimitedSamples, 0, NULL);
702         AuSetElements(snd->aud, snsd->flow, AuTrue, 3, elms, &as);
703
704         if (as != AuSuccess) {
705                 NAS_DEBUG_C("play(): AuSetElements failed\n");
706                 return 0;
707         }
708
709 #if 0                           /* atm we insist on having stereo access */
710         /* the channel effect */
711         ADD_MEDIA_SAMPLE_EFFECT(
712                 snsd->coe_chain, snsd->coe_ch_cnt,
713                 MEDIA_SAMPLE_EFFECT(sxe_mse_2ch_to_1ch), NULL);
714 #endif
715
716         /* the volume effect */
717         ADD_MEDIA_SAMPLE_EFFECT(
718                 snsd->coe_chain, snsd->coe_ch_cnt,
719                 MEDIA_SAMPLE_EFFECT(sxe_mse_volume), volargs);
720         volargs->num_channels = snsd->channels;
721         snsd->volargs = volargs;
722
723         /* the rerate effect */
724         ADD_MEDIA_SAMPLE_EFFECT(
725                 snsd->coe_chain, snsd->coe_ch_cnt,
726                 MEDIA_SAMPLE_EFFECT(sxe_mse_rerate), rrargs);
727         rrargs->num_channels = snsd->channels;
728         rrargs->srcrate = rrargs->tgtrate = 1;
729         snsd->rrargs = rrargs;
730
731         AuRegisterEventHandler(snd->aud, AuEventHandlerIDMask |
732                                AuEventHandlerTypeMask,
733                                AuEventTypeElementNotify, snsd->flow,
734                                nas_event_handler, (AuPointer)aj);
735
736         /* rewind the stream */
737         media_stream_meth(ms, rewind)(mss);
738
739         /* play chunks of the stream */
740         SXE_MUTEX_LOCK(&aj->mtx);
741         if (aj->buffer_alloc_size < SOUND_MAX_AUDIO_FRAME_SIZE) {
742                 alloced_myself = 1;
743                 aj->buffer = xmalloc_atomic(SOUND_MAX_AUDIO_FRAME_SIZE);
744                 aj->buffer_alloc_size = SOUND_MAX_AUDIO_FRAME_SIZE;
745         }
746         SXE_MUTEX_UNLOCK(&aj->mtx);
747         snsd->mtp = MTPSTATE_STOP;
748         snsd->volume = -1;
749         audio_job_device_data(aj) = snsd;
750
751         /* prefill the buffer */
752         NAS_DEBUG_S("prefill the buffer: %d\n", 4*bsize);
753         if (!nas_fill(aj, 4*bsize))
754                 goto uhoh;
755
756         while (aj->play_state != MTPSTATE_STOP) {
757
758 #if 0
759                 if (aj->volume != snsd->volume) {
760                         AuElementParameters aep;
761                         NAS_DEBUG_S("Setting volume.\n");
762                         snsd->volume = aj->volume;
763                         snsd->gain = AU_FIXED_POINT_SCALE*(snsd->volume)/127;
764                         aep.parameters[AuParmsMultiplyConstantConstant] =
765                                 snsd->gain;
766                         aep.flow = snd->flow;
767                         aep.element_num = 1;
768                         aep.num_parameters = AuParmsMultiplyConstant;
769
770                         AuSetElementParameters(snd->aud, 1, &aep, &as);
771                         if (as != AuSuccess) {
772                                 NAS_DEBUG_S("Setting volume failed.\n");
773                         }
774                 }
775 #endif
776
777 #ifdef EF_USE_ASYNEQ
778                 /* events for me audio-job? */
779                 if (audio_job_queue(aj)) {
780                         sound_nas_handle_aj_events(aj);
781                 }
782 #endif
783
784                 SXE_MUTEX_LOCK(&aj->mtx);
785                 mtp = aj->play_state;
786                 SXE_MUTEX_UNLOCK(&aj->mtx);
787                 switch (mtp) {
788                 case MTPSTATE_RUN:
789                         if (snsd->mtp != mtp) {
790                                 NAS_DEBUG("ah, gotta work again\n");
791                                 AuStartFlow(snd->aud, snsd->flow, &as);
792                                 if (as != AuSuccess) {
793                                         NAS_DEBUG_C("play(): "
794                                                     "AuStartFlow failed\n");
795                                         aj->play_state = MTPSTATE_STOP;
796                                 }
797                         }
798                         nas_empty_event_queue(snsd);
799                         usleep(snsd->resolution);
800                         break;
801                 case MTPSTATE_PAUSE:
802                         if (snsd->mtp != mtp) {
803                                 NAS_DEBUG("sleeping for %d\n",
804                                           snsd->resolution);
805                                 AuStopFlow(snd->aud, snsd->flow, &as);
806                         }
807                         usleep(snsd->resolution);
808                         break;
809
810                 case MTPSTATE_UNKNOWN:
811                 case MTPSTATE_STOP:
812                 case NUMBER_OF_MEDIA_THREAD_PLAY_STATES:
813                 default:
814                         NAS_DEBUG("ACK, quit\n");
815                         AuStopFlow(snd->aud, snsd->flow, &as);
816                         SXE_MUTEX_LOCK(&aj->mtx);
817                         aj->play_state = MTPSTATE_STOP;
818                         SXE_MUTEX_UNLOCK(&aj->mtx);
819                         break;
820                 }
821                 snsd->mtp = mtp;
822         }
823
824 uhoh:
825         /* -- Close and shutdown -- */
826         SXE_MUTEX_LOCK(&aj->mtx);
827         if (alloced_myself && aj->buffer) {
828                 xfree(aj->buffer);
829         }
830         aj->buffer = NULL;
831         aj->buffer_alloc_size = 0;
832         SXE_MUTEX_UNLOCK(&aj->mtx);
833
834         return 1;
835 }
836
837 #undef MYSELF