Add fixed-up framepos_plus_beats() and use it for the BeatsFramesConverter, since it only ever passed in beats to framepos_plus_bbt anyway.

git-svn-id: svn://localhost/ardour2/branches/3.0@10964 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Carl Hetherington 2011-12-10 13:49:08 +00:00
parent 2a9ceb74c4
commit e02e25e3f3
6 changed files with 228 additions and 3 deletions

View 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);

View 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

View 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

View file

@ -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));
}

View file

@ -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 ();
};

View 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()