summaryrefslogtreecommitdiff
path: root/libs/audiographer/tests
diff options
context:
space:
mode:
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