Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1157 lines
40 KiB

  1. /******************************************************************************
  2. Copyright (c) 1985-1999 Microsoft Corporation
  3. Title: mciseq.c - Multimedia Systems Media Control Interface
  4. Sequencer driver for MIDI files.
  5. Version: 1.00
  6. Date: 24-Apr-1992
  7. Author: Greg Simons
  8. ------------------------------------------------------------------------------
  9. Change log:
  10. DATE REV DESCRIPTION
  11. ----------- ----- -----------------------------------------------------------
  12. 24-APR-1990 GregSi Original
  13. 1 -OCT-1990 GregSi Merge with MMSEQ
  14. 10-MAR-1992 RobinSp Move to Windows NT
  15. *****************************************************************************/
  16. #define UNICODE
  17. //MMSYSTEM
  18. #define MMNOSOUND - Sound support
  19. #define MMNOWAVE - Waveform support
  20. #define MMNOAUX - Auxiliary output support
  21. #define MMNOJOY - Joystick support
  22. //MMDDK
  23. #define NOWAVEDEV - Waveform support
  24. #define NOAUXDEV - Auxiliary output support
  25. #define NOJOYDEV - Joystick support
  26. #include <stdlib.h>
  27. #include <windows.h>
  28. #include <mmsystem.h>
  29. #include <mmddk.h>
  30. #include <string.h>
  31. #include <wchar.h>
  32. #include "mmsys.h"
  33. #include "list.h"
  34. #include "mciseq.h"
  35. #define CONFIG_ID 0xFFFFFFFF // Use this value to identify config. opens
  36. #define GETMOTDWORD(lpd) ((((DWORD)GETMOTWORD(lpd)) << (8 * sizeof(WORD))) + GETMOTWORD((LPBYTE)(lpd) + sizeof(WORD)))
  37. #define ASYNCMESSAGE(w) (((w) == MCI_PLAY) || ((w) == MCI_SEEK))
  38. /***************************************************************************
  39. *
  40. * Globals
  41. *
  42. **************************************************************************/
  43. ListHandle SeqStreamListHandle;
  44. HINSTANCE hInstance;
  45. UINT MINPERIOD; // Minimum timer period supported.
  46. int MIDIConfig (HWND hwndParent);
  47. /***************************************************************************
  48. *
  49. * @doc INTERNAL MCISEQ
  50. *
  51. * @api DWORD | mciDriverEntry | Single entry point for MCI drivers
  52. *
  53. * @parm MCIDEVICEID | wDeviceID | The MCI device ID
  54. *
  55. * @parm UINT | wMessage | The requested action to be performed.
  56. *
  57. * @parm DWORD | dwParam1 | Data for this message. Defined seperately for
  58. * each message
  59. *
  60. * @parm DWORD | dwParam2 | Data for this message. Defined seperately for
  61. * each message
  62. *
  63. * @rdesc Defined separately for each message.
  64. *
  65. * @comm This may not be called at interrupt time.
  66. *
  67. ***************************************************************************/
  68. PUBLIC DWORD FAR PASCAL mciDriverEntry (MCIDEVICEID wDeviceID, UINT wMessage,
  69. DWORD_PTR dwParam1, DWORD_PTR dwParam2)
  70. {
  71. pSeqStreamType pStream;
  72. DWORD dwError;
  73. // get the sequence stream handle for the given ID
  74. pStream = (pSeqStreamType) mciGetDriverData (wDeviceID);
  75. // (unless it's irrelevent to the command)
  76. if (!pStream &&
  77. (!((wMessage == MCI_OPEN_DRIVER) || (wMessage == MCI_GETDEVCAPS) ||
  78. (wMessage == MCI_INFO) || (wMessage == MCI_CLOSE_DRIVER))))
  79. return MCIERR_UNSUPPORTED_FUNCTION;
  80. switch (wMessage)
  81. {
  82. case MCI_OPEN_DRIVER:
  83. dwError = msOpen(&pStream, wDeviceID, (DWORD)dwParam1, (LPMCI_OPEN_PARMS)dwParam2);
  84. break;
  85. case MCI_CLOSE_DRIVER: // close file
  86. dwError = msClose(pStream, wDeviceID, (DWORD)dwParam1);
  87. pStream = NULL;
  88. break;
  89. case MCI_PLAY: // play a file (pass thru to sequencer)
  90. dwError = msPlay(pStream, wDeviceID, (DWORD)dwParam1, (LPMCI_PLAY_PARMS)dwParam2);
  91. break;
  92. case MCI_PAUSE:
  93. case MCI_STOP:
  94. if (wMessage == MCI_PAUSE) // remember pause status for "mci_mode"
  95. pStream->bLastPaused = TRUE;
  96. else
  97. pStream->bLastPaused = FALSE;
  98. if (!(dwError = (DWORD)midiSeqMessage(pStream->hSeq, SEQ_STOP, 0L, 0L)))
  99. midiSeqMessage(pStream->hSeq, SEQ_SETPORTOFF, TRUE, 0L);
  100. break;
  101. case MCI_SEEK: // pass thru as song ptr
  102. pStream->bLastPaused = FALSE;
  103. dwError = msSeek(pStream, wDeviceID, (DWORD)dwParam1, (LPMCI_SEEK_PARMS)dwParam2);
  104. break;
  105. case MCI_STATUS:
  106. dwError = msStatus(pStream, wDeviceID, (DWORD)dwParam1, (LPMCI_STATUS_PARMS)dwParam2);
  107. break;
  108. case MCI_GETDEVCAPS:
  109. dwError = msGetDevCaps(pStream, wDeviceID, (DWORD)dwParam1, (LPMCI_GETDEVCAPS_PARMS)dwParam2);
  110. break;
  111. case MCI_INFO: // TBD: use resource for string
  112. dwError = msInfo(pStream, wDeviceID, (DWORD)dwParam1, (LPMCI_INFO_PARMS)dwParam2);
  113. break;
  114. case MCI_SET:
  115. dwError = msSet(pStream, wDeviceID, (DWORD)dwParam1, (LPMCI_SEQ_SET_PARMS)dwParam2);
  116. break;
  117. case MCI_STEP:
  118. case MCI_RECORD:
  119. case MCI_SAVE:
  120. case MCI_CUE:
  121. case MCI_REALIZE:
  122. case MCI_WINDOW:
  123. case MCI_PUT:
  124. case MCI_WHERE:
  125. case MCI_FREEZE:
  126. case MCI_UNFREEZE:
  127. case MCI_LOAD:
  128. case MCI_CUT:
  129. case MCI_COPY:
  130. case MCI_PASTE:
  131. case MCI_UPDATE:
  132. case MCI_DELETE:
  133. case MCI_RESUME:
  134. return MCIERR_UNSUPPORTED_FUNCTION;
  135. default:
  136. //case MCI_SOUND: This is obsolete and has been removed from the public headers
  137. return MCIERR_UNRECOGNIZED_COMMAND;
  138. } // switch
  139. // NOTIFY HANDLED HERE
  140. if (!LOWORD(dwError))
  141. {
  142. MIDISEQINFO seqInfo;
  143. DWORD dwTo;
  144. // first derive info crucial for play abort
  145. if (wMessage == MCI_PLAY)
  146. {
  147. // get info to aid in possible time format conversions (from & to)
  148. midiSeqMessage((HMIDISEQ) pStream->hSeq,
  149. SEQ_GETINFO, (DWORD_PTR)(LPMIDISEQINFO) &seqInfo, 0L);
  150. if (dwParam1 & MCI_TO)
  151. {
  152. // is the user typing in what he believes to be the end?
  153. if (((LPMCI_PLAY_PARMS)dwParam2)->dwTo == CnvtTimeFromSeq(pStream, seqInfo.dwLength, &seqInfo))
  154. dwTo = seqInfo.dwLength; // if so, let him have it
  155. else
  156. dwTo = CnvtTimeToSeq(pStream, // else straight cnvt
  157. ((LPMCI_PLAY_PARMS)dwParam2)->dwTo, &seqInfo);
  158. }
  159. else
  160. dwTo = seqInfo.dwLength; // already in native format
  161. }
  162. if (pStream) {
  163. // HANDLE ABORT/SUPERSEDE OF ANY OUTSTANDING DELAYED NOTIFY
  164. if (pStream->hNotifyCB)
  165. {
  166. if (bMutex(wMessage, pStream->wNotifyMsg,
  167. (DWORD)dwParam1 /*flags*/, dwTo, pStream->dwNotifyOldTo))
  168. // if msg cancels old notify (regardless of whether
  169. // it notifies) abort pending notify
  170. Notify(pStream, MCI_NOTIFY_ABORTED);
  171. else if (dwParam1 & MCI_NOTIFY) // else if this one notifies,
  172. // that supersedes old one
  173. Notify(pStream, MCI_NOTIFY_SUPERSEDED);
  174. }
  175. // HANDLE THIS MESSAGE'S NOTIFY
  176. if (dwParam1 & MCI_NOTIFY)
  177. {
  178. // HANDLE THIS NOTIFY
  179. PrepareForNotify(pStream, wMessage,
  180. (LPMCI_GENERIC_PARMS) dwParam2, dwTo);
  181. if (!ASYNCMESSAGE(wMessage) || ((wMessage == MCI_PLAY) && (seqInfo.dwCurrentTick == dwTo)))
  182. Notify(pStream, MCI_NOTIFY_SUCCESSFUL);
  183. }
  184. } else if (dwParam1 & MCI_NOTIFY)
  185. mciDriverNotify((HWND)((LPMCI_GENERIC_PARMS)dwParam2)->dwCallback, wDeviceID, MCI_NOTIFY_SUCCESSFUL);
  186. }
  187. return dwError;
  188. }
  189. /****************************************************************************
  190. *
  191. * Helper Functions
  192. *
  193. ***************************************************************************/
  194. PRIVATE BOOL NEAR PASCAL bMutex(UINT wNewMsg, UINT wOldMsg, DWORD wNewFlags,
  195. DWORD dwNewTo, DWORD dwOldTo)
  196. {
  197. switch (wOldMsg)
  198. {
  199. case MCI_PLAY:
  200. switch (wNewMsg)
  201. {
  202. case MCI_STOP:
  203. case MCI_PAUSE:
  204. case MCI_SEEK:
  205. case MCI_CLOSE_DRIVER:
  206. return TRUE;
  207. case MCI_PLAY:
  208. if ((wNewFlags & MCI_FROM) || (dwNewTo != dwOldTo))
  209. return TRUE;
  210. else
  211. return FALSE;
  212. default:
  213. return FALSE;
  214. }
  215. break;
  216. case MCI_SEEK:
  217. switch (wNewMsg)
  218. {
  219. case MCI_CLOSE_DRIVER:
  220. case MCI_SEEK:
  221. return TRUE;
  222. case MCI_PLAY:
  223. if (wNewFlags & MCI_FROM)
  224. return TRUE;
  225. else
  226. return FALSE;
  227. default:
  228. return FALSE;
  229. }
  230. break;
  231. default: // should never get here
  232. return FALSE;
  233. }
  234. }
  235. /***************************************************************************/
  236. PUBLIC VOID FAR PASCAL PrepareForNotify(pSeqStreamType pStream,
  237. UINT wMessage, LPMCI_GENERIC_PARMS lpParms, DWORD dwTo)
  238. /* This function's purpose is to setup for notify in cases where
  239. * an asynchronous message is about to be sent to the sequencer --
  240. * e.g. before a 'play,' where the sequencer must call back to tell you
  241. * it's done (then you, in turn, call back the client).
  242. * This funtion sets up the MCISEQ -> CLIENT interface.
  243. */
  244. {
  245. // remember this notify's dwCallback, and message
  246. // which notify was for
  247. //mci client's notify callback handle
  248. pStream->hNotifyCB = (HWND)lpParms->dwCallback;
  249. pStream->wNotifyMsg = wMessage; // remember for possible supersed/abort
  250. pStream->dwNotifyOldTo = dwTo; // save to position for possible
  251. // subsequent abort/supersede
  252. }
  253. /****************************************************************************/
  254. PUBLIC VOID NEAR PASCAL EndStreamCycle(SeqStreamType* seqStream)
  255. {
  256. // signal on all tracks' buffers
  257. // as a result, the Stream Cycle process will finish (i.e. die)
  258. // account for cases where stream or part of it wasn't allocated
  259. TrackStreamType* trackStream;
  260. int i;
  261. if (!seqStream)
  262. return;
  263. seqStream->streaming = FALSE; // first let it exit
  264. if (seqStream->trackStreamListHandle == NULLLIST)
  265. return;
  266. // now signal on all to let it escape
  267. trackStream = (TrackStreamType*) List_Get_First(seqStream->trackStreamListHandle);
  268. while(trackStream)
  269. {
  270. for(i = 0; i < NUMHDRS; i++) // signal on all buffers
  271. {
  272. if (seqStream->streamTaskHandle)
  273. {
  274. dprintf2(("about to signal in EndStreamCycle"));
  275. if (seqStream->streamTaskHandle) {
  276. TaskSignal(seqStream->streamTaskHandle, WTM_QUITTASK);
  277. #ifdef WIN32
  278. TaskWaitComplete(seqStream->streamThreadHandle);
  279. #else
  280. Yield();
  281. #endif // WIN32
  282. }
  283. }
  284. else
  285. break;
  286. }
  287. trackStream = (TrackStreamType*) List_Get_Next(seqStream->trackStreamListHandle, trackStream);
  288. }
  289. }
  290. /****************************************************************************/
  291. PUBLIC DWORD NEAR PASCAL EndFileStream(pSeqStreamType pStream)
  292. /* Closes the file and frees all stream memory. Handles cases where
  293. allocation failed part way through. */
  294. {
  295. if (!pStream)
  296. return 0;
  297. EndStreamCycle(pStream);
  298. if (pStream->hSeq)
  299. midiSeqMessage(pStream->hSeq, SEQ_CLOSE, 0L, 0L); //directly close it
  300. if (pStream->trackStreamListHandle != NULLLIST)
  301. {
  302. TrackStreamType *trackStream;
  303. trackStream = (TrackStreamType*) List_Get_First(pStream->trackStreamListHandle);
  304. while (trackStream)
  305. {
  306. int i;
  307. for(i = 0; i < NUMHDRS; i++) // unlock two midihdr buffers for it
  308. {
  309. if (trackStream->fileHeaders[i])
  310. {
  311. #ifdef WIN16
  312. GlobalFreePtr(trackStream->fileHeaders[i]);
  313. #else
  314. GlobalFree(trackStream->fileHeaders[i]);
  315. #endif
  316. }
  317. else
  318. break;
  319. }
  320. trackStream = (TrackStreamType*) List_Get_Next(pStream->trackStreamListHandle, trackStream);
  321. }
  322. List_Destroy(pStream->trackStreamListHandle);
  323. }
  324. List_Deallocate(SeqStreamListHandle, (NPSTR) pStream);
  325. return 0;
  326. }
  327. /*****************************************************************************/
  328. PRIVATE void PASCAL NEAR InitMMIOOpen(LPMMIOPROC pIOProc, LPMMIOINFO lpmmioInfo)
  329. {
  330. _fmemset(lpmmioInfo, 0, sizeof(MMIOINFO));
  331. if (pIOProc)
  332. lpmmioInfo->pIOProc = pIOProc;
  333. else
  334. lpmmioInfo->fccIOProc = FOURCC_DOS;
  335. }
  336. /*****************************************************************************/
  337. PRIVATE HMMIO NEAR PASCAL msOpenFile(LPWSTR szName, LPMMCKINFO lpmmckData, LPMMIOPROC pIOProc)
  338. /* Returns hmmio. This value will be null if failure.
  339. Reads both RIFF and dos midifiles.
  340. Sets the current file position to the start of MIDI data. */
  341. {
  342. #define RMIDFORMTYPE mmioFOURCC('R', 'M', 'I', 'D')
  343. #define DATACKID mmioFOURCC('d', 'a', 't', 'a')
  344. MMIOINFO mmioInfo;
  345. HMMIO hmmio;
  346. MMCKINFO mmckRiff;
  347. InitMMIOOpen(pIOProc, &mmioInfo);
  348. hmmio = mmioOpen(szName, &mmioInfo, MMIO_READ | MMIO_DENYWRITE);
  349. if (hmmio == NULL)
  350. return NULL;
  351. mmckRiff.fccType = RMIDFORMTYPE;
  352. lpmmckData->ckid = DATACKID;
  353. if (mmioDescend(hmmio, &mmckRiff, NULL, MMIO_FINDRIFF) || mmioDescend(hmmio, lpmmckData, &mmckRiff, MMIO_FINDCHUNK))
  354. {
  355. lpmmckData->cksize = mmioSeek(hmmio, 0, SEEK_END);
  356. lpmmckData->fccType = 0;
  357. lpmmckData->dwDataOffset = 0;
  358. lpmmckData->dwFlags = 0;
  359. mmioSeek(hmmio, 0, SEEK_SET);
  360. }
  361. return hmmio;
  362. }
  363. /*****************************************************************************/
  364. PUBLIC DWORD NEAR PASCAL msOpenStream(pSeqStreamType FAR * lppStream, LPCWSTR szName, LPMMIOPROC pIOProc)
  365. /* opens file, sets up streaming variables and buffers, calls routine
  366. to load them and send them to the sequencer. Returns stream handle in
  367. pStream var pointed to by lppStream. Returns error code */
  368. {
  369. #define MAXHDRSIZE 0x100
  370. #define MThd mmioFOURCC('M', 'T', 'h', 'd')
  371. #define MTrk mmioFOURCC('M', 'T', 'r', 'k')
  372. UINT wTracks;
  373. UINT wFormat;
  374. HMMIO hmmio;
  375. SeqStreamType *thisSeqStream = NULL;
  376. TrackStreamType *thisTrackStream;
  377. BYTE fileHeader[MAXHDRSIZE];
  378. int iTrackNum;
  379. DWORD smErrCode, errCode;
  380. MIDISEQOPENDESC open;
  381. MMCKINFO mmckData;
  382. MMCKINFO mmck;
  383. MMIOINFO mmioInfo;
  384. WCHAR szPathName[128];
  385. wcsncpy(szPathName, szName, (sizeof(szPathName)/sizeof(WCHAR)) - 1);
  386. InitMMIOOpen(pIOProc, &mmioInfo);
  387. if (!mmioOpen(szPathName, &mmioInfo, MMIO_PARSE)) {
  388. hmmio = NULL;
  389. errCode = MCIERR_FILE_NOT_FOUND;
  390. goto ERROR_HDLR;
  391. }
  392. // check if it's a RIFF file or not -- if so, descend into RMID chunk
  393. // if not, just open it and assume that it's a MIDI file
  394. hmmio = msOpenFile(szPathName, &mmckData, pIOProc);
  395. if (!hmmio) // open the MIDI file
  396. {
  397. errCode = MCIERR_FILE_NOT_FOUND;
  398. goto ERROR_HDLR;
  399. }
  400. mmck.ckid = MThd;
  401. if (mmioDescend(hmmio, &mmck, &mmckData, MMIO_FINDCHUNK))
  402. {
  403. errCode = MCIERR_INVALID_FILE;
  404. goto ERROR_HDLR;
  405. }
  406. mmck.cksize = GETMOTDWORD(&mmck.cksize);
  407. if (mmck.cksize < 3 * sizeof(WORD))
  408. {
  409. errCode = MCIERR_INVALID_FILE;
  410. goto ERROR_HDLR;
  411. }
  412. // allocate sequence stream structure & its track stream list
  413. if (! (thisSeqStream = (SeqStreamType*) List_Allocate(SeqStreamListHandle)))
  414. {
  415. errCode = MCIERR_OUT_OF_MEMORY;
  416. goto ERROR_HDLR;
  417. }
  418. List_Attach_Tail(SeqStreamListHandle, (NPSTR) thisSeqStream);
  419. thisSeqStream->trackStreamListHandle = NULLLIST;
  420. thisSeqStream->hmmio = hmmio;
  421. thisSeqStream->pIOProc = pIOProc;
  422. lstrcpy(thisSeqStream->szFilename, szPathName);
  423. Yield();
  424. thisSeqStream->dwFileLength = mmckData.cksize;
  425. open.dwLen = min(mmck.cksize, MAXHDRSIZE);
  426. mmioRead(hmmio, (HPSTR)fileHeader, open.dwLen); // read the header info now
  427. wFormat = GETMOTWORD(fileHeader);
  428. wTracks = GETMOTWORD(fileHeader + sizeof(WORD));
  429. if (((wFormat == 0) && (wTracks > 1)) || // illegal format 0
  430. (wFormat > 1)) // illegal format
  431. {
  432. errCode = MCIERR_INVALID_FILE;
  433. goto ERROR_HDLR;
  434. }
  435. thisSeqStream->wPortNum = MIDI_MAPPER;
  436. /* Create a sequence given the header data (will add stream as param) */
  437. open.lpMIDIFileHdr = (LPBYTE) fileHeader; // step over mhdr+len
  438. open.dwCallback = (DWORD_PTR) mciSeqCallback;
  439. open.dwInstance = (DWORD_PTR) thisSeqStream;
  440. open.hStream = (HANDLE)thisSeqStream;
  441. smErrCode = (DWORD)midiSeqMessage(NULL, // open sequence
  442. SEQ_OPEN,
  443. (DWORD_PTR)(LPVOID)&open,
  444. (DWORD_PTR)(LPVOID)&(thisSeqStream->hSeq));
  445. if (smErrCode != MIDISEQERR_NOERROR)
  446. {
  447. // N.B. at this point if failed in sequencer, sequence is invalid
  448. // thisSeqStream->hSeq should be NULL
  449. if (smErrCode == MIDISEQERR_NOMEM)
  450. errCode = MCIERR_OUT_OF_MEMORY;
  451. else
  452. errCode = MCIERR_INVALID_FILE;
  453. goto ERROR_HDLR;
  454. }
  455. thisSeqStream->trackStreamListHandle = List_Create((LONG) sizeof(TrackStreamType),0l);
  456. if (thisSeqStream->trackStreamListHandle == NULLLIST)
  457. {
  458. errCode = MCIERR_OUT_OF_MEMORY; // not a memory problem
  459. goto ERROR_HDLR;
  460. }
  461. mmioAscend(hmmio, &mmck, 0);
  462. // MIDI track data does not have RIFF even byte restriction
  463. if (mmck.cksize & 1L)
  464. mmioSeek(hmmio, -1L, SEEK_CUR);
  465. mmck.ckid = MTrk;
  466. iTrackNum = 0;
  467. while (wTracks-- > 0)
  468. {
  469. int i;
  470. // allocate trackstream record and put it in list
  471. if (! (thisTrackStream = (TrackStreamType*)
  472. List_Allocate(thisSeqStream->trackStreamListHandle)))
  473. {
  474. errCode = MCIERR_OUT_OF_MEMORY;
  475. goto ERROR_HDLR;
  476. }
  477. List_Attach_Tail(thisSeqStream->trackStreamListHandle,
  478. (NPSTR) thisTrackStream);
  479. for(i = 0; i < NUMHDRS; i++) // alloc and lock two midihdr buffers for it
  480. {
  481. if (! (thisTrackStream->fileHeaders[i] = (LPMIDISEQHDR)
  482. #ifdef WIN16
  483. GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE, (LONG) (sizeof(MIDISEQHDR) + BUFFSIZE))))
  484. #else
  485. GlobalAlloc(GPTR, (LONG) (sizeof(MIDISEQHDR) + BUFFSIZE))))
  486. #endif // WIN16
  487. {
  488. errCode = MCIERR_OUT_OF_MEMORY;
  489. goto ERROR_HDLR;
  490. }
  491. thisTrackStream->fileHeaders[i]->lpData =
  492. (LPSTR) (((DWORD_PTR) thisTrackStream->fileHeaders[i]) +
  493. sizeof(MIDISEQHDR));
  494. thisTrackStream->fileHeaders[i]->wTrack = (WORD)iTrackNum;
  495. thisTrackStream->fileHeaders[i]->lpNext = NULL;
  496. thisTrackStream->fileHeaders[i]->wFlags = MIDISEQHDR_DONE;
  497. };
  498. Yield();
  499. // set up to read this track's 'Mtrk' & length
  500. if (mmioDescend(hmmio, &mmck, &mmckData, MMIO_FINDCHUNK))
  501. {
  502. errCode = MCIERR_INVALID_FILE;
  503. goto ERROR_HDLR;
  504. }
  505. mmck.cksize = GETMOTDWORD(&mmck.cksize);
  506. // set up beginning, current, and end
  507. thisTrackStream->beginning = mmck.dwDataOffset;
  508. thisTrackStream->current = thisTrackStream->beginning;
  509. thisTrackStream->end = thisTrackStream->beginning + mmck.cksize - 1;
  510. // minimum track length is 3 bytes
  511. // verify track ends with "End Of Track" meta event
  512. mmioSeek(hmmio, (LONG)thisTrackStream->end - 2, SEEK_SET);
  513. mmioRead(hmmio, (HPSTR)fileHeader, 3L); // read EOT
  514. if ((fileHeader[0] != 0xFF) || (fileHeader[1] != 0x2F) ||
  515. (fileHeader[2] != 0x00))
  516. {
  517. errCode = MCIERR_INVALID_FILE;
  518. goto ERROR_HDLR;
  519. }
  520. mmioAscend(hmmio, &mmck, 0);
  521. // MIDI track data does not have RIFF even byte restriction
  522. if (mmck.cksize & 1L)
  523. mmioSeek(hmmio, -1L, SEEK_CUR);
  524. iTrackNum++;
  525. }
  526. mmioClose(hmmio, 0); // don't need in this task context any longer
  527. hmmio = NULL;
  528. // create cycle task
  529. thisSeqStream->streaming = TRUE;
  530. thisSeqStream->streamTaskHandle = 0; // don't know it yet
  531. if (mmTaskCreate(mciStreamCycle, &thisSeqStream->streamThreadHandle,
  532. (DWORD_PTR)thisSeqStream))
  533. //mmTaskCreate returns 0 if successful
  534. {
  535. errCode = MCIERR_OUT_OF_MEMORY;
  536. goto ERROR_HDLR;
  537. }
  538. thisSeqStream->bLastPaused = FALSE; // never paused
  539. thisSeqStream->hNotifyCB = NULL; // no notify pending
  540. *lppStream = thisSeqStream;
  541. return 0;
  542. ERROR_HDLR:
  543. if (hmmio) // close file if it was opened
  544. mmioClose(hmmio, 0);
  545. if (thisSeqStream)
  546. {
  547. midiSeqMessage((HMIDISEQ) thisSeqStream->hSeq, SEQ_SETPORTOFF, FALSE, 0L);
  548. EndFileStream(thisSeqStream); //dealloc it and everything it owns
  549. }
  550. return errCode;
  551. }
  552. /***************************************************************************/
  553. PUBLIC VOID FAR PASCAL StreamTrackReset(pSeqStreamType pStream, UINT wTrackNum)
  554. /* Find track stream struct within pStream as specified by wTrackNum & reset
  555. it to start over. */
  556. {
  557. TrackStreamType *trackStream;
  558. int iTrackNum;
  559. if (pStream->trackStreamListHandle == NULLLIST)
  560. return;
  561. trackStream = (TrackStreamType*) List_Get_First(pStream->trackStreamListHandle);
  562. iTrackNum = 0;
  563. while ((trackStream) && ((int)wTrackNum != iTrackNum++))
  564. {
  565. trackStream = (TrackStreamType*) List_Get_Next(pStream->trackStreamListHandle, trackStream);
  566. }
  567. if (trackStream)
  568. {
  569. int i;
  570. trackStream->current = trackStream->beginning; // reset stream
  571. // now signal on all buffers that have been blocked on
  572. // (have done bit set)
  573. for(i = 0; i < NUMHDRS; i++) // fill any of these that're MT
  574. if (!(trackStream->fileHeaders[i]->wFlags & MIDISEQHDR_DONE))
  575. {
  576. trackStream->fileHeaders[i]->wFlags |= MIDISEQHDR_DONE; //set it
  577. TaskSignal(pStream->streamTaskHandle, WTM_FILLBUFFER);
  578. }
  579. }
  580. }
  581. /***************************************************************************/
  582. PUBLIC VOID FAR PASCAL _LOADDS mciStreamCycle(DWORD_PTR dwInst)
  583. /* Fills any buffers for this track that are empty. Rule: at any time, the
  584. block count = the number of buffers - number of buffers with done bit clr
  585. (i.e. sent) - 1. Note that this will normally be -1 (asleep). When a
  586. buffer is freed, the done bit is set and we signal (block count++).
  587. Important: when a buffer is available, but we've run out of data to send
  588. on that track, we clr the done bit and block anyway (otherwise we wouldn't
  589. go back to sleep properly). The only thing is that we must properly regain
  590. our original status if the sequence is ever reset. This is accomplished by
  591. signaling on every buffer with its done bit cleared. (This is like
  592. signaling on every buffer that's been sent but not returned + any buffers
  593. ignored becuase there was no data to send on their track.)
  594. Whenever we signal for a buffer, we must be SURE that its done bit is set
  595. -- this is to maintain our rule above. Otherwise it will break badly! */
  596. {
  597. TrackStreamType *trackStream;
  598. SeqStreamType *seqStream = (SeqStreamType*)dwInst;
  599. MMCKINFO mmckData;
  600. HMMIO hmmio;
  601. EnterSeq();
  602. /*
  603. ** Make a safe "user" call so that user knows about our thread.
  604. */
  605. GetDesktopWindow();
  606. if (!seqStream->streamTaskHandle) {
  607. seqStream->streamTaskHandle = mmGetCurrentTask(); // fill it in asap
  608. }
  609. hmmio = msOpenFile(seqStream->szFilename, &mmckData, seqStream->pIOProc); // open the MIDI file
  610. seqStream->hmmio = hmmio;
  611. // block count = 0
  612. // first signal on all
  613. trackStream = (TrackStreamType*) List_Get_First(seqStream->trackStreamListHandle);
  614. while(trackStream)
  615. {
  616. int i;
  617. for(i = 0; i < NUMHDRS; i++) // fill any of these that're MT
  618. {
  619. trackStream->fileHeaders[i]->wFlags |= MIDISEQHDR_DONE; //set it
  620. TaskSignal(seqStream->streamTaskHandle, WTM_FILLBUFFER);
  621. }
  622. trackStream = (TrackStreamType*) List_Get_Next(seqStream->trackStreamListHandle, trackStream);
  623. }
  624. // block count = number of buffers
  625. TaskBlock();
  626. // block count == number of buffers - 1
  627. do
  628. {
  629. trackStream = (TrackStreamType*) List_Get_First(seqStream->trackStreamListHandle);
  630. while ((trackStream) && (seqStream->streaming))
  631. {
  632. int i;
  633. for(i = 0; i < NUMHDRS; i++) // fill any of these that're MT
  634. {
  635. /* if the header isn't being used, fill it and send it to
  636. the sequencer */
  637. if ((trackStream->fileHeaders[i]->wFlags & MIDISEQHDR_DONE) &&
  638. (seqStream->streaming))
  639. {
  640. int iDataToRead;
  641. mmioSeek(seqStream->hmmio, (LONG) trackStream->current, SEEK_SET);
  642. iDataToRead = (int) min((DWORD) BUFFSIZE,
  643. (trackStream->end - trackStream->current) + 1);
  644. trackStream->fileHeaders[i]->wFlags &=
  645. ~(MIDISEQHDR_DONE + MIDISEQHDR_EOT + MIDISEQHDR_BOT);
  646. // clr the done beginning and end regardless
  647. if (iDataToRead > 0)
  648. {
  649. if (trackStream->current == trackStream->beginning)
  650. trackStream->fileHeaders[i]->wFlags |=
  651. MIDISEQHDR_BOT; // set the beginning of track flag
  652. mmioRead(seqStream->hmmio,
  653. (HPSTR) trackStream->fileHeaders[i]->lpData, iDataToRead);
  654. trackStream->fileHeaders[i]->dwLength = iDataToRead;
  655. trackStream->current += iDataToRead;
  656. trackStream->fileHeaders[i]->reserved =
  657. ((trackStream->current - trackStream->beginning) - 1);
  658. if (trackStream->current > trackStream->end)
  659. trackStream->fileHeaders[i]->wFlags |=
  660. MIDISEQHDR_EOT; // set the end of track flag
  661. if (seqStream->streaming)
  662. midiSeqMessage((HMIDISEQ) seqStream->hSeq, SEQ_TRACKDATA,
  663. (DWORD_PTR) trackStream->fileHeaders[i], 0L); // send it
  664. } // if data to read
  665. while (seqStream->streaming) {
  666. MIDISEQINFO seqInfo;
  667. switch (TaskBlock()) {
  668. case WTM_DONEPLAY:
  669. midiSeqMessage((HMIDISEQ)seqStream->hSeq, SEQ_GETINFO, (DWORD_PTR)(LPMIDISEQINFO)&seqInfo, 0L);
  670. if (!seqInfo.bPlaying)
  671. midiSeqMessage((HMIDISEQ)seqStream->hSeq, SEQ_SETPORTOFF, FALSE, 0L);
  672. continue;
  673. case WTM_QUITTASK:
  674. case WTM_FILLBUFFER:
  675. break;
  676. }
  677. break;
  678. }
  679. //BLOCK even if data wasn't available (buffer still "used")
  680. // when all buffers have been blocked, we'll sleep here
  681. if (seqStream->streaming)
  682. // don't yield to close, 'cause it deallocs seq
  683. Yield(); // yield in case cpu bound
  684. } // if done bit set and streaming
  685. } // for i
  686. if (seqStream->streaming)
  687. trackStream = (TrackStreamType*) List_Get_Next(seqStream->trackStreamListHandle, trackStream);
  688. } // while (trackStream)
  689. } while(seqStream->streaming);
  690. mmioClose(seqStream->hmmio, 0);
  691. seqStream->streamTaskHandle = 0;
  692. LeaveSeq();
  693. }
  694. /***************************************************************************
  695. *
  696. * @doc INTERNAL
  697. *
  698. * @api LRESULT | DriverProc | The entry point for an installable driver.
  699. *
  700. * @parm DWORD | dwDriverId | For most messages, dwDriverId is the DWORD
  701. * value that the driver returns in response to a DRV_OPEN message.
  702. * Each time that the driver is opened, through the DrvOpen API,
  703. * the driver receives a DRV_OPEN message and can return an
  704. * arbitrary, non-zero, value. The installable driver interface
  705. * saves this value and returns a unique driver handle to the
  706. * application. Whenever the application sends a message to the
  707. * driver using the driver handle, the interface routes the message
  708. * to this entry point and passes the corresponding dwDriverId.
  709. *
  710. * This mechanism allows the driver to use the same or different
  711. * identifiers for multiple opens but ensures that driver handles
  712. * are unique at the application interface layer.
  713. *
  714. * The following messages are not related to a particular open
  715. * instance of the driver. For these messages, the dwDriverId
  716. * will always be ZERO.
  717. *
  718. * DRV_LOAD, DRV_FREE, DRV_ENABLE, DRV_DISABLE, DRV_OPEN
  719. *
  720. * @parm UINT | wMessage | The requested action to be performed. Message
  721. * values below DRV_RESERVED are used for globally defined messages.
  722. * Message values from DRV_RESERVED to DRV_USER are used for
  723. * defined driver portocols. Messages above DRV_USER are used
  724. * for driver specific messages.
  725. *
  726. * @parm LPARAM | lParam1 | Data for this message. Defined separately for
  727. * each message
  728. *
  729. * @parm LPARAM | lParam2 | Data for this message. Defined separately for
  730. * each message
  731. *
  732. * @rdesc Defined separately for each message.
  733. *
  734. ***************************************************************************/
  735. PUBLIC LRESULT FAR PASCAL _LOADDS DriverProc (DWORD_PTR dwDriverID, HDRVR hDriver, UINT wMessage, LPARAM lParam1, LPARAM lParam2)
  736. {
  737. DWORD_PTR dwRes = 0L;
  738. switch (wMessage)
  739. {
  740. TIMECAPS timeCaps;
  741. // Standard, globally used messages.
  742. case DRV_LOAD:
  743. /*
  744. Sent to the driver when it is loaded. Always the first
  745. message received by a driver.
  746. */
  747. /*
  748. Find the minimum period we can support
  749. */
  750. if (timeGetDevCaps(&timeCaps, sizeof(timeCaps)) == MMSYSERR_NOERROR)
  751. {
  752. MINPERIOD = timeCaps.wPeriodMin;
  753. /* create a list of seq streams */
  754. SeqStreamListHandle = List_Create((LONG) sizeof(SeqStreamType), 0L);
  755. // following sets up command table to enable subsequent commands
  756. dwRes = 1L;
  757. }
  758. break;
  759. case DRV_FREE:
  760. /*
  761. Sent to the driver when it is about to be discarded. This
  762. will always be the last message received by a driver before
  763. it is freed.
  764. dwDriverID is 0L.
  765. lParam1 is 0L.
  766. lParam2 is 0L.
  767. Return value is IGNORED.
  768. */
  769. //dwReturn = midiSeqTerminate();
  770. dwRes = 1L;
  771. break;
  772. case DRV_OPEN:
  773. /*
  774. Sent to the driver when it is opened.
  775. dwDriverID is 0L.
  776. lParam1 is a far pointer to a zero-terminated string
  777. containing the name used to open the driver.
  778. lParam2 is passed through from the drvOpen call.
  779. Return 0L to FAIL the open.
  780. */
  781. if (!lParam2)
  782. dwRes = CONFIG_ID;
  783. else
  784. {
  785. ((LPMCI_OPEN_DRIVER_PARMS)lParam2)->wCustomCommandTable = MCI_TABLE_NOT_PRESENT;
  786. ((LPMCI_OPEN_DRIVER_PARMS)lParam2)->wType = MCI_DEVTYPE_SEQUENCER;
  787. dwRes = ((LPMCI_OPEN_DRIVER_PARMS)lParam2)->wDeviceID;
  788. }
  789. break;
  790. case DRV_CLOSE:
  791. /*
  792. Sent to the driver when it is closed. Drivers are unloaded
  793. when the close count reaches zero.
  794. dwDriverID is the driver identifier returned from the
  795. corresponding DRV_OPEN.
  796. lParam1 is passed through from the drvOpen call.
  797. lParam2 is passed through from the drvOpen call.
  798. Return 0L to FAIL the close.
  799. */
  800. dwRes = 1L;
  801. break;
  802. case DRV_ENABLE:
  803. /*
  804. Sent to the driver when the driver is loaded or reloaded
  805. and whenever windows is enabled. Drivers should only
  806. hook interrupts or expect ANY part of the driver to be in
  807. memory between enable and disable messages
  808. dwDriverID is 0L.
  809. lParam1 is 0L.
  810. lParam2 is 0L.
  811. Return value is ignored.
  812. */
  813. dwRes = 1L;
  814. break;
  815. case DRV_DISABLE:
  816. /*
  817. Sent to the driver before the driver is freed.
  818. and whenever windows is disabled
  819. dwDriverID is 0L.
  820. lParam1 is 0L.
  821. lParam2 is 0L.
  822. Return value is ignored.
  823. */
  824. dwRes = 1L;
  825. break;
  826. case DRV_QUERYCONFIGURE:
  827. /*
  828. Sent to the driver so that applications can
  829. determine whether the driver supports custom
  830. configuration. The driver should return a
  831. non-zero value to indicate that configuration
  832. is supported.
  833. dwDriverID is the value returned from the DRV_OPEN
  834. call that must have succeeded before this message
  835. was sent.
  836. lParam1 is passed from the app and is undefined.
  837. lParam2 is passed from the app and is undefined.
  838. return 1L to indicate configuration supported.
  839. */
  840. dwRes = 1L;
  841. break;
  842. case DRV_CONFIGURE:
  843. /*
  844. Sent to the driver so that it can display a custom
  845. configuration dialog box.
  846. lParam1 is passed from the app. and should contain
  847. the parent window handle in the loword.
  848. lParam2 is passed from the app and is undefined.
  849. return value is undefined.
  850. Drivers should create their own section in
  851. system.ini. The section name should be the driver
  852. name.
  853. */
  854. if ( lParam1 )
  855. dwRes = MIDIConfig((HWND)LOWORD (lParam1));
  856. else
  857. dwRes = (LRESULT)DRVCNF_CANCEL;
  858. break;
  859. case DRV_INSTALL:
  860. case DRV_REMOVE:
  861. dwRes = DRVCNF_OK;
  862. break;
  863. default:
  864. if (CONFIG_ID != dwDriverID &&
  865. wMessage >= DRV_MCI_FIRST && wMessage <= DRV_MCI_LAST) {
  866. EnterSeq();
  867. dwRes = mciDriverEntry ((MCIDEVICEID)dwDriverID, wMessage,
  868. (DWORD_PTR)lParam1, (DWORD_PTR)lParam2);
  869. LeaveSeq();
  870. }
  871. else
  872. dwRes = (DWORD_PTR)DefDriverProc(dwDriverID, hDriver, wMessage, lParam1, lParam2);
  873. break;
  874. }
  875. return (LRESULT)dwRes;
  876. }
  877. #ifdef WIN32
  878. /**************************************************************************
  879. @doc EXTERNAL
  880. @api BOOL | DllInstanceInit | This procedure is called whenever a
  881. process attaches or detaches from the DLL.
  882. @parm PVOID | hModule | Handle of the DLL.
  883. @parm ULONG | Reason | What the reason for the call is.
  884. @parm PCONTEXT | pContext | Some random other information.
  885. @rdesc The return value is TRUE if the initialisation completed ok,
  886. FALSE if not.
  887. **************************************************************************/
  888. BOOL DllInstanceInit(PVOID hModule, ULONG Reason, PCONTEXT pContext)
  889. {
  890. UNREFERENCED_PARAMETER(pContext);
  891. if (Reason == DLL_PROCESS_ATTACH) {
  892. /*
  893. Initialize our critical section
  894. */
  895. InitCrit();
  896. hInstance = hModule;
  897. DisableThreadLibraryCalls(hModule);
  898. } else if (Reason == DLL_PROCESS_DETACH) {
  899. DeleteCrit();
  900. }
  901. return TRUE;
  902. }
  903. /************************************************************************/
  904. #endif
  905. /*****************************************************************************
  906. @doc INTERNAL MCISEQ
  907. @api int | MIDIConfig |
  908. @parm HWND | hwndParent |
  909. @rdesc
  910. @comm
  911. *****************************************************************************/
  912. typedef BOOL (WINAPI *SHOWMMCPLPROPSHEETW)(HWND hwndParent,
  913. LPCWSTR szPropSheetID,
  914. LPWSTR szTabName,
  915. LPWSTR szCaption);
  916. int MIDIConfig (HWND hwndParent)
  917. {
  918. static HWND hwndPrevParent = NULL;
  919. WCHAR szCaptionW[ 128 ];
  920. // We need only a unicode version of the caption (for FindWindow()
  921. // and ShowMMCPLPropertySheetW(), which are unicode-enabled).
  922. //
  923. LoadStringW(hInstance,IDS_MIDICAPTION,szCaptionW,cchLENGTH(szCaptionW));
  924. if (hwndPrevParent)
  925. {
  926. BringWindowToTop(FindWindowW(NULL, szCaptionW));
  927. }
  928. else
  929. {
  930. HINSTANCE h;
  931. SHOWMMCPLPROPSHEETW fn;
  932. static TCHAR aszMMSystemW[] = TEXT("MMSYS.CPL");
  933. static char aszShowPropSheetA[] = "ShowMMCPLPropertySheetW";
  934. static WCHAR aszMIDIW[] = L"MIDI";
  935. WCHAR szTabNameW[64];
  936. LoadStringW(hInstance, IDS_MIDITAB, szTabNameW, cchLENGTH(szTabNameW));
  937. h = LoadLibrary (aszMMSystemW);
  938. if (h)
  939. {
  940. fn = (SHOWMMCPLPROPSHEETW)GetProcAddress(h, aszShowPropSheetA);
  941. if (fn)
  942. {
  943. BOOL f;
  944. hwndPrevParent = hwndParent;
  945. f = fn(hwndParent, aszMIDIW, szTabNameW, szCaptionW);
  946. hwndPrevParent = NULL;
  947. }
  948. FreeLibrary(h);
  949. }
  950. }
  951. return DRVCNF_OK;
  952. }