Source code of Windows XP (NT5)
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.

676 lines
25 KiB

  1. /*---------------------------------------------------------------------------*/
  2. /*
  3. Copyright (c) 1985-1998 Microsoft Corporation
  4. Title: mcicmds.c - Multimedia Systems Media Control Interface
  5. Contains specific mci command implementations
  6. Version: 1.00
  7. Date: 7-MAR-1991
  8. Author: Greg Simons
  9. ------------------------------------------------------------------------------
  10. Change log:
  11. DATE REV DESCRIPTION
  12. ----------- ----- -----------------------------------------------------------
  13. 7-MAR-1991 GregSi Original
  14. */
  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 <windows.h>
  27. #include <mmsystem.h>
  28. #include <mmddk.h>
  29. #include <string.h>
  30. #include <wchar.h>
  31. #include "mmsys.h"
  32. #include "list.h"
  33. #include "mciseq.h"
  34. #ifdef WIN16
  35. #define SZCODE char _based(_segname("_CODE"))
  36. #else
  37. #define WSZCODE WCHAR
  38. #endif
  39. PRIVATE WSZCODE aszAppName[] = L"mciseq.drv";
  40. PRIVATE WSZCODE aszSystemIni[] = L"system.ini";
  41. PRIVATE WSZCODE aszTrue[] = L"true";
  42. PRIVATE WSZCODE aszNull[] = L"";
  43. PRIVATE WSZCODE aszDisableWarning[] = L"disablewarning";
  44. /*---------------------------------------------------------------------------*/
  45. PUBLIC DWORD NEAR PASCAL msOpen(
  46. pSeqStreamType FAR *lppStream,
  47. MCIDEVICEID wDeviceID,
  48. DWORD dwFlags,
  49. LPMCI_OPEN_PARMS lpOpen)
  50. {
  51. DWORD dwReturn; // to be returned from this function
  52. LPCWSTR lpstrFileName;
  53. LPMMIOPROC pIOProc;
  54. pSeqStreamType pStream;
  55. if ((dwFlags & (MCI_OPEN_ELEMENT | MCI_OPEN_ELEMENT_ID)) == (MCI_OPEN_ELEMENT | MCI_OPEN_ELEMENT_ID))
  56. return MCIERR_FLAGS_NOT_COMPATIBLE;
  57. if (!(dwFlags & (MCI_OPEN_ELEMENT | MCI_OPEN_ELEMENT_ID)))
  58. return 0L;
  59. if (dwFlags & MCI_OPEN_SHAREABLE)
  60. return MCIERR_UNSUPPORTED_FUNCTION;
  61. if (dwFlags & MCI_OPEN_ELEMENT)
  62. pIOProc = NULL;
  63. else
  64. pIOProc = *(LPMMIOPROC *)(lpOpen + 1);
  65. // open the "stream" (opens file, allocs data, creates streaming task,
  66. // and calls mmseq (sequencer) to have it allocate sequence structure)
  67. lpstrFileName = lpOpen->lpstrElementName;
  68. // Does not support 'new'
  69. if (lpstrFileName != NULL && *lpstrFileName == '\0')
  70. return MCIERR_FILENAME_REQUIRED;
  71. dwReturn = msOpenStream(&pStream, lpstrFileName, pIOProc);
  72. if (!dwReturn) {
  73. MIDISEQINFO seqInfo;
  74. pStream->wDeviceID = wDeviceID;
  75. pStream->wNotifyMsg = 0;
  76. // set up to remember when seq calls back
  77. // tell sequencer to prepare to play (actually parses whole file
  78. // and creates tempo map [which allocs memory])
  79. if (midiSeqMessage(pStream->hSeq, SEQ_SETUPTOPLAY, 0L, 0L) == MIDISEQERR_NOMEM)
  80. return MCIERR_OUT_OF_MEMORY;
  81. // get back file division type (ppqn, smpte, etc...)
  82. midiSeqMessage(pStream->hSeq, SEQ_GETINFO, (DWORD_PTR)(LPMIDISEQINFO)&seqInfo, 0L);
  83. pStream->fileDivType = seqInfo.wDivType;
  84. // set stream display type default based on file div type
  85. switch (pStream->fileDivType) {
  86. case SEQ_DIV_PPQN:
  87. pStream->userDisplayType = MCI_SEQ_FORMAT_SONGPTR;
  88. break;
  89. case SEQ_DIV_SMPTE_24:
  90. pStream->userDisplayType = MCI_FORMAT_SMPTE_24;
  91. break;
  92. case SEQ_DIV_SMPTE_25:
  93. pStream->userDisplayType = MCI_FORMAT_SMPTE_25;
  94. break;
  95. case SEQ_DIV_SMPTE_30:
  96. pStream->userDisplayType = MCI_FORMAT_SMPTE_30;
  97. break;
  98. case SEQ_DIV_SMPTE_30DROP:
  99. pStream->userDisplayType = MCI_FORMAT_SMPTE_30DROP;
  100. break;
  101. }
  102. //Force a wait until stream is initialized.
  103. do {
  104. Yield();
  105. midiSeqMessage(pStream->hSeq, SEQ_GETINFO, (DWORD_PTR)(LPMIDISEQINFO)&seqInfo, 0L);
  106. } while ((!seqInfo.bReadyToPlay) && (seqInfo.tempoMapExists) && (seqInfo.bLegalFile));
  107. // (if tempo map deleted, this is a sign that a tempo map alloc failed)
  108. // important: check to see if tempo map allocation failed
  109. // if so, close sequence and return failure
  110. mciSetDriverData(wDeviceID, (DWORD_PTR)pStream);
  111. // MCI bookkeeping -- must come before close below
  112. if (!seqInfo.tempoMapExists) {
  113. dwReturn = MCIERR_OUT_OF_MEMORY;
  114. mciDriverEntry(wDeviceID, MCI_CLOSE_DRIVER, dwFlags & ~MCI_NOTIFY, (DWORD_PTR)lpOpen);
  115. } else if (!seqInfo.bLegalFile) {
  116. dwReturn = MCIERR_INVALID_FILE;
  117. mciDriverEntry(wDeviceID, MCI_CLOSE_DRIVER, dwFlags & ~MCI_NOTIFY, (DWORD_PTR)lpOpen);
  118. } else
  119. *lppStream = pStream;
  120. }
  121. return dwReturn;
  122. }
  123. /*---------------------------------------------------------------------------*/
  124. PUBLIC DWORD NEAR PASCAL msClose(
  125. pSeqStreamType pStream,
  126. MCIDEVICEID wDeviceID,
  127. DWORD dwFlags)
  128. {
  129. dprintf2(("close"));
  130. if (pStream) {
  131. midiSeqMessage(pStream->hSeq, SEQ_STOP, 0L, 0L);
  132. dprintf4(("calling endfilestream"));
  133. midiSeqMessage(pStream->hSeq, SEQ_SETPORTOFF, FALSE, 0L);
  134. dprintf4(("closed port"));
  135. Notify(pStream, (dwFlags == MCI_NOTIFY) ? MCI_NOTIFY_SUPERSEDED : MCI_NOTIFY_ABORTED);
  136. EndFileStream(pStream);
  137. dprintf4(("back from endfilestream"));
  138. }
  139. return 0;
  140. }
  141. /*---------------------------------------------------------------------------*/
  142. PRIVATE DWORD PASCAL NEAR OpenMidiPort(
  143. pSeqStreamType pStream)
  144. {
  145. if ((pStream->wPortNum == MCI_SEQ_NONE) || (midiSeqMessage(pStream->hSeq, SEQ_QUERYHMIDI, 0L, 0L)))
  146. return 0;
  147. if (!midiOutGetNumDevs()) {
  148. dprintf1(("OpenMidiPort - no MIDI ports present"));
  149. return MCIERR_SEQ_NOMIDIPRESENT; // No midi ports present
  150. }
  151. switch ((UINT)midiSeqMessage(pStream->hSeq, SEQ_SETPORT, (DWORD)pStream->wPortNum, 0L)) {
  152. case 0:
  153. return 0;
  154. case MMSYSERR_ALLOCATED:
  155. return MCIERR_SEQ_PORT_INUSE;
  156. case MMSYSERR_BADDEVICEID:
  157. return MCIERR_SEQ_PORT_NONEXISTENT;
  158. case MIDIERR_NODEVICE:
  159. return MIDIERR_NODEVICE;
  160. default:
  161. return MCIERR_SEQ_NOMIDIPRESENT;
  162. }
  163. }
  164. /*---------------------------------------------------------------------------*/
  165. PUBLIC DWORD NEAR PASCAL msPlay(pSeqStreamType pStream, MCIDEVICEID wDeviceID,
  166. DWORD dwFlags, LPMCI_PLAY_PARMS lpPlay)
  167. {
  168. MIDISEQINFO seqInfo;
  169. DWORD dwPlayFrom;
  170. DWORD dwPlayTo;
  171. DWORD dwReturn;
  172. if (0 != (dwReturn = OpenMidiPort(pStream)))
  173. return dwReturn;
  174. // get info to aid in possible time format conversions (from & to)
  175. midiSeqMessage(pStream->hSeq, SEQ_GETINFO, (DWORD_PTR)(LPMIDISEQINFO)&seqInfo, 0L);
  176. // convert "to," if any
  177. if (MCI_TO & dwFlags) {
  178. // is the user typing in what he believes to be the end?
  179. if (lpPlay->dwTo == CnvtTimeFromSeq(pStream, seqInfo.dwLength, &seqInfo))
  180. dwPlayTo = seqInfo.dwLength; // if so, let him have it
  181. else
  182. dwPlayTo = CnvtTimeToSeq(pStream, lpPlay->dwTo, &seqInfo);
  183. } else
  184. dwPlayTo = PLAYTOEND; // has no effect
  185. // convert "from," if any
  186. if (MCI_FROM & dwFlags)
  187. dwPlayFrom = CnvtTimeToSeq(pStream, lpPlay->dwFrom, &seqInfo);
  188. else
  189. dwPlayFrom = 0;
  190. // complain if input out of range
  191. // "to" in range [0..length]
  192. if ((MCI_TO & dwFlags) && (!RangeCheck(pStream, lpPlay->dwTo)))
  193. dwReturn = MCIERR_OUTOFRANGE;
  194. // "from" in range [0..length]
  195. else if ((MCI_FROM & dwFlags) && (!RangeCheck(pStream, lpPlay->dwFrom)))
  196. dwReturn = MCIERR_OUTOFRANGE;
  197. // from before to (can't play backwards!)
  198. else if ((MCI_FROM & dwFlags) && (MCI_TO & dwFlags) && (dwPlayFrom > dwPlayTo))
  199. dwReturn = MCIERR_OUTOFRANGE;
  200. // if from not specified, current pos implied -- don't play backwards
  201. else if ((!(MCI_FROM & dwFlags)) && (MCI_TO & dwFlags) && (dwPlayTo < seqInfo.dwCurrentTick))
  202. dwReturn = MCIERR_OUTOFRANGE;
  203. else if (MCI_FROM & dwFlags)
  204. if (midiSeqMessage(pStream->hSeq, SEQ_SYNCSEEKTICKS, dwPlayFrom, 0) != MIDISEQERR_NOERROR)
  205. dwReturn = MCIERR_DEVICE_NOT_READY;
  206. if (!dwReturn) {
  207. if (0 != (dwReturn = (DWORD)midiSeqMessage(pStream->hSeq, SEQ_PLAY, dwPlayTo, 0L))) {
  208. // handle possible timer error (possibly due to timer.dll not loading)
  209. if (dwReturn == MIDISEQERR_TIMER)
  210. dwReturn = MCIERR_SEQ_TIMER;
  211. } else if ((dwFlags & MCI_WAIT)) {
  212. // handle "Play Wait"
  213. if (pStream->hNotifyCB)
  214. if (pStream->wNotifyMsg == MCI_PLAY) {
  215. if (dwPlayTo == PLAYTOEND)
  216. dwPlayTo = seqInfo.dwLength;
  217. Notify(pStream, ((dwFlags & MCI_FROM) || (pStream->dwNotifyOldTo != dwPlayTo)) ? MCI_NOTIFY_ABORTED : MCI_NOTIFY_SUPERSEDED);
  218. } else if (pStream->wNotifyMsg == MCI_SEEK)
  219. Notify(pStream, MCI_NOTIFY_SUPERSEDED);
  220. do {
  221. Yield();
  222. midiSeqMessage(pStream->hSeq, SEQ_GETINFO, (DWORD_PTR)(LPMIDISEQINFO)&seqInfo, 0L);
  223. } while ((seqInfo.bPlaying) && (!mciDriverYield(wDeviceID)));
  224. }
  225. }
  226. if (dwReturn && !seqInfo.bPlaying)
  227. midiSeqMessage(pStream->hSeq, SEQ_SETPORTOFF, FALSE, 0L);
  228. return dwReturn;
  229. }
  230. /*---------------------------------------------------------------------------*/
  231. PUBLIC DWORD NEAR PASCAL msSeek(
  232. pSeqStreamType pStream,
  233. MCIDEVICEID wDeviceID,
  234. DWORD dwParam1,
  235. LPMCI_SEEK_PARMS lpSeek)
  236. {
  237. DWORD dwFlags;
  238. DWORD dwSeekTo;
  239. MIDISEQINFO seqInfo;
  240. dwFlags = dwParam1 & ~(MCI_WAIT | MCI_NOTIFY); // don't consider these here
  241. if (!dwFlags)
  242. return MCIERR_MISSING_PARAMETER;
  243. if (dwFlags != (dwFlags & (MCI_TO | MCI_SEEK_TO_START | MCI_SEEK_TO_END)))
  244. return MCIERR_UNRECOGNIZED_KEYWORD;
  245. midiSeqMessage(pStream->hSeq, SEQ_GETINFO, (DWORD_PTR)(LPMIDISEQINFO)&seqInfo, 0L);
  246. switch (dwFlags) {
  247. case MCI_TO:
  248. if (!RangeCheck(pStream, lpSeek->dwTo))
  249. return MCIERR_OUTOFRANGE;
  250. dwSeekTo = CnvtTimeToSeq(pStream, lpSeek->dwTo, &seqInfo);
  251. break;
  252. case MCI_SEEK_TO_START:
  253. dwSeekTo = 0L;
  254. break;
  255. case MCI_SEEK_TO_END:
  256. dwSeekTo = seqInfo.dwLength;
  257. break;
  258. default:
  259. return MCIERR_FLAGS_NOT_COMPATIBLE;
  260. }
  261. // if playing, call self to pause 1st.
  262. if (seqInfo.bPlaying)
  263. mciDriverEntry(wDeviceID, MCI_PAUSE, dwFlags, (DWORD_PTR)lpSeek);
  264. // set up to remember when seq calls back
  265. if (midiSeqMessage(pStream->hSeq, SEQ_SEEKTICKS, dwSeekTo, 0) != MIDISEQERR_NOERROR)
  266. return MCIERR_DEVICE_NOT_READY;
  267. else if (dwParam1 & MCI_WAIT) {
  268. if (pStream->hNotifyCB)
  269. if (pStream->wNotifyMsg == MCI_SEEK)
  270. Notify(pStream, (pStream->dwNotifyOldTo != lpSeek->dwTo) ? MCI_NOTIFY_ABORTED : MCI_NOTIFY_SUPERSEDED);
  271. else if (pStream->wNotifyMsg == MCI_PLAY)
  272. Notify(pStream, MCI_NOTIFY_SUPERSEDED);
  273. do {
  274. midiSeqMessage(pStream->hSeq, SEQ_GETINFO, (DWORD_PTR)(LPMIDISEQINFO)&seqInfo, 0L);
  275. Yield();
  276. } while ((seqInfo.bSeeking) && (!mciDriverYield(wDeviceID)));
  277. }
  278. return 0;
  279. }
  280. /*---------------------------------------------------------------------------*/
  281. PUBLIC DWORD NEAR PASCAL msStatus(
  282. pSeqStreamType pStream,
  283. MCIDEVICEID wDeviceID,
  284. DWORD dwFlags,
  285. LPMCI_STATUS_PARMS lpStatus)
  286. {
  287. DWORD dwReturn;
  288. MIDISEQINFO seqInfo;
  289. DWORD dwStatusReturn = 0;
  290. if (!(dwFlags & MCI_STATUS_ITEM))
  291. return MCIERR_MISSING_PARAMETER;
  292. if ((dwFlags & MCI_TRACK) && (!((lpStatus->dwItem) & (MCI_STATUS_POSITION | MCI_STATUS_LENGTH))))
  293. return MCIERR_DUPLICATE_FLAGS;
  294. dwReturn = 0;
  295. midiSeqMessage(pStream->hSeq, SEQ_GETINFO, (DWORD_PTR)(LPMIDISEQINFO)&seqInfo, 0L);
  296. switch (lpStatus->dwItem) {
  297. UINT wThingsPerMin;
  298. UINT wResource;
  299. case MCI_STATUS_TIME_FORMAT:
  300. wResource = (UINT)pStream->userDisplayType;
  301. if (wResource == MCI_SEQ_FORMAT_SONGPTR)
  302. dwStatusReturn = (DWORD)MAKEMCIRESOURCE(MCI_SEQ_FORMAT_SONGPTR, MCI_SEQ_FORMAT_SONGPTR_S);
  303. else
  304. dwStatusReturn = (DWORD)MAKEMCIRESOURCE(wResource, wResource + MCI_FORMAT_RETURN_BASE);
  305. dwReturn = MCI_RESOURCE_RETURNED;
  306. break;
  307. case MCI_STATUS_POSITION:
  308. if (dwFlags & MCI_TRACK) {
  309. if ((lpStatus->dwTrack) != 1)
  310. dwReturn = MCIERR_OUTOFRANGE;
  311. else
  312. dwStatusReturn = 0L; //beginning of track
  313. } else if (dwFlags & MCI_STATUS_START)
  314. dwStatusReturn = 0L; //beginning of track
  315. else // normal status position request
  316. dwStatusReturn = CnvtTimeFromSeq(pStream, seqInfo.dwCurrentTick, &seqInfo);
  317. if (ColonizeOutput(pStream))
  318. dwReturn = MCI_COLONIZED4_RETURN;
  319. break;
  320. case MCI_STATUS_LENGTH:
  321. if (dwFlags & MCI_TRACK)
  322. if ((lpStatus->dwTrack) != 1)
  323. dwReturn = MCIERR_OUTOFRANGE;
  324. dwStatusReturn = CnvtTimeFromSeq(pStream, seqInfo.dwLength, &seqInfo);
  325. if (ColonizeOutput(pStream))
  326. dwReturn = MCI_COLONIZED4_RETURN;
  327. break;
  328. case MCI_STATUS_READY:
  329. dwReturn = MCI_RESOURCE_RETURNED;
  330. if (seqInfo.bReadyToPlay)
  331. dwStatusReturn = (DWORD)MAKEMCIRESOURCE(TRUE, MCI_TRUE);
  332. else
  333. dwStatusReturn = (DWORD)MAKEMCIRESOURCE(FALSE, MCI_FALSE);
  334. break;
  335. case MCI_STATUS_NUMBER_OF_TRACKS:
  336. dwStatusReturn = 1;
  337. break;
  338. case MCI_STATUS_MODE:
  339. dwReturn = MCI_RESOURCE_RETURNED;
  340. if (seqInfo.bSeeking)
  341. wResource = MCI_MODE_SEEK;
  342. else if (seqInfo.bPlaying)
  343. wResource = MCI_MODE_PLAY;
  344. else if (pStream->bLastPaused)
  345. wResource = MCI_MODE_PAUSE;
  346. else
  347. wResource = MCI_MODE_STOP;
  348. dwStatusReturn = (DWORD)MAKEMCIRESOURCE(wResource, wResource);
  349. break;
  350. case MCI_STATUS_MEDIA_PRESENT:
  351. dwReturn = MCI_RESOURCE_RETURNED;
  352. dwStatusReturn = (DWORD)MAKEMCIRESOURCE(TRUE, MCI_TRUE);
  353. break;
  354. case MCI_SEQ_STATUS_DIVTYPE:
  355. dwReturn = MCI_RESOURCE_RETURNED;
  356. switch (seqInfo.wDivType) {
  357. //NB: the MCI_SEQ_DIV... codes are string ids
  358. case SEQ_DIV_PPQN:
  359. wResource = MCI_SEQ_DIV_PPQN;
  360. break;
  361. case SEQ_DIV_SMPTE_24:
  362. wResource = MCI_SEQ_DIV_SMPTE_24;
  363. break;
  364. case SEQ_DIV_SMPTE_25:
  365. wResource = MCI_SEQ_DIV_SMPTE_25;
  366. break;
  367. case SEQ_DIV_SMPTE_30:
  368. wResource = MCI_SEQ_DIV_SMPTE_30;
  369. break;
  370. case SEQ_DIV_SMPTE_30DROP:
  371. wResource = MCI_SEQ_DIV_SMPTE_30DROP;
  372. break;
  373. }
  374. dwStatusReturn = (DWORD)MAKEMCIRESOURCE(wResource, wResource);
  375. break;
  376. case MCI_SEQ_STATUS_TEMPO:
  377. // tempo comes back in microseconds per tick -- convert to
  378. // more human-readable form if smpte: fps else bpm
  379. wThingsPerMin = (UINT)(USecPerMinute / ((DWORD)seqInfo.wResolution * (DWORD)seqInfo.dwTempo));
  380. if (seqInfo.wDivType == SEQ_DIV_PPQN)
  381. dwStatusReturn = (DWORD)wThingsPerMin;
  382. else
  383. dwStatusReturn = (DWORD)(wThingsPerMin / 60); // things per second
  384. break;
  385. case MCI_SEQ_STATUS_PORT:
  386. switch (pStream->wPortNum) {
  387. case (UINT)MIDI_MAPPER:
  388. dwReturn = MCI_RESOURCE_RETURNED;
  389. dwStatusReturn = (DWORD)MAKEMCIRESOURCE(MIDI_MAPPER, MIDIMAPPER_S);
  390. break;
  391. case MCI_SEQ_NONE:
  392. dwReturn = MCI_RESOURCE_RETURNED;
  393. dwStatusReturn = (DWORD)MAKEMCIRESOURCE(MCI_SEQ_NONE, MCI_SEQ_NONE_S);
  394. break;
  395. default:
  396. dwStatusReturn = pStream->wPortNum;
  397. break;
  398. }
  399. break;
  400. case MCI_SEQ_STATUS_SLAVE:
  401. dwReturn = MCI_RESOURCE_RETURNED;
  402. switch(seqInfo.wInSync) {
  403. case SEQ_SYNC_NOTHING:
  404. dwStatusReturn = (DWORD)MAKEMCIRESOURCE(MCI_SEQ_NONE, MCI_SEQ_NONE_S);
  405. break;
  406. case SEQ_SYNC_FILE:
  407. dwStatusReturn = (DWORD)MAKEMCIRESOURCE(MCI_SEQ_FILE, MCI_SEQ_FILE_S);
  408. break;
  409. case SEQ_SYNC_MIDI:
  410. dwStatusReturn = (DWORD)MAKEMCIRESOURCE(MCI_SEQ_MIDI, MCI_SEQ_MIDI_S);
  411. break;
  412. case SEQ_SYNC_SMPTE:
  413. dwStatusReturn = (DWORD)MAKEMCIRESOURCE(MCI_SEQ_SMPTE, MCI_SEQ_SMPTE_S);
  414. break;
  415. }
  416. break;
  417. case MCI_SEQ_STATUS_MASTER:
  418. dwReturn = MCI_RESOURCE_RETURNED;
  419. switch(seqInfo.wOutSync) {
  420. case SEQ_SYNC_NOTHING:
  421. dwStatusReturn = (DWORD)MAKEMCIRESOURCE(MCI_SEQ_NONE, MCI_SEQ_NONE_S);
  422. break;
  423. case SEQ_SYNC_MIDI:
  424. dwStatusReturn = (DWORD)MAKEMCIRESOURCE(MCI_SEQ_MIDI, MCI_SEQ_MIDI_S);
  425. break;
  426. case SEQ_SYNC_SMPTE:
  427. dwStatusReturn = (DWORD)MAKEMCIRESOURCE(MCI_SEQ_SMPTE, MCI_SEQ_SMPTE_S);
  428. break;
  429. }
  430. break;
  431. case MCI_SEQ_STATUS_OFFSET:
  432. dwStatusReturn = seqInfo.mmSmpteOffset.u.smpte.hour + ((DWORD)seqInfo.mmSmpteOffset.u.smpte.min << 8) + ((DWORD)seqInfo.mmSmpteOffset.u.smpte.sec << 16) + ((DWORD)seqInfo.mmSmpteOffset.u.smpte.frame << 24);
  433. dwReturn = MCI_COLONIZED4_RETURN;
  434. break;
  435. case MCI_STATUS_CURRENT_TRACK:
  436. dwStatusReturn = 1;
  437. break;
  438. default:
  439. dwReturn = MCIERR_UNSUPPORTED_FUNCTION;
  440. break;
  441. }
  442. // Return the status value in the struct
  443. lpStatus->dwReturn = dwStatusReturn;
  444. return dwReturn;
  445. }
  446. /*---------------------------------------------------------------------------*/
  447. PUBLIC DWORD NEAR PASCAL msGetDevCaps(
  448. pSeqStreamType pStream,
  449. MCIDEVICEID wDeviceID,
  450. DWORD dwFlags,
  451. LPMCI_GETDEVCAPS_PARMS lpCapParms)
  452. {
  453. if (!(dwFlags & MCI_GETDEVCAPS_ITEM)) {
  454. dprintf1(("msGetDevCaps - no capability requested"));
  455. return MCIERR_MISSING_PARAMETER;
  456. }
  457. switch (lpCapParms->dwItem) {
  458. case MCI_GETDEVCAPS_HAS_AUDIO:
  459. case MCI_GETDEVCAPS_COMPOUND_DEVICE:
  460. case MCI_GETDEVCAPS_USES_FILES:
  461. case MCI_GETDEVCAPS_CAN_PLAY:
  462. if (midiOutGetNumDevs()) {
  463. dprintf2(("msGetDevCaps - %d midi out port(s) found",
  464. midiOutGetNumDevs()));
  465. lpCapParms->dwReturn = (DWORD)MAKEMCIRESOURCE(TRUE, MCI_TRUE);
  466. } else {
  467. dprintf2(("msGetDevCaps - NO midi out ports found"));
  468. lpCapParms->dwReturn = (DWORD)MAKEMCIRESOURCE(FALSE, MCI_FALSE);
  469. }
  470. break;
  471. case MCI_GETDEVCAPS_CAN_EJECT:
  472. case MCI_GETDEVCAPS_CAN_RECORD:
  473. case MCI_GETDEVCAPS_HAS_VIDEO:
  474. case MCI_GETDEVCAPS_CAN_SAVE:
  475. lpCapParms->dwReturn = (DWORD)MAKEMCIRESOURCE(FALSE, MCI_FALSE);
  476. break;
  477. case MCI_GETDEVCAPS_DEVICE_TYPE:
  478. lpCapParms->dwReturn = (DWORD)MAKEMCIRESOURCE(MCI_DEVTYPE_SEQUENCER, MCI_DEVTYPE_SEQUENCER);
  479. break;
  480. default:
  481. return MCIERR_MISSING_PARAMETER;
  482. }
  483. return MCI_RESOURCE_RETURNED;
  484. }
  485. /*---------------------------------------------------------------------------*/
  486. PUBLIC DWORD NEAR PASCAL msInfo(
  487. pSeqStreamType pStream,
  488. MCIDEVICEID wDeviceID,
  489. DWORD dwFlags,
  490. LPMCI_INFO_PARMS lpInfo)
  491. {
  492. UINT wReturnLength;
  493. if (!lpInfo->lpstrReturn)
  494. return MCIERR_PARAM_OVERFLOW;
  495. dwFlags &= ~(MCI_NOTIFY | MCI_WAIT);
  496. if (dwFlags != (dwFlags & (MCI_INFO_PRODUCT | MCI_INFO_FILE)))
  497. return MCIERR_UNRECOGNIZED_KEYWORD;
  498. wReturnLength = (UINT)lpInfo->dwRetSize;
  499. *(lpInfo->lpstrReturn + wReturnLength - 1) = '\0';
  500. switch (dwFlags) {
  501. case MCI_INFO_PRODUCT:
  502. LoadString(hInstance, MSEQ_PRODUCTNAME, lpInfo->lpstrReturn, wReturnLength);
  503. break;
  504. case MCI_INFO_FILE:
  505. if (!pStream)
  506. return MCIERR_UNSUPPORTED_FUNCTION;
  507. else
  508. wcsncpy(lpInfo->lpstrReturn, (LPWSTR)pStream->szFilename, wReturnLength);
  509. break;
  510. default:
  511. return MCIERR_MISSING_PARAMETER;
  512. }
  513. if ( *(lpInfo->lpstrReturn + wReturnLength - 1) != '\0' ) {
  514. return MCIERR_PARAM_OVERFLOW;
  515. }
  516. else {
  517. lpInfo->dwRetSize = wcslen( lpInfo->lpstrReturn );
  518. return 0;
  519. }
  520. }
  521. /*---------------------------------------------------------------------------*/
  522. PUBLIC DWORD NEAR PASCAL msSet(
  523. pSeqStreamType pStream,
  524. MCIDEVICEID wDeviceID,
  525. DWORD dwFlags,
  526. LPMCI_SEQ_SET_PARMS lpSetParms)
  527. {
  528. DWORD dwReturn;
  529. dwFlags &= ~(MCI_NOTIFY | MCI_WAIT | MCI_SET_ON | MCI_SET_OFF);
  530. if (dwFlags != (dwFlags & (MCI_SEQ_SET_TEMPO | MCI_SEQ_SET_PORT | MCI_SEQ_SET_SLAVE | MCI_SEQ_SET_MASTER | MCI_SEQ_SET_OFFSET | MCI_SET_AUDIO | MCI_SET_VIDEO | MCI_SET_DOOR_OPEN | MCI_SET_DOOR_CLOSED | MCI_SET_TIME_FORMAT)))
  531. return MCIERR_UNRECOGNIZED_KEYWORD;
  532. dwReturn = 0; // no error by default
  533. switch (dwFlags) {
  534. UINT wPort;
  535. MMTIME mmOffset;
  536. case MCI_SET_TIME_FORMAT:
  537. switch ((UINT)lpSetParms->dwTimeFormat) {
  538. case MCI_FORMAT_SMPTE_30DROP:
  539. case MCI_FORMAT_SMPTE_30:
  540. case MCI_FORMAT_SMPTE_25:
  541. case MCI_FORMAT_SMPTE_24:
  542. case MCI_FORMAT_MILLISECONDS:
  543. pStream->userDisplayType = lpSetParms->dwTimeFormat;
  544. break;
  545. case MCI_SEQ_FORMAT_SONGPTR:
  546. if (pStream->fileDivType == SEQ_DIV_PPQN)
  547. pStream->userDisplayType = lpSetParms->dwTimeFormat;
  548. else
  549. dwReturn = MCIERR_SEQ_DIV_INCOMPATIBLE;
  550. break;
  551. default:
  552. dwReturn = MCIERR_BAD_TIME_FORMAT;
  553. break;
  554. }
  555. break;
  556. case MCI_SEQ_SET_TEMPO:
  557. dwReturn = (DWORD)midiSeqMessage(pStream->hSeq, SEQ_SETTEMPO, lpSetParms->dwTempo, 0L);
  558. break;
  559. case MCI_SEQ_SET_PORT:
  560. wPort = (UINT)lpSetParms->dwPort; // else use # passed in
  561. if (wPort == MCI_SEQ_NONE) {
  562. midiSeqMessage(pStream->hSeq, SEQ_SETPORTOFF, TRUE, 0L);
  563. pStream->wPortNum = MCI_SEQ_NONE; //store port num (so can return "none")
  564. } else if (!midiOutGetNumDevs())
  565. dwReturn = MCIERR_SEQ_NOMIDIPRESENT;
  566. else if ((wPort != MIDI_MAPPER) && (wPort >= midiOutGetNumDevs()))
  567. dwReturn = MCIERR_SEQ_PORT_NONEXISTENT;
  568. else if (wPort != pStream->wPortNum) { // ignore if using wPort already
  569. MIDISEQINFO seqInfo;
  570. // it's 0, 1...., or MAPPER
  571. pStream->wPortNum = wPort; //store port number
  572. midiSeqMessage(pStream->hSeq, SEQ_GETINFO, (DWORD_PTR)(LPMIDISEQINFO)&seqInfo, 0L);
  573. if (seqInfo.bPlaying) {
  574. midiSeqMessage(pStream->hSeq, SEQ_SETPORTOFF, TRUE, 0L);
  575. dwReturn = OpenMidiPort(pStream);
  576. }
  577. }
  578. break;
  579. case MCI_SEQ_SET_SLAVE:
  580. switch (lpSetParms->dwSlave) {
  581. case MCI_SEQ_SMPTE:
  582. case MCI_SEQ_MIDI:
  583. dwReturn = MCIERR_UNSUPPORTED_FUNCTION;
  584. break;
  585. case MCI_SEQ_NONE:
  586. dwReturn = (DWORD)midiSeqMessage(pStream->hSeq, SEQ_SETSYNCSLAVE, SEQ_SYNC_NOTHING, 0L);
  587. break;
  588. case MCI_SEQ_FILE:
  589. dwReturn = (DWORD)midiSeqMessage(pStream->hSeq, SEQ_SETSYNCSLAVE, SEQ_SYNC_FILE, 0L);
  590. break;
  591. }
  592. break;
  593. case MCI_SEQ_SET_MASTER:
  594. switch (lpSetParms->dwMaster) {
  595. case MCI_SEQ_SMPTE:
  596. case MCI_SEQ_MIDI:
  597. dwReturn = MCIERR_UNSUPPORTED_FUNCTION;
  598. break;
  599. case MCI_SEQ_NONE:
  600. dwReturn = (DWORD)midiSeqMessage(pStream->hSeq, SEQ_SETSYNCMASTER, SEQ_SYNC_NOTHING, 0L);
  601. break;
  602. }
  603. break;
  604. case MCI_SEQ_SET_OFFSET:
  605. mmOffset.u.smpte.hour = (BYTE)lpSetParms->dwOffset;
  606. mmOffset.u.smpte.min = (BYTE)(lpSetParms->dwOffset >> 8);
  607. mmOffset.u.smpte.sec = (BYTE)(lpSetParms->dwOffset >> 16);
  608. mmOffset.u.smpte.frame = (BYTE)(lpSetParms->dwOffset >> 24);
  609. if ((lpSetParms->dwOffset & 0x80808080) || // all positive
  610. (mmOffset.u.smpte.hour > 23) ||
  611. (mmOffset.u.smpte.min > 59) ||
  612. (mmOffset.u.smpte.sec > 59) ||
  613. (mmOffset.u.smpte.frame > 29)) // allow max for all fmts
  614. dwReturn = MCIERR_OUTOFRANGE;
  615. else
  616. dwReturn = (DWORD)midiSeqMessage(pStream->hSeq, SEQ_SETSYNCSLAVE, SEQ_SYNC_OFFSET, (DWORD_PTR)(LPMMTIME)&mmOffset);
  617. break;
  618. case MCI_SET_AUDIO: //fall thru...
  619. case MCI_SET_VIDEO:
  620. case MCI_SET_DOOR_OPEN:
  621. case MCI_SET_DOOR_CLOSED:
  622. dwReturn = MCIERR_UNSUPPORTED_FUNCTION;
  623. break;
  624. default:
  625. dwReturn = MCIERR_MISSING_PARAMETER;
  626. }
  627. return dwReturn;
  628. }
  629. /*---------------------------------------------------------------------------*/