1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/src/audio/nas/SDL_nasaudio.c Thu Apr 26 16:45:43 2001 +0000
1.3 @@ -0,0 +1,308 @@
1.4 +/*
1.5 + SDL - Simple DirectMedia Layer
1.6 + Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga
1.7 +
1.8 + This library is free software; you can redistribute it and/or
1.9 + modify it under the terms of the GNU Library General Public
1.10 + License as published by the Free Software Foundation; either
1.11 + version 2 of the License, or (at your option) any later version.
1.12 +
1.13 + This library is distributed in the hope that it will be useful,
1.14 + but WITHOUT ANY WARRANTY; without even the implied warranty of
1.15 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1.16 + Library General Public License for more details.
1.17 +
1.18 + You should have received a copy of the GNU Library General Public
1.19 + License along with this library; if not, write to the Free
1.20 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1.21 +
1.22 + Sam Lantinga
1.23 + slouken@devolution.com
1.24 +
1.25 + This driver was written by:
1.26 + Erik Inge Bolsų
1.27 + knan@mo.himolde.no
1.28 +*/
1.29 +
1.30 +#ifdef SAVE_RCSID
1.31 +static char rcsid =
1.32 + "@(#) $Id$";
1.33 +#endif
1.34 +
1.35 +/* Allow access to a raw mixing buffer */
1.36 +
1.37 +#include <stdlib.h>
1.38 +#include <stdio.h>
1.39 +#include <string.h>
1.40 +#include <errno.h>
1.41 +#include <signal.h>
1.42 +#include <unistd.h>
1.43 +
1.44 +#include "SDL_audio.h"
1.45 +#include "SDL_error.h"
1.46 +#include "SDL_audiomem.h"
1.47 +#include "SDL_audio_c.h"
1.48 +#include "SDL_timer.h"
1.49 +#include "SDL_audiodev_c.h"
1.50 +#include "SDL_nasaudio.h"
1.51 +
1.52 +/* The tag name used by artsc audio */
1.53 +#define NAS_DRIVER_NAME "nas"
1.54 +
1.55 +static struct SDL_PrivateAudioData *this2 = NULL;
1.56 +
1.57 +/* Audio driver functions */
1.58 +static int NAS_OpenAudio(_THIS, SDL_AudioSpec *spec);
1.59 +static void NAS_WaitAudio(_THIS);
1.60 +static void NAS_PlayAudio(_THIS);
1.61 +static Uint8 *NAS_GetAudioBuf(_THIS);
1.62 +static void NAS_CloseAudio(_THIS);
1.63 +
1.64 +/* Audio driver bootstrap functions */
1.65 +
1.66 +static int Audio_Available(void)
1.67 +{
1.68 + AuServer *aud = AuOpenServer("", 0, NULL, 0, NULL, NULL);
1.69 + if (!aud) return 0;
1.70 +
1.71 + AuCloseServer(aud);
1.72 + return 1;
1.73 +}
1.74 +
1.75 +static void Audio_DeleteDevice(SDL_AudioDevice *device)
1.76 +{
1.77 + free(device->hidden);
1.78 + free(device);
1.79 +}
1.80 +
1.81 +static SDL_AudioDevice *Audio_CreateDevice(int devindex)
1.82 +{
1.83 + SDL_AudioDevice *this;
1.84 +
1.85 + /* Initialize all variables that we clean on shutdown */
1.86 + this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice));
1.87 + if ( this ) {
1.88 + memset(this, 0, (sizeof *this));
1.89 + this->hidden = (struct SDL_PrivateAudioData *)
1.90 + malloc((sizeof *this->hidden));
1.91 + }
1.92 + if ( (this == NULL) || (this->hidden == NULL) ) {
1.93 + SDL_OutOfMemory();
1.94 + if ( this ) {
1.95 + free(this);
1.96 + }
1.97 + return(0);
1.98 + }
1.99 + memset(this->hidden, 0, (sizeof *this->hidden));
1.100 +
1.101 + /* Set the function pointers */
1.102 + this->OpenAudio = NAS_OpenAudio;
1.103 + this->WaitAudio = NAS_WaitAudio;
1.104 + this->PlayAudio = NAS_PlayAudio;
1.105 + this->GetAudioBuf = NAS_GetAudioBuf;
1.106 + this->CloseAudio = NAS_CloseAudio;
1.107 +
1.108 + this->free = Audio_DeleteDevice;
1.109 +
1.110 + return this;
1.111 +}
1.112 +
1.113 +AudioBootStrap NAS_bootstrap = {
1.114 + NAS_DRIVER_NAME, "Network Audio System",
1.115 + Audio_Available, Audio_CreateDevice
1.116 +};
1.117 +
1.118 +/* This function waits until it is possible to write a full sound buffer */
1.119 +static void NAS_WaitAudio(_THIS)
1.120 +{
1.121 + while ( this->hidden->buf_free < this->hidden->mixlen ) {
1.122 + AuEvent ev;
1.123 + AuNextEvent(this->hidden->aud, AuTrue, &ev);
1.124 + AuDispatchEvent(this->hidden->aud, &ev);
1.125 + }
1.126 +}
1.127 +
1.128 +static void NAS_PlayAudio(_THIS)
1.129 +{
1.130 + while (this->hidden->mixlen > this->hidden->buf_free) { /* We think the buffer is full? Yikes! Ask the server for events,
1.131 + in the hope that some of them is LowWater events telling us more
1.132 + of the buffer is free now than what we think. */
1.133 + AuEvent ev;
1.134 + AuNextEvent(this->hidden->aud, AuTrue, &ev);
1.135 + AuDispatchEvent(this->hidden->aud, &ev);
1.136 + }
1.137 + this->hidden->buf_free -= this->hidden->mixlen;
1.138 +
1.139 + /* Write the audio data */
1.140 + AuWriteElement(this->hidden->aud, this->hidden->flow, 0, this->hidden->mixlen, this->hidden->mixbuf, AuFalse, NULL);
1.141 +
1.142 + this->hidden->written += this->hidden->mixlen;
1.143 +
1.144 +#ifdef DEBUG_AUDIO
1.145 + fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
1.146 +#endif
1.147 +}
1.148 +
1.149 +static Uint8 *NAS_GetAudioBuf(_THIS)
1.150 +{
1.151 + return(this->hidden->mixbuf);
1.152 +}
1.153 +
1.154 +static void NAS_CloseAudio(_THIS)
1.155 +{
1.156 + if ( this->hidden->mixbuf != NULL ) {
1.157 + SDL_FreeAudioMem(this->hidden->mixbuf);
1.158 + this->hidden->mixbuf = NULL;
1.159 + }
1.160 + if ( this->hidden->aud ) {
1.161 + AuCloseServer(this->hidden->aud);
1.162 + this->hidden->aud = 0;
1.163 + }
1.164 +}
1.165 +
1.166 +static unsigned char sdlformat_to_auformat(unsigned int fmt)
1.167 +{
1.168 + switch (fmt)
1.169 + {
1.170 + case AUDIO_U8:
1.171 + return AuFormatLinearUnsigned8;
1.172 + case AUDIO_S8:
1.173 + return AuFormatLinearSigned8;
1.174 + case AUDIO_U16LSB:
1.175 + return AuFormatLinearUnsigned16LSB;
1.176 + case AUDIO_U16MSB:
1.177 + return AuFormatLinearUnsigned16MSB;
1.178 + case AUDIO_S16LSB:
1.179 + return AuFormatLinearSigned16LSB;
1.180 + case AUDIO_S16MSB:
1.181 + return AuFormatLinearSigned16MSB;
1.182 + }
1.183 + return AuNone;
1.184 +}
1.185 +
1.186 +static AuBool
1.187 +event_handler(AuServer* aud, AuEvent* ev, AuEventHandlerRec* hnd)
1.188 +{
1.189 + switch (ev->type) {
1.190 + case AuEventTypeElementNotify: {
1.191 + AuElementNotifyEvent* event = (AuElementNotifyEvent *)ev;
1.192 +
1.193 + switch (event->kind) {
1.194 + case AuElementNotifyKindLowWater:
1.195 + if (this2->buf_free >= 0) {
1.196 + this2->really += event->num_bytes;
1.197 + gettimeofday(&this2->last_tv, 0);
1.198 + this2->buf_free += event->num_bytes;
1.199 + } else {
1.200 + this2->buf_free = event->num_bytes;
1.201 + }
1.202 + break;
1.203 + case AuElementNotifyKindState:
1.204 + switch (event->cur_state) {
1.205 + case AuStatePause:
1.206 + if (event->reason != AuReasonUser) {
1.207 + if (this2->buf_free >= 0) {
1.208 + this2->really += event->num_bytes;
1.209 + gettimeofday(&this2->last_tv, 0);
1.210 + this2->buf_free += event->num_bytes;
1.211 + } else {
1.212 + this2->buf_free = event->num_bytes;
1.213 + }
1.214 + }
1.215 + break;
1.216 + }
1.217 + }
1.218 + }
1.219 + }
1.220 + return AuTrue;
1.221 +}
1.222 +
1.223 +static AuDeviceID
1.224 +find_device(_THIS, int nch)
1.225 +{
1.226 + int i;
1.227 + for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) {
1.228 + if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) ==
1.229 + AuComponentKindPhysicalOutput) &&
1.230 + AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) {
1.231 + return AuDeviceIdentifier(AuServerDevice(this->hidden->aud, i));
1.232 + }
1.233 + }
1.234 + return AuNone;
1.235 +}
1.236 +
1.237 +static int NAS_OpenAudio(_THIS, SDL_AudioSpec *spec)
1.238 +{
1.239 + AuElement elms[3];
1.240 + int buffer_size;
1.241 + Uint16 test_format, format;
1.242 +
1.243 + this->hidden->mixbuf = NULL;
1.244 +
1.245 + /* Try for a closest match on audio format */
1.246 + format = 0;
1.247 + for ( test_format = SDL_FirstAudioFormat(spec->format);
1.248 + ! format && test_format; ) {
1.249 + format = sdlformat_to_auformat(test_format);
1.250 +
1.251 + if (format == AuNone) {
1.252 + test_format = SDL_NextAudioFormat();
1.253 + }
1.254 + }
1.255 + if ( format == 0 ) {
1.256 + SDL_SetError("Couldn't find any hardware audio formats");
1.257 + return(-1);
1.258 + }
1.259 + spec->format = test_format;
1.260 +
1.261 + this->hidden->aud = AuOpenServer("", 0, NULL, 0, NULL, NULL);
1.262 + if (this->hidden->aud == 0)
1.263 + {
1.264 + SDL_SetError("Couldn't open connection to NAS server");
1.265 + return (-1);
1.266 + }
1.267 +
1.268 + this->hidden->dev = find_device(this, spec->channels);
1.269 + if ((this->hidden->dev == AuNone) || (!(this->hidden->flow = AuCreateFlow(this->hidden->aud, NULL)))) {
1.270 + AuCloseServer(this->hidden->aud);
1.271 + this->hidden->aud = 0;
1.272 + SDL_SetError("Couldn't find a fitting playback device on NAS server");
1.273 + return (-1);
1.274 + }
1.275 +
1.276 + buffer_size = spec->freq;
1.277 + if (buffer_size < 4096)
1.278 + buffer_size = 4096;
1.279 +
1.280 + if (buffer_size > 32768)
1.281 + buffer_size = 32768; /* So that the buffer won't get unmanageably big. */
1.282 +
1.283 + /* Calculate the final parameters for this audio specification */
1.284 + SDL_CalculateAudioSpec(spec);
1.285 +
1.286 + this2 = this->hidden;
1.287 +
1.288 + AuMakeElementImportClient(elms, spec->freq, format, spec->channels, AuTrue,
1.289 + buffer_size, buffer_size / 4, 0, NULL);
1.290 + AuMakeElementExportDevice(elms+1, 0, this->hidden->dev, spec->freq,
1.291 + AuUnlimitedSamples, 0, NULL);
1.292 + AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms, NULL);
1.293 + AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0, this->hidden->flow,
1.294 + event_handler, (AuPointer) NULL);
1.295 +
1.296 + AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
1.297 +
1.298 + /* Allocate mixing buffer */
1.299 + this->hidden->mixlen = spec->size;
1.300 + this->hidden->mixbuf = (Uint8 *)SDL_AllocAudioMem(this->hidden->mixlen);
1.301 + if ( this->hidden->mixbuf == NULL ) {
1.302 + return(-1);
1.303 + }
1.304 + memset(this->hidden->mixbuf, spec->silence, spec->size);
1.305 +
1.306 + /* Get the parent process id (we're the parent of the audio thread) */
1.307 + this->hidden->parent = getpid();
1.308 +
1.309 + /* We're ready to rock and roll. :-) */
1.310 + return(0);
1.311 +}