+
+ return prev_t->frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
+}
+
+Tempo
+TempoMap::tempo_at_frame (const framepos_t& frame) const
+{
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+ return tempo_at_frame_locked (_metrics, frame);
+}
+
+Tempo
+TempoMap::tempo_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
+{
+ TempoSection* prev_t = 0;
+
+ TempoSection* t;
+
+ for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
+ if ((*i)->is_tempo()) {
+ t = static_cast<TempoSection*> (*i);
+ if (!t->active()) {
+ continue;
+ }
+ if ((prev_t) && t->frame() > frame) {
+ /* t is the section past frame */
+ const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
+ const Tempo ret_tempo (ret_bpm, prev_t->note_type());
+ return ret_tempo;
+ }
+ prev_t = t;
+ }
+ }
+
+ const double ret = prev_t->beats_per_minute();
+ const Tempo ret_tempo (ret, prev_t->note_type ());
+
+ return ret_tempo;
+}
+
+/** returns the frame at which the supplied tempo occurs, or
+ * the frame of the last tempo section (search exhausted)
+ * only the position of the first occurence will be returned
+ * (extend me)
+*/
+framepos_t
+TempoMap::frame_at_tempo (const Tempo& tempo) const
+{
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+ return frame_at_tempo_locked (_metrics, tempo);
+}
+
+
+framepos_t
+TempoMap::frame_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
+{
+ TempoSection* prev_t = 0;
+ const double tempo_ppm = tempo.beats_per_minute() / tempo.note_type();
+
+ Metrics::const_iterator i;
+
+ for (i = _metrics.begin(); i != _metrics.end(); ++i) {
+ TempoSection* t;
+ if ((*i)->is_tempo()) {
+ t = static_cast<TempoSection*> (*i);
+
+ if (!t->active()) {
+ continue;
+ }
+
+ const double t_ppm = t->beats_per_minute() / t->note_type();
+
+ if (t_ppm == tempo_ppm) {
+ return t->frame();
+ }
+
+ if (prev_t) {
+ const double prev_t_ppm = prev_t->beats_per_minute() / prev_t->note_type();
+
+ if ((t_ppm > tempo_ppm && prev_t_ppm < tempo_ppm) || (t_ppm < tempo_ppm && prev_t_ppm > tempo_ppm)) {
+ return prev_t->frame_at_tempo (tempo_ppm, prev_t->pulse(), _frame_rate);
+ }
+ }
+ prev_t = t;
+ }
+ }
+
+ return prev_t->frame();
+}
+
+/** more precise than doing tempo_at_frame (frame_at_beat (b)),
+ * as there is no intermediate frame rounding.
+ */
+Tempo
+TempoMap::tempo_at_beat (const double& beat) const
+{
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+ const MeterSection* prev_m = &meter_section_at_beat_locked (_metrics, beat);
+ const TempoSection* prev_t = &tempo_section_at_beat_locked (_metrics, beat);
+ const double note_type = prev_t->note_type();
+
+ return Tempo (prev_t->tempo_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse()) * note_type, note_type);
+}
+
+double
+TempoMap::pulse_at_beat (const double& beat) const
+{
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+ return pulse_at_beat_locked (_metrics, beat);
+}
+
+double
+TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
+{
+ const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
+
+ return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());