2 Copyright (C) 2000-2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include <glibmm/thread.h>
28 #include "pbd/xml++.h"
29 #include "evoral/types.hpp"
30 #include "ardour/debug.h"
31 #include "ardour/tempo.h"
32 #include "ardour/utils.h"
38 using namespace ARDOUR;
41 using Timecode::BBT_Time;
43 /* _default tempo is 4/4 qtr=120 */
45 Meter TempoMap::_default_meter (4.0, 4.0);
46 Tempo TempoMap::_default_tempo (120.0);
49 Tempo::frames_per_beat (framecnt_t sr) const
51 return (60.0 * sr) / _beats_per_minute;
54 /***********************************************************************/
57 Meter::frames_per_division (const Tempo& tempo, framecnt_t sr) const
59 return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
63 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
65 return frames_per_division (tempo, sr) * _divisions_per_bar;
68 /***********************************************************************/
70 const string TempoSection::xml_state_node_name = "Tempo";
72 TempoSection::TempoSection (const XMLNode& node)
73 : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
75 const XMLProperty *prop;
77 LocaleGuard lg (X_("POSIX"));
79 if ((prop = node.property ("start")) == 0) {
80 error << _("TempoSection XML node has no \"start\" property") << endmsg;
81 throw failed_constructor();
84 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
88 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
89 throw failed_constructor();
94 if ((prop = node.property ("beats-per-minute")) == 0) {
95 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
96 throw failed_constructor();
99 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
100 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
101 throw failed_constructor();
104 if ((prop = node.property ("note-type")) == 0) {
105 /* older session, make note type be quarter by default */
108 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
109 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
110 throw failed_constructor();
114 if ((prop = node.property ("movable")) == 0) {
115 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
116 throw failed_constructor();
119 set_movable (string_is_affirmative (prop->value()));
121 if ((prop = node.property ("bar-offset")) == 0) {
124 if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
125 error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
126 throw failed_constructor();
132 TempoSection::get_state() const
134 XMLNode *root = new XMLNode (xml_state_node_name);
136 LocaleGuard lg (X_("POSIX"));
138 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
142 root->add_property ("start", buf);
143 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
144 root->add_property ("beats-per-minute", buf);
145 snprintf (buf, sizeof (buf), "%f", _note_type);
146 root->add_property ("note-type", buf);
147 // snprintf (buf, sizeof (buf), "%f", _bar_offset);
148 // root->add_property ("bar-offset", buf);
149 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
150 root->add_property ("movable", buf);
157 TempoSection::update_bar_offset_from_bbt (const Meter& m)
159 _bar_offset = ((start().beats - 1) * BBT_Time::ticks_per_bar_division + start().ticks) /
160 (m.divisions_per_bar() * BBT_Time::ticks_per_bar_division);
162 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, start(), m.divisions_per_bar()));
166 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
170 if (_bar_offset < 0.0) {
175 new_start.bars = start().bars;
177 double ticks = BBT_Time::ticks_per_bar_division * meter.divisions_per_bar() * _bar_offset;
178 new_start.beats = (uint32_t) floor(ticks/BBT_Time::ticks_per_bar_division);
179 new_start.ticks = (uint32_t) fmod (ticks, BBT_Time::ticks_per_bar_division);
181 /* remember the 1-based counting properties of beats */
182 new_start.beats += 1;
184 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
185 _bar_offset, meter.divisions_per_bar(), ticks, new_start.ticks, new_start.beats));
187 set_start (new_start);
190 /***********************************************************************/
192 const string MeterSection::xml_state_node_name = "Meter";
194 MeterSection::MeterSection (const XMLNode& node)
195 : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
197 const XMLProperty *prop;
199 LocaleGuard lg (X_("POSIX"));
201 if ((prop = node.property ("start")) == 0) {
202 error << _("MeterSection XML node has no \"start\" property") << endmsg;
203 throw failed_constructor();
206 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
210 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
211 throw failed_constructor();
216 /* beats-per-bar is old; divisions-per-bar is new */
218 if ((prop = node.property ("divisions-per-bar")) == 0) {
219 if ((prop = node.property ("beats-per-bar")) == 0) {
220 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
221 throw failed_constructor();
225 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
226 error << _("MeterSection XML node has an illegal \"beats-per-bar\" or \"divisions-per-bar\" value") << endmsg;
227 throw failed_constructor();
230 if ((prop = node.property ("note-type")) == 0) {
231 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
232 throw failed_constructor();
235 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
236 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
237 throw failed_constructor();
240 if ((prop = node.property ("movable")) == 0) {
241 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
242 throw failed_constructor();
245 set_movable (string_is_affirmative (prop->value()));
249 MeterSection::get_state() const
251 XMLNode *root = new XMLNode (xml_state_node_name);
253 LocaleGuard lg (X_("POSIX"));
255 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
259 root->add_property ("start", buf);
260 snprintf (buf, sizeof (buf), "%f", _note_type);
261 root->add_property ("note-type", buf);
262 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
263 root->add_property ("divisions-per-bar", buf);
264 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
265 root->add_property ("movable", buf);
270 /***********************************************************************/
272 struct MetricSectionSorter {
273 bool operator() (const MetricSection* a, const MetricSection* b) {
274 return a->start() < b->start();
278 TempoMap::TempoMap (framecnt_t fr)
280 metrics = new Metrics;
281 _map = new BBTPointList;
283 last_bbt_valid = false;
290 TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
291 MeterSection *m = new MeterSection (start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
293 t->set_movable (false);
294 m->set_movable (false);
296 /* note: frame time is correct (zero) for both of these */
298 metrics->push_back (t);
299 metrics->push_back (m);
302 TempoMap::~TempoMap ()
309 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
311 bool removed = false;
314 Glib::RWLock::WriterLock lm (lock);
317 for (i = metrics->begin(); i != metrics->end(); ++i) {
318 if (dynamic_cast<TempoSection*> (*i) != 0) {
319 if (tempo.frame() == (*i)->frame()) {
320 if ((*i)->movable()) {
329 if (removed && complete_operation) {
330 recompute_map (false, false);
334 if (removed && complete_operation) {
335 PropertyChanged (PropertyChange ());
340 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
342 bool removed = false;
345 Glib::RWLock::WriterLock lm (lock);
348 for (i = metrics->begin(); i != metrics->end(); ++i) {
349 if (dynamic_cast<MeterSection*> (*i) != 0) {
350 if (tempo.frame() == (*i)->frame()) {
351 if ((*i)->movable()) {
360 if (removed && complete_operation) {
361 recompute_map (true, false);
365 if (removed && complete_operation) {
366 PropertyChanged (PropertyChange ());
371 TempoMap::do_insert (MetricSection* section)
373 bool need_add = true;
375 assert (section->start().ticks == 0);
377 /* we only allow new meters to be inserted on beat 1 of an existing
381 if (dynamic_cast<MeterSection*>(section)) {
383 /* we need to (potentially) update the BBT times of tempo
384 sections based on this new meter.
387 if ((section->start().beats != 1) || (section->start().ticks != 0)) {
389 BBT_Time corrected = section->start();
393 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
394 section->start(), corrected) << endmsg;
396 section->set_start (corrected);
402 /* Look for any existing MetricSection that is of the same type and
403 at the same time as the new one, and remove it before adding
407 Metrics::iterator to_remove = metrics->end ();
409 for (i = metrics->begin(); i != metrics->end(); ++i) {
411 int const c = (*i)->compare (*section);
414 /* this section is before the one to be added; go back round */
417 /* this section is after the one to be added; there can't be any at the same time */
421 /* hacky comparison of type */
422 bool const iter_is_tempo = dynamic_cast<TempoSection*> (*i) != 0;
423 bool const insert_is_tempo = dynamic_cast<TempoSection*> (section) != 0;
425 if (iter_is_tempo == insert_is_tempo) {
427 if (!(*i)->movable()) {
429 /* can't (re)move this section, so overwrite it
432 if (!iter_is_tempo) {
433 *(dynamic_cast<MeterSection*>(*i)) = *(dynamic_cast<MeterSection*>(section));
435 *(dynamic_cast<TempoSection*>(*i)) = *(dynamic_cast<TempoSection*>(section));
446 if (to_remove != metrics->end()) {
447 /* remove the MetricSection at the same time as the one we are about to add */
448 metrics->erase (to_remove);
451 /* Add the given MetricSection */
454 for (i = metrics->begin(); i != metrics->end(); ++i) {
456 if ((*i)->compare (*section) < 0) {
460 metrics->insert (i, section);
464 if (i == metrics->end()) {
465 metrics->insert (metrics->end(), section);
471 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where)
473 const TempoSection& first (first_tempo());
476 remove_tempo (ts, false);
477 add_tempo (tempo, where);
480 Glib::RWLock::WriterLock lm (lock);
481 /* cannot move the first tempo section */
482 *((Tempo*)&first) = tempo;
483 recompute_map (false, false);
487 PropertyChanged (PropertyChange ());
491 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
494 Glib::RWLock::WriterLock lm (lock);
496 /* new tempos always start on a beat */
499 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type());
501 /* find the meter to use to set the bar offset of this
505 const Meter* meter = &first_meter();
507 /* as we start, we are *guaranteed* to have m.meter and m.tempo pointing
508 at something, because we insert the default tempo and meter during
509 TempoMap construction.
511 now see if we can find better candidates.
514 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
516 const MeterSection* m;
518 if (where < (*i)->start()) {
522 if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
527 ts->update_bar_offset_from_bbt (*meter);
533 recompute_map (false, false);
537 PropertyChanged (PropertyChange ());
541 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
543 const MeterSection& first (first_meter());
546 remove_meter (ms, false);
547 add_meter (meter, where);
550 Glib::RWLock::WriterLock lm (lock);
551 /* cannot move the first meter section */
552 *((Meter*)&first) = meter;
553 recompute_map (true, false);
557 PropertyChanged (PropertyChange ());
561 TempoMap::add_meter (const Meter& meter, BBT_Time where)
564 Glib::RWLock::WriterLock lm (lock);
566 /* a new meter always starts a new bar on the first beat. so
567 round the start time appropriately. remember that
568 `where' is based on the existing tempo map, not
569 the result after we insert the new meter.
573 if (where.beats != 1) {
578 /* new meters *always* start on a beat. */
581 do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()));
582 recompute_map (true, false);
587 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
592 PropertyChanged (PropertyChange ());
596 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
598 Tempo newtempo (beats_per_minute, note_type);
601 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
602 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
604 Glib::RWLock::WriterLock lm (lock);
605 *((Tempo*) t) = newtempo;
606 recompute_map (false, false);
608 PropertyChanged (PropertyChange ());
615 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
617 Tempo newtempo (beats_per_minute, note_type);
623 /* find the TempoSection immediately preceding "where"
626 for (first = 0, i = metrics->begin(), prev = 0; i != metrics->end(); ++i) {
628 if ((*i)->frame() > where) {
634 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
644 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
654 Glib::RWLock::WriterLock lm (lock);
655 /* cannot move the first tempo section */
656 *((Tempo*)prev) = newtempo;
657 recompute_map (false, false);
660 PropertyChanged (PropertyChange ());
664 TempoMap::first_meter () const
666 const MeterSection *m = 0;
668 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
669 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
674 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
680 TempoMap::first_tempo () const
682 const TempoSection *t = 0;
684 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
685 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
690 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
696 TempoMap::require_map_to (framepos_t pos)
698 Glib::RWLock::WriterLock lm (lock);
700 if (_map->empty() || _map->back().frame < pos) {
701 recompute_map (false, pos);
706 TempoMap::require_map_to (const BBT_Time& bbt)
708 Glib::RWLock::WriterLock lm (lock);
710 /* since we have no idea where BBT is if its off the map, see the last
711 * point in the map is past BBT, and if not add an arbitrary amount of
715 int additional_minutes = 1;
718 if (!_map->empty() && _map->back().bar >= (bbt.bars + 1)) {
721 /* add some more distance, using bigger steps each time */
722 recompute_map (false, _map->back().frame + (_frame_rate * 60 * additional_minutes));
723 additional_minutes *= 2;
728 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
730 /* CALLER MUST HOLD WRITE LOCK */
736 double divisions_per_bar;
738 double current_frame;
740 Metrics::iterator next_metric;
741 BBTPointList* new_map = new BBTPointList;
746 /* compute 1 mins worth */
747 end = _frame_rate * 60;
749 end = _map->back().frame;
752 if (!_map->empty ()) {
753 /* never allow the map to be shortened */
754 end = max (end, _map->back().frame);
758 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
760 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
761 if ((ms = dynamic_cast<MeterSection *> (*i)) != 0) {
767 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
768 if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
774 /* assumes that the first meter & tempo are at frame zero */
776 meter->set_frame (0);
777 tempo->set_frame (0);
779 /* assumes that the first meter & tempo are at 1|1|0 */
784 divisions_per_bar = meter->divisions_per_bar ();
785 beat_frames = meter->frames_per_division (*tempo,_frame_rate);
787 if (reassign_tempo_bbt) {
789 MeterSection* rmeter = meter;
791 DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n");
793 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
795 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
797 /* reassign the BBT time of this tempo section
798 * based on its bar offset position.
801 ts->update_bbt_time_from_bar_offset (*rmeter);
803 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
806 fatal << _("programming error: unhandled MetricSection type") << endmsg;
812 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2 dpb %3 fpb %4\n",
813 *((Meter*)meter), *((Tempo*)tempo), divisions_per_bar, beat_frames));
815 next_metric = metrics->begin();
816 ++next_metric; // skip meter (or tempo)
817 ++next_metric; // skip tempo (or meter)
819 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
820 new_map->push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), 1, 1));
822 while (current_frame < end) {
825 current_frame += beat_frames;
827 if (current.beats > meter->divisions_per_bar()) {
832 if (next_metric != metrics->end()) {
834 /* no operator >= so invert operator < */
836 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1 next metric @ %2\n", current, (*next_metric)->start()));
838 if (!(current < (*next_metric)->start())) {
841 if (((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0)) {
845 /* new tempo section: if its on a beat,
846 * we don't have to do anything other
847 * than recompute various distances,
848 * done further below as we transition
849 * the next metric section.
851 * if its not on the beat, we have to
852 * compute the duration of the beat it
853 * is within, which will be different
854 * from the preceding following ones
855 * since it takes part of its duration
856 * from the preceding tempo and part
857 * from this new tempo.
860 if (tempo->start().ticks != 0) {
862 double next_beat_frames = meter->frames_per_division (*tempo,_frame_rate);
864 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n",
865 tempo->start(), current_frame, tempo->bar_offset()));
867 /* back up to previous beat */
868 current_frame -= beat_frames;
869 /* set tempo section location based on offset from last beat */
870 tempo->set_frame (current_frame + (ts->bar_offset() * beat_frames));
871 /* advance to the location of the new (adjusted) beat */
872 current_frame += (ts->bar_offset() * beat_frames) + ((1.0 - ts->bar_offset()) * next_beat_frames);
873 /* next metric doesn't have to
874 * match this precisely to
877 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Adjusted last beat to %1\n", current_frame));
881 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into beat-aligned tempo metric at %1 = %2\n",
882 tempo->start(), current_frame));
883 tempo->set_frame (current_frame);
886 } else if ((ms = dynamic_cast<MeterSection*>(*next_metric)) != 0) {
890 /* new meter section: always defines the
894 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n",
895 meter->start(), current, current_frame));
897 assert (current.beats == 1);
899 meter->set_frame (current_frame);
902 divisions_per_bar = meter->divisions_per_bar ();
903 beat_frames = meter->frames_per_division (*tempo, _frame_rate);
905 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n",
906 beat_frames, divisions_per_bar, *((Meter*)meter), *((Tempo*)tempo)));
910 if (next_metric != metrics->end() && ((*next_metric)->start() == current)) {
911 /* same position so go back and set this one up before advancing
918 if (current.beats == 1) {
919 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
920 new_map->push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), current.bars, 1));
922 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
923 new_map->push_back (BBTPoint (*meter, *tempo, (framepos_t) llrint(current_frame), current.bars, current.beats));
927 swap (_map, new_map);
932 TempoMap::metric_at (framepos_t frame) const
934 Glib::RWLock::ReaderLock lm (lock);
935 TempoMetric m (first_meter(), first_tempo());
939 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
940 at something, because we insert the default tempo and meter during
941 TempoMap construction.
943 now see if we can find better candidates.
946 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
948 // cerr << "Looking at a metric section " << **i << endl;
950 if ((*i)->frame() > frame) {
954 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
955 m.set_tempo (*tempo);
956 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
957 m.set_meter (*meter);
960 m.set_frame ((*i)->frame ());
961 m.set_start ((*i)->start ());
964 // cerr << "for framepos " << frame << " returning " << m.meter() << " @ " << m.tempo() << " location " << m.frame() << " = " << m.start() << endl;
969 TempoMap::metric_at (BBT_Time bbt) const
971 Glib::RWLock::ReaderLock lm (lock);
972 TempoMetric m (first_meter(), first_tempo());
976 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
977 at something, because we insert the default tempo and meter during
978 TempoMap construction.
980 now see if we can find better candidates.
983 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
985 BBT_Time section_start ((*i)->start());
987 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
991 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
992 m.set_tempo (*tempo);
993 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
994 m.set_meter (*meter);
997 m.set_frame ((*i)->frame ());
998 m.set_start (section_start);
1005 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1007 require_map_to (frame);
1009 Glib::RWLock::ReaderLock lm (lock);
1010 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1014 TempoMap::bbt_time_rt (framepos_t frame, BBT_Time& bbt)
1016 Glib::RWLock::ReaderLock lm (lock, Glib::TRY_LOCK);
1019 throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
1022 if (_map->empty() || _map->back().frame < frame) {
1023 throw std::logic_error (string_compose ("map not long enough to reach %1", frame));
1026 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1030 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_iterator& i)
1032 /* CALLER MUST HOLD READ LOCK */
1034 bbt.bars = (*i).bar;
1035 bbt.beats = (*i).beat;
1037 if ((*i).frame == frame) {
1040 bbt.ticks = llrint (((frame - (*i).frame) / (*i).meter->frames_per_division(*((*i).tempo), _frame_rate)) *
1041 BBT_Time::ticks_per_bar_division);
1046 TempoMap::frame_time (const BBT_Time& bbt)
1048 require_map_to (bbt);
1050 Glib::RWLock::ReaderLock lm (lock);
1052 BBTPointList::const_iterator s = bbt_before_or_at (BBT_Time (1, 1, 0));
1053 BBTPointList::const_iterator e = bbt_before_or_at (BBT_Time (bbt.bars, bbt.beats, 0));
1055 if (bbt.ticks != 0) {
1056 return ((*e).frame - (*s).frame) +
1057 llrint ((*e).meter->frames_per_division (*(*e).tempo, _frame_rate) * (bbt.ticks/BBT_Time::ticks_per_bar_division));
1059 return ((*e).frame - (*s).frame);
1064 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1066 Glib::RWLock::ReaderLock lm (lock);
1067 framecnt_t frames = 0;
1070 bbt_time (pos, when);
1071 frames = bbt_duration_at_unlocked (when, bbt,dir);
1077 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir)
1079 if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) {
1083 /* round back to the previous precise beat */
1084 BBTPointList::const_iterator wi = bbt_before_or_at (BBT_Time (when.bars, when.beats, 0));
1085 BBTPointList::const_iterator start (wi);
1086 double tick_frames = 0;
1088 assert (wi != _map->end());
1090 /* compute how much rounding we did because of non-zero ticks */
1092 if (when.ticks != 0) {
1093 tick_frames = (*wi).meter->frames_per_division (*(*wi).tempo, _frame_rate) * (when.ticks/BBT_Time::ticks_per_bar_division);
1099 while (wi != _map->end() && bars < bbt.bars) {
1101 if ((*wi).is_bar()) {
1105 assert (wi != _map->end());
1107 while (wi != _map->end() && beats < bbt.beats) {
1111 assert (wi != _map->end());
1113 /* add any additional frames related to ticks in the added value */
1115 if (bbt.ticks != 0) {
1116 tick_frames += (*wi).meter->frames_per_division (*(*wi).tempo, _frame_rate) * (bbt.ticks/BBT_Time::ticks_per_bar_division);
1119 return ((*wi).frame - (*start).frame) + llrint (tick_frames);
1123 TempoMap::round_to_bar (framepos_t fr, int dir)
1125 return round_to_type (fr, dir, Bar);
1129 TempoMap::round_to_beat (framepos_t fr, int dir)
1131 return round_to_type (fr, dir, Beat);
1135 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
1137 require_map_to (fr);
1139 Glib::RWLock::ReaderLock lm (lock);
1140 BBTPointList::const_iterator i = bbt_before_or_at (fr);
1142 uint32_t ticks_one_subdivisions_worth;
1143 uint32_t difference;
1145 bbt_time (fr, the_beat, i);
1147 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round %1 to nearest 1/%2 beat, before-or-at = %3 @ %4|%5 precise = %6\n",
1148 fr, sub_num, (*i).frame, (*i).bar, (*i).beat, the_beat));
1150 ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_bar_division / sub_num;
1154 /* round to next (even if we're on a subdivision */
1156 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1159 /* right on the subdivision, so the difference is just the subdivision ticks */
1160 the_beat.ticks += ticks_one_subdivisions_worth;
1163 /* not on subdivision, compute distance to next subdivision */
1165 the_beat.ticks += ticks_one_subdivisions_worth - mod;
1168 if (the_beat.ticks > BBT_Time::ticks_per_bar_division) {
1169 assert (i != _map->end());
1171 assert (i != _map->end());
1172 the_beat.ticks -= BBT_Time::ticks_per_bar_division;
1176 } else if (dir < 0) {
1178 /* round to previous (even if we're on a subdivision) */
1180 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1183 /* right on the subdivision, so the difference is just the subdivision ticks */
1184 difference = ticks_one_subdivisions_worth;
1186 /* not on subdivision, compute distance to previous subdivision, which
1187 is just the modulus.
1193 if (the_beat.ticks < difference) {
1194 if (i == _map->begin()) {
1195 /* can't go backwards from wherever pos is, so just return it */
1199 the_beat.ticks = BBT_Time::ticks_per_bar_division - the_beat.ticks;
1201 the_beat.ticks -= difference;
1205 /* round to nearest */
1209 /* compute the distance to the previous and next subdivision */
1211 if ((rem = fmod ((double) the_beat.ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1213 /* closer to the next subdivision, so shift forward */
1215 the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem));
1217 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", the_beat.ticks));
1219 if (the_beat.ticks > BBT_Time::ticks_per_bar_division) {
1220 assert (i != _map->end());
1222 assert (i != _map->end());
1223 the_beat.ticks -= BBT_Time::ticks_per_bar_division;
1224 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat));
1227 } else if (rem > 0) {
1229 /* closer to previous subdivision, so shift backward */
1231 if (rem > the_beat.ticks) {
1232 if (i == _map->begin()) {
1233 /* can't go backwards past zero, so ... */
1236 /* step back to previous beat */
1238 the_beat.ticks = lrint (BBT_Time::ticks_per_bar_division - rem);
1239 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", the_beat));
1241 the_beat.ticks = lrint (the_beat.ticks - rem);
1242 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", the_beat.ticks));
1245 /* on the subdivision, do nothing */
1249 return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_bar_division) *
1250 (*i).meter->frames_per_division (*((*i).tempo), _frame_rate);
1254 TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
1256 require_map_to (frame);
1258 Glib::RWLock::ReaderLock lm (lock);
1259 BBTPointList::const_iterator fi;
1262 fi = bbt_after_or_at (frame);
1264 fi = bbt_before_or_at (frame);
1267 assert (fi != _map->end());
1269 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3|%4 @ %5) to bars in direction %2\n", frame, dir, (*fi).bar, (*fi).beat, (*fi).frame));
1274 /* find bar previous to 'frame' */
1276 if ((*fi).is_bar() && (*fi).frame == frame) {
1280 while (!(*fi).is_bar()) {
1281 if (fi == _map->begin()) {
1286 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1287 (*fi).bar, (*fi).beat, (*fi).frame));
1290 } else if (dir > 0) {
1292 /* find bar following 'frame' */
1294 if ((*fi).is_bar() && (*fi).frame == frame) {
1298 while (!(*fi).is_bar()) {
1300 if (fi == _map->end()) {
1306 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1307 (*fi).bar, (*fi).beat, (*fi).frame));
1312 /* true rounding: find nearest bar */
1314 BBTPointList::const_iterator prev = fi;
1315 BBTPointList::const_iterator next = fi;
1317 if ((*fi).frame == frame) {
1321 while ((*prev).beat != 1) {
1322 if (prev == _map->begin()) {
1328 while ((*next).beat != 1) {
1330 if (next == _map->end()) {
1336 if ((frame - (*prev).frame) < ((*next).frame - frame)) {
1337 return (*prev).frame;
1339 return (*next).frame;
1348 if ((*fi).frame >= frame) {
1349 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n");
1352 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1353 (*fi).bar, (*fi).beat, (*fi).frame));
1355 } else if (dir > 0) {
1356 if ((*fi).frame <= frame) {
1357 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step forward\n");
1360 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1361 (*fi).bar, (*fi).beat, (*fi).frame));
1364 /* find beat nearest to frame */
1365 if ((*fi).frame == frame) {
1369 BBTPointList::const_iterator prev = fi;
1370 BBTPointList::const_iterator next = fi;
1374 if ((frame - (*prev).frame) < ((*next).frame - frame)) {
1375 return (*prev).frame;
1377 return (*next).frame;
1389 TempoMap::map (TempoMap::BBTPointList::const_iterator& begin,
1390 TempoMap::BBTPointList::const_iterator& end,
1391 framepos_t lower, framepos_t upper)
1394 Glib::RWLock::WriterLock lm (lock);
1395 if (_map->empty() || (_map->back().frame < upper)) {
1396 recompute_map (false, upper);
1400 begin = lower_bound (_map->begin(), _map->end(), lower);
1401 end = upper_bound (_map->begin(), _map->end(), upper);
1405 TempoMap::tempo_section_at (framepos_t frame) const
1407 Glib::RWLock::ReaderLock lm (lock);
1408 Metrics::const_iterator i;
1409 TempoSection* prev = 0;
1411 for (i = metrics->begin(); i != metrics->end(); ++i) {
1414 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1416 if ((*i)->frame() > frame) {
1432 TempoMap::tempo_at (framepos_t frame) const
1434 TempoMetric m (metric_at (frame));
1440 TempoMap::meter_at (framepos_t frame) const
1442 TempoMetric m (metric_at (frame));
1447 TempoMap::get_state ()
1449 Metrics::const_iterator i;
1450 XMLNode *root = new XMLNode ("TempoMap");
1453 Glib::RWLock::ReaderLock lm (lock);
1454 for (i = metrics->begin(); i != metrics->end(); ++i) {
1455 root->add_child_nocopy ((*i)->get_state());
1463 TempoMap::set_state (const XMLNode& node, int /*version*/)
1466 Glib::RWLock::WriterLock lm (lock);
1469 XMLNodeConstIterator niter;
1470 Metrics old_metrics (*metrics);
1471 MeterSection* last_meter = 0;
1475 nlist = node.children();
1477 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1478 XMLNode* child = *niter;
1480 if (child->name() == TempoSection::xml_state_node_name) {
1483 TempoSection* ts = new TempoSection (*child);
1484 metrics->push_back (ts);
1486 if (ts->bar_offset() < 0.0) {
1488 ts->update_bar_offset_from_bbt (*last_meter);
1493 catch (failed_constructor& err){
1494 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1495 *metrics = old_metrics;
1499 } else if (child->name() == MeterSection::xml_state_node_name) {
1502 MeterSection* ms = new MeterSection (*child);
1503 metrics->push_back (ms);
1507 catch (failed_constructor& err) {
1508 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1509 *metrics = old_metrics;
1515 if (niter == nlist.end()) {
1516 MetricSectionSorter cmp;
1517 metrics->sort (cmp);
1520 recompute_map (true, false);
1523 PropertyChanged (PropertyChange ());
1529 TempoMap::dump (std::ostream& o) const
1531 Glib::RWLock::ReaderLock lm (lock);
1532 const MeterSection* m;
1533 const TempoSection* t;
1535 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1537 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1538 o << "Tempo @ " << *i << " (Bar-offset: " << t->bar_offset() << ") " << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (movable? "
1539 << t->movable() << ')' << endl;
1540 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1541 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1542 << " (movable? " << m->movable() << ')' << endl;
1548 TempoMap::n_tempos() const
1550 Glib::RWLock::ReaderLock lm (lock);
1553 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1554 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1563 TempoMap::n_meters() const
1565 Glib::RWLock::ReaderLock lm (lock);
1568 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1569 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1578 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1581 Glib::RWLock::WriterLock lm (lock);
1582 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
1583 if ((*i)->frame() >= where && (*i)->movable ()) {
1584 (*i)->set_frame ((*i)->frame() + amount);
1588 /* now reset the BBT time of all metrics, based on their new
1589 * audio time. This is the only place where we do this reverse
1593 Metrics::iterator i;
1594 const MeterSection* meter;
1595 const TempoSection* tempo;
1599 meter = &first_meter ();
1600 tempo = &first_tempo ();
1605 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
1608 MetricSection* prev = 0;
1610 for (i = metrics->begin(); i != metrics->end(); ++i) {
1613 TempoMetric metric (*meter, *tempo);
1616 metric.set_start (prev->start());
1617 metric.set_frame (prev->frame());
1619 // metric will be at frames=0 bbt=1|1|0 by default
1620 // which is correct for our purpose
1623 BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame());
1624 bbt_time ((*i)->frame(), bbt, bi);
1626 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
1632 if (bbt.ticks > BBT_Time::ticks_per_bar_division/2) {
1633 /* round up to next beat */
1639 if (bbt.beats != 1) {
1640 /* round up to next bar */
1646 // cerr << bbt << endl;
1648 (*i)->set_start (bbt);
1650 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1652 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1653 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
1655 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1657 fatal << _("programming error: unhandled MetricSection type") << endmsg;
1664 recompute_map (true, false);
1668 PropertyChanged (PropertyChange ());
1671 /** Add some (fractional) beats to a session frame position, and return the result in frames.
1672 * pos can be -ve, if required.
1675 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats)
1677 return framepos_plus_bbt (pos, BBT_Time (beats));
1680 /** Subtract some (fractional) beats to a frame position, and return the result in frames */
1682 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats)
1684 return framepos_minus_bbt (pos, BBT_Time (beats));
1688 TempoMap::framepos_minus_bbt (framepos_t pos, BBT_Time op)
1690 Glib::RWLock::ReaderLock lm (lock);
1691 BBTPointList::const_iterator i;
1692 framecnt_t extra_frames = 0;
1693 bool had_bars = (op.bars != 0);
1695 /* start from the bar|beat right before (or at) pos */
1697 i = bbt_before_or_at (pos);
1699 /* we know that (*i).frame is less than or equal to pos */
1700 extra_frames = pos - (*i).frame;
1702 /* walk backwards */
1704 while (i != _map->begin() && (op.bars || op.beats)) {
1708 if ((*i).is_bar()) {
1715 if ((had_bars && op.bars == 0) || !had_bars) {
1716 /* finished counting bars, or none to count,
1717 so decrement beat count
1725 /* handle ticks (assumed to be less than
1726 * BBT_Time::ticks_per_bar_division, as always.
1730 frameoffset_t tick_frames = llrint ((*i).meter->frames_per_division (*(*i).tempo, _frame_rate) * (op.ticks/BBT_Time::ticks_per_bar_division));
1731 framepos_t pre_tick_frames = (*i).frame + extra_frames;
1732 if (tick_frames < pre_tick_frames) {
1733 return pre_tick_frames - tick_frames;
1737 return (*i).frame + extra_frames;
1741 /** Add the BBT interval op to pos and return the result */
1743 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op)
1745 Glib::RWLock::ReaderLock lm (lock);
1746 BBT_Time op_copy (op);
1747 int additional_minutes = 1;
1748 BBTPointList::const_iterator i;
1749 framecnt_t backup_frames = 0;
1750 bool had_bars = (op.bars != 0);
1754 i = bbt_before_or_at (pos);
1758 /* we know that (*i).frame is before or equal to pos */
1759 backup_frames = pos - (*i).frame;
1761 while (i != _map->end() && (op.bars || op.beats)) {
1766 if ((*i).is_bar()) {
1773 if ((had_bars && op.bars == 0) || !had_bars) {
1774 /* finished counting bars, or none to count,
1775 so decrement beat count
1784 if (i != _map->end()) {
1788 /* we hit the end of the map before finish the bbt walk.
1791 recompute_map (pos + (_frame_rate * 60 * additional_minutes));
1792 additional_minutes *= 2;
1794 /* go back and try again */
1795 warning << "reached end of map with op now at " << op << " end = "
1796 << _map->back().frame << ' ' << _map->back().bar << '|' << _map->back().beat << ", trying to walk "
1797 << op_copy << " ... retry"
1802 return (*i).frame - backup_frames +
1803 llrint ((*i).meter->frames_per_division (*(*i).tempo, _frame_rate) * (op.ticks/BBT_Time::ticks_per_bar_division));
1805 return (*i).frame - backup_frames;
1809 /** Count the number of beats that are equivalent to distance when going forward,
1813 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance)
1815 framepos_t end = pos + distance;
1817 require_map_to (end);
1819 Glib::RWLock::ReaderLock lm (lock);
1820 BBTPointList::const_iterator i = bbt_after_or_at (pos);
1821 Evoral::MusicalTime beats = 0;
1823 /* if our starting BBTPoint is after pos, add a fractional beat
1824 to represent that distance.
1827 if ((*i).frame != pos) {
1828 beats += ((*i).frame - pos) / (*i).meter->frames_per_division (*(*i).tempo, _frame_rate);
1831 while (i != _map->end() && (*i).frame < end) {
1836 assert (i != _map->end());
1838 /* if our ending BBTPoint is after the end, subtract a fractional beat
1839 to represent that distance.
1842 if ((*i).frame > end) {
1843 beats -= ((*i).frame - end) / (*i).meter->frames_per_division (*(*i).tempo, _frame_rate);
1849 TempoMap::BBTPointList::const_iterator
1850 TempoMap::bbt_before_or_at (framepos_t pos)
1852 /* CALLER MUST HOLD READ LOCK */
1854 BBTPointList::const_iterator i;
1856 i = lower_bound (_map->begin(), _map->end(), pos);
1857 assert (i != _map->end());
1858 if ((*i).frame > pos) {
1859 assert (i != _map->begin());
1866 bool operator() (const BBT_Time& a, const BBT_Time& b) {
1871 TempoMap::BBTPointList::const_iterator
1872 TempoMap::bbt_before_or_at (const BBT_Time& bbt)
1874 BBTPointList::const_iterator i;
1877 i = lower_bound (_map->begin(), _map->end(), bbt, cmp);
1878 assert (i != _map->end());
1879 if ((*i).bar > bbt.bars || (*i).beat > bbt.beats) {
1880 assert (i != _map->begin());
1886 TempoMap::BBTPointList::const_iterator
1887 TempoMap::bbt_after_or_at (framepos_t pos)
1889 /* CALLER MUST HOLD READ LOCK */
1891 BBTPointList::const_iterator i;
1893 if (_map->back().frame == pos) {
1895 assert (i != _map->begin());
1900 i = upper_bound (_map->begin(), _map->end(), pos);
1901 assert (i != _map->end());
1905 /** Compare the time of this with that of another MetricSection.
1906 * @param with_bbt True to compare using start(), false to use frame().
1907 * @return -1 for less than, 0 for equal, 1 for greater than.
1911 MetricSection::compare (const MetricSection& other) const
1913 if (start() == other.start()) {
1915 } else if (start() < other.start()) {
1926 MetricSection::operator== (const MetricSection& other) const
1928 return compare (other) == 0;
1932 MetricSection::operator!= (const MetricSection& other) const
1934 return compare (other) != 0;
1938 operator<< (std::ostream& o, const Meter& m) {
1939 return o << m.divisions_per_bar() << '/' << m.note_divisor();
1943 operator<< (std::ostream& o, const Tempo& t) {
1944 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
1948 operator<< (std::ostream& o, const MetricSection& section) {
1950 o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';
1952 const TempoSection* ts;
1953 const MeterSection* ms;
1955 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
1956 o << *((Tempo*) ts);
1957 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
1958 o << *((Meter*) ms);