diff --git a/sync.cc b/sync.cc index cda34cd..84b2543 100644 --- a/sync.cc +++ b/sync.cc @@ -2,6 +2,261 @@ #include "sync.hh" +/*= T_SyncTime ===============================================================*/ + +void T_SyncTime::setDuration( + __rd__ const float uDuration , + __rd__ const uint32_t iDuration ) +{ + this->uDuration = std::max( 1e-3f , uDuration ); + this->iDuration = std::max( 1u , iDuration ); + time = std::min( time , duration( ) ); +} + +/*= T_SyncCurves =============================================================*/ + +void T_SyncCurves::clear( ) +{ + curves.clear( ); + positions.clear( ); +} + +bool T_SyncCurves::addCurve( __rd__ T_SyncCurve curve ) +{ + const auto p( positions.find( curve.name ) ); + if ( p == positions.end( ) ) { + positions.emplace( curve.name , curves.size( ) ); + curves.emplace_back( std::move( curve ) ); + return true; + } + return false; +} + +int32_t T_SyncCurves::indexOf( __rd__ std::string const& name ) +{ + const auto p( positions.find( name ) ); + return p == positions.end( ) ? -1 : p->second; +} + + +/*= T_SyncCurveCache =========================================================*/ + +T_SyncCurveCache::T_SyncCurveCache( + __rd__ T_SyncTime const& time , + __rd__ T_SyncCurves const& curves , + __rd__ const uint32_t curve ) noexcept + : curve( curve ) , curPos( 0 ) +{ + auto const& c( curves.curves[ curve ] ); + const auto ns( c.segments.size( ) ); + assert( ns > 0 ); + + uint32_t s = 0; + for ( auto i = 0u ; i < ns ; i ++ ) { + auto const& v( c.segments[ i ] ); + assert( v.nPoints >= 2 ); + assert( v.durations.size( ) == v.nPoints - 1 ); + + const auto nd( v.nPoints - 1 ); + for ( auto j = 0u ; j < nd ; j ++ ) { + const auto sStart( s * time.uDuration ); + if ( time.time >= sStart ) { + curPos = segStarts.size( ); + } + segStarts.push_back( sStart ); + segRefs.push_back( std::make_pair( i , j ) ); + s += v.durations[ j ]; + segEnds.push_back( std::min( s , time.iDuration ) * time.uDuration ); + if ( s > time.iDuration ) { + return; + } + } + } +} + +/*----------------------------------------------------------------------------*/ + +uint32_t T_SyncCurveCache::findSegment( + __rd__ const float time ) const noexcept +{ + const auto ns( segStarts.size( ) ); + for ( auto i = 0u ; i < ns ; i ++ ) { + if ( segStarts[ i ] <= time && segEnds[ i ] > time ) { + return i; + } + } + return ns; +} + +/*----------------------------------------------------------------------------*/ + +float T_SyncCurveCache::valueAt( + __rd__ T_SyncTime const& time , + __rd__ T_SyncCurves const& curves , + __rd__ const float position ) const noexcept +{ + return segmentValue( time.time , findSegment( position ) , + curves.curves[ curve ].segments ); +} + +float T_SyncCurveCache::value( + __rd__ T_SyncTime const& time , + __rd__ T_SyncCurves const& curves ) noexcept +{ + const auto t( time.time ); + + // Check / update curPos + const float ss0( curPos == segStarts.size( ) + ? time.duration( ) + : segStarts[ curPos ] ); + if ( ss0 > t ) { + curPos = findSegment( t ); + } else { + while ( curPos < segStarts.size( ) && t >= segEnds[ curPos ] ) { + curPos ++; + } + } + + // We got the actual index in curPos, now compute the value. + return segmentValue( t , curPos , curves.curves[ curve ].segments ); +} + +/*----------------------------------------------------------------------------*/ + +float T_SyncCurveCache::segmentValue( + __rd__ float time , + __rd__ uint32_t segIndex , + __rd__ std::vector< T_SyncSegment > const& segments ) const noexcept +{ + const auto sss( segStarts.size( ) ); + if ( segIndex >= sss ) { + assert( sss != 0 ); + segIndex = sss - 1; + time = segEnds[ segIndex ]; + } + + auto const& idxp( segRefs[ segIndex ] ); + auto const& seg( segments[ idxp.second ] ); + + // Interpolation factor + const float st( segStarts[ segIndex ] ); + const float et( segEnds[ segIndex ] ); + const float v0 = ( time - st ) / ( et - st ); + float v = v0; + if ( seg.type != T_SyncSegment::LINEAR ) { + v *= v0; + if ( seg.type == T_SyncSegment::SMOOTH ) { + v *= 3 - 2 * v0; + } + } + + const auto pid( idxp.second ); + const float sv( seg.values[ pid ] ); + const float ev( seg.values[ pid + 1 ] ); + return v * ( ev - sv ) + sv; +} + + +/*= T_SyncValues =============================================================*/ + +T_SyncValues::T_SyncValues( ) +{ + values.push_back( 0 ); +} + +// --------------------------------------------------------------------- + +void T_SyncValues::clear( ) +{ + identifiers.clear( ); + values.clear( ); + overriden.clear( ); + positions.clear( ); + values.push_back( 0 ); +} + +bool T_SyncValues::addValue( + __rd__ std::string const& name , + __rd__ const float initial ) +{ + const auto np( positions.find( name ) ); + if ( np != positions.end( ) ) { + return false; + } + const auto li( values.size( ) - 1 ); + positions.emplace( name , li ); + identifiers.push_back( name ); + values.push_back( initial ); + std::swap( values[ li ] , values[ li + 1 ] ); + overriden.push_back( false ); + return true; +} + +uint32_t T_SyncValues::indexOf( + __rd__ std::string const& name ) const +{ + const auto np( positions.find( name ) ); + if ( np == positions.end( ) ) { + return values.size( ) - 1; + } else { + return np->second; + } +} + + +/*= T_SyncManager ============================================================*/ + +void T_SyncManager::setDuration( + __rd__ const float uDuration , + __rd__ const uint32_t iDuration ) +{ + time_.setDuration( uDuration , iDuration ); + updateCurveCaches( ); +} + +void T_SyncManager::setTime( + __rd__ const float time ) +{ + time_.setTime( time ); + updateValues( ); +} + +/*----------------------------------------------------------------------------*/ + +void T_SyncManager::updateCurveCaches( ) +{ + curveCaches_.clear( ); + const uint32_t nv( values_.identifiers.size( ) ); + for ( auto i = 0u ; i < nv ; i ++ ) { + auto const& id( values_.identifiers[ i ] ); + const auto cp( curves_.indexOf( id ) ); + if ( cp < 0 ) { + curveCaches_.emplace_back( ); + } else { + curveCaches_.emplace_back( new T_SyncCurveCache( + time_ , curves_ , cp + ) ); + } + } + updateValues( ); +} + +void T_SyncManager::updateValues( ) +{ + const auto nv( values_.identifiers.size( ) ); + assert( nv == curveCaches_.size( ) ); + for ( auto i = 0u ; i < nv ; i ++ ) { + auto const& cc( curveCaches_[ i ] ); + if ( !cc || values_.overriden[ i ] ) { + continue; + } + values_.values[ i ] = cc->value( time_ , curves_ ); + } +} + +/*============================================================================*/ + +#if 0 void T_SyncManager::makeUI( ) { auto const& dspSize( ImGui::GetIO( ).DisplaySize ); @@ -73,6 +328,7 @@ void T_SyncManager::displayOvControls( } } } +#endif /*============================================================================*/ diff --git a/sync.hh b/sync.hh index fda3cea..08f04f5 100644 --- a/sync.hh +++ b/sync.hh @@ -4,6 +4,177 @@ #endif +// Duration and current playing time +struct T_SyncTime +{ + float uDuration = 1e-3f; // Duration - unit size + uint32_t iDuration = 1; // Duration - total units + float time = 0; + + void setDuration( + __rd__ const float uDuration , + __rd__ const uint32_t iDuration ); + + float duration( ) const noexcept + { return uDuration * iDuration; } + + void setTime( __rd__ const float t ) noexcept + { time = std::max( 0.f , std::min( t , duration( ) ) ); } +}; + +/*============================================================================*/ + +// Segment of an input's curve +struct T_SyncSegment +{ + enum E_SegmentType + { + LINEAR , + RAMP , + SMOOTH , + //HERMITE + }; + + uint32_t nPoints; + E_SegmentType type; + std::vector< float > values; // nPoints items + std::vector< uint32_t > durations; // nPoints - 1 items +}; + +// An input curve +struct T_SyncCurve +{ + std::string name; + std::vector< T_SyncSegment > segments; +}; + +// All configured curves. Some may not actually correspond to an input and may +// have been defined for inputs that have been removed temporarily (e.g. +// because some include was commented out), in which case we don't want to +// waste them. +struct T_SyncCurves +{ + + std::vector< T_SyncCurve > curves; + std::map< std::string , int32_t > positions; + + void clear( ); + + // Returns true on success, false on duplicate + bool addCurve( __rd__ T_SyncCurve curve ); + + // Returns -1 on lookup failure + int32_t indexOf( __rd__ std::string const& name ); +}; + +/*----------------------------------------------------------------------------*/ + +// Pre-computed data for a curve +struct T_SyncCurveCache +{ + using T_SegRef = std::pair< uint32_t , uint32_t >; + + uint32_t curve; + std::vector< T_SegRef > segRefs; + std::vector< float > segStarts; + std::vector< float > segEnds; + uint32_t curPos; + + T_SyncCurveCache( + __rd__ T_SyncTime const& time , + __rd__ T_SyncCurves const& curves , + __rd__ const uint32_t curve ) noexcept; + + // Find the index of the segment for the specified time + uint32_t findSegment( + __rd__ const float time ) const noexcept; + + // Compute the value of the curve at the specified location, ignoring + // curPos. + float valueAt( + __rd__ T_SyncTime const& time , + __rd__ T_SyncCurves const& curves , + __rd__ const float position ) const noexcept; + + // Compute the value of the curve at the current time, using and + // updating curPos as necessary. + float value( + __rd__ T_SyncTime const& time , + __rd__ T_SyncCurves const& curves ) noexcept; + + float segmentValue( + __rd__ float time , + __rd__ uint32_t segIndex , + __rd__ std::vector< T_SyncSegment > const& segments ) const noexcept; +}; +using P_SyncCurveCache = std::unique_ptr< T_SyncCurveCache >; + +/*============================================================================*/ + +// Synchronization values. The values vector always contains an extra entry +// used for missing inputs. +struct T_SyncValues +{ + std::vector< std::string > identifiers; + std::vector< float > values; + std::vector< bool > overriden; + std::unordered_map< std::string , uint32_t > positions; + + T_SyncValues( ); + void clear( ); + + // Returns true on success, false on duplicate + bool addValue( + __rd__ std::string const& name , + __rd__ const float initial = 0.f ); + + // If the name isn't found, the last entry of values[] is returned + uint32_t indexOf( + __rd__ std::string const& name ) const; +}; + +/*============================================================================*/ + +// Synchronisation manager; handles all the synchronization data and makes it +// work together. +struct T_SyncManager +{ + // --------------------------------------------------------------------- + // Duration & time controls + + void setDuration( + __rd__ const float uDuration , + __rd__ const uint32_t iDuration ); + float duration( ) const noexcept + { return time_.duration( ); } + + void setTime( __rd__ const float time ); + void timeDelta( __rd__ const float delta ) + { setTime( time_.time + delta ); } + float time( ) const noexcept + { return time_.time; } + + bool finished( ) const noexcept + { return time_.time >= time_.duration( ); } + + // --------------------------------------------------------------------- + + void updateCurveCaches( ); + void updateValues( ); + + private: + T_SyncTime time_; + T_SyncValues values_; + T_SyncCurves curves_; + std::vector< P_SyncCurveCache > curveCaches_; +}; + + +/*============================================================================*/ +// DISREGARD EVERYTHING BELOW, IT SUCKS! +/*============================================================================*/ + +#if 0 // Definition of a single input - id & range struct T_SyncInputDfn { @@ -50,53 +221,6 @@ struct T_SyncUISection /*============================================================================*/ -// Segment of an input's curve -struct T_SyncSegment -{ - enum E_SegmentType - { - LINEAR , - RAMP , - SMOOTH , - //HERMITE - }; - - uint32_t nPoints; - E_SegmentType type; - std::vector< float > values; // nPoints items - std::vector< uint32_t > durations; // nPoints - 1 items -}; - -// An input curve -struct T_SyncCurve -{ - std::string name; - std::vector< T_SyncSegment > segments; -}; - -// Pre-computed data for a curve -struct T_SyncCurveCache -{ - using T_SegRef = std::pair< uint32_t , uint32_t >; - - T_SyncCurve const* variable; - std::vector< T_SegRef > segRefs; - std::vector< float > segStarts; - uint32_t curPos; - - T_SyncCurveCache( - __rd__ T_SyncCurve const* const variable , - __rd__ const uint32_t duration , - __rd__ const uint32_t position ) noexcept; - - float valueAt( - __rd__ const float position , - __rd__ const float units ) const noexcept; -}; -using P_SyncCurveCache = std::unique_ptr< T_SyncCurveCache >; - -/*============================================================================*/ - struct T_SyncManager { float uDuration; // Duration - unit size @@ -139,6 +263,7 @@ struct T_SyncManager void displayOvControls( __rw__ T_SyncUIOverrides& overrides ); }; +#endif /*============================================================================*/