/tags/JSTUDIO3D_CPP_FILE_ONLY/source/Engine/JetEngine/Mp3Mgr/Mp3Mgr.cpp

# · C++ · 487 lines · 258 code · 94 blank · 135 comment · 44 complexity · 90cae9976362e1bf40e1d068b91ad77f MD5 · raw file

  1. //Mp3Mgr
  2. //It lets your app play MP3s
  3. #include "Mp3Mgr_h.h"
  4. #include "sound.h"
  5. #include "jet.h"
  6. #include "assert.h"
  7. // DEFINES AND DECLARATIONS ==============================================
  8. static Media media;
  9. // FUNCTIONS ==============================================================
  10. jeBoolean Initmp3Mgr(HWND mainwindowhandle)
  11. {
  12. // Filter interface initialize?
  13. if( SUCCEEDED( CoInitialize( NULL )))
  14. return TRUE;
  15. return FALSE;
  16. }
  17. //
  18. // UnInitmp3Mgr
  19. // Unloads mp3Mgr
  20. //
  21. void UnInitmp3Mgr(HWND mainwindowhandle)
  22. {
  23. CoUninitialize( );
  24. }
  25. //
  26. // mp3Mgr
  27. //
  28. /* This funcion initializez mp3Mgr and connects it to the main window.
  29. This is necessary so that streaming audio calls will deliver audio
  30. to the main game window. */
  31. //this is the main component and the engine should init it with the soundsystem
  32. JETAPI jeMp3Mgr * JETCC jeMp3_CreateManager(HWND mainwindowhandle)
  33. {
  34. jeMp3Mgr *Mp3Mgr;
  35. Mp3Mgr = JE_RAM_ALLOCATE_STRUCT(jeMp3Mgr);
  36. memset(Mp3Mgr, 0, sizeof(jeMp3Mgr));
  37. // Initialise COM and the application
  38. Initmp3Mgr(mainwindowhandle);
  39. InitMedia(mainwindowhandle);
  40. Mp3Mgr->num_mp3s = 0;
  41. Mp3Mgr->cur_mp3 = 0;
  42. Mp3Mgr->mwh = mainwindowhandle;
  43. return(Mp3Mgr);
  44. } // Mp3Mgr
  45. JETAPI jeBoolean JETCC jeMp3_DestroyManager(jeMp3Mgr **Mp3Mgr)
  46. {
  47. //destroys the Mp3Manager
  48. jeMp3Mgr * mp3;
  49. int i;
  50. assert(Mp3Mgr != NULL);
  51. mp3 = *Mp3Mgr;
  52. if ( mp3 ) //check 1
  53. {
  54. if (media.pGraph) //check 2
  55. {
  56. if (mp3->num_mp3s != 0) //check 3 (just to be sure)
  57. {
  58. StopMp3();
  59. for(i = 0; i < mp3->num_mp3s; i++)
  60. {
  61. mp3->files[i].szFileName = NULL;
  62. }
  63. DeleteContentsMp3();
  64. }
  65. }
  66. jeRam_Free(mp3);
  67. return JE_TRUE;
  68. }
  69. return JE_FALSE;
  70. }
  71. // CanPlay
  72. //
  73. // Return true if we can go to a playing state from our current state
  74. //
  75. BOOL CanPlay()
  76. {
  77. return (media.state == Stopped || media.state == Paused);
  78. }
  79. //
  80. // CanStop
  81. //
  82. // Return true if we can go to a stopped state from our current state
  83. //
  84. BOOL CanStop()
  85. {
  86. return (media.state == Playing || media.state == Paused);
  87. }
  88. //
  89. // IsInitialized
  90. //
  91. // Return true if we have loaded and initialized a multimedia file
  92. //
  93. BOOL IsInitialized()
  94. {
  95. return (media.state != Uninitialized);
  96. }
  97. //
  98. // ChangeStateTo
  99. //
  100. void ChangeStateTo( State newState )
  101. {
  102. media.state = newState;
  103. }
  104. //
  105. // InitMedia
  106. //
  107. // Initialization
  108. //
  109. jeBoolean InitMedia(HWND mainwindowhandle)
  110. {
  111. ChangeStateTo( Uninitialized );
  112. media.hGraphNotifyEvent = NULL;
  113. media.pGraph = NULL;
  114. return JE_TRUE;
  115. }
  116. /* WHAT THE HECK ARE FILTER GRAPHS!? (explaination by Tom Morris)
  117. Here is a brief tutorial from the Microsoft DirectX Media SDK.
  118. filter graph
  119. A collection of filters. Typically, a filter graph contains filters that
  120. are connected to perform a particular operation, such as playing back a media
  121. file, or capturing video from a VCR to the hard disk.
  122. filter
  123. A key component in the DirectShow architecture, a filter is a COM object that
  124. supports DirectShow interfaces or base classes. It might operate on streams of
  125. data .3
  126. in a variety of ways, such as reading, copying, modifying, or writing the
  127. data to a file. Sources, transform filters, and renderers are all particular
  128. types of filters. A filter contains pins that it uses to connect to other filters.
  129. Tom's spin on filter graphs.
  130. About the stupidest and most confusing name I ever heard. But that complaint aside,
  131. the Windows Media Player handles audio data by streaming it straight from the data
  132. source, through the CPU and out to the sound card. It is my understanding that the
  133. sound data don't ever reside in memory as a static entity at a particular address.
  134. As a result, this means that we have to use different kinds of techniques to manage
  135. sounds processed by Mp3Mgr. Mp3Mgr accesses and uses the guts of the Windows Media
  136. Player to manage streaming audio files, including mp3.
  137. Since the sound exists as a dynamic, streaming entity and not a pool of data, Mp3Mgr
  138. must communicate with it by checking the status of the stream (which is always
  139. changing). We will do this by querying the Media Player interface (which keeps tabs
  140. on streaming status) and processing the report our query returns (by way of HRESULT
  141. types. Right now, Mp3Mgr is very limited in the ways it can influence sound output.
  142. Hopefully, future versions will add more versatility and functionality.
  143. The following code is a total hack by me, a modification of the sample code provided
  144. by Microsoft in its cplay.c example, found in the Microsoft DirectX Media SDK.
  145. */
  146. //
  147. // CreateFilterGraph
  148. //
  149. BOOL CreateFilterGraph()
  150. {
  151. IMediaEvent *pME; // Event Pointer
  152. HRESULT hr; // for communicating with the filter graph
  153. ASSERT(media.pGraph == NULL); // make sure we start clean
  154. hr = CoCreateInstance(CLSID_FilterGraph, // CLSID of object
  155. NULL, // Outer unknown
  156. CLSCTX_INPROC_SERVER, // Type of server
  157. IID_IGraphBuilder, // Interface wanted
  158. (void **) &media.pGraph); // Returned object
  159. if (FAILED(hr))
  160. {
  161. media.pGraph = NULL;
  162. return FALSE;
  163. }
  164. // We use this to find out events sent by the filtergraph
  165. hr = media.pGraph->QueryInterface(IID_IMediaEvent,
  166. (void **) &pME); // open lines of communication
  167. if (FAILED(hr))
  168. {
  169. DeleteContentsMp3();
  170. return FALSE;
  171. }
  172. // establish event handle
  173. hr = pME->GetEventHandle((OAEVENT*) &media.hGraphNotifyEvent);
  174. pME->Release();
  175. if (FAILED(hr))
  176. {
  177. DeleteContentsMp3();
  178. return FALSE;
  179. }
  180. return TRUE;
  181. } // CreateFilterGraph
  182. // Destruction
  183. //
  184. // DeleteContents
  185. //
  186. void DeleteContentsMp3()
  187. {
  188. if (media.pGraph != NULL)
  189. {
  190. media.pGraph->Release();
  191. media.pGraph = NULL;
  192. }
  193. // this event is owned by the filter graph and is thus invalid
  194. media.hGraphNotifyEvent = NULL;
  195. ChangeStateTo( Uninitialized );
  196. } // Delete Contents
  197. //
  198. // RenderFile
  199. // Process the file through the appropriate filter path. This function is called by
  200. // OpenMediaFile()
  201. //
  202. BOOL RenderFile( LPSTR szFileName )
  203. {
  204. HRESULT hr;
  205. WCHAR wPath[MAX_PATH];
  206. DeleteContentsMp3();
  207. if ( !CreateFilterGraph() )
  208. {
  209. return FALSE;
  210. }
  211. MultiByteToWideChar( CP_ACP, 0, szFileName, -1, wPath, MAX_PATH );
  212. hr = media.pGraph->RenderFile(wPath, NULL);
  213. if (FAILED( hr ))
  214. {
  215. return FALSE;
  216. }
  217. return TRUE;
  218. } // RenderFile
  219. //
  220. // OpenMediaFile
  221. // This function opens and renders the specified media file.
  222. // File..Open has been selected
  223. //
  224. jeBoolean OpenMediaFile(LPSTR szFile )
  225. {
  226. if( RenderFile( szFile )) // this calls the filter graph
  227. {
  228. ChangeStateTo( Stopped );
  229. return JE_TRUE;
  230. }
  231. return JE_FALSE;
  232. } // OpenMediaFile
  233. //
  234. // PlayMp3
  235. //
  236. void PlayMp3(long volume, jeBoolean loop)
  237. {
  238. if( CanPlay() )
  239. {
  240. HRESULT hr;
  241. IMediaControl *pMC;
  242. IBasicAudio *pMA;
  243. IMediaPosition *pMP;
  244. // Obtain the interface to our filter graph
  245. hr = media.pGraph->QueryInterface(IID_IMediaControl,
  246. (void **) &pMC);
  247. if( SUCCEEDED(hr) )
  248. {
  249. /* In order to loop sounds, we will check with Media
  250. Player to see when the sound is almost over. When
  251. it's within 0.05 sec of ending, we'll stop the sound,
  252. rewind it and declare that the sound is ready to play
  253. again. The next time around your game's main loop,
  254. PlayMp3 will start it up again. And so it goes...
  255. */
  256. // Set volume level
  257. hr = media.pGraph->QueryInterface(IID_IBasicAudio,
  258. (void**) &pMA);
  259. if (SUCCEEDED(hr))
  260. { /* Set volume.
  261. -10000 is silence, 0 is full volume*/
  262. hr = pMA->put_Volume(volume);
  263. pMA->Release(); // release the interface
  264. }
  265. if (loop == JE_TRUE)
  266. {
  267. hr = media.pGraph->QueryInterface(IID_IMediaPosition,
  268. (void**) &pMP);
  269. if (SUCCEEDED(hr))
  270. {
  271. // start from last position, but rewind if near the end
  272. REFTIME tCurrent, tLength;
  273. hr = pMP->get_Duration(&tLength);
  274. if (SUCCEEDED(hr))
  275. {
  276. hr = pMP->get_CurrentPosition(&tCurrent);
  277. if (SUCCEEDED(hr))
  278. {
  279. // within 0.05 sec of end? (or past end?)
  280. if ((tLength - tCurrent) < 0.05)
  281. {
  282. // Rewind it
  283. pMP->put_CurrentPosition(0);
  284. CanPlay(); // It's ready to play again
  285. } // If not, you can't loop.
  286. }
  287. }
  288. pMP->Release();
  289. }
  290. // Ask the filter graph to play and release the interface
  291. hr = pMC->Run();
  292. pMC->Release();
  293. if( SUCCEEDED(hr) )
  294. {
  295. return;
  296. }
  297. }
  298. else
  299. // Ask the filter graph to play and release the interface
  300. hr = pMC->Run();
  301. pMC->Release();
  302. if( SUCCEEDED(hr) )
  303. {
  304. return;
  305. }
  306. }
  307. }
  308. } // PlayMp3
  309. //
  310. // StopMp3
  311. //
  312. //
  313. void StopMp3()
  314. {
  315. HRESULT hr;
  316. IMediaControl *pMC;
  317. // Obtain the interface to our filter graph
  318. hr = media.pGraph->QueryInterface(IID_IMediaControl,
  319. (void **) &pMC);
  320. if( SUCCEEDED(hr) )
  321. {
  322. pMC->Stop(); // stop it!
  323. pMC->Release();
  324. ChangeStateTo( Stopped );
  325. }
  326. return ;
  327. } //StopMp3
  328. //
  329. // GetGraphEventHandle
  330. //
  331. // We use this to check for graph events
  332. //
  333. HANDLE GetGraphEventHandle()
  334. {
  335. return media.hGraphNotifyEvent;
  336. } // GetGraphEventHandle
  337. //
  338. // OnGraphNotify
  339. //
  340. // If the event handle is valid, then ask the graph if
  341. // anything has happened (eg the graph has stopped...)
  342. //
  343. void OnGraphNotify()
  344. {
  345. IMediaEvent *pME;
  346. long lEventCode, lParam1, lParam2;
  347. ASSERT( media.hGraphNotifyEvent != NULL );
  348. if( SUCCEEDED(media.pGraph->QueryInterface(IID_IMediaEvent, (void **) &pME)))
  349. {
  350. if( SUCCEEDED(pME->GetEvent(&lEventCode, &lParam1, &lParam2, 0)))
  351. {
  352. if (lEventCode == EC_COMPLETE)
  353. {
  354. StopMp3();
  355. }
  356. }
  357. pME->Release();
  358. }
  359. }
  360. /*===================================================================================
  361. MP3PLAYING
  362. This function checks to see if the current mp3 file is still playing.
  363. =====================================================================================*/
  364. jeBoolean Mp3Playing()
  365. {
  366. HRESULT hr;
  367. IMediaPosition *pMP;
  368. // query the interface
  369. hr = media.pGraph->QueryInterface(IID_IMediaPosition,
  370. (void**) &pMP);
  371. if (SUCCEEDED(hr))
  372. {
  373. REFTIME tCurrent, tLength; // find the max playtime
  374. hr = pMP->get_Duration(&tLength);
  375. if (SUCCEEDED(hr))
  376. { // where are we now?
  377. hr = pMP->get_CurrentPosition(&tCurrent);
  378. if (SUCCEEDED(hr))
  379. {
  380. // Test to see if there is any time left
  381. while ((tLength - tCurrent) > 0)
  382. return JE_TRUE; // if so, still playing, buddy.
  383. }
  384. }
  385. } // when done playing...
  386. pMP->Release(); // release our access to the interface
  387. return JE_FALSE; // mp3 file is all done playing.
  388. } //Mp3Playing