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.

1843 lines
64 KiB

  1. /*******************************Module*Header*********************************\
  2. * Module Name: mmseq.c
  3. *
  4. * MultiMedia Systems MIDI Sequencer DLL
  5. *
  6. * Created: 4/10/90
  7. * Author: Greg Simons
  8. *
  9. * History:
  10. *
  11. * Copyright (c) 1985-1998 Microsoft Corporation
  12. *
  13. \******************************************************************************/
  14. #define UNICODE
  15. //MMSYSTEM
  16. #define MMNOSOUND - Sound support
  17. #define MMNOWAVE - Waveform support
  18. #define MMNOAUX - Auxiliary output support
  19. #define MMNOJOY - Joystick support
  20. //MMDDK
  21. #define NOWAVEDEV - Waveform support
  22. #define NOAUXDEV - Auxiliary output support
  23. #define NOJOYDEV - Joystick support
  24. #include <windows.h>
  25. #include <memory.h>
  26. #include <mmsystem.h>
  27. #include <mmddk.h>
  28. #include "mmsys.h"
  29. #include "list.h"
  30. #include "mmseqi.h"
  31. #include "mciseq.h"
  32. static ListHandle seqListHandle;
  33. // Debug macro which checks sequencer structure signature.
  34. #ifdef DEBUG
  35. #define DEBUG_SIG 0x45427947
  36. #define ValidateNPSEQ(npSeq) ((npSeq)->dwDebug == DEBUG_SIG)
  37. #endif
  38. #define SeqSetTempo(npSeq, dwTempo) ((npSeq)->tempo = (dwTempo)) // usec per tick
  39. /* Setup strucure to handle meta event properly at real time. Since meta
  40. events can update time-critical internal variables **and optionally ** be
  41. buffered and send out as well, we will defer from reading them until real
  42. time. */
  43. #define SetUpMetaEvent(npTrack) ((npTrack)->shortMIDIData.byteMsg.status = METAEVENT)
  44. /**************************** PRIVATE FUNCTIONS *************************/
  45. PUBLIC VOID NEAR PASCAL SkipBytes(NPTRACK npTrack, LONG length)
  46. // skips "length" bytes in the given track.
  47. {
  48. LONG i = 0;
  49. while (i < length)
  50. {
  51. GetByte(npTrack);
  52. if ((!npTrack->blockedOn) || (npTrack->endOfTrack))
  53. i++;
  54. else
  55. break;
  56. }
  57. npTrack->dwBytesLeftToSkip = length - i; // remember for next time
  58. return;
  59. }
  60. /**********************************************************/
  61. PRIVATE DWORD NEAR PASCAL GetMotorola24(NPTRACK npTrack)
  62. // reads integer in 24 bit motorola format from the given track
  63. {
  64. WORD w;
  65. w = (WORD)GetByte(npTrack) << 8;
  66. w += GetByte(npTrack);
  67. return ((DWORD)w << 8) + GetByte(npTrack);
  68. }
  69. PRIVATE DWORD NEAR PASCAL MStoTicks(NPSEQ npSeq, DWORD dwMs)
  70. /* Convert milliseconds into ticks (some unit of time) in the given
  71. file. If it's a ppqn file, this conversion totally depends on the
  72. tempo map (which tells what tempo changes happen at what times).
  73. */
  74. {
  75. NPTEMPOMAPELEMENT npFrontTME;
  76. NPTEMPOMAPELEMENT npBehindTME;
  77. DWORD dwElapsedMs;
  78. DWORD dwElapsedTicks;
  79. DWORD dwTotalTicks;
  80. npBehindTME = NULL; // behind tempo map item: starts null
  81. // Find the last element that's before the time passed in
  82. npFrontTME = (NPTEMPOMAPELEMENT) List_Get_First(npSeq->tempoMapList);
  83. while ((npFrontTME) && (npFrontTME->dwMs <= dwMs))
  84. {
  85. npBehindTME = npFrontTME;
  86. npFrontTME = (NPTEMPOMAPELEMENT) List_Get_Next(npSeq->tempoMapList, npFrontTME);
  87. }
  88. if (!npBehindTME)
  89. return (DWORD)-1L; //fail bad -- list was empty, or no dwMs = 0 item
  90. // Great, we found it. Now just extrapolate, and return result.
  91. dwElapsedMs = dwMs - npBehindTME->dwMs;
  92. //compute dwet = dwems * 1000 / dwtempo
  93. // (ticks from last tempo chg to here)
  94. dwElapsedTicks = muldiv32(dwElapsedMs, 1000, npBehindTME->dwTempo);
  95. // ticks from beginning of file to here
  96. dwTotalTicks = npBehindTME->dwTicks + dwElapsedTicks;
  97. return dwTotalTicks;
  98. }
  99. PRIVATE DWORD NEAR PASCAL TickstoMS(NPSEQ npSeq, DWORD dwTicks)
  100. /* Convert ticks (some unit of time) into milliseconds in the given
  101. file. If it's a ppqn file, this conversion totally depends on the
  102. tempo map (which tells what tempo changes happen at what times).
  103. */
  104. {
  105. NPTEMPOMAPELEMENT npFrontTME;
  106. NPTEMPOMAPELEMENT npBehindTME;
  107. DWORD dwRet;
  108. DWORD dwElapsedTicks;
  109. npBehindTME = NULL;
  110. // Find the last element that's before the ticks passed in
  111. npFrontTME = (NPTEMPOMAPELEMENT) List_Get_First(npSeq->tempoMapList);
  112. while ((npFrontTME) && (npFrontTME->dwTicks <= dwTicks))
  113. {
  114. npBehindTME = npFrontTME;
  115. npFrontTME = (NPTEMPOMAPELEMENT) List_Get_Next(npSeq->tempoMapList, npFrontTME);
  116. }
  117. if (!npBehindTME)
  118. return (DWORD)-1L; //fail bad -- list was empty, or didn't have tick = 0 item
  119. // Great, found it! Now extrapolate and return result.
  120. dwElapsedTicks = dwTicks - npBehindTME->dwTicks;
  121. dwRet = npBehindTME->dwMs + muldiv32(dwElapsedTicks,
  122. npBehindTME->dwTempo, 1000);
  123. // (((dwTicks - npBehindTME->dwTicks) * npBehindTME->dwTempo)
  124. // / 1000); // remember, tempo in microseconds per tick
  125. return dwRet;
  126. }
  127. PRIVATE BOOL NEAR PASCAL AddTempoMapItem(NPSEQ npSeq, DWORD dwTempo, DWORD dwTicks)
  128. /* given a tempo change to dwTempo, happening at time dwTicks, in
  129. sequence npSeq, allocate a tempo map element, and put it at the
  130. end of the list. Return false iff memory alloc error.
  131. */
  132. {
  133. NPTEMPOMAPELEMENT npNewTME;
  134. NPTEMPOMAPELEMENT npLastTME;
  135. NPTEMPOMAPELEMENT npTestTME;
  136. DWORD dwElapsedTicks;
  137. npLastTME = NULL;
  138. // Find last tempo map element
  139. npTestTME = (NPTEMPOMAPELEMENT) List_Get_First(npSeq->tempoMapList);
  140. while (npTestTME)
  141. {
  142. npLastTME = npTestTME;
  143. npTestTME = (NPTEMPOMAPELEMENT) List_Get_Next(npSeq->tempoMapList, npTestTME);
  144. }
  145. // Allocate new element
  146. if (!(npNewTME = (NPTEMPOMAPELEMENT) List_Allocate(npSeq->tempoMapList)))
  147. return FALSE; // failure
  148. List_Attach_Tail(npSeq->tempoMapList, (NPSTR) npNewTME);
  149. npNewTME->dwTicks = dwTicks; // these fields are always the same
  150. npNewTME->dwTempo = dwTempo;
  151. // ms field depends on last element
  152. if (!npLastTME) // if list was empty
  153. npNewTME->dwMs = 0;
  154. else // base new element data on last element
  155. {
  156. dwElapsedTicks = dwTicks - npLastTME->dwTicks;
  157. npNewTME->dwMs = npLastTME->dwMs + ((npLastTME->dwTempo * dwElapsedTicks)
  158. / 1000);
  159. }
  160. return TRUE; // success
  161. }
  162. PRIVATE VOID NEAR PASCAL SetBit(BitVector128 *bvPtr, UINT wIndex, BOOL On)
  163. /* Affect "index" bit of filter pointed to by "bvPtr."
  164. If On then set the bit, else clear it. */
  165. {
  166. UINT mask;
  167. mask = 1 << (wIndex & 0x000F);
  168. wIndex >>= 4;
  169. if (On)
  170. bvPtr->filter[wIndex] |= mask; // set the bit
  171. else
  172. bvPtr->filter[wIndex] &= (~mask); // clear the bit
  173. }
  174. PRIVATE BOOL NEAR PASCAL GetBit(BitVector128 *bvPtr, int index)
  175. /* If the bit indicated by "index" is set, return true,
  176. else return false */
  177. {
  178. UINT mask;
  179. mask = 1 << (index & 0x000F);
  180. index >>= 4;
  181. return (bvPtr->filter[index] & mask); // returns true iff bit set
  182. }
  183. PRIVATE VOID NEAR PASCAL AllNotesOff(NPSEQ npSeq, HMIDIOUT hMIDIPort)
  184. // Sends a note off for every key and every channel that is on,
  185. // according to npSeq->keyOnBitVect
  186. {
  187. ShortMIDI myShortMIDIData;
  188. UINT channel;
  189. UINT key;
  190. if (hMIDIPort)
  191. for(channel = 0; channel < 16; channel++)
  192. {
  193. // sustain pedal off for all channels
  194. myShortMIDIData.byteMsg.status= (BYTE) (0xB0 + channel);
  195. myShortMIDIData.byteMsg.byte2 = (BYTE) 0x40;
  196. myShortMIDIData.byteMsg.byte3 = 0x0;
  197. midiOutShortMsg(hMIDIPort, myShortMIDIData.wordMsg);
  198. // now do note offs
  199. myShortMIDIData.byteMsg.status= (BYTE) (0x80 + channel);
  200. myShortMIDIData.byteMsg.byte3 = 0x40; // release velocity
  201. for(key = 0; key < 128; key++)
  202. {
  203. if (GetBit(&npSeq->keyOnBitVect[channel], key)) // is key "on" ?
  204. {
  205. myShortMIDIData.byteMsg.byte2 = (BYTE) key;
  206. // turn it off
  207. // doubly, for layered synths (2 on STOP 2 off)
  208. midiOutShortMsg(hMIDIPort, myShortMIDIData.wordMsg);
  209. // remember that it's off
  210. SetBit(&npSeq->keyOnBitVect[channel], key, FALSE);
  211. }
  212. }
  213. }
  214. }
  215. PRIVATE NPSEQ NEAR PASCAL InitASeq(LPMIDISEQOPENDESC lpOpen,
  216. int divisionType, int resolution)
  217. // Create a sequence by allocating a sequence data structure for it.
  218. // Put it in the sequence list.
  219. {
  220. NPSEQ npSeqNew;
  221. ListHandle hListTrack;
  222. ListHandle hTempoMapList;
  223. int buff;
  224. if (!seqListHandle)
  225. {
  226. seqListHandle = List_Create((LONG)sizeof(SEQ), 0L);
  227. if (seqListHandle == NULLLIST)
  228. return(NULL);
  229. }
  230. // allocate the sequence structure
  231. npSeqNew = pSEQ(List_Allocate(seqListHandle));
  232. if (!npSeqNew)
  233. return(NULL);
  234. // create the sequence's track list
  235. hListTrack = List_Create((LONG) sizeof(TRACK), 0L);
  236. if (hListTrack == NULLLIST)
  237. {
  238. List_Deallocate(seqListHandle, (NPSTR) npSeqNew);
  239. return(NULL);
  240. }
  241. // create the sequence's tempo map list
  242. hTempoMapList = List_Create((LONG) sizeof(TempoMapElement), 0L);
  243. if (hTempoMapList == NULLLIST)
  244. {
  245. List_Deallocate(seqListHandle, (NPSTR) npSeqNew);
  246. List_Destroy(hListTrack);
  247. return(NULL);
  248. }
  249. // set these sequencer fields to default values
  250. _fmemset(npSeqNew, 0, sizeof(SEQ));
  251. npSeqNew->divType = divisionType;
  252. npSeqNew->resolution = resolution;
  253. npSeqNew->slaveOf = SEQ_SYNC_FILE;
  254. npSeqNew->seekTicks = NotInUse;
  255. // set these sequencer fields to specific values already derived
  256. npSeqNew->trackList = hListTrack;
  257. npSeqNew->tempoMapList = hTempoMapList;
  258. npSeqNew->hStream = lpOpen->hStream;
  259. npSeqNew->fwFlags = LEGALFILE; // assume good till proven otherwise
  260. for (buff = 0; buff < NUMSYSEXHDRS + 1; buff++)
  261. {
  262. npSeqNew->longMIDI[buff].midihdr.lpData =
  263. (LPSTR) &npSeqNew->longMIDI[buff].data; // resolve data ptr
  264. // make buffer refer to seq so can find owner on callback
  265. npSeqNew->longMIDI[buff].midihdr.dwUser = (DWORD_PTR)(LPVOID)npSeqNew;
  266. npSeqNew->longMIDI[buff].midihdr.dwFlags |= MHDR_DONE; //just set done bit
  267. }
  268. // initialize internal filter on meta events to ignore all but
  269. // tempo, time signature, smpte offset, and end of track meta events.
  270. SetBit(&npSeqNew->intMetaFilter, TEMPOCHANGE, TRUE); // accept int tempo changes
  271. SetBit(&npSeqNew->intMetaFilter, ENDOFTRACK, TRUE); // accept int end of track
  272. SetBit(&npSeqNew->intMetaFilter, SMPTEOFFSET, TRUE); // accept int SMPTE offset
  273. SetBit(&npSeqNew->intMetaFilter, TIMESIG, TRUE); // accept int time sig
  274. SetBit(&npSeqNew->intMetaFilter, SEQSTAMP, TRUE);
  275. // put sequence in global list of all sequences
  276. List_Attach_Tail(seqListHandle, (NPSTR) npSeqNew);
  277. return npSeqNew;
  278. }
  279. PRIVATE DWORD NEAR PASCAL InitTempo(int divType, int resolution)
  280. {
  281. DWORD ticksPerMinute;
  282. DWORD tempo;
  283. // set tempo to correct default (120 bpm or 24, 25, 30 fps).
  284. switch (divType)
  285. {
  286. case SEQ_DIV_PPQN:
  287. ticksPerMinute = (DWORD) DefaultTempo * resolution;
  288. break;
  289. case SEQ_DIV_SMPTE_24:
  290. ticksPerMinute = ((DWORD) (24 * 60)) * resolution; // 24 frames per second
  291. break;
  292. case SEQ_DIV_SMPTE_25:
  293. ticksPerMinute = ((DWORD) (25 * 60)) * resolution;
  294. break;
  295. case SEQ_DIV_SMPTE_30:
  296. case SEQ_DIV_SMPTE_30DROP:
  297. ticksPerMinute = ((DWORD) (30 * 60)) * resolution;
  298. break;
  299. }
  300. tempo = USecPerMinute / ticksPerMinute;
  301. return(tempo);
  302. }
  303. PRIVATE BOOL NEAR PASCAL SetUpToPlay(NPSEQ npSeq)
  304. /* After the sequence has been initialized and "connected" to the streamer,
  305. this function should be called. It scans the file to create a tempo
  306. map, set up for the patch-cache message, and determine the length of
  307. the file. (Actually, it just set's this process in motion, and much
  308. of the important code is in the blocking/unblocking logic.)
  309. Returns false only if there's a fatal error (e.g. memory alloc error),
  310. else true.
  311. */
  312. {
  313. BOOL tempoChange;
  314. // set tempo to 120bpm or normal SMPTE frame rate
  315. //npSeq->tempo = InitTempo(npSeq->divType, npSeq->resolution);
  316. SeqSetTempo(npSeq, InitTempo(npSeq->divType, npSeq->resolution));
  317. if (!(AddTempoMapItem(npSeq, npSeq->tempo, 0L)))
  318. return FALSE;
  319. if (npSeq->slaveOf != SEQ_SYNC_FILE)
  320. tempoChange = FALSE;
  321. else
  322. tempoChange = TRUE;
  323. SetBit(&npSeq->intMetaFilter,TEMPOCHANGE, tempoChange);
  324. ResetToBeginning(npSeq); // this is considered reset 1
  325. SetBlockedTracksTo(npSeq, on_input, in_rewind_1); // 'mature' the input block state
  326. /* In state code, goes on to reset, scan early metas, build tempo
  327. map, and reset again, set tempo to 120bpm or normal SMPTE frame rate
  328. fill in the tracks (search to song pointer value) and then sets
  329. "ready to play." Iff npSeq->playing, then plays the sequence.
  330. */
  331. return TRUE;
  332. }
  333. PRIVATE VOID NEAR PASCAL Destroy(NPSEQ npSeq)
  334. {
  335. int buff;
  336. Stop(npSeq); // among other things, this cancels any pending callbacks
  337. List_Destroy(npSeq->trackList); //destroys track data
  338. List_Destroy(npSeq->tempoMapList); //destroys tempo map
  339. if (npSeq->npTrkArr)
  340. LocalFree((HANDLE)npSeq->npTrkArr); // free track array
  341. // (ptr == handle since lmem_fixed)
  342. if (npSeq->hMIDIOut) // should have already been closed -- but just in case
  343. for (buff = 0; buff < NUMSYSEXHDRS; buff++)
  344. midiOutUnprepareHeader(npSeq->hMIDIOut,
  345. (LPMIDIHDR) &npSeq->longMIDI[buff].midihdr,
  346. sizeof(npSeq->longMIDI[buff].midihdr));
  347. List_Deallocate(seqListHandle, (NPSTR) npSeq); // deallocate memory
  348. }
  349. PRIVATE int NEAR PASCAL MIDILength(BYTE status) /* returns length of various MIDI messages */
  350. {
  351. if (status & 0x80) // status byte since ms bit set
  352. {
  353. switch (status & 0xf0) // look at ms nibble
  354. {
  355. case 0x80: // note on
  356. case 0x90: // note off
  357. case 0xA0: // key aftertouch
  358. case 0xB0: // cntl change or channel mode
  359. case 0xE0: return 3; // pitch bend
  360. case 0xC0: // pgm change
  361. case 0xD0: return 2; // channel pressure
  362. case 0xF0: // system
  363. {
  364. switch (status & 0x0F) // look at ls nibble
  365. {
  366. // "system common"
  367. case 0x0: return SysExCode; // sysex: variable size
  368. case 0x1: // 2 MTC Q-Frame
  369. case 0x3: return 2; // 2 Song Select
  370. case 0x2: return 3; // 3 Song Pos Ptr
  371. case 0x4: // 0 undefined
  372. case 0x5: return 0; // 0 undefined
  373. case 0x6: // 1 tune request
  374. case 0x7: return 1; // 1 end of sysex (not really a message)
  375. // "system real-time"
  376. case 0x8: // 1 timing clock
  377. case 0xA: // 1 start
  378. case 0xB: // 1 continue
  379. case 0xC: // 1 stop
  380. case 0xE: return 1; // 1 active sensing
  381. case 0x9: // 0 undefined
  382. case 0xD: return 0; // 0 undefined
  383. /* 0xFF is really system reset, but is used
  384. as a meta event header in MIDI files. */
  385. case 0xF: return(MetaEventCode);
  386. } // case ls
  387. }// sytem messages
  388. } // case ms
  389. } // if status
  390. // else
  391. return 0; // 0 undefined not a status byte
  392. } // MIDILength
  393. PRIVATE LONG NEAR PASCAL GetVarLen(NPTRACK npTrack) // returns next variable length qty in track
  394. { // will have to account for end of track here (perhaps change GetByte)
  395. int count = 1;
  396. BYTE c;
  397. LONG delta;
  398. if ((delta = GetByte(npTrack)) & 0x80) /* gets the next delta */
  399. {
  400. delta &= 0x7f;
  401. do
  402. {
  403. delta = (delta << 7) + ((c = GetByte(npTrack)) & 0x7f);
  404. count++;
  405. }
  406. while (c & 0x80);
  407. }
  408. if (count > 4) /* 4 byte max on deltas */
  409. {
  410. dprintf1(("BOGUS DELTA !!!!"));
  411. return 0x7fffffff;
  412. }
  413. else
  414. return delta;
  415. }
  416. PRIVATE VOID NEAR PASCAL SkipEvent(BYTE status, NPTRACK npTrack)
  417. // skips event in track, based on status byte passed in.
  418. {
  419. LONG length;
  420. if ((status == METAEVENT) || (status == SYSEX) || (status == SYSEXF7))
  421. length = GetVarLen(npTrack);
  422. else
  423. length = MIDILength(status) -1 ;// -1 becuase already read status
  424. if ((!npTrack->blockedOn) && (length))
  425. {
  426. SkipBytes(npTrack, length);
  427. if (npTrack->blockedOn)
  428. npTrack->blockedOn = in_SkipBytes_ScanEM;
  429. }
  430. return;
  431. }
  432. PRIVATE VOID NEAR PASCAL FlushMidi(HMIDIOUT hMidiOut, LongMIDI * pBuf)
  433. {
  434. if (pBuf->midihdr.dwBufferLength) {
  435. midiOutLongMsg(hMidiOut, &pBuf->midihdr, sizeof(MIDIHDR));
  436. pBuf->midihdr.dwBufferLength = 0;
  437. }
  438. }
  439. PRIVATE VOID NEAR PASCAL SetData(HMIDIOUT hMidiOut, LongMIDI * pBuf,
  440. ShortMIDI Data, int length)
  441. {
  442. if (LONGBUFFSIZE < pBuf->midihdr.dwBufferLength + length) {
  443. FlushMidi(hMidiOut, pBuf);
  444. }
  445. pBuf->data[pBuf->midihdr.dwBufferLength++] = Data.byteMsg.status;
  446. if (length > 1) {
  447. pBuf->data[pBuf->midihdr.dwBufferLength++] = Data.byteMsg.byte2;
  448. if (length > 2) {
  449. pBuf->data[pBuf->midihdr.dwBufferLength++] = Data.byteMsg.byte3;
  450. }
  451. }
  452. }
  453. PRIVATE VOID NEAR PASCAL SendMIDI(NPSEQ npSeq, NPTRACK npTrack)
  454. {
  455. ShortMIDI myShortMIDIData;
  456. int length;
  457. BYTE status;
  458. BYTE channel;
  459. BYTE key;
  460. BYTE velocity;
  461. BOOL setBit;
  462. myShortMIDIData = npTrack->shortMIDIData;
  463. if ((length = MIDILength(myShortMIDIData.byteMsg.status)) <= 3)
  464. {
  465. if (npSeq->hMIDIOut)
  466. {
  467. //send short MIDI message
  468. //maintain note on/off structure
  469. status = (BYTE)((myShortMIDIData.byteMsg.status) & 0xF0);
  470. if ((status == 0x80) || (status == 0x90)) // note on or off
  471. {
  472. channel = (BYTE)((myShortMIDIData.byteMsg.status) & 0x0F);
  473. key = myShortMIDIData.byteMsg.byte2;
  474. velocity = myShortMIDIData.byteMsg.byte3;
  475. //
  476. // Only play channels 1 to 12 for marked files
  477. //
  478. if ((npSeq->fwFlags & GENERALMSMIDI) && channel >= 12) {
  479. return;
  480. }
  481. if ((status == 0x90) && (velocity != 0)) // note on
  482. {
  483. setBit = TRUE;
  484. if (GetBit(&npSeq->keyOnBitVect[channel], key))
  485. // are we hitting a key that's ALREADY "on" ?
  486. { // if so, turn it OFF
  487. myShortMIDIData.byteMsg.status &= 0xEF; //9x->8x
  488. SetData(npSeq->hMIDIOut,
  489. &npSeq->longMIDI[NUMSYSEXHDRS],
  490. myShortMIDIData,
  491. length);
  492. // midiOutShortMsg(npSeq->hMIDIOut, myShortMIDIData.wordMsg);
  493. myShortMIDIData.byteMsg.status |= 0x10; //8x->9x
  494. }
  495. }
  496. else
  497. setBit = FALSE;
  498. SetBit(&npSeq->keyOnBitVect[channel], key, setBit);
  499. }
  500. SetData(npSeq->hMIDIOut,
  501. &npSeq->longMIDI[NUMSYSEXHDRS],
  502. myShortMIDIData,
  503. length);
  504. // midiOutShortMsg(npSeq->hMIDIOut, myShortMIDIData.wordMsg);
  505. }
  506. }
  507. }
  508. PRIVATE VOID NEAR PASCAL SubtractAllTracks(NPSEQ npSeq, LONG subValue) // subtract subvalue from every track
  509. {
  510. NPTRACK npTrack;
  511. if (subValue) // ignore if zero
  512. {
  513. npTrack = (NPTRACK) List_Get_First(npSeq->trackList);
  514. while (npTrack) /* subtract this delta from all others */
  515. {
  516. if (npTrack->delta != TrackEmpty)
  517. npTrack->delta -= subValue;
  518. npTrack = (NPTRACK) List_Get_Next(npSeq->trackList, npTrack);
  519. }
  520. }
  521. }
  522. PRIVATE VOID NEAR PASCAL SetUpSysEx(NPTRACK npTrack, BYTE status)
  523. /* Handle similar to Metas (don't prebuffer, since several tracks could
  524. have sysex, and we only have 2 buffers */
  525. {
  526. npTrack->shortMIDIData.byteMsg.status = status;
  527. npTrack->sysExRemLength = GetVarLen(npTrack);
  528. }
  529. PRIVATE VOID NEAR PASCAL GetShortMIDIData(NPTRACK npTrack, BYTE status, int length)
  530. {
  531. npTrack->shortMIDIData.byteMsg.status = status;
  532. if (length >= 2)
  533. {
  534. npTrack->shortMIDIData.byteMsg.byte2 = GetByte(npTrack);
  535. if (length == 3)
  536. npTrack->shortMIDIData.byteMsg.byte3 = GetByte(npTrack);
  537. }
  538. }
  539. PRIVATE BYTE NEAR PASCAL GetStatus(NPTRACK npTrack)
  540. // returns correct status byte, taking running status fully into account.
  541. {
  542. BYTE firstByte;
  543. BYTE status;
  544. if ((firstByte = LookByte(npTrack)) & 0x80) // status byte??
  545. {
  546. firstByte = GetByte(npTrack); // actually get it if status
  547. if ((firstByte >= 0xF0) && (firstByte <= 0xF7))
  548. // sysex or sys common?
  549. npTrack->lastStatus = 0; // cancel running status
  550. else if (firstByte < 0xF0) // only use channel messages
  551. npTrack->lastStatus = firstByte; // else save it for running status
  552. status = firstByte; // return this as status byte regardless
  553. }
  554. else // 1st byte wasn't a status byte
  555. {
  556. if (npTrack->lastStatus & 0x80) // there was prev. running status
  557. status = npTrack->lastStatus; // return previous status
  558. else
  559. status = 0; // error
  560. }
  561. return status;
  562. }
  563. PRIVATE VOID NEAR PASCAL FillInEvent(NPTRACK npTrack)
  564. {
  565. BYTE status;
  566. if (!npTrack->blockedOn)
  567. {
  568. status = GetStatus(npTrack);
  569. if (!npTrack->blockedOn)
  570. {
  571. int length;
  572. if ((length = MIDILength(status)) <= 3)
  573. GetShortMIDIData(npTrack, status, length);
  574. else if ((status == SYSEX) || (status == SYSEXF7))
  575. // set up for sysEx
  576. SetUpSysEx(npTrack, status);
  577. else if (status == METAEVENT)
  578. // set up for meta event
  579. SetUpMetaEvent(npTrack);
  580. else {
  581. dprintf1(("Bogus long message encountered!!!"));
  582. }
  583. }
  584. }
  585. }
  586. PRIVATE UINT NEAR PASCAL SetTempo(NPSEQ npSeq, DWORD dwUserTempo)
  587. // tempo passed in from user. Convert from beats per minute or frames
  588. // per second to internal format (microseconds per tick)
  589. {
  590. DWORD dwTempo;
  591. if (!dwUserTempo) // zero is an illegal tempo!
  592. return MCIERR_OUTOFRANGE;
  593. if (npSeq->divType == SEQ_DIV_PPQN)
  594. dwTempo = USecPerMinute / (dwUserTempo * npSeq->resolution);
  595. else
  596. dwTempo = USecPerSecond / (dwUserTempo * npSeq->resolution);
  597. if (!dwTempo)
  598. dwTempo = 1; // at least 1 usec per tick! This is spec'ed max tempo
  599. SeqSetTempo(npSeq, dwTempo);
  600. if (npSeq->wTimerID)
  601. {
  602. DestroyTimer(npSeq); // recompute everything from current position
  603. npSeq->nextExactTime = timeGetTime();
  604. //
  605. // Bug fix - make everything happen on the timer thread instead
  606. // of calling TimerIntRoutine which can get deadlocked.
  607. //
  608. SetTimerCallback(npSeq, MINPERIOD, npSeq->dwTimerParam);
  609. }
  610. return MIDISEQERR_NOERROR;
  611. }
  612. /**************************** PUBLIC FUNCTIONS *************************/
  613. /****************************************************************************
  614. *
  615. * @doc INTERNAL SEQUENCER
  616. *
  617. * @api DWORD | midiSeqMessage | Single entry point for Sequencer
  618. *
  619. * @parm HMIDISEQ | hMIDISeq | Handle to MIDI Sequence
  620. *
  621. * @parm UINT | wMessage | The requested action to be performed.
  622. *
  623. * @parm DWORD | dwParam1 | Data for this message.
  624. *
  625. * @parm DWORD | dwParam2 | Data for this message.
  626. *
  627. * @rdesc Sequencer error code (see mmseq.h).
  628. *
  629. ***************************************************************************/
  630. PUBLIC DWORD_PTR FAR PASCAL midiSeqMessage(
  631. HMIDISEQ hMIDISeq,
  632. UINT wMessage,
  633. DWORD_PTR dwParam1,
  634. DWORD_PTR dwParam2)
  635. {
  636. if (wMessage == SEQ_OPEN)
  637. return CreateSequence((LPMIDISEQOPENDESC)dwParam1, (LPHMIDISEQ)dwParam2);
  638. if (!hMIDISeq)
  639. return MIDISEQERR_INVALSEQHANDLE;
  640. switch (wMessage) {
  641. case SEQ_CLOSE:
  642. Destroy(pSEQ(hMIDISeq));
  643. break;
  644. case SEQ_PLAY:
  645. return Play(pSEQ(hMIDISeq), (DWORD)dwParam1);
  646. case SEQ_RESET:
  647. // set song pointer to beginning of the sequence;
  648. return midiSeqMessage(hMIDISeq, SEQ_SETSONGPTR, 0L, 0L);
  649. case SEQ_SETSYNCMASTER:
  650. switch ((WORD)dwParam1) {
  651. case SEQ_SYNC_NOTHING:
  652. pSEQ(hMIDISeq)->masterOf = LOWORD(dwParam1);
  653. break;
  654. case SEQ_SYNC_MIDI: // not yet implemented...
  655. case SEQ_SYNC_SMPTE:
  656. return MIDISEQERR_INVALPARM;
  657. case SEQ_SYNC_OFFSET: // in both master and slave (same)
  658. pSEQ(hMIDISeq)->smpteOffset = *((LPMMTIME) dwParam2);
  659. break;
  660. default:
  661. return MIDISEQERR_INVALPARM;
  662. }
  663. break;
  664. case SEQ_SETSYNCSLAVE: // what we should slave to
  665. switch ((WORD)dwParam1) {
  666. case SEQ_SYNC_NOTHING:
  667. // don't accept internal tempo changes;
  668. SetBit(&pSEQ(hMIDISeq)->intMetaFilter, TEMPOCHANGE, FALSE);
  669. pSEQ(hMIDISeq)->slaveOf = LOWORD(dwParam1);
  670. break;
  671. case SEQ_SYNC_FILE:
  672. // accept internal tempo changes;
  673. SetBit(&pSEQ(hMIDISeq)->intMetaFilter, TEMPOCHANGE, TRUE);
  674. pSEQ(hMIDISeq)->slaveOf = LOWORD(dwParam1);
  675. break;
  676. case SEQ_SYNC_SMPTE: // not yet implemented...
  677. case SEQ_SYNC_MIDI:
  678. return MIDISEQERR_INVALPARM;
  679. case SEQ_SYNC_OFFSET: // in both master and slave (same)
  680. pSEQ(hMIDISeq)->smpteOffset = *((LPMMTIME)dwParam2);
  681. break;
  682. default:
  683. return MIDISEQERR_INVALPARM;
  684. }
  685. break;
  686. case SEQ_MSTOTICKS: // given an ms value, convert it to ticks
  687. *((DWORD FAR *)dwParam2) = MStoTicks(pSEQ(hMIDISeq), (DWORD)dwParam1);
  688. break;
  689. case SEQ_TICKSTOMS: // given a tick value, convert it to ms
  690. *((DWORD FAR *)dwParam2) = TickstoMS(pSEQ(hMIDISeq), (DWORD)dwParam1);
  691. break;
  692. case SEQ_SETTEMPO:
  693. return SetTempo(pSEQ(hMIDISeq), (DWORD)dwParam1);
  694. case SEQ_SETSONGPTR:
  695. // remember it in case blocked;
  696. if (pSEQ(hMIDISeq)->divType == SEQ_DIV_PPQN) // div 4 16th->1/4 note
  697. pSEQ(hMIDISeq)->seekTicks = (DWORD)((dwParam1 * pSEQ(hMIDISeq)->resolution) / 4);
  698. else
  699. pSEQ(hMIDISeq)->seekTicks = (DWORD)dwParam1 * pSEQ(hMIDISeq)->resolution; // frames
  700. SeekTicks(pSEQ(hMIDISeq));
  701. break;
  702. case SEQ_SEEKTICKS:
  703. pSEQ(hMIDISeq)->wCBMessage = wMessage; // remember message type
  704. // No break;
  705. case SEQ_SYNCSEEKTICKS:
  706. // finer resolution than song ptr command;
  707. pSEQ(hMIDISeq)->seekTicks = (DWORD)dwParam1;
  708. SeekTicks(pSEQ(hMIDISeq));
  709. break;
  710. case SEQ_SETUPTOPLAY:
  711. if (!(SetUpToPlay(pSEQ(hMIDISeq)))) {
  712. Destroy(pSEQ(hMIDISeq));
  713. return MIDISEQERR_NOMEM;
  714. }
  715. break;
  716. case SEQ_STOP:
  717. Stop(pSEQ(hMIDISeq));
  718. break;
  719. case SEQ_TRACKDATA:
  720. if (!dwParam1)
  721. return MIDISEQERR_INVALPARM;
  722. else
  723. return NewTrackData(pSEQ(hMIDISeq), (LPMIDISEQHDR)dwParam1);
  724. case SEQ_GETINFO:
  725. if (!dwParam1)
  726. return MIDISEQERR_INVALPARM;
  727. else
  728. return GetInfo(pSEQ(hMIDISeq), (LPMIDISEQINFO) dwParam1);
  729. case SEQ_SETPORT:
  730. {
  731. UINT wRet;
  732. if (MMSYSERR_NOERROR !=
  733. (wRet =
  734. midiOutOpen(&pSEQ(hMIDISeq)->hMIDIOut,
  735. (DWORD)dwParam1,
  736. (DWORD_PTR)MIDICallback,
  737. 0L,
  738. CALLBACK_FUNCTION)))
  739. return wRet;
  740. if (MMSYSERR_NOERROR !=
  741. (wRet = SendPatchCache(pSEQ(hMIDISeq), TRUE))) {
  742. midiOutClose(pSEQ(hMIDISeq)->hMIDIOut);
  743. pSEQ(hMIDISeq)->hMIDIOut = NULL;
  744. return wRet;
  745. }
  746. for (wRet = 0; wRet < NUMSYSEXHDRS + 1; wRet++) {
  747. midiOutPrepareHeader(pSEQ(hMIDISeq)->hMIDIOut, (LPMIDIHDR)&pSEQ(hMIDISeq)->longMIDI[wRet].midihdr, sizeof(pSEQ(hMIDISeq)->longMIDI[wRet].midihdr));
  748. pSEQ(hMIDISeq)->longMIDI[wRet].midihdr.dwFlags |= MHDR_DONE;
  749. }
  750. break;
  751. }
  752. case SEQ_SETPORTOFF:
  753. if (pSEQ(hMIDISeq)->hMIDIOut) {
  754. UINT wHeader;
  755. HMIDIOUT hTempMIDIOut;
  756. for (wHeader = 0; wHeader < NUMSYSEXHDRS + 1; wHeader++)
  757. midiOutUnprepareHeader(pSEQ(hMIDISeq)->hMIDIOut, (LPMIDIHDR)&pSEQ(hMIDISeq)->longMIDI[wHeader].midihdr, sizeof(pSEQ(hMIDISeq)->longMIDI[wHeader].midihdr));
  758. hTempMIDIOut = pSEQ(hMIDISeq)->hMIDIOut;
  759. pSEQ(hMIDISeq)->hMIDIOut = NULL; // avoid notes during "notesoff"
  760. if ((BOOL)dwParam1)
  761. AllNotesOff(pSEQ(hMIDISeq), hTempMIDIOut);
  762. midiOutClose(hTempMIDIOut);
  763. }
  764. break;
  765. case SEQ_QUERYGENMIDI:
  766. return pSEQ(hMIDISeq)->fwFlags & GENERALMSMIDI;
  767. case SEQ_QUERYHMIDI:
  768. return (DWORD_PTR)pSEQ(hMIDISeq)->hMIDIOut;
  769. default:
  770. return MIDISEQERR_INVALMSG;
  771. }
  772. return MIDISEQERR_NOERROR;
  773. }
  774. /**********************************************************/
  775. PRIVATE VOID NEAR PASCAL SeekTicks(NPSEQ npSeq)
  776. /* Used for song pointer and seek ticks (same, but finer res.) command. */
  777. {
  778. if (npSeq->playing) // not a good idea to seek while playing!
  779. Stop(npSeq);
  780. if (npSeq->currentTick >= npSeq->seekTicks) // = because may have already
  781. // played current notes
  782. {
  783. // seeking behind: must reset first
  784. npSeq->readyToPlay = FALSE;
  785. ResetToBeginning(npSeq); // tell streamer to start over
  786. // tell blocking logic what operation we're in
  787. SetBlockedTracksTo(npSeq, on_input, in_Seek_Tick);
  788. }
  789. else // seeking ahead in the file
  790. {
  791. if (GetNextEvent(npSeq) == NoErr) // if there's a valid event set up
  792. // send ALL events in the file up through time = seekTicks
  793. SendAllEventsB4(npSeq, (npSeq->seekTicks - npSeq->currentTick),
  794. MODE_SEEK_TICKS);
  795. if ((AllTracksUnblocked(npSeq)) &&
  796. ((npSeq->currentTick + npSeq->nextEventTrack->delta)
  797. >= npSeq->seekTicks)) // Did we complete the operation??
  798. {
  799. npSeq->seekTicks = NotInUse; // signify -- got there
  800. if (npSeq->wCBMessage == SEQ_SEEKTICKS)
  801. NotifyCallback(npSeq->hStream);
  802. }
  803. else
  804. npSeq->readyToPlay = FALSE; // didn't get there -- protect from play
  805. }
  806. }
  807. PUBLIC UINT NEAR PASCAL GetInfo(NPSEQ npSeq, LPMIDISEQINFO lpInfo)
  808. /* Used to fulfill seqInfo command. Fills in seq info structure passed in */
  809. {
  810. // fill in the lpInfo structure
  811. lpInfo->wDivType = (WORD)npSeq->divType;
  812. lpInfo->wResolution = (WORD)npSeq->resolution;
  813. lpInfo->dwLength = npSeq->length;
  814. lpInfo->bPlaying = npSeq->playing;
  815. lpInfo->bSeeking = !(npSeq->seekTicks == NotInUse);
  816. lpInfo->bReadyToPlay = npSeq->readyToPlay;
  817. lpInfo->dwCurrentTick = npSeq->currentTick;
  818. lpInfo->dwPlayTo = npSeq->playTo;
  819. lpInfo->dwTempo = npSeq->tempo;
  820. // lpInfo->bTSNum = (BYTE) npSeq->timeSignature.numerator;
  821. // lpInfo->bTSDenom = (BYTE) npSeq->timeSignature.denominator;
  822. // lpInfo->wNumTracks = npSeq->wNumTrks;
  823. // lpInfo->hPort = npSeq->hMIDIOut;
  824. lpInfo->mmSmpteOffset = npSeq->smpteOffset;
  825. lpInfo->wInSync = npSeq->slaveOf;
  826. lpInfo->wOutSync = npSeq->masterOf;
  827. lpInfo->bLegalFile = (BYTE)(npSeq->fwFlags & LEGALFILE);
  828. if (List_Get_First(npSeq->tempoMapList))
  829. lpInfo->tempoMapExists = TRUE;
  830. else
  831. lpInfo->tempoMapExists = FALSE;
  832. return MIDISEQERR_NOERROR;
  833. }
  834. PUBLIC UINT NEAR PASCAL CreateSequence(LPMIDISEQOPENDESC lpOpen,
  835. LPHMIDISEQ lphMIDISeq)
  836. // Given a structure holding MIDI file header info, allocate and initialize
  837. // all internal structures to play this file. Return the allocated
  838. // structure in lphMIDISeq.
  839. {
  840. WORD wTracks;
  841. int division;
  842. int divType;
  843. int resolution;
  844. NPTRACK npTrackCur;
  845. NPSEQ npSeq;
  846. BOOL trackAllocError;
  847. WORD iTrkNum;
  848. *lphMIDISeq = NULL; // initially set up for error return
  849. if (lpOpen->dwLen < 6) // header must be at least 6 bytes
  850. return MIDISEQERR_INVALPARM;
  851. wTracks = GETMOTWORD(lpOpen->lpMIDIFileHdr + sizeof(WORD));
  852. if (wTracks > MAXTRACKS) // protect from random wTracks
  853. return MIDISEQERR_INVALPARM;
  854. division = (int)GETMOTWORD(lpOpen->lpMIDIFileHdr + 2 * sizeof(WORD));
  855. if (!(division & 0x8000)) // check division type: smpte or ppqn
  856. {
  857. divType = SEQ_DIV_PPQN;
  858. resolution = division; // ticks per q-note
  859. }
  860. else // SMPTE
  861. {
  862. divType = -(division >> 8); /* this will be -24, -25, -29 or -30 for
  863. each different SMPTE frame rate. Negate to make positive */
  864. resolution = (division & 0x00FF);
  865. }
  866. // allocate actual seq struct
  867. npSeq = InitASeq(lpOpen, divType, resolution);
  868. if (!npSeq)
  869. return MIDISEQERR_NOMEM;
  870. trackAllocError = FALSE;
  871. // allocate track array
  872. npSeq->npTrkArr =
  873. (NPTRACKARRAY) LocalAlloc(LMEM_FIXED, sizeof(NPTRACK) * wTracks);
  874. npSeq->wNumTrks = wTracks;
  875. if (!npSeq->npTrkArr)
  876. trackAllocError = TRUE;
  877. if (!trackAllocError)
  878. for (iTrkNum = 0; iTrkNum < wTracks; iTrkNum++)
  879. {
  880. if (!(npTrackCur = (NPTRACK) List_Allocate(npSeq->trackList)))
  881. {
  882. trackAllocError = TRUE;
  883. break;
  884. }
  885. // set trk array entry
  886. npSeq->npTrkArr->trkArr[iTrkNum] = npTrackCur;
  887. List_Attach_Tail(npSeq->trackList, (NPSTR) npTrackCur);
  888. if (npSeq->firstTrack == (NPTRACK) NULL)
  889. npSeq->firstTrack = npTrackCur; //1st track is special for metas
  890. npTrackCur->inPort.hdrList = NULL;
  891. npTrackCur->length = 0;
  892. npTrackCur->blockedOn = not_blocked;
  893. npTrackCur->dwCallback = (DWORD_PTR)lpOpen->dwCallback;
  894. npTrackCur->dwInstance = (DWORD_PTR)lpOpen->dwInstance;
  895. npTrackCur->sysExRemLength = 0;
  896. npTrackCur->iTrackNum = iTrkNum;
  897. }
  898. if (trackAllocError)
  899. {
  900. Destroy(npSeq); // dealloc seq related memory...
  901. return MIDISEQERR_NOMEM;
  902. }
  903. *lphMIDISeq = hSEQ(npSeq); /* Make what lphMIDISeq points to, point to
  904. sequence. */
  905. return MIDISEQERR_NOERROR;
  906. }
  907. PUBLIC VOID NEAR PASCAL SetBlockedTracksTo(NPSEQ npSeq,
  908. int fromState, int toState)
  909. /* Set all tracks that are blocked with a given state, to a new state */
  910. {
  911. NPTRACK npTrack;
  912. npTrack = (NPTRACK) List_Get_First(npSeq->trackList);
  913. while (npTrack)
  914. {
  915. if (npTrack->blockedOn == fromState)
  916. npTrack->blockedOn = toState;
  917. npTrack = (NPTRACK) List_Get_Next(npSeq->trackList, npTrack);
  918. }
  919. }
  920. PUBLIC VOID NEAR PASCAL ResetToBeginning(NPSEQ npSeq)
  921. /* set all globals and streams to play from beginning */
  922. {
  923. NPTRACK npTrack;
  924. npSeq->currentTick = 0;
  925. npSeq->nextExactTime = timeGetTime();
  926. npTrack = (NPTRACK) List_Get_First(npSeq->trackList);
  927. while (npTrack)
  928. {
  929. npTrack->delta = 0;
  930. npTrack->shortMIDIData.wordMsg = 0;
  931. npTrack->endOfTrack = FALSE;
  932. RewindToStart(npSeq, npTrack); /* reset stream to beginning of track
  933. (this basically entails freeing the buffers and setting a
  934. low-level block) */
  935. npTrack = (NPTRACK) List_Get_Next(npSeq->trackList, npTrack);
  936. }
  937. }
  938. PUBLIC UINT NEAR PASCAL Play(NPSEQ npSeq, DWORD dwPlayTo) /* play the sequence */
  939. {
  940. npSeq->wCBMessage = SEQ_PLAY;
  941. if (dwPlayTo == PLAYTOEND) // default is play to end
  942. dwPlayTo = npSeq->length;
  943. if (npSeq->currentTick > npSeq->length) // illegal position in file
  944. return MIDISEQERR_ERROR;
  945. else if ((npSeq->playing) && (npSeq->playTo == dwPlayTo))
  946. return MIDISEQERR_NOERROR; //do nothing, this play redundant
  947. else
  948. {
  949. if (npSeq->playing)
  950. Stop(npSeq); // stop it before playing again
  951. npSeq->playing = TRUE;
  952. npSeq->nextExactTime = timeGetTime(); // start time of reference
  953. npSeq->playTo = dwPlayTo; // set limit
  954. if (!npSeq->bSetPeriod)
  955. {
  956. timeBeginPeriod(MINPERIOD);
  957. npSeq->bSetPeriod = TRUE;
  958. }
  959. if (npSeq->readyToPlay)
  960. //
  961. // Bug fix - make everything happen on the timer thread instead
  962. // of calling TimerIntRoutine which can get deadlocked.
  963. //
  964. return SetTimerCallback(npSeq, MINPERIOD, 0);
  965. else
  966. return MIDISEQERR_NOERROR;
  967. /* don't worry--if doesn't play here, it will start playing from state
  968. code in case where npSeq->playing == true (but may mask timer error). */
  969. }
  970. }
  971. /**********************************************************/
  972. PUBLIC VOID NEAR PASCAL Stop(NPSEQ npSeq) /* stop the sequence */
  973. {
  974. DestroyTimer(npSeq);
  975. if (npSeq->bSetPeriod) // only reset it once!
  976. {
  977. timeEndPeriod(MINPERIOD);
  978. npSeq->bSetPeriod = FALSE;
  979. }
  980. npSeq->playing = FALSE;
  981. AllNotesOff(npSeq, npSeq->hMIDIOut);
  982. }
  983. PUBLIC BOOL NEAR PASCAL HandleMetaEvent(NPSEQ npSeq, NPTRACK npTrack,
  984. UINT wMode) // called at time ready to send!!!
  985. /* Look at the meta event currently being pointed to in this track.
  986. Act on it accordingly.
  987. Ignore all except tempo change, end of track, time signature, and
  988. smpte offset. Returns false only if tempo map allocation failed.
  989. wMode: MODE_SEEK_TICKS, MODE_PLAYING, MODE_SCANEM: passed in by caller
  990. to indicate tempo map allocation, and how to mature blocking status.
  991. Caution: wState must be FALSE when called at interrupt time!
  992. (This variable causes a tempo map element to be added to the tempo map
  993. list every time a tempo change meta event is encountered.)
  994. */
  995. {
  996. BYTE metaIDByte;
  997. int bytesRead;
  998. LONG length;
  999. DWORD tempTempo;
  1000. MMTIME tempMM = {0, 0};
  1001. TimeSigType tempTimeSig;
  1002. BYTE Manufacturer[3];
  1003. // it is assumed at this point that the leading 0xFF status byte
  1004. // has been read.
  1005. metaIDByte = GetByte(npTrack);
  1006. length = GetVarLen(npTrack);
  1007. bytesRead = 0;
  1008. if (GetBit(&npSeq->intMetaFilter, metaIDByte) && (!npTrack->blockedOn))
  1009. /* only consider meta events that you've allowed to pass */
  1010. {
  1011. switch (metaIDByte)
  1012. {
  1013. case ENDOFTRACK: // end of track
  1014. npTrack->endOfTrack = TRUE;
  1015. break; // (read 0 bytes)
  1016. case TEMPOCHANGE: // tempo change
  1017. if (npTrack == npSeq->firstTrack)
  1018. {
  1019. tempTempo = GetMotorola24(npTrack);
  1020. bytesRead = 3;
  1021. if (npTrack->blockedOn == not_blocked)
  1022. {
  1023. //npSeq->tempo = tempTempo / npSeq->resolution;
  1024. SeqSetTempo(npSeq, tempTempo / npSeq->resolution);
  1025. if (wMode == MODE_SCANEM)
  1026. if (!(AddTempoMapItem(npSeq, npSeq->tempo,
  1027. npTrack->length)))
  1028. return FALSE; // memory alloc failure !
  1029. }
  1030. }
  1031. break;
  1032. case SMPTEOFFSET: // SMPTE Offset
  1033. if (npTrack == npSeq->firstTrack)
  1034. {
  1035. tempMM.u.smpte.hour = GetByte(npTrack);
  1036. tempMM.u.smpte.min = GetByte(npTrack);
  1037. tempMM.u.smpte.sec = GetByte(npTrack);
  1038. tempMM.u.smpte.frame = GetByte(npTrack);
  1039. //tempSMPTEOff.fractionalFrame = GetByte(npTrack); // add later?
  1040. bytesRead = 4;
  1041. if (npTrack->blockedOn == not_blocked)
  1042. npSeq->smpteOffset = tempMM;
  1043. }
  1044. break;
  1045. case TIMESIG: // time signature
  1046. // spec doesn't say, but probably only use if on track 1.
  1047. tempTimeSig.numerator = GetByte(npTrack);
  1048. tempTimeSig.denominator = GetByte(npTrack);
  1049. tempTimeSig.midiClocksMetro = GetByte(npTrack);
  1050. tempTimeSig.thirtySecondQuarter = GetByte(npTrack);
  1051. bytesRead = 4;
  1052. if (npTrack->blockedOn == not_blocked)
  1053. npSeq->timeSignature = tempTimeSig;
  1054. break;
  1055. case SEQSTAMP: // General MS midi stamp
  1056. if ((length < 3) || npTrack->delta)
  1057. break;
  1058. for (; bytesRead < 3;)
  1059. Manufacturer[bytesRead++] = GetByte(npTrack);
  1060. if (!Manufacturer[0] && !Manufacturer[1] && (Manufacturer[2] == 0x41))
  1061. npSeq->fwFlags |= GENERALMSMIDI;
  1062. break;
  1063. } // end switch
  1064. } // if metaFilter
  1065. if (!npTrack->blockedOn)
  1066. {
  1067. SkipBytes(npTrack, length - bytesRead);// skip unexpected bytes (as per spec)
  1068. if (npTrack->blockedOn)
  1069. switch (wMode) // mature blocking status
  1070. {
  1071. case MODE_SEEK_TICKS:
  1072. npTrack->blockedOn = in_SkipBytes_Seek;
  1073. break;
  1074. case MODE_PLAYING:
  1075. case MODE_SILENT:
  1076. npTrack->blockedOn = in_SkipBytes_Play;
  1077. break;
  1078. case MODE_SCANEM:
  1079. npTrack->blockedOn = in_SkipBytes_ScanEM;
  1080. break;
  1081. }
  1082. }
  1083. return TRUE;
  1084. }
  1085. PRIVATE BOOL NEAR PASCAL LegalMIDIFileStatus(BYTE status)
  1086. {
  1087. if (status < 0x80)
  1088. return FALSE;
  1089. switch (status)
  1090. {
  1091. // legal case 0xf0: sysex excape
  1092. case 0xf1:
  1093. case 0xf2:
  1094. case 0xf3:
  1095. case 0xf4:
  1096. case 0xf5:
  1097. case 0xf6:
  1098. // legal case 0xf7: no f0 sysex excape
  1099. case 0xf8:
  1100. case 0xf9:
  1101. case 0xfA:
  1102. case 0xfB:
  1103. case 0xfC:
  1104. case 0xfD:
  1105. case 0xfE:
  1106. // legal case 0xfF: meta escape
  1107. return FALSE;
  1108. break;
  1109. default:
  1110. return TRUE; // all other cases are legal status bytes
  1111. }
  1112. }
  1113. PUBLIC BOOL NEAR PASCAL ScanEarlyMetas(NPSEQ npSeq, NPTRACK npTrack, DWORD dwUntil)
  1114. /* Scan each track for meta events that affect the initialization
  1115. of data such as tempo, time sig, key sig, SMPTE offset...
  1116. If track passed in null, start at beginning of seq, else
  1117. start with the track passed in.
  1118. Warning: the track parameter is for reentrancy in case of blocking.
  1119. This function should be called with track NULL first, else ListGetNext
  1120. will not function properly.
  1121. This function assumes that all sequence tracks have been rewound.
  1122. Returns false only on memory allocation error.
  1123. */
  1124. {
  1125. BYTE status;
  1126. BYTE patch;
  1127. BYTE chan;
  1128. BYTE key;
  1129. BOOL bTempoMap;
  1130. LONG lOldDelta;
  1131. #define BASEDRUMCHAN 15
  1132. #define EXTENDDRUMCHAN 9
  1133. #define NOTEON 0X90
  1134. // determine if need to create a tempo map
  1135. if (npSeq->divType == SEQ_DIV_PPQN)
  1136. bTempoMap = TRUE;
  1137. else
  1138. bTempoMap = FALSE;
  1139. if (!npTrack) // if track passed in null, get the first one
  1140. {
  1141. npTrack = (NPTRACK) List_Get_First(npSeq->trackList);
  1142. npTrack->lastStatus = 0; // start with null running status
  1143. npTrack->length = 0;
  1144. }
  1145. do
  1146. {
  1147. do
  1148. {
  1149. MarkLocation(npTrack); // remember current location
  1150. lOldDelta = npTrack->delta; // remember last delta
  1151. FillInDelta(npTrack);
  1152. // ***TBD ck illegal delta
  1153. if (npTrack->blockedOn) // abort on block
  1154. break;
  1155. if ((npTrack->delta + npTrack->length) < dwUntil)
  1156. {
  1157. status = GetStatus(npTrack);
  1158. chan = (BYTE)(status & 0x0F);
  1159. if (npTrack->blockedOn)
  1160. break;
  1161. // check illegal status
  1162. if (!LegalMIDIFileStatus(status)) //error
  1163. {
  1164. npSeq->fwFlags &= ~LEGALFILE;
  1165. return TRUE;
  1166. }
  1167. else if (status == METAEVENT)
  1168. {
  1169. // these actions will set the sequencer globals
  1170. if (!(HandleMetaEvent(npSeq, npTrack, MODE_SCANEM)))
  1171. return FALSE; // blew a tempo memory alloc
  1172. }
  1173. else if ((status & 0xF0) == PROGRAMCHANGE)
  1174. {
  1175. patch = GetByte(npTrack);
  1176. if ((patch < 128) && (!npTrack->blockedOn))
  1177. npSeq->patchArray[patch] |= (1 << chan);
  1178. }
  1179. else if ( ((status & 0xF0) == NOTEON) &&
  1180. ((chan == BASEDRUMCHAN) || (chan == EXTENDDRUMCHAN)) )
  1181. {
  1182. key = GetByte(npTrack);
  1183. if ((key < 128) && (!npTrack->blockedOn))
  1184. {
  1185. npSeq->drumKeyArray[key] |= (1 << chan);
  1186. GetByte(npTrack); // toss velocity byte
  1187. }
  1188. }
  1189. else
  1190. SkipEvent(status, npTrack); //skip bytes block set within
  1191. }
  1192. if ((npTrack->blockedOn == not_blocked) && (!npTrack->endOfTrack))
  1193. {
  1194. /* NB: eot avoids adding last delta (which can be large,
  1195. but isn't really a part of the sequence) */
  1196. npTrack->length += npTrack->delta; //add in this delta
  1197. npTrack->delta = 0; // zero it out (emulates playing it)
  1198. }
  1199. }
  1200. while ((npTrack->blockedOn == not_blocked) && (!npTrack->endOfTrack)
  1201. && (npTrack->length < dwUntil));
  1202. if (npTrack->blockedOn == not_blocked)
  1203. {
  1204. if (npTrack->length > npSeq->length)
  1205. npSeq->length = npTrack->length; // seq length is longest track
  1206. if (NULL !=
  1207. (npTrack = (NPTRACK) List_Get_Next(npSeq->trackList, npTrack)))
  1208. {
  1209. npTrack->lastStatus = 0; // start with null running status
  1210. npTrack->length = 0;
  1211. }
  1212. }
  1213. }
  1214. // get the next track
  1215. while (npTrack && (npTrack->blockedOn == not_blocked));
  1216. // now reset location and mature the block status if blocked on input
  1217. // (note: doesn't affect skip bytes status, which is set at lower level)
  1218. if (npTrack && (npTrack->blockedOn == on_input))
  1219. {
  1220. ResetLocation(npTrack); // restore last saved location
  1221. npTrack->delta = lOldDelta; // "undo" any change to delta
  1222. npTrack->blockedOn = in_ScanEarlyMetas;
  1223. }
  1224. return TRUE;
  1225. }
  1226. PUBLIC UINT NEAR PASCAL TimerIntRoutine(NPSEQ npSeq, LONG elapsedTick)
  1227. /* This routine does everything that should be done at this time (usually
  1228. sending notes) and sets up the timer to wake us up the next time
  1229. something should happen.
  1230. Interface: elapsedTick is set by the caller to tell how much time
  1231. has elapsed since this fn was last called. (For ppqn files, a tick is
  1232. 1 ppqn in 960 ppqn format. For SMPTE files, a tick is some fraction
  1233. of a frame. */
  1234. {
  1235. FileStatus fStatus = NoErr;
  1236. BOOL loop;
  1237. LONG delta;
  1238. LONG wakeupInterval;
  1239. DWORD dTime; //delta in ms. 'till next event
  1240. int mode;
  1241. #ifdef WIN32
  1242. EnterCrit();
  1243. #endif // WIN32
  1244. if (npSeq->bTimerEntered)
  1245. {
  1246. dprintf1(("TI REENTERED!!!!!!"));
  1247. #ifdef WIN32
  1248. LeaveCrit();
  1249. #endif // WIN32
  1250. return 0;
  1251. }
  1252. npSeq->bTimerEntered = TRUE;
  1253. // compute whether we're behind so we know whether to sound note-ons.
  1254. wakeupInterval = (DWORD)npSeq->nextExactTime - timeGetTime();
  1255. if (npSeq->playing)
  1256. {
  1257. do
  1258. {
  1259. loop = FALSE;
  1260. /* "ElapsedTick is set by whoever sets up timer callback
  1261. ALL TIMING IS IN TERMS OF Ticks!!! (except for timer API) */
  1262. if (wakeupInterval > -100) // send all notes not more than
  1263. mode = MODE_PLAYING; // 0.1 seconds behind
  1264. else
  1265. mode = MODE_SILENT;
  1266. fStatus = SendAllEventsB4(npSeq, elapsedTick, mode);
  1267. if (fStatus == NoErr)
  1268. {
  1269. delta = npSeq->nextEventTrack->delta; // get delta 'till next event
  1270. elapsedTick = delta; // for next time
  1271. if (delta)
  1272. dTime = muldiv32(delta, npSeq->tempo, 1000);
  1273. else
  1274. dTime = 0;
  1275. npSeq->nextExactTime += dTime;
  1276. /* nextExactTime is a global that is always looking
  1277. at next event. Remember, tempo is in u-sec per tick
  1278. (+500 for rounding) */
  1279. wakeupInterval = (DWORD)npSeq->nextExactTime -
  1280. timeGetTime();
  1281. if (wakeupInterval > (LONG)MINPERIOD) // buffer to prevent reentrancy
  1282. {
  1283. #ifdef DEBUG
  1284. if (wakeupInterval > 60000) { // 1 minute
  1285. dprintf2(("MCISEQ: Setting HUGE TIMER INTERVAL!!!"));
  1286. }
  1287. #endif
  1288. //
  1289. // we are going to program a event, clear bTimerEntered
  1290. // just in case it goes off before we get out of this
  1291. // function.
  1292. //
  1293. npSeq->bTimerEntered = FALSE;
  1294. if (SetTimerCallback(npSeq, (UINT) wakeupInterval,
  1295. elapsedTick) == MIDISEQERR_TIMER)
  1296. {
  1297. #ifndef WIN32
  1298. // Win 16 effectively releases the critical section
  1299. // more easily than NT. The flag could have been
  1300. // reset since it was cleared above
  1301. #ifdef DEBUG
  1302. npSeq->bTimerEntered = FALSE;
  1303. #endif
  1304. #endif
  1305. Stop(npSeq);
  1306. seqCallback(npSeq->firstTrack, MIDISEQ_DONEPLAY, 0, 0);
  1307. dprintf1(("MCISEQ: TIMER ERROR!!!"));
  1308. #ifdef WIN32
  1309. LeaveCrit();
  1310. #endif // WIN32
  1311. return MIDISEQERR_TIMER;
  1312. }
  1313. }
  1314. else
  1315. {
  1316. loop = TRUE; // already time to fire next note!
  1317. // while ((DWORD)npSeq->nextExactTime
  1318. // > timeGetTime()); // busy wait 'till then
  1319. }
  1320. } //if (fStatus == NoErr)
  1321. else if ((fStatus == AllTracksEmpty) || (fStatus == AtLimit))
  1322. {
  1323. if (npSeq->wCBMessage == SEQ_PLAY)
  1324. NotifyCallback(npSeq->hStream);
  1325. Stop(npSeq);
  1326. seqCallback(npSeq->firstTrack, MIDISEQ_DONEPLAY, 0, 0);
  1327. dprintf1(("MCISEQ: At Limit or EOF"));
  1328. }
  1329. else {
  1330. dprintf1(("MCISEQ: QUIT!!! fStatus = %x", fStatus));
  1331. }
  1332. }
  1333. while (loop); // if enough time elapsed to fire next note already.
  1334. } // if npSeq->playing
  1335. FlushMidi(npSeq->hMIDIOut, &npSeq->longMIDI[NUMSYSEXHDRS]);
  1336. npSeq->bTimerEntered = FALSE;
  1337. #ifdef WIN32
  1338. LeaveCrit();
  1339. #endif // WIN32
  1340. return MIDISEQERR_NOERROR;
  1341. }
  1342. PUBLIC FileStatus NEAR PASCAL SendAllEventsB4(NPSEQ npSeq,
  1343. LONG elapsedTick, int mode)
  1344. /* send all events in the MIDI stream that occur before current tick, where
  1345. currentTick is elapsed ticks since last called.
  1346. This function is called both to play notes, and to scan forward to a song
  1347. pointer position. The mode parameter reflects this state. */
  1348. {
  1349. LONG residualDelta; // residual holds how much of elapsed
  1350. // tick has been accounted for
  1351. FileStatus fStatus = GetNextEvent(npSeq);
  1352. WORD wAdj;
  1353. BYTE status;
  1354. DWORD dwNextTick;
  1355. if (npSeq->bSending) // prevent reentrancy
  1356. return NoErr;
  1357. npSeq->bSending = TRUE; // set entered flag
  1358. #ifdef DEBUG
  1359. if (mode == MODE_SEEK_TICKS) {
  1360. dprintf2(("ENTERING SEND ALL EVENTS"));
  1361. }
  1362. #endif
  1363. if (elapsedTick > 0)
  1364. residualDelta = elapsedTick;
  1365. else
  1366. residualDelta = 0;
  1367. if (mode == MODE_SEEK_TICKS) // hack for song ptr -- don't send unless before eT
  1368. wAdj = 1;
  1369. else
  1370. wAdj = 0;
  1371. while ((fStatus == NoErr) &&
  1372. (residualDelta >= (npSeq->nextEventTrack->delta + wAdj)) &&
  1373. (!npSeq->bSendingSysEx)) // can't process any other msgs during sysex
  1374. /* send all events within delta */
  1375. {
  1376. if (mode == MODE_PLAYING)
  1377. // if playing, are we at the end yet yet?
  1378. {
  1379. // compute temp var
  1380. dwNextTick = npSeq->currentTick + npSeq->nextEventTrack->delta;
  1381. // if not play to end, don't play the last note
  1382. if ((dwNextTick > npSeq->playTo) ||
  1383. ((npSeq->playTo < npSeq->length) &&
  1384. (dwNextTick == npSeq->playTo)))
  1385. {
  1386. fStatus = AtLimit; // have reached play limit user requested
  1387. SubtractAllTracks(npSeq, (npSeq->playTo - npSeq->currentTick));
  1388. npSeq->currentTick = npSeq->playTo; // set to limit
  1389. break; // leave while loop
  1390. }
  1391. }
  1392. status = npSeq->nextEventTrack->shortMIDIData.byteMsg.status;
  1393. if (status == METAEVENT)
  1394. {
  1395. MarkLocation(npSeq->nextEventTrack); // remember current location
  1396. // note that these are "handled" even within song ptr traversal
  1397. HandleMetaEvent(npSeq, npSeq->nextEventTrack, mode);
  1398. if (npSeq->nextEventTrack->blockedOn == on_input)
  1399. { // nb: not affected if blocked in skip bytes!!
  1400. ResetLocation(npSeq->nextEventTrack); // reset location
  1401. if (mode == MODE_SEEK_TICKS)
  1402. npSeq->nextEventTrack->blockedOn = in_Seek_Meta;
  1403. else
  1404. npSeq->nextEventTrack->blockedOn = in_Normal_Meta;
  1405. }
  1406. }
  1407. else if ((status == SYSEX) || (status == SYSEXF7))
  1408. {
  1409. SendSysEx(npSeq);
  1410. if (npSeq->bSendingSysEx)
  1411. fStatus = InSysEx;
  1412. }
  1413. // send all but note-ons unless playing and note is current
  1414. else if (((mode == MODE_PLAYING) &&
  1415. (npSeq->nextEventTrack->delta >= 0)) ||
  1416. ( ! (((status & 0xF0) == 0x90) && // note on with vel != 0
  1417. (npSeq->nextEventTrack->shortMIDIData.byteMsg.byte3)) ))
  1418. SendMIDI(npSeq, npSeq->nextEventTrack);
  1419. if ((npSeq->nextEventTrack->blockedOn == not_blocked) ||
  1420. (npSeq->bSendingSysEx) ||
  1421. (npSeq->nextEventTrack->blockedOn == in_SkipBytes_Play) ||
  1422. (npSeq->nextEventTrack->blockedOn == in_SkipBytes_Seek) ||
  1423. (npSeq->nextEventTrack->blockedOn == in_SkipBytes_ScanEM))
  1424. {
  1425. // account for time spent only if it was sent
  1426. // ...and only if dealing with a fresh delta
  1427. if (npSeq->nextEventTrack->delta > 0)
  1428. {
  1429. residualDelta -= npSeq->nextEventTrack->delta;
  1430. npSeq->currentTick += npSeq->nextEventTrack->delta;
  1431. // account for delta
  1432. SubtractAllTracks(npSeq, npSeq->nextEventTrack->delta);
  1433. }
  1434. }
  1435. if ((npSeq->nextEventTrack->blockedOn == not_blocked) &&
  1436. (!npSeq->nextEventTrack->endOfTrack) &&
  1437. (!npSeq->bSendingSysEx))
  1438. //fill in the next event from stream
  1439. FillInNextTrack(npSeq->nextEventTrack);
  1440. if (npSeq->nextEventTrack->blockedOn == on_input) //mature block
  1441. {
  1442. if (mode == MODE_SEEK_TICKS) // set blocked status depending on mode
  1443. npSeq->nextEventTrack->blockedOn = in_Seek_Tick;
  1444. else
  1445. npSeq->nextEventTrack->blockedOn = between_msg_out;
  1446. }
  1447. if (!npSeq->bSendingSysEx)
  1448. // Make nextEventTrack point to next track to play
  1449. if (((fStatus = GetNextEvent(npSeq)) == NoErr) &&
  1450. (npSeq->nextEventTrack->endOfTrack))
  1451. npSeq->readyToPlay = FALSE;
  1452. }
  1453. if ((fStatus == NoErr) && AllTracksUnblocked(npSeq))
  1454. {
  1455. npSeq->currentTick += residualDelta;
  1456. SubtractAllTracks(npSeq, residualDelta); //account for rest of delta
  1457. }
  1458. #ifdef DEBUG
  1459. if (mode == MODE_SEEK_TICKS) {
  1460. dprintf2(("LEAVING SEND ALL EVENTS"));
  1461. }
  1462. #endif
  1463. npSeq->bSending = FALSE; // reset entered flag
  1464. return fStatus;
  1465. }
  1466. PUBLIC FileStatus NEAR PASCAL GetNextEvent(NPSEQ npSeq)
  1467. /* scan all track queues for the next event, and put the next one to occur in
  1468. "nextEvent." Remember that each track can use its own running status
  1469. (we'll fill in all status).
  1470. */
  1471. #define MAXDELTA 0x7FFFFFFF
  1472. {
  1473. NPTRACK npTrack;
  1474. NPTRACK npTrackMin = NULL;
  1475. LONG minDelta = MAXDELTA; /* larger than any possible delta */
  1476. BOOL foundBlocked = FALSE;
  1477. npTrack = (NPTRACK) List_Get_First(npSeq->trackList);
  1478. while (npTrack) /* find smallest delta */
  1479. {
  1480. if ((!npTrack->endOfTrack) && (npTrack->delta < minDelta))
  1481. // note that "ties" go to earliest track
  1482. {
  1483. if (npTrack->blockedOn)
  1484. foundBlocked = TRUE;
  1485. else
  1486. {
  1487. minDelta = npTrack->delta;
  1488. npTrackMin = npTrack;
  1489. }
  1490. }
  1491. npTrack = (NPTRACK) List_Get_Next(npSeq->trackList, npTrack);
  1492. }
  1493. npSeq->nextEventTrack = npTrackMin;
  1494. if (npTrackMin == NULL)
  1495. if (foundBlocked)
  1496. return OnlyBlockedTracks;
  1497. else
  1498. return AllTracksEmpty;
  1499. else
  1500. return NoErr;
  1501. }
  1502. PUBLIC VOID NEAR PASCAL FillInNextTrack(NPTRACK npTrack)
  1503. /* given a pointer to a track structure, fill it in with next event data
  1504. (delta time and data) received from mciseq streamer. */
  1505. {
  1506. LONG lOldDelta;
  1507. MarkLocation(npTrack); // remember where you started in case get blocked
  1508. lOldDelta = npTrack->delta; // remember this delta in case block
  1509. FillInDelta(npTrack);
  1510. FillInEvent(npTrack);
  1511. if (npTrack->blockedOn)
  1512. {
  1513. ResetLocation(npTrack); // blocked -- hence rewind
  1514. npTrack->delta = lOldDelta; // restore old delta
  1515. }
  1516. }
  1517. /**********************************************************/
  1518. PUBLIC VOID NEAR PASCAL FillInDelta(NPTRACK npTrack) // fills in delta of track passed in
  1519. {
  1520. npTrack->delta += GetVarLen(npTrack);
  1521. }
  1522. PUBLIC UINT NEAR PASCAL SendPatchCache(NPSEQ npSeq, BOOL cache)
  1523. {
  1524. UINT cacheOrNot;
  1525. UINT wRet;
  1526. #define BANK 0
  1527. #define DRUMPATCH 0
  1528. if (!npSeq->hMIDIOut) // do nothing if no midiport
  1529. return MIDISEQERR_NOERROR;
  1530. if (cache)
  1531. cacheOrNot = MIDI_CACHE_BESTFIT;
  1532. else
  1533. cacheOrNot = MIDI_UNCACHE;
  1534. wRet = midiOutCachePatches(npSeq->hMIDIOut, BANK, // send it
  1535. (LPPATCHARRAY) &npSeq->patchArray, cacheOrNot);
  1536. if (!wRet)
  1537. wRet = midiOutCacheDrumPatches(npSeq->hMIDIOut, DRUMPATCH, // send it
  1538. (LPKEYARRAY) &npSeq->drumKeyArray, cacheOrNot);
  1539. return wRet == MMSYSERR_NOTSUPPORTED ? 0 : wRet;
  1540. }
  1541. PUBLIC VOID NEAR PASCAL SendSysEx(NPSEQ npSeq)
  1542. // get a sysex buffer, fill it, and send it off
  1543. // keep doing this until done (!sysexremlength), or blocked on input,
  1544. // or blocked on output.
  1545. {
  1546. NPLONGMIDI myBuffer;
  1547. // temp variable to reduce address computation time
  1548. DWORD sysExRemLength = npSeq->nextEventTrack->sysExRemLength;
  1549. int max, bytesSent;
  1550. npSeq->bSendingSysEx = TRUE;
  1551. dprintf2(("Entering SendSysEx"));
  1552. while ((sysExRemLength) && (!npSeq->nextEventTrack->blockedOn))
  1553. {
  1554. if (!(myBuffer = GetSysExBuffer(npSeq)))
  1555. break; // can't get a buffer
  1556. bytesSent = 0; // init buffer data index
  1557. // if status byte was F0, make that 1st byte of message
  1558. // (remember, it could be F7, which shouldn't be sent)
  1559. if (npSeq->nextEventTrack->shortMIDIData.byteMsg.status == SYSEX)
  1560. {
  1561. dprintf3(("Packing Sysex Byte"));
  1562. myBuffer->data[bytesSent++] = SYSEX;
  1563. sysExRemLength++; // semi-hack to account for extra byte
  1564. //by convention, clear f0 status after f0 has been sent
  1565. npSeq->nextEventTrack->shortMIDIData.byteMsg.status = 0;
  1566. }
  1567. max = min(LONGBUFFSIZE, (int)sysExRemLength); // max bytes for this buff
  1568. // fill buffer -- note that i will reflect # of valid bytes in buff
  1569. do
  1570. myBuffer->data[bytesSent] = GetByte(npSeq->nextEventTrack);
  1571. while ((!npSeq->nextEventTrack->blockedOn) && (++bytesSent < max));
  1572. // account for bytes sent
  1573. sysExRemLength -= bytesSent;
  1574. // send buffer
  1575. myBuffer->midihdr.dwBufferLength = bytesSent;
  1576. dprintf3(("SENDing SendSysEx"));
  1577. if (npSeq->hMIDIOut)
  1578. midiOutLongMsg(npSeq->hMIDIOut, &myBuffer->midihdr,
  1579. sizeof(MIDIHDR));
  1580. }
  1581. if (sysExRemLength) // not done -- must be blocked
  1582. {
  1583. //blocked on in or out?
  1584. if (npSeq->nextEventTrack->blockedOn)
  1585. {
  1586. //blocked on in
  1587. npSeq->nextEventTrack->blockedOn = in_SysEx;
  1588. dprintf3(("Sysex blocked on INPUT"));
  1589. }
  1590. else
  1591. {
  1592. //blocked on out
  1593. npSeq->bSysExBlock = TRUE;
  1594. dprintf3(("Sysex blocked on OUTPUT"));
  1595. }
  1596. }
  1597. else // done
  1598. {
  1599. npSeq->bSendingSysEx = FALSE;
  1600. dprintf4(("Sysex Legally Finished"));
  1601. }
  1602. npSeq->nextEventTrack->sysExRemLength = sysExRemLength; // restore
  1603. }
  1604. /*
  1605. // BOGUS
  1606. void DoSyncPrep(NPSEQ npSeq)
  1607. {
  1608. //a bunch of sync prep will go here, such as:
  1609. if ((npSeq->slaveOf != SEQ_SYNC_NOTHING) && (npSeq->slaveOf != SEQ_SYNC_MIDICLOCK) &&
  1610. (npSeq->division == SEQ_DIV_PPQN))
  1611. addTempoMap(npSeq);
  1612. else
  1613. destroyTempoMap(npSeq);
  1614. if (npSeq->masterOf != SEQ_SYNC_NOTHING)
  1615. addSyncOut(npSeq);
  1616. else
  1617. destroySyncOut(npSeq);
  1618. }
  1619. */