new Temporal API to allow keeping MIDI notes in position after a map-tempo operation
This commit is contained in:
parent
30d2d7824e
commit
fa225846af
@ -25,6 +25,7 @@
|
||||
#define __ardour_midi_model_h__
|
||||
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <utility>
|
||||
|
||||
@ -252,6 +253,9 @@ public:
|
||||
PatchChangePtr unmarshal_patch_change (XMLNode *);
|
||||
};
|
||||
|
||||
void create_mapping_stash (Temporal::Beats const & offset);
|
||||
void rebuild_from_mapping_stash (Temporal::Beats const & offset);
|
||||
|
||||
/** Start a new NoteDiff command.
|
||||
*
|
||||
* This has no side-effects on the model or Session, the returned command
|
||||
@ -359,6 +363,10 @@ private:
|
||||
|
||||
MidiSource& _midi_source;
|
||||
InsertMergePolicy _insert_merge_policy;
|
||||
|
||||
typedef std::map<void*,superclock_t> TempoMappingStash;
|
||||
TempoMappingStash tempo_mapping_stash;
|
||||
|
||||
};
|
||||
|
||||
} /* namespace ARDOUR */
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
#include "temporal/beats.h"
|
||||
#include "temporal/range.h"
|
||||
#include "temporal/types.h"
|
||||
|
||||
#include "pbd/string_convert.h"
|
||||
|
||||
@ -54,7 +55,7 @@ class ThawList;
|
||||
|
||||
template<typename T> class MidiRingBuffer;
|
||||
|
||||
class LIBARDOUR_API MidiRegion : public Region
|
||||
class LIBARDOUR_API MidiRegion : public Region, public Temporal::TimeThing
|
||||
{
|
||||
public:
|
||||
~MidiRegion();
|
||||
@ -116,6 +117,9 @@ class LIBARDOUR_API MidiRegion : public Region
|
||||
timecnt_t const & read_length,
|
||||
MidiChannelFilter* filter) const;
|
||||
|
||||
void globally_change_time_domain (Temporal::TimeDomain from, Temporal::TimeDomain to);
|
||||
void swap_domain (Temporal::TimeDomain, Temporal::TimeDomain);
|
||||
|
||||
protected:
|
||||
|
||||
virtual bool can_trim_start_before_source_start () const {
|
||||
|
@ -114,7 +114,7 @@ public:
|
||||
|
||||
const DataType& data_type () const { return _type; }
|
||||
Temporal::TimeDomain time_domain() const;
|
||||
void globally_change_time_domain (Temporal::TimeDomain from, Temporal::TimeDomain to);
|
||||
virtual void globally_change_time_domain (Temporal::TimeDomain from, Temporal::TimeDomain to);
|
||||
|
||||
/** How the region parameters play together:
|
||||
*
|
||||
|
@ -770,9 +770,13 @@ Location::globally_change_time_domain (Temporal::TimeDomain from, Temporal::Time
|
||||
|
||||
if (_start.time_domain() == from) {
|
||||
|
||||
std::cerr << "switching location [" << name() << "] from " << _start;
|
||||
|
||||
_start.set_time_domain (to);
|
||||
_end.set_time_domain (to);
|
||||
|
||||
std::cerr << " to " << _start << std::endl;
|
||||
|
||||
domain_swap->add (_start);
|
||||
domain_swap->add (_end);
|
||||
} else {
|
||||
|
@ -1772,3 +1772,101 @@ MidiModel::control_list_marked_dirty ()
|
||||
|
||||
ContentsChanged (); /* EMIT SIGNAL */
|
||||
}
|
||||
|
||||
void
|
||||
MidiModel::create_mapping_stash (Temporal::Beats const & src_pos_offset)
|
||||
{
|
||||
using namespace Evoral;
|
||||
using namespace Temporal;
|
||||
|
||||
TempoMap::SharedPtr tmap (TempoMap::use());
|
||||
|
||||
if (!tempo_mapping_stash.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto const & n : notes()) {
|
||||
Event<Beats>& on (n->on_event());
|
||||
superclock_t audio_time = tmap->superclock_at (src_pos_offset + on.time());
|
||||
tempo_mapping_stash.insert (std::make_pair (&on, audio_time));
|
||||
|
||||
Event<Beats>& off (n->off_event());
|
||||
audio_time = tmap->superclock_at (src_pos_offset + off.time());
|
||||
tempo_mapping_stash.insert (std::make_pair (&off, audio_time));
|
||||
|
||||
}
|
||||
|
||||
for (auto const & s : sysexes()) {
|
||||
superclock_t audio_time = tmap->superclock_at (src_pos_offset + s->time());
|
||||
tempo_mapping_stash.insert (std::make_pair ((void*)s.get(), audio_time));
|
||||
}
|
||||
|
||||
for (uint8_t chan = 0; chan < 16; ++chan) {
|
||||
for (auto const & p : pitches(chan)) {
|
||||
superclock_t audio_time = tmap->superclock_at (src_pos_offset + p->time());
|
||||
tempo_mapping_stash.insert (std::make_pair ((void*) &p, audio_time));
|
||||
}
|
||||
}
|
||||
|
||||
for (auto & c : controls()) {
|
||||
std::shared_ptr<Evoral::ControlList> l = c.second->list();
|
||||
if (l) {
|
||||
l->set_time_domain (AudioTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MidiModel::rebuild_from_mapping_stash (Temporal::Beats const & src_pos_offset)
|
||||
{
|
||||
using namespace Evoral;
|
||||
using namespace Temporal;
|
||||
|
||||
if (tempo_mapping_stash.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
TempoMap::SharedPtr tmap (TempoMap::use());
|
||||
|
||||
for (auto & n : notes()) {
|
||||
|
||||
Event<Beats>& on (n->on_event());
|
||||
Event<Beats>& off (n->off_event());
|
||||
|
||||
TempoMappingStash::iterator tms (tempo_mapping_stash.find (&on));
|
||||
assert (tms != tempo_mapping_stash.end());
|
||||
Beats beat_time (tmap->quarters_at_superclock (tms->second) - src_pos_offset);
|
||||
on.set_time (beat_time);
|
||||
|
||||
tms = tempo_mapping_stash.find (&off);
|
||||
assert (tms != tempo_mapping_stash.end());
|
||||
beat_time = tmap->quarters_at_superclock (tms->second) - src_pos_offset;
|
||||
off.set_time (beat_time);
|
||||
|
||||
}
|
||||
|
||||
for (auto & s : sysexes()) {
|
||||
TempoMappingStash::iterator tms (tempo_mapping_stash.find ((void*) &s));
|
||||
assert (tms != tempo_mapping_stash.end());
|
||||
Beats beat_time (tmap->quarters_at_superclock (tms->second) - src_pos_offset);
|
||||
s->set_time (beat_time);
|
||||
}
|
||||
|
||||
for (uint8_t chan = 0; chan < 16; ++chan) {
|
||||
for (auto & p : pitches(chan)) {
|
||||
TempoMappingStash::iterator tms (tempo_mapping_stash.find ((void*) &p));
|
||||
assert (tms != tempo_mapping_stash.end());
|
||||
Beats beat_time (tmap->quarters_at_superclock (tms->second) - src_pos_offset);
|
||||
p->set_time (beat_time);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto & c : controls()) {
|
||||
std::shared_ptr<Evoral::ControlList> l = c.second->list();
|
||||
if (l) {
|
||||
l->set_time_domain (BeatTime);
|
||||
}
|
||||
}
|
||||
|
||||
tempo_mapping_stash.clear ();
|
||||
}
|
||||
|
@ -612,3 +612,25 @@ MidiRegion::merge (std::shared_ptr<MidiRegion const> other_region)
|
||||
|
||||
set_length (max (length(), position().distance (other_region->end())));
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegion::swap_domain (Temporal::TimeDomain from, Temporal::TimeDomain to)
|
||||
{
|
||||
if (from == Temporal::BeatTime) {
|
||||
model()->create_mapping_stash (source_position().beats());
|
||||
} else {
|
||||
model()->rebuild_from_mapping_stash (source_position().beats());
|
||||
|
||||
_model_changed_connection.disconnect ();
|
||||
model()->ContentsChanged ();
|
||||
model()->ContentsChanged.connect_same_thread (_model_changed_connection, boost::bind (&MidiRegion::model_contents_changed, this));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegion::globally_change_time_domain (Temporal::TimeDomain from, Temporal::TimeDomain to)
|
||||
{
|
||||
Region::globally_change_time_domain (from, to);
|
||||
swap_domain (from, to);
|
||||
Temporal::domain_swap->add (*this);
|
||||
}
|
||||
|
@ -2199,7 +2199,9 @@ Region::globally_change_time_domain (Temporal::TimeDomain from, Temporal::TimeDo
|
||||
|
||||
if (_length.val().time_domain() == from) {
|
||||
timecnt_t& l (_length.non_const_val());
|
||||
std::cerr << "old domain after GCTD " << _length.val() << std::endl;
|
||||
l.set_time_domain (to);
|
||||
Temporal::domain_swap->add (l);
|
||||
std::cerr << "new domain after GCTD " << _length.val() << std::endl;
|
||||
}
|
||||
}
|
||||
|
@ -302,6 +302,8 @@ Session::any_duration_to_samples (samplepos_t position, AnyTime const & duration
|
||||
void
|
||||
Session::globally_change_time_domain (Temporal::TimeDomain from, Temporal::TimeDomain to)
|
||||
{
|
||||
std::cerr << "GCTD from " << from << " to " << to << std::endl;
|
||||
|
||||
{
|
||||
std::shared_ptr<RouteList const> rl (routes.reader());
|
||||
|
||||
|
@ -4882,6 +4882,10 @@ DomainSwapInformation::undo ()
|
||||
p->set_time_domain (previous);
|
||||
}
|
||||
|
||||
for (auto & tt : time_things) {
|
||||
tt->swap_domain (previous == AudioTime ? BeatTime : AudioTime, previous);
|
||||
}
|
||||
|
||||
clear ();
|
||||
}
|
||||
|
||||
|
@ -1273,6 +1273,7 @@ class LIBTEMPORAL_API DomainSwapInformation {
|
||||
|
||||
void add (timecnt_t& t) { counts.push_back (&t); }
|
||||
void add (timepos_t& p) { positions.push_back (&p); }
|
||||
void add (TimeThing& tt) { time_things.push_back (&tt); }
|
||||
void clear ();
|
||||
|
||||
private:
|
||||
@ -1280,6 +1281,7 @@ class LIBTEMPORAL_API DomainSwapInformation {
|
||||
|
||||
std::vector<timecnt_t*> counts;
|
||||
std::vector<timepos_t*> positions;
|
||||
std::vector<TimeThing*> time_things;
|
||||
TimeDomain previous;
|
||||
|
||||
void undo ();
|
||||
|
@ -124,6 +124,11 @@ enum RoundMode {
|
||||
|
||||
extern void setup_enum_writer ();
|
||||
|
||||
struct LIBTEMPORAL_API TimeThing {
|
||||
virtual ~TimeThing() {}
|
||||
virtual void swap_domain (Temporal::TimeDomain from, Temporal::TimeDomain to) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
std::ostream& operator<< (std::ostream& o, Temporal::ratio_t const & r);
|
||||
|
Loading…
Reference in New Issue
Block a user