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 write_fmt_str(pcfun, "0x%x", (unsigned int)snd->aud);
187 nas_find_device(AuServer *aud, int channels)
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);
201 nas_setup_defaults(char **server, int *cnt)
206 /* considering some defaults */
207 NAS_DEBUG("trying to find some defaults\n");
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)))
215 DEVICE_CONNECTION(XDEVICE(tmp)));
217 /* tbd: check for conn of the initial frame */
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)
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)
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)
244 server[(*cnt)++] = "localhost:0.0";
246 /* finally we try NULL, too */
247 server[(*cnt)++] = NULL;
253 nas_try_connection(char *server)
255 AuServer *result = NULL;
256 char *err_message = NULL;
259 NAS_DEBUG_C("trying to contact NAS server: %s\n", server);
260 message(GETTEXT("trying to contact NAS server at %s..."),
262 result = AuOpenServer(server, 0, NULL, 0, NULL, &err_message);
265 NAS_DEBUG_C("cannot contact NAS server: %s\n",
266 (err_message ? err_message : ":("));
272 static ad_device_data *
273 sound_nas_create(Lisp_Object nas_options)
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;
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);
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++] =
296 DEVICE_CONNECTION(XDEVICE(opt_server)));
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])))
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 */
312 /* -- initialise -- */
313 snd = xnew_and_zero(sound_nas_data_t);
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);
323 NAS_DEBUG_C("created: 0x%x\n", (unsigned int)snd);
329 sound_nas_finish(ad_device_data *data)
331 sound_nas_data_t *snd = data;
333 NAS_DEBUG("finishing NAS\n");
336 NAS_DEBUG("closing 0x%x\n", (unsigned int)snd->aud);
337 AuCloseServer(snd->aud);
341 NAS_DEBUG("audio-device finished.\n");
347 #define snsd_t sound_nas_aj_data_t
349 nas_fill(audio_job_t aj, int nframes)
351 size_t len = 0, tmplen = 0;
352 sxe_media_sample_t *tmpbuf;
353 snsd_t *snsd = audio_job_device_data(aj);
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,
361 /* set up the volume args */
362 snsd->volargs->volume[0] = snsd->volargs->volume[1] =
364 /* set up the rerate args */
365 snsd->rrargs->tweak = aj->ratetrafo;
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);
376 /* bring back to S16 or U8 */
377 MEDIA_SAMPLE_FORMAT_DOWNSAMPLE(snsd->msf)(
378 aj->buffer + snsd->writepos,
379 aj->buffer + snsd->writepos,
382 /* convert tmplen (#samples) to number of bytes */
383 tmplen = tmplen * snsd->framesize/snsd->channels;
384 snsd->overfill += tmplen;
385 snsd->writepos += tmplen;
387 /* increase underrun counter to detect the end */
395 nas_read(audio_job_t aj, size_t nbytes)
399 snsd_t *snsd = audio_job_device_data(aj);
401 NAS_DEBUG_EV("want to read: %d\n", nbytes);
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");
409 NAS_DEBUG_S("resetting write position.\n");
411 aj->buffer+snsd->readpos,
412 snsd->writepos - snsd->readpos);
413 snsd->writepos -= snsd->readpos;
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);
423 /* care for buffer underruns */
424 if (nbytes > snsd->overfill)
425 nbytes = snsd->overfill;
428 * Now write the new buffer to the network.
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);
434 NAS_DEBUG_C("nas_read(): AuWriteElement\n");
435 snsd->readpos += nbytes;
436 snsd->overfill -= nbytes;
438 /* detect end of track */
439 if (snsd->underrun >= 4 && snsd->overfill < 64)
440 aj->play_state = MTPSTATE_STOP;
446 nas_event_handler(AuServer *aud, AuEvent *ev, AuEventHandlerRec *hnd)
448 AuElementNotifyEvent *event = (AuElementNotifyEvent*)ev;
449 audio_job_t aj = hnd->data;
450 snsd_t *snsd = audio_job_device_data(aj);
452 NAS_DEBUG_EV("event_handler(): "
453 "type %s kind %s state %s->%s reason %s "
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);
462 if (event->num_bytes > INT_MAX) {
463 NAS_CRITICAL("num_bytes > 2GB, server buggy?\n");
466 switch (event->reason) {
467 case AuReasonWatermark:
468 nas_read(aj, event->num_bytes);
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");
477 NAS_DEBUG_S("Can't refill buffer, stopping flow.\n");
478 AuStopFlow(aud, snsd->flow, NULL);
479 aj->play_state = MTPSTATE_STOP;
489 nas_error_handler(AuServer* aud, AuErrorEvent* ev)
492 AuGetErrorText(aud, ev->error_code, s, 100);
493 NAS_CRITICAL("error [%s]\n"
497 s, ev->error_code, ev->request_code, ev->minor_code);
499 LONGJMP(nas_server_sig, 1);
504 nas_IOerror_handler(AuServer *aud)
506 NAS_CRITICAL("Strange things happened.\n");
508 NAS_CRITICAL("Connection seems to be broken.\n");
510 NAS_CRITICAL("Server raised SIGPIPE.\n");
513 LONGJMP(nas_server_sig, 1);
518 nas_empty_event_queue(snsd_t *snsd)
523 while (AuScanForTypedEvent(
524 snsd->snd->aud, AuEventsQueuedAfterFlush,
525 AuTrue, AuEventTypeElementNotify, &ev)) {
526 AuDispatchEvent(snsd->snd->aud, &ev);
534 sound_nas_change_volume(audio_job_t aj, audio_job_event_args_t args)
536 SXE_MUTEX_LOCK(&aj->mtx);
537 aj->volume = args->volume_args;
538 SXE_MUTEX_UNLOCK(&aj->mtx);
542 sound_nas_change_rate(audio_job_t aj, audio_job_event_args_t args)
544 SXE_MUTEX_LOCK(&aj->mtx);
545 aj->ratetrafo = args->rate_args;
546 SXE_MUTEX_UNLOCK(&aj->mtx);
550 sound_nas_change_state(audio_job_t aj, audio_job_event_args_t args)
552 SXE_MUTEX_LOCK(&aj->mtx);
553 switch (args->state_args) {
555 NAS_DEBUG_AJ("->pause state\n");
556 aj->play_state = MTPSTATE_PAUSE;
559 NAS_DEBUG_AJ("->resume state\n");
560 aj->play_state = MTPSTATE_RUN;
563 NAS_DEBUG_AJ("->start state\n");
566 NAS_DEBUG_AJ("->stop state\n");
567 aj->play_state = MTPSTATE_STOP;
569 case no_audio_job_change_states:
571 NAS_DEBUG_AJ("->unknown state\n");
574 SXE_MUTEX_UNLOCK(&aj->mtx);
578 sound_nas_handle_aj_events(audio_job_t aj)
579 __attribute__((always_inline));
581 sound_nas_handle_aj_events(audio_job_t aj)
583 sound_nas_aj_data_t *sasd;
584 audio_job_event_t ev = NULL;
587 assert(audio_job_queue(aj));
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);
596 SXE_MUTEX_UNLOCK(&aj->mtx);
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));
604 case aj_change_volume:
605 NAS_DEBUG_AJ("change volume event\n");
606 sound_nas_change_volume(aj, &audio_job_event_args(ev));
609 NAS_DEBUG_AJ("change rate event\n");
610 sound_nas_change_rate(aj, &audio_job_event_args(ev));
612 case no_audio_job_event_kinds:
614 NAS_CRITICAL("unknown event\n");
617 free_audio_job_event(ev);
619 #endif /* EF_USE_ASYNEQ */
622 sound_nas_play(audio_job_t aj)
625 Lisp_Media_Stream *ms;
626 media_substream *mss;
628 media_thread_play_state mtp;
631 Lisp_Audio_Device *lad = NULL;
632 sound_nas_data_t *snd = NULL;
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;
642 int alloced_myself = 0;
644 SOUND_UNPACK_MT(aj, device, ms, mss, lad, snd, snsd->mtap);
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");
652 /* install error handlers before anything else */
653 AuSetErrorHandler(snd->aud, nas_error_handler);
654 AuSetIOErrorHandler(snd->aud, nas_IOerror_handler);
656 /* find physical output device */
657 snsd->dev = nas_find_device(snd->aud, snsd->mtap->channels);
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");
666 /* A system call interrupted with a SIGALRM or SIGIO
668 if (SETJMP(nas_server_sig)) {
669 NAS_CRITICAL("Caught the lethal signal.\n");
671 sound_nas_finish(snd);
676 snsd->samplerate = 0;
677 snsd->framesize = (snsd->channels = snsd->mtap->channels)
679 snsd->msf = MEDIA_SAMPLE_FORMAT(sxe_msf_S16);
680 snsd->coe_ch_cnt = 0;
682 (snsd->mtap->samplerate * MTPSTATE_REACT_TIME) / 1000000;
683 bsize = (snsd->resolution + NAS_FRAG_SIZE - 1) & ~(NAS_FRAG_SIZE-1);
686 snsd->buffer_size = bsize;
687 snsd->writepos = snsd->readpos = 0;
688 snsd->overfill = snsd->underrun = 0;
691 AuMakeElementImportClient(elms+0, snsd->mtap->samplerate,
692 AuFormatLinearSigned16LSB,
693 snsd->mtap->channels,
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);
704 if (as != AuSuccess) {
705 NAS_DEBUG_C("play(): AuSetElements failed\n");
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);
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;
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;
731 AuRegisterEventHandler(snd->aud, AuEventHandlerIDMask |
732 AuEventHandlerTypeMask,
733 AuEventTypeElementNotify, snsd->flow,
734 nas_event_handler, (AuPointer)aj);
736 /* rewind the stream */
737 media_stream_meth(ms, rewind)(mss);
739 /* play chunks of the stream */
740 SXE_MUTEX_LOCK(&aj->mtx);
741 if (aj->buffer_alloc_size < SOUND_MAX_AUDIO_FRAME_SIZE) {
743 aj->buffer = xmalloc_atomic(SOUND_MAX_AUDIO_FRAME_SIZE);
744 aj->buffer_alloc_size = SOUND_MAX_AUDIO_FRAME_SIZE;
746 SXE_MUTEX_UNLOCK(&aj->mtx);
747 snsd->mtp = MTPSTATE_STOP;
749 audio_job_device_data(aj) = snsd;
751 /* prefill the buffer */
752 NAS_DEBUG_S("prefill the buffer: %d\n", 4*bsize);
753 if (!nas_fill(aj, 4*bsize))
756 while (aj->play_state != MTPSTATE_STOP) {
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] =
766 aep.flow = snd->flow;
768 aep.num_parameters = AuParmsMultiplyConstant;
770 AuSetElementParameters(snd->aud, 1, &aep, &as);
771 if (as != AuSuccess) {
772 NAS_DEBUG_S("Setting volume failed.\n");
778 /* events for me audio-job? */
779 if (audio_job_queue(aj)) {
780 sound_nas_handle_aj_events(aj);
784 SXE_MUTEX_LOCK(&aj->mtx);
785 mtp = aj->play_state;
786 SXE_MUTEX_UNLOCK(&aj->mtx);
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;
798 nas_empty_event_queue(snsd);
799 usleep(snsd->resolution);
802 if (snsd->mtp != mtp) {
803 NAS_DEBUG("sleeping for %d\n",
805 AuStopFlow(snd->aud, snsd->flow, &as);
807 usleep(snsd->resolution);
810 case MTPSTATE_UNKNOWN:
812 case NUMBER_OF_MEDIA_THREAD_PLAY_STATES:
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);
825 /* -- Close and shutdown -- */
826 SXE_MUTEX_LOCK(&aj->mtx);
827 if (alloced_myself && aj->buffer) {
831 aj->buffer_alloc_size = 0;
832 SXE_MUTEX_UNLOCK(&aj->mtx);