Emmanuel BENOîT
6557d369eb
If an inconsistent override (i.e. an override that has segments of different lengths or durations) is selected, it will be modified in order to make it consistent. Also, if some (but not all) of the inputs are missing segments or points, they will be added.
455 lines
12 KiB
C++
455 lines
12 KiB
C++
#pragma once
|
|
#include "c-filewatcher.hh"
|
|
#include "c-project.hh"
|
|
|
|
#include <ebcl/SRDParserConfig.hh>
|
|
#include <ebcl/Sets.hh>
|
|
#include <ebcl/Algorithms.hh>
|
|
|
|
|
|
// Duration and current playing time
|
|
struct T_SyncTime
|
|
{
|
|
float uDuration = 1.f / 60.f; // Duration - unit size
|
|
uint32_t iDuration = 3600; // Duration - total units
|
|
float time = 0;
|
|
|
|
void setDuration(
|
|
const float uDuration ,
|
|
const uint32_t iDuration );
|
|
|
|
float duration( ) const noexcept
|
|
{ return uDuration * iDuration; }
|
|
|
|
void setTime( 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
|
|
};
|
|
|
|
E_SegmentType type;
|
|
T_Array< float > values{ T_Array< float >( 16 ) };
|
|
T_Array< uint32_t > durations{ T_Array< uint32_t >( 16 ) };
|
|
|
|
// Find the amount of time units before point
|
|
uint32_t findTimeOfPoint( uint32_t index ) const noexcept;
|
|
|
|
// Compute the value for the specified position, which is relative
|
|
// to the start of the segment.
|
|
float computeValue( float relTimeUnits ) const noexcept;
|
|
};
|
|
M_LSHIFT_OP( T_StringBuilder , T_SyncSegment::E_SegmentType );
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
// Possible results when trying to check whether two curves have a similar
|
|
// structure.
|
|
enum class E_SyncCurveMatch
|
|
{
|
|
// Both curves have the same structure.
|
|
IDENTICAL ,
|
|
|
|
// One of the curves has less information than the other, but the
|
|
// information in the shortest curve matches the corresponding
|
|
// information in the longest curve (i.e. it is only missing
|
|
// segments). _SHORT/_LONG indicate that the curve for which the
|
|
// comparison method was called is the shortest/longest (resp.)
|
|
MATCHING_SHORT ,
|
|
MATCHING_LONG ,
|
|
|
|
// One of the curves has less information than the other, and its
|
|
// last segment is shorter than the corresponding segment in the
|
|
// longest record (but its end matches a point in the longest curve's
|
|
// segment).
|
|
LASTSEG_SHORT ,
|
|
LASTSEG_LONG ,
|
|
|
|
// The curves do not match.
|
|
MISMATCH
|
|
};
|
|
|
|
// An input curve
|
|
struct T_SyncCurve
|
|
{
|
|
T_String name;
|
|
T_Array< T_SyncSegment > segments{ 16 };
|
|
|
|
T_SyncCurve( ) noexcept { }
|
|
|
|
T_SyncCurve( T_String const& name ) noexcept
|
|
: name( name )
|
|
{ }
|
|
|
|
T_SyncCurve( char const* name ) noexcept
|
|
: name( T_String( name ).usePool( ) )
|
|
{ }
|
|
|
|
// Compute the value at the specified time.
|
|
float computeValue( float timeUnits ) const noexcept;
|
|
|
|
// Compare this curve to another.
|
|
E_SyncCurveMatch matches(
|
|
T_SyncCurve const& other ) const noexcept;
|
|
|
|
// Count the points in the curve. A "point" corresponds to a value
|
|
// inside a segment, or the first and last value in the curve, or
|
|
// a junction between segments.
|
|
uint32_t points( ) const noexcept;
|
|
};
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
// 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
|
|
{
|
|
|
|
T_ObjectTable< T_String , T_SyncCurve > curves;
|
|
|
|
T_SyncCurves( )
|
|
: curves( []( T_SyncCurve const& c ) -> T_String { return c.name; } )
|
|
{ }
|
|
|
|
void clear( ) noexcept
|
|
{ curves.clear( ); }
|
|
void setCurve( T_SyncCurve curve ) noexcept
|
|
{ curves.set( std::move( curve ) ); }
|
|
bool removeCurve( T_String const& curve ) noexcept
|
|
{ return curves.remove( curve ); }
|
|
|
|
// Returns -1 on lookup failure
|
|
int32_t indexOf( T_String const& name ) noexcept;
|
|
int32_t indexOf( char const* name ) noexcept
|
|
{
|
|
return indexOf( T_String( name ) );
|
|
}
|
|
};
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
// Pre-computed data for a curve
|
|
struct T_SyncCurveCache
|
|
{
|
|
using T_SegRef = std::pair< uint32_t , uint32_t >;
|
|
|
|
uint32_t curve;
|
|
T_Array< T_SegRef > segRefs;
|
|
T_Array< float > segStarts;
|
|
T_Array< float > segEnds;
|
|
uint32_t curPos;
|
|
|
|
T_SyncCurveCache(
|
|
T_SyncTime const& time ,
|
|
T_SyncCurves const& curves ,
|
|
const uint32_t curve ) noexcept;
|
|
|
|
// Find the index of the segment for the specified time
|
|
uint32_t findSegment(
|
|
const float time ) const noexcept;
|
|
|
|
// Compute the value of the curve at the specified location, ignoring
|
|
// curPos.
|
|
float valueAt(
|
|
T_SyncTime const& time ,
|
|
T_SyncCurves const& curves ,
|
|
const float position ) const noexcept;
|
|
|
|
// Compute the value of the curve at the current time, using and
|
|
// updating curPos as necessary.
|
|
float value(
|
|
T_SyncTime const& time ,
|
|
T_SyncCurves const& curves ) noexcept;
|
|
|
|
float segmentValue(
|
|
float time ,
|
|
uint32_t segIndex ,
|
|
T_Array< T_SyncSegment > const& segments ) const noexcept;
|
|
};
|
|
using P_SyncCurveCache = T_OwnPtr< T_SyncCurveCache >;
|
|
|
|
/*============================================================================*/
|
|
|
|
// Synchronization values. The values vector always contains an extra entry
|
|
// used for missing inputs.
|
|
struct T_SyncValues
|
|
{
|
|
T_HashIndex index;
|
|
T_Array< T_String > identifiers;
|
|
T_Array< float > values;
|
|
T_Array< bool > overriden;
|
|
|
|
T_SyncValues( );
|
|
void clear( );
|
|
|
|
// Returns true on success, false on duplicate
|
|
bool addValue(
|
|
T_String const& name ,
|
|
const float initial = 0.f ) noexcept;
|
|
|
|
bool addValue(
|
|
char const* name ,
|
|
const float initial = 0.f ) noexcept
|
|
{
|
|
return addValue( T_String( name ) , initial );
|
|
}
|
|
|
|
// If the name isn't found, the last entry of values[] is returned
|
|
uint32_t indexOf(
|
|
T_String const& name ) const noexcept;
|
|
};
|
|
|
|
/*============================================================================*/
|
|
|
|
class A_SyncOverride;
|
|
using P_SyncOverride = T_OwnPtr< A_SyncOverride >;
|
|
|
|
struct T_SyncOverrideSection;
|
|
using P_SyncOverrideSection = T_OwnPtr< T_SyncOverrideSection >;
|
|
|
|
// Base class for overrides
|
|
class A_SyncOverride
|
|
{
|
|
private:
|
|
const T_String type_;
|
|
bool enabled_{ false };
|
|
ebcl::T_SRDLocation location_;
|
|
T_String id_;
|
|
|
|
protected:
|
|
ebcl::T_Buffer< char > title_;
|
|
ebcl::T_Set< T_String > inputs_{
|
|
ebcl::UseTag< ebcl::ArrayBacked< 8 > >( ) };
|
|
T_AutoArray< uint32_t , 8 > inputPos_;
|
|
|
|
A_SyncOverride( char const* type ,
|
|
T_String const& title ) noexcept;
|
|
|
|
public:
|
|
A_SyncOverride( ) = delete;
|
|
virtual ~A_SyncOverride( ) = 0;
|
|
|
|
T_String const& type( ) const noexcept
|
|
{ return type_; }
|
|
|
|
auto const& inputNames( ) const noexcept
|
|
{ return inputs_; }
|
|
auto const& inputPositions( ) const noexcept
|
|
{ return inputPos_; }
|
|
|
|
bool& enabled( ) noexcept
|
|
{ return enabled_; }
|
|
bool enabled( ) const noexcept
|
|
{ return enabled_; }
|
|
|
|
T_String const& id( ) const noexcept
|
|
{ return id_; }
|
|
char const* title( ) const noexcept
|
|
{ return &title_[ 0 ]; }
|
|
|
|
ebcl::T_SRDLocation& location( ) noexcept
|
|
{ return location_; }
|
|
ebcl::T_SRDLocation const& location( ) const noexcept
|
|
{ return location_; }
|
|
|
|
// Connect the required inputs to the sync manager. Called once
|
|
// the inputs have been added.
|
|
virtual void setup( ) noexcept;
|
|
|
|
// Create a clone of the current override.
|
|
virtual T_OwnPtr< A_SyncOverride > clone( ) const noexcept = 0;
|
|
};
|
|
|
|
// Overrides section
|
|
struct T_SyncOverrideSection
|
|
{
|
|
const T_String title;
|
|
const ebcl::T_Buffer< char > cTitle;
|
|
bool open{ false };
|
|
T_Array< P_SyncOverrideSection > subsections;
|
|
T_Array< P_SyncOverride > overrides;
|
|
|
|
T_SyncOverrideSection( ) = delete;
|
|
NO_COPY( T_SyncOverrideSection );
|
|
NO_MOVE( T_SyncOverrideSection );
|
|
|
|
explicit T_SyncOverrideSection( T_String title ) noexcept;
|
|
|
|
void merge( T_SyncOverrideSection& other ) noexcept;
|
|
|
|
// Generate the UI. A counter and temporary string builder are passed
|
|
// to help generating "fake" labels for use with ImGui.
|
|
void makeUI( uint32_t& counter ,
|
|
T_StringBuilder& sb ,
|
|
bool topLevel = false ) noexcept;
|
|
|
|
T_SyncOverrideSection& section(
|
|
T_String const& name ) noexcept;
|
|
T_SyncOverrideSection const* section(
|
|
T_String const& name ) const noexcept;
|
|
};
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
struct T_SyncOverrideVisitor
|
|
{
|
|
public:
|
|
using T_Element = ebcl::T_Union<
|
|
T_SyncOverrideSection* ,
|
|
A_SyncOverride* >;
|
|
using T_OpElement = T_Optional< T_Element >;
|
|
using T_Visitor = ebcl::T_Visitor< T_Element , T_Element >;
|
|
using F_NodeAction = T_Visitor::F_NodeAction;
|
|
|
|
static T_OpElement nodeBrowser( T_Element element , uint32_t child );
|
|
|
|
T_Visitor visitor{ nodeBrowser };
|
|
};
|
|
|
|
/*============================================================================*/
|
|
|
|
// Synchronisation manager; handles all the synchronization data and makes it
|
|
// work together.
|
|
struct T_SyncManager : public virtual A_ProjectPathListener
|
|
{
|
|
T_SyncManager( ) noexcept;
|
|
~T_SyncManager( );
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Duration & time controls
|
|
|
|
void setDuration(
|
|
const float uDuration ,
|
|
const uint32_t iDuration );
|
|
float duration( ) const noexcept
|
|
{ return time_.duration( ); }
|
|
|
|
uint32_t durationUnits( ) const noexcept
|
|
{ return time_.iDuration; }
|
|
float durationUnitSize( ) const noexcept
|
|
{ return time_.uDuration; }
|
|
|
|
void setTime( const float time );
|
|
void timeDelta( const float delta )
|
|
{ setTime( time_.time + delta ); }
|
|
float time( ) const noexcept
|
|
{ return time_.time; }
|
|
|
|
bool playing( ) const noexcept
|
|
{ return playing_; }
|
|
bool& playing( ) noexcept
|
|
{ return playing_; }
|
|
|
|
bool finished( ) const noexcept
|
|
{ return time_.time >= time_.duration( ); }
|
|
void updateTime( ) noexcept;
|
|
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Value access
|
|
|
|
void clearInputs( ) noexcept;
|
|
bool addInput( T_String const& name ,
|
|
const float initial = 0.f ) noexcept
|
|
{ return values_.addValue( name , initial ); }
|
|
bool hasInput( T_String const& name ) const noexcept
|
|
{ return values_.identifiers.contains( name ); }
|
|
|
|
T_Array< T_String > const& inputNames( ) const noexcept
|
|
{ return values_.identifiers; }
|
|
uint32_t inputPos( T_String const& name ) const noexcept
|
|
{ return values_.indexOf( name ); }
|
|
T_Array< float > const& inputs( ) const noexcept
|
|
{ return values_.values; }
|
|
T_Array< float >& inputs( ) noexcept
|
|
{ return values_.values; }
|
|
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Curves
|
|
|
|
void clearCurves( );
|
|
void setCurve( T_SyncCurve curve );
|
|
void removeCurve( T_String const& curve ) noexcept;
|
|
T_SyncCurve const* getCurve(
|
|
T_String const& name ) const noexcept;
|
|
|
|
T_Array< T_SyncCurve > const& curves( ) const noexcept
|
|
{ return curves_.curves.values( ); }
|
|
|
|
bool curvesModified( ) const noexcept
|
|
{ return modified_; }
|
|
bool curvesFileChanged( ) const noexcept
|
|
{ return fileChanged_; }
|
|
|
|
bool loadCurves( bool undoable = true );
|
|
bool saveCurves( );
|
|
|
|
private:
|
|
void curvesChanged_( );
|
|
void addReloadUndoData_( void* data ) const noexcept;
|
|
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Overrides
|
|
|
|
public:
|
|
void clearOverrides( ) noexcept;
|
|
void mergeOverrides( T_SyncOverrideSection& overrides );
|
|
|
|
bool overrideExists( T_String const& id ) const noexcept;
|
|
A_SyncOverride* getOverride( T_String const& id ) const noexcept;
|
|
|
|
// Mark a bunch of inputs as overridden / not overridden
|
|
void setOverridesActive( bool active ,
|
|
uint32_t n ,
|
|
uint32_t const* pos );
|
|
|
|
// Visit the overrides tree
|
|
void visitOverrides( T_SyncOverrideVisitor::F_NodeAction visitor );
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Update
|
|
|
|
void updateCurveCaches( );
|
|
void updateValues( );
|
|
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Private data
|
|
|
|
private:
|
|
ebcl::T_SRDParserConfig pConfig_; // Parser config for curves
|
|
T_String curvesFile_; // Path to the curves file
|
|
T_WatchedFiles watcher_; // Curves file watcher
|
|
bool saving_{ false }; // True if file is being saved
|
|
bool modified_; // Locally modified
|
|
bool fileChanged_; // File modified
|
|
|
|
T_SyncTime time_; // Duration/time information
|
|
bool playing_{ false }; // Is it playing?
|
|
|
|
T_SyncValues values_; // Value storage
|
|
T_SyncCurves curves_; // Curves storage
|
|
T_Array< P_SyncCurveCache > curveCaches_; // Cache for curve segments
|
|
|
|
T_SyncOverrideSection soRoot_; // Root for overrides
|
|
T_SyncOverrideVisitor soVisitor_;
|
|
T_KeyValueTable< T_String , A_SyncOverride* > soTable_;
|
|
// Table of sync overrides, by ID
|
|
|
|
protected:
|
|
void projectPathChanged( ) noexcept;
|
|
};
|