From 22e8c588324184b777cea9f55ec0fc73b56949b8 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sun, 15 Sep 2013 09:28:15 +0200 Subject: New experimental sample selector (enable using --with-experimental argument on ./configure). --- src/Makefile.am | 4 +- src/Makefile.am.drumgizmo | 4 +- src/audiofile.cc | 4 +- src/audiofile.h | 4 +- src/drumkit.h | 3 + src/drumkitparser.cc | 12 +++ src/instrument.cc | 23 ++++- src/instrument.h | 8 ++ src/instrumentparser.cc | 16 ++++ src/powerlist.cc | 219 ++++++++++++++++++++++++++++++++++++++++++++++ src/powerlist.h | 57 ++++++++++++ src/sample.h | 1 + src/versionstr.cc | 150 +++++++++++++++++++++++++++++++ src/versionstr.h | 112 ++++++++++++++++++++++++ 14 files changed, 612 insertions(+), 5 deletions(-) create mode 100644 src/powerlist.cc create mode 100644 src/powerlist.h create mode 100644 src/versionstr.cc create mode 100644 src/versionstr.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 197485b..e686ec0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -29,6 +29,7 @@ EXTRA_DIST = \ semaphore.h \ thread.h \ velocity.h \ + versionstr.h \ \ audiofile.cc \ audioinputengine.cc \ @@ -56,4 +57,5 @@ EXTRA_DIST = \ saxparser.cc \ semaphore.cc \ thread.cc \ - velocity.cc + velocity.cc \ + versionstr.cc \ No newline at end of file diff --git a/src/Makefile.am.drumgizmo b/src/Makefile.am.drumgizmo index 606ed27..47d54a2 100644 --- a/src/Makefile.am.drumgizmo +++ b/src/Makefile.am.drumgizmo @@ -18,10 +18,12 @@ DRUMGIZMO_SOURCES = \ $(top_srcdir)/src/midimapper.cc \ $(top_srcdir)/src/mutex.cc \ $(top_srcdir)/src/path.cc \ + $(top_srcdir)/src/powerlist.cc \ $(top_srcdir)/src/sample.cc \ $(top_srcdir)/src/semaphore.cc \ $(top_srcdir)/src/saxparser.cc \ $(top_srcdir)/src/thread.cc \ - $(top_srcdir)/src/velocity.cc + $(top_srcdir)/src/velocity.cc \ + $(top_srcdir)/src/versionstr.cc DRUMGIZMO_LIBS = $(SNDFILE_LIBS) $(EXPAT_LIBS) \ No newline at end of file diff --git a/src/audiofile.cc b/src/audiofile.cc index 511df27..37fb5cc 100644 --- a/src/audiofile.cc +++ b/src/audiofile.cc @@ -171,7 +171,7 @@ void AudioFile::reset() mutex.unlock(); } -void AudioFile::load() +void AudioFile::load(int num_samples) { /* Lazy load of drum kits @@ -191,6 +191,8 @@ void AudioFile::load() size = sf_info.frames; + if(num_samples != ALL_SAMPLES && (int)size > num_samples) size = num_samples; + sample_t* data = new sample_t[size]; size = sf_read_float(fh, data, size); diff --git a/src/audiofile.h b/src/audiofile.h index 46c9285..20ffdf3 100644 --- a/src/audiofile.h +++ b/src/audiofile.h @@ -67,12 +67,14 @@ #define LAZYLOAD +#define ALL_SAMPLES -1 + class AudioFile { public: AudioFile(std::string filename); ~AudioFile(); - void load(); + void load(int num_samples = ALL_SAMPLES); void unload(); bool isLoaded(); diff --git a/src/drumkit.h b/src/drumkit.h index 2ed558d..c1e5a69 100644 --- a/src/drumkit.h +++ b/src/drumkit.h @@ -32,6 +32,7 @@ #include "channel.h" #include "instrument.h" +#include "versionstr.h" class DrumKitParser; class DrumKit { @@ -55,6 +56,8 @@ private: std::string _name; std::string _description; + + VersionStr _version; }; #endif/*__DRUMGIZMO_DRUMKIT_H__*/ diff --git a/src/drumkitparser.cc b/src/drumkitparser.cc index 9100b5b..d838346 100644 --- a/src/drumkitparser.cc +++ b/src/drumkitparser.cc @@ -60,6 +60,18 @@ void DrumKitParser::startTag(std::string name, if(attr.find("description") != attr.end()) kit._description = attr["description"]; + + if(attr.find("version") != attr.end()) { + try { + kit._version = VersionStr(attr["version"]); + } catch(const char *err) { + ERR(kitparser, "Error parsing version number: %s, using 1.0\n", err); + kit._version = VersionStr(1,0,0); + } + } else { + WARN(kitparser, "Missing version number, assuming 1.0\n"); + kit._version = VersionStr(1,0,0); + } } if(name == "channels") {} diff --git a/src/instrument.cc b/src/instrument.cc index 7232431..3b357eb 100644 --- a/src/instrument.cc +++ b/src/instrument.cc @@ -34,6 +34,10 @@ #include "sample.h" #include "configuration.h" +#ifdef EXPERIMENTAL +#define NEW_ALGORITHM +#endif + Instrument::Instrument() { DEBUG(instrument, "new %p\n", this); @@ -62,6 +66,8 @@ bool Instrument::isValid() Sample *Instrument::sample(level_t level, size_t pos) { + Sample *sample = NULL; + if(Conf::enable_velocity_modifier == false) { mod = 1.0; lastpos = 0; @@ -81,24 +87,39 @@ Sample *Instrument::sample(level_t level, size_t pos) if(mod > 1.0) mod = 1.0; } +#ifdef NEW_ALGORITHM + sample = powerlist.get(level * mod); +#else // printf("Find level %f\n", level); std::vector s = samples.get(level * mod); if(s.size() == 0) return NULL; size_t idx = rand()%(s.size()); + sample = s[idx]; +#endif/*NEW_ALGORITHM*/ if(Conf::enable_velocity_modifier) { lastpos = pos; mod *= Conf::velocity_modifier_weight; } - return s[idx]; + return sample; } void Instrument::addSample(level_t a, level_t b, Sample *s) { samples.insert(a, b, s); +#ifdef NEW_ALGORITHM + powerlist.add(s); +#endif/*NEW_ALGORITHM*/ } +void Instrument::finalise() +{ +#ifdef NEW_ALGORITHM + powerlist.finalise(); +#endif/*NEW_ALGORITHM*/ +}} + std::string Instrument::name() { return _name; diff --git a/src/instrument.h b/src/instrument.h index a302412..b7ddeea 100644 --- a/src/instrument.h +++ b/src/instrument.h @@ -31,8 +31,10 @@ #include #include "rangemap.h" +#include "powerlist.h" #include "sample.h" +#include "versionstr.h" class InstrumentParser; class Instrument { @@ -61,8 +63,14 @@ private: std::string _group; std::string _name; std::string _description; + + VersionStr _version; + RangeMap samples; + PowerList powerlist; + void addSample(level_t a, level_t b, Sample *s); + void finalise(); ///< Signal instrument that no more samples will be added. std::vector samplelist; diff --git a/src/instrumentparser.cc b/src/instrumentparser.cc index 15195e1..454877f 100644 --- a/src/instrumentparser.cc +++ b/src/instrumentparser.cc @@ -57,6 +57,18 @@ void InstrumentParser::startTag(std::string name, if(attr.find("description") != attr.end()) instrument._description = attr["description"]; + + if(attr.find("version") != attr.end()) { + try { + instrument._version = VersionStr(attr["version"]); + } catch(const char *err) { + ERR(instrparser, "Error parsing version number: %s, using 1.0\n", err); + instrument._version = VersionStr(1,0,0); + } + } else { + WARN(instrparser, "Missing version number, assuming 1.0\n"); + instrument._version = VersionStr(1,0,0); + } } if(name == "samples") { @@ -147,6 +159,10 @@ void InstrumentParser::endTag(std::string name) instrument.samplelist.push_back(s); s = NULL; } + + if(name == "instrument") { + instrument.finalise(); + } } int InstrumentParser::readData(char *data, size_t size) diff --git a/src/powerlist.cc b/src/powerlist.cc new file mode 100644 index 0000000..a298927 --- /dev/null +++ b/src/powerlist.cc @@ -0,0 +1,219 @@ +/* -*- 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 2 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 + +#include + +#define SIZE 500 + +// Box–Muller transform. +// See: http://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform +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; +} + +#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() +{ + 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()); + + std::vector::iterator si = samples.begin(); + while(si != samples.end()) { + PowerListItem &item = *si; + Sample *sample = item.sample; + + 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(); + + float power = 0; + size_t s = 0; + for(; s < SIZE && s < master->size; s++) { + power += master->data[s] * master->data[s]; + } + + power = sqrt(power); + + if(power > power_max) power_max = power; + + item.power = power; + + DEBUG(rand, " - power: %f\n", power); + + si++; + } +} + +Sample *PowerList::get(level_t level) +{ + Sample *sample = NULL; + float power = 0; + + float mean = level * power_max; + float stddev = power_max / samples.size() * 2; + + float lvl = box_muller_transform(mean, stddev); + + DEBUG(rand, "lvl: %f (mean: %.2f, stddev: %.2f)\n", + lvl, mean, stddev); + + 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++; + } + + DEBUG(rand, "Found power %f\n", power); + + return sample; +} diff --git a/src/powerlist.h b/src/powerlist.h new file mode 100644 index 0000000..0bcd11c --- /dev/null +++ b/src/powerlist.h @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * powerlist.h + * + * Sun Jul 28 19:45:47 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 2 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. + */ +#ifndef __DRUMGIZMO_POWERLIST_H__ +#define __DRUMGIZMO_POWERLIST_H__ + +#include + +#include "sample.h" + +class PowerList { +public: + PowerList(); + + void add(Sample *s); + void finalise(); ///< Call this when no more samples will be added. + + Sample *get(level_t velocity); + +private: + class PowerListItem { + public: + Sample *sample; + float power; + }; + + std::vector samples; + float power_max; + float power_min; + + Channel *getMasterChannel(); +}; + +#endif/*__DRUMGIZMO_POWERLIST_H__*/ diff --git a/src/sample.h b/src/sample.h index 70cfee2..cd3e111 100644 --- a/src/sample.h +++ b/src/sample.h @@ -38,6 +38,7 @@ typedef std::map< Channel*, AudioFile* > AudioFiles; class InstrumentParser; class Sample { friend class InstrumentParser; + friend class PowerList; public: Sample(std::string name); ~Sample(); diff --git a/src/versionstr.cc b/src/versionstr.cc new file mode 100644 index 0000000..3ef09e2 --- /dev/null +++ b/src/versionstr.cc @@ -0,0 +1,150 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * versionstr.cc + * + * Wed Jul 22 11:41:32 CEST 2009 + * Copyright 2009 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 2 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 "versionstr.h" + +#include +#include +#include + +// Workaround - major, minor and patch are defined as macros when using _GNU_SOURCES +#ifdef major +#undef major +#endif +#ifdef minor +#undef minor +#endif +#ifdef patch +#undef patch +#endif + +VersionStr::VersionStr(std::string v) throw(const char *) +{ + memset(version, 0, sizeof(version)); + set(v); +} + +VersionStr::VersionStr(size_t major, size_t minor, size_t patch) +{ + version[0] = major; + version[1] = minor; + version[2] = patch; +} + +void VersionStr::set(std::string v) throw(const char *) +{ + std::string num; + size_t idx = 0; + for(size_t i = 0; i < v.length(); i++) { + if(v[i] == '.') { + if(idx > 2) throw "Version string is too long."; + version[idx] = atoi(num.c_str()); + idx++; + num = ""; + } else if(v[i] >= '0' && v[i] <= '9') { + num.append(1, v[i]); + } else { + throw "Version string contains illegal character."; + } + } + if(idx > 2) throw "Version string is too long."; + version[idx] = atoi(num.c_str()); +} + +VersionStr::operator std::string() const +{ + std::string v; + char *buf; + size_t sz; + if(patch()) sz = asprintf(&buf, "%d.%d.%d", major(), minor(), patch()); + else sz = asprintf(&buf, "%d.%d", major(), minor()); + if(sz) v = buf; + free(buf); + return v; +} + +void VersionStr::operator=(std::string v) throw(const char *) +{ + set(v); +} + +// return a - b simplified as -1, 0 or 1 +static int vdiff(const VersionStr &a, const VersionStr &b) +{ + if(a.major() < b.major()) return -1; + if(a.major() > b.major()) return 1; + if(a.minor() < b.minor()) return -1; + if(a.minor() > b.minor()) return 1; + if(a.patch() < b.patch()) return -1; + if(a.patch() > b.patch()) return 1; + return 0; +} + +bool VersionStr::operator<(const VersionStr &other) const +{ + if(vdiff(*this, other) == -1) return true; + return false; +} + +bool VersionStr::operator>(const VersionStr &other) const +{ + if(vdiff(*this, other) == 1) return true; + return false; +} + +bool VersionStr::operator==(const VersionStr &other) const +{ + if(vdiff(*this, other) == 0) return true; + return false; +} + +bool VersionStr::operator<=(const VersionStr &other) const +{ + if(vdiff(*this, other) != 1) return true; + return false; +} + +bool VersionStr::operator>=(const VersionStr &other) const +{ + if(vdiff(*this, other) != -1) return true; + return false; +} + +size_t VersionStr::major() const +{ + return version[0]; +} + +size_t VersionStr::minor() const +{ + return version[1]; +} + +size_t VersionStr::patch() const +{ + return version[2]; +} diff --git a/src/versionstr.h b/src/versionstr.h new file mode 100644 index 0000000..ecb1df3 --- /dev/null +++ b/src/versionstr.h @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * versionstr.h + * + * Wed Jul 22 11:41:32 CEST 2009 + * Copyright 2009 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 2 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. + */ +#ifndef __PRACRO_VERSIONSTR_H__ +#define __PRACRO_VERSIONSTR_H__ + +#include + +// Workaround - major, minor and patch are defined as macros when using _GNU_SOURCES +#ifdef major +#undef major +#endif +#ifdef minor +#undef minor +#endif +#ifdef patch +#undef patch +#endif + +/** + * VersionStr class. + * It hold a version number and is capable of correct sorting, as well as string + * conversion both ways. + */ +class VersionStr { +public: + /** + * Constructor. + * Throws an exeption if the string does not parse. + * @param v A std::string containing a version string on the form a.b or a.b.c + */ + VersionStr(std::string v) throw(const char *); + + /** + * Constructor. + * @param major A size_t containing the major version number. + * @param minor A size_t containing the minor version number. + * @param patch A size_t containing the patch level. + */ + VersionStr(size_t major = 0, size_t minor = 0, size_t patch = 0); + + /** + * Typecast to std::string operator. + * It simply converts the version numbers into a string of the form major.minor + * (if patch i 0) or major.minor.patch + */ + operator std::string() const; + + /** + * Assignment from std::string operator. + * Same as in the VersionStr(std::string v) constructor. + * Throws an exeption if the string does not parse. + */ + void operator=(std::string v) throw(const char *); + + /** + * Comparison operator. + * The version objects are sorted according to their major, minor and patch + * level numbers. + */ + bool operator<(const VersionStr &other) const; + bool operator==(const VersionStr &other) const; + bool operator>(const VersionStr &other) const; + bool operator>=(const VersionStr &other) const; + bool operator<=(const VersionStr &other) const; + + + /** + * @return Major version number. + */ + size_t major() const; + + /** + * @return Minor version number. + */ + size_t minor() const; + + /** + * @return Patch level. + */ + size_t patch() const; + +private: + void set(std::string v) throw(const char *); + size_t version[3]; +}; + +#endif/*__PRACRO_VERSIONSTR_H__*/ -- cgit v1.2.3