summaryrefslogtreecommitdiff
path: root/chips/audio.c
diff options
context:
space:
mode:
authorThomas Bushnell <thomas@gnu.org>1997-02-25 21:28:37 +0000
committerThomas Bushnell <thomas@gnu.org>1997-02-25 21:28:37 +0000
commitf07a4c844da9f0ecae5bbee1ab94be56505f26f7 (patch)
tree12b07c7e578fc1a5f53dbfde2632408491ff2a70 /chips/audio.c
Initial source
Diffstat (limited to 'chips/audio.c')
-rw-r--r--chips/audio.c733
1 files changed, 733 insertions, 0 deletions
diff --git a/chips/audio.c b/chips/audio.c
new file mode 100644
index 0000000..00bf2be
--- /dev/null
+++ b/chips/audio.c
@@ -0,0 +1,733 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*-
+ * Copyright (c) 1991, 1992 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the Computer Systems
+ * Engineering Group at Lawrence Berkeley Laboratory.
+ * 4. The name of the Laboratory may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <audio.h>
+#if NAUDIO > 0
+
+#include <mach_kdb.h>
+#include <platforms.h>
+
+#include <mach/std_types.h>
+#include <machine/machspl.h>
+#include <kern/kalloc.h>
+#include <kern/sched_prim.h>
+#include <chips/busses.h>
+
+#include <device/device_types.h>
+#include <device/io_req.h>
+#include <device/ds_routines.h>
+#include <device/audio_status.h> /* user interface */
+#include <chips/audio_defs.h> /* chip interface */
+#include <chips/audio_config.h> /* machdep config */
+
+#define private static
+
+/*
+ * Exported functions and data structures
+ * [see header file for listing]
+ */
+int audio_blocksize = DEFBLKSIZE; /* patchable */
+int audio_backlog = 400; /* 50ms in samples */
+
+/*
+ * Software state, per AMD79C30 audio chip.
+ */
+private
+struct audio_softc {
+ void *hw; /* chip status */
+ audio_switch_t *ops; /* chip operations */
+ au_io_t *sc_au; /* recv and xmit buffers, etc */
+
+
+ unsigned int sc_wseek; /* timestamp of last frame written */
+ unsigned int sc_rseek; /* timestamp of last frame read */
+#if 0
+ struct selinfo sc_wsel; /* write selector */
+ struct selinfo sc_rsel; /* read selector */
+#endif
+
+} audio_softc_data[NAUDIO];
+
+#define unit_to_softc(u) &audio_softc_data[u]
+
+
+/* forward declarations */
+private int audio_sleep (au_cb_t *cb, int thresh);
+private void audio_swintr (struct audio_softc *sc);
+
+/*
+ * Audio chip found.
+ */
+void
+audio_attach(
+ void *hw, /* IN, chip status */
+ audio_switch_t *ops,
+ void **audio_status) /* OUT, audio status */
+{
+ register struct audio_softc *sc;
+ static int next = 0;
+
+ if (next >= NAUDIO)
+ panic("Please configure more than %d audio devices\n", NAUDIO);
+ sc = &audio_softc_data[next++];
+
+ printf(" audio");
+
+ sc->hw = hw;
+ sc->ops = ops;
+
+ *audio_status = (void *)sc;
+}
+
+
+private int audio_setinfo (struct audio_softc *, audio_info_t *);
+private int audio_getinfo (struct audio_softc *, audio_info_t *);
+
+io_return_t
+audio_open(
+ int unit,
+ int mode,
+ io_req_t req)
+{
+ register struct audio_softc *sc;
+ register au_io_t *au;
+
+ sc = unit_to_softc(unit);
+ if (unit > NAUDIO || (!sc->hw))
+ return (D_NO_SUCH_DEVICE);
+
+ if (!sc->sc_au) {
+ sc->sc_au = (au_io_t *) kalloc(sizeof(au_io_t));
+ bzero(sc->sc_au, sizeof(au_io_t));
+ }
+ au = sc->sc_au;
+
+ au->au_lowat = audio_blocksize;
+ au->au_hiwat = AUCB_SIZE - au->au_lowat;
+ au->au_blksize = audio_blocksize;
+ au->au_backlog = audio_backlog;
+
+ /* set up read and write blocks and `dead sound' zero value. */
+ AUCB_INIT(&au->au_rb);
+ au->au_rb.cb_thresh = AUCB_SIZE;
+ AUCB_INIT(&au->au_wb);
+ au->au_wb.cb_thresh = -1;
+
+ /* nothing read or written yet */
+ sc->sc_rseek = 0;
+ sc->sc_wseek = 0;
+
+ (*sc->ops->init)(sc->hw);
+
+ return (0);
+}
+
+private int
+audio_drain(
+ register au_io_t *au)
+{
+ register int error;
+
+ while (!AUCB_EMPTY(&au->au_wb))
+ if ((error = audio_sleep(&au->au_wb, 0)) != 0)
+ return (error);
+ return (0);
+}
+
+/*
+ * Close an audio chip.
+ */
+/* ARGSUSED */
+io_return_t
+audio_close(
+ int unit)
+{
+ register struct audio_softc *sc = unit_to_softc(unit);
+ register au_cb_t *cb;
+ register spl_t s;
+
+ /*
+ * Block until output drains, but allow ^C interrupt.
+ */
+ sc->sc_au->au_lowat = 0; /* avoid excessive wakeups */
+
+ /*
+ * If there is pending output, let it drain (unless
+ * the output is paused).
+ */
+ cb = &sc->sc_au->au_wb;
+ s = splaudio();
+ if (!AUCB_EMPTY(cb) && !cb->cb_pause)
+ (void)audio_drain(sc->sc_au);
+ /*
+ * Disable interrupts, and done.
+ */
+ (*sc->ops->close)(sc->hw);
+ splx(s);
+ return (D_SUCCESS);
+}
+
+private int
+audio_sleep(
+ register au_cb_t *cb,
+ register int thresh)
+{
+ register spl_t s = splaudio();
+
+ cb->cb_thresh = thresh;
+ assert_wait((event_t)cb, TRUE);
+ splx(s);
+ thread_block((void (*)()) 0);
+ return (0); /* XXXX */
+}
+
+io_return_t
+audio_read(
+ int unit,
+ io_req_t ior)
+{
+ register struct audio_softc *sc = unit_to_softc(unit);
+ register au_cb_t *cb;
+ register int n, head, taildata;
+ register int blocksize = sc->sc_au->au_blksize;
+ io_return_t rc;
+ unsigned char *data;
+
+ /*
+ * Allocate read buffer
+ */
+ rc = device_read_alloc(ior, (vm_size_t)ior->io_count);
+ if (rc != KERN_SUCCESS)
+ return rc;
+ data = (unsigned char *) ior->io_data;
+ ior->io_residual = ior->io_count;
+
+ cb = &sc->sc_au->au_rb;
+ cb->cb_drops = 0;
+ sc->sc_rseek = sc->sc_au->au_stamp - AUCB_LEN(cb);
+ do {
+ while (AUCB_LEN(cb) < blocksize) {
+
+ if (ior->io_mode & D_NODELAY)
+ return (D_WOULD_BLOCK);
+
+ if ((rc = audio_sleep(cb, blocksize)) != 0)
+ return(rc);
+ }
+ /*
+ * The space calculation can only err on the short
+ * side if an interrupt occurs during processing:
+ * only cb_tail is altered in the interrupt code.
+ */
+ head = cb->cb_head;
+ if ((n = AUCB_LEN(cb)) > ior->io_residual)
+ n = ior->io_residual;
+ taildata = AUCB_SIZE - head;
+
+ if (n > taildata) {
+ bcopy(cb->cb_data + head, data, taildata);
+ bcopy(cb->cb_data, data + taildata, n - taildata);
+ } else
+ bcopy(cb->cb_data + head, data, n);
+ data += n;
+ ior->io_residual -= n;
+
+ head = AUCB_MOD(head + n);
+ cb->cb_head = head;
+ } while (ior->io_residual >= blocksize);
+
+ return (rc);
+}
+
+io_return_t
+audio_write(
+ int unit,
+ io_req_t ior)
+{
+ register struct audio_softc *sc = unit_to_softc(unit);
+ register au_io_t *au = sc->sc_au;
+ register au_cb_t *cb = &au->au_wb;
+ register int n, tail, tailspace, first, watermark;
+ io_return_t rc;
+ unsigned char *data;
+ vm_offset_t addr = 0;
+
+ if (!(ior->io_op & IO_INBAND)) {
+ /*
+ * Copy out-of-line data into kernel address space.
+ * Since data is copied as page list, it will be
+ * accessible.
+ */
+ vm_map_copy_t copy = (vm_map_copy_t) ior->io_data;
+ kern_return_t kr;
+
+ kr = vm_map_copyout(device_io_map, &addr, copy);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ data = (unsigned char *) addr;
+ } else
+ data = (unsigned char *) ior->io_data;
+ ior->io_residual = ior->io_count;
+
+ rc = D_SUCCESS;
+ first = 1;
+ while (ior->io_residual > 0) {
+ watermark = au->au_hiwat;
+ while (AUCB_LEN(cb) > watermark) {
+
+ if (ior->io_mode & D_NODELAY) {
+ rc = D_WOULD_BLOCK;
+ goto out;
+ }
+
+ if ((rc = audio_sleep(cb, watermark)) != 0)
+ goto out;
+
+ watermark = au->au_lowat;
+ }
+ /*
+ * The only value that can change on an interrupt is
+ * cb->cb_head. We only pull that out once to decide
+ * how much to write into cb_data; if we lose a race
+ * and cb_head changes, we will merely be overly
+ * conservative. For a legitimate time stamp,
+ * however, we need to synchronize the accesses to
+ * au_stamp and cb_head at a high ipl below.
+ */
+ tail = cb->cb_tail;
+ if ((n = (AUCB_SIZE - 1) - AUCB_LEN(cb)) > ior->io_residual) {
+ n = ior->io_residual;
+ if (cb->cb_head == tail &&
+ n <= au->au_blksize &&
+ au->au_stamp - sc->sc_wseek > 400) {
+ /*
+ * the write is 'small', the buffer is empty
+ * and we have been silent for at least 50ms
+ * so we might be dealing with an application
+ * that writes frames synchronously with
+ * reading them. If so, we need an output
+ * backlog to cover scheduling delays or
+ * there will be gaps in the sound output.
+ * Also take this opportunity to reset the
+ * buffer pointers in case we ended up on
+ * a bad boundary (odd byte, blksize bytes
+ * from end, etc.).
+ */
+ register unsigned long *ip;
+ register unsigned long muzero;
+ spl_t s;
+ register int i;
+
+ s = splaudio();
+ cb->cb_head = cb->cb_tail = 0;
+ splx(s);
+
+ tail = au->au_backlog;
+ ip = (unsigned long *)cb->cb_data;
+ muzero = sample_rpt_long(0x7fL);
+ for (i = tail / sizeof muzero; --i >= 0; )
+ *ip++ = muzero;
+ }
+ }
+ tailspace = AUCB_SIZE - tail;
+ if (n > tailspace) {
+ /* write first part at tail and rest at head */
+ bcopy(data, cb->cb_data + tail, tailspace);
+ bcopy(data + tailspace, cb->cb_data,
+ n - tailspace);
+ } else
+ bcopy(data, cb->cb_data + tail, n);
+ data += n;
+ ior->io_residual -= n;
+
+ tail = AUCB_MOD(tail + n);
+ if (first) {
+ register spl_t s = splaudio();
+ sc->sc_wseek = AUCB_LEN(cb) + au->au_stamp + 1;
+ /*
+ * To guarantee that a write is contiguous in the
+ * sample space, we clear the drop count the first
+ * time through. If we later get drops, we will
+ * break out of the loop below, before writing
+ * a new frame.
+ */
+ cb->cb_drops = 0;
+ cb->cb_tail = tail;
+ splx(s);
+ first = 0;
+ } else {
+#if 0
+ if (cb->cb_drops != 0)
+ break;
+#endif
+ cb->cb_tail = tail;
+ }
+ }
+out:
+ if (!(ior->io_op & IO_INBAND))
+ (void) vm_deallocate(device_io_map, addr, ior->io_count);
+ return (rc);
+}
+
+#include <sys/ioctl.h>
+
+io_return_t
+audio_get_status(
+ int unit,
+ dev_flavor_t flavor,
+ dev_status_t status,
+ natural_t *status_count)
+{
+ register struct audio_softc *sc = unit_to_softc(unit);
+ register au_io_t *au = sc->sc_au;
+ io_return_t rc = D_SUCCESS;
+ spl_t s;
+
+ switch (flavor) {
+
+ case AUDIO_GETMAP:
+ case AUDIOGETREG:
+ rc = (*sc->ops->getstate)(sc->hw, flavor,
+ (void *)status, status_count);
+ break;
+
+ /*
+ * Number of read samples dropped. We don't know where or
+ * when they were dropped.
+ */
+ case AUDIO_RERROR:
+ *(int *)status = au->au_rb.cb_drops;
+ *status_count = 1;
+ break;
+
+ case AUDIO_WERROR:
+ *(int *)status = au->au_wb.cb_drops;
+ *status_count = 1;
+ break;
+
+ /*
+ * How many samples will elapse until mike hears the first
+ * sample of what we last wrote?
+ */
+ case AUDIO_WSEEK:
+ s = splaudio();
+ *(unsigned int *)status = sc->sc_wseek - au->au_stamp
+ + AUCB_LEN(&au->au_rb);
+ splx(s);
+ *status_count = 1;
+ break;
+
+ case AUDIO_GETINFO:
+ rc = audio_getinfo(sc, (audio_info_t *)status);
+ *status_count = sizeof(audio_info_t) / sizeof(int);
+ break;
+
+ default:
+ rc = D_INVALID_OPERATION;
+ break;
+ }
+ return (rc);
+}
+
+io_return_t
+audio_set_status(
+ int unit,
+ dev_flavor_t flavor,
+ dev_status_t status,
+ natural_t status_count)
+{
+ register struct audio_softc *sc = unit_to_softc(unit);
+ register au_io_t *au = sc->sc_au;
+ io_return_t rc = D_SUCCESS;
+ spl_t s;
+
+ switch (flavor) {
+
+ case AUDIO_SETMAP:
+ case AUDIOSETREG:
+ rc = (*sc->ops->setstate)(sc->hw, flavor,
+ (void *)status, status_count);
+ break;
+
+ case AUDIO_FLUSH:
+ s = splaudio();
+ AUCB_INIT(&au->au_rb);
+ AUCB_INIT(&au->au_wb);
+ au->au_stamp = 0;
+ splx(s);
+ sc->sc_wseek = 0;
+ sc->sc_rseek = 0;
+ break;
+
+ case AUDIO_SETINFO:
+ rc = audio_setinfo(sc, (audio_info_t *)status);
+ break;
+
+ case AUDIO_DRAIN:
+ rc = audio_drain(au);
+ break;
+
+ default:
+ rc = D_INVALID_OPERATION;
+ break;
+ }
+ return (rc);
+}
+
+
+/*
+ * Interrupt routine
+ */
+boolean_t
+audio_hwintr(
+ void *status,
+ unsigned int s_in,
+ unsigned int *s_out)
+{
+ register au_io_t *au = ((struct audio_softc *) status)->sc_au;
+ register au_cb_t *cb;
+ register int h, t, k;
+ register boolean_t wakeit = FALSE;
+
+ ++au->au_stamp;
+
+ /* receive incoming data */
+ cb = &au->au_rb;
+ h = cb->cb_head;
+ t = cb->cb_tail;
+ k = AUCB_MOD(t + 1);
+ if (h == k)
+ cb->cb_drops++;
+ else if (cb->cb_pause != 0)
+ cb->cb_pdrops++;
+ else {
+ cb->cb_data[t] = s_in;
+ cb->cb_tail = t = k;
+ }
+ if (AUCB_MOD(t - h) >= cb->cb_thresh) {
+ cb->cb_thresh = AUCB_SIZE;
+ cb->cb_waking = 1;
+ wakeit = TRUE;
+ }
+ /* send outgoing data */
+ cb = &au->au_wb;
+ h = cb->cb_head;
+ t = cb->cb_tail;
+ k = 0;
+ if (h == t)
+ cb->cb_drops++;
+ else if (cb->cb_pause != 0)
+ cb->cb_pdrops++;
+ else {
+ cb->cb_head = h = AUCB_MOD(h + 1);
+ *s_out = cb->cb_data[h];
+ k = 1;
+ }
+ if (AUCB_MOD(t - h) <= cb->cb_thresh) {
+ cb->cb_thresh = -1;
+ cb->cb_waking = 1;
+ wakeit = TRUE;
+ }
+ if (wakeit)
+ audio_swintr((struct audio_softc *) status);
+ return (k == 1);
+}
+
+private void
+audio_swintr(
+ register struct audio_softc *sc)
+{
+ register au_io_t *au = sc->sc_au;
+
+ if (au->au_rb.cb_waking != 0) {
+ au->au_rb.cb_waking = 0;
+ wakeup(&au->au_rb);
+ }
+ if (au->au_wb.cb_waking != 0) {
+ au->au_wb.cb_waking = 0;
+ wakeup(&au->au_wb);
+ }
+}
+
+private int
+audio_setinfo(
+ struct audio_softc *sc,
+ audio_info_t *ai)
+{
+ struct audio_prinfo *r = &ai->record, *p = &ai->play;
+ register int bsize;
+ register au_io_t *au = sc->sc_au;
+ spl_t s;
+
+ (*sc->ops->setgains)(sc->hw, p->gain, r->gain, ai->monitor_gain );
+
+ if (p->pause != (unsigned char)~0)
+ au->au_wb.cb_pause = p->pause;
+ if (r->pause != (unsigned char)~0)
+ au->au_rb.cb_pause = r->pause;
+
+ if (p->port != ~0)
+ (*sc->ops->setport)(sc->hw, p->port);
+
+ if (ai->blocksize != ~0) {
+ if (ai->blocksize == 0)
+ bsize = ai->blocksize = DEFBLKSIZE;
+ else if (ai->blocksize > MAXBLKSIZE)
+ bsize = ai->blocksize = MAXBLKSIZE;
+ else
+ bsize = ai->blocksize;
+
+ s = splaudio();
+ au->au_blksize = bsize;
+ /* AUDIO_FLUSH */
+ AUCB_INIT(&au->au_rb);
+ AUCB_INIT(&au->au_wb);
+ splx(s);
+
+ }
+ if (ai->hiwat != ~0 && (unsigned)ai->hiwat < AUCB_SIZE)
+ au->au_hiwat = ai->hiwat;
+ if (ai->lowat != ~0 && ai->lowat < AUCB_SIZE)
+ au->au_lowat = ai->lowat;
+ if (ai->backlog != ~0 && ai->backlog < (AUCB_SIZE/2))
+ au->au_backlog = ai->backlog;
+
+ return (0);
+}
+
+private int
+audio_getinfo(
+ struct audio_softc *sc,
+ audio_info_t *ai)
+{
+ struct audio_prinfo *r = &ai->record, *p = &ai->play;
+ register au_io_t *au = sc->sc_au;
+
+ p->sample_rate = r->sample_rate = 8000;
+ p->channels = r->channels = 1;
+ p->precision = r->precision = 8;
+ p->encoding = r->encoding = AUDIO_ENCODING_ULAW;
+
+ (*sc->ops->getgains)(sc->hw, &p->gain, &r->gain, &ai->monitor_gain );
+
+ r->port = AUDIO_MIKE;
+ p->port = (*sc->ops->getport)(sc->hw);
+
+ p->pause = au->au_wb.cb_pause;
+ r->pause = au->au_rb.cb_pause;
+ p->error = au->au_wb.cb_drops != 0;
+ r->error = au->au_rb.cb_drops != 0;
+
+ /* Now this is funny. If you got here it means you must have
+ opened the device, so how could it possibly be closed ?
+ Unless we upgrade the berkeley code to check if the chip
+ is currently playing and/or recording... Later. */
+ p->open = TRUE;
+ r->open = TRUE;
+
+ p->samples = au->au_stamp - au->au_wb.cb_pdrops;
+ r->samples = au->au_stamp - au->au_rb.cb_pdrops;
+
+ p->seek = sc->sc_wseek;
+ r->seek = sc->sc_rseek;
+
+ ai->blocksize = au->au_blksize;
+ ai->hiwat = au->au_hiwat;
+ ai->lowat = au->au_lowat;
+ ai->backlog = au->au_backlog;
+
+ return (0);
+}
+
+#if MACH_KDB
+#include <ddb/db_output.h>
+
+void audio_queue_status( au_cb_t *cb, char *logo)
+{
+ db_printf("%s ring status:\n", logo);
+ db_printf(" h %x t %x sh %x w %d p %d d %x pd %x\n",
+ cb->cb_head, cb->cb_tail, cb->cb_thresh,
+ cb->cb_waking, cb->cb_pause, (long)cb->cb_drops,
+ (long)cb->cb_pdrops);
+}
+
+int audio_status(int unit)
+{
+ struct audio_softc *sc = unit_to_softc(unit);
+ au_io_t *au;
+
+ if (!sc) {
+ db_printf("No such thing\n");
+ return 0;
+ }
+ db_printf("@%lx: wseek %d rseek %d, au @%lx\n",
+ sc, sc->sc_wseek, sc->sc_rseek, sc->sc_au);
+ if (!(au = sc->sc_au)) return 0;
+
+ db_printf("au: stamp %x lo %x hi %x blk %x blg %x\n",
+ au->au_stamp, au->au_lowat, au->au_hiwat,
+ au->au_blksize, au->au_backlog);
+ audio_queue_status(&au->au_rb, "read");
+ audio_queue_status(&au->au_wb, "write");
+
+ return 0;
+}
+#endif /* MACH_KDB */
+
+#endif /* NAUDIO > 0 */
+