+ /* handle sample before first meter */
+ if (minute < prev_m->minute()) {
+ beat = 0.0;
+ }
+ /* audio locked meters fake their beat */
+ if (next_m && next_m->beat() < beat) {
+ beat = next_m->beat();
+ }
+
+ beat = max (0.0, beat);
+
+ const double beats_in_ms = beat - prev_m->beat();
+ const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
+ const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
+ const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
+ const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
+
+ BBT_Time ret;
+
+ ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
+ ret.beats = (uint32_t) floor (remaining_beats);
+ ret.bars = total_bars;
+
+ /* 0 0 0 to 1 1 0 - based mapping*/
+ ++ret.bars;
+ ++ret.beats;
+
+ if (ret.ticks >= BBT_Time::ticks_per_beat) {
+ ++ret.beats;
+ ret.ticks -= BBT_Time::ticks_per_beat;
+ }
+
+ if (ret.beats >= prev_m->divisions_per_bar() + 1) {
+ ++ret.bars;
+ ret.beats = 1;
+ }
+
+ return ret;
+}
+
+/** Returns the sample position corresponding to the supplied BBT time.
+ * @param bbt the position in BBT time.
+ * @return the sample position at bbt.
+ *
+ */
+samplepos_t
+TempoMap::sample_at_bbt (const BBT_Time& bbt)
+{
+ if (bbt.bars < 1) {
+#ifndef NDEBUG
+ warning << string_compose (_("tempo map asked for sample time at bar < 1 (%1)\n"), bbt) << endmsg;
+#endif
+ return 0;
+ }
+
+ if (bbt.beats < 1) {
+ throw std::logic_error ("beats are counted from one");
+ }
+
+ double minute;
+ {
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+ minute = minute_at_bbt_locked (_metrics, bbt);
+ }
+
+ return sample_at_minute (minute);
+}
+
+/* meter & tempo section based */
+double
+TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
+{
+ /* HOLD THE READER LOCK */
+
+ const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
+ return ret;
+}
+
+/**
+ * Returns the quarter-note beat position corresponding to the supplied sample.
+ *
+ * @param sample the position in samples.
+ * @return The quarter-note position of the supplied sample. Ignores meter.
+ *
+*/
+double
+TempoMap::quarter_note_at_sample (const samplepos_t sample) const
+{
+ const double minute = minute_at_sample (sample);
+
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+
+ return pulse_at_minute_locked (_metrics, minute) * 4.0;
+}
+
+double
+TempoMap::quarter_note_at_sample_rt (const samplepos_t sample) const
+{
+ const double minute = minute_at_sample (sample);
+
+ Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
+
+ if (!lm.locked()) {
+ throw std::logic_error ("TempoMap::quarter_note_at_sample_rt() could not lock tempo map");
+ }
+
+ return pulse_at_minute_locked (_metrics, minute) * 4.0;
+}
+
+/**
+ * Returns the sample position corresponding to the supplied quarter-note beat.
+ *
+ * @param quarter_note the quarter-note position.
+ * @return the sample position of the supplied quarter-note. Ignores meter.
+ *
+ *
+*/
+samplepos_t
+TempoMap::sample_at_quarter_note (const double quarter_note) const
+{
+ 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;