1 /*
2 * madplay - MPEG audio decoder and player
3 * Copyright (C) 2000-2004 Robert Leslie
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program 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
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 * $Id: audio_carbon.c,v 1.4 2004/01/23 09:41:31 rob Exp $
20 */
21
22 # ifdef HAVE_CONFIG_H
23 # include "config.h"
24 # endif
25
26 # include "global.h"
27
28 # include <Carbon/Carbon.h>
29 # include <mad.h>
30
31 # include "gettext.h"
32
33 # include "audio.h"
34
35 static SndChannelPtr channel;
36 static audio_pcmfunc_t *audio_pcm;
37
38 static unsigned int config_channels;
39 static unsigned int config_speed;
40 static unsigned int config_precision;
41
42 # define NBUFFERS 16
43 # define NQUEUESAMPLES 1152
44
45 static struct buffer {
46 ExtSoundHeader header;
47 MPSemaphoreID semaphore;
48 unsigned int pcm_nsamples;
49 unsigned int pcm_length;
50 unsigned char pcm_data[MAX_NSAMPLES * 2 * 2];
51 } output[NBUFFERS];
52
53 static int bindex;
54
55 enum mode {
56 QUEUE,
57 IMMEDIATE
58 };
59
60 static
soundcmd(enum mode mode,unsigned short cmd,short param1,long param2)61 int soundcmd(enum mode mode, unsigned short cmd, short param1, long param2)
62 {
63 SndCommand command;
64
65 command.cmd = cmd;
66 command.param1 = param1;
67 command.param2 = param2;
68
69 switch (mode) {
70 case IMMEDIATE:
71 if (SndDoImmediate(channel, &command) != noErr) {
72 audio_error = _("SndDoImmediate() failed");
73 return -1;
74 }
75 break;
76
77 case QUEUE:
78 if (SndDoCommand(channel, &command, FALSE) != noErr) {
79 audio_error = _("SndDoCommand() failed");
80 return -1;
81 }
82 break;
83 }
84
85 return 0;
86 }
87
88 static
callback(SndChannelPtr channel,SndCommand * command)89 void callback(SndChannelPtr channel, SndCommand *command)
90 {
91 struct buffer *buffer = (struct buffer *) command->param2;
92
93 MPSignalSemaphore(buffer->semaphore);
94 }
95
96 static
wait(struct buffer * buffer)97 int wait(struct buffer *buffer)
98 {
99 if (MPWaitOnSemaphore(buffer->semaphore, kDurationForever) != noErr) {
100 audio_error = _("MPWaitOnSemaphore() failed");
101 return -1;
102 }
103
104 return 0;
105 }
106
107 static
init(struct audio_init * init)108 int init(struct audio_init *init)
109 {
110 int i;
111
112 channel = 0;
113
114 if (SndNewChannel(&channel, sampledSynth, 0, callback) != noErr) {
115 audio_error = _("SndNewChannel() failed");
116 return -1;
117 }
118
119 for (i = 0; i < NBUFFERS; ++i) {
120 if (MPCreateBinarySemaphore(&output[i].semaphore) != noErr) {
121 while (i--)
122 MPDeleteSemaphore(output[i].semaphore);
123
124 audio_error = _("failed to create synchronization object");
125 return -1;
126 }
127
128 output[i].pcm_nsamples = 0;
129 output[i].pcm_length = 0;
130 }
131
132 bindex = 0;
133
134 return 0;
135 }
136
137 static
set_pause(int flag)138 int set_pause(int flag)
139 {
140 static int paused;
141
142 if (flag != paused) {
143 paused = 0;
144
145 if (flag) {
146 if (soundcmd(IMMEDIATE, pauseCmd, 0, 0) == -1 ||
147 soundcmd(IMMEDIATE, quietCmd, 0, 0) == -1)
148 return -1;
149 }
150 else if (soundcmd(IMMEDIATE, resumeCmd, 0, 0) == -1)
151 return -1;
152
153 paused = flag;
154 }
155
156 return 0;
157 }
158
159 static
init_header(struct ExtSoundHeader * header,Ptr samples,unsigned int nsamples,unsigned int channels,unsigned int speed,unsigned int bits)160 void init_header(struct ExtSoundHeader *header, Ptr samples,
161 unsigned int nsamples, unsigned int channels,
162 unsigned int speed, unsigned int bits)
163 {
164 double dspeed = speed;
165
166 header->samplePtr = samples;
167 header->numChannels = channels;
168 header->sampleRate = FixRatio(speed, 1);
169 header->loopStart = 0;
170 header->loopEnd = 0;
171 header->encode = extSH;
172 header->baseFrequency = kMiddleC;
173 header->numFrames = nsamples;
174 dtox80(&dspeed, &header->AIFFSampleRate);
175 header->markerChunk = 0;
176 header->instrumentChunks = 0;
177 header->AESRecording = 0;
178 header->sampleSize = bits;
179 header->futureUse1 = 0;
180 header->futureUse2 = 0;
181 header->futureUse3 = 0;
182 header->futureUse4 = 0;
183 }
184
185 static
queue(struct buffer * buffer)186 int queue(struct buffer *buffer)
187 {
188 init_header(&buffer->header, buffer->pcm_data, buffer->pcm_nsamples,
189 config_channels, config_speed, config_precision);
190
191 if (soundcmd(QUEUE, bufferCmd, 0, (long) &buffer->header) == -1 ||
192 soundcmd(QUEUE, callBackCmd, 0, (long) buffer) == -1)
193 return -1;
194
195 return 0;
196 }
197
198 static
drain(void)199 int drain(void)
200 {
201 int result = 0;
202
203 if (output[bindex].pcm_nsamples) {
204 if (queue(&output[bindex]) == -1)
205 result = -1;
206
207 bindex = (bindex + 1) % NBUFFERS;
208 output[bindex].pcm_nsamples = 0;
209 }
210
211 return result;
212 }
213
214 static
config(struct audio_config * config)215 int config(struct audio_config *config)
216 {
217 unsigned int bitdepth;
218
219 if (set_pause(0) == -1)
220 return -1;
221
222 bitdepth = config->precision & ~7;
223 if (bitdepth == 0 || bitdepth > 16)
224 bitdepth = 16;
225
226 if (drain() == -1 ||
227 soundcmd(QUEUE, reInitCmd, 0, initNoDrop |
228 (config->channels == 1 ? initMono : initStereo)) == -1)
229 return -1;
230
231 switch (config->precision = bitdepth) {
232 case 8:
233 audio_pcm = audio_pcm_u8;
234 break;
235
236 case 16:
237 audio_pcm = audio_pcm_s16be;
238 break;
239 }
240
241 config_channels = config->channels;
242 config_speed = config->speed;
243 config_precision = config->precision;
244
245 return 0;
246 }
247
248 static
play(struct audio_play * play)249 int play(struct audio_play *play)
250 {
251 struct buffer *buffer;
252 unsigned int len;
253
254 if (set_pause(0) == -1)
255 return -1;
256
257 if (output[bindex].pcm_nsamples + play->nsamples > MAX_NSAMPLES &&
258 drain() == -1)
259 return -1;
260
261 buffer = &output[bindex];
262
263 /* wait for block to finish playing */
264
265 if (buffer->pcm_nsamples == 0) {
266 if (wait(buffer) == -1)
267 return -1;
268
269 buffer->pcm_length = 0;
270 }
271
272 /* prepare block */
273
274 len = audio_pcm(&buffer->pcm_data[buffer->pcm_length], play->nsamples,
275 play->samples[0], play->samples[1], play->mode, play->stats);
276
277 buffer->pcm_nsamples += play->nsamples;
278 buffer->pcm_length += len;
279
280 if (buffer->pcm_nsamples >= NQUEUESAMPLES &&
281 drain() == -1)
282 return -1;
283
284 return 0;
285 }
286
287 static
flush(void)288 int flush(void)
289 {
290 int i, result = 0;
291
292 if (soundcmd(IMMEDIATE, flushCmd, 0, 0) == -1)
293 result = -1;
294
295 for (i = 0; i < NBUFFERS; ++i)
296 MPSignalSemaphore(output[i].semaphore);
297
298 output[bindex].pcm_nsamples = 0;
299
300 return result;
301 }
302
303 static
stop(struct audio_stop * stop)304 int stop(struct audio_stop *stop)
305 {
306 int result;
307
308 result = set_pause(1);
309
310 if (result == 0 && stop->flush && flush() == -1)
311 result = -1;
312
313 return result;
314 }
315
316 static
finish(struct audio_finish * finish)317 int finish(struct audio_finish *finish)
318 {
319 int i, result = 0;
320
321 if (set_pause(0) == -1 || drain() == -1)
322 result = -1;
323
324 if (SndDisposeChannel(channel, FALSE) != noErr && result == 0) {
325 audio_error = _("SndDisposeChannel() failed");
326 result = -1;
327 }
328
329 for (i = 0; i < NBUFFERS; ++i) {
330 if (MPDeleteSemaphore(output[i].semaphore) != noErr && result == 0) {
331 audio_error = _("failed to delete synchronization object");
332 result = -1;
333 }
334 }
335
336 return result;
337 }
338
audio_carbon(union audio_control * control)339 int audio_carbon(union audio_control *control)
340 {
341 audio_error = 0;
342
343 switch (control->command) {
344 case AUDIO_COMMAND_INIT:
345 return init(&control->init);
346
347 case AUDIO_COMMAND_CONFIG:
348 return config(&control->config);
349
350 case AUDIO_COMMAND_PLAY:
351 return play(&control->play);
352
353 case AUDIO_COMMAND_STOP:
354 return stop(&control->stop);
355
356 case AUDIO_COMMAND_FINISH:
357 return finish(&control->finish);
358 }
359
360 return 0;
361 }
362