summaryrefslogtreecommitdiff
path: root/libs/audiographer/tests
diff options
context:
space:
mode:
authorSakari Bergen <sakari.bergen@beatwaves.net>2009-12-27 14:46:23 +0000
committerSakari Bergen <sakari.bergen@beatwaves.net>2009-12-27 14:46:23 +0000
commitdde0848a984e06cbc1d4117d9cffa75c191f3b39 (patch)
tree11f3a5fe94ac792e753297e16e4e80dd7e296aea /libs/audiographer/tests
parent35c72a53b4c6bbc61b4b86db9de629e18362b48d (diff)
Re-integrate export-optimization branch.
Export now happens directly to file (unless normalizing is required), and can be easily optimized even further. The Session process connection is still broken during export (as it was before this commit also). git-svn-id: svn://localhost/ardour2/branches/3.0@6401 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/audiographer/tests')
-rw-r--r--libs/audiographer/tests/chunker_test.cc112
-rw-r--r--libs/audiographer/tests/deinterleaver_test.cc133
-rw-r--r--libs/audiographer/tests/identity_vertex_test.cc99
-rw-r--r--libs/audiographer/tests/interleaver_deinterleaver_test.cc120
-rw-r--r--libs/audiographer/tests/interleaver_test.cc132
-rw-r--r--libs/audiographer/tests/normalizer_test.cc60
-rw-r--r--libs/audiographer/tests/peak_reader_test.cc53
-rw-r--r--libs/audiographer/tests/sample_format_converter_test.cc209
-rw-r--r--libs/audiographer/tests/silence_trimmer_test.cc196
-rw-r--r--libs/audiographer/tests/sndfile_writer_test.cc42
-rw-r--r--libs/audiographer/tests/sr_converter_test.cc101
-rw-r--r--libs/audiographer/tests/test_runner.cc14
-rw-r--r--libs/audiographer/tests/threader_test.cc155
-rw-r--r--libs/audiographer/tests/utils.h119
14 files changed, 1545 insertions, 0 deletions
diff --git a/libs/audiographer/tests/chunker_test.cc b/libs/audiographer/tests/chunker_test.cc
new file mode 100644
index 0000000000..ab0c04d7f9
--- /dev/null
+++ b/libs/audiographer/tests/chunker_test.cc
@@ -0,0 +1,112 @@
+#include "utils.h"
+#include "audiographer/chunker.h"
+
+#include <cassert>
+
+using namespace AudioGrapher;
+
+class ChunkerTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE (ChunkerTest);
+ CPPUNIT_TEST (testSynchronousProcess);
+ CPPUNIT_TEST (testAsynchronousProcess);
+ CPPUNIT_TEST_SUITE_END ();
+
+ public:
+ void setUp()
+ {
+ frames = 128;
+ random_data = TestUtils::init_random_data(frames);
+ sink.reset (new VectorSink<float>());
+ chunker.reset (new Chunker<float>(frames * 2));
+ }
+
+ void tearDown()
+ {
+ delete [] random_data;
+ }
+
+ void testSynchronousProcess()
+ {
+ chunker->add_output (sink);
+ nframes_t frames_output = 0;
+
+ ProcessContext<float> const context (random_data, frames, 1);
+
+ chunker->process (context);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL ((nframes_t) 0, frames_output);
+
+ chunker->process (context);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (2 * frames, frames_output);
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data, sink->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data, &sink->get_array()[frames], frames));
+
+ sink->reset();
+
+ chunker->process (context);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL ((nframes_t) 0, frames_output);
+
+ chunker->process (context);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (2 * frames, frames_output);
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data, sink->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data, &sink->get_array()[frames], frames));
+ }
+
+ void testAsynchronousProcess()
+ {
+ assert (frames % 2 == 0);
+
+ chunker->add_output (sink);
+ nframes_t frames_output = 0;
+
+ ProcessContext<float> const half_context (random_data, frames / 2, 1);
+ ProcessContext<float> const context (random_data, frames, 1);
+
+ // 0.5
+ chunker->process (half_context);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL ((nframes_t) 0, frames_output);
+
+ // 1.5
+ chunker->process (context);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL ((nframes_t) 0, frames_output);
+
+ // 2.5
+ chunker->process (context);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (2 * frames, frames_output);
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data, sink->get_array(), frames / 2));
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data, &sink->get_array()[frames / 2], frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data, &sink->get_array()[ 3 * frames / 2], frames / 2));
+
+ sink->reset();
+
+ // 3.5
+ chunker->process (context);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL ((nframes_t) 0, frames_output);
+
+ // 4.0
+ chunker->process (half_context);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (2 * frames, frames_output);
+ CPPUNIT_ASSERT (TestUtils::array_equals (&random_data[frames / 2], sink->get_array(), frames / 2));
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data, &sink->get_array()[frames / 2], frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data, &sink->get_array()[ 3 * frames / 2], frames / 2));
+ }
+
+ private:
+ boost::shared_ptr<Chunker<float> > chunker;
+ boost::shared_ptr<VectorSink<float> > sink;
+
+ float * random_data;
+ nframes_t frames;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION (ChunkerTest);
+
diff --git a/libs/audiographer/tests/deinterleaver_test.cc b/libs/audiographer/tests/deinterleaver_test.cc
new file mode 100644
index 0000000000..b0adbc0444
--- /dev/null
+++ b/libs/audiographer/tests/deinterleaver_test.cc
@@ -0,0 +1,133 @@
+#include "utils.h"
+#include "audiographer/deinterleaver.h"
+
+using namespace AudioGrapher;
+
+class DeInterleaverTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE (DeInterleaverTest);
+ CPPUNIT_TEST (testUninitialized);
+ CPPUNIT_TEST (testInvalidOutputIndex);
+ CPPUNIT_TEST (testInvalidInputSize);
+ CPPUNIT_TEST (testOutputSize);
+ CPPUNIT_TEST (testZeroInput);
+ CPPUNIT_TEST_SUITE_END ();
+
+ public:
+ void setUp()
+ {
+ channels = 3;
+ frames_per_channel = 128;
+ total_frames = channels * frames_per_channel;
+ random_data = TestUtils::init_random_data (total_frames, 1.0);
+
+ deinterleaver.reset (new DeInterleaver<float>());
+ sink_a.reset (new VectorSink<float>());
+ sink_b.reset (new VectorSink<float>());
+ sink_c.reset (new VectorSink<float>());
+ }
+
+ void tearDown()
+ {
+ delete [] random_data;
+ }
+
+ void testUninitialized()
+ {
+ deinterleaver.reset (new DeInterleaver<float>());
+ CPPUNIT_ASSERT_THROW (deinterleaver->output(0)->add_output (sink_a), Exception);
+ }
+
+ void testInvalidOutputIndex()
+ {
+ deinterleaver->init (3, frames_per_channel);
+ CPPUNIT_ASSERT_THROW (deinterleaver->output(3)->add_output (sink_a), Exception);
+ }
+
+ void testInvalidInputSize()
+ {
+ deinterleaver->init (channels, frames_per_channel);
+
+ ProcessContext<float> c (random_data, 0, channels);
+
+ // Too many, frames % channels == 0
+ c.frames() = total_frames + channels;
+ CPPUNIT_ASSERT_THROW (deinterleaver->process (c), Exception);
+
+ // Too many, frames % channels != 0
+ c.frames() = total_frames + 1;
+ CPPUNIT_ASSERT_THROW (deinterleaver->process (c), Exception);
+
+ // Too few, frames % channels != 0
+ c.frames() = total_frames - 1;
+ CPPUNIT_ASSERT_THROW (deinterleaver->process (c), Exception);
+ }
+
+ void assert_outputs (nframes_t expected_frames)
+ {
+ nframes_t generated_frames = 0;
+
+ generated_frames = sink_a->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (expected_frames, generated_frames);
+
+ generated_frames = sink_b->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (expected_frames, generated_frames);
+
+ generated_frames = sink_c->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (expected_frames, generated_frames);
+ }
+
+ void testOutputSize()
+ {
+ deinterleaver->init (channels, frames_per_channel);
+
+ deinterleaver->output (0)->add_output (sink_a);
+ deinterleaver->output (1)->add_output (sink_b);
+ deinterleaver->output (2)->add_output (sink_c);
+
+ // Test maximum frame input
+ ProcessContext<float> c (random_data, total_frames, channels);
+ deinterleaver->process (c);
+ assert_outputs (frames_per_channel);
+
+ // Now with less frames
+ nframes_t const less_frames = frames_per_channel / 4;
+ c.frames() = less_frames * channels;
+ deinterleaver->process (c);
+ assert_outputs (less_frames);
+ }
+
+ void testZeroInput()
+ {
+ deinterleaver->init (channels, frames_per_channel);
+
+ deinterleaver->output (0)->add_output (sink_a);
+ deinterleaver->output (1)->add_output (sink_b);
+ deinterleaver->output (2)->add_output (sink_c);
+
+ // Input zero frames
+ ProcessContext<float> c (random_data, 0, channels);
+ deinterleaver->process (c);
+
+ // ...and now test regular input
+ c.frames() = total_frames;
+ deinterleaver->process (c);
+ assert_outputs (frames_per_channel);
+ }
+
+
+ private:
+ boost::shared_ptr<DeInterleaver<float> > deinterleaver;
+
+ boost::shared_ptr<VectorSink<float> > sink_a;
+ boost::shared_ptr<VectorSink<float> > sink_b;
+ boost::shared_ptr<VectorSink<float> > sink_c;
+
+ float * random_data;
+ nframes_t frames_per_channel;
+ nframes_t total_frames;
+ unsigned int channels;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION (DeInterleaverTest);
+
diff --git a/libs/audiographer/tests/identity_vertex_test.cc b/libs/audiographer/tests/identity_vertex_test.cc
new file mode 100644
index 0000000000..5a3ae7c9f2
--- /dev/null
+++ b/libs/audiographer/tests/identity_vertex_test.cc
@@ -0,0 +1,99 @@
+#include "utils.h"
+#include "audiographer/identity_vertex.h"
+
+using namespace AudioGrapher;
+
+class IdentityVertexTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE (IdentityVertexTest);
+ CPPUNIT_TEST (testProcess);
+ CPPUNIT_TEST (testRemoveOutput);
+ CPPUNIT_TEST (testClearOutputs);
+ CPPUNIT_TEST_SUITE_END ();
+
+ public:
+ void setUp()
+ {
+ frames = 128;
+ random_data = TestUtils::init_random_data(frames);
+
+ zero_data = new float[frames];
+ memset (zero_data, 0, frames * sizeof(float));
+
+ sink_a.reset (new VectorSink<float>());
+ sink_b.reset (new VectorSink<float>());
+ }
+
+ void tearDown()
+ {
+ delete [] random_data;
+ delete [] zero_data;
+ }
+
+ void testProcess()
+ {
+ vertex.reset (new IdentityVertex<float>());
+ vertex->add_output (sink_a);
+ vertex->add_output (sink_b);
+
+ nframes_t frames_output = 0;
+
+ ProcessContext<float> c (random_data, frames, 1);
+ vertex->process (c);
+
+ frames_output = sink_a->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames, frames_output);
+
+ frames_output = sink_b->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames, frames_output);
+
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data, sink_a->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data, sink_b->get_array(), frames));
+ }
+
+ void testRemoveOutput()
+ {
+ vertex.reset (new IdentityVertex<float>());
+ vertex->add_output (sink_a);
+ vertex->add_output (sink_b);
+
+ ProcessContext<float> c (random_data, frames, 1);
+ vertex->process (c);
+
+ vertex->remove_output (sink_a);
+ ProcessContext<float> zc (zero_data, frames, 1);
+ vertex->process (zc);
+
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data, sink_a->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals (zero_data, sink_b->get_array(), frames));
+ }
+
+ void testClearOutputs()
+ {
+ vertex.reset (new IdentityVertex<float>());
+ vertex->add_output (sink_a);
+ vertex->add_output (sink_b);
+
+ ProcessContext<float> c (random_data, frames, 1);
+ vertex->process (c);
+
+ vertex->clear_outputs ();
+ ProcessContext<float> zc (zero_data, frames, 1);
+ vertex->process (zc);
+
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data, sink_a->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data, sink_b->get_array(), frames));
+ }
+
+ private:
+ boost::shared_ptr<IdentityVertex<float> > vertex;
+ boost::shared_ptr<VectorSink<float> > sink_a;
+ boost::shared_ptr<VectorSink<float> > sink_b;
+
+ float * random_data;
+ float * zero_data;
+ nframes_t frames;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION (IdentityVertexTest);
+
diff --git a/libs/audiographer/tests/interleaver_deinterleaver_test.cc b/libs/audiographer/tests/interleaver_deinterleaver_test.cc
new file mode 100644
index 0000000000..5655253e62
--- /dev/null
+++ b/libs/audiographer/tests/interleaver_deinterleaver_test.cc
@@ -0,0 +1,120 @@
+#include "utils.h"
+#include "audiographer/interleaver.h"
+#include "audiographer/deinterleaver.h"
+
+using namespace AudioGrapher;
+
+class InterleaverDeInterleaverTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE (InterleaverDeInterleaverTest);
+ CPPUNIT_TEST (testInterleavedInput);
+ CPPUNIT_TEST (testDeInterleavedInput);
+ CPPUNIT_TEST_SUITE_END ();
+
+ public:
+ void setUp()
+ {
+ channels = 3;
+ frames_per_channel = 128;
+ total_frames = channels * frames_per_channel;
+
+ random_data_a = TestUtils::init_random_data (total_frames, 1.0);
+ random_data_b = TestUtils::init_random_data (frames_per_channel, 1.0);
+ random_data_c = TestUtils::init_random_data (frames_per_channel, 1.0);
+
+ deinterleaver.reset (new DeInterleaver<float>());
+ interleaver.reset (new Interleaver<float>());
+
+ sink_a.reset (new VectorSink<float>());
+ sink_b.reset (new VectorSink<float>());
+ sink_c.reset (new VectorSink<float>());
+ }
+
+ void tearDown()
+ {
+ delete [] random_data_a;
+ delete [] random_data_b;
+ delete [] random_data_c;
+ }
+
+ void testInterleavedInput()
+ {
+ deinterleaver->init (channels, frames_per_channel);
+ interleaver->init (channels, frames_per_channel);
+
+ deinterleaver->output (0)->add_output (interleaver->input (0));
+ deinterleaver->output (1)->add_output (interleaver->input (1));
+ deinterleaver->output (2)->add_output (interleaver->input (2));
+
+ interleaver->add_output (sink_a);
+
+ // Process and assert
+ ProcessContext<float> c (random_data_a, total_frames, channels);
+ deinterleaver->process (c);
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data_a, sink_a->get_array(), total_frames));
+
+ // And a second round...
+ nframes_t less_frames = (frames_per_channel / 10) * channels;
+ c.frames() = less_frames;
+ deinterleaver->process (c);
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data_a, sink_a->get_array(), less_frames));
+ }
+
+ void testDeInterleavedInput()
+ {
+ deinterleaver->init (channels, frames_per_channel);
+ interleaver->init (channels, frames_per_channel);
+
+ interleaver->add_output (deinterleaver);
+
+ deinterleaver->output (0)->add_output (sink_a);
+ deinterleaver->output (1)->add_output (sink_b);
+ deinterleaver->output (2)->add_output (sink_c);
+
+ ProcessContext<float> c_a (random_data_a, frames_per_channel, 1);
+ ProcessContext<float> c_b (random_data_b, frames_per_channel, 1);
+ ProcessContext<float> c_c (random_data_c, frames_per_channel, 1);
+
+ // Process and assert
+ interleaver->input (0)->process (c_a);
+ interleaver->input (1)->process (c_b);
+ interleaver->input (2)->process (c_c);
+
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data_a, sink_a->get_array(), frames_per_channel));
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data_b, sink_b->get_array(), frames_per_channel));
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data_c, sink_c->get_array(), frames_per_channel));
+
+ // And a second round...
+ nframes_t less_frames = frames_per_channel / 5;
+ c_a.frames() = less_frames;
+ c_b.frames() = less_frames;
+ c_c.frames() = less_frames;
+ interleaver->input (0)->process (c_a);
+ interleaver->input (1)->process (c_b);
+ interleaver->input (2)->process (c_c);
+
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data_a, sink_a->get_array(), less_frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data_b, sink_b->get_array(), less_frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data_c, sink_c->get_array(), less_frames));
+
+ }
+
+ private:
+ boost::shared_ptr<Interleaver<float> > interleaver;
+ boost::shared_ptr<DeInterleaver<float> > deinterleaver;
+
+ boost::shared_ptr<VectorSink<float> > sink_a;
+ boost::shared_ptr<VectorSink<float> > sink_b;
+ boost::shared_ptr<VectorSink<float> > sink_c;
+
+ float * random_data_a;
+ float * random_data_b;
+ float * random_data_c;
+
+ nframes_t frames_per_channel;
+ nframes_t total_frames;
+ unsigned int channels;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION (InterleaverDeInterleaverTest);
+
diff --git a/libs/audiographer/tests/interleaver_test.cc b/libs/audiographer/tests/interleaver_test.cc
new file mode 100644
index 0000000000..abe385699d
--- /dev/null
+++ b/libs/audiographer/tests/interleaver_test.cc
@@ -0,0 +1,132 @@
+#include "utils.h"
+#include "audiographer/interleaver.h"
+
+using namespace AudioGrapher;
+
+class InterleaverTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE (InterleaverTest);
+ CPPUNIT_TEST (testUninitialized);
+ CPPUNIT_TEST (testInvalidInputIndex);
+ CPPUNIT_TEST (testInvalidInputSize);
+ CPPUNIT_TEST (testOutputSize);
+ CPPUNIT_TEST (testZeroInput);
+ CPPUNIT_TEST (testChannelSync);
+ CPPUNIT_TEST_SUITE_END ();
+
+ public:
+ void setUp()
+ {
+ channels = 3;
+ frames = 128;
+ random_data = TestUtils::init_random_data (frames, 1.0);
+
+ interleaver.reset (new Interleaver<float>());
+ sink.reset (new VectorSink<float>());
+
+ interleaver->init (channels, frames);
+ }
+
+ void tearDown()
+ {
+ delete [] random_data;
+ }
+
+ void testUninitialized()
+ {
+ interleaver.reset (new Interleaver<float>());
+ ProcessContext<float> c (random_data, frames, 1);
+ CPPUNIT_ASSERT_THROW (interleaver->input(0)->process (c), Exception);
+ }
+
+ void testInvalidInputIndex()
+ {
+ ProcessContext<float> c (random_data, frames, 1);
+ CPPUNIT_ASSERT_THROW (interleaver->input (3)->process (c), Exception);
+ }
+
+ void testInvalidInputSize()
+ {
+ ProcessContext<float> c (random_data, frames + 1, 1);
+ CPPUNIT_ASSERT_THROW (interleaver->input (0)->process (c), Exception);
+
+ c.frames() = frames;
+ interleaver->input (0)->process (c);
+ interleaver->input (1)->process (c);
+ c.frames() = frames -1;
+ CPPUNIT_ASSERT_THROW (interleaver->input (2)->process (c), Exception);
+
+ interleaver->input (0)->process (c);
+ interleaver->input (1)->process (c);
+ c.frames() = frames;
+ CPPUNIT_ASSERT_THROW (interleaver->input (2)->process (c), Exception);
+ }
+
+ void testOutputSize()
+ {
+ interleaver->add_output (sink);
+
+ ProcessContext<float> c (random_data, frames, 1);
+ interleaver->input (0)->process (c);
+ interleaver->input (1)->process (c);
+ interleaver->input (2)->process (c);
+
+ nframes_t expected_frames = frames * channels;
+ nframes_t generated_frames = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (expected_frames, generated_frames);
+
+ nframes_t less_frames = frames / 2;
+ c.frames() = less_frames;
+ interleaver->input (0)->process (c);
+ interleaver->input (1)->process (c);
+ interleaver->input (2)->process (c);
+
+ expected_frames = less_frames * channels;
+ generated_frames = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (expected_frames, generated_frames);
+ }
+
+ void testZeroInput()
+ {
+ interleaver->add_output (sink);
+
+ // input zero frames to all inputs
+ ProcessContext<float> c (random_data, 0, 1);
+ interleaver->input (0)->process (c);
+ interleaver->input (1)->process (c);
+ interleaver->input (2)->process (c);
+
+ // NOTE zero input is allowed to be a NOP
+
+ // ...now test regular input
+ c.frames() = frames;
+ interleaver->input (0)->process (c);
+ interleaver->input (1)->process (c);
+ interleaver->input (2)->process (c);
+
+ nframes_t expected_frames = frames * channels;
+ nframes_t generated_frames = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (expected_frames, generated_frames);
+ }
+
+ void testChannelSync()
+ {
+ interleaver->add_output (sink);
+ ProcessContext<float> c (random_data, frames, 1);
+ interleaver->input (0)->process (c);
+ CPPUNIT_ASSERT_THROW (interleaver->input (0)->process (c), Exception);
+ }
+
+
+ private:
+ boost::shared_ptr<Interleaver<float> > interleaver;
+
+ boost::shared_ptr<VectorSink<float> > sink;
+
+ nframes_t channels;
+ float * random_data;
+ nframes_t frames;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION (InterleaverTest);
+
diff --git a/libs/audiographer/tests/normalizer_test.cc b/libs/audiographer/tests/normalizer_test.cc
new file mode 100644
index 0000000000..711e0018ca
--- /dev/null
+++ b/libs/audiographer/tests/normalizer_test.cc
@@ -0,0 +1,60 @@
+#include "utils.h"
+
+#include "audiographer/normalizer.h"
+#include "audiographer/peak_reader.h"
+
+using namespace AudioGrapher;
+
+class NormalizerTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE (NormalizerTest);
+ CPPUNIT_TEST (testConstAmplify);
+ CPPUNIT_TEST_SUITE_END ();
+
+ public:
+ void setUp()
+ {
+ frames = 1024;
+ }
+
+ void tearDown()
+ {
+ delete [] random_data;
+ }
+
+ void testConstAmplify()
+ {
+ float target = 0.0;
+ random_data = TestUtils::init_random_data(frames, 0.5);
+
+ normalizer.reset (new Normalizer(target));
+ peak_reader.reset (new PeakReader());
+ sink.reset (new VectorSink<float>());
+
+ ProcessContext<float> const c (random_data, frames, 1);
+ peak_reader->process (c);
+
+ float peak = peak_reader->get_peak();
+ normalizer->alloc_buffer (frames);
+ normalizer->set_peak (peak);
+ normalizer->add_output (sink);
+ normalizer->process (c);
+
+ peak_reader->reset();
+ ConstProcessContext<float> normalized (sink->get_array(), frames, 1);
+ peak_reader->process (normalized);
+
+ peak = peak_reader->get_peak();
+ CPPUNIT_ASSERT (-FLT_EPSILON <= (peak - 1.0) && (peak - 1.0) <= 0.0);
+ }
+
+ private:
+ boost::shared_ptr<Normalizer> normalizer;
+ boost::shared_ptr<PeakReader> peak_reader;
+ boost::shared_ptr<VectorSink<float> > sink;
+
+ float * random_data;
+ nframes_t frames;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION (NormalizerTest);
diff --git a/libs/audiographer/tests/peak_reader_test.cc b/libs/audiographer/tests/peak_reader_test.cc
new file mode 100644
index 0000000000..dce03b6caf
--- /dev/null
+++ b/libs/audiographer/tests/peak_reader_test.cc
@@ -0,0 +1,53 @@
+#include "utils.h"
+#include "audiographer/peak_reader.h"
+
+using namespace AudioGrapher;
+
+class PeakReaderTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE (PeakReaderTest);
+ CPPUNIT_TEST (testProcess);
+ CPPUNIT_TEST_SUITE_END ();
+
+ public:
+ void setUp()
+ {
+ frames = 128;
+ random_data = TestUtils::init_random_data(frames);
+ }
+
+ void tearDown()
+ {
+ delete [] random_data;
+ }
+
+ void testProcess()
+ {
+ reader.reset (new PeakReader());
+ ProcessContext<float> c (random_data, frames, 1);
+
+ float peak = 1.5;
+ random_data[10] = peak;
+ reader->process (c);
+ CPPUNIT_ASSERT_EQUAL(peak, reader->get_peak());
+
+ peak = 2.0;
+ random_data[10] = peak;
+ reader->process (c);
+ CPPUNIT_ASSERT_EQUAL(peak, reader->get_peak());
+
+ peak = -2.1;
+ random_data[10] = peak;
+ reader->process (c);
+ float expected = fabs(peak);
+ CPPUNIT_ASSERT_EQUAL(expected, reader->get_peak());
+ }
+
+ private:
+ boost::shared_ptr<PeakReader> reader;
+
+ float * random_data;
+ nframes_t frames;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION (PeakReaderTest);
diff --git a/libs/audiographer/tests/sample_format_converter_test.cc b/libs/audiographer/tests/sample_format_converter_test.cc
new file mode 100644
index 0000000000..f723f7af53
--- /dev/null
+++ b/libs/audiographer/tests/sample_format_converter_test.cc
@@ -0,0 +1,209 @@
+#include "utils.h"
+#include "audiographer/sample_format_converter.h"
+
+using namespace AudioGrapher;
+
+class SampleFormatConverterTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE (SampleFormatConverterTest);
+ CPPUNIT_TEST (testInit);
+ CPPUNIT_TEST (testFrameCount);
+ CPPUNIT_TEST (testFloat);
+ CPPUNIT_TEST (testInt32);
+ CPPUNIT_TEST (testInt24);
+ CPPUNIT_TEST (testInt16);
+ CPPUNIT_TEST (testUint8);
+ CPPUNIT_TEST (testChannelCount);
+ CPPUNIT_TEST_SUITE_END ();
+
+ public:
+ void setUp()
+ {
+ frames = 128;
+ random_data = TestUtils::init_random_data(frames, 1.0);
+ }
+
+ void tearDown()
+ {
+ delete [] random_data;
+ }
+
+ void testInit()
+ {
+ boost::shared_ptr<SampleFormatConverter<float> > f_converter (new SampleFormatConverter<float>(1));
+ f_converter->init (frames, D_Tri, 32); // Doesn't throw
+ CPPUNIT_ASSERT_THROW (f_converter->init (frames, D_Tri, 24), Exception);
+ CPPUNIT_ASSERT_THROW (f_converter->init (frames, D_Tri, 48), Exception);
+
+ boost::shared_ptr<SampleFormatConverter<int32_t> > i_converter (new SampleFormatConverter<int32_t>(1));
+ i_converter->init (frames, D_Tri, 32); // Doesn't throw
+ i_converter->init (frames, D_Tri, 24); // Doesn't throw
+ CPPUNIT_ASSERT_THROW (i_converter->init (frames, D_Tri, 8), Exception);
+ CPPUNIT_ASSERT_THROW (i_converter->init (frames, D_Tri, 16), Exception);
+ CPPUNIT_ASSERT_THROW (i_converter->init (frames, D_Tri, 48), Exception);
+
+ boost::shared_ptr<SampleFormatConverter<int16_t> > i16_converter (new SampleFormatConverter<int16_t>(1));
+ i16_converter->init (frames, D_Tri, 16); // Doesn't throw
+ CPPUNIT_ASSERT_THROW (i16_converter->init (frames, D_Tri, 8), Exception);
+ CPPUNIT_ASSERT_THROW (i16_converter->init (frames, D_Tri, 32), Exception);
+ CPPUNIT_ASSERT_THROW (i16_converter->init (frames, D_Tri, 48), Exception);
+
+ boost::shared_ptr<SampleFormatConverter<uint8_t> > ui_converter (new SampleFormatConverter<uint8_t>(1));
+ ui_converter->init (frames, D_Tri, 8); // Doesn't throw
+ CPPUNIT_ASSERT_THROW (ui_converter->init (frames, D_Tri, 4), Exception);
+ CPPUNIT_ASSERT_THROW (ui_converter->init (frames, D_Tri, 16), Exception);
+ }
+
+ void testFrameCount()
+ {
+ boost::shared_ptr<SampleFormatConverter<int32_t> > converter (new SampleFormatConverter<int32_t>(1));
+ boost::shared_ptr<VectorSink<int32_t> > sink (new VectorSink<int32_t>());
+
+ converter->init (frames, D_Tri, 32);
+ converter->add_output (sink);
+ nframes_t frames_output = 0;
+
+ {
+ ProcessContext<float> pc(random_data, frames / 2, 1);
+ converter->process (pc);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames / 2, frames_output);
+ }
+
+ {
+ ProcessContext<float> pc(random_data, frames, 1);
+ converter->process (pc);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames, frames_output);
+ }
+
+ {
+ ProcessContext<float> pc(random_data, frames + 1, 1);
+ CPPUNIT_ASSERT_THROW(converter->process (pc), Exception);
+ }
+ }
+
+ void testFloat()
+ {
+ boost::shared_ptr<SampleFormatConverter<float> > converter (new SampleFormatConverter<float>(1));
+ boost::shared_ptr<VectorSink<float> > sink (new VectorSink<float>());
+ nframes_t frames_output = 0;
+
+ converter->init(frames, D_Tri, 32);
+ converter->add_output (sink);
+
+ converter->set_clip_floats (false);
+ ProcessContext<float> const pc(random_data, frames, 1);
+ converter->process (pc);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames, frames_output);
+ CPPUNIT_ASSERT (TestUtils::array_equals(sink->get_array(), random_data, frames));
+
+ // Make sure a few samples are < -1.0 and > 1.0
+ random_data[10] = -1.5;
+ random_data[20] = 1.5;
+
+ converter->set_clip_floats (true);
+ converter->process (pc);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames, frames_output);
+ CPPUNIT_ASSERT (TestUtils::array_filled(sink->get_array(), frames));
+
+ for (nframes_t i = 0; i < frames; ++i) {
+ // fp comparison needs a bit of tolerance, 1.01 << 1.5
+ CPPUNIT_ASSERT(sink->get_data()[i] < 1.01);
+ CPPUNIT_ASSERT(sink->get_data()[i] > -1.01);
+ }
+ }
+
+ void testInt32()
+ {
+ boost::shared_ptr<SampleFormatConverter<int32_t> > converter (new SampleFormatConverter<int32_t>(1));
+ boost::shared_ptr<VectorSink<int32_t> > sink (new VectorSink<int32_t>());
+ nframes_t frames_output = 0;
+
+ converter->init(frames, D_Tri, 32);
+ converter->add_output (sink);
+
+ ProcessContext<float> pc(random_data, frames, 1);
+ converter->process (pc);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames, frames_output);
+ CPPUNIT_ASSERT (TestUtils::array_filled(sink->get_array(), frames));
+ }
+
+ void testInt24()
+ {
+ boost::shared_ptr<SampleFormatConverter<int32_t> > converter (new SampleFormatConverter<int32_t>(1));
+ boost::shared_ptr<VectorSink<int32_t> > sink (new VectorSink<int32_t>());
+ nframes_t frames_output = 0;
+
+ converter->init(frames, D_Tri, 24);
+ converter->add_output (sink);
+
+ ProcessContext<float> pc(random_data, frames, 1);
+ converter->process (pc);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames, frames_output);
+ CPPUNIT_ASSERT (TestUtils::array_filled(sink->get_array(), frames));
+ }
+
+ void testInt16()
+ {
+ boost::shared_ptr<SampleFormatConverter<int16_t> > converter (new SampleFormatConverter<int16_t>(1));
+ boost::shared_ptr<VectorSink<int16_t> > sink (new VectorSink<int16_t>());
+ nframes_t frames_output = 0;
+
+ converter->init(frames, D_Tri, 16);
+ converter->add_output (sink);
+
+ ProcessContext<float> pc(random_data, frames, 1);
+ converter->process (pc);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames, frames_output);
+ CPPUNIT_ASSERT (TestUtils::array_filled(sink->get_array(), frames));
+ }
+
+ void testUint8()
+ {
+ boost::shared_ptr<SampleFormatConverter<uint8_t> > converter (new SampleFormatConverter<uint8_t>(1));
+ boost::shared_ptr<VectorSink<uint8_t> > sink (new VectorSink<uint8_t>());
+ nframes_t frames_output = 0;
+
+ converter->init(frames, D_Tri, 8);
+ converter->add_output (sink);
+
+ ProcessContext<float> pc(random_data, frames, 1);
+ converter->process (pc);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames, frames_output);
+ CPPUNIT_ASSERT (TestUtils::array_filled(sink->get_array(), frames));
+ }
+
+ void testChannelCount()
+ {
+ boost::shared_ptr<SampleFormatConverter<int32_t> > converter (new SampleFormatConverter<int32_t>(3));
+ boost::shared_ptr<VectorSink<int32_t> > sink (new VectorSink<int32_t>());
+ nframes_t frames_output = 0;
+
+ converter->init(frames, D_Tri, 32);
+ converter->add_output (sink);
+
+ ProcessContext<float> pc(random_data, 4, 1);
+ CPPUNIT_ASSERT_THROW (converter->process (pc), Exception);
+
+ pc.frames() = frames - (frames % 3);
+ converter->process (pc);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (pc.frames(), frames_output);
+ CPPUNIT_ASSERT (TestUtils::array_filled(sink->get_array(), pc.frames()));
+ }
+
+ private:
+
+ float * random_data;
+ nframes_t frames;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION (SampleFormatConverterTest);
+
diff --git a/libs/audiographer/tests/silence_trimmer_test.cc b/libs/audiographer/tests/silence_trimmer_test.cc
new file mode 100644
index 0000000000..16234bec37
--- /dev/null
+++ b/libs/audiographer/tests/silence_trimmer_test.cc
@@ -0,0 +1,196 @@
+#include "utils.h"
+
+#include "audiographer/silence_trimmer.h"
+
+using namespace AudioGrapher;
+
+class SilenceTrimmerTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE (SilenceTrimmerTest);
+ CPPUNIT_TEST (testExceptions);
+ CPPUNIT_TEST (testFullBuffers);
+ CPPUNIT_TEST (testPartialBuffers);
+ CPPUNIT_TEST (testAddSilenceBeginning);
+ CPPUNIT_TEST (testAddSilenceEnd);
+ CPPUNIT_TEST_SUITE_END ();
+
+ public:
+ void setUp()
+ {
+ frames = 128;
+
+ random_data = TestUtils::init_random_data(frames);
+ random_data[0] = 0.5;
+ random_data[frames - 1] = 0.5;
+
+ zero_data = new float[frames];
+ memset(zero_data, 0, frames * sizeof(float));
+
+ half_random_data = TestUtils::init_random_data(frames);
+ memset(half_random_data, 0, (frames / 2) * sizeof(float));
+
+ trimmer.reset (new SilenceTrimmer<float>());
+ sink.reset (new AppendingVectorSink<float>());
+
+ trimmer->set_trim_beginning (true);
+ trimmer->set_trim_end (true);
+ }
+
+ void tearDown()
+ {
+ delete [] random_data;
+ delete [] zero_data;
+ delete [] half_random_data;
+
+ AudioGrapher::Utils::free_resources();
+ }
+
+ void testFullBuffers()
+ {
+ trimmer->add_output (sink);
+ AudioGrapher::Utils::init_zeros<float>(frames / 2);
+
+ {
+ ProcessContext<float> c (zero_data, frames, 1);
+ trimmer->process (c);
+ nframes_t frames_processed = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL ((nframes_t) 0, frames_processed);
+ }
+
+ {
+ ProcessContext<float> c (random_data, frames, 1);
+ trimmer->process (c);
+ nframes_t frames_processed = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames, frames_processed);
+ CPPUNIT_ASSERT (TestUtils::array_equals (sink->get_array(), random_data, frames));
+ }
+
+ {
+ ProcessContext<float> c (zero_data, frames, 1);
+ trimmer->process (c);
+ nframes_t frames_processed = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames, frames_processed);
+ }
+
+ {
+ ProcessContext<float> c (random_data, frames, 1);
+ trimmer->process (c);
+ nframes_t frames_processed = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (3 * frames, frames_processed);
+ CPPUNIT_ASSERT (TestUtils::array_equals (sink->get_array(), random_data, frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals (&sink->get_array()[frames], zero_data, frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals (&sink->get_array()[2 * frames], random_data, frames));
+ }
+
+ {
+ ProcessContext<float> c (zero_data, frames, 1);
+ trimmer->process (c);
+ nframes_t frames_processed = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (3 * frames, frames_processed);
+ }
+ }
+
+ void testPartialBuffers()
+ {
+ trimmer->add_output (sink);
+ AudioGrapher::Utils::init_zeros<float>(frames / 4);
+
+ {
+ ProcessContext<float> c (half_random_data, frames, 1);
+ trimmer->process (c);
+ nframes_t frames_processed = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames / 2, frames_processed);
+ CPPUNIT_ASSERT (TestUtils::array_equals (sink->get_array(), &half_random_data[frames / 2], frames / 2));
+ }
+
+ {
+ ProcessContext<float> c (zero_data, frames, 1);
+ trimmer->process (c);
+ nframes_t frames_processed = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames / 2, frames_processed);
+ }
+
+ {
+ ProcessContext<float> c (half_random_data, frames, 1);
+ trimmer->process (c);
+ nframes_t frames_processed = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (2 * frames + frames / 2, frames_processed);
+ CPPUNIT_ASSERT (TestUtils::array_equals (&sink->get_array()[frames + frames / 2], half_random_data, frames));
+ }
+ }
+
+ void testExceptions()
+ {
+ // TODO more tests here
+
+ trimmer->add_output (sink);
+
+ {
+ ProcessContext<float> c (random_data, frames, 1);
+ trimmer->process (c);
+ }
+
+ {
+ ProcessContext<float> c (zero_data, frames, 1);
+ trimmer->process (c);
+ }
+
+ {
+ // Zeros not inited, so this should throw
+ ProcessContext<float> c (random_data, frames, 1);
+ CPPUNIT_ASSERT_THROW (trimmer->process (c), Exception);
+ }
+ }
+
+ void testAddSilenceBeginning()
+ {
+ trimmer->add_output (sink);
+ AudioGrapher::Utils::init_zeros<float>(frames / 2);
+
+ nframes_t silence = frames / 2;
+ trimmer->add_silence_to_beginning (silence);
+
+ {
+ ProcessContext<float> c (random_data, frames, 1);
+ trimmer->process (c);
+ }
+
+ CPPUNIT_ASSERT (TestUtils::array_equals (sink->get_array(), zero_data, silence));
+ CPPUNIT_ASSERT (TestUtils::array_equals (&sink->get_array()[silence], random_data, frames));
+ }
+
+ void testAddSilenceEnd()
+ {
+ trimmer->add_output (sink);
+ AudioGrapher::Utils::init_zeros<float>(frames / 2);
+
+ nframes_t silence = frames / 3;
+ trimmer->add_silence_to_end (silence);
+
+ {
+ ProcessContext<float> c (random_data, frames, 1);
+ trimmer->process (c);
+ }
+
+ {
+ ProcessContext<float> c (random_data, frames, 1);
+ c.set_flag (ProcessContext<float>::EndOfInput);
+ trimmer->process (c);
+ }
+
+ CPPUNIT_ASSERT (TestUtils::array_equals (sink->get_array(), random_data, frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals (&sink->get_array()[frames], random_data, frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals (&sink->get_array()[frames * 2], zero_data, silence));
+ }
+
+ private:
+ boost::shared_ptr<SilenceTrimmer<float> > trimmer;
+ boost::shared_ptr<AppendingVectorSink<float> > sink;
+
+ float * random_data;
+ float * zero_data;
+ float * half_random_data;
+ nframes_t frames;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION (SilenceTrimmerTest);
diff --git a/libs/audiographer/tests/sndfile_writer_test.cc b/libs/audiographer/tests/sndfile_writer_test.cc
new file mode 100644
index 0000000000..359d456f15
--- /dev/null
+++ b/libs/audiographer/tests/sndfile_writer_test.cc
@@ -0,0 +1,42 @@
+#include "utils.h"
+#include "audiographer/sndfile_writer.h"
+
+using namespace AudioGrapher;
+
+class SndfileWriterTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE (SndfileWriterTest);
+ CPPUNIT_TEST (testProcess);
+ CPPUNIT_TEST_SUITE_END ();
+
+ public:
+ void setUp()
+ {
+ frames = 128;
+ random_data = TestUtils::init_random_data(frames);
+ }
+
+ void tearDown()
+ {
+ delete [] random_data;
+ }
+
+ void testProcess()
+ {
+ uint channels = 2;
+ std::string filename ("test.wav");
+ writer.reset (new SndfileWriter<float>(channels, 44100, SF_FORMAT_WAV | SF_FORMAT_FLOAT, filename));
+ ProcessContext<float> c (random_data, frames, channels);
+ c.set_flag (ProcessContext<float>::EndOfInput);
+ writer->process (c);
+ }
+
+ private:
+ boost::shared_ptr<SndfileWriter<float> > writer;
+
+ float * random_data;
+ nframes_t frames;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION (SndfileWriterTest);
+
diff --git a/libs/audiographer/tests/sr_converter_test.cc b/libs/audiographer/tests/sr_converter_test.cc
new file mode 100644
index 0000000000..59c05806c6
--- /dev/null
+++ b/libs/audiographer/tests/sr_converter_test.cc
@@ -0,0 +1,101 @@
+#include "utils.h"
+#include "audiographer/sr_converter.h"
+
+using namespace AudioGrapher;
+
+class SampleRateConverterTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE (SampleRateConverterTest);
+ CPPUNIT_TEST (testNoConversion);
+ CPPUNIT_TEST (testUpsampleLength);
+ CPPUNIT_TEST (testDownsampleLength);
+ CPPUNIT_TEST_SUITE_END ();
+
+ public:
+ void setUp()
+ {
+ frames = 128;
+ random_data = TestUtils::init_random_data(frames);
+ sink.reset (new AppendingVectorSink<float>());
+ converter.reset (new SampleRateConverter (1));
+ }
+
+ void tearDown()
+ {
+ delete [] random_data;
+ }
+
+ void testNoConversion()
+ {
+ assert (frames % 2 == 0);
+ nframes_t const half_frames = frames / 2;
+ nframes_t frames_output = 0;
+
+ converter->init (44100, 44100);
+ converter->add_output (sink);
+
+ ProcessContext<float> c (random_data, half_frames, 1);
+ converter->process (c);
+ ProcessContext<float> c2 (&random_data[half_frames], half_frames, 1);
+ c2.set_flag (ProcessContext<float>::EndOfInput);
+ converter->process (c2);
+
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames, frames_output);
+
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data, sink->get_array(), frames));
+ }
+
+ void testUpsampleLength()
+ {
+ assert (frames % 2 == 0);
+ nframes_t const half_frames = frames / 2;
+ nframes_t frames_output = 0;
+
+ converter->init (44100, 88200);
+ converter->allocate_buffers (half_frames);
+ converter->add_output (sink);
+
+ ProcessContext<float> c (random_data, half_frames, 1);
+ converter->process (c);
+ ProcessContext<float> c2 (&random_data[half_frames], half_frames, 1);
+ c2.set_flag (ProcessContext<float>::EndOfInput);
+ converter->process (c2);
+
+ frames_output = sink->get_data().size();
+ nframes_t tolerance = 3;
+ CPPUNIT_ASSERT (2 * frames - tolerance < frames_output && frames_output < 2 * frames + tolerance);
+ }
+
+ void testDownsampleLength()
+ {
+ assert (frames % 2 == 0);
+ nframes_t const half_frames = frames / 2;
+ nframes_t frames_output = 0;
+
+ converter->init (88200, 44100);
+ converter->allocate_buffers (half_frames);
+ converter->add_output (sink);
+
+ ProcessContext<float> c (random_data, half_frames, 1);
+ converter->process (c);
+ ProcessContext<float> c2 (&random_data[half_frames], half_frames, 1);
+ c2.set_flag (ProcessContext<float>::EndOfInput);
+ converter->process (c2);
+
+ frames_output = sink->get_data().size();
+ nframes_t tolerance = 3;
+ CPPUNIT_ASSERT (half_frames - tolerance < frames_output && frames_output < half_frames + tolerance);
+ }
+
+
+ private:
+ boost::shared_ptr<SampleRateConverter > converter;
+ boost::shared_ptr<AppendingVectorSink<float> > sink;
+
+ float * random_data;
+ nframes_t frames;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION (SampleRateConverterTest);
+
diff --git a/libs/audiographer/tests/test_runner.cc b/libs/audiographer/tests/test_runner.cc
new file mode 100644
index 0000000000..6fed393dc8
--- /dev/null
+++ b/libs/audiographer/tests/test_runner.cc
@@ -0,0 +1,14 @@
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <cppunit/ui/text/TestRunner.h>
+
+#include <glibmm/thread.h>
+
+int main()
+{
+ Glib::thread_init();
+ CppUnit::TextUi::TestRunner runner;
+ CppUnit::TestFactoryRegistry &registry = CppUnit::TestFactoryRegistry::getRegistry();
+ runner.addTest (registry.makeTest());
+ runner.run();
+ return 0;
+}
diff --git a/libs/audiographer/tests/threader_test.cc b/libs/audiographer/tests/threader_test.cc
new file mode 100644
index 0000000000..ac5588d79c
--- /dev/null
+++ b/libs/audiographer/tests/threader_test.cc
@@ -0,0 +1,155 @@
+#include "utils.h"
+#include "audiographer/threader.h"
+
+using namespace AudioGrapher;
+
+class ThreaderTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE (ThreaderTest);
+ CPPUNIT_TEST (testProcess);
+ CPPUNIT_TEST (testRemoveOutput);
+ CPPUNIT_TEST (testClearOutputs);
+ CPPUNIT_TEST (testExceptions);
+ CPPUNIT_TEST_SUITE_END ();
+
+ public:
+ void setUp()
+ {
+ frames = 128;
+ random_data = TestUtils::init_random_data (frames, 1.0);
+
+ zero_data = new float[frames];
+ memset (zero_data, 0, frames * sizeof(float));
+
+ thread_pool = new Glib::ThreadPool (3);
+ threader.reset (new Threader<float> (*thread_pool));
+
+ sink_a.reset (new VectorSink<float>());
+ sink_b.reset (new VectorSink<float>());
+ sink_c.reset (new VectorSink<float>());
+ sink_d.reset (new VectorSink<float>());
+ sink_e.reset (new VectorSink<float>());
+ sink_f.reset (new VectorSink<float>());
+
+ throwing_sink.reset (new ThrowingSink<float>());
+ }
+
+ void tearDown()
+ {
+ delete [] random_data;
+ delete [] zero_data;
+
+ thread_pool->shutdown();
+ delete thread_pool;
+ }
+
+ void testProcess()
+ {
+ threader->add_output (sink_a);
+ threader->add_output (sink_b);
+ threader->add_output (sink_c);
+ threader->add_output (sink_d);
+ threader->add_output (sink_e);
+ threader->add_output (sink_f);
+
+ ProcessContext<float> c (random_data, frames, 1);
+ threader->process (c);
+
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_a->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_b->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_c->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_d->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_e->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_f->get_array(), frames));
+ }
+
+ void testRemoveOutput()
+ {
+ threader->add_output (sink_a);
+ threader->add_output (sink_b);
+ threader->add_output (sink_c);
+ threader->add_output (sink_d);
+ threader->add_output (sink_e);
+ threader->add_output (sink_f);
+
+ ProcessContext<float> c (random_data, frames, 1);
+ threader->process (c);
+
+ // Remove a, b and f
+ threader->remove_output (sink_a);
+ threader->remove_output (sink_b);
+ threader->remove_output (sink_f);
+
+ ProcessContext<float> zc (zero_data, frames, 1);
+ threader->process (zc);
+
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_a->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_b->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(zero_data, sink_c->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(zero_data, sink_d->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(zero_data, sink_e->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_f->get_array(), frames));
+ }
+
+ void testClearOutputs()
+ {
+ threader->add_output (sink_a);
+ threader->add_output (sink_b);
+ threader->add_output (sink_c);
+ threader->add_output (sink_d);
+ threader->add_output (sink_e);
+ threader->add_output (sink_f);
+
+ ProcessContext<float> c (random_data, frames, 1);
+ threader->process (c);
+
+ threader->clear_outputs();
+ ProcessContext<float> zc (zero_data, frames, 1);
+ threader->process (zc);
+
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_a->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_b->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_c->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_d->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_e->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_f->get_array(), frames));
+ }
+
+ void testExceptions()
+ {
+ threader->add_output (sink_a);
+ threader->add_output (sink_b);
+ threader->add_output (sink_c);
+ threader->add_output (throwing_sink);
+ threader->add_output (sink_e);
+ threader->add_output (throwing_sink);
+
+ ProcessContext<float> c (random_data, frames, 1);
+ CPPUNIT_ASSERT_THROW (threader->process (c), Exception);
+
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_a->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_b->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_c->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_e->get_array(), frames));
+ }
+
+ private:
+ Glib::ThreadPool * thread_pool;
+
+ boost::shared_ptr<Threader<float> > threader;
+ boost::shared_ptr<VectorSink<float> > sink_a;
+ boost::shared_ptr<VectorSink<float> > sink_b;
+ boost::shared_ptr<VectorSink<float> > sink_c;
+ boost::shared_ptr<VectorSink<float> > sink_d;
+ boost::shared_ptr<VectorSink<float> > sink_e;
+ boost::shared_ptr<VectorSink<float> > sink_f;
+
+ boost::shared_ptr<ThrowingSink<float> > throwing_sink;
+
+ float * random_data;
+ float * zero_data;
+ nframes_t frames;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION (ThreaderTest);
+
diff --git a/libs/audiographer/tests/utils.h b/libs/audiographer/tests/utils.h
new file mode 100644
index 0000000000..6b6a1fea0f
--- /dev/null
+++ b/libs/audiographer/tests/utils.h
@@ -0,0 +1,119 @@
+#ifndef AUDIOGRAPHER_TESTS_UTILS_H
+#define AUDIOGRAPHER_TESTS_UTILS_H
+
+// Includes we want almost always
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <boost/shared_ptr.hpp>
+
+// includes used in this file
+
+#include "audiographer/sink.h"
+#include "audiographer/exception.h"
+
+#include <vector>
+#include <cstring>
+#include <cstdlib>
+#include <ctime>
+
+using AudioGrapher::nframes_t;
+
+struct TestUtils
+{
+ template<typename T>
+ static bool array_equals (T const * a, T const * b, nframes_t frames)
+ {
+ for (nframes_t i = 0; i < frames; ++i) {
+ if (a[i] != b[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ template<typename T>
+ static bool array_filled (T const * array, nframes_t frames)
+ {
+ for (nframes_t i = 0; i < frames; ++i) {
+ if (array[i] == static_cast<T> (0.0)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /// Generate random data, all samples guaranteed not to be 0.0, 1.0 or -1.0
+ static float * init_random_data (nframes_t frames, float range = 1.0)
+ {
+ unsigned int const granularity = 4096;
+ float * data = new float[frames];
+ srand (std::time (NULL));
+
+ for (nframes_t i = 0; i < frames; ++i) {
+ do {
+ int biased_int = (rand() % granularity) - (granularity / 2);
+ data[i] = (range * biased_int) / granularity;
+ } while (data[i] == 0.0 || data[i] == 1.0 || data[i] == -1.0);
+ }
+ return data;
+ }
+};
+
+template<typename T>
+class VectorSink : public AudioGrapher::Sink<T>
+{
+ public:
+ virtual void process (AudioGrapher::ProcessContext<T> const & c)
+ {
+ data.resize (c.frames());
+ memcpy (&data[0], c.data(), c.frames() * sizeof(T));
+ }
+
+ void process (AudioGrapher::ProcessContext<T> & c) { AudioGrapher::Sink<T>::process (c); }
+ using AudioGrapher::Sink<T>::process;
+
+ std::vector<T> const & get_data() const { return data; }
+ T const * get_array() const { return &data[0]; }
+ void reset() { data.clear(); }
+
+ protected:
+ std::vector<T> data;
+
+};
+
+template<typename T>
+class AppendingVectorSink : public VectorSink<T>
+{
+ public:
+ void process (AudioGrapher::ProcessContext<T> const & c)
+ {
+ std::vector<T> & data (VectorSink<T>::data);
+ data.resize (total_frames + c.frames());
+ memcpy (&data[total_frames], c.data(), c.frames() * sizeof(T));
+ total_frames += c.frames();
+ }
+ using AudioGrapher::Sink<T>::process;
+
+ void reset ()
+ {
+ total_frames = 0;
+ VectorSink<T>::reset();
+ }
+
+ private:
+ nframes_t total_frames;
+};
+
+
+template<typename T>
+class ThrowingSink : public AudioGrapher::Sink<T>
+{
+ public:
+ void process (AudioGrapher::ProcessContext<T> const &)
+ {
+ throw AudioGrapher::Exception(*this, "ThrowingSink threw!");
+ }
+ using AudioGrapher::Sink<T>::process;
+};
+
+#endif // AUDIOGRAPHER_TESTS_UTILS_H