+ double minute;
+ {
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+
+ minute = minute_at_pulse_locked (_metrics, quarter_note / 4.0);
+ }
+
+ return sample_at_minute (minute);
+}
+
+/** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
+ * @param beat The BBT (meter-based) beat.
+ * @return The quarter-note position of the supplied BBT (meter-based) beat.
+ *
+ * a quarter-note may be compared with and assigned to Temporal::Beats.
+ *
+ */
+double
+TempoMap::quarter_note_at_beat (const double beat) const
+{
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+
+ return pulse_at_beat_locked (_metrics, beat) * 4.0;
+}
+
+/** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
+ * @param quarter_note The position in quarter-note beats.
+ * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
+ *
+ * a quarter-note is the musical unit of Temporal::Beats.
+ *
+ */
+double
+TempoMap::beat_at_quarter_note (const double quarter_note) const
+{
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+
+ return beat_at_pulse_locked (_metrics, quarter_note / 4.0);
+}
+
+/** Returns the duration in samples between two supplied quarter-note beat positions.
+ * @param start the first position in quarter-note beats.
+ * @param end the end position in quarter-note beats.
+ * @return the sample distance ober the quarter-note beats duration.
+ *
+ * use this rather than e.g.
+ * sample_at-quarter_note (end_beats) - sample_at_quarter_note (start_beats).
+ * samples_between_quarter_notes() doesn't round to audio samples as an intermediate step,
+ *
+ */
+samplecnt_t
+TempoMap::samples_between_quarter_notes (const double start, const double end) const
+{
+ double minutes;
+
+ {
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+ minutes = minutes_between_quarter_notes_locked (_metrics, start, end);
+ }
+
+ return sample_at_minute (minutes);
+}
+
+double
+TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
+{
+
+ return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
+}
+
+double
+TempoMap::quarter_notes_between_samples (const samplecnt_t start, const samplecnt_t end) const
+{
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+
+ return quarter_notes_between_samples_locked (_metrics, start, end);
+}
+
+double
+TempoMap::quarter_notes_between_samples_locked (const Metrics& metrics, const samplecnt_t start, const samplecnt_t end) const
+{
+ const TempoSection* prev_t = 0;
+
+ for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+ TempoSection* t;
+
+ if ((*i)->is_tempo()) {
+ t = static_cast<TempoSection*> (*i);
+ if (!t->active()) {
+ continue;
+ }
+ if (prev_t && t->sample() > start) {
+ break;
+ }
+ prev_t = t;
+ }
+ }
+ assert (prev_t);
+ const double start_qn = prev_t->pulse_at_sample (start);
+
+ for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+ TempoSection* t;
+
+ if ((*i)->is_tempo()) {
+ t = static_cast<TempoSection*> (*i);
+ if (!t->active()) {
+ continue;
+ }
+ if (prev_t && t->sample() > end) {
+ break;
+ }
+ prev_t = t;
+ }
+ }
+ const double end_qn = prev_t->pulse_at_sample (end);
+
+ return (end_qn - start_qn) * 4.0;
+}
+
+bool
+TempoMap::check_solved (const Metrics& metrics) const
+{
+ TempoSection* prev_t = 0;
+ MeterSection* prev_m = 0;
+
+ for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+ TempoSection* t;
+ MeterSection* m;
+ if ((*i)->is_tempo()) {
+ t = static_cast<TempoSection*> (*i);
+ if (!t->active()) {
+ continue;
+ }
+ if (prev_t) {
+ /* check ordering */
+ if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
+ return false;
+ }
+
+ /* precision check ensures tempo and samples align.*/
+ if (t->sample() != sample_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))) {
+ if (!t->locked_to_meter()) {
+ return false;
+ }
+ }
+
+ /* gradient limit - who knows what it should be?
+ things are also ok (if a little chaotic) without this
+ */
+ if (fabs (prev_t->c()) > 1000.0) {
+ //std::cout << "c : " << prev_t->c() << std::endl;
+ return false;
+ }
+ }
+ prev_t = t;
+ }
+
+ if (!(*i)->is_tempo()) {
+ m = static_cast<MeterSection*> (*i);
+ if (prev_m && m->position_lock_style() == AudioTime) {
+ const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_sample (m->sample() - 1));
+ const samplepos_t nascent_m_sample = sample_at_minute (t->minute_at_pulse (m->pulse()));
+ /* Here we check that a preceding section of music doesn't overlap a subsequent one.
+ */
+ if (t && (nascent_m_sample > m->sample() || nascent_m_sample < 0)) {
+ return false;
+ }
+ }
+
+ prev_m = m;
+ }
+
+ }
+
+ return true;
+}
+
+bool
+TempoMap::set_active_tempi (const Metrics& metrics, const samplepos_t sample)
+{
+ for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+ TempoSection* t;
+ if ((*i)->is_tempo()) {
+ t = static_cast<TempoSection*> (*i);
+ if (t->locked_to_meter()) {