|
From: Lasse Kärkkäi. <tr...@us...> - 2011-02-25 19:17:36
|
Author: Lasse Kärkkäinen <tronic+ndrm at trn.iki.fi> Date: Fri Feb 25 20:16:54 2011 +0100 Midi export support (works on my computer). --- songwriter-ini.cc | 62 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 files changed, 58 insertions(+), 4 deletions(-) diff --git a/songwriter-ini.cc b/songwriter-ini.cc index 526b135..aa394dd 100644 --- a/songwriter-ini.cc +++ b/songwriter-ini.cc @@ -1,17 +1,71 @@ #include "songwriter.hh" #include "midifile.hh" +#include "util.hh" #include <QTextStream> - void FoFMIDIWriter::writeMIDI() const { Notes const& notes = s.getVocalTrack().notes; - unsigned division = 16; + if (notes.empty()) throw std::runtime_error("No notes"); + double tempo = s.bpm > 0 ? s.bpm : 120.0; + unsigned division = 64; // Allow for very precise timing + unsigned endtc = round(tempo / 60.0 * division * notes.back().end); midifile::Writer writer(1, 2, division); + using midifile::Event; writer.startTrack(); - // TODO: write timing info + Event ev; + ev.type = Event::SPECIAL; + ev.channel = 0x0F; + unsigned char buf[16]; + ev.begin = ev.end = buf; + if (tempo != 120.0) { // MIDI defaults to 120 BPM + ev.arg1 = Event::META_TEMPO; + ev.end = ev.begin + 3; + unsigned val = 6e+7 / tempo; // Microseconds per beat + buf[0] = val >> 16; + buf[1] = val >> 8; + buf[2] = val; + writer.writeEvent(ev); + } + // TODO: write Performous Composer and title= & artist= like EoF does + ev.arg1 = Event::META_ENDOFTRACK; + writer.writeEvent(ev); + // Vocals track begins writer.startTrack(); - // TODO: write notes + // Write track name + ev.arg1 = Event::META_SEQNAME; + std::string partvocals = "PART VOCALS"; + std::copy(partvocals.begin(), partvocals.end(), buf); + ev.end = ev.begin + partvocals.size(); + writer.writeEvent(ev); + // Write notes + unsigned timecode = 0; + for (Notes::const_iterator it = notes.begin(), itend = notes.end(); it != itend; ++it) { + // Lyric + QByteArray bytes = it->syllable.toUtf8(); + ev.timecode = round(tempo / 60.0 * division * it->begin) - timecode; + timecode += ev.timecode; + ev.type = Event::SPECIAL; + ev.channel = 0x0F; + ev.arg1 = Event::META_LYRIC; + ev.begin = reinterpret_cast<unsigned char*>(bytes.data()); + ev.end = ev.begin + bytes.size(); + writer.writeEvent(ev); + // Note begin + ev.channel = 0; + ev.timecode = 0; // Same timecode as the lyric + ev.type = Event::NOTE_ON; + ev.arg1 = it->note; + ev.arg2 = 64; + writer.writeEvent(ev); + // Note end + ev.timecode = round(tempo / 60.0 * division * it->end) - timecode; + timecode += ev.timecode; + ev.type = Event::NOTE_OFF; + ev.arg2 = 0; + writer.writeEvent(ev); + } + // Write to file QByteArray name = (path + "/notes.mid").toLocal8Bit(); writer.save(std::string(name.data(), name.size()).c_str()); } |