| @@ -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 | |||
| # -------------------------------------------------------------- | |||