11namespace tracktion {
inline namespace engine
20 : name (name_), hi (high), lo (low)
37 if (set.banks.isEmpty())
41 rootXml->setAttribute (
"name", set.name);
43 for (
auto bankSet : set.banks)
45 auto bankXml = rootXml->createNewChildElement (
"bank");
46 bankXml->setAttribute (
"name", bankSet->name);
47 bankXml->setAttribute (
"high", bankSet->hi);
48 bankXml->setAttribute (
"low", bankSet->lo);
50 for (
int j = 0; j < bankSet->patches.size(); ++j)
52 auto programXml = bankXml->createNewChildElement (
"program");
53 programXml->setAttribute (
"name", bankSet->patches[j]);
54 programXml->setAttribute (
"number", bankSet->patchNumbers[j]);
61inline BankSet* getBankSet (ProgramSet& set,
const juce::String& name)
63 for (
int i = 0; i < set.banks.size(); ++i)
64 if (set.banks.getUnchecked (i)->name == name)
65 return set.banks.getUnchecked (i);
67 return set.banks.add (
new BankSet (name));
77 if (rootXml ==
nullptr)
81 src.loadFileAsData (mb);
83 for (
size_t i = 0; i < mb.
getSize(); ++i)
89 int firstidx = str.
indexOf(
"<");
90 int lastidx = str.lastIndexOf(
">");
92 if (firstidx != -1 && lastidx != -1)
93 str = str.substring(firstidx, lastidx);
97 if (rootXml ==
nullptr)
101 auto devXml = rootXml->getChildByName (
"MasterDeviceNames");
103 if (devXml ==
nullptr)
106 if (
auto manufacturerXml = devXml->getChildByName (
"Manufacturer"))
107 manufacturer += manufacturerXml->getAllSubText();
109 for (
auto modelXml : devXml->getChildWithTagNameIterator (
"Model"))
110 models.add (modelXml->getAllSubText());
112 ProgramSet programSet;
113 programSet.name = manufacturer;
115 for (
int i = 0; i < models.
size(); ++i)
116 programSet.name +=
" " + models[i];
118 programSet.name.trim();
120 for (
auto patchNameListXml : devXml->getChildWithTagNameIterator (
"PatchNameList"))
122 auto name = patchNameListXml->getStringAttribute(
"Name");
123 auto bankSet = getBankSet (programSet, name);
125 for (
auto patchXml : patchNameListXml->getChildWithTagNameIterator (
"Patch"))
127 auto patchName = patchXml->getStringAttribute (
"Name");
129 if (patchName.isNotEmpty())
131 int num = patchXml->getIntAttribute(
"ProgramChange");
133 if (! bankSet->patchNumbers.contains (num))
135 bankSet->patches.add (patchName);
136 bankSet->patchNumbers.add (num);
142 for (
auto channelNameSetXml : devXml->getChildWithTagNameIterator (
"ChannelNameSet"))
144 for (
auto patchBankXml : channelNameSetXml->getChildWithTagNameIterator (
"PatchBank"))
146 auto name = patchBankXml->getStringAttribute (
"Name");
147 auto bankSet = getBankSet (programSet, name);
149 if (
auto midiCommandsXml = patchBankXml->getChildByName (
"MIDICommands"))
151 for (
auto controlChangeXml : midiCommandsXml->getChildWithTagNameIterator (
"ControlChange"))
153 if (controlChangeXml->hasAttribute (
"Control") && controlChangeXml->hasAttribute(
"Value"))
155 switch (controlChangeXml->getIntAttribute (
"Control"))
157 case 0 : bankSet->hi = controlChangeXml->getIntAttribute (
"Value");
break;
158 case 32: bankSet->lo = controlChangeXml->getIntAttribute (
"Value");
break;
164 if (
auto patchNameListXml = patchBankXml->getChildByName (
"PatchNameList"))
166 for (
auto patchXml : patchNameListXml->getChildWithTagNameIterator (
"Patch"))
168 auto patchName = patchXml->getStringAttribute(
"Name");
170 if (patchName.isNotEmpty())
172 const int num = patchXml->getIntAttribute (
"ProgramChange");
174 if (! bankSet->patchNumbers.contains (num))
176 bankSet->patches.add (patchName);
177 bankSet->patchNumbers.add (num);
185 for (
int i = programSet.banks.size(); --i >= 0;)
186 if (
auto b = programSet.banks.getUnchecked (i))
187 if (b->patches.isEmpty())
188 programSet.banks.remove (i);
190 return exportProgramSet (programSet);
194MidiProgramManager::MidiProgramSet::MidiProgramSet()
196 for (
int i = 0; i < 16; ++i)
198 midiBanks[i].id = i << 8;
206 n->setAttribute (
"name", name);
207 n->setAttribute (
"low",
id % 256);
208 n->setAttribute (
"high",
id / 256);
210 for (
auto& p : programNames)
212 auto currentProgramXml = n->createNewChildElement (
"program");
213 currentProgramXml->setAttribute (
"number", p.first);
214 currentProgramXml->setAttribute (
"name", p.second);
225 for (
auto programXml : n.getChildWithTagNameIterator (
"program"))
227 programNames[programXml->getIntAttribute (
"number")]
228 = programXml->getStringAttribute (
"name");
239 for (
int i = 0; i < 16; ++i)
245void MidiProgramManager::MidiProgramSet::updateFromXml (
const juce::XmlElement& n)
252 for (
auto currentChildXml : n.getChildIterator())
254 if (currentChildXml->hasTagName (
"bank"))
255 midiBanks[idx++].updateFromXml (*currentChildXml);
263MidiProgramManager::MidiProgramManager (
Engine& e)
266 auto xml = e.getPropertyStorage().getXmlProperty (SettingID::midiProgramManager);
270 for (
auto currentSetXml : xml->getChildIterator())
272 auto* currentSet =
new MidiProgramSet();
273 currentSet->updateFromXml (*currentSetXml);
274 programSets.add (currentSet);
278 if (xml ==
nullptr || ! xml->getBoolAttribute (
"createdRootGroup",
false))
280 auto* defaultSet =
new MidiProgramSet();
281 defaultSet->name =
TRANS(
"Custom");
282 programSets.add (defaultSet);
288void MidiProgramManager::saveAll()
291 xml.setAttribute (
"createdRootGroup",
true);
293 for (
int i = 0; i < programSets.size(); ++i)
295 auto* currentSet = programSets[i];
296 auto* currentSetXml = currentSet->createXml();
297 xml.addChildElement (currentSetXml);
308 for (
int i = 0; i < programSets.size(); ++i)
309 names.
add (programSets.getUnchecked (i)->name);
314juce::String MidiProgramManager::getProgramName (
int set,
int bank,
int program)
319 if (
auto p = programSets[set - 1])
323 auto n = p->midiBanks[bank].programNames.find (program);
325 if (n != p->midiBanks[bank].programNames.end())
330 return getDefaultName (bank, program, programSets[set - 1]->zeroBased);
333juce::String MidiProgramManager::getBankName (
int set,
int bank)
335 if (
auto p = programSets[set - 1])
339 auto name = p->midiBanks[bank].name;
341 if (name.isNotEmpty())
349int MidiProgramManager::getBankID (
int set,
int bank)
351 if (
auto p = programSets[set - 1])
353 return p->midiBanks[bank].id;
358void MidiProgramManager::setBankID (
int set,
int bank,
int id)
360 if (
auto p = programSets[set - 1])
362 p->midiBanks[bank].id = id;
365bool MidiProgramManager::isZeroBased (
int set)
367 if (
auto p = programSets[set - 1])
373void MidiProgramManager::setZeroBased (
int set,
bool zeroBased)
375 if (
auto p = programSets[set - 1])
376 p->zeroBased = zeroBased;
379void MidiProgramManager::setProgramName (
int set,
int bank,
int program,
const juce::String& newName)
381 if (
auto p = programSets[set - 1])
383 p->midiBanks[bank].programNames[program] = newName;
386void MidiProgramManager::setBankName (
int set,
int bank,
const juce::String& newName)
388 if (
auto p = programSets[set - 1])
390 p->midiBanks[bank].name = newName;
393void MidiProgramManager::clearProgramName (
int set,
int bank,
int program)
395 if (
auto p = programSets[set - 1])
397 p->midiBanks[bank].programNames.erase (program);
400juce::String MidiProgramManager::getDefaultName (
int,
int program,
bool zeroBased)
402 return TRANS(
"Patch") +
" "
408 for (
int i = 0; i < programSets.size(); ++i)
409 if (programSets.getUnchecked (i)->name ==
TRANS(
"Custom"))
410 return TRANS(
"Custom");
412 return TRANS(
"General MIDI");
415int MidiProgramManager::getSetIndex (
const juce::String& setName)
417 for (
int i = 0; i < programSets.size(); ++i)
418 if (programSets.getUnchecked (i)->name == setName)
424bool MidiProgramManager::doesSetExist (
const juce::String& name)
const noexcept
426 if (name ==
TRANS(
"General MIDI"))
429 for (
int i = 0; i < programSets.size(); ++i)
430 if (programSets.getUnchecked (i)->name == name)
436bool MidiProgramManager::canEditProgramSet (
int set)
const noexcept
441bool MidiProgramManager::canDeleteProgramSet (
int set)
const noexcept
446void MidiProgramManager::addNewSet (
const juce::String& name)
448 auto newSet = programSets.add (
new MidiProgramSet());
455 auto newSet = programSets.add (
new MidiProgramSet());
456 newSet->updateFromXml (element);
461void MidiProgramManager::deleteSet (
int set)
463 programSets.remove (set - 1);
467bool MidiProgramManager::importSet (
int set,
const juce::File& file)
471 if (
auto xml = file.hasFileExtension(
"midnam") ? convertMidnamToXml (file)
474 if (
auto s = programSets[set - 1])
476 auto oldName = s->name;
477 s->updateFromXml (*xml);
487bool MidiProgramManager::exportSet (
int set,
const juce::File& file)
491 if (
auto programSet = programSets[set - 1])
492 xml.
reset (programSet->createXml());
494 return xml !=
nullptr && xml->writeTo (file);
499 auto presetZipFile = engine.getTemporaryFileManager().getTempDirectory()
500 .getChildFile (
"patchnames.zip");
502 if (! presetZipFile.exists())
504 presetZipFile.getParentDirectory().createDirectory();
505 presetZipFile.replaceWithData (TracktionBinaryData::patchnames_zip, TracktionBinaryData::patchnames_zipSize);
508 return presetZipFile;
516 for (
int i = 0; i < zf.getNumEntries(); ++i)
518 auto entry = zf.getEntry(i);
519 auto lastDot = entry->filename.lastIndexOfChar (
'.');
522 presets.
add (entry->filename.substring (0, lastDot));
524 presets.
add (entry->filename);
536 for (
int i = 0; i < zf.getNumEntries(); ++i)
538 auto entry = zf.getEntry(i);
539 auto lastDot = entry->filename.lastIndexOfChar (
'.');
540 auto curName = (lastDot >= 0) ? entry->filename.substring (0, lastDot)
543 if (curName == presetName)
548 return is->readEntireStreamAsString();
size_t getSize() const noexcept
static const char * getGMInstrumentName(int midiInstrumentNumber)
void sort(bool ignoreCase)
int size() const noexcept
void add(String stringToAdd)
int indexOf(StringRef textToLookFor) const noexcept
void addChildElement(XmlElement *newChildElement) noexcept
bool getBoolAttribute(StringRef attributeName, bool defaultReturnValue=false) const
int getIntAttribute(StringRef attributeName, int defaultReturnValue=0) const
const String & getStringAttribute(StringRef attributeName) const noexcept
void setAttribute(const Identifier &attributeName, const String &newValue)
PropertyStorage & getPropertyStorage() const
Returns the PropertyStorage user settings customisable XML file.
#define TRANS(stringLiteral)
std::unique_ptr< XmlElement > parseXML(const String &textToParse)
constexpr int numElementsInArray(Type(&)[N]) noexcept