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