2025-07-27 16:23:44 +00:00
# pragma once
# include <JuceHeader.h>
# include <cmath>
class MultiplexEffect : public osci : : EffectApplication {
public :
osci : : Point apply ( int index , osci : : Point input , const std : : vector < std : : atomic < double > > & values , double sampleRate ) override {
2025-08-01 14:49:18 +00:00
jassert ( values . size ( ) > = 6 ) ;
2025-07-27 16:23:44 +00:00
2025-08-10 19:45:06 +00:00
double gridX = values [ 0 ] . load ( ) + 0.0001 ;
double gridY = values [ 1 ] . load ( ) + 0.0001 ;
double gridZ = values [ 2 ] . load ( ) + 0.0001 ;
2025-07-27 16:23:44 +00:00
double interpolation = values [ 3 ] . load ( ) ;
double phase = values [ 4 ] . load ( ) ;
2025-08-01 14:49:18 +00:00
double gridDelay = values [ 5 ] . load ( ) ;
head + + ;
if ( head > = buffer . size ( ) ) {
head = 0 ;
}
buffer [ head ] = input ;
2025-07-27 16:23:44 +00:00
osci : : Point grid = osci : : Point ( gridX , gridY , gridZ ) ;
osci : : Point gridFloor = osci : : Point ( std : : floor ( gridX ) , std : : floor ( gridY ) , std : : floor ( gridZ ) ) ;
gridFloor . x = std : : max ( gridFloor . x , 1.0 ) ;
gridFloor . y = std : : max ( gridFloor . y , 1.0 ) ;
gridFloor . z = std : : max ( gridFloor . z , 1.0 ) ;
double totalPositions = gridFloor . x * gridFloor . y * gridFloor . z ;
double position = phase * totalPositions ;
2025-08-01 14:49:18 +00:00
double delayPosition = static_cast < int > ( position ) / totalPositions ;
int delayedIndex = head - static_cast < int > ( delayPosition * gridDelay * sampleRate ) ;
if ( delayedIndex < 0 ) {
delayedIndex + = buffer . size ( ) ;
}
osci : : Point delayedInput = buffer [ delayedIndex % buffer . size ( ) ] ;
2025-07-27 16:23:44 +00:00
osci : : Point nextGrid = gridFloor + 1.0 ;
2025-08-01 14:49:18 +00:00
osci : : Point current = multiplex ( delayedInput , position , gridFloor ) ;
osci : : Point next = multiplex ( delayedInput , position , nextGrid ) ;
2025-07-27 16:23:44 +00:00
// Calculate interpolation factors
osci : : Point gridDiff = grid - gridFloor ;
osci : : Point interpolationFactor = gridDiff * interpolation ;
return ( 1.0 - interpolationFactor ) * current + interpolationFactor * next ;
}
2025-08-10 19:45:06 +00:00
std : : shared_ptr < osci : : Effect > build ( ) const override {
auto eff = std : : make_shared < osci : : Effect > (
std : : make_shared < MultiplexEffect > ( ) ,
std : : vector < osci : : EffectParameter * > {
new osci : : EffectParameter ( " Multiplex X " , " Controls the horizontal grid size for the multiplex effect. " , " multiplexGridX " , VERSION_HINT , 2.0 , 1.0 , 8.0 ) ,
new osci : : EffectParameter ( " Multiplex Y " , " Controls the vertical grid size for the multiplex effect. " , " multiplexGridY " , VERSION_HINT , 2.0 , 1.0 , 8.0 ) ,
new osci : : EffectParameter ( " Multiplex Z " , " Controls the depth grid size for the multiplex effect. " , " multiplexGridZ " , VERSION_HINT , 1.0 , 1.0 , 8.0 ) ,
new osci : : EffectParameter ( " Multiplex Smooth " , " Controls the smoothness of transitions between grid sizes. " , " multiplexSmooth " , VERSION_HINT , 0.0 , 0.0 , 1.0 ) ,
new osci : : EffectParameter ( " Multiplex Phase " , " Controls the current phase of the multiplex grid animation. " , " gridPhase " , VERSION_HINT , 0.0 , 0.0 , 1.0 , 0.0001f , osci : : LfoType : : Sawtooth , 55.0f ) ,
new osci : : EffectParameter ( " Multiplex Delay " , " Controls the delay of the audio samples used in the multiplex effect. " , " gridDelay " , VERSION_HINT , 0.0 , 0.0 , 1.0 ) ,
}
) ;
eff - > setName ( " Multiplex " ) ;
eff - > setIcon ( BinaryData : : multiplex_svg ) ;
return eff ;
}
2025-07-27 16:23:44 +00:00
private :
osci : : Point multiplex ( osci : : Point point , double position , osci : : Point grid ) {
osci : : Point unit = 1.0 / grid ;
point * = unit ;
point . x = - point . x ;
point . y = - point . y ;
double xPosRaw = std : : floor ( position ) ;
double yPosRaw = std : : floor ( position / grid . x ) ;
double zPosRaw = std : : floor ( position / ( grid . x * grid . y ) ) ;
// Use fmod for positive modulo with doubles
double xPos = std : : fmod ( std : : fmod ( xPosRaw , grid . x ) + grid . x , grid . x ) ;
double yPos = std : : fmod ( std : : fmod ( yPosRaw , grid . y ) + grid . y , grid . y ) ;
double zPos = std : : fmod ( std : : fmod ( zPosRaw , grid . z ) + grid . z , grid . z ) ;
point . x - = ( grid . x - 1.0 ) / grid . x ;
point . y + = ( grid . y - 1.0 ) / grid . y ;
point . z + = ( grid . z - 1.0 ) / grid . z ;
point . x + = xPos * 2.0 * unit . x ;
point . y - = yPos * 2.0 * unit . y ;
point . z - = zPos * 2.0 * unit . z ;
return point ;
}
2025-08-01 14:49:18 +00:00
const static int MAX_DELAY = 192000 * 10 ;
std : : vector < osci : : Point > buffer = std : : vector < osci : : Point > ( MAX_DELAY ) ;
int head = 0 ;
2025-07-27 16:23:44 +00:00
} ;