From 9f63ab9931e6478472853bdda58da47ea29ac125 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sat, 2 Feb 2008 03:57:35 +0000 Subject: Merge with trunk R2978. git-svn-id: svn://localhost/ardour2/branches/3.0@2988 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/ardour/SConscript | 21 ++- libs/ardour/ardour/audioanalyser.h | 74 ++++++++ libs/ardour/ardour/audioregion.h | 24 ++- libs/ardour/ardour/audiosource.h | 17 +- libs/ardour/ardour/cycles.h | 10 -- libs/ardour/ardour/lv2_plugin.h | 45 ++++- libs/ardour/ardour/midi_region.h | 8 +- libs/ardour/ardour/midi_source.h | 11 +- libs/ardour/ardour/plugin_manager.h | 6 +- libs/ardour/ardour/readable.h | 20 +++ libs/ardour/ardour/region.h | 7 +- libs/ardour/ardour/region_factory.h | 2 +- libs/ardour/ardour/session.h | 3 + libs/ardour/ardour/source.h | 3 +- libs/ardour/ardour/tempo.h | 20 ++- libs/ardour/ardour/transient_detector.h | 52 ++++++ libs/ardour/audioanalyser.cc | 157 +++++++++++++++++ libs/ardour/audioregion.cc | 283 ++++++++++++++++++++++--------- libs/ardour/audiosource.cc | 50 ++++++ libs/ardour/globals.cc | 11 ++ libs/ardour/ladspa_plugin.cc | 1 - libs/ardour/lv2_plugin.cc | 163 +++++++++++------- libs/ardour/midi_diskstream.cc | 2 +- libs/ardour/midi_region.cc | 6 +- libs/ardour/midi_source.cc | 4 +- libs/ardour/plugin_manager.cc | 3 +- libs/ardour/port.cc | 4 +- libs/ardour/quantize.cc | 6 +- libs/ardour/region.cc | 16 +- libs/ardour/region_factory.cc | 2 +- libs/ardour/resampled_source.cc | 2 +- libs/ardour/session.cc | 4 +- libs/ardour/session_state.cc | 46 +++-- libs/ardour/source_factory.cc | 2 +- libs/ardour/tempo.cc | 289 +++++++++++++++++++++----------- libs/ardour/transient_detector.cc | 61 +++++++ 36 files changed, 1115 insertions(+), 320 deletions(-) create mode 100644 libs/ardour/ardour/audioanalyser.h create mode 100644 libs/ardour/ardour/readable.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 (limited to 'libs/ardour') diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript index bb66118b53..c407917263 100644 --- a/libs/ardour/SConscript +++ b/libs/ardour/SConscript @@ -31,6 +31,7 @@ amp.cc audio_buffer.cc auto_bundle.cc user_bundle.cc +audioanalyser.cc audio_diskstream.cc audio_library.cc audio_playlist.cc @@ -134,6 +135,7 @@ tape_file_matcher.cc template_utils.cc tempo.cc track.cc +transient_detector.cc utils.cc version.cc """) @@ -162,6 +164,7 @@ if ardour['LIBLO']: ardour.Append(CCFLAGS="-D_REENTRANT -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE") ardour.Append(CXXFLAGS="-DDATA_DIR=\\\"" + os.path.join (final_prefix, 'share') + "\\\"") ardour.Append(CXXFLAGS="-DMODULE_DIR=\\\"" + os.path.join (final_prefix, env['LIBDIR']) + "\\\"") +ardour.Append(CXXFLAGS="-DVAMP_DIR=\\\"" + os.path.join (final_prefix, env['LIBDIR'], 'ardour2', 'vamp') + "\\\"") ardour.Append(CXXFLAGS="-DCONFIG_DIR=\\\"" + final_config_prefix + "\\\"") ardour.Append(CXXFLAGS="-DLOCALEDIR=\\\"" + os.path.join (final_prefix, 'share', 'locale') + "\\\"") @@ -304,15 +307,19 @@ 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'] ]) -# timefx_sources += [ 'rb_effect.cc' ] -#else: -ardour.Merge ([ libraries['soundtouch'] ]) -timefx_sources += [ 'st_stretch.cc', 'st_pitch.cc' ] +if ardour['RUBBERBAND']: + ardour.Merge ([ libraries['rubberband']]) + timefx_sources += [ 'rb_effect.cc' ] +else: + ardour.Merge ([ libraries['soundtouch'] ]) + timefx_sources += [ 'st_stretch.cc', 'st_pitch.cc' ] if ardour['LV2']: ardour.Merge ([ libraries['slv2'] ]) diff --git a/libs/ardour/ardour/audioanalyser.h b/libs/ardour/ardour/audioanalyser.h new file mode 100644 index 0000000000..dbd8a52d5a --- /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 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, Readable*, uint32_t channel); + + /* instances of an analysis object will have this method called + whenever there are results to process. if out is non-null, + the data should be written to the stream it points to. + */ + + virtual int use_features (Vamp::Plugin::FeatureSet&, std::ostream*) = 0; +}; + +} /* namespace */ + +#endif /* __ardour_audioanalyser_h__ */ diff --git a/libs/ardour/ardour/audioregion.h b/libs/ardour/ardour/audioregion.h index b84d197c3f..2c5630aec0 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 @@ -75,6 +76,11 @@ 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, @@ -128,12 +134,14 @@ class AudioRegion : public Region void resume_fade_in (); void resume_fade_out (); + int get_transients (std::vector&, bool force_new = false); + private: friend class RegionFactory; 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, const XMLNode&); AudioRegion (SourceList &, const XMLNode&); @@ -148,10 +156,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; void recompute_at_start (); void recompute_at_end (); @@ -178,6 +187,11 @@ class AudioRegion : public Region AudioRegion (boost::shared_ptr); int set_live_state (const XMLNode&, Change&, bool send); + + std::vector _transients; + bool valid_transients; + void invalidate_transients (); + void cleanup_transients (std::vector&); }; } /* namespace ARDOUR */ diff --git a/libs/ardour/ardour/audiosource.h b/libs/ardour/ardour/audiosource.h index 93708a5b07..b11174abe8 100644 --- a/libs/ardour/ardour/audiosource.h +++ b/libs/ardour/ardour/audiosource.h @@ -50,10 +50,20 @@ 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; @@ -134,6 +147,8 @@ class AudioSource : public Source, public boost::enable_shared_from_this -#else // Due to MacTypes.h and libgnomecanvasmm Rect conflict -typedef unsigned long long UInt64; - -extern UInt64 -AudioGetCurrentHostTime(); - -extern UInt64 -AudioConvertHostTimeToNanos(UInt64 inHostTime); -#endif typedef UInt64 cycles_t; static inline cycles_t get_cycles (void) diff --git a/libs/ardour/ardour/lv2_plugin.h b/libs/ardour/ardour/lv2_plugin.h index 40b3c669fa..777f285e9d 100644 --- a/libs/ardour/ardour/lv2_plugin.h +++ b/libs/ardour/ardour/lv2_plugin.h @@ -37,20 +37,21 @@ namespace ARDOUR { class AudioEngine; class Session; +struct LV2World; class LV2Plugin : public ARDOUR::Plugin { public: - LV2Plugin (ARDOUR::AudioEngine&, ARDOUR::Session&, SLV2Plugin plugin, nframes_t sample_rate); + LV2Plugin (ARDOUR::AudioEngine&, ARDOUR::Session&, ARDOUR::LV2World&, SLV2Plugin plugin, nframes_t sample_rate); LV2Plugin (const LV2Plugin &); ~LV2Plugin (); /* Plugin interface */ std::string unique_id() const; - const char* label() const { return slv2_plugin_get_name(_plugin); } - const char* name() const { return slv2_plugin_get_name(_plugin); } - const char* maker() const { return slv2_plugin_get_author_name(_plugin); } + const char* label() const { return slv2_value_as_string(_name); } + const char* name() const { return slv2_value_as_string(_name); } + const char* maker() const { return _author ? slv2_value_as_string(_author) : "Unknown"; } uint32_t parameter_count() const { return slv2_plugin_get_num_ports(_plugin); } float default_value (uint32_t port); nframes_t signal_latency() const; @@ -58,6 +59,9 @@ class LV2Plugin : public ARDOUR::Plugin float get_parameter (uint32_t port) const; int get_parameter_descriptor (uint32_t which, ParameterDescriptor&) const; uint32_t nth_parameter (uint32_t port, bool& ok) const; + + SLV2Plugin slv2_plugin() { return _plugin; } + SLV2Port slv2_port(uint32_t i) { return slv2_plugin_get_port_by_index(_plugin, i); } std::set automatable() const; @@ -105,29 +109,56 @@ class LV2Plugin : public ARDOUR::Plugin private: void* _module; + LV2World& _world; SLV2Plugin _plugin; - SLV2Template _template; + SLV2Value _name; + SLV2Value _author; SLV2Instance _instance; nframes_t _sample_rate; float* _control_data; float* _shadow_data; + float* _defaults; float* _latency_control_port; bool _was_activated; vector _port_is_input; - void init (SLV2Plugin plugin, nframes_t rate); + void init (LV2World& world, SLV2Plugin plugin, nframes_t rate); void run (nframes_t nsamples); void latency_compute_run (); }; + +/** The SLV2World, and various cached (as symbols, fast) URIs. + * + * This object represents everything ardour 'knows' about LV2 + * (ie understood extensions/features/etc) + */ +struct LV2World { + LV2World(); + ~LV2World(); + + SLV2World world; + SLV2Value input_class; + SLV2Value output_class; + SLV2Value audio_class; + SLV2Value control_class; + SLV2Value event_class; + SLV2Value in_place_broken; + SLV2Value integer; + SLV2Value toggled; + SLV2Value srate; +}; + + class LV2PluginInfo : public PluginInfo { public: - LV2PluginInfo (void* slv2_plugin);; + LV2PluginInfo (void* slv2_world, void* slv2_plugin);; ~LV2PluginInfo ();; static PluginInfoList discover (void* slv2_world); PluginPtr load (Session& session); + void* _lv2_world; void* _slv2_plugin; }; diff --git a/libs/ardour/ardour/midi_region.h b/libs/ardour/ardour/midi_region.h index 869436e423..50c6b8bce7 100644 --- a/libs/ardour/ardour/midi_region.h +++ b/libs/ardour/ardour/midi_region.h @@ -50,6 +50,10 @@ class MidiRegion : public Region ~MidiRegion(); boost::shared_ptr midi_source (uint32_t n=0) const; + + /* Stub Readable interface */ + virtual nframes64_t read (Sample*, nframes64_t pos, nframes64_t cnt, int channel) const { return 0; } + virtual nframes64_t readable_length() const { return length(); } nframes_t read_at (MidiRingBuffer& dst, nframes_t position, @@ -86,11 +90,11 @@ class MidiRegion : public Region MidiRegion (boost::shared_ptr, nframes_t start, nframes_t length); MidiRegion (boost::shared_ptr, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags); - MidiRegion (SourceList &, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags); + MidiRegion (const SourceList &, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags); MidiRegion (boost::shared_ptr, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags); MidiRegion (boost::shared_ptr); MidiRegion (boost::shared_ptr, const XMLNode&); - MidiRegion (SourceList &, const XMLNode&); + MidiRegion (const SourceList &, const XMLNode&); private: nframes_t _read_at (const SourceList&, MidiRingBuffer& dst, diff --git a/libs/ardour/ardour/midi_source.h b/libs/ardour/ardour/midi_source.h index c83debec3d..323fc8b5a1 100644 --- a/libs/ardour/ardour/midi_source.h +++ b/libs/ardour/ardour/midi_source.h @@ -49,8 +49,15 @@ class MidiSource : public Source MidiSource (Session& session, const XMLNode&); virtual ~MidiSource (); - virtual nframes_t read (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset) const; - virtual nframes_t write (MidiRingBuffer& src, nframes_t cnt); + /* Stub Readable interface */ + virtual nframes64_t read (Sample*, nframes64_t pos, nframes64_t cnt, int channel) const { return 0; } + virtual nframes64_t readable_length() const { return length(); } + virtual uint32_t n_channels () const { return 1; } + + // FIXME: integrate this with the Readable::read interface somehow + virtual nframes_t midi_read (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset) const; + virtual nframes_t midi_write (MidiRingBuffer& src, nframes_t cnt); + virtual void append_event_unlocked(const MidiEvent& ev) = 0; virtual void mark_for_remove() = 0; diff --git a/libs/ardour/ardour/plugin_manager.h b/libs/ardour/ardour/plugin_manager.h index dd50a079e3..892c8bd75a 100644 --- a/libs/ardour/ardour/plugin_manager.h +++ b/libs/ardour/ardour/plugin_manager.h @@ -28,7 +28,7 @@ #include #ifdef HAVE_SLV2 -#include +#include #endif namespace ARDOUR { @@ -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; } @@ -59,7 +61,7 @@ class PluginManager { ARDOUR::PluginInfoList _au_plugin_info; #ifdef HAVE_SLV2 - SLV2World _lv2_world; + LV2World* _lv2_world; #endif std::map rdf_type; diff --git a/libs/ardour/ardour/readable.h b/libs/ardour/ardour/readable.h new file mode 100644 index 0000000000..e072a1c95e --- /dev/null +++ b/libs/ardour/ardour/readable.h @@ -0,0 +1,20 @@ +#ifndef __ardour_readable_h__ +#define __ardour_readable_h__ + +#include + +namespace ARDOUR { + +class Readable { + public: + Readable () {} + virtual ~Readable() {} + + virtual nframes64_t read (Sample*, nframes64_t pos, nframes64_t cnt, int channel) const = 0; + virtual nframes64_t readable_length() const = 0; + virtual uint32_t n_channels () const = 0; +}; + +} + +#endif /* __ardour_readable_h__ */ diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h index 76b41a04cb..da07c580b4 100644 --- a/libs/ardour/ardour/region.h +++ b/libs/ardour/ardour/region.h @@ -29,6 +29,7 @@ #include #include #include +#include class XMLNode; @@ -43,7 +44,7 @@ enum RegionEditState { EditChangesID = 2 }; -class Region : public Automatable, public boost::enable_shared_from_this +class Region : public Automatable, public boost::enable_shared_from_this, public Readable { public: typedef std::vector > SourceList; @@ -217,13 +218,13 @@ class Region : public Automatable, public boost::enable_shared_from_this Region (boost::shared_ptr src, nframes_t start, nframes_t length, const string& name, DataType type, layer_t = 0, Flag flags = DefaultFlags); - Region (SourceList& srcs, nframes_t start, nframes_t length, + Region (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, DataType type, layer_t = 0, Flag flags = DefaultFlags); Region (boost::shared_ptr, nframes_t start, nframes_t length, const string& name, layer_t = 0, Flag flags = DefaultFlags); Region (boost::shared_ptr); Region (boost::shared_ptr src, const XMLNode&); - Region (SourceList& srcs, const XMLNode&); + Region (const SourceList& srcs, const XMLNode&); /* this one is for derived types of derived types */ diff --git a/libs/ardour/ardour/region_factory.h b/libs/ardour/ardour/region_factory.h index e6b9e5dde6..59749613ac 100644 --- a/libs/ardour/ardour/region_factory.h +++ b/libs/ardour/ardour/region_factory.h @@ -48,7 +48,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 c6b913b979..d27df2f7ea 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -261,6 +261,9 @@ class Session : public PBD::StatefulDestructible const SessionDirectory& session_directory () const { return *(_session_dir.get()); } 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 aa8bc0ca1f..174e58c61b 100644 --- a/libs/ardour/ardour/source.h +++ b/libs/ardour/ardour/source.h @@ -30,13 +30,14 @@ #include #include #include +#include namespace ARDOUR { class Session; class Playlist; -class Source : public SessionObject +class Source : public SessionObject, public ARDOUR::Readable { public: Source (Session&, const std::string& name, DataType type); diff --git a/libs/ardour/ardour/tempo.h b/libs/ardour/ardour/tempo.h index 72f24c1054..dc49f5cdef 100644 --- a/libs/ardour/ardour/tempo.h +++ b/libs/ardour/ardour/tempo.h @@ -109,6 +109,9 @@ class MetricSection { public: MetricSection (const BBT_Time& start) : _start (start), _frame (0), _movable (true) {} + MetricSection (nframes_t start) + : _frame (start), _movable (true) {} + virtual ~MetricSection() {} const BBT_Time& start() const { return _start; } @@ -142,6 +145,8 @@ class MeterSection : public MetricSection, public Meter { public: MeterSection (const BBT_Time& start, double bpb, double note_type) : MetricSection (start), Meter (bpb, note_type) {} + MeterSection (nframes_t start, double bpb, double note_type) + : MetricSection (start), Meter (bpb, note_type) {} MeterSection (const XMLNode&); static const string xml_state_node_name; @@ -153,6 +158,8 @@ class TempoSection : public MetricSection, public Tempo { public: TempoSection (const BBT_Time& start, double qpm, double note_type) : MetricSection (start), Tempo (qpm, note_type) {} + TempoSection (nframes_t start, double qpm, double note_type) + : MetricSection (start), Tempo (qpm, note_type) {} TempoSection (const XMLNode&); static const string xml_state_node_name; @@ -165,7 +172,6 @@ typedef list Metrics; class TempoMap : public PBD::StatefulDestructible { public: - TempoMap (nframes_t frame_rate); ~TempoMap(); @@ -207,9 +213,14 @@ class TempoMap : public PBD::StatefulDestructible const Tempo& tempo_at (nframes_t); const Meter& meter_at (nframes_t); + const TempoSection& tempo_section_at (nframes_t); + void add_tempo(const Tempo&, BBT_Time where); void add_meter(const Meter&, BBT_Time where); + void add_tempo(const Tempo&, nframes_t where); + void add_meter(const Meter&, nframes_t where); + void move_tempo (TempoSection&, const BBT_Time& to); void move_meter (MeterSection&, const BBT_Time& to); @@ -267,6 +278,8 @@ class TempoMap : public PBD::StatefulDestructible Metric metric_at (nframes_t) const; void bbt_time_with_metric (nframes_t, BBT_Time&, const Metric&) const; + void change_existing_tempo_at (nframes_t, double bpm, double note_type); + sigc::signal StateChanged; private: @@ -280,8 +293,7 @@ class TempoMap : public PBD::StatefulDestructible BBT_Time last_bbt; mutable Glib::RWLock lock; - void timestamp_metrics (); - + void timestamp_metrics (bool use_bbt); nframes_t round_to_type (nframes_t fr, int dir, BBTPointType); @@ -298,7 +310,7 @@ class TempoMap : public PBD::StatefulDestructible nframes_t count_frames_between_metrics (const Meter&, const Tempo&, const BBT_Time&, const BBT_Time&) const; int move_metric_section (MetricSection&, const BBT_Time& to); - void do_insert (MetricSection* section); + void do_insert (MetricSection* section, bool with_bbt); }; }; /* namespace ARDOUR */ diff --git a/libs/ardour/ardour/transient_detector.h b/libs/ardour/ardour/transient_detector.h new file mode 100644 index 0000000000..c65bae3ed5 --- /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, Readable*, 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..4cc99a5d5e --- /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) { + error << string_compose (_("VAMP Plugin \"%1\" could not be loaded"), key) << endmsg; + return -1; + } + + /* we asked for the buffering adapter, so set the blocksize to + something that makes for efficient disk i/o + */ + + bufsize = 65536; + 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, Readable* src, uint32_t channel) +{ + ofstream ofile; + Plugin::FeatureSet onsets; + int ret = -1; + bool done = false; + Sample* data = 0; + nframes64_t len = src->readable_length(); + nframes64_t pos = 0; + float* bufs[1] = { 0 }; + + if (!path.empty()) { + ofile.open (path.c_str()); + if (!ofile) { + goto out; + } + } + + /* create VAMP percussion onset plugin and initialize */ + + if (plugin == 0) { + if (initialize_plugin (plugin_key, sample_rate)) { + goto out; + } + } + + data = new Sample[bufsize]; + bufs[0] = data; + + while (!done) { + + nframes64_t to_read; + + /* read from source */ + + to_read = min ((len - pos), bufsize); + + if (src->read (data, pos, to_read, channel) != to_read) { + cerr << "bad read\n"; + goto out; + } + + /* zero fill buffer if necessary */ + + if (to_read != bufsize) { + memset (data + to_read, 0, (bufsize - to_read)); + } + + onsets = plugin->process (bufs, RealTime::fromSeconds ((double) pos / sample_rate)); + + if (use_features (onsets, (path.empty() ? &ofile : 0))) { + goto out; + } + + pos += stepsize; + + if (pos >= len) { + done = true; + } + } + + /* finish up VAMP plugin */ + + onsets = plugin->getRemainingFeatures (); + + if (use_features (onsets, (path.empty() ? &ofile : 0))) { + goto out; + } + + ret = 0; + + out: + /* works even if it has not been opened */ + ofile.close (); + + if (ret) { + g_remove (path.c_str()); + } + if (data) { + delete data; + } + + return ret; +} + diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index 86abd4beaa..822fe2cb72 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -42,6 +43,7 @@ #include #include #include +#include #include "i18n.h" #include @@ -64,6 +66,7 @@ void AudioRegion::init () { _scale_amplitude = 1.0; + valid_transients = false; set_default_fades (); set_default_envelope (); @@ -112,7 +115,7 @@ AudioRegion::AudioRegion (boost::shared_ptr src, nframes_t start, n } /* Basic AudioRegion constructor (many channels) */ -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 (srcs, start, length, name, DataType::AUDIO, layer, flags) , _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0)) , _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0)) @@ -121,7 +124,6 @@ AudioRegion::AudioRegion (SourceList& srcs, nframes_t start, nframes_t length, c init (); } - /** Create a new AudioRegion, that is part of an existing one */ AudioRegion::AudioRegion (boost::shared_ptr other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags) : Region (other, offset, length, name, layer, flags) @@ -170,6 +172,7 @@ AudioRegion::AudioRegion (boost::shared_ptr other, nframes_t } _scale_amplitude = other->_scale_amplitude; + valid_transients = false; assert(_type == DataType::AUDIO); } @@ -181,6 +184,7 @@ AudioRegion::AudioRegion (boost::shared_ptr other) , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0)) { _scale_amplitude = other->_scale_amplitude; + valid_transients = false; _envelope = other->_envelope; set_default_fades (); @@ -202,6 +206,7 @@ AudioRegion::AudioRegion (boost::shared_ptr src, const XMLNode& nod } init (); + valid_transients = false; if (set_state (node)) { throw failed_constructor(); @@ -229,6 +234,13 @@ AudioRegion::~AudioRegion () { } +void +AudioRegion::invalidate_transients () +{ + valid_transients = false; + _transients.clear (); +} + void AudioRegion::listen_to_my_curves () { @@ -273,12 +285,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 @@ -291,13 +311,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 */ } @@ -320,14 +343,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()) { @@ -336,7 +361,9 @@ AudioRegion::_read_at (const SourceList& srcs, Sample *buf, Sample *mixdown_buff return 0; /* "read nothing" */ } - _read_data_count += src->read_data_count(); + if (!raw) { + _read_data_count += src->read_data_count(); + } } else { @@ -348,37 +375,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->curve().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->curve().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 @@ -389,65 +420,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->curve().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->curve().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->curve().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->curve().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) { - 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; } @@ -1224,6 +1256,93 @@ AudioRegion::audio_source (uint32_t n) const return boost::dynamic_pointer_cast(source(n)); } +void +AudioRegion::cleanup_transients (vector& t) +{ + sort (t.begin(), t.end()); + + /* remove duplicates or other things that are too close */ + + vector::iterator i = t.begin(); + nframes64_t curr = (*i); + + /* XXX force a 3msec gap - use a config variable */ + + nframes64_t gap_frames = (nframes64_t) floor (3.0 * (playlist()->session().frame_rate() / 1000.0)); + + ++i; + + while (i != t.end()) { + if (((*i) == curr) || (((*i) - curr) < gap_frames)) { + i = t.erase (i); + } else { + ++i; + curr = *i; + } + } +} + +int +AudioRegion::get_transients (vector& results, bool force_new) +{ + if (!playlist()) { + return -1; + } + + if (valid_transients && !force_new) { + results = _transients; + return 0; + } + + TransientDetector t (playlist()->session().frame_rate()); + bool existing_results = !results.empty(); + + _transients.clear (); + valid_transients = false; + + for (uint32_t i = 0; i < n_channels(); ++i) { + + vector these_results; + + t.reset (); + + if (t.run ("", this, i, these_results)) { + return -1; + } + + /* translate all transients to give absolute position */ + + for (vector::iterator i = these_results.begin(); i != these_results.end(); ++i) { + (*i) += _position; + } + + /* merge */ + + _transients.insert (_transients.end(), these_results.begin(), these_results.end()); + } + + if (!results.empty()) { + if (existing_results) { + + /* merge our transients into the existing ones, then clean up + those. + */ + + results.insert (results.end(), _transients.begin(), _transients.end()); + cleanup_transients (results); + } + + /* make sure ours are clean too */ + + cleanup_transients (_transients); + } + + valid_transients = true; + + return 0; +} + + extern "C" { int region_read_peaks_from_c (void *arg, uint32_t npeaks, uint32_t start, uint32_t cnt, intptr_t data, uint32_t n_chan, double samples_per_unit) diff --git a/libs/ardour/audiosource.cc b/libs/ardour/audiosource.cc index ce8aa95964..80116988d5 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" @@ -916,3 +919,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/globals.cc b/libs/ardour/globals.cc index 7405077cf3..6bb21a419c 100644 --- a/libs/ardour/globals.cc +++ b/libs/ardour/globals.cc @@ -285,6 +285,17 @@ ARDOUR::init (bool use_vst, bool try_optimization) return -1; } #endif + + /* Make VAMP look in our library ahead of anything else */ + + char *p = getenv ("VAMP_PATH"); + string vamppath = VAMP_DIR; + if (p) { + vamppath += ':'; + vamppath += p; + } + setenv ("VAMP_PATH", vamppath.c_str(), 1); + setup_hardware_optimization (try_optimization); diff --git a/libs/ardour/ladspa_plugin.cc b/libs/ardour/ladspa_plugin.cc index ccc12f8cf8..29f2d16767 100644 --- a/libs/ardour/ladspa_plugin.cc +++ b/libs/ardour/ladspa_plugin.cc @@ -467,7 +467,6 @@ LadspaPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& des desc.label = port_names()[which]; - return 0; } diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index 5df3756364..e58e5ed140 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -43,16 +43,18 @@ using namespace std; using namespace ARDOUR; using namespace PBD; -LV2Plugin::LV2Plugin (AudioEngine& e, Session& session, SLV2Plugin plugin, nframes_t rate) +LV2Plugin::LV2Plugin (AudioEngine& e, Session& session, LV2World& world, SLV2Plugin plugin, nframes_t rate) : Plugin (e, session) + , _world(world) { - init (plugin, rate); + init (world, plugin, rate); } LV2Plugin::LV2Plugin (const LV2Plugin &other) : Plugin (other) + , _world(other._world) { - init (other._plugin, other._sample_rate); + init (other._world, other._plugin, other._sample_rate); for (uint32_t i = 0; i < parameter_count(); ++i) { _control_data[i] = other._shadow_data[i]; @@ -61,24 +63,30 @@ LV2Plugin::LV2Plugin (const LV2Plugin &other) } void -LV2Plugin::init (SLV2Plugin plugin, nframes_t rate) +LV2Plugin::init (LV2World& world, SLV2Plugin plugin, nframes_t rate) { + _world = world; _plugin = plugin; - _template = slv2_plugin_get_template(plugin); _control_data = 0; _shadow_data = 0; _latency_control_port = 0; _was_activated = false; _instance = slv2_plugin_instantiate(plugin, rate, NULL); + _name = slv2_plugin_get_name(plugin); + assert(_name); + _author = slv2_plugin_get_author_name(plugin); if (_instance == 0) { error << _("LV2: Failed to instantiate plugin ") << slv2_plugin_get_uri(plugin) << endl; throw failed_constructor(); } - if (slv2_plugin_has_feature(plugin, "http://lv2plug.in/ns/lv2core#inPlaceBroken")) { - error << string_compose(_("LV2: \"%1\" cannot be used, since it cannot do inplace processing"), slv2_plugin_get_name(plugin)) << endmsg; + if (slv2_plugin_has_feature(plugin, world.in_place_broken)) { + error << string_compose(_("LV2: \"%1\" cannot be used, since it cannot do inplace processing"), + slv2_value_as_string(_name)); + slv2_value_free(_name); + slv2_value_free(_author); throw failed_constructor(); } @@ -88,14 +96,21 @@ LV2Plugin::init (SLV2Plugin plugin, nframes_t rate) _control_data = new float[num_ports]; _shadow_data = new float[num_ports]; + _defaults = new float[num_ports]; const bool latent = slv2_plugin_has_latency(plugin); - uint32_t latency_port = (latent ? slv2_plugin_get_latency_port(plugin) : 0); + uint32_t latency_port = (latent ? slv2_plugin_get_latency_port_index(plugin) : 0); for (uint32_t i = 0; i < num_ports; ++i) { if (parameter_is_control(i)) { + SLV2Port port = slv2_plugin_get_port_by_index(plugin, i); + SLV2Value def; + slv2_port_get_range(plugin, port, &def, NULL, NULL); + _defaults[i] = def ? slv2_value_as_float(def) : 0.0f; + slv2_value_free(def); + slv2_instance_connect_port (_instance, i, &_control_data[i]); - + if (latent && i == latency_port) { _latency_control_port = &_control_data[i]; *_latency_control_port = 0; @@ -104,6 +119,8 @@ LV2Plugin::init (SLV2Plugin plugin, nframes_t rate) if (parameter_is_input(i)) { _shadow_data[i] = default_value (i); } + } else { + _defaults[i] = 0.0f; } } @@ -118,6 +135,8 @@ LV2Plugin::~LV2Plugin () GoingAway (); /* EMIT SIGNAL */ slv2_instance_free(_instance); + slv2_value_free(_name); + slv2_value_free(_author); if (_control_data) { delete [] _control_data; @@ -131,15 +150,14 @@ LV2Plugin::~LV2Plugin () string LV2Plugin::unique_id() const { - return slv2_plugin_get_uri(_plugin); + return slv2_value_as_uri(slv2_plugin_get_uri(_plugin)); } float LV2Plugin::default_value (uint32_t port) { - return slv2_port_get_default_value(_plugin, - slv2_plugin_get_port_by_index(_plugin, port)); + return _defaults[port]; } void @@ -276,15 +294,16 @@ LV2Plugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& desc) { SLV2Port port = slv2_plugin_get_port_by_index(_plugin, which); - #define LV2_URI "http://lv2plug.in/ns/lv2core#" + SLV2Value def, min, max; + slv2_port_get_range(_plugin, port, &def, &min, &max); - desc.integer_step = slv2_port_has_property(_plugin, port, LV2_URI "integer"); - desc.toggled = slv2_port_has_property(_plugin, port, LV2_URI "toggled"); + desc.integer_step = slv2_port_has_property(_plugin, port, _world.integer); + desc.toggled = slv2_port_has_property(_plugin, port, _world.toggled); desc.logarithmic = false; // TODO (LV2 extension) - desc.sr_dependent = slv2_port_has_property(_plugin, port, LV2_URI "sampleRate"); - desc.label = slv2_port_get_name(_plugin, port); - desc.lower = slv2_port_get_minimum_value(_plugin, port); - desc.upper = slv2_port_get_maximum_value(_plugin, port); + desc.sr_dependent = slv2_port_has_property(_plugin, port, _world.srate); + desc.label = slv2_value_as_string(slv2_port_get_name(_plugin, port)); + desc.lower = min ? slv2_value_as_float(min) : 0.0f; + desc.upper = max ? slv2_value_as_float(max) : 1.0f; desc.min_unbound = false; // TODO (LV2 extension) desc.max_unbound = false; // TODO (LV2 extension) @@ -299,6 +318,10 @@ LV2Plugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& desc) desc.largestep = delta/10.0f; } + slv2_value_free(def); + slv2_value_free(min); + slv2_value_free(max); + return 0; } @@ -307,8 +330,11 @@ string LV2Plugin::describe_parameter (Parameter which) { if (which.type() == PluginAutomation && which.id() < parameter_count()) { - return slv2_port_get_name(_plugin, - slv2_plugin_get_port_by_index(_plugin, which)); + SLV2Value name = slv2_port_get_name(_plugin, + slv2_plugin_get_port_by_index(_plugin, which)); + string ret(slv2_value_as_string(name)); + slv2_value_free(name); + return ret; } else { return "??"; } @@ -377,29 +403,29 @@ LV2Plugin::connect_and_run (BufferSet& bufs, uint32_t& in_index, uint32_t& out_i bool LV2Plugin::parameter_is_control (uint32_t param) const { - SLV2PortSignature sig = slv2_template_get_port(_template, param); - return (slv2_port_signature_get_type(sig) == SLV2_PORT_DATA_TYPE_CONTROL); + SLV2Port port = slv2_plugin_get_port_by_index(_plugin, param); + return slv2_port_is_a(_plugin, port, _world.control_class); } bool LV2Plugin::parameter_is_audio (uint32_t param) const { - SLV2PortSignature sig = slv2_template_get_port(_template, param); - return (slv2_port_signature_get_type(sig) == SLV2_PORT_DATA_TYPE_AUDIO); + SLV2Port port = slv2_plugin_get_port_by_index(_plugin, param); + return slv2_port_is_a(_plugin, port, _world.audio_class); } bool LV2Plugin::parameter_is_output (uint32_t param) const { - SLV2PortSignature sig = slv2_template_get_port(_template, param); - return (slv2_port_signature_get_direction(sig) == SLV2_PORT_DIRECTION_OUTPUT); + SLV2Port port = slv2_plugin_get_port_by_index(_plugin, param); + return slv2_port_is_a(_plugin, port, _world.output_class); } bool LV2Plugin::parameter_is_input (uint32_t param) const { - SLV2PortSignature sig = slv2_template_get_port(_template, param); - return (slv2_port_signature_get_direction(sig) == SLV2_PORT_DIRECTION_INPUT); + SLV2Port port = slv2_plugin_get_port_by_index(_plugin, param); + return slv2_port_is_a(_plugin, port, _world.input_class); } void @@ -418,9 +444,7 @@ void LV2Plugin::run (nframes_t nframes) { for (uint32_t i = 0; i < parameter_count(); ++i) { - SLV2PortSignature sig = slv2_template_get_port(_template, i); - if (slv2_port_signature_get_type(sig) == SLV2_PORT_DATA_TYPE_CONTROL - && slv2_port_signature_get_direction(sig) == SLV2_PORT_DIRECTION_INPUT) { + if (parameter_is_control(i) && parameter_is_input(i)) { _control_data[i] = _shadow_data[i]; } } @@ -472,8 +496,34 @@ LV2Plugin::latency_compute_run () deactivate (); } -LV2PluginInfo::LV2PluginInfo (void* slv2_plugin) - : _slv2_plugin(slv2_plugin) +LV2World::LV2World() + : world(slv2_world_new()) +{ + slv2_world_load_all(world); + input_class = slv2_value_new_uri(world, SLV2_PORT_CLASS_INPUT); + output_class = slv2_value_new_uri(world, SLV2_PORT_CLASS_OUTPUT); + control_class = slv2_value_new_uri(world, SLV2_PORT_CLASS_CONTROL); + audio_class = slv2_value_new_uri(world, SLV2_PORT_CLASS_AUDIO); + event_class = slv2_value_new_uri(world, SLV2_PORT_CLASS_EVENT); + in_place_broken = slv2_value_new_uri(world, SLV2_NAMESPACE_LV2 "inPlaceBroken"); + integer = slv2_value_new_uri(world, SLV2_NAMESPACE_LV2 "integer"); + toggled = slv2_value_new_uri(world, SLV2_NAMESPACE_LV2 "toggled"); + srate = slv2_value_new_uri(world, SLV2_NAMESPACE_LV2 "sampleRate"); +} + +LV2World::~LV2World() +{ + slv2_value_free(input_class); + slv2_value_free(output_class); + slv2_value_free(control_class); + slv2_value_free(audio_class); + slv2_value_free(event_class); + slv2_value_free(in_place_broken); +} + +LV2PluginInfo::LV2PluginInfo (void* lv2_world, void* slv2_plugin) + : _lv2_world(lv2_world) + , _slv2_plugin(slv2_plugin) { } @@ -484,12 +534,11 @@ LV2PluginInfo::~LV2PluginInfo() PluginPtr LV2PluginInfo::load (Session& session) { - SLV2Plugin p = (SLV2Plugin)_slv2_plugin; - try { PluginPtr plugin; - plugin.reset (new LV2Plugin (session.engine(), session, p, session.frame_rate())); + plugin.reset (new LV2Plugin (session.engine(), session, + *(LV2World*)_lv2_world, (SLV2Plugin)_slv2_plugin, session.frame_rate())); plugin->set_info(PluginInfoPtr(new LV2PluginInfo(*this))); return plugin; @@ -503,40 +552,42 @@ LV2PluginInfo::load (Session& session) } PluginInfoList -LV2PluginInfo::discover (void* slv2_world) +LV2PluginInfo::discover (void* lv2_world) { PluginInfoList plugs; - SLV2Plugins plugins = slv2_world_get_all_plugins((SLV2World)slv2_world); + LV2World* world = (LV2World*)lv2_world; + SLV2Plugins plugins = slv2_world_get_all_plugins(world->world); for (unsigned i=0; i < slv2_plugins_size(plugins); ++i) { SLV2Plugin p = slv2_plugins_get_at(plugins, i); - LV2PluginInfoPtr info (new LV2PluginInfo(p)); + LV2PluginInfoPtr info (new LV2PluginInfo(lv2_world, p)); - info->name = slv2_plugin_get_name(p); + SLV2Value name = slv2_plugin_get_name(p); + info->name = string(slv2_value_as_string(name)); + slv2_value_free(name); SLV2PluginClass pclass = slv2_plugin_get_class(p); - info->category = slv2_plugin_class_get_label(pclass); + SLV2Value label = slv2_plugin_class_get_label(pclass); + info->category = slv2_value_as_string(label); - char* author_name = slv2_plugin_get_author_name(p); - info->creator = author_name ? string(author_name) : "Unknown"; - free(author_name); + SLV2Value author_name = slv2_plugin_get_author_name(p); + info->creator = author_name ? string(slv2_value_as_string(author_name)) : "Unknown"; + slv2_value_free(author_name); info->path = "/NOPATH"; // Meaningless for LV2 - SLV2Template io = slv2_plugin_get_template(p); - - info->n_inputs.set_audio(slv2_template_get_num_ports_of_type(io, - SLV2_PORT_DIRECTION_INPUT, SLV2_PORT_DATA_TYPE_AUDIO)); - info->n_outputs.set_audio(slv2_template_get_num_ports_of_type(io, - SLV2_PORT_DIRECTION_OUTPUT, SLV2_PORT_DATA_TYPE_AUDIO)); + info->n_inputs.set_audio(slv2_plugin_get_num_ports_of_class(p, + world->input_class, world->audio_class, NULL)); + info->n_inputs.set_midi(slv2_plugin_get_num_ports_of_class(p, + world->input_class, world->event_class, NULL)); - info->n_inputs.set_midi(slv2_template_get_num_ports_of_type(io, - SLV2_PORT_DIRECTION_INPUT, SLV2_PORT_DATA_TYPE_MIDI)); - info->n_outputs.set_midi(slv2_template_get_num_ports_of_type(io, - SLV2_PORT_DIRECTION_OUTPUT, SLV2_PORT_DATA_TYPE_MIDI)); + info->n_outputs.set_audio(slv2_plugin_get_num_ports_of_class(p, + world->output_class, world->audio_class, NULL)); + info->n_outputs.set_midi(slv2_plugin_get_num_ports_of_class(p, + world->output_class, world->event_class, NULL)); - info->unique_id = slv2_plugin_get_uri(p); + info->unique_id = slv2_value_as_uri(slv2_plugin_get_uri(p)); info->index = 0; // Meaningless for LV2 plugs.push_back (info); diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc index 3a2842be7a..ea340598ac 100644 --- a/libs/ardour/midi_diskstream.cc +++ b/libs/ardour/midi_diskstream.cc @@ -933,7 +933,7 @@ MidiDiskstream::do_flush (Session::RunContext context, bool force_flush) assert(!destructive()); if (record_enabled() && _session.transport_frame() - _last_flush_frame > disk_io_chunk_frames) { - if ((!_write_source) || _write_source->write (*_capture_buf, to_write) != to_write) { + if ((!_write_source) || _write_source->midi_write (*_capture_buf, to_write) != to_write) { error << string_compose(_("MidiDiskstream %1: cannot write to disk"), _id) << endmsg; return -1; } else { diff --git a/libs/ardour/midi_region.cc b/libs/ardour/midi_region.cc index cc1ba4b2a8..b0b7e4575f 100644 --- a/libs/ardour/midi_region.cc +++ b/libs/ardour/midi_region.cc @@ -65,7 +65,7 @@ MidiRegion::MidiRegion (boost::shared_ptr src, nframes_t start, nfra } /* Basic MidiRegion constructor (many channels) */ -MidiRegion::MidiRegion (SourceList& srcs, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags) +MidiRegion::MidiRegion (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags) : Region (srcs, start, length, name, DataType::MIDI, layer, flags) { assert(_name.find("/") == string::npos); @@ -100,7 +100,7 @@ MidiRegion::MidiRegion (boost::shared_ptr src, const XMLNode& node) assert(_type == DataType::MIDI); } -MidiRegion::MidiRegion (SourceList& srcs, const XMLNode& node) +MidiRegion::MidiRegion (const SourceList& srcs, const XMLNode& node) : Region (srcs, node) { if (set_state (node)) { @@ -171,7 +171,7 @@ MidiRegion::_read_at (const SourceList& srcs, MidiRingBuffer& dst, nframes_t pos boost::shared_ptr src = midi_source(chan_n); src->set_note_mode(mode); - if (src->read (dst, _start + internal_offset, to_read, _position) != to_read) { + if (src->midi_read (dst, _start + internal_offset, to_read, _position) != to_read) { return 0; /* "read nothing" */ } diff --git a/libs/ardour/midi_source.cc b/libs/ardour/midi_source.cc index f072c2a7ef..794959328a 100644 --- a/libs/ardour/midi_source.cc +++ b/libs/ardour/midi_source.cc @@ -101,7 +101,7 @@ MidiSource::set_state (const XMLNode& node) } nframes_t -MidiSource::read (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset) const +MidiSource::midi_read (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset) const { Glib::Mutex::Lock lm (_lock); if (_model) { @@ -114,7 +114,7 @@ MidiSource::read (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t } nframes_t -MidiSource::write (MidiRingBuffer& dst, nframes_t cnt) +MidiSource::midi_write (MidiRingBuffer& dst, nframes_t cnt) { Glib::Mutex::Lock lm (_lock); return write_unlocked (dst, cnt); diff --git a/libs/ardour/plugin_manager.cc b/libs/ardour/plugin_manager.cc index ae5d6f52ee..6ff780a25f 100644 --- a/libs/ardour/plugin_manager.cc +++ b/libs/ardour/plugin_manager.cc @@ -110,8 +110,7 @@ PluginManager::PluginManager () } #ifdef HAVE_SLV2 - _lv2_world = slv2_world_new(); - slv2_world_load_all(_lv2_world); + _lv2_world = new LV2World(); #endif refresh (); diff --git a/libs/ardour/port.cc b/libs/ardour/port.cc index 1c79e0c438..7aadb9183f 100644 --- a/libs/ardour/port.cc +++ b/libs/ardour/port.cc @@ -296,8 +296,8 @@ PortFacade::disconnect (Port& other) int PortFacade::disconnect_all () { - int reta; - int retb; + int reta = 0; + int retb = 0; if (_ext_port) { reta = _ext_port->disconnect_all (); diff --git a/libs/ardour/quantize.cc b/libs/ardour/quantize.cc index de3ed4ef22..ccbda9711a 100644 --- a/libs/ardour/quantize.cc +++ b/libs/ardour/quantize.cc @@ -71,9 +71,9 @@ Quantize::run (boost::shared_ptr r) for (MidiModel::Notes::iterator i = model->notes().begin(); i != model->notes().end(); ++i) { const double new_time = lrint((*i)->time() / q_frames) * q_frames; - const double new_dur = (((*i)->time() != 0 && new_dur < (q_frames * 1.5)) - ? q_frames - : lrint((*i)->duration() / q_frames) * q_frames); + double new_dur = lrint((*i)->duration() / q_frames) * q_frames; + if (new_dur == 0.0) + new_dur = q_frames; (*i)->set_time(new_time); (*i)->set_duration(new_dur); diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc index 6d92c0bc88..054e85cd2f 100644 --- a/libs/ardour/region.cc +++ b/libs/ardour/region.cc @@ -99,7 +99,7 @@ Region::Region (boost::shared_ptr src, nframes_t start, nframes_t length } /** Basic Region constructor (many sources) */ -Region::Region (SourceList& srcs, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags) +Region::Region (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags) : Automatable(srcs.front()->session(), name) , _type(type) , _flags(flags) @@ -117,13 +117,13 @@ Region::Region (SourceList& srcs, nframes_t start, nframes_t length, const strin set > unique_srcs; - 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); (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i))); unique_srcs.insert (*i); } - for (SourceList::iterator i = srcs.begin(); i != srcs.end(); ++i) { + for (SourceList::const_iterator i = srcs.begin(); i != srcs.end(); ++i) { _master_sources.push_back (*i); if (unique_srcs.find (*i) == unique_srcs.end()) { (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i))); @@ -222,7 +222,7 @@ Region::Region (boost::shared_ptr other) assert(_sources.size() > 0); } -Region::Region (SourceList& srcs, const XMLNode& node) +Region::Region (const SourceList& srcs, const XMLNode& node) : Automatable(srcs.front()->session(), X_("error: XML did not reset this")) , _type(DataType::NIL) // to be loaded from XML , _flags(Flag(0)) @@ -239,13 +239,13 @@ Region::Region (SourceList& srcs, const XMLNode& node) { set > unique_srcs; - 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); (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i))); unique_srcs.insert (*i); } - for (SourceList::iterator i = srcs.begin(); i != srcs.end(); ++i) { + for (SourceList::const_iterator i = srcs.begin(); i != srcs.end(); ++i) { _master_sources.push_back (*i); if (unique_srcs.find (*i) == unique_srcs.end()) { (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i))); @@ -974,9 +974,9 @@ Region::state (bool full_state) node->add_property ("length", buf); snprintf (buf, sizeof (buf), "%u", _position); node->add_property ("position", buf); - snprintf (buf, sizeof (buf), "%lu", _ancestral_start); + snprintf (buf, sizeof (buf), "%Ld", _ancestral_start); node->add_property ("ancestral-start", buf); - snprintf (buf, sizeof (buf), "%lu", _ancestral_length); + snprintf (buf, sizeof (buf), "%Ld", _ancestral_length); node->add_property ("ancestral-length", buf); snprintf (buf, sizeof (buf), "%.12g", _stretch); node->add_property ("stretch", buf); diff --git a/libs/ardour/region_factory.cc b/libs/ardour/region_factory.cc index a0aa3be759..bd4b0873a7 100644 --- a/libs/ardour/region_factory.cc +++ b/libs/ardour/region_factory.cc @@ -106,7 +106,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) { if (srcs.empty()) { return boost::shared_ptr(); diff --git a/libs/ardour/resampled_source.cc b/libs/ardour/resampled_source.cc index 8330196d8a..b5d23fb4a2 100644 --- a/libs/ardour/resampled_source.cc +++ b/libs/ardour/resampled_source.cc @@ -38,7 +38,7 @@ ResampledImportableSource::ResampledImportableSource (const std::string& path, /* Initialize the sample rate converter. */ - int src_type; + int src_type = SRC_LINEAR; switch (srcq) { case SrcBest: diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index dbec881b0b..c6ace87b73 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -148,7 +149,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 4b7e0875ee..d18b9cedd7 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -432,19 +432,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 = session_directory().peak_path().to_string(); 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; } @@ -465,17 +460,39 @@ Session::create (bool& new_session, const string& mix_template, nframes_t initia dir = session_directory().dead_sound_path().to_string(); 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 = session_directory().export_path().to_string(); 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 */ @@ -524,7 +541,6 @@ Session::create (bool& new_session, const string& mix_template, nframes_t initia _state_of_the_state = Clean; - save_state (""); return 0; @@ -1979,6 +1995,14 @@ Session::automation_dir () const return res; } +string +Session::analysis_dir () const +{ + string res = _path; + res += "analysis/"; + return res; +} + int Session::load_bundles (XMLNode const & node) { @@ -2544,7 +2568,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/source_factory.cc b/libs/ardour/source_factory.cc index 661beef40b..02c35d2188 100644 --- a/libs/ardour/source_factory.cc +++ b/libs/ardour/source_factory.cc @@ -40,7 +40,7 @@ using namespace PBD; sigc::signal > SourceFactory::SourceCreated; Glib::Cond* SourceFactory::PeaksToBuild; -Glib::StaticMutex SourceFactory::peak_building_lock; +Glib::StaticMutex SourceFactory::peak_building_lock = GLIBMM_STATIC_MUTEX_INIT; std::list > SourceFactory::files_with_peaks; static void diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index 780f5c6a5d..b2865fc399 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -268,7 +268,7 @@ TempoMap::move_metric_section (MetricSection& section, const BBT_Time& when) section.set_start (corrected); metrics->sort (cmp); - timestamp_metrics (); + timestamp_metrics (true); return 0; } @@ -345,16 +345,22 @@ TempoMap::remove_meter (const MeterSection& tempo) } void -TempoMap::do_insert (MetricSection* section) +TempoMap::do_insert (MetricSection* section, bool with_bbt) { Metrics::iterator i; for (i = metrics->begin(); i != metrics->end(); ++i) { - if ((*i)->start() < section->start()) { - continue; + if (with_bbt) { + if ((*i)->start() < section->start()) { + continue; + } + } else { + if ((*i)->frame() < section->frame()) { + continue; + } } - + metrics->insert (i, section); break; } @@ -363,7 +369,7 @@ TempoMap::do_insert (MetricSection* section) metrics->insert (metrics->end(), section); } - timestamp_metrics (); + timestamp_metrics (with_bbt); } void @@ -376,7 +382,18 @@ TempoMap::add_tempo (const Tempo& tempo, BBT_Time where) where.ticks = 0; - do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type())); + do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), true); + } + + StateChanged (Change (0)); +} + +void +TempoMap::add_tempo (const Tempo& tempo, nframes_t where) +{ + { + Glib::RWLock::WriterLock lm (lock); + do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), false); } StateChanged (Change (0)); @@ -399,7 +416,7 @@ TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement) *((Tempo *) ts) = replacement; replaced = true; - timestamp_metrics (); + timestamp_metrics (true); break; } } @@ -432,7 +449,18 @@ TempoMap::add_meter (const Meter& meter, BBT_Time where) where.ticks = 0; - do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor())); + do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), true); + } + + StateChanged (Change (0)); +} + +void +TempoMap::add_meter (const Meter& meter, nframes_t where) +{ + { + Glib::RWLock::WriterLock lm (lock); + do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), false); } StateChanged (Change (0)); @@ -454,7 +482,7 @@ TempoMap::replace_meter (MeterSection& existing, const Meter& replacement) *((Meter*) ms) = replacement; replaced = true; - timestamp_metrics (); + timestamp_metrics (true); break; } } @@ -465,6 +493,49 @@ TempoMap::replace_meter (MeterSection& existing, const Meter& replacement) } } +void +TempoMap::change_existing_tempo_at (nframes_t where, double beats_per_minute, double note_type) +{ + Tempo newtempo (beats_per_minute, note_type); + + TempoSection* prev; + TempoSection* first; + Metrics::iterator i; + + /* find the TempoSection immediately preceding "where" + */ + + for (first = 0, i = metrics->begin(), prev = 0; i != metrics->end(); ++i) { + + if ((*i)->frame() > where) { + break; + } + + TempoSection* t; + + if ((t = dynamic_cast(*i)) != 0) { + if (!first) { + first = t; + } + prev = t; + } + } + + if (!prev) { + if (!first) { + error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg; + return; + } + + prev = first; + } + + /* reset */ + + *((Tempo*)prev) = newtempo; + StateChanged (Change (0)); +} + const MeterSection& TempoMap::first_meter () const { @@ -498,43 +569,84 @@ TempoMap::first_tempo () const } void -TempoMap::timestamp_metrics () +TempoMap::timestamp_metrics (bool use_bbt) { Metrics::iterator i; const Meter* meter; const Tempo* tempo; Meter *m; Tempo *t; - nframes_t current; - nframes_t section_frames; - BBT_Time start; - BBT_Time end; meter = &first_meter (); tempo = &first_tempo (); - current = 0; - for (i = metrics->begin(); i != metrics->end(); ++i) { - - end = (*i)->start(); + if (use_bbt) { - section_frames = count_frames_between_metrics (*meter, *tempo, start, end); + nframes_t current = 0; + nframes_t section_frames; + BBT_Time start; + BBT_Time end; - current += section_frames; + for (i = metrics->begin(); i != metrics->end(); ++i) { + + end = (*i)->start(); + + section_frames = count_frames_between_metrics (*meter, *tempo, start, end); + + current += section_frames; + + start = end; + + (*i)->set_frame (current); + + if ((t = dynamic_cast(*i)) != 0) { + tempo = t; + } else if ((m = dynamic_cast(*i)) != 0) { + meter = m; + } else { + fatal << _("programming error: unhandled MetricSection type") << endmsg; + /*NOTREACHED*/ + } + } - start = end; + } else { - (*i)->set_frame (current); + bool first = true; - if ((t = dynamic_cast(*i)) != 0) { - tempo = t; - } else if ((m = dynamic_cast(*i)) != 0) { - meter = m; - } else { - fatal << _("programming error: unhandled MetricSection type") << endmsg; - /*NOTREACHED*/ + for (i = metrics->begin(); i != metrics->end(); ++i) { + + BBT_Time bbt; + + bbt_time_with_metric ((*i)->frame(), bbt, Metric (*meter, *tempo)); + + // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => "; + + if (first) { + first = false; + } else { + if (bbt.beats != 1 || bbt.ticks != 0) { + bbt.bars += 1; + bbt.beats = 1; + bbt.ticks = 0; + } + } + + // cerr << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << endl; + + (*i)->set_start (bbt); + + if ((t = dynamic_cast(*i)) != 0) { + tempo = t; + } else if ((m = dynamic_cast(*i)) != 0) { + meter = m; + } else { + fatal << _("programming error: unhandled MetricSection type") << endmsg; + /*NOTREACHED*/ + } } } + + // dump (cerr); } TempoMap::Metric @@ -666,17 +778,15 @@ TempoMap::bbt_time_with_metric (nframes_t frame, BBT_Time& bbt, const Metric& me nframes_t TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) const { - - /* for this to work with fractional measure types, start and end have to "legal" BBT types, - that means that the beats and ticks should be inside a bar + /* for this to work with fractional measure types, start and end have to be "legal" BBT types, + that means that the beats and ticks should be inside a bar */ - nframes_t frames = 0; nframes_t start_frame = 0; nframes_t end_frame = 0; - Metric m = metric_at(start); + Metric m = metric_at (start); uint32_t bar_offset = start.bars - m.start().bars; @@ -939,67 +1049,6 @@ TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num) } return frame_time (the_beat); - - - - /***************************** - XXX just keeping this for reference - - TempoMap::BBTPointList::iterator i; - TempoMap::BBTPointList *more_zoomed_bbt_points; - nframes_t frame_one_beats_worth; - nframes_t pos = 0; - nframes_t next_pos = 0 ; - double tempo = 1; - double frames_one_subdivisions_worth; - bool fr_has_changed = false; - - int n; - - frame_one_beats_worth = (nframes_t) ::floor ((double) _frame_rate * 60 / 20 ); //one beat @ 20 bpm - { - Glib::RWLock::ReaderLock lm (lock); - more_zoomed_bbt_points = get_points((fr >= frame_one_beats_worth) ? - fr - frame_one_beats_worth : 0, fr+frame_one_beats_worth ); - } - if (more_zoomed_bbt_points == 0 || more_zoomed_bbt_points->empty()) { - return fr; - } - - for (i = more_zoomed_bbt_points->begin(); i != more_zoomed_bbt_points->end(); i++) { - if ((*i).frame <= fr) { - pos = (*i).frame; - tempo = (*i).tempo->beats_per_minute(); - - } else { - i++; - next_pos = (*i).frame; - break; - } - } - frames_one_subdivisions_worth = ((double) _frame_rate * 60 / (sub_num * tempo)); - - for (n = sub_num; n > 0; n--) { - if (fr >= (pos + ((n - 0.5) * frames_one_subdivisions_worth))) { - fr = (nframes_t) round(pos + (n * frames_one_subdivisions_worth)); - if (fr > next_pos) { - fr = next_pos; //take care of fractional beats that don't match the subdivision asked - } - fr_has_changed = true; - break; - } - } - - if (!fr_has_changed) { - fr = pos; - } - - delete more_zoomed_bbt_points; - return fr ; - - ******************************/ - - } nframes_t @@ -1051,6 +1100,12 @@ TempoMap::round_to_type (nframes_t frame, int dir, BBTPointType type) } + /* + cerr << "for " << frame << " round to " << bbt << " using " + << metric.start() + << endl; + */ + return metric.frame() + count_frames_between (metric.start(), bbt); } @@ -1148,6 +1203,7 @@ TempoMap::get_points (nframes_t lower, nframes_t upper) const if (beat == 1) { if (current >= lower) { + // cerr << "Add Bar at " << bar << "|1" << " @ " << current << endl; points->push_back (BBTPoint (*meter, *tempo,(nframes_t)rint(current), Bar, bar, 1)); } @@ -1159,6 +1215,7 @@ TempoMap::get_points (nframes_t lower, nframes_t upper) const while (beat <= ceil( beats_per_bar) && beat_frame < limit) { if (beat_frame >= lower) { + // cerr << "Add Beat at " << bar << '|' << beat << " @ " << beat_frame << endl; points->push_back (BBTPoint (*meter, *tempo, (nframes_t) rint(beat_frame), Beat, bar, beat)); } beat_frame += beat_frames; @@ -1167,7 +1224,7 @@ TempoMap::get_points (nframes_t lower, nframes_t upper) const beat++; } - if (beat > ceil(beats_per_bar) ) { + if (beat > ceil(beats_per_bar) || i != metrics->end()) { /* we walked an entire bar. its important to move `current' forward @@ -1185,10 +1242,15 @@ TempoMap::get_points (nframes_t lower, nframes_t upper) const so we subtract the possible extra fraction from the current */ - current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar); + if (beat > ceil (beats_per_bar)) { + /* next bar goes where the numbers suggest */ + current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar); + } else { + /* next bar goes where the next metric is */ + current = limit; + } bar++; beat = 1; - } } @@ -1225,6 +1287,33 @@ TempoMap::get_points (nframes_t lower, nframes_t upper) const return points; } +const TempoSection& +TempoMap::tempo_section_at (nframes_t frame) +{ + Glib::RWLock::ReaderLock lm (lock); + Metrics::iterator i; + TempoSection* prev = 0; + + for (i = metrics->begin(); i != metrics->end(); ++i) { + TempoSection* t; + + if ((t = dynamic_cast (*i)) != 0) { + + if ((*i)->frame() > frame) { + break; + } + + prev = t; + } + } + + if (prev == 0) { + fatal << endmsg; + } + + return *prev; +} + const Tempo& TempoMap::tempo_at (nframes_t frame) { @@ -1303,7 +1392,7 @@ TempoMap::set_state (const XMLNode& node) MetricSectionSorter cmp; metrics->sort (cmp); - timestamp_metrics (); + timestamp_metrics (true); } } diff --git a/libs/ardour/transient_detector.cc b/libs/ardour/transient_detector.cc new file mode 100644 index 0000000000..b85700dd90 --- /dev/null +++ b/libs/ardour/transient_detector.cc @@ -0,0 +1,61 @@ +#include + +#include "i18n.h" + +using namespace Vamp; +using namespace ARDOUR; +using namespace std; + +TransientDetector::TransientDetector (float sr) + : AudioAnalyser (sr, X_("libardourvampplugins:percussiononsets")) +{ +} + +TransientDetector::~TransientDetector() +{ +} + +int +TransientDetector::run (const std::string& path, Readable* 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, (nframes_t) floor(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); + } +} -- cgit v1.2.3