Mercurial > hg > silvet
changeset 319:c37da62ba4e5 livemode
Add onsets output (lower-latency than the notes output)
author | Chris Cannam |
---|---|
date | Wed, 29 Apr 2015 09:27:38 +0100 |
parents | ba02cdc839db |
children | 7f9683c8de69 |
files | src/Silvet.cpp src/Silvet.h |
diffstat | 2 files changed, 111 insertions(+), 15 deletions(-) [+] |
line wrap: on
line diff
--- a/src/Silvet.cpp Tue Apr 28 16:54:12 2015 +0100 +++ b/src/Silvet.cpp Wed Apr 29 09:27:38 2015 +0100 @@ -253,6 +253,22 @@ m_notesOutputNo = list.size(); list.push_back(d); + d.identifier = "onsets"; + d.name = "Note onsets"; + d.description = "Note onsets, without durations. These can be calculated sooner than complete notes as it isn't necessary to wait for the note to finish. Each event has time, estimated fundamental frequency in Hz, and a synthetic MIDI velocity (1-127) estimated from the strength of the pitch in the mixture."; + d.unit = "Hz"; + d.hasFixedBinCount = true; + d.binCount = 2; + d.binNames.push_back("Frequency"); + d.binNames.push_back("Velocity"); + d.hasKnownExtents = false; + d.isQuantized = false; + d.sampleType = OutputDescriptor::VariableSampleRate; + d.sampleRate = processingSampleRate / (m_cq ? m_cq->getColumnHop() : 62); + d.hasDuration = false; + m_onsetsOutputNo = list.size(); + list.push_back(d); + d.identifier = "timefreq"; d.name = "Time-frequency distribution"; d.description = "Filtered constant-Q time-frequency distribution as used as input to the expectation-maximisation algorithm."; @@ -716,13 +732,20 @@ f.values[j % 12] += filtered[j] / inputGain; } fs[m_chromaOutputNo].push_back(f); - - FeatureList noteFeatures = noteTrack(shiftCount); + auto events = noteTrack(shiftCount); + + FeatureList noteFeatures = events.first; for (FeatureList::const_iterator fi = noteFeatures.begin(); fi != noteFeatures.end(); ++fi) { fs[m_notesOutputNo].push_back(*fi); } + + FeatureList onsetFeatures = events.second; + for (FeatureList::const_iterator fi = onsetFeatures.begin(); + fi != onsetFeatures.end(); ++fi) { + fs[m_onsetsOutputNo].push_back(*fi); + } } } @@ -944,7 +967,7 @@ return filtered; } -Vamp::Plugin::FeatureList +pair<Vamp::Plugin::FeatureList, Vamp::Plugin::FeatureList> Silvet::noteTrack(int shiftCount) { // Minimum duration pruning, and conversion to notes. We can only @@ -964,10 +987,10 @@ int durationThreshold = floor(0.1 / columnDuration); // columns if (durationThreshold < 1) durationThreshold = 1; - FeatureList noteFeatures; + FeatureList noteFeatures, onsetFeatures; if (width < durationThreshold + 1) { - return noteFeatures; + return { noteFeatures, onsetFeatures }; } //!!! try: repeated note detection? (look for change in first derivative of the pitch matrix) @@ -976,13 +999,7 @@ ni != m_pianoRoll[width-1].end(); ++ni) { int note = ni->first; - - if (active.find(note) != active.end()) { - // the note is still playing - continue; - } - // the note was playing but just ended int end = width; int start = end-1; @@ -991,16 +1008,25 @@ } ++start; - if ((end - start) < durationThreshold) { + int duration = end - start; + + if (duration < durationThreshold) { continue; } - emitNote(start, end, note, shiftCount, noteFeatures); + if (duration == durationThreshold) { + emitOnset(start, note, shiftCount, onsetFeatures); + } + + if (active.find(note) == active.end()) { + // the note was playing but just ended + emitNote(start, end, note, shiftCount, noteFeatures); + } } // cerr << "returning " << noteFeatures.size() << " complete note(s) " << endl; - return noteFeatures; + return { noteFeatures, onsetFeatures }; } void @@ -1065,6 +1091,40 @@ } } +void +Silvet::emitOnset(int start, int note, int shiftCount, + FeatureList &onsetFeatures) +{ + int len = int(m_pianoRoll.size()); + int velocity = 0; + + int shift = 0; + if (shiftCount > 1) { + shift = m_pianoRollShifts[start][note]; + } + + for (int i = start; i < len; ++i) { + + double strength = m_pianoRoll[i][note]; + + int v; + if (m_mode == LiveMode) { + v = round(strength * 20); + } else { + v = round(strength * 2); + } + if (v > velocity) { + velocity = v; + } + } + + onsetFeatures.push_back(makeOnsetFeature(start, + note, + shift, + shiftCount, + velocity)); +} + RealTime Silvet::getColumnTimestamp(int column) { @@ -1108,6 +1168,36 @@ return f; } +Silvet::Feature +Silvet::makeOnsetFeature(int start, + int note, + int shift, + int shiftCount, + int velocity) +{ + Feature f; + + f.hasTimestamp = true; + f.timestamp = getColumnTimestamp(start); + + f.hasDuration = false; + + f.values.clear(); + + f.values.push_back + (noteFrequency(note, shift, shiftCount)); + + float inputGain = getInputGainAt(f.timestamp); + velocity = round(velocity / inputGain); + if (velocity > 127) velocity = 127; + if (velocity < 1) velocity = 1; + f.values.push_back(velocity); + + f.label = noteName(note, shift, shiftCount); + + return f; +} + float Silvet::getInputGainAt(RealTime t) {
--- a/src/Silvet.h Tue Apr 28 16:54:12 2015 +0100 +++ b/src/Silvet.h Wed Apr 29 09:27:38 2015 +0100 @@ -116,15 +116,20 @@ const vector<int> &bestShifts, bool wantShifts); // -> piano roll column - FeatureList noteTrack(int shiftCount); + std::pair<FeatureList, FeatureList> noteTrack(int shiftCount); // notes, onsets void emitNote(int start, int end, int note, int shiftCount, FeatureList ¬eFeatures); + + void emitOnset(int start, int note, int shiftCount, + FeatureList ¬eFeatures); Vamp::RealTime getColumnTimestamp(int column); Feature makeNoteFeature(int start, int end, int note, int shift, int shiftCount, int velocity); + Feature makeOnsetFeature(int start, int note, int shift, + int shiftCount, int velocity); float getInputGainAt(Vamp::RealTime t); @@ -143,6 +148,7 @@ bool m_haveStartTime; mutable int m_notesOutputNo; + mutable int m_onsetsOutputNo; mutable int m_fcqOutputNo; mutable int m_pitchOutputNo; mutable int m_templateOutputNo;