drv_mac.c
上传用户:wstnjxml
上传日期:2014-04-03
资源大小:7248k
文件大小:13k
源码类别:

Windows CE

开发平台:

C/C++

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