diff options
Diffstat (limited to 'libs/ardour')
32 files changed, 854 insertions, 166 deletions
diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript index 5b4f0cda87..c4f8eece0f 100644 --- a/libs/ardour/SConscript +++ b/libs/ardour/SConscript @@ -28,6 +28,7 @@ ardour.Append(CPPPATH = '#libs/surfaces/control_protocol') ardour_files=Split(""" diskstream.cc +analyser.cc audioanalyser.cc audio_diskstream.cc audio_library.cc diff --git a/libs/ardour/analyser.cc b/libs/ardour/analyser.cc new file mode 100644 index 0000000000..7ddb5428e9 --- /dev/null +++ b/libs/ardour/analyser.cc @@ -0,0 +1,119 @@ +/* + 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. + +*/ + +#include <ardour/analyser.h> +#include <ardour/audiofilesource.h> +#include <ardour/transient_detector.h> + +#include <pbd/pthread_utils.h> +#include <pbd/convert.h> + +using namespace std; +using namespace sigc; +using namespace ARDOUR; +using namespace PBD; + +Analyser* Analyser::the_analyser = 0; +Glib::StaticMutex Analyser::analysis_queue_lock = GLIBMM_STATIC_MUTEX_INIT; +Glib::Cond* Analyser::SourcesToAnalyse = 0; +list<boost::weak_ptr<Source> > Analyser::analysis_queue; + +Analyser::Analyser () +{ + +} + +Analyser::~Analyser () +{ +} + +static void +analyser_work () +{ + Analyser::work (); +} + +void +Analyser::init () +{ + SourcesToAnalyse = new Glib::Cond(); + Glib::Thread::create (sigc::ptr_fun (analyser_work), false); +} + +void +Analyser::queue_source_for_analysis (boost::shared_ptr<Source> src, bool force) +{ + if (!src->can_be_analysed()) { + return; + } + + if (!force && src->has_been_analysed()) { + return; + } + + Glib::Mutex::Lock lm (analysis_queue_lock); + analysis_queue.push_back (boost::weak_ptr<Source>(src)); + SourcesToAnalyse->broadcast (); +} + +void +Analyser::work () +{ + PBD::ThreadCreated (pthread_self(), string ("analyser-") + to_string (pthread_self(), std::dec)); + + while (true) { + analysis_queue_lock.lock (); + + wait: + if (analysis_queue.empty()) { + SourcesToAnalyse->wait (analysis_queue_lock); + } + + if (analysis_queue.empty()) { + goto wait; + } + + boost::shared_ptr<Source> src (analysis_queue.front().lock()); + analysis_queue.pop_front(); + analysis_queue_lock.unlock (); + + boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src); + + if (afs) { + analyse_audio_file_source (afs); + } + } +} + +void +Analyser::analyse_audio_file_source (boost::shared_ptr<AudioFileSource> src) +{ + AnalysisFeatureList results; + + TransientDetector td (src->sample_rate()); + + if (td.run (src->get_transients_path(), src.get(), 0, results) == 0) { + src->set_been_analysed (true); + } else { + src->set_been_analysed (false); + } + +} + + diff --git a/libs/ardour/ardour/analyser.h b/libs/ardour/ardour/analyser.h new file mode 100644 index 0000000000..8771cab6b0 --- /dev/null +++ b/libs/ardour/ardour/analyser.h @@ -0,0 +1,35 @@ +#ifndef __ardour_analyser_h__ +#define __ardour_analyser_h__ + +#include <glibmm/thread.h> +#include <boost/shared_ptr.hpp> + +namespace ARDOUR { + +class AudioFileSource; +class Source; +class TransientDetector; + +class Analyser { + + public: + Analyser(); + ~Analyser (); + + static void init (); + static void queue_source_for_analysis (boost::shared_ptr<Source>, bool force); + static void work (); + + private: + static Analyser* the_analyser; + static Glib::StaticMutex analysis_queue_lock; + static Glib::Cond* SourcesToAnalyse; + static std::list<boost::weak_ptr<Source> > analysis_queue; + + static void analyse_audio_file_source (boost::shared_ptr<AudioFileSource>); +}; + + +} + +#endif /* __ardour_analyser_h__ */ diff --git a/libs/ardour/ardour/audioanalyser.h b/libs/ardour/ardour/audioanalyser.h index dbd8a52d5a..06b841990a 100644 --- a/libs/ardour/ardour/audioanalyser.h +++ b/libs/ardour/ardour/audioanalyser.h @@ -40,7 +40,7 @@ class AudioAnalyser { 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 diff --git a/libs/ardour/ardour/audiofilesource.h b/libs/ardour/ardour/audiofilesource.h index 22359d9149..c339c1cfc6 100644 --- a/libs/ardour/ardour/audiofilesource.h +++ b/libs/ardour/ardour/audiofilesource.h @@ -123,6 +123,8 @@ class AudioFileSource : public AudioSource { virtual void handle_header_position_change () {} + bool can_be_analysed() const { return _length > 0; } + protected: /* constructor to be called for existing external-to-session files */ diff --git a/libs/ardour/ardour/audioregion.h b/libs/ardour/ardour/audioregion.h index ba14d05afe..c5d8618a1c 100644 --- a/libs/ardour/ardour/audioregion.h +++ b/libs/ardour/ardour/audioregion.h @@ -148,7 +148,7 @@ class AudioRegion : public Region void set_playlist (boost::weak_ptr<Playlist>); - int get_transients (std::vector<nframes64_t>&, bool force_new = false); + int get_transients (AnalysisFeatureList&, bool force_new = false); private: friend class RegionFactory; @@ -188,6 +188,7 @@ class AudioRegion : public Region void fade_out_changed (); void source_offset_changed (); void listen_to_my_curves (); + void listen_to_my_sources (); void source_deleted (); @@ -207,11 +208,6 @@ class AudioRegion : public Region protected: int set_live_state (const XMLNode&, Change&, bool send); - - std::vector<nframes64_t> _transients; - bool valid_transients; - void invalidate_transients (); - void cleanup_transients (std::vector<nframes64_t>&); }; } /* namespace ARDOUR */ diff --git a/libs/ardour/ardour/audiosource.h b/libs/ardour/ardour/audiosource.h index e8ef3a0fa2..20acd9fcd8 100644 --- a/libs/ardour/ardour/audiosource.h +++ b/libs/ardour/ardour/audiosource.h @@ -122,19 +122,23 @@ class AudioSource : public Source, public boost::enable_shared_from_this<ARDOUR: int prepare_for_peakfile_writes (); void done_with_peakfile_writes (bool done = true); - std::vector<nframes64_t> transients; + AnalysisFeatureList transients; std::string get_transients_path() const; + void set_been_analysed (bool yn); + bool check_for_analysis_data_on_disk (); + protected: static bool _build_missing_peakfiles; static bool _build_peakfiles; bool _peaks_built; + bool _analysed; mutable Glib::Mutex _lock; mutable Glib::Mutex _peaks_ready_lock; nframes_t _length; - Glib::ustring peakpath; - Glib::ustring _captured_for; + Glib::ustring peakpath; + Glib::ustring _captured_for; mutable uint32_t _read_data_count; // modified in read() mutable uint32_t _write_data_count; // modified in write() diff --git a/libs/ardour/ardour/configuration_vars.h b/libs/ardour/ardour/configuration_vars.h index 81d04abfe3..cb045091ef 100644 --- a/libs/ardour/ardour/configuration_vars.h +++ b/libs/ardour/ardour/configuration_vars.h @@ -53,6 +53,7 @@ CONFIG_VARIABLE (float, track_buffer_seconds, "track-buffer-seconds", 5.0) CONFIG_VARIABLE (uint32_t, disk_choice_space_threshold, "disk-choice-space-threshold", 57600000) CONFIG_VARIABLE (SampleFormat, native_file_data_format, "native-file-data-format", ARDOUR::FormatFloat) CONFIG_VARIABLE (HeaderFormat, native_file_header_format, "native-file-header-format", ARDOUR::WAVE) +CONFIG_VARIABLE (bool, auto_analyse_audio, "auto-analyse-audio", true) /* OSC */ @@ -118,6 +119,7 @@ CONFIG_VARIABLE (bool, quieten_at_speed, "quieten-at-speed", true) CONFIG_VARIABLE (bool, primary_clock_delta_edit_cursor, "primary-clock-delta-edit-cursor", false) CONFIG_VARIABLE (bool, secondary_clock_delta_edit_cursor, "secondary-clock-delta-edit-cursor", false) CONFIG_VARIABLE (bool, show_track_meters, "show-track-meters", true) + /* timecode and sync */ CONFIG_VARIABLE (bool, jack_time_master, "jack-time-master", true) diff --git a/libs/ardour/ardour/playlist.h b/libs/ardour/ardour/playlist.h index 402e02109a..c80330c04a 100644 --- a/libs/ardour/ardour/playlist.h +++ b/libs/ardour/ardour/playlist.h @@ -93,6 +93,7 @@ class Playlist : public PBD::StatefulDestructible, public boost::enable_shared_f void duplicate (boost::shared_ptr<Region>, nframes_t position, float times); void nudge_after (nframes_t start, nframes_t distance, bool forwards); void shuffle (boost::shared_ptr<Region>, int dir); + void update_after_tempo_map_change (); boost::shared_ptr<Playlist> cut (list<AudioRange>&, bool result_is_hidden = true); boost::shared_ptr<Playlist> copy (list<AudioRange>&, bool result_is_hidden = true); @@ -107,6 +108,8 @@ class Playlist : public PBD::StatefulDestructible, public boost::enable_shared_f nframes64_t find_next_region_boundary (nframes64_t frame, int dir); bool region_is_shuffle_constrained (boost::shared_ptr<Region>); + nframes64_t find_next_transient (nframes64_t position, int dir); + template<class T> void foreach_region (T *t, void (T::*func)(boost::shared_ptr<Region>, void *), void *arg); template<class T> void foreach_region (T *t, void (T::*func)(boost::shared_ptr<Region>)); diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h index e75fb43acc..19600f3db6 100644 --- a/libs/ardour/ardour/region.h +++ b/libs/ardour/ardour/region.h @@ -67,6 +67,11 @@ class Region : public PBD::StatefulDestructible, public Readable, public boost:: range_guarantoor = USHRT_MAX }; + enum PositionLockStyle { + AudioTime, + MusicTime + }; + static const Flag DefaultFlags = Flag (Opaque|DefaultFadeIn|DefaultFadeOut|FadeIn|FadeOut); static Change FadeChanged; @@ -121,6 +126,9 @@ class Region : public PBD::StatefulDestructible, public Readable, public boost:: bool whole_file() const { return _flags & WholeFile ; } Flag flags() const { return _flags; } + PositionLockStyle positional_lock_style() const { return _positional_lock_style; } + void set_position_lock_style (PositionLockStyle ps); + virtual bool should_save_state () const { return !(_flags & DoNotSaveState); }; void freeze (); @@ -149,6 +157,7 @@ class Region : public PBD::StatefulDestructible, public Readable, public boost:: void set_position (nframes_t, void *src); void set_position_on_top (nframes_t, void *src); void special_set_position (nframes_t); + void update_position_after_tempo_map_change (); void nudge_position (nframes64_t, void *src); bool at_natural_position () const; @@ -189,6 +198,13 @@ class Region : public PBD::StatefulDestructible, public Readable, public boost:: uint64_t last_layer_op() const { return _last_layer_op; } void set_last_layer_op (uint64_t when); + virtual int get_transients (AnalysisFeatureList&, bool force_new = false) { + // no transients, but its OK + return 0; + } + + void invalidate_transients (); + protected: friend class RegionFactory; @@ -198,13 +214,13 @@ class Region : public PBD::StatefulDestructible, public Readable, public boost:: Region (boost::shared_ptr<const Region>); Region (const XMLNode&); - protected: XMLNode& get_short_state (); /* used only by Session */ void send_change (Change); void trim_to_internal (nframes_t position, nframes_t length, void *src); + void set_position_internal (nframes_t pos, bool allow_bbt_recompute); bool copied() const { return _flags & Copied; } void maybe_uncopy (); @@ -217,27 +233,30 @@ class Region : public PBD::StatefulDestructible, public Readable, public boost:: virtual void recompute_at_start () = 0; virtual void recompute_at_end () = 0; - - nframes_t _start; - nframes_t _length; - nframes_t _last_length; - nframes_t _position; - nframes_t _last_position; - Flag _flags; - nframes_t _sync_position; - layer_t _layer; - string _name; - mutable RegionEditState _first_edit; - int _frozen; - Glib::Mutex lock; + nframes_t _start; + nframes_t _length; + nframes_t _last_length; + nframes_t _position; + nframes_t _last_position; + Flag _flags; + PositionLockStyle _positional_lock_style; + nframes_t _sync_position; + layer_t _layer; + string _name; + mutable RegionEditState _first_edit; + int _frozen; + Glib::Mutex lock; boost::weak_ptr<ARDOUR::Playlist> _playlist; - mutable uint32_t _read_data_count; // modified in read() - Change pending_changed; - uint64_t _last_layer_op; // timestamp - nframes64_t _ancestral_start; - nframes64_t _ancestral_length; - float _stretch; - float _shift; + mutable uint32_t _read_data_count; // modified in read() + Change pending_changed; + uint64_t _last_layer_op; // timestamp + nframes64_t _ancestral_start; + nframes64_t _ancestral_length; + float _stretch; + float _shift; + BBT_Time _bbt_time; + AnalysisFeatureList _transients; + bool valid_transients; }; } /* namespace ARDOUR */ diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index acb9f5f53f..c61137b4f3 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -381,7 +381,10 @@ class Session : public PBD::StatefulDestructible nframes_t get_maximum_extent () const; nframes_t current_end_frame() const { return end_location->start(); } nframes_t current_start_frame() const { return start_location->start(); } + // "actual" sample rate of session, set by current audioengine rate, pullup/down etc. nframes_t frame_rate() const { return _current_frame_rate; } + // "native" sample rate of session, regardless of current audioengine rate, pullup/down etc + nframes_t nominal_frame_rate() const { return _nominal_frame_rate; } nframes_t frames_per_hour() const { return _frames_per_hour; } double frames_per_smpte_frame() const { return _frames_per_smpte_frame; } @@ -615,6 +618,12 @@ class Session : public PBD::StatefulDestructible sigc::signal<int,boost::shared_ptr<ARDOUR::Playlist> > AskAboutPlaylistDeletion; + /* handlers should return 0 for "ignore the rate mismatch" + and !0 for "do not use this session" + */ + + static sigc::signal<int,nframes_t, nframes_t> AskAboutSampleRateMismatch; + /* handlers should return !0 for use pending state, 0 for ignore it. */ @@ -983,6 +992,7 @@ class Session : public PBD::StatefulDestructible bool waiting_for_sync_offset; nframes_t _base_frame_rate; nframes_t _current_frame_rate; //this includes video pullup offset + nframes_t _nominal_frame_rate; //ignores audioengine setting, "native" SR int transport_sub_state; mutable gint _record_status; volatile nframes_t _transport_frame; diff --git a/libs/ardour/ardour/silentfilesource.h b/libs/ardour/ardour/silentfilesource.h index e0103185c2..87065c1cd0 100644 --- a/libs/ardour/ardour/silentfilesource.h +++ b/libs/ardour/ardour/silentfilesource.h @@ -35,6 +35,7 @@ class SilentFileSource : public AudioFileSource { void set_length (nframes_t len); bool destructive() const { return false; } + bool can_be_analysed() const { return false; } protected: diff --git a/libs/ardour/ardour/source.h b/libs/ardour/ardour/source.h index 6e6561f22f..aa9931eae3 100644 --- a/libs/ardour/ardour/source.h +++ b/libs/ardour/ardour/source.h @@ -59,12 +59,21 @@ class Source : public PBD::StatefulDestructible, public ARDOUR::Readable uint32_t used() const; - protected: - Session& _session; - string _name; - time_t _timestamp; + bool has_been_analysed() const; + virtual bool can_be_analysed() const { return false; } + virtual void set_been_analysed (bool yn); + virtual bool check_for_analysis_data_on_disk () { return false; } + + sigc::signal<void> AnalysisChanged; - Glib::Mutex playlist_lock; + protected: + Session& _session; + string _name; + time_t _timestamp; + bool _analysed; + mutable Glib::Mutex _analysis_lock; + Glib::Mutex playlist_lock; + typedef std::map<boost::shared_ptr<ARDOUR::Playlist>, uint32_t > PlaylistMap; PlaylistMap _playlists; diff --git a/libs/ardour/ardour/tempo.h b/libs/ardour/ardour/tempo.h index fa1d882ab3..c4915072c5 100644 --- a/libs/ardour/ardour/tempo.h +++ b/libs/ardour/ardour/tempo.h @@ -203,7 +203,7 @@ class TempoMap : public PBD::StatefulDestructible BBTPointList *get_points (nframes_t start, nframes_t end) const; - void bbt_time (nframes_t when, BBT_Time&) const; + void bbt_time (nframes_t when, BBT_Time&) const; nframes_t frame_time (const BBT_Time&) const; nframes_t bbt_duration_at (nframes_t, const BBT_Time&, int dir) const; @@ -279,6 +279,10 @@ class TempoMap : public PBD::StatefulDestructible void bbt_time_with_metric (nframes_t, BBT_Time&, const Metric&) const; void change_existing_tempo_at (nframes_t, double bpm, double note_type); + void change_initial_tempo (double bpm, double note_type); + + int n_tempos () const; + int n_meters () const; sigc::signal<void,ARDOUR::Change> StateChanged; @@ -286,12 +290,12 @@ class TempoMap : public PBD::StatefulDestructible static Tempo _default_tempo; static Meter _default_meter; - Metrics *metrics; - nframes_t _frame_rate; - nframes_t last_bbt_when; - bool last_bbt_valid; - BBT_Time last_bbt; - mutable Glib::RWLock lock; + Metrics* metrics; + nframes_t _frame_rate; + nframes_t last_bbt_when; + bool last_bbt_valid; + BBT_Time last_bbt; + mutable Glib::RWLock lock; void timestamp_metrics (bool use_bbt); diff --git a/libs/ardour/ardour/transient_detector.h b/libs/ardour/ardour/transient_detector.h index c65bae3ed5..259b79176f 100644 --- a/libs/ardour/ardour/transient_detector.h +++ b/libs/ardour/ardour/transient_detector.h @@ -34,17 +34,23 @@ class TransientDetector : public AudioAnalyser TransientDetector (float sample_rate); ~TransientDetector(); + static std::string operational_identifier(); + 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<nframes64_t>& results); + int run (const std::string& path, Readable*, uint32_t channel, AnalysisFeatureList& results); - protected: - std::vector<nframes64_t>* current_results; + static void cleanup_transients (AnalysisFeatureList&, float sr, float gap_msecs); + + protected: + AnalysisFeatureList* current_results; int use_features (Vamp::Plugin::FeatureSet&, std::ostream*); + + static std::string _op_id; }; } /* namespace */ diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index 933ca44e73..5c88acfcad 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -380,6 +380,8 @@ namespace ARDOUR { int opts; // really RubberBandStretcher::Options }; + typedef std::list<nframes64_t> AnalysisFeatureList; + } // namespace ARDOUR std::istream& operator>>(std::istream& o, ARDOUR::SampleFormat& sf); diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc index 8b02af2101..b44753ee96 100644 --- a/libs/ardour/audio_diskstream.cc +++ b/libs/ardour/audio_diskstream.cc @@ -40,6 +40,7 @@ #include <ardour/ardour.h> #include <ardour/audioengine.h> +#include <ardour/analyser.h> #include <ardour/audio_diskstream.h> #include <ardour/utils.h> #include <ardour/configuration.h> @@ -1597,6 +1598,7 @@ AudioDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_ca s->update_header (capture_info.front()->start, when, twhen); s->set_captured_for (_name); s->mark_immutable (); + Analyser::queue_source_for_analysis (s, true); } } diff --git a/libs/ardour/audioanalyser.cc b/libs/ardour/audioanalyser.cc index 4cc99a5d5e..9da404e5bf 100644 --- a/libs/ardour/audioanalyser.cc +++ b/libs/ardour/audioanalyser.cc @@ -18,13 +18,19 @@ using namespace ARDOUR; AudioAnalyser::AudioAnalyser (float sr, AnalysisPluginKey key) : sample_rate (sr) - , plugin (0) , plugin_key (key) { + /* create VAMP plugin and initialize */ + + if (initialize_plugin (plugin_key, sample_rate)) { + error << string_compose (_("cannot load VAMP plugin \"%1\""), key) << endmsg; + throw failed_constructor(); + } } AudioAnalyser::~AudioAnalyser () { + delete plugin; } int @@ -73,29 +79,28 @@ int AudioAnalyser::analyse (const string& path, Readable* src, uint32_t channel) { ofstream ofile; - Plugin::FeatureSet onsets; + Plugin::FeatureSet features; int ret = -1; bool done = false; Sample* data = 0; nframes64_t len = src->readable_length(); nframes64_t pos = 0; float* bufs[1] = { 0 }; + string tmp_path; if (!path.empty()) { - ofile.open (path.c_str()); + + /* store data in tmp file, not the real one */ + + tmp_path = path; + tmp_path += ".tmp"; + + ofile.open (tmp_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; @@ -108,7 +113,6 @@ AudioAnalyser::analyse (const string& path, Readable* src, uint32_t channel) to_read = min ((len - pos), bufsize); if (src->read (data, pos, to_read, channel) != to_read) { - cerr << "bad read\n"; goto out; } @@ -118,14 +122,14 @@ AudioAnalyser::analyse (const string& path, Readable* src, uint32_t channel) memset (data + to_read, 0, (bufsize - to_read)); } - onsets = plugin->process (bufs, RealTime::fromSeconds ((double) pos / sample_rate)); + features = plugin->process (bufs, RealTime::fromSeconds ((double) pos / sample_rate)); - if (use_features (onsets, (path.empty() ? &ofile : 0))) { + if (use_features (features, (path.empty() ? 0 : &ofile))) { goto out; } pos += stepsize; - + if (pos >= len) { done = true; } @@ -133,9 +137,9 @@ AudioAnalyser::analyse (const string& path, Readable* src, uint32_t channel) /* finish up VAMP plugin */ - onsets = plugin->getRemainingFeatures (); + features = plugin->getRemainingFeatures (); - if (use_features (onsets, (path.empty() ? &ofile : 0))) { + if (use_features (features, (path.empty() ? &ofile : 0))) { goto out; } @@ -146,10 +150,14 @@ AudioAnalyser::analyse (const string& path, Readable* src, uint32_t channel) ofile.close (); if (ret) { - g_remove (path.c_str()); + g_remove (tmp_path.c_str()); + } else if (!path.empty()) { + /* move the data file to the requested path */ + g_rename (tmp_path.c_str(), path.c_str()); } + if (data) { - delete data; + delete [] data; } return ret; diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index 2337e51481..4c2a7a2f63 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -797,13 +797,16 @@ void AudioEngine::halted (void *arg) { AudioEngine* ae = static_cast<AudioEngine *> (arg); + bool was_running = ae->_running; ae->_running = false; ae->_buffer_size = 0; ae->_frame_rate = 0; ae->_jack = 0; - ae->Halted(); /* EMIT SIGNAL */ + if (was_running) { + ae->Halted(); /* EMIT SIGNAL */ + } } uint32_t diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index fd39e05636..4f9e70e334 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -80,12 +80,12 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, n } _scale_amplitude = 1.0; - valid_transients = false; set_default_fades (); set_default_envelope (); listen_to_my_curves (); + listen_to_my_sources (); } AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags) @@ -106,12 +106,12 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, n } _scale_amplitude = 1.0; - valid_transients = false; set_default_fades (); set_default_envelope (); listen_to_my_curves (); + listen_to_my_sources (); } AudioRegion::AudioRegion (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags) @@ -134,12 +134,12 @@ AudioRegion::AudioRegion (const SourceList& srcs, nframes_t start, nframes_t len } _scale_amplitude = 1.0; - valid_transients = false; set_default_fades (); set_default_envelope (); listen_to_my_curves (); + listen_to_my_sources (); } @@ -203,9 +203,9 @@ AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, nframes_t } _scale_amplitude = other->_scale_amplitude; - valid_transients = false; listen_to_my_curves (); + listen_to_my_sources (); } AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other) @@ -241,13 +241,13 @@ AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other) } _scale_amplitude = other->_scale_amplitude; - valid_transients = false; _envelope = other->_envelope; _fade_in_disabled = 0; _fade_out_disabled = 0; listen_to_my_curves (); + listen_to_my_sources (); } AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, const XMLNode& node) @@ -266,13 +266,13 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, const XMLNode& nod } set_default_fades (); - valid_transients = false; if (set_state (node)) { throw failed_constructor(); } listen_to_my_curves (); + listen_to_my_sources (); } AudioRegion::AudioRegion (SourceList& srcs, const XMLNode& node) @@ -307,13 +307,13 @@ AudioRegion::AudioRegion (SourceList& srcs, const XMLNode& node) set_default_fades (); _scale_amplitude = 1.0; - valid_transients = false; if (set_state (node)) { throw failed_constructor(); } listen_to_my_curves (); + listen_to_my_sources (); } AudioRegion::~AudioRegion () @@ -331,10 +331,11 @@ AudioRegion::~AudioRegion () } void -AudioRegion::invalidate_transients () +AudioRegion::listen_to_my_sources () { - valid_transients = false; - _transients.clear (); + for (SourceList::const_iterator i = sources.begin(); i != sources.end(); ++i) { + (*i)->AnalysisChanged.connect (mem_fun (*this, &AudioRegion::invalidate_transients)); + } } void @@ -1517,36 +1518,12 @@ AudioRegion::set_playlist (boost::weak_ptr<Playlist> wpl) } } -void -AudioRegion::cleanup_transients (vector<nframes64_t>& t) -{ - sort (t.begin(), t.end()); - - /* remove duplicates or other things that are too close */ - - vector<nframes64_t>::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<nframes64_t>& results, bool force_new) +AudioRegion::get_transients (AnalysisFeatureList& results, bool force_new) { - if (!playlist()) { + boost::shared_ptr<Playlist> pl = playlist(); + + if (!pl) { return -1; } @@ -1555,7 +1532,51 @@ AudioRegion::get_transients (vector<nframes64_t>& results, bool force_new) return 0; } - TransientDetector t (playlist()->session().frame_rate()); + SourceList::iterator s; + + for (s = sources.begin() ; s != sources.end(); ++s) { + if (!(*s)->has_been_analysed()) { + cerr << "For " << name() << " source " << (*s)->name() << " has not been analyzed\n"; + break; + } + } + + if (s == sources.end()) { + /* all sources are analyzed, merge data from each one */ + + for (s = sources.begin() ; s != sources.end(); ++s) { + + /* find the set of transients within the bounds of this region */ + + AnalysisFeatureList::iterator low = lower_bound ((*s)->transients.begin(), + (*s)->transients.end(), + _start); + + AnalysisFeatureList::iterator high = upper_bound ((*s)->transients.begin(), + (*s)->transients.end(), + _start + _length); + + /* and add them */ + + results.insert (results.end(), low, high); + } + + TransientDetector::cleanup_transients (results, pl->session().frame_rate(), 3.0); + + /* translate all transients to current position */ + + for (AnalysisFeatureList::iterator x = results.begin(); x != results.end(); ++x) { + (*x) -= _start; + (*x) += _position; + } + + _transients = results; + valid_transients = true; + + return 0; + } + + TransientDetector t (pl->session().frame_rate()); bool existing_results = !results.empty(); _transients.clear (); @@ -1563,7 +1584,7 @@ AudioRegion::get_transients (vector<nframes64_t>& results, bool force_new) for (uint32_t i = 0; i < n_channels(); ++i) { - vector<nframes64_t> these_results; + AnalysisFeatureList these_results; t.reset (); @@ -1573,7 +1594,7 @@ AudioRegion::get_transients (vector<nframes64_t>& results, bool force_new) /* translate all transients to give absolute position */ - for (vector<nframes64_t>::iterator i = these_results.begin(); i != these_results.end(); ++i) { + for (AnalysisFeatureList::iterator i = these_results.begin(); i != these_results.end(); ++i) { (*i) += _position; } @@ -1590,12 +1611,12 @@ AudioRegion::get_transients (vector<nframes64_t>& results, bool force_new) */ results.insert (results.end(), _transients.begin(), _transients.end()); - cleanup_transients (results); + TransientDetector::cleanup_transients (results, pl->session().frame_rate(), 3.0); } /* make sure ours are clean too */ - cleanup_transients (_transients); + TransientDetector::cleanup_transients (_transients, pl->session().frame_rate(), 3.0); } valid_transients = true; @@ -1603,7 +1624,6 @@ AudioRegion::get_transients (vector<nframes64_t>& results, bool force_new) 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 68bfff10ee..51b4763e24 100644 --- a/libs/ardour/audiosource.cc +++ b/libs/ardour/audiosource.cc @@ -961,9 +961,37 @@ AudioSource::get_transients_path () const s = _id.to_s(); s += '.'; - s += X_("transients"); + s += TransientDetector::operational_identifier(); parts.push_back (s); return Glib::build_filename (parts); } +void +AudioSource::set_been_analysed (bool yn) +{ + Source::set_been_analysed (yn); + + if (yn) { + load_transients (get_transients_path()); + } +} + +bool +AudioSource::check_for_analysis_data_on_disk () +{ + /* looks to see if the analysis files for this source are on disk. + if so, mark us already analysed. + */ + + string path = get_transients_path (); + bool ok = true; + + if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS)) { + ok = false; + } + + // XXX add other tests here as appropriate + + set_been_analysed (ok); +} diff --git a/libs/ardour/enums.cc b/libs/ardour/enums.cc index 17dff0bda6..78583c8ddf 100644 --- a/libs/ardour/enums.cc +++ b/libs/ardour/enums.cc @@ -80,6 +80,7 @@ setup_enum_writer () Location::Flags _Location_Flags; RouteGroup::Flag _RouteGroup_Flag; Region::Flag _Region_Flag; + Region::PositionLockStyle _Region_PositionLockStyle; Track::FreezeState _Track_FreezeState; #define REGISTER(e) enum_writer->register_distinct (typeid(e).name(), i, s); i.clear(); s.clear() @@ -352,6 +353,10 @@ setup_enum_writer () REGISTER_CLASS_ENUM (Region, DoNotSaveState); REGISTER_BITS (_Region_Flag); + REGISTER_CLASS_ENUM (Region, AudioTime); + REGISTER_CLASS_ENUM (Region, MusicTime); + REGISTER_BITS (_Region_PositionLockStyle); + REGISTER_CLASS_ENUM (Track, NoFreeze); REGISTER_CLASS_ENUM (Track, Frozen); REGISTER_CLASS_ENUM (Track, UnFrozen); diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc index 5ada27a787..422ed16786 100644 --- a/libs/ardour/globals.cc +++ b/libs/ardour/globals.cc @@ -46,6 +46,7 @@ #include <midi++/mmc.h> #include <ardour/ardour.h> +#include <ardour/analyser.h> #include <ardour/audio_library.h> #include <ardour/configuration.h> #include <ardour/profile.h> @@ -300,6 +301,7 @@ ARDOUR::init (bool use_vst, bool try_optimization) setup_hardware_optimization (try_optimization); SourceFactory::init (); + Analyser::init (); /* singleton - first object is "it" */ new PluginManager (); diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc index f495e00510..aa79812f98 100644 --- a/libs/ardour/playlist.cc +++ b/libs/ardour/playlist.cc @@ -37,6 +37,7 @@ #include <ardour/region.h> #include <ardour/region_factory.h> #include <ardour/playlist_factory.h> +#include <ardour/transient_detector.h> #include "i18n.h" @@ -1411,6 +1412,65 @@ Playlist::regions_touched (nframes_t start, nframes_t end) return rlist; } +nframes64_t +Playlist::find_next_transient (nframes64_t from, int dir) +{ + RegionLock rlock (this); + AnalysisFeatureList points; + AnalysisFeatureList these_points; + + for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { + if (dir > 0) { + if ((*i)->last_frame() < from) { + continue; + } + } else { + if ((*i)->first_frame() > from) { + continue; + } + } + + (*i)->get_transients (these_points); + + /* add first frame, just, err, because */ + + these_points.push_back ((*i)->first_frame()); + + points.insert (points.end(), these_points.begin(), these_points.end()); + these_points.clear (); + } + + if (points.empty()) { + return -1; + } + + TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0); + bool reached = false; + + if (dir > 0) { + for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) { + if ((*x) >= from) { + reached = true; + } + + if (reached && (*x) > from) { + return *x; + } + } + } else { + for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) { + if ((*x) <= from) { + reached = true; + } + + if (reached && (*x) < from) { + return *x; + } + } + } + + return -1; +} boost::shared_ptr<Region> Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir) @@ -2209,3 +2269,18 @@ Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>) return false; } + +void +Playlist::update_after_tempo_map_change () +{ + RegionLock rlock (const_cast<Playlist*> (this)); + RegionList copy (regions); + + freeze (); + + for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) { + (*i)->update_position_after_tempo_map_change (); + } + + thaw (); +} diff --git a/libs/ardour/recent_sessions.cc b/libs/ardour/recent_sessions.cc index 954162dd18..f14661d3d0 100644 --- a/libs/ardour/recent_sessions.cc +++ b/libs/ardour/recent_sessions.cc @@ -64,14 +64,9 @@ ARDOUR::read_recent_sessions (RecentSessions& rs) break; } - if (!access(newpair.second.c_str(), R_OK)) { - rs.push_back (newpair); - } + rs.push_back (newpair); } - // This deletes any missing sessions - ARDOUR::write_recent_sessions (rs); - /* display sorting should be done in the GUI, otherwise the * natural order will be broken */ diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc index 75682ec592..6bbc4329eb 100644 --- a/libs/ardour/region.cc +++ b/libs/ardour/region.cc @@ -21,6 +21,7 @@ #include <cmath> #include <climits> #include <algorithm> +#include <sstream> #include <sigc++/bind.h> #include <sigc++/class_slot.h> @@ -28,10 +29,12 @@ #include <glibmm/thread.h> #include <pbd/xml++.h> #include <pbd/stacktrace.h> +#include <pbd/enumwriter.h> #include <ardour/region.h> #include <ardour/playlist.h> #include <ardour/session.h> +#include <ardour/tempo.h> #include <ardour/region_factory.h> #include "i18n.h" @@ -56,7 +59,7 @@ Region::Region (nframes_t start, nframes_t length, const string& name, layer_t l _read_data_count = 0; _frozen = 0; pending_changed = Change (0); - + valid_transients = false; _name = name; _start = start; _sync_position = _start; @@ -72,6 +75,7 @@ Region::Region (nframes_t start, nframes_t length, const string& name, layer_t l _read_data_count = 0; _first_edit = EditChangesNothing; _last_layer_op = 0; + _positional_lock_style = AudioTime; } Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags) @@ -81,6 +85,7 @@ Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes _frozen = 0; pending_changed = Change (0); _read_data_count = 0; + valid_transients = false; _start = other->_start + offset; if (other->_sync_position < offset) { @@ -101,6 +106,7 @@ Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes _flags = Flag (flags & ~(Locked|WholeFile|Hidden)); _first_edit = EditChangesNothing; _last_layer_op = 0; + _positional_lock_style = AudioTime; } Region::Region (boost::shared_ptr<const Region> other) @@ -110,6 +116,7 @@ Region::Region (boost::shared_ptr<const Region> other) _frozen = 0; pending_changed = Change (0); _read_data_count = 0; + valid_transients = false; _first_edit = EditChangesID; other->_first_edit = EditChangesName; @@ -134,12 +141,14 @@ Region::Region (boost::shared_ptr<const Region> other) _layer = other->_layer; _flags = Flag (other->_flags & ~Locked); _last_layer_op = other->_last_layer_op; + _positional_lock_style = AudioTime; } Region::Region (const XMLNode& node) { _frozen = 0; pending_changed = Change (0); + valid_transients = false; _read_data_count = 0; _start = 0; _sync_position = _start; @@ -151,6 +160,7 @@ Region::Region (const XMLNode& node) _layer = 0; _flags = Flag (0); _first_edit = EditChangesNothing; + _positional_lock_style = AudioTime; if (set_state (node)) { throw failed_constructor(); @@ -206,6 +216,7 @@ Region::set_length (nframes_t len, void *src) first_edit (); maybe_uncopy (); + invalidate_transients (); if (!_frozen) { recompute_at_end (); @@ -283,12 +294,49 @@ Region::special_set_position (nframes_t pos) } void +Region::set_position_lock_style (PositionLockStyle ps) +{ + boost::shared_ptr<Playlist> pl (playlist()); + + if (!pl) { + return; + } + + _positional_lock_style = ps; + + if (_positional_lock_style == MusicTime) { + pl->session().tempo_map().bbt_time (_position, _bbt_time); + } + +} + +void +Region::update_position_after_tempo_map_change () +{ + boost::shared_ptr<Playlist> pl (playlist()); + + if (!pl || _positional_lock_style != MusicTime) { + return; + } + + TempoMap& map (pl->session().tempo_map()); + nframes_t pos = map.frame_time (_bbt_time); + set_position_internal (pos, false); +} + +void Region::set_position (nframes_t pos, void *src) { if (_flags & Locked) { return; } + set_position_internal (pos, true); +} + +void +Region::set_position_internal (nframes_t pos, bool allow_bbt_recompute) +{ if (_position != pos) { _last_position = _position; _position = pos; @@ -303,6 +351,15 @@ Region::set_position (nframes_t pos, void *src) _last_length = _length; _length = max_frames - _position; } + + if (allow_bbt_recompute && _positional_lock_style == MusicTime) { + boost::shared_ptr<Playlist> pl (playlist()); + if (pl) { + pl->session().tempo_map().bbt_time (_position, _bbt_time); + } + } + + invalidate_transients (); } /* do this even if the position is the same. this helps out @@ -396,6 +453,7 @@ Region::set_start (nframes_t pos, void *src) _start = pos; _flags = Region::Flag (_flags & ~WholeFile); first_edit (); + invalidate_transients (); send_change (StartChanged); } @@ -793,9 +851,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), "%Ld", _ancestral_start); + snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_start); node->add_property ("ancestral-start", buf); - snprintf (buf, sizeof (buf), "%Ld", _ancestral_length); + snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_length); node->add_property ("ancestral-length", buf); snprintf (buf, sizeof (buf), "%.12g", _stretch); node->add_property ("stretch", buf); @@ -826,6 +884,13 @@ Region::state (bool full_state) snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position); node->add_property ("sync-position", buf); + if (_positional_lock_style != AudioTime) { + node->add_property ("positional-lock-style", enum_2_string (_positional_lock_style)); + stringstream str; + str << _bbt_time; + node->add_property ("bbt-position", str.str()); + } + return *node; } @@ -908,6 +973,27 @@ Region::set_live_state (const XMLNode& node, Change& what_changed, bool send) _sync_position = _start; } + if ((prop = node.property ("positional-lock-style")) != 0) { + _positional_lock_style = PositionLockStyle (string_2_enum (prop->value(), _positional_lock_style)); + + if (_positional_lock_style == MusicTime) { + if ((prop = node.property ("bbt-position")) == 0) { + /* missing BBT info, revert to audio time locking */ + _positional_lock_style = AudioTime; + } else { + if (sscanf (prop->value().c_str(), "%d|%d|%d", + &_bbt_time.bars, + &_bbt_time.beats, + &_bbt_time.ticks) != 3) { + _positional_lock_style = AudioTime; + } + } + } + + } else { + _positional_lock_style = AudioTime; + } + /* XXX FIRST EDIT !!! */ /* these 3 properties never change as a result of any editing */ @@ -1070,3 +1156,11 @@ Region::region_list_equivalent (boost::shared_ptr<const Region> other) const { return size_equivalent (other) && source_equivalent (other) && _name == other->_name; } + +void +Region::invalidate_transients () +{ + valid_transients = false; + _transients.clear (); +} + diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index ad629f5d6e..1f2ba8d61b 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -46,6 +46,7 @@ #include <ardour/audioengine.h> #include <ardour/configuration.h> #include <ardour/session.h> +#include <ardour/analyser.h> #include <ardour/audio_diskstream.h> #include <ardour/utils.h> #include <ardour/audioplaylist.h> @@ -105,6 +106,7 @@ Session::mix_buffers_with_gain_t Session::mix_buffers_with_gain = 0; Session::mix_buffers_no_gain_t Session::mix_buffers_no_gain = 0; sigc::signal<int> Session::AskAboutPendingState; +sigc::signal<int,nframes_t,nframes_t> Session::AskAboutSampleRateMismatch; sigc::signal<void> Session::SendFeedback; sigc::signal<void> Session::SMPTEOffsetChanged; @@ -2819,6 +2821,9 @@ Session::add_source (boost::shared_ptr<Source> source) set_dirty(); } + if (Config->get_auto_analyse_audio()) { + Analyser::queue_source_for_analysis (source, false); + } } } @@ -3606,18 +3611,36 @@ void Session::tempo_map_changed (Change ignored) { clear_clicks (); + + for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) { + (*i)->update_after_tempo_map_change (); + } + + for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) { + (*i)->update_after_tempo_map_change (); + } + set_dirty (); } void Session::ensure_passthru_buffers (uint32_t howmany) { + if (current_block_size == 0) { + return; + } + while (howmany > _passthru_buffers.size()) { Sample *p; #ifdef NO_POSIX_MEMALIGN p = (Sample *) malloc(current_block_size * sizeof(Sample)); #else - posix_memalign((void **)&p,CPU_CACHE_ALIGN,current_block_size * sizeof(Sample)); + if (posix_memalign((void **)&p,CPU_CACHE_ALIGN,current_block_size * sizeof(Sample)) != 0) { + fatal << string_compose (_("Memory allocation error: posix_memalign (%1 * %2) failed (%3)"), + current_block_size, sizeof (Sample), strerror (errno)) + << endmsg; + /*NOTREACHED*/ + } #endif _passthru_buffers.push_back (p); @@ -3626,7 +3649,12 @@ Session::ensure_passthru_buffers (uint32_t howmany) #ifdef NO_POSIX_MEMALIGN p = (Sample *) malloc(current_block_size * sizeof(Sample)); #else - posix_memalign((void **)&p,CPU_CACHE_ALIGN,current_block_size * 4); + if (posix_memalign((void **)&p,CPU_CACHE_ALIGN,current_block_size * 4) != 0) { + fatal << string_compose (_("Memory allocation error: posix_memalign (%1 * %2) failed (%3)"), + current_block_size, sizeof (Sample), strerror (errno)) + << endmsg; + /*NOTREACHED*/ + } #endif memset (p, 0, sizeof (Sample) * current_block_size); _silent_buffers.push_back (p); diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 265be63960..d0302ea675 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -127,6 +127,9 @@ Session::first_stage_init (string fullpath, string snapshot_name) _name = _current_snapshot_name = snapshot_name; _current_frame_rate = _engine.frame_rate (); + _nominal_frame_rate = _current_frame_rate; + _base_frame_rate = _current_frame_rate; + _tempo_map = new TempoMap (_current_frame_rate); _tempo_map->StateChanged.connect (mem_fun (*this, &Session::tempo_map_changed)); @@ -222,9 +225,6 @@ Session::first_stage_init (string fullpath, string snapshot_name) waiting_for_sync_offset = false; } - _current_frame_rate = 48000; - _base_frame_rate = 48000; - last_smpte_when = 0; _smpte_offset = 0; _smpte_offset_negative = true; @@ -931,16 +931,16 @@ Session::state(bool full_state) // store libardour version, just in case char buf[16]; - snprintf(buf, sizeof(buf)-1, "%d.%d.%d", - libardour2_major_version, libardour2_minor_version, libardour2_micro_version); + snprintf(buf, sizeof(buf), "%d.%d.%d", libardour2_major_version, libardour2_minor_version, libardour2_micro_version); node->add_property("version", string(buf)); /* store configuration settings */ if (full_state) { - /* store the name */ node->add_property ("name", _name); + snprintf (buf, sizeof (buf), "%" PRId32, _nominal_frame_rate); + node->add_property ("sample-rate", buf); if (session_dirs.size() > 1) { @@ -1158,7 +1158,6 @@ Session::set_state (const XMLNode& node) int ret = -1; _state_of_the_state = StateOfTheState (_state_of_the_state|CannotSave); - if (node.name() != X_("Session")){ fatal << _("programming error: Session: incorrect XML node sent to set_state()") << endmsg; @@ -1169,6 +1168,17 @@ Session::set_state (const XMLNode& node) _name = prop->value (); } + if ((prop = node.property (X_("sample-rate"))) != 0) { + + _nominal_frame_rate = atoi (prop->value()); + + if (_nominal_frame_rate != _current_frame_rate) { + if (AskAboutSampleRateMismatch (_nominal_frame_rate, _current_frame_rate)) { + return -1; + } + } + } + setup_raid_path(_path); if ((prop = node.property (X_("id-counter"))) != 0) { diff --git a/libs/ardour/source.cc b/libs/ardour/source.cc index 7f680e7100..16cc603089 100644 --- a/libs/ardour/source.cc +++ b/libs/ardour/source.cc @@ -46,6 +46,7 @@ Source::Source (Session& s, string name) : _session (s) { _name = name; + _analysed = false; _timestamp = 0; _in_use = 0; } @@ -54,6 +55,7 @@ Source::Source (Session& s, const XMLNode& node) : _session (s) { _timestamp = 0; + _analysed = false; _in_use = 0; if (set_state (node)) { @@ -151,3 +153,24 @@ Source::used () const { return _playlists.size(); } + +bool +Source::has_been_analysed() const +{ + Glib::Mutex::Lock lm (_analysis_lock); + return _analysed; +} + +void +Source::set_been_analysed (bool yn) +{ + { + Glib::Mutex::Lock lm (_analysis_lock); + _analysed = yn; + } + + if (yn) { + AnalysisChanged(); // EMIT SIGNAL + } +} + diff --git a/libs/ardour/source_factory.cc b/libs/ardour/source_factory.cc index 9d632b9a84..23a9c29c96 100644 --- a/libs/ardour/source_factory.cc +++ b/libs/ardour/source_factory.cc @@ -115,6 +115,7 @@ boost::shared_ptr<Source> SourceFactory::createSilent (Session& s, const XMLNode& node, nframes_t nframes, float sr) { boost::shared_ptr<Source> ret (new SilentFileSource (s, node, nframes, sr)); + // no analysis data - the file is non-existent SourceCreated (ret); return ret; } @@ -128,6 +129,7 @@ SourceFactory::create (Session& s, const XMLNode& node, bool defer_peaks) if (setup_peakfile (ret, defer_peaks)) { return boost::shared_ptr<Source>(); } + ret->check_for_analysis_data_on_disk (); SourceCreated (ret); return ret; } @@ -141,6 +143,7 @@ SourceFactory::create (Session& s, const XMLNode& node, bool defer_peaks) if (setup_peakfile (ret, defer_peaks)) { return boost::shared_ptr<Source>(); } + ret->check_for_analysis_data_on_disk (); SourceCreated (ret); return ret; } @@ -160,7 +163,7 @@ SourceFactory::create (Session& s, const XMLNode& node, bool defer_peaks) if (setup_peakfile (ret, defer_peaks)) { return boost::shared_ptr<Source>(); } - + ret->check_for_analysis_data_on_disk (); SourceCreated (ret); return ret; } @@ -178,6 +181,7 @@ SourceFactory::createReadable (Session& s, string path, int chn, AudioFileSource if (setup_peakfile (ret, defer_peaks)) { return boost::shared_ptr<Source>(); } + ret->check_for_analysis_data_on_disk (); if (announce) { SourceCreated (ret); } @@ -192,6 +196,7 @@ SourceFactory::createReadable (Session& s, string path, int chn, AudioFileSource if (setup_peakfile (ret, defer_peaks)) { return boost::shared_ptr<Source>(); } + ret->check_for_analysis_data_on_disk (); if (announce) { SourceCreated (ret); } @@ -204,6 +209,7 @@ SourceFactory::createReadable (Session& s, string path, int chn, AudioFileSource if (setup_peakfile (ret, defer_peaks)) { return boost::shared_ptr<Source>(); } + ret->check_for_analysis_data_on_disk (); if (announce) { SourceCreated (ret); } @@ -224,6 +230,8 @@ SourceFactory::createReadable (Session& s, string path, int chn, AudioFileSource return boost::shared_ptr<Source>(); } + ret->check_for_analysis_data_on_disk (); + if (announce) { SourceCreated (ret); } @@ -250,6 +258,8 @@ SourceFactory::createWritable (Session& s, std::string path, bool destructive, n return boost::shared_ptr<Source>(); } + // no analysis data - this is a new file + if (announce) { SourceCreated (ret); } diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index b2865fc399..3170d588a1 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -246,29 +246,49 @@ TempoMap::~TempoMap () int TempoMap::move_metric_section (MetricSection& section, const BBT_Time& when) { - if (when == section.start()) { + if (when == section.start() || !section.movable()) { return -1; } - if (!section.movable()) { - return 1; - } - Glib::RWLock::WriterLock lm (lock); MetricSectionSorter cmp; - BBT_Time corrected (when); - - if (dynamic_cast<MeterSection*>(§ion) != 0) { - if (corrected.beats > 1) { - corrected.beats = 1; - corrected.bars++; + + if (when.beats != 1) { + + /* position by audio frame, then recompute BBT timestamps from the audio ones */ + + nframes_t frame = frame_time (when); + // cerr << "nominal frame time = " << frame << endl; + + nframes_t prev_frame = round_to_type (frame, -1, Beat); + nframes_t next_frame = round_to_type (frame, 1, Beat); + + // cerr << "previous beat at " << prev_frame << " next at " << next_frame << endl; + + /* use the closest beat */ + + if ((frame - prev_frame) < (next_frame - frame)) { + frame = prev_frame; + } else { + frame = next_frame; } + + // cerr << "actual frame time = " << frame << endl; + section.set_frame (frame); + // cerr << "frame time = " << section.frame() << endl; + timestamp_metrics (false); + // cerr << "new BBT time = " << section.start() << endl; + metrics->sort (cmp); + + } else { + + /* positioned at bar start already, so just put it there */ + + section.set_start (when); + metrics->sort (cmp); + timestamp_metrics (true); } - corrected.ticks = 0; - section.set_start (corrected); - metrics->sort (cmp); - timestamp_metrics (true); return 0; } @@ -288,7 +308,6 @@ TempoMap::move_meter (MeterSection& meter, const BBT_Time& when) StateChanged (Change (0)); } } - void TempoMap::remove_tempo (const TempoSection& tempo) @@ -412,11 +431,12 @@ TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement) TempoSection *ts; if ((ts = dynamic_cast<TempoSection*>(*i)) != 0 && ts == &existing) { - - *((Tempo *) ts) = replacement; + + *((Tempo *) ts) = replacement; replaced = true; timestamp_metrics (true); + break; } } @@ -494,6 +514,21 @@ TempoMap::replace_meter (MeterSection& existing, const Meter& replacement) } void +TempoMap::change_initial_tempo (double beats_per_minute, double note_type) +{ + Tempo newtempo (beats_per_minute, note_type); + TempoSection* t; + + for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) { + if ((t = dynamic_cast<TempoSection*> (*i)) != 0) { + *((Tempo*) t) = newtempo; + StateChanged (Change (0)); + break; + } + } +} + +void TempoMap::change_existing_tempo_at (nframes_t where, double beats_per_minute, double note_type) { Tempo newtempo (beats_per_minute, note_type); @@ -582,6 +617,8 @@ TempoMap::timestamp_metrics (bool use_bbt) if (use_bbt) { + // cerr << "\n\n\n ######################\nTIMESTAMP via BBT ##############\n" << endl; + nframes_t current = 0; nframes_t section_frames; BBT_Time start; @@ -611,42 +648,68 @@ TempoMap::timestamp_metrics (bool use_bbt) } else { + // cerr << "\n\n\n ######################\nTIMESTAMP via AUDIO ##############\n" << endl; + bool first = true; + MetricSection* prev = 0; for (i = metrics->begin(); i != metrics->end(); ++i) { BBT_Time bbt; - - bbt_time_with_metric ((*i)->frame(), bbt, Metric (*meter, *tempo)); + Metric metric (*meter, *tempo); + + if (prev) { + metric.set_start (prev->start()); + } else { + // metric will be at frames=0 bbt=1|1|0 by default + // which is correct for our purpose + } + + bbt_time_with_metric ((*i)->frame(), bbt, metric); // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => "; + if (first) { first = false; } else { - if (bbt.beats != 1 || bbt.ticks != 0) { + + if (bbt.ticks > Meter::ticks_per_beat/2) { + /* round up to next beat */ + bbt.beats += 1; + } + + bbt.ticks = 0; + + if (bbt.beats != 1) { + /* round up to next bar */ bbt.bars += 1; bbt.beats = 1; - bbt.ticks = 0; } } - - // cerr << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << endl; - + + //s cerr << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << endl; + (*i)->set_start (bbt); if ((t = dynamic_cast<TempoSection*>(*i)) != 0) { tempo = t; + // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl; } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) { meter = m; + // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl; } else { fatal << _("programming error: unhandled MetricSection type") << endmsg; /*NOTREACHED*/ } + + prev = (*i); } } // dump (cerr); + // cerr << "###############################################\n\n\n" << endl; + } TempoMap::Metric @@ -741,6 +804,8 @@ TempoMap::bbt_time_with_metric (nframes_t frame, BBT_Time& bbt, const Metric& me double xtra_beats = 0; double beats = 0; + // cerr << "---- BBT time for " << frame << " using metric @ " << metric.frame() << " BBT " << metric.start() << endl; + const double beats_per_bar = metric.meter().beats_per_bar(); const double frames_per_bar = metric.meter().frames_per_bar (metric.tempo(), _frame_rate); const double beat_frames = metric.tempo().frames_per_beat (_frame_rate, metric.meter()); @@ -748,11 +813,14 @@ TempoMap::bbt_time_with_metric (nframes_t frame, BBT_Time& bbt, const Metric& me /* now compute how far beyond that point we actually are. */ frame_diff = frame - metric.frame(); + + // cerr << "----\tdelta = " << frame_diff << endl; xtra_bars = (uint32_t) floor (frame_diff / frames_per_bar); frame_diff -= (uint32_t) floor (xtra_bars * frames_per_bar); xtra_beats = (double) frame_diff / beat_frames; + // cerr << "---\tmeaning " << xtra_bars << " xtra bars and " << xtra_beats << " xtra beats\n"; /* and set the returned value */ @@ -765,21 +833,21 @@ TempoMap::bbt_time_with_metric (nframes_t frame, BBT_Time& bbt, const Metric& me bbt.bars = metric.start().bars + xtra_bars; beats = (double) metric.start().beats + xtra_beats; - + bbt.bars += (uint32_t) floor(beats/ (beats_per_bar+1) ); beats = fmod(beats - 1, beats_per_bar )+ 1.0; bbt.ticks = (uint32_t)( round((beats - floor(beats)) *(double) Meter::ticks_per_beat)); bbt.beats = (uint32_t) floor(beats); - + + // cerr << "-----\t RETURN " << bbt << endl; } - 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 be "legal" BBT types, - that means that the beats and ticks should be inside a bar + that means that the beats and ticks should be inside a bar */ nframes_t frames = 0; @@ -814,7 +882,7 @@ TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) con nframes_t TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const { - /*this is used in timestamping the metrics by actually counting the beats */ + /* this is used in timestamping the metrics by actually counting the beats */ nframes_t frames = 0; uint32_t bar = start.bars; @@ -834,16 +902,26 @@ TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, beat = 1; ++bar; ++beats_counted; - } else { - ++beat; - ++beats_counted; + if (beat > beats_per_bar) { + /* this is a fractional beat at the end of a fractional bar - so it should only count for the fraction */ + so it should only count for the fraction + */ + beats_counted -= (ceil(beats_per_bar) - beats_per_bar); } + + } else { + ++beat; + ++beats_counted; } } + + // cerr << "Counted " << beats_counted << " from " << start << " to " << end + // << " bpb were " << beats_per_bar + // << " fpb was " << beat_frames + // << endl; frames = (nframes_t) floor (beats_counted * beat_frames); @@ -1052,7 +1130,6 @@ TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num) } nframes_t - TempoMap::round_to_type (nframes_t frame, int dir, BBTPointType type) { Metric metric = metric_at (frame); @@ -1191,7 +1268,9 @@ TempoMap::get_points (nframes_t lower, nframes_t upper) const if (i == metrics->end()) { limit = upper; + // cerr << "== limit set to end of request @ " << limit << endl; } else { + // cerr << "== limit set to next metric @ " << (*i)->frame() << endl; limit = (*i)->frame(); } @@ -1224,6 +1303,10 @@ TempoMap::get_points (nframes_t lower, nframes_t upper) const beat++; } + // cerr << "out of beats, @ end ? " << (i == metrics->end()) << " out of bpb ? " + // << (beat > ceil(beats_per_bar)) + // << endl; + if (beat > ceil(beats_per_bar) || i != metrics->end()) { /* we walked an entire bar. its @@ -1245,9 +1328,11 @@ TempoMap::get_points (nframes_t lower, nframes_t upper) const if (beat > ceil (beats_per_bar)) { /* next bar goes where the numbers suggest */ current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar); + // cerr << "++ next bar from numbers\n"; } else { /* next bar goes where the next metric is */ current = limit; + // cerr << "++ next bar at next metric\n"; } bar++; beat = 1; @@ -1275,6 +1360,9 @@ TempoMap::get_points (nframes_t lower, nframes_t upper) const beat = 1; } + current = (*i)->frame (); + // cerr << "loop around with current @ " << current << endl; + beats_per_bar = meter->beats_per_bar (); frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate); beat_frames = tempo->frames_per_beat (_frame_rate, *meter); @@ -1419,3 +1507,32 @@ TempoMap::dump (std::ostream& o) const } } +int +TempoMap::n_tempos() const +{ + Glib::RWLock::ReaderLock lm (lock); + int cnt = 0; + + for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) { + if (dynamic_cast<const TempoSection*>(*i) != 0) { + cnt++; + } + } + + return cnt; +} + +int +TempoMap::n_meters() const +{ + Glib::RWLock::ReaderLock lm (lock); + int cnt = 0; + + for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) { + if (dynamic_cast<const MeterSection*>(*i) != 0) { + cnt++; + } + } + + return cnt; +} diff --git a/libs/ardour/transient_detector.cc b/libs/ardour/transient_detector.cc index b85700dd90..b92bf5fb2d 100644 --- a/libs/ardour/transient_detector.cc +++ b/libs/ardour/transient_detector.cc @@ -6,20 +6,36 @@ using namespace Vamp; using namespace ARDOUR; using namespace std; +string TransientDetector::_op_id; + TransientDetector::TransientDetector (float sr) : AudioAnalyser (sr, X_("libardourvampplugins:percussiononsets")) { + if (_op_id.empty()) { + _op_id = X_("libardourvampplugins:percussiononsets"); + + // XXX this should load the above-named plugin and get the current version + + _op_id += ":2"; + } } TransientDetector::~TransientDetector() { } +string +TransientDetector::operational_identifier() +{ + return _op_id; +} + int -TransientDetector::run (const std::string& path, Readable* src, uint32_t channel, vector<nframes64_t>& results) +TransientDetector::run (const std::string& path, Readable* src, uint32_t channel, AnalysisFeatureList& results) { current_results = &results; int ret = analyse (path, src, channel); + current_results = 0; return ret; } @@ -59,3 +75,42 @@ TransientDetector::set_sensitivity (float val) plugin->setParameter ("sensitivity", val); } } + +void +TransientDetector::cleanup_transients (AnalysisFeatureList& t, float sr, float gap_msecs) +{ + if (t.empty()) { + return; + } + + t.sort (); + + /* remove duplicates or other things that are too close */ + + AnalysisFeatureList::iterator i = t.begin(); + AnalysisFeatureList::iterator f, b; + const nframes64_t gap_frames = (nframes64_t) floor (gap_msecs * (sr / 1000.0)); + + while (i != t.end()) { + + // move front iterator to just past i, and back iterator the same place + + f = i; + ++f; + b = f; + + // move f until we find a new value that is far enough away + + while ((f != t.end()) && (((*f) - (*i)) < gap_frames)) { + ++f; + } + + i = f; + + // if f moved forward from b, we had duplicates/too-close points: get rid of them + + if (b != f) { + t.erase (b, f); + } + } +} |