1 /*	MikMod sound library
2 	(c) 1998, 1999, 2000 Miodrag Vallat and others - see file AUTHORS for
3 	complete list.
4 
5 	This library is free software; you can redistribute it and/or modify
6 	it under the terms of the GNU Library General Public License as
7 	published by the Free Software Foundation; either version 2 of
8 	the License, or (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 Library General Public License for more details.
14 
15 	You should have received a copy of the GNU Library General Public
16 	License along with this library; if not, write to the Free Software
17 	Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18 	02111-1307, USA.
19 */
20 
21 /*==============================================================================
22 
23   $Id$
24 
25   Driver for output to the Macintosh Sound Manager
26 
27 ==============================================================================*/
28 
29 /*
30 	Written by Anders F Bjoerklund <afb@algonet.se>
31 
32 	Based on free code by:
33 	- Antoine Rosset <RossetAntoine@bluewin.ch> (author of PlayerPRO)
34 	- John Stiles <stiles@emulation.net>
35 	- Pierre-Olivier Latour <pol@french-touch.net>
36 
37 	This code uses two different ways of filling the buffers:
38 	- Classic code uses SndPlayDoubleBuffer callbacks
39 	- Carbon code uses SndCallBacks with Deferred Tasks
40 
41 	Updated by Axel Wefers <awe@fruitz-of-dojo.de>:
42 	- changed code for compatibility with ProjectBuilder/OSX:
43 	- "NewSndCallBackProc()" to "NewSndCallBackUPP()".
44 	- "NewDeferredTaskProc()" to "NewDeferredTaskUPP()".
45 	- added some conditionals to avoid compiler warnings.
46 
47 	Updated again in 2004 by afb, to fix some bugs:
48 	- deadlock in Player_Paused, when using HAVE_PTHREAD
49 	  (since it is now using the global "vars" MUTEX too)
50 	- playback was wrong speed when running under CarbonLib
51 	  (due to Deferred Tasks having lame latencies there)
52 	- proper playing of partially filled buffers too
53 */
54 
55 #ifdef HAVE_CONFIG_H
56 #include "config.h"
57 #endif
58 
59 #include "mikmod_internals.h"
60 
61 #ifdef DRV_MAC
62 
63 #if defined(__APPLE__) && defined(__MACH__)
64 
65 #include <Carbon/Carbon.h>
66 
67 #elif TARGET_API_MAC_CARBON && (UNIVERSAL_INTERFACES_VERSION >= 0x0335)
68 
69 #include <Carbon.h>
70 
71 #else
72 
73 #include <Sound.h>
74 #include <OSUtils.h>
75 #include <Gestalt.h>
76 
77 #endif
78 
79 #ifndef TARGET_API_MAC_CARBON
80 #define TARGET_API_MAC_CARBON	TARGET_CARBON
81 #endif
82 
83 #ifndef TARGET_CPU_68K
84 #define TARGET_CPU_68K	GENERATING68K
85 #endif
86 
87 #if TARGET_API_MAC_CARBON
88 #define USE_SNDDOUBLEBUFFER		0
89 #else
90 #define USE_SNDDOUBLEBUFFER		1
91 #endif
92 
93 #if TARGET_API_MAC_CARBON
94 #define USE_DEFERREDTASKS		0
95 #else
96 #define USE_DEFERREDTASKS		1
97 #endif
98 
99 #define SOUND_BUFFER_SIZE		4096L
100 
101 static SndChannelPtr			soundChannel = NULL;	/* pointer to a sound channel */
102 
103 #if USE_SNDDOUBLEBUFFER
104 static SndDoubleBufferHeader	doubleHeader;			/* pointer to double buffers  */
105 #else
106 static SndCallBackUPP 			sndCallBack = NULL;
107 static ExtSoundHeader			sndHeader;		/* a sound manager bufferCmd header */
108 
109 static Ptr				sndBuffer1 = NULL;
110 static Ptr				sndBuffer2 = NULL;
111 static Ptr				currentBuffer;
112 static long				currentFrames;
113 
114 #if USE_DEFERREDTASKS
115 static DeferredTask			dtask;			/* deferred task record */
116 static volatile Boolean			deferredTaskFired = true;
117 static volatile Boolean			deferredTaskDone = true;
118 #endif
119 
120 #endif /* USE_SNDDOUBLEBUFFER */
121 
122 #define FILL_BUFFER(_buffer_data,_buffer_size,_bytes) \
123 	MUTEX_LOCK(vars); \
124 	if (Player_Paused_internal()) \
125 		_bytes=VC_SilenceBytes((SBYTE*)_buffer_data,(ULONG)_buffer_size); \
126 	else	_bytes=VC_WriteBytes((SBYTE*)_buffer_data,(ULONG)_buffer_size); \
127 	MUTEX_UNLOCK(vars);
128 
129 
130 #if USE_SNDDOUBLEBUFFER
131 
132 /* DoubleBackProc, called at interrupt time */
MyDoubleBackProc(SndChannelPtr channel,SndDoubleBufferPtr doubleBuffer)133 static pascal void MyDoubleBackProc(SndChannelPtr channel,SndDoubleBufferPtr doubleBuffer)
134 {
135 #ifndef GCC
136 #pragma unused(channel)
137 #endif
138 	long written;
139 #if TARGET_CPU_68K
140 	long oldA5=SetA5(doubleBuffer->dbUserInfo[0]);
141 #endif
142 
143 	FILL_BUFFER(doubleBuffer->dbSoundData, SOUND_BUFFER_SIZE, written)
144 
145 	if (doubleHeader.dbhNumChannels==2) written>>=1;
146 	if (doubleHeader.dbhSampleSize==16) written>>=1;
147 
148 	doubleBuffer->dbNumFrames=written;
149 	doubleBuffer->dbFlags|=dbBufferReady;
150 
151 #if TARGET_CPU_68K
152 	SetA5(oldA5);
153 #endif
154 }
155 
156 #else /* USE_SNDDOUBLEBUFFER */
157 
158 #if USE_DEFERREDTASKS
159 /* DeferredTask, called at almost-interrupt time (not for 68K - doesn't set A5) */
DeferredTaskCallback(long param)160 static pascal void DeferredTaskCallback(long param)
161 {
162 	long written;
163 
164 	deferredTaskFired = true;
165 
166 	FILL_BUFFER(param, SOUND_BUFFER_SIZE, written)
167 
168 	deferredTaskDone = true;
169 }
170 #endif /* USE_DEFERREDTASKS */
171 
172 /* SoundCallback, called at interrupt time (not for 68K - doesn't set A5)  */
SoundCallback(SndChannelPtr channel,SndCommand * command)173 static pascal void SoundCallback(SndChannelPtr channel, SndCommand *command)
174 {
175 #ifndef GCC
176 #pragma unused(channel,command)
177 #endif
178 	SndCommand buffer   = { bufferCmd, 0, (long) &sndHeader };
179 	SndCommand callback = { callBackCmd, 0, 0 };
180 
181 	/* Install current buffer */
182 	sndHeader.samplePtr = currentBuffer;
183 	sndHeader.numFrames = currentFrames;
184 	SndDoImmediate(soundChannel, &buffer);
185 
186 #if USE_DEFERREDTASKS
187 	/* Setup deferred task to fill next buffer */
188 	if(deferredTaskFired)
189 	{
190 		currentBuffer = (currentBuffer == sndBuffer1) ? sndBuffer2 : sndBuffer1;
191 		if (currentBuffer != NULL)
192 		{
193 			deferredTaskFired = false;
194 			deferredTaskDone = false;
195 			dtask.dtParam = (long) currentBuffer;
196 			DTInstall((DeferredTaskPtr) &dtask);
197 		}
198 	}
199 #else
200 	{
201 		long	bytes;
202 
203 		currentBuffer = (currentBuffer == sndBuffer1) ? sndBuffer2 : sndBuffer1;
204 		FILL_BUFFER(currentBuffer, SOUND_BUFFER_SIZE, bytes)
205 
206 		if (sndHeader.numChannels == 2) bytes >>= 1;
207 		if (sndHeader.sampleSize == 16) bytes >>= 1;
208 
209 		currentFrames = bytes;
210 	}
211 #endif /* USE_DEFERREDTASKS */
212 
213 	/* Queue next callback */
214 	SndDoCommand(soundChannel, &callback, true);
215 }
216 
217 #endif /* USE_SNDDOUBLEBUFFER */
218 
MAC_IsThere(void)219 static BOOL MAC_IsThere(void)
220 {
221 	NumVersion nVers;
222 
223 	nVers=SndSoundManagerVersion();
224 	if (nVers.majorRev>=2)
225 		return 1; /* need SoundManager 2.0+ */
226 	else
227 		return 0;
228 }
229 
MAC_Init(void)230 static int MAC_Init(void)
231 {
232 	OSErr err,iErr;
233 #if USE_SNDDOUBLEBUFFER
234 	int i;
235 	SndDoubleBufferPtr doubleBuffer;
236 #endif
237 	long rate,maxrate,maxbits;
238 	long gestaltAnswer;
239 	NumVersion nVers;
240 	Boolean Stereo,StereoMixing,Audio16;
241 	Boolean NewSoundManager,NewSoundManager31;
242 
243 	NewSoundManager31=NewSoundManager=false;
244 
245 	nVers=SndSoundManagerVersion();
246 	if (nVers.majorRev>=3) {
247 		NewSoundManager=true;
248 		if (nVers.minorAndBugRev>=0x10)
249 			NewSoundManager31=true;
250 	} else
251 	  if (nVers.majorRev<2)
252 		return 1; /* failure, need SoundManager 2.0+ */
253 
254 	iErr=Gestalt(gestaltSoundAttr,&gestaltAnswer);
255 	if (iErr==noErr) {
256 		Stereo=(gestaltAnswer & (1<<gestaltStereoCapability))!=0;
257 		StereoMixing=(gestaltAnswer & (1<<gestaltStereoMixing))!=0;
258 		Audio16=(gestaltAnswer & (1<<gestalt16BitSoundIO))!=0;
259 	} else {
260 		/* failure, couldn't get any sound info at all ? */
261 		Stereo=StereoMixing=Audio16=false;
262 	}
263 
264 #if !TARGET_CPU_68K || !TARGET_RT_MAC_CFM
265 	if (NewSoundManager31) {
266 		iErr=GetSoundOutputInfo(0L,siSampleRate,(void*)&maxrate);
267 		if (iErr==noErr)
268 			iErr=GetSoundOutputInfo(0L,siSampleSize,(void*)&maxbits);
269 	}
270 
271 	if (iErr!=noErr) {
272 #endif
273 		maxrate=rate22khz;
274 
275 		if (NewSoundManager && Audio16)
276 			maxbits=16;
277 		else
278 			maxbits=8;
279 #if !TARGET_CPU_68K || !TARGET_RT_MAC_CFM
280 	}
281 #endif
282 
283 	switch (md_mixfreq) {
284 		case 48000:rate=rate48khz;break;
285 		case 44100:rate=rate44khz;break;
286 		case 22254:rate=rate22khz;break;
287 		case 22050:rate=rate22050hz;break;
288 		case 11127:rate=rate11khz;break;
289 		case 11025:rate=rate11025hz;break;
290 		default:   rate=0;break;
291 	}
292 
293 	if (!rate) {
294 		_mm_errno=MMERR_MAC_SPEED;
295 		return 1;
296 	}
297 
298 	md_mode|=DMODE_SOFT_MUSIC|DMODE_SOFT_SNDFX;
299 
300 	if ((md_mode&DMODE_16BITS)&&(maxbits<16))
301 		md_mode&=~DMODE_16BITS;
302 
303 	if (!Stereo || !StereoMixing)
304 		md_mode&=~DMODE_STEREO;
305 
306 	if (rate>maxrate)
307 		rate=maxrate;
308 	if (md_mixfreq>(maxrate>>16))
309 		md_mixfreq=maxrate>>16;
310 
311 #if USE_SNDDOUBLEBUFFER
312 	err=SndNewChannel(&soundChannel,sampledSynth,
313 			  (md_mode&DMODE_STEREO)?initStereo:initMono, NULL);
314 	if(err!=noErr) {
315 		_mm_errno=MMERR_OPENING_AUDIO;
316 		return 1;
317 	}
318 
319 	doubleHeader.dbhCompressionID=0;
320 	doubleHeader.dbhPacketSize   =0;
321 	doubleHeader.dbhSampleRate   =rate;
322 	doubleHeader.dbhSampleSize   =(md_mode&DMODE_16BITS)?16:8;
323 	doubleHeader.dbhNumChannels  =(md_mode&DMODE_STEREO)?2:1;
324 	doubleHeader.dbhDoubleBack   =NewSndDoubleBackProc(&MyDoubleBackProc);
325 
326 	for(i=0;i<2;i++) {
327 		doubleBuffer=(SndDoubleBufferPtr)NewPtrClear(sizeof(SndDoubleBuffer)+
328 							     SOUND_BUFFER_SIZE);
329 		if(!doubleBuffer) {
330 			_mm_errno=MMERR_OUT_OF_MEMORY;
331 			return 1;
332 		}
333 
334 		doubleBuffer->dbNumFrames=0;
335 		doubleBuffer->dbFlags=0;
336 		doubleBuffer->dbUserInfo[0]=SetCurrentA5();
337 		doubleBuffer->dbUserInfo[1]=0;
338 
339 		doubleHeader.dbhBufferPtr[i]=doubleBuffer;
340 	}
341 
342 #else /* USE_SNDDOUBLEBUFFER */
343 	if(sndCallBack == NULL)
344 		sndCallBack = NewSndCallBackUPP(SoundCallback); /* <AWE> was "NewSndCallBackProc()" */
345 
346 	err=SndNewChannel(&soundChannel,sampledSynth,
347 			  (md_mode&DMODE_STEREO)?initStereo:initMono, sndCallBack);
348 	if(err!=noErr) {
349 		_mm_errno=MMERR_OPENING_AUDIO;
350 		return 1;
351 	}
352 
353 	sndBuffer1 = NewPtrClear(SOUND_BUFFER_SIZE);
354 	sndBuffer2 = NewPtrClear(SOUND_BUFFER_SIZE);
355 	if (sndBuffer1 == NULL || sndBuffer2 == NULL) {
356 		_mm_errno=MMERR_OUT_OF_MEMORY;
357 		return 1;
358 	}
359 	currentBuffer = sndBuffer1;
360 
361 	/* Setup sound header */
362 	memset(&sndHeader, 0, sizeof(sndHeader));
363 	sndHeader.numChannels = (md_mode&DMODE_STEREO)? 2: 1;
364 	sndHeader.sampleRate = rate;
365 	sndHeader.encode = extSH;
366 	sndHeader.baseFrequency = kMiddleC;
367 	sndHeader.numFrames = SOUND_BUFFER_SIZE >> (((md_mode&DMODE_STEREO)? 1: 0) + ((md_mode&DMODE_16BITS)?1: 0));
368 	sndHeader.sampleSize = (md_mode&DMODE_16BITS)? 16: 8;
369 	sndHeader.samplePtr = currentBuffer;
370 
371 #if USE_DEFERREDTASKS
372 	/* Setup deferred task record */
373 	memset(&dtask, 0, sizeof(dtask));
374 	dtask.qType = dtQType;
375 	dtask.dtFlags = 0;
376 	dtask.dtAddr = NewDeferredTaskUPP(DeferredTaskCallback); /* <AWE> was "NewDeferredTaskProc()" */
377 	dtask.dtReserved = 0;
378 	deferredTaskFired = true;
379 #endif /* USE_DEFERREDTASKS */
380 
381 #endif /* USE_SNDDOUBLEBUFFER */
382 
383 	return VC_Init();
384 }
385 
MAC_Exit(void)386 static void MAC_Exit(void)
387 {
388 #if USE_SNDDOUBLEBUFFER
389 	int i;
390 #else
391 	Ptr	temp1,temp2;
392 #endif
393 
394 	if (soundChannel != NULL)
395 	{
396 		SndDisposeChannel(soundChannel,true); /* "true" means to flush and quiet */
397 		soundChannel=NULL;
398 	}
399 
400 #if USE_SNDDOUBLEBUFFER
401 	DisposeRoutineDescriptor((UniversalProcPtr)doubleHeader.dbhDoubleBack);
402 	doubleHeader.dbhDoubleBack=NULL;
403 
404 	for(i=0;i<doubleHeader.dbhNumChannels;i++) {
405 		DisposePtr((Ptr)doubleHeader.dbhBufferPtr[i]);
406 		doubleHeader.dbhBufferPtr[i]=NULL;
407 	}
408 
409 #else /* USE_SNDDOUBLEBUFFER */
410 	if (sndCallBack != NULL)
411 	{
412 		DisposeSndCallBackUPP(sndCallBack);
413 		sndCallBack = NULL;
414 	}
415 
416 	temp1 = sndBuffer1;
417 	sndBuffer1 = NULL;
418 	temp2 = sndBuffer2;
419 	sndBuffer2 = NULL;
420 
421 #if USE_DEFERREDTASKS
422 	/* <afb> we can't dispose of the buffers until the DT is done with them */
423 	while (!deferredTaskDone)
424 		;
425 #endif
426 	DisposePtr(temp1);
427 	DisposePtr(temp2);
428 #endif /* USE_SNDDOUBLEBUFFER */
429 
430 	VC_Exit();
431 }
432 
MAC_PlayStart(void)433 static int MAC_PlayStart(void)
434 {
435 	OSErr err;
436 
437 #if USE_SNDDOUBLEBUFFER
438 	MyDoubleBackProc(soundChannel,doubleHeader.dbhBufferPtr[0]);
439 	MyDoubleBackProc(soundChannel,doubleHeader.dbhBufferPtr[1]);
440 
441 	err=SndPlayDoubleBuffer(soundChannel,&doubleHeader);
442 	if(err!=noErr) {
443 		_mm_errno=MMERR_MAC_START;
444 		return 1;
445 	}
446 
447 #else /* USE_SNDDOUBLEBUFFER */
448 	SndCommand callback = { callBackCmd, 0, 0 };
449 
450 	err=SndDoCommand(soundChannel, &callback, true);
451 	if(err!=noErr) {
452 		_mm_errno=MMERR_MAC_START;
453 		return 1;
454 	}
455 #endif /* USE_SNDDOUBLEBUFFER */
456 
457 	return VC_PlayStart();
458 }
459 
MAC_PlayStop(void)460 static void MAC_PlayStop(void)
461 {
462 	SndCommand flush = { flushCmd, 0, 0 };
463 	SndCommand quiet = { quietCmd, 0, 0 };
464 
465 	/* <afb> IM:Sound says we should issue the flushCmd before the quietCmd. */
466 	SndDoImmediate(soundChannel,&flush);
467 	SndDoImmediate(soundChannel,&quiet);
468 
469 	VC_PlayStop();
470 }
471 
MAC_Update(void)472 static void MAC_Update(void)
473 {
474 	return;
475 }
476 
477 MIKMODAPI MDRIVER drv_mac={
478     NULL,
479     "Mac Driver (Carbonized)",
480     "Macintosh Sound Manager Driver v2.1",
481     0,255,
482     "mac",
483     NULL,
484     NULL,
485     MAC_IsThere,
486     VC_SampleLoad,
487     VC_SampleUnload,
488     VC_SampleSpace,
489     VC_SampleLength,
490     MAC_Init,
491     MAC_Exit,
492     NULL,
493     VC_SetNumVoices,
494     MAC_PlayStart,
495     MAC_PlayStop,
496     MAC_Update,
497     NULL,
498     VC_VoiceSetVolume,
499     VC_VoiceGetVolume,
500     VC_VoiceSetFrequency,
501     VC_VoiceGetFrequency,
502     VC_VoiceSetPanning,
503     VC_VoiceGetPanning,
504     VC_VoicePlay,
505     VC_VoiceStop,
506     VC_VoiceStopped,
507     VC_VoiceGetPosition,
508     VC_VoiceRealVolume
509 };
510 
511 #else /* ifdef DRV_MAC */
512 
513 MISSING(drv_mac);
514 
515 #endif
516 
517 /* ex:set ts=4: */
518 
519