changeset 66:7ad142c710c6 parameters

Add some parameters
author Chris Cannam
date Fri, 30 Aug 2013 15:35:49 +0100
parents 9e414ae1f2fb
children dd5ab48fd58a
files AgentFeeder.cpp AgentFeeder.h CepstralPitchTracker.cpp CepstralPitchTracker.h Makefile.inc NoteHypothesis.cpp NoteHypothesis.h test/TestAgentFeeder.cpp test/TestNoteHypothesis.cpp
diffstat 9 files changed, 136 insertions(+), 36 deletions(-) [+]
line wrap: on
line diff
--- a/AgentFeeder.cpp	Thu Dec 06 17:27:16 2012 +0000
+++ b/AgentFeeder.cpp	Fri Aug 30 15:35:49 2013 +0100
@@ -76,7 +76,7 @@
     }
     
     if (!swallowed) {
-        NoteHypothesis h;
+        NoteHypothesis h(m_slack);
         if (h.accept(e)) {
             newCandidates.push_back(h);
         }
--- a/AgentFeeder.h	Thu Dec 06 17:27:16 2012 +0000
+++ b/AgentFeeder.h	Fri Aug 30 15:35:49 2013 +0100
@@ -49,7 +49,11 @@
 class AgentFeeder
 {
 public:
-    AgentFeeder() : m_haveCurrent(false) { }
+    AgentFeeder(float slack) : 
+        m_slack(slack),
+        m_current(slack), 
+        m_haveCurrent(false)
+    { }
 
     void feed(NoteHypothesis::Estimate);
     void finish();
@@ -63,6 +67,7 @@
     Hypotheses reap(Hypotheses);
 
 private:
+    float m_slack;
     Hypotheses m_candidates;
     NoteHypothesis m_current;
     bool m_haveCurrent;
--- a/CepstralPitchTracker.cpp	Thu Dec 06 17:27:16 2012 +0000
+++ b/CepstralPitchTracker.cpp	Fri Aug 30 15:35:49 2013 +0100
@@ -50,6 +50,9 @@
     m_fmin(50),
     m_fmax(900),
     m_vflen(1),
+    m_slack(40),
+    m_sensitivity(10),
+    m_threshold(0.1),
     m_binFrom(0),
     m_binTo(0),
     m_bins(0),
@@ -92,7 +95,7 @@
 {
     // Increment this each time you release a version that behaves
     // differently from the previous one
-    return 1;
+    return 2;
 }
 
 string
@@ -135,18 +138,58 @@
 CepstralPitchTracker::getParameterDescriptors() const
 {
     ParameterList list;
+
+    ParameterDescriptor d;
+    d.identifier = "sensitivity";
+    d.name = "Sensitivity";
+    d.description = "Sensitivity of the voicing detector";
+    d.unit = "";
+    d.minValue = 0;
+    d.maxValue = 100;
+    d.defaultValue = 10;
+    d.isQuantized = true;
+    d.quantizeStep = 1;
+    list.push_back(d);
+
+    d.identifier = "slack";
+    d.name = "Slack";
+    d.description = "Maximum permissible length of voicing gap for a continuous note";
+    d.unit = "ms";
+    d.minValue = 0;
+    d.maxValue = 200;
+    d.defaultValue = 40;
+    d.isQuantized = true;
+    d.quantizeStep = 1;
+    list.push_back(d);
+    
+    d.identifier = "threshold";
+    d.name = "Silence threshold";
+    d.description = "Threshold for silence detection";
+    d.unit = ""; //!!! todo: convert this threshold to a meaningful unit!
+    d.minValue = 0;
+    d.maxValue = 0.5;
+    d.defaultValue = 0.1;
+    d.isQuantized = false;
+    list.push_back(d);
+    
     return list;
 }
 
 float
 CepstralPitchTracker::getParameter(string identifier) const
 {
+    if (identifier == "sensitivity") return m_sensitivity;
+    else if (identifier == "slack") return m_slack;
+    else if (identifier == "threshold") return m_threshold;
     return 0.f;
 }
 
 void
 CepstralPitchTracker::setParameter(string identifier, float value) 
 {
+    if (identifier == "sensitivity") m_sensitivity = value;
+    else if (identifier == "slack") m_slack = value;
+    else if (identifier == "threshold") m_threshold = value;
 }
 
 CepstralPitchTracker::ProgramList
@@ -204,6 +247,20 @@
     d.hasDuration = true;
     outputs.push_back(d);
 
+    d.identifier = "raw";
+    d.name = "Raw frequencies";
+    d.description = "Raw peak frequencies from cepstrum, including unvoiced segments";
+    d.unit = "Hz";
+    d.hasFixedBinCount = true;
+    d.binCount = 1;
+    d.hasKnownExtents = true;
+    d.minValue = m_fmin;
+    d.maxValue = m_fmax;
+    d.isQuantized = false;
+    d.sampleType = OutputDescriptor::OneSamplePerStep;
+    d.hasDuration = false;
+    outputs.push_back(d);
+
     return outputs;
 }
 
@@ -243,7 +300,7 @@
 CepstralPitchTracker::reset()
 {
     delete m_feeder;
-    m_feeder = new AgentFeeder();
+    m_feeder = new AgentFeeder(m_slack);
     m_nAccepted = 0;
 }
 
@@ -327,12 +384,18 @@
     double cimax = pi.findPeakLocation(data, m_bins, maxbin);
     double peakfreq = m_inputSampleRate / (cimax + m_binFrom);
 
+    FeatureSet fs;
+    Feature rawf;
+    rawf.hasTimestamp = false;
+    rawf.hasDuration = false;
+    rawf.values.push_back(peakfreq);
+    fs[2].push_back(rawf);
+
     double confidence = 0.0;
-    double threshold = 0.1; // for magmean
 
     if (nextPeakVal != 0.0) {
-        confidence = (maxval - nextPeakVal) * 10.0;
-        if (magmean < threshold) confidence = 0.0;
+        confidence = (maxval - nextPeakVal) * m_sensitivity;
+        if (magmean < m_threshold) confidence = 0.0;
     }
 
     delete[] data;
@@ -344,7 +407,6 @@
 
     m_feeder->feed(e);
 
-    FeatureSet fs;
     addNewFeatures(fs);
     return fs;
 }
