This repository has been archived by the owner on Feb 11, 2021. It is now read-only.
/
SDL_romaudio.c
325 lines (276 loc) · 9.07 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
#include "SDL_romaudio.h"
53
static int
54
SNDMGR_Available(void)
55
{
56
return (1);
57
58
59
}
60
61
62
63
#pragma options align=power
static volatile SInt32 audio_is_locked = 0;
static volatile SInt32 need_to_mix = 0;
64
65
static UInt8 *buffer[2];
66
67
static volatile UInt32 running = 0;
static CmpSoundHeader header;
68
69
static volatile Uint32 fill_me = 0;
70
71
72
static void
mix_buffer(SDL_AudioDevice * audio, UInt8 * buffer)
73
{
74
if (!audio->paused) {
75
#ifdef __MACOSX__
76
77
SDL_mutexP(audio->mixer_lock);
#endif
78
if (audio->convert.needed) {
79
audio->spec.callback(audio->spec.userdata,
80
81
(Uint8 *) audio->convert.buf,
audio->convert.len);
82
SDL_ConvertAudio(&audio->convert);
83
84
if (audio->convert.len_cvt != audio->spec.size) {
/* Uh oh... probably crashes here */ ;
85
}
86
SDL_memcpy(buffer, audio->convert.buf, audio->convert.len_cvt);
87
} else {
88
89
audio->spec.callback(audio->spec.userdata, buffer,
audio->spec.size);
90
}
91
#ifdef __MACOSX__
92
93
SDL_mutexV(audio->mixer_lock);
#endif
94
95
}
96
DecrementAtomic((SInt32 *) & need_to_mix);
97
98
}
99
static void
100
SNDMGR_LockDevice(_THIS)
101
{
102
IncrementAtomic((SInt32 *) & audio_is_locked);
103
104
}
105
static void
106
SNDMGR_UnlockDevice(_THIS)
107
108
{
SInt32 oldval;
109
110
111
oldval = DecrementAtomic((SInt32 *) & audio_is_locked);
if (oldval != 1) /* != 1 means audio is still locked. */
112
113
114
return;
/* Did we miss the chance to mix in an interrupt? Do it now. */
115
if (BitAndAtomic(0xFFFFFFFF, (UInt32 *) & need_to_mix)) {
116
117
118
119
120
/*
* 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.
*/
121
mix_buffer(this, buffer[fill_me]);
122
123
}
}
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
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);
}
166
167
}
168
static int
169
SNDMGR_OpenDevice(_THIS, const char *devname, int iscapture)
170
{
171
172
SDL_AudioSpec *spec = &this->spec;
SndChannelPtr channel = NULL;
173
174
175
176
SndCallBackUPP callback;
int sample_bits;
int i;
long initOptions;
177
178
179
180
181
182
183
184
185
186
187
/* 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: iterate through format matrix... */
188
/* Very few conversions are required, but... */
189
switch (spec->format) {
190
case AUDIO_S8:
191
192
spec->format = AUDIO_U8;
break;
193
case AUDIO_U16LSB:
194
195
spec->format = AUDIO_S16LSB;
break;
196
case AUDIO_U16MSB:
197
198
spec->format = AUDIO_S16MSB;
break;
199
200
201
case AUDIO_F32LSB:
spec->format = AUDIO_F32MSB;
break;
202
}
203
SDL_CalculateAudioSpec(&this->spec);
204
205
/* initialize bufferCmd header */
206
207
memset(&header, 0, sizeof(header));
callback = (SndCallBackUPP) NewSndCallBackUPP(callBackProc);
208
209
210
211
sample_bits = spec->size / spec->samples / spec->channels * 8;
#ifdef DEBUG_AUDIO
fprintf(stderr,
212
213
"Audio format 0x%x, channels = %d, sample_bits = %d, frequency = %d\n",
spec->format, spec->channels, sample_bits, spec->freq);
214
#endif /* DEBUG_AUDIO */
215
216
header.numChannels = spec->channels;
217
218
219
220
221
header.sampleSize = sample_bits;
header.sampleRate = spec->freq << 16;
header.numFrames = spec->samples;
header.encode = cmpSH;
222
/* Note that we install the 16bitLittleEndian Converter if needed. */
223
if (spec->format == AUDIO_S16LSB) {
224
225
header.compressionID = fixedCompression;
header.format = k16BitLittleEndianFormat;
226
227
228
229
230
231
232
233
234
} 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;
235
}
236
237
/* allocate 2 buffers */
238
239
240
for (i = 0; i < 2; i++) {
buffer[i] = (UInt8 *) malloc(sizeof(UInt8) * spec->size);
if (buffer[i] == NULL) {
241
SNDMGR_CloseDevice(this);
242
SDL_OutOfMemory();
243
return 0;
244
245
246
247
248
249
250
}
memset(buffer[i], 0, spec->size);
}
/* Create the sound manager channel */
channel = (SndChannelPtr) SDL_malloc(sizeof(*channel));
if (channel == NULL) {
251
SNDMGR_CloseDevice(this);
252
SDL_OutOfMemory();
253
return 0;
254
}
255
this->hidden->channel = channel;
256
if (spec->channels >= 2) {
257
258
259
260
initOptions = initStereo;
} else {
initOptions = initMono;
}
261
channel->userInfo = (long) this;
262
channel->qLength = 128;
263
if (SndNewChannel(&channel, sampledSynth, initOptions, callback) != noErr) {
264
SNDMGR_CloseDevice(this);
265
SDL_SetError("Unable to create audio channel");
266
return 0;
267
}
268
269
270
271
272
273
274
275
276
277
278
/* start playback */
{
SndCommand cmd;
cmd.cmd = callBackCmd;
cmd.param2 = 0;
running = 1;
SndDoCommand(channel, &cmd, 0);
}
return 1;
279
280
}
281
static void
282
SNDMGR_CloseDevice(_THIS)
283
284
285
286
287
{
int i;
running = 0;
288
289
290
if (this->hidden->channel) {
SndDisposeChannel(this->hidden->channel, true);
this->hidden->channel = NULL;
291
292
293
294
}
for (i = 0; i < 2; ++i) {
if (buffer[i]) {
295
SDL_free(buffer[i]);
296
297
298
buffer[i] = NULL;
}
}
299
300
SDL_free(this->hidden);
this->hidden = NULL;
301
302
}
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
static int
SNDMGR_Init(SDL_AudioDriverImpl *impl)
{
/* Set the function pointers */
impl->OpenDevice = SNDMGR_OpenDevice;
impl->CloseDevice = SNDMGR_CloseDevice;
impl->ProvidesOwnCallbackThread = 1;
impl->OnlyHasDefaultOutputDevice = 1;
#ifndef __MACOSX__ /* Mac OS X uses threaded audio, so normal thread code is okay */
impl->LockDevice = SNDMGR_LockDevice;
impl->UnlockDevice = SNDMGR_UnlockDevice;
#endif
return 1;
}
AudioBootStrap SNDMGR_bootstrap = {
"sndmgr", SDL_MACOS_NAME " SoundManager",
SNDMGR_Available, SNDMGR_Init, 0
};
325
/* vi: set ts=4 sw=4 expandtab: */