|
@@ -5,168 +5,172 @@
|
5
|
5
|
extern "C" {
|
6
|
6
|
#endif
|
7
|
7
|
|
8
|
|
-// MIDI file timing:
|
9
|
|
-// Messages in the MIDI file are time tagged with a delta offset in 'ticks'
|
10
|
|
-// from the previous message in the same track.
|
11
|
|
-//
|
12
|
|
-// A 'tick' can be converted to microsends as follows:
|
13
|
|
-//
|
14
|
|
-// microsecond per tick = micros per quarter note / ticks per quarter note
|
15
|
|
-//
|
16
|
|
-// MpT = MpQN / TpQN
|
17
|
|
-//
|
18
|
|
-// TpQN is given as a constant in the MIDI file header.
|
19
|
|
-// MpQN is given as the value of the MIDI file tempo message.
|
20
|
|
-//
|
21
|
|
-// See cmMidiFileSeekUSecs() for an example of converting ticks to milliseconds.
|
22
|
|
-//
|
23
|
|
-// As part of the file reading process, the status byte of note-on messages
|
24
|
|
-// with velocity=0 are is changed to a note-off message. See _cmMidiFileReadChannelMsg().
|
25
|
|
-
|
26
|
|
-
|
27
|
|
-
|
28
|
|
-typedef cmHandle_t cmMidiFileH_t;
|
29
|
|
-typedef unsigned cmMfRC_t;
|
30
|
|
-
|
31
|
|
-typedef struct
|
32
|
|
-{
|
33
|
|
- cmMidiByte_t hr;
|
34
|
|
- cmMidiByte_t min;
|
35
|
|
- cmMidiByte_t sec;
|
36
|
|
- cmMidiByte_t frm;
|
37
|
|
- cmMidiByte_t sfr;
|
38
|
|
-} cmMidiSmpte_t;
|
39
|
|
-
|
40
|
|
-typedef struct
|
41
|
|
-{
|
42
|
|
- cmMidiByte_t num;
|
43
|
|
- cmMidiByte_t den;
|
44
|
|
- cmMidiByte_t metro;
|
45
|
|
- cmMidiByte_t th2s;
|
46
|
|
-} cmMidiTimeSig_t;
|
47
|
|
-
|
48
|
|
-typedef struct
|
49
|
|
-{
|
50
|
|
- cmMidiByte_t key;
|
51
|
|
- cmMidiByte_t scale;
|
52
|
|
-} cmMidiKeySig_t;
|
53
|
|
-
|
54
|
|
-typedef struct
|
55
|
|
-{
|
56
|
|
- cmMidiByte_t ch;
|
57
|
|
- cmMidiByte_t d0;
|
58
|
|
- cmMidiByte_t d1;
|
59
|
|
- unsigned durTicks; // note duration calc'd by
|
60
|
|
-} cmMidiChMsg_t;
|
61
|
|
-
|
62
|
|
-
|
63
|
|
-typedef struct cmMidiTrackMsg_str
|
64
|
|
-{
|
65
|
|
- unsigned dtick; // delta ticks
|
66
|
|
- cmMidiByte_t status; // ch msg's have the channel value removed (it is stored in u.chMsgPtr->ch)
|
67
|
|
- cmMidiByte_t metaId; //
|
68
|
|
- unsigned short trkIdx; //
|
69
|
|
- unsigned byteCnt; // length of data pointed to by u.voidPtr (or any other pointer in the union)
|
70
|
|
- struct cmMidiTrackMsg_str* link; // link to next record in this track
|
71
|
|
-
|
72
|
|
- union
|
|
8
|
+ // MIDI file timing:
|
|
9
|
+ // Messages in the MIDI file are time tagged with a delta offset in 'ticks'
|
|
10
|
+ // from the previous message in the same track.
|
|
11
|
+ //
|
|
12
|
+ // A 'tick' can be converted to microsends as follows:
|
|
13
|
+ //
|
|
14
|
+ // microsecond per tick = micros per quarter note / ticks per quarter note
|
|
15
|
+ //
|
|
16
|
+ // MpT = MpQN / TpQN
|
|
17
|
+ //
|
|
18
|
+ // TpQN is given as a constant in the MIDI file header.
|
|
19
|
+ // MpQN is given as the value of the MIDI file tempo message.
|
|
20
|
+ //
|
|
21
|
+ // See cmMidiFileSeekUSecs() for an example of converting ticks to milliseconds.
|
|
22
|
+ //
|
|
23
|
+ // As part of the file reading process, the status byte of note-on messages
|
|
24
|
+ // with velocity=0 are is changed to a note-off message. See _cmMidiFileReadChannelMsg().
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+ typedef cmHandle_t cmMidiFileH_t;
|
|
29
|
+ typedef unsigned cmMfRC_t;
|
|
30
|
+
|
|
31
|
+ typedef struct
|
73
|
32
|
{
|
74
|
|
- cmMidiByte_t bVal;
|
75
|
|
- unsigned iVal;
|
76
|
|
- unsigned short sVal;
|
77
|
|
- const char* text;
|
78
|
|
- const void* voidPtr;
|
79
|
|
- const cmMidiSmpte_t* smptePtr;
|
80
|
|
- const cmMidiTimeSig_t* timeSigPtr;
|
81
|
|
- const cmMidiKeySig_t* keySigPtr;
|
82
|
|
- const cmMidiChMsg_t* chMsgPtr;
|
83
|
|
- const cmMidiByte_t* sysExPtr;
|
84
|
|
- } u;
|
85
|
|
-} cmMidiTrackMsg_t;
|
86
|
|
-
|
87
|
|
-enum
|
88
|
|
-{
|
89
|
|
- kOkMfRC = cmOkRC, // 0
|
90
|
|
- kSysFopenFailMfRC, // 1
|
91
|
|
- kSysFreadFailMfRC, // 2
|
92
|
|
- kSysFseekFailMfRC, // 3
|
93
|
|
- kSysFtellFailMfRC, // 4
|
94
|
|
- kSysFcloseFailMfRC, // 5
|
95
|
|
- kNotAMidiFileMfRC, // 6
|
96
|
|
- kMemAllocFailMfRC, // 7
|
97
|
|
- kFileCorruptMfRC, // 8
|
98
|
|
- kMissingEoxMfRC, // 9
|
99
|
|
- kUnknownMetaIdMfRC, // 10
|
100
|
|
- kInvalidHandleMfRC, // 11
|
101
|
|
- kMissingNoteOffMfRC, // 12
|
102
|
|
- kInvalidStatusMfRC // 13
|
103
|
|
-};
|
104
|
|
-
|
105
|
|
-extern cmMidiFileH_t cmMidiFileNullHandle;
|
106
|
|
-
|
107
|
|
-cmMfRC_t cmMidiFileOpen( const char* fn, cmMidiFileH_t* hPtr, cmCtx_t* ctx );
|
108
|
|
-cmMfRC_t cmMidiFileClose( cmMidiFileH_t* hp );
|
109
|
|
-
|
110
|
|
-// Returns track count or kInvalidCnt if 'h' is invalid.
|
111
|
|
-unsigned cmMidiFileTrackCount( cmMidiFileH_t h );
|
112
|
|
-
|
113
|
|
-// Return midi file format id (0,1,2) or kInvalidId if 'h' is invalid.
|
114
|
|
-unsigned cmMidiFileType( cmMidiFileH_t h );
|
115
|
|
-
|
116
|
|
-// Returns ticks per quarter note or kInvalidMidiByte if 'h' is invalid or 0 if file uses SMPTE ticks per frame time base.
|
117
|
|
-unsigned cmMidiFileTicksPerQN( cmMidiFileH_t h );
|
118
|
|
-
|
119
|
|
-// The file name used in an earlier call to midiFileOpen() or NULL if this
|
120
|
|
-// midi file did not originate from an actual file.
|
121
|
|
-const char* cmMidiFileName( cmMidiFileH_t h );
|
122
|
|
-
|
123
|
|
-// Returns SMPTE ticks per frame or kInvalidMidiByte if 'h' is invalid or 0 if file uses ticks per quarter note time base.
|
124
|
|
-cmMidiByte_t cmMidiFileTicksPerSmpteFrame( cmMidiFileH_t h );
|
125
|
|
-
|
126
|
|
-// Returns SMPTE format or kInvalidMidiByte if 'h' is invalid or 0 if file uses ticks per quarter note time base.
|
127
|
|
-cmMidiByte_t cmMidiFileSmpteFormatId( cmMidiFileH_t h );
|
128
|
|
-
|
129
|
|
-// Returns count of records in track 'trackIdx' or kInvalidCnt if 'h' is invalid.
|
130
|
|
-unsigned cmMidiFileTrackMsgCount( cmMidiFileH_t h, unsigned trackIdx );
|
131
|
|
-
|
132
|
|
-// Returns base of record chain from track 'trackIdx' or NULL if 'h' is invalid.
|
133
|
|
-const cmMidiTrackMsg_t* cmMidiFileTrackMsg( cmMidiFileH_t h, unsigned trackIdx );
|
134
|
|
-
|
135
|
|
-// Returns the total count of records in the midi file and the number in the array returned by cmMidiFileMsgArray().
|
136
|
|
-// Return kInvalidCnt if 'h' is invalid.
|
137
|
|
-unsigned cmMidiFileMsgCount( cmMidiFileH_t h );
|
138
|
|
-
|
139
|
|
-// Returns a pointer to the base of an array of pointers to each record in the file sorted in ascending time order.
|
140
|
|
-// Returns NULL if 'h' is invalid.
|
141
|
|
-const cmMidiTrackMsg_t** cmMidiFileMsgArray( cmMidiFileH_t h );
|
142
|
|
-
|
143
|
|
-// Return a pointer to the first msg at or after 'usecsOffs' or kInvalidIdx if no
|
144
|
|
-// msg exists after 'usecsOffs'. Note that 'usecOffs' is an offset from the beginning
|
145
|
|
-// of the file.
|
146
|
|
-// On return *'msgUsecsPtr' is set to the actual time of the msg.
|
147
|
|
-// (which will be equal to or greater than 'usecsOffs').
|
148
|
|
-unsigned cmMidiFileSeekUsecs( cmMidiFileH_t h, unsigned usecsOffs, unsigned* msgUsecsPtr, unsigned* newMicrosPerTickPtr );
|
149
|
|
-
|
150
|
|
-double cmMidiFileDurSecs( cmMidiFileH_t h );
|
151
|
|
-
|
152
|
|
-// Convert the track message 'dtick' field to delta-microseconds.
|
153
|
|
-void cmMidiFileTickToMicros( cmMidiFileH_t h );
|
154
|
|
-
|
155
|
|
-// Calculate Note Duration
|
156
|
|
-void cmMidiFileCalcNoteDurations( cmMidiFileH_t h );
|
157
|
|
-
|
158
|
|
-// Set the delay prior to the first non-zero msg.
|
159
|
|
-void cmMidiFileSetDelay( cmMidiFileH_t h, unsigned ticks );
|
160
|
|
-
|
161
|
|
-// This function packs a track msg into a single consecutive
|
162
|
|
-// block of memory buf[ bufByteCnt ]. Call cmMidiFilePackTracMsgBufByteCount()
|
163
|
|
-// to get the required buffer length for any given cmMidiTrackMsg_t instance.
|
164
|
|
-cmMidiTrackMsg_t* cmMidiFilePackTrackMsg( const cmMidiTrackMsg_t* m, void* buf, unsigned bufByteCnt );
|
165
|
|
-unsigned cmMidiFilePackTrackMsgBufByteCount( const cmMidiTrackMsg_t* m );
|
166
|
|
-
|
167
|
|
-void cmMidiFilePrint( cmMidiFileH_t h, unsigned trkIdx, cmRpt_t* rpt );
|
168
|
|
-bool cmMidiFileIsNull( cmMidiFileH_t h );
|
169
|
|
-void cmMidiFileTest( const char* fn, cmCtx_t* ctx );
|
|
33
|
+ cmMidiByte_t hr;
|
|
34
|
+ cmMidiByte_t min;
|
|
35
|
+ cmMidiByte_t sec;
|
|
36
|
+ cmMidiByte_t frm;
|
|
37
|
+ cmMidiByte_t sfr;
|
|
38
|
+ } cmMidiSmpte_t;
|
|
39
|
+
|
|
40
|
+ typedef struct
|
|
41
|
+ {
|
|
42
|
+ cmMidiByte_t num;
|
|
43
|
+ cmMidiByte_t den;
|
|
44
|
+ cmMidiByte_t metro;
|
|
45
|
+ cmMidiByte_t th2s;
|
|
46
|
+ } cmMidiTimeSig_t;
|
|
47
|
+
|
|
48
|
+ typedef struct
|
|
49
|
+ {
|
|
50
|
+ cmMidiByte_t key;
|
|
51
|
+ cmMidiByte_t scale;
|
|
52
|
+ } cmMidiKeySig_t;
|
|
53
|
+
|
|
54
|
+ typedef struct
|
|
55
|
+ {
|
|
56
|
+ cmMidiByte_t ch;
|
|
57
|
+ cmMidiByte_t d0;
|
|
58
|
+ cmMidiByte_t d1;
|
|
59
|
+ unsigned durTicks; // note duration calc'd by
|
|
60
|
+ } cmMidiChMsg_t;
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+ typedef struct cmMidiTrackMsg_str
|
|
64
|
+ {
|
|
65
|
+ unsigned dtick; // delta ticks
|
|
66
|
+ cmMidiByte_t status; // ch msg's have the channel value removed (it is stored in u.chMsgPtr->ch)
|
|
67
|
+ cmMidiByte_t metaId; //
|
|
68
|
+ unsigned short trkIdx; //
|
|
69
|
+ unsigned byteCnt; // length of data pointed to by u.voidPtr (or any other pointer in the union)
|
|
70
|
+ struct cmMidiTrackMsg_str* link; // link to next record in this track
|
|
71
|
+
|
|
72
|
+ union
|
|
73
|
+ {
|
|
74
|
+ cmMidiByte_t bVal;
|
|
75
|
+ unsigned iVal;
|
|
76
|
+ unsigned short sVal;
|
|
77
|
+ const char* text;
|
|
78
|
+ const void* voidPtr;
|
|
79
|
+ const cmMidiSmpte_t* smptePtr;
|
|
80
|
+ const cmMidiTimeSig_t* timeSigPtr;
|
|
81
|
+ const cmMidiKeySig_t* keySigPtr;
|
|
82
|
+ const cmMidiChMsg_t* chMsgPtr;
|
|
83
|
+ const cmMidiByte_t* sysExPtr;
|
|
84
|
+ } u;
|
|
85
|
+ } cmMidiTrackMsg_t;
|
|
86
|
+
|
|
87
|
+ enum
|
|
88
|
+ {
|
|
89
|
+ kOkMfRC = cmOkRC, // 0
|
|
90
|
+ kSysFopenFailMfRC, // 1
|
|
91
|
+ kSysFreadFailMfRC, // 2
|
|
92
|
+ kSysFseekFailMfRC, // 3
|
|
93
|
+ kSysFtellFailMfRC, // 4
|
|
94
|
+ kSysFcloseFailMfRC, // 5
|
|
95
|
+ kNotAMidiFileMfRC, // 6
|
|
96
|
+ kMemAllocFailMfRC, // 7
|
|
97
|
+ kFileCorruptMfRC, // 8
|
|
98
|
+ kMissingEoxMfRC, // 9
|
|
99
|
+ kUnknownMetaIdMfRC, // 10
|
|
100
|
+ kInvalidHandleMfRC, // 11
|
|
101
|
+ kMissingNoteOffMfRC, // 12
|
|
102
|
+ kInvalidStatusMfRC // 13
|
|
103
|
+ };
|
|
104
|
+
|
|
105
|
+ extern cmMidiFileH_t cmMidiFileNullHandle;
|
|
106
|
+
|
|
107
|
+ cmMfRC_t cmMidiFileOpen( const char* fn, cmMidiFileH_t* hPtr, cmCtx_t* ctx );
|
|
108
|
+ cmMfRC_t cmMidiFileClose( cmMidiFileH_t* hp );
|
|
109
|
+
|
|
110
|
+ // Returns track count or kInvalidCnt if 'h' is invalid.
|
|
111
|
+ unsigned cmMidiFileTrackCount( cmMidiFileH_t h );
|
|
112
|
+
|
|
113
|
+ // Return midi file format id (0,1,2) or kInvalidId if 'h' is invalid.
|
|
114
|
+ unsigned cmMidiFileType( cmMidiFileH_t h );
|
|
115
|
+
|
|
116
|
+ // Returns ticks per quarter note or kInvalidMidiByte if 'h' is invalid or 0 if file uses SMPTE ticks per frame time base.
|
|
117
|
+ unsigned cmMidiFileTicksPerQN( cmMidiFileH_t h );
|
|
118
|
+
|
|
119
|
+ // The file name used in an earlier call to midiFileOpen() or NULL if this
|
|
120
|
+ // midi file did not originate from an actual file.
|
|
121
|
+ const char* cmMidiFileName( cmMidiFileH_t h );
|
|
122
|
+
|
|
123
|
+ // Returns SMPTE ticks per frame or kInvalidMidiByte if 'h' is invalid or 0 if file uses ticks per quarter note time base.
|
|
124
|
+ cmMidiByte_t cmMidiFileTicksPerSmpteFrame( cmMidiFileH_t h );
|
|
125
|
+
|
|
126
|
+ // Returns SMPTE format or kInvalidMidiByte if 'h' is invalid or 0 if file uses ticks per quarter note time base.
|
|
127
|
+ cmMidiByte_t cmMidiFileSmpteFormatId( cmMidiFileH_t h );
|
|
128
|
+
|
|
129
|
+ // Returns count of records in track 'trackIdx' or kInvalidCnt if 'h' is invalid.
|
|
130
|
+ unsigned cmMidiFileTrackMsgCount( cmMidiFileH_t h, unsigned trackIdx );
|
|
131
|
+
|
|
132
|
+ // Returns base of record chain from track 'trackIdx' or NULL if 'h' is invalid.
|
|
133
|
+ const cmMidiTrackMsg_t* cmMidiFileTrackMsg( cmMidiFileH_t h, unsigned trackIdx );
|
|
134
|
+
|
|
135
|
+ // Returns the total count of records in the midi file and the number in the array returned by cmMidiFileMsgArray().
|
|
136
|
+ // Return kInvalidCnt if 'h' is invalid.
|
|
137
|
+ unsigned cmMidiFileMsgCount( cmMidiFileH_t h );
|
|
138
|
+
|
|
139
|
+ // Returns a pointer to the base of an array of pointers to each record in the file sorted in ascending time order.
|
|
140
|
+ // Returns NULL if 'h' is invalid.
|
|
141
|
+ const cmMidiTrackMsg_t** cmMidiFileMsgArray( cmMidiFileH_t h );
|
|
142
|
+
|
|
143
|
+ // Return a pointer to the first msg at or after 'usecsOffs' or kInvalidIdx if no
|
|
144
|
+ // msg exists after 'usecsOffs'. Note that 'usecOffs' is an offset from the beginning
|
|
145
|
+ // of the file.
|
|
146
|
+ // On return *'msgUsecsPtr' is set to the actual time of the msg.
|
|
147
|
+ // (which will be equal to or greater than 'usecsOffs').
|
|
148
|
+ unsigned cmMidiFileSeekUsecs( cmMidiFileH_t h, unsigned usecsOffs, unsigned* msgUsecsPtr, unsigned* newMicrosPerTickPtr );
|
|
149
|
+
|
|
150
|
+ double cmMidiFileDurSecs( cmMidiFileH_t h );
|
|
151
|
+
|
|
152
|
+ // Convert the track message 'dtick' field to delta-microseconds.
|
|
153
|
+ void cmMidiFileTickToMicros( cmMidiFileH_t h );
|
|
154
|
+
|
|
155
|
+ // Convert the track message 'dtick' field to samples.
|
|
156
|
+ // If the absFl is set then the delta times are converted to absolute time.
|
|
157
|
+ void cmMidiFileTickToSamples( cmMidiFileH_t h, double srate, bool absFl );
|
|
158
|
+
|
|
159
|
+ // Calculate Note Duration
|
|
160
|
+ void cmMidiFileCalcNoteDurations( cmMidiFileH_t h );
|
|
161
|
+
|
|
162
|
+ // Set the delay prior to the first non-zero msg.
|
|
163
|
+ void cmMidiFileSetDelay( cmMidiFileH_t h, unsigned ticks );
|
|
164
|
+
|
|
165
|
+ // This function packs a track msg into a single consecutive
|
|
166
|
+ // block of memory buf[ bufByteCnt ]. Call cmMidiFilePackTracMsgBufByteCount()
|
|
167
|
+ // to get the required buffer length for any given cmMidiTrackMsg_t instance.
|
|
168
|
+ cmMidiTrackMsg_t* cmMidiFilePackTrackMsg( const cmMidiTrackMsg_t* m, void* buf, unsigned bufByteCnt );
|
|
169
|
+ unsigned cmMidiFilePackTrackMsgBufByteCount( const cmMidiTrackMsg_t* m );
|
|
170
|
+
|
|
171
|
+ void cmMidiFilePrint( cmMidiFileH_t h, unsigned trkIdx, cmRpt_t* rpt );
|
|
172
|
+ bool cmMidiFileIsNull( cmMidiFileH_t h );
|
|
173
|
+ void cmMidiFileTest( const char* fn, cmCtx_t* ctx );
|
170
|
174
|
|
171
|
175
|
#ifdef __cplusplus
|
172
|
176
|
}
|