summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2008-01-25 05:35:46 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2008-01-25 05:35:46 +0000
commitd3f64c28489d2358498b9d7dcfc6fa4228ebd63e (patch)
tree8195496c9330bb4fb279d282598da6dc5c98a361
parent28e6ad009158ddaea80fd5d800befcbf58ce47ee (diff)
meet rhythm ferret: cute, furry and always on time (ardour build now requires fftw3 & fftw3f, no exceptions, ever)
git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@2959 d708f5d6-7413-0410-9779-e7cbd77b26cf
-rw-r--r--SConstruct7
-rw-r--r--gtk2_ardour/SConscript8
-rw-r--r--gtk2_ardour/ardour.menus1
-rw-r--r--gtk2_ardour/au_pluginui.h1
-rw-r--r--gtk2_ardour/editor.cc18
-rw-r--r--gtk2_ardour/editor.h6
-rw-r--r--gtk2_ardour/editor_actions.cc3
-rw-r--r--gtk2_ardour/editor_ops.cc82
-rw-r--r--gtk2_ardour/rhythm_ferret.cc376
-rw-r--r--gtk2_ardour/rhythm_ferret.h100
-rw-r--r--gtk2_ardour/time_axis_view.cc35
-rw-r--r--gtk2_ardour/time_axis_view.h5
-rw-r--r--libs/ardour/SConscript9
-rw-r--r--libs/ardour/ardour/audioanalyser.h74
-rw-r--r--libs/ardour/ardour/audioregion.h26
-rw-r--r--libs/ardour/ardour/audiosource.h17
-rw-r--r--libs/ardour/ardour/plugin_manager.h2
-rw-r--r--libs/ardour/ardour/region.h3
-rw-r--r--libs/ardour/ardour/region_factory.h2
-rw-r--r--libs/ardour/ardour/session.h3
-rw-r--r--libs/ardour/ardour/source.h3
-rw-r--r--libs/ardour/ardour/transient_detector.h52
-rw-r--r--libs/ardour/audioanalyser.cc157
-rw-r--r--libs/ardour/audioregion.cc187
-rw-r--r--libs/ardour/audiosource.cc50
-rw-r--r--libs/ardour/region_factory.cc2
-rw-r--r--libs/ardour/session.cc4
-rw-r--r--libs/ardour/session_state.cc48
-rw-r--r--libs/ardour/transient_detector.cc62
29 files changed, 1229 insertions, 114 deletions
diff --git a/SConstruct b/SConstruct
index 961fa1afc2..ffa61a0b5a 100644
--- a/SConstruct
+++ b/SConstruct
@@ -793,9 +793,16 @@ def prep_libcheck(topenv, libinfo):
prep_libcheck(env, env)
+#
+# these are part of the Ardour source tree because they are C++
+#
+
libraries['vamp'] = LibraryInfo (LIBS='vampsdk',
LIBPATH='#libs/vamp-sdk',
CPPPATH='#libs/vamp-sdk/vamp')
+libraries['vamphost'] = LibraryInfo (LIBS='vamphostsdk',
+ LIBPATH='#libs/vamp-sdk',
+ CPPPATH='#libs/vamp-sdk/vamp')
env['RUBBERBAND'] = False
diff --git a/gtk2_ardour/SConscript b/gtk2_ardour/SConscript
index 3ece6ee91c..f8d107a8a9 100644
--- a/gtk2_ardour/SConscript
+++ b/gtk2_ardour/SConscript
@@ -50,6 +50,10 @@ gtkardour.Merge ([
libraries['xml'],
libraries['xslt'],
libraries['samplerate'],
+ libraries['vamp'],
+ libraries['vamphost'],
+ libraries['fftw3f'],
+ libraries['fftw3'],
libraries['jack']
])
@@ -75,7 +79,7 @@ if gtkardour['FFT_ANALYSIS']:
gtkardour.Append(CCFLAGS='-DFFT_ANALYSIS')
if gtkardour['RUBBERBAND']:
- gtkardour.Merge ([ libraries['rubberband'], libraries['vamp'], libraries['fftw3f'], libraries['fftw3'] ])
+ gtkardour.Merge ([ libraries['rubberband'] ])
else:
gtkardour.Merge ([ libraries['soundtouch'] ])
@@ -188,7 +192,6 @@ new_session_dialog.cc
option_editor.cc
opts.cc
pan_automation_time_axis.cc
-
panner.cc
panner2d.cc
panner_ui.cc
@@ -200,6 +203,7 @@ public_editor.cc
redirect_automation_line.cc
redirect_automation_time_axis.cc
redirect_box.cc
+rhythm_ferret.cc
audio_region_editor.cc
region_gain_line.cc
region_selection.cc
diff --git a/gtk2_ardour/ardour.menus b/gtk2_ardour/ardour.menus
index 23a803161b..09d07494dd 100644
--- a/gtk2_ardour/ardour.menus
+++ b/gtk2_ardour/ardour.menus
@@ -161,6 +161,7 @@
<menuitem action='select-prev-route'/>
</menu>
<menu name='Regions' action='Regions'>
+ <menuitem action='split-region-at-transients'/>
<menuitem action='crop'/>
<menuitem action='duplicate-region'/>
<menuitem action='multi-duplicate-region'/>
diff --git a/gtk2_ardour/au_pluginui.h b/gtk2_ardour/au_pluginui.h
index 46e4cde12f..5bec967091 100644
--- a/gtk2_ardour/au_pluginui.h
+++ b/gtk2_ardour/au_pluginui.h
@@ -3,6 +3,7 @@
#include <AppKit/AppKit.h>
#include <Carbon/Carbon.h>
+#include <AudioUnit/AudioUnitCarbonView.h>
#include <AudioUnit/AudioUnit.h>
/* fix up stupid apple macros */
diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc
index 9e3cc60466..cb43a987b5 100644
--- a/gtk2_ardour/editor.cc
+++ b/gtk2_ardour/editor.cc
@@ -79,6 +79,7 @@
#include "actions.h"
#include "gui_thread.h"
#include "sfdb_ui.h"
+#include "rhythm_ferret.h"
#ifdef FFT_ANALYSIS
#include "analysis_window.h"
@@ -328,6 +329,7 @@ Editor::Editor ()
_dragging_hscrollbar = false;
select_new_marker = false;
zoomed_to_region = false;
+ rhythm_ferret = 0;
scrubbing_direction = 0;
@@ -1161,6 +1163,10 @@ Editor::connect_to_session (Session *t)
_playlist_selector->set_session (session);
nudge_clock.set_session (session);
+ if (rhythm_ferret) {
+ rhythm_ferret->set_session (session);
+ }
+
#ifdef FFT_ANALYSIS
if (analysis_window != 0)
analysis_window->set_session (session);
@@ -4359,3 +4365,15 @@ Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<R
}
}
}
+
+void
+Editor::show_rhythm_ferret ()
+{
+ if (rhythm_ferret == 0) {
+ rhythm_ferret = new RhythmFerret(*this);
+ }
+
+ rhythm_ferret->set_session (session);
+ rhythm_ferret->show ();
+ rhythm_ferret->present ();
+}
diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h
index 9e155dc5b9..5ce99636db 100644
--- a/gtk2_ardour/editor.h
+++ b/gtk2_ardour/editor.h
@@ -105,6 +105,7 @@ class StreamView;
class AudioStreamView;
class ControlPoint;
class SoundFileOmega;
+class RhythmFerret;
#ifdef FFT_ANALYSIS
class AnalysisWindow;
#endif
@@ -361,6 +362,8 @@ class Editor : public PublicEditor
void toggle_meter_updating();
+ void show_rhythm_ferret();
+
protected:
void map_transport_state ();
void map_position_change (nframes_t);
@@ -981,6 +984,7 @@ class Editor : public PublicEditor
void normalize_region ();
void denormalize_region ();
void adjust_region_scale_amplitude (bool up);
+ void split_region_at_transients ();
void use_region_as_bar ();
void use_range_as_bar ();
@@ -2061,6 +2065,8 @@ class Editor : public PublicEditor
void select_next_route ();
void select_prev_route ();
+
+ RhythmFerret* rhythm_ferret;
};
#endif /* __ardour_editor_h__ */
diff --git a/gtk2_ardour/editor_actions.cc b/gtk2_ardour/editor_actions.cc
index 4bb0d74aeb..ae1b93e919 100644
--- a/gtk2_ardour/editor_actions.cc
+++ b/gtk2_ardour/editor_actions.cc
@@ -364,6 +364,9 @@ Editor::register_actions ()
act = ActionManager::register_action (editor_actions, "set-tempo-from-edit-range", _("Set Tempo from Edit Range=Bar"), mem_fun(*this, &Editor::use_range_as_bar));
ActionManager::session_sensitive_actions.push_back (act);
+ act = ActionManager::register_action (editor_actions, "split-region-at-transients", _("Other Temporary Label"), mem_fun(*this, &Editor::split_region_at_transients));
+ ActionManager::session_sensitive_actions.push_back (act);
+
act = ActionManager::register_action (editor_actions, "crop", _("Crop"), mem_fun(*this, &Editor::crop_region_to_selection));
ActionManager::session_sensitive_actions.push_back (act);
act = ActionManager::register_action (editor_actions, "insert-chunk", _("Insert Chunk"), bind (mem_fun(*this, &Editor::paste_named_selection), 1.0f));
diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc
index f960f27d0f..0e725aeda2 100644
--- a/gtk2_ardour/editor_ops.cc
+++ b/gtk2_ardour/editor_ops.cc
@@ -43,10 +43,12 @@
#include <ardour/location.h>
#include <ardour/named_selection.h>
#include <ardour/audio_track.h>
+#include <ardour/audiofilesource.h>
#include <ardour/audioplaylist.h>
#include <ardour/region_factory.h>
#include <ardour/playlist_factory.h>
#include <ardour/reverse.h>
+#include <ardour/transient_detector.h>
#include <ardour/dB.h>
#include "ardour_ui.h"
@@ -5013,3 +5015,83 @@ Editor::define_one_bar (nframes64_t start, nframes64_t end)
session->add_command (new MementoCommand<TempoMap>(session->tempo_map(), &before, &after));
commit_reversible_command ();
}
+
+void
+Editor::split_region_at_transients ()
+{
+ list<nframes64_t> transients;
+
+ if (!session) {
+ return;
+ }
+
+ ExclusiveRegionSelection esr (*this, entered_regionview);
+
+ if (selection->regions.empty()) {
+ return;
+ }
+
+ show_rhythm_ferret ();
+ return;
+#if 0
+
+ cerr << "selection size is " << selection->regions.size() << endl;
+
+ for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ) {
+
+ RegionSelection::iterator tmp;
+
+ tmp = i;
+ ++tmp;
+
+ cerr << "working on " << (*i)->get_item_name() << endl;
+
+ boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
+
+ if (!ar) {
+ continue;
+ }
+
+ boost::shared_ptr<Playlist> pl = ar->playlist();
+
+ if (!pl) {
+ continue;
+ }
+
+ cerr << "getting transients\n";
+
+ ar->get_transients (transients);
+ nframes64_t start = ar->start();
+ nframes64_t pos = ar->position();
+
+ pl->freeze ();
+ pl->remove_region (ar);
+
+ cerr << "creating new regions from " << transients.size() << " transients\n";
+
+ for (list<nframes64_t>::iterator x = transients.begin(); x != transients.end(); ++x) {
+
+ nframes_t len = (*x) - start;
+
+ string new_name;
+
+ if (session->region_name (new_name, ar->name())) {
+ continue;
+ }
+
+ pl->add_region (RegionFactory::create (ar->get_sources(), start, len, new_name), pos);
+
+ start = (*x);
+ pos += len;
+ }
+
+ pl->thaw ();
+
+ transients.clear ();
+
+ cerr << "done with that one\n";
+
+ i = tmp;
+ }
+#endif
+}
diff --git a/gtk2_ardour/rhythm_ferret.cc b/gtk2_ardour/rhythm_ferret.cc
new file mode 100644
index 0000000000..a52980167f
--- /dev/null
+++ b/gtk2_ardour/rhythm_ferret.cc
@@ -0,0 +1,376 @@
+#include <gtkmm/stock.h>
+#include <gtkmm2ext/utils.h>
+
+#include <pbd/memento_command.h>
+
+#include <ardour/transient_detector.h>
+#include <ardour/audiosource.h>
+#include <ardour/audioregion.h>
+#include <ardour/playlist.h>
+#include <ardour/region_factory.h>
+#include <ardour/session.h>
+
+#include "rhythm_ferret.h"
+#include "audio_region_view.h"
+#include "public_editor.h"
+
+#include "i18n.h"
+
+using namespace std;
+using namespace Gtk;
+using namespace Gdk;
+using namespace PBD;
+using namespace ARDOUR;
+
+/* order of these must match the AnalysisMode enums
+ in rhythm_ferret.h
+*/
+static const gchar * _analysis_mode_strings[] = {
+ N_("Percussive Onset"),
+ N_("Note Onset"),
+ 0
+};
+
+RhythmFerret::RhythmFerret (PublicEditor& e)
+ : ArdourDialog (_("Rhythm Ferret"))
+ , editor (e)
+ , operation_frame (_("Operation"))
+ , selection_frame (_("Selection"))
+ , ferret_frame (_("Analysis"))
+ , logo (0)
+ , region_split_button (operation_button_group, _("Split Region"))
+ , tempo_button (operation_button_group, _("Set Tempo Map"))
+ , region_conform_button (operation_button_group, _("Conform Region"))
+ , analysis_mode_label (_("Mode"))
+ , detection_threshold_adjustment (3, 0, 20, 1, 4)
+ , detection_threshold_scale (detection_threshold_adjustment)
+ , detection_threshold_label (_("Threshold"))
+ , sensitivity_adjustment (40, 0, 100, 1, 10)
+ , sensitivity_scale (sensitivity_adjustment)
+ , sensitivity_label (_("Sensitivity"))
+ , analyze_button (_("Analyze"))
+ , trigger_gap_adjustment (3, 0, 100, 1, 10)
+ , trigger_gap_spinner (trigger_gap_adjustment)
+ , trigger_gap_label (_("Trigger gap (msecs)"))
+ , action_button (Stock::APPLY)
+
+{
+ upper_hpacker.set_spacing (6);
+
+ upper_hpacker.pack_start (operation_frame, true, true);
+ upper_hpacker.pack_start (selection_frame, true, true);
+ upper_hpacker.pack_start (ferret_frame, true, true);
+
+ op_packer.pack_start (region_split_button, false, false);
+ op_packer.pack_start (tempo_button, false, false);
+ op_packer.pack_start (region_conform_button, false, false);
+
+ operation_frame.add (op_packer);
+
+ HBox* box;
+
+ ferret_packer.set_spacing (6);
+ ferret_packer.set_border_width (6);
+
+ vector<string> strings;
+
+ 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());
+
+ box = manage (new HBox);
+ box->set_spacing (6);
+ box->pack_start (analysis_mode_label, false, false);
+ box->pack_start (analysis_mode_selector, true, true);
+ ferret_packer.pack_start (*box, 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);
+ ferret_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);
+
+ 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);
+
+ ferret_packer.pack_start (analyze_button, false, false);
+
+ analyze_button.signal_clicked().connect (mem_fun (*this, &RhythmFerret::run_analysis));
+
+ ferret_frame.add (ferret_packer);
+
+ // Glib::RefPtr<Pixbuf> logo_pixbuf ("somefile");
+
+ if (logo) {
+ lower_hpacker.pack_start (*logo, false, false);
+ }
+
+ lower_hpacker.pack_start (operation_clarification_label, false, false);
+ lower_hpacker.pack_start (action_button, false, false);
+
+ action_button.signal_clicked().connect (mem_fun (*this, &RhythmFerret::do_action));
+
+ get_vbox()->set_border_width (6);
+ get_vbox()->set_spacing (6);
+ get_vbox()->pack_start (upper_hpacker, true, true);
+ get_vbox()->pack_start (lower_hpacker, false, false);
+
+ show_all ();
+}
+
+RhythmFerret::~RhythmFerret()
+{
+ if (logo) {
+ delete logo;
+ }
+}
+
+RhythmFerret::AnalysisMode
+RhythmFerret::get_analysis_mode () const
+{
+ string str = analysis_mode_selector.get_active_text ();
+
+ if (str == _(_analysis_mode_strings[(int) NoteOnset])) {
+ return NoteOnset;
+ }
+
+ return PercussionOnset;
+}
+
+RhythmFerret::Action
+RhythmFerret::get_action () const
+{
+ if (tempo_button.get_active()) {
+ return DefineTempoMap;
+ } else if (region_conform_button.get_active()) {
+ return ConformRegion;
+ }
+
+ return SplitRegion;
+}
+
+void
+RhythmFerret::run_analysis ()
+{
+ if (!session) {
+ return;
+ }
+
+ RegionSelection& regions (editor.get_selection().regions);
+
+ current_results.clear ();
+
+ if (regions.empty()) {
+ return;
+ }
+
+ for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
+
+ boost::shared_ptr<Readable> rd = boost::static_pointer_cast<AudioRegion> ((*i)->region());
+
+ switch (get_analysis_mode()) {
+ case PercussionOnset:
+ run_percussion_onset_analysis (rd, (*i)->region()->position(), current_results);
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
+ (*i)->get_time_axis_view().show_temporary_lines (current_results);
+ }
+
+}
+
+int
+RhythmFerret::run_percussion_onset_analysis (boost::shared_ptr<Readable> readable, nframes64_t offset, vector<nframes64_t>& results)
+{
+ TransientDetector t (session->frame_rate());
+ bool existing_results = !results.empty();
+
+ for (uint32_t i = 0; i < readable->n_channels(); ++i) {
+
+ vector<nframes64_t> these_results;
+
+ t.reset ();
+ t.set_threshold (detection_threshold_adjustment.get_value());
+ t.set_sensitivity (sensitivity_adjustment.get_value());
+
+ if (t.run ("", readable, i, these_results)) {
+ continue;
+ }
+
+ /* translate all transients to give absolute position */
+
+ for (vector<nframes64_t>::iterator i = these_results.begin(); i != these_results.end(); ++i) {
+ (*i) += offset;
+ }
+
+ /* merge */
+
+ results.insert (results.end(), these_results.begin(), these_results.end());
+ }
+
+ if (!results.empty() && (existing_results || readable->n_channels() > 1)) {
+
+ /* now resort to bring transients from different channels together */
+
+ sort (results.begin(), results.end());
+
+ /* remove duplicates or other things that are too close */
+
+ vector<nframes64_t>::iterator i = results.begin();
+ nframes64_t curr = (*i);
+ nframes64_t gap_frames = (nframes64_t) floor (trigger_gap_adjustment.get_value() * (session->frame_rate() / 1000.0));
+
+ ++i;
+
+ while (i != results.end()) {
+ if (((*i) == curr) || (((*i) - curr) < gap_frames)) {
+ i = results.erase (i);
+ } else {
+ ++i;
+ curr = *i;
+ }
+ }
+
+ }
+
+ return 0;
+}
+
+void
+RhythmFerret::do_action ()
+{
+ if (!session || current_results.empty()) {
+ return;
+ }
+
+ switch (get_action()) {
+ case SplitRegion:
+ do_split_action ();
+ break;
+
+ default:
+ break;
+ }
+}
+
+void
+RhythmFerret::do_split_action ()
+{
+ /* this can/will change the current selection, so work with a copy */
+
+ RegionSelection& regions (editor.get_selection().regions);
+
+ if (regions.empty()) {
+ return;
+ }
+
+ session->begin_reversible_command (_("split regions (rhythm ferret)"));
+
+ for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ) {
+
+ RegionSelection::iterator tmp;
+
+ tmp = i;
+ ++tmp;
+
+ (*i)->get_time_axis_view().hide_temporary_lines ();
+
+ do_region_split ((*i), current_results);
+
+ /* i is invalid at this point */
+
+ i = tmp;
+ }
+
+ session->commit_reversible_command ();
+}
+
+void
+RhythmFerret::do_region_split (RegionView* rv, const vector<nframes64_t>& positions)
+{
+ boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (rv->region());
+
+ if (!ar) {
+ return;
+ }
+
+ boost::shared_ptr<Playlist> pl = ar->playlist();
+
+ if (!pl) {
+ return;
+ }
+
+ vector<nframes64_t>::const_iterator x;
+
+ nframes64_t pos = ar->position();
+
+ XMLNode& before (pl->get_state());
+
+ x = positions.begin();
+
+ while (x != positions.end()) {
+ if ((*x) > pos) {
+ break;
+ }
+ }
+
+ if (x == positions.end()) {
+ return;
+ }
+
+ pl->freeze ();
+ pl->remove_region (ar);
+
+ do {
+
+ /* file start = original start + how far we from the initial position ?
+ */
+
+ nframes64_t file_start = ar->start() + (pos - ar->position());
+
+ /* length = next position - current position
+ */
+
+ nframes64_t len = (*x) - pos;
+
+ string new_name;
+
+ if (session->region_name (new_name, ar->name())) {
+ continue;
+ }
+
+ pl->add_region (RegionFactory::create (ar->get_sources(), file_start, len, new_name), pos);
+
+ pos += len;
+
+ ++x;
+
+ } while (x != positions.end() && (*x) < ar->last_frame());
+
+ pl->thaw ();
+
+ XMLNode& after (pl->get_state());
+
+ session->add_command (new MementoCommand<Playlist>(*pl, &before, &after));
+}
+
+void
+RhythmFerret::set_session (Session* s)
+{
+ ArdourDialog::set_session (s);
+ current_results.clear ();
+}
diff --git a/gtk2_ardour/rhythm_ferret.h b/gtk2_ardour/rhythm_ferret.h
new file mode 100644
index 0000000000..36d4450939
--- /dev/null
+++ b/gtk2_ardour/rhythm_ferret.h
@@ -0,0 +1,100 @@
+#ifndef __gtk2_ardour_rhythm_ferret_h__
+#define __gtk2_ardour_rhythm_ferret_h__
+
+#include <gtkmm/box.h>
+#include <gtkmm/scale.h>
+#include <gtkmm/spinbutton.h>
+#include <gtkmm/radiobutton.h>
+#include <gtkmm/radiobuttongroup.h>
+#include <gtkmm/frame.h>
+#include <gtkmm/image.h>
+#include <gtkmm/comboboxtext.h>
+#include <gtkmm/button.h>
+#include <gtkmm/label.h>
+
+#include "ardour_dialog.h"
+
+namespace ARDOUR {
+ class Readable;
+}
+
+class PublicEditor;
+class RegionView;
+
+class RhythmFerret : public ArdourDialog {
+ public:
+ /* order of these enums must match the _analyse_mode_strings
+ in rhythm_ferret.cc
+ */
+ enum AnalysisMode {
+ PercussionOnset,
+ NoteOnset
+ };
+
+ enum Action {
+ SplitRegion,
+ DefineTempoMap,
+ ConformRegion
+ };
+
+ RhythmFerret (PublicEditor&);
+ ~RhythmFerret ();
+
+ void set_session (ARDOUR::Session*);
+
+ private:
+ PublicEditor& editor;
+
+ Gtk::HBox upper_hpacker;
+ Gtk::HBox lower_hpacker;
+
+ Gtk::Frame operation_frame;
+ Gtk::Frame selection_frame;
+ Gtk::Frame ferret_frame;
+
+ Gtk::VBox op_logo_packer;
+ Gtk::Image* logo;
+
+ /* operation frame */
+
+ Gtk::VBox op_packer;
+ Gtk::RadioButtonGroup operation_button_group;
+ Gtk::RadioButton region_split_button;
+ Gtk::RadioButton tempo_button;
+ Gtk::RadioButton region_conform_button;
+
+ /* analysis frame */
+
+ Gtk::VBox ferret_packer;
+ Gtk::ComboBoxText analysis_mode_selector;
+ Gtk::Label analysis_mode_label;
+ Gtk::Adjustment detection_threshold_adjustment;
+ Gtk::HScale detection_threshold_scale;
+ Gtk::Label detection_threshold_label;
+ Gtk::Adjustment sensitivity_adjustment;
+ Gtk::HScale sensitivity_scale;
+ Gtk::Label sensitivity_label;
+ Gtk::Button analyze_button;
+ Gtk::Adjustment trigger_gap_adjustment;
+ Gtk::SpinButton trigger_gap_spinner;
+ Gtk::Label trigger_gap_label;
+
+ Gtk::Label operation_clarification_label;
+ Gtk::Button action_button;
+
+ std::vector<std::string> analysis_mode_strings;
+
+ std::vector<nframes64_t> current_results;
+
+ AnalysisMode get_analysis_mode () const;
+ Action get_action() const;
+
+ void run_analysis ();
+ int run_percussion_onset_analysis (boost::shared_ptr<ARDOUR::Readable> region, nframes64_t offset, std::vector<nframes64_t>& results);
+
+ void do_action ();
+ void do_split_action ();
+ void do_region_split (RegionView* rv, const std::vector<nframes64_t>&);
+};
+
+#endif /* __gtk2_ardour_rhythm_ferret_h__ */
diff --git a/gtk2_ardour/time_axis_view.cc b/gtk2_ardour/time_axis_view.cc
index c4f5a558ab..c6dc773dd8 100644
--- a/gtk2_ardour/time_axis_view.cc
+++ b/gtk2_ardour/time_axis_view.cc
@@ -43,6 +43,7 @@
#include "public_editor.h"
#include "time_axis_view.h"
#include "simplerect.h"
+#include "simpleline.h"
#include "selection.h"
#include "keyboard.h"
#include "rgb_macros.h"
@@ -1099,3 +1100,37 @@ TimeAxisView::covers_y_position (double y)
return 0;
}
+
+void
+TimeAxisView::show_temporary_lines (const vector<nframes64_t>& pos)
+{
+ while (temp_lines.size()< pos.size()) {
+ ArdourCanvas::SimpleLine* l = new ArdourCanvas::SimpleLine (*canvas_display);
+ l->property_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();
+ l->property_y1() = 0;
+ l->property_y2() = height;
+ temp_lines.push_back (l);
+ }
+
+ while (temp_lines.size() > pos.size()) {
+ ArdourCanvas::SimpleLine *line = temp_lines.back();
+ temp_lines.pop_back ();
+ delete line;
+ }
+
+ vector<nframes64_t>::const_iterator i;
+ list<ArdourCanvas::SimpleLine*>::iterator l;
+
+ for (i = pos.begin(), l = temp_lines.begin(); i != pos.end() && l != temp_lines.end(); ++i, ++l) {
+ (*l)->property_x1() = editor.frame_to_pixel ((double) *i);
+ (*l)->property_x2() = editor.frame_to_pixel ((double) *i);
+ }
+}
+
+void
+TimeAxisView::hide_temporary_lines ()
+{
+ for (list<ArdourCanvas::SimpleLine*>::iterator l = temp_lines.begin(); l != temp_lines.end(); ++l) {
+ (*l)->hide ();
+ }
+}
diff --git a/gtk2_ardour/time_axis_view.h b/gtk2_ardour/time_axis_view.h
index 2c45325679..c98a72c626 100644
--- a/gtk2_ardour/time_axis_view.h
+++ b/gtk2_ardour/time_axis_view.h
@@ -176,6 +176,9 @@ class TimeAxisView : public virtual AxisView
virtual ARDOUR::RouteGroup* edit_group() const { return 0; }
virtual boost::shared_ptr<ARDOUR::Playlist> playlist() const { return boost::shared_ptr<ARDOUR::Playlist> (); }
+ virtual void show_temporary_lines (const std::vector<nframes64_t>&);
+ virtual void hide_temporary_lines ();
+
virtual void set_samples_per_unit (double);
virtual void show_selection (TimeSelection&);
virtual void hide_selection ();
@@ -322,6 +325,8 @@ class TimeAxisView : public virtual AxisView
void set_height_pixels (uint32_t h);
void color_handler ();
+ list<ArdourCanvas::SimpleLine*> temp_lines;
+
}; /* class TimeAxisView */
#endif /* __ardour_gtk_time_axis_h__ */
diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript
index 733feb2c00..dada73ff43 100644
--- a/libs/ardour/SConscript
+++ b/libs/ardour/SConscript
@@ -93,6 +93,7 @@ sndfilesource.cc
source.cc
source_factory.cc
tempo.cc
+transient_detector.cc
utils.cc
version.cc
mix.cc
@@ -267,11 +268,15 @@ ardour.Merge ([
libraries['pbd'],
libraries['midi++2'],
libraries['glib2'],
- libraries['glibmm2']
+ libraries['glibmm2'],
+ libraries['vamp'],
+ libraries['vamphost'],
+ libraries['fftw3f'],
+ libraries['fftw3'],
])
if ardour['RUBBERBAND']:
- ardour.Merge ([ libraries['rubberband'], libraries['vamp'], libraries['fftw3f'] ])
+ ardour.Merge ([ libraries['rubberband']])
timefx_sources += [ 'rb_effect.cc' ]
else:
ardour.Merge ([ libraries['soundtouch'] ])
diff --git a/libs/ardour/ardour/audioanalyser.h b/libs/ardour/ardour/audioanalyser.h
new file mode 100644
index 0000000000..aac2600ba6
--- /dev/null
+++ b/libs/ardour/ardour/audioanalyser.h
@@ -0,0 +1,74 @@
+/*
+ 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_audioanalyser_h__
+#define __ardour_audioanalyser_h__
+
+#include <vector>
+#include <string>
+#include <ostream>
+#include <fstream>
+#include <vamp-sdk/Plugin.h>
+#include <ardour/audioregion.h>
+
+namespace ARDOUR {
+
+class Readable;
+class Session;
+
+class AudioAnalyser {
+
+ public:
+ typedef Vamp::Plugin AnalysisPlugin;
+ typedef std::string AnalysisPluginKey;
+
+ AudioAnalyser (float sample_rate, AnalysisPluginKey key);
+ virtual ~AudioAnalyser();
+
+ /* analysis object should provide a run method
+ that accepts a path to write the results to (optionally empty)
+ a boost::shared_ptr<Readable> to read data from
+ and a reference to a type-specific container to return the
+ results.
+ */
+
+ void reset ();
+
+ protected:
+ float sample_rate;
+ AnalysisPlugin* plugin;
+ AnalysisPluginKey plugin_key;
+
+ nframes64_t bufsize;
+ nframes64_t stepsize;
+
+ int initialize_plugin (AnalysisPluginKey name, float sample_rate);
+ int analyse (const std::string& path, boost::shared_ptr<Readable>, uint32_t channel);
+
+ /* instances of an analysis object will have this method called
+ whenever there are results to process. if out is non-null,
+ the data should be written to the stream it points to.
+ */
+
+ virtual int use_features (Vamp::Plugin::FeatureSet&, std::ostream*) = 0;
+};
+
+} /* namespace */
+
+#endif /* __ardour_audioanalyser_h__ */
diff --git a/libs/ardour/ardour/audioregion.h b/libs/ardour/ardour/audioregion.h
index 934b396a8e..e35b041a08 100644
--- a/libs/ardour/ardour/audioregion.h
+++ b/libs/ardour/ardour/audioregion.h
@@ -21,6 +21,7 @@
#define __ardour_audio_region_h__
#include <vector>
+#include <list>
#include <pbd/fastlog.h>
#include <pbd/undo.h>
@@ -59,6 +60,7 @@ class AudioRegion : public Region
bool speed_mismatch (float) const;
boost::shared_ptr<AudioSource> source (uint32_t n=0) const { if (n < sources.size()) return sources[n]; else return sources[0]; }
+ const SourceList& get_sources() const { return sources; }
void set_scale_amplitude (gain_t);
gain_t scale_amplitude() const { return _scale_amplitude; }
@@ -82,11 +84,16 @@ class AudioRegion : public Region
nframes_t offset, nframes_t cnt,
uint32_t chan_n=0, double samples_per_unit= 1.0) const;
+ /* Readable interface */
+
+ virtual nframes64_t read (Sample*, nframes64_t pos, nframes64_t cnt, int channel) const;
+ virtual nframes64_t readable_length() const { return length(); }
+
virtual nframes_t read_at (Sample *buf, Sample *mixdown_buf,
- float *gain_buf, nframes_t position, nframes_t cnt,
- uint32_t chan_n = 0,
- nframes_t read_frames = 0,
- nframes_t skip_frames = 0) const;
+ float *gain_buf, nframes_t position, nframes_t cnt,
+ uint32_t chan_n = 0,
+ nframes_t read_frames = 0,
+ nframes_t skip_frames = 0) const;
nframes_t master_read_at (Sample *buf, Sample *mixdown_buf,
float *gain_buf,
@@ -146,7 +153,7 @@ class AudioRegion : public Region
AudioRegion (boost::shared_ptr<AudioSource>, nframes_t start, nframes_t length);
AudioRegion (boost::shared_ptr<AudioSource>, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags);
- AudioRegion (SourceList &, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags);
+ AudioRegion (const SourceList &, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags);
AudioRegion (boost::shared_ptr<const AudioRegion>, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags);
AudioRegion (boost::shared_ptr<const AudioRegion>);
AudioRegion (boost::shared_ptr<AudioSource>, const XMLNode&);
@@ -161,10 +168,11 @@ class AudioRegion : public Region
void recompute_gain_at_start ();
nframes_t _read_at (const SourceList&, Sample *buf, Sample *mixdown_buffer,
- float *gain_buffer, nframes_t position, nframes_t cnt,
- uint32_t chan_n = 0,
- nframes_t read_frames = 0,
- nframes_t skip_frames = 0) const;
+ float *gain_buffer, nframes_t position, nframes_t cnt,
+ uint32_t chan_n = 0,
+ nframes_t read_frames = 0,
+ nframes_t skip_frames = 0,
+ bool raw = false) const;
bool verify_start (nframes_t position);
bool verify_length (nframes_t& length);
diff --git a/libs/ardour/ardour/audiosource.h b/libs/ardour/ardour/audiosource.h
index b7097154aa..e8ef3a0fa2 100644
--- a/libs/ardour/ardour/audiosource.h
+++ b/libs/ardour/ardour/audiosource.h
@@ -57,7 +57,8 @@ class AudioSource : public Source, public boost::enable_shared_from_this<ARDOUR:
virtual nframes_t natural_position() const { return 0; }
- /* returns the number of items in this `audio_source' */
+ nframes64_t readable_length() const { return _length; }
+ uint32_t n_channels() const { return 1; }
virtual nframes_t length() const {
return _length;
@@ -65,6 +66,15 @@ class AudioSource : public Source, public boost::enable_shared_from_this<ARDOUR:
virtual nframes_t available_peaks (double zoom) const;
+ /* stopgap until nframes_t becomes nframes64_t. this function is needed by the Readable interface */
+
+ virtual nframes64_t read (Sample *dst, nframes64_t start, nframes64_t cnt, int channel) const {
+ /* XXX currently ignores channel, assuming that source is always mono, which
+ historically has been true.
+ */
+ return read (dst, (nframes_t) start, (nframes_t) cnt);
+ }
+
virtual nframes_t read (Sample *dst, nframes_t start, nframes_t cnt) const;
virtual nframes_t write (Sample *src, nframes_t cnt);
@@ -112,6 +122,9 @@ class AudioSource : public Source, public boost::enable_shared_from_this<ARDOUR:
int prepare_for_peakfile_writes ();
void done_with_peakfile_writes (bool done = true);
+ std::vector<nframes64_t> transients;
+ std::string get_transients_path() const;
+
protected:
static bool _build_missing_peakfiles;
static bool _build_peakfiles;
@@ -146,6 +159,8 @@ class AudioSource : public Source, public boost::enable_shared_from_this<ARDOUR:
int compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframes_t cnt, bool force,
bool intermediate_peaks_ready_signal, nframes_t frames_per_peak);
+ int load_transients (const std::string&);
+
private:
int peakfile;
nframes_t peak_leftover_cnt;
diff --git a/libs/ardour/ardour/plugin_manager.h b/libs/ardour/ardour/plugin_manager.h
index dd50a079e3..b7e9fc23d8 100644
--- a/libs/ardour/ardour/plugin_manager.h
+++ b/libs/ardour/ardour/plugin_manager.h
@@ -40,6 +40,8 @@ class PluginManager {
PluginManager ();
~PluginManager ();
+ /* realtime plugin APIs */
+
ARDOUR::PluginInfoList &vst_plugin_info () { return _vst_plugin_info; }
ARDOUR::PluginInfoList &ladspa_plugin_info () { return _ladspa_plugin_info; }
ARDOUR::PluginInfoList &lv2_plugin_info () { return _lv2_plugin_info; }
diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h
index 3aae8c4767..e75fb43acc 100644
--- a/libs/ardour/ardour/region.h
+++ b/libs/ardour/ardour/region.h
@@ -27,6 +27,7 @@
#include <pbd/statefuldestructible.h>
#include <ardour/ardour.h>
+#include <ardour/readable.h>
class XMLNode;
@@ -40,7 +41,7 @@ enum RegionEditState {
EditChangesID = 2
};
-class Region : public PBD::StatefulDestructible, public boost::enable_shared_from_this<Region>
+class Region : public PBD::StatefulDestructible, public Readable, public boost::enable_shared_from_this<Region>
{
public:
enum Flag {
diff --git a/libs/ardour/ardour/region_factory.h b/libs/ardour/ardour/region_factory.h
index 197983a29d..b944202c36 100644
--- a/libs/ardour/ardour/region_factory.h
+++ b/libs/ardour/ardour/region_factory.h
@@ -49,7 +49,7 @@ class RegionFactory {
nframes_t length, std::string name,
layer_t = 0, Region::Flag flags = Region::DefaultFlags, bool announce = true);
static boost::shared_ptr<Region> create (boost::shared_ptr<Source>, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags, bool announce = true);
- static boost::shared_ptr<Region> create (SourceList &, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags, bool announce = true);
+ static boost::shared_ptr<Region> create (const SourceList &, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags, bool announce = true);
static boost::shared_ptr<Region> create (boost::shared_ptr<Region>);
static boost::shared_ptr<Region> create (Session&, XMLNode&, bool);
static boost::shared_ptr<Region> create (SourceList &, const XMLNode&);
diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h
index 20457069d9..acb9f5f53f 100644
--- a/libs/ardour/ardour/session.h
+++ b/libs/ardour/ardour/session.h
@@ -252,6 +252,9 @@ class Session : public PBD::StatefulDestructible
std::string peak_dir () const;
std::string dead_sound_dir () const;
std::string automation_dir () const;
+ std::string analysis_dir() const;
+
+ int ensure_subdirs ();
Glib::ustring peak_path (Glib::ustring) const;
diff --git a/libs/ardour/ardour/source.h b/libs/ardour/ardour/source.h
index 7ffa616a40..6e6561f22f 100644
--- a/libs/ardour/ardour/source.h
+++ b/libs/ardour/ardour/source.h
@@ -28,13 +28,14 @@
#include <pbd/statefuldestructible.h>
#include <ardour/ardour.h>
+#include <ardour/readable.h>
namespace ARDOUR {
class Session;
class Playlist;
-class Source : public PBD::StatefulDestructible
+class Source : public PBD::StatefulDestructible, public ARDOUR::Readable
{
public:
Source (Session&, std::string name);
diff --git a/libs/ardour/ardour/transient_detector.h b/libs/ardour/ardour/transient_detector.h
new file mode 100644
index 0000000000..45172d8f54
--- /dev/null
+++ b/libs/ardour/ardour/transient_detector.h
@@ -0,0 +1,52 @@
+/*
+ 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_transient_detector_h__
+#define __ardour_transient_detector_h__
+
+#include <ardour/audioanalyser.h>
+
+namespace ARDOUR {
+
+class AudioSource;
+class Session;
+
+class TransientDetector : public AudioAnalyser
+{
+
+ public:
+ TransientDetector (float sample_rate);
+ ~TransientDetector();
+
+ void set_threshold (float);
+ void set_sensitivity (float);
+
+ float get_threshold () const;
+ float get_sensitivity () const;
+
+ int run (const std::string& path, boost::shared_ptr<Readable>, uint32_t channel, std::vector<nframes64_t>& results);
+
+ protected:
+ std::vector<nframes64_t>* current_results;
+ int use_features (Vamp::Plugin::FeatureSet&, std::ostream*);
+};
+
+} /* namespace */
+
+#endif /* __ardour_audioanalyser_h__ */
diff --git a/libs/ardour/audioanalyser.cc b/libs/ardour/audioanalyser.cc
new file mode 100644
index 0000000000..ea91510f94
--- /dev/null
+++ b/libs/ardour/audioanalyser.cc
@@ -0,0 +1,157 @@
+#include <vamp-sdk/hostext/PluginLoader.h>
+#include <glibmm/miscutils.h>
+#include <glibmm/fileutils.h>
+#include <glib/gstdio.h> // for g_remove()
+
+#include <pbd/error.h>
+
+#include <ardour/audioanalyser.h>
+#include <ardour/readable.h>
+#include <ardour/readable.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace Vamp;
+using namespace PBD;
+using namespace ARDOUR;
+
+AudioAnalyser::AudioAnalyser (float sr, AnalysisPluginKey key)
+ : sample_rate (sr)
+ , plugin (0)
+ , plugin_key (key)
+{
+}
+
+AudioAnalyser::~AudioAnalyser ()
+{
+}
+
+int
+AudioAnalyser::initialize_plugin (AnalysisPluginKey key, float sr)
+{
+ using namespace Vamp::HostExt;
+
+ PluginLoader* loader (PluginLoader::getInstance());
+
+ plugin = loader->loadPlugin (key, sr, PluginLoader::ADAPT_ALL);
+
+ if (!plugin) {
+ return -1;
+ }
+
+ if ((bufsize = plugin->getPreferredBlockSize ()) == 0) {
+ bufsize = 65536;
+ }
+
+ if ((stepsize = plugin->getPreferredStepSize()) == 0) {
+ stepsize = bufsize;
+ }
+
+ if (plugin->getMinChannelCount() > 1) {
+ delete plugin;
+ return -1;
+ }
+
+ if (!plugin->initialise (1, stepsize, bufsize)) {
+ delete plugin;
+ return -1;
+ }
+
+ return 0;
+}
+
+void
+AudioAnalyser::reset ()
+{
+ if (plugin) {
+ plugin->reset ();
+ }
+}
+
+int
+AudioAnalyser::analyse (const string& path, boost::shared_ptr<Readable> src, uint32_t channel)
+{
+ ofstream ofile;
+ Plugin::FeatureSet onsets;
+ int ret = -1;
+ bool done = false;
+ Sample* data = 0;
+ nframes64_t len = src->readable_length();
+ nframes64_t pos = 0;
+ float* bufs[1] = { 0 };
+
+ if (!path.empty()) {
+ ofile.open (path.c_str());
+ if (!ofile) {
+ goto out;
+ }
+ }
+
+ /* create VAMP percussion onset plugin and initialize */
+
+ if (plugin == 0) {
+ if (initialize_plugin (plugin_key, sample_rate)) {
+ goto out;
+ }
+ }
+
+ data = new Sample[bufsize];
+ bufs[0] = data;
+
+ while (!done) {
+
+ nframes64_t to_read;
+
+ /* read from source */
+
+ to_read = min ((len - pos), bufsize);
+
+ if (src->read (data, pos, to_read, channel) != to_read) {
+ cerr << "bad read\n";
+ goto out;
+ }
+
+ /* zero fill buffer if necessary */
+
+ if (to_read != bufsize) {
+ memset (data + to_read, 0, (bufsize - to_read));
+ }
+
+ onsets = plugin->process (bufs, RealTime::fromSeconds ((double) pos / sample_rate));
+
+ if (use_features (onsets, (path.empty() ? &ofile : 0))) {
+ goto out;
+ }
+
+ pos += stepsize;
+
+ if (pos >= len) {
+ done = true;
+ }
+ }
+
+ /* finish up VAMP plugin */
+
+ onsets = plugin->getRemainingFeatures ();
+
+ if (use_features (onsets, (path.empty() ? &ofile : 0))) {
+ goto out;
+ }
+
+ ret = 0;
+
+ out:
+ /* works even if it has not been opened */
+ ofile.close ();
+
+ if (ret) {
+ g_remove (path.c_str());
+ }
+ if (data) {
+ delete data;
+ }
+
+ return ret;
+}
+
diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc
index 61ccc203b0..ebf42255cc 100644
--- a/libs/ardour/audioregion.cc
+++ b/libs/ardour/audioregion.cc
@@ -20,6 +20,7 @@
#include <cmath>
#include <climits>
#include <cfloat>
+#include <algorithm>
#include <set>
@@ -110,15 +111,15 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, n
listen_to_my_curves ();
}
-AudioRegion::AudioRegion (SourceList& srcs, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags)
+AudioRegion::AudioRegion (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags)
: Region (start, length, name, layer, flags),
_fade_in (0.0, 2.0, 1.0, false),
_fade_out (0.0, 2.0, 1.0, false),
_envelope (0.0, 2.0, 1.0, false)
{
/* basic AudioRegion constructor */
-
- for (SourceList::iterator i=srcs.begin(); i != srcs.end(); ++i) {
+
+ for (SourceList::const_iterator i=srcs.begin(); i != srcs.end(); ++i) {
sources.push_back (*i);
master_sources.push_back (*i);
(*i)->GoingAway.connect (mem_fun (*this, &AudioRegion::source_deleted));
@@ -437,12 +438,20 @@ AudioRegion::read_peaks (PeakData *buf, nframes_t npeaks, nframes_t offset, nfra
}
}
+nframes64_t
+AudioRegion::read (Sample* buf, nframes64_t position, nframes64_t cnt, int channel) const
+{
+ /* raw read, no fades, no gain, nada */
+ return _read_at (sources, buf, 0, 0, _position + position, cnt, channel, 0, 0, true);
+}
+
nframes_t
AudioRegion::read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nframes_t position,
nframes_t cnt,
uint32_t chan_n, nframes_t read_frames, nframes_t skip_frames) const
{
- return _read_at (sources, buf, mixdown_buffer, gain_buffer, position, cnt, chan_n, read_frames, skip_frames);
+ /* regular diskstream/butler read complete with fades etc */
+ return _read_at (sources, buf, mixdown_buffer, gain_buffer, position, cnt, chan_n, read_frames, skip_frames, false);
}
nframes_t
@@ -455,13 +464,16 @@ AudioRegion::master_read_at (Sample *buf, Sample *mixdown_buffer, float *gain_bu
nframes_t
AudioRegion::_read_at (const SourceList& srcs, Sample *buf, Sample *mixdown_buffer, float *gain_buffer,
nframes_t position, nframes_t cnt,
- uint32_t chan_n, nframes_t read_frames, nframes_t skip_frames) const
+ uint32_t chan_n,
+ nframes_t read_frames,
+ nframes_t skip_frames,
+ bool raw) const
{
nframes_t internal_offset;
nframes_t buf_offset;
nframes_t to_read;
- if (muted()) {
+ if (muted() && !raw) {
return 0; /* read nothing */
}
@@ -484,14 +496,16 @@ AudioRegion::_read_at (const SourceList& srcs, Sample *buf, Sample *mixdown_buff
return 0; /* read nothing */
}
- if (opaque()) {
+ if (opaque() || raw) {
/* overwrite whatever is there */
mixdown_buffer = buf + buf_offset;
} else {
mixdown_buffer += buf_offset;
}
- _read_data_count = 0;
+ if (!raw) {
+ _read_data_count = 0;
+ }
if (chan_n < n_channels()) {
@@ -500,7 +514,9 @@ AudioRegion::_read_at (const SourceList& srcs, Sample *buf, Sample *mixdown_buff
return 0; /* "read nothing" */
}
- _read_data_count += srcs[chan_n]->read_data_count();
+ if (!raw) {
+ _read_data_count += srcs[chan_n]->read_data_count();
+ }
} else {
@@ -512,37 +528,41 @@ AudioRegion::_read_at (const SourceList& srcs, Sample *buf, Sample *mixdown_buff
/* no fades required */
- goto merge;
+ if (!raw) {
+ goto merge;
+ }
}
/* fade in */
- if (_flags & FadeIn) {
-
- nframes_t fade_in_length = (nframes_t) _fade_in.back()->when;
-
- /* see if this read is within the fade in */
-
- if (internal_offset < fade_in_length) {
-
- nframes_t limit;
-
- limit = min (to_read, fade_in_length - internal_offset);
-
- _fade_in.get_vector (internal_offset, internal_offset+limit, gain_buffer, limit);
-
- for (nframes_t n = 0; n < limit; ++n) {
- mixdown_buffer[n] *= gain_buffer[n];
+ if (!raw) {
+
+ if (_flags & FadeIn) {
+
+ nframes_t fade_in_length = (nframes_t) _fade_in.back()->when;
+
+ /* see if this read is within the fade in */
+
+ if (internal_offset < fade_in_length) {
+
+ nframes_t limit;
+
+ limit = min (to_read, fade_in_length - internal_offset);
+
+ _fade_in.get_vector (internal_offset, internal_offset+limit, gain_buffer, limit);
+
+ for (nframes_t n = 0; n < limit; ++n) {
+ mixdown_buffer[n] *= gain_buffer[n];
+ }
}
}
- }
-
- /* fade out */
-
- if (_flags & FadeOut) {
-
- /* see if some part of this read is within the fade out */
-
+
+ /* fade out */
+
+ if (_flags & FadeOut) {
+
+ /* see if some part of this read is within the fade out */
+
/* ................. >| REGION
_length
@@ -553,65 +573,66 @@ AudioRegion::_read_at (const SourceList& srcs, Sample *buf, Sample *mixdown_buff
|--------------|
^internal_offset
^internal_offset + to_read
-
- we need the intersection of [internal_offset,internal_offset+to_read] with
- [_length - fade_out_length, _length]
-
+
+ we need the intersection of [internal_offset,internal_offset+to_read] with
+ [_length - fade_out_length, _length]
+
*/
- nframes_t fade_out_length = (nframes_t) _fade_out.back()->when;
- nframes_t fade_interval_start = max(internal_offset, _length-fade_out_length);
- nframes_t fade_interval_end = min(internal_offset + to_read, _length);
-
- if (fade_interval_end > fade_interval_start) {
- /* (part of the) the fade out is in this buffer */
+ nframes_t fade_out_length = (nframes_t) _fade_out.back()->when;
+ nframes_t fade_interval_start = max(internal_offset, _length-fade_out_length);
+ nframes_t fade_interval_end = min(internal_offset + to_read, _length);
- nframes_t limit = fade_interval_end - fade_interval_start;
- nframes_t curve_offset = fade_interval_start - (_length-fade_out_length);
- nframes_t fade_offset = fade_interval_start - internal_offset;
-
- _fade_out.get_vector (curve_offset,curve_offset+limit, gain_buffer, limit);
-
- for (nframes_t n = 0, m = fade_offset; n < limit; ++n, ++m) {
- mixdown_buffer[m] *= gain_buffer[n];
- }
- }
-
- }
-
- /* Regular gain curves */
-
- if (envelope_active()) {
- _envelope.get_vector (internal_offset, internal_offset + to_read, gain_buffer, to_read);
+ if (fade_interval_end > fade_interval_start) {
+ /* (part of the) the fade out is in this buffer */
+
+ nframes_t limit = fade_interval_end - fade_interval_start;
+ nframes_t curve_offset = fade_interval_start - (_length-fade_out_length);
+ nframes_t fade_offset = fade_interval_start - internal_offset;
+
+ _fade_out.get_vector (curve_offset,curve_offset+limit, gain_buffer, limit);
+
+ for (nframes_t n = 0, m = fade_offset; n < limit; ++n, ++m) {
+ mixdown_buffer[m] *= gain_buffer[n];
+ }
+ }
+
+ }
- if (_scale_amplitude != 1.0f) {
- for (nframes_t n = 0; n < to_read; ++n) {
- mixdown_buffer[n] *= gain_buffer[n] * _scale_amplitude;
+ /* Regular gain curves */
+
+ if (envelope_active()) {
+ _envelope.get_vector (internal_offset, internal_offset + to_read, gain_buffer, to_read);
+
+ if (_scale_amplitude != 1.0f) {
+ for (nframes_t n = 0; n < to_read; ++n) {
+ mixdown_buffer[n] *= gain_buffer[n] * _scale_amplitude;
+ }
+ } else {
+ for (nframes_t n = 0; n < to_read; ++n) {
+ mixdown_buffer[n] *= gain_buffer[n];
+ }
}
- } else {
+ } else if (_scale_amplitude != 1.0f) {
+ Session::apply_gain_to_buffer (mixdown_buffer, to_read, _scale_amplitude);
+ }
+
+ merge:
+
+ if (!opaque()) {
+
+ /* gack. the things we do for users.
+ */
+
+ buf += buf_offset;
+
for (nframes_t n = 0; n < to_read; ++n) {
- mixdown_buffer[n] *= gain_buffer[n];
+ buf[n] += mixdown_buffer[n];
}
- }
- } else if (_scale_amplitude != 1.0f) {
- Session::apply_gain_to_buffer (mixdown_buffer, to_read, _scale_amplitude);
+ }
}
- merge:
-
- if (!opaque()) {
-
- /* gack. the things we do for users.
- */
-
- buf += buf_offset;
-
- for (nframes_t n = 0; n < to_read; ++n) {
- buf[n] += mixdown_buffer[n];
- }
- }
-
return to_read;
}
diff --git a/libs/ardour/audiosource.cc b/libs/ardour/audiosource.cc
index a71c927f62..68bfff10ee 100644
--- a/libs/ardour/audiosource.cc
+++ b/libs/ardour/audiosource.cc
@@ -27,10 +27,12 @@
#include <ctime>
#include <cmath>
#include <iomanip>
+#include <fstream>
#include <algorithm>
#include <vector>
#include <glibmm/fileutils.h>
+#include <glibmm/miscutils.h>
#include <pbd/xml++.h>
#include <pbd/pthread_utils.h>
@@ -38,6 +40,7 @@
#include <ardour/audiosource.h>
#include <ardour/cycle_timer.h>
#include <ardour/session.h>
+#include <ardour/transient_detector.h>
#include "i18n.h"
@@ -917,3 +920,50 @@ AudioSource::update_length (nframes_t pos, nframes_t cnt)
}
}
+int
+AudioSource::load_transients (const string& path)
+{
+ ifstream file (path.c_str());
+
+ if (!file) {
+ return -1;
+ }
+
+ transients.clear ();
+
+ stringstream strstr;
+ double val;
+
+ while (file.good()) {
+ file >> val;
+
+ if (!file.fail()) {
+ nframes64_t frame = (nframes64_t) floor (val * _session.frame_rate());
+ transients.push_back (frame);
+ }
+ }
+
+ return 0;
+}
+
+string
+AudioSource::get_transients_path () const
+{
+ vector<string> parts;
+ string s;
+
+ /* old sessions may not have the analysis directory */
+
+ _session.ensure_subdirs ();
+
+ s = _session.analysis_dir ();
+ parts.push_back (s);
+
+ s = _id.to_s();
+ s += '.';
+ s += X_("transients");
+ parts.push_back (s);
+
+ return Glib::build_filename (parts);
+}
+
diff --git a/libs/ardour/region_factory.cc b/libs/ardour/region_factory.cc
index 631c27b42f..5cab802801 100644
--- a/libs/ardour/region_factory.cc
+++ b/libs/ardour/region_factory.cc
@@ -90,7 +90,7 @@ RegionFactory::create (Session& session, XMLNode& node, bool yn)
}
boost::shared_ptr<Region>
-RegionFactory::create (SourceList& srcs, nframes_t start, nframes_t length, const string& name, layer_t layer, Region::Flag flags, bool announce)
+RegionFactory::create (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, layer_t layer, Region::Flag flags, bool announce)
{
boost::shared_ptr<AudioRegion> arp (new AudioRegion (srcs, start, length, name, layer, flags));
boost::shared_ptr<Region> ret (boost::static_pointer_cast<Region> (arp));
diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc
index 29995c6676..ad629f5d6e 100644
--- a/libs/ardour/session.cc
+++ b/libs/ardour/session.cc
@@ -34,6 +34,7 @@
#include <glibmm/thread.h>
#include <glibmm/miscutils.h>
+#include <glibmm/fileutils.h>
#include <pbd/error.h>
#include <glibmm/thread.h>
@@ -290,7 +291,8 @@ Session::Session (AudioEngine &eng,
first_stage_init (fullpath, snapshot_name);
- new_session = !g_file_test (_path.c_str(), GFileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
+ new_session = !Glib::file_test (_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
+
if (new_session) {
if (create (new_session, mix_template, compute_initial_length())) {
destroy ();
diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc
index 20f2e06d84..265be63960 100644
--- a/libs/ardour/session_state.cc
+++ b/libs/ardour/session_state.cc
@@ -469,19 +469,14 @@ Session::setup_raid_path (string path)
}
int
-Session::create (bool& new_session, const string& mix_template, nframes_t initial_length)
+Session::ensure_subdirs ()
{
string dir;
- if (g_mkdir_with_parents (_path.c_str(), 0755) < 0) {
- error << string_compose(_("Session: cannot create session dir \"%1\" (%2)"), _path, strerror (errno)) << endmsg;
- return -1;
- }
-
dir = peak_dir ();
if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
- error << string_compose(_("Session: cannot create session peakfile dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
+ error << string_compose(_("Session: cannot create session peakfile folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
return -1;
}
@@ -492,7 +487,7 @@ Session::create (bool& new_session, const string& mix_template, nframes_t initia
dir = sound_dir ();
if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
- error << string_compose(_("Session: cannot create session sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
+ error << string_compose(_("Session: cannot create session sounds folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
return -1;
}
}
@@ -500,17 +495,39 @@ Session::create (bool& new_session, const string& mix_template, nframes_t initia
dir = dead_sound_dir ();
if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
- error << string_compose(_("Session: cannot create session dead sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
+ error << string_compose(_("Session: cannot create session dead sounds folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
return -1;
}
dir = export_dir ();
if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
- error << string_compose(_("Session: cannot create session export dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
+ error << string_compose(_("Session: cannot create session export folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
+ return -1;
+ }
+
+ dir = analysis_dir ();
+
+ if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
+ error << string_compose(_("Session: cannot create session analysis folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+Session::create (bool& new_session, const string& mix_template, nframes_t initial_length)
+{
+
+ if (g_mkdir_with_parents (_path.c_str(), 0755) < 0) {
+ error << string_compose(_("Session: cannot create session folder \"%1\" (%2)"), _path, strerror (errno)) << endmsg;
return -1;
}
+ if (ensure_subdirs ()) {
+ return -1;
+ }
/* check new_session so we don't overwrite an existing one */
@@ -559,7 +576,6 @@ Session::create (bool& new_session, const string& mix_template, nframes_t initia
_state_of_the_state = Clean;
-
save_state ("");
return 0;
@@ -2045,6 +2061,14 @@ Session::automation_dir () const
}
string
+Session::analysis_dir () const
+{
+ string res = _path;
+ res += "analysis/";
+ return res;
+}
+
+string
Session::template_dir ()
{
string path = get_user_ardour_path();
@@ -2808,7 +2832,7 @@ Session::cleanup_sources (Session::cleanup_report& rep)
newpath += dead_sound_dir_name;
if (g_mkdir_with_parents (newpath.c_str(), 0755) < 0) {
- error << string_compose(_("Session: cannot create session peakfile dir \"%1\" (%2)"), newpath, strerror (errno)) << endmsg;
+ error << string_compose(_("Session: cannot create session peakfile folder \"%1\" (%2)"), newpath, strerror (errno)) << endmsg;
return -1;
}
diff --git a/libs/ardour/transient_detector.cc b/libs/ardour/transient_detector.cc
new file mode 100644
index 0000000000..e4692b9c95
--- /dev/null
+++ b/libs/ardour/transient_detector.cc
@@ -0,0 +1,62 @@
+#include <ardour/transient_detector.h>
+
+#include "i18n.h"
+
+using namespace Vamp;
+using namespace ARDOUR;
+using namespace std;
+
+TransientDetector::TransientDetector (float sr)
+ : AudioAnalyser (sr, X_("vamp-example-plugins:percussiononsets"))
+{
+ cerr << "plugin in constructor = " << plugin << endl;
+}
+
+TransientDetector::~TransientDetector()
+{
+}
+
+int
+TransientDetector::run (const std::string& path, boost::shared_ptr<Readable> src, uint32_t channel, vector<nframes64_t>& results)
+{
+ current_results = &results;
+ int ret = analyse (path, src, channel);
+ current_results = 0;
+ return ret;
+}
+
+int
+TransientDetector::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, sample_rate));
+ }
+ }
+
+ return 0;
+}
+
+void
+TransientDetector::set_threshold (float val)
+{
+ if (plugin) {
+ plugin->setParameter ("threshold", val);
+ }
+}
+
+void
+TransientDetector::set_sensitivity (float val)
+{
+ if (plugin) {
+ plugin->setParameter ("sensitivity", val);
+ }
+}