summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndré Nusser <andre.nusser@googlemail.com>2019-03-06 09:54:29 +0100
committerAndré Nusser <andre.nusser@googlemail.com>2019-05-11 14:54:51 +0200
commita673a209a71b06488df3244903b5b4b7f994451d (patch)
tree61403df77a0916d52ec1631b515422bfd1082f92
parent1965889ac953b354763f194cd7ec44932942477d (diff)
Split sample selection into own class.
-rw-r--r--src/Makefile.am1
-rw-r--r--src/instrument.cc5
-rw-r--r--src/instrument.h3
-rw-r--r--src/powerlist.cc104
-rw-r--r--src/powerlist.h26
-rw-r--r--src/sample_selection.cc226
-rw-r--r--src/sample_selection.h63
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);
+};