changeset 39:822cf7b8e070

Start separating out PeakInterpolator & writing test for it
author Chris Cannam
date Thu, 19 Jul 2012 18:10:50 +0100
parents 944898c2e14e
children 8f56ef28b0b1
files CepstralPitchTracker.cpp Makefile.inc PeakInterpolator.cpp PeakInterpolator.h libmain.cpp test/Makefile test/TestPeakInterpolator.cpp
diffstat 7 files changed, 237 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/CepstralPitchTracker.cpp	Thu Jul 19 14:05:22 2012 +0100
+++ b/CepstralPitchTracker.cpp	Thu Jul 19 18:10:50 2012 +0100
@@ -59,13 +59,13 @@
 string
 CepstralPitchTracker::getIdentifier() const
 {
-    return "cepstrum-pitch";
+    return "cepstral-pitchtracker";
 }
 
 string
 CepstralPitchTracker::getName() const
 {
-    return "Cepstrum Pitch Tracker";
+    return "Cepstral Pitch Tracker";
 }
 
 string
@@ -415,7 +415,7 @@
     if (nextPeakVal != 0.0) {
         confidence = (maxval - nextPeakVal) * 10.0;
         if (magmean < threshold) confidence = 0.0;
-        std::cerr << "magmean = " << magmean << ", confidence = " << confidence << std::endl;
+//        std::cerr << "magmean = " << magmean << ", confidence = " << confidence << std::endl;
     }
 
     NoteHypothesis::Estimate e;
--- a/Makefile.inc	Thu Jul 19 14:05:22 2012 +0100
+++ b/Makefile.inc	Thu Jul 19 18:10:50 2012 +0100
@@ -6,13 +6,15 @@
 
 CFLAGS		:= $(CFLAGS) 
 CXXFLAGS	:= $(CXXFLAGS) 
-LDFLAGS		:= $(LDFLAGS)
+LDFLAGS		:= $(LDFLAGS) -lvamp-sdk
 
 HEADERS := CepstralPitchTracker.h \
-	   NoteHypothesis.h
+	   NoteHypothesis.h \
+	   PeakInterpolator.h
 
 SOURCES := CepstralPitchTracker.cpp \
 	   NoteHypothesis.cpp \
+	   PeakInterpolator.cpp \
            libmain.cpp
 
 OBJECTS := $(SOURCES:.cpp=.o)
@@ -22,12 +24,17 @@
 
 $(PLUGIN):	$(OBJECTS)
 		$(CXX) -o $@ $^ $(LDFLAGS)
+		$(MAKE) -C test
 
 clean:		
-		rm $(OBJECTS)
+		rm -f $(OBJECTS)
+		$(MAKE) -C test clean
 
 distclean:	clean
-		rm $(PLUGIN)
+		rm -f $(PLUGIN)
+		$(MAKE) -C test distclean
 
 libmain.o:	$(HEADERS) $(SOURCES)
-SimpleCepstrum.o:	$(HEADERS) $(SOURCES)
+CepstralPitchTracker.o:	$(HEADERS) $(SOURCES)
+NoteHypothesis.o:	$(HEADERS) $(SOURCES)
+PeakInterpolator.o:	$(HEADERS) $(SOURCES)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PeakInterpolator.cpp	Thu Jul 19 18:10:50 2012 +0100
@@ -0,0 +1,81 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+/*
+    This file is Copyright (c) 2012 Chris Cannam
+  
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "PeakInterpolator.h"
+
+static double cubicInterpolate(const double y[4], double x)
+{
+    double a0 = y[3] - y[2] - y[0] + y[1];
+    double a1 = y[0] - y[1] - a0;
+    double a2 = y[2] - y[0];
+    double a3 = y[1];
+    return
+        a0 * x * x * x +
+        a1 * x * x +
+        a2 * x +
+        a3;
+}
+
+double
+PeakInterpolator::findPeakLocation(const double *data, int size, int peakIndex)
+{
+    if (peakIndex < 2 || peakIndex > size - 3) {
+        return peakIndex;
+    }
+
+    double maxval = 0.0;
+    double location = peakIndex;
+
+    const int divisions = 10;
+    double y[4];
+
+    y[0] = data[peakIndex-1];
+    y[1] = data[peakIndex];
+    y[2] = data[peakIndex+1];
+    y[3] = data[peakIndex+2];
+    for (int i = 0; i < divisions; ++i) {
+        double probe = double(i) / double(divisions);
+        double value = cubicInterpolate(y, probe);
+        if (value > maxval) {
+            maxval = value; 
+            location = peakIndex + probe;
+        }
+    }
+
+    y[3] = y[2];
+    y[2] = y[1];
+    y[1] = y[0];
+    y[0] = data[peakIndex-2];
+    for (int i = 0; i < divisions; ++i) {
+        double probe = double(i) / double(divisions);
+        double value = cubicInterpolate(y, probe);
+        if (value > maxval) {
+            maxval = value; 
+            location = peakIndex - 1 + probe;
+        }
+    }
+
+    return location;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PeakInterpolator.h	Thu Jul 19 18:10:50 2012 +0100
@@ -0,0 +1,42 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+/*
+    This file is Copyright (c) 2012 Chris Cannam
+  
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef _PEAK_INTERPOLATOR_H_
+#define _PEAK_INTERPOLATOR_H_
+
+class PeakInterpolator
+{
+public:
+    PeakInterpolator() { } 
+    ~PeakInterpolator() { }
+
+    /**
+     * Return the interpolated location (i.e. between sample point
+     * indices) of the peak whose sample is found at peakIndex in a
+     * series of size samples.
+     */
+    double findPeakLocation(const double *data, int size, int peakIndex);
+};
+
+#endif
--- a/libmain.cpp	Thu Jul 19 14:05:22 2012 +0100
+++ b/libmain.cpp	Thu Jul 19 18:10:50 2012 +0100
@@ -35,7 +35,7 @@
     if (version < 1) return 0;
 
     switch (index) {
-    case  1: return cepitchPluginAdapter.getDescriptor();
+    case  0: return cepitchPluginAdapter.getDescriptor();
     default: return 0;
     }
 }
