1 /*
2 TiMidity++ -- MIDI to WAVE converter and player
3 Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp>
4 Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
20 Macintosh interface for TiMidity
21 by T.Nogami <t-nogami@happy.email.ne.jp>
22
23 mac_a.c
24 Macintosh audio driver
25 */
26
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif /* HAVE_CONFIG_H */
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <math.h>
35
36 #include <Sound.h>
37 #include <Threads.h>
38
39 #include "timidity.h"
40 #include "common.h"
41 #include "output.h"
42 #include "controls.h"
43 #include "miditrace.h"
44
45 #include "mac_main.h"
46 #include "mac_util.h"
47
48 extern int default_play_event(void *);
49 static int open_output(void); /* 0=success, 1=warning, -1=fatal error */
50 static void close_output(void);
51 static int output_data(char *buf, int32 nbytes);
52 static int acntl(int request, void *arg);
53 static int detect(void);
54
55 /* export the playback mode */
56
57 #define dpm mac_play_mode
58
59 PlayMode dpm = {
60 44100, PE_16BIT|PE_SIGNED, PF_PCM_STREAM|PF_CAN_TRACE|PF_BUFF_FRAGM_OPT,
61 -1, //file descriptor
62 {0}, /* default: get all the buffer fragments you can */
63 "Mac audio driver", 'm',
64 "", //device file name
65 open_output,
66 close_output,
67 output_data,
68 acntl,
69 detect
70 };
71
72 #define SOUND_MANAGER_3_OR_LATER 1 // Always available on System 7.5 or later
73
74 #if SOUND_MANAGER_3_OR_LATER
75 #define MySoundHeader CmpSoundHeader
76 #else
77 #define MySoundHeader ExtSoundHeader
78 #endif
79
80 #define FLUSH_END -1
81
82 static MySoundHeader *soundHeader;
83 static char **soundBuffer, *soundBufferMasterPtr;
84 static int bufferSize, bufferCount;
85 static int nextBuf, filling_flag;
86
87 SndChannelPtr gSndCannel=0;
88 short mac_amplitude=0x00FF;
89 volatile static int32 play_counter;
90 volatile int mac_buf_using_num, mac_flushing_flag;
91
92 /* ******************************************************************* */
MyCreateSndChannel(short synth,long initOptions,SndCallBackUPP userRoutine,short queueLength)93 static SndChannelPtr MyCreateSndChannel(short synth, long initOptions,
94 SndCallBackUPP userRoutine, short queueLength)
95 {
96 SndChannelPtr mySndChan; // {pointer to a sound channel}
97 OSErr myErr;
98 // {Allocate memory for sound channel.}
99 mySndChan = (SndChannelPtr)malloc(
100 sizeof(SndChannel) + (queueLength-stdQLength)*sizeof(SndCommand) );
101 if( mySndChan != 0 ){
102 mySndChan->qLength = queueLength; // {set number of commands in queue}
103 // {Create a new sound channel.}
104 myErr = SndNewChannel(&mySndChan, synth, initOptions, userRoutine);
105 if( myErr != noErr ){ // {couldn't allocate channel}
106 free(mySndChan); // {free memory already allocated}
107 mySndChan = 0; // {return NIL}
108 }
109 else
110 mySndChan->userInfo = 0; // {reset userInfo field}
111 }
112 return mySndChan; // {return new sound channel}
113 }
114
115 // ***************************************
initCounter()116 static void initCounter()
117 {
118 play_counter=0;
119 mac_buf_using_num=0;
120 filling_flag=0;
121 mac_flushing_flag=0;
122 }
123
callback(SndChannelPtr chan,SndCommand * cmd)124 static pascal void callback(SndChannelPtr chan, SndCommand * cmd)
125 {
126 if( cmd->param2==FLUSH_END ){
127 mac_flushing_flag=0;
128 }else{
129 play_counter+= cmd->param2;
130 }
131 mac_buf_using_num--;
132 }
133
GetCurrentFrameSize(void)134 static int GetCurrentFrameSize(void)
135 {
136 int frameSize;
137
138 frameSize = (dpm.encoding & PE_MONO) ? 1 : 2;
139 if (dpm.encoding & PE_16BIT)
140 frameSize *= 2;
141 else if (dpm.encoding & PE_24BIT)
142 frameSize *= 3;
143 return frameSize;
144 }
145
CleanupDriverMemory(void)146 static void CleanupDriverMemory(void)
147 {
148 if (gSndCannel != NULL)
149 {
150 SndDisposeChannel(gSndCannel, 0);
151 free(gSndCannel);
152 gSndCannel = NULL;
153 }
154 free(soundBuffer);
155 soundBuffer = NULL;
156 free(soundHeader);
157 soundHeader = NULL;
158 free(soundBufferMasterPtr);
159 soundBufferMasterPtr = NULL;
160 }
161
open_output(void)162 static int open_output (void)
163 {
164 int i, include_enc, exclude_enc, sndBufferSize, sndBufferCount;
165 SndCommand theCmd;
166 char *sndBufferPtr;
167
168 if (dpm.fd != -1)
169 return -1;
170 // buffer fragments
171 sndBufferCount = dpm.extra_param[0];
172 if (sndBufferCount == 0) // default
173 sndBufferCount = audio_buffer_bits >= 11 ? 256 : 512;
174 else if (sndBufferCount < 64)
175 sndBufferCount = 64;
176 bufferCount = sndBufferCount;
177 // allocate channel
178 gSndCannel = MyCreateSndChannel(sampledSynth, 0,
179 NewSndCallBackUPP(callback), ((sndBufferCount - 1) * 2));
180 if (gSndCannel == NULL)
181 mac_ErrorExit("\pCan't open Sound Channel");
182 // encoding
183 if (dpm.encoding & (PE_16BIT | PE_24BIT))
184 include_enc = PE_SIGNED, exclude_enc = 0;
185 else
186 include_enc = 0, exclude_enc = PE_SIGNED;
187 exclude_enc |= PE_ULAW | PE_ALAW | PE_BYTESWAP;
188 dpm.encoding = validate_encoding(dpm.encoding, include_enc, exclude_enc);
189 // allocate buffer
190 bufferSize = sndBufferSize = audio_buffer_size * GetCurrentFrameSize();
191 if ((soundBuffer = (char **)malloc(sndBufferCount * sizeof(char *))) == NULL)
192 goto bail;
193 if ((soundHeader = (MySoundHeader *)malloc(sndBufferCount * sizeof(MySoundHeader))) == NULL)
194 goto bail;
195 if ((sndBufferPtr = malloc(sndBufferSize * sndBufferCount)) == NULL)
196 goto bail;
197 soundBufferMasterPtr = sndBufferPtr;
198 nextBuf = 0;
199 // make sound headers
200 for(i = 0; i < sndBufferCount; i++)
201 {
202 MySoundHeader *header = &soundHeader[i];
203
204 header->samplePtr = soundBuffer[i] = sndBufferPtr;
205 header->loopStart = header->loopEnd = 0;
206 header->encode = SOUND_MANAGER_3_OR_LATER ? cmpSH : extSH;
207 header->baseFrequency = kMiddleC;
208 header->markerChunk = NULL;
209 #if SOUND_MANAGER_3_OR_LATER // supports fixedCompression
210 header->futureUse2 = 0;
211 header->stateVars = NULL;
212 header->leftOverSamples = NULL;
213 header->compressionID = fixedCompression;
214 header->packetSize = 0;
215 header->snthID = 0;
216 #else
217 header->instrumentChunks = header->AESRecording = NULL;
218 header->futureUse1 = header->futureUse2 = header->futureUse3 = header->futureUse4 = 0;
219 #endif
220 sndBufferPtr += sndBufferSize;
221 }
222 theCmd.cmd=ampCmd; /*setting volume*/
223 theCmd.param1=mac_amplitude;
224 SndDoCommand(gSndCannel, &theCmd, 0);
225 initCounter();
226 #ifdef MAC_INITIAL_FILLING
227 do_initial_filling=1;
228 #else
229 do_initial_filling=0;
230 #endif
231 dpm.fd = 0;
232 return 0;
233 bail:
234 CleanupDriverMemory();
235 return -1;
236 }
237
filling_end()238 static void filling_end()
239 {
240 if( filling_flag && do_initial_filling){
241 filling_flag=0;
242 if( skin_state!=PAUSE ){
243 SndCommand theCmd;
244 theCmd.cmd=resumeCmd; SndDoImmediate(gSndCannel, &theCmd);
245 }
246 }
247 if( gCursorIsWatch ){
248 InitCursor(); gCursorIsWatch=false;
249 }
250 }
251
QuingSndCommand(SndChannelPtr chan,const SndCommand * cmd)252 static void QuingSndCommand(SndChannelPtr chan, const SndCommand *cmd)
253 {
254 OSErr err;
255
256 for(;;)/* wait for successful quing */
257 {
258 err= SndDoCommand(chan, cmd, 1);
259 if( err==noErr ){ gBusy=true; break; }/*buffer has more rooms*/
260 else if( err==queueFull )
261 {
262 gBusy=false;
263 //end of INITIAL FILLING
264 #ifdef MAC_INITIAL_FILLING
265 filling_end();
266 #endif
267 trace_loop();
268 YieldToAnyThread();
269 }
270 else /*queueFull �ȊO��err�Ȃ�I��*/
271 mac_ErrorExit("\pSound out error--quit");
272 }
273 }
274
output_data(char * buf,int32 nbytes)275 static int output_data (char *buf, int32 nbytes)
276 {
277 short numChannels, sampleSize;
278 int32 samples, rest;
279 MySoundHeader *header;
280 OSType codec;
281 SndCommand theCmd;
282 int frameSize;
283
284 if( gCursorIsWatch ){
285 InitCursor(); gCursorIsWatch=false;
286 }
287
288 #ifdef MAC_INITIAL_FILLING // start INITIAL FILLING
289 if( play_counter==0 && filling_flag==0 && do_initial_filling){
290 filling_flag=1;
291 theCmd.cmd=pauseCmd; SndDoImmediate(gSndCannel, &theCmd);
292 }
293 #endif
294
295 if (dpm.encoding & PE_MONO)
296 numChannels = 1, frameSize = 1;
297 else /* Stereo sample */
298 numChannels = 2, frameSize = 2;
299
300 #if SOUND_MANAGER_3_OR_LATER
301 if (dpm.encoding & PE_16BIT)
302 sampleSize = 16, frameSize *= 2, codec = k16BitBigEndianFormat; // kSoundNotCompressed
303 else if (dpm.encoding & PE_24BIT)
304 sampleSize = 24, frameSize *= 3, codec = k24BitFormat;
305 else
306 sampleSize = 8, codec = k8BitOffsetBinaryFormat; // kSoundNotCompressed
307 #else
308 if (dpm.encoding & PE_16BIT)
309 sampleSize = 16, frameSize *= 2;
310 else if (dpm.encoding & PE_24BIT)
311 mac_ErrorExit("\pThis build doesn't support 24-bit audio.");
312 else
313 sampleSize = 8;
314 #endif
315
316 rest = nbytes;
317 do {
318 header = &soundHeader[nextBuf];
319
320 nbytes = rest;
321 if (nbytes > bufferSize)
322 {
323 samples = bufferSize / frameSize;
324 nbytes = samples * frameSize;
325 }
326 else
327 samples = nbytes / frameSize;
328 rest -= nbytes;
329
330 header->numChannels = numChannels;
331 header->sampleRate = dpm.rate << 16;
332 header->numFrames = samples;
333 //header->AIFFSampleRate = 0; // unused
334 #if SOUND_MANAGER_3_OR_LATER
335 header->format = codec;
336 #endif
337 header->sampleSize = sampleSize;
338 BlockMoveData(buf, soundBuffer[nextBuf], nbytes);
339 buf += nbytes;
340
341 theCmd.cmd= bufferCmd;
342 theCmd.param2=(long)header;
343
344 QuingSndCommand(gSndCannel, &theCmd);
345 mac_buf_using_num++;
346 if (++nextBuf >= bufferCount)
347 nextBuf = 0;
348
349 theCmd.cmd= callBackCmd; // post set
350 theCmd.param1= 0;
351 theCmd.param2= samples;
352 QuingSndCommand(gSndCannel, &theCmd);
353 } while(rest > 0);
354 return 0; /*good*/
355 }
356
fade_output()357 static void fade_output()
358 {
359 unsigned int fade_start_tick=TickCount();
360 int i;
361 SndCommand theCmd;
362
363 for( i=0; i<=30; i++ ){
364 theCmd.cmd=ampCmd;
365 theCmd.param1=mac_amplitude*(30-i)/30; /*amplitude->0*/
366 SndDoImmediate(gSndCannel, &theCmd);
367 while( TickCount() < fade_start_tick+i )
368 YieldToAnyThread();
369 }
370 }
371
purge_output(void)372 static void purge_output (void)
373 {
374 OSErr err;
375 SndCommand theCmd;
376 #if FADE_AT_PURGE
377 if( skin_state==PLAYING ) fade_output();
378 #endif
379 theCmd.cmd=flushCmd; /*clear buffer*/
380 err= SndDoImmediate(gSndCannel, &theCmd);
381 theCmd.cmd=quietCmd;
382 err= SndDoImmediate(gSndCannel, &theCmd);
383
384 theCmd.cmd=ampCmd;
385 theCmd.param1=0; /*amplitude->0*/
386 SndDoImmediate(gSndCannel, &theCmd);
387
388 theCmd.cmd=resumeCmd;
389 err= SndDoImmediate(gSndCannel, &theCmd);
390
391 theCmd.cmd=waitCmd;
392 theCmd.param1=2000*0.5; /* wait 0.5 sec */
393 SndDoCommand(gSndCannel, &theCmd, 1);
394
395 theCmd.cmd=ampCmd;
396 theCmd.param1=mac_amplitude;
397 SndDoCommand(gSndCannel, &theCmd,0);
398
399 filling_end();
400 initCounter();
401 }
402
close_output(void)403 static void close_output (void)
404 {
405 if (dpm.fd == -1)
406 return;
407 purge_output();
408 CleanupDriverMemory();
409 initCounter();
410 dpm.fd = -1;
411 }
412
flush_output(void)413 static int flush_output (void)
414 {
415 int ret=RC_NONE;
416 SndCommand theCmd;
417
418 mac_flushing_flag=1;
419 theCmd.cmd= callBackCmd;
420 theCmd.param1= 0;
421 theCmd.param2= FLUSH_END;
422 QuingSndCommand(gSndCannel, &theCmd);
423
424 filling_end();
425 for(;;){
426 trace_loop();
427 YieldToAnyThread();
428 //ctl->current_time(current_samples());
429 if( ! mac_flushing_flag ){ //end of midi
430 ret= RC_NONE;
431 break;
432 }else if( mac_rc!=RC_NONE ){
433 ret= mac_rc;
434 break;
435 }
436 }
437 initCounter();
438 return ret;
439 }
440
current_samples(void)441 static int32 current_samples(void)
442 {
443 return play_counter;
444 }
445
acntl(int request,void * arg)446 static int acntl(int request, void * arg)
447 {
448 switch(request)
449 {
450 case PM_REQ_DISCARD:
451 purge_output();
452 return 0;
453 case PM_REQ_FLUSH:
454 case PM_REQ_OUTPUT_FINISH:
455 flush_output();
456 return 0;
457 case PM_REQ_GETQSIZ:
458 *(int32*)arg = bufferCount * bufferSize;
459 return 0;
460 case PM_REQ_GETSAMPLES:
461 *(int*)arg= current_samples();
462 return 0;
463 case PM_REQ_PLAY_START:
464 initCounter();
465 return 0;
466 }
467 return -1;
468 }
469
detect(void)470 static int detect(void)
471 {
472 return 1; // assume it is available.
473 }
474
475 /* ************************************************************* */
476
477
478