--- a/CepstralPitchTracker.h	Thu Dec 06 17:27:16 2012 +0000
+++ b/CepstralPitchTracker.h	Fri Aug 30 15:35:49 2013 +0100
@@ -76,6 +76,10 @@
     float m_fmax;
     int m_vflen;
 
+    float m_slack;
+    float m_sensitivity;
+    float m_threshold;
+
     int m_binFrom;
     int m_binTo;
     int m_bins; // count of "interesting" bins, those returned in m_cepOutput
--- a/Makefile.inc	Thu Dec 06 17:27:16 2012 +0000
+++ b/Makefile.inc	Fri Aug 30 15:35:49 2013 +0100
@@ -73,13 +73,16 @@
 
 # DO NOT DELETE
 
+AgentFeeder.o: AgentFeeder.h NoteHypothesis.h
 CepstralPitchTracker.o: CepstralPitchTracker.h NoteHypothesis.h Cepstrum.h
-CepstralPitchTracker.o: MeanFilter.h PeakInterpolator.h
-libmain.o: CepstralPitchTracker.h NoteHypothesis.h
+CepstralPitchTracker.o: MeanFilter.h PeakInterpolator.h AgentFeeder.h
 NoteHypothesis.o: NoteHypothesis.h
 PeakInterpolator.o: PeakInterpolator.h
+libmain.o: CepstralPitchTracker.h NoteHypothesis.h
+test/TestAgentFeeder.o: AgentFeeder.h NoteHypothesis.h
 test/TestCepstrum.o: Cepstrum.h
 test/TestMeanFilter.o: MeanFilter.h
 test/TestNoteHypothesis.o: NoteHypothesis.h
 test/TestPeakInterpolator.o: PeakInterpolator.h
