diff options
-rwxr-xr-x | autogen.sh | 1 | ||||
-rw-r--r-- | drumgizmo/input/jackmidi.cc | 8 | ||||
-rw-r--r-- | src/DGDOM.h | 1 | ||||
-rw-r--r-- | src/audioinputenginemidi.cc | 58 | ||||
-rw-r--r-- | src/audioinputenginemidi.h | 1 | ||||
-rw-r--r-- | src/dgxmlparser.cc | 2 | ||||
-rw-r--r-- | src/domloader.cc | 1 | ||||
-rw-r--r-- | src/drumkit.cc | 5 | ||||
-rw-r--r-- | src/drumkit.h | 2 | ||||
-rw-r--r-- | src/drumkitloader.cc | 2 | ||||
-rw-r--r-- | src/inputprocessor.cc | 31 | ||||
-rw-r--r-- | src/midimapparser.cc | 6 | ||||
-rw-r--r-- | src/midimapparser.h | 1 | ||||
-rw-r--r-- | src/midimapper.cc | 42 | ||||
-rw-r--r-- | src/midimapper.h | 15 | ||||
-rw-r--r-- | src/settings.h | 3 |
16 files changed, 122 insertions, 57 deletions
@@ -16,6 +16,7 @@ mkdir -p actest cat << EOF > actest/configure.ac AC_INIT([actest], [1.0.0]) AC_PROG_OBJCXX +AC_OUTPUT([]) EOF [ -f acinclude.m4 ] && rm acinclude.m4 autoreconf -W error actest 2>/dev/null || echo "AC_DEFUN([AC_PROG_OBJCXX],[echo ' - ObjC++ hack - not support by this platform, but not needed either.'])" > acinclude.m4 diff --git a/drumgizmo/input/jackmidi.cc b/drumgizmo/input/jackmidi.cc index 445678b..39aebf8 100644 --- a/drumgizmo/input/jackmidi.cc +++ b/drumgizmo/input/jackmidi.cc @@ -122,6 +122,12 @@ void JackMidiInputEngine::process(jack_nframes_t num_frames) jack_midi_event_get(&event, buffer, i); processNote(event.buffer, event.size, event.time, events); } - jack_midi_clear_buffer(buffer); + + // Disable clear buffer on input port because for Edrumulus usage we need that + // two clients use the some output buffer and if we clear it here, the other + // application will not get any MIDI input signal. Usually, the output port + // is responsible for clearing the buffer, not the input port. + //jack_midi_clear_buffer(buffer); + pos += num_frames; } diff --git a/src/DGDOM.h b/src/DGDOM.h index a03f0ef..e39f445 100644 --- a/src/DGDOM.h +++ b/src/DGDOM.h @@ -139,6 +139,7 @@ struct DrumkitDOM { std::string version; double samplerate; + bool islogpower; MetadataDOM metadata; diff --git a/src/audioinputenginemidi.cc b/src/audioinputenginemidi.cc index 4f8ff1f..564a5d6 100644 --- a/src/audioinputenginemidi.cc +++ b/src/audioinputenginemidi.cc @@ -78,7 +78,7 @@ bool AudioInputEngineMidi::loadMidiMap(const std::string& file, instrmap[instruments[i]->getName()] = i; } - mmap.swap(instrmap, midimap_parser.midimap); + mmap.swap(instrmap, midimap_parser.midimap, midimap_parser.controlthreshmap); midimap = file; is_valid = true; @@ -108,11 +108,6 @@ static const std::uint8_t TypeMask = 0xF0; // See: // https://www.midi.org/specifications-old/item/table-1-summary-of-midi-message - -// TODO better implementation: use member variable for controller value, set MIDI key on command line -int hihat_controller = 0; // open hi-hat -int hihat_midi_key = 26; - void AudioInputEngineMidi::processNote(const std::uint8_t* midi_buffer, std::size_t midi_buffer_length, std::size_t offset, @@ -123,24 +118,6 @@ void AudioInputEngineMidi::processNote(const std::uint8_t* midi_buffer, return; } -const bool use_edrumulus_debugging_output = false; -if(use_edrumulus_debugging_output) -{ - auto type = midi_buffer[0] & TypeMask; - auto key = midi_buffer[1]; - auto velocity = midi_buffer[2]; - if((type == ControlChange) && (key == 16)) - { - std::string bar = "--------------------"; - bar[static_cast<int>(static_cast<float>(velocity) / 128 * 20)] = '*'; - printf(std::string(" " + bar + "\n").c_str()); - } - else - { - printf("key: %d, velocity: %d\n", key, velocity); - } -} - switch(midi_buffer[0] & TypeMask) { case NoteOff: @@ -151,7 +128,7 @@ if(use_edrumulus_debugging_output) { auto key = midi_buffer[1]; auto velocity = midi_buffer[2]; - auto instrument_idx = mmap.lookup(key); + auto instrument_idx = mmap.lookup(key, controller4_value); if(velocity != 0 && instrument_idx != -1) { // maps velocities to [.5/127, 126.5/127] @@ -159,16 +136,21 @@ if(use_edrumulus_debugging_output) events.push_back({ EventType::OnSet, (std::size_t)instrument_idx, offset, centered_velocity, positional_information }); + +/* +// TODO better implementation: use member variable for controller value, set MIDI key on command line +int hihat_midi_key = 26; // quick hack, add 1000000 to offset to transport hi-hat controller value auto instrument_idx1 = mmap.lookup(hihat_midi_key); if(instrument_idx == instrument_idx1) { - if(hihat_controller < 100) // quick hack: hard-coded value + if(controller4_value < 100) // quick hack: hard-coded value { events.push_back({ EventType::Choke, (std::size_t)instrument_idx, - 1000000 + hihat_controller, .0f, .0f }); + 1000000 + controller4_value, .0f, .0f }); } } +*/ } } @@ -200,18 +182,20 @@ if(instrument_idx == instrument_idx1) return; } - if(controller_number == 4) // hi-hat pedal + if(controller_number == 4) // usually, controller 4 is the hi-hat controller { + // in case the hi-hat was just closed, choke current hi-hat samples + if(controller4_value < mmap.getMaxControlthresh() && value >= mmap.getMaxControlthresh()) + { + for(auto instrument_idx : mmap.getInstWithControlthresh()) + { + events.push_back({ EventType::Choke, (std::size_t)instrument_idx, + offset, .0f, .0f }); + } + } -// quick hack: if hi-hat control pedal is down, choke hi-hat instrument -auto instrument_idx = mmap.lookup(hihat_midi_key); -hihat_controller = value; -if(value > 100 && instrument_idx != -1) // quick hack: hard-coded value -{ - events.push_back({ EventType::Choke, (std::size_t)instrument_idx, - offset, .0f, .0f }); -} - + // Store value for use in next NoteOn event. + controller4_value = value; } } break; diff --git a/src/audioinputenginemidi.h b/src/audioinputenginemidi.h index 12efd66..7656f8d 100644 --- a/src/audioinputenginemidi.h +++ b/src/audioinputenginemidi.h @@ -72,4 +72,5 @@ private: ConfigFile refs; float positional_information{0.0f}; + int controller4_value{0}; }; diff --git a/src/dgxmlparser.cc b/src/dgxmlparser.cc index bd9af66..b3aba75 100644 --- a/src/dgxmlparser.cc +++ b/src/dgxmlparser.cc @@ -209,6 +209,8 @@ bool parseDrumkitFile(const std::string& filename, DrumkitDOM& dom, LogFunction res &= attrcpy(dom.version, drumkit, "version", logger, filename, true); dom.samplerate = 44100.0; res &= attrcpy(dom.samplerate, drumkit, "samplerate", logger, filename, true); + dom.islogpower = false; + res &= attrcpy(dom.islogpower, drumkit, "islogpower", logger, filename, true); // Use the old name and description attributes on the drumkit node as fallback res &= attrcpy(dom.metadata.title, drumkit, "name", logger, filename, true); diff --git a/src/domloader.cc b/src/domloader.cc index 5d411bd..d422c9a 100644 --- a/src/domloader.cc +++ b/src/domloader.cc @@ -60,6 +60,7 @@ bool DOMLoader::loadDom(const std::string& basepath, drumkit.metadata._version = dom.version; drumkit.metadata._description = dom.metadata.description; drumkit.metadata._samplerate = dom.samplerate; + drumkit.metadata._islogpower = dom.islogpower; for(const auto& channel: dom.channels) { diff --git a/src/drumkit.cc b/src/drumkit.cc index 9df9a33..2fc16db 100644 --- a/src/drumkit.cc +++ b/src/drumkit.cc @@ -55,6 +55,11 @@ bool DrumKit::isValid() const return this == magic; } +bool DrumKit::isLogPower() const +{ + return metadata._islogpower; +} + std::string DrumKit::getFile() const { return _file; diff --git a/src/drumkit.h b/src/drumkit.h index aee5d19..3f2677f 100644 --- a/src/drumkit.h +++ b/src/drumkit.h @@ -51,6 +51,7 @@ public: void clear(); bool isValid() const; + bool isLogPower() const; float getSamplerate() const; @@ -74,5 +75,6 @@ private: std::string _description; float _samplerate{44100.0f}; VersionStr _version; + bool _islogpower{false}; } metadata; }; diff --git a/src/drumkitloader.cc b/src/drumkitloader.cc index 9167201..91a235f 100644 --- a/src/drumkitloader.cc +++ b/src/drumkitloader.cc @@ -136,6 +136,7 @@ bool DrumKitLoader::loadkit(const std::string& file) settings.drumkit_description.store(""); settings.drumkit_version.store(""); settings.drumkit_samplerate.store(44100); + settings.drumkit_is_log_power.store(false); settings.load_status_text.store(""); settings.drumkit_load_status.store(LoadStatus::Parsing); @@ -219,6 +220,7 @@ bool DrumKitLoader::loadkit(const std::string& file) settings.drumkit_description = kit.getDescription(); settings.drumkit_version = kit.getVersion(); settings.drumkit_samplerate = kit.getSamplerate(); + settings.drumkit_is_log_power = kit.isLogPower(); // only load the default midi map if there is one and no midimap is selected yet if (drumkitdom.metadata.default_midimap_file != "" && settings.midimap_file == "") { diff --git a/src/inputprocessor.cc b/src/inputprocessor.cc index 4b0a1bb..6d50c68 100644 --- a/src/inputprocessor.cc +++ b/src/inputprocessor.cc @@ -210,6 +210,16 @@ bool InputProcessor::processOnset(event_t& event, std::size_t pos, return false; } + // In case log-power and dynamic expander is used, apply a special velocity curve + if(settings.drumkit_is_log_power) + { + auto a = 0.981; // compatible to Roland sound modules + event.velocity = (126.0f / (pow(a, 126.0f) - 1.0f) * (pow(a, event.velocity * 127.0f - 1.0f) - 1.0f) + 1.0f) / 127.0f; + } + +// TODO velocity 1 does not select thehighest amplitude samples... -> maybe bug in sample selection +//event.velocity=1.0; + std::size_t instrument_id = event.instrument; Instrument* instr = nullptr; @@ -291,19 +301,12 @@ bool InputProcessor::processOnset(event_t& event, std::size_t pos, event_sample.scale *= event.velocity; } -// TEST dynamic expander -//printf("event_sample.scale: %f, event.velocity: %f, sample->getPower(): %f, instr->getMinPower(): %f, instr->getMaxPower(): %f\n", event_sample.scale, event.velocity, sample->getPower(), instr->getMinPower(), instr->getMaxPower()); - -auto target_max_power_db = 10 * log10(instr->getMaxPower()); -auto sel_power_db = 10 * log10(sample->getPower()); -auto target_dynamic_db = 40.0; // dB - -auto target_power_db = sqrt(event.velocity) * target_dynamic_db + target_max_power_db - target_dynamic_db; -auto diff_power_db = target_power_db - sel_power_db; -event_sample.scale = pow(10.0, diff_power_db / 20); - -//printf("event_sample.scale: %f, diff_power_db: %f, target_power_db: %f, sel_power_db: %f\n", event_sample.scale, diff_power_db, target_power_db, sel_power_db); - + // Dynamic expander (only enabled if log-power is configured) + if(settings.drumkit_is_log_power) + { + auto target_dynamic_db = 50.0; // dB, defined maximum dynamic after expander + event_sample.scale = pow(10.0, (event.velocity * target_dynamic_db + instr->getMaxPower() - target_dynamic_db - sample->getPower()) / 20); + } } } @@ -358,7 +361,7 @@ bool InputProcessor::processChoke(event_t& event, if(event.offset >= 1000000) // quick hack: added 1000000 to offset to transport hi-hat controller value { int hihat_controller = event.offset - 1000000; - int rampdown_time = static_cast<int>(std::max(100.0, pow(10.0, (127.0 - hihat_controller) / 38.0))); // TODO optimize + int rampdown_time = static_cast<int>(std::max(180.0, pow(10.0, (127.0 - hihat_controller) / 38.0))); // TODO optimize applyChoke(settings, event_sample, rampdown_time, 0); } else diff --git a/src/midimapparser.cc b/src/midimapparser.cc index 059dfec..2361199 100644 --- a/src/midimapparser.cc +++ b/src/midimapparser.cc @@ -45,11 +45,17 @@ bool MidiMapParser::parseFile(const std::string& filename) constexpr int bad_value = 10000; auto note = map_node.attribute("note").as_int(bad_value); auto instr = map_node.attribute("instr").as_string(); + auto controlthresh = map_node.attribute("controlthresh").as_int(bad_value); if(std::string(instr) == "" || note == bad_value) { continue; } + if(controlthresh != bad_value) + { + controlthreshmap[note][instr] = controlthresh; + } + midimap[note] = instr; } diff --git a/src/midimapparser.h b/src/midimapparser.h index d2f2ddd..d13b4f5 100644 --- a/src/midimapparser.h +++ b/src/midimapparser.h @@ -35,4 +35,5 @@ public: bool parseFile(const std::string& filename); midimap_t midimap; + controlthreshmap_t controlthreshmap; }; diff --git a/src/midimapper.cc b/src/midimapper.cc index 9593aae..1f75461 100644 --- a/src/midimapper.cc +++ b/src/midimapper.cc @@ -26,7 +26,7 @@ */ #include "midimapper.h" -int MidiMapper::lookup(int note) +int MidiMapper::lookup(int note, int controller) { std::lock_guard<std::mutex> guard(mutex); @@ -42,15 +42,53 @@ int MidiMapper::lookup(int note) return -1; } + if(controller >= 0 && !controlthreshmap[note].empty()) + { + // find instrument where controller is above threshold with smallest distance to threshold + int diff = 10000; + std::string instr = controlthreshmap[note].begin()->first; + for(auto& control_thresh : controlthreshmap[note]) + { + int cur_diff = controller - control_thresh.second; + if(cur_diff >= 0 && cur_diff < diff) + { + diff = cur_diff; + instr = control_thresh.first; + } + } + instrmap_it = instrmap.find(instr); + } + return instrmap_it->second; } -void MidiMapper::swap(instrmap_t& instrmap, midimap_t& midimap) +void MidiMapper::swap(instrmap_t& instrmap, midimap_t& midimap, controlthreshmap_t& controlthreshmap) { std::lock_guard<std::mutex> guard(mutex); std::swap(this->instrmap, instrmap); std::swap(this->midimap, midimap); + std::swap(this->controlthreshmap, controlthreshmap); + + // find instruments which define a control threshold and store it + for(auto& control_thresh : this->controlthreshmap) + { + for(auto& instr : control_thresh.second) + { + auto instrmap_it = this->instrmap.find(instr.first); + if(instrmap_it != this->instrmap.end()) + { + instwithcontrolthresh.push_back(instrmap_it->second); + } + maxcontrolthresh = std::max(maxcontrolthresh, instr.second); + } + } + + // in case no controller threshold is defined, use fix definition + if(maxcontrolthresh == 0) + { + maxcontrolthresh = 100; + } } const midimap_t& MidiMapper::getMap() diff --git a/src/midimapper.h b/src/midimapper.h index 4673e33..adb5777 100644 --- a/src/midimapper.h +++ b/src/midimapper.h @@ -29,25 +29,36 @@ #include <map> #include <string> #include <mutex> +#include <vector> typedef std::map<int, std::string> midimap_t; typedef std::map<std::string, int> instrmap_t; +typedef std::map<int, std::map<std::string, int> > controlthreshmap_t; class MidiMapper { public: //! Lookup note in map and return its index. //! \returns -1 if not found or the note index. - int lookup(int note); + int lookup(int note, int controller = -1); + + //! Get all instruments with controller thresholds defined. + std::vector<int>& getInstWithControlthresh() { return instwithcontrolthresh; } + + //! Get the maximum configured control threshold + int getMaxControlthresh() { return maxcontrolthresh; } //! Set new map sets. - void swap(instrmap_t& instrmap, midimap_t& midimap); + void swap(instrmap_t& instrmap, midimap_t& midimap, controlthreshmap_t& controlthreshmap); const midimap_t& getMap(); private: instrmap_t instrmap; midimap_t midimap; + controlthreshmap_t controlthreshmap; + std::vector<int> instwithcontrolthresh; + int maxcontrolthresh{0}; std::mutex mutex; }; diff --git a/src/settings.h b/src/settings.h index 5c2e4ee..f2d656e 100644 --- a/src/settings.h +++ b/src/settings.h @@ -3,7 +3,7 @@ * settings.h * * Tue Mar 22 10:59:46 CET 2016 - * Copyright 2016 Christian Glöckner + * Copyright 2016 Christian Gl�ckner * cgloeckner@freenet.de ****************************************************************************/ @@ -53,6 +53,7 @@ struct Settings Atomic<std::string> drumkit_description{""}; Atomic<std::string> drumkit_version{""}; Atomic<std::size_t> drumkit_samplerate{44100}; + Atomic<bool> drumkit_is_log_power{false}; //! The maximum amount of memory in bytes that the AudioCache //! is allowed to use for preloading. Default is 1GB. |