#include <sigc++/bind.h>
#include <glibmm/thread.h>
-#include <pbd/xml++.h>
-#include <ardour/tempo.h>
-#include <ardour/utils.h>
+#include "pbd/xml++.h"
+#include "ardour/tempo.h"
+#include "ardour/utils.h"
#include "i18n.h"
#include <locale.h>
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, 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()));
}
/***********************************************************************/
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;
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);
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);
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::RWLock::WriterLock lm (lock);
MetricSectionSorter cmp;
- BBT_Time corrected (when);
-
- if (dynamic_cast<MeterSection*>(§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 ();
return 0;
}
StateChanged (Change (0));
}
}
-
void
TempoMap::remove_tempo (const TempoSection& tempo)
}
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;
}
metrics->insert (metrics->end(), section);
}
- timestamp_metrics ();
+ timestamp_metrics (with_bbt);
}
void
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));
+}
+
+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);
}
StateChanged (Change (0));
TempoSection *ts;
if ((ts = dynamic_cast<TempoSection*>(*i)) != 0 && ts == &existing) {
-
- *((Tempo *) ts) = replacement;
+
+ *((Tempo *) ts) = replacement;
replaced = true;
- timestamp_metrics ();
+ timestamp_metrics (true);
+
break;
}
}
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));
+}
+
+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);
}
StateChanged (Change (0));
*((Meter*) ms) = replacement;
replaced = true;
- timestamp_metrics ();
+ timestamp_metrics (true);
break;
}
}
}
}
+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<TempoSection*> (*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<TempoSection*>(*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
{
}
void
-TempoMap::timestamp_metrics ()
+TempoMap::timestamp_metrics (bool use_bbt)
{
Metrics::iterator i;
const Meter* meter;
const Tempo* tempo;
Meter *m;
Tempo *t;
- nframes_t current;
- 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<TempoSection*>(*i)) != 0) {
+ tempo = t;
+ } else if ((m = dynamic_cast<MeterSection*>(*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<TempoSection*>(*i)) != 0) {
- tempo = t;
- } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
- meter = m;
- } else {
- fatal << _("programming error: unhandled MetricSection type") << endmsg;
- /*NOTREACHED*/
+ if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
+ tempo = t;
+ // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
+ } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
+ meter = m;
+ // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
+ } else {
+ fatal << _("programming error: unhandled MetricSection type") << endmsg;
+ /*NOTREACHED*/
+ }
+
+ prev = (*i);
}
}
+
+ // dump (cerr);
+ // cerr << "###############################################\n\n\n" << endl;
+
}
TempoMap::Metric
void
TempoMap::bbt_time (nframes_t frame, BBT_Time& bbt) const
{
- {
- Glib::RWLock::ReaderLock lm (lock);
+ {
+ Glib::RWLock::ReaderLock lm (lock);
bbt_time_unlocked (frame, bbt);
}
}
{
nframes_t frame_diff;
- uint32_t xtra_bars = 0;
- double xtra_beats = 0;
- double beats = 0;
+ // cerr << "---- BBT time for " << frame << " using metric @ " << metric.frame() << " BBT " << metric.start() << endl;
const double beats_per_bar = metric.meter().beats_per_bar();
- const double frames_per_bar = metric.meter().frames_per_bar (metric.tempo(), _frame_rate);
- const double beat_frames = metric.tempo().frames_per_beat (_frame_rate);
+ const double ticks_per_frame = metric.tempo().frames_per_beat (_frame_rate, metric.meter()) / Meter::ticks_per_beat;
/* now compute how far beyond that point we actually are. */
frame_diff = frame - metric.frame();
- xtra_bars = (uint32_t) floor (frame_diff / frames_per_bar);
- frame_diff -= (uint32_t) floor (xtra_bars * frames_per_bar);
- xtra_beats = (double) frame_diff / beat_frames;
-
-
- /* and set the returned value */
-
- /* and correct beat/bar shifts to match the meter.
- remember: beat and bar counting is 1-based,
- not zero-based
- also the meter may contain a fraction
- */
-
- bbt.bars = metric.start().bars + xtra_bars;
-
- beats = (double) metric.start().beats + xtra_beats;
-
- bbt.bars += (uint32_t) floor(beats/ (beats_per_bar+1) );
-
- beats = fmod(beats - 1, beats_per_bar )+ 1.0;
- bbt.ticks = (uint32_t)( round((beats - floor(beats)) *(double) Meter::ticks_per_beat));
- bbt.beats = (uint32_t) floor(beats);
-
+ bbt.ticks = metric.start().ticks + (uint32_t)round((double)frame_diff / ticks_per_frame);
+ uint32_t xtra_beats = bbt.ticks / (uint32_t)Meter::ticks_per_beat;
+ bbt.ticks %= (uint32_t)Meter::ticks_per_beat;
+
+ bbt.beats = metric.start().beats + xtra_beats - 1; // correction for 1-based counting, see below for matching operation.
+ bbt.bars = metric.start().bars + (uint32_t)floor((double)bbt.beats / beats_per_bar);
+ bbt.beats = (uint32_t)fmod((double)bbt.beats, beats_per_bar);
+
+ /* if we have a fractional number of beats per bar, we see if
+ we're in the last beat (the fractional one). if so, we
+ round ticks appropriately and bump to the next bar. */
+ double beat_fraction = beats_per_bar - floor(beats_per_bar);
+ /* XXX one problem here is that I'm not sure how to handle
+ fractional beats that don't evenly divide ticks_per_beat.
+ If they aren't handled consistently, I would guess we'll
+ continue to have strange discrepancies occuring. Perhaps
+ this will also behave badly in the case of meters like
+ 0.1/4, but I can't be bothered to test that.
+ */
+ uint32_t ticks_on_last_beat = (uint32_t)floor(Meter::ticks_per_beat * beat_fraction);
+ if(bbt.beats > (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;
}
-
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;
- Metric m = metric_at(start);
+ Metric m = metric_at (start);
uint32_t bar_offset = start.bars - m.start().bars;
+ start.ticks/Meter::ticks_per_beat;
- start_frame = m.frame() + (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);
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() + (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;
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 */
nframes_t frames = 0;
uint32_t bar = start.bars;
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;
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 = (nframes_t) floor (beats_counted * beat_frames);
nframes_t frames = 0;
BBT_Time when;
- bbt_time(pos,when);
+ bbt_time(pos, when);
{
Glib::RWLock::ReaderLock lm (lock);
double beats_per_bar;
BBT_Time result;
- result.bars = max(1U,when.bars + dir * bbt.bars) ;
+ result.bars = max(1U, when.bars + dir * bbt.bars) ;
result.beats = 1;
result.ticks = 0;
result.beats = when.beats + bbt.beats;
result.ticks = when.ticks + bbt.ticks;
- while (result.beats >= (beats_per_bar+1)) {
+ while (result.beats >= (beats_per_bar + 1)) {
result.bars++;
result.beats -= (uint32_t) ceil(beats_per_bar);
metric = metric_at(result); // maybe there is a meter change
while (result.ticks >= ticks_at_beat) {
result.beats++;
result.ticks -= ticks_at_beat;
- if (result.beats >= (beats_per_bar+1)) {
+ if (result.beats >= (beats_per_bar + 1)) {
result.bars++;
result.beats = 1;
metric = metric_at(result); // maybe there is a meter change
beats_per_bar = metric.meter().beats_per_bar();
}
ticks_at_beat= (uint32_t) ( result.beats == ceil(beats_per_bar) ?
- (1 - (ceil(beats_per_bar) - beats_per_bar) )* Meter::ticks_per_beat
+ (1 - (ceil(beats_per_bar) - beats_per_bar) ) * Meter::ticks_per_beat
: Meter::ticks_per_beat);
}
b -= (uint32_t) ceil(beats_per_bar);
} else {
- b = (uint32_t) ceil(beats_per_bar)- b + when.beats ;
+ b = (uint32_t) ceil(beats_per_bar) - b + when.beats ;
}
}
result.beats = when.beats - b;
do {
if (result.beats == 1) {
- result.bars = max(1U,result.bars-- ) ;
+ result.bars = max(1U, result.bars-- ) ;
metric = metric_at(result); // maybe there is a meter change
beats_per_bar = metric.meter().beats_per_bar();
result.beats = (uint32_t) ceil(beats_per_bar);
- ticks_at_beat = (uint32_t) ((1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat) ;
+ ticks_at_beat = (uint32_t) ((1 - (ceil(beats_per_bar) - beats_per_bar)) * Meter::ticks_per_beat) ;
} else {
result.beats --;
ticks_at_beat = (uint32_t) Meter::ticks_per_beat;
}
nframes_t
-
-TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num)
+TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num, int dir)
{
BBT_Time the_beat;
uint32_t ticks_one_half_subdivisions_worth;
uint32_t ticks_one_subdivisions_worth;
+ uint32_t difference;
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 (dir > 0) {
+
+ /* round to next */
- 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;
- }
+ uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
- return frame_time (the_beat);
+ if (mod == 0) {
+ /* right on the subdivision, so the difference is just the subdivision ticks */
+ difference = ticks_one_subdivisions_worth;
+ } else {
+ /* not on subdivision, compute distance to next subdivision */
+ difference = ticks_one_subdivisions_worth - mod;
+ }
- /*****************************
- XXX just keeping this for reference
+ 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;
+ }
- TempoMap::BBTPointList::iterator i;
- TempoMap::BBTPointList *more_zoomed_bbt_points;
- nframes_t frame_one_beats_worth;
- nframes_t pos = 0;
- nframes_t next_pos = 0 ;
- double tempo = 1;
- double frames_one_subdivisions_worth;
- bool fr_has_changed = false;
+ } else if (dir < 0) {
- int n;
+ /* round to previous */
- frame_one_beats_worth = (nframes_t) ::floor ((double) _frame_rate * 60 / 20 ); //one beat @ 20 bpm
- {
- Glib::RWLock::ReaderLock lm (lock);
- 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;
- }
+ uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
- 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();
-
+ if (mod == 0) {
+ /* right on the subdivision, so the difference is just the subdivision ticks */
+ difference = ticks_one_subdivisions_worth;
+ cerr << "On the sub, move by 1 sub = " << difference << endl;
} 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 = (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;
+ /* not on subdivision, compute distance to previous subdivision, which
+ is just the modulus.
+ */
+
+ difference = mod;
+ cerr << "off the sub, move by 1 sub = " << difference << endl;
}
- }
- if (!fr_has_changed) {
- fr = pos;
- }
- delete more_zoomed_bbt_points;
- return fr ;
+ cerr << "ticks = " << the_beat.ticks << endl;
- ******************************/
+ if (the_beat.ticks < difference) {
+ cerr << "backup beats, set ticks to "
+ << (uint32_t)Meter::ticks_per_beat - difference << endl;
+ the_beat.beats--;
+ the_beat.ticks = (uint32_t)Meter::ticks_per_beat - difference;
+ } else {
+ cerr << " reduce ticks\n";
+ the_beat.ticks -= difference;
+ }
+ } else {
+ /* round to nearest */
+
+ if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
+ 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 {
+ // difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
+ the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
+ }
+ }
+ return frame_time (the_beat);
}
nframes_t
-
TempoMap::round_to_type (nframes_t frame, int dir, BBTPointType type)
{
Metric metric = metric_at (frame);
switch (type) {
case Bar:
if (dir < 0) {
- /* relax */
-
+ if (bbt.bars > 1) {
+ bbt.bars--;
+ }
} 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;
case Beat:
if (dir < 0) {
- /* relax */
+ if (bbt.beats > 1) {
+ bbt.beats--;
+ }
} 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)) {
break;
}
-
+
+ /*
+ cerr << "for " << frame << " round to " << bbt << " using "
+ << metric.start()
+ << endl;
+ */
+
return metric.frame() + count_frames_between (metric.start(), bbt);
}
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);
+ beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
if (meter->frame() > tempo->frame()) {
bar = meter->start().bars;
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();
}
if (beat == 1) {
if (current >= lower) {
+ // cerr << "Add Bar at " << bar << "|1" << " @ " << current << endl;
points->push_back (BBTPoint (*meter, *tempo,(nframes_t)rint(current), Bar, bar, 1));
}
while (beat <= ceil( beats_per_bar) && beat_frame < limit) {
if (beat_frame >= lower) {
+ // 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;
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
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;
-
}
}
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);
+ beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
++i;
}
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<TempoSection*> (*i)) != 0) {
+
+ if ((*i)->frame() > frame) {
+ break;
+ }
+
+ prev = t;
+ }
+ }
+
+ if (prev == 0) {
+ fatal << endmsg;
+ }
+
+ return *prev;
+}
+
const Tempo&
TempoMap::tempo_at (nframes_t frame)
{
MetricSectionSorter cmp;
metrics->sort (cmp);
- timestamp_metrics ();
+ timestamp_metrics (true);
}
}
for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
if ((t = dynamic_cast<const TempoSection*>(*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<const MeterSection*>(*i)) != 0) {
o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
}
}
+int
+TempoMap::n_tempos() const
+{
+ Glib::RWLock::ReaderLock lm (lock);
+ int cnt = 0;
+
+ for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
+ if (dynamic_cast<const TempoSection*>(*i) != 0) {
+ cnt++;
+ }
+ }
+
+ return cnt;
+}
+
+int
+TempoMap::n_meters() const
+{
+ Glib::RWLock::ReaderLock lm (lock);
+ int cnt = 0;
+
+ for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
+ if (dynamic_cast<const MeterSection*>(*i) != 0) {
+ cnt++;
+ }
+ }
+
+ return cnt;
+}
+
+void
+TempoMap::insert_time (nframes_t where, nframes_t amount)
+{
+ for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
+ if ((*i)->frame() >= where) {
+ (*i)->set_frame ((*i)->frame() + amount);
+ }
+ }
+
+ timestamp_metrics (false);
+
+ StateChanged (Change (0));
+}