// Generate a single midi data list for TSE3 from all current track // tabulature. TSE3::PhraseEdit *TabTrack::midiTrack(bool tracking, int tracknum) { TSE3::PhraseEdit *midi = new TSE3::PhraseEdit(); long timer = 0; // midi timestamp for each note int midilen = 0; // midi ticks until start of next note int duration; // note duration (dead note: less than midilen) uchar pitch; // note pitch const int velocity = 0x60; // note velocity // Ugly MIDI shift between real start of notes and things like // patch/bank changes, etc. Required for stupid MIDI devices that // can't handle patch/bank change and note on event on the same tick. int midiShift = tracking ? 5 : 0; cursortimer = -1; for (uint x = 0; x < c.size(); x++) { // Calculate real duration (including all the linked beats) // for the non-ringing notes, which determines midilen // note: need to keep x unchanged, because pitch and effects are stored // in the first column of a set linked beats // remember x of the last linked beat, though int xl; // x last linked note xl = x; midilen = c[xl].fullDuration(); while ((xl + 1 < c.size()) && (c[xl + 1].flags & FLAG_ARC)) { xl++; midilen += c[xl].fullDuration(); } if (x == this->x || (cursortimer == -1 && x > this->x)) cursortimer = timer; // Note on/off events for (int i = 0; i < string; i++) { if (c[x].a[i] == -1) continue; if (c[x].a[i] == DEAD_NOTE) { pitch = tune[i]; duration = 5; } else { pitch = c[x].a[i] + tune[i]; duration = midilen; } if (c[x].flags & FLAG_PM) duration = duration / 2; // ringing overrides the duration if (c[x].e[i] == EFFECT_LETRING) { // LVIFIX: add support for notes linked to ringing note // LVIFIX: the linked note should be able to be ringing itself duration = noteDuration(x, i); } if (c[x].e[i] == EFFECT_ARTHARM) pitch += 12; if (c[x].e[i] == EFFECT_HARMONIC) { switch (c[x].a[i]) { case 3: pitch += 28; break; case 4: pitch += 24; break; case 5: pitch += 19; break; case 7: pitch += 12; break; case 9: pitch += 19; break; case 12: pitch += 0; break; case 16: pitch += 12; break; // same as 9th fret case 19: pitch += 0; break; // same as 7th fret case 24: pitch += 0; break; // same as 5th fret } } // midi->insert( // TSE3::MidiEvent(TSE3::MidiCommand(TSE3::MidiCommand_NoteOn, // channel - 1, Settings::midiPort(), // pitch, velocity), // timer + midiShift, velocity, timer + duration)); midi->insert( TSE3::MidiEvent(TSE3::MidiCommand(TSE3::MidiCommand_NoteOn, channel - 1, Settings::midiPort(), pitch, velocity), timer + midiShift)); midi->insert( TSE3::MidiEvent(TSE3::MidiCommand(TSE3::MidiCommand_NoteOff, channel - 1, Settings::midiPort(), pitch, velocity), timer + midiShift + duration - 1)); // cout << "Inserted note pitch " << (int) pitch // << ", start " << timer << ", duration " << duration << "\n"; } // for (int i = 0; i < string ... if (tracking) midi->insert(TSE3::MidiEvent(encodeTimeTracking(tracknum, x), timer)); timer += midilen; x = xl; // step over linked notes } // for (uint x = 0; x < c.size() ... if (tracking) { // GREYFIX: Workaround for TSE3 bug (?) - last event not playing midi->insert( TSE3::MidiEvent(TSE3::MidiCommand(TSE3::MidiCommand_NoteOff, 0, Settings::midiPort(), 0, 0), timer + 120) ); } // Initial setup, patches, midi volumes, choruses, etc. midi->insert(TSE3::MidiEvent(TSE3::MidiCommand( TSE3::MidiCommand_ProgramChange, channel - 1, Settings::midiPort(), patch), tracking ? cursortimer : 0) ); return midi; }
int main() { /************************************************************************** * 1. Create a Song containing a scale *************************************************************************/ // First, we create a scale in a PhraseEdit TSE3::PhraseEdit phraseEdit; int note = rootNote; TSE3::Clock time = 0; for (int n = 0; n < 8; n++) { const int delta[] = {2, 2, 1, 2, 2, 2, 1, 0}; const int velocity = 0x60; const int channel = 0; const int port = 0; phraseEdit.insert (TSE3::MidiEvent(TSE3::MidiCommand(TSE3::MidiCommand_NoteOn, channel, port, note, velocity), time, velocity, time+duration)); note += delta[n]; time += duration; } // Now assemble the Song TSE3::Song song(1); TSE3::Phrase *phrase = phraseEdit.createPhrase(song.phraseList()); TSE3::Part *part = new TSE3::Part(0, phraseEdit.lastClock()); part->setPhrase(phrase); song[0]->insert(part); /************************************************************************** * 2. Play the Song *************************************************************************/ // Create transport objects // (You really want to create a MidiScheduler for your platform, perhaps // you'll use the UnixMidiSchedulerFactory) TSE3::Metronome metronome; TSE3::Util::StreamMidiScheduler scheduler; TSE3::Transport transport(&metronome, &scheduler); // Play and wait for the end transport.play(&song, 0); while (transport.status() != TSE3::Transport::Resting) { transport.poll(); // perhaps sleep here to prevent slaughtering the CPU } /************************************************************************** * 3. Save the Song as a standard MIDI file *************************************************************************/ TSE3::MidiFileExport mfe; mfe.save("export.midi", &song); /************************************************************************** * All done - note that there is no memory leak - when the Song is deleted * (it is on the stack, so this will happen automatically) all the other * components will be deleted by the Song's destructor. *************************************************************************/ return 0; }