/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /*************************************************************************** * powerlist.cc * * Sun Jul 28 19:45:48 CEST 2013 * Copyright 2013 Bent Bisballe Nyeng * deva@aasimon.org ****************************************************************************/ /* * This file is part of DrumGizmo. * * DrumGizmo is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU 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 "powerlist.h" #include #include #include // M_PI is not defined in math.h if __STRICT_ANSI__ is defined. #ifdef __STRICT_ANSI__ #undef __STRICT_ANSI__ #endif #include /** * 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. */ #define MIN_SAMPLE_SET_SIZE 26 // Enable to calculate power on old samples without power attribute //#define AUTO_CALCULATE_POWER #define SIZE 500 // Box–Muller transform. // See: http://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform static float box_muller_transform(float mean, float stddev) { float U1 = (float)rand() / (float)RAND_MAX; float U2 = (float)rand() / (float)RAND_MAX; float x = sqrt(-2.0 * log(U1)) * cos(2.0 * M_PI * U2); return mean + stddev * x; } PowerList::PowerList() { power_max = 0; power_min = 100000000; lastsample = NULL; } #define THRES 1.0 void PowerList::add(Sample *sample) { PowerListItem item; item.power = -1; item.sample = sample; samples.push_back(item); } Channel *PowerList::getMasterChannel() { std::map count; std::vector::iterator si = samples.begin(); while(si != samples.end()) { PowerListItem &item = *si; Sample *sample = item.sample; Channel *max_channel = NULL; sample_t max_val = 0; // DEBUG(rand, "Sample: %s\n", sample->name.c_str()); size_t ci = 0; AudioFiles::iterator ai = sample->audiofiles.begin(); while(ai != sample->audiofiles.end()) { Channel *c = ai->first; AudioFile *af = ai->second; af->load(SIZE); float silence = 0; size_t silence_length = 4; for(size_t s = af->size; s > 0 && s > af->size - silence_length; s--) { silence += af->data[s]; } silence /= silence_length; size_t s = 0; for(; s < af->size; s++) { float val = af->data[s] * af->data[s] * (1.0 / (float)(s+1)); if(val > max_val) { max_val = val; max_channel = c; break; } } af->unload(); ai++; ci++; } if(max_channel) { if(count.find(max_channel) == count.end()) count[max_channel] = 0; count[max_channel]++; } si++; } Channel *master = NULL; int max_count = -1; std::map::iterator ci = count.begin(); while(ci != count.end()) { if(ci->second > max_count && strstr(ci->first->name.c_str(), "Alesis") == 0) { master = ci->first; max_count = ci->second; } ci++; } return master; } void PowerList::finalise() { #ifdef AUTO_CALCULATE_POWER Channel *master_channel = getMasterChannel(); if(master_channel == NULL) { ERR(rand, "No master channel found!\n"); return; // This should not happen... } DEBUG(rand, "Master channel: %s\n", master_channel->name.c_str()); #endif/*AUTO_CALCULATE_POWER*/ std::vector::iterator si = samples.begin(); while(si != samples.end()) { PowerListItem &item = *si; Sample *sample = item.sample; #ifdef AUTO_CALCULATE_POWER DEBUG(rand, "Sample: %s\n", sample->name.c_str()); AudioFile *master = NULL; AudioFiles::iterator afi = sample->audiofiles.begin(); while(afi != sample->audiofiles.end()) { if(afi->first->name == master_channel->name) { master = afi->second; break; } afi++; } if(master == NULL) { si++; continue; } master->load(); #endif/*AUTO_CALCULATE_POWER*/ #ifdef AUTO_CALCULATE_POWER if(sample->power == -1) { // Power not defined. Calculate it! DEBUG(powerlist, "Calculating power\n"); float power = 0; size_t s = 0; for(; s < SIZE && s < master->size; s++) { power += master->data[s] * master->data[s]; } power = sqrt(power); sample->power = power; } #endif/*AUTO_CALCULATE_POWER*/ item.power = sample->power; if(item.power > power_max) power_max = item.power; if(item.power < power_min) power_min = item.power; DEBUG(rand, " - power: %f\n", item.power); si++; } } Sample *PowerList::get(level_t level) { int retry = 3; // TODO: This must be user controllable via the UI. Sample *sample = NULL; if(!samples.size()) return NULL; // No samples to choose from. float power_span = power_max - power_min; // Width is limited to at least 10. Fioxes problem with instrument with a // sample set smaller than MIN_SAMPLE_SET_SIZE. float width = fmax(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 stddev = 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 - stddev) + (stddev / 2.0); again: // Select normal distributed value between // (stddev/2) and (power_span-stddev/2) float lvl = box_muller_transform(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)\n", level, lvl, mean, stddev); float power = 0; std::vector::iterator i = samples.begin(); while(i != samples.end()) { if(sample == NULL) { sample = i->sample; power = i->power; } if(fabs(i->power - lvl) < fabs(power - lvl)) { sample = i->sample; power = i->power; } i++; } if(lastsample == sample && retry--) { DEBUG(rand, "Retry [%d retries left]", retry); goto again; } DEBUG(rand, "Found sample with power %f\n", power); lastsample = sample; return sample; }