From d3f64c28489d2358498b9d7dcfc6fa4228ebd63e Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Fri, 25 Jan 2008 05:35:46 +0000 Subject: [PATCH] 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 --- SConstruct | 7 + gtk2_ardour/SConscript | 8 +- gtk2_ardour/ardour.menus | 1 + gtk2_ardour/au_pluginui.h | 1 + gtk2_ardour/editor.cc | 18 ++ gtk2_ardour/editor.h | 6 + gtk2_ardour/editor_actions.cc | 3 + gtk2_ardour/editor_ops.cc | 82 ++++++ gtk2_ardour/rhythm_ferret.cc | 376 ++++++++++++++++++++++++ gtk2_ardour/rhythm_ferret.h | 100 +++++++ gtk2_ardour/time_axis_view.cc | 35 +++ gtk2_ardour/time_axis_view.h | 5 + libs/ardour/SConscript | 9 +- libs/ardour/ardour/audioanalyser.h | 74 +++++ libs/ardour/ardour/audioregion.h | 26 +- libs/ardour/ardour/audiosource.h | 17 +- libs/ardour/ardour/plugin_manager.h | 2 + libs/ardour/ardour/region.h | 3 +- libs/ardour/ardour/region_factory.h | 2 +- libs/ardour/ardour/session.h | 3 + libs/ardour/ardour/source.h | 3 +- libs/ardour/ardour/transient_detector.h | 52 ++++ libs/ardour/audioanalyser.cc | 157 ++++++++++ libs/ardour/audioregion.cc | 187 ++++++------ libs/ardour/audiosource.cc | 50 ++++ libs/ardour/region_factory.cc | 2 +- libs/ardour/session.cc | 4 +- libs/ardour/session_state.cc | 48 ++- libs/ardour/transient_detector.cc | 62 ++++ 29 files changed, 1229 insertions(+), 114 deletions(-) create mode 100644 gtk2_ardour/rhythm_ferret.cc create mode 100644 gtk2_ardour/rhythm_ferret.h create mode 100644 libs/ardour/ardour/audioanalyser.h create mode 100644 libs/ardour/ardour/transient_detector.h create mode 100644 libs/ardour/audioanalyser.cc create mode 100644 libs/ardour/transient_detector.cc 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 @@ + 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 #include +#include #include /* 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, vectorset_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 #include #include +#include #include #include #include #include +#include #include #include "ardour_ui.h" @@ -5013,3 +5015,83 @@ Editor::define_one_bar (nframes64_t start, nframes64_t end) session->add_command (new MementoCommand(session->tempo_map(), &before, &after)); commit_reversible_command (); } + +void +Editor::split_region_at_transients () +{ + list 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 ar = boost::dynamic_pointer_cast ((*i)->region()); + + if (!ar) { + continue; + } + + boost::shared_ptr 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::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 +#include + +#include + +#include +#include +#include +#include +#include +#include + +#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 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 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 rd = boost::static_pointer_cast ((*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, nframes64_t offset, vector& results) +{ + TransientDetector t (session->frame_rate()); + bool existing_results = !results.empty(); + + for (uint32_t i = 0; i < readable->n_channels(); ++i) { + + vector 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::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::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& positions) +{ + boost::shared_ptr ar = boost::dynamic_pointer_cast (rv->region()); + + if (!ar) { + return; + } + + boost::shared_ptr pl = ar->playlist(); + + if (!pl) { + return; + } + + vector::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(*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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 analysis_mode_strings; + + std::vector current_results; + + AnalysisMode get_analysis_mode () const; + Action get_action() const; + + void run_analysis (); + int run_percussion_onset_analysis (boost::shared_ptr region, nframes64_t offset, std::vector& results); + + void do_action (); + void do_split_action (); + void do_region_split (RegionView* rv, const std::vector&); +}; + +#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& 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::const_iterator i; + list::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::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 playlist() const { return boost::shared_ptr (); } + virtual void show_temporary_lines (const std::vector&); + 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 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 +#include +#include +#include +#include +#include + +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 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, 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 +#include #include #include @@ -59,6 +60,7 @@ class AudioRegion : public Region bool speed_mismatch (float) const; boost::shared_ptr 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, nframes_t start, nframes_t length); AudioRegion (boost::shared_ptr, 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, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags); AudioRegion (boost::shared_ptr); AudioRegion (boost::shared_ptr, 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 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 #include +#include class XMLNode; @@ -40,7 +41,7 @@ enum RegionEditState { EditChangesID = 2 }; -class Region : public PBD::StatefulDestructible, public boost::enable_shared_from_this +class Region : public PBD::StatefulDestructible, public Readable, public boost::enable_shared_from_this { 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 create (boost::shared_ptr, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags, bool announce = true); - static boost::shared_ptr 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 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 create (boost::shared_ptr); static boost::shared_ptr create (Session&, XMLNode&, bool); static boost::shared_ptr 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 #include +#include 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 + +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, uint32_t channel, std::vector& results); + + protected: + std::vector* 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 +#include +#include +#include // for g_remove() + +#include + +#include +#include +#include + +#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 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 #include #include +#include #include @@ -110,15 +111,15 @@ AudioRegion::AudioRegion (boost::shared_ptr 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 #include #include +#include #include #include #include +#include #include #include @@ -38,6 +40,7 @@ #include #include #include +#include #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 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 -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 arp (new AudioRegion (srcs, start, length, name, layer, flags)); boost::shared_ptr ret (boost::static_pointer_cast (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 #include +#include #include #include @@ -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; @@ -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 index 0000000000..e4692b9c95 --- /dev/null +++ b/libs/ardour/transient_detector.cc @@ -0,0 +1,62 @@ +#include + +#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 src, uint32_t channel, vector& 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); + } +} -- 2.30.2