This repository has been archived by the owner on Feb 11, 2021. It is now read-only.
/
SDL_romaudio.c
517 lines (443 loc) · 13.8 KB
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
11
12
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
Lesser General Public License for more details.
14
15
16
17
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19
Sam Lantinga
20
slouken@libsdl.org
21
*/
22
#include "SDL_config.h"
23
24
25
26
#if defined(__APPLE__) && defined(__MACH__)
# include <Carbon/Carbon.h>
#elif TARGET_API_MAC_CARBON && (UNIVERSAL_INTERFACES_VERSION > 0x0335)
27
28
# include <Carbon.h>
#else
29
# include <Sound.h> /* SoundManager interface */
30
# include <Gestalt.h>
31
# include <DriverServices.h>
32
33
#endif
34
#if !defined(NewSndCallBackUPP) && (UNIVERSAL_INTERFACES_VERSION < 0x0335)
35
#if !defined(NewSndCallBackProc) /* avoid circular redefinition... */
36
37
#define NewSndCallBackUPP NewSndCallBackProc
#endif
38
39
40
#if !defined(NewSndCallBackUPP)
#define NewSndCallBackUPP NewSndCallBackProc
#endif
41
42
#endif
43
#include "SDL_audio.h"
44
45
#include "../SDL_audio_c.h"
#include "../SDL_sysaudio.h"
46
47
48
49
#include "SDL_romaudio.h"
/* Audio driver functions */
50
51
52
53
static void Mac_CloseAudio(_THIS);
static int Mac_OpenAudio(_THIS, SDL_AudioSpec * spec);
static void Mac_LockAudio(_THIS);
static void Mac_UnlockAudio(_THIS);
54
55
56
57
/* Audio driver bootstrap functions */
58
static int
59
Audio_Available(void)
60
{
61
return (1);
62
63
}
64
static void
65
Audio_DeleteDevice(SDL_AudioDevice * device)
66
{
67
68
SDL_free(device->hidden);
SDL_free(device);
69
70
}
71
static SDL_AudioDevice *
72
Audio_CreateDevice(int devindex)
73
74
75
76
{
SDL_AudioDevice *this;
/* Initialize all variables that we clean on shutdown */
77
this = (SDL_AudioDevice *) SDL_malloc(sizeof(SDL_AudioDevice));
78
if (this) {
79
SDL_memset(this, 0, (sizeof *this));
80
this->hidden = (struct SDL_PrivateAudioData *)
81
SDL_malloc((sizeof *this->hidden));
82
}
83
if ((this == NULL) || (this->hidden == NULL)) {
84
SDL_OutOfMemory();
85
if (this) {
86
SDL_free(this);
87
}
88
return (0);
89
}
90
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
91
92
/* Set the function pointers */
93
94
95
this->OpenAudio = Mac_OpenAudio;
this->CloseAudio = Mac_CloseAudio;
this->LockAudio = Mac_LockAudio;
96
this->UnlockAudio = Mac_UnlockAudio;
97
this->free = Audio_DeleteDevice;
98
99
100
#ifdef __MACOSX__ /* Mac OS X uses threaded audio, so normal thread code is okay */
this->LockAudio = NULL;
101
102
this->UnlockAudio = NULL;
#endif
103
104
105
106
return this;
}
AudioBootStrap SNDMGR_bootstrap = {
107
108
"sndmgr", "MacOS SoundManager 3.0",
Audio_Available, Audio_CreateDevice
109
110
};
111
#if defined(TARGET_API_MAC_CARBON) || defined(USE_RYANS_SOUNDCODE)
112
/* This works correctly on Mac OS X */
113
114
115
116
117
#pragma options align=power
static volatile SInt32 audio_is_locked = 0;
static volatile SInt32 need_to_mix = 0;
118
119
static UInt8 *buffer[2];
120
121
static volatile UInt32 running = 0;
static CmpSoundHeader header;
122
123
static volatile Uint32 fill_me = 0;
124
static void
125
mix_buffer(SDL_AudioDevice * audio, UInt8 * buffer)
126
{
127
if (!audio->paused) {
128
#ifdef __MACOSX__
129
SDL_mutexP(audio->mixer_lock);
130
#endif
131
if (audio->convert.needed) {
132
133
134
135
audio->spec.callback(audio->spec.userdata,
(Uint8 *) audio->convert.buf,
audio->convert.len);
SDL_ConvertAudio(&audio->convert);
136
137
if (audio->convert.len_cvt != audio->spec.size) {
/* Uh oh... probably crashes here */ ;
138
}
139
SDL_memcpy(buffer, audio->convert.buf, audio->convert.len_cvt);
140
} else {
141
142
audio->spec.callback(audio->spec.userdata, buffer,
audio->spec.size);
143
}
144
#ifdef __MACOSX__
145
SDL_mutexV(audio->mixer_lock);
146
#endif
147
148
}
149
DecrementAtomic((SInt32 *) & need_to_mix);
150
151
}
152
static void
153
Mac_LockAudio(_THIS)
154
{
155
IncrementAtomic((SInt32 *) & audio_is_locked);
156
157
}
158
static void
159
Mac_UnlockAudio(_THIS)
160
161
{
SInt32 oldval;
162
163
oldval = DecrementAtomic((SInt32 *) & audio_is_locked);
164
if (oldval != 1) /* != 1 means audio is still locked. */
165
166
167
return;
/* Did we miss the chance to mix in an interrupt? Do it now. */
168
if (BitAndAtomic(0xFFFFFFFF, (UInt32 *) & need_to_mix)) {
169
170
171
172
173
/*
* Note that this could be a problem if you missed an interrupt
* while the audio was locked, and get preempted by a second
* interrupt here, but that means you locked for way too long anyhow.
*/
174
mix_buffer(this, buffer[fill_me]);
175
176
}
}
177
178
static void
179
callBackProc(SndChannel * chan, SndCommand * cmd_passed)
180
181
182
183
184
{
UInt32 play_me;
SndCommand cmd;
SDL_AudioDevice *audio = (SDL_AudioDevice *) chan->userInfo;
185
IncrementAtomic((SInt32 *) & need_to_mix);
186
187
188
189
190
191
192
193
194
195
196
197
198
fill_me = cmd_passed->param2; /* buffer that has just finished playing, so fill it */
play_me = !fill_me; /* filled buffer to play _now_ */
if (!audio->enabled) {
return;
}
/* queue previously mixed buffer for playback. */
header.samplePtr = (Ptr) buffer[play_me];
cmd.cmd = bufferCmd;
cmd.param1 = 0;
cmd.param2 = (long) &header;
199
SndDoCommand(chan, &cmd, 0);
200
201
memset(buffer[fill_me], 0, audio->spec.size);
202
203
204
205
206
/*
* if audio device isn't locked, mix the next buffer to be queued in
* the memory block that just finished playing.
*/
207
208
if (!BitAndAtomic(0xFFFFFFFF, (UInt32 *) & audio_is_locked)) {
mix_buffer(audio, buffer[fill_me]);
209
210
211
212
213
214
215
216
}
/* set this callback to run again when current buffer drains. */
if (running) {
cmd.cmd = callBackCmd;
cmd.param1 = 0;
cmd.param2 = play_me;
217
SndDoCommand(chan, &cmd, 0);
218
}
219
220
}
221
static int
222
Mac_OpenAudio(_THIS, SDL_AudioSpec * spec)
223
224
225
226
227
228
{
SndCallBackUPP callback;
int sample_bits;
int i;
long initOptions;
229
230
/* Very few conversions are required, but... */
231
switch (spec->format) {
232
case AUDIO_S8:
233
234
spec->format = AUDIO_U8;
break;
235
case AUDIO_U16LSB:
236
237
spec->format = AUDIO_S16LSB;
break;
238
case AUDIO_U16MSB:
239
240
241
spec->format = AUDIO_S16MSB;
break;
}
242
SDL_CalculateAudioSpec(spec);
243
244
/* initialize bufferCmd header */
245
246
memset(&header, 0, sizeof(header));
callback = (SndCallBackUPP) NewSndCallBackUPP(callBackProc);
247
248
249
sample_bits = spec->size / spec->samples / spec->channels * 8;
#ifdef DEBUG_AUDIO
250
251
252
fprintf(stderr,
"Audio format 0x%x, channels = %d, sample_bits = %d, frequency = %d\n",
spec->format, spec->channels, sample_bits, spec->freq);
253
#endif /* DEBUG_AUDIO */
254
255
header.numChannels = spec->channels;
256
257
258
259
260
header.sampleSize = sample_bits;
header.sampleRate = spec->freq << 16;
header.numFrames = spec->samples;
header.encode = cmpSH;
261
/* Note that we install the 16bitLittleEndian Converter if needed. */
262
if (spec->format == 0x8010) {
263
264
265
header.compressionID = fixedCompression;
header.format = k16BitLittleEndianFormat;
}
266
267
/* allocate 2 buffers */
268
for (i = 0; i < 2; i++) {
269
buffer[i] = (UInt8 *) malloc(sizeof(UInt8) * spec->size);
270
if (buffer[i] == NULL) {
271
SDL_OutOfMemory();
272
273
return (-1);
}
274
memset(buffer[i], 0, spec->size);
275
276
277
}
/* Create the sound manager channel */
278
channel = (SndChannelPtr) SDL_malloc(sizeof(*channel));
279
if (channel == NULL) {
280
SDL_OutOfMemory();
281
return (-1);
282
}
283
if (spec->channels >= 2) {
284
285
286
287
initOptions = initStereo;
} else {
initOptions = initMono;
}
288
channel->userInfo = (long) this;
289
channel->qLength = 128;
290
291
292
if (SndNewChannel(&channel, sampledSynth, initOptions, callback) != noErr) {
SDL_SetError("Unable to create audio channel");
SDL_free(channel);
293
channel = NULL;
294
return (-1);
295
}
296
297
298
299
300
301
302
/* start playback */
{
SndCommand cmd;
cmd.cmd = callBackCmd;
cmd.param2 = 0;
running = 1;
303
SndDoCommand(channel, &cmd, 0);
304
305
306
}
return 1;
307
308
}
309
static void
310
Mac_CloseAudio(_THIS)
311
312
313
314
315
316
317
{
int i;
running = 0;
if (channel) {
318
SndDisposeChannel(channel, true);
319
320
321
322
323
channel = NULL;
}
for (i = 0; i < 2; ++i) {
if (buffer[i]) {
324
SDL_free(buffer[i]);
325
326
327
328
329
buffer[i] = NULL;
}
}
}
330
331
#else /* !TARGET_API_MAC_CARBON && !USE_RYANS_SOUNDCODE */
332
static void
333
Mac_LockAudio(_THIS)
334
335
336
337
{
/* no-op. */
}
338
static void
339
Mac_UnlockAudio(_THIS)
340
341
342
343
{
/* no-op. */
}
344
345
346
347
348
/* This function is called by Sound Manager when it has exhausted one of
the buffers, so we'll zero it to silence and fill it with audio if
we're not paused.
*/
349
static pascal void
350
sndDoubleBackProc(SndChannelPtr chan, SndDoubleBufferPtr newbuf)
351
{
352
SDL_AudioDevice *audio = (SDL_AudioDevice *) newbuf->dbUserInfo[0];
353
354
/* If audio is quitting, don't do anything */
355
if (!audio->enabled) {
356
357
return;
}
358
memset(newbuf->dbSoundData, 0, audio->spec.size);
359
newbuf->dbNumFrames = audio->spec.samples;
360
361
if (!audio->paused) {
if (audio->convert.needed) {
362
363
364
365
audio->spec.callback(audio->spec.userdata,
(Uint8 *) audio->convert.buf,
audio->convert.len);
SDL_ConvertAudio(&audio->convert);
366
#if 0
367
368
if (audio->convert.len_cvt != audio->spec.size) {
/* Uh oh... probably crashes here */ ;
369
370
}
#endif
371
372
SDL_memcpy(newbuf->dbSoundData, audio->convert.buf,
audio->convert.len_cvt);
373
} else {
374
375
376
audio->spec.callback(audio->spec.userdata,
(Uint8 *) newbuf->dbSoundData,
audio->spec.size);
377
378
}
}
379
newbuf->dbFlags |= dbBufferReady;
380
381
}
382
static int
383
DoubleBufferAudio_Available(void)
384
385
386
387
388
389
{
int available;
NumVersion sndversion;
long response;
available = 0;
390
sndversion = SndSoundManagerVersion();
391
if (sndversion.majorRev >= 3) {
392
if (Gestalt(gestaltSoundAttr, &response) == noErr) {
393
if ((response & (1 << gestaltSndPlayDoubleBuffer))) {
394
395
396
397
available = 1;
}
}
} else {
398
if (Gestalt(gestaltSoundAttr, &response) == noErr) {
399
if ((response & (1 << gestaltHasASC))) {
400
401
402
403
available = 1;
}
}
}
404
return (available);
405
406
}
407
static void
408
Mac_CloseAudio(_THIS)
409
410
411
{
int i;
412
if (channel != NULL) {
413
/* Clean up the audio channel */
414
SndDisposeChannel(channel, true);
415
416
channel = NULL;
}
417
418
for (i = 0; i < 2; ++i) {
if (audio_buf[i]) {
419
SDL_free(audio_buf[i]);
420
421
422
423
424
audio_buf[i] = NULL;
}
}
}
425
static int
426
Mac_OpenAudio(_THIS, SDL_AudioSpec * spec)
427
428
429
430
431
432
433
434
{
SndDoubleBufferHeader2 audio_dbh;
int i;
long initOptions;
int sample_bits;
SndDoubleBackUPP doubleBackProc;
/* Check to make sure double-buffered audio is available */
435
436
if (!DoubleBufferAudio_Available()) {
SDL_SetError("Sound manager doesn't support double-buffering");
437
return (-1);
438
439
440
441
}
/* Very few conversions are required, but... */
switch (spec->format) {
442
case AUDIO_S8:
443
444
spec->format = AUDIO_U8;
break;
445
case AUDIO_U16LSB:
446
447
spec->format = AUDIO_S16LSB;
break;
448
case AUDIO_U16MSB:
449
450
451
spec->format = AUDIO_S16MSB;
break;
}
452
SDL_CalculateAudioSpec(spec);
453
454
/* initialize the double-back header */
455
456
SDL_memset(&audio_dbh, 0, sizeof(audio_dbh));
doubleBackProc = NewSndDoubleBackProc(sndDoubleBackProc);
457
sample_bits = spec->size / spec->samples / spec->channels * 8;
458
459
audio_dbh.dbhNumChannels = spec->channels;
460
audio_dbh.dbhSampleSize = sample_bits;
461
audio_dbh.dbhCompressionID = 0;
462
463
464
465
audio_dbh.dbhPacketSize = 0;
audio_dbh.dbhSampleRate = spec->freq << 16;
audio_dbh.dbhDoubleBack = doubleBackProc;
audio_dbh.dbhFormat = 0;
466
467
/* Note that we install the 16bitLittleEndian Converter if needed. */
468
if (spec->format == 0x8010) {
469
470
471
472
473
audio_dbh.dbhCompressionID = fixedCompression;
audio_dbh.dbhFormat = k16BitLittleEndianFormat;
}
/* allocate the 2 double-back buffers */
474
for (i = 0; i < 2; ++i) {
475
audio_buf[i] = SDL_calloc(1, sizeof(SndDoubleBuffer) + spec->size);
476
if (audio_buf[i] == NULL) {
477
SDL_OutOfMemory();
478
return (-1);
479
480
481
}
audio_buf[i]->dbNumFrames = spec->samples;
audio_buf[i]->dbFlags = dbBufferReady;
482
audio_buf[i]->dbUserInfo[0] = (long) this;
483
484
485
486
audio_dbh.dbhBufferPtr[i] = audio_buf[i];
}
/* Create the sound manager channel */
487
channel = (SndChannelPtr) SDL_malloc(sizeof(*channel));
488
if (channel == NULL) {
489
SDL_OutOfMemory();
490
return (-1);
491
}
492
if (spec->channels >= 2) {
493
494
495
496
497
498
initOptions = initStereo;
} else {
initOptions = initMono;
}
channel->userInfo = 0;
channel->qLength = 128;
499
500
501
if (SndNewChannel(&channel, sampledSynth, initOptions, 0L) != noErr) {
SDL_SetError("Unable to create audio channel");
SDL_free(channel);
502
channel = NULL;
503
return (-1);
504
}
505
506
/* Start playback */
507
if (SndPlayDoubleBuffer(channel, (SndDoubleBufferHeaderPtr) & audio_dbh)
508
!= noErr) {
509
SDL_SetError("Unable to play double buffered audio");
510
return (-1);
511
}
512
513
514
515
return 1;
}
516
#endif /* TARGET_API_MAC_CARBON || USE_RYANS_SOUNDCODE */
517
/* vi: set ts=4 sw=4 expandtab: */