MIDI.CPP
上传用户:lianyisd
上传日期:2019-11-03
资源大小:5188k
文件大小:34k
源码类别:

midi

开发平台:

Visual C++

  1. // Midi.cpp
  2. //
  3. #include "stdafx.h"
  4. #include "Midi.h"
  5. #ifdef _DEBUG
  6. #define new DEBUG_NEW
  7. #undef THIS_FILE
  8. static char THIS_FILE[] = __FILE__;
  9. #endif
  10. #define MThd 0x6468544D // Start of file
  11. #define MTrk 0x6B72544D // Start of track
  12. #define BUFFER_TIME_LENGTH 60   // Amount to fill in milliseconds
  13. // These structures are stored in MIDI files; they need to be byte aligned.
  14. //
  15. #pragma pack(1)
  16. // Contents of MThd chunk.
  17. struct MIDIFILEHDR
  18. {
  19.     WORD wFormat; // Format (hi-lo)
  20.     WORD wTrackCount; // # tracks (hi-lo)
  21.     WORD wTimeDivision; // Time division (hi-lo)
  22. };
  23. #pragma pack() // End of need for byte-aligned structures
  24. // Macros for swapping hi/lo-endian data
  25. //
  26. #define WORDSWAP(w) (((w) >> 8) | 
  27. (((w) << 8) & 0xFF00))
  28. #define DWORDSWAP(dw) (((dw) >> 24) | 
  29. (((dw) >> 8) & 0x0000FF00) | 
  30. (((dw) << 8) & 0x00FF0000) | 
  31. (((dw) << 24) & 0xFF000000))
  32. static char gteBadRunStat[]  = "Reference to missing running status.";
  33. static char gteRunStatMsgTrunc[]= "Running status message truncated";
  34. static char gteChanMsgTrunc[] = "Channel message truncated";
  35. static char gteSysExLenTrunc[] = "SysEx event truncated (length)";
  36. static char gteSysExTrunc[] = "SysEx event truncated";
  37. static char gteMetaNoClass[] = "Meta event truncated (no class byte)";
  38. static char gteMetaLenTrunc[] = "Meta event truncated (length)";
  39. static char gteMetaTrunc[] = "Meta event truncated";
  40. static char gteNoMem[] = "Out of memory during malloc call";
  41. //////////////////////////////////////////////////////////////////////
  42. // CMIDI -- Construction/Destruction
  43. //////////////////////////////////////////////////////////////////////
  44. CMIDI::CMIDI()
  45. : m_dwSoundSize(0)
  46. , m_pSoundData(0)
  47. , m_dwFormat(0)
  48. , m_dwTrackCount(0)
  49. , m_dwTimeDivision(0)
  50. , m_bPlaying(FALSE)
  51. , m_hStream(0)
  52. , m_dwProgressBytes(0)
  53. , m_bLooped(FALSE)
  54. , m_tkCurrentTime(0)
  55. , m_dwBufferTickLength(0)
  56. , m_dwCurrentTempo(0)
  57. , m_dwTempoMultiplier(100)
  58. , m_bInsertTempo(FALSE)
  59. , m_bBuffersPrepared(FALSE)
  60. , m_nCurrentBuffer(0)
  61. , m_uMIDIDeviceID(MIDI_MAPPER)
  62. , m_nEmptyBuffers(0)
  63. , m_bPaused(FALSE)
  64. , m_uCallbackStatus(0)
  65. , m_hBufferReturnEvent(0)
  66. , m_ptsTrack(0)
  67. , m_ptsFound(0)
  68. , m_dwStatus(0)
  69. , m_tkNext(0)
  70. , m_dwMallocBlocks(0)
  71. {
  72. m_hBufferReturnEvent = ::CreateEvent(0, FALSE, FALSE, TEXT("Wait For Buffer Return"));
  73. ASSERT(m_hBufferReturnEvent != 0);
  74. }
  75. CMIDI::~CMIDI()
  76. {
  77. Stop(FALSE);
  78. if(m_hBufferReturnEvent)
  79. ::CloseHandle(m_hBufferReturnEvent);
  80. }
  81. BOOL CMIDI::Create(UINT uResID, CWnd * pWndParent /* = NULL */)
  82. {
  83. return Create(MAKEINTRESOURCE(uResID), pWndParent);
  84. }
  85. BOOL CMIDI::Create(LPCTSTR pszResID, CWnd * pWndParent /* = NULL */)
  86. {
  87. //////////////////////////////////////////////////////////////////
  88. // load resource
  89. HINSTANCE hApp = ::GetModuleHandle(0);
  90. ASSERT(hApp);
  91. HRSRC hResInfo = ::FindResource(hApp, pszResID, TEXT("MIDI"));
  92. if(hResInfo == 0)
  93. return FALSE;
  94. HGLOBAL hRes = ::LoadResource(hApp, hResInfo);
  95. if(hRes == 0)
  96. return FALSE;
  97. LPVOID pTheSound = ::LockResource(hRes);
  98. if(pTheSound == 0)
  99. return FALSE;
  100. DWORD dwTheSound = ::SizeofResource(hApp, hResInfo);
  101. return Create(pTheSound, dwTheSound, pWndParent);
  102. }
  103. BOOL CMIDI::Create(LPVOID pSoundData, DWORD dwSize, CWnd * pWndParent /* = NULL */)
  104. {
  105. if( m_pSoundData ) {
  106. // already created
  107. ASSERT(FALSE);
  108. return FALSE;
  109. }
  110. ASSERT(pSoundData != 0);
  111. ASSERT(dwSize > 0);
  112. register LPBYTE p = LPBYTE(pSoundData);
  113. // check header of MIDI
  114. if(*(DWORD*)p != MThd) {
  115. ASSERT(FALSE);
  116. return FALSE;
  117. }
  118. p += sizeof(DWORD);
  119. // check header size
  120. DWORD dwHeaderSize = DWORDSWAP(*(DWORD*)p);
  121. if( dwHeaderSize != sizeof(MIDIFILEHDR) ) {
  122. ASSERT(FALSE);
  123. return FALSE;
  124. }
  125. p += sizeof(DWORD);
  126. // get header
  127. MIDIFILEHDR hdr;
  128. ::CopyMemory(&hdr, p, dwHeaderSize);
  129. m_dwFormat = DWORD(WORDSWAP(hdr.wFormat));
  130. m_dwTrackCount = DWORD(WORDSWAP(hdr.wTrackCount));
  131. m_dwTimeDivision = DWORD(WORDSWAP(hdr.wTimeDivision));
  132. p += dwHeaderSize;
  133. // create the array of tracks
  134. m_Tracks.resize(m_dwTrackCount);
  135. for(register DWORD i = 0; i < m_dwTrackCount; ++i) {
  136. // check header of track
  137. if(*(DWORD*)p != MTrk) {
  138. ASSERT(FALSE);
  139. return FALSE;
  140. }
  141. p += sizeof(DWORD);
  142. m_Tracks[i].dwTrackLength = DWORDSWAP(*(DWORD*)p);
  143. p += sizeof(DWORD);
  144. m_Tracks[i].pTrackStart = m_Tracks[i].pTrackCurrent = p;
  145. p += m_Tracks[i].dwTrackLength;
  146.         // Handle bozo MIDI files which contain empty track chunks
  147. if( !m_Tracks[i].dwTrackLength ) {
  148. m_Tracks[i].fdwTrack |= ITS_F_ENDOFTRK;
  149. continue;
  150. }
  151. // We always preread the time from each track so the mixer code can
  152. // determine which track has the next event with a minimum of work
  153. if( !GetTrackVDWord( &m_Tracks[i], &m_Tracks[i].tkNextEventDue )) {
  154. TRACE0("Error in MIDI datan");
  155. ASSERT(FALSE);
  156. return FALSE;
  157. }
  158. }
  159. m_pSoundData = pSoundData;
  160. m_dwSoundSize = dwSize;
  161. m_pWndParent = pWndParent;
  162. // allocate volume channels and initialise them
  163. m_Volumes.resize(NUM_CHANNELS, VOLUME_INIT);
  164. if( ! StreamBufferSetup() ) {
  165. ASSERT(FALSE);
  166. return FALSE;
  167. }
  168. return TRUE;
  169. }
  170. BOOL CMIDI :: Play(BOOL bInfinite /* = FALSE */) {
  171. if( IsPaused() ) {
  172. Continue();
  173. return TRUE;
  174. }
  175. // calling Play() while it is already playing will restart from scratch
  176. if( IsPlaying() )
  177. Stop();
  178. // Clear the status of our callback so it will handle
  179. // MOM_DONE callbacks once more
  180. m_uCallbackStatus = 0;
  181. if( !m_bLooped )
  182. m_bInsertTempo = TRUE;
  183. MMRESULT mmResult;
  184. if( (mmResult = midiStreamRestart(m_hStream)) != MMSYSERR_NOERROR ) {
  185. MidiError(mmResult);
  186. return FALSE;
  187. }
  188. m_bPlaying = TRUE;
  189. m_bLooped = bInfinite;
  190. return m_bPlaying;
  191. }
  192. BOOL CMIDI :: Stop(BOOL bReOpen /*=TRUE*/) {
  193. MMRESULT mmrRetVal;
  194. if( IsPlaying() || (m_uCallbackStatus != STATUS_CALLBACKDEAD) ) {
  195. m_bPlaying = m_bPaused = FALSE;
  196. if( m_uCallbackStatus != STATUS_CALLBACKDEAD && m_uCallbackStatus != STATUS_WAITINGFOREND )
  197. m_uCallbackStatus = STATUS_KILLCALLBACK;
  198. if( (mmrRetVal = midiStreamStop(m_hStream) ) != MMSYSERR_NOERROR ) {
  199. MidiError(mmrRetVal);
  200. return FALSE;
  201. }
  202. if( (mmrRetVal = midiOutReset((HMIDIOUT)m_hStream)) != MMSYSERR_NOERROR ) {
  203. MidiError(mmrRetVal);
  204. return FALSE;
  205. }
  206. // Wait for the callback thread to release this thread, which it will do by
  207. // calling SetEvent() once all buffers are returned to it
  208. if( WaitForSingleObject( m_hBufferReturnEvent, DEBUG_CALLBACK_TIMEOUT ) == WAIT_TIMEOUT ) {
  209. // Note, this is a risky move because the callback may be genuinely busy, but
  210. // when we're debugging, it's safer and faster than freezing the application,
  211. // which leaves the MIDI device locked up and forces a system reset...
  212. TRACE0("Timed out waiting for MIDI callbackn");
  213. m_uCallbackStatus = STATUS_CALLBACKDEAD;
  214. }
  215. }
  216. if( m_uCallbackStatus == STATUS_CALLBACKDEAD ) {
  217. m_uCallbackStatus = 0;
  218. FreeBuffers();
  219. if( m_hStream ) {
  220. if( (mmrRetVal = midiStreamClose(m_hStream) ) != MMSYSERR_NOERROR ) {
  221. MidiError(mmrRetVal);
  222. }
  223. m_hStream = 0;
  224. }
  225. if( bReOpen ) {
  226. if( !StreamBufferSetup() ) {
  227. // Error setting up for MIDI file
  228. // Notification is already taken care of...
  229. return FALSE;
  230. }
  231. if( ! m_bLooped ) {
  232. Rewind();
  233. m_dwProgressBytes = 0;
  234. m_dwStatus = 0;
  235. }
  236. }
  237. }
  238. return TRUE;
  239. }
  240. BOOL CMIDI :: Pause() {
  241. if( ! m_bPaused && m_bPlaying && m_pSoundData && m_hStream ) {
  242. midiStreamPause(m_hStream);
  243. m_bPaused = TRUE;
  244. }
  245. return FALSE;
  246. }
  247. BOOL CMIDI :: Continue() {
  248. if( m_bPaused && m_bPlaying && m_pSoundData && m_hStream ) {
  249. midiStreamRestart(m_hStream);
  250. m_bPaused = FALSE;
  251. }
  252. return FALSE;
  253. }
  254. BOOL CMIDI :: Rewind() {
  255. if( ! m_pSoundData )
  256. return FALSE;
  257. for(register DWORD i = 0; i < m_dwTrackCount; ++i) {
  258. m_Tracks[i].pTrackCurrent = m_Tracks[i].pTrackStart;
  259. m_Tracks[i].byRunningStatus = 0;
  260. m_Tracks[i].tkNextEventDue = 0;
  261. m_Tracks[i].fdwTrack = 0;
  262.         // Handle bozo MIDI files which contain empty track chunks
  263. if( !m_Tracks[i].dwTrackLength ) {
  264. m_Tracks[i].fdwTrack |= ITS_F_ENDOFTRK;
  265. continue;
  266. }
  267. // We always preread the time from each track so the mixer code can
  268. // determine which track has the next event with a minimum of work
  269. if( !GetTrackVDWord( &m_Tracks[i], &m_Tracks[i].tkNextEventDue )) {
  270. TRACE0("Error in MIDI datan");
  271. ASSERT(FALSE);
  272. return FALSE;
  273. }
  274. }
  275. return TRUE;
  276. }
  277. DWORD CMIDI :: GetChannelCount() const {
  278. return m_Volumes.size();
  279. }
  280. void CMIDI :: SetVolume(DWORD dwPercent) {
  281. const DWORD dwSize = m_Volumes.size();
  282. for( register DWORD i = 0; i < dwSize; ++i )
  283. SetChannelVolume(i, dwPercent);
  284. }
  285. DWORD CMIDI :: GetVolume() const {
  286. DWORD dwVolume = 0;
  287. const DWORD dwSize = m_Volumes.size();
  288. for( register DWORD i = 0; i < dwSize; ++i )
  289. dwVolume += GetChannelVolume(i);
  290. return dwVolume / GetChannelCount();
  291. }
  292. void CMIDI :: SetChannelVolume(DWORD dwChannel, DWORD dwPercent) {
  293. ASSERT(dwChannel < m_Volumes.size());
  294. if( !m_bPlaying )
  295. return;
  296. m_Volumes[dwChannel] = (dwPercent > 100) ? 100 : dwPercent;
  297. DWORD dwEvent = MIDI_CTRLCHANGE | dwChannel | ((DWORD)MIDICTRL_VOLUME << 8) | ((DWORD)(m_Volumes[dwChannel]*VOLUME_MAX/100) << 16);
  298. MMRESULT mmrRetVal;
  299. if(( mmrRetVal = midiOutShortMsg((HMIDIOUT)m_hStream, dwEvent)) != MMSYSERR_NOERROR ) {
  300. MidiError(mmrRetVal);
  301. return;
  302. }
  303. }
  304. DWORD CMIDI :: GetChannelVolume(DWORD dwChannel) const {
  305. ASSERT(dwChannel < GetChannelCount());
  306. return m_Volumes[dwChannel];
  307. }
  308. void CMIDI :: SetTempo(DWORD dwPercent) {
  309. m_dwTempoMultiplier = dwPercent ? dwPercent : 1;
  310. m_bInsertTempo = TRUE;
  311. }
  312. DWORD CMIDI :: GetTempo() const {
  313. return m_dwTempoMultiplier;
  314. }
  315. void CMIDI :: SetInfinitePlay(BOOL bSet) {
  316. m_bLooped = bSet;
  317. }
  318. //////////////////////////////////////////////////////////////////////
  319. // CMIDI -- implementation
  320. //////////////////////////////////////////////////////////////////////
  321. // This function converts MIDI data from the track buffers setup by a
  322. // previous call to ConverterInit().  It will convert data until an error is
  323. // encountered or the output buffer has been filled with as much event data
  324. // as possible, not to exceed dwMaxLength. This function can take a couple
  325. // bit flags, passed through dwFlags. Information about the success/failure
  326. // of this operation and the number of output bytes actually converted will
  327. // be returned in the CONVERTINFO structure pointed at by lpciInfo.
  328. int CMIDI :: ConvertToBuffer(DWORD dwFlags, CONVERTINFO * lpciInfo) {
  329. int     nChkErr;
  330. lpciInfo->dwBytesRecorded = 0;
  331. if( dwFlags & CONVERTF_RESET ) {
  332. m_dwProgressBytes = 0;
  333. m_dwStatus = 0;
  334. memset( &m_teTemp, 0, sizeof(TEMPEVENT));
  335. m_ptsTrack = m_ptsFound = 0;
  336. }
  337. // If we were already done, then return with a warning...
  338. if( m_dwStatus & CONVERTF_STATUS_DONE ) {
  339. if( m_bLooped ) {
  340. Rewind();
  341. m_dwProgressBytes = 0;
  342. m_dwStatus = 0;
  343. } else
  344. return CONVERTERR_DONE;
  345. } else if( m_dwStatus & CONVERTF_STATUS_STUCK ) {
  346. // The caller is asking us to continue, but we're already hosed because we
  347. // previously identified something as corrupt, so complain louder this time.
  348. return( CONVERTERR_STUCK );
  349. } else if( m_dwStatus & CONVERTF_STATUS_GOTEVENT ) {
  350. // Turn off this bit flag
  351. m_dwStatus ^= CONVERTF_STATUS_GOTEVENT;
  352. // The following code for this case is duplicated from below, and is
  353. // designed to handle a "straggler" event, should we have one left over
  354. // from previous processing the last time this function was called.
  355. // Don't add end of track event 'til we're done
  356. if( m_teTemp.byShortData[0] == MIDI_META && m_teTemp.byShortData[1] == MIDI_META_EOT ) {
  357. if( m_dwMallocBlocks ) {
  358. delete [] m_teTemp.pLongData;
  359. --m_dwMallocBlocks;
  360. }
  361. } else if(( nChkErr = AddEventToStreamBuffer( &m_teTemp, lpciInfo )) != CONVERTERR_NOERROR ) {
  362. if( nChkErr == CONVERTERR_BUFFERFULL ) {
  363. // Do some processing and tell caller that this buffer's full
  364. m_dwStatus |= CONVERTF_STATUS_GOTEVENT;
  365. return CONVERTERR_NOERROR;
  366. } else if( nChkErr == CONVERTERR_METASKIP ) {
  367. // We skip by all meta events that aren't tempo changes...
  368. } else {
  369. TRACE0("Unable to add event to stream buffer.n");
  370. if( m_dwMallocBlocks ) {
  371. delete [] m_teTemp.pLongData;
  372. m_dwMallocBlocks--;
  373. }
  374. return( TRUE );
  375. }
  376. }
  377. }
  378. for(;;) {
  379. m_ptsFound = 0;
  380. m_tkNext = 0xFFFFFFFFL;
  381. // Find nearest event due
  382. for( register DWORD idx = 0; idx < m_Tracks.size(); ++idx ) {
  383. m_ptsTrack = &m_Tracks[idx];
  384. if( !(m_ptsTrack->fdwTrack & ITS_F_ENDOFTRK) && (m_ptsTrack->tkNextEventDue < m_tkNext) ) {
  385. m_tkNext = m_ptsTrack->tkNextEventDue;
  386. m_ptsFound = m_ptsTrack;
  387. }
  388. }
  389. // None found?  We must be done, so return to the caller with a smile.
  390. if( !m_ptsFound ) {
  391. m_dwStatus |= CONVERTF_STATUS_DONE;
  392. // Need to set return buffer members properly
  393. return CONVERTERR_NOERROR;
  394. }
  395. // Ok, get the event header from that track
  396. if( !GetTrackEvent( m_ptsFound, &m_teTemp )) {
  397. // Warn future calls that this converter is stuck at a corrupt spot
  398. // and can't continue
  399. m_dwStatus |= CONVERTF_STATUS_STUCK;
  400. return CONVERTERR_CORRUPT;
  401. }
  402. // Don't add end of track event 'til we're done
  403. if( m_teTemp.byShortData[0] == MIDI_META && m_teTemp.byShortData[1] == MIDI_META_EOT ) {
  404. if( m_dwMallocBlocks ) {
  405. delete [] m_teTemp.pLongData;
  406. --m_dwMallocBlocks;
  407. }
  408. continue;
  409. }
  410. if(( nChkErr = AddEventToStreamBuffer( &m_teTemp, lpciInfo )) != CONVERTERR_NOERROR ) {
  411. if( nChkErr == CONVERTERR_BUFFERFULL ) {
  412. // Do some processing and tell somebody this buffer is full...
  413. m_dwStatus |= CONVERTF_STATUS_GOTEVENT;
  414. return CONVERTERR_NOERROR;
  415. } else if( nChkErr == CONVERTERR_METASKIP ) {
  416. // We skip by all meta events that aren't tempo changes...
  417. } else {
  418. TRACE0("Unable to add event to stream buffer.n");
  419. if( m_dwMallocBlocks ) {
  420. delete [] m_teTemp.pLongData;
  421. m_dwMallocBlocks--;
  422. }
  423. return TRUE;
  424. }
  425. }
  426. }
  427. return CONVERTERR_NOERROR;
  428. }
  429. // GetTrackEvent
  430. //
  431. // Fills in the event struct with the next event from the track
  432. //
  433. // pteTemp->tkEvent will contain the absolute tick time of the event
  434. // pteTemp->byShortData[0] will contain
  435. //  MIDI_META if the event is a meta event;
  436. //   in this case pteTemp->byShortData[1] will contain the meta class
  437. //  MIDI_SYSEX or MIDI_SYSEXEND if the event is a SysEx event
  438. //  Otherwise, the event is a channel message and pteTemp->byShortData[1]
  439. //   and pteTemp->byShortData[2] will contain the rest of the event.
  440. //
  441. // pteTemp->dwEventLength will contain
  442. //  The total length of the channel message in pteTemp->byShortData if
  443. //   the event is a channel message
  444. //  The total length of the paramter data pointed to by
  445. //   pteTemp->pLongData otherwise
  446. //
  447. // pteTemp->pLongData will point at any additional paramters if the 
  448. //  event is a SysEx or meta event with non-zero length; else
  449. //  it will contain NULL
  450. //
  451. // Returns TRUE on success or FALSE on any kind of parse error
  452. // Prints its own error message ONLY in the debug version
  453. //
  454. // Maintains the state of the input track (i.e. 
  455. // ptsTrack->pTrackPointers, and ptsTrack->byRunningStatus).
  456. //
  457. BOOL CMIDI :: GetTrackEvent(TRACK * ptsTrack, TEMPEVENT * pteTemp) {
  458. DWORD   idx;
  459. UINT    dwEventLength;
  460. // Clear out the temporary event structure to get rid of old data...
  461. memset( pteTemp, 0, sizeof(TEMPEVENT));
  462. // Already at end of track? There's nothing to read.
  463.     if( ptsTrack->fdwTrack & ITS_F_ENDOFTRK )
  464. return FALSE;
  465. // Get the first byte, which determines the type of event.
  466. BYTE byByte;
  467. if( !GetTrackByte(ptsTrack, &byByte) )
  468. return FALSE;
  469. // If the high bit is not set, then this is a channel message
  470. // which uses the status byte from the last channel message
  471. // we saw. NOTE: We do not clear running status across SysEx or
  472. // meta events even though the spec says to because there are
  473. // actually files out there which contain that sequence of data.
  474. if( !(byByte & 0x80) ) {
  475. // No previous status byte? We're hosed.
  476. if( !ptsTrack->byRunningStatus ) {
  477. TrackError(ptsTrack, gteBadRunStat);
  478. return FALSE;
  479. }
  480. pteTemp->byShortData[0] = ptsTrack->byRunningStatus;
  481. pteTemp->byShortData[1] = byByte;
  482. byByte = pteTemp->byShortData[0] & 0xF0;
  483. pteTemp->dwEventLength = 2;
  484. // Only program change and channel pressure events are 2 bytes long;
  485. // the rest are 3 and need another byte
  486. if(( byByte != MIDI_PRGMCHANGE ) && ( byByte != MIDI_CHANPRESS )) {
  487. if( !GetTrackByte( ptsTrack, &pteTemp->byShortData[2] ))
  488. return FALSE;
  489. ++pteTemp->dwEventLength;
  490. }
  491. } else if(( byByte & 0xF0 ) != MIDI_SYSEX ) {
  492. // Not running status, not in SysEx range - must be
  493. // normal channel message (0x80-0xEF)
  494. pteTemp->byShortData[0] = byByte;
  495. ptsTrack->byRunningStatus = byByte;
  496. // Strip off channel and just keep message type
  497. byByte &= 0xF0;
  498. dwEventLength = ( byByte == MIDI_PRGMCHANGE || byByte == MIDI_CHANPRESS ) ? 1 : 2;
  499. pteTemp->dwEventLength = dwEventLength + 1;
  500. if( !GetTrackByte( ptsTrack, &pteTemp->byShortData[1] ))
  501. return FALSE;
  502. if( dwEventLength == 2 )
  503. if( !GetTrackByte( ptsTrack, &pteTemp->byShortData[2] ))
  504. return FALSE;
  505. } else if(( byByte == MIDI_SYSEX ) || ( byByte == MIDI_SYSEXEND )) {
  506. // One of the SysEx types. (They are the same as far as we're concerned;
  507. // there is only a semantic difference in how the data would actually
  508. // get sent when the file is played. We must take care to put the proper
  509. // event type back on the output track, however.)
  510. //
  511. // Parse the general format of:
  512. //  BYTE  bEvent (MIDI_SYSEX or MIDI_SYSEXEND)
  513. //  VDWORD  cbParms
  514. //  BYTE    abParms[cbParms]
  515. pteTemp->byShortData[0] = byByte;
  516. if( !GetTrackVDWord( ptsTrack, &pteTemp->dwEventLength )) {
  517. TrackError( ptsTrack, gteSysExLenTrunc );
  518. return FALSE;
  519. }
  520. // Malloc a temporary memory block to hold the parameter data
  521. pteTemp->pLongData = new BYTE [pteTemp->dwEventLength];
  522. if( pteTemp->pLongData == 0 ) {
  523. TrackError( ptsTrack, gteNoMem );
  524. return FALSE;
  525. }
  526. // Increment our counter, which tells the program to look around for
  527. // a malloc block to free, should it need to exit or reset before the
  528. // block would normally be freed
  529. ++m_dwMallocBlocks;
  530. // Copy from the input buffer to the parameter data buffer
  531. for( idx = 0; idx < pteTemp->dwEventLength; idx++ )
  532. if( !GetTrackByte( ptsTrack, pteTemp->pLongData + idx )) {
  533. TrackError( ptsTrack, gteSysExTrunc );
  534. return FALSE;
  535. }
  536. } else if( byByte == MIDI_META ) {
  537. // It's a meta event. Parse the general form:
  538. //  BYTE bEvent (MIDI_META)
  539. //  BYTE bClass
  540. //  VDWORD cbParms
  541. //  BYTE abParms[cbParms]
  542. pteTemp->byShortData[0] = byByte;
  543. if( !GetTrackByte( ptsTrack, &pteTemp->byShortData[1] ))
  544. return FALSE;
  545. if( !GetTrackVDWord( ptsTrack, &pteTemp->dwEventLength )) {
  546. TrackError( ptsTrack, gteMetaLenTrunc );
  547. return FALSE;
  548. }
  549. // NOTE: It's perfectly valid to have a meta with no data
  550. // In this case, dwEventLength == 0 and pLongData == NULL
  551. if( pteTemp->dwEventLength ) {
  552. // Malloc a temporary memory block to hold the parameter data
  553. pteTemp->pLongData = new BYTE [pteTemp->dwEventLength];
  554. if( pteTemp->pLongData == 0 ) {
  555. TrackError( ptsTrack, gteNoMem );
  556. return FALSE;
  557. }
  558. // Increment our counter, which tells the program to look around for
  559. // a malloc block to free, should it need to exit or reset before the
  560. // block would normally be freed
  561. ++m_dwMallocBlocks;
  562. // Copy from the input buffer to the parameter data buffer
  563. for( idx = 0; idx < pteTemp->dwEventLength; idx++ )
  564. if( !GetTrackByte( ptsTrack, pteTemp->pLongData + idx )) {
  565. TrackError( ptsTrack, gteMetaTrunc );
  566. return FALSE;
  567. }
  568. }
  569. if( pteTemp->byShortData[1] == MIDI_META_EOT )
  570. ptsTrack->fdwTrack |= ITS_F_ENDOFTRK;
  571. } else {
  572. // Messages in this range are system messages and aren't supposed to
  573. // be in a normal MIDI file. If they are, we've either misparsed or the
  574. // authoring software is stupid.
  575. return FALSE;
  576. }
  577. // Event time was already stored as the current track time
  578. pteTemp->tkEvent = ptsTrack->tkNextEventDue;
  579. // Now update to the next event time. The code above MUST properly
  580. // maintain the end of track flag in case the end of track meta is
  581. // missing.  NOTE: This code is a continuation of the track event
  582. // time pre-read which is done at the end of track initialization.
  583. if( !( ptsTrack->fdwTrack & ITS_F_ENDOFTRK )) {
  584. DWORD tkDelta;
  585. if( !GetTrackVDWord( ptsTrack, &tkDelta ))
  586. return FALSE;
  587. ptsTrack->tkNextEventDue += tkDelta;
  588. }
  589. return TRUE;
  590. }
  591. // GetTrackVDWord
  592. //
  593. // Attempts to parse a variable length DWORD from the given track. A VDWord
  594. // in a MIDI file
  595. //  (a) is in lo-hi format 
  596. //  (b) has the high bit set on every byte except the last
  597. //
  598. // Returns the DWORD in *lpdw and TRUE on success; else
  599. // FALSE if we hit end of track first.
  600. BOOL CMIDI :: GetTrackVDWord(TRACK * ptsTrack, LPDWORD lpdw) {
  601. ASSERT(ptsTrack != 0);
  602. ASSERT(lpdw != 0);
  603. if( ptsTrack->fdwTrack & ITS_F_ENDOFTRK )
  604. return FALSE;
  605. BYTE byByte;
  606. DWORD dw = 0;
  607. do {
  608. if( !GetTrackByte( ptsTrack, &byByte ))
  609. return FALSE;
  610. dw = ( dw << 7 ) | ( byByte & 0x7F );
  611. } while( byByte & 0x80 );
  612. *lpdw = dw;
  613. return TRUE;
  614. }
  615. // AddEventToStreamBuffer
  616. //
  617. // Put the given event into the given stream buffer at the given location
  618. // pteTemp must point to an event filled out in accordance with the
  619. // description given in GetTrackEvent
  620. //
  621. // Handles its own error notification by displaying to the appropriate
  622. // output device (either our debugging window, or the screen).
  623. int CMIDI :: AddEventToStreamBuffer( TEMPEVENT * pteTemp, CONVERTINFO *lpciInfo ) {
  624. MIDIEVENT * pmeEvent = (MIDIEVENT *)( lpciInfo->mhBuffer.lpData
  625. + lpciInfo->dwStartOffset
  626. + lpciInfo->dwBytesRecorded );
  627. // When we see a new, empty buffer, set the start time on it...
  628. if( !lpciInfo->dwBytesRecorded )
  629. lpciInfo->tkStart = m_tkCurrentTime;
  630. // Use the above set start time to figure out how much longer we should fill
  631. // this buffer before officially declaring it as "full"
  632. if( m_tkCurrentTime - lpciInfo->tkStart > m_dwBufferTickLength )
  633. if( lpciInfo->bTimesUp ) {
  634. lpciInfo->bTimesUp = FALSE;
  635. return CONVERTERR_BUFFERFULL;
  636. } else
  637. lpciInfo->bTimesUp = TRUE;
  638. DWORD tkNow = m_tkCurrentTime;
  639. // Delta time is absolute event time minus absolute time
  640. // already gone by on this track
  641. DWORD tkDelta = pteTemp->tkEvent - m_tkCurrentTime;
  642. // Event time is now current time on this track
  643. m_tkCurrentTime = pteTemp->tkEvent;
  644. if( m_bInsertTempo ) {
  645. m_bInsertTempo = FALSE;
  646. if( lpciInfo->dwMaxLength-lpciInfo->dwBytesRecorded < 3*sizeof(DWORD)) {
  647. // Cleanup from our write operation
  648. return CONVERTERR_BUFFERFULL;
  649. }
  650. if( m_dwCurrentTempo ) {
  651. pmeEvent->dwDeltaTime = 0;
  652. pmeEvent->dwStreamID = 0;
  653. pmeEvent->dwEvent = ( m_dwCurrentTempo * 100 ) / m_dwTempoMultiplier;
  654. pmeEvent->dwEvent |= (((DWORD)MEVT_TEMPO ) << 24 ) | MEVT_F_SHORT;
  655. lpciInfo->dwBytesRecorded += 3 * sizeof(DWORD);
  656. pmeEvent += 3 * sizeof(DWORD);
  657. }
  658. }
  659. if( pteTemp->byShortData[0] < MIDI_SYSEX ) {
  660. // Channel message. We know how long it is, just copy it.
  661. // Need 3 DWORD's: delta-t, stream-ID, event
  662. if( lpciInfo->dwMaxLength-lpciInfo->dwBytesRecorded < 3*sizeof(DWORD)) {
  663. // Cleanup from our write operation
  664. return CONVERTERR_BUFFERFULL;
  665. }
  666. pmeEvent->dwDeltaTime = tkDelta;
  667. pmeEvent->dwStreamID = 0;
  668. pmeEvent->dwEvent = ( pteTemp->byShortData[0] )
  669. | (((DWORD)pteTemp->byShortData[1] ) << 8 )
  670. | (((DWORD)pteTemp->byShortData[2] ) << 16 )
  671. | MEVT_F_SHORT;
  672. if((( pteTemp->byShortData[0] & 0xF0) == MIDI_CTRLCHANGE ) && ( pteTemp->byShortData[1] == MIDICTRL_VOLUME )) {
  673. // If this is a volume change, generate a callback so we can grab
  674. // the new volume for our cache
  675. pmeEvent->dwEvent |= MEVT_F_CALLBACK;
  676. }
  677. lpciInfo->dwBytesRecorded += 3 *sizeof(DWORD);
  678. } else if(( pteTemp->byShortData[0] == MIDI_SYSEX ) || ( pteTemp->byShortData[0] == MIDI_SYSEXEND )) {
  679. TRACE0("AddEventToStreamBuffer: Ignoring SysEx event.n");
  680. if( m_dwMallocBlocks ) {
  681. delete [] pteTemp->pLongData;
  682. --m_dwMallocBlocks;
  683. }
  684. } else {
  685. // Better be a meta event.
  686. //  BYTE byEvent
  687. //  BYTE byEventType
  688. //  VDWORD dwEventLength
  689. //  BYTE pLongEventData[dwEventLength]
  690. ASSERT( pteTemp->byShortData[0] == MIDI_META );
  691. // The only meta-event we care about is change tempo
  692. if( pteTemp->byShortData[1] != MIDI_META_TEMPO ) {
  693. if( m_dwMallocBlocks ) {
  694. delete [] pteTemp->pLongData;
  695. --m_dwMallocBlocks;
  696. }
  697. return CONVERTERR_METASKIP;
  698. }
  699. // We should have three bytes of parameter data...
  700. ASSERT(pteTemp->dwEventLength == 3);
  701. // Need 3 DWORD's: delta-t, stream-ID, event data
  702. if( lpciInfo->dwMaxLength - lpciInfo->dwBytesRecorded < 3 *sizeof(DWORD)) {
  703. // Cleanup the temporary event if necessary and return
  704. if( m_dwMallocBlocks ) {
  705. delete [] pteTemp->pLongData;
  706. --m_dwMallocBlocks;
  707. }
  708. return CONVERTERR_BUFFERFULL;
  709. }
  710. pmeEvent->dwDeltaTime = tkDelta;
  711. pmeEvent->dwStreamID = 0;
  712. // Note: this is backwards from above because we're converting a single
  713. //  data value from hi-lo to lo-hi format...
  714. pmeEvent->dwEvent = ( pteTemp->pLongData[2] )
  715. | (((DWORD)pteTemp->pLongData[1] ) << 8 )
  716. | (((DWORD)pteTemp->pLongData[0] ) << 16 );
  717. // This next step has absolutely nothing to do with the conversion of a
  718. // MIDI file to a stream, it's simply put here to add the functionality
  719. // of the tempo slider. If you don't need this, be sure to remove the
  720. // next two lines.
  721. m_dwCurrentTempo = pmeEvent->dwEvent;
  722. pmeEvent->dwEvent = (pmeEvent->dwEvent * 100 ) / m_dwTempoMultiplier;
  723. pmeEvent->dwEvent |= (((DWORD)MEVT_TEMPO ) << 24 ) | MEVT_F_SHORT;
  724. m_dwBufferTickLength = (m_dwTimeDivision * 1000 * BUFFER_TIME_LENGTH) / m_dwCurrentTempo;
  725. TRACE1("m_dwBufferTickLength = %lun", m_dwBufferTickLength);
  726. if( m_dwMallocBlocks ) {
  727. delete [] pteTemp->pLongData;
  728. --m_dwMallocBlocks;
  729. }
  730. lpciInfo->dwBytesRecorded += 3 *sizeof(DWORD);
  731. }
  732. return CONVERTERR_NOERROR;
  733. }
  734. // StreamBufferSetup()
  735. //
  736. // Opens a MIDI stream. Then it goes about converting the data into a midiStream buffer for playback.
  737. BOOL CMIDI :: StreamBufferSetup() {
  738. int nChkErr;
  739. BOOL bFoundEnd = FALSE;
  740. MMRESULT mmrRetVal;
  741. if( !m_hStream )
  742. if(( mmrRetVal = midiStreamOpen( &m_hStream,
  743. &m_uMIDIDeviceID,
  744. DWORD(1), DWORD(MidiProc),
  745. DWORD(this),
  746. CALLBACK_FUNCTION )) != MMSYSERR_NOERROR ) {
  747. MidiError(mmrRetVal);
  748. return FALSE;
  749. }
  750. // allocate stream buffers and initialise them
  751. m_StreamBuffers.resize(NUM_STREAM_BUFFERS);
  752. MIDIPROPTIMEDIV mptd;
  753. mptd.cbStruct = sizeof(mptd);
  754. mptd.dwTimeDiv = m_dwTimeDivision;
  755. if(( mmrRetVal = midiStreamProperty( m_hStream, (LPBYTE)&mptd,
  756.     MIDIPROP_SET | MIDIPROP_TIMEDIV )) != MMSYSERR_NOERROR ) {
  757. MidiError( mmrRetVal );
  758. return FALSE;
  759. }
  760. m_nEmptyBuffers = 0;
  761. DWORD dwConvertFlag = CONVERTF_RESET;
  762. for( m_nCurrentBuffer = 0; m_nCurrentBuffer < NUM_STREAM_BUFFERS; m_nCurrentBuffer++ ) {
  763. m_StreamBuffers[m_nCurrentBuffer].mhBuffer.dwBufferLength = OUT_BUFFER_SIZE;
  764. m_StreamBuffers[m_nCurrentBuffer].mhBuffer.lpData = new char [OUT_BUFFER_SIZE];
  765. if( m_StreamBuffers[m_nCurrentBuffer].mhBuffer.lpData == 0 )
  766. return FALSE;
  767. // Tell the converter to convert up to one entire buffer's length of output
  768. // data. Also, set a flag so it knows to reset any saved state variables it
  769. // may keep from call to call.
  770. m_StreamBuffers[m_nCurrentBuffer].dwStartOffset = 0;
  771. m_StreamBuffers[m_nCurrentBuffer].dwMaxLength = OUT_BUFFER_SIZE;
  772. m_StreamBuffers[m_nCurrentBuffer].tkStart = 0;
  773. m_StreamBuffers[m_nCurrentBuffer].bTimesUp = FALSE;
  774. if(( nChkErr = ConvertToBuffer( dwConvertFlag, &m_StreamBuffers[m_nCurrentBuffer] )) != CONVERTERR_NOERROR ) {
  775. if( nChkErr == CONVERTERR_DONE ) {
  776. bFoundEnd = TRUE;
  777. } else {
  778. TRACE0("Initial conversion pass failedn");
  779. return FALSE;
  780. }
  781. }
  782. m_StreamBuffers[m_nCurrentBuffer].mhBuffer.dwBytesRecorded = m_StreamBuffers[m_nCurrentBuffer].dwBytesRecorded;
  783. if( !m_bBuffersPrepared )
  784. if(( mmrRetVal = midiOutPrepareHeader( (HMIDIOUT)m_hStream,
  785. &m_StreamBuffers[m_nCurrentBuffer].mhBuffer,
  786. sizeof(MIDIHDR))) != MMSYSERR_NOERROR ) {
  787. MidiError( mmrRetVal );
  788. return FALSE;
  789. }
  790. if(( mmrRetVal = midiStreamOut( m_hStream,
  791. &m_StreamBuffers[m_nCurrentBuffer].mhBuffer,
  792. sizeof(MIDIHDR))) != MMSYSERR_NOERROR ) {
  793. MidiError(mmrRetVal);
  794. break;
  795. }
  796. dwConvertFlag = 0;
  797. if( bFoundEnd )
  798. break;
  799. }
  800. m_bBuffersPrepared = TRUE;
  801. m_nCurrentBuffer = 0;
  802. return TRUE;
  803. }
  804. // This function unprepares and frees all our buffers -- something we must
  805. // do to work around a bug in MMYSYSTEM that prevents a device from playing
  806. // back properly unless it is closed and reopened after each stop.
  807. void CMIDI :: FreeBuffers() {
  808. DWORD idx;
  809. MMRESULT mmrRetVal;
  810. if( m_bBuffersPrepared ) {
  811. for( idx = 0; idx < NUM_STREAM_BUFFERS; idx++ )
  812. if(( mmrRetVal = midiOutUnprepareHeader( (HMIDIOUT)m_hStream,
  813. &m_StreamBuffers[idx].mhBuffer,
  814. sizeof(MIDIHDR))) != MMSYSERR_NOERROR ) {
  815. MidiError(mmrRetVal);
  816. }
  817. m_bBuffersPrepared = FALSE;
  818. }
  819. // Free our stream buffers...
  820. for( idx = 0; idx < NUM_STREAM_BUFFERS; idx++ )
  821. if( m_StreamBuffers[idx].mhBuffer.lpData ) {
  822. delete [] m_StreamBuffers[idx].mhBuffer.lpData;
  823. m_StreamBuffers[idx].mhBuffer.lpData = 0;
  824. }
  825. }
  826. //////////////////////////////////////////////////////////////////////
  827. // CMIDI -- error handling
  828. //////////////////////////////////////////////////////////////////////
  829. void CMIDI :: MidiError(MMRESULT mmResult) {
  830. #ifdef _DEBUG
  831. char chText[512];
  832. midiOutGetErrorText(mmResult, chText, sizeof(chText));
  833. TRACE1("Midi error: %hsn", chText);
  834. #endif
  835. }
  836. void CMIDI :: TrackError(TRACK * ptsTrack, LPSTR lpszErr ) {
  837. TRACE1("Track buffer offset %lun", (DWORD)(ptsTrack->pTrackCurrent - ptsTrack->pTrackStart));
  838.     TRACE1("Track total length %lun", ptsTrack->dwTrackLength);
  839. TRACE1("%hsn", lpszErr);
  840. }
  841. //////////////////////////////////////////////////////////////////////
  842. // CMIDI -- overridables
  843. //////////////////////////////////////////////////////////////////////
  844. void CMIDI :: OnMidiOutOpen() {
  845. }
  846. void CMIDI :: OnMidiOutDone(MIDIHDR & rHdr) {
  847. if( m_uCallbackStatus == STATUS_CALLBACKDEAD )
  848. return;
  849. ++m_nEmptyBuffers;
  850. if( m_uCallbackStatus == STATUS_WAITINGFOREND ) {
  851. if( m_nEmptyBuffers < NUM_STREAM_BUFFERS )
  852. return;
  853. else {
  854. m_uCallbackStatus = STATUS_CALLBACKDEAD;
  855. Stop();
  856. SetEvent(m_hBufferReturnEvent);
  857. return;
  858. }
  859. }
  860. // This flag is set whenever the callback is waiting for all buffers to
  861. // come back.
  862. if( m_uCallbackStatus == STATUS_KILLCALLBACK ) {
  863. // Count NUM_STREAM_BUFFERS-1 being returned for the last time
  864. if( m_nEmptyBuffers < NUM_STREAM_BUFFERS )
  865. return;
  866. else {
  867. // Change the status to callback dead
  868. m_uCallbackStatus = STATUS_CALLBACKDEAD;
  869. SetEvent(m_hBufferReturnEvent);
  870. return;
  871. }
  872. }
  873. m_dwProgressBytes += m_StreamBuffers[m_nCurrentBuffer].mhBuffer.dwBytesRecorded;
  874. ///////////////////////////////////////////////////////////////////////////////
  875. // Fill an available buffer with audio data again...
  876. if( m_bPlaying && m_nEmptyBuffers ) {
  877. m_StreamBuffers[m_nCurrentBuffer].dwStartOffset = 0;
  878. m_StreamBuffers[m_nCurrentBuffer].dwMaxLength = OUT_BUFFER_SIZE;
  879. m_StreamBuffers[m_nCurrentBuffer].tkStart = 0;
  880. m_StreamBuffers[m_nCurrentBuffer].dwBytesRecorded = 0;
  881. m_StreamBuffers[m_nCurrentBuffer].bTimesUp = FALSE;
  882. int nChkErr;
  883. if(( nChkErr = ConvertToBuffer( 0, &m_StreamBuffers[m_nCurrentBuffer] )) != CONVERTERR_NOERROR ) {
  884. if( nChkErr == CONVERTERR_DONE ) {
  885. m_uCallbackStatus = STATUS_WAITINGFOREND;
  886. return;
  887. } else {
  888. TRACE0("MidiProc() conversion pass failed!n");
  889. return;
  890. }
  891. }
  892. m_StreamBuffers[m_nCurrentBuffer].mhBuffer.dwBytesRecorded = m_StreamBuffers[m_nCurrentBuffer].dwBytesRecorded;
  893. MMRESULT mmrRetVal;
  894. if( (mmrRetVal = midiStreamOut(m_hStream, &m_StreamBuffers[m_nCurrentBuffer].mhBuffer, sizeof(MIDIHDR))) != MMSYSERR_NOERROR ) {
  895. MidiError(mmrRetVal);
  896. return;
  897. }
  898. m_nCurrentBuffer = ( m_nCurrentBuffer + 1 ) % NUM_STREAM_BUFFERS;
  899. m_nEmptyBuffers--;
  900. }
  901. }
  902. void CMIDI :: OnMidiOutPositionCB(MIDIHDR & rHdr, MIDIEVENT & rEvent) {
  903. if( MIDIEVENT_TYPE(rEvent.dwEvent) == MIDI_CTRLCHANGE )
  904. {
  905. if( MIDIEVENT_DATA1(rEvent.dwEvent) == MIDICTRL_VOLUME ) {
  906. // Mask off the channel number and cache the volume data byte
  907. m_Volumes[MIDIEVENT_CHANNEL(rEvent.dwEvent)] = DWORD(MIDIEVENT_VOLUME(rEvent.dwEvent)*100/VOLUME_MAX);
  908. if( m_pWndParent && ::IsWindow(m_pWndParent->GetSafeHwnd()) )
  909. // Do not use SendMessage(), because a change of the midi stream has no effect
  910. // during callback handling, so if the owner wants to adjust the volume, as a
  911. // result of the windows message, (s)he will not hear that change.
  912. m_pWndParent->PostMessage(
  913. WM_MIDI_VOLUMECHANGED,
  914. WPARAM(this),
  915. LPARAM(
  916. MAKELONG(
  917. WORD(MIDIEVENT_CHANNEL(rEvent.dwEvent)),
  918. WORD(MIDIEVENT_VOLUME(rEvent.dwEvent)*100/VOLUME_MAX)
  919. )
  920. )
  921. );
  922. }
  923. }
  924. }
  925. void CMIDI :: OnMidiOutClose() {
  926. }
  927. //////////////////////////////////////////////////////////////////////
  928. // CMIDI -- static members
  929. //////////////////////////////////////////////////////////////////////
  930. void CMIDI :: MidiProc(HMIDIOUT hMidi, UINT uMsg, DWORD dwInstanceData, DWORD dwParam1, DWORD dwParam2) {
  931. CMIDI * pMidi = (CMIDI *) dwInstanceData;
  932. ASSERT(pMidi != 0);
  933. MIDIHDR * pHdr = (MIDIHDR*) dwParam1;
  934. switch(uMsg) {
  935. case MOM_OPEN:
  936. pMidi->OnMidiOutOpen();
  937. break;
  938. case MOM_CLOSE:
  939. pMidi->OnMidiOutClose();
  940. break;
  941. case MOM_DONE:
  942. ASSERT(pHdr != 0);
  943. pMidi->OnMidiOutDone(*pHdr);
  944. break;
  945. case MOM_POSITIONCB:
  946. ASSERT(pHdr != 0);
  947. pMidi->OnMidiOutPositionCB(*pHdr, *((MIDIEVENT*)(pHdr->lpData + pHdr->dwOffset)));
  948. break;
  949. default:
  950. break;
  951. }
  952. }