2024-02-20 14:14:24 +00:00
# pragma once
2025-04-23 14:26:33 +00:00
# include <JuceHeader.h>
2025-08-14 07:39:51 +00:00
# include "../PluginProcessor.h"
2024-02-20 14:14:24 +00:00
2025-04-23 14:26:33 +00:00
class DashedLineEffect : public osci : : EffectApplication {
2024-02-20 14:14:24 +00:00
public :
2025-08-14 07:39:51 +00:00
DashedLineEffect ( OscirenderAudioProcessor & p ) : audioProcessor ( p ) { }
osci : : Point apply ( int index , osci : : Point input , const std : : vector < std : : atomic < double > > & values , double sampleRate ) override {
2025-08-16 19:03:08 +00:00
// if only 2 parameters are provided, this is being used as a 'trace effect'
// where the dash count is 1.
double dashCount = 1.0 ;
int i = 0 ;
if ( values . size ( ) > 2 ) {
dashCount = juce : : jmax ( 1.0 , values [ i + + ] . load ( ) ) ; // Dashes per cycle
2025-04-24 09:47:03 +00:00
}
2024-02-20 14:14:24 +00:00
2025-08-16 19:03:08 +00:00
double dashOffset = values [ i + + ] ;
2025-08-16 20:03:25 +00:00
double dashCoverage = juce : : jlimit ( 0.0 , 1.0 , values [ i + + ] . load ( ) ) ;
2025-08-16 19:03:08 +00:00
2025-08-14 07:39:51 +00:00
double dashLengthSamples = ( sampleRate / audioProcessor . frequency ) / dashCount ;
double dashPhase = framePhase * dashCount - dashOffset ;
dashPhase = dashPhase - std : : floor ( dashPhase ) ; // Wrap
buffer [ bufferIndex ] = input ;
// Linear interpolation works much better than nearest for this
double samplePos = bufferIndex - dashLengthSamples * dashPhase * ( 1 - dashCoverage ) ;
samplePos = samplePos - buffer . size ( ) * std : : floor ( samplePos / buffer . size ( ) ) ; // Wrap to [0, size]
int lowIndex = ( int ) std : : floor ( samplePos ) % buffer . size ( ) ;
int highIndex = ( lowIndex + 1 ) % buffer . size ( ) ;
double mixFactor = samplePos - std : : floor ( samplePos ) ; // Fractional part
osci : : Point output = ( 1 - mixFactor ) * buffer [ lowIndex ] + mixFactor * buffer [ highIndex ] ;
2024-02-20 14:14:24 +00:00
2025-04-24 09:47:03 +00:00
bufferIndex + + ;
2025-08-14 07:39:51 +00:00
if ( bufferIndex > = buffer . size ( ) ) {
bufferIndex = 0 ;
2025-04-24 09:47:03 +00:00
}
2025-08-14 07:39:51 +00:00
framePhase + = audioProcessor . frequency / sampleRate ;
framePhase = framePhase - std : : floor ( framePhase ) ;
return output ;
2025-04-24 09:47:03 +00:00
}
2024-02-20 14:14:24 +00:00
2025-08-10 19:45:06 +00:00
std : : shared_ptr < osci : : Effect > build ( ) const override {
auto eff = std : : make_shared < osci : : Effect > (
2025-08-16 19:03:08 +00:00
std : : make_shared < DashedLineEffect > ( audioProcessor ) ,
2025-08-10 19:45:06 +00:00
std : : vector < osci : : EffectParameter * > {
2025-08-16 19:03:08 +00:00
new osci : : EffectParameter ( " Dash Count " , " Controls the number of dashed lines in the drawing. " , " dashCount " , VERSION_HINT , 16.0 , 1.0 , 32.0 ) ,
new osci : : EffectParameter ( " Dash Offset " , " Offsets the location of the dashed lines. " , " dashOffset " , VERSION_HINT , 0.0 , 0.0 , 1.0 , 0.0001f , osci : : LfoType : : Sawtooth , 1.0f ) ,
2025-08-16 20:03:25 +00:00
new osci : : EffectParameter ( " Dash Width " , " Controls how much each dash unit is drawn. " , " dashWidth " , VERSION_HINT , 0.5 , 0.0 , 1.0 ) ,
2025-08-10 19:45:06 +00:00
}
) ;
eff - > setName ( " Dash " ) ;
eff - > setIcon ( BinaryData : : dash_svg ) ;
return eff ;
}
2025-08-16 20:03:25 +00:00
protected :
2025-08-14 07:39:51 +00:00
OscirenderAudioProcessor & audioProcessor ;
2025-08-16 20:03:25 +00:00
private :
2024-02-20 14:14:24 +00:00
const static int MAX_BUFFER = 192000 ;
2025-04-23 14:26:33 +00:00
std : : vector < osci : : Point > buffer = std : : vector < osci : : Point > ( MAX_BUFFER ) ;
2024-02-20 14:14:24 +00:00
int bufferIndex = 0 ;
2025-08-14 07:39:51 +00:00
double framePhase = 0.0 ; // [0, 1]
2024-08-26 17:09:29 +00:00
} ;
2025-08-16 20:03:25 +00:00
class TraceEffect : public DashedLineEffect {
public :
TraceEffect ( OscirenderAudioProcessor & p ) : DashedLineEffect ( p ) { }
std : : shared_ptr < osci : : Effect > build ( ) const override {
auto eff = std : : make_shared < osci : : Effect > (
std : : make_shared < TraceEffect > ( audioProcessor ) ,
std : : vector < osci : : EffectParameter * > {
new osci : : EffectParameter (
" Trace Start " ,
" Defines how far into the frame the drawing is started at. This has the effect of 'tracing' out the image from a single dot when animated. By default, we start drawing from the beginning of the frame, so this value is 0.0. " ,
" traceStart " ,
VERSION_HINT , 0.0 , 0.0 , 1.0 , 0.001
) ,
new osci : : EffectParameter (
" Trace Length " ,
" Defines how much of the frame is drawn per cycle. This has the effect of 'tracing' out the image from a single dot when animated. By default, we draw the whole frame, corresponding to a value of 1.0. " ,
" traceLength " ,
VERSION_HINT , 1.0 , 0.0 , 1.0 , 0.001 , osci : : LfoType : : Sawtooth
)
}
) ;
eff - > setName ( " Trace " ) ;
eff - > setIcon ( BinaryData : : trace_svg ) ;
return eff ;
}
} ;