X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Ftempo.cc;h=5698b0afe74c57570ecdd1f55544c60b018746f1;hb=4e1f451520975868659f4c00d00883f5f1cd5805;hp=c0e52279e1bd7280633b38eddd6371ddb23471c7;hpb=fe13d08874f08b723df53116e5655c3d229a657e;p=ardour.git diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index c0e52279e1..5698b0afe7 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -15,7 +15,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id$ */ #include @@ -44,12 +43,17 @@ Tempo TempoMap::_default_tempo (120.0); const double Meter::ticks_per_beat = 1920.0; +double Tempo::frames_per_beat (nframes_t sr, const Meter& meter) const +{ + return ((60.0 * sr) / (_beats_per_minute * meter.note_divisor()/_note_type)); +} + /***********************************************************************/ double -Meter::frames_per_bar (const Tempo& tempo, jack_nframes_t sr) const +Meter::frames_per_bar (const Tempo& tempo, nframes_t sr) const { - return ((60.0 * sr * _beats_per_bar) / tempo.beats_per_minute()); + return ((60.0 * sr * _beats_per_bar) / (tempo.beats_per_minute() * _note_type/tempo.note_type())); } /***********************************************************************/ @@ -87,6 +91,16 @@ TempoSection::TempoSection (const XMLNode& node) error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg; throw failed_constructor(); } + + if ((prop = node.property ("note-type")) == 0) { + /* older session, make note type be quarter by default */ + _note_type = 4.0; + } else { + if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) { + error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg; + throw failed_constructor(); + } + } if ((prop = node.property ("movable")) == 0) { error << _("TempoSection XML node has no \"movable\" property") << endmsg; @@ -110,6 +124,8 @@ TempoSection::get_state() const root->add_property ("start", buf); snprintf (buf, sizeof (buf), "%f", _beats_per_minute); root->add_property ("beats-per-minute", buf); + snprintf (buf, sizeof (buf), "%f", _note_type); + root->add_property ("note-type", buf); snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no"); root->add_property ("movable", buf); @@ -200,19 +216,18 @@ struct MetricSectionSorter { } }; -TempoMap::TempoMap (jack_nframes_t fr) +TempoMap::TempoMap (nframes_t fr) { metrics = new Metrics; _frame_rate = fr; last_bbt_valid = false; BBT_Time start; - in_set_state = false; start.bars = 1; start.beats = 1; start.ticks = 0; - TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute()); + TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type()); MeterSection *m = new MeterSection (start, _default_meter.beats_per_bar(), _default_meter.note_divisor()); t->set_movable (false); @@ -222,8 +237,6 @@ TempoMap::TempoMap (jack_nframes_t fr) metrics->push_back (t); metrics->push_back (m); - - save_state (_("initial")); } TempoMap::~TempoMap () @@ -233,30 +246,49 @@ TempoMap::~TempoMap () int TempoMap::move_metric_section (MetricSection& section, const BBT_Time& when) { - if (when == section.start()) { + if (when == section.start() || !section.movable()) { return -1; } - if (!section.movable()) { - return 1; - } - - Glib::Mutex::Lock lm (lock); + Glib::RWLock::WriterLock lm (lock); MetricSectionSorter cmp; - BBT_Time corrected (when); - - if (dynamic_cast(§ion) != 0) { - if (corrected.beats > 1) { - corrected.beats = 1; - corrected.bars++; + + if (when.beats != 1) { + + /* position by audio frame, then recompute BBT timestamps from the audio ones */ + + nframes_t frame = frame_time (when); + // cerr << "nominal frame time = " << frame << endl; + + nframes_t prev_frame = round_to_type (frame, -1, Beat); + nframes_t next_frame = round_to_type (frame, 1, Beat); + + // cerr << "previous beat at " << prev_frame << " next at " << next_frame << endl; + + /* use the closest beat */ + + if ((frame - prev_frame) < (next_frame - frame)) { + frame = prev_frame; + } else { + frame = next_frame; } + + // cerr << "actual frame time = " << frame << endl; + section.set_frame (frame); + // cerr << "frame time = " << section.frame() << endl; + timestamp_metrics (false); + // cerr << "new BBT time = " << section.start() << endl; + metrics->sort (cmp); + + } else { + + /* positioned at bar start already, so just put it there */ + + section.set_start (when); + metrics->sort (cmp); + timestamp_metrics (true); } - corrected.ticks = 0; - section.set_start (corrected); - metrics->sort (cmp); - timestamp_metrics (); - save_state (_("move metric")); return 0; } @@ -265,7 +297,7 @@ void TempoMap::move_tempo (TempoSection& tempo, const BBT_Time& when) { if (move_metric_section (tempo, when) == 0) { - send_state_changed (Change (0)); + StateChanged (Change (0)); } } @@ -273,10 +305,9 @@ void TempoMap::move_meter (MeterSection& meter, const BBT_Time& when) { if (move_metric_section (meter, when) == 0) { - send_state_changed (Change (0)); + StateChanged (Change (0)); } } - void TempoMap::remove_tempo (const TempoSection& tempo) @@ -284,7 +315,7 @@ TempoMap::remove_tempo (const TempoSection& tempo) bool removed = false; { - Glib::Mutex::Lock lm (lock); + Glib::RWLock::WriterLock lm (lock); Metrics::iterator i; for (i = metrics->begin(); i != metrics->end(); ++i) { @@ -301,7 +332,7 @@ TempoMap::remove_tempo (const TempoSection& tempo) } if (removed) { - send_state_changed (Change (0)); + StateChanged (Change (0)); } } @@ -311,7 +342,7 @@ TempoMap::remove_meter (const MeterSection& tempo) bool removed = false; { - Glib::Mutex::Lock lm (lock); + Glib::RWLock::WriterLock lm (lock); Metrics::iterator i; for (i = metrics->begin(); i != metrics->end(); ++i) { @@ -325,28 +356,30 @@ TempoMap::remove_meter (const MeterSection& tempo) } } } - - if (removed) { - save_state (_("metric removed")); - } } if (removed) { - send_state_changed (Change (0)); + StateChanged (Change (0)); } } void -TempoMap::do_insert (MetricSection* section) +TempoMap::do_insert (MetricSection* section, bool with_bbt) { Metrics::iterator i; for (i = metrics->begin(); i != metrics->end(); ++i) { - if ((*i)->start() < section->start()) { - continue; + if (with_bbt) { + if ((*i)->start() < section->start()) { + continue; + } + } else { + if ((*i)->frame() < section->frame()) { + continue; + } } - + metrics->insert (i, section); break; } @@ -355,25 +388,34 @@ TempoMap::do_insert (MetricSection* section) metrics->insert (metrics->end(), section); } - timestamp_metrics (); + timestamp_metrics (with_bbt); } void TempoMap::add_tempo (const Tempo& tempo, BBT_Time where) { { - Glib::Mutex::Lock lm (lock); + Glib::RWLock::WriterLock lm (lock); /* new tempos always start on a beat */ where.ticks = 0; - do_insert (new TempoSection (where, tempo.beats_per_minute())); + do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), true); + } + + StateChanged (Change (0)); +} - save_state (_("add tempo")); +void +TempoMap::add_tempo (const Tempo& tempo, nframes_t where) +{ + { + Glib::RWLock::WriterLock lm (lock); + do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), false); } - send_state_changed (Change (0)); + StateChanged (Change (0)); } void @@ -382,29 +424,26 @@ TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement) bool replaced = false; { - Glib::Mutex::Lock lm (lock); + Glib::RWLock::WriterLock lm (lock); Metrics::iterator i; for (i = metrics->begin(); i != metrics->end(); ++i) { TempoSection *ts; if ((ts = dynamic_cast(*i)) != 0 && ts == &existing) { - - *((Tempo *) ts) = replacement; + + *((Tempo *) ts) = replacement; replaced = true; - timestamp_metrics (); + timestamp_metrics (true); + break; } } - - if (replaced) { - save_state (_("replace tempo")); - } } if (replaced) { - send_state_changed (Change (0)); + StateChanged (Change (0)); } } @@ -412,7 +451,7 @@ void TempoMap::add_meter (const Meter& meter, BBT_Time where) { { - Glib::Mutex::Lock lm (lock); + Glib::RWLock::WriterLock lm (lock); /* a new meter always starts a new bar on the first beat. so round the start time appropriately. remember that @@ -430,12 +469,21 @@ TempoMap::add_meter (const Meter& meter, BBT_Time where) where.ticks = 0; - do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor())); + do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), true); + } + + StateChanged (Change (0)); +} - save_state (_("add meter")); +void +TempoMap::add_meter (const Meter& meter, nframes_t where) +{ + { + Glib::RWLock::WriterLock lm (lock); + do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), false); } - send_state_changed (Change (0)); + StateChanged (Change (0)); } void @@ -444,7 +492,7 @@ TempoMap::replace_meter (MeterSection& existing, const Meter& replacement) bool replaced = false; { - Glib::Mutex::Lock lm (lock); + Glib::RWLock::WriterLock lm (lock); Metrics::iterator i; for (i = metrics->begin(); i != metrics->end(); ++i) { @@ -454,21 +502,75 @@ TempoMap::replace_meter (MeterSection& existing, const Meter& replacement) *((Meter*) ms) = replacement; replaced = true; - timestamp_metrics (); + timestamp_metrics (true); break; } } - - if (replaced) { - save_state (_("replaced meter")); - } } if (replaced) { - send_state_changed (Change (0)); + StateChanged (Change (0)); } } +void +TempoMap::change_initial_tempo (double beats_per_minute, double note_type) +{ + Tempo newtempo (beats_per_minute, note_type); + TempoSection* t; + + for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) { + if ((t = dynamic_cast (*i)) != 0) { + *((Tempo*) t) = newtempo; + StateChanged (Change (0)); + break; + } + } +} + +void +TempoMap::change_existing_tempo_at (nframes_t where, double beats_per_minute, double note_type) +{ + Tempo newtempo (beats_per_minute, note_type); + + TempoSection* prev; + TempoSection* first; + Metrics::iterator i; + + /* find the TempoSection immediately preceding "where" + */ + + for (first = 0, i = metrics->begin(), prev = 0; i != metrics->end(); ++i) { + + if ((*i)->frame() > where) { + break; + } + + TempoSection* t; + + if ((t = dynamic_cast(*i)) != 0) { + if (!first) { + first = t; + } + prev = t; + } + } + + if (!prev) { + if (!first) { + error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg; + return; + } + + prev = first; + } + + /* reset */ + + *((Tempo*)prev) = newtempo; + StateChanged (Change (0)); +} + const MeterSection& TempoMap::first_meter () const { @@ -502,47 +604,117 @@ TempoMap::first_tempo () const } void -TempoMap::timestamp_metrics () +TempoMap::timestamp_metrics (bool use_bbt) { Metrics::iterator i; const Meter* meter; const Tempo* tempo; Meter *m; Tempo *t; - jack_nframes_t current; - jack_nframes_t section_frames; - BBT_Time start; - BBT_Time end; meter = &first_meter (); tempo = &first_tempo (); - current = 0; - for (i = metrics->begin(); i != metrics->end(); ++i) { + if (use_bbt) { + + // cerr << "\n\n\n ######################\nTIMESTAMP via BBT ##############\n" << endl; + + nframes_t current = 0; + nframes_t section_frames; + BBT_Time start; + BBT_Time end; + + for (i = metrics->begin(); i != metrics->end(); ++i) { + + end = (*i)->start(); + + section_frames = count_frames_between_metrics (*meter, *tempo, start, end); + + current += section_frames; + + start = end; + + (*i)->set_frame (current); + + if ((t = dynamic_cast(*i)) != 0) { + tempo = t; + } else if ((m = dynamic_cast(*i)) != 0) { + meter = m; + } else { + fatal << _("programming error: unhandled MetricSection type") << endmsg; + /*NOTREACHED*/ + } + } + + } else { + + // cerr << "\n\n\n ######################\nTIMESTAMP via AUDIO ##############\n" << endl; + + bool first = true; + MetricSection* prev = 0; + + for (i = metrics->begin(); i != metrics->end(); ++i) { + + BBT_Time bbt; + Metric metric (*meter, *tempo); + + if (prev) { + metric.set_start (prev->start()); + metric.set_frame (prev->frame()); + } else { + // metric will be at frames=0 bbt=1|1|0 by default + // which is correct for our purpose + } - end = (*i)->start(); + bbt_time_with_metric ((*i)->frame(), bbt, metric); - section_frames = count_frames_between_metrics (*meter, *tempo, start, end); + // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => "; + - current += section_frames; + if (first) { + first = false; + } else { + + if (bbt.ticks > Meter::ticks_per_beat/2) { + /* round up to next beat */ + bbt.beats += 1; + } - start = end; + bbt.ticks = 0; - (*i)->set_frame (current); + if (bbt.beats != 1) { + /* round up to next bar */ + bbt.bars += 1; + bbt.beats = 1; + } + } + + //s cerr << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << endl; + + (*i)->set_start (bbt); - if ((t = dynamic_cast(*i)) != 0) { - tempo = t; - } else if ((m = dynamic_cast(*i)) != 0) { - meter = m; - } else { - fatal << _("programming error: unhandled MetricSection type") << endmsg; - /*NOTREACHED*/ + if ((t = dynamic_cast(*i)) != 0) { + tempo = t; + // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <(*i)) != 0) { + meter = m; + // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() < (uint32_t)floor(beats_per_bar) && + bbt.ticks >= ticks_on_last_beat) { + bbt.ticks -= ticks_on_last_beat; + bbt.beats = 0; + bbt.bars++; + } + + bbt.beats++; // correction for 1-based counting, see above for matching operation. + + // cerr << "-----\t RETURN " << bbt << endl; } - -jack_nframes_t +nframes_t TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) const { - - /* for this to work with fractional measure types, start and end have to "legal" BBT types, - that means that the beats and ticks should be inside a bar + /* for this to work with fractional measure types, start and end have to be "legal" BBT types, + that means that the beats and ticks should be inside a bar */ + nframes_t frames = 0; + nframes_t start_frame = 0; + nframes_t end_frame = 0; - jack_nframes_t frames = 0; - jack_nframes_t start_frame = 0; - jack_nframes_t end_frame = 0; - - Metric m = metric_at(start); + Metric m = metric_at (start); uint32_t bar_offset = start.bars - m.start().bars; @@ -686,7 +861,7 @@ TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) con + start.ticks/Meter::ticks_per_beat; - start_frame = m.frame() + (jack_nframes_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate)); + start_frame = m.frame() + (nframes_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter())); m = metric_at(end); @@ -695,7 +870,7 @@ TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) con beat_offset = bar_offset * m.meter().beats_per_bar() - (m.start().beats -1) + (end.beats - 1) + end.ticks/Meter::ticks_per_beat; - end_frame = m.frame() + (jack_nframes_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate)); + end_frame = m.frame() + (nframes_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter())); frames = end_frame - start_frame; @@ -703,12 +878,12 @@ TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) con } -jack_nframes_t +nframes_t TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const { - /*this is used in timestamping the metrics by actually counting the beats */ + /* this is used in timestamping the metrics by actually counting the beats */ - jack_nframes_t frames = 0; + nframes_t frames = 0; uint32_t bar = start.bars; double beat = (double) start.beats; double beats_counted = 0; @@ -716,7 +891,7 @@ TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, double beat_frames = 0; beats_per_bar = meter.beats_per_bar(); - beat_frames = tempo.frames_per_beat (_frame_rate); + beat_frames = tempo.frames_per_beat (_frame_rate,meter); frames = 0; @@ -726,24 +901,34 @@ TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, beat = 1; ++bar; ++beats_counted; - } else { - ++beat; - ++beats_counted; + if (beat > beats_per_bar) { + /* this is a fractional beat at the end of a fractional bar - so it should only count for the fraction */ + so it should only count for the fraction + */ + beats_counted -= (ceil(beats_per_bar) - beats_per_bar); } + + } else { + ++beat; + ++beats_counted; } } + + // cerr << "Counted " << beats_counted << " from " << start << " to " << end + // << " bpb were " << beats_per_bar + // << " fpb was " << beat_frames + // << endl; - frames = (jack_nframes_t) floor (beats_counted * beat_frames); + frames = (nframes_t) floor (beats_counted * beat_frames); return frames; } -jack_nframes_t +nframes_t TempoMap::frame_time (const BBT_Time& bbt) const { BBT_Time start ; /* 1|1|0 */ @@ -751,27 +936,27 @@ TempoMap::frame_time (const BBT_Time& bbt) const return count_frames_between ( start, bbt); } -jack_nframes_t -TempoMap::bbt_duration_at (jack_nframes_t pos, const BBT_Time& bbt, int dir) const +nframes_t +TempoMap::bbt_duration_at (nframes_t pos, const BBT_Time& bbt, int dir) const { - jack_nframes_t frames = 0; + nframes_t frames = 0; BBT_Time when; bbt_time(pos,when); { - Glib::Mutex::Lock lm (lock); + Glib::RWLock::ReaderLock lm (lock); frames = bbt_duration_at_unlocked (when, bbt,dir); } return frames; } -jack_nframes_t +nframes_t TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) const { - jack_nframes_t frames = 0; + nframes_t frames = 0; double beats_per_bar; BBT_Time result; @@ -894,80 +1079,57 @@ TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, i -jack_nframes_t -TempoMap::round_to_bar (jack_nframes_t fr, int dir) +nframes_t +TempoMap::round_to_bar (nframes_t fr, int dir) { - Glib::Mutex::Lock lm (lock); - return round_to_type (fr, dir, Bar); + { + Glib::RWLock::ReaderLock lm (lock); + return round_to_type (fr, dir, Bar); + } } -jack_nframes_t -TempoMap::round_to_beat (jack_nframes_t fr, int dir) +nframes_t +TempoMap::round_to_beat (nframes_t fr, int dir) { - Glib::Mutex::Lock lm (lock); - return round_to_type (fr, dir, Beat); + { + Glib::RWLock::ReaderLock lm (lock); + return round_to_type (fr, dir, Beat); + } } -jack_nframes_t +nframes_t -TempoMap::round_to_beat_subdivision (jack_nframes_t fr, int sub_num) +TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num) { - Glib::Mutex::Lock lm (lock); - TempoMap::BBTPointList::iterator i; - TempoMap::BBTPointList *more_zoomed_bbt_points; - jack_nframes_t frame_one_beats_worth; - jack_nframes_t pos = 0; - jack_nframes_t next_pos = 0 ; - double tempo = 1; - double frames_one_subdivisions_worth; - bool fr_has_changed = false; - - int n; - - frame_one_beats_worth = (jack_nframes_t) ::floor ((double) _frame_rate * 60 / 20 ); //one beat @ 20 bpm - more_zoomed_bbt_points = get_points((fr >= frame_one_beats_worth) ? - fr - frame_one_beats_worth : 0, fr+frame_one_beats_worth ); - - if (more_zoomed_bbt_points == 0 || more_zoomed_bbt_points->empty()) { - return fr; - } - for (i = more_zoomed_bbt_points->begin(); i != more_zoomed_bbt_points->end(); i++) { - if ((*i).frame <= fr) { - pos = (*i).frame; - tempo = (*i).tempo->beats_per_minute(); - - } else { - i++; - next_pos = (*i).frame; - break; - } - } - frames_one_subdivisions_worth = ((double) _frame_rate * 60 / (sub_num * tempo)); - - for (n = sub_num; n > 0; n--) { - if (fr >= (pos + ((n - 0.5) * frames_one_subdivisions_worth))) { - fr = (jack_nframes_t) round(pos + (n * frames_one_subdivisions_worth)); - if (fr > next_pos) { - fr = next_pos; //take care of fractional beats that don't match the subdivision asked - } - fr_has_changed = true; - break; - } - } + BBT_Time the_beat; + uint32_t ticks_one_half_subdivisions_worth; + uint32_t ticks_one_subdivisions_worth; + + bbt_time(fr, the_beat); + + ticks_one_subdivisions_worth = (uint32_t)Meter::ticks_per_beat / sub_num; + ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2; - if (!fr_has_changed) { - fr = pos; + if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) { + uint32_t difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth); + if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) { + the_beat.beats++; + the_beat.ticks += difference; + the_beat.ticks -= (uint32_t)Meter::ticks_per_beat; + } else { + the_beat.ticks += difference; + } + } else { + the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth; } - delete more_zoomed_bbt_points; - return fr ; + return frame_time (the_beat); } -jack_nframes_t - -TempoMap::round_to_type (jack_nframes_t frame, int dir, BBTPointType type) +nframes_t +TempoMap::round_to_type (nframes_t frame, int dir, BBTPointType type) { Metric metric = metric_at (frame); BBT_Time bbt; @@ -978,16 +1140,16 @@ TempoMap::round_to_type (jack_nframes_t frame, int dir, BBTPointType type) case Bar: if (dir < 0) { /* relax */ - } else if (dir > 0) { if (bbt.beats > 0) { bbt.bars++; + } else if (metric.frame() < frame) { + bbt.bars++; } } else { if (bbt.beats > metric.meter().beats_per_bar()/2) { bbt.bars++; } - } bbt.beats = 1; bbt.ticks = 0; @@ -999,6 +1161,8 @@ TempoMap::round_to_type (jack_nframes_t frame, int dir, BBTPointType type) } else if (dir > 0) { if (bbt.ticks > 0) { bbt.beats++; + } else if (metric.frame() < frame) { + bbt.beats++; } } else { if (bbt.ticks >= (Meter::ticks_per_beat/2)) { @@ -1013,12 +1177,17 @@ TempoMap::round_to_type (jack_nframes_t frame, int dir, BBTPointType type) break; } - + + /* + cerr << "for " << frame << " round to " << bbt << " using " + << metric.start() + << endl; + */ return metric.frame() + count_frames_between (metric.start(), bbt); } TempoMap::BBTPointList * -TempoMap::get_points (jack_nframes_t lower, jack_nframes_t upper) const +TempoMap::get_points (nframes_t lower, nframes_t upper) const { Metrics::const_iterator i; @@ -1030,6 +1199,14 @@ TempoMap::get_points (jack_nframes_t lower, jack_nframes_t upper) const const TempoSection* t; uint32_t bar; uint32_t beat; + double beats_per_bar; + double beat_frame; + double beat_frames; + double frames_per_bar; + double delta_bars; + double delta_beats; + double dummy; + nframes_t limit; meter = &first_meter (); tempo = &first_tempo (); @@ -1058,6 +1235,10 @@ TempoMap::get_points (jack_nframes_t lower, jack_nframes_t upper) const Now start generating points. */ + beats_per_bar = meter->beats_per_bar (); + frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate); + beat_frames = tempo->frames_per_beat (_frame_rate, *meter); + if (meter->frame() > tempo->frame()) { bar = meter->start().bars; beat = meter->start().beats; @@ -1068,22 +1249,28 @@ TempoMap::get_points (jack_nframes_t lower, jack_nframes_t upper) const current = tempo->frame(); } - points = new BBTPointList; + /* initialize current to point to the bar/beat just prior to the + lower frame bound passed in. assumes that current is initialized + above to be on a beat. + */ + + delta_bars = (lower-current) / frames_per_bar; + delta_beats = modf(delta_bars, &dummy) * beats_per_bar; + current += (floor(delta_bars) * frames_per_bar) + (floor(delta_beats) * beat_frames); - do { - double beats_per_bar; - double beat_frame; - double beat_frames; - double frames_per_bar; - jack_nframes_t limit; + // adjust bars and beats too + bar += (uint32_t) (floor(delta_bars)); + beat += (uint32_t) (floor(delta_beats)); + + points = new BBTPointList; - beats_per_bar = meter->beats_per_bar (); - frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate); - beat_frames = tempo->frames_per_beat (_frame_rate); + do { if (i == metrics->end()) { limit = upper; + // cerr << "== limit set to end of request @ " << limit << endl; } else { + // cerr << "== limit set to next metric @ " << (*i)->frame() << endl; limit = (*i)->frame(); } @@ -1095,7 +1282,8 @@ TempoMap::get_points (jack_nframes_t lower, jack_nframes_t upper) const if (beat == 1) { if (current >= lower) { - points->push_back (BBTPoint (*meter, *tempo,(jack_nframes_t)rint(current), Bar, bar, 1)); + // cerr << "Add Bar at " << bar << "|1" << " @ " << current << endl; + points->push_back (BBTPoint (*meter, *tempo,(nframes_t)rint(current), Bar, bar, 1)); } } @@ -1106,7 +1294,8 @@ TempoMap::get_points (jack_nframes_t lower, jack_nframes_t upper) const while (beat <= ceil( beats_per_bar) && beat_frame < limit) { if (beat_frame >= lower) { - points->push_back (BBTPoint (*meter, *tempo, (jack_nframes_t) rint(beat_frame), Beat, bar, beat)); + // cerr << "Add Beat at " << bar << '|' << beat << " @ " << beat_frame << endl; + points->push_back (BBTPoint (*meter, *tempo, (nframes_t) rint(beat_frame), Beat, bar, beat)); } beat_frame += beat_frames; current+= beat_frames; @@ -1114,7 +1303,11 @@ TempoMap::get_points (jack_nframes_t lower, jack_nframes_t upper) const beat++; } - if (beat > ceil(beats_per_bar) ) { + // cerr << "out of beats, @ end ? " << (i == metrics->end()) << " out of bpb ? " + // << (beat > ceil(beats_per_bar)) + // << endl; + + if (beat > ceil(beats_per_bar) || i != metrics->end()) { /* we walked an entire bar. its important to move `current' forward @@ -1132,10 +1325,17 @@ TempoMap::get_points (jack_nframes_t lower, jack_nframes_t upper) const so we subtract the possible extra fraction from the current */ - current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar); + if (beat > ceil (beats_per_bar)) { + /* next bar goes where the numbers suggest */ + current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar); + // cerr << "++ next bar from numbers\n"; + } else { + /* next bar goes where the next metric is */ + current = limit; + // cerr << "++ next bar at next metric\n"; + } bar++; beat = 1; - } } @@ -1160,6 +1360,13 @@ TempoMap::get_points (jack_nframes_t lower, jack_nframes_t upper) const beat = 1; } + current = (*i)->frame (); + // cerr << "loop around with current @ " << current << endl; + + beats_per_bar = meter->beats_per_bar (); + frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate); + beat_frames = tempo->frames_per_beat (_frame_rate, *meter); + ++i; } @@ -1168,8 +1375,35 @@ TempoMap::get_points (jack_nframes_t lower, jack_nframes_t upper) const return points; } +const TempoSection& +TempoMap::tempo_section_at (nframes_t frame) +{ + Glib::RWLock::ReaderLock lm (lock); + Metrics::iterator i; + TempoSection* prev = 0; + + for (i = metrics->begin(); i != metrics->end(); ++i) { + TempoSection* t; + + if ((t = dynamic_cast (*i)) != 0) { + + if ((*i)->frame() > frame) { + break; + } + + prev = t; + } + } + + if (prev == 0) { + fatal << endmsg; + } + + return *prev; +} + const Tempo& -TempoMap::tempo_at (jack_nframes_t frame) +TempoMap::tempo_at (nframes_t frame) { Metric m (metric_at (frame)); return m.tempo(); @@ -1177,7 +1411,7 @@ TempoMap::tempo_at (jack_nframes_t frame) const Meter& -TempoMap::meter_at (jack_nframes_t frame) +TempoMap::meter_at (nframes_t frame) { Metric m (metric_at (frame)); return m.meter(); @@ -1186,12 +1420,14 @@ TempoMap::meter_at (jack_nframes_t frame) XMLNode& TempoMap::get_state () { - Glib::Mutex::Lock lm (lock); Metrics::const_iterator i; XMLNode *root = new XMLNode ("TempoMap"); - for (i = metrics->begin(); i != metrics->end(); ++i) { - root->add_child_nocopy ((*i)->get_state()); + { + Glib::RWLock::ReaderLock lm (lock); + for (i = metrics->begin(); i != metrics->end(); ++i) { + root->add_child_nocopy ((*i)->get_state()); + } } return *root; @@ -1201,14 +1437,12 @@ int TempoMap::set_state (const XMLNode& node) { { - Glib::Mutex::Lock lm (lock); + Glib::RWLock::WriterLock lm (lock); XMLNodeList nlist; XMLNodeConstIterator niter; Metrics old_metrics (*metrics); - in_set_state = true; - metrics->clear(); nlist = node.children(); @@ -1246,22 +1480,11 @@ TempoMap::set_state (const XMLNode& node) MetricSectionSorter cmp; metrics->sort (cmp); - timestamp_metrics (); + timestamp_metrics (true); } - - in_set_state = false; } - /* This state needs to be saved. This string will never be a part of the - object's history though, because the allow_save flag is false during - session load. This state will eventually be tagged "initial state", - by a call to StateManager::allow_save from Session::set_state. - - If this state is not saved, there is no way to reach it through undo actions. - */ - save_state(_("load XML data")); - - send_state_changed (Change (0)); + StateChanged (Change (0)); return 0; } @@ -1275,7 +1498,7 @@ TempoMap::dump (std::ostream& o) const for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) { if ((t = dynamic_cast(*i)) != 0) { - o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM at " << t->start() << " frame= " << t->frame() << " (move? " + o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM (denom = " << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (move? " << t->movable() << ')' << endl; } else if ((m = dynamic_cast(*i)) != 0) { o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame() @@ -1284,65 +1507,32 @@ TempoMap::dump (std::ostream& o) const } } -UndoAction -TempoMap::get_memento () const -{ - return sigc::bind (mem_fun (*(const_cast (this)), &StateManager::use_state), _current_state_id); -} - -Change -TempoMap::restore_state (StateManager::State& state) +int +TempoMap::n_tempos() const { - Glib::Mutex::Lock lm (lock); - - TempoMapState* tmstate = dynamic_cast (&state); + Glib::RWLock::ReaderLock lm (lock); + int cnt = 0; - /* We can't just set the metrics pointer to the address of the metrics list - stored in the state, cause this would ruin this state for restoring in - the future. If they have the same address, they are the same list. - Thus we need to copy all the elements from the state metrics list to the - current metrics list. - */ - metrics->clear(); - for (Metrics::iterator i = tmstate->metrics->begin(); i != tmstate->metrics->end(); ++i) { - TempoSection *ts; - MeterSection *ms; - - if ((ts = dynamic_cast(*i)) != 0) { - metrics->push_back (new TempoSection (*ts)); - } else if ((ms = dynamic_cast(*i)) != 0) { - metrics->push_back (new MeterSection (*ms)); + for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) { + if (dynamic_cast(*i) != 0) { + cnt++; } } - - last_bbt_valid = false; - return Change (0); + return cnt; } -StateManager::State* -TempoMap::state_factory (std::string why) const +int +TempoMap::n_meters() const { - TempoMapState* state = new TempoMapState (why); + Glib::RWLock::ReaderLock lm (lock); + int cnt = 0; - for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) { - TempoSection *ts; - MeterSection *ms; - - if ((ts = dynamic_cast(*i)) != 0) { - state->metrics->push_back (new TempoSection (*ts)); - } else if ((ms = dynamic_cast(*i)) != 0) { - state->metrics->push_back (new MeterSection (*ms)); + for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) { + if (dynamic_cast(*i) != 0) { + cnt++; } } - - return state; -} -void -TempoMap::save_state (std::string why) -{ - if (!in_set_state) { - StateManager::save_state (why); - } + return cnt; }