+AgentFeeder.o: NoteHypothesis.h
 CepstralPitchTracker.o: NoteHypothesis.h
--- a/NoteHypothesis.cpp	Thu Dec 06 17:27:16 2012 +0000
+++ b/NoteHypothesis.cpp	Fri Aug 30 15:35:49 2013 +0100
@@ -28,9 +28,10 @@
 
 using Vamp::RealTime;
 
-NoteHypothesis::NoteHypothesis()
+NoteHypothesis::NoteHypothesis(float slack) :
+    m_state(New),
+    m_slack(slack)
 {
-    m_state = New;
 }
 
 NoteHypothesis::~NoteHypothesis()
@@ -65,7 +66,7 @@
 {
     if (m_pending.empty()) return false;
     return ((s.time - m_pending[m_pending.size()-1].time) > 
-            RealTime::fromMilliseconds(40));
+            RealTime::fromMilliseconds(m_slack));
 }
 
 bool 
--- a/NoteHypothesis.h	Thu Dec 06 17:27:16 2012 +0000
+++ b/NoteHypothesis.h	Fri Aug 30 15:35:49 2013 +0100
@@ -57,10 +57,15 @@
     };
     
     /**
-     * Construct an empty hypothesis. This will be in New state and
-     * will provisionally accept any estimate.
+     * Construct an empty hypothesis. The given slack (in
+     * milliseconds) determines how long the hypothesis is prepared to
+     * tolerate unacceptable estimates in between accepted estimates
+     * before it becomes rejected. A reasonable default is 40ms.
+     *
+     * This hypothesis will be in New state and will provisionally
+     * accept any estimate.
      */
