2012-10-30 03:52:39 +00:00
|
|
|
#ifndef cmMidiFile_h
|
|
|
|
#define cmMidiFile_h
|
|
|
|
|
2012-11-15 04:03:30 +00:00
|
|
|
#ifdef __cplusplus
|
|
|
|
extern "C" {
|
|
|
|
#endif
|
|
|
|
|
2015-11-16 19:59:27 +00:00
|
|
|
//( { file_desc:"MIDI file reader and writer." kw:[midi file]}
|
2012-11-18 01:32:21 +00:00
|
|
|
// MIDI file timing:
|
|
|
|
// Messages in the MIDI file are time tagged with a delta offset in 'ticks'
|
|
|
|
// from the previous message in the same track.
|
|
|
|
//
|
|
|
|
// A 'tick' can be converted to microsends as follows:
|
|
|
|
//
|
|
|
|
// microsecond per tick = micros per quarter note / ticks per quarter note
|
|
|
|
//
|
|
|
|
// MpT = MpQN / TpQN
|
|
|
|
//
|
|
|
|
// TpQN is given as a constant in the MIDI file header.
|
|
|
|
// MpQN is given as the value of the MIDI file tempo message.
|
|
|
|
//
|
|
|
|
// See cmMidiFileSeekUSecs() for an example of converting ticks to milliseconds.
|
|
|
|
//
|
|
|
|
// As part of the file reading process, the status byte of note-on messages
|
|
|
|
// with velocity=0 are is changed to a note-off message. See _cmMidiFileReadChannelMsg().
|
2015-11-16 19:59:27 +00:00
|
|
|
//)
|
|
|
|
|
|
|
|
//(
|
2012-11-18 01:32:21 +00:00
|
|
|
typedef cmHandle_t cmMidiFileH_t;
|
|
|
|
typedef unsigned cmMfRC_t;
|
|
|
|
|
|
|
|
typedef struct
|
2012-10-30 03:52:39 +00:00
|
|
|
{
|
2012-11-18 01:32:21 +00:00
|
|
|
cmMidiByte_t hr;
|
|
|
|
cmMidiByte_t min;
|
|
|
|
cmMidiByte_t sec;
|
|
|
|
cmMidiByte_t frm;
|
|
|
|
cmMidiByte_t sfr;
|
|
|
|
} cmMidiSmpte_t;
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
cmMidiByte_t num;
|
|
|
|
cmMidiByte_t den;
|
|
|
|
cmMidiByte_t metro;
|
|
|
|
cmMidiByte_t th2s;
|
|
|
|
} cmMidiTimeSig_t;
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
cmMidiByte_t key;
|
|
|
|
cmMidiByte_t scale;
|
|
|
|
} cmMidiKeySig_t;
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
cmMidiByte_t ch;
|
|
|
|
cmMidiByte_t d0;
|
|
|
|
cmMidiByte_t d1;
|
2015-11-20 00:08:39 +00:00
|
|
|
unsigned durMicros; // note duration in microseconds (corrected for tempo changes)
|
2012-11-18 01:32:21 +00:00
|
|
|
} cmMidiChMsg_t;
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct cmMidiTrackMsg_str
|
|
|
|
{
|
2013-09-26 06:43:59 +00:00
|
|
|
unsigned uid; // uid's are unique among all msg's in the file
|
2015-11-20 00:08:39 +00:00
|
|
|
unsigned dtick; // delta ticks between events on this track
|
2016-04-13 21:02:25 +00:00
|
|
|
unsigned long long atick; // global (all tracks interleaved) accumulated ticks
|
|
|
|
unsigned long long amicro; // global (all tracks interleaved) accumulated microseconds adjusted for tempo changes
|
2012-11-18 01:32:21 +00:00
|
|
|
cmMidiByte_t status; // ch msg's have the channel value removed (it is stored in u.chMsgPtr->ch)
|
|
|
|
cmMidiByte_t metaId; //
|
|
|
|
unsigned short trkIdx; //
|
|
|
|
unsigned byteCnt; // length of data pointed to by u.voidPtr (or any other pointer in the union)
|
|
|
|
struct cmMidiTrackMsg_str* link; // link to next record in this track
|
|
|
|
|
|
|
|
union
|
|
|
|
{
|
|
|
|
cmMidiByte_t bVal;
|
|
|
|
unsigned iVal;
|
|
|
|
unsigned short sVal;
|
|
|
|
const char* text;
|
|
|
|
const void* voidPtr;
|
|
|
|
const cmMidiSmpte_t* smptePtr;
|
|
|
|
const cmMidiTimeSig_t* timeSigPtr;
|
|
|
|
const cmMidiKeySig_t* keySigPtr;
|
|
|
|
const cmMidiChMsg_t* chMsgPtr;
|
|
|
|
const cmMidiByte_t* sysExPtr;
|
|
|
|
} u;
|
|
|
|
} cmMidiTrackMsg_t;
|
|
|
|
|
2016-05-12 23:04:55 +00:00
|
|
|
#define cmMidiFileIsNoteOn(m) (cmMidiIsNoteOn((m)->status) && (m)->u.chMsgPtr->d1>0)
|
|
|
|
#define cmMidiFileIsNoteOff(m) (cmMidiIsNoteOff((m)->status,(m)->u.chMsgPtr->d1))
|
2016-05-18 17:11:36 +00:00
|
|
|
|
|
|
|
#define cmMidiFileIsSustainPedalUp(m) (cmMidiIsSustainPedalUp( (m)->status,(m)->u.chMsgPtr->d0,(m)->u.chMsgPtr->d1))
|
|
|
|
#define cmMidiFileIsSustainPedalDown(m) (cmMidiIsSustainPedalDown( (m)->status,(m)->u.chMsgPtr->d0,(m)->u.chMsgPtr->d1))
|
|
|
|
|
|
|
|
#define cmMidiFileIsSostenutoPedalUp(m) (cmMidiIsSostenutoPedalUp( (m)->status,(m)->u.chMsgPtr->d0,(m)->u.chMsgPtr->d1))
|
|
|
|
#define cmMidiFileIsSostenutoPedalDown(m) (cmMidiIsSostenutoPedalDown((m)->status,(m)->u.chMsgPtr->d0,(m)->u.chMsgPtr->d1))
|
2016-05-12 23:04:55 +00:00
|
|
|
|
2012-11-18 01:32:21 +00:00
|
|
|
enum
|
|
|
|
{
|
2015-02-24 23:38:31 +00:00
|
|
|
kOkMfRC = cmOkRC, // 0
|
|
|
|
kFileFailMfRC, // 1
|
|
|
|
kNotAMidiFileMfRC, // 2
|
|
|
|
kMemAllocFailMfRC, // 3
|
|
|
|
kFileCorruptMfRC, // 4
|
|
|
|
kMissingEoxMfRC, // 5
|
|
|
|
kUnknownMetaIdMfRC, // 6
|
|
|
|
kInvalidHandleMfRC, // 7
|
|
|
|
kMissingNoteOffMfRC, // 8
|
|
|
|
kInvalidStatusMfRC, // 9
|
2016-04-13 19:43:20 +00:00
|
|
|
kSustainPedalMfRC, // 10
|
2016-05-12 23:04:55 +00:00
|
|
|
kSostenutoPedalMfRC, // 11
|
2016-06-16 16:50:43 +00:00
|
|
|
kLargeDeltaTickMfRC, // 12 (a large delta tick value was filtered)
|
|
|
|
kUidNotFoundMfRC, // 13
|
|
|
|
kUidNotANoteMsgMfRC // 14
|
2012-11-18 01:32:21 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
extern cmMidiFileH_t cmMidiFileNullHandle;
|
|
|
|
|
2016-02-17 22:14:43 +00:00
|
|
|
cmMfRC_t cmMidiFileOpen( cmCtx_t* ctx, cmMidiFileH_t* hPtr, const char* fn );
|
2012-11-18 01:32:21 +00:00
|
|
|
cmMfRC_t cmMidiFileClose( cmMidiFileH_t* hp );
|
|
|
|
|
2013-09-24 20:03:01 +00:00
|
|
|
cmMfRC_t cmMidiFileWrite( cmMidiFileH_t h, const char* fn );
|
|
|
|
|
2012-11-27 07:03:35 +00:00
|
|
|
bool cmMidiFileIsValid( cmMidiFileH_t h );
|
|
|
|
|
2012-11-18 01:32:21 +00:00
|
|
|
// Returns track count or kInvalidCnt if 'h' is invalid.
|
|
|
|
unsigned cmMidiFileTrackCount( cmMidiFileH_t h );
|
|
|
|
|
|
|
|
// Return midi file format id (0,1,2) or kInvalidId if 'h' is invalid.
|
|
|
|
unsigned cmMidiFileType( cmMidiFileH_t h );
|
|
|
|
|
|
|
|
// Returns ticks per quarter note or kInvalidMidiByte if 'h' is invalid or 0 if file uses SMPTE ticks per frame time base.
|
|
|
|
unsigned cmMidiFileTicksPerQN( cmMidiFileH_t h );
|
|
|
|
|
|
|
|
// The file name used in an earlier call to midiFileOpen() or NULL if this
|
|
|
|
// midi file did not originate from an actual file.
|
|
|
|
const char* cmMidiFileName( cmMidiFileH_t h );
|
|
|
|
|
|
|
|
// Returns SMPTE ticks per frame or kInvalidMidiByte if 'h' is invalid or 0 if file uses ticks per quarter note time base.
|
|
|
|
cmMidiByte_t cmMidiFileTicksPerSmpteFrame( cmMidiFileH_t h );
|
|
|
|
|
|
|
|
// Returns SMPTE format or kInvalidMidiByte if 'h' is invalid or 0 if file uses ticks per quarter note time base.
|
|
|
|
cmMidiByte_t cmMidiFileSmpteFormatId( cmMidiFileH_t h );
|
|
|
|
|
|
|
|
// Returns count of records in track 'trackIdx' or kInvalidCnt if 'h' is invalid.
|
|
|
|
unsigned cmMidiFileTrackMsgCount( cmMidiFileH_t h, unsigned trackIdx );
|
|
|
|
|
|
|
|
// Returns base of record chain from track 'trackIdx' or NULL if 'h' is invalid.
|
|
|
|
const cmMidiTrackMsg_t* cmMidiFileTrackMsg( cmMidiFileH_t h, unsigned trackIdx );
|
|
|
|
|
|
|
|
// Returns the total count of records in the midi file and the number in the array returned by cmMidiFileMsgArray().
|
|
|
|
// Return kInvalidCnt if 'h' is invalid.
|
|
|
|
unsigned cmMidiFileMsgCount( cmMidiFileH_t h );
|
|
|
|
|
|
|
|
// Returns a pointer to the base of an array of pointers to each record in the file sorted in ascending time order.
|
|
|
|
// Returns NULL if 'h' is invalid.
|
|
|
|
const cmMidiTrackMsg_t** cmMidiFileMsgArray( cmMidiFileH_t h );
|
|
|
|
|
2016-06-16 16:50:43 +00:00
|
|
|
// Set the velocity of a note-on/off msg identified by 'uid'.
|
|
|
|
cmMfRC_t cmMidiFileSetVelocity( cmMidiFileH_t h, unsigned uid, cmMidiByte_t vel );
|
|
|
|
|
2016-06-22 21:08:47 +00:00
|
|
|
// Insert a MIDI message relative to the reference msg identified by 'uid'.
|
|
|
|
// If dtick is positive/negative then the new msg is inserted after/before the reference msg.
|
|
|
|
cmMfRC_t cmMidiFileInsertMsg( cmMidiFileH_t h, unsigned uid, int dtick, cmMidiByte_t ch, cmMidiByte_t status, cmMidiByte_t d0, cmMidiByte_t d1 );
|
|
|
|
|
2012-11-18 01:32:21 +00:00
|
|
|
// Return a pointer to the first msg at or after 'usecsOffs' or kInvalidIdx if no
|
|
|
|
// msg exists after 'usecsOffs'. Note that 'usecOffs' is an offset from the beginning
|
|
|
|
// of the file.
|
|
|
|
// On return *'msgUsecsPtr' is set to the actual time of the msg.
|
|
|
|
// (which will be equal to or greater than 'usecsOffs').
|
2016-04-13 21:02:25 +00:00
|
|
|
unsigned cmMidiFileSeekUsecs( cmMidiFileH_t h, unsigned long long usecsOffs, unsigned* msgUsecsPtr, unsigned* newMicrosPerTickPtr );
|
2012-11-18 01:32:21 +00:00
|
|
|
|
|
|
|
double cmMidiFileDurSecs( cmMidiFileH_t h );
|
|
|
|
|
|
|
|
// Calculate Note Duration
|
|
|
|
void cmMidiFileCalcNoteDurations( cmMidiFileH_t h );
|
|
|
|
|
|
|
|
// Set the delay prior to the first non-zero msg.
|
|
|
|
void cmMidiFileSetDelay( cmMidiFileH_t h, unsigned ticks );
|
|
|
|
|
|
|
|
// This function packs a track msg into a single consecutive
|
|
|
|
// block of memory buf[ bufByteCnt ]. Call cmMidiFilePackTracMsgBufByteCount()
|
|
|
|
// to get the required buffer length for any given cmMidiTrackMsg_t instance.
|
|
|
|
cmMidiTrackMsg_t* cmMidiFilePackTrackMsg( const cmMidiTrackMsg_t* m, void* buf, unsigned bufByteCnt );
|
|
|
|
unsigned cmMidiFilePackTrackMsgBufByteCount( const cmMidiTrackMsg_t* m );
|
|
|
|
|
2015-11-18 23:56:18 +00:00
|
|
|
void cmMidiFilePrintMsgs( cmMidiFileH_t h, cmRpt_t* rpt );
|
|
|
|
void cmMidiFilePrintTrack( cmMidiFileH_t h, unsigned trkIdx, cmRpt_t* rpt );
|
2012-11-18 01:32:21 +00:00
|
|
|
bool cmMidiFileIsNull( cmMidiFileH_t h );
|
|
|
|
void cmMidiFileTest( const char* fn, cmCtx_t* ctx );
|
2012-11-15 04:03:30 +00:00
|
|
|
|
2016-03-31 23:06:20 +00:00
|
|
|
// Generate a piano-roll plot description file which can be displayed with cmXScore.m
|
|
|
|
cmMfRC_t cmMidiFileGenPlotFile( cmCtx_t* ctx, const cmChar_t* midiFn, const cmChar_t* outFn );
|
|
|
|
|
|
|
|
|
2015-11-16 19:59:27 +00:00
|
|
|
//)
|
|
|
|
|
2012-11-15 04:03:30 +00:00
|
|
|
#ifdef __cplusplus
|
|
|
|
}
|
|
|
|
#endif
|
2012-10-30 03:52:39 +00:00
|
|
|
|
|
|
|
#endif
|