changeset 298:ebe5e0942bb8 livemode

More toward a possible live mode
author Chris Cannam
date Fri, 28 Nov 2014 10:18:22 +0000
parents d6ab1b4918bd
children a4216826f01c
files Makefile.inc src/Instruments.cpp src/Instruments.h src/LiveInstruments.cpp src/LiveInstruments.h src/Silvet.cpp src/Silvet.h
diffstat 7 files changed, 166 insertions(+), 38 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile.inc	Fri Nov 28 09:42:56 2014 +0000
+++ b/Makefile.inc	Fri Nov 28 10:18:22 2014 +0000
@@ -20,8 +20,8 @@
 
 PLUGIN	:= silvet$(PLUGIN_EXT)
 
-PLUGIN_HEADERS := $(SRC_DIR)/Silvet.h $(SRC_DIR)/EM.h $(SRC_DIR)/Instruments.h
-PLUGIN_SOURCES := $(SRC_DIR)/Silvet.cpp $(SRC_DIR)/EM.cpp $(SRC_DIR)/Instruments.cpp $(SRC_DIR)/libmain.cpp
+PLUGIN_HEADERS := $(SRC_DIR)/Silvet.h $(SRC_DIR)/EM.h $(SRC_DIR)/Instruments.h $(SRC_DIR)/LiveInstruments.h
+PLUGIN_SOURCES := $(SRC_DIR)/Silvet.cpp $(SRC_DIR)/EM.cpp $(SRC_DIR)/Instruments.cpp $(SRC_DIR)/LiveInstruments.cpp $(SRC_DIR)/libmain.cpp
 
 BQVEC_HEADERS	:= $(BQVEC_DIR)/Allocators.h $(BQVEC_DIR)/Restrict.h $(BQVEC_DIR)/VectorOps.h
 BQVEC_SOURCES	:= $(BQVEC_DIR)/Allocators.cpp
--- a/src/Instruments.cpp	Fri Nov 28 09:42:56 2014 +0000
+++ b/src/Instruments.cpp	Fri Nov 28 10:18:22 2014 +0000
@@ -19,16 +19,28 @@
 
 #include <iostream>
 
-const int InstrumentPack::templateNoteCount = SILVET_TEMPLATE_NOTE_COUNT;
-const int InstrumentPack::templateHeight = SILVET_TEMPLATE_HEIGHT;
-const int InstrumentPack::templateMaxShift = SILVET_TEMPLATE_MAX_SHIFT;
-const int InstrumentPack::templateSize = SILVET_TEMPLATE_SIZE;
-
 using std::string;
 using std::vector;
 using std::cerr;
 using std::endl;
 
