Mercurial > sdl-ios-xcode
annotate src/audio/sun/SDL_sunaudio.c @ 1026:0f3aa6ab3341
Select patches included from The NetBSD Package Collection (www.pkgsrc.org)
author | Sam Lantinga <slouken@libsdl.org> |
---|---|
date | Wed, 12 Jan 2005 06:04:48 +0000 |
parents | b8d311d90021 |
children | a9542c38dcdb |
rev | line source |
---|---|
0 | 1 /* |
2 SDL - Simple DirectMedia Layer | |
769
b8d311d90021
Updated copyright information for 2004 (Happy New Year!)
Sam Lantinga <slouken@libsdl.org>
parents:
297
diff
changeset
|
3 Copyright (C) 1997-2004 Sam Lantinga |
0 | 4 |
5 This library is free software; you can redistribute it and/or | |
6 modify it under the terms of the GNU Library General Public | |
7 License as published by the Free Software Foundation; either | |
8 version 2 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 Library General Public License for more details. | |
14 | |
15 You should have received a copy of the GNU Library General Public | |
16 License along with this library; if not, write to the Free | |
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | |
19 Sam Lantinga | |
252
e8157fcb3114
Updated the source with the correct e-mail address
Sam Lantinga <slouken@libsdl.org>
parents:
148
diff
changeset
|
20 slouken@libsdl.org |
0 | 21 */ |
22 | |
23 #ifdef SAVE_RCSID | |
24 static char rcsid = | |
25 "@(#) $Id$"; | |
26 #endif | |
27 | |
28 /* Allow access to a raw mixing buffer */ | |
29 | |
30 #include <stdlib.h> | |
31 #include <stdio.h> | |
32 #include <fcntl.h> | |
33 #include <errno.h> | |
34 #include <string.h> | |
35 #ifdef __NetBSD__ | |
36 #include <sys/ioctl.h> | |
37 #include <sys/audioio.h> | |
38 #endif | |
39 #ifdef __SVR4 | |
40 #include <sys/audioio.h> | |
41 #else | |
42 #include <sys/time.h> | |
43 #include <sys/types.h> | |
44 #endif | |
45 #include <unistd.h> | |
46 | |
47 #include "SDL_endian.h" | |
48 #include "SDL_audio.h" | |
49 #include "SDL_audiomem.h" | |
50 #include "SDL_audiodev_c.h" | |
51 #include "SDL_sunaudio.h" | |
52 #include "SDL_audio_c.h" | |
53 #include "SDL_timer.h" | |
54 | |
55 /* Open the audio device for playback, and don't block if busy */ | |
56 #define OPEN_FLAGS (O_WRONLY|O_NONBLOCK) | |
57 | |
58 /* Audio driver functions */ | |
59 static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec); | |
60 static void DSP_WaitAudio(_THIS); | |
61 static void DSP_PlayAudio(_THIS); | |
62 static Uint8 *DSP_GetAudioBuf(_THIS); | |
63 static void DSP_CloseAudio(_THIS); | |
64 | |
65 /* Audio driver bootstrap functions */ | |
66 | |
67 static int Audio_Available(void) | |
68 { | |
69 int fd; | |
70 int available; | |
71 | |
72 available = 0; | |
73 fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 1); | |
74 if ( fd >= 0 ) { | |
75 available = 1; | |
76 close(fd); | |
77 } | |
78 return(available); | |
79 } | |
80 | |
81 static void Audio_DeleteDevice(SDL_AudioDevice *device) | |
82 { | |
83 free(device->hidden); | |
84 free(device); | |
85 } | |
86 | |
87 static SDL_AudioDevice *Audio_CreateDevice(int devindex) | |
88 { | |
89 SDL_AudioDevice *this; | |
90 | |
91 /* Initialize all variables that we clean on shutdown */ | |
92 this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice)); | |
93 if ( this ) { | |
94 memset(this, 0, (sizeof *this)); | |
95 this->hidden = (struct SDL_PrivateAudioData *) | |
96 malloc((sizeof *this->hidden)); | |
97 } | |
98 if ( (this == NULL) || (this->hidden == NULL) ) { | |
99 SDL_OutOfMemory(); | |
100 if ( this ) { | |
101 free(this); | |
102 } | |
103 return(0); | |
104 } | |
105 memset(this->hidden, 0, (sizeof *this->hidden)); | |
106 audio_fd = -1; | |
107 | |
108 /* Set the function pointers */ | |
109 this->OpenAudio = DSP_OpenAudio; | |
110 this->WaitAudio = DSP_WaitAudio; | |
111 this->PlayAudio = DSP_PlayAudio; | |
112 this->GetAudioBuf = DSP_GetAudioBuf; | |
113 this->CloseAudio = DSP_CloseAudio; | |
114 | |
115 this->free = Audio_DeleteDevice; | |
116 | |
117 return this; | |
118 } | |
119 | |
148
8758b8d42cd9
Audio subsystem no longer assumes sun audio API on UNIX systems
Sam Lantinga <slouken@libsdl.org>
parents:
0
diff
changeset
|
120 AudioBootStrap SUNAUDIO_bootstrap = { |
0 | 121 "audio", "UNIX /dev/audio interface", |
122 Audio_Available, Audio_CreateDevice | |
123 }; | |
124 | |
125 #ifdef DEBUG_AUDIO | |
126 void CheckUnderflow(_THIS) | |
127 { | |
128 #ifdef AUDIO_GETINFO | |
129 audio_info_t info; | |
130 int left; | |
131 | |
132 ioctl(audio_fd, AUDIO_GETINFO, &info); | |
133 left = (written - info.play.samples); | |
134 if ( written && (left == 0) ) { | |
135 fprintf(stderr, "audio underflow!\n"); | |
136 } | |
137 #endif | |
138 } | |
139 #endif | |
140 | |
141 void DSP_WaitAudio(_THIS) | |
142 { | |
143 #ifdef AUDIO_GETINFO | |
144 #define SLEEP_FUDGE 10 /* 10 ms scheduling fudge factor */ | |
145 audio_info_t info; | |
146 Sint32 left; | |
147 | |
148 ioctl(audio_fd, AUDIO_GETINFO, &info); | |
149 left = (written - info.play.samples); | |
150 if ( left > fragsize ) { | |
151 Sint32 sleepy; | |
152 | |
153 sleepy = ((left - fragsize)/frequency); | |
154 sleepy -= SLEEP_FUDGE; | |
155 if ( sleepy > 0 ) { | |
156 SDL_Delay(sleepy); | |
157 } | |
158 } | |
159 #else | |
160 fd_set fdset; | |
161 | |
162 FD_ZERO(&fdset); | |
163 FD_SET(audio_fd, &fdset); | |
164 select(audio_fd+1, NULL, &fdset, NULL, NULL); | |
165 #endif | |
166 } | |
167 | |
168 void DSP_PlayAudio(_THIS) | |
169 { | |
170 static Uint8 snd2au(int sample); | |
171 /* Write the audio data */ | |
172 if ( ulaw_only ) { | |
173 /* Assuming that this->spec.freq >= 8000 Hz */ | |
174 int accum, incr, pos; | |
175 Uint8 *aubuf; | |
176 | |
177 accum = 0; | |
178 incr = this->spec.freq/8; | |
179 aubuf = ulaw_buf; | |
180 switch (audio_fmt & 0xFF) { | |
181 case 8: { | |
182 Uint8 *sndbuf; | |
183 | |
184 sndbuf = mixbuf; | |
185 for ( pos=0; pos < fragsize; ++pos ) { | |
186 *aubuf = snd2au((0x80-*sndbuf)*64); | |
187 accum += incr; | |
188 while ( accum > 0 ) { | |
189 accum -= 1000; | |
190 sndbuf += 1; | |
191 } | |
192 aubuf += 1; | |
193 } | |
194 } | |
195 break; | |
196 case 16: { | |
197 Sint16 *sndbuf; | |
198 | |
199 sndbuf = (Sint16 *)mixbuf; | |
200 for ( pos=0; pos < fragsize; ++pos ) { | |
201 *aubuf = snd2au(*sndbuf/4); | |
202 accum += incr; | |
203 while ( accum > 0 ) { | |
204 accum -= 1000; | |
205 sndbuf += 1; | |
206 } | |
207 aubuf += 1; | |
208 } | |
209 } | |
210 break; | |
211 } | |
212 #ifdef DEBUG_AUDIO | |
213 CheckUnderflow(this); | |
214 #endif | |
215 if ( write(audio_fd, ulaw_buf, fragsize) < 0 ) { | |
216 /* Assume fatal error, for now */ | |
217 this->enabled = 0; | |
218 } | |
219 written += fragsize; | |
220 } else { | |
221 #ifdef DEBUG_AUDIO | |
222 CheckUnderflow(this); | |
223 #endif | |
224 if ( write(audio_fd, mixbuf, this->spec.size) < 0 ) { | |
225 /* Assume fatal error, for now */ | |
226 this->enabled = 0; | |
227 } | |
228 written += fragsize; | |
229 } | |
230 } | |
231 | |
232 Uint8 *DSP_GetAudioBuf(_THIS) | |
233 { | |
234 return(mixbuf); | |
235 } | |
236 | |
237 void DSP_CloseAudio(_THIS) | |
238 { | |
239 if ( mixbuf != NULL ) { | |
240 SDL_FreeAudioMem(mixbuf); | |
241 mixbuf = NULL; | |
242 } | |
243 if ( ulaw_buf != NULL ) { | |
244 free(ulaw_buf); | |
245 ulaw_buf = NULL; | |
246 } | |
247 close(audio_fd); | |
248 } | |
249 | |
250 int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec) | |
251 { | |
252 char audiodev[1024]; | |
253 #ifdef AUDIO_SETINFO | |
254 int enc; | |
255 #endif | |
256 int desired_freq = spec->freq; | |
257 | |
258 /* Initialize our freeable variables, in case we fail*/ | |
259 audio_fd = -1; | |
260 mixbuf = NULL; | |
261 ulaw_buf = NULL; | |
262 | |
263 /* Determine the audio parameters from the AudioSpec */ | |
264 switch ( spec->format & 0xFF ) { | |
265 | |
266 case 8: { /* Unsigned 8 bit audio data */ | |
267 spec->format = AUDIO_U8; | |
268 #ifdef AUDIO_SETINFO | |
269 enc = AUDIO_ENCODING_LINEAR8; | |
270 #endif | |
271 } | |
272 break; | |
273 | |
274 case 16: { /* Signed 16 bit audio data */ | |
275 spec->format = AUDIO_S16SYS; | |
276 #ifdef AUDIO_SETINFO | |
277 enc = AUDIO_ENCODING_LINEAR; | |
278 #endif | |
279 } | |
280 break; | |
281 | |
282 default: { | |
283 SDL_SetError("Unsupported audio format"); | |
284 return(-1); | |
285 } | |
286 } | |
287 audio_fmt = spec->format; | |
288 | |
289 /* Open the audio device */ | |
290 audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 1); | |
291 if ( audio_fd < 0 ) { | |
292 SDL_SetError("Couldn't open %s: %s", audiodev, | |
293 strerror(errno)); | |
294 return(-1); | |
295 } | |
296 | |
297 ulaw_only = 0; /* modern Suns do support linear audio */ | |
298 #ifdef AUDIO_SETINFO | |
299 for(;;) { | |
300 audio_info_t info; | |
301 AUDIO_INITINFO(&info); /* init all fields to "no change" */ | |
302 | |
303 /* Try to set the requested settings */ | |
304 info.play.sample_rate = spec->freq; | |
305 info.play.channels = spec->channels; | |
306 info.play.precision = (enc == AUDIO_ENCODING_ULAW) | |
307 ? 8 : spec->format & 0xff; | |
308 info.play.encoding = enc; | |
309 if( ioctl(audio_fd, AUDIO_SETINFO, &info) == 0 ) { | |
310 | |
311 /* Check to be sure we got what we wanted */ | |
312 if(ioctl(audio_fd, AUDIO_GETINFO, &info) < 0) { | |
313 SDL_SetError("Error getting audio parameters: %s", | |
314 strerror(errno)); | |
315 return -1; | |
316 } | |
317 if(info.play.encoding == enc | |
318 && info.play.precision == (spec->format & 0xff) | |
319 && info.play.channels == spec->channels) { | |
320 /* Yow! All seems to be well! */ | |
321 spec->freq = info.play.sample_rate; | |
322 break; | |
323 } | |
324 } | |
325 | |
326 switch(enc) { | |
327 case AUDIO_ENCODING_LINEAR8: | |
328 /* unsigned 8bit apparently not supported here */ | |
329 enc = AUDIO_ENCODING_LINEAR; | |
330 spec->format = AUDIO_S16SYS; | |
331 break; /* try again */ | |
332 | |
333 case AUDIO_ENCODING_LINEAR: | |
334 /* linear 16bit didn't work either, resort to µ-law */ | |
335 enc = AUDIO_ENCODING_ULAW; | |
336 spec->channels = 1; | |
337 spec->freq = 8000; | |
338 spec->format = AUDIO_U8; | |
339 ulaw_only = 1; | |
340 break; | |
341 | |
342 default: | |
343 /* oh well... */ | |
344 SDL_SetError("Error setting audio parameters: %s", | |
345 strerror(errno)); | |
346 return -1; | |
347 } | |
348 } | |
349 #endif /* AUDIO_SETINFO */ | |
350 written = 0; | |
351 | |
352 /* We can actually convert on-the-fly to U-Law */ | |
353 if ( ulaw_only ) { | |
354 spec->freq = desired_freq; | |
355 fragsize = (spec->samples*1000)/(spec->freq/8); | |
356 frequency = 8; | |
357 ulaw_buf = (Uint8 *)malloc(fragsize); | |
358 if ( ulaw_buf == NULL ) { | |
359 SDL_OutOfMemory(); | |
360 return(-1); | |
361 } | |
362 spec->channels = 1; | |
363 } else { | |
364 fragsize = spec->samples; | |
365 frequency = spec->freq/1000; | |
366 } | |
367 #ifdef DEBUG_AUDIO | |
368 fprintf(stderr, "Audio device %s U-Law only\n", | |
369 ulaw_only ? "is" : "is not"); | |
370 fprintf(stderr, "format=0x%x chan=%d freq=%d\n", | |
371 spec->format, spec->channels, spec->freq); | |
372 #endif | |
373 | |
374 /* Update the fragment size as size in bytes */ | |
375 SDL_CalculateAudioSpec(spec); | |
376 | |
377 /* Allocate mixing buffer */ | |
378 mixbuf = (Uint8 *)SDL_AllocAudioMem(spec->size); | |
379 if ( mixbuf == NULL ) { | |
380 SDL_OutOfMemory(); | |
381 return(-1); | |
382 } | |
383 memset(mixbuf, spec->silence, spec->size); | |
384 | |
385 /* We're ready to rock and roll. :-) */ | |
386 return(0); | |
387 } | |
388 | |
389 /************************************************************************/ | |
390 /* This function (snd2au()) copyrighted: */ | |
391 /************************************************************************/ | |
392 /* Copyright 1989 by Rich Gopstein and Harris Corporation */ | |
393 /* */ | |
394 /* Permission to use, copy, modify, and distribute this software */ | |
395 /* and its documentation for any purpose and without fee is */ | |
396 /* hereby granted, provided that the above copyright notice */ | |
397 /* appears in all copies and that both that copyright notice and */ | |
398 /* this permission notice appear in supporting documentation, and */ | |
399 /* that the name of Rich Gopstein and Harris Corporation not be */ | |
400 /* used in advertising or publicity pertaining to distribution */ | |
401 /* of the software without specific, written prior permission. */ | |
402 /* Rich Gopstein and Harris Corporation make no representations */ | |
403 /* about the suitability of this software for any purpose. It */ | |
404 /* provided "as is" without express or implied warranty. */ | |
405 /************************************************************************/ | |
406 | |
407 static Uint8 snd2au(int sample) | |
408 { | |
409 | |
410 int mask; | |
411 | |
412 if (sample < 0) { | |
413 sample = -sample; | |
414 mask = 0x7f; | |
415 } else { | |
416 mask = 0xff; | |
417 } | |
418 | |
419 if (sample < 32) { | |
420 sample = 0xF0 | (15 - sample / 2); | |
421 } else if (sample < 96) { | |
422 sample = 0xE0 | (15 - (sample - 32) / 4); | |
423 } else if (sample < 224) { | |
424 sample = 0xD0 | (15 - (sample - 96) / 8); | |
425 } else if (sample < 480) { | |
426 sample = 0xC0 | (15 - (sample - 224) / 16); | |
427 } else if (sample < 992) { | |
428 sample = 0xB0 | (15 - (sample - 480) / 32); | |
429 } else if (sample < 2016) { | |
430 sample = 0xA0 | (15 - (sample - 992) / 64); | |
431 } else if (sample < 4064) { | |
432 sample = 0x90 | (15 - (sample - 2016) / 128); | |
433 } else if (sample < 8160) { | |
434 sample = 0x80 | (15 - (sample - 4064) / 256); | |
435 } else { | |
436 sample = 0x80; | |
437 } | |
438 return (mask & sample); | |
439 } |