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 1195703..06e97c9 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(); @@ -134,22 +139,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);  | 
