/* -*- 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 #include "powerlist.h" #include "random.h" #include "settings.h" #include namespace { 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::finalise() { last.assign(powerlist.getPowerListItems().size(), 0); } // FIXME: For the position, weird hacks via the powerlist are necessary. Refactor! // FIXME: bad variable naming const Sample* SampleSelection::get(float power, float instrument_power_span, float position, std::size_t pos) { const auto& samples = powerlist.getPowerListItems(); if(!samples.size()) { return nullptr; // No samples to choose from. } std::size_t index_opt = 0; float power_opt{0.f}; float pos_opt{0.f}; float value_opt{std::numeric_limits::max()}; // the following three values are mostly for debugging 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 = 4.*settings.sample_selection_f_position.load(); const float f_diverse = (1./2.)*settings.sample_selection_f_diverse.load(); const float f_random = (1./3.)*settings.sample_selection_f_random.load(); // If all power values are the same then instrument_power_span is invalid but we want // to avoid division by zero. if (instrument_power_span == 0.) { instrument_power_span = 1.0; } // start with most promising power value and then stop when reaching far values // which cannot become opt anymore auto closest_it = std::lower_bound(samples.begin(), samples.end(), power); std::size_t up_index = std::distance(samples.begin(), closest_it); std::size_t down_index = (up_index == 0 ? 0 : up_index - 1); float up_value_lb; if (up_index < samples.size()) { auto const close_up = (samples[up_index].power-power)/instrument_power_span; up_value_lb = f_close*pow2(close_up); } else { --up_index; up_value_lb = std::numeric_limits::max(); } auto const close_down = (samples[down_index].power-power)/instrument_power_span; float down_value_lb = (up_index != 0 ? f_close*pow2(close_down) : std::numeric_limits::max()); std::size_t count = 0; do { DEBUG(rand, "%d %d", (int)up_index, (int)down_index); assert(down_index <= up_index); std::size_t current_index; if (up_value_lb < down_value_lb) { current_index = up_index; if (up_index != samples.size()-1) { ++up_index; up_value_lb = f_close*pow2((samples[up_index].power-power)/instrument_power_span); } else { up_value_lb = std::numeric_limits::max(); } } else { current_index = down_index; if (down_index != 0) { --down_index; down_value_lb = f_close*pow2((samples[down_index].power-power)/instrument_power_span); } else { down_value_lb = std::numeric_limits::max(); } } auto random = rand.floatInRange(0.,1.); auto close = (samples[current_index].power - power)/instrument_power_span; auto diverse = 1./(1. + (float)(pos - last[current_index])/settings.samplerate); 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, 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; }