comparison src/audio/macrom/SDL_romaudio.c @ 0:74212992fb08

Initial revision
author Sam Lantinga <slouken@lokigames.com>
date Thu, 26 Apr 2001 16:45:43 +0000
parents
children 45b1c4303f87
comparison
equal deleted inserted replaced
-1:000000000000 0:74212992fb08
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