This repository has been archived by the owner on Feb 11, 2021. It is now read-only.
/
SDL_romaudio.c
352 lines (295 loc) · 9.7 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
/* This should work on PowerPC and Intel Mac OS X, and Carbonized Mac OS 9. */
26
#if defined(__APPLE__) && defined(__MACH__)
27
# define SDL_MACOS_NAME "Mac OS X"
28
29
# include <Carbon/Carbon.h>
#elif TARGET_API_MAC_CARBON && (UNIVERSAL_INTERFACES_VERSION > 0x0335)
30
# define SDL_MACOS_NAME "Mac OS 9"
31
32
# include <Carbon.h>
#else
33
# define SDL_MACOS_NAME "Mac OS 9"
34
# include <Sound.h> /* SoundManager interface */
35
# include <Gestalt.h>
36
# include <DriverServices.h>
37
38
#endif
39
#if !defined(NewSndCallBackUPP) && (UNIVERSAL_INTERFACES_VERSION < 0x0335)
40
#if !defined(NewSndCallBackProc) /* avoid circular redefinition... */
41
42
#define NewSndCallBackUPP NewSndCallBackProc
#endif
43
44
45
#if !defined(NewSndCallBackUPP)
#define NewSndCallBackUPP NewSndCallBackProc
#endif
46
47
#endif
48
#include "SDL_audio.h"
49
50
#include "../SDL_audio_c.h"
#include "../SDL_sysaudio.h"
51
52
53
54
#include "SDL_romaudio.h"
/* Audio driver functions */
55
56
57
58
static void SNDMGR_CloseAudio(_THIS);
static int SNDMGR_OpenAudio(_THIS, const char *devname, int iscapture);
static void SNDMGR_LockAudio(_THIS);
static void SNDMGR_UnlockAudio(_THIS);
59
60
61
62
/* Audio driver bootstrap functions */
63
static int
64
SNDMGR_Available(void)
65
{
66
return (1);
67
68
69
}
70
71
static int
SNDMGR_Init(SDL_AudioDriverImpl *impl)
72
73
{
/* Set the function pointers */
74
75
76
77
impl->OpenAudio = SNDMGR_OpenAudio;
impl->CloseAudio = SNDMGR_CloseAudio;
impl->LockAudio = SNDMGR_LockAudio;
impl->UnlockAudio = SNDMGR_UnlockAudio;
78
79
#ifdef __MACOSX__ /* Mac OS X uses threaded audio, so normal thread code is okay */
80
81
impl->LockAudio = NULL;
impl->UnlockAudio = NULL;
82
#endif
83
84
return 1;
85
86
87
}
AudioBootStrap SNDMGR_bootstrap = {
88
"sndmgr", SDL_MACOS_NAME " SoundManager",
89
SNDMGR_Available, SNDMGR_Init
90
91
};
92
93
#pragma options align=power
94
static volatile int audio_is_opened = 0;
95
96
static volatile SInt32 audio_is_locked = 0;
static volatile SInt32 need_to_mix = 0;
97
98
static UInt8 *buffer[2];
99
100
static volatile UInt32 running = 0;
static CmpSoundHeader header;
101
102
static volatile Uint32 fill_me = 0;
103
104
static void
mix_buffer(SDL_AudioDevice * audio, UInt8 * buffer)
105
{
106
if (!audio->paused) {
107
#ifdef __MACOSX__
108
109
SDL_mutexP(audio->mixer_lock);
#endif
110
if (audio->convert.needed) {
111
audio->spec.callback(audio->spec.userdata,
112
113
(Uint8 *) audio->convert.buf,
audio->convert.len);
114
SDL_ConvertAudio(&audio->convert);
115
116
if (audio->convert.len_cvt != audio->spec.size) {
/* Uh oh... probably crashes here */ ;
117
}
118
SDL_memcpy(buffer, audio->convert.buf, audio->convert.len_cvt);
119
} else {
120
121
audio->spec.callback(audio->spec.userdata, buffer,
audio->spec.size);
122
}
123
#ifdef __MACOSX__
124
125
SDL_mutexV(audio->mixer_lock);
#endif
126
127
}
128
DecrementAtomic((SInt32 *) & need_to_mix);
129
130
}
131
static void
132
SNDMGR_LockAudio(_THIS)
133
{
134
IncrementAtomic((SInt32 *) & audio_is_locked);
135
136
}
137
static void
138
SNDMGR_UnlockAudio(_THIS)
139
140
{
SInt32 oldval;
141
142
143
oldval = DecrementAtomic((SInt32 *) & audio_is_locked);
if (oldval != 1) /* != 1 means audio is still locked. */
144
145
146
return;
/* Did we miss the chance to mix in an interrupt? Do it now. */
147
if (BitAndAtomic(0xFFFFFFFF, (UInt32 *) & need_to_mix)) {
148
149
150
151
152
/*
* 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.
*/
153
mix_buffer(this, buffer[fill_me]);
154
155
}
}
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
static void
callBackProc(SndChannel * chan, SndCommand * cmd_passed)
{
UInt32 play_me;
SndCommand cmd;
SDL_AudioDevice *audio = (SDL_AudioDevice *) chan->userInfo;
IncrementAtomic((SInt32 *) & need_to_mix);
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;
SndDoCommand(chan, &cmd, 0);
memset(buffer[fill_me], 0, audio->spec.size);
/*
* if audio device isn't locked, mix the next buffer to be queued in
* the memory block that just finished playing.
*/
if (!BitAndAtomic(0xFFFFFFFF, (UInt32 *) & audio_is_locked)) {
mix_buffer(audio, buffer[fill_me]);
}
/* set this callback to run again when current buffer drains. */
if (running) {
cmd.cmd = callBackCmd;
cmd.param1 = 0;
cmd.param2 = play_me;
SndDoCommand(chan, &cmd, 0);
}
198
199
}
200
static int
201
SNDMGR_OpenAudio(_THIS, const char *devname, int iscapture)
202
{
203
204
SDL_AudioSpec *spec = &this->spec;
SndChannelPtr channel = NULL;
205
206
207
208
SndCallBackUPP callback;
int sample_bits;
int i;
long initOptions;
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
if (audio_is_opened) {
SDL_SetError("SoundManager driver doesn't support multiple opens");
return 0;
}
if (iscapture) {
SDL_SetError("SoundManager driver doesn't support recording");
return 0;
}
/* !!! FIXME: ignore devname? */
/* Initialize all variables that we clean on shutdown */
this->hidden = (struct SDL_PrivateAudioData *)
SDL_malloc((sizeof *this->hidden));
if (this->hidden == NULL) {
SDL_OutOfMemory();
return 0;
}
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
/* !!! FIXME: check devname and iscapture... */
/* !!! FIXME: iterate through format matrix... */
234
/* Very few conversions are required, but... */
235
switch (spec->format) {
236
case AUDIO_S8:
237
238
spec->format = AUDIO_U8;
break;
239
case AUDIO_U16LSB:
240
241
spec->format = AUDIO_S16LSB;
break;
242
case AUDIO_U16MSB:
243
244
spec->format = AUDIO_S16MSB;
break;
245
246
247
case AUDIO_F32LSB:
spec->format = AUDIO_F32MSB;
break;
248
}
249
SDL_CalculateAudioSpec(&this->spec);
250
251
/* initialize bufferCmd header */
252
253
memset(&header, 0, sizeof(header));
callback = (SndCallBackUPP) NewSndCallBackUPP(callBackProc);
254
255
256
257
sample_bits = spec->size / spec->samples / spec->channels * 8;
#ifdef DEBUG_AUDIO
fprintf(stderr,
258
259
"Audio format 0x%x, channels = %d, sample_bits = %d, frequency = %d\n",
spec->format, spec->channels, sample_bits, spec->freq);
260
#endif /* DEBUG_AUDIO */
261
262
header.numChannels = spec->channels;
263
264
265
266
267
header.sampleSize = sample_bits;
header.sampleRate = spec->freq << 16;
header.numFrames = spec->samples;
header.encode = cmpSH;
268
/* Note that we install the 16bitLittleEndian Converter if needed. */
269
if (spec->format == AUDIO_S16LSB) {
270
271
header.compressionID = fixedCompression;
header.format = k16BitLittleEndianFormat;
272
273
274
275
276
277
278
279
280
} else if (spec->format == AUDIO_S32MSB) {
header.compressionID = fixedCompression;
header.format = k32BitFormat;
} else if (spec->format == AUDIO_S32LSB) {
header.compressionID = fixedCompression;
header.format = k32BitLittleEndianFormat;
} else if (spec->format == AUDIO_F32MSB) {
header.compressionID = fixedCompression;
header.format = kFloat32Format;
281
}
282
283
/* allocate 2 buffers */
284
285
286
for (i = 0; i < 2; i++) {
buffer[i] = (UInt8 *) malloc(sizeof(UInt8) * spec->size);
if (buffer[i] == NULL) {
287
SNDMGR_CloseAudio(this);
288
SDL_OutOfMemory();
289
return 0;
290
291
292
293
294
295
296
}
memset(buffer[i], 0, spec->size);
}
/* Create the sound manager channel */
channel = (SndChannelPtr) SDL_malloc(sizeof(*channel));
if (channel == NULL) {
297
SNDMGR_CloseAudio(this);
298
SDL_OutOfMemory();
299
return 0;
300
}
301
this->hidden->channel = channel;
302
if (spec->channels >= 2) {
303
304
305
306
initOptions = initStereo;
} else {
initOptions = initMono;
}
307
channel->userInfo = (long) this;
308
channel->qLength = 128;
309
if (SndNewChannel(&channel, sampledSynth, initOptions, callback) != noErr) {
310
SNDMGR_CloseAudio(this);
311
SDL_SetError("Unable to create audio channel");
312
return 0;
313
}
314
315
316
317
318
319
320
321
322
323
/* start playback */
{
SndCommand cmd;
cmd.cmd = callBackCmd;
cmd.param2 = 0;
running = 1;
SndDoCommand(channel, &cmd, 0);
}
324
audio_is_opened = 1;
325
return 1;
326
327
}
328
static void
329
SNDMGR_CloseAudio(_THIS)
330
331
332
333
334
335
{
int i;
running = 0;
336
337
338
if (this->hidden->channel) {
SndDisposeChannel(this->hidden->channel, true);
this->hidden->channel = NULL;
339
340
341
342
}
for (i = 0; i < 2; ++i) {
if (buffer[i]) {
343
SDL_free(buffer[i]);
344
345
346
buffer[i] = NULL;
}
}
347
348
349
SDL_free(this->hidden);
this->hidden = NULL;
audio_is_opened = 0;
350
351
}
352
/* vi: set ts=4 sw=4 expandtab: */