#include <cmath>
-
#include <glibmm/thread.h>
#include "pbd/xml++.h"
+#include "evoral/types.hpp"
#include "ardour/debug.h"
#include "ardour/tempo.h"
#include "ardour/utils.h"
return result;
}
+/** Add some (fractional) beats to a frame position, and return the result in frames */
+framepos_t
+TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const
+{
+ Metrics::const_iterator i;
+ const TempoSection* tempo;
+ const MeterSection* meter;
+
+ /* Find the starting metrics for tempo & meter */
+
+ for (i = metrics->begin(); i != metrics->end(); ++i) {
+
+ if ((*i)->frame() > pos) {
+ break;
+ }
+
+ const TempoSection* t;
+ const MeterSection* m;
+
+ if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
+ tempo = t;
+ } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
+ meter = m;
+ }
+ }
+
+ /* We now have:
+
+ meter -> the Meter for "pos"
+ tempo -> the Tempo for "pos"
+ i -> for first new metric after "pos", possibly metrics->end()
+ */
+
+ while (beats) {
+
+ /* End of this section */
+ framepos_t end = i == metrics->end() ? max_framepos : (*i)->frame ();
+
+ /* Distance to the end in beats */
+ Evoral::MusicalTime distance_beats = (end - pos) / tempo->frames_per_beat (_frame_rate, *meter);
+
+ /* Amount to subtract this time */
+ double const sub = min (distance_beats, beats);
+
+ /* Update */
+ beats -= sub;
+ pos += sub * tempo->frames_per_beat (_frame_rate, *meter);
+
+ /* Move on if there's anything to move to */
+ if (i != metrics->end ()) {
+ const TempoSection* t;
+ const MeterSection* m;
+
+ if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
+ tempo = t;
+ } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
+ meter = m;
+ }
+
+ ++i;
+ }
+ }
+
+ return pos;
+}
+
/** Add the BBT interval op to pos and return the result */
framepos_t
TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
return pos;
}
+
/** Count the number of beats that are equivalent to distance when starting at pos */
double
TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
--- /dev/null
+#include "framepos_plus_beats_test.h"
+#include "ardour/tempo.h"
+#include "timecode/bbt_time.h"
+
+CPPUNIT_TEST_SUITE_REGISTRATION (FrameposPlusBeatsTest);
+
+using namespace std;
+using namespace ARDOUR;
+using namespace Timecode;
+
+/* Basic tests with no tempo / meter changes */
+void
+FrameposPlusBeatsTest::singleTempoTest ()
+{
+ int const sampling_rate = 48000;
+ int const bpm = 120;
+
+ double const frames_per_beat = (60 / double (bpm)) * double (sampling_rate);
+
+ TempoMap map (sampling_rate);
+ Tempo tempo (bpm);
+ Meter meter (4, 4);
+
+ map.add_meter (meter, BBT_Time (1, 1, 0));
+ map.add_tempo (tempo, BBT_Time (1, 1, 0));
+
+ /* Add 1 beat to beat 3 of the first bar */
+ framepos_t r = map.framepos_plus_beats (frames_per_beat * 2, 1);
+ CPPUNIT_ASSERT_EQUAL (r, framepos_t (frames_per_beat * 3));
+}
+
+/* Test adding things that overlap a tempo change */
+void
+FrameposPlusBeatsTest::doubleTempoTest ()
+{
+ int const sampling_rate = 48000;
+
+ TempoMap map (sampling_rate);
+ Meter meter (4, 4);
+ map.add_meter (meter, BBT_Time (1, 1, 0));
+
+ /*
+ 120bpm at bar 1, 240bpm at bar 4
+
+ 120bpm = 24e3 samples per beat
+ 240bpm = 12e3 samples per beat
+ */
+
+
+ /*
+
+ 120bpm 240bpm
+ 0 beats 12 beats
+ 0 frames 288e3 frames
+ | | | | |
+ | 1.1 1.2 1.3 1.4 | 2.1 2.2 2.3.2.4 | 3.1 3.2 3.3 3.4 | 4.1 4.2 4.3 4.4 |
+
+ */
+
+ Tempo tempoA (120);
+ map.add_tempo (tempoA, BBT_Time (1, 1, 0));
+ Tempo tempoB (240);
+ map.add_tempo (tempoB, BBT_Time (4, 1, 0));
+
+ /* Now some tests */
+
+ /* Add 1 beat to 1|2 */
+ framepos_t r = map.framepos_plus_beats (24e3, 1);
+ CPPUNIT_ASSERT_EQUAL (r, framepos_t (48e3));
+
+ /* Add 2 beats to 3|4 (over the tempo change) */
+ r = map.framepos_plus_beats (264e3, 2);
+ CPPUNIT_ASSERT_EQUAL (r, framepos_t (264e3 + 24e3 + 12e3));
+
+ /* Add 2.5 beats to 3|3|960 (over the tempo change) */
+ r = map.framepos_plus_beats (264e3 - 12e3, 2.5);
+ CPPUNIT_ASSERT_EQUAL (r, framepos_t (264e3 + 24e3 + 12e3));
+}
+
+/* Same as doubleTempoTest () except put a meter change at the same time as the
+ tempo change (which shouldn't affect anything, since we are just dealing with
+ beats)
+*/
+
+void
+FrameposPlusBeatsTest::doubleTempoWithMeterTest ()
+{
+ int const sampling_rate = 48000;
+
+ TempoMap map (sampling_rate);
+ Meter meterA (4, 4);
+ map.add_meter (meterA, BBT_Time (1, 1, 0));
+
+ /*
+ 120bpm at bar 1, 240bpm at bar 4
+
+ 120bpm = 24e3 samples per beat
+ 240bpm = 12e3 samples per beat
+ */
+
+
+ /*
+
+ 120bpm 240bpm
+ 0 beats 12 beats
+ 0 frames 288e3 frames
+ | | | | |
+ | 1.1 1.2 1.3 1.4 | 2.1 2.2 2.3.2.4 | 3.1 3.2 3.3 3.4 | 4.1 4.2 4.3 |
+
+ */
+
+ Tempo tempoA (120);
+ map.add_tempo (tempoA, BBT_Time (1, 1, 0));
+ Tempo tempoB (240);
+ map.add_tempo (tempoB, BBT_Time (4, 1, 0));
+ Meter meterB (3, 4);
+ map.add_meter (meterB, BBT_Time (4, 1, 0));
+
+ /* Now some tests */
+
+ /* Add 1 beat to 1|2 */
+ framepos_t r = map.framepos_plus_beats (24e3, 1);
+ CPPUNIT_ASSERT_EQUAL (r, framepos_t (48e3));
+
+ /* Add 2 beats to 3|4 (over the tempo change) */
+ r = map.framepos_plus_beats (264e3, 2);
+ CPPUNIT_ASSERT_EQUAL (r, framepos_t (264e3 + 24e3 + 12e3));
+
+ /* Add 2.5 beats to 3|3|960 (over the tempo change) */
+ r = map.framepos_plus_beats (264e3 - 12e3, 2.5);
+ CPPUNIT_ASSERT_EQUAL (r, framepos_t (264e3 + 24e3 + 12e3));
+}
+
+