diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.am | 1 | ||||
| -rw-r--r-- | src/instrument.cc | 5 | ||||
| -rw-r--r-- | src/instrument.h | 3 | ||||
| -rw-r--r-- | src/powerlist.cc | 104 | ||||
| -rw-r--r-- | src/powerlist.h | 26 | ||||
| -rw-r--r-- | src/sample_selection.cc | 226 | ||||
| -rw-r--r-- | src/sample_selection.h | 63 | 
7 files changed, 309 insertions, 119 deletions
| diff --git a/src/Makefile.am b/src/Makefile.am index 7d07719..3da5d0f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -37,6 +37,7 @@ nodist_libdg_la_SOURCES = \  	powerlist.cc \  	random.cc \  	sample.cc \ +	sample_selection.cc \  	semaphore.cc \  	staminafilter.cc \  	thread.cc \ diff --git a/src/instrument.cc b/src/instrument.cc index ffc928d..b7bcdd9 100644 --- a/src/instrument.cc +++ b/src/instrument.cc @@ -33,7 +33,7 @@  Instrument::Instrument(Settings& settings, Random& rand)  	: settings(settings)  	, rand(rand) -	, powerlist(rand, settings) +	, sample_selection(settings, rand, powerlist)  {  	DEBUG(instrument, "new %p\n", this);  	mod = 1.0; @@ -59,7 +59,7 @@ const Sample* Instrument::sample(level_t level, size_t pos)  	if(version >= VersionStr("2.0"))  	{  		// Version 2.0 -		return powerlist.get(level * mod, pos); +		return sample_selection.get(level * mod, pos);  	}  	else  	{ @@ -91,6 +91,7 @@ void Instrument::finalise()  		}  		powerlist.finalise(); +		sample_selection.finalise();  	}  } diff --git a/src/instrument.h b/src/instrument.h index e4cf481..2cb0813 100644 --- a/src/instrument.h +++ b/src/instrument.h @@ -33,6 +33,7 @@  #include "rangemap.h" // for v1.0 kits  #include "powerlist.h" +#include "sample_selection.h"  #include "sample.h"  #include "versionstr.h" @@ -104,8 +105,8 @@ private:  	Settings& settings;  	Random& rand;  	PowerList powerlist; -  	std::vector<Choke> chokes; +	SampleSelection sample_selection;  };  // typedef std::map< std::string, Instrument > Instruments; diff --git a/src/powerlist.cc b/src/powerlist.cc index e593f7e..87d6e9f 100644 --- a/src/powerlist.cc +++ b/src/powerlist.cc @@ -27,7 +27,6 @@  #include "powerlist.h"  #include <stdlib.h> -  #include <string.h>  #include <hugin.hpp> @@ -41,35 +40,19 @@  #include "random.h"  #include "settings.h" -/** - * Minimum sample set size. - * Smaller means wider 'velocity groups'. - * Limited by sample set size, ie. only kicks in if sample set size is smaller - * than this number. - */ -std::size_t const MIN_SAMPLE_SET_SIZE = 26u; +namespace +{  // Enable to calculate power on old samples without power attribute  //#define AUTO_CALCULATE_POWER  unsigned int const LOAD_SIZE = 500u; -namespace -{ - -float pow2(float f) -{ -	return f*f; -} -  } // end anonymous namespace -PowerList::PowerList(Random& rand, Settings& settings) -	: rand(rand) -	, settings(settings) +PowerList::PowerList()  {  	power_max = 0;  	power_min = 100000000; -	lastsample = nullptr;  }  void PowerList::add(Sample* sample) @@ -214,88 +197,11 @@ void PowerList::finalise()  		DEBUG(rand, " - power: %f\n", item.power);  	} - -	last.resize(samples.size(), 0);  } -Sample* PowerList::get(level_t level, std::size_t pos) +const PowerListItems& PowerList::getPowerListItems() const  { -	auto velocity_stddev = settings.velocity_stddev.load(); - -	if(!samples.size()) -	{ -		return nullptr; // No samples to choose from. -	} - -	float power_span = power_max - power_min; - -	// Width is limited to at least 10. Fixes problem with instrument with a -	// sample set smaller than MIN_SAMPLE_SET_SIZE. -	float width = std::max(samples.size(), MIN_SAMPLE_SET_SIZE); - -	// Spread out at most ~2 samples away from center if all samples have a -	// uniform distribution over the power spectrum (which they probably don't). -	float mean_stepwidth = power_span / width; - -	// Cut off mean value with stddev/2 in both ends in order to make room for -	// downwards expansion on velocity 0 and upwards expansion on velocity 1. -	float mean = level * (power_span - mean_stepwidth) + (mean_stepwidth / 2.0); -	float stddev = settings.enable_velocity_modifier.load() ? velocity_stddev * mean_stepwidth : 0.; - -	std::size_t index_opt = 0; -	float power_opt{0.f}; -	float value_opt{std::numeric_limits<float>::max()}; -	// TODO: those are mostly for debugging at the moment -	float random_opt = 0.; -	float distance_opt = 0.; -	float recent_opt = 0.; - -	// Select normal distributed value between -	// (stddev/2) and (power_span-stddev/2) -	float lvl = rand.normalDistribution(mean, stddev); - -	// Adjust this value to be in range -	// (power_min+stddev/2) and (power_max-stddev/2) -	lvl += power_min; - -	DEBUG(rand, "level: %f, lvl: %f (mean: %.2f, stddev: %.2f, mean_stepwidth: %f," -		"power_min: %f, power_max: %f)\n", level, lvl, mean, stddev, mean_stepwidth, -		power_min, power_max); - -	// TODO: expose parameters to GUI -	float alpha = 1.0; -	float beta = 1.0; -	float gamma = .5; - -	// TODO: start with most promising power value and then stop when reaching far values -	// which cannot become opt anymore -	for (std::size_t i = 0; i < samples.size(); ++i) -	{ -		auto const& item = samples[i]; - -		// compute objective function value -		auto random = rand.floatInRange(0.,1.); -		auto distance = item.power - lvl; -		auto recent = (float)settings.samplerate/std::max<std::size_t>(pos - last[i], 1); -		auto value = alpha*pow2(distance) + beta*pow2(recent) + gamma*random; - -		if (value < value_opt) -		{ -			index_opt = i; -			power_opt = item.power; -			value_opt = value; -			random_opt = random; -			distance_opt = distance; -			recent_opt = recent; -		} -	} - -	DEBUG(rand, "Chose sample with index: %d, value: %f, power %f, random: %f, distance: %f, recent: %f", (int)index_opt, value_opt, power_opt, random_opt, distance_opt, recent_opt); - -	lastsample = samples[index_opt].sample; -	last[index_opt] = pos; - -	return samples[index_opt].sample; +	return samples;  }  float PowerList::getMaxPower() const diff --git a/src/powerlist.h b/src/powerlist.h index b3b47e4..000da15 100644 --- a/src/powerlist.h +++ b/src/powerlist.h @@ -30,38 +30,30 @@  #include "sample.h" -class Random; -struct Settings; +struct PowerListItem +{ +	Sample* sample; +	float power; +}; +using PowerListItems = std::vector<PowerListItem>;  class PowerList  {  public: -	PowerList(Random& rand, Settings& settings); +	PowerList();  	void add(Sample* s);  	void finalise(); ///< Call this when no more samples will be added. -	Sample* get(level_t velocity, std::size_t pos); +	const PowerListItems& getPowerListItems() const;  	float getMaxPower() const;  	float getMinPower() const;  private: -	struct PowerListItem -	{ -		Sample* sample; -		float power; -	}; - -	Random& rand; -	Settings& settings; - -	std::vector<PowerListItem> samples; +	PowerListItems samples;  	float power_max;  	float power_min;  	const Channel* getMasterChannel(); -	Sample* lastsample; - -	std::vector<std::size_t> last;  }; diff --git a/src/sample_selection.cc b/src/sample_selection.cc new file mode 100644 index 0000000..8000879 --- /dev/null +++ b/src/sample_selection.cc @@ -0,0 +1,226 @@ +/* -*- Mode: c++ -*- */ +/*************************************************************************** + *            sample_selection.h + * + *  Mon Mar  4 23:58:12 CET 2019 + *  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 "sample_selection.h" + +#include <hugin.hpp> + +#include "powerlist.h" +#include "random.h" +#include "settings.h" + +namespace +{ + +// Minimum sample set size. +// Smaller means wider 'velocity groups'. +// Limited by sample set size, ie. only kicks in if sample set size is smaller +// than this number. +std::size_t const MIN_SAMPLE_SET_SIZE = 26u; + +float pow2(float f) +{ +	return f*f; +} + +} // end anonymous namespace + +SampleSelection::SampleSelection(Settings& settings, Random& rand, const PowerList& powerlist) +	: settings(settings), rand(rand), powerlist(powerlist) +{ +} + +void SampleSelection::setSelectionAlg(SelectionAlg alg) +{ +	this->alg = alg; +} + +void SampleSelection::finalise() +{ +	last.assign(powerlist.getPowerListItems().size(), 0); +} + +const Sample* SampleSelection::get(level_t level, std::size_t pos) +{ +	// TODO: switch objective to default at some point +	switch (alg) +	{ +		case SelectionAlg::Objective: +			return getObjective(level, pos); +			break; +		case SelectionAlg::Old: +		default: +			return getOld(level, pos); +	} +} + +const Sample* SampleSelection::getOld(level_t level, std::size_t pos) +{ +	auto velocity_stddev = settings.velocity_stddev.load(); + +	const auto& samples = powerlist.getPowerListItems(); +	if(!samples.size()) +	{ +		return nullptr; // No samples to choose from. +	} + +	int retry = settings.sample_selection_retry_count.load(); + +	Sample* sample{nullptr}; + +	auto power_max = powerlist.getMaxPower(); +	auto power_min = powerlist.getMinPower(); +	float power_span = power_max - power_min; + +	// Width is limited to at least 10. Fixes problem with instrument with a +	//  sample set smaller than MIN_SAMPLE_SET_SIZE. +	float width = std::max(samples.size(), MIN_SAMPLE_SET_SIZE); + +	// Spread out at most ~2 samples away from center if all samples have a +	// uniform distribution over the power spectrum (which they probably don't). +	float mean_stepwidth = power_span / width; + +	// Cut off mean value with stddev/2 in both ends in order to make room for +	//  downwards expansion on velocity 0 and upwards expansion on velocity 1. +	float mean = level * (power_span - mean_stepwidth) + (mean_stepwidth / 2.0); +	float stddev = velocity_stddev * mean_stepwidth; + +	float power{0.f}; + +	// note: loop is executed once + #retry +	do +	{ +		--retry; + +		// Select normal distributed value between +		//  (stddev/2) and (power_span-stddev/2) +		float lvl = rand.normalDistribution(mean, stddev); + +		// Adjust this value to be in range +		//  (power_min+stddev/2) and (power_max-stddev/2) +		lvl += power_min; + +		DEBUG(rand, +			  "level: %f, lvl: %f (mean: %.2f, stddev: %.2f, mean_stepwidth: %f, power_min: %f, power_max: %f)\n", +			  level, lvl, mean, stddev, mean_stepwidth, power_min, power_max); + +		for (auto& item: samples) +		{ +			if (sample == nullptr || std::fabs(item.power - lvl) < std::fabs(power - lvl)) +			{ +				sample = item.sample; +				power = item.power; +			} +		} +	} while (lastsample == sample && retry >= 0); + +	DEBUG(rand, "Found sample with power %f\n", power); + +	lastsample = sample; + +	return sample; +} + +const Sample* SampleSelection::getObjective(level_t level, std::size_t pos) +{ +	auto velocity_stddev = settings.velocity_stddev.load(); + +	const auto& samples = powerlist.getPowerListItems(); +	if(!samples.size()) +	{ +		return nullptr; // No samples to choose from. +	} + +	auto power_max = powerlist.getMaxPower(); +	auto power_min = powerlist.getMinPower(); +	float power_span = power_max - power_min; + +	// Width is limited to at least 10. Fixes problem with instrument with a +	// sample set smaller than MIN_SAMPLE_SET_SIZE. +	float width = std::max(samples.size(), MIN_SAMPLE_SET_SIZE); + +	// Spread out at most ~2 samples away from center if all samples have a +	// uniform distribution over the power spectrum (which they probably don't). +	float mean_stepwidth = power_span / width; + +	// Cut off mean value with stddev/2 in both ends in order to make room for +	// downwards expansion on velocity 0 and upwards expansion on velocity 1. +	float mean = level * (power_span - mean_stepwidth) + (mean_stepwidth / 2.0); +	float stddev = settings.enable_velocity_modifier.load() ? velocity_stddev * mean_stepwidth : 0.; + +	std::size_t index_opt = 0; +	float power_opt{0.f}; +	float value_opt{std::numeric_limits<float>::max()}; +	// TODO: those are mostly for debugging at the moment +	float random_opt = 0.; +	float distance_opt = 0.; +	float recent_opt = 0.; + +	// Select normal distributed value between +	// (stddev/2) and (power_span-stddev/2) +	float lvl = rand.normalDistribution(mean, stddev); + +	// Adjust this value to be in range +	// (power_min+stddev/2) and (power_max-stddev/2) +	lvl += power_min; + +	DEBUG(rand, "level: %f, lvl: %f (mean: %.2f, stddev: %.2f, mean_stepwidth: %f," +		"power_min: %f, power_max: %f)\n", level, lvl, mean, stddev, mean_stepwidth, +		power_min, power_max); + +	// TODO: expose parameters to GUI +	float alpha = 1.0; +	float beta = 1.0; +	float gamma = .5; + +	// TODO: start with most promising power value and then stop when reaching far values +	// which cannot become opt anymore +	for (std::size_t i = 0; i < samples.size(); ++i) +	{ +		auto const& item = samples[i]; + +		// compute objective function value +		auto random = rand.floatInRange(0.,1.); +		auto distance = item.power - lvl; +		auto recent = (float)settings.samplerate/std::max<std::size_t>(pos - last[i], 1); +		auto value = alpha*pow2(distance) + beta*pow2(recent) + gamma*random; + +		if (value < value_opt) +		{ +			index_opt = i; +			power_opt = item.power; +			value_opt = value; +			random_opt = random; +			distance_opt = distance; +			recent_opt = recent; +		} +	} + +	DEBUG(rand, "Chose sample with index: %d, value: %f, power %f, random: %f, distance: %f, recent: %f", (int)index_opt, value_opt, power_opt, random_opt, distance_opt, recent_opt); + +	last[index_opt] = pos; +	return samples[index_opt].sample; +} diff --git a/src/sample_selection.h b/src/sample_selection.h new file mode 100644 index 0000000..29d8f54 --- /dev/null +++ b/src/sample_selection.h @@ -0,0 +1,63 @@ +/* -*- Mode: c++ -*- */ +/*************************************************************************** + *            sample_selection.h + * + *  Mon Mar  4 23:58:12 CET 2019 + *  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 <vector> + +#include "sample.h" + +class PowerList; +class Random; +struct Settings; + +enum class SelectionAlg +{ +	Old, +	Objective, +}; + +class SampleSelection +{ +private: +	Settings& settings; +	Random& rand; +	const PowerList& powerlist; + +	Sample* lastsample; +	std::vector<std::size_t> last; + +	SelectionAlg alg = SelectionAlg::Old; +	const Sample* getOld(level_t level, std::size_t pos); +	const Sample* getObjective(level_t level, std::size_t pos); + +public: +	SampleSelection(Settings& settings, Random& rand, const PowerList& powerlist); + +	void setSelectionAlg(SelectionAlg alg); +	void finalise(); +	const Sample* get(level_t level, std::size_t pos); +}; | 
