summaryrefslogtreecommitdiff
path: root/muse2/muse/exportmidi.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'muse2/muse/exportmidi.cpp')
-rw-r--r--muse2/muse/exportmidi.cpp379
1 files changed, 379 insertions, 0 deletions
diff --git a/muse2/muse/exportmidi.cpp b/muse2/muse/exportmidi.cpp
new file mode 100644
index 00000000..c068d719
--- /dev/null
+++ b/muse2/muse/exportmidi.cpp
@@ -0,0 +1,379 @@
+//=========================================================
+// MusE
+// Linux Music Editor
+// $Id: exportmidi.cpp,v 1.9.2.1 2009/04/01 01:37:10 terminator356 Exp $
+//
+// (C) Copyright 1999-2003 Werner Schweer (ws@seh.de)
+//=========================================================
+
+#include <stdio.h>
+#include <qstring.h>
+
+#include "app.h"
+#include "midifile.h"
+#include "midi.h"
+#include "midictrl.h"
+#include "globals.h"
+#include "filedialog.h"
+#include "track.h"
+#include "song.h"
+#include "mpevent.h"
+#include "event.h"
+#include "marker/marker.h"
+#include "drummap.h"
+#include "gconfig.h"
+
+//---------------------------------------------------------
+// addController
+//---------------------------------------------------------
+
+static void addController(MPEventList* l, int tick, int port, int channel, int a, int b)
+ {
+ // p3.3.37
+ //if (a < 0x1000) { // 7 Bit Controller
+ if (a < CTRL_14_OFFSET) { // 7 Bit Controller
+ l->add(MidiPlayEvent(tick, port, channel, ME_CONTROLLER, a, b));
+ }
+ //else if (a < 0x20000) { // 14 Bit Controller
+ else if (a < CTRL_RPN_OFFSET) { // 14 Bit Controller
+ int ctrlH = (a >> 8) & 0x7f;
+ int ctrlL = a & 0x7f;
+ int dataH = (b >> 7) & 0x7f;
+ int dataL = b & 0x7f;
+ l->add(MidiPlayEvent(tick, port, channel, ME_CONTROLLER, ctrlH, dataH));
+ l->add(MidiPlayEvent(tick+1, port, channel, ME_CONTROLLER, ctrlL, dataL));
+ }
+ //else if (a < 0x30000) { // RPN 7-Bit Controller
+ else if (a < CTRL_NRPN_OFFSET) { // RPN 7-Bit Controller
+ int ctrlH = (a >> 8) & 0x7f;
+ int ctrlL = a & 0x7f;
+ l->add(MidiPlayEvent(tick, port, channel, ME_CONTROLLER, CTRL_HRPN, ctrlH));
+ l->add(MidiPlayEvent(tick+1, port, channel, ME_CONTROLLER, CTRL_LRPN, ctrlL));
+ l->add(MidiPlayEvent(tick+2, port, channel, ME_CONTROLLER, CTRL_HDATA, b));
+ }
+ //else if (a < 0x40000) { // NRPN 7-Bit Controller
+ else if (a < CTRL_INTERNAL_OFFSET) { // NRPN 7-Bit Controller
+ int ctrlH = (a >> 8) & 0x7f;
+ int ctrlL = a & 0x7f;
+ l->add(MidiPlayEvent(tick, port, channel, ME_CONTROLLER, CTRL_HNRPN, ctrlH));
+ l->add(MidiPlayEvent(tick+1, port, channel, ME_CONTROLLER, CTRL_LNRPN, ctrlL));
+ l->add(MidiPlayEvent(tick+2, port, channel, ME_CONTROLLER, CTRL_HDATA, b));
+ }
+ else if (a == CTRL_PITCH) {
+ int a = b + 8192;
+ int b = a >> 7;
+ l->add(MidiPlayEvent(tick, port, channel, ME_PITCHBEND, a & 0x7f, b & 0x7f));
+ }
+ else if (a == CTRL_PROGRAM) {
+ int hb = (b >> 16) & 0xff;
+ int lb = (b >> 8) & 0xff;
+ int pr = b & 0x7f;
+ int tickoffset = 0;
+ switch(song->mtype()) {
+ case MT_GM: // no HBANK/LBANK
+ break;
+ case MT_GS:
+ case MT_XG:
+ case MT_UNKNOWN:
+ if (hb != 0xff) {
+ l->add(MidiPlayEvent(tick, port, channel, ME_CONTROLLER, CTRL_HBANK, hb));
+ ++tickoffset;
+ }
+ if (lb != 0xff) {
+ l->add(MidiPlayEvent(tick+tickoffset, port, channel, ME_CONTROLLER, CTRL_LBANK, lb));
+ ++tickoffset;
+ }
+ break;
+ }
+ l->add(MidiPlayEvent(tick+tickoffset, port, channel, ME_PROGRAM, pr, 0));
+ }
+ //else if (a < 0x60000) { // RPN14 Controller
+ else if (a < CTRL_NRPN14_OFFSET) { // RPN14 Controller
+ int ctrlH = (a >> 8) & 0x7f;
+ int ctrlL = a & 0x7f;
+ int dataH = (b >> 7) & 0x7f;
+ int dataL = b & 0x7f;
+ l->add(MidiPlayEvent(tick, port, channel, ME_CONTROLLER, CTRL_HRPN, ctrlH));
+ l->add(MidiPlayEvent(tick+1, port, channel, ME_CONTROLLER, CTRL_LRPN, ctrlL));
+ l->add(MidiPlayEvent(tick+2, port, channel, ME_CONTROLLER, CTRL_HDATA, dataH));
+ l->add(MidiPlayEvent(tick+3, port, channel, ME_CONTROLLER, CTRL_LDATA, dataL));
+ }
+ //else if (a < 0x70000) { // NRPN14 Controller
+ else if (a < CTRL_NONE_OFFSET) { // NRPN14 Controller
+ int ctrlH = (a >> 8) & 0x7f;
+ int ctrlL = a & 0x7f;
+ int dataH = (b >> 7) & 0x7f;
+ int dataL = b & 0x7f;
+ l->add(MidiPlayEvent(tick, port, channel, ME_CONTROLLER, CTRL_HNRPN, ctrlH));
+ l->add(MidiPlayEvent(tick+1, port, channel, ME_CONTROLLER, CTRL_LNRPN, ctrlL));
+ l->add(MidiPlayEvent(tick+2, port, channel, ME_CONTROLLER, CTRL_HDATA, dataH));
+ l->add(MidiPlayEvent(tick+3, port, channel, ME_CONTROLLER, CTRL_LDATA, dataL));
+ }
+ }
+
+//---------------------------------------------------------
+// exportMidi
+//---------------------------------------------------------
+
+void MusE::exportMidi()
+ {
+ MFile file(QString("midis"), QString(".mid"));
+
+ //FILE* fp = file.open("w", midi_file_pattern, this, false, true,
+ FILE* fp = file.open("w", midi_file_save_pattern, this, false, true,
+ tr("MusE: Export Midi"));
+ if (fp == 0)
+ return;
+ MidiFile mf(fp);
+
+ MidiTrackList* tl = song->midis();
+ int ntracks = tl->size();
+ MidiFileTrackList* mtl = new MidiFileTrackList;
+
+ int i = 0;
+ for (iMidiTrack im = tl->begin(); im != tl->end(); ++im, ++i) {
+ MidiTrack* track = *im;
+ MidiFileTrack* mft = new MidiFileTrack;
+ mtl->push_back(mft);
+ MPEventList* l = &(mft->events);
+ int port = track->outPort();
+ int channel = track->outChannel();
+
+ //---------------------------------------------------
+ // only first midi track contains
+ // - Track Marker
+ // - copyright
+ // - time signature
+ // - tempo map
+ // - GM/GS/XG Initialization
+ //---------------------------------------------------
+
+ if (i == 0) {
+ //---------------------------------------------------
+ // Write Track Marker
+ //
+ MarkerList* ml = song->marker();
+ for (ciMarker m = ml->begin(); m != ml->end(); ++m) {
+ const char* name = m->second.name().latin1();
+ int len = strlen(name);
+ MidiPlayEvent ev(m->first, port, ME_META, (unsigned char*)name, len);
+ ev.setA(0x6);
+ l->add(ev);
+ }
+
+ //---------------------------------------------------
+ // Write Copyright
+ //
+ const char* copyright = config.copyright.latin1();
+ if (copyright && *copyright) {
+ int len = strlen(copyright);
+ MidiPlayEvent ev(0, port, ME_META, (unsigned char*)copyright, len);
+ ev.setA(0x2);
+ l->add(ev);
+ }
+
+ //---------------------------------------------------
+ // Write Coment
+ //
+ QString comment = track->comment();
+ if (!comment.isEmpty()) {
+ int len = comment.length();
+ MidiPlayEvent ev(0, port, ME_META, (const unsigned char*)(comment.latin1()), len);
+ ev.setA(0x1);
+ l->add(ev);
+ }
+
+ //---------------------------------------------------
+ // Write Songtype SYSEX: GM/GS/XG
+ //
+
+ switch(song->mtype()) {
+ case MT_GM:
+ l->add(MidiPlayEvent(0, port, ME_SYSEX, gmOnMsg, gmOnMsgLen));
+ break;
+ case MT_GS:
+ l->add(MidiPlayEvent(0, port, ME_SYSEX, gmOnMsg, gmOnMsgLen));
+ l->add(MidiPlayEvent(250, port, ME_SYSEX, gsOnMsg, gsOnMsgLen));
+ break;
+ case MT_XG:
+ l->add(MidiPlayEvent(0, port, ME_SYSEX, gmOnMsg, gmOnMsgLen));
+ l->add(MidiPlayEvent(250, port, ME_SYSEX, xgOnMsg, xgOnMsgLen));
+ break;
+ case MT_UNKNOWN:
+ break;
+ }
+
+ //---------------------------------------------------
+ // Write Tempomap
+ //
+ TempoList* tl = &tempomap;
+ for (ciTEvent e = tl->begin(); e != tl->end(); ++e) {
+ TEvent* event = e->second;
+ unsigned char data[3];
+ int tempo = event->tempo;
+ data[2] = tempo & 0xff;
+ data[1] = (tempo >> 8) & 0xff;
+ data[0] = (tempo >> 16) & 0xff;
+ MidiPlayEvent ev(event->tick, port, ME_META, data, 3);
+ ev.setA(0x51);
+ l->add(ev);
+ }
+
+ //---------------------------------------------------
+ // Write Signatures
+ //
+ const SigList* sl = &sigmap;
+ for (ciSigEvent e = sl->begin(); e != sl->end(); ++e) {
+ SigEvent* event = e->second;
+ int sz = (config.exp2ByteTimeSigs ? 2 : 4); // export 2 byte timesigs instead of 4 ?
+ unsigned char data[sz];
+ data[0] = event->z;
+ switch(event->n) {
+ case 1: data[1] = 0; break;
+ case 2: data[1] = 1; break;
+ case 4: data[1] = 2; break;
+ case 8: data[1] = 3; break;
+ case 16: data[1] = 4; break;
+ case 32: data[1] = 5; break;
+ case 64: data[1] = 6; break;
+ default:
+ fprintf(stderr, "falsche Signatur; nenner %d\n", event->n);
+ break;
+ }
+ // By T356. In muse the metronome pulse is fixed at 24 (once per quarter-note).
+ // The number of 32nd notes per 24 MIDI clock signals (per quarter-note) is 8.
+ if(!config.exp2ByteTimeSigs)
+ {
+ data[2] = 24;
+ data[3] = 8;
+ }
+
+ MidiPlayEvent ev(event->tick, port, ME_META, data, sz);
+
+ ev.setA(0x58);
+ l->add(ev);
+ }
+ }
+
+ //-----------------------------------
+ // track name
+ //-----------------------------------
+
+ if (!track->name().isEmpty()) {
+ const char* name = track->name().latin1();
+ int len = strlen(name);
+ MidiPlayEvent ev(0, port, ME_META, (unsigned char*)name, len+1);
+ ev.setA(0x3); // Meta Sequence/Track Name
+ l->add(ev);
+ }
+
+ //-----------------------------------
+ // track comment
+ //-----------------------------------
+
+ if (!track->comment().isEmpty()) {
+ const char* comment = track->comment().latin1();
+ int len = strlen(comment);
+ MidiPlayEvent ev(0, port, ME_META, (unsigned char*)comment, len+1);
+ ev.setA(0xf); // Meta Text
+ l->add(ev);
+ }
+ PartList* parts = track->parts();
+ for (iPart p = parts->begin(); p != parts->end(); ++p) {
+ MidiPart* part = (MidiPart*) (p->second);
+ EventList* evlist = part->events();
+ for (iEvent i = evlist->begin(); i != evlist->end(); ++i) {
+ Event ev = i->second;
+ int tick = ev.tick() + part->tick();
+
+ switch (ev.type()) {
+ case Note:
+ {
+ if (ev.velo() == 0) {
+ printf("Warning: midi note has velocity 0, (ignored)\n");
+ continue;
+ }
+ int pitch;
+ if (track->type() == Track::DRUM) {
+ //
+ // Map drum-notes to the drum-map values
+ //
+ int instr = ev.pitch();
+ pitch = drumMap[instr].anote;
+ // port = drumMap[instr].port;
+ // channel = drumMap[instr].channel;
+ }
+ else
+ pitch = ev.pitch();
+
+ int velo = ev.velo();
+ int len = ev.lenTick();
+
+ //---------------------------------------
+ // apply trackinfo values
+ //---------------------------------------
+
+ if (track->transposition
+ || track->velocity
+ || track->compression != 100
+ || track->len != 100) {
+ pitch += track->transposition;
+ if (pitch > 127)
+ pitch = 127;
+ if (pitch < 0)
+ pitch = 0;
+
+ velo += track->velocity;
+ velo = (velo * track->compression) / 100;
+ if (velo > 127)
+ velo = 127;
+ if (velo < 1) // no off event
+ velo = 1;
+ len = (len * track->len) / 100;
+ }
+ if (len <= 0)
+ len = 1;
+ l->add(MidiPlayEvent(tick, port, channel, ME_NOTEON, pitch, velo));
+
+ if(config.expOptimNoteOffs) // Save space by replacing note offs with note on velocity 0
+ l->add(MidiPlayEvent(tick+len, port, channel, ME_NOTEON, pitch, 0));
+ else
+ l->add(MidiPlayEvent(tick+len, port, channel, ME_NOTEOFF, pitch, velo));
+ }
+ break;
+
+ case Controller:
+ addController(l, tick, port, channel, ev.dataA(), ev.dataB());
+ break;
+
+ case Sysex:
+ l->add(MidiPlayEvent(tick, port, ME_SYSEX, ev.eventData()));
+ break;
+
+ case PAfter:
+ l->add(MidiPlayEvent(tick, port, channel, ME_AFTERTOUCH, ev.dataA(), ev.dataB()));
+ break;
+
+ case CAfter:
+ l->add(MidiPlayEvent(tick, port, channel, ME_POLYAFTER, ev.dataA(), ev.dataB()));
+ break;
+
+ case Meta:
+ {
+ MidiPlayEvent mpev(tick, port, ME_META, ev.eventData());
+ mpev.setA(ev.dataA());
+ l->add(mpev);
+ }
+ break;
+ case Wave:
+ break;
+ }
+ }
+ }
+ }
+ mf.setDivision(config.midiDivision);
+ mf.setMType(song->mtype());
+ mf.setTrackList(mtl, ntracks);
+ mf.write();
+ }
+