summaryrefslogtreecommitdiff
path: root/libs/ardour
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2010-12-14 20:03:40 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2010-12-14 20:03:40 +0000
commite3c67bceb864adc10764e901b87ad8b6677caebd (patch)
tree98cc093e7255dc658bd839a1bbf6c92f76b58331 /libs/ardour
parentdf5e700f90d9fd6c803e8e85c56f3fe8118bc101 (diff)
implement methods in TempoMap for walking a given distance along a tempo map and returning the resulting time; add a new property, _length_beats, to MidiRegion; use previously mentioned methods to keep _length_beats up to date as regions are moved AND as tempo map changes occur
git-svn-id: svn://localhost/ardour2/branches/3.0@8274 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/ardour')
-rw-r--r--libs/ardour/ardour/midi_region.h5
-rw-r--r--libs/ardour/ardour/region.h1
-rw-r--r--libs/ardour/ardour/tempo.h7
-rw-r--r--libs/ardour/beats_frames_converter.cc20
-rw-r--r--libs/ardour/globals.cc2
-rw-r--r--libs/ardour/midi_region.cc54
-rw-r--r--libs/ardour/region.cc10
-rw-r--r--libs/ardour/tempo.cc204
8 files changed, 273 insertions, 30 deletions
diff --git a/libs/ardour/ardour/midi_region.h b/libs/ardour/ardour/midi_region.h
index dae984c331..53ffb4497c 100644
--- a/libs/ardour/ardour/midi_region.h
+++ b/libs/ardour/ardour/midi_region.h
@@ -117,6 +117,7 @@ class MidiRegion : public Region
private:
friend class RegionFactory;
+ PBD::Property<Evoral::MusicalTime> _length_beats;
MidiRegion (const SourceList&);
MidiRegion (boost::shared_ptr<const MidiRegion>, frameoffset_t offset = 0, bool offset_relative = true);
@@ -134,6 +135,8 @@ class MidiRegion : public Region
void recompute_at_end ();
void set_position_internal (framepos_t pos, bool allow_bbt_recompute);
+ void set_length_internal (framecnt_t len);
+ void update_length_beats ();
void model_changed ();
void model_automation_state_changed (Evoral::Parameter const &);
@@ -143,6 +146,8 @@ class MidiRegion : public Region
PBD::ScopedConnection _model_connection;
PBD::ScopedConnection _source_connection;
PBD::ScopedConnection _model_contents_connection;
+
+ double _last_length_beats;
};
} /* namespace ARDOUR */
diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h
index 8a3cb1d9f5..e1bba21b52 100644
--- a/libs/ardour/ardour/region.h
+++ b/libs/ardour/ardour/region.h
@@ -325,6 +325,7 @@ class Region
void trim_to_internal (framepos_t position, framecnt_t length, void *src);
virtual void set_position_internal (framepos_t pos, bool allow_bbt_recompute);
+ virtual void set_length_internal (framepos_t pos);
void modify_front (framepos_t new_position, bool reset_fade, void* src);
void modify_end (framepos_t new_position, bool reset_fade, void* src);
diff --git a/libs/ardour/ardour/tempo.h b/libs/ardour/ardour/tempo.h
index de45c788f1..90d27ebed0 100644
--- a/libs/ardour/ardour/tempo.h
+++ b/libs/ardour/ardour/tempo.h
@@ -204,15 +204,13 @@ class TempoMap : public PBD::StatefulDestructible
framecnt_t frame_time (const Timecode::BBT_Time&) const;
framecnt_t bbt_duration_at (framepos_t, const Timecode::BBT_Time&, int dir) const;
- void bbt_time_add (framepos_t origin, Timecode::BBT_Time& start, const Timecode::BBT_Time& shift);
-
static const Tempo& default_tempo() { return _default_tempo; }
static const Meter& default_meter() { return _default_meter; }
const Tempo& tempo_at (framepos_t) const;
const Meter& meter_at (framepos_t) const;
- const TempoSection& tempo_section_at (framepos_t);
+ const TempoSection& tempo_section_at (framepos_t) const;
void add_tempo(const Tempo&, Timecode::BBT_Time where);
void add_meter(const Meter&, Timecode::BBT_Time where);
@@ -250,6 +248,9 @@ class TempoMap : public PBD::StatefulDestructible
Timecode::BBT_Time bbt_add (const Timecode::BBT_Time& a, const Timecode::BBT_Time& b) const;
Timecode::BBT_Time bbt_subtract (const Timecode::BBT_Time&, const Timecode::BBT_Time&) const;
+ framepos_t framepos_plus_bbt (framepos_t pos, Timecode::BBT_Time b) const;
+ double framewalk_to_beats (framepos_t pos, framecnt_t distance) const;
+
void change_existing_tempo_at (framepos_t, double bpm, double note_type);
void change_initial_tempo (double bpm, double note_type);
diff --git a/libs/ardour/beats_frames_converter.cc b/libs/ardour/beats_frames_converter.cc
index 180b3e563a..4cda4173b3 100644
--- a/libs/ardour/beats_frames_converter.cc
+++ b/libs/ardour/beats_frames_converter.cc
@@ -27,25 +27,19 @@ namespace ARDOUR {
framecnt_t
BeatsFramesConverter::to(double beats) const
{
- // FIXME: assumes tempo never changes after origin
- const Tempo& tempo = _tempo_map.tempo_at (_origin_b);
- const double frames_per_beat = tempo.frames_per_beat(
- _tempo_map.frame_rate(),
- _tempo_map.meter_at (_origin_b));
+ Timecode::BBT_Time delta;
- return llrint (beats * frames_per_beat);
+ delta.bars = 0;
+ delta.beats = rint (floor (beats));
+ delta.ticks = rint (floor (Meter::ticks_per_beat * fmod (beats, 1.0)));
+
+ return _tempo_map.framepos_plus_bbt (_origin_b, delta);
}
double
BeatsFramesConverter::from (framecnt_t frames) const
{
- // FIXME: assumes tempo never changes after origin
- const Tempo& tempo = _tempo_map.tempo_at (_origin_b);
- const double frames_per_beat = tempo.frames_per_beat(
- _tempo_map.frame_rate(),
- _tempo_map.meter_at (_origin_b));
-
- return frames / frames_per_beat;
+ return _tempo_map.framewalk_to_beats (_origin_b, frames);
}
} /* namespace ARDOUR */
diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc
index 65125370aa..d5579b47da 100644
--- a/libs/ardour/globals.cc
+++ b/libs/ardour/globals.cc
@@ -67,6 +67,7 @@
#include "ardour/control_protocol_manager.h"
#include "ardour/debug.h"
#include "ardour/filesystem_paths.h"
+#include "ardour/midi_region.h"
#include "ardour/mix.h"
#include "ardour/audioplaylist.h"
#include "ardour/plugin_manager.h"
@@ -247,6 +248,7 @@ ARDOUR::init (bool use_vst, bool try_optimization)
make_property_quarks ();
SessionObject::make_property_quarks ();
Region::make_property_quarks ();
+ MidiRegion::make_property_quarks ();
AudioRegion::make_property_quarks ();
RouteGroup::make_property_quarks ();
Playlist::make_property_quarks ();
diff --git a/libs/ardour/midi_region.cc b/libs/ardour/midi_region.cc
index ddf582553b..ee5d42dcf9 100644
--- a/libs/ardour/midi_region.cc
+++ b/libs/ardour/midi_region.cc
@@ -49,7 +49,8 @@ using namespace PBD;
namespace ARDOUR {
namespace Properties {
- PBD::PropertyDescriptor<void*> midi_data;
+ PBD::PropertyDescriptor<void*> midi_data;
+ PBD::PropertyDescriptor<Evoral::MusicalTime> length_beats;
}
}
@@ -58,18 +59,22 @@ MidiRegion::make_property_quarks ()
{
Properties::midi_data.property_id = g_quark_from_static_string (X_("midi-data"));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for midi-data = %1\n", Properties::midi_data.property_id));
+ Properties::length_beats.property_id = g_quark_from_static_string (X_("length-beats"));
+ DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length-beats = %1\n", Properties::length_beats.property_id));
}
void
MidiRegion::register_properties ()
{
- /* none yet, but its only a matter of time */
+ add_property (_length_beats);
}
/* Basic MidiRegion constructor (many channels) */
MidiRegion::MidiRegion (const SourceList& srcs)
: Region (srcs)
+ , _length_beats (Properties::length_beats, (Evoral::MusicalTime) 0)
{
+ update_length_beats ();
register_properties ();
midi_source(0)->ModelChanged.connect_same_thread (_source_connection, boost::bind (&MidiRegion::model_changed, this));
@@ -81,7 +86,9 @@ MidiRegion::MidiRegion (const SourceList& srcs)
/** Create a new MidiRegion, that is part of an existing one */
MidiRegion::MidiRegion (boost::shared_ptr<const MidiRegion> other, frameoffset_t offset, bool offset_relative)
: Region (other, offset, offset_relative)
+ , _length_beats (Properties::length_beats, (Evoral::MusicalTime) 0)
{
+ update_length_beats ();
register_properties ();
assert(_name.val().find("/") == string::npos);
@@ -110,22 +117,43 @@ MidiRegion::clone ()
plist.add (Properties::whole_file, true);
plist.add (Properties::start, _start);
plist.add (Properties::length, _length);
+ plist.add (Properties::length_beats, _length_beats);
plist.add (Properties::layer, 0);
return boost::dynamic_pointer_cast<MidiRegion> (RegionFactory::create (ms, plist, true));
}
void
-MidiRegion::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
+MidiRegion::set_length_internal (framecnt_t len)
{
- BeatsFramesConverter old_converter(_session.tempo_map(), _position - _start);
- double length_beats = old_converter.from(_length);
-
- Region::set_position_internal (pos, allow_bbt_recompute);
+ Region::set_length_internal (len);
+ update_length_beats ();
+}
- BeatsFramesConverter new_converter(_session.tempo_map(), pos - _start);
+void
+MidiRegion::update_length_beats ()
+{
+ cerr << name() << " Updating length beats, currently = " << _length_beats << " w/length = " << _length << endl;
+ BeatsFramesConverter converter (_session.tempo_map(), _position - _start);
+ _length_beats = converter.from (_length);
+ cerr << "\tnew value: " << _length_beats << endl;
+}
- set_length(new_converter.to(length_beats), 0);
+void
+MidiRegion::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
+{
+ Region::set_position_internal (pos, allow_bbt_recompute);
+ /* zero length regions don't exist - so if _length_beats is zero, this object
+ is under construction.
+ */
+ if (_length_beats) {
+ /* leave _length_beats alone, and change _length to reflect the state of things
+ at the new position (tempo map may dictate a different number of frames
+ */
+ BeatsFramesConverter converter (_session.tempo_map(), _position - _start);
+ cerr << name() << " change frame length to " << converter.to (_length_beats) << endl;
+ Region::set_length_internal (converter.to (_length_beats));
+ }
}
framecnt_t
@@ -212,7 +240,13 @@ MidiRegion::state ()
int
MidiRegion::set_state (const XMLNode& node, int version)
{
- return Region::set_state (node, version);
+ int ret = Region::set_state (node, version);
+
+ if (ret == 0) {
+ update_length_beats ();
+ }
+
+ return ret;
}
void
diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc
index 585689a656..a0da02be2a 100644
--- a/libs/ardour/region.cc
+++ b/libs/ardour/region.cc
@@ -462,7 +462,7 @@ Region::set_length (framecnt_t len, void */*src*/)
_last_length = _length;
- _length = len;
+ set_length_internal (len);
_whole_file = false;
first_edit ();
maybe_uncopy ();
@@ -477,6 +477,12 @@ Region::set_length (framecnt_t len, void */*src*/)
}
void
+Region::set_length_internal (framecnt_t len)
+{
+ _length = len;
+}
+
+void
Region::maybe_uncopy ()
{
/* this does nothing but marked a semantic moment once upon a time */
@@ -925,7 +931,7 @@ Region::trim_to_internal (framepos_t position, framecnt_t length, void */*src*/)
if (!property_changes_suspended()) {
_last_length = _length;
}
- _length = length;
+ set_length_internal (length);
what_changed.add (Properties::length);
}
if (_position != position) {
diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc
index adb207c752..90a428b9a0 100644
--- a/libs/ardour/tempo.cc
+++ b/libs/ardour/tempo.cc
@@ -1510,10 +1510,10 @@ TempoMap::get_points (framepos_t lower, framepos_t upper) const
}
const TempoSection&
-TempoMap::tempo_section_at (framepos_t frame)
+TempoMap::tempo_section_at (framepos_t frame) const
{
Glib::RWLock::ReaderLock lm (lock);
- Metrics::iterator i;
+ Metrics::const_iterator i;
TempoSection* prev = 0;
for (i = metrics->begin(); i != metrics->end(); ++i) {
@@ -1904,6 +1904,206 @@ TempoMap::bbt_subtract (const BBT_Time& start, const BBT_Time& decrement) const
return result;
}
+/**
+ * add the BBT interval @param increment to @param start and return the result
+ */
+framepos_t
+TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
+{
+ Metrics::const_iterator i;
+ const MeterSection* meter;
+ const MeterSection* m;
+ const TempoSection* tempo;
+ const TempoSection* t;
+ framecnt_t frames_per_beat;
+
+ meter = &first_meter ();
+ tempo = &first_tempo ();
+
+ assert (meter);
+ assert (tempo);
+
+ /* find the starting metrics for tempo & meter */
+
+ for (i = metrics->begin(); i != metrics->end(); ++i) {
+
+ if ((*i)->frame() > pos) {
+ break;
+ }
+
+ if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
+ tempo = t;
+ } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
+ meter = m;
+ }
+ }
+
+ /* We now have:
+
+ meter -> the Meter for "pos"
+ tempo -> the Tempo for "pos"
+ i -> for first new metric after "pos", possibly metrics->end()
+ */
+
+ /* now comes the complicated part. we have to add one beat a time,
+ checking for a new metric on every beat.
+ */
+
+ frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter);
+
+ while (op.bars) {
+
+ pos += llrint (frames_per_beat * meter->beats_per_bar());
+ op.bars--;
+
+ /* check if we need to use a new metric section: has adding frames moved us
+ to or after the start of the next metric section? in which case, use it.
+ */
+
+ if (i != metrics->end()) {
+ if ((*i)->frame() <= pos) {
+
+ if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
+ tempo = t;
+ } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
+ meter = m;
+ }
+ ++i;
+ frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter);
+
+ }
+ }
+
+ }
+
+ while (op.beats) {
+
+ /* given the current meter, have we gone past the end of the bar ? */
+
+ pos += frames_per_beat;
+ op.beats--;
+
+ /* check if we need to use a new metric section: has adding frames moved us
+ to or after the start of the next metric section? in which case, use it.
+ */
+
+ if (i != metrics->end()) {
+ if ((*i)->frame() <= pos) {
+
+ if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
+ tempo = t;
+ } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
+ meter = m;
+ }
+ ++i;
+ frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter);
+ }
+ }
+ }
+
+ if (op.ticks) {
+ if (op.ticks >= Meter::ticks_per_beat) {
+ pos += frames_per_beat;
+ pos += llrint (frames_per_beat * ((op.ticks % (uint32_t) Meter::ticks_per_beat) / (double) Meter::ticks_per_beat));
+ } else {
+ pos += llrint (frames_per_beat * (op.ticks / (double) Meter::ticks_per_beat));
+ }
+ }
+
+ return pos;
+}
+
+/**
+ * add the BBT interval @param increment to @param start and return the result
+ */
+double
+TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
+{
+ Metrics::const_iterator i;
+ double beats = 0;
+ const MeterSection* meter;
+ const MeterSection* m;
+ const TempoSection* tempo;
+ const TempoSection* t;
+ double frames_per_beat;
+
+ double ddist = distance;
+ double dpos = pos;
+
+ meter = &first_meter ();
+ tempo = &first_tempo ();
+
+ assert (meter);
+ assert (tempo);
+
+ /* find the starting metrics for tempo & meter */
+
+ for (i = metrics->begin(); i != metrics->end(); ++i) {
+
+ if ((*i)->frame() > pos) {
+ break;
+ }
+
+ if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
+ tempo = t;
+ } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
+ meter = m;
+ }
+ }
+
+ /* We now have:
+
+ meter -> the Meter for "pos"
+ tempo -> the Tempo for "pos"
+ i -> for first new metric after "pos", possibly metrics->end()
+ */
+
+ /* now comes the complicated part. we have to add one beat a time,
+ checking for a new metric on every beat.
+ */
+
+ frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter);
+
+ while (ddist > 0) {
+
+ /* if we're nearly at the end, but have a fractional beat left,
+ compute the fraction and then its all over
+ */
+
+ if (ddist < frames_per_beat) {
+ beats += Meter::ticks_per_beat * (ddist/frames_per_beat);
+ break;
+ }
+
+ /* walk one beat */
+
+ ddist -= frames_per_beat;
+ dpos += frames_per_beat;
+ beats += 1.0;
+
+ /* check if we need to use a new metric section: has adding frames moved us
+ to or after the start of the next metric section? in which case, use it.
+ */
+
+ if (i != metrics->end()) {
+ if ((*i)->frame() <= (framepos_t) dpos) {
+
+ if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
+ tempo = t;
+ } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
+ meter = m;
+ }
+ ++i;
+ frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter);
+ }
+ }
+
+ }
+
+ return beats;
+}
+
+
/** Compare the time of this with that of another MetricSection.
* @param with_bbt True to compare using ::start(), false to use ::frame().
* @return -1 for less than, 0 for equal, 1 for greater than.