mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-07 15:25:01 +01:00
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.
This commit is contained in:
parent
40f6859905
commit
6d92be80c1
6 changed files with 69 additions and 9 deletions
|
|
@ -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 */
|
/** @return true if the each source sample s must be clamped to -1 < s < 1 */
|
||||||
virtual bool clamped_at_unity () const = 0;
|
virtual bool clamped_at_unity () const = 0;
|
||||||
|
|
||||||
|
void estimate_tempo ();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static bool _build_missing_peakfiles;
|
static bool _build_missing_peakfiles;
|
||||||
static bool _build_peakfiles;
|
static bool _build_peakfiles;
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,8 @@ public:
|
||||||
bool get_segment_descriptor (TimelineRange const &, SegmentDescriptor&);
|
bool get_segment_descriptor (TimelineRange const &, SegmentDescriptor&);
|
||||||
int set_segment_descriptor (SegmentDescriptor const &, bool replace = false);
|
int set_segment_descriptor (SegmentDescriptor const &, bool replace = false);
|
||||||
|
|
||||||
|
void copy_segment_descriptors (Source const & other);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
DataType _type;
|
DataType _type;
|
||||||
Flag _flags;
|
Flag _flags;
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@
|
||||||
#include "ardour/rc_configuration.h"
|
#include "ardour/rc_configuration.h"
|
||||||
#include "ardour/runtime_functions.h"
|
#include "ardour/runtime_functions.h"
|
||||||
#include "ardour/session.h"
|
#include "ardour/session.h"
|
||||||
|
#include "ardour/utils.h"
|
||||||
|
|
||||||
#include "pbd/i18n.h"
|
#include "pbd/i18n.h"
|
||||||
|
|
||||||
|
|
@ -131,6 +132,46 @@ AudioSource::~AudioSource ()
|
||||||
delete [] peak_leftovers;
|
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<Sample[]> 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&
|
XMLNode&
|
||||||
AudioSource::get_state () const
|
AudioSource::get_state () const
|
||||||
{
|
{
|
||||||
|
|
@ -1165,6 +1206,9 @@ AudioSource::available_peaks (double zoom_factor) const
|
||||||
|
|
||||||
void
|
void
|
||||||
AudioSource::mark_streaming_write_completed (const WriterLock& lock, Temporal::timecnt_t const &)
|
AudioSource::mark_streaming_write_completed (const WriterLock& lock, Temporal::timecnt_t const &)
|
||||||
|
{
|
||||||
|
estimate_tempo ();
|
||||||
|
|
||||||
{
|
{
|
||||||
Glib::Threads::Mutex::Lock lm (_peaks_ready_lock);
|
Glib::Threads::Mutex::Lock lm (_peaks_ready_lock);
|
||||||
|
|
||||||
|
|
@ -1172,3 +1216,5 @@ AudioSource::mark_streaming_write_completed (const WriterLock& lock, Temporal::t
|
||||||
PeaksReady (); /* EMIT SIGNAL */
|
PeaksReady (); /* EMIT SIGNAL */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -329,6 +329,8 @@ SndFileSource::SndFileSource (Session& s, const AudioFileSource& other, const st
|
||||||
tbuf.modtime = statbuf.st_mtime; // = time ((time_t*) 0);
|
tbuf.modtime = statbuf.st_mtime; // = time ((time_t*) 0);
|
||||||
g_utime (path.c_str(), &tbuf);
|
g_utime (path.c_str(), &tbuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
copy_segment_descriptors (other);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
|
|
@ -582,3 +582,9 @@ Source::set_segment_descriptor (SegmentDescriptor const & sr, bool replace)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Source::copy_segment_descriptors (Source const & other)
|
||||||
|
{
|
||||||
|
segment_descriptors = other.segment_descriptors;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -252,13 +252,14 @@ SourceFactory::createExternal (DataType type, Session& s, const string& path,
|
||||||
{
|
{
|
||||||
if (type == DataType::AUDIO) {
|
if (type == DataType::AUDIO) {
|
||||||
try {
|
try {
|
||||||
Source* src = new SndFileSource (s, path, chn, flags);
|
AudioSource* src = new SndFileSource (s, path, chn, flags);
|
||||||
std::shared_ptr<Source> ret (src);
|
std::shared_ptr<Source> ret (src);
|
||||||
BOOST_MARK_SOURCE (ret);
|
BOOST_MARK_SOURCE (ret);
|
||||||
if (setup_peakfile (ret, defer_peaks)) {
|
if (setup_peakfile (ret, defer_peaks)) {
|
||||||
throw failed_constructor ();
|
throw failed_constructor ();
|
||||||
}
|
}
|
||||||
ret->check_for_analysis_data_on_disk ();
|
ret->check_for_analysis_data_on_disk ();
|
||||||
|
src->estimate_tempo ();
|
||||||
if (announce) {
|
if (announce) {
|
||||||
SourceCreated (ret);
|
SourceCreated (ret);
|
||||||
}
|
}
|
||||||
|
|
@ -268,13 +269,14 @@ SourceFactory::createExternal (DataType type, Session& s, const string& path,
|
||||||
|
|
||||||
#ifdef HAVE_COREAUDIO
|
#ifdef HAVE_COREAUDIO
|
||||||
try {
|
try {
|
||||||
Source* src = new CoreAudioSource (s, path, chn, flags);
|
AudioSource* src = new CoreAudioSource (s, path, chn, flags);
|
||||||
std::shared_ptr<Source> ret (src);
|
std::shared_ptr<Source> ret (src);
|
||||||
BOOST_MARK_SOURCE (ret);
|
BOOST_MARK_SOURCE (ret);
|
||||||
if (setup_peakfile (ret, defer_peaks)) {
|
if (setup_peakfile (ret, defer_peaks)) {
|
||||||
throw failed_constructor ();
|
throw failed_constructor ();
|
||||||
}
|
}
|
||||||
ret->check_for_analysis_data_on_disk ();
|
ret->check_for_analysis_data_on_disk ();
|
||||||
|
src->estimate_tempo ();
|
||||||
if (announce) {
|
if (announce) {
|
||||||
SourceCreated (ret);
|
SourceCreated (ret);
|
||||||
}
|
}
|
||||||
|
|
@ -415,8 +417,7 @@ SourceFactory::createFromPlaylist (DataType type, Session& s, std::shared_ptr<Pl
|
||||||
start = timecnt_t::zero (Temporal::AudioTime);
|
start = timecnt_t::zero (Temporal::AudioTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
Source* src = new AudioPlaylistSource (s, orig, name, ap, chn, start, len, Source::Flag (0));
|
AudioSource* src = new AudioPlaylistSource (s, orig, name, ap, chn, start, len, Source::Flag (0));
|
||||||
|
|
||||||
std::shared_ptr<Source> ret (src);
|
std::shared_ptr<Source> ret (src);
|
||||||
|
|
||||||
if (setup_peakfile (ret, defer_peaks)) {
|
if (setup_peakfile (ret, defer_peaks)) {
|
||||||
|
|
@ -424,6 +425,7 @@ SourceFactory::createFromPlaylist (DataType type, Session& s, std::shared_ptr<Pl
|
||||||
}
|
}
|
||||||
|
|
||||||
ret->check_for_analysis_data_on_disk ();
|
ret->check_for_analysis_data_on_disk ();
|
||||||
|
src->estimate_tempo ();
|
||||||
SourceCreated (ret);
|
SourceCreated (ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue