diff options
Diffstat (limited to 'muse2')
53 files changed, 1430 insertions, 737 deletions
diff --git a/muse2/AUTHORS b/muse2/AUTHORS index 22d70210..db045180 100644 --- a/muse2/AUTHORS +++ b/muse2/AUTHORS @@ -9,7 +9,7 @@ Name: user: <user> <at> users <dot> sourceforge <dot> net Active Developers: Robert Jonsson spamatica -Tim Donnelly terminator356 +Tim (E Real) Donnelly terminator356 Orcan Ogetbil ogetbilo Florian Jung flo93 diff --git a/muse2/ChangeLog b/muse2/ChangeLog index 07625080..9494ba7d 100644 --- a/muse2/ChangeLog +++ b/muse2/ChangeLog @@ -1,3 +1,23 @@ +09.10.2012: + - Fixed old fluidsynth bug: Drum patch numbers incorrect. (Tim) + Confusion due to special fluidsynth drum bank = 129. Changed FluidSynth::getFirstPatch, + ::getNextPatch, and CTRL_PROGRAM section of ::setController. + - Midi track info patches popup menu: Don't display category if empty. (Tim) +08.10.2012: + * !!! Song type is now removed !!! : (Tim) + TODO: Instrument init sequences. Need some kind of editor. Need timing mechanism while in 'stop' mode. + Was thinking these events should be in FRAMES or real time - not affected by tempo. Jack slow sync + callback might help. (Also try to re-enable metronome precount - similar concept here!) + * Improved: Multi-port (aka multi-channel) midi import and export, via Device Name, Port, or Instrument metas. (Tim) + - MusE can now export a midi file and re-import exactly as it was, all ports + devices + instruments incl. + - Extra options for import/export added. + - Import midi now passes 'unabsorbed' meta events on to the track. They were discarded before - lost forever. + - Solved: Export midi now stores an instrument's Init sequence, instead of the old song type sysexes. + - MPEventList now has highly customized same-time sorting, helps 'cement' import/export events in place, such + as controllers or program changes before notes, instrument metas before mode sysexes. (EventList NOT done.) + - Old bug: Fixed superfluous NULLS in meta texts in midi export. + - Old bug, annoying: Midi Import: parts are too short and 'events past end' indicator is on. + - TODO: Bug: Deicsonze: Midi track info patch popup: Screen all black until esc or click. 04.10.2012: - Added Gain knob on channel strip for audio tracks (rj) - Added descriptive tooltips for midi parameters in midi track info (rj) diff --git a/muse2/muse/arranger/arranger.cpp b/muse2/muse/arranger/arranger.cpp index 8d2328c3..942a5013 100644 --- a/muse2/muse/arranger/arranger.cpp +++ b/muse2/muse/arranger/arranger.cpp @@ -268,18 +268,6 @@ Arranger::Arranger(ArrangerView* parent, const char* name) toolbar->addWidget(lenEntry); connect(lenEntry, SIGNAL(valueChanged(int)), SLOT(songlenChanged(int))); - typeBox = new LabelCombo(tr("Type"), 0); - typeBox->insertItem(0, tr("NO")); - typeBox->insertItem(1, tr("GM")); - typeBox->insertItem(2, tr("GS")); - typeBox->insertItem(3, tr("XG")); - typeBox->setCurrentIndex(0); - typeBox->setToolTip(tr("midi song type")); - typeBox->setWhatsThis(tr("midi song type")); - typeBox->setFocusPolicy(Qt::TabFocus); - toolbar->addWidget(typeBox); - connect(typeBox, SIGNAL(activated(int)), SLOT(modeChange(int))); - label = new QLabel(tr("Pitch")); label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); label->setIndent(3); @@ -668,9 +656,6 @@ void Arranger::songChanged(MusECore::SongChangedFlags_t type) lenEntry->blockSignals(false); } - if(type & SC_SONG_TYPE) - setMode(MusEGlobal::song->mtype()); - if(type & (SC_SELECTION | SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED)) trackSelectionChanged(); @@ -739,29 +724,6 @@ void Arranger::trackSelectionChanged() } //--------------------------------------------------------- -// modeChange -//--------------------------------------------------------- - -void Arranger::modeChange(int mode) - { - MusEGlobal::song->setMType(MType(mode)); - updateTrackInfo(-1); - focusCanvas(); - } - -//--------------------------------------------------------- -// setMode -//--------------------------------------------------------- - -void Arranger::setMode(int mode) - { - typeBox->blockSignals(true); - // This will only set if different. - typeBox->setCurrentIndex(mode); - typeBox->blockSignals(false); - } - -//--------------------------------------------------------- // writeStatus //--------------------------------------------------------- diff --git a/muse2/muse/arranger/arranger.h b/muse2/muse/arranger/arranger.h index dd2c5290..e7d48d70 100644 --- a/muse2/muse/arranger/arranger.h +++ b/muse2/muse/arranger/arranger.h @@ -135,7 +135,6 @@ class Arranger : public QWidget { MusECore::Track* selected; - LabelCombo* typeBox; QToolButton* ib; int trackInfoType; Splitter* split; @@ -159,7 +158,6 @@ class Arranger : public QWidget { void trackSelectionChanged(); void trackInfoScroll(int); void songChanged(MusECore::SongChangedFlags_t); - void modeChange(int); void setTime(unsigned); void globalPitchChanged(int); void globalTempoChanged(int); @@ -216,7 +214,6 @@ class Arranger : public QWidget { Arranger(ArrangerView* parent, const char* name = 0); PartCanvas* getCanvas() { return canvas; } - void setMode(int); void reset(); void writeStatus(int level, MusECore::Xml&); diff --git a/muse2/muse/arranger/arrangerview.cpp b/muse2/muse/arranger/arrangerview.cpp index 66e3bbbb..a2024159 100644 --- a/muse2/muse/arranger/arrangerview.cpp +++ b/muse2/muse/arranger/arrangerview.cpp @@ -388,8 +388,7 @@ void ArrangerView::songChanged(MusECore::SongChangedFlags_t type) //SC_SIG | SC_TEMPO | SC_MASTER | //SC_MIDI_TRACK_PROP | SC_CONFIG | - SC_DRUMMAP| - SC_SONG_TYPE)) + SC_DRUMMAP)) visTracks->updateVisibleTracksButtons(); } diff --git a/muse2/muse/arranger/tlist.cpp b/muse2/muse/arranger/tlist.cpp index d16a8d29..dc65b0c1 100644 --- a/muse2/muse/arranger/tlist.cpp +++ b/muse2/muse/arranger/tlist.cpp @@ -492,7 +492,7 @@ void TList::paint(const QRect& r) MusECore::MidiInstrument* instr = mp->instrument(); QString name; if (val!=MusECore::CTRL_VAL_UNKNOWN) - name = instr->getPatchName(mt->outChannel(), val, MusEGlobal::song->mtype(), mt->isDrumTrack()); + name = instr->getPatchName(mt->outChannel(), val, mt->isDrumTrack()); else name = tr("<unknown>"); @@ -2123,8 +2123,8 @@ void TList::mousePressEvent(QMouseEvent* ev) else { MusECore::MidiInstrument* instr = mp->instrument(); - if (delta>0) val=instr->getNextPatch(mt->outChannel(), val, MusEGlobal::song->mtype(), false); - else if (delta<0) val=instr->getPrevPatch(mt->outChannel(), val, MusEGlobal::song->mtype(), false); + if (delta>0) val=instr->getNextPatch(mt->outChannel(), val, false); + else if (delta<0) val=instr->getPrevPatch(mt->outChannel(), val, false); } if (val != oldval) @@ -2169,7 +2169,7 @@ void TList::mousePressEvent(QMouseEvent* ev) MusECore::MidiInstrument* instr = mp->instrument(); PopupMenu* pup = new PopupMenu(true); - instr->populatePatchPopup(pup, mt->outChannel(), MusEGlobal::song->mtype(), mt->isDrumTrack()); + instr->populatePatchPopup(pup, mt->outChannel(), mt->isDrumTrack()); if(pup->actions().count() == 0) { @@ -2623,8 +2623,8 @@ void TList::wheelEvent(QWheelEvent* ev) else { MusECore::MidiInstrument* instr = mp->instrument(); - if (delta>0) val=instr->getNextPatch(mt->outChannel(), val, MusEGlobal::song->mtype(), false); - else if (delta<0) val=instr->getPrevPatch(mt->outChannel(), val, MusEGlobal::song->mtype(), false); + if (delta>0) val=instr->getNextPatch(mt->outChannel(), val, false); + else if (delta<0) val=instr->getPrevPatch(mt->outChannel(), val, false); } if (val != oldval) diff --git a/muse2/muse/audio.cpp b/muse2/muse/audio.cpp index 6349971b..b03366e1 100644 --- a/muse2/muse/audio.cpp +++ b/muse2/muse/audio.cpp @@ -702,7 +702,7 @@ void Audio::processMsg(AudioMsg* msg) for (int i = 0; i < MIDI_PORTS; ++i) { if(MusEGlobal::midiPorts[i].device()) - MusEGlobal::midiPorts[i].instrument()->reset(i, MusEGlobal::song->mtype()); + MusEGlobal::midiPorts[i].instrument()->reset(i); } break; case SEQM_INIT_DEVICES: diff --git a/muse2/muse/conf.cpp b/muse2/muse/conf.cpp index 37959f0f..21a50e3d 100644 --- a/muse2/muse/conf.cpp +++ b/muse2/muse/conf.cpp @@ -804,6 +804,28 @@ void readConfiguration(Xml& xml, bool doReadMidiPortConfig, bool doReadGlobalCon MusEGlobal::config.expOptimNoteOffs = xml.parseInt(); else if (tag == "importMidiSplitParts") MusEGlobal::config.importMidiSplitParts = xml.parseInt(); + else if (tag == "importDevNameMetas") + MusEGlobal::config.importDevNameMetas = xml.parseInt(); + else if (tag == "importInstrNameMetas") + MusEGlobal::config.importInstrNameMetas = xml.parseInt(); + else if (tag == "exportPortsDevices") + { + int i = xml.parseInt(); + if(i >= MusEGlobal::EXPORT_PORTS_DEVICES_END) + i = MusEGlobal::EXPORT_PORTS_DEVICES_ALL; + MusEGlobal::config.exportPortsDevices = MusEGlobal::ExportPortsDevices_t(i); + } + else if (tag == "exportPortDeviceSMF0") + MusEGlobal::config.exportPortDeviceSMF0 = xml.parseInt(); + else if (tag == "exportModeInstr") + { + int i = xml.parseInt(); + if(i >= MusEGlobal::EXPORT_MODE_INSTR_END) + i = MusEGlobal::EXPORT_MODE_INSTR_ALL; + MusEGlobal::config.exportModeInstr = MusEGlobal::ExportModeInstr_t(i); + } + else if (tag == "importMidiDefaultInstr") + MusEGlobal::config.importMidiDefaultInstr = xml.parse1(); else if (tag == "showSplashScreen") MusEGlobal::config.showSplashScreen = xml.parseInt(); @@ -1087,7 +1109,7 @@ static void writeSeqConfiguration(int level, Xml& xml, bool writePortInfo) // or ALSA reorders or renames devices etc etc, then we have at least kept the track <-> port routes. mport->defaultInChannels() != (1<<MIDI_CHANNELS)-1 || // p4.0.17 Default is now to connect to all channels. mport->defaultOutChannels() || - (!mport->instrument()->iname().isEmpty() && mport->instrument()->iname() != "GM") || + (!mport->instrument()->iname().isEmpty() && mport->instrument()->midiType() != MT_GM) || !mport->syncInfo().isDefault()) used = true; else @@ -1114,7 +1136,8 @@ static void writeSeqConfiguration(int level, Xml& xml, bool writePortInfo) xml.intTag(level, "defaultOutChans", mport->defaultOutChannels()); if(!mport->instrument()->iname().isEmpty() && // Tim. - (mport->instrument()->iname() != "GM")) // FIXME: TODO: Make this user configurable. + (mport->instrument()->iname() != "GM")) // FIXME: TODO: Make this user configurable. + //(mport->instrument()->midiType() != MT_GM)) xml.strTag(level, "instrument", mport->instrument()->iname()); if (dev) { @@ -1203,6 +1226,12 @@ void MusE::writeGlobalConfiguration(int level, MusECore::Xml& xml) const xml.intTag(level, "exp2ByteTimeSigs", MusEGlobal::config.exp2ByteTimeSigs); xml.intTag(level, "expOptimNoteOffs", MusEGlobal::config.expOptimNoteOffs); xml.intTag(level, "importMidiSplitParts", MusEGlobal::config.importMidiSplitParts); + xml.intTag(level, "importDevNameMetas", MusEGlobal::config.importDevNameMetas); + xml.intTag(level, "importInstrNameMetas", MusEGlobal::config.importInstrNameMetas); + xml.intTag(level, "exportPortsDevices", MusEGlobal::config.exportPortsDevices); + xml.intTag(level, "exportPortDeviceSMF0", MusEGlobal::config.exportPortDeviceSMF0); + xml.intTag(level, "exportModeInstr", MusEGlobal::config.exportModeInstr); + xml.strTag(level, "importMidiDefaultInstr", MusEGlobal::config.importMidiDefaultInstr); xml.intTag(level, "startMode", MusEGlobal::config.startMode); xml.strTag(level, "startSong", MusEGlobal::config.startSong); xml.intTag(level, "startSongLoadConfig", MusEGlobal::config.startSongLoadConfig); @@ -1478,6 +1507,18 @@ MidiFileConfig::MidiFileConfig(QWidget* parent) void MidiFileConfig::updateValues() { + importDefaultInstr->clear(); + for(MusECore::iMidiInstrument i = MusECore::midiInstruments.begin(); i != MusECore::midiInstruments.end(); ++i) + if(!dynamic_cast<MusECore::SynthI*>(*i)) // Sorry, no synths for now. + importDefaultInstr->addItem((*i)->iname()); + int idx = importDefaultInstr->findText(MusEGlobal::config.importMidiDefaultInstr); + if(idx != -1) + importDefaultInstr->setCurrentIndex(idx); + + QString defInstr = importDefaultInstr->currentText(); + if(!defInstr.isEmpty()) + MusEGlobal::config.importMidiDefaultInstr = defInstr; + int divisionIdx = 2; switch(MusEGlobal::config.midiDivision) { case 96: divisionIdx = 0; break; @@ -1493,6 +1534,37 @@ void MidiFileConfig::updateValues() splitPartsCheckBox->setChecked(MusEGlobal::config.importMidiSplitParts); newDrumsCheckbox->setChecked(MusEGlobal::config.importMidiNewStyleDrum); oldDrumsCheckbox->setChecked(!MusEGlobal::config.importMidiNewStyleDrum); + importDevNameMetas->setChecked(MusEGlobal::config.importDevNameMetas); + importInstrNameMetas->setChecked(MusEGlobal::config.importInstrNameMetas); + exportPortDeviceSMF0->setChecked(MusEGlobal::config.exportPortDeviceSMF0); + switch(MusEGlobal::config.exportPortsDevices) + { + case MusEGlobal::PORT_NUM_META: + exportPortMetas->setChecked(true); + break; + case MusEGlobal::DEVICE_NAME_META: + exportDeviceNameMetas->setChecked(true); + break; + case MusEGlobal::EXPORT_PORTS_DEVICES_ALL: + exportPortAndDeviceNameMetas->setChecked(true); + break; + default: + printf("MidiFileConfig::updateValues FIXME: Unknown exportPortsDevices type\n"); + } + switch(MusEGlobal::config.exportModeInstr) + { + case MusEGlobal::MODE_SYSEX: + exportModeSysexes->setChecked(true); + break; + case MusEGlobal::INSTRUMENT_NAME_META: + exportInstrumentNames->setChecked(true); + break; + case MusEGlobal::EXPORT_MODE_INSTR_ALL: + exportModeAndInstrName->setChecked(true); + break; + default: + printf("MidiFileConfig::updateValues FIXME: Unknown exportModeInstr type\n"); + } } //--------------------------------------------------------- @@ -1501,6 +1573,10 @@ void MidiFileConfig::updateValues() void MidiFileConfig::okClicked() { + QString defInstr = importDefaultInstr->currentText(); + if(!defInstr.isEmpty()) + MusEGlobal::config.importMidiDefaultInstr = defInstr; + int divisionIdx = divisionCombo->currentIndex(); int divisions[3] = { 96, 192, 384 }; @@ -1513,7 +1589,24 @@ void MidiFileConfig::okClicked() MusEGlobal::config.exp2ByteTimeSigs = twoByteTimeSigs->isChecked(); MusEGlobal::config.importMidiSplitParts = splitPartsCheckBox->isChecked(); MusEGlobal::config.importMidiNewStyleDrum = newDrumsCheckbox->isChecked(); - + + MusEGlobal::config.importDevNameMetas = importDevNameMetas->isChecked(); + MusEGlobal::config.importInstrNameMetas = importInstrNameMetas->isChecked(); + MusEGlobal::config.exportPortDeviceSMF0 = exportPortDeviceSMF0->isChecked(); + if(exportPortMetas->isChecked()) + MusEGlobal::config.exportPortsDevices = MusEGlobal::PORT_NUM_META; + else if(exportDeviceNameMetas->isChecked()) + MusEGlobal::config.exportPortsDevices = MusEGlobal::DEVICE_NAME_META; + else if(exportPortAndDeviceNameMetas->isChecked()) + MusEGlobal::config.exportPortsDevices = MusEGlobal::EXPORT_PORTS_DEVICES_ALL; + + if(exportModeSysexes->isChecked()) + MusEGlobal::config.exportModeInstr = MusEGlobal::MODE_SYSEX; + else if(exportInstrumentNames->isChecked()) + MusEGlobal::config.exportModeInstr = MusEGlobal::INSTRUMENT_NAME_META; + else if(exportModeAndInstrName->isChecked()) + MusEGlobal::config.exportModeInstr = MusEGlobal::EXPORT_MODE_INSTR_ALL; + MusEGlobal::muse->changeConfig(true); // write config file close(); } diff --git a/muse2/muse/ctrl/ctrlcanvas.cpp b/muse2/muse/ctrl/ctrlcanvas.cpp index 8d4bc8a3..1b243204 100644 --- a/muse2/muse/ctrl/ctrlcanvas.cpp +++ b/muse2/muse/ctrl/ctrlcanvas.cpp @@ -943,12 +943,7 @@ void CtrlCanvas::newValRamp(int x1, int y1, int x2, int y2) if (type == MusECore::CTRL_PROGRAM) { if (lastpv == MusECore::CTRL_VAL_UNKNOWN) - { - if (MusEGlobal::song->mtype() == MT_GM) - event.setB(0xffff00 | (nval - 1)); - else - event.setB(nval - 1); - } + event.setB(nval - 1); else event.setB((lastpv & 0xffff00) | (nval - 1)); } @@ -991,11 +986,7 @@ void CtrlCanvas::changeValRamp(int x1, int y1, int x2, int y2) if (type == MusECore::CTRL_PROGRAM) { if (event.dataB() == MusECore::CTRL_VAL_UNKNOWN) - { --nval; - if(MusEGlobal::song->mtype() == MT_GM) - nval |= 0xffff00; - } else nval = (event.dataB() & 0xffff00) | (nval - 1); } @@ -1066,11 +1057,7 @@ void CtrlCanvas::changeVal(int x1, int x2, int y) if(type == MusECore::CTRL_PROGRAM) { if(event.dataB() == MusECore::CTRL_VAL_UNKNOWN) - { --nval; - if(MusEGlobal::song->mtype() == MT_GM) - nval |= 0xffff00; - } else nval = (event.dataB() & 0xffff00) | (nval - 1); } @@ -1167,11 +1154,7 @@ void CtrlCanvas::newVal(int x1, int y) if(event.dataB() == MusECore::CTRL_VAL_UNKNOWN) { if(lastpv == MusECore::CTRL_VAL_UNKNOWN) - { --nval; - if(MusEGlobal::song->mtype() == MT_GM) - nval |= 0xffff00; - } else nval = (lastpv & 0xffff00) | (nval - 1); } @@ -1238,12 +1221,7 @@ void CtrlCanvas::newVal(int x1, int y) if(type == MusECore::CTRL_PROGRAM) { if(lastpv == MusECore::CTRL_VAL_UNKNOWN) - { - if(MusEGlobal::song->mtype() == MT_GM) - event.setB(0xffff00 | (newval - 1)); - else - event.setB(newval - 1); - } + event.setB(newval - 1); else event.setB((lastpv & 0xffff00) | (newval - 1)); } @@ -1411,12 +1389,7 @@ void CtrlCanvas::newVal(int x1, int y1, int x2, int y2) if(type == MusECore::CTRL_PROGRAM) { if(lastpv == MusECore::CTRL_VAL_UNKNOWN) - { - if(MusEGlobal::song->mtype() == MT_GM) - event.setB(0xffff00 | (nval - 1)); - else - event.setB(nval - 1); - } + event.setB(nval - 1); else event.setB((lastpv & 0xffff00) | (nval - 1)); } @@ -1900,7 +1873,12 @@ void CtrlCanvas::setCurDrumPitch(int instrument) curDrumPitch = instrument; else // new style drummap mode { - if (drumedit->get_instrument_map()[instrument].tracks.contains(curTrack)) + // Crash protection by Tim. + // FIXME: Still, drum list is blank, editor can't edit. Other values of instrument or curDrumPitch just crash too. + // Seems only with drum tracks that were created by importing a midi file (then changed to use fluidsynth device?). + if(instrument == -1) curDrumPitch = -1; + + else if (drumedit->get_instrument_map()[instrument].tracks.contains(curTrack)) curDrumPitch = drumedit->get_instrument_map()[instrument].pitch; else curDrumPitch = -2; // this means "invalid", but not "unused" diff --git a/muse2/muse/driver/alsamidi.cpp b/muse2/muse/driver/alsamidi.cpp index e3e71365..d8b3a947 100644 --- a/muse2/muse/driver/alsamidi.cpp +++ b/muse2/muse/driver/alsamidi.cpp @@ -426,8 +426,8 @@ bool MidiAlsaDevice::putMidiEvent(const MidiPlayEvent& e) event.type = SND_SEQ_EVENT_STOP; break; default: - printf("MidiAlsaDevice::putEvent(): event type %d not implemented\n", - e.type()); + if(MusEGlobal::debugMsg) + printf("MidiAlsaDevice::putEvent(): event type %d not implemented\n", e.type()); return true; } return putEvent(&event); diff --git a/muse2/muse/driver/jackmidi.cpp b/muse2/muse/driver/jackmidi.cpp index e3e67dfb..6b1506e1 100644 --- a/muse2/muse/driver/jackmidi.cpp +++ b/muse2/muse/driver/jackmidi.cpp @@ -752,7 +752,6 @@ bool MidiJackDevice::queueEvent(const MidiPlayEvent& e) if(MusEGlobal::debugMsg) printf("MidiJackDevice::queueEvent: event type %x not supported\n", e.type()); return true; // Absorb the event. Don't want it hanging around in the list. - break; } return true; diff --git a/muse2/muse/dssihost.cpp b/muse2/muse/dssihost.cpp index 01f9c0f3..c76631e4 100644 --- a/muse2/muse/dssihost.cpp +++ b/muse2/muse/dssihost.cpp @@ -2114,7 +2114,7 @@ void DssiSynthIF::doSelectProgram(LADSPA_Handle handle, int bank, int prog) // getPatchName //--------------------------------------------------------- -const char* DssiSynthIF::getPatchName(int /*chan*/, int prog, MType /*type*/, bool /*drum*/) +const char* DssiSynthIF::getPatchName(int /*chan*/, int prog, bool /*drum*/) { unsigned program = prog & 0x7f; int lbank = (prog >> 8) & 0xff; @@ -2138,7 +2138,7 @@ const char* DssiSynthIF::getPatchName(int /*chan*/, int prog, MType /*type*/, bo // populatePatchPopup //--------------------------------------------------------- -void DssiSynthIF::populatePatchPopup(MusEGui::PopupMenu* menu, int /*ch*/, MType /*type*/, bool /*drum*/) +void DssiSynthIF::populatePatchPopup(MusEGui::PopupMenu* menu, int /*ch*/, bool /*drum*/) { // The plugin can change the programs, patches etc. // So make sure we're up to date by calling queryPrograms. diff --git a/muse2/muse/dssihost.h b/muse2/muse/dssihost.h index 93038011..baa6adbe 100644 --- a/muse2/muse/dssihost.h +++ b/muse2/muse/dssihost.h @@ -171,9 +171,8 @@ class DssiSynthIF : public SynthIF, public PluginIBase virtual void deactivate3() {} - virtual const char* getPatchName(int, int, int, bool) const { return ""; } - virtual const char* getPatchName(int, int, MType, bool); - virtual void populatePatchPopup(MusEGui::PopupMenu*, int, MType, bool); + virtual const char* getPatchName(int, int, bool); + virtual void populatePatchPopup(MusEGui::PopupMenu*, int, bool); virtual void write(int level, Xml& xml) const; diff --git a/muse2/muse/exportmidi.cpp b/muse2/muse/exportmidi.cpp index 8f5af4c5..8e10884c 100644 --- a/muse2/muse/exportmidi.cpp +++ b/muse2/muse/exportmidi.cpp @@ -4,6 +4,7 @@ // $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) +// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -23,9 +24,9 @@ #include <stdio.h> #include <QString> +#include <QMessageBox> #include "al/sig.h" // Tim. - #include "app.h" #include "midifile.h" #include "midi.h" @@ -83,12 +84,13 @@ static void addController(MPEventList* l, int tick, int port, int channel, int a int lb = (b >> 8) & 0xff; int pr = b & 0x7f; int tickoffset = 0; - switch(MusEGlobal::song->mtype()) { - case MT_GM: // no HBANK/LBANK - break; - case MT_GS: - case MT_XG: - case MT_UNKNOWN: + // REMOVE Tim. Song type removal. + //switch(MusEGlobal::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; @@ -97,8 +99,8 @@ static void addController(MPEventList* l, int tick, int port, int channel, int a l->add(MidiPlayEvent(tick+tickoffset, port, channel, ME_CONTROLLER, CTRL_LBANK, lb)); ++tickoffset; } - break; - } + // break; + // } l->add(MidiPlayEvent(tick+tickoffset, port, channel, ME_PROGRAM, pr, 0)); } else if (a < CTRL_NRPN14_OFFSET) { // RPN14 Controller @@ -123,6 +125,109 @@ static void addController(MPEventList* l, int tick, int port, int channel, int a } } +//--------------------------------------------------------- +// addEventList +// part can be NULL meaning no part used. +// track can be NULL meaning no concept of drum notes is allowed in init sequences. +//--------------------------------------------------------- + +static void addEventList(MusECore::EventList* evlist, MusECore::MPEventList* mpevlist, MusECore::MidiTrack* track, MusECore::Part* part, int port, int channel) +{ + for (MusECore::iEvent i = evlist->begin(); i != evlist->end(); ++i) + { + MusECore::Event ev = i->second; + int tick = ev.tick(); + if(part) + tick += part->tick(); + switch (ev.type()) + { + case MusECore::Note: + { + if (ev.velo() == 0) { + printf("Warning: midi note has velocity 0, (ignored)\n"); + continue; + } + int pitch; + if (track && track->type() == MusECore::Track::DRUM) { + // Map drum-notes to the drum-map values + int instr = ev.pitch(); + pitch = MusEGlobal::drumMap[instr].anote; + } + else + pitch = ev.pitch(); + + int velo = ev.velo(); + int len = ev.lenTick(); + + //--------------------------------------- + // apply trackinfo values + //--------------------------------------- + + if (track && (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; + mpevlist->add(MusECore::MidiPlayEvent(tick, port, channel, MusECore::ME_NOTEON, pitch, velo)); + + if(MusEGlobal::config.expOptimNoteOffs) // Save space by replacing note offs with note on velocity 0 + mpevlist->add(MusECore::MidiPlayEvent(tick+len, port, channel, MusECore::ME_NOTEON, pitch, 0)); + else + mpevlist->add(MusECore::MidiPlayEvent(tick+len, port, channel, MusECore::ME_NOTEOFF, pitch, velo)); + } + break; + + case MusECore::Controller: + addController(mpevlist, tick, port, channel, ev.dataA(), ev.dataB()); + break; + + case MusECore::Sysex: + { + mpevlist->add(MusECore::MidiPlayEvent(tick, port, MusECore::ME_SYSEX, ev.eventData())); + //MusECore::MidiPlayEvent ev(tick, port, MusECore::ME_SYSEX, ev.eventData()); + //ev.setChannel(channel); // Sysex are channelless, but this is required for sorting! + //mpevlist->add(ev); + } + break; + + case MusECore::PAfter: + mpevlist->add(MusECore::MidiPlayEvent(tick, port, channel, MusECore::ME_AFTERTOUCH, ev.dataA(), ev.dataB())); + break; + + case MusECore::CAfter: + mpevlist->add(MusECore::MidiPlayEvent(tick, port, channel, MusECore::ME_POLYAFTER, ev.dataA(), ev.dataB())); + break; + + case MusECore::Meta: + { + MusECore::MidiPlayEvent mpev(tick, port, MusECore::ME_META, ev.eventData()); + mpev.setA(ev.dataA()); + //mpev.setChannel(channel); // Metas are channelless, but this is required for sorting! + mpevlist->add(mpev); + } + break; + case MusECore::Wave: + break; + } + } +} + + } // namespace MusECore namespace MusEGui { @@ -133,6 +238,34 @@ namespace MusEGui { void MusE::exportMidi() { + if(MusEGlobal::config.smfFormat == 0) // Want single track? Warn if multiple ports in song... + { + MusECore::MidiTrackList* mtl = MusEGlobal::song->midis(); + int prev_port = -1; + for(MusECore::ciMidiTrack im = mtl->begin(); im != mtl->end(); ++im) + { + int port = (*im)->outPort(); + if(prev_port == -1) + { + prev_port = port; + continue; + } + if(port != prev_port) + { + if(QMessageBox::warning(this, + tr("MusE: Warning"), + tr("The song uses multiple ports but export format 0 (single track) is set.\n" + "The first track's port will be used. Playback will likely be wrong\n" + " unless the channels used in one port are different from all other ports.\n" + "Canceling and setting a different export format would be better.\nContinue?"), + QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) + != QMessageBox::Ok) + return; + break; + } + } + } + MusEGui::MFile file(QString("midis"), QString(".mid")); FILE* fp = file.open("w", MusEGlobal::midi_file_save_pattern, this, false, true, @@ -144,6 +277,8 @@ void MusE::exportMidi() MusECore::TrackList* tl = MusEGlobal::song->tracks(); // Changed to full track list so user can rearrange tracks. MusECore::MidiFileTrackList* mtl = new MusECore::MidiFileTrackList; + std::set<int> used_ports; + int i = 0; MusECore::MidiFileTrack* mft = 0; for (MusECore::ciTrack im = tl->begin(); im != tl->end(); ++im) { @@ -153,7 +288,7 @@ void MusE::exportMidi() MusECore::MidiTrack* track = (MusECore::MidiTrack*)(*im); - if (i == 0 || (i != 0 && MusEGlobal::config.smfFormat != 0)) // Changed to single track. Tim + if (i == 0 || MusEGlobal::config.smfFormat != 0) // Changed to single track. Tim { mft = new MusECore::MidiFileTrack; mtl->push_back(mft); @@ -162,14 +297,13 @@ void MusE::exportMidi() MusECore::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) { @@ -181,8 +315,8 @@ void MusE::exportMidi() QByteArray ba = m->second.name().toLatin1(); const char* name = ba.constData(); int len = ba.length(); - MusECore::MidiPlayEvent ev(m->first, port, MusECore::ME_META, (unsigned char*)name, len); - ev.setA(0x6); + MusECore::MidiPlayEvent ev(m->first, port, MusECore::ME_META, (const unsigned char*)name, len); + ev.setA(MusECore::ME_META_TEXT_6_MARKER); l->add(ev); } @@ -193,8 +327,8 @@ void MusE::exportMidi() const char* copyright = ba.constData(); if (copyright && *copyright) { int len = ba.length(); - MusECore::MidiPlayEvent ev(0, port, MusECore::ME_META, (unsigned char*)copyright, len); - ev.setA(0x2); + MusECore::MidiPlayEvent ev(0, port, MusECore::ME_META, (const unsigned char*)copyright, len); + ev.setA(MusECore::ME_META_TEXT_2_COPYRIGHT); l->add(ev); } @@ -203,36 +337,21 @@ void MusE::exportMidi() // //if (MusEGlobal::config.smfFormat == 0) // Only for smf 0 added by Tim. FIXME: Is this correct? See below. { - QString comment = track->comment(); - if (!comment.isEmpty()) { - int len = comment.length(); - MusECore::MidiPlayEvent ev(0, port, MusECore::ME_META, (const unsigned char*)(comment.toLatin1().constData()), len); - ev.setA(0x1); + //QString comment = track->comment(); + //if (!comment.isEmpty()) { + if (!track->comment().isEmpty()) { + //int len = comment.length(); + QByteArray ba = track->comment().toLatin1(); + const char* comment = ba.constData(); + int len = ba.length(); + //MusECore::MidiPlayEvent ev(0, port, MusECore::ME_META, (const unsigned char*)(comment.toLatin1().constData()), len); + MusECore::MidiPlayEvent ev(0, port, MusECore::ME_META, (const unsigned char*)comment, len); + ev.setA(MusECore::ME_META_TEXT_1_COMMENT); l->add(ev); } } //--------------------------------------------------- - // Write Songtype SYSEX: GM/GS/XG - // - - switch(MusEGlobal::song->mtype()) { - case MT_GM: - l->add(MusECore::MidiPlayEvent(0, port, MusECore::ME_SYSEX, MusECore::gmOnMsg, MusECore::gmOnMsgLen)); - break; - case MT_GS: - l->add(MusECore::MidiPlayEvent(0, port, MusECore::ME_SYSEX, MusECore::gmOnMsg, MusECore::gmOnMsgLen)); - l->add(MusECore::MidiPlayEvent(250, port, MusECore::ME_SYSEX, MusECore::gsOnMsg, MusECore::gsOnMsgLen)); - break; - case MT_XG: - l->add(MusECore::MidiPlayEvent(0, port, MusECore::ME_SYSEX, MusECore::gmOnMsg, MusECore::gmOnMsgLen)); - l->add(MusECore::MidiPlayEvent(250, port, MusECore::ME_SYSEX, MusECore::xgOnMsg, MusECore::xgOnMsgLen)); - break; - case MT_UNKNOWN: - break; - } - - //--------------------------------------------------- // Write Tempomap // MusECore::TempoList* tl = &MusEGlobal::tempomap; @@ -244,7 +363,7 @@ void MusE::exportMidi() data[1] = (tempo >> 8) & 0xff; data[0] = (tempo >> 16) & 0xff; MusECore::MidiPlayEvent ev(event->tick, port, MusECore::ME_META, data, 3); - ev.setA(0x51); + ev.setA(MusECore::ME_META_SET_TEMPO); l->add(ev); } @@ -279,7 +398,7 @@ void MusE::exportMidi() MusECore::MidiPlayEvent ev(event->tick, port, MusECore::ME_META, data, sz); - ev.setA(0x58); + ev.setA(MusECore::ME_META_TIME_SIGNATURE); l->add(ev); } } @@ -288,14 +407,15 @@ void MusE::exportMidi() // track name //----------------------------------- - if (i == 0 || (i != 0 && MusEGlobal::config.smfFormat != 0)) + if (i == 0 || MusEGlobal::config.smfFormat != 0) { if (!track->name().isEmpty()) { QByteArray ba = track->name().toLatin1(); const char* name = ba.constData(); int len = ba.length(); - MusECore::MidiPlayEvent ev(0, port, MusECore::ME_META, (unsigned char*)name, len+1); - ev.setA(0x3); // Meta Sequence/Track Name + MusECore::MidiPlayEvent ev(0, port, MusECore::ME_META, (const unsigned char*)name, len); + ev.setA(MusECore::ME_META_TEXT_3_TRACK_NAME); // Meta Sequence/Track Name + //ev.setChannel(channel); // Metas are channelless, but this is required for sorting! l->add(ev); } } @@ -310,104 +430,112 @@ void MusE::exportMidi() QByteArray ba = track->comment().toLatin1(); const char* comment = ba.constData(); int len = ba.length(); - MusECore::MidiPlayEvent ev(0, port, MusECore::ME_META, (unsigned char*)comment, len+1); - ev.setA(0xf); // Meta Text + MusECore::MidiPlayEvent ev(0, port, MusECore::ME_META, (const unsigned char*)comment, len); + ev.setA(MusECore::ME_META_TEXT_F_TRACK_COMMENT); // Meta Text + //ev.setChannel(channel); // Metas are channelless, but this is required for sorting! l->add(ev); } } + //----------------------------------------- + // Write device name or port change meta + //----------------------------------------- + + if((i == 0 && MusEGlobal::config.exportPortDeviceSMF0) || (MusEGlobal::config.smfFormat != 0)) + { + if(port >= 0 && port < MIDI_PORTS) + { + if(MusEGlobal::config.exportPortsDevices == MusEGlobal::EXPORT_PORTS_DEVICES_ALL || + MusEGlobal::config.exportPortsDevices == MusEGlobal::PORT_NUM_META) + { + unsigned char port_char = (unsigned char)port; + MusECore::MidiPlayEvent ev(0, port, MusECore::ME_META, &port_char, 1); + ev.setA(MusECore::ME_META_PORT_CHANGE); // Meta port change + //ev.setChannel(channel); // Metas are channelless, but this is required for sorting! + l->add(ev); + } + + if(MusEGlobal::config.exportPortsDevices == MusEGlobal::EXPORT_PORTS_DEVICES_ALL || + MusEGlobal::config.exportPortsDevices == MusEGlobal::DEVICE_NAME_META) + { + MusECore::MidiDevice* dev = MusEGlobal::midiPorts[port].device(); + const char* str; + int len; + QByteArray ba; + if(dev && !dev->name().isEmpty()) + ba = dev->name().toLatin1(); + else + ba = QString::number(port).toLatin1(); + str = ba.constData(); + len = ba.length(); + MusECore::MidiPlayEvent ev(0, port, MusECore::ME_META, (const unsigned char*)str, len); + ev.setA(MusECore::ME_META_TEXT_9_DEVICE_NAME); // Meta Device Name + //ev.setChannel(channel); // Metas are channelless, but this is required for sorting! + l->add(ev); + } + } + } + + //--------------------------------------------------- + // Write midi port init sequence: GM/GS/XG etc. + // and Instrument Name meta. + //--------------------------------------------------- + + std::set<int>::iterator iup = used_ports.find(port); + if(iup == used_ports.end()) + { + if(port >= 0 && port < MIDI_PORTS) + { + MusECore::MidiInstrument* instr = MusEGlobal::midiPorts[port].instrument(); + if(instr) + { + if(i == 0 || MusEGlobal::config.smfFormat != 0) + { + //-------------------------- + // Port midi init sequence + //-------------------------- + if(MusEGlobal::config.exportModeInstr == MusEGlobal::EXPORT_MODE_INSTR_ALL || + MusEGlobal::config.exportModeInstr == MusEGlobal::MODE_SYSEX) + { + MusECore::EventList* el = instr->midiInit(); + if(!el->empty()) + MusECore::addEventList(el, l, NULL, NULL, port, channel); // No track or part passed for init sequences + } + + //-------------------------- + // Instrument Name meta + //-------------------------- + if(!instr->iname().isEmpty() && + (MusEGlobal::config.exportModeInstr == MusEGlobal::EXPORT_MODE_INSTR_ALL || + MusEGlobal::config.exportModeInstr == MusEGlobal::INSTRUMENT_NAME_META)) + { + const char* str; + int len; + QByteArray ba = instr->iname().toLatin1(); + str = ba.constData(); + len = ba.length(); + MusECore::MidiPlayEvent ev(0, port, MusECore::ME_META, (const unsigned char*)str, len); + ev.setA(MusECore::ME_META_TEXT_4_INSTRUMENT_NAME); // Meta Instrument Name + //ev.setChannel(channel); // Metas are channelless, but this is required for sorting! + l->add(ev); + } + } + } + used_ports.insert(port); + } + } + MusECore::PartList* parts = track->parts(); for (MusECore::iPart p = parts->begin(); p != parts->end(); ++p) { MusECore::MidiPart* part = (MusECore::MidiPart*) (p->second); MusECore::EventList* evlist = part->events(); - for (MusECore::iEvent i = evlist->begin(); i != evlist->end(); ++i) { - MusECore::Event ev = i->second; - int tick = ev.tick() + part->tick(); - switch (ev.type()) { - case MusECore::Note: - { - if (ev.velo() == 0) { - printf("Warning: midi note has velocity 0, (ignored)\n"); - continue; - } - int pitch; - if (track->type() == MusECore::Track::DRUM) { - // Map drum-notes to the drum-map values - int instr = ev.pitch(); - pitch = MusEGlobal::drumMap[instr].anote; - } - 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(MusECore::MidiPlayEvent(tick, port, channel, MusECore::ME_NOTEON, pitch, velo)); - - if(MusEGlobal::config.expOptimNoteOffs) // Save space by replacing note offs with note on velocity 0 - l->add(MusECore::MidiPlayEvent(tick+len, port, channel, MusECore::ME_NOTEON, pitch, 0)); - else - l->add(MusECore::MidiPlayEvent(tick+len, port, channel, MusECore::ME_NOTEOFF, pitch, velo)); - } - break; - - case MusECore::Controller: - addController(l, tick, port, channel, ev.dataA(), ev.dataB()); - break; - - case MusECore::Sysex: - l->add(MusECore::MidiPlayEvent(tick, port, MusECore::ME_SYSEX, ev.eventData())); - break; - - case MusECore::PAfter: - l->add(MusECore::MidiPlayEvent(tick, port, channel, MusECore::ME_AFTERTOUCH, ev.dataA(), ev.dataB())); - break; - - case MusECore::CAfter: - l->add(MusECore::MidiPlayEvent(tick, port, channel, MusECore::ME_POLYAFTER, ev.dataA(), ev.dataB())); - break; - - case MusECore::Meta: - { - MusECore::MidiPlayEvent mpev(tick, port, MusECore::ME_META, ev.eventData()); - mpev.setA(ev.dataA()); - l->add(mpev); - } - break; - case MusECore::Wave: - break; - } - } + MusECore::addEventList(evlist, l, track, part, port, channel); } - ++i; - + + ++i; } + mf.setDivision(MusEGlobal::config.midiDivision); - mf.setMType(MusEGlobal::song->mtype()); mf.setTrackList(mtl, i); mf.write(); diff --git a/muse2/muse/gconfig.cpp b/muse2/muse/gconfig.cpp index 302007b3..ff5545ef 100644 --- a/muse2/muse/gconfig.cpp +++ b/muse2/muse/gconfig.cpp @@ -141,6 +141,12 @@ GlobalConfigValues config = { true, // optimize midi export file note offs true, // Split imported tracks into multiple parts. true, // importMidiNewStyleDrum + true, // importDevNameMetas Import Prefer Device Name metas over port number metas if both exist. + true, // importInstrNameMetas Import Prefer Instrument Name metas over Mode sysexes if both exist. + EXPORT_PORTS_DEVICES_ALL, // exportPortsDevices Export port number metas and/or device name metas. + true, // exportPortDeviceSMF0 Export a port and/or device meta even for SMF0. + EXPORT_MODE_INSTR_ALL, // exportModeInstr Export mode sysexes and/or instrument name metas. + QString("GM"), // importMidiDefaultInstr Default to this instrument not Generic, if no match found 1, // startMode QString(""), // start song path false, // startSongLoadConfig diff --git a/muse2/muse/gconfig.h b/muse2/muse/gconfig.h index ead9f258..a9d2c0a0 100644 --- a/muse2/muse/gconfig.h +++ b/muse2/muse/gconfig.h @@ -54,6 +54,23 @@ enum drumTrackPreference_t ONLY_OLD = 2, ONLY_NEW = 3 }; + +enum ExportPortsDevices_t +{ + EXPORT_PORTS_DEVICES_ALL = 0, + PORT_NUM_META = 1, + DEVICE_NAME_META = 2, + EXPORT_PORTS_DEVICES_END = 3 +}; + +enum ExportModeInstr_t +{ + EXPORT_MODE_INSTR_ALL = 0, + MODE_SYSEX = 1, + INSTRUMENT_NAME_META = 2, + EXPORT_MODE_INSTR_END = 3 +}; + //--------------------------------------------------------- // MixerConfig //--------------------------------------------------------- @@ -133,6 +150,12 @@ struct GlobalConfigValues { bool expOptimNoteOffs; // Save space by replacing note offs with note on velocity 0 bool importMidiSplitParts; // Split imported tracks into multiple parts. bool importMidiNewStyleDrum; // Use new style drum tracks + bool importDevNameMetas; // Import Prefer Device Name metas over port number metas if both exist. + bool importInstrNameMetas; // Import Prefer Instrument Name metas over Mode sysexes if both exist. + ExportPortsDevices_t exportPortsDevices; // Export port number metas and/or device name metas. + bool exportPortDeviceSMF0; // Export a port and/or device meta even for SMF0. + ExportModeInstr_t exportModeInstr; // Export mode sysexes and/or instrument name metas. + QString importMidiDefaultInstr; // Default to this instrument not Generic, if no match found int startMode; // 0 - start with last song // 1 - start with default template diff --git a/muse2/muse/importmidi.cpp b/muse2/muse/importmidi.cpp index 4f5871d6..73f2e071 100644 --- a/muse2/muse/importmidi.cpp +++ b/muse2/muse/importmidi.cpp @@ -4,6 +4,7 @@ // $Id: importmidi.cpp,v 1.26.2.10 2009/11/05 03:14:35 terminator356 Exp $ // // (C) Copyright 1999-2003 Werner Schweer (ws@seh.de) +// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -36,6 +37,7 @@ #include "midi.h" #include "midifile.h" #include "midiport.h" +#include "midiseq.h" #include "transport.h" #include "arranger.h" #include "mpevent.h" @@ -117,33 +119,130 @@ bool MusE::importMidi(const QString name, bool merge) QMessageBox::critical(this, QString("MusE"), s); return rv; } + + MusECore::MidiFileTrackList* etl = mf.trackList(); + int division = mf.division(); + + // Find the default instrument, we may need it later... + MusECore::MidiInstrument* def_instr = 0; + if(!MusEGlobal::config.importMidiDefaultInstr.isEmpty()) + { + for(MusECore::iMidiInstrument i = MusECore::midiInstruments.begin(); i != MusECore::midiInstruments.end(); ++i) + { + if((*i)->iname() == MusEGlobal::config.importMidiDefaultInstr) + { + def_instr = *i; + break; + } + } + } + // - // evaluate song Type (GM, XG, GS, unknown) + // Need to set up ports and instruments first // - MType t = MusEGlobal::song->mtype(); - if (!merge) { - t = mf.mtype(); - MusEGlobal::song->setMType(t); - } - MusECore::MidiInstrument* instr = 0; - for (MusECore::iMidiInstrument i = MusECore::midiInstruments.begin(); i != MusECore::midiInstruments.end(); ++i) { - MusECore::MidiInstrument* mi = *i; - if ((mi->iname() == "GM" && ((t == MT_UNKNOWN) || (t == MT_GM))) - || ((mi->iname() == "GS") && (t == MT_GS)) - || ((mi->iname() == "XG") && (t == MT_XG))) { - instr = mi; - break; - } - } - if (instr == 0) { - // the standard instrument files (GM, GS, XG) must be present - printf("no instrument, type %d\n", t); - abort(); - } + + MusECore::MidiFilePortMap* usedPortMap = mf.usedPortMap(); + bool dev_changed = false; + for(MusECore::iMidiFilePort imp = usedPortMap->begin(); imp != usedPortMap->end(); ++imp) + { + MType midi_type = imp->second._midiType; + QString instr_name = MusEGlobal::config.importInstrNameMetas ? imp->second._instrName : QString(); + MusECore::MidiInstrument* typed_instr = 0; + MusECore::MidiInstrument* named_instr = 0; + // Find a typed instrument and a named instrument, if requested + for(MusECore::iMidiInstrument i = MusECore::midiInstruments.begin(); i != MusECore::midiInstruments.end(); ++i) + { + MusECore::MidiInstrument* mi = *i; + if(midi_type != MT_UNKNOWN && midi_type == mi->midiType()) + typed_instr = mi; + if(!instr_name.isEmpty() && instr_name == mi->iname()) + named_instr = mi; + if((typed_instr && named_instr) || ((typed_instr && instr_name.isEmpty()) || (named_instr && midi_type == MT_UNKNOWN))) + break; // Done searching + } - MusECore::MidiFileTrackList* etl = mf.trackList(); - int division = mf.division(); + int port = imp->first; + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; + MusECore::MidiDevice* md = mp->device(); + // Take care of assigning devices to empty ports here rather than in midifile. + //if(MusEGlobal::config.importDevNameMetas) // TODO + { + if(!md) + { + QString dev_name = imp->second._subst4DevName; + md = MusEGlobal::midiDevices.find(dev_name); // Find any type of midi device - HW, synth etc. + if(md) + { + // TEST: Hopefully shouldn't require any routing saves/restorations as in midi port config set device name... + MusEGlobal::midiSeq->msgSetMidiDevice(mp, md); + // TEST: Hopefully can get away with this ouside the loop below... + //MusEGlobal::muse->changeConfig(true); // save configuration file + //MusEGlobal::audio->msgUpdateSoloStates(); + //MusEGlobal::song->update(); + dev_changed = true; + } + else + printf("importMidi error: assign to empty port: device not found: %s\n", dev_name.toLatin1().constData()); + } + } + MusECore::MidiInstrument* instr = 0; + // Priority is exact named instrument over a typed instrument. + // This allows a named instrument plus a typed sysex, and the name will take priority. + // But it is possible that named mismatches may occur. So this named/typed order is user-selectable. + if(named_instr && (!typed_instr || MusEGlobal::config.importInstrNameMetas)) + { + instr = named_instr; + if(MusEGlobal::debugMsg) + printf("port:%d named instrument found:%s\n", + port, instr->iname().toLatin1().constData()); + } + else if(typed_instr) + { + instr = typed_instr; + if(MusEGlobal::debugMsg) + printf("port:%d typed instrument found:%s\n", + port, instr->iname().toLatin1().constData()); + } + else if(def_instr) + { + instr = def_instr; + if(MusEGlobal::debugMsg) + printf("port:%d no named or typed instrument found. Using default:%s\n", + port, instr->iname().toLatin1().constData()); + } + else + { + instr = MusECore::genericMidiInstrument; + if(MusEGlobal::debugMsg) + printf("port:%d no named, typed, or default instrument found! Using:%s\n", + port, instr->iname().toLatin1().constData()); + } + + // If the instrument is one of the three standard GM, GS, or XG, mark the usedPort as "ch 10 is drums". + // Otherwise it's anybody's guess what channel(s) drums are on. + // Code is a bit HACKISH just to accomplish passing this bool value to the next stage, where tracks are created. + if(instr->midiType() != MT_UNKNOWN) + imp->second._isStandardDrums = true; + + // Set the device's instrument - ONLY for non-synths because they provide their own. + if(!md || (md->deviceType() != MusECore::MidiDevice::SYNTH_MIDI)) + { + // this overwrites any instrument set for this port: + if(mp->instrument() != instr) + mp->setInstrument(instr); + } + } + + if(dev_changed) + { + // TEST: Hopefully can get away with this here instead of inside the loop above... + // TEST: Are these really necessary as in midi port config set device name? + MusEGlobal::muse->changeConfig(true); // save configuration file + MusEGlobal::audio->msgUpdateSoloStates(); // + MusEGlobal::song->update(); + } + // // create MidiTrack and copy events to ->events() // - combine note on/off events @@ -177,7 +276,7 @@ bool MusE::importMidi(const QString name, bool merge) already_processed.insert(pair<int,int>(channel, port)); MusECore::MidiTrack* track = new MusECore::MidiTrack(); - if ((*t)->isDrumTrack) + if ((*t)->_isDrumTrack) { if (MusEGlobal::config.importMidiNewStyleDrum) track->setType(MusECore::Track::NEW_DRUM); @@ -188,10 +287,8 @@ bool MusE::importMidi(const QString name, bool merge) track->setOutChannel(channel); track->setOutPort(port); - MusECore::MidiPort* mport = &MusEGlobal::midiPorts[track->outPort()]; - // this overwrites any instrument set for this port: - mport->setInstrument(instr); - + MusECore::MidiPort* mport = &MusEGlobal::midiPorts[port]; + //MusECore::MidiInstrument* instr = mport->instrument(); MusECore::EventList* mel = track->events(); buildMidiEventList(mel, el, track, division, first, false); // Don't do loops. first = false; @@ -199,7 +296,9 @@ bool MusE::importMidi(const QString name, bool merge) // Comment Added by T356. // Hmm. buildMidiEventList already takes care of this. // But it seems to work. How? Must test. - if (channel == 9 && MusEGlobal::song->mtype() != MT_UNKNOWN) { + //if (channel == 9 && instr->midiType() != MT_UNKNOWN) { + MusECore::ciMidiFilePort imp = usedPortMap->find(port); + if(imp != usedPortMap->end() && imp->second._isStandardDrums && channel == 9) { // A bit HACKISH, see above if (MusEGlobal::config.importMidiNewStyleDrum) track->setType(MusECore::Track::NEW_DRUM); else @@ -246,7 +345,7 @@ bool MusE::importMidi(const QString name, bool merge) MusEGlobal::song->insertTrack0(track, -1); } } - + if (!merge) { MusECore::TrackList* tl = MusEGlobal::song->tracks(); if (!tl->empty()) { @@ -324,8 +423,7 @@ void MusE::processTrack(MusECore::MidiTrack* track) for (int bar = 0; bar < bar2; ++bar, x1 = x2) { x2 = AL::sigmap.bar2tick(bar+1, 0, 0); - if (lastOff > x2) { - // this measure is busy! + if (lastOff > x2) { continue; } MusECore::iEvent i1 = tevents->lower_bound(x1); @@ -335,7 +433,7 @@ void MusE::processTrack(MusECore::MidiTrack* track) if (st != -1) { MusECore::MidiPart* part = new MusECore::MidiPart(track); part->setTick(st); - part->setLenTick(x1-st); + part->setLenTick((lastOff > x1 ? x2 : x1) - st); part->setName(partname); pl->add(part); st = -1; @@ -495,48 +593,6 @@ void MusE::importPart() //--------------------------------------------------------- void MusE::importPartToTrack(QString& filename, unsigned tick, MusECore::Track* track) { - // DELETETHIS 41 - // Changed by T356 - /* - bool popenFlag = false; - FILE* fp = MusEGui::fileOpen(this, filename, ".mpt", "r", popenFlag, false, false); - - if(fp) - { - MusECore::MidiPart* importedPart = new MusECore::MidiPart((MusECore::MidiTrack*)track); - MusECore::Xml tmpXml = MusECore::Xml(fp); - - MusECore::Xml::Token token = tmpXml.parse(); - const QString& tag = tmpXml.s1(); - if (token == MusECore::Xml::TagStart && tag == "part") - { - // Make a backup of the current clone list, to retain any 'copy' items, - // so that pasting works properly after. - MusECore::CloneList copyCloneList = MusEGlobal::cloneList; - // Clear the clone list to prevent any dangerous associations with - // current non-original parts. - MusEGlobal::cloneList.clear(); - - importedPart->read(tmpXml); - importedPart->setTick(tick); - - // Restore backup of the clone list, to retain any 'copy' items, - // so that pasting works properly after. - MusEGlobal::cloneList.clear(); - MusEGlobal::cloneList = copyCloneList; - - MusEGlobal::audio->msgAddPart(importedPart); - } - else - { - printf("Unknown tag: %s\n", tag.toLatin1().constData()); - } - fclose(fp); - } - return; - */ - - bool popenFlag = false; FILE* fp = MusEGui::fileOpen(this, filename, ".mpt", "r", popenFlag, false, false); diff --git a/muse2/muse/instruments/editinstrument.cpp b/muse2/muse/instruments/editinstrument.cpp index 8378bd68..57c466b8 100644 --- a/muse2/muse/instruments/editinstrument.cpp +++ b/muse2/muse/instruments/editinstrument.cpp @@ -79,6 +79,14 @@ EditInstrument::EditInstrument(QWidget* parent, Qt::WFlags fl) toolBar->addAction(QWhatsThis::createAction(this)); Help->addAction(QWhatsThis::createAction(this)); + // REMOVE Tim. OBSOLETE. When gui boxes are finally removed. + checkBoxGM->setEnabled(false); + checkBoxGS->setEnabled(false); + checkBoxXG->setEnabled(false); + checkBoxGM->setVisible(false); + checkBoxGS->setVisible(false); + checkBoxXG->setVisible(false); + // populate instrument list // Populate common controller list. for(int i = 0; i < 128; ++i) @@ -1405,9 +1413,10 @@ void EditInstrument::patchChanged() spinBoxLBank->setEnabled(false); spinBoxProgram->setEnabled(false); checkBoxDrum->setEnabled(false); - checkBoxGM->setEnabled(false); - checkBoxGS->setEnabled(false); - checkBoxXG->setEnabled(false); + // REMOVE Tim. OBSOLETE. When gui boxes are finally removed. + //checkBoxGM->setEnabled(false); + //checkBoxGS->setEnabled(false); + //checkBoxXG->setEnabled(false); return; } @@ -1420,9 +1429,10 @@ void EditInstrument::patchChanged() spinBoxLBank->setEnabled(true); spinBoxProgram->setEnabled(true); checkBoxDrum->setEnabled(true); - checkBoxGM->setEnabled(true); - checkBoxGS->setEnabled(true); - checkBoxXG->setEnabled(true); + // REMOVE Tim. OBSOLETE. When gui boxes are finally removed. + //checkBoxGM->setEnabled(true); + //checkBoxGS->setEnabled(true); + //checkBoxXG->setEnabled(true); int hb = ((p->hbank + 1) & 0xff); int lb = ((p->lbank + 1) & 0xff); @@ -1431,9 +1441,10 @@ void EditInstrument::patchChanged() spinBoxLBank->setValue(lb); spinBoxProgram->setValue(pr); checkBoxDrum->setChecked(p->drum); - checkBoxGM->setChecked(p->typ & 1); - checkBoxGS->setChecked(p->typ & 2); - checkBoxXG->setChecked(p->typ & 4); + // REMOVE Tim. OBSOLETE. When gui boxes are finally removed. + //checkBoxGM->setChecked(p->typ & 1); + //checkBoxGS->setChecked(p->typ & 2); + //checkBoxXG->setChecked(p->typ & 4); } else // The item is a patch group item. @@ -1443,9 +1454,10 @@ void EditInstrument::patchChanged() spinBoxLBank->setEnabled(false); spinBoxProgram->setEnabled(false); checkBoxDrum->setEnabled(false); - checkBoxGM->setEnabled(false); - checkBoxGS->setEnabled(false); - checkBoxXG->setEnabled(false); + // REMOVE Tim. OBSOLETE. When gui boxes are finally removed. + //checkBoxGM->setEnabled(false); + //checkBoxGS->setEnabled(false); + //checkBoxXG->setEnabled(false); } } @@ -2550,7 +2562,7 @@ void EditInstrument::newPatchClicked() patch->hbank = hb; patch->lbank = lb; patch->prog = prg; - patch->typ = -1; + //patch->typ = -1; patch->drum = false; if(selpatch) @@ -2558,7 +2570,7 @@ void EditInstrument::newPatchClicked() hb = selpatch->hbank; lb = selpatch->lbank; prg = selpatch->prog; - patch->typ = selpatch->typ; + //patch->typ = selpatch->typ; patch->drum = selpatch->drum; } @@ -2638,9 +2650,10 @@ void EditInstrument::newPatchClicked() spinBoxLBank->setEnabled(true); spinBoxProgram->setEnabled(true); checkBoxDrum->setEnabled(true); - checkBoxGM->setEnabled(true); - checkBoxGS->setEnabled(true); - checkBoxXG->setEnabled(true); + // REMOVE Tim. OBSOLETE. When gui boxes are finally removed. + //checkBoxGM->setEnabled(true); + //checkBoxGS->setEnabled(true); + //checkBoxXG->setEnabled(true); oldPatchItem = 0; patchChanged(); @@ -2706,9 +2719,10 @@ void EditInstrument::newGroupClicked() spinBoxLBank->setEnabled(false); spinBoxProgram->setEnabled(false); checkBoxDrum->setEnabled(false); - checkBoxGM->setEnabled(false); - checkBoxGS->setEnabled(false); - checkBoxXG->setEnabled(false); + // REMOVE Tim. OBSOLETE. When gui boxes are finally removed. + //checkBoxGM->setEnabled(false); + //checkBoxGS->setEnabled(false); + //checkBoxXG->setEnabled(false); workingInstrument.setDirty(true); } @@ -2957,23 +2971,24 @@ void EditInstrument::updatePatch(MusECore::MidiInstrument* instrument, MusECore: } // there is no logical xor in c++ - bool a = p->typ & 1; - bool b = p->typ & 2; - bool c = p->typ & 4; - bool aa = checkBoxGM->isChecked(); - bool bb = checkBoxGS->isChecked(); - bool cc = checkBoxXG->isChecked(); - if ((a ^ aa) || (b ^ bb) || (c ^ cc)) { - int value = 0; - if (checkBoxGM->isChecked()) - value |= 1; - if (checkBoxGS->isChecked()) - value |= 2; - if (checkBoxXG->isChecked()) - value |= 4; - p->typ = value; - instrument->setDirty(true); - } +// REMOVE Tim. OBSOLETE. When gui boxes are finally removed. +// bool a = p->typ & 1; +// bool b = p->typ & 2; +// bool c = p->typ & 4; +// bool aa = checkBoxGM->isChecked(); +// bool bb = checkBoxGS->isChecked(); +// bool cc = checkBoxXG->isChecked(); +// if ((a ^ aa) || (b ^ bb) || (c ^ cc)) { +// int value = 0; +// if (checkBoxGM->isChecked()) +// value |= 1; +// if (checkBoxGS->isChecked()) +// value |= 2; +// if (checkBoxXG->isChecked()) +// value |= 4; +// p->typ = value; +// instrument->setDirty(true); +// } } //--------------------------------------------------------- diff --git a/muse2/muse/instruments/minstrument.cpp b/muse2/muse/instruments/minstrument.cpp index 196ce513..e2477e60 100644 --- a/muse2/muse/instruments/minstrument.cpp +++ b/muse2/muse/instruments/minstrument.cpp @@ -49,8 +49,6 @@ namespace MusECore { MidiInstrumentList midiInstruments; MidiInstrument* genericMidiInstrument; -static const char* gmdrumname = "GM-drums"; - //--------------------------------------------------------- // string2sysex // Return -1 if cannot be converted. @@ -369,7 +367,7 @@ MidiInstrument::~MidiInstrument() MidiInstrument& MidiInstrument::assign(const MidiInstrument& ins) { //--------------------------------------------------------- - // TODO: Copy the _initScript, and _midiInit, _midiReset, and _midiState lists. + // TODO: Copy the _initScript (if and when it is ever used) //--------------------------------------------------------- for(iMidiController i = _controller->begin(); i != _controller->end(); ++i) @@ -399,6 +397,10 @@ MidiInstrument& MidiInstrument::assign(const MidiInstrument& ins) _sysex.append(new MusECore::SysEx(*(ins.sysex().at(i)))); } + *(_midiInit) = *(ins._midiInit); + *(_midiReset) = *(ins._midiReset); + *(_midiState) = *(ins._midiState); + for (ciPatchGroup g = pg.begin(); g != pg.end(); ++g) { PatchGroup* pgp = *g; @@ -424,7 +426,7 @@ MidiInstrument& MidiInstrument::assign(const MidiInstrument& ins) { Patch* pp = *p; Patch* np = new Patch; - np->typ = pp->typ; + //np->typ = pp->typ; np->hbank = pp->hbank; np->lbank = pp->lbank; np->prog = pp->prog; @@ -448,11 +450,26 @@ MidiInstrument& MidiInstrument::assign(const MidiInstrument& ins) } //--------------------------------------------------------- +// midiType +//--------------------------------------------------------- + +MType MidiInstrument::midiType() const +{ + if(_name == "GM") + return MT_GM; + if(_name == "GS") + return MT_GS; + if(_name == "XG") + return MT_XG; + return MT_UNKNOWN; +} + +//--------------------------------------------------------- // reset // send note off to all channels //--------------------------------------------------------- -void MidiInstrument::reset(int portNo, MType) +void MidiInstrument::reset(int portNo) { MusECore::MidiPort* port = &MusEGlobal::midiPorts[portNo]; if(port->device() == 0) // p4.0.15 @@ -517,7 +534,7 @@ void PatchGroup::read(Xml& xml) void Patch::read(Xml& xml) { - typ = -1; + //typ = -1; hbank = -1; lbank = -1; prog = 0; @@ -535,8 +552,11 @@ void Patch::read(Xml& xml) case Xml::Attribut: if (tag == "name") name = xml.s2(); - else if (tag == "mode") - typ = xml.s2().toInt(); + else if (tag == "mode") // Obsolete + { + //typ = xml.s2().toInt(); + xml.s2().toInt(); + } else if (tag == "hbank") hbank = xml.s2().toInt(); else if (tag == "lbank") @@ -562,8 +582,8 @@ void Patch::read(Xml& xml) void Patch::write(int level, Xml& xml) { xml.nput(level, "<Patch name=\"%s\"", Xml::xmlString(name).toLatin1().constData()); - if(typ != -1) - xml.nput(" mode=\"%d\"", typ); + //if(typ != -1) + // xml.nput(" mode=\"%d\"", typ); // Obsolete if(hbank != -1) xml.nput(" hbank=\"%d\"", hbank); @@ -982,10 +1002,6 @@ void MidiInstrument::write(int level, Xml& xml) } xml.put(">"); - // ------------- - // TODO: What about Init, Reset, State, and InitScript ? - // ------------- - level++; for (ciPatchGroup g = pg.begin(); g != pg.end(); ++g) { PatchGroup* pgp = *g; @@ -1006,6 +1022,15 @@ void MidiInstrument::write(int level, Xml& xml) _sysex.at(i)->write(level, xml); } + xml.tag(level++, "Init"); + for(ciEvent ev=_midiInit->begin(); ev != _midiInit->end(); ++ev) + ev->second.write(level, xml, MusECore::Pos(0, true)); + xml.etag(--level, "Init"); + + // ------------- + // TODO: What about _midiReset, _midiState, and _initScript ? + // ------------- + writeDrummaps(level, xml); level--; @@ -1019,45 +1044,30 @@ void MidiInstrument::write(int level, Xml& xml) // populatePatchPopup //--------------------------------------------------------- -void MidiInstrument::populatePatchPopup(MusEGui::PopupMenu* menu, int chan, MType songType, bool drum) +void MidiInstrument::populatePatchPopup(MusEGui::PopupMenu* menu, int /*chan*/, bool drum) { menu->clear(); - int mask = 0; - bool drumchan = chan == 9; - switch (songType) { - case MT_XG: mask = 4; break; - case MT_GS: mask = 2; break; - case MT_GM: - if(drumchan) - { - int id = (0xff << 16) + (0xff << 8) + 0x00; // First patch - QAction* act = menu->addAction(gmdrumname); - act->setData(id); - return; - } - mask = 1; - break; - case MT_UNKNOWN: mask = 7; break; - } + //int mask = 7; + if (pg.size() > 1) { for (ciPatchGroup i = pg.begin(); i != pg.end(); ++i) { PatchGroup* pgp = *i; - MusEGui::PopupMenu* pm = new MusEGui::PopupMenu(pgp->name, menu, menu->stayOpen()); // Use the parent stayOpen here. - menu->addMenu(pm); - pm->setFont(MusEGlobal::config.fonts[0]); + MusEGui::PopupMenu* pm = 0; const PatchList& pl = pgp->patches; for (ciPatch ipl = pl.begin(); ipl != pl.end(); ++ipl) { const Patch* mp = *ipl; - if ((mp->typ & mask) && - ((drum && songType != MT_GM) || - (mp->drum == drumchan)) ) - { + if (//(mp->typ & mask) && + (mp->drum == drum)) { + if(!pm) { + pm = new MusEGui::PopupMenu(pgp->name, menu, menu->stayOpen()); // Use the parent stayOpen here. + menu->addMenu(pm); + pm->setFont(MusEGlobal::config.fonts[0]); + } int id = ((mp->hbank & 0xff) << 16) + ((mp->lbank & 0xff) << 8) + (mp->prog & 0xff); QAction* act = pm->addAction(mp->name); act->setData(id); } - } } } @@ -1066,12 +1076,12 @@ void MidiInstrument::populatePatchPopup(MusEGui::PopupMenu* menu, int chan, MTyp const PatchList& pl = pg.front()->patches; for (ciPatch ipl = pl.begin(); ipl != pl.end(); ++ipl) { const Patch* mp = *ipl; - if (mp->typ & mask) { + //if (mp->typ & mask) { int id = ((mp->hbank & 0xff) << 16) + ((mp->lbank & 0xff) << 8) + (mp->prog & 0xff); QAction* act = menu->addAction(mp->name); act->setData(id); - } + //} } } @@ -1083,7 +1093,7 @@ void MidiInstrument::populatePatchPopup(MusEGui::PopupMenu* menu, int chan, MTyp // getPatchName //--------------------------------------------------------- -QString MidiInstrument::getPatchName(int channel, int prog, MType mode, bool drum) +QString MidiInstrument::getPatchName(int /*channel*/, int prog, bool drum) { int pr = prog & 0xff; if(prog == CTRL_VAL_UNKNOWN || pr == 0xff) @@ -1091,38 +1101,17 @@ QString MidiInstrument::getPatchName(int channel, int prog, MType mode, bool dru int hbank = (prog >> 16) & 0xff; int lbank = (prog >> 8) & 0xff; - int tmask = 1; - bool drumchan = channel == 9; - bool hb = false; - bool lb = false; - switch (mode) { - case MT_GS: - tmask = 2; - hb = true; - break; - case MT_XG: - hb = true; - lb = true; - tmask = 4; - break; - case MT_GM: - if(drumchan) - return gmdrumname; - tmask = 1; - break; - default: - hb = true; // MSB bank matters - lb = true; // LSB bank matters - break; - } + //int tmask = 1; + + bool hb = hbank != 0xff; + bool lb = lbank != 0xff; for (ciPatchGroup i = pg.begin(); i != pg.end(); ++i) { const PatchList& pl = (*i)->patches; for (ciPatch ipl = pl.begin(); ipl != pl.end(); ++ipl) { const Patch* mp = *ipl; - if ((mp->typ & tmask) - && (pr == mp->prog) - && ((drum && mode != MT_GM) || - (mp->drum == drumchan)) + if (//(mp->typ & tmask) && + (pr == mp->prog) + && (mp->drum == drum) && (hbank == mp->hbank || !hb || mp->hbank == -1) && (lbank == mp->lbank || !lb || mp->lbank == -1)) @@ -1132,14 +1121,9 @@ QString MidiInstrument::getPatchName(int channel, int prog, MType mode, bool dru return "<unknown>"; } - - - - - -unsigned MidiInstrument::getNextPatch(int channel, unsigned patch, MType songType, bool drum) +unsigned MidiInstrument::getNextPatch(int channel, unsigned patch, bool drum) { - QList<dumb_patchlist_entry_t> haystack=getPatches(channel,songType,drum); + QList<dumb_patchlist_entry_t> haystack=getPatches(channel,drum); if (haystack.empty()) return MusECore::CTRL_VAL_UNKNOWN; int prog=patch&0xFF; @@ -1168,9 +1152,9 @@ unsigned MidiInstrument::getNextPatch(int channel, unsigned patch, MType songTyp ((((it->hbank==-1)?0xFF:it->hbank)<<16)&0xFF0000); } -unsigned MidiInstrument::getPrevPatch(int channel, unsigned patch, MType songType, bool drum) +unsigned MidiInstrument::getPrevPatch(int channel, unsigned patch, bool drum) { - QList<dumb_patchlist_entry_t> haystack=getPatches(channel,songType,drum); + QList<dumb_patchlist_entry_t> haystack=getPatches(channel,drum); if (haystack.empty()) return MusECore::CTRL_VAL_UNKNOWN; int prog=patch&0xFF; @@ -1197,51 +1181,21 @@ unsigned MidiInstrument::getPrevPatch(int channel, unsigned patch, MType songTyp ((((it->hbank==-1)?0xFF:it->hbank)<<16)&0xFF0000); } -QList<dumb_patchlist_entry_t> MidiInstrument::getPatches(int channel, MType mode, bool drum) +QList<dumb_patchlist_entry_t> MidiInstrument::getPatches(int /*channel*/, bool drum) { - int tmask = 1; - bool drumchan = channel == 9; - bool hb = false; - bool lb = false; - switch (mode) { - case MT_GS: - tmask = 2; - hb = true; - break; - case MT_XG: - hb = true; - lb = true; - tmask = 4; - break; - case MT_GM: - if(drumchan) - { - QList<dumb_patchlist_entry_t> tmp; - tmp.push_back(dumb_patchlist_entry_t(0,-1,-1)); - } - else - tmask = 1; - break; - default: - hb = true; // MSB bank matters - lb = true; // LSB bank matters - break; - } - - + //int tmask = 1; QList<dumb_patchlist_entry_t> tmp; for (ciPatchGroup i = pg.begin(); i != pg.end(); ++i) { const PatchList& pl = (*i)->patches; for (ciPatch ipl = pl.begin(); ipl != pl.end(); ++ipl) { const Patch* mp = *ipl; - if ((mp->typ & tmask) && - ((drum && mode != MT_GM) || - (mp->drum == drumchan)) ) + if (//(mp->typ & tmask) && + (mp->drum == drum)) { int prog = mp->prog; - int lbank = (mp->lbank==-1 || !lb) ? -1 : mp->lbank; - int hbank = (mp->hbank==-1 || !hb) ? -1 : mp->hbank; + int lbank = mp->lbank; + int hbank = mp->hbank; tmp.push_back(dumb_patchlist_entry_t(prog,lbank,hbank)); } } diff --git a/muse2/muse/instruments/minstrument.h b/muse2/muse/instruments/minstrument.h index 6b534672..7ff6f7d8 100644 --- a/muse2/muse/instruments/minstrument.h +++ b/muse2/muse/instruments/minstrument.h @@ -47,7 +47,8 @@ class DrumMap; //--------------------------------------------------------- struct Patch { - signed char typ; // 1 - GM 2 - GS 4 - XG + // REMOVE Tim. OBSOLETE. + //signed char typ; // 1 - GM 2 - GS 4 - XG signed char hbank, lbank, prog; QString name; bool drum; @@ -200,6 +201,7 @@ class MidiInstrument { MidiInstrument(const QString& txt); const QString& iname() const { return _name; } void setIName(const QString& txt) { _name = txt; } + MType midiType() const; //MidiInstrument& uniqueCopy(const MidiInstrument&); // Assign will 'delete' all existing patches and groups from the instrument. @@ -214,9 +216,9 @@ class MidiInstrument { void addSysex(SysEx* sysex) { _sysex.append(sysex); } const DrumMap* drummap_for_patch(int patch) const; - QList<dumb_patchlist_entry_t> getPatches(int channel, MType songType, bool drum); - unsigned getNextPatch(int channel, unsigned patch, MType songType, bool drum); - unsigned getPrevPatch(int channel, unsigned patch, MType songType, bool drum); + QList<dumb_patchlist_entry_t> getPatches(int channel, bool drum); + unsigned getNextPatch(int channel, unsigned patch, bool drum); + unsigned getPrevPatch(int channel, unsigned patch, bool drum); EventList* midiInit() const { return _midiInit; } EventList* midiReset() const { return _midiReset; } @@ -235,9 +237,9 @@ class MidiInstrument { virtual bool hasNativeGui() const { return false; } virtual void writeToGui(const MidiPlayEvent&) {} - virtual void reset(int, MType); - virtual QString getPatchName(int,int,MType,bool); - virtual void populatePatchPopup(MusEGui::PopupMenu*, int, MType, bool); + virtual void reset(int); + virtual QString getPatchName(int,int,bool); + virtual void populatePatchPopup(MusEGui::PopupMenu*, int, bool); void read(Xml&); void write(int level, Xml&); diff --git a/muse2/muse/liste/editevent.cpp b/muse2/muse/liste/editevent.cpp index a60ea229..415c6385 100644 --- a/muse2/muse/liste/editevent.cpp +++ b/muse2/muse/liste/editevent.cpp @@ -823,7 +823,7 @@ void EditCtrlDialog::updatePatch() int port = track->outPort(); int channel = track->outChannel(); MusECore::MidiInstrument* instr = MusEGlobal::midiPorts[port].instrument(); - patchName->setText(instr->getPatchName(channel, val, MusEGlobal::song->mtype(), track->isDrumTrack())); + patchName->setText(instr->getPatchName(channel, val, track->isDrumTrack())); int hb = ((val >> 16) & 0xff) + 1; if (hb == 0x100) @@ -860,7 +860,7 @@ void EditCtrlDialog::instrPopup() MusECore::MidiInstrument* instr = MusEGlobal::midiPorts[port].instrument(); MusEGui::PopupMenu* pup = new MusEGui::PopupMenu(this); - instr->populatePatchPopup(pup, channel, MusEGlobal::song->mtype(), track->isDrumTrack()); + instr->populatePatchPopup(pup, channel, track->isDrumTrack()); if(pup->actions().count() == 0) { diff --git a/muse2/muse/midi.cpp b/muse2/muse/midi.cpp index 503208e6..f1d5124d 100644 --- a/muse2/muse/midi.cpp +++ b/muse2/muse/midi.cpp @@ -4,7 +4,7 @@ // $Id: midi.cpp,v 1.43.2.22 2009/11/09 20:28:28 terminator356 Exp $ // // (C) Copyright 1999/2004 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net) +// (C) Copyright 2011-2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -71,6 +71,7 @@ const unsigned int mmcStopMsgLen = sizeof(mmcStopMsg); const unsigned int mmcLocateMsgLen = sizeof(mmcLocateMsg); #define CALC_TICK(the_tick) lrintf((float(the_tick) * float(MusEGlobal::config.division) + float(div/2)) / float(div)); + /*--------------------------------------------------------- * midi_meta_name *---------------------------------------------------------*/ @@ -79,22 +80,22 @@ QString midiMetaName(int meta) { const char* s = ""; switch (meta) { - case 0: s = "Sequence Number"; break; - case 1: s = "Text Event"; break; - case 2: s = "Copyright"; break; - case 3: s = "Sequence/Track Name"; break; - case 4: s = "Instrument Name"; break; - case 5: s = "Lyric"; break; - case 6: s = "Marker"; break; - case 7: s = "Cue Point"; break; - case 8: - case 9: - case 0x0a: - case 0x0b: - case 0x0c: - case 0x0d: - case 0x0e: - case 0x0f: s = "Text"; break; + case 0: s = "Text 0: Sequence Number"; break; + case 1: s = "Text 1: Track comment"; break; + case 2: s = "Text 2: Copyright"; break; + case 3: s = "Text 3: Sequence/Track Name"; break; + case 4: s = "Text 4: Instrument Name"; break; + case 5: s = "Text 5: Lyric"; break; + case 6: s = "Text 6: Marker"; break; + case 7: s = "Text 7: Cue Point"; break; + case 8: s = "Text 8"; break; + case 9: s = "Text 9: Device Name"; break; + case 0x0a: s = "Text A"; break; + case 0x0b: s = "Text B"; break; + case 0x0c: s = "Text C"; break; + case 0x0d: s = "Text D"; break; + case 0x0e: s = "Text E"; break; + case 0x0f: s = "Text F"; break; case 0x20: s = "Channel Prefix"; break; case 0x21: s = "Port Change"; break; case 0x2f: s = "End of Track"; break; @@ -411,31 +412,30 @@ void buildMidiEventList(EventList* del, const MPEventList* el, MidiTrack* track, { const unsigned char* data = ev.data(); switch (ev.dataA()) { - case 0x01: // Text + case ME_META_TEXT_1_COMMENT: // Text if (track->comment().isEmpty()) track->setComment(QString((const char*)data)); else track->setComment(track->comment() + "\n" + QString((const char*)data)); break; - case 0x03: // Sequence-/TrackName + case ME_META_TEXT_3_TRACK_NAME: // Sequence-/TrackName track->setName(QString((char*)data)); break; - case 0x6: // Marker + case ME_META_TEXT_6_MARKER: // Marker { unsigned ltick = CALC_TICK(tick); MusEGlobal::song->addMarker(QString((const char*)(data)), ltick, false); } break; - case 0x5: // Lyrics - case 0x8: // text - case 0x9: - case 0xa: + case ME_META_TEXT_5_LYRIC: // Lyrics + case ME_META_TEXT_8: // text + case ME_META_TEXT_9_DEVICE_NAME: + case ME_META_TEXT_A: break; - - case 0x0f: // Track Comment + case ME_META_TEXT_F_TRACK_COMMENT: // Track Comment track->setComment(QString((char*)data)); break; - case 0x51: // Tempo + case ME_META_SET_TEMPO: // Tempo { unsigned tempo = data[2] + (data[1] << 8) + (data[0] <<16); unsigned ltick = CALC_TICK(tick); @@ -443,7 +443,7 @@ void buildMidiEventList(EventList* del, const MPEventList* el, MidiTrack* track, MusEGlobal::tempomap.addTempo(ltick, tempo); } break; - case 0x58: // Time Signature + case ME_META_TIME_SIGNATURE: // Time Signature { int timesig_z = data[0]; int n = data[1]; @@ -454,10 +454,13 @@ void buildMidiEventList(EventList* del, const MPEventList* el, MidiTrack* track, AL::sigmap.add(ltick, AL::TimeSignature(timesig_z, timesig_n)); } break; - case 0x59: // Key Signature + case ME_META_KEY_SIGNATURE: // Key Signature break; - default: - printf("unknown Meta 0x%x %d\n", ev.dataA(), ev.dataA()); + default: + printf("buildMidiEventList: unknown Meta 0x%x %d unabsorbed, adding instead to track:%s\n", ev.dataA(), ev.dataA(), track->name().toLatin1().constData()); + e.setType(Meta); + e.setA(ev.dataA()); + e.setData(ev.data(), ev.len()); } } break; @@ -633,46 +636,6 @@ void Audio::initDevices() activePorts[i] = false; // no standard initialization } } - // - // First all ports are initialized to GM and then are changed - // to XG/GS in order to prevent that devices with more than one - // port, e.g. Korg NS5R, toggle between GM and XG/GS several times. - // - // Standard initialization... - for (int i = 0; i < MIDI_PORTS; ++i) { - if (!activePorts[i]) - continue; - MusECore::MidiPort* port = &MusEGlobal::midiPorts[i]; - switch(MusEGlobal::song->mtype()) { - case MT_GS: - case MT_UNKNOWN: - break; - case MT_GM: - case MT_XG: - port->sendGmOn(); - break; - } - } - for (int i = 0; i < MIDI_PORTS; ++i) { - if (!activePorts[i]) - continue; - MusECore::MidiPort* port = &MusEGlobal::midiPorts[i]; - switch(MusEGlobal::song->mtype()) { - case MT_UNKNOWN: - break; - case MT_GM: - port->sendGmInitValues(); - break; - case MT_GS: - port->sendGsOn(); - port->sendGsInitValues(); - break; - case MT_XG: - port->sendXgOn(); - port->sendXgInitValues(); - break; - } - } } //--------------------------------------------------------- diff --git a/muse2/muse/midi.h b/muse2/muse/midi.h index 711b36d5..1aab8ab0 100644 --- a/muse2/muse/midi.h +++ b/muse2/muse/midi.h @@ -4,7 +4,7 @@ // $Id: midi.h,v 1.4.2.2 2009/11/09 20:28:28 terminator356 Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net) +// (C) Copyright 2011-2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -58,8 +58,37 @@ enum { ME_SENSE = 0xfe }; -#define ME_TIMESIG 0x58 - +//-------------------------------------- +// Recognized / transmitted meta events: +//-------------------------------------- +enum { + ME_META_TEXT_0_SEQUENCE_NUMBER = 0x00, + ME_META_TEXT_1_COMMENT = 0x01, + ME_META_TEXT_2_COPYRIGHT = 0x02, + ME_META_TEXT_3_TRACK_NAME = 0x03, + ME_META_TEXT_4_INSTRUMENT_NAME = 0x04, + ME_META_TEXT_5_LYRIC = 0x05, + ME_META_TEXT_6_MARKER = 0x06, + ME_META_TEXT_7_CUE_POINT = 0x07, + ME_META_TEXT_8 = 0x08, + ME_META_TEXT_9_DEVICE_NAME = 0x09, + ME_META_TEXT_A = 0x0a, + ME_META_TEXT_B = 0x0b, + ME_META_TEXT_C = 0x0c, + ME_META_TEXT_D = 0x0d, + ME_META_TEXT_E = 0x0e, + ME_META_TEXT_F_TRACK_COMMENT = 0x0f, + ME_META_CHANNEL_CHANGE = 0x20, + ME_META_PORT_CHANGE = 0x21, + ME_META_END_OF_TRACK = 0x2f, + ME_META_SET_TEMPO = 0x51, + ME_META_SMPTE_OFFSET = 0x54, + ME_META_TIME_SIGNATURE = 0x58, + ME_META_KEY_SIGNATURE = 0x59, + ME_META_SEQ_SPECIFIC_1 = 0x74, + ME_META_SEQ_SPECIFIC_2 = 0x7f +}; + extern const unsigned char gmOnMsg[]; extern const unsigned char gsOnMsg[]; diff --git a/muse2/muse/mididev.cpp b/muse2/muse/mididev.cpp index 5ff8bf94..9f303e43 100644 --- a/muse2/muse/mididev.cpp +++ b/muse2/muse/mididev.cpp @@ -445,8 +445,6 @@ bool MidiDevice::putEvent(const MidiPlayEvent& ev) return putMidiEvent(MidiPlayEvent(0, 0, chn, ME_PITCHBEND, b, 0)); } if (a == CTRL_PROGRAM) { - // don't output program changes for GM drum channel - if (!(MusEGlobal::song->mtype() == MT_GM && chn == 9)) { int hb = (b >> 16) & 0xff; int lb = (b >> 8) & 0xff; int pr = b & 0x7f; @@ -455,8 +453,6 @@ bool MidiDevice::putEvent(const MidiPlayEvent& ev) if (lb != 0xff) putMidiEvent(MidiPlayEvent(0, 0, chn, ME_CONTROLLER, CTRL_LBANK, lb)); return putMidiEvent(MidiPlayEvent(0, 0, chn, ME_PROGRAM, pr, 0)); - } - return false; // Should absorb anyway and return, right? p4.0.48 Tim. } #if 1 // if ALSA cannot handle RPN NRPN etc. DELETETHIS? remove the wrapping #if #endif diff --git a/muse2/muse/midifile.cpp b/muse2/muse/midifile.cpp index bc546729..6f92d0f5 100644 --- a/muse2/muse/midifile.cpp +++ b/muse2/muse/midifile.cpp @@ -4,6 +4,7 @@ // $Id: midifile.cpp,v 1.17 2004/06/18 08:36:43 wschweer Exp $ // // (C) Copyright 1999-2003 Werner Schweer (ws@seh.de) +// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -75,14 +76,16 @@ MidiFile::MidiFile(FILE* f) { fp = f; curPos = 0; - _mtype = MT_GM; // MT_UNKNOWN; + lastMtype = MT_UNKNOWN; _error = MF_NO_ERROR; _tracks = new MidiFileTrackList; + _usedPortMap = new MidiFilePortMap; } MidiFile::~MidiFile() { delete _tracks; + delete _usedPortMap; } //--------------------------------------------------------- @@ -243,11 +246,14 @@ bool MidiFile::readTrack(MidiFileTrack* t) int port = 0; int channel = 0; - + for (;;) { MidiPlayEvent event; lastport = -1; lastchannel = -1; + lastMtype = MT_UNKNOWN; + lastInstrName.clear(); + lastDeviceName.clear(); int rv = readEvent(&event, t); if (lastport != -1) { @@ -264,6 +270,67 @@ bool MidiFile::readTrack(MidiFileTrack* t) channel = 0; } } + + if(!lastDeviceName.isEmpty()) + { + iMidiFilePort iup = _usedPortMap->begin(); + for( ; iup != _usedPortMap->end(); ++iup) + { + if(iup->second._subst4DevName == lastDeviceName) + { + port = iup->first; + break; + } + } + if(iup == _usedPortMap->end()) + { + MidiDevice* md = MusEGlobal::midiDevices.find(lastDeviceName); + if(md) + { + int pn = md->midiPort(); + if(pn != -1) + port = pn; + else + { + for(int i = 0; i < MIDI_PORTS; ++i) + { + iMidiFilePort ip = _usedPortMap->find(i); + MidiPort* mp = &MusEGlobal::midiPorts[i]; + if(!mp->device() && (ip == _usedPortMap->end() || ip->second._subst4DevName.isEmpty())) + { + //mp->setMidiDevice(); // No, done in importMidi + //msgSetMidiDevice( + port = i; + break; + } + } + } + } + } + } + + iMidiFilePort iup = _usedPortMap->find(port); + if(iup == _usedPortMap->end()) + { + MidiFilePort up; + if(lastMtype != MT_UNKNOWN) + up._midiType = lastMtype; + if(!lastInstrName.isEmpty()) + up._instrName = lastInstrName; + if(!lastDeviceName.isEmpty()) + up._subst4DevName = lastDeviceName; + _usedPortMap->insert(std::pair<int, MidiFilePort>(port, up)); + } + else + { + if(lastMtype != MT_UNKNOWN) + iup->second._midiType = lastMtype; + if(!lastInstrName.isEmpty()) + iup->second._instrName = lastInstrName; + if(!lastDeviceName.isEmpty()) + iup->second._subst4DevName = lastDeviceName; + } + if (rv == 0) break; else if (rv == -1) @@ -278,6 +345,7 @@ bool MidiFile::readTrack(MidiFileTrack* t) channel = event.channel(); el->add(event); } + int end = curPos; if (end != endPos) { printf("MidiFile::readTrack(): TRACKLEN does not fit %d+%d != %d, %d too much\n", @@ -332,6 +400,7 @@ int MidiFile::readEvent(MidiPlayEvent* event, MidiFileTrack* t) printf("readEvent: error 3\n"); return -2; } + // Buffer can be deleted by caller's event when it goes out of scope. buffer = new unsigned char[len]; if (read(buffer, len)) { printf("readEvent: error 4\n"); @@ -347,24 +416,22 @@ int MidiFile::readEvent(MidiPlayEvent* event, MidiFileTrack* t) event->setType(ME_SYSEX); event->setData(buffer, len); if (((unsigned)len == gmOnMsgLen) && memcmp(buffer, gmOnMsg, gmOnMsgLen) == 0) { - setMType(MT_GM); + lastMtype = MT_GM; return -1; } if (((unsigned)len == gsOnMsgLen) && memcmp(buffer, gsOnMsg, gsOnMsgLen) == 0) { - setMType(MT_GS); + lastMtype = MT_GS; return -1; } if (((unsigned)len == xgOnMsgLen) && memcmp(buffer, xgOnMsg, xgOnMsgLen) == 0) { - setMType(MT_XG); + lastMtype = MT_XG; return -1; } if (buffer[0] == 0x41) { // Roland - if (mtype() != MT_UNKNOWN) - setMType(MT_GS); + lastMtype = MT_GS; } else if (buffer[0] == 0x43) { // Yamaha - if (mtype() == MT_UNKNOWN || mtype() == MT_GM) - setMType(MT_XG); + lastMtype = MT_XG; int type = buffer[1] & 0xf0; switch (type) { case 0x00: // bulk dump @@ -384,7 +451,7 @@ int MidiFile::readEvent(MidiPlayEvent* event, MidiFileTrack* t) // 5 - DRUM 4 printf("xg set part mode channel %d to %d\n", buffer[4]+1, buffer[6]); if (buffer[6] != 0) - t->isDrumTrack = true; + t->_isDrumTrack = true; } break; case 0x20: @@ -398,6 +465,8 @@ int MidiFile::readEvent(MidiPlayEvent* event, MidiFileTrack* t) return -1; } } + if(MusEGlobal::debugMsg) + printf("MidiFile::readEvent: unknown Sysex 0x%02x unabsorbed, passing thru intead\n", me & 0xff); return 3; } if (me == 0xff) { @@ -424,18 +493,28 @@ int MidiFile::readEvent(MidiPlayEvent* event, MidiFileTrack* t) } buffer[len] = 0; switch(type) { - case 0x21: // switch port + case ME_META_TEXT_9_DEVICE_NAME: // device name + lastDeviceName = QString((const char*)buffer); + delete[] buffer; + return -1; + case ME_META_TEXT_4_INSTRUMENT_NAME: // instrument name + lastInstrName = QString((const char*)buffer); + delete[] buffer; + return -1; + case ME_META_PORT_CHANGE: // switch port lastport = buffer[0]; delete[] buffer; return -1; - case 0x20: // switch channel + case ME_META_CHANNEL_CHANGE: // switch channel lastchannel = buffer[0]; delete[] buffer; return -1; - case 0x2f: // End of Track + case ME_META_END_OF_TRACK: // End of Track delete[] buffer; return 0; default: + if(MusEGlobal::debugMsg) + printf("MidiFile::readEvent: unknown Meta 0x%x %d unabsorbed, passing thru instead\n", type, type); event->setType(ME_META); event->setData(buffer, len+1); event->setA(type); diff --git a/muse2/muse/midifile.h b/muse2/muse/midifile.h index 9ef169e0..0baf2af9 100644 --- a/muse2/muse/midifile.h +++ b/muse2/muse/midifile.h @@ -4,6 +4,7 @@ // $Id: midifile.h,v 1.3 2004/01/04 18:24:43 wschweer Exp $ // // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) +// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -24,6 +25,8 @@ #ifndef __MIDIFILE_H__ #define __MIDIFILE_H__ +#include <QString> + #include <stdio.h> #include <list> @@ -34,6 +37,27 @@ namespace MusECore { struct MPEventList; class MidiPlayEvent; +class MidiInstrument; + +//--------------------------------------------------------- +// MidiFileTrack +//--------------------------------------------------------- + +struct MidiFilePort { + bool _isStandardDrums; + MType _midiType; + QString _instrName; + QString _subst4DevName; + MidiFilePort() { + _midiType = MT_UNKNOWN; + _isStandardDrums = false; + } +}; + + +typedef std::map<int, MidiFilePort> MidiFilePortMap; +typedef MidiFilePortMap::iterator iMidiFilePort; +typedef MidiFilePortMap::const_iterator ciMidiFilePort; //--------------------------------------------------------- // MidiFileTrack @@ -41,9 +65,9 @@ class MidiPlayEvent; struct MidiFileTrack { MPEventList events; - bool isDrumTrack; + bool _isDrumTrack; MidiFileTrack() { - isDrumTrack = false; + _isDrumTrack = false; } }; @@ -60,12 +84,18 @@ class MidiFile { int format; // smf file format int ntracks; // number of midi tracks int _division; - MType _mtype; + //MType _mtype; MidiFileTrackList* _tracks; int status, click; int sstatus; int lastport, lastchannel; + MType lastMtype; + //QString lastMtypeInstrument; + QString lastInstrName; + QString lastDeviceName; + //MidiInstrument* def_instr; + MidiFilePortMap* _usedPortMap; FILE* fp; int curPos; @@ -92,6 +122,7 @@ class MidiFile { bool read(); bool write(); QString error(); + MidiFilePortMap* usedPortMap() { return _usedPortMap; } MidiFileTrackList* trackList() { return _tracks; } int tracks() const { return ntracks; } void setTrackList(MidiFileTrackList* tr, int n) { @@ -100,8 +131,6 @@ class MidiFile { } void setDivision(int d) { _division = d; } int division() const { return _division; } - void setMType(MType t) { _mtype = t; } - MType mtype() const { return _mtype; } }; } // namespace MusECore diff --git a/muse2/muse/mpevent.cpp b/muse2/muse/mpevent.cpp index a8596224..1ad09ff3 100644 --- a/muse2/muse/mpevent.cpp +++ b/muse2/muse/mpevent.cpp @@ -4,6 +4,7 @@ // $Id: mpevent.cpp,v 1.6.2.2 2009/11/25 09:09:43 terminator356 Exp $ // // (C) Copyright 2002-2004 Werner Schweer (ws@seh.de) +// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -101,6 +102,101 @@ void MEvent::dump() const } //--------------------------------------------------------- +// sortingWeight +//--------------------------------------------------------- + +int MEvent::sortingWeight() const +{ + // Sorting weight initially worked out by Tim E. Real + // Sorted here by most popular for quickest reponse. + + switch(_type) + { + case ME_NOTEON: + return 98; + case ME_NOTEOFF: + return 7; + + case ME_PITCHBEND: + return 25; + case ME_CONTROLLER: + switch(_a) + { + case CTRL_PROGRAM: + return 21; + default: + return 24; + } + case ME_PROGRAM: + return 20; + + case ME_CLOCK: + return 0; + case ME_MTC_QUARTER: + return 1; + case ME_TICK: + return 2; + case ME_SENSE: + return 3; + + case ME_SYSEX_END: + return 4; + case ME_AFTERTOUCH: + return 5; + case ME_POLYAFTER: + return 6; + case ME_STOP: + return 8; + + case ME_SONGSEL: + return 9; + case ME_SYSEX: + return 18; + case ME_META: + switch(_a) + { + case ME_META_TEXT_2_COPYRIGHT: + return 10; + case ME_META_TEXT_1_COMMENT: + return 11; + case ME_META_PORT_CHANGE: + return 12; + case ME_META_TEXT_9_DEVICE_NAME: + return 13; + case ME_META_CHANNEL_CHANGE: + return 14; + + case ME_META_TEXT_3_TRACK_NAME: + return 15; + case ME_META_TEXT_F_TRACK_COMMENT: + return 16; + case ME_META_TEXT_0_SEQUENCE_NUMBER: + return 17; + + case ME_META_TEXT_4_INSTRUMENT_NAME: + return 19; + case ME_META_END_OF_TRACK: + return 99; + default: + return 97; + } + + case ME_TUNE_REQ: + return 22; + case ME_SONGPOS: + return 23; + + case ME_START: + return 26; + case ME_CONTINUE: + return 27; + } + + printf("FIXME: MEvent::sortingWeight: unknown event type:%d\n", _type); + return 100; +} + +//--------------------------------------------------------- // operator < //--------------------------------------------------------- @@ -115,9 +211,13 @@ bool MEvent::operator<(const MEvent& e) const // notes if (channel() == e.channel()) - return type() == ME_NOTEOFF - || (type() == ME_NOTEON && dataB() == 0) - || type() != ME_NOTEON; // Make note-ons last so that controllers such as program come before notes played. 1/31/2012 Tim. + { +// REMOVE Tim. +// return (type() == ME_NOTEOFF +// || (type() == ME_NOTEON && dataB() == 0) +// || type() != ME_NOTEON; // Make note-ons last so that controllers such as program come before notes played. 1/31/2012 Tim. + return sortingWeight() < e.sortingWeight(); + } int map[16] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, 11, 12, 13, 14, 15 }; return map[channel()] < map[e.channel()]; diff --git a/muse2/muse/mpevent.h b/muse2/muse/mpevent.h index 903a8126..7c28bde4 100644 --- a/muse2/muse/mpevent.h +++ b/muse2/muse/mpevent.h @@ -4,6 +4,7 @@ // $Id: mpevent.h,v 1.8.2.5 2009/11/25 09:09:43 terminator356 Exp $ // // (C) Copyright 1999-2002 Werner Schweer (ws@seh.de) +// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -77,7 +78,9 @@ class MEvent { _loopNum = ed._loopNum; return *this; } - + + int sortingWeight() const; + int port() const { return _port; } int channel() const { return _channel; } int type() const { return _type; } diff --git a/muse2/muse/song.cpp b/muse2/muse/song.cpp index 026ec1a9..000bbec3 100644 --- a/muse2/muse/song.cpp +++ b/muse2/muse/song.cpp @@ -1583,12 +1583,6 @@ PartList* Song::getSelectedWaveParts() const return parts; } -void Song::setMType(MType t) - { - _mtype = t; - MusEGlobal::song->update(SC_SONG_TYPE); - } - //--------------------------------------------------------- // beat //--------------------------------------------------------- @@ -2129,8 +2123,6 @@ void Song::clear(bool signal, bool clear_all) punchoutFlag = false; recordFlag = false; soloFlag = false; - // seq - _mtype = MT_UNKNOWN; _recMode = REC_OVERDUP; _cycleMode = CYCLE_NORMAL; _click = false; diff --git a/muse2/muse/song.h b/muse2/muse/song.h index e9e893f1..63256af3 100644 --- a/muse2/muse/song.h +++ b/muse2/muse/song.h @@ -97,7 +97,6 @@ class AudioDevice; #define SC_CLIP_MODIFIED 0x4000000 #define SC_MIDI_CONTROLLER_ADD 0x8000000 // a hardware midi controller was added or deleted #define SC_MIDI_TRACK_PROP 0x10000000 // a midi track's properties changed (channel, compression etc) -#define SC_SONG_TYPE 0x20000000 // the midi song type (mtype) changed #define SC_KEY 0x40000000 // key map changed #define SC_EVERYTHING -1 // global update @@ -153,7 +152,6 @@ class Song : public QObject { bool punchoutFlag; bool recordFlag; bool soloFlag; - enum MType _mtype; int _recMode; int _cycleMode; bool _click; @@ -181,9 +179,6 @@ class Song : public QObject { void endMsgCmd(); void processMsg(AudioMsg* msg); - void setMType(MType t); - MType mtype() const { return _mtype; } - void setFollow(FollowMode m) { _follow = m; } FollowMode follow() const { return _follow; } diff --git a/muse2/muse/songfile.cpp b/muse2/muse/songfile.cpp index 07430c26..90805175 100644 --- a/muse2/muse/songfile.cpp +++ b/muse2/muse/songfile.cpp @@ -937,8 +937,8 @@ void Song::read(Xml& xml, bool isTemplate) setRecord(xml.parseInt()); else if (tag == "solo") soloFlag = xml.parseInt(); - else if (tag == "type") - _mtype = MType(xml.parseInt()); + else if (tag == "type") // Obsolete. + xml.parseInt(); else if (tag == "recmode") _recMode = xml.parseInt(); else if (tag == "cycle") @@ -1088,7 +1088,6 @@ void Song::write(int level, Xml& xml) const xml.intTag(level, "punchout", punchoutFlag); xml.intTag(level, "record", recordFlag); xml.intTag(level, "solo", soloFlag); - xml.intTag(level, "type", _mtype); xml.intTag(level, "recmode", _recMode); xml.intTag(level, "cycle", _cycleMode); xml.intTag(level, "click", _click); diff --git a/muse2/muse/synth.cpp b/muse2/muse/synth.cpp index 332ff90f..781cab3f 100644 --- a/muse2/muse/synth.cpp +++ b/muse2/muse/synth.cpp @@ -863,11 +863,11 @@ void SynthI::read(Xml& xml) // getPatchName //--------------------------------------------------------- -const char* MessSynthIF::getPatchName(int channel, int prog, MType type, bool drum) +const char* MessSynthIF::getPatchName(int channel, int prog, bool drum) { if (_mess) { - const char* s = _mess->getPatchName(channel, prog, type, drum); + const char* s = _mess->getPatchName(channel, prog, drum); if(s) return s; } @@ -878,7 +878,7 @@ const char* MessSynthIF::getPatchName(int channel, int prog, MType type, bool dr // populatePatchPopup //--------------------------------------------------------- -void MessSynthIF::populatePatchPopup(MusEGui::PopupMenu* menu, int ch, MType, bool) +void MessSynthIF::populatePatchPopup(MusEGui::PopupMenu* menu, int ch, bool) { menu->clear(); const MidiPatch* mp = _mess->getPatchInfo(ch, 0); diff --git a/muse2/muse/synth.h b/muse2/muse/synth.h index efa76f5f..f5ecc9ab 100644 --- a/muse2/muse/synth.h +++ b/muse2/muse/synth.h @@ -153,9 +153,8 @@ class SynthIF { virtual int totalOutChannels() const = 0; virtual int totalInChannels() const = 0; virtual void deactivate3() = 0; - virtual const char* getPatchName(int, int, int, bool) const = 0; - virtual const char* getPatchName(int, int, MType, bool) = 0; - virtual void populatePatchPopup(MusEGui::PopupMenu*, int, MType, bool) = 0; + virtual const char* getPatchName(int, int, bool) = 0; + virtual void populatePatchPopup(MusEGui::PopupMenu*, int, bool) = 0; virtual void write(int level, Xml& xml) const = 0; virtual float getParameter(unsigned long idx) const = 0; virtual void setParameter(unsigned long idx, float value) = 0; @@ -228,12 +227,12 @@ class SynthI : public AudioTrack, public MidiDevice, Synth* synth() const { return synthesizer; } virtual bool isSynti() const { return true; } - virtual QString getPatchName(int ch, int prog, MType t, bool dr) { - return _sif->getPatchName(ch, prog, t, dr); + virtual QString getPatchName(int ch, int prog, bool dr) { + return _sif->getPatchName(ch, prog, dr); } - virtual void populatePatchPopup(MusEGui::PopupMenu* m, int i, MType t, bool d) { - _sif->populatePatchPopup(m, i, t, d); + virtual void populatePatchPopup(MusEGui::PopupMenu* m, int i, bool d) { + _sif->populatePatchPopup(m, i, d); } void currentProg(unsigned long *prog, unsigned long *bankL, unsigned long *bankH); @@ -316,9 +315,8 @@ class MessSynthIF : public SynthIF { virtual int totalOutChannels() const; virtual int totalInChannels() const; virtual void deactivate3(); - virtual const char* getPatchName(int, int, int, bool) const { return ""; } - virtual const char* getPatchName(int, int, MType, bool); - virtual void populatePatchPopup(MusEGui::PopupMenu*, int, MType, bool); + virtual const char* getPatchName(int, int, bool); + virtual void populatePatchPopup(MusEGui::PopupMenu*, int, bool); virtual void write(int level, Xml& xml) const; virtual float getParameter(unsigned long) const { return 0.0; } virtual void setParameter(unsigned long, float) {} diff --git a/muse2/muse/ticksynth.cpp b/muse2/muse/ticksynth.cpp index 48e1ba2b..f03414fc 100644 --- a/muse2/muse/ticksynth.cpp +++ b/muse2/muse/ticksynth.cpp @@ -100,9 +100,8 @@ class MetronomeSynthIF : public SynthIF virtual int totalOutChannels() const { return 1; } virtual int totalInChannels() const { return 0; } virtual void deactivate3() {} - virtual const char* getPatchName(int, int, int, bool) const { return ""; } - virtual const char* getPatchName(int, int, MType, bool) { return ""; } - virtual void populatePatchPopup(MusEGui::PopupMenu*, int, MType, bool) {}; + virtual const char* getPatchName(int, int, bool) { return ""; } + virtual void populatePatchPopup(MusEGui::PopupMenu*, int, bool) {}; virtual void write(int, Xml&) const {} virtual float getParameter(unsigned long) const { return 0.0; } virtual void setParameter(unsigned long, float) {} diff --git a/muse2/muse/vst.h b/muse2/muse/vst.h index 3490cb39..52c45f16 100644 --- a/muse2/muse/vst.h +++ b/muse2/muse/vst.h @@ -93,9 +93,8 @@ class VstSynthIF : public SynthIF virtual int totalOutChannels() const; virtual int totalInChannels() const; virtual void deactivate3(); - virtual const char* getPatchName(int, int, int, bool) const { return ""; } - virtual const char* getPatchName(int, int, MType, bool) { return ""; } - virtual void populatePatchPopup(PopupMenu*, int, MType, bool) {}; + virtual const char* getPatchName(int, int, bool) { return ""; } + virtual void populatePatchPopup(PopupMenu*, int, bool) {}; virtual void write(int level, Xml& xml) const; virtual float getParameter(unsigned long idx) const; virtual void setParameter(unsigned long idx, float value); diff --git a/muse2/muse/widgets/configmidifilebase.ui b/muse2/muse/widgets/configmidifilebase.ui index 3eb7063e..50e28c19 100644 --- a/muse2/muse/widgets/configmidifilebase.ui +++ b/muse2/muse/widgets/configmidifilebase.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>546</width> - <height>367</height> + <width>408</width> + <height>537</height> </rect> </property> <property name="windowTitle"> @@ -16,8 +16,8 @@ <property name="sizeGripEnabled"> <bool>true</bool> </property> - <layout class="QVBoxLayout"> - <item> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="0" column="0"> <widget class="QGroupBox" name="midiImportGroupBox"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> @@ -28,8 +28,55 @@ <property name="title"> <string>Import:</string> </property> - <layout class="QGridLayout"> + <layout class="QGridLayout" name="gridLayout_3"> <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Default instrument:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QComboBox" name="importDefaultInstr"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item row="0" column="2"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>164</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="0" colspan="3"> + <widget class="QCheckBox" name="importDevNameMetas"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Device Name metas trump Port metas if both exist</string> + </property> + </widget> + </item> + <item row="2" column="0" colspan="3"> + <widget class="QCheckBox" name="importInstrNameMetas"> + <property name="text"> + <string>Instrument Name metas trump Mode sysexes if both exist</string> + </property> + </widget> + </item> + <item row="3" column="0" colspan="2"> <widget class="QCheckBox" name="splitPartsCheckBox"> <property name="toolTip"> <string>Split tracks into parts, or one single part</string> @@ -42,7 +89,7 @@ </property> </widget> </item> - <item row="1" column="0"> + <item row="4" column="0" colspan="3"> <layout class="QHBoxLayout" name="horizontalLayout"> <item> <widget class="QRadioButton" name="newDrumsCheckbox"> @@ -56,6 +103,12 @@ </item> <item> <widget class="QRadioButton" name="oldDrumsCheckbox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <property name="text"> <string>Use old-style drum tracks</string> </property> @@ -66,7 +119,62 @@ </layout> </widget> </item> - <item> + <item row="3" column="0"> + <layout class="QHBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <item> + <spacer name="Horizontal Spacing2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Expanding</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="buttonOk"> + <property name="text"> + <string>&OK</string> + </property> + <property name="shortcut"> + <string/> + </property> + <property name="autoDefault"> + <bool>true</bool> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonCancel"> + <property name="text"> + <string>&Cancel</string> + </property> + <property name="shortcut"> + <string/> + </property> + <property name="autoDefault"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + <item row="1" column="0"> <widget class="QGroupBox" name="midiExportGroupBox"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Expanding"> @@ -77,12 +185,61 @@ <property name="title"> <string>Export:</string> </property> - <layout class="QGridLayout"> - <item row="3" column="1"> - <widget class="QLineEdit" name="copyrightEdit"/> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="textLabel3"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Format:</string> + </property> + <property name="wordWrap"> + <bool>false</bool> + </property> + </widget> </item> - <item row="2" column="1"> + <item row="0" column="1"> + <widget class="QComboBox" name="formatCombo"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <item> + <property name="text"> + <string>0 (single track)</string> + </property> + </item> + <item> + <property name="text"> + <string>1 (multiple tracks)</string> + </property> + </item> + </widget> + </item> + <item row="0" column="2"> + <widget class="QLabel" name="textLabel1"> + <property name="text"> + <string>Division:</string> + </property> + <property name="wordWrap"> + <bool>false</bool> + </property> + </widget> + </item> + <item row="0" column="3"> <widget class="QComboBox" name="divisionCombo"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <item> <property name="text"> <string>96</string> @@ -100,73 +257,80 @@ </item> </widget> </item> - <item row="4" column="0" colspan="2"> - <widget class="QCheckBox" name="extendedFormat"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="text"> - <string>Enable extended smf format (currently not implemented)</string> - </property> - </widget> - </item> - <item row="5" column="0" colspan="2"> - <widget class="QCheckBox" name="twoByteTimeSigs"> - <property name="text"> - <string>Use &2-byte time signatures instead of standard 4</string> + <item row="0" column="4"> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> </property> - <property name="shortcut"> - <string>Alt+2</string> + <property name="sizeHint" stdset="0"> + <size> + <width>59</width> + <height>20</height> + </size> </property> - </widget> + </spacer> </item> - <item row="3" column="0"> - <widget class="QLabel" name="textLabel2"> - <property name="text"> - <string>Copyright:</string> + <item row="1" column="0" colspan="5"> + <widget class="QLabel" name="textLabel4"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> </property> - <property name="wordWrap"> - <bool>false</bool> + <property name="font"> + <font> + <pointsize>8</pointsize> + </font> </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="QLabel" name="textLabel3"> <property name="text"> - <string>Format:</string> + <string>Note: Format 0 uses the FIRST midi track's name/comment in the arranger</string> </property> <property name="wordWrap"> <bool>false</bool> </property> </widget> </item> - <item row="1" column="0" colspan="2"> - <widget class="QLabel" name="textLabel4"> + <item row="2" column="0"> + <widget class="QLabel" name="textLabel2"> <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> - <string>Note: Format 0 uses the FIRST midi track's name/comment in the arranger</string> + <string>Copyright:</string> </property> <property name="wordWrap"> <bool>false</bool> </property> </widget> </item> - <item row="2" column="0"> - <widget class="QLabel" name="textLabel1"> + <item row="2" column="1" colspan="4"> + <widget class="QLineEdit" name="copyrightEdit"/> + </item> + <item row="3" column="0" colspan="5"> + <widget class="QCheckBox" name="extendedFormat"> + <property name="enabled"> + <bool>false</bool> + </property> <property name="text"> - <string>Division:</string> + <string>Enable extended smf format (currently not implemented)</string> </property> - <property name="wordWrap"> - <bool>false</bool> + </widget> + </item> + <item row="4" column="0" colspan="4"> + <widget class="QCheckBox" name="twoByteTimeSigs"> + <property name="text"> + <string>Use &2-byte time signatures instead of standard 4</string> + </property> + <property name="shortcut"> + <string>Alt+2</string> </property> </widget> </item> - <item row="6" column="0" colspan="2"> + <item row="5" column="0" colspan="5"> <widget class="QCheckBox" name="optNoteOffs"> <property name="text"> <string>Save space by replacing note-offs with &zero velocity note-ons</string> @@ -176,77 +340,133 @@ </property> </widget> </item> - <item row="0" column="1"> - <widget class="QComboBox" name="formatCombo"> - <item> - <property name="text"> - <string>0 (single track)</string> + <item row="6" column="0" colspan="5"> + <widget class="QFrame" name="frame"> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Plain</enum> + </property> + <property name="lineWidth"> + <number>0</number> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_6"> + <property name="spacing"> + <number>6</number> </property> - </item> - <item> - <property name="text"> - <string>1 (multiple tracks)</string> + <property name="margin"> + <number>0</number> </property> - </item> + <item> + <widget class="QRadioButton" name="exportModeSysexes"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Mode sysexes</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="exportInstrumentNames"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Instrument name metas</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="exportModeAndInstrName"> + <property name="text"> + <string>Both</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="7" column="0" colspan="5"> + <widget class="QFrame" name="frame_2"> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Plain</enum> + </property> + <property name="lineWidth"> + <number>0</number> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_7"> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QRadioButton" name="exportPortMetas"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Port metas</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="exportDeviceNameMetas"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Device name metas</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="exportPortAndDeviceNameMetas"> + <property name="text"> + <string>Both</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="8" column="0" colspan="3"> + <widget class="QCheckBox" name="exportPortDeviceSMF0"> + <property name="text"> + <string>Export a Port/Device meta for format 0</string> + </property> </widget> </item> </layout> </widget> </item> - <item> - <layout class="QHBoxLayout"> - <property name="spacing"> - <number>6</number> + <item row="2" column="0"> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> </property> - <property name="margin"> - <number>0</number> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> </property> - <item> - <spacer name="Horizontal Spacing2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Expanding</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QPushButton" name="buttonOk"> - <property name="text"> - <string>&OK</string> - </property> - <property name="shortcut"> - <string/> - </property> - <property name="autoDefault"> - <bool>true</bool> - </property> - <property name="default"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="buttonCancel"> - <property name="text"> - <string>&Cancel</string> - </property> - <property name="shortcut"> - <string/> - </property> - <property name="autoDefault"> - <bool>true</bool> - </property> - </widget> - </item> - </layout> + </spacer> </item> </layout> </widget> diff --git a/muse2/muse/widgets/gensetbase.ui b/muse2/muse/widgets/gensetbase.ui index f1dc405b..c204b9f4 100644 --- a/muse2/muse/widgets/gensetbase.ui +++ b/muse2/muse/widgets/gensetbase.ui @@ -1526,7 +1526,7 @@ left button behave like the middle button in such areas.</string> <property name="title"> <string>Drum tracks</string> </property> - <layout class="QGridLayout" name="gridLayout"> + <layout class="QGridLayout" name="gridLayout9"> <item row="0" column="0"> <widget class="QRadioButton" name="onlyOldDrumBtn"> <property name="text"> @@ -1559,7 +1559,7 @@ left button behave like the middle button in such areas.</string> </widget> </item> <item> - <spacer name="verticalSpacer_2"> + <spacer name="verticalSpacer_21"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> diff --git a/muse2/muse/widgets/mtrackinfo.cpp b/muse2/muse/widgets/mtrackinfo.cpp index 1b98f100..23c7bf34 100644 --- a/muse2/muse/widgets/mtrackinfo.cpp +++ b/muse2/muse/widgets/mtrackinfo.cpp @@ -407,7 +407,7 @@ void MidiTrackInfo::heartBeat() else { MusECore::MidiInstrument* instr = mp->instrument(); - QString name = instr->getPatchName(outChannel, nprogram, MusEGlobal::song->mtype(), track->isDrumTrack()); + QString name = instr->getPatchName(outChannel, nprogram, track->isDrumTrack()); if(name.isEmpty()) { const QString n("???"); @@ -448,7 +448,7 @@ void MidiTrackInfo::heartBeat() //else //{ MusECore::MidiInstrument* instr = mp->instrument(); - QString name = instr->getPatchName(outChannel, program, MusEGlobal::song->mtype(), track->isDrumTrack()); + QString name = instr->getPatchName(outChannel, program, track->isDrumTrack()); if(iPatch->text() != name) iPatch->setText(name); @@ -827,7 +827,7 @@ void MidiTrackInfo::iProgHBankChanged() MusEGlobal::audio->msgPlayMidiEvent(&ev); MusECore::MidiInstrument* instr = mp->instrument(); - iPatch->setText(instr->getPatchName(channel, program, MusEGlobal::song->mtype(), track->isDrumTrack())); + iPatch->setText(instr->getPatchName(channel, program, track->isDrumTrack())); // updateTrackInfo(); } @@ -905,7 +905,7 @@ void MidiTrackInfo::iProgLBankChanged() MusEGlobal::audio->msgPlayMidiEvent(&ev); MusECore::MidiInstrument* instr = mp->instrument(); - iPatch->setText(instr->getPatchName(channel, program, MusEGlobal::song->mtype(), track->isDrumTrack())); + iPatch->setText(instr->getPatchName(channel, program, track->isDrumTrack())); // updateTrackInfo(); } @@ -983,7 +983,7 @@ void MidiTrackInfo::iProgramChanged() MusEGlobal::audio->msgPlayMidiEvent(&ev); MusECore::MidiInstrument* instr = mp->instrument(); - iPatch->setText(instr->getPatchName(channel, program, MusEGlobal::song->mtype(), track->isDrumTrack())); + iPatch->setText(instr->getPatchName(channel, program, track->isDrumTrack())); } // updateTrackInfo(); @@ -1150,7 +1150,7 @@ void MidiTrackInfo::instrPopup() MusECore::MidiInstrument* instr = MusEGlobal::midiPorts[port].instrument(); PopupMenu* pup = new PopupMenu(true); - instr->populatePatchPopup(pup, channel, MusEGlobal::song->mtype(), track->isDrumTrack()); + instr->populatePatchPopup(pup, channel, track->isDrumTrack()); if(pup->actions().count() == 0) { @@ -1479,7 +1479,7 @@ void MidiTrackInfo::updateTrackInfo(MusECore::SongChangedFlags_t flags) else { MusECore::MidiInstrument* instr = mp->instrument(); - iPatch->setText(instr->getPatchName(outChannel, nprogram, MusEGlobal::song->mtype(), track->isDrumTrack())); + iPatch->setText(instr->getPatchName(outChannel, nprogram, track->isDrumTrack())); } } else @@ -1495,7 +1495,7 @@ void MidiTrackInfo::updateTrackInfo(MusECore::SongChangedFlags_t flags) //else //{ MusECore::MidiInstrument* instr = mp->instrument(); - iPatch->setText(instr->getPatchName(outChannel, program, MusEGlobal::song->mtype(), track->isDrumTrack())); + iPatch->setText(instr->getPatchName(outChannel, program, track->isDrumTrack())); int hb = ((program >> 16) & 0xff) + 1; if (hb == 0x100) diff --git a/muse2/muse/widgets/musewidgetsplug.cpp b/muse2/muse/widgets/musewidgetsplug.cpp index 9c82b5f5..83a9fca9 100644 --- a/muse2/muse/widgets/musewidgetsplug.cpp +++ b/muse2/muse/widgets/musewidgetsplug.cpp @@ -169,6 +169,12 @@ MusEGlobal::GlobalConfigValues config = { true, // optimize midi export file note offs true, // Split imported tracks into multiple parts. true, // importMidiNewStyleDrum + true, // importDevNameMetas Import Prefer Device Name metas over port number metas if both exist. + true, // importInstrNameMetas Import Prefer Instrument Name metas over Mode sysexes if both exist. + MusEGlobal::EXPORT_PORTS_DEVICES_ALL, // exportPortsDevices Export port number metas and/or device name metas. + true, // exportPortDeviceSMF0 Export a port and/or device meta even for SMF0. + MusEGlobal::EXPORT_MODE_INSTR_ALL, // exportModeInstr Export mode sysexes and/or instrument name metas. + QString("GM"), // importMidiDefaultInstr Default to this instrument not Generic, if no match found 1, // startMode QString(""), // start song path false, // startSongLoadConfig diff --git a/muse2/share/instruments/Yamaha-S90.idf b/muse2/share/instruments/Yamaha-S90.idf index dd0125a1..54316999 100644 --- a/muse2/share/instruments/Yamaha-S90.idf +++ b/muse2/share/instruments/Yamaha-S90.idf @@ -1,6 +1,32 @@ <?xml version="1.0"?> <muse version="1.0"> <MidiInstrument name="Yamaha-S90"> + <SysEx name="Master 0"> + <comment>Master number 0 select</comment> + <data>0x43 0x10 0x6b 0xa 0x0 0x0 0x0</data> + </SysEx> + <SysEx name="SeqPlay"> + <comment>Switch to SeqPlay Mode</comment> + <data>0x43 0x10 0x6b 0xa 0x0 0x1 0x3</data> + </SysEx> + <SysEx name="Local off"> + <comment>Local Off</comment> + <data>0x43 0x10 0x6b 0x0 0x0 0x9 0x0</data> + </SysEx> + <Init> + <!-- Master number 0 select --> + <event tick="0" type="2" datalen="7"> + 0x43 0x10 0x6b 0xa 0x0 0x0 0x0 + </event> + <!-- Switch to SeqPlay Mode --> + <event tick="386" type="2" datalen="7"> + 0x43 0x10 0x6b 0xa 0x0 0x1 0x3 + </event> + <!-- Local Off --> + <event tick="386" type="2" datalen="7"> + 0x43 0x10 0x6b 0x0 0x0 0x9 0x0 + </event> + </Init> <PatchGroup name="PRE1"> <Patch name="3LayerS700" hbank="63" lbank="0" prog="0" /> <Patch name="Soft Touch" hbank="63" lbank="0" prog="1" /> diff --git a/muse2/share/instruments/gm.idf b/muse2/share/instruments/gm.idf index f0a66ecb..6c346eb2 100644 --- a/muse2/share/instruments/gm.idf +++ b/muse2/share/instruments/gm.idf @@ -6,6 +6,9 @@ <data>7e 7f 09 01</data> </SysEx> <Init> + <event tick="0" type="2" datalen="4"> + 7e 7f 09 01 + </event> </Init> <PatchGroup name="Piano"> <Patch name="Grand Piano" prog="0" /> diff --git a/muse2/share/instruments/gs.idf b/muse2/share/instruments/gs.idf index 3f03f6bc..873cbd18 100644 --- a/muse2/share/instruments/gs.idf +++ b/muse2/share/instruments/gs.idf @@ -1,11 +1,21 @@ <?xml version="1.0"?> <muse version="1.0"> <MidiInstrument name="GS" nullparam="32639"> + <SysEx name="GM on"> + <comment>Switch General Midi mode on</comment> + <data>7e 7f 09 01</data> + </SysEx> <SysEx name="GS on"> <comment>Switch GS mode on</comment> <data>0x41 0x10 0x42 0x12 0x40 0x00 0x7f 0x00 0x41</data> </SysEx> <Init> + <event tick="0" type="2" datalen="4"> + 7e 7f 09 01 + </event> + <event tick="250" type="2" datalen="9"> + 41 10 42 12 40 00 7f 00 41 + </event> </Init> <PatchGroup name="Piano"> <Patch name="Grand Piano" hbank="0" lbank="0" prog="0" /> diff --git a/muse2/share/instruments/xg.idf b/muse2/share/instruments/xg.idf index 5c3175bf..6d597d38 100644 --- a/muse2/share/instruments/xg.idf +++ b/muse2/share/instruments/xg.idf @@ -1,19 +1,21 @@ <?xml version="1.0"?> <muse version="1.0"> <MidiInstrument name="XG" nullparam="32639"> + <SysEx name="GM on"> + <comment>Switch General Midi mode on</comment> + <data>7e 7f 09 01</data> + </SysEx> <SysEx name="XG on"> <comment>Switch XG mode on</comment> <data>0x43 0x10 0x4c 0x00 0x00 0x7e 0x00</data> </SysEx> <Init> - <!-- <event tick="0" type="2" datalen="4"> 7e 7f 09 01 </event> - <event tick="0" type="2" datalen="7"> + <event tick="250" type="2" datalen="7"> 43 10 4c 00 00 7e 00 </event> - --> </Init> <PatchGroup name="Piano"> <Patch name="Grand Piano" hbank="0" lbank="0" prog="0" /> diff --git a/muse2/share/templates/MusE.cfg b/muse2/share/templates/MusE.cfg index 550b44a8..927234e0 100644 --- a/muse2/share/templates/MusE.cfg +++ b/muse2/share/templates/MusE.cfg @@ -22,10 +22,16 @@ <exp2ByteTimeSigs>0</exp2ByteTimeSigs> <expOptimNoteOffs>1</expOptimNoteOffs> <importMidiSplitParts>1</importMidiSplitParts> + <importDevNameMetas>1</importDevNameMetas> + <importInstrNameMetas>1</importInstrNameMetas> + <exportPortsDevices>0</exportPortsDevices> + <exportPortDeviceSMF0>1</exportPortDeviceSMF0> + <exportModeInstr>0</exportModeInstr> + <importMidiDefaultInstr>GM</importMidiDefaultInstr> <startMode>1</startMode> <startSong></startSong> <startSongLoadConfig>0</startSongLoadConfig> - <projectBaseFolder>/home/flo/MusE</projectBaseFolder> + <projectBaseFolder></projectBaseFolder> <projectStoreInFolder>1</projectStoreInFolder> <useProjectSaveDialog>1</useProjectSaveDialog> <midiInputDevice>0</midiInputDevice> diff --git a/muse2/synti/deicsonze/deicsonze.cpp b/muse2/synti/deicsonze/deicsonze.cpp index 7d3f8995..7ca0deed 100644 --- a/muse2/synti/deicsonze/deicsonze.cpp +++ b/muse2/synti/deicsonze/deicsonze.cpp @@ -3708,7 +3708,7 @@ bool DeicsOnze::setController(int ch, int ctrl, int val, bool fromGui) { // getPatchName //--------------------------------------------------------- -const char* DeicsOnze::getPatchName(int ch, int val, int) const { +const char* DeicsOnze::getPatchName(int ch, int val, bool) const { if(_global.channel[ch].isEnable) { Preset* p_preset; int hbank = (val & 0xff0000) >> 16; diff --git a/muse2/synti/deicsonze/deicsonze.h b/muse2/synti/deicsonze/deicsonze.h index 124c8d46..06e29fa7 100644 --- a/muse2/synti/deicsonze/deicsonze.h +++ b/muse2/synti/deicsonze/deicsonze.h @@ -585,7 +585,7 @@ class DeicsOnze : public Mess { bool sysex(int length, const unsigned char* data, bool fromGui); virtual bool sysex(int l, const unsigned char* d); - virtual const char* getPatchName(int ch, int number, int) const; + virtual const char* getPatchName(int ch, int number, bool) const; virtual const MidiPatch* getPatchInfo(int, const MidiPatch *) const; virtual int getControllerInfo(int arg1, const char** arg2, diff --git a/muse2/synti/fluid/fluid.cpp b/muse2/synti/fluid/fluid.cpp index 8a2fd13b..fa34d67e 100644 --- a/muse2/synti/fluid/fluid.cpp +++ b/muse2/synti/fluid/fluid.cpp @@ -405,7 +405,7 @@ bool ISynth::processEvent(const MusECore::MidiPlayEvent& ev) // getPatchName //--------------------------------------------------------- -const char* ISynth::getPatchName(int /*ch*/, int val, int, bool /*drum*/) const +const char* ISynth::getPatchName(int /*ch*/, int val, bool /*drum*/) const { int prog = val & 0xff; if(val == MusECore::CTRL_VAL_UNKNOWN || prog == 0xff) diff --git a/muse2/synti/fluid/fluid.h b/muse2/synti/fluid/fluid.h index 658be687..60449568 100644 --- a/muse2/synti/fluid/fluid.h +++ b/muse2/synti/fluid/fluid.h @@ -76,7 +76,7 @@ class ISynth : public Mess { virtual bool sysex(int len, const unsigned char* p); virtual bool processEvent(const MusECore::MidiPlayEvent&); - virtual const char* getPatchName (int, int, int, bool) const; + virtual const char* getPatchName (int, int, bool) const; virtual const MidiPatch* getPatchInfo(int, const MidiPatch *) const; virtual void getInitData(int*, const unsigned char**); diff --git a/muse2/synti/fluidsynth/fluidsynti.cpp b/muse2/synti/fluidsynth/fluidsynti.cpp index 0fca4122..46f20156 100644 --- a/muse2/synti/fluidsynth/fluidsynti.cpp +++ b/muse2/synti/fluidsynth/fluidsynti.cpp @@ -1005,6 +1005,13 @@ void FluidSynth::setController(int channel, int id, int val, bool fromGui) byte patch = (val & 0xff); //printf("val: %d banknum: %x patch: %d\n", val, banknum, patch); + if(val == MusECore::CTRL_VAL_UNKNOWN || patch == 0xff) + return; + if(channels[channel].drumchannel) + banknum = 128; + else if(banknum == 0xff) + banknum = 0; // Is wise? Else try to keep a previous value when 'off' (0xff) like the HW values? + err = fluid_synth_program_select(fluidsynth, channel, font_intid , banknum, patch); if (err) printf("FluidSynth::setController() - Error changing program on soundfont %s, channel: %d\n", fluid_synth_error(fluidsynth), channel); @@ -1205,18 +1212,15 @@ void FluidSynth::rewriteChannelSettings() //--------------------------------------------------------- // getPatchName //--------------------------------------------------------- -const char* FluidSynth::getPatchName(int i, int, int, bool /*drum*/) const +const char* FluidSynth::getPatchName(int i, int, bool /*drum*/) const { if (channels[i].font_intid == FS_UNSPECIFIED_FONT || channels[i].font_intid == FS_UNSPECIFIED_ID) - //return "no preset"; return "<unknown>"; else if (channels[i].preset == FS_UNSPECIFIED_PRESET) - //return "no preset"; return "<unknown>"; else { fluid_preset_t *preset = fluid_synth_get_channel_preset(fluidsynth, i); - //if (!preset) return "no preset"; if (!preset) return "<unknown>"; return preset->get_name(preset); } @@ -1267,6 +1271,7 @@ const MidiPatch* FluidSynth::getFirstPatch (int channel) const preset = sfont->get_preset (sfont, bank, patch); if (preset) { midiPatch.hbank = bank; + midiPatch.lbank = 0xff; // Off midiPatch.prog = patch; midiPatch.name = preset->get_name (preset); return &midiPatch; @@ -1280,7 +1285,8 @@ const MidiPatch* FluidSynth::getFirstPatch (int channel) const for (unsigned patch = 0; patch < 128; ++patch) { preset = sfont->get_preset (sfont, bank, patch); if (preset) { - midiPatch.hbank = bank; + midiPatch.hbank = 0xff; // Off + midiPatch.lbank = 0xff; // Off midiPatch.prog = patch; midiPatch.name = preset->get_name(preset); return &midiPatch; @@ -1318,6 +1324,7 @@ const MidiPatch* FluidSynth::getNextPatch (int channel, const MidiPatch* patch) if (preset) { //printf("Preset info: bank: %d prog: %d name: %s\n", bank, prog, preset->get_name(preset)); midiPatch.hbank = bank; + midiPatch.lbank = 0xff; // Off midiPatch.prog = prog; midiPatch.name = preset->get_name (preset); return &midiPatch; @@ -1333,7 +1340,8 @@ const MidiPatch* FluidSynth::getNextPatch (int channel, const MidiPatch* patch) preset = sfont->get_preset (sfont, bank, prog); if (preset) { //printf("Preset info: bank: %d prog: %d name: %s\n",bank, prog, preset->get_name(preset)); - midiPatch.hbank = bank; + midiPatch.hbank = 0xff; // Off + midiPatch.lbank = 0xff; // Off midiPatch.prog = prog; midiPatch.name = preset->get_name (preset); return &midiPatch; diff --git a/muse2/synti/fluidsynth/fluidsynti.h b/muse2/synti/fluidsynth/fluidsynti.h index 0628a385..7e448536 100644 --- a/muse2/synti/fluidsynth/fluidsynti.h +++ b/muse2/synti/fluidsynth/fluidsynti.h @@ -138,7 +138,7 @@ public: virtual bool setController(int, int, int); void setController(int, int , int, bool); virtual void getInitData(int*, const unsigned char**); - virtual const char* getPatchName(int, int, int, bool) const; + virtual const char* getPatchName(int, int, bool) const; virtual const MidiPatch* getPatchInfo(int i, const MidiPatch* patch) const; virtual int getControllerInfo(int, const char**, int*, int*, int*, int*) const; virtual bool processEvent(const MusECore::MidiPlayEvent&); diff --git a/muse2/synti/libsynti/mess.h b/muse2/synti/libsynti/mess.h index f9853330..79d3d7e7 100644 --- a/muse2/synti/libsynti/mess.h +++ b/muse2/synti/libsynti/mess.h @@ -88,7 +88,7 @@ class Mess { virtual void getInitData(int* n, const unsigned char**) /*const*/ { *n = 0; } // No const: Synths may need to allocate member pointers. p4.0.27 Tim virtual int getControllerInfo(int, const char**, int*, int*, int*, int*) const {return 0;} - virtual const char* getPatchName(int, int, int, bool) const { return "?"; } + virtual const char* getPatchName(int, int, bool) const { return "?"; } virtual const MidiPatch* getPatchInfo(int, const MidiPatch*) const { return 0; } // synthesizer -> host communication diff --git a/muse2/synti/simpledrums2/simpledrums.cpp b/muse2/synti/simpledrums2/simpledrums.cpp index 4265c33c..8b3ea69c 100644 --- a/muse2/synti/simpledrums2/simpledrums.cpp +++ b/muse2/synti/simpledrums2/simpledrums.cpp @@ -647,7 +647,7 @@ bool SimpleSynth::sysex(int len, const unsigned char* d) \return const char* with patchname */ //--------------------------------------------------------- -const char* SimpleSynth::getPatchName(int /*index*/, int, int) const +const char* SimpleSynth::getPatchName(int /*index*/, int, bool) const { SS_TRACE_IN SS_TRACE_OUT diff --git a/muse2/synti/simpledrums2/simpledrums.h b/muse2/synti/simpledrums2/simpledrums.h index f76a3237..8de202ea 100644 --- a/muse2/synti/simpledrums2/simpledrums.h +++ b/muse2/synti/simpledrums2/simpledrums.h @@ -137,7 +137,7 @@ class SimpleSynth : public Mess virtual bool processEvent(const MusECore::MidiPlayEvent& arg1); virtual bool setController(int arg1, int arg2, int arg3); virtual bool sysex(int arg1, const unsigned char* arg2); - virtual const char* getPatchName(int arg1, int arg2, int arg3) const; + virtual const char* getPatchName(int arg1, int arg2, bool arg3) const; virtual const MidiPatch* getPatchInfo(int arg1, const MidiPatch* arg2) const; virtual int getControllerInfo(int arg1, const char** arg2, int* arg3, int* arg4, int* arg5, int* arg6) const; virtual void processMessages(); |