Audio plugin host https://kx.studio/carla
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.

353 lines
12KB

  1. /**********************************************************************/
  2. /*! \class MidiFileIn
  3. \brief A standard MIDI file reading/parsing class.
  4. This class can be used to read events from a standard MIDI file.
  5. Event bytes are copied to a C++ vector and must be subsequently
  6. interpreted by the user. The function getNextMidiEvent() skips
  7. meta and sysex events, returning only MIDI channel messages.
  8. Event delta-times are returned in the form of "ticks" and a
  9. function is provided to determine the current "seconds per tick".
  10. Tempo changes are internally tracked by the class and reflected in
  11. the values returned by the function getTickSeconds().
  12. by Gary P. Scavone, 2003 - 2010.
  13. */
  14. /**********************************************************************/
  15. #include "MidiFileIn.h"
  16. #include <cstring>
  17. #include <iostream>
  18. namespace stk {
  19. MidiFileIn :: MidiFileIn( std::string fileName )
  20. {
  21. // Attempt to open the file.
  22. file_.open( fileName.c_str(), std::ios::in | std::ios::binary );
  23. if ( !file_ ) {
  24. oStream_ << "MidiFileIn: error opening or finding file (" << fileName << ").";
  25. handleError( StkError::FILE_NOT_FOUND );
  26. }
  27. // Parse header info.
  28. char chunkType[4];
  29. char buffer[4];
  30. SINT32 *length;
  31. if ( !file_.read( chunkType, 4 ) ) goto error;
  32. if ( !file_.read( buffer, 4 ) ) goto error;
  33. #ifdef __LITTLE_ENDIAN__
  34. swap32((unsigned char *)&buffer);
  35. #endif
  36. length = (SINT32 *) &buffer;
  37. if ( strncmp( chunkType, "MThd", 4 ) || ( *length != 6 ) ) {
  38. oStream_ << "MidiFileIn: file (" << fileName << ") does not appear to be a MIDI file!";
  39. handleError( StkError::FILE_UNKNOWN_FORMAT );
  40. }
  41. // Read the MIDI file format.
  42. SINT16 *data;
  43. if ( !file_.read( buffer, 2 ) ) goto error;
  44. #ifdef __LITTLE_ENDIAN__
  45. swap16((unsigned char *)&buffer);
  46. #endif
  47. data = (SINT16 *) &buffer;
  48. if ( *data < 0 || *data > 2 ) {
  49. oStream_ << "MidiFileIn: the file (" << fileName << ") format is invalid!";
  50. handleError( StkError::FILE_ERROR );
  51. }
  52. format_ = *data;
  53. // Read the number of tracks.
  54. if ( !file_.read( buffer, 2 ) ) goto error;
  55. #ifdef __LITTLE_ENDIAN__
  56. swap16((unsigned char *)&buffer);
  57. #endif
  58. if ( format_ == 0 && *data != 1 ) {
  59. oStream_ << "MidiFileIn: invalid number of tracks (>1) for a file format = 0!";
  60. handleError( StkError::FILE_ERROR );
  61. }
  62. nTracks_ = *data;
  63. // Read the beat division.
  64. if ( !file_.read( buffer, 2 ) ) goto error;
  65. #ifdef __LITTLE_ENDIAN__
  66. swap16((unsigned char *)&buffer);
  67. #endif
  68. division_ = (int) *data;
  69. double tickrate;
  70. usingTimeCode_ = false;
  71. if ( *data & 0x8000 ) {
  72. // Determine ticks per second from time-code formats.
  73. tickrate = (double) -(*data & 0x7F00);
  74. // If frames per second value is 29, it really should be 29.97.
  75. if ( tickrate == 29.0 ) tickrate = 29.97;
  76. tickrate *= (*data & 0x00FF);
  77. usingTimeCode_ = true;
  78. }
  79. else {
  80. tickrate = (double) (*data & 0x7FFF); // ticks per quarter note
  81. }
  82. // Now locate the track offsets and lengths. If not using time
  83. // code, we can initialize the "tick time" using a default tempo of
  84. // 120 beats per minute. We will then check for tempo meta-events
  85. // afterward.
  86. unsigned int i;
  87. for ( i=0; i<nTracks_; i++ ) {
  88. if ( !file_.read( chunkType, 4 ) ) goto error;
  89. if ( strncmp( chunkType, "MTrk", 4 ) ) goto error;
  90. if ( !file_.read( buffer, 4 ) ) goto error;
  91. #ifdef __LITTLE_ENDIAN__
  92. swap32((unsigned char *)&buffer);
  93. #endif
  94. length = (SINT32 *) &buffer;
  95. trackLengths_.push_back( *length );
  96. trackOffsets_.push_back( (long) file_.tellg() );
  97. trackPointers_.push_back( (long) file_.tellg() );
  98. trackStatus_.push_back( 0 );
  99. file_.seekg( *length, std::ios_base::cur );
  100. if ( usingTimeCode_ ) tickSeconds_.push_back( (double) (1.0 / tickrate) );
  101. else tickSeconds_.push_back( (double) (0.5 / tickrate) );
  102. }
  103. // Save the initial tickSeconds parameter.
  104. TempoChange tempoEvent;
  105. tempoEvent.count = 0;
  106. tempoEvent.tickSeconds = tickSeconds_[0];
  107. tempoEvents_.push_back( tempoEvent );
  108. // If format 1 and not using time code, parse and save the tempo map
  109. // on track 0.
  110. if ( format_ == 1 && !usingTimeCode_ ) {
  111. std::vector<unsigned char> event;
  112. unsigned long value, count;
  113. // We need to temporarily change the usingTimeCode_ value here so
  114. // that the getNextEvent() function doesn't try to check the tempo
  115. // map (which we're creating here).
  116. usingTimeCode_ = true;
  117. count = getNextEvent( &event, 0 );
  118. while ( event.size() ) {
  119. if ( ( event.size() == 6 ) && ( event[0] == 0xff ) &&
  120. ( event[1] == 0x51 ) && ( event[2] == 0x03 ) ) {
  121. tempoEvent.count = count;
  122. value = ( event[3] << 16 ) + ( event[4] << 8 ) + event[5];
  123. tempoEvent.tickSeconds = (double) (0.000001 * value / tickrate);
  124. if ( count > tempoEvents_.back().count )
  125. tempoEvents_.push_back( tempoEvent );
  126. else
  127. tempoEvents_.back() = tempoEvent;
  128. }
  129. count += getNextEvent( &event, 0 );
  130. }
  131. rewindTrack( 0 );
  132. for ( unsigned int i=0; i<nTracks_; i++ ) {
  133. trackCounters_.push_back( 0 );
  134. trackTempoIndex_.push_back( 0 );
  135. }
  136. // Change the time code flag back!
  137. usingTimeCode_ = false;
  138. }
  139. return;
  140. error:
  141. oStream_ << "MidiFileIn: error reading from file (" << fileName << ").";
  142. handleError( StkError::FILE_ERROR );
  143. }
  144. MidiFileIn :: ~MidiFileIn()
  145. {
  146. // An ifstream object implicitly closes itself during destruction
  147. // but we'll make an explicit call to "close" anyway.
  148. file_.close();
  149. }
  150. void MidiFileIn :: rewindTrack( unsigned int track )
  151. {
  152. if ( track >= nTracks_ ) {
  153. oStream_ << "MidiFileIn::getNextEvent: invalid track argument (" << track << ").";
  154. handleError( StkError::WARNING ); return;
  155. }
  156. trackPointers_[track] = trackOffsets_[track];
  157. trackStatus_[track] = 0;
  158. tickSeconds_[track] = tempoEvents_[0].tickSeconds;
  159. }
  160. double MidiFileIn :: getTickSeconds( unsigned int track )
  161. {
  162. // Return the current tick value in seconds for the given track.
  163. if ( track >= nTracks_ ) {
  164. oStream_ << "MidiFileIn::getTickSeconds: invalid track argument (" << track << ").";
  165. handleError( StkError::WARNING ); return 0.0;
  166. }
  167. return tickSeconds_[track];
  168. }
  169. unsigned long MidiFileIn :: getNextEvent( std::vector<unsigned char> *event, unsigned int track )
  170. {
  171. // Fill the user-provided vector with the next event in the
  172. // specified track (default = 0) and return the event delta time in
  173. // ticks. This function assumes that the stored track pointer is
  174. // positioned at the start of a track event. If the track has
  175. // reached its end, the event vector size will be zero.
  176. //
  177. // If we have a format 0 or 2 file and we're not using timecode, we
  178. // should check every meta-event for tempo changes and make
  179. // appropriate updates to the tickSeconds_ parameter if so.
  180. //
  181. // If we have a format 1 file and we're not using timecode, keep a
  182. // running sum of ticks for each track and update the tickSeconds_
  183. // parameter as needed based on the stored tempo map.
  184. event->clear();
  185. if ( track >= nTracks_ ) {
  186. oStream_ << "MidiFileIn::getNextEvent: invalid track argument (" << track << ").";
  187. handleError( StkError::WARNING ); return 0;
  188. }
  189. // Check for the end of the track.
  190. if ( (trackPointers_[track] - trackOffsets_[track]) >= trackLengths_[track] )
  191. return 0;
  192. unsigned long ticks = 0, bytes = 0;
  193. bool isTempoEvent = false;
  194. // Read the event delta time.
  195. file_.seekg( trackPointers_[track], std::ios_base::beg );
  196. if ( !readVariableLength( &ticks ) ) goto error;
  197. // Parse the event stream to determine the event length.
  198. unsigned char c;
  199. if ( !file_.read( (char *)&c, 1 ) ) goto error;
  200. switch ( c ) {
  201. case 0xFF: // A Meta-Event
  202. unsigned long position;
  203. trackStatus_[track] = 0;
  204. event->push_back( c );
  205. if ( !file_.read( (char *)&c, 1 ) ) goto error;
  206. event->push_back( c );
  207. if ( format_ != 1 && ( c == 0x51 ) ) isTempoEvent = true;
  208. position = file_.tellg();
  209. if ( !readVariableLength( &bytes ) ) goto error;
  210. bytes += ( (unsigned long)file_.tellg() - position );
  211. file_.seekg( position, std::ios_base::beg );
  212. break;
  213. case 0xF0:
  214. case 0xF7: // The start or continuation of a Sysex event
  215. trackStatus_[track] = 0;
  216. event->push_back( c );
  217. position = file_.tellg();
  218. if ( !readVariableLength( &bytes ) ) goto error;
  219. bytes += ( (unsigned long)file_.tellg() - position );
  220. file_.seekg( position, std::ios_base::beg );
  221. break;
  222. default: // Should be a MIDI channel event
  223. if ( c & 0x80 ) { // MIDI status byte
  224. if ( c > 0xF0 ) goto error;
  225. trackStatus_[track] = c;
  226. event->push_back( c );
  227. c &= 0xF0;
  228. if ( (c == 0xC0) || (c == 0xD0) ) bytes = 1;
  229. else bytes = 2;
  230. }
  231. else if ( trackStatus_[track] & 0x80 ) { // Running status
  232. event->push_back( trackStatus_[track] );
  233. event->push_back( c );
  234. c = trackStatus_[track] & 0xF0;
  235. if ( (c != 0xC0) && (c != 0xD0) ) bytes = 1;
  236. }
  237. else goto error;
  238. }
  239. // Read the rest of the event into the event vector.
  240. unsigned long i;
  241. for ( i=0; i<bytes; i++ ) {
  242. if ( !file_.read( (char *)&c, 1 ) ) goto error;
  243. event->push_back( c );
  244. }
  245. if ( !usingTimeCode_ ) {
  246. if ( isTempoEvent ) {
  247. // Parse the tempo event and update tickSeconds_[track].
  248. double tickrate = (double) (division_ & 0x7FFF);
  249. unsigned long value = ( event->at(3) << 16 ) + ( event->at(4) << 8 ) + event->at(5);
  250. tickSeconds_[track] = (double) (0.000001 * value / tickrate);
  251. }
  252. if ( format_ == 1 ) {
  253. // Update track counter and check the tempo map.
  254. trackCounters_[track] += ticks;
  255. TempoChange tempoEvent = tempoEvents_[ trackTempoIndex_[track] ];
  256. if ( trackCounters_[track] >= tempoEvent.count && trackTempoIndex_[track] < tempoEvents_.size() - 1 ) {
  257. trackTempoIndex_[track]++;
  258. tickSeconds_[track] = tempoEvent.tickSeconds;
  259. }
  260. }
  261. }
  262. // Save the current track pointer value.
  263. trackPointers_[track] = file_.tellg();
  264. return ticks;
  265. error:
  266. oStream_ << "MidiFileIn::getNextEvent: file read error!";
  267. handleError( StkError::FILE_ERROR );
  268. return 0;
  269. }
  270. unsigned long MidiFileIn :: getNextMidiEvent( std::vector<unsigned char> *midiEvent, unsigned int track )
  271. {
  272. // Fill the user-provided vector with the next MIDI event in the
  273. // specified track (default = 0) and return the event delta time in
  274. // ticks. Meta-Events preceeding this event are skipped and ignored.
  275. if ( track >= nTracks_ ) {
  276. oStream_ << "MidiFileIn::getNextMidiEvent: invalid track argument (" << track << ").";
  277. handleError( StkError::WARNING ); return 0;
  278. }
  279. unsigned long ticks = getNextEvent( midiEvent, track );
  280. while ( midiEvent->size() && ( midiEvent->at(0) >= 0xF0 ) ) {
  281. //for ( unsigned int i=0; i<midiEvent->size(); i++ )
  282. //std::cout << "event byte = " << i << ", value = " << (int)midiEvent->at(i) << std::endl;
  283. ticks = getNextEvent( midiEvent, track );
  284. }
  285. //for ( unsigned int i=0; i<midiEvent->size(); i++ )
  286. //std::cout << "event byte = " << i << ", value = " << (int)midiEvent->at(i) << std::endl;
  287. return ticks;
  288. }
  289. bool MidiFileIn :: readVariableLength( unsigned long *value )
  290. {
  291. // It is assumed that this function is called with the file read
  292. // pointer positioned at the start of a variable-length value. The
  293. // function returns "true" if the value is successfully parsed and
  294. // "false" otherwise.
  295. *value = 0;
  296. char c;
  297. if ( !file_.read( &c, 1 ) ) return false;
  298. *value = (unsigned long) c;
  299. if ( *value & 0x80 ) {
  300. *value &= 0x7f;
  301. do {
  302. if ( !file_.read( &c, 1 ) ) return false;
  303. *value = ( *value << 7 ) + ( c & 0x7f );
  304. } while ( c & 0x80 );
  305. }
  306. return true;
  307. }
  308. } // stk namespace