#ifndef MIDI_FILE_INCLUDED
#define MIDI_FILE_INCLUDED

/*
 * Div's Standard MIDI File API
 * Copyright 2003-2006 by David G. Slomin
 * Provided under the terms of the BSD license
 *
 * Usage notes:
 *
 * 1.  Running status is eliminated from messages at load time; it should not
 *     be used at any other time.
 *
 * 2.  MIDI files in formats 0, 1, and 2 are supported, but the caller is
 *     responsible for placing events into the appropriate tracks.  Format
 *     0 files should only use a single track.  Format 1 files use their
 *     first track as a "conductor track" for meta events like tempo and 
 *     meter changes.
 *
 * 3.  MidiFile_visitEvents() and MidiFileTrack_visitEvents() are specially
 *     designed so that you can add, delete, or change the tick of events
 *     (thereby modifying the sorting order) without upsetting the iterator.
 *
 * 4.  Any data passed into these functions is memory-managed by the caller.
 *     Any data returned from these functions is memory-managed by the API.
 *     Don't forget to call MidiFile_free().
 *
 * 5.  This API is not thread-safe.
 *
 * 6.  You can navigate through events one track at a time, or through all
 *     tracks at once in an interwoven, time-sorted manner.
 *
 * 7.  Because a note on event with a velocity of zero is functionally
 *     equivalent to a note off event, you cannot simply look at the type of 
 *     an event to see whether it signifies the start or the end of a note.
 *     To handle this problem, convenience wrappers are provided for pseudo 
 *     "note start" and "note end" events.
 *
 * 8.  Convenience functions are provided for working with tempo and
 *     absolute time in files of format 1 or 0.  Tempo events (a particular
 *     kind of meta event) are only meaningful when using the PPQ division
 *     type.
 *
 * 9.  Events other than sysex and meta are considered "voice events".  For 
 *     interaction with other APIs, it is sometimes useful to pack their 
 *     messages into 32 bit integers.
 *
 * 10. All numbers in this API are zero-based, to correspond with the actual
 *     byte values of the MIDI protocol, rather than one-based, as they are
 *     commonly displayed to the user.  Channels range from 0 to 15, notes
 *     range from 0 to 127, etc.
 */

