+ return can_solve;
+}
+
+/** Returns the sample position of the musical position zero */
+samplepos_t
+TempoMap::music_origin ()
+{
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+
+ return first_tempo().sample();
+}
+
+/** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
+ * the supplied sample, possibly returning a negative value.
+ *
+ * @param sample The session sample position.
+ * @param sub_num The subdivision to use when rounding the beat.
+ * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
+ * Positive integers indicate quarter note (non BBT) divisions.
+ * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_sample()).
+ * @return The beat position of the supplied sample.
+ *
+ * when working to a musical grid, the use of sub_nom indicates that
+ * the position should be interpreted musically.
+ *
+ * it effectively snaps to meter bars, meter beats or quarter note divisions
+ * (as per current gui convention) and returns a musical position independent of frame rate.
+ *
+ * If the supplied sample lies before the first meter, the return will be negative,
+ * in which case the returned beat uses the first meter (for BBT subdivisions) and
+ * the continuation of the tempo curve (backwards).
+ *
+ * This function is sensitive to tempo and meter.
+ */
+double
+TempoMap::exact_beat_at_sample (const samplepos_t sample, const int32_t sub_num) const
+{
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+
+ return exact_beat_at_sample_locked (_metrics, sample, sub_num);
+}
+
+double
+TempoMap::exact_beat_at_sample_locked (const Metrics& metrics, const samplepos_t sample, const int32_t divisions) const
+{
+ return beat_at_pulse_locked (_metrics, exact_qn_at_sample_locked (metrics, sample, divisions) / 4.0);
+}
+
+/** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
+ * the supplied sample, possibly returning a negative value.
+ *
+ * @param sample The session sample position.
+ * @param sub_num The subdivision to use when rounding the quarter note.
+ * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
+ * Positive integers indicate quarter note (non BBT) divisions.
+ * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_sample()).
+ * @return The quarter note position of the supplied sample.
+ *
+ * When working to a musical grid, the use of sub_nom indicates that
+ * the sample position should be interpreted musically.
+ *
+ * it effectively snaps to meter bars, meter beats or quarter note divisions
+ * (as per current gui convention) and returns a musical position independent of frame rate.
+ *
+ * If the supplied sample lies before the first meter, the return will be negative,
+ * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
+ * the continuation of the tempo curve (backwards).
+ *
+ * This function is tempo-sensitive.
+ */
+double
+TempoMap::exact_qn_at_sample (const samplepos_t sample, const int32_t sub_num) const
+{
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+
+ return exact_qn_at_sample_locked (_metrics, sample, sub_num);
+}
+
+double
+TempoMap::exact_qn_at_sample_locked (const Metrics& metrics, const samplepos_t sample, const int32_t sub_num) const
+{
+ double qn = pulse_at_minute_locked (metrics, minute_at_sample (sample)) * 4.0;
+
+ if (sub_num > 1) {
+ qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
+ } else if (sub_num == 1) {
+ /* the gui requested exact musical (BBT) beat */
+ qn = pulse_at_beat_locked (metrics, (floor (beat_at_minute_locked (metrics, minute_at_sample (sample)) + 0.5))) * 4.0;
+ } else if (sub_num == -1) {
+ /* snap to bar */
+ Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
+ bbt.beats = 1;
+ bbt.ticks = 0;
+
+ const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
+ ++bbt.bars;
+ const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
+
+ if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
+ qn = next_b;
+ } else {
+ qn = prev_b;
+ }
+ }
+
+ return qn;
+}
+
+/** returns the sample duration of the supplied BBT time at a specified sample position in the tempo map.
+ * @param pos the sample position in the tempo map.
+ * @param bbt the distance in BBT time from pos to calculate.
+ * @param dir the rounding direction..
+ * @return the duration in samples between pos and bbt
+*/
+samplecnt_t
+TempoMap::bbt_duration_at (samplepos_t pos, const BBT_Time& bbt, int dir)
+{
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+
+ BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_sample (pos));
+
+ const double divisions = meter_section_at_minute_locked (_metrics, minute_at_sample (pos)).divisions_per_bar();
+
+ if (dir > 0) {
+ pos_bbt.bars += bbt.bars;
+
+ pos_bbt.ticks += bbt.ticks;
+ if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
+ pos_bbt.beats += 1;
+ pos_bbt.ticks -= BBT_Time::ticks_per_beat;
+ }
+
+ pos_bbt.beats += bbt.beats;
+ if ((double) pos_bbt.beats > divisions) {
+ pos_bbt.bars += 1;
+ pos_bbt.beats -= divisions;
+ }
+ const samplecnt_t pos_bbt_sample = sample_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
+
+ return pos_bbt_sample - pos;
+
+ } else {
+
+ if (pos_bbt.bars <= bbt.bars) {
+ pos_bbt.bars = 1;
+ } else {
+ pos_bbt.bars -= bbt.bars;
+ }
+
+ if (pos_bbt.ticks < bbt.ticks) {
+ if (pos_bbt.bars > 1) {
+ if (pos_bbt.beats == 1) {
+ pos_bbt.bars--;
+ pos_bbt.beats = divisions;
+ } else {
+ pos_bbt.beats--;