# HG changeset patch # User Chris Cannam # Date 1401285241 -3600 # Node ID 9b9cdfccbd14d8f44c625f120ff4cc193486e422 # Parent e1718e64a9216a44acfb2064f8e6cb415d4adb17 Wire up note agent code -- results are not very good, so far diff -r e1718e64a921 -r 9b9cdfccbd14 Makefile.inc --- a/Makefile.inc Fri May 23 17:03:27 2014 +0100 +++ b/Makefile.inc Wed May 28 14:54:01 2014 +0100 @@ -67,7 +67,9 @@ # DO NOT DELETE -src/Silvet.o: src/Silvet.h src/MedianFilter.h src/Instruments.h src/EM.h +src/Silvet.o: src/Silvet.h src/MedianFilter.h src/Instruments.h +src/Silvet.o: src/NoteHypothesis.h src/AgentHypothesis.h src/EM.h +src/Silvet.o: src/AgentFeederPoly.h src/AgentFeeder.h src/Silvet.o: constant-q-cpp/src/dsp/Resampler.h src/EM.o: src/EM.h src/Instruments.h src/Instruments.o: src/Instruments.h data/include/templates.h @@ -77,9 +79,17 @@ src/Instruments.o: data/include/oboe.h data/include/tenorsax.h src/Instruments.o: data/include/violin.h data/include/piano1.h src/Instruments.o: data/include/piano2.h data/include/piano3.h +src/NoteHypothesis.o: src/NoteHypothesis.h src/AgentHypothesis.h +src/NoteHypothesis.o: src/AgentFeederPoly.h src/AgentFeeder.h src/libmain.o: src/Silvet.h src/MedianFilter.h src/Instruments.h +src/libmain.o: src/NoteHypothesis.h src/AgentHypothesis.h bqvec/src/Allocators.o: bqvec/src/Allocators.h bqvec/src/VectorOps.h bqvec/src/Allocators.o: bqvec/src/Restrict.h -src/Silvet.o: src/MedianFilter.h src/Instruments.h +src/Silvet.o: src/MedianFilter.h src/Instruments.h src/NoteHypothesis.h +src/Silvet.o: src/AgentHypothesis.h +src/AgentFeeder.o: src/AgentHypothesis.h +src/AgentFeederMono.o: src/AgentFeeder.h src/AgentHypothesis.h +src/AgentFeederPoly.o: src/AgentFeeder.h src/AgentHypothesis.h +src/NoteHypothesis.o: src/AgentHypothesis.h bqvec/src/Allocators.o: bqvec/src/VectorOps.h bqvec/src/Restrict.h bqvec/src/VectorOps.o: bqvec/src/Restrict.h diff -r e1718e64a921 -r 9b9cdfccbd14 src/AgentFeederPoly.h --- a/src/AgentFeederPoly.h Fri May 23 17:03:27 2014 +0100 +++ b/src/AgentFeederPoly.h Wed May 28 14:54:01 2014 +0100 @@ -20,8 +20,9 @@ #include "AgentFeeder.h" #include +#include -//#define DEBUG_FEEDER 1 +#define DEBUG_FEEDER 1 /** * Take a series of observations or estimates (one at a time) and feed @@ -39,10 +40,9 @@ template class AgentFeederPoly : public AgentFeeder { -public: - typedef std::set Hypotheses; - private: + typedef std::vector Hypotheses; + struct State { Hypotheses provisional; Hypotheses satisfied; @@ -55,10 +55,10 @@ virtual void feed(AgentHypothesis::Observation o) { #ifdef DEBUG_FEEDER - std::cerr << "feed: have observation [value = " << o.value << ", time = " << o.time << "]" << std::endl; + std::cerr << "\nfeed: have observation [value = " << o.value << ", time = " << o.time << "]" << std::endl; #endif - update(m_state, o); + m_state = update(m_state, o); } virtual void finish() { @@ -67,16 +67,21 @@ #endif for (typename Hypotheses::const_iterator i = m_state.satisfied.begin(); i != m_state.satisfied.end(); ++i) { - m_state.completed.insert(*i); + m_state.completed.push_back(*i); } } - Hypotheses getAcceptedHypotheses() const { - return m_state.completed; + std::set getAcceptedHypotheses() const { + std::set hs; + for (typename Hypotheses::const_iterator i = m_state.completed.begin(); + i != m_state.completed.end(); ++i) { + hs.insert(*i); + } + return hs; } private: - void update(State &s, AgentHypothesis::Observation o) { + State update(State s, AgentHypothesis::Observation o) { /* An observation can "belong" to any number of provisional @@ -98,14 +103,14 @@ must be discarded. */ + State newState; + // We only ever add to the completed hypotheses, never remove // anything from them. But we may remove from provisional (if // rejected or transferred to satisfied) and satisfied (when // completed). - - Hypotheses toCompleted; - Hypotheses toSatisfied; - Hypotheses toProvisional; + + newState.completed = s.completed; bool swallowed = false; @@ -126,6 +131,7 @@ // for expiry, because the state is only updated when // accept() is called. //!!! That looks like a limitation in the Hypothesis API + newState.satisfied.push_back(h); } else { // !swallowed @@ -134,8 +140,11 @@ std::cerr << "accepted by satisfied hypothesis " << &(*i) << ", state is " << h.getState() << std::endl; #endif swallowed = true; + newState.satisfied.push_back(h); } else if (h.getState() == Hypothesis::Expired) { - toCompleted.insert(h); + newState.completed.push_back(h); + } else { + newState.satisfied.push_back(h); } } } @@ -147,6 +156,7 @@ #endif // no provisional hypotheses have become satisfied, no new // ones have been introduced + newState.provisional = s.provisional; } else { @@ -171,13 +181,13 @@ if (promoted == Hypothesis() && h.accept(o) && h.getState() == Hypothesis::Satisfied) { - toSatisfied.insert(h); + newState.satisfied.push_back(h); #ifdef DEBUG_FEEDER std::cerr << "promoting a hypothesis to satisfied, have " << newState.satisfied.size() << " satisfied now" << std::endl; #endif promoted = h; } else if (h.getState() != Hypothesis::Rejected) { - // leave as provisional + newState.provisional.push_back(h); } } @@ -189,50 +199,41 @@ h.accept(o); if (h.getState() == Hypothesis::Provisional) { - toProvisional.insert(h); + newState.provisional.push_back(h); } else if (h.getState() == Hypothesis::Satisfied) { - toSatisfied.insert(h); + newState.satisfied.push_back(h); } #ifdef DEBUG_FEEDER std::cerr << "update: new hypothesis of state " << h.getState() << ", provisional count -> " << newState.provisional.size() << std::endl; #endif + } else { +#ifdef DEBUG_FEEDER + std::cerr << "a hypothesis became satisfied, reaping its observations" << std::endl; +#endif + newState = reap(newState); } } - for (typename Hypotheses::const_iterator i = toCompleted.begin(); - i != toCompleted.end(); ++i) { - s.satisfied.erase(*i); - s.completed.insert(*i); - } - for (typename Hypotheses::const_iterator i = toSatisfied.begin(); - i != toSatisfied.end(); ++i) { - s.provisional.erase(*i); - s.satisfied.insert(*i); - } - for (typename Hypotheses::const_iterator i = toProvisional.begin(); - i != toProvisional.end(); ++i) { - s.provisional.insert(*i); - } - - reap(s); + return newState; } - void reap(State &s) { + State reap(State s) { // "When a hypothesis subsequently becomes satisfied, all // other provisional hypotheses containing any of its // observations must be discarded." - if (s.provisional.empty()) return; + if (s.provisional.empty()) return s; int reaped = 0; - Hypotheses toRemove = Hypotheses();; + Hypotheses prior = s.provisional; + s.provisional = Hypotheses(); - for (typename Hypotheses::const_iterator hi = s.provisional.begin(); - hi != s.provisional.end(); ++hi) { + for (typename Hypotheses::const_iterator hi = prior.begin(); + hi != prior.end(); ++hi) { const AgentHypothesis::Observations obs = hi->getAcceptedObservations(); @@ -261,16 +262,12 @@ } } - if (!keep) { - toRemove.insert(*hi); + if (keep) { + s.provisional.push_back(*hi); + } else { ++reaped; } } - - for (typename Hypotheses::const_iterator i = toRemove.begin(); - i != toRemove.end(); ++i) { - s.provisional.erase(i); - } #ifdef DEBUG_FEEDER std::cerr << "reap: have " @@ -279,6 +276,8 @@ << s.completed.size() << " completed, reaped " << reaped << std::endl; #endif + + return s; } }; diff -r e1718e64a921 -r 9b9cdfccbd14 src/NoteHypothesis.cpp --- a/src/NoteHypothesis.cpp Fri May 23 17:03:27 2014 +0100 +++ b/src/NoteHypothesis.cpp Wed May 28 14:54:01 2014 +0100 @@ -21,10 +21,14 @@ #include #include +#include using Vamp::RealTime; -//#define DEBUG_NOTE_HYPOTHESIS 1 +using std::cerr; +using std::endl; + +#define DEBUG_NOTE_HYPOTHESIS 1 NoteHypothesis::NoteHypothesis() { @@ -50,8 +54,8 @@ double r = s.value / last.value; int cents = lrint(1200.0 * (log(r) / log(2.0))); #ifdef DEBUG_NOTE_HYPOTHESIS - std::cerr << "isWithinTolerance: this " << s.value << " is " << cents - << " cents from prior " << last.value << std::endl; + cerr << "isWithinTolerance: this " << s.value << " is " << cents + << " cents from prior " << last.value << endl; #endif if (cents < -60 || cents > 60) return false; @@ -60,8 +64,8 @@ r = s.value / meanFreq; cents = lrint(1200.0 * (log(r) / log(2.0))); #ifdef DEBUG_NOTE_HYPOTHESIS - std::cerr << "isWithinTolerance: this " << s.value << " is " << cents - << " cents from mean " << meanFreq << std::endl; + cerr << "isWithinTolerance: this " << s.value << " is " << cents + << " cents from mean " << meanFreq << endl; #endif if (cents < -80 || cents > 80) return false; @@ -78,10 +82,10 @@ Observation last = *i; #ifdef DEBUG_NOTE_HYPOTHESIS - std::cerr << "isOutOfDateFor: this " << s.time << " is " + cerr << "isOutOfDateFor: this " << s.time << " is " << (s.time - last.time) << " from last " << last.time << " (threshold " << RealTime::fromMilliseconds(40) << ")" - << std::endl; + << endl; #endif return ((s.time - last.time) > RealTime::fromMilliseconds(40)); @@ -99,26 +103,42 @@ } meanConfidence /= m_pending.size(); + //!!! surely this depends on the hop size? int lengthRequired = 100; if (meanConfidence > 0.0) { lengthRequired = int(2.0 / meanConfidence + 0.5); } -// if (lengthRequired < 1) lengthRequired = 1; + //!!! + lengthRequired = lengthRequired / 2; + if (lengthRequired < 1) lengthRequired = 1; #ifdef DEBUG_NOTE_HYPOTHESIS - std::cerr << "meanConfidence " << meanConfidence << ", lengthRequired " << lengthRequired << std::endl; + cerr << "meanConfidence " << meanConfidence << ", lengthRequired " << lengthRequired << endl; #endif return ((int)m_pending.size() > lengthRequired); } +static void printState(NoteHypothesis::State s) +{ + switch (s) { + case NoteHypothesis::New: cerr << "New"; break; + case NoteHypothesis::Provisional: cerr << "Provisional"; break; + case NoteHypothesis::Rejected: cerr << "Rejected"; break; + case NoteHypothesis::Satisfied: cerr << "Satisfied"; break; + case NoteHypothesis::Expired: cerr << "Expired"; break; + } +} + bool NoteHypothesis::accept(Observation s) { bool accept = false; #ifdef DEBUG_NOTE_HYPOTHESIS - std::cerr << "NoteHypothesis[" << this << "]::accept: state " << m_state << "..." << std::endl; + cerr << "NoteHypothesis[" << this << "]::accept (value " << s.value << ", time " << s.time << ", confidence " << s.confidence << "): state "; + printState(m_state); + cerr << "..." << endl; #endif static double negligibleConfidence = 0.0001; @@ -164,14 +184,23 @@ } if (accept) { +#ifdef DEBUG_NOTE_HYPOTHESIS + cerr << "... accepting" << endl; +#endif m_pending.insert(s); if (m_state == Provisional && isSatisfied()) { m_state = Satisfied; } + } else { +#ifdef DEBUG_NOTE_HYPOTHESIS + cerr << "... not accepting" << endl; +#endif } #ifdef DEBUG_NOTE_HYPOTHESIS - std::cerr << "... -> " << m_state << " (pending: " << m_pending.size() << ")" << std::endl; + cerr << "... -> "; + printState(m_state); + cerr << " (pending: " << m_pending.size() << ")" << endl; #endif return accept; @@ -194,6 +223,19 @@ } double +NoteHypothesis::getMedianFrequency() const +{ + if (m_pending.empty()) return 0.0; + std::vector freqs; + for (Observations::const_iterator i = m_pending.begin(); + i != m_pending.end(); ++i) { + freqs.push_back(i->value); + } + std::sort(freqs.begin(), freqs.end()); + return freqs[freqs.size()/2]; +} + +double NoteHypothesis::getMeanFrequency() const { double acc = 0.0; @@ -206,6 +248,19 @@ return acc; } +double +NoteHypothesis::getMedianConfidence() const +{ + if (m_pending.empty()) return 0.0; + std::vector confs; + for (Observations::const_iterator i = m_pending.begin(); + i != m_pending.end(); ++i) { + confs.push_back(i->confidence); + } + std::sort(confs.begin(), confs.end()); + return confs[confs.size()/2]; +} + NoteHypothesis::Note NoteHypothesis::getAveragedNote() const { @@ -213,9 +268,8 @@ n.time = getStartTime(); n.duration = getDuration(); - - // just mean frequency for now, but this isn't at all right perceptually - n.freq = getMeanFrequency(); + n.freq = getMedianFrequency(); + n.confidence = getMedianConfidence(); return n; } diff -r e1718e64a921 -r 9b9cdfccbd14 src/NoteHypothesis.h --- a/src/NoteHypothesis.h Fri May 23 17:03:27 2014 +0100 +++ b/src/NoteHypothesis.h Wed May 28 14:54:01 2014 +0100 @@ -47,21 +47,33 @@ virtual Observations getAcceptedObservations() const; struct Note { - Note() : freq(0), time(), duration() { } - Note(double _f, Vamp::RealTime _t, Vamp::RealTime _d) : - freq(_f), time(_t), duration(_d) { } + Note() : freq(0), time(), duration(), confidence(1.0) { } + Note(double _f, Vamp::RealTime _t, Vamp::RealTime _d, double _i) : + freq(_f), time(_t), duration(_d), confidence(_i) { } bool operator==(const Note &e) const { - return e.freq == freq && e.time == time && e.duration == duration; + return e.freq == freq && e.time == time && + e.duration == duration && e.confidence == confidence; } double freq; Vamp::RealTime time; Vamp::RealTime duration; + double confidence; }; /** * Return the mean frequency of the accepted observations */ double getMeanFrequency() const; + + /** + * Return the median frequency of the accepted observations + */ + double getMedianFrequency() const; + + /** + * Return the median confidence of the accepted observations + */ + double getMedianConfidence() const; /** * Return a single note roughly matching this hypothesis @@ -85,7 +97,25 @@ } bool operator<(const NoteHypothesis &other) const { - return getStartTime() < other.getStartTime(); + if (getStartTime() != other.getStartTime()) { + return getStartTime() < other.getStartTime(); + } else if (m_state != other.m_state) { + return m_state < other.m_state; + } else if (m_pending.size() != other.m_pending.size()) { + return m_pending.size() < other.m_pending.size(); + } else { + Observations::const_iterator i = m_pending.begin(); + Observations::const_iterator j = other.m_pending.begin(); + while (i != m_pending.end()) { + if (*i == *j) { + ++i; + ++j; + } else { + return *i < *j; + } + } + return false; + } } private: diff -r e1718e64a921 -r 9b9cdfccbd14 src/Silvet.cpp --- a/src/Silvet.cpp Fri May 23 17:03:27 2014 +0100 +++ b/src/Silvet.cpp Wed May 28 14:54:01 2014 +0100 @@ -19,6 +19,9 @@ #include #include "MedianFilter.h" +#include "AgentFeederPoly.h" +#include "NoteHypothesis.h" + #include "constant-q-cpp/src/dsp/Resampler.h" #include @@ -42,7 +45,8 @@ m_hqMode(true), m_fineTuning(false), m_instrument(0), - m_colsPerSec(50) + m_colsPerSec(50), + m_agentFeeder(0) { } @@ -53,6 +57,7 @@ for (int i = 0; i < (int)m_postFilter.size(); ++i) { delete m_postFilter[i]; } + delete m_agentFeeder; } string @@ -353,6 +358,7 @@ { delete m_resampler; delete m_cq; + delete m_agentFeeder; if (m_inputSampleRate != processingSampleRate) { m_resampler = new Resampler(m_inputSampleRate, processingSampleRate); @@ -393,15 +399,18 @@ for (int i = 0; i < m_instruments[0].templateNoteCount; ++i) { m_postFilter.push_back(new MedianFilter(3)); } - m_pianoRoll.clear(); - m_columnCount = 0; + + m_columnCountIn = 0; + m_columnCountOut = 0; m_startTime = RealTime::zeroTime; + + m_agentFeeder = new AgentFeederPoly(); } Silvet::FeatureSet Silvet::process(const float *const *inputBuffers, Vamp::RealTime timestamp) { - if (m_columnCount == 0) { + if (m_columnCountIn == 0) { m_startTime = timestamp; } @@ -423,7 +432,17 @@ Silvet::getRemainingFeatures() { Grid cqout = m_cq->getRemainingOutput(); + FeatureSet fs = transcribe(cqout); + + m_agentFeeder->finish(); + + FeatureList noteFeatures = obtainNotes(); + for (FeatureList::const_iterator fi = noteFeatures.begin(); + fi != noteFeatures.end(); ++fi) { + fs[m_notesOutputNo].push_back(*fi); + } + return fs; } @@ -453,7 +472,7 @@ //!!! pitches or notes? [terminology] Grid localPitches(width, vector(pack.templateNoteCount, 0.0)); - bool wantShifts = m_hqMode && m_fineTuning; + bool wantShifts = m_hqMode; int shiftCount = 1; if (wantShifts) { shiftCount = pack.templateMaxShift * 2 + 1; @@ -513,16 +532,13 @@ for (int j = 0; j < pack.templateNoteCount; ++j) { m_postFilter[j]->push(0.0); } - m_pianoRoll.push_back(map()); - if (wantShifts) { - m_pianoRollShifts.push_back(map()); - } continue; } - postProcess(localPitches[i], localBestShifts[i], wantShifts); + postProcess(localPitches[i], localBestShifts[i], + wantShifts, shiftCount); - FeatureList noteFeatures = noteTrack(shiftCount); + FeatureList noteFeatures = obtainNotes(); for (FeatureList::const_iterator fi = noteFeatures.begin(); fi != noteFeatures.end(); ++fi) { @@ -556,13 +572,13 @@ for (int i = 0; i < width; ++i) { - if (m_columnCount < latentColumns) { - ++m_columnCount; + if (m_columnCountIn < latentColumns) { + ++m_columnCountIn; continue; } - int prevSampleNo = (m_columnCount - 1) * m_cq->getColumnHop(); - int sampleNo = m_columnCount * m_cq->getColumnHop(); + int prevSampleNo = (m_columnCountIn - 1) * m_cq->getColumnHop(); + int sampleNo = m_columnCountIn * m_cq->getColumnHop(); bool select = (sampleNo / spacing != prevSampleNo / spacing); @@ -611,7 +627,7 @@ out.push_back(outCol); } - ++m_columnCount; + ++m_columnCountIn; } return out; @@ -620,7 +636,8 @@ void Silvet::postProcess(const vector &pitches, const vector &bestShifts, - bool wantShifts) + bool wantShifts, + int shiftCount) { const InstrumentPack &pack = m_instruments[m_instrument]; @@ -631,178 +648,82 @@ filtered.push_back(m_postFilter[j]->get()); } - // Threshold for level and reduce number of candidate pitches + double threshold = 1; - int polyphony = 5; - - //!!! make this a parameter (was 4.8, try adjusting, compare levels against matlab code) - double threshold = 6; -// double threshold = 4.8; - - typedef std::multimap ValueIndexMap; - - ValueIndexMap strengths; + double columnDuration = 1.0 / m_colsPerSec; + int postFilterLatency = int(m_postFilter[0]->getSize() / 2); + RealTime t = RealTime::fromSeconds + (columnDuration * (m_columnCountOut - postFilterLatency) + 0.02); for (int j = 0; j < pack.templateNoteCount; ++j) { + double strength = filtered[j]; - if (strength < threshold) continue; - strengths.insert(ValueIndexMap::value_type(strength, j)); + if (strength < threshold) { + continue; + } + + double freq; + if (wantShifts) { + freq = noteFrequency(j, bestShifts[j], shiftCount); + } else { + freq = noteFrequency(j, 0, shiftCount); + } + + double confidence = strength / 50.0; //!!!??? + if (confidence > 1.0) confidence = 1.0; + + AgentHypothesis::Observation obs(freq, t, confidence); + m_agentFeeder->feed(obs); } - ValueIndexMap::const_iterator si = strengths.end(); - - map active; - map activeShifts; - - while (int(active.size()) < polyphony && si != strengths.begin()) { - - --si; - - double strength = si->first; - int j = si->second; - - active[j] = strength; - - if (wantShifts) { - activeShifts[j] = bestShifts[j]; - } - } - - m_pianoRoll.push_back(active); - - if (wantShifts) { - m_pianoRollShifts.push_back(activeShifts); - } + m_columnCountOut ++; } Vamp::Plugin::FeatureList -Silvet::noteTrack(int shiftCount) +Silvet::obtainNotes() { - // Minimum duration pruning, and conversion to notes. We can only - // report notes that have just ended (i.e. that are absent in the - // latest active set but present in the prior set in the piano - // roll) -- any notes that ended earlier will have been reported - // already, and if they haven't ended, we don't know their - // duration. - - int width = m_pianoRoll.size() - 1; - - const map &active = m_pianoRoll[width]; - - double columnDuration = 1.0 / m_colsPerSec; - - // only keep notes >= 100ms or thereabouts - int durationThreshold = floor(0.1 / columnDuration); // columns - if (durationThreshold < 1) durationThreshold = 1; - FeatureList noteFeatures; - if (width < durationThreshold + 1) { + typedef AgentFeederPoly NoteFeeder; + + NoteFeeder *feeder = dynamic_cast(m_agentFeeder); + + if (!feeder) { + cerr << "INTERNAL ERROR: Feeder is not a poly-note-hypothesis-feeder!" + << endl; return noteFeatures; } - - //!!! try: repeated note detection? (look for change in first derivative of the pitch matrix) - for (map::const_iterator ni = m_pianoRoll[width-1].begin(); - ni != m_pianoRoll[width-1].end(); ++ni) { + std::set hh = feeder->getAcceptedHypotheses(); - int note = ni->first; - - if (active.find(note) != active.end()) { - // the note is still playing - continue; + //!!! inefficient + for (std::set::const_iterator hi = hh.begin(); + hi != hh.end(); ++hi) { + + NoteHypothesis h(*hi); + + if (m_emitted.find(h) != m_emitted.end()) { + continue; // already returned this one } - // the note was playing but just ended - int end = width; - int start = end-1; + m_emitted.insert(h); - while (m_pianoRoll[start].find(note) != m_pianoRoll[start].end()) { - --start; - } - ++start; + NoteHypothesis::Note n = h.getAveragedNote(); - if ((end - start) < durationThreshold) { - continue; - } + int velocity = n.confidence * 127; + if (velocity > 127) velocity = 127; - emitNote(start, end, note, shiftCount, noteFeatures); + Feature f; + f.hasTimestamp = true; + f.hasDuration = true; + f.timestamp = n.time; + f.duration = n.duration; + f.values.clear(); + f.values.push_back(n.freq); + f.values.push_back(velocity); +// f.label = noteName(note, partShift, shiftCount); + noteFeatures.push_back(f); } -// cerr << "returning " << noteFeatures.size() << " complete note(s) " << endl; - return noteFeatures; } - -void -Silvet::emitNote(int start, int end, int note, int shiftCount, - FeatureList ¬eFeatures) -{ - int partStart = start; - int partShift = 0; - int partVelocity = 0; - - Feature f; - f.hasTimestamp = true; - f.hasDuration = true; - - double columnDuration = 1.0 / m_colsPerSec; - int postFilterLatency = int(m_postFilter[0]->getSize() / 2); - int partThreshold = floor(0.05 / columnDuration); - - for (int i = start; i != end; ++i) { - - double strength = m_pianoRoll[i][note]; - - int shift = 0; - - if (shiftCount > 1) { - - shift = m_pianoRollShifts[i][note]; - - if (i == partStart) { - partShift = shift; - } - - if (i > partStart + partThreshold && shift != partShift) { - -// cerr << "i = " << i << ", partStart = " << partStart << ", shift = " << shift << ", partShift = " << partShift << endl; - - // pitch has changed, emit an intermediate note - f.timestamp = RealTime::fromSeconds - (columnDuration * (partStart - postFilterLatency) + 0.02); - f.duration = RealTime::fromSeconds - (columnDuration * (i - partStart)); - f.values.clear(); - f.values.push_back - (noteFrequency(note, partShift, shiftCount)); - f.values.push_back(partVelocity); - f.label = noteName(note, partShift, shiftCount); - noteFeatures.push_back(f); - partStart = i; - partShift = shift; - partVelocity = 0; - } - } - - int v = strength * 2; - if (v > 127) v = 127; - - if (v > partVelocity) { - partVelocity = v; - } - } - - if (end >= partStart + partThreshold) { - f.timestamp = RealTime::fromSeconds - (columnDuration * (partStart - postFilterLatency) + 0.02); - f.duration = RealTime::fromSeconds - (columnDuration * (end - partStart)); - f.values.clear(); - f.values.push_back - (noteFrequency(note, partShift, shiftCount)); - f.values.push_back(partVelocity); - f.label = noteName(note, partShift, shiftCount); - noteFeatures.push_back(f); - } -} diff -r e1718e64a921 -r 9b9cdfccbd14 src/Silvet.h --- a/src/Silvet.h Fri May 23 17:03:27 2014 +0100 +++ b/src/Silvet.h Wed May 28 14:54:01 2014 +0100 @@ -24,6 +24,7 @@ #include "MedianFilter.h" #include "Instruments.h" +#include "NoteHypothesis.h" using std::string; using std::vector; @@ -32,6 +33,7 @@ class Resampler; class CQSpectrogram; +class AgentFeeder; class Silvet : public Vamp::Plugin { @@ -84,19 +86,18 @@ typedef vector > Grid; vector *> m_postFilter; - vector > m_pianoRoll; - vector > m_pianoRollShifts; + + AgentFeeder *m_agentFeeder; + std::set m_emitted; Grid preProcess(const Grid &); void postProcess(const vector &pitches, const vector &bestShifts, - bool wantShifts); // -> piano roll column + bool wantShifts, + int shiftCount); // -> feeder - FeatureList noteTrack(int shiftCount); - - void emitNote(int start, int end, int note, int shiftCount, - FeatureList ¬eFeatures); + FeatureList obtainNotes(); FeatureSet transcribe(const Grid &); @@ -104,7 +105,8 @@ float noteFrequency(int n, int shift, int shiftCount) const; int m_blockSize; - int m_columnCount; + int m_columnCountIn; + int m_columnCountOut; Vamp::RealTime m_startTime; mutable int m_notesOutputNo;