/* -*- Mode: c++ -*- */ /*************************************************************************** * domloader.cc * * Sun Jun 10 17:39:01 CEST 2018 * Copyright 2018 Bent Bisballe Nyeng * deva@aasimon.org ****************************************************************************/ /* * This file is part of DrumGizmo. * * DrumGizmo is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * DrumGizmo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with DrumGizmo; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include "domloader.h" #include #include #include "DGDOM.h" #include "drumkit.h" #include "path.h" #include "channel.h" #include "cpp11fix.h" struct channel_attribute_t { std::string cname; main_state_t main_state; }; DOMLoader::DOMLoader(Settings& settings, Random& random) : settings(settings) , random(random) { } bool DOMLoader::loadDom(const std::string& basepath, const DrumkitDOM& dom, const std::vector& instrumentdoms, DrumKit& drumkit, LogFunction logger) { settings.has_bleed_control.store(false); drumkit.metadata._name = dom.metadata.title; drumkit.metadata._version = dom.version; drumkit.metadata._description = dom.metadata.description; drumkit.metadata._samplerate = dom.samplerate; for(const auto& channel: dom.channels) { drumkit.channels.emplace_back(); drumkit.channels.back().name = channel.name; drumkit.channels.back().num = drumkit.channels.size() - 1; } // Pass 1 - handling everything that is instrument name based and ultimately // inserts the instrument into the drumkit instrument list which results in // it getting an instrument id (ie. the index in the list). for(const auto& instrumentref : dom.instruments) { bool found{false}; std::unordered_map channelmap; for(const auto& map : instrumentref.channel_map) { channel_attribute_t cattr{map.out, map.main}; channelmap[map.in] = cattr; } for(const auto& instrumentdom : instrumentdoms) { if(instrumentdom.name != instrumentref.name) { continue; } auto instrument = std::make_unique(settings, random); instrument->setGroup(instrumentref.group); instrument->_name = instrumentdom.name; instrument->version = instrumentdom.version; instrument->_description = instrumentdom.description; auto path = getPath(basepath + "/" + instrumentref.file); for(const auto& sampledom : instrumentdom.samples) { auto sample = new Sample(sampledom.name, sampledom.power, sampledom.normalized); for(const auto& audiofiledom : sampledom.audiofiles) { InstrumentChannel *instrument_channel = DOMLoader::addOrGetChannel(*instrument, audiofiledom.instrument_channel); auto audio_file = std::make_unique(path + "/" + audiofiledom.file, audiofiledom.filechannel - 1, // xml is 1-based instrument_channel); sample->addAudioFile(instrument_channel, audio_file.get()); // Transfer audio_file ownership to the instrument. instrument->audiofiles.push_back(std::move(audio_file)); } instrument->samplelist.push_back(sample); } main_state_t default_main_state = main_state_t::unset; for(const auto& channel : channelmap) { if(channel.second.main_state != main_state_t::unset) { default_main_state = main_state_t::is_not_main; } } // Assign kit channel numbers to instruments channels and reset // main_state attribute as needed. for(auto& instrument_channel: instrument->instrument_channels) { channel_attribute_t cattr{instrument_channel.name, main_state_t::unset}; if(channelmap.find(instrument_channel.name) != channelmap.end()) { cattr = channelmap[instrument_channel.name]; } if(cattr.main_state == main_state_t::unset) { cattr.main_state = default_main_state; } if(cattr.main_state != main_state_t::unset) { instrument_channel.main = cattr.main_state; } for(auto cnt = 0u; cnt < drumkit.channels.size(); ++cnt) { if(drumkit.channels[cnt].name == cattr.cname) { instrument_channel.num = drumkit.channels[cnt].num; instrument_channel.name = drumkit.channels[cnt].name; if(instrument_channel.main == main_state_t::is_main) { settings.has_bleed_control.store(true); } } } if(instrument_channel.num == NO_CHANNEL) { ERR(kitparser, "Missing channel '%s' in instrument '%s'\n", instrument_channel.name.c_str(), instrument->getName().c_str()); logger(LogLevel::Warning, "Missing channel '" + instrument_channel.name + "' in the '" + instrument->getName() + "' instrument."); } } if(instrument->version == VersionStr(1,0,0)) { // Version 1.0 use velocity groups for(auto& velocity : instrumentdom.velocities) { for(const auto& sampleref : velocity.samplerefs) { bool found_sample{false}; for(const auto& sample : instrument->samplelist) { // TODO: What should be done with the probability?? if(sample->name == sampleref.name) { instrument->addSample(velocity.lower, velocity.upper, sample); found_sample = true; break; } } if(!found_sample) { ERR(kitparser, "Missing sample '%s' from sampleref in instrument '%s'\n", sampleref.name.data(), instrument->getName().data()); logger(LogLevel::Warning, "Missing sample '" + sampleref.name + "' in the '" + instrument->getName() + "' instrument."); return false; } } } } instrument->finalise(); // Transfer ownership to the DrumKit object. drumkit.instruments.emplace_back(std::move(instrument)); drumkit.instruments.back()->id = drumkit.instruments.size() - 1; found = true; } if(!found) { ERR(domloader, "No instrument with name '%s'", instrumentref.name.data()); logger(LogLevel::Warning, "No instrument with name '" + instrumentref.name + "'."); return false; } } // Pass 2 - handle nodes that require instrument name to instrument id // mappings for(const auto& instrumentref : dom.instruments) { std::size_t instr_id{0}; { bool found{false}; for(std::size_t idx = 0; idx < drumkit.instruments.size(); ++idx) { if(drumkit.instruments[idx]->getName() == instrumentref.name) { instr_id = idx; found = true; break; } } if(!found) { // Missing instrument - skip continue; } } for(const auto& choke : instrumentref.chokes) { std::size_t choke_instr_id{0}; bool choke_found{false}; for(std::size_t idx = 0; idx < drumkit.instruments.size(); ++idx) { if(drumkit.instruments[idx]->getName() == choke.instrument) { choke_instr_id = idx; choke_found = true; break; } } if(!choke_found) { // Missing choke target instrument - skip continue; } drumkit.instruments[instr_id]->chokes.emplace_back(); drumkit.instruments[instr_id]->chokes.back().instrument_id = choke_instr_id; drumkit.instruments[instr_id]->chokes.back().choketime = choke.choketime; } } return true; } InstrumentChannel* DOMLoader::addOrGetChannel(Instrument& instrument, const std::string& name) { for(auto& channel : instrument.instrument_channels) { if(channel.name == name) { return &channel; } } instrument.instrument_channels.emplace_back(name); InstrumentChannel& channel = instrument.instrument_channels.back(); channel.main = main_state_t::unset; return &channel; }