diff --git a/meson.build b/meson.build index cce1856..b0eec3b 100644 --- a/meson.build +++ b/meson.build @@ -2,7 +2,7 @@ project( 'Rubber Band Library', 'c', 'cpp', - version: '2.0.2', + version: '3.0.0', license: 'GPL-2.0-or-later', default_options: [ 'cpp_std=c++11', @@ -89,6 +89,7 @@ unit_test_sources = [ 'src/test/TestResampler.cpp', 'src/test/TestVectorOpsComplex.cpp', 'src/test/TestVectorOps.cpp', + 'src/test/TestSignalBits.cpp', 'src/test/test.cpp', ] @@ -738,6 +739,8 @@ if have_boost_unit_test unit_tests, args: [ '--run_test=TestVectorOps', general_test_args ]) test('VectorOpsComplex', unit_tests, args: [ '--run_test=TestVectorOpsComplex', general_test_args ]) + test('SignalBits', + unit_tests, args: [ '--run_test=TestSignalBits', general_test_args ]) else target_summary += { 'Unit tests': false } message('Not building unit tests: boost_unit_test_framework dependency not found') diff --git a/src/common/MovingMedian.h b/src/common/MovingMedian.h index 8d742ab..5c5af5b 100644 --- a/src/common/MovingMedian.h +++ b/src/common/MovingMedian.h @@ -205,15 +205,15 @@ public: } // Convenience function that applies a given filter to an array - // in-place. Array must have length equal to getSize(). Modifies - // both the filter and the array. + // in-place. Array has length n. Modifies both the filter and the + // array. // - static void filter(MovingMedian &mm, T *v) { - int n = mm.getSize(); - int lag = n / 2; + static void filter(MovingMedian &mm, T *v, int n) { + int fn = mm.getSize(); + int lag = fn / 2; mm.reset(); for (int i = 0; i < lag; ++i) { - mm.push(v[i]); + if (i < n) mm.push(v[i]); } for (int i = lag; i < n; ++i) { mm.push(v[i]); @@ -224,6 +224,12 @@ public: v[i-lag] = mm.get(); } } + + // As above but with a vector argument + // + static void filter(MovingMedian &mm, std::vector &v) { + filter(mm, v.data(), v.size()); + } private: MovingMedianStack m_mm; diff --git a/src/finer/BinClassifier.h b/src/finer/BinClassifier.h index 94b1fa6..350db7c 100644 --- a/src/finer/BinClassifier.h +++ b/src/finer/BinClassifier.h @@ -109,7 +109,7 @@ public: } v_copy(m_vf, mag, n); - MovingMedian::filter(*m_vFilter, m_vf); + MovingMedian::filter(*m_vFilter, m_vf, n); if (m_parameters.horizontalFilterLag > 0) { double *lagged = m_vfQueue.readOne(); diff --git a/src/finer/BinSegmenter.h b/src/finer/BinSegmenter.h index 00fb1e4..31ce08a 100644 --- a/src/finer/BinSegmenter.h +++ b/src/finer/BinSegmenter.h @@ -70,7 +70,7 @@ public: m_numeric[i] = 2; break; } } - MovingMedian::filter(m_classFilter, m_numeric.data()); + MovingMedian::filter(m_classFilter, m_numeric.data(), m_numeric.size()); double f0 = 0.0; for (int i = 1; i < n; ++i) { if (m_numeric[i] != 1) { diff --git a/src/test/TestSignalBits.cpp b/src/test/TestSignalBits.cpp new file mode 100644 index 0000000..58b8c54 --- /dev/null +++ b/src/test/TestSignalBits.cpp @@ -0,0 +1,137 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rubber Band Library + An audio time-stretching and pitch-shifting library. + Copyright 2007-2022 Particular Programs Ltd. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. + + Alternatively, if you have a valid commercial licence for the + Rubber Band Library obtained by agreement with the copyright + holders, you may redistribute and/or modify it under the terms + described in that licence. + + If you wish to distribute code using the Rubber Band Library + under terms other than those of the GNU General Public License, + you must obtain a valid commercial licence before doing so. +*/ + +#define BOOST_TEST_DYN_LINK +#include + +#include "../common/MovingMedian.h" +#include "../finer/Peak.h" + +using namespace RubberBand; +using namespace std; + +BOOST_AUTO_TEST_SUITE(TestSignalBits) + +#define COMPARE_N(a, b, n) \ + for (int cmp_i = 0; cmp_i < n; ++cmp_i) { \ + BOOST_CHECK_SMALL(a[cmp_i] - b[cmp_i], 1e-14); \ + } + +#define COMPARE_INT_N(a, b, n) \ + for (int cmp_i = 0; cmp_i < n; ++cmp_i) { \ + BOOST_CHECK_EQUAL(a[cmp_i], b[cmp_i]); \ + } + +BOOST_AUTO_TEST_CASE(moving_median_simple_3) +{ + MovingMedian mm(3); + vector arr { 1.0, 2.0, 3.0 }; + vector expected { 2.0, 2.0, 3.0 }; + MovingMedian::filter(mm, arr); + for (int i = 0; i < arr.size(); ++i) { + cerr << arr[i] << endl; + } + COMPARE_N(arr, expected, 3); +} + +BOOST_AUTO_TEST_CASE(moving_median_simple_4) +{ + MovingMedian mm(4); + vector arr { 1.0, 2.0, 3.0, 4.0 }; + vector expected { 2.0, 2.0, 3.0, 3.0 }; + MovingMedian::filter(mm, arr); + for (int i = 0; i < arr.size(); ++i) { + cerr << arr[i] << endl; + } + COMPARE_N(arr, expected, 4); +} + +BOOST_AUTO_TEST_CASE(moving_median_simple_5_4) +{ + MovingMedian mm(5); + vector arr { 1.2, 0.6, 1.0e-6, 1.0e-6 }; + vector expected { 0.6, 1.2, 1.2, 0.6 }; + MovingMedian::filter(mm, arr); + for (int i = 0; i < arr.size(); ++i) { + cerr << arr[i] << endl; + } + COMPARE_N(arr, expected, 4); +} + +BOOST_AUTO_TEST_CASE(moving_median_order_1) +{ + MovingMedian mm(1); + vector arr { 1.2, 0.6, 1.0e-6, 1.0e-6 }; + vector expected = arr; + MovingMedian::filter(mm, arr); + for (int i = 0; i < arr.size(); ++i) { + cerr << arr[i] << endl; + } + COMPARE_N(arr, expected, 4); +} + +BOOST_AUTO_TEST_CASE(moving_median_n_1) +{ + MovingMedian mm(6); + vector arr { 1.0 }; + vector expected = arr; + MovingMedian::filter(mm, arr); + for (int i = 0; i < arr.size(); ++i) { + cerr << arr[i] << endl; + } + COMPARE_N(arr, expected, 1); +} + +BOOST_AUTO_TEST_CASE(peakpick_nearest_2_1) +{ + Peak pp(1); + vector in { -0.1 }; + vector out(1); + vector expected { 0 }; + pp.findNearestAndNextPeaks(in.data(), 2, out.data(), nullptr); + COMPARE_INT_N(out, expected, 1); +} + +BOOST_AUTO_TEST_CASE(peakpick_nearest_2_5) +{ + Peak pp(5); + vector in { -0.3, -0.1, -0.2, 1.0, -0.3 }; + vector out(5); + vector expected { 3, 3, 3, 3, 3 }; + pp.findNearestAndNextPeaks(in.data(), 2, out.data(), nullptr); + COMPARE_INT_N(out, expected, 5); +} + +BOOST_AUTO_TEST_CASE(peakpick_nearest_2_12) +{ + Peak pp(12); + vector in { -0.3, -0.1, -0.2, 1.0, -0.3, -0.5, + -0.5, -0.4, -0.1, -0.1, -0.2, -0.3 }; + vector out(12); + vector expected { 3, 3, 3, 3, 3, 3, 8, 8, 8, 8, 8, 8 }; + pp.findNearestAndNextPeaks(in.data(), 2, out.data(), nullptr); + COMPARE_INT_N(out, expected, 12); +} + +BOOST_AUTO_TEST_SUITE_END() +