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
This commit is contained in:
parent
df5e700f90
commit
e3c67bceb8
@ -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 */
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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 ();
|
||||
|
@ -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_length_internal (framecnt_t len)
|
||||
{
|
||||
Region::set_length_internal (len);
|
||||
update_length_beats ();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegion::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
|
||||
{
|
||||
BeatsFramesConverter old_converter(_session.tempo_map(), _position - _start);
|
||||
double length_beats = old_converter.from(_length);
|
||||
|
||||
Region::set_position_internal (pos, allow_bbt_recompute);
|
||||
|
||||
BeatsFramesConverter new_converter(_session.tempo_map(), pos - _start);
|
||||
|
||||
set_length(new_converter.to(length_beats), 0);
|
||||
/* 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
|
||||
|
@ -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 ();
|
||||
@ -476,6 +476,12 @@ Region::set_length (framecnt_t len, void */*src*/)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Region::set_length_internal (framecnt_t len)
|
||||
{
|
||||
_length = len;
|
||||
}
|
||||
|
||||
void
|
||||
Region::maybe_uncopy ()
|
||||
{
|
||||
@ -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) {
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user