summaryrefslogtreecommitdiffstats
path: root/kscd/libwm/audio
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commite2de64d6f1beb9e492daf5b886e19933c1fa41dd (patch)
tree9047cf9e6b5c43878d5bf82660adae77ceee097a /kscd/libwm/audio
downloadtdemultimedia-e2de64d6f1beb9e492daf5b886e19933c1fa41dd.tar.gz
tdemultimedia-e2de64d6f1beb9e492daf5b886e19933c1fa41dd.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdemultimedia@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kscd/libwm/audio')
-rw-r--r--kscd/libwm/audio/Makefile.am5
-rw-r--r--kscd/libwm/audio/audio.c29
-rw-r--r--kscd/libwm/audio/audio.h21
-rw-r--r--kscd/libwm/audio/audio_alsa.c334
-rw-r--r--kscd/libwm/audio/audio_arts.c141
-rw-r--r--kscd/libwm/audio/audio_sun.c525
6 files changed, 1055 insertions, 0 deletions
diff --git a/kscd/libwm/audio/Makefile.am b/kscd/libwm/audio/Makefile.am
new file mode 100644
index 00000000..318cb0a3
--- /dev/null
+++ b/kscd/libwm/audio/Makefile.am
@@ -0,0 +1,5 @@
+INCLUDES = $(all_includes) $(ARTSC_INCLUDE)
+
+noinst_LTLIBRARIES = libworkmanaudio.la
+
+libworkmanaudio_la_SOURCES = audio.c audio_alsa.c audio_arts.c audio_sun.c
diff --git a/kscd/libwm/audio/audio.c b/kscd/libwm/audio/audio.c
new file mode 100644
index 00000000..39c8330d
--- /dev/null
+++ b/kscd/libwm/audio/audio.c
@@ -0,0 +1,29 @@
+#include "audio.h"
+
+#include <string.h>
+
+struct audio_oops* setup_arts(const char *dev, const char *ctl);
+struct audio_oops* setup_alsa(const char *dev, const char *ctl);
+
+struct audio_oops* setup_soundsystem(const char* ss, const char* dev, const char* ctl)
+{
+ if(!ss) {
+ ERRORLOG("audio: Internal error, trying to setup a NULL soundsystem.\n");
+ return NULL;
+ }
+
+#ifdef USE_ARTS
+ if(!strcmp(ss, "arts"))
+ return setup_arts(dev, ctl);
+#endif
+#if defined(HAVE_ARTS_LIBASOUND2)
+ if(!strcmp(ss, "alsa"))
+ return setup_alsa(dev, ctl);
+#endif
+#ifdef USE_SUN_AUDIO
+ if(!strcmp(ss, "sun"))
+ return setup_sun_audio(dev, ctl);
+#endif
+ ERRORLOG("audio: unknown soundsystem '%s'\n", ss);
+ return NULL;
+}
diff --git a/kscd/libwm/audio/audio.h b/kscd/libwm/audio/audio.h
new file mode 100644
index 00000000..089ea116
--- /dev/null
+++ b/kscd/libwm/audio/audio.h
@@ -0,0 +1,21 @@
+/*
+ * Audio 'LIB' defines
+ */
+#include "../include/wm_cdda.h"
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+struct audio_oops {
+ int (*wmaudio_open)(void);
+ int (*wmaudio_close)(void);
+ int (*wmaudio_play)(struct cdda_block*);
+ int (*wmaudio_stop)(void);
+ int (*wmaudio_state)(struct cdda_block*);
+ int (*wmaudio_balance)(int);
+ int (*wmaudio_volume)(int);
+};
+
+extern struct audio_oops* setup_soundsystem(const char*, const char*, const char*);
+
diff --git a/kscd/libwm/audio/audio_alsa.c b/kscd/libwm/audio/audio_alsa.c
new file mode 100644
index 00000000..b1d4e938
--- /dev/null
+++ b/kscd/libwm/audio/audio_alsa.c
@@ -0,0 +1,334 @@
+/*
+ * Driver for Advanced Linux Sound Architecture, http://alsa.jcu.cz
+ *
+ * mpg123 comments:
+ * Code by Anders Semb Hermansen <[email protected]>
+ * Cleanups by Jaroslav Kysela <[email protected]>
+ * Ville Syrjala <[email protected]>
+ *
+ * adopted for libworkman cdda audio backend from Alexander Kern [email protected]
+ *
+ * Adapted to support both ALSA V0.x and V1.x APIs for PCM calls
+ * (Philip Nelson <[email protected]> 2004-03-15)
+ *
+ * This file comes under GPL license.
+ */
+
+
+
+#include <config.h>
+
+#if defined(HAVE_ARTS_LIBASOUND2)
+
+#include <alsa/asoundlib.h>
+#include "audio.h"
+
+char* device = NULL;
+snd_pcm_t *handle;
+
+snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */
+int rate = 44100; /* stream rate */
+int channels = 2; /* count of channels */
+int buffer_time = 2000000; /* ring buffer length in us */
+int period_time = 100000; /* period time in us */
+
+snd_pcm_sframes_t buffer_size;
+snd_pcm_sframes_t period_size;
+
+int alsa_open(void);
+int alsa_close(void);
+int alsa_stop(void);
+int alsa_play(struct cdda_block *blk);
+int alsa_state(struct cdda_block *blk);
+struct audio_oops* setup_alsa(const char *dev, const char *ctl);
+
+static int set_hwparams(snd_pcm_hw_params_t *params,
+ snd_pcm_access_t accesspar)
+{
+ int err, dir, new_rate;
+
+ /* choose all parameters */
+ err = snd_pcm_hw_params_any(handle, params);
+ if (err < 0) {
+ ERRORLOG("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* set the interleaved read/write format */
+ err = snd_pcm_hw_params_set_access(handle, params, accesspar);
+ if (err < 0) {
+ ERRORLOG("Access type not available for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* set the sample format */
+ err = snd_pcm_hw_params_set_format(handle, params, format);
+ if (err < 0) {
+ ERRORLOG("Sample format not available for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* set the count of channels */
+ err = snd_pcm_hw_params_set_channels(handle, params, channels);
+ if (err < 0) {
+ ERRORLOG("Channels count (%i) not available for playbacks: %s\n", channels, snd_strerror(err));
+ return err;
+ }
+ /* set the stream rate */
+#if (SND_LIB_MAJOR < 1)
+ err = new_rate = snd_pcm_hw_params_set_rate_near(handle, params, rate, 0);
+#else
+ new_rate = rate;
+ err = snd_pcm_hw_params_set_rate_near(handle, params, &new_rate, 0);
+#endif
+ if (err < 0) {
+ ERRORLOG("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err));
+ return err;
+ }
+ if (new_rate != rate) {
+ ERRORLOG("Rate doesn't match (requested %iHz, get %iHz)\n", rate, new_rate);
+ return -EINVAL;
+ }
+ /* set the buffer time */
+#if (SND_LIB_MAJOR < 1)
+ err = snd_pcm_hw_params_set_buffer_time_near(handle, params, buffer_time, &dir);
+#else
+ err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir);
+#endif
+ if (err < 0) {
+ ERRORLOG("Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err));
+ return err;
+ }
+#if (SND_LIB_MAJOR < 1)
+ buffer_size = snd_pcm_hw_params_get_buffer_size(params);
+#else
+ err = snd_pcm_hw_params_get_buffer_size(params, &buffer_size);
+ if (err < 0) {
+ ERRORLOG("Unable to get buffer size : %s\n", snd_strerror(err));
+ return err;
+ }
+#endif
+ DEBUGLOG("buffersize %i\n", buffer_size);
+
+ /* set the period time */
+#if (SND_LIB_MAJOR < 1)
+ err = snd_pcm_hw_params_set_period_time_near(handle, params, period_time, &dir);
+#else
+ err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir);
+#endif
+ if (err < 0) {
+ ERRORLOG("Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err));
+ return err;
+ }
+
+#if (SND_LIB_MAJOR < 1)
+ period_size = snd_pcm_hw_params_get_period_size(params, &dir);
+#else
+ err = snd_pcm_hw_params_get_period_size(params, &period_size, &dir);
+ if (err < 0) {
+ ERRORLOG("Unable to get hw period size: %s\n", snd_strerror(err));
+ }
+#endif
+
+ DEBUGLOG("period_size %i\n", period_size);
+
+ /* write the parameters to device */
+ err = snd_pcm_hw_params(handle, params);
+ if (err < 0) {
+ ERRORLOG("Unable to set hw params for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ return 0;
+}
+
+static int set_swparams(snd_pcm_sw_params_t *swparams)
+{
+ int err;
+
+ /* get the current swparams */
+ err = snd_pcm_sw_params_current(handle, swparams);
+ if (err < 0) {
+ ERRORLOG("Unable to determine current swparams for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* start the transfer when the buffer is full */
+ err = snd_pcm_sw_params_set_start_threshold(handle, swparams, buffer_size);
+ if (err < 0) {
+ ERRORLOG("Unable to set start threshold mode for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* allow the transfer when at least period_size samples can be processed */
+ err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_size);
+ if (err < 0) {
+ ERRORLOG("Unable to set avail min for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* align all transfers to 1 sample */
+ err = snd_pcm_sw_params_set_xfer_align(handle, swparams, 1);
+ if (err < 0) {
+ ERRORLOG("Unable to set transfer align for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* write the parameters to the playback device */
+ err = snd_pcm_sw_params(handle, swparams);
+ if (err < 0) {
+ ERRORLOG("Unable to set sw params for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ return 0;
+}
+
+int alsa_open( void )
+{
+ int err;
+
+ snd_pcm_hw_params_t *hwparams;
+ snd_pcm_sw_params_t *swparams;
+
+ DEBUGLOG("alsa_open\n");
+
+ snd_pcm_hw_params_alloca(&hwparams);
+ snd_pcm_sw_params_alloca(&swparams);
+
+ if((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0/*SND_PCM_NONBLOCK*/)) < 0 ) {
+ ERRORLOG("open failed: %s\n", snd_strerror(err));
+ return -1;
+ }
+
+ if((err = set_hwparams(hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
+ ERRORLOG("Setting of hwparams failed: %s\n", snd_strerror(err));
+ return -1;
+ }
+ if((err = set_swparams(swparams)) < 0) {
+ ERRORLOG("Setting of swparams failed: %s\n", snd_strerror(err));
+ return -1;
+ }
+
+ return 0;
+}
+
+int alsa_close( void )
+{
+ int err;
+
+ DEBUGLOG("alsa_close\n");
+
+ err = alsa_stop();
+
+#if (SND_LIB_MAJOR < 1)
+ err = snd_pcm_close(handle);
+#else
+ err = snd_pcm_close(handle);
+#endif
+
+ free(device);
+
+ return err;
+}
+
+/*
+ * Play some audio and pass a status message upstream, if applicable.
+ * Returns 0 on success.
+ */
+int
+alsa_play(struct cdda_block *blk)
+{
+ signed short *ptr;
+ int err = 0, frames;
+
+ ptr = (signed short *)blk->buf;
+ frames = blk->buflen / (channels * 2);
+ DEBUGLOG("play %i frames, %i bytes\n", frames, blk->buflen);
+ while (frames > 0) {
+ err = snd_pcm_writei(handle, ptr, frames);
+
+ if (err == -EAGAIN)
+ continue;
+ if(err == -EPIPE) {
+ err = snd_pcm_prepare(handle);
+ continue;
+ } else if (err < 0)
+ break;
+
+ ptr += err * channels;
+ frames -= err;
+ DEBUGLOG("played %i, rest %i\n", err / channels, frames);
+ }
+
+ if (err < 0) {
+ ERRORLOG("alsa_write failed: %s\n", snd_strerror(err));
+ err = snd_pcm_prepare(handle);
+
+ if (err < 0) {
+ ERRORLOG("Unable to snd_pcm_prepare pcm stream: %s\n", snd_strerror(err));
+ }
+ blk->status = WM_CDM_CDDAERROR;
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * Stop the audio immediately.
+ */
+int
+alsa_stop( void )
+{
+ int err;
+
+ DEBUGLOG("alsa_stop\n");
+
+ err = snd_pcm_drop(handle);
+ if (err < 0) {
+ ERRORLOG("Unable to drop pcm stream: %s\n", snd_strerror(err));
+ }
+
+ err = snd_pcm_prepare(handle);
+ if (err < 0) {
+ ERRORLOG("Unable to snd_pcm_prepare pcm stream: %s\n", snd_strerror(err));
+ }
+
+ return err;
+}
+
+/*
+ * Get the current audio state.
+ */
+int
+alsa_state(struct cdda_block *blk)
+{
+ return -1; /* not implemented yet for ALSA */
+}
+
+static struct audio_oops alsa_oops = {
+ .wmaudio_open = alsa_open,
+ .wmaudio_close = alsa_close,
+ .wmaudio_play = alsa_play,
+ .wmaudio_stop = alsa_stop,
+ .wmaudio_state = alsa_state,
+ .wmaudio_balance = NULL,
+ .wmaudio_volume = NULL
+};
+
+struct audio_oops*
+setup_alsa(const char *dev, const char *ctl)
+{
+ static int init_complete = 0;
+
+ if(dev && strlen(dev) > 0) {
+ device = strdup(dev);
+ } else {
+ device = strdup("plughw:0,0"); /* playback device */
+ }
+
+ if(init_complete) {
+ ERRORLOG("already initialized\n");
+ return NULL;
+ }
+ if(!alsa_open())
+ init_complete = 1;
+ else
+ return NULL;
+
+ return &alsa_oops;
+}
+
+#endif /* ALSA */
diff --git a/kscd/libwm/audio/audio_arts.c b/kscd/libwm/audio/audio_arts.c
new file mode 100644
index 00000000..a7b033c9
--- /dev/null
+++ b/kscd/libwm/audio/audio_arts.c
@@ -0,0 +1,141 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Linux digital audio functions.
+ *
+ *
+ * Forget /dev/audio
+ * most modern soundcards accept 16LE with 44.1kHz
+ * Alexander Kern [email protected]
+ */
+
+#include <config.h>
+
+#ifdef USE_ARTS
+
+#include <artsc.h>
+
+#include "audio.h"
+
+arts_stream_t arts_stream = NULL;
+
+int arts_open(void);
+int arts_close(void);
+int arts_stop(void);
+int arts_play(struct cdda_block *blk);
+int arts_state(struct cdda_block *blk);
+struct audio_oops* setup_arts(const char *dev, const char *ctl);
+
+/*
+ * Initialize the audio device.
+ */
+int
+arts_open(void)
+{
+ int err;
+
+ DEBUGLOG("arts_open\n");
+
+ if(!(arts_stream = arts_play_stream(44100, 16, 2, "cddaslave"))) {
+ ERRORLOG("cannot open ARTS stream for playback\n");
+ return -1;
+ }
+ /* 1000 ms because we read 75 frames = 1 sec */
+ if((err = arts_stream_set(arts_stream, ARTS_P_BUFFER_TIME, 1000)) < 0) {
+ ERRORLOG("arts_stream_set failed (%s)\n", arts_error_text(err));
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Close the audio device.
+ */
+int
+arts_close(void)
+{
+ arts_stop();
+
+ DEBUGLOG("arts_close\n");
+ arts_close_stream(arts_stream);
+
+ arts_free();
+
+ return 0;
+}
+
+/*
+ * Play some audio and pass a status message upstream, if applicable.
+ * Returns 0 on success.
+ */
+int
+arts_play(struct cdda_block *blk)
+{
+ int err;
+
+ if((err = arts_write(arts_stream, blk->buf, blk->buflen)) < 0) {
+ ERRORLOG("arts_write failed (%s)\n", arts_error_text(err));
+ blk->status = WM_CDM_CDDAERROR;
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Stop the audio immediately.
+ */
+int
+arts_stop(void)
+{
+ DEBUGLOG("arts_stop\n");
+ return 0;
+}
+
+/*
+ * Get the current audio state.
+ */
+int
+arts_state(struct cdda_block *blk)
+{
+ return -1; /* not implemented yet for ARTS */
+}
+
+static struct audio_oops arts_oops = {
+ .wmaudio_open = arts_open,
+ .wmaudio_close = arts_close,
+ .wmaudio_play = arts_play,
+ .wmaudio_stop = arts_stop,
+ .wmaudio_state = arts_state,
+ .wmaudio_balance = NULL,
+ .wmaudio_volume = NULL
+};
+
+struct audio_oops*
+setup_arts(const char *dev, const char *ctl)
+{
+ int err;
+
+ if((err = arts_init())) {
+ ERRORLOG("cannot initialize ARTS audio subsystem (%s)\n", arts_error_text(err));
+ return NULL;
+ }
+
+ arts_open();
+
+ return &arts_oops;
+}
+#endif
diff --git a/kscd/libwm/audio/audio_sun.c b/kscd/libwm/audio/audio_sun.c
new file mode 100644
index 00000000..78b1ab21
--- /dev/null
+++ b/kscd/libwm/audio/audio_sun.c
@@ -0,0 +1,525 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk F�rsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Sun (really Solaris) digital audio functions.
+ */
+
+#include <config.h>
+
+#ifdef USE_SUN_AUDIO
+
+#include <stdio.h>
+#include <malloc.h>
+#include <sys/ioctl.h>
+#include <sys/audioio.h>
+#include <sys/stropts.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include "audio.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+/*
+ * Since there's a lag time between writing audio to the audio device and
+ * hearing it, we need to make sure the status indicators correlate to what's
+ * playing out the speaker. Luckily, Solaris gives us some audio
+ * synchronization facilities that make this pretty easy.
+ *
+ * We maintain a circular queue of status information. When we write some
+ * sound to the audio device, we put its status info into the queue. We write
+ * a marker into the audio stream; when the audio device driver encounters the
+ * marker, it increments a field in a status structure. When we see that
+ * field go up, we grab the next status structure from the queue and send it
+ * to the parent process.
+ *
+ * The minimum size of the queue depends on the latency of the audio stream.
+ */
+#define QSIZE 500
+
+struct cdda_block queue[QSIZE];
+int qtail;
+int qstart;
+
+/*
+ * We only send WM_CDM_PLAYING status messages upstream when the CD is supposed
+ * to be playing; this is used to keep track.
+ */
+extern int playing;
+
+static int aufd, aucfd;
+static int raw_audio = 1; /* Can /dev/audio take 44.1KHz stereo? */
+
+/*
+ * For fast linear-to-ulaw mapping, we use a lookup table that's generated
+ * at startup.
+ */
+unsigned char *ulawmap, linear_to_ulaw();
+
+char *getenv();
+
+/*
+ * Dummy signal handler so writes to /dev/audio will interrupt.
+ */
+static void
+dummy( void )
+{
+ signal(SIGALRM, dummy);
+}
+
+/*
+ * Initialize the audio device.
+ */
+void
+sun_audio_init( void )
+{
+ audio_info_t info;
+ char *audiodev, *acdev;
+ int linval;
+
+ audiodev = getenv("AUDIODEV");
+ if (audiodev == NULL ||
+ strncmp("/dev/", audiodev, 5) ||
+ strstr(audiodev, "/../") )
+ audiodev = "/dev/audio";
+
+ acdev = malloc(strlen(audiodev) + 4);
+ if (acdev == NULL)
+ {
+ perror("Can't allocate audio control filename");
+ exit(1);
+ }
+ strcpy(acdev, audiodev);
+ strcat(acdev, "ctl");
+
+ aucfd = open(acdev, O_WRONLY, 0);
+ if (aucfd < 0)
+ {
+ perror(acdev);
+ exit(1);
+ }
+ free(acdev);
+
+ aufd = open(audiodev, O_WRONLY, 0);
+ if (aufd < 0)
+ {
+ perror(audiodev);
+ exit(1);
+ }
+
+ signal(SIGALRM, dummy);
+
+ /*
+ * Try to set the device to CD-style audio; we can process it
+ * with the least CPU overhead.
+ */
+ AUDIO_INITINFO(&info);
+ info.play.sample_rate = 44100;
+ info.play.channels = 2;
+ info.play.precision = 16;
+ info.play.encoding = AUDIO_ENCODING_LINEAR;
+ info.play.pause = 0;
+ info.record.pause = 0;
+ info.monitor_gain = 0;
+
+ if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
+ if (errno == EINVAL)
+ {
+ /*
+ * Oh well, so much for that idea.
+ */
+ AUDIO_INITINFO(&info);
+ info.play.sample_rate = 8000;
+ info.play.channels = 1;
+ info.play.precision = 8;
+ info.play.encoding = AUDIO_ENCODING_ULAW;
+ info.play.pause = 0;
+ info.record.pause = 0;
+ info.monitor_gain = 0;
+ if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
+ {
+ perror("Can't set up audio device");
+ exit(1);
+ }
+
+ /*
+ * Initialize the linear-to-ulaw mapping table.
+ */
+ if (ulawmap == NULL)
+ ulawmap = malloc(65536);
+ if (ulawmap == NULL)
+ {
+ perror("malloc");
+ exit(1);
+ }
+ for (linval = 0; linval < 65536; linval++)
+ ulawmap[linval] = linear_to_ulaw(linval-32768);
+ ulawmap += 32768;
+ raw_audio = 0;
+ }
+ else
+ {
+ perror(audiodev);
+ exit(1);
+ }
+}
+
+/*
+ * Get ready to play some sound.
+ */
+void
+sun_audio_ready( void )
+{
+ audio_info_t info;
+
+ /*
+ * Start at the correct queue position.
+ */
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ qtail = info.play.eof % QSIZE;
+ qstart = qtail;
+
+ queue[qtail].status = WM_CDM_PLAYING;
+}
+
+/*
+ * Stop the audio immediately.
+ */
+int
+sun_audio_stop( void )
+{
+ if (ioctl(aufd, I_FLUSH, FLUSHRW) < 0)
+ perror("flush");
+ return 0;
+}
+
+/*
+ * Close the audio device.
+ */
+int
+sun_audio_close( void )
+{
+ wmaudio_stop();
+ close(aufd);
+ close(aucfd);
+ return 0;
+}
+
+/*
+ * Set the volume level.
+ */
+int
+sun_audio_volume(int level)
+{
+ audio_info_t info;
+
+ AUDIO_INITINFO(&info);
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ info.play.gain = level;
+ if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) {
+ perror("AUDIO_SETINFO");
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Set the balance level.
+ */
+int
+sun_audio_balance(int level)
+{
+ audio_info_t info;
+
+ AUDIO_INITINFO(&info);
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ level *= AUDIO_RIGHT_BALANCE;
+ info.play.balance = level / 255;
+ if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) {
+ perror("AUDIO_SETINFO");
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Mark the most recent audio block on the queue as the last one.
+ */
+void
+sun_audio_mark_last( void )
+{
+ queue[qtail].status = WM_CDM_TRACK_DONE;
+}
+
+/*
+ * Figure out the most recent status information and send it upstream.
+ */
+int
+sun_audio_send_status( void )
+{
+ audio_info_t info;
+ int qhead;
+
+ /*
+ * Now send the most current status information to our parent.
+ */
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
+ perror("AUDIO_GETINFO");
+ qhead = info.play.eof % QSIZE;
+
+ if (qhead != qstart && playing)
+ {
+ int balance;
+
+ if (queue[qhead].status != WM_CDM_TRACK_DONE)
+ queue[qhead].status = WM_CDM_PLAYING;
+ queue[qhead].volume = info.play.gain;
+ queue[qhead].balance = (info.play.balance * 255) /
+ AUDIO_RIGHT_BALANCE;
+
+ send_status(queue + qhead);
+ qstart = -1;
+ }
+
+ return (queue[qhead].status == WM_CDM_TRACK_DONE);
+}
+
+/*
+ * Play some audio and pass a status message upstream, if applicable.
+ * Returns 0 on success.
+ */
+int
+sun_audio_play(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
+{
+ int i;
+ short *buf16;
+ int alarmcount = 0;
+ struct itimerval it;
+ long playablelen;
+
+ alarm(1);
+ playablelen = dev_audio_convert(rawbuf, buflen, blk);
+ while (write(aufd, rawbuf, playablelen) <= 0)
+ if (errno == EINTR)
+ {
+ if (! raw_audio && alarmcount++ < 5)
+ {
+ /*
+ * 8KHz /dev/audio blocks for several seconds
+ * waiting for its queue to drop below a low
+ * water mark.
+ */
+ wmaudio_send_status();
+ timerclear(&it.it_interval);
+ timerclear(&it.it_value);
+ it.it_value.tv_usec = 500000;
+ setitimer(ITIMER_REAL, &it, NULL);
+ continue;
+ }
+
+/* close(aufd);
+ close(aucfd);
+ wmaudio_init();
+*/ sun_audio_stop();
+ alarm(2);
+ continue;
+ }
+ else
+ {
+ blk->status = WM_CDM_CDDAERROR;
+ return (-1);
+ }
+ alarm(0);
+
+ /*
+ * Mark this spot in the audio stream.
+ *
+ * Marks don't always succeed (if the audio buffer is empty
+ * this call will block forever) so do it asynchronously.
+ */
+ fcntl(aufd, F_SETFL, O_NONBLOCK);
+ if (write(aufd, rawbuf, 0) < 0)
+ {
+ if (errno != EAGAIN)
+ perror("audio mark");
+ }
+ else
+ qtail = (qtail + 1) % QSIZE;
+
+ fcntl(aufd, F_SETFL, 0);
+
+ queue[qtail] = *blk;
+
+ if (wmaudio_send_status() < 0)
+ return (-1);
+ else
+ return (0);
+}
+
+/*
+ * Get the current audio state.
+ */
+int
+sun_audio_state(struct cdda_block *blk)
+{
+ audio_info_t info;
+ int balance;
+
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
+ perror("AUDIO_GETINFO");
+ blk->volume = info.play.gain;
+ blk->balance = (info.play.balance * 255) / AUDIO_RIGHT_BALANCE;
+ return 0;
+}
+
+/*
+** This routine converts from linear to ulaw.
+**
+** Craig Reese: IDA/Supercomputing Research Center
+** Joe Campbell: Department of Defense
+** 29 September 1989
+**
+** References:
+** 1) CCITT Recommendation G.711 (very difficult to follow)
+** 2) "A New Digital Technique for Implementation of Any
+** Continuous PCM Companding Law," Villeret, Michel,
+** et al. 1973 IEEE Int. Conf. on Communications, Vol 1,
+** 1973, pg. 11.12-11.17
+** 3) MIL-STD-188-113,"Interoperability and Performance Standards
+** for Analog-to_Digital Conversion Techniques,"
+** 17 February 1987
+**
+** Input: Signed 16 bit linear sample
+** Output: 8 bit ulaw sample
+*/
+#define ZEROTRAP /* turn on the trap as per the MIL-STD */
+#define BIAS 0x84 /* define the add-in bias for 16 bit samples */
+#define CLIP 32635
+
+unsigned char
+linear_to_ulaw( sample )
+int sample;
+{
+ static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
+ int sign, exponent, mantissa;
+ unsigned char ulawbyte;
+
+ /* Get the sample into sign-magnitude. */
+ sign = (sample >> 8) & 0x80; /* set aside the sign */
+ if ( sign != 0 ) sample = -sample; /* get magnitude */
+ if ( sample > CLIP ) sample = CLIP; /* clip the magnitude */
+
+ /* Convert from 16 bit linear to ulaw. */
+ sample = sample + BIAS;
+ exponent = exp_lut[( sample >> 7 ) & 0xFF];
+ mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F;
+ ulawbyte = ~ ( sign | ( exponent << 4 ) | mantissa );
+#ifdef ZEROTRAP
+ if ( ulawbyte == 0 ) ulawbyte = 0x02; /* optional CCITT trap */
+#endif
+
+ return ulawbyte;
+}
+
+/*
+ * Downsample a block of CDDA data, if necessary, for playing out an old-style
+ * audio device.
+ */
+long
+dev_audio_convert(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
+{
+ short *buf16 = (short *)rawbuf;
+ int i, j, samples;
+ int mono_value;
+ unsigned char *rbend = rawbuf + buflen;
+
+ /* Don't do anything if the audio device can take the raw values. */
+ if (raw_audio)
+ return (buflen);
+
+ for (i = 0; buf16 < (short *)(rbend); i++)
+ {
+ /* Downsampling to 8KHz is a little irregular. */
+ samples = (i & 1) ? ((i % 20) ? 10 : 12) : 12;
+
+ /* And unfortunately, we don't always end on a nice boundary. */
+ if (buf16 + samples > (short *)(rbend))
+ samples = ((short *)rbend) - buf16;
+
+ /*
+ * No need to average all the values; taking the first one
+ * is sufficient and less CPU-intensive. But we do need to
+ * do both channels.
+ */
+ mono_value = (buf16[0] + buf16[1]) / 2;
+ buf16 += samples;
+ rawbuf[i] = ulawmap[mono_value];
+ }
+
+ return (i);
+}
+
+static struct audio_oops sun_audio_oops = {
+ .wmaudio_open = sun_audio_open,
+ .wmaudio_close = sun_audio_close,
+ .wmaudio_play = sun_audio_play,
+ .wmaudio_stop = sun_audio_stop,
+ .wmaudio_state = sun_audio_state,
+ .wmaudio_balance = sun_audio_balance,
+ .wmaudio_volume = sun_audio_volume
+};
+
+struct audio_oops*
+setup_sun_audio(const char *dev, const char *ctl)
+{
+ int err;
+
+ if((err = sun_audio_init())) {
+ ERRORLOG("cannot initialize SUN /dev/audio subsystem \n");
+ return NULL;
+ }
+
+ sun_audio_open();
+
+ return &sun_audio_oops;
+}
+
+#endif /* USE_SUN_AUDIO */