0
|
1 /*
|
|
2 SDL - Simple DirectMedia Layer
|
|
3 Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga
|
|
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
|
|
20 slouken@devolution.com
|
|
21 */
|
|
22
|
|
23 #ifdef SAVE_RCSID
|
|
24 static char rcsid =
|
|
25 "@(#) $Id$";
|
|
26 #endif
|
|
27
|
|
28 #if TARGET_API_MAC_CARBON
|
|
29 # include <Carbon.h>
|
|
30 #else
|
|
31 # include <Sound.h> /* SoundManager interface */
|
|
32 # include <Gestalt.h>
|
|
33 #endif
|
|
34
|
|
35 #include <stdlib.h>
|
|
36 #include <stdio.h>
|
|
37
|
|
38 #include "SDL_endian.h"
|
|
39 #include "SDL_audio.h"
|
|
40 #include "SDL_audio_c.h"
|
|
41 #include "SDL_audiomem.h"
|
|
42 #include "SDL_sysaudio.h"
|
|
43 #include "SDL_romaudio.h"
|
|
44
|
|
45 /* Audio driver functions */
|
|
46
|
|
47 static void Mac_CloseAudio(_THIS);
|
|
48 static int Mac_OpenAudio(_THIS, SDL_AudioSpec *spec);
|
|
49
|
|
50 /* Audio driver bootstrap functions */
|
|
51
|
|
52
|
|
53 static int Audio_Available(void)
|
|
54 {
|
|
55 return(1);
|
|
56 }
|
|
57
|
|
58 static void Audio_DeleteDevice(SDL_AudioDevice *device)
|
|
59 {
|
|
60 free(device->hidden);
|
|
61 free(device);
|
|
62 }
|
|
63
|
|
64 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
|
|
65 {
|
|
66 SDL_AudioDevice *this;
|
|
67
|
|
68 /* Initialize all variables that we clean on shutdown */
|
|
69 this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice));
|
|
70 if ( this ) {
|
|
71 memset(this, 0, (sizeof *this));
|
|
72 this->hidden = (struct SDL_PrivateAudioData *)
|
|
73 malloc((sizeof *this->hidden));
|
|
74 }
|
|
75 if ( (this == NULL) || (this->hidden == NULL) ) {
|
|
76 SDL_OutOfMemory();
|
|
77 if ( this ) {
|
|
78 free(this);
|
|
79 }
|
|
80 return(0);
|
|
81 }
|
|
82 memset(this->hidden, 0, (sizeof *this->hidden));
|
|
83
|
|
84 /* Set the function pointers */
|
|
85 this->OpenAudio = Mac_OpenAudio;
|
|
86 this->CloseAudio = Mac_CloseAudio;
|
|
87 this->free = Audio_DeleteDevice;
|
|
88
|
|
89 return this;
|
|
90 }
|
|
91
|
|
92 AudioBootStrap SNDMGR_bootstrap = {
|
|
93 "sndmgr", "MacOS SoundManager 3.0",
|
|
94 Audio_Available, Audio_CreateDevice
|
|
95 };
|
|
96
|
|
97 #if TARGET_API_MAC_CARBON
|
|
98
|
|
99 static UInt8 *buffer[2];
|
|
100 static volatile UInt32 running = 0;
|
|
101 static CmpSoundHeader header;
|
|
102
|
|
103 static void callBackProc (SndChannel *chan, SndCommand *cmd_passed ) {
|
|
104
|
|
105 UInt32 fill_me, play_me;
|
|
106 SndCommand cmd;
|
|
107 SDL_AudioDevice *audio = (SDL_AudioDevice *)chan->userInfo;
|
|
108
|
|
109 fill_me = cmd_passed->param2; /* buffer that has just finished playing,
|
|
110 so fill it */
|
|
111 play_me = ! fill_me; /* filled buffer to play _now_ */
|
|
112
|
|
113 if ( ! audio->enabled ) {
|
|
114 return;
|
|
115 }
|
|
116
|
|
117 header.samplePtr = (Ptr)buffer[play_me];
|
|
118
|
|
119 cmd.cmd = bufferCmd;
|
|
120 cmd.param1 = 0;
|
|
121 cmd.param2 = (long)&header;
|
|
122
|
|
123 SndDoCommand (chan, &cmd, 0);
|
|
124
|
|
125 memset (buffer[fill_me], 0, audio->spec.size);
|
|
126
|
|
127 if ( ! audio->paused ) {
|
|
128 if ( audio->convert.needed ) {
|
|
129 audio->spec.callback(audio->spec.userdata,
|
|
130 (Uint8 *)audio->convert.buf,audio->convert.len);
|
|
131 SDL_ConvertAudio(&audio->convert);
|
|
132 #if 0
|
|
133 if ( audio->convert.len_cvt != audio->spec.size ) {
|
|
134 /* Uh oh... probably crashes here; */
|
|
135 }
|
|
136 #endif
|
|
137 memcpy(buffer[fill_me], audio->convert.buf,
|
|
138 audio->convert.len_cvt);
|
|
139 } else {
|
|
140 audio->spec.callback(audio->spec.userdata,
|
|
141 (Uint8 *)buffer[fill_me], audio->spec.size);
|
|
142 }
|
|
143 }
|
|
144
|
|
145 if ( running ) {
|
|
146
|
|
147 cmd.cmd = callBackCmd;
|
|
148 cmd.param1 = 0;
|
|
149 cmd.param2 = play_me;
|
|
150
|
|
151 SndDoCommand (chan, &cmd, 0);
|
|
152 }
|
|
153 }
|
|
154
|
|
155 static int Mac_OpenAudio(_THIS, SDL_AudioSpec *spec) {
|
|
156
|
|
157 SndCallBackUPP callback;
|
|
158 int sample_bits;
|
|
159 int i;
|
|
160 long initOptions;
|
|
161
|
|
162 /* Very few conversions are required, but... */
|
|
163 switch (spec->format) {
|
|
164 case AUDIO_S8:
|
|
165 spec->format = AUDIO_U8;
|
|
166 break;
|
|
167 case AUDIO_U16LSB:
|
|
168 spec->format = AUDIO_S16LSB;
|
|
169 break;
|
|
170 case AUDIO_U16MSB:
|
|
171 spec->format = AUDIO_S16MSB;
|
|
172 break;
|
|
173 }
|
|
174 SDL_CalculateAudioSpec(spec);
|
|
175
|
|
176 /* initialize bufferCmd header */
|
|
177 memset (&header, 0, sizeof(header));
|
|
178 callback = NewSndCallBackUPP (callBackProc);
|
|
179 sample_bits = spec->size / spec->samples / spec->channels * 8;
|
|
180
|
|
181 #ifdef DEBUG_AUDIO
|
|
182 fprintf(stderr,
|
|
183 "Audio format 0x%x, channels = %d, sample_bits = %d, frequency = %d\n",
|
|
184 spec->format, spec->channels, sample_bits, spec->freq);
|
|
185 #endif /* DEBUG_AUDIO */
|
|
186
|
|
187 header.numChannels = spec->channels;
|
|
188 header.sampleSize = sample_bits;
|
|
189 header.sampleRate = spec->freq << 16;
|
|
190 header.numFrames = spec->samples;
|
|
191 header.encode = cmpSH;
|
|
192
|
|
193 /* Note that we install the 16bitLittleEndian Converter if needed. */
|
|
194 if ( spec->format == 0x8010 ) {
|
|
195 header.compressionID = fixedCompression;
|
|
196 header.format = k16BitLittleEndianFormat;
|
|
197 }
|
|
198
|
|
199 /* allocate 2 buffers */
|
|
200 for (i=0; i<2; i++) {
|
|
201 buffer[i] = (UInt8*)malloc (sizeof(UInt8) * spec->size);
|
|
202 if (buffer[i] == NULL) {
|
|
203 SDL_OutOfMemory();
|
|
204 return (-1);
|
|
205 }
|
|
206 memset (buffer[i], 0, spec->size);
|
|
207 }
|
|
208
|
|
209 /* Create the sound manager channel */
|
|
210 channel = (SndChannelPtr)malloc(sizeof(*channel));
|
|
211 if ( channel == NULL ) {
|
|
212 SDL_OutOfMemory();
|
|
213 return(-1);
|
|
214 }
|
|
215 if ( spec->channels >= 2 ) {
|
|
216 initOptions = initStereo;
|
|
217 } else {
|
|
218 initOptions = initMono;
|
|
219 }
|
|
220 channel->userInfo = (long)this;
|
|
221 channel->qLength = 128;
|
|
222 if ( SndNewChannel(&channel, sampledSynth, initOptions, callback) !=
|
|
223 noErr ) {
|
|
224 SDL_SetError("Unable to create audio channel");
|
|
225 free(channel);
|
|
226 channel = NULL;
|
|
227 return(-1);
|
|
228 }
|
|
229
|
|
230 /* start playback */
|
|
231 {
|
|
232 SndCommand cmd;
|
|
233 cmd.cmd = callBackCmd;
|
|
234 cmd.param2 = 0;
|
|
235 running = 1;
|
|
236 SndDoCommand (channel, &cmd, 0);
|
|
237 }
|
|
238
|
|
239 return 1;
|
|
240 }
|
|
241
|
|
242 static void Mac_CloseAudio(_THIS) {
|
|
243
|
|
244 int i;
|
|
245
|
|
246 running = 0;
|
|
247
|
|
248 if (channel) {
|
|
249 SndDisposeChannel (channel, true);
|
|
250 channel = NULL;
|
|
251 }
|
|
252
|
|
253 for ( i=0; i<2; ++i ) {
|
|
254 if ( buffer[i] ) {
|
|
255 free(buffer[i]);
|
|
256 buffer[i] = NULL;
|
|
257 }
|
|
258 }
|
|
259 }
|
|
260
|
|
261 #else /* !TARGET_API_MAC_CARBON */
|
|
262
|
|
263 /* This function is called by Sound Manager when it has exhausted one of
|
|
264 the buffers, so we'll zero it to silence and fill it with audio if
|
|
265 we're not paused.
|
|
266 */
|
|
267 static pascal
|
|
268 void sndDoubleBackProc (SndChannelPtr chan, SndDoubleBufferPtr newbuf)
|
|
269 {
|
|
270 SDL_AudioDevice *audio = (SDL_AudioDevice *)newbuf->dbUserInfo[0];
|
|
271
|
|
272 /* If audio is quitting, don't do anything */
|
|
273 if ( ! audio->enabled ) {
|
|
274 return;
|
|
275 }
|
|
276 memset (newbuf->dbSoundData, 0, audio->spec.size);
|
|
277 newbuf->dbNumFrames = audio->spec.samples;
|
|
278 if ( ! audio->paused ) {
|
|
279 if ( audio->convert.needed ) {
|
|
280 audio->spec.callback(audio->spec.userdata,
|
|
281 (Uint8 *)audio->convert.buf,audio->convert.len);
|
|
282 SDL_ConvertAudio(&audio->convert);
|
|
283 #if 0
|
|
284 if ( audio->convert.len_cvt != audio->spec.size ) {
|
|
285 /* Uh oh... probably crashes here */;
|
|
286 }
|
|
287 #endif
|
|
288 memcpy(newbuf->dbSoundData, audio->convert.buf,
|
|
289 audio->convert.len_cvt);
|
|
290 } else {
|
|
291 audio->spec.callback(audio->spec.userdata,
|
|
292 (Uint8 *)newbuf->dbSoundData, audio->spec.size);
|
|
293 }
|
|
294 }
|
|
295 newbuf->dbFlags |= dbBufferReady;
|
|
296 }
|
|
297
|
|
298 static int DoubleBufferAudio_Available(void)
|
|
299 {
|
|
300 int available;
|
|
301 NumVersion sndversion;
|
|
302 long response;
|
|
303
|
|
304 available = 0;
|
|
305 sndversion = SndSoundManagerVersion();
|
|
306 if ( sndversion.majorRev >= 3 ) {
|
|
307 if ( Gestalt(gestaltSoundAttr, &response) == noErr ) {
|
|
308 if ( (response & (1 << gestaltSndPlayDoubleBuffer)) ) {
|
|
309 available = 1;
|
|
310 }
|
|
311 }
|
|
312 } else {
|
|
313 if ( Gestalt(gestaltSoundAttr, &response) == noErr ) {
|
|
314 if ( (response & (1 << gestaltHasASC)) ) {
|
|
315 available = 1;
|
|
316 }
|
|
317 }
|
|
318 }
|
|
319 return(available);
|
|
320 }
|
|
321
|
|
322 static void Mac_CloseAudio(_THIS)
|
|
323 {
|
|
324 int i;
|
|
325
|
|
326 if ( channel != NULL ) {
|
|
327 #if 0
|
|
328 SCStatus status;
|
|
329
|
|
330 /* Wait for audio to complete */
|
|
331 do {
|
|
332 SndChannelStatus(channel, sizeof(status), &status);
|
|
333 } while ( status.scChannelBusy );
|
|
334 #endif
|
|
335 /* Clean up the audio channel */
|
|
336 SndDisposeChannel(channel, true);
|
|
337 channel = NULL;
|
|
338 }
|
|
339 for ( i=0; i<2; ++i ) {
|
|
340 if ( audio_buf[i] ) {
|
|
341 free(audio_buf[i]);
|
|
342 audio_buf[i] = NULL;
|
|
343 }
|
|
344 }
|
|
345 }
|
|
346
|
|
347 static int Mac_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|
348 {
|
|
349 SndDoubleBufferHeader2 audio_dbh;
|
|
350 int i;
|
|
351 long initOptions;
|
|
352 int sample_bits;
|
|
353 SndDoubleBackUPP doubleBackProc;
|
|
354
|
|
355 /* Check to make sure double-buffered audio is available */
|
|
356 if ( ! DoubleBufferAudio_Available() ) {
|
|
357 SDL_SetError("Sound manager doesn't support double-buffering");
|
|
358 return(-1);
|
|
359 }
|
|
360
|
|
361 /* Very few conversions are required, but... */
|
|
362 switch (spec->format) {
|
|
363 case AUDIO_S8:
|
|
364 spec->format = AUDIO_U8;
|
|
365 break;
|
|
366 case AUDIO_U16LSB:
|
|
367 spec->format = AUDIO_S16LSB;
|
|
368 break;
|
|
369 case AUDIO_U16MSB:
|
|
370 spec->format = AUDIO_S16MSB;
|
|
371 break;
|
|
372 }
|
|
373 SDL_CalculateAudioSpec(spec);
|
|
374
|
|
375 /* initialize the double-back header */
|
|
376 memset(&audio_dbh, 0, sizeof(audio_dbh));
|
|
377 doubleBackProc = NewSndDoubleBackProc (sndDoubleBackProc);
|
|
378 sample_bits = spec->size / spec->samples / spec->channels * 8;
|
|
379
|
|
380 audio_dbh.dbhNumChannels = spec->channels;
|
|
381 audio_dbh.dbhSampleSize = sample_bits;
|
|
382 audio_dbh.dbhCompressionID = 0;
|
|
383 audio_dbh.dbhPacketSize = 0;
|
|
384 audio_dbh.dbhSampleRate = spec->freq << 16;
|
|
385 audio_dbh.dbhDoubleBack = doubleBackProc;
|
|
386 audio_dbh.dbhFormat = 0;
|
|
387
|
|
388 /* Note that we install the 16bitLittleEndian Converter if needed. */
|
|
389 if ( spec->format == 0x8010 ) {
|
|
390 audio_dbh.dbhCompressionID = fixedCompression;
|
|
391 audio_dbh.dbhFormat = k16BitLittleEndianFormat;
|
|
392 }
|
|
393
|
|
394 /* allocate the 2 double-back buffers */
|
|
395 for ( i=0; i<2; ++i ) {
|
|
396 audio_buf[i] = calloc(1, sizeof(SndDoubleBuffer)+spec->size);
|
|
397 if ( audio_buf[i] == NULL ) {
|
|
398 SDL_OutOfMemory();
|
|
399 return(-1);
|
|
400 }
|
|
401 audio_buf[i]->dbNumFrames = spec->samples;
|
|
402 audio_buf[i]->dbFlags = dbBufferReady;
|
|
403 audio_buf[i]->dbUserInfo[0] = (long)this;
|
|
404 audio_dbh.dbhBufferPtr[i] = audio_buf[i];
|
|
405 }
|
|
406
|
|
407 /* Create the sound manager channel */
|
|
408 channel = (SndChannelPtr)malloc(sizeof(*channel));
|
|
409 if ( channel == NULL ) {
|
|
410 SDL_OutOfMemory();
|
|
411 return(-1);
|
|
412 }
|
|
413 if ( spec->channels >= 2 ) {
|
|
414 initOptions = initStereo;
|
|
415 } else {
|
|
416 initOptions = initMono;
|
|
417 }
|
|
418 channel->userInfo = 0;
|
|
419 channel->qLength = 128;
|
|
420 if ( SndNewChannel(&channel, sampledSynth, initOptions, 0L) != noErr ) {
|
|
421 SDL_SetError("Unable to create audio channel");
|
|
422 free(channel);
|
|
423 channel = NULL;
|
|
424 return(-1);
|
|
425 }
|
|
426
|
|
427 /* Start playback */
|
|
428 if ( SndPlayDoubleBuffer(channel, (SndDoubleBufferHeaderPtr)&audio_dbh)
|
|
429 != noErr ) {
|
|
430 SDL_SetError("Unable to play double buffered audio");
|
|
431 return(-1);
|
|
432 }
|
|
433
|
|
434 return 1;
|
|
435 }
|
|
436
|
|
437 #endif /* TARGET_API_MAC_CARBON */
|
|
438
|
|
439
|