summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndré Nusser <andre.nusser@googlemail.com>2016-03-24 00:11:28 +0100
committerAndré Nusser <andre.nusser@googlemail.com>2016-03-29 22:18:48 +0200
commit866b09992668f97af063dcd77dc5dd0e3a512b94 (patch)
treedfeffaa3eb1a559d746d04f3fbb7402e74d1b290
parentefe93864d53f72be4fa4dfe003f0f7578fc558e2 (diff)
New Random class for convenient random number generation.
-rw-r--r--.gitignore1
-rw-r--r--src/Makefile.am.drumgizmo1
-rw-r--r--src/instrument.cc8
-rw-r--r--src/instrument.h3
-rw-r--r--src/powerlist.cc14
-rw-r--r--src/powerlist.h3
-rw-r--r--src/random.cc58
-rw-r--r--src/random.h62
-rw-r--r--src/velocity.cc2
-rw-r--r--src/velocity.h3
-rw-r--r--test/Makefile.am12
-rw-r--r--test/randomtest.cc119
12 files changed, 266 insertions, 20 deletions
diff --git a/.gitignore b/.gitignore
index 50418d1..ad79988 100644
--- a/.gitignore
+++ b/.gitignore
@@ -47,6 +47,7 @@ test/audiocachefile
test/audiocacheidmanager
test/configfile
test/memchecker
+test/random
drumgizmo-*.tar.gz
tst
vst/Makefile.mingw32
diff --git a/src/Makefile.am.drumgizmo b/src/Makefile.am.drumgizmo
index e41bacc..8f648d0 100644
--- a/src/Makefile.am.drumgizmo
+++ b/src/Makefile.am.drumgizmo
@@ -28,6 +28,7 @@ DRUMGIZMO_SOURCES = \
$(top_srcdir)/src/mutex.cc \
$(top_srcdir)/src/path.cc \
$(top_srcdir)/src/powerlist.cc \
+ $(top_srcdir)/src/random.cc \
$(top_srcdir)/src/sample.cc \
$(top_srcdir)/src/semaphore.cc \
$(top_srcdir)/src/saxparser.cc \
diff --git a/src/instrument.cc b/src/instrument.cc
index 96a6bfd..21371f0 100644
--- a/src/instrument.cc
+++ b/src/instrument.cc
@@ -70,9 +70,8 @@ Sample *Instrument::sample(level_t level, size_t pos)
}
if(Conf::enable_velocity_randomiser) {
- float r = (float)rand() / (float)RAND_MAX; // random number: [0;1]
- r -= 0.5; // random number [-0.5;0.5]
- r *= Conf::velocity_randomiser_weight * 2; // ex. random number [-0.1;0.1]
+ float r = rand.floatInRange(-1.0*Conf::velocity_randomiser_weight,
+ Conf::velocity_randomiser_weight);
level += r;
if(level > 1.0) level = 1.0;
if(level < 0.0) level = 0.0;
@@ -91,8 +90,7 @@ Sample *Instrument::sample(level_t level, size_t pos)
// Version 1.0
std::vector<Sample*> s = samples.get(level * mod);
if(s.size() == 0) return NULL;
- size_t idx = rand()%(s.size());
- sample = s[idx];
+ sample = rand.choose(s);
}
if(Conf::enable_velocity_modifier) {
diff --git a/src/instrument.h b/src/instrument.h
index e880d8d..62f8d01 100644
--- a/src/instrument.h
+++ b/src/instrument.h
@@ -35,6 +35,7 @@
#include "sample.h"
#include "versionstr.h"
+#include "random.h"
class InstrumentParser;
class Instrument {
@@ -76,6 +77,8 @@ private:
size_t lastpos;
float mod;
+
+ Random rand;
};
//typedef std::map< std::string, Instrument > Instruments;
diff --git a/src/powerlist.cc b/src/powerlist.cc
index efb1f97..b6640e9 100644
--- a/src/powerlist.cc
+++ b/src/powerlist.cc
@@ -51,18 +51,6 @@
#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;
@@ -242,7 +230,7 @@ Sample *PowerList::get(level_t level)
again:
// Select normal distributed value between
// (stddev/2) and (power_span-stddev/2)
- float lvl = box_muller_transform(mean, stddev);
+ float lvl = rand.normalDistribution(mean, stddev);
// Adjust this value to be in range
// (power_min+stddev/2) and (power_max-stddev/2)
diff --git a/src/powerlist.h b/src/powerlist.h
index 43f51d2..1077d8c 100644
--- a/src/powerlist.h
+++ b/src/powerlist.h
@@ -30,6 +30,7 @@
#include <vector>
#include "sample.h"
+#include "random.h"
class PowerList {
public:
@@ -47,6 +48,8 @@ private:
float power;
};
+ Random rand;
+
std::vector<PowerListItem> samples;
float power_max;
float power_min;
diff --git a/src/random.cc b/src/random.cc
new file mode 100644
index 0000000..1df9a62
--- /dev/null
+++ b/src/random.cc
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/***************************************************************************
+ * random.cc
+ *
+ * Wed Mar 23 19:17:24 CET 2016
+ * Copyright 2016 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 "random.h"
+
+#include <chrono>
+
+Random::Random()
+ : Random(std::chrono::system_clock::now().time_since_epoch().count())
+{
+
+}
+
+Random::Random(unsigned int seed)
+{
+ generator.seed(seed);
+}
+
+int Random::intInRange(int lower_bound, int upper_bound)
+{
+ std::uniform_int_distribution<int> distribution(lower_bound, upper_bound);
+ return distribution(generator);
+}
+
+float Random::floatInRange(float lower_bound, float upper_bound)
+{
+ std::uniform_real_distribution<float> distribution(lower_bound, upper_bound);
+ return distribution(generator);
+}
+
+float Random::normalDistribution(float mean, float stddev)
+{
+ std::normal_distribution<float> distribution(mean, stddev);
+ return distribution(generator);
+}
diff --git a/src/random.h b/src/random.h
new file mode 100644
index 0000000..9eaefad
--- /dev/null
+++ b/src/random.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/***************************************************************************
+ * random.h
+ *
+ * Wed Mar 23 19:17:24 CET 2016
+ * Copyright 2016 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 <random>
+#include <vector>
+
+class Random
+{
+public:
+ Random();
+ Random(unsigned int seed);
+
+ //! \return random int in range [<lower_bound>, <upper_bound>].
+ int intInRange(int lower_bound, int upper_bound);
+
+ //! \return random float in range [<lower_bound>, <upper_bound>].
+ float floatInRange(float lower_bound, float upper_bound);
+
+ //! \return random float drawn from a normal distribution with mean <mean>
+ //! and standard deviation <stddev>.
+ float normalDistribution(float mean, float stddev);
+
+ //! \return uniformly at random chosen element from <vec>.
+ template <typename T>
+ T& choose(std::vector<T>& vec);
+
+private:
+ std::default_random_engine generator;
+};
+
+template <typename T>
+T& Random::choose(std::vector<T>& vec)
+{
+ std::uniform_int_distribution<size_t> distribution(0, vec.size()-1);
+ size_t rand_index = distribution(generator);
+ return vec[rand_index];
+}
diff --git a/src/velocity.cc b/src/velocity.cc
index 04d0475..0ee00df 100644
--- a/src/velocity.cc
+++ b/src/velocity.cc
@@ -47,7 +47,7 @@ Sample *Velocity::getSample()
{
Sample *sample = NULL;
- float x = (float)rand() / (float)RAND_MAX;
+ float x = rand.floatInRange(0, 1);
float sum = 0.0;
Samples::iterator i = samples.begin();
diff --git a/src/velocity.h b/src/velocity.h
index 439ca68..c62ddad 100644
--- a/src/velocity.h
+++ b/src/velocity.h
@@ -30,6 +30,7 @@
#include <map>
#include "sample.h"
+#include "random.h"
class Velocity {
public:
@@ -44,6 +45,8 @@ public:
private:
typedef std::map< Sample *, float > Samples;
Samples samples;
+
+ Random rand;
};
#endif/*__DRUMGIZMO_VELOCITY_H__*/
diff --git a/test/Makefile.am b/test/Makefile.am
index 6e56043..ccb21e6 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -3,7 +3,7 @@ include $(top_srcdir)/src/Makefile.am.drumgizmo
TESTS = resource engine gui resampler lv2 configfile audiocache \
audiocachefile audiocacheidmanager audiocacheeventhandler \
- memchecker
+ memchecker random
check_PROGRAMS = $(TESTS)
@@ -128,5 +128,15 @@ memchecker_SOURCES = \
test.cc \
memcheckertest.cc
+random_CXXFLAGS = -DOUTPUT=\"random\" $(CPPUNIT_CFLAGS) \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/hugin -DDISABLE_HUGIN
+random_CFLAGS = -DDISABLE_HUGIN
+random_LDFLAGS = $(CPPUNIT_LIBS)
+random_SOURCES = \
+ $(top_srcdir)/src/random.cc \
+ test.cc \
+ randomtest.cc
+
EXTRA_DIST = \
lv2_test_host.h
diff --git a/test/randomtest.cc b/test/randomtest.cc
new file mode 100644
index 0000000..16a16c4
--- /dev/null
+++ b/test/randomtest.cc
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/***************************************************************************
+ * randomtest.cc
+ *
+ * Sat Mar 26 08:48:53 CET 2016
+ * Copyright 2016 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 <cppunit/extensions/HelperMacros.h>
+
+#include <random.h>
+
+#include <vector>
+#include <cmath>
+
+class RandomTest
+ : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(RandomTest);
+ CPPUNIT_TEST(rangeTest);
+ CPPUNIT_TEST(normalTest);
+ CPPUNIT_TEST(chooseTest);
+ CPPUNIT_TEST_SUITE_END();
+
+public:
+ void setUp()
+ {
+ }
+
+ void tearDown()
+ {
+
+ }
+
+ void rangeTest()
+ {
+ // check if random numbers are in the wanted range
+ Random rand;
+
+ float float_lb = -42.23;
+ float float_ub = 666.666;
+ int int_lb = -42;
+ int int_ub = 23;
+ for (int i = 0; i<10000; i++)
+ {
+ float rand_float = rand.floatInRange(float_lb, float_ub);
+ float rand_int = rand.intInRange(int_lb, int_ub);
+ CPPUNIT_ASSERT(rand_float >= float_lb && rand_float <= float_ub);
+ CPPUNIT_ASSERT(rand_int >= int_lb && rand_int <= int_ub);
+ }
+
+ // check if the series of random numbers is the one we expect
+ // for a certain seed.
+ rand = Random(666);
+ CPPUNIT_ASSERT_EQUAL(0, rand.intInRange(0,100));
+ CPPUNIT_ASSERT_EQUAL(61, rand.intInRange(0,100));
+ CPPUNIT_ASSERT_EQUAL(23, rand.intInRange(0,100));
+ }
+
+ void normalTest()
+ {
+ // This test might theoretically fail but it is extremely unlikely to happen.
+ Random rand;
+
+ float real_mean = 42.0;
+ float real_stddev = 2.0;
+ int nr_of_samples = 50000;
+
+ float sum = 0.;
+ float sum_of_squares = 0.;
+ for (int i=0; i<nr_of_samples; i++)
+ {
+ float rand_float = rand.normalDistribution(real_mean, real_stddev);
+ sum += rand_float;
+ sum_of_squares += rand_float*rand_float;
+ }
+
+ float estimated_mean = sum/nr_of_samples;
+ float estimated_stddev = sqrt(sum_of_squares/nr_of_samples - estimated_mean*estimated_mean);
+
+ float epsilon = 0.1;
+ CPPUNIT_ASSERT(estimated_mean >= real_mean-epsilon && estimated_mean <= real_mean+epsilon);
+ CPPUNIT_ASSERT(estimated_stddev >= real_stddev-epsilon && estimated_stddev <= real_stddev+epsilon);
+ }
+
+ void chooseTest()
+ {
+ Random rand;
+
+ std::vector<int> vec = { 42, 42, 42 };
+ int nr_of_samples = 1000;
+
+ for (int i=0; i<nr_of_samples; i++)
+ {
+ CPPUNIT_ASSERT_EQUAL(42, rand.choose(vec));
+ }
+ }
+};
+
+// Registers the fixture into the 'registry'
+CPPUNIT_TEST_SUITE_REGISTRATION(RandomTest);