Jules,
You don't need any investigation at this level. The Midi File format is just a short value (16 bits in big endian) that you put at 1 by default for all the case. Your class don't need change this is just a header value used to identify the format implemented (1 by default is multitrack format).
By convention, in format 1, the first track you put in the midi file is a free-of-note track that include the tempo, time signature, key signature, section, and other master information of the song. All other tracks are dedicated to the song tracks, always by convention, one instrument by track, one channel by track (but you can multiply the program change and channel change inside a track, this is just a convention)
The Midi File format 0, is a single track midi file that include all the events of all "song tracks" mixed together and with all the tempo, time, key, ... informations. So, your class is already able to generate Midi File in format 0 and 1.
The Midi File format 2, is a single track where you put multiple pattern. This is a specific format used by drum machine for example to generate multiple partial sequences that can be played with a playlist. This is not really a used format, all the midi files you can find are in Format 1 and 0.
Your class don't need to manage the difference between 0, 1 or 2, but you need to allow the access to the value you write here :
bool MidiFile::writeTo (OutputStream& out)
{
out.writeIntBigEndian ((int) ByteOrder::bigEndianInt ("MThd"));
out.writeIntBigEndian (6);
out.writeShortBigEndian (1); // type
out.writeShortBigEndian ((short) tracks.size());
out.writeShortBigEndian (timeFormat);
for (int i = 0; i < tracks.size(); ++i)
writeTrack (out, i);
out.flush();
return true;
}
The class need just a new variable "fileType" that have "1" as default value, and two accessors (set/get) :
private:
//==============================================================================
OwnedArray<MidiMessageSequence> tracks;
short timeFormat;
short fileType;
void readNextTrack (const uint8*, int size);
void writeTrack (OutputStream&, int trackNum);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiFile)
/** Returns the midi file format.
Values can be 0, 1 or 2 (default 1).
General Midi specification :
Midifile Format 0 : monotrack
Midifile Format 1 : multitrack
Midifile Format 2 : patterns (monotrack)
*/
short getFileType () const noexcept;
void setFileType ( short fileType ) noexcept;
You already read the fileType (fileFormat) :
static bool parseMidiHeader (const uint8* &data, short& timeFormat, short& fileType, short& numberOfTracks) noexcept
{
unsigned int ch = ByteOrder::bigEndianInt (data);
data += 4;
if (ch != ByteOrder::bigEndianInt ("MThd"))
{
bool ok = false;
if (ch == ByteOrder::bigEndianInt ("RIFF"))
{
for (int i = 0; i < 8; ++i)
{
ch = ByteOrder::bigEndianInt (data);
data += 4;
if (ch == ByteOrder::bigEndianInt ("MThd"))
{
ok = true;
break;
}
}
}
if (! ok)
return false;
}
unsigned int bytesRemaining = ByteOrder::bigEndianInt (data);
data += 4;
fileType = (short) ByteOrder::bigEndianShort (data);
data += 2;
numberOfTracks = (short) ByteOrder::bigEndianShort (data);
data += 2;
timeFormat = (short) ByteOrder::bigEndianShort (data);
data += 2;
bytesRemaining -= 6;
data += bytesRemaining;
return true;
}
You just need to allow access to this value (read), and modify the previous method writeTo :
bool MidiFile::writeTo (OutputStream& out)
{
out.writeIntBigEndian ((int) ByteOrder::bigEndianInt ("MThd"));
out.writeIntBigEndian (6);
out.writeShortBigEndian (fileType);
out.writeShortBigEndian ((short) tracks.size());
out.writeShortBigEndian (timeFormat);
for (int i = 0; i < tracks.size(); ++i)
writeTrack (out, i);
out.flush();
return true;
}
That's all, this is the user of the this class that is responsable to the integrity of is midi file. In all case, with a 1 as default value, the use of the class stay unchanged and the final user will be not constrain by this change.
All my informations come's froms the legendary book "Maximum MIDI" by Paul Messick (subtitle Music Applications in C++) (ISBN : 1-884777-44-1) - http://www.manning.com/messick/
Do you think you'll we able to do that ? If not, and for information for other users that want to generate midi file format 0. You can just write a file in midi file format 0 style with the Juce class and hack the file like that :
FileOutputStream stream (midiFile);
stream->setPosition (9);
stream->writeByte ( 0, 1 or 2 );
This is a non-elegant way to modify the format by putting a byte in the half part of the short :
position 8 : always 0
position 9 : 0, 1 or 2
Thank's for all Jules !
Max