summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2008-05-15 19:37:35 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2008-05-15 19:37:35 +0000
commit5f61efff8f4d80c9576fb8524f37cb8905ba51a8 (patch)
tree65789ad104dd88209dfb1a518f610457a127650a
parent5ea9dee73b75b01e6db3a5a97c5a02493b358b00 (diff)
add note onset detection to the ferret, c/o the aubio-based Onset VAMP plugin (REQUIRES libaubio installed); add toggle-region-opaque menu item for scalable ops on region opacity
git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@3356 d708f5d6-7413-0410-9779-e7cbd77b26cf
-rw-r--r--SConstruct23
-rw-r--r--gtk2_ardour/ardour.menus1
-rw-r--r--gtk2_ardour/editor_actions.cc2
-rw-r--r--gtk2_ardour/rhythm_ferret.cc151
-rw-r--r--gtk2_ardour/rhythm_ferret.h25
-rw-r--r--libs/ardour/SConscript1
-rw-r--r--libs/ardour/ardour/onset_detector.h56
-rw-r--r--libs/ardour/onset_detector.cc126
-rw-r--r--libs/vamp-plugins/Onset.cpp280
-rw-r--r--libs/vamp-plugins/Onset.h73
-rw-r--r--libs/vamp-plugins/SConscript22
-rw-r--r--libs/vamp-plugins/plugins.cpp3
12 files changed, 748 insertions, 15 deletions
diff --git a/SConstruct b/SConstruct
index 157c387ca5..2e09c25328 100644
--- a/SConstruct
+++ b/SConstruct
@@ -57,7 +57,9 @@ opts.AddOptions(
BoolOption('LV2', 'Compile with support for LV2 (if slv2 is available)', 0),
BoolOption('GPROFILE', 'Compile with support for gprofile (Developers only)', 0),
BoolOption('FREEDESKTOP', 'Install MIME type, icons and .desktop file as per the freedesktop.org spec (requires xdg-utils and shared-mime-info). "scons uninstall" removes associations in desktop database', 0),
- BoolOption('TRANZPORT', 'Compile with support for Frontier Designs (if libusb is available)', 1)
+ BoolOption('TRANZPORT', 'Compile with support for Frontier Designs (if libusb is available)', 1),
+ BoolOption('AUBIO', "Use Paul Brossier's aubio library for feature detection (if available)", 1)
+
)
#----------------------------------------------------------------------
@@ -450,7 +452,8 @@ deps = \
'raptor' : '1.4.2',
'lrdf' : '0.4.0',
'jack' : '0.101.1',
- 'libgnomecanvas-2.0' : '2.0'
+ 'libgnomecanvas-2.0' : '2.0',
+ 'aubio' : '0.3.2'
}
def DependenciesRequiredMessage():
@@ -523,6 +526,10 @@ if conf.CheckPKGExists ('fftw3'):
libraries['fftw3'] = LibraryInfo()
libraries['fftw3'].ParseConfig('pkg-config --cflags --libs fftw3')
+if conf.CheckPKGExists ('aubio'):
+ libraries['aubio'] = LibraryInfo()
+ libraries['aubio'].ParseConfig('pkg-config --cflags --libs aubio')
+
env = conf.Finish ()
if env['FFT_ANALYSIS']:
@@ -537,6 +544,18 @@ if env['FFT_ANALYSIS']:
sys.exit (1)
conf.Finish()
+if env['AUBIO']:
+ #
+ # Check for aubio header as well as the library
+ #
+
+ conf = Configure(libraries['aubio'])
+
+ if conf.CheckHeader ('aubio/aubio.h') == False:
+ print ('AUBIO-related features be compiled without the aubio headers, which do not seem to be installed')
+ env['AUBIO'] = 0
+ conf.Finish()
+
if env['FREESOUND']:
#
# Check for curl header as well as the library
diff --git a/gtk2_ardour/ardour.menus b/gtk2_ardour/ardour.menus
index 5df1eceef2..91d48477c4 100644
--- a/gtk2_ardour/ardour.menus
+++ b/gtk2_ardour/ardour.menus
@@ -224,6 +224,7 @@
<menuitem action='boost-region-gain'/>
<menuitem action='cut-region-gain'/>
<menuitem action='pitch-shift-region'/>
+ <menuitem action='toggle-opaque-region'/>
<separator/>
<menuitem action='duplicate-region'/>
<menuitem action='multi-duplicate-region'/>
diff --git a/gtk2_ardour/editor_actions.cc b/gtk2_ardour/editor_actions.cc
index d7611eb350..5899293e84 100644
--- a/gtk2_ardour/editor_actions.cc
+++ b/gtk2_ardour/editor_actions.cc
@@ -365,6 +365,8 @@ Editor::register_actions ()
ActionManager::region_selection_sensitive_actions.push_back (act);
act = ActionManager::register_action (editor_actions, "pitch-shift-region", _("Transpose"), mem_fun(*this, &Editor::pitch_shift_regions));
ActionManager::session_sensitive_actions.push_back (act);
+ act = ActionManager::register_action (editor_actions, "toggle-opaque-region", _("Toggle Opaque"), mem_fun(*this, &Editor::toggle_region_opaque));
+ ActionManager::session_sensitive_actions.push_back (act);
ActionManager::region_selection_sensitive_actions.push_back (act);
act = ActionManager::register_action (editor_actions, "set-fade-in-length", _("Set Fade In Length"), bind (mem_fun(*this, &Editor::set_fade_length), true));
diff --git a/gtk2_ardour/rhythm_ferret.cc b/gtk2_ardour/rhythm_ferret.cc
index 92bd2360a0..4a4f3d4ec4 100644
--- a/gtk2_ardour/rhythm_ferret.cc
+++ b/gtk2_ardour/rhythm_ferret.cc
@@ -4,6 +4,7 @@
#include <pbd/memento_command.h>
#include <ardour/transient_detector.h>
+#include <ardour/onset_detector.h>
#include <ardour/audiosource.h>
#include <ardour/audioregion.h>
#include <ardour/playlist.h>
@@ -32,6 +33,17 @@ static const gchar * _analysis_mode_strings[] = {
0
};
+static const gchar * _onset_function_strings[] = {
+ N_("Energy Based"),
+ N_("Spectral Difference"),
+ N_("High-Frequency Content"),
+ N_("Complex Domain"),
+ N_("Phase Deviation"),
+ N_("Kullback-Liebler"),
+ N_("Modified Kullback-Liebler"),
+ 0
+};
+
RhythmFerret::RhythmFerret (PublicEditor& e)
: ArdourDialog (_("Rhythm Ferret"))
, editor (e)
@@ -50,6 +62,13 @@ RhythmFerret::RhythmFerret (PublicEditor& e)
, sensitivity_scale (sensitivity_adjustment)
, sensitivity_label (_("Sensitivity"))
, analyze_button (_("Analyze"))
+ , onset_function_label (_("Detection function"))
+ , peak_picker_threshold_adjustment (0.3, 0.0, 1.0, 0.01, 0.1)
+ , peak_picker_threshold_scale (peak_picker_threshold_adjustment)
+ , peak_picker_label (_("Peak Threshold"))
+ , silence_threshold_adjustment (-90.0, -120.0, 0.0, 1, 10)
+ , silence_threshold_scale (silence_threshold_adjustment)
+ , silence_label (_("Silent Threshold (dB)"))
, trigger_gap_adjustment (3, 0, 100, 1, 10)
, trigger_gap_spinner (trigger_gap_adjustment)
, trigger_gap_label (_("Trigger gap (msecs)"))
@@ -78,6 +97,14 @@ RhythmFerret::RhythmFerret (PublicEditor& e)
analysis_mode_strings = I18N (_analysis_mode_strings);
Gtkmm2ext::set_popdown_strings (analysis_mode_selector, analysis_mode_strings);
analysis_mode_selector.set_active_text (analysis_mode_strings.front());
+ analysis_mode_selector.signal_changed().connect (mem_fun (*this, &RhythmFerret::analysis_mode_changed));
+
+ onset_function_strings = I18N (_onset_function_strings);
+ Gtkmm2ext::set_popdown_strings (onset_detection_function_selector, onset_function_strings);
+ /* Onset plugin uses complex domain as default function
+ XXX there should be a non-hacky way to set this
+ */
+ onset_detection_function_selector.set_active_text (onset_function_strings[3]);
box = manage (new HBox);
box->set_spacing (6);
@@ -85,28 +112,50 @@ RhythmFerret::RhythmFerret (PublicEditor& e)
box->pack_start (analysis_mode_selector, true, true);
ferret_packer.pack_start (*box, false, false);
+ ferret_packer.pack_start (analysis_packer, false, false);
+
box = manage (new HBox);
box->set_spacing (6);
- box->pack_start (detection_threshold_label, false, false);
- box->pack_start (detection_threshold_scale, true, true);
+ box->pack_start (trigger_gap_label, false, false);
+ box->pack_start (trigger_gap_spinner, false, false);
ferret_packer.pack_start (*box, false, false);
+ ferret_packer.pack_start (analyze_button, false, false);
+
+ analyze_button.signal_clicked().connect (mem_fun (*this, &RhythmFerret::run_analysis));
+
+ box = manage (new HBox);
+ box->set_spacing (6);
+ box->pack_start (detection_threshold_label, false, false);
+ box->pack_start (detection_threshold_scale, true, true);
+ perc_onset_packer.pack_start (*box, false, false);
+
box = manage (new HBox);
box->set_spacing (6);
box->pack_start (sensitivity_label, false, false);
box->pack_start (sensitivity_scale, true, true);
- ferret_packer.pack_start (*box, false, false);
+ perc_onset_packer.pack_start (*box, false, false);
box = manage (new HBox);
box->set_spacing (6);
- box->pack_start (trigger_gap_label, false, false);
- box->pack_start (trigger_gap_spinner, false, false);
- ferret_packer.pack_start (*box, false, false);
+ box->pack_start (onset_function_label, false, false);
+ box->pack_start (onset_detection_function_selector, true, true);
+ note_onset_packer.pack_start (*box, false, false);
+
+ box = manage (new HBox);
+ box->set_spacing (6);
+ box->pack_start (peak_picker_label, false, false);
+ box->pack_start (peak_picker_threshold_scale, true, true);
+ note_onset_packer.pack_start (*box, false, false);
+
+ box = manage (new HBox);
+ box->set_spacing (6);
+ box->pack_start (silence_label, false, false);
+ box->pack_start (silence_threshold_scale, true, true);
+ note_onset_packer.pack_start (*box, false, false);
- ferret_packer.pack_start (analyze_button, false, false);
+ analysis_mode_changed ();
- analyze_button.signal_clicked().connect (mem_fun (*this, &RhythmFerret::run_analysis));
-
ferret_frame.add (ferret_packer);
logo = manage (new Gtk::Image (::get_icon (X_("ferret_02"))));
@@ -137,12 +186,30 @@ RhythmFerret::~RhythmFerret()
}
}
+void
+RhythmFerret::analysis_mode_changed ()
+{
+ analysis_packer.children().clear ();
+
+ switch (get_analysis_mode()) {
+ case PercussionOnset:
+ analysis_packer.pack_start (perc_onset_packer);
+ break;
+
+ case NoteOnset:
+ analysis_packer.pack_start (note_onset_packer);
+ break;
+ }
+
+ analysis_packer.show_all ();
+}
+
RhythmFerret::AnalysisMode
RhythmFerret::get_analysis_mode () const
{
string str = analysis_mode_selector.get_active_text ();
- if (str == _(_analysis_mode_strings[(int) NoteOnset])) {
+ if (str == analysis_mode_strings[(int) NoteOnset]) {
return NoteOnset;
}
@@ -184,6 +251,9 @@ RhythmFerret::run_analysis ()
case PercussionOnset:
run_percussion_onset_analysis (rd, (*i)->region()->position(), current_results);
break;
+ case NoteOnset:
+ run_note_onset_analysis (rd, (*i)->region()->position(), current_results);
+ break;
default:
break;
}
@@ -232,6 +302,67 @@ RhythmFerret::run_percussion_onset_analysis (boost::shared_ptr<Readable> readabl
return 0;
}
+int
+RhythmFerret::get_note_onset_function ()
+{
+ string txt = onset_detection_function_selector.get_active_text();
+
+ for (int n = 0; _onset_function_strings[n]; ++n) {
+ /* compare translated versions */
+ if (txt == onset_function_strings[n]) {
+ return n;
+ }
+ }
+ fatal << string_compose (_("programming error: %1 (%2)"), X_("illegal note onset function string"), txt)
+ << endmsg;
+ /*NOTREACHED*/
+ return -1;
+}
+
+int
+RhythmFerret::run_note_onset_analysis (boost::shared_ptr<Readable> readable, nframes64_t offset, AnalysisFeatureList& results)
+{
+ try {
+ OnsetDetector t (session->frame_rate());
+
+ for (uint32_t i = 0; i < readable->n_channels(); ++i) {
+
+ AnalysisFeatureList these_results;
+
+ t.reset ();
+
+ t.set_function (get_note_onset_function());
+ t.set_silence_threshold (silence_threshold_adjustment.get_value());
+ t.set_peak_threshold (peak_picker_threshold_adjustment.get_value());
+
+ if (t.run ("", readable.get(), i, these_results)) {
+ continue;
+ }
+
+ /* translate all transients to give absolute position */
+
+ for (AnalysisFeatureList::iterator x = these_results.begin(); x != these_results.end(); ++x) {
+ (*x) += offset;
+ }
+
+ /* merge */
+
+ results.insert (results.end(), these_results.begin(), these_results.end());
+ these_results.clear ();
+ }
+
+ } catch (failed_constructor& err) {
+ error << "Could not load note onset detection plugin" << endmsg;
+ return -1;
+ }
+
+ if (!results.empty()) {
+ OnsetDetector::cleanup_onsets (results, session->frame_rate(), trigger_gap_adjustment.get_value());
+ }
+
+ return 0;
+}
+
void
RhythmFerret::do_action ()
{
diff --git a/gtk2_ardour/rhythm_ferret.h b/gtk2_ardour/rhythm_ferret.h
index 2ddb2a4ffd..91c10695ad 100644
--- a/gtk2_ardour/rhythm_ferret.h
+++ b/gtk2_ardour/rhythm_ferret.h
@@ -71,6 +71,9 @@ class RhythmFerret : public ArdourDialog {
Gtk::VBox ferret_packer;
Gtk::ComboBoxText analysis_mode_selector;
Gtk::Label analysis_mode_label;
+
+ /* transient detection widgets */
+
Gtk::Adjustment detection_threshold_adjustment;
Gtk::HScale detection_threshold_scale;
Gtk::Label detection_threshold_label;
@@ -78,22 +81,44 @@ class RhythmFerret : public ArdourDialog {
Gtk::HScale sensitivity_scale;
Gtk::Label sensitivity_label;
Gtk::Button analyze_button;
+ Gtk::VBox perc_onset_packer;
+
+ /* onset detection widgets */
+
+ Gtk::ComboBoxText onset_detection_function_selector;
+ Gtk::Label onset_function_label;
+ Gtk::Adjustment peak_picker_threshold_adjustment;
+ Gtk::HScale peak_picker_threshold_scale;
+ Gtk::Label peak_picker_label;
+ Gtk::Adjustment silence_threshold_adjustment;
+ Gtk::HScale silence_threshold_scale;
+ Gtk::Label silence_label;
+ Gtk::VBox note_onset_packer;
+
+ /* generic stuff */
+
Gtk::Adjustment trigger_gap_adjustment;
Gtk::SpinButton trigger_gap_spinner;
Gtk::Label trigger_gap_label;
+ Gtk::VBox analysis_packer;
+
Gtk::Label operation_clarification_label;
Gtk::Button action_button;
std::vector<std::string> analysis_mode_strings;
+ std::vector<std::string> onset_function_strings;
ARDOUR::AnalysisFeatureList current_results;
AnalysisMode get_analysis_mode () const;
Action get_action() const;
+ void analysis_mode_changed ();
+ int get_note_onset_function ();
void run_analysis ();
int run_percussion_onset_analysis (boost::shared_ptr<ARDOUR::Readable> region, nframes64_t offset, ARDOUR::AnalysisFeatureList& results);
+ int run_note_onset_analysis (boost::shared_ptr<ARDOUR::Readable> region, nframes64_t offset, ARDOUR::AnalysisFeatureList& results);
void do_action ();
void do_split_action ();
diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript
index d671387e66..261ed4c697 100644
--- a/libs/ardour/SConscript
+++ b/libs/ardour/SConscript
@@ -65,6 +65,7 @@ location.cc
mix.cc
mtc_slave.cc
named_selection.cc
+onset_detector.cc
panner.cc
pcm_utils.cc
playlist.cc
diff --git a/libs/ardour/ardour/onset_detector.h b/libs/ardour/ardour/onset_detector.h
new file mode 100644
index 0000000000..9243653d94
--- /dev/null
+++ b/libs/ardour/ardour/onset_detector.h
@@ -0,0 +1,56 @@
+/*
+ Copyright (C) 2008 Paul Davis
+
+ 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.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef __ardour_onset_detector_h__
+#define __ardour_onset_detector_h__
+
+#include <ardour/audioanalyser.h>
+
+namespace ARDOUR {
+
+class AudioSource;
+class Session;
+
+class OnsetDetector : public AudioAnalyser
+{
+
+ public:
+ OnsetDetector (float sample_rate);
+ ~OnsetDetector();
+
+ static std::string operational_identifier();
+
+ void set_silence_threshold (float);
+ void set_peak_threshold (float);
+ void set_function (int);
+
+ int run (const std::string& path, Readable*, uint32_t channel, AnalysisFeatureList& results);
+
+ static void cleanup_onsets (AnalysisFeatureList&, float sr, float gap_msecs);
+
+ protected:
+ AnalysisFeatureList* current_results;
+ int use_features (Vamp::Plugin::FeatureSet&, std::ostream*);
+
+ static std::string _op_id;
+};
+
+} /* namespace */
+
+#endif /* __ardour_audioanalyser_h__ */
diff --git a/libs/ardour/onset_detector.cc b/libs/ardour/onset_detector.cc
new file mode 100644
index 0000000000..f58e64ad4a
--- /dev/null
+++ b/libs/ardour/onset_detector.cc
@@ -0,0 +1,126 @@
+#include <ardour/onset_detector.h>
+
+#include "i18n.h"
+
+using namespace Vamp;
+using namespace ARDOUR;
+using namespace std;
+
+/* need a static initializer function for this */
+
+string OnsetDetector::_op_id = X_("libardourvampplugins:aubioonset:2");
+
+OnsetDetector::OnsetDetector (float sr)
+ : AudioAnalyser (sr, X_("libardourvampplugins:aubioonset"))
+{
+ /* update the op_id */
+
+ _op_id = X_("libardourvampplugins:aubioonset");
+
+ // XXX this should load the above-named plugin and get the current version
+
+ _op_id += ":2";
+}
+
+OnsetDetector::~OnsetDetector()
+{
+}
+
+string
+OnsetDetector::operational_identifier()
+{
+ return _op_id;
+}
+
+int
+OnsetDetector::run (const std::string& path, Readable* src, uint32_t channel, AnalysisFeatureList& results)
+{
+ current_results = &results;
+ int ret = analyse (path, src, channel);
+
+ current_results = 0;
+ return ret;
+}
+
+int
+OnsetDetector::use_features (Plugin::FeatureSet& features, ostream* out)
+{
+ const Plugin::FeatureList& fl (features[0]);
+
+ for (Plugin::FeatureList::const_iterator f = fl.begin(); f != fl.end(); ++f) {
+
+ if ((*f).hasTimestamp) {
+
+ if (out) {
+ (*out) << (*f).timestamp.toString() << endl;
+ }
+
+ current_results->push_back (RealTime::realTime2Frame ((*f).timestamp, (nframes_t) floor(sample_rate)));
+ }
+ }
+
+ return 0;
+}
+
+void
+OnsetDetector::set_silence_threshold (float val)
+{
+ if (plugin) {
+ plugin->setParameter ("silencethreshold", val);
+ }
+}
+
+void
+OnsetDetector::set_peak_threshold (float val)
+{
+ if (plugin) {
+ plugin->setParameter ("peakpickthreshold", val);
+ }
+}
+
+void
+OnsetDetector::set_function (int val)
+{
+ if (plugin) {
+ plugin->setParameter ("onsettype", (float) val);
+ }
+}
+
+void
+OnsetDetector::cleanup_onsets (AnalysisFeatureList& t, float sr, float gap_msecs)
+{
+ if (t.empty()) {
+ return;
+ }
+
+ t.sort ();
+
+ /* remove duplicates or other things that are too close */
+
+ AnalysisFeatureList::iterator i = t.begin();
+ AnalysisFeatureList::iterator f, b;
+ const nframes64_t gap_frames = (nframes64_t) floor (gap_msecs * (sr / 1000.0));
+
+ while (i != t.end()) {
+
+ // move front iterator to just past i, and back iterator the same place
+
+ f = i;
+ ++f;
+ b = f;
+
+ // move f until we find a new value that is far enough away
+
+ while ((f != t.end()) && (((*f) - (*i)) < gap_frames)) {
+ ++f;
+ }
+
+ i = f;
+
+ // if f moved forward from b, we had duplicates/too-close points: get rid of them
+
+ if (b != f) {
+ t.erase (b, f);
+ }
+ }
+}
diff --git a/libs/vamp-plugins/Onset.cpp b/libs/vamp-plugins/Onset.cpp
new file mode 100644
index 0000000000..d475b11be9
--- /dev/null
+++ b/libs/vamp-plugins/Onset.cpp
@@ -0,0 +1,280 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Vamp feature extraction plugins using Paul Brossier's Aubio library.
+
+ Centre for Digital Music, Queen Mary, University of London.
+ This file copyright 2006 Chris Cannam.
+
+ 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 <math.h>
+#include "Onset.h"
+
+using std::string;
+using std::vector;
+using std::cerr;
+using std::endl;
+
+Onset::Onset(float inputSampleRate) :
+ Plugin(inputSampleRate),
+ m_ibuf(0),
+ m_fftgrain(0),
+ m_onset(0),
+ m_pv(0),
+ m_peakpick(0),
+ m_onsetdet(0),
+ m_onsettype(aubio_onset_complex),
+ m_threshold(0.3),
+ m_silence(-90),
+ m_channelCount(1)
+{
+}
+
+Onset::~Onset()
+{
+ if (m_onsetdet) aubio_onsetdetection_free(m_onsetdet);
+ if (m_ibuf) del_fvec(m_ibuf);
+ if (m_onset) del_fvec(m_onset);
+ if (m_fftgrain) del_cvec(m_fftgrain);
+ if (m_pv) del_aubio_pvoc(m_pv);
+ if (m_peakpick) del_aubio_peakpicker(m_peakpick);
+}
+
+string
+Onset::getIdentifier() const
+{
+ return "aubioonset";
+}
+
+string
+Onset::getName() const
+{
+ return "Aubio Onset Detector";
+}
+
+string
+Onset::getDescription() const
+{
+ return "Estimate note onset times";
+}
+
+string
+Onset::getMaker() const
+{
+ return "Paul Brossier (plugin by Chris Cannam)";
+}
+
+int
+Onset::getPluginVersion() const
+{
+ return 1;
+}
+
+string
+Onset::getCopyright() const
+{
+ return "GPL";
+}
+
+bool
+Onset::initialise(size_t channels, size_t stepSize, size_t blockSize)
+{
+ m_channelCount = channels;
+ m_stepSize = stepSize;
+ m_blockSize = blockSize;
+
+ m_ibuf = new_fvec(stepSize, channels);
+ m_onset = new_fvec(1, channels);
+ m_fftgrain = new_cvec(blockSize, channels);
+ m_pv = new_aubio_pvoc(blockSize, stepSize, channels);
+ m_peakpick = new_aubio_peakpicker(m_threshold);
+
+ m_onsetdet = new_aubio_onsetdetection(m_onsettype, blockSize, channels);
+
+ m_delay = Vamp::RealTime::frame2RealTime(4 * stepSize,
+ lrintf(m_inputSampleRate));
+
+ m_lastOnset = Vamp::RealTime::zeroTime - m_delay - m_delay;
+
+ return true;
+}
+
+void
+Onset::reset()
+{
+}
+
+size_t
+Onset::getPreferredStepSize() const
+{
+ return 512;
+}
+
+size_t
+Onset::getPreferredBlockSize() const
+{
+ return 2 * getPreferredStepSize();
+}
+
+Onset::ParameterList
+Onset::getParameterDescriptors() const
+{
+ ParameterList list;
+
+ ParameterDescriptor desc;
+ desc.identifier = "onsettype";
+ desc.name = "Onset Detection Function Type";
+ desc.minValue = 0;
+ desc.maxValue = 6;
+ desc.defaultValue = (int)aubio_onset_complex;
+ desc.isQuantized = true;
+ desc.quantizeStep = 1;
+ desc.valueNames.push_back("Energy Based");
+ desc.valueNames.push_back("Spectral Difference");
+ desc.valueNames.push_back("High-Frequency Content");
+ desc.valueNames.push_back("Complex Domain");
+ desc.valueNames.push_back("Phase Deviation");
+ desc.valueNames.push_back("Kullback-Liebler");
+ desc.valueNames.push_back("Modified Kullback-Liebler");
+ list.push_back(desc);
+
+ desc = ParameterDescriptor();
+ desc.identifier = "peakpickthreshold";
+ desc.name = "Peak Picker Threshold";
+ desc.minValue = 0;
+ desc.maxValue = 1;
+ desc.defaultValue = 0.3;
+ desc.isQuantized = false;
+ list.push_back(desc);
+
+ desc = ParameterDescriptor();
+ desc.identifier = "silencethreshold";
+ desc.name = "Silence Threshold";
+ desc.minValue = -120;
+ desc.maxValue = 0;
+ desc.defaultValue = -90;
+ desc.unit = "dB";
+ desc.isQuantized = false;
+ list.push_back(desc);
+
+ return list;
+}
+
+float
+Onset::getParameter(std::string param) const
+{
+ if (param == "onsettype") {
+ return m_onsettype;
+ } else if (param == "peakpickthreshold") {
+ return m_threshold;
+ } else if (param == "silencethreshold") {
+ return m_silence;
+ } else {
+ return 0.0;
+ }
+}
+
+void
+Onset::setParameter(std::string param, float value)
+{
+ if (param == "onsettype") {
+ switch (lrintf(value)) {
+ case 0: m_onsettype = aubio_onset_energy; break;
+ case 1: m_onsettype = aubio_onset_specdiff; break;
+ case 2: m_onsettype = aubio_onset_hfc; break;
+ case 3: m_onsettype = aubio_onset_complex; break;
+ case 4: m_onsettype = aubio_onset_phase; break;
+ case 5: m_onsettype = aubio_onset_kl; break;
+ case 6: m_onsettype = aubio_onset_mkl; break;
+ }
+ } else if (param == "peakpickthreshold") {
+ m_threshold = value;
+ } else if (param == "silencethreshold") {
+ m_silence = value;
+ }
+}
+
+Onset::OutputList
+Onset::getOutputDescriptors() const
+{
+ OutputList list;
+
+ OutputDescriptor d;
+ d.identifier = "onsets";
+ d.name = "Onsets";
+ d.unit = "";
+ d.hasFixedBinCount = true;
+ d.binCount = 0;
+ d.sampleType = OutputDescriptor::VariableSampleRate;
+ d.sampleRate = 0;
+ list.push_back(d);
+
+ d = OutputDescriptor();
+ d.identifier = "detectionfunction";
+ d.name = "Onset Detection Function";
+ d.unit = "";
+ d.hasFixedBinCount = true;
+ d.binCount = m_channelCount;
+ d.hasKnownExtents = false;
+ d.isQuantized = false;
+ d.sampleType = OutputDescriptor::OneSamplePerStep;
+ list.push_back(d);
+
+ return list;
+}
+
+Onset::FeatureSet
+Onset::process(const float *const *inputBuffers,
+ Vamp::RealTime timestamp)
+{
+ for (size_t i = 0; i < m_stepSize; ++i) {
+ for (size_t j = 0; j < m_channelCount; ++j) {
+ fvec_write_sample(m_ibuf, inputBuffers[j][i], j, i);
+ }
+ }
+
+ aubio_pvoc_do(m_pv, m_ibuf, m_fftgrain);
+ aubio_onsetdetection(m_onsetdet, m_fftgrain, m_onset);
+
+ bool isonset = aubio_peakpick_pimrt(m_onset, m_peakpick);
+
+ if (isonset) {
+ if (aubio_silence_detection(m_ibuf, m_silence)) {
+ isonset = false;
+ }
+ }
+
+ FeatureSet returnFeatures;
+
+ if (isonset) {
+ if (timestamp - m_lastOnset >= m_delay) {
+ Feature onsettime;
+ onsettime.hasTimestamp = true;
+ if (timestamp < m_delay) timestamp = m_delay;
+ onsettime.timestamp = timestamp - m_delay;
+ returnFeatures[0].push_back(onsettime);
+ m_lastOnset = timestamp;
+ }
+ }
+ Feature feature;
+ for (size_t j = 0; j < m_channelCount; ++j) {
+ feature.values.push_back(m_onset->data[j][0]);
+ }
+ returnFeatures[1].push_back(feature);
+
+ return returnFeatures;
+}
+
+Onset::FeatureSet
+Onset::getRemainingFeatures()
+{
+ return FeatureSet();
+}
+
diff --git a/libs/vamp-plugins/Onset.h b/libs/vamp-plugins/Onset.h
new file mode 100644
index 0000000000..314e107308
--- /dev/null
+++ b/libs/vamp-plugins/Onset.h
@@ -0,0 +1,73 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Vamp feature extraction plugins using Paul Brossier's Aubio library.
+
+ Centre for Digital Music, Queen Mary, University of London.
+ This file copyright 2006 Chris Cannam.
+
+ 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 _ONSET_PLUGIN_H_
+#define _ONSET_PLUGIN_H_
+
+#include <vamp-sdk/Plugin.h>
+#include <aubio/aubio.h>
+
+class Onset : public Vamp::Plugin
+{
+public:
+ Onset(float inputSampleRate);
+ virtual ~Onset();
+
+ bool initialise(size_t channels, size_t stepSize, size_t blockSize);
+ void reset();
+
+ InputDomain getInputDomain() const { return TimeDomain; }
+
+ std::string getIdentifier() const;
+ std::string getName() const;
+ std::string getDescription() const;
+ std::string getMaker() const;
+ int getPluginVersion() const;
+ std::string getCopyright() const;
+
+ ParameterList getParameterDescriptors() const;
+ float getParameter(std::string) const;
+ void setParameter(std::string, float);
+
+ size_t getPreferredStepSize() const;
+ size_t getPreferredBlockSize() const;
+
+ OutputList getOutputDescriptors() const;
+
+ FeatureSet process(const float *const *inputBuffers,
+ Vamp::RealTime timestamp);
+
+ FeatureSet getRemainingFeatures();
+
+protected:
+ fvec_t *m_ibuf;
+ cvec_t *m_fftgrain;
+ fvec_t *m_onset;
+ aubio_pvoc_t *m_pv;
+ aubio_pickpeak_t *m_peakpick;
+ aubio_onsetdetection_t *m_onsetdet;
+ aubio_onsetdetection_type m_onsettype;
+ float m_threshold;
+ float m_silence;
+ size_t m_stepSize;
+ size_t m_blockSize;
+ size_t m_channelCount;
+ Vamp::RealTime m_delay;
+ Vamp::RealTime m_lastOnset;
+};
+
+
+#endif
diff --git a/libs/vamp-plugins/SConscript b/libs/vamp-plugins/SConscript
index a35b789ef3..0828b81b66 100644
--- a/libs/vamp-plugins/SConscript
+++ b/libs/vamp-plugins/SConscript
@@ -4,7 +4,17 @@ import os
import os.path
import glob
-plugin_files = glob.glob ("*.cpp")
+plugin_files = Split("""
+plugins.cpp
+AmplitudeFollower.cpp
+PercussionOnsetDetector.cpp
+SpectralCentroid.cpp
+ZeroCrossing.cpp
+""")
+
+aubio_files = Split ("""
+Onset.cpp
+""")
Import('env install_prefix libraries')
vampplugs = env.Copy()
@@ -14,7 +24,13 @@ vampplugs.Merge ([libraries['vamp'],
libraries['vamphost']
])
-libvampplugins = vampplugs.SharedLibrary('ardourvampplugins', plugin_files)
+sources = plugin_files
+
+if vampplugs['AUBIO']:
+ sources += aubio_files
+ vampplugs.Merge ([libraries['aubio']])
+
+libvampplugins = vampplugs.SharedLibrary('ardourvampplugins', sources)
Default(libvampplugins)
@@ -22,5 +38,5 @@ env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ar
env.Alias('tarball', env.Distribute (env['DISTTREE'],
[ 'SConscript' ] +
- plugin_files +
+ plugin_files + aubio_files +
glob.glob('*.h')))
diff --git a/libs/vamp-plugins/plugins.cpp b/libs/vamp-plugins/plugins.cpp
index 25c6e6c0d4..c45912be9b 100644
--- a/libs/vamp-plugins/plugins.cpp
+++ b/libs/vamp-plugins/plugins.cpp
@@ -41,11 +41,13 @@
#include "SpectralCentroid.h"
#include "PercussionOnsetDetector.h"
#include "AmplitudeFollower.h"
+#include "Onset.h"
static Vamp::PluginAdapter<ZeroCrossing> zeroCrossingAdapter;
static Vamp::PluginAdapter<SpectralCentroid> spectralCentroidAdapter;
static Vamp::PluginAdapter<PercussionOnsetDetector> percussionOnsetAdapter;
static Vamp::PluginAdapter<AmplitudeFollower> amplitudeAdapter;
+static Vamp::PluginAdapter<Onset> onsetAdapter;
const VampPluginDescriptor *vampGetPluginDescriptor(unsigned int version,
unsigned int index)
@@ -57,6 +59,7 @@ const VampPluginDescriptor *vampGetPluginDescriptor(unsigned int version,
case 1: return spectralCentroidAdapter.getDescriptor();
case 2: return percussionOnsetAdapter.getDescriptor();
case 3: return amplitudeAdapter.getDescriptor();
+ case 4: return onsetAdapter.getDescriptor();
default: return 0;
}
}