mirror of
https://codeberg.org/armin/wavetable.git
synced 2026-09-01 05:50:46 +02:00
Add more fx
This commit is contained in:
parent
8a793ba5b4
commit
b20f153c84
15 changed files with 5211 additions and 22 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit f79efd9b83a7a546df4ba8c437cc7d487144aa89
|
||||
Subproject commit 33a6a308642734c70746c025290398e550b04858
|
||||
|
|
@ -106,9 +106,9 @@
|
|||
},
|
||||
{ "id": "fx", "x": 0, "y": "prevB()+1", "w": "parW() - 169", "h": 137, "children":
|
||||
[
|
||||
{ "id": "gate", "x": "0", "y": 0, "w": 290, "h": 137, "children":
|
||||
{ "id": "gate", "x": "0", "y": 0, "w": 306, "h": 137, "children":
|
||||
[
|
||||
{ "id": "Beat", "x": 61, "y": 30, "w": 42, "h": 57 },
|
||||
{ "id": "Beat", "x": 69, "y": 30, "w": 42, "h": 57 },
|
||||
{ "id": "Length", "x": "prevR()", "y": "prevY()", "w": 42, "h": 57 },
|
||||
{ "id": "A", "x": "prevR()", "y": "prevY()", "w": 42, "h": 57 },
|
||||
{ "id": "R", "x": "prevR()", "y": "prevY()", "w": 42, "h": 57 },
|
||||
|
|
@ -124,9 +124,42 @@
|
|||
{ "id": "Width", "x": "prevR()", "y": "prevY()", "w": 42, "h": 57 }
|
||||
]
|
||||
},
|
||||
{ "id": "distort", "x": "prevR()+1", "y": "prevY()", "w": 100, "h": 137, "children":
|
||||
{ "id": "distort", "x": "prevR()+1", "y": "prevY()", "w": 84, "h": 137, "children":
|
||||
[
|
||||
{ "id": "Amount", "x": 22, "y": "23 + 22", "w": 56, "h": 70 }
|
||||
{ "id": "Distort", "children":
|
||||
[
|
||||
{ "id": "mode", "r": "parW() - 4", "y": 4, "w": 16, "h": 16 }
|
||||
]
|
||||
},
|
||||
{ "id": "page1", "bounds": "parent", "children":
|
||||
[
|
||||
{ "id": "Amount", "x": 14, "y": "23 + 22", "w": 56, "h": 70 }
|
||||
]
|
||||
},
|
||||
{ "id": "page2", "bounds": "parent", "children":
|
||||
[
|
||||
{ "id": "Rate", "x": 0, "y": 23, "w": 42, "h": 57 },
|
||||
{ "id": "Rez", "x": "prevR()", "y": "prevY()", "w": 42, "h": 57 },
|
||||
{ "id": "Hard", "x": 0, "y": "prevB()", "w": 42, "h": 57 },
|
||||
{ "id": "Mix", "x": "prevR()", "y": "prevY()", "w": 42, "h": 57 }
|
||||
]
|
||||
},
|
||||
{ "id": "page3", "bounds": "parent", "children":
|
||||
[
|
||||
{ "id": "Gain", "x": 0, "y": 23, "w": 42, "h": 57 },
|
||||
{ "id": "Tone", "x": "prevR()", "y": "prevY()", "w": 42, "h": 57 },
|
||||
{ "id": "Output", "x": 0, "y": "prevB()", "w": 42, "h": 57 },
|
||||
{ "id": "Mix", "x": "prevR()", "y": "prevY()", "w": 42, "h": 57 }
|
||||
]
|
||||
},
|
||||
{ "id": "page4", "bounds": "parent", "children":
|
||||
[
|
||||
{ "id": "Gain", "x": 0, "y": 23, "w": 42, "h": 57 },
|
||||
{ "id": "Tone", "x": "prevR()", "y": "prevY()", "w": 42, "h": 57 },
|
||||
{ "id": "Output", "x": 0, "y": "prevB()", "w": 42, "h": 57 },
|
||||
{ "id": "Mix", "x": "prevR()", "y": "prevY()", "w": 42, "h": 57 }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{ "id": "delay", "x": "prevR()+1", "y": "prevY()", "w": 126, "h": 137, "children":
|
||||
|
|
|
|||
156
plugin/Source/FX/DeRez2.cpp
Executable file
156
plugin/Source/FX/DeRez2.cpp
Executable file
|
|
@ -0,0 +1,156 @@
|
|||
/* ========================================
|
||||
* DeRez2 - DeRez2.h
|
||||
* Copyright (c) 2016 airwindows, Airwindows uses the MIT license
|
||||
* ======================================== */
|
||||
|
||||
#ifndef __DeRez2_H
|
||||
#include "DeRez2.h"
|
||||
#endif
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfloat-conversion", "-Wimplicit-float-conversion", "-Wunused-parameter")
|
||||
|
||||
DeRez2::DeRez2(audioMasterCallback audioMaster) :
|
||||
AudioEffectX(audioMaster, kNumPrograms, kNumParameters)
|
||||
{
|
||||
A = 1.0;
|
||||
B = 1.0;
|
||||
C = 1.0;
|
||||
D = 1.0;
|
||||
lastSampleL = 0.0;
|
||||
heldSampleL = 0.0;
|
||||
lastDrySampleL = 0.0;
|
||||
lastOutputSampleL = 0.0;
|
||||
|
||||
lastSampleR = 0.0;
|
||||
heldSampleR = 0.0;
|
||||
lastDrySampleR = 0.0;
|
||||
lastOutputSampleR = 0.0;
|
||||
|
||||
position = 0.0;
|
||||
incrementA = 0.0;
|
||||
incrementB = 0.0;
|
||||
fpdL = 1.0; while (fpdL < 16386) fpdL = rand()*UINT32_MAX;
|
||||
fpdR = 1.0; while (fpdR < 16386) fpdR = rand()*UINT32_MAX;
|
||||
//this is reset: values being initialized only once. Startup values, whatever they are.
|
||||
|
||||
_canDo.insert("plugAsChannelInsert"); // plug-in can be used as a channel insert effect.
|
||||
_canDo.insert("plugAsSend"); // plug-in can be used as a send effect.
|
||||
_canDo.insert("x2in2out");
|
||||
setNumInputs(kNumInputs);
|
||||
setNumOutputs(kNumOutputs);
|
||||
setUniqueID(kUniqueId);
|
||||
canProcessReplacing(); // supports output replacing
|
||||
canDoubleReplacing(); // supports double precision processing
|
||||
programsAreChunks(true);
|
||||
vst_strncpy (_programName, "Default", kVstMaxProgNameLen); // default program name
|
||||
}
|
||||
|
||||
DeRez2::~DeRez2() {}
|
||||
VstInt32 DeRez2::getVendorVersion () {return 1000;}
|
||||
void DeRez2::setProgramName(char *name) {vst_strncpy (_programName, name, kVstMaxProgNameLen);}
|
||||
void DeRez2::getProgramName(char *name) {vst_strncpy (name, _programName, kVstMaxProgNameLen);}
|
||||
//airwindows likes to ignore this stuff. Make your own programs, and make a different plugin rather than
|
||||
//trying to do versioning and preventing people from using older versions. Maybe they like the old one!
|
||||
|
||||
static float pinParameter(float data)
|
||||
{
|
||||
if (data < 0.0f) return 0.0f;
|
||||
if (data > 1.0f) return 1.0f;
|
||||
return data;
|
||||
}
|
||||
|
||||
VstInt32 DeRez2::getChunk (void** data, bool isPreset)
|
||||
{
|
||||
float *chunkData = (float *)calloc(kNumParameters, sizeof(float));
|
||||
chunkData[0] = A;
|
||||
chunkData[1] = B;
|
||||
chunkData[2] = C;
|
||||
chunkData[3] = D;
|
||||
/* Note: The way this is set up, it will break if you manage to save settings on an Intel
|
||||
machine and load them on a PPC Mac. However, it's fine if you stick to the machine you
|
||||
started with. */
|
||||
|
||||
*data = chunkData;
|
||||
return kNumParameters * sizeof(float);
|
||||
}
|
||||
|
||||
VstInt32 DeRez2::setChunk (void* data, VstInt32 byteSize, bool isPreset)
|
||||
{
|
||||
float *chunkData = (float *)data;
|
||||
A = pinParameter(chunkData[0]);
|
||||
B = pinParameter(chunkData[1]);
|
||||
C = pinParameter(chunkData[2]);
|
||||
D = pinParameter(chunkData[3]);
|
||||
/* We're ignoring byteSize as we found it to be a filthy liar */
|
||||
|
||||
/* calculate any other fields you need here - you could copy in
|
||||
code from setParameter() here. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DeRez2::setParameter(VstInt32 index, float value) {
|
||||
switch (index) {
|
||||
case kParamA: A = value; break;
|
||||
case kParamB: B = value; break;
|
||||
case kParamC: C = value; break;
|
||||
case kParamD: D = value; break;
|
||||
default: throw; // unknown parameter, shouldn't happen!
|
||||
}
|
||||
}
|
||||
|
||||
float DeRez2::getParameter(VstInt32 index) {
|
||||
switch (index) {
|
||||
case kParamA: return A; break;
|
||||
case kParamB: return B; break;
|
||||
case kParamC: return C; break;
|
||||
case kParamD: return D; break;
|
||||
default: break; // unknown parameter, shouldn't happen!
|
||||
} return 0.0; //we only need to update the relevant name, this is simple to manage
|
||||
}
|
||||
|
||||
void DeRez2::getParameterName(VstInt32 index, char *text) {
|
||||
switch (index) {
|
||||
case kParamA: vst_strncpy (text, "Rate", kVstMaxParamStrLen); break;
|
||||
case kParamB: vst_strncpy (text, "Rez", kVstMaxParamStrLen); break;
|
||||
case kParamC: vst_strncpy (text, "Hard", kVstMaxParamStrLen); break;
|
||||
case kParamD: vst_strncpy (text, "Dry/Wet", kVstMaxParamStrLen); break;
|
||||
default: break; // unknown parameter, shouldn't happen!
|
||||
} //this is our labels for displaying in the VST host
|
||||
}
|
||||
|
||||
void DeRez2::getParameterDisplay(VstInt32 index, char *text) {
|
||||
switch (index) {
|
||||
case kParamA: float2string (A, text, kVstMaxParamStrLen); break;
|
||||
case kParamB: float2string (B, text, kVstMaxParamStrLen); break;
|
||||
case kParamC: float2string (C, text, kVstMaxParamStrLen); break;
|
||||
case kParamD: float2string (D, text, kVstMaxParamStrLen); break;
|
||||
default: break; // unknown parameter, shouldn't happen!
|
||||
} //this displays the values and handles 'popups' where it's discrete choices
|
||||
}
|
||||
|
||||
void DeRez2::getParameterLabel(VstInt32 index, char *text) {
|
||||
switch (index) {
|
||||
case kParamA: vst_strncpy (text, "", kVstMaxParamStrLen); break;
|
||||
case kParamB: vst_strncpy (text, "", kVstMaxParamStrLen); break;
|
||||
case kParamC: vst_strncpy (text, "", kVstMaxParamStrLen); break;
|
||||
case kParamD: vst_strncpy (text, "", kVstMaxParamStrLen); break;
|
||||
default: break; // unknown parameter, shouldn't happen!
|
||||
}
|
||||
}
|
||||
|
||||
VstInt32 DeRez2::canDo(char *text)
|
||||
{ return (_canDo.find(text) == _canDo.end()) ? -1: 1; } // 1 = yes, -1 = no, 0 = don't know
|
||||
|
||||
bool DeRez2::getEffectName(char* name) {
|
||||
vst_strncpy(name, "DeRez2", kVstMaxProductStrLen); return true;
|
||||
}
|
||||
|
||||
VstPlugCategory DeRez2::getPlugCategory() {return kPlugCategEffect;}
|
||||
|
||||
bool DeRez2::getProductString(char* text) {
|
||||
vst_strncpy (text, "airwindows DeRez2", kVstMaxProductStrLen); return true;
|
||||
}
|
||||
|
||||
bool DeRez2::getVendorString(char* text) {
|
||||
vst_strncpy (text, "airwindows", kVstMaxVendorStrLen); return true;
|
||||
}
|
||||
79
plugin/Source/FX/DeRez2.h
Executable file
79
plugin/Source/FX/DeRez2.h
Executable file
|
|
@ -0,0 +1,79 @@
|
|||
/* ========================================
|
||||
* DeRez2 - DeRez2.h
|
||||
* Created 8/12/11 by SPIAdmin
|
||||
* Copyright (c) 2011 __MyCompanyName__, Airwindows uses the MIT license
|
||||
* ======================================== */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FXBase.h"
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <math.h>
|
||||
|
||||
class DeRez2 :
|
||||
public AudioEffectX
|
||||
{
|
||||
public:
|
||||
enum {
|
||||
kParamA = 0,
|
||||
kParamB = 1,
|
||||
kParamC = 2,
|
||||
kParamD = 3,
|
||||
kNumParameters = 4
|
||||
}; //
|
||||
|
||||
static constexpr int kNumPrograms = 0;
|
||||
static constexpr int kNumInputs = 2;
|
||||
static constexpr int kNumOutputs = 2;
|
||||
static constexpr unsigned long kUniqueId = 'eerz'; //Change this to what the AU identity is!
|
||||
|
||||
DeRez2(audioMasterCallback audioMaster);
|
||||
~DeRez2();
|
||||
virtual bool getEffectName(char* name); // The plug-in name
|
||||
virtual VstPlugCategory getPlugCategory(); // The general category for the plug-in
|
||||
virtual bool getProductString(char* text); // This is a unique plug-in string provided by Steinberg
|
||||
virtual bool getVendorString(char* text); // Vendor info
|
||||
virtual VstInt32 getVendorVersion(); // Version number
|
||||
virtual void processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames);
|
||||
virtual void processDoubleReplacing (double** inputs, double** outputs, VstInt32 sampleFrames);
|
||||
virtual void getProgramName(char *name); // read the name from the host
|
||||
virtual void setProgramName(char *name); // changes the name of the preset displayed in the host
|
||||
virtual VstInt32 getChunk (void** data, bool isPreset);
|
||||
virtual VstInt32 setChunk (void* data, VstInt32 byteSize, bool isPreset);
|
||||
virtual float getParameter(VstInt32 index); // get the parameter value at the specified index
|
||||
virtual void setParameter(VstInt32 index, float value); // set the parameter at index to value
|
||||
virtual void getParameterLabel(VstInt32 index, char *text); // label for the parameter (eg dB)
|
||||
virtual void getParameterName(VstInt32 index, char *text); // name of the parameter
|
||||
virtual void getParameterDisplay(VstInt32 index, char *text); // text description of the current value
|
||||
virtual VstInt32 canDo(char *text);
|
||||
private:
|
||||
char _programName[kVstMaxProgNameLen + 1];
|
||||
std::set< std::string > _canDo;
|
||||
|
||||
double lastSampleL;
|
||||
double heldSampleL;
|
||||
double lastDrySampleL;
|
||||
double lastOutputSampleL;
|
||||
|
||||
double lastSampleR;
|
||||
double heldSampleR;
|
||||
double lastDrySampleR;
|
||||
double lastOutputSampleR;
|
||||
|
||||
double position;
|
||||
double incrementA;
|
||||
double incrementB;
|
||||
|
||||
uint32_t fpdL;
|
||||
uint32_t fpdR;
|
||||
//default stuff
|
||||
|
||||
float A;
|
||||
float B;
|
||||
float C;
|
||||
float D;
|
||||
|
||||
};
|
||||
|
||||
366
plugin/Source/FX/DeRez2Proc.cpp
Executable file
366
plugin/Source/FX/DeRez2Proc.cpp
Executable file
|
|
@ -0,0 +1,366 @@
|
|||
/* ========================================
|
||||
* DeRez2 - DeRez2.h
|
||||
* Copyright (c) 2016 airwindows, Airwindows uses the MIT license
|
||||
* ======================================== */
|
||||
|
||||
#ifndef __DeRez2_H
|
||||
#include "DeRez2.h"
|
||||
#endif
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfloat-conversion", "-Wimplicit-float-conversion", "-Wunused-parameter", "-Wunused-value")
|
||||
|
||||
void DeRez2::processReplacing(float **inputs, float **outputs, VstInt32 sampleFrames)
|
||||
{
|
||||
float* in1 = inputs[0];
|
||||
float* in2 = inputs[1];
|
||||
float* out1 = outputs[0];
|
||||
float* out2 = outputs[1];
|
||||
|
||||
double targetA = pow(A,3)+0.0005;
|
||||
if (targetA > 1.0) targetA = 1.0;
|
||||
double soften = (1.0 + targetA)/2;
|
||||
double targetB = pow(1.0-B,3) / 3;
|
||||
double hard = C;
|
||||
double wet = D;
|
||||
|
||||
double overallscale = 1.0;
|
||||
overallscale /= 44100.0;
|
||||
overallscale *= getSampleRate();
|
||||
targetA /= overallscale;
|
||||
|
||||
while (--sampleFrames >= 0)
|
||||
{
|
||||
double inputSampleL = *in1;
|
||||
double inputSampleR = *in2;
|
||||
if (fabs(inputSampleL)<1.18e-23) inputSampleL = fpdL * 1.18e-17;
|
||||
if (fabs(inputSampleR)<1.18e-23) inputSampleR = fpdR * 1.18e-17;
|
||||
double drySampleL = inputSampleL;
|
||||
double drySampleR = inputSampleR;
|
||||
|
||||
|
||||
incrementA = ((incrementA*999.0)+targetA)/1000.0;
|
||||
incrementB = ((incrementB*999.0)+targetB)/1000.0;
|
||||
//incrementA is the frequency derez
|
||||
//incrementB is the bit depth derez
|
||||
position += incrementA;
|
||||
|
||||
double outputSampleL = heldSampleL;
|
||||
double outputSampleR = heldSampleR;
|
||||
if (position > 1.0)
|
||||
{
|
||||
position -= 1.0;
|
||||
heldSampleL = (lastSampleL * position) + (inputSampleL * (1.0-position));
|
||||
outputSampleL = (outputSampleL * (1.0-soften)) + (heldSampleL * soften);
|
||||
//softens the edge of the derez
|
||||
heldSampleR = (lastSampleR * position) + (inputSampleR * (1.0-position));
|
||||
outputSampleR = (outputSampleR * (1.0-soften)) + (heldSampleR * soften);
|
||||
//softens the edge of the derez
|
||||
}
|
||||
inputSampleL = outputSampleL;
|
||||
inputSampleR = outputSampleR;
|
||||
|
||||
double tempL = inputSampleL;
|
||||
double tempR = inputSampleR;
|
||||
|
||||
if (inputSampleL != lastOutputSampleL) {
|
||||
tempL = inputSampleL;
|
||||
inputSampleL = (inputSampleL * hard) + (lastDrySampleL * (1.0-hard));
|
||||
//transitions get an intermediate dry sample
|
||||
lastOutputSampleL = tempL; //only one intermediate sample
|
||||
} else {
|
||||
lastOutputSampleL = inputSampleL;
|
||||
}
|
||||
|
||||
if (inputSampleR != lastOutputSampleR) {
|
||||
tempR = inputSampleR;
|
||||
inputSampleR = (inputSampleR * hard) + (lastDrySampleR * (1.0-hard));
|
||||
//transitions get an intermediate dry sample
|
||||
lastOutputSampleR = tempR; //only one intermediate sample
|
||||
} else {
|
||||
lastOutputSampleR = inputSampleR;
|
||||
}
|
||||
|
||||
lastDrySampleL = drySampleL;
|
||||
lastDrySampleR = drySampleR;
|
||||
//freq section of soft/hard interpolates dry samples
|
||||
|
||||
tempL = inputSampleL;
|
||||
tempR = inputSampleR;
|
||||
|
||||
if (inputSampleL > 1.0) inputSampleL = 1.0;
|
||||
if (inputSampleL < -1.0) inputSampleL = -1.0;
|
||||
|
||||
if (inputSampleR > 1.0) inputSampleR = 1.0;
|
||||
if (inputSampleR < -1.0) inputSampleR = -1.0;
|
||||
|
||||
if (inputSampleL > 0) inputSampleL = log(1.0+(255*fabs(inputSampleL))) / log(256);
|
||||
if (inputSampleL < 0) inputSampleL = -log(1.0+(255*fabs(inputSampleL))) / log(256);
|
||||
|
||||
if (inputSampleR > 0) inputSampleR = log(1.0+(255*fabs(inputSampleR))) / log(256);
|
||||
if (inputSampleR < 0) inputSampleR = -log(1.0+(255*fabs(inputSampleR))) / log(256);
|
||||
|
||||
inputSampleL = (tempL * hard) + (inputSampleL * (1.0-hard));
|
||||
inputSampleR = (tempR * hard) + (inputSampleR * (1.0-hard)); //uLaw encode as part of soft/hard
|
||||
|
||||
tempL = inputSampleL;
|
||||
tempR = inputSampleR;
|
||||
|
||||
if (incrementB > 0.0005)
|
||||
{
|
||||
if (inputSampleL > 0)
|
||||
{
|
||||
tempL = inputSampleL;
|
||||
while (tempL > 0) {tempL -= incrementB;}
|
||||
inputSampleL -= tempL;
|
||||
//it's below 0 so subtracting adds the remainder
|
||||
}
|
||||
if (inputSampleR > 0)
|
||||
{
|
||||
tempR = inputSampleR;
|
||||
while (tempR > 0) {tempR -= incrementB;}
|
||||
inputSampleR -= tempR;
|
||||
//it's below 0 so subtracting adds the remainder
|
||||
}
|
||||
|
||||
if (inputSampleL < 0)
|
||||
{
|
||||
tempL = inputSampleL;
|
||||
while (tempL < 0) {tempL += incrementB;}
|
||||
inputSampleL -= tempL;
|
||||
//it's above 0 so subtracting subtracts the remainder
|
||||
}
|
||||
if (inputSampleR < 0)
|
||||
{
|
||||
tempR = inputSampleR;
|
||||
while (tempR < 0) {tempR += incrementB;}
|
||||
inputSampleR -= tempR;
|
||||
//it's above 0 so subtracting subtracts the remainder
|
||||
}
|
||||
|
||||
inputSampleL *= (1.0 - incrementB);
|
||||
inputSampleR *= (1.0 - incrementB);
|
||||
}
|
||||
|
||||
tempL = inputSampleL;
|
||||
tempR = inputSampleR;
|
||||
|
||||
if (inputSampleL > 1.0) inputSampleL = 1.0;
|
||||
if (inputSampleL < -1.0) inputSampleL = -1.0;
|
||||
|
||||
if (inputSampleR > 1.0) inputSampleR = 1.0;
|
||||
if (inputSampleR < -1.0) inputSampleR = -1.0;
|
||||
|
||||
if (inputSampleL > 0) inputSampleL = (pow(256,fabs(inputSampleL))-1.0) / 255;
|
||||
if (inputSampleL < 0) inputSampleL = -(pow(256,fabs(inputSampleL))-1.0) / 255;
|
||||
|
||||
if (inputSampleR > 0) inputSampleR = (pow(256,fabs(inputSampleR))-1.0) / 255;
|
||||
if (inputSampleR < 0) inputSampleR = -(pow(256,fabs(inputSampleR))-1.0) / 255;
|
||||
|
||||
inputSampleL = (tempL * hard) + (inputSampleL * (1.0-hard));
|
||||
inputSampleR = (tempR * hard) + (inputSampleR * (1.0-hard)); //uLaw decode as part of soft/hard
|
||||
|
||||
if (wet !=1.0) {
|
||||
inputSampleL = (inputSampleL * wet) + (drySampleL * (1.0-wet));
|
||||
inputSampleR = (inputSampleR * wet) + (drySampleR * (1.0-wet));
|
||||
}
|
||||
//Dry/Wet control, defaults to the last slider
|
||||
|
||||
lastSampleL = drySampleL;
|
||||
lastSampleR = drySampleR;
|
||||
|
||||
//begin 32 bit stereo floating point dither
|
||||
int expon; frexpf((float)inputSampleL, &expon);
|
||||
fpdL ^= fpdL << 13; fpdL ^= fpdL >> 17; fpdL ^= fpdL << 5;
|
||||
inputSampleL += ((double(fpdL)-uint32_t(0x7fffffff)) * 5.5e-36l * pow(2,expon+62));
|
||||
frexpf((float)inputSampleR, &expon);
|
||||
fpdR ^= fpdR << 13; fpdR ^= fpdR >> 17; fpdR ^= fpdR << 5;
|
||||
inputSampleR += ((double(fpdR)-uint32_t(0x7fffffff)) * 5.5e-36l * pow(2,expon+62));
|
||||
//end 32 bit stereo floating point dither
|
||||
|
||||
*out1 = inputSampleL;
|
||||
*out2 = inputSampleR;
|
||||
|
||||
*in1++;
|
||||
*in2++;
|
||||
*out1++;
|
||||
*out2++;
|
||||
}
|
||||
}
|
||||
|
||||
void DeRez2::processDoubleReplacing(double **inputs, double **outputs, VstInt32 sampleFrames)
|
||||
{
|
||||
double* in1 = inputs[0];
|
||||
double* in2 = inputs[1];
|
||||
double* out1 = outputs[0];
|
||||
double* out2 = outputs[1];
|
||||
|
||||
double targetA = pow(A,3)+0.0005;
|
||||
if (targetA > 1.0) targetA = 1.0;
|
||||
double soften = (1.0 + targetA)/2;
|
||||
double targetB = pow(1.0-B,3) / 3;
|
||||
double hard = C;
|
||||
double wet = D;
|
||||
|
||||
double overallscale = 1.0;
|
||||
overallscale /= 44100.0;
|
||||
overallscale *= getSampleRate();
|
||||
targetA /= overallscale;
|
||||
|
||||
while (--sampleFrames >= 0)
|
||||
{
|
||||
double inputSampleL = *in1;
|
||||
double inputSampleR = *in2;
|
||||
if (fabs(inputSampleL)<1.18e-23) inputSampleL = fpdL * 1.18e-17;
|
||||
if (fabs(inputSampleR)<1.18e-23) inputSampleR = fpdR * 1.18e-17;
|
||||
double drySampleL = inputSampleL;
|
||||
double drySampleR = inputSampleR;
|
||||
|
||||
|
||||
incrementA = ((incrementA*999.0)+targetA)/1000.0;
|
||||
incrementB = ((incrementB*999.0)+targetB)/1000.0;
|
||||
//incrementA is the frequency derez
|
||||
//incrementB is the bit depth derez
|
||||
position += incrementA;
|
||||
|
||||
double outputSampleL = heldSampleL;
|
||||
double outputSampleR = heldSampleR;
|
||||
if (position > 1.0)
|
||||
{
|
||||
position -= 1.0;
|
||||
heldSampleL = (lastSampleL * position) + (inputSampleL * (1.0-position));
|
||||
outputSampleL = (outputSampleL * (1.0-soften)) + (heldSampleL * soften);
|
||||
//softens the edge of the derez
|
||||
heldSampleR = (lastSampleR * position) + (inputSampleR * (1.0-position));
|
||||
outputSampleR = (outputSampleR * (1.0-soften)) + (heldSampleR * soften);
|
||||
//softens the edge of the derez
|
||||
}
|
||||
inputSampleL = outputSampleL;
|
||||
inputSampleR = outputSampleR;
|
||||
|
||||
double tempL = inputSampleL;
|
||||
double tempR = inputSampleR;
|
||||
|
||||
if (inputSampleL != lastOutputSampleL) {
|
||||
tempL = inputSampleL;
|
||||
inputSampleL = (inputSampleL * hard) + (lastDrySampleL * (1.0-hard));
|
||||
//transitions get an intermediate dry sample
|
||||
lastOutputSampleL = tempL; //only one intermediate sample
|
||||
} else {
|
||||
lastOutputSampleL = inputSampleL;
|
||||
}
|
||||
|
||||
if (inputSampleR != lastOutputSampleR) {
|
||||
tempR = inputSampleR;
|
||||
inputSampleR = (inputSampleR * hard) + (lastDrySampleR * (1.0-hard));
|
||||
//transitions get an intermediate dry sample
|
||||
lastOutputSampleR = tempR; //only one intermediate sample
|
||||
} else {
|
||||
lastOutputSampleR = inputSampleR;
|
||||
}
|
||||
|
||||
lastDrySampleL = drySampleL;
|
||||
lastDrySampleR = drySampleR;
|
||||
//freq section of soft/hard interpolates dry samples
|
||||
|
||||
tempL = inputSampleL;
|
||||
tempR = inputSampleR;
|
||||
|
||||
if (inputSampleL > 1.0) inputSampleL = 1.0;
|
||||
if (inputSampleL < -1.0) inputSampleL = -1.0;
|
||||
|
||||
if (inputSampleR > 1.0) inputSampleR = 1.0;
|
||||
if (inputSampleR < -1.0) inputSampleR = -1.0;
|
||||
|
||||
if (inputSampleL > 0) inputSampleL = log(1.0+(255*fabs(inputSampleL))) / log(256);
|
||||
if (inputSampleL < 0) inputSampleL = -log(1.0+(255*fabs(inputSampleL))) / log(256);
|
||||
|
||||
if (inputSampleR > 0) inputSampleR = log(1.0+(255*fabs(inputSampleR))) / log(256);
|
||||
if (inputSampleR < 0) inputSampleR = -log(1.0+(255*fabs(inputSampleR))) / log(256);
|
||||
|
||||
inputSampleL = (tempL * hard) + (inputSampleL * (1.0-hard));
|
||||
inputSampleR = (tempR * hard) + (inputSampleR * (1.0-hard)); //uLaw encode as part of soft/hard
|
||||
|
||||
tempL = inputSampleL;
|
||||
tempR = inputSampleR;
|
||||
|
||||
if (incrementB > 0.0005)
|
||||
{
|
||||
if (inputSampleL > 0)
|
||||
{
|
||||
tempL = inputSampleL;
|
||||
while (tempL > 0) {tempL -= incrementB;}
|
||||
inputSampleL -= tempL;
|
||||
//it's below 0 so subtracting adds the remainder
|
||||
}
|
||||
if (inputSampleR > 0)
|
||||
{
|
||||
tempR = inputSampleR;
|
||||
while (tempR > 0) {tempR -= incrementB;}
|
||||
inputSampleR -= tempR;
|
||||
//it's below 0 so subtracting adds the remainder
|
||||
}
|
||||
|
||||
if (inputSampleL < 0)
|
||||
{
|
||||
tempL = inputSampleL;
|
||||
while (tempL < 0) {tempL += incrementB;}
|
||||
inputSampleL -= tempL;
|
||||
//it's above 0 so subtracting subtracts the remainder
|
||||
}
|
||||
if (inputSampleR < 0)
|
||||
{
|
||||
tempR = inputSampleR;
|
||||
while (tempR < 0) {tempR += incrementB;}
|
||||
inputSampleR -= tempR;
|
||||
//it's above 0 so subtracting subtracts the remainder
|
||||
}
|
||||
|
||||
inputSampleL *= (1.0 - incrementB);
|
||||
inputSampleR *= (1.0 - incrementB);
|
||||
}
|
||||
|
||||
tempL = inputSampleL;
|
||||
tempR = inputSampleR;
|
||||
|
||||
if (inputSampleL > 1.0) inputSampleL = 1.0;
|
||||
if (inputSampleL < -1.0) inputSampleL = -1.0;
|
||||
|
||||
if (inputSampleR > 1.0) inputSampleR = 1.0;
|
||||
if (inputSampleR < -1.0) inputSampleR = -1.0;
|
||||
|
||||
if (inputSampleL > 0) inputSampleL = (pow(256,fabs(inputSampleL))-1.0) / 255;
|
||||
if (inputSampleL < 0) inputSampleL = -(pow(256,fabs(inputSampleL))-1.0) / 255;
|
||||
|
||||
if (inputSampleR > 0) inputSampleR = (pow(256,fabs(inputSampleR))-1.0) / 255;
|
||||
if (inputSampleR < 0) inputSampleR = -(pow(256,fabs(inputSampleR))-1.0) / 255;
|
||||
|
||||
inputSampleL = (tempL * hard) + (inputSampleL * (1.0-hard));
|
||||
inputSampleR = (tempR * hard) + (inputSampleR * (1.0-hard)); //uLaw decode as part of soft/hard
|
||||
|
||||
if (wet !=1.0) {
|
||||
inputSampleL = (inputSampleL * wet) + (drySampleL * (1.0-wet));
|
||||
inputSampleR = (inputSampleR * wet) + (drySampleR * (1.0-wet));
|
||||
}
|
||||
//Dry/Wet control, defaults to the last slider
|
||||
|
||||
lastSampleL = drySampleL;
|
||||
lastSampleR = drySampleR;
|
||||
|
||||
//begin 64 bit stereo floating point dither
|
||||
//int expon; frexp((double)inputSampleL, &expon);
|
||||
fpdL ^= fpdL << 13; fpdL ^= fpdL >> 17; fpdL ^= fpdL << 5;
|
||||
//inputSampleL += ((double(fpdL)-uint32_t(0x7fffffff)) * 1.1e-44l * pow(2,expon+62));
|
||||
//frexp((double)inputSampleR, &expon);
|
||||
fpdR ^= fpdR << 13; fpdR ^= fpdR >> 17; fpdR ^= fpdR << 5;
|
||||
//inputSampleR += ((double(fpdR)-uint32_t(0x7fffffff)) * 1.1e-44l * pow(2,expon+62));
|
||||
//end 64 bit stereo floating point dither
|
||||
|
||||
*out1 = inputSampleL;
|
||||
*out2 = inputSampleR;
|
||||
|
||||
*in1++;
|
||||
*in2++;
|
||||
*out1++;
|
||||
*out2++;
|
||||
}
|
||||
}
|
||||
108
plugin/Source/FX/FXBase.h
Executable file
108
plugin/Source/FX/FXBase.h
Executable file
|
|
@ -0,0 +1,108 @@
|
|||
#pragma once
|
||||
|
||||
#include <JuceHeader.h>
|
||||
|
||||
#define VstInt32 int32_t
|
||||
#define AudioEffect FXBase
|
||||
#define AudioEffectX FXBase
|
||||
#define audioMasterCallback FXBaseCallback
|
||||
#define VstPlugCategory int
|
||||
#define kPlugCategEffect 1
|
||||
#define kVstMaxProgNameLen 32
|
||||
#define kVstMaxParamStrLen 32
|
||||
#define kVstMaxProductStrLen 32
|
||||
#define kVstMaxVendorStrLen 32
|
||||
#define vst_strncpy strncpy
|
||||
|
||||
inline void float2string (float f, char* text, int len)
|
||||
{
|
||||
int decimals = 0;
|
||||
if (std::fabs (f) >= 10.0f)
|
||||
decimals = 1;
|
||||
else if (std::fabs (f) > 1.0f)
|
||||
decimals = 2;
|
||||
else
|
||||
decimals = 3;
|
||||
|
||||
juce::String str (f, decimals);
|
||||
str.copyToUTF8 (text, (size_t)len);
|
||||
}
|
||||
|
||||
inline void int2string (float i, char* text, int len)
|
||||
{
|
||||
juce::String str (i);
|
||||
str.copyToUTF8 (text, (size_t)len);
|
||||
}
|
||||
|
||||
inline void dB2string (float value, char* text, int maxLen)
|
||||
{
|
||||
if (value <= 0)
|
||||
vst_strncpy (text, "-oo", (size_t) maxLen);
|
||||
else
|
||||
float2string (20.0f * log10 (value), text, maxLen);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class FXBaseCallback
|
||||
{
|
||||
public:
|
||||
FXBaseCallback (std::function<double()> cb_) : cb (cb_) {}
|
||||
|
||||
double getSampleRate()
|
||||
{
|
||||
return cb();
|
||||
}
|
||||
|
||||
std::function<double()> cb;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class FXBase
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
FXBase (FXBaseCallback c, int numPrograms_, int numParams_)
|
||||
: numPrograms (numPrograms_), numParams (numParams_), callback (c)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~FXBase() = default;
|
||||
|
||||
int getNumInputs() { return numInputs; }
|
||||
int getNumOutputs() { return numOutputs; }
|
||||
int getNumParameters() { return numParams; }
|
||||
|
||||
//==============================================================================
|
||||
virtual bool getEffectName(char* name) = 0;
|
||||
virtual VstPlugCategory getPlugCategory() = 0;
|
||||
virtual bool getProductString(char* text) = 0;
|
||||
virtual bool getVendorString(char* text) = 0;
|
||||
virtual VstInt32 getVendorVersion() = 0;
|
||||
virtual void processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames) = 0;
|
||||
virtual void processDoubleReplacing (double** inputs, double** outputs, VstInt32 sampleFrames) = 0;
|
||||
virtual void getProgramName(char* name) = 0;
|
||||
virtual void setProgramName(char* name) = 0;
|
||||
virtual VstInt32 getChunk (void** data, bool isPreset) { juce::ignoreUnused (data, isPreset); return 0; }
|
||||
virtual VstInt32 setChunk (void* data, VstInt32 byteSize, bool isPreset) { juce::ignoreUnused (data, byteSize, isPreset); return 0; }
|
||||
virtual float getParameter(VstInt32 index) { juce::ignoreUnused (index); return 0; }
|
||||
virtual void setParameter(VstInt32 index, float value) { juce::ignoreUnused (index, value); }
|
||||
virtual void getParameterLabel(VstInt32 index, char* text) { juce::ignoreUnused (index, text); }
|
||||
virtual void getParameterName(VstInt32 index, char* text) { juce::ignoreUnused (index, text); }
|
||||
virtual void getParameterDisplay(VstInt32 index, char* text) { juce::ignoreUnused (index, text); }
|
||||
virtual VstInt32 canDo(char *text) = 0;
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
void setNumInputs (int numIn) { numInputs = numIn; }
|
||||
void setNumOutputs (int numOut) { numOutputs = numOut; }
|
||||
void setUniqueID (int) {}
|
||||
void canProcessReplacing() {}
|
||||
void canDoubleReplacing() {}
|
||||
void programsAreChunks (bool) {}
|
||||
|
||||
int numInputs = 0, numOutputs = 0, numPrograms = 0, numParams = 0;
|
||||
|
||||
FXBaseCallback callback;
|
||||
|
||||
double getSampleRate() { return callback.getSampleRate(); }
|
||||
};
|
||||
238
plugin/Source/FX/FireAmp.cpp
Executable file
238
plugin/Source/FX/FireAmp.cpp
Executable file
|
|
@ -0,0 +1,238 @@
|
|||
/* ========================================
|
||||
* FireAmp - FireAmp.h
|
||||
* Copyright (c) 2016 airwindows, Airwindows uses the MIT license
|
||||
* ======================================== */
|
||||
|
||||
#ifndef __Gain_H
|
||||
#include "FireAmp.h"
|
||||
#endif
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfloat-conversion", "-Wimplicit-float-conversion", "-Wunused-parameter")
|
||||
|
||||
FireAmp::FireAmp(audioMasterCallback audioMaster) :
|
||||
AudioEffectX(audioMaster, kNumPrograms, kNumParameters)
|
||||
{
|
||||
A = 0.5;
|
||||
B = 0.5;
|
||||
C = 0.8;
|
||||
D = 1.0;
|
||||
|
||||
lastSampleL = 0.0;
|
||||
storeSampleL = 0.0;
|
||||
smoothAL = 0.0;
|
||||
smoothBL = 0.0;
|
||||
smoothCL = 0.0;
|
||||
smoothDL = 0.0;
|
||||
smoothEL = 0.0;
|
||||
smoothFL = 0.0;
|
||||
smoothGL = 0.0;
|
||||
smoothHL = 0.0;
|
||||
smoothIL = 0.0;
|
||||
smoothJL = 0.0;
|
||||
smoothKL = 0.0;
|
||||
smoothLL = 0.0;
|
||||
iirSampleAL = 0.0;
|
||||
iirSampleBL = 0.0;
|
||||
iirSampleCL = 0.0;
|
||||
iirSampleDL = 0.0;
|
||||
iirSampleEL = 0.0;
|
||||
iirSampleFL = 0.0;
|
||||
iirSampleGL = 0.0;
|
||||
iirSampleHL = 0.0;
|
||||
iirSampleIL = 0.0;
|
||||
iirSampleJL = 0.0;
|
||||
iirSampleKL = 0.0;
|
||||
iirSampleLL = 0.0;
|
||||
iirLowpassL = 0.0;
|
||||
iirSpkAL = 0.0;
|
||||
iirSpkBL = 0.0;
|
||||
iirSubL = 0.0;
|
||||
|
||||
lastSampleR = 0.0;
|
||||
storeSampleR = 0.0;
|
||||
smoothAR = 0.0;
|
||||
smoothBR = 0.0;
|
||||
smoothCR = 0.0;
|
||||
smoothDR = 0.0;
|
||||
smoothER = 0.0;
|
||||
smoothFR = 0.0;
|
||||
smoothGR = 0.0;
|
||||
smoothHR = 0.0;
|
||||
smoothIR = 0.0;
|
||||
smoothJR = 0.0;
|
||||
smoothKR = 0.0;
|
||||
smoothLR = 0.0;
|
||||
iirSampleAR = 0.0;
|
||||
iirSampleBR = 0.0;
|
||||
iirSampleCR = 0.0;
|
||||
iirSampleDR = 0.0;
|
||||
iirSampleER = 0.0;
|
||||
iirSampleFR = 0.0;
|
||||
iirSampleGR = 0.0;
|
||||
iirSampleHR = 0.0;
|
||||
iirSampleIR = 0.0;
|
||||
iirSampleJR = 0.0;
|
||||
iirSampleKR = 0.0;
|
||||
iirSampleLR = 0.0;
|
||||
iirLowpassR = 0.0;
|
||||
iirSpkAR = 0.0;
|
||||
iirSpkBR = 0.0;
|
||||
iirSubR = 0.0;
|
||||
|
||||
for (int fcount = 0; fcount < 257; fcount++) {
|
||||
OddL[fcount] = 0.0;
|
||||
EvenL[fcount] = 0.0;
|
||||
OddR[fcount] = 0.0;
|
||||
EvenR[fcount] = 0.0;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
flip = false; //amp
|
||||
|
||||
for(int fcount = 0; fcount < 90; fcount++) {
|
||||
bL[fcount] = 0;
|
||||
bR[fcount] = 0;
|
||||
}
|
||||
smoothCabAL = 0.0; smoothCabBL = 0.0; lastCabSampleL = 0.0; //cab
|
||||
smoothCabAR = 0.0; smoothCabBR = 0.0; lastCabSampleR = 0.0; //cab
|
||||
|
||||
for (int fcount = 0; fcount < 9; fcount++) {
|
||||
lastRefL[fcount] = 0.0;
|
||||
lastRefR[fcount] = 0.0;
|
||||
}
|
||||
cycle = 0; //undersampling
|
||||
|
||||
for (int x = 0; x < fix_total; x++) {
|
||||
fixA[x] = 0.0;
|
||||
fixB[x] = 0.0;
|
||||
fixC[x] = 0.0;
|
||||
fixD[x] = 0.0;
|
||||
fixE[x] = 0.0;
|
||||
fixF[x] = 0.0;
|
||||
} //filtering
|
||||
|
||||
fpdL = 1.0; while (fpdL < 16386) fpdL = rand()*UINT32_MAX;
|
||||
fpdR = 1.0; while (fpdR < 16386) fpdR = rand()*UINT32_MAX;
|
||||
//this is reset: values being initialized only once. Startup values, whatever they are.
|
||||
|
||||
_canDo.insert("plugAsChannelInsert"); // plug-in can be used as a channel insert effect.
|
||||
_canDo.insert("plugAsSend"); // plug-in can be used as a send effect.
|
||||
_canDo.insert("x2in2out");
|
||||
setNumInputs(kNumInputs);
|
||||
setNumOutputs(kNumOutputs);
|
||||
setUniqueID(kUniqueId);
|
||||
canProcessReplacing(); // supports output replacing
|
||||
canDoubleReplacing(); // supports double precision processing
|
||||
programsAreChunks(true);
|
||||
vst_strncpy (_programName, "Default", kVstMaxProgNameLen); // default program name
|
||||
}
|
||||
|
||||
FireAmp::~FireAmp() {}
|
||||
VstInt32 FireAmp::getVendorVersion () {return 1000;}
|
||||
void FireAmp::setProgramName(char *name) {vst_strncpy (_programName, name, kVstMaxProgNameLen);}
|
||||
void FireAmp::getProgramName(char *name) {vst_strncpy (name, _programName, kVstMaxProgNameLen);}
|
||||
//airwindows likes to ignore this stuff. Make your own programs, and make a different plugin rather than
|
||||
//trying to do versioning and preventing people from using older versions. Maybe they like the old one!
|
||||
|
||||
static float pinParameter(float data)
|
||||
{
|
||||
if (data < 0.0f) return 0.0f;
|
||||
if (data > 1.0f) return 1.0f;
|
||||
return data;
|
||||
}
|
||||
|
||||
VstInt32 FireAmp::getChunk (void** data, bool isPreset)
|
||||
{
|
||||
float *chunkData = (float *)calloc(kNumParameters, sizeof(float));
|
||||
chunkData[0] = A;
|
||||
chunkData[1] = B;
|
||||
chunkData[2] = C;
|
||||
chunkData[3] = D;
|
||||
/* Note: The way this is set up, it will break if you manage to save settings on an Intel
|
||||
machine and load them on a PPC Mac. However, it's fine if you stick to the machine you
|
||||
started with. */
|
||||
|
||||
*data = chunkData;
|
||||
return kNumParameters * sizeof(float);
|
||||
}
|
||||
|
||||
VstInt32 FireAmp::setChunk (void* data, VstInt32 byteSize, bool isPreset)
|
||||
{
|
||||
float *chunkData = (float *)data;
|
||||
A = pinParameter(chunkData[0]);
|
||||
B = pinParameter(chunkData[1]);
|
||||
C = pinParameter(chunkData[2]);
|
||||
D = pinParameter(chunkData[3]);
|
||||
/* We're ignoring byteSize as we found it to be a filthy liar */
|
||||
|
||||
/* calculate any other fields you need here - you could copy in
|
||||
code from setParameter() here. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FireAmp::setParameter(VstInt32 index, float value) {
|
||||
switch (index) {
|
||||
case kParamA: A = value; break;
|
||||
case kParamB: B = value; break;
|
||||
case kParamC: C = value; break;
|
||||
case kParamD: D = value; break;
|
||||
default: throw; // unknown parameter, shouldn't happen!
|
||||
}
|
||||
}
|
||||
|
||||
float FireAmp::getParameter(VstInt32 index) {
|
||||
switch (index) {
|
||||
case kParamA: return A; break;
|
||||
case kParamB: return B; break;
|
||||
case kParamC: return C; break;
|
||||
case kParamD: return D; break;
|
||||
default: break; // unknown parameter, shouldn't happen!
|
||||
} return 0.0; //we only need to update the relevant name, this is simple to manage
|
||||
}
|
||||
|
||||
void FireAmp::getParameterName(VstInt32 index, char *text) {
|
||||
switch (index) {
|
||||
case kParamA: vst_strncpy (text, "Gain", kVstMaxParamStrLen); break;
|
||||
case kParamB: vst_strncpy (text, "Tone", kVstMaxParamStrLen); break;
|
||||
case kParamC: vst_strncpy (text, "Output", kVstMaxParamStrLen); break;
|
||||
case kParamD: vst_strncpy (text, "Dry/Wet", kVstMaxParamStrLen); break;
|
||||
default: break; // unknown parameter, shouldn't happen!
|
||||
} //this is our labels for displaying in the VST host
|
||||
}
|
||||
|
||||
void FireAmp::getParameterDisplay(VstInt32 index, char *text) {
|
||||
switch (index) {
|
||||
case kParamA: float2string (A, text, kVstMaxParamStrLen); break;
|
||||
case kParamB: float2string (B, text, kVstMaxParamStrLen); break;
|
||||
case kParamC: float2string (C, text, kVstMaxParamStrLen); break;
|
||||
case kParamD: float2string (D, text, kVstMaxParamStrLen); break;
|
||||
default: break; // unknown parameter, shouldn't happen!
|
||||
} //this displays the values and handles 'popups' where it's discrete choices
|
||||
}
|
||||
|
||||
void FireAmp::getParameterLabel(VstInt32 index, char *text) {
|
||||
switch (index) {
|
||||
case kParamA: vst_strncpy (text, "", kVstMaxParamStrLen); break;
|
||||
case kParamB: vst_strncpy (text, "", kVstMaxParamStrLen); break;
|
||||
case kParamC: vst_strncpy (text, "", kVstMaxParamStrLen); break;
|
||||
case kParamD: vst_strncpy (text, "", kVstMaxParamStrLen); break;
|
||||
default: break; // unknown parameter, shouldn't happen!
|
||||
}
|
||||
}
|
||||
|
||||
VstInt32 FireAmp::canDo(char *text)
|
||||
{ return (_canDo.find(text) == _canDo.end()) ? -1: 1; } // 1 = yes, -1 = no, 0 = don't know
|
||||
|
||||
bool FireAmp::getEffectName(char* name) {
|
||||
vst_strncpy(name, "FireAmp", kVstMaxProductStrLen); return true;
|
||||
}
|
||||
|
||||
VstPlugCategory FireAmp::getPlugCategory() {return kPlugCategEffect;}
|
||||
|
||||
bool FireAmp::getProductString(char* text) {
|
||||
vst_strncpy (text, "airwindows FireAmp", kVstMaxProductStrLen); return true;
|
||||
}
|
||||
|
||||
bool FireAmp::getVendorString(char* text) {
|
||||
vst_strncpy (text, "airwindows", kVstMaxVendorStrLen); return true;
|
||||
}
|
||||
168
plugin/Source/FX/FireAmp.h
Executable file
168
plugin/Source/FX/FireAmp.h
Executable file
|
|
@ -0,0 +1,168 @@
|
|||
/* ========================================
|
||||
* FireAmp - FireAmp.h
|
||||
* Created 8/12/11 by SPIAdmin
|
||||
* Copyright (c) 2011 __MyCompanyName__, Airwindows uses the MIT license
|
||||
* ======================================== */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FXBase.h"
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <math.h>
|
||||
|
||||
class FireAmp :
|
||||
public AudioEffectX
|
||||
{
|
||||
public:
|
||||
enum {
|
||||
kParamA = 0,
|
||||
kParamB = 1,
|
||||
kParamC = 2,
|
||||
kParamD = 3,
|
||||
kNumParameters = 4
|
||||
}; //
|
||||
|
||||
static constexpr int kNumPrograms = 0;
|
||||
static constexpr int kNumInputs = 2;
|
||||
static constexpr int kNumOutputs = 2;
|
||||
static constexpr unsigned long kUniqueId = 'fira'; //Change this to what the AU identity is!
|
||||
|
||||
FireAmp(audioMasterCallback audioMaster);
|
||||
~FireAmp();
|
||||
virtual bool getEffectName(char* name); // The plug-in name
|
||||
virtual VstPlugCategory getPlugCategory(); // The general category for the plug-in
|
||||
virtual bool getProductString(char* text); // This is a unique plug-in string provided by Steinberg
|
||||
virtual bool getVendorString(char* text); // Vendor info
|
||||
virtual VstInt32 getVendorVersion(); // Version number
|
||||
virtual void processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames);
|
||||
virtual void processDoubleReplacing (double** inputs, double** outputs, VstInt32 sampleFrames);
|
||||
virtual void getProgramName(char *name); // read the name from the host
|
||||
virtual void setProgramName(char *name); // changes the name of the preset displayed in the host
|
||||
virtual VstInt32 getChunk (void** data, bool isPreset);
|
||||
virtual VstInt32 setChunk (void* data, VstInt32 byteSize, bool isPreset);
|
||||
virtual float getParameter(VstInt32 index); // get the parameter value at the specified index
|
||||
virtual void setParameter(VstInt32 index, float value); // set the parameter at index to value
|
||||
virtual void getParameterLabel(VstInt32 index, char *text); // label for the parameter (eg dB)
|
||||
virtual void getParameterName(VstInt32 index, char *text); // name of the parameter
|
||||
virtual void getParameterDisplay(VstInt32 index, char *text); // text description of the current value
|
||||
virtual VstInt32 canDo(char *text);
|
||||
private:
|
||||
char _programName[kVstMaxProgNameLen + 1];
|
||||
std::set< std::string > _canDo;
|
||||
|
||||
|
||||
double lastSampleL;
|
||||
double storeSampleL;
|
||||
double smoothAL;
|
||||
double smoothBL;
|
||||
double smoothCL;
|
||||
double smoothDL;
|
||||
double smoothEL;
|
||||
double smoothFL;
|
||||
double smoothGL;
|
||||
double smoothHL;
|
||||
double smoothIL;
|
||||
double smoothJL;
|
||||
double smoothKL;
|
||||
double smoothLL;
|
||||
double iirSampleAL;
|
||||
double iirSampleBL;
|
||||
double iirSampleCL;
|
||||
double iirSampleDL;
|
||||
double iirSampleEL;
|
||||
double iirSampleFL;
|
||||
double iirSampleGL;
|
||||
double iirSampleHL;
|
||||
double iirSampleIL;
|
||||
double iirSampleJL;
|
||||
double iirSampleKL;
|
||||
double iirSampleLL;
|
||||
double iirLowpassL;
|
||||
double iirSpkAL;
|
||||
double iirSpkBL;
|
||||
double iirSubL;
|
||||
double OddL[257];
|
||||
double EvenL[257];
|
||||
|
||||
double lastSampleR;
|
||||
double storeSampleR;
|
||||
double smoothAR;
|
||||
double smoothBR;
|
||||
double smoothCR;
|
||||
double smoothDR;
|
||||
double smoothER;
|
||||
double smoothFR;
|
||||
double smoothGR;
|
||||
double smoothHR;
|
||||
double smoothIR;
|
||||
double smoothJR;
|
||||
double smoothKR;
|
||||
double smoothLR;
|
||||
double iirSampleAR;
|
||||
double iirSampleBR;
|
||||
double iirSampleCR;
|
||||
double iirSampleDR;
|
||||
double iirSampleER;
|
||||
double iirSampleFR;
|
||||
double iirSampleGR;
|
||||
double iirSampleHR;
|
||||
double iirSampleIR;
|
||||
double iirSampleJR;
|
||||
double iirSampleKR;
|
||||
double iirSampleLR;
|
||||
double iirLowpassR;
|
||||
double iirSpkAR;
|
||||
double iirSpkBR;
|
||||
double iirSubR;
|
||||
double OddR[257];
|
||||
double EvenR[257];
|
||||
|
||||
bool flip;
|
||||
int count; //amp
|
||||
|
||||
double bL[90];
|
||||
double lastCabSampleL;
|
||||
double smoothCabAL;
|
||||
double smoothCabBL; //cab
|
||||
|
||||
double bR[90];
|
||||
double lastCabSampleR;
|
||||
double smoothCabAR;
|
||||
double smoothCabBR; //cab
|
||||
|
||||
double lastRefL[10];
|
||||
double lastRefR[10];
|
||||
int cycle; //undersampling
|
||||
|
||||
enum {
|
||||
fix_freq,
|
||||
fix_reso,
|
||||
fix_a0,
|
||||
fix_a1,
|
||||
fix_a2,
|
||||
fix_b1,
|
||||
fix_b2,
|
||||
fix_sL1,
|
||||
fix_sL2,
|
||||
fix_sR1,
|
||||
fix_sR2,
|
||||
fix_total
|
||||
}; //fixed frequency biquad filter for ultrasonics, stereo
|
||||
double fixA[fix_total];
|
||||
double fixB[fix_total];
|
||||
double fixC[fix_total];
|
||||
double fixD[fix_total];
|
||||
double fixE[fix_total];
|
||||
double fixF[fix_total]; //filtering
|
||||
|
||||
uint32_t fpdL;
|
||||
uint32_t fpdR;
|
||||
//default stuff
|
||||
|
||||
float A;
|
||||
float B;
|
||||
float C;
|
||||
float D;
|
||||
};
|
||||
1709
plugin/Source/FX/FireAmpProc.cpp
Executable file
1709
plugin/Source/FX/FireAmpProc.cpp
Executable file
File diff suppressed because it is too large
Load diff
258
plugin/Source/FX/GrindAmp.cpp
Executable file
258
plugin/Source/FX/GrindAmp.cpp
Executable file
|
|
@ -0,0 +1,258 @@
|
|||
/* ========================================
|
||||
* GrindAmp - GrindAmp.h
|
||||
* Copyright (c) 2016 airwindows, Airwindows uses the MIT license
|
||||
* ======================================== */
|
||||
|
||||
#ifndef __Gain_H
|
||||
#include "GrindAmp.h"
|
||||
#endif
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfloat-conversion", "-Wimplicit-float-conversion", "-Wunused-parameter")
|
||||
|
||||
GrindAmp::GrindAmp(audioMasterCallback audioMaster) :
|
||||
AudioEffectX(audioMaster, kNumPrograms, kNumParameters)
|
||||
{
|
||||
A = 0.5;
|
||||
B = 0.5;
|
||||
C = 0.8;
|
||||
D = 1.0;
|
||||
|
||||
smoothAL = 0.0;
|
||||
smoothBL = 0.0;
|
||||
smoothCL = 0.0;
|
||||
smoothDL = 0.0;
|
||||
smoothEL = 0.0;
|
||||
smoothFL = 0.0;
|
||||
smoothGL = 0.0;
|
||||
smoothHL = 0.0;
|
||||
smoothIL = 0.0;
|
||||
smoothJL = 0.0;
|
||||
smoothKL = 0.0;
|
||||
secondAL = 0.0;
|
||||
secondBL = 0.0;
|
||||
secondCL = 0.0;
|
||||
secondDL = 0.0;
|
||||
secondEL = 0.0;
|
||||
secondFL = 0.0;
|
||||
secondGL = 0.0;
|
||||
secondHL = 0.0;
|
||||
secondIL = 0.0;
|
||||
secondJL = 0.0;
|
||||
secondKL = 0.0;
|
||||
thirdAL = 0.0;
|
||||
thirdBL = 0.0;
|
||||
thirdCL = 0.0;
|
||||
thirdDL = 0.0;
|
||||
thirdEL = 0.0;
|
||||
thirdFL = 0.0;
|
||||
thirdGL = 0.0;
|
||||
thirdHL = 0.0;
|
||||
thirdIL = 0.0;
|
||||
thirdJL = 0.0;
|
||||
thirdKL = 0.0;
|
||||
iirSampleAL = 0.0;
|
||||
iirSampleBL = 0.0;
|
||||
iirSampleCL = 0.0;
|
||||
iirSampleDL = 0.0;
|
||||
iirSampleEL = 0.0;
|
||||
iirSampleFL = 0.0;
|
||||
iirSampleGL = 0.0;
|
||||
iirSampleHL = 0.0;
|
||||
iirSampleIL = 0.0;
|
||||
iirLowpassL = 0.0;
|
||||
iirSubL = 0.0;
|
||||
storeSampleL = 0.0; //amp
|
||||
|
||||
smoothAR = 0.0;
|
||||
smoothBR = 0.0;
|
||||
smoothCR = 0.0;
|
||||
smoothDR = 0.0;
|
||||
smoothER = 0.0;
|
||||
smoothFR = 0.0;
|
||||
smoothGR = 0.0;
|
||||
smoothHR = 0.0;
|
||||
smoothIR = 0.0;
|
||||
smoothJR = 0.0;
|
||||
smoothKR = 0.0;
|
||||
secondAR = 0.0;
|
||||
secondBR = 0.0;
|
||||
secondCR = 0.0;
|
||||
secondDR = 0.0;
|
||||
secondER = 0.0;
|
||||
secondFR = 0.0;
|
||||
secondGR = 0.0;
|
||||
secondHR = 0.0;
|
||||
secondIR = 0.0;
|
||||
secondJR = 0.0;
|
||||
secondKR = 0.0;
|
||||
thirdAR = 0.0;
|
||||
thirdBR = 0.0;
|
||||
thirdCR = 0.0;
|
||||
thirdDR = 0.0;
|
||||
thirdER = 0.0;
|
||||
thirdFR = 0.0;
|
||||
thirdGR = 0.0;
|
||||
thirdHR = 0.0;
|
||||
thirdIR = 0.0;
|
||||
thirdJR = 0.0;
|
||||
thirdKR = 0.0;
|
||||
iirSampleAR = 0.0;
|
||||
iirSampleBR = 0.0;
|
||||
iirSampleCR = 0.0;
|
||||
iirSampleDR = 0.0;
|
||||
iirSampleER = 0.0;
|
||||
iirSampleFR = 0.0;
|
||||
iirSampleGR = 0.0;
|
||||
iirSampleHR = 0.0;
|
||||
iirSampleIR = 0.0;
|
||||
iirLowpassR = 0.0;
|
||||
iirSubR = 0.0;
|
||||
storeSampleR = 0.0; //amp
|
||||
|
||||
for(int fcount = 0; fcount < 90; fcount++) {
|
||||
bL[fcount] = 0;
|
||||
bR[fcount] = 0;
|
||||
}
|
||||
smoothCabAL = 0.0; smoothCabBL = 0.0; lastCabSampleL = 0.0; //cab
|
||||
smoothCabAR = 0.0; smoothCabBR = 0.0; lastCabSampleR = 0.0; //cab
|
||||
|
||||
for (int fcount = 0; fcount < 9; fcount++) {
|
||||
lastRefL[fcount] = 0.0;
|
||||
lastRefR[fcount] = 0.0;
|
||||
}
|
||||
cycle = 0; //undersampling
|
||||
|
||||
for (int x = 0; x < fix_total; x++) {
|
||||
fixA[x] = 0.0;
|
||||
fixB[x] = 0.0;
|
||||
fixC[x] = 0.0;
|
||||
fixD[x] = 0.0;
|
||||
fixE[x] = 0.0;
|
||||
fixF[x] = 0.0;
|
||||
} //filtering
|
||||
|
||||
fpdL = 1.0; while (fpdL < 16386) fpdL = rand()*UINT32_MAX;
|
||||
fpdR = 1.0; while (fpdR < 16386) fpdR = rand()*UINT32_MAX;
|
||||
//this is reset: values being initialized only once. Startup values, whatever they are.
|
||||
|
||||
_canDo.insert("plugAsChannelInsert"); // plug-in can be used as a channel insert effect.
|
||||
_canDo.insert("plugAsSend"); // plug-in can be used as a send effect.
|
||||
_canDo.insert("x2in2out");
|
||||
setNumInputs(kNumInputs);
|
||||
setNumOutputs(kNumOutputs);
|
||||
setUniqueID(kUniqueId);
|
||||
canProcessReplacing(); // supports output replacing
|
||||
canDoubleReplacing(); // supports double precision processing
|
||||
programsAreChunks(true);
|
||||
vst_strncpy (_programName, "Default", kVstMaxProgNameLen); // default program name
|
||||
}
|
||||
|
||||
GrindAmp::~GrindAmp() {}
|
||||
VstInt32 GrindAmp::getVendorVersion () {return 1000;}
|
||||
void GrindAmp::setProgramName(char *name) {vst_strncpy (_programName, name, kVstMaxProgNameLen);}
|
||||
void GrindAmp::getProgramName(char *name) {vst_strncpy (name, _programName, kVstMaxProgNameLen);}
|
||||
//airwindows likes to ignore this stuff. Make your own programs, and make a different plugin rather than
|
||||
//trying to do versioning and preventing people from using older versions. Maybe they like the old one!
|
||||
|
||||
static float pinParameter(float data)
|
||||
{
|
||||
if (data < 0.0f) return 0.0f;
|
||||
if (data > 1.0f) return 1.0f;
|
||||
return data;
|
||||
}
|
||||
|
||||
VstInt32 GrindAmp::getChunk (void** data, bool isPreset)
|
||||
{
|
||||
float *chunkData = (float *)calloc(kNumParameters, sizeof(float));
|
||||
chunkData[0] = A;
|
||||
chunkData[1] = B;
|
||||
chunkData[2] = C;
|
||||
chunkData[3] = D;
|
||||
/* Note: The way this is set up, it will break if you manage to save settings on an Intel
|
||||
machine and load them on a PPC Mac. However, it's fine if you stick to the machine you
|
||||
started with. */
|
||||
|
||||
*data = chunkData;
|
||||
return kNumParameters * sizeof(float);
|
||||
}
|
||||
|
||||
VstInt32 GrindAmp::setChunk (void* data, VstInt32 byteSize, bool isPreset)
|
||||
{
|
||||
float *chunkData = (float *)data;
|
||||
A = pinParameter(chunkData[0]);
|
||||
B = pinParameter(chunkData[1]);
|
||||
C = pinParameter(chunkData[2]);
|
||||
D = pinParameter(chunkData[3]);
|
||||
/* We're ignoring byteSize as we found it to be a filthy liar */
|
||||
|
||||
/* calculate any other fields you need here - you could copy in
|
||||
code from setParameter() here. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GrindAmp::setParameter(VstInt32 index, float value) {
|
||||
switch (index) {
|
||||
case kParamA: A = value; break;
|
||||
case kParamB: B = value; break;
|
||||
case kParamC: C = value; break;
|
||||
case kParamD: D = value; break;
|
||||
default: throw; // unknown parameter, shouldn't happen!
|
||||
}
|
||||
}
|
||||
|
||||
float GrindAmp::getParameter(VstInt32 index) {
|
||||
switch (index) {
|
||||
case kParamA: return A; break;
|
||||
case kParamB: return B; break;
|
||||
case kParamC: return C; break;
|
||||
case kParamD: return D; break;
|
||||
default: break; // unknown parameter, shouldn't happen!
|
||||
} return 0.0; //we only need to update the relevant name, this is simple to manage
|
||||
}
|
||||
|
||||
void GrindAmp::getParameterName(VstInt32 index, char *text) {
|
||||
switch (index) {
|
||||
case kParamA: vst_strncpy (text, "Gain", kVstMaxParamStrLen); break;
|
||||
case kParamB: vst_strncpy (text, "Tone", kVstMaxParamStrLen); break;
|
||||
case kParamC: vst_strncpy (text, "Output", kVstMaxParamStrLen); break;
|
||||
case kParamD: vst_strncpy (text, "Dry/Wet", kVstMaxParamStrLen); break;
|
||||
default: break; // unknown parameter, shouldn't happen!
|
||||
} //this is our labels for displaying in the VST host
|
||||
}
|
||||
|
||||
void GrindAmp::getParameterDisplay(VstInt32 index, char *text) {
|
||||
switch (index) {
|
||||
case kParamA: float2string (A, text, kVstMaxParamStrLen); break;
|
||||
case kParamB: float2string (B, text, kVstMaxParamStrLen); break;
|
||||
case kParamC: float2string (C, text, kVstMaxParamStrLen); break;
|
||||
case kParamD: float2string (D, text, kVstMaxParamStrLen); break;
|
||||
default: break; // unknown parameter, shouldn't happen!
|
||||
} //this displays the values and handles 'popups' where it's discrete choices
|
||||
}
|
||||
|
||||
void GrindAmp::getParameterLabel(VstInt32 index, char *text) {
|
||||
switch (index) {
|
||||
case kParamA: vst_strncpy (text, "", kVstMaxParamStrLen); break;
|
||||
case kParamB: vst_strncpy (text, "", kVstMaxParamStrLen); break;
|
||||
case kParamC: vst_strncpy (text, "", kVstMaxParamStrLen); break;
|
||||
case kParamD: vst_strncpy (text, "", kVstMaxParamStrLen); break;
|
||||
default: break; // unknown parameter, shouldn't happen!
|
||||
}
|
||||
}
|
||||
|
||||
VstInt32 GrindAmp::canDo(char *text)
|
||||
{ return (_canDo.find(text) == _canDo.end()) ? -1: 1; } // 1 = yes, -1 = no, 0 = don't know
|
||||
|
||||
bool GrindAmp::getEffectName(char* name) {
|
||||
vst_strncpy(name, "GrindAmp", kVstMaxProductStrLen); return true;
|
||||
}
|
||||
|
||||
VstPlugCategory GrindAmp::getPlugCategory() {return kPlugCategEffect;}
|
||||
|
||||
bool GrindAmp::getProductString(char* text) {
|
||||
vst_strncpy (text, "airwindows GrindAmp", kVstMaxProductStrLen); return true;
|
||||
}
|
||||
|
||||
bool GrindAmp::getVendorString(char* text) {
|
||||
vst_strncpy (text, "airwindows", kVstMaxVendorStrLen); return true;
|
||||
}
|
||||
190
plugin/Source/FX/GrindAmp.h
Executable file
190
plugin/Source/FX/GrindAmp.h
Executable file
|
|
@ -0,0 +1,190 @@
|
|||
/* ========================================
|
||||
* GrindAmp - GrindAmp.h
|
||||
* Created 8/12/11 by SPIAdmin
|
||||
* Copyright (c) 2011 __MyCompanyName__, Airwindows uses the MIT license
|
||||
* ======================================== */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FXBase.h"
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <math.h>
|
||||
|
||||
class GrindAmp :
|
||||
public AudioEffectX
|
||||
{
|
||||
public:
|
||||
enum {
|
||||
kParamA = 0,
|
||||
kParamB = 1,
|
||||
kParamC = 2,
|
||||
kParamD = 3,
|
||||
kNumParameters = 4
|
||||
}; //
|
||||
|
||||
static constexpr int kNumPrograms = 0;
|
||||
static constexpr int kNumInputs = 2;
|
||||
static constexpr int kNumOutputs = 2;
|
||||
static constexpr unsigned long kUniqueId = 'grda'; //Change this to what the AU identity is!
|
||||
|
||||
GrindAmp(audioMasterCallback audioMaster);
|
||||
~GrindAmp();
|
||||
virtual bool getEffectName(char* name); // The plug-in name
|
||||
virtual VstPlugCategory getPlugCategory(); // The general category for the plug-in
|
||||
virtual bool getProductString(char* text); // This is a unique plug-in string provided by Steinberg
|
||||
virtual bool getVendorString(char* text); // Vendor info
|
||||
virtual VstInt32 getVendorVersion(); // Version number
|
||||
virtual void processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames);
|
||||
virtual void processDoubleReplacing (double** inputs, double** outputs, VstInt32 sampleFrames);
|
||||
virtual void getProgramName(char *name); // read the name from the host
|
||||
virtual void setProgramName(char *name); // changes the name of the preset displayed in the host
|
||||
virtual VstInt32 getChunk (void** data, bool isPreset);
|
||||
virtual VstInt32 setChunk (void* data, VstInt32 byteSize, bool isPreset);
|
||||
virtual float getParameter(VstInt32 index); // get the parameter value at the specified index
|
||||
virtual void setParameter(VstInt32 index, float value); // set the parameter at index to value
|
||||
virtual void getParameterLabel(VstInt32 index, char *text); // label for the parameter (eg dB)
|
||||
virtual void getParameterName(VstInt32 index, char *text); // name of the parameter
|
||||
virtual void getParameterDisplay(VstInt32 index, char *text); // text description of the current value
|
||||
virtual VstInt32 canDo(char *text);
|
||||
private:
|
||||
char _programName[kVstMaxProgNameLen + 1];
|
||||
std::set< std::string > _canDo;
|
||||
|
||||
double smoothAL;
|
||||
double smoothBL;
|
||||
double smoothCL;
|
||||
double smoothDL;
|
||||
double smoothEL;
|
||||
double smoothFL;
|
||||
double smoothGL;
|
||||
double smoothHL;
|
||||
double smoothIL;
|
||||
double smoothJL;
|
||||
double smoothKL;
|
||||
double secondAL;
|
||||
double secondBL;
|
||||
double secondCL;
|
||||
double secondDL;
|
||||
double secondEL;
|
||||
double secondFL;
|
||||
double secondGL;
|
||||
double secondHL;
|
||||
double secondIL;
|
||||
double secondJL;
|
||||
double secondKL;
|
||||
double thirdAL;
|
||||
double thirdBL;
|
||||
double thirdCL;
|
||||
double thirdDL;
|
||||
double thirdEL;
|
||||
double thirdFL;
|
||||
double thirdGL;
|
||||
double thirdHL;
|
||||
double thirdIL;
|
||||
double thirdJL;
|
||||
double thirdKL;
|
||||
double iirSampleAL;
|
||||
double iirSampleBL;
|
||||
double iirSampleCL;
|
||||
double iirSampleDL;
|
||||
double iirSampleEL;
|
||||
double iirSampleFL;
|
||||
double iirSampleGL;
|
||||
double iirSampleHL;
|
||||
double iirSampleIL;
|
||||
double iirLowpassL;
|
||||
double iirSubL;
|
||||
double storeSampleL; //amp
|
||||
|
||||
double smoothAR;
|
||||
double smoothBR;
|
||||
double smoothCR;
|
||||
double smoothDR;
|
||||
double smoothER;
|
||||
double smoothFR;
|
||||
double smoothGR;
|
||||
double smoothHR;
|
||||
double smoothIR;
|
||||
double smoothJR;
|
||||
double smoothKR;
|
||||
double secondAR;
|
||||
double secondBR;
|
||||
double secondCR;
|
||||
double secondDR;
|
||||
double secondER;
|
||||
double secondFR;
|
||||
double secondGR;
|
||||
double secondHR;
|
||||
double secondIR;
|
||||
double secondJR;
|
||||
double secondKR;
|
||||
double thirdAR;
|
||||
double thirdBR;
|
||||
double thirdCR;
|
||||
double thirdDR;
|
||||
double thirdER;
|
||||
double thirdFR;
|
||||
double thirdGR;
|
||||
double thirdHR;
|
||||
double thirdIR;
|
||||
double thirdJR;
|
||||
double thirdKR;
|
||||
double iirSampleAR;
|
||||
double iirSampleBR;
|
||||
double iirSampleCR;
|
||||
double iirSampleDR;
|
||||
double iirSampleER;
|
||||
double iirSampleFR;
|
||||
double iirSampleGR;
|
||||
double iirSampleHR;
|
||||
double iirSampleIR;
|
||||
double iirLowpassR;
|
||||
double iirSubR;
|
||||
double storeSampleR; //amp
|
||||
|
||||
double bL[90];
|
||||
double lastCabSampleL;
|
||||
double smoothCabAL;
|
||||
double smoothCabBL; //cab
|
||||
|
||||
double bR[90];
|
||||
double lastCabSampleR;
|
||||
double smoothCabAR;
|
||||
double smoothCabBR; //cab
|
||||
|
||||
double lastRefL[10];
|
||||
double lastRefR[10];
|
||||
int cycle; //undersampling
|
||||
|
||||
enum {
|
||||
fix_freq,
|
||||
fix_reso,
|
||||
fix_a0,
|
||||
fix_a1,
|
||||
fix_a2,
|
||||
fix_b1,
|
||||
fix_b2,
|
||||
fix_sL1,
|
||||
fix_sL2,
|
||||
fix_sR1,
|
||||
fix_sR2,
|
||||
fix_total
|
||||
}; //fixed frequency biquad filter for ultrasonics, stereo
|
||||
double fixA[fix_total];
|
||||
double fixB[fix_total];
|
||||
double fixC[fix_total];
|
||||
double fixD[fix_total];
|
||||
double fixE[fix_total];
|
||||
double fixF[fix_total]; //filtering
|
||||
|
||||
uint32_t fpdL;
|
||||
uint32_t fpdR;
|
||||
//default stuff
|
||||
|
||||
float A;
|
||||
float B;
|
||||
float C;
|
||||
float D;
|
||||
};
|
||||
1652
plugin/Source/FX/GrindAmpProc.cpp
Executable file
1652
plugin/Source/FX/GrindAmpProc.cpp
Executable file
File diff suppressed because it is too large
Load diff
|
|
@ -638,15 +638,16 @@ public:
|
|||
getProperties().set ("fxId", fxGate);
|
||||
|
||||
addEnable (proc.gateParams.enable);
|
||||
getHeader().setMouseCursor (juce::MouseCursor::LeftRightResizeCursor);
|
||||
|
||||
addControl (new gin::Select (proc.gateParams.beat), 0, 0);
|
||||
addControl (new gin::Knob (proc.gateParams.length), 1, 0);
|
||||
addControl (new gin::Knob (proc.gateParams.attack), 2, 0);
|
||||
addControl (new gin::Knob (proc.gateParams.release), 3, 0);
|
||||
addControl (new gin::Select (proc.gateParams.beat));
|
||||
addControl (new gin::Knob (proc.gateParams.length));
|
||||
addControl (new gin::Knob (proc.gateParams.attack));
|
||||
addControl (new gin::Knob (proc.gateParams.release));
|
||||
|
||||
auto g = new gin::GateEffectComponent (Cfg::numGateSteps);
|
||||
g->setParams (proc.gateParams.length, proc.gateParams.l, proc.gateParams.r, proc.gateParams.enable);
|
||||
addControl (g, 0, 1, 4, 1);
|
||||
addControl (g);
|
||||
}
|
||||
|
||||
WavetableAudioProcessor& proc;
|
||||
|
|
@ -663,6 +664,7 @@ public:
|
|||
getProperties().set ("fxId", fxChorus);
|
||||
|
||||
addEnable (proc.chorusParams.enable);
|
||||
getHeader().setMouseCursor (juce::MouseCursor::LeftRightResizeCursor);
|
||||
|
||||
addControl (new gin::Knob (proc.chorusParams.delay), 0, 0);
|
||||
addControl (new gin::Knob (proc.chorusParams.rate), 1, 0);
|
||||
|
|
@ -686,11 +688,110 @@ public:
|
|||
getProperties().set ("fxId", fxDistort);
|
||||
|
||||
addEnable (proc.distortionParams.enable);
|
||||
getHeader().setMouseCursor (juce::MouseCursor::LeftRightResizeCursor);
|
||||
|
||||
addControl (new gin::Knob (proc.distortionParams.amount), 0, 0);
|
||||
{
|
||||
page1 = new juce::Component ("page1");
|
||||
page1->setInterceptsMouseClicks (false, true);
|
||||
|
||||
addControl (page1);
|
||||
|
||||
addControl (page1, new gin::Knob (proc.distortionParams.amount));
|
||||
}
|
||||
|
||||
{
|
||||
page2 = new juce::Component ("page2");
|
||||
page2->setInterceptsMouseClicks (false, true);
|
||||
|
||||
addControl (page2);
|
||||
|
||||
addControl (page2, new gin::Knob (proc.bitcrusherParams.rate));
|
||||
addControl (page2, new gin::Knob (proc.bitcrusherParams.rez));
|
||||
addControl (page2, new gin::Knob (proc.bitcrusherParams.hard));
|
||||
addControl (page2, new gin::Knob (proc.bitcrusherParams.mix));
|
||||
}
|
||||
|
||||
{
|
||||
page3 = new juce::Component ("page3");
|
||||
page3->setInterceptsMouseClicks (false, true);
|
||||
|
||||
addControl (page3);
|
||||
|
||||
addControl (page3, new gin::Knob (proc.fireAmpParams.gain));
|
||||
addControl (page3, new gin::Knob (proc.fireAmpParams.tone));
|
||||
addControl (page3, new gin::Knob (proc.fireAmpParams.output));
|
||||
addControl (page3, new gin::Knob (proc.fireAmpParams.mix));
|
||||
}
|
||||
|
||||
{
|
||||
page4 = new juce::Component ("page4");
|
||||
page4->setInterceptsMouseClicks (false, true);
|
||||
|
||||
addControl (page4);
|
||||
|
||||
addControl (page4, new gin::Knob (proc.grindAmpParams.gain));
|
||||
addControl (page4, new gin::Knob (proc.grindAmpParams.tone));
|
||||
addControl (page4, new gin::Knob (proc.grindAmpParams.output));
|
||||
addControl (page4, new gin::Knob (proc.grindAmpParams.mix));
|
||||
}
|
||||
|
||||
auto& h = getHeader();
|
||||
h.addAndMakeVisible (modeButton);
|
||||
modeButton.onClick = [this]
|
||||
{
|
||||
juce::PopupMenu m;
|
||||
m.setLookAndFeel (&getLookAndFeel());
|
||||
|
||||
m.addItem ("Simple", true, proc.fxParams.distMode->getUserValueInt() == 0, [this]
|
||||
{
|
||||
proc.fxParams.distMode->setUserValue (0.0f);
|
||||
});
|
||||
m.addItem ("Bitcrusher", true, proc.fxParams.distMode->getUserValueInt() == 1, [this]
|
||||
{
|
||||
proc.fxParams.distMode->setUserValue (1.0f);
|
||||
});
|
||||
m.addItem ("Fire Amp", true, proc.fxParams.distMode->getUserValueInt() == 2, [this]
|
||||
{
|
||||
proc.fxParams.distMode->setUserValue (2.0f);
|
||||
});
|
||||
m.addItem ("Grind Amp", true, proc.fxParams.distMode->getUserValueInt() == 3, [this]
|
||||
{
|
||||
proc.fxParams.distMode->setUserValue (3.0f);
|
||||
});
|
||||
|
||||
m.showMenuAsync ({});
|
||||
};
|
||||
|
||||
watchParam (proc.fxParams.distMode);
|
||||
paramChanged();
|
||||
}
|
||||
|
||||
void paramChanged() override
|
||||
{
|
||||
gin::ParamBox::paramChanged();
|
||||
|
||||
auto mode = proc.fxParams.distMode->getUserValueInt();
|
||||
|
||||
page1->setVisible (mode == 0);
|
||||
page2->setVisible (mode == 1);
|
||||
page3->setVisible (mode == 2);
|
||||
page4->setVisible (mode == 3);
|
||||
|
||||
auto& h = getHeader();
|
||||
if (mode == 0) h.setTitle ("Dist");
|
||||
if (mode == 1) h.setTitle ("Crush");
|
||||
if (mode == 2) h.setTitle ("Fire");
|
||||
if (mode == 3) h.setTitle ("Grind");
|
||||
}
|
||||
|
||||
juce::Component* page1;
|
||||
juce::Component* page2;
|
||||
juce::Component* page3;
|
||||
juce::Component* page4;
|
||||
|
||||
WavetableAudioProcessor& proc;
|
||||
|
||||
gin::SVGButton modeButton { "mode", gin::Assets::caretDown, 6 };
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -704,6 +805,7 @@ public:
|
|||
getProperties().set ("fxId", fxDelay);
|
||||
|
||||
addEnable (proc.delayParams.enable);
|
||||
getHeader().setMouseCursor (juce::MouseCursor::LeftRightResizeCursor);
|
||||
|
||||
addControl (t = new gin::Knob (proc.delayParams.time), 0, 0);
|
||||
addControl (b = new gin::Select (proc.delayParams.beat), 0, 0);
|
||||
|
|
@ -742,6 +844,7 @@ public:
|
|||
getProperties().set ("fxId", fxReverb);
|
||||
|
||||
addEnable (proc.reverbParams.enable);
|
||||
getHeader().setMouseCursor (juce::MouseCursor::LeftRightResizeCursor);
|
||||
|
||||
addControl (new gin::Knob (proc.reverbParams.size), 0, 0);
|
||||
addControl (new gin::Knob (proc.reverbParams.decay), 1, 0);
|
||||
|
|
|
|||
|
|
@ -365,11 +365,48 @@ void WavetableAudioProcessor::ReverbParams::setup (WavetableAudioProcessor& p)
|
|||
//==============================================================================
|
||||
void WavetableAudioProcessor::FXParams::setup (WavetableAudioProcessor& p)
|
||||
{
|
||||
fx1 = p.addIntParam ("fxOrder1", "FX1", "", "", { 0.0, 4.0, 1.0, 1.0 }, fxGate, 0.0f);
|
||||
fx2 = p.addIntParam ("fxOrder2", "FX2", "", "", { 0.0, 4.0, 1.0, 1.0 }, fxChorus, 0.0f);
|
||||
fx3 = p.addIntParam ("fxOrder3", "FX3", "", "", { 0.0, 4.0, 1.0, 1.0 }, fxDistort, 0.0f);
|
||||
fx4 = p.addIntParam ("fxOrder4", "FX4", "", "", { 0.0, 4.0, 1.0, 1.0 }, fxDelay, 0.0f);
|
||||
fx5 = p.addIntParam ("fxOrder5", "FX5", "", "", { 0.0, 4.0, 1.0, 1.0 }, fxReverb, 0.0f);
|
||||
fx1 = p.addIntParam ("fxOrder1", "FX1", "", "", { 0.0, 4.0, 1.0, 1.0 }, fxGate, 0.0f);
|
||||
fx2 = p.addIntParam ("fxOrder2", "FX2", "", "", { 0.0, 4.0, 1.0, 1.0 }, fxChorus, 0.0f);
|
||||
fx3 = p.addIntParam ("fxOrder3", "FX3", "", "", { 0.0, 4.0, 1.0, 1.0 }, fxDistort, 0.0f);
|
||||
fx4 = p.addIntParam ("fxOrder4", "FX4", "", "", { 0.0, 4.0, 1.0, 1.0 }, fxDelay, 0.0f);
|
||||
fx5 = p.addIntParam ("fxOrder5", "FX5", "", "", { 0.0, 4.0, 1.0, 1.0 }, fxReverb, 0.0f);
|
||||
distMode = p.addIntParam ("distMode", "Dist Mode", "", "", { 0.0, 3.0, 1.0, 1.0 }, 0, 0.0f);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void WavetableAudioProcessor::BitCrusherParams::setup (WavetableAudioProcessor& p)
|
||||
{
|
||||
juce::String id = "crush";
|
||||
juce::String nm = "Crush ";
|
||||
|
||||
rate = p.addExtParam (id + "rate", nm + "Rate", "Rate", "", { 0.0, 1.0, 0.0, 1.0f }, 0.5f, 0.0f);
|
||||
rez = p.addExtParam (id + "rez", nm + "Rez", "Rez", "", { 0.0, 1.0, 0.0, 1.0f }, 0.5f, 0.0f);
|
||||
hard = p.addExtParam (id + "hard", nm + "Hard", "Hard", "", { 0.0, 1.0, 0.0, 1.0f }, 0.8f, 0.0f);
|
||||
mix = p.addExtParam (id + "mix", nm + "Mix", "Mix", "", { 0.0, 1.0, 0.0, 1.0f }, 1.0f, 0.0f);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void WavetableAudioProcessor::FireAmpParams::setup (WavetableAudioProcessor& p)
|
||||
{
|
||||
juce::String id = "fire";
|
||||
juce::String nm = "Fire ";
|
||||
|
||||
gain = p.addExtParam (id + "gain", nm + "Gain", "Gain", "", { 0.0, 1.0, 0.0, 1.0f }, 0.5f, 0.0f);
|
||||
tone = p.addExtParam (id + "tone", nm + "Tone", "Tone", "", { 0.0, 1.0, 0.0, 1.0f }, 0.5f, 0.0f);
|
||||
output = p.addExtParam (id + "output", nm + "Output", "Output", "", { 0.0, 1.0, 0.0, 1.0f }, 0.8f, 0.0f);
|
||||
mix = p.addExtParam (id + "mix", nm + "Mix", "Mix", "", { 0.0, 1.0, 0.0, 1.0f }, 1.0f, 0.0f);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void WavetableAudioProcessor::GrindAmpParams::setup (WavetableAudioProcessor& p)
|
||||
{
|
||||
juce::String id = "grind";
|
||||
juce::String nm = "Grind ";
|
||||
|
||||
gain = p.addExtParam (id + "gain", nm + "Gain", "Gain", "", { 0.0, 1.0, 0.0, 1.0f }, 0.5f, 0.0f);
|
||||
tone = p.addExtParam (id + "tone", nm + "Tone", "Tone", "", { 0.0, 1.0, 0.0, 1.0f }, 0.5f, 0.0f);
|
||||
output = p.addExtParam (id + "output", nm + "Output", "Output", "", { 0.0, 1.0, 0.0, 1.0f }, 0.8f, 0.0f);
|
||||
mix = p.addExtParam (id + "mix", nm + "Mix", "Mix", "", { 0.0, 1.0, 0.0, 1.0f }, 1.0f, 0.0f);
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
|
@ -467,7 +504,10 @@ void extractWavetables()
|
|||
|
||||
//==============================================================================
|
||||
WavetableAudioProcessor::WavetableAudioProcessor()
|
||||
: gin::Processor (juce::AudioProcessor::BusesProperties().withOutput ("Output", juce::AudioChannelSet::stereo(), true), false)
|
||||
: gin::Processor (juce::AudioProcessor::BusesProperties().withOutput ("Output", juce::AudioChannelSet::stereo(), true), false),
|
||||
bitcrusher (FXBaseCallback ([this] { return gin::Processor::getSampleRate(); })),
|
||||
fireAmp (FXBaseCallback ([this] { return gin::Processor::getSampleRate(); })),
|
||||
grindAmp (FXBaseCallback ([this] { return gin::Processor::getSampleRate(); }))
|
||||
{
|
||||
{
|
||||
auto sz = 0;
|
||||
|
|
@ -510,6 +550,11 @@ WavetableAudioProcessor::WavetableAudioProcessor()
|
|||
reverbParams.setup (*this);
|
||||
fxParams.setup (*this);
|
||||
|
||||
versionHint++;
|
||||
bitcrusherParams.setup (*this);
|
||||
fireAmpParams.setup (*this);
|
||||
grindAmpParams.setup (*this);
|
||||
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
auto voice = new WavetableVoice (*this);
|
||||
|
|
@ -885,8 +930,24 @@ void WavetableAudioProcessor::applyEffect (juce::AudioSampleBuffer& buffer, int
|
|||
// Apply Distortion
|
||||
if (fxId == fxDistort && distortionParams.enable->isOn())
|
||||
{
|
||||
auto clip = 1.0f / (2.0f * distortionVal);
|
||||
gin::Distortion::processBlock (buffer, distortionVal, -clip, clip);
|
||||
auto mode = fxParams.distMode->getUserValueInt();
|
||||
if (mode == 0)
|
||||
{
|
||||
auto clip = 1.0f / (2.0f * distortionVal);
|
||||
gin::Distortion::processBlock (buffer, distortionVal, -clip, clip);
|
||||
}
|
||||
else if (mode == 1)
|
||||
{
|
||||
bitcrusher.processReplacing ((float**)buffer.getArrayOfWritePointers(), (float**)buffer.getArrayOfWritePointers(), buffer.getNumSamples());
|
||||
}
|
||||
else if (mode == 2)
|
||||
{
|
||||
fireAmp.processReplacing ((float**)buffer.getArrayOfWritePointers(), (float**)buffer.getArrayOfWritePointers(), buffer.getNumSamples());
|
||||
}
|
||||
else if (mode == 3)
|
||||
{
|
||||
grindAmp.processReplacing ((float**)buffer.getArrayOfWritePointers(), (float**)buffer.getArrayOfWritePointers(), buffer.getNumSamples());
|
||||
}
|
||||
}
|
||||
|
||||
// Apply Delay
|
||||
|
|
@ -999,7 +1060,34 @@ void WavetableAudioProcessor::updateParams (int newBlockSize)
|
|||
|
||||
// Update Distortion
|
||||
if (distortionParams.enable->isOn())
|
||||
distortionVal = modMatrix.getValue (distortionParams.amount);
|
||||
{
|
||||
auto mode = fxParams.distMode->getUserValueInt();
|
||||
if (mode == 0)
|
||||
{
|
||||
distortionVal = modMatrix.getValue (distortionParams.amount);
|
||||
}
|
||||
else if (mode == 1)
|
||||
{
|
||||
bitcrusher.setParameter (0, modMatrix.getValue (bitcrusherParams.rez));
|
||||
bitcrusher.setParameter (1, modMatrix.getValue (bitcrusherParams.rate));
|
||||
bitcrusher.setParameter (2, modMatrix.getValue (bitcrusherParams.hard));
|
||||
bitcrusher.setParameter (3, modMatrix.getValue (bitcrusherParams.mix));
|
||||
}
|
||||
else if (mode == 2)
|
||||
{
|
||||
fireAmp.setParameter (0, modMatrix.getValue (fireAmpParams.gain));
|
||||
fireAmp.setParameter (1, modMatrix.getValue (fireAmpParams.tone));
|
||||
fireAmp.setParameter (2, modMatrix.getValue (fireAmpParams.output));
|
||||
fireAmp.setParameter (3, modMatrix.getValue (fireAmpParams.mix));
|
||||
}
|
||||
else if (mode == 3)
|
||||
{
|
||||
grindAmp.setParameter (0, modMatrix.getValue (grindAmpParams.gain));
|
||||
grindAmp.setParameter (1, modMatrix.getValue (grindAmpParams.tone));
|
||||
grindAmp.setParameter (2, modMatrix.getValue (grindAmpParams.output));
|
||||
grindAmp.setParameter (3, modMatrix.getValue (grindAmpParams.mix));
|
||||
}
|
||||
}
|
||||
|
||||
// Update Delay
|
||||
if (delayParams.enable->isOn())
|
||||
|
|
@ -1032,7 +1120,6 @@ void WavetableAudioProcessor::updateParams (int newBlockSize)
|
|||
reverb.setDamping (modMatrix.getValue (reverbParams.damping));
|
||||
reverb.setPredelay (modMatrix.getValue (reverbParams.predelay));
|
||||
reverb.setMix (modMatrix.getValue (reverbParams.mix));
|
||||
|
||||
}
|
||||
|
||||
// Output gain
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@
|
|||
#include <JuceHeader.h>
|
||||
|
||||
#include "WavetableVoice.h"
|
||||
#include "FX/DeRez2.h"
|
||||
#include "FX/FireAmp.h"
|
||||
#include "FX/GrindAmp.h"
|
||||
|
||||
constexpr auto fxGate = 0;
|
||||
constexpr auto fxChorus = 1;
|
||||
|
|
@ -232,13 +235,46 @@ public:
|
|||
{
|
||||
FXParams() = default;
|
||||
|
||||
gin::Parameter::Ptr fx1, fx2, fx3, fx4, fx5;
|
||||
gin::Parameter::Ptr fx1, fx2, fx3, fx4, fx5, distMode;
|
||||
|
||||
void setup (WavetableAudioProcessor& p);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (FXParams)
|
||||
};
|
||||
|
||||
struct BitCrusherParams
|
||||
{
|
||||
BitCrusherParams() = default;
|
||||
|
||||
gin::Parameter::Ptr rate, rez, hard, mix;
|
||||
|
||||
void setup (WavetableAudioProcessor& p);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (BitCrusherParams)
|
||||
};
|
||||
|
||||
struct FireAmpParams
|
||||
{
|
||||
FireAmpParams() = default;
|
||||
|
||||
gin::Parameter::Ptr gain, tone, output, mix;
|
||||
|
||||
void setup (WavetableAudioProcessor& p);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (FireAmpParams)
|
||||
};
|
||||
|
||||
struct GrindAmpParams
|
||||
{
|
||||
GrindAmpParams() = default;
|
||||
|
||||
gin::Parameter::Ptr gain, tone, output, mix;
|
||||
|
||||
void setup (WavetableAudioProcessor& p);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (GrindAmpParams)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
gin::ModSrcId modSrcPressure, modSrcTimbre, modSrcPitchbend, modScrPitchBend,
|
||||
modSrcFilter, modSrcNote, modSrcVelocity, modSrcStep, modSrcMonoStep;
|
||||
|
|
@ -264,6 +300,9 @@ public:
|
|||
DistortionParams distortionParams;
|
||||
DelayParams delayParams;
|
||||
ReverbParams reverbParams;
|
||||
BitCrusherParams bitcrusherParams;
|
||||
FireAmpParams fireAmpParams;
|
||||
GrindAmpParams grindAmpParams;
|
||||
UIParams uiParams;
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -274,6 +313,9 @@ public:
|
|||
gin::GainProcessor outputGain;
|
||||
gin::AudioFifo scopeFifo { 2, 44100 };
|
||||
float distortionVal = 0.0f;
|
||||
DeRez2 bitcrusher;
|
||||
FireAmp fireAmp;
|
||||
GrindAmp grindAmp;
|
||||
|
||||
juce::OwnedArray<gin::BandLimitedLookupTable> osc1Tables;
|
||||
juce::OwnedArray<gin::BandLimitedLookupTable> osc2Tables;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue