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