• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2006 Sam Lantinga
4 
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Lesser General Public
7     License as published by the Free Software Foundation; either
8     version 2.1 of the License, or (at your option) any later version.
9 
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     Lesser General Public License for more details.
14 
15     You should have received a copy of the GNU Lesser General Public
16     License along with this library; if not, write to the Free Software
17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 
19     Sam Lantinga
20     slouken@libsdl.org
21 
22     This driver was written by:
23     Erik Inge Bols�
24     knan@mo.himolde.no
25 */
26 #include "SDL_config.h"
27 
28 /* Allow access to a raw mixing buffer */
29 
30 #include <signal.h>
31 #include <unistd.h>
32 
33 #include "SDL_timer.h"
34 #include "SDL_audio.h"
35 #include "../SDL_audiomem.h"
36 #include "../SDL_audio_c.h"
37 #include "../SDL_audiodev_c.h"
38 #include "SDL_nasaudio.h"
39 
40 /* The tag name used by artsc audio */
41 #define NAS_DRIVER_NAME         "nas"
42 
43 static struct SDL_PrivateAudioData *this2 = NULL;
44 
45 /* Audio driver functions */
46 static int NAS_OpenAudio(_THIS, SDL_AudioSpec *spec);
47 static void NAS_WaitAudio(_THIS);
48 static void NAS_PlayAudio(_THIS);
49 static Uint8 *NAS_GetAudioBuf(_THIS);
50 static void NAS_CloseAudio(_THIS);
51 
52 /* Audio driver bootstrap functions */
53 
Audio_Available(void)54 static int Audio_Available(void)
55 {
56 	AuServer *aud = AuOpenServer("", 0, NULL, 0, NULL, NULL);
57 	if (!aud) return 0;
58 
59 	AuCloseServer(aud);
60 	return 1;
61 }
62 
Audio_DeleteDevice(SDL_AudioDevice * device)63 static void Audio_DeleteDevice(SDL_AudioDevice *device)
64 {
65 	SDL_free(device->hidden);
66 	SDL_free(device);
67 }
68 
Audio_CreateDevice(int devindex)69 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
70 {
71 	SDL_AudioDevice *this;
72 
73 	/* Initialize all variables that we clean on shutdown */
74 	this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
75 	if ( this ) {
76 		SDL_memset(this, 0, (sizeof *this));
77 		this->hidden = (struct SDL_PrivateAudioData *)
78 				SDL_malloc((sizeof *this->hidden));
79 	}
80 	if ( (this == NULL) || (this->hidden == NULL) ) {
81 		SDL_OutOfMemory();
82 		if ( this ) {
83 			SDL_free(this);
84 		}
85 		return(0);
86 	}
87 	SDL_memset(this->hidden, 0, (sizeof *this->hidden));
88 
89 	/* Set the function pointers */
90 	this->OpenAudio = NAS_OpenAudio;
91 	this->WaitAudio = NAS_WaitAudio;
92 	this->PlayAudio = NAS_PlayAudio;
93 	this->GetAudioBuf = NAS_GetAudioBuf;
94 	this->CloseAudio = NAS_CloseAudio;
95 
96 	this->free = Audio_DeleteDevice;
97 
98 	return this;
99 }
100 
101 AudioBootStrap NAS_bootstrap = {
102 	NAS_DRIVER_NAME, "Network Audio System",
103 	Audio_Available, Audio_CreateDevice
104 };
105 
106 /* This function waits until it is possible to write a full sound buffer */
NAS_WaitAudio(_THIS)107 static void NAS_WaitAudio(_THIS)
108 {
109 	while ( this->hidden->buf_free < this->hidden->mixlen ) {
110 		AuEvent ev;
111 		AuNextEvent(this->hidden->aud, AuTrue, &ev);
112 		AuDispatchEvent(this->hidden->aud, &ev);
113 	}
114 }
115 
NAS_PlayAudio(_THIS)116 static void NAS_PlayAudio(_THIS)
117 {
118 	while (this->hidden->mixlen > this->hidden->buf_free) { /* We think the buffer is full? Yikes! Ask the server for events,
119 				    in the hope that some of them is LowWater events telling us more
120 				    of the buffer is free now than what we think. */
121 		AuEvent ev;
122 		AuNextEvent(this->hidden->aud, AuTrue, &ev);
123 		AuDispatchEvent(this->hidden->aud, &ev);
124 	}
125 	this->hidden->buf_free -= this->hidden->mixlen;
126 
127 	/* Write the audio data */
128 	AuWriteElement(this->hidden->aud, this->hidden->flow, 0, this->hidden->mixlen, this->hidden->mixbuf, AuFalse, NULL);
129 
130 	this->hidden->written += this->hidden->mixlen;
131 
132 #ifdef DEBUG_AUDIO
133 	fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
134 #endif
135 }
136 
NAS_GetAudioBuf(_THIS)137 static Uint8 *NAS_GetAudioBuf(_THIS)
138 {
139 	return(this->hidden->mixbuf);
140 }
141 
NAS_CloseAudio(_THIS)142 static void NAS_CloseAudio(_THIS)
143 {
144 	if ( this->hidden->mixbuf != NULL ) {
145 		SDL_FreeAudioMem(this->hidden->mixbuf);
146 		this->hidden->mixbuf = NULL;
147 	}
148 	if ( this->hidden->aud ) {
149 		AuCloseServer(this->hidden->aud);
150 		this->hidden->aud = 0;
151 	}
152 }
153 
sdlformat_to_auformat(unsigned int fmt)154 static unsigned char sdlformat_to_auformat(unsigned int fmt)
155 {
156   switch (fmt)
157     {
158     case AUDIO_U8:
159       return AuFormatLinearUnsigned8;
160     case AUDIO_S8:
161       return AuFormatLinearSigned8;
162     case AUDIO_U16LSB:
163       return AuFormatLinearUnsigned16LSB;
164     case AUDIO_U16MSB:
165       return AuFormatLinearUnsigned16MSB;
166     case AUDIO_S16LSB:
167       return AuFormatLinearSigned16LSB;
168     case AUDIO_S16MSB:
169       return AuFormatLinearSigned16MSB;
170     }
171   return AuNone;
172 }
173 
174 static AuBool
event_handler(AuServer * aud,AuEvent * ev,AuEventHandlerRec * hnd)175 event_handler(AuServer* aud, AuEvent* ev, AuEventHandlerRec* hnd)
176 {
177 	switch (ev->type) {
178 	case AuEventTypeElementNotify: {
179 		AuElementNotifyEvent* event = (AuElementNotifyEvent *)ev;
180 
181 		switch (event->kind) {
182 		case AuElementNotifyKindLowWater:
183 			if (this2->buf_free >= 0) {
184 				this2->really += event->num_bytes;
185 				gettimeofday(&this2->last_tv, 0);
186 				this2->buf_free += event->num_bytes;
187 			} else {
188 				this2->buf_free = event->num_bytes;
189 			}
190 			break;
191 		case AuElementNotifyKindState:
192 			switch (event->cur_state) {
193 			case AuStatePause:
194 				if (event->reason != AuReasonUser) {
195 					if (this2->buf_free >= 0) {
196 						this2->really += event->num_bytes;
197 						gettimeofday(&this2->last_tv, 0);
198 						this2->buf_free += event->num_bytes;
199 					} else {
200 						this2->buf_free = event->num_bytes;
201 					}
202 				}
203 				break;
204 			}
205 		}
206 	}
207 	}
208 	return AuTrue;
209 }
210 
211 static AuDeviceID
find_device(_THIS,int nch)212 find_device(_THIS, int nch)
213 {
214 	int i;
215 	for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) {
216 		if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) ==
217 				AuComponentKindPhysicalOutput) &&
218 			AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) {
219 			return AuDeviceIdentifier(AuServerDevice(this->hidden->aud, i));
220 		}
221 	}
222 	return AuNone;
223 }
224 
NAS_OpenAudio(_THIS,SDL_AudioSpec * spec)225 static int NAS_OpenAudio(_THIS, SDL_AudioSpec *spec)
226 {
227 	AuElement elms[3];
228 	int buffer_size;
229 	Uint16 test_format, format;
230 
231 	this->hidden->mixbuf = NULL;
232 
233 	/* Try for a closest match on audio format */
234 	format = 0;
235 	for ( test_format = SDL_FirstAudioFormat(spec->format);
236 						! format && test_format; ) {
237 		format = sdlformat_to_auformat(test_format);
238 
239 		if (format == AuNone) {
240 			test_format = SDL_NextAudioFormat();
241 		}
242 	}
243 	if ( format == 0 ) {
244 		SDL_SetError("Couldn't find any hardware audio formats");
245 		return(-1);
246 	}
247 	spec->format = test_format;
248 
249 	this->hidden->aud = AuOpenServer("", 0, NULL, 0, NULL, NULL);
250 	if (this->hidden->aud == 0)
251 	{
252 		SDL_SetError("Couldn't open connection to NAS server");
253 		return (-1);
254 	}
255 
256 	this->hidden->dev = find_device(this, spec->channels);
257 	if ((this->hidden->dev == AuNone) || (!(this->hidden->flow = AuCreateFlow(this->hidden->aud, NULL)))) {
258 		AuCloseServer(this->hidden->aud);
259 		this->hidden->aud = 0;
260 		SDL_SetError("Couldn't find a fitting playback device on NAS server");
261 		return (-1);
262 	}
263 
264 	buffer_size = spec->freq;
265 	if (buffer_size < 4096)
266 		buffer_size = 4096;
267 
268 	if (buffer_size > 32768)
269 		buffer_size = 32768; /* So that the buffer won't get unmanageably big. */
270 
271 	/* Calculate the final parameters for this audio specification */
272 	SDL_CalculateAudioSpec(spec);
273 
274 	this2 = this->hidden;
275 
276 	AuMakeElementImportClient(elms, spec->freq, format, spec->channels, AuTrue,
277 				buffer_size, buffer_size / 4, 0, NULL);
278 	AuMakeElementExportDevice(elms+1, 0, this->hidden->dev, spec->freq,
279 				AuUnlimitedSamples, 0, NULL);
280 	AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms, NULL);
281 	AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0, this->hidden->flow,
282 				event_handler, (AuPointer) NULL);
283 
284 	AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
285 
286 	/* Allocate mixing buffer */
287 	this->hidden->mixlen = spec->size;
288 	this->hidden->mixbuf = (Uint8 *)SDL_AllocAudioMem(this->hidden->mixlen);
289 	if ( this->hidden->mixbuf == NULL ) {
290 		return(-1);
291 	}
292 	SDL_memset(this->hidden->mixbuf, spec->silence, spec->size);
293 
294 	/* Get the parent process id (we're the parent of the audio thread) */
295 	this->hidden->parent = getpid();
296 
297 	/* We're ready to rock and roll. :-) */
298 	return(0);
299 }
300