2023-01-09 21:58:49 +00:00
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
This file contains the basic framework code for a JUCE plugin processor .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
# include "PluginProcessor.h"
# include "PluginEditor.h"
2023-01-15 17:01:27 +00:00
# include "parser/FileParser.h"
# include "parser/FrameProducer.h"
2023-03-28 13:33:56 +00:00
# include "audio/VectorCancellingEffect.h"
# include "audio/DistortEffect.h"
2023-03-28 14:52:51 +00:00
# include "audio/SmoothEffect.h"
2023-07-04 13:58:36 +00:00
# include "audio/BitCrushEffect.h"
# include "audio/BulgeEffect.h"
2023-07-18 16:25:09 +00:00
# include "audio/EffectParameter.h"
2023-01-09 21:58:49 +00:00
//==============================================================================
OscirenderAudioProcessor : : OscirenderAudioProcessor ( )
# ifndef JucePlugin_PreferredChannelConfigurations
: AudioProcessor ( BusesProperties ( )
# if ! JucePlugin_IsMidiEffect
# if ! JucePlugin_IsSynth
. withInput ( " Input " , juce : : AudioChannelSet : : stereo ( ) , true )
# endif
. withOutput ( " Output " , juce : : AudioChannelSet : : stereo ( ) , true )
# endif
)
# endif
2023-03-30 16:28:47 +00:00
{
2023-07-25 11:23:27 +00:00
// locking isn't necessary here because we are in the constructor
2023-03-26 12:58:31 +00:00
2023-07-18 16:25:09 +00:00
toggleableEffects . push_back ( std : : make_shared < Effect > (
2023-07-16 19:54:41 +00:00
std : : make_shared < BitCrushEffect > ( ) ,
2024-02-20 14:57:52 +00:00
new EffectParameter ( " Bit Crush " , " Limits the resolution of points drawn to the screen, making the object look pixelated, and making the audio sound more 'digital' and distorted. " , " bitCrush " , VERSION_HINT , 0.6 , 0.0 , 1.0 )
2023-07-16 19:54:41 +00:00
) ) ;
2023-07-18 16:25:09 +00:00
toggleableEffects . push_back ( std : : make_shared < Effect > (
2023-07-16 19:54:41 +00:00
std : : make_shared < BulgeEffect > ( ) ,
2024-02-20 14:57:52 +00:00
new EffectParameter ( " Bulge " , " Applies a bulge that makes the centre of the image larger, and squishes the edges of the image. This applies a distortion to the audio. " , " bulge " , VERSION_HINT , 0.5 , 0.0 , 1.0 )
2023-07-16 19:54:41 +00:00
) ) ;
2023-07-18 16:25:09 +00:00
toggleableEffects . push_back ( std : : make_shared < Effect > (
2023-07-16 19:54:41 +00:00
std : : make_shared < VectorCancellingEffect > ( ) ,
2024-02-20 14:57:52 +00:00
new EffectParameter ( " Vector Cancelling " , " Inverts the audio and image every few samples to 'cancel out' the audio, making the audio quiet, and distorting the image. " , " vectorCancelling " , VERSION_HINT , 0.1111111 , 0.0 , 1.0 )
2023-07-19 20:40:31 +00:00
) ) ;
2024-02-16 21:09:39 +00:00
toggleableEffects . push_back ( std : : make_shared < Effect > (
2024-10-23 11:44:31 +00:00
[ this ] ( int index , OsciPoint input , const std : : vector < std : : atomic < double > > & values , double sampleRate ) {
return input * OsciPoint ( values [ 0 ] , values [ 1 ] , values [ 2 ] ) ;
2024-02-16 21:09:39 +00:00
} , std : : vector < EffectParameter * > {
new EffectParameter ( " Scale X " , " Scales the object in the horizontal direction. " , " scaleX " , VERSION_HINT , 1.0 , - 5.0 , 5.0 ) ,
new EffectParameter ( " Scale Y " , " Scales the object in the vertical direction. " , " scaleY " , VERSION_HINT , 1.0 , - 5.0 , 5.0 ) ,
new EffectParameter ( " Scale Z " , " Scales the depth of the object. " , " scaleZ " , VERSION_HINT , 1.0 , - 5.0 , 5.0 ) ,
}
) ) ;
2023-07-19 20:40:31 +00:00
toggleableEffects . push_back ( std : : make_shared < Effect > (
2024-10-23 11:44:31 +00:00
[ this ] ( int index , OsciPoint input , const std : : vector < std : : atomic < double > > & values , double sampleRate ) {
2024-02-16 21:09:39 +00:00
int flip = index % 2 = = 0 ? 1 : - 1 ;
2024-10-23 11:44:31 +00:00
OsciPoint jitter = OsciPoint ( flip * values [ 0 ] , flip * values [ 1 ] , flip * values [ 2 ] ) ;
2024-02-16 21:09:39 +00:00
return input + jitter ;
} , std : : vector < EffectParameter * > {
new EffectParameter ( " Distort X " , " Distorts the image in the horizontal direction by jittering the audio sample being drawn. " , " distortX " , VERSION_HINT , 0.0 , 0.0 , 1.0 ) ,
new EffectParameter ( " Distort Y " , " Distorts the image in the vertical direction by jittering the audio sample being drawn. " , " distortY " , VERSION_HINT , 0.0 , 0.0 , 1.0 ) ,
2024-02-20 14:57:52 +00:00
new EffectParameter ( " Distort Z " , " Distorts the depth of the image by jittering the audio sample being drawn. " , " distortZ " , VERSION_HINT , 0.1 , 0.0 , 1.0 ) ,
2024-02-16 21:09:39 +00:00
}
2024-02-13 08:48:15 +00:00
) ) ;
2024-02-20 14:57:52 +00:00
auto rippleEffect = std : : make_shared < Effect > (
2024-10-23 11:44:31 +00:00
[ this ] ( int index , OsciPoint input , const std : : vector < std : : atomic < double > > & values , double sampleRate ) {
2024-02-13 08:48:15 +00:00
double phase = values [ 1 ] * std : : numbers : : pi ;
double distance = 100 * values [ 2 ] * ( input . x * input . x + input . y * input . y ) ;
input . z + = values [ 0 ] * std : : sin ( phase + distance ) ;
return input ;
} , std : : vector < EffectParameter * > {
2024-02-20 14:57:52 +00:00
new EffectParameter ( " Ripple Depth " , " Controls how large the ripples applied to the image are. " , " rippleDepth " , VERSION_HINT , 0.2 , 0.0 , 1.0 ) ,
2024-02-13 08:48:15 +00:00
new EffectParameter ( " Ripple Phase " , " Controls the position of the ripple. Animate this to see a moving ripple effect. " , " ripplePhase " , VERSION_HINT , 0.0 , - 1.0 , 1.0 ) ,
2024-02-20 14:57:52 +00:00
new EffectParameter ( " Ripple Amount " , " Controls how many ripples are applied to the image. " , " rippleAmount " , VERSION_HINT , 0.1 , 0.0 , 1.0 ) ,
2024-02-13 08:48:15 +00:00
}
2024-02-20 14:57:52 +00:00
) ;
rippleEffect - > getParameter ( " ripplePhase " ) - > lfo - > setUnnormalisedValueNotifyingHost ( ( int ) LfoType : : Sawtooth ) ;
toggleableEffects . push_back ( rippleEffect ) ;
auto rotateEffect = std : : make_shared < Effect > (
2024-10-23 11:44:31 +00:00
[ this ] ( int index , OsciPoint input , const std : : vector < std : : atomic < double > > & values , double sampleRate ) {
2024-02-12 22:33:06 +00:00
input . rotate ( values [ 0 ] * std : : numbers : : pi , values [ 1 ] * std : : numbers : : pi , values [ 2 ] * std : : numbers : : pi ) ;
return input ;
} , std : : vector < EffectParameter * > {
new EffectParameter ( " Rotate X " , " Controls the rotation of the object in the X axis. " , " rotateX " , VERSION_HINT , 0.0 , - 1.0 , 1.0 ) ,
new EffectParameter ( " Rotate Y " , " Controls the rotation of the object in the Y axis. " , " rotateY " , VERSION_HINT , 0.0 , - 1.0 , 1.0 ) ,
new EffectParameter ( " Rotate Z " , " Controls the rotation of the object in the Z axis. " , " rotateZ " , VERSION_HINT , 0.0 , - 1.0 , 1.0 ) ,
}
2024-02-20 14:57:52 +00:00
) ;
2024-02-24 09:39:05 +00:00
rotateEffect - > getParameter ( " rotateY " ) - > lfo - > setUnnormalisedValueNotifyingHost ( ( int ) LfoType : : Sawtooth ) ;
rotateEffect - > getParameter ( " rotateY " ) - > lfoRate - > setUnnormalisedValueNotifyingHost ( 0.2 ) ;
2024-02-20 14:57:52 +00:00
toggleableEffects . push_back ( rotateEffect ) ;
2023-07-18 16:25:09 +00:00
toggleableEffects . push_back ( std : : make_shared < Effect > (
2024-10-23 11:44:31 +00:00
[ this ] ( int index , OsciPoint input , const std : : vector < std : : atomic < double > > & values , double sampleRate ) {
return input + OsciPoint ( values [ 0 ] , values [ 1 ] , values [ 2 ] ) ;
2023-12-17 22:46:24 +00:00
} , std : : vector < EffectParameter * > {
2024-02-12 20:34:00 +00:00
new EffectParameter ( " Translate X " , " Moves the object horizontally. " , " translateX " , VERSION_HINT , 0.0 , - 1.0 , 1.0 ) ,
new EffectParameter ( " Translate Y " , " Moves the object vertically. " , " translateY " , VERSION_HINT , 0.0 , - 1.0 , 1.0 ) ,
new EffectParameter ( " Translate Z " , " Moves the object away from the camera. " , " translateZ " , VERSION_HINT , 0.0 , - 1.0 , 1.0 ) ,
2023-12-17 22:46:24 +00:00
}
2023-07-16 19:54:41 +00:00
) ) ;
2024-02-16 21:09:39 +00:00
toggleableEffects . push_back ( std : : make_shared < Effect > (
2024-10-23 11:44:31 +00:00
[ this ] ( int index , OsciPoint input , const std : : vector < std : : atomic < double > > & values , double sampleRate ) {
2024-02-16 21:09:39 +00:00
double length = 10 * values [ 0 ] * input . magnitude ( ) ;
double newX = input . x * std : : cos ( length ) - input . y * std : : sin ( length ) ;
double newY = input . x * std : : sin ( length ) + input . y * std : : cos ( length ) ;
2024-10-23 11:44:31 +00:00
return OsciPoint ( newX , newY , input . z ) ;
2024-02-16 21:09:39 +00:00
} , std : : vector < EffectParameter * > {
2024-02-20 14:57:52 +00:00
new EffectParameter ( " Swirl " , " Swirls the image in a spiral pattern. " , " swirl " , VERSION_HINT , 0.3 , - 1.0 , 1.0 ) ,
2024-02-16 21:09:39 +00:00
}
) ) ;
2023-07-18 16:25:09 +00:00
toggleableEffects . push_back ( std : : make_shared < Effect > (
2023-07-16 19:54:41 +00:00
std : : make_shared < SmoothEffect > ( ) ,
2024-02-20 14:57:52 +00:00
new EffectParameter ( " Smoothing " , " This works as a low-pass frequency filter that removes high frequencies, making the image look smoother, and audio sound less harsh. " , " smoothing " , VERSION_HINT , 0.75 , 0.0 , 1.0 )
2023-07-16 19:54:41 +00:00
) ) ;
2023-07-18 16:25:09 +00:00
toggleableEffects . push_back ( std : : make_shared < Effect > (
2023-07-16 19:54:41 +00:00
wobbleEffect ,
2024-02-20 14:57:52 +00:00
new EffectParameter ( " Wobble " , " Adds a sine wave of the prominent frequency in the audio currently playing. The sine wave's frequency is slightly offset to create a subtle 'wobble' in the image. Increasing the slider increases the strength of the wobble. " , " wobble " , VERSION_HINT , 0.3 , 0.0 , 1.0 )
2023-07-16 19:54:41 +00:00
) ) ;
2023-07-18 16:25:09 +00:00
toggleableEffects . push_back ( std : : make_shared < Effect > (
2023-07-11 12:32:52 +00:00
delayEffect ,
2023-12-17 22:46:24 +00:00
std : : vector < EffectParameter * > {
2024-02-20 14:57:52 +00:00
new EffectParameter ( " Delay Decay " , " Adds repetitions, delays, or echos to the audio. This slider controls the volume of the echo. " , " delayDecay " , VERSION_HINT , 0.4 , 0.0 , 1.0 ) ,
2023-12-17 22:46:24 +00:00
new EffectParameter ( " Delay Length " , " Controls the time in seconds between echos. " , " delayLength " , VERSION_HINT , 0.5 , 0.0 , 1.0 )
}
2023-07-11 12:32:52 +00:00
) ) ;
2024-02-20 14:14:24 +00:00
toggleableEffects . push_back ( std : : make_shared < Effect > (
dashedLineEffect ,
std : : vector < EffectParameter * > {
2024-02-20 14:57:52 +00:00
new EffectParameter ( " Dash Length " , " Controls the length of the dashed line. " , " dashLength " , VERSION_HINT , 0.2 , 0.0 , 1.0 ) ,
2024-02-20 14:14:24 +00:00
}
) ) ;
2024-02-26 22:11:37 +00:00
toggleableEffects . push_back ( custom ) ;
2023-07-18 16:25:09 +00:00
toggleableEffects . push_back ( traceMax ) ;
toggleableEffects . push_back ( traceMin ) ;
2023-07-28 12:55:44 +00:00
for ( int i = 0 ; i < toggleableEffects . size ( ) ; i + + ) {
auto effect = toggleableEffects [ i ] ;
2023-07-18 18:20:54 +00:00
effect - > markEnableable ( false ) ;
addParameter ( effect - > enabled ) ;
effect - > enabled - > setValueNotifyingHost ( false ) ;
2023-07-28 12:55:44 +00:00
effect - > setPrecedence ( i ) ;
2023-07-18 16:25:09 +00:00
}
2023-07-04 13:58:36 +00:00
2024-01-07 19:48:02 +00:00
permanentEffects . push_back ( perspective ) ;
2023-07-14 14:34:24 +00:00
permanentEffects . push_back ( frequencyEffect ) ;
permanentEffects . push_back ( volumeEffect ) ;
permanentEffects . push_back ( thresholdEffect ) ;
2024-06-02 14:01:39 +00:00
permanentEffects . push_back ( imageThreshold ) ;
permanentEffects . push_back ( imageStride ) ;
2024-09-12 22:09:54 +00:00
permanentEffects . push_back ( visualiserParameters . brightnessEffect ) ;
permanentEffects . push_back ( visualiserParameters . intensityEffect ) ;
permanentEffects . push_back ( visualiserParameters . persistenceEffect ) ;
permanentEffects . push_back ( visualiserParameters . hueEffect ) ;
2024-09-29 18:46:35 +00:00
permanentEffects . push_back ( visualiserParameters . saturationEffect ) ;
2024-09-29 19:36:16 +00:00
permanentEffects . push_back ( visualiserParameters . focusEffect ) ;
2024-11-06 20:15:09 +00:00
permanentEffects . push_back ( visualiserParameters . noiseEffect ) ;
2023-07-14 14:34:24 +00:00
2023-07-21 10:08:55 +00:00
for ( int i = 0 ; i < 26 ; i + + ) {
2023-07-04 13:58:36 +00:00
addLuaSlider ( ) ;
}
2023-07-17 19:09:13 +00:00
2023-07-25 11:23:27 +00:00
allEffects = toggleableEffects ;
allEffects . insert ( allEffects . end ( ) , permanentEffects . begin ( ) , permanentEffects . end ( ) ) ;
allEffects . insert ( allEffects . end ( ) , luaEffects . begin ( ) , luaEffects . end ( ) ) ;
2023-07-17 19:09:13 +00:00
2023-07-25 11:23:27 +00:00
for ( auto effect : allEffects ) {
2023-07-20 19:01:09 +00:00
for ( auto effectParameter : effect - > parameters ) {
auto parameters = effectParameter - > getParameters ( ) ;
for ( auto parameter : parameters ) {
addParameter ( parameter ) ;
}
2023-07-17 19:09:13 +00:00
}
}
2023-07-22 14:07:11 +00:00
2023-09-07 21:04:08 +00:00
booleanParameters . push_back ( midiEnabled ) ;
2023-12-14 21:26:40 +00:00
booleanParameters . push_back ( inputEnabled ) ;
2024-05-11 22:10:54 +00:00
booleanParameters . push_back ( animateFrames ) ;
2024-04-27 22:30:42 +00:00
booleanParameters . push_back ( animationSyncBPM ) ;
2024-05-11 22:10:54 +00:00
booleanParameters . push_back ( invertImage ) ;
2024-09-12 22:09:54 +00:00
booleanParameters . push_back ( visualiserParameters . graticuleEnabled ) ;
booleanParameters . push_back ( visualiserParameters . smudgesEnabled ) ;
booleanParameters . push_back ( visualiserParameters . upsamplingEnabled ) ;
booleanParameters . push_back ( visualiserParameters . legacyVisualiserEnabled ) ;
booleanParameters . push_back ( visualiserParameters . visualiserFullScreen ) ;
2023-07-25 13:09:21 +00:00
for ( auto parameter : booleanParameters ) {
addParameter ( parameter ) ;
}
2023-08-28 21:06:21 +00:00
2023-12-21 14:43:15 +00:00
floatParameters . push_back ( attackTime ) ;
floatParameters . push_back ( attackLevel ) ;
floatParameters . push_back ( attackShape ) ;
floatParameters . push_back ( decayTime ) ;
floatParameters . push_back ( decayShape ) ;
floatParameters . push_back ( sustainLevel ) ;
floatParameters . push_back ( releaseTime ) ;
floatParameters . push_back ( releaseShape ) ;
2024-04-21 18:07:15 +00:00
floatParameters . push_back ( animationRate ) ;
floatParameters . push_back ( animationOffset ) ;
2023-12-21 14:43:15 +00:00
for ( auto parameter : floatParameters ) {
addParameter ( parameter ) ;
}
2023-11-25 15:37:33 +00:00
2023-12-21 14:14:33 +00:00
for ( int i = 0 ; i < voices - > getValueUnnormalised ( ) ; i + + ) {
2023-08-28 21:06:21 +00:00
synth . addVoice ( new ShapeVoice ( * this ) ) ;
}
2023-12-21 14:14:33 +00:00
2023-12-21 14:43:15 +00:00
intParameters . push_back ( voices ) ;
for ( auto parameter : intParameters ) {
addParameter ( parameter ) ;
}
2023-12-21 14:14:33 +00:00
voices - > addListener ( this ) ;
2024-09-12 22:09:54 +00:00
for ( int i = 0 ; i < luaEffects . size ( ) ; i + + ) {
luaEffects [ i ] - > parameters [ 0 ] - > addListener ( this ) ;
}
2023-09-05 21:57:29 +00:00
synth . addSound ( defaultSound ) ;
2023-01-09 21:58:49 +00:00
}
2023-09-16 12:59:52 +00:00
OscirenderAudioProcessor : : ~ OscirenderAudioProcessor ( ) {
2024-09-12 22:09:54 +00:00
for ( int i = luaEffects . size ( ) - 1 ; i > = 0 ; i - - ) {
luaEffects [ i ] - > parameters [ 0 ] - > removeListener ( this ) ;
}
2023-12-21 14:14:33 +00:00
voices - > removeListener ( this ) ;
2023-09-16 12:59:52 +00:00
}
2023-01-09 21:58:49 +00:00
2023-07-13 19:11:24 +00:00
const juce : : String OscirenderAudioProcessor : : getName ( ) const {
2023-01-09 21:58:49 +00:00
return JucePlugin_Name ;
}
2024-01-28 19:58:28 +00:00
void OscirenderAudioProcessor : : setAudioThreadCallback ( std : : function < void ( const juce : : AudioBuffer < float > & ) > callback ) {
juce : : SpinLock : : ScopedLockType lock ( audioThreadCallbackLock ) ;
audioThreadCallback = callback ;
}
2023-07-13 19:11:24 +00:00
bool OscirenderAudioProcessor : : acceptsMidi ( ) const {
2023-01-09 21:58:49 +00:00
# if JucePlugin_WantsMidiInput
return true ;
# else
return false ;
# endif
}
2023-07-13 19:11:24 +00:00
bool OscirenderAudioProcessor : : producesMidi ( ) const {
2023-01-09 21:58:49 +00:00
# if JucePlugin_ProducesMidiOutput
return true ;
# else
return false ;
# endif
}
2023-07-13 19:11:24 +00:00
bool OscirenderAudioProcessor : : isMidiEffect ( ) const {
2023-01-09 21:58:49 +00:00
# if JucePlugin_IsMidiEffect
return true ;
# else
return false ;
# endif
}
2023-07-13 19:11:24 +00:00
double OscirenderAudioProcessor : : getTailLengthSeconds ( ) const {
2023-01-09 21:58:49 +00:00
return 0.0 ;
}
2023-07-13 19:11:24 +00:00
int OscirenderAudioProcessor : : getNumPrograms ( ) {
2023-01-09 21:58:49 +00:00
return 1 ; // NB: some hosts don't cope very well if you tell them there are 0 programs,
// so this should be at least 1, even if you're not really implementing programs.
}
2023-07-13 19:11:24 +00:00
int OscirenderAudioProcessor : : getCurrentProgram ( ) {
2023-01-09 21:58:49 +00:00
return 0 ;
}
2023-07-13 19:11:24 +00:00
void OscirenderAudioProcessor : : setCurrentProgram ( int index ) {
2023-01-09 21:58:49 +00:00
}
2023-07-13 19:11:24 +00:00
const juce : : String OscirenderAudioProcessor : : getProgramName ( int index ) {
2023-01-09 21:58:49 +00:00
return { } ;
}
2023-07-13 19:11:24 +00:00
void OscirenderAudioProcessor : : changeProgramName ( int index , const juce : : String & newName ) { }
2023-01-09 21:58:49 +00:00
2023-07-13 19:11:24 +00:00
void OscirenderAudioProcessor : : prepareToPlay ( double sampleRate , int samplesPerBlock ) {
2023-01-15 17:01:27 +00:00
currentSampleRate = sampleRate ;
2024-01-01 15:09:46 +00:00
volumeBuffer = std : : vector < double > ( VOLUME_BUFFER_SECONDS * sampleRate , 0 ) ;
2023-08-28 21:06:21 +00:00
synth . setCurrentPlaybackSampleRate ( sampleRate ) ;
2023-12-21 17:24:39 +00:00
retriggerMidi = true ;
2023-12-21 21:33:05 +00:00
for ( auto & effect : allEffects ) {
effect - > updateSampleRate ( currentSampleRate ) ;
}
2024-10-27 14:30:39 +00:00
threadManager . prepare ( sampleRate , samplesPerBlock ) ;
2023-01-09 21:58:49 +00:00
}
2023-07-13 19:11:24 +00:00
void OscirenderAudioProcessor : : releaseResources ( ) {
2023-01-09 21:58:49 +00:00
// When playback stops, you can use this as an opportunity to free up any
// spare memory, etc.
}
# ifndef JucePlugin_PreferredChannelConfigurations
bool OscirenderAudioProcessor : : isBusesLayoutSupported ( const BusesLayout & layouts ) const
{
# if JucePlugin_IsMidiEffect
juce : : ignoreUnused ( layouts ) ;
return true ;
# else
// This is the place where you check if the layout is supported.
// In this template code we only support mono or stereo.
// Some plugin hosts, such as certain GarageBand versions, will only
// load plugins that support stereo bus layouts.
if ( layouts . getMainOutputChannelSet ( ) ! = juce : : AudioChannelSet : : mono ( )
& & layouts . getMainOutputChannelSet ( ) ! = juce : : AudioChannelSet : : stereo ( ) )
return false ;
// This checks if the input layout matches the output layout
# if ! JucePlugin_IsSynth
if ( layouts . getMainOutputChannelSet ( ) ! = layouts . getMainInputChannelSet ( ) )
return false ;
# endif
return true ;
# endif
}
# endif
2023-07-05 14:17:17 +00:00
// effectsLock should be held when calling this
2023-07-04 13:58:36 +00:00
void OscirenderAudioProcessor : : addLuaSlider ( ) {
juce : : String sliderName = " " ;
2024-02-26 22:11:37 +00:00
int sliderIndex = luaEffects . size ( ) ;
int sliderNum = sliderIndex + 1 ;
2023-07-04 13:58:36 +00:00
while ( sliderNum > 0 ) {
int mod = ( sliderNum - 1 ) % 26 ;
sliderName = ( char ) ( mod + ' A ' ) + sliderName ;
sliderNum = ( sliderNum - mod ) / 26 ;
}
2023-07-16 19:54:41 +00:00
luaEffects . push_back ( std : : make_shared < Effect > (
2024-10-23 11:44:31 +00:00
[ this , sliderIndex ] ( int index , OsciPoint input , const std : : vector < std : : atomic < double > > & values , double sampleRate ) {
2024-09-12 22:09:54 +00:00
luaValues [ sliderIndex ] . store ( values [ 0 ] ) ;
2024-02-26 22:11:37 +00:00
return input ;
} , new EffectParameter (
2023-12-17 22:46:24 +00:00
" Lua Slider " + sliderName ,
2023-12-21 21:33:05 +00:00
" Controls the value of the Lua variable called slider_ " + sliderName . toLowerCase ( ) + " . " ,
2023-12-17 22:46:24 +00:00
" lua " + sliderName ,
VERSION_HINT , 0.0 , 0.0 , 1.0 , 0.001 , false
)
2023-07-16 19:54:41 +00:00
) ) ;
2023-07-04 13:58:36 +00:00
}
2023-12-20 17:13:38 +00:00
void OscirenderAudioProcessor : : addErrorListener ( ErrorListener * listener ) {
juce : : SpinLock : : ScopedLockType lock ( errorListenersLock ) ;
errorListeners . push_back ( listener ) ;
}
void OscirenderAudioProcessor : : removeErrorListener ( ErrorListener * listener ) {
juce : : SpinLock : : ScopedLockType lock ( errorListenersLock ) ;
errorListeners . erase ( std : : remove ( errorListeners . begin ( ) , errorListeners . end ( ) , listener ) , errorListeners . end ( ) ) ;
}
2023-07-25 13:09:21 +00:00
// effectsLock should be held when calling this
std : : shared_ptr < Effect > OscirenderAudioProcessor : : getEffect ( juce : : String id ) {
for ( auto & effect : allEffects ) {
if ( effect - > getId ( ) = = id ) {
return effect ;
}
}
return nullptr ;
}
// effectsLock should be held when calling this
BooleanParameter * OscirenderAudioProcessor : : getBooleanParameter ( juce : : String id ) {
for ( auto & parameter : booleanParameters ) {
if ( parameter - > paramID = = id ) {
return parameter ;
}
}
return nullptr ;
}
2023-12-21 14:43:15 +00:00
// effectsLock should be held when calling this
FloatParameter * OscirenderAudioProcessor : : getFloatParameter ( juce : : String id ) {
for ( auto & parameter : floatParameters ) {
if ( parameter - > paramID = = id ) {
return parameter ;
}
}
return nullptr ;
}
// effectsLock should be held when calling this
IntParameter * OscirenderAudioProcessor : : getIntParameter ( juce : : String id ) {
for ( auto & parameter : intParameters ) {
if ( parameter - > paramID = = id ) {
return parameter ;
}
}
return nullptr ;
}
2023-07-05 14:17:17 +00:00
// effectsLock MUST be held when calling this
2023-03-28 15:21:18 +00:00
void OscirenderAudioProcessor : : updateEffectPrecedence ( ) {
2023-07-05 16:57:41 +00:00
auto sortFunc = [ ] ( std : : shared_ptr < Effect > a , std : : shared_ptr < Effect > b ) {
return a - > getPrecedence ( ) < b - > getPrecedence ( ) ;
} ;
2023-07-18 16:25:09 +00:00
std : : sort ( toggleableEffects . begin ( ) , toggleableEffects . end ( ) , sortFunc ) ;
2023-03-26 12:58:31 +00:00
}
2023-07-05 14:17:17 +00:00
// parsersLock AND effectsLock must be locked before calling this function
2023-03-30 16:28:47 +00:00
void OscirenderAudioProcessor : : updateFileBlock ( int index , std : : shared_ptr < juce : : MemoryBlock > block ) {
2023-07-04 19:47:54 +00:00
if ( index < 0 | | index > = fileBlocks . size ( ) ) {
return ;
}
2023-03-30 16:28:47 +00:00
fileBlocks [ index ] = block ;
openFile ( index ) ;
}
2023-07-05 14:17:17 +00:00
// parsersLock AND effectsLock must be locked before calling this function
2023-03-30 16:28:47 +00:00
void OscirenderAudioProcessor : : addFile ( juce : : File file ) {
fileBlocks . push_back ( std : : make_shared < juce : : MemoryBlock > ( ) ) ;
2023-07-05 21:45:51 +00:00
fileNames . push_back ( file . getFileName ( ) ) ;
2023-12-20 18:43:03 +00:00
fileIds . push_back ( currentFileId + + ) ;
2024-05-11 22:10:54 +00:00
parsers . push_back ( std : : make_shared < FileParser > ( * this , errorCallback ) ) ;
sounds . push_back ( new ShapeSound ( * this , parsers . back ( ) ) ) ;
2023-03-30 16:28:47 +00:00
file . createInputStream ( ) - > readIntoMemoryBlock ( * fileBlocks . back ( ) ) ;
openFile ( fileBlocks . size ( ) - 1 ) ;
}
2023-07-05 21:45:51 +00:00
// parsersLock AND effectsLock must be locked before calling this function
void OscirenderAudioProcessor : : addFile ( juce : : String fileName , const char * data , const int size ) {
fileBlocks . push_back ( std : : make_shared < juce : : MemoryBlock > ( ) ) ;
fileNames . push_back ( fileName ) ;
2023-12-20 18:43:03 +00:00
fileIds . push_back ( currentFileId + + ) ;
2024-05-11 22:10:54 +00:00
parsers . push_back ( std : : make_shared < FileParser > ( * this , errorCallback ) ) ;
sounds . push_back ( new ShapeSound ( * this , parsers . back ( ) ) ) ;
2023-07-05 21:45:51 +00:00
fileBlocks . back ( ) - > append ( data , size ) ;
openFile ( fileBlocks . size ( ) - 1 ) ;
}
2023-07-25 13:09:21 +00:00
// parsersLock AND effectsLock must be locked before calling this function
void OscirenderAudioProcessor : : addFile ( juce : : String fileName , std : : shared_ptr < juce : : MemoryBlock > data ) {
fileBlocks . push_back ( data ) ;
fileNames . push_back ( fileName ) ;
2023-12-20 18:43:03 +00:00
fileIds . push_back ( currentFileId + + ) ;
2024-05-11 22:10:54 +00:00
parsers . push_back ( std : : make_shared < FileParser > ( * this , errorCallback ) ) ;
sounds . push_back ( new ShapeSound ( * this , parsers . back ( ) ) ) ;
2023-07-25 13:09:21 +00:00
openFile ( fileBlocks . size ( ) - 1 ) ;
}
2023-07-05 14:17:17 +00:00
// parsersLock AND effectsLock must be locked before calling this function
2023-03-30 16:28:47 +00:00
void OscirenderAudioProcessor : : removeFile ( int index ) {
2023-03-30 20:09:53 +00:00
if ( index < 0 | | index > = fileBlocks . size ( ) ) {
return ;
}
2023-03-30 16:28:47 +00:00
fileBlocks . erase ( fileBlocks . begin ( ) + index ) ;
2023-07-05 21:45:51 +00:00
fileNames . erase ( fileNames . begin ( ) + index ) ;
2023-12-20 18:43:03 +00:00
fileIds . erase ( fileIds . begin ( ) + index ) ;
2023-07-25 13:09:21 +00:00
parsers . erase ( parsers . begin ( ) + index ) ;
2023-08-28 21:06:21 +00:00
sounds . erase ( sounds . begin ( ) + index ) ;
2023-07-25 13:09:21 +00:00
auto newFileIndex = index ;
if ( newFileIndex > = fileBlocks . size ( ) ) {
newFileIndex = fileBlocks . size ( ) - 1 ;
}
changeCurrentFile ( newFileIndex ) ;
2023-03-30 16:28:47 +00:00
}
int OscirenderAudioProcessor : : numFiles ( ) {
return fileBlocks . size ( ) ;
}
2023-07-05 14:17:17 +00:00
// used for opening NEW files. Should be the default way of opening files as
// it will reparse any existing files, so it is safer.
// parsersLock AND effectsLock must be locked before calling this function
2023-03-30 16:28:47 +00:00
void OscirenderAudioProcessor : : openFile ( int index ) {
2023-03-30 20:09:53 +00:00
if ( index < 0 | | index > = fileBlocks . size ( ) ) {
return ;
}
2023-08-27 18:33:42 +00:00
juce : : SpinLock : : ScopedLockType lock ( fontLock ) ;
2023-12-20 18:43:03 +00:00
parsers [ index ] - > parse ( juce : : String ( fileIds [ index ] ) , fileNames [ index ] . fromLastOccurrenceOf ( " . " , true , false ) , std : : make_unique < juce : : MemoryInputStream > ( * fileBlocks [ index ] , false ) , font ) ;
2023-07-05 14:17:17 +00:00
changeCurrentFile ( index ) ;
2023-03-30 20:09:53 +00:00
}
2023-07-05 14:17:17 +00:00
// used ONLY for changing the current file to an EXISTING file.
// much faster than openFile(int index) because it doesn't reparse any files.
// parsersLock AND effectsLock must be locked before calling this function
2023-03-30 20:09:53 +00:00
void OscirenderAudioProcessor : : changeCurrentFile ( int index ) {
2023-07-02 12:09:24 +00:00
if ( index = = - 1 ) {
currentFile = - 1 ;
2023-09-07 21:04:08 +00:00
changeSound ( defaultSound ) ;
2023-07-02 12:09:24 +00:00
}
2023-03-30 20:09:53 +00:00
if ( index < 0 | | index > = fileBlocks . size ( ) ) {
return ;
}
currentFile = index ;
2023-09-11 20:28:34 +00:00
changeSound ( sounds [ index ] ) ;
2023-03-30 16:28:47 +00:00
}
2023-09-07 21:04:08 +00:00
void OscirenderAudioProcessor : : changeSound ( ShapeSound : : Ptr sound ) {
2023-09-10 16:43:37 +00:00
if ( ! objectServerRendering | | sound = = objectServerSound ) {
synth . clearSounds ( ) ;
synth . addSound ( sound ) ;
for ( int i = 0 ; i < synth . getNumVoices ( ) ; i + + ) {
auto voice = dynamic_cast < ShapeVoice * > ( synth . getVoice ( i ) ) ;
voice - > updateSound ( sound . get ( ) ) ;
}
2023-09-07 21:04:08 +00:00
}
}
2024-03-13 23:07:40 +00:00
void OscirenderAudioProcessor : : notifyErrorListeners ( int lineNumber , juce : : String id , juce : : String error ) {
2023-12-20 17:13:38 +00:00
juce : : SpinLock : : ScopedLockType lock ( errorListenersLock ) ;
for ( auto listener : errorListeners ) {
2024-03-13 23:07:40 +00:00
if ( listener - > getId ( ) = = id ) {
2023-12-20 17:47:58 +00:00
listener - > onError ( lineNumber , error ) ;
}
2023-12-20 17:13:38 +00:00
}
}
2023-03-30 20:09:53 +00:00
int OscirenderAudioProcessor : : getCurrentFileIndex ( ) {
2023-03-30 16:28:47 +00:00
return currentFile ;
}
2023-07-04 13:58:36 +00:00
std : : shared_ptr < FileParser > OscirenderAudioProcessor : : getCurrentFileParser ( ) {
return parsers [ currentFile ] ;
}
2023-07-05 21:45:51 +00:00
juce : : String OscirenderAudioProcessor : : getCurrentFileName ( ) {
2023-09-10 16:43:37 +00:00
if ( objectServerRendering | | currentFile = = - 1 ) {
2023-07-05 21:45:51 +00:00
return " " ;
} else {
return fileNames [ currentFile ] ;
}
2023-03-30 20:09:53 +00:00
}
2023-07-05 21:45:51 +00:00
juce : : String OscirenderAudioProcessor : : getFileName ( int index ) {
return fileNames [ index ] ;
2023-07-02 12:09:24 +00:00
}
2023-12-20 18:43:03 +00:00
juce : : String OscirenderAudioProcessor : : getFileId ( int index ) {
return juce : : String ( fileIds [ index ] ) ;
}
2023-03-30 16:28:47 +00:00
std : : shared_ptr < juce : : MemoryBlock > OscirenderAudioProcessor : : getFileBlock ( int index ) {
return fileBlocks [ index ] ;
}
2023-09-10 16:43:37 +00:00
void OscirenderAudioProcessor : : setObjectServerRendering ( bool enabled ) {
{
juce : : SpinLock : : ScopedLockType lock1 ( parsersLock ) ;
objectServerRendering = enabled ;
if ( enabled ) {
changeSound ( objectServerSound ) ;
} else {
changeCurrentFile ( currentFile ) ;
}
}
{
juce : : MessageManagerLock lock ;
fileChangeBroadcaster . sendChangeMessage ( ) ;
}
}
2023-08-28 21:06:21 +00:00
void OscirenderAudioProcessor : : processBlock ( juce : : AudioBuffer < float > & buffer , juce : : MidiBuffer & midiMessages ) {
2023-01-09 21:58:49 +00:00
juce : : ScopedNoDenormals noDenormals ;
2024-04-15 19:54:25 +00:00
// Audio info variables
int totalNumInputChannels = getTotalNumInputChannels ( ) ;
int totalNumOutputChannels = getTotalNumOutputChannels ( ) ;
double sampleRate = getSampleRate ( ) ;
2024-04-27 22:34:48 +00:00
// MIDI transport info variables (defaults to 60bpm, 4/4 time signature at zero seconds and not playing)
double bpm = 60 ;
2024-04-15 19:54:25 +00:00
double playTimeSeconds = 0 ;
bool isPlaying = false ;
2024-04-26 18:27:28 +00:00
juce : : AudioPlayHead : : TimeSignature timeSig ;
2024-04-15 19:54:25 +00:00
// Get MIDI transport info
playHead = this - > getPlayHead ( ) ;
2024-04-16 16:14:30 +00:00
if ( playHead ! = nullptr ) {
2024-04-26 18:27:28 +00:00
auto pos = playHead - > getPosition ( ) ;
if ( pos . hasValue ( ) ) {
juce : : AudioPlayHead : : PositionInfo pi = * pos ;
bpm = pi . getBpm ( ) . orFallback ( bpm ) ;
playTimeSeconds = pi . getTimeInSeconds ( ) . orFallback ( playTimeSeconds ) ;
isPlaying = pi . getIsPlaying ( ) ;
timeSig = pi . getTimeSignature ( ) . orFallback ( timeSig ) ;
2024-04-16 16:14:30 +00:00
}
2024-04-15 19:54:25 +00:00
}
// Calculated number of beats
2024-04-26 18:27:28 +00:00
// TODO: To make this more resilient to changing BPMs, we should change how this is calculated
// or use another property of the AudioPlayHead::PositionInfo
2024-04-15 19:54:25 +00:00
double playTimeBeats = bpm * playTimeSeconds / 60 ;
// Calculated time per sample in seconds and beats
double sTimeSec = 1.f / sampleRate ;
double sTimeBeats = bpm * sTimeSec / 60 ;
2023-01-09 21:58:49 +00:00
2023-12-18 13:41:56 +00:00
// merge keyboard state and midi messages
keyboardState . processNextMidiBuffer ( midiMessages , 0 , buffer . getNumSamples ( ) , true ) ;
2023-12-14 21:26:40 +00:00
bool usingInput = inputEnabled - > getBoolValue ( ) ;
2023-09-07 21:04:08 +00:00
bool usingMidi = midiEnabled - > getBoolValue ( ) ;
if ( ! usingMidi ) {
midiMessages . clear ( ) ;
}
// if midi enabled has changed state
if ( prevMidiEnabled ! = usingMidi ) {
for ( int i = 1 ; i < = 16 ; i + + ) {
midiMessages . addEvent ( juce : : MidiMessage : : allNotesOff ( i ) , i ) ;
}
}
2023-12-21 17:24:39 +00:00
// if midi has just been disabled or we need to retrigger
if ( ! usingMidi & & ( retriggerMidi | | prevMidiEnabled ) ) {
2023-09-07 21:04:08 +00:00
midiMessages . addEvent ( juce : : MidiMessage : : noteOn ( 1 , 60 , 1.0f ) , 17 ) ;
2023-12-21 17:24:39 +00:00
retriggerMidi = false ;
2023-09-07 21:04:08 +00:00
}
prevMidiEnabled = usingMidi ;
2023-09-09 14:32:03 +00:00
const double EPSILON = 0.00001 ;
2023-12-14 21:26:40 +00:00
2024-01-01 16:21:10 +00:00
juce : : AudioBuffer < float > inputBuffer = juce : : AudioBuffer < float > ( totalNumInputChannels , buffer . getNumSamples ( ) ) ;
for ( auto channel = 0 ; channel < totalNumInputChannels ; channel + + ) {
inputBuffer . copyFrom ( channel , 0 , buffer , channel , 0 , buffer . getNumSamples ( ) ) ;
2024-01-01 15:09:46 +00:00
}
2024-01-07 19:48:02 +00:00
juce : : AudioBuffer < float > outputBuffer3d = juce : : AudioBuffer < float > ( 3 , buffer . getNumSamples ( ) ) ;
outputBuffer3d . clear ( ) ;
2023-12-14 21:26:40 +00:00
if ( usingInput & & totalNumInputChannels > = 2 ) {
2024-01-07 19:48:02 +00:00
for ( auto channel = 0 ; channel < juce : : jmin ( 2 , totalNumInputChannels ) ; channel + + ) {
outputBuffer3d . copyFrom ( channel , 0 , inputBuffer , channel , 0 , buffer . getNumSamples ( ) ) ;
}
2023-12-14 21:26:40 +00:00
// handle all midi messages
auto midiIterator = midiMessages . cbegin ( ) ;
std : : for_each ( midiIterator ,
midiMessages . cend ( ) ,
[ & ] ( const juce : : MidiMessageMetadata & meta ) { synth . publicHandleMidiEvent ( meta . getMessage ( ) ) ; }
) ;
} else {
2023-12-21 19:58:00 +00:00
juce : : SpinLock : : ScopedLockType lock1 ( parsersLock ) ;
juce : : SpinLock : : ScopedLockType lock2 ( effectsLock ) ;
2024-01-07 19:48:02 +00:00
synth . renderNextBlock ( outputBuffer3d , midiMessages , 0 , buffer . getNumSamples ( ) ) ;
2024-02-22 13:10:24 +00:00
for ( int i = 0 ; i < synth . getNumVoices ( ) ; i + + ) {
auto voice = dynamic_cast < ShapeVoice * > ( synth . getVoice ( i ) ) ;
if ( voice - > isVoiceActive ( ) ) {
customEffect - > frequency = voice - > getFrequency ( ) ;
break ;
}
}
2023-09-07 21:04:08 +00:00
}
2023-12-14 21:26:40 +00:00
2023-09-05 21:57:29 +00:00
midiMessages . clear ( ) ;
2023-01-15 17:01:27 +00:00
auto * channelData = buffer . getArrayOfWritePointers ( ) ;
2024-04-15 19:54:25 +00:00
2023-01-15 17:01:27 +00:00
2024-04-17 15:00:59 +00:00
for ( int sample = 0 ; sample < buffer . getNumSamples ( ) ; + + sample ) {
2024-05-11 22:10:54 +00:00
// Update frame animation
if ( animateFrames - > getValue ( ) ) {
2024-04-27 22:30:42 +00:00
if ( animationSyncBPM - > getValue ( ) ) {
2024-04-21 18:07:15 +00:00
animationTime = playTimeBeats ;
2024-05-11 22:10:54 +00:00
} else {
2024-04-21 18:07:15 +00:00
animationTime = playTimeSeconds ;
}
2024-05-11 22:10:54 +00:00
2024-08-26 17:09:29 +00:00
juce : : SpinLock : : ScopedLockType lock1 ( parsersLock ) ;
juce : : SpinLock : : ScopedLockType lock2 ( effectsLock ) ;
2024-05-11 22:10:54 +00:00
if ( currentFile > = 0 & & sounds [ currentFile ] - > parser - > isAnimatable ) {
2024-04-21 18:07:15 +00:00
int animFrame = ( int ) ( animationTime * animationRate - > getValueUnnormalised ( ) + animationOffset - > getValueUnnormalised ( ) ) ;
2024-04-26 19:34:50 +00:00
auto lineArt = sounds [ currentFile ] - > parser - > getLineArt ( ) ;
2024-05-11 22:10:54 +00:00
auto img = sounds [ currentFile ] - > parser - > getImg ( ) ;
2024-04-26 19:34:50 +00:00
if ( lineArt ! = nullptr ) {
lineArt - > setFrame ( animFrame ) ;
2024-05-11 22:10:54 +00:00
} else if ( img ! = nullptr ) {
img - > setFrame ( animFrame ) ;
2024-04-26 19:34:50 +00:00
}
2024-04-17 15:00:59 +00:00
}
}
2024-04-15 19:54:25 +00:00
2024-01-01 16:21:10 +00:00
auto left = 0.0 ;
auto right = 0.0 ;
if ( totalNumInputChannels > = 2 ) {
left = inputBuffer . getSample ( 0 , sample ) ;
right = inputBuffer . getSample ( 1 , sample ) ;
} else if ( totalNumInputChannels = = 1 ) {
left = inputBuffer . getSample ( 0 , sample ) ;
right = inputBuffer . getSample ( 0 , sample ) ;
}
// update volume using a moving average
int oldestBufferIndex = ( volumeBufferIndex + 1 ) % volumeBuffer . size ( ) ;
squaredVolume - = volumeBuffer [ oldestBufferIndex ] / volumeBuffer . size ( ) ;
volumeBufferIndex = oldestBufferIndex ;
volumeBuffer [ volumeBufferIndex ] = ( left * left + right * right ) / 2 ;
squaredVolume + = volumeBuffer [ volumeBufferIndex ] / volumeBuffer . size ( ) ;
currentVolume = std : : sqrt ( squaredVolume ) ;
2024-01-01 17:30:06 +00:00
currentVolume = juce : : jlimit ( 0.0 , 1.0 , currentVolume ) ;
2024-01-01 16:21:10 +00:00
2024-10-23 11:44:31 +00:00
OsciPoint channels = { outputBuffer3d . getSample ( 0 , sample ) , outputBuffer3d . getSample ( 1 , sample ) , outputBuffer3d . getSample ( 2 , sample ) } ;
2023-01-15 17:01:27 +00:00
2023-07-05 14:17:17 +00:00
{
2023-07-22 17:42:30 +00:00
juce : : SpinLock : : ScopedLockType lock1 ( parsersLock ) ;
juce : : SpinLock : : ScopedLockType lock2 ( effectsLock ) ;
2023-09-09 14:32:03 +00:00
if ( volume > EPSILON ) {
for ( auto & effect : toggleableEffects ) {
if ( effect - > enabled - > getValue ( ) ) {
2024-01-01 15:09:46 +00:00
channels = effect - > apply ( sample , channels , currentVolume ) ;
2023-09-09 14:32:03 +00:00
}
2023-07-18 16:25:09 +00:00
}
2023-07-05 14:17:17 +00:00
}
2023-07-14 14:34:24 +00:00
for ( auto & effect : permanentEffects ) {
2024-01-01 15:09:46 +00:00
channels = effect - > apply ( sample , channels , currentVolume ) ;
2023-07-14 14:34:24 +00:00
}
2024-02-26 22:11:37 +00:00
auto lua = currentFile > = 0 ? sounds [ currentFile ] - > parser - > getLua ( ) : nullptr ;
if ( lua ! = nullptr | | custom - > enabled - > getBoolValue ( ) ) {
for ( auto & effect : luaEffects ) {
effect - > apply ( sample , channels , currentVolume ) ;
}
}
2023-07-05 14:17:17 +00:00
}
2023-03-25 20:24:10 +00:00
2023-08-28 21:06:21 +00:00
double x = channels . x ;
double y = channels . y ;
2023-07-05 11:02:28 +00:00
2023-07-10 16:42:22 +00:00
x * = volume ;
y * = volume ;
// clip
2023-07-10 21:00:36 +00:00
x = juce : : jmax ( - threshold , juce : : jmin ( threshold . load ( ) , x ) ) ;
y = juce : : jmax ( - threshold , juce : : jmin ( threshold . load ( ) , y ) ) ;
2023-07-01 14:29:53 +00:00
2023-01-15 17:01:27 +00:00
if ( totalNumOutputChannels > = 2 ) {
channelData [ 0 ] [ sample ] = x ;
channelData [ 1 ] [ sample ] = y ;
} else if ( totalNumOutputChannels = = 1 ) {
channelData [ 0 ] [ sample ] = x ;
}
2024-10-27 14:30:39 +00:00
threadManager . write ( OsciPoint ( x , y , 1 ) ) ;
2024-04-15 19:54:25 +00:00
if ( isPlaying ) {
playTimeSeconds + = sTimeSec ;
playTimeBeats + = sTimeBeats ;
}
2023-01-15 17:01:27 +00:00
}
2024-01-28 19:58:28 +00:00
// used for any callback that must guarantee all audio is recieved (e.g. when recording to a file)
juce : : SpinLock : : ScopedLockType lock ( audioThreadCallbackLock ) ;
2024-01-30 20:14:07 +00:00
if ( audioThreadCallback ! = nullptr ) {
audioThreadCallback ( buffer ) ;
}
2023-01-09 21:58:49 +00:00
}
//==============================================================================
2023-07-25 11:23:27 +00:00
bool OscirenderAudioProcessor : : hasEditor ( ) const {
2023-01-09 21:58:49 +00:00
return true ; // (change this to false if you choose to not supply an editor)
}
2023-07-25 11:23:27 +00:00
juce : : AudioProcessorEditor * OscirenderAudioProcessor : : createEditor ( ) {
2023-07-28 20:10:21 +00:00
auto editor = new OscirenderAudioProcessorEditor ( * this ) ;
return editor ;
2023-01-09 21:58:49 +00:00
}
//==============================================================================
2023-07-25 11:23:27 +00:00
void OscirenderAudioProcessor : : getStateInformation ( juce : : MemoryBlock & destData ) {
juce : : SpinLock : : ScopedLockType lock1 ( parsersLock ) ;
juce : : SpinLock : : ScopedLockType lock2 ( effectsLock ) ;
std : : unique_ptr < juce : : XmlElement > xml = std : : make_unique < juce : : XmlElement > ( " project " ) ;
xml - > setAttribute ( " version " , ProjectInfo : : versionString ) ;
auto effectsXml = xml - > createNewChildElement ( " effects " ) ;
for ( auto effect : allEffects ) {
effect - > save ( effectsXml - > createNewChildElement ( " effect " ) ) ;
}
2023-07-25 13:09:21 +00:00
auto booleanParametersXml = xml - > createNewChildElement ( " booleanParameters " ) ;
for ( auto parameter : booleanParameters ) {
auto parameterXml = booleanParametersXml - > createNewChildElement ( " parameter " ) ;
parameter - > save ( parameterXml ) ;
}
2023-12-21 14:43:15 +00:00
auto floatParametersXml = xml - > createNewChildElement ( " floatParameters " ) ;
for ( auto parameter : floatParameters ) {
auto parameterXml = floatParametersXml - > createNewChildElement ( " parameter " ) ;
parameter - > save ( parameterXml ) ;
}
auto intParametersXml = xml - > createNewChildElement ( " intParameters " ) ;
for ( auto parameter : intParameters ) {
auto parameterXml = intParametersXml - > createNewChildElement ( " parameter " ) ;
parameter - > save ( parameterXml ) ;
}
2024-01-07 19:48:02 +00:00
auto customFunction = xml - > createNewChildElement ( " customFunction " ) ;
customFunction - > addTextElement ( juce : : Base64 : : toBase64 ( customEffect - > getCode ( ) ) ) ;
2023-08-27 21:01:37 +00:00
auto fontXml = xml - > createNewChildElement ( " font " ) ;
fontXml - > setAttribute ( " family " , font . getTypefaceName ( ) ) ;
fontXml - > setAttribute ( " bold " , font . isBold ( ) ) ;
fontXml - > setAttribute ( " italic " , font . isItalic ( ) ) ;
2023-07-25 11:23:27 +00:00
auto filesXml = xml - > createNewChildElement ( " files " ) ;
for ( int i = 0 ; i < fileBlocks . size ( ) ; i + + ) {
auto fileXml = filesXml - > createNewChildElement ( " file " ) ;
fileXml - > setAttribute ( " name " , fileNames [ i ] ) ;
2024-06-01 20:50:56 +00:00
auto base64 = fileBlocks [ i ] - > toBase64Encoding ( ) ;
fileXml - > addTextElement ( base64 ) ;
2023-07-25 11:23:27 +00:00
}
xml - > setAttribute ( " currentFile " , currentFile ) ;
2024-06-01 21:23:00 +00:00
auto visualiserXml = xml - > createNewChildElement ( " visualiser " ) ;
2024-09-12 22:09:54 +00:00
visualiserXml - > setAttribute ( " roughness " , visualiserParameters . roughness ) ;
visualiserXml - > setAttribute ( " intensity " , visualiserParameters . intensity ) ;
2023-08-27 21:01:37 +00:00
2023-07-25 11:23:27 +00:00
copyXmlToBinary ( * xml , destData ) ;
2023-01-09 21:58:49 +00:00
}
2023-07-25 11:23:27 +00:00
void OscirenderAudioProcessor : : setStateInformation ( const void * data , int sizeInBytes ) {
2023-07-28 12:55:44 +00:00
std : : unique_ptr < juce : : XmlElement > xml ;
2023-07-25 13:09:21 +00:00
2023-07-28 12:55:44 +00:00
const uint32_t magicXmlNumber = 0x21324356 ;
if ( sizeInBytes > 8 & & juce : : ByteOrder : : littleEndianInt ( data ) = = magicXmlNumber ) {
// this is a binary xml format
xml = getXmlFromBinary ( data , sizeInBytes ) ;
} else {
// this is a text xml format
xml = juce : : XmlDocument : : parse ( juce : : String ( ( const char * ) data , sizeInBytes ) ) ;
}
2023-07-25 13:09:21 +00:00
if ( xml . get ( ) ! = nullptr & & xml - > hasTagName ( " project " ) ) {
2023-07-28 12:55:44 +00:00
auto versionXml = xml - > getChildByName ( " version " ) ;
if ( versionXml ! = nullptr & & versionXml - > getAllSubText ( ) . startsWith ( " v1. " ) ) {
openLegacyProject ( xml . get ( ) ) ;
return ;
}
2024-06-11 19:00:39 +00:00
auto version = xml - > hasAttribute ( " version " ) ? xml - > getStringAttribute ( " version " ) : " 2.0.0 " ;
2023-07-28 12:55:44 +00:00
juce : : SpinLock : : ScopedLockType lock1 ( parsersLock ) ;
juce : : SpinLock : : ScopedLockType lock2 ( effectsLock ) ;
2023-07-25 13:09:21 +00:00
auto effectsXml = xml - > getChildByName ( " effects " ) ;
if ( effectsXml ! = nullptr ) {
for ( auto effectXml : effectsXml - > getChildIterator ( ) ) {
auto effect = getEffect ( effectXml - > getStringAttribute ( " id " ) ) ;
if ( effect ! = nullptr ) {
effect - > load ( effectXml ) ;
}
}
}
updateEffectPrecedence ( ) ;
auto booleanParametersXml = xml - > getChildByName ( " booleanParameters " ) ;
if ( booleanParametersXml ! = nullptr ) {
for ( auto parameterXml : booleanParametersXml - > getChildIterator ( ) ) {
auto parameter = getBooleanParameter ( parameterXml - > getStringAttribute ( " id " ) ) ;
if ( parameter ! = nullptr ) {
parameter - > load ( parameterXml ) ;
2023-12-21 14:43:15 +00:00
}
}
}
auto floatParametersXml = xml - > getChildByName ( " floatParameters " ) ;
if ( floatParametersXml ! = nullptr ) {
for ( auto parameterXml : floatParametersXml - > getChildIterator ( ) ) {
auto parameter = getFloatParameter ( parameterXml - > getStringAttribute ( " id " ) ) ;
if ( parameter ! = nullptr ) {
parameter - > load ( parameterXml ) ;
}
}
}
auto intParametersXml = xml - > getChildByName ( " intParameters " ) ;
if ( intParametersXml ! = nullptr ) {
for ( auto parameterXml : intParametersXml - > getChildIterator ( ) ) {
auto parameter = getIntParameter ( parameterXml - > getStringAttribute ( " id " ) ) ;
if ( parameter ! = nullptr ) {
parameter - > load ( parameterXml ) ;
2023-07-25 13:09:21 +00:00
}
}
}
2024-01-07 19:48:02 +00:00
auto customFunction = xml - > getChildByName ( " customFunction " ) ;
if ( customFunction = = nullptr ) {
customFunction = xml - > getChildByName ( " perspectiveFunction " ) ;
}
if ( customFunction ! = nullptr ) {
2023-07-25 13:09:21 +00:00
auto stream = juce : : MemoryOutputStream ( ) ;
2024-01-07 19:48:02 +00:00
juce : : Base64 : : convertFromBase64 ( stream , customFunction - > getAllSubText ( ) ) ;
customEffect - > updateCode ( stream . toString ( ) ) ;
2023-07-25 13:09:21 +00:00
}
2023-08-27 21:01:37 +00:00
auto fontXml = xml - > getChildByName ( " font " ) ;
if ( fontXml ! = nullptr ) {
auto family = fontXml - > getStringAttribute ( " family " ) ;
auto bold = fontXml - > getBoolAttribute ( " bold " ) ;
auto italic = fontXml - > getBoolAttribute ( " italic " ) ;
juce : : SpinLock : : ScopedLockType lock ( fontLock ) ;
font = juce : : Font ( family , 1.0 , ( bold ? juce : : Font : : bold : 0 ) | ( italic ? juce : : Font : : italic : 0 ) ) ;
}
2023-07-25 13:09:21 +00:00
// close all files
auto numFiles = fileBlocks . size ( ) ;
for ( int i = 0 ; i < numFiles ; i + + ) {
removeFile ( 0 ) ;
}
auto filesXml = xml - > getChildByName ( " files " ) ;
if ( filesXml ! = nullptr ) {
for ( auto fileXml : filesXml - > getChildIterator ( ) ) {
auto fileName = fileXml - > getStringAttribute ( " name " ) ;
2024-05-12 20:12:35 +00:00
auto text = fileXml - > getAllSubText ( ) ;
std : : shared_ptr < juce : : MemoryBlock > fileBlock ;
if ( lessThanVersion ( version , " 2.2.0 " ) ) {
// Older versions of osci-render opened files in a silly way
auto stream = juce : : MemoryOutputStream ( ) ;
2024-06-01 20:50:56 +00:00
juce : : Base64 : : convertFromBase64 ( stream , text ) ;
2024-05-12 20:12:35 +00:00
fileBlock = std : : make_shared < juce : : MemoryBlock > ( stream . getData ( ) , stream . getDataSize ( ) ) ;
} else {
fileBlock = std : : make_shared < juce : : MemoryBlock > ( ) ;
fileBlock - > fromBase64Encoding ( text ) ;
}
2023-07-25 13:09:21 +00:00
addFile ( fileName , fileBlock ) ;
}
}
changeCurrentFile ( xml - > getIntAttribute ( " currentFile " , - 1 ) ) ;
2024-06-01 21:23:00 +00:00
auto visualiserXml = xml - > getChildByName ( " visualiser " ) ;
if ( visualiserXml ! = nullptr ) {
2024-09-12 22:09:54 +00:00
visualiserParameters . roughness = visualiserXml - > getIntAttribute ( " roughness " ) ;
visualiserParameters . intensity = visualiserXml - > getDoubleAttribute ( " intensity " ) ;
2024-06-01 21:23:00 +00:00
}
2024-04-17 15:00:59 +00:00
2023-07-25 13:09:21 +00:00
broadcaster . sendChangeMessage ( ) ;
2023-09-10 18:30:04 +00:00
prevMidiEnabled = ! midiEnabled - > getBoolValue ( ) ;
2023-07-25 13:09:21 +00:00
}
2023-01-09 21:58:49 +00:00
}
2023-09-16 12:59:52 +00:00
void OscirenderAudioProcessor : : parameterValueChanged ( int parameterIndex , float newValue ) {
2024-09-12 22:09:54 +00:00
for ( auto effect : luaEffects ) {
if ( parameterIndex = = effect - > parameters [ 0 ] - > getParameterIndex ( ) ) {
effect - > apply ( ) ;
}
}
2023-12-21 14:14:33 +00:00
if ( parameterIndex = = voices - > getParameterIndex ( ) ) {
int numVoices = voices - > getValueUnnormalised ( ) ;
// if the number of voices has changed, update the synth without clearing all the voices
if ( numVoices ! = synth . getNumVoices ( ) ) {
if ( numVoices > synth . getNumVoices ( ) ) {
for ( int i = synth . getNumVoices ( ) ; i < numVoices ; i + + ) {
synth . addVoice ( new ShapeVoice ( * this ) ) ;
}
} else {
for ( int i = synth . getNumVoices ( ) - 1 ; i > = numVoices ; i - - ) {
synth . removeVoice ( i ) ;
}
}
}
}
2023-09-16 12:59:52 +00:00
}
void OscirenderAudioProcessor : : parameterGestureChanged ( int parameterIndex , bool gestureIsStarting ) { }
2023-11-25 16:38:09 +00:00
void updateIfApproxEqual ( FloatParameter * parameter , float newValue ) {
if ( std : : abs ( parameter - > getValueUnnormalised ( ) - newValue ) > 0.0001 ) {
parameter - > setUnnormalisedValueNotifyingHost ( newValue ) ;
}
}
2023-11-25 15:37:33 +00:00
void OscirenderAudioProcessor : : envelopeChanged ( EnvelopeComponent * changedEnvelope ) {
Env env = changedEnvelope - > getEnv ( ) ;
std : : vector < double > levels = env . getLevels ( ) ;
std : : vector < double > times = env . getTimes ( ) ;
EnvCurveList curves = env . getCurves ( ) ;
if ( levels . size ( ) = = 4 & & times . size ( ) = = 3 & & curves . size ( ) = = 3 ) {
2023-11-25 17:57:35 +00:00
{
juce : : SpinLock : : ScopedLockType lock ( effectsLock ) ;
this - > adsrEnv = env ;
}
2023-11-25 16:38:09 +00:00
updateIfApproxEqual ( attackTime , times [ 0 ] ) ;
updateIfApproxEqual ( attackLevel , levels [ 1 ] ) ;
updateIfApproxEqual ( attackShape , curves [ 0 ] . getCurve ( ) ) ;
updateIfApproxEqual ( decayTime , times [ 1 ] ) ;
updateIfApproxEqual ( sustainLevel , levels [ 2 ] ) ;
updateIfApproxEqual ( decayShape , curves [ 1 ] . getCurve ( ) ) ;
updateIfApproxEqual ( releaseTime , times [ 2 ] ) ;
updateIfApproxEqual ( releaseShape , curves [ 2 ] . getCurve ( ) ) ;
2023-11-25 15:37:33 +00:00
}
}
2024-09-12 22:09:54 +00:00
double OscirenderAudioProcessor : : getSampleRate ( ) {
return currentSampleRate ;
}
2023-09-01 22:42:17 +00:00
2023-01-09 21:58:49 +00:00
//==============================================================================
// This creates new instances of the plugin..
juce : : AudioProcessor * JUCE_CALLTYPE createPluginFilter ( )
{
return new OscirenderAudioProcessor ( ) ;
}