2020-08-05 18:02:39 -04:00
/*
Copyright ( C ) 2017 Paul Davis
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <algorithm>
2020-12-30 23:06:19 -05:00
# include <vector>
2020-08-05 18:02:39 -04:00
2021-03-23 17:22:28 -04:00
# include <inttypes.h>
2020-08-05 18:02:39 -04:00
# include "pbd/error.h"
# include "pbd/i18n.h"
# include "pbd/compose.h"
# include "pbd/enumwriter.h"
# include "pbd/failed_constructor.h"
# include "pbd/stacktrace.h"
# include "temporal/debug.h"
# include "temporal/tempo.h"
using namespace PBD ;
using namespace Temporal ;
using std : : cerr ;
using std : : cout ;
using std : : endl ;
using Temporal : : superclock_t ;
std : : string Tempo : : xml_node_name = X_ ( " Tempo " ) ;
std : : string Meter : : xml_node_name = X_ ( " Meter " ) ;
2020-11-19 11:45:25 -05:00
SerializedRCUManager < TempoMap > TempoMap : : _map_mgr ( 0 ) ;
2020-08-05 18:13:32 -04:00
thread_local TempoMap : : SharedPtr TempoMap : : _tempo_map_p ;
2020-12-31 00:27:37 -05:00
PBD : : Signal0 < void > TempoMap : : MapChanged ;
2020-08-05 18:13:32 -04:00
2020-08-05 18:02:39 -04:00
void
Point : : add_state ( XMLNode & node ) const
{
node . set_property ( X_ ( " sclock " ) , _sclock ) ;
node . set_property ( X_ ( " quarters " ) , _quarters ) ;
node . set_property ( X_ ( " bbt " ) , _bbt ) ;
}
Point : : Point ( TempoMap const & map , XMLNode const & node )
: _map ( & map )
{
if ( ! node . get_property ( X_ ( " sclock " ) , _sclock ) ) {
throw failed_constructor ( ) ;
}
if ( ! node . get_property ( X_ ( " quarters " ) , _quarters ) ) {
throw failed_constructor ( ) ;
}
if ( ! node . get_property ( X_ ( " bbt " ) , _bbt ) ) {
throw failed_constructor ( ) ;
}
}
#if 0
samplepos_t
Point : : sample ( ) const
{
return superclock_to_samples ( _sclock , _map - > sample_rate ( ) ) ;
}
# endif
timepos_t
Point : : time ( ) const
{
2020-12-07 16:43:54 -05:00
if ( _map - > time_domain ( ) = = AudioTime ) {
2020-08-11 20:26:25 -04:00
return timepos_t : : from_superclock ( sclock ( ) ) ;
2020-08-05 18:02:39 -04:00
}
2020-12-07 16:43:54 -05:00
return timepos_t ( beats ( ) ) ;
2020-08-05 18:02:39 -04:00
}
Tempo : : Tempo ( XMLNode const & node )
{
assert ( node . name ( ) = = xml_node_name ) ;
2020-12-20 13:39:12 -05:00
node . get_property ( X_ ( " npm " ) , _npm ) ;
node . get_property ( X_ ( " enpm " ) , _enpm ) ;
_superclocks_per_note_type = double_npm_to_scpn ( _npm ) ;
_end_superclocks_per_note_type = double_npm_to_scpn ( _enpm ) ;
_super_note_type_per_second = double_npm_to_snps ( _npm ) ;
_end_super_note_type_per_second = double_npm_to_snps ( _enpm ) ;
2020-08-05 18:02:39 -04:00
if ( ! node . get_property ( X_ ( " note-type " ) , _note_type ) ) {
throw failed_constructor ( ) ;
}
if ( ! node . get_property ( X_ ( " type " ) , _type ) ) {
throw failed_constructor ( ) ;
}
if ( ! node . get_property ( X_ ( " active " ) , _active ) ) {
throw failed_constructor ( ) ;
}
2021-01-01 17:45:15 -05:00
if ( ! node . get_property ( X_ ( " locked-to-meter " ) , _locked_to_meter ) ) {
_locked_to_meter = true ;
}
if ( ! node . get_property ( X_ ( " clamped " ) , _clamped ) ) {
_clamped = false ;
}
2020-08-05 18:02:39 -04:00
}
2021-03-22 13:27:47 -04:00
void
2020-08-05 18:02:39 -04:00
Tempo : : set_ramped ( bool yn )
{
_type = ( yn ? Ramped : Constant ) ;
}
2021-04-04 19:53:49 -04:00
void
Tempo : : set_end ( uint64_t n , superclock_t s )
{
_end_super_note_type_per_second = n ;
_end_superclocks_per_note_type = s ;
}
2021-03-22 13:27:47 -04:00
void
Tempo : : set_clamped ( bool yn )
2020-08-05 18:02:39 -04:00
{
2021-03-22 13:27:47 -04:00
_clamped = yn ;
2020-08-05 18:02:39 -04:00
}
XMLNode &
Tempo : : get_state ( ) const
{
XMLNode * node = new XMLNode ( xml_node_name ) ;
2020-12-20 13:39:12 -05:00
node - > set_property ( X_ ( " npm " ) , note_types_per_minute ( ) ) ;
node - > set_property ( X_ ( " enpm " ) , end_note_types_per_minute ( ) ) ;
2020-08-05 18:02:39 -04:00
node - > set_property ( X_ ( " note-type " ) , note_type ( ) ) ;
node - > set_property ( X_ ( " type " ) , type ( ) ) ;
node - > set_property ( X_ ( " active " ) , active ( ) ) ;
2021-01-01 17:45:15 -05:00
node - > set_property ( X_ ( " locked-to-meter " ) , _locked_to_meter ) ;
node - > set_property ( X_ ( " clamped " ) , _clamped ) ;
2020-12-20 13:39:12 -05:00
2020-08-05 18:02:39 -04:00
return * node ;
}
int
Tempo : : set_state ( XMLNode const & node , int /*version*/ )
{
if ( node . name ( ) ! = xml_node_name ) {
return - 1 ;
}
2020-12-20 13:39:12 -05:00
node . get_property ( X_ ( " npm " ) , _npm ) ;
node . get_property ( X_ ( " enpm " ) , _enpm ) ;
_superclocks_per_note_type = double_npm_to_scpn ( _npm ) ;
_end_superclocks_per_note_type = double_npm_to_scpn ( _enpm ) ;
_super_note_type_per_second = double_npm_to_snps ( _npm ) ;
_end_super_note_type_per_second = double_npm_to_snps ( _enpm ) ;
2020-08-05 18:02:39 -04:00
node . get_property ( X_ ( " note-type " ) , _note_type ) ;
node . get_property ( X_ ( " type " ) , _type ) ;
node . get_property ( X_ ( " active " ) , _active ) ;
2021-01-01 17:45:15 -05:00
if ( ! node . get_property ( X_ ( " locked-to-meter " ) , _locked_to_meter ) ) {
_locked_to_meter = true ;
}
2020-12-20 13:39:12 -05:00
2021-01-01 17:45:15 -05:00
if ( ! node . get_property ( X_ ( " clamped " ) , _clamped ) ) {
_clamped = false ;
}
2020-12-20 13:39:12 -05:00
2020-08-05 18:02:39 -04:00
return 0 ;
}
Meter : : Meter ( XMLNode const & node )
{
assert ( node . name ( ) = = xml_node_name ) ;
if ( ! node . get_property ( X_ ( " note-value " ) , _note_value ) ) {
throw failed_constructor ( ) ;
}
if ( ! node . get_property ( X_ ( " divisions-per-bar " ) , _divisions_per_bar ) ) {
throw failed_constructor ( ) ;
}
}
XMLNode &
Meter : : get_state ( ) const
{
XMLNode * node = new XMLNode ( xml_node_name ) ;
node - > set_property ( X_ ( " note-value " ) , note_value ( ) ) ;
node - > set_property ( X_ ( " divisions-per-bar " ) , divisions_per_bar ( ) ) ;
return * node ;
}
int
Meter : : set_state ( XMLNode const & node , int /* version */ )
{
if ( node . name ( ) ! = xml_node_name ) {
return - 1 ;
}
node . get_property ( X_ ( " note-value " ) , _note_value ) ;
node . get_property ( X_ ( " divisions-per-bar " ) , _divisions_per_bar ) ;
return 0 ;
}
Temporal : : BBT_Time
Meter : : bbt_add ( Temporal : : BBT_Time const & bbt , Temporal : : BBT_Offset const & add ) const
{
int32_t bars = bbt . bars ;
int32_t beats = bbt . beats ;
int32_t ticks = bbt . ticks ;
if ( ( bars ^ add . bars ) < 0 ) {
/* signed-ness varies */
if ( abs ( add . bars ) > = abs ( bars ) ) {
/* addition will change which side of "zero" the answer is on;
adjust bbt . bars towards zero to deal with " unusual " BBT math
*/
if ( bars < 0 ) {
bars + + ;
} else {
bars - - ;
}
}
}
if ( ( beats ^ add . beats ) < 0 ) {
/* signed-ness varies */
if ( abs ( add . beats ) > = abs ( beats ) ) {
/* adjust bbt.beats towards zero to deal with "unusual" BBT math */
if ( beats < 0 ) {
beats + + ;
} else {
beats - - ;
}
}
}
Temporal : : BBT_Offset r ( bars + add . bars , beats + add . beats , ticks + add . ticks ) ;
/* ticks-per-bar-division; PPQN is ticks-per-quarter note */
const int32_t tpg = ticks_per_grid ( ) ;
if ( r . ticks > = tpg ) {
/* ticks per bar */
const int32_t tpb = tpg * _divisions_per_bar ;
if ( r . ticks > = tpb ) {
r . bars + = r . ticks / tpb ;
r . ticks % = tpb ;
}
if ( r . ticks > = tpg ) {
r . beats + = r . ticks / tpg ;
r . ticks % = tpg ;
}
}
if ( r . beats > _divisions_per_bar ) {
/* adjust to zero-based math, since that's what C++ operators expect */
r . beats - = 1 ;
r . bars + = r . beats / _divisions_per_bar ;
r . beats % = _divisions_per_bar ;
/* adjust back */
r . beats + = 1 ;
}
if ( r . bars = = 0 ) {
r . bars = 1 ;
}
return Temporal : : BBT_Time ( r . bars , r . beats , r . ticks ) ;
}
Temporal : : BBT_Time
Meter : : bbt_subtract ( Temporal : : BBT_Time const & bbt , Temporal : : BBT_Offset const & sub ) const
{
int32_t bars = bbt . bars ;
int32_t beats = bbt . beats ;
int32_t ticks = bbt . ticks ;
if ( ( bars ^ sub . bars ) < 0 ) {
/* signed-ness varies */
if ( abs ( sub . bars ) > = abs ( bars ) ) {
/* adjust bbt.bars towards zero to deal with "unusual" BBT math */
if ( bars < 0 ) {
bars + + ;
} else {
bars - - ;
}
}
}
if ( ( beats ^ sub . beats ) < 0 ) {
/* signed-ness varies */
if ( abs ( sub . beats ) > = abs ( beats ) ) {
/* adjust bbt.beats towards zero to deal with "unusual" BBT math */
if ( beats < 0 ) {
beats + + ;
} else {
beats - - ;
}
}
}
Temporal : : BBT_Offset r ( bars - sub . bars , beats - sub . beats , ticks - sub . ticks ) ;
/* ticks-per-bar-division; PPQN is ticks-per-quarter note */
const int32_t tpg = ticks_per_grid ( ) ;
if ( r . ticks < 0 ) {
r . beats - = ( r . ticks / tpg ) ;
r . ticks = tpg + ( r . ticks % Temporal : : Beats : : PPQN ) ;
}
if ( r . beats < 0 ) {
r . beats + = 1 ;
r . bars - = r . beats / _divisions_per_bar ;
r . beats = r . beats % _divisions_per_bar ;
r . beats - = 1 ;
}
if ( r . bars < = 0 ) {
r . bars - = 1 ;
}
return Temporal : : BBT_Time ( r . bars , r . beats , r . ticks ) ;
}
Temporal : : BBT_Time
Meter : : round_to_bar ( Temporal : : BBT_Time const & bbt ) const
{
2021-01-04 23:55:14 -05:00
Beats b ( bbt . beats , bbt . ticks ) ;
Beats half ( Beats : : ticks ( Beats : : PPQN + ( ( _divisions_per_bar * Beats : : PPQN / 2 ) ) ) ) ;
if ( b > = half ) {
return BBT_Time ( bbt . bars + 1 , 1 , 0 ) ;
2020-08-05 18:02:39 -04:00
}
2021-01-04 23:55:14 -05:00
return BBT_Time ( bbt . bars , 1 , 0 ) ;
2020-08-05 18:02:39 -04:00
}
Temporal : : BBT_Time
Meter : : round_up_to_bar ( Temporal : : BBT_Time const & bbt ) const
{
if ( bbt . ticks = = 0 & & bbt . beats = = 1 ) {
return bbt ;
}
BBT_Time b = bbt . round_up_to_beat ( ) ;
if ( b . beats > 1 ) {
b . bars + = 1 ;
b . beats = 1 ;
}
return b ;
}
Temporal : : BBT_Time
Meter : : round_down_to_bar ( Temporal : : BBT_Time const & bbt ) const
{
if ( bbt . ticks = = 0 & & bbt . beats = = 1 ) {
return bbt ;
}
BBT_Time b = bbt . round_down_to_beat ( ) ;
if ( b . beats > 1 ) {
b . beats = 1 ;
}
return b ;
}
Temporal : : BBT_Time
Meter : : round_up_to_beat ( Temporal : : BBT_Time const & bbt ) const
{
Temporal : : BBT_Time b = bbt . round_up_to_beat ( ) ;
if ( b . beats > _divisions_per_bar ) {
b . bars + + ;
b . beats = 1 ;
}
return b ;
}
Temporal : : Beats
Meter : : to_quarters ( Temporal : : BBT_Offset const & offset ) const
{
int64_t ticks = 0 ;
ticks + = ( Beats : : PPQN * offset . bars * _divisions_per_bar * 4 ) / _note_value ;
ticks + = ( Beats : : PPQN * offset . beats * 4 ) / _note_value ;
/* "parts per bar division" */
const int tpg = ticks_per_grid ( ) ;
if ( offset . ticks > tpg ) {
ticks + = Beats : : PPQN * offset . ticks / tpg ;
ticks + = offset . ticks % tpg ;
} else {
ticks + = offset . ticks ;
}
return Beats ( ticks / Beats : : PPQN , ticks % Beats : : PPQN ) ;
}
int
TempoPoint : : set_state ( XMLNode const & node , int version )
{
int ret ;
if ( ( ret = Tempo : : set_state ( node , version ) ) = = 0 ) {
node . get_property ( X_ ( " omega " ) , _omega ) ;
}
return ret ;
}
XMLNode &
TempoPoint : : get_state ( ) const
{
XMLNode & base ( Tempo : : get_state ( ) ) ;
Point : : add_state ( base ) ;
base . set_property ( X_ ( " omega " ) , _omega ) ;
return base ;
}
TempoPoint : : TempoPoint ( TempoMap const & map , XMLNode const & node )
2021-02-07 00:09:16 -05:00
: Point ( map , node )
, Tempo ( node )
2020-08-05 18:02:39 -04:00
, _omega ( 0 )
{
2021-04-04 19:53:49 -04:00
node . get_property ( X_ ( " omega " ) , _omega ) ;
2020-08-05 18:02:39 -04:00
}
/* To understand the math(s) behind ramping, see the file doc/tempo.{pdf,tex}
*/
void
2021-04-04 19:53:49 -04:00
TempoPoint : : compute_omega ( TempoPoint const & next )
2020-08-05 18:02:39 -04:00
{
2021-04-04 19:53:49 -04:00
cerr < < " ********* COMPUTING OMEGA for " < < * this < < " next = " < < next < < endl ;
superclock_t end_scpqn ;
if ( _clamped ) {
end_scpqn = end_superclocks_per_quarter_note ( ) ;
} else {
end_scpqn = next . superclocks_per_quarter_note ( ) ;
}
const DoubleableBeats quarter_duration = next . beats ( ) - beats ( ) ;
if ( ( _type = = Constant ) | | ( superclocks_per_quarter_note ( ) = = end_scpqn ) ) {
cerr < < " no ramp, my start == " < < superclocks_per_quarter_note ( ) < < " next end " < < end_scpqn < < " type " < < enum_2_string ( _type ) < < " clamped ? " < < _clamped < < endl ;
2020-08-05 18:02:39 -04:00
_omega = 0.0 ;
return ;
}
2021-04-04 19:53:49 -04:00
_omega = ( ( 1.0 / end_scpqn ) - ( 1.0 / superclocks_per_quarter_note ( ) ) ) / quarter_duration . to_double ( ) ;
2020-08-05 18:02:39 -04:00
2020-12-18 21:05:29 -05:00
DEBUG_TRACE ( DEBUG : : TemporalMap , string_compose ( " computed omega = %1%2 dur was %3 \n " , std : : setprecision ( 12 ) , _omega , DoubleableBeats ( quarter_duration ) . to_double ( ) ) ) ;
2020-08-05 18:02:39 -04:00
}
superclock_t
TempoPoint : : superclock_at ( Temporal : : Beats const & qn ) const
{
if ( qn = = _quarters ) {
return _sclock ;
}
if ( ! actually_ramped ( ) ) {
/* not ramped, use linear */
2021-03-22 18:05:20 -04:00
assert ( qn > = _quarters ) ;
const Beats delta = qn - _quarters ;
2020-08-05 18:02:39 -04:00
const superclock_t spqn = superclocks_per_quarter_note ( ) ;
2021-03-22 18:05:20 -04:00
return _sclock + ( spqn * delta . get_beats ( ) ) + int_div_round ( ( spqn * delta . get_ticks ( ) ) , superclock_t ( Temporal : : ticks_per_beat ) ) ;
2020-08-05 18:02:39 -04:00
}
2020-12-18 21:05:29 -05:00
return _sclock + llrint ( log1p ( superclocks_per_quarter_note ( ) * _omega * DoubleableBeats ( qn - _quarters ) . to_double ( ) ) / _omega ) ;
2020-08-05 18:02:39 -04:00
}
superclock_t
TempoPoint : : superclocks_per_note_type_at ( timepos_t const & pos ) const
{
if ( ! actually_ramped ( ) ) {
return _superclocks_per_note_type ;
}
return _superclocks_per_note_type * exp ( - _omega * pos . superclocks ( ) ) ;
}
Temporal : : Beats
2020-12-07 13:39:02 -05:00
TempoPoint : : quarters_at_superclock ( superclock_t sc ) const
2020-08-05 18:02:39 -04:00
{
2020-12-29 21:26:13 -05:00
/* catch a special case. The maximum superclock_t value cannot be
converted into a 32 bit beat + 32 bit tick value for common tempos .
Obviously , values less than this can also cause overflow , but are
unlikely to be encountered .
A longer term / big picture solution for this is likely required in
order to deal with longer sessions . Still , even at 300 bpm , a 32 bit
integer should cover 165 days . The problem is that a 62 bit ( int62_t )
superclock counter can cover 105064 days , so the theoretical
potential for errors here is real .
*/
if ( sc > = int62_t : : max ) {
return std : : numeric_limits < Beats > : : max ( ) ;
}
2020-08-05 18:02:39 -04:00
if ( ! actually_ramped ( ) ) {
2021-03-22 18:06:02 -04:00
2021-08-23 13:36:31 -04:00
// assert (sc >= _sclock);
2021-03-22 18:06:02 -04:00
superclock_t sc_delta = sc - _sclock ;
2020-08-05 18:02:39 -04:00
/* convert sc into superbeats, given that sc represents some number of seconds */
2021-03-22 18:06:02 -04:00
const superclock_t whole_seconds = sc_delta / superclock_ticks_per_second ;
const superclock_t remainder = sc_delta - ( whole_seconds * superclock_ticks_per_second ) ;
2020-12-06 01:03:15 -05:00
2020-12-29 21:27:34 -05:00
const int64_t supernotes = ( ( _super_note_type_per_second ) * whole_seconds ) + int_div_round ( superclock_t ( ( _super_note_type_per_second ) * remainder ) , superclock_ticks_per_second ) ;
/* multiply after divide to reduce overflow risk */
const int64_t superbeats = int_div_round ( supernotes , ( superclock_t ) _note_type ) * 4 ;
2020-08-05 18:02:39 -04:00
/* convert superbeats to beats:ticks */
int32_t b ;
int32_t t ;
Tempo : : superbeats_to_beats_ticks ( superbeats , b , t ) ;
2021-08-23 13:36:31 -04:00
if ( sc < _sclock ) {
std : : cout < < string_compose ( " %8 => \n sc %1 delta %9 = %2 secs rem = %3 rem snotes %4 sbeats = %5 => %6 : %7 \n " , sc , whole_seconds , remainder , supernotes , superbeats , b , t , * this , sc_delta ) ;
}
2021-03-22 18:06:02 -04:00
DEBUG_TRACE ( DEBUG : : TemporalMap , string_compose ( " %8 => \n sc %1 delta %9 = %2 secs rem = %3 rem snotes %4 sbeats = %5 => %6 : %7 \n " , sc , whole_seconds , remainder , supernotes , superbeats , b , t , * this , sc_delta ) ) ;
2020-12-06 01:05:17 -05:00
2021-03-22 18:06:02 -04:00
return _quarters + Beats ( b , t ) ;
2020-08-05 18:02:39 -04:00
}
const double b = ( exp ( _omega * ( sc - _sclock ) ) - 1 ) / ( superclocks_per_quarter_note ( ) * _omega ) ;
return _quarters + Beats : : from_double ( b ) ;
}
MeterPoint : : MeterPoint ( TempoMap const & map , XMLNode const & node )
2021-02-07 00:09:16 -05:00
: Point ( map , node )
, Meter ( node )
2020-08-05 18:02:39 -04:00
{
}
/* Given a time in BBT_Time, compute the equivalent Beat Time.
*
* Computation assumes that the Meter is in effect at the time specified as
* BBT_Time ( i . e . there is no other MeterPoint between this one and the specified
* time .
*/
Temporal : : Beats
MeterPoint : : quarters_at ( Temporal : : BBT_Time const & bbt ) const
{
Temporal : : BBT_Offset offset = bbt_delta ( bbt , _bbt ) ;
return _quarters + to_quarters ( offset ) ;
}
/* Given a time in Beats, compute the equivalent BBT Time.
*
* Computation assumes that the Meter is in effect at the time specified in
* Beats ( i . e . there is no other MeterPoint between this one and the specified
* time .
*/
Temporal : : BBT_Time
MeterPoint : : bbt_at ( Temporal : : Beats const & qn ) const
{
return bbt_add ( _bbt , Temporal : : BBT_Offset ( 0 , 0 , ( qn - _quarters ) . to_ticks ( ) ) ) ;
}
XMLNode &
MeterPoint : : get_state ( ) const
{
XMLNode & base ( Meter : : get_state ( ) ) ;
Point : : add_state ( base ) ;
return base ;
}
Temporal : : BBT_Time
2021-03-25 12:24:05 -04:00
TempoMetric : : bbt_at ( timepos_t const & pos ) const
2020-08-05 18:02:39 -04:00
{
2021-03-25 12:24:05 -04:00
if ( pos . is_beats ( ) ) {
return bbt_at ( pos . beats ( ) ) ;
}
superclock_t sc = pos . superclocks ( ) ;
2020-12-07 13:39:02 -05:00
const Beats dq = _tempo - > quarters_at_superclock ( sc ) - _meter - > beats ( ) ;
2020-12-06 01:05:17 -05:00
2021-03-22 18:06:24 -04:00
DEBUG_TRACE ( DEBUG : : TemporalMap , string_compose ( " qn @ %1 = %2, meter @ %3 , delta %4 \n " , sc , _tempo - > quarters_at_superclock ( sc ) , _meter - > beats ( ) , dq ) ) ;
2021-01-04 23:56:06 -05:00
/* dq is delta in quarters (beats). Convert to delta in note types of
the current meter , which we ' ll call " grid "
*/
const int64_t note_value_count = int_div_round ( dq . get_beats ( ) * _meter - > note_value ( ) , 4 ) ;
/* now construct a BBT_Offset using the count in grid units */
const BBT_Offset bbt_offset ( 0 , note_value_count , dq . get_ticks ( ) ) ;
DEBUG_TRACE ( DEBUG : : TemporalMap , string_compose ( " BBT offset from meter @ %1: %2 \n " , _meter - > bbt ( ) , bbt_offset ) ) ;
2020-08-05 18:02:39 -04:00
return _meter - > bbt_add ( _meter - > bbt ( ) , bbt_offset ) ;
}
superclock_t
TempoMetric : : superclock_at ( BBT_Time const & bbt ) const
{
2020-12-06 01:05:17 -05:00
DEBUG_TRACE ( DEBUG : : TemporalMap , string_compose ( " get quarters for %1 = %2 \n " , bbt , _meter - > quarters_at ( bbt ) ) ) ;
2020-08-05 18:02:39 -04:00
return _tempo - > superclock_at ( _meter - > quarters_at ( bbt ) ) ;
}
MusicTimePoint : : MusicTimePoint ( TempoMap const & map , XMLNode const & node )
: Point ( map , node )
2021-02-09 01:01:32 -05:00
, TempoPoint ( map , * node . child ( Tempo : : xml_node_name . c_str ( ) ) )
, MeterPoint ( map , * node . child ( Meter : : xml_node_name . c_str ( ) ) )
2020-08-05 18:02:39 -04:00
{
}
XMLNode &
MusicTimePoint : : get_state ( ) const
{
XMLNode * node = new XMLNode ( X_ ( " MusicTime " ) ) ;
2021-02-09 01:01:32 -05:00
2020-08-05 18:02:39 -04:00
Point : : add_state ( * node ) ;
2021-02-09 01:01:32 -05:00
node - > add_child_nocopy ( Tempo : : get_state ( ) ) ;
node - > add_child_nocopy ( Meter : : get_state ( ) ) ;
2020-08-05 18:02:39 -04:00
return * node ;
}
void
TempoMapPoint : : start_float ( )
{
_floating = true ;
}
void
TempoMapPoint : : end_float ( )
{
_floating = false ;
}
/* TEMPOMAP */
2020-11-25 12:35:12 -05:00
TempoMap : : TempoMap ( Tempo const & initial_tempo , Meter const & initial_meter )
2020-11-29 14:47:52 -05:00
: _time_domain ( AudioTime )
2020-08-05 18:02:39 -04:00
{
2020-11-25 19:05:24 -05:00
TempoPoint * tp = new TempoPoint ( * this , initial_tempo , 0 , Beats ( ) , BBT_Time ( ) ) ;
MeterPoint * mp = new MeterPoint ( * this , initial_meter , 0 , Beats ( ) , BBT_Time ( ) ) ;
2020-08-05 18:02:39 -04:00
2020-11-25 19:05:24 -05:00
_tempos . push_back ( * tp ) ;
_meters . push_back ( * mp ) ;
2021-02-07 12:48:15 -05:00
_points . push_back ( * tp ) ;
_points . push_back ( * mp ) ;
2020-08-05 18:02:39 -04:00
}
TempoMap : : ~ TempoMap ( )
{
}
2020-11-25 19:05:24 -05:00
TempoMap : : TempoMap ( XMLNode const & node , int version )
{
set_state ( node , version ) ;
}
2020-11-19 11:03:28 -05:00
TempoMap : : TempoMap ( TempoMap const & other )
2020-11-25 12:35:12 -05:00
: _time_domain ( other . time_domain ( ) )
2020-11-19 11:03:28 -05:00
{
2020-12-30 23:06:19 -05:00
copy_points ( other ) ;
}
TempoMap &
TempoMap : : operator = ( TempoMap const & other )
{
_time_domain = other . time_domain ( ) ;
copy_points ( other ) ;
2021-01-04 23:56:26 -05:00
return * this ;
2020-12-30 23:06:19 -05:00
}
void
TempoMap : : copy_points ( TempoMap const & other )
{
2021-02-07 12:48:15 -05:00
std : : vector < Point * > p ;
p . reserve ( other . _meters . size ( ) + other . _tempos . size ( ) + other . _bartimes . size ( ) ) ;
2020-12-30 23:06:19 -05:00
for ( Meters : : const_iterator m = other . _meters . begin ( ) ; m ! = other . _meters . end ( ) ; + + m ) {
MeterPoint * mp = new MeterPoint ( * m ) ;
_meters . push_back ( * mp ) ;
2021-02-07 12:48:15 -05:00
p . push_back ( mp ) ;
2020-12-30 23:06:19 -05:00
}
for ( Tempos : : const_iterator t = other . _tempos . begin ( ) ; t ! = other . _tempos . end ( ) ; + + t ) {
TempoPoint * tp = new TempoPoint ( * t ) ;
_tempos . push_back ( * tp ) ;
2021-02-07 12:48:15 -05:00
p . push_back ( tp ) ;
2020-12-30 23:06:19 -05:00
}
for ( MusicTimes : : const_iterator mt = other . _bartimes . begin ( ) ; mt ! = other . _bartimes . end ( ) ; + + mt ) {
MusicTimePoint * mtp = new MusicTimePoint ( * mt ) ;
_bartimes . push_back ( * mtp ) ;
2021-02-07 12:48:15 -05:00
p . push_back ( mtp ) ;
}
sort ( p . begin ( ) , p . end ( ) , Point : : ptr_sclock_comparator ( ) ) ;
for ( std : : vector < Point * > : : iterator pi = p . begin ( ) ; pi ! = p . end ( ) ; + + pi ) {
_points . push_back ( * * pi ) ;
2020-12-30 23:06:19 -05:00
}
2020-11-19 11:03:28 -05:00
}
2020-08-05 18:02:39 -04:00
void
TempoMap : : set_time_domain ( TimeDomain td )
{
if ( td = = time_domain ( ) ) {
return ;
}
# warning paul tempo_map::set_time_domain needs implementing
#if 0
switch ( td ) {
case AudioTime :
for ( Tempos : : iterator t = _tempos . begin ( ) ; t ! = _tempos . end ( ) ; + + t ) {
t - > set_sclock ( t - > superclock_at ( t - > beats ( ) ) ) ;
}
for ( Meters : : iterator m = _meters . begin ( ) ; m ! = _meters . end ( ) ; + + m ) {
m - > set_sclock ( m - > superclock_at ( m - > beats ( ) ) ) ;
}
break ;
default :
for ( Tempos : : iterator t = _tempos . begin ( ) ; t ! = _tempos . end ( ) ; + + t ) {
2020-12-07 13:39:02 -05:00
t - > set_beats ( t - > quarters_at_superclock ( t - > sclock ( ) ) ) ;
2020-08-05 18:02:39 -04:00
}
for ( Meters : : iterator m = _meters . begin ( ) ; m ! = _meters . end ( ) ; + + m ) {
2020-12-07 13:39:02 -05:00
m - > set_beats ( m - > quarters_at_superclock ( m - > sclock ( ) ) ) ;
2020-08-05 18:02:39 -04:00
}
}
# endif
_time_domain = td ;
}
MeterPoint *
2021-01-02 12:56:24 -05:00
TempoMap : : add_meter ( MeterPoint * mp )
2020-08-05 18:02:39 -04:00
{
Meters : : iterator m ;
2021-02-07 13:05:17 -05:00
Points : : iterator p ;
2021-01-02 12:56:24 -05:00
const superclock_t sclock_limit = mp - > sclock ( ) ;
const Beats beats_limit = mp - > beats ( ) ;
2020-08-05 18:02:39 -04:00
switch ( time_domain ( ) ) {
case AudioTime :
2021-01-02 12:56:24 -05:00
for ( m = _meters . begin ( ) ; m ! = _meters . end ( ) & & m - > sclock ( ) < sclock_limit ; + + m ) ;
2021-02-07 13:05:17 -05:00
for ( p = _points . begin ( ) ; p ! = _points . end ( ) & & p - > sclock ( ) < sclock_limit ; + + p ) ;
2020-08-05 18:02:39 -04:00
break ;
case BeatTime :
2021-01-02 12:56:24 -05:00
for ( m = _meters . begin ( ) ; m ! = _meters . end ( ) & & m - > beats ( ) < beats_limit ; + + m ) ;
2021-02-07 13:05:17 -05:00
for ( p = _points . begin ( ) ; p ! = _points . end ( ) & & p - > beats ( ) < beats_limit ; + + p ) ;
2020-08-05 18:02:39 -04:00
break ;
}
bool replaced = false ;
MeterPoint * ret = 0 ;
if ( m ! = _meters . end ( ) ) {
2021-01-02 12:56:24 -05:00
if ( m - > sclock ( ) = = sclock_limit ) {
2020-08-05 18:02:39 -04:00
/* overwrite Meter part of this point */
2021-01-02 12:56:24 -05:00
* ( ( Meter * ) & ( * m ) ) = * mp ;
delete mp ;
2020-08-05 18:02:39 -04:00
ret = & ( * m ) ;
replaced = true ;
}
}
if ( ! replaced ) {
2021-01-02 12:56:24 -05:00
ret = & ( * ( _meters . insert ( m , * mp ) ) ) ;
2021-02-07 13:05:17 -05:00
_points . insert ( p , * mp ) ;
2020-08-05 18:02:39 -04:00
}
2021-01-02 12:56:24 -05:00
reset_starting_at ( sclock_limit ) ;
2020-08-05 18:02:39 -04:00
return ret ;
}
void
TempoMap : : change_tempo ( TempoPoint & p , Tempo const & t )
{
* ( ( Tempo * ) & p ) = t ;
}
TempoPoint &
2020-11-25 14:51:17 -05:00
TempoMap : : set_tempo ( Tempo const & t , BBT_Time const & bbt )
2020-08-05 18:02:39 -04:00
{
2020-12-07 13:39:02 -05:00
return set_tempo ( t , timepos_t ( quarters_at ( bbt ) ) ) ;
2020-08-05 18:02:39 -04:00
}
TempoPoint &
2020-11-25 14:51:17 -05:00
TempoMap : : set_tempo ( Tempo const & t , timepos_t const & time )
2020-08-05 18:02:39 -04:00
{
2020-11-25 14:51:17 -05:00
TempoPoint * ret ;
DEBUG_TRACE ( DEBUG : : TemporalMap , string_compose ( " Set tempo @ %1 to %2 \n " , time , t ) ) ;
if ( time . is_beats ( ) ) {
2020-08-05 18:02:39 -04:00
/* tempo changes are required to be on-beat */
2020-11-25 14:51:17 -05:00
Beats on_beat = time . beats ( ) . round_to_beat ( ) ;
2020-08-05 18:02:39 -04:00
superclock_t sc ;
2020-11-25 14:51:17 -05:00
BBT_Time bbt ;
2020-08-05 18:02:39 -04:00
2021-02-01 01:54:08 -05:00
TempoMetric metric ( metric_at ( on_beat , false ) ) ;
2020-08-05 18:02:39 -04:00
2020-11-25 14:51:17 -05:00
bbt = metric . bbt_at ( on_beat ) ;
2020-08-05 18:02:39 -04:00
sc = metric . superclock_at ( on_beat ) ;
2021-01-02 12:56:24 -05:00
TempoPoint * tp = new TempoPoint ( * this , t , sc , on_beat , bbt ) ;
2020-08-05 18:02:39 -04:00
ret = add_tempo ( tp ) ;
2020-11-25 14:51:17 -05:00
} else {
2020-08-05 18:02:39 -04:00
2020-11-25 14:51:17 -05:00
Beats beats ;
BBT_Time bbt ;
superclock_t sc = time . superclocks ( ) ;
2020-08-05 18:02:39 -04:00
2021-02-01 01:54:08 -05:00
TempoMetric tm ( metric_at ( sc , false ) ) ;
2020-08-05 18:02:39 -04:00
2020-11-25 14:51:17 -05:00
/* tempo changes must be on beat */
2020-08-05 18:02:39 -04:00
2020-12-07 13:39:02 -05:00
beats = tm . quarters_at_superclock ( sc ) . round_to_beat ( ) ;
2020-11-25 14:51:17 -05:00
bbt = tm . bbt_at ( beats ) ;
2020-08-05 18:02:39 -04:00
2020-11-25 14:51:17 -05:00
/* recompute superclock position of rounded beat */
sc = tm . superclock_at ( beats ) ;
2020-08-05 18:02:39 -04:00
2021-01-02 12:56:24 -05:00
TempoPoint * tp = new TempoPoint ( * this , t , sc , beats , bbt ) ;
2020-08-05 18:02:39 -04:00
ret = add_tempo ( tp ) ;
2020-11-25 14:51:17 -05:00
2020-08-05 18:02:39 -04:00
}
2021-04-04 19:53:49 -04:00
dump ( cerr ) ;
2020-08-05 18:02:39 -04:00
return * ret ;
}
TempoPoint *
2021-01-02 12:56:24 -05:00
TempoMap : : add_tempo ( TempoPoint * tp )
2020-08-05 18:02:39 -04:00
{
Tempos : : iterator t ;
2021-02-07 13:05:17 -05:00
Points : : iterator p ;
2021-01-02 12:56:24 -05:00
const superclock_t sclock_limit = tp - > sclock ( ) ;
const Beats beats_limit = tp - > beats ( ) ;
2020-08-05 18:02:39 -04:00
switch ( time_domain ( ) ) {
case AudioTime :
2021-01-02 12:56:24 -05:00
for ( t = _tempos . begin ( ) ; t ! = _tempos . end ( ) & & t - > sclock ( ) < sclock_limit ; + + t ) ;
2021-02-07 13:05:17 -05:00
for ( p = _points . begin ( ) ; p ! = _points . end ( ) & & p - > sclock ( ) < sclock_limit ; + + p ) ;
2020-08-05 18:02:39 -04:00
break ;
case BeatTime :
2021-01-02 12:56:24 -05:00
for ( t = _tempos . begin ( ) ; t ! = _tempos . end ( ) & & t - > beats ( ) < beats_limit ; + + t ) ;
2021-02-07 13:05:17 -05:00
for ( p = _points . begin ( ) ; p ! = _points . end ( ) & & p - > beats ( ) < beats_limit ; + + p ) ;
2020-08-05 18:02:39 -04:00
break ;
}
bool replaced = false ;
TempoPoint * ret = 0 ;
if ( t ! = _tempos . end ( ) ) {
2021-01-02 12:56:24 -05:00
if ( t - > sclock ( ) = = sclock_limit ) {
2020-08-05 18:02:39 -04:00
/* overwrite Tempo part of this point */
2021-01-02 12:56:24 -05:00
* ( ( Tempo * ) & ( * t ) ) = * tp ;
delete tp ;
2020-08-05 18:02:39 -04:00
ret = & ( * t ) ;
DEBUG_TRACE ( DEBUG : : TemporalMap , string_compose ( " overwrote old tempo with %1 \n " , tp ) ) ;
replaced = true ;
}
}
if ( ! replaced ) {
2021-01-02 12:56:24 -05:00
t = _tempos . insert ( t , * tp ) ;
2021-02-07 13:05:17 -05:00
p = _points . insert ( p , * tp ) ;
2020-08-05 18:02:39 -04:00
ret = & * t ;
DEBUG_TRACE ( DEBUG : : TemporalMap , string_compose ( " inserted tempo %1 \n " , tp ) ) ;
}
/* t is guaranteed not to be _tempos.end() : it was either the
* TempoPoint we overwrote , or its the one we inserted .
*/
assert ( t ! = _tempos . end ( ) ) ;
2021-02-07 13:05:17 -05:00
assert ( p ! = _points . end ( ) ) ;
2020-08-05 18:02:39 -04:00
Tempos : : iterator nxt = t ;
+ + nxt ;
2021-01-02 12:56:24 -05:00
reset_starting_at ( sclock_limit ) ;
2020-08-05 18:02:39 -04:00
return ret ;
}
void
TempoMap : : remove_tempo ( TempoPoint const & tp )
{
2020-12-07 16:51:48 -05:00
superclock_t sc ( tp . sclock ( ) ) ;
Tempos : : iterator t ;
for ( t = _tempos . begin ( ) ; t ! = _tempos . end ( ) & & t - > sclock ( ) < tp . sclock ( ) ; + + t ) ;
if ( t - > sclock ( ) ! = tp . sclock ( ) ) {
/* error ... no tempo point at the time of tp */
return ;
2020-08-05 18:02:39 -04:00
}
2020-12-07 16:51:48 -05:00
_tempos . erase ( t ) ;
2021-02-07 13:23:37 -05:00
remove_point ( tp ) ;
2020-12-07 16:51:48 -05:00
reset_starting_at ( sc ) ;
2020-08-05 18:02:39 -04:00
}
MusicTimePoint &
TempoMap : : set_bartime ( BBT_Time const & bbt , timepos_t const & pos )
{
MusicTimePoint * ret ;
assert ( pos . time_domain ( ) = = AudioTime ) ;
2020-12-07 16:51:48 -05:00
superclock_t sc ( pos . superclocks ( ) ) ;
2021-02-01 01:54:08 -05:00
TempoMetric metric ( metric_at ( sc ) ) ;
2021-02-07 00:09:16 -05:00
MusicTimePoint * tp = new MusicTimePoint ( * this , sc , metric . quarters_at_superclock ( sc ) , bbt , metric . tempo ( ) , metric . meter ( ) ) ;
2020-08-05 18:02:39 -04:00
2021-01-31 20:39:29 -05:00
ret = add_or_replace_bartime ( * tp ) ;
2020-08-05 18:02:39 -04:00
return * ret ;
}
MusicTimePoint *
TempoMap : : add_or_replace_bartime ( MusicTimePoint & tp )
{
MusicTimes : : iterator m ;
2021-02-07 13:05:17 -05:00
Points : : iterator p ;
superclock_t sclock_limit = tp . sclock ( ) ;
2020-08-05 18:02:39 -04:00
2021-02-07 13:05:17 -05:00
for ( m = _bartimes . begin ( ) ; m ! = _bartimes . end ( ) & & m - > sclock ( ) < sclock_limit ; + + m ) ;
for ( p = _points . begin ( ) ; p ! = _points . end ( ) & & p - > sclock ( ) < sclock_limit ; + + p ) ;
2020-08-05 18:02:39 -04:00
bool replaced = false ;
MusicTimePoint * ret = 0 ;
if ( m ! = _bartimes . end ( ) ) {
if ( m - > sclock ( ) = = tp . sclock ( ) ) {
/* overwrite the point with */
* m = tp ;
ret = & ( * m ) ;
DEBUG_TRACE ( DEBUG : : TemporalMap , string_compose ( " overwrote old bartime with %1 \n " , tp ) ) ;
replaced = true ;
}
}
if ( ! replaced ) {
m = _bartimes . insert ( m , tp ) ;
2021-02-07 13:05:17 -05:00
_points . insert ( p , tp ) ;
2020-08-05 18:02:39 -04:00
ret = & * m ;
DEBUG_TRACE ( DEBUG : : TemporalMap , string_compose ( " inserted bartime %1 \n " , tp ) ) ;
}
/* m is guaranteed not to be _bartimes.end() : it was either the
* TempoPoint we overwrote , or its the one we inserted .
*/
assert ( m ! = _bartimes . end ( ) ) ;
reset_starting_at ( tp . sclock ( ) ) ;
return ret ;
}
void
TempoMap : : remove_bartime ( MusicTimePoint const & tp )
{
2020-12-07 16:51:48 -05:00
superclock_t sc ( tp . sclock ( ) ) ;
MusicTimes : : iterator m ;
for ( m = _bartimes . begin ( ) ; m ! = _bartimes . end ( ) & & m - > sclock ( ) < tp . sclock ( ) ; + + m ) ;
2021-02-07 13:23:37 -05:00
2020-12-07 16:51:48 -05:00
if ( m - > sclock ( ) ! = tp . sclock ( ) ) {
2021-02-07 13:23:37 -05:00
/* error ... no music time point at the time of tp */
2020-12-07 16:51:48 -05:00
return ;
2020-08-05 18:02:39 -04:00
}
2021-02-07 13:23:37 -05:00
2020-12-07 16:51:48 -05:00
_bartimes . erase ( m ) ;
2021-02-07 13:23:37 -05:00
remove_point ( tp ) ;
2020-12-07 16:51:48 -05:00
reset_starting_at ( sc ) ;
2020-08-05 18:02:39 -04:00
}
2021-02-07 13:23:37 -05:00
void
TempoMap : : remove_point ( Point const & point )
{
Points : : iterator p ;
Point const * tpp ( & point ) ;
for ( p = _points . begin ( ) ; p ! = _points . end ( ) ; + + p ) {
if ( & ( * p ) = = tpp ) {
// XXX need to fix this leak delete tpp;
_points . erase ( p ) ;
break ;
}
}
}
2020-08-05 18:02:39 -04:00
void
TempoMap : : reset_starting_at ( superclock_t sc )
{
DEBUG_TRACE ( DEBUG : : TemporalMap , string_compose ( " reset starting at %1 \n " , sc ) ) ;
2021-04-04 19:53:49 -04:00
cerr < < " RESET starting at " < < sc < < endl ;
dump ( cerr ) ;
2020-08-05 18:02:39 -04:00
assert ( ! _tempos . empty ( ) ) ;
assert ( ! _meters . empty ( ) ) ;
2021-04-04 19:53:49 -04:00
TempoPoint * current_tempo ;
MeterPoint * current_meter ;
2020-08-05 18:02:39 -04:00
2021-04-04 19:53:49 -04:00
/* final argument = false means "return the _points iterator
corresponding to the latter of the tempo or meter point used . "
( i . e . the point * at * the latter of the two , not the one after it )
*/
Points : : iterator p = get_tempo_and_meter ( current_tempo , current_meter , sc , true , false ) ;
2020-08-05 18:02:39 -04:00
2021-04-04 19:53:49 -04:00
TempoMetric metric ( * current_tempo , * current_meter ) ;
cerr < < " We begin with metric = " < < metric < < endl ;
2020-08-05 18:02:39 -04:00
2021-04-04 19:53:49 -04:00
Tempos : : iterator nxt_tempo ;
2020-08-05 18:02:39 -04:00
2021-04-04 19:53:49 -04:00
for ( nxt_tempo = _tempos . begin ( ) ; nxt_tempo ! = _tempos . end ( ) ; + + nxt_tempo ) {
if ( * nxt_tempo = = * current_tempo ) {
break ;
2020-08-05 18:02:39 -04:00
}
}
2021-04-04 19:53:49 -04:00
assert ( nxt_tempo ! = _tempos . end ( ) ) ;
+ + nxt_tempo ;
2020-08-05 18:02:39 -04:00
2021-04-04 19:53:49 -04:00
if ( nxt_tempo ! = _tempos . end ( ) & & current_tempo - > sclock ( ) ! = sc ) {
+ + nxt_tempo ;
}
2020-08-05 18:02:39 -04:00
2021-04-04 19:53:49 -04:00
if ( nxt_tempo ! = _tempos . end ( ) ) {
cerr < < " :::: next tempo is " < < * nxt_tempo < < endl ;
} else {
cerr < < " ;;;; next tempo at end \n " ;
}
2020-08-05 18:02:39 -04:00
2021-04-04 19:53:49 -04:00
TempoPoint * tp ;
MeterPoint * mp ;
MusicTimePoint * mtp ;
2020-08-05 18:02:39 -04:00
2021-04-04 19:53:49 -04:00
cerr < < " after all that, p @ end ? " < < ( p = = _points . end ( ) ) < < endl ;
2020-08-05 18:02:39 -04:00
2021-04-04 19:53:49 -04:00
for ( ; p ! = _points . end ( ) ; + + p ) {
2020-08-05 18:02:39 -04:00
2021-04-04 19:53:49 -04:00
bool reset_metric = false ;
bool reset_point = false ;
bool is_tempo = false ;
2020-08-05 18:02:39 -04:00
2021-04-04 19:53:49 -04:00
cerr < < " Now looking at " < < * p < < endl ;
2020-08-05 18:02:39 -04:00
2021-04-04 19:53:49 -04:00
if ( ( mtp = dynamic_cast < MusicTimePoint * > ( & * p ) ) ! = 0 ) {
2020-08-05 18:02:39 -04:00
2021-04-04 19:53:49 -04:00
/* nothing to do here. We do not reset music time (bar
* time ) points since everything their position is set
* by the user . The only time we would change them is
* if we alter the time domain of the tempo map .
*/
2020-08-05 18:02:39 -04:00
2021-04-04 19:53:49 -04:00
cerr < < " MTP! \n " ;
2020-08-05 18:02:39 -04:00
2021-04-04 19:53:49 -04:00
} else if ( ( tp = dynamic_cast < TempoPoint * > ( & * p ) ) ! = 0 ) {
reset_metric = true ;
reset_point = true ;
is_tempo = true ;
current_tempo = tp ;
cerr < < " Tempo! " < < * tp < < endl ;
} else if ( ( mp = dynamic_cast < MeterPoint * > ( & * p ) ) ! = 0 ) {
reset_metric = true ;
reset_point = true ;
current_meter = mp ;
cerr < < " Meter! " < < * mp < < endl ;
}
2020-08-05 18:02:39 -04:00
2021-04-04 19:53:49 -04:00
if ( is_tempo ) {
if ( tp - > ramped ( ) & & nxt_tempo ! = _tempos . end ( ) ) {
tp - > compute_omega ( * nxt_tempo ) ;
}
+ + nxt_tempo ;
}
2020-08-05 18:02:39 -04:00
2021-04-04 19:53:49 -04:00
if ( reset_point ) {
superclock_t sc = metric . superclock_at ( p - > bbt ( ) ) ;
DEBUG_TRACE ( DEBUG : : TemporalMap , string_compose ( " \t based on %1 move to %2,%3 \n " , p - > bbt ( ) , sc , p - > beats ( ) ) ) ;
p - > set ( sc , p - > beats ( ) , p - > bbt ( ) ) ;
}
2020-08-05 18:02:39 -04:00
2021-04-04 19:53:49 -04:00
if ( reset_metric ) {
metric = TempoMetric ( * current_tempo , * current_meter ) ;
}
2020-08-05 18:02:39 -04:00
}
2021-04-04 19:53:49 -04:00
cerr < < " RESET DONE \n \n \n " ;
2020-08-05 18:02:39 -04:00
}
bool
TempoMap : : move_meter ( MeterPoint const & mp , timepos_t const & when , bool push )
{
2020-12-07 16:51:48 -05:00
assert ( ! _tempos . empty ( ) ) ;
assert ( ! _meters . empty ( ) ) ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
if ( _meters . size ( ) < 2 | | mp = = _meters . front ( ) ) {
/* not movable */
return false ;
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
superclock_t sc ;
Beats beats ;
BBT_Time bbt ;
TimeDomain td ( time_domain ( ) ) ;
bool round_up ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
switch ( td ) {
case AudioTime :
sc = when . superclocks ( ) ;
if ( sc > mp . sclock ( ) ) {
round_up = true ;
} else {
round_up = false ;
2020-08-05 18:02:39 -04:00
}
2020-12-07 16:51:48 -05:00
break ;
case BeatTime :
beats = when . beats ( ) ;
if ( beats > mp . beats ( ) ) {
round_up = true ;
} else {
round_up = false ;
}
break ;
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
/* Do not allow moving a meter marker to the same position as
* an existing one .
*/
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
Tempos : : iterator t , prev_t ;
Meters : : iterator m , prev_m ;
switch ( time_domain ( ) ) {
case AudioTime : {
2021-01-04 23:57:02 -05:00
/* Find TempoMetric *prior* to the intended new location, * using superclock position */
2020-12-07 16:51:48 -05:00
for ( t = _tempos . begin ( ) , prev_t = _tempos . end ( ) ; t ! = _tempos . end ( ) & & t - > sclock ( ) < sc ; + + t ) { prev_t = t ; }
for ( m = _meters . begin ( ) , prev_m = _meters . end ( ) ; m ! = _meters . end ( ) & & m - > sclock ( ) < sc & & * m ! = mp ; + + m ) { prev_m = m ; }
assert ( prev_m ! = _meters . end ( ) ) ;
if ( prev_t = = _tempos . end ( ) ) { prev_t = _tempos . begin ( ) ; }
TempoMetric metric ( * prev_t , * prev_m ) ;
2021-01-04 23:57:02 -05:00
/* check the duration of 1 bar here. If we're not more than
* half - way to the next bar ( in whatever the appropriate
* direction is ) , don ' t move
*/
2021-01-06 12:06:14 -05:00
const superclock_t one_bar = metric . superclocks_per_bar ( ) ;
2021-01-04 23:57:02 -05:00
if ( abs ( sc - mp . sclock ( ) ) < one_bar / 2 ) {
return false ;
}
/* compute the BBT at the given superclock position, given the prior TempoMetric */
2021-03-25 12:24:05 -04:00
bbt = metric . bbt_at ( timepos_t : : from_superclock ( sc ) ) ;
2021-01-04 23:57:02 -05:00
/* meter changes must fall on a bar change */
if ( round_up ) {
bbt = metric . meter ( ) . round_up_to_bar ( bbt ) ;
} else {
bbt = metric . meter ( ) . round_down_to_bar ( bbt ) ;
}
/* Repeat using the computed (new) BBT location */
2020-12-07 16:51:48 -05:00
for ( m = _meters . begin ( ) , prev_m = _meters . end ( ) ; m ! = _meters . end ( ) & & m - > bbt ( ) < bbt & & * m ! = mp ; + + m ) { prev_m = m ; }
for ( t = _tempos . begin ( ) , prev_t = _tempos . end ( ) ; t ! = _tempos . end ( ) & & t - > bbt ( ) < bbt ; + + t ) { prev_t = t ; }
2021-01-04 23:57:02 -05:00
if ( prev_m = = _meters . end ( ) ) {
/* given position is going to put us over the initial
meter . Not allowed for a meter move .
*/
return false ;
}
2020-12-07 16:51:48 -05:00
if ( prev_t = = _tempos . end ( ) ) { prev_t = _tempos . begin ( ) ; }
metric = TempoMetric ( * prev_t , * prev_m ) ;
2021-01-04 23:57:02 -05:00
/* recompute the superclock position of the new BBT position,
* since this is what we ' ll use to set the meter point .
*/
2020-12-07 16:51:48 -05:00
sc = metric . superclock_at ( bbt ) ;
2021-01-04 23:57:02 -05:00
/* check to see if there's already a meter point at that location */
2020-12-07 16:51:48 -05:00
for ( m = _meters . begin ( ) , prev_m = _meters . end ( ) ; m ! = _meters . end ( ) ; + + m ) {
if ( & * m ! = & mp ) {
if ( m - > sclock ( ) = = sc ) {
return false ;
2020-08-05 18:02:39 -04:00
}
}
}
2020-12-07 16:51:48 -05:00
beats = metric . quarters_at ( bbt ) ;
break ;
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
case BeatTime : {
/* meter changes must be on bar */
for ( t = _tempos . begin ( ) , prev_t = _tempos . end ( ) ; t ! = _tempos . end ( ) & & t - > beats ( ) < beats ; + + t ) { prev_t = t ; }
for ( m = _meters . begin ( ) , prev_m = _meters . end ( ) ; m ! = _meters . end ( ) & & m - > beats ( ) < beats & & * m ! = mp ; + + m ) { prev_m = m ; }
assert ( prev_m ! = _meters . end ( ) ) ;
if ( prev_t = = _tempos . end ( ) ) { prev_t = _tempos . begin ( ) ; }
TempoMetric metric ( * prev_t , * prev_m ) ;
bbt = metric . bbt_at ( beats ) ;
if ( round_up ) {
bbt = metric . meter ( ) . round_up_to_bar ( bbt ) ;
} else {
bbt = metric . meter ( ) . round_down_to_bar ( bbt ) ;
}
for ( t = _tempos . begin ( ) , prev_t = _tempos . end ( ) ; t ! = _tempos . end ( ) & & t - > bbt ( ) < bbt ; + + t ) { prev_t = t ; }
for ( m = _meters . begin ( ) , prev_m = _meters . end ( ) ; m ! = _meters . end ( ) & & m - > bbt ( ) < bbt & & * m ! = mp ; + + m ) { prev_m = m ; }
assert ( prev_m ! = _meters . end ( ) ) ;
if ( prev_t = = _tempos . end ( ) ) { prev_t = _tempos . begin ( ) ; }
metric = TempoMetric ( * prev_t , * prev_m ) ;
beats = metric . quarters_at ( bbt ) ;
for ( m = _meters . begin ( ) , prev_m = _meters . end ( ) ; m ! = _meters . end ( ) ; + + m ) {
if ( & * m ! = & mp ) {
if ( m - > beats ( ) = = beats ) {
return false ;
2020-08-05 18:02:39 -04:00
}
}
}
2020-12-07 16:51:48 -05:00
sc = metric . superclock_at ( bbt ) ;
break ;
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
default :
/* NOTREACHED */
return false ;
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
if ( mp . sclock ( ) = = sc & & mp . beats ( ) = = beats & & mp . bbt ( ) = = bbt ) {
return false ;
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
const superclock_t old_sc = mp . sclock ( ) ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
Meters : : iterator current = _meters . end ( ) ;
Meters : : iterator insert_before = _meters . end ( ) ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
for ( Meters : : iterator m = _meters . begin ( ) ; m ! = _meters . end ( ) ; + + m ) {
if ( * m = = mp ) {
current = m ;
}
if ( insert_before = = _meters . end ( ) & & ( m - > sclock ( ) > sc ) ) {
insert_before = m ;
2020-08-05 18:02:39 -04:00
}
2020-12-07 16:51:48 -05:00
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
/* existing meter must have been found */
assert ( current ! = _meters . end ( ) ) ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
/* reset position of this meter */
current - > set ( sc , beats , bbt ) ;
/* reposition in list */
_meters . splice ( insert_before , _meters , current ) ;
/* recompute 3 domain positions for everything after this */
reset_starting_at ( std : : min ( sc , old_sc ) ) ;
2020-08-05 18:02:39 -04:00
return true ;
}
bool
TempoMap : : move_tempo ( TempoPoint const & tp , timepos_t const & when , bool push )
{
2020-12-07 16:51:48 -05:00
assert ( ! _tempos . empty ( ) ) ;
assert ( ! _meters . empty ( ) ) ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
if ( _tempos . size ( ) < 2 | | tp = = _tempos . front ( ) ) {
/* not movable */
return false ;
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
superclock_t sc ;
Beats beats ;
BBT_Time bbt ;
TimeDomain td ( time_domain ( ) ) ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
switch ( td ) {
case AudioTime :
sc = when . superclocks ( ) ;
break ;
case BeatTime :
beats = when . beats ( ) ;
break ;
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
/* Do not allow moving a tempo marker to the same position as
* an existing one .
*/
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
Tempos : : iterator t , prev_t ;
Meters : : iterator m , prev_m ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
switch ( time_domain ( ) ) {
case AudioTime : {
for ( t = _tempos . begin ( ) , prev_t = _tempos . end ( ) ; t ! = _tempos . end ( ) & & t - > sclock ( ) < sc & & * t ! = tp ; + + t ) { prev_t = t ; }
for ( m = _meters . begin ( ) , prev_m = _meters . end ( ) ; m ! = _meters . end ( ) & & m - > sclock ( ) < sc ; + + m ) { prev_m = m ; }
assert ( prev_t ! = _tempos . end ( ) ) ;
if ( prev_m = = _meters . end ( ) ) { prev_m = _meters . begin ( ) ; }
TempoMetric metric ( * prev_t , * prev_m ) ;
beats = metric . quarters_at_superclock ( sc ) ;
/* tempo changes must be on beat, so round and then
* recompute superclock and BBT with rounded result
*/
beats = beats . round_to_beat ( ) ;
for ( t = _tempos . begin ( ) , prev_t = _tempos . end ( ) ; t ! = _tempos . end ( ) & & t - > sclock ( ) < sc & & * t ! = tp ; + + t ) { prev_t = t ; }
for ( m = _meters . begin ( ) , prev_m = _meters . end ( ) ; m ! = _meters . end ( ) & & m - > sclock ( ) < sc ; + + m ) { prev_m = m ; }
assert ( prev_t ! = _tempos . end ( ) ) ;
if ( prev_m = = _meters . end ( ) ) { prev_m = _meters . begin ( ) ; }
metric = TempoMetric ( * prev_t , * prev_m ) ;
sc = metric . superclock_at ( beats ) ;
bbt = metric . bbt_at ( beats ) ;
break ;
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
case BeatTime : {
/* tempo changes must be on beat */
beats = beats . round_to_beat ( ) ;
for ( t = _tempos . begin ( ) , prev_t = _tempos . end ( ) ; t ! = _tempos . end ( ) & & t - > beats ( ) < beats & & * t ! = tp ; + + t ) { prev_t = t ; }
for ( m = _meters . begin ( ) , prev_m = _meters . end ( ) ; m ! = _meters . end ( ) & & m - > beats ( ) < beats ; + + m ) { prev_m = m ; }
assert ( prev_t ! = _tempos . end ( ) ) ;
assert ( prev_m ! = _meters . end ( ) ) ;
TempoMetric metric ( * prev_t , * prev_m ) ;
sc = metric . superclock_at ( beats ) ;
bbt = metric . bbt_at ( beats ) ;
break ;
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
default :
/* NOTREACHED */
return false ;
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
if ( tp . sclock ( ) = = sc & & tp . beats ( ) = = beats & & tp . bbt ( ) = = bbt ) {
return false ;
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
const superclock_t old_sc = tp . sclock ( ) ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
Tempos : : iterator current = _tempos . end ( ) ;
Tempos : : iterator insert_before = _tempos . end ( ) ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
for ( Tempos : : iterator t = _tempos . begin ( ) ; t ! = _tempos . end ( ) ; + + t ) {
if ( * t = = tp ) {
current = t ;
}
if ( insert_before = = _tempos . end ( ) & & ( t - > sclock ( ) > sc ) ) {
insert_before = t ;
}
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
/* existing tempo must have been found */
assert ( current ! = _tempos . end ( ) ) ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
/* reset position of this tempo */
current - > set ( sc , beats , bbt ) ;
/* reposition in list */
_tempos . splice ( insert_before , _tempos , current ) ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
/* recompute 3 domain positions for everything after this */
reset_starting_at ( std : : min ( sc , old_sc ) ) ;
2020-08-05 18:02:39 -04:00
return true ;
}
MeterPoint &
TempoMap : : set_meter ( Meter const & m , timepos_t const & time )
{
MeterPoint * ret = 0 ;
2020-11-25 14:51:17 -05:00
DEBUG_TRACE ( DEBUG : : TemporalMap , string_compose ( " Set meter @ %1 to %2 \n " , time , m ) ) ;
2020-08-05 18:02:39 -04:00
2020-11-25 14:51:17 -05:00
if ( time . is_beats ( ) ) {
2020-08-05 18:02:39 -04:00
2020-11-25 14:51:17 -05:00
Beats beats ( time . beats ( ) ) ;
2021-02-01 01:54:08 -05:00
TempoMetric metric ( metric_at ( beats ) ) ;
2020-08-05 18:02:39 -04:00
/* meter changes are required to be on-bar */
BBT_Time rounded_bbt = metric . bbt_at ( beats ) ;
rounded_bbt = metric . round_to_bar ( rounded_bbt ) ;
const Beats rounded_beats = metric . quarters_at ( rounded_bbt ) ;
const superclock_t sc = metric . superclock_at ( rounded_beats ) ;
2021-01-02 12:56:24 -05:00
MeterPoint * mp = new MeterPoint ( * this , m , sc , rounded_beats , rounded_bbt ) ;
2020-08-05 18:02:39 -04:00
ret = add_meter ( mp ) ;
2020-11-25 14:51:17 -05:00
} else {
2020-08-05 18:02:39 -04:00
2020-11-25 14:51:17 -05:00
superclock_t sc ( time . superclocks ( ) ) ;
2020-08-05 18:02:39 -04:00
Beats beats ;
BBT_Time bbt ;
2021-02-01 01:54:08 -05:00
TempoMetric metric ( metric_at ( sc ) ) ;
2020-08-05 18:02:39 -04:00
/* meter changes must be on bar */
2021-03-25 12:24:05 -04:00
bbt = metric . bbt_at ( time ) ;
2020-08-05 18:02:39 -04:00
bbt = metric . round_to_bar ( bbt ) ;
/* compute beat position */
beats = metric . quarters_at ( bbt ) ;
/* recompute superclock position of bar-rounded position */
sc = metric . superclock_at ( beats ) ;
2021-01-02 12:56:24 -05:00
MeterPoint * mp = new MeterPoint ( * this , m , sc , beats , bbt ) ;
2021-01-01 20:30:00 -05:00
2020-08-05 18:02:39 -04:00
ret = add_meter ( mp ) ;
}
return * ret ;
}
2020-11-25 14:51:17 -05:00
MeterPoint &
TempoMap : : set_meter ( Meter const & t , BBT_Time const & bbt )
{
2020-12-07 13:39:02 -05:00
return set_meter ( t , timepos_t ( quarters_at ( bbt ) ) ) ;
2020-11-25 14:51:17 -05:00
}
2020-08-05 18:02:39 -04:00
void
TempoMap : : remove_meter ( MeterPoint const & mp )
{
2020-12-07 16:51:48 -05:00
superclock_t sc = mp . sclock ( ) ;
Meters : : iterator m = std : : upper_bound ( _meters . begin ( ) , _meters . end ( ) , mp , Point : : sclock_comparator ( ) ) ;
if ( m - > sclock ( ) ! = mp . sclock ( ) ) {
/* error ... no meter point at the time of mp */
return ;
2020-08-05 18:02:39 -04:00
}
2020-12-07 16:51:48 -05:00
_meters . erase ( m ) ;
2021-02-07 13:23:37 -05:00
remove_point ( mp ) ;
2020-12-07 16:51:48 -05:00
reset_starting_at ( sc ) ;
2020-08-05 18:02:39 -04:00
}
Temporal : : BBT_Time
TempoMap : : bbt_at ( timepos_t const & pos ) const
{
if ( pos . is_beats ( ) ) {
return bbt_at ( pos . beats ( ) ) ;
}
return bbt_at ( pos . superclocks ( ) ) ;
}
Temporal : : BBT_Time
TempoMap : : bbt_at ( superclock_t s ) const
{
2021-03-25 12:24:05 -04:00
return metric_at ( s ) . bbt_at ( timepos_t : : from_superclock ( s ) ) ;
2020-08-05 18:02:39 -04:00
}
Temporal : : BBT_Time
TempoMap : : bbt_at ( Temporal : : Beats const & qn ) const
{
2021-02-01 01:54:08 -05:00
return metric_at ( qn ) . bbt_at ( qn ) ;
2020-08-05 18:02:39 -04:00
}
#if 0
samplepos_t
TempoMap : : sample_at ( Temporal : : Beats const & qn ) const
{
2021-02-01 01:54:08 -05:00
return superclock_to_samples ( metric_at ( qn ) . superclock_at ( qn ) , TEMPORAL_SAMPLE_RATE ) ;
2020-08-05 18:02:39 -04:00
}
samplepos_t
TempoMap : : sample_at ( Temporal : : BBT_Time const & bbt ) const
{
2021-02-01 01:54:08 -05:00
return samples_to_superclock ( metric_at ( bbt ) . superclock_at ( bbt ) , TEMPORAL_SAMPLE_RATE ) ;
2020-08-05 18:02:39 -04:00
}
samplepos_t
TempoMap : : sample_at ( timepos_t const & pos ) const
{
if ( pos . is_beat ( ) ) {
return sample_at ( pos . beats ( ) ) ;
}
/* somewhat nonsensical to call this under these conditions but ... */
return pos . superclocks ( ) ;
}
# endif
superclock_t
TempoMap : : superclock_at ( Temporal : : Beats const & qn ) const
{
2021-02-01 01:54:08 -05:00
return metric_at ( qn ) . superclock_at ( qn ) ;
2020-08-05 18:02:39 -04:00
}
superclock_t
TempoMap : : superclock_at ( Temporal : : BBT_Time const & bbt ) const
{
2021-02-01 01:54:08 -05:00
return metric_at ( bbt ) . superclock_at ( bbt ) ;
2020-08-05 18:02:39 -04:00
}
superclock_t
TempoMap : : superclock_at ( timepos_t const & pos ) const
{
if ( pos . is_beats ( ) ) {
return superclock_at ( pos . beats ( ) ) ;
}
/* somewhat nonsensical to call this under these conditions but ... */
return pos . superclocks ( ) ;
}
2020-12-07 19:12:47 -05:00
# define S2Sc(s) (samples_to_superclock ((s), TEMPORAL_SAMPLE_RATE))
# define Sc2S(s) (superclock_to_samples ((s), TEMPORAL_SAMPLE_RATE))
2020-08-05 18:02:39 -04:00
/** Count the number of beats that are equivalent to distance when going forward,
starting at pos .
*/
Temporal : : Beats
TempoMap : : scwalk_to_quarters ( superclock_t pos , superclock_t distance ) const
{
TempoMetric first ( metric_at ( pos ) ) ;
TempoMetric last ( metric_at ( pos + distance ) ) ;
2020-12-07 13:39:02 -05:00
Temporal : : Beats a = first . quarters_at_superclock ( pos ) ;
Temporal : : Beats b = last . quarters_at_superclock ( pos + distance ) ;
2020-08-05 18:02:39 -04:00
return b - a ;
}
Temporal : : Beats
TempoMap : : scwalk_to_quarters ( Temporal : : Beats const & pos , superclock_t distance ) const
{
/* XXX this converts from beats to superclock and back to beats... which is OK (reversible) */
2021-02-01 01:54:08 -05:00
superclock_t s = metric_at ( pos ) . superclock_at ( pos ) ;
2020-08-05 18:02:39 -04:00
s + = distance ;
2021-02-01 01:54:08 -05:00
return metric_at ( s ) . quarters_at_superclock ( s ) ;
2020-08-05 18:02:39 -04:00
}
Temporal : : Beats
TempoMap : : bbtwalk_to_quarters ( Beats const & pos , BBT_Offset const & distance ) const
{
2020-12-07 13:39:02 -05:00
return quarters_at ( bbt_walk ( bbt_at ( pos ) , distance ) ) - pos ;
2020-08-05 18:02:39 -04:00
}
void
2020-11-25 12:35:12 -05:00
TempoMap : : sample_rate_changed ( samplecnt_t new_sr )
2020-08-05 18:02:39 -04:00
{
2020-12-07 19:12:47 -05:00
const double ratio = new_sr / ( double ) TEMPORAL_SAMPLE_RATE ;
2020-08-05 18:02:39 -04:00
for ( Tempos : : iterator t = _tempos . begin ( ) ; t ! = _tempos . end ( ) ; + + t ) {
t - > map_reset_set_sclock_for_sr_change ( llrint ( ratio * t - > sclock ( ) ) ) ;
}
for ( Meters : : iterator m = _meters . begin ( ) ; m ! = _meters . end ( ) ; + + m ) {
m - > map_reset_set_sclock_for_sr_change ( llrint ( ratio * m - > sclock ( ) ) ) ;
}
for ( MusicTimes : : iterator p = _bartimes . begin ( ) ; p ! = _bartimes . end ( ) ; + + p ) {
p - > map_reset_set_sclock_for_sr_change ( llrint ( ratio * p - > sclock ( ) ) ) ;
}
}
void
TempoMap : : dump ( std : : ostream & ostr ) const
{
2021-04-04 19:53:49 -04:00
ostr < < " \n \n TEMPO MAP @ " < < this < < " : \n " ;
2020-08-05 18:02:39 -04:00
for ( Tempos : : const_iterator t = _tempos . begin ( ) ; t ! = _tempos . end ( ) ; + + t ) {
ostr < < & * t < < ' ' < < * t < < endl ;
}
for ( Meters : : const_iterator m = _meters . begin ( ) ; m ! = _meters . end ( ) ; + + m ) {
ostr < < & * m < < ' ' < < * m < < endl ;
}
2020-12-20 13:39:12 -05:00
for ( MusicTimes : : const_iterator m = _bartimes . begin ( ) ; m ! = _bartimes . end ( ) ; + + m ) {
ostr < < & * m < < ' ' < < * m < < endl ;
}
ostr < < " ------------ \n \n \n " ;
2020-08-05 18:02:39 -04:00
}
2021-04-06 12:43:55 -04:00
template < class const_traits_t > typename const_traits_t : : iterator_type
TempoMap : : _get_tempo_and_meter ( typename const_traits_t : : tempo_point_type & tp ,
typename const_traits_t : : meter_point_type & mp ,
typename const_traits_t : : time_reference ( Point : : * method ) ( ) const ,
typename const_traits_t : : time_type arg ,
typename const_traits_t : : iterator_type begini ,
typename const_traits_t : : iterator_type endi ,
typename const_traits_t : : tempo_point_type tstart ,
typename const_traits_t : : meter_point_type mstart ,
bool can_match , bool ret_iterator_after_not_at ) const
{
typename const_traits_t : : iterator_type p ;
typename const_traits_t : : iterator_type last_used = endi ;
2021-03-26 23:15:10 -04:00
bool tempo_done = false ;
bool meter_done = false ;
assert ( ! _tempos . empty ( ) ) ;
assert ( ! _meters . empty ( ) ) ;
assert ( ! _points . empty ( ) ) ;
2021-04-06 12:43:55 -04:00
/* If the starting position is the beginning of the timeline (indicated
* by the default constructor value for the time_type ( superclock_t ,
* Beats , BBT_Time ) , then we are always allowed to use the tempo &
* meter at that position .
*
* Without this , it would be necessary to special case " can_match " in
* the caller if the start is " zero " . Instead we do that here , since
* common cases ( e . g . : : get_grid ( ) ) will use can_match = false , but may
* pass in a zero start point .
2021-03-27 11:11:10 -04:00
*/
2021-04-06 12:43:55 -04:00
can_match = ( can_match | | arg = = typename const_traits_t : : time_type ( ) ) ;
2021-03-27 11:11:10 -04:00
2021-04-06 12:43:55 -04:00
for ( tp = tstart , mp = mstart , p = begini ; p ! = endi ; + + p ) {
2021-03-26 23:15:10 -04:00
2021-04-06 12:43:55 -04:00
typename const_traits_t : : tempo_point_type tpp ;
typename const_traits_t : : meter_point_type mpp ;
2021-03-26 23:15:10 -04:00
2021-04-06 12:43:55 -04:00
if ( ! tempo_done & & ( tpp = dynamic_cast < typename const_traits_t : : tempo_point_type > ( & ( * p ) ) ) ! = 0 ) {
2021-04-04 19:53:49 -04:00
if ( ( can_match & & ( ( ( * p ) . * method ) ( ) > arg ) ) | | ( ! can_match & & ( ( ( * p ) . * method ) ( ) > = arg ) ) ) {
2021-03-26 23:15:10 -04:00
tempo_done = true ;
} else {
tp = tpp ;
2021-04-04 19:53:49 -04:00
last_used = p ;
2021-03-26 23:15:10 -04:00
}
}
2021-04-06 12:43:55 -04:00
if ( ! meter_done & & ( mpp = dynamic_cast < typename const_traits_t : : meter_point_type > ( & ( * p ) ) ) ! = 0 ) {
2021-04-04 19:53:49 -04:00
if ( ( can_match & & ( ( ( * p ) . * method ) ( ) > arg ) ) | | ( ! can_match & & ( ( ( * p ) . * method ) ( ) > = arg ) ) ) {
meter_done = true ;
} else {
mp = mpp ;
last_used = p ;
}
}
if ( meter_done & & tempo_done ) {
break ;
}
}
if ( ! tp | | ! mp ) {
2021-04-06 12:43:55 -04:00
return endi ;
2021-04-04 19:53:49 -04:00
}
if ( ret_iterator_after_not_at ) {
p = last_used ;
if ( can_match ) {
2021-04-06 12:43:55 -04:00
while ( ( ( * p ) . * method ) ( ) < = arg & & p ! = endi ) + + p ;
2021-04-04 19:53:49 -04:00
} else {
2021-04-06 12:43:55 -04:00
while ( ( ( * p ) . * method ) ( ) < arg & & p ! = endi ) + + p ;
2021-04-04 19:53:49 -04:00
}
return p ;
}
return last_used ;
}
2020-08-05 18:02:39 -04:00
void
TempoMap : : get_grid ( TempoMapPoints & ret , superclock_t start , superclock_t end , uint32_t bar_mod )
{
assert ( ! _tempos . empty ( ) ) ;
assert ( ! _meters . empty ( ) ) ;
2021-02-09 01:02:26 -05:00
assert ( ! _points . empty ( ) ) ;
2020-08-05 18:02:39 -04:00
2021-01-06 12:03:32 -05:00
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " >>> GRID START %1 .. %2 (barmod = %3) \n " , start , end , bar_mod ) ) ;
2020-08-05 18:02:39 -04:00
2021-03-26 23:15:10 -04:00
TempoPoint const * tp = 0 ;
MeterPoint const * mp = 0 ;
Points : : const_iterator p ;
2020-08-05 18:02:39 -04:00
2021-02-09 01:02:26 -05:00
/* first task: get to the right starting point for the requested
* grid . if bar_mod is zero , then we ' ll start on the next beat after
* @ param start . if bar_mod is non - zero , we ' ll start on the first bar
* after @ param start . This bar position may or may not be a part of the
* grid , depending on whether or not it is a multiple of bar_mod .
2021-04-04 19:53:49 -04:00
*
* final argument = true means " return the iterator corresponding the
* point after the latter of the tempo / meter points "
2021-02-09 01:02:26 -05:00
*/
2020-12-06 01:05:17 -05:00
2021-04-04 19:53:49 -04:00
p = get_tempo_and_meter ( tp , mp , start , true , true ) ;
TempoMetric metric = TempoMetric ( * tp , * mp ) ;
2021-03-26 23:15:10 -04:00
2021-02-09 01:02:26 -05:00
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " metric in effect at %1 = %2 \n " , start , metric ) ) ;
/* p now points to either the point *after* start, or the end of the
* _points list .
*
* metric is the TempoMetric that is in effect at start
2020-08-05 18:02:39 -04:00
*/
2021-02-09 01:02:26 -05:00
/* determine the BBT at start */
2021-03-25 12:24:05 -04:00
BBT_Time bbt = metric . bbt_at ( timepos_t : : from_superclock ( start ) ) ;
2021-02-09 01:02:26 -05:00
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " start %1 is %2 \n " , start , bbt ) ) ;
2020-08-05 18:02:39 -04:00
if ( bar_mod = = 0 ) {
/* round to next beat, then find the tempo/meter/bartime points
* in effect at that time .
*/
2020-12-06 01:04:00 -05:00
const BBT_Time new_bbt = metric . meter ( ) . round_up_to_beat ( bbt ) ;
2020-08-05 18:02:39 -04:00
2020-12-06 01:04:00 -05:00
if ( new_bbt ! = bbt ) {
2020-08-05 18:02:39 -04:00
2020-12-06 01:04:00 -05:00
bbt = new_bbt ;
2020-08-05 18:02:39 -04:00
2020-12-06 01:04:00 -05:00
/* rounded up, determine new starting superclock position */
2020-08-05 18:02:39 -04:00
2021-04-04 19:53:49 -04:00
p = get_tempo_and_meter ( tp , mp , bbt , false , true ) ;
2020-12-06 01:04:00 -05:00
2021-02-09 01:02:26 -05:00
metric = TempoMetric ( * tp , * mp ) ;
2021-03-26 23:15:10 -04:00
2021-04-04 19:53:49 -04:00
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " metric in effect(2) at %1 = %2 \n " , bbt , metric ) ) ;
2020-12-06 01:04:00 -05:00
/* recompute superclock position */
superclock_t new_start = metric . superclock_at ( bbt ) ;
2021-03-22 18:06:24 -04:00
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " metric %1 says that %2 is at %3 \n " , metric , bbt , new_start ) ) ;
2020-12-06 01:04:00 -05:00
if ( new_start < start ) {
2021-03-22 18:06:24 -04:00
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " we've gone backwards, new is %1 start is %2 \n " , new_start , start ) ) ;
2020-12-06 01:04:00 -05:00
abort ( ) ;
}
start = new_start ;
2020-08-05 18:02:39 -04:00
2020-12-06 01:04:00 -05:00
} else {
2021-01-06 12:03:32 -05:00
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " %1 was on a beat, no rounding up necessary \n " , bbt ) ) ;
2020-12-06 01:04:00 -05:00
}
2020-08-05 18:02:39 -04:00
} else {
BBT_Time bar = bbt . round_down_to_bar ( ) ;
2021-02-09 01:02:26 -05:00
/* adjust to match bar_mod (i.e. we only want every 4th bar)
*/
2020-08-05 18:02:39 -04:00
if ( bar_mod ! = 1 ) {
bar . bars - = bar . bars % bar_mod ;
+ + bar . bars ;
}
2021-02-09 01:02:26 -05:00
/* the rounding we've just done cannot change the meter in
effect , because it remains within the bar . But it could
change the tempo ( which are only quantized to grid positions
within a bar ) . So if it has generated a new BBT time ,
recompute the metric .
*/
2020-12-06 01:04:00 -05:00
if ( bar ! = bbt ) {
2020-08-05 18:02:39 -04:00
2020-12-06 01:04:00 -05:00
bbt = bar ;
2020-08-05 18:02:39 -04:00
2021-04-04 19:53:49 -04:00
p = get_tempo_and_meter ( tp , mp , bbt , true , true ) ;
2021-02-09 01:02:26 -05:00
metric = TempoMetric ( * tp , * mp ) ;
2021-03-26 23:15:10 -04:00
2021-03-25 13:29:45 -04:00
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " metric in effect(3) at %1 = %2 \n " , start , metric ) ) ;
2020-12-06 01:04:00 -05:00
start = metric . superclock_at ( bbt ) ;
} else {
2021-01-06 12:03:32 -05:00
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " %1 was on a bar, no round down to bar necessary \n " , bbt ) ) ;
2020-12-06 01:04:00 -05:00
}
2020-08-05 18:02:39 -04:00
}
/* at this point:
*
2021-02-09 01:02:26 -05:00
* - metric is a TempoMetric that describes the situation at start
* - p is an iterator pointin to either the end of the _points list , or
* the next point in the list after start .
2020-08-05 18:02:39 -04:00
*/
2021-02-09 01:02:26 -05:00
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " start filling points with start = %1 end = %2 with limit @ %3 \n " , start , end , * p ) ) ;
2020-12-06 01:05:17 -05:00
2020-08-05 18:02:39 -04:00
while ( start < end ) {
2021-02-09 01:02:26 -05:00
Temporal : : Beats beats = metric . quarters_at_superclock ( start ) ;
2020-08-05 18:02:39 -04:00
2021-02-09 01:02:26 -05:00
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " start %1 end %2 bbt %3 find first/limit with limit @ = %4 \n " , start , end , bbt , * p ) ) ;
2020-08-05 18:02:39 -04:00
2021-02-09 01:02:26 -05:00
while ( start < p - > sclock ( ) & & start < end ) {
2020-08-05 18:02:39 -04:00
2021-02-09 01:02:26 -05:00
/* add point to grid, perhaps */
2021-01-31 20:48:23 -05:00
2021-02-11 00:30:05 -05:00
if ( bar_mod ! = 0 ) {
if ( bbt . is_bar ( ) & & ( bar_mod = = 1 | | ( ( bbt . bars % bar_mod = = 0 ) ) ) ) {
ret . push_back ( TempoMapPoint ( * this , metric , start , beats , bbt ) ) ;
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " G %1 \t %2 \n " , metric , ret . back ( ) ) ) ;
} else {
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " -- skip %1 not on bar_mod %2 \n " , bbt , bar_mod ) ) ;
}
2021-02-09 01:02:26 -05:00
} else {
ret . push_back ( TempoMapPoint ( * this , metric , start , beats , bbt ) ) ;
2021-02-11 00:30:05 -05:00
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " G %1 \t %2 \n " , metric , ret . back ( ) ) ) ;
2020-08-05 18:02:39 -04:00
}
2021-02-09 01:02:26 -05:00
superclock_t step ;
2020-08-05 18:02:39 -04:00
2021-02-09 01:02:26 -05:00
if ( bar_mod = = 0 ) {
2021-01-07 18:31:30 -05:00
2021-02-09 01:02:26 -05:00
/* Advance by the meter note value size */
2021-01-07 18:31:30 -05:00
2021-02-09 01:02:26 -05:00
step = metric . superclocks_per_grid_at ( start ) ;
start + = step ;
2021-03-25 12:24:05 -04:00
bbt = metric . bbt_at ( timepos_t : : from_superclock ( start ) ) ;
2021-02-09 01:02:26 -05:00
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " step for note type was %1, now @ %2 \n " , step , start ) ) ;
2021-01-07 18:31:30 -05:00
2021-02-09 01:02:26 -05:00
} else {
2021-01-31 20:48:23 -05:00
2021-02-09 01:02:26 -05:00
/* Advance by the number of bars specified by bar_mod */
2021-01-07 18:31:30 -05:00
2021-02-09 01:02:26 -05:00
bbt . bars + = bar_mod ;
start = metric . superclock_at ( bbt ) ;
2021-02-11 00:30:05 -05:00
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " bar mod %1 moved to %2 (start %3) \n " , bar_mod , bbt , start ) )
2021-01-07 18:31:30 -05:00
}
2021-02-09 01:02:26 -05:00
}
/* we might be finished ...*/
2021-01-07 18:31:30 -05:00
2021-02-09 01:02:26 -05:00
if ( start > = end ) {
2020-08-05 18:02:39 -04:00
break ;
}
2021-02-11 00:30:05 -05:00
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " stopped fill with start %1 and point at %2 \n " , start , ( p = = _points . end ( ) ? - 1 : p - > sclock ( ) ) ) ) ;
2020-08-05 18:02:39 -04:00
2021-02-09 01:02:26 -05:00
while ( ( p ! = _points . end ( ) ) & & ( start > = p - > sclock ( ) ) ) {
2020-08-05 18:02:39 -04:00
2021-02-09 01:02:26 -05:00
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " pausing to deal with point => %1 \n " , * p ) ) ;
2020-08-05 18:02:39 -04:00
2021-02-09 01:02:26 -05:00
/* have just passed or arrived at the next
* point . Consider adding a grid point for this point .
*/
2020-08-05 18:02:39 -04:00
2021-02-09 01:02:26 -05:00
start = p - > sclock ( ) ;
bbt = p - > bbt ( ) ;
beats = p - > beats ( ) ;
2020-08-05 18:02:39 -04:00
2021-02-11 00:30:05 -05:00
if ( bar_mod ! = 0 ) {
if ( bbt . is_bar ( ) & & ( bar_mod = = 1 | | ( ( bbt . bars % bar_mod = = 0 ) ) ) ) {
ret . push_back ( TempoMapPoint ( * this , metric , start , beats , bbt ) ) ;
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " G %1 \t %2 \n " , metric , ret . back ( ) ) ) ;
} else {
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " -- skip %1 not on bar_mod %2 \n " , bbt , bar_mod ) ) ;
}
2021-02-09 01:02:26 -05:00
} else {
ret . push_back ( TempoMapPoint ( * this , metric , start , beats , bbt ) ) ;
2021-02-11 00:30:05 -05:00
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " G %1 \t %2 \n " , metric , ret . back ( ) ) ) ;
2021-02-09 01:02:26 -05:00
}
2020-08-05 18:02:39 -04:00
2021-02-09 01:02:26 -05:00
/* But there may be multiple points here, and we have
* to check them all ( Tempo / Meter / MusicTime . . . which
* is itself both a Tempo * and * Meter point ) before
* proceeding .
*/
2020-08-05 18:02:39 -04:00
2021-02-09 01:02:26 -05:00
const superclock_t pos = p - > sclock ( ) ;
2020-08-05 18:02:39 -04:00
2021-03-26 23:15:10 -04:00
Points : : const_iterator nxt = p ;
2021-02-09 01:02:26 -05:00
+ + nxt ;
2021-01-21 19:31:43 -05:00
2021-03-26 23:15:10 -04:00
TempoPoint const * tpp ;
MeterPoint const * mpp ;
2020-08-05 18:02:39 -04:00
2021-02-09 01:02:26 -05:00
/* use this point */
2020-08-05 18:02:39 -04:00
2021-03-26 23:15:10 -04:00
if ( ( tpp = dynamic_cast < TempoPoint const * > ( & ( * p ) ) ) ! = 0 ) {
2021-02-09 01:02:26 -05:00
tp = tpp ;
2020-08-05 18:02:39 -04:00
}
2021-03-26 23:15:10 -04:00
if ( ( mpp = dynamic_cast < MeterPoint const * > ( & ( * p ) ) ) ! = 0 ) {
2021-02-09 01:02:26 -05:00
mp = mpp ;
2020-08-05 18:02:39 -04:00
}
2021-02-09 01:02:26 -05:00
/* use any subsequent ones at the same location */
2020-08-05 18:02:39 -04:00
2021-02-09 01:02:26 -05:00
while ( ( nxt ! = _points . end ( ) ) & & ( nxt - > sclock ( ) = = pos ) ) {
2021-01-31 20:48:23 -05:00
2021-02-09 01:02:26 -05:00
/* Set up the new metric given the new point */
2020-08-05 18:02:39 -04:00
2021-03-26 23:15:10 -04:00
if ( ( tpp = dynamic_cast < TempoPoint const * > ( & ( * nxt ) ) ) ! = 0 ) {
2021-02-09 01:02:26 -05:00
tp = tpp ;
2020-08-05 18:02:39 -04:00
}
2021-03-26 23:15:10 -04:00
if ( ( mpp = dynamic_cast < MeterPoint const * > ( & ( * nxt ) ) ) ! = 0 ) {
2021-02-09 01:02:26 -05:00
mp = mpp ;
}
2020-08-05 18:02:39 -04:00
2021-02-09 01:02:26 -05:00
+ + nxt ;
}
2020-08-05 18:02:39 -04:00
2021-02-09 01:02:26 -05:00
/* Build a new metric from the composite of all the
* points at this position .
*/
2020-08-05 18:02:39 -04:00
2021-02-09 01:02:26 -05:00
metric = TempoMetric ( * tp , * mp ) ;
p = nxt ;
}
2020-08-05 18:02:39 -04:00
2021-02-09 01:02:26 -05:00
if ( p = = _points . end ( ) ) {
break ;
}
}
2020-08-05 18:02:39 -04:00
2021-02-09 01:02:26 -05:00
/* reached the end or no more points to consider, so just
* finish by filling the grid to the end , if necessary .
*/
2020-08-05 18:02:39 -04:00
2021-02-09 01:02:26 -05:00
if ( start < end ) {
2020-08-05 18:02:39 -04:00
2021-02-09 01:02:26 -05:00
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " reached end, no more map points, finish between %1 .. %2 \n " , start , end ) ) ;
2020-08-05 18:02:39 -04:00
2021-02-09 01:02:26 -05:00
/* note: if start < end, then p == _points.end(). This means there are
* no more Points beyond the current value of start .
*
* Since there are no more Points beyond start , the current metric
* cannot involve a ramp , so the step size per grid element is
* constant . metric will also remain constant until we finish .
*/
2020-08-05 18:02:39 -04:00
2021-02-09 01:02:26 -05:00
const superclock_t step = metric . superclocks_per_grid_at ( start ) ;
2021-01-31 20:39:29 -05:00
2021-02-09 01:02:26 -05:00
do {
const Temporal : : Beats beats = metric . quarters_at_superclock ( start ) ;
2021-02-11 00:30:05 -05:00
if ( bar_mod ! = 0 ) {
if ( bbt . is_bar ( ) & & ( bar_mod = = 1 | | ( ( bbt . bars % bar_mod = = 0 ) ) ) ) {
ret . push_back ( TempoMapPoint ( * this , metric , start , beats , bbt ) ) ;
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " Gend %1 \t %2 \n " , metric , ret . back ( ) ) ) ;
}
} else {
ret . push_back ( TempoMapPoint ( * this , metric , start , beats , bbt ) ) ;
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " Gend %1 \t %2 \n " , metric , ret . back ( ) ) ) ;
}
2021-02-09 01:02:26 -05:00
start + = step ;
2021-03-25 12:24:05 -04:00
bbt = metric . bbt_at ( timepos_t : : from_superclock ( start ) ) ;
2021-01-31 20:39:29 -05:00
2021-02-09 01:02:26 -05:00
} while ( start < end ) ;
2020-08-05 18:02:39 -04:00
2021-02-09 01:02:26 -05:00
/* all done */
} else {
if ( p = = _points . end ( ) ) {
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " ended loop with start %1 end %2, p @ END \n " , start , end ) ) ;
} else {
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " ended loop with start %1 end %2, p=> %3 \n " , start , end , * p ) ) ;
2020-08-05 18:02:39 -04:00
}
}
2021-01-06 12:03:32 -05:00
DEBUG_TRACE ( DEBUG : : Grid , " <<< GRID DONE \n " ) ;
2020-08-05 18:02:39 -04:00
}
2021-03-19 16:17:29 -04:00
uint32_t
TempoMap : : count_bars ( Beats const & start , Beats const & end )
{
TempoMapPoints bar_grid ;
superclock_t s ( superclock_at ( start ) ) ;
superclock_t e ( superclock_at ( end ) ) ;
get_grid ( bar_grid , s , e , 1 ) ;
return bar_grid . size ( ) ;
}
2020-08-05 18:02:39 -04:00
std : : ostream &
std : : operator < < ( std : : ostream & str , Meter const & m )
{
return str < < m . divisions_per_bar ( ) < < ' / ' < < m . note_value ( ) ;
}
std : : ostream &
std : : operator < < ( std : : ostream & str , Tempo const & t )
{
2020-12-20 13:39:12 -05:00
if ( t . ramped ( ) ) {
2021-01-24 17:25:43 -05:00
return str < < t . note_types_per_minute ( ) < < " 1/ " < < t . note_type ( ) < < " RAMPED notes per minute [ " < < t . super_note_type_per_second ( ) < < " => " < < t . end_super_note_type_per_second ( ) < < " sntpm ] ( " < < t . superclocks_per_note_type ( ) < < " sc-per-1/ " < < t . note_type ( ) < < ' ) ' ;
2020-12-20 13:39:12 -05:00
} else {
return str < < t . note_types_per_minute ( ) < < " 1/ " < < t . note_type ( ) < < " notes per minute [ " < < t . super_note_type_per_second ( ) < < " sntpm] ( " < < t . superclocks_per_note_type ( ) < < " sc-per-1/ " < < t . note_type ( ) < < ' ) ' ;
}
2020-08-05 18:02:39 -04:00
}
std : : ostream &
std : : operator < < ( std : : ostream & str , Point const & p )
{
return str < < " P@ " < < p . sclock ( ) < < ' / ' < < p . beats ( ) < < ' / ' < < p . bbt ( ) ;
}
std : : ostream &
std : : operator < < ( std : : ostream & str , MeterPoint const & m )
{
return str < < * ( ( Meter const * ) & m ) < < ' ' < < * ( ( Point const * ) & m ) ;
}
std : : ostream &
std : : operator < < ( std : : ostream & str , TempoPoint const & t )
{
str < < * ( ( Tempo const * ) & t ) < < ' ' < < * ( ( Point const * ) & t ) ;
if ( t . ramped ( ) ) {
if ( t . actually_ramped ( ) ) {
str < < ' ' < < " ramp to " < < t . end_note_types_per_minute ( ) ;
} else {
str < < ' ' < < " !ramp to " < < t . end_note_types_per_minute ( ) ;
}
str < < " omega = " < < std : : setprecision ( 12 ) < < t . omega ( ) ;
}
return str ;
}
2021-02-07 00:09:16 -05:00
std : : ostream &
std : : operator < < ( std : : ostream & str , MusicTimePoint const & p )
{
str < < " MP@ " ;
2021-02-07 13:05:17 -05:00
str < < * ( ( Point const * ) & p ) ;
str < < * ( ( Tempo const * ) & p ) ;
str < < * ( ( Meter const * ) & p ) ;
2021-02-07 00:09:16 -05:00
return str ;
}
2020-08-05 18:02:39 -04:00
std : : ostream &
std : : operator < < ( std : : ostream & str , TempoMetric const & tm )
{
return str < < tm . tempo ( ) < < ' ' < < tm . meter ( ) ;
}
std : : ostream &
std : : operator < < ( std : : ostream & str , TempoMapPoint const & tmp )
{
str < < ' @ ' < < std : : setw ( 12 ) < < tmp . sclock ( ) < < ' ' < < tmp . sclock ( ) / ( double ) superclock_ticks_per_second
2021-01-31 20:48:23 -05:00
< < " secs " < < tmp . sample ( TEMPORAL_SAMPLE_RATE ) < < " samples "
2020-08-05 18:02:39 -04:00
< < ( tmp . is_explicit_tempo ( ) ? " EXP-T " : " imp-t " )
< < ( tmp . is_explicit_meter ( ) ? " EXP-M " : " imp-m " )
< < ( tmp . is_explicit_position ( ) ? " EXP-P " : " imp-p " )
< < " qn " < < tmp . beats ( )
< < " bbt " < < tmp . bbt ( )
;
if ( tmp . is_explicit_tempo ( ) ) {
str < < " tempo " < < tmp . tempo ( ) ;
}
if ( tmp . is_explicit_meter ( ) ) {
str < < " meter " < < tmp . meter ( ) ;
}
if ( tmp . is_explicit_tempo ( ) & & tmp . tempo ( ) . ramped ( ) ) {
str < < " ramp omega = " < < tmp . tempo ( ) . omega ( ) ;
}
return str ;
}
BBT_Time
TempoMap : : bbt_walk ( BBT_Time const & bbt , BBT_Offset const & o ) const
{
BBT_Offset offset ( o ) ;
Tempos : : const_iterator t , prev_t , next_t ;
Meters : : const_iterator m , prev_m , next_m ;
assert ( ! _tempos . empty ( ) ) ;
assert ( ! _meters . empty ( ) ) ;
/* trivial (and common) case: single tempo, single meter */
if ( _tempos . size ( ) = = 1 & & _meters . size ( ) = = 1 ) {
return _meters . front ( ) . bbt_add ( bbt , o ) ;
}
/* Find tempo,meter pair for bbt, and also for the next tempo and meter
* after each ( if any )
*/
/* Yes, linear search because the typical size of _tempos and _meters
* is 1 , and extreme sizes are on the order of 10
*/
next_t = _tempos . end ( ) ;
next_m = _meters . end ( ) ;
for ( t = _tempos . begin ( ) , prev_t = t ; t ! = _tempos . end ( ) & & t - > bbt ( ) < bbt ; ) {
prev_t = t ;
+ + t ;
if ( t ! = _tempos . end ( ) ) {
next_t = t ;
+ + next_t ;
}
}
for ( m = _meters . begin ( ) , prev_m = m ; m ! = _meters . end ( ) & & m - > bbt ( ) < bbt ; ) {
prev_m = m ;
+ + m ;
if ( m ! = _meters . end ( ) ) {
next_m = m ;
+ + next_m ;
}
}
/* may have found tempo and/or meter precisely at the tiem given */
if ( t ! = _tempos . end ( ) & & t - > bbt ( ) = = bbt ) {
prev_t = t ;
}
if ( m ! = _meters . end ( ) & & m - > bbt ( ) = = bbt ) {
prev_m = m ;
}
2021-02-01 01:54:08 -05:00
/* see ::metric_at() for comments about the use of const_cast here
2020-08-05 18:02:39 -04:00
*/
TempoMetric metric ( * const_cast < TempoPoint * > ( & * prev_t ) , * const_cast < MeterPoint * > ( & * prev_m ) ) ;
superclock_t pos = metric . superclock_at ( bbt ) ;
/* normalize possibly too-large ticks count */
const int32_t tpg = metric . meter ( ) . ticks_per_grid ( ) ;
if ( offset . ticks > tpg ) {
/* normalize */
offset . beats + = offset . ticks / tpg ;
offset . ticks % = tpg ;
}
/* add tick count, now guaranteed to be less than 1 grid unit */
if ( offset . ticks ) {
pos + = metric . superclocks_per_ppqn ( ) * offset . ticks ;
}
/* add each beat, 1 by 1, rechecking to see if there's a new
* TempoMetric in effect after each addition
*/
# define TEMPO_CHECK_FOR_NEW_METRIC \
if ( ( ( next_t ! = _tempos . end ( ) ) & & ( pos > = next_t - > sclock ( ) ) ) | | \
( ( next_m ! = _meters . end ( ) ) & & ( pos > = next_m - > sclock ( ) ) ) ) { \
/* need new metric */ \
if ( pos > = next_t - > sclock ( ) ) { \
if ( pos > = next_m - > sclock ( ) ) { \
metric = TempoMetric ( * const_cast < TempoPoint * > ( & * next_t ) , * const_cast < MeterPoint * > ( & * next_m ) ) ; \
+ + next_t ; \
+ + next_m ; \
} else { \
metric = TempoMetric ( * const_cast < TempoPoint * > ( & * next_t ) , metric . meter ( ) ) ; \
+ + next_t ; \
} \
} else if ( pos > = next_m - > sclock ( ) ) { \
metric = TempoMetric ( metric . tempo ( ) , * const_cast < MeterPoint * > ( & * next_m ) ) ; \
+ + next_m ; \
} \
}
for ( int32_t b = 0 ; b < offset . beats ; + + b ) {
TEMPO_CHECK_FOR_NEW_METRIC ;
2021-01-06 12:06:14 -05:00
pos + = metric . superclocks_per_grid ( ) ;
2020-08-05 18:02:39 -04:00
}
/* add each bar, 1 by 1, rechecking to see if there's a new
* TempoMetric in effect after each addition
*/
for ( int32_t b = 0 ; b < offset . bars ; + + b ) {
TEMPO_CHECK_FOR_NEW_METRIC ;
2021-01-06 12:06:14 -05:00
pos + = metric . superclocks_per_bar ( ) ;
2020-08-05 18:02:39 -04:00
}
2021-03-25 12:24:05 -04:00
return metric . bbt_at ( timepos_t : : from_superclock ( pos ) ) ;
2020-08-05 18:02:39 -04:00
}
Temporal : : Beats
2020-12-07 13:39:02 -05:00
TempoMap : : quarters_at ( timepos_t const & pos ) const
2020-08-05 18:02:39 -04:00
{
if ( pos . is_beats ( ) ) {
/* a bit redundant */
return pos . beats ( ) ;
}
2020-12-07 14:28:21 -05:00
return quarters_at_superclock ( pos . superclocks ( ) ) ;
2020-08-05 18:02:39 -04:00
}
Temporal : : Beats
2020-12-07 13:39:02 -05:00
TempoMap : : quarters_at ( Temporal : : BBT_Time const & bbt ) const
2020-08-05 18:02:39 -04:00
{
2021-02-01 01:54:08 -05:00
return metric_at ( bbt ) . quarters_at ( bbt ) ;
2020-08-05 18:02:39 -04:00
}
Temporal : : Beats
2020-12-07 13:39:02 -05:00
TempoMap : : quarters_at_superclock ( superclock_t pos ) const
2020-08-05 18:02:39 -04:00
{
2021-02-01 01:54:08 -05:00
return metric_at ( pos ) . quarters_at_superclock ( pos ) ;
2020-08-05 18:02:39 -04:00
}
XMLNode &
TempoMap : : get_state ( )
{
XMLNode * node = new XMLNode ( X_ ( " TempoMap " ) ) ;
node - > set_property ( X_ ( " time-domain " ) , _time_domain ) ;
node - > set_property ( X_ ( " superclocks-per-second " ) , superclock_ticks_per_second ) ;
XMLNode * children ;
children = new XMLNode ( X_ ( " Tempos " ) ) ;
node - > add_child_nocopy ( * children ) ;
for ( Tempos : : const_iterator t = _tempos . begin ( ) ; t ! = _tempos . end ( ) ; + + t ) {
children - > add_child_nocopy ( t - > get_state ( ) ) ;
}
children = new XMLNode ( X_ ( " Meters " ) ) ;
node - > add_child_nocopy ( * children ) ;
for ( Meters : : const_iterator m = _meters . begin ( ) ; m ! = _meters . end ( ) ; + + m ) {
children - > add_child_nocopy ( m - > get_state ( ) ) ;
}
children = new XMLNode ( X_ ( " MusicTimes " ) ) ;
node - > add_child_nocopy ( * children ) ;
for ( MusicTimes : : const_iterator b = _bartimes . begin ( ) ; b ! = _bartimes . end ( ) ; + + b ) {
children - > add_child_nocopy ( b - > get_state ( ) ) ;
}
return * node ;
}
int
2021-03-23 17:22:28 -04:00
TempoMap : : set_state ( XMLNode const & node , int version )
2020-08-05 18:02:39 -04:00
{
2021-04-04 19:53:49 -04:00
cerr < < " \n \n \n TMAP set state \n \n " ;
2021-03-25 10:51:08 -04:00
if ( version < = 6000 ) {
2021-04-04 19:53:49 -04:00
cerr < < " Old version " < < version < < " \n " ;
2021-03-23 17:22:28 -04:00
return set_state_3x ( node ) ;
}
2020-12-07 16:51:48 -05:00
/* global map properties */
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
/* XXX this should probably be at the global level in the session file because it affects a lot more than just the tempo map, potentially */
node . get_property ( X_ ( " superclocks-per-second " ) , superclock_ticks_per_second ) ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
node . get_property ( X_ ( " time-domain " ) , _time_domain ) ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
XMLNodeList const & children ( node . children ( ) ) ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
for ( XMLNodeList : : const_iterator c = children . begin ( ) ; c ! = children . end ( ) ; + + c ) {
if ( ( * c ) - > name ( ) = = X_ ( " Tempos " ) ) {
if ( set_tempos_from_state ( * * c ) ) {
2021-04-04 19:53:49 -04:00
cerr < < " tempo fail \n " ;
2020-12-07 16:51:48 -05:00
return - 1 ;
2020-08-05 18:02:39 -04:00
}
2020-12-07 16:51:48 -05:00
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
if ( ( * c ) - > name ( ) = = X_ ( " Meters " ) ) {
if ( set_meters_from_state ( * * c ) ) {
2021-04-04 19:53:49 -04:00
cerr < < " meter fail \n " ;
2020-12-07 16:51:48 -05:00
return - 1 ;
2020-08-05 18:02:39 -04:00
}
2020-12-07 16:51:48 -05:00
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
if ( ( * c ) - > name ( ) = = X_ ( " MusicTimes " ) ) {
if ( set_music_times_from_state ( * * c ) ) {
2021-04-04 19:53:49 -04:00
cerr < < " bartimes fail \n " ;
2020-12-07 16:51:48 -05:00
return - 1 ;
2020-08-05 18:02:39 -04:00
}
}
}
2021-04-04 19:53:49 -04:00
for ( Tempos : : iterator t = _tempos . begin ( ) ; t ! = _tempos . end ( ) ; + + t ) { _points . push_back ( * t ) ; }
for ( Meters : : iterator m = _meters . begin ( ) ; m ! = _meters . end ( ) ; + + m ) { _points . push_back ( * m ) ; }
for ( MusicTimes : : iterator b = _bartimes . begin ( ) ; b ! = _bartimes . end ( ) ; + + b ) { _points . push_back ( * b ) ; }
_points . sort ( Point : : sclock_comparator ( ) ) ;
cerr < < " MAP LOADED \n " ;
dump ( cerr ) ;
2020-08-05 18:02:39 -04:00
return 0 ;
}
int
2020-12-20 13:39:12 -05:00
TempoMap : : set_music_times_from_state ( XMLNode const & mt_node )
2020-08-05 18:02:39 -04:00
{
2020-12-20 13:39:12 -05:00
XMLNodeList const & children ( mt_node . children ( ) ) ;
try {
_bartimes . clear ( ) ;
for ( XMLNodeList : : const_iterator c = children . begin ( ) ; c ! = children . end ( ) ; + + c ) {
MusicTimePoint * mp = new MusicTimePoint ( * this , * * c ) ;
_bartimes . push_back ( * mp ) ;
}
} catch ( . . . ) {
_bartimes . clear ( ) ; /* remove any that were created */
return - 1 ;
}
2020-08-05 18:02:39 -04:00
return 0 ;
}
int
TempoMap : : set_tempos_from_state ( XMLNode const & tempos_node )
{
XMLNodeList const & children ( tempos_node . children ( ) ) ;
try {
_tempos . clear ( ) ;
for ( XMLNodeList : : const_iterator c = children . begin ( ) ; c ! = children . end ( ) ; + + c ) {
2020-12-19 20:29:44 -05:00
TempoPoint * tp = new TempoPoint ( * this , * * c ) ;
_tempos . push_back ( * tp ) ;
2020-08-05 18:02:39 -04:00
}
} catch ( . . . ) {
_tempos . clear ( ) ; /* remove any that were created */
return - 1 ;
}
return 0 ;
}
int
TempoMap : : set_meters_from_state ( XMLNode const & meters_node )
{
XMLNodeList const & children ( meters_node . children ( ) ) ;
try {
_meters . clear ( ) ;
for ( XMLNodeList : : const_iterator c = children . begin ( ) ; c ! = children . end ( ) ; + + c ) {
2020-12-19 20:29:44 -05:00
MeterPoint * mp = new MeterPoint ( * this , * * c ) ;
_meters . push_back ( * mp ) ;
2020-08-05 18:02:39 -04:00
}
} catch ( . . . ) {
_meters . clear ( ) ; /* remove any that were created */
return - 1 ;
}
return 0 ;
}
bool
TempoMap : : can_remove ( TempoPoint const & t ) const
{
return ! is_initial ( t ) ;
}
bool
TempoMap : : is_initial ( TempoPoint const & t ) const
{
return t . sclock ( ) = = 0 ;
}
bool
TempoMap : : is_initial ( MeterPoint const & m ) const
{
return m . sclock ( ) = = 0 ;
}
bool
TempoMap : : can_remove ( MeterPoint const & m ) const
{
return ! is_initial ( m ) ;
}
2020-11-24 17:06:35 -05:00
/** returns the duration (using the domain of @param pos) of the supplied BBT time at a specified sample position in the tempo map.
2020-08-05 18:02:39 -04:00
* @ param pos the frame position in the tempo map .
* @ param bbt the distance in BBT time from pos to calculate .
* @ param dir the rounding direction . .
2020-11-24 17:06:35 -05:00
* @ return the timecnt_t that @ param bbt represents when starting at @ param pos , in
* the time domain of @ param pos
2020-08-05 18:02:39 -04:00
*/
2020-11-24 17:06:35 -05:00
timecnt_t
TempoMap : : bbt_duration_at ( timepos_t const & pos , BBT_Offset const & dur ) const
2020-08-05 18:02:39 -04:00
{
2020-11-24 17:06:35 -05:00
if ( pos . time_domain ( ) = = AudioTime ) {
return timecnt_t : : from_superclock ( superclock_at ( bbt_walk ( bbt_at ( pos ) , dur ) ) - pos . superclocks ( ) , pos ) ;
}
return timecnt_t ( bbtwalk_to_quarters ( pos . beats ( ) , dur ) - pos . beats ( ) , pos ) ;
2020-08-05 18:02:39 -04:00
}
2021-02-26 14:43:50 -05:00
/** Takes a position and distance (both in any time domain), and returns a timecnt_t
* that describes that distance from that position in a specified time domain .
2020-08-05 18:02:39 -04:00
*
2021-02-26 14:43:50 -05:00
* This method is used when converting a ( distance , pos ) pair ( a timecnt_t ) into
* ( distance , pos ' ) in a different time domain . For an english example : " given a
* distance of N beats from position P , what is that same distance measured in
* superclocks from P ' ? "
*
* A different example : " given a distance N superclocks from position P, what
* is that same distance measured in beats from P ' ? "
*
* There is a trivial case in which the requested return domain is the same as
* the domain for the distance . In english : " given a distance of N beats from
* P , what is the same distance measured in beats from P ' ? " In this case, we
* can simply construct a new timecnt_t that uses P ' instead of P , since the
* distance component must necessarily be same . Notice that this true no matter
* what domain is used .
2020-08-05 18:02:39 -04:00
*/
timecnt_t
2021-02-26 15:02:45 -05:00
TempoMap : : convert_duration ( timecnt_t const & duration , timepos_t const & new_position , TimeDomain return_domain ) const
2020-08-05 18:02:39 -04:00
{
2020-09-20 18:33:43 -04:00
timepos_t p ( return_domain ) ;
2020-08-05 18:02:39 -04:00
Beats b ;
superclock_t s ;
if ( return_domain = = duration . time_domain ( ) ) {
2021-02-26 14:44:18 -05:00
/* new timecnt_t: same distance, but new position */
2021-02-26 15:02:45 -05:00
return timecnt_t ( duration . distance ( ) , new_position ) ;
2020-08-05 18:02:39 -04:00
}
switch ( return_domain ) {
case AudioTime :
switch ( duration . time_domain ( ) ) {
case AudioTime :
/*NOTREACHED*/
break ;
case BeatTime :
/* duration is in beats but we're asked to return superclocks */
2021-02-26 15:02:45 -05:00
switch ( new_position . time_domain ( ) ) {
2020-08-05 18:02:39 -04:00
case BeatTime :
2021-02-26 15:02:45 -05:00
/* new_position is already in beats */
p = new_position ;
2020-08-05 18:02:39 -04:00
break ;
case AudioTime :
/* Determine beats at sc pos, so that we can add beats */
2021-02-26 15:02:45 -05:00
p = timepos_t ( metric_at ( new_position ) . quarters_at_superclock ( new_position . superclocks ( ) ) ) ;
2020-08-05 18:02:39 -04:00
break ;
}
/* add beats */
p + = duration ;
/* determine superclocks */
s = metric_at ( p ) . superclock_at ( p . beats ( ) ) ;
/* return duration in sc */
2021-02-26 15:02:45 -05:00
return timecnt_t : : from_superclock ( s - new_position . superclocks ( ) , new_position ) ;
2020-08-05 18:02:39 -04:00
break ;
}
break ;
case BeatTime :
switch ( duration . time_domain ( ) ) {
case AudioTime :
/* duration is in superclocks but we're asked to return beats */
2021-02-26 15:02:45 -05:00
switch ( new_position . time_domain ( ) ) {
2020-08-05 18:02:39 -04:00
case AudioTime :
/* pos is already in superclocks */
2021-02-26 15:02:45 -05:00
p = new_position ;
2020-08-05 18:02:39 -04:00
break ;
case BeatTime :
/* determined sc at beat position so we can add superclocks */
2021-02-26 15:02:45 -05:00
p = timepos_t ( metric_at ( new_position ) . sample_at ( new_position . beats ( ) ) ) ;
2020-08-05 18:02:39 -04:00
break ;
}
/* add superclocks */
p + = duration ;
/* determine beats */
2020-12-07 13:39:02 -05:00
b = metric_at ( p ) . quarters_at_superclock ( p . superclocks ( ) ) ;
2020-08-05 18:02:39 -04:00
/* return duration in beats */
2021-02-26 15:02:45 -05:00
return timecnt_t ( b - new_position . beats ( ) , new_position ) ;
2020-08-05 18:02:39 -04:00
break ;
case BeatTime :
/*NOTREACHED*/
break ;
}
break ;
}
/*NOTREACHED*/
abort ( ) ;
/*NOTREACHED*/
2020-08-11 20:26:25 -04:00
return timecnt_t : : from_superclock ( 0 ) ;
2020-08-05 18:02:39 -04:00
}
Tempo const *
TempoMap : : next_tempo ( Tempo const & t ) const
{
Tempos : : const_iterator p = _tempos . begin ( ) ;
while ( p ! = _tempos . end ( ) ) {
if ( & t = = & * p ) {
break ;
}
+ + p ;
}
if ( p ! = _tempos . end ( ) ) {
+ + p ;
if ( p ! = _tempos . end ( ) ) {
return & * p ; ;
}
}
return 0 ;
}
uint32_t
TempoMap : : n_meters ( ) const
{
return _meters . size ( ) ;
}
uint32_t
TempoMap : : n_tempos ( ) const
{
return _tempos . size ( ) ;
}
void
TempoMap : : insert_time ( timepos_t const & pos , timecnt_t const & duration )
{
assert ( ! _tempos . empty ( ) ) ;
assert ( ! _meters . empty ( ) ) ;
if ( pos = = std : : numeric_limits < timepos_t > : : min ( ) ) {
/* can't insert time at the front of the map: those entries are fixed */
return ;
}
2020-12-07 16:51:48 -05:00
Tempos : : iterator t ( _tempos . begin ( ) ) ;
Meters : : iterator m ( _meters . begin ( ) ) ;
MusicTimes : : iterator b ( _bartimes . begin ( ) ) ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
TempoPoint current_tempo = * t ;
MeterPoint current_meter = * m ;
2021-02-07 00:09:16 -05:00
MusicTimePoint current_time_point ( * this , 0 , Beats ( ) , BBT_Time ( ) , current_tempo , current_meter ) ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
if ( _bartimes . size ( ) > 0 ) {
current_time_point = * b ;
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
superclock_t sc ;
Beats beats ;
BBT_Time bbt ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
/* set these to true so that we set current_* on our first pass
* through the while loop ( s )
*/
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
bool moved_tempo = true ;
bool moved_meter = true ;
bool moved_bartime = true ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
switch ( duration . time_domain ( ) ) {
case AudioTime :
sc = pos . superclocks ( ) ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
/* handle a common case quickly */
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
if ( ( _tempos . size ( ) < 2 | | sc > _tempos . back ( ) . sclock ( ) ) & &
( _meters . size ( ) < 2 | | sc > _meters . back ( ) . sclock ( ) ) & &
( _bartimes . size ( ) < 2 | | ( _bartimes . empty ( ) | | sc > _bartimes . back ( ) . sclock ( ) ) ) ) {
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
/* only one tempo, plus one meter and zero or
one bartimes , or insertion point is after last
item . nothing to do here .
*/
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
return ;
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
/* advance fundamental iterators to correct position */
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
while ( t ! = _tempos . end ( ) & & t - > sclock ( ) < sc ) + + t ;
while ( m ! = _meters . end ( ) & & m - > sclock ( ) < sc ) + + m ;
while ( b ! = _bartimes . end ( ) & & b - > sclock ( ) < sc ) + + b ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
while ( t ! = _tempos . end ( ) & & m ! = _meters . end ( ) & & b ! = _bartimes . end ( ) ) {
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
if ( moved_tempo & & t ! = _tempos . end ( ) ) {
current_tempo = * t ;
moved_tempo = false ;
}
if ( moved_meter & & m ! = _meters . end ( ) ) {
current_meter = * m ;
moved_meter = false ;
}
if ( moved_bartime & & b ! = _bartimes . end ( ) ) {
current_time_point = * b ;
moved_bartime = false ;
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
/* for each of t, m and b:
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
if the point is earlier than the other two ,
recompute the superclock , beat and bbt
positions , and reset the point .
*/
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
if ( t - > sclock ( ) < m - > sclock ( ) & & t - > sclock ( ) < b - > sclock ( ) ) {
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
sc = t - > sclock ( ) + duration . superclocks ( ) ;
beats = current_tempo . quarters_at_superclock ( sc ) ;
/* round tempo to beats */
beats = beats . round_to_beat ( ) ;
sc = current_tempo . superclock_at ( beats ) ;
bbt = current_meter . bbt_at ( beats ) ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
t - > set ( sc , beats , bbt ) ;
+ + t ;
moved_tempo = true ;
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
if ( m - > sclock ( ) < t - > sclock ( ) & & m - > sclock ( ) < b - > sclock ( ) ) {
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
sc = m - > sclock ( ) + duration . superclocks ( ) ;
beats = current_tempo . quarters_at_superclock ( sc ) ;
/* round meter to bars */
bbt = current_meter . bbt_at ( beats ) ;
beats = current_meter . quarters_at ( current_meter . round_to_bar ( bbt ) ) ;
/* recompute */
sc = current_tempo . superclock_at ( beats ) ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
m - > set ( sc , beats , bbt ) ;
+ + m ;
moved_meter = true ;
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
if ( b - > sclock ( ) < t - > sclock ( ) & & b - > sclock ( ) < m - > sclock ( ) ) {
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
sc = b - > sclock ( ) + duration . superclocks ( ) ;
beats = current_tempo . quarters_at_superclock ( sc ) ;
/* round bartime to beats */
beats = beats . round_to_beat ( ) ;
sc = current_tempo . superclock_at ( beats ) ;
bbt = current_meter . bbt_at ( beats ) ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
m - > set ( sc , beats , bbt ) ;
+ + m ;
moved_meter = true ;
2020-08-05 18:02:39 -04:00
}
}
2020-12-07 16:51:48 -05:00
break ;
case BeatTime :
break ;
2020-08-05 18:02:39 -04:00
}
}
bool
TempoMap : : remove_time ( timepos_t const & pos , timecnt_t const & duration )
{
2021-03-22 13:27:22 -04:00
superclock_t start ( pos . superclocks ( ) ) ;
superclock_t end ( ( pos + duration ) . superclocks ( ) ) ;
superclock_t shift ( duration . superclocks ( ) ) ;
TempoPoint * last_tempo = 0 ;
MeterPoint * last_meter = 0 ;
TempoPoint * tempo_after = 0 ;
MeterPoint * meter_after = 0 ;
bool moved = false ;
for ( Tempos : : iterator t = _tempos . begin ( ) ; t ! = _tempos . end ( ) ; ) {
if ( t - > sclock ( ) > = start & & t - > sclock ( ) < end ) {
last_tempo = & * t ;
t = _tempos . erase ( t ) ;
moved = true ;
} else if ( t - > sclock ( ) > = start ) {
t - > set ( t - > sclock ( ) - shift , t - > beats ( ) , t - > bbt ( ) ) ;
moved = true ;
if ( t - > sclock ( ) = = start ) {
tempo_after = & * t ;
}
+ + t ;
}
}
for ( Meters : : iterator m = _meters . begin ( ) ; m ! = _meters . end ( ) ; ) {
if ( m - > sclock ( ) > = start & & m - > sclock ( ) < end ) {
last_meter = & * m ;
m = _meters . erase ( m ) ;
moved = true ;
} else if ( m - > sclock ( ) > = start ) {
m - > set ( m - > sclock ( ) - shift , m - > beats ( ) , m - > bbt ( ) ) ;
moved = true ;
if ( m - > sclock ( ) = = start ) {
meter_after = & * m ;
}
+ + m ;
}
}
if ( last_tempo & & ! tempo_after ) {
last_tempo - > set ( start , last_tempo - > beats ( ) , last_tempo - > bbt ( ) ) ;
moved = true ;
}
if ( last_meter & & ! meter_after ) {
last_tempo - > set ( start , last_meter - > beats ( ) , last_meter - > bbt ( ) ) ;
moved = true ;
}
if ( moved ) {
reset_starting_at ( start ) ;
}
return moved ;
2020-08-05 18:02:39 -04:00
}
TempoPoint const *
TempoMap : : previous_tempo ( TempoPoint const & point ) const
{
Tempos : : const_iterator t = _tempos . begin ( ) ;
Tempos : : const_iterator prev = _tempos . end ( ) ;
while ( t ! = _tempos . end ( ) ) {
if ( t - > sclock ( ) = = point . sclock ( ) ) {
if ( prev ! = _tempos . end ( ) ) {
return & * prev ;
}
}
prev = t ;
+ + t ;
}
return 0 ;
}
TempoMetric
TempoMap : : metric_at ( timepos_t const & pos ) const
{
if ( pos . is_beats ( ) ) {
return metric_at ( pos . beats ( ) ) ;
}
return metric_at ( pos . superclocks ( ) ) ;
}
TempoMetric
2021-02-01 01:54:08 -05:00
TempoMap : : metric_at ( superclock_t sc , bool can_match ) const
2020-08-05 18:02:39 -04:00
{
2021-03-26 23:15:10 -04:00
TempoPoint const * tp = 0 ;
MeterPoint const * mp = 0 ;
2021-03-25 13:29:45 -04:00
2021-04-04 19:53:49 -04:00
( void ) get_tempo_and_meter ( tp , mp , sc , can_match , false ) ;
2021-03-25 13:29:45 -04:00
2021-03-26 23:15:10 -04:00
return TempoMetric ( * tp , * mp ) ;
2020-08-05 18:02:39 -04:00
}
TempoMetric
2021-02-01 01:54:08 -05:00
TempoMap : : metric_at ( Beats const & b , bool can_match ) const
2020-08-05 18:02:39 -04:00
{
2021-03-26 23:15:10 -04:00
TempoPoint const * tp = 0 ;
MeterPoint const * mp = 0 ;
2021-02-09 11:48:25 -05:00
2021-04-04 19:53:49 -04:00
( void ) get_tempo_and_meter ( tp , mp , b , can_match , false ) ;
2020-08-05 18:02:39 -04:00
2021-03-26 23:15:10 -04:00
return TempoMetric ( * tp , * mp ) ;
2020-08-05 18:02:39 -04:00
}
TempoMetric
2021-02-01 01:54:08 -05:00
TempoMap : : metric_at ( BBT_Time const & bbt , bool can_match ) const
2020-08-05 18:02:39 -04:00
{
2021-03-26 23:15:10 -04:00
TempoPoint const * tp = 0 ;
MeterPoint const * mp = 0 ;
2020-08-05 18:02:39 -04:00
2021-04-04 19:53:49 -04:00
( void ) get_tempo_and_meter ( tp , mp , bbt , can_match , false ) ;
2020-08-05 18:02:39 -04:00
2021-03-26 23:15:10 -04:00
return TempoMetric ( * tp , * mp ) ;
2020-08-05 18:02:39 -04:00
}
2021-03-22 13:27:47 -04:00
void
2020-08-05 18:02:39 -04:00
TempoMap : : set_ramped ( TempoPoint & tp , bool yn )
{
2021-04-04 19:53:49 -04:00
assert ( ! _tempos . empty ( ) ) ;
2020-08-05 18:02:39 -04:00
Rampable & r ( tp ) ;
2021-04-04 19:53:49 -04:00
if ( _tempos . size ( ) = = 1 | | tp = = _tempos . back ( ) ) {
/* nothing to do */
return ;
}
Tempos : : iterator nxt = _tempos . begin ( ) ;
+ + nxt ;
for ( Tempos : : iterator t = _tempos . begin ( ) ; nxt ! = _tempos . end ( ) ; + + t , + + nxt ) {
if ( tp = = * t ) {
break ;
}
}
r . set_end ( nxt - > end_super_note_type_per_second ( ) , nxt - > end_superclocks_per_note_type ( ) ) ;
2021-03-22 13:27:47 -04:00
r . set_ramped ( yn ) ;
2021-04-04 19:53:49 -04:00
2021-03-22 13:27:47 -04:00
reset_starting_at ( tp . sclock ( ) ) ;
2020-08-05 18:02:39 -04:00
}
#if 0
bool
TempoMap : : twist_tempi ( TempoSection * ts , const Tempo & bpm , const framepos_t frame , const framepos_t end_frame )
{
TempoSection * next_t = 0 ;
TempoSection * next_to_next_t = 0 ;
Metrics future_map ;
bool can_solve = false ;
/* minimum allowed measurement distance in frames */
framepos_t const min_dframe = 2 ;
2020-12-07 16:51:48 -05:00
if ( ! ts ) {
return false ;
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
TempoSection * tempo_copy = copy_metrics_and_point ( _metrics , future_map , ts ) ;
TempoSection * prev_to_prev_t = 0 ;
const frameoffset_t fr_off = end_frame - frame ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
if ( ! tempo_copy ) {
return false ;
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
if ( tempo_copy - > pulse ( ) > 0.0 ) {
prev_to_prev_t = const_cast < TempoSection * > ( & tempo_section_at_minute_locked ( future_map , minute_at_frame ( tempo_copy - > frame ( ) - 1 ) ) ) ;
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
for ( Metrics : : const_iterator i = future_map . begin ( ) ; i ! = future_map . end ( ) ; + + i ) {
if ( ( * i ) - > is_tempo ( ) & & ( * i ) - > minute ( ) > tempo_copy - > minute ( ) ) {
next_t = static_cast < TempoSection * > ( * i ) ;
break ;
2020-08-05 18:02:39 -04:00
}
2020-12-07 16:51:48 -05:00
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
if ( ! next_t ) {
return false ;
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
for ( Metrics : : const_iterator i = future_map . begin ( ) ; i ! = future_map . end ( ) ; + + i ) {
if ( ( * i ) - > is_tempo ( ) & & ( * i ) - > minute ( ) > next_t - > minute ( ) ) {
next_to_next_t = static_cast < TempoSection * > ( * i ) ;
break ;
2020-08-05 18:02:39 -04:00
}
2020-12-07 16:51:48 -05:00
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
if ( ! next_to_next_t ) {
return false ;
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
double prev_contribution = 0.0 ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
if ( next_t & & prev_to_prev_t & & prev_to_prev_t - > type ( ) = = TempoSection : : Ramp ) {
prev_contribution = ( tempo_copy - > frame ( ) - prev_to_prev_t - > frame ( ) ) / ( double ) ( next_t - > frame ( ) - prev_to_prev_t - > frame ( ) ) ;
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
const frameoffset_t tempo_copy_frame_contribution = fr_off - ( prev_contribution * ( double ) fr_off ) ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
framepos_t old_tc_minute = tempo_copy - > minute ( ) ;
double old_next_minute = next_t - > minute ( ) ;
double old_next_to_next_minute = next_to_next_t - > minute ( ) ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
double new_bpm ;
double new_next_bpm ;
double new_copy_end_bpm ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
if ( frame > tempo_copy - > frame ( ) + min_dframe & & ( frame + tempo_copy_frame_contribution ) > tempo_copy - > frame ( ) + min_dframe ) {
new_bpm = tempo_copy - > note_types_per_minute ( ) * ( ( frame - tempo_copy - > frame ( ) )
/ ( double ) ( end_frame - tempo_copy - > frame ( ) ) ) ;
} else {
new_bpm = tempo_copy - > note_types_per_minute ( ) ;
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
/* don't clamp and proceed here.
testing has revealed that this can go negative ,
which is an entirely different thing to just being too low .
*/
if ( new_bpm < 0.5 ) {
return false ;
}
new_bpm = min ( new_bpm , ( double ) 1000.0 ) ;
tempo_copy - > set_note_types_per_minute ( new_bpm ) ;
if ( tempo_copy - > type ( ) = = TempoSection : : Constant ) {
tempo_copy - > set_end_note_types_per_minute ( new_bpm ) ;
}
recompute_tempi ( future_map ) ;
if ( check_solved ( future_map ) ) {
if ( ! next_t ) {
2020-08-05 18:02:39 -04:00
return false ;
}
2020-12-07 16:51:48 -05:00
ts - > set_note_types_per_minute ( new_bpm ) ;
if ( ts - > type ( ) = = TempoSection : : Constant ) {
ts - > set_end_note_types_per_minute ( new_bpm ) ;
}
recompute_map ( _metrics ) ;
can_solve = true ;
}
if ( next_t - > type ( ) = = TempoSection : : Constant | | next_t - > c ( ) = = 0.0 ) {
if ( frame > tempo_copy - > frame ( ) + min_dframe & & end_frame > tempo_copy - > frame ( ) + min_dframe ) {
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
new_next_bpm = next_t - > note_types_per_minute ( ) * ( ( next_to_next_t - > minute ( ) - old_next_minute )
/ ( double ) ( ( old_next_to_next_minute ) - old_next_minute ) ) ;
} else {
new_next_bpm = next_t - > note_types_per_minute ( ) ;
2020-08-05 18:02:39 -04:00
}
2020-12-07 16:51:48 -05:00
next_t - > set_note_types_per_minute ( new_next_bpm ) ;
2020-08-05 18:02:39 -04:00
recompute_tempi ( future_map ) ;
if ( check_solved ( future_map ) ) {
2020-12-07 16:51:48 -05:00
for ( Metrics : : const_iterator i = _metrics . begin ( ) ; i ! = _metrics . end ( ) ; + + i ) {
if ( ( * i ) - > is_tempo ( ) & & ( * i ) - > minute ( ) > ts - > minute ( ) ) {
next_t = static_cast < TempoSection * > ( * i ) ;
break ;
}
}
2020-08-05 18:02:39 -04:00
if ( ! next_t ) {
return false ;
}
2020-12-07 16:51:48 -05:00
next_t - > set_note_types_per_minute ( new_next_bpm ) ;
2020-08-05 18:02:39 -04:00
recompute_map ( _metrics ) ;
can_solve = true ;
}
2020-12-07 16:51:48 -05:00
} else {
double next_frame_ratio = 1.0 ;
double copy_frame_ratio = 1.0 ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
if ( next_to_next_t ) {
next_frame_ratio = ( next_to_next_t - > minute ( ) - old_next_minute ) / ( old_next_to_next_minute - old_next_minute ) ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
copy_frame_ratio = ( ( old_tc_minute - next_t - > minute ( ) ) / ( double ) ( old_tc_minute - old_next_minute ) ) ;
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
new_next_bpm = next_t - > note_types_per_minute ( ) * next_frame_ratio ;
new_copy_end_bpm = tempo_copy - > end_note_types_per_minute ( ) * copy_frame_ratio ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
tempo_copy - > set_end_note_types_per_minute ( new_copy_end_bpm ) ;
if ( next_t - > clamped ( ) ) {
next_t - > set_note_types_per_minute ( new_copy_end_bpm ) ;
} else {
2020-08-05 18:02:39 -04:00
next_t - > set_note_types_per_minute ( new_next_bpm ) ;
2020-12-07 16:51:48 -05:00
}
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
recompute_tempi ( future_map ) ;
2020-08-05 18:02:39 -04:00
2020-12-07 16:51:48 -05:00
if ( check_solved ( future_map ) ) {
for ( Metrics : : const_iterator i = _metrics . begin ( ) ; i ! = _metrics . end ( ) ; + + i ) {
if ( ( * i ) - > is_tempo ( ) & & ( * i ) - > minute ( ) > ts - > minute ( ) ) {
next_t = static_cast < TempoSection * > ( * i ) ;
break ;
2020-08-05 18:02:39 -04:00
}
}
2020-12-07 16:51:48 -05:00
if ( ! next_t ) {
return false ;
2020-08-05 18:02:39 -04:00
}
if ( next_t - > clamped ( ) ) {
next_t - > set_note_types_per_minute ( new_copy_end_bpm ) ;
} else {
next_t - > set_note_types_per_minute ( new_next_bpm ) ;
}
2020-12-07 16:51:48 -05:00
ts - > set_end_note_types_per_minute ( new_copy_end_bpm ) ;
recompute_map ( _metrics ) ;
can_solve = true ;
2020-08-05 18:02:39 -04:00
}
}
Metrics : : const_iterator d = future_map . begin ( ) ;
while ( d ! = future_map . end ( ) ) {
delete ( * d ) ;
+ + d ;
}
MetricPositionChanged ( PropertyChange ( ) ) ; // Emit Signal
return can_solve ;
}
# endif
2020-11-27 18:24:44 -05:00
void
TempoMap : : MementoBinder : : set_state ( XMLNode const & node , int version ) const
{
2020-11-27 18:26:19 -05:00
/* fetch a writable copy of this thread's tempo map */
2020-11-27 18:24:44 -05:00
TempoMap : : SharedPtr map ( write_copy ( ) ) ;
2020-11-27 18:26:19 -05:00
/* change the state of the copy */
2020-11-27 18:24:44 -05:00
map - > set_state ( node , version ) ;
2021-02-10 11:30:43 -05:00
/* do the update step of RCU. This will also update this thread's map pointer */
2020-11-27 18:24:44 -05:00
update ( map ) ;
}
2020-11-28 00:13:41 -05:00
void
TempoMap : : init ( )
{
2021-01-24 17:25:43 -05:00
SharedPtr new_map ( new TempoMap ( Tempo ( 120 , 4 ) , Meter ( 4 , 4 ) ) ) ;
2020-11-28 00:13:41 -05:00
_map_mgr . init ( new_map ) ;
fetch ( ) ;
}
2020-12-31 16:04:25 -05:00
2021-03-22 18:03:45 -04:00
TempoMap : : SharedPtr
TempoMap : : write_copy ( )
{
return _map_mgr . write_copy ( ) ;
}
2021-02-22 01:37:45 -05:00
int
2020-12-31 16:04:25 -05:00
TempoMap : : update ( TempoMap : : SharedPtr m )
{
2021-02-22 01:37:45 -05:00
if ( ! _map_mgr . update ( m ) ) {
return - 1 ;
}
2020-12-31 16:04:25 -05:00
2021-04-04 19:53:49 -04:00
cerr < < " ************** new tempo map @ " < < m < < endl ;
2020-12-31 16:04:25 -05:00
/* update thread local map pointer in the calling thread */
2021-02-10 11:31:13 -05:00
update_thread_tempo_map ( ) ;
2020-12-31 16:04:25 -05:00
MapChanged ( ) ; /* EMIT SIGNAL */
2021-02-22 01:37:45 -05:00
return 0 ;
2020-12-31 16:04:25 -05:00
}
2021-01-01 20:28:26 -05:00
void
TempoMap : : abort_update ( )
{
/* drop lock taken by write_copy() */
_map_mgr . abort ( ) ;
/* update thread local map pointer in calling thread. Note that this
will reset _tempo_map_p , which is ( almost guaranteed to be ) the only
reference to the copy of the map made in : : write_copy ( ) , so it will
be destroyed here .
*/
TempoMap : : fetch ( ) ;
}
2021-03-17 15:03:56 -04:00
void
TempoMap : : midi_clock_beat_at_or_after ( samplepos_t const pos , samplepos_t & clk_pos , uint32_t & clk_beat )
{
/* Sequences are always assumed to start on a MIDI Beat of 0 (ie, the downbeat).
*
* There are 24 MIDI clock per quarter note ( 1 Temporal : : Beat )
*
* from http : //midi.teragonaudio.com/tech/midispec/seq.htm
*/
Temporal : : Beats b = ( quarters_at_sample ( pos ) ) . round_up_to_beat ( ) ;
clk_pos = sample_at ( b , TEMPORAL_SAMPLE_RATE ) ;
clk_beat = b . get_beats ( ) * 24 ;
assert ( clk_pos > = pos ) ;
}
2021-03-23 17:22:28 -04:00
/******** OLD STATE LOADING CODE SECTION *************/
static bool
bbt_time_to_string ( const BBT_Time & bbt , std : : string & str )
{
char buf [ 256 ] ;
int retval = snprintf ( buf , sizeof ( buf ) , " % " PRIu32 " |% " PRIu32 " |% " PRIu32 , bbt . bars , bbt . beats ,
bbt . ticks ) ;
if ( retval < = 0 | | retval > = ( int ) sizeof ( buf ) ) {
return false ;
}
str = buf ;
return true ;
}
static bool
string_to_bbt_time ( const std : : string & str , BBT_Time & bbt )
{
if ( sscanf ( str . c_str ( ) , " % " PRIu32 " |% " PRIu32 " |% " PRIu32 , & bbt . bars , & bbt . beats ,
& bbt . ticks ) = = 3 ) {
return true ;
}
return false ;
}
int
TempoMap : : parse_tempo_state_3x ( const XMLNode & node , LegacyTempoState & lts )
{
BBT_Time bbt ;
std : : string start_bbt ;
// _legacy_bbt.bars = 0; // legacy session check compars .bars != 0; default BBT_Time c'tor uses 1.
if ( node . get_property ( " start " , start_bbt ) ) {
if ( string_to_bbt_time ( start_bbt , bbt ) ) {
/* legacy session - start used to be in bbt*/
// _legacy_bbt = bbt;
// set_pulse(-1.0);
info < < _ ( " Legacy session detected. TempoSection XML node will be altered. " ) < < endmsg ;
}
}
/* position is the only data we extract from older XML */
2021-03-25 10:51:08 -04:00
if ( ! node . get_property ( " frame " , lts . sample ) ) {
2021-03-23 17:22:28 -04:00
error < < _ ( " Legacy tempo section XML does not have a \" frame \" node - map will be ignored " ) < < endmsg ;
2021-03-25 10:51:08 -04:00
cerr < < _ ( " Legacy tempo section XML does not have a \" frame \" node - map will be ignored " ) < < endl ;
2021-03-23 17:22:28 -04:00
return - 1 ;
}
if ( node . get_property ( " beats-per-minute " , lts . note_types_per_minute ) ) {
if ( lts . note_types_per_minute < 0.0 ) {
error < < _ ( " TempoSection XML node has an illegal \" beats_per_minute \" value " ) < < endmsg ;
return - 1 ;
}
}
2021-03-25 10:51:08 -04:00
if ( ! node . get_property ( " note-type " , lts . note_type ) ) {
2021-03-23 17:22:28 -04:00
if ( lts . note_type < 1.0 ) {
error < < _ ( " TempoSection XML node has an illegal \" note-type \" value " ) < < endmsg ;
2021-03-25 10:51:08 -04:00
cerr < < _ ( " TempoSection XML node has an illegal \" note-type \" value " ) < < endl ;
2021-03-23 17:22:28 -04:00
return - 1 ;
}
} else {
/* older session, make note type be quarter by default */
lts . note_type = 4.0 ;
}
if ( ! node . get_property ( " clamped " , lts . clamped ) ) {
lts . clamped = false ;
}
if ( node . get_property ( " end-beats-per-minute " , lts . end_note_types_per_minute ) ) {
if ( lts . end_note_types_per_minute < 0.0 ) {
info < < _ ( " TempoSection XML node has an illegal \" end-beats-per-minute \" value " ) < < endmsg ;
2021-03-25 10:51:08 -04:00
cerr < < _ ( " TempoSection XML node has an illegal \" end-beats-per-minute \" value " ) < < endl ;
2021-03-23 17:22:28 -04:00
return - 1 ;
}
}
Tempo : : Type old_type ;
if ( node . get_property ( " tempo-type " , old_type ) ) {
/* sessions with a tempo-type node contain no end-beats-per-minute.
if the legacy node indicates a constant tempo , simply fill this in with the
start tempo . otherwise we need the next neighbour to know what it will be .
*/
if ( old_type = = Tempo : : Constant ) {
lts . end_note_types_per_minute = lts . note_types_per_minute ;
} else {
lts . end_note_types_per_minute = - 1.0 ;
}
}
if ( ! node . get_property ( " active " , lts . active ) ) {
warning < < _ ( " TempoSection XML node has no \" active \" property " ) < < endmsg ;
lts . active = true ;
}
return 0 ;
}
int
TempoMap : : parse_meter_state_3x ( const XMLNode & node , LegacyMeterState & lms )
{
std : : string bbt_str ;
if ( node . get_property ( " start " , bbt_str ) ) {
if ( string_to_bbt_time ( bbt_str , lms . bbt ) ) {
/* legacy session - start used to be in bbt*/
info < < _ ( " Legacy session detected - MeterSection XML node will be altered. " ) < < endmsg ;
// set_pulse (-1.0);
} else {
error < < _ ( " MeterSection XML node has an illegal \" start \" value " ) < < endmsg ;
}
}
/* position is the only data we extract from older XML */
2021-03-25 10:51:08 -04:00
if ( ! node . get_property ( " frame " , lms . sample ) ) {
2021-03-23 17:22:28 -04:00
error < < _ ( " Legacy tempo section XML does not have a \" frame \" node - map will be ignored " ) < < endmsg ;
return - 1 ;
}
2021-03-25 10:51:08 -04:00
if ( ! node . get_property ( " beat " , lms . beat ) ) {
2021-03-23 17:22:28 -04:00
lms . beat = 0.0 ;
}
if ( node . get_property ( " bbt " , bbt_str ) ) {
if ( ! string_to_bbt_time ( bbt_str , lms . bbt ) ) {
error < < _ ( " MeterSection XML node has an illegal \" bbt \" value " ) < < endmsg ;
return - 1 ;
}
} else {
warning < < _ ( " MeterSection XML node has no \" bbt \" property " ) < < endmsg ;
}
/* beats-per-bar is old; divisions-per-bar is new */
if ( ! node . get_property ( " divisions-per-bar " , lms . divisions_per_bar ) ) {
if ( ! node . get_property ( " beats-per-bar " , lms . divisions_per_bar ) ) {
error < < _ ( " MeterSection XML node has no \" beats-per-bar \" or \" divisions-per-bar \" property " ) < < endmsg ;
return - 1 ;
}
}
if ( lms . divisions_per_bar < 0.0 ) {
error < < _ ( " MeterSection XML node has an illegal \" divisions-per-bar \" value " ) < < endmsg ;
return - 1 ;
}
if ( ! node . get_property ( " note-type " , lms . note_type ) ) {
error < < _ ( " MeterSection XML node has no \" note-type \" property " ) < < endmsg ;
return - 1 ;
}
if ( lms . note_type < 0.0 ) {
error < < _ ( " MeterSection XML node has an illegal \" note-type \" value " ) < < endmsg ;
return - 1 ;
}
return 0 ;
}
int
TempoMap : : set_state_3x ( const XMLNode & node )
{
XMLNodeList nlist ;
XMLNodeConstIterator niter ;
_tempos . clear ( ) ;
_meters . clear ( ) ;
_points . clear ( ) ;
nlist = node . children ( ) ;
2021-03-25 10:51:08 -04:00
/* Need initial tempo & meter points, because subsequent ones will use
* set_tempo ( ) and set_meter ( ) which require pre - existing data
*/
bool have_initial_tempo = false ;
bool have_initial_meter = false ;
2021-03-23 17:22:28 -04:00
for ( niter = nlist . begin ( ) ; niter ! = nlist . end ( ) ; + + niter ) {
XMLNode * child = * niter ;
2021-03-25 10:51:08 -04:00
if ( ! have_initial_tempo & & ( child - > name ( ) = = Tempo : : xml_node_name ) ) {
2021-03-23 17:22:28 -04:00
LegacyTempoState lts ;
if ( parse_tempo_state_3x ( * child , lts ) ) {
error < < _ ( " Tempo map: could not set new state, restoring old one. " ) < < endmsg ;
break ;
}
Tempo t ( lts . note_types_per_minute ,
lts . end_note_types_per_minute ,
lts . note_type ) ;
2021-03-25 10:51:08 -04:00
TempoPoint * tp = new TempoPoint ( * this , t , samples_to_superclock ( 0 , TEMPORAL_SAMPLE_RATE ) , Beats ( ) , BBT_Time ( ) ) ;
_tempos . push_back ( * tp ) ;
_points . push_back ( * tp ) ;
have_initial_tempo = true ;
2021-03-23 17:22:28 -04:00
2021-03-25 10:51:08 -04:00
}
if ( ! have_initial_meter & & ( child - > name ( ) = = Meter : : xml_node_name ) ) {
LegacyMeterState lms ;
if ( parse_meter_state_3x ( * child , lms ) ) {
error < < _ ( " Tempo map: could not use old meter state, restoring old one. " ) < < endmsg ;
break ;
}
Meter m ( lms . divisions_per_bar , lms . note_type ) ;
MeterPoint * mp = new MeterPoint ( * this , m , 0 , Beats ( ) , BBT_Time ( ) ) ;
_meters . push_back ( * mp ) ;
_points . push_back ( * mp ) ;
have_initial_meter = true ;
}
if ( have_initial_meter & & have_initial_tempo ) {
break ;
}
}
2021-03-25 12:22:41 -04:00
if ( ! have_initial_meter | | ! have_initial_tempo ) {
2021-03-25 10:51:08 -04:00
error < < _ ( " Old tempo map information is missing either tempo or meter information - ignored " ) < < endmsg ;
return - 1 ;
}
for ( niter = nlist . begin ( ) ; niter ! = nlist . end ( ) ; + + niter ) {
XMLNode * child = * niter ;
if ( child - > name ( ) = = Tempo : : xml_node_name ) {
LegacyTempoState lts ;
if ( parse_tempo_state_3x ( * child , lts ) ) {
error < < _ ( " Tempo map: could not set new state, restoring old one. " ) < < endmsg ;
break ;
}
Tempo t ( lts . note_types_per_minute ,
lts . end_note_types_per_minute ,
lts . note_type ) ;
2021-03-23 17:22:28 -04:00
set_tempo ( t , timepos_t ( lts . sample ) ) ;
} else if ( child - > name ( ) = = Meter : : xml_node_name ) {
LegacyMeterState lms ;
if ( parse_meter_state_3x ( * child , lms ) ) {
2021-03-25 10:51:08 -04:00
error < < _ ( " Tempo map: could not use old meter state, restoring old one. " ) < < endmsg ;
2021-03-23 17:22:28 -04:00
break ;
}
Meter m ( lms . divisions_per_bar , lms . note_type ) ;
set_meter ( m , timepos_t ( lms . sample ) ) ;
}
}
#if 0
/* check for legacy sessions where bbt was the base musical unit for tempo */
for ( Metrics : : const_iterator i = _metrics . begin ( ) ; i ! = _metrics . end ( ) ; + + i ) {
TempoSection * t ;
if ( ( t = dynamic_cast < TempoSection * > ( * i ) ) ! = 0 ) {
if ( t - > legacy_bbt ( ) . bars ! = 0 ) {
fix_legacy_session ( ) ;
break ;
}
if ( t - > end_note_types_per_minute ( ) < 0.0 ) {
fix_legacy_end_session ( ) ;
break ;
}
}
}
if ( niter = = nlist . end ( ) ) {
MetricSectionSorter cmp ;
_metrics . sort ( cmp ) ;
}
# endif
#if 0
/* check for multiple tempo/meters at the same location, which
ardour2 somehow allowed .
*/
{
Tempos : : iterator prev = _tempos . end ( ) ;
for ( Tempos : : iterator i = _tempos . begin ( ) ; i ! = _tempos . end ( ) ; + + i ) {
if ( prev ! = _tempos . end ( ) ) {
MeterSection * ms ;
MeterSection * prev_m ;
TempoSection * ts ;
TempoSection * prev_t ;
if ( ( prev_m = dynamic_cast < MeterSection * > ( * prev ) ) ! = 0 & & ( ms = dynamic_cast < MeterSection * > ( * i ) ) ! = 0 ) {
if ( prev_m - > beat ( ) = = ms - > beat ( ) ) {
cerr < < string_compose ( _ ( " Multiple meter definitions found at %1 " ) , prev_m - > beat ( ) ) < < endmsg ;
error < < string_compose ( _ ( " Multiple meter definitions found at %1 " ) , prev_m - > beat ( ) ) < < endmsg ;
return - 1 ;
}
} else if ( ( prev_t = dynamic_cast < TempoSection * > ( * prev ) ) ! = 0 & & ( ts = dynamic_cast < TempoSection * > ( * i ) ) ! = 0 ) {
if ( prev_t - > pulse ( ) = = ts - > pulse ( ) ) {
cerr < < string_compose ( _ ( " Multiple tempo definitions found at %1 " ) , prev_t - > pulse ( ) ) < < endmsg ;
error < < string_compose ( _ ( " Multiple tempo definitions found at %1 " ) , prev_t - > pulse ( ) ) < < endmsg ;
return - 1 ;
}
}
}
prev = i ;
}
# endif
return 0 ;
}
#if 0
void
TempoMap : : fix_legacy_session ( )
{
MeterSection * prev_m = 0 ;
TempoSection * prev_t = 0 ;
bool have_initial_t = false ;
for ( Metrics : : iterator i = _metrics . begin ( ) ; i ! = _metrics . end ( ) ; + + i ) {
MeterSection * m ;
TempoSection * t ;
if ( ( m = dynamic_cast < MeterSection * > ( * i ) ) ! = 0 ) {
if ( m - > initial ( ) ) {
pair < double , BBT_Time > bbt = make_pair ( 0.0 , BBT_Time ( 1 , 1 , 0 ) ) ;
m - > set_beat ( bbt ) ;
m - > set_pulse ( 0.0 ) ;
m - > set_minute ( 0.0 ) ;
m - > set_position_lock_style ( AudioTime ) ;
prev_m = m ;
continue ;
}
if ( prev_m ) {
pair < double , BBT_Time > start = make_pair ( ( ( m - > bbt ( ) . bars - 1 ) * prev_m - > note_divisor ( ) )
+ ( m - > bbt ( ) . beats - 1 )
+ ( m - > bbt ( ) . ticks / BBT_Time : : ticks_per_beat )
, m - > bbt ( ) ) ;
m - > set_beat ( start ) ;
const double start_beat = ( ( m - > bbt ( ) . bars - 1 ) * prev_m - > note_divisor ( ) )
+ ( m - > bbt ( ) . beats - 1 )
+ ( m - > bbt ( ) . ticks / BBT_Time : : ticks_per_beat ) ;
m - > set_pulse ( start_beat / prev_m - > note_divisor ( ) ) ;
}
prev_m = m ;
} else if ( ( t = dynamic_cast < TempoSection * > ( * i ) ) ! = 0 ) {
if ( ! t - > active ( ) ) {
continue ;
}
/* Ramp type never existed in the era of this tempo section */
t - > set_end_note_types_per_minute ( t - > note_types_per_minute ( ) ) ;
if ( t - > initial ( ) ) {
t - > set_pulse ( 0.0 ) ;
t - > set_minute ( 0.0 ) ;
t - > set_position_lock_style ( AudioTime ) ;
prev_t = t ;
have_initial_t = true ;
continue ;
}
if ( prev_t ) {
/* some 4.x sessions have no initial (non-movable) tempo. */
if ( ! have_initial_t ) {
prev_t - > set_pulse ( 0.0 ) ;
prev_t - > set_minute ( 0.0 ) ;
prev_t - > set_position_lock_style ( AudioTime ) ;
prev_t - > set_initial ( true ) ;
prev_t - > set_locked_to_meter ( true ) ;
have_initial_t = true ;
}
const double beat = ( ( t - > legacy_bbt ( ) . bars - 1 ) * ( ( prev_m ) ? prev_m - > note_divisor ( ) : 4.0 ) )
+ ( t - > legacy_bbt ( ) . beats - 1 )
+ ( t - > legacy_bbt ( ) . ticks / BBT_Time : : ticks_per_beat ) ;
if ( prev_m ) {
t - > set_pulse ( beat / prev_m - > note_divisor ( ) ) ;
} else {
/* really shouldn't happen but.. */
t - > set_pulse ( beat / 4.0 ) ;
}
}
prev_t = t ;
}
}
}
void
TempoMap : : fix_legacy_end_session ( )
{
TempoSection * prev_t = 0 ;
for ( Metrics : : iterator i = _metrics . begin ( ) ; i ! = _metrics . end ( ) ; + + i ) {
TempoSection * t ;
if ( ( t = dynamic_cast < TempoSection * > ( * i ) ) ! = 0 ) {
if ( ! t - > active ( ) ) {
continue ;
}
if ( prev_t ) {
if ( prev_t - > end_note_types_per_minute ( ) < 0.0 ) {
prev_t - > set_end_note_types_per_minute ( t - > note_types_per_minute ( ) ) ;
}
}
prev_t = t ;
}
}
if ( prev_t ) {
prev_t - > set_end_note_types_per_minute ( prev_t - > note_types_per_minute ( ) ) ;
}
}
# endif