1 /* nas.c --- SXEmacs support for the Network Audio System server.
3 * Author: Sebastian Freundt <hroptatyr@sxemacs.org>
5 * Copyright 2006 Sebastian Freundt
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.
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.
26 /* Synched up with: Not in FSF. */
31 #include "syssignal.h"
43 #include "sound-nas.h"
44 #include "ui/device.h"
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);
50 DECLARE_AUDIO_DEVICE_SIMPLE_METHS(sound_nas);
51 DEFINE_AUDIO_DEVICE_SIMPLE(sound_nas);
54 #define MYSELF ADRIVER_NAS
56 #define __NAS_DEBUG__(args...) fprintf(stderr, "NAS " args)
57 #ifndef NAS_DEBUG_FLAG
58 #define NAS_DEBUG(args...)
60 #define NAS_DEBUG(args...) __NAS_DEBUG__(args)
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)
70 static char *nas_event_types[] = {
80 static char *nas_elementnotify_kinds[] = {
87 static char *nas_states[] = {
94 static char *nas_reasons[] = {
105 static char* nas_reason(unsigned int reason) __attribute__((unused));
108 nas_reason(unsigned int reason)
113 return nas_reasons[reason];
117 static char* nas_elementnotify_kind(unsigned int kind) __attribute__((unused));
119 static char* nas_elementnotify_kind(unsigned int kind)
121 if (kind > 2) kind = 3;
122 return nas_elementnotify_kinds[kind];
126 static char* nas_event_type(unsigned int type) __attribute__((unused));
128 static char* nas_event_type(unsigned int type)
130 if (type > 6) type = 0;
131 return nas_event_types[type];
135 static char* nas_state(unsigned int state) __attribute__((unused));
137 static char* nas_state(unsigned int state)
139 if (state>3) state = 3;
140 return nas_states[state];
145 sound_nas_mark(ad_device_data *devdata)
147 sound_nas_data_t *snd = devdata;
155 sound_nas_print(Lisp_Object device, Lisp_Object pcfun, int ef)
157 sound_nas_data_t *snd = NULL;
159 snd = get_audio_device_data(device);
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;
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);
174 write_c_string("#unknown", pcfun);
176 write_c_string(" :server-handle ", pcfun);
177 if (snd->aud == NULL)
178 write_c_string("#b0rked", pcfun);
180 char *tmp = alloca(32);
181 snprintf(tmp, 31, "0x%x", (unsigned int)snd->aud);
182 write_c_string(tmp, pcfun);
190 nas_find_device(AuServer *aud, int channels)
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);
204 nas_setup_defaults(char **server, int *cnt)
209 /* considering some defaults */
210 NAS_DEBUG("trying to find some defaults\n");
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)))
218 DEVICE_CONNECTION(XDEVICE(tmp)));
220 /* tbd: check for conn of the initial frame */
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)
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)
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)
247 server[(*cnt)++] = "localhost:0.0";
249 /* finally we try NULL, too */
250 server[(*cnt)++] = NULL;
256 nas_try_connection(char *server)
258 AuServer *result = NULL;
259 char *err_message = NULL;
262 NAS_DEBUG_C("trying to contact NAS server: %s\n", server);
263 message(GETTEXT("trying to contact NAS server at %s..."),
265 result = AuOpenServer(server, 0, NULL, 0, NULL, &err_message);
268 NAS_DEBUG_C("cannot contact NAS server: %s\n",
269 (err_message ? err_message : ":("));
275 static ad_device_data *
276 sound_nas_create(Lisp_Object nas_options)
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;
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);
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++] =
299 DEVICE_CONNECTION(XDEVICE(opt_server)));
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])))
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 */
315 /* -- initialise -- */
316 snd = xnew_and_zero(sound_nas_data_t);
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);
326 NAS_DEBUG_C("created: 0x%x\n", (unsigned int)snd);
332 sound_nas_finish(ad_device_data *data)
334 sound_nas_data_t *snd = data;
336 NAS_DEBUG("finishing NAS\n");
339 NAS_DEBUG("closing 0x%x\n", (unsigned int)snd->aud);
340 AuCloseServer(snd->aud);
344 NAS_DEBUG("audio-device finished.\n");
350 #define snsd_t sound_nas_aj_data_t
352 nas_fill(audio_job_t aj, int nframes)
354 size_t len = 0, tmplen = 0;
355 sxe_media_sample_t *tmpbuf;
356 snsd_t *snsd = audio_job_device_data(aj);
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,
364 /* set up the volume args */
365 snsd->volargs->volume[0] = snsd->volargs->volume[1] =
367 /* set up the rerate args */
368 snsd->rrargs->tweak = aj->ratetrafo;
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);
379 /* bring back to S16 or U8 */
380 MEDIA_SAMPLE_FORMAT_DOWNSAMPLE(snsd->msf)(
381 aj->buffer + snsd->writepos,
382 aj->buffer + snsd->writepos,
385 /* convert tmplen (#samples) to number of bytes */
386 tmplen = tmplen * snsd->framesize/snsd->channels;
387 snsd->overfill += tmplen;
388 snsd->writepos += tmplen;
390 /* increase underrun counter to detect the end */
398 nas_read(audio_job_t aj, size_t nbytes)
402 snsd_t *snsd = audio_job_device_data(aj);
404 NAS_DEBUG_EV("want to read: %d\n", nbytes);
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");
412 NAS_DEBUG_S("resetting write position.\n");
414 aj->buffer+snsd->readpos,
415 snsd->writepos - snsd->readpos);
416 snsd->writepos -= snsd->readpos;
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);
426 /* care for buffer underruns */
427 if (nbytes > snsd->overfill)
428 nbytes = snsd->overfill;
431 * Now write the new buffer to the network.
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);
437 NAS_DEBUG_C("nas_read(): AuWriteElement\n");
438 snsd->readpos += nbytes;
439 snsd->overfill -= nbytes;
441 /* detect end of track */
442 if (snsd->underrun >= 4 && snsd->overfill < 64)
443 aj->play_state = MTPSTATE_STOP;
449 nas_event_handler(AuServer *aud, AuEvent *ev, AuEventHandlerRec *hnd)
451 AuElementNotifyEvent *event = (AuElementNotifyEvent*)ev;
452 audio_job_t aj = hnd->data;
453 snsd_t *snsd = audio_job_device_data(aj);
455 NAS_DEBUG_EV("event_handler(): "
456 "type %s kind %s state %s->%s reason %s "
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);
465 if (event->num_bytes > INT_MAX) {
466 NAS_CRITICAL("num_bytes > 2GB, server buggy?\n");
469 switch (event->reason) {
470 case AuReasonWatermark:
471 nas_read(aj, event->num_bytes);
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");
480 NAS_DEBUG_S("Can't refill buffer, stopping flow.\n");
481 AuStopFlow(aud, snsd->flow, NULL);
482 aj->play_state = MTPSTATE_STOP;
492 nas_error_handler(AuServer* aud, AuErrorEvent* ev)
495 AuGetErrorText(aud, ev->error_code, s, 100);
496 NAS_CRITICAL("error [%s]\n"
500 s, ev->error_code, ev->request_code, ev->minor_code);
502 LONGJMP(nas_server_sig, 1);
507 nas_IOerror_handler(AuServer *aud)
509 NAS_CRITICAL("Strange things happened.\n");
511 NAS_CRITICAL("Connection seems to be broken.\n");
513 NAS_CRITICAL("Server raised SIGPIPE.\n");
516 LONGJMP(nas_server_sig, 1);
521 nas_empty_event_queue(snsd_t *snsd)
526 while (AuScanForTypedEvent(
527 snsd->snd->aud, AuEventsQueuedAfterFlush,
528 AuTrue, AuEventTypeElementNotify, &ev)) {
529 AuDispatchEvent(snsd->snd->aud, &ev);
537 sound_nas_change_volume(audio_job_t aj, audio_job_event_args_t args)
539 SXE_MUTEX_LOCK(&aj->mtx);
540 aj->volume = args->volume_args;
541 SXE_MUTEX_UNLOCK(&aj->mtx);
545 sound_nas_change_rate(audio_job_t aj, audio_job_event_args_t args)
547 SXE_MUTEX_LOCK(&aj->mtx);
548 aj->ratetrafo = args->rate_args;
549 SXE_MUTEX_UNLOCK(&aj->mtx);
553 sound_nas_change_state(audio_job_t aj, audio_job_event_args_t args)
555 SXE_MUTEX_LOCK(&aj->mtx);
556 switch (args->state_args) {
558 NAS_DEBUG_AJ("->pause state\n");
559 aj->play_state = MTPSTATE_PAUSE;
562 NAS_DEBUG_AJ("->resume state\n");
563 aj->play_state = MTPSTATE_RUN;
566 NAS_DEBUG_AJ("->start state\n");
569 NAS_DEBUG_AJ("->stop state\n");
570 aj->play_state = MTPSTATE_STOP;
572 case no_audio_job_change_states:
574 NAS_DEBUG_AJ("->unknown state\n");
577 SXE_MUTEX_UNLOCK(&aj->mtx);
581 sound_nas_handle_aj_events(audio_job_t aj)
582 __attribute__((always_inline));
584 sound_nas_handle_aj_events(audio_job_t aj)
586 sound_nas_aj_data_t *sasd;
587 audio_job_event_t ev = NULL;
590 assert(audio_job_queue(aj));
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);
599 SXE_MUTEX_UNLOCK(&aj->mtx);
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));
607 case aj_change_volume:
608 NAS_DEBUG_AJ("change volume event\n");
609 sound_nas_change_volume(aj, &audio_job_event_args(ev));
612 NAS_DEBUG_AJ("change rate event\n");
613 sound_nas_change_rate(aj, &audio_job_event_args(ev));
615 case no_audio_job_event_kinds:
617 NAS_CRITICAL("unknown event\n");
620 free_audio_job_event(ev);
622 #endif /* EF_USE_ASYNEQ */
625 sound_nas_play(audio_job_t aj)
628 Lisp_Media_Stream *ms;
629 media_substream *mss;
631 media_thread_play_state mtp;
634 Lisp_Audio_Device *lad = NULL;
635 sound_nas_data_t *snd = NULL;
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;
645 int alloced_myself = 0;
647 SOUND_UNPACK_MT(aj, device, ms, mss, lad, snd, snsd->mtap);
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");
655 /* install error handlers before anything else */
656 AuSetErrorHandler(snd->aud, nas_error_handler);
657 AuSetIOErrorHandler(snd->aud, nas_IOerror_handler);
659 /* find physical output device */
660 snsd->dev = nas_find_device(snd->aud, snsd->mtap->channels);
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");
669 /* A system call interrupted with a SIGALRM or SIGIO
671 if (SETJMP(nas_server_sig)) {
672 NAS_CRITICAL("Caught the lethal signal.\n");
674 sound_nas_finish(snd);
679 snsd->samplerate = 0;
680 snsd->framesize = (snsd->channels = snsd->mtap->channels)
682 snsd->msf = MEDIA_SAMPLE_FORMAT(sxe_msf_S16);
683 snsd->coe_ch_cnt = 0;
685 (snsd->mtap->samplerate * MTPSTATE_REACT_TIME) / 1000000;
686 bsize = (snsd->resolution + NAS_FRAG_SIZE - 1) & ~(NAS_FRAG_SIZE-1);
689 snsd->buffer_size = bsize;
690 snsd->writepos = snsd->readpos = 0;
691 snsd->overfill = snsd->underrun = 0;
694 AuMakeElementImportClient(elms+0, snsd->mtap->samplerate,
695 AuFormatLinearSigned16LSB,
696 snsd->mtap->channels,
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);
707 if (as != AuSuccess) {
708 NAS_DEBUG_C("play(): AuSetElements failed\n");
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);
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;
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;
734 AuRegisterEventHandler(snd->aud, AuEventHandlerIDMask |
735 AuEventHandlerTypeMask,
736 AuEventTypeElementNotify, snsd->flow,
737 nas_event_handler, (AuPointer)aj);
739 /* rewind the stream */
740 media_stream_meth(ms, rewind)(mss);
742 /* play chunks of the stream */
743 SXE_MUTEX_LOCK(&aj->mtx);
744 if (aj->buffer_alloc_size < SOUND_MAX_AUDIO_FRAME_SIZE) {
746 aj->buffer = xmalloc_atomic(SOUND_MAX_AUDIO_FRAME_SIZE);
747 aj->buffer_alloc_size = SOUND_MAX_AUDIO_FRAME_SIZE;
749 SXE_MUTEX_UNLOCK(&aj->mtx);
750 snsd->mtp = MTPSTATE_STOP;
752 audio_job_device_data(aj) = snsd;
754 /* prefill the buffer */
755 NAS_DEBUG_S("prefill the buffer: %d\n", 4*bsize);
756 if (!nas_fill(aj, 4*bsize))
759 while (aj->play_state != MTPSTATE_STOP) {
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] =
769 aep.flow = snd->flow;
771 aep.num_parameters = AuParmsMultiplyConstant;
773 AuSetElementParameters(snd->aud, 1, &aep, &as);
774 if (as != AuSuccess) {
775 NAS_DEBUG_S("Setting volume failed.\n");
781 /* events for me audio-job? */
782 if (audio_job_queue(aj)) {
783 sound_nas_handle_aj_events(aj);
787 SXE_MUTEX_LOCK(&aj->mtx);
788 mtp = aj->play_state;
789 SXE_MUTEX_UNLOCK(&aj->mtx);
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;
801 nas_empty_event_queue(snsd);
802 usleep(snsd->resolution);
805 if (snsd->mtp != mtp) {
806 NAS_DEBUG("sleeping for %d\n",
808 AuStopFlow(snd->aud, snsd->flow, &as);
810 usleep(snsd->resolution);
813 case MTPSTATE_UNKNOWN:
815 case NUMBER_OF_MEDIA_THREAD_PLAY_STATES:
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);
828 /* -- Close and shutdown -- */
829 SXE_MUTEX_LOCK(&aj->mtx);
830 if (alloced_myself && aj->buffer) {
834 aj->buffer_alloc_size = 0;
835 SXE_MUTEX_UNLOCK(&aj->mtx);