+InstrumentPack::InstrumentPack(int lowest, int highest,
+                               std::string n, std::vector<Templates> tt) :
+    templateNoteCount(SILVET_TEMPLATE_NOTE_COUNT),
+    templateHeight(SILVET_TEMPLATE_HEIGHT),
+    templateMaxShift(SILVET_TEMPLATE_MAX_SHIFT),
+    templateSize(SILVET_TEMPLATE_SIZE),
+    lowestNote(lowest),
+    highestNote(highest),
+    maxPolyphony(5),
+    pitchSparsity(1.1),
+    sourceSparsity(1.2),
+    levelThreshold(5),
+    name(n),
+    templates(tt)
+{
+}
+
 const char *simpleInstruments[] = {
     // Each instrument has two consecutive slots, one for the pack
     // name and one for the template to look up
--- a/src/Instruments.h	Fri Nov 28 09:42:56 2014 +0000
+++ b/src/Instruments.h	Fri Nov 28 10:18:22 2014 +0000
@@ -32,10 +32,10 @@
 class InstrumentPack
 {
 public:
-    static const int templateNoteCount;
-    static const int templateHeight;
-    static const int templateMaxShift;
-    static const int templateSize;
+    int templateNoteCount;
+    int templateHeight;
+    int templateMaxShift;
+    int templateSize;
 
     int lowestNote;
     int highestNote;
@@ -60,15 +60,9 @@
 
 private:
     InstrumentPack(int lowest, int highest, std::string n,
-		   std::vector<Templates> tt) :
-	lowestNote(lowest),
-	highestNote(highest),
-        maxPolyphony(5),
-        pitchSparsity(1.1),
-        sourceSparsity(1.2),
-        levelThreshold(5),
-	name(n),
-	templates(tt) { }
+		   std::vector<Templates> tt);
+    
+    friend class LiveAdapter;
 };
 
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/LiveInstruments.cpp	Fri Nov 28 10:18:22 2014 +0000
@@ -0,0 +1,71 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+  Silvet
+
+  A Vamp plugin for note transcription.
+  Centre for Digital Music, Queen Mary University of London.
+    
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.  See the file
+  COPYING included with this distribution for more information.
+*/
+
+#include "LiveInstruments.h"
+
+#include "data/include/templates.h"
+
+using namespace std;
+
+InstrumentPack
+LiveAdapter::adapt(const InstrumentPack &original)
+{
+    vector<InstrumentPack::Templates> templates;
+
+    for (vector<InstrumentPack::Templates>::const_iterator i =
+	     original.templates.begin();
+	 i != original.templates.end(); ++i) {
+
+	InstrumentPack::Templates t;
+	t.lowestNote = i->lowestNote;
+	t.highestNote = i->highestNote;
+	t.data.resize(i->data.size());
+
+	for (int j = 0; j < int(i->data.size()); ++j) {
+	    t.data[j].resize(SILVET_TEMPLATE_HEIGHT/5);
+	    for (int k = 0; k < SILVET_TEMPLATE_HEIGHT/5; ++k) {
+		t.data[j][k] = i->data[j][k * 5 + 2 - SILVET_TEMPLATE_MAX_SHIFT];
+	    }
+	}
+	templates.push_back(t);
+    }
+    
+    InstrumentPack live(original.lowestNote,
+			original.highestNote,
+			original.name,
+			templates);
+
+    live.templateHeight = SILVET_TEMPLATE_HEIGHT/5;
+    live.templateMaxShift = 0;
+    live.templateSize = live.templateHeight;
+    
+    live.maxPolyphony = original.maxPolyphony;
+    live.pitchSparsity = original.pitchSparsity;
+    live.sourceSparsity = original.sourceSparsity;
+    live.levelThreshold = original.levelThreshold;
+
+    return live;
+}
+
+vector<InstrumentPack>
+LiveAdapter::adaptAll(const vector<InstrumentPack> &v)
+{
+    vector<InstrumentPack> out;
+    for (int i = 0; i < (int)v.size(); ++i) {
+        InstrumentPack p(LiveAdapter::adapt(v[i]));
+        out.push_back(p);
+    }
+    return out;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/LiveInstruments.h	Fri Nov 28 10:18:22 2014 +0000
@@ -0,0 +1,32 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+  Silvet
+
+  A Vamp plugin for note transcription.
+  Centre for Digital Music, Queen Mary University of London.
+    
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.  See the file
+  COPYING included with this distribution for more information.
+*/
+
+#ifndef SILVET_LIVE_INSTRUMENTS_H
+#define SILVET_LIVE_INSTRUMENTS_H
+
+#include "Instruments.h"
+
+/**
+ * Adapt an instrument pack into a "live" version, with fewer bins per
+ * octave and so lower CQ latency.
+ */
+class LiveAdapter
+{
+public:
+    static InstrumentPack adapt(const InstrumentPack &original);
+    static std::vector<InstrumentPack> adaptAll(const std::vector<InstrumentPack> &);
+};
+
+#endif
--- a/src/Silvet.cpp	Fri Nov 28 09:42:56 2014 +0000
+++ b/src/Silvet.cpp	Fri Nov 28 10:18:22 2014 +0000
@@ -21,6 +21,7 @@
 #include "MedianFilter.h"
 #include "constant-q-cpp/src/dsp/Resampler.h"
 #include "flattendynamics-ladspa.h"
+#include "LiveInstruments.h"
 
 #include <vector>
 
@@ -33,7 +34,9 @@
 using Vamp::RealTime;
 
 static int processingSampleRate = 44100;
-static int processingBPO = 60;
+
+static int binsPerSemitoneLive = 1;
+static int binsPerSemitoneNormal = 5;
 
 static int minInputSampleRate = 100;
 static int maxInputSampleRate = 192000;
@@ -41,6 +44,7 @@
 Silvet::Silvet(float inputSampleRate) :
     Plugin(inputSampleRate),
     m_instruments(InstrumentPack::listInstrumentPacks()),
+    m_liveInstruments(LiveAdapter::adaptAll(m_instruments)),
     m_resampler(0),
     m_flattener(0),
     m_cq(0),
@@ -247,18 +251,18 @@
     d.description = "Filtered constant-Q time-frequency distribution as used as input to the expectation-maximisation algorithm.";
     d.unit = "";
     d.hasFixedBinCount = true;
-    d.binCount = m_instruments[0].templateHeight;
+    d.binCount = getPack(0).templateHeight;
     d.binNames.clear();
     if (m_cq) {
         char name[50];
-        for (int i = 0; i < m_instruments[0].templateHeight; ++i) {
+        for (int i = 0; i < getPack(0).templateHeight; ++i) {
             // We have a 600-bin (10 oct 60-bin CQ) of which the
             // lowest-frequency 55 bins have been dropped, for a
             // 545-bin template. The native CQ bins go high->low
             // frequency though, so these are still the first 545 bins
             // as reported by getBinFrequency, though in reverse order
             float freq = m_cq->getBinFrequency
-                (m_instruments[0].templateHeight - i - 1);
+                (getPack(0).templateHeight - i - 1);
             sprintf(name, "%.1f Hz", freq);
             d.binNames.push_back(name);
         }
@@ -276,10 +280,10 @@
     d.description = "Pitch activation distribution resulting from expectation-maximisation algorithm, prior to note extraction.";
     d.unit = "";
     d.hasFixedBinCount = true;
-    d.binCount = m_instruments[0].templateNoteCount;
+    d.binCount = getPack(0).templateNoteCount;
     d.binNames.clear();
     if (m_cq) {
-        for (int i = 0; i < m_instruments[0].templateNoteCount; ++i) {
+        for (int i = 0; i < getPack(0).templateNoteCount; ++i) {
             d.binNames.push_back(noteName(i, 0, 1));
         }
     }
@@ -406,10 +410,13 @@
         minFreq *= 2;
     }
 
+    int bpo = 12 *
+        (m_mode == LiveMode ? binsPerSemitoneLive : binsPerSemitoneNormal);
+        
     CQParameters params(processingSampleRate,
                         minFreq, 
                         processingSampleRate / 3,
-                        processingBPO);
+                        bpo);
 
     params.q = 0.95; // MIREX code uses 0.8, but it seems 0.9 or lower
                      // drops the FFT size to 512 from 1024 and alters
@@ -430,7 +437,7 @@
         delete m_postFilter[i];
     }
     m_postFilter.clear();
-    for (int i = 0; i < m_instruments[0].templateNoteCount; ++i) {
+    for (int i = 0; i < getPack(0).templateNoteCount; ++i) {
         m_postFilter.push_back(new MedianFilter<double>(3));
     }
     m_pianoRoll.clear();
@@ -506,7 +513,7 @@
 
     if (filtered.empty()) return fs;
     
-    const InstrumentPack &pack = m_instruments[m_instrument];
+    const InstrumentPack &pack(getPack(m_instrument));
 
     for (int i = 0; i < (int)filtered.size(); ++i) {
         Feature f;
@@ -633,7 +640,7 @@
     // size we reduce to in a moment
     int latentColumns = m_cq->getLatency() / m_cq->getColumnHop();
 
-    const InstrumentPack &pack = m_instruments[m_instrument];
+    const InstrumentPack &pack(getPack(m_instrument));
 
     for (int i = 0; i < width; ++i) {
 
@@ -652,7 +659,7 @@
             vector<double> outCol(pack.templateHeight);
 
             // In HQ mode, the CQ returns 600 bins and we ignore the
-            // lowest 55 of them.
+            // lowest 55 of them (assuming binsPerSemitone == 5).
             // 
             // In draft and live mode the CQ is an octave shorter,
             // returning 540 bins, so we instead pad them with an
@@ -662,29 +669,32 @@
             // raw CQ has the high frequencies first and we need it
             // the other way around.
 
+            int bps = (m_mode == LiveMode ?
+                       binsPerSemitoneLive : binsPerSemitoneNormal);
+            
             if (m_mode == HighQualityMode) {
                 for (int j = 0; j < pack.templateHeight; ++j) {
-                    int ix = inCol.size() - j - 55;
+                    int ix = inCol.size() - j - (11 * bps);
                     outCol[j] = inCol[ix];
                 }
             } else {
-                for (int j = 0; j < 5; ++j) {
+                for (int j = 0; j < bps; ++j) {
                     outCol[j] = 0.0;
                 }
-                for (int j = 5; j < pack.templateHeight; ++j) {
-                    int ix = inCol.size() - j + 4;
+                for (int j = bps; j < pack.templateHeight; ++j) {
+                    int ix = inCol.size() - j + (bps-1);
                     outCol[j] = inCol[ix];
                 }
             }
 
             vector<double> noiseLevel1 = 
-                MedianFilter<double>::filter(40, outCol);
+                MedianFilter<double>::filter(8 * bps, outCol);
             for (int j = 0; j < pack.templateHeight; ++j) {
                 noiseLevel1[j] = std::min(outCol[j], noiseLevel1[j]);
             }
 
             vector<double> noiseLevel2 = 
-                MedianFilter<double>::filter(40, noiseLevel1);
+                MedianFilter<double>::filter(8 * bps, noiseLevel1);
             for (int j = 0; j < pack.templateHeight; ++j) {
                 outCol[j] = std::max(outCol[j] - noiseLevel2[j], 0.0);
             }
@@ -703,7 +713,7 @@
                     const vector<int> &bestShifts,
                     bool wantShifts)
 {
-    const InstrumentPack &pack = m_instruments[m_instrument];
+    const InstrumentPack &pack(getPack(m_instrument));
 
     vector<double> filtered;
 
--- a/src/Silvet.h	Fri Nov 28 09:42:56 2014 +0000
+++ b/src/Silvet.h	Fri Nov 28 10:18:22 2014 +0000
@@ -73,7 +73,16 @@
 
 protected:
     const std::vector<InstrumentPack> m_instruments;
+    const std::vector<InstrumentPack> m_liveInstruments;
 
+    const InstrumentPack &getPack(int instrument) const {
+        if (m_mode == LiveMode) {
+            return m_liveInstruments[instrument];
+        } else {
+            return m_instruments[instrument];
+        }
+    }
+    
     Resampler *m_resampler;
     FlattenDynamics *m_flattener;
     CQSpectrogram *m_cq;