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/compose.h"
2022-04-08 13:16:39 -04:00
# include "pbd/convert.h"
2020-08-05 18:02:39 -04:00
# include "pbd/enumwriter.h"
2022-04-08 13:16:39 -04:00
# include "pbd/error.h"
2020-08-05 18:02:39 -04:00
# include "pbd/failed_constructor.h"
# include "pbd/stacktrace.h"
2022-04-08 13:16:39 -04:00
# include "pbd/string_convert.h"
2020-08-05 18:02:39 -04:00
# include "temporal/debug.h"
# include "temporal/tempo.h"
2022-03-30 12:33:49 -04:00
# include "temporal/types_convert.h"
2020-08-05 18:02:39 -04:00
2021-09-27 09:49:41 -04:00
# include "pbd/i18n.h"
2020-08-05 18:02:39 -04:00
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 ) ;
2022-04-08 11:23:19 -04:00
thread_local TempoMap : : WritableSharedPtr 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 )
2022-06-17 00:11:56 -04:00
: MapOwned ( map )
2020-08-05 18:02:39 -04:00
{
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
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 ) ;
2022-05-13 21:04:35 -04:00
2020-12-20 13:39:12 -05:00
_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 ( ) ;
}
2022-05-13 20:18:18 -04:00
2020-08-05 18:02:39 -04:00
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 ;
}
2022-05-23 10:06:36 -04:00
/* older versions used "clamped" as the property name here */
if ( ! node . get_property ( X_ ( " continuing " ) , _continuing ) & & ! node . get_property ( X_ ( " clamped " ) , _continuing ) ) {
_continuing = false ;
2021-01-01 17:45:15 -05:00
}
2020-08-05 18:02:39 -04:00
}
2022-05-23 13:24:31 -04:00
void
Tempo : : set_note_types_per_minute ( double npm )
{
_npm = npm ;
_superclocks_per_note_type = double_npm_to_scpn ( _npm ) ;
_super_note_type_per_second = double_npm_to_snps ( _npm ) ;
}
2021-03-22 13:27:47 -04:00
void
2022-05-13 20:55:25 -04:00
Tempo : : set_end_npm ( double npm )
2020-08-05 18:02:39 -04:00
{
2022-05-23 13:24:31 -04:00
_enpm = npm ;
_end_super_note_type_per_second = double_npm_to_snps ( _enpm ) ;
_end_superclocks_per_note_type = double_npm_to_scpn ( _enpm ) ;
2021-04-04 19:53:49 -04:00
}
2021-03-22 13:27:47 -04:00
void
2022-05-23 10:06:36 -04:00
Tempo : : set_continuing ( bool yn )
2020-08-05 18:02:39 -04:00
{
2022-05-23 10:06:36 -04:00
_continuing = 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 ) ;
2022-05-23 10:06:36 -04:00
node - > set_property ( X_ ( " continuing " ) , _continuing ) ;
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_ ( " 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
2022-05-23 10:06:36 -04:00
/* older versions used "clamped" as the property name here */
if ( ! node . get_property ( X_ ( " continuing " ) , _continuing ) & & ! node . get_property ( X_ ( " continuing " ) , _continuing ) ) {
_continuing = false ;
2021-01-01 17:45:15 -05:00
}
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 ) {
2022-03-04 13:40:30 -05:00
r . beats + = floor ( ( double ) r . ticks / tpg ) ;
2020-08-05 18:02:39 -04:00
r . ticks = tpg + ( r . ticks % Temporal : : Beats : : PPQN ) ;
}
2022-03-04 13:40:30 -05:00
if ( r . beats < = 0 ) {
r . bars + = floor ( ( r . beats - 1.0 ) / _divisions_per_bar ) ;
r . beats = _divisions_per_bar + ( r . beats % _divisions_per_bar ) ;
2020-08-05 18:02:39 -04:00
}
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_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 ;
}
2022-11-29 11:34:28 -05:00
Temporal : : BBT_Time
Meter : : round_to_beat ( Temporal : : BBT_Time const & bbt ) const
{
Temporal : : BBT_Time b = bbt . round_to_beat ( ) ;
if ( b . beats > _divisions_per_bar ) {
b . bars + + ;
b . beats = 1 ;
}
return b ;
}
2020-08-05 18:02:39 -04:00
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 ) {
2022-05-20 11:06:18 -04:00
if ( node . get_property ( X_ ( " omega " ) , _omega ) ) {
/* XXX ?? */
2022-05-13 20:58:17 -04:00
}
2020-08-05 18:02:39 -04:00
}
return ret ;
}
XMLNode &
TempoPoint : : get_state ( ) const
{
XMLNode & base ( Tempo : : get_state ( ) ) ;
Point : : add_state ( base ) ;
2022-05-20 11:06:18 -04:00
base . set_property ( X_ ( " omega " ) , _omega ) ;
2020-08-05 18:02:39 -04:00
return base ;
}
TempoPoint : : TempoPoint ( TempoMap const & map , XMLNode const & node )
2021-02-07 00:09:16 -05:00
: Point ( map , node )
, Tempo ( node )
2022-05-20 11:06:18 -04:00
, _omega ( 0 )
2020-08-05 18:02:39 -04:00
{
2022-05-20 11:06:18 -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
2022-05-20 11:06:18 -04:00
TempoPoint : : compute_omega_from_next_tempo ( TempoPoint const & next )
2022-11-29 11:36:43 -05:00
{
compute_omega_from_distance_and_next_tempo ( next . beats ( ) - beats ( ) , next ) ;
}
void
TempoPoint : : compute_omega_from_distance_and_next_tempo ( Beats const & quarter_duration , TempoPoint const & next )
2020-08-05 18:02:39 -04:00
{
2021-04-04 19:53:49 -04:00
superclock_t end_scpqn ;
2022-05-23 10:06:36 -04:00
if ( ! _continuing ) {
2022-05-13 20:58:17 -04:00
/* tempo is defined by our own start and end */
2021-04-04 19:53:49 -04:00
end_scpqn = end_superclocks_per_quarter_note ( ) ;
} else {
2022-05-13 20:58:17 -04:00
/* tempo is defined by our own start the start of the next tempo */
2021-04-04 19:53:49 -04:00
end_scpqn = next . superclocks_per_quarter_note ( ) ;
}
2022-05-13 20:18:18 -04:00
if ( superclocks_per_quarter_note ( ) = = end_scpqn ) {
2022-05-20 11:06:18 -04:00
_omega = 0.0 ;
2020-08-05 18:02:39 -04:00
return ;
}
2022-11-29 11:36:43 -05:00
compute_omega_from_quarter_duration ( quarter_duration , end_scpqn ) ;
2022-05-02 18:44:15 -04:00
}
2020-08-05 18:02:39 -04:00
2022-05-02 18:44:15 -04:00
void
TempoPoint : : compute_omega_from_quarter_duration ( Beats const & quarter_duration , superclock_t end_scpqn )
{
2022-05-20 11:06:18 -04:00
_omega = ( ( 1.0 / end_scpqn ) - ( 1.0 / superclocks_per_quarter_note ( ) ) ) / DoubleableBeats ( quarter_duration ) . to_double ( ) ;
2022-11-29 11:36:43 -05:00
DEBUG_TRACE ( DEBUG : : TemporalMap , string_compose ( " quarter-computed omega from qtr duration = %1 dur was %2 start speed %3 end speed [%4] \n " , _omega , quarter_duration . str ( ) , superclocks_per_quarter_note ( ) , end_scpqn ) ) ;
2022-05-02 18:44:15 -04:00
}
2022-11-29 11:36:43 -05:00
2022-05-02 18:44:15 -04:00
void
TempoPoint : : compute_omega_from_audio_duration ( samplecnt_t audio_duration , superclock_t end_scpqn )
{
2022-05-20 11:06:18 -04:00
_omega = ( 1.0 / ( samples_to_superclock ( audio_duration , TEMPORAL_SAMPLE_RATE ) ) ) * log ( ( double ) superclocks_per_note_type ( ) / end_scpqn ) ;
2022-11-29 11:36:43 -05:00
DEBUG_TRACE ( DEBUG : : TemporalMap , string_compose ( " computed omega from audio duration= %1%2 dur was %3 \n " , std : : setprecision ( 12 ) , _omega , audio_duration ) ) ;
2020-08-05 18:02:39 -04:00
}
superclock_t
TempoPoint : : superclock_at ( Temporal : : Beats const & qn ) const
{
if ( qn = = _quarters ) {
return _sclock ;
}
2021-10-29 14:55:14 -04:00
if ( qn < Beats ( ) ) {
/* negative */
assert ( _quarters = = Beats ( ) ) ;
} else {
/* positive */
assert ( qn > = _quarters ) ;
}
2020-08-05 18:02:39 -04:00
if ( ! actually_ramped ( ) ) {
/* not ramped, use linear */
2021-03-22 18:05:20 -04:00
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
}
2022-05-20 11:06:18 -04:00
superclock_t r ;
2022-05-20 13:20:35 -04:00
const double log_expr = superclocks_per_quarter_note ( ) * _omega * DoubleableBeats ( qn - _quarters ) . to_double ( ) ;
2022-05-20 11:06:18 -04:00
2022-05-20 13:20:35 -04:00
if ( log_expr < - 1 ) {
r = _sclock + llrint ( log ( - log_expr - 1.0 ) / - _omega ) ;
2022-05-24 19:08:59 -04:00
if ( r < 0 ) {
std : : cerr < < " CASE 1: " < < * this < < endl < < " scpqn = " < < superclocks_per_quarter_note ( ) < < std : : endl ;
std : : cerr < < " for " < < qn < < " @ " < < _quarters < < " | " < < _sclock < < " + log ( " < < log_expr < < " ) "
< < log ( - log_expr - 1.0 )
< < " - omega = " < < - _omega
< < " => "
< < r < < std : : endl ;
abort ( ) ;
}
2022-05-20 11:06:18 -04:00
} else {
2022-05-20 13:20:35 -04:00
r = _sclock + llrint ( log1p ( log_expr ) / _omega ) ;
2022-05-20 11:06:18 -04:00
2022-05-24 19:08:59 -04:00
if ( r < 0 ) {
std : : cerr < < " CASE 2: scpqn = " < < superclocks_per_quarter_note ( ) < < std : : endl ;
std : : cerr < < " for " < < qn < < " @ " < < _quarters < < " | " < < _sclock < < " + log1p ( " < < superclocks_per_quarter_note ( ) * _omega * DoubleableBeats ( qn - _quarters ) . to_double ( ) < < " = "
< < log1p ( superclocks_per_quarter_note ( ) * _omega * DoubleableBeats ( qn - _quarters ) . to_double ( ) )
< < " => "
< < r < < std : : endl ;
abort ( ) ;
}
2022-05-20 11:06:18 -04:00
}
2022-05-24 19:08:59 -04:00
2022-05-20 11:06:18 -04:00
return r ;
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 ;
}
2022-05-20 11:06:18 -04:00
return _superclocks_per_note_type * exp ( - _omega * ( pos . superclocks ( ) - sclock ( ) ) ) ;
2020-08-05 18:02:39 -04:00
}
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 */
2022-03-17 16:14:41 -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
2022-03-17 16:14:41 -04: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 ( ) ) ;
2020-12-29 21:27:34 -05:00
/* 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-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
2022-06-21 13:39:03 -04:00
const Beats ret = _quarters + Beats ( b , t ) ;
/* positive superclock can never generate negative beats unless
* it is too large . If that happens , handle it the same way as
* the opening special case in this method .
*/
if ( sc > = 0 & & ret < Beats ( ) ) {
return std : : numeric_limits < Beats > : : max ( ) ;
}
return ret ;
2020-08-05 18:02:39 -04:00
}
2022-05-20 11:06:18 -04:00
const double b = ( exp ( _omega * ( sc - _sclock ) ) - 1 ) / ( superclocks_per_quarter_note ( ) * _omega ) ;
2020-08-05 18:02:39 -04:00
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 ( ) ;
2022-10-30 21:34:52 -04:00
/* Use the later of the tempo or meter as the reference point to
* compute the BBT distance . All map points are fully defined by all 3
* time types , but we need the latest one to avoid incorrect
* computations of quarter duration .
*/
const Point * reference_point ;
if ( _tempo - > beats ( ) < _meter - > beats ( ) ) {
reference_point = _meter ;
} else {
reference_point = _tempo ;
}
const Beats dq = _tempo - > quarters_at_superclock ( sc ) - reference_point - > 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 "
*/
2022-06-21 14:26:09 -04:00
const int64_t note_value_count = int_div_round ( dq . get_beats ( ) * _meter - > note_value ( ) , int64_t ( 4 ) ) ;
2021-01-04 23:56:06 -05:00
/* now construct a BBT_Offset using the count in grid units */
const BBT_Offset bbt_offset ( 0 , note_value_count , dq . get_ticks ( ) ) ;
2022-10-30 21:34:52 -04:00
DEBUG_TRACE ( DEBUG : : TemporalMap , string_compose ( " BBT offset from %3 @ %1: %2 \n " , ( _tempo - > beats ( ) < _meter - > beats ( ) ? _meter - > bbt ( ) : _tempo - > bbt ( ) ) , bbt_offset ,
( _tempo - > beats ( ) < _meter - > beats ( ) ? " meter " : " tempo " ) ) ) ;
return _meter - > bbt_add ( reference_point - > bbt ( ) , bbt_offset ) ;
2020-08-05 18:02:39 -04:00
}
superclock_t
TempoMetric : : superclock_at ( BBT_Time const & bbt ) const
{
2022-11-29 11:39:01 -05:00
DEBUG_TRACE ( DEBUG : : TemporalMap , string_compose ( " get quarters for %1 = %2 using %3 \n " , bbt , _meter - > quarters_at ( bbt ) , * this ) ) ;
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
{
2022-06-03 15:19:56 -04:00
node . get_property ( X_ ( " name " ) , _name ) ; /* may fail, leaves name empty */
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 ( ) ) ;
2022-06-03 15:19:56 -04:00
node - > set_property ( X_ ( " name " ) , _name ) ; /* failure is OK */
2020-08-05 18:02:39 -04:00
return * node ;
}
2022-06-03 15:19:56 -04:00
void
MusicTimePoint : : set_name ( std : : string const & str )
{
_name = str ;
/* XXX need a signal or something to announce change */
}
2020-08-05 18:02:39 -04:00
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-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-12-30 23:06:19 -05:00
copy_points ( other ) ;
}
TempoMap &
TempoMap : : operator = ( TempoMap const & other )
{
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 ) {
2022-06-03 12:50:46 -04:00
if ( dynamic_cast < MusicTimePoint const * > ( & * m ) ) {
continue ;
}
2020-12-30 23:06:19 -05:00
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 ) {
2022-06-03 12:50:46 -04:00
if ( dynamic_cast < MusicTimePoint const * > ( & * t ) ) {
continue ;
}
2020-12-30 23:06:19 -05:00
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 ) ;
2022-06-03 12:50:46 -04:00
_tempos . push_back ( * mtp ) ;
_meters . push_back ( * mtp ) ;
2021-02-07 12:48:15 -05:00
p . push_back ( mtp ) ;
}
sort ( p . begin ( ) , p . end ( ) , Point : : ptr_sclock_comparator ( ) ) ;
2022-06-17 00:11:56 -04:00
for ( auto & pi : p ) {
pi - > set_map ( * this ) ;
_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
MeterPoint *
2021-01-02 12:56:24 -05:00
TempoMap : : add_meter ( MeterPoint * mp )
2020-08-05 18:02:39 -04:00
{
2022-05-23 18:17:37 -04:00
bool replaced ;
MeterPoint * ret = core_add_meter ( mp , replaced ) ;
2020-08-05 18:02:39 -04:00
if ( ! replaced ) {
2022-05-23 18:17:37 -04:00
core_add_point ( mp ) ;
2022-05-29 17:01:54 -04:00
} else {
delete mp ;
2020-08-05 18:02:39 -04:00
}
2022-05-24 19:10:14 -04:00
reset_starting_at ( ret - > sclock ( ) ) ;
2020-08-05 18:02:39 -04:00
return ret ;
}
void
TempoMap : : change_tempo ( TempoPoint & p , Tempo const & t )
{
* ( ( Tempo * ) & p ) = t ;
2022-05-10 11:44:01 -04:00
reset_starting_at ( p . sclock ( ) ) ;
2020-08-05 18:02:39 -04:00
}
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
}
2022-05-21 18:32:19 -04:00
# ifndef NDEBUG
if ( DEBUG_ENABLED ( DEBUG : : TemporalMap ) ) {
dump ( cerr ) ;
}
# endif
2021-04-04 19:53:49 -04:00
2020-08-05 18:02:39 -04:00
return * ret ;
}
2022-05-23 18:17:37 -04:00
void
TempoMap : : core_add_point ( Point * pp )
{
Points : : iterator p ;
const Beats beats_limit = pp - > beats ( ) ;
for ( p = _points . begin ( ) ; p ! = _points . end ( ) & & p - > beats ( ) < beats_limit ; + + p ) ;
_points . insert ( p , * pp ) ;
}
2020-08-05 18:02:39 -04:00
TempoPoint *
2022-05-23 18:17:37 -04:00
TempoMap : : core_add_tempo ( TempoPoint * tp , bool & replaced )
2020-08-05 18:02:39 -04:00
{
Tempos : : iterator t ;
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
2022-05-23 16:24:34 -04:00
for ( t = _tempos . begin ( ) ; t ! = _tempos . end ( ) & & t - > beats ( ) < beats_limit ; + + t ) ;
2020-08-05 18:02:39 -04:00
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 ;
2022-05-29 17:01:54 -04:00
/* caller must delete tp when replaced is true */
2022-05-24 19:10:14 -04:00
DEBUG_TRACE ( DEBUG : : TemporalMap , string_compose ( " overwrote old tempo with %1 \n " , * tp ) ) ;
2020-08-05 18:02:39 -04:00
replaced = true ;
2022-05-23 18:17:37 -04:00
return & ( * t ) ;
2020-08-05 18:02:39 -04:00
}
}
2022-05-24 19:10:14 -04:00
DEBUG_TRACE ( DEBUG : : TemporalMap , string_compose ( " inserted tempo %1 \n " , * tp ) ) ;
2020-08-05 18:02:39 -04:00
2022-05-23 18:17:37 -04:00
replaced = false ;
return & ( * _tempos . insert ( t , * tp ) ) ;
}
2020-08-05 18:02:39 -04:00
2022-05-23 18:17:37 -04:00
MeterPoint *
TempoMap : : core_add_meter ( MeterPoint * mp , bool & replaced )
{
Meters : : iterator m ;
const superclock_t sclock_limit = mp - > sclock ( ) ;
const Beats beats_limit = mp - > beats ( ) ;
2020-08-05 18:02:39 -04:00
2022-05-23 18:17:37 -04:00
for ( m = _meters . begin ( ) ; m ! = _meters . end ( ) & & m - > beats ( ) < beats_limit ; + + m ) ;
2020-08-05 18:02:39 -04:00
2022-05-23 18:17:37 -04:00
if ( m ! = _meters . end ( ) ) {
if ( m - > sclock ( ) = = sclock_limit ) {
/* overwrite Meter part of this point */
* ( ( Meter * ) & ( * m ) ) = * mp ;
2022-05-29 17:01:54 -04:00
/* caller must delete mp when replaced is true */
2022-05-23 18:17:37 -04:00
replaced = true ;
return & ( * m ) ;
}
}
replaced = false ;
return & ( * ( _meters . insert ( m , * mp ) ) ) ;
}
2020-08-05 18:02:39 -04:00
2022-05-24 19:10:14 -04:00
MusicTimePoint *
TempoMap : : core_add_bartime ( MusicTimePoint * mtp , bool & replaced )
{
MusicTimes : : iterator m ;
const superclock_t sclock_limit = mtp - > sclock ( ) ;
for ( m = _bartimes . begin ( ) ; m ! = _bartimes . end ( ) & & m - > sclock ( ) < sclock_limit ; + + m ) ;
if ( m ! = _bartimes . end ( ) ) {
if ( m - > sclock ( ) = = sclock_limit ) {
/* overwrite Tempo part of this point */
* m = * mtp ;
2022-05-29 17:01:54 -04:00
/* caller must delete mtp when replaced is true */
2022-05-24 19:10:14 -04:00
DEBUG_TRACE ( DEBUG : : TemporalMap , string_compose ( " overwrote old bartime with %1 \n " , mtp ) ) ;
replaced = true ;
return & ( * m ) ;
}
}
DEBUG_TRACE ( DEBUG : : TemporalMap , string_compose ( " inserted bartime %1 \n " , mtp ) ) ;
replaced = false ;
return & ( * _bartimes . insert ( m , * mtp ) ) ;
}
2022-05-23 18:17:37 -04:00
TempoPoint *
TempoMap : : add_tempo ( TempoPoint * tp )
{
bool replaced ;
TempoPoint * ret = core_add_tempo ( tp , replaced ) ;
if ( ! replaced ) {
core_add_point ( tp ) ;
2022-05-29 17:01:54 -04:00
} else {
delete tp ;
2022-05-23 18:17:37 -04:00
}
2022-05-29 17:01:54 -04:00
2022-05-24 19:10:14 -04:00
reset_starting_at ( ret - > sclock ( ) ) ;
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 ;
2021-11-13 11:17:26 -05:00
2021-11-15 23:27:41 -05:00
assert ( _tempos . size ( ) > 1 ) ;
2021-11-13 11:17:26 -05:00
/* the argument is likely to be a Point-derived object that doesn't
* actually exist in this TempoMap , since the caller called
* TempoMap : : write_copy ( ) in order to perform an RCU operation , but
* will be removing an element known from the original map .
*
* However , since we do not allow points of the same type ( Tempo ,
* Meter , BarTime ) at the same time , we can effectively search here
* using what is effectively a duple of ( type , time ) for the
* comparison .
*
* Once / if found , we will have a pointer to the actual Point - derived
* object in this TempoMap , and we can then remove that from the
* _points list .
*/
2020-12-07 16:51:48 -05:00
for ( t = _tempos . begin ( ) ; t ! = _tempos . end ( ) & & t - > sclock ( ) < tp . sclock ( ) ; + + t ) ;
2021-11-13 11:17:26 -05:00
2021-11-15 23:27:41 -05:00
if ( t = = _tempos . end ( ) ) {
/* not found */
return ;
}
2020-12-07 16:51:48 -05:00
if ( t - > sclock ( ) ! = tp . sclock ( ) ) {
/* error ... no tempo point at the time of tp */
return ;
2020-08-05 18:02:39 -04:00
}
2021-11-13 11:17:26 -05:00
2021-11-15 23:27:41 -05:00
Tempos : : iterator nxt = _tempos . begin ( ) ;
Tempos : : iterator prev = _tempos . end ( ) ;
if ( t ! = _tempos . end ( ) ) {
nxt = t ;
+ + nxt ;
}
if ( t ! = _tempos . begin ( ) ) {
prev = t ;
- - prev ;
}
const bool was_end = ( nxt = = _tempos . end ( ) ) ;
2020-12-07 16:51:48 -05:00
_tempos . erase ( t ) ;
2021-11-13 09:51:12 -05:00
remove_point ( * t ) ;
2021-11-13 11:17:26 -05:00
2021-11-15 23:27:41 -05:00
if ( prev ! = _tempos . end ( ) & & was_end ) {
2022-05-13 20:58:52 -04:00
prev - > set_end_npm ( prev - > note_types_per_minute ( ) ) ; /* remove any ramp */
2021-11-15 23:27:41 -05:00
} else {
reset_starting_at ( sc ) ;
}
2020-08-05 18:02:39 -04:00
}
2022-06-03 15:19:56 -04:00
void
TempoMap : : set_bartime ( BBT_Time const & bbt , timepos_t const & pos , std : : string name )
2020-08-05 18:02:39 -04:00
{
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 ) ) ;
2022-06-03 15:19:56 -04:00
MusicTimePoint * tp = new MusicTimePoint ( * this , sc , metric . quarters_at_superclock ( sc ) , bbt , metric . tempo ( ) , metric . meter ( ) , name ) ;
2020-08-05 18:02:39 -04:00
2022-06-03 15:19:56 -04:00
add_or_replace_bartime ( tp ) ;
2020-08-05 18:02:39 -04:00
}
MusicTimePoint *
2022-05-24 19:10:14 -04:00
TempoMap : : add_or_replace_bartime ( MusicTimePoint * mtp )
2020-08-05 18:02:39 -04:00
{
2022-05-24 19:10:14 -04:00
bool replaced ;
MusicTimePoint * ret = core_add_bartime ( mtp , replaced ) ;
2020-08-05 18:02:39 -04:00
if ( ! replaced ) {
2022-05-24 19:10:14 -04:00
bool ignore ;
( void ) core_add_tempo ( mtp , ignore ) ;
( void ) core_add_meter ( mtp , ignore ) ;
core_add_point ( mtp ) ;
2022-05-29 17:01:54 -04:00
} else {
delete mtp ;
2020-08-05 18:02:39 -04:00
}
2022-05-24 19:10:14 -04:00
reset_starting_at ( ret - > sclock ( ) ) ;
2020-08-05 18:02:39 -04:00
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 ;
2021-11-13 11:17:26 -05:00
/* the argument is likely to be a Point-derived object that doesn't
* actually exist in this TempoMap , since the caller called
* TempoMap : : write_copy ( ) in order to perform an RCU operation , but
* will be removing an element known from the original map .
*
* However , since we do not allow points of the same type ( Tempo ,
* Meter , BarTime ) at the same time , we can effectively search here
* using what is effectively a duple of ( type , time ) for the
* comparison .
*
* Once / if found , we will have a pointer to the actual Point - derived
* object in this TempoMap , and we can then remove that from the
* _points list .
*/
2020-12-07 16:51:48 -05:00
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-11-13 09:51:12 -05:00
remove_point ( * m ) ;
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 ) ;
2021-11-13 11:17:26 -05:00
/* note that the point passed here must be an element of the _points
* list , which is not true for the point passed to the callees
* ( remove_tempo ( ) , remove_meter ( ) , remove_bartime ( ) .
*
* in those methods , we effectively search for a match on a duple of
* ( type , time ) , but here we are comparing pointer addresses .
*/
2021-02-07 13:23:37 -05:00
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 )
{
2022-05-13 20:11:50 -04:00
DEBUG_TRACE ( DEBUG : : MapReset , string_compose ( " reset starting at %1 \n " , sc ) ) ;
# ifndef NDEBUG
if ( DEBUG_ENABLED ( DEBUG : : MapReset ) ) {
dump ( std : : cerr ) ;
}
# endif
2020-08-05 18:02:39 -04:00
assert ( ! _tempos . empty ( ) ) ;
assert ( ! _meters . empty ( ) ) ;
2021-04-04 19:53:49 -04:00
TempoPoint * tp ;
2022-03-23 19:35:41 -04:00
TempoPoint * nxt_tempo = 0 ;
2021-04-04 19:53:49 -04:00
MeterPoint * mp ;
MusicTimePoint * mtp ;
2022-03-23 19:35:41 -04:00
TempoMetric metric ( _tempos . front ( ) , _meters . front ( ) ) ;
Points : : iterator p ;
2022-05-21 18:32:54 -04:00
bool need_initial_ramp_reset = false ;
2020-08-05 18:02:39 -04:00
2022-05-13 20:11:50 -04:00
DEBUG_TRACE ( DEBUG : : MapReset , string_compose ( " we begin at %1 with metric %2 \n " , sc , metric ) ) ;
2022-11-29 11:39:40 -05:00
/* We will reset the superclock (audio) and beat time based on the BBT
* time of each point . To make sure this works correctly , sort them by
* BBT time first . If we do not do this , then we will use points as
* TempoMetrics in a ( potentially ) incorrect order .
*/
Point : : bbt_comparator cmp ;
_points . sort ( cmp ) ;
# ifndef NDEBUG
if ( DEBUG_ENABLED ( DEBUG : : MapReset ) ) {
std : : cerr < < " \n POST SORT: \n " ;
dump ( std : : cerr ) ;
}
# endif
2022-05-13 20:59:34 -04:00
/* Setup the metric that is in effect at the starting point */
2020-08-05 18:02:39 -04:00
2022-03-23 19:35:41 -04:00
for ( p = _points . begin ( ) ; p ! = _points . end ( ) ; + + p ) {
2020-08-05 18:02:39 -04:00
2022-05-24 19:10:14 -04:00
DEBUG_TRACE ( DEBUG : : MapReset , string_compose ( " Now looking at %1 => %2 \n " , & ( * p ) , * p ) ) ;
2020-08-05 18:02:39 -04:00
2022-05-24 19:10:14 -04:00
if ( p - > sclock ( ) > sc ) {
2022-05-02 18:44:15 -04:00
break ;
}
mtp = 0 ;
tp = 0 ;
mp = 0 ;
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
2022-03-23 19:35:41 -04:00
* time ) points since everything about their position is set
2021-04-04 19:53:49 -04:00
* 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
2022-03-23 19:35:41 -04:00
metric = TempoMetric ( * mtp , * mtp ) ;
2022-05-24 19:10:14 -04:00
DEBUG_TRACE ( DEBUG : : MapReset , string_compose ( " Bartime!, used tempo @ %1 \n " , ( TempoPoint * ) mtp ) ) ;
2022-08-17 18:27:28 -04:00
need_initial_ramp_reset = false ;
2021-04-04 19:53:49 -04:00
} else if ( ( tp = dynamic_cast < TempoPoint * > ( & * p ) ) ! = 0 ) {
2022-03-23 19:35:41 -04:00
metric = TempoMetric ( * tp , metric . meter ( ) ) ;
2022-05-21 18:32:54 -04:00
if ( tp - > ramped ( ) ) {
need_initial_ramp_reset = true ;
} else {
need_initial_ramp_reset = true ;
}
2022-05-24 19:10:14 -04:00
DEBUG_TRACE ( DEBUG : : MapReset , string_compose ( " Tempo! @ %1, metric's tempo is %2 \n " , tp , & metric . tempo ( ) ) ) ;
2021-04-04 19:53:49 -04:00
} else if ( ( mp = dynamic_cast < MeterPoint * > ( & * p ) ) ! = 0 ) {
2022-03-23 19:35:41 -04:00
metric = TempoMetric ( metric . tempo ( ) , * mp ) ;
2022-05-13 20:11:50 -04:00
DEBUG_TRACE ( DEBUG : : MapReset , " Meter! \n " ) ;
2022-03-23 19:35:41 -04:00
}
2022-05-02 18:44:15 -04:00
}
2022-03-23 19:35:41 -04:00
2022-05-21 19:28:38 -04:00
/* if the tempo point the defines our starting metric for position
* @ param sc is ramped , recompute its omega value based on the beat
* time of the following tempo point . If we do not do this before we
* start , then : : superclock_at ( ) for subsequent points will be
* incorrect .
*/
if ( need_initial_ramp_reset ) {
const TempoPoint * nxt = next_tempo ( metric . tempo ( ) ) ;
if ( nxt ) {
const_cast < TempoPoint * > ( & metric . tempo ( ) ) - > compute_omega_from_next_tempo ( * nxt ) ;
}
need_initial_ramp_reset = false ;
}
2022-05-13 20:59:34 -04:00
/* Now iterate over remaining points and recompute their audio time
2022-11-21 20:34:13 -05:00
* and beat time positions .
2022-05-13 20:59:34 -04:00
*/
2022-05-02 18:44:15 -04:00
for ( ; p ! = _points . end ( ) ; + + p ) {
mtp = 0 ;
tp = 0 ;
mp = 0 ;
2022-05-13 20:59:51 -04:00
if ( ( mtp = dynamic_cast < MusicTimePoint * > ( & * p ) ) = = 0 ) {
if ( ( tp = dynamic_cast < TempoPoint * > ( & * p ) ) = = 0 ) {
2022-05-02 18:44:15 -04:00
mp = dynamic_cast < MeterPoint * > ( & * p ) ;
}
2021-04-04 19:53:49 -04:00
}
2020-08-05 18:02:39 -04:00
2022-05-13 20:11:50 -04:00
DEBUG_TRACE ( DEBUG : : MapReset , string_compose ( " workong on it! tp = %1 \n " , tp ) ) ;
2022-05-02 18:44:15 -04:00
if ( tp ) {
2022-03-23 19:35:41 -04:00
Points : : iterator pp = p ;
nxt_tempo = 0 ;
2022-03-23 20:03:42 -04:00
+ + pp ;
2022-03-23 19:35:41 -04:00
while ( pp ! = _points . end ( ) ) {
TempoPoint * nt = dynamic_cast < TempoPoint * > ( & * pp ) ;
if ( nt ) {
nxt_tempo = nt ;
break ;
}
2022-03-23 20:03:42 -04:00
+ + pp ;
2022-03-23 19:35:41 -04:00
}
2022-05-13 21:00:11 -04:00
DEBUG_TRACE ( DEBUG : : MapReset , string_compose ( " considering omega comp for %1 with nxt = %2 \n " , * tp , nxt_tempo ) ) ;
2022-03-23 19:35:41 -04:00
if ( tp - > ramped ( ) & & nxt_tempo ) {
2022-05-20 11:06:18 -04:00
tp - > compute_omega_from_next_tempo ( * nxt_tempo ) ;
2021-04-04 19:53:49 -04:00
}
}
2020-08-05 18:02:39 -04:00
2022-05-02 18:44:15 -04:00
if ( ! mtp ) {
2022-05-13 20:11:50 -04:00
DEBUG_TRACE ( DEBUG : : MapReset , string_compose ( " recompute %1 using %2 \n " , p - > bbt ( ) , metric ) ) ;
2021-04-04 19:53:49 -04:00
superclock_t sc = metric . superclock_at ( p - > bbt ( ) ) ;
2022-11-21 17:33:53 -05:00
DEBUG_TRACE ( DEBUG : : MapReset , string_compose ( " \t based on %1 move to %2,%3 \n " , p - > bbt ( ) , sc , metric . meter ( ) . quarters_at ( p - > bbt ( ) ) ) ) ;
p - > set ( sc , metric . meter ( ) . quarters_at ( p - > bbt ( ) ) , p - > bbt ( ) ) ;
2022-05-02 18:44:15 -04:00
} else {
2022-05-13 20:11:50 -04:00
DEBUG_TRACE ( DEBUG : : MapReset , " \t not recomputing this one \n " ) ;
2022-05-02 18:44:15 -04:00
}
/* Now ensure that metric is correct moving forward */
if ( ( mtp = dynamic_cast < MusicTimePoint * > ( & * p ) ) ! = 0 ) {
metric = TempoMetric ( * mtp , * mtp ) ;
} else if ( ( tp = dynamic_cast < TempoPoint * > ( & * p ) ) ! = 0 ) {
metric = TempoMetric ( * tp , metric . meter ( ) ) ;
} else if ( ( mp = dynamic_cast < MeterPoint * > ( & * p ) ) ! = 0 ) {
metric = TempoMetric ( metric . tempo ( ) , * mp ) ;
2021-04-04 19:53:49 -04:00
}
2020-08-05 18:02:39 -04:00
}
2022-05-13 20:11:50 -04:00
DEBUG_TRACE ( DEBUG : : MapReset , " RESET DONE \n " ) ;
# ifndef NDEBUG
if ( DEBUG_ENABLED ( DEBUG : : MapReset ) ) {
dump ( std : : cerr ) ;
}
# endif
2020-08-05 18:02:39 -04:00
}
bool
2022-05-30 23:14:33 -04:00
TempoMap : : move_meter ( MeterPoint const & mp , timepos_t const & when , bool earlier , bool push )
2020-08-05 18:02:39 -04:00
{
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 ;
bool round_up ;
2020-08-05 18:02:39 -04:00
2022-05-23 16:24:34 -04:00
beats = when . beats ( ) ;
2022-05-30 23:14:33 -04:00
if ( earlier ) {
2022-05-23 16:24:34 -04:00
round_up = false ;
2022-05-30 23:14:33 -04:00
} else {
round_up = true ;
2020-12-07 16:51:48 -05:00
}
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 ;
2022-05-23 16:24:34 -04:00
/* 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 ; }
2022-05-30 23:14:33 -04:00
if ( prev_m = = _meters . end ( ) ) {
return false ;
}
if ( prev_t = = _tempos . end ( ) ) {
prev_t = _tempos . begin ( ) ;
}
2022-05-23 16:24:34 -04:00
TempoMetric metric ( * prev_t , * prev_m ) ;
bbt = metric . bbt_at ( beats ) ;
2022-05-30 23:14:33 -04:00
2022-05-23 16:24:34 -04:00
if ( round_up ) {
2022-08-03 12:59:29 -04:00
bbt = bbt . round_up_to_bar ( ) ;
2022-05-23 16:24:34 -04:00
} else {
2022-08-03 12:59:29 -04:00
bbt = bbt . round_down_to_bar ( ) ;
2022-05-23 16:24:34 -04:00
}
2022-05-30 23:14:33 -04:00
2022-05-23 16:24:34 -04:00
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 ; }
2022-05-30 23:14:33 -04:00
if ( prev_m = = _meters . end ( ) ) {
return false ;
}
if ( prev_t = = _tempos . end ( ) ) {
prev_t = _tempos . begin ( ) ;
}
2022-05-23 16:24:34 -04:00
metric = TempoMetric ( * prev_t , * prev_m ) ;
beats = metric . quarters_at ( bbt ) ;
2022-05-30 23:14:33 -04:00
2022-05-23 16:24:34 -04:00
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
}
2020-08-05 18:02:39 -04:00
2022-05-23 16:24:34 -04:00
sc = metric . superclock_at ( bbt ) ;
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
2022-06-03 12:51:57 -04:00
/* reset position of this meter */
const_cast < MeterPoint * > ( & mp ) - > set ( sc , beats , bbt ) ;
2020-08-05 18:02:39 -04:00
2022-06-03 12:51:57 -04:00
{
Meters : : iterator current = _meters . end ( ) ;
Meters : : iterator insert_before = _meters . end ( ) ;
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
}
2022-06-03 12:51:57 -04:00
/* existing meter must have been found */
assert ( current ! = _meters . end ( ) ) ;
/* reposition in list */
_meters . splice ( insert_before , _meters , current ) ;
2020-12-07 16:51:48 -05:00
}
2020-08-05 18:02:39 -04:00
2022-06-03 12:51:57 -04:00
{
Points : : iterator current = _points . end ( ) ;
Points : : iterator insert_before = _points . end ( ) ;
for ( Points : : iterator m = _points . begin ( ) ; m ! = _points . end ( ) ; + + m ) {
if ( * m = = mp ) {
current = m ;
}
if ( insert_before = = _points . end ( ) & & ( m - > sclock ( ) > sc ) ) {
insert_before = m ;
}
}
/* existing meter must have been found */
assert ( current ! = _points . end ( ) ) ;
/* reposition in list */
_points . splice ( insert_before , _points , 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 ;
}
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 ;
2020-08-05 18:02:39 -04:00
2022-11-29 11:40:34 -05:00
/* tempo changes must be on beat */
2020-08-05 18:02:39 -04:00
2022-11-29 11:40:34 -05:00
beats = when . beats ( ) ;
2022-10-25 12:59:22 -04:00
2022-11-29 11:40:34 -05:00
/* XXX need to assert that meter note value is >= 4 */
MeterPoint const & mm ( meter_at ( beats ) ) ;
beats . round_to_subdivision ( mm . note_value ( ) / 4 , RoundNearest ) ;
2022-10-25 12:59:22 -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
2022-11-29 11:40:34 -05:00
/* find tempo & meter in effect at the new target location */
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 ; }
2022-10-25 14:52:30 -04:00
if ( prev_t = = _tempos . end ( ) ) {
/* moved earlier than first, no movement */
return false ;
}
2022-11-29 11:40:34 -05:00
2022-10-25 14:52:30 -04:00
if ( prev_m = = _meters . end ( ) ) {
/* moved earlier than first, no movement */
return false ;
}
2022-11-29 11:40:34 -05:00
/* If the previous tempo is ramped, we need to recompute its omega
* constant to cover the ( new ) duration of the ramp .
*/
if ( prev_t - > actually_ramped ( ) ) {
prev_t - > compute_omega_from_distance_and_next_tempo ( beats - prev_t - > beats ( ) , tp ) ;
}
2022-05-23 16:24:34 -04:00
TempoMetric metric ( * prev_t , * prev_m ) ;
2022-11-29 11:40:34 -05:00
const Beats delta ( ( beats - tp . beats ( ) ) . abs ( ) ) ;
if ( delta < Beats : : ticks ( metric . meter ( ) . ticks_per_grid ( ) ) ) {
return false ;
}
2022-05-23 16:24:34 -04:00
sc = metric . superclock_at ( beats ) ;
bbt = metric . bbt_at ( beats ) ;
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 ( ) ;
2022-06-03 12:51:57 -04:00
/* reset position of this tempo */
const_cast < TempoPoint * > ( & tp ) - > set ( sc , beats , bbt ) ;
2020-08-05 18:02:39 -04:00
2022-06-03 12:51:57 -04:00
/* move to correct position in tempo list */
2020-08-05 18:02:39 -04:00
2022-06-03 12:51:57 -04:00
{
Tempos : : iterator current = _tempos . end ( ) ;
Tempos : : iterator insert_before = _tempos . end ( ) ;
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-12-07 16:51:48 -05:00
}
2022-06-03 12:51:57 -04:00
/* existing tempo must have been found */
assert ( current ! = _tempos . end ( ) ) ;
/* reposition in list */
_tempos . splice ( insert_before , _tempos , current ) ;
2020-12-07 16:51:48 -05:00
}
2020-08-05 18:02:39 -04:00
2022-06-03 12:51:57 -04:00
/* move to correct position in points list */
2020-08-05 18:02:39 -04:00
2022-06-03 12:51:57 -04:00
{
Points : : iterator current = _points . end ( ) ;
Points : : iterator insert_before = _points . end ( ) ;
for ( Points : : iterator t = _points . begin ( ) ; t ! = _points . end ( ) ; + + t ) {
if ( * t = = tp ) {
current = t ;
}
if ( insert_before = = _points . end ( ) & & ( t - > sclock ( ) > sc ) ) {
insert_before = t ;
}
}
/* existing tempo must have been found */
assert ( current ! = _points . end ( ) ) ;
/* reposition in list */
_points . splice ( insert_before , _points , 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 ( ) ;
2022-03-21 13:35:26 -04:00
Meters : : iterator m ;
2022-03-23 19:35:41 -04:00
2021-11-13 11:17:26 -05:00
/* the argument is likely to be a Point-derived object that doesn't
* actually exist in this TempoMap , since the caller called
* TempoMap : : write_copy ( ) in order to perform an RCU operation , but
* will be removing an element known from the original map .
*
* However , since we do not allow points of the same type ( Tempo ,
* Meter , BarTime ) at the same time , we can effectively search here
* using what is effectively a duple of ( type , time ) for the
* comparison .
*
* Once / if found , we will have a pointer to the actual Point - derived
* object in this TempoMap , and we can then remove that from the
* _points list .
*/
2022-03-21 13:35:26 -04:00
for ( m = _meters . begin ( ) ; m ! = _meters . end ( ) & & m - > sclock ( ) < mp . sclock ( ) ; + + m ) ;
if ( m = = _meters . end ( ) ) {
/* not found */
return ;
}
2021-11-13 11:17:26 -05:00
2020-12-07 16:51:48 -05:00
if ( m - > sclock ( ) ! = mp . sclock ( ) ) {
/* error ... no meter point at the time of mp */
return ;
2020-08-05 18:02:39 -04:00
}
2021-11-13 11:17:26 -05:00
2020-12-07 16:51:48 -05:00
_meters . erase ( m ) ;
2021-11-13 09:51:12 -05:00
remove_point ( * m ) ;
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
}
2022-03-19 17:21:51 -04:00
Temporal : : Beats
TempoMap : : bbtwalk_to_quarters ( BBT_Time const & pos , BBT_Offset const & distance ) const
{
return quarters_at ( bbt_walk ( pos , distance ) ) - quarters_at ( 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
{
2022-05-24 19:07:56 -04:00
ostr < < " \n \n TEMPO MAP @ " < < this < < " : \n " < < std : : dec ;
ostr < < " ... tempos... \n " ;
2020-08-05 18:02:39 -04:00
for ( Tempos : : const_iterator t = _tempos . begin ( ) ; t ! = _tempos . end ( ) ; + + t ) {
ostr < < & * t < < ' ' < < * t < < endl ;
}
2022-05-24 19:07:56 -04:00
ostr < < " ... meters... \n " ;
2020-08-05 18:02:39 -04:00
for ( Meters : : const_iterator m = _meters . begin ( ) ; m ! = _meters . end ( ) ; + + m ) {
ostr < < & * m < < ' ' < < * m < < endl ;
}
2020-12-20 13:39:12 -05:00
2022-05-24 19:07:56 -04:00
ostr < < " ... bartimes... \n " ;
2020-12-20 13:39:12 -05:00
for ( MusicTimes : : const_iterator m = _bartimes . begin ( ) ; m ! = _bartimes . end ( ) ; + + m ) {
ostr < < & * m < < ' ' < < * m < < endl ;
}
2021-11-13 09:55:52 -05:00
ostr < < " ... all points ... \n " ;
for ( Points : : const_iterator p = _points . begin ( ) ; p ! = _points . end ( ) ; + + p ) {
2022-05-24 19:07:56 -04:00
ostr < < & * p < < ' ' < < * p ;
if ( dynamic_cast < MusicTimePoint const * > ( & ( * p ) ) ) {
ostr < < " BarTime " ;
2022-06-03 12:50:17 -04:00
}
if ( dynamic_cast < TempoPoint const * > ( & ( * p ) ) ) {
2022-05-24 19:07:56 -04:00
ostr < < " Tempo " ;
2022-06-03 12:50:17 -04:00
}
if ( dynamic_cast < MeterPoint const * > ( & ( * p ) ) ) {
2022-05-24 19:07:56 -04:00
ostr < < " Meter " ;
}
ostr < < endl ;
2021-11-13 09:55:52 -05:00
}
2020-12-20 13:39:12 -05:00
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
2022-03-23 19:36:07 -04:00
/* Set return tempo and meter points by value using the starting tempo
* and meter passed in .
*
* Then advance through all points , resetting either tempo and / or meter
2022-10-30 20:44:28 -04:00
* until we find a point beyond ( or equal to , if @ p can_match is
* true ) the @ p arg ( end time )
2022-03-23 19:36:07 -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 ) {
2022-01-24 18:34:15 -05:00
while ( ( p ! = endi ) & & ( ( * p ) . * method ) ( ) < = arg ) + + p ;
2021-04-04 19:53:49 -04:00
} else {
2022-01-24 18:34:15 -05:00
while ( ( p ! = endi ) & & ( ( * p ) . * method ) ( ) < arg ) + + p ;
2021-04-04 19:53:49 -04:00
}
return p ;
}
return last_used ;
}
2020-08-05 18:02:39 -04:00
void
2022-11-29 11:41:17 -05:00
TempoMap : : get_grid ( TempoMapPoints & ret , superclock_t start , superclock_t end , uint32_t bar_mod , uint32_t beat_div ) const
2020-08-05 18:02:39 -04:00
{
2022-10-30 20:44:28 -04:00
/* note: @p bar_mod is "bar modulo", and describes the N in "give
2021-11-17 12:48:06 -05:00
me every Nth bar " . If the caller wants every 4th bar, bar_mod ==
4. If we want every point defined by the tempo note type ( e . g . every
quarter not , then bar_mod is zero .
*/
2020-08-05 18:02:39 -04:00
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
2022-05-21 18:32:19 -04:00
# ifndef NDEBUG
if ( DEBUG_ENABLED ( DEBUG : : Grid ) ) {
dump ( std : : cout ) ;
}
# endif
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 ;
2022-11-29 11:41:17 -05:00
Points : : const_iterator p = _points . begin ( ) ;
BBT_Time bbt ;
Beats beats ;
2020-08-05 18:02:39 -04:00
2022-11-29 11:41:17 -05:00
/* Find relevant meter for nominal start point */
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 ) ;
2021-02-09 01:02:26 -05:00
/* 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
*/
2022-11-29 11:41:17 -05:00
TempoMetric metric = TempoMetric ( * tp , * mp ) ;
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " metric in effect at %1 = %2 \n " , start , metric ) ) ;
2021-02-09 01:02:26 -05:00
/* determine the BBT at start */
2022-11-29 11:41:17 -05:00
bbt = metric . bbt_at ( timepos_t : : from_superclock ( start ) ) ;
/* 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 @ p 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 .
*
* 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
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
2022-11-29 11:41:17 -05:00
/* adjust to match bar_mod (e.g. we only want every 4th bar)
2021-02-09 01:02:26 -05:00
*/
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
2022-11-29 11:41:17 -05:00
/* rebuild metric */
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
2022-11-29 11:41:17 -05: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:
*
2022-11-29 11:41:17 -05:00
* - metric is a TempoMetric that describes the situation at the adjusted start time
2021-02-09 01:02:26 -05:00
* - 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
*/
2022-12-05 11:39:26 -05:00
2022-11-29 11:41:17 -05:00
while ( p ! = _points . end ( ) & & start < end ) {
2020-12-06 01:05:17 -05:00
2022-12-05 11:39:26 -05:00
bool next_point_is_bbt_marker = ( dynamic_cast < MusicTimePoint const * > ( & * p ) ) ;
2022-11-29 11:41:17 -05:00
/* Generate grid points (either actual meter-defined
* beats , or bars based on bar_mod ) up until the next point
* in the map
*/
if ( bar_mod ! = 0 ) {
if ( bbt . is_bar ( ) & & ( bar_mod = = 1 | | ( ( bbt . bars % bar_mod = = 1 ) ) ) ) {
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 ) ) ;
}
2020-08-05 18:02:39 -04:00
2022-11-29 11:41:17 -05:00
/* Advance by the number of bars specified by bar_mod */
2020-08-05 18:02:39 -04:00
2022-11-29 11:41:17 -05:00
bbt . bars + = bar_mod ;
2020-08-05 18:02:39 -04:00
2022-11-29 11:41:17 -05:00
} else {
2021-11-17 10:58:49 -05:00
2022-11-29 11:41:17 -05:00
ret . push_back ( TempoMapPoint ( * this , metric , start , beats , bbt ) ) ;
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " G %1 \t [%2] \n " , metric , ret . back ( ) ) ) ;
2020-08-05 18:02:39 -04:00
2022-11-29 11:41:17 -05:00
/* Advance beats by 1 meter-defined "beat */
2021-01-31 20:48:23 -05:00
2022-11-29 11:41:17 -05:00
if ( beat_div = = 1 ) {
bbt = metric . bbt_add ( bbt , BBT_Offset ( 0 , 1 , 0 ) ) ;
2021-02-09 01:02:26 -05:00
} else {
2022-11-29 11:41:17 -05:00
bbt = metric . bbt_add ( bbt , BBT_Offset ( 0 , 0 , Temporal : : ticks_per_beat / beat_div ) ) ;
2020-08-05 18:02:39 -04:00
}
2022-11-29 11:41:17 -05:00
}
2020-08-05 18:02:39 -04:00
2022-12-05 11:39:26 -05:00
start = metric . superclock_at ( bbt ) ;
2020-08-05 18:02:39 -04:00
2022-12-05 11:39:26 -05:00
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " check overrun of next point with bbt @ %1 audio %2 point %3 \n " , bbt , start , * p ) ) ;
2020-08-05 18:02:39 -04:00
2022-12-05 11:39:26 -05:00
if ( ( ! next_point_is_bbt_marker & & bbt > = p - > bbt ( ) ) | | ( start > = p - > sclock ( ) ) ) {
2022-05-20 11:06:18 -04:00
2022-12-05 11:39:26 -05:00
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " we've reached/passed the next point, BBT %1 audio %2 point %3 \n " , bbt , start , * p ) ) ;
2020-08-05 18:02:39 -04:00
2022-12-05 11:39:26 -05:00
/* reset our sense of "now" to be wherever the point is */
2022-05-27 20:46:06 -04:00
start = p - > sclock ( ) ;
bbt = p - > bbt ( ) ;
beats = p - > beats ( ) ;
2022-11-29 11:41:17 -05:00
/* If we just arrived at a point (indicated by bbt ==
* p - > bbt ( ) ) , use all points at the same location to
* potentially reconstruct the metric . Note that a BBT point is
* both a tempo and a meter point , which is why we do test each
* point found at this location as both .
2021-02-09 01:02:26 -05:00
*/
2020-08-05 18:02:39 -04:00
2022-11-29 11:41:17 -05:00
bool rebuild_metric = false ;
2020-08-05 18:02:39 -04:00
2022-11-29 11:41:17 -05:00
while ( p - > bbt ( ) = = bbt ) {
2021-01-21 19:31:43 -05:00
2022-11-29 11:41:17 -05:00
TempoPoint const * tpp ;
MeterPoint const * mpp ;
2020-08-05 18:02:39 -04:00
2022-11-29 11:41:17 -05:00
if ( ( tpp = dynamic_cast < TempoPoint const * > ( & ( * p ) ) ) ! = 0 ) {
rebuild_metric = true ;
2021-02-09 01:02:26 -05:00
tp = tpp ;
2020-08-05 18:02:39 -04:00
}
2022-11-29 11:41:17 -05:00
if ( ( mpp = dynamic_cast < MeterPoint const * > ( & ( * p ) ) ) ! = 0 ) {
rebuild_metric = true ;
2021-02-09 01:02:26 -05:00
mp = mpp ;
}
2020-08-05 18:02:39 -04:00
2022-11-29 11:41:17 -05:00
+ + p ;
2021-02-09 01:02:26 -05:00
}
2020-08-05 18:02:39 -04:00
2022-11-29 11:41:17 -05:00
/* reset the metric to use the most recent tempo & meter */
if ( rebuild_metric ) {
metric = TempoMetric ( * tp , * mp ) ;
2022-12-05 11:39:26 -05:00
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " second| with start = %1 aka %2 rebuilt metric from points, now %3 \n " , start , bbt , metric ) ) ;
2022-11-29 11:41:17 -05:00
}
2020-08-05 18:02:39 -04:00
2021-02-09 01:02:26 -05:00
}
2020-08-05 18:02:39 -04:00
2022-11-29 11:41:17 -05:00
/* Update the quarter-note time value to match the BBT and
* audio time positions
2021-11-17 12:50:35 -05:00
*/
2022-11-29 11:41:17 -05:00
beats = metric . quarters_at ( bbt ) ;
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " bar mod %1 moved to %2 qn %3 sc %4) \n " , bar_mod , bbt , beats , start ) ) ;
2021-02-09 01:02:26 -05:00
}
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
/* 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
2022-11-29 11:41:17 -05:00
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " reached end, no more map points, use %5 to finish between %1 .. %2 initial bbt %3, beats %4 \n " , start , end , bbt , beats . str ( ) , metric ) ) ;
2021-01-31 20:39:29 -05:00
2022-11-29 11:41:17 -05:00
while ( start < end ) {
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " bar mod %1 moved to %2 qn %3 sc %4) \n " , bar_mod , bbt , beats , start ) ) ;
2021-02-11 00:30:05 -05:00
2022-11-22 20:09:07 -05:00
/* It is possible we already added the current BBT
* point , so check to avoid doubling up
*/
2022-11-29 11:41:17 -05:00
if ( bar_mod ! = 0 ) {
if ( bbt . is_bar ( ) & & ( bar_mod = = 1 | | ( ( bbt . bars % bar_mod = = 0 ) ) ) ) {
2021-02-11 00:30:05 -05:00
ret . push_back ( TempoMapPoint ( * this , metric , start , beats , bbt ) ) ;
2022-11-29 11:41:17 -05:00
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " GendA %1 \t %2 \n " , metric , ret . back ( ) ) ) ;
}
/* Advance by the number of bars specified by
bar_mod , then recompute the beats and
superclock position corresponding to that
BBT time .
*/
bbt . bars + = bar_mod ;
} else {
ret . push_back ( TempoMapPoint ( * this , metric , start , beats , bbt ) ) ;
DEBUG_TRACE ( DEBUG : : Grid , string_compose ( " GendB %1 \t %2 \n " , metric , ret . back ( ) ) ) ;
/* move on by 1 meter-defined "beat" */
if ( beat_div = = 1 ) {
bbt = metric . bbt_add ( bbt , BBT_Offset ( 0 , 1 , 0 ) ) ;
} else {
bbt = metric . bbt_add ( bbt , BBT_Offset ( 0 , 0 , Temporal : : ticks_per_beat / beat_div ) ) ;
2021-02-11 00:30:05 -05:00
}
}
2022-11-29 11:41:17 -05:00
/* compute audio and quarter-note time from the new BBT position */
2021-01-31 20:39:29 -05:00
2022-11-29 11:41:17 -05:00
start = metric . superclock_at ( bbt ) ;
beats = metric . quarters_at ( bbt ) ;
}
2020-08-05 18:02:39 -04:00
2021-02-09 01:02:26 -05:00
/* all done */
2022-11-29 11:41:17 -05:00
2021-02-09 01:02:26 -05:00
} 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
2022-04-08 11:23:19 -04:00
TempoMap : : count_bars ( Beats const & start , Beats const & end ) const
2021-03-19 16:17:29 -04:00
{
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 ( ) ) {
2022-05-13 20:58:17 -04:00
return str < < t . note_types_per_minute ( ) < < " .. " < < t . end_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 ( ) ;
}
2022-05-20 11:06:18 -04:00
str < < " omega = " < < std : : setprecision ( 12 ) < < t . omega ( ) ;
2020-08-05 18:02:39 -04:00
}
return str ;
}
2021-02-07 00:09:16 -05:00
std : : ostream &
std : : operator < < ( std : : ostream & str , MusicTimePoint const & p )
{
2022-05-24 19:08:17 -04:00
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 )
{
2022-03-17 16:14:41 -04:00
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 ( ) ) {
2022-05-20 11:06:18 -04:00
str < < " ramp omega = " < < tmp . tempo ( ) . omega ( ) ;
2020-08-05 18:02:39 -04:00
}
return str ;
}
BBT_Time
TempoMap : : bbt_walk ( BBT_Time const & bbt , BBT_Offset const & o ) const
{
BBT_Offset offset ( o ) ;
2021-11-17 17:59:03 -05:00
BBT_Time start ( bbt ) ;
2020-08-05 18:02:39 -04:00
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 ;
}
}
Fix various typos
Found via `codespell -q 3 -S *.po,./.git,./share/patchfiles,./libs,./msvc_extra_headers,./share/web_surfaces,*.patch -L ba,buss,busses,discreet,doubleclick,hsi,ontop,ro,scrollin,seh,siz,sord,sur,te,trough,ue`
2022-08-01 14:21:37 -04:00
/* may have found tempo and/or meter precisely at the time given */
2020-08-05 18:02:39 -04:00
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 ) ) ;
2021-11-17 17:59:03 -05:00
2020-08-05 18:02:39 -04:00
/* 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 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 \
2021-11-17 17:59:03 -05:00
if ( ( ( next_t ! = _tempos . end ( ) ) & & ( start > = next_t - > bbt ( ) ) ) | | \
( ( next_m ! = _meters . end ( ) ) & & ( start > = next_m - > bbt ( ) ) ) ) { \
2020-08-05 18:02:39 -04:00
/* need new metric */ \
2021-11-17 17:59:03 -05:00
if ( start > = next_t - > bbt ( ) ) { \
if ( start > = next_m - > bbt ( ) ) { \
2020-08-05 18:02:39 -04:00
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 ; \
} \
2021-11-17 17:59:03 -05:00
} else if ( start > = next_m - > bbt ( ) ) { \
2020-08-05 18:02:39 -04:00
metric = TempoMetric ( metric . tempo ( ) , * const_cast < MeterPoint * > ( & * next_m ) ) ; \
+ + next_m ; \
} \
}
2021-11-17 17:59:03 -05:00
for ( int32_t b = 0 ; b < offset . bars ; + + b ) {
2020-08-05 18:02:39 -04:00
TEMPO_CHECK_FOR_NEW_METRIC ;
2021-11-17 17:59:03 -05:00
start . bars + = 1 ;
2020-08-05 18:02:39 -04:00
}
2021-11-17 17:59:03 -05:00
for ( int32_t b = 0 ; b < offset . beats ; + + b ) {
2020-08-05 18:02:39 -04:00
TEMPO_CHECK_FOR_NEW_METRIC ;
2021-11-17 17:59:03 -05:00
start . beats + = 1 ;
if ( start . beats > metric . divisions_per_bar ( ) ) {
start . bars + = 1 ;
start . beats = 1 ;
}
}
start . ticks + = offset . ticks ;
2020-08-05 18:02:39 -04:00
2021-11-17 17:59:03 -05:00
if ( start . ticks > = ticks_per_beat ) {
start . beats + = 1 ;
start . ticks % = ticks_per_beat ;
2020-08-05 18:02:39 -04:00
}
2021-11-17 17:59:03 -05:00
return start ;
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 &
2022-04-06 23:56:32 -04:00
TempoMap : : get_state ( ) const
2020-08-05 18:02:39 -04:00
{
XMLNode * node = new XMLNode ( X_ ( " TempoMap " ) ) ;
2022-03-17 16:14:41 -04:00
node - > set_property ( X_ ( " superclocks-per-second " ) , superclock_ticks_per_second ( ) ) ;
2020-08-05 18:02:39 -04:00
XMLNode * children ;
children = new XMLNode ( X_ ( " Tempos " ) ) ;
node - > add_child_nocopy ( * children ) ;
for ( Tempos : : const_iterator t = _tempos . begin ( ) ; t ! = _tempos . end ( ) ; + + t ) {
2022-05-24 19:10:14 -04:00
if ( ! dynamic_cast < MusicTimePoint const * > ( & ( * t ) ) ) {
children - > add_child_nocopy ( t - > get_state ( ) ) ;
}
2020-08-05 18:02:39 -04:00
}
children = new XMLNode ( X_ ( " Meters " ) ) ;
node - > add_child_nocopy ( * children ) ;
for ( Meters : : const_iterator m = _meters . begin ( ) ; m ! = _meters . end ( ) ; + + m ) {
2022-05-24 19:10:14 -04:00
if ( ! dynamic_cast < MusicTimePoint const * > ( & ( * m ) ) ) {
children - > add_child_nocopy ( m - > get_state ( ) ) ;
}
2020-08-05 18:02:39 -04:00
}
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-03-25 10:51:08 -04:00
if ( version < = 6000 ) {
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
2022-05-24 19:21:18 -04:00
/* XXX this should probably be at the global level in the session file
* because it is the time unit for anything in the audio time domain ,
* and affects a lot more than just the tempo map
*/
2022-03-17 16:14:41 -04:00
superclock_t sc ;
2022-05-06 22:12:46 -04:00
if ( node . get_property ( X_ ( " superclocks-per-second " ) , sc ) ) {
set_superclock_ticks_per_second ( sc ) ;
}
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
2022-06-16 12:18:32 -04:00
/* XXX might be good to have a recovery mechanism in case setting
* things from XML fails . Not very likely , however .
*/
_tempos . clear ( ) ;
_meters . clear ( ) ;
_bartimes . clear ( ) ;
_points . clear ( ) ;
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 ) ) {
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 ) ) {
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 ) ) {
return - 1 ;
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 {
for ( XMLNodeList : : const_iterator c = children . begin ( ) ; c ! = children . end ( ) ; + + c ) {
MusicTimePoint * mp = new MusicTimePoint ( * this , * * c ) ;
2022-05-24 19:10:14 -04:00
add_or_replace_bartime ( mp ) ;
2020-12-20 13:39:12 -05:00
}
} 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 ( ) ) ;
2022-05-24 19:10:14 -04:00
bool ignore ;
2020-08-05 18:02:39 -04:00
try {
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 ) ;
2022-05-24 19:10:14 -04:00
core_add_tempo ( tp , ignore ) ;
core_add_point ( 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 ( ) ) ;
2022-05-24 19:10:14 -04:00
bool ignore ;
2020-08-05 18:02:39 -04:00
try {
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 ) ;
2022-05-24 19:10:14 -04:00
core_add_meter ( mp , ignore ) ;
core_add_point ( 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 ) ;
}
2022-10-30 20:44:28 -04:00
/** returns the duration (using the domain of @p 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 . .
2022-10-30 20:44:28 -04:00
* @ return the timecnt_t that @ p bbt represents when starting at @ p pos , in
* the time domain of @ p 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 ) ;
}
2022-06-03 22:33:48 -04:00
return timecnt_t ( bbtwalk_to_quarters ( pos . beats ( ) , dur ) , pos ) ;
2020-11-24 17:06:35 -05:00
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
}
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
}
2022-05-02 13:40:23 -04:00
TempoPoint const *
TempoMap : : next_tempo ( TempoPoint const & t ) const
{
2022-05-15 19:49:00 -04:00
Tempos : : const_iterator i = _tempos . iterator_to ( t ) ;
+ + i ;
2022-05-02 13:40:23 -04:00
2022-05-15 19:49:00 -04:00
if ( i ! = _tempos . end ( ) ) {
return & ( * i ) ;
2022-05-02 13:40:23 -04:00
}
return 0 ;
}
2020-08-05 18:02:39 -04:00
TempoPoint const *
TempoMap : : previous_tempo ( TempoPoint const & point ) const
{
2022-05-15 19:49:00 -04:00
Tempos : : const_iterator i = _tempos . iterator_to ( point ) ;
2020-08-05 18:02:39 -04:00
2022-05-15 19:49:00 -04:00
if ( i = = _tempos . begin ( ) ) {
return 0 ;
2020-08-05 18:02:39 -04:00
}
2022-05-21 20:58:41 -04:00
- - i ;
2022-05-15 19:49:00 -04:00
return & ( * i ) ;
2020-08-05 18:02:39 -04:00
}
2021-11-12 16:19:10 -05:00
double
TempoMap : : quarters_per_minute_at ( timepos_t const & pos ) const
{
TempoPoint const & tp ( tempo_at ( pos ) ) ;
const double val = tp . note_types_per_minute_at_DOUBLE ( pos ) * ( 4.0 / tp . note_type ( ) ) ;
return val ;
}
2021-11-11 18:50:04 -05:00
TempoPoint const &
TempoMap : : tempo_at ( timepos_t const & pos ) const
{
2021-11-12 16:19:10 -05:00
return pos . is_beats ( ) ? tempo_at ( pos . beats ( ) ) : tempo_at ( pos . superclocks ( ) ) ;
2021-11-11 18:50:04 -05:00
}
2021-11-13 12:36:27 -05:00
MeterPoint const &
TempoMap : : meter_at ( timepos_t const & pos ) const
2021-11-11 18:50:04 -05:00
{
2021-11-13 12:36:27 -05:00
return pos . is_beats ( ) ? meter_at ( pos . beats ( ) ) : meter_at ( pos . superclocks ( ) ) ;
2021-11-11 18:50:04 -05:00
}
2020-08-05 18:02:39 -04:00
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
}
2022-05-23 13:24:59 -04:00
bool
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 ( ) ) ;
2021-11-15 23:27:41 -05:00
if ( tp . ramped ( ) = = yn ) {
2022-05-23 13:24:59 -04:00
return false ;
2021-04-04 19:53:49 -04:00
}
Tempos : : iterator nxt = _tempos . begin ( ) ;
+ + nxt ;
for ( Tempos : : iterator t = _tempos . begin ( ) ; nxt ! = _tempos . end ( ) ; + + t , + + nxt ) {
if ( tp = = * t ) {
break ;
}
}
2022-05-02 18:44:15 -04:00
if ( nxt = = _tempos . end ( ) ) {
2022-05-23 13:24:59 -04:00
return false ;
2022-05-02 18:44:15 -04:00
}
2021-11-15 23:27:41 -05:00
if ( yn ) {
2022-05-13 20:55:25 -04:00
tp . set_end_npm ( nxt - > end_note_types_per_minute ( ) ) ;
2021-11-15 23:27:41 -05:00
} else {
2022-05-13 20:55:25 -04:00
tp . set_end_npm ( tp . note_types_per_minute ( ) ) ;
2021-11-15 23:27:41 -05:00
}
2022-05-13 20:55:25 -04:00
reset_starting_at ( tp . sclock ( ) ) ;
2022-05-23 13:24:59 -04:00
return true ;
2020-08-05 18:02:39 -04:00
}
2022-05-23 13:24:59 -04:00
bool
TempoMap : : set_continuing ( TempoPoint & tp , bool yn )
{
if ( ! yn ) {
tp . set_continuing ( false ) ;
return true ; /* change made */
}
TempoPoint const * prev = previous_tempo ( tp ) ;
if ( ! prev ) {
return false ;
}
tp . set_note_types_per_minute ( prev - > note_types_per_minute ( ) ) ;
return true ;
}
2022-05-12 14:14:45 -04:00
void
2022-05-13 21:00:33 -04:00
TempoMap : : stretch_tempo ( TempoPoint * ts , samplepos_t sample , samplepos_t end_sample , Beats const & start_qnote , Beats const & end_qnote )
2022-05-12 14:14:45 -04:00
{
/*
Ts ( future prev_t ) Tnext
| |
| [ drag ^ ] |
| - - - - - - - - - - | - - - - - - - - - -
e_f qn_beats ( sample )
*/
if ( ! ts ) {
return ;
}
superclock_t start_sclock = samples_to_superclock ( sample , TEMPORAL_SAMPLE_RATE ) ;
superclock_t end_sclock = samples_to_superclock ( end_sample , TEMPORAL_SAMPLE_RATE ) ;
/* minimum allowed measurement distance in samples */
const superclock_t min_delta_sclock = samples_to_superclock ( 2 , TEMPORAL_SAMPLE_RATE ) ;
double new_bpm ;
2022-05-23 10:06:36 -04:00
if ( ts - > continuing ( ) ) {
2022-05-12 14:14:45 -04:00
/* this tempo point is required to start using the same bpm
* that the previous tempo ended with .
*/
TempoPoint * next_t = const_cast < TempoPoint * > ( next_tempo ( * ts ) ) ;
TempoPoint * prev_to_ts = const_cast < TempoPoint * > ( previous_tempo ( * ts ) ) ;
assert ( prev_to_ts ) ;
/* the change in samples is the result of changing the slope of at most 2 previous tempo sections.
* constant to constant is straightforward , as the tempo prev to ts has constant slope .
*/
double contribution = 0.0 ;
if ( next_t & & prev_to_ts - > ramped ( ) ) {
const DoubleableBeats delta_tp = ts - > beats ( ) - prev_to_ts - > beats ( ) ;
const DoubleableBeats delta_np = next_t - > beats ( ) - prev_to_ts - > beats ( ) ;
contribution = delta_tp . to_double ( ) / delta_np . to_double ( ) ;
}
samplepos_t const fr_off = end_sclock - start_sclock ;
sampleoffset_t const ts_sample_contribution = fr_off - ( contribution * ( double ) fr_off ) ;
if ( start_sclock > prev_to_ts - > sclock ( ) + min_delta_sclock & & ( start_sclock + ts_sample_contribution ) > prev_to_ts - > sclock ( ) + min_delta_sclock ) {
DoubleableBeats delta_sp = start_qnote - prev_to_ts - > beats ( ) ;
DoubleableBeats delta_ep = end_qnote - prev_to_ts - > beats ( ) ;
new_bpm = ts - > note_types_per_minute ( ) * ( delta_sp . to_double ( ) / delta_ep . to_double ( ) ) ;
} else {
new_bpm = ts - > note_types_per_minute ( ) ;
}
} else {
/* ts is free to have it's bpm changed to any value (within limits) */
if ( start_sclock > ts - > sclock ( ) + min_delta_sclock & & end_sclock > ts - > sclock ( ) + min_delta_sclock ) {
new_bpm = ts - > note_types_per_minute ( ) * ( ( start_sclock - ts - > sclock ( ) ) / ( double ) ( end_sclock - ts - > sclock ( ) ) ) ;
} else {
new_bpm = ts - > note_types_per_minute ( ) ;
}
new_bpm = std : : min ( new_bpm , 1000.0 ) ;
}
/* 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 ;
}
ts - > set_note_types_per_minute ( new_bpm ) ;
2022-05-23 10:06:36 -04:00
if ( ts - > continuing ( ) ) {
2022-05-12 14:14:45 -04:00
TempoPoint * prev = 0 ;
if ( ( prev = const_cast < TempoPoint * > ( previous_tempo ( * ts ) ) ) ! = 0 ) {
2022-05-13 20:55:25 -04:00
prev - > set_end_npm ( ts - > end_note_types_per_minute ( ) ) ;
2022-05-12 14:14:45 -04:00
}
}
reset_starting_at ( ts - > sclock ( ) + 1 ) ;
}
2022-05-02 18:47:23 -04:00
void
2022-05-13 21:01:27 -04:00
TempoMap : : stretch_tempo_end ( TempoPoint * ts , samplepos_t sample , samplepos_t end_sample )
2020-08-05 18:02:39 -04:00
{
2022-05-13 21:01:27 -04:00
/*
Ts ( future prev_t ) Tnext
| |
| [ drag ^ ] |
| - - - - - - - - - - | - - - - - - - - - -
e_f qn_beats ( sample )
*/
if ( ! ts ) {
return ;
}
2020-08-05 18:02:39 -04:00
2022-05-13 21:01:27 -04:00
const superclock_t start_sclock = samples_to_superclock ( sample , TEMPORAL_SAMPLE_RATE ) ;
const superclock_t end_sclock = samples_to_superclock ( end_sample , TEMPORAL_SAMPLE_RATE ) ;
2020-08-05 18:02:39 -04:00
2022-05-13 21:01:27 -04:00
TempoPoint * prev_t = const_cast < TempoPoint * > ( previous_tempo ( * ts ) ) ;
2020-08-05 18:02:39 -04:00
2022-05-13 21:01:27 -04:00
if ( ! prev_t ) {
return ;
}
/* minimum allowed measurement distance in superclocks */
const superclock_t min_delta_sclock = samples_to_superclock ( 2 , TEMPORAL_SAMPLE_RATE ) ;
double new_bpm ;
if ( start_sclock > prev_t - > sclock ( ) + min_delta_sclock & & end_sclock > prev_t - > sclock ( ) + min_delta_sclock ) {
new_bpm = prev_t - > end_note_types_per_minute ( ) * ( ( prev_t - > sclock ( ) - start_sclock ) / ( double ) ( prev_t - > sclock ( ) - end_sclock ) ) ;
2020-12-07 16:51:48 -05:00
} else {
2022-05-13 21:01:27 -04:00
new_bpm = prev_t - > end_note_types_per_minute ( ) ;
}
new_bpm = std : : min ( new_bpm , ( double ) 1000.0 ) ;
if ( new_bpm < 0.5 ) {
return ;
}
prev_t - > set_end_npm ( new_bpm ) ;
2022-05-23 10:06:36 -04:00
if ( ts - > continuing ( ) ) {
2022-05-23 13:25:50 -04:00
ts - > set_note_types_per_minute ( prev_t - > note_types_per_minute ( ) ) ;
2022-05-13 21:01:27 -04:00
}
reset_starting_at ( prev_t - > sclock ( ) ) ;
}
void
TempoMap : : twist_tempi ( TempoPoint * ts , samplepos_t start_sample , samplepos_t end_sample )
{
if ( ! ts ) {
return ;
}
TempoPoint * next_t = 0 ;
TempoPoint * next_to_next_t = 0 ;
/* minimum allowed measurement distance in superclocks */
const superclock_t min_delta_sclock = samples_to_superclock ( 2 , TEMPORAL_SAMPLE_RATE ) ;
const superclock_t start_sclock = samples_to_superclock ( start_sample , TEMPORAL_SAMPLE_RATE ) ;
const superclock_t end_sclock = samples_to_superclock ( end_sample , TEMPORAL_SAMPLE_RATE ) ;
TempoPoint * prev_t = 0 ;
const superclock_t sclock_offset = end_sclock - start_sclock ;
if ( ts - > beats ( ) > Beats ( ) ) {
prev_t = const_cast < TempoPoint * > ( previous_tempo ( * ts ) ) ;
}
next_t = const_cast < TempoPoint * > ( next_tempo ( * ts ) ) ;
if ( ! next_t ) {
return ;
}
next_to_next_t = const_cast < TempoPoint * > ( next_tempo ( * next_t ) ) ;
if ( ! next_to_next_t ) {
return ;
2020-12-07 16:51:48 -05:00
}
2020-08-05 18:02:39 -04:00
2022-05-13 21:01:27 -04:00
double prev_contribution = 0.0 ;
if ( next_t & & prev_t & & prev_t - > type ( ) = = TempoPoint : : Ramped ) {
prev_contribution = ( ts - > sclock ( ) - prev_t - > sclock ( ) ) / ( double ) ( next_t - > sclock ( ) - prev_t - > sclock ( ) ) ;
2020-12-07 16:51:48 -05:00
}
2022-05-13 21:01:27 -04:00
const sampleoffset_t ts_sclock_contribution = sclock_offset - ( prev_contribution * ( double ) sclock_offset ) ;
superclock_t old_tc_sclock = ts - > sclock ( ) ;
superclock_t old_next_sclock = next_t - > sclock ( ) ;
superclock_t old_next_to_next_sclock = next_to_next_t - > sclock ( ) ;
double new_bpm ;
double new_next_bpm ;
double new_copy_end_bpm ;
if ( start_sclock > ts - > sclock ( ) + min_delta_sclock & & ( start_sclock + ts_sclock_contribution ) > ts - > sclock ( ) + min_delta_sclock ) {
new_bpm = ts - > note_types_per_minute ( ) * ( ( start_sclock - ts - > sclock ( ) ) / ( double ) ( end_sclock - ts - > sclock ( ) ) ) ;
2022-05-02 18:47:23 -04:00
} else {
2022-05-13 21:01:27 -04:00
new_bpm = ts - > note_types_per_minute ( ) ;
}
/* 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 ;
2020-12-07 16:51:48 -05:00
}
2022-05-13 21:01:27 -04:00
new_bpm = std : : min ( new_bpm , ( double ) 1000.0 ) ;
2020-12-07 16:51:48 -05:00
2022-05-13 21:01:27 -04:00
bool was_constant = ( ts - > type ( ) = = TempoPoint : : Constant ) ;
2020-08-05 18:02:39 -04:00
2022-05-13 21:01:27 -04:00
ts - > set_note_types_per_minute ( new_bpm ) ;
if ( was_constant ) {
ts - > set_end_npm ( new_bpm ) ;
}
2020-12-07 16:51:48 -05:00
2022-05-13 21:01:27 -04:00
if ( ! next_t - > actually_ramped ( ) ) {
if ( start_sclock > ts - > sclock ( ) + min_delta_sclock & & end_sclock > ts - > sclock ( ) + min_delta_sclock ) {
new_next_bpm = next_t - > note_types_per_minute ( ) * ( ( next_to_next_t - > sclock ( ) - old_next_sclock ) / ( double ) ( ( old_next_to_next_sclock ) - old_next_sclock ) ) ;
2020-12-07 16:51:48 -05:00
} else {
2022-05-13 21:01:27 -04:00
new_next_bpm = next_t - > note_types_per_minute ( ) ;
2020-08-05 18:02:39 -04:00
}
2022-05-13 21:01:27 -04:00
next_t - > set_note_types_per_minute ( new_next_bpm ) ;
} else {
double next_sclock_ratio = 1.0 ;
double copy_sclock_ratio = 1.0 ;
if ( next_to_next_t ) {
2022-10-30 04:55:23 -04:00
next_sclock_ratio = ( next_to_next_t - > sclock ( ) - old_next_sclock ) / ( double ) ( old_next_to_next_sclock - old_next_sclock ) ;
2022-05-13 21:01:27 -04:00
copy_sclock_ratio = ( ( old_tc_sclock - next_t - > sclock ( ) ) / ( double ) ( old_tc_sclock - old_next_sclock ) ) ;
}
new_next_bpm = next_t - > note_types_per_minute ( ) * next_sclock_ratio ;
new_copy_end_bpm = ts - > end_note_types_per_minute ( ) * copy_sclock_ratio ;
ts - > set_end_npm ( new_copy_end_bpm ) ;
2022-05-23 10:06:36 -04:00
if ( next_t - > continuing ( ) ) {
2022-05-13 21:01:27 -04:00
next_t - > set_note_types_per_minute ( new_copy_end_bpm ) ;
2020-12-07 16:51:48 -05:00
} else {
2022-05-13 21:01:27 -04:00
next_t - > set_note_types_per_minute ( new_next_bpm ) ;
2020-12-07 16:51:48 -05:00
}
2022-05-13 21:01:27 -04:00
ts - > set_end_npm ( new_copy_end_bpm ) ;
2020-08-05 18:02:39 -04:00
}
2022-05-13 21:01:27 -04:00
reset_starting_at ( ts - > sclock ( ) ) ;
2020-08-05 18:02:39 -04:00
}
2020-11-28 00:13:41 -05:00
void
TempoMap : : init ( )
{
2022-04-08 11:23:19 -04:00
WritableSharedPtr 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
2022-04-08 11:23:19 -04:00
TempoMap : : WritableSharedPtr
2021-03-22 18:03:45 -04:00
TempoMap : : write_copy ( )
{
return _map_mgr . write_copy ( ) ;
}
2021-02-22 01:37:45 -05:00
int
2022-04-08 11:23:19 -04:00
TempoMap : : update ( TempoMap : : WritableSharedPtr m )
2020-12-31 16:04:25 -05:00
{
2021-02-22 01:37:45 -05:00
if ( ! _map_mgr . update ( m ) ) {
return - 1 ;
}
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
2022-06-16 12:18:05 -04:00
# ifndef NDEBUG
if ( DEBUG_ENABLED ( DEBUG : : TemporalMap ) ) {
m - > dump ( std : : cerr ) ;
}
# endif
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
2022-04-08 11:23:19 -04:00
TempoMap : : midi_clock_beat_at_or_after ( samplepos_t const pos , samplepos_t & clk_pos , uint32_t & clk_beat ) const
2021-03-17 15:03:56 -04:00
{
/* 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 ( ) ;
Fix MIDI Clock generator
MIDI clock start at the next beat (round_up_to_beat), so
here we have to round the current tick, rather than fall
back to a tick that is not yet complete, as 14da117bc88 does.
Reproduced with the Session from #9027
Start loop at bar 40 with MClk generator enabled.
```
#3 in __GI___assert_fail (assertion=0x7fedd86c4fd5 "clk_pos >= pos", file=0x7fedd86c38b7 "../libs/temporal/tempo.cc", line=3336, function=0x7fedd86c4f60 "void Temporal::TempoMap::midi_clock_beat_at_or_after(Temporal::samplepos_t, Temporal::samplepos_t&, uint32_t&) const") at assert.c:101
#4 in Temporal::TempoMap::midi_clock_beat_at_or_after(long, long&, unsigned int&) const (this= 0x560187e92c00, pos=20691033, clk_pos=@0x7fedc02178b8: 20691032, clk_beat=@0x7fedc02178c4: 11472) at ../libs/temporal/tempo.cc:3336
#5 in ARDOUR::MidiClockTicker::tick(long, long, unsigned int, long) (this=0x56018eed6db0, start_sample=20691033, end_sample=20692057, n_samples=1024, pre_roll=0) at ../libs/ardour/ticker.cc:170
#6 in ARDOUR::Session::send_mclk_for_cycle(long, long, unsigned int, long) (this=0x56018a216340, start_sample=20691033, end_sample=20692057, n_samples=1024, pre_roll=0) at ../libs/ardour/session.cc:7495
#7 in ARDOUR::AudioEngine::process_callback(unsigned int) (this=0x5601881a4f20, nframes=1024) at ../libs/ardour/audioengine.cc:563
```
2022-10-24 00:14:26 -04:00
/* We cannot use
* clk_pos = sample_at ( b ) ;
* because in this case we have to round up to the start
* of the next tick , not round to to the current tick .
* ( compare to 14 da117bc88 )
*/
clk_pos = PBD : : muldiv_round ( superclock_at ( b ) , TEMPORAL_SAMPLE_RATE , superclock_ticks_per_second ( ) ) ;
2021-03-17 15:03:56 -04:00
clk_beat = b . get_beats ( ) * 24 ;
assert ( clk_pos > = pos ) ;
}
2021-03-23 17:22:28 -04:00
/******** OLD STATE LOADING CODE SECTION *************/
2022-06-21 19:30:59 -04:00
#if 0
2021-03-23 17:22:28 -04:00
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 ;
}
2022-06-21 19:30:59 -04:00
# endif
2021-03-23 17:22:28 -04:00
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 ;
return - 1 ;
}
} else {
/* older session, make note type be quarter by default */
lts . note_type = 4.0 ;
}
2022-05-23 10:06:36 -04:00
/* older versions used "clamped" as the property name here */
if ( ! node . get_property ( " clamped " , lts . continuing ) ) {
lts . continuing = false ;
2021-03-23 17:22:28 -04:00
}
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 ;
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 ;
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
*/
2021-10-05 15:57:10 -04:00
int32_t initial_tempo_index = - 1 ;
int32_t initial_meter_index = - 1 ;
int32_t index ;
bool need_points_clear = true ;
2022-05-25 23:52:29 -04:00
bool initial_tempo_at_zero = true ;
bool initial_meter_at_zero = true ;
2021-03-25 10:51:08 -04:00
2021-10-05 15:57:10 -04:00
for ( niter = nlist . begin ( ) , index = 0 ; niter ! = nlist . end ( ) ; + + niter , + + index ) {
2021-03-23 17:22:28 -04:00
XMLNode * child = * niter ;
2021-10-05 15:57:10 -04:00
if ( ( initial_tempo_index < 0 ) & & ( 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 ;
}
2022-03-23 19:35:41 -04:00
if ( lts . sample ! = 0 ) {
2022-05-25 23:52:29 -04:00
initial_tempo_at_zero = false ;
2022-03-23 19:35:41 -04:00
}
2021-03-23 17:22:28 -04:00
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 ( ) ) ;
2022-05-23 10:06:36 -04:00
tp - > set_continuing ( lts . continuing ) ;
2021-10-05 15:57:10 -04:00
_tempos . clear ( ) ;
if ( need_points_clear ) {
_points . clear ( ) ;
need_points_clear = false ;
}
2021-03-25 10:51:08 -04:00
_tempos . push_back ( * tp ) ;
_points . push_back ( * tp ) ;
2021-10-05 15:57:10 -04:00
initial_tempo_index = index ;
2021-03-23 17:22:28 -04:00
2021-03-25 10:51:08 -04:00
}
2021-10-05 15:57:10 -04:00
if ( ( initial_meter_index < 0 ) & & ( child - > name ( ) = = Meter : : xml_node_name ) ) {
2021-03-25 10:51:08 -04:00
LegacyMeterState lms ;
if ( parse_meter_state_3x ( * child , lms ) ) {
error < < _ ( " Tempo map: could not use old meter state, restoring old one. " ) < < endmsg ;
break ;
}
2022-10-27 17:02:12 -04:00
if ( lms . sample ! = 0 ) {
2022-05-25 23:52:29 -04:00
initial_meter_at_zero = false ;
2022-03-23 19:35:41 -04:00
}
2021-03-25 10:51:08 -04:00
Meter m ( lms . divisions_per_bar , lms . note_type ) ;
MeterPoint * mp = new MeterPoint ( * this , m , 0 , Beats ( ) , BBT_Time ( ) ) ;
2021-10-05 15:57:10 -04:00
_meters . clear ( ) ;
if ( need_points_clear ) {
_points . clear ( ) ;
need_points_clear = false ;
}
2021-03-25 10:51:08 -04:00
_meters . push_back ( * mp ) ;
_points . push_back ( * mp ) ;
2021-10-05 15:57:10 -04:00
initial_meter_index = index ;
2021-03-25 10:51:08 -04:00
}
2021-10-05 15:57:10 -04:00
if ( initial_tempo_index > = 0 & & initial_meter_index > = 0 ) {
2021-03-25 10:51:08 -04:00
break ;
}
}
2022-02-11 10:46:49 -05:00
if ( initial_tempo_index < 0 | | initial_meter_index < 0 ) {
2021-03-25 10:51:08 -04:00
error < < _ ( " Old tempo map information is missing either tempo or meter information - ignored " ) < < endmsg ;
return - 1 ;
}
2021-10-05 15:57:10 -04:00
for ( niter = nlist . begin ( ) , index = 0 ; niter ! = nlist . end ( ) ; + + niter , + + index ) {
2021-03-25 10:51:08 -04:00
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 ;
}
2021-10-05 15:57:10 -04:00
if ( index = = initial_tempo_index ) {
/* already added */
continue ;
}
2022-03-23 19:35:41 -04:00
if ( index = = initial_tempo_index ) {
2022-05-25 23:52:29 -04:00
if ( ! initial_tempo_at_zero ) {
2022-03-23 19:35:41 -04:00
/* already added */
continue ;
}
}
2021-10-05 15:57:10 -04:00
2021-03-25 10:51:08 -04:00
Tempo t ( lts . note_types_per_minute ,
lts . end_note_types_per_minute ,
lts . note_type ) ;
2021-10-05 15:57:10 -04:00
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 ;
}
2021-10-05 15:57:10 -04:00
if ( index = = initial_meter_index ) {
2022-05-25 23:52:29 -04:00
if ( ! initial_meter_at_zero ) {
2022-03-23 19:35:41 -04:00
/* Add a BBT point to fix the meter location */
set_bartime ( lms . bbt , timepos_t ( lms . sample ) ) ;
} else {
continue ;
}
2021-10-05 15:57:10 -04:00
}
2021-03-23 17:22:28 -04:00
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 ( ) ) {
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 ( ) ) {
error < < string_compose ( _ ( " Multiple tempo definitions found at %1 " ) , prev_t - > pulse ( ) ) < < endmsg ;
return - 1 ;
}
}
}
prev = i ;
}
# endif
return 0 ;
}
2022-04-08 13:16:39 -04:00
2021-03-23 17:22:28 -04:00
#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 */
2022-05-13 20:55:25 -04:00
t - > set_end_npm ( t - > note_types_per_minute ( ) ) ;
2021-03-23 17:22:28 -04:00
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 ) {
2022-05-13 20:55:25 -04:00
prev_t - > set_end_npm ( t - > note_types_per_minute ( ) ) ;
2021-03-23 17:22:28 -04:00
}
}
prev_t = t ;
}
}
if ( prev_t ) {
2022-05-13 20:55:25 -04:00
prev_t - > set_end_npm ( prev_t - > note_types_per_minute ( ) ) ;
2021-03-23 17:22:28 -04:00
}
}
# endif
2022-04-08 13:16:39 -04:00
TempoCommand : : TempoCommand ( XMLNode const & node )
: _before ( 0 )
, _after ( 0 )
{
if ( ! node . get_property ( X_ ( " name " ) , _name ) ) {
throw failed_constructor ( ) ;
}
XMLNodeList const & children ( node . children ( ) ) ;
for ( XMLNodeList : : const_iterator n = children . begin ( ) ; n ! = children . end ( ) ; + + n ) {
if ( ( * n ) - > name ( ) = = X_ ( " before " ) ) {
if ( ( * n ) - > children ( ) . empty ( ) ) {
throw failed_constructor ( ) ;
}
_before = new XMLNode ( * ( * n ) - > children ( ) . front ( ) ) ;
} else if ( ( * n ) - > name ( ) = = X_ ( " after " ) ) {
if ( ( * n ) - > children ( ) . empty ( ) ) {
throw failed_constructor ( ) ;
}
2022-04-08 13:56:43 -04:00
_after = new XMLNode ( * ( * n ) - > children ( ) . front ( ) ) ;
2022-04-08 13:16:39 -04:00
}
}
if ( ! _before | | ! _after ) {
throw failed_constructor ( ) ;
}
}
TempoCommand : : TempoCommand ( std : : string const & str , XMLNode const * before , XMLNode const * after )
: _name ( str )
, _before ( before )
, _after ( after )
{
}
TempoCommand : : ~ TempoCommand ( )
{
delete _before ;
delete _after ;
}
XMLNode &
TempoCommand : : get_state ( ) const
{
XMLNode * node = new XMLNode ( X_ ( " TempoCommand " ) ) ;
node - > set_property ( X_ ( " name " ) , _name ) ;
if ( _before ) {
XMLNode * b = new XMLNode ( X_ ( " before " ) ) ;
b - > add_child_copy ( * _before ) ;
node - > add_child_nocopy ( * b ) ;
}
if ( _after ) {
XMLNode * a = new XMLNode ( X_ ( " after " ) ) ;
a - > add_child_copy ( * _after ) ;
node - > add_child_nocopy ( * a ) ;
}
return * node ;
}
void
TempoCommand : : undo ( )
{
if ( ! _before ) {
return ;
}
TempoMap : : WritableSharedPtr map ( TempoMap : : write_copy ( ) ) ;
map - > set_state ( * _before , Stateful : : current_state_version ) ;
TempoMap : : update ( map ) ;
}
void
TempoCommand : : operator ( ) ( )
{
if ( ! _after ) {
return ;
}
TempoMap : : WritableSharedPtr map ( TempoMap : : write_copy ( ) ) ;
map - > set_state ( * _after , Stateful : : current_state_version ) ;
TempoMap : : update ( map ) ;
}