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