meet rhythm ferret: cute, furry and always on time (ardour build now requires fftw3...
authorPaul Davis <paul@linuxaudiosystems.com>
Fri, 25 Jan 2008 05:35:46 +0000 (05:35 +0000)
committerPaul Davis <paul@linuxaudiosystems.com>
Fri, 25 Jan 2008 05:35:46 +0000 (05:35 +0000)
git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@2959 d708f5d6-7413-0410-9779-e7cbd77b26cf

29 files changed:
SConstruct
gtk2_ardour/SConscript
gtk2_ardour/ardour.menus
gtk2_ardour/au_pluginui.h
gtk2_ardour/editor.cc
gtk2_ardour/editor.h
gtk2_ardour/editor_actions.cc
gtk2_ardour/editor_ops.cc
gtk2_ardour/rhythm_ferret.cc [new file with mode: 0644]
gtk2_ardour/rhythm_ferret.h [new file with mode: 0644]
gtk2_ardour/time_axis_view.cc
gtk2_ardour/time_axis_view.h
libs/ardour/SConscript
libs/ardour/ardour/audioanalyser.h [new file with mode: 0644]
libs/ardour/ardour/audioregion.h
libs/ardour/ardour/audiosource.h
libs/ardour/ardour/plugin_manager.h
libs/ardour/ardour/region.h
libs/ardour/ardour/region_factory.h
libs/ardour/ardour/session.h
libs/ardour/ardour/source.h
libs/ardour/ardour/transient_detector.h [new file with mode: 0644]
libs/ardour/audioanalyser.cc [new file with mode: 0644]
libs/ardour/audioregion.cc
libs/ardour/audiosource.cc
libs/ardour/region_factory.cc
libs/ardour/session.cc
libs/ardour/session_state.cc
libs/ardour/transient_detector.cc [new file with mode: 0644]

index 961fa1afc29f1b9c419de0e32897a0576e0ae2ec..ffa61a0b5a528aa2675e3fef4beac1b791a9cd47 100644 (file)
@@ -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
 
index 3ece6ee91cd0a5e36918200aad4517a9c437aa67..f8d107a8a95bc7ecf788a21406bb54cce76891f1 100644 (file)
@@ -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
index 23a803161b086138ec5a9e22009f6fd8c7279548..09d07494dd927072426534875ed7afdb8394b8e5 100644 (file)
                    <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'/>
index 46e4cde12fa77d6ef5eba9bb0786e9fa4246fa31..5bec967091d91ea0a262b577f6f2f5b9e77ab945 100644 (file)
@@ -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 */
index 9e3cc6046664b8b74139debd2a7515dd43b7ffc8..cb43a987b5fc31a3f0232dd75f8a1dea55a0f1e1 100644 (file)
@@ -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 ();
+}
index 9e155dc5b94df3ceaa61089085cba981b54abe5e..5ce99636dbda0eb3e15c22f2fb639140881acf4d 100644 (file)
@@ -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__ */
index 4bb0d74aeb350074f82750b7553603adebc7c5b6..ae1b93e91962f99cb82e0593e3012ce08728517c 100644 (file)
@@ -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));
index f960f27d0fcec8d6952c987656e0266fb1c151ab..0e725aeda2e4c970b8ecfaf2dcba1c914ce1b009 100644 (file)
 #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 (file)
index 0000000..a529801
--- /dev/null
@@ -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 (file)
index 0000000..36d4450
--- /dev/null
@@ -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__ */
index c4f5a558ab39ec1a15f7a9acd95e26701c01f081..c6dc773dd8e9ec6328c457427aa3730a2e171719 100644 (file)
@@ -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 ();
+       }
+}
index 2c4532567994c5b95f2d70319a1787b94122e2cb..c98a72c6263483cde3532a6e54dc7a3503950e5b 100644 (file)
@@ -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__ */
index 733feb2c007b9825595f818c9c52ef79ab468d0d..dada73ff43c4cf3fa0d06901ab5e8b69c259d55b 100644 (file)
@@ -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 (file)
index 0000000..aac2600
--- /dev/null
@@ -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__ */
index 934b396a8eae58c027e268d315d3271b9f5666f4..e35b041a08221939c57a85d8326f1194ee6e03bc 100644 (file)
@@ -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);
index b7097154aaea5b748d9c217ca68900268cefac7a..e8ef3a0fa2b02d9b62c8805f992ad52897b7c00e 100644 (file)
@@ -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;
index dd50a079e3eab35dfda3676b2b355ce7bbe7017f..b7e9fc23d8de95d0c443f909ebadbcc1c73e8eca 100644 (file)
@@ -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; }
index 3aae8c47677fd826e14caa9b1a2b8612049d2b75..e75fb43acc598c7b0005ecd494231f4bc19090a6 100644 (file)
@@ -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 {
index 197983a29d6caf12efa89be9ae06de624c088582..b944202c36bf5bb438586cf77ccc4030d4f0f2c8 100644 (file)
@@ -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&);
index 20457069d92d94408be874e655905811658df6cc..acb9f5f53f3e7227a266cbd850cd8d8a8565605c 100644 (file)
@@ -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;
 
index 7ffa616a404ef797e091ea8dbbbc61fed7e4da2a..6e6561f22fc8220d494710094e50bf6792d99a61 100644 (file)
 #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 (file)
index 0000000..45172d8
--- /dev/null
@@ -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 (file)
index 0000000..ea91510
--- /dev/null
@@ -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;
+}
+
index 61ccc203b0932e50e268f6f418926e39bc6b34d2..ebf42255cc8ea5572e693f74f6e8515a8864263c 100644 (file)
@@ -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;
 }
        
index a71c927f6250d21924fa1ef7b2ee47257d4ae6b5..68bfff10ee9c23804000d1df23ecb0ef1d500f5f 100644 (file)
 #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);
+}
+
index 631c27b42f8a3077b6c723d6528342a921c240e6..5cab80280142a3631607419ea309b71d966f93db 100644 (file)
@@ -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));
index 29995c66769e1815b67700845fdb16c8ad55eda2..ad629f5d6e53213dd092571e03f289e779a22601 100644 (file)
@@ -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 ();
index 20f2e06d84c43781fccf890a14cfe64a1bc3b43e..265be639608e83eb888cadae2ac898049f3d4884 100644 (file)
@@ -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;
@@ -2044,6 +2060,14 @@ Session::automation_dir () const
        return res;
 }
 
+string
+Session::analysis_dir () const
+{
+       string res = _path;
+       res += "analysis/";
+       return res;
+}
+
 string
 Session::template_dir ()
 {
@@ -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 (file)
index 0000000..e4692b9
--- /dev/null
@@ -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);
+       }
+}