13
0

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:
Paul Davis 2010-12-14 20:03:40 +00:00
parent df5e700f90
commit e3c67bceb8
8 changed files with 273 additions and 30 deletions

View File

@ -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 */

View File

@ -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);

View File

@ -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);

View File

@ -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 */

View File

@ -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 ();

View File

@ -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

View File

@ -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) {

View File

@ -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.