#ifdef __cplusplus
extern "c"
{
#endif

typedef struct MidiFile *MidiFile_t;
typedef struct MidiFileTrack *MidiFileTrack_t;
typedef struct MidiFileEvent *MidiFileEvent_t;
typedef void (*MidiFileEventVisitorCallback_t)(MidiFileEvent_t event, void *user_data);

typedef enum
{
	MIDI_FILE_DIVISION_TYPE_INVALID = -1,
	MIDI_FILE_DIVISION_TYPE_PPQ,
	MIDI_FILE_DIVISION_TYPE_SMPTE24,
	MIDI_FILE_DIVISION_TYPE_SMPTE25,
	MIDI_FILE_DIVISION_TYPE_SMPTE30DROP,
	MIDI_FILE_DIVISION_TYPE_SMPTE30
}
MidiFileDivisionType_t;

typedef enum
{
	MIDI_FILE_EVENT_TYPE_INVALID = -1,
	MIDI_FILE_EVENT_TYPE_NOTE_OFF,
	MIDI_FILE_EVENT_TYPE_NOTE_ON,
	MIDI_FILE_EVENT_TYPE_KEY_PRESSURE,
	MIDI_FILE_EVENT_TYPE_CONTROL_CHANGE,
	MIDI_FILE_EVENT_TYPE_PROGRAM_CHANGE,
	MIDI_FILE_EVENT_TYPE_CHANNEL_PRESSURE,
	MIDI_FILE_EVENT_TYPE_PITCH_WHEEL,
	MIDI_FILE_EVENT_TYPE_SYSEX,
	MIDI_FILE_EVENT_TYPE_META
}
MidiFileEventType_t;

MidiFile_t MidiFile_load(char *filename);
int MidiFile_save(MidiFile_t midi_file, const char* filename);

MidiFile_t MidiFile_new(int file_format, MidiFileDivisionType_t division_type, int resolution);
int MidiFile_free(MidiFile_t midi_file);
int MidiFile_getFileFormat(MidiFile_t midi_file);
int MidiFile_setFileFormat(MidiFile_t midi_file, int file_format);
MidiFileDivisionType_t MidiFile_getDivisionType(MidiFile_t midi_file);
int MidiFile_setDivisionType(MidiFile_t midi_file, MidiFileDivisionType_t division_type);
int MidiFile_getResolution(MidiFile_t midi_file);
int MidiFile_setResolution(MidiFile_t midi_file, int resolution);
MidiFileTrack_t MidiFile_createTrack(MidiFile_t midi_file);
int MidiFile_getNumberOfTracks(MidiFile_t midi_file);
MidiFileTrack_t MidiFile_getTrackByNumber(MidiFile_t midi_file, int number, int create);
MidiFileTrack_t MidiFile_getFirstTrack(MidiFile_t midi_file);
MidiFileTrack_t MidiFile_getLastTrack(MidiFile_t midi_file);
MidiFileEvent_t MidiFile_getFirstEvent(MidiFile_t midi_file);
MidiFileEvent_t MidiFile_getLastEvent(MidiFile_t midi_file);
int MidiFile_visitEvents(MidiFile_t midi_file, MidiFileEventVisitorCallback_t visitor_callback, void *user_data);
float MidiFile_getTimeFromTick(MidiFile_t midi_file, long tick); /* time is in seconds */
long MidiFile_getTickFromTime(MidiFile_t midi_file, float time);
float MidiFile_getBeatFromTick(MidiFile_t midi_file, long tick);
long MidiFile_getTickFromBeat(MidiFile_t midi_file, float beat);

int MidiFileTrack_delete(MidiFileTrack_t track);
MidiFile_t MidiFileTrack_getMidiFile(MidiFileTrack_t track);
int MidiFileTrack_getNumber(MidiFileTrack_t track);
long MidiFileTrack_getEndTick(MidiFileTrack_t track);
int MidiFileTrack_setEndTick(MidiFileTrack_t track, long end_tick);
MidiFileTrack_t MidiFileTrack_createTrackBefore(MidiFileTrack_t track);
MidiFileTrack_t MidiFileTrack_getPreviousTrack(MidiFileTrack_t track);
MidiFileTrack_t MidiFileTrack_getNextTrack(MidiFileTrack_t track);
MidiFileEvent_t MidiFileTrack_createNoteOffEvent(MidiFileTrack_t track, long tick, int channel, int note, int velocity);
MidiFileEvent_t MidiFileTrack_createNoteOnEvent(MidiFileTrack_t track, long tick, int channel, int note, int velocity);
MidiFileEvent_t MidiFileTrack_createKeyPressureEvent(MidiFileTrack_t track, long tick, int channel, int note, int amount);
MidiFileEvent_t MidiFileTrack_createControlChangeEvent(MidiFileTrack_t track, long tick, int channel, int number, int value);
MidiFileEvent_t MidiFileTrack_createProgramChangeEvent(MidiFileTrack_t track, long tick, int channel, int number);
MidiFileEvent_t MidiFileTrack_createChannelPressureEvent(MidiFileTrack_t track, long tick, int channel, int amount);
MidiFileEvent_t MidiFileTrack_createPitchWheelEvent(MidiFileTrack_t track, long tick, int channel, int value);
MidiFileEvent_t MidiFileTrack_createSysexEvent(MidiFileTrack_t track, long tick, int data_length, unsigned char *data_buffer);
MidiFileEvent_t MidiFileTrack_createMetaEvent(MidiFileTrack_t track, long tick, int number, int data_length, unsigned char *data_buffer);
MidiFileEvent_t MidiFileTrack_createNoteStartAndEndEvents(MidiFileTrack_t track, long start_tick, long end_tick, int channel, int note, int start_velocity, int end_velocity); /* returns the start event */
MidiFileEvent_t MidiFileTrack_createTempoEvent(MidiFileTrack_t track, long tick, float tempo); /* tempo is in BPM */
MidiFileEvent_t MidiFileTrack_createVoiceEvent(MidiFileTrack_t track, long tick, unsigned long data);
MidiFileEvent_t MidiFileTrack_getFirstEvent(MidiFileTrack_t track);
MidiFileEvent_t MidiFileTrack_getLastEvent(MidiFileTrack_t track);
int MidiFileTrack_visitEvents(MidiFileTrack_t track, MidiFileEventVisitorCallback_t visitor_callback, void *user_data);

int MidiFileEvent_delete(MidiFileEvent_t event);
MidiFileTrack_t MidiFileEvent_getTrack(MidiFileEvent_t event);
MidiFileEvent_t MidiFileEvent_getPreviousEvent(MidiFileEvent_t event); /* deprecated:  use MidiFileEvent_getPreviousEventInTrack() */
MidiFileEvent_t MidiFileEvent_getNextEvent(MidiFileEvent_t event); /* deprecated:  use MidiFileEvent_getNextEventInTrack() */
MidiFileEvent_t MidiFileEvent_getPreviousEventInTrack(MidiFileEvent_t event);
MidiFileEvent_t MidiFileEvent_getNextEventInTrack(MidiFileEvent_t event);
MidiFileEvent_t MidiFileEvent_getPreviousEventInFile(MidiFileEvent_t event);
MidiFileEvent_t MidiFileEvent_getNextEventInFile(MidiFileEvent_t event);
long MidiFileEvent_getTick(MidiFileEvent_t event);
int MidiFileEvent_setTick(MidiFileEvent_t event, long tick);
MidiFileEventType_t MidiFileEvent_getType(MidiFileEvent_t event);
int MidiFileEvent_isNoteStartEvent(MidiFileEvent_t event);
int MidiFileEvent_isNoteEndEvent(MidiFileEvent_t event);
int MidiFileEvent_isTempoEvent(MidiFileEvent_t event);
int MidiFileEvent_isVoiceEvent(MidiFileEvent_t event);

int MidiFileNoteOffEvent_getChannel(MidiFileEvent_t event);
int MidiFileNoteOffEvent_setChannel(MidiFileEvent_t event, int channel);
int MidiFileNoteOffEvent_getNote(MidiFileEvent_t event);
int MidiFileNoteOffEvent_setNote(MidiFileEvent_t event, int note);
int MidiFileNoteOffEvent_getVelocity(MidiFileEvent_t event);
int MidiFileNoteOffEvent_setVelocity(MidiFileEvent_t event, int velocity);

int MidiFileNoteOnEvent_getChannel(MidiFileEvent_t event);
int MidiFileNoteOnEvent_setChannel(MidiFileEvent_t event, int channel);
int MidiFileNoteOnEvent_getNote(MidiFileEvent_t event);
int MidiFileNoteOnEvent_setNote(MidiFileEvent_t event, int note);
int MidiFileNoteOnEvent_getVelocity(MidiFileEvent_t event);
int MidiFileNoteOnEvent_setVelocity(MidiFileEvent_t event, int velocity);

int MidiFileKeyPressureEvent_getChannel(MidiFileEvent_t event);
int MidiFileKeyPressureEvent_setChannel(MidiFileEvent_t event, int channel);
int MidiFileKeyPressureEvent_getNote(MidiFileEvent_t event);
int MidiFileKeyPressureEvent_setNote(MidiFileEvent_t event, int note);
int MidiFileKeyPressureEvent_getAmount(MidiFileEvent_t event);
int MidiFileKeyPressureEvent_setAmount(MidiFileEvent_t event, int amount);

int MidiFileControlChangeEvent_getChannel(MidiFileEvent_t event);
int MidiFileControlChangeEvent_setChannel(MidiFileEvent_t event, int channel);
int MidiFileControlChangeEvent_getNumber(MidiFileEvent_t event);
int MidiFileControlChangeEvent_setNumber(MidiFileEvent_t event, int number);
int MidiFileControlChangeEvent_getValue(MidiFileEvent_t event);
int MidiFileControlChangeEvent_setValue(MidiFileEvent_t event, int value);

int MidiFileProgramChangeEvent_getChannel(MidiFileEvent_t event);
int MidiFileProgramChangeEvent_setChannel(MidiFileEvent_t event, int channel);
int MidiFileProgramChangeEvent_getNumber(MidiFileEvent_t event);
int MidiFileProgramChangeEvent_setNumber(MidiFileEvent_t event, int number);

int MidiFileChannelPressureEvent_getChannel(MidiFileEvent_t event);
int MidiFileChannelPressureEvent_setChannel(MidiFileEvent_t event, int channel);
int MidiFileChannelPressureEvent_getAmount(MidiFileEvent_t event);
int MidiFileChannelPressureEvent_setAmount(MidiFileEvent_t event, int amount);

int MidiFilePitchWheelEvent_getChannel(MidiFileEvent_t event);
int MidiFilePitchWheelEvent_setChannel(MidiFileEvent_t event, int channel);
int MidiFilePitchWheelEvent_getValue(MidiFileEvent_t event);
int MidiFilePitchWheelEvent_setValue(MidiFileEvent_t event, int value);

int MidiFileSysexEvent_getDataLength(MidiFileEvent_t event);
unsigned char *MidiFileSysexEvent_getData(MidiFileEvent_t event);
int MidiFileSysexEvent_setData(MidiFileEvent_t event, int data_length, unsigned char *data_buffer);

int MidiFileMetaEvent_getNumber(MidiFileEvent_t event);
int MidiFileMetaEvent_setNumber(MidiFileEvent_t event, int number);
int MidiFileMetaEvent_getDataLength(MidiFileEvent_t event);
unsigned char *MidiFileMetaEvent_getData(MidiFileEvent_t event);
int MidiFileMetaEvent_setData(MidiFileEvent_t event, int data_length, unsigned char *data_buffer);

int MidiFileNoteStartEvent_getChannel(MidiFileEvent_t event);
int MidiFileNoteStartEvent_setChannel(MidiFileEvent_t event, int channel);
int MidiFileNoteStartEvent_getNote(MidiFileEvent_t event);
int MidiFileNoteStartEvent_setNote(MidiFileEvent_t event, int note);
int MidiFileNoteStartEvent_getVelocity(MidiFileEvent_t event);
int MidiFileNoteStartEvent_setVelocity(MidiFileEvent_t event, int velocity);
MidiFileEvent_t MidiFileNoteStartEvent_getNoteEndEvent(MidiFileEvent_t event);

int MidiFileNoteEndEvent_getChannel(MidiFileEvent_t event);
int MidiFileNoteEndEvent_setChannel(MidiFileEvent_t event, int channel);
int MidiFileNoteEndEvent_getNote(MidiFileEvent_t event);
int MidiFileNoteEndEvent_setNote(MidiFileEvent_t event, int note);
int MidiFileNoteEndEvent_getVelocity(MidiFileEvent_t event);
int MidiFileNoteEndEvent_setVelocity(MidiFileEvent_t event, int velocity); /* caution:  will replace a note on event with a note off */
MidiFileEvent_t MidiFileNoteEndEvent_getNoteStartEvent(MidiFileEvent_t event);

float MidiFileTempoEvent_getTempo(MidiFileEvent_t event);
int MidiFileTempoEvent_setTempo(MidiFileEvent_t event, float tempo);

unsigned long MidiFileVoiceEvent_getData(MidiFileEvent_t event);
int MidiFileVoiceEvent_setData(MidiFileEvent_t event, unsigned long data);

#ifdef __cplusplus
}
#endif

#endif