--- a/test/Makefile	Thu Jul 19 14:05:22 2012 +0100
+++ b/test/Makefile	Thu Jul 19 18:10:50 2012 +0100
@@ -5,7 +5,26 @@
 CXXFLAGS	:= $(CXXFLAGS) -I..
 LDFLAGS		:= $(LDFLAGS) -lvamp-sdk -lboost_unit_test_framework
 
-SUPER_OBJECTS	:= ../NoteHypothesis.o
+SUPER_OBJECTS	:= ../NoteHypothesis.o ../PeakInterpolator.o
 
-tests:	TestNoteHypothesis.o
+OBJECTS		:= TestNoteHypothesis.o TestPeakInterpolator.o
+
+TESTS		:= test-notehypothesis test-peakinterpolator
+
+all:	run-tests
+
+test-notehypothesis:	TestNoteHypothesis.o
 	$(CXX) -o $@ $^ $(SUPER_OBJECTS) $(LDFLAGS)
+
+test-peakinterpolator:	TestPeakInterpolator.o
+	$(CXX) -o $@ $^ $(SUPER_OBJECTS) $(LDFLAGS)
+
+run-tests:	$(TESTS)
+		for t in $(TESTS); do ./"$$t" || exit 1; done
+
+clean:	
+	rm -f $(OBJECTS)
+
+distclean:	clean
+	rm -f $(TESTS)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/TestPeakInterpolator.cpp	Thu Jul 19 18:10:50 2012 +0100
@@ -0,0 +1,77 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+/*
+    This file is Copyright (c) 2012 Chris Cannam
+  
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "PeakInterpolator.h"
+
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_MAIN
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_SUITE(TestPeakInterpolator)
+
+BOOST_AUTO_TEST_CASE(peakAtSample_N3)
+{
+    double data[] = { 0.0, 1.0, 0.0 };
+    PeakInterpolator p;
+    double result = p.findPeakLocation(data, 3, 1);
+    BOOST_CHECK_EQUAL(result, 1.0);
+}
+
+BOOST_AUTO_TEST_CASE(peakAtSample_N4)
+{
+    double data[] = { 0.0, 1.0, 2.0, 0.0 };
+    PeakInterpolator p;
+    double result = p.findPeakLocation(data, 4, 2);
+    //!!! Actually, this isn't certain at all I think? It could quite
+    //!!! reasonably be smaller than 2.0
+    BOOST_CHECK_EQUAL(result, 2.0);
+}
+
+BOOST_AUTO_TEST_CASE(peakAtSample_N5)
+{
+    double data[] = { 0.0, 1.0, 2.0, 1.0, 0.0 };
+    PeakInterpolator p;
+    double result = p.findPeakLocation(data, 5, 2);
+    BOOST_CHECK_EQUAL(result, 2.0);
+}
+
+BOOST_AUTO_TEST_CASE(flat)
+{
+    double data[] = { 1.0, 1.0, 1.0, 1.0, 1.0 };
+    PeakInterpolator p;
+    double result = p.findPeakLocation(data, 5, 2);
+    BOOST_CHECK_EQUAL(result, 2.0);
+}
+
+BOOST_AUTO_TEST_CASE(multiPeak)
+{
+    double data[] = { 1.0, 2.0, 1.0, 2.0, 1.0 };
+    PeakInterpolator p;
+    double result = p.findPeakLocation(data, 5, 3);
+    BOOST_CHECK_EQUAL(result, 3.0);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+