diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | e2de64d6f1beb9e492daf5b886e19933c1fa41dd (patch) | |
tree | 9047cf9e6b5c43878d5bf82660adae77ceee097a /kscd/libwm/audio | |
download | tdemultimedia-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.am | 5 | ||||
-rw-r--r-- | kscd/libwm/audio/audio.c | 29 | ||||
-rw-r--r-- | kscd/libwm/audio/audio.h | 21 | ||||
-rw-r--r-- | kscd/libwm/audio/audio_alsa.c | 334 | ||||
-rw-r--r-- | kscd/libwm/audio/audio_arts.c | 141 | ||||
-rw-r--r-- | kscd/libwm/audio/audio_sun.c | 525 |
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 <ahermans@vf.telia.no> + * Cleanups by Jaroslav Kysela <perex@jcu.cz> + * Ville Syrjala <syrjala@sci.fi> + * + * adopted for libworkman cdda audio backend from Alexander Kern alex.kern@gmx.de + * + * Adapted to support both ALSA V0.x and V1.x APIs for PCM calls + * (Philip Nelson <teamdba@scotdb.com> 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 alex.kern@gmx.de + */ + +#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: + * milliByte@DeathsDoor.com + * + * 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 */ |