Coverity fixes
[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                 char *tmp = alloca(32);
181                 snprintf(tmp, 31, "0x%x", (unsigned int)snd->aud);
182                 write_c_string(tmp, pcfun);
183         }
184
185         return;
186 }
187
188 \f
189 static AuDeviceID
190 nas_find_device(AuServer *aud, int channels)
191 {
192         int i;
193         for (i = 0; i < AuServerNumDevices(aud); i++) {
194                 AuDeviceAttributes *dev = AuServerDevice(aud, i);
195                 if ((AuDeviceKind(dev) == AuComponentKindPhysicalOutput) &&
196                      AuDeviceNumTracks(dev) == channels) {
197                         return AuDeviceIdentifier(dev);
198                 }
199         }
200         return AuNone;
201 }
202
203 static void
204 nas_setup_defaults(char **server, int *cnt)
205 {
206         int i;
207         Lisp_Object tmp;
208
209         /* considering some defaults */
210         NAS_DEBUG("trying to find some defaults\n");
211
212 #ifdef HAVE_X_WINDOWS
213         /* check for the device connection of the currently active frame */
214         tmp = Fselected_device(Qnil);
215         if (DEVICEP(tmp) && DEVICE_X_P(XDEVICE(tmp)))
216                 server[(*cnt)++] =
217                         (char*)XSTRING_DATA(
218                                 DEVICE_CONNECTION(XDEVICE(tmp)));
219
220         /* tbd: check for conn of the initial frame */
221 #endif
222
223         /* try to look for $AUDIOSERVER */
224         if ((server[(*cnt)] = getenv("AUDIOSERVER"))) {
225                 /* only add the stuff, if not already in the try queue */
226                 for (i=0; i < (*cnt); i++)
227                         if (strcmp(server[i], server[(*cnt)]) == 0)
228                                 break;
229                 if (i == (*cnt))
230                         (*cnt)++;
231         }
232         /* try to look for $DISPLAY */
233         if ((server[(*cnt)] = getenv("DISPLAY"))){
234                 /* only add the stuff, if not already in the try queue */
235                 for (i=0; i < (*cnt); i++)
236                         if (strcmp(server[i], server[(*cnt)]) == 0)
237                                 break;
238                 if (i == (*cnt))
239                         (*cnt)++;
240         }
241
242         /* oh, let's try localhost:0.0, if not already there of course */
243         for (i=0; i < (*cnt); i++)
244                 if (strcmp(server[i], "localhost:0.0") == 0)
245                         break;
246         if (i == (*cnt))
247                 server[(*cnt)++] = "localhost:0.0";
248
249         /* finally we try NULL, too */
250         server[(*cnt)++] = NULL;
251
252         return;
253 }
254
255 static AuServer *
256 nas_try_connection(char *server)
257 {
258         AuServer *result = NULL;
259         char *err_message = NULL;
260
261         /* open server */
262         NAS_DEBUG_C("trying to contact NAS server: %s\n", server);
263         message(GETTEXT("trying to contact NAS server at %s..."),
264                 server);
265         result = AuOpenServer(server, 0, NULL, 0, NULL, &err_message);
266
267         if (!result) {
268                 NAS_DEBUG_C("cannot contact NAS server: %s\n",
269                              (err_message ? err_message : ":("));
270         }
271
272         return result;
273 }
274
275 static ad_device_data *
276 sound_nas_create(Lisp_Object nas_options)
277 {
278         sound_nas_data_t *snd;
279         char *server[6] = {NULL, NULL, NULL, NULL, NULL, NULL};
280         int i, server_cnt = 0;
281         AuServer *aud = NULL;
282         Lisp_Object opt_server = Qnil;
283
284         /* parse options */
285         opt_server = Fplist_get(nas_options, intern(":server"), Qnil);
286         if (!NILP(opt_server) && !STRINGP(opt_server) && !DEVICEP(opt_server)) {
287                 wrong_type_argument(Qstringp, opt_server);
288                 return NULL;
289         }
290
291         if (NILP(opt_server))
292                 nas_setup_defaults(server, &server_cnt);
293         else if (STRINGP(opt_server))
294                 server[server_cnt++] = (char*)XSTRING_DATA(opt_server);
295 #ifdef HAVE_X_WINDOWS
296         else if (DEVICEP(opt_server) && DEVICE_X_P(XDEVICE(opt_server)))
297                 server[server_cnt++] =
298                         (char*)XSTRING_DATA(
299                                 DEVICE_CONNECTION(XDEVICE(opt_server)));
300 #endif
301
302         NAS_DEBUG("trying %d connections\n", server_cnt);
303         for (i = 0; i<server_cnt; i++)
304                 if ((aud = nas_try_connection(server[i])))
305                         break;
306
307         if (!aud) {
308                 NAS_DEBUG_C("cannot contact any NAS server\n");
309                 warn_when_safe(Qnas, Qwarning,
310                                GETTEXT("No NAS servers in sight.\n"));
311                 return NULL; /* Could not contact NAS server */
312         }
313
314
315         /* -- initialise -- */
316         snd = xnew_and_zero(sound_nas_data_t);
317         snd->aud = aud;
318
319         /* round up SOUND_MAX_AUDIO_FRAME_SIZE to multiple of NAS_FRAG_SIZE
320          * divide by 3 first because of 2:1 split */
321         snd->proposed_buffer_size =
322                 (SOUND_MAX_AUDIO_FRAME_SIZE/3 + NAS_FRAG_SIZE-1)
323                 & ~(NAS_FRAG_SIZE-1);
324         NAS_DEBUG_C("proposed buffer size: %u\n", snd->proposed_buffer_size);
325
326         NAS_DEBUG_C("created: 0x%x\n", (unsigned int)snd);
327
328         return snd;
329 }
330
331 static void
332 sound_nas_finish(ad_device_data *data)
333 {
334         sound_nas_data_t *snd = data;
335
336         NAS_DEBUG("finishing NAS\n");
337
338         if (snd->aud) {
339                 NAS_DEBUG("closing 0x%x\n", (unsigned int)snd->aud);
340                 AuCloseServer(snd->aud);
341         }
342         snd->aud = NULL;
343
344         NAS_DEBUG("audio-device finished.\n");
345
346         return;
347 }
348
349 \f
350 #define snsd_t  sound_nas_aj_data_t
351 static int
352 nas_fill(audio_job_t aj, int nframes)
353 {
354         size_t len = 0, tmplen = 0;
355         sxe_media_sample_t *tmpbuf;
356         snsd_t *snsd = audio_job_device_data(aj);
357         int i;
358
359         tmpbuf = (sxe_media_sample_t*)(aj->buffer + snsd->writepos);
360         len = media_stream_meth(aj->substream->up, read)(
361                 aj->substream, aj->buffer + snsd->writepos,
362                 nframes);
363
364         /* set up the volume args */
365         snsd->volargs->volume[0] = snsd->volargs->volume[1] =
366                 aj->volume;
367         /* set up the rerate args */
368         snsd->rrargs->tweak = aj->ratetrafo;
369
370         /* coerce the stuff */
371         tmplen = snsd->channels*len;
372         for (i = 0; i < snsd->coe_ch_cnt; i++) {
373                 NAS_DEBUG_COE("calling coerce "
374                               "%d on b:0x%x l:%d\n",
375                               i, (unsigned int)tmpbuf, tmplen);
376                 tmplen = CALL_MEDIA_SAMPLE_EFFECT(
377                         snsd->coe_chain, i, tmpbuf, tmpbuf, tmplen);
378         }
379         /* bring back to S16 or U8 */
380         MEDIA_SAMPLE_FORMAT_DOWNSAMPLE(snsd->msf)(
381                 aj->buffer + snsd->writepos,
382                 aj->buffer + snsd->writepos,
383                 tmplen);
384
385         /* convert tmplen (#samples) to number of bytes */
386         tmplen = tmplen * snsd->framesize/snsd->channels;
387         snsd->overfill += tmplen;
388         snsd->writepos += tmplen;
389
390         /* increase underrun counter to detect the end */
391         if (len == 0)
392                 snsd->underrun++;
393
394         return tmplen;
395 }
396
397 static int
398 nas_read(audio_job_t aj, size_t nbytes)
399 {
400         AuStatus as;
401         size_t tmplen = 0;
402         snsd_t *snsd = audio_job_device_data(aj);
403
404         NAS_DEBUG_EV("want to read: %d\n", nbytes);
405
406         if (snsd->writepos < aj->buffer_alloc_size >> 2 &&
407             snsd->underrun < 4) {
408                 tmplen = nas_fill(aj, nbytes*snsd->channels/snsd->framesize);
409         } else if ((size_t)snsd->overfill > nbytes * 8 || snsd->underrun >= 4) {
410                 NAS_DEBUG_S("having a rest\n");
411         } else {
412                 NAS_DEBUG_S("resetting write position.\n");
413                 memcpy(aj->buffer,
414                        aj->buffer+snsd->readpos,
415                        snsd->writepos - snsd->readpos);
416                 snsd->writepos -= snsd->readpos;
417                 snsd->readpos = 0;
418         }
419
420         NAS_DEBUG_S("req:%d p, got:%d p, readpos %d, writepos %d, "
421                     "overfill: %d, framesize: %d\n",
422                     nbytes, tmplen*snsd->framesize,
423                     snsd->readpos, snsd->writepos,
424                     snsd->overfill, snsd->framesize);
425
426         /* care for buffer underruns */
427         if (nbytes > snsd->overfill)
428                 nbytes = snsd->overfill;
429
430         /*
431          * Now write the new buffer to the network.
432          */
433         NAS_DEBUG_C("writing %d bytes\n", nbytes);
434         AuWriteElement(snsd->snd->aud, snsd->flow, 0, nbytes,
435                        aj->buffer+snsd->readpos, AuFalse, &as);
436         if (as != AuSuccess)
437                 NAS_DEBUG_C("nas_read(): AuWriteElement\n");
438         snsd->readpos += nbytes;
439         snsd->overfill -= nbytes;
440
441         /* detect end of track */
442         if (snsd->underrun >= 4 && snsd->overfill < 64)
443                 aj->play_state = MTPSTATE_STOP;
444
445         return nbytes;
446 }
447
448 static AuBool
449 nas_event_handler(AuServer *aud, AuEvent *ev, AuEventHandlerRec *hnd)
450 {
451         AuElementNotifyEvent *event = (AuElementNotifyEvent*)ev;
452         audio_job_t aj = hnd->data;
453         snsd_t *snsd = audio_job_device_data(aj);
454
455         NAS_DEBUG_EV("event_handler(): "
456                      "type %s kind %s state %s->%s reason %s "
457                      "numbytes %u\n",
458                      nas_event_type(event->type),
459                      nas_elementnotify_kind(event->kind),
460                      nas_state(event->prev_state),
461                      nas_state(event->cur_state),
462                      nas_reason(event->reason),
463                      (uint32_t)event->num_bytes);
464         
465         if (event->num_bytes > INT_MAX) {
466                 NAS_CRITICAL("num_bytes > 2GB, server buggy?\n");
467         }
468
469         switch (event->reason) {
470         case AuReasonWatermark:
471                 nas_read(aj, event->num_bytes);
472                 break;
473         case AuReasonUnderrun:
474                 /* buffer underrun -> refill buffer */
475                 if (nas_read(aj, event->num_bytes) != 0) {
476                         event->cur_state = AuStateStart;
477                         NAS_DEBUG_EV("restarting\n");
478                         break;
479                 }
480                 NAS_DEBUG_S("Can't refill buffer, stopping flow.\n");
481                 AuStopFlow(aud, snsd->flow, NULL);
482                 aj->play_state = MTPSTATE_STOP;
483                 break;
484         default:
485                 break;
486         }
487
488         return AuTrue;
489 }
490
491 static AuBool
492 nas_error_handler(AuServer* aud, AuErrorEvent* ev)
493 {
494         char s[100];
495         AuGetErrorText(aud, ev->error_code, s, 100);
496         NAS_CRITICAL("error [%s]\n"
497                      "error_code: %d\n"
498                      "request_code: %d\n"
499                      "minor_code: %d\n",
500                      s, ev->error_code, ev->request_code, ev->minor_code);
501
502         LONGJMP(nas_server_sig, 1);
503         return AuTrue;
504 }
505
506 static AuBool
507 nas_IOerror_handler(AuServer *aud)
508 {
509         NAS_CRITICAL("Strange things happened.\n");
510         if (aud) {
511                 NAS_CRITICAL("Connection seems to be broken.\n");
512         } else {
513                 NAS_CRITICAL("Server raised SIGPIPE.\n");
514         }
515
516         LONGJMP(nas_server_sig, 1);
517         return AuTrue;
518 }
519
520 static int
521 nas_empty_event_queue(snsd_t *snsd)
522 {
523         AuEvent ev;
524         int result = 0;
525         
526         while (AuScanForTypedEvent(
527                        snsd->snd->aud, AuEventsQueuedAfterFlush,
528                        AuTrue, AuEventTypeElementNotify, &ev)) {
529                 AuDispatchEvent(snsd->snd->aud, &ev);
530                 result = 1;
531         }
532         return result;
533 }
534 \f
535 #ifdef EF_USE_ASYNEQ
536 static inline void
537 sound_nas_change_volume(audio_job_t aj, audio_job_event_args_t args)
538 {
539         SXE_MUTEX_LOCK(&aj->mtx);
540         aj->volume = args->volume_args;
541         SXE_MUTEX_UNLOCK(&aj->mtx);
542 }
543
544 static inline void
545 sound_nas_change_rate(audio_job_t aj, audio_job_event_args_t args)
546 {
547         SXE_MUTEX_LOCK(&aj->mtx);
548         aj->ratetrafo = args->rate_args;
549         SXE_MUTEX_UNLOCK(&aj->mtx);
550 }
551
552 static inline void
553 sound_nas_change_state(audio_job_t aj, audio_job_event_args_t args)
554 {
555         SXE_MUTEX_LOCK(&aj->mtx);
556         switch (args->state_args) {
557         case aj_pause:
558                 NAS_DEBUG_AJ("->pause state\n");
559                 aj->play_state = MTPSTATE_PAUSE;
560                 break;
561         case aj_resume:
562                 NAS_DEBUG_AJ("->resume state\n");
563                 aj->play_state = MTPSTATE_RUN;
564                 break;
565         case aj_start:
566                 NAS_DEBUG_AJ("->start state\n");
567                 break;
568         case aj_stop:
569                 NAS_DEBUG_AJ("->stop state\n");
570                 aj->play_state = MTPSTATE_STOP;
571                 break;
572         case no_audio_job_change_states:
573         default:
574                 NAS_DEBUG_AJ("->unknown state\n");
575                 break;
576         }
577         SXE_MUTEX_UNLOCK(&aj->mtx);
578 }
579
580 static inline void
581 sound_nas_handle_aj_events(audio_job_t aj)
582         __attribute__((always_inline));
583 static inline void
584 sound_nas_handle_aj_events(audio_job_t aj)
585 {
586         sound_nas_aj_data_t *sasd;
587         audio_job_event_t ev = NULL;
588
589 #if 0
590         assert(audio_job_queue(aj));
591 #endif
592
593         SXE_MUTEX_LOCK(&aj->mtx);
594         sasd = audio_job_device_data(aj);
595         if ((ev = eq_noseeum_dequeue(audio_job_queue(aj))) == NULL) {
596                 SXE_MUTEX_UNLOCK(&aj->mtx);
597                 return;
598         }
599         SXE_MUTEX_UNLOCK(&aj->mtx);
600
601         NAS_DEBUG_AJ("Event 0x%lx\n", (long unsigned int)ev);
602         switch (audio_job_event_kind(ev)) {
603         case aj_change_state:
604                 NAS_DEBUG_AJ("change state event\n");
605                 sound_nas_change_state(aj, &audio_job_event_args(ev));
606                 break;
607         case aj_change_volume:
608                 NAS_DEBUG_AJ("change volume event\n");
609                 sound_nas_change_volume(aj, &audio_job_event_args(ev));
610                 break;
611         case aj_change_rate:
612                 NAS_DEBUG_AJ("change rate event\n");
613                 sound_nas_change_rate(aj, &audio_job_event_args(ev));
614                 break;
615         case no_audio_job_event_kinds:
616         default:
617                 NAS_CRITICAL("unknown event\n");
618                 break;
619         }
620         free_audio_job_event(ev);
621 }
622 #endif  /* EF_USE_ASYNEQ */
623 \f
624 static int
625 sound_nas_play(audio_job_t aj)
626 {
627         /* stream stuff */
628         Lisp_Media_Stream *ms;
629         media_substream *mss;
630         /* thread stuff */
631         media_thread_play_state mtp;
632         /* device stuff */
633         Lisp_Object device;
634         Lisp_Audio_Device *lad = NULL;
635         sound_nas_data_t *snd = NULL;
636         /* nas stuff */
637         AuElement elms[3];
638         AuStatus as;
639         int bsize = 0;
640         /* subthread stuff */
641         sound_nas_aj_data_t _snsd, *snsd = &_snsd;
642         sxe_mse_volume_args _volargs, *volargs = &_volargs;
643         sxe_mse_rerate_args _rrargs, *rrargs = &_rrargs;
644         /* cache stuff */
645         int alloced_myself = 0;
646
647         SOUND_UNPACK_MT(aj, device, ms, mss, lad, snd, snsd->mtap);
648
649         /* refuse to do anything if the AuServer pointer is not set */
650         if (snd->aud == NULL) {
651                 NAS_DEBUG_C("b0rked connection, gute Nacht!\n");
652                 return 0;
653         }
654
655         /* install error handlers before anything else */
656         AuSetErrorHandler(snd->aud, nas_error_handler);
657         AuSetIOErrorHandler(snd->aud, nas_IOerror_handler);
658
659         /* find physical output device */
660         snsd->dev = nas_find_device(snd->aud, snsd->mtap->channels);
661
662         if (snsd->dev == AuNone || 
663             !(snsd->flow = AuCreateFlow(snd->aud, NULL))) {
664                 /* No physical output device found or flow creation failed. */
665                 NAS_DEBUG_C("no physical devices for this stream\n");
666                 return 0;
667         }
668
669         /* A system call interrupted with a SIGALRM or SIGIO
670            comes back here */
671         if (SETJMP(nas_server_sig)) {
672                 NAS_CRITICAL("Caught the lethal signal.\n");
673                 snd->aud = NULL;
674                 sound_nas_finish(snd);
675                 goto uhoh;
676         }
677
678         /* init the snsd */
679         snsd->samplerate = 0;
680         snsd->framesize = (snsd->channels = snsd->mtap->channels)
681                 * sizeof(int16_t);
682         snsd->msf = MEDIA_SAMPLE_FORMAT(sxe_msf_S16);
683         snsd->coe_ch_cnt = 0;
684         snsd->resolution =
685                 (snsd->mtap->samplerate * MTPSTATE_REACT_TIME) / 1000000;
686         bsize = (snsd->resolution + NAS_FRAG_SIZE - 1) & ~(NAS_FRAG_SIZE-1);
687
688         snsd->snd = snd;
689         snsd->buffer_size = bsize;
690         snsd->writepos = snsd->readpos = 0;
691         snsd->overfill = snsd->underrun = 0;
692
693         /* set up flow */
694         AuMakeElementImportClient(elms+0, snsd->mtap->samplerate,
695                                   AuFormatLinearSigned16LSB,
696                                   snsd->mtap->channels,
697                                   AuTrue,
698                                   bsize, bsize / 2,
699                                   0, NULL);
700         snsd->gain = AuFixedPointFromFraction(1, 1);
701         AuMakeElementMultiplyConstant(elms+1, 0, snsd->gain);
702         AuMakeElementExportDevice(elms+2, 0, snsd->dev,
703                                   snsd->mtap->samplerate,
704                                   AuUnlimitedSamples, 0, NULL);
705         AuSetElements(snd->aud, snsd->flow, AuTrue, 3, elms, &as);
706
707         if (as != AuSuccess) {
708                 NAS_DEBUG_C("play(): AuSetElements failed\n");
709                 return 0;
710         }
711
712 #if 0                           /* atm we insist on having stereo access */
713         /* the channel effect */
714         ADD_MEDIA_SAMPLE_EFFECT(
715                 snsd->coe_chain, snsd->coe_ch_cnt,
716                 MEDIA_SAMPLE_EFFECT(sxe_mse_2ch_to_1ch), NULL);
717 #endif
718
719         /* the volume effect */
720         ADD_MEDIA_SAMPLE_EFFECT(
721                 snsd->coe_chain, snsd->coe_ch_cnt,
722                 MEDIA_SAMPLE_EFFECT(sxe_mse_volume), volargs);
723         volargs->num_channels = snsd->channels;
724         snsd->volargs = volargs;
725
726         /* the rerate effect */
727         ADD_MEDIA_SAMPLE_EFFECT(
728                 snsd->coe_chain, snsd->coe_ch_cnt,
729                 MEDIA_SAMPLE_EFFECT(sxe_mse_rerate), rrargs);
730         rrargs->num_channels = snsd->channels;
731         rrargs->srcrate = rrargs->tgtrate = 1;
732         snsd->rrargs = rrargs;
733
734         AuRegisterEventHandler(snd->aud, AuEventHandlerIDMask |
735                                AuEventHandlerTypeMask,
736                                AuEventTypeElementNotify, snsd->flow,
737                                nas_event_handler, (AuPointer)aj);
738
739         /* rewind the stream */
740         media_stream_meth(ms, rewind)(mss);
741
742         /* play chunks of the stream */
743         SXE_MUTEX_LOCK(&aj->mtx);
744         if (aj->buffer_alloc_size < SOUND_MAX_AUDIO_FRAME_SIZE) {
745                 alloced_myself = 1;
746                 aj->buffer = xmalloc_atomic(SOUND_MAX_AUDIO_FRAME_SIZE);
747                 aj->buffer_alloc_size = SOUND_MAX_AUDIO_FRAME_SIZE;
748         }
749         SXE_MUTEX_UNLOCK(&aj->mtx);
750         snsd->mtp = MTPSTATE_STOP;
751         snsd->volume = -1;
752         audio_job_device_data(aj) = snsd;
753
754         /* prefill the buffer */
755         NAS_DEBUG_S("prefill the buffer: %d\n", 4*bsize);
756         if (!nas_fill(aj, 4*bsize))
757                 goto uhoh;
758
759         while (aj->play_state != MTPSTATE_STOP) {
760
761 #if 0
762                 if (aj->volume != snsd->volume) {
763                         AuElementParameters aep;
764                         NAS_DEBUG_S("Setting volume.\n");
765                         snsd->volume = aj->volume;
766                         snsd->gain = AU_FIXED_POINT_SCALE*(snsd->volume)/127;
767                         aep.parameters[AuParmsMultiplyConstantConstant] =
768                                 snsd->gain;
769                         aep.flow = snd->flow;
770                         aep.element_num = 1;
771                         aep.num_parameters = AuParmsMultiplyConstant;
772
773                         AuSetElementParameters(snd->aud, 1, &aep, &as);
774                         if (as != AuSuccess) {
775                                 NAS_DEBUG_S("Setting volume failed.\n");
776                         }
777                 }
778 #endif
779
780 #ifdef EF_USE_ASYNEQ
781                 /* events for me audio-job? */
782                 if (audio_job_queue(aj)) {
783                         sound_nas_handle_aj_events(aj);
784                 }
785 #endif
786
787                 SXE_MUTEX_LOCK(&aj->mtx);
788                 mtp = aj->play_state;
789                 SXE_MUTEX_UNLOCK(&aj->mtx);
790                 switch (mtp) {
791                 case MTPSTATE_RUN:
792                         if (snsd->mtp != mtp) {
793                                 NAS_DEBUG("ah, gotta work again\n");
794                                 AuStartFlow(snd->aud, snsd->flow, &as);
795                                 if (as != AuSuccess) {
796                                         NAS_DEBUG_C("play(): "
797                                                     "AuStartFlow failed\n");
798                                         aj->play_state = MTPSTATE_STOP;
799                                 }
800                         }
801                         nas_empty_event_queue(snsd);
802                         usleep(snsd->resolution);
803                         break;
804                 case MTPSTATE_PAUSE:
805                         if (snsd->mtp != mtp) {
806                                 NAS_DEBUG("sleeping for %d\n",
807                                           snsd->resolution);
808                                 AuStopFlow(snd->aud, snsd->flow, &as);
809                         }
810                         usleep(snsd->resolution);
811                         break;
812
813                 case MTPSTATE_UNKNOWN:
814                 case MTPSTATE_STOP:
815                 case NUMBER_OF_MEDIA_THREAD_PLAY_STATES:
816                 default:
817                         NAS_DEBUG("ACK, quit\n");
818                         AuStopFlow(snd->aud, snsd->flow, &as);
819                         SXE_MUTEX_LOCK(&aj->mtx);
820                         aj->play_state = MTPSTATE_STOP;
821                         SXE_MUTEX_UNLOCK(&aj->mtx);
822                         break;
823                 }
824                 snsd->mtp = mtp;
825         }
826
827 uhoh:
828         /* -- Close and shutdown -- */
829         SXE_MUTEX_LOCK(&aj->mtx);
830         if (alloced_myself && aj->buffer) {
831                 xfree(aj->buffer);
832         }
833         aj->buffer = NULL;
834         aj->buffer_alloc_size = 0;
835         SXE_MUTEX_UNLOCK(&aj->mtx);
836
837         return 1;
838 }
839
840 #undef MYSELF