1 //=========================================================
2 // MusE
3 // Linux Music Editor
4 // $Id: minstrument.cpp,v 1.10.2.5 2009/03/28 01:46:10 terminator356 Exp $
5 //
6 // (C) Copyright 2000-2003 Werner Schweer (ws@seh.de)
7 // (C) Copyright 2016 Tim E. Real (terminator356 on users dot sourceforge dot net)
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; version 2 of
12 // the License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 //
23 //=========================================================
24
25 #include <stdio.h>
26 #include <string.h>
27
28 #include <QAction>
29 #include <QDir>
30 #include <QFileInfo>
31 #include <QString>
32 #include <QByteArray>
33 #include <QApplication>
34
35 #include "minstrument.h"
36 #include "mididev.h"
37 #include "midiport.h"
38 #include "audio.h"
39 #include "midi_consts.h"
40 #include "globals.h"
41 #include "midictrl.h"
42 #include "gconfig.h"
43 #include "helper.h"
44 #include "menutitleitem.h"
45 #include "synth.h"
46 #include "icons.h"
47
48 // Forwards from header:
49 #include "event.h"
50 #include "popupmenu.h"
51 #include "midi_controller.h"
52 #include "mpevent.h"
53 #include "xml.h"
54
55 #ifdef _USE_INSTRUMENT_OVERRIDES_
56 namespace MusEGlobal {
57 // This list holds instrument drum map overrides read from config.
58 // Whenever an instrument has been loaded it will adopt any corresponding item in this list.
59 // (Instruments are loaded long after config is loaded. So we need this 'holding' list.)
60 MusECore::WorkingDrumMapInstrumentList workingDrumMapInstrumentList;
61 }
62 #endif
63
64 namespace MusECore {
65
66 MidiInstrumentList midiInstruments;
67 MidiInstrument* genericMidiInstrument;
68
69 //---------------------------------------------------------
70 // string2sysex
71 // Return -1 if cannot be converted.
72 //---------------------------------------------------------
73
string2sysex(const QString & s,unsigned char ** data)74 int string2sysex(const QString& s, unsigned char** data)
75 {
76 QByteArray ba = s.toLatin1();
77 const char* src = ba.constData();
78 char buffer[2048];
79 char* dst = buffer;
80
81 if(src) {
82 while (*src) {
83 while (*src == ' ' || *src == '\n') {
84 ++src;
85 }
86 if(!(*src))
87 break;
88 char* ep;
89 long val = strtol(src, &ep, 16);
90 if (ep == src) {
91 printf("string2sysex: Cannot convert string to sysex %s\n", src);
92 return -1;
93 }
94 src = ep;
95 // Strip all f0 and f7 (whether accidental or on purpose enclosing etc).
96 if(val == MusECore::ME_SYSEX || val == MusECore::ME_SYSEX_END)
97 continue;
98 *dst++ = val;
99 if (dst - buffer >= 2048) {
100 printf("string2sysex: Hex String too long (2048 bytes limit)\n");
101 return -1;
102 }
103 }
104 }
105
106 int len = dst - buffer;
107 if(len > 0)
108 {
109 unsigned char* b = new unsigned char[len];
110 memcpy(b, buffer, len);
111 *data = b;
112 }
113 else
114 *data = 0;
115
116 return len;
117 }
118
119 //---------------------------------------------------------
120 // sysex2string
121 //---------------------------------------------------------
122
sysex2string(int len,unsigned char * data)123 QString sysex2string(int len, unsigned char* data)
124 {
125 QString d;
126 for (int i = 0; i < len; ++i) {
127 if ((i > 0) && ((i % 8)==0)) {
128 d += QString("\n");
129 }
130 else if (i)
131 d += QString(" ");
132 // Strip all f0 and f7 (whether accidental or on purpose enclosing etc).
133 if(data[i] == MusECore::ME_SYSEX || data[i] == MusECore::ME_SYSEX_END)
134 continue;
135 d += QString("%1").arg(data[i], 2, 16, QLatin1Char('0'));
136 }
137 return d;
138 }
139
140 //---------------------------------------------------------
141 // readEventList
142 //---------------------------------------------------------
143
readEventList(Xml & xml,EventList * el,const char * name)144 static void readEventList(Xml& xml, EventList* el, const char* name)
145 {
146 for (;;) {
147 Xml::Token token = xml.parse();
148 const QString& tag = xml.s1();
149 switch (token) {
150 case Xml::Error:
151 case Xml::End:
152 return;
153 case Xml::TagStart:
154 if (tag == "event") {
155 Event e(Note);
156 e.read(xml);
157 el->add(e);
158 }
159 else
160 xml.unknown("readEventList");
161 break;
162 case Xml::TagEnd:
163 if (tag == name)
164 return;
165 default:
166 break;
167 }
168 }
169 }
170
171 //---------------------------------------------------------
172 // read
173 //---------------------------------------------------------
174
175 //---------------------------------------------------------
176 // loadIDF
177 //---------------------------------------------------------
178
loadIDF(QFileInfo * fi)179 static void loadIDF(QFileInfo* fi)
180 {
181 FILE* f = fopen(fi->filePath().toLatin1().constData(), "r");
182 if (f == 0)
183 return;
184 if (MusEGlobal::debugMsg)
185 printf("READ IDF %s\n", fi->filePath().toLatin1().constData());
186 Xml xml(f);
187
188 bool skipmode = true;
189 for (;;) {
190 Xml::Token token = xml.parse();
191 const QString& tag = xml.s1();
192 switch (token) {
193 case Xml::Error:
194 case Xml::End:
195 goto loadIDF_end;
196 case Xml::TagStart:
197 if (skipmode && tag == "muse")
198 skipmode = false;
199 else if (skipmode)
200 break;
201 else if (tag == "MidiInstrument") {
202 MidiInstrument* i = new MidiInstrument();
203 i->setFilePath(fi->filePath());
204 i->read(xml);
205 // Ignore duplicate named instruments.
206 iMidiInstrument ii = midiInstruments.begin();
207 for(; ii != midiInstruments.end(); ++ii)
208 {
209 if((*ii)->iname() == i->iname())
210 break;
211 }
212 if(ii == midiInstruments.end())
213 {
214
215 #ifdef _USE_INSTRUMENT_OVERRIDES_
216 // Add in the drum map overrides that were found in config.
217 // They can only be added now that the instrument has been loaded.
218 ciWorkingDrumMapInstrumentList_t iwdmil =
219 MusEGlobal::workingDrumMapInstrumentList.find(i->iname().toStdString());
220 if(iwdmil != MusEGlobal::workingDrumMapInstrumentList.end())
221 {
222 const WorkingDrumMapPatchList& wdmil = iwdmil->second;
223 patch_drummap_mapping_list_t* pdml = i->get_patch_drummap_mapping();
224 int patch;
225 for(ciWorkingDrumMapPatchList_t iwdmpl = wdmil.begin(); iwdmpl != wdmil.end(); ++iwdmpl)
226 {
227 patch = iwdmpl->first;
228 iPatchDrummapMapping_t ipdm = pdml->find(patch, false); // No default.
229 if(ipdm != pdml->end())
230 {
231 patch_drummap_mapping_t& pdm = *ipdm;
232 const WorkingDrumMapList& wdml = iwdmpl->second;
233 pdm._workingDrumMapList = wdml;
234 }
235 }
236 // TODO: Done with the config override, so erase it? Hm, maybe we might need it later...
237 //MusEGlobal::workingDrumMapInstrumentList.erase(iwdmil);
238 }
239 #endif
240 midiInstruments.push_back(i);
241 }
242 else
243 delete i;
244 }
245 else
246 xml.unknown("muse");
247 break;
248 case Xml::Attribut:
249 break;
250 case Xml::TagEnd:
251 if (!skipmode && tag == "muse") {
252 goto loadIDF_end;
253 }
254 default:
255 break;
256 }
257 }
258
259 loadIDF_end:
260 fclose(f);
261 }
262
263 //---------------------------------------------------------
264 // initMidiInstruments
265 //---------------------------------------------------------
266
initMidiInstruments()267 void initMidiInstruments()
268 {
269 genericMidiInstrument = new MidiInstrument(QWidget::tr("Generic midi"));
270 midiInstruments.push_back(genericMidiInstrument);
271
272 // Initialize with a default drum map on default channel. Patch is default 0xffffff. GM-1 does not specify a drum patch number.
273 ChannelDrumMappingList* cdml = genericMidiInstrument->getChannelDrumMapping();
274 cdml->add(-1, patch_drummap_mapping_list_t());
275
276 #ifdef _USE_INSTRUMENT_OVERRIDES_
277 // Add in the drum map overrides that were found in config.
278 // They can only be added now that the instrument has been created.
279 ciWorkingDrumMapInstrumentList_t iwdmil =
280 MusEGlobal::workingDrumMapInstrumentList.find(genericMidiInstrument->iname().toStdString());
281 if(iwdmil != MusEGlobal::workingDrumMapInstrumentList.end())
282 {
283 const WorkingDrumMapPatchList& wdmil = iwdmil->second;
284 int patch;
285 for(ciWorkingDrumMapPatchList_t iwdmpl = wdmil.begin(); iwdmpl != wdmil.end(); ++iwdmpl)
286 {
287 patch = iwdmpl->first;
288 iPatchDrummapMapping_t ipdm = pdml->find(patch, false); // No default.
289 if(ipdm != pdml->end())
290 {
291 patch_drummap_mapping_t& pdm = *ipdm;
292 const WorkingDrumMapList& wdml = iwdmpl->second;
293 pdm._workingDrumMapList = wdml;
294 }
295 }
296 // TODO: Done with the config override, so erase it? Hm, maybe we might need it later...
297 //MusEGlobal::workingDrumMapInstrumentList.erase(iwdmil);
298 }
299 #endif
300
301 if (MusEGlobal::debugMsg)
302 printf("load user instrument definitions from <%s>\n", MusEGlobal::museUserInstruments.toLatin1().constData());
303 QDir usrInstrumentsDir(MusEGlobal::museUserInstruments, QString("*.idf"));
304 if (usrInstrumentsDir.exists()) {
305 QFileInfoList list = usrInstrumentsDir.entryInfoList();
306 QFileInfoList::iterator it=list.begin(); // ddskrjo
307 while(it != list.end()) { // ddskrjo
308 loadIDF(&*it);
309 ++it;
310 }
311 }
312
313 if (MusEGlobal::debugMsg)
314 printf("load instrument definitions from <%s>\n", MusEGlobal::museInstruments.toLatin1().constData());
315 QDir instrumentsDir(MusEGlobal::museInstruments, QString("*.idf"));
316 if (instrumentsDir.exists()) {
317 QFileInfoList list = instrumentsDir.entryInfoList();
318 QFileInfoList::iterator it=list.begin(); // ddskrjo
319 while(it!=list.end()) {
320 loadIDF(&*it);
321 ++it;
322 }
323 }
324 else
325 printf("Instrument directory not found: %s\n", MusEGlobal::museInstruments.toLatin1().constData());
326
327 }
328
329 //---------------------------------------------------------
330 // registerMidiInstrument
331 //---------------------------------------------------------
332
registerMidiInstrument(const QString & name)333 MidiInstrument* registerMidiInstrument(const QString& name)
334 {
335 for (iMidiInstrument i = midiInstruments.begin();
336 i != midiInstruments.end(); ++i) {
337 if ((*i)->iname() == name)
338 return *i;
339 }
340 return genericMidiInstrument;
341 }
342
343 //---------------------------------------------------------
344 // removeMidiInstrument
345 //---------------------------------------------------------
346
removeMidiInstrument(const QString & name)347 void removeMidiInstrument(const QString& name)
348 {
349 for (iMidiInstrument i = midiInstruments.begin();
350 i != midiInstruments.end(); ++i) {
351 if ((*i)->iname() == name) {
352 midiInstruments.erase(i);
353 return;
354 }
355 }
356 }
357
removeMidiInstrument(const MidiInstrument * instr)358 void removeMidiInstrument(const MidiInstrument* instr)
359 {
360 for (iMidiInstrument i = midiInstruments.begin();
361 i != midiInstruments.end(); ++i) {
362 if (*i == instr) {
363 midiInstruments.erase(i);
364 return;
365 }
366 }
367 }
368
369 //---------------------------------------------------------
370 // findMidiInstrument
371 //---------------------------------------------------------
372
find(const MidiInstrument * instr)373 iMidiInstrument MidiInstrumentList::find(const MidiInstrument* instr)
374 {
375 for (iMidiInstrument i = begin();
376 i != end(); ++i) {
377 if (*i == instr) {
378 return i;
379 }
380 }
381 return end();
382 }
383
384 #ifdef _USE_INSTRUMENT_OVERRIDES_
writeDrummapOverrides(int level,Xml & xml) const385 void MidiInstrumentList::writeDrummapOverrides(int level, Xml& xml) const
386 {
387 MidiInstrument* mi;
388 for(ciMidiInstrument imi = begin(); imi != end(); ++imi)
389 {
390 mi = *imi;
391 mi->writeDrummapOverrides(level, xml);
392 }
393 }
394 #endif
395
396 //---------------------------------------------------------
397 // MidiInstrument
398 //---------------------------------------------------------
399
init()400 void MidiInstrument::init()
401 {
402 _noteOffMode = NoteOffAll; // By default, use note offs.
403 _tmpMidiStateVersion = 1; // Assume old version. readMidiState will overwrite anyway.
404 _initScript = 0;
405 _waitForLSB = true;
406 _midiInit = new EventList();
407 _midiReset = new EventList();
408 _midiState = new EventList();
409 _controller = new MidiControllerList;
410
411 // add some default controller to controller list
412 // this controllers are always available for all instruments
413 //
414 MidiController* prog = new MidiController("Program", CTRL_PROGRAM, 0, 0xffffff, 0, 0);
415 _controller->add(prog);
416 _dirty = false;
417
418 }
419
MidiInstrument()420 MidiInstrument::MidiInstrument()
421 {
422 init();
423 }
424
425 //---------------------------------------------------------
426 // MidiInstrument
427 //---------------------------------------------------------
428
MidiInstrument(const QString & txt)429 MidiInstrument::MidiInstrument(const QString& txt)
430 {
431 _name = txt;
432 init();
433 }
434
435 //---------------------------------------------------------
436 // MidiInstrument
437 //---------------------------------------------------------
438
~MidiInstrument()439 MidiInstrument::~MidiInstrument()
440 {
441 for (ciPatchGroup g = pg.begin(); g != pg.end(); ++g)
442 {
443 PatchGroup* pgp = *g;
444 const PatchList& pl = pgp->patches;
445 for (ciPatch p = pl.begin(); p != pl.end(); ++p)
446 {
447 delete *p;
448 }
449 delete pgp;
450 }
451
452
453 delete _midiInit;
454 delete _midiReset;
455 delete _midiState;
456 for(iMidiController i = _controller->begin(); i != _controller->end(); ++i)
457 delete i->second;
458 delete _controller;
459
460 if (_initScript)
461 delete _initScript;
462
463 if(!_sysex.isEmpty())
464 {
465 int j = _sysex.size();
466 for(int i = 0; i < j; ++i)
467 delete _sysex.at(i);
468 }
469
470 _channelDrumMapping.clear();
471 }
472
473 //---------------------------------------------------------
474 // assign
475 //---------------------------------------------------------
476
assign(const MidiInstrument & ins)477 MidiInstrument& MidiInstrument::assign(const MidiInstrument& ins)
478 {
479 //---------------------------------------------------------
480 // TODO: Copy the _initScript (if and when it is ever used)
481 //---------------------------------------------------------
482
483 for(iMidiController i = _controller->begin(); i != _controller->end(); ++i)
484 delete i->second;
485
486 _controller->clr();
487 _waitForLSB = ins._waitForLSB;
488 _noteOffMode = ins._noteOffMode;
489
490 // Assignment
491 for(ciMidiController i = ins._controller->begin(); i != ins._controller->end(); ++i)
492 {
493 MidiController* mc = i->second;
494 _controller->add(new MidiController(*mc));
495 }
496
497 if(!_sysex.isEmpty())
498 {
499 int j = _sysex.size();
500 for(int i = 0; i < j; ++i)
501 delete _sysex.at(i);
502 _sysex.clear();
503 }
504 if(!ins.sysex().isEmpty())
505 {
506 int j = ins.sysex().size();
507 for(int i = 0; i < j; ++i)
508 _sysex.append(new MusECore::SysEx(*(ins.sysex().at(i))));
509 }
510
511 *(_midiInit) = *(ins._midiInit);
512 *(_midiReset) = *(ins._midiReset);
513 *(_midiState) = *(ins._midiState);
514
515 for (ciPatchGroup g = pg.begin(); g != pg.end(); ++g)
516 {
517 PatchGroup* pgp = *g;
518 const PatchList& pl = pgp->patches;
519 for (ciPatch p = pl.begin(); p != pl.end(); ++p)
520 {
521 delete *p;
522 }
523
524 delete pgp;
525 }
526 pg.clear();
527
528 // Assignment
529 for(ciPatchGroup g = ins.pg.begin(); g != ins.pg.end(); ++g)
530 {
531 PatchGroup* pgp = *g;
532 const PatchList& pl = pgp->patches;
533 PatchGroup* npg = new PatchGroup;
534 npg->name = pgp->name;
535 pg.push_back(npg);
536 for (ciPatch p = pl.begin(); p != pl.end(); ++p)
537 {
538 Patch* pp = *p;
539 Patch* np = new Patch;
540 //np->typ = pp->typ;
541 np->hbank = pp->hbank;
542 np->lbank = pp->lbank;
543 np->program = pp->program;
544 np->name = pp->name;
545 np->drum = pp->drum;
546 npg->patches.push_back(np);
547 }
548 }
549
550 _name = ins._name;
551 _filePath = ins._filePath;
552
553 _channelDrumMapping = ins._channelDrumMapping;
554
555 // Hmm, dirty, yes? But init sets it to false... DELETETHIS
556 //_dirty = ins._dirty;
557 //_dirty = false;
558 //_dirty = true;
559
560 return *this;
561 }
562
563 //---------------------------------------------------------
564 // midiType
565 //---------------------------------------------------------
566
midiType() const567 MType MidiInstrument::midiType() const
568 {
569 if(_name == "GM")
570 return MT_GM;
571 if(_name == "GM2")
572 return MT_GM2;
573 if(_name == "GS")
574 return MT_GS;
575 if(_name == "XG")
576 return MT_XG;
577 return MT_UNKNOWN;
578 }
579
580 //---------------------------------------------------------
581 // reset
582 // send note off to all channels
583 // To be called by audio thread only.
584 //---------------------------------------------------------
585
reset(int portNo)586 void MidiInstrument::reset(int portNo)
587 {
588 MusECore::MidiPort* port = &MusEGlobal::midiPorts[portNo];
589 if(port->device() == 0)
590 return;
591
592 MusECore::MidiPlayEvent ev;
593 ev.setType(ME_NOTEOFF);
594 ev.setPort(portNo);
595 ev.setTime(0); // Immediate processing. TODO: Use curFrame?
596 ev.setB(64);
597
598 for (int chan = 0; chan < MusECore::MUSE_MIDI_CHANNELS; ++chan)
599 {
600 ev.setChannel(chan);
601 for (int pitch = 0; pitch < 128; ++pitch)
602 {
603 ev.setA(pitch);
604 port->device()->putEvent(ev, MidiDevice::NotLate);
605 }
606 }
607 }
608
609 //---------------------------------------------------------
610 // readPatchGroup
611 //---------------------------------------------------------
612
read(Xml & xml)613 void PatchGroup::read(Xml& xml)
614 {
615 for (;;) {
616 Xml::Token token = xml.parse();
617 const QString& tag = xml.s1();
618 switch (token) {
619 case Xml::Error:
620 case Xml::End:
621 return;
622 case Xml::TagStart:
623 if (tag == "Patch") {
624 Patch* patch = new Patch;
625 patch->read(xml);
626 patches.push_back(patch);
627 }
628 else
629 xml.unknown("PatchGroup");
630 break;
631 case Xml::Attribut:
632 if (tag == "name")
633 name = xml.s2();
634 break;
635 case Xml::TagEnd:
636 if (tag == "PatchGroup")
637 return;
638 default:
639 break;
640 }
641 }
642 }
643
644 //---------------------------------------------------------
645 // read
646 //---------------------------------------------------------
647
read(Xml & xml)648 void Patch::read(Xml& xml)
649 {
650 hbank = -1;
651 lbank = -1;
652 program = -1;
653 drum = false;
654 for (;;) {
655 Xml::Token token = xml.parse();
656 const QString& tag = xml.s1();
657 switch (token) {
658 case Xml::Error:
659 case Xml::End:
660 return;
661 case Xml::TagStart:
662 xml.unknown("Patch");
663 break;
664 case Xml::Attribut:
665 if (tag == "name")
666 name = xml.s2();
667 else if (tag == "mode") // Obsolete
668 {
669 xml.s2().toInt();
670 }
671 else if (tag == "hbank")
672 hbank = xml.s2().toInt();
673 else if (tag == "lbank")
674 lbank = xml.s2().toInt();
675 else if (tag == "prog")
676 program = xml.s2().toInt();
677 else if (tag == "drum")
678 drum = xml.s2().toInt();
679 break;
680 case Xml::TagEnd:
681 if (tag == "Patch")
682 return;
683 default:
684 break;
685 }
686 }
687 }
688
689 //---------------------------------------------------------
690 // write
691 //---------------------------------------------------------
692
write(int level,Xml & xml)693 void Patch::write(int level, Xml& xml)
694 {
695 xml.nput(level, "<Patch name=\"%s\"", Xml::xmlString(name).toLatin1().constData());
696
697 if(hbank != -1)
698 xml.nput(" hbank=\"%d\"", hbank);
699
700 if(lbank != -1)
701 xml.nput(" lbank=\"%d\"", lbank);
702
703 if(program != -1)
704 xml.nput(" prog=\"%d\"", program);
705
706 if(drum)
707 xml.nput(" drum=\"%d\"", int(drum));
708 xml.put(" />");
709 }
710
find(int patch,bool drum,bool includeDefault)711 iPatch PatchList::find(int patch, bool drum, bool includeDefault)
712 {
713 int pnum;
714 Patch* p;
715 iPatch ip_default = end();
716 for(iPatch ip = begin(); ip != end(); ++ip)
717 {
718 p = *ip;
719 pnum = p->patch();
720 // Look for an exact match above all else. The given patch must be valid.
721 if(patch != CTRL_VAL_UNKNOWN && pnum == patch && p->drum == drum)
722 return ip;
723 // If no exact match is found we'll take a default if found (all three pr, hb, lb = don't care).
724 if(includeDefault && p->dontCare() && p->drum == drum && ip_default == end())
725 ip_default = ip;
726 }
727 return ip_default;
728 }
729
find(int patch,bool drum,bool includeDefault) const730 ciPatch PatchList::find(int patch, bool drum, bool includeDefault) const
731 {
732 int pnum;
733 const Patch* p;
734 ciPatch ip_default = end();
735 for(ciPatch ip = begin(); ip != end(); ++ip)
736 {
737 p = *ip;
738 pnum = p->patch();
739 // Look for an exact match above all else. The given patch must be valid.
740 if(patch != CTRL_VAL_UNKNOWN && pnum == patch && p->drum == drum)
741 return ip;
742 // If no exact match is found we'll take a default if found (all three pr, hb, lb = don't care).
743 if(includeDefault && p->dontCare() && p->drum == drum && ip_default == end())
744 ip_default = ip;
745 }
746 return ip_default;
747 }
748
findPatch(int patch,bool drum,bool includeDefault)749 Patch* PatchGroupList::findPatch(int patch, bool drum, bool includeDefault)
750 {
751 for(iPatchGroup ipg = begin(); ipg != end(); ++ipg)
752 {
753 PatchGroup* pg = *ipg;
754 iPatch ip = pg->patches.find(patch, drum, includeDefault);
755 if(ip != pg->patches.end())
756 return *ip;
757 }
758 return 0;
759 }
760
findPatch(int patch,bool drum,bool includeDefault) const761 Patch* PatchGroupList::findPatch(int patch, bool drum, bool includeDefault) const
762 {
763 for(ciPatchGroup ipg = begin(); ipg != end(); ++ipg)
764 {
765 const PatchGroup* pg = *ipg;
766 ciPatch ip = pg->patches.find(patch, drum, includeDefault);
767 if(ip != pg->patches.end())
768 return *ip;
769 }
770 return 0;
771 }
772
773
774 //---------------------------------------------------------
775 // SysEx
776 //---------------------------------------------------------
777
SysEx()778 SysEx::SysEx()
779 {
780 dataLen = 0;
781 data = 0;
782 }
783
SysEx(const SysEx & src)784 SysEx::SysEx(const SysEx& src)
785 {
786 name = src.name;
787 comment = src.comment;
788 dataLen = src.dataLen;
789 data = 0;
790 if(dataLen != 0 && src.data)
791 {
792 data = new unsigned char[dataLen];
793 memcpy(data, src.data, dataLen);
794 }
795 }
796
~SysEx()797 SysEx::~SysEx()
798 {
799 if(dataLen != 0 && data)
800 delete[] data;
801 }
802
read(Xml & xml)803 bool SysEx::read(Xml& xml)
804 {
805 for (;;) {
806 Xml::Token token = xml.parse();
807 const QString& tag = xml.s1();
808 switch (token) {
809 case Xml::Error:
810 case Xml::End:
811 return false;
812 case Xml::TagStart:
813 if (tag == "comment")
814 comment = xml.parse1();
815 else if (tag == "data")
816 {
817 unsigned char*d;
818 int len = string2sysex(xml.parse1(), &d);
819 // Was the conversion successful, even if empty?
820 if(len != -1)
821 {
822 // Delete existing.
823 if(dataLen != 0 && data)
824 delete[] data;
825 dataLen = len;
826 data = d;
827 }
828 }
829 else
830 xml.unknown("SysEx");
831 break;
832 case Xml::Attribut:
833 if (tag == "name")
834 name = xml.s2();
835 break;
836 case Xml::TagEnd:
837 if (tag == "SysEx")
838 {
839 return !name.isEmpty();
840 }
841 default:
842 break;
843 }
844 }
845
846 return false;
847 }
848
write(int level,Xml & xml)849 void SysEx::write(int level, Xml& xml)
850 {
851 xml.nput(level, "<SysEx name=\"%s\">\n", Xml::xmlString(name).toLatin1().constData());
852
853 level++;
854 if(!comment.isEmpty())
855 xml.strTag(level, "comment", comment.toLatin1().constData());
856 if(dataLen > 0 && data)
857 xml.strTag(level, "data", sysex2string(dataLen, data));
858
859 xml.etag(level, "SysEx");
860 }
861
862 //---------------------------------------------------------
863 // readMidiState
864 //---------------------------------------------------------
865
readMidiState(Xml & xml)866 void MidiInstrument::readMidiState(Xml& xml)
867 {
868 // A kludge to support old midistates by wrapping them in the proper header.
869 _tmpMidiStateVersion = 1; // Assume old (unmarked) first version 1.
870 for (;;)
871 {
872 Xml::Token token = xml.parse();
873 const QString tag = xml.s1();
874 switch (token)
875 {
876 case Xml::Error:
877 case Xml::End:
878 return;
879 case Xml::TagStart:
880 if (tag == "event")
881 {
882 Event e(Note);
883 e.read(xml);
884 _midiState->add(e);
885 }
886 else
887 xml.unknown("midistate");
888 break;
889 case Xml::Attribut:
890 if(tag == "version")
891 _tmpMidiStateVersion = xml.s2().toInt();
892 else
893 xml.unknown("MidiInstrument");
894 break;
895 case Xml::TagEnd:
896 if(tag == "midistate")
897 return;
898 default:
899 break;
900 }
901 }
902 }
903
readDrummaps(Xml & xml)904 void MidiInstrument::readDrummaps(Xml& xml)
905 {
906 //_channelDrumMapping.clear(); // ???
907 const QString start_tag = xml.s1();
908 for (;;)
909 {
910 Xml::Token token = xml.parse();
911 const QString& tag = xml.s1();
912 switch (token)
913 {
914 case Xml::Error:
915 case Xml::End:
916 return;
917
918 case Xml::TagStart:
919 if (tag == "drumMapChannel")
920 _channelDrumMapping.read(xml);
921 else if (tag == "entry")
922 {
923 patch_drummap_mapping_list_t pdml;
924 pdml.read(xml);
925 if(!pdml.empty())
926 _channelDrumMapping.add(-1, pdml); // Add to the default channel.
927 }
928 else
929 xml.unknown("MidiInstrument::readDrummaps");
930 break;
931
932 case Xml::TagEnd:
933 if (tag == start_tag)
934 return;
935
936 default:
937 break;
938 }
939 }
940 }
941
writeDrummaps(int level,Xml & xml) const942 void MidiInstrument::writeDrummaps(int level, Xml& xml) const
943 {
944 xml.tag(level++, "Drummaps");
945
946 _channelDrumMapping.write(level, xml);
947
948 xml.etag(--level, "Drummaps");
949 }
950
951 //---------------------------------------------------------
952 // read
953 //---------------------------------------------------------
954
read(Xml & xml)955 void MidiInstrument::read(Xml& xml)
956 {
957 for (;;) {
958 Xml::Token token = xml.parse();
959 const QString& tag = xml.s1();
960 switch (token) {
961 case Xml::Error:
962 case Xml::End:
963 return;
964 case Xml::TagStart:
965 if (tag == "Patch") {
966 Patch* patch = new Patch;
967 patch->read(xml);
968 if (pg.empty()) {
969 PatchGroup* p = new PatchGroup;
970 p->patches.push_back(patch);
971 pg.push_back(p);
972 }
973 else
974 pg[0]->patches.push_back(patch);
975 }
976 else if (tag == "PatchGroup") {
977 PatchGroup* p = new PatchGroup;
978 p->read(xml);
979 pg.push_back(p);
980 }
981 else if (tag == "Controller") {
982 MidiController* mc = new MidiController();
983 mc->read(xml);
984 //
985 // HACK: make predefined "Program" controller overloadable
986 //
987 if (mc->name() == "Program") {
988 for (iMidiController i = _controller->begin(); i != _controller->end(); ++i) {
989 if (i->second->name() == mc->name()) {
990 delete i->second;
991 _controller->del(i);
992 break;
993 }
994 }
995 }
996
997 _controller->add(mc);
998 }
999 else if (tag == "Drummaps") {
1000 readDrummaps(xml);
1001 }
1002 else if (tag == "Init")
1003 readEventList(xml, _midiInit, "Init");
1004 else if (tag == "Reset")
1005 readEventList(xml, _midiReset, "Reset");
1006 else if (tag == "State")
1007 readEventList(xml, _midiState, "State");
1008 else if (tag == "InitScript") {
1009 if (_initScript)
1010 delete _initScript;
1011 QByteArray ba = xml.parse1().toLatin1();
1012 const char* istr = ba.constData();
1013 int len = ba.length() +1;
1014 if (len > 1) {
1015 _initScript = new char[len];
1016 memcpy(_initScript, istr, len);
1017 }
1018 }
1019 else if (tag == "SysEx") {
1020 SysEx* se = new SysEx;
1021 if(!se->read(xml))
1022 {
1023 delete se;
1024 printf("MidiInstrument::read():SysEx: reading sysex failed\n");
1025 }
1026 else
1027 _sysex.append(se);
1028 }
1029 else
1030 xml.unknown("MidiInstrument");
1031 break;
1032 case Xml::Attribut:
1033 if (tag == "name")
1034 setIName(xml.s2());
1035 else if(tag == "nullparam") { } // Obsolete.
1036 else if(tag == "NoteOffMode")
1037 _noteOffMode = (NoteOffMode)xml.s2().toInt(); // Default is NoteOffAll.
1038 break;
1039 case Xml::TagEnd:
1040 if (tag == "MidiInstrument")
1041 return;
1042 default:
1043 break;
1044 }
1045 }
1046 }
1047
1048 //---------------------------------------------------------
1049 // write
1050 //---------------------------------------------------------
1051
write(int level,Xml & xml)1052 void MidiInstrument::write(int level, Xml& xml)
1053 {
1054 xml.header();
1055 xml.tag(level, "muse version=\"1.0\"");
1056 level++;
1057 xml.nput(level, "<MidiInstrument name=\"%s\"", Xml::xmlString(iname()).toLatin1().constData());
1058
1059 if(noteOffMode() != NoteOffAll) // Default is NoteOffAll.
1060 xml.nput(" NoteOffMode=\"%d\"", noteOffMode());
1061 xml.put(">");
1062
1063 level++;
1064 for (ciPatchGroup g = pg.begin(); g != pg.end(); ++g) {
1065 PatchGroup* pgp = *g;
1066 const PatchList& pl = pgp->patches;
1067 xml.tag(level, "PatchGroup name=\"%s\"", Xml::xmlString(pgp->name).toLatin1().constData());
1068 level++;
1069 for (ciPatch p = pl.begin(); p != pl.end(); ++p)
1070 (*p)->write(level, xml);
1071 level--;
1072 xml.etag(level, "PatchGroup");
1073 }
1074 for (iMidiController ic = _controller->begin(); ic != _controller->end(); ++ic)
1075 ic->second->write(level, xml);
1076 if(!_sysex.isEmpty())
1077 {
1078 int j = _sysex.size();
1079 for(int i = 0; i < j; ++i)
1080 _sysex.at(i)->write(level, xml);
1081 }
1082
1083 xml.tag(level++, "Init");
1084 for(ciEvent ev=_midiInit->begin(); ev != _midiInit->end(); ++ev)
1085 ev->second.write(level, xml, MusECore::Pos(0, true));
1086 xml.etag(--level, "Init");
1087
1088 // -------------
1089 // TODO: What about _midiReset, _midiState, and _initScript ?
1090 // -------------
1091
1092 writeDrummaps(level, xml);
1093
1094 level--;
1095 xml.etag(level, "MidiInstrument");
1096 level--;
1097 xml.etag(level, "muse");
1098 }
1099
1100 #ifdef _USE_INSTRUMENT_OVERRIDES_
writeDrummapOverrides(int level,Xml & xml) const1101 void MidiInstrument::writeDrummapOverrides(int level, Xml& xml) const
1102 {
1103 for(ciPatchDrummapMapping_t ipdm = patch_drummap_mapping.begin(); ipdm != patch_drummap_mapping.end(); ++ipdm)
1104 {
1105 if(!(*ipdm)._workingDrumMapList.empty())
1106 {
1107 xml.tag(level++, "drummapOverrides instrument=\"%s\"", Xml::xmlString(iname()).toLatin1().constData());
1108 patch_drummap_mapping.writeDrummapOverrides(level, xml);
1109 xml.etag(--level, "drummapOverrides");
1110 break;
1111 }
1112 }
1113 }
1114 #endif
1115
1116 //---------------------------------------------------------
1117 // readMidnamDocument
1118 //---------------------------------------------------------
1119
1120 #ifdef MIDNAM_SUPPORT
readMidnamDocument(Xml & xml)1121 bool MidiInstrument::readMidnamDocument(Xml& xml)
1122 {
1123 return _midnamDocument.read(xml);
1124 }
1125 #endif
1126
get_patch_drummap_mapping(int channel,bool includeDefault)1127 patch_drummap_mapping_list_t* MidiInstrument::get_patch_drummap_mapping(int channel, bool includeDefault)
1128 {
1129 patch_drummap_mapping_list_t* pdml = _channelDrumMapping.find(channel, includeDefault);
1130 if(!pdml)
1131 // Not found? Search the global mapping list.
1132 return genericMidiInstrument->getChannelDrumMapping()->find(channel, includeDefault);
1133 return pdml;
1134 }
1135
1136 //---------------------------------------------------------
1137 // populateInstrPopup (static)
1138 //---------------------------------------------------------
1139
populateInstrPopup(MusEGui::PopupMenu * menu,int port,bool show_synths)1140 void MidiInstrument::populateInstrPopup(MusEGui::PopupMenu* menu, int port, bool show_synths)
1141 {
1142 menu->clear();
1143
1144 if(port < 0 || port >= MIDI_PORTS)
1145 return;
1146
1147 const MidiPort* mp = &MusEGlobal::midiPorts[port];
1148 const MidiDevice* md = mp->device();
1149
1150 const MidiInstrument* dev_curr_instr = mp->instrument();
1151 const SynthI* dev_synth = nullptr;
1152 const MidiInstrument* dev_synth_instr = nullptr;
1153 QAction* act;
1154
1155 act = menu->addAction(*MusEGui::editInstrumentSVGIcon, QWidget::tr("Edit Instrument..."));
1156 act->setData(100);
1157 menu->addSeparator();
1158
1159 menu->addAction(new MusEGui::MenuTitleItem(QObject::tr("Instruments"), menu));
1160 menu->addSeparator();
1161
1162 if(md && md->isSynti())
1163 {
1164 dev_synth = static_cast<const SynthI*>(md);
1165 dev_synth_instr = static_cast<const MidiInstrument*>(dev_synth);
1166 }
1167
1168 if(dev_synth_instr)
1169 {
1170 menu->addAction(new MusEGui::MenuTitleItem(QObject::tr("Current device"), menu));
1171 act = menu->addAction(dev_synth_instr->iname());
1172 act->setCheckable(true);
1173 if(dev_synth_instr == dev_curr_instr)
1174 act->setChecked(true);
1175 }
1176
1177 if(!MusECore::midiInstruments.empty())
1178 {
1179 bool has_synths = false;
1180 for (MusECore::ciMidiInstrument i = MusECore::midiInstruments.cbegin(); i
1181 != MusECore::midiInstruments.cend(); ++i)
1182 {
1183 if(show_synths && (*i)->isSynti() && (*i) != dev_synth_instr)
1184 {
1185 has_synths = true;
1186 break;
1187 }
1188 }
1189
1190 if(has_synths)
1191 {
1192 if(dev_synth_instr)
1193 menu->addAction(new MusEGui::MenuTitleItem(QObject::tr("Others"), menu));
1194
1195 MusEGui::PopupMenu* instr_menu = new MusEGui::PopupMenu(menu, false);
1196 instr_menu->setTitle(QObject::tr("Files"));
1197 for (MusECore::ciMidiInstrument i = MusECore::midiInstruments.cbegin(); i
1198 != MusECore::midiInstruments.cend(); ++i)
1199 {
1200 if(!(*i)->isSynti())
1201 {
1202 act = instr_menu->addAction((*i)->iname());
1203 act->setCheckable(true);
1204 if((*i) == dev_curr_instr)
1205 act->setChecked(true);
1206 }
1207 }
1208 menu->addMenu(instr_menu);
1209
1210 MusEGui::PopupMenu* synth_submenu = new MusEGui::PopupMenu(menu, false);
1211 synth_submenu->setTitle(QObject::tr("Synthesizers"));
1212 for (MusECore::ciMidiInstrument i = MusECore::midiInstruments.cbegin(); i
1213 != MusECore::midiInstruments.cend(); ++i)
1214 {
1215 if((*i)->isSynti() && (*i) != dev_synth_instr)
1216 {
1217 act = synth_submenu->addAction((*i)->iname());
1218 act->setCheckable(true);
1219 if((*i) == dev_curr_instr)
1220 act->setChecked(true);
1221 }
1222 }
1223 menu->addMenu(synth_submenu);
1224 }
1225 else
1226 {
1227 menu->addAction(new MusEGui::MenuTitleItem(QObject::tr("Files"), menu));
1228
1229 for (MusECore::iMidiInstrument i = MusECore::midiInstruments.begin(); i
1230 != MusECore::midiInstruments.end(); ++i)
1231 {
1232 if(!(*i)->isSynti())
1233 {
1234 act = menu->addAction((*i)->iname());
1235 act->setCheckable(true);
1236 if((*i) == dev_curr_instr)
1237 act->setChecked(true);
1238 }
1239 }
1240 }
1241 }
1242 }
1243
1244 //---------------------------------------------------------
1245 // populatePatchPopup
1246 //---------------------------------------------------------
1247
populatePatchPopup(MusEGui::PopupMenu * menu,int,bool drum)1248 void MidiInstrument::populatePatchPopup(MusEGui::PopupMenu* menu, int /*chan*/, bool drum)
1249 {
1250 menu->clear();
1251 //int mask = 7;
1252
1253 if (pg.size() > 1) {
1254 for (ciPatchGroup i = pg.begin(); i != pg.end(); ++i) {
1255 PatchGroup* pgp = *i;
1256 MusEGui::PopupMenu* pm = 0;
1257 const PatchList& pl = pgp->patches;
1258 for (ciPatch ipl = pl.begin(); ipl != pl.end(); ++ipl) {
1259 const Patch* mp = *ipl;
1260 if (//(mp->typ & mask) &&
1261 (mp->drum == drum)) {
1262 if(!pm) {
1263 pm = new MusEGui::PopupMenu(pgp->name, menu, menu->stayOpen()); // Use the parent stayOpen here.
1264 menu->addMenu(pm);
1265 pm->setFont(qApp->font());
1266 }
1267 const int hb = mp->hbank & 0xff;
1268 const int lb = mp->lbank & 0xff;
1269 const int pr = mp->program & 0xff;
1270 const int id = (hb << 16) | (lb << 8) | pr;
1271 const bool vhb = hb != 0xff;
1272 const bool vlb = lb != 0xff;
1273 const bool vpr = pr != 0xff;
1274 QString astr;
1275 if(vhb || vlb || vpr) {
1276 if(vhb)
1277 astr += QString::number(hb + 1) + QString(":");
1278 if(vlb)
1279 astr += QString::number(lb + 1) + QString(":");
1280 else if(vhb)
1281 astr += QString("--:");
1282 if(vpr)
1283 astr += QString::number(pr + 1);
1284 else if(vhb && vlb)
1285 astr += QString("--");
1286 astr += QString(" ");
1287 }
1288 astr += mp->name;
1289 QAction* act = pm->addAction(astr);
1290 act->setData(id);
1291 }
1292 }
1293 }
1294 }
1295 else if (pg.size() == 1 ){
1296 // no groups
1297 const PatchList& pl = pg.front()->patches;
1298 for (ciPatch ipl = pl.begin(); ipl != pl.end(); ++ipl) {
1299 const Patch* mp = *ipl;
1300 //if (mp->typ & mask) {
1301 const int hb = mp->hbank & 0xff;
1302 const int lb = mp->lbank & 0xff;
1303 const int pr = mp->program & 0xff;
1304 const int id = (hb << 16) | (lb << 8) | pr;
1305 const bool vhb = hb != 0xff;
1306 const bool vlb = lb != 0xff;
1307 const bool vpr = pr != 0xff;
1308 QString astr;
1309 if(vhb || vlb || vpr) {
1310 if(vhb)
1311 astr += QString::number(hb + 1) + QString(":");
1312 if(vlb)
1313 astr += QString::number(lb + 1) + QString(":");
1314 else if(vhb)
1315 astr += QString("--:");
1316 if(vpr)
1317 astr += QString::number(pr + 1);
1318 else if(vhb && vlb)
1319 astr += QString("--");
1320 astr += QString(" ");
1321 }
1322 astr += mp->name;
1323 QAction* act = menu->addAction(astr);
1324 act->setData(id);
1325 //}
1326 }
1327 }
1328
1329 }
1330
getMapItem(int channel,int patch,int index,DrumMap & dest_map,int overrideType) const1331 void MidiInstrument::getMapItem(int channel, int patch, int index, DrumMap& dest_map, int
1332 #ifdef _USE_INSTRUMENT_OVERRIDES_
1333 overrideType
1334 #endif
1335 ) const
1336 {
1337 const patch_drummap_mapping_list_t* pdml = _channelDrumMapping.find(channel, true); // Include default.
1338 if(!pdml)
1339 {
1340 fprintf(stderr, "MidiInstrument::getMapItem Error: No channel:%d mapping or default found. Using iNewDrumMap.\n", channel);
1341 dest_map = iNewDrumMap[index];
1342 return;
1343 }
1344
1345 // Always search this instrument's mapping first.
1346 ciPatchDrummapMapping_t ipdm = pdml->find(patch, false); // Don't include defaults here.
1347 if(ipdm == pdml->end())
1348 {
1349 // Not found? Is there a default patch mapping?
1350 #ifdef _USE_INSTRUMENT_OVERRIDES_
1351 if(overrideType & WorkingDrumMapEntry::InstrumentDefaultOverride)
1352 #endif
1353 ipdm = pdml->find(CTRL_PROGRAM_VAL_DONT_CARE, false); // Don't include defaults here.
1354
1355 if(ipdm == pdml->end())
1356 {
1357 // Not found? Search the global mapping list.
1358 patch_drummap_mapping_list_t* def_pdml = genericMidiInstrument->get_patch_drummap_mapping(channel, false);
1359 if(!def_pdml)
1360 {
1361 //fprintf(stderr, "MidiInstrument::getMapItem Error: No default patch mapping found in genericMidiInstrument. Using iNewDrumMap.\n");
1362 dest_map = iNewDrumMap[index];
1363 return;
1364 }
1365 ipdm = def_pdml->find(patch, false); // Don't include defaults here.
1366 if(ipdm == def_pdml->end())
1367 {
1368 // Not found? Is there a default patch mapping?
1369 #ifdef _USE_INSTRUMENT_OVERRIDES_
1370 if(overrideType & WorkingDrumMapEntry::InstrumentDefaultOverride)
1371 #endif
1372 ipdm = def_pdml->find(CTRL_PROGRAM_VAL_DONT_CARE, false); // Don't include defaults here.
1373
1374 if(ipdm == def_pdml->end())
1375 {
1376 // Not found? Use the global drum map.
1377 // Update: This shouldn't really happen now, since we have added a default patch drum map to the genericMidiInstrument.
1378 fprintf(stderr, "MidiInstrument::getMapItem Error: No default patch mapping found in genericMidiInstrument. Using iNewDrumMap.\n");
1379 dest_map = iNewDrumMap[index];
1380 return;
1381 }
1382 }
1383 }
1384 }
1385 const patch_drummap_mapping_t& pdm = (*ipdm);
1386
1387 dest_map = pdm.drummap[index];
1388
1389 #ifdef _USE_INSTRUMENT_OVERRIDES_
1390 // Did we request to include any instrument overrides?
1391 if(!(overrideType & WorkingDrumMapEntry::InstrumentOverride))
1392 return;
1393
1394 // Get any instrument overrides.
1395 ciWorkingDrumMapPatch_t iwdp = pdm._workingDrumMapList.find(index);
1396 if(iwdp == pdm._workingDrumMapList.end())
1397 return;
1398
1399 const WorkingDrumMapEntry& wdm = iwdp->second;
1400
1401 if(wdm._fields & WorkingDrumMapEntry::NameField)
1402 dest_map.name = wdm._mapItem.name;
1403
1404 if(wdm._fields & WorkingDrumMapEntry::VolField)
1405 dest_map.vol = wdm._mapItem.vol;
1406
1407 if(wdm._fields & WorkingDrumMapEntry::QuantField)
1408 dest_map.quant = wdm._mapItem.quant;
1409
1410 if(wdm._fields & WorkingDrumMapEntry::LenField)
1411 dest_map.len = wdm._mapItem.len;
1412
1413 if(wdm._fields & WorkingDrumMapEntry::ChanField)
1414 dest_map.channel = wdm._mapItem.channel;
1415
1416 if(wdm._fields & WorkingDrumMapEntry::PortField)
1417 dest_map.port = wdm._mapItem.port;
1418
1419 if(wdm._fields & WorkingDrumMapEntry::Lv1Field)
1420 dest_map.lv1 = wdm._mapItem.lv1;
1421
1422 if(wdm._fields & WorkingDrumMapEntry::Lv2Field)
1423 dest_map.lv2 = wdm._mapItem.lv2;
1424
1425 if(wdm._fields & WorkingDrumMapEntry::Lv3Field)
1426 dest_map.lv3 = wdm._mapItem.lv3;
1427
1428 if(wdm._fields & WorkingDrumMapEntry::Lv4Field)
1429 dest_map.lv4 = wdm._mapItem.lv4;
1430
1431 if(wdm._fields & WorkingDrumMapEntry::ENoteField)
1432 dest_map.enote = wdm._mapItem.enote;
1433
1434 if(wdm._fields & WorkingDrumMapEntry::ANoteField)
1435 dest_map.anote = wdm._mapItem.anote;
1436
1437 if(wdm._fields & WorkingDrumMapEntry::MuteField)
1438 dest_map.mute = wdm._mapItem.mute;
1439
1440 if(wdm._fields & WorkingDrumMapEntry::HideField)
1441 dest_map.hide = wdm._mapItem.hide;
1442 #endif
1443
1444 }
1445
findController(int num,int channel,int patch) const1446 MidiController* MidiInstrument::findController(int num, int channel, int patch) const
1447 {
1448 #ifdef MIDNAM_SUPPORT
1449 // Is there a midnam controller list for the given channel and patch?
1450 const MidiControllerList* mcl = _midnamDocument.getControllers(channel, patch);
1451 if(mcl)
1452 {
1453 // Is there a controller for the given num?
1454 MidiController* mc = mcl->findController(num);
1455 if(mc)
1456 // Return that controller.
1457 return mc;
1458 }
1459 #endif
1460 // No midnam controller was found. Does the instrument have the controller?
1461 return controller()->findController(num);
1462 }
1463
1464
getControllers(MidiControllerList * dest,int channel,int patch) const1465 void MidiInstrument::getControllers(MidiControllerList* dest, int channel, int patch) const
1466 {
1467 ciMidiController imc;
1468 #ifdef MIDNAM_SUPPORT
1469 // Is there a midnam controller list for the given channel and patch?
1470 const MidiControllerList* mcl = _midnamDocument.getControllers(channel, patch);
1471 if(mcl)
1472 {
1473 // Copy the midnam controller pointers directly to the destination.
1474 // Defer the RPN update until after done.
1475 for(imc = mcl->begin(); imc != mcl->end(); ++imc)
1476 dest->add(imc->second, false);
1477 }
1478 #endif
1479 mcl = controller();
1480 // Copy the instrument controller pointers directly to the destination.
1481 // Defer the RPN update until after done.
1482 for(imc = mcl->begin(); imc != mcl->end(); ++imc)
1483 dest->add(imc->second, false);
1484
1485 // Be sure to call this since we deferred it above.
1486 dest->update_RPN_Ctrls_Reserved();
1487 }
1488
1489 #ifdef _USE_INSTRUMENT_OVERRIDES_
isWorkingMapItem(int patch,int index,int fields) const1490 int MidiInstrument::isWorkingMapItem(int patch, int index, int fields) const
1491 {
1492 int ret = WorkingDrumMapEntry::NoOverride;
1493
1494 // Is there a default patch override for this drum map item?
1495 bool def_ipdm_valid = true;
1496 ciPatchDrummapMapping_t def_ipdm = patch_drummap_mapping.find(CTRL_PROGRAM_VAL_DONT_CARE, false); // Don't include defaults here.
1497 if(def_ipdm == patch_drummap_mapping.end())
1498 {
1499 // Not found? Search the global mapping list.
1500 def_ipdm = genericMidiInstrument->get_patch_drummap_mapping()->find(CTRL_PROGRAM_VAL_DONT_CARE, false);
1501 if(def_ipdm == genericMidiInstrument->get_patch_drummap_mapping()->end())
1502 def_ipdm_valid = false;
1503 }
1504 if(def_ipdm_valid)
1505 {
1506 const patch_drummap_mapping_t& pdm = (*def_ipdm);
1507 ciWorkingDrumMapPatch_t iwdp = pdm._workingDrumMapList.find(index);
1508 if(iwdp != pdm._workingDrumMapList.end())
1509 {
1510 const WorkingDrumMapEntry& wdm = iwdp->second;
1511 if(wdm._fields & fields)
1512 ret |= WorkingDrumMapEntry::InstrumentDefaultOverride;
1513 }
1514 }
1515
1516 // Is there a patch override for this drum map item?
1517 // Always search this instrument's mapping first.
1518 bool ipdm_valid = true;
1519 ciPatchDrummapMapping_t ipdm = patch_drummap_mapping.find(patch, false);
1520 if(ipdm == patch_drummap_mapping.end())
1521 {
1522 // Not found? Search the global mapping list.
1523 ipdm = MusECore::genericMidiInstrument->get_patch_drummap_mapping()->find(patch, false);
1524 if(ipdm == MusECore::genericMidiInstrument->get_patch_drummap_mapping()->end())
1525 ipdm_valid = false;
1526 }
1527 if(ipdm_valid)
1528 {
1529 const patch_drummap_mapping_t& pdm = (*ipdm);
1530 ciWorkingDrumMapPatch_t iwdp = pdm._workingDrumMapList.find(index);
1531 if(iwdp != pdm._workingDrumMapList.end())
1532 {
1533 const WorkingDrumMapEntry& wdm = iwdp->second;
1534 if(wdm._fields & fields)
1535 ret |= WorkingDrumMapEntry::InstrumentOverride;
1536 }
1537 }
1538
1539 return ret;
1540 }
1541
clearDrumMapOverrides()1542 void MidiInstrument::clearDrumMapOverrides()
1543 {
1544 for(iPatchDrummapMapping_t ipdm = patch_drummap_mapping.begin(); ipdm != patch_drummap_mapping.end(); ++ipdm)
1545 {
1546 patch_drummap_mapping_t& pdm = *ipdm;
1547 pdm._workingDrumMapList.clear();
1548 }
1549 }
1550
setWorkingDrumMapItem(int patch,int index,const WorkingDrumMapEntry & item,bool isReset)1551 bool MidiInstrument::setWorkingDrumMapItem(int patch, int index, const WorkingDrumMapEntry& item, bool isReset)
1552 {
1553 // Special value. Save it from searching.
1554 // if(patch == CTRL_VAL_UNKNOWN)
1555 // return false;
1556
1557 // iPatchDrummapMapping_t patch_ipm;
1558 // patch_ipm = patch_drummap_mapping.find(patch);
1559 // // You can't edit a drum map item in a collection that doesn't exist.
1560 // if(patch_ipm == patch_drummap_mapping.end())
1561 // return false;
1562
1563 // Always search this instrument's mapping first.
1564 iPatchDrummapMapping_t ipdm = patch_drummap_mapping.find(patch, false); // Don't include defaults here.
1565 if(ipdm == patch_drummap_mapping.end())
1566 {
1567 // Not found? Search the global mapping list.
1568 ipdm = MusECore::genericMidiInstrument->get_patch_drummap_mapping()->find(patch, false); // Don't include defaults here.
1569 // Not found? You can't edit a drum map item in a collection that doesn't exist.
1570 if(ipdm == MusECore::genericMidiInstrument->get_patch_drummap_mapping()->end())
1571 return false;
1572 }
1573
1574 patch_drummap_mapping_t& pdm = *ipdm;
1575
1576 const int fields = item._fields;
1577
1578 DrumMap cur_dm;
1579 getMapItem(patch, index, cur_dm, WorkingDrumMapEntry::InstrumentOverride | WorkingDrumMapEntry::InstrumentDefaultOverride);
1580 const int cur_enote = cur_dm.enote;
1581
1582 if(isReset)
1583 pdm.removeWorkingDrumMapEntry(index, fields);
1584 else
1585 pdm.addWorkingDrumMapEntry(index, item);
1586
1587 DrumMap new_dm;
1588 getMapItem(patch, index, new_dm, WorkingDrumMapEntry::InstrumentOverride | WorkingDrumMapEntry::InstrumentDefaultOverride);
1589
1590 if(fields & WorkingDrumMapEntry::ENoteField)
1591 {
1592 int new_enote = new_dm.enote;
1593 int other_index = pdm.drum_in_map[new_enote];
1594 {
1595 DrumMap other_dm;
1596 if(isReset)
1597 {
1598 // Here we need to see the map item value just /before/ any override, so that we can tell
1599 // whether this other_index brute-force 'reset' value is still technically an
1600 // override, and either remove or add (modify) the list appropriately.
1601 getMapItem(patch, other_index, other_dm, WorkingDrumMapEntry::InstrumentDefaultOverride);
1602 if(other_dm.enote == cur_enote)
1603 {
1604 // The values are equal. This is technically no longer an override and we may remove it.
1605 //_workingDrumMapPatchList->remove(patch, other_index, WorkingDrumMapEntry::ENoteField);
1606 pdm.removeWorkingDrumMapEntry(other_index, WorkingDrumMapEntry::ENoteField);
1607 }
1608 else
1609 {
1610 // The values are not equal. This is technically still an override, so add (modify) it.
1611 other_dm.enote = cur_enote;
1612 WorkingDrumMapEntry other_wdme(other_dm, WorkingDrumMapEntry::ENoteField);
1613 //_workingDrumMapPatchList->add(patch, other_index, other_wdme);
1614 pdm.addWorkingDrumMapEntry(other_index, other_wdme);
1615 }
1616 }
1617 else
1618 {
1619 other_dm.enote = cur_enote;
1620 WorkingDrumMapEntry other_wdme(other_dm, WorkingDrumMapEntry::ENoteField);
1621 //_workingDrumMapPatchList->add(patch, other_index, other_wdme);
1622 pdm.addWorkingDrumMapEntry(other_index, other_wdme);
1623 }
1624 pdm.drum_in_map[cur_enote] = other_index;
1625 pdm.drum_in_map[new_enote] = index;
1626 }
1627 }
1628
1629
1630
1631
1632 return true;
1633 }
1634 #endif
1635
1636
1637 //---------------------------------------------------------
1638 // getPatchName
1639 //---------------------------------------------------------
1640
getPatchName(int,int prog,bool drum,bool includeDefault) const1641 QString MidiInstrument::getPatchName(int /*channel*/, int prog, bool drum, bool includeDefault) const
1642 {
1643 if(MusECore::Patch* p = pg.findPatch(prog, drum, includeDefault))
1644 return p->name;
1645 return "<unknown>";
1646 }
1647
getNextPatch(int channel,unsigned patch,bool drum)1648 unsigned MidiInstrument::getNextPatch(int channel, unsigned patch, bool drum)
1649 {
1650 QList<dumb_patchlist_entry_t> haystack=getPatches(channel,drum);
1651 if (haystack.empty()) return MusECore::CTRL_VAL_UNKNOWN;
1652
1653 int prog=patch&0xFF;
1654 int lbank=(patch>>8)&0xFF;
1655 int hbank=(patch>>16)&0xFF;
1656
1657 dumb_patchlist_entry_t needle=dumb_patchlist_entry_t(prog, (lbank!=0xFF)?lbank:-1, (hbank!=0xFF)?hbank:-1);
1658
1659 QList<dumb_patchlist_entry_t>::iterator it;
1660 for (it=haystack.begin(); it!=haystack.end(); it++)
1661 if ((*it) == needle)
1662 break;
1663
1664 if (it==haystack.end()) //not found? use first entry
1665 it=haystack.begin();
1666 else
1667 {
1668 for (;it!=haystack.end(); it++)
1669 if ((*it)!=needle)
1670 break;
1671 if (it==haystack.end()) it=haystack.begin(); //wrap-over
1672 }
1673
1674 return (it->prog&0xFF) |
1675 ((((it->lbank==-1)?0xFF:it->lbank)<<8)&0xFF00) |
1676 ((((it->hbank==-1)?0xFF:it->hbank)<<16)&0xFF0000);
1677 }
1678
getPrevPatch(int channel,unsigned patch,bool drum)1679 unsigned MidiInstrument::getPrevPatch(int channel, unsigned patch, bool drum)
1680 {
1681 QList<dumb_patchlist_entry_t> haystack=getPatches(channel,drum);
1682 if (haystack.empty()) return MusECore::CTRL_VAL_UNKNOWN;
1683
1684 int prog=patch&0xFF;
1685 int lbank=(patch>>8)&0xFF;
1686 int hbank=(patch>>16)&0xFF;
1687
1688 dumb_patchlist_entry_t needle=dumb_patchlist_entry_t(prog, (lbank!=0xFF)?lbank:-1, (hbank!=0xFF)?hbank:-1);
1689
1690 QList<dumb_patchlist_entry_t>::iterator it;
1691 for (it=haystack.begin(); it!=haystack.end(); it++)
1692 if ((*it) == needle)
1693 break;
1694
1695 if (it==haystack.end()) //not found? use first entry
1696 it=haystack.begin();
1697 else
1698 {
1699 if (it==haystack.begin()) it=haystack.end(); //wrap-over
1700 it--;
1701 }
1702
1703 return (it->prog&0xFF) |
1704 ((((it->lbank==-1)?0xFF:it->lbank)<<8)&0xFF00) |
1705 ((((it->hbank==-1)?0xFF:it->hbank)<<16)&0xFF0000);
1706 }
1707
getPatches(int,bool drum)1708 QList<dumb_patchlist_entry_t> MidiInstrument::getPatches(int /*channel*/, bool drum)
1709 {
1710 //int tmask = 1;
1711 QList<dumb_patchlist_entry_t> tmp;
1712
1713 for (ciPatchGroup i = pg.begin(); i != pg.end(); ++i) {
1714 const PatchList& pl = (*i)->patches;
1715 for (ciPatch ipl = pl.begin(); ipl != pl.end(); ++ipl) {
1716 const Patch* mp = *ipl;
1717 if (//(mp->typ & tmask) &&
1718 (mp->drum == drum))
1719 {
1720 int prog = mp->program;
1721 int lbank = mp->lbank;
1722 int hbank = mp->hbank;
1723 tmp.push_back(dumb_patchlist_entry_t(prog,lbank,hbank));
1724 }
1725 }
1726 }
1727
1728 return tmp;
1729 }
1730
1731
1732 //---------------------------------------------------------
1733 // patch_drummap_mapping_t
1734 //---------------------------------------------------------
1735
patch_drummap_mapping_t()1736 patch_drummap_mapping_t::patch_drummap_mapping_t()
1737 {
1738 _patch = CTRL_PROGRAM_VAL_DONT_CARE;
1739 drummap=new DrumMap[128];
1740 for (int i=0;i<128;i++)
1741 drummap[i]=iNewDrumMap[i];
1742 update_drum_in_map();
1743 }
1744
patch_drummap_mapping_t(const patch_drummap_mapping_t & that)1745 patch_drummap_mapping_t::patch_drummap_mapping_t(const patch_drummap_mapping_t& that)
1746 {
1747 drummap=NULL;
1748 if(that.drummap)
1749 {
1750 drummap=new DrumMap[128];
1751 for (int i=0;i<128;i++)
1752 drummap[i]=that.drummap[i];
1753 }
1754
1755 _patch = that._patch;
1756 update_drum_in_map();
1757 }
1758
operator =(const patch_drummap_mapping_t & that)1759 patch_drummap_mapping_t& patch_drummap_mapping_t::operator=(const patch_drummap_mapping_t& that)
1760 {
1761 if (drummap)
1762 delete [] drummap;
1763 drummap=NULL;
1764
1765 if(that.drummap)
1766 {
1767 drummap=new DrumMap[128];
1768 for (int i=0;i<128;i++)
1769 drummap[i]=that.drummap[i];
1770 }
1771
1772 _patch = that._patch;
1773
1774 update_drum_in_map();
1775 return *this;
1776 }
1777
isValid() const1778 bool patch_drummap_mapping_t::isValid() const
1779 {
1780 return _patch != CTRL_VAL_UNKNOWN && drummap != NULL;
1781 }
1782
~patch_drummap_mapping_t()1783 patch_drummap_mapping_t::~patch_drummap_mapping_t()
1784 {
1785 if(drummap)
1786 delete [] drummap;
1787 }
1788
1789 #ifdef _USE_INSTRUMENT_OVERRIDES_
addWorkingDrumMapEntry(int index,const WorkingDrumMapEntry & item)1790 void patch_drummap_mapping_t::addWorkingDrumMapEntry(int index,const WorkingDrumMapEntry& item)
1791 {
1792 _workingDrumMapList.add(index, item);
1793 }
1794
removeWorkingDrumMapEntry(int index,const WorkingDrumMapEntry & item)1795 void patch_drummap_mapping_t::removeWorkingDrumMapEntry(int index, const WorkingDrumMapEntry& item)
1796 {
1797 _workingDrumMapList.remove(index, item);
1798 }
1799
removeWorkingDrumMapEntry(int index,int fields)1800 void patch_drummap_mapping_t::removeWorkingDrumMapEntry(int index, int fields)
1801 {
1802 _workingDrumMapList.remove(index, fields);
1803 }
1804 #endif
1805
update_drum_in_map()1806 void patch_drummap_mapping_t::update_drum_in_map()
1807 {
1808 if(drummap)
1809 {
1810 for(int i = 0; i < 128; ++i)
1811 drum_in_map[(int)drummap[i].enote] = i;
1812 }
1813 else
1814 {
1815 for(int i = 0; i < 128; ++i)
1816 drum_in_map[i] = i;
1817 }
1818
1819 #ifdef _USE_INSTRUMENT_OVERRIDES_
1820 int index;
1821 int enote;
1822 for(ciWorkingDrumMapPatch_t iwdmp = _workingDrumMapList.begin(); iwdmp != _workingDrumMapList.end(); ++iwdmp)
1823 {
1824 const WorkingDrumMapEntry& wde = iwdmp->second;
1825 if(wde._fields & WorkingDrumMapEntry::ENoteField)
1826 {
1827 index = iwdmp->first;
1828 const DrumMap& dm = wde._mapItem;
1829 enote = (int)dm.enote;
1830 drum_in_map[enote] = index;
1831 }
1832 }
1833 #endif
1834 }
1835
isPatchInRange(int patch,bool includeDefault) const1836 bool patch_drummap_mapping_t::isPatchInRange(int patch, bool includeDefault) const
1837 {
1838 // No exceptions: If all three prg, hb, and lb are don't care, then patch is always in range.
1839 if(dontCare())
1840 return includeDefault;
1841
1842 // Special value. Unknown cannot be part of a collection (unless don't care).
1843 if(!isValid() || patch == CTRL_VAL_UNKNOWN)
1844 return false;
1845
1846 const int hb = (patch >> 16) & 0xff;
1847 const int lb = (patch >> 8) & 0xff;
1848 const int pr = patch & 0xff;
1849
1850 const bool hboff = hb >= 128;
1851 const bool lboff = lb >= 128;
1852 const bool prgoff = pr >= 128; // Shouldn't happen.
1853
1854 return (programDontCare() || (!prgoff && pr == prog())) &&
1855 (hbankDontCare() || (!hboff && hb == hbank())) &&
1856 (lbankDontCare() || (!lboff && lb == lbank()));
1857 }
1858
to_string()1859 QString patch_drummap_mapping_t::to_string()
1860 {
1861 QString tmp;
1862
1863 if (dontCare())
1864 tmp="default";
1865 else
1866 {
1867 if(hbankDontCare())
1868 tmp += "---";
1869 else
1870 tmp += QString::number(hbank() + 1);
1871
1872 tmp+=" / ";
1873
1874 if(lbankDontCare())
1875 tmp += "---";
1876 else
1877 tmp += QString::number(lbank() + 1);
1878
1879 tmp+=" / ";
1880
1881 if(programDontCare())
1882 tmp += "---";
1883 else
1884 tmp += QString::number(prog() + 1);
1885 }
1886 return tmp;
1887 }
1888
1889 //---------------------------------------------------------
1890 // patch_drummap_mapping_t
1891 //---------------------------------------------------------
1892
add(const patch_drummap_mapping_list_t & other)1893 void patch_drummap_mapping_list_t::add(const patch_drummap_mapping_list_t& other)
1894 {
1895 for(ciPatchDrummapMapping_t ipdm = other.begin(); ipdm != other.end(); ++ipdm)
1896 {
1897 const patch_drummap_mapping_t& pdm = *ipdm;
1898 add(pdm);
1899 }
1900 }
1901
add(const patch_drummap_mapping_t & pdm)1902 void patch_drummap_mapping_list_t::add(const patch_drummap_mapping_t& pdm)
1903 {
1904 // No duplicates: If a mapping item by that patch already exists, replace it.
1905 iPatchDrummapMapping_t ipdm = find(pdm._patch, false); // No default.
1906 if(ipdm == end())
1907 push_back(pdm);
1908 else
1909 *ipdm = pdm;
1910 }
1911
find(int patch,bool includeDefault)1912 iPatchDrummapMapping_t patch_drummap_mapping_list_t::find(int patch, bool includeDefault)
1913 {
1914 iPatchDrummapMapping_t ipdm_default = end();
1915 for(iPatchDrummapMapping_t ipdm = begin(); ipdm != end(); ++ipdm)
1916 {
1917 // Look for an exact match above all else. The given patch must be valid.
1918 if(patch != CTRL_VAL_UNKNOWN && ipdm->_patch == patch)
1919 return ipdm;
1920 // If no exact match is found we'll take a default if found (all three pr, hb, lb = don't care).
1921 if(includeDefault && ipdm->dontCare() && ipdm_default == end())
1922 ipdm_default = ipdm;
1923 }
1924 return ipdm_default;
1925 }
1926
find(int patch,bool includeDefault) const1927 ciPatchDrummapMapping_t patch_drummap_mapping_list_t::find(int patch, bool includeDefault) const
1928 {
1929 ciPatchDrummapMapping_t ipdm_default = end();
1930 for(ciPatchDrummapMapping_t ipdm = begin(); ipdm != end(); ++ipdm)
1931 {
1932 // Look for an exact match above all else. The given patch must be valid.
1933 if(patch != CTRL_VAL_UNKNOWN && ipdm->_patch == patch)
1934 return ipdm;
1935 // If no exact match is found we'll take a default if found (all three pr, hb, lb = don't care).
1936 if(includeDefault && ipdm->dontCare() && ipdm_default == end())
1937 ipdm_default = ipdm;
1938 }
1939 return ipdm_default;
1940 }
1941
read(Xml & xml)1942 void patch_drummap_mapping_list_t::read(Xml& xml)
1943 {
1944 int patch = CTRL_PROGRAM_VAL_DONT_CARE;
1945 DrumMap* drummap=new DrumMap[128];
1946 for (int i=0;i<128;i++)
1947 drummap[i]=iNewDrumMap[i];
1948
1949 for (;;)
1950 {
1951 Xml::Token token = xml.parse();
1952 const QString& tag = xml.s1();
1953 switch (token)
1954 {
1955 case Xml::Error:
1956 case Xml::End:
1957 goto pdml_read_end;
1958
1959 case Xml::TagStart:
1960 if (tag == "patch_collection")
1961 patch = readDrummapsEntryPatchCollection(xml);
1962 else if (tag == "drummap")
1963 read_new_style_drummap(xml, "drummap", drummap);
1964 else
1965 xml.unknown("patch_drummap_mapping_list_t::read");
1966 break;
1967
1968 case Xml::TagEnd:
1969 if (tag == "entry")
1970 {
1971 push_back(patch_drummap_mapping_t(drummap, patch));
1972 return;
1973 }
1974
1975 default:
1976 break;
1977 }
1978 }
1979
1980 pdml_read_end:
1981 fprintf(stderr, "End or Error in patch_drummap_mapping_list_t::read()!\n");
1982 delete [] drummap;
1983 }
1984
write(int level,Xml & xml) const1985 void patch_drummap_mapping_list_t::write(int level, Xml& xml) const
1986 {
1987 for (ciPatchDrummapMapping_t it = begin();
1988 it != end(); it++)
1989 {
1990 xml.tag(level++, "entry");
1991
1992 const patch_drummap_mapping_t& pdm = *it;
1993
1994 if(!pdm.dontCare())
1995 {
1996 QString tmp="<patch_collection ";
1997
1998 if(!pdm.programDontCare())
1999 tmp += "prog=\"" + QString::number(pdm.prog()) + QString("\" ");
2000 if(!pdm.lbankDontCare())
2001 tmp += "lbank=\"" + QString::number(pdm.lbank()) + QString("\" ");
2002 if(!pdm.hbankDontCare())
2003 tmp += "hbank=\"" + QString::number(pdm.hbank()) + QString("\" ");
2004
2005 tmp+="/>\n";
2006
2007 xml.nput(level, tmp.toLatin1().data());
2008 }
2009
2010 write_new_style_drummap(level, xml, "drummap", it->drummap);
2011 //write_new_style_drummap(level, xml, "drummap", it->drummap, true); // true = Need to save all entries.
2012
2013 xml.etag(--level, "entry");
2014 }
2015 }
2016
2017 #ifdef _USE_INSTRUMENT_OVERRIDES_
writeDrummapOverrides(int level,Xml & xml) const2018 void patch_drummap_mapping_list_t::writeDrummapOverrides(int level, Xml& xml) const
2019 {
2020 for(ciPatchDrummapMapping_t ipdm = begin(); ipdm != end(); ++ipdm)
2021 {
2022 const patch_drummap_mapping_t& pdm = *ipdm;
2023 if(pdm._workingDrumMapList.empty())
2024 continue;
2025 xml.tag(level++, "drumMapPatch patch=\"%d\"", pdm._patch);
2026 pdm._workingDrumMapList.write(level, xml);
2027 xml.etag(--level, "drumMapPatch");
2028 }
2029 }
2030 #endif
2031
2032 //---------------------------------------------------------
2033 // WorkingDrumMapEntry
2034 //---------------------------------------------------------
2035
WorkingDrumMapEntry()2036 WorkingDrumMapEntry::WorkingDrumMapEntry()
2037 {
2038 _fields = NoField;
2039 }
2040
WorkingDrumMapEntry(const DrumMap & dm,fields_t fields)2041 WorkingDrumMapEntry::WorkingDrumMapEntry(const DrumMap& dm, fields_t fields)
2042 {
2043 _fields = fields;
2044 _mapItem = dm;
2045 }
2046
WorkingDrumMapEntry(const WorkingDrumMapEntry & other)2047 WorkingDrumMapEntry::WorkingDrumMapEntry(const WorkingDrumMapEntry& other)
2048 {
2049 _fields = other._fields;
2050 _mapItem = other._mapItem;
2051 }
2052
operator =(const WorkingDrumMapEntry & other)2053 WorkingDrumMapEntry& WorkingDrumMapEntry::operator=(const WorkingDrumMapEntry& other)
2054 {
2055 _fields = other._fields;
2056 _mapItem = other._mapItem;
2057 return *this;
2058 }
2059
2060 //---------------------------------------------------------
2061 // WorkingDrumMapList
2062 //---------------------------------------------------------
2063
add(int index,const WorkingDrumMapEntry & item)2064 void WorkingDrumMapList::add(int index, const WorkingDrumMapEntry& item)
2065 {
2066 WorkingDrumMapPatchInsertResult_t res = insert(WorkingDrumMapPatchInsertPair_t(index, item));
2067 if(res.second == false)
2068 {
2069 iWorkingDrumMapPatch_t& iwp = res.first;
2070 WorkingDrumMapEntry& wde = iwp->second;
2071
2072 if(item._fields & WorkingDrumMapEntry::NameField)
2073 wde._mapItem.name = item._mapItem.name;
2074
2075 if(item._fields & WorkingDrumMapEntry::VolField)
2076 wde._mapItem.vol = item._mapItem.vol;
2077
2078 if(item._fields & WorkingDrumMapEntry::QuantField)
2079 wde._mapItem.quant = item._mapItem.quant;
2080
2081 if(item._fields & WorkingDrumMapEntry::LenField)
2082 wde._mapItem.len = item._mapItem.len;
2083
2084 if(item._fields & WorkingDrumMapEntry::ChanField)
2085 wde._mapItem.channel = item._mapItem.channel;
2086
2087 if(item._fields & WorkingDrumMapEntry::PortField)
2088 wde._mapItem.port = item._mapItem.port;
2089
2090 if(item._fields & WorkingDrumMapEntry::Lv1Field)
2091 wde._mapItem.lv1 = item._mapItem.lv1;
2092
2093 if(item._fields & WorkingDrumMapEntry::Lv2Field)
2094 wde._mapItem.lv2 = item._mapItem.lv2;
2095
2096 if(item._fields & WorkingDrumMapEntry::Lv3Field)
2097 wde._mapItem.lv3 = item._mapItem.lv3;
2098
2099 if(item._fields & WorkingDrumMapEntry::Lv4Field)
2100 wde._mapItem.lv4 = item._mapItem.lv4;
2101
2102 if(item._fields & WorkingDrumMapEntry::ENoteField)
2103 wde._mapItem.enote = item._mapItem.enote;
2104
2105 if(item._fields & WorkingDrumMapEntry::ANoteField)
2106 wde._mapItem.anote = item._mapItem.anote;
2107
2108 if(item._fields & WorkingDrumMapEntry::MuteField)
2109 wde._mapItem.mute = item._mapItem.mute;
2110
2111 if(item._fields & WorkingDrumMapEntry::HideField)
2112 wde._mapItem.hide = item._mapItem.hide;
2113
2114 wde._fields |= item._fields;
2115 }
2116 }
2117
remove(int index,const WorkingDrumMapEntry & item)2118 int WorkingDrumMapList::remove(int index, const WorkingDrumMapEntry& item)
2119 {
2120 return remove(index, item._fields);
2121 }
2122
remove(int index,int fields)2123 int WorkingDrumMapList::remove(int index, int fields)
2124 {
2125 iWorkingDrumMapPatch_t iwp = find(index);
2126 if(iwp == end())
2127 return fields;
2128 WorkingDrumMapEntry& wde = iwp->second;
2129 int ret = wde._fields ^ fields;
2130 wde._fields &= ~fields;
2131 ret ^= wde._fields;
2132 if(wde._fields == WorkingDrumMapEntry::NoField)
2133 erase(iwp);
2134 return ret;
2135 }
2136
read(Xml & xml,bool fillUnused,int defaultIndex)2137 void WorkingDrumMapList::read(Xml& xml, bool fillUnused, int defaultIndex)
2138 {
2139 const QString start_tag = xml.s1();
2140 int index = defaultIndex;
2141 int index_read;
2142 bool enote_read = false;
2143 bool anote_read = false;
2144 bool ok;
2145 WorkingDrumMapEntry wdme;
2146 if(fillUnused)
2147 {
2148 // Must initialize the map item in case some fields aren't given.
2149 wdme._mapItem.init();
2150 // Technically we are overriding all fields even if some are not given.
2151 wdme._fields = WorkingDrumMapEntry::AllFields;
2152 }
2153
2154 for (;;) {
2155 Xml::Token token = xml.parse();
2156 const QString& tag = xml.s1();
2157 switch (token) {
2158 case Xml::Error:
2159 case Xml::End:
2160 return;
2161 case Xml::TagStart:
2162 if (tag == "name")
2163 {
2164 wdme._mapItem.name = xml.parse1();
2165 wdme._fields |= WorkingDrumMapEntry::NameField;
2166 }
2167 else if (tag == "vol")
2168 {
2169 wdme._mapItem.vol = xml.parseInt();
2170 wdme._fields |= WorkingDrumMapEntry::VolField;
2171 }
2172 else if (tag == "quant")
2173 {
2174 wdme._mapItem.quant = xml.parseInt();
2175 wdme._fields |= WorkingDrumMapEntry::QuantField;
2176 }
2177 else if (tag == "len")
2178 {
2179 wdme._mapItem.len = xml.parseInt();
2180 wdme._fields |= WorkingDrumMapEntry::LenField;
2181 }
2182 else if (tag == "channel")
2183 {
2184 wdme._mapItem.channel = xml.parseInt();
2185 wdme._fields |= WorkingDrumMapEntry::ChanField;
2186 }
2187 else if (tag == "port")
2188 {
2189 wdme._mapItem.port = xml.parseInt();
2190 wdme._fields |= WorkingDrumMapEntry::PortField;
2191 }
2192 else if (tag == "lv1")
2193 {
2194 wdme._mapItem.lv1 = xml.parseInt();
2195 wdme._fields |= WorkingDrumMapEntry::Lv1Field;
2196 }
2197 else if (tag == "lv2")
2198 {
2199 wdme._mapItem.lv2 = xml.parseInt();
2200 wdme._fields |= WorkingDrumMapEntry::Lv2Field;
2201 }
2202 else if (tag == "lv3")
2203 {
2204 wdme._mapItem.lv3 = xml.parseInt();
2205 wdme._fields |= WorkingDrumMapEntry::Lv3Field;
2206 }
2207 else if (tag == "lv4")
2208 {
2209 wdme._mapItem.lv4 = xml.parseInt();
2210 wdme._fields |= WorkingDrumMapEntry::Lv4Field;
2211 }
2212 else if (tag == "enote")
2213 {
2214 wdme._mapItem.enote = xml.parseInt();
2215 enote_read = true;
2216 wdme._fields |= WorkingDrumMapEntry::ENoteField;
2217 }
2218 else if (tag == "anote")
2219 {
2220 wdme._mapItem.anote = xml.parseInt();
2221 anote_read = true;
2222 wdme._fields |= WorkingDrumMapEntry::ANoteField;
2223 }
2224 else if (tag == "mute")
2225 {
2226 wdme._mapItem.mute = xml.parseInt();
2227 wdme._fields |= WorkingDrumMapEntry::MuteField;
2228 }
2229 else if (tag == "hide")
2230 {
2231 wdme._mapItem.hide = xml.parseInt();
2232 wdme._fields |= WorkingDrumMapEntry::HideField;
2233 }
2234 else
2235 xml.unknown(start_tag.toLatin1().constData());
2236 break;
2237 case Xml::Attribut:
2238 if (tag == "idx" || tag == "pitch")
2239 {
2240 index_read = xml.s2().toInt(&ok);
2241 if(ok)
2242 index = index_read;
2243 }
2244 break;
2245 case Xml::TagEnd:
2246 if (tag == start_tag)
2247 {
2248 if(index >= 0 && index < 128)
2249 {
2250 // If no enote was given, set it to the index.
2251 if(fillUnused)
2252 {
2253 if(!enote_read)
2254 wdme._mapItem.enote = index;
2255 // If no anote was given, set it to the enote.
2256 if(!anote_read)
2257 wdme._mapItem.anote = wdme._mapItem.enote;
2258 }
2259 insert(WorkingDrumMapPatchInsertPair_t(index, wdme));
2260 }
2261 return;
2262 }
2263 default:
2264 break;
2265 }
2266 }
2267 }
2268
write(int level,Xml & xml) const2269 void WorkingDrumMapList::write(int level, Xml& xml) const
2270 {
2271 int index;
2272 for(ciWorkingDrumMapPatch_t iwdp = begin(); iwdp != end(); ++iwdp)
2273 {
2274 index = iwdp->first;
2275 xml.tag(level++, "entry idx=\"%d\"", index);
2276
2277 const WorkingDrumMapEntry& wde = iwdp->second;
2278
2279 if(wde._fields & WorkingDrumMapEntry::NameField)
2280 xml.strTag(level, "name", wde._mapItem.name);
2281
2282 if(wde._fields & WorkingDrumMapEntry::VolField)
2283 xml.intTag(level, "vol", wde._mapItem.vol);
2284
2285 if(wde._fields & WorkingDrumMapEntry::QuantField)
2286 xml.intTag(level, "quant", wde._mapItem.quant);
2287
2288 if(wde._fields & WorkingDrumMapEntry::LenField)
2289 xml.intTag(level, "len", wde._mapItem.len);
2290
2291 if(wde._fields & WorkingDrumMapEntry::ChanField)
2292 xml.intTag(level, "channel", wde._mapItem.channel);
2293
2294 if(wde._fields & WorkingDrumMapEntry::PortField)
2295 xml.intTag(level, "port", wde._mapItem.port);
2296
2297 if(wde._fields & WorkingDrumMapEntry::Lv1Field)
2298 xml.intTag(level, "lv1", wde._mapItem.lv1);
2299
2300 if(wde._fields & WorkingDrumMapEntry::Lv2Field)
2301 xml.intTag(level, "lv2", wde._mapItem.lv2);
2302
2303 if(wde._fields & WorkingDrumMapEntry::Lv3Field)
2304 xml.intTag(level, "lv3", wde._mapItem.lv3);
2305
2306 if(wde._fields & WorkingDrumMapEntry::Lv4Field)
2307 xml.intTag(level, "lv4", wde._mapItem.lv4);
2308
2309 if(wde._fields & WorkingDrumMapEntry::ENoteField)
2310 xml.intTag(level, "enote", wde._mapItem.enote);
2311
2312 if(wde._fields & WorkingDrumMapEntry::ANoteField)
2313 xml.intTag(level, "anote", wde._mapItem.anote);
2314
2315 if(wde._fields & WorkingDrumMapEntry::MuteField)
2316 xml.intTag(level, "mute", wde._mapItem.mute);
2317
2318 if(wde._fields & WorkingDrumMapEntry::HideField)
2319 xml.intTag(level, "hide", wde._mapItem.hide);
2320
2321 xml.tag(--level, "/entry");
2322 }
2323 }
2324
2325
2326 //---------------------------------------------------------
2327 // WorkingDrumMapInstrumentList
2328 //---------------------------------------------------------
2329
read(Xml & xml)2330 void WorkingDrumMapInstrumentList::read(Xml& xml)
2331 {
2332 const QString start_tag = xml.s1();
2333 QString instr_name;
2334 WorkingDrumMapPatchList wdmpl;
2335 for (;;) {
2336 Xml::Token token = xml.parse();
2337 const QString& tag = xml.s1();
2338 switch (token) {
2339 case Xml::Error:
2340 case Xml::End:
2341 return;
2342 case Xml::TagStart:
2343 if (tag == "drumMapPatch")
2344 // false = Do not fill in unused items.
2345 wdmpl.read(xml, false);
2346 else
2347 xml.unknown(start_tag.toLatin1().constData());
2348 break;
2349 case Xml::Attribut:
2350 if (tag == "instrument")
2351 {
2352 instr_name = xml.s2();
2353 }
2354 break;
2355 case Xml::TagEnd:
2356 if (tag == start_tag)
2357 {
2358 if(!instr_name.isEmpty() && !wdmpl.empty())
2359 insert(WorkingDrumMapInstrumentListInsertPair_t(instr_name.toStdString(), wdmpl));
2360 return;
2361 }
2362 default:
2363 break;
2364 }
2365 }
2366 }
2367
2368
2369 //---------------------------------------------------------
2370 // WorkingDrumMapPatchList
2371 //---------------------------------------------------------
2372
add(const WorkingDrumMapPatchList & other)2373 void WorkingDrumMapPatchList::add(const WorkingDrumMapPatchList& other)
2374 {
2375 int patch;
2376 int index;
2377 for(ciWorkingDrumMapPatchList_t iwdmpl = other.begin(); iwdmpl != other.end(); ++iwdmpl)
2378 {
2379 patch = iwdmpl->first;
2380 const WorkingDrumMapList& wdml = iwdmpl->second;
2381 WorkingDrumMapPatchListInsertResult_t res = insert(WorkingDrumMapPatchListInsertPair_t(patch, wdml));
2382 iWorkingDrumMapPatchList_t res_iwdmpl = res.first;
2383 if(res_iwdmpl == end()) // Error.
2384 continue;
2385 WorkingDrumMapList& res_wdml = res_iwdmpl->second;
2386 for(iWorkingDrumMapPatch_t res_iwdp = res_wdml.begin(); res_iwdp != res_wdml.end(); ++res_iwdp)
2387 {
2388 index = res_iwdp->first;
2389 WorkingDrumMapEntry& wdme = res_iwdp->second;
2390 res_wdml.add(index, wdme);
2391 }
2392 }
2393 }
2394
add(int patch,const WorkingDrumMapList & list)2395 void WorkingDrumMapPatchList::add(int patch, const WorkingDrumMapList& list)
2396 {
2397 insert(WorkingDrumMapPatchListInsertPair_t(patch, list));
2398 }
2399
add(int patch,int index,const WorkingDrumMapEntry & item)2400 void WorkingDrumMapPatchList::add(int patch, int index, const WorkingDrumMapEntry& item)
2401 {
2402 WorkingDrumMapPatchListInsertResult_t res = insert(WorkingDrumMapPatchListInsertPair_t(patch, WorkingDrumMapList()));
2403 iWorkingDrumMapPatchList_t iwdmpl = res.first;
2404 if(iwdmpl == end()) // Error, should exist.
2405 return;
2406 WorkingDrumMapList& wdml = iwdmpl->second;
2407 wdml.add(index, item);
2408 }
2409
remove(int patch,int index,const WorkingDrumMapEntry & item,bool includeDefault)2410 void WorkingDrumMapPatchList::remove(int patch, int index, const WorkingDrumMapEntry& item, bool includeDefault)
2411 {
2412 remove(patch, index, item._fields, includeDefault);
2413 }
2414
remove(int patch,int index,int fields,bool includeDefault)2415 void WorkingDrumMapPatchList::remove(int patch, int index, int fields, bool includeDefault)
2416 {
2417 // Remove requested fields from the exact patch number first.
2418 iWorkingDrumMapPatchList_t iwdmpl = WorkingDrumMapPatchList_t::find(patch);
2419 if(iwdmpl != end())
2420 {
2421 WorkingDrumMapList& wdml = iwdmpl->second;
2422 // Consider defaults and real patch overrides as part of the same deal,
2423 // remove all requested fields from BOTH.
2424 //fields = wdml.remove(index, fields);
2425 wdml.remove(index, fields);
2426 // No more items in the list? Remove this container list.
2427 if(wdml.empty())
2428 erase(iwdmpl);
2429 }
2430
2431 // Consider defaults and real patch overrides as part of the same deal,
2432 // remove all requested fields from BOTH.
2433 //if(includeDefault && fields != WorkingDrumMapEntry::NoField)
2434 if(includeDefault)
2435 {
2436 iwdmpl = WorkingDrumMapPatchList_t::find(CTRL_PROGRAM_VAL_DONT_CARE);
2437 if(iwdmpl != end())
2438 {
2439 WorkingDrumMapList& wdml = iwdmpl->second;
2440 wdml.remove(index, fields);
2441 // No more items in the list? Remove this container list.
2442 if(wdml.empty())
2443 erase(iwdmpl);
2444 }
2445 }
2446 }
2447
remove(int patch,bool includeDefault)2448 void WorkingDrumMapPatchList::remove(int patch, bool includeDefault)
2449 {
2450 // Remove requested fields from the exact patch number first.
2451 iWorkingDrumMapPatchList_t iwdmpl = WorkingDrumMapPatchList_t::find(patch);
2452 if(iwdmpl != end())
2453 erase(iwdmpl);
2454 // Patch map not found? Look for a default patch number (all three pr, hb, lb = don't care).
2455 else if(includeDefault)
2456 {
2457 iwdmpl = WorkingDrumMapPatchList_t::find(CTRL_PROGRAM_VAL_DONT_CARE);
2458 if(iwdmpl != end())
2459 erase(iwdmpl);
2460 }
2461 }
2462
find(int patch,bool includeDefault)2463 WorkingDrumMapList* WorkingDrumMapPatchList::find(int patch, bool includeDefault)
2464 {
2465 // Look for an exact match above all else. The given patch must be valid.
2466 iWorkingDrumMapPatchList_t iwdmpl = WorkingDrumMapPatchList_t::find(patch);
2467 // If no exact match is found we'll take a default if found (all three pr, hb, lb = don't care).
2468 if(iwdmpl == end())
2469 {
2470 if(!includeDefault)
2471 return NULL;
2472 iwdmpl = WorkingDrumMapPatchList_t::find(CTRL_PROGRAM_VAL_DONT_CARE);
2473 if(iwdmpl == end())
2474 return NULL;
2475 }
2476 return &iwdmpl->second;
2477 }
2478
find(int patch,bool includeDefault) const2479 const WorkingDrumMapList* WorkingDrumMapPatchList::find(int patch, bool includeDefault) const
2480 {
2481 // Look for an exact match above all else. The given patch must be valid.
2482 ciWorkingDrumMapPatchList_t iwdmpl = WorkingDrumMapPatchList_t::find(patch);
2483 // If no exact match is found we'll take a default if found (all three pr, hb, lb = don't care).
2484 if(iwdmpl == end())
2485 {
2486 if(!includeDefault)
2487 return NULL;
2488 iwdmpl = WorkingDrumMapPatchList_t::find(CTRL_PROGRAM_VAL_DONT_CARE);
2489 if(iwdmpl == end())
2490 return NULL;
2491 }
2492 return &iwdmpl->second;
2493 }
2494
find(int patch,int index,bool includeDefault)2495 WorkingDrumMapEntry* WorkingDrumMapPatchList::find(int patch, int index, bool includeDefault)
2496 {
2497 WorkingDrumMapList* wdmpl = find(patch, includeDefault);
2498 if(!wdmpl)
2499 return NULL;
2500 iWorkingDrumMapPatch_t iwdmp = wdmpl->find(index);
2501 if(iwdmp == wdmpl->end())
2502 return NULL;
2503 return &iwdmp->second;
2504 }
2505
find(int patch,int index,bool includeDefault) const2506 const WorkingDrumMapEntry* WorkingDrumMapPatchList::find(int patch, int index, bool includeDefault) const
2507 {
2508 const WorkingDrumMapList* wdmpl = find(patch, includeDefault);
2509 if(!wdmpl)
2510 return NULL;
2511 ciWorkingDrumMapPatch_t iwdmp = wdmpl->find(index);
2512 if(iwdmp == wdmpl->end())
2513 return NULL;
2514 return &iwdmp->second;
2515 }
2516
read(Xml & xml,bool fillUnused)2517 void WorkingDrumMapPatchList::read(Xml& xml, bool fillUnused)
2518 {
2519 const QString start_tag = xml.s1();
2520 // Default "don't care" patch number, in case no patch number found.
2521 int patch = 0xffffff;
2522 int patch_read;
2523 bool ok;
2524 int index = 0;
2525 WorkingDrumMapList wdml;
2526
2527 // TODO Need to move this stuff up into the caller, because a default patch map may not have been loaded yet!
2528 // For now, we rely on the loaded map being trustworthy with no duplicate enotes. Still the situation IS compensated
2529 // for at the lowest level in MidiTrack::normalizeDrumMap(), so it IS tolerant of mistakes in the loaded map.
2530 //
2531 // REMOVE Tim. newdrums. Removed.
2532 // WorkingDrumMapList* def_wdml = 0;
2533 // //if(patch != CTRL_PROGRAM_VAL_DONT_CARE)
2534 // def_wdml = find(CTRL_PROGRAM_VAL_DONT_CARE, false);
2535 // WorkingDrumMapEntry new_wdme;
2536 // // We can init these outside of the loop.
2537 // new_wdme._fields = WorkingDrumMapEntry::AllFields;
2538 // new_wdme._mapItem.init();
2539 //
2540 // bool used_index[128];
2541 // int used_enotes[128];
2542 // for(int i = 0; i < 128; ++i)
2543 // {
2544 // used_index[i] = false;
2545 // used_enotes[i] = 0;
2546 // }
2547 // char unused_enotes[128];
2548 // int unused_enotes_sz = 0;
2549 // char unused_index[128];
2550 // int unused_index_sz = 0;
2551
2552 for (;;) {
2553 Xml::Token token = xml.parse();
2554 const QString& tag = xml.s1();
2555 switch (token) {
2556 case Xml::Error:
2557 case Xml::End:
2558 return;
2559 case Xml::TagStart:
2560 if (tag == "entry")
2561 {
2562 // In case there are no index attributes in this drum map,
2563 // we use a running index.
2564 wdml.read(xml, fillUnused, index);
2565 ++index;
2566 }
2567 else if (tag == "comment")
2568 xml.parse();
2569 else
2570 xml.unknown(start_tag.toLatin1().constData());
2571 break;
2572 case Xml::Attribut:
2573 if (tag == "patch")
2574 {
2575 patch_read = xml.s2().toInt(&ok);
2576 if(ok)
2577 patch = patch_read;
2578 }
2579 break;
2580 case Xml::TagEnd:
2581 if (tag == start_tag)
2582 {
2583
2584 if(!wdml.empty())
2585 {
2586 // // // We can only deal with duplicate enotes here if requesting to
2587 // // // fill up all items, because in the context of further overrides
2588 // // // masking any unused ones here, we cannot fully know which enotes are used.
2589 // //if(fillUnused)
2590 // //{
2591 // // Find all the used enotes and indexes.
2592 // for(iWorkingDrumMapPatch_t iwdml = wdml.begin(); iwdml != wdml.end(); ++iwdml)
2593 // {
2594 // used_index[iwdml->first] = true;
2595 // ++used_enotes[(unsigned char)iwdml->second._mapItem.enote];
2596 // }
2597 //
2598 // // Find all the unused enotes and indexes.
2599 // for(int i = 0; i < 128; ++i)
2600 // {
2601 // if(!used_index[i])
2602 // unused_index[unused_index_sz++] = i;
2603 // if(used_enotes[i] == 0)
2604 // unused_enotes[unused_enotes_sz++] = i;
2605 // }
2606 //
2607 // // Ensure there are NO duplicate enotes in the existing map items so far.
2608 // int unused_enotes_cnt = 0;
2609 // for(iWorkingDrumMapPatch_t iwdml = wdml.begin(); iwdml != wdml.end(); ++iwdml)
2610 // {
2611 // // More than 1 (this) usage?
2612 // if(used_enotes[(unsigned char)iwdml->second._mapItem.enote] > 1)
2613 // {
2614 // if(unused_enotes_cnt >= unused_enotes_sz)
2615 // {
2616 // fprintf(stderr, "WorkingDrumMapPatchList::read: Error: unused_enotes_cnt >= unused_enotes_sz:%d\n",
2617 // unused_enotes_sz);
2618 // break;
2619 // }
2620 // --used_enotes[(unsigned char)iwdml->second._mapItem.enote];
2621 // iwdml->second._mapItem.enote = unused_enotes[unused_enotes_cnt++];
2622 // }
2623 // }
2624 //
2625 // // Technically we are overriding the entire map, even if some map items weren't given.
2626 // // In case of loading a partial or incomplete map, ensure that all 128 map items are filled.
2627 // for(int i = 0; i < unused_index_sz; ++i)
2628 // {
2629 // if(unused_enotes_cnt >= unused_enotes_sz)
2630 // {
2631 // fprintf(stderr, "WorkingDrumMapPatchList::read: Error filling unused items: unused_enotes_cnt >= unused_enotes_sz:%d\n",
2632 // unused_enotes_sz);
2633 // break;
2634 // }
2635 // // Set the enote.
2636 // new_wdme._mapItem.enote = unused_enotes[unused_enotes_cnt++];
2637 // // Might as well set the anote to enote.
2638 // new_wdme._mapItem.anote = new_wdme._mapItem.enote;
2639 // // Insert the item at the unused index.
2640 // wdml.insert(WorkingDrumMapPatchInsertPair_t(unused_index[i], new_wdme));
2641 // }
2642 // //}
2643
2644 // Insert the working drum map list at the patch.
2645 insert(WorkingDrumMapPatchListInsertPair_t(patch, wdml));
2646 }
2647 return;
2648 }
2649 default:
2650 break;
2651 }
2652 }
2653 }
2654
write(int level,Xml & xml) const2655 void WorkingDrumMapPatchList::write(int level, Xml& xml) const
2656 {
2657 int patch;
2658 for(ciWorkingDrumMapPatchList_t iwdpl = begin(); iwdpl != end(); ++iwdpl)
2659 {
2660 const WorkingDrumMapList& wdml = iwdpl->second;
2661 if(wdml.empty())
2662 continue;
2663 patch = iwdpl->first;
2664 xml.tag(level++, "drumMapPatch patch=\"%d\"", patch);
2665 wdml.write(level, xml);
2666 xml.etag(--level, "drumMapPatch");
2667 }
2668 }
2669
2670
2671 //---------------------------------------------------------
2672 // ChannelDrumMappingList
2673 //---------------------------------------------------------
2674
ChannelDrumMappingList()2675 ChannelDrumMappingList::ChannelDrumMappingList()
2676 {
2677 // Ensure there is always a default channel.
2678 // Initialize with a default drum map on default channel. Patch is default 0xffffff. GM-1 does not specify a drum patch number.
2679 add(-1, patch_drummap_mapping_list_t());
2680 }
2681
add(const ChannelDrumMappingList & other)2682 void ChannelDrumMappingList::add(const ChannelDrumMappingList& other)
2683 {
2684 int channel;
2685
2686 for(ciChannelDrumMappingList_t icdml = other.begin(); icdml != other.end(); ++icdml)
2687 {
2688 channel = icdml->first;
2689 const patch_drummap_mapping_list_t& pdml = icdml->second;
2690 add(channel, pdml);
2691 }
2692 }
2693
add(int channel,const patch_drummap_mapping_list_t & list)2694 void ChannelDrumMappingList::add(int channel, const patch_drummap_mapping_list_t& list)
2695 {
2696 ChannelDrumMappingListInsertResult_t res = insert(ChannelDrumMappingListInsertPair_t(channel, list));
2697 if(res.second == false)
2698 {
2699 iChannelDrumMappingList_t res_icdml = res.first;
2700 patch_drummap_mapping_list_t& res_pdml = res_icdml->second;
2701 res_pdml.add(list);
2702 }
2703 }
2704
find(int channel,bool includeDefault)2705 patch_drummap_mapping_list_t* ChannelDrumMappingList::find(int channel, bool includeDefault)
2706 {
2707 // Look for an exact match above all else. The given channel must be valid.
2708 iChannelDrumMappingList_t icdml = ChannelDrumMappingList_t::find(channel);
2709 // If no exact match is found we'll take a default if found.
2710 if(icdml == end())
2711 {
2712 if(!includeDefault)
2713 return NULL;
2714 icdml = ChannelDrumMappingList_t::find(-1);
2715 if(icdml == end())
2716 return NULL;
2717 }
2718 return &icdml->second;
2719 }
2720
find(int channel,bool includeDefault) const2721 const patch_drummap_mapping_list_t* ChannelDrumMappingList::find(int channel, bool includeDefault) const
2722 {
2723 // Look for an exact match above all else. The given channel must be valid.
2724 ciChannelDrumMappingList_t icdml = ChannelDrumMappingList_t::find(channel);
2725 // If no exact match is found we'll take a default if found.
2726 if(icdml == end())
2727 {
2728 if(!includeDefault)
2729 return NULL;
2730 icdml = ChannelDrumMappingList_t::find(-1);
2731 if(icdml == end())
2732 return NULL;
2733 }
2734 return &icdml->second;
2735 }
2736
read(Xml & xml)2737 void ChannelDrumMappingList::read(Xml& xml)
2738 {
2739 const QString start_tag = xml.s1();
2740 // Default "don't care" channel number, in case no channel number found.
2741 int channel = -1; // Default.
2742 int channel_read;
2743 bool ok;
2744
2745 for (;;) {
2746 Xml::Token token = xml.parse();
2747 const QString& tag = xml.s1();
2748 switch (token) {
2749 case Xml::Error:
2750 case Xml::End:
2751 return;
2752 case Xml::TagStart:
2753 if (tag == "entry")
2754 {
2755 patch_drummap_mapping_list_t pdml;
2756 pdml.read(xml);
2757 if(!pdml.empty())
2758 add(channel, pdml);
2759 }
2760 else if (tag == "comment")
2761 xml.parse();
2762 else
2763 xml.unknown(start_tag.toLatin1().constData());
2764 break;
2765 case Xml::Attribut:
2766 if (tag == "channel")
2767 {
2768 channel_read = xml.s2().toInt(&ok);
2769 if(ok)
2770 channel = channel_read;
2771 }
2772 break;
2773 case Xml::TagEnd:
2774 if (tag == start_tag)
2775 return;
2776 default:
2777 break;
2778 }
2779 }
2780 }
2781
write(int level,Xml & xml) const2782 void ChannelDrumMappingList::write(int level, Xml& xml) const
2783 {
2784 int channel;
2785
2786 // Count the items used.
2787 int sz = 0;
2788 for(ciChannelDrumMappingList_t icdml = begin(); icdml != end(); ++icdml)
2789 {
2790 const patch_drummap_mapping_list_t& pdml = icdml->second;
2791 if(pdml.empty())
2792 continue;
2793 ++sz;
2794 }
2795
2796 for(ciChannelDrumMappingList_t icdml = begin(); icdml != end(); ++icdml)
2797 {
2798 const patch_drummap_mapping_list_t& pdml = icdml->second;
2799 if(pdml.empty())
2800 continue;
2801 channel = icdml->first;
2802
2803 // Don't bother with the drumMapChannel tag if not required.
2804 if(sz >= 2 || channel != -1) // -1 is default.
2805 xml.tag(level++, "drumMapChannel channel=\"%d\"", channel);
2806
2807 pdml.write(level, xml);
2808
2809 if(sz >= 2 || channel != -1) // -1 is default.
2810 xml.etag(--level, "drumMapChannel");
2811 }
2812 }
2813
2814 } // namespace MusECore
2815