diff options
-rw-r--r-- | drumgizmo/drumgizmoc.cc | 12 | ||||
-rw-r--r-- | plugin/Makefile.mingw32.in | 1 | ||||
-rw-r--r-- | src/DGDOM.h | 1 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/audioinputenginemidi.cc | 56 | ||||
-rw-r--r-- | src/audioinputenginemidi.h | 2 | ||||
-rw-r--r-- | src/dgxmlparser.cc | 16 | ||||
-rw-r--r-- | src/domloader.cc | 1 | ||||
-rw-r--r-- | src/event.h | 9 | ||||
-rw-r--r-- | src/inputprocessor.cc | 5 | ||||
-rw-r--r-- | src/instrument.cc | 5 | ||||
-rw-r--r-- | src/instrument.h | 3 | ||||
-rw-r--r-- | src/positionfilter.cc | 48 | ||||
-rw-r--r-- | src/positionfilter.h | 52 | ||||
-rw-r--r-- | src/sample.cc | 9 | ||||
-rw-r--r-- | src/sample.h | 5 | ||||
-rw-r--r-- | src/sample_selection.cc | 17 | ||||
-rw-r--r-- | src/sample_selection.h | 2 | ||||
-rw-r--r-- | src/settings.h | 12 |
19 files changed, 226 insertions, 32 deletions
diff --git a/drumgizmo/drumgizmoc.cc b/drumgizmo/drumgizmoc.cc index 8eba4c9..b906c8a 100644 --- a/drumgizmo/drumgizmoc.cc +++ b/drumgizmo/drumgizmoc.cc @@ -139,6 +139,8 @@ static std::string arguments() "Sample selection parameters:\n" " close: The importance given to choosing a sample close to\n" " the actual velocity value (after humanization). [0,1]\n" + " position: The importance given to choosing a sample close to\n" + " the actual position value (after humanization). [0,1]\n" " diverse: The importance given to choosing samples\n" " which haven't been played recently. [0,1]\n" " random: The amount of randomness added. [0,1]\n" @@ -617,6 +619,16 @@ int main(int argc, char* argv[]) } settings.sample_selection_f_close.store(val); } + else if(token.key == "position") + { + auto val = atof_nol(token.value.data()); + if(val < 0 || val > 1) + { + std::cerr << "position range is [0, 1].\n"; + return 1; + } + settings.sample_selection_f_position.store(val); + } else if(token.key == "diverse") { auto val = atof_nol(token.value.data()); diff --git a/plugin/Makefile.mingw32.in b/plugin/Makefile.mingw32.in index ad47bcc..1b8c848 100644 --- a/plugin/Makefile.mingw32.in +++ b/plugin/Makefile.mingw32.in @@ -49,6 +49,7 @@ DG_SRC = \ @top_srcdir@/src/thread.cc \ @top_srcdir@/src/translation.cc \ @top_srcdir@/src/velocityfilter.cc \ + @top_srcdir@/src/positionfilter.cc \ @top_srcdir@/src/versionstr.cc DG_CFLAGS = -I@top_srcdir@ -I@top_srcdir@/src \ -I@top_srcdir@/zita-resampler/libs \ diff --git a/src/DGDOM.h b/src/DGDOM.h index 474b29c..a03f0ef 100644 --- a/src/DGDOM.h +++ b/src/DGDOM.h @@ -59,6 +59,7 @@ struct SampleDOM { std::string name; double power; // >= v2.0 only + double position; // >=v2.0 only bool normalized; // >= v2.0 only std::vector<AudioFileDOM> audiofiles; }; diff --git a/src/Makefile.am b/src/Makefile.am index b656f48..3e691b1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -75,6 +75,7 @@ libdg_la_SOURCES = \ staminafilter.cc \ thread.cc \ velocityfilter.cc \ + positionfilter.cc \ versionstr.cc EXTRA_DIST = \ @@ -138,5 +139,6 @@ EXTRA_DIST = \ thread.h \ translation.h \ velocityfilter.h \ + positionfilter.h \ versionstr.h \ zrwrapper.h diff --git a/src/audioinputenginemidi.cc b/src/audioinputenginemidi.cc index ce3ce18..f6fedd6 100644 --- a/src/audioinputenginemidi.cc +++ b/src/audioinputenginemidi.cc @@ -100,9 +100,13 @@ bool AudioInputEngineMidi::isValid() const static const std::uint8_t NoteOff = 0x80; static const std::uint8_t NoteOn = 0x90; static const std::uint8_t NoteAftertouch = 0xA0; +static const std::uint8_t ControlChange = 0xB0; // Note type mask: -static int const NoteMask = 0xF0; +static const std::uint8_t TypeMask = 0xF0; + +// See: +// https://www.midi.org/specifications-old/item/table-1-summary-of-midi-message void AudioInputEngineMidi::processNote(const std::uint8_t* midi_buffer, std::size_t midi_buffer_length, @@ -114,35 +118,59 @@ void AudioInputEngineMidi::processNote(const std::uint8_t* midi_buffer, return; } - auto key = midi_buffer[1]; - auto velocity = midi_buffer[2]; - auto instrument_idx = mmap.lookup(key); - - switch(midi_buffer[0] & NoteMask) + switch(midi_buffer[0] & TypeMask) { case NoteOff: // Ignore for now break; case NoteOn: - if(velocity != 0 && instrument_idx != -1) { - // maps velocities to [.5/127, 126.5/127] - auto centered_velocity = (velocity-.5f)/127.0f; - events.push_back({EventType::OnSet, (std::size_t)instrument_idx, - offset, centered_velocity}); + auto key = midi_buffer[1]; + auto velocity = midi_buffer[2]; + auto instrument_idx = mmap.lookup(key); + if(velocity != 0 && instrument_idx != -1) + { + // maps velocities to [.5/127, 126.5/127] + auto centered_velocity = (velocity-.5f)/127.0f; + events.push_back({ EventType::OnSet, (std::size_t)instrument_idx, + offset, centered_velocity, positional_information }); + } } break; case NoteAftertouch: - if(velocity == 0 && instrument_idx != -1) { - events.push_back({EventType::Choke, (std::size_t)instrument_idx, - offset, .0f}); + auto key = midi_buffer[1]; + auto velocity = midi_buffer[2]; + auto instrument_idx = mmap.lookup(key); + if(velocity == 0 && instrument_idx != -1) + { + events.push_back({ EventType::Choke, (std::size_t)instrument_idx, + offset, .0f, .0f }); + } + } + break; + + case ControlChange: + { + auto controller_number = midi_buffer[1]; + auto value = midi_buffer[2]; + if(controller_number == 16) // positional information + { + // Store value for use in next NoteOn event. + positional_information = value / 127.0f; + + // Return here to prevent reset of cached positional information. + return; + } } break; default: break; } + + // Clear cached positional information. + positional_information = 0.0f; } diff --git a/src/audioinputenginemidi.h b/src/audioinputenginemidi.h index 8da7bd2..12efd66 100644 --- a/src/audioinputenginemidi.h +++ b/src/audioinputenginemidi.h @@ -70,4 +70,6 @@ private: bool is_valid; ConfigFile refs; + + float positional_information{0.0f}; }; diff --git a/src/dgxmlparser.cc b/src/dgxmlparser.cc index 0d3cdcd..bd9af66 100644 --- a/src/dgxmlparser.cc +++ b/src/dgxmlparser.cc @@ -358,9 +358,19 @@ bool parseInstrumentFile(const std::string& filename, InstrumentDOM& dom, LogFun } else { - res &= attrcpy(dom.samples.back().power, sample, "power", logger, filename); - dom.samples.back().normalized = false; - res &= attrcpy(dom.samples.back().normalized, sample, "normalized", logger, filename, true); + res &= attrcpy(dom.samples.back().power, sample, "power", + logger, filename); + + dom.samples.back().position = 0.0; // optional - defaults to 0 + res &= attrcpy(dom.samples.back().position, sample, "position", + logger, filename, true); + // Clamp to [0; 1] range. + dom.samples.back().position = + std::min(1.0, std::max(dom.samples.back().position, 0.0)); + + dom.samples.back().normalized = false; // optional - defaults to false + res &= attrcpy(dom.samples.back().normalized, sample, "normalized", + logger, filename, true); } for(pugi::xml_node audiofile: sample.children("audiofile")) diff --git a/src/domloader.cc b/src/domloader.cc index c78ed75..5d411bd 100644 --- a/src/domloader.cc +++ b/src/domloader.cc @@ -99,6 +99,7 @@ bool DOMLoader::loadDom(const std::string& basepath, for(const auto& sampledom : instrumentdom.samples) { auto sample = new Sample(sampledom.name, sampledom.power, + sampledom.position, sampledom.normalized); for(const auto& audiofiledom : sampledom.audiofiles) { diff --git a/src/event.h b/src/event.h index 737fb18..5780cc9 100644 --- a/src/event.h +++ b/src/event.h @@ -39,8 +39,9 @@ enum class EventType //! POD datatype for input event transport. struct event_t { - EventType type; //!< The type of the event. - std::size_t instrument; //!< The instrument number. - std::size_t offset; //!< The offset position in the input buffer - float velocity; //!< The velocity if the type is a note on [0; 1] + EventType type; //!< The type of the event. + std::size_t instrument; //!< The instrument number. + std::size_t offset; //!< The offset position in the input buffer + float velocity; //!< The velocity if the type is a note on [0; 1] + float position; //!< The position of the note. 0 := center, 1 := rim }; diff --git a/src/inputprocessor.cc b/src/inputprocessor.cc index 4bafb1d..a883286 100644 --- a/src/inputprocessor.cc +++ b/src/inputprocessor.cc @@ -36,6 +36,7 @@ #include "powermapfilter.h" #include "staminafilter.h" #include "velocityfilter.h" +#include "positionfilter.h" #include "cpp11fix.h" @@ -93,6 +94,7 @@ InputProcessor::InputProcessor(Settings& settings, filters.emplace_back(std::make_unique<StaminaFilter>(settings)); filters.emplace_back(std::make_unique<LatencyFilter>(settings, random)); filters.emplace_back(std::make_unique<VelocityFilter>(settings, random)); + filters.emplace_back(std::make_unique<PositionFilter>(settings, random)); filters.emplace_back(std::make_unique<Reporter>(settings, original_velocity)); } @@ -244,7 +246,8 @@ bool InputProcessor::processOnset(event_t& event, std::size_t pos, auto const power_min = instr->getMinPower(); float const power_span = power_max - power_min; float const instrument_level = power_min + event.velocity*power_span; - const auto sample = instr->sample(instrument_level, event.offset + pos); + // FIXME: bad variable naming of parameters + const auto sample = instr->sample(instrument_level, event.position, event.offset + pos); if(sample == nullptr) { diff --git a/src/instrument.cc b/src/instrument.cc index b7bcdd9..ac6aa28 100644 --- a/src/instrument.cc +++ b/src/instrument.cc @@ -54,12 +54,13 @@ bool Instrument::isValid() const return this == magic; } -const Sample* Instrument::sample(level_t level, size_t pos) +// FIXME: very bad variable naming of parameters +const Sample* Instrument::sample(level_t level, float position , std::size_t pos) { if(version >= VersionStr("2.0")) { // Version 2.0 - return sample_selection.get(level * mod, pos); + return sample_selection.get(level * mod, position, pos); } else { diff --git a/src/instrument.h b/src/instrument.h index c06ccdc..89918de 100644 --- a/src/instrument.h +++ b/src/instrument.h @@ -49,7 +49,8 @@ public: Instrument(Settings& settings, Random& rand); ~Instrument(); - const Sample* sample(level_t level, size_t pos); + // FIXME: variable naming + const Sample* sample(level_t level, float position, std::size_t pos); std::size_t getID() const; const std::string& getName() const; diff --git a/src/positionfilter.cc b/src/positionfilter.cc new file mode 100644 index 0000000..8d2f987 --- /dev/null +++ b/src/positionfilter.cc @@ -0,0 +1,48 @@ +/* -*- Mode: c++ -*- */ +/*************************************************************************** + * positionfilter.cc + * + * Sat 13 Feb 2021 12:46:41 CET + * Copyright 2019 André Nusser + * andre.nusser@googlemail.com + ****************************************************************************/ + +/* + * 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 "positionfilter.h" + +#include "random.h" +#include "settings.h" + +PositionFilter::PositionFilter(Settings& settings, Random& random) + : settings(settings), random(random) +{ +} + +bool PositionFilter::filter(event_t& event, size_t pos) +{ + if (settings.enable_velocity_modifier.load()) + { + float mean = event.position; + float stddev = settings.position_stddev.load(); + // the 30.0f were determined empirically + event.position = random.normalDistribution(mean, stddev / 30.0f); // FIXME: right magic value? + } + + return true; +} diff --git a/src/positionfilter.h b/src/positionfilter.h new file mode 100644 index 0000000..ac989f6 --- /dev/null +++ b/src/positionfilter.h @@ -0,0 +1,52 @@ +/* -*- Mode: c++ -*- */ +/*************************************************************************** + * positionfilter.h + * + * Sat 13 Feb 2021 12:46:41 CET + * Copyright 2019 André Nusser + * andre.nusser@googlemail.com + ****************************************************************************/ + +/* + * 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. + */ +#pragma once + +#include <cstddef> +#include <map> +#include <utility> + +#include "inputfilter.h" +#include "instrument.h" + +struct Settings; +class Random; + +class PositionFilter + : public InputFilter +{ +public: + PositionFilter(Settings& settings, Random& random); + + bool filter(event_t& event, std::size_t pos) override; + + // Note getLatency not overloaded because this filter doesn't add latency. + +private: + Settings& settings; + Random& random; +}; diff --git a/src/sample.cc b/src/sample.cc index 9af2c08..2c59f5e 100644 --- a/src/sample.cc +++ b/src/sample.cc @@ -28,9 +28,11 @@ #include <sndfile.h> -Sample::Sample(const std::string& name, double power, bool normalized) +Sample::Sample(const std::string& name, double power, double position, + bool normalized) : name{name} , power{power} + , position{position} , normalized(normalized) , audiofiles{} { @@ -69,6 +71,11 @@ double Sample::getPower() const return power; } +double Sample::getPosition() const +{ + return position; +} + bool Sample::getNormalized() const { return normalized; diff --git a/src/sample.h b/src/sample.h index 6c31b6b..b13f624 100644 --- a/src/sample.h +++ b/src/sample.h @@ -37,12 +37,14 @@ using AudioFiles = std::map<const InstrumentChannel*, AudioFile*>; class Sample { public: - Sample(const std::string& name, double power, bool normalized = false); + Sample(const std::string& name, double power, double position, + bool normalized = false); ~Sample(); AudioFile* getAudioFile(const Channel& channel) const; double getPower() const; + double getPosition() const; bool getNormalized() const; private: @@ -55,6 +57,7 @@ private: std::string name; double power; + double position{0.0}; bool normalized; AudioFiles audiofiles; }; diff --git a/src/sample_selection.cc b/src/sample_selection.cc index 7267faf..0ff2c51 100644 --- a/src/sample_selection.cc +++ b/src/sample_selection.cc @@ -54,7 +54,9 @@ void SampleSelection::finalise() last.assign(powerlist.getPowerListItems().size(), 0); } -const Sample* SampleSelection::get(level_t level, std::size_t pos) +// FIXME: For the position, weird hacks via the powerlist are necessary. Refactor! +// FIXME: bad variable naming +const Sample* SampleSelection::get(level_t level, float position, std::size_t pos) { const auto& samples = powerlist.getPowerListItems(); if(!samples.size()) @@ -64,14 +66,17 @@ const Sample* SampleSelection::get(level_t level, std::size_t pos) std::size_t index_opt = 0; float power_opt{0.f}; + float pos_opt{0.f}; float value_opt{std::numeric_limits<float>::max()}; // the following three values are mostly for debugging - float random_opt = 0.; float close_opt = 0.; + float closepos_opt = 0.; + float random_opt = 0.; float diverse_opt = 0.; // Note the magic values in front of the settings factors. const float f_close = 4.*settings.sample_selection_f_close.load(); + const float f_position = 1000.*settings.sample_selection_f_position.load(); // FIXME: huge factor for now const float f_diverse = (1./2.)*settings.sample_selection_f_diverse.load(); const float f_random = (1./3.)*settings.sample_selection_f_random.load(); @@ -137,22 +142,26 @@ const Sample* SampleSelection::get(level_t level, std::size_t pos) auto random = rand.floatInRange(0.,1.); auto close = (samples[current_index].power - level)/power_range; auto diverse = 1./(1. + (float)(pos - last[current_index])/settings.samplerate); - auto value = f_close*pow2(close) + f_diverse*diverse + f_random*random; + auto closepos = samples[current_index].sample->getPosition() - position; + // note that the value below for close and closepos is actually the weighted squared l2 distance in 2d + auto value = f_close*pow2(close) + f_position*pow2(closepos) + f_diverse*diverse + f_random*random; if (value < value_opt) { index_opt = current_index; power_opt = samples[current_index].power; + pos_opt = samples[current_index].sample->getPosition(); value_opt = value; random_opt = random; close_opt = close; diverse_opt = diverse; + closepos_opt = closepos; } ++count; } while (up_value_lb <= value_opt || down_value_lb <= value_opt); - DEBUG(rand, "Chose sample with index: %d, value: %f, power %f, random: %f, close: %f, diverse: %f, count: %d", (int)index_opt, value_opt, power_opt, random_opt, close_opt, diverse_opt, (int)count); + DEBUG(rand, "Chose sample with index: %d, value: %f, power: %f, position: %f, random: %f, close: %f, diverse: %f, closepos: %f, count: %d", (int)index_opt, value_opt, power_opt, pos_opt, random_opt, close_opt, diverse_opt, closepos_opt, (int)count); last[index_opt] = pos; return samples[index_opt].sample; diff --git a/src/sample_selection.h b/src/sample_selection.h index 8da4e0d..1f6b290 100644 --- a/src/sample_selection.h +++ b/src/sample_selection.h @@ -40,7 +40,7 @@ public: SampleSelection(Settings& settings, Random& rand, const PowerList& powerlist); void finalise(); - const Sample* get(level_t level, std::size_t pos); + const Sample* get(level_t level, float position, std::size_t pos); private: Settings& settings; diff --git a/src/settings.h b/src/settings.h index fb93d79..5c2e4ee 100644 --- a/src/settings.h +++ b/src/settings.h @@ -76,13 +76,17 @@ struct Settings static float constexpr velocity_modifier_falloff_default = 0.5f; static float constexpr velocity_modifier_weight_default = 0.25f; static float constexpr velocity_stddev_default = .45f; + static float constexpr position_stddev_default = 0.f; // FIXME: set to something sensible static float constexpr sample_selection_f_close_default = .85f; + static float constexpr sample_selection_f_position_default = 1.f; static float constexpr sample_selection_f_diverse_default = .16f; static float constexpr sample_selection_f_random_default = .07f; Atomic<float> velocity_modifier_falloff{velocity_modifier_falloff_default}; Atomic<float> velocity_modifier_weight{velocity_modifier_weight_default}; Atomic<float> velocity_stddev{velocity_stddev_default}; + Atomic<float> position_stddev{position_stddev_default}; Atomic<float> sample_selection_f_close{sample_selection_f_close_default}; + Atomic<float> sample_selection_f_position{sample_selection_f_position_default}; Atomic<float> sample_selection_f_diverse{sample_selection_f_diverse_default}; Atomic<float> sample_selection_f_random{sample_selection_f_random_default}; @@ -200,7 +204,9 @@ struct SettingsGetter SettingRef<float> velocity_modifier_falloff; SettingRef<float> velocity_modifier_weight; SettingRef<float> velocity_stddev; + SettingRef<float> position_stddev; SettingRef<float> sample_selection_f_close; + SettingRef<float> sample_selection_f_position; SettingRef<float> sample_selection_f_diverse; SettingRef<float> sample_selection_f_random; @@ -275,7 +281,9 @@ struct SettingsGetter , velocity_modifier_falloff{settings.velocity_modifier_falloff} , velocity_modifier_weight{settings.velocity_modifier_weight} , velocity_stddev{settings.velocity_stddev} + , position_stddev{settings.position_stddev} , sample_selection_f_close{settings.sample_selection_f_close} + , sample_selection_f_position{settings.sample_selection_f_position} , sample_selection_f_diverse{settings.sample_selection_f_diverse} , sample_selection_f_random{settings.sample_selection_f_random} , sample_selection_retry_count(settings.sample_selection_retry_count) @@ -345,7 +353,9 @@ public: Notifier<float> velocity_modifier_falloff; Notifier<float> velocity_modifier_weight; Notifier<float> velocity_stddev; + Notifier<float> position_stddev; Notifier<float> sample_selection_f_close; + Notifier<float> sample_selection_f_position; Notifier<float> sample_selection_f_diverse; Notifier<float> sample_selection_f_random; Notifier<std::size_t> sample_selection_retry_count; @@ -424,7 +434,9 @@ public: EVAL(velocity_modifier_falloff); EVAL(velocity_modifier_weight); EVAL(velocity_stddev); + EVAL(position_stddev); EVAL(sample_selection_f_close); + EVAL(sample_selection_f_position); EVAL(sample_selection_f_diverse); EVAL(sample_selection_f_random); EVAL(sample_selection_retry_count); |