Add fixed-up framepos_plus_beats() and use it for the BeatsFramesConverter, since...
authorCarl Hetherington <carl@carlh.net>
Sat, 10 Dec 2011 13:49:08 +0000 (13:49 +0000)
committerCarl Hetherington <carl@carlh.net>
Sat, 10 Dec 2011 13:49:08 +0000 (13:49 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@10964 d708f5d6-7413-0410-9779-e7cbd77b26cf

libs/ardour/ardour/tempo.h
libs/ardour/beats_frames_converter.cc
libs/ardour/tempo.cc
libs/ardour/test/framepos_plus_beats_test.cc [new file with mode: 0644]
libs/ardour/test/framepos_plus_beats_test.h [new file with mode: 0644]
libs/ardour/wscript

index 9f61d1d54d405e7f4411d694a9e33dc84aa332b0..aa4266f35a599bf680f4679982333b8ad67518a1 100644 (file)
@@ -30,6 +30,7 @@
 #include "pbd/stateful.h"
 #include "pbd/statefuldestructible.h"
 
+#include "evoral/types.hpp"
 
 #include "ardour/ardour.h"
 
@@ -247,6 +248,7 @@ class TempoMap : public PBD::StatefulDestructible
        Timecode::BBT_Time bbt_subtract (const Timecode::BBT_Time&, const Timecode::BBT_Time&) const;
 
        framepos_t framepos_plus_bbt (framepos_t pos, Timecode::BBT_Time b) const;
+       framepos_t framepos_plus_beats (framepos_t, Evoral::MusicalTime) const;
        double framewalk_to_beats (framepos_t pos, framecnt_t distance) const;
 
        void change_existing_tempo_at (framepos_t, double bpm, double note_type);
index 59c0bb28962ffe10e954e9ed68ced4dbc0de6b35..f5d1794abb07c00bbaac4aca73a6512b23b88e86 100644 (file)
@@ -29,7 +29,7 @@ BeatsFramesConverter::to (double beats) const
 {
        assert (beats >= 0);
 
-       return _tempo_map.framepos_plus_bbt (_origin_b, Timecode::BBT_Time(beats)) - _origin_b;
+       return _tempo_map.framepos_plus_beats (_origin_b, beats) - _origin_b;
 }
 
 double
index 617553129ce6ab927ceff67f44caaf55487e2dff..97ec1224f0101501ae902569cd05389a7885c500 100644 (file)
@@ -24,9 +24,9 @@
 
 #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"
@@ -1935,6 +1935,72 @@ TempoMap::bbt_subtract (const BBT_Time& start, const BBT_Time& decrement) const
        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
@@ -2069,6 +2135,7 @@ 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
diff --git a/libs/ardour/test/framepos_plus_beats_test.cc b/libs/ardour/test/framepos_plus_beats_test.cc
new file mode 100644 (file)
index 0000000..19aa7f2
--- /dev/null
@@ -0,0 +1,134 @@
+#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));
+}
+
+
diff --git a/libs/ardour/test/framepos_plus_beats_test.h b/libs/ardour/test/framepos_plus_beats_test.h
new file mode 100644 (file)
index 0000000..4d9ec4b
--- /dev/null
@@ -0,0 +1,21 @@
+#include <sigc++/sigc++.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+class FrameposPlusBeatsTest : public CppUnit::TestFixture
+{
+       CPPUNIT_TEST_SUITE (FrameposPlusBeatsTest);
+       CPPUNIT_TEST (singleTempoTest);
+       CPPUNIT_TEST (doubleTempoTest);
+       CPPUNIT_TEST (doubleTempoWithMeterTest);
+       CPPUNIT_TEST_SUITE_END ();
+
+public:
+       void setUp () {}
+       void tearDown () {}
+
+       void singleTempoTest ();
+       void doubleTempoTest ();
+       void doubleTempoWithMeterTest ();
+};
+
index 2b9151c38532d2e4df7c7cf952ac7bd8f166690f..0eb4f0ceb677799c06ffd258b1a426006bdb0a7d 100644 (file)
@@ -427,7 +427,8 @@ def build(bld):
                 test/interpolation_test.cc
                 test/midi_clock_slave_test.cc
                 test/resampled_source_test.cc
-               test/framewalk_to_beats_test.cc
+                test/framewalk_to_beats_test.cc
+                test/framepos_plus_beats_test.cc
                 test/testrunner.cc
         '''.split()