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/RotateEffect.h"
# 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"
# include "audio/LuaEffect.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 > ( ) ,
2023-12-17 22:46:24 +00:00
new EffectParameter ( " Bit Crush " , " Limits the resolution of points drawn to the screen, making the image look pixelated, and making the audio sound more 'digital' and distorted. " , " bitCrush " , VERSION_HINT , 0.0 , 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 > ( ) ,
2023-12-17 22:46:24 +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.0 , 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 < RotateEffect > ( ) ,
2023-12-17 22:46:24 +00:00
new EffectParameter ( " 2D Rotate " , " Rotates the image, and pans the audio. " , " 2DRotateSpeed " , VERSION_HINT , 0.0 , 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 > ( ) ,
2023-12-17 22:46:24 +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.0 , 0.0 , 1.0 )
2023-07-19 20:40:31 +00:00
) ) ;
toggleableEffects . push_back ( std : : make_shared < Effect > (
std : : make_shared < DistortEffect > ( false ) ,
2023-12-17 22:46:24 +00:00
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 )
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 < DistortEffect > ( true ) ,
2023-12-17 22:46:24 +00:00
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 )
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-19 20:40:31 +00:00
[ this ] ( int index , Vector2 input , const std : : vector < double > & values , double sampleRate ) {
input . x + = values [ 0 ] ;
input . y + = values [ 1 ] ;
return input ;
2023-12-17 22:46:24 +00:00
} , std : : vector < EffectParameter * > {
new EffectParameter ( " Translate X " , " Moves the image horizontally. " , " translateX " , VERSION_HINT , 0.0 , - 1.0 , 1.0 ) ,
new EffectParameter ( " Translate Y " , " Moves the image vertically. " , " translateY " , VERSION_HINT , 0.0 , - 1.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 < SmoothEffect > ( ) ,
2023-12-17 22:46:24 +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.0 , 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 ,
2023-12-17 22:46:24 +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.0 , 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 * > {
new EffectParameter ( " Delay Decay " , " Adds repetitions, delays, or echos to the audio. This slider controls the volume of the echo. " , " delayDecay " , VERSION_HINT , 0.0 , 0.0 , 1.0 ) ,
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
) ) ;
2023-07-21 16:42:29 +00:00
toggleableEffects . push_back ( std : : make_shared < Effect > (
perspectiveEffect ,
std : : vector < EffectParameter * > {
2023-12-17 22:46:24 +00:00
new EffectParameter ( " 3D Perspective " , " Controls the strength of the 3D perspective effect which treats the image as a 3D object that can be rotated. " , " perspectiveStrength " , VERSION_HINT , 0.0 , 0.0 , 1.0 ) ,
new EffectParameter ( " Depth (z) " , " Controls how far away the 3D object is drawn away from the camera (the Z position). " , " perspectiveZPos " , VERSION_HINT , 0.1 , 0.0 , 1.0 ) ,
new EffectParameter ( " Rotate Speed " , " Controls how fast the 3D object rotates in the direction determined by the rotation sliders below. " , " perspectiveRotateSpeed " , VERSION_HINT , 0.0 , - 1.0 , 1.0 ) ,
new EffectParameter ( " Rotate X " , " Controls the rotation of the object in the X axis. " , " perspectiveRotateX " , VERSION_HINT , 1.0 , - 1.0 , 1.0 ) ,
new EffectParameter ( " Rotate Y " , " Controls the rotation of the object in the Y axis. " , " perspectiveRotateY " , VERSION_HINT , 1.0 , - 1.0 , 1.0 ) ,
new EffectParameter ( " Rotate Z " , " Controls the rotation of the object in the Z axis. " , " perspectiveRotateZ " , VERSION_HINT , 0.0 , - 1.0 , 1.0 ) ,
2023-12-17 21:32:56 +00:00
}
2023-07-21 16:42:29 +00:00
) ) ;
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
2023-07-14 14:34:24 +00:00
permanentEffects . push_back ( frequencyEffect ) ;
permanentEffects . push_back ( volumeEffect ) ;
permanentEffects . push_back ( thresholdEffect ) ;
permanentEffects . push_back ( rotateSpeed ) ;
permanentEffects . push_back ( rotateX ) ;
permanentEffects . push_back ( rotateY ) ;
permanentEffects . push_back ( rotateZ ) ;
permanentEffects . push_back ( focalLength ) ;
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-09-16 12:59:52 +00:00
for ( auto & effect : luaEffects ) {
effect - > addListener ( 0 , this ) ;
}
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-07-25 13:09:21 +00:00
booleanParameters . push_back ( fixedRotateX ) ;
booleanParameters . push_back ( fixedRotateY ) ;
booleanParameters . push_back ( fixedRotateZ ) ;
booleanParameters . push_back ( perspectiveEffect - > fixedRotateX ) ;
booleanParameters . push_back ( perspectiveEffect - > fixedRotateY ) ;
booleanParameters . push_back ( perspectiveEffect - > fixedRotateZ ) ;
2023-09-07 21:04:08 +00:00
booleanParameters . push_back ( midiEnabled ) ;
2023-12-14 21:26:40 +00:00
booleanParameters . push_back ( inputEnabled ) ;
2023-07-25 13:09:21 +00:00
for ( auto parameter : booleanParameters ) {
addParameter ( parameter ) ;
}
2023-08-28 21:06:21 +00:00
2023-11-25 16:38:09 +00:00
addParameter ( attackTime ) ;
addParameter ( attackLevel ) ;
addParameter ( attackShape ) ;
addParameter ( decayTime ) ;
addParameter ( decayShape ) ;
addParameter ( sustainLevel ) ;
addParameter ( releaseTime ) ;
addParameter ( releaseShape ) ;
2023-11-25 15:37:33 +00:00
2023-08-28 21:06:21 +00:00
for ( int i = 0 ; i < 4 ; i + + ) {
synth . addVoice ( new ShapeVoice ( * 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 ( ) {
for ( auto & effect : luaEffects ) {
effect - > removeListener ( 0 , this ) ;
}
}
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 ;
}
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 ;
2023-07-13 19:11:24 +00:00
pitchDetector . setSampleRate ( sampleRate ) ;
2023-08-28 21:06:21 +00:00
synth . setCurrentPlaybackSampleRate ( sampleRate ) ;
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 = " " ;
int sliderNum = luaEffects . size ( ) + 1 ;
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 > (
std : : make_shared < LuaEffect > ( sliderName , * this ) ,
2023-12-17 22:46:24 +00:00
new EffectParameter (
" Lua Slider " + sliderName ,
" Controls the value of the Lua variable called slider_ " + sliderName + " . " ,
" lua " + sliderName ,
VERSION_HINT , 0.0 , 0.0 , 1.0 , 0.001 , false
)
2023-07-16 19:54:41 +00:00
) ) ;
2023-07-21 10:08:55 +00:00
auto & effect = luaEffects . back ( ) ;
effect - > parameters [ 0 ] - > disableLfo ( ) ;
2023-07-04 13:58:36 +00:00
}
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 : : updateLuaValues ( ) {
for ( auto & effect : luaEffects ) {
2023-07-05 11:02:28 +00:00
effect - > apply ( ) ;
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-05 11:02:28 +00:00
// parsersLock should be held when calling this
void OscirenderAudioProcessor : : updateObjValues ( ) {
2023-07-14 14:34:24 +00:00
focalLength - > apply ( ) ;
rotateX - > apply ( ) ;
rotateY - > apply ( ) ;
rotateZ - > apply ( ) ;
rotateSpeed - > apply ( ) ;
2023-01-15 17:01:27 +00:00
}
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-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 + + ) ;
2023-12-20 21:41:28 +00:00
parsers . push_back ( std : : make_shared < FileParser > ( errorCallback , variableCallback ) ) ;
2023-08-28 21:06:21 +00:00
sounds . push_back ( new ShapeSound ( 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 + + ) ;
2023-12-20 21:41:28 +00:00
parsers . push_back ( std : : make_shared < FileParser > ( errorCallback , variableCallback ) ) ;
2023-08-28 21:06:21 +00:00
sounds . push_back ( new ShapeSound ( 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 + + ) ;
2023-12-20 21:41:28 +00:00
parsers . push_back ( std : : make_shared < FileParser > ( errorCallback , variableCallback ) ) ;
2023-08-28 21:06:21 +00:00
sounds . push_back ( new ShapeSound ( 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-07-05 14:17:17 +00:00
updateLuaValues ( ) ;
updateObjValues ( ) ;
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
}
}
2023-12-20 17:47:58 +00:00
void OscirenderAudioProcessor : : notifyErrorListeners ( int lineNumber , juce : : String fileName , juce : : String error ) {
2023-12-20 17:13:38 +00:00
juce : : SpinLock : : ScopedLockType lock ( errorListenersLock ) ;
for ( auto listener : errorListeners ) {
2023-12-20 17:47:58 +00:00
if ( listener - > getFileName ( ) = = fileName ) {
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 ;
auto totalNumInputChannels = getTotalNumInputChannels ( ) ;
auto totalNumOutputChannels = getTotalNumOutputChannels ( ) ;
2023-12-14 21:26:40 +00:00
// clear output channels
for ( auto i = totalNumInputChannels ; i < totalNumOutputChannels ; + + i ) {
buffer . clear ( i , 0 , buffer . getNumSamples ( ) ) ;
}
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 ) ;
}
}
// if midi has just been disabled
if ( prevMidiEnabled & & ! usingMidi ) {
midiMessages . addEvent ( juce : : MidiMessage : : noteOn ( 1 , 60 , 1.0f ) , 17 ) ;
}
prevMidiEnabled = usingMidi ;
2023-09-09 14:32:03 +00:00
const double EPSILON = 0.00001 ;
2023-12-14 21:26:40 +00:00
if ( usingInput & & totalNumInputChannels > = 2 ) {
// handle all midi messages
auto midiIterator = midiMessages . cbegin ( ) ;
std : : for_each ( midiIterator ,
midiMessages . cend ( ) ,
[ & ] ( const juce : : MidiMessageMetadata & meta ) { synth . publicHandleMidiEvent ( meta . getMessage ( ) ) ; }
) ;
} else {
for ( auto i = 0 ; i < totalNumInputChannels ; + + i ) {
buffer . clear ( i , 0 , buffer . getNumSamples ( ) ) ;
}
if ( volume > EPSILON ) {
juce : : SpinLock : : ScopedLockType lock1 ( parsersLock ) ;
juce : : SpinLock : : ScopedLockType lock2 ( effectsLock ) ;
synth . renderNextBlock ( buffer , midiMessages , 0 , buffer . getNumSamples ( ) ) ;
}
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 ( ) ;
2023-08-28 21:06:21 +00:00
for ( auto sample = 0 ; sample < buffer . getNumSamples ( ) ; + + sample ) {
Vector2 channels = { buffer . getSample ( 0 , sample ) , buffer . getSample ( 1 , 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 ( ) ) {
channels = effect - > apply ( sample , channels ) ;
}
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 ) {
channels = effect - > apply ( sample , channels ) ;
}
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 ;
}
2023-07-06 16:57:10 +00:00
2023-09-01 22:42:17 +00:00
{
juce : : SpinLock : : ScopedLockType scope ( consumerLock ) ;
for ( auto consumer : consumers ) {
consumer - > write ( x ) ;
consumer - > write ( y ) ;
consumer - > notifyIfFull ( ) ;
}
}
2023-01-15 17:01:27 +00:00
}
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 ) ;
2023-12-20 20:58:08 +00:00
if ( wrapperType = = wrapperType_Standalone ) {
if ( juce : : TopLevelWindow : : getNumTopLevelWindows ( ) = = 1 ) {
juce : : TopLevelWindow * w = juce : : TopLevelWindow : : getTopLevelWindow ( 0 ) ;
w - > setUsingNativeTitleBar ( true ) ;
}
}
2023-07-28 20:10:21 +00:00
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-07-25 11:23:27 +00:00
auto perspectiveFunction = xml - > createNewChildElement ( " perspectiveFunction " ) ;
perspectiveFunction - > addTextElement ( juce : : Base64 : : toBase64 ( perspectiveEffect - > 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 ] ) ;
auto fileString = juce : : MemoryInputStream ( * fileBlocks [ i ] , false ) . readEntireStreamAsString ( ) ;
fileXml - > addTextElement ( juce : : Base64 : : toBase64 ( fileString ) ) ;
}
xml - > setAttribute ( " currentFile " , currentFile ) ;
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 ;
}
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 ) ;
}
}
}
auto perspectiveFunction = xml - > getChildByName ( " perspectiveFunction " ) ;
if ( perspectiveFunction ! = nullptr ) {
auto stream = juce : : MemoryOutputStream ( ) ;
juce : : Base64 : : convertFromBase64 ( stream , perspectiveFunction - > getAllSubText ( ) ) ;
perspectiveEffect - > updateCode ( stream . toString ( ) ) ;
}
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 " ) ;
auto stream = juce : : MemoryOutputStream ( ) ;
juce : : Base64 : : convertFromBase64 ( stream , fileXml - > getAllSubText ( ) ) ;
auto fileBlock = std : : make_shared < juce : : MemoryBlock > ( stream . getData ( ) , stream . getDataSize ( ) ) ;
addFile ( fileName , fileBlock ) ;
}
}
changeCurrentFile ( xml - > getIntAttribute ( " currentFile " , - 1 ) ) ;
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-05 19:46:05 +00:00
std : : shared_ptr < BufferConsumer > OscirenderAudioProcessor : : consumerRegister ( std : : vector < float > & buffer ) {
2023-09-01 22:42:17 +00:00
std : : shared_ptr < BufferConsumer > consumer = std : : make_shared < BufferConsumer > ( buffer ) ;
2023-09-05 19:46:05 +00:00
juce : : SpinLock : : ScopedLockType scope ( consumerLock ) ;
consumers . push_back ( consumer ) ;
return consumer ;
}
void OscirenderAudioProcessor : : consumerRead ( std : : shared_ptr < BufferConsumer > consumer ) {
2023-09-01 22:42:17 +00:00
consumer - > waitUntilFull ( ) ;
2023-09-05 19:46:05 +00:00
juce : : SpinLock : : ScopedLockType scope ( consumerLock ) ;
consumers . erase ( std : : remove ( consumers . begin ( ) , consumers . end ( ) , consumer ) , consumers . end ( ) ) ;
}
void OscirenderAudioProcessor : : consumerStop ( std : : shared_ptr < BufferConsumer > consumer ) {
if ( consumer ! = nullptr ) {
2023-09-01 22:42:17 +00:00
juce : : SpinLock : : ScopedLockType scope ( consumerLock ) ;
2023-09-05 19:46:05 +00:00
consumer - > forceNotify ( ) ;
2023-09-01 22:42:17 +00:00
}
}
2023-09-16 12:59:52 +00:00
void OscirenderAudioProcessor : : parameterValueChanged ( int parameterIndex , float newValue ) {
// call apply on lua effects
for ( auto & effect : luaEffects ) {
if ( parameterIndex = = effect - > parameters [ 0 ] - > getParameterIndex ( ) ) {
effect - > apply ( ) ;
return ;
}
}
}
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
}
}
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 ( ) ;
}