From 6d92be80c1e69f8ff01c89430f19a4bb52f9791d Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Tue, 14 Oct 2025 14:31:45 -0600 Subject: [PATCH] do tempo estimation within AudioSources, after capture and upon import we read 10 seconds from the middle of the data; not infallible but a reasonable first pass at a heuristic. --- libs/ardour/ardour/audiosource.h | 2 ++ libs/ardour/ardour/source.h | 2 ++ libs/ardour/audiosource.cc | 52 ++++++++++++++++++++++++++++++-- libs/ardour/sndfilesource.cc | 2 ++ libs/ardour/source.cc | 6 ++++ libs/ardour/source_factory.cc | 14 +++++---- 6 files changed, 69 insertions(+), 9 deletions(-) diff --git a/libs/ardour/ardour/audiosource.h b/libs/ardour/ardour/audiosource.h index 0c0bd049a3..ce6a1ce8be 100644 --- a/libs/ardour/ardour/audiosource.h +++ b/libs/ardour/ardour/audiosource.h @@ -95,6 +95,8 @@ class LIBARDOUR_API AudioSource : virtual public Source, public ARDOUR::AudioRea /** @return true if the each source sample s must be clamped to -1 < s < 1 */ virtual bool clamped_at_unity () const = 0; + void estimate_tempo (); + protected: static bool _build_missing_peakfiles; static bool _build_peakfiles; diff --git a/libs/ardour/ardour/source.h b/libs/ardour/ardour/source.h index 9376f152d2..33c5954028 100644 --- a/libs/ardour/ardour/source.h +++ b/libs/ardour/ardour/source.h @@ -156,6 +156,8 @@ public: bool get_segment_descriptor (TimelineRange const &, SegmentDescriptor&); int set_segment_descriptor (SegmentDescriptor const &, bool replace = false); + void copy_segment_descriptors (Source const & other); + protected: DataType _type; Flag _flags; diff --git a/libs/ardour/audiosource.cc b/libs/ardour/audiosource.cc index e263d5b616..fb91a80661 100644 --- a/libs/ardour/audiosource.cc +++ b/libs/ardour/audiosource.cc @@ -61,6 +61,7 @@ #include "ardour/rc_configuration.h" #include "ardour/runtime_functions.h" #include "ardour/session.h" +#include "ardour/utils.h" #include "pbd/i18n.h" @@ -131,6 +132,46 @@ AudioSource::~AudioSource () delete [] peak_leftovers; } +void +AudioSource::estimate_tempo () +{ + /* CALLER MUST HOLD WRITER LOCK */ + + const samplecnt_t ten_seconds = _session.sample_rate() * 10; + + if (length().samples() < ten_seconds) { + return; + } + + samplecnt_t data_size = ten_seconds; + std::unique_ptr data (new Sample[data_size]); + + /* Read ten seconds from the middle of the data */ + + if (read_unlocked (data.get(), std::max ((samplecnt_t) 0, (length().samples() - data_size) / 2), data_size) == data_size) { + + double tempo; + double beatcount; + Temporal::Tempo t (120, 4); + Temporal::Meter m (4, 4); + TimelineRange r (timepos_t (0), timepos_t (0) + length(), 0); + + estimate_audio_tempo_source (r, shared_from_this(), data.get(), data_size, _session.sample_rate(), tempo, m, beatcount); + t = Temporal::Tempo (tempo, 4); + + SegmentDescriptor sd; + + sd.set_tempo (t); + sd.set_meter (m); + sd.set_position (0); + sd.set_duration (length().samples()); + + /* One segment descriptor to rule them all */ + + set_segment_descriptor (sd, true); + } +} + XMLNode& AudioSource::get_state () const { @@ -1166,9 +1207,14 @@ AudioSource::available_peaks (double zoom_factor) const void AudioSource::mark_streaming_write_completed (const WriterLock& lock, Temporal::timecnt_t const &) { - Glib::Threads::Mutex::Lock lm (_peaks_ready_lock); + estimate_tempo (); - if (_peaks_built) { - PeaksReady (); /* EMIT SIGNAL */ + { + Glib::Threads::Mutex::Lock lm (_peaks_ready_lock); + + if (_peaks_built) { + PeaksReady (); /* EMIT SIGNAL */ + } } } + diff --git a/libs/ardour/sndfilesource.cc b/libs/ardour/sndfilesource.cc index fbecf0c70e..b6234a1d94 100644 --- a/libs/ardour/sndfilesource.cc +++ b/libs/ardour/sndfilesource.cc @@ -329,6 +329,8 @@ SndFileSource::SndFileSource (Session& s, const AudioFileSource& other, const st tbuf.modtime = statbuf.st_mtime; // = time ((time_t*) 0); g_utime (path.c_str(), &tbuf); } + + copy_segment_descriptors (other); } void diff --git a/libs/ardour/source.cc b/libs/ardour/source.cc index 64d4c45d49..bd81a12f78 100644 --- a/libs/ardour/source.cc +++ b/libs/ardour/source.cc @@ -582,3 +582,9 @@ Source::set_segment_descriptor (SegmentDescriptor const & sr, bool replace) return 0; } + +void +Source::copy_segment_descriptors (Source const & other) +{ + segment_descriptors = other.segment_descriptors; +} diff --git a/libs/ardour/source_factory.cc b/libs/ardour/source_factory.cc index 75be0d5768..1e316ad4d2 100644 --- a/libs/ardour/source_factory.cc +++ b/libs/ardour/source_factory.cc @@ -200,7 +200,7 @@ SourceFactory::create (Session& s, const XMLNode& node, bool defer_peaks) } else { try { - Source* src = new SndFileSource (s, node); + Source* src = new SndFileSource (s, node); std::shared_ptr ret (src); BOOST_MARK_SOURCE (ret); if (setup_peakfile (ret, defer_peaks)) { @@ -214,7 +214,7 @@ SourceFactory::create (Session& s, const XMLNode& node, bool defer_peaks) #ifdef HAVE_COREAUDIO try { - Source* src = new CoreAudioSource (s, node); + Source* src = new CoreAudioSource (s, node); std::shared_ptr ret (src); BOOST_MARK_SOURCE (ret); @@ -252,13 +252,14 @@ SourceFactory::createExternal (DataType type, Session& s, const string& path, { if (type == DataType::AUDIO) { try { - Source* src = new SndFileSource (s, path, chn, flags); + AudioSource* src = new SndFileSource (s, path, chn, flags); std::shared_ptr ret (src); BOOST_MARK_SOURCE (ret); if (setup_peakfile (ret, defer_peaks)) { throw failed_constructor (); } ret->check_for_analysis_data_on_disk (); + src->estimate_tempo (); if (announce) { SourceCreated (ret); } @@ -268,13 +269,14 @@ SourceFactory::createExternal (DataType type, Session& s, const string& path, #ifdef HAVE_COREAUDIO try { - Source* src = new CoreAudioSource (s, path, chn, flags); + AudioSource* src = new CoreAudioSource (s, path, chn, flags); std::shared_ptr ret (src); BOOST_MARK_SOURCE (ret); if (setup_peakfile (ret, defer_peaks)) { throw failed_constructor (); } ret->check_for_analysis_data_on_disk (); + src->estimate_tempo (); if (announce) { SourceCreated (ret); } @@ -415,8 +417,7 @@ SourceFactory::createFromPlaylist (DataType type, Session& s, std::shared_ptr ret (src); if (setup_peakfile (ret, defer_peaks)) { @@ -424,6 +425,7 @@ SourceFactory::createFromPlaylist (DataType type, Session& s, std::shared_ptrcheck_for_analysis_data_on_disk (); + src->estimate_tempo (); SourceCreated (ret); return ret; }