Mercurial > hg > cepstral-pitchtracker
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);