@@ -0,0 +1,36 @@ | |||||
/* | |||||
* Grain Juice Plugin | |||||
* Copyright (C) 2014 Andre Sklenar <andre.sklenar@gmail.com>, www.juicelab.cz | |||||
* | |||||
* This program is free software; you can redistribute it and/or | |||||
* modify it under the terms of the GNU General Public License as | |||||
* published by the Free Software Foundation; either version 2 of | |||||
* the License, or any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* For a full copy of the GNU General Public License see the doc/GPL.txt file. | |||||
*/ | |||||
#ifndef DISTRHO_PLUGIN_INFO_H_INCLUDED | |||||
#define DISTRHO_PLUGIN_INFO_H_INCLUDED | |||||
#define DISTRHO_PLUGIN_NAME "GrainJuice" | |||||
#define DISTRHO_PLUGIN_HAS_UI 1 | |||||
#define DISTRHO_PLUGIN_IS_SYNTH 0 | |||||
#define DISTRHO_PLUGIN_NUM_INPUTS 2 | |||||
#define DISTRHO_PLUGIN_NUM_OUTPUTS 2 | |||||
#define DISTRHO_PLUGIN_WANT_LATENCY 0 | |||||
#define DISTRHO_PLUGIN_WANT_PROGRAMS 1 | |||||
#define DISTRHO_PLUGIN_WANT_STATE 0 | |||||
#define DISTRHO_PLUGIN_WANT_TIMEPOS 0 | |||||
#define DISTRHO_PLUGIN_URI "urn:distrho:GrainJuice" | |||||
#endif // DISTRHO_PLUGIN_INFO_H_INCLUDED |
@@ -0,0 +1,35 @@ | |||||
/* (Auto-generated binary data file). */ | |||||
#ifndef BINARY_GRAINJUICEARTWORK_HPP | |||||
#define BINARY_GRAINJUICEARTWORK_HPP | |||||
namespace GrainJuiceArtwork | |||||
{ | |||||
extern const char* aboutData; | |||||
const unsigned int aboutDataSize = 180000; | |||||
const unsigned int aboutWidth = 300; | |||||
const unsigned int aboutHeight = 200; | |||||
extern const char* aboutButtonHoverData; | |||||
const unsigned int aboutButtonHoverDataSize = 5888; | |||||
const unsigned int aboutButtonHoverWidth = 92; | |||||
const unsigned int aboutButtonHoverHeight = 16; | |||||
extern const char* aboutButtonNormalData; | |||||
const unsigned int aboutButtonNormalDataSize = 5888; | |||||
const unsigned int aboutButtonNormalWidth = 92; | |||||
const unsigned int aboutButtonNormalHeight = 16; | |||||
extern const char* backgroundData; | |||||
const unsigned int backgroundDataSize = 450000; | |||||
const unsigned int backgroundWidth = 500; | |||||
const unsigned int backgroundHeight = 300; | |||||
extern const char* knobData; | |||||
const unsigned int knobDataSize = 6084; | |||||
const unsigned int knobWidth = 39; | |||||
const unsigned int knobHeight = 39; | |||||
} | |||||
#endif // BINARY_GRAINJUICEARTWORK_HPP | |||||
@@ -0,0 +1,520 @@ | |||||
/* | |||||
* Grain Juice Plugin | |||||
* Copyright (C) 2014 Andre Sklenar <andre.sklenar@gmail.com>, www.juicelab.cz | |||||
* | |||||
* This program is free software; you can redistribute it and/or | |||||
* modify it under the terms of the GNU General Public License as | |||||
* published by the Free Software Foundation; either version 2 of | |||||
* the License, or any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* For a full copy of the GNU General Public License see the doc/GPL.txt file. | |||||
*/ | |||||
#include "GrainJuicePlugin.hpp" | |||||
#include <iostream> | |||||
#include <math.h> | |||||
#include <cmath> | |||||
START_NAMESPACE_DISTRHO | |||||
// ----------------------------------------------------------------------- | |||||
GrainJuicePlugin::GrainJuicePlugin() | |||||
: Plugin(paramCount, 1, 0) { // 1 program, 0 states | |||||
// set default values | |||||
d_setProgram(0); | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
// Init | |||||
void GrainJuicePlugin::d_initParameter(uint32_t index, Parameter& parameter) { | |||||
switch (index) { | |||||
case paramSize: | |||||
parameter.hints = kParameterIsAutomable; | |||||
parameter.name = "Size"; | |||||
parameter.symbol = "siz"; | |||||
parameter.unit = ""; | |||||
parameter.ranges.def = 0.5f; | |||||
parameter.ranges.min = 0.0f; | |||||
parameter.ranges.max = 1.0f; | |||||
break; | |||||
case paramScatter: | |||||
parameter.hints = kParameterIsAutomable; | |||||
parameter.name = "Scatter"; | |||||
parameter.symbol = "siz"; | |||||
parameter.unit = ""; | |||||
parameter.ranges.def = 0.5f; | |||||
parameter.ranges.min = 0.0f; | |||||
parameter.ranges.max = 1.0f; | |||||
break; | |||||
case paramGain: | |||||
parameter.hints = kParameterIsAutomable; | |||||
parameter.name = "Gain"; | |||||
parameter.symbol = "siz"; | |||||
parameter.unit = ""; | |||||
parameter.ranges.def = 0.5f; | |||||
parameter.ranges.min = 0.0f; | |||||
parameter.ranges.max = 1.0f; | |||||
break; | |||||
case paramDensity: | |||||
parameter.hints = kParameterIsAutomable; | |||||
parameter.name = "Density"; | |||||
parameter.symbol = "siz"; | |||||
parameter.unit = ""; | |||||
parameter.ranges.def = 0.5f; | |||||
parameter.ranges.min = 0.0f; | |||||
parameter.ranges.max = 1.0f; | |||||
break; | |||||
case paramLPF: | |||||
parameter.hints = kParameterIsAutomable; | |||||
parameter.name = "LPF"; | |||||
parameter.symbol = "siz"; | |||||
parameter.unit = ""; | |||||
parameter.ranges.def = 0.5f; | |||||
parameter.ranges.min = 0.0f; | |||||
parameter.ranges.max = 1.0f; | |||||
break; | |||||
case paramHPF: | |||||
parameter.hints = kParameterIsAutomable; | |||||
parameter.name = "HPF"; | |||||
parameter.symbol = "siz"; | |||||
parameter.unit = ""; | |||||
parameter.ranges.def = 0.5f; | |||||
parameter.ranges.min = 0.0f; | |||||
parameter.ranges.max = 1.0f; | |||||
break; | |||||
case paramSizeR: | |||||
parameter.hints = kParameterIsAutomable; | |||||
parameter.name = "Size"; | |||||
parameter.symbol = "siz"; | |||||
parameter.unit = ""; | |||||
parameter.ranges.def = 0.5f; | |||||
parameter.ranges.min = 0.0f; | |||||
parameter.ranges.max = 1.0f; | |||||
break; | |||||
case paramScatterR: | |||||
parameter.hints = kParameterIsAutomable; | |||||
parameter.name = "Size"; | |||||
parameter.symbol = "siz"; | |||||
parameter.unit = ""; | |||||
parameter.ranges.def = 0.5f; | |||||
parameter.ranges.min = 0.0f; | |||||
parameter.ranges.max = 1.0f; | |||||
break; | |||||
case paramGainR: | |||||
parameter.hints = kParameterIsAutomable; | |||||
parameter.name = "Size"; | |||||
parameter.symbol = "siz"; | |||||
parameter.unit = ""; | |||||
parameter.ranges.def = 0.5f; | |||||
parameter.ranges.min = 0.0f; | |||||
parameter.ranges.max = 1.0f; | |||||
break; | |||||
case paramDensityR: | |||||
parameter.hints = kParameterIsAutomable; | |||||
parameter.name = "Size"; | |||||
parameter.symbol = "siz"; | |||||
parameter.unit = ""; | |||||
parameter.ranges.def = 0.5f; | |||||
parameter.ranges.min = 0.0f; | |||||
parameter.ranges.max = 1.0f; | |||||
break; | |||||
case paramLPFR: | |||||
parameter.hints = kParameterIsAutomable; | |||||
parameter.name = "Size"; | |||||
parameter.symbol = "siz"; | |||||
parameter.unit = ""; | |||||
parameter.ranges.def = 0.5f; | |||||
parameter.ranges.min = 0.0f; | |||||
parameter.ranges.max = 1.0f; | |||||
break; | |||||
case paramHPFR: | |||||
parameter.hints = kParameterIsAutomable; | |||||
parameter.name = "Size"; | |||||
parameter.symbol = "siz"; | |||||
parameter.unit = ""; | |||||
parameter.ranges.def = 0.5f; | |||||
parameter.ranges.min = 0.0f; | |||||
parameter.ranges.max = 1.0f; | |||||
break; | |||||
} | |||||
} | |||||
void GrainJuicePlugin::d_initProgramName(uint32_t index, d_string& programName) { | |||||
if (index != 0) | |||||
return; | |||||
programName = "Default"; | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
// Internal data | |||||
float GrainJuicePlugin::d_getParameterValue(uint32_t index) const { | |||||
switch (index) { | |||||
case paramSize: | |||||
return size; | |||||
case paramScatter: | |||||
return scatter; | |||||
case paramGain: | |||||
return gain; | |||||
case paramDensity: | |||||
return density; | |||||
case paramLPF: | |||||
return LPF; | |||||
case paramHPF: | |||||
return HPF; | |||||
case paramSizeR: | |||||
return sizeR; | |||||
case paramScatterR: | |||||
return scatterR; | |||||
case paramGainR: | |||||
return gainR; | |||||
case paramDensityR: | |||||
return densityR; | |||||
case paramLPFR: | |||||
return LPFR; | |||||
case paramHPFR: | |||||
return HPFR; | |||||
default: | |||||
return 0.0f; | |||||
} | |||||
} | |||||
void GrainJuicePlugin::d_setParameterValue(uint32_t index, float value) { | |||||
switch (index) { | |||||
case paramSize: | |||||
size = value; | |||||
break; | |||||
case paramScatter: | |||||
scatter = value; | |||||
break; | |||||
case paramGain: | |||||
gain = value; | |||||
break; | |||||
case paramDensity: | |||||
density = value; | |||||
break; | |||||
case paramLPF: | |||||
LPF = value; | |||||
break; | |||||
case paramHPF: | |||||
HPF = value; | |||||
break; | |||||
case paramSizeR: | |||||
sizeR = value; | |||||
break; | |||||
case paramScatterR: | |||||
scatterR = value; | |||||
break; | |||||
case paramGainR: | |||||
gainR = value; | |||||
break; | |||||
case paramDensityR: | |||||
densityR = value; | |||||
break; | |||||
case paramLPFR: | |||||
LPFR = value; | |||||
break; | |||||
case paramHPFR: | |||||
HPFR = value; | |||||
break; | |||||
} | |||||
} | |||||
void GrainJuicePlugin::d_setProgram(uint32_t index) { | |||||
if (index != 0) | |||||
return; | |||||
/* Default parameter values */ | |||||
size=scatter=gain=density=HPF=LPF=1.f; | |||||
sizeR=scatterR=gainR=densityR=HPFR=LPFR=1.f; | |||||
prevTotalGrains = 0; | |||||
/* Default variable values */ | |||||
srand (time(NULL)); | |||||
nSample = new float[2]; | |||||
prevSample = new float[2]; | |||||
std::cout << "Starting." << std::endl; | |||||
for (int i=0; i<1000; i++) { | |||||
logTable[i] = expf((logf(0.999f)-logf(0.001f))*(i/1000.0f)+logf(0.999f)); | |||||
} | |||||
logTable[0] = 0.0f; | |||||
concurrentFilling = 0; | |||||
d_activate(); | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
// GRAINS | |||||
GrainJuicePlugin::CGrain::CGrain(uint32_t nLength, uint32_t nDelay, float lpf, float hpf, float nGain) { | |||||
length = nLength; | |||||
readyForPlayback = false; | |||||
delay = nDelay; | |||||
playhead = 0; | |||||
gain = nGain; | |||||
//highPass = new HPF[2]; | |||||
//lowPass = new LPF[2]; | |||||
highPass[0].setSampleRate(48000); | |||||
highPass[1].setSampleRate(48000); | |||||
highPass[0].setReso(0.1f); | |||||
highPass[1].setReso(0.1f); | |||||
highPass[0].setFreq(hpf); | |||||
highPass[1].setFreq(hpf); | |||||
highPass[0].compute(); | |||||
highPass[1].compute(); | |||||
lowPass[0].setSampleRate(48000); | |||||
lowPass[1].setSampleRate(48000); | |||||
lowPass[0].setReso(1.4f); | |||||
lowPass[1].setReso(1.4f); | |||||
lowPass[0].setFreq(lpf); | |||||
lowPass[1].setFreq(lpf); | |||||
lowPass[0].compute(); | |||||
lowPass[1].compute(); | |||||
} | |||||
bool GrainJuicePlugin::CGrain::wantsSample() { | |||||
if (playhead<length && !readyForPlayback) { | |||||
return true; | |||||
} else { | |||||
return false; | |||||
} | |||||
} | |||||
void GrainJuicePlugin::CGrain::fillSample(float *nSample) { | |||||
//std::cout << "omnom" << std::endl; | |||||
//std::cout << "playhead: " << playhead << std::endl; | |||||
//std::cout << "length: " << length << std::endl; | |||||
samples[playhead][0] = nSample[0]; | |||||
samples[playhead][1] = nSample[1]; | |||||
playhead++; | |||||
if (playhead==length-5) { | |||||
readyForPlayback = true; | |||||
applyEnvelope(); | |||||
playhead = length-5; | |||||
} | |||||
} | |||||
float *GrainJuicePlugin::CGrain::getSample() { | |||||
playhead--; | |||||
float coeff = 0.5f * (1 - cosf((2.f * M_PI * playhead)/(length-1))); | |||||
float newSample[2]; | |||||
for (int i = 0; i < 2; i++) { | |||||
newSample[i] = samples[playhead][i]; | |||||
newSample[i] *= coeff*gain; | |||||
newSample[i] = lowPass[i].process(newSample[i]); | |||||
//newSample[i] = highPass[i].process(newSample[i]); | |||||
} | |||||
samples[playhead][0] = newSample[0]; | |||||
samples[playhead][1] = newSample[1]; | |||||
return samples[playhead]; | |||||
} | |||||
bool GrainJuicePlugin::CGrain::inDelay() { | |||||
if (delay>0) { | |||||
playhead = length; | |||||
delay--; | |||||
return true; | |||||
} else { | |||||
return false; | |||||
} | |||||
} | |||||
uint32_t GrainJuicePlugin::CGrain::getLength() { | |||||
return length; | |||||
} | |||||
bool GrainJuicePlugin::CGrain::donePlaying() { | |||||
if (playhead<1) { //TODO: fix so all the samples get played/recorded | |||||
return true; | |||||
} else { | |||||
return false; | |||||
} | |||||
} | |||||
void GrainJuicePlugin::CGrain::applyEnvelope() { | |||||
} | |||||
void GrainJuicePlugin::CGrain::updateParams(float nDensity, float nScatter, uint32_t nSRT) { | |||||
density = nDensity; | |||||
scatter = nScatter; | |||||
SRT = nSRT; | |||||
} | |||||
void GrainJuicePlugin::CGrain::setDelay(uint32_t nDelay) { | |||||
delay = nDelay; | |||||
} | |||||
float GrainJuicePlugin::CGrain::inspectSample(uint32_t index, int channel) { | |||||
return samples[index][channel]; | |||||
} | |||||
void GrainJuicePlugin::newGrain(bool first) { | |||||
// create a new grain | |||||
concurrentFilling = 0; | |||||
bool create = false; | |||||
if (first) { | |||||
create = true; | |||||
} else if (grains.size()+1 <= density*MAXGRAINS) { | |||||
create = true; | |||||
} | |||||
if (create) { | |||||
uint32_t newGrainSize =(int) size*d_getSampleRate()*MAXGRAINSIZE; | |||||
newGrainSize = rand() % newGrainSize-1000; | |||||
newGrainSize += 1000; | |||||
uint32_t delayChance = (1000 - (rand() % (int) (density*1000.f)))/25+1; | |||||
//std::cout << "grain fill!" << std::endl; | |||||
//std::cout << "chance: " << delayChance << std::endl;; | |||||
//delayChance = 2; | |||||
for (int d=0; d<delayChance; d++) { | |||||
if (grains.size()+1 > density*MAXGRAINS) { | |||||
break; | |||||
} | |||||
int newDelayTime = rand() % (int) (MAXSCATTER*scatter*d_getSampleRate()); | |||||
newDelayTime += 100; | |||||
int newLPF = LPF*998; | |||||
int random = (rand() % (int) (LPFR*1000+1))-(rand() % (int) (LPFR*500+1)); | |||||
newLPF = limit(newLPF-random); | |||||
int LPFLimit = newLPF; | |||||
newLPF = logTable[newLPF]*20+100; | |||||
if (newLPF>20000) newLPF = 20000; | |||||
if (newLPF<100) newLPF = 100; | |||||
int newHPF = HPF*998; | |||||
random = (rand() % (int) (HPFR*1000+1))-(rand() % (int) (HPFR*500+1)); | |||||
newHPF = limit(newHPF-random); | |||||
newHPF = logTable[newHPF]*20+20; | |||||
if (newHPF>20000) newHPF = 20000; | |||||
newHPF *= newLPF/20000.f; | |||||
if (newHPF<50) newHPF = 50; | |||||
std::cout << newHPF << std::endl; | |||||
//int newHPF = rand() % (int) (LPF); | |||||
int newGain = rand() % (int) (1000); | |||||
grains.push_back(new CGrain(newGrainSize, newDelayTime, newLPF, newHPF, newGain/1000.f)); | |||||
//grains.push_back(new CGrain(newGrainSize, newDelayTime)); | |||||
concurrentFilling++; | |||||
} | |||||
} | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
// Process | |||||
void GrainJuicePlugin::d_activate() { | |||||
} | |||||
void GrainJuicePlugin::d_run(const float** inputs, float** outputs, uint32_t frames) { | |||||
static int k = 0; | |||||
for (uint32_t i = 0; i < frames; i++) { | |||||
// RUNTIME CODE HERE | |||||
//std::cout << "..record" << std::endl; | |||||
//------------------------------------- | |||||
// RECORD | |||||
//std::cout << "total grains:" << grains.size() << std::endl; | |||||
//std::cout << "max grains:" << density*MAXGRAINS << std::endl; | |||||
if (grains.size() <= density*MAXGRAINS) { | |||||
//std::cout << "not enough full grains" << std::endl; | |||||
if (grains.size() == 0) { | |||||
newGrain(true); | |||||
} | |||||
if (grains.back() -> wantsSample()) { | |||||
nSample[0] = inputs[0][i]; | |||||
nSample[1] = inputs[1][i]; | |||||
for (int q=0; q<concurrentFilling; q++) { | |||||
grains[grains.size()-1-q] -> fillSample(nSample); | |||||
} | |||||
} else { | |||||
newGrain(false); | |||||
} | |||||
} | |||||
//std::cout << "..playback" << std::endl; | |||||
//------------------------------------- | |||||
// PLAYBACK | |||||
outputs[0][i] = 0.0f; | |||||
outputs[1][i] = 0.0f; | |||||
eraseIndex = 0; | |||||
int grainsPlaying = 0; | |||||
int l = 0; | |||||
for (uint32_t j = 0; j < grains.size(); j++) { | |||||
if (!grains[j]->wantsSample()) { | |||||
grainsPlaying++; | |||||
if (!grains[j]->inDelay()) { | |||||
//play this sample back | |||||
grains[j]->updateParams(density, scatter, d_getSampleRate()); | |||||
if (grains[j]->donePlaying()) { | |||||
// mark grains for erase | |||||
eraseGrains[eraseIndex] = j; | |||||
eraseIndex++; | |||||
//grains.erase(grains.begin()+j); | |||||
//j--; // COME ON BABY LIGHT MY FIRE | |||||
} else { | |||||
nSample = grains[j]->getSample(); | |||||
outputs[0][i] += nSample[0]; | |||||
outputs[1][i] += nSample[1]; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
for (int j=0; j<eraseIndex; j++) { | |||||
grains.erase(grains.begin()+eraseGrains[j]); | |||||
} | |||||
if (grains.size() != prevTotalGrains) { | |||||
//std::cout << "total grains: " << grainsPlaying << " conc: " << concurrentFilling << " size: " << grains.size() << std::endl; | |||||
//std::cout << "total grains playing: " << grainsPlaying << std::endl; | |||||
prevTotalGrains = grainsPlaying; | |||||
} | |||||
if (k==9) k=0; | |||||
} | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
Plugin* createPlugin() { | |||||
return new GrainJuicePlugin(); | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
END_NAMESPACE_DISTRHO |
@@ -0,0 +1,307 @@ | |||||
/* | |||||
* Grain Juice Plugin | |||||
* Copyright (C) 2014 Andre Sklenar <andre.sklenar@gmail.com>, www.juicelab.cz | |||||
* | |||||
* This program is free software; you can redistribute it and/or | |||||
* modify it under the terms of the GNU General Public License as | |||||
* published by the Free Software Foundation; either version 2 of | |||||
* the License, or any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* For a full copy of the GNU General Public License see the doc/GPL.txt file. | |||||
*/ | |||||
#ifndef GrainJUICEPLUGIN_HPP_INCLUDED | |||||
#define GrainJUICEPLUGIN_HPP_INCLUDED | |||||
#include "DistrhoPlugin.hpp" | |||||
#include <cstdlib> | |||||
#include <time.h> | |||||
#include <vector> | |||||
#define MAXGRAINSIZE 0.5f // in seconds | |||||
#define MAXSCATTER 3.f // in seconds | |||||
#define MAXGRAINS 300 // in grains | |||||
START_NAMESPACE_DISTRHO | |||||
// ----------------------------------------------------------------------- | |||||
class GrainJuicePlugin : public Plugin { | |||||
public: | |||||
enum Parameters { | |||||
paramSize = 0, | |||||
paramScatter, | |||||
paramGain, | |||||
paramDensity, | |||||
paramLPF, | |||||
paramHPF, | |||||
paramSizeR, | |||||
paramScatterR, | |||||
paramGainR, | |||||
paramDensityR, | |||||
paramLPFR, | |||||
paramHPFR, | |||||
paramCount | |||||
}; | |||||
GrainJuicePlugin(); | |||||
protected: | |||||
// ------------------------------------------------------------------- | |||||
// Information | |||||
const char* d_getLabel() const noexcept override { | |||||
return "GrainJuice"; | |||||
} | |||||
const char* d_getMaker() const noexcept override { | |||||
return "Andre Sklenar"; | |||||
} | |||||
const char* d_getLicense() const noexcept override { | |||||
return "GPL v2+"; | |||||
} | |||||
uint32_t d_getVersion() const noexcept override { | |||||
return 0x1000; | |||||
} | |||||
long d_getUniqueId() const noexcept override { | |||||
return d_cconst('G', 'r', 'n', 'J'); | |||||
} | |||||
// ------------------------------------------------------------------- | |||||
// Init | |||||
void d_initParameter(uint32_t index, Parameter& parameter) override; | |||||
void d_initProgramName(uint32_t index, d_string& programName) override; | |||||
// ------------------------------------------------------------------- | |||||
// Internal data | |||||
float d_getParameterValue(uint32_t index) const override; | |||||
void d_setParameterValue(uint32_t index, float value) override; | |||||
void d_setProgram(uint32_t index) override; | |||||
// ------------------------------------------------------------------- | |||||
// Process | |||||
void d_activate() override; | |||||
void d_run(const float** inputs, float** outputs, uint32_t frames) override; | |||||
// ------------------------------------------------------------------- | |||||
private: | |||||
float size, scatter, gain, density, LPF, HPF; | |||||
float sizeR, scatterR, gainR, densityR, LPFR, HPFR; | |||||
float *nSample; | |||||
float *prevSample; | |||||
int concurrentFilling; | |||||
int eraseIndex; | |||||
int eraseGrains[100]; | |||||
float logTable[1000]; | |||||
int limit(int what) { | |||||
if (what>1000) | |||||
return 999; | |||||
if (what<0) | |||||
return 0; | |||||
return what; | |||||
} | |||||
void newGrain(bool first); | |||||
//float *nSample; | |||||
bool isNan(float& value ) { | |||||
if (((*(uint32_t *) &value) & 0x7fffffff) > 0x7f800000) { | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
uint32_t prevTotalGrains; | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(GrainJuicePlugin) | |||||
class CGrain { | |||||
public: | |||||
CGrain(uint32_t nLength, uint32_t nDelay, float lpf, float hpf, float nGain); | |||||
uint32_t getLength(); | |||||
void fillSample(float *nSample); | |||||
bool wantsSample(); | |||||
float *getSample(); | |||||
bool donePlaying(); | |||||
void cleanUp(); | |||||
bool inDelay(); | |||||
uint32_t length; | |||||
bool repeat(); | |||||
void setDelay(uint32_t nDelay); | |||||
void updateParams(float nDensity, float nScatter, uint32_t nSRT); | |||||
float inspectSample(uint32_t index, int channel); | |||||
private: | |||||
void applyEnvelope(); | |||||
float samples[(int)(48000*MAXGRAINSIZE)][2]; | |||||
uint32_t playhead; | |||||
bool readyForPlayback; | |||||
uint32_t delay; | |||||
float *emptySamples; | |||||
float density; | |||||
float scatter; | |||||
float gain; | |||||
uint32_t SRT; | |||||
class CLPF | |||||
{ | |||||
private: | |||||
float r; // reso - 0.1 - 1.4 | |||||
float f; // freq in Hz | |||||
float a1, a2, a3, b1, b2; | |||||
float in, in1, in2; | |||||
float out, out1, out2; | |||||
float c; | |||||
float sampleRate; | |||||
public: | |||||
CLPF() { | |||||
r = 1.0f; | |||||
f = 0.0f; | |||||
a1=a2=a3=b1=b2=0.0f; | |||||
in=in1=in2=out=out1=out2=0.0f; | |||||
} | |||||
void compute() { | |||||
c = 1.0 / tan(M_PI * f / sampleRate); | |||||
a1 = 1.0 / (1.0 + r*c + c*c); | |||||
a2 = 2 * a1; | |||||
a3 = a1; | |||||
b1 = 2.0 * (1.0 - c*c) * a1; | |||||
b2 = (1.0 - r*c + c*c) * a1; | |||||
} | |||||
void setFreq(float nFreq) { | |||||
f = nFreq; | |||||
if (f==0) { | |||||
f=20; | |||||
} | |||||
} | |||||
void setReso(float nReso) { | |||||
r = nReso; | |||||
if (r==0) { | |||||
r = 0.1f; | |||||
} | |||||
r = 1.0f; | |||||
f = 0.0f; | |||||
a1=a2=a3=b1=b2=0.0f; | |||||
in=in1=in2=out=out1=out2=0.0f; | |||||
} | |||||
void setSampleRate(float nSampleRate) { | |||||
sampleRate = nSampleRate; | |||||
} | |||||
float process(float sample) { | |||||
in = sample; | |||||
out = a1 * in + a2 * in1 + a3 * in2 - b1*out1 - b2*out2; | |||||
in2=in1; | |||||
in1=in; | |||||
out2=out1; | |||||
out1 = out; | |||||
return out; | |||||
} | |||||
}; | |||||
class CHPF | |||||
{ | |||||
private: | |||||
float r; // reso - 0.1 - 1.4 | |||||
float f; // freq in Hz | |||||
float a1, a2, a3, b1, b2; | |||||
float in, in1, in2; | |||||
float out, out1, out2; | |||||
float c; | |||||
float sampleRate; | |||||
public: | |||||
CHPF() { | |||||
r = 1.0f; | |||||
f = 0.0f; | |||||
a1=a2=a3=b1=b2=0.0f; | |||||
in=in1=in2=out=out1=out2=0.0f; | |||||
} | |||||
void compute() { | |||||
c = tan(M_PI * f / sampleRate); | |||||
a1 = 1.0 / (1.0 + r*c + c*c); | |||||
a2 = -2*a1; | |||||
a3 = a1; | |||||
b1 = 2.0 * (c*c - 1.0) * a1; | |||||
b2 = (1.0 - r*c + c*c) * a1; | |||||
} | |||||
void setFreq(float nFreq) { | |||||
f = nFreq; | |||||
if (f==0) { | |||||
f=20; | |||||
} | |||||
} | |||||
void setReso(float nReso) { | |||||
r = nReso; | |||||
if (r==0) { | |||||
r = 0.1f; | |||||
} | |||||
} | |||||
void setSampleRate(float nSampleRate) { | |||||
sampleRate = nSampleRate; | |||||
} | |||||
float process(float sample) { | |||||
//compute(); | |||||
in = sample; | |||||
out = a1 * in + a2 * in1 + a3 * in2 - b1*out1 - b2*out2; | |||||
in2=in1; | |||||
in1=in; | |||||
out2=out1; | |||||
out1 = out; | |||||
return out; | |||||
} | |||||
}; | |||||
CHPF highPass[2]; | |||||
CLPF lowPass[2]; | |||||
}; | |||||
std::vector<CGrain*> grains; | |||||
}; | |||||
// ----------------------------------------------------------------------- | |||||
END_NAMESPACE_DISTRHO | |||||
#endif // GrainJUICE_HPP_INCLUDED |
@@ -0,0 +1,283 @@ | |||||
/* | |||||
* Grain Juice Plugin | |||||
* Copyright (C) 2014 Andre Sklenar <andre.sklenar@gmail.com>, www.juicelab.cz | |||||
* | |||||
* This program is free software; you can redistribute it and/or | |||||
* modify it under the terms of the GNU General Public License as | |||||
* published by the Free Software Foundation; either version 2 of | |||||
* the License, or any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* For a full copy of the GNU General Public License see the doc/GPL.txt file. | |||||
*/ | |||||
#include "GrainJuicePlugin.hpp" | |||||
#include "GrainJuiceUI.hpp" | |||||
START_NAMESPACE_DISTRHO | |||||
// ----------------------------------------------------------------------- | |||||
GrainJuiceUI::GrainJuiceUI() | |||||
: UI(), | |||||
fAboutWindow(this) { | |||||
setSize(GrainJuiceArtwork::backgroundWidth, GrainJuiceArtwork::backgroundHeight); | |||||
// background | |||||
fImgBackground = Image(GrainJuiceArtwork::backgroundData, GrainJuiceArtwork::backgroundWidth, GrainJuiceArtwork::backgroundHeight, GL_BGR); | |||||
// about | |||||
Image aboutImage(GrainJuiceArtwork::aboutData, GrainJuiceArtwork::aboutWidth, GrainJuiceArtwork::aboutHeight, GL_BGR); | |||||
fAboutWindow.setImage(aboutImage); | |||||
// knobs | |||||
Image knobImage(GrainJuiceArtwork::knobData, GrainJuiceArtwork::knobWidth, GrainJuiceArtwork::knobHeight); | |||||
#define PX 56 //position top X | |||||
#define PY 98 //position top Y | |||||
#define SX 70 //spacing X | |||||
#define SY 86 //spacing Y | |||||
// knob Size | |||||
fKnobSize = new ImageKnob(this, knobImage, ImageKnob::Vertical); | |||||
fKnobSize->setId(GrainJuicePlugin::paramSize); | |||||
fKnobSize->setAbsolutePos(PX, PY); | |||||
fKnobSize->setRotationAngle(270); | |||||
fKnobSize->setRange(0.0f, 1.0f); | |||||
fKnobSize->setDefault(0.5f); | |||||
fKnobSize->setStep(0.01f); | |||||
fKnobSize->setCallback(this); | |||||
// knob Scatter | |||||
fKnobScatter = new ImageKnob(this, knobImage, ImageKnob::Vertical); | |||||
fKnobScatter->setId(GrainJuicePlugin::paramScatter); | |||||
fKnobScatter->setAbsolutePos(PX+SX, PY); | |||||
fKnobScatter->setRotationAngle(270); | |||||
fKnobScatter->setRange(0.0f, 1.0f); | |||||
fKnobScatter->setDefault(0.5f); | |||||
fKnobScatter->setStep(0.01f); | |||||
fKnobScatter->setCallback(this); | |||||
// knob Gain | |||||
fKnobGain = new ImageKnob(this, knobImage, ImageKnob::Vertical); | |||||
fKnobGain->setId(GrainJuicePlugin::paramGain); | |||||
fKnobGain->setAbsolutePos(PX+SX*2, PY); | |||||
fKnobGain->setRotationAngle(270); | |||||
fKnobGain->setRange(0.0f, 1.0f); | |||||
fKnobGain->setDefault(0.5f); | |||||
fKnobGain->setStep(0.01f); | |||||
fKnobGain->setCallback(this); | |||||
// knob Density | |||||
fKnobDensity = new ImageKnob(this, knobImage, ImageKnob::Vertical); | |||||
fKnobDensity->setId(GrainJuicePlugin::paramDensity); | |||||
fKnobDensity->setAbsolutePos(PX+SX*3, PY); | |||||
fKnobDensity->setRotationAngle(270); | |||||
fKnobDensity->setRange(0.0f, 1.0f); | |||||
fKnobDensity->setDefault(0.5f); | |||||
fKnobDensity->setStep(0.01f); | |||||
fKnobDensity->setCallback(this); | |||||
// knob LPF | |||||
fKnobLPF = new ImageKnob(this, knobImage, ImageKnob::Vertical); | |||||
fKnobLPF->setId(GrainJuicePlugin::paramLPF); | |||||
fKnobLPF->setAbsolutePos(PX+SX*4, PY); | |||||
fKnobLPF->setRotationAngle(270); | |||||
fKnobLPF->setRange(0.0f, 1.0f); | |||||
fKnobLPF->setDefault(0.5f); | |||||
fKnobLPF->setStep(0.01f); | |||||
fKnobLPF->setCallback(this); | |||||
// knob HPF | |||||
fKnobHPF = new ImageKnob(this, knobImage, ImageKnob::Vertical); | |||||
fKnobHPF->setId(GrainJuicePlugin::paramHPF); | |||||
fKnobHPF->setAbsolutePos(PX+SX*5, PY); | |||||
fKnobHPF->setRotationAngle(270); | |||||
fKnobHPF->setRange(0.0f, 1.0f); | |||||
fKnobHPF->setDefault(0.5f); | |||||
fKnobHPF->setStep(0.01f); | |||||
fKnobHPF->setCallback(this); | |||||
//////////// RANDOMIZE KNOBS | |||||
// knob Size | |||||
fKnobSizeR = new ImageKnob(this, knobImage, ImageKnob::Vertical); | |||||
fKnobSizeR->setId(GrainJuicePlugin::paramSizeR); | |||||
fKnobSizeR->setAbsolutePos(PX, PY+SY); | |||||
fKnobSizeR->setRotationAngle(270); | |||||
fKnobSizeR->setRange(0.0f, 1.0f); | |||||
fKnobSizeR->setDefault(0.5f); | |||||
fKnobSizeR->setStep(0.01f); | |||||
fKnobSizeR->setCallback(this); | |||||
// knob Scatter | |||||
fKnobScatterR = new ImageKnob(this, knobImage, ImageKnob::Vertical); | |||||
fKnobScatterR->setId(GrainJuicePlugin::paramScatterR); | |||||
fKnobScatterR->setAbsolutePos(PX+SX, PY+SY); | |||||
fKnobScatterR->setRotationAngle(270); | |||||
fKnobScatterR->setRange(0.0f, 1.0f); | |||||
fKnobScatterR->setDefault(0.5f); | |||||
fKnobScatterR->setStep(0.01f); | |||||
fKnobScatterR->setCallback(this); | |||||
// knob Gain | |||||
fKnobGainR = new ImageKnob(this, knobImage, ImageKnob::Vertical); | |||||
fKnobGainR->setId(GrainJuicePlugin::paramGainR); | |||||
fKnobGainR->setAbsolutePos(PX+SX*2, PY+SY); | |||||
fKnobGainR->setRotationAngle(270); | |||||
fKnobGainR->setRange(0.0f, 1.0f); | |||||
fKnobGainR->setDefault(0.5f); | |||||
fKnobGainR->setStep(0.01f); | |||||
fKnobGainR->setCallback(this); | |||||
// knob Density | |||||
fKnobDensityR = new ImageKnob(this, knobImage, ImageKnob::Vertical); | |||||
fKnobDensityR->setId(GrainJuicePlugin::paramDensityR); | |||||
fKnobDensityR->setAbsolutePos(PX+SX*3, PY+SY); | |||||
fKnobDensityR->setRotationAngle(270); | |||||
fKnobDensityR->setRange(0.0f, 1.0f); | |||||
fKnobDensityR->setDefault(0.5f); | |||||
fKnobDensityR->setStep(0.01f); | |||||
fKnobDensityR->setCallback(this); | |||||
// knob LPF | |||||
fKnobLPFR = new ImageKnob(this, knobImage, ImageKnob::Vertical); | |||||
fKnobLPFR->setId(GrainJuicePlugin::paramLPFR); | |||||
fKnobLPFR->setAbsolutePos(PX+SX*4, PY+SY); | |||||
fKnobLPFR->setRotationAngle(270); | |||||
fKnobLPFR->setRange(0.0f, 1.0f); | |||||
fKnobLPFR->setDefault(0.5f); | |||||
fKnobLPFR->setStep(0.01f); | |||||
fKnobLPFR->setCallback(this); | |||||
// knob HPF | |||||
fKnobHPFR = new ImageKnob(this, knobImage, ImageKnob::Vertical); | |||||
fKnobHPFR->setId(GrainJuicePlugin::paramHPFR); | |||||
fKnobHPFR->setAbsolutePos(PX+SX*5, PY+SY); | |||||
fKnobHPFR->setRotationAngle(270); | |||||
fKnobHPFR->setRange(0.0f, 1.0f); | |||||
fKnobHPFR->setDefault(0.5f); | |||||
fKnobHPFR->setStep(0.01f); | |||||
fKnobHPFR->setCallback(this); | |||||
// about button | |||||
Image aboutImageNormal(GrainJuiceArtwork::aboutButtonNormalData, GrainJuiceArtwork::aboutButtonNormalWidth, GrainJuiceArtwork::aboutButtonNormalHeight); | |||||
Image aboutImageHover(GrainJuiceArtwork::aboutButtonHoverData, GrainJuiceArtwork::aboutButtonHoverWidth, GrainJuiceArtwork::aboutButtonHoverHeight); | |||||
fButtonAbout = new ImageButton(this, aboutImageNormal, aboutImageHover, aboutImageHover); | |||||
fButtonAbout->setAbsolutePos(390, 20); | |||||
fButtonAbout->setCallback(this); | |||||
// set default values | |||||
d_programChanged(0); | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
// DSP Callbacks | |||||
void GrainJuiceUI::d_parameterChanged(uint32_t index, float value) { | |||||
switch (index) { | |||||
case GrainJuicePlugin::paramSize: | |||||
fKnobSize->setValue(value); | |||||
break; | |||||
case GrainJuicePlugin::paramScatter: | |||||
fKnobScatter->setValue(value); | |||||
break; | |||||
case GrainJuicePlugin::paramGain: | |||||
fKnobGain->setValue(value); | |||||
break; | |||||
case GrainJuicePlugin::paramDensity: | |||||
fKnobDensity->setValue(value); | |||||
break; | |||||
case GrainJuicePlugin::paramLPF: | |||||
fKnobLPF->setValue(value); | |||||
break; | |||||
case GrainJuicePlugin::paramHPF: | |||||
fKnobHPF->setValue(value); | |||||
break; | |||||
case GrainJuicePlugin::paramSizeR: | |||||
fKnobSizeR->setValue(value); | |||||
break; | |||||
case GrainJuicePlugin::paramScatterR: | |||||
fKnobScatterR->setValue(value); | |||||
break; | |||||
case GrainJuicePlugin::paramGainR: | |||||
fKnobGainR->setValue(value); | |||||
break; | |||||
case GrainJuicePlugin::paramDensityR: | |||||
fKnobDensityR->setValue(value); | |||||
break; | |||||
case GrainJuicePlugin::paramLPFR: | |||||
fKnobLPFR->setValue(value); | |||||
break; | |||||
case GrainJuicePlugin::paramHPFR: | |||||
fKnobHPFR->setValue(value); | |||||
break; | |||||
} | |||||
} | |||||
void GrainJuiceUI::d_programChanged(uint32_t index) { | |||||
if (index != 0) | |||||
return; | |||||
// Default values | |||||
fKnobSize->setValue(0.5f); | |||||
fKnobScatter->setValue(0.5f); | |||||
fKnobGain->setValue(0.5f); | |||||
fKnobDensity->setValue(0.5f); | |||||
fKnobLPF->setValue(0.5f); | |||||
fKnobHPF->setValue(0.5f); | |||||
fKnobSizeR->setValue(0.5f); | |||||
fKnobScatterR->setValue(0.5f); | |||||
fKnobGainR->setValue(0.5f); | |||||
fKnobDensityR->setValue(0.5f); | |||||
fKnobLPFR->setValue(0.5f); | |||||
fKnobHPFR->setValue(0.5f); | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
// Widget Callbacks | |||||
void GrainJuiceUI::imageButtonClicked(ImageButton* button, int) { | |||||
if (button != fButtonAbout) | |||||
return; | |||||
fAboutWindow.exec(); | |||||
} | |||||
void GrainJuiceUI::imageKnobDragStarted(ImageKnob* knob) { | |||||
d_editParameter(knob->getId(), true); | |||||
} | |||||
void GrainJuiceUI::imageKnobDragFinished(ImageKnob* knob) { | |||||
d_editParameter(knob->getId(), false); | |||||
} | |||||
void GrainJuiceUI::imageKnobValueChanged(ImageKnob* knob, float value) { | |||||
d_setParameterValue(knob->getId(), value); | |||||
} | |||||
void GrainJuiceUI::onDisplay() { | |||||
fImgBackground.draw(); | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
UI* createUI() { | |||||
return new GrainJuiceUI(); | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
END_NAMESPACE_DISTRHO |
@@ -0,0 +1,80 @@ | |||||
/* | |||||
* Grain Juice Plugin | |||||
* Copyright (C) 2014 Andre Sklenar <andre.sklenar@gmail.com>, www.juicelab.cz | |||||
* | |||||
* This program is free software; you can redistribute it and/or | |||||
* modify it under the terms of the GNU General Public License as | |||||
* published by the Free Software Foundation; either version 2 of | |||||
* the License, or any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* For a full copy of the GNU General Public License see the doc/GPL.txt file. | |||||
*/ | |||||
#ifndef GrainJUICEUI_HPP_INCLUDED | |||||
#define GrainJUICEUI_HPP_INCLUDED | |||||
#include "DistrhoUI.hpp" | |||||
#include "ImageAboutWindow.hpp" | |||||
#include "ImageButton.hpp" | |||||
#include "ImageKnob.hpp" | |||||
#include "GrainJuiceArtwork.hpp" | |||||
using DGL::Image; | |||||
using DGL::ImageAboutWindow; | |||||
using DGL::ImageButton; | |||||
using DGL::ImageKnob; | |||||
START_NAMESPACE_DISTRHO | |||||
// ----------------------------------------------------------------------- | |||||
class GrainJuiceUI : public UI, | |||||
public ImageButton::Callback, | |||||
public ImageKnob::Callback { | |||||
public: | |||||
GrainJuiceUI(); | |||||
protected: | |||||
// ------------------------------------------------------------------- | |||||
// DSP Callbacks | |||||
void d_parameterChanged(uint32_t index, float value) override; | |||||
void d_programChanged(uint32_t index) override; | |||||
// ------------------------------------------------------------------- | |||||
// Widget Callbacks | |||||
void imageButtonClicked(ImageButton* button, int) override; | |||||
void imageKnobDragStarted(ImageKnob* knob) override; | |||||
void imageKnobDragFinished(ImageKnob* knob) override; | |||||
void imageKnobValueChanged(ImageKnob* knob, float value) override; | |||||
void onDisplay() override; | |||||
private: | |||||
Image fImgBackground; | |||||
ImageAboutWindow fAboutWindow; | |||||
ScopedPointer<ImageButton> fButtonAbout; | |||||
ScopedPointer<ImageKnob> fKnobSize, fKnobScatter, fKnobGain; | |||||
ScopedPointer<ImageKnob> fKnobDensity, fKnobLPF, fKnobHPF; | |||||
ScopedPointer<ImageKnob> fKnobSizeR, fKnobScatterR, fKnobGainR; | |||||
ScopedPointer<ImageKnob> fKnobDensityR, fKnobLPFR, fKnobHPFR; | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(GrainJuiceUI) | |||||
}; | |||||
// ----------------------------------------------------------------------- | |||||
END_NAMESPACE_DISTRHO | |||||
#endif // GrainJUICEUI_HPP_INCLUDED |
@@ -0,0 +1,37 @@ | |||||
#!/usr/bin/make -f | |||||
# Makefile for DISTRHO Plugins # | |||||
# ---------------------------- # | |||||
# Created by falkTX | |||||
# | |||||
# -------------------------------------------------------------- | |||||
# Project name, used for binaries | |||||
NAME = GrainJuice | |||||
# -------------------------------------------------------------- | |||||
# Files to build | |||||
OBJS_DSP = \ | |||||
GrainJuicePlugin.cpp.o | |||||
OBJS_UI = \ | |||||
GrainJuiceArtwork.cpp.o \ | |||||
GrainJuiceUI.cpp.o | |||||
# -------------------------------------------------------------- | |||||
# Do some magic | |||||
include ../Makefile.mk | |||||
# -------------------------------------------------------------- | |||||
# Enable all possible plugin types | |||||
ifeq ($(LINUX),true) | |||||
all: jack | |||||
#lv2_sep vst | |||||
else | |||||
all: lv2_sep vst | |||||
endif | |||||
# -------------------------------------------------------------- |