libcm is a C development framework with an emphasis on audio signal processing applications.
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.

cmMidiFile.h 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. //| Copyright: (C) 2009-2020 Kevin Larke <contact AT larke DOT org>
  2. //| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
  3. #ifndef cmMidiFile_h
  4. #define cmMidiFile_h
  5. #ifdef __cplusplus
  6. extern "C" {
  7. #endif
  8. //( { file_desc:"MIDI file reader and writer." kw:[midi file]}
  9. // MIDI file timing:
  10. // Messages in the MIDI file are time tagged with a delta offset in 'ticks'
  11. // from the previous message in the same track.
  12. //
  13. // A 'tick' can be converted to microsends as follows:
  14. //
  15. // microsecond per tick = micros per quarter note / ticks per quarter note
  16. //
  17. // MpT = MpQN / TpQN
  18. //
  19. // TpQN is given as a constant in the MIDI file header.
  20. // MpQN is given as the value of the MIDI file tempo message.
  21. //
  22. // See cmMidiFileSeekUSecs() for an example of converting ticks to milliseconds.
  23. //
  24. // As part of the file reading process, the status byte of note-on messages
  25. // with velocity=0 are is changed to a note-off message. See _cmMidiFileReadChannelMsg().
  26. //)
  27. //(
  28. typedef cmHandle_t cmMidiFileH_t;
  29. typedef unsigned cmMfRC_t;
  30. typedef struct
  31. {
  32. cmMidiByte_t hr;
  33. cmMidiByte_t min;
  34. cmMidiByte_t sec;
  35. cmMidiByte_t frm;
  36. cmMidiByte_t sfr;
  37. } cmMidiSmpte_t;
  38. typedef struct
  39. {
  40. cmMidiByte_t num;
  41. cmMidiByte_t den;
  42. cmMidiByte_t metro;
  43. cmMidiByte_t th2s;
  44. } cmMidiTimeSig_t;
  45. typedef struct
  46. {
  47. cmMidiByte_t key;
  48. cmMidiByte_t scale;
  49. } cmMidiKeySig_t;
  50. struct cmMidiTrackMsg_str;
  51. typedef struct
  52. {
  53. cmMidiByte_t ch;
  54. cmMidiByte_t d0;
  55. cmMidiByte_t d1;
  56. unsigned durMicros; // note duration in microseconds (corrected for tempo changes)
  57. struct cmMidiTrackMsg_str* end; // note-off or pedal-up message
  58. } cmMidiChMsg_t;
  59. enum
  60. {
  61. kDropTrkMsgFl = 0x01
  62. };
  63. typedef struct cmMidiTrackMsg_str
  64. {
  65. unsigned flags; // see k???TrkMsgFl
  66. unsigned uid; // uid's are unique among all msg's in the file
  67. unsigned dtick; // delta ticks between events on this track (ticks between this event and the previous event on this track)
  68. unsigned long long atick; // global (all tracks interleaved) accumulated ticks
  69. unsigned long long amicro; // global (all tracks interleaved) accumulated microseconds adjusted for tempo changes
  70. cmMidiByte_t status; // ch msg's have the channel value removed (it is stored in u.chMsgPtr->ch)
  71. cmMidiByte_t metaId; //
  72. unsigned short trkIdx; //
  73. unsigned byteCnt; // length of data pointed to by u.voidPtr (or any other pointer in the union)
  74. struct cmMidiTrackMsg_str* link; // link to next record in this track
  75. union
  76. {
  77. cmMidiByte_t bVal;
  78. unsigned iVal;
  79. unsigned short sVal;
  80. const char* text;
  81. const void* voidPtr;
  82. const cmMidiSmpte_t* smptePtr;
  83. const cmMidiTimeSig_t* timeSigPtr;
  84. const cmMidiKeySig_t* keySigPtr;
  85. const cmMidiChMsg_t* chMsgPtr;
  86. const cmMidiByte_t* sysExPtr;
  87. } u;
  88. } cmMidiTrackMsg_t;
  89. #define cmMidiFileIsNoteOn(m) (cmMidiIsNoteOn((m)->status) && ((m)->u.chMsgPtr->d1>0))
  90. #define cmMidiFileIsNoteOff(m) (cmMidiIsNoteOff((m)->status,(m)->u.chMsgPtr->d1))
  91. #define cmMidiFileIsPedalUp(m) (cmMidiIsPedalUp( (m)->status, (m)->u.chMsgPtr->d0, (m)->u.chMsgPtr->d1) )
  92. #define cmMidiFileIsPedalDown(m) (cmMidiIsPedalDown( (m)->status, (m)->u.chMsgPtr->d0, (m)->u.chMsgPtr->d1) )
  93. #define cmMidiFileIsSustainPedalUp(m) (cmMidiIsSustainPedalUp( (m)->status,(m)->u.chMsgPtr->d0,(m)->u.chMsgPtr->d1))
  94. #define cmMidiFileIsSustainPedalDown(m) (cmMidiIsSustainPedalDown( (m)->status,(m)->u.chMsgPtr->d0,(m)->u.chMsgPtr->d1))
  95. #define cmMidiFileIsSostenutoPedalUp(m) (cmMidiIsSostenutoPedalUp( (m)->status,(m)->u.chMsgPtr->d0,(m)->u.chMsgPtr->d1))
  96. #define cmMidiFileIsSostenutoPedalDown(m) (cmMidiIsSostenutoPedalDown((m)->status,(m)->u.chMsgPtr->d0,(m)->u.chMsgPtr->d1))
  97. enum
  98. {
  99. kOkMfRC = cmOkRC, // 0
  100. kFileFailMfRC, // 1
  101. kNotAMidiFileMfRC, // 2
  102. kMemAllocFailMfRC, // 3
  103. kFileCorruptMfRC, // 4
  104. kMissingEoxMfRC, // 5
  105. kUnknownMetaIdMfRC, // 6
  106. kInvalidHandleMfRC, // 7
  107. kMissingNoteOffMfRC, // 8
  108. kInvalidStatusMfRC, // 9
  109. kSustainPedalMfRC, // 10
  110. kSostenutoPedalMfRC, // 11
  111. kLargeDeltaTickMfRC, // 12 (a large delta tick value was filtered)
  112. kUidNotFoundMfRC, // 13
  113. kUidNotANoteMsgMfRC, // 14
  114. kInvalidTrkIndexMfRC,// 15
  115. kSvgFailMfRC, // 16
  116. kMsgNotFoundMfRC, // 17
  117. kEventTerminationMfRC // 18
  118. };
  119. extern cmMidiFileH_t cmMidiFileNullHandle;
  120. cmMfRC_t cmMidiFileOpen( cmCtx_t* ctx, cmMidiFileH_t* h, const char* fn );
  121. cmMfRC_t cmMidiFileCreate( cmCtx_t* ctx, cmMidiFileH_t* hp, unsigned trkN, unsigned ticksPerQN );
  122. cmMfRC_t cmMidiFileClose( cmMidiFileH_t* hp );
  123. cmMfRC_t cmMidiFileWrite( cmMidiFileH_t h, const char* fn );
  124. bool cmMidiFileIsValid( cmMidiFileH_t h );
  125. // Returns track count or kInvalidCnt if 'h' is invalid.
  126. unsigned cmMidiFileTrackCount( cmMidiFileH_t h );
  127. // Return midi file format id (0,1,2) or kInvalidId if 'h' is invalid.
  128. unsigned cmMidiFileType( cmMidiFileH_t h );
  129. // Returns ticks per quarter note or kInvalidMidiByte if 'h' is
  130. // invalid or 0 if file uses SMPTE ticks per frame time base.
  131. unsigned cmMidiFileTicksPerQN( cmMidiFileH_t h );
  132. // The file name used in an earlier call to midiFileOpen() or NULL if this
  133. // midi file did not originate from an actual file.
  134. const char* cmMidiFileName( cmMidiFileH_t h );
  135. // Returns SMPTE ticks per frame or kInvalidMidiByte if 'h' is
  136. // invalid or 0 if file uses ticks per quarter note time base.
  137. cmMidiByte_t cmMidiFileTicksPerSmpteFrame( cmMidiFileH_t h );
  138. // Returns SMPTE format or kInvalidMidiByte if 'h' is invalid or 0
  139. // if file uses ticks per quarter note time base.
  140. cmMidiByte_t cmMidiFileSmpteFormatId( cmMidiFileH_t h );
  141. // Returns count of records in track 'trackIdx' or kInvalidCnt if 'h' is invalid.
  142. unsigned cmMidiFileTrackMsgCount( cmMidiFileH_t h, unsigned trackIdx );
  143. // Returns base of record chain from track 'trackIdx' or NULL if 'h' is invalid.
  144. const cmMidiTrackMsg_t* cmMidiFileTrackMsg( cmMidiFileH_t h, unsigned trackIdx );
  145. // Returns the total count of records in the midi file and the
  146. // number in the array returned by cmMidiFileMsgArray().
  147. // Return kInvalidCnt if 'h' is invalid.
  148. unsigned cmMidiFileMsgCount( cmMidiFileH_t h );
  149. // Returns a pointer to the base of an array of pointers to all records
  150. // in the file sorted in ascending time order.
  151. // Returns NULL if 'h' is invalid.
  152. const cmMidiTrackMsg_t** cmMidiFileMsgArray( cmMidiFileH_t h );
  153. // Set the velocity of a note-on/off msg identified by 'uid'.
  154. cmMfRC_t cmMidiFileSetVelocity( cmMidiFileH_t h, unsigned uid, cmMidiByte_t vel );
  155. // Insert a MIDI message relative to the reference msg identified by 'uid'.
  156. // If dtick is positive/negative then the new msg is inserted after/before the reference msg.
  157. cmMfRC_t cmMidiFileInsertMsg( cmMidiFileH_t h, unsigned uid, int dtick, cmMidiByte_t ch, cmMidiByte_t status, cmMidiByte_t d0, cmMidiByte_t d1 );
  158. //
  159. // Insert a new cmMidiTrackMsg_t into the MIDI file on the specified track.
  160. //
  161. // Only the following fields need be set in 'msg'.
  162. // atick - used to position the msg in the track
  163. // status - this field is always set (Note that channel information must stripped from the status byte and included in the channel msg data)
  164. // metaId - this field is optional depending on the msg type
  165. // byteCnt - used to allocate storage for the data element in 'cmMidiTrackMsg_t.u'
  166. // u - the message data
  167. //
  168. cmMfRC_t cmMidiFileInsertTrackMsg( cmMidiFileH_t h, unsigned trkIdx, const cmMidiTrackMsg_t* msg );
  169. cmMfRC_t cmMidiFileInsertTrackChMsg( cmMidiFileH_t h, unsigned trkIdx, unsigned atick, cmMidiByte_t status, cmMidiByte_t d0, cmMidiByte_t d1 );
  170. cmMfRC_t cmMidFileInsertTrackTempoMsg( cmMidiFileH_t h, unsigned trkIdx, unsigned atick, unsigned bpm );
  171. // Return a pointer to the first msg at or after 'usecsOffs' or kInvalidIdx if no
  172. // msg exists after 'usecsOffs'. Note that 'usecOffs' is an offset from the beginning
  173. // of the file.
  174. // On return *'msgUsecsPtr' is set to the actual time of the msg.
  175. // (which will be equal to or greater than 'usecsOffs').
  176. unsigned cmMidiFileSeekUsecs( cmMidiFileH_t h, unsigned long long usecsOffs, unsigned* msgUsecsPtr, unsigned* newMicrosPerTickPtr );
  177. double cmMidiFileDurSecs( cmMidiFileH_t h );
  178. // Calculate Note Duration
  179. enum { kWarningsMfFl=0x01, kDropReattacksMfFl=0x02 };
  180. void cmMidiFileCalcNoteDurations( cmMidiFileH_t h, unsigned flags );
  181. // Set the delay prior to the first non-zero msg.
  182. void cmMidiFileSetDelay( cmMidiFileH_t h, unsigned ticks );
  183. // This function packs a track msg into a single consecutive
  184. // block of memory buf[ bufByteCnt ]. Call cmMidiFilePackTracMsgBufByteCount()
  185. // to get the required buffer length for any given cmMidiTrackMsg_t instance.
  186. cmMidiTrackMsg_t* cmMidiFilePackTrackMsg( const cmMidiTrackMsg_t* m, void* buf, unsigned bufByteCnt );
  187. unsigned cmMidiFilePackTrackMsgBufByteCount( const cmMidiTrackMsg_t* m );
  188. void cmMidiFilePrintMsgs( cmMidiFileH_t h, cmRpt_t* rpt );
  189. void cmMidiFilePrintTrack( cmMidiFileH_t h, unsigned trkIdx, cmRpt_t* rpt );
  190. typedef struct
  191. {
  192. unsigned uid;
  193. unsigned long long amicro;
  194. unsigned density;
  195. } cmMidiFileDensity_t;
  196. // Generate the note onset density measure for each note in the MIDI file.
  197. // Delete the returned memory with a call to cmMemFree().
  198. cmMidiFileDensity_t* cmMidiFileNoteDensity( cmMidiFileH_t h, unsigned* cntRef );
  199. // Generate a piano-roll plot description file which can be displayed with cmXScore.m
  200. cmMfRC_t cmMidiFileGenPlotFile( cmCtx_t* ctx, const cmChar_t* midiFn, const cmChar_t* outFn );
  201. cmMfRC_t cmMidiFileGenSvgFile( cmCtx_t* ctx, const cmChar_t* midiFn, const cmChar_t* outSvgFn, const cmChar_t* cssFn, bool standAloneFl, bool panZoomFl );
  202. // Generate a text file reportusing cmMIdiFilePrintMsgs()
  203. cmMfRC_t cmMidiFileReport( cmCtx_t* ctx, const cmChar_t* midiFn, const cmChar_t* outTextFn );
  204. void cmMidiFileTest( const char* fn, cmCtx_t* ctx );
  205. //)
  206. #ifdef __cplusplus
  207. }
  208. #endif
  209. #endif