-    NoteHypothesis();
+    NoteHypothesis(float slack);
 
     /**
      * Destroy the hypothesis
@@ -134,6 +139,7 @@
     
     State m_state;
     Estimates m_pending;
+    float m_slack;
 };
 
 #endif
--- a/test/TestAgentFeeder.cpp	Thu Dec 06 17:27:16 2012 +0000
+++ b/test/TestAgentFeeder.cpp	Fri Aug 30 15:35:49 2013 +0100
@@ -35,11 +35,13 @@
 
 typedef NoteHypothesis::Estimate Est;
 
+#define DEFAULT_SLACK_MS 40
+
 BOOST_AUTO_TEST_SUITE(TestAgentFeeder)
 
 BOOST_AUTO_TEST_CASE(feederEmpty)
 {
-    AgentFeeder f;
+    AgentFeeder f(DEFAULT_SLACK_MS);
     f.finish();
     AgentFeeder::Hypotheses accepted = f.getAcceptedHypotheses();
     BOOST_CHECK(accepted.empty());
@@ -52,7 +54,7 @@
     Est e20(low, ms(20), 1);
     Est e30(low, ms(30), 1);
 
-    AgentFeeder f;
+    AgentFeeder f(DEFAULT_SLACK_MS);
     f.feed(e0);
     f.feed(e10);
     f.feed(e20);
@@ -76,7 +78,7 @@
     Est f20(high, ms(2020), 1);
     Est f30(high, ms(2030), 1);
 
-    AgentFeeder f;
+    AgentFeeder f(DEFAULT_SLACK_MS);
     f.feed(e0);
     f.feed(e10);
     f.feed(e20);
@@ -118,7 +120,7 @@
     Est f43(high, ms(43), 1);
     Est f44(high, ms(44), 1);
 
-    AgentFeeder f;
+    AgentFeeder f(DEFAULT_SLACK_MS);
     f.feed(e0);
     f.feed(e10);
     f.feed(e20);
@@ -171,7 +173,7 @@
     Est f70(high, ms(70), 1);
     Est f80(high, ms(80), 1);
 
-    AgentFeeder f;
+    AgentFeeder f(DEFAULT_SLACK_MS);
     f.feed(e0);
     f.feed(e10);
     f.feed(e20);
@@ -225,7 +227,7 @@
     Est f40(high, ms(40), 1);
     Est f50(high, ms(50), 1);
 
-    AgentFeeder f;
+    AgentFeeder f(DEFAULT_SLACK_MS);
 
     f.feed(e0);
     f.feed(e10);
--- a/test/TestNoteHypothesis.cpp	Thu Dec 06 17:27:16 2012 +0000
+++ b/test/TestNoteHypothesis.cpp	Fri Aug 30 15:35:49 2013 +0100
@@ -49,13 +49,15 @@
 
 using Vamp::RealTime;
 
+#define DEFAULT_SLACK_MS 40
+
 BOOST_AUTO_TEST_SUITE(TestNoteHypothesis)
 
 BOOST_AUTO_TEST_CASE(emptyAccept)
 {
     // An empty hypothesis should accept any estimate with a
     // non-negligible confidence, and enter provisional state
-    NoteHypothesis h;
+    NoteHypothesis h(DEFAULT_SLACK_MS);
     NoteHypothesis::Estimate e; // default estimate has confidence 1
     BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::New);
     BOOST_CHECK(h.accept(e));
@@ -66,7 +68,7 @@
 {
     // A hypothesis should reject any estimate that has a negligible
     // confidence
-    NoteHypothesis h;
+    NoteHypothesis h(DEFAULT_SLACK_MS);
     NoteHypothesis::Estimate e;
     e.confidence = 0;
     BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::New);
@@ -78,7 +80,7 @@
 {
     // But if we're already in process we don't go to rejected state,
     // we just ignore this hypothesis
-    NoteHypothesis h;
+    NoteHypothesis h(DEFAULT_SLACK_MS);
     NoteHypothesis::Estimate e;
     BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::New);
     BOOST_CHECK(h.accept(e));
@@ -92,8 +94,12 @@
 {
     // Having accepted a first estimate, a hypothesis should reject a
     // second (and enter rejected state) if there is too long a gap
-    // between them for them to belong to a single note
-    NoteHypothesis h;
+    // between them for them to belong to a single note. Test this
+    // with more than one slack parameter, since that's what
+    // determines how long the gap can be.
+
+    {
+    NoteHypothesis h(40);
     NoteHypothesis::Estimate e1(500, RealTime::fromMilliseconds(0), 1);
     NoteHypothesis::Estimate e2(500, RealTime::fromMilliseconds(50), 1);
     BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::New);
@@ -101,6 +107,17 @@
     BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Provisional);
     BOOST_CHECK(!h.accept(e2));
     BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Rejected);
+    }
+    {
+    NoteHypothesis h(100);
+    NoteHypothesis::Estimate e1(500, RealTime::fromMilliseconds(0), 1);
+    NoteHypothesis::Estimate e2(500, RealTime::fromMilliseconds(50), 1);
+    BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::New);
+    BOOST_CHECK(h.accept(e1));
+    BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Provisional);
+    BOOST_CHECK(h.accept(e2));
+    BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Provisional);
+    }
 }
 
 BOOST_AUTO_TEST_CASE(simpleSatisfy)
@@ -108,7 +125,7 @@
     // A hypothesis should enter satisfied state after accepting three
     // consistent estimates, and then remain satisfied while accepting
     // further consistent estimates
-    NoteHypothesis h;
+    NoteHypothesis h(DEFAULT_SLACK_MS);
     NoteHypothesis::Estimate e1(500, RealTime::fromMilliseconds(0), 1);
     NoteHypothesis::Estimate e2(500, RealTime::fromMilliseconds(10), 1);
     NoteHypothesis::Estimate e3(500, RealTime::fromMilliseconds(20), 1);
@@ -130,7 +147,7 @@
     // offered an estimate that follows too long a gap, should enter
     // expired state rather than rejected state (showing that it has a
     // valid note but that the note has apparently finished)
-    NoteHypothesis h;
+    NoteHypothesis h(DEFAULT_SLACK_MS);
     NoteHypothesis::Estimate e1(500, RealTime::fromMilliseconds(0), 1);
     NoteHypothesis::Estimate e2(500, RealTime::fromMilliseconds(10), 1);
     NoteHypothesis::Estimate e3(500, RealTime::fromMilliseconds(20), 1);
@@ -156,7 +173,7 @@
 {
     // A wildly different frequency occurring in the middle of a
     // provisionally accepted note should be ignored
-    NoteHypothesis h;
+    NoteHypothesis h(DEFAULT_SLACK_MS);
     NoteHypothesis::Estimate e1(500, RealTime::fromMilliseconds(0), 1);
     NoteHypothesis::Estimate e2(1000, RealTime::fromMilliseconds(10), 1);
     NoteHypothesis::Estimate e3(500, RealTime::fromMilliseconds(20), 1);
@@ -176,7 +193,7 @@
 {
     // A wildly different frequency occurring in the middle of a
     // satisfied note should be ignored
-    NoteHypothesis h;
+    NoteHypothesis h(DEFAULT_SLACK_MS);
     NoteHypothesis::Estimate e1(500, RealTime::fromMilliseconds(0), 1);
     NoteHypothesis::Estimate e2(500, RealTime::fromMilliseconds(10), 1);
     NoteHypothesis::Estimate e3(500, RealTime::fromMilliseconds(20), 1);
@@ -199,7 +216,7 @@
 {
     // Behaviour with slightly varying frequencies should be as for
     // that with fixed frequency
-    NoteHypothesis h;
+    NoteHypothesis h(DEFAULT_SLACK_MS);
     NoteHypothesis::Estimate e1(500, RealTime::fromMilliseconds(0), 0.5);
     NoteHypothesis::Estimate e2(502, RealTime::fromMilliseconds(10), 0.5);
     NoteHypothesis::Estimate e3(504, RealTime::fromMilliseconds(20), 0.5);
@@ -225,7 +242,7 @@
 {
     // But there's a limit: outside a certain range we should reject
     //!!! (but what is this range? is it part of the spec?)
-    NoteHypothesis h;
+    NoteHypothesis h(DEFAULT_SLACK_MS);
     NoteHypothesis::Estimate e1(440, RealTime::fromMilliseconds(0), 1);
     NoteHypothesis::Estimate e2(448, RealTime::fromMilliseconds(10), 1);
     NoteHypothesis::Estimate e3(444, RealTime::fromMilliseconds(20), 1);
@@ -244,7 +261,7 @@
 BOOST_AUTO_TEST_CASE(acceptedEstimates)
 {
     // Check that getAcceptedEstimates() returns the right result
-    NoteHypothesis h;
+    NoteHypothesis h(DEFAULT_SLACK_MS);
     NoteHypothesis::Estimate e1(440, RealTime::fromMilliseconds(0), 1);
     NoteHypothesis::Estimate e2(448, RealTime::fromMilliseconds(10), 1);
     NoteHypothesis::Estimate e3(444, RealTime::fromMilliseconds(20), 1);
@@ -276,7 +293,7 @@
 BOOST_AUTO_TEST_CASE(meanFrequency)
 {
     // Check that the mean frequency is the mean of the frequencies
-    NoteHypothesis h;
+    NoteHypothesis h(DEFAULT_SLACK_MS);
     NoteHypothesis::Estimate e1(440, RealTime::fromMilliseconds(0), 1);
     NoteHypothesis::Estimate e2(448, RealTime::fromMilliseconds(10), 1);
     NoteHypothesis::Estimate e3(444, RealTime::fromMilliseconds(20), 1);
@@ -289,7 +306,7 @@
 BOOST_AUTO_TEST_CASE(averagedNote)
 {
     // Check that getAveragedNote returns something sane
-    NoteHypothesis h;
+    NoteHypothesis h(DEFAULT_SLACK_MS);
     NoteHypothesis::Estimate e1(440, RealTime::fromMilliseconds(10), 1);
     NoteHypothesis::Estimate e2(448, RealTime::fromMilliseconds(20), 1);
     NoteHypothesis::Estimate e3(444, RealTime::fromMilliseconds(30), 1);