// // Programmer: Craig Stuart Sapp // Creation Date: Sun Oct 24 10:39:47 PDT 1999 // Last Modified: Fri Nov 10 10:10:03 PST 2000 // Last Modified: Sat Nov 25 20:17:58 PST 2000 (added instrument selection) // Last Modified: Tue Feb 20 19:20:09 PST 2001 (add articulation interpretation) // Filename: ...sig/examples/all/smf.cpp // Web Address: http://sig.sapp.org/examples/museinfo/humdrum/smf.cpp // Syntax: C++; museinfo // // Description: Converts Humdrum **kern data into MIDI data in a // Standard MIDI File format. // #include "museinfo.h" #include #include // global variables: const char *outlocation = NULL; int trackcount = 0; // number of tracks in MIDI file int track = 0; // track number starting at 0 int offset = 0; // start-time offset in ticks int tempo = 60; // default tempo // user interface variables Options options; int storeCommentQ = 1; // used with the -C option int storeTextQ = 1; // used with the -T option int plusQ = 0; // used with the --plus option int defaultvolume = 64; // used with the -v option double tscaling = 1.0; // used with the -s option int multitimbreQ = 1; // used with the -c option int instrumentQ = 1; // used with the -I option int fixedChannel = 0; // used with the -c option int instrumentnumber = -1; // used with the -f option int forcedQ = 0; // used with the -f option int mine = 0; // used with the -m option int shortenQ = 0; // used with the -s option int shortenamount = 0; // used with the -s option int plainQ = 0; // used with the -p option int debugQ = 0; // used with the --debug option // function declarations: void assignTracks (HumdrumFile& infile, Array& trackchannel); double checkForTempo (HumdrumRecord& record); void checkOptions (Options& opts, int argc, char** argv); void example (void); int makeVLV (uchar *buffer, int number); void reviseInstrumentMidiNumbers(const char* string); int setMidiPlusVolume (const char* kernnote); void storeMetaText (MidiFile& mfile, int track, const char* string, int tick, int metaType = 1); void storeMidiData (HumdrumFile& infile, MidiFile& outfile); void storeInstrument (int ontick, MidiFile& mfile, HumdrumFile& infile, int i, int j, int pcQ); void usage (const char* command); Array tracknamed; // for storing boolean if track is named Array trackchannel; // channel of each track ////////////////////////////////////////////////////////////////////////// int main(int argc, char* argv[]) { HumdrumFile infile; MidiFile outfile; outfile.setTicksPerQuarterNote(120); // process the command-line options checkOptions(options, argc, argv); // figure out the number of input files to process int numinputs = options.getArgCount(); for (int i=0; i gmsysex; gmsysex.setSize(6); gmsysex[0] = 0xf0; // Start of SysEx gmsysex[1] = 0x7e; // Universal (reserved) ID number gmsysex[2] = 0x7f; // Device ID (general transmission) gmsysex[3] = 0x09; // Means 'This is a message about General MIDI' gmsysex[4] = 0x01; // Means 'Turn General MIDI On'. 02 means 'Off' gmsysex[5] = 0xf7; // End of SysEx outfile.addEvent(0, 0, gmsysex); } */ storeMidiData(infile, outfile); if (outlocation == NULL) { outfile.sortTracks(); cout << outfile; } else { outfile.sortTracks(); outfile.write(outlocation); } } return 0; } ////////////////////////////////////////////////////////////////////////// ////////////////////////////// // // assignTracks -- give a track number for each spine in the input file // trying to place the same instruments into the same channels if // there are not enough channels to go around. // void assignTracks(HumdrumFile& infile, Array& trackchannel) { int i, j; Array instruments; // MIDI instruments indicated in track trackchannel.setSize(infile.getMaxTracks() + 1); instruments.setSize(trackchannel.getSize()); for (i=0; i 16) { // channel allocation is over quota: squash over-allocations: for (i=0; i 15) { trackchannel[i] = 0; } } } } } ////////////////////////////// // // checkForTempo -- // double checkForTempo(HumdrumRecord& record) { int i; float tempo = 60.0; for (i=0; i 127) { defaultvolume = 127; } tscaling = opts.getDouble("tempo-scaling"); if (tscaling < 0.001) { tscaling = 1.0; } else if (tscaling > 1000.0) { tscaling = 1.0; } instrumentQ = !opts.getBoolean("noinstrument"); if (opts.getBoolean("channel")) { multitimbreQ = 0; fixedChannel = opts.getInteger("channel") - 1; if (fixedChannel < 0) { fixedChannel = 0; } if (fixedChannel > 15) { fixedChannel = 15; } instrumentQ = 0; } else { multitimbreQ = 1; fixedChannel = -1; } if (opts.getBoolean("output")) { outlocation = opts.getString("output"); } else { outlocation = NULL; } forcedQ = opts.getBoolean("forceinstrument"); if (forcedQ) { instrumentnumber = opts.getInteger("forceinstrument"); if (instrumentnumber < 0 || instrumentnumber > 127) { instrumentnumber = 0; } } else { instrumentnumber = -1; } mine = opts.getInteger("min"); if (mine < 0) { mine = 0; } debugQ = opts.getBoolean("debug"); shortenQ = opts.getBoolean("shorten"); shortenamount = opts.getInteger("shorten"); } ////////////////////////////// // // example -- // void example(void) { } ////////////////////////////// // // storeMetaText -- // void storeMetaText(MidiFile& mfile, int track, const char* string, int tick, int metaType) { int i; int length = strlen(string); Array metadata; uchar size[23] = {0}; int lengthsize = makeVLV(size, length); metadata.setSize(2+lengthsize+length); metadata[0] = 0xff; metadata[1] = metaType; for (i=0; i= (1 << 28)) { cout << "Error: number too large to handle" << endl; exit(1); } buffer[0] = (value >> 21) & 0x7f; buffer[1] = (value >> 14) & 0x7f; buffer[2] = (value >> 7) & 0x7f; buffer[3] = (value >> 0) & 0x7f; int i; int flag = 0; int length = -1; for (i=0; i<3; i++) { if (buffer[i] != 0) { flag = 1; } if (flag) { buffer[i] |= 0x80; } if (length == -1 && buffer[i] >= 0x80) { length = 4-i; } } if (length == -1) { length = 1; } if (length < 4) { for (i=0; i mididata; int i, j, k; int tokencount = 0; char buffer1[1024] = {0}; int staccatoQ = 0; int accentQ = 0; int sforzandoQ = 0; int volume = defaultvolume; int ttempo; if (tscaling != 1.0) { ttempo = (int)(60 * tscaling); if (ttempo > 0) { mididata.setSize(6); mididata[0] = 0xff; mididata[1] = 0x51; mididata[2] = 3; ttempo = (int)(60000000.0 / ttempo + 0.5); mididata[3] = (uchar)((ttempo >> 16) & 0xff); mididata[4] = (uchar)((ttempo >> 8) & 0xff); mididata[5] = (uchar)(ttempo & 0xff); outfile.addEvent(0, 0 + offset, mididata); } } if (options.getBoolean("comment")) { storeMetaText(outfile, 0, options.getString("comment"), 0); } for (i=0; i 0) { mididata.setSize(6); mididata[0] = 0xff; mididata[1] = 0x51; mididata[2] = 3; ttempo = (int)(60000000.0 / ttempo + 0.5); mididata[3] = (uchar)((ttempo >> 16) & 0xff); mididata[4] = (uchar)((ttempo >> 8) & 0xff); mididata[5] = (uchar)(ttempo & 0xff); absbeat = infile.getAbsBeat(i); ontick = int(absbeat * outfile.getTicksPerQuarterNote()); outfile.addEvent(0, ontick + offset, mididata); outfile.addEvent(0, 10 + offset, mididata); } } for (j=0; j mididata; mididata.setSize(2); mididata[0] = 0xc0 | (0x0f & trackchannel[track]); mididata[1] = instrumentnumber; outfile.addEvent(track, ontick + offset, mididata); continue; } if (strncmp(infile[i][j], "*I", 2) == 0) { storeInstrument(ontick, outfile, infile, i, j, instrumentQ); continue; } } } else if (infile[i].getType() == E_humrec_data) { for (j=0; j 127) { volume = 127; } mididata.setSize(3); mididata[0] = 0x90 | trackchannel[track]; mididata[1] = midinote; mididata[2] = volume; outfile.addEvent(track, ontick + offset, mididata); mididata[0] = 0x80 | trackchannel[track]; outfile.addEvent(track, offtick + offset, mididata); } } } } } // now add the end-of-track message to all tracks so that they // end at the same time mididata.setSize(3); mididata[0] = 0xff; mididata[1] = 0x2f; mididata[2] = 0x00; absbeat = infile.getAbsBeat(infile.getNumLines()-1); ontick = int(absbeat * outfile.getTicksPerQuarterNote()); if (options.getBoolean("type0")) { outfile.joinTracks(); outfile.addEvent(0, ontick, mididata); } else { // type 1 MIDI file int trackcount = outfile.getNumTracks(); for (i=0; i 127) { continue; } x.setGM(humdrumname, instnum); pointer = strtok(NULL, spaces); } } ////////////////////////////// // // setMidiPlusVolume -- store slur and enharmonic spelling information. // based on definition in the book: // Beyond MIDI: The Handbook of Musical Codes. pp. 99-104. // int setMidiPlusVolume(const char* kernnote) { int output = 1 << 6; // check for slurs, but do not worry about separating multiple slurs if (strchr(kernnote, '(') != NULL) { // start of slur A output |= (1 << 2); } else if (strchr(kernnote, ')') != NULL) { // end of slur A output |= (1 << 4); } // set the accidental marking int base40class = Convert::kernToBase40(kernnote) % 40; int midinoteclass = Convert::kernToMidiNoteNumber(kernnote) % 12; int acheck = 0; switch (midinoteclass) { case 0: // C/Dbb/B# switch (base40class) { case 4: acheck = 1; break; // Dbb case 0: acheck = 2; break; // C case 36: acheck = 3; break; // B# default: acheck = 0; } break; case 1: // C#/Db/B## switch (base40class) { case 5: acheck = 1; break; // Db case 1: acheck = 2; break; // C# case 37: acheck = 3; break; // B## default: acheck = 0; } break; case 2: // D/C##/Ebb switch (base40class) { case 10: acheck = 1; break; // Ebb case 6: acheck = 2; break; // D case 2: acheck = 3; break; // C## default: acheck = 0; } break; case 3: // D#/Eb/Fbb switch (base40class) { case 15: acheck = 1; break; // Fbb case 11: acheck = 2; break; // Eb case 7: acheck = 3; break; // D# default: acheck = 0; } break; case 4: // E/Fb/D## switch (base40class) { case 16: acheck = 1; break; // Fb case 12: acheck = 2; break; // E case 8: acheck = 3; break; // D## default: acheck = 0; } break; case 5: // F/E#/Gbb switch (base40class) { case 21: acheck = 1; break; // Gbb case 17: acheck = 2; break; // F case 13: acheck = 3; break; // E# default: acheck = 0; } break; case 6: // F#/Gb/E## switch (base40class) { case 22: acheck = 1; break; // Gb case 18: acheck = 2; break; // F# case 14: acheck = 3; break; // E## default: acheck = 0; } break; case 7: // G/Abb/F## switch (base40class) { case 27: acheck = 1; break; // Abb case 23: acheck = 2; break; // G case 19: acheck = 3; break; // F## default: acheck = 0; } break; case 8: // G#/Ab/F### switch (base40class) { case 28: acheck = 1; break; // Ab case 24: acheck = 2; break; // G# default: acheck = 0; // F### } break; case 9: // A/Bbb/G## switch (base40class) { case 33: acheck = 1; break; // Bbb case 29: acheck = 2; break; // A case 25: acheck = 3; break; // G## default: acheck = 0; } break; case 10: // Bb/A#/Cbb switch (base40class) { case 38: acheck = 1; break; // Cbb case 34: acheck = 2; break; // Bb case 30: acheck = 3; break; // A# default: acheck = 0; } break; case 11: // B/Cf/A## switch (base40class) { case 39: acheck = 1; break; // Cb case 35: acheck = 2; break; // B case 31: acheck = 3; break; // A## default: acheck = 0; } break; } output |= acheck; return output; } ////////////////////////////// // // storeInstrument -- // void storeInstrument(int ontick, MidiFile& mfile, HumdrumFile& infile, int i, int j, int pcQ) { static HumdrumInstrument huminst; int track = infile[i].getPrimaryTrack(j); const char* trackname = huminst.getName(infile[i][j]); int pc = huminst.getGM(infile[i][j]); if (forcedQ) { pc = instrumentnumber; } int channel = 0x0f & trackchannel[track]; // store the program change if requested: Array mididata; mididata.setSize(2); mididata[0] = 0xc0 | channel; mididata[1] = (uchar)pc; if (pcQ && pc >= 0 && !forcedQ) { mfile.addEvent(track, ontick + offset, mididata); } if (!tracknamed[track]) { tracknamed[track] = 1; storeMetaText(mfile, track, trackname, 0, 3); // Track Name } storeMetaText(mfile, track, trackname + offset, ontick, 4); // Inst. Name } ////////////////////////////// // // usage -- // void usage(const char* command) { }