intial steps for adding legacy (v0-5) tempo map deserialization support

This commit is contained in:
Paul Davis
2021-03-23 15:22:28 -06:00
parent 344dd26788
commit b84402f4f8
2 changed files with 403 additions and 3 deletions

View File

@@ -19,6 +19,8 @@
#include <algorithm>
#include <vector>
#include <inttypes.h>
#include "pbd/error.h"
#include "pbd/i18n.h"
#include "pbd/compose.h"
@@ -1119,7 +1121,7 @@ TempoMap::reset_starting_at (superclock_t sc)
}
if (advance_meter && (m != _meters.end())) {
++m;
++m;
}
if (advance_tempo && (t != _tempos.end())) {
++t;
@@ -2252,8 +2254,12 @@ TempoMap::get_state ()
}
int
TempoMap::set_state (XMLNode const & node, int /*version*/)
TempoMap::set_state (XMLNode const & node, int version)
{
if (version <= 3000) {
return set_state_3x (node);
}
/* global map properties */
/* XXX this should probably be at the global level in the session file because it affects a lot more than just the tempo map, potentially */
@@ -3177,3 +3183,372 @@ TempoMap::midi_clock_beat_at_or_after (samplepos_t const pos, samplepos_t& clk_p
assert (clk_pos >= pos);
}
/******** OLD STATE LOADING CODE SECTION *************/
static bool
bbt_time_to_string (const BBT_Time& bbt, std::string& str)
{
char buf[256];
int retval = snprintf (buf, sizeof(buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, bbt.bars, bbt.beats,
bbt.ticks);
if (retval <= 0 || retval >= (int)sizeof(buf)) {
return false;
}
str = buf;
return true;
}
static bool
string_to_bbt_time (const std::string& str, BBT_Time& bbt)
{
if (sscanf (str.c_str (), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, &bbt.bars, &bbt.beats,
&bbt.ticks) == 3) {
return true;
}
return false;
}
int
TempoMap::parse_tempo_state_3x (const XMLNode& node, LegacyTempoState& lts)
{
BBT_Time bbt;
std::string start_bbt;
// _legacy_bbt.bars = 0; // legacy session check compars .bars != 0; default BBT_Time c'tor uses 1.
if (node.get_property ("start", start_bbt)) {
if (string_to_bbt_time (start_bbt, bbt)) {
/* legacy session - start used to be in bbt*/
// _legacy_bbt = bbt;
// set_pulse(-1.0);
info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
}
}
/* position is the only data we extract from older XML */
if (node.get_property ("frame", lts.sample)) {
error << _("Legacy tempo section XML does not have a \"frame\" node - map will be ignored") << endmsg;
return -1;
}
if (node.get_property ("beats-per-minute", lts.note_types_per_minute)) {
if (lts.note_types_per_minute < 0.0) {
error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
return -1;
}
}
if (node.get_property ("note-type", lts.note_type)) {
if (lts.note_type < 1.0) {
error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
return -1;
}
} else {
/* older session, make note type be quarter by default */
lts.note_type = 4.0;
}
if (!node.get_property ("clamped", lts.clamped)) {
lts.clamped = false;
}
if (node.get_property ("end-beats-per-minute", lts.end_note_types_per_minute)) {
if (lts.end_note_types_per_minute < 0.0) {
info << _("TempoSection XML node has an illegal \"end-beats-per-minute\" value") << endmsg;
return -1;
}
}
Tempo::Type old_type;
if (node.get_property ("tempo-type", old_type)) {
/* sessions with a tempo-type node contain no end-beats-per-minute.
if the legacy node indicates a constant tempo, simply fill this in with the
start tempo. otherwise we need the next neighbour to know what it will be.
*/
if (old_type == Tempo::Constant) {
lts.end_note_types_per_minute = lts.note_types_per_minute;
} else {
lts.end_note_types_per_minute = -1.0;
}
}
if (!node.get_property ("active", lts.active)) {
warning << _("TempoSection XML node has no \"active\" property") << endmsg;
lts.active = true;
}
return 0;
}
int
TempoMap::parse_meter_state_3x (const XMLNode& node, LegacyMeterState& lms)
{
std::string bbt_str;
if (node.get_property ("start", bbt_str)) {
if (string_to_bbt_time (bbt_str, lms.bbt)) {
/* legacy session - start used to be in bbt*/
info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
// set_pulse (-1.0);
} else {
error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
}
}
/* position is the only data we extract from older XML */
if (node.get_property ("frame", lms.sample)) {
error << _("Legacy tempo section XML does not have a \"frame\" node - map will be ignored") << endmsg;
return -1;
}
if (node.get_property ("beat", lms.beat)) {
lms.beat = 0.0;
}
if (node.get_property ("bbt", bbt_str)) {
if (!string_to_bbt_time (bbt_str, lms.bbt)) {
error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
return -1;
}
} else {
warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
}
/* beats-per-bar is old; divisions-per-bar is new */
if (!node.get_property ("divisions-per-bar", lms.divisions_per_bar)) {
if (!node.get_property ("beats-per-bar", lms.divisions_per_bar)) {
error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
return -1;
}
}
if (lms.divisions_per_bar < 0.0) {
error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
return -1;
}
if (!node.get_property ("note-type", lms.note_type)) {
error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
return -1;
}
if (lms.note_type < 0.0) {
error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
return -1;
}
return 0;
}
int
TempoMap::set_state_3x (const XMLNode& node)
{
XMLNodeList nlist;
XMLNodeConstIterator niter;
_tempos.clear ();
_meters.clear ();
_points.clear ();
nlist = node.children();
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
XMLNode* child = *niter;
if (child->name() == Tempo::xml_node_name) {
LegacyTempoState lts;
if (parse_tempo_state_3x (*child, lts)) {
error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
break;
}
Tempo t (lts.note_types_per_minute,
lts.end_note_types_per_minute,
lts.note_type);
set_tempo (t, timepos_t (lts.sample));
} else if (child->name() == Meter::xml_node_name) {
LegacyMeterState lms;
if (parse_meter_state_3x (*child, lms)) {
error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
break;
}
Meter m (lms.divisions_per_bar, lms.note_type);
set_meter (m, timepos_t (lms.sample));
}
}
#if 0
/* check for legacy sessions where bbt was the base musical unit for tempo */
for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
TempoSection* t;
if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
if (t->legacy_bbt().bars != 0) {
fix_legacy_session();
break;
}
if (t->end_note_types_per_minute() < 0.0) {
fix_legacy_end_session();
break;
}
}
}
if (niter == nlist.end()) {
MetricSectionSorter cmp;
_metrics.sort (cmp);
}
#endif
#if 0
/* check for multiple tempo/meters at the same location, which
ardour2 somehow allowed.
*/
{
Tempos::iterator prev = _tempos.end();
for (Tempos::iterator i = _tempos.begin(); i != _tempos.end(); ++i) {
if (prev != _tempos.end()) {
MeterSection* ms;
MeterSection* prev_m;
TempoSection* ts;
TempoSection* prev_t;
if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
if (prev_m->beat() == ms->beat()) {
cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->beat()) << endmsg;
error << string_compose (_("Multiple meter definitions found at %1"), prev_m->beat()) << endmsg;
return -1;
}
} else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
if (prev_t->pulse() == ts->pulse()) {
cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
return -1;
}
}
}
prev = i;
}
#endif
return 0;
}
#if 0
void
TempoMap::fix_legacy_session ()
{
MeterSection* prev_m = 0;
TempoSection* prev_t = 0;
bool have_initial_t = false;
for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
MeterSection* m;
TempoSection* t;
if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
if (m->initial()) {
pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
m->set_beat (bbt);
m->set_pulse (0.0);
m->set_minute (0.0);
m->set_position_lock_style (AudioTime);
prev_m = m;
continue;
}
if (prev_m) {
pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
+ (m->bbt().beats - 1)
+ (m->bbt().ticks / BBT_Time::ticks_per_beat)
, m->bbt());
m->set_beat (start);
const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
+ (m->bbt().beats - 1)
+ (m->bbt().ticks / BBT_Time::ticks_per_beat);
m->set_pulse (start_beat / prev_m->note_divisor());
}
prev_m = m;
} else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
if (!t->active()) {
continue;
}
/* Ramp type never existed in the era of this tempo section */
t->set_end_note_types_per_minute (t->note_types_per_minute());
if (t->initial()) {
t->set_pulse (0.0);
t->set_minute (0.0);
t->set_position_lock_style (AudioTime);
prev_t = t;
have_initial_t = true;
continue;
}
if (prev_t) {
/* some 4.x sessions have no initial (non-movable) tempo. */
if (!have_initial_t) {
prev_t->set_pulse (0.0);
prev_t->set_minute (0.0);
prev_t->set_position_lock_style (AudioTime);
prev_t->set_initial (true);
prev_t->set_locked_to_meter (true);
have_initial_t = true;
}
const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
+ (t->legacy_bbt().beats - 1)
+ (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
if (prev_m) {
t->set_pulse (beat / prev_m->note_divisor());
} else {
/* really shouldn't happen but.. */
t->set_pulse (beat / 4.0);
}
}
prev_t = t;
}
}
}
void
TempoMap::fix_legacy_end_session ()
{
TempoSection* prev_t = 0;
for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
TempoSection* t;
if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
if (!t->active()) {
continue;
}
if (prev_t) {
if (prev_t->end_note_types_per_minute() < 0.0) {
prev_t->set_end_note_types_per_minute (t->note_types_per_minute());
}
}
prev_t = t;
}
}
if (prev_t) {
prev_t->set_end_note_types_per_minute (prev_t->note_types_per_minute());
}
}
#endif

View File

@@ -93,7 +93,7 @@ class LIBTEMPORAL_API Point {
}
};
struct ptr_sclock_comparator {
struct ptr_sclock_comparator {
bool operator() (Point const * a, Point const * b) const {
return a->sclock() < b->sclock();
}
@@ -849,6 +849,31 @@ class LIBTEMPORAL_API TempoMap : public PBD::StatefulDestructible
void remove_point (Point const &);
void copy_points (TempoMap const & other);
/* parsing legacy tempo maps */
struct LegacyTempoState
{
samplepos_t sample;
double note_types_per_minute;
double end_note_types_per_minute;
double note_type;
bool clamped;
bool active;
};
struct LegacyMeterState
{
samplepos_t sample;
BBT_Time bbt;
double beat;
double divisions_per_bar;
double note_type;
};
int parse_tempo_state_3x (const XMLNode& node, LegacyTempoState& lts);
int parse_meter_state_3x (const XMLNode& node, LegacyMeterState& lts);
int set_state_3x (XMLNode const &);
};
} /* end of namespace Temporal */