summaryrefslogtreecommitdiff
path: root/muse2/muse
diff options
context:
space:
mode:
authorFlorian Jung <flo@windfisch.org>2011-12-30 17:55:58 +0000
committerFlorian Jung <flo@windfisch.org>2011-12-30 17:55:58 +0000
commit6f35a1b2b84ab6cfc5d77fd46d5e31887a1590e1 (patch)
treeadece0e0c4fbe63659741539296df9fd32bfcaab /muse2/muse
parent4d8477ab60093fc4c1f6190a931d0c2fdc65384c (diff)
instruments can load their patch'es drummaps
automatic setting of drummap according to patch this is turned off when the user manually changes the drummap TODO: let him turn it on again moved MidiTrack::read/writeOurDrummap out to helper.cpp extended xg.idf and gs.idf to ship the drummaps still work in progress, but should be usable and stable, though incomplete
Diffstat (limited to 'muse2/muse')
-rw-r--r--muse2/muse/CMakeLists.txt2
-rw-r--r--muse2/muse/app.cpp2
-rw-r--r--muse2/muse/helper.cpp199
-rw-r--r--muse2/muse/helper.h12
-rw-r--r--muse2/muse/instruments/minstrument.cpp188
-rw-r--r--muse2/muse/instruments/minstrument.h47
-rw-r--r--muse2/muse/midiedit/dcanvas.cpp21
-rw-r--r--muse2/muse/midiedit/dlist.cpp2
-rw-r--r--muse2/muse/midiedit/drumedit.cpp2
-rw-r--r--muse2/muse/midiedit/drummap.cpp73
-rw-r--r--muse2/muse/midiedit/drummap.h7
-rw-r--r--muse2/muse/midiedit/scoreedit.cpp4
-rw-r--r--muse2/muse/song.cpp1
-rw-r--r--muse2/muse/track.cpp246
-rw-r--r--muse2/muse/track.h10
-rw-r--r--muse2/muse/trackdrummapupdater.cpp61
-rw-r--r--muse2/muse/trackdrummapupdater.h43
17 files changed, 743 insertions, 177 deletions
diff --git a/muse2/muse/CMakeLists.txt b/muse2/muse/CMakeLists.txt
index 187bd41e..36645db4 100644
--- a/muse2/muse/CMakeLists.txt
+++ b/muse2/muse/CMakeLists.txt
@@ -65,6 +65,7 @@ QT4_WRAP_CPP ( muse_moc_headers
plugin.h
song.h
transport.h
+ trackdrummapupdater.h
value.h
steprec.h
)
@@ -132,6 +133,7 @@ file (GLOB core_source_files
thread.cpp
ticksynth.cpp
track.cpp
+ trackdrummapupdater.cpp
transport.cpp
undo.cpp
value.cpp
diff --git a/muse2/muse/app.cpp b/muse2/muse/app.cpp
index f63eb6e2..6a4e7c06 100644
--- a/muse2/muse/app.cpp
+++ b/muse2/muse/app.cpp
@@ -81,6 +81,7 @@
#include "tools.h"
#include "widgets/unusedwavefiles.h"
#include "functions.h"
+#include "trackdrummapupdater.h"
namespace MusECore {
extern void initMidiSynth();
@@ -346,6 +347,7 @@ MusE::MusE(int /*argc*/, char** /*argv*/) : QMainWindow()
MusEGlobal::heartBeatTimer->setObjectName("timer");
connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), MusEGlobal::song, SLOT(beat()));
connect(this, SIGNAL(activeTopWinChanged(MusEGui::TopWin*)), SLOT(activeTopWinChangedSlot(MusEGui::TopWin*)));
+ new MusECore::TrackDrummapUpdater(); // no need for keeping the reference, the thing autoconnects on its own.
#ifdef ENABLE_PYTHON
//---------------------------------------------------
diff --git a/muse2/muse/helper.cpp b/muse2/muse/helper.cpp
index 05cecc08..7d07a6ce 100644
--- a/muse2/muse/helper.cpp
+++ b/muse2/muse/helper.cpp
@@ -116,15 +116,12 @@ bool any_event_selected(const set<Part*>& parts, bool in_range)
return !get_events(parts, in_range ? 3 : 1).empty();
}
-bool drummaps_almost_equal(DrumMap* one, DrumMap* two, int len)
+bool drummaps_almost_equal(const DrumMap* one, const DrumMap* two, int len)
{
for (int i=0; i<len; i++)
- {
- DrumMap tmp = one[i];
- tmp.mute=two[i].mute;
- if (tmp!=two[i])
+ if (!one[i].almost_equals(two[i]))
return false;
- }
+
return true;
}
@@ -164,6 +161,196 @@ QSet<Part*> parts_at_tick(unsigned tick, const QSet<Track*>& tracks)
return result;
}
+bool parse_range(const QString& str, int* from, int* to)
+{
+ int idx = str.indexOf("-");
+ if (idx<0) // no "-" in str
+ {
+ bool ok;
+ int i = str.toInt(&ok);
+ if (!ok)
+ {
+ *from=-1; *to=-1;
+ return false;
+ }
+ else
+ {
+ *from=i; *to=i;
+ return true;
+ }
+ }
+ else // there is a "-" in str
+ {
+ QString str1=str.mid(0,idx);
+ QString str2=str.mid(idx+1);
+
+ bool ok;
+ int i = str1.toInt(&ok);
+ if (!ok)
+ {
+ *from=-1; *to=-1;
+ return false;
+ }
+ else
+ {
+ *from=i;
+
+ i = str2.toInt(&ok);
+ if (!ok)
+ {
+ *from=-1; *to=-1;
+ return false;
+ }
+ else
+ {
+ *to=i;
+ return true;
+ }
+ }
+ }
+}
+
+void write_new_style_drummap(int level, Xml& xml, const char* tagname,
+ DrumMap* drummap, bool* drummap_hidden, bool full)
+{
+ xml.tag(level++, tagname);
+
+ for (int i=0;i<128;i++)
+ {
+ DrumMap* dm = &drummap[i];
+ const DrumMap* idm = &iNewDrumMap[i];
+
+ if ( (dm->name != idm->name) || (dm->vol != idm->vol) ||
+ (dm->quant != idm->quant) || (dm->len != idm->len) ||
+ (dm->lv1 != idm->lv1) || (dm->lv2 != idm->lv2) ||
+ (dm->lv3 != idm->lv3) || (dm->lv4 != idm->lv4) ||
+ (dm->enote != idm->enote) || (dm->mute != idm->mute) ||
+ (drummap_hidden && drummap_hidden[i]) || full)
+ {
+ xml.tag(level++, "entry pitch=\"%d\"", i);
+
+ // when any of these "if"s changes, also update the large "if"
+ // above (this scope's parent)
+ if (full || dm->name != idm->name) xml.strTag(level, "name", dm->name);
+ if (full || dm->vol != idm->vol) xml.intTag(level, "vol", dm->vol);
+ if (full || dm->quant != idm->quant) xml.intTag(level, "quant", dm->quant);
+ if (full || dm->len != idm->len) xml.intTag(level, "len", dm->len);
+ if (full || dm->lv1 != idm->lv1) xml.intTag(level, "lv1", dm->lv1);
+ if (full || dm->lv2 != idm->lv2) xml.intTag(level, "lv2", dm->lv2);
+ if (full || dm->lv3 != idm->lv3) xml.intTag(level, "lv3", dm->lv3);
+ if (full || dm->lv4 != idm->lv4) xml.intTag(level, "lv4", dm->lv4);
+ if (full || dm->enote != idm->enote) xml.intTag(level, "enote", dm->enote);
+ if (full || dm->mute != idm->mute) xml.intTag(level, "mute", dm->mute);
+ if (drummap_hidden &&
+ (full || drummap_hidden[i])) xml.intTag(level, "hide", drummap_hidden[i]);
+
+ // anote is ignored anyway, as dm->anote == i, and this is
+ // already stored in the begin tag (pitch=...)
+
+ // channel and port are ignored as well, as they're not used
+ // in new-style-drum-mode
+
+ xml.tag(level--, "/entry");
+ }
+ }
+
+ xml.etag(level, tagname);
+}
+
+void read_new_style_drummap(Xml& xml, const char* tagname,
+ DrumMap* drummap, bool* drummap_hidden)
+{
+ for (;;)
+ {
+ Xml::Token token = xml.parse();
+ if (token == Xml::Error || token == Xml::End)
+ break;
+ const QString& tag = xml.s1();
+ switch (token)
+ {
+ case Xml::TagStart:
+ if (tag == "entry") // then read that entry with a nested loop
+ {
+ DrumMap* dm=NULL;
+ bool* hidden=NULL;
+ for (;;) // nested loop
+ {
+ Xml::Token token = xml.parse();
+ const QString& tag = xml.s1();
+ switch (token)
+ {
+ case Xml::Error:
+ case Xml::End:
+ goto end_of_nested_for;
+
+ case Xml::Attribut:
+ if (tag == "pitch")
+ {
+ int pitch = xml.s2().toInt() & 0x7f;
+ if (pitch < 0 || pitch > 127)
+ printf("ERROR: THIS SHOULD NEVER HAPPEN: invalid pitch in read_new_style_drummap()!\n");
+ else
+ {
+ dm = &drummap[pitch];
+ hidden = drummap_hidden ? &drummap_hidden[pitch] : NULL;
+ }
+ }
+ break;
+
+ case Xml::TagStart:
+ if (dm==NULL)
+ printf("ERROR: THIS SHOULD NEVER HAPPEN: no valid 'pitch' attribute in <entry> tag, but sub-tags follow in read_new_style_drummap()!\n");
+ else if (tag == "name")
+ dm->name = xml.parse(QString("name"));
+ else if (tag == "vol")
+ dm->vol = (unsigned char)xml.parseInt();
+ else if (tag == "quant")
+ dm->quant = xml.parseInt();
+ else if (tag == "len")
+ dm->len = xml.parseInt();
+ else if (tag == "lv1")
+ dm->lv1 = xml.parseInt();
+ else if (tag == "lv2")
+ dm->lv2 = xml.parseInt();
+ else if (tag == "lv3")
+ dm->lv3 = xml.parseInt();
+ else if (tag == "lv4")
+ dm->lv4 = xml.parseInt();
+ else if (tag == "enote")
+ dm->enote = xml.parseInt();
+ else if (tag == "mute")
+ dm->mute = xml.parseInt();
+ else if (tag == "hide")
+ {
+ if (hidden) *hidden = xml.parseInt();
+ }
+ else
+ xml.unknown("read_new_style_drummap");
+ break;
+
+ case Xml::TagEnd:
+ if (tag == "entry")
+ goto end_of_nested_for;
+
+ default:
+ break;
+ }
+ } // end of nested loop
+ end_of_nested_for: ;
+ } // end of 'if (tag == "entry")'
+ else
+ xml.unknown("read_new_style_drummap");
+ break;
+
+ case Xml::TagEnd:
+ if (tag == tagname)
+ return;
+
+ default:
+ break;
+ }
+ }
+}
} // namespace MusECore
diff --git a/muse2/muse/helper.h b/muse2/muse/helper.h
index 8ef39346..2a26c08e 100644
--- a/muse2/muse/helper.h
+++ b/muse2/muse/helper.h
@@ -44,11 +44,21 @@ QString pitch2string(int v);
Part* partFromSerialNumber(int serial);
bool any_event_selected(const std::set<Part*>&, bool in_range=false);
-bool drummaps_almost_equal(DrumMap* one, DrumMap* two, int drummap_size=128);
+bool drummaps_almost_equal(const DrumMap* one, const DrumMap* two, int drummap_size=128);
+
+// drummap_hidden may be NULL.
+void write_new_style_drummap(int level, Xml& xml, const char* tagname,
+ DrumMap* drummap, bool* drummap_hidden=NULL, bool full=false);
+void read_new_style_drummap(Xml& xml, const char* tagname,
+ DrumMap* drummap, bool* drummap_hidden=NULL);
+
QSet<Part*> parts_at_tick(unsigned tick);
QSet<Part*> parts_at_tick(unsigned tick, const Track* track);
QSet<Part*> parts_at_tick(unsigned tick, const QSet<Track*>& tracks);
+
+bool parse_range(const QString& str, int* from, int* to); // returns true if successful, false on error
+
}
namespace MusEGui {
diff --git a/muse2/muse/instruments/minstrument.cpp b/muse2/muse/instruments/minstrument.cpp
index a01903ae..3876fb28 100644
--- a/muse2/muse/instruments/minstrument.cpp
+++ b/muse2/muse/instruments/minstrument.cpp
@@ -41,6 +41,8 @@
#include "midictrl.h"
#include "gconfig.h"
#include "popupmenu.h"
+#include "drummap.h"
+#include "helper.h"
namespace MusECore {
@@ -364,6 +366,7 @@ void MidiInstrument::init()
MidiController* prog = new MidiController("Program", CTRL_PROGRAM, 0, 0xffffff, 0);
_controller->add(prog);
_dirty = false;
+
}
MidiInstrument::MidiInstrument()
@@ -408,8 +411,20 @@ MidiInstrument::~MidiInstrument()
if (_initScript)
delete _initScript;
+
+ clear_delete_patch_drummap_mapping();
}
+void MidiInstrument::clear_delete_patch_drummap_mapping()
+{
+ for (std::list<patch_drummap_mapping_t>::iterator it = patch_drummap_mapping.begin();
+ it!=patch_drummap_mapping.end(); it++)
+ delete[] it->drummap;
+
+ patch_drummap_mapping.clear();
+}
+
+
/*
//---------------------------------------------------------
// uniqueCopy
@@ -515,7 +530,20 @@ MidiInstrument& MidiInstrument::assign(const MidiInstrument& ins)
_name = ins._name;
_filePath = ins._filePath;
-
+
+ clear_delete_patch_drummap_mapping();
+ // do a deep copy
+ for (std::list<patch_drummap_mapping_t>::const_iterator it = ins.patch_drummap_mapping.begin();
+ it!=ins.patch_drummap_mapping.end(); it++)
+ {
+ patch_drummap_mapping_t temp = *it;
+ temp.drummap=new DrumMap[128];
+ for (int i=0;i<128;i++)
+ temp.drummap[i]=it->drummap[i];
+ }
+
+
+
// Hmm, dirty, yes? But init sets it to false...
//_dirty = ins._dirty;
//_dirty = false;
@@ -733,6 +761,123 @@ void MidiInstrument::readMidiState(Xml& xml)
}
}
+void MidiInstrument::readDrummaps(Xml& xml)
+{
+ clear_delete_patch_drummap_mapping();
+
+ for (;;)
+ {
+ Xml::Token token = xml.parse();
+ const QString& tag = xml.s1();
+ switch (token)
+ {
+ case Xml::Error:
+ case Xml::End:
+ return;
+
+ case Xml::TagStart:
+ if (tag == "entry")
+ patch_drummap_mapping.push_back(readDrummapsEntry(xml));
+ else
+ xml.unknown("MidiInstrument::readDrummaps");
+ break;
+
+ case Xml::TagEnd:
+ if (tag == "Drummaps")
+ return;
+
+ default:
+ break;
+ }
+ }
+ printf("ERROR: THIS CANNOT HAPPEN: exited infinite loop in MidiInstrument::readDrummaps()!\n"
+ " not returning anything. expect undefined behaviour or even crashes.\n");
+}
+
+patch_drummap_mapping_t MidiInstrument::readDrummapsEntry(Xml& xml)
+{
+ using std::list;
+
+ list<patch_collection_t> collection;
+ DrumMap* drummap=new DrumMap[128];
+ for (int i=0;i<128;i++)
+ drummap[i]=iNewDrumMap[i];
+
+ for (;;)
+ {
+ Xml::Token token = xml.parse();
+ const QString& tag = xml.s1();
+ switch (token)
+ {
+ case Xml::Error:
+ case Xml::End:
+ return patch_drummap_mapping_t(collection, drummap);
+
+ case Xml::TagStart:
+ if (tag == "patch_collection")
+ collection.push_back(readDrummapsEntryPatchCollection(xml));
+ else if (tag == "drummap")
+ read_new_style_drummap(xml, "drummap", drummap);
+ else
+ xml.unknown("MidiInstrument::readDrummapsEntry");
+ break;
+
+ case Xml::TagEnd:
+ if (tag == "entry")
+ return patch_drummap_mapping_t(collection, drummap);
+
+ default:
+ break;
+ }
+ }
+ printf("ERROR: THIS CANNOT HAPPEN: exited infinite loop in MidiInstrument::readDrummapsEntry()!\n"
+ " not returning anything. expect undefined behaviour or even crashes.\n");
+ return patch_drummap_mapping_t();
+}
+
+patch_collection_t MidiInstrument::readDrummapsEntryPatchCollection(Xml& xml)
+{
+ int first_prog=0, last_prog=256; // this means:
+ int first_lbank=0, last_lbank=256; // "does not matter"
+ int first_hbank=0, last_hbank=256;
+
+ for (;;)
+ {
+ Xml::Token token = xml.parse();
+ const QString& tag = xml.s1();
+ switch (token)
+ {
+ case Xml::Error:
+ case Xml::End:
+ return patch_collection_t(-1,-1,-1,-1,-1,-1); // an invalid collection
+
+ case Xml::TagStart:
+ xml.unknown("MidiInstrument::readDrummapsEntryPatchCollection");
+ break;
+
+ case Xml::Attribut:
+ if (tag == "prog")
+ parse_range(xml.s2(), &first_prog, &last_prog);
+ else if (tag == "lbank")
+ parse_range(xml.s2(), &first_lbank, &last_lbank);
+ else if (tag == "hbank")
+ parse_range(xml.s2(), &first_hbank, &last_hbank);
+ break;
+
+ case Xml::TagEnd:
+ if (tag == "patch_collection")
+ return patch_collection_t(first_prog, last_prog, first_lbank, last_lbank, first_hbank, last_hbank);
+
+ default:
+ break;
+ }
+ }
+
+ printf("ERROR: THIS CANNOT HAPPEN: exited infinite loop in MidiInstrument::readDrummapsEntryPatchCollection()!\n"
+ " not returning anything. expect undefined behaviour or even crashes.\n");
+}
+
+
//---------------------------------------------------------
// read
//---------------------------------------------------------
@@ -785,6 +930,9 @@ void MidiInstrument::read(Xml& xml)
_controller->add(mc);
}
+ else if (tag == "Drummaps") {
+ readDrummaps(xml);
+ }
else if (tag == "Init")
readEventList(xml, _midiInit, "Init");
else if (tag == "Reset")
@@ -794,7 +942,7 @@ void MidiInstrument::read(Xml& xml)
else if (tag == "InitScript") {
if (_initScript)
delete _initScript;
- QByteArray ba = xml.parse1().toLatin1();
+ QByteArray ba = xml.parse1().toLatin1();
const char* istr = ba.constData();
int len = strlen(istr) +1;
if (len > 1) {
@@ -998,4 +1146,40 @@ void MidiInstrument::populatePatchPopup(MusEGui::PopupMenu* menu, int chan, MTyp
}
+const DrumMap* MidiInstrument::drummap_for_patch(int patch) const
+{
+ using std::list;
+
+ int program = (patch & 0x0000FF);
+ int lbank = (patch & 0x00FF00) >> 8;
+ int hbank = (patch & 0xFF0000) >> 16;
+
+ for (list<patch_drummap_mapping_t>::const_iterator it=patch_drummap_mapping.begin();
+ it!=patch_drummap_mapping.end(); it++)
+ {
+ for (list<patch_collection_t>::const_iterator it2=it->affected_patches.begin();
+ it2!=it->affected_patches.end(); it2++)
+ {
+ // if the entry matches our patch
+ if ( (program >= it2->first_program && program <= it2->last_program) &&
+ (hbank >= it2->first_hbank && hbank <= it2->last_hbank) &&
+ (lbank >= it2->first_lbank && lbank <= it2->last_lbank) )
+ {
+ return it->drummap;
+ }
+ }
+ }
+
+ // if nothing was found
+ return iNewDrumMap;
+}
+
+patch_drummap_mapping_t::patch_drummap_mapping_t()
+{
+ drummap=new DrumMap[128];
+ for (int i=0;i<128;i++)
+ drummap[i]=iNewDrumMap[i];
+}
+
+
} // namespace MusECore
diff --git a/muse2/muse/instruments/minstrument.h b/muse2/muse/instruments/minstrument.h
index 385e67b4..9a65598b 100644
--- a/muse2/muse/instruments/minstrument.h
+++ b/muse2/muse/instruments/minstrument.h
@@ -40,6 +40,7 @@ class MidiControllerList;
class MidiPort;
class MidiPlayEvent;
class Xml;
+class DrumMap;
//---------------------------------------------------------
@@ -80,6 +81,43 @@ struct SysEx {
unsigned char* data;
};
+
+
+struct patch_collection_t
+{
+ int first_program;
+ int last_program;
+ int first_hbank;
+ int last_hbank;
+ int first_lbank;
+ int last_lbank;
+
+ patch_collection_t(int p1=0, int p2=127, int l1=0, int l2=127, int h1=0, int h2=127)
+ {
+ first_program=p1;
+ last_program=p2;
+ first_lbank=l1;
+ last_lbank=l2;
+ first_hbank=h1;
+ last_hbank=h2;
+ }
+
+};
+
+struct patch_drummap_mapping_t
+{
+ std::list<patch_collection_t> affected_patches;
+ DrumMap* drummap;
+
+ patch_drummap_mapping_t(const std::list<patch_collection_t>& a, DrumMap* d)
+ {
+ affected_patches=a;
+ drummap=d;
+ }
+
+ patch_drummap_mapping_t();
+};
+
//---------------------------------------------------------
// MidiInstrument
//---------------------------------------------------------
@@ -88,6 +126,7 @@ class MidiInstrument {
PatchGroupList pg;
MidiControllerList* _controller;
QList<SysEx*> _sysex;
+ std::list<patch_drummap_mapping_t> patch_drummap_mapping;
bool _dirty;
int _nullvalue;
@@ -103,6 +142,12 @@ class MidiInstrument {
char* _initScript;
QString _name;
QString _filePath;
+
+ void clear_delete_patch_drummap_mapping();
+
+ void readDrummaps(Xml& xml);
+ patch_drummap_mapping_t readDrummapsEntry(Xml& xml);
+ patch_collection_t readDrummapsEntryPatchCollection(Xml& xml);
public:
MidiInstrument();
@@ -123,6 +168,8 @@ class MidiInstrument {
void removeSysex(SysEx* sysex) { _sysex.removeAll(sysex); }
void addSysex(SysEx* sysex) { _sysex.append(sysex); }
+ const DrumMap* drummap_for_patch(int patch) const;
+
EventList* midiInit() const { return _midiInit; }
EventList* midiReset() const { return _midiReset; }
EventList* midiState() const { return _midiState; }
diff --git a/muse2/muse/midiedit/dcanvas.cpp b/muse2/muse/midiedit/dcanvas.cpp
index 4c033946..ad0226da 100644
--- a/muse2/muse/midiedit/dcanvas.cpp
+++ b/muse2/muse/midiedit/dcanvas.cpp
@@ -1008,6 +1008,19 @@ void DrumCanvas::mapChanged(int spitch, int dpitch)
using MusEGlobal::global_drum_ordering_t;
using MusEGlobal::global_drum_ordering;
+ for (QSet<MusECore::Track*>::iterator it=instrument_map[spitch].tracks.begin();
+ it!=instrument_map[spitch].tracks.end(); it++)
+ {
+ if (dynamic_cast<MusECore::MidiTrack*>(*it))
+ dynamic_cast<MusECore::MidiTrack*>(*it)->set_drummap_ordering_tied_to_patch(false);
+ }
+ for (QSet<MusECore::Track*>::iterator it=instrument_map[dpitch].tracks.begin();
+ it!=instrument_map[dpitch].tracks.end(); it++)
+ {
+ if (dynamic_cast<MusECore::MidiTrack*>(*it))
+ dynamic_cast<MusECore::MidiTrack*>(*it)->set_drummap_ordering_tied_to_patch(false);
+ }
+
MusECore::DrumMap dm_temp = ourDrumMap[spitch];
instrument_number_mapping_t im_temp = instrument_map[spitch];
@@ -1450,9 +1463,13 @@ void DrumCanvas::propagate_drummap_change(int instr, bool update_druminmap)
for (QSet<MusECore::Track*>::const_iterator it = tracks.begin(); it != tracks.end(); it++)
{
- dynamic_cast<MusECore::MidiTrack*>(*it)->drummap()[index] = ourDrumMap[instr];
+ MusECore::MidiTrack* mt=dynamic_cast<MusECore::MidiTrack*>(*it);
+ // if we're not only changing "mute" state...
+ if (!mt->drummap()[index].almost_equals(ourDrumMap[instr]))
+ mt->set_drummap_tied_to_patch(false);
+ mt->drummap()[index] = ourDrumMap[instr];
if (update_druminmap)
- dynamic_cast<MusECore::MidiTrack*>(*it)->update_drum_in_map();
+ mt->update_drum_in_map();
}
}
diff --git a/muse2/muse/midiedit/dlist.cpp b/muse2/muse/midiedit/dlist.cpp
index 2eb5e64a..fc6384f7 100644
--- a/muse2/muse/midiedit/dlist.cpp
+++ b/muse2/muse/midiedit/dlist.cpp
@@ -401,6 +401,7 @@ void DList::viewMousePressEvent(QMouseEvent* ev)
{
MusECore::MidiTrack* mt = dynamic_cast<MusECore::MidiTrack*>(*it);
mt->drummap()[mt->map_drum_in(val)].enote=dm->enote;
+ mt->set_drummap_tied_to_patch(false);
}
// propagating this is unneccessary as it's already done.
// updating the drumInmap is unneccessary, as the propagate call below
@@ -900,6 +901,7 @@ void DList::pitchEdited()
{
MusECore::MidiTrack* mt = dynamic_cast<MusECore::MidiTrack*>(*it);
mt->drummap()[mt->map_drum_in(val)].enote=editEntry->enote;
+ mt->set_drummap_tied_to_patch(false);
}
// propagating this is unneccessary as it's already done.
// updating the drumInmap is unneccessary, as the propagate call below
diff --git a/muse2/muse/midiedit/drumedit.cpp b/muse2/muse/midiedit/drumedit.cpp
index 3bb48705..1f159d1a 100644
--- a/muse2/muse/midiedit/drumedit.cpp
+++ b/muse2/muse/midiedit/drumedit.cpp
@@ -644,7 +644,7 @@ DrumEdit::DrumEdit(MusECore::PartList* pl, QWidget* parent, const char* name, un
void DrumEdit::songChanged1(int bits)
{
- if(_isDeleting) // Ignore while while deleting to prevent crash.
+ if(_isDeleting) // Ignore while deleting to prevent crash.
return;
if (bits & SC_SOLO)
diff --git a/muse2/muse/midiedit/drummap.cpp b/muse2/muse/midiedit/drummap.cpp
index 2705a252..d86bcd65 100644
--- a/muse2/muse/midiedit/drummap.cpp
+++ b/muse2/muse/midiedit/drummap.cpp
@@ -43,15 +43,13 @@ namespace MusECore {
const DrumMap blankdm = { QString(""), 100, 16, 32, 9, 0, 70, 90, 127, 110, 127, 127, false };
-// this map must have 128 entries, as it's used for initalising new-style-drummaps as well.
-// new-style-drummaps only have 128 entries. also, the every "out-note" ("anote") must be
-// represented exactly once in that map, and there may be no duplicate or unused "out-notes".
-// reason: the track's drummap are inited as follows: iterate through the full idrumMap[],
-// tracks_drummap[ idrumMap[i].anote ] = idrumMap[i]
-// if you ever want to change this, you will need to go through track.cpp/.h and
-// {dlist,dcanvas,drumedit,drummap}{.cpp,.h} (and possibly some more) and find every usage
-// of idrumMap by new style drummaps, and fix the problem. a possible fix would be duplicating
-// idrumMap, change idrumMap, and use the duplicate for the new style stuff instead.
+// this map should have 128 entries, as it's used for initalising iNewDrumMap as well.
+// iNewDrumMap only has 128 entries. also, the every "out-note" ("anote") should be
+// represented exactly once in idrumMap, and there shall be no duplicate or unused
+// "out-notes".
+// reason: iNewDrumMap is inited as follows: iterate through the full idrumMap[],
+// iNewDrumMap[ idrumMap[i].anote ] = idrumMap[i]
+// if you ever want to change this, you will need to fix the initNewDrumMap() function.
const DrumMap idrumMap[DRUM_MAPSIZE] = {
{ QString("Acoustic Bass Drum"), 100, 16, 32, 9, 0, 70, 90, 127, 110, 35, 35, false },
{ QString("Bass Drum 1"), 100, 16, 32, 9, 0, 70, 90, 127, 110, 36, 36, false },
@@ -261,6 +259,55 @@ const DrumMap idrumMap[DRUM_MAPSIZE] = {
{ QString(""), 100, 16, 32, 9, 0, 70, 90, 127, 110, 34, 34, false }
};
+DrumMap iNewDrumMap[128];
+
+void initNewDrumMap()
+{
+ bool done[128];
+ for (int i=0;i<128;i++) done[i]=false;
+
+ for (int i=0;i<DRUM_MAPSIZE;i++)
+ {
+ int idx=idrumMap[i].anote;
+ if (idx < 0 || idx >= 128)
+ printf("ERROR: THIS SHOULD NEVER HAPPEN: idrumMap[%i].anote is not within 0..127!\n", idx);
+ else
+ {
+ if (done[idx]==true)
+ {
+ printf("ERROR: iNewDrumMap[%i] is already initalized!\n"
+ " this will be probably not a problem, but some programmer didn't read\n"
+ " flo's comment at drummap.cpp, above idrumMap[].\n", idx);
+ }
+ else
+ {
+ iNewDrumMap[idx]=idrumMap[i];
+ done[idx]=true;
+ }
+ }
+ }
+
+ for (int i=0;i<128;i++)
+ {
+ if (done[i]==false)
+ {
+ printf("ERROR: iNewDrumMap[%i] is uninitalized!\n"
+ " this will be probably not a problem, but some programmer didn't read\n"
+ " flo's comment at drummap.cpp, above idrumMap[].\n", i);
+ iNewDrumMap[i].name="";
+ iNewDrumMap[i].vol=100;
+ iNewDrumMap[i].quant=16;
+ iNewDrumMap[i].len=32;
+ iNewDrumMap[i].lv1=70;
+ iNewDrumMap[i].lv2=90;
+ iNewDrumMap[i].lv3=127;
+ iNewDrumMap[i].lv4=110;
+ iNewDrumMap[i].enote=i;
+ iNewDrumMap[i].anote=i;
+ }
+ }
+}
+
//---------------------------------------------------------
// initDrumMap
@@ -332,6 +379,14 @@ bool DrumMap::operator==(const DrumMap& map) const
&& mute == map.mute;
}
+bool DrumMap::almost_equals(const DrumMap& map) const
+{
+ DrumMap tmp=map;
+ tmp.mute=this->mute;
+ return tmp==*this;
+}
+
+
//---------------------------------------------------------
// writeDrumMap
//---------------------------------------------------------
diff --git a/muse2/muse/midiedit/drummap.h b/muse2/muse/midiedit/drummap.h
index 3b6ffaf3..7f28b068 100644
--- a/muse2/muse/midiedit/drummap.h
+++ b/muse2/muse/midiedit/drummap.h
@@ -49,12 +49,15 @@ struct DrumMap {
bool operator==(const DrumMap& map) const;
bool operator!=(const DrumMap& map) const { return !operator==(map); }
+ bool almost_equals(const DrumMap& map) const;
};
-// please let this at "128". idrumMap must have length 128 (see drummap.cpp for details)
+// please let this at "128". idrumMap should have length 128 (see drummap.cpp for details)
#define DRUM_MAPSIZE 128
-extern const DrumMap idrumMap[DRUM_MAPSIZE]; //FINDMICH dummy!
+extern DrumMap iNewDrumMap[128];
+extern void initNewDrumMap();
+
extern void initDrumMap();
extern void writeDrumMap(int level, Xml& xml, bool external);
extern void readDrumMap(Xml& xml, bool external);
diff --git a/muse2/muse/midiedit/scoreedit.cpp b/muse2/muse/midiedit/scoreedit.cpp
index c10adafd..055134e2 100644
--- a/muse2/muse/midiedit/scoreedit.cpp
+++ b/muse2/muse/midiedit/scoreedit.cpp
@@ -4686,8 +4686,8 @@ void ScoreCanvas::add_new_parts(const std::map< MusECore::Part*, std::set<MusECo
* ("BUGGY! problem is: while changing entries, ourDrumMap
* may be reallocated which causes abort()s and/or bugs.")
* [ seems to work now, needs further testing! ]
- * > o my record flag handling
- * o once, using super glue while a score editor displaying the glued
+ * x my record flag handling
+ * * once, using super glue while a score editor displaying the glued
* parts is open let muse segfault. this may or may not be fixed
* now. check!
* state of revision #1337: no segfaults, but the score editors
diff --git a/muse2/muse/song.cpp b/muse2/muse/song.cpp
index 484e2d42..69900bbc 100644
--- a/muse2/muse/song.cpp
+++ b/muse2/muse/song.cpp
@@ -2074,6 +2074,7 @@ void Song::clear(bool signal, bool clear_all)
// _tempo = 500000; // default tempo 120
dirty = false;
initDrumMap();
+ initNewDrumMap();
if (signal) {
emit loopChanged(false);
recordChanged(false);
diff --git a/muse2/muse/track.cpp b/muse2/muse/track.cpp
index 4dd2e0c5..595a25e7 100644
--- a/muse2/muse/track.cpp
+++ b/muse2/muse/track.cpp
@@ -34,6 +34,8 @@
#include "globaldefs.h"
#include "route.h"
#include "drummap.h"
+#include "midictrl.h"
+#include "helper.h"
namespace MusECore {
@@ -618,19 +620,28 @@ void MidiTrack::init()
_recEcho = true;
}
+void MidiTrack::init_drum_ordering()
+{
+ // first display entries with non-empty names, then with empty names.
+
+ remove_ourselves_from_drum_ordering();
+
+ for (int i=0;i<128;i++)
+ if (_drummap[i].name!="" && _drummap[i].name!="?") // non-empty name?
+ MusEGlobal::global_drum_ordering.push_back(std::pair<MidiTrack*,int>(this,i));
+
+ for (int i=0;i<128;i++)
+ if (!(_drummap[i].name!="" && _drummap[i].name!="?")) // empty name?
+ MusEGlobal::global_drum_ordering.push_back(std::pair<MidiTrack*,int>(this,i));
+}
+
void MidiTrack::init_drummap(bool write_ordering)
{
for (int i=0;i<128;i++)
- {
- int idx=idrumMap[i].anote;
- if (idx < 0 || idx >= 128)
- printf ("ERROR: THIS SHOULD NEVER HAPPEN: idrumMap[%i].anote is not within 0..127!\n", idx);
- else
- _drummap[idx]=idrumMap[i];
-
- if (write_ordering)
- MusEGlobal::global_drum_ordering.push_back(std::pair<MidiTrack*,int>(this,idx));
- }
+ _drummap[i]=iNewDrumMap[i];
+
+ if (write_ordering)
+ init_drum_ordering();
update_drum_in_map();
@@ -638,6 +649,7 @@ void MidiTrack::init_drummap(bool write_ordering)
_drummap_hidden[i]=false;
_drummap_tied_to_patch=true;
+ _drummap_ordering_tied_to_patch=true;
}
void MidiTrack::update_drum_in_map()
@@ -945,62 +957,14 @@ void MidiTrack::writeOurDrumSettings(int level, Xml& xml) const
writeOurDrumMap(level, xml, false);
xml.intTag(level, "tied", _drummap_tied_to_patch);
+ xml.intTag(level, "ordering_tied", _drummap_ordering_tied_to_patch);
xml.etag(level, "our_drum_settings");
}
void MidiTrack::writeOurDrumMap(int level, Xml& xml, bool full) const
{
- xml.tag(level++, "our_drummap");
-
- for (int i=0;i<128;i++)
- {
- int j;
- for (j=0;j<128;j++)
- if (idrumMap[j].anote == i) break;
-
- if (j==128)
- printf("THIS SHOULD NEVER HAPPEN: couldn't find initial drum map entry in MidiTrack::writeOurDrumMap()\n");
- else
- {
- DrumMap* dm = &_drummap[i];
- const DrumMap* idm = &idrumMap[j];
-
- if ( (dm->name != idm->name) || (dm->vol != idm->vol) ||
- (dm->quant != idm->quant) || (dm->len != idm->len) ||
- (dm->lv1 != idm->lv1) || (dm->lv2 != idm->lv2) ||
- (dm->lv3 != idm->lv3) || (dm->lv4 != idm->lv4) ||
- (dm->enote != idm->enote) || (dm->mute != idm->mute) ||
- _drummap_hidden[i] || full)
- {
- xml.tag(level++, "entry pitch=\"%d\"", i);
-
- // when any of these "if"s changes, also update the large "if"
- // above (this scope's parent)
- if (full || dm->name != idm->name) xml.strTag(level, "name", dm->name);
- if (full || dm->vol != idm->vol) xml.intTag(level, "vol", dm->vol);
- if (full || dm->quant != idm->quant) xml.intTag(level, "quant", dm->quant);
- if (full || dm->len != idm->len) xml.intTag(level, "len", dm->len);
- if (full || dm->lv1 != idm->lv1) xml.intTag(level, "lv1", dm->lv1);
- if (full || dm->lv2 != idm->lv2) xml.intTag(level, "lv2", dm->lv2);
- if (full || dm->lv3 != idm->lv3) xml.intTag(level, "lv3", dm->lv3);
- if (full || dm->lv4 != idm->lv4) xml.intTag(level, "lv4", dm->lv4);
- if (full || dm->enote != idm->enote) xml.intTag(level, "enote", dm->enote);
- if (full || dm->mute != idm->mute) xml.intTag(level, "mute", dm->mute);
- if (full || _drummap_hidden[i]) xml.intTag(level, "hide", _drummap_hidden[i]);
-
- // anote is ignored anyway, as dm->anote == i, and this is
- // already stored in the begin tag (pitch=...)
-
- // channel and port are ignored as well, as they're not used
- // in new-style-drum-mode
-
- xml.tag(level--, "/entry");
- }
- }
- }
-
- xml.etag(level, "our_drummap");
+ write_new_style_drummap(level, xml, "our_drummap", _drummap, _drummap_hidden, full);
}
//---------------------------------------------------------
@@ -1095,6 +1059,8 @@ void MidiTrack::readOurDrumSettings(Xml& xml)
case Xml::TagStart:
if (tag == "tied")
_drummap_tied_to_patch = xml.parseInt();
+ else if (tag == "ordering_tied")
+ _drummap_ordering_tied_to_patch = xml.parseInt();
else if (tag == "our_drummap")
readOurDrumMap(xml);
else
@@ -1115,98 +1081,9 @@ void MidiTrack::readOurDrumMap(Xml& xml, bool dont_init)
{
if (!dont_init) init_drummap(false);
_drummap_tied_to_patch=false;
-
- for (;;)
- {
- Xml::Token token = xml.parse();
- if (token == Xml::Error || token == Xml::End)
- break;
- const QString& tag = xml.s1();
- switch (token)
- {
- case Xml::TagStart:
- if (tag == "entry") // then read that entry with a nested loop
- {
- DrumMap* dm=NULL;
- bool* hidden=NULL;
- for (;;) // nested loop
- {
- Xml::Token token = xml.parse();
- const QString& tag = xml.s1();
- switch (token)
- {
- case Xml::Error:
- case Xml::End:
- goto end_of_nested_for;
-
- case Xml::Attribut:
- if (tag == "pitch")
- {
- int pitch = xml.s2().toInt() & 0x7f;
- if (pitch < 0 || pitch > 127)
- printf("ERROR: THIS SHOULD NEVER HAPPEN: invalid pitch in MidiTrack::readOurDrumMap()!\n");
- else
- {
- dm = &_drummap[pitch];
- hidden = &_drummap_hidden[pitch];
- }
- }
- break;
-
- case Xml::TagStart:
- if (dm==NULL)
- printf("ERROR: THIS SHOULD NEVER HAPPEN: no valid 'pitch' attribute in <entry> tag, but sub-tags follow in MidiTrack::readOurDrumMap()!\n");
- else if (tag == "name")
- dm->name = xml.parse(QString("name"));
- else if (tag == "vol")
- dm->vol = (unsigned char)xml.parseInt();
- else if (tag == "quant")
- dm->quant = xml.parseInt();
- else if (tag == "len")
- dm->len = xml.parseInt();
- else if (tag == "lv1")
- dm->lv1 = xml.parseInt();
- else if (tag == "lv2")
- dm->lv2 = xml.parseInt();
- else if (tag == "lv3")
- dm->lv3 = xml.parseInt();
- else if (tag == "lv4")
- dm->lv4 = xml.parseInt();
- else if (tag == "enote")
- dm->enote = xml.parseInt();
- else if (tag == "mute")
- dm->mute = xml.parseInt();
- else if (tag == "hide")
- *hidden = xml.parseInt();
- else
- xml.unknown("MidiTrack::readOurDrumMap");
- break;
-
- case Xml::TagEnd:
- if (tag == "entry")
- goto end_of_nested_for;
-
- default:
- break;
- }
- } // end of nested loop
- end_of_nested_for: ;
- } // end of 'if (tag == "entry")'
- else
- xml.unknown("MidiTrack::readOurDrumMap");
- break;
-
- case Xml::TagEnd:
- if (tag == "our_drummap")
- {
- update_drum_in_map();
- return;
- }
-
- default:
- break;
- }
- }
+ _drummap_ordering_tied_to_patch=false;
+ read_new_style_drummap(xml, "our_drummap", _drummap, _drummap_hidden);
+ update_drum_in_map();
}
@@ -1395,4 +1272,69 @@ void Track::writeRouting(int level, Xml& xml) const
}
}
+int MidiTrack::getFirstControllerValue(int ctrl, int def)
+{
+ int val=def;
+ unsigned tick=-1; // maximum integer
+
+ for (iPart pit=parts()->begin(); pit!=parts()->end(); pit++)
+ {
+ Part* part=pit->second;
+ if (part->tick() > tick) break; // ignore this and the rest. we won't find anything new.
+ for (iEvent eit=part->events()->begin(); eit!=part->events()->end(); eit++)
+ {
+ if (eit->first+part->tick() >= tick) break;
+ // else if (eit->first+part->tick() < tick) and
+ if (eit->second.type()==Controller && eit->second.dataA()==ctrl)
+ {
+ val = eit->second.dataB();
+ tick = eit->first+part->tick();
+ break;
+ }
+ }
+ }
+
+ return val;
+}
+
+
+// returns true if the autoupdate changed something
+bool MidiTrack::auto_update_drummap()
+{
+ if (_drummap_tied_to_patch)
+ {
+ int patch = getFirstControllerValue(CTRL_PROGRAM,0);
+ const DrumMap* new_drummap = MusEGlobal::midiPorts[_outPort].instrument()->drummap_for_patch(patch);
+
+ if (!drummaps_almost_equal(new_drummap, this->drummap(), 128))
+ {
+ for (int i=0;i<128;i++)
+ {
+ bool temp_mute=_drummap[i].mute;
+ _drummap[i]=new_drummap[i];
+ _drummap[i].mute=temp_mute;
+ }
+
+ if (_drummap_ordering_tied_to_patch)
+ init_drum_ordering();
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void MidiTrack::set_drummap_tied_to_patch(bool val)
+{
+ _drummap_tied_to_patch=val;
+ if (val) auto_update_drummap();
+}
+
+void MidiTrack::set_drummap_ordering_tied_to_patch(bool val)
+{
+ _drummap_ordering_tied_to_patch=val;
+ if (val && _drummap_tied_to_patch) init_drum_ordering();
+}
+
} // namespace MusECore
diff --git a/muse2/muse/track.h b/muse2/muse/track.h
index f2e12e3a..1df8cad0 100644
--- a/muse2/muse/track.h
+++ b/muse2/muse/track.h
@@ -236,11 +236,13 @@ class MidiTrack : public Track {
DrumMap* _drummap; // _drummap[foo].anote is always equal to foo
bool* _drummap_hidden; // _drummap und _drummap_hidden will be an array[128]
bool _drummap_tied_to_patch; //if true, changing patch also changes drummap
+ bool _drummap_ordering_tied_to_patch; //if true, changing patch also changes drummap-ordering
int drum_in_map[128];
void init();
void init_drummap(bool write_ordering); // function without argument in public
void remove_ourselves_from_drum_ordering();
+ void init_drum_ordering();
void writeOurDrumSettings(int level, Xml& xml) const;
void readOurDrumSettings(Xml& xml);
@@ -310,6 +312,8 @@ class MidiTrack : public Track {
virtual bool canRecord() const { return true; }
static void setVisible(bool t) { _isVisible = t; }
static bool visible() { return _isVisible; }
+
+ int getFirstControllerValue(int ctrl, int def=-1);
void setClef(clefTypes i) { clefType = i; }
clefTypes getClef() { return clefType; }
@@ -320,6 +324,12 @@ class MidiTrack : public Track {
void update_drum_in_map();
void init_drummap() { init_drummap(false); } // function with argument in private
+
+ bool auto_update_drummap();
+ void set_drummap_tied_to_patch(bool);
+ bool drummap_tied_to_patch() { return _drummap_tied_to_patch; }
+ void set_drummap_ordering_tied_to_patch(bool);
+ bool drummap_ordering_tied_to_patch() { return _drummap_ordering_tied_to_patch; }
//void writeOurDrumSettings(int level, Xml& xml) const; // above in private:
//void readOurDrumSettings(Xml& xml); // above in private:
diff --git a/muse2/muse/trackdrummapupdater.cpp b/muse2/muse/trackdrummapupdater.cpp
new file mode 100644
index 00000000..35d5c056
--- /dev/null
+++ b/muse2/muse/trackdrummapupdater.cpp
@@ -0,0 +1,61 @@
+//=========================================================
+// MusE
+// Linux Music Editor
+// $Id: trackdrummapupdater.cpp,v 1.59.2.52 2011/12/27 20:25:58 flo93 Exp $
+//
+// (C) Copyright 2011 Florian Jung (florian.a.jung (at) web.de)
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; version 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+//=========================================================
+
+#include "trackdrummapupdater.h"
+#include "song.h"
+#include "globals.h"
+
+namespace MusECore {
+
+using MusEGlobal::song;
+
+TrackDrummapUpdater::TrackDrummapUpdater()
+{
+ connect(song,SIGNAL(songChanged(int)), this, SLOT(songChanged(int)));
+}
+
+void TrackDrummapUpdater::songChanged(int flags)
+{
+ if (flags & (SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED |
+ SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED |
+ SC_EVENT_INSERTED | SC_EVENT_REMOVED | SC_EVENT_MODIFIED ) )
+ {
+ bool changed=false;
+ for (iTrack t=song->tracks()->begin(); t!=song->tracks()->end(); t++)
+ {
+ MidiTrack* track=dynamic_cast<MidiTrack*>(*t);
+ if (track && track->auto_update_drummap())
+ changed=true;
+ }
+
+ if (changed)
+ {
+ // allow recursion. there will be no more recursion, because this
+ // is only executed when something other than SC_DRUMMAP happens
+ song->update(SC_DRUMMAP, true);
+ }
+
+ }
+}
+
+} //namespace MusECore
diff --git a/muse2/muse/trackdrummapupdater.h b/muse2/muse/trackdrummapupdater.h
new file mode 100644
index 00000000..caa2ac28
--- /dev/null
+++ b/muse2/muse/trackdrummapupdater.h
@@ -0,0 +1,43 @@
+//=========================================================
+// MusE
+// Linux Music Editor
+// $Id: trackdrummapupdater.h,v 1.59.2.52 2011/12/27 20:25:58 flo93 Exp $
+//
+// (C) Copyright 2011 Florian Jung (florian.a.jung (at) web.de)
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; version 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+//=========================================================
+
+#ifndef __TRACKDRUMMAPUPDATER_H__
+#define __TRACKDRUMMAPUPDATER_H__
+
+#include <QObject>
+
+namespace MusECore {
+
+class TrackDrummapUpdater : public QObject
+{
+ Q_OBJECT
+
+ public:
+ TrackDrummapUpdater();
+
+ private slots:
+ void songChanged(int flags);
+};
+
+} //namespace MusECore
+#endif