diff --git a/plugins/community/repos/squinkylabs-plug1/.gitignore b/plugins/community/repos/squinkylabs-plug1/.gitignore new file mode 100644 index 00000000..d283cbf3 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/.gitignore @@ -0,0 +1,16 @@ +**/build/ +**/build_test/ +**/dist/ +**/Debug/ +**/Release/ +**/.vs/ +.vscode/ +*.opensdf +*.sdf +*.ipch +Squinky.suo +*.dll +*.so +*.dylib +*.exe +*.user diff --git a/plugins/community/repos/squinkylabs-plug1/LICENSE-dist.txt b/plugins/community/repos/squinkylabs-plug1/LICENSE-dist.txt index 636c5ee5..8aec0774 100644 --- a/plugins/community/repos/squinkylabs-plug1/LICENSE-dist.txt +++ b/plugins/community/repos/squinkylabs-plug1/LICENSE-dist.txt @@ -7,7 +7,17 @@ All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Copyright 2016 Andrew Belt + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. @@ -65,3 +75,40 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND Component Library graphics by Grayscale (http://grayscale.info/) Licensed under CC BY-NC 4.0 (https://creativecommons.org/licenses/by-nc/4.0/) + +# Befaco modules for VCV Rack + +Copyright 2016 Andrew Belt + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +Copyright 2016 Andrew Belt + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# VCV Fundamental modules +Copyright 2016 Andrew Belt + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/Makefile b/plugins/community/repos/squinkylabs-plug1/Makefile index 184d302f..a6ec4a9b 100644 --- a/plugins/community/repos/squinkylabs-plug1/Makefile +++ b/plugins/community/repos/squinkylabs-plug1/Makefile @@ -2,13 +2,14 @@ SLUG = squinkylabs-plug1 # Must follow the format in the Versioning section of https://vcvrack.com/manual/PluginDevelopmentTutorial.html -VERSION = 0.6.5 +VERSION = 0.6.9 # FLAGS will be passed to both the C and C++ compiler FLAGS += -I./dsp/generators -I./dsp/utils -I./dsp/filters -FLAGS += -I./dsp/third-party/falco -I./dsp/third-party/kiss_fft130 -I./dsp/third-party/kiss_fft130/tools +FLAGS += -I./dsp/third-party/falco -I./dsp/third-party/kiss_fft130 +FLAGS += -I./dsp/third-party/kiss_fft130/tools -I./dsp/third-party/src FLAGS += -I./sqsrc/thread -I./dsp/fft -I./composites -FLAGS += -I./sqsrc/noise -I./sqsrc/util -I./sqsrc/clock +FLAGS += -I./sqsrc/noise -I./sqsrc/util -I./sqsrc/clock -I./sqsrc/grammar -I./sqsrc/delay CFLAGS += CXXFLAGS += @@ -35,6 +36,7 @@ LDFLAGS += -lpthread SOURCES += $(wildcard src/*.cpp) SOURCES += $(wildcard dsp/**/*.cpp) SOURCES += $(wildcard dsp/third-party/falco/*.cpp) +xxSOURCES += dsp/third-party/src/minblep.cpp SOURCES += dsp/third-party/kiss_fft130/kiss_fft.c SOURCES += dsp/third-party/kiss_fft130/tools/kiss_fftr.c SOURCES += $(wildcard sqsrc/**/*.cpp) diff --git a/plugins/community/repos/squinkylabs-plug1/README.md b/plugins/community/repos/squinkylabs-plug1/README.md index 13a7bbcd..823e4797 100644 --- a/plugins/community/repos/squinkylabs-plug1/README.md +++ b/plugins/community/repos/squinkylabs-plug1/README.md @@ -4,10 +4,12 @@ This project is a growing collection of modules for the VCV Rack vritual modular You can find us on Facebook [here](https://www.facebook.com/SquinkyLabs). -## Manuals +## Manuals and Release Notes Here is the user's manual for our modules: [instruction manual](./docs/booty-shifter.md). It contains descriptions of all of them. +The [release notes](./docs/release-notes.md) describe recent changes. + ## Contributing Please use our GitHub issues page to report problems, request features, etc. If you don’t already have a GitHub account you will need to create one, as you must be logged in to post to GitHub. diff --git a/plugins/community/repos/squinkylabs-plug1/composites/Blank.h b/plugins/community/repos/squinkylabs-plug1/composites/Blank.h new file mode 100644 index 00000000..06f95c17 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/composites/Blank.h @@ -0,0 +1,65 @@ + +#pragma once + + +template +class Blank : public TBase +{ +public: + + Blank(struct Module * module) : TBase(module) + { + } + Blank() : TBase() + { + } + + /** + * re-calc everything that changes with sample + * rate. Also everything that depends on baseFrequency. + * + * Only needs to be called once. + */ + void init(); + + enum ParamIds + { + NUM_PARAMS + }; + + enum InputIds + { + NUM_INPUTS + }; + + enum OutputIds + { + NUM_OUTPUTS + }; + + enum LightIds + { + NUM_LIGHTS + }; + + /** + * Main processing entry point. Called every sample + */ + void step() override; + +private: + +}; + + +template +inline void Blank::init() +{ +} + + +template +inline void Blank::step() +{ +} + diff --git a/plugins/community/repos/squinkylabs-plug1/composites/CHB.h b/plugins/community/repos/squinkylabs-plug1/composites/CHB.h new file mode 100644 index 00000000..5a28757c --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/composites/CHB.h @@ -0,0 +1,382 @@ + +#pragma once + +#include + +#include "AudioMath.h" +#include "poly.h" +#include "ObjectCache.h" +#include "SinOscillator.h" + +using Osc = SinOscillator; + +#ifndef _CLAMP +#define _CLAMP +namespace std { + inline float clamp(float v, float lo, float hi) + { + assert(lo < hi); + return std::min(hi, std::max(v, lo)); + } +} +#endif + +/** + */ +template +class CHB : public TBase +{ +public: + CHB(struct Module * module) : TBase(module) + { + init(); + } + CHB() : TBase() + { + init(); + } + + enum ParamIds + { + PARAM_TUNE, + PARAM_OCTAVE, + PARAM_EXTGAIN, + PARAM_PITCH_MOD_TRIM, + PARAM_LINEAR_FM_TRIM, + PARAM_EXTGAIN_TRIM, + PARAM_FOLD, + PARAM_SLOPE, + PARAM_MAG_EVEN, + PARAM_MAG_ODD, + PARAM_H0, + PARAM_H1, + PARAM_H2, + PARAM_H3, + PARAM_H4, + PARAM_H5, + PARAM_H6, + PARAM_H7, + PARAM_H8, + PARAM_H9, + + NUM_PARAMS + }; + const int numHarmonics = 1 + PARAM_H9 - PARAM_H0; + + enum InputIds + { + CV_INPUT, + PITCH_MOD_INPUT, + LINEAR_FM_INPUT, + ENV_INPUT, + GAIN_INPUT, + AUDIO_INPUT, + SLOPE_INPUT, + H0_INPUT, + H1_INPUT, + H2_INPUT, + H3_INPUT, + H4_INPUT, + H5_INPUT, + H6_INPUT, + H7_INPUT, + H8_INPUT, + H9_INPUT, + H10_INPUT, + NUM_INPUTS + }; + + enum OutputIds + { + MIX_OUTPUT, + NUM_OUTPUTS + }; + + enum LightIds + { + GAIN_GREEN_LIGHT, + GAIN_RED_LIGHT, + NUM_LIGHTS + }; + + /** + * Main processing entry point. Called every sample + */ + void step() override; + + void setEconomy(bool); + bool isEconomy() const; + + float _freq = 0; + +private: + bool economyMode = true; // let's default to economy mode + int cycleCount = 1; + int clipCount = 0; + int signalCount = 0; + const int clipDuration = 4000; + float finalGain = 0; + bool isExternalAudio = false; + + /** + * The waveshaper that is the heart of this module + */ + Poly poly; + + /* + * maps freq multiple to "octave". + * In other words, log base 12. + */ + float _octave[11]; + float getOctave(int mult) const ; + void init(); + + float _volume[11] = {0}; + + /** + * Internal sine wave oscillator to drive the waveshaper + */ + SinOscillatorParams sinParams; + SinOscillatorState sinState; + + // just maps 0..1 to 0..1 + std::shared_ptr> audioTaper = {ObjectCache::getAudioTaper()}; + + AudioMath::ScaleFun gainCombiner = AudioMath::makeLinearScaler(0.f, 1.f); + + std::function expLookup = ObjectCache::getExp2Ex(); + std::shared_ptr> db2gain = ObjectCache::getDb2Gain(); + + /** + * Audio taper for the slope. + */ + AudioMath::ScaleFun slopeScale = + {AudioMath::makeLinearScaler(-18, 0)}; + + /** + * do one-time calculations when sample rate changes + */ + void internalUpdate(); + + /** + * Do all the processing to get the input waveform + * that will be fed to the polynomials + */ + float getInput(); + + void calcVolumes(float *); + + void checkClipping(float sample); + + /** + * Does audio taper + * @param raw = 0..1 + * @return 0..1 + */ + float taper(float raw) + { + return LookupTable::lookup(*audioTaper, raw, false); + } +}; + +template +inline void CHB::init() +{ + for (int i = 0; i < 11; ++i) { + _octave[i] = log2(float(i + 1)); + } +} + +template +inline float CHB::getOctave(int i) const +{ + assert(i >= 0 && i < 11); + return _octave[i]; +} + +#if 1 +template +inline void CHB::setEconomy(bool b) +{ + economyMode = b; +} + +template +inline bool CHB::isEconomy() const +{ + return economyMode; +} +#endif + +template +inline float CHB::getInput() +{ + assert(TBase::engineGetSampleTime() > 0); + + // Get the frequency from the inputs. + float pitch = 1.0f + roundf(TBase::params[PARAM_OCTAVE].value) + TBase::params[PARAM_TUNE].value / 12.0f; + pitch += TBase::inputs[CV_INPUT].value; + pitch += .25f * TBase::inputs[PITCH_MOD_INPUT].value * + taper(TBase::params[PARAM_PITCH_MOD_TRIM].value); + + const float q = float(log2(261.626)); // move up to pitch range of EvenVCO + pitch += q; + _freq = expLookup(pitch); + + if (_freq < .01f) { + _freq = .01f; + } + + // Multiply in the Linear FM contribution + _freq *= 1.0f + TBase::inputs[LINEAR_FM_INPUT].value * taper(TBase::params[PARAM_LINEAR_FM_TRIM].value); + float time = std::clamp(_freq * TBase::engineGetSampleTime(), -.5f, 0.5f); + + Osc::setFrequency(sinParams, time); + + if (cycleCount == 0) { + // Get the gain from the envelope generator in + // eGain = {0 .. 10.0f } + float eGain = TBase::inputs[ENV_INPUT].active ? TBase::inputs[ENV_INPUT].value : 10.f; + isExternalAudio = TBase::inputs[AUDIO_INPUT].active; + + const float gainKnobValue = TBase::params[PARAM_EXTGAIN].value; + const float gainCVValue = TBase::inputs[GAIN_INPUT].value; + const float gainTrimValue = TBase::params[PARAM_EXTGAIN_TRIM].value; + const float combinedGain = gainCombiner(gainCVValue, gainKnobValue, gainTrimValue); + + // tapered gain {0 .. 0.5} + const float taperedGain = .5f * taper(combinedGain); + + // final gain 0..5 + finalGain = taperedGain * eGain; + } + + float input = finalGain * (isExternalAudio ? + TBase::inputs[AUDIO_INPUT].value : + Osc::run(sinState, sinParams)); + + checkClipping(input); + + + // Now clip or fold to keep in -1...+1 + if (TBase::params[PARAM_FOLD].value > .5) { + input = AudioMath::fold(input); + } else { + input = std::max(input, -1.f); + input = std::min(input, 1.f); + } + + return input; +} + +/** + * Desired behavior: + * If we clip, led goes red and stays red for clipDuration + * if not red, sign present goes green + * nothing - turn off + */ +template +inline void CHB::checkClipping(float input) +{ + if (input > 1) { + // if clipping, go red + clipCount = clipDuration; + TBase::lights[GAIN_RED_LIGHT].value = 10; + TBase::lights[GAIN_GREEN_LIGHT].value = 0; + } else if (clipCount) { + // If red,run down the clock + clipCount--; + if (clipCount <= 0) { + TBase::lights[GAIN_RED_LIGHT].value = 0; + TBase::lights[GAIN_GREEN_LIGHT].value = 0; + } + } else if (input > .3f) { + // if signal present + signalCount = clipDuration; + TBase::lights[GAIN_GREEN_LIGHT].value = 10; + } else if (signalCount) { + signalCount--; + if (signalCount <= 0) { + TBase::lights[GAIN_GREEN_LIGHT].value = 0; + } + } +} + +template +inline void CHB::calcVolumes(float * volumes) +{ + // first get the harmonics knobs, and scale them + for (int i = 0; i < numHarmonics; ++i) { + float val = taper(TBase::params[i + PARAM_H0].value); // apply taper to the knobs + + // If input connected, scale and multiply with knob value + if (TBase::inputs[i + H0_INPUT].active) { + const float inputCV = TBase::inputs[i + H0_INPUT].value * .1f; + val *= std::max(inputCV, 0.f); + } + + volumes[i] = val; + } + + // Second: apply the even and odd knobs + { + const float even = taper(TBase::params[PARAM_MAG_EVEN].value); + const float odd = taper(TBase::params[PARAM_MAG_ODD].value); + for (int i = 1; i < 11; ++i) { + const float mul = (i & 1) ? even : odd; // 0 = fundamental, 1=even, 2=odd.... + volumes[i] *= mul; + } + } + + // Third: slope + { + const float slope = slopeScale(TBase::params[PARAM_SLOPE].value, TBase::inputs[SLOPE_INPUT].value, 1); + + for (int i = 0; i < 11; ++i) { + float slopeAttenDb = slope * getOctave(i); + float slopeAtten = LookupTable::lookup(*db2gain, slopeAttenDb); + volumes[i] *= slopeAtten; + } + } +} + +template +inline void CHB::step() +{ + if (economyMode) { + if (--cycleCount < 0) { + cycleCount = 3; + } + } else { + cycleCount = 0; + } + + // do all the processing to get the carrier signal + const float input = getInput(); + +#if 0 + { + static float high=0; + static float low=0; + if (input high) { + high = std::max(high, input); + low = std::min(low, input); + printf("%f, %f\n", high, low); + fflush(stdout); + } + } + #endif + + // float volume[11]; + if (cycleCount == 0) { + calcVolumes(_volume); + + for (int i = 0; i < 11; ++i) { + poly.setGain(i, _volume[i]); + } + } + + float output = poly.run(input); + TBase::outputs[MIX_OUTPUT].value = 5.0f * output; +} + diff --git a/plugins/community/repos/squinkylabs-plug1/composites/ColoredNoise.h b/plugins/community/repos/squinkylabs-plug1/composites/ColoredNoise.h index 087daff3..b6e354f6 100644 --- a/plugins/community/repos/squinkylabs-plug1/composites/ColoredNoise.h +++ b/plugins/community/repos/squinkylabs-plug1/composites/ColoredNoise.h @@ -75,7 +75,7 @@ public: /** * Main processing entry point. Called every sample */ - void step(); + void step() override; float getSlope() const; @@ -159,9 +159,9 @@ protected: FFT::makeNoiseSpectrum(noiseSpectrum.get(), noiseMessage->noiseSpec); -// Now inverse FFT to time domain noise in client's buffer + // Now inverse FFT to time domain noise in client's buffer FFT::inverse(noiseMessage->dataBuffer.get(), *noiseSpectrum.get()); - FFT::normalize(noiseMessage->dataBuffer.get()); + FFT::normalize(noiseMessage->dataBuffer.get(), 5); // use 5v amplitude. sendMessageToClient(noiseMessage); } private: diff --git a/plugins/community/repos/squinkylabs-plug1/composites/EV3.h b/plugins/community/repos/squinkylabs-plug1/composites/EV3.h new file mode 100644 index 00000000..69dc6cbd --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/composites/EV3.h @@ -0,0 +1,279 @@ +#pragma once + +#include "MinBLEPVCO.h" +#include "ObjectCache.h" + +#include "dsp/functions.hpp" // rack math + +/** + * + */ +template +class EV3 : public TBase +{ +public: + friend class TestMB; + EV3(struct Module * module) : TBase(module) + { + init(); + } + + EV3() : TBase() + { + init(); + } + + enum class Waves + { + SIN, + TRI, + SAW, + SQUARE, + EVEN, + NONE, + END // just a marker + }; + + enum ParamIds + { + MIX1_PARAM, + MIX2_PARAM, + MIX3_PARAM, + OCTAVE1_PARAM, + SEMI1_PARAM, + FINE1_PARAM, + FM1_PARAM, + SYNC1_PARAM, + WAVE1_PARAM, + PW1_PARAM, + PWM1_PARAM, + + OCTAVE2_PARAM, + SEMI2_PARAM, + FINE2_PARAM, + FM2_PARAM, + SYNC2_PARAM, + WAVE2_PARAM, + PW2_PARAM, + PWM2_PARAM, + + OCTAVE3_PARAM, + SEMI3_PARAM, + FINE3_PARAM, + FM3_PARAM, + SYNC3_PARAM, + WAVE3_PARAM, + PW3_PARAM, + PWM3_PARAM, + + NUM_PARAMS + }; + + enum InputIds + { + CV1_INPUT, + CV2_INPUT, + CV3_INPUT, + FM1_INPUT, + FM2_INPUT, + FM3_INPUT, + PWM1_INPUT, + PWM2_INPUT, + PWM3_INPUT, + NUM_INPUTS + }; + + enum OutputIds + { + MIX_OUTPUT, + VCO1_OUTPUT, + VCO2_OUTPUT, + VCO3_OUTPUT, + NUM_OUTPUTS + }; + + enum LightIds + { + NUM_LIGHTS + }; + + void step() override; + +private: + void setSync(); + void processPitchInputs(); + void processPitchInputs(int osc); + void processWaveforms(); + void stepVCOs(); + void init(); + void processPWInputs(); + void processPWInput(int osc); + float getInput(int osc, InputIds in0, InputIds in1, InputIds in2); + + MinBLEPVCO vcos[3]; + float _freq[3]; + float _out[3]; + std::function expLookup = + ObjectCache::getExp2Ex(); + std::shared_ptr> audioTaper = + ObjectCache::getAudioTaper(); +}; + +template +inline void EV3::init() +{ + for (int i = 0; i < 3; ++i) { + vcos[i].setWaveform(MinBLEPVCO::Waveform::Saw); + } + + vcos[0].setSyncCallback([this](float f) { + + if (TBase::params[SYNC2_PARAM].value > .5) { + vcos[1].onMasterSync(f); + } + if (TBase::params[SYNC3_PARAM].value > .5) { + vcos[2].onMasterSync(f); + } + }); +} + +template +inline void EV3::setSync() +{ + vcos[0].setSyncEnabled(false); + vcos[1].setSyncEnabled(TBase::params[SYNC2_PARAM].value > .5); + vcos[2].setSyncEnabled(TBase::params[SYNC3_PARAM].value > .5); +} + +template +inline void EV3::processWaveforms() +{ + vcos[0].setWaveform((MinBLEPVCO::Waveform)(int)TBase::params[WAVE1_PARAM].value); + vcos[1].setWaveform((MinBLEPVCO::Waveform)(int)TBase::params[WAVE2_PARAM].value); + vcos[2].setWaveform((MinBLEPVCO::Waveform)(int)TBase::params[WAVE3_PARAM].value); +} + +template +float EV3::getInput(int osc, InputIds in1, InputIds in2, InputIds in3) +{ + const bool in2Connected = TBase::inputs[in2].active; + const bool in3Connected = TBase::inputs[in3].active; + InputIds id = in1; + if ((osc == 1) && in2Connected) { + id = in2; + } + if (osc == 2) { + if (in3Connected) id = in3; + else if (in2Connected) id = in2; + } + return TBase::inputs[id].value; +} + +template +void EV3::processPWInput(int osc) +{ + const float pwmInput = getInput(osc, PWM1_INPUT, PWM2_INPUT, PWM3_INPUT) / 5.f; + + const int delta = osc * (OCTAVE2_PARAM - OCTAVE1_PARAM); + const float pwmTrim = TBase::params[PWM1_PARAM + delta].value; + const float pwInit = TBase::params[PW1_PARAM + delta].value; + + float pw = pwInit + pwmInput * pwmTrim; + const float minPw = 0.05f; + pw = rack::rescale(std::clamp(pw, -1.0f, 1.0f), -1.0f, 1.0f, minPw, 1.0f - minPw); + vcos[osc].setPulseWidth(pw); +} + +template +inline void EV3::processPWInputs() +{ + processPWInput(0); + processPWInput(1); + processPWInput(2); +} + +template +inline void EV3::step() +{ + setSync(); + processPitchInputs(); + processWaveforms(); + processPWInputs(); + stepVCOs(); + float mix = 0; + + for (int i = 0; i < 3; ++i) { + + const float knob = TBase::params[MIX1_PARAM + i].value; + const float gain = LookupTable::lookup(*audioTaper, knob, false); + const float rawWaveform = vcos[i].getOutput(); + const float scaledWaveform = rawWaveform * gain; + mix += scaledWaveform; + _out[i] = scaledWaveform; + TBase::outputs[VCO1_OUTPUT + i].value = rawWaveform; + } + TBase::outputs[MIX_OUTPUT].value = mix; +} + +template +inline void EV3::stepVCOs() +{ + for (int i = 0; i < 3; ++i) { + vcos[i].step(); + } +} + +template +inline void EV3::processPitchInputs() +{ + float lastFM = 0; + for (int osc = 0; osc < 3; ++osc) { + assert(osc >= 0 && osc <= 2); + const int delta = osc * (OCTAVE2_PARAM - OCTAVE1_PARAM); + + const float cv = getInput(osc, CV1_INPUT, CV2_INPUT, CV3_INPUT); + const float finePitch = TBase::params[FINE1_PARAM + delta].value / 12.0f; + const float semiPitch = TBase::params[SEMI1_PARAM + delta].value / 12.0f; + // const float fm = getInput(osc, FM1_INPUT, FM2_INPUT, FM3_INPUT); + + float pitch = 1.0f + roundf(TBase::params[OCTAVE1_PARAM + delta].value) + + semiPitch + + finePitch; + pitch += cv; + + float fmCombined = 0; // The final, scaled, value (post knob + if (TBase::inputs[FM1_INPUT + osc].active) { + const float fm = TBase::inputs[FM1_INPUT + osc].value; + // const float fmKnob = TBase::params[FM1_PARAM + delta].value; + //const float fmDepth = LookupTable::lookup(*audioTaper, fmKnob, false); + const float fmDepth = rack::quadraticBipolar(TBase::params[FM1_PARAM + delta].value); + + fmCombined = (fmDepth * fm); +#if 0 + static float biggest = 0; + if (fmCombined > biggest) { + printf("CV =%f knob = %f depth=%f combined=%f\n", fm, fmKnob, fmDepth, fmCombined); + fflush(stdout); + biggest = fmCombined; + } +#endif + + // pitch += (fmDepth * fm * 12); + } else { + fmCombined = lastFM; + } + pitch += fmCombined; + lastFM = fmCombined; + + + const float q = float(log2(261.626)); // move up to pitch range of EvenVCO + pitch += q; + const float freq = expLookup(pitch); + _freq[osc] = freq; + vcos[osc].setNormalizedFreq(TBase::engineGetSampleTime() * freq, + TBase::engineGetSampleTime()); + } +} + + + diff --git a/plugins/community/repos/squinkylabs-plug1/composites/FrequencyShifter.h b/plugins/community/repos/squinkylabs-plug1/composites/FrequencyShifter.h index 67456eb1..e29647f1 100644 --- a/plugins/community/repos/squinkylabs-plug1/composites/FrequencyShifter.h +++ b/plugins/community/repos/squinkylabs-plug1/composites/FrequencyShifter.h @@ -67,7 +67,7 @@ public: /** * Main processing entry point. Called every sample */ - void step(); + void step() override; typedef float T; // use floats for all signals T freqRange = 5; // the freq range switch diff --git a/plugins/community/repos/squinkylabs-plug1/composites/FunVCOComposite.h b/plugins/community/repos/squinkylabs-plug1/composites/FunVCOComposite.h new file mode 100644 index 00000000..91ee929d --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/composites/FunVCOComposite.h @@ -0,0 +1,107 @@ +#pragma once + + +#include "FunVCO.h" +//#define _ORIGVCO + +template +class FunVCOComposite : public TBase +{ +public: + FunVCOComposite() + { + init(); + } + FunVCOComposite(struct Module * module) : TBase(module) + { + init(); + } + enum ParamIds + { + MODE_PARAM, + SYNC_PARAM, + FREQ_PARAM, + FINE_PARAM, + FM_PARAM, + PW_PARAM, + PWM_PARAM, + NUM_PARAMS + }; + enum InputIds + { + PITCH_INPUT, + FM_INPUT, + SYNC_INPUT, + PW_INPUT, + NUM_INPUTS + }; + enum OutputIds + { + SIN_OUTPUT, + TRI_OUTPUT, + SAW_OUTPUT, + SQR_OUTPUT, + NUM_OUTPUTS + }; + enum LightIds + { + NUM_LIGHTS + }; + + + void step() override; + void init() + { + oscillator.init(); + } + + void setSampleRate(float rate) + { + oscillator.sampleTime = 1.f / rate; + } + +private: +#ifdef _ORIGVCO + VoltageControlledOscillatorOrig<16, 16> oscillator; +#else + VoltageControlledOscillator<16, 16> oscillator; +#endif +}; + +template +inline void FunVCOComposite::step() +{ + oscillator.analog = TBase::params[MODE_PARAM].value > 0.0f; + oscillator.soft = TBase::params[SYNC_PARAM].value <= 0.0f; + + float pitchFine = 3.0f * quadraticBipolar(TBase::params[FINE_PARAM].value); + float pitchCv = 12.0f * TBase::inputs[PITCH_INPUT].value; + if (TBase::inputs[FM_INPUT].active) { + pitchCv += quadraticBipolar(TBase::params[FM_PARAM].value) * 12.0f * TBase::inputs[FM_INPUT].value; + } + + oscillator.setPitch(TBase::params[FREQ_PARAM].value, pitchFine + pitchCv); + + + oscillator.setPulseWidth(TBase::params[PW_PARAM].value + TBase::params[PWM_PARAM].value * TBase::inputs[PW_INPUT].value / 10.0f); + oscillator.syncEnabled = TBase::inputs[SYNC_INPUT].active; + +#ifndef _ORIGVCO + oscillator.sawEnabled = TBase::outputs[SAW_OUTPUT].active; + oscillator.sinEnabled = TBase::outputs[SIN_OUTPUT].active; + oscillator.sqEnabled = TBase::outputs[SQR_OUTPUT].active; + oscillator.triEnabled = TBase::outputs[TRI_OUTPUT].active; +#endif + + oscillator.process(TBase::engineGetSampleTime(), TBase::inputs[SYNC_INPUT].value); + // Set output + if (TBase::outputs[SIN_OUTPUT].active) + TBase::outputs[SIN_OUTPUT].value = 5.0f * oscillator.sin(); + if (TBase::outputs[TRI_OUTPUT].active) + TBase::outputs[TRI_OUTPUT].value = 5.0f * oscillator.tri(); + if (TBase::outputs[SAW_OUTPUT].active) + TBase::outputs[SAW_OUTPUT].value = 5.0f * oscillator.saw(); + if (TBase::outputs[SQR_OUTPUT].active) + TBase::outputs[SQR_OUTPUT].value = 5.0f * oscillator.sqr(); + +} \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/composites/GMR.h b/plugins/community/repos/squinkylabs-plug1/composites/GMR.h new file mode 100644 index 00000000..284e02c6 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/composites/GMR.h @@ -0,0 +1,88 @@ + +#pragma once +#include "ObjectCache.h" +#include "GenerativeTriggerGenerator.h" +#include "TriggerOutput.h" + +#include + +/** + */ +template +class GMR : public TBase +{ +public: + GMR(struct Module * module) : TBase(module), inputClockProcessing(true) + { + } + GMR() : TBase(), inputClockProcessing(true) + { + } + void setSampleRate(float rate) + { + reciprocalSampleRate = 1 / rate; + } + + // must be called after setSampleRate + void init(); + + enum ParamIds + { + NUM_PARAMS + }; + + enum InputIds + { + CLOCK_INPUT, + NUM_INPUTS + }; + + enum OutputIds + { + TRIGGER_OUTPUT, + NUM_OUTPUTS + }; + + enum LightIds + { + NUM_LIGHTS + }; + + /** + * Main processing entry point. Called every sample + */ + void step() override; + +private: + float reciprocalSampleRate = 0; + std::shared_ptr gtg; + GateTrigger inputClockProcessing; + TriggerOutput outputProcessing; +}; + + + +template +inline void GMR::init() +{ + StochasticGrammarDictionary::Grammar grammar = StochasticGrammarDictionary::getGrammar(0); + gtg = std::make_shared( + AudioMath::random(), + grammar.rules, + grammar.numRules, + grammar.firstRule); +} + +template +inline void GMR::step() +{ + bool outClock = false; + float inClock = TBase::inputs[CLOCK_INPUT].value; + inputClockProcessing.go(inClock); + if (inputClockProcessing.trigger()) { + outClock = gtg->clock(); + } + outputProcessing.go(outClock); + TBase::outputs[TRIGGER_OUTPUT].value = outputProcessing.get(); +} + diff --git a/plugins/community/repos/squinkylabs-plug1/composites/Gray.h b/plugins/community/repos/squinkylabs-plug1/composites/Gray.h new file mode 100644 index 00000000..b5b1e234 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/composites/Gray.h @@ -0,0 +1,141 @@ +#pragma once + +#include "GateTrigger.h" + +static const uint8_t gtable[256] = +{ +0, 1, 3, 2, 6, 7, 5, 4, 12, 13, 15, 14, 10, 11, 9, 8, +24, 25, 27, 26, 30, 31, 29, 28, 20, 21, 23, 22, 18, 19, 17, 16, +48, 49, 51, 50, 54, 55, 53, 52, 60, 61, 63, 62, 58, 59, 57, 56, +40, 41, 43, 42, 46, 47, 45, 44, 36, 37, 39, 38, 34, 35, 33, 32, +96, 97, 99, 98, 102, 103, 101, 100, 108, 109, 111, 110, 106, 107, 105, 104, +120, 121, 123, 122, 126, 127, 125, 124, 116, 117, 119, 118, 114, 115, 113, 112, +80, 81, 83, 82, 86, 87, 85, 84, 92, 93, 95, 94, 90, 91, 89, 88, +72, 73, 75, 74, 78, 79, 77, 76, 68, 69, 71, 70, 66, 67, 65, 64, +192, 193, 195, 194, 198, 199, 197, 196, 204, 205, 207, 206, 202, 203, 201, 200, +216, 217, 219, 218, 222, 223, 221, 220, 212, 213, 215, 214, 210, 211, 209, 208, +240, 241, 243, 242, 246, 247, 245, 244, 252, 253, 255, 254, 250, 251, 249, 248, +232, 233, 235, 234, 238, 239, 237, 236, 228, 229, 231, 230, 226, 227, 225, 224, +160, 161, 163, 162, 166, 167, 165, 164, 172, 173, 175, 174, 170, 171, 169, 168, +184, 185, 187, 186, 190, 191, 189, 188, 180, 181, 183, 182, 178, 179, 177, 176, +144, 145, 147, 146, 150, 151, 149, 148, 156, 157, 159, 158, 154, 155, 153, 152, +136, 137, 139, 138, 142, 143, 141, 140, 132, 133, 135, 134, 130, 131, 129, 128 +}; + +static const uint8_t bgtable[256] = +{ +0x00, 0x01, 0x03, 0x02, 0x06, 0x0E, 0x0A, 0x0B, 0x09, 0x0D, 0x0F, 0x07, 0x05, 0x04, 0x0C, 0x08, +0x18, 0x1C, 0x14, 0x15, 0x17, 0x1F, 0x3F, 0x37, 0x35, 0x34, 0x3C, 0x38, 0x28, 0x2C, 0x24, 0x25, +0x27, 0x2F, 0x2D, 0x29, 0x39, 0x3D, 0x1D, 0x19, 0x1B, 0x3B, 0x2B, 0x2A, 0x3A, 0x1A, 0x1E, 0x16, +0x36, 0x3E, 0x2E, 0x26, 0x22, 0x32, 0x12, 0x13, 0x33, 0x23, 0x21, 0x31, 0x11, 0x10, 0x30, 0x20, +0x60, 0x70, 0x50, 0x51, 0x71, 0x61, 0x63, 0x73, 0x53, 0x52, 0x72, 0x62, 0x66, 0x6E, 0x7E, 0x76, +0x56, 0x5E, 0x5A, 0x7A, 0x6A, 0x6B, 0xEB, 0xEA, 0xFA, 0xDA, 0xDE, 0xD6, 0xF6, 0xFE, 0xEE, 0xE6, +0xE2, 0xF2, 0xD2, 0xD3, 0xF3, 0xE3, 0xE1, 0xF1, 0xD1, 0xD0, 0xF0, 0xE0, 0xA0, 0xB0, 0x90, 0x91, +0xB1, 0xA1, 0xA3, 0xB3, 0x93, 0x92, 0xB2, 0xA2, 0xA6, 0xAE, 0xBE, 0xB6, 0x96, 0x9E, 0x9A, 0xBA, +0xAA, 0xAB, 0xBB, 0x9B, 0x99, 0x9D, 0xDD, 0xD9, 0xDB, 0xFB, 0x7B, 0x5B, 0x59, 0x5D, 0x7D, 0x79, +0xF9, 0xFD, 0xBD, 0xB9, 0xA9, 0xE9, 0x69, 0x6D, 0x6F, 0x67, 0x65, 0x64, 0xE4, 0xE5, 0xE7, 0xEF, +0xED, 0xAD, 0xAF, 0xA7, 0xA5, 0xA4, 0xAC, 0xEC, 0x6C, 0x68, 0xE8, 0xA8, 0xB8, 0xF8, 0x78, 0x7C, +0xFC, 0xBC, 0xB4, 0xB5, 0xB7, 0xF7, 0xF5, 0xF4, 0x74, 0x75, 0x77, 0x7F, 0xFF, 0xBF, 0x9F, 0xDF, +0x5F, 0x57, 0x55, 0x54, 0xD4, 0xD5, 0xD7, 0x97, 0x95, 0x94, 0x9C, 0xDC, 0x5C, 0x58, 0xD8, 0x98, +0x88, 0xC8, 0x48, 0x4C, 0xCC, 0x8C, 0x84, 0xC4, 0x44, 0x45, 0xC5, 0x85, 0x87, 0xC7, 0x47, 0x4F, +0xCF, 0x8F, 0x8D, 0xCD, 0x4D, 0x49, 0xC9, 0x89, 0x8B, 0xCB, 0x4B, 0x4A, 0xCA, 0x8A, 0x8E, 0xCE, +0x4E, 0x46, 0xC6, 0x86, 0x82, 0xC2, 0x42, 0x43, 0xC3, 0x83, 0x81, 0xC1, 0x41, 0x40, 0xC0, 0x80 +}; + +template +class Gray : public TBase +{ +public: + Gray(struct Module * module) : TBase(module), gateTrigger(true) + { + init(); + } + Gray() : TBase(), gateTrigger(true) + { + init(); + } + + enum ParamIds + { + PARAM_CODE, + NUM_PARAMS + }; + + enum InputIds + { + INPUT_CLOCK, + NUM_INPUTS + }; + + enum OutputIds + { + OUTPUT_MIXED, + OUTPUT_0, + OUTPUT_1, + OUTPUT_2, + OUTPUT_3, + OUTPUT_4, + OUTPUT_5, + OUTPUT_6, + OUTPUT_7, + NUM_OUTPUTS + }; + + enum LightIds + { + LIGHT_0, + LIGHT_1, + LIGHT_2, + LIGHT_3, + LIGHT_4, + LIGHT_5, + LIGHT_6, + LIGHT_7, + NUM_LIGHTS + }; + + /** + * Main processing entry point. Called every sample + */ + void step() override; + +private: + uint8_t counterValue = 0; + GateTrigger gateTrigger; + int c = 0; + void init(); +}; + + +template +void Gray::init() +{ + // Init all the outputs to zero, + // since they don't all get update until a clock. + for (int i=0; i +void Gray::step() +{ + gateTrigger.go(TBase::inputs[INPUT_CLOCK].value); + if (!gateTrigger.trigger()) { + return; + } + ++counterValue; + + const uint8_t* table = TBase::params[PARAM_CODE].value > .5 ? gtable : bgtable; + + const auto g0 = table[counterValue]; + auto g = g0; + for (int i=0; i<8; ++i) { + bool b = g & 1; + TBase::lights[i + LIGHT_0].value = b ? 10 : 0; + TBase::outputs[i + OUTPUT_0].value = b ? 10 : 0; + g >>= 1; + } + TBase::outputs[OUTPUT_MIXED].value = (float) g0/25.f; +} diff --git a/plugins/community/repos/squinkylabs-plug1/composites/LFN.h b/plugins/community/repos/squinkylabs-plug1/composites/LFN.h new file mode 100644 index 00000000..d5003422 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/composites/LFN.h @@ -0,0 +1,247 @@ + +#pragma once + +#include "ButterworthFilterDesigner.h" +#include "Decimator.h" +#include "GraphicEq.h" +#include "LowpassFilter.h" +#include "BiquadParams.h" +#include "BiquadState.h" +#include "BiquadFilter.h" +#include "ObjectCache.h" +#include + +/** + * Noise generator feeding a graphic equalizer. + * Calculated at very low sample rate, then re-sampled + * up to audio rate. + * + * Below assumes 44k SR. TODO: other rates. + * + * We first design the EQ around bands of 100, 200, 400, 800, + * 1600. EQ gets noise. + * + * Then output of EQ is re-sampled up by a factor of 100 + * to bring the first band down to 1hz. + * or : decimation factor = 100 * (fs) / 44100. + * + * A butterworth lowpass then removes the re-sampling artifacts. + * Otherwise these images bring in high frequencies that we + * don't want. + * + * Cutoff for the filter can be as low as the top of the eq, + * which is 3.2khz. 44k/3.2k is about 10, + * so fc/fs can be 1/1000. + * + * or : fc = (fs / 44100) / 1000; + * + * (had been using fc/fs = float(1.0 / (44 * 100.0)));) + * + * Design for R = root freq (was 1 Hz, above) + * EQ first band at E (was 100 Hz, above) + * + * Decimation divider = E / R + * + * Imaging filter fc = 3.2khz / decimation-divider + * fc/fs = 3200 * (reciprocal sr) / decimation-divider. + * + * Experiment: let's use those values and compare to what we had been using. + * result: not too far off. + * + * make a range/base control. map -5 to +5 into 1/10 Hz to 2 Hz rate. Can use regular + * functions, since we won't calc that often. + */ + +template +class LFN : public TBase +{ +public: + + LFN(struct Module * module) : TBase(module) + { + } + LFN() : TBase() + { + } + + void setSampleTime(float time) + { + reciprocalSampleRate = time; + updateLPF(); + } + + /** + * re-calc everything that changes with sample + * rate. Also everything that depends on baseFrequency. + * + * Only needs to be called once. + */ + void init(); + + enum ParamIds + { + EQ0_PARAM, + EQ1_PARAM, + EQ2_PARAM, + EQ3_PARAM, + EQ4_PARAM, + FREQ_RANGE_PARAM, + NUM_PARAMS + }; + + enum InputIds + { + EQ0_INPUT, + EQ1_INPUT, + EQ2_INPUT, + EQ3_INPUT, + EQ4_INPUT, + NUM_INPUTS + }; + + enum OutputIds + { + OUTPUT, + NUM_OUTPUTS + }; + + enum LightIds + { + NUM_LIGHTS + }; + + /** + * Main processing entry point. Called every sample + */ + void step() override; + + float getBaseFrequency() const + { + return baseFrequency; + } + + /** + * This lets the butterworth get re-calculated on the UI thread. + * We can't do it on the audio thread, because it calls malloc. + */ + void pollForChangeOnUIThread(); + +private: + float reciprocalSampleRate = 0; + + ::Decimator decimator; + + GraphicEq2<5> geq; + + /** + * Template type for butterworth reconstruction filter + * Tried double for best low frequency performance. It's + * probably overkill, but calculates plenty fast. + */ + using TButter = double; + BiquadParams lpfParams; + BiquadState lpfState; + + /** + * Frequency, in Hz, of the lowest band in the graphic EQ + */ + float baseFrequency = 1; + + /** + * The last value baked by the LPF filter calculation + * done on the UI thread. + */ + float lastBaseFrequencyParamValue = -100; + + std::default_random_engine generator{57}; + std::normal_distribution distribution{-1.0, 1.0}; + float noise() + { + return (float) distribution(generator); + } + + /** + * Must be called after baseFrequency is updated. + * re-calculates the butterworth lowpass. + */ + void updateLPF(); + + /** + * scaling function for the range / base frequency knob + * map knob range from .1 Hz to 2.0 Hz + */ + std::function rangeFunc = + {AudioMath::makeFunc_Exp(-5, 5, .1, 2)}; + + /** + * Audio taper for the EQ gains. Arbitrary max value selected + * to give "good" output level. + */ + AudioMath::SimpleScaleFun gainScale = + {AudioMath::makeSimpleScalerAudioTaper(0, 35)}; +}; + +template +inline void LFN::pollForChangeOnUIThread() +{ + if (lastBaseFrequencyParamValue != TBase::params[FREQ_RANGE_PARAM].value) { + lastBaseFrequencyParamValue = TBase::params[FREQ_RANGE_PARAM].value; + baseFrequency = float(rangeFunc(lastBaseFrequencyParamValue)); + + updateLPF(); // now get the filters updated + } +} + +template +inline void LFN::init() +{ + updateLPF(); +} + +template +inline void LFN::updateLPF() +{ + assert(reciprocalSampleRate > 0); + // decimation must be 100hz (what our EQ is designed at) + // divided by base. + const float decimationDivider = float(100.0 / baseFrequency); + decimator.setDecimationRate(decimationDivider); + + // calculate lpFc ( Fc / sr) + // Imaging filter fc = 3.2khz / decimation-divider + // fc/fs = 3200 * (reciprocal sr) / decimation-divider. + const float lpFc = 3200 * reciprocalSampleRate / decimationDivider; + ButterworthFilterDesigner::designThreePoleLowpass( + lpfParams, lpFc); +} + +template +inline void LFN::step() +{ + // Let's only check the inputs every 4 samples. Still plenty fast, but + // get the CPU usage down really far. + static int count = 0; + if (count++ > 4) { + count = 0; + const int numEqStages = geq.getNumStages(); + for (int i = 0; i < numEqStages; ++i) { + auto paramNum = i + EQ0_PARAM; + auto cvNum = i + EQ0_INPUT; + const float gainParamKnob = TBase::params[paramNum].value; + const float gainParamCV = TBase::inputs[cvNum].value; + const float gain = gainScale(gainParamKnob, gainParamCV); + geq.setGain(i, gain); + } + } + + bool needsData; + TButter x = decimator.clock(needsData); + x = BiquadFilter::run(x, lpfState, lpfParams); + if (needsData) { + const float z = geq.run(noise()); + decimator.acceptData(z); + } + + TBase::outputs[OUTPUT].value = (float) x; +} + diff --git a/plugins/community/repos/squinkylabs-plug1/composites/Shaper.h b/plugins/community/repos/squinkylabs-plug1/composites/Shaper.h new file mode 100644 index 00000000..12b81305 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/composites/Shaper.h @@ -0,0 +1,383 @@ +#pragma once + +#include "IIRUpsampler.h" +#include "IIRDecimator.h" +#include "LookupTable.h" +#include "AsymWaveShaper.h" +#include "ObjectCache.h" + +/** +Version 1, cpu usage: + full wave: 95 + crush: 281 + asy:149 + fold: 102 + fold2: 154 + + X4 on input scanning: + full wave: 85 + crush: 278 + asy:163 + fold: 89 + fold2: 154 + + inline: + fw: 75 + crush: 87 + asy: 112 + fold: 77 + fold2: 136 + + + */ +template +class Shaper : public TBase +{ +public: + Shaper(struct Module * module) : TBase(module) + { + init(); + } + Shaper() : TBase() + { + init(); + } + + enum class Shapes + { + AsymSpline, + Clip, + EmitterCoupled, + FullWave, + HalfWave, + Fold, + Fold2, + Crush, + Invalid + }; + + static const char* getString(Shapes); + + enum ParamIds + { + PARAM_SHAPE, + PARAM_GAIN, + PARAM_GAIN_TRIM, + PARAM_OFFSET, + PARAM_OFFSET_TRIM, + PARAM_OVERSAMPLE, + NUM_PARAMS + }; + + enum InputIds + { + INPUT_AUDIO, + INPUT_GAIN, + INPUT_OFFSET, + NUM_INPUTS + }; + + enum OutputIds + { + OUTPUT_AUDIO, + NUM_OUTPUTS + }; + + enum LightIds + { + NUM_LIGHTS + }; + + /** + * Main processing entry point. Called every sample + */ + void step() override; + + float _gain = 0; + float _offset = 0; + float _gainInput = 0; + +private: + std::shared_ptr> audioTaper = {ObjectCache::getAudioTaper()}; + std::shared_ptr> sinLookup = {ObjectCache::getSinLookup()}; + AudioMath::ScaleFun scaleGain = AudioMath::makeLinearScaler(0, 1); + AudioMath::ScaleFun scaleOffset = AudioMath::makeLinearScaler(-5, 5); + + // domain starts at 2. + // std::shared_ptr> exp2Lookup = {ObjectCache::getExp2()}; + + const static int maxOversample = 16; + int curOversample = 16; + void init(); + IIRUpsampler up; + IIRDecimator dec; + std::shared_ptr> tanhLookup; + AsymWaveShaper asymShaper; + int cycleCount = 0; + Shapes shape = Shapes::Clip; + int asymCurveindex = 0; + + void processCV(); + void setOversample(); + void processBuffer(float *) const; +}; + +template +const char* Shaper::getString(Shapes shape) +{ + const char* ret = ""; + switch (shape) { + case Shapes::Clip: + ret = "Clip"; + break; + case Shapes::EmitterCoupled: + ret = "Emitter Coupled"; + break; + case Shapes::FullWave: + ret = "Full Wave"; + break; + case Shapes::HalfWave: + ret = "Half Wave"; + break; + case Shapes::Fold: + ret = "Folder"; + break; + case Shapes::Fold2: + ret = "Folder II"; + break; + case Shapes::AsymSpline: + ret = "Smooth"; + break; + case Shapes::Crush: + ret = "Crush"; + break; + default: + assert(false); + ret = "error"; + } + return ret; +} + + +template +void Shaper::init() +{ + setOversample(); + tanhLookup = ObjectCache::getTanh5(); +} + +template +void Shaper::setOversample() +{ + // float fc = .25 / float(oversample); + up.setup(curOversample); + dec.setup(curOversample); +} + +template +void Shaper::processCV() +{ + int oversampleCode = (int) std::round(TBase::params[PARAM_OVERSAMPLE].value); + switch (oversampleCode) { + case 0: + curOversample = 16; + setOversample(); + break; + case 1: + curOversample = 4; + setOversample(); + break; + case 2: + curOversample = 1; + break; + default: + assert(false); + } + + // 0..1 + _gainInput = scaleGain( + TBase::inputs[INPUT_GAIN].value, + TBase::params[PARAM_GAIN].value, + TBase::params[PARAM_GAIN_TRIM].value); + + _gain = 5 * LookupTable::lookup(*audioTaper, _gainInput, false); + + + // -5 .. 5 + const float offsetInput = scaleOffset( + TBase::inputs[INPUT_OFFSET].value, + TBase::params[PARAM_OFFSET].value, + TBase::params[PARAM_OFFSET_TRIM].value); + + _offset = offsetInput; + + const int iShape = (int) std::round(TBase::params[PARAM_SHAPE].value); + shape = Shapes(iShape); + + const float sym = .1f * (5 - _offset); + asymCurveindex = (int) round(sym * 15.1); // This math belongs in the shaper +} + +template +void Shaper::step() +{ + if (--cycleCount < 0) { + cycleCount = 7; + processCV(); + } + + float buffer[maxOversample]; + float input = TBase::inputs[INPUT_AUDIO].value; + // const float rawInput = input; + + // TODO: maybe add offset after gain? + if (shape != Shapes::AsymSpline) { + input += _offset; + } + if (shape != Shapes::Crush) { + input *= _gain; + } + + if (curOversample != 1) { + up.process(buffer, input); + } else { + buffer[0] = input; + } + + processBuffer(buffer); + float output; + if (curOversample != 1) { + output = dec.process(buffer); + } else { + output = buffer[0]; + } + TBase::outputs[OUTPUT_AUDIO].value = output; + // printf("in step input = %f, output = %f\n", input, output); +} + +#if 1 +template +void Shaper::processBuffer(float* buffer) const +{ + switch (shape) { + case Shapes::FullWave: + for (int i = 0; i < curOversample; ++i) { + float x = buffer[i]; + x = std::abs(x); + x = std::min(x, 10.f); + buffer[i] = x; + } + break; + case Shapes::AsymSpline: + for (int i = 0; i < curOversample; ++i) { + float x = buffer[i]; + x *= .15f; + x = asymShaper.lookup(x, asymCurveindex); + x *= 6.1f; + buffer[i] = x; + } + break; + case Shapes::Clip: + for (int i = 0; i < curOversample; ++i) { + float x = buffer[i]; + x *= 3; + x = std::min(3.f, x); + x = std::max(-3.f, x); + x *= 1.2f; + buffer[i] = x; + } + break; + case Shapes::EmitterCoupled: + for (int i = 0; i < curOversample; ++i) { + float x = buffer[i]; + x *= .25; + x = LookupTable::lookup(*tanhLookup.get(), x, true); + x *= 5.4f; + buffer[i] = x; + } + break; + + case Shapes::HalfWave: + for (int i = 0; i < curOversample; ++i) { + float x = buffer[i]; + x = std::max(0.f, x); + x *= 1.4f; + x = std::min(x, 10.f); + buffer[i] = x; + } + break; + case Shapes::Fold: + for (int i = 0; i < curOversample; ++i) { + float x = buffer[i]; + x = AudioMath::fold(x); + x *= 5.6f; + buffer[i] = x; + } + break; + case Shapes::Fold2: + for (int i = 0; i < curOversample; ++i) { + float x = buffer[i]; + x = .3f * AudioMath::fold(x); + if (x > 0) { + x = LookupTable::lookup(*sinLookup, 1.3f * x, false); + } else { + x = -LookupTable::lookup(*sinLookup, -x, false); + } + if (x > 0) x = std::sqrt(x); + x *= 4.4f; + buffer[i] = x; + } + break; + + case Shapes::Crush: + { + float invGain = 1 + (1 - _gainInput) * 100; //0..10 + invGain *= .01f; + invGain = std::max(invGain, .09f); + assert(invGain >= .09); + for (int i = 0; i < curOversample; ++i) { + float x = buffer[i]; // for crush, no gain has been applied + +#if 0 + if (invGain < 1) { + printf("invg gain = %f\n", invGain); + fflush(stdout); + invGain = 1; + + } +#endif + // printf("crush, x=%.2f, gi=%.2f invGain = %.2f", x, _gainInput, invGain); + + x *= invGain; + x = std::round(x + .5f) - .5f; + + // printf("invGain = %f, x = %f\n", invGain, x); + // printf(" mult=%.2f", x); + x /= invGain; + // printf("after div back %f\n", x); fflush(stdout); + // printf(" dv=%.2f\n", x); fflush(stdout); + buffer[i] = x; + } + } + break; + + + + default: + assert(false); + } + +} +#else +template +void Shaper::step() +{ + float buffer[oversample]; + float input = TBase::inputs[INPUT_AUDIO].value; + + up.process(buffer, input); + + const float output = dec.process(buffer); + TBase::outputs[OUTPUT_AUDIO].value = output; +} +#endif diff --git a/plugins/community/repos/squinkylabs-plug1/composites/Super.h b/plugins/community/repos/squinkylabs-plug1/composites/Super.h new file mode 100644 index 00000000..34cf708a --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/composites/Super.h @@ -0,0 +1,180 @@ + +#pragma once + +#include "ButterworthLookup.h" +#include "BiquadState.h" +#include "BiquadFilter.h" + +#include "ObjectCache.h" + + +template +class Super : public TBase +{ +public: + + Super(struct Module * module) : TBase(module) + { + } + Super() : TBase() + { + } + + /** + * re-calc everything that changes with sample + * rate. Also everything that depends on baseFrequency. + * + * Only needs to be called once. + */ + void init(); + + enum ParamIds + { + OCTAVE_PARAM, + SEMI_PARAM, + FINE_PARAM, + DETUNE_PARAM, + MIX_PARAM, + NUM_PARAMS + }; + + enum InputIds + { + CV_INPUT, + GATE_INPUT, + DEBUG_INPUT, + NUM_INPUTS + }; + + enum OutputIds + { + MAIN_OUTPUT, + DEBUG_OUTPUT, + NUM_OUTPUTS + }; + + enum LightIds + { + NUM_LIGHTS + }; + + /** + * Main processing entry point. Called every sample + */ + void step() override; + +private: + static const int numSaws = 7; + + float phase[numSaws] = {0}; + float phaseInc[numSaws] = {0}; + float globalPhaseInc = 0; + + std::function expLookup = + ObjectCache::getExp2Ex(); + std::shared_ptr> audioTaper = + ObjectCache::getAudioTaper(); + + void updatePhaseInc(); + void updateAudio(); + +// TODO: make static + float const detuneFactors[numSaws] = { + .89f, + .94f, + .98f, + 1.f, + 1.02f, + 1.06f, + 1.107f + }; + + // For debugging filters + BiquadState filterState; + BiquadParams filterParams; + void updateHPFilters(); + ButterworthLookup4PHP filterLookup; + +}; + + +template +inline void Super::init() +{ +} + +template +inline void Super::updatePhaseInc() +{ + + const float cv = TBase::inputs[CV_INPUT].value; + + const float finePitch = TBase::params[FINE_PARAM].value / 12.0f; + const float semiPitch = TBase::params[SEMI_PARAM].value / 12.0f; + + + float pitch = 1.0f + roundf(TBase::params[OCTAVE_PARAM].value) + + semiPitch + + finePitch; + + pitch += cv; + + const float q = float(log2(261.626)); // move up to pitch range of even vco + pitch += q; + const float freq = expLookup(pitch); + globalPhaseInc = TBase::engineGetSampleTime() * freq; + + for (int i=0; i +inline void Super::updateAudio() +{ + float mix = 0; + for (int i=0; i 1) { + phase[i] -= 1; + } + if (phase[i] > 1) { + printf("hey, phase too big %f\n", phase[i]); fflush(stdout); + } + if (phase[i] < 0) { + + printf("hey, phase too bismallg %f\n", phase[i]); fflush(stdout); + } + mix += phase[i]; + } + + // mix = phase[3]; // just for test + + mix *= 2; + const float output = BiquadFilter::run(mix, filterState, filterParams); + TBase::outputs[MAIN_OUTPUT].value = output; +} + +template +inline void Super::updateHPFilters() +{ + filterLookup.get(filterParams, globalPhaseInc); +#if 0 + const float input = TBase::inputs[DEBUG_INPUT].value; + filterLookup.get(filterParams, globalPhaseInc); + const float output = BiquadFilter::run(input, filterState, filterParams); + TBase::outputs[DEBUG_OUTPUT].value = output * 10; +#endif +} + +template +inline void Super::step() +{ + updatePhaseInc(); + updateHPFilters(); + updateAudio(); +} + diff --git a/plugins/community/repos/squinkylabs-plug1/composites/TestComposite.h b/plugins/community/repos/squinkylabs-plug1/composites/TestComposite.h index 3f68162a..3648c04a 100644 --- a/plugins/community/repos/squinkylabs-plug1/composites/TestComposite.h +++ b/plugins/community/repos/squinkylabs-plug1/composites/TestComposite.h @@ -13,7 +13,7 @@ public: TestComposite() : inputs(20), outputs(20), - params(20), + params(40), lights(20) { @@ -32,7 +32,9 @@ public: { value = (brightness > 0.f) ? brightness * brightness : 0.f; } - void setBrightnessSmooth(float brightness); + void setBrightnessSmooth(float brightness) + { + } }; struct Input @@ -54,7 +56,7 @@ public: /** Voltage of the port. Write-only by Module */ float value = 0.0; /** Whether a wire is plugged in */ - bool active = false; + bool active = true; Light plugLights[2]; }; @@ -62,4 +64,18 @@ public: std::vector outputs; std::vector params; std::vector lights; + + float engineGetSampleTime() + { + return 1.0f / 44100.0f; + } + + float engineGetSampleRate() + { + return 44100.f; + } + + virtual void step() + { + } }; diff --git a/plugins/community/repos/squinkylabs-plug1/composites/Tremolo.h b/plugins/community/repos/squinkylabs-plug1/composites/Tremolo.h index 57fd03af..ba710374 100644 --- a/plugins/community/repos/squinkylabs-plug1/composites/Tremolo.h +++ b/plugins/community/repos/squinkylabs-plug1/composites/Tremolo.h @@ -14,10 +14,10 @@ template class Tremolo : public TBase { public: - Tremolo(struct Module * module) : TBase(module) + Tremolo(struct Module * module) : TBase(module), gateTrigger(true) { } - Tremolo() : TBase() + Tremolo() : TBase(), gateTrigger(true) { } void setSampleRate(float rate) @@ -71,7 +71,7 @@ public: /** * Main processing entry point. Called every sample */ - void step(); + void step() override; private: @@ -80,7 +80,7 @@ private: float reciprocalSampleRate = 0; AsymRampShaperParams rampShaper; - std::shared_ptr> exp2 = ObjectCache::getExp2(); + std::shared_ptr> exp2 = ObjectCache::getExp2(); // make some bootstrap scalers AudioMath::ScaleFun scale_rate; @@ -126,7 +126,7 @@ inline void Tremolo::step() clock.setMultiplier(clockMul); - + const float shape = scale_shape( TBase::inputs[LFO_SHAPE_INPUT].value, @@ -160,7 +160,7 @@ inline void Tremolo::step() clock.setFreeRunFreq(scaledRate * reciprocalSampleRate); } - + // For now, call setup every sample. will eat a lot of cpu AsymRampShaper::setup(rampShaper, skew, phase); diff --git a/plugins/community/repos/squinkylabs-plug1/composites/VocalAnimator.h b/plugins/community/repos/squinkylabs-plug1/composites/VocalAnimator.h index 1a18c209..57d03a67 100644 --- a/plugins/community/repos/squinkylabs-plug1/composites/VocalAnimator.h +++ b/plugins/community/repos/squinkylabs-plug1/composites/VocalAnimator.h @@ -91,7 +91,7 @@ public: }; void init(); - void step(); + void step() override; T modulatorOutput[numModOutputs]; // The frequency inputs to the filters, exposed for testing. @@ -144,10 +144,10 @@ inline void VocalAnimator::init() normalizedFilterFreq[i] = nominalFilterCenterHz[i] * reciprocalSampleRate; } - scale0_1 = AudioMath::makeBipolarAudioScaler(0, 1); // full CV range -> 0..1 - scalem2_2 = AudioMath::makeBipolarAudioScaler(-2, 2); // full CV range -> -2..2 - scaleQ = AudioMath::makeBipolarAudioScaler(.71f, 21); - scalen5_5 = AudioMath::makeBipolarAudioScaler(-5, 5); + scale0_1 = AudioMath::makeScalerWithBipolarAudioTrim(0, 1); // full CV range -> 0..1 + scalem2_2 = AudioMath::makeScalerWithBipolarAudioTrim(-2, 2); // full CV range -> -2..2 + scaleQ = AudioMath::makeScalerWithBipolarAudioTrim(.71f, 21); + scalen5_5 = AudioMath::makeScalerWithBipolarAudioTrim(-5, 5); // make table of 2 ** x expLookup = ObjectCache::getExp2(); diff --git a/plugins/community/repos/squinkylabs-plug1/composites/VocalFilter.h b/plugins/community/repos/squinkylabs-plug1/composites/VocalFilter.h index 4e0b1d11..33e99c1a 100644 --- a/plugins/community/repos/squinkylabs-plug1/composites/VocalFilter.h +++ b/plugins/community/repos/squinkylabs-plug1/composites/VocalFilter.h @@ -72,7 +72,7 @@ public: }; void init(); - void step(); + void step() override; float reciprocalSampleRate; @@ -156,7 +156,7 @@ inline void VocalFilter::step() for (int i = LED_A; i <= LED_U; ++i) { if (i == iVowel) { TBase::lights[i].value = ((i + 1) - fVowel) * 1; - TBase::lights[i+1].value = (fVowel - i) * 1; + TBase::lights[i + 1].value = (fVowel - i) * 1; } else if (i != (iVowel + 1)) { TBase::lights[i].value = 0; } @@ -199,7 +199,7 @@ inline void VocalFilter::step() T modifiedGainDB = (1 - gainDB) * brightness + gainDB; // TODO: why is normalizedBW in this equation? - const T gain =LookupTable::lookup(*db2GainLookup, modifiedGainDB) * normalizedBw; + const T gain = LookupTable::lookup(*db2GainLookup, modifiedGainDB) * normalizedBw; T fcFinalLog = fcLog + fPara; T fcFinal = LookupTable::lookup(*expLookup, fcFinalLog); diff --git a/plugins/community/repos/squinkylabs-plug1/composites/WidgetComposite.h b/plugins/community/repos/squinkylabs-plug1/composites/WidgetComposite.h index b1f3bfc7..d0e71656 100644 --- a/plugins/community/repos/squinkylabs-plug1/composites/WidgetComposite.h +++ b/plugins/community/repos/squinkylabs-plug1/composites/WidgetComposite.h @@ -11,12 +11,26 @@ public: inputs(parent->inputs), outputs(parent->outputs), params(parent->params), - lights(parent->lights) + lights(parent->lights), + module(parent) { } + virtual void step() + { + }; + float engineGetSampleRate() + { + return ::engineGetSampleRate(); + } + float engineGetSampleTime() + { + return ::engineGetSampleTime(); + } protected: std::vector& inputs; std::vector& outputs; std::vector& params; std::vector& lights; +private: + Module * const module; }; diff --git a/plugins/community/repos/squinkylabs-plug1/composites/daveguide.h b/plugins/community/repos/squinkylabs-plug1/composites/daveguide.h new file mode 100644 index 00000000..9972b251 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/composites/daveguide.h @@ -0,0 +1,111 @@ +#pragma once + +#include "AudioMath.h" +#include "FractionalDelay.h" +#include "ObjectCache.h" + +template +class Daveguide : public TBase +{ +public: + Daveguide(struct Module * module) : TBase(module), delay(44100) + { + // init(); + } + Daveguide() : TBase(), delay(44100) + { + // init(); + } + + enum ParamIds + { + OCTAVE_PARAM, + TUNE_PARAM, + DECAY_PARAM, + FC_PARAM, + NUM_PARAMS + }; + + enum InputIds + { + AUDIO_INPUT, + CV_INPUT, + NUM_INPUTS + }; + + enum OutputIds + { + AUDIO_OUTPUT, + NUM_OUTPUTS + }; + + enum LightIds + { + NUM_LIGHTS + }; + + /** + * Main processing entry point. Called every sample + */ + void step() override; + + float _freq = 0; +private: + RecirculatingFractionalDelay delay; + + //static std::function makeFunc_Exp(double xMin, double xMax, double yMin, double yMax); + + // std::function delayScale = AudioMath::makeFunc_Exp(-5, 5, 1, 500); + + // AudioMath::ScaleFun feedbackScale = AudioMath::makeLinearScaler(0.f, 1.f); + + std::function expLookup = ObjectCache::getExp2Ex(); + + +}; + + +template +void Daveguide::step() +{ +#if 0 + // make delay knob to from 1 ms. to 1000 + double delayMS = delayScale(TBase::params[PARAM_DELAY].value); + double feedback = feedbackScale(0, (TBase::params[PARAM_FEEDBACK].value), 1); + + double delaySeconds = delayMS * .001; + double delaySamples = delaySeconds * TBase::engineGetSampleRate(); + + delay.setDelay((float) delaySamples); + delay.setFeedback((float) feedback); + + const float input = TBase::inputs[INPUT_AUDIO].value; + const float output = delay.run(input); + TBase::outputs[OUTPUT_AUDIO].value = output; +#endif + float pitch = 1.0f + roundf(TBase::params[OCTAVE_PARAM].value) + TBase::params[TUNE_PARAM].value / 12.0f; + pitch += TBase::inputs[CV_INPUT].value; + //pitch += .25f * TBase::inputs[PITCH_MOD_INPUT].value * + // taper(TBase::params[PARAM_PITCH_MOD_TRIM].value); + + const float q = float(log2(261.626)); // move up to pitch range of even vco + pitch += q; + _freq = expLookup(pitch); + const float delaySeconds = 1.0f / _freq; + float delaySamples = delaySeconds * TBase::engineGetSampleRate(); + + delay.setDelay(delaySamples); + delay.setFeedback(.999f); + // printf("set delay to %f samples (%f sec)\n", delaySamples, delaySeconds); + // fflush(stdout); + + const float input = TBase::inputs[AUDIO_INPUT].value; + const float output = delay.run(input); + TBase::outputs[AUDIO_OUTPUT].value = output; + + + + + + // clock.setMultiplier(1); // no mult +} \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/docs/README.md b/plugins/community/repos/squinkylabs-plug1/docs/README.md index 71d640ab..5dc58619 100644 --- a/plugins/community/repos/squinkylabs-plug1/docs/README.md +++ b/plugins/community/repos/squinkylabs-plug1/docs/README.md @@ -2,6 +2,8 @@ All of our plugins are free and open source. The [instruction manual](booty-shifter.md) describes all of the released modules. +The [release notes](release-notes.md) describe recent changes. + All of our released modules may be found in the [VCV Rack plugin manager] (https://vcvrack.com/plugins.html). This is by far the easiest way for most users to install our modules and keep them up to date. It is also quite easy to clone this repo and build them yourself. In order to do this, however, you must first download and build [VCV Rack itself](https://github.com/VCVRack/Rack). @@ -22,10 +24,6 @@ As with all third-party modules for VCV, you must: * `CD SquinkyVCV` * `make` -## Experimental modules - -At any given time, there may partially finished "experimental" modules in this repo. You can find up to date information on them [here](experimental.md). - ## Unit testing framework We have reasonably thorough tests for our code. Some of this might be of interest - it's [here](unit-test.md). \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/docs/alias-setup.png b/plugins/community/repos/squinkylabs-plug1/docs/alias-setup.png new file mode 100644 index 00000000..00aa607a Binary files /dev/null and b/plugins/community/repos/squinkylabs-plug1/docs/alias-setup.png differ diff --git a/plugins/community/repos/squinkylabs-plug1/docs/aliasing.md b/plugins/community/repos/squinkylabs-plug1/docs/aliasing.md new file mode 100644 index 00000000..d7a1fce0 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/docs/aliasing.md @@ -0,0 +1,39 @@ +# Some information about aliasing + +We’ve been thinking about aliasing a lot lately. It has been a major issue in many of our recent projects. With Fundamental VCO-1 we wanted to be sure that after we re-designed the anti-alias filters to use less CPU that the aliasing was still good. So we spent a lot of time writing unit tests to compare the two. + +With EV3, we wanted to add sync to the VCO without adding aliasing. It took us way longer than it should have to figure out exactly how to do that. Then with Shaper it was clear that it would generate a lot of aliasing if we didn’t oversample it. So we borrowed the oversampler from Fundamental VCO-1. + +Aliasing in digital audio happens when an algorithm attempts to create frequencies that are impossible to represent. You probably know that the highest frequency that can be contained in a digital signal is one-half the sampling rate. So at the standard 44,100 sample rate, the highest frequency that can be encoded is 22,050 Hz. + +If a DSP algorithm, like a VCV module, attempts to generate frequencies higher than than this, they will appear at a frequency below 22,050. In fact they will “fold back” - increasing frequencies alias and manifest themselves as decreasing frequencies below fs/2. + +Most people find that aliasing sounds terrible. Unlike harmonic distortion, which occurs at harmonic multiples of the pitch, the alias frequencies are not related to the non-aliased frequencies in a harmonious way. As an extreme example: if you play a high-pitched note and bend it up, the alias frequencies will bend in the opposite direction, so you hear you main signal bending up in pitch, with some “ghost tones” bending down in pitch. + +For more information on aliasing, especially how it sounds, there is a wealth of information on the internet. Spoiler alert: most people think if sounds bad and most commercial digital audio equipment goes to some lengths to suppress it. + +Now if you are new to this, and paying attention, you might be perplexed by the statement above that aliasing happens when a DSP algorithm attempts to generate sounds above 22,050. Why would any algorithm try to do this? Well, a simple example is a square wave. An analog square wave has an infinite number of harmonics going up to infinite frequencies (look it up if you don’t believe us). So a naive attempt to make a square wave VCO is going to generate a ton of aliasing. Similarly, a distortion or waveshaper module that does hard clipping will tend to create square waves and a lot of aliasing. + +![alias setup image](./alias-setup.png) + +Luckily for VCV Rack users it is easy to see if a particular module is aliasing. Pick a spectrum analyzer module. Our favorite is the Bogaudio one. To examine a VCO, just patch the VCO output to the analyzer’s input. To examine a waveshaper, patch a sine wave from a VCO into the input of the shaper, then patch the output of the shaper to the spectrum analyzer. + +Aliasing is far more severe at high frequencies, so set the VCO for somewhere between 1 kHz and 2kHz. At lower frequencies it might not be visible (or audible). If you set it at an exact division of the sampling rate the aliasing will blend in with the harmonics and you won’t see it. 1k and 2k are very close to exact divisions of 44,100, so pick something in between. Then adjust the VCO frequency up and down until you see the worst aliasing. You may see new frequency lines appear in between real harmonics. Or you may see that as you raise the pitch some of the lines go down - those are the alias frequencies folding over. + +This excellent video by the author of the Vult modules shows how to do it: [Can you hear the alias?](https://www.youtube.com/watch?v=LSIO5R0fuoU&feature=youtu.be&fbclid=IwAR1_wsaqiuLiYNO7Tn0c5smP3mLnwxgYJmD8MXcn7eNuhlb6To_XwzR3Kzc) + +As an example we will show the effect of changing the oversample rate in our “Shaper” module using the “Clip” setting. The gain is up pretty high. + +![clipping image](./clip-collage.png) + +This image shows the output of Shaper on the spectrum analyzer at 1X (left), 4X, and 16X (on the right). You can easily see the alising in the 1X image. At this setting there is no visible difference between 4X and 16X. No doubt there would be if the analyzer were more sensitive, but it’s worth noting here that at 1X the alias components are getting pretty high compared to the harmonics in the 10k region. But down in the 1-2k area the alias is below the fundamental by a bit more than 50 db. Also notice that there are alias components below the fundamental frequency. + +This next image is the same test, but with the “Folder” setting. Again, the gain is pretty high, and there is some offset to bring in even harmonics. + +![folding image](./fold-collage.png) + +Not surprisingly, this highly folded sine wave shows a **lot** of aliasing at 1X. Notice the line at 2 kHz - that’s an alias component that is louder than the fundamental. And now you can see many more alias components below the fundamental. + +Moving right to the 4X image, clearly most of the aliasing is gone. But there are still alias components adjacent to the harmonics, and they are only perhaps 25 db down from the good signal. Then in the 16X image things are as they should be. The lines are all at even multiples of 1.5 k, so they are harmonics, not aliasing. Note that the level of the harmonics is not falling off at all with increasing frequency. That is why this shape brings out so much aliasing. + +We hope you have learned how easy it is to evaluate the aliasing of a VCV module. Next time you are experimenting with a new module, why not take a look at the aliasing? Make your own opinion about aliasing. Your are fortunate to have the tools already. diff --git a/plugins/community/repos/squinkylabs-plug1/docs/booster.md b/plugins/community/repos/squinkylabs-plug1/docs/booster.md new file mode 100644 index 00000000..543b157e --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/docs/booster.md @@ -0,0 +1,21 @@ +# Thread Booster + +![thread booster image](./thread-booster.png) + +Thread booster raises the priority of VCV Rack's audio rendering thread. In many cases this decreases the annoying pops, ticks, and dropouts that many users are experiencing. + +Many users have reported that Thread Booster helps significantly. Others have reported that it does not help at all. No one has reported a detrimental effect. + +For a deeper dive into the Thread Booster, you should read [this document](./thread-booster.md). + +Thread Booster has a UI that lets you boost the priority of the audio thread. There are three arbitrary settings: normal, boosted, and real time. When the switch is in the bottom position, the plugin does nothing; the audio thread keeps its default priority. In the boost (middle) position, it sets the thread priority to the highest priority non-real-time setting. In the real-time position it attempts to set it to the highest possible priority, or near it. + +If setting the priority fails, the red error light lights up, and the priority stays where it was last. + +To use Thread Booster, just insert an instance into VCV Rack, then adjust the boost switch. In general we recommend the "real time" setting, if it is available on your computer. + +Once Thread booster is in your session, it will boost all the audio processing - it doesn't matter if other modules are added before or after - they all get boosted. + +Linux users - you must read [the detailed document](./thread-booster.md) to use this module. + +Note to users who downloaded the original version of Thread Booster: we've improved it a bit since then, especially on Linux and Windows. diff --git a/plugins/community/repos/squinkylabs-plug1/docs/booty-shifter-old.md b/plugins/community/repos/squinkylabs-plug1/docs/booty-shifter-old.md new file mode 100644 index 00000000..f21b268f --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/docs/booty-shifter-old.md @@ -0,0 +1,249 @@ +# Table of contents + +[Chebyshev Waveshaper VCO](../docs/chebyshev.md) Click on link to go to Chbeyshev manual. + +[Functional VCO-1](#fun) Is an improved version of the Fundamental-VCO1. + +[LFN](#lfn) Is a random voltage generator made by running low frequency noise through a graphic equalizer. + +[Chopper](#chopper) Is a tremolo powered by a clock-synchable LFO. The LFO is highly programmable to give a range of waveforms. + +[Thread Booster](#booster) reduces pops and clicks in VCV Rack by reprogramming VCV's audio engine. + +[Colors](#colors) is a colored noise generator. It can generate all the common **"colors"** of noise, including white, pink, red, blue, and violet. + +[Growler](#growler) is a "vocal animator." It imparts random vocal timbres on anything played through it. The pseudo-random LFOs all have discrete outputs. + +[Booty Shifter](#shifter) is an emulation of the legendary Moog/Bode frequency shifter. + +[Formants](#formants) is a programmable bank of filters that can synthesize various vowel sounds and morph between them. + +[Attenuverters](#atten) + +[CV ranges](#cv) + +The [release notes](release-notes.md) describe recent changes to our modules. + +# Functional VCO-1 + +![Functional image](../docs/functional.png) + +Functional VCO-1 works just like its namesake. The control layout is familiar, the sound is the same, but it uses about 1/4 as much CPU as the original. + +We believe VCV's Fundamental VCO is an unsung hero. It's one of the few VCOs that never has audible aliasing artifacts. You can sync it, and modulate all its inputs, but the sound never falls apart. + +We "forked" the code to Fundamental VCO-1 and modified it a little bit to make it much more CPU efficient. Now you may use a lot more of them without pops, clicks, and dropouts. + +If you would like the details of how we did this, you can [find them here](../docs/vco-optimization.md). + + +# LFN Low Frequency Noise Generator + +![LFN image](../docs/lfn.png) + +LFN stands for Low Frequency Noise. Technically it is a white noise generator run through a graphic equalizer at extremely low frequencies. People may find it easier to think of it as a random voltage source with unique control over the output. + +The top knob, which is unlabeled, sets the "base frequency" of LFN. + +The five other knobs, and the CV inputs beside them, control the gain of the graphic equalizers sections. Beside each EQ gain knob is a label indicating what frequency range that knob controls. + +For example, it the base frequency is 1.0, the EQ sections will be at 1Hz, 2Hz, 4Hz, 8Hz, and 16Hz. If the base frequency is 0.2, The EQ sections will be at 0.2Hz, 0.4Hz, 0.8Hz, 1.6Hz, and 3.2Hz. + +But instead of thinking about frequencies like 1Hz, which are a little difficult to imagine, think of the knobs as mixing very slow random voltages, with less slow ones. For example if LFN is driving a pitch quantizer into a VCO, turn all the knobs down to zero except the first, lowest, one. This will make a series of pitches slowly rising and falling. Then bring in a little of the faster channels. The pitch will still be slowly rising and falling, but will also quickly move up and down by smaller steps. + +A good way to learn what makes LFN tick is to set it slow and watch it on the scope. At the same time run it straight into a VCO. Experiment with different mixes of the slow knobs and the fast ones. + +As you would expect from Squinky Labs, the CPU usage of LFN is very low. In fact it is one of our leanest modules yet. So feel free to use as many instances as you like. + +# Chopper tremolo / programmable LFO +![chopper image](./chopper.png) + +In its simplest use, Chopper produces a very wide range of **tremolo** effects. The built-in LFO can produce a wide range of waveforms that cover many of the waveforms produced by the tremolo circuits built into **vintage guitar amplifiers**. + +The LFO is sent to an output so that it may modulate other modules. + +There is also a **clock synchronizer** and multiplier. + +To use Chopper as a tremolo, send a signal to the *in* jack, and listen to the *out* jack. Leave the *clock* control at the default *int* setting. Most of the knob settings will now affect the tremolo effect. + +## Chopper LFO + +![chopper LFO image](../docs/lfo-waveforms.png) + +To understand all the LFO settings, it helps to watch the outputs on a scope. + +The LFO starts as **skewed** sawtooth. In the middle position it is a symmetric triangle wave, at one end a positive sawtooth and at the other a negative sawtooth. The signal is sent to the **saw** output. + +The skewed saw then goes to a **waveshaper**. As the shape control is increased the LFO is gradually rounded and then flattened. The shaped LFO is send to the *lfo* output, and used internally to modulate the audio input. + +LFO Controls: + +* **Shape** Flattens the LFO waveform. +* **Skew** Dials in the amount of asymmetry in the LFO. +* **Depth** Shifts and scales the LFO. + +When used as a tremolo effect, you will hear **more tremolo** when these controls are turned up. + +## Chopper clock + +The LFO in Chopper may be synchronized with the ckin signal. There is a built-in **clock multiplier**. To use the synchronization, patch a clock to the ckin, and select x1 from the **clock** knob. To run at a multiple of the input clock, select x2, x3, or x4. + +When Chopper is being synched, the **Phase** control sets the phase difference between the external clock and the synchronized LFO. This may be used to "dial in" the tremolo so that it sounds exactly on the beat (or off the beat). + +There is also an internal LFO that is controlled by the **Rate** control. Set the clock control to *int* to use the internal clock. + +# Thread Booster + +![thread booster image](./thread-booster.png) + +Thread booster raises the priority of VCV Rack's audio rendering thread. In many cases this decreases the annoying pops, ticks, and dropouts that many users are experiencing. + +Many users have reported that Thread Booster helps significantly. Others have reported that it does not help at all. No one has reported a detrimental effect. + +For a deeper dive into the Thread Booster, you should read [this document](./thread-booster.md). + +Thread Booster has a UI that lets you boost the priority of the audio thread. There are three arbitrary settings: normal, boosted, and real time. When the switch is in the bottom position, the plugin does nothing; the audio thread keeps its default priority. In the boost (middle) position, it sets the thread priority to the highest priority non-real-time setting. In the real-time position it attempts to set it to the highest possible priority, or near it. + +If setting the priority fails, the red error light lights up, and the priority stays where it was last. + +To use Thread Booster, just insert an instance into VCV Rack, then adjust the boost switch. In general we recommend the "real time" setting, if it is available on your computer. + +Once Thread booster is in your session, it will boost all the audio processing - it doesn't matter if other modules are added before or after - they all get boosted. + +Linux users - you must read [the detailed document](./thread-booster.md) to use this module. + +Note to users who downloaded the original version of Thread Booster: we've improved it a bit since then, especially on Linux and Windows. + +# Colors variable slope noise generator + +![noise image](../docs/colors.png) + +Colors is a colored noise generator. It can generate all the common **"colors"** of noise, including white, pink, red, blue, and violet. It can also produce all the colors in between, as it has a **continuously variable slope**. + +Colors has a single control, "slope." This is the slope of the noise spectrum, from -8 dB/octave to +8 dB/octave. + +The slope of the noise is quite accurate in the mid-band, but at the extremes we flatten the slope to keep from boosting super-low frequencies too much, and to avoid putting out enormous amounts of highs. So the slope is flat below 40hz, and above 6kHz. + +## Things to be aware of + +When the **slope** changes, Color needs to do a lot of calculations. While this is normally not a problem, it’s possible that quickly changing the slope of many instances of Colors could cause pops and dropouts. + +The slope control does not respond instantly. If you turn the knob, you will hear the change, but if you were to modulate the CV very quickly you might notice the slowness. + +# Growler + +![vocal formant filter image](./growler.jpg) + +**Growler** is a re-creation of the Vocal Animator circuit invented by Bernie Hutchins, and published in Electronotes magazine in the late 70's. It continuously morphs between different vaguely voice like tones. + +**To get a good sound:** run any harmonically rich signal into the input, and something good will come out. Low frequency pulse waves and distorted sounds make great input. + +The controls do pretty much what you would expect: + +* **LFO** controls the speed of the modulation LFOs. +* **Fc** controls the average frequency of the multiple filters. +* **Q** controls the sharpness of the filters. +* **Depth** controls how much of the modulation LFOs are applied to the filters. + +## How Growler works +![growler scope](./growler.png) + +There are four **bandpass filters**, roughly tuned to some typical vocal formant frequencies: 522, 1340, 2570, and 3700 Hz. The filters are run in parallel, with their outputs summed together. + +The first three filter frequencies are modulated by an LFO comprised of **4 triangle wave LFOs** running at different frequencies. They are summed together in various combinations to drive each of the filters. + +Each **CV input stage** is the same: a knob that supplies a fixed offset and a CV input that is processed by an attenuverter. The processed CV is added to the knob voltage. See below for more on [Attenuverters](#atten) and [CV ranges](#cv). + +The **LFO** Rate control shifts the speed of all 4 LFOs while maintaining the ratio of their frequencies. + +The **Fc** control moves the frequencies of the first three filters, but not by equal amounts. The lowest filter moves at 1V/Oct, but the middle two move less. The top filter is fixed at 3700 Hz. + +The **Q** control does just what it says - controls the Q (resonance) of the filters. + +The **Modulation Depth** controls how much of the summed LFOs get to each filter. Again, the lower filters move farther, and the top filter is fixed. + +The smaller knobs next to the main knobs are **attenuverters**, which scale control voltages. For more on attenuverters, [see below](#atten) + +There are three LFO outputs next to the blinking LFOs. These may be used to modulate other modules, or as semi-random voltage sources. + +**Bass boost** switch. When it’s in the up position (on) there should be more bass. This is done by switching some or all of the filters from bandpass to lowpass. + +LFO **Matrix** switch. This is the unlabeled switch in the LFO section. When it’s down (default position) the LFOs are closely correlated. In the middle we try to make them a little bit more independent. When it’s in the up position the LFOs will often go in different directions. + +# Booty Shifter frequency shifter + +**Booty Shifter** is a frequency shifter inspired by the Moog/Bode frequency shifter module. + +![booty shifter image](./booty-shifter.png) + +The name "Booty Shifter" is a nod to the classic analog module, as well as to a black cat named Booty. + +Booty Shifter will take an audio input and shift the frequencies up or down. This is not like a pitch shift where harmonics will remain in tune; it is an absolute frequency shift in Hz, so in general **harmonics will go way out of tune.** It is similar to a ring-modulator, but less extreme and more versatile. + +## Getting good sounds from Booty Shifter + +Feed in music and shift the frequency a good amount. + +Feed in **speech or radio** and shift it. + +Feed the CV from a **sequencer** to sequence the mayhem. + +Shift **drums** up or down a little bit to re-tune them without the usual pitch-shifting artifacts. + +Small shifts in conjunction with delays can make a chorus-like effect to thicken music. + +## Inputs and outputs + +* **IN** is the audio input. +* **CV** is the pitch shift control voltage. -5V will give minimum shift, +5 will give maximum. +* **DN** is the down-shifted output. +* **UP** is the up-shifted output. + +## Controls + +**RANGE** sets the total shift range in Hz. For example, the 50 Hz setting means that the minimum shift is 50 Hz down, and the maximum is 50 Hz up. + +Range value **Exp is different**. Here minimum shift is 2 Hz, maximum is 2 kHz, with an exponential response. As of version 0.6.2 the response is an accurate 1 Volt per Octave. + +Shift **AMT** is added to the control voltage, with a range of -5..5. + +## Oddities and limitations + +If you shift the frequency up too far, it will alias. There is no anti-aliasing, so if the highest input frequency + shift amount > sample_rate / 2, you will get aliasing. Of course the Bode analog original did not alias. + +If you shift the input down a lot, frequencies will go **below zero and wrap around**. Taken far enough this will completely **reverse the spectrum** of the input. This was a prized feature of the Bode original. + +As you shift the input down, you may start to generate a lot of subsonic energy. A **High Pass filter** may clean this up. + +The down shift **frequency fold-over**, while true to the original, does cause problems when trying to pitch drum tracks down a lot. High pass filtering the input before it is down-shifted can control this. + +# Formants vocal filter + +![formants image](./formants.png) + +Like the **Vocal Animator**, this is a filter bank tuned to the formant frequencies of typical **singing voices**. Unlike Growler, however, the filters do not animate on their own. In addition, the filters are preset to frequencies, bandwidths, and gains that are taken from **measurements of human singers**. + +One of the easiest ways to **get a good sound** from Formants is to use it like a regular VCF. For example, control Fc with an ADSR. Then put a second modulation source into the vowel CV - something as simple as a slow LFO will add interest. + +Use it as a **filter bank**. Just set the knobs for a good sound and leave it fixed to add vocal tones to a pad. Again, modulating the vowel CV can easily give great results. + +Try to synthesize something like **singing** by sequencing the vowel CV of several formants. Leave the Fc in place, or move it slightly as the input pitches move. + +Controls: + +* **Fc** control moves all the filters up and down by the standard one "volt" per octave. +* **Vowel** control smoothly interpolates between 'a', 'e', 'i', 'o', and 'u'. +* **Model** control selects different vocal models: bass, tenor, countertenor, alto, and soprano. +* **Brightness** control gradually boosts the level of the higher formants. When it is all the way down, the filter gains are set by the singing models in the module, which typically fall off with increasing frequency. As this control is increased the gain of the high formant filters is brought up to match the F1 formant filter. + +The **LEDs across the top** indicate which formant is currently being "sung". + +## About Attenuverters + +The small knobs next to the bigger knobs are **attenuverters**. They scale and/or invert the control voltage inputs next to them. When they are turned all the way up the full CV comes through. As they are turned down less CV comes through. Straight up none passes. As they are turned down further the CV comes back, but inverted. + +Sometimes we use attenuverters with a *linear taper*, and sometimes we use an *audio taper*. If you find that on a particular module you do not like the response of the attenuverters, please log a github issue. + +## Control voltage ranges + +Our modules usually expect a control voltage range of **-5 to +5**. The associated offset knobs will also add -5 to +5. After attenuverters are applied to CV the knob value is added. After all that, the result is usually clipped to the -5 to +5 range. diff --git a/plugins/community/repos/squinkylabs-plug1/docs/booty-shifter.md b/plugins/community/repos/squinkylabs-plug1/docs/booty-shifter.md index 44c078a4..94cc1951 100644 --- a/plugins/community/repos/squinkylabs-plug1/docs/booty-shifter.md +++ b/plugins/community/repos/squinkylabs-plug1/docs/booty-shifter.md @@ -1,207 +1,40 @@ -# Table of contents +# The Squinky Labs modules for VCV Rack -[Chopper](#chopper) Is a tremolo powered by a clock-synchable LFO. The LFO is highly programmable to give a range of waveforms. +Below are short descriptions of our modules with links to more detailed manuals. -[Thread Booster](#booster) reduces pops and clicks in VCV Rack by reprogramming VCV's audio engine. +The [release notes](release-notes.md) describe recent changes to our modules. -[Colors](#colors) is a colored noise generator. It can generate all the common **"colors"** of noise, including white, pink, red, blue, and violet. +# Things that make sound -[Growler](#growler) is a "vocal animator." It imparts random vocal timbres on anything played through it. The pseudo-random LFOs all have discrete outputs. +![Intro 1 image](./intro-1-110.png) -[Booty Shifter](#shifter) is an emulation of the legendary Moog/Bode frequency shifter. +[EV3](./ev3.md) is three VCOs in a single module. Each of the three VCOs is a clone of Befaco's EvenVCO, with oscillator sync added. Like EvenVCO, it sounds good, uses little CPU, and has very little aliasing distortion. -[Formants](#formants) is a programmable bank of filters that can synthesize various vowel sounds and morph between them. +[Colors](./colors.md) is a colored noise generator. It can generate all the common **"colors"** of noise, including white, pink, red, blue, and violet. And all the colors in between. -[Attenuverters](#atten) +[Chebyshev Waveshaper VCO](../docs/chebyshev.md) can make sounds like no other VCO. It contains a VCO, ten polynomial wave-shapers, and one clipper/folder. Among other things, it is a harmonic oscillator. -[CV ranges](#cv) +[Functional VCO-1](./functional-vco-1.md) Is an improved version of the Fundamental VCO-1. Like Fundamental VCO-1, it will never alias, no matter what you throw at it. It is one of the few VCOs that can do sync, FM, and PWM without aliasing. Functional VCO-1 improves on Fundamental by lowering the CPU usage dramatically. -# Chopper tremolo / programmable LFO +# Things that process sound -In its simplest use, Chopper produces a very wide range of **tremolo** effects. The built-in LFO can produce a wide range of waveforms that cover many of the waveforms produced by the tremolo circuits built into **vintage guitar amplifiers**. +![Intro 2 image](./intro-2-110.png) -The LFO is sent to an output so that it may modulate other modules. +[Shaper](./shaper.md). Yet another wave shaper. But unlike most, this one has almost no aliasing distortion. And a few new shapes that sound nice. -There is also a **clock synchronizer** and multiplier. +[Chopper](./chopper.md) Is a tremolo powered by a clock-synchable LFO. The LFO is highly programmable to give a range of waveforms. A built-in clock multiplier enables easy rhythmic effects. -To use Chopper as a tremolo, send a signal to the *in* jack, and listen to the *out* jack. Leave the *clock* control at the default *int* setting. Most of the knob settings will now affect the tremolo effect. +[Growler](./growler.md) is a "vocal animator." It imparts random vocal timbres on anything played through it. The pseudo-random LFOs all have discrete outputs. -## Chopper LFO +[Booty Shifter](./shifter.md). An emulation of the legendary Moog/Bode frequency shifter. It is great for "warping" sounds run through it. -![chopper image](../docs/lfo-waveforms.png) +[Formants](./formants.md) is a programmable bank of filters that can synthesize various vowel sounds and morph between them easily. -To understand all the LFO settings, it helps to watch the outputs on a scope. +# Other things +![Intro 3 image](./intro-3-110.png) -The LFO starts as **skewed** sawtooth. In the middle position it is a symmetric triangle wave, at one end a positive sawtooth and at the other a negative sawtooth. The signal is sent to the **saw** output. +[Gray Code](./gray-code.md). Think of it as a semi-random clock divider. Or not. Gray codes have the cool property that only one bit changes at a time. Having only one “thing” change at a time can be interesting for music, so we are hoping you will find some good things to do with it. -The skewed saw then goes to a **waveshaper**. As the shape control is increased the LFO is gradually rounded and then flattened. The shaped LFO is send to the *lfo* output, and used internally to modulate the audio input. +[LFN](./lfn.md) is a random voltage generator made by running low frequency noise through a graphic equalizer. The equalizer gives a lot of easy control over the shape of the randomness. -LFO Controls: - -* **Shape** Flattens the LFO waveform. -* **Skew** Dials in the amount of asymmetry in the LFO. -* **Depth** Shifts and scales the LFO. - -When used as a tremolo effect, you will hear **more tremolo** when these controls are turned up. - -## Chopper clock - -The LFO in Chopper may be synchronized with the ckin signal. There is a built-in **clock multiplier**. To use the synchronization, patch a clock to the ckin, and select x1 from the **clock** knob. To run at a multiple of the input clock, select x2, x3, or x4. - -When Chopper is being synched, the **Phase** control sets the phase difference between the external clock and the synchronized LFO. This may be used to "dial in" the tremolo so that it sounds exactly on the beat (or off the beat). - -There is also an internal LFO that is controlled by the **Rate** control. Set the clock control to *int* to use the internal clock. - -# Thread Booster - -Thread booster raises the priority of VCV Rack's audio rendering thread. In many cases this decreases the annoying pops, ticks, and dropouts that many users are experiencing. - -Many users have reported that Thread Booster helps significantly. Others have reported that it does not help at all. No one has reported a detrimental effect. - -For a deeper dive into the Thread Booster, you should read [this document](./thread-booster.md). - -Thread Booster has a UI that lets you boost the priority of the audio thread. There are three arbitrary settings: normal, boosted, and real time. When the switch is in the bottom position, the plugin does nothing; the audio thread keeps its default priority. In the boost (middle) position, it sets the thread priority to the highest priority non-real-time setting. In the real-time position it attempts to set it to the highest possible priority, or near it. - -If setting the priority fails, the red error light lights up, and the priority stays where it was last. - -To use Thread Booster, just insert an instance into VCV Rack, then adjust the boost switch. In general we recommend the "real time" setting, if it is available on your computer. - -Once Thread booster is in your session, it will boost all the audio processing - it doesn't matter if other modules are added before or after - they all get boosted. - -Linux users - you must read [the detailed document](./thread-booster.md) to use this module. - -Note to users who downloaded the original version of Thread Booster: we've improved it a bit since then, especially on Linux and Windows. - -# Colors variable slope noise generator - -![noise image](../docs/colors.png) - -Colors is a colored noise generator. It can generate all the common **"colors"** of noise, including white, pink, red, blue, and violet. It can also produce all the colors in between, as it has a **continuously variable slope**. - -Colors has a single control, "slope." This is the slope of the noise spectrum, from -8 dB/octave to +8 dB/octave. - -The slope of the noise is quite accurate in the mid-band, but at the extremes we flatten the slope to keep from boosting super-low frequencies too much, and to avoid putting out enormous amounts of highs. So the slope is flat below 40hz, and above 6kHz. - -## Things to be aware of - -When the **slope** changes, Color needs to do a lot of calculations. While this is normally not a problem, it’s possible that quickly changing the slope of many instances of Colors could cause pops and dropouts. - -The slope control does not respond instantly. If you turn the knob, you will hear the change, but if you were to modulate the CV very quickly you might notice the slowness. - -# Growler - -![vocal formant filter image](./growler.jpg) - -**Growler** is a re-creation of the Vocal Animator circuit invented by Bernie Hutchins, and published in Electronotes magazine in the late 70's. It continuously morphs between different vaguely voice like tones. - -**To get a good sound:** run any harmonically rich signal into the input, and something good will come out. Low frequency pulse waves and distorted sounds make great input. - -The controls do pretty much what you would expect: - -* **LFO** controls the speed of the modulation LFOs. -* **Fc** controls the average frequency of the multiple filters. -* **Q** controls the sharpness of the filters. -* **Depth** controls how much of the modulation LFOs are applied to the filters. - -## How Growler works -![growler scope](./growler.png) - -There are four **bandpass filters**, roughly tuned to some typical vocal formant frequencies: 522, 1340, 2570, and 3700 Hz. The filters are run in parallel, with their outputs summed together. - -The first three filter frequencies are modulated by an LFO comprised of **4 triangle wave LFOs** running at different frequencies. They are summed together in various combinations to drive each of the filters. - -Each **CV input stage** is the same: a knob that supplies a fixed offset and a CV input that is processed by an attenuverter. The processed CV is added to the knob voltage. See below for more on [Attenuverters](#atten) and [CV ranges](#cv). - -The **LFO** Rate control shifts the speed of all 4 LFOs while maintaining the ratio of their frequencies. - -The **Fc** control moves the frequencies of the first three filters, but not by equal amounts. The lowest filter moves at 1V/Oct, but the middle two move less. The top filter is fixed at 3700 Hz. - -The **Q** control does just what it says - controls the Q (resonance) of the filters. - -The **Modulation Depth** controls how much of the summed LFOs get to each filter. Again, the lower filters move farther, and the top filter is fixed. - -The smaller knobs next to the main knobs are **attenuverters**, which scale control voltages. For more on attenuverters, [see below](#atten) - -There are three LFO outputs next to the blinking LFOs. These may be used to modulate other modules, or as semi-random voltage sources. - -**Bass boost** switch. When it’s in the up position (on) there should be more bass. This is done by switching some or all of the filters from bandpass to lowpass. - -LFO **Matrix** switch. This is the unlabeled switch in the LFO section. When it’s down (default position) the LFOs are closely correlated. In the middle we try to make them a little bit more independent. When it’s in the up position the LFOs will often go in different directions. - -# Booty Shifter frequency shifter - -**Booty Shifter** is a frequency shifter inspired by the Moog/Bode frequency shifter module. - -![booty shifter image](./booty-shifter.png) - -The name "Booty Shifter" is a nod to the classic analog module, as well as to a black cat named Booty. - -Booty Shifter will take an audio input and shift the frequencies up or down. This is not like a pitch shift where harmonics will remain in tune; it is an absolute frequency shift in Hz, so in general **harmonics will go way out of tune.** It is similar to a ring-modulator, but less extreme and more versatile. - -## Getting good sounds from Booty Shifter - -Feed in music and shift the frequency a good amount. - -Feed in **speech or radio** and shift it. - -Feed the CV from a **sequencer** to sequence the mayhem. - -Shift **drums** up or down a little bit to re-tune them without the usual pitch-shifting artifacts. - -Small shifts in conjunction with delays can make a chorus-like effect to thicken music. - -## Inputs and outputs - -* **IN** is the audio input. -* **CV** is the pitch shift control voltage. -5V will give minimum shift, +5 will give maximum. -* **DN** is the down-shifted output. -* **UP** is the up-shifted output. - -## Controls - -**RANGE** sets the total shift range in Hz. For example, the 50 Hz setting means that the minimum shift is 50 Hz down, and the maximum is 50 Hz up. - -Range value **Exp is different**. Here minimum shift is 2 Hz, maximum is 2 kHz, with an exponential response. As of version 0.6.2 the response is an accurate 1 Volt per Octave. - -Shift **AMT** is added to the control voltage, with a range of -5..5. - -## Oddities and limitations - -If you shift the frequency up too far, it will alias. There is no anti-aliasing, so if the highest input frequency + shift amount > sample_rate / 2, you will get aliasing. Of course the Bode analog original did not alias. - -If you shift the input down a lot, frequencies will go **below zero and wrap around**. Taken far enough this will completely **reverse the spectrum** of the input. This was a prized feature of the Bode original. - -As you shift the input down, you may start to generate a lot of subsonic energy. A **High Pass filter** may clean this up. - -The down shift **frequency fold-over**, while true to the original, does cause problems when trying to pitch drum tracks down a lot. High pass filtering the input before it is down-shifted can control this. - -# Formants vocal filter - -![formants image](./formants.png) - -Like the **Vocal Animator**, this is a filter bank tuned to the formant frequencies of typical **singing voices**. Unlike Growler, however, the filters do not animate on their own. In addition, the filters are preset to frequencies, bandwidths, and gains that are taken from **measurements of human singers**. - -One of the easiest ways to **get a good sound** from Formants is to use it like a regular VCF. For example, control Fc with an ADSR. Then put a second modulation source into the vowel CV - something as simple as a slow LFO will add interest. - -Use it as a **filter bank**. Just set the knobs for a good sound and leave it fixed to add vocal tones to a pad. Again, modulating the vowel CV can easily give great results. - -Try to synthesize something like **singing** by sequencing the vowel CV of several formants. Leave the Fc in place, or move it slightly as the input pitches move. - -Controls: - -* **Fc** control moves all the filters up and down by the standard one "volt" per octave. -* **Vowel** control smoothly interpolates between 'a', 'e', 'i', 'o', and 'u'. -* **Model** control selects different vocal models: bass, tenor, countertenor, alto, and soprano. -* **Brightness** control gradually boosts the level of the higher formants. When it is all the way down, the filter gains are set by the singing models in the module, which typically fall off with increasing frequency. As this control is increased the gain of the high formant filters is brought up to match the F1 formant filter. - -The **LEDs across the top** indicate which formant is currently being "sung". - -## About Attenuverters - -The small knobs next to the bigger knobs are **attenuverters**. They scale and/or invert the control voltage inputs next to them. When they are turned all the way up the full CV comes through. As they are turned down less CV comes through. Straight up none passes. As they are turned down further the CV comes back, but inverted. - -Sometimes we use attenuverters with a *linear taper*, and sometimes we use an *audio taper*. If you find that on a particular module you do not like the response of the attenuverters, please log a github issue. - -## Control voltage ranges - -Our modules usually expect a control voltage range of **-5 to +5**. The associated offset knobs will also add -5 to +5. After attenuverters are applied to CV the knob value is added. After all that, the result is usually clipped to the -5 to +5 range. +[Thread Booster](./booster.md) reduces pops and clicks in VCV Rack by reprogramming VCV's audio engine. \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/docs/both.png b/plugins/community/repos/squinkylabs-plug1/docs/both.png new file mode 100644 index 00000000..a0cf4e05 Binary files /dev/null and b/plugins/community/repos/squinkylabs-plug1/docs/both.png differ diff --git a/plugins/community/repos/squinkylabs-plug1/docs/chebyshev.md b/plugins/community/repos/squinkylabs-plug1/docs/chebyshev.md new file mode 100644 index 00000000..31334517 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/docs/chebyshev.md @@ -0,0 +1,145 @@ +# Chebyshev +Our waveshaper VCO. + +![Functional image](../docs/chebyshev.png) + +## Description of the module + +Chebyshev polynomials have been used to generate complex tones since the early days of computer music. This special math discovered by Mr. Chebyshev enables digital generation of waveforms with any overtone structure using very little computer power. In addition, it is easy and computationally inexpensive to vary the spectrum over time. + +Eventually, this form of synthesis fell out of favor, as FM could provide a wider variety of timbres with acceptable CPU usage. Now, however, the distinctive sound of this form of synthesis provides another unique source of sounds for VCV Rack users. + +The magic of the Chebyshev polynomials is that if a sine wave with amplitude one is put into a Chebyshev polynomial, the output will still be a sine wave, but multiplied in pitch by an integer. + +In our implementation we include the first ten Chebyshev polynomials to generate the first ten harmonics of the harmonic series. These are then mixed together based on knob settings and control voltages to give an output tone with complete control over ten harmonics. + +Many of the controls in this module allow different ways of mixing together these ten harmonics. In addition external signals may be shaped by the waveshaper, with folding or clipping applied. + +Like all our modules, Chebyshev's CPU usage is quite low. + +## Some tips + +It can be a great help learning this module if you patch the output to a Scope module and a frequency analyzer. As you adjust the controls it will be clearer what is going on. + +Also, note that we will repeat that when set up normally, each Chebyshev waveshaper is producing a different harmonic of the VCO output. Because of this, we will refer to these as "harmonic levels" and "waveshaper levels" interchangeably. + +And - each waveshaper is a perfect harmonic only when driven by a pure sine at exactly 1Vp-p. + +## Signal Flow + +First there is a sine wave VCO. It has the controls you would expect, as well as a through-zero linear FM input, which allows a minimal DX7-style FM. + +The VCO output then goes to a wave folder/clipper with gain controls. This allows for some distortion effects, and keeps the signal in a range that will keep the next stage happy. + +The output of the folder/clipper then goes to ten parallel Chebyshev waveshapers. The outputs of these are then mixed together through a specialized mixer. + +When everything is set in a typical manner, each of the Chebyshev waveshapers will be outputting a pure sine wave at an integer multiple of the fundamental frequency. Thus, each one will be a discrete harmonic. + +## Description of the controls + +### VCO + +The controls in the upper right are all for the sine wave VCO. +Octave transposes the pitch in even octaves. + +* **Tune** raises or lowers the pitch by up to a perfect fifth. +* **Mod** controls the modulation (exponential FM) depth of the signal patched to the Mod jack. +* **LFM** controls the linear FM depth of the signal patched to the LFM jack. +* **V/Oct** input is where the main control voltage is patched. + +Mod and LFM perform different functions. Mod, like the CV input, is an exponential control. If an LFO is patched into the Mod input and the Mod depth is adjusted for a vibrato of one semitone, that vibrato will be one semitone regardless of the base pitch. But if an audio rate signal is patched into the Mod input you will tend to get “clangorous” sounds with inharmonic overtones. + +LFM, on the other hand, allows through-zero linear FM. While this is not very good for vibrato it does create complex timbres where the harmonics are in tune, and that "in tuneness" will remain as the mod depth is changed. Exponential FM at audio rates can also be tuned, but the tuning will disappear as the mod depth changes, making it impossible to to generate dynamic harmonic sounds. + +### Folder/Clipper + +Chebyshev polynomials are poorly behaved if they see more than one (volt) at their input. So we use a folder/clipper to make sure this doesn’t happen. + +The controls and CV of the Folder/Clipper: + +* **Fold/Clip** switch. In clip mode, it is a simple hard clipper. In fold mode it’s a waveform folder. +* **Clip LED**. The LED will be green when there is signal, and red when the folder/clipper engages. +* *Gain*. Controls how hard the folder/clipper is driven. Gain knob and CV are combined. +* **Gain trim**. The small knob below the **gain** knob is an attenuator for the **gain** CV. New in 0.6.9. +* **EG**. Also combines with the gains. +* **Ext In.** When a signal is patched here it replaces the internal VCO, allowing any signal to be run through the waveshaper. + +Note that while you can get some cool effects with clipping and folding, they will tend to cause audible aliasing at higher frequencies. Use with care. + +In classic waveshaping synthesis an ADSR or similar would be connected to the EG input. By dynamically changing the level of the sine wave hitting the waveshapers a dynamic timbre will be generated. + +The output of the folder/clipper drives the Chebyshev waveshapers. The last group of controls all work together to determine how the waveshapers are mixed together. + +### Waveshaper controls + +There are a lot of controls that work together to determine how the waveshapers are mixed. When configured normally, that means these controls determine the ratios of all the harmonics of the VCO. + +The **small knobs** running up the left side individually control each waveshaper/harmonic, with fundamental on top, and harmonic 10 on the bottom. + +The input jacks next to them allow the levels of each harmonic to be voltage controlled. + +The **Preset** button toggles all ten harmonics between some good starting points, and also resets the Gain to be exactly 1. + +The **Even** control increases/decreases the level of all the even harmonics together. + +The **Odd** control increases/decreases the level of all the odd harmonics together. + +The **Slope** control will apply a gradual roll-off of the upper harmonics. When it is all the way down the roll-off is 18 decibels per octave. When it is all the way up it’s flat. + +Note that the level of the fundamental is not affected by either the Even or Odd control. + +The Odd, Even, and Slope controls may be thought of as subtractive. When they are all the way up, they have no effect, and you get the mix you would expect from the individual harmonic levels. When you turn these controls down they will reduce the levels of the corresponding harmonics. + +The **Preset** button toggles between two or three settings. It will always have a setting where the fundamental is full and all other harmonics off, and a setting where all harmonics are up full. In addition, if you started with your own setting of the harmonics, the preset button will eventually take you back there, but with the master gain set back to one. + +## Several patching ideas + +### Arbitrary waveform VCO + +Turn the Odd, Even, and Slope controls all the way up. Then the level of each harmonic is controller its own volume control. Mix the harmonics to get a pleasing sound, then use as a static timbre, or run it into a VCO. + +Or start with the individual harmonics all the way up, manipulate Even/Odd/Slope to start sculpting the harmonics. + +Don’t forget the Preset button - it’s your friend here. + +### Dynamic Waveshaping + +Use the built-in VCO. Adjust the harmonic mix to something nice and bright. Then connect an ADSR to the EG input. The ADSR will more or less control the brightness. Make sure the the clip LED is just on the edge of clipping when the EG input is at max. + +At very low levels the output will be primarily fundamental. At max level it will be determined by the waveshaper mix controls. The timbre will go from dull to bright as the EG input increases, but the evolution of the timbre will not be completely even, and definitely will be different than what you would get modulating a VCF with an ADSR. + +The evolution of timbres often sounds "brassy," like a brass instrument. Brass synthesis was indeed a common use of waveshaping synthesis before the era of affordable sampling and physical modelling. + +### Voltage controlled filter slope + +Most conventional VCFs allow a filter of a specific shape to be modulated up and down in frequency. A variable slope VCF lets the shape of the filter be modulated. Modulating the filter slope can be more "natural" sounding. + +In the case of Chebyshev we don’t have a filter, but by controlling the harmonic levels directly we can mimic one. Set up a nice bright sound, patch an ADSR into the Slope input, and try out a simulated variable slope filter. + +### Voltage control of spectrum + +The possibilities for timbral variation seem limitless if you take the time to patch controls signals into the harmonic level CV inputs. Use all the usual suspects here - clocks, LFOs, shift registers, sequencers. + +### FM oscillator + +The inclusion of the LFM input allows a simple form of FM synthesis - one operator FM. + +Use an external sine VCO, and patch it into the LFM input on Chebyshev. Turn up the LFM knob. Use the Preset button to turn up the fundamental and turn off the other waveshaper outputs. + +In FM speak, the external VCO is the modulator and the VCO in Chebyshev is the carrier. The result should be consonant if the frequency of the carrier is a small integer multiple of the modulator frequency. For example, set the modulator an octave lower than the carrier. + +Once again, as the modulation is increased more harmonics will be present, so use an external ADSR and VCA to modulate the level of the modulator sine before it’s patched into Chebyshev. + +Of course FM will work alongside the waveshapers, so feel free to go crazy with all the knobs. But don’t be disappointed if the results are harsh and strange. + +### Process external signals + +Run something other than a sine wave into Ext In, then process your signal with the folder and/or the Chebyshev waveshapers. Again, the output of the waveshapers can be pretty unpredictable as the input signal becomes more complex. + +## A note about Aliasing + +In the standard configuration there will be little, if any, aliasing. Since the highest harmonic is 10X the fundamental, the Chebyshev module can’t even start to alias until the fundamental gets to 2kHz. + +That said, there is no anti-aliasing in this module. The wavefolder can easily alias. Normally the LFM will not alias very much, but with high modulation depth and high pitches it will alias quite a lot. + +We have an informational article that talks more about aliasing [here](./aliasing.md). \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/docs/chebyshev.png b/plugins/community/repos/squinkylabs-plug1/docs/chebyshev.png new file mode 100644 index 00000000..1417d0fd Binary files /dev/null and b/plugins/community/repos/squinkylabs-plug1/docs/chebyshev.png differ diff --git a/plugins/community/repos/squinkylabs-plug1/docs/chopper.md b/plugins/community/repos/squinkylabs-plug1/docs/chopper.md new file mode 100644 index 00000000..f6293da6 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/docs/chopper.md @@ -0,0 +1,36 @@ +# Chopper tremolo / programmable LFO +![chopper image](./chopper.png) + +In its simplest use, Chopper produces a very wide range of **tremolo** effects. The built-in LFO can produce a wide range of waveforms that cover many of the waveforms produced by the tremolo circuits built into **vintage guitar amplifiers**. + +The LFO is sent to an output so that it may modulate other modules. + +There is also a **clock synchronizer** and multiplier. + +To use Chopper as a tremolo, send a signal to the *in* jack, and listen to the *out* jack. Leave the *clock* control at the default *int* setting. Most of the knob settings will now affect the tremolo effect. + +## Chopper LFO + +![chopper LFO image](../docs/lfo-waveforms.png) + +To understand all the LFO settings, it helps to watch the outputs on a scope. + +The LFO starts as **skewed** sawtooth. In the middle position it is a symmetric triangle wave, at one end a positive sawtooth and at the other a negative sawtooth. The signal is sent to the **saw** output. + +The skewed saw then goes to a **waveshaper**. As the shape control is increased the LFO is gradually rounded and then flattened. The shaped LFO is send to the *lfo* output, and used internally to modulate the audio input. + +LFO Controls: + +* **Shape** Flattens the LFO waveform. +* **Skew** Dials in the amount of asymmetry in the LFO. +* **Depth** Shifts and scales the LFO. + +When used as a tremolo effect, you will hear **more tremolo** when these controls are turned up. + +## Chopper clock + +The LFO in Chopper may be synchronized with the ckin signal. There is a built-in **clock multiplier**. To use the synchronization, patch a clock to the ckin, and select x1 from the **clock** knob. To run at a multiple of the input clock, select x2, x3, or x4. + +When Chopper is being synched, the **Phase** control sets the phase difference between the external clock and the synchronized LFO. This may be used to "dial in" the tremolo so that it sounds exactly on the beat (or off the beat). + +There is also an internal LFO that is controlled by the **Rate** control. Set the clock control to *int* to use the internal clock. diff --git a/plugins/community/repos/squinkylabs-plug1/docs/chopper.png b/plugins/community/repos/squinkylabs-plug1/docs/chopper.png new file mode 100644 index 00000000..8bb51887 Binary files /dev/null and b/plugins/community/repos/squinkylabs-plug1/docs/chopper.png differ diff --git a/plugins/community/repos/squinkylabs-plug1/docs/clip-collage.png b/plugins/community/repos/squinkylabs-plug1/docs/clip-collage.png new file mode 100644 index 00000000..3fa46cca Binary files /dev/null and b/plugins/community/repos/squinkylabs-plug1/docs/clip-collage.png differ diff --git a/plugins/community/repos/squinkylabs-plug1/docs/clip.png b/plugins/community/repos/squinkylabs-plug1/docs/clip.png new file mode 100644 index 00000000..8d6a96c3 Binary files /dev/null and b/plugins/community/repos/squinkylabs-plug1/docs/clip.png differ diff --git a/plugins/community/repos/squinkylabs-plug1/docs/colors.md b/plugins/community/repos/squinkylabs-plug1/docs/colors.md new file mode 100644 index 00000000..9e34a297 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/docs/colors.md @@ -0,0 +1,9 @@ +# Colors variable slope noise generator + +![noise image](../docs/colors.png) + +Colors is a colored noise generator. It can generate all the common **"colors"** of noise, including white, pink, red, blue, and violet. It can also produce all the colors in between, as it has a **continuously variable slope**. + +Colors has a single control, "slope." This is the slope of the noise spectrum, from -8 dB/octave to +8 dB/octave. + +The slope of the noise is quite accurate in the mid-band, but at the extremes we flatten the slope to keep from boosting super-low frequencies too much, and to avoid putting out enormous amounts of highs. So the slope is flat below 40hz, and above 6kHz. diff --git a/plugins/community/repos/squinkylabs-plug1/docs/crush.png b/plugins/community/repos/squinkylabs-plug1/docs/crush.png new file mode 100644 index 00000000..8c0e976d Binary files /dev/null and b/plugins/community/repos/squinkylabs-plug1/docs/crush.png differ diff --git a/plugins/community/repos/squinkylabs-plug1/docs/emitter-coupled.png b/plugins/community/repos/squinkylabs-plug1/docs/emitter-coupled.png new file mode 100644 index 00000000..1953a3dc Binary files /dev/null and b/plugins/community/repos/squinkylabs-plug1/docs/emitter-coupled.png differ diff --git a/plugins/community/repos/squinkylabs-plug1/docs/ev3-panel.png b/plugins/community/repos/squinkylabs-plug1/docs/ev3-panel.png new file mode 100644 index 00000000..00b1029f Binary files /dev/null and b/plugins/community/repos/squinkylabs-plug1/docs/ev3-panel.png differ diff --git a/plugins/community/repos/squinkylabs-plug1/docs/ev3.md b/plugins/community/repos/squinkylabs-plug1/docs/ev3.md new file mode 100644 index 00000000..739c80ba --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/docs/ev3.md @@ -0,0 +1,63 @@ +# EV3 triple VCO + +![ev3 image](./ev3-panel.png) + +## About EV3 + +EV3 is made from the sound generating part of Befaco’s EvenVCO, replicated three times. dWe added sync, which was never implemented in the EvenVCO. Then we did our usual overhaul to reduce CPU usage. + +The result is a module containing three VCOs that together use about the same amount of CPU as a single instance of EvenVCO. + +While the waveform generation and alias suppression is lifted directly from EvenVCO, the control selection and response of EV3 is our own design. While the three VCOs are easiest to use together, they may be used completely independently. + +EvenVCO remains an excellent choice for a VCO, but there are definitely some cases where you might choose EV3 instead: + +* It is very easy to make giant stacked oscillator patches, like we did in the old days. + +* One EV3 uses less panel space and CPU than three separate instances of EvenVCO. + +* The sync feature is a welcome addition. + +* The semitone pitch offset can inspire patches and harmonies that you might not find with the standard controls on most VCOs. + +* It is easy to patch up chords with EV3. + +That said, EV3 only offers one waveform output at a time per VCO, whereas EvenVCO makes them all available at the same time. +Using EV3 + +The Initial pitch is controlled by a stepped octave control, a stepped semitone control, and a fine tune. The octave and semitone are displayed as an octave number and a musical interval. + +VCO 2 and 3 have an option for hard sync. The sync input is always the saw output of VCO 1. + +There are independent outputs for each VCO, as well as a 3-to-1 mixer driving a mixed output. + +The CV connections are a bit unusual. If you patch one of the top inputs (VCO 1 inputs) it will drive all three. Each VCO will pick up its input from the first patched input. So, for example, VCO 2 will get its input from the second row, but if nothing is patched to this input it will pick up input from the first row (VCO 1). + +This makes for much less patching when stacking two or three VCO sections in a single voice. + +The controls in depth +The octave knob is at the top left. It is unlabeled, but does have the octave number displayed on top of it. It has a 10 octave range, just like EvenVCO. + +The semitone knob just to the right will add or subtract up to 12 semitones. The label above the knob displays the semitone offset as an interval in diatonic harmony. For example, 7 semitones up is labeled “5th”. Note that the intervals displayed are always an octave plus a transposition up. So lowering 5:0 by two semitones will give you 4:m7th – so it’s displayed as one octave down and a minor 7th up. Note that some of these intervals have more than one spelling. In these cases we made some arbitrary decisions: + +* One semitone up is called minor second, although some would call it a flat second. +* Six semitones up is called Diminished fifth, although it could be called an augmented fourth or a tritone. +* No transposition is displayed as zero, although it would be more correct to call it P1, or perhaps unison. + +The fine control transposes the VCO pitch up and down up to a semitone. + +The mod knob controls the amount of pitch modulation applied to the Fm input CV. This is of course exponential pitch modulation, suitable for pitch bending and vibrato, but not so much for FM synthesis which works linear FM. + +Below the blue knobs are two small black knobs to control the pulse width and pulse width modulation depth. These only have an effect when the square wave output is selected. Although they are only labeled on VCO1, they function just the same on VCO 2 and 3. + +The switches below the knobs are radio buttons that select the waveform for each VCO. The waveforms are sine, triangle, saw-tooth, square, even, and off. The even waveform is what gave the original EvenVCO its name. It is an unusual waveform that has only even harmonics, and not odd ones (not counting the fundamental, which is there). Selecting “no waveform” can be useful when you are patching and want to hear each VCO by itself – like a mute button on a mixing console. + +The CV inputs are at the bottom. The top row is for VCO1, the next for VCO2, and the last row is for VCO3. + +V/Oct is where the main pitch CV is patched, and sets the overall pitch of the VCO. Fm is a less sensitive pitch input used to modulate the pitch. As noted above, the sensitivity of this input is controlled by the Mod knob. The last column of CV inputs is for pulse width modulation. This only has an effect when the square wave is selected. It works in conjunction with the PW and PWM knobs. + +The output section has a column of three output level controls, one for each VCO. Then there are the three jack, one for each VCO output, and a mixed output. + +We have an informational article that talks more about aliasing. It shows you how to compare different modules using a spectrum analyzer. [Aliasing Story](./aliasing.md). + +If you would like some information on how we reduced the CPU usage of EvenVCO, you can [find it here](../docs/vco-optimization.md). \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/docs/fold-collage.png b/plugins/community/repos/squinkylabs-plug1/docs/fold-collage.png new file mode 100644 index 00000000..03dc7c77 Binary files /dev/null and b/plugins/community/repos/squinkylabs-plug1/docs/fold-collage.png differ diff --git a/plugins/community/repos/squinkylabs-plug1/docs/folder-II.png b/plugins/community/repos/squinkylabs-plug1/docs/folder-II.png new file mode 100644 index 00000000..542ea375 Binary files /dev/null and b/plugins/community/repos/squinkylabs-plug1/docs/folder-II.png differ diff --git a/plugins/community/repos/squinkylabs-plug1/docs/folder.png b/plugins/community/repos/squinkylabs-plug1/docs/folder.png new file mode 100644 index 00000000..80d2b809 Binary files /dev/null and b/plugins/community/repos/squinkylabs-plug1/docs/folder.png differ diff --git a/plugins/community/repos/squinkylabs-plug1/docs/formants.md b/plugins/community/repos/squinkylabs-plug1/docs/formants.md new file mode 100644 index 00000000..484718a2 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/docs/formants.md @@ -0,0 +1,20 @@ +# Formants vocal filter + +![formants image](./formants.png) + +Like the **Vocal Animator**, this is a filter bank tuned to the formant frequencies of typical **singing voices**. Unlike Growler, however, the filters do not animate on their own. In addition, the filters are preset to frequencies, bandwidths, and gains that are taken from **measurements of human singers**. + +One of the easiest ways to **get a good sound** from Formants is to use it like a regular VCF. For example, control Fc with an ADSR. Then put a second modulation source into the vowel CV - something as simple as a slow LFO will add interest. + +Use it as a **filter bank**. Just set the knobs for a good sound and leave it fixed to add vocal tones to a pad. Again, modulating the vowel CV can easily give great results. + +Try to synthesize something like **singing** by sequencing the vowel CV of several formants. Leave the Fc in place, or move it slightly as the input pitches move. + +Controls: + +* **Fc** control moves all the filters up and down by the standard one "volt" per octave. +* **Vowel** control smoothly interpolates between 'a', 'e', 'i', 'o', and 'u'. +* **Model** control selects different vocal models: bass, tenor, countertenor, alto, and soprano. +* **Brightness** control gradually boosts the level of the higher formants. When it is all the way down, the filter gains are set by the singing models in the module, which typically fall off with increasing frequency. As this control is increased the gain of the high formant filters is brought up to match the F1 formant filter. + +The **LEDs across the top** indicate which formant is currently being "sung". diff --git a/plugins/community/repos/squinkylabs-plug1/docs/full-wave.png b/plugins/community/repos/squinkylabs-plug1/docs/full-wave.png new file mode 100644 index 00000000..35f4986a Binary files /dev/null and b/plugins/community/repos/squinkylabs-plug1/docs/full-wave.png differ diff --git a/plugins/community/repos/squinkylabs-plug1/docs/functional-vco-1.md b/plugins/community/repos/squinkylabs-plug1/docs/functional-vco-1.md new file mode 100644 index 00000000..62a402b9 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/docs/functional-vco-1.md @@ -0,0 +1,13 @@ +# Functional VCO-1 + +![Functional image](../docs/functional.png) + +Functional VCO-1 works just like its namesake. The control layout is familiar, the sound is the same, but it uses about 1/4 as much CPU as the original. + +We believe VCV's Fundamental VCO is an unsung hero. It's one of the few VCOs that never has audible aliasing artifacts. You can sync it, and modulate all its inputs, but the sound never falls apart. + +We "forked" the code to Fundamental VCO-1 and modified it a little bit to make it much more CPU efficient. Now you may use a lot more of them without pops, clicks, and dropouts. + +If you would like the details of how we did this optimization, you can [find them here](../docs/vco-optimization.md). + +We have an informational article that talks more about aliasing. It shows you how to compare different modules using a spectrum analyzer. [Aliasing Story](./aliasing.md). diff --git a/plugins/community/repos/squinkylabs-plug1/docs/functional.png b/plugins/community/repos/squinkylabs-plug1/docs/functional.png new file mode 100644 index 00000000..2c927ee3 Binary files /dev/null and b/plugins/community/repos/squinkylabs-plug1/docs/functional.png differ diff --git a/plugins/community/repos/squinkylabs-plug1/docs/gray-code.md b/plugins/community/repos/squinkylabs-plug1/docs/gray-code.md new file mode 100644 index 00000000..49949695 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/docs/gray-code.md @@ -0,0 +1,18 @@ +# Gray Code: eclectic clock divider + +![gray code image](./gray-code.png) + +## About Gray Code +A cool feature of gray codes is that only one bit changes at a time. Having only one “thing” change at a time can be interesting for music, so we are hoping you will find some good things to do with it. + +WikiPedia has a very good article on gray codes: https://en.wikipedia.org/wiki/Gray_code + +Our Gray Code module has only one control. It selects between standard gray code and balanced gray codes. With a standard gray code, the lower bits change much more often than the high bits. You can see it counting up. With the balanced gray codes, all the bits change more or less the same amount, but of course no two ever change at the same time. + +Each bit of the 8-bit gray code comes out to an output jack. The LED beside the output shows when it goes high and low. + +There is an additional output that adds up all the bits with a binary weighting, kind of like a DAC. + +The external clock input must be driven with a clock - there is no internal clock. + +Now let your imagination run wild! \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/docs/gray-code.png b/plugins/community/repos/squinkylabs-plug1/docs/gray-code.png new file mode 100644 index 00000000..25d440cf Binary files /dev/null and b/plugins/community/repos/squinkylabs-plug1/docs/gray-code.png differ diff --git a/plugins/community/repos/squinkylabs-plug1/docs/growler.md b/plugins/community/repos/squinkylabs-plug1/docs/growler.md new file mode 100644 index 00000000..ef9e3397 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/docs/growler.md @@ -0,0 +1,39 @@ +# Growler + +![vocal formant filter image](./growler.jpg) + +**Growler** is a re-creation of the Vocal Animator circuit invented by Bernie Hutchins, and published in Electronotes magazine in the late 70's. It continuously morphs between different vaguely voice like tones. + +**To get a good sound:** run any harmonically rich signal into the input, and something good will come out. Low frequency pulse waves and distorted sounds make great input. + +The controls do pretty much what you would expect: + +* **LFO** controls the speed of the modulation LFOs. +* **Fc** controls the average frequency of the multiple filters. +* **Q** controls the sharpness of the filters. +* **Depth** controls how much of the modulation LFOs are applied to the filters. + +## How Growler works +![growler scope](./growler.png) + +There are four **bandpass filters**, roughly tuned to some typical vocal formant frequencies: 522, 1340, 2570, and 3700 Hz. The filters are run in parallel, with their outputs summed together. + +The first three filter frequencies are modulated by an LFO comprised of **4 triangle wave LFOs** running at different frequencies. They are summed together in various combinations to drive each of the filters. + +Each **CV input stage** is the same: a knob that supplies a fixed offset and a CV input that is processed by an attenuverter. The processed CV is added to the knob voltage. See below for more on [Attenuverters](#atten) and [CV ranges](#cv). + +The **LFO** Rate control shifts the speed of all 4 LFOs while maintaining the ratio of their frequencies. + +The **Fc** control moves the frequencies of the first three filters, but not by equal amounts. The lowest filter moves at 1V/Oct, but the middle two move less. The top filter is fixed at 3700 Hz. + +The **Q** control does just what it says - controls the Q (resonance) of the filters. + +The **Modulation Depth** controls how much of the summed LFOs get to each filter. Again, the lower filters move farther, and the top filter is fixed. + +The smaller knobs next to the main knobs are **attenuverters**, which scale control voltages. For more on attenuverters, [see below](#atten) + +There are three LFO outputs next to the blinking LFOs. These may be used to modulate other modules, or as semi-random voltage sources. + +**Bass boost** switch. When it’s in the up position (on) there should be more bass. This is done by switching some or all of the filters from bandpass to lowpass. + +LFO **Matrix** switch. This is the unlabeled switch in the LFO section. When it’s down (default position) the LFOs are closely correlated. In the middle we try to make them a little bit more independent. When it’s in the up position the LFOs will often go in different directions. diff --git a/plugins/community/repos/squinkylabs-plug1/docs/half-wave.png b/plugins/community/repos/squinkylabs-plug1/docs/half-wave.png new file mode 100644 index 00000000..fc599076 Binary files /dev/null and b/plugins/community/repos/squinkylabs-plug1/docs/half-wave.png differ diff --git a/plugins/community/repos/squinkylabs-plug1/docs/intro-1-110.png b/plugins/community/repos/squinkylabs-plug1/docs/intro-1-110.png new file mode 100644 index 00000000..6e225cda Binary files /dev/null and b/plugins/community/repos/squinkylabs-plug1/docs/intro-1-110.png differ diff --git a/plugins/community/repos/squinkylabs-plug1/docs/intro-2-110.png b/plugins/community/repos/squinkylabs-plug1/docs/intro-2-110.png new file mode 100644 index 00000000..a583ec64 Binary files /dev/null and b/plugins/community/repos/squinkylabs-plug1/docs/intro-2-110.png differ diff --git a/plugins/community/repos/squinkylabs-plug1/docs/intro-3-110.png b/plugins/community/repos/squinkylabs-plug1/docs/intro-3-110.png new file mode 100644 index 00000000..b98b1e7b Binary files /dev/null and b/plugins/community/repos/squinkylabs-plug1/docs/intro-3-110.png differ diff --git a/plugins/community/repos/squinkylabs-plug1/docs/lfn.md b/plugins/community/repos/squinkylabs-plug1/docs/lfn.md new file mode 100644 index 00000000..0d55c615 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/docs/lfn.md @@ -0,0 +1,17 @@ +# LFN Low Frequency Noise Generator + +![LFN image](../docs/lfn.png) + +LFN stands for Low Frequency Noise. Technically it is a white noise generator run through a graphic equalizer at extremely low frequencies. People may find it easier to think of it as a random voltage source with unique control over the output. + +The top knob, which is unlabeled, sets the "base frequency" of LFN. + +The five other knobs, and the CV inputs beside them, control the gain of the graphic equalizers sections. Beside each EQ gain knob is a label indicating what frequency range that knob controls. + +For example, it the base frequency is 1.0, the EQ sections will be at 1Hz, 2Hz, 4Hz, 8Hz, and 16Hz. If the base frequency is 0.2, The EQ sections will be at 0.2Hz, 0.4Hz, 0.8Hz, 1.6Hz, and 3.2Hz. + +But instead of thinking about frequencies like 1Hz, which are a little difficult to imagine, think of the knobs as mixing very slow random voltages, with less slow ones. For example if LFN is driving a pitch quantizer into a VCO, turn all the knobs down to zero except the first, lowest, one. This will make a series of pitches slowly rising and falling. Then bring in a little of the faster channels. The pitch will still be slowly rising and falling, but will also quickly move up and down by smaller steps. + +A good way to learn what makes LFN tick is to set it slow and watch it on the scope. At the same time run it straight into a VCO. Experiment with different mixes of the slow knobs and the fast ones. + +As you would expect from Squinky Labs, the CPU usage of LFN is very low. In fact it is one of our leanest modules yet. So feel free to use as many instances as you like. diff --git a/plugins/community/repos/squinkylabs-plug1/docs/lfn.png b/plugins/community/repos/squinkylabs-plug1/docs/lfn.png new file mode 100644 index 00000000..63598c42 Binary files /dev/null and b/plugins/community/repos/squinkylabs-plug1/docs/lfn.png differ diff --git a/plugins/community/repos/squinkylabs-plug1/docs/odd.png b/plugins/community/repos/squinkylabs-plug1/docs/odd.png new file mode 100644 index 00000000..a84919fe Binary files /dev/null and b/plugins/community/repos/squinkylabs-plug1/docs/odd.png differ diff --git a/plugins/community/repos/squinkylabs-plug1/docs/release-notes.md b/plugins/community/repos/squinkylabs-plug1/docs/release-notes.md new file mode 100644 index 00000000..a46b13a9 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/docs/release-notes.md @@ -0,0 +1,19 @@ +# Release notes for Squinky Labs modules + +## 0.6.9 + +Introduced three new modules: EV3, Gray Code, and Shaper. + +Added a trim control for external gain CV in Chebyshev. Previously saved patches may require that the gain trim be increased. + +Minor graphic tweaks to module panels. + +## 0.6.8 + +Introduced Chebyshev waveshaper VCO. + +Introduced release notes. + +Lowered the distortion in sin oscillator that is used in all modules. + +Re-ordered and re-worded module names in the browser. \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/docs/shaper-panel.png b/plugins/community/repos/squinkylabs-plug1/docs/shaper-panel.png new file mode 100644 index 00000000..ab5cc72f Binary files /dev/null and b/plugins/community/repos/squinkylabs-plug1/docs/shaper-panel.png differ diff --git a/plugins/community/repos/squinkylabs-plug1/docs/shaper.md b/plugins/community/repos/squinkylabs-plug1/docs/shaper.md new file mode 100644 index 00000000..ba66fa71 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/docs/shaper.md @@ -0,0 +1,105 @@ +# Shaper precision waveshaper + +![shaper image](./shaper-panel.png) + +## About Shaper +Shaper is a waveshaper offering many different shape options. Some of these shapes are commonly found in other wave shapers, and some are unique to Shaper. It can be used to modify the waveforms from a VCO, or to add distortion to some other sound. And, as usual, the creative user may use it to process control voltages, or other "left field" uses. + +A unique feature of Shaper is that it has very little aliasing, whereas most we have seen have a lot of aliasing. The other special thing about Shaper is that it has a few shapes that are good for "soft overdrive". + +Although there are many creative ways to use a wave shaper, the two most common are as a mangled waveform shaper, and as a distortion/overdrive effect. + +In the first use case, the waveshaper is often connected directly to the output of a VCO. This gives a large number of different sounds from the VCO. In this use, typically extreme settings are used, with folding being a classic example. + +In the second use case, as a distortion/overdrive effect, often less extreme settings are used. For example, it would be unusual to run a recording of singing through a wavefolder, but a gentle overdrive is pretty common. + +The switch with the labels 16X, 4X, and 1X controls the amount of oversampling. This is how Shaper keeps aliasing under control. Waveshapers by their nature generate a lot of harmonics at high frequencies, and these tend to “fold back” into the audio range as aliasing. Oversampling reduces this effect by doing all the processing at a higher sample rate, then removing the frequencies that are too high, and reducing the sample rate back down. The more oversampling, the less aliasing. + +At 16X, Shaper is oversampling by a factor of 16. So for a 44,100 sampling rate, Shaper would be working at 705kHz! This is the oversampling rate used by Fundamental VCO-1 and Functional VCO-1. At this setting it is very difficult to hear or measure any aliasing at all, although it is present in tiny amounts. + +In general we recommend 16X, but there are several reasons you might want to set it lower. Firstly, you may actually want aliasing. When using Shaper as an extreme mangler the extra grit and digital nasties may fit perfectly. The other reason is CPU usage. Although Shaper at X16 does not use a large amount of CPU, it uses a lot less at 4X or 1X. It’s pretty much proportional to the setting. At 16X Shaper does 16 times as much work as at 1X. + +If you are using one of the gentler settings of Shaper, 4X may have completely inaudible alisasing also. With some settings, however, we can measure significant aliasing at 4X. If you have plenty of CPU, just leave it at 16X or 1X. But if you are running out of CPU 4X can be a very workable and smooth sounding alternative. + +Aside from the Oversampling selector, there are three controls: + +*Gain* – boosts the input signal, which tends to cause more shaping, distorting, and mangling. Controlled by the gain knob, and the gain CV. There is an attenuator on the gain CV. + +*Offset* – shifts the signal before it hits the waveshaper. In general that will increase the level of even harmonics in the output. Many of the shapes will output no even harmonics at all if the offset is zero. Like the gain, the offset is controlled by a knob, and a CV with attenuator. +The offset is bidirectional, so there is no offset when the knob is straight up and the CV is zero. + +*Shape* – this is the big unlabeled button. It selects from the different shapes that Shaper can produce. The name of the selected shape is to the right of the knob. + +## Some notes on the different shapes + +### Smooth + +![smooth image](./smooth.png) + +Smooth is inspired by the asymmetrical distortion curve of a vacuum tube triode. It is by no means a model of a tube at all, but the shape is similar. +Smooth uses gentle curves, and wants to be used more as a distortion or thickener that a full on mangler. + +With the Smooth setting, the offset control doesn’t actually control the offset; it controls the amount of asymmetry in the output. So, like a normal offset it will bring in even harmonics, but the way the even harmonics come in at different levels is unique. + +Where normally there are the least even harmonics when the knob is in the middle, with Smooth there are no even harmonics when it’s all the way down. + +### Clip + +![clip image](./clip.png) + +Classic hard clipping. With the gain high the output will be a square wave. +Clip is not very useful for shaping a VCO output, since most VCOs already put out a square wave. But it is useful for generating a lot of distortion. It is similar to some guitar fuzz-boxes. + +### Emitter Coupled + +![ec image](./emitter-coupled.png) + +Models the saturation of an emitter coupled pair amplifier input, as was commonly found in the classic 3080 “OTA” chip that was used in many analog synthesizers and some phase shifters. Even when driven hard, Emitter Coupled will not distort as much as some of the other shapes, and definitely won’t mangle a sound. + +### Full Wave + +![fw image](./full-wave.png) + +A very simple shape, but one that has some unique characteristics. This is just the absolute value function we learned about in school. Its unique characteristic is that while it generates a lot of distortion harmonics, it does not compress the dynamic range of music played through it. + +It is called Full Wave because this is an idealized version of the shape that will come out of a full wave rectifier circuit. + +While the full wave shape is usually too harsh and "buzzy" to use as an overdrive/distortion, it does make a nice mangler. + +### Half Wave + +![hw image](./half-wave.png) + +Has much in common with the full wave shape, but its radical asymmetry guarantees that a lot of even harmonics will be in the output. + +Unlike most of the shapes, both of the rectifier shapes have the same harmonic content regardless of the gain setting. So here it only functions as a volume control. + +### Fold + +![fold image](./folder.png) + +The "wavefolder" is a legendary and classic synthesizer module. Both Buchla and Serge offer wavefolder modules, and they were (and remain) very popular. The wavefolder we have implemented in Shaper is not an attempt at an emulation of those classics, but is a mathematically simple folding of the input. + +One thing all wavefolders have in common is that the sound changes quite a bit as the gain increases, sounding something like a filter sweep, or even more like the sweep of a synched VCO. So one of the first things to try is modulating the gain input with an envelope or other modulation source. + +One thing our wavefolder has in common with the analog classics is that it has little or no aliasing. Wavefolding generates a huge amount of high harmonics, so a digital implementation that does not deal with the aliasing is going to sound different from Shaper (or an analog module). + +That said, musicians can undoubtedly find uses for the fully aliased version. We encourage you to try at 16X and at 1X. + +### Fold 2 + +![fold 2 image](./folder-II.png) + +Whereas “Fold” is a standard wavefolder, Fold2 is a little bit different. It is asymmetric no matter what offset is fed into it. It also generates more high harmonics than any other shape. + +### Crush + +![crush image](./crush.png) + +Crush simulates bit reduction by using a continuous voltage quantizer. As the gain is turned up it will pass 16 bits, down to 8 bits, and finally one bit. + +## More Info + +We have an informational article that talks more about aliasing. It goes into some specifics about Shaper, but also has some useful general information. + +[Aliasing Story](./aliasing.md). \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/docs/shifter.md b/plugins/community/repos/squinkylabs-plug1/docs/shifter.md new file mode 100644 index 00000000..048ea5c6 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/docs/shifter.md @@ -0,0 +1,46 @@ +# Booty Shifter frequency shifter + +**Booty Shifter** is a frequency shifter inspired by the Moog/Bode frequency shifter module. + +![booty shifter image](./booty-shifter.png) + +The name "Booty Shifter" is a nod to the classic analog module, as well as to a black cat named Booty. + +Booty Shifter will take an audio input and shift the frequencies up or down. This is not like a pitch shift where harmonics will remain in tune; it is an absolute frequency shift in Hz, so in general **harmonics will go way out of tune.** It is similar to a ring-modulator, but less extreme and more versatile. + +## Getting good sounds from Booty Shifter + +Feed in music and shift the frequency a good amount. + +Feed in **speech or radio** and shift it. + +Feed the CV from a **sequencer** to sequence the mayhem. + +Shift **drums** up or down a little bit to re-tune them without the usual pitch-shifting artifacts. + +Small shifts in conjunction with delays can make a chorus-like effect to thicken music. + +## Inputs and outputs + +* **IN** is the audio input. +* **CV** is the pitch shift control voltage. -5V will give minimum shift, +5 will give maximum. +* **DN** is the down-shifted output. +* **UP** is the up-shifted output. + +## Controls + +**RANGE** sets the total shift range in Hz. For example, the 50 Hz setting means that the minimum shift is 50 Hz down, and the maximum is 50 Hz up. + +Range value **Exp is different**. Here minimum shift is 2 Hz, maximum is 2 kHz, with an exponential response. As of version 0.6.2 the response is an accurate 1 Volt per Octave. + +Shift **AMT** is added to the control voltage, with a range of -5..5. + +## Oddities and limitations + +If you shift the frequency up too far, it will alias. There is no anti-aliasing, so if the highest input frequency + shift amount > sample_rate / 2, you will get aliasing. Of course the Bode analog original did not alias. + +If you shift the input down a lot, frequencies will go **below zero and wrap around**. Taken far enough this will completely **reverse the spectrum** of the input. This was a prized feature of the Bode original. + +As you shift the input down, you may start to generate a lot of subsonic energy. A **High Pass filter** may clean this up. + +The down shift **frequency fold-over**, while true to the original, does cause problems when trying to pitch drum tracks down a lot. High pass filtering the input before it is down-shifted can control this. diff --git a/plugins/community/repos/squinkylabs-plug1/docs/smooth.png b/plugins/community/repos/squinkylabs-plug1/docs/smooth.png new file mode 100644 index 00000000..77c0b178 Binary files /dev/null and b/plugins/community/repos/squinkylabs-plug1/docs/smooth.png differ diff --git a/plugins/community/repos/squinkylabs-plug1/docs/thread-booster.png b/plugins/community/repos/squinkylabs-plug1/docs/thread-booster.png new file mode 100644 index 00000000..64a5db75 Binary files /dev/null and b/plugins/community/repos/squinkylabs-plug1/docs/thread-booster.png differ diff --git a/plugins/community/repos/squinkylabs-plug1/docs/vco-optimization.md b/plugins/community/repos/squinkylabs-plug1/docs/vco-optimization.md new file mode 100644 index 00000000..3eccca33 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/docs/vco-optimization.md @@ -0,0 +1,72 @@ +# Notes about the creation of Functional VCO-1 + +## About the Original + +Fundamental VCO-1 is a very high quality, excellent sounding VCO. It does exactly what it claims to do with very little digital artifacts. VCO-1 is a very popular module, but it does use a lot of CPU. For this reason it seemed like a good candidate for a CPU diet. VCV users continue to complain about popping and clicking with large patches, so we hope improvements to this popular module will help. + +Fundamental VCO-1 uses 16X oversampling to generate standard waveforms, in both "analog" and "digital" versions. The oversampling keeps the aliasing low, allows hard and soft sync with low aliasing, and suppresses aliasing from audio rate modulation. + +As such, Fundamental VCO-1 is a very good substitute for an analog VCO. The only down-side is that the 16X oversampling increases the CPU usage dramatically. + +In revamping this VCO, we wanted to try as much as possible to preserve the sound exactly like the original. We did not add any new features, or try to make anything "better". And when we put the code on a diet we did not want to lower the sound quality of this workhorse module. + +## Initial Measurements + +It is difficult to accurately measure the CPU usage of a VCV module. Since version 0.6 there have been CPU meters which are useful for getting an overall picture of CPU usage; but the CPU meters do not enable stable, accurate, and repeatable measurements. + +So we more or less run the plugins in an isolated test framework. This lets us get precise measurements. The down side is that the isolated system is different from running in VCV, and the numbers won’t correlate exactly. + +We use an arbitrary scale for our measurements, where "100" means that the plugin under test seems to be using 1% of the available CPU on one core of our quite old Intel Core i5 Windows-7 computer. + +Here are the initial measurements we took before any optimizations were done, along with some Squinky Labs modules for reference: + +* Fundamental VCO-1, all outputs patched, digital: 798 +* Fundamental VCO-1, saw only, digital: 489 +* Fundamental VCO-1, saw only, analog: 270 +* SL Formants: 84.1 +* SL Growler: 50.9 +* SL Chopper: 14.9 +* SL Booty Shifter: 11.2 +* SL Colors: 11.6 + +Fundamental VCO-1 uses a *lot* of CPU. Since it is so heavy in its CPU usage, we thought it would be easy to make it much faster. But it was not as easy as we had hoped. + +## General approach to optimization + +Every theory must be validated by experiment. So we look at the code, formulate a theory, throw together a simplified implementation of the theory, and compare before and after measurements. + +If the CPU usage goes down a lot, the experiment is a success, and we try to make a full implementation that preserves the drop in CPU usage without compromising the quality. + +Then repeat this process over and over until done. + +## What we did to Fundamental VCO-1 + +VCO-1 already had some optimization. Although the waveform generation runs for all waveforms all the time, the decimation filters are only run for outputs that are connected. The decimation filters are the same ones used by the VCV Rack audio engine, so we assume they are linear phase FIR filters, as is customary for high quality sample rate conversion. + +But, since this is an oversampling VCO, the waveform generation is running at 16X sample rate. Any extra work in this "inner loop" is going to be magnified by 16. + +That said, our experiments showed few surprises. + +* Since cosf() is called all the time in the 16X waveform generation loop, it was a no-brainer to replace it with the same sin lookup table we use in most of our modules. Likewise, we made sure that the cosf lookup is only called if the SIN output is connected. +* The powf() call is also slow and it was worthwhile getting rid of the powf call, although the gain was not enormous since powf is only called once per sample. +* We refactored the inner loop to make sure that the waveform generation is only done for waveforms whose output is patched. +* The decimation filters use a lot of CPU, so we replaced the stock ones with simple 6-pole butterworth lowpass filters. +* It would of course save CPU to reduce the oversampling rate, but we did not want to decrease the quality. + +Again, most of the software required was already in the code-base for our other modules. The one thing that was difficult here was devising test software to measure the aliasing. We wanted to be sure that our faster decimation filters were not increasing the level of aliasing. While this new alias test is not perfect, it did give us confidence that we were not increasing the aliasing with our substituted filters. + +## Results + +Before and after: + +* Fundamental VCO-1, all outputs patched, digital: 798 -> 187.8 (X4.2) +* Fundamental VCO-1, saw only, digital: 489 -> 83.7 (X5.8) +* Fundamental VCO-1, saw only, analog: 270 -> 83 (X2.3) + +## Addendum for EV3 VCO + +When we looked at EvenVCO, we found many of the same issues as we found in Fundamental VCO-1. The same extremely slow trig and exponential functions that we replaced with lookup tables. Work was being done for waveforms that weren't patched. + +But some other things were different. Since EvenVCO is a MinBLEP VCO, it does not need anti-alias filters (as such). The existing MinBLEP code is quite efficient. But in the VCO we did achieve significant speedup by getting rid of the one routine that generates all the waves, and instead have dedicated routines for each waveform. This eliminated a lot of conditional branching. + +While EvenVCO is already quite efficient, it was still worthwhile to make it faster. We believe out triple version uses about the same CPU as a single instance of EvenVCO. \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/fft/FFT.cpp b/plugins/community/repos/squinkylabs-plug1/dsp/fft/FFT.cpp index ab4c736c..3f55b9cd 100644 --- a/plugins/community/repos/squinkylabs-plug1/dsp/fft/FFT.cpp +++ b/plugins/community/repos/squinkylabs-plug1/dsp/fft/FFT.cpp @@ -49,7 +49,6 @@ bool FFT::forward(FFTDataCpx* out, const FFTDataReal& in) return true; } - bool FFT::inverse(FFTDataReal* out, const FFTDataCpx& in) { if (out->buffer.size() != in.buffer.size()) { @@ -83,16 +82,16 @@ bool FFT::inverse(FFTDataReal* out, const FFTDataCpx& in) return true; } -int FFT::freqToBin(float freq, float sampleRate, int numBins) +int FFT::freqToBin(double freq, double sampleRate, int numBins) { assert(freq <= (sampleRate / 2)); // bin(numBins) <> sr / 2; - return (int)((freq / sampleRate)*(numBins * 2)); + return (int)((freq / sampleRate)*(numBins)); } -float FFT::bin2Freq(int bin, float sampleRate, int numBins) +double FFT::bin2Freq(int bin, double sampleRate, int numBins) { - return sampleRate * float(bin) / (float(numBins) * 2); + return sampleRate * double(bin) / double(numBins); } static float randomPhase() @@ -120,8 +119,8 @@ static void makeNegSlope(FFTDataCpx* output, const ColoredNoiseSpec& spec) static float k = -spec.slope * log2(lowFreqCorner); for (int i = bin40 + 1; i < numBins; ++i) { if (i < numBins / 2) { - const float f = FFT::bin2Freq(i, spec.sampleRate, numBins); - const float gainDb = std::log2(f) * spec.slope + k; + const double f = FFT::bin2Freq(i, spec.sampleRate, numBins); + const double gainDb = std::log2(f) * spec.slope + k; const float gain = float(AudioMath::gainFromDb(gainDb)); output->set(i, std::polar(gain, randomPhase())); } else { @@ -143,8 +142,8 @@ static void makePosSlope(FFTDataCpx* output, const ColoredNoiseSpec& spec) static float k = -spec.slope * log2(spec.highFreqCorner); for (int i = binHigh - 1; i > 0; --i) { if (i < numBins / 2) { - const float f = FFT::bin2Freq(i, spec.sampleRate, numBins); - const float gainDb = std::log2(f) * spec.slope + k; + const double f = FFT::bin2Freq(i, spec.sampleRate, numBins); + const double gainDb = std::log2(f) * spec.slope + k; const float gain = float(AudioMath::gainFromDb(gainDb)); gainMax = std::max(gain, gainMax); output->set(i, std::polar(gain, randomPhase())); @@ -189,10 +188,11 @@ static float getPeak(const FFTDataReal& data) return peak; } -void FFT::normalize(FFTDataReal* data) +void FFT::normalize(FFTDataReal* data, float maxValue) { + assert(maxValue > 0); const float peak = getPeak(*data); - const float correction = 1.0f / peak; + const float correction = maxValue / peak; for (int i = 0; i < data->size(); ++i) { float x = data->get(i); x *= correction; diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/fft/FFT.h b/plugins/community/repos/squinkylabs-plug1/dsp/fft/FFT.h index 01b717bb..a2a9de32 100644 --- a/plugins/community/repos/squinkylabs-plug1/dsp/fft/FFT.h +++ b/plugins/community/repos/squinkylabs-plug1/dsp/fft/FFT.h @@ -1,8 +1,9 @@ #pragma once -class FFTDataCpx; -class FFTDataReal; +//class FFTDataCpx; +//class FFTDataReal; +#include "FFTData.h" class ColoredNoiseSpec { @@ -33,7 +34,7 @@ public: */ static void makeNoiseSpectrum(FFTDataCpx* output, const ColoredNoiseSpec&); - static void normalize(FFTDataReal*); - static float bin2Freq(int bin, float sampleRate, int numBins); - static int freqToBin(float freq, float sampleRate, int numBins); + static void normalize(FFTDataReal*, float maxValue); + static double bin2Freq(int bin, double sampleRate, int numBins); + static int freqToBin(double freq, double sampleRate, int numBins); }; \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/fft/FFTCrossFader.cpp b/plugins/community/repos/squinkylabs-plug1/dsp/fft/FFTCrossFader.cpp index ecda08d5..7c5a1f5a 100644 --- a/plugins/community/repos/squinkylabs-plug1/dsp/fft/FFTCrossFader.cpp +++ b/plugins/community/repos/squinkylabs-plug1/dsp/fft/FFTCrossFader.cpp @@ -15,12 +15,12 @@ NoiseMessage* FFTCrossFader::step(float* out) // curPlayOffset1 is the index into buffer 1, but also the crossfade index assert(curPlayOffset[1] < crossfadeSamples); - float buffer0Value = dataFrames[0]->dataBuffer->get(curPlayOffset[0]) * - (crossfadeSamples - (curPlayOffset[1]+1)); + float buffer0Value = dataFrames[0]->dataBuffer->get(curPlayOffset[0]) * + (crossfadeSamples - (curPlayOffset[1] + 1)); float buffer1Value = dataFrames[1]->dataBuffer->get(curPlayOffset[1]) * curPlayOffset[1]; // TODO: do we need to pre-divide - *out = (buffer1Value + buffer0Value) / (crossfadeSamples-1); + *out = (buffer1Value + buffer0Value) / (crossfadeSamples - 1); if (makeupGain) { float gain = std::sqrt(2.0f) - 1; float offset = float(curPlayOffset[1]); @@ -70,8 +70,7 @@ NoiseMessage * FFTCrossFader::acceptData(NoiseMessage* msg) if (dataFrames[0] == nullptr) { dataFrames[0] = msg; curPlayOffset[0] = 0; - } - else if (dataFrames[1] == nullptr) { + } else if (dataFrames[1] == nullptr) { dataFrames[1] = msg; curPlayOffset[1] = 0; } else { diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/fft/FFTData.cpp b/plugins/community/repos/squinkylabs-plug1/dsp/fft/FFTData.cpp index 7324028c..26712e0a 100644 --- a/plugins/community/repos/squinkylabs-plug1/dsp/fft/FFTData.cpp +++ b/plugins/community/repos/squinkylabs-plug1/dsp/fft/FFTData.cpp @@ -1,67 +1,10 @@ #include "FFTData.h" -#include "kiss_fft.h" -#include "kiss_fftr.h" -#include +//template -int FFTDataCpx::_count = 0; -FFTDataCpx::FFTDataCpx(int numBins) : - buffer(numBins) -{ - ++_count; -} +template <> +int FFTData::_count = 0; -FFTDataCpx::~FFTDataCpx() -{ - // We need to manually delete the cfg, since only "we" know - // what type it is. - if (kiss_cfg) { - free(kiss_cfg); - } - --_count; -} - -cpx FFTDataCpx::get(int index) const -{ - assert(index < (int)buffer.size()); - return buffer[index]; -} - - -void FFTDataCpx::set(int index, cpx value) -{ - assert(index < (int)buffer.size()); - buffer[index] = value; -} - -/******************************************************************/ -int FFTDataReal::_count = 0; -FFTDataReal::FFTDataReal(int numBins) : - buffer(numBins) -{ - ++_count; -} - -FFTDataReal::~FFTDataReal() -{ - // We need to manually delete the cfg, since only "we" know - // what type it is. - if (kiss_cfg) { - free(kiss_cfg); - } - --_count; -} - -float FFTDataReal::get(int index) const -{ - assert(index < (int)buffer.size()); - return buffer[index]; -} - - -void FFTDataReal::set(int index, float value) -{ - assert(index < (int)buffer.size()); - buffer[index] = value; -} +template <> +int FFTDataReal::_count = 0; \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/fft/FFTData.h b/plugins/community/repos/squinkylabs-plug1/dsp/fft/FFTData.h index 6db2c050..5b33fac6 100644 --- a/plugins/community/repos/squinkylabs-plug1/dsp/fft/FFTData.h +++ b/plugins/community/repos/squinkylabs-plug1/dsp/fft/FFTData.h @@ -2,6 +2,7 @@ #include #include +#include class FFT; @@ -13,22 +14,34 @@ class FFT; */ using cpx = std::complex; -class FFTDataCpx +template +class FFTData { public: friend FFT; - FFTDataCpx(int numBins); - ~FFTDataCpx(); - cpx get(int bin) const; - void set(int bin, cpx value); + FFTData(int numBins); + ~FFTData(); + T get(int bin) const; + void set(int bin, T value); int size() const { return (int) buffer.size(); } + + T * data() + { + return buffer.data(); + } + + float getAbs(int bin) const + { + return std::abs(buffer[bin]); + } + static int _count; private: - std::vector buffer; + std::vector buffer; /** * we store this without type so that clients don't need @@ -40,31 +53,39 @@ private: mutable void * kiss_cfg = 0; }; -/** - * Holds an fft frame of real data. - */ -class FFTDataReal +using FFTDataReal = FFTData; +using FFTDataCpx = FFTData; + +//int FFTDataCpx::_count = 0; + +template +inline FFTData::FFTData(int numBins) : + buffer(numBins) { -public: - friend FFT; - FFTDataReal(int numBins); - ~FFTDataReal(); - float get(int numBin) const; - void set(int numBin, float value); - int size() const - { - return (int) buffer.size(); + ++_count; +} + +template +inline FFTData::~FFTData() +{ + // We need to manually delete the cfg, since only "we" know + // what type it is. + if (kiss_cfg) { + free(kiss_cfg); } - static int _count; -private: - std::vector buffer; + --_count; +} - /** - * we store this without type so that clients don't need - * to pull in the kiss_fft headers. It's mutable so it can - * be lazy created by FFT functions. - * Note that the cfg has a "direction" baked into it. For - * now we assume that all FFT with real input will be forward FFTs. - */ - mutable void * kiss_cfg = 0; -}; \ No newline at end of file +template +inline T FFTData::get(int index) const +{ + assert(index < (int) buffer.size() && index >= 0); + return buffer[index]; +} + +template +inline void FFTData::set(int index, T value) +{ + assert(index < (int) buffer.size() && index >= 0); + buffer[index] = value; +} diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/filters/BiquadParams.h b/plugins/community/repos/squinkylabs-plug1/dsp/filters/BiquadParams.h index 3fa84dab..c50946c5 100644 --- a/plugins/community/repos/squinkylabs-plug1/dsp/filters/BiquadParams.h +++ b/plugins/community/repos/squinkylabs-plug1/dsp/filters/BiquadParams.h @@ -34,6 +34,13 @@ public: T A2(int stage) const; void dump() const; + + /** + * Direct access for the rare client that + * wants to see taps as a straight array. + */ + T getAtIndex(int index); + void setAtIndex(T x, int index); private: T _taps[5 * N]; }; @@ -88,50 +95,64 @@ inline T& BiquadParams::B2(int stage) } template -T BiquadParams::A1(int stage) const +inline T BiquadParams::A1(int stage) const { assert(stage >= 0 && stage < N); return _taps[stage * 5 + 3]; } template -T BiquadParams::A2(int stage) const +inline T BiquadParams::A2(int stage) const { assert(stage >= 0 && stage < N); return _taps[stage * 5 + 4]; } template -T BiquadParams::B0(int stage) const +inline T BiquadParams::B0(int stage) const { assert(stage >= 0 && stage < N); return _taps[stage * 5]; } template -T BiquadParams::B1(int stage) const +inline T BiquadParams::B1(int stage) const { assert(stage >= 0 && stage < N); return _taps[stage * 5 + 1]; } template -T BiquadParams::B2(int stage) const +inline T BiquadParams::B2(int stage) const { assert(stage >= 0 && stage < N); return _taps[stage * 5 + 2]; } template -T& BiquadParams::A1(int stage) +inline T& BiquadParams::A1(int stage) { assert(stage >= 0 && stage < N); return _taps[stage * 5 + 3]; } template -T& BiquadParams::A2(int stage) +inline T& BiquadParams::A2(int stage) { assert(stage >= 0 && stage < N); return _taps[stage * 5 + 4]; +} + +template +inline T BiquadParams::getAtIndex(int index) +{ + assert(index < N * 5); + return _taps[index]; +} + +template +inline void BiquadParams::setAtIndex(T x, int index) +{ + assert(index < N * 5); + _taps[index] = x; } \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/filters/BiquadState.h b/plugins/community/repos/squinkylabs-plug1/dsp/filters/BiquadState.h index 9ab8e217..87f543fc 100644 --- a/plugins/community/repos/squinkylabs-plug1/dsp/filters/BiquadState.h +++ b/plugins/community/repos/squinkylabs-plug1/dsp/filters/BiquadState.h @@ -1,6 +1,8 @@ #pragma once +extern int _numBiquads; + /** * Structure to hold the mutable state of a biquad filter: in this case the delay memory. * diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/filters/ButterworthFilterDesigner.cpp b/plugins/community/repos/squinkylabs-plug1/dsp/filters/ButterworthFilterDesigner.cpp index 2064a8b0..4cfa0d21 100644 --- a/plugins/community/repos/squinkylabs-plug1/dsp/filters/ButterworthFilterDesigner.cpp +++ b/plugins/community/repos/squinkylabs-plug1/dsp/filters/ButterworthFilterDesigner.cpp @@ -8,6 +8,37 @@ #include "BiquadFilter.h" #include + +template +void ButterworthFilterDesigner::designEightPoleLowpass(BiquadParams& outParams, T frequency) +{ + using Filter = Dsp::ButterLowPass<8, 1>; + std::unique_ptr lp(new Filter()); // std::make_unique is not until C++14 + lp->SetupAs(frequency); + assert(lp->GetStageCount() == 4); + BiquadFilter::fillFromStages(outParams, lp->Stages(), lp->GetStageCount()); +} + +template +void ButterworthFilterDesigner::designSixPoleLowpass(BiquadParams& outParams, T frequency) +{ + using Filter = Dsp::ButterLowPass<6, 1>; + std::unique_ptr lp6(new Filter()); // std::make_unique is not until C++14 + lp6->SetupAs(frequency); + assert(lp6->GetStageCount() == 3); + BiquadFilter::fillFromStages(outParams, lp6->Stages(), lp6->GetStageCount()); +} + +template +void ButterworthFilterDesigner::designFivePoleLowpass(BiquadParams& outParams, T frequency) +{ + using Filter = Dsp::ButterLowPass<5, 1>; + std::unique_ptr lp5(new Filter()); // std::make_unique is not until C++14 + lp5->SetupAs(frequency); + assert(lp5->GetStageCount() == 3); + BiquadFilter::fillFromStages(outParams, lp5->Stages(), lp5->GetStageCount()); +} + template void ButterworthFilterDesigner::designThreePoleLowpass(BiquadParams& outParams, T frequency) { @@ -18,6 +49,25 @@ void ButterworthFilterDesigner::designThreePoleLowpass(BiquadParams& ou BiquadFilter::fillFromStages(outParams, lp3->Stages(), lp3->GetStageCount()); } +template +void ButterworthFilterDesigner::designFourPoleLowpass(BiquadParams& outParams, T frequency) +{ + using Filter = Dsp::ButterLowPass<4, 1>; + std::unique_ptr lp4(new Filter()); // std::make_unique is not until C++14 + lp4->SetupAs(frequency); + assert(lp4->GetStageCount() == 2); + BiquadFilter::fillFromStages(outParams, lp4->Stages(), lp4->GetStageCount()); +} + +template +void ButterworthFilterDesigner::designFourPoleHighpass(BiquadParams& outParams, T frequency) +{ + using Filter = Dsp::ButterHighPass<4, 1>; + std::unique_ptr lp4(new Filter()); // std::make_unique is not until C++14 + lp4->SetupAs(frequency); + assert(lp4->GetStageCount() == 2); + BiquadFilter::fillFromStages(outParams, lp4->Stages(), lp4->GetStageCount()); +} template void ButterworthFilterDesigner::designTwoPoleLowpass(BiquadParams& outParams, T frequency) { @@ -28,6 +78,32 @@ void ButterworthFilterDesigner::designTwoPoleLowpass(BiquadParams& outP BiquadFilter::fillFromStages(outParams, lp2->Stages(), lp2->GetStageCount()); } +template +void ButterworthFilterDesigner::designSixPoleElliptic(BiquadParams& outParams, T frequency, T rippleDb, T stopbandAttenDb) +{ + assert(stopbandAttenDb > 0); + using Filter = Dsp::EllipticLowPass<6, 1>; + std::unique_ptr ellip6(new Filter()); + // void SetupAs( CalcT cutoffFreq, CalcT passRippleDb, CalcT rollOff ) + ellip6->SetupAs(frequency, rippleDb, stopbandAttenDb); + assert(ellip6->GetStageCount() == 3); + + BiquadFilter::fillFromStages(outParams, ellip6->Stages(), ellip6->GetStageCount()); +} + +template +void ButterworthFilterDesigner::designEightPoleElliptic(BiquadParams& outParams, T frequency, T rippleDb, T stopbandAttenDb) +{ + assert(stopbandAttenDb > 0); + using Filter = Dsp::EllipticLowPass<8, 1>; + std::unique_ptr ellip8(new Filter()); + // void SetupAs( CalcT cutoffFreq, CalcT passRippleDb, CalcT rollOff ) + ellip8->SetupAs(frequency, rippleDb, stopbandAttenDb); + assert(ellip8->GetStageCount() == 4); + + BiquadFilter::fillFromStages(outParams, ellip8->Stages(), ellip8->GetStageCount()); +} + // Explicit instantiation, so we can put implementation into .cpp file // TODO: option to take out float version (if we don't need it) // Or put all in header diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/filters/ButterworthFilterDesigner.h b/plugins/community/repos/squinkylabs-plug1/dsp/filters/ButterworthFilterDesigner.h index 8f0ef436..a562d1c1 100644 --- a/plugins/community/repos/squinkylabs-plug1/dsp/filters/ButterworthFilterDesigner.h +++ b/plugins/community/repos/squinkylabs-plug1/dsp/filters/ButterworthFilterDesigner.h @@ -9,6 +9,14 @@ class ButterworthFilterDesigner { public: ButterworthFilterDesigner() = delete; // we are only static + static void designEightPoleLowpass(BiquadParams& pOut, T frequency); + static void designSixPoleLowpass(BiquadParams& pOut, T frequency); static void designThreePoleLowpass(BiquadParams& pOut, T frequency); + static void designFourPoleLowpass(BiquadParams& pOut, T frequency); + static void designFourPoleHighpass(BiquadParams& pOut, T frequency); + static void designFivePoleLowpass(BiquadParams& pOut, T frequency); static void designTwoPoleLowpass(BiquadParams& pOut, T frequency); + + static void designSixPoleElliptic(BiquadParams& pOut, T frequency, T rippleDb, T stopbandAttenDb); + static void designEightPoleElliptic(BiquadParams& pOut, T frequency, T rippleDb, T stopbandAttenDb); }; \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/filters/ButterworthLookup.h b/plugins/community/repos/squinkylabs-plug1/dsp/filters/ButterworthLookup.h new file mode 100644 index 00000000..f3b984cd --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/dsp/filters/ButterworthLookup.h @@ -0,0 +1,41 @@ +#pragma once + +#include "BiquadParams.h" +#include "ButterworthFilterDesigner.h" +#include "LookupTable.h" + +/** +* Interpolating lookup for filter parameters +*/ +class ButterworthLookup4PHP +{ +public: + ButterworthLookup4PHP(); + void get(BiquadParams& params, float normalizedCutoff); +private: + static const int numTables = 10; + LookupTableParams tables[numTables]; // five params per two biquads +}; + +inline ButterworthLookup4PHP::ButterworthLookup4PHP() +{ + const int numBins = 256; + for (int index = 0; index < numTables; ++index) { + LookupTable::init(tables[index], numBins, 100.0f / 44100.0f, 2000 / 44100.0f, [index](double x) { + // first design a filter at x hz + BiquadParams params; + ButterworthFilterDesigner::designFourPoleHighpass(params, float(x)); + // then save off tap 0; + return params.getAtIndex(index); + }); + } +} + +inline void ButterworthLookup4PHP::get(BiquadParams& params, float normalizedCutoff) +{ + for (int i = 0; i < numTables; ++i) { + // const int stage = i < numTables / 2; + float p = LookupTable::lookup(tables[i], normalizedCutoff, true); + params.setAtIndex(p, i); + } +} \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/filters/GraphicEq.h b/plugins/community/repos/squinkylabs-plug1/dsp/filters/GraphicEq.h new file mode 100644 index 00000000..1c73e677 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/dsp/filters/GraphicEq.h @@ -0,0 +1,134 @@ +#pragma once + +#include "StateVariableFilter.h" + +// Unfinished single stage eq +class GraphicEq +{ +public: + + GraphicEq(int stages, float bw); + + float run(float); + void setGain(int stage, float g) + { + gain[stage] = g; + // printf("just set gain[%d] to %f\n", stage, g); + } +private: + StateVariableFilterParams params[6]; + StateVariableFilterState states[6]; + float gain[6]; + const int _stages; + +}; + +// todo: refactor +inline GraphicEq::GraphicEq(int stages, float bw) : _stages(stages) +{ + assert(stages < 6); + // .5, 1 stage is 78..128. 2stage 164 273 / + // .8 67..148 / 63..314 / 72..456 + const float baseFreq = 100.f / 44100.f; + float freq = baseFreq; + for (int i = 0; i < stages; ++i) { + params[i].setMode(StateVariableFilterParams::Mode::BandPass); + params[i].setFreq(freq); + params[i].setNormalizedBandwidth(bw); + freq *= 2.f; + gain[i] = 1; + + } +} + +inline float GraphicEq::run(float input) +{ + // printf("run filter with "); + float out = 0; + for (int i = 0; i < _stages; ++i) { + // printf("%f ", gain[i]); + out += StateVariableFilter::run(input, states[i], params[i]) * gain[i]; + } + // printf("\n"); + return out; +} + +/** + * Two bandpass filters in series. + */ +class TwoStageBandpass +{ +public: + TwoStageBandpass(); + float run(float); + void setFreq(float); +private: + StateVariableFilterParams params[2]; + StateVariableFilterState state[2]; +}; + +inline TwoStageBandpass::TwoStageBandpass() +{ + for (int i = 0; i <= 1; ++i) { + params[i].setMode(StateVariableFilterParams::Mode::BandPass); + params[i].setFreq(.1f); + params[i].setNormalizedBandwidth(1); + } +} + +inline void TwoStageBandpass::setFreq(float freq) +{ + for (int i = 0; i <= 1; ++i) { + params[i].setFreq(freq); + } +} + +inline float TwoStageBandpass::run(float input) +{ + auto y = StateVariableFilter::run(input, state[0], params[0]); + auto z = StateVariableFilter::run(y, state[1], params[1]); + return z; +} + +/** + * Octave EQ using dual bandpass sections + * Currently hard-wired to 100 Hz. + */ +template +class GraphicEq2 +{ +public: + GraphicEq2() + { + float freq = 100.0f / 44100.0f; + for (int i = 0; i < NumStages; ++i) { + filters[i].setFreq(freq); + freq *= 2.0f; + } + } + float run(float); + void setGain(int stage, float g) + { + assert(stage < NumStages); + gain[stage] = g; + + } + int getNumStages() + { + return NumStages; + } +private: + TwoStageBandpass filters[NumStages]; + float gain[NumStages]; +}; + +template +inline float GraphicEq2::run(float input) +{ + float out = 0; + for (int i = 0; i < NumStages; ++i) { + out += filters[i].run(input) * gain[i]; + } +; + return out; +} \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/filters/LowpassFilter.h b/plugins/community/repos/squinkylabs-plug1/dsp/filters/LowpassFilter.h new file mode 100644 index 00000000..7e3d6e1d --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/dsp/filters/LowpassFilter.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include "AudioMath.h" + +template +class LowpassFilterState +{ +public: + T z=0; +}; + +template +class LowpassFilterParams +{ +public: + T k=0; + T l=0; +}; + +template +class LowpassFilter +{ +public: + /** + * fs is normalize frequency + */ + static void setCutoff(LowpassFilterParams& params, T fs); + + static T run(T input, LowpassFilterState& state, const LowpassFilterParams& params); +}; + +template +inline void LowpassFilter::setCutoff(LowpassFilterParams& params, T fs) +{ + assert(fs > 00 && fs < .5); + params.k = T(1.0 - (std::exp(-2.0 * AudioMath::Pi * fs))); + params.l = T(1.0 - params.k); +} + + +/* +void go_dbl(double x) +{ +_z = _z * _l + _k * x; +} +*/ +template +inline T LowpassFilter::run(T input, LowpassFilterState& state, const LowpassFilterParams& params) +{ + state.z = state.z * params.l + params.k * input; + return state.z; +} \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/filters/StateVariableFilter.h b/plugins/community/repos/squinkylabs-plug1/dsp/filters/StateVariableFilter.h index a9a61109..924fae52 100644 --- a/plugins/community/repos/squinkylabs-plug1/dsp/filters/StateVariableFilter.h +++ b/plugins/community/repos/squinkylabs-plug1/dsp/filters/StateVariableFilter.h @@ -111,6 +111,7 @@ public: * units are 1 == sample rate */ void setFreq(T f); + void setFreqAccurate(T f); void setMode(Mode m) { mode = m; @@ -145,6 +146,12 @@ inline void StateVariableFilterParams::setFreq(T fc) fcGain = T(AudioMath::Pi) * T(2) * fc; } +template +inline void StateVariableFilterParams::setFreqAccurate(T fc) +{ + fcGain = T(2) * std::sin( T(AudioMath::Pi) * fc); +} + /*******************************************************************************************/ template diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/generators/MinBLEPVCO.h b/plugins/community/repos/squinkylabs-plug1/dsp/generators/MinBLEPVCO.h new file mode 100644 index 00000000..0549e28d --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/dsp/generators/MinBLEPVCO.h @@ -0,0 +1,588 @@ +#pragma once + + +// Need to make this compile in MS tools for unit tests +#if defined(_MSC_VER) +#define __attribute__(x) + +#pragma warning (push) +#pragma warning ( disable: 4244 4267 ) +#endif + +#ifndef _CLAMP +#define _CLAMP +namespace std { + inline float clamp(float v, float lo, float hi) + { + assert(lo < hi); +#define sMIN(a,b) (((a)>(b))?(b):(a)) +#define sMAX(a,b) (((a)>(b))?(a):(b)) + //return std::min(hi, std::max(v, lo)); + return sMIN(hi, sMAX(v, lo)); +#undef sMIN +#undef sMAX + } +} +#endif + +//#include "math.hpp" + + +#include "dsp/minblep.hpp" +#include "dsp/filter.hpp" +#include "AudioMath.h" +#include "ObjectCache.h" + +#include + +// until c++17 +#ifndef _CLAMP +#define _CLAMP +namespace std { + inline float clamp(float v, float lo, float hi) + { + assert(lo < hi); + return std::min(hi, std::max(v, lo)); + } +} +#endif + +/* VCO core using MinBLEP to reduce aliasing. + * Originally based on Befaco EvenVCO + */ + +class MinBLEPVCO +{ +public: + friend class TestMB; + + /** + * ph is the "phase (-1..0)" + */ + using SyncCallback = std::function; + + MinBLEPVCO(); + enum class Waveform + { + Sin, Tri, Saw, Square, Even, END + }; + + void step(); + + void setNormalizedFreq(float f, float st) + { + normalizedFreq = std::clamp(f, 1e-6f, 0.5f); + sampleTime = st; + } + + void setWaveform(Waveform); + + float getOutput() const + { + return output; + } + + /** + * Send the sync waveform to VCO. + * usually called from outside. + */ + void onMasterSync(float phase); + void setSyncCallback(SyncCallback); + void setPulseWidth(float); + void setSyncEnabled(bool f) + { + syncEnabled = f; + } + +private: + + float output = 0; + Waveform waveform = Waveform::Saw; + + float phase = 0.0; + float normalizedFreq = 0; + float sampleTime = 0; + SyncCallback syncCallback = nullptr; + float tri = 0; + bool syncEnabled = false; + + bool gotSyncCallback = false; + float syncCallbackCrossing = 0; + + /** + * References to shared lookup tables. + * Destructor will free them automatically. + */ + + std::shared_ptr> sinLookup = {ObjectCache::getSinLookup()}; + + /** Whether we are past the pulse width already */ + bool halfPhase = false; + + int loopCounter = 0; // still used? + float pulseWidth = .5; + + + rack::MinBLEP<16> syncMinBLEP; + rack::MinBLEP<16> aMinBLEP; + rack::MinBLEP<16> bMinBLEP; + bool aIsNext = false; + rack::MinBLEP<16>* getNextMinBLEP(); + + + + /** + * Waveform generation helper + */ + void step_even(); + void step_saw(); + void step_sq(); + void step_sin(); + void step_tri(); + + /** + * input = phase, 0..1 + * output = sin(2pi * input) + */ + float sineLook(float input) const; + + float evenLook(float input) const; + + std::string name; + bool lastSq = false; + bool isSqHigh() const; +}; + +inline MinBLEPVCO::MinBLEPVCO() +{ + syncMinBLEP.minblep = rack::minblep_16_32; + syncMinBLEP.oversample = 32; + aMinBLEP.minblep = rack::minblep_16_32; + aMinBLEP.oversample = 32; + bMinBLEP.minblep = rack::minblep_16_32; + bMinBLEP.oversample = 32; +} + +inline rack::MinBLEP<16>* MinBLEPVCO::getNextMinBLEP() +{ + aIsNext = !aIsNext; + return aIsNext ? &aMinBLEP : &bMinBLEP; +} + +inline void MinBLEPVCO::setSyncCallback(SyncCallback cb) +{ + assert(!syncCallback); + syncCallback = cb; +} + +inline void MinBLEPVCO::setWaveform(Waveform wf) +{ + waveform = wf; +} + +inline void MinBLEPVCO::setPulseWidth(float pw) +{ + pulseWidth = pw; +} + +inline void MinBLEPVCO::step() +{ + // call the dedicated dispatch routines for the special case waveforms. + switch (waveform) { + case Waveform::Saw: + step_saw(); + break; + case Waveform::Square: + step_sq(); + break; + case Waveform::Sin: + step_sin(); + break; + case Waveform::Tri: + // Tri sync doesn't work -> use sin + if (syncEnabled) { + step_sin(); + } else { + step_tri(); + } + break; + case Waveform::Even: + step_even(); + break; + case Waveform::END: + output = 0; + break; // don't do anything if no outputs + default: + assert(false); + } +} + +// callback from master sync when it rolls over +inline void MinBLEPVCO::onMasterSync(float masterPhase) +{ + gotSyncCallback = true; + syncCallbackCrossing = masterPhase; +} + +inline void MinBLEPVCO::step_saw() +{ + phase += normalizedFreq; + const float predictedPhase = phase; + if (gotSyncCallback) { + const float excess = -syncCallbackCrossing * normalizedFreq; + // Figure out where our sub-sample phase should be after reset + // reset to zero + const float newPhase = excess; + phase = newPhase; + } + if (phase >= 1.0) { + phase -= 1.0; + } + + // see if we jumped + if (phase != predictedPhase) { + const float jump = phase - predictedPhase; + // printf("%s jump = %f\n", name.c_str(), jump); fflush(stdout); + if (gotSyncCallback) { + const float crossing = syncCallbackCrossing; + syncMinBLEP.jump(crossing, jump); + if (syncCallback) { + syncCallback(crossing); + } + } else { + // phase overflowed + const float crossing = -phase / normalizedFreq; + aMinBLEP.jump(crossing, jump); + if (syncCallback) { + syncCallback(crossing); + } + } + } + + float totalPhase = phase; + totalPhase += aMinBLEP.shift(); + totalPhase += syncMinBLEP.shift(); + float saw = -1.0 + 2.0 * totalPhase; + output = 5.0*saw; + + gotSyncCallback = false; +} + +inline bool MinBLEPVCO::isSqHigh() const +{ + return phase >= pulseWidth; +} + +inline void MinBLEPVCO::step_sq() +{ + bool phaseDidOverflow = false; + phase += normalizedFreq; + if (gotSyncCallback) { + const float excess = -syncCallbackCrossing * normalizedFreq; + // reset phase to near zero on sync + phase = excess; + } + if (phase > 1.0f) { + phase -= 1.0f; + phaseDidOverflow = true; + } + + // now examine for any pending edges, + // and if found apply minBLEP and + // send sync signal + bool newSq = isSqHigh(); + if (newSq != lastSq) { + lastSq = newSq; + const float jump = newSq ? 2 : -2; + if (gotSyncCallback) { + const float crossing = syncCallbackCrossing; + syncMinBLEP.jump(crossing, jump); + if (syncCallback) { + syncCallback(crossing); + } + } else if (phaseDidOverflow) { + const float crossing = -phase / normalizedFreq; + aMinBLEP.jump(crossing, jump); + if (syncCallback) { + syncCallback(crossing); + } + } else { + // crossed PW boundary + const float crossing = -(phase - pulseWidth) / normalizedFreq; + bMinBLEP.jump(crossing, jump); + } + } + + float square = newSq ? 1.0f : -1.0f; + square += aMinBLEP.shift(); + square += bMinBLEP.shift(); + square += syncMinBLEP.shift(); + + output = 5.0*square; + + gotSyncCallback = false; +} + +inline float MinBLEPVCO::sineLook(float input) const +{ + // want cosine, but only have sine lookup + float adjPhase = input + .25f; + if (adjPhase >= 1) { + adjPhase -= 1; + } + + return -LookupTable::lookup(*sinLookup, adjPhase, true); +} + +inline void MinBLEPVCO::step_sin() +{ + if (gotSyncCallback) { + gotSyncCallback = false; + + // All calculations based on slave sync discontinuity happening at + // the same sub-sample as the mater discontinuity. + + // First, figure out how much excess phase we are going to have after reset + const float excess = -syncCallbackCrossing * normalizedFreq; + + // Figure out where our sub-sample phase should be after reset + const float newPhase = .5 + excess; + + const float oldOutput = sineLook(phase); + const float newOutput = sineLook(newPhase); + const float jump = newOutput - oldOutput; + + syncMinBLEP.jump(syncCallbackCrossing, jump); + this->phase = newPhase; + // return; + } else { + + phase += normalizedFreq; + + // Reset phase if at end of cycle + if (phase >= 1.0) { + phase -= 1.0; + if (syncCallback) { + float crossing = -phase / normalizedFreq; + syncCallback(crossing); + } + } + } + + float sine = sineLook(phase); + sine += syncMinBLEP.shift(); + output = 5.0*sine; +} + +inline void MinBLEPVCO::step_tri() +{ + if (gotSyncCallback) { + gotSyncCallback = false; + + // All calculations based on slave sync discontinuity happening at + // the same sub-sample as the mater discontinuity. + + // First, figure out how much excess phase we are going to have after reset + const float excess = -syncCallbackCrossing * normalizedFreq; + + // Figure out where our sub-sample phase should be after reset + const float newPhase = .5 + excess; + const float jump = -2.f * (phase - newPhase); +#ifdef _LOG + printf("%s: got sync ph=%.2f nph=%.2f excess=%.2f send cross %.2f jump %.2f \n", name.c_str(), + phase, newPhase, + excess, + syncCallbackCrossing, jump); +#endif + syncMinBLEP.jump(syncCallbackCrossing, jump); + this->phase = newPhase; + return; + } + float oldPhase = phase; + phase += normalizedFreq; + + if (oldPhase < 0.5 && phase >= 0.5) { + const float crossing = -(phase - 0.5) / normalizedFreq; + aMinBLEP.jump(crossing, 2.0); + } + + // Reset phase if at end of cycle + if (phase >= 1.0) { + phase -= 1.0; + float crossing = -phase / normalizedFreq; + aMinBLEP.jump(crossing, -2.0); + halfPhase = false; + if (syncCallback) { + syncCallback(crossing); + } + } + + // Outputs + float triSquare = (phase < 0.5) ? -1.0 : 1.0; + triSquare += aMinBLEP.shift(); + triSquare += syncMinBLEP.shift(); + + // Integrate square for triangle + tri += 4.0 * triSquare * normalizedFreq; + tri *= (1.0 - 40.0 * sampleTime); + + // Set output + output = 5.0*tri; +} + + +inline float calcDoubleSaw(float phase) +{ + return (phase < 0.5) ? (-1.0 + 4.0*phase) : (-1.0 + 4.0*(phase - 0.5)); +} + + +inline float MinBLEPVCO::evenLook(float input) const +{ + float doubleSaw = calcDoubleSaw(input); + const float sine = sineLook(input); + const float even = 0.55 * (doubleSaw + 1.27 * sine); + return even; +} + +inline void MinBLEPVCO::step_even() +{ + float oldPhase = phase; + phase += normalizedFreq; + float syncJump = 0; + if (gotSyncCallback) { + + + // All calculations based on slave sync discontinuity happening at + // the same sub-sample as the mater discontinuity. + + // First, figure out how much excess phase we are going to have after reset + const float excess = -syncCallbackCrossing * normalizedFreq; + + // Figure out where our sub-sample phase should be after reset + const float newPhase = .5 + excess; + // const float jump = -2.f * (phase - newPhase); +#ifdef _LOG + printf("%s: got sync ph=%.2f nph=%.2f excess=%.2f send cross %.2f jump %.2f \n", name.c_str(), + phase, newPhase, + excess, + syncCallbackCrossing, jump); +#endif + // syncMinBLEP.jump(syncCallbackCrossing, jump); + syncJump = evenLook(newPhase) - evenLook(this->phase); + this->phase = newPhase; + } + + bool jump5 = false; + bool jump1 = false; + if (oldPhase < 0.5 && phase >= 0.5) { + jump5 = true; + } + + // Reset phase if at end of cycle + if (phase >= 1.0) { + phase -= 1.0; + jump1 = true; + } + + if (gotSyncCallback) { + const float crossing = syncCallbackCrossing; + + // FIXME!! + float jump = syncJump; + syncMinBLEP.jump(crossing, jump); + if (syncCallback) { + syncCallback(crossing); + } + + } else if (jump1) { + const float jump = -2; + float crossing = -phase / normalizedFreq; + aMinBLEP.jump(crossing, jump); + if (syncCallback) { + syncCallback(crossing); + } + + } else if (jump5) { + const float jump = -2; + const float crossing = -(phase - 0.5) / normalizedFreq; + aMinBLEP.jump(crossing, jump); + } + + + // note that non-sync minBLEP is added to double saw, + // but for sync it's added to even. + const float sine = sineLook(phase); + float doubleSaw = (phase < 0.5) ? (-1.0 + 4.0*phase) : (-1.0 + 4.0*(phase - 0.5)); + doubleSaw += aMinBLEP.shift(); + float even = 0.55 * (doubleSaw + 1.27 * sine); + even += syncMinBLEP.shift(); + + output = 5.0*even; + gotSyncCallback = false; +} +#if 0 // old way +inline void MinBLEPVCO::step_even() +{ + if (gotSyncCallback) { + gotSyncCallback = false; + + // All calculations based on slave sync discontinuity happening at + // the same sub-sample as the mater discontinuity. + + // First, figure out how much excess phase we are going to have after reset + const float excess = -syncCallbackCrossing * normalizedFreq; + + // Figure out where our sub-sample phase should be after reset + const float newPhase = .5 + excess; + const float jump = -2.f * (phase - newPhase); +#ifdef _LOG + printf("%s: got sync ph=%.2f nph=%.2f excess=%.2f send cross %.2f jump %.2f \n", name.c_str(), + phase, newPhase, + excess, + syncCallbackCrossing, jump); +#endif + syncMinBLEP.jump(syncCallbackCrossing, jump); + this->phase = newPhase; + return; + } + float oldPhase = phase; + phase += normalizedFreq; + + if (oldPhase < 0.5 && phase >= 0.5) { + float crossing = -(phase - 0.5) / normalizedFreq; + aMinBLEP.jump(crossing, -2.0); + } + + // Reset phase if at end of cycle + if (phase >= 1.0) { + phase -= 1.0; + float crossing = -phase / normalizedFreq; + aMinBLEP.jump(crossing, -2.0); + if (syncCallback) { + syncCallback(crossing); + } + } + + //sine = -cosf(2*AudioMath::Pi * phase); + // want cosine, but only have sine lookup + float adjPhase = phase + .25f; + if (adjPhase >= 1) { + adjPhase -= 1; + } + const float sine = -LookupTable::lookup(*sinLookup, adjPhase, true); + + float doubleSaw = (phase < 0.5) ? (-1.0 + 4.0*phase) : (-1.0 + 4.0*(phase - 0.5)); + doubleSaw += aMinBLEP.shift(); + doubleSaw += syncMinBLEP.shift(); + const float even = 0.55 * (doubleSaw + 1.27 * sine); + + //TBase::outputs[SINE_OUTPUT].value = 5.0*sine; + output = 5.0*even; +} +#endif + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif + diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/generators/SawOscillator.h b/plugins/community/repos/squinkylabs-plug1/dsp/generators/SawOscillator.h index 1290096c..5c253a9e 100644 --- a/plugins/community/repos/squinkylabs-plug1/dsp/generators/SawOscillator.h +++ b/plugins/community/repos/squinkylabs-plug1/dsp/generators/SawOscillator.h @@ -78,9 +78,9 @@ template inline void SawOscillator::setFrequency(SawOscillatorParams& params, T freq) { if (frequencyCanBeNegative) { - assert(freq >= -.5 && freq < .5); + assert(freq >= -.5 && freq <= .5); } else { - assert(freq >= 0 && freq < .5); + assert(freq >= 0 && freq <= .5); } params.phaseIncrement = freq; } diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/generators/SinOscillator.h b/plugins/community/repos/squinkylabs-plug1/dsp/generators/SinOscillator.h index 1b74b3f8..c9122270 100644 --- a/plugins/community/repos/squinkylabs-plug1/dsp/generators/SinOscillator.h +++ b/plugins/community/repos/squinkylabs-plug1/dsp/generators/SinOscillator.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "AudioMath.h" #include "LookupTable.h" #include "ObjectCache.h" @@ -30,25 +32,14 @@ public: template inline void SinOscillator::setFrequency(SinOscillatorParams& params, T frequency) { - - std::function f = AudioMath::makeFunc_Sin(); - - // TODO: figure out a better initialization strategy - // and a better strategy for table size - // with 4096 thd was -130 db. let's use less memory! - // if (!params.lookupParams.isValid()) { - // LookupTable::init(params.lookupParams, 256, 0, 1, f); - // } assert(params.lookupParams->isValid()); - - SawOscillator::setFrequency(params.sawParams, frequency); + SawOscillator::setFrequency(params.sawParams, frequency); } template inline T SinOscillator::run( SinOscillatorState& state, const SinOscillatorParams& params) { - const T temp = SawOscillator::runSaw(state.sawState, params.sawParams); const T ret = LookupTable::lookup(*params.lookupParams, temp); return ret; @@ -58,7 +49,6 @@ template inline void SinOscillator::runQuadrature( T& output, T& outputQuadrature, SinOscillatorState& state, const SinOscillatorParams& params) { - T saw, quadratureSaw; SawOscillator::runQuadrature(saw, quadratureSaw, state.sawState, params.sawParams); output = LookupTable::lookup(*params.lookupParams, saw); diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/third-party/src/EvenVCO.h b/plugins/community/repos/squinkylabs-plug1/dsp/third-party/src/EvenVCO.h new file mode 100644 index 00000000..4a82acf5 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/dsp/third-party/src/EvenVCO.h @@ -0,0 +1,483 @@ +/** + * This file contains a modified version of EvenVCO.cpp, from the + * Befaco repo. See LICENSE-dist.txt for full license info. + * + * This code has been modified extensively by Squinky Labs. Mainly modifications were: + * re-code hot-spots to lower CPU usage. + * Fix compiler warnings. + * Make it compile in Visual Studio + */ + +// Need to make this compile in MS tools for unit tests +#if defined(_MSC_VER) +#define __attribute__(x) + +#pragma warning (push) +#pragma warning ( disable: 4244 4267 ) +#endif + +#include "dsp/minblep.hpp" +#include "dsp/filter.hpp" +#include "AudioMath.h" +#include "ObjectCache.h" + + +using namespace rack; + +template +struct EvenVCO : TBase +{ + EvenVCO(struct Module * module); + EvenVCO(); + + enum ParamIds + { + OCTAVE_PARAM, + TUNE_PARAM, + PWM_PARAM, + NUM_PARAMS + }; + enum InputIds + { + PITCH1_INPUT, + PITCH2_INPUT, + FM_INPUT, + PWM_INPUT, + NUM_INPUTS + }; + enum OutputIds + { + TRI_OUTPUT, + SINE_OUTPUT, + EVEN_OUTPUT, + SAW_OUTPUT, + SQUARE_OUTPUT, + NUM_OUTPUTS + }; + enum LightIds + { + NUM_LIGHTS + }; + + float phase = 0.0; + float tri = 0.0; + + /** + * References to shared lookup tables. + * Destructor will free them automatically. + */ + std::shared_ptr> sinLookup; + std::function expLookup; + + /** Whether we are past the pulse width already */ + bool halfPhase = false; + + MinBLEP<16> triSquareMinBLEP; + MinBLEP<16> triMinBLEP; + MinBLEP<16> sineMinBLEP; + MinBLEP<16> doubleSawMinBLEP; + MinBLEP<16> sawMinBLEP; + MinBLEP<16> squareMinBLEP; + + void step() override; + void step_even(float deltaPhase); + void step_saw(float deltaPhase); + void step_sq(float deltaPhase); + void step_sin(float deltaPhase); + void step_tri(float deltaPhase); + void step_all(float deltaPhase); + void step_old(); + void initialize(); + void zeroOutputsExcept(int except); + int dispatcher = 0; + int loopCounter = 0; + + + /** + * To avoid scanning outputs for changes every sample, we + * save the state here. + */ + bool doSaw = false; + bool doEven = false; + bool doTri = false; + bool doSq = false; + bool doSin = false; + + /** + * Variables added purely to enable unit testing + */ + float _freq = 0; + float _testFreq = 0; +}; + +template +inline EvenVCO::EvenVCO() : TBase() +{ + initialize(); +} + +template +inline EvenVCO::EvenVCO(struct Module * module) : TBase(module) +{ + initialize(); +} + +template +inline void EvenVCO::initialize() +{ + triSquareMinBLEP.minblep = rack::minblep_16_32; + triSquareMinBLEP.oversample = 32; + triMinBLEP.minblep = minblep_16_32; + triMinBLEP.oversample = 32; + sineMinBLEP.minblep = minblep_16_32; + sineMinBLEP.oversample = 32; + doubleSawMinBLEP.minblep = minblep_16_32; + doubleSawMinBLEP.oversample = 32; + sawMinBLEP.minblep = minblep_16_32; + sawMinBLEP.oversample = 32; + squareMinBLEP.minblep = minblep_16_32; + squareMinBLEP.oversample = 32; + + sinLookup = ObjectCache::getSinLookup(); + expLookup = ObjectCache::getExp2Ex(); +} + +template +void EvenVCO::zeroOutputsExcept(int except) +{ + for (int i = 0; i < NUM_OUTPUTS; ++i) { + if (i != except) { + // if we do even, we do sin at same time + if ((i == SINE_OUTPUT) && (except == EVEN_OUTPUT)) { + } else { + TBase::outputs[i].value = 0; + } + } + } +} + +template +inline void EvenVCO::step_even(float deltaPhase) +{ + float oldPhase = phase; + phase += deltaPhase; + + if (oldPhase < 0.5 && phase >= 0.5) { + float crossing = -(phase - 0.5) / deltaPhase; + doubleSawMinBLEP.jump(crossing, -2.0); + } + + // Reset phase if at end of cycle + if (phase >= 1.0) { + phase -= 1.0; + float crossing = -phase / deltaPhase; + doubleSawMinBLEP.jump(crossing, -2.0); + } + + + //sine = -cosf(2*AudioMath::Pi * phase); + // want cosine, but only have sine lookup + float adjPhase = phase + .25f; + if (adjPhase >= 1) { + adjPhase -= 1; + } + const float sine = -LookupTable::lookup(*sinLookup, adjPhase, true); + + float doubleSaw = (phase < 0.5) ? (-1.0 + 4.0*phase) : (-1.0 + 4.0*(phase - 0.5)); + doubleSaw += doubleSawMinBLEP.shift(); + const float even = 0.55 * (doubleSaw + 1.27 * sine); + + TBase::outputs[SINE_OUTPUT].value = 5.0*sine; + TBase::outputs[EVEN_OUTPUT].value = 5.0*even; +} + +template +inline void EvenVCO::step_saw(float deltaPhase) +{ + phase += deltaPhase; + + // Reset phase if at end of cycle + if (phase >= 1.0) { + phase -= 1.0; + float crossing = -phase / deltaPhase; + + static float cMin = 100; + static float cMax = -100; + cMin = std::min(crossing, cMin); + cMax = std::max(crossing, cMax); + + // printf("sawJump ph=%.2f, delta=%.2f cross=%.2f (%.2f, %.2f)\n", phase, deltaPhase, crossing, cMin, cMax); + sawMinBLEP.jump(crossing, -2.0); + } + + float saw = -1.0 + 2.0*phase; + saw += sawMinBLEP.shift(); + TBase::outputs[SAW_OUTPUT].value = 5.0*saw; +} + +template +inline void EvenVCO::step_sin(float deltaPhase) +{ + phase += deltaPhase; + + // Reset phase if at end of cycle + if (phase >= 1.0) { + phase -= 1.0; + } + + // want cosine, but only have sine lookup + float adjPhase = phase + .25f; + if (adjPhase >= 1) { + adjPhase -= 1; + } + + const float sine = -LookupTable::lookup(*sinLookup, adjPhase, true); + TBase::outputs[SINE_OUTPUT].value = 5.0*sine; +} + + +template +inline void EvenVCO::step_tri(float deltaPhase) +{ + float oldPhase = phase; + phase += deltaPhase; + + if (oldPhase < 0.5 && phase >= 0.5) { + const float crossing = -(phase - 0.5) / deltaPhase; + triSquareMinBLEP.jump(crossing, 2.0); + } + + // Reset phase if at end of cycle + if (phase >= 1.0) { + phase -= 1.0; + float crossing = -phase / deltaPhase; + triSquareMinBLEP.jump(crossing, -2.0); + halfPhase = false; + } + + // Outputs + float triSquare = (phase < 0.5) ? -1.0 : 1.0; + triSquare += triSquareMinBLEP.shift(); + + // Integrate square for triangle + tri += 4.0 * triSquare * _freq * TBase::engineGetSampleTime(); + tri *= (1.0 - 40.0 * TBase::engineGetSampleTime()); + + // Set output + TBase::outputs[TRI_OUTPUT].value = 5.0*tri; +} + +template +inline void EvenVCO::step_sq(float deltaPhase) +{ + phase += deltaPhase; + + // Pulse width + float pw; + if (doSq) { + pw = TBase::params[PWM_PARAM].value + TBase::inputs[PWM_INPUT].value / 5.0; + const float minPw = 0.05f; + pw = rescale(clamp(pw, -1.0f, 1.0f), -1.0f, 1.0f, minPw, 1.0f - minPw); + + if (!halfPhase && phase >= pw) { + float crossing = -(phase - pw) / deltaPhase; + squareMinBLEP.jump(crossing, 2.0); + halfPhase = true; + } + } + + // Reset phase if at end of cycle + if (phase >= 1.0) { + phase -= 1.0; + float crossing = -phase / deltaPhase; + squareMinBLEP.jump(crossing, -2.0); + halfPhase = false; + } + + float square = (phase < pw) ? -1.0 : 1.0; + square += squareMinBLEP.shift(); + TBase::outputs[SQUARE_OUTPUT].value = 5.0*square; +} + +template +inline void EvenVCO::step() +{ + // We don't need to look for connected outputs every cycle. + // do it less often, and store results. + if (--loopCounter < 0) { + loopCounter = 16; + + doSaw = TBase::outputs[SAW_OUTPUT].active; + doEven = TBase::outputs[EVEN_OUTPUT].active; + doTri = TBase::outputs[TRI_OUTPUT].active; + doSq = TBase::outputs[SQUARE_OUTPUT].active; + doSin = TBase::outputs[SINE_OUTPUT].active; + + if (doSaw && !doEven && !doTri && !doSq && !doSin) { + dispatcher = SAW_OUTPUT; + zeroOutputsExcept(SAW_OUTPUT); + } else if (!doSaw && doEven && !doTri && !doSq) { + dispatcher = EVEN_OUTPUT; + zeroOutputsExcept(EVEN_OUTPUT); + } else if (!doSaw && !doEven && !doTri && !doSq && doSin) { + dispatcher = SINE_OUTPUT; + zeroOutputsExcept(SINE_OUTPUT); + } else if (!doSaw && !doEven && doTri && !doSq && !doSin) { + dispatcher = TRI_OUTPUT; + zeroOutputsExcept(TRI_OUTPUT); + } else if (!doSaw && !doEven && !doTri && doSq && !doSin) { + dispatcher = SQUARE_OUTPUT; + zeroOutputsExcept(SQUARE_OUTPUT); + } else { + dispatcher = NUM_OUTPUTS; + } + } + + // Compute frequency, pitch is 1V/oct + float pitch = 1.0 + roundf(TBase::params[OCTAVE_PARAM].value) + TBase::params[TUNE_PARAM].value / 12.0; + pitch += TBase::inputs[PITCH1_INPUT].value + TBase::inputs[PITCH2_INPUT].value; + pitch += TBase::inputs[FM_INPUT].value / 4.0; + +#if 1 // Use lookup table for pitch lookup + const float q = float(log2(261.626)); // move up to pitch range of EvenVCO + pitch += q; + _freq = expLookup(pitch); +#else + _freq = 261.626 * powf(2.0, pitch); + _freq = clamp(_freq, 0.0f, 20000.0f); +#endif + + // Advance phase + float f = (_testFreq) ? _testFreq : _freq; + float deltaPhase = clamp(f * TBase::engineGetSampleTime(), 1e-6f, 0.5f); + + // call the dedicated dispatch routines for the special case waveforms. + switch (dispatcher) { + case SAW_OUTPUT: + step_saw(deltaPhase); + break; + case EVEN_OUTPUT: + step_even(deltaPhase); + break; + case SINE_OUTPUT: + step_sin(deltaPhase); + break; + case TRI_OUTPUT: + step_tri(deltaPhase); + break; + case SQUARE_OUTPUT: + step_sq(deltaPhase); + break; + case NUM_OUTPUTS: + step_all(deltaPhase); + break; + default: + assert(false); + } +} + +/** + * Less optimized version that can do all waveform combinations + */ +template +inline void EvenVCO::step_all(float deltaPhase) +{ + float oldPhase = phase; + phase += deltaPhase; + + if (oldPhase < 0.5 && phase >= 0.5) { + const float crossing = -(phase - 0.5) / deltaPhase; + + if (doTri) { + triSquareMinBLEP.jump(crossing, 2.0); + } + if (doEven) { + doubleSawMinBLEP.jump(crossing, -2.0); + } + } + + // Pulse width + float pw; + if (doSq) { + pw = TBase::params[PWM_PARAM].value + TBase::inputs[PWM_INPUT].value / 5.0; + const float minPw = 0.05f; + pw = rescale(clamp(pw, -1.0f, 1.0f), -1.0f, 1.0f, minPw, 1.0f - minPw); + + if (!halfPhase && phase >= pw) { + const float crossing = -(phase - pw) / deltaPhase; + squareMinBLEP.jump(crossing, 2.0); + halfPhase = true; + } + } + + // Reset phase if at end of cycle + if (phase >= 1.0) { + phase -= 1.0; + float crossing = -phase / deltaPhase; + if (doTri) { + triSquareMinBLEP.jump(crossing, -2.0); + } + if (doEven) { + doubleSawMinBLEP.jump(crossing, -2.0); + } + if (doSq) { + squareMinBLEP.jump(crossing, -2.0); + } + if (doSaw) { + sawMinBLEP.jump(crossing, -2.0); + } + halfPhase = false; + } + + // Outputs + if (doTri) { + float triSquare = (phase < 0.5) ? -1.0 : 1.0; + triSquare += triSquareMinBLEP.shift(); + + // Integrate square for triangle + tri += 4.0 * triSquare * _freq * TBase::engineGetSampleTime(); + tri *= (1.0 - 40.0 * TBase::engineGetSampleTime()); + } + + float sine = 0; + float even = 0; + float saw = 0; + float square = 0; + if (doSin || doEven) { + //sine = -cosf(2*AudioMath::Pi * phase); + // want cosine, but only have sine lookup + float adjPhase = phase + .25f; + if (adjPhase >= 1) { + adjPhase -= 1; + } + sine = -LookupTable::lookup(*sinLookup, adjPhase, true); + } + if (doEven) { + float doubleSaw = (phase < 0.5) ? (-1.0 + 4.0*phase) : (-1.0 + 4.0*(phase - 0.5)); + doubleSaw += doubleSawMinBLEP.shift(); + even = 0.55 * (doubleSaw + 1.27 * sine); + } + if (doSaw) { + saw = -1.0 + 2.0*phase; + saw += sawMinBLEP.shift(); + } + if (doSq) { + square = (phase < pw) ? -1.0 : 1.0; + square += squareMinBLEP.shift(); + + } else { + TBase::outputs[SQUARE_OUTPUT].value = 0; + } + + // Set outputs + // get rid of redundant stuff here + TBase::outputs[TRI_OUTPUT].value = doTri ? 5.0*tri : 0; + TBase::outputs[SINE_OUTPUT].value = 5.0*sine; + TBase::outputs[EVEN_OUTPUT].value = 5.0*even; + TBase::outputs[SAW_OUTPUT].value = 5.0*saw; + TBase::outputs[SQUARE_OUTPUT].value = 5.0*square; +} + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/third-party/src/EvenVCO_orig.h b/plugins/community/repos/squinkylabs-plug1/dsp/third-party/src/EvenVCO_orig.h new file mode 100644 index 00000000..c913d2ab --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/dsp/third-party/src/EvenVCO_orig.h @@ -0,0 +1,195 @@ +/** + * This file is the original source for Befaco EvenVCO. It has been modified + * slightly to allow it to be compiles in the Squinky Labs test framework. + * + * This file is only use for before/after profiling of EvilVCO. + */ +//#include "Befaco.hpp" +#include "dsp/minblep.hpp" +#include "dsp/filter.hpp" + +#include "AudioMath.h" + +template +struct EvenVCO_orig : TBase +{ + EvenVCO_orig(struct Module * module); + EvenVCO_orig(); + + void initialize(); + + enum ParamIds { + OCTAVE_PARAM, + TUNE_PARAM, + PWM_PARAM, + NUM_PARAMS + }; + enum InputIds { + PITCH1_INPUT, + PITCH2_INPUT, + FM_INPUT, + SYNC_INPUT, + PWM_INPUT, + NUM_INPUTS + }; + enum OutputIds { + TRI_OUTPUT, + SINE_OUTPUT, + EVEN_OUTPUT, + SAW_OUTPUT, + SQUARE_OUTPUT, + NUM_OUTPUTS + }; + + float phase = 0.0; + /** The value of the last sync input */ + float sync = 0.0; + /** The outputs */ + float tri = 0.0; + /** Whether we are past the pulse width already */ + bool halfPhase = false; + + MinBLEP<16> triSquareMinBLEP; + MinBLEP<16> triMinBLEP; + MinBLEP<16> sineMinBLEP; + MinBLEP<16> doubleSawMinBLEP; + MinBLEP<16> sawMinBLEP; + MinBLEP<16> squareMinBLEP; + + RCFilter triFilter; + + + void step() override; + +}; + +template +inline EvenVCO_orig::EvenVCO_orig() : TBase() +{ + initialize(); +} + +template +inline EvenVCO_orig::EvenVCO_orig(struct Module * module) : TBase(module) +{ + initialize(); +} + + + +template +inline void EvenVCO_orig::initialize() +{ + triSquareMinBLEP.minblep = minblep_16_32; + triSquareMinBLEP.oversample = 32; + triMinBLEP.minblep = minblep_16_32; + triMinBLEP.oversample = 32; + sineMinBLEP.minblep = minblep_16_32; + sineMinBLEP.oversample = 32; + doubleSawMinBLEP.minblep = minblep_16_32; + doubleSawMinBLEP.oversample = 32; + sawMinBLEP.minblep = minblep_16_32; + sawMinBLEP.oversample = 32; + squareMinBLEP.minblep = minblep_16_32; + squareMinBLEP.oversample = 32; +} + +template +inline void EvenVCO_orig::step() { + // Compute frequency, pitch is 1V/oct + float pitch = 1.0 + roundf(TBase::params[OCTAVE_PARAM].value) + TBase::params[TUNE_PARAM].value / 12.0; + pitch += TBase::inputs[PITCH1_INPUT].value + TBase::inputs[PITCH2_INPUT].value; + pitch += TBase::inputs[FM_INPUT].value / 4.0; + float freq = 261.626 * powf(2.0, pitch); + freq = clamp(freq, 0.0f, 20000.0f); + + // Pulse width + float pw = TBase::params[PWM_PARAM].value + TBase::inputs[PWM_INPUT].value / 5.0; + const float minPw = 0.05; + pw = rescale(clamp(pw, -1.0f, 1.0f), -1.0f, 1.0f, minPw, 1.0f - minPw); + + // Advance phase + float deltaPhase = clamp(freq * TBase::engineGetSampleTime(), 1e-6f, 0.5f); + float oldPhase = phase; + phase += deltaPhase; + + if (oldPhase < 0.5 && phase >= 0.5) { + float crossing = -(phase - 0.5) / deltaPhase; + triSquareMinBLEP.jump(crossing, 2.0); + doubleSawMinBLEP.jump(crossing, -2.0); + } + + if (!halfPhase && phase >= pw) { + float crossing = -(phase - pw) / deltaPhase; + squareMinBLEP.jump(crossing, 2.0); + halfPhase = true; + } + + // Reset phase if at end of cycle + if (phase >= 1.0) { + phase -= 1.0; + float crossing = -phase / deltaPhase; + triSquareMinBLEP.jump(crossing, -2.0); + doubleSawMinBLEP.jump(crossing, -2.0); + squareMinBLEP.jump(crossing, -2.0); + sawMinBLEP.jump(crossing, -2.0); + halfPhase = false; + } + + // Outputs + float triSquare = (phase < 0.5) ? -1.0 : 1.0; + triSquare += triSquareMinBLEP.shift(); + + // Integrate square for triangle + tri += 4.0 * triSquare * freq * TBase::engineGetSampleTime(); + tri *= (1.0 - 40.0 * TBase::engineGetSampleTime()); + + float sine = -cosf(2*AudioMath::Pi * phase); + float doubleSaw = (phase < 0.5) ? (-1.0 + 4.0*phase) : (-1.0 + 4.0*(phase - 0.5)); + doubleSaw += doubleSawMinBLEP.shift(); + float even = 0.55 * (doubleSaw + 1.27 * sine); + float saw = -1.0 + 2.0*phase; + saw += sawMinBLEP.shift(); + float square = (phase < pw) ? -1.0 : 1.0; + square += squareMinBLEP.shift(); + + // Set outputs + TBase::outputs[TRI_OUTPUT].value = 5.0*tri; + TBase::outputs[SINE_OUTPUT].value = 5.0*sine; + TBase::outputs[EVEN_OUTPUT].value = 5.0*even; + TBase::outputs[SAW_OUTPUT].value = 5.0*saw; + TBase::outputs[SQUARE_OUTPUT].value = 5.0*square; +} + +#if 0 +struct EvenVCOWidget : ModuleWidget { + EvenVCOWidget(EvenVCO *module) : ModuleWidget(module) { + setPanel(SVG::load(assetPlugin(plugin, "res/EvenVCO.svg"))); + + addChild(Widget::create(Vec(15, 0))); + addChild(Widget::create(Vec(15, 365))); + addChild(Widget::create(Vec(15*6, 0))); + addChild(Widget::create(Vec(15*6, 365))); + + addParam(ParamWidget::create(Vec(22, 32), module, EvenVCO::OCTAVE_PARAM, -5.0, 4.0, 0.0)); + addParam(ParamWidget::create(Vec(73, 131), module, EvenVCO::TUNE_PARAM, -7.0, 7.0, 0.0)); + addParam(ParamWidget::create(Vec(16, 230), module, EvenVCO::PWM_PARAM, -1.0, 1.0, 0.0)); + + addInput(Port::create(Vec(8, 120), Port::INPUT, module, EvenVCO::PITCH1_INPUT)); + addInput(Port::create(Vec(19, 157), Port::INPUT, module, EvenVCO::PITCH2_INPUT)); + addInput(Port::create(Vec(48, 183), Port::INPUT, module, EvenVCO::FM_INPUT)); + addInput(Port::create(Vec(86, 189), Port::INPUT, module, EvenVCO::SYNC_INPUT)); + + addInput(Port::create(Vec(72, 236), Port::INPUT, module, EvenVCO::PWM_INPUT)); + + addOutput(Port::create(Vec(10, 283), Port::OUTPUT, module, EvenVCO::TRI_OUTPUT)); + addOutput(Port::create(Vec(87, 283), Port::OUTPUT, module, EvenVCO::SINE_OUTPUT)); + addOutput(Port::create(Vec(48, 306), Port::OUTPUT, module, EvenVCO::EVEN_OUTPUT)); + addOutput(Port::create(Vec(10, 327), Port::OUTPUT, module, EvenVCO::SAW_OUTPUT)); + addOutput(Port::create(Vec(87, 327), Port::OUTPUT, module, EvenVCO::SQUARE_OUTPUT)); + } +}; + + +Model *modelEvenVCO = Model::create("Befaco", "EvenVCO", "EvenVCO", OSCILLATOR_TAG); +#endif diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/third-party/src/FunVCO.h b/plugins/community/repos/squinkylabs-plug1/dsp/third-party/src/FunVCO.h new file mode 100644 index 00000000..4d5c975d --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/dsp/third-party/src/FunVCO.h @@ -0,0 +1,447 @@ +/** + * This is a modified version of the VCV Fundamental VCO. + * See LICENSE-dist.txt for full license info. + * This code has been modified extensively by Squinky Labs. Mainly modifications were: + * re-code hot-spots to lower CPU usage. + * Fix compiler warnings. + * Make it compile in Visual Studio + */ +#pragma once + +#if defined(_MSC_VER) +#pragma warning (push) +#pragma warning (disable: 4305 4244 4267) +#endif + +#if !defined(M_PI) +#define M_PI 3.14159265358979323846264338327950288 +#endif + +#include "dsp/functions.hpp" +#include "dsp/filter.hpp" +#include + +#include "BiquadFilter.h" +#include "BiquadParams.h" +#include "BiquadState.h" +#include "ButterworthFilterDesigner.h" +#include "ObjectCache.h" +#include "IIRDecimator.h" + +extern float sqsawTable[2048]; +extern float sqtriTable[2048]; + +// When this is defined, will use Squinky Labs anti-aliasing decimators, +// rather than rack::Decimator<> +#define _USEIIR + + + +template +struct VoltageControlledOscillator +{ + float sampleTime = 0; + bool analog = false; + bool soft = false; + float lastSyncValue = 0.0f; + float phase = 0.0f; + float freq; + float pw = 0.5f; + float pitch; + bool syncEnabled = false; + bool syncDirection = false; + + /** + * flags to help decide not to do redundant work + */ + bool sinEnabled = false; + bool sqEnabled = false; + bool sawEnabled = false; + bool triEnabled = false; + +#ifdef _USEIIR + IIRDecimator sinDecimator; + IIRDecimator triDecimator; + IIRDecimator sawDecimator; + IIRDecimator sqrDecimator; +#else + rack::Decimator sinDecimator; + rack::Decimator triDecimator; + rack::Decimator sawDecimator; + rack::Decimator sqrDecimator; +#endif + RCFilter sqrFilter; + + // For analog detuning effect + float pitchSlew = 0.0f; + int pitchSlewIndex = 0; + + float sinBuffer[OVERSAMPLE] = {}; + float triBuffer[OVERSAMPLE] = {}; + float sawBuffer[OVERSAMPLE] = {}; + float sqrBuffer[OVERSAMPLE] = {}; + + // Use interpolating lookups for these transcendentals + std::shared_ptr> sinLookup; + std::function expLookup; + + void init() + { + sinLookup = ObjectCache::getSinLookup(); + expLookup = ObjectCache::getExp2Ex(); + + // Set anti-alias 3-db down point an octave below nyquist: .25 + //float cutoff = .25f / float(OVERSAMPLE); + + sinDecimator.setup(16); + sinDecimator.setup(16); + sawDecimator.setup(16); + sqrDecimator.setup(16); + triDecimator.setup(16); + } + + // Use the standard c++ library for random generation + std::default_random_engine generator{99}; + std::normal_distribution distribution{0, 1.0}; + float noise() + { + return (float) distribution(generator); + } + + void setPitch(float pitchKnob, float pitchCv) + { + // Compute frequency + pitch = pitchKnob; + if (analog) { + // Apply pitch slew + const float pitchSlewAmount = 3.0f; + pitch += pitchSlew * pitchSlewAmount; + } else { + // Quantize coarse knob if digital mode + pitch = roundf(pitch); + } + pitch += pitchCv; + + // Note C4 + // freq = 261.626f * powf(2.0f, pitch / 12.0f); + const float q = float(log2(261.626)); // move up to pitch range up + pitch = (pitch / 12.0f) + q; + freq = expLookup(pitch); + } + + void setPulseWidth(float pulseWidth) + { + const float pwMin = 0.01f; + pw = clamp(pulseWidth, pwMin, 1.0f - pwMin); + } + + void process(float deltaTime, float syncValue) + { + assert(sinLookup); + assert(sampleTime > 0); + if (analog) { + // Adjust pitch slew + if (++pitchSlewIndex > 64) { + const float pitchSlewTau = 100.0f; // Time constant for leaky integrator in seconds + pitchSlew += (noise() - pitchSlew / pitchSlewTau) *sampleTime; + pitchSlewIndex = 0; + } + } + + // Advance phase + float deltaPhaseOver = clamp(freq * deltaTime, 1e-6, 0.5f) * (1.0f / OVERSAMPLE); + + // Detect sync + int syncIndex = -1; // Index in the oversample loop where sync occurs [0, OVERSAMPLE) + float syncCrossing = 0.0f; // Offset that sync occurs [0.0f, 1.0f) + if (syncEnabled) { + syncValue -= 0.01f; + if (syncValue > 0.0f && lastSyncValue <= 0.0f) { + float deltaSync = syncValue - lastSyncValue; + syncCrossing = 1.0f - syncValue / deltaSync; + syncCrossing *= OVERSAMPLE; + syncIndex = (int) syncCrossing; + syncCrossing -= syncIndex; + } + lastSyncValue = syncValue; + } + + if (syncDirection) + deltaPhaseOver *= -1.0f; + + if (sqEnabled) { + sqrFilter.setCutoff(40.0f * deltaTime); + } + + for (int i = 0; i < OVERSAMPLE; i++) { + if (syncIndex == i) { + if (soft) { + syncDirection = !syncDirection; + deltaPhaseOver *= -1.0f; + } else { + // phase = syncCrossing * deltaPhase / OVERSAMPLE; + phase = 0.0f; + } + } + + if (sinEnabled) { + if (analog) { + // Quadratic approximation of sine, slightly richer harmonics + if (phase < 0.5f) + sinBuffer[i] = 1.f - 16.f * powf(phase - 0.25f, 2); + else + sinBuffer[i] = -1.f + 16.f * powf(phase - 0.75f, 2); + sinBuffer[i] *= 1.08f; + } else { + // sinBuffer[i] = sinf(2.f*M_PI * phase); + sinBuffer[i] = LookupTable::lookup(*sinLookup, phase, true); + } + } + + if (triEnabled) { + if (analog) { + triBuffer[i] = 1.25f * interpolateLinear(sqtriTable, phase * 2047.f); + } else { + if (phase < 0.25f) + triBuffer[i] = 4.f * phase; + else if (phase < 0.75f) + triBuffer[i] = 2.f - 4.f * phase; + else + triBuffer[i] = -4.f + 4.f * phase; + } + } + + if (sawEnabled) { + if (analog) { + sawBuffer[i] = 1.66f * interpolateLinear(sqsawTable, phase * 2047.f); + } else { + if (phase < 0.5f) + sawBuffer[i] = 2.f * phase; + else + sawBuffer[i] = -2.f + 2.f * phase; + } + } + + if (sqEnabled) { + sqrBuffer[i] = (phase < pw) ? 1.f : -1.f; + if (analog) { + // Simply filter here + sqrFilter.process(sqrBuffer[i]); + sqrBuffer[i] = 0.71f * sqrFilter.highpass(); + } + } + + // don't divide by oversample every time. + // don't do that expensive mod + phase += deltaPhaseOver; + while (phase > 1.0f) { + phase -= 1.0f; + } + while (phase < 0) { + phase += 1.0f; + } + } + } + + float sin() + { + return sinDecimator.process(sinBuffer); + } + float tri() + { + return triDecimator.process(triBuffer); + } + float saw() + { + return sawDecimator.process(sawBuffer); + } + float sqr() + { + return sqrDecimator.process(sqrBuffer); + } +#if 0 + float light() + { + return sinf(2 * M_PI * phase); + } +#endif +}; + + +#if 0 // let's remove from regular builds +template +struct VoltageControlledOscillatorOrig +{ + float sampleTime = 0; + bool analog = false; + bool soft = false; + float lastSyncValue = 0.0f; + float phase = 0.0f; + float freq; + float pw = 0.5f; + float pitch; + bool syncEnabled = false; + bool syncDirection = false; + + rack::Decimator sinDecimator; + rack::Decimator triDecimator; + rack::Decimator sawDecimator; + rack::Decimator sqrDecimator; + RCFilter sqrFilter; + + // For analog detuning effect + float pitchSlew = 0.0f; + int pitchSlewIndex = 0; + + float sinBuffer[OVERSAMPLE] = {}; + float triBuffer[OVERSAMPLE] = {}; + float sawBuffer[OVERSAMPLE] = {}; + float sqrBuffer[OVERSAMPLE] = {}; + + std::default_random_engine generator{99}; + std::normal_distribution distribution{-1.0, 1.0}; + float noise() + { + return (float) distribution(generator); + } + + void setPitch(float pitchKnob, float pitchCv) + { + // Compute frequency + pitch = pitchKnob; + if (analog) { + // Apply pitch slew + const float pitchSlewAmount = 3.0f; + pitch += pitchSlew * pitchSlewAmount; + } else { + // Quantize coarse knob if digital mode + pitch = roundf(pitch); + } + pitch += pitchCv; + // Note C4 + freq = 261.626f * powf(2.0f, pitch / 12.0f); + } + void setPulseWidth(float pulseWidth) + { + const float pwMin = 0.01f; + pw = clamp(pulseWidth, pwMin, 1.0f - pwMin); + } + + void init() + { + } + + void process(float deltaTime, float syncValue) + { + assert(sampleTime > 0); + if (analog) { + // Adjust pitch slew + if (++pitchSlewIndex > 32) { + const float pitchSlewTau = 100.0f; // Time constant for leaky integrator in seconds + // pitchSlew += (randomNormal() - pitchSlew / pitchSlewTau) * sampleTime; + pitchSlew += (noise() - pitchSlew / pitchSlewTau) *sampleTime; + pitchSlewIndex = 0; + } + } + + // Advance phase + float deltaPhase = clamp(freq * deltaTime, 1e-6, 0.5f); + + // Detect sync + int syncIndex = -1; // Index in the oversample loop where sync occurs [0, OVERSAMPLE) + float syncCrossing = 0.0f; // Offset that sync occurs [0.0f, 1.0f) + if (syncEnabled) { + syncValue -= 0.01f; + if (syncValue > 0.0f && lastSyncValue <= 0.0f) { + float deltaSync = syncValue - lastSyncValue; + syncCrossing = 1.0f - syncValue / deltaSync; + syncCrossing *= OVERSAMPLE; + syncIndex = (int) syncCrossing; + syncCrossing -= syncIndex; + } + lastSyncValue = syncValue; + } + + if (syncDirection) + deltaPhase *= -1.0f; + + sqrFilter.setCutoff(40.0f * deltaTime); + + for (int i = 0; i < OVERSAMPLE; i++) { + if (syncIndex == i) { + if (soft) { + syncDirection = !syncDirection; + deltaPhase *= -1.0f; + } else { + // phase = syncCrossing * deltaPhase / OVERSAMPLE; + phase = 0.0f; + } + } + + if (analog) { + // Quadratic approximation of sine, slightly richer harmonics + if (phase < 0.5f) + sinBuffer[i] = 1.f - 16.f * powf(phase - 0.25f, 2); + else + sinBuffer[i] = -1.f + 16.f * powf(phase - 0.75f, 2); + sinBuffer[i] *= 1.08f; + } else { + sinBuffer[i] = sinf(2.f*M_PI * phase); + } + if (analog) { + triBuffer[i] = 1.25f * interpolateLinear(sqtriTable, phase * 2047.f); + } else { + if (phase < 0.25f) + triBuffer[i] = 4.f * phase; + else if (phase < 0.75f) + triBuffer[i] = 2.f - 4.f * phase; + else + triBuffer[i] = -4.f + 4.f * phase; + } + if (analog) { + sawBuffer[i] = 1.66f * interpolateLinear(sqsawTable, phase * 2047.f); + } else { + if (phase < 0.5f) + sawBuffer[i] = 2.f * phase; + else + sawBuffer[i] = -2.f + 2.f * phase; + } + sqrBuffer[i] = (phase < pw) ? 1.f : -1.f; + if (analog) { + // Simply filter here + sqrFilter.process(sqrBuffer[i]); + sqrBuffer[i] = 0.71f * sqrFilter.highpass(); + } + + // Advance phase + phase += deltaPhase / OVERSAMPLE; + phase = eucmod(phase, 1.0f); + } + } + + float sin() + { + return sinDecimator.process(sinBuffer); + } + float tri() + { + return triDecimator.process(triBuffer); + } + float saw() + { + return sawDecimator.process(sawBuffer); + } + float sqr() + { + return sqrDecimator.process(sqrBuffer); + } + float light() + { + return sinf(2 * M_PI * phase); + } +}; +#endif + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/third-party/src/minblep.cpp b/plugins/community/repos/squinkylabs-plug1/dsp/third-party/src/minblep.cpp new file mode 100644 index 00000000..91f60ba0 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/dsp/third-party/src/minblep.cpp @@ -0,0 +1,26 @@ +/** + * This is the minblep.cpp from VCVRack. + * Moved here to make it easier to build. + * TODO: don't do this - build from normal folder. + */ + +// Need to make this compile in MS tools for unit tests +#if defined(_MSC_VER) +#define __attribute__(x) + +#pragma warning(disable: 4244 4838 4305) +#endif + +#include "dsp/minblep.hpp" + + +namespace rack { + + +// TODO I should probably compute this on launch +const float minblep_16_32[] = { + 0.00011023, 0.00022416, 0.00034297, 0.00046661, 0.00059353, 0.00072403, 0.00085807, 0.00099544, 0.00113669, 0.00128244, 0.00143357, 0.00159180, 0.00175889, 0.00193681, 0.00212798, 0.00233618, 0.00256460, 0.00281804, 0.00310111, 0.00341981, 0.00378001, 0.00418855, 0.00465331, 0.00518249, 0.00578578, 0.00647247, 0.00725390, 0.00814134, 0.00914690, 0.01028436, 0.01156735, 0.01301055, 0.01462957, 0.01644073, 0.01846137, 0.02070885, 0.02320160, 0.02595890, 0.02900020, 0.03234551, 0.03601537, 0.04003051, 0.04441243, 0.04918219, 0.05436156, 0.05997159, 0.06603400, 0.07256979, 0.07960004, 0.08714473, 0.09522393, 0.10385663, 0.11306075, 0.12285375, 0.13325208, 0.14427035, 0.15592176, 0.16821821, 0.18117011, 0.19478568, 0.20907153, 0.22403158, 0.23966792, 0.25598019, 0.27296567, 0.29061884, 0.30893153, 0.32789305, 0.34748974, 0.36770493, 0.38851914, 0.40990984, 0.43185139, 0.45431516, 0.47726974, 0.50068015, 0.52450907, 0.54871643, 0.57325858, 0.59809023, 0.62316221, 0.64842403, 0.67382252, 0.69930243, 0.72480643, 0.75027585, 0.77565008, 0.80086774, 0.82586610, 0.85058230, 0.87495226, 0.89891225, 0.92239881, 0.94534874, 0.96769959, 0.98939002, 1.01036096, 1.03055346, 1.04991257, 1.06838441, 1.08591819, 1.10246634, 1.11798477, 1.13243258, 1.14577281, 1.15797329, 1.16900539, 1.17884552, 1.18747461, 1.19487906, 1.20105016, 1.20598388, 1.20968246, 1.21215260, 1.21340692, 1.21346366, 1.21234596, 1.21008229, 1.20670640, 1.20225751, 1.19677913, 1.19032025, 1.18293369, 1.17467666, 1.16561043, 1.15579987, 1.14531350, 1.13422263, 1.12260056, 1.11052382, 1.09806967, 1.08531761, 1.07234740, 1.05923963, 1.04607463, 1.03293216, 1.01989150, 1.00703001, 0.99442369, 0.98214602, 0.97026730, 0.95885533, 0.94797397, 0.93768352, 0.92803955, 0.91909295, 0.91089052, 0.90347338, 0.89687651, 0.89113057, 0.88625956, 0.88228178, 0.87920940, 0.87704903, 0.87580091, 0.87545884, 0.87601125, 0.87744081, 0.87972385, 0.88283205, 0.88673097, 0.89138156, 0.89674008, 0.90275854, 0.90938461, 0.91656262, 0.92423326, 0.93233514, 0.94080383, 0.94957352, 0.95857650, 0.96774524, 0.97700989, 0.98630202, 0.99555433, 1.00469887, 1.01367104, 1.02240634, 1.03084445, 1.03892660, 1.04659796, 1.05380774, 1.06050789, 1.06665611, 1.07221341, 1.07714641, 1.08142638, 1.08503044, 1.08793986, 1.09014225, 1.09163105, 1.09240448, 1.09246624, 1.09182608, 1.09049749, 1.08850145, 1.08586180, 1.08260751, 1.07877254, 1.07439494, 1.06951582, 1.06418049, 1.05843639, 1.05233502, 1.04592884, 1.03927267, 1.03242254, 1.02543569, 1.01836896, 1.01127994, 1.00422585, 0.99726236, 0.99044448, 0.98382431, 0.97745323, 0.97137886, 0.96564668, 0.96029860, 0.95537275, 0.95090336, 0.94692129, 0.94345307, 0.94051903, 0.93813735, 0.93632013, 0.93507481, 0.93440408, 0.93430620, 0.93477470, 0.93579876, 0.93736202, 0.93944514, 0.94202399, 0.94507092, 0.94855475, 0.95244062, 0.95669132, 0.96126628, 0.96612370, 0.97121888, 0.97650659, 0.98193920, 0.98746979, 0.99305087, 0.99863440, 1.00417352, 1.00962198, 1.01493454, 1.02006841, 1.02498233, 1.02963722, 1.03399694, 1.03802824, 1.04170084, 1.04498816, 1.04786694, 1.05031765, 1.05232465, 1.05387664, 1.05496621, 1.05558980, 1.05574775, 1.05544484, 1.05468917, 1.05349278, 1.05187225, 1.04984665, 1.04743862, 1.04467380, 1.04158032, 1.03818953, 1.03453457, 1.03065002, 1.02657270, 1.02234089, 1.01799285, 1.01356828, 1.00910676, 1.00464690, 1.00022852, 0.99588913, 0.99166596, 0.98759449, 0.98370826, 0.98003900, 0.97661632, 0.97346693, 0.97061497, 0.96808159, 0.96588528, 0.96404094, 0.96256018, 0.96145141, 0.96072024, 0.96036756, 0.96039182, 0.96078801, 0.96154761, 0.96265966, 0.96410930, 0.96587980, 0.96795046, 0.97029966, 0.97290313, 0.97573400, 0.97876412, 0.98196387, 0.98530257, 0.98874873, 0.99227041, 0.99583501, 0.99940956, 1.00296247, 1.00646222, 1.00987804, 1.01318014, 1.01634085, 1.01933396, 1.02213407, 1.02471924, 1.02706873, 1.02916455, 1.03099120, 1.03253567, 1.03378832, 1.03474081, 1.03538895, 1.03573155, 1.03576887, 1.03550470, 1.03494596, 1.03410137, 1.03298199, 1.03160226, 1.02997768, 1.02812684, 1.02606928, 1.02382588, 1.02142048, 1.01887643, 1.01621914, 1.01347411, 1.01066744, 1.00782573, 1.00497484, 1.00214148, 0.99935061, 0.99662775, 0.99399650, 0.99147928, 0.98909825, 0.98687279, 0.98482132, 0.98295969, 0.98130250, 0.97986293, 0.97865003, 0.97767186, 0.97693449, 0.97644144, 0.97619355, 0.97618985, 0.97642678, 0.97689945, 0.97759926, 0.97851688, 0.97964054, 0.98095751, 0.98245293, 0.98410982, 0.98591143, 0.98783875, 0.98987180, 0.99199086, 0.99417531, 0.99640393, 0.99865520, 1.00090814, 1.00314200, 1.00533652, 1.00747168, 1.00952899, 1.01149011, 1.01333869, 1.01505876, 1.01663721, 1.01806092, 1.01931858, 1.02040136, 1.02130210, 1.02201474, 1.02253604, 1.02286315, 1.02299595, 1.02293634, 1.02268875, 1.02225709, 1.02164853, 1.02087140, 1.01993537, 1.01885104, 1.01763213, 1.01629102, 1.01484251, 1.01330113, 1.01168346, 1.01000535, 1.00828362, 1.00653470, 1.00477552, 1.00302279, 1.00129271, 0.99960083, 0.99796313, 0.99639338, 0.99490535, 0.99351192, 0.99222440, 0.99105316, 0.99000835, 0.98909634, 0.98832458, 0.98769748, 0.98721880, 0.98689038, 0.98671335, 0.98668671, 0.98680866, 0.98707467, 0.98748106, 0.98802143, 0.98868805, 0.98947275, 0.99036646, 0.99135911, 0.99243897, 0.99359548, 0.99481559, 0.99608690, 0.99739677, 0.99873203, 1.00007975, 1.00142658, 1.00276041, 1.00406802, 1.00533855, 1.00655985, 1.00772119, 1.00881267, 1.00982487, 1.01075006, 1.01158035, 1.01230979, 1.01293266, 1.01344562, 1.01384568, 1.01412964, 1.01429844, 1.01435149, 1.01429081, 1.01411796, 1.01383793, 1.01345396, 1.01297212, 1.01239896, 1.01174057, 1.01100540, 1.01020169, 1.00933838, 1.00842452, 1.00746989, 1.00648463, 1.00547814, 1.00446069, 1.00344217, 1.00243223, 1.00143993, 1.00047517, 0.99954665, 0.99866235, 0.99782991, 0.99705631, 0.99634796, 0.99571103, 0.99514896, 0.99466711, 0.99426842, 0.99395454, 0.99372780, 0.99358785, 0.99353504, 0.99356812, 0.99368507, 0.99388355, 0.99415958, 0.99450946, 0.99492884, 0.99541146, 0.99595195, 0.99654400, 0.99718058, 0.99785507, 0.99856061, 0.99928933, 1.00003409, 1.00078654, 1.00154030, 1.00228751, 1.00302088, 1.00373411, 1.00442064, 1.00507402, 1.00568807, 1.00625861, 1.00678062, 1.00724947, 1.00766242, 1.00801611, 1.00830781, 1.00853622, 1.00870037, 1.00880003, 1.00883472, 1.00880599, 1.00871444, 1.00856256, 1.00835276, 1.00808787, 1.00777161, 1.00740683, 1.00699902, 1.00655174, 1.00606918, 1.00555754, 1.00502074, 1.00446463, 1.00389385, 1.00331378, 1.00272894, 1.00214446, 1.00156450, 1.00099480, 1.00043809, 0.99989861, 0.99938035, 0.99888563, 0.99841738, 0.99797773, 0.99756885, 0.99719167, 0.99684638, 0.99653435, 0.99625492, 0.99600822, 0.99579257, 0.99560666, 0.99544859, 0.99531645, 0.99520802, 0.99512035, 0.99504977, 0.99499327, 0.99494720, 0.99490863, 0.99487358, 0.99483842, 0.99479860, 0.99475139, 0.99469274, 0.99461985, 0.99452943, 0.99441826, 0.99428391, 0.99412394, 0.99393618, 0.99371916, 0.99347186, 0.99319351, 0.99288350, 0.99254173, 0.99216926, 0.99176681, 0.99133593, 0.99087846, 0.99039710, 0.98989451, 0.98937410, 0.98883879, 0.98829341, 0.98774183, 0.98718870, 0.98663867, 0.98609704, 0.98556888, 0.98505920, 0.98457366, 0.98411739, 0.98369551, 0.98331392, 0.98297703, 0.98268992, 0.98245704, 0.98228276, 0.98217118, 0.98212564, 0.98214996, 0.98224646, 0.98241693, 0.98266387, 0.98298812, 0.98339027, 0.98387039, 0.98442757, 0.98506147, 0.98577005, 0.98655069, 0.98740059, 0.98831660, 0.98929417, 0.99032903, 0.99141610, 0.99254966, 0.99372399, 0.99493235, 0.99616790, 0.99742448, 0.99869454, 0.99997079, 1.00124514, 1.00251019, 1.00375855, 1.00498211, 1.00617325, 1.00732577, 1.00843060, 1.00948179, 1.01047242, 1.01139629, 1.01224756, 1.01302052, 1.01371098, 1.01431394, 1.01482534, 1.01524329, 1.01556420, 1.01578641, 1.01590872, 1.01593041, 1.01585221, 1.01567483, 1.01539934, 1.01502872, 1.01456475, 1.01401234, 1.01337564, 1.01265824, 1.01186681, 1.01100624, 1.01008308, 1.00910449, 1.00807726, 1.00700867, 1.00590730, 1.00478077, 1.00363719, 1.00248516, 1.00133204, 1.00018704, 0.99905777, 0.99795246, 0.99687880, 0.99584419, 0.99485654, 0.99392235, 0.99304855, 0.99224019, 0.99150354, 0.99084306, 0.99026269, 0.98976678, 0.98935860, 0.98903960, 0.98881179, 0.98867643, 0.98863405, 0.98868364, 0.98882431, 0.98905468, 0.98937207, 0.98977262, 0.99025381, 0.99081081, 0.99143785, 0.99213088, 0.99288279, 0.99368858, 0.99454105, 0.99543440, 0.99635935, 0.99730903, 0.99827605, 0.99925363, 1.00023246, 1.00120509, 1.00216508, 1.00310433, 1.00401509, 1.00489199, 1.00572717, 1.00651467, 1.00724912, 1.00792384, 1.00853491, 1.00907779, 1.00954843, 1.00994480, 1.01026309, 1.01050198, 1.01066041, 1.01073647, 1.01073205, 1.01064730, 1.01048374, 1.01024330, 1.00992775, 1.00954151, 1.00908792, 1.00857103, 1.00799608, 1.00736785, 1.00669181, 1.00597465, 1.00522304, 1.00444114, 1.00363779, 1.00281966, 1.00199306, 1.00116563, 1.00034273, 0.99953192, 0.99873996, 0.99797446, 0.99724007, 0.99654233, 0.99588805, 0.99528110, 0.99472731, 0.99422991, 0.99379259, 0.99341941, 0.99311274, 0.99287349, 0.99270529, 0.99260652, 0.99257857, 0.99261945, 0.99273038, 0.99290955, 0.99315345, 0.99346066, 0.99382687, 0.99424839, 0.99472272, 0.99524361, 0.99580789, 0.99640882, 0.99704123, 0.99769914, 0.99837738, 0.99906975, 0.99976873, 1.00046957, 1.00116682, 1.00185359, 1.00252378, 1.00317132, 1.00379229, 1.00438106, 1.00493193, 1.00543988, 1.00590360, 1.00631797, 1.00667834, 1.00698411, 1.00723267, 1.00742185, 1.00755143, 1.00761986, 1.00762773, 1.00757504, 1.00746322, 1.00729394, 1.00706911, 1.00679159, 1.00646341, 1.00608873, 1.00567114, 1.00521481, 1.00472414, 1.00420344, 1.00365949, 1.00309455, 1.00251555, 1.00192726, 1.00133574, 1.00074565, 1.00016236, 0.99959171, 0.99903804, 0.99850535, 0.99799985, 0.99752414, 0.99708354, 0.99668151, 0.99632025, 0.99600381, 0.99573332, 0.99551266, 0.99534053, 0.99522054, 0.99515241, 0.99513555, 0.99517030, 0.99525541, 0.99538952, 0.99557215, 0.99579948, 0.99607027, 0.99638057, 0.99672699, 0.99710703, 0.99751633, 0.99795067, 0.99840581, 0.99887735, 0.99936110, 0.99985218, 1.00034654, 1.00083947, 1.00132573, 1.00180173, 1.00226319, 1.00270593, 1.00312614, 1.00351942, 1.00388277, 1.00421333, 1.00450921, 1.00476730, 1.00498581, 1.00516307, 1.00529838, 1.00539017, 1.00543821, 1.00544345, 1.00540483, 1.00532460, 1.00520289, 1.00504267, 1.00484514, 1.00461233, 1.00434721, 1.00405192, 1.00373054, 1.00338578, 1.00302088, 1.00264025, 1.00224662, 1.00184488, 1.00143826, 1.00103080, 1.00062573, 1.00022781, 0.99984139, 0.99946749, 0.99911129, 0.99877495, 0.99846184, 0.99817514, 0.99791592, 0.99768758, 0.99749118, 0.99732894, 0.99720013, 0.99710739, 0.99705017, 0.99702936, 0.99704379, 0.99709320, 0.99717599, 0.99729306, 0.99744141, 0.99761915, 0.99782479, 0.99805552, 0.99830848, 0.99858129, 0.99887139, 0.99917626, 0.99949169, 0.99981594, 1.00014460, 1.00047433, 1.00080299, 1.00112653, 1.00144279, 1.00174880, 1.00204086, 1.00231719, 1.00257576, 1.00281310, 1.00302708, 1.00321829, 1.00338340, 1.00352204, 1.00363243, 1.00371420, 1.00376749, 1.00379157, 1.00378597, 1.00375187, 1.00369036, 1.00360274, 1.00348926, 1.00335169, 1.00319159, 1.00301039, 1.00281143, 1.00259674, 1.00236738, 1.00212586, 1.00187516, 1.00161850, 1.00135744, 1.00109494, 1.00083435, 1.00057673, 1.00032485, 1.00008142, 0.99984884, 0.99962848, 0.99942255, 0.99923331, 0.99906200, 0.99890971, 0.99877805, 0.99866712, 0.99857920, 0.99851406, 0.99847245, 0.99845350, 0.99845672, 0.99848276, 0.99853104, 0.99859989, 0.99868929, 0.99879676, 0.99892145, 0.99906349, 0.99921906, 0.99938750, 0.99956673, 0.99975437, 0.99995005, 1.00015068, 1.00035346, 1.00055718, 1.00075948, 1.00095868, 1.00115299, 1.00134051, 1.00151980, 1.00168908, 1.00184536, 1.00198960, 1.00211966, 1.00223446, 1.00233328, 1.00241554, 1.00247967, 1.00252616, 1.00255466, 1.00256550, 1.00255883, 1.00253487, 1.00249410, 1.00243723, 1.00236428, 1.00227737, 1.00217772, 1.00206661, 1.00194490, 1.00181246, 1.00167227, 1.00152564, 1.00137448, 1.00121903, 1.00106311, 1.00090694, 1.00075161, 1.00059938, 1.00045121, 1.00030851, 1.00017321, 1.00004661, 0.99992871, 0.99982160, 0.99972528, 0.99964148, 0.99956852, 0.99950951, 0.99946433, 0.99943203, 0.99941361, 0.99940944, 0.99941790, 0.99943894, 0.99947232, 0.99951869, 0.99957561, 0.99964315, 0.99972034, 0.99980599, 0.99989998, 1.00000000 +}; + + +} // namespace rack diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/utils/AsymWaveShaper.cpp b/plugins/community/repos/squinkylabs-plug1/dsp/utils/AsymWaveShaper.cpp new file mode 100644 index 00000000..34767b95 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/dsp/utils/AsymWaveShaper.cpp @@ -0,0 +1,669 @@ + +#include "AsymWaveShaper.h" + + +extern float symmetry_table_0[]; +extern float symmetry_table_1[]; +extern float symmetry_table_2[]; +extern float symmetry_table_3[]; +extern float symmetry_table_4[]; +extern float symmetry_table_5[]; +extern float symmetry_table_6[]; +extern float symmetry_table_7[]; +extern float symmetry_table_8[]; +extern float symmetry_table_9[]; +extern float symmetry_table_10[]; +extern float symmetry_table_11[]; +extern float symmetry_table_12[]; +extern float symmetry_table_13[]; +extern float symmetry_table_14[]; +extern float symmetry_table_15[]; + +float * lookup_tables[16] = { + symmetry_table_0, + symmetry_table_1, + symmetry_table_2, + symmetry_table_3, + symmetry_table_4, + symmetry_table_5, + symmetry_table_6, + symmetry_table_7, + symmetry_table_8, + symmetry_table_9, + symmetry_table_10, + symmetry_table_11, + symmetry_table_12, + symmetry_table_13, + symmetry_table_14, + symmetry_table_15, +}; + + +AsymWaveShaper::AsymWaveShaper() +{ + for (int i = 0; i < iSymmetryTables; ++i) { + const float* entries = lookup_tables[i]; + LookupTable::initDiscrete(tables[i], iNumPoints, entries); + } +} + +void AsymWaveShaper::genTableValues(const Spline& spline, int numPoints) +{ + const double x0 = spline[0].first; + + // first build non-uniform lookup + NonUniformLookup nu; + const double delta = 1.0 / (numPoints * 8); // let's oversample in t space + for (double t = 0; t <= 1; t += delta) { + auto pt = calcPoint(spline, t); + //printf("adding point to table:%f, %f\n", pt.first, pt.second); + nu.add(pt.first, pt.second); + } + + // next output uniform + for (int i = 0; i < numPoints; ++i) { + double x = x0 + (double(i) / numPoints); + double y = nu.lookup(x); + printf("%ff", y); + if (i != numPoints - 1) { + printf(", "); + + if ((i % 8) == 7) { + printf("\n"); + } + } + } +} + + +void AsymWaveShaper::genTable(int index, double symmetry) +{ + printf("float symmetry_table_%d[%d] = {\n", index, iNumPoints); + + genTableValues(makeSplineLeft(symmetry), iNumPoints / 2); + printf(",\n"); + genTableValues(makeSplineRight(symmetry), iNumPoints / 2); + printf("\n};\n"); + fflush(stdout); +} + +Spline AsymWaveShaper::makeSplineRight(double symmetry) +{ + Spline ret; + ret.push_back(std::pair(0.0, 0.0)); + ret.push_back(std::pair(0.5, 1.0)); + ret.push_back(std::pair(0.5, 1.0)); + ret.push_back(std::pair(1.0, 1.0)); + return ret; +} + +Spline AsymWaveShaper::makeSplineLeft(double symmetry) +{ + // symmetry from 0..1 + Spline ret; + ret.push_back(std::pair(-1, -symmetry)); + ret.push_back(std::pair(-symmetry/2.f, -symmetry)); + ret.push_back(std::pair(-symmetry/2.f, -symmetry)); + ret.push_back(std::pair(0, 0)); + return ret; +} + +std::pair AsymWaveShaper::calcPoint(const Spline& spline, double t) +{ + std::pair ret; + + ret.first = pow(1 - t, 3) * spline[0].first + + 3 * t * pow(1 - t, 2) * spline[1].first + + 3 * pow(t, 2) * (1 - t) * spline[2].first + + pow(t, 3) * spline[3].first; + + ret.second = pow(1 - t, 3) * spline[0].second + + 3 * t * pow(1 - t, 2) * spline[1].second + + 3 * pow(t, 2) * (1 - t) * spline[2].second + + pow(t, 3) * spline[3].second; + return ret; +} +float symmetry_table_0[256] = { +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_1[256] = { +-0.066667f, -0.066667f, -0.066667f, -0.066667f, -0.066667f, -0.066667f, -0.066666f, -0.066666f, +-0.066666f, -0.066666f, -0.066665f, -0.066665f, -0.066664f, -0.066663f, -0.066663f, -0.066662f, +-0.066661f, -0.066659f, -0.066658f, -0.066656f, -0.066654f, -0.066652f, -0.066650f, -0.066647f, +-0.066644f, -0.066641f, -0.066638f, -0.066634f, -0.066630f, -0.066625f, -0.066621f, -0.066615f, +-0.066610f, -0.066603f, -0.066597f, -0.066590f, -0.066582f, -0.066574f, -0.066565f, -0.066556f, +-0.066546f, -0.066535f, -0.066523f, -0.066511f, -0.066498f, -0.066484f, -0.066470f, -0.066454f, +-0.066438f, -0.066420f, -0.066402f, -0.066382f, -0.066361f, -0.066339f, -0.066316f, -0.066292f, +-0.066266f, -0.066238f, -0.066210f, -0.066179f, -0.066147f, -0.066113f, -0.066078f, -0.066040f, +-0.066001f, -0.065959f, -0.065916f, -0.065870f, -0.065821f, -0.065770f, -0.065717f, -0.065660f, +-0.065601f, -0.065538f, -0.065473f, -0.065404f, -0.065331f, -0.065255f, -0.065174f, -0.065089f, +-0.065000f, -0.064906f, -0.064808f, -0.064703f, -0.064594f, -0.064478f, -0.064356f, -0.064228f, +-0.064092f, -0.063949f, -0.063798f, -0.063639f, -0.063470f, -0.063292f, -0.063103f, -0.062903f, +-0.062691f, -0.062465f, -0.062226f, -0.061972f, -0.061702f, -0.061413f, -0.061106f, -0.060777f, +-0.060425f, -0.060048f, -0.059643f, -0.059207f, -0.058737f, -0.058228f, -0.057677f, -0.057076f, +-0.056421f, -0.055703f, -0.054911f, -0.054035f, -0.053059f, -0.051965f, -0.050726f, -0.049311f, +-0.047673f, -0.045747f, -0.043439f, -0.040598f, -0.036977f, -0.032128f, -0.025192f, -0.014656f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_2[256] = { +-0.133333f, -0.133333f, -0.133333f, -0.133333f, -0.133333f, -0.133333f, -0.133333f, -0.133332f, +-0.133332f, -0.133331f, -0.133330f, -0.133329f, -0.133328f, -0.133326f, -0.133324f, -0.133322f, +-0.133320f, -0.133317f, -0.133314f, -0.133310f, -0.133306f, -0.133301f, -0.133296f, -0.133290f, +-0.133284f, -0.133276f, -0.133269f, -0.133260f, -0.133251f, -0.133241f, -0.133230f, -0.133218f, +-0.133205f, -0.133191f, -0.133176f, -0.133160f, -0.133143f, -0.133124f, -0.133104f, -0.133083f, +-0.133060f, -0.133036f, -0.133010f, -0.132982f, -0.132953f, -0.132921f, -0.132888f, -0.132852f, +-0.132815f, -0.132775f, -0.132732f, -0.132688f, -0.132640f, -0.132590f, -0.132537f, -0.132480f, +-0.132421f, -0.132358f, -0.132292f, -0.132222f, -0.132148f, -0.132070f, -0.131988f, -0.131902f, +-0.131810f, -0.131714f, -0.131613f, -0.131506f, -0.131393f, -0.131274f, -0.131149f, -0.131018f, +-0.130879f, -0.130733f, -0.130579f, -0.130417f, -0.130246f, -0.130066f, -0.129876f, -0.129676f, +-0.129465f, -0.129243f, -0.129008f, -0.128761f, -0.128500f, -0.128224f, -0.127932f, -0.127624f, +-0.127299f, -0.126954f, -0.126590f, -0.126203f, -0.125794f, -0.125360f, -0.124899f, -0.124410f, +-0.123889f, -0.123335f, -0.122745f, -0.122115f, -0.121442f, -0.120723f, -0.119952f, -0.119125f, +-0.118235f, -0.117277f, -0.116243f, -0.115125f, -0.113911f, -0.112591f, -0.111150f, -0.109572f, +-0.107837f, -0.105920f, -0.103793f, -0.101419f, -0.098751f, -0.095733f, -0.092290f, -0.088326f, +-0.083713f, -0.078285f, -0.071824f, -0.064055f, -0.054668f, -0.043400f, -0.030217f, -0.015502f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_3[256] = { +-0.200000f, -0.200000f, -0.200000f, -0.200000f, -0.200000f, -0.199999f, -0.199999f, -0.199998f, +-0.199997f, -0.199996f, -0.199995f, -0.199993f, -0.199991f, -0.199988f, -0.199985f, -0.199981f, +-0.199977f, -0.199972f, -0.199967f, -0.199960f, -0.199953f, -0.199945f, -0.199937f, -0.199927f, +-0.199916f, -0.199904f, -0.199891f, -0.199877f, -0.199861f, -0.199844f, -0.199825f, -0.199805f, +-0.199783f, -0.199759f, -0.199734f, -0.199706f, -0.199677f, -0.199645f, -0.199611f, -0.199575f, +-0.199536f, -0.199494f, -0.199449f, -0.199402f, -0.199351f, -0.199297f, -0.199240f, -0.199179f, +-0.199114f, -0.199046f, -0.198973f, -0.198895f, -0.198813f, -0.198726f, -0.198634f, -0.198537f, +-0.198434f, -0.198325f, -0.198210f, -0.198089f, -0.197960f, -0.197825f, -0.197681f, -0.197530f, +-0.197370f, -0.197202f, -0.197024f, -0.196837f, -0.196639f, -0.196430f, -0.196210f, -0.195978f, +-0.195733f, -0.195474f, -0.195201f, -0.194914f, -0.194610f, -0.194289f, -0.193951f, -0.193594f, +-0.193216f, -0.192818f, -0.192396f, -0.191951f, -0.191479f, -0.190981f, -0.190453f, -0.189894f, +-0.189302f, -0.188674f, -0.188007f, -0.187300f, -0.186548f, -0.185748f, -0.184897f, -0.183990f, +-0.183023f, -0.181990f, -0.180886f, -0.179704f, -0.178437f, -0.177078f, -0.175616f, -0.174041f, +-0.172341f, -0.170504f, -0.168512f, -0.166349f, -0.163992f, -0.161418f, -0.158598f, -0.155499f, +-0.152081f, -0.148298f, -0.144093f, -0.139402f, -0.134148f, -0.128240f, -0.121576f, -0.114043f, +-0.105522f, -0.095903f, -0.085107f, -0.073109f, -0.059969f, -0.045841f, -0.030958f, -0.015591f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_4[256] = { +-0.266667f, -0.266667f, -0.266667f, -0.266666f, -0.266666f, -0.266666f, -0.266665f, -0.266664f, +-0.266663f, -0.266661f, -0.266659f, -0.266656f, -0.266653f, -0.266649f, -0.266644f, -0.266639f, +-0.266632f, -0.266625f, -0.266617f, -0.266607f, -0.266596f, -0.266584f, -0.266571f, -0.266556f, +-0.266540f, -0.266522f, -0.266502f, -0.266480f, -0.266457f, -0.266431f, -0.266402f, -0.266372f, +-0.266338f, -0.266303f, -0.266264f, -0.266222f, -0.266177f, -0.266128f, -0.266077f, -0.266021f, +-0.265961f, -0.265897f, -0.265829f, -0.265756f, -0.265679f, -0.265596f, -0.265508f, -0.265414f, +-0.265315f, -0.265209f, -0.265096f, -0.264977f, -0.264850f, -0.264716f, -0.264573f, -0.264423f, +-0.264263f, -0.264094f, -0.263915f, -0.263726f, -0.263526f, -0.263314f, -0.263090f, -0.262854f, +-0.262604f, -0.262340f, -0.262061f, -0.261766f, -0.261455f, -0.261126f, -0.260778f, -0.260411f, +-0.260023f, -0.259613f, -0.259180f, -0.258722f, -0.258238f, -0.257726f, -0.257185f, -0.256613f, +-0.256007f, -0.255366f, -0.254687f, -0.253968f, -0.253206f, -0.252398f, -0.251540f, -0.250630f, +-0.249664f, -0.248637f, -0.247544f, -0.246381f, -0.245143f, -0.243822f, -0.242413f, -0.240908f, +-0.239298f, -0.237575f, -0.235729f, -0.233747f, -0.231617f, -0.229325f, -0.226855f, -0.224189f, +-0.221306f, -0.218183f, -0.214794f, -0.211110f, -0.207097f, -0.202719f, -0.197933f, -0.192692f, +-0.186945f, -0.180635f, -0.173704f, -0.166089f, -0.157731f, -0.148574f, -0.138577f, -0.127713f, +-0.115983f, -0.103419f, -0.090082f, -0.076066f, -0.061485f, -0.046465f, -0.031134f, -0.015611f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_5[256] = { +-0.333333f, -0.333333f, -0.333333f, -0.333333f, -0.333333f, -0.333332f, -0.333331f, -0.333330f, +-0.333328f, -0.333325f, -0.333322f, -0.333318f, -0.333314f, -0.333308f, -0.333301f, -0.333294f, +-0.333284f, -0.333274f, -0.333262f, -0.333249f, -0.333234f, -0.333217f, -0.333198f, -0.333177f, +-0.333153f, -0.333128f, -0.333099f, -0.333068f, -0.333034f, -0.332997f, -0.332957f, -0.332913f, +-0.332865f, -0.332814f, -0.332758f, -0.332698f, -0.332633f, -0.332564f, -0.332489f, -0.332409f, +-0.332323f, -0.332231f, -0.332132f, -0.332027f, -0.331915f, -0.331795f, -0.331667f, -0.331531f, +-0.331386f, -0.331232f, -0.331068f, -0.330894f, -0.330710f, -0.330513f, -0.330305f, -0.330085f, +-0.329851f, -0.329603f, -0.329340f, -0.329062f, -0.328767f, -0.328455f, -0.328125f, -0.327775f, +-0.327406f, -0.327014f, -0.326600f, -0.326162f, -0.325699f, -0.325209f, -0.324690f, -0.324141f, +-0.323560f, -0.322946f, -0.322295f, -0.321606f, -0.320877f, -0.320104f, -0.319286f, -0.318419f, +-0.317500f, -0.316525f, -0.315491f, -0.314393f, -0.313228f, -0.311990f, -0.310674f, -0.309274f, +-0.307785f, -0.306199f, -0.304509f, -0.302706f, -0.300783f, -0.298728f, -0.296532f, -0.294183f, +-0.291667f, -0.288969f, -0.286075f, -0.282967f, -0.279625f, -0.276029f, -0.272155f, -0.267977f, +-0.263468f, -0.258598f, -0.253334f, -0.247642f, -0.241485f, -0.234825f, -0.227623f, -0.219844f, +-0.211450f, -0.202413f, -0.192708f, -0.182321f, -0.171248f, -0.159501f, -0.147103f, -0.134096f, +-0.120530f, -0.106468f, -0.091979f, -0.077137f, -0.062014f, -0.046678f, -0.031194f, -0.015618f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_6[256] = { +-0.400000f, -0.400000f, -0.400000f, -0.400000f, -0.399999f, -0.399998f, -0.399997f, -0.399995f, +-0.399992f, -0.399989f, -0.399985f, -0.399979f, -0.399973f, -0.399965f, -0.399956f, -0.399946f, +-0.399933f, -0.399919f, -0.399903f, -0.399885f, -0.399864f, -0.399840f, -0.399814f, -0.399785f, +-0.399753f, -0.399718f, -0.399679f, -0.399636f, -0.399589f, -0.399538f, -0.399482f, -0.399421f, +-0.399355f, -0.399284f, -0.399207f, -0.399123f, -0.399034f, -0.398937f, -0.398833f, -0.398721f, +-0.398601f, -0.398473f, -0.398335f, -0.398188f, -0.398031f, -0.397863f, -0.397684f, -0.397493f, +-0.397289f, -0.397073f, -0.396842f, -0.396597f, -0.396336f, -0.396059f, -0.395765f, -0.395452f, +-0.395120f, -0.394768f, -0.394395f, -0.393999f, -0.393579f, -0.393134f, -0.392662f, -0.392162f, +-0.391632f, -0.391071f, -0.390476f, -0.389846f, -0.389178f, -0.388471f, -0.387721f, -0.386926f, +-0.386084f, -0.385192f, -0.384246f, -0.383243f, -0.382179f, -0.381050f, -0.379852f, -0.378581f, +-0.377232f, -0.375798f, -0.374275f, -0.372655f, -0.370933f, -0.369102f, -0.367152f, -0.365075f, +-0.362862f, -0.360503f, -0.357987f, -0.355302f, -0.352434f, -0.349371f, -0.346095f, -0.342592f, +-0.338842f, -0.334828f, -0.330528f, -0.325920f, -0.320981f, -0.315687f, -0.310013f, -0.303931f, +-0.297415f, -0.290439f, -0.282976f, -0.275004f, -0.266499f, -0.257444f, -0.247826f, -0.237638f, +-0.226877f, -0.215553f, -0.203678f, -0.191275f, -0.178375f, -0.165012f, -0.151228f, -0.137068f, +-0.122577f, -0.107804f, -0.092794f, -0.077591f, -0.062236f, -0.046768f, -0.031219f, -0.015621f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_7[256] = { +-0.466667f, -0.466667f, -0.466667f, -0.466666f, -0.466665f, -0.466664f, -0.466662f, -0.466660f, +-0.466657f, -0.466652f, -0.466646f, -0.466639f, -0.466631f, -0.466621f, -0.466608f, -0.466594f, +-0.466578f, -0.466559f, -0.466537f, -0.466512f, -0.466484f, -0.466453f, -0.466418f, -0.466379f, +-0.466336f, -0.466288f, -0.466235f, -0.466178f, -0.466114f, -0.466045f, -0.465969f, -0.465887f, +-0.465798f, -0.465701f, -0.465596f, -0.465483f, -0.465361f, -0.465229f, -0.465087f, -0.464934f, +-0.464771f, -0.464595f, -0.464407f, -0.464205f, -0.463990f, -0.463759f, -0.463513f, -0.463251f, +-0.462971f, -0.462672f, -0.462354f, -0.462015f, -0.461654f, -0.461270f, -0.460862f, -0.460428f, +-0.459967f, -0.459477f, -0.458956f, -0.458403f, -0.457817f, -0.457194f, -0.456532f, -0.455831f, +-0.455086f, -0.454296f, -0.453458f, -0.452569f, -0.451625f, -0.450624f, -0.449562f, -0.448434f, +-0.447238f, -0.445968f, -0.444620f, -0.443189f, -0.441669f, -0.440054f, -0.438338f, -0.436516f, +-0.434578f, -0.432518f, -0.430327f, -0.427996f, -0.425515f, -0.422875f, -0.420063f, -0.417069f, +-0.413879f, -0.410479f, -0.406856f, -0.402994f, -0.398875f, -0.394484f, -0.389802f, -0.384810f, +-0.379489f, -0.373819f, -0.367779f, -0.361349f, -0.354510f, -0.347243f, -0.339529f, -0.331352f, +-0.322701f, -0.313562f, -0.303931f, -0.293803f, -0.283181f, -0.272071f, -0.260484f, -0.248436f, +-0.235946f, -0.223041f, -0.209746f, -0.196093f, -0.182113f, -0.167839f, -0.153304f, -0.138541f, +-0.123580f, -0.108453f, -0.093188f, -0.077810f, -0.062344f, -0.046811f, -0.031232f, -0.015623f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_8[256] = { +-0.533333f, -0.533333f, -0.533333f, -0.533333f, -0.533332f, -0.533330f, -0.533328f, -0.533325f, +-0.533320f, -0.533314f, -0.533307f, -0.533297f, -0.533286f, -0.533273f, -0.533257f, -0.533238f, +-0.533216f, -0.533191f, -0.533162f, -0.533130f, -0.533093f, -0.533051f, -0.533005f, -0.532953f, +-0.532896f, -0.532832f, -0.532762f, -0.532685f, -0.532601f, -0.532508f, -0.532407f, -0.532297f, +-0.532178f, -0.532048f, -0.531908f, -0.531756f, -0.531591f, -0.531414f, -0.531223f, -0.531018f, +-0.530797f, -0.530560f, -0.530306f, -0.530033f, -0.529741f, -0.529428f, -0.529094f, -0.528737f, +-0.528356f, -0.527949f, -0.527515f, -0.527052f, -0.526559f, -0.526033f, -0.525474f, -0.524878f, +-0.524244f, -0.523570f, -0.522852f, -0.522090f, -0.521279f, -0.520418f, -0.519502f, -0.518529f, +-0.517495f, -0.516397f, -0.515230f, -0.513991f, -0.512674f, -0.511276f, -0.509790f, -0.508211f, +-0.506534f, -0.504753f, -0.502859f, -0.500848f, -0.498710f, -0.496437f, -0.494022f, -0.491454f, +-0.488725f, -0.485822f, -0.482737f, -0.479456f, -0.475967f, -0.472257f, -0.468313f, -0.464120f, +-0.459664f, -0.454928f, -0.449897f, -0.444556f, -0.438887f, -0.432874f, -0.426502f, -0.419754f, +-0.412616f, -0.405075f, -0.397117f, -0.388734f, -0.379917f, -0.370659f, -0.360958f, -0.350815f, +-0.340232f, -0.329217f, -0.317779f, -0.305932f, -0.293690f, -0.281074f, -0.268103f, -0.254800f, +-0.241189f, -0.227294f, -0.213139f, -0.198750f, -0.184152f, -0.169367f, -0.154418f, -0.139327f, +-0.124115f, -0.108799f, -0.093398f, -0.077927f, -0.062402f, -0.046835f, -0.031238f, -0.015624f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_9[256] = { +-0.600000f, -0.600000f, -0.600000f, -0.599999f, -0.599998f, -0.599996f, -0.599993f, -0.599989f, +-0.599983f, -0.599975f, -0.599965f, -0.599953f, -0.599939f, -0.599921f, -0.599900f, -0.599875f, +-0.599847f, -0.599814f, -0.599776f, -0.599734f, -0.599685f, -0.599631f, -0.599570f, -0.599502f, +-0.599426f, -0.599342f, -0.599250f, -0.599148f, -0.599036f, -0.598914f, -0.598780f, -0.598634f, +-0.598475f, -0.598303f, -0.598116f, -0.597913f, -0.597694f, -0.597458f, -0.597203f, -0.596928f, +-0.596632f, -0.596314f, -0.595972f, -0.595606f, -0.595213f, -0.594792f, -0.594341f, -0.593859f, +-0.593343f, -0.592792f, -0.592204f, -0.591576f, -0.590905f, -0.590191f, -0.589428f, -0.588616f, +-0.587750f, -0.586829f, -0.585847f, -0.584802f, -0.583690f, -0.582507f, -0.581248f, -0.579909f, +-0.578484f, -0.576969f, -0.575359f, -0.573646f, -0.571826f, -0.569890f, -0.567833f, -0.565647f, +-0.563323f, -0.560853f, -0.558229f, -0.555441f, -0.552478f, -0.549332f, -0.545990f, -0.542441f, +-0.538673f, -0.534675f, -0.530432f, -0.525932f, -0.521161f, -0.516106f, -0.510752f, -0.505087f, +-0.499096f, -0.492766f, -0.486085f, -0.479041f, -0.471622f, -0.463820f, -0.455626f, -0.447034f, +-0.438039f, -0.428640f, -0.418836f, -0.408630f, -0.398026f, -0.387032f, -0.375656f, -0.363911f, +-0.351811f, -0.339370f, -0.326606f, -0.313536f, -0.300179f, -0.286555f, -0.272683f, -0.258583f, +-0.244274f, -0.229776f, -0.215106f, -0.200282f, -0.185322f, -0.170242f, -0.155055f, -0.139777f, +-0.124420f, -0.108997f, -0.093518f, -0.077994f, -0.062435f, -0.046848f, -0.031242f, -0.015624f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_10[256] = { +-0.666667f, -0.666667f, -0.666666f, -0.666666f, -0.666664f, -0.666661f, -0.666657f, -0.666652f, +-0.666644f, -0.666634f, -0.666622f, -0.666606f, -0.666587f, -0.666564f, -0.666537f, -0.666505f, +-0.666468f, -0.666425f, -0.666376f, -0.666320f, -0.666257f, -0.666185f, -0.666105f, -0.666016f, +-0.665917f, -0.665807f, -0.665685f, -0.665551f, -0.665404f, -0.665242f, -0.665065f, -0.664872f, +-0.664662f, -0.664434f, -0.664186f, -0.663917f, -0.663625f, -0.663310f, -0.662970f, -0.662604f, +-0.662208f, -0.661783f, -0.661326f, -0.660834f, -0.660307f, -0.659741f, -0.659135f, -0.658485f, +-0.657789f, -0.657045f, -0.656250f, -0.655400f, -0.654491f, -0.653522f, -0.652486f, -0.651382f, +-0.650204f, -0.648948f, -0.647610f, -0.646184f, -0.644665f, -0.643047f, -0.641325f, -0.639491f, +-0.637540f, -0.635465f, -0.633257f, -0.630910f, -0.628414f, -0.625761f, -0.622943f, -0.619948f, +-0.616769f, -0.613393f, -0.609811f, -0.606010f, -0.601981f, -0.597711f, -0.593187f, -0.588399f, +-0.583333f, -0.577978f, -0.572322f, -0.566353f, -0.560059f, -0.553431f, -0.546459f, -0.539134f, +-0.531447f, -0.523393f, -0.514967f, -0.506166f, -0.496987f, -0.487431f, -0.477501f, -0.467200f, +-0.456535f, -0.445512f, -0.434141f, -0.422433f, -0.410401f, -0.398057f, -0.385417f, -0.372494f, +-0.359306f, -0.345868f, -0.332196f, -0.318307f, -0.304217f, -0.289941f, -0.275495f, -0.260893f, +-0.246151f, -0.231281f, -0.216296f, -0.201208f, -0.186029f, -0.170770f, -0.155440f, -0.140049f, +-0.124605f, -0.109117f, -0.093591f, -0.078035f, -0.062455f, -0.046857f, -0.031245f, -0.015624f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_11[256] = { +-0.733333f, -0.733333f, -0.733333f, -0.733332f, -0.733330f, -0.733327f, -0.733321f, -0.733314f, +-0.733304f, -0.733292f, -0.733275f, -0.733255f, -0.733230f, -0.733201f, -0.733165f, -0.733124f, +-0.733075f, -0.733020f, -0.732955f, -0.732882f, -0.732800f, -0.732706f, -0.732602f, -0.732485f, +-0.732354f, -0.732210f, -0.732050f, -0.731873f, -0.731679f, -0.731466f, -0.731233f, -0.730978f, +-0.730700f, -0.730397f, -0.730067f, -0.729710f, -0.729322f, -0.728903f, -0.728449f, -0.727960f, +-0.727432f, -0.726862f, -0.726250f, -0.725591f, -0.724882f, -0.724122f, -0.723305f, -0.722430f, +-0.721492f, -0.720487f, -0.719412f, -0.718261f, -0.717032f, -0.715717f, -0.714313f, -0.712813f, +-0.711213f, -0.709506f, -0.707686f, -0.705745f, -0.703678f, -0.701476f, -0.699131f, -0.696636f, +-0.693982f, -0.691160f, -0.688161f, -0.684974f, -0.681591f, -0.678000f, -0.674191f, -0.670154f, +-0.665877f, -0.661350f, -0.656561f, -0.651499f, -0.646154f, -0.640515f, -0.634571f, -0.628313f, +-0.621732f, -0.614820f, -0.607570f, -0.599974f, -0.592029f, -0.583730f, -0.575074f, -0.566062f, +-0.556694f, -0.546972f, -0.536899f, -0.526482f, -0.515726f, -0.504639f, -0.493233f, -0.481515f, +-0.469498f, -0.457195f, -0.444618f, -0.431780f, -0.418696f, -0.405378f, -0.391842f, -0.378100f, +-0.364167f, -0.350056f, -0.335780f, -0.321352f, -0.306784f, -0.292087f, -0.277273f, -0.262352f, +-0.247335f, -0.232229f, -0.217046f, -0.201792f, -0.186475f, -0.171104f, -0.155684f, -0.140221f, +-0.124723f, -0.109194f, -0.093638f, -0.078062f, -0.062468f, -0.046862f, -0.031246f, -0.015625f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_12[256] = { +-0.800000f, -0.800000f, -0.800000f, -0.799998f, -0.799996f, -0.799991f, -0.799985f, -0.799975f, +-0.799963f, -0.799946f, -0.799925f, -0.799899f, -0.799867f, -0.799828f, -0.799782f, -0.799728f, +-0.799665f, -0.799592f, -0.799509f, -0.799413f, -0.799305f, -0.799183f, -0.799045f, -0.798892f, +-0.798720f, -0.798530f, -0.798319f, -0.798086f, -0.797830f, -0.797548f, -0.797238f, -0.796900f, +-0.796531f, -0.796128f, -0.795690f, -0.795213f, -0.794696f, -0.794136f, -0.793530f, -0.792874f, +-0.792166f, -0.791402f, -0.790579f, -0.789693f, -0.788739f, -0.787714f, -0.786613f, -0.785431f, +-0.784164f, -0.782805f, -0.781350f, -0.779792f, -0.778126f, -0.776344f, -0.774441f, -0.772407f, +-0.770238f, -0.767923f, -0.765456f, -0.762828f, -0.760029f, -0.757050f, -0.753883f, -0.750518f, +-0.746943f, -0.743150f, -0.739128f, -0.734866f, -0.730354f, -0.725583f, -0.720540f, -0.715218f, +-0.709606f, -0.703694f, -0.697475f, -0.690940f, -0.684083f, -0.676897f, -0.669377f, -0.661520f, +-0.653321f, -0.644781f, -0.635898f, -0.626674f, -0.617111f, -0.607214f, -0.596986f, -0.586434f, +-0.575567f, -0.564391f, -0.552918f, -0.541156f, -0.529117f, -0.516812f, -0.504253f, -0.491452f, +-0.478422f, -0.465174f, -0.451722f, -0.438077f, -0.424251f, -0.410255f, -0.396103f, -0.381803f, +-0.367368f, -0.352806f, -0.338128f, -0.323344f, -0.308461f, -0.293488f, -0.278434f, -0.263304f, +-0.248107f, -0.232849f, -0.217536f, -0.202174f, -0.186768f, -0.171323f, -0.155844f, -0.140335f, +-0.124801f, -0.109244f, -0.093669f, -0.078079f, -0.062477f, -0.046865f, -0.031247f, -0.015625f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_13[256] = { +-0.866667f, -0.866667f, -0.866666f, -0.866664f, -0.866661f, -0.866655f, -0.866647f, -0.866635f, +-0.866618f, -0.866597f, -0.866570f, -0.866535f, -0.866494f, -0.866443f, -0.866383f, -0.866313f, +-0.866230f, -0.866135f, -0.866025f, -0.865900f, -0.865757f, -0.865596f, -0.865415f, -0.865212f, +-0.864986f, -0.864734f, -0.864455f, -0.864146f, -0.863805f, -0.863430f, -0.863019f, -0.862568f, +-0.862075f, -0.861536f, -0.860950f, -0.860312f, -0.859619f, -0.858866f, -0.858051f, -0.857169f, +-0.856216f, -0.855186f, -0.854075f, -0.852878f, -0.851590f, -0.850204f, -0.848714f, -0.847114f, +-0.845398f, -0.843558f, -0.841586f, -0.839477f, -0.837220f, -0.834808f, -0.832233f, -0.829485f, +-0.826556f, -0.823435f, -0.820114f, -0.816581f, -0.812828f, -0.808845f, -0.804621f, -0.800146f, +-0.795410f, -0.790404f, -0.785119f, -0.779546f, -0.773676f, -0.767502f, -0.761016f, -0.754213f, +-0.747087f, -0.739635f, -0.731852f, -0.723737f, -0.715290f, -0.706510f, -0.697400f, -0.687961f, +-0.678199f, -0.668118f, -0.657724f, -0.647026f, -0.636029f, -0.624744f, -0.613180f, -0.601348f, +-0.589256f, -0.576917f, -0.564342f, -0.551541f, -0.538527f, -0.525309f, -0.511900f, -0.498310f, +-0.484549f, -0.470629f, -0.456560f, -0.442350f, -0.428010f, -0.413549f, -0.398974f, -0.384295f, +-0.369519f, -0.354653f, -0.339705f, -0.324681f, -0.309587f, -0.294429f, -0.279214f, -0.263945f, +-0.248628f, -0.233267f, -0.217867f, -0.202433f, -0.186966f, -0.171472f, -0.155953f, -0.140413f, +-0.124854f, -0.109279f, -0.093691f, -0.078091f, -0.062483f, -0.046868f, -0.031248f, -0.015625f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_14[256] = { +-0.933333f, -0.933333f, -0.933332f, -0.933330f, -0.933326f, -0.933319f, -0.933308f, -0.933292f, +-0.933271f, -0.933243f, -0.933207f, -0.933162f, -0.933107f, -0.933041f, -0.932963f, -0.932870f, +-0.932761f, -0.932635f, -0.932490f, -0.932324f, -0.932136f, -0.931922f, -0.931682f, -0.931413f, +-0.931111f, -0.930775f, -0.930403f, -0.929990f, -0.929534f, -0.929032f, -0.928480f, -0.927874f, +-0.927211f, -0.926487f, -0.925697f, -0.924836f, -0.923900f, -0.922883f, -0.921781f, -0.920587f, +-0.919295f, -0.917900f, -0.916394f, -0.914770f, -0.913022f, -0.911142f, -0.909121f, -0.906952f, +-0.904625f, -0.902133f, -0.899466f, -0.896614f, -0.893569f, -0.890320f, -0.886857f, -0.883171f, +-0.879252f, -0.875090f, -0.870674f, -0.865996f, -0.861046f, -0.855816f, -0.850296f, -0.844480f, +-0.838360f, -0.831930f, -0.825184f, -0.818118f, -0.810729f, -0.803014f, -0.794973f, -0.786603f, +-0.777908f, -0.768889f, -0.759549f, -0.749893f, -0.739926f, -0.729655f, -0.719086f, -0.708227f, +-0.697088f, -0.685677f, -0.674004f, -0.662078f, -0.649911f, -0.637512f, -0.624893f, -0.612063f, +-0.599033f, -0.585813f, -0.572414f, -0.558845f, -0.545117f, -0.531238f, -0.517218f, -0.503066f, +-0.488789f, -0.474396f, -0.459895f, -0.445293f, -0.430597f, -0.415813f, -0.400948f, -0.386007f, +-0.370997f, -0.355923f, -0.340789f, -0.325601f, -0.310363f, -0.295078f, -0.279752f, -0.264387f, +-0.248988f, -0.233557f, -0.218097f, -0.202612f, -0.187104f, -0.171576f, -0.156029f, -0.140467f, +-0.124891f, -0.109303f, -0.093706f, -0.078100f, -0.062487f, -0.046870f, -0.031248f, -0.015625f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_15[256] = { +-1.000000f, -1.000000f, -0.999999f, -0.999996f, -0.999990f, -0.999981f, -0.999966f, -0.999946f, +-0.999918f, -0.999881f, -0.999834f, -0.999775f, -0.999702f, -0.999615f, -0.999511f, -0.999387f, +-0.999243f, -0.999076f, -0.998882f, -0.998661f, -0.998409f, -0.998124f, -0.997802f, -0.997440f, +-0.997035f, -0.996583f, -0.996081f, -0.995524f, -0.994908f, -0.994229f, -0.993482f, -0.992661f, +-0.991762f, -0.990778f, -0.989704f, -0.988534f, -0.987260f, -0.985876f, -0.984375f, -0.982748f, +-0.980989f, -0.979089f, -0.977038f, -0.974829f, -0.972452f, -0.969898f, -0.967157f, -0.964218f, +-0.961074f, -0.957712f, -0.954123f, -0.950296f, -0.946223f, -0.941893f, -0.937297f, -0.932426f, +-0.927270f, -0.921822f, -0.916075f, -0.910022f, -0.903657f, -0.896976f, -0.889974f, -0.882649f, +-0.875000f, -0.867024f, -0.858724f, -0.850101f, -0.841157f, -0.831897f, -0.822325f, -0.812447f, +-0.802270f, -0.791801f, -0.781047f, -0.770018f, -0.758723f, -0.747171f, -0.735373f, -0.723337f, +-0.711074f, -0.698593f, -0.685907f, -0.673023f, -0.659952f, -0.646704f, -0.633288f, -0.619714f, +-0.605989f, -0.592123f, -0.578125f, -0.564001f, -0.549760f, -0.535409f, -0.520954f, -0.506403f, +-0.491762f, -0.477036f, -0.462232f, -0.447354f, -0.432408f, -0.417399f, -0.402331f, -0.387208f, +-0.372035f, -0.356815f, -0.341552f, -0.326249f, -0.310909f, -0.295536f, -0.280132f, -0.264701f, +-0.249243f, -0.233762f, -0.218261f, -0.202740f, -0.187202f, -0.171650f, -0.156084f, -0.140506f, +-0.124918f, -0.109321f, -0.093716f, -0.078106f, -0.062490f, -0.046871f, -0.031249f, -0.015625f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/utils/AsymWaveShaper.h b/plugins/community/repos/squinkylabs-plug1/dsp/utils/AsymWaveShaper.h new file mode 100644 index 00000000..b44c0289 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/dsp/utils/AsymWaveShaper.h @@ -0,0 +1,84 @@ +#pragma once + +#include +#include +#include +#include "LookupTable.h" + +using Spline = std::vector< std::pair >; + + + +class NonUniformLookup +{ +public: + void add(double x, double y) + { + data[x] = y; + } + double lookup(double x) + { + // printf("lookup %f\n", x); + auto l = data.lower_bound(x); + assert(l != data.end()); + // printf("lower = %f, %f\n", l->first, l->second); + auto p = l; + p--; + if (p == data.end()) { + assert(l->first == x); + return l->second; + } + assert(p != data.end()); + // printf("p = %f, %f\n", p->first, p->second); + + // construct line y = y0 + (y1 -y0)/(x1 - x0) * x-x0; + // = b + a(x -b); + const double b = p->second; + const double a = (l->second - p->second) / (l->first - p->first); + + const double ret = b + a * (x - p->first); + // printf("ret = %f\n", ret); + + return ret; + } +private: + std::map data; +}; + +class AsymWaveShaper +{ +public: + const static int iNumPoints = 256; + const static int iSymmetryTables = 16; +private: + LookupTableParams tables[iSymmetryTables]; +public: + + AsymWaveShaper(); + + float lookup(float x, int index) const + { + float x_scaled = 0; + if (x >= 1) { + x_scaled = iNumPoints - 1; + } else if (x < -1) { + x_scaled = 0; + } else { + x_scaled = (x + 1) * iNumPoints / 2; + } + + assert(index >= 0 && index < iSymmetryTables); + const LookupTableParams& table = tables[index]; + // TODO: we are going outside of domain!. + const float y = LookupTable::lookup(table, x_scaled, true); + // printf("lookup %f -> %f ret %f\n", x, x_scaled, y); + return y; + } + + static void genTableValues(const Spline& spline, int numPoints); + static void genTable(int index, double symmetry); + static Spline makeSplineRight(double symmetry); + static Spline makeSplineLeft(double symmetry); + static std::pair calcPoint(const Spline& spline, double t); +}; + diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/utils/AudioMath.cpp b/plugins/community/repos/squinkylabs-plug1/dsp/utils/AudioMath.cpp index caad2afc..17216d89 100644 --- a/plugins/community/repos/squinkylabs-plug1/dsp/utils/AudioMath.cpp +++ b/plugins/community/repos/squinkylabs-plug1/dsp/utils/AudioMath.cpp @@ -1,10 +1,12 @@ #include #include +#include #include "AudioMath.h" #include "LookupTable.h" #include "ObjectCache.h" + const double AudioMath::Pi = 3.1415926535897932384626433832795028841971; const double AudioMath::Pi_2 = 1.5707963267948966192313216916397514420986; const double AudioMath::Ln2 = 0.693147180559945309417; @@ -62,7 +64,7 @@ std::function AudioMath::makeFunc_AudioTaper(double dbAtten) }; } -AudioMath::ScaleFun AudioMath::makeBipolarAudioScaler(float y0, float y1) +AudioMath::ScaleFun AudioMath::makeScalerWithBipolarAudioTrim(float y0, float y1) { // Use a cached singleton for the lookup table - don't need to have unique copies std::shared_ptr> lookup = ObjectCache::getBipolarAudioTaper(); @@ -82,6 +84,57 @@ AudioMath::ScaleFun AudioMath::makeBipolarAudioScaler(float y0, float y1) }; } +AudioMath::SimpleScaleFun AudioMath::makeSimpleScalerAudioTaper(float y0, float y1) +{ + // Use a cached singleton for the lookup table - don't need to have unique copies + std::shared_ptr> lookup = ObjectCache::getAudioTaper(); + const float x0 = 0; + const float x1 = 1; + const float a = (y1 - y0) / (x1 - x0); + const float b = y0 - a * x0; + + return [a, b, lookup](float cv, float knob) { + // get the clipped sum of the inputs + float x = cv + knob; + x = std::max(-5.0f, x); + x = std::min(5.0f, x); + + // adjust to 0..1 and map + x += 5.f; + x /= 10.f; + assert(x >= 0 && x <= 1); + x = LookupTable::lookup(*lookup, x); + + // scale to output + return a * x + b; + }; +} + +// TODO: move this to audio math +AudioMath::RandomUniformFunc AudioMath::random() +{ + return []() { + std::default_random_engine generator{57}; + std::uniform_real_distribution distribution{0, 1.0}; + return distribution(generator); + }; +} + + // declare some test variables here int _numLookupParams = 0; -int _numBiquads = 0; \ No newline at end of file +int _numBiquads = 0; + + +// TOOD: put these in their own file +// These are the MinBLEP lookup tables from the Befaco EvenVCO +#if defined(_MSC_VER) +#pragma warning(disable: 132 4305) +#endif +float sqsawTable[2048] = { + 0.00221683, 0.00288535, 0.00382874, 0.00493397, 0.006088, 0.00717778, 0.0080962, 0.00887097, 0.00957292, 0.0102536, 0.0109644, 0.0117569, 0.0126765, 0.0137091, 0.014811, 0.0159388, 0.0170492, 0.0180987, 0.0190781, 0.0200243, 0.0209441, 0.0218439, 0.0227299, 0.023608, 0.0244664, 0.0253042, 0.0261303, 0.0269533, 0.0277823, 0.0286234, 0.0294678, 0.0303142, 0.0311635, 0.0320168, 0.0328749, 0.0337369, 0.0346001, 0.0354672, 0.0363411, 0.0372246, 0.0381206, 0.0390346, 0.039964, 0.0409017, 0.0418407, 0.042774, 0.0436961, 0.0446189, 0.0455409, 0.0464563, 0.047359, 0.0482433, 0.0491029, 0.049938, 0.0507563, 0.0515658, 0.0523744, 0.0531901, 0.0540067, 0.0548165, 0.0556269, 0.0564456, 0.0572799, 0.0581378, 0.0590252, 0.059934, 0.0608529, 0.0617707, 0.0626761, 0.0635623, 0.0644415, 0.0653142, 0.0661789, 0.0670339, 0.0678778, 0.0687058, 0.0695165, 0.070317, 0.0711147, 0.0719168, 0.0727304, 0.0735539, 0.0743828, 0.075215, 0.0760485, 0.0768811, 0.07771, 0.0785292, 0.0793449, 0.0801652, 0.080998, 0.0818513, 0.082737, 0.0836581, 0.0845968, 0.0855346, 0.0864532, 0.0873342, 0.0881769, 0.0889945, 0.0897932, 0.0905797, 0.0913603, 0.0921398, 0.0929034, 0.0936544, 0.0944023, 0.0951566, 0.0959267, 0.0967196, 0.0975297, 0.0983516, 0.09918, 0.10001, 0.100836, 0.101666, 0.102504, 0.103342, 0.104172, 0.104985, 0.105773, 0.106533, 0.107272, 0.107999, 0.108722, 0.109449, 0.110184, 0.110909, 0.11163, 0.112359, 0.113107, 0.113883, 0.114701, 0.115559, 0.116438, 0.117321, 0.118187, 0.119019, 0.11982, 0.120602, 0.121371, 0.122131, 0.122889, 0.123648, 0.124405, 0.125157, 0.125905, 0.126649, 0.127387, 0.128116, 0.128828, 0.129533, 0.130244, 0.130971, 0.131727, 0.132522, 0.133348, 0.134189, 0.13503, 0.135856, 0.136654, 0.137426, 0.138182, 0.13893, 0.13968, 0.140439, 0.141215, 0.142006, 0.142805, 0.143602, 0.144389, 0.145159, 0.145905, 0.146633, 0.147351, 0.148066, 0.148785, 0.149516, 0.150256, 0.151, 0.151747, 0.152495, 0.153242, 0.15399, 0.154742, 0.155496, 0.156248, 0.156992, 0.157725, 0.158443, 0.15915, 0.159849, 0.160542, 0.16123, 0.161916, 0.162592, 0.163258, 0.163921, 0.164589, 0.165267, 0.165962, 0.166664, 0.167374, 0.168094, 0.168827, 0.169575, 0.170347, 0.171149, 0.171967, 0.172788, 0.173595, 0.174376, 0.175127, 0.17586, 0.17658, 0.177293, 0.178003, 0.178717, 0.179439, 0.180163, 0.180879, 0.181579, 0.182254, 0.182887, 0.183461, 0.184006, 0.184553, 0.185136, 0.185786, 0.186519, 0.187312, 0.188146, 0.189001, 0.189859, 0.190701, 0.191556, 0.192426, 0.193296, 0.194149, 0.194968, 0.195739, 0.19647, 0.197172, 0.197852, 0.198518, 0.199177, 0.199827, 0.200455, 0.20107, 0.201683, 0.202303, 0.202941, 0.203594, 0.204255, 0.204924, 0.205599, 0.206279, 0.206967, 0.207672, 0.208389, 0.209106, 0.209812, 0.210497, 0.211154, 0.211791, 0.212413, 0.213022, 0.213622, 0.214216, 0.214789, 0.215338, 0.21588, 0.216432, 0.21701, 0.217632, 0.218309, 0.219022, 0.219748, 0.220466, 0.221152, 0.221789, 0.222391, 0.22297, 0.223536, 0.224097, 0.224665, 0.225227, 0.225769, 0.22631, 0.226866, 0.227457, 0.2281, 0.228809, 0.229566, 0.230349, 0.231134, 0.231897, 0.232619, 0.233316, 0.233998, 0.234672, 0.235345, 0.236023, 0.236715, 0.237415, 0.238118, 0.238815, 0.239499, 0.240162, 0.240802, 0.241424, 0.242035, 0.242638, 0.24324, 0.243846, 0.244452, 0.245055, 0.245654, 0.24625, 0.246841, 0.247425, 0.248001, 0.248572, 0.249141, 0.249711, 0.250285, 0.250867, 0.251454, 0.252043, 0.252627, 0.253202, 0.253762, 0.254314, 0.254859, 0.255395, 0.255918, 0.256428, 0.256916, 0.257372, 0.257809, 0.258245, 0.258695, 0.259177, 0.259689, 0.260217, 0.260762, 0.26133, 0.261924, 0.262549, 0.263221, 0.263934, 0.264667, 0.2654, 0.266114, 0.266792, 0.267451, 0.268097, 0.268732, 0.269357, 0.269973, 0.270576, 0.271158, 0.271729, 0.2723, 0.272879, 0.273479, 0.274107, 0.274757, 0.275414, 0.276063, 0.27669, 0.277282, 0.277837, 0.278368, 0.278887, 0.279406, 0.279937, 0.280489, 0.281057, 0.281633, 0.282207, 0.282772, 0.283319, 0.283845, 0.284355, 0.284855, 0.285351, 0.285848, 0.286353, 0.286861, 0.287368, 0.287877, 0.288389, 0.288904, 0.289424, 0.289941, 0.290461, 0.290987, 0.291524, 0.292076, 0.292648, 0.293238, 0.293841, 0.294449, 0.295058, 0.295663, 0.296276, 0.296899, 0.297519, 0.298127, 0.29871, 0.299257, 0.299765, 0.300247, 0.300716, 0.301185, 0.301669, 0.302178, 0.302707, 0.303244, 0.303774, 0.304286, 0.304765, 0.305192, 0.305574, 0.305941, 0.306323, 0.306748, 0.307245, 0.307827, 0.308466, 0.309123, 0.309764, 0.310351, 0.310859, 0.311312, 0.311727, 0.312122, 0.312512, 0.312915, 0.313329, 0.313739, 0.314148, 0.314558, 0.314972, 0.315393, 0.315812, 0.316231, 0.316656, 0.317095, 0.317554, 0.318043, 0.318572, 0.319125, 0.319683, 0.320227, 0.320739, 0.321207, 0.321641, 0.322057, 0.322467, 0.322886, 0.323327, 0.32378, 0.324239, 0.324707, 0.325188, 0.325686, 0.326205, 0.326755, 0.327327, 0.327907, 0.328484, 0.329045, 0.329585, 0.330116, 0.33064, 0.331156, 0.331664, 0.332165, 0.332653, 0.33313, 0.3336, 0.334068, 0.334539, 0.33502, 0.33552, 0.33603, 0.336536, 0.337024, 0.337481, 0.337893, 0.338261, 0.3386, 0.338927, 0.339258, 0.33961, 0.339982, 0.340358, 0.340742, 0.341136, 0.341541, 0.34196, 0.342407, 0.342874, 0.343348, 0.343815, 0.344261, 0.344675, 0.345061, 0.345429, 0.345787, 0.346144, 0.346507, 0.346879, 0.347253, 0.347628, 0.348002, 0.348377, 0.34875, 0.349118, 0.349481, 0.349845, 0.350216, 0.350597, 0.350995, 0.351411, 0.351838, 0.352273, 0.352708, 0.353139, 0.353563, 0.353984, 0.354404, 0.354824, 0.355243, 0.355661, 0.356077, 0.356489, 0.356901, 0.357316, 0.357737, 0.358168, 0.358608, 0.359055, 0.359506, 0.35996, 0.360414, 0.360869, 0.361328, 0.361789, 0.36225, 0.362706, 0.363154, 0.363596, 0.364034, 0.364466, 0.364893, 0.365313, 0.365724, 0.366133, 0.366538, 0.366935, 0.367317, 0.367681, 0.36802, 0.368324, 0.368606, 0.36888, 0.369159, 0.369458, 0.369786, 0.370135, 0.370494, 0.370854, 0.371206, 0.371541, 0.371852, 0.372145, 0.372432, 0.372726, 0.373037, 0.373377, 0.373753, 0.374151, 0.374555, 0.374948, 0.375316, 0.375645, 0.375944, 0.376225, 0.376497, 0.376772, 0.377061, 0.377367, 0.377681, 0.377998, 0.378314, 0.378622, 0.378916, 0.379197, 0.379469, 0.379735, 0.380002, 0.380274, 0.380552, 0.380831, 0.38111, 0.381395, 0.381687, 0.381992, 0.382311, 0.382642, 0.382983, 0.383329, 0.383676, 0.384021, 0.384367, 0.384716, 0.385066, 0.385417, 0.385767, 0.386116, 0.38647, 0.386824, 0.387177, 0.387522, 0.387858, 0.388187, 0.388517, 0.388841, 0.389149, 0.389433, 0.389684, 0.389883, 0.390037, 0.390171, 0.39031, 0.390476, 0.390693, 0.390945, 0.391221, 0.391516, 0.391825, 0.392141, 0.392465, 0.392808, 0.393164, 0.393524, 0.393881, 0.394226, 0.39456, 0.394891, 0.395217, 0.395538, 0.395851, 0.396157, 0.396451, 0.396736, 0.397016, 0.397296, 0.39758, 0.397874, 0.39818, 0.39849, 0.398797, 0.399095, 0.399375, 0.399634, 0.399877, 0.400107, 0.400331, 0.400554, 0.40078, 0.401001, 0.401216, 0.401431, 0.401652, 0.401885, 0.402136, 0.402411, 0.402699, 0.402992, 0.403279, 0.403551, 0.403804, 0.404051, 0.404289, 0.404515, 0.404728, 0.404924, 0.405086, 0.405215, 0.405334, 0.405465, 0.405629, 0.405848, 0.406118, 0.406424, 0.406751, 0.407085, 0.407412, 0.407728, 0.408054, 0.408384, 0.408707, 0.409011, 0.409286, 0.40952, 0.409718, 0.409896, 0.410074, 0.410268, 0.410495, 0.410756, 0.411038, 0.411329, 0.411618, 0.411891, 0.412139, 0.41237, 0.412589, 0.412802, 0.413013, 0.413229, 0.413447, 0.413663, 0.413878, 0.414097, 0.414321, 0.414556, 0.4148, 0.415051, 0.415309, 0.41557, 0.415834, 0.416101, 0.416385, 0.416677, 0.416965, 0.417238, 0.417483, 0.417691, 0.417871, 0.418029, 0.418173, 0.418311, 0.418451, 0.418583, 0.418702, 0.418816, 0.418934, 0.419064, 0.419214, 0.419382, 0.419563, 0.419752, 0.419942, 0.42013, 0.420312, 0.420491, 0.420669, 0.420849, 0.42103, 0.421215, 0.421399, 0.421577, 0.421758, 0.421949, 0.42216, 0.422397, 0.422673, 0.422978, 0.423295, 0.423607, 0.4239, 0.424162, 0.424418, 0.424664, 0.424892, 0.425091, 0.425255, 0.425365, 0.425417, 0.425437, 0.425453, 0.425491, 0.425579, 0.425726, 0.425913, 0.426116, 0.426313, 0.426481, 0.426598, 0.42666, 0.426688, 0.426709, 0.426745, 0.42682, 0.426953, 0.427125, 0.427325, 0.427539, 0.427754, 0.427957, 0.428158, 0.428365, 0.428571, 0.42877, 0.428954, 0.429116, 0.429254, 0.429373, 0.429483, 0.429592, 0.429707, 0.429833, 0.429956, 0.43008, 0.430211, 0.430356, 0.43052, 0.430711, 0.430923, 0.431151, 0.431385, 0.431618, 0.431843, 0.432075, 0.432315, 0.43255, 0.432772, 0.432969, 0.43313, 0.43326, 0.433365, 0.433455, 0.433539, 0.433624, 0.4337, 0.433748, 0.43379, 0.433848, 0.433947, 0.434111, 0.434373, 0.434713, 0.435083, 0.435437, 0.435726, 0.435907, 0.435996, 0.436022, 0.436011, 0.435989, 0.435982, 0.435999, 0.436012, 0.436022, 0.436035, 0.436054, 0.436083, 0.436125, 0.436177, 0.436234, 0.436294, 0.436352, 0.436403, 0.436434, 0.436453, 0.436478, 0.436526, 0.436614, 0.43676, 0.436966, 0.437211, 0.43747, 0.43772, 0.437936, 0.438113, 0.438274, 0.43842, 0.438554, 0.438679, 0.438796, 0.438898, 0.438986, 0.439067, 0.439145, 0.439229, 0.439323, 0.439421, 0.439522, 0.439625, 0.43973, 0.439836, 0.439943, 0.440053, 0.440164, 0.440277, 0.440389, 0.440502, 0.440621, 0.440748, 0.440873, 0.440989, 0.441088, 0.441163, 0.441213, 0.441245, 0.441266, 0.441282, 0.441302, 0.441323, 0.441334, 0.441341, 0.441354, 0.441383, 0.441436, 0.44152, 0.441628, 0.441752, 0.441883, 0.442013, 0.442135, 0.44226, 0.44239, 0.442518, 0.442638, 0.442745, 0.44283, 0.442892, 0.442939, 0.442982, 0.443033, 0.4431, 0.443186, 0.443279, 0.443381, 0.443494, 0.443621, 0.443764, 0.443936, 0.444132, 0.444338, 0.444539, 0.44472, 0.444871, 0.445005, 0.445125, 0.445231, 0.445322, 0.445397, 0.445451, 0.445479, 0.445491, 0.445496, 0.445505, 0.445526, 0.445556, 0.445586, 0.445621, 0.445663, 0.445715, 0.445779, 0.445858, 0.445947, 0.446044, 0.446145, 0.446248, 0.44635, 0.446452, 0.446557, 0.446667, 0.446784, 0.446909, 0.447046, 0.447194, 0.447348, 0.447505, 0.447661, 0.447814, 0.447987, 0.448173, 0.448351, 0.4485, 0.448599, 0.448623, 0.448552, 0.448426, 0.448289, 0.448186, 0.448161, 0.448239, 0.448392, 0.44859, 0.448805, 0.449007, 0.449168, 0.449302, 0.449424, 0.449536, 0.449637, 0.449728, 0.44981, 0.449878, 0.449934, 0.449982, 0.450025, 0.450064, 0.450101, 0.45013, 0.450154, 0.450176, 0.450199, 0.450225, 0.450252, 0.450279, 0.450307, 0.450338, 0.450373, 0.450413, 0.450461, 0.450514, 0.45057, 0.450625, 0.450677, 0.450725, 0.450775, 0.450825, 0.45087, 0.450906, 0.450929, 0.450938, 0.450934, 0.450921, 0.450901, 0.450877, 0.45085, 0.450811, 0.450761, 0.45071, 0.450664, 0.450633, 0.450621, 0.45062, 0.450628, 0.450649, 0.450683, 0.450732, 0.450802, 0.450892, 0.450997, 0.45111, 0.451225, 0.451334, 0.451451, 0.451579, 0.451705, 0.451819, 0.451909, 0.451964, 0.451985, 0.451981, 0.451963, 0.451939, 0.451919, 0.451906, 0.451887, 0.451865, 0.451845, 0.451832, 0.45183, 0.451841, 0.451861, 0.451888, 0.451919, 0.451949, 0.451977, 0.452007, 0.452039, 0.452071, 0.452102, 0.452129, 0.452153, 0.452177, 0.452199, 0.452217, 0.452231, 0.452237, 0.452233, 0.452219, 0.452199, 0.452179, 0.452163, 0.452155, 0.452151, 0.452149, 0.452152, 0.452159, 0.452173, 0.452194, 0.452227, 0.452267, 0.45231, 0.452352, 0.452388, 0.452422, 0.45246, 0.452497, 0.452525, 0.452538, 0.452531, 0.452507, 0.452471, 0.45242, 0.452351, 0.452262, 0.452146, 0.451988, 0.451802, 0.451605, 0.451418, 0.45126, 0.451143, 0.451054, 0.450984, 0.450924, 0.450866, 0.450801, 0.450725, 0.450645, 0.450566, 0.450496, 0.45044, 0.450406, 0.450404, 0.450422, 0.450449, 0.450469, 0.450469, 0.450438, 0.450381, 0.450308, 0.450229, 0.450154, 0.450093, 0.450049, 0.450013, 0.449983, 0.449957, 0.449933, 0.449908, 0.449892, 0.449885, 0.449877, 0.449858, 0.449822, 0.44976, 0.449688, 0.449602, 0.449498, 0.449371, 0.449217, 0.449034, 0.448826, 0.448593, 0.448337, 0.448059, 0.447758, 0.447436, 0.44709, 0.446721, 0.446331, 0.445918, 0.445483, 0.445027, 0.444548, 0.444047, 0.443524, 0.44298, 0.442413, 0.441818, 0.441202, 0.440567, 0.439919, 0.439262, 0.438614, 0.437974, 0.437319, 0.436626, 0.43587, 0.435049, 0.434402, 0.433835, 0.433123, 0.432039, 0.430358, 0.428368, 0.427503, 0.426601, 0.424216, 0.418905, 0.409225, 0.396402, 0.382576, 0.365547, 0.34307, 0.312906, 0.272788, 0.221013, 0.159633, 0.091601, 0.0198945, -0.0525338, -0.124172, -0.201123, -0.28145, -0.361514, -0.437677, -0.506323, -0.565947, -0.61981, -0.668566, -0.712743, -0.752877, -0.789483, -0.821637, -0.849041, -0.872562, -0.893078, -0.911459, -0.928371, -0.94253, -0.953999, -0.963373, -0.971244, -0.978205, -0.984408, -0.988963, -0.992177, -0.994451, -0.996186, -0.997786, -0.999106, -0.999791, -1.0, -0.999892, -0.999627, -0.99935, -0.998924, -0.998293, -0.997489, -0.996544, -0.99549, -0.994324, -0.992938, -0.9914, -0.989797, -0.988217, -0.98675, -0.985459, -0.984295, -0.983177, -0.982026, -0.98076, -0.9793, -0.977576, -0.975661, -0.973667, -0.971707, -0.969891, -0.968331, -0.967043, -0.965911, -0.964815, -0.963632, -0.962241, -0.960532, -0.958548, -0.956417, -0.954273, -0.952245, -0.950466, -0.948995, -0.947739, -0.946587, -0.94543, -0.944156, -0.942662, -0.940966, -0.939147, -0.937271, -0.935407, -0.933622, -0.931964, -0.930389, -0.928864, -0.927356, -0.925834, -0.924266, -0.922637, -0.920969, -0.919284, -0.917603, -0.915947, -0.914338, -0.912774, -0.91124, -0.909724, -0.908215, -0.9067, -0.905174, -0.903657, -0.902144, -0.900623, -0.899085, -0.897519, -0.895907, -0.894246, -0.892562, -0.890886, -0.889245, -0.887668, -0.886183, -0.884763, -0.883367, -0.881952, -0.880476, -0.878893, -0.877185, -0.875402, -0.873605, -0.871856, -0.870213, -0.868734, -0.867403, -0.866151, -0.864909, -0.863607, -0.862175, -0.86057, -0.85884, -0.857056, -0.855289, -0.853607, -0.852081, -0.850725, -0.849477, -0.848268, -0.847031, -0.845696, -0.844208, -0.842597, -0.840914, -0.839205, -0.83752, -0.835907, -0.834385, -0.83292, -0.831484, -0.830055, -0.828605, -0.827111, -0.825575, -0.824013, -0.82244, -0.820868, -0.81931, -0.817777, -0.816267, -0.814769, -0.813273, -0.811771, -0.810251, -0.808705, -0.807134, -0.805552, -0.803974, -0.802414, -0.800886, -0.799393, -0.797924, -0.796471, -0.795025, -0.793575, -0.792118, -0.790676, -0.78924, -0.787798, -0.786332, -0.784828, -0.78326, -0.781618, -0.77994, -0.778267, -0.776639, -0.775098, -0.773676, -0.772344, -0.771056, -0.769763, -0.768417, -0.766973, -0.765427, -0.763815, -0.762179, -0.760556, -0.758986, -0.757505, -0.756108, -0.754755, -0.753405, -0.752018, -0.750554, -0.748967, -0.747275, -0.745539, -0.743822, -0.742186, -0.740694, -0.739378, -0.738183, -0.737041, -0.735882, -0.734639, -0.73325, -0.731736, -0.730147, -0.728531, -0.726936, -0.725409, -0.723977, -0.722606, -0.721272, -0.719951, -0.718621, -0.717259, -0.715868, -0.714464, -0.713052, -0.711638, -0.710226, -0.708824, -0.707431, -0.706042, -0.704652, -0.703255, -0.701847, -0.700422, -0.698983, -0.697535, -0.696081, -0.694629, -0.693182, -0.691736, -0.690286, -0.688838, -0.687396, -0.685967, -0.684557, -0.683176, -0.681815, -0.680459, -0.679094, -0.677703, -0.676271, -0.674794, -0.673291, -0.671787, -0.670306, -0.668872, -0.66751, -0.666213, -0.664942, -0.663664, -0.66234, -0.660935, -0.659418, -0.657823, -0.656203, -0.654607, -0.653087, -0.651693, -0.650422, -0.649228, -0.648061, -0.64687, -0.645608, -0.64424, -0.642798, -0.641314, -0.639821, -0.638352, -0.636938, -0.635586, -0.634273, -0.63298, -0.631688, -0.630378, -0.629033, -0.627661, -0.626273, -0.624876, -0.623477, -0.622085, -0.620705, -0.619334, -0.617966, -0.616596, -0.615218, -0.613827, -0.612416, -0.61099, -0.609555, -0.608122, -0.606697, -0.60529, -0.603898, -0.602516, -0.60114, -0.599766, -0.598392, -0.597014, -0.595638, -0.594262, -0.592886, -0.59151, -0.590132, -0.588747, -0.587351, -0.585953, -0.584565, -0.583196, -0.581857, -0.580561, -0.579296, -0.578045, -0.576789, -0.575509, -0.574187, -0.572823, -0.571433, -0.570038, -0.568655, -0.567304, -0.566, -0.564732, -0.563486, -0.562244, -0.560992, -0.559712, -0.558394, -0.557048, -0.555694, -0.55435, -0.553035, -0.551767, -0.550554, -0.549377, -0.548213, -0.547039, -0.545833, -0.544579, -0.543296, -0.541991, -0.540673, -0.539347, -0.538023, -0.536698, -0.535368, -0.534033, -0.532691, -0.531345, -0.529992, -0.528625, -0.527247, -0.525867, -0.524493, -0.523133, -0.521796, -0.520479, -0.519176, -0.517878, -0.51658, -0.515273, -0.513957, -0.512638, -0.511316, -0.509991, -0.508665, -0.507338, -0.506007, -0.504673, -0.503337, -0.502003, -0.500672, -0.499347, -0.498028, -0.496714, -0.495401, -0.494085, -0.492762, -0.491429, -0.490084, -0.488734, -0.487386, -0.486047, -0.484726, -0.483416, -0.482114, -0.480822, -0.479543, -0.478281, -0.477039, -0.475822, -0.474626, -0.473441, -0.47226, -0.471073, -0.469878, -0.468688, -0.467499, -0.466305, -0.465101, -0.463881, -0.462642, -0.461385, -0.460117, -0.458846, -0.457581, -0.456327, -0.455092, -0.453867, -0.452643, -0.451408, -0.450154, -0.448868, -0.447541, -0.44619, -0.444838, -0.443506, -0.442214, -0.44098, -0.439791, -0.438628, -0.437473, -0.436306, -0.43511, -0.433885, -0.432645, -0.431396, -0.430143, -0.428894, -0.427652, -0.426416, -0.425183, -0.423949, -0.422711, -0.421466, -0.420212, -0.418951, -0.417684, -0.416414, -0.415141, -0.413868, -0.412589, -0.411302, -0.410015, -0.408732, -0.407459, -0.406204, -0.404969, -0.403749, -0.402535, -0.401316, -0.400084, -0.398829, -0.397551, -0.396262, -0.394973, -0.393697, -0.392445, -0.391224, -0.390027, -0.388843, -0.387665, -0.386484, -0.385291, -0.384089, -0.382883, -0.381676, -0.380469, -0.379266, -0.378068, -0.37687, -0.375674, -0.374482, -0.373293, -0.372111, -0.370936, -0.369768, -0.368605, -0.367445, -0.366287, -0.365128, -0.36397, -0.362812, -0.361657, -0.360503, -0.359351, -0.358203, -0.357061, -0.355922, -0.354784, -0.353643, -0.352495, -0.351336, -0.350168, -0.348994, -0.34782, -0.346649, -0.345486, -0.344334, -0.343189, -0.342048, -0.340906, -0.339762, -0.338611, -0.337458, -0.336305, -0.335148, -0.333984, -0.33281, -0.331624, -0.330426, -0.329218, -0.328005, -0.326791, -0.32558, -0.324371, -0.323158, -0.321945, -0.320736, -0.319535, -0.318345, -0.317165, -0.315991, -0.314825, -0.313666, -0.312516, -0.311376, -0.310249, -0.309132, -0.30802, -0.30691, -0.305796, -0.304683, -0.30358, -0.302479, -0.301367, -0.300236, -0.299074, -0.297859, -0.296599, -0.295325, -0.294068, -0.292855, -0.291717, -0.290659, -0.289654, -0.28867, -0.287676, -0.286641, -0.28555, -0.284431, -0.28329, -0.282128, -0.280946, -0.279746, -0.278513, -0.277246, -0.27596, -0.274674, -0.273405, -0.272167, -0.270948, -0.26974, -0.268546, -0.26737, -0.266215, -0.265084, -0.263983, -0.262904, -0.261837, -0.260774, -0.259706, -0.258637, -0.257578, -0.256522, -0.255458, -0.254379, -0.253276, -0.252151, -0.251008, -0.249851, -0.248684, -0.247511, -0.246333, -0.245133, -0.243922, -0.242709, -0.241508, -0.240329, -0.239178, -0.238042, -0.236921, -0.235815, -0.234725, -0.233651, -0.232605, -0.231587, -0.230582, -0.229575, -0.228551, -0.227496, -0.226409, -0.225303, -0.22419, -0.223083, -0.221996, -0.22094, -0.219911, -0.218897, -0.217885, -0.216863, -0.215818, -0.214744, -0.213651, -0.212546, -0.211438, -0.210337, -0.209249, -0.208177, -0.207112, -0.20605, -0.204984, -0.203909, -0.202819, -0.201716, -0.200605, -0.19949, -0.198377, -0.197271, -0.196173, -0.195078, -0.193985, -0.192896, -0.191809, -0.190725, -0.189653, -0.188591, -0.187529, -0.186458, -0.185368, -0.18425, -0.183097, -0.181922, -0.180741, -0.179569, -0.17842, -0.1773, -0.17619, -0.175094, -0.174015, -0.172958, -0.171927, -0.170938, -0.169986, -0.169054, -0.168122, -0.167172, -0.166186, -0.165179, -0.164157, -0.163123, -0.162077, -0.161021, -0.159952, -0.158857, -0.157749, -0.15664, -0.155542, -0.15447, -0.153432, -0.152424, -0.151428, -0.150429, -0.149413, -0.148364, -0.147283, -0.146183, -0.145069, -0.14395, -0.142834, -0.141724, -0.140608, -0.139489, -0.138373, -0.137265, -0.136171, -0.135093, -0.134025, -0.132968, -0.131919, -0.130878, -0.129844, -0.128821, -0.127809, -0.126804, -0.125801, -0.124797, -0.123787, -0.122773, -0.121758, -0.120744, -0.119733, -0.118728, -0.117733, -0.11675, -0.115771, -0.114792, -0.113806, -0.112807, -0.111794, -0.110771, -0.109741, -0.108707, -0.107672, -0.106636, -0.105591, -0.104541, -0.103491, -0.102448, -0.10142, -0.100412, -0.0994198, -0.0984399, -0.0974673, -0.0964976, -0.0955265, -0.0945581, -0.0935982, -0.0926402, -0.0916777, -0.0907045, -0.0897139, -0.0887001, -0.0876694, -0.0866321, -0.0855982, -0.0845778, -0.0835811, -0.0826095, -0.0816517, -0.0806952, -0.0797277, -0.0787369, -0.0777107, -0.0766526, -0.075579, -0.0745061, -0.0734503, -0.0724281, -0.0714473, -0.0704955, -0.0695569, -0.0686153, -0.067655, -0.0666595, -0.0656228, -0.0645626, -0.0634995, -0.0624539, -0.0614458, -0.0604904, -0.0595764, -0.0586898, -0.0578174, -0.0569453, -0.0560601, -0.0551719, -0.0542917, -0.0534099, -0.0525163, -0.0516014, -0.0506555, -0.0496757, -0.0486708, -0.0476511, -0.0466261, -0.0456061, -0.044596, -0.0435836, -0.0425703, -0.0415587, -0.040552, -0.0395533, -0.0385637, -0.0375815, -0.0366045, -0.0356314, -0.0346604, -0.0336902, -0.0327247, -0.0317635, -0.0308037, -0.0298422, -0.0288762, -0.0279032, -0.026925, -0.025943, -0.0249582, -0.0239714, -0.0229839, -0.0219896, -0.0209822, -0.0199722, -0.0189704, -0.0179881, -0.0170357, -0.0161152, -0.0152188, -0.0143404, -0.0134736, -0.0126123, -0.0117534, -0.0109179, -0.0100977, -0.00927668, -0.00843894, -0.0075681, -0.00664104, -0.0056472, -0.00462252, -0.00360524, -0.00263304, -0.00174417 +}; + +float sqtriTable[2048] = { + 0.00205034, 0.00360933, 0.00578754, 0.00835358, 0.0110761, 0.0137236, 0.0160981, 0.0183687, 0.020617, 0.0228593, 0.0251122, 0.027392, 0.0297206, 0.0321003, 0.034499, 0.0368839, 0.0392222, 0.0414812, 0.0436497, 0.0457532, 0.0478204, 0.0498797, 0.0519597, 0.0540801, 0.0562156, 0.0583599, 0.0605111, 0.0626673, 0.0648265, 0.0669885, 0.069155, 0.0713261, 0.0735021, 0.0756831, 0.0778692, 0.0800601, 0.0822559, 0.0844568, 0.086663, 0.0888749, 0.091096, 0.0933308, 0.095572, 0.097812, 0.100043, 0.102257, 0.104463, 0.106663, 0.108853, 0.111028, 0.113181, 0.115309, 0.11741, 0.11949, 0.121555, 0.123613, 0.125667, 0.127712, 0.129738, 0.131757, 0.133783, 0.135828, 0.137904, 0.140002, 0.142116, 0.144248, 0.146397, 0.148564, 0.150756, 0.152981, 0.155227, 0.157478, 0.159719, 0.161935, 0.164142, 0.166348, 0.16854, 0.170706, 0.172831, 0.174903, 0.176915, 0.178884, 0.180829, 0.18277, 0.184725, 0.186695, 0.188658, 0.190622, 0.192593, 0.194577, 0.196582, 0.198611, 0.200659, 0.202715, 0.204773, 0.206824, 0.208863, 0.210895, 0.212924, 0.214952, 0.216985, 0.219024, 0.221064, 0.223103, 0.225147, 0.227201, 0.229273, 0.231368, 0.23349, 0.235633, 0.237784, 0.239934, 0.242072, 0.244201, 0.246336, 0.248466, 0.250583, 0.252677, 0.254738, 0.256759, 0.258749, 0.260722, 0.26269, 0.264667, 0.26666, 0.268659, 0.27066, 0.272662, 0.274664, 0.276664, 0.278666, 0.280672, 0.282676, 0.284675, 0.286663, 0.288636, 0.290596, 0.292546, 0.294487, 0.296422, 0.298354, 0.300276, 0.30218, 0.304077, 0.305982, 0.307905, 0.309861, 0.311873, 0.313923, 0.315983, 0.318021, 0.320007, 0.321913, 0.323747, 0.325536, 0.327304, 0.329078, 0.330883, 0.33272, 0.334568, 0.336425, 0.338291, 0.340166, 0.342047, 0.34393, 0.345818, 0.347717, 0.349633, 0.351571, 0.353541, 0.355551, 0.357581, 0.359612, 0.361626, 0.363604, 0.365556, 0.367494, 0.369414, 0.371311, 0.373181, 0.375019, 0.376812, 0.378576, 0.380325, 0.382078, 0.38385, 0.385646, 0.387452, 0.389264, 0.391078, 0.39289, 0.394698, 0.396507, 0.398318, 0.400125, 0.401927, 0.403717, 0.405494, 0.407256, 0.40901, 0.41076, 0.41251, 0.414267, 0.416024, 0.41778, 0.419538, 0.421301, 0.423072, 0.424857, 0.426659, 0.428472, 0.430287, 0.432097, 0.433893, 0.435678, 0.437459, 0.439233, 0.440997, 0.442746, 0.444477, 0.446184, 0.44787, 0.449548, 0.451226, 0.452915, 0.454622, 0.456343, 0.45807, 0.459797, 0.461518, 0.463227, 0.464921, 0.466606, 0.468285, 0.469962, 0.47164, 0.473324, 0.475015, 0.476707, 0.478398, 0.480083, 0.481759, 0.483424, 0.485082, 0.486734, 0.488379, 0.490016, 0.491647, 0.493268, 0.49488, 0.496487, 0.49809, 0.499692, 0.501294, 0.502892, 0.504487, 0.506082, 0.507679, 0.509283, 0.510886, 0.512485, 0.514089, 0.515705, 0.51734, 0.519007, 0.520729, 0.522484, 0.524239, 0.52596, 0.527614, 0.529175, 0.530657, 0.532094, 0.533515, 0.534954, 0.53644, 0.537952, 0.539475, 0.541016, 0.542579, 0.544171, 0.545804, 0.547497, 0.549225, 0.550958, 0.552666, 0.554319, 0.555907, 0.557451, 0.558968, 0.560474, 0.561985, 0.563516, 0.565063, 0.566615, 0.568164, 0.569701, 0.571217, 0.572711, 0.574192, 0.575659, 0.577106, 0.578532, 0.579932, 0.581296, 0.582629, 0.583943, 0.585252, 0.586569, 0.5879, 0.589228, 0.590557, 0.591892, 0.593238, 0.594602, 0.595985, 0.597385, 0.598797, 0.600218, 0.601643, 0.603069, 0.604501, 0.605938, 0.60738, 0.608827, 0.610277, 0.61173, 0.613187, 0.614649, 0.616114, 0.617584, 0.619058, 0.620545, 0.622044, 0.623545, 0.625037, 0.62651, 0.627958, 0.629403, 0.630838, 0.632249, 0.633624, 0.634949, 0.636201, 0.63738, 0.63852, 0.639657, 0.640828, 0.642065, 0.643361, 0.644693, 0.646047, 0.647405, 0.648753, 0.650083, 0.651415, 0.652746, 0.654072, 0.655389, 0.656694, 0.65799, 0.659279, 0.660559, 0.661826, 0.663078, 0.664312, 0.665526, 0.666724, 0.667911, 0.66909, 0.670267, 0.671435, 0.672582, 0.673724, 0.674874, 0.676046, 0.677253, 0.678497, 0.679767, 0.681053, 0.682348, 0.683643, 0.684935, 0.686242, 0.687554, 0.688863, 0.690158, 0.691429, 0.692672, 0.693896, 0.695105, 0.696306, 0.697506, 0.698708, 0.699909, 0.701106, 0.7023, 0.703489, 0.704673, 0.705857, 0.707048, 0.708236, 0.709408, 0.710553, 0.711661, 0.712721, 0.713743, 0.714746, 0.715747, 0.716765, 0.71781, 0.718864, 0.719926, 0.720996, 0.722076, 0.723167, 0.724272, 0.725391, 0.726519, 0.727651, 0.728784, 0.729915, 0.731064, 0.732222, 0.733373, 0.734501, 0.73559, 0.736625, 0.737607, 0.738559, 0.739503, 0.740458, 0.741448, 0.742462, 0.74349, 0.744529, 0.745577, 0.746634, 0.747701, 0.748797, 0.749906, 0.751012, 0.752098, 0.753145, 0.754144, 0.755107, 0.756048, 0.756979, 0.757913, 0.758861, 0.759812, 0.760762, 0.761714, 0.76267, 0.763634, 0.76461, 0.765599, 0.766594, 0.76759, 0.768579, 0.769555, 0.770513, 0.77146, 0.772402, 0.773347, 0.774301, 0.775274, 0.776266, 0.777269, 0.778272, 0.779266, 0.780239, 0.781189, 0.782124, 0.783047, 0.783961, 0.784869, 0.785775, 0.786678, 0.787576, 0.788467, 0.78935, 0.790223, 0.791082, 0.791926, 0.79276, 0.793589, 0.794419, 0.795256, 0.796098, 0.79694, 0.797783, 0.79863, 0.799482, 0.80034, 0.801205, 0.802075, 0.802947, 0.80382, 0.804691, 0.805566, 0.806452, 0.807338, 0.808211, 0.80906, 0.809874, 0.810645, 0.811386, 0.81211, 0.812829, 0.813558, 0.814304, 0.815056, 0.815811, 0.816566, 0.81732, 0.818071, 0.818818, 0.819561, 0.820303, 0.821046, 0.821791, 0.82254, 0.823293, 0.824047, 0.824803, 0.825561, 0.82632, 0.827082, 0.827848, 0.828615, 0.829381, 0.830142, 0.830896, 0.83163, 0.832351, 0.833072, 0.833807, 0.83457, 0.835373, 0.83622, 0.837092, 0.837972, 0.838841, 0.839683, 0.840509, 0.841335, 0.842146, 0.842929, 0.84367, 0.844352, 0.844957, 0.845509, 0.846038, 0.846575, 0.847151, 0.847775, 0.848416, 0.849075, 0.849755, 0.850456, 0.851183, 0.85195, 0.852752, 0.85357, 0.854387, 0.855184, 0.855947, 0.856677, 0.857391, 0.858105, 0.858834, 0.859596, 0.860406, 0.861255, 0.862121, 0.86298, 0.863811, 0.864592, 0.86533, 0.86604, 0.866731, 0.867413, 0.868096, 0.868783, 0.869464, 0.87014, 0.870811, 0.871481, 0.872149, 0.872817, 0.873483, 0.874146, 0.874806, 0.875459, 0.876105, 0.876739, 0.877367, 0.877993, 0.878622, 0.879259, 0.879906, 0.880561, 0.88122, 0.881879, 0.882535, 0.883182, 0.883819, 0.884449, 0.885079, 0.885715, 0.886363, 0.887038, 0.887757, 0.888489, 0.889203, 0.889868, 0.890451, 0.890953, 0.891393, 0.891786, 0.892143, 0.892477, 0.8928, 0.89311, 0.893397, 0.893649, 0.893858, 0.894011, 0.894122, 0.894207, 0.894245, 0.894213, 0.894089, 0.893852, 0.893506, 0.893064, 0.892538, 0.891937, 0.891272, 0.890547, 0.889748, 0.888879, 0.887944, 0.886946, 0.885891, 0.884773, 0.883588, 0.882343, 0.881043, 0.879694, 0.878299, 0.876848, 0.875342, 0.87379, 0.872197, 0.87057, 0.868908, 0.867204, 0.86546, 0.863682, 0.861874, 0.860039, 0.858177, 0.856286, 0.854363, 0.852406, 0.850414, 0.848385, 0.846323, 0.844227, 0.842095, 0.839925, 0.837715, 0.835457, 0.83315, 0.830807, 0.82844, 0.826061, 0.82368, 0.821277, 0.818856, 0.816424, 0.81399, 0.811562, 0.80915, 0.806747, 0.804345, 0.801937, 0.799514, 0.797067, 0.7946, 0.792119, 0.789623, 0.787112, 0.784586, 0.782038, 0.779443, 0.776826, 0.774214, 0.771636, 0.76912, 0.766686, 0.764314, 0.761978, 0.759652, 0.757309, 0.754927, 0.752535, 0.750137, 0.747724, 0.745288, 0.742821, 0.740305, 0.737727, 0.73512, 0.732517, 0.72995, 0.727452, 0.725032, 0.722667, 0.72033, 0.717997, 0.715644, 0.713254, 0.710851, 0.708438, 0.706014, 0.703575, 0.701121, 0.698641, 0.696136, 0.693616, 0.691095, 0.688585, 0.686098, 0.683641, 0.6812, 0.678761, 0.676308, 0.673827, 0.671306, 0.668755, 0.666184, 0.663599, 0.661012, 0.65843, 0.655845, 0.653253, 0.65066, 0.64807, 0.645488, 0.64292, 0.640369, 0.637827, 0.635287, 0.632742, 0.630183, 0.6276, 0.624998, 0.622388, 0.619784, 0.617199, 0.614645, 0.612122, 0.60962, 0.607132, 0.60465, 0.602168, 0.599682, 0.597203, 0.594727, 0.592252, 0.589774, 0.58729, 0.584793, 0.582286, 0.579778, 0.577277, 0.57479, 0.572324, 0.569874, 0.567436, 0.56501, 0.562592, 0.560181, 0.557782, 0.555401, 0.553029, 0.550655, 0.548272, 0.545869, 0.543444, 0.541004, 0.538559, 0.536116, 0.533683, 0.531266, 0.52886, 0.52646, 0.524064, 0.521671, 0.519279, 0.516893, 0.514518, 0.512143, 0.50976, 0.507362, 0.504938, 0.502484, 0.50001, 0.497529, 0.495055, 0.4926, 0.490175, 0.487775, 0.485386, 0.482995, 0.480589, 0.478154, 0.475672, 0.473159, 0.47064, 0.468141, 0.46569, 0.463311, 0.460999, 0.458729, 0.456477, 0.454215, 0.451918, 0.449584, 0.44723, 0.444865, 0.442494, 0.440126, 0.437766, 0.43542, 0.433079, 0.430733, 0.428371, 0.425982, 0.423555, 0.421087, 0.418597, 0.416105, 0.413627, 0.411182, 0.408763, 0.406361, 0.403972, 0.401595, 0.399227, 0.396868, 0.394526, 0.392195, 0.389871, 0.387547, 0.385218, 0.382886, 0.380556, 0.378225, 0.375891, 0.373548, 0.371195, 0.368828, 0.366452, 0.364071, 0.361691, 0.359317, 0.356948, 0.354579, 0.352212, 0.349851, 0.347498, 0.345159, 0.342839, 0.340534, 0.338236, 0.335938, 0.333632, 0.331313, 0.328992, 0.326667, 0.324335, 0.321992, 0.319635, 0.317258, 0.314862, 0.312455, 0.310044, 0.307636, 0.305238, 0.30284, 0.300442, 0.298047, 0.29566, 0.293284, 0.290916, 0.288546, 0.286183, 0.283838, 0.281522, 0.279246, 0.277034, 0.274878, 0.272745, 0.270602, 0.268418, 0.266164, 0.263851, 0.261499, 0.259129, 0.256758, 0.254405, 0.25208, 0.249766, 0.247457, 0.245146, 0.242826, 0.240491, 0.238132, 0.235759, 0.233382, 0.231014, 0.228664, 0.22634, 0.224037, 0.221748, 0.219468, 0.217193, 0.214919, 0.212651, 0.210391, 0.208136, 0.205878, 0.203614, 0.201338, 0.19905, 0.196756, 0.194458, 0.192161, 0.189868, 0.187578, 0.185288, 0.182999, 0.180713, 0.178434, 0.176164, 0.173903, 0.171648, 0.1694, 0.167155, 0.164912, 0.162674, 0.16045, 0.158231, 0.156008, 0.153768, 0.151502, 0.149211, 0.146903, 0.144578, 0.142234, 0.139871, 0.137483, 0.135038, 0.132556, 0.130074, 0.127626, 0.125247, 0.12297, 0.120783, 0.118646, 0.116519, 0.114362, 0.112133, 0.109827, 0.107474, 0.105099, 0.102728, 0.100388, 0.0980987, 0.0958451, 0.0936141, 0.0913946, 0.0891749, 0.0869439, 0.084699, 0.0824483, 0.0801958, 0.0779458, 0.0757025, 0.073469, 0.0712418, 0.06902, 0.066804, 0.0645945, 0.0623925, 0.0602031, 0.0580333, 0.0558718, 0.0537071, 0.0515269, 0.0493195, 0.0470875, 0.0448382, 0.0425741, 0.0402977, 0.0380118, 0.0357137, 0.0333861, 0.0310419, 0.028698, 0.0263721, 0.0240815, 0.0218298, 0.019603, 0.0173978, 0.015211, 0.013039, 0.0108804, 0.00875302, 0.00665008, 0.00455578, 0.00245417, 0.000329488, -0.00183022, -0.00401578, -0.00621704, -0.00842485, -0.0106295, -0.0128215, -0.0149995, -0.0171705, -0.019339, -0.0215093, -0.0236866, -0.0258755, -0.0280772, -0.0302866, -0.0324974, -0.0347034, -0.0368983, -0.0390758, -0.0412378, -0.0433932, -0.0455518, -0.0477225, -0.0499145, -0.0521294, -0.0543603, -0.056599, -0.0588375, -0.0610684, -0.0632876, -0.0655037, -0.0677167, -0.0699247, -0.0721264, -0.0743201, -0.0765108, -0.0786993, -0.0808802, -0.0830487, -0.0851995, -0.0873284, -0.089439, -0.0915334, -0.0936121, -0.0956753, -0.0977242, -0.0997517, -0.101752, -0.103736, -0.105717, -0.107708, -0.10972, -0.111741, -0.113769, -0.115806, -0.117857, -0.119925, -0.122017, -0.124138, -0.126278, -0.128425, -0.130568, -0.132695, -0.134806, -0.136907, -0.139004, -0.141099, -0.143195, -0.145297, -0.147412, -0.149533, -0.151649, -0.15375, -0.155824, -0.157859, -0.159851, -0.161824, -0.163796, -0.16579, -0.167825, -0.169907, -0.172019, -0.174147, -0.176275, -0.178386, -0.18047, -0.182542, -0.184604, -0.186657, -0.188702, -0.19074, -0.192767, -0.19478, -0.196787, -0.198791, -0.200797, -0.202811, -0.204826, -0.206843, -0.208861, -0.210884, -0.212911, -0.214946, -0.216992, -0.219043, -0.221093, -0.223135, -0.225165, -0.227174, -0.229169, -0.231159, -0.233156, -0.235167, -0.237203, -0.239262, -0.241337, -0.243416, -0.245491, -0.247553, -0.249605, -0.251657, -0.253703, -0.255735, -0.257747, -0.25973, -0.261673, -0.263588, -0.265491, -0.267398, -0.269325, -0.271286, -0.27327, -0.275268, -0.277268, -0.279258, -0.281228, -0.283188, -0.285145, -0.28709, -0.289016, -0.290914, -0.292773, -0.294573, -0.296337, -0.298095, -0.299876, -0.301711, -0.303622, -0.305594, -0.307596, -0.309599, -0.311574, -0.313491, -0.315348, -0.31717, -0.318975, -0.320785, -0.322621, -0.324499, -0.326405, -0.328328, -0.330257, -0.332182, -0.334093, -0.33599, -0.337882, -0.339769, -0.341653, -0.343533, -0.345412, -0.347287, -0.34916, -0.351029, -0.352894, -0.354757, -0.356615, -0.358471, -0.360323, -0.362171, -0.364016, -0.365857, -0.367695, -0.36953, -0.371361, -0.373187, -0.375008, -0.376822, -0.378625, -0.380423, -0.382218, -0.384017, -0.385823, -0.387639, -0.389461, -0.391286, -0.393111, -0.39493, -0.396744, -0.398568, -0.400394, -0.402209, -0.404001, -0.405756, -0.407455, -0.409092, -0.410699, -0.412308, -0.41395, -0.415656, -0.417438, -0.419271, -0.421126, -0.422972, -0.424782, -0.426532, -0.428234, -0.429909, -0.431572, -0.433239, -0.434928, -0.436646, -0.438382, -0.440124, -0.44186, -0.443579, -0.445267, -0.446914, -0.448535, -0.450152, -0.451785, -0.453454, -0.455174, -0.456935, -0.458721, -0.460517, -0.462308, -0.464079, -0.465839, -0.467598, -0.46935, -0.471092, -0.472818, -0.474526, -0.476213, -0.477884, -0.479547, -0.481207, -0.482871, -0.484546, -0.486228, -0.487908, -0.489577, -0.491226, -0.492845, -0.49444, -0.496014, -0.497571, -0.499113, -0.500644, -0.502158, -0.503643, -0.505112, -0.506581, -0.508065, -0.509579, -0.511122, -0.512685, -0.514262, -0.515847, -0.517436, -0.519026, -0.520626, -0.522233, -0.523842, -0.525448, -0.527046, -0.528634, -0.530216, -0.531793, -0.533369, -0.534945, -0.536525, -0.538114, -0.539707, -0.541298, -0.542879, -0.544443, -0.545986, -0.54751, -0.549021, -0.550522, -0.552019, -0.553515, -0.555017, -0.556521, -0.558019, -0.559502, -0.56096, -0.562383, -0.563768, -0.565125, -0.566468, -0.567813, -0.569172, -0.57054, -0.571898, -0.573261, -0.574643, -0.576059, -0.577525, -0.579052, -0.580624, -0.582221, -0.583824, -0.585413, -0.586978, -0.588546, -0.590111, -0.591667, -0.593204, -0.594717, -0.596205, -0.597674, -0.599125, -0.600559, -0.601977, -0.60338, -0.604764, -0.606132, -0.607485, -0.608824, -0.610153, -0.611468, -0.612764, -0.614047, -0.615324, -0.616602, -0.617887, -0.619173, -0.620458, -0.621743, -0.623032, -0.624325, -0.625623, -0.626915, -0.628209, -0.629514, -0.630837, -0.632189, -0.633581, -0.635008, -0.636455, -0.637902, -0.639332, -0.640731, -0.642122, -0.643505, -0.644871, -0.646211, -0.647516, -0.648774, -0.649981, -0.651157, -0.652322, -0.653495, -0.654696, -0.655923, -0.657163, -0.658409, -0.659653, -0.660887, -0.662105, -0.66331, -0.664506, -0.6657, -0.666896, -0.668101, -0.669321, -0.670557, -0.671796, -0.673025, -0.674233, -0.675407, -0.676535, -0.677633, -0.678721, -0.679822, -0.680957, -0.682138, -0.683351, -0.684586, -0.685833, -0.687084, -0.688329, -0.689583, -0.69085, -0.692118, -0.69337, -0.694595, -0.69578, -0.696928, -0.698049, -0.699154, -0.700253, -0.701358, -0.702475, -0.703598, -0.704718, -0.705824, -0.70691, -0.707963, -0.708967, -0.709937, -0.710897, -0.711871, -0.712884, -0.713953, -0.715065, -0.716205, -0.717357, -0.718507, -0.719638, -0.720763, -0.721891, -0.723013, -0.724122, -0.725208, -0.726265, -0.72729, -0.728292, -0.72928, -0.730265, -0.731255, -0.732248, -0.73323, -0.734211, -0.7352, -0.736205, -0.737238, -0.738311, -0.739412, -0.740521, -0.741619, -0.742686, -0.743708, -0.744696, -0.745661, -0.746613, -0.747561, -0.748516, -0.749476, -0.750434, -0.75139, -0.752341, -0.753286, -0.754223, -0.75515, -0.756071, -0.756987, -0.757902, -0.758819, -0.759739, -0.760661, -0.761581, -0.762501, -0.763418, -0.764331, -0.765241, -0.76615, -0.767056, -0.767958, -0.768856, -0.769747, -0.770623, -0.771492, -0.772365, -0.773251, -0.77416, -0.775104, -0.776077, -0.777062, -0.778043, -0.779001, -0.779922, -0.780812, -0.781681, -0.782536, -0.783385, -0.784234, -0.785089, -0.785946, -0.7868, -0.787644, -0.788474, -0.789283, -0.790058, -0.790807, -0.791546, -0.792295, -0.793069, -0.793885, -0.79474, -0.795619, -0.796505, -0.797383, -0.798236, -0.799069, -0.799894, -0.800708, -0.801505, -0.802282, -0.803032, -0.80375, -0.804442, -0.80512, -0.805793, -0.806474, -0.807167, -0.807859, -0.808552, -0.809247, -0.809944, -0.810644, -0.811352, -0.812064, -0.812778, -0.813491, -0.814198, -0.814899, -0.815595, -0.816289, -0.816978, -0.817663, -0.818343, -0.819017, -0.819684, -0.820347, -0.821007, -0.821667, -0.822327, -0.822983, -0.823637, -0.824291, -0.824947, -0.82561, -0.826279, -0.826953, -0.827631, -0.828313, -0.829, -0.82969, -0.830403, -0.831135, -0.831867, -0.832579, -0.833252, -0.833867, -0.834421, -0.834934, -0.835429, -0.835926, -0.836447, -0.836999, -0.837564, -0.838138, -0.838714, -0.839288, -0.839852, -0.840397, -0.840932, -0.841471, -0.842025, -0.842609, -0.843234, -0.843893, -0.844576, -0.845274, -0.845979, -0.846681, -0.847395, -0.848127, -0.848861, -0.849583, -0.850275, -0.850926, -0.851554, -0.85216, -0.852739, -0.853287, -0.853801, -0.85427, -0.854693, -0.855083, -0.855456, -0.855826, -0.856207, -0.85659, -0.856966, -0.857339, -0.857712, -0.858088, -0.858469, -0.858855, -0.859243, -0.85963, -0.860011, -0.860383, -0.86074, -0.861083, -0.861421, -0.861764, -0.862121, -0.862499, -0.862889, -0.863289, -0.863701, -0.86413, -0.864576, -0.865053, -0.865568, -0.866101, -0.866632, -0.867139, -0.867603, -0.86803, -0.868434, -0.868816, -0.86918, -0.869529, -0.869881, -0.870272, -0.870662, -0.871001, -0.871238, -0.871324, -0.87118, -0.870828, -0.870378, -0.869937, -0.869614, -0.869502, -0.869488, -0.869554, -0.869727, -0.870037, -0.870513, -0.871247, -0.872318, -0.873557, -0.874789, -0.875836, -0.876525, -0.876945, -0.877194, -0.877256, -0.877115, -0.876754, -0.87615, -0.875272, -0.874164, -0.872877, -0.871464, -0.869974, -0.868396, -0.866678, -0.864844, -0.862921, -0.860934, -0.858903, -0.856797, -0.854614, -0.852368, -0.850075, -0.84775, -0.845392, -0.842974, -0.840512, -0.838024, -0.83553, -0.833047, -0.830567, -0.828078, -0.825584, -0.823086, -0.820586, -0.818085, -0.815569, -0.813046, -0.810528, -0.808027, -0.805556, -0.803131, -0.800752, -0.798393, -0.796028, -0.79363, -0.791176, -0.788669, -0.786125, -0.783558, -0.780983, -0.778414, -0.775859, -0.773306, -0.770751, -0.768194, -0.765633, -0.763064, -0.76048, -0.757883, -0.755282, -0.752691, -0.750121, -0.747581, -0.745068, -0.742576, -0.740095, -0.737621, -0.735144, -0.732669, -0.730205, -0.727744, -0.725278, -0.722802, -0.720306, -0.717784, -0.715244, -0.712698, -0.710156, -0.707631, -0.70513, -0.702646, -0.700175, -0.697711, -0.695248, -0.692784, -0.69032, -0.687859, -0.6854, -0.682941, -0.680482, -0.67802, -0.675555, -0.673088, -0.670622, -0.668159, -0.665703, -0.663256, -0.660822, -0.658392, -0.655958, -0.653513, -0.651048, -0.64856, -0.646055, -0.643541, -0.641026, -0.63852, -0.636029, -0.633553, -0.631083, -0.628611, -0.626129, -0.62363, -0.62111, -0.618576, -0.616031, -0.613479, -0.610924, -0.608366, -0.605796, -0.603217, -0.600638, -0.598067, -0.59551, -0.592974, -0.590451, -0.58794, -0.585435, -0.582935, -0.580436, -0.577928, -0.575415, -0.572911, -0.570425, -0.567972, -0.565563, -0.563209, -0.560889, -0.55858, -0.55626, -0.553906, -0.551515, -0.549106, -0.546682, -0.544246, -0.541801, -0.53935, -0.536897, -0.534438, -0.531967, -0.529476, -0.526961, -0.524413, -0.521828, -0.519219, -0.516602, -0.513988, -0.511392, -0.508796, -0.506192, -0.503597, -0.501024, -0.498489, -0.496007, -0.493576, -0.491182, -0.488812, -0.486449, -0.484079, -0.481706, -0.479345, -0.476989, -0.474628, -0.472254, -0.46986, -0.467445, -0.465015, -0.462576, -0.460135, -0.457698, -0.455269, -0.452846, -0.450424, -0.448, -0.445567, -0.443123, -0.440673, -0.438221, -0.435759, -0.433282, -0.430783, -0.428251, -0.425661, -0.423039, -0.420415, -0.417822, -0.415292, -0.412848, -0.410474, -0.40814, -0.405816, -0.40347, -0.401075, -0.39863, -0.396155, -0.393671, -0.391196, -0.388748, -0.386341, -0.38396, -0.381597, -0.379251, -0.376914, -0.374584, -0.372274, -0.369988, -0.367709, -0.365422, -0.36311, -0.360759, -0.358377, -0.355973, -0.353552, -0.35112, -0.348683, -0.346232, -0.343757, -0.341272, -0.338791, -0.33633, -0.333904, -0.331509, -0.329135, -0.326778, -0.324435, -0.322102, -0.319783, -0.317494, -0.315222, -0.312948, -0.310658, -0.308332, -0.305972, -0.303591, -0.301191, -0.298771, -0.296331, -0.293869, -0.291362, -0.288824, -0.286279, -0.28375, -0.281261, -0.278825, -0.276424, -0.274049, -0.27169, -0.269337, -0.266981, -0.264622, -0.262268, -0.259921, -0.257585, -0.255261, -0.252955, -0.250671, -0.248402, -0.246138, -0.243872, -0.241594, -0.239308, -0.237026, -0.234738, -0.232437, -0.230116, -0.227767, -0.225386, -0.222981, -0.220562, -0.218139, -0.215721, -0.213308, -0.210888, -0.208467, -0.206051, -0.203648, -0.201264, -0.198897, -0.196543, -0.194201, -0.191873, -0.18956, -0.187264, -0.184994, -0.182742, -0.180497, -0.178248, -0.175985, -0.173709, -0.17143, -0.169145, -0.16685, -0.164542, -0.162215, -0.159862, -0.15749, -0.15511, -0.152733, -0.150369, -0.148029, -0.145705, -0.143391, -0.141079, -0.138761, -0.136432, -0.134085, -0.131728, -0.129367, -0.127009, -0.124661, -0.122327, -0.119997, -0.117672, -0.115358, -0.113058, -0.110777, -0.108528, -0.106311, -0.10411, -0.101907, -0.0996853, -0.097427, -0.0951316, -0.0928122, -0.0904801, -0.0881476, -0.0858269, -0.0835248, -0.0812303, -0.0789414, -0.0766578, -0.074378, -0.0721014, -0.0698264, -0.0675536, -0.0652851, -0.0630235, -0.0607712, -0.0585312, -0.0563106, -0.0541026, -0.0518976, -0.0496861, -0.0474588, -0.0452096, -0.0429452, -0.040671, -0.0383926, -0.0361152, -0.0338438, -0.0315732, -0.0293009, -0.0270301, -0.024764, -0.0225054, -0.0202499, -0.0179787, -0.0157089, -0.0134595, -0.011251, -0.00910325 +}; diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/utils/AudioMath.h b/plugins/community/repos/squinkylabs-plug1/dsp/utils/AudioMath.h index fa9cc2e8..bbebbc0a 100644 --- a/plugins/community/repos/squinkylabs-plug1/dsp/utils/AudioMath.h +++ b/plugins/community/repos/squinkylabs-plug1/dsp/utils/AudioMath.h @@ -24,6 +24,16 @@ public: return 20 * log(g) / Ln10; } + static double cents(double f1, double f2) + { + return 1200 * std::log2(f1 / f2); + } + + static double acents(double f1, double f2) + { + return std::abs(cents(f1, f2)); + } + static double gainFromDb(double db) { return std::exp(Ln10 * db / 20.0); @@ -32,6 +42,9 @@ public: /** * Returns a function that generates one period of sin for x = {0..1}. * Range (output) is -1 to 1. + * + * All makeFunc_xxx functions return functions that are not optimized. + * They do not use lookup tables. */ static std::function makeFunc_Sin(); @@ -53,7 +66,8 @@ public: /** * ScaleFun is a function the combines CV, knob, and trim into a voltage. - * Typically a ScaleFun is like an "attenuverter" + * Typically a ScaleFun is like an "attenuverter", where the trim input + * is the attenuverter. */ template using ScaleFun = std::function; @@ -66,6 +80,10 @@ public: * * This particular function is used when knobs are -5..5, * and CV range is -5..5. + * + * + * Can easily be used to add, clip and scale just a knob an a CV by + * passing 1 for the trim param. */ template static ScaleFun makeLinearScaler(T y0, T y1) @@ -87,6 +105,67 @@ public: * Details the same as makeLinearScaler except that the CV * scaling will be exponential for most values, becoming * linear near zero. + * + * Note that the cv and knob will have linear taper, only the + * attenuverter is audio taper. + * + * Implemented with a cached lookup table - + * only the final scaling is done at run time */ - static ScaleFun makeBipolarAudioScaler(float y0, float y1); + static ScaleFun makeScalerWithBipolarAudioTrim(float y0, float y1); + + /** + * SimpleScaleFun is a function the combines CV, and knob, into a voltage. + * Usually in a synth module that has knob and cv, but no trim. + * + * cv and knob are -5 to +5. They are summed, and the sum is limited to 5, -5. + * Then the sum gets an audio taper + */ + template + using SimpleScaleFun = std::function; + + /** + * maps +/- 5 volts from cv and knob to y0, y1 + * with an audio taper. + */ + static SimpleScaleFun makeSimpleScalerAudioTaper(float y0, float y1); + + template + static std::pair getMinMax(const T* data, int numSamples) + { + T min = 1, max = -1; + for (int i = 0; i < numSamples; ++i) { + const T x = data[i]; + min = std::min(min, x); + max = std::max(max, x); + } + return std::pair(min, max); + } + + /** + * A random number generator function. uniform random 0..1 + */ + using RandomUniformFunc = std::function; + + static RandomUniformFunc random(); + + + /** + * Folds numbers between +1 and -1 + */ + static inline float fold(float x) + { + float fold; + const float bias = (x < 0) ? -1.f : 1.f; + int phase = int((x + bias) / 2.f); + bool isEven = !(phase & 1); + // printf(" wrap(%f) phase=%d, isEven=%d", x, phase, isEven); + if (isEven) { + fold = x - 2.f * phase; + } else { + fold = -x + 2.f * phase; + } + // printf(" y=%f\n", wrap); + return fold; + } }; diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/utils/Decimator.h b/plugins/community/repos/squinkylabs-plug1/dsp/utils/Decimator.h new file mode 100644 index 00000000..a2dc2bc0 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/dsp/utils/Decimator.h @@ -0,0 +1,46 @@ +#pragma once + +/** + * Outputs a stair-step decimation. + * So maybe it's the integral or a decimation? + */ +class Decimator +{ +public: + + /** + * ret the next sample from the decimator. + * if (needsInput), then next call must be acceptData. + */ + float clock(bool& needsInput) + { + --phaseAccumulator; // one more sample + if (phaseAccumulator <= 0) { + needsInput = true; + phaseAccumulator += rate; + } else { + needsInput = false; + } + return memory; + } + + void acceptData(float data) + { + memory = data; + } + + /** + * Rate must be > 1. + * Fractional rates are fine. + */ + void setDecimationRate(float r) + { + rate = r; + phaseAccumulator = rate; + } + +private: + float rate=0; + float memory=0; + float phaseAccumulator = 0; +}; diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/utils/IIRDecimator.h b/plugins/community/repos/squinkylabs-plug1/dsp/utils/IIRDecimator.h new file mode 100644 index 00000000..355eed2a --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/dsp/utils/IIRDecimator.h @@ -0,0 +1,52 @@ +#pragma once + +#include "BiquadParams.h" +#include "BiquadState.h" +#include "BiquadFilter.h" + +/** + * A traditional decimator, using IIR filters for interpolation + * + * template parameter OVERSAMPLE is the + * decimation rate. + */ +class IIRDecimator +{ +public: + float process(const float * input) + { + float x = 0; + for (int i = 0; i < oversample; ++i) { + x = BiquadFilter::run(input[i], state, *params); + } + return x; + } + + /** + * cutoff is normalized freq (.5 = nyquist). + * typically cutoff will be < (.5 / OVERSAMPLE), + * if not, the filters wouldn't work. + */ +#if 0 + void setCutoff(float cutoff) + { + assert(cutoff > 0 && cutoff < .5f); + params = ObjectCache::get6PLPParams(cutoff); + } +#endif + /** + * will set the oversample factor, and set the filter cutoff. + * NOrmalized cutoff is fixed at 1 / 4 * oversample, for a one + * octave passband + */ + void setup(int oversampleFactor) + { + assert(oversampleFactor == 4 || oversampleFactor == 16); + oversample = oversampleFactor; + params = ObjectCache::get6PLPParams(1.f / (4.0f * oversample)); + } +private: + std::shared_ptr> params; + BiquadState state; + int oversample = 16; +}; \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/utils/IIRUpsampler.h b/plugins/community/repos/squinkylabs-plug1/dsp/utils/IIRUpsampler.h new file mode 100644 index 00000000..16763de0 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/dsp/utils/IIRUpsampler.h @@ -0,0 +1,48 @@ +#pragma once + +#include "BiquadParams.h" +#include "BiquadState.h" +#include "BiquadFilter.h" +#include "ObjectCache.h" + +class IIRUpsampler +{ +public: + void process(float * outputBuffer, float input) + { + input *= oversample; + for (int i = 0; i < oversample; ++i) { + outputBuffer[i] = BiquadFilter::run(input, state, *params); + input = 0; // just filter a delta - don't average the whole signal + } + } + /** + * cutoff is normalized freq (.5 = nyquist). + * typically cutoff will be < (.5 / FACTOR), + * if not, the filters wouldn't work. + */ +#if 0 + void setCutoff(float cutoff) + { + assert(cutoff > 0 && cutoff < .5f); + // ButterworthFilterDesigner::designSixPoleLowpass(params, cutoff); + params = ObjectCache::get6PLPParams(cutoff); + } +#endif + + /** + * Will set the oversample factor, and set the filter cutoff. + * NOrmalized cutoff is fixed at 1 / 4 * oversample, for a one + * octave passband + */ + void setup(int oversampleFactor) + { + assert(oversampleFactor == 4 || oversampleFactor == 16); + oversample = oversampleFactor; + params = ObjectCache::get6PLPParams(1.f / (4.0f * oversample)); + } +private: + std::shared_ptr> params; + BiquadState state; + int oversample = 16; +}; \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/utils/LookupTable.h b/plugins/community/repos/squinkylabs-plug1/dsp/utils/LookupTable.h index 6359681b..105fe70e 100644 --- a/plugins/community/repos/squinkylabs-plug1/dsp/utils/LookupTable.h +++ b/plugins/community/repos/squinkylabs-plug1/dsp/utils/LookupTable.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/utils/LookupTableFactory.h b/plugins/community/repos/squinkylabs-plug1/dsp/utils/LookupTableFactory.h index 6ef5b4b4..67754d70 100644 --- a/plugins/community/repos/squinkylabs-plug1/dsp/utils/LookupTableFactory.h +++ b/plugins/community/repos/squinkylabs-plug1/dsp/utils/LookupTableFactory.h @@ -7,7 +7,16 @@ template class LookupTableFactory { public: + /** + * domain (x) = -1 .. +1 + */ static void makeBipolarAudioTaper(LookupTableParams& params); + + /** + * domain (x) = 0..1 + */ + static void makeAudioTaper(LookupTableParams& params); + static double audioTaperKnee() { return -24; @@ -18,32 +27,99 @@ public: * range = 20..20k (for now). but should be .001 to 1.0? */ static void makeExp2(LookupTableParams& params); - static double expYMin() + static double exp2YMin() { return 4; } - static double expYMax() + static double exp2YMax() { return 40000; } - static double expXMin() + static double exp2XMin() + { + return std::log2(exp2YMin()); + } + static double exp2XMax() + { + return std::log2(exp2YMax()); + } + + static void makeExp2ExLow(LookupTableParams& params); + static double exp2ExLowYMin() + { + return 2; + } + + static double exp2ExLowYMax() + { + return 400; + } + static double exp2ExLowXMin() { - return std::log2(expYMin()); + return std::log2(exp2ExLowYMin()); } - static double expXMax() + static double exp2ExLowXMax() { - return std::log2(expYMax()); + return std::log2(exp2ExLowYMax()); } + + static void makeExp2ExHigh(LookupTableParams& params); + static double exp2ExHighYMin() + { + return 400; + } + static double exp2ExHighYMax() + { + return 20000; + } + static double exp2ExHighXMin() + { + return std::log2(exp2ExHighYMin()); + } + + static double exp2ExHighXMax() + { + return std::log2(exp2ExHighYMax()); + } + + }; template inline void LookupTableFactory::makeExp2(LookupTableParams& params) { - // 128 not enough for one cent + // 256 enough for one cent const int bins = 256; - const T xMin = (T) std::log2(expYMin()); - const T xMax = (T) std::log2(expYMax()); + const T xMin = (T) std::log2(exp2YMin()); + const T xMax = (T) std::log2(exp2YMax()); + assert(xMin < xMax); + LookupTable::init(params, bins, xMin, xMax, [](double x) { + return std::pow(2, x); + }); +} + +// hit specs with 256 / 128 @200 crossover +// try 800 ng - need 256/256 +// 400 need 256 / 128 +template +inline void LookupTableFactory::makeExp2ExHigh(LookupTableParams& params) +{ + const int bins = 512; + const T xMin = (T) std::log2(exp2ExHighYMin()); + const T xMax = (T) std::log2(exp2ExHighYMax()); + assert(xMin < xMax); + LookupTable::init(params, bins, xMin, xMax, [](double x) { + return std::pow(2, x); + }); +} + +template +inline void LookupTableFactory::makeExp2ExLow(LookupTableParams& params) +{ + const int bins = 256; + const T xMin = (T) std::log2(exp2ExLowYMin()); + const T xMax = (T) std::log2(exp2ExLowYMax()); assert(xMin < xMax); LookupTable::init(params, bins, xMin, xMax, [](double x) { return std::pow(2, x); @@ -61,4 +137,17 @@ inline void LookupTableFactory::makeBipolarAudioTaper(LookupTableParams& p return (x >= 0) ? audioTaper(x) : -audioTaper(-x); }); +} + +template +inline void LookupTableFactory::makeAudioTaper(LookupTableParams& params) +{ + const int bins = 32; + std::function audioTaper = AudioMath::makeFunc_AudioTaper(audioTaperKnee()); + const T xMin = 0; + const T xMax = 1; + LookupTable::init(params, bins, xMin, xMax, [audioTaper](double x) { + return audioTaper(x); + }); + } \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/utils/ObjectCache.cpp b/plugins/community/repos/squinkylabs-plug1/dsp/utils/ObjectCache.cpp index 2fee4982..518062de 100644 --- a/plugins/community/repos/squinkylabs-plug1/dsp/utils/ObjectCache.cpp +++ b/plugins/community/repos/squinkylabs-plug1/dsp/utils/ObjectCache.cpp @@ -2,6 +2,7 @@ #include #include "AudioMath.h" +#include "ButterworthFilterDesigner.h" #include "LookupTableFactory.h" #include "ObjectCache.h" @@ -17,6 +18,21 @@ std::shared_ptr> ObjectCache::getBipolarAudioTaper() return ret; } +template +std::shared_ptr> ObjectCache::getAudioTaper() +{ + + std::shared_ptr< LookupTableParams> ret = audioTaper.lock(); + if (!ret) { + ret = std::make_shared>(); + LookupTableFactory::makeAudioTaper(*ret); + audioTaper = ret; + } + return ret; + + return nullptr; +} + template std::shared_ptr> ObjectCache::getSinLookup() { @@ -24,8 +40,8 @@ std::shared_ptr> ObjectCache::getSinLookup() if (!ret) { ret = std::make_shared>(); std::function f = AudioMath::makeFunc_Sin(); - // Used to use 4096, but 256 gives about 80db snr, so let's save memory - LookupTable::init(*ret, 256, 0, 1, f); + // Used to use 4096, but 512 gives about 92db snr, so let's save memory + LookupTable::init(*ret, 512, 0, 1, f); sinLookupTable = ret; } return ret; @@ -43,6 +59,30 @@ std::shared_ptr> ObjectCache::getExp2() return ret; } +template +std::shared_ptr> ObjectCache::getExp2ExtendedLow() +{ + std::shared_ptr< LookupTableParams> ret = exp2ExLow.lock(); + if (!ret) { + ret = std::make_shared>(); + LookupTableFactory::makeExp2ExLow(*ret); + exp2ExLow = ret; + } + return ret; +} + +template +std::shared_ptr> ObjectCache::getExp2ExtendedHigh() +{ + std::shared_ptr< LookupTableParams> ret = exp2ExHigh.lock(); + if (!ret) { + ret = std::make_shared>(); + LookupTableFactory::makeExp2ExHigh(*ret); + exp2ExHigh = ret; + } + return ret; +} + template @@ -53,7 +93,7 @@ std::shared_ptr> ObjectCache::getDb2Gain() ret = std::make_shared>(); LookupTable::init(*ret, 32, -80, 20, [](double x) { return AudioMath::gainFromDb(x); - }); + }); db2Gain = ret; } return ret; @@ -66,7 +106,7 @@ std::shared_ptr> ObjectCache::getTanh5() std::shared_ptr< LookupTableParams> ret = tanh5.lock(); if (!ret) { ret = std::make_shared>(); - LookupTable::init(*ret, 256, -5, 5, [](double x) { + LookupTable::init(*ret, 256, -5, 5, [](double x) { return std::tanh(x); }); tanh5 = ret; @@ -74,16 +114,71 @@ std::shared_ptr> ObjectCache::getTanh5() return ret; } -// The weak pointer that hold our singletons. +/** + * Lambda capture two smart pointers to lookup table params, + * so lifetime of the lambda control their reft. + */ +template +std::function ObjectCache::getExp2Ex() +{ + std::shared_ptr < LookupTableParams> low = getExp2ExtendedLow(); + std::shared_ptr < LookupTableParams> high = getExp2ExtendedHigh(); + const T xDivide = (T) LookupTableFactory::exp2ExHighXMin(); + return [low, high, xDivide](T x) { + auto params = (x < xDivide) ? low : high; + return LookupTable::lookup(*params, x, true); + }; +} + +template +std::shared_ptr> ObjectCache::get6PLPParams(float normalizedFc) +{ + const int div = (int) std::round(1.0 / normalizedFc); + if (div == 64) { + std::shared_ptr < BiquadParams> ret = lowpass64.lock(); + if (!ret) { + ret = std::make_shared>(); + ButterworthFilterDesigner::designSixPoleLowpass(*ret, normalizedFc); + lowpass64 = ret; + } + return ret; + } else if (div == 16) { + std::shared_ptr < BiquadParams> ret = lowpass16.lock(); + if (!ret) { + ret = std::make_shared>(); + ButterworthFilterDesigner::designSixPoleLowpass(*ret, normalizedFc); + lowpass16 = ret; + } + return ret; + } + else assert(false); + return nullptr; +} + +// The weak pointers that hold our singletons. +template +std::weak_ptr< BiquadParams > ObjectCache::lowpass64; +template +std::weak_ptr< BiquadParams > ObjectCache::lowpass16; + template std::weak_ptr> ObjectCache::bipolarAudioTaper; +template +std::weak_ptr> ObjectCache::audioTaper; + template std::weak_ptr> ObjectCache::sinLookupTable; template std::weak_ptr> ObjectCache::exp2; +template +std::weak_ptr> ObjectCache::exp2ExLow; + +template +std::weak_ptr> ObjectCache::exp2ExHigh; + template std::weak_ptr> ObjectCache::db2Gain; diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/utils/ObjectCache.h b/plugins/community/repos/squinkylabs-plug1/dsp/utils/ObjectCache.h index 78c63568..9f517bb1 100644 --- a/plugins/community/repos/squinkylabs-plug1/dsp/utils/ObjectCache.h +++ b/plugins/community/repos/squinkylabs-plug1/dsp/utils/ObjectCache.h @@ -2,6 +2,7 @@ #include "LookupTable.h" +#include "BiquadParams.h" /** * This class creates objects and caches them. @@ -18,6 +19,7 @@ class ObjectCache { public: static std::shared_ptr> getBipolarAudioTaper(); + static std::shared_ptr> getAudioTaper(); static std::shared_ptr> getSinLookup(); /** @@ -30,6 +32,14 @@ public: * accuracy = 1 cent (1V/octave) */ static std::shared_ptr> getExp2(); + + static std::function getExp2Ex(); + + static std::shared_ptr> getExp2ExtendedLow(); + static std::shared_ptr> getExp2ExtendedHigh(); + + + static std::shared_ptr> getDb2Gain(); /** @@ -37,14 +47,22 @@ public: */ static std::shared_ptr> getTanh5(); + static std::shared_ptr> get6PLPParams(float normalizedFc); + private: /** * Cache uses weak pointers. This allows the cached objects to be * freed when the last client reference goes away. */ static std::weak_ptr> bipolarAudioTaper; + static std::weak_ptr> audioTaper; static std::weak_ptr> sinLookupTable; static std::weak_ptr> exp2; + static std::weak_ptr> exp2ExHigh; + static std::weak_ptr> exp2ExLow; static std::weak_ptr> db2Gain; static std::weak_ptr> tanh5; + + static std::weak_ptr< BiquadParams> lowpass64; + static std::weak_ptr< BiquadParams> lowpass16; }; diff --git a/plugins/community/repos/squinkylabs-plug1/dsp/utils/poly.h b/plugins/community/repos/squinkylabs-plug1/dsp/utils/poly.h new file mode 100644 index 00000000..200dc50b --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/dsp/utils/poly.h @@ -0,0 +1,64 @@ +#pragma once +template +class Poly +{ +public: + Poly(); + float run(float x) + { + fillPowers(x); + return (float) doSum(); + } + void setGain(int index, float value) + { + assert(index >= 0 && index < order); + gains[index] = value; + } +private: + T gains[order]; + + // powers[0] = x, powers[1] = x**2 + T powers[order]; + + void fillPowers(T); + T doSum(); +}; + +template +inline Poly::Poly() +{ + assert(order == 11); + for (int i = 0; i < order; ++i) { + gains[i] = 0; + powers[i] = 0; + } +} + +template +inline void Poly::fillPowers(T x) +{ + T value = x; + for (int i = 0; i < order; ++i) { + powers[i] = value; + value *= x; + } +} + + + +template +inline T Poly::doSum() +{ + T ret = gains[0] * powers[0]; + ret += gains[1] * (2 * powers[1] - 1); + ret += gains[2] * (4 * powers[2] - 3 * powers[0]); + ret += gains[3] * (8 * powers[3] - 8 * powers[1] + 1); + ret += gains[4] * (16 * powers[4] - 20 * powers[2] + 5 * powers[0]); + ret += gains[5] * (32 * powers[5] - 48 * powers[3] + 18 * powers[1] - 1); + ret += gains[6] * (64 * powers[6] - 112 * powers[4] + 56 * powers[2] - 7 * powers[0]); + ret += gains[7] * (128 * powers[7] - 256 * powers[5] + 160 * powers[3] - 32 * powers[1] + 1); + ret += gains[8] * (256 * powers[8] - 576 * powers[6] + 432 * powers[4] - 120 * powers[2] + 9 * powers[0]); + ret += gains[9] * (512 * powers[9] - 1280 * powers[7] + 1120 * powers[5] - 400 * powers[3] + 50 * powers[1] - 1); + ret += gains[10] * (1024 * powers[10] - 2816 * powers[8] + 2816 * powers[6] - 1232 * powers[4] + 220 * powers[2] - 11 * powers[0]); + return ret; +} \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/gfx/lfn-panel-design.xd b/plugins/community/repos/squinkylabs-plug1/gfx/lfn-panel-design.xd new file mode 100644 index 00000000..561c3b7e Binary files /dev/null and b/plugins/community/repos/squinkylabs-plug1/gfx/lfn-panel-design.xd differ diff --git a/plugins/community/repos/squinkylabs-plug1/make.objects b/plugins/community/repos/squinkylabs-plug1/make.objects index 38a9a264..c32d685a 100644 --- a/plugins/community/repos/squinkylabs-plug1/make.objects +++ b/plugins/community/repos/squinkylabs-plug1/make.objects @@ -6,16 +6,28 @@ ALL_OBJ= \ dsp/filters/FormantTables2.o \ dsp/filters/HilbertFilterDesigner.o \ dsp/third-party/falco/DspFilter.o \ + dsp/utils/AsymWaveShaper.o \ dsp/utils/AudioMath.o \ dsp/utils/ObjectCache.o \ sqsrc/clock/ClockMult.o \ sqsrc/thread/ThreadClient.o \ sqsrc/thread/ThreadServer.o \ sqsrc/thread/ThreadSharedState.o \ + src/BlankModule.o \ src/BootyModule.o \ + src/CHBModule.o \ src/CPU_Hog.o \ src/ColoredNoiseModule.o \ + src/DGModule.o \ + src/EV3Module.o \ + src/EVModule.o \ + src/FunVModule.o \ + src/GMRModule.o \ + src/GrayModule.o \ + src/LFNModule.o \ + src/ShaperModule.o \ src/Squinky.o \ + src/SuperModule.o \ src/ThreadBoost.o \ src/TremoloModule.o \ src/VocalFilterModule.o \ diff --git a/plugins/community/repos/squinkylabs-plug1/makefile.linux b/plugins/community/repos/squinkylabs-plug1/makefile.linux index bb84a492..fb67ac52 100644 --- a/plugins/community/repos/squinkylabs-plug1/makefile.linux +++ b/plugins/community/repos/squinkylabs-plug1/makefile.linux @@ -2,7 +2,12 @@ SLUG=squinkylabs-plug1 include ../../../build_plugin_pre_linux.mk -EXTRAFLAGS += -Icomposites/ -Idsp/utils/ -Idsp/third-party/kiss_fft130/ -Idsp/third-party/kiss_fft130/tools/ -Isqsrc/util/ -Isqsrc/thread/ -Idsp/third-party/falco/ -Idsp/generators/ -Idsp/filters/ -Idsp/fft/ -Isqsrc/clock/ +EXTRAFLAGS += -Icomposites/ -Idsp/utils/ -Idsp/third-party/kiss_fft130/ -Idsp/third-party/kiss_fft130/tools/ -Idsp/third-party/src -Isqsrc/util/ -Isqsrc/thread/ -Idsp/third-party/falco/ -Idsp/generators/ -Idsp/filters/ -Idsp/fft/ -Isqsrc/clock/ -Isqsrc/grammar -Isqsrc/delay +#EXTRAFLAGS += -D_CHB +#EXTRAFLAGS += -D_DG +#EXTRAFLAGS += -D_EV3 +#EXTRAFLAGS += -D_EV +#EXTRAFLAGS += -D_GMR include make.objects diff --git a/plugins/community/repos/squinkylabs-plug1/makefile.msvc b/plugins/community/repos/squinkylabs-plug1/makefile.msvc index 0e4875f1..cb79a378 100644 --- a/plugins/community/repos/squinkylabs-plug1/makefile.msvc +++ b/plugins/community/repos/squinkylabs-plug1/makefile.msvc @@ -2,7 +2,12 @@ SLUG=squinkylabs-plug1 include ../../../build_plugin_pre_msvc.mk -EXTRAFLAGS += -Icomposites/ -Idsp/utils/ -Idsp/third-party/kiss_fft130/ -Idsp/third-party/kiss_fft130/tools/ -Isqsrc/util/ -Isqsrc/thread/ -Idsp/third-party/falco/ -Idsp/generators/ -Idsp/filters/ -Idsp/fft/ -Isqsrc/clock/ +EXTRAFLAGS += -Icomposites/ -Idsp/utils/ -Idsp/third-party/kiss_fft130/ -Idsp/third-party/kiss_fft130/tools/ -Idsp/third-party/src -Isqsrc/util/ -Isqsrc/thread/ -Idsp/third-party/falco/ -Idsp/generators/ -Idsp/filters/ -Idsp/fft/ -Isqsrc/clock/ -Isqsrc/grammar -Isqsrc/delay +#EXTRAFLAGS += -D_CHB +#EXTRAFLAGS += -D_DG +#EXTRAFLAGS += -D_EV3 +#EXTRAFLAGS += -D_EV +#EXTRAFLAGS += -D_GMR include make.objects diff --git a/plugins/community/repos/squinkylabs-plug1/projects/vs_windows/Squinky.vcxproj b/plugins/community/repos/squinkylabs-plug1/projects/vs_windows/Squinky.vcxproj index 6c369054..8118524f 100644 --- a/plugins/community/repos/squinkylabs-plug1/projects/vs_windows/Squinky.vcxproj +++ b/plugins/community/repos/squinkylabs-plug1/projects/vs_windows/Squinky.vcxproj @@ -28,38 +28,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -72,21 +101,37 @@ + + + + + + + + + + + + + + + + @@ -96,6 +141,8 @@ + + @@ -164,7 +211,7 @@ Level3 ProgramDatabase Disabled - ..\..\dsp\third-party\falco;..\..\dsp\utils;..\..\dsp\generators;..\..\dsp\filters;%(AdditionalIncludeDirectories) + ..\..\dsp\utils;..\..\dsp\fft;..\..\dsp\filters;..\..\dsp\generators;..\..\dsp\third-party\src;..\..\dsp\third-party\falco;..\..\dsp\third-party\kiss_fft130;..\..\dsp\third-party\kiss_fft130\tools;..\..\composites;..\..\sqsrc\util;..\..\sqsrc\thread;..\..\sqsrc\grammar;..\..\sqsrc\clock;..\..\..\..\include;..\..\..\..\dep\speexdsp-SpeexDSP-1.2rc3\include;D:\VCVRack\x6\dep\jpommier-pffft-29e4f76ac53b;%(AdditionalIncludeDirectories) stdcpp14 @@ -180,7 +227,7 @@ Level3 ProgramDatabase Disabled - ..\..\dsp\fft;..\..\dsp\generators;..\..\dsp\filters;..\..\dsp\third-party\falco;..\..\composites;..\..\sqsrc\clock;..\..\sqsrc\util;..\..\sqsrc\thread;..\..\dsp\utils;..\..\dsp\third-party\kiss_fft130;..\..\dsp\third-party\kiss_fft130\tools;%(AdditionalIncludeDirectories) + ..\..\sqsrc\grammar;..\..\dsp\fft;..\..\dsp\generators;..\..\dsp\filters;..\..\dsp\third-party\src;..\..\dsp\third-party\falco;..\..\composites;..\..\sqsrc\clock;..\..\sqsrc\delay;..\..\sqsrc\util;..\..\sqsrc\thread;..\..\dsp\utils;..\..\dsp\third-party\kiss_fft130;..\..\dsp\third-party\kiss_fft130\tools;..\..\..\..\include;..\..\..\..\dep\speexdsp-SpeexDSP-1.2rc3\include;..\..\..\..\dep\jpommier-pffft-29e4f76ac53b;%(AdditionalIncludeDirectories) stdcpp14 @@ -195,6 +242,7 @@ MultiThreadedDLL Level3 ProgramDatabase + ..\..\dsp\utils;..\..\dsp\fft;..\..\dsp\filters;..\..\dsp\generators;..\..\dsp\third-party\src;..\..\dsp\third-party\falco;..\..\dsp\third-party\kiss_fft130;..\..\dsp\third-party\kiss_fft130\tools;..\..\composites;..\..\sqsrc\util;..\..\sqsrc\thread;..\..\sqsrc\grammar;..\..\sqsrc\clock;..\..\..\..\include;..\..\..\..\dep\speexdsp-SpeexDSP-1.2rc3\include;D:\VCVRack\x6\dep\jpommier-pffft-29e4f76ac53b;%(AdditionalIncludeDirectories) MachineX86 @@ -210,7 +258,7 @@ MultiThreadedDLL Level3 ProgramDatabase - ..\..\dsp\fft;..\..\dsp\generators;..\..\dsp\filters;..\..\dsp\third-party\falco;..\..\composites;..\..\sqsrc\clock;..\..\sqsrc\util;..\..\sqsrc\thread;..\..\dsp\utils;..\..\dsp\third-party\kiss_fft130;..\..\dsp\third-party\kiss_fft130\tools;%(AdditionalIncludeDirectories) + ..\..\sqsrc\grammar;..\..\dsp\fft;..\..\dsp\generators;..\..\dsp\filters;..\..\dsp\third-party\src;..\..\dsp\third-party\falco;..\..\composites;..\..\sqsrc\clock;..\..\sqsrc\delay;..\..\sqsrc\util;..\..\sqsrc\thread;..\..\dsp\utils;..\..\dsp\third-party\kiss_fft130;..\..\dsp\third-party\kiss_fft130\tools;..\..\..\..\include;..\..\..\..\dep\speexdsp-SpeexDSP-1.2rc3\include;..\..\..\..\dep\jpommier-pffft-29e4f76ac53b;%(AdditionalIncludeDirectories) true diff --git a/plugins/community/repos/squinkylabs-plug1/projects/vs_windows/Squinky.vcxproj.filters b/plugins/community/repos/squinkylabs-plug1/projects/vs_windows/Squinky.vcxproj.filters index b646a4d4..3bee6a04 100644 --- a/plugins/community/repos/squinkylabs-plug1/projects/vs_windows/Squinky.vcxproj.filters +++ b/plugins/community/repos/squinkylabs-plug1/projects/vs_windows/Squinky.vcxproj.filters @@ -85,6 +85,24 @@ {6c381890-e472-4d98-a18a-8c768ec7a670} + + {c8db16de-4e6f-4c6d-bd7a-aeb75e464495} + + + {d8e54a08-bbcc-4495-8f95-cd2178b57811} + + + {726b598f-f9ad-4df3-9db8-7cb86dc1cd03} + + + {bcce7e7a-2123-42c9-8557-a31f6444e05e} + + + {fc621d2c-8726-4e99-a6dd-bbc7135bee28} + + + {2b1546d2-0426-4a86-9aa8-0d5448be9ad0} + @@ -198,6 +216,66 @@ Source Files\test + + Source Files\test + + + Source Files\test + + + Source Files\test + + + Source Files\test + + + Source Files\test + + + Source Files\sqsrc\grammar + + + Source Files\test + + + Source Files\test + + + Source Files\dsp\third-party\src + + + Source Files\test + + + Source Files\test + + + Source Files\test + + + Source Files\test + + + Source Files\test + + + Source Files\test + + + Source Files\test + + + Source Files\sqsrc\delay + + + Source Files\test + + + Source Files\dsp\utils + + + Source Files\test + @@ -332,5 +410,86 @@ Header Files\sqsrc\thread + + Source Files\test + + + Header Files\sqsrc\grammar + + + Header Files\dsp\filters + + + Header Files\dsp\filters + + + Header Files\dsp\utils + + + Header Files\composites + + + Header Files\composites + + + Header Files\sqsrc\clock + + + Header Files\sqsrc\clock + + + Header Files\dsp\utils + + + Header Files\composites + + + Header Files\sqsrc\util + + + Header Files\dsp\third-party\src + + + Header Files\composites + + + Header Files\dsp\third-party\src + + + Header Files\dsp\third-party\src + + + Header Files\dsp\generators + + + Header Files\composites + + + Header Files\dsp\utils + + + Header Files\dsp\utils + + + Header Files\sqsrc\delay + + + Header Files\composites + + + Header Files\composites + + + Header Files\composites + + + Header Files\dsp\utils + + + Header Files\dsp\filters + + + Header Files\composites + \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/res/Blue30.svg b/plugins/community/repos/squinkylabs-plug1/res/Blue30.svg new file mode 100644 index 00000000..ce7b8ad3 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/Blue30.svg @@ -0,0 +1,9 @@ + + Blue302 + + + + + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/BluePush_0.svg b/plugins/community/repos/squinkylabs-plug1/res/BluePush_0.svg new file mode 100644 index 00000000..7a292df1 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/BluePush_0.svg @@ -0,0 +1,10 @@ + + BluePush_0 + + + + + + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/BluePush_1.svg b/plugins/community/repos/squinkylabs-plug1/res/BluePush_1.svg new file mode 100644 index 00000000..30e3aa9c --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/BluePush_1.svg @@ -0,0 +1,10 @@ + + BluePush_1 + + + + + + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/BlueTrimmer.svg b/plugins/community/repos/squinkylabs-plug1/res/BlueTrimmer.svg new file mode 100644 index 00000000..81aefcd2 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/BlueTrimmer.svg @@ -0,0 +1,7 @@ + + TrimBlueOrig2 + + + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/NKKSmall_0.svg b/plugins/community/repos/squinkylabs-plug1/res/NKKSmall_0.svg new file mode 100644 index 00000000..946b5d2c --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/NKKSmall_0.svg @@ -0,0 +1,13 @@ + + Artboard 1 + + + + + + + + + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/NKKSmall_1.svg b/plugins/community/repos/squinkylabs-plug1/res/NKKSmall_1.svg new file mode 100644 index 00000000..b25bbfe9 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/NKKSmall_1.svg @@ -0,0 +1,13 @@ + + Artboard 1 + + + + + + + + + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/NKKSmall_2.svg b/plugins/community/repos/squinkylabs-plug1/res/NKKSmall_2.svg new file mode 100644 index 00000000..3f314d45 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/NKKSmall_2.svg @@ -0,0 +1,13 @@ + + Artboard 1 + + + + + + + + + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/blank_panel.svg b/plugins/community/repos/squinkylabs-plug1/res/blank_panel.svg index 4e30c038..9b9869fc 100644 --- a/plugins/community/repos/squinkylabs-plug1/res/blank_panel.svg +++ b/plugins/community/repos/squinkylabs-plug1/res/blank_panel.svg @@ -1,16 +1,20 @@ - + - + panel-6b-flat - + - - + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/chb_panel.svg b/plugins/community/repos/squinkylabs-plug1/res/chb_panel.svg new file mode 100644 index 00000000..13269abf --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/chb_panel.svg @@ -0,0 +1,32 @@ + + cb_panel + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/ev3_panel.svg b/plugins/community/repos/squinkylabs-plug1/res/ev3_panel.svg new file mode 100644 index 00000000..5df1b04a --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/ev3_panel.svg @@ -0,0 +1,26 @@ + + ev3-3-ghost + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/formants_panel.svg b/plugins/community/repos/squinkylabs-plug1/res/formants_panel.svg index de591576..6c24b440 100644 --- a/plugins/community/repos/squinkylabs-plug1/res/formants_panel.svg +++ b/plugins/community/repos/squinkylabs-plug1/res/formants_panel.svg @@ -7,7 +7,10 @@ formants_panel - + + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/fun_panel.svg b/plugins/community/repos/squinkylabs-plug1/res/fun_panel.svg new file mode 100644 index 00000000..02625237 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/fun_panel.svg @@ -0,0 +1,52 @@ + + + + + + + fun-panel + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/gray.svg b/plugins/community/repos/squinkylabs-plug1/res/gray.svg new file mode 100644 index 00000000..4688975e --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/gray.svg @@ -0,0 +1,30 @@ + + gray-narrow-ghost + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/lfn_panel.svg b/plugins/community/repos/squinkylabs-plug1/res/lfn_panel.svg new file mode 100644 index 00000000..8ac5fbdd --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/lfn_panel.svg @@ -0,0 +1,42 @@ + + + + + + + lfn_panel + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/saw_wave-1.svg b/plugins/community/repos/squinkylabs-plug1/res/saw_wave-1.svg new file mode 100644 index 00000000..266745c4 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/saw_wave-1.svg @@ -0,0 +1,4 @@ + + Artboard 1 + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/saw_wave.svg b/plugins/community/repos/squinkylabs-plug1/res/saw_wave.svg new file mode 100644 index 00000000..9fc992ac --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/saw_wave.svg @@ -0,0 +1,5 @@ + + ramp3 + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/saw_wave_on.svg b/plugins/community/repos/squinkylabs-plug1/res/saw_wave_on.svg new file mode 100644 index 00000000..127e3091 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/saw_wave_on.svg @@ -0,0 +1,5 @@ + + ramp3 + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/saw_wave_on1.svg b/plugins/community/repos/squinkylabs-plug1/res/saw_wave_on1.svg new file mode 100644 index 00000000..027e6318 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/saw_wave_on1.svg @@ -0,0 +1,4 @@ + + Artboard 1 + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/shaper.svg b/plugins/community/repos/squinkylabs-plug1/res/shaper.svg new file mode 100644 index 00000000..9d372b59 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/shaper.svg @@ -0,0 +1,35 @@ + + shaper2-ghost-01 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/thread_booster_panel.svg b/plugins/community/repos/squinkylabs-plug1/res/thread_booster_panel.svg index c73c52ef..07f92a01 100644 --- a/plugins/community/repos/squinkylabs-plug1/res/thread_booster_panel.svg +++ b/plugins/community/repos/squinkylabs-plug1/res/thread_booster_panel.svg @@ -7,7 +7,11 @@ thread_booster_panel + + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/trem_panel.svg b/plugins/community/repos/squinkylabs-plug1/res/trem_panel.svg index eb70aaa7..abff6e93 100644 --- a/plugins/community/repos/squinkylabs-plug1/res/trem_panel.svg +++ b/plugins/community/repos/squinkylabs-plug1/res/trem_panel.svg @@ -7,10 +7,9 @@ trem_panel - - - - + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/vocal_animator_panel.svg b/plugins/community/repos/squinkylabs-plug1/res/vocal_animator_panel.svg index a567e16f..1b2cafb4 100644 --- a/plugins/community/repos/squinkylabs-plug1/res/vocal_animator_panel.svg +++ b/plugins/community/repos/squinkylabs-plug1/res/vocal_animator_panel.svg @@ -7,9 +7,12 @@ vocal_animator_panel - - - + + + + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-01.svg b/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-01.svg new file mode 100644 index 00000000..c1c091c5 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-01.svg @@ -0,0 +1,5 @@ + + waveforms-6 + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-02.svg b/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-02.svg new file mode 100644 index 00000000..be844fe0 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-02.svg @@ -0,0 +1,5 @@ + + waveforms-6 + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-03.svg b/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-03.svg new file mode 100644 index 00000000..6f855985 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-03.svg @@ -0,0 +1,7 @@ + + waveforms-6 + + + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-04.svg b/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-04.svg new file mode 100644 index 00000000..f613f355 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-04.svg @@ -0,0 +1,7 @@ + + waveforms-6 + + + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-05.svg b/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-05.svg new file mode 100644 index 00000000..3abdedf7 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-05.svg @@ -0,0 +1,5 @@ + + waveforms-6 + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-06.svg b/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-06.svg new file mode 100644 index 00000000..0eb0f217 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-06.svg @@ -0,0 +1,5 @@ + + waveforms-6 + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-07.svg b/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-07.svg new file mode 100644 index 00000000..81284e26 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-07.svg @@ -0,0 +1,4 @@ + + waveforms-6 + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-08.svg b/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-08.svg new file mode 100644 index 00000000..21798e91 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-08.svg @@ -0,0 +1,4 @@ + + waveforms-6 + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-09.svg b/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-09.svg new file mode 100644 index 00000000..0f9cedf7 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-09.svg @@ -0,0 +1,5 @@ + + waveforms-6 + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-10.svg b/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-10.svg new file mode 100644 index 00000000..e606a0d2 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-10.svg @@ -0,0 +1,5 @@ + + waveforms-6 + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-11.svg b/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-11.svg new file mode 100644 index 00000000..b591d4e5 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-11.svg @@ -0,0 +1,7 @@ + + waveforms-6 + + + + + diff --git a/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-12.svg b/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-12.svg new file mode 100644 index 00000000..ee663399 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/res/waveforms-6-12.svg @@ -0,0 +1,7 @@ + + waveforms-6 + + + + + diff --git a/plugins/community/repos/squinkylabs-plug1/sqsrc/clock/ClockMult.cpp b/plugins/community/repos/squinkylabs-plug1/sqsrc/clock/ClockMult.cpp index 82e8b695..51e27726 100644 --- a/plugins/community/repos/squinkylabs-plug1/sqsrc/clock/ClockMult.cpp +++ b/plugins/community/repos/squinkylabs-plug1/sqsrc/clock/ClockMult.cpp @@ -32,7 +32,7 @@ void ClockMult::sampleClockLockedMode() case State::TRAINING: ++trainingCounter; break; - case State::RUNNING: + case State::RUNNING: ++trainingCounter; // we are still training, even while running sawPhase += learnedFrequency; if (sawPhase >= 1) { @@ -47,7 +47,7 @@ void ClockMult::sampleClockLockedMode() break; - default: + default: assert(false); } // printf("leave sampleClock: state=%d saw=%f\n", state, sawPhase); @@ -75,14 +75,14 @@ void ClockMult::refClock() trainingCounter = 0; learnedFrequency = (float) freqMultFactor / learnedPeriod; state = State::RUNNING; - + startNewClock(); // printf("refClock moved from TRAINING to RUNNING. period = %d freq=%f clockOut=%d\n", learnedPeriod, learnedFrequency, clockOutValue); break; - + default: assert(0); - + } //printf("leave refClock: state=%d\n", state); } diff --git a/plugins/community/repos/squinkylabs-plug1/sqsrc/clock/ClockMult.h b/plugins/community/repos/squinkylabs-plug1/sqsrc/clock/ClockMult.h index 931e1f39..66b552d0 100644 --- a/plugins/community/repos/squinkylabs-plug1/sqsrc/clock/ClockMult.h +++ b/plugins/community/repos/squinkylabs-plug1/sqsrc/clock/ClockMult.h @@ -58,7 +58,7 @@ private: int trainingCounter = 12345; int learnedPeriod = 999; float learnedFrequency = 0; - int freqMultFactor = 0; // if 0, free run, else mult by n. + int freqMultFactor = 0; // if 0, free run, else multiply by n. State state = State::INIT; bool clockOutValue = 0; diff --git a/plugins/community/repos/squinkylabs-plug1/sqsrc/clock/GenerativeTriggerGenerator.h b/plugins/community/repos/squinkylabs-plug1/sqsrc/clock/GenerativeTriggerGenerator.h new file mode 100644 index 00000000..2c1bab96 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/sqsrc/clock/GenerativeTriggerGenerator.h @@ -0,0 +1,114 @@ +#ifndef GENERATIVETRIGGERGENERATOR +#define GENERATIVETRIGGERGENERATOR + +#include "StochasticGrammar.h" +#include "TriggerSequencer.h" + +/* Knows how to generate trigger sequence data + * when evaluating a grammar + */ +class GTGEvaluator : public ProductionRule::EvaluationState +{ +public: + GTGEvaluator(AudioMath::RandomUniformFunc xr, TriggerSequencer::Event * buf) : + ProductionRule::EvaluationState(xr), + _buf(buf), + _delay(0) + { + } + + void writeSymbol(GKEY key) override + { + // first: write out a trigger at "current delay" + _buf->evt = TriggerSequencer::TRIGGER; + _buf->delay = _delay; + ++_buf; + + // then set current dealy to duration of key + _delay = ProductionRuleKeys::getDuration(key); + } + + // call this to write final event + void writeEnd() + { + _buf->evt = TriggerSequencer::END; + _buf->delay = _delay; + } +private: + TriggerSequencer::Event * _buf; + int _delay; +}; + + +/* wraps up some stochastic gnerative grammar stuff feeding + * a trigger sequencer + */ +class GenerativeTriggerGenerator +{ +public: + GenerativeTriggerGenerator(AudioMath::RandomUniformFunc r, const ProductionRule * rules, int numRules, GKEY initialState) : + _r(r), + _rules(rules), + _numRules(numRules), + _initKey(initialState) + { + _data[0].delay = 0; + _data[0].evt = TriggerSequencer::END; + _seq = new TriggerSequencer(_data); + } + ~GenerativeTriggerGenerator() + { + delete _seq; + } + + + void setGrammar(const ProductionRule * rules, int numRules, GKEY initialState) + { + _rules = rules; + _numRules = numRules; + _initKey = initialState; + } + + // returns true if trigger generated + bool clock() + { + _seq->clock(); + bool ret = _seq->getTrigger(); + if (_seq->getEnd()) { + // when we finish playing the seq, generate a new random one + generate(); + ret |= _seq->getTrigger(); + //printf("this should be getTrigger!!!\n"); + } + return ret; + } +private: + TriggerSequencer * _seq; + TriggerSequencer::Event _data[33]; + AudioMath::RandomUniformFunc _r; + const ProductionRule * _rules; + int _numRules; + GKEY _initKey; + // + void generate() + { + GTGEvaluator es(_r, _data); + es.rules = _rules; + es.numRules = _numRules; + ProductionRule::evaluate(es, _initKey); + + es.writeEnd(); + TriggerSequencer::isValid(_data); + _seq->reset(_data); + assert(!_seq->getEnd()); +#if 0 + printf("just generated trigger seq\n"); + TriggerSequencer::Event * p; + for (p = _data; p->evt != TriggerSequencer::END; ++p) { + printf("evt=%d, delay=%d\n", p->evt, p->delay); + } + printf("delay to end = %d\n", p->delay); +#endif + } +}; +#endif \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/sqsrc/clock/TriggerSequencer.h b/plugins/community/repos/squinkylabs-plug1/sqsrc/clock/TriggerSequencer.h new file mode 100644 index 00000000..b724f4da --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/sqsrc/clock/TriggerSequencer.h @@ -0,0 +1,114 @@ +#ifndef TRIGGERSEQUENCER +#define TRIGGERSEQUENCER + +class TriggerSequencer +{ +public: + enum EVT + { + TRIGGER, + END + }; + class Event + { + public: + short int evt; + short int delay; + }; + + // data is a buffer that will be bound to the sequencer, although it is "owned" + // by the caller + TriggerSequencer(const Event * data) : curEvent(data), trigger(false), delay(0) + { + reset(data); + } + + // send one clock to the seq + void clock(); + + // reset by concatenating new data to seq + // note that reset may generate a trigger + void reset(const Event * data) + { + //printf("reset: initial delay = %d\n", delay); + curEvent = data; + delay += data->delay; // should this be += delay?? + //printf("after reset: delay = %d\n", delay); + processClocks(); + } + + bool getTrigger() const + { + return trigger; + } // get trigger state after last clock + bool getEnd() const + { + return curEvent == 0; + } // did sequencer end after last clock? + + + // checks that a sequence is valid + static bool isValid(const Event * data); +private: + const Event * curEvent; + bool trigger; + short delay; + + void processClocks(); +}; + +inline void TriggerSequencer::clock() +{ + --delay; + processClocks(); +} + +inline void TriggerSequencer::processClocks() +{ + trigger = false; + //printf("enter proc clock, curevt =%p, delay = %d\n", curEvent, delay); + if (!curEvent) { + //printf("leave clock early - ended\n"); + return; // seq is stopped + } + + + while (delay < 0) { + //printf("delay went to %d, evt=%d\n", delay, curEvent->evt); + switch (curEvent->evt) { + case END: + //printf("setting end at 58\n"); + curEvent = 0; // stop seq by clering ptr + return; + case TRIGGER: + trigger = true; + ++curEvent; // and go to next one + //printf("trigger set true\n"); + break; + + default: + assert(false); + } + + delay += curEvent->delay; + } + + //printf("leave clock %d\n", delay); +}; + + +inline bool TriggerSequencer::isValid(const Event * data) +{ + while (data->evt != END) { + assert(data->evt == TRIGGER); + assert(data->delay >= 0); + assert(data->delay < 2000); // just for now - we expect them to be small + ++data; + } + + return true; +} + + + +#endif diff --git a/plugins/community/repos/squinkylabs-plug1/sqsrc/delay/FractionalDelay.cpp b/plugins/community/repos/squinkylabs-plug1/sqsrc/delay/FractionalDelay.cpp new file mode 100644 index 00000000..9aa1701f --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/sqsrc/delay/FractionalDelay.cpp @@ -0,0 +1,97 @@ +#include "FractionalDelay.h" + +#include + + +float FractionalDelay::getOutput() +{ +#ifdef _LOG + printf("\n"); +#endif + int delayTimeSamples = (int) delayTime; + const double x = delayTime - delayTimeSamples; + + const double y0 = getDelayedOutput(delayTimeSamples - 1); + const double y1 = getDelayedOutput(delayTimeSamples); + const double y2 = getDelayedOutput(delayTimeSamples + 1); + const double y3 = getDelayedOutput(delayTimeSamples + 2); +#ifdef _LOG + printf("dt=%.2f, dts=%d x=%.2f ", delayTime, delayTimeSamples, x); + printf("y0=%.2f y1=%.2f y2=%.2f y3=%.2f\n", y0, y1, y2, y3); +#endif + + const double x0 = -1.0; + const double x1 = 0.0; + const double x2 = 1.0; + const double x3 = 2.0; + assert(x >= x1); + assert(x <= x2); + + double dRet = -(1.0 / 6.0)*y0*(x - x1)*(x - x2)*(x - x3); + dRet += (1.0 / 2.0)* y1*(x - x0) * (x - x2) * (x - x3); + dRet += (-1.0 / 2.0)*y2*(x - x0) * (x - x1) * (x - x3); + dRet += (1.0 / 6.0) * y3*(x - x0) * (x - x1) * (x - x2); + +#if 0 + static int ct = 0; + if (++ct > 100) { + ct = 0; + char buf[500]; + sprintf(buf, "del = %f int=%d, rem=%f ret=%f\n", time, delayTimeSamples, x, dRet); + DebugUtil::trace(buf); + sprintf(buf, " y0=%f y1=%f y2=%f y3=%f\n", y0, y1, y2, y3); + DebugUtil::trace(buf); + + + } +#endif + + return float(dRet); + +} + +float FractionalDelay::getDelayedOutput(int delaySamples) +{ + int index = inputPointerIndex; + + index -= delaySamples; // indexes increase as time goes by, + // to the output (in the past), is at a lower index + if (index < 0) { + //int n = state.numSamples' + index += numSamples; + + assert(index >= 0 && index < numSamples); + } +#ifdef _LOG + printf("getting output from area of %d\n", index); +#endif + + return delayMemory[index]; + +} + +void FractionalDelay::setInput(float input) +{ + //printf("setting input at %d\n", inputPointerIndex); + delayMemory[inputPointerIndex++] = input; + if (inputPointerIndex >= numSamples) { + inputPointerIndex = 0; + } +} + + +/* +float run(float input) +{ +float ret = getOutput(); +setInput(input); +return ret; +} +*/ +float RecirculatingFractionalDelay::run(float input) +{ + float output = getOutput(); + input += (output * feedback); + setInput(input); + return output; +} diff --git a/plugins/community/repos/squinkylabs-plug1/sqsrc/delay/FractionalDelay.h b/plugins/community/repos/squinkylabs-plug1/sqsrc/delay/FractionalDelay.h new file mode 100644 index 00000000..03cfd3fe --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/sqsrc/delay/FractionalDelay.h @@ -0,0 +1,88 @@ +#pragma once + +#include +#include + +/** + * When ignoring wrap, inputIndex > outputIndex. + * so output "pull up the rear", reading the samples that were written + * delayTime samples ago. + */ +class FractionalDelay +{ +public: + FractionalDelay(int numSamples) : numSamples(numSamples), delayMemory( new float[numSamples]) + { + for (int i = 0; i < numSamples; ++i) { + delayMemory[i] = 0; + } + } + ~FractionalDelay() + { + delete delayMemory; + } + + void setDelay(float samples) + { + assert(samples < numSamples); + delayTime = samples; + } + float run(float input) + { + float ret = getOutput(); + setInput(input); + return ret; + } +protected: + + /** + * get the fractional delayed output, based in delayTime + */ + float getOutput(); + + /** + * send the next input to the delay line + */ + void setInput(float); +private: + /** + * get delay output with integer (non-fractional) delay time + */ + float getDelayedOutput(int delaySamples); + + double delayTime = 0; + int inputPointerIndex = 0; + + /** + * The size of the delay line, in samples + */ + const int numSamples; + + float* delayMemory; +}; + +class RecirculatingFractionalDelay : public FractionalDelay +{ +public: + RecirculatingFractionalDelay(int numSamples) : FractionalDelay(numSamples) + { + } +#if 0 + void setDelay(float samples) + { + delay.setDelay(samples); + } +#endif + void setFeedback(float in_feedback) + { + assert(feedback < 1); + assert(feedback > -1); + feedback = in_feedback; + } + + float run(float); +private: + // FractionalDelay delay; + float feedback = 0; +}; + diff --git a/plugins/community/repos/squinkylabs-plug1/sqsrc/grammar/StochasticGrammar.cpp b/plugins/community/repos/squinkylabs-plug1/sqsrc/grammar/StochasticGrammar.cpp new file mode 100644 index 00000000..d5cfd0f6 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/sqsrc/grammar/StochasticGrammar.cpp @@ -0,0 +1,644 @@ + +#include "StochasticGrammar.h" +#include + +#if 0 +// eventually get rid of this global random generator +std::default_random_engine generator{57}; +std::uniform_real_distribution distribution{0, 1.0}; +float Random::get() +{ + return distribution(generator); +} +#endif + + +/*************************************************************************************************************** + * + * ProductionRuleKeys + * + ***************************************************************************************************************/ + +void ProductionRuleKeys::breakDown(GKEY key, GKEY * outKeys) +{ + switch (key) { + // terminal keys expand to themselves + case sg_w2: + case sg_w: + case sg_h: + case sg_q: + case sg_e: + case sg_e3: + case sg_sx: + case sg_68: + case sg_78: + case sg_98: + case sg_dq: + case sg_dh: + case sg_de: + //case sg_hdq: + //case sg_qhe: + *outKeys++ = key; + *outKeys++ = sg_invalid; + break; + case sg_798: + *outKeys++ = sg_78; + *outKeys++ = sg_98; + *outKeys++ = sg_invalid; + break; + case sg_ww: + *outKeys++ = sg_w; + *outKeys++ = sg_w; + *outKeys++ = sg_invalid; + break; + case sg_hh: + *outKeys++ = sg_h; + *outKeys++ = sg_h; + *outKeys++ = sg_invalid; + break; + case sg_qq: + *outKeys++ = sg_q; + *outKeys++ = sg_q; + *outKeys++ = sg_invalid; + break; + case sg_sxsx: + *outKeys++ = sg_sx; + *outKeys++ = sg_sx; + *outKeys++ = sg_invalid; + break; + case sg_ee: + *outKeys++ = sg_e; + *outKeys++ = sg_e; + *outKeys++ = sg_invalid; + break; + case sg_e3e3e3: + *outKeys++ = sg_e3; + *outKeys++ = sg_e3; + *outKeys++ = sg_e3; + *outKeys++ = sg_invalid; + break; + case sg_hdq: + *outKeys++ = sg_h; + *outKeys++ = sg_dq; + *outKeys++ = sg_invalid; + break; + case sg_hq: + *outKeys++ = sg_h; + *outKeys++ = sg_q; + *outKeys++ = sg_invalid; + break; + case sg_qh: + *outKeys++ = sg_q; + *outKeys++ = sg_h; + *outKeys++ = sg_invalid; + break; + + case sg_qhe: + *outKeys++ = sg_q; + *outKeys++ = sg_h; + *outKeys++ = sg_e; + *outKeys++ = sg_invalid; + break; + case sg_q78: + *outKeys++ = sg_q; + *outKeys++ = sg_78; + *outKeys++ = sg_invalid; + break; + case sg_qe68: + *outKeys++ = sg_q; + *outKeys++ = sg_e; + *outKeys++ = sg_68; + *outKeys++ = sg_invalid; + break; + default: + assert(false); + } +} + + +const char * ProductionRuleKeys::toString(GKEY key) +{ + const char * ret; + switch (key) { + case sg_w2: ret = "2xw"; break; + case sg_ww: ret = "w,w"; break; + case sg_w: ret = "w"; break; + case sg_h: ret = "h"; break; + case sg_hh: ret = "h,h"; break; + case sg_q: ret = "q"; break; + case sg_qq: ret = "q,q"; break; + case sg_e: ret = "e"; break; + case sg_ee: ret = "e,e"; break; + + case sg_e3e3e3: ret = "3e,3e,3e"; break; + case sg_e3: ret = "3e"; break; + + case sg_sx: ret = "sx"; break; + case sg_sxsx: ret = "sx, sx"; break; + case sg_68: ret = "<6/8>"; break; + case sg_78: ret = "<7/8>"; break; + case sg_98: ret = "<9/8>"; break; + case sg_798: ret = "7+9/8"; break; + + case sg_dq: ret = "q."; break; + case sg_dh: ret = "h."; break; + case sg_de: ret = "e."; break; + + case sg_hdq: ret = "h,q."; break; + case sg_qhe: ret = "q,h,e"; break; + case sg_qh: ret = "q,h"; break; + case sg_hq: ret = "h,q"; break; + case sg_q78: ret = "q,<7/8>"; break; + case sg_qe68: ret = "q,e,<6/8>"; break; + + + default: + printf("can't print key %d\n", key); + assert(false); + ret = "error"; + } + return ret; +} + +int ProductionRuleKeys::getDuration(GKEY key) +{ + int ret; + + assert((PPQ % 3) == 0); + switch (key) { + case sg_798: + case sg_w2: ret = 2 * 4 * PPQ; break; + case sg_ww: ret = 2 * 4 * PPQ; break; + case sg_w: ret = 4 * PPQ; break; + case sg_h: ret = 2 * PPQ; break; + case sg_hh: ret = 2 * 2 * PPQ; break; + case sg_q: ret = 1 * PPQ; break; + case sg_qq: ret = 2 * PPQ; break; + case sg_e: + assert((PPQ % 2) == 0); + ret = PPQ / 2; + break; + case sg_ee: ret = PPQ; break; + case sg_sxsx: ret = PPQ / 2; break; + case sg_sx: + assert((PPQ % 4) == 0); + ret = PPQ / 4; + break; + case sg_e3e3e3: ret = PPQ; break; + case sg_e3: + assert(PPQ % 3 == 0); + ret = PPQ / 3; + break; + case sg_68: ret = 6 * (PPQ / 2); break; + case sg_78: ret = 7 * (PPQ / 2); break; + + case sg_q78: + case sg_qe68: + case sg_98: + ret = 9 * (PPQ / 2); break; + + case sg_dq: ret = 3 * PPQ / 2; break; + case sg_dh: ret = 3 * PPQ; break; + case sg_de: ret = 3 * PPQ / 4; break; + + case sg_hdq: ret = 2 * PPQ + 3 * PPQ / 2; break; + case sg_qhe: ret = PPQ * 3 + PPQ / 2; break; + + + + case sg_hq: + case sg_qh: ret = PPQ * 3; break; + + + default: +#ifdef _MSC_VER + printf("can't get dur key %d\n", key); +#endif + assert(false); + ret = 0; + } + return ret; + +} + + +/*************************************************************************************************************** +* +* ProductionRule +* +***************************************************************************************************************/ + + +// generate production, return code for what happened +int ProductionRule::_evaluateRule(const ProductionRule& rule, float random) +{ + assert(random >= 0 && random <= 1); + + int i = 0; + for (bool done2 = false; !done2; ++i) { + assert(i < numEntries); + //printf("prob[%d] is %d\n", i, rule.entries[i].probability); + if (rule.entries[i].probability >= random) { + GKEY code = rule.entries[i].code; + //printf("rule fired on code abs val=%d\n", code); + return code; + } + } + assert(false); // no rule fired + return 0; +} + +void ProductionRule::evaluate(EvaluationState& es, int ruleToEval) +{ + //printf("\n evaluate called on rule #%d\n", ruleToEval); + const ProductionRule& rule = es.rules[ruleToEval]; + +#ifdef _MSC_VER + assert(rule._isValid(ruleToEval)); +#endif + GKEY result = _evaluateRule(rule, es.r()); + if (result == sg_invalid) // request to terminate recursion + { + GKEY code = ruleToEval; // our "real" terminal code is our table index + //printf("production rule #%d terminated\n", ruleToEval); + //printf("rule terminated! execute code %s\n", ProductionRuleKeys::toString(code)); + es.writeSymbol(code); + } else { + //printf("production rule #%d expanded to %d\n", ruleToEval, result); + + // need to expand,then eval all of the expanded codes + + GKEY buffer[ProductionRuleKeys::bufferSize]; + ProductionRuleKeys::breakDown(result, buffer); + for (GKEY * p = buffer; *p != sg_invalid; ++p) { + //printf("expanding rule #%d with %d\n", ruleToEval, *p); + evaluate(es, *p); + } + //printf("done expanding %d\n", ruleToEval); + } +} + +// is the data self consistent, and appropriate for index +#if defined(_MSC_VER) && defined(_DEBUG) +bool ProductionRule::_isValid(int index) const +{ + if (index == sg_invalid) { + printf("rule not allowed in first slot\n"); + return false; + } + + + if (entries[0] == ProductionRuleEntry()) { + printf("rule at index %d is ininitizlied. bad graph (%s)\n", + index, + ProductionRuleKeys::toString(index)); + return false; + } + + float last = -1; + bool foundTerminator = false; + for (int i = 0; !foundTerminator; ++i) { + if (i >= numEntries) { + printf("entries not terminated index=%d 'i' is too big: %d\n", index, i); + return false; + } + const ProductionRuleEntry& e = entries[i]; + if (e.probability > 1.0f) { + printf("probability %f > 1 \n", e.probability); + return false; + } + if (e.probability == 0.f) { + printf("zero probability in rule\n"); + return false; + } + if (e.probability <= last) // probabilities grow + { + printf("probability not growing is %f was %f\n", e.probability, last); + return false; + } + if (e.probability == 1.0f) { + foundTerminator = true; // must have a 255 to end it + if (e.code == index) { + printf("rule terminates on self: recursion not allowed\n"); + return false; + } + } + + if (e.code < sg_invalid || e.code > sg_last) { + printf("rule[%d] entry[%d] had invalid code: %d\n", index, i, e.code); + return false; + } + + // if we are terminating recursion, then by definition our duration is correct + if (e.code != sg_invalid) { + // otherwise, make sure the entry has the right duration + int entryDuration = ProductionRuleKeys::getDuration(e.code); + int ruleDuration = ProductionRuleKeys::getDuration(index); + if (entryDuration != ruleDuration) { + printf("production rule[%d] (name %s) duration mismatch (time not conserved) rule dur = %d entry dur %d\n", + index, ProductionRuleKeys::toString(index), ruleDuration, entryDuration); + return false; + } + } + last = e.probability; + } + return true; +} +#endif + +#ifdef _DEBUG +bool ProductionRule::isGrammarValid(const ProductionRule * rules, int numRules, GKEY firstRule) +{ + //printf("is grammar valid, numRules = %d first = %d\n", numRules, firstRule); + if (firstRule < sg_first) { + printf("first rule index (%d) bad\n", firstRule); + return false; + } + if (numRules != (sg_last + 1)) { + printf("bad number of rules\n"); + return false; + } + + const ProductionRule& r = rules[firstRule]; + + if (!r._isValid(firstRule)) { + return false; + } + + // now, make sure every entry goes to something real + bool foundTerminator = false; + for (int i = 0; !foundTerminator; ++i) { + const ProductionRuleEntry& e = r.entries[i]; + if (e.probability == 1.0f) + foundTerminator = true; // must have a 255 to end it + GKEY _newKey = e.code; + if (_newKey != sg_invalid) { + GKEY outKeys[4]; + ProductionRuleKeys::breakDown(_newKey, outKeys); + for (GKEY * p = outKeys; *p != sg_invalid; ++p) { + if (!isGrammarValid(rules, numRules, *p)) { + printf("followed rules to bad one\n"); + return false; + } + } + } + } + + return true; +} +#endif + + + +/* + +StochasticGrammarDictionary + +maybe move this to a test file? +**/ + +static ProductionRule _rules0[fullRuleTableSize]; +static ProductionRule _rules1[fullRuleTableSize]; +static ProductionRule _rules2[fullRuleTableSize]; +static ProductionRule _rules3[fullRuleTableSize]; + +bool StochasticGrammarDictionary::_didInitRules = false; + +void StochasticGrammarDictionary::initRules() +{ + initRule0(_rules0); + initRule1(_rules1); + initRule2(_rules2); + initRule3(_rules3); +} + + + + +// super dumb one - makes quarter notes +void StochasticGrammarDictionary::initRule0(ProductionRule * rules) +{ + // break w2 into w,w prob 100 + { + ProductionRule& r = rules[sg_w2]; + r.entries[0].probability = 1.0f; + r.entries[0].code = sg_ww; + } + + // break w into h, h + { + ProductionRule& r = rules[sg_w]; + r.entries[0].probability = 1.0f; + r.entries[0].code = sg_hh; + } + + // break h into q,q + { + ProductionRule&r = rules[sg_h]; + r.entries[0].probability = 1.0f; + r.entries[0].code = sg_qq; + } + // stop on q + rules[sg_q].makeTerminal(); + +} + +void StochasticGrammarDictionary::initRule1(ProductionRule * rules) +{ + + // break w2 into w,w prob 100 + { + ProductionRule& r = rules[sg_w2]; + r.entries[0].probability = 1.0f; + r.entries[0].code = sg_ww; + } + + // break w into h, h + { + ProductionRule& r = rules[sg_w]; + r.entries[0].probability = 1.0f; + r.entries[0].code = sg_hh; + } + + // break h into q,q, or h + { + ProductionRule&r = rules[sg_h]; + r.entries[0].probability = .75f; + r.entries[0].code = sg_qq; + + r.entries[1].probability = 1.0f; + r.entries[1].code = sg_invalid; + } + + // stop on q, or make e + { + + ProductionRule&r = rules[sg_q]; + r.entries[0].probability = .3f; + r.entries[0].code = sg_ee; + + r.entries[1].probability = 1.0f; + r.entries[1].code = sg_invalid; + } + + // stop on e, or make sx + { + + ProductionRule&r = rules[sg_e]; + r.entries[0].probability = .3f; + r.entries[0].code = sg_sxsx; + + r.entries[1].probability = 1.0f; + r.entries[1].code = sg_invalid; + } + + rules[sg_sx].makeTerminal(); +} + +void StochasticGrammarDictionary::initRule2(ProductionRule * rules) +{ + // break w2 into 7+9/8 prob 100 + { + ProductionRule& r = rules[sg_w2]; + r.entries[0].probability = 1.0f; + r.entries[0].code = sg_798; + } + + // 9/8 -> different combos + { + ProductionRule& r = rules[sg_98]; + r.entries[0].probability = .5f; + r.entries[0].code = sg_q78; + r.entries[1].probability = 1.0f; + r.entries[1].code = sg_qe68; + } + + // 6/8 -> + { + ProductionRule& r = rules[sg_68]; + r.entries[0].probability = .5f; + r.entries[0].code = sg_hq; + r.entries[1].probability = 1.0f; + r.entries[1].code = sg_qh; + } + + + //78 -> different combos + { + ProductionRule& r = rules[sg_78]; + r.entries[0].probability = .5f; + r.entries[0].code = sg_qhe; + r.entries[1].probability = 1.0f; + r.entries[1].code = sg_hdq; + } + + + + // terminate on these + rules[sg_hdq].makeTerminal(); + rules[sg_qhe].makeTerminal(); + rules[sg_q].makeTerminal(); + rules[sg_dq].makeTerminal(); + rules[sg_h].makeTerminal(); + rules[sg_e].makeTerminal(); +} + +// 3 is like 1, but with some trips +void StochasticGrammarDictionary::initRule3(ProductionRule * rules) +{ + + // break w2 into w,w prob 100 + { + ProductionRule& r = rules[sg_w2]; + r.entries[0].probability = 1.0f; + r.entries[0].code = sg_ww; + } + + // break w into h, h + { + ProductionRule& r = rules[sg_w]; + r.entries[0].probability = 1.0f; + r.entries[0].code = sg_hh; + } + + // break h into q,q, or h + { + ProductionRule&r = rules[sg_h]; + r.entries[0].probability = .75f; + r.entries[0].code = sg_qq; + + r.entries[1].probability = 1.0f; + r.entries[1].code = sg_invalid; + } + + // stop on q, or make e, or make trips + { + + ProductionRule&r = rules[sg_q]; + r.entries[0].probability = .3f; + r.entries[0].code = sg_ee; + + r.entries[1].probability = .7f; + r.entries[1].code = sg_e3e3e3; + + r.entries[2].probability = 1.0f; + r.entries[2].code = sg_invalid; + } + + // expand trip 8ths + rules[sg_e3].makeTerminal(); + + + // stop on e, or make sx, + { + + ProductionRule&r = rules[sg_e]; + r.entries[0].probability = .3f; + r.entries[0].code = sg_sxsx; + + r.entries[1].probability = 1.0f; + r.entries[1].code = sg_invalid; + } + + rules[sg_sx].makeTerminal(); +} + + +int StochasticGrammarDictionary::getNumGrammars() +{ + return 4; +} + +StochasticGrammarDictionary::Grammar StochasticGrammarDictionary::getGrammar(int index) +{ + if (!_didInitRules) + initRules(); + + assert(index >= 0 && index < getNumGrammars()); + + + Grammar ret; + ret.firstRule = sg_w2; + ret.numRules = fullRuleTableSize; + + switch (index) { + case 0: + ret.rules = _rules0; + break; + case 1: + ret.rules = _rules1; + break; + case 2: + ret.rules = _rules2; + break; + case 3: + ret.rules = _rules3; + break; + default: + assert(false); + } + return ret; +} + + + diff --git a/plugins/community/repos/squinkylabs-plug1/sqsrc/grammar/StochasticGrammar.h b/plugins/community/repos/squinkylabs-plug1/sqsrc/grammar/StochasticGrammar.h new file mode 100644 index 00000000..7572be74 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/sqsrc/grammar/StochasticGrammar.h @@ -0,0 +1,205 @@ + +#pragma once + +#include +#include +#include +#include "AudioMath.h" + +/*********************************************** + ********** rhythmic grouping codes ************* +************************************************/ + +typedef unsigned short GKEY; + +/* Rules for keys - super important! + * + * There are two kinds of keys: terminal keys and non-terminal + * + * Terminal keys either directly generate themselves, or may be divided if there is a + * specific production rule to divide them. As an example, sg_q (quarter note) is a terminal key. + * It will always generate itself unless a production rule divides it. + * + * On the other hand sg_w2 is a non-terminal representing all the time in two bars of 4/4. + * sg_w2 will NEVER generate itself. Lacking a specific rule, it will auto generate two whole notes + * + * programmer must be aware of the difference in two places: + * when making production rules for a specific grammar + * when implementing ProductionRuleKeys::breakDown + */ + + +const GKEY sg_invalid = 0; // either uninitialized rule, or return value that stops recursion. + // Note that this means table of production rules must have a dummy entry up front +const GKEY sg_w2 = 1; // duration of two whole notes +const GKEY sg_w = 2; // whole +const GKEY sg_ww = 3; // w,w +const GKEY sg_h = 4; +const GKEY sg_hh = 5; +const GKEY sg_q = 6; +const GKEY sg_qq = 7; +const GKEY sg_e = 8; +const GKEY sg_ee = 9; + +// triplets +const GKEY sg_e3e3e3 = 10; // three trip eights +const GKEY sg_e3 = 11; // trip eight + +const GKEY sg_sx = 12; +const GKEY sg_sxsx = 13; + +// crazy stuff for syncopation (unequal measure divisions) +// Note that there are not "tuples", they are just straight durations +// of a group of notes. +const GKEY sg_68 = 14; // the time duration of 6/8 +const GKEY sg_78 = 15; // the time duration of 7/8 +const GKEY sg_98 = 16; // the time duration of 9/8 +const GKEY sg_798 = 17; // 7/8 + 9/8 = 2w + +// dotted notes +const GKEY sg_dq = 18; // dotted quarter +const GKEY sg_dh = 19; // dotted half +const GKEY sg_de = 20; // dotted eighth + +// odd groupings +const GKEY sg_hdq = 21; // half + dotted Q +const GKEY sg_qhe = 22; // q,h,e +const GKEY sg_hq = 23; // h,q +const GKEY sg_qh = 24; // h,q +const GKEY sg_q78 = 25; // q + 7x8 +const GKEY sg_qe68 = 26; // q+e+6x8 + +const GKEY sg_first = 1; // first valid one +const GKEY sg_last = 26; + +const int fullRuleTableSize = sg_last + 1; + +// Do we really want to use something this coarse? +const int PPQ = 24; + +/* class ProductionRuleKeys + * collection of utility functions around rule keys + */ +class ProductionRuleKeys +{ +public: + static const int bufferSize = 6; // size of a buffer that must be passed to breakDown + + /** + * Turn a key into a 0 terminated list of keys for individual notes. + * If called with a terminal key, just returns itself. + */ + static void breakDown(GKEY key, GKEY * outKeys); + + /** + * get the duration in clocks for a key + */ + static int getDuration(GKEY key); + + /** + * Get a human readable string representation + */ + static const char * toString(GKEY key); +}; + + +/* class ProductionRuleEntry + * A single entry in a production rule. + * if A -> B or A -> C, then each of these would be a separate rule entry + */ +class ProductionRuleEntry +{ +public: + ProductionRuleEntry() : probability(0), code(sg_invalid) + { + } + float probability; // 0 to 1 + GKEY code; // what to do if this one fires +}; + +inline bool operator == (const ProductionRuleEntry& a, const ProductionRuleEntry& b) +{ + return a.probability == b.probability && a.code == b.code; +} + +/* class ProductionRule + * A production rule encapsulates every way that a starting symbol + * can produce others. + * if A -> B or A -> C, then a single production rule could represent this + * + */ +class ProductionRule +{ +public: + static const int numEntries = 3; + class EvaluationState + { + public: + EvaluationState(AudioMath::RandomUniformFunc xr) : r(xr) + { + } + const ProductionRule * rules; + int numRules; + AudioMath::RandomUniformFunc r; //random number generator to use + virtual void writeSymbol(GKEY) + { + } + }; + + ProductionRule() + { + } + + void makeTerminal() + { + entries[0].code = sg_invalid; + entries[0].probability = 1.0f; + } + + /* the data */ + + // each possible production rule for this state + ProductionRuleEntry entries[numEntries]; + + static void evaluate(EvaluationState& es, int ruleToEval); +#ifdef _DEBUG + static bool isGrammarValid(const ProductionRule * rules, int numRules, GKEY firstRule); +#endif +private: + static int _evaluateRule(const ProductionRule& rule, float random); +#ifdef _DEBUG + bool _isValid(int index) const; +#endif +}; + + + +/* class StochasticGrammarDictionary + * + * just a collection of pre-made grammars + * + * 0: simple test + * 1: mixed duration, with some trips + * 2: some syncopation, no trips + */ +class StochasticGrammarDictionary +{ +public: + class Grammar + { + public: + const ProductionRule * rules; + int numRules; + GKEY firstRule; + }; + static Grammar getGrammar(int index); + static int getNumGrammars(); +private: + static bool _didInitRules; + static void initRules(); + static void initRule0(ProductionRule * rules); + static void initRule1(ProductionRule * rules); + static void initRule2(ProductionRule * rules); + static void initRule3(ProductionRule * rules); +}; + diff --git a/plugins/community/repos/squinkylabs-plug1/sqsrc/util/Constants.h b/plugins/community/repos/squinkylabs-plug1/sqsrc/util/Constants.h index 917f436c..e25ee3c8 100644 --- a/plugins/community/repos/squinkylabs-plug1/sqsrc/util/Constants.h +++ b/plugins/community/repos/squinkylabs-plug1/sqsrc/util/Constants.h @@ -10,3 +10,10 @@ const float cGateLow = .8f; */ const float cGateHi = 1.6f; +const float cGateOutHi = 10.0f; + +const float cGateOutLow = 0.0f; + +const int TRIGGER_OUT_TIME_MS = 5; + + diff --git a/plugins/community/repos/squinkylabs-plug1/sqsrc/util/GateTrigger.h b/plugins/community/repos/squinkylabs-plug1/sqsrc/util/GateTrigger.h index 74e19e7f..6de5ec16 100644 --- a/plugins/community/repos/squinkylabs-plug1/sqsrc/util/GateTrigger.h +++ b/plugins/community/repos/squinkylabs-plug1/sqsrc/util/GateTrigger.h @@ -4,16 +4,27 @@ #include +/** + * Gate trigger if for processing gate and trigger inputs. + * Features: + * SchmidtTrigger on the input, to condition it. + * Level sensor, for gate inputs. + * Edge detector, for trigger inputs. + */ class GateTrigger { public: - GateTrigger() : + GateTrigger(bool wantResetLogic) : _gate(false), _trigger(false), - _reset(true) + _reset(wantResetLogic) { } + /** + * Clock in one input sample. Afterwards may query + * gate() and trigger() + */ void go(float v) { const bool newGate = _sc.go(v); diff --git a/plugins/community/repos/squinkylabs-plug1/sqsrc/util/ManagedPool.h b/plugins/community/repos/squinkylabs-plug1/sqsrc/util/ManagedPool.h index 339ab7cc..fca5d02f 100644 --- a/plugins/community/repos/squinkylabs-plug1/sqsrc/util/ManagedPool.h +++ b/plugins/community/repos/squinkylabs-plug1/sqsrc/util/ManagedPool.h @@ -6,7 +6,7 @@ #include "RingBuffer.h" /** - * A very specialized container. Made for holding one free + * A very specialized container. Made for holding one free * work buffers, and making sure they are destroyed. * * At construction time, objects are created to fill the pool. @@ -17,7 +17,7 @@ * * Note that unlike RingBuffer, ManagePool manages T*, not T. * - * All public functions are no-blocking, so may be called from the audio thread + * All public functions are no-blocking, so may be called from the audio thread * without danger. Of course the constructor and destructor are exceptions - they may block. */ template @@ -38,7 +38,7 @@ private: * this ring buffer is where the raw T* are kept. * client pops and pushes here */ - RingBuffer ringBuffer; + SqRingBuffer ringBuffer; std::vector< std::unique_ptr> lifetimeManager; }; diff --git a/plugins/community/repos/squinkylabs-plug1/sqsrc/util/RingBuffer.h b/plugins/community/repos/squinkylabs-plug1/sqsrc/util/RingBuffer.h index 120250c6..95f16799 100644 --- a/plugins/community/repos/squinkylabs-plug1/sqsrc/util/RingBuffer.h +++ b/plugins/community/repos/squinkylabs-plug1/sqsrc/util/RingBuffer.h @@ -10,10 +10,10 @@ * Objects in RingBuffer are not owned by RingBuffer - they will not be destroyed. */ template -class RingBuffer +class SqRingBuffer { public: - RingBuffer() + SqRingBuffer() { for (int i = 0; i < SIZE; ++i) { memory[i] = 0; @@ -38,7 +38,7 @@ private: }; template -inline void RingBuffer::push(T value) +inline void SqRingBuffer::push(T value) { assert(!full()); memory[inIndex] = value; @@ -48,7 +48,7 @@ inline void RingBuffer::push(T value) template -inline T RingBuffer::pop() +inline T SqRingBuffer::pop() { assert(!empty()); T value = memory[outIndex]; @@ -58,19 +58,19 @@ inline T RingBuffer::pop() } template -inline bool RingBuffer::full() const +inline bool SqRingBuffer::full() const { return (inIndex == outIndex) && couldBeFull; } template -inline bool RingBuffer::empty() const +inline bool SqRingBuffer::empty() const { return (inIndex == outIndex) && !couldBeFull; } template -inline void RingBuffer::advance(int &p) +inline void SqRingBuffer::advance(int &p) { if (++p >= SIZE) p = 0; } diff --git a/plugins/community/repos/squinkylabs-plug1/sqsrc/util/TriggerOutput.h b/plugins/community/repos/squinkylabs-plug1/sqsrc/util/TriggerOutput.h new file mode 100644 index 00000000..15ff624a --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/sqsrc/util/TriggerOutput.h @@ -0,0 +1,39 @@ +#pragma once + +#include "GateTrigger.h" + +/** + * Output processing for triggers. + * Outputs a finite duration trigger when gate changes. + */ +class TriggerOutput +{ +public: + TriggerOutput() : + _gateProcessor(false), // defeat reset logic that we don't want + _counter(0), + _duration(TRIGGER_OUT_TIME_MS * 44100 / 1000) // TODO: make this better + { + } + void go(bool gate) + { + if (_counter) { + --_counter; + return; + } + _gateProcessor.go(gate ? cGateOutHi : cGateOutLow); + if (_gateProcessor.trigger()) { + _counter = _duration; + } + + } + float get() const + { + return (_counter > 0) ? cGateOutHi : cGateOutLow; // TODO: 0..10 for gates/trig/clock? + } +private: + GateTrigger _gateProcessor; + int _counter; + const int _duration; +}; + diff --git a/plugins/community/repos/squinkylabs-plug1/src/BlankModule.cpp b/plugins/community/repos/squinkylabs-plug1/src/BlankModule.cpp new file mode 100644 index 00000000..a2152183 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/src/BlankModule.cpp @@ -0,0 +1,96 @@ + +#include +#include "Squinky.hpp" +#include "WidgetComposite.h" + +#include "Blank.h" + + +/** + */ +struct BlankModule : Module +{ +public: + BlankModule(); + /** + * + * Overrides of Module functions + */ + void step() override; + void onSampleRateChange() override; + + Blank blank; +private: + +}; + +void BlankModule::onSampleRateChange() +{ +} + +BlankModule::BlankModule() + : Module(blank.NUM_PARAMS, + blank.NUM_INPUTS, + blank.NUM_OUTPUTS, + blank.NUM_LIGHTS), + blank(this) +{ + onSampleRateChange(); + blank.init(); +} + +void BlankModule::step() +{ + blank.step(); +} + +//////////////////// +// module widget +//////////////////// + +struct BlankWidget : ModuleWidget +{ + BlankWidget(BlankModule *); + + Label* addLabel(const Vec& v, const char* str, const NVGcolor& color = COLOR_BLACK) + { + Label* label = new Label(); + label->box.pos = v; + label->text = str; + label->color = color; + addChild(label); + return label; + } +}; + + +/** + * Widget constructor will describe my implementation structure and + * provide meta-data. + * This is not shared by all modules in the DLL, just one + */ +BlankWidget::BlankWidget(BlankModule *module) : ModuleWidget(module) +{ + box.size = Vec(6 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT); + { + SVGPanel *panel = new SVGPanel(); + panel->box.size = box.size; + panel->setBackground(SVG::load(assetPlugin(plugin, "res/blank_panel.svg"))); + addChild(panel); + } + + + // screws + addChild(Widget::create(Vec(RACK_GRID_WIDTH, 0))); + addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); + addChild(Widget::create(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); +} + +RACK_PLUGIN_MODEL_INIT(squinkylabs_plug1, Blank) { + Model *modelBlankModule = Model::create("Squinky Labs", + "squinkylabs-blank", + "-- Blank --", RANDOM_TAG); + return modelBlankModule; +} diff --git a/plugins/community/repos/squinkylabs-plug1/src/BootyModule.cpp b/plugins/community/repos/squinkylabs-plug1/src/BootyModule.cpp index 6e0b1891..fbc7e716 100644 --- a/plugins/community/repos/squinkylabs-plug1/src/BootyModule.cpp +++ b/plugins/community/repos/squinkylabs-plug1/src/BootyModule.cpp @@ -213,7 +213,7 @@ BootyWidget::BootyWidget(BootyModule *module) : ModuleWidget(module) RACK_PLUGIN_MODEL_INIT(squinkylabs_plug1, Booty) { Model *modelBootyModule = Model::create("Squinky Labs", "squinkylabs-freqshifter", - "Booty Frequency Shifter", EFFECT_TAG, RING_MODULATOR_TAG); + "Booty Shifter: Frequency Shifter", EFFECT_TAG, RING_MODULATOR_TAG); return modelBootyModule; } diff --git a/plugins/community/repos/squinkylabs-plug1/src/CHBModule.cpp b/plugins/community/repos/squinkylabs-plug1/src/CHBModule.cpp new file mode 100644 index 00000000..1b13de2b --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/src/CHBModule.cpp @@ -0,0 +1,391 @@ + + + +#include "Squinky.hpp" +#include "SQWidgets.h" +#include "WidgetComposite.h" + +#include + +#ifdef _CHB +#include "CHB.h" + +/** + */ +struct CHBModule : Module +{ +public: + CHBModule(); + /** + * + * + * Overrides of Module functions + */ + void step() override; + + CHB chb; +private: +}; + +CHBModule::CHBModule() + : Module(chb.NUM_PARAMS, + chb.NUM_INPUTS, + chb.NUM_OUTPUTS, + chb.NUM_LIGHTS), + chb(this) +{ +} + +void CHBModule::step() +{ + chb.step(); +} + +//////////////////// +// module widget +//////////////////// + +struct CHBWidget : ModuleWidget +{ + friend struct CHBEconomyItem; + CHBWidget(CHBModule *); + + /** + * Helper to add a text label to this widget + */ + Label* addLabel(const Vec& v, const char* str, const NVGcolor& color = COLOR_BLACK) + { + Label* label = new Label(); + label->box.pos = v; + label->text = str; + label->color = color; + addChild(label); + return label; + } + + void addHarmonics(CHBModule *module); + void addVCOKnobs(CHBModule *module); + void addOtherKnobs(CHBModule *module); + void addMisc(CHBModule *module); + void addBottomJacks(CHBModule *module); + void resetMe(CHBModule *module); +private: + bool fake; + const float defaultGainParam = .63; + + const int numHarmonics; + CHBModule* const module; + std::vector harmonicParams; + std::vector harmonicParamMemory; + ParamWidget* gainParam=nullptr; +}; + +/** + * Global coordinate contstants + */ +const float colHarmonicsJacks = 21; +const float rowFirstHarmonicJackY = 47; +const float harmonicJackSpacing = 32; +const float harmonicTrimDeltax = 27.5; + +// columns of knobs +const float col1 = 95; +const float col2 = 150; +const float col3 = 214; + +// rows of knobs +const float row1 = 75; +const float row2 = 131; +const float row3 = 228; +const float row4 = 287; +const float row5 = 332; + +const float labelAboveKnob = 33; +const float labelAboveJack = 30; + +inline void CHBWidget::addHarmonics(CHBModule *module) +{ + for (int i = 0; i < numHarmonics; ++i) { + const float row = rowFirstHarmonicJackY + i * harmonicJackSpacing; + addInput(createInputCentered( + Vec(colHarmonicsJacks, row), + module, + module->chb.H0_INPUT + i)); + + const float defaultValue = (i == 0) ? 1 : 0; + auto p = createParamCentered( + Vec(colHarmonicsJacks + harmonicTrimDeltax, row), + module, + module->chb.PARAM_H0 + i, + 0.0f, 1.0f, defaultValue); + addParam(p); + harmonicParams.push_back(p); + } +} + +inline void CHBWidget::addVCOKnobs(CHBModule *module) +{ + addParam(createParamCentered( + Vec(col2, row1), + module, + module->chb.PARAM_OCTAVE, + -5.0f, 4.0f, 0.f)); + addLabel(Vec(col2 - 27, row1 - labelAboveKnob), "Octave"); + + addParam(createParamCentered( + Vec(col3, row1), + module, + module->chb.PARAM_TUNE, + -7.0f, 7.0f, 0)); + addLabel(Vec(col3 - 22, row1 - labelAboveKnob), "Tune"); + + addParam(createParamCentered( + Vec(col2, row2), + module, + module->chb.PARAM_PITCH_MOD_TRIM, + 0, 1.0f, 0.0f)); + addLabel(Vec(col2 - 20, row2 - labelAboveKnob), "Mod"); + + addParam(createParamCentered( + Vec(col3, row2), + module, + module->chb.PARAM_LINEAR_FM_TRIM, + 0, 1.0f, 0.0f)); + addLabel(Vec(col3 - 18, row2 - labelAboveKnob), "LFM"); +} + +inline void CHBWidget::addOtherKnobs(CHBModule *module) +{ + // gain + + gainParam = createParamCentered( + Vec(col1, row2), + module, + module->chb.PARAM_EXTGAIN, + -5.0f, 5.0f, defaultGainParam); + addParam(gainParam); + + addLabel(Vec(col1 - 22, row2 - labelAboveKnob), "Gain"); + + addParam(createParamCentered( + Vec(col1, row2+30), + module, + module->chb.PARAM_EXTGAIN_TRIM, + 0, 1, 0)); + + + // slope + addParam(createParamCentered( + Vec(185, 188), + module, + module->chb.PARAM_SLOPE, + -5, 5, 5)); + addLabel(Vec(185 - 23, 188 - labelAboveKnob), "Slope"); + + //even + addParam(createParamCentered( + Vec(col2, row3), + module, + module->chb.PARAM_MAG_EVEN, + 0, 1, 1)); + addLabel(Vec(col2 - 21.5, row3 - labelAboveKnob), "Even"); + + //odd + addParam(createParamCentered( + Vec(col3, row3), + module, + module->chb.PARAM_MAG_ODD, + 0, 1, 1)); + addLabel(Vec(col3 - 20, row3 - labelAboveKnob), "Odd"); +} + +void CHBWidget::addMisc(CHBModule *module) +{ + auto sw = new SQPush(); + Vec pos(col1, row1); + sw->center(pos); + sw->onClick([this, module]() { + this->resetMe(module); + }); + + addChild(sw); + addLabel(Vec(col1 - 25, row1 - labelAboveKnob), "Preset"); + + const float switchY = 219; + addParam(createParamCentered( + Vec(col1, switchY), + module, + module->chb.PARAM_FOLD, + 0.0f, 1.0f, 0.0f)); + auto l = addLabel(Vec(col1 - 18, 219 - 30), "Fold"); + l->fontSize = 11; + l = addLabel(Vec(col1 - 17, 219 + 10), "Clip"); + l->fontSize = 11; + + // Vec(col1, 165), + addChild(createLightCentered>( + Vec(col1-16, switchY), + module, + module->chb.GAIN_GREEN_LIGHT)); +} + +static const char* labels[] = { + "V/Oct", + "Mod", + "LFM", + "Slope", + "Ext In", + "Gain", + "EG", + "Out", +}; +static const int offsets[] = { + -1, + 1, + 2, + -1, + -1, + 1, + 5, + 2 +}; + +static const int ids[] = { + CHB::CV_INPUT, + CHB::PITCH_MOD_INPUT, + CHB::LINEAR_FM_INPUT, + CHB::SLOPE_INPUT, + CHB::AUDIO_INPUT, + CHB::GAIN_INPUT, + CHB::ENV_INPUT, + CHB::MIX_OUTPUT +}; + +void CHBWidget::addBottomJacks(CHBModule *module) +{ + const int deltaX = .5f + ((col3 - col1) / 3.0); + for (int jackRow = 0; jackRow < 2; ++jackRow) { + for (int jackCol = 0; jackCol < 4; ++jackCol) { + const Vec pos(col1 + deltaX * jackCol, + jackRow == 0 ? row4 : row5); + const int index = jackCol + 4 * jackRow; + + auto color = COLOR_BLACK; + if (index == 7) { + color = COLOR_WHITE; + } + + const int id = ids[index]; + if (index == 7) { + addOutput(createOutputCentered( + pos, + module, + id)); + } else { + addInput(createInputCentered( + pos, + module, + id)); + } + auto l = addLabel(Vec(pos.x - 20 + offsets[index], pos.y - labelAboveJack), + labels[index], + color); + l->fontSize = 11; + // printf("def font size %f\n", l->fontSize); + } + } +} + +void CHBWidget::resetMe(CHBModule *module) +{ + bool isOnlyFundamental = true; + bool isAll = true; + bool havePreset = !harmonicParamMemory.empty(); + const float val0 = harmonicParams[0]->value; + if (val0 < .99) { + isOnlyFundamental = false; + isAll = false; + } + + for (int i = 1; i < numHarmonics; ++i) { + const float value = harmonicParams[i]->value; + if (value < .9) { + isAll = false; + } + + if (value > .1) { + isOnlyFundamental = false; + } + } + + if (!isOnlyFundamental && !isAll) { + // take snapshot + if (harmonicParamMemory.empty()) { + harmonicParamMemory.resize(numHarmonics); + } + for (int i = 0; i < numHarmonics; ++i) { + harmonicParamMemory[i] = harmonicParams[i]->value; + } + } + + // fundamental -> all + if (isOnlyFundamental) { + for (int i = 0; i < numHarmonics; ++i) { + harmonicParams[i]->setValue(1); + } + } + // all -> preset, if any + else if (isAll && havePreset) { + for (int i = 0; i < numHarmonics; ++i) { + harmonicParams[i]->setValue(harmonicParamMemory[i]); + } + } + // preset -> fund. if no preset all -> fund + else { + for (int i = 0; i < numHarmonics; ++i) { + harmonicParams[i]->setValue((i == 0) ? 1 : 0); + } + } + + gainParam->setValue(defaultGainParam); +} + +/** + * Widget constructor will describe my implementation structure and + * provide meta-data. + * This is not shared by all modules in the DLL, just one + */ +CHBWidget::CHBWidget(CHBModule *module) : + ModuleWidget(module), + numHarmonics(module->chb.numHarmonics), + module(module) +{ + box.size = Vec(16 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT); + { + SVGPanel *panel = new SVGPanel(); + panel->box.size = box.size; + panel->setBackground(SVG::load(assetPlugin(plugin, "res/chb_panel.svg"))); + addChild(panel); + } + + addHarmonics(module); + addVCOKnobs(module); + addOtherKnobs(module); + addMisc(module); + addBottomJacks(module); + + // screws + addChild(Widget::create(Vec(RACK_GRID_WIDTH, 0))); + addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); + addChild(Widget::create(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); +} + +RACK_PLUGIN_MODEL_INIT(squinkylabs_plug1, CHB) { + Model *modelCHBModule = Model::create("Squinky Labs", + "squinkylabs-CHB", + "Chebyshev: Waveshaper VCO", EFFECT_TAG, OSCILLATOR_TAG, WAVESHAPER_TAG); + return modelCHBModule; +} +#endif diff --git a/plugins/community/repos/squinkylabs-plug1/src/CPU_Hog.cpp b/plugins/community/repos/squinkylabs-plug1/src/CPU_Hog.cpp index ee529ed2..6643dcce 100644 --- a/plugins/community/repos/squinkylabs-plug1/src/CPU_Hog.cpp +++ b/plugins/community/repos/squinkylabs-plug1/src/CPU_Hog.cpp @@ -31,7 +31,7 @@ struct CPU_HogModule : Module */ void step() override; - int stepsWhileDrawing=0; + int stepsWhileDrawing = 0; private: typedef float T; std::vector< std::shared_ptr > threads; @@ -40,29 +40,30 @@ private: class PServer : public ThreadServer { public: - PServer(std::shared_ptr state) - : ThreadServer(state) + PServer(std::shared_ptr state) + : ThreadServer(state) { } - virtual void threadFunction () override; + virtual void threadFunction() override; - ~PServer() { + ~PServer() + { } private: bool didRun = false; double dummy = 0; }; - void PServer::threadFunction() - { +void PServer::threadFunction() +{ sharedState->serverRunning = true; for (bool done = false; !done; ) { if (sharedState->serverStopRequested.load()) { done = true; } else { // now kill a lot of time - for (int i=0; i< 10000; ++i) { + for (int i = 0; i < 10000; ++i) { dummy += std::log(rand()) * std::sin(rand()); } @@ -71,19 +72,19 @@ private: thread->detach(); sharedState->serverRunning = false; - } +} -CPU_HogModule::CPU_HogModule() : Module(0,0,0,0) +CPU_HogModule::CPU_HogModule() : Module(0, 0, 0, 0) { - for (int i=0; i state = std::make_shared(); std::unique_ptr server(new PServer(state)); - threads.push_back( + threads.push_back( std::make_shared( - state, - std::move(server))); + state, + std::move(server))); } - + // TODO: can we assume onSampleRateChange() gets called first, so this is unnecessary? onSampleRateChange(); } @@ -114,7 +115,7 @@ struct CPU_HogWidget : ModuleWidget std::stringstream s; s << pMod->stepsWhileDrawing; steps->text = s.str(); - + ModuleWidget::draw(vg); if (drawMillisecondSleep) { drawIsSleeping = true; @@ -141,13 +142,13 @@ CPU_HogWidget::CPU_HogWidget(CPU_HogModule *module) : ModuleWidget(module) addChild(panel); } - Label* label=new Label(); + Label* label = new Label(); label->box.pos = Vec(10, 140); label->text = "SleepSteps"; label->color = COLOR_BLACK; addChild(label); - steps=new Label(); + steps = new Label(); steps->box.pos = Vec(10, 180); steps->text = ""; steps->color = COLOR_BLACK; diff --git a/plugins/community/repos/squinkylabs-plug1/src/ColoredNoiseModule.cpp b/plugins/community/repos/squinkylabs-plug1/src/ColoredNoiseModule.cpp index dd461583..7867e5ca 100644 --- a/plugins/community/repos/squinkylabs-plug1/src/ColoredNoiseModule.cpp +++ b/plugins/community/repos/squinkylabs-plug1/src/ColoredNoiseModule.cpp @@ -244,6 +244,3 @@ RACK_PLUGIN_MODEL_INIT(squinkylabs_plug1, ColoredNoise) { "Colored Noise", NOISE_TAG); return modelColoredNoiseModule; } - - - diff --git a/plugins/community/repos/squinkylabs-plug1/src/DGModule.cpp b/plugins/community/repos/squinkylabs-plug1/src/DGModule.cpp new file mode 100644 index 00000000..af81b26b --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/src/DGModule.cpp @@ -0,0 +1,143 @@ + +#include "Squinky.hpp" +#include "WidgetComposite.h" + +#ifdef _DG +#include "daveguide.h" + +/** + */ +struct DGModule : Module +{ +public: + DGModule(); + /** + * + * + * Overrides of Module functions + */ + void step() override; + + Daveguide dave; +private: +}; + +DGModule::DGModule() + : Module(dave.NUM_PARAMS, + dave.NUM_INPUTS, + dave.NUM_OUTPUTS, + dave.NUM_LIGHTS), + dave(this) +{ +} + +void DGModule::step() +{ + dave.step(); +} + +//////////////////// +// module widget +//////////////////// + +struct DGWidget : ModuleWidget +{ + DGWidget(DGModule *); + + /** + * Helper to add a text label to this widget + */ + Label* addLabel(const Vec& v, const char* str, const NVGcolor& color = COLOR_BLACK) + { + Label* label = new Label(); + label->box.pos = v; + label->text = str; + label->color = color; + addChild(label); + return label; + } + + +private: + DGModule* const module; +}; + + + + +/** + * Widget constructor will describe my implementation structure and + * provide meta-data. + * This is not shared by all modules in the DLL, just one + */ +DGWidget::DGWidget(DGModule *module) : + ModuleWidget(module), + module(module) +{ + box.size = Vec(10 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT); + { + SVGPanel *panel = new SVGPanel(); + panel->box.size = box.size; + panel->setBackground(SVG::load(assetPlugin(plugin, "res/blank_panel.svg"))); + addChild(panel); + } + + addLabel(Vec(35, 20), "Daveguide"); + + addInput(createInputCentered( + Vec(40,340), + module, + Daveguide::AUDIO_INPUT)); + + addOutput(createOutputCentered( + Vec(120,340), + module, + Daveguide::AUDIO_OUTPUT)); + + + const float labelDeltaY = 25; + const float gainX = 40; + const float offsetX = 114; + const float labelDeltaX = -20; + const float y = 100; + const float y2 = y + 70; + + addParam(createParamCentered( + Vec(gainX, y), + module, Daveguide::OCTAVE_PARAM, -5, 5, 0)); + addLabel(Vec(gainX+labelDeltaX, y + labelDeltaY), "octave"); + + addParam(createParamCentered( + Vec(offsetX, y), + module, Daveguide::TUNE_PARAM, -5, 5, 0)); + addLabel(Vec(offsetX+labelDeltaX, y + labelDeltaY), "tune"); + + addParam(createParamCentered( + Vec(gainX, y2), + module, Daveguide::DECAY_PARAM, -5, 5, 0)); + addLabel(Vec(gainX+labelDeltaX, y2 + labelDeltaY), "decay"); + + addParam(createParamCentered( + Vec(offsetX, y2), + module, Daveguide::FC_PARAM, -5, 5, 0)); + addLabel(Vec(offsetX+labelDeltaX, y2 + labelDeltaY), "filter"); + + + + + // screws + addChild(Widget::create(Vec(RACK_GRID_WIDTH, 0))); + addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); + addChild(Widget::create(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); +} + +RACK_PLUGIN_MODEL_INIT(squinkylabs_plug1, DG) { + Model *modelDGModule = Model::create("Squinky Labs", + "squinkylabs-dvg", + "dg", EFFECT_TAG, OSCILLATOR_TAG, WAVESHAPER_TAG); + return modelDGModule; +} +#endif + diff --git a/plugins/community/repos/squinkylabs-plug1/src/EV3Module.cpp b/plugins/community/repos/squinkylabs-plug1/src/EV3Module.cpp new file mode 100644 index 00000000..ec9a7074 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/src/EV3Module.cpp @@ -0,0 +1,421 @@ +#include "Squinky.hpp" +#include "WaveformSelector.h" +#include "SQWidgets.h" +#include "WidgetComposite.h" + +#ifdef _EV3 + +#include "EV3.h" +#include + +struct EV3Module : Module +{ + EV3Module(); + void step() override; + EV3 ev3; +}; + +EV3Module::EV3Module() + : Module(ev3.NUM_PARAMS, + ev3.NUM_INPUTS, + ev3.NUM_OUTPUTS, + ev3.NUM_LIGHTS), + ev3(this) +{ +} + +void EV3Module::step() +{ + ev3.step(); +} + +/************************************************************/ + +class PitchDisplay +{ +public: + PitchDisplay(EV3Module * mod) : module(mod) {} + void step(); + + /** + * Labels must be added in order + */ + void addOctLabel(Label*); + void addSemiLabel(Label*); + +private: + EV3Module * const module; + + std::vector octLabels; + std::vector semiLabels; + std::vector semiX; + int lastOctave[3] = {100, 100, 100}; + int lastSemi[3] = {100, 100, 100}; + void update(int); +}; + +void PitchDisplay::step() +{ + const int delta = EV3::OCTAVE2_PARAM - EV3::OCTAVE1_PARAM; + for (int i=0; i<3; ++i) { + const int octaveParam = EV3::OCTAVE1_PARAM + delta * i; + const int semiParam = EV3::SEMI1_PARAM + delta * i; + const int oct = module->params[octaveParam].value; + const int semi = module->params[semiParam].value; + if (semi != lastSemi[i] || oct != lastOctave[i]) { + lastSemi[i] = semi; + lastOctave[i] = oct; + update(i); + } + } +} + +static const char* names[] = { + "0", + "m2nd", + "2nd", + "m3rd", + "M3rd", + "4th", + "Dim5th", + "5th", + "m6th", + "M6th", + "m7th", + "M7th", + "oct" +}; + +static int offsets[] = { + 11, + 0, + 5, // 2nd + 0, + 0, + 4, // 4th + -2, + 3, // 5th + 0, + 0, + 0, + 2, + 2 // M7 +}; + +void PitchDisplay::addOctLabel(Label* l) +{ + octLabels.push_back(l); +} + +void PitchDisplay::addSemiLabel(Label* l) +{ + semiLabels.push_back(l); + semiX.push_back(l->box.pos.x); +} + +void PitchDisplay::update(int osc) { + std::stringstream so; + int oct = 5 + lastOctave[osc]; + int semi = lastSemi[osc]; + + if (semi < 0) { + --oct; + semi += 12; + } + so << oct; + + octLabels[osc]->text = so.str(); + semiLabels[osc]->text = names[semi]; + semiLabels[osc]->box.pos.x = semiX[osc] + offsets[semi]; +} + +struct EV3Widget : ModuleWidget +{ + EV3Widget(EV3Module *); + void makeSections(EV3Module *); + void makeSection(EV3Module *, int index); + void makeInputs(EV3Module *); + void makeInput(EV3Module* module, int row, int col, int input, + const char* name, float labelDeltaX); + void makeOutputs(EV3Module *); + Label* addLabel(const Vec& v, const char* str, const NVGcolor& color = COLOR_BLACK) + { + Label* label = new Label(); + label->box.pos = v; + label->text = str; + label->color = color; + addChild(label); + return label; + } + + void step() override; + + PitchDisplay pitchDisplay; +}; + +void EV3Widget::step() +{ + ModuleWidget::step(); + pitchDisplay.step(); +} + +const int dy = -6; // apply to everything + +void EV3Widget::makeSection(EV3Module *module, int index) +{ + const float x = (30-4) + index * (86+4); + const float x2 = x + (36+2); + const float y = 80+dy; + const float y2 = y + 56+dy; + const float y3 = y2 + 40 + dy; + + const int delta = EV3::OCTAVE2_PARAM - EV3::OCTAVE1_PARAM; + + pitchDisplay.addOctLabel( + addLabel(Vec(x - 10, y - 32), "Oct")); + pitchDisplay.addSemiLabel( + addLabel(Vec(x2 - 22, y - 32), "Semi")); + + addParam(createParamCentered( + Vec(x, y), module, + EV3::OCTAVE1_PARAM + delta * index, + -5.0f, 4.0f, 0.f)); + + addParam(createParamCentered( + Vec(x2, y), module, + EV3::SEMI1_PARAM + delta * index, + -11.f, 11.0f, 0.f)); + + addParam(createParamCentered( + Vec(x, y2), module, + EV3::FINE1_PARAM + delta * index, + -1.0f, 1.0f, 0)); + addLabel(Vec(x - 20, y2 - 34), "Fine"); + + addParam(createParamCentered( + Vec(x2, y2), module, + EV3::FM1_PARAM + delta * index, + 0.f, 1.f, 0)); + addLabel(Vec(x2 - 20, y2 - 34), "Mod"); + + const float dy = 27; + const float x0 = x; + + addParam(createParamCentered( + Vec(x0, y3), module, EV3::PW1_PARAM + delta * index, + -1.f, 1.f, 0)); + if (index == 0) + addLabel(Vec(x0 + 10, y3 - 12), "pw"); + + addParam(createParamCentered( + Vec(x0, y3 + dy), module, + EV3::PWM1_PARAM + delta * index, + -1.0f, 1.0f, 0)); + if (index == 0) + addLabel(Vec(x0 + 10, y3 + dy - 12), "pwm"); + + // sync switches + const float swx = x + 29; + const float lbx = x + 19; + + if (index != 0) { + addParam(ParamWidget::create( + Vec(swx, y3), module, EV3::SYNC1_PARAM + delta * index, + 0.0f, 1.0f, 0.0f)); + addLabel(Vec(lbx-2, y3 - 20), "sync"); + addLabel(Vec(lbx+1, y3 + 20), "off"); + } + + const float y4 = y3 + 43; + const float xx = x - 12; + // include one extra wf - none + const float numWaves = (float) EV3::Waves::END; + const float defWave = (float) EV3::Waves::SIN; + addParam(ParamWidget::create( + Vec(xx, y4), + module, + EV3::WAVE1_PARAM + delta * index, + 0.0f, numWaves, defWave)); +} + +void EV3Widget::makeSections(EV3Module* module) +{ + makeSection(module, 0); + makeSection(module, 1); + makeSection(module, 2); +} + +const float row1Y = 280+dy-4; // -4 = move the last section up +const float rowDY = 30; +const float colDX = 45; + +void EV3Widget::makeInput(EV3Module* module, int row, int col, + int inputNum, const char* name, float labelXDelta) +{ + EV3::InputIds input = EV3::InputIds(inputNum); + const float y = row1Y + row * rowDY; + const float x = 14 + col * colDX; + const float labelX = labelXDelta + x - 6; + addInput(Port::create( + Vec(x, y), Port::INPUT, module, input)); + if (row == 0) + addLabel(Vec(labelX, y - 20), name); +} + +void EV3Widget::makeInputs(EV3Module* module) +{ +#ifdef _FLIPROWS + auto row2Input = [](int row, EV3::InputIds baseInput) { + // map inputs directly to rows + return baseInput + (2 - row); + }; +#else + // Row 0 = top row, 2 = bottom row + auto row2Input = [](int row, EV3::InputIds baseInput) { + // map inputs directly to rows + return baseInput + row; + }; +#endif + + for (int row = 0; row < 3; ++row) { + makeInput(module, row, 0, + row2Input(row, EV3::CV1_INPUT), + "V/oct", -3); + makeInput(module, row, 1, + row2Input(row, EV3::FM1_INPUT), + "Fm", 3); + makeInput(module, row, 2, + row2Input(row, EV3::PWM1_INPUT), + "Pwm", -2); + } +} + + +#ifdef _FLIPROWS +void EV3Widget::makeOutputs(EV3Module *) +{ + const float x = 160; + const float trimY = row1Y + 11; + const float outX = x + 30; + + + addParam(createParamCentered( + Vec(x, trimY), module, EV3::MIX3_PARAM, + 0.0f, 1.0f, 0)); + addParam(createParamCentered( + Vec(x, trimY + rowDY), module, EV3::MIX2_PARAM, + 0.0f, 1.0f, 0)); + addParam(createParamCentered( + Vec(x, trimY + 2 * rowDY), module, EV3::MIX1_PARAM, + 0.0f, 1.0f, 0)); + + addOutput(Port::create( + Vec(outX, row1Y), + Port::OUTPUT, module, EV3::VCO3_OUTPUT)); + addLabel(Vec(outX + 20, row1Y + 0 * rowDY+2), "3", COLOR_WHITE); + + addOutput(Port::create( + Vec(outX, row1Y + rowDY), + Port::OUTPUT, module, EV3::VCO2_OUTPUT)); + addLabel(Vec(outX + 20, row1Y + 1 * rowDY+2), "2", COLOR_WHITE); + + addOutput(Port::create( + Vec(outX, row1Y + 2 * rowDY), + Port::OUTPUT, module, EV3::VCO1_OUTPUT)); + addLabel(Vec(outX + 20, row1Y + 2 * rowDY+2), "1", COLOR_WHITE); + + addOutput(Port::create( + Vec(outX + 41, row1Y + rowDY), + Port::OUTPUT, module, EV3::MIX_OUTPUT)); + addLabel(Vec(outX + 41, row1Y + rowDY - 17), "+", COLOR_WHITE); + addLabel(Vec(outX + 41, row1Y + rowDY + 20), "+", COLOR_WHITE); + +} +#endif + + +#ifndef _FLIPROWS +void EV3Widget::makeOutputs(EV3Module *) +{ + const float x = 160; + const float trimY = row1Y + 11; + const float outX = x + 30; + + + addParam(createParamCentered( + Vec(x, trimY), module, EV3::MIX1_PARAM, + 0.0f, 1.0f, 0)); + addParam(createParamCentered( + Vec(x, trimY + rowDY), module, EV3::MIX2_PARAM, + 0.0f, 1.0f, 0)); + addParam(createParamCentered( + Vec(x, trimY + 2 * rowDY), module, EV3::MIX3_PARAM, + 0.0f, 1.0f, 0)); + + addOutput(Port::create( + Vec(outX, row1Y), + Port::OUTPUT, module, EV3::VCO1_OUTPUT)); + addLabel(Vec(outX + 20, row1Y + 0 * rowDY+2), "1", COLOR_WHITE); + + addOutput(Port::create( + Vec(outX, row1Y + rowDY), + Port::OUTPUT, module, EV3::VCO2_OUTPUT)); + addLabel(Vec(outX + 20, row1Y + 1 * rowDY+2), "2", COLOR_WHITE); + + addOutput(Port::create( + Vec(outX, row1Y + 2 * rowDY), + Port::OUTPUT, module, EV3::VCO3_OUTPUT)); + addLabel(Vec(outX + 20, row1Y + 2 * rowDY+2), "3", COLOR_WHITE); + + addOutput(Port::create( + Vec(outX + 41, row1Y + rowDY), + Port::OUTPUT, module, EV3::MIX_OUTPUT)); + addLabel(Vec(outX + 41, row1Y + rowDY - 17), "+", COLOR_WHITE); + addLabel(Vec(outX + 41, row1Y + rowDY + 20), "+", COLOR_WHITE); + +} +#endif + +/** + * Widget constructor will describe my implementation structure and + * provide meta-data. + * This is not shared by all modules in the DLL, just one + */ +EV3Widget::EV3Widget(EV3Module *module) : + ModuleWidget(module), + pitchDisplay(module) +{ + box.size = Vec(18 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT); + { + SVGPanel *panel = new SVGPanel(); + panel->box.size = box.size; + panel->setBackground(SVG::load(assetPlugin(plugin, "res/ev3_panel.svg"))); + addChild(panel); + } + + // auto l = addLabel( Vec(110, 10), "EV3"); + // l->fontSize = 18; + + makeSections(module); + makeInputs(module); + makeOutputs(module); + + // screws + addChild(Widget::create(Vec(RACK_GRID_WIDTH, 0))); + addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); + addChild(Widget::create(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + +} + + + +RACK_PLUGIN_MODEL_INIT(squinkylabs_plug1, EV3) { + Model *modelEV3Module = Model::create("Squinky Labs", + "squinkylabs-ev3", + "EV3: Triple VCO with even waveform", OSCILLATOR_TAG); + return modelEV3Module; +} +#endif + diff --git a/plugins/community/repos/squinkylabs-plug1/src/EVModule.cpp b/plugins/community/repos/squinkylabs-plug1/src/EVModule.cpp new file mode 100644 index 00000000..6410d199 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/src/EVModule.cpp @@ -0,0 +1,225 @@ + +#include +#include "Squinky.hpp" +#include "WidgetComposite.h" + +#ifdef _EV + +#include "EvenVCO.h" + + +/** + */ +struct EVModule : Module +{ +public: + EVModule(); + /** + * + * Overrides of Module functions + */ + void step() override; + void onSampleRateChange() override; + + EvenVCO vco; +private: +}; + +void EVModule::onSampleRateChange() +{ + // TODO + // float rate = engineGetSampleRate(); + //vco.setSampleRate(rate); + printf("on sample rate change\n"); fflush(stdout); +} + +EVModule::EVModule() + : Module(vco.NUM_PARAMS, + vco.NUM_INPUTS, + vco.NUM_OUTPUTS, + vco.NUM_LIGHTS), + vco(this) +{ + onSampleRateChange(); + // vco.init(); + printf("ctor\n"); fflush(stdout); +} + +void EVModule::step() +{ + vco.step(); +} + +//////////////////// +// module widget +//////////////////// + +struct EVWidget : ModuleWidget +{ + EVWidget(EVModule *); + + Label* addLabel(const Vec& v, const char* str, const NVGcolor& color = COLOR_BLACK) + { + Label* label = new Label(); + label->box.pos = v; + label->text = str; + label->color = color; + addChild(label); + return label; + } + void draw(NVGcontext *vg) override; + + void addPWM(EVModule *, float verticalShift); + void addMiddle(EVModule *, float verticalShift); + void addOutputs(EVModule *, float verticalShift); + + Label* octaveLabel; + ParamWidget* octaveKnob; + int lastOctave = -100; +}; + + void EVWidget::draw(NVGcontext *vg) +{ + float value = octaveKnob->value; + int oct = roundf(value); + if (oct != lastOctave) { + const char * val="yy"; + switch(oct) { + case -5: + val = "32'"; + break; + case -4: + val = "16'"; + break; + case -3: + val = "8'"; + break; + case -2: + val = "4'"; + break; + case -1: + val = "2'"; + break; + case 0: + val = "1'"; + break; + case 1: + val = "1/2'"; + break; + case 2: + val = "1/4'"; + break; + case 3: + val = "1/8'"; + break; + case 4: + val = "1/16'"; + break; + } + + + + octaveLabel->text = val; + } + + ModuleWidget::draw(vg); +} + +void EVWidget::addPWM(EVModule * module, float verticalShift) +{ + addInput(Port::create(Vec(72, 236+verticalShift), + Port::INPUT, module, module->vco.PWM_INPUT)); + + addParam(ParamWidget::create(Vec(16, 212+verticalShift), + module, module->vco.PWM_PARAM, -1.0, 1.0, 0.0)); + + addLabel(Vec(30, 246+verticalShift), "pwm"); +} + +void EVWidget::addMiddle(EVModule * module, float verticalShift) +{ + addParam(ParamWidget::create(Vec(73, 125+verticalShift), + module, module->vco.TUNE_PARAM, -7.0, 7.0, 0.0)); + addLabel(Vec(69, 164 + verticalShift), "tune"); + + addInput(Port::create(Vec(10, 124+verticalShift), + Port::INPUT, module, module->vco.PITCH1_INPUT)); + + addInput(Port::create(Vec(34, 160+verticalShift), + Port::INPUT, module, module->vco.PITCH2_INPUT)); + addLabel(Vec(6, 164+verticalShift), "cv"); + addInput(Port::create(Vec(62, 194+verticalShift), + Port::INPUT, module, module->vco.FM_INPUT)); + addLabel(Vec(84, 200+verticalShift), "fm"); +// addInput(Port::create(Vec(86, 189), Port::INPUT, module, module->vco.SYNC_INPUT)); + + +} + +void EVWidget::addOutputs(EVModule * module, float verticalShift) +{ + const float penultimateRow = 273 + verticalShift; + const float penultimateLabelRow = penultimateRow + 24; + + addOutput(Port::create(Vec(10, penultimateRow), Port::OUTPUT, module, module->vco.TRI_OUTPUT)); + addLabel(Vec(8, penultimateLabelRow), "tri"); + + addOutput(Port::create(Vec(87, penultimateRow), Port::OUTPUT, module, module->vco.SINE_OUTPUT)); + addLabel(Vec(84, penultimateLabelRow), "sin"); + + const float bottomRow = 317 + verticalShift; // 320 -> 317 to make room? + const float bottomLabelRow = bottomRow + 24; + + addOutput(Port::create(Vec(48, bottomRow), Port::OUTPUT, module, module->vco.EVEN_OUTPUT)); + addLabel(Vec(38, bottomLabelRow), "even"); + addOutput(Port::create(Vec(10, bottomRow), Port::OUTPUT, module, module->vco.SAW_OUTPUT)); + addLabel(Vec(4, bottomLabelRow), "saw"); + addOutput(Port::create(Vec(87, bottomRow), Port::OUTPUT, module, module->vco.SQUARE_OUTPUT)); + addLabel(Vec(83, bottomLabelRow), "sqr"); +} + + +/** + * Widget constructor will describe my implementation structure and + * provide meta-data. + * This is not shared by all modules in the DLL, just one + */ +EVWidget::EVWidget(EVModule *module) : ModuleWidget(module) +{ + box.size = Vec(8 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT); + { + SVGPanel *panel = new SVGPanel(); + panel->box.size = box.size; + panel->setBackground(SVG::load(assetPlugin(plugin, "res/blank_panel.svg"))); + addChild(panel); + } + + addPWM(module, -10); + addMiddle(module, -14); + addOutputs(module, -12); + + addChild(Widget::create(Vec(15, 0))); + addChild(Widget::create(Vec(15, 365))); + addChild(Widget::create(Vec(15 * 6, 0))); + addChild(Widget::create(Vec(15 * 6, 365))); + + octaveKnob = ParamWidget::create(Vec(34, 32), + module, module->vco.OCTAVE_PARAM, -5.0, 4.0, 0.0); + + addParam(octaveKnob); + addLabel(Vec(20, 88), "octave:"); + //label->fontSize = 16; + + octaveLabel = addLabel(Vec(70, 90), "xx"); + } + +RACK_PLUGIN_MODEL_INIT(squinkylabs_plug1, EV) { + Model *modelEVModule = Model::create("Squinky Labs", + "squinkylabs-evco", + "EvilVCO", OSCILLATOR_TAG); + return modelEVModule; +} + +#endif + diff --git a/plugins/community/repos/squinkylabs-plug1/src/FunVModule.cpp b/plugins/community/repos/squinkylabs-plug1/src/FunVModule.cpp new file mode 100644 index 00000000..e2eb4dd1 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/src/FunVModule.cpp @@ -0,0 +1,179 @@ + +#include +#include "Squinky.hpp" +#include "WidgetComposite.h" + +#if 1 +#include "FunVCOComposite.h" + +/** + */ +struct FunVModule : Module +{ +public: + FunVModule(); + /** + * + * Overrides of Module functions + */ + void step() override; + void onSampleRateChange() override; + + FunVCOComposite vco; +private: +}; + +void FunVModule::onSampleRateChange() +{ + float rate = engineGetSampleRate(); + vco.setSampleRate(rate); +} + +FunVModule::FunVModule() + : Module(vco.NUM_PARAMS, + vco.NUM_INPUTS, + vco.NUM_OUTPUTS, + vco.NUM_LIGHTS), + vco(this) +{ + onSampleRateChange(); +} + +void FunVModule::step() +{ + vco.step(); +} + +//////////////////// +// module widget +//////////////////// + +struct FunVWidget : ModuleWidget +{ + FunVWidget(FunVModule *); + + void addTop3(FunVModule *, float verticalShift); + void addMiddle4(FunVModule *, float verticalShift); + void addJacks(FunVModule *, float verticalShift); + + Label* addLabel(const Vec& v, const char* str, const NVGcolor& color = COLOR_BLACK) + { + Label* label = new Label(); + label->box.pos = v; + label->text = str; + label->color = color; + addChild(label); + return label; + } +}; + +void FunVWidget::addTop3(FunVModule * module, float verticalShift) +{ + const float left = 8; + const float right = 112; + const float center = 49; + + addParam(ParamWidget::create(Vec(left, 66 + verticalShift), + module, module->vco.MODE_PARAM, 0.0f, 1.0f, 1.0f)); + addLabel(Vec(left -4, 48+ verticalShift), "anlg"); + addLabel(Vec(left -3, 108+ verticalShift), "dgtl"); + + addParam(ParamWidget::create(Vec(center, 61 + verticalShift), + module, module->vco.FREQ_PARAM, -54.0f, 54.0f, 0.0f)); + auto label = addLabel(Vec(center + 3, 40+ verticalShift), "pitch"); + label->fontSize = 16; + + addParam(ParamWidget::create(Vec(right, 66 + verticalShift), + module, module->vco.SYNC_PARAM, 0.0f, 1.0f, 1.0f)); + addLabel(Vec(right-5, 48+ verticalShift), "hard"); + addLabel(Vec(right-2, 108+ verticalShift), "soft"); +} + +void FunVWidget::addMiddle4(FunVModule * module, float verticalShift) +{ + addParam(ParamWidget::create(Vec(23, 143 + verticalShift), + module, module->vco.FINE_PARAM, -1.0f, 1.0f, 0.0f)); + addLabel(Vec(25, 124 +verticalShift), "fine"); + + addParam(ParamWidget::create(Vec(91, 143 + verticalShift), + module, module->vco.PW_PARAM, 0.0f, 1.0f, 0.5f)); + addLabel(Vec(84, 124 +verticalShift), "p width"); + + addParam(ParamWidget::create(Vec(23, 208 + verticalShift), + module, module->vco.FM_PARAM, 0.0f, 1.0f, 0.0f)); + addLabel(Vec(19, 188 +verticalShift), "fm cv"); + + addParam(ParamWidget::create(Vec(91, 208 + verticalShift), + module, module->vco.PWM_PARAM, 0.0f, 1.0f, 0.0f)); + addLabel(Vec(82, 188 +verticalShift), "pwm cv"); +} + +void FunVWidget::addJacks(FunVModule * module, float verticalShift) +{ + const float col1 = 12; + const float col2 = 46; + const float col3 = 81; + const float col4 = 115; + const float outputLabelY = 300; + + addInput(Port::create(Vec(col1, 273+verticalShift), Port::INPUT, module, module->vco.PITCH_INPUT)); + addLabel(Vec(9, 255+verticalShift), "cv"); + + addInput(Port::create(Vec(col2, 273+verticalShift), Port::INPUT, module, module->vco.FM_INPUT)); + addLabel(Vec(43, 255+verticalShift), "fm"); + + addInput(Port::create(Vec(col3, 273+verticalShift), Port::INPUT, module, module->vco.SYNC_INPUT)); + addLabel(Vec(72, 255+verticalShift), "sync"); + + addInput(Port::create(Vec(col4, 273+verticalShift), Port::INPUT, module, module->vco.PW_INPUT)); + addLabel(Vec(107, 255+verticalShift), "pwm"); + + addOutput(Port::create(Vec(col1, 317+verticalShift), Port::OUTPUT, module, module->vco.SIN_OUTPUT)); + addLabel(Vec(8, outputLabelY+verticalShift), "sin", COLOR_WHITE); + + addOutput(Port::create(Vec(col2, 317+verticalShift), Port::OUTPUT, module, module->vco.TRI_OUTPUT)); + addLabel(Vec(44, outputLabelY+verticalShift), "tri", COLOR_WHITE); + + addOutput(Port::create(Vec(col3, 317+verticalShift), Port::OUTPUT, module, module->vco.SAW_OUTPUT)); + addLabel(Vec(75, outputLabelY+verticalShift), "saw", COLOR_WHITE); + + addOutput(Port::create(Vec(col4, 317+verticalShift), Port::OUTPUT, module, module->vco.SQR_OUTPUT)); + addLabel(Vec(111, outputLabelY+verticalShift), "sqr", COLOR_WHITE); +} + +/** + * Widget constructor will describe my implementation structure and + * provide meta-data. + * This is not shared by all modules in the DLL, just one + */ +FunVWidget::FunVWidget(FunVModule *module) : ModuleWidget(module) +{ + box.size = Vec(10 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT); + { + SVGPanel *panel = new SVGPanel(); + panel->box.size = box.size; + panel->setBackground(SVG::load(assetPlugin(plugin, "res/fun_panel.svg"))); + addChild(panel); + } + + addTop3(module, 0); + addMiddle4(module, 0); + addJacks(module, 0); + + // screws + addChild(Widget::create(Vec(RACK_GRID_WIDTH, 0))); + addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); + addChild(Widget::create(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); +} + +RACK_PLUGIN_MODEL_INIT(squinkylabs_plug1, FunV) { + Model *modelFunVModule = Model::create("Squinky Labs", + "squinkylabs-funv", + "Functional VCO-1", OSCILLATOR_TAG); + return modelFunVModule; +} + +#endif + diff --git a/plugins/community/repos/squinkylabs-plug1/src/GMRModule.cpp b/plugins/community/repos/squinkylabs-plug1/src/GMRModule.cpp new file mode 100644 index 00000000..84f0e749 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/src/GMRModule.cpp @@ -0,0 +1,104 @@ + +#include +#include "Squinky.hpp" +#include "WidgetComposite.h" + +#ifdef _GMR +#include "GMR.h" + + +/** + */ +struct GMRModule : Module +{ +public: + GMRModule(); + /** + * + * Overrides of Module functions + */ + void step() override; + void onSampleRateChange() override; + + GMR gmr; +private: +}; + +void GMRModule::onSampleRateChange() +{ + float rate = engineGetSampleRate(); + gmr.setSampleRate(rate); +} + +GMRModule::GMRModule() + : Module(gmr.NUM_PARAMS, + gmr.NUM_INPUTS, + gmr.NUM_OUTPUTS, + gmr.NUM_LIGHTS), + gmr(this) +{ + onSampleRateChange(); + gmr.init(); +} + +void GMRModule::step() +{ + gmr.step(); +} + +//////////////////// +// module widget +//////////////////// + +struct GMRWidget : ModuleWidget +{ + GMRWidget(GMRModule *); + + void addLabel(const Vec& v, const char* str, const NVGcolor& color = COLOR_BLACK) + { + Label* label = new Label(); + label->box.pos = v; + label->text = str; + label->color = color; + addChild(label); + } +}; + + +/** + * Widget constructor will describe my implementation structure and + * provide meta-data. + * This is not shared by all modules in the DLL, just one + */ +GMRWidget::GMRWidget(GMRModule *module) : ModuleWidget(module) +{ + box.size = Vec(6 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT); + { + SVGPanel *panel = new SVGPanel(); + panel->box.size = box.size; + panel->setBackground(SVG::load(assetPlugin(plugin, "res/blank_panel.svg"))); + addChild(panel); + } + + addInput(Port::create( + Vec(40, 200), Port::INPUT, module, module->gmr.CLOCK_INPUT)); + addOutput(Port::create( + Vec(40, 300), Port::OUTPUT, module, module->gmr.TRIGGER_OUTPUT)); + + + // screws + addChild(Widget::create(Vec(RACK_GRID_WIDTH, 0))); + addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); + addChild(Widget::create(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); +} + +RACK_PLUGIN_MODEL_INIT(squinkylabs_plug1, GMR) { + Model *modelGMRModule = Model::create("Squinky Labs", + "squinkylabs-GMR", + "GMR", EFFECT_TAG, LFO_TAG); + return modelGMRModule; +} +#endif + diff --git a/plugins/community/repos/squinkylabs-plug1/src/GrayModule.cpp b/plugins/community/repos/squinkylabs-plug1/src/GrayModule.cpp new file mode 100644 index 00000000..a4d8d5cf --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/src/GrayModule.cpp @@ -0,0 +1,136 @@ + +#include "Squinky.hpp" +#include "WidgetComposite.h" + +#include "Gray.h" + +/** + */ +struct GrayModule : Module +{ +public: + GrayModule(); + /** + * + * + * Overrides of Module functions + */ + void step() override; + + Gray gray; +private: +}; + +GrayModule::GrayModule() + : Module(gray.NUM_PARAMS, + gray.NUM_INPUTS, + gray.NUM_OUTPUTS, + gray.NUM_LIGHTS), + gray(this) +{ +} + +void GrayModule::step() +{ + gray.step(); +} + +//////////////////// +// module widget +//////////////////// + +struct GrayWidget : ModuleWidget +{ + GrayWidget(GrayModule *); + + /** + * Helper to add a text label to this widget + */ + Label* addLabel(const Vec& v, const char* str, const NVGcolor& color = COLOR_BLACK) + { + Label* label = new Label(); + label->box.pos = v; + label->text = str; + label->color = color; + addChild(label); + return label; + } + +private: + void addBits(GrayModule *module); + + GrayModule* const module; +}; + +const float jackCol = 99.5; +const float ledCol = 69; +const float vertSpace = 31; // 31.4 +const float firstBitY = 64; + +inline void GrayWidget::addBits(GrayModule *module) +{ + printf("add bits\n"); fflush(stdout); + for (int i=0; i<8; ++i) { + const Vec v(jackCol, firstBitY + i * vertSpace); + addOutput(createOutputCentered( + v, + module, + Gray::OUTPUT_0 + i)); + addChild(ModuleLightWidget::create>( + Vec(ledCol, firstBitY + i * vertSpace - 6), + module, + Gray::LIGHT_0+i)); + } +} + +/** + * Widget constructor will describe my implementation structure and + * provide meta-data. + * This is not shared by all modules in the DLL, just one + */ +GrayWidget::GrayWidget(GrayModule *module) : + ModuleWidget(module), + module(module) +{ + box.size = Vec(8 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT); + { + SVGPanel *panel = new SVGPanel(); + panel->box.size = box.size; + panel->setBackground(SVG::load(assetPlugin(plugin, "res/gray.svg"))); + addChild(panel); + } + + addBits(module); + addInput(createInputCentered( + Vec(22, 339), + module, + Gray::INPUT_CLOCK)); + addLabel(Vec(0, 310), "Clock"); + + addParam(createParamCentered( + Vec(71,33), + module, + Gray::PARAM_CODE, + 0.0f, 1.0f, 0.0f)); + addLabel(Vec(2, 27), "Balanced"); + + addOutput(createOutputCentered( + Vec(100, 339), + module, + Gray::OUTPUT_MIXED)); + addLabel(Vec(81, 310), "Mix", COLOR_WHITE); + + // screws + addChild(Widget::create(Vec(RACK_GRID_WIDTH, 0))); + addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); + addChild(Widget::create(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); +} + +RACK_PLUGIN_MODEL_INIT(squinkylabs_plug1, Gray) { + Model *modelGrayModule = Model::create("Squinky Labs", + "squinkylabs-gry", + "Gray Code: Eclectic clock divider", CLOCK_MODULATOR_TAG, RANDOM_TAG); + return modelGrayModule; +} diff --git a/plugins/community/repos/squinkylabs-plug1/src/LFNModule.cpp b/plugins/community/repos/squinkylabs-plug1/src/LFNModule.cpp new file mode 100644 index 00000000..481a1d1c --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/src/LFNModule.cpp @@ -0,0 +1,180 @@ + +#include +#include "Squinky.hpp" +#include "WidgetComposite.h" + +#include "LFN.h" + + +/** + */ +struct LFNModule : Module +{ +public: + LFNModule(); + /** + * + * Overrides of Module functions + */ + void step() override; + void onSampleRateChange() override; + + LFN lfn; +private: + +}; + +void LFNModule::onSampleRateChange() +{ + // engineGetSampleTime(); + // float rate = Module::engineGetSampleRate(); + // float rate = 1.0f / engineGetSampleTime(); // TODO: what's up with this? this used to work! + lfn.setSampleTime(engineGetSampleTime()); +} + +LFNModule::LFNModule() + : Module(lfn.NUM_PARAMS, + lfn.NUM_INPUTS, + lfn.NUM_OUTPUTS, + lfn.NUM_LIGHTS), + lfn(this) +{ + onSampleRateChange(); + lfn.init(); +} + +void LFNModule::step() +{ + lfn.step(); +} + +//////////////////// +// module widget +//////////////////// + +class LFNLabelUpdater +{ +public: + void update(struct LFNWidget& widget); + void makeLabel(struct LFNWidget& widget, int index, float x, float y); +private: + LFNModule * module; + Label* labels[5] = {0,0,0,0,0}; + float baseFrequency = -1; +}; + + + +struct LFNWidget : ModuleWidget +{ + LFNWidget(LFNModule *); + + Label* addLabel(const Vec& v, const char* str, const NVGcolor& color = COLOR_BLACK) + { + Label* label = new Label(); + label->box.pos = v; + label->text = str; + label->color = color; + addChild(label); + return label; + } + + void draw(NVGcontext *vg) override + { + updater.update(*this); + module.lfn.pollForChangeOnUIThread(); + ModuleWidget::draw(vg); + } + + void addStage(int i); + + LFNLabelUpdater updater; + LFNModule& module; +}; + +static const float knobX = 42; +static const float knobY = 100; +static const float knobDy = 50; +static const float inputY = knobY + 16; +static const float inputX = 6; +static const float labelX = 2; + +void LFNWidget::addStage(int index) +{ + const float gmin = -5; + const float gmax = 5; + const float gdef = 0; + addParam(ParamWidget::create( + Vec(knobX, knobY + index * knobDy), + &module, module.lfn.EQ0_PARAM + index, gmin, gmax, gdef)); + + updater.makeLabel((*this), index, labelX, knobY - 2 + index * knobDy); + + addInput(Port::create(Vec(inputX, inputY + index * knobDy), + Port::INPUT, &module, module.lfn.EQ0_INPUT + index)); +} +/** + * Widget constructor will describe my implementation structure and + * provide meta-data. + * This is not shared by all modules in the DLL, just one + */ +LFNWidget::LFNWidget(LFNModule *module) : ModuleWidget(module), module(*module) +{ + box.size = Vec(6 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT); + { + SVGPanel *panel = new SVGPanel(); + panel->box.size = box.size; + panel->setBackground(SVG::load(assetPlugin(plugin, "res/lfn_panel.svg"))); + addChild(panel); + } + + addOutput(Port::create( + Vec(59, inputY - knobDy -1), Port::OUTPUT, module, module->lfn.OUTPUT)); + addLabel( + Vec(54 , inputY - knobDy - 18), "out", COLOR_WHITE); + + addParam(ParamWidget::create( + Vec(10, knobY - 1 * knobDy), module, module->lfn.FREQ_RANGE_PARAM, -5, 5, 0)); + + // addLabel(Vec(59, knobY - 1 * knobDy), "R"); + + for (int i = 0; i < 5; ++i) { + addStage(i); + } + + // screws + addChild(Widget::create(Vec(RACK_GRID_WIDTH, 0))); + addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); + addChild(Widget::create(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); +} + +void LFNLabelUpdater::makeLabel(struct LFNWidget& widget, int index, float x, float y) +{ + labels[index] = widget.addLabel(Vec(x, y), "Hz"); +} + +void LFNLabelUpdater::update(struct LFNWidget& widget) +{ + float baseFreq = widget.module.lfn.getBaseFrequency(); + if (baseFreq != baseFrequency) { + baseFrequency = baseFreq; + for (int i = 0; i < 5; ++i) { + std::stringstream str; + str.precision(1); + str.setf(std::ios::fixed, std::ios::floatfield); + str << baseFreq; + labels[i]->text = str.str(); + baseFreq *= 2.0f; + } + } +} + +RACK_PLUGIN_MODEL_INIT(squinkylabs_plug1, LFN) { + Model *modelLFNModule = Model::create("Squinky Labs", + "squinkylabs-lfn", + "LFN: Random Voltages", NOISE_TAG, RANDOM_TAG, LFO_TAG); + return modelLFNModule; +} + diff --git a/plugins/community/repos/squinkylabs-plug1/src/SQWidgets.h b/plugins/community/repos/squinkylabs-plug1/src/SQWidgets.h new file mode 100644 index 00000000..342826d4 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/src/SQWidgets.h @@ -0,0 +1,94 @@ +#pragma once + +#include "rack.hpp" +#include "WidgetComposite.h" + +#include + + +/** + * Like Trimpot, but with blue stripe + */ +struct BlueTrimmer : SVGKnob { + BlueTrimmer() { + // printf("ctrol of blue trimmer\n"); fflush(stdout); + minAngle = -0.75*M_PI; + maxAngle = 0.75*M_PI; + setSVG(SVG::load(assetPlugin(plugin, "res/BlueTrimmer.svg"))); + } +}; + +/** + * Like Rogan1PSBlue, but smaller. + */ +struct Blue30Knob : SVGKnob { + Blue30Knob() { + minAngle = -0.83*M_PI; + maxAngle = 0.83*M_PI; + setSVG(SVG::load(assetPlugin(plugin, "res/Blue30.svg"))); + } +}; + +struct Blue30SnapKnob : Blue30Knob { + Blue30SnapKnob() { + snap = true; + smooth = false; + } +}; + +struct NKKSmall : SVGSwitch, ToggleSwitch { + NKKSmall() { + addFrame(SVG::load(assetPlugin(plugin, "res/NKKSmall_0.svg"))); + addFrame(SVG::load(assetPlugin(plugin, "res/NKKSmall_1.svg"))); + addFrame(SVG::load(assetPlugin(plugin, "res/NKKSmall_2.svg"))); + } +}; + +struct BlueToggle : public SVGSwitch, ToggleSwitch { + BlueToggle() { + addFrame(SVG::load(assetPlugin(plugin, "res/BluePush_1.svg"))); + addFrame(SVG::load(assetPlugin(plugin, "res/BluePush_0.svg"))); + #if 0 + setSVGs( + SVG::load(assetPlugin(plugin, "res/BluePush_0.svg")), + SVG::load(assetPlugin(plugin, "res/BluePush_1.svg")) + ); + #endif + } +}; + +/** + * A very basic momentary push button. + */ +struct SQPush : SVGButton +{ + SQPush() + { + setSVGs( + SVG::load(assetPlugin(plugin, "res/BluePush_0.svg")), + SVG::load(assetPlugin(plugin, "res/BluePush_1.svg")) + ); + } + void center(Vec& pos) + { + this->box.pos = pos.minus(this->box.size.div(2)); + } + + void onDragEnd(EventDragEnd &e) override + { + SVGButton::onDragEnd(e); + if (clickHandler) { + clickHandler(); + } + } + + /** + * User of button passes in a callback lamba here + */ + void onClick(std::function callback) + { + clickHandler = callback; + } + + std::function clickHandler; +}; diff --git a/plugins/community/repos/squinkylabs-plug1/src/ShaperModule.cpp b/plugins/community/repos/squinkylabs-plug1/src/ShaperModule.cpp new file mode 100644 index 00000000..ddd07c9a --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/src/ShaperModule.cpp @@ -0,0 +1,232 @@ + +#include "Squinky.hpp" +#include "WidgetComposite.h" + + +#include "Shaper.h" + +/** + */ +struct ShaperModule : Module +{ +public: + ShaperModule(); + /** + * + * + * Overrides of Module functions + */ + void step() override; + + Shaper shaper; +private: +}; + +ShaperModule::ShaperModule() + : Module(shaper.NUM_PARAMS, + shaper.NUM_INPUTS, + shaper.NUM_OUTPUTS, + shaper.NUM_LIGHTS), + shaper(this) +{ +} + +void ShaperModule::step() +{ + shaper.step(); +} + +//////////////////// +// module widget +//////////////////// + +struct ShaperWidget : ModuleWidget +{ + ShaperWidget(ShaperModule *); + + /** + * Helper to add a text label to this widget + */ + Label* addLabel(const Vec& v, const char* str, const NVGcolor& color = COLOR_BLACK) + { + Label* label = new Label(); + label->box.pos = v; + label->text = str; + label->color = color; + label->fontSize = 16; + addChild(label); + return label; + } + + void step() override; +private: + Label* shapeLabel=nullptr; + Label* shapeLabel2=nullptr; + Label* oversampleLabel=nullptr; + ParamWidget* shapeParam = nullptr; + ParamWidget* oversampleParam = nullptr; + Shaper::Shapes curShape = Shaper::Shapes::Invalid; + // ShaperModule* const module; + void addSelector(ShaperModule* module); + int curOversample =-1; +}; + +void ShaperWidget::step() +{ + ModuleWidget::step(); + const int iShape = (int) std::round(shapeParam->value); + const Shaper::Shapes shape = Shaper::Shapes(iShape); + if (shape != curShape) { + curShape = shape; + std::string shapeString = Shaper::getString(shape); + if (shapeString.length() > 8) { + auto pos = shapeString.find(' '); + if (pos != std::string::npos) { + shapeLabel->text = shapeString.substr(0, pos); + shapeLabel2->text = shapeString.substr(pos+1); + } else { + shapeLabel->text = "too"; + shapeLabel2->text = "big"; + } + } else { + shapeLabel->text = shapeString; + shapeLabel2->text = ""; + } + } + const int overS = (int) std::round(oversampleParam->value); + if (overS != curOversample) { + curOversample = overS; + const char * str = ""; + switch (curOversample) { + case 0: + str = "16X"; + break; + case 1: + str = "4X"; + break; + case 2: + str = "1X"; + break; + } + oversampleLabel->text = str; + + } + +} + +void ShaperWidget::addSelector(ShaperModule* module) +{ + const float x = 37; + const float y = 80; + auto p = createParamCentered( + Vec(x, y), + module, Shaper::PARAM_SHAPE, + 0, + float(Shaper::Shapes::Invalid)-1, + 0); + p->snap = true; + p->smooth = false; + addParam(p); + shapeLabel = addLabel(Vec(70, 60), ""); + shapeLabel2 = addLabel(Vec(70, 60+18), ""); + shapeParam = p; + shapeLabel->fontSize = 18; +} + +/** + * Global coordinate constants + */ +/** + * Widget constructor will describe my implementation structure and + * provide meta-data. + * This is not shared by all modules in the DLL, just one + */ +ShaperWidget::ShaperWidget(ShaperModule *module) : + ModuleWidget(module) +{ + box.size = Vec(10 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT); + { + SVGPanel *panel = new SVGPanel(); + panel->box.size = box.size; + panel->setBackground(SVG::load(assetPlugin(plugin, "res/shaper.svg"))); + addChild(panel); + } + + addSelector(module); + + + const float gainX = 35; + const float offsetX = 108; + const float gainY = 232; + const float offsetY = 147; + + addParam(createParamCentered( + Vec(gainX, gainY), + module, Shaper::PARAM_GAIN, -5, 5, 0)); + addLabel(Vec(8, 191), "Gain"); + + addParam(createParamCentered( + Vec(offsetX, offsetY), + module, Shaper::PARAM_OFFSET, -5, 5, 0)); + addLabel(Vec(34, 135), "Offset"); + + addParam(createParamCentered( + Vec(56, 275), + module, Shaper::PARAM_GAIN_TRIM, -1, 1, 0)); + addParam(createParamCentered( + Vec(81, 199), + module, Shaper::PARAM_OFFSET_TRIM, -1, 1, 0)); + + const float jackY = 327; + const float jackLabelY = jackY - 29; + addInput(createInputCentered( + Vec(30,jackY), + module, + Shaper::INPUT_AUDIO)); + addLabel(Vec(17, jackLabelY), "In")->fontSize = 12; + + addOutput(createOutputCentered( + Vec(127,jackY), + module, + Shaper::OUTPUT_AUDIO)); + addLabel(Vec(109, jackLabelY), "Out", COLOR_WHITE)->fontSize = 12; + + + addInput(createInputCentered( + Vec(62, jackY), + module, + Shaper::INPUT_GAIN)); + addInput(createInputCentered( + Vec(95,jackY), + module, + Shaper::INPUT_OFFSET)); + + const float swX = 127; + const float swY = 235; + oversampleParam = createParamCentered( + Vec(swX, swY+4), + module, + Shaper::PARAM_OVERSAMPLE, + 0.0f, 2.0f, 0.0f + ); + + addParam(oversampleParam); + oversampleLabel = addLabel(Vec(swX-32, swY+30), "x"); + oversampleLabel->box.size.x = 60; + oversampleLabel->alignment = Label::Alignment::CENTER_ALIGNMENT; + + // screws + addChild(Widget::create(Vec(RACK_GRID_WIDTH, 0))); + addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); + addChild(Widget::create(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); +} + +RACK_PLUGIN_MODEL_INIT(squinkylabs_plug1, Shaper) { + Model *modelShaperModule = Model::create("Squinky Labs", + "squinkylabs-shp", + "Shaper: Precision Wave Shaper", WAVESHAPER_TAG, DISTORTION_TAG); + return modelShaperModule; +} + diff --git a/plugins/community/repos/squinkylabs-plug1/src/Squinky.cpp b/plugins/community/repos/squinkylabs-plug1/src/Squinky.cpp index 0ab926b4..e1ddfe9d 100644 --- a/plugins/community/repos/squinkylabs-plug1/src/Squinky.cpp +++ b/plugins/community/repos/squinkylabs-plug1/src/Squinky.cpp @@ -1,18 +1,59 @@ // plugin main #include "Squinky.hpp" +RACK_PLUGIN_MODEL_DECLARE(squinkylabs_plug1, Blank); RACK_PLUGIN_MODEL_DECLARE(squinkylabs_plug1, Booty); +RACK_PLUGIN_MODEL_DECLARE(squinkylabs_plug1, CHB); +#ifdef _DG +RACK_PLUGIN_MODEL_DECLARE(squinkylabs_plug1, DG); +#endif +#ifdef _EV3 +RACK_PLUGIN_MODEL_DECLARE(squinkylabs_plug1, EV3); +#endif +#ifdef _EV +RACK_PLUGIN_MODEL_DECLARE(squinkylabs_plug1, EV); +#endif +RACK_PLUGIN_MODEL_DECLARE(squinkylabs_plug1, FunV); +#ifdef _GMR +RACK_PLUGIN_MODEL_DECLARE(squinkylabs_plug1, GMR); +#endif +RACK_PLUGIN_MODEL_DECLARE(squinkylabs_plug1, Gray); +RACK_PLUGIN_MODEL_DECLARE(squinkylabs_plug1, LFN); +RACK_PLUGIN_MODEL_DECLARE(squinkylabs_plug1, Shaper); +RACK_PLUGIN_MODEL_DECLARE(squinkylabs_plug1, Super); RACK_PLUGIN_MODEL_DECLARE(squinkylabs_plug1, Vocal); RACK_PLUGIN_MODEL_DECLARE(squinkylabs_plug1, VocalFilter); RACK_PLUGIN_MODEL_DECLARE(squinkylabs_plug1, ColoredNoise); RACK_PLUGIN_MODEL_DECLARE(squinkylabs_plug1, Tremolo); +#ifdef _CPU_HOG RACK_PLUGIN_MODEL_DECLARE(squinkylabs_plug1, CPU_Hog); +#endif RACK_PLUGIN_MODEL_DECLARE(squinkylabs_plug1, ThreadBoost); RACK_PLUGIN_INIT(squinkylabs_plug1) { RACK_PLUGIN_INIT_ID(); + RACK_PLUGIN_INIT_VERSION("0.6.9"); + // RACK_PLUGIN_MODEL_ADD(squinkylabs_plug1, Blank); // crashes RACK_PLUGIN_MODEL_ADD(squinkylabs_plug1, Booty); + RACK_PLUGIN_MODEL_ADD(squinkylabs_plug1, CHB); +#ifdef _DG + RACK_PLUGIN_MODEL_ADD(squinkylabs_plug1, DG); +#endif +#ifdef _EV3 + RACK_PLUGIN_MODEL_ADD(squinkylabs_plug1, EV3); +#endif +#ifdef _EV + RACK_PLUGIN_MODEL_ADD(squinkylabs_plug1, EV); +#endif + // RACK_PLUGIN_MODEL_ADD(squinkylabs_plug1, FunV); // crashes (read from 0xfffffff) +#ifdef _GMR + RACK_PLUGIN_MODEL_ADD(squinkylabs_plug1, GMR); +#endif + RACK_PLUGIN_MODEL_ADD(squinkylabs_plug1, Gray); + RACK_PLUGIN_MODEL_ADD(squinkylabs_plug1, LFN); + // RACK_PLUGIN_MODEL_ADD(squinkylabs_plug1, Shaper); // crashes (read from 0x00000010) + RACK_PLUGIN_MODEL_ADD(squinkylabs_plug1, Super); RACK_PLUGIN_MODEL_ADD(squinkylabs_plug1, Vocal); RACK_PLUGIN_MODEL_ADD(squinkylabs_plug1, VocalFilter); RACK_PLUGIN_MODEL_ADD(squinkylabs_plug1, ColoredNoise); diff --git a/plugins/community/repos/squinkylabs-plug1/src/Squinky.hpp b/plugins/community/repos/squinkylabs-plug1/src/Squinky.hpp index c9122bf8..0876a9b7 100644 --- a/plugins/community/repos/squinkylabs-plug1/src/Squinky.hpp +++ b/plugins/community/repos/squinkylabs-plug1/src/Squinky.hpp @@ -1,5 +1,10 @@ #include "rack.hpp" +//#define _GMR +#define _CHB +#define _EV3 +//#define _SUPER + using namespace rack; RACK_PLUGIN_DECLARE(squinkylabs_plug1); diff --git a/plugins/community/repos/squinkylabs-plug1/src/SuperModule.cpp b/plugins/community/repos/squinkylabs-plug1/src/SuperModule.cpp new file mode 100644 index 00000000..12f9f630 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/src/SuperModule.cpp @@ -0,0 +1,149 @@ + +#include +#include "Squinky.hpp" +#include "WidgetComposite.h" +#include "SQWidgets.h" + +#include "Super.h" + + +/** + */ +struct SuperModule : Module +{ +public: + SuperModule(); + /** + * + * Overrides of Module functions + */ + void step() override; + void onSampleRateChange() override; + + Super super; +private: + +}; + +void SuperModule::onSampleRateChange() +{ +} + +SuperModule::SuperModule() + : Module(super.NUM_PARAMS, + super.NUM_INPUTS, + super.NUM_OUTPUTS, + super.NUM_LIGHTS), + super(this) +{ + onSampleRateChange(); + super.init(); +} + +void SuperModule::step() +{ + super.step(); +} + +//////////////////// +// module widget +//////////////////// + +struct superWidget : ModuleWidget +{ + superWidget(SuperModule *); + + Label* addLabel(const Vec& v, const char* str, const NVGcolor& color = COLOR_BLACK) + { + Label* label = new Label(); + label->box.pos = v; + label->text = str; + label->color = color; + addChild(label); + return label; + } + void addDebug(SuperModule*); +}; + +void superWidget::addDebug(SuperModule*) +{ + addInput(Port::create( + Vec(60, 10), Port::INPUT, module, Super::DEBUG_INPUT)); + addOutput(Port::create( + Vec(60, 40), Port::OUTPUT, module, Super::DEBUG_OUTPUT)); +} + + +/** + * Widget constructor will describe my implementation structure and + * provide meta-data. + * This is not shared by all modules in the DLL, just one + */ +superWidget::superWidget(SuperModule *module) : ModuleWidget(module) +{ + box.size = Vec(6 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT); + { + SVGPanel *panel = new SVGPanel(); + panel->box.size = box.size; + panel->setBackground(SVG::load(assetPlugin(plugin, "res/blank_panel.svg"))); + addChild(panel); + } + + addOutput(Port::create( + Vec(60, 330), Port::OUTPUT, module, Super::MAIN_OUTPUT)); + addLabel( + Vec(60 , 310), "out"); + + addInput(Port::create( + Vec(6, 330), Port::INPUT, module, Super::CV_INPUT)); + addLabel( + Vec(2 , 310), "V/8"); + + addInput(Port::create( + Vec(34, 330), Port::INPUT, module, Super::GATE_INPUT)); + addLabel( + Vec(34 , 310), "Trig"); + + addParam(ParamWidget::create( + Vec(10, 30), module, Super::OCTAVE_PARAM, -5, 5, 0)); + addLabel( + Vec(10, 10), "Oct"); + + addDebug(module); + + addParam(ParamWidget::create( + Vec(10, 95), module, Super::SEMI_PARAM, -5, 5, 0)); + addLabel( + Vec(10, 75), "Semi"); + + addParam(ParamWidget::create( + Vec(10, 160), module, Super::FINE_PARAM, -5, 5, 0)); + addLabel( + Vec(10, 140), "Fine"); + + addParam(ParamWidget::create( + Vec(10, 220), module, Super::DETUNE_PARAM, -5, 5, 0)); + addLabel( + Vec(10, 200), "Detune"); + + addParam(ParamWidget::create( + Vec(10, 270), module, Super::MIX_PARAM, -5, 5, 0)); + addLabel( + Vec(10, 250), "Mix"); + + // screws + addChild(Widget::create(Vec(RACK_GRID_WIDTH, 0))); + addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); + addChild(Widget::create(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); +} + + +RACK_PLUGIN_MODEL_INIT(squinkylabs_plug1, Super) { + Model *modelSuperModule = Model::create("Squinky Labs", + "squinkylabs-super", + "-- super --", RANDOM_TAG); + return modelSuperModule; +} + diff --git a/plugins/community/repos/squinkylabs-plug1/src/VocalFilterModule.cpp b/plugins/community/repos/squinkylabs-plug1/src/VocalFilterModule.cpp index 526b821a..e0bee5c4 100644 --- a/plugins/community/repos/squinkylabs-plug1/src/VocalFilterModule.cpp +++ b/plugins/community/repos/squinkylabs-plug1/src/VocalFilterModule.cpp @@ -260,6 +260,6 @@ VocalFilterWidget::VocalFilterWidget(VocalFilterModule *module) : ModuleWidget(m RACK_PLUGIN_MODEL_INIT(squinkylabs_plug1, VocalFilter) { Model *modelVocalFilterModule = Model::create("Squinky Labs", "squinkylabs-vocalfilter", - "Vocal Filter", EFFECT_TAG, FILTER_TAG); + "Formants: Vocal Filter", EFFECT_TAG, FILTER_TAG); return modelVocalFilterModule; } diff --git a/plugins/community/repos/squinkylabs-plug1/src/VocalModule.cpp b/plugins/community/repos/squinkylabs-plug1/src/VocalModule.cpp index ce5576ea..03e31741 100644 --- a/plugins/community/repos/squinkylabs-plug1/src/VocalModule.cpp +++ b/plugins/community/repos/squinkylabs-plug1/src/VocalModule.cpp @@ -195,6 +195,6 @@ VocalWidget::VocalWidget(VocalModule *module) : ModuleWidget(module) RACK_PLUGIN_MODEL_INIT(squinkylabs_plug1, Vocal) { Model *modelVocalModule = Model::create("Squinky Labs", "squinkylabs-vocalanimator", - "Vocal Animator", EFFECT_TAG, FILTER_TAG, LFO_TAG, RANDOM_TAG); + "Growler: Vocal Animator", EFFECT_TAG, FILTER_TAG, LFO_TAG, RANDOM_TAG); return modelVocalModule; } diff --git a/plugins/community/repos/squinkylabs-plug1/src/WaveformSelector.h b/plugins/community/repos/squinkylabs-plug1/src/WaveformSelector.h new file mode 100644 index 00000000..788a4d46 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/src/WaveformSelector.h @@ -0,0 +1,151 @@ +#pragma once + +#include "widgets.hpp" + +class ButtonCell +{ +public: + friend class WaveformSelector; + ButtonCell(const ButtonCell&) = delete; + ButtonCell& operator = (const ButtonCell&) = delete; + + ButtonCell(float x) : value(x) {} + void loadSVG(const char* res, const char* resOn); + + const float value; + rack::Rect box; + + void dump(const char*); +private: + SVGWidget svg; + SVGWidget svgOn; +}; + +inline void ButtonCell::loadSVG(const char* res, const char* resOn) +{ + svg.setSVG(SVG::load (assetPlugin(plugin, res))); + svgOn.setSVG(SVG::load (assetPlugin(plugin, resOn))); + this->box.size = svg.box.size; +} + +inline void ButtonCell::dump(const char* label) +{ + printf("cell(%.2f) {%s} box size=%f, %f po %f, %f\n", + value, + label, + box.size.x, + box.size.y, + box.pos.x, + box.pos.y); +} + +using CellPtr = std::shared_ptr; + +struct WaveformSelector : ParamWidget +{ + WaveformSelector(); + void draw(NVGcontext *vg) override; + ~WaveformSelector() override; + + std::vector< std::vector< CellPtr>> svgs; + void addSvg(int row, const char* res, const char* resOn); + void drawSVG(NVGcontext *vg, SVGWidget&, float x, float y); + void onMouseDown( EventMouseDown &e ) override; + CellPtr hitTest(float x, float y); + // + float nextValue = 0; +}; + + CellPtr WaveformSelector::hitTest(float x, float y) + { + const Vec pos(x, y); + for (auto& r : svgs) { + for (auto& s : r) { + if (s->box.contains(pos)) { + return s; + } + } + } + return nullptr; + } + +inline void WaveformSelector::addSvg(int row, const char* res, const char* resOn) +{ + if ((int)svgs.size() < row+1) { + svgs.resize(row+1); + } + + // make a new cell, put the SVGs in it + CellPtr cell = std::make_shared(nextValue++); + cell->loadSVG(res, resOn); + svgs[row].push_back(cell); + + // now set the box for cell + float y = 0; + if (row > 0) { + // if we are going in the second row, y = height of first + assert(!svgs[row-1].empty()); + CellPtr otherCell = svgs[row-1][0]; + y = otherCell->box.pos.y + otherCell->box.size.y; + } + cell->box.pos.y = y; + + const int cellsInRow = (int) svgs[row].size(); + if (cellsInRow == 1) { + cell->box.pos.x = 0; + } else { + cell->box.pos.x = + svgs[row][cellsInRow-2]->box.pos.x + + svgs[row][cellsInRow-2]->box.size.x; + } +} + +inline WaveformSelector::WaveformSelector() +{ + addSvg(0, "res/waveforms-6-08.svg","res/waveforms-6-07.svg"); + addSvg(0, "res/waveforms-6-06.svg","res/waveforms-6-05.svg"); + addSvg(0, "res/waveforms-6-02.svg","res/waveforms-6-01.svg"); + addSvg(1, "res/waveforms-6-04.svg","res/waveforms-6-03.svg"); + addSvg(1, "res/waveforms-6-12.svg","res/waveforms-6-11.svg"); + addSvg(1, "res/waveforms-6-10.svg","res/waveforms-6-09.svg"); +} + +inline WaveformSelector::~WaveformSelector() +{ +} + +inline void WaveformSelector::drawSVG(NVGcontext *vg, SVGWidget& svg, float x, float y) +{ + nvgSave(vg); + float transform[6]; + nvgTransformIdentity(transform); + nvgTransformTranslate(transform, x, y); + nvgTransform(vg, transform[0], transform[1], transform[2], transform[3], transform[4], transform[5]); + svg.draw(vg); + nvgRestore(vg); +} + +void inline WaveformSelector::draw(NVGcontext *vg) +{ + for (auto& r : svgs) { + for (auto& s : r) { + const bool on = (this->value == s->value); + drawSVG(vg, on ? s->svgOn : s->svg, s->box.pos.x, s->box.pos.y); + } + } +} + +inline void WaveformSelector::onMouseDown( EventMouseDown &e ) +{ + e.consumed = false; + + CellPtr hit = hitTest(e.pos.x, e.pos.y); + if (hit) { + e.consumed = true; + if (hit->value == this->value) { + printf("value same\n"); fflush(stdout); + return; + } + setValue(hit->value); + } +} \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/table.txt b/plugins/community/repos/squinkylabs-plug1/table.txt new file mode 100644 index 00000000..7b018fbe --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/table.txt @@ -0,0 +1,544 @@ +float symmetry_table_0[256] = { +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_1[256] = { +-0.066667f, -0.066667f, -0.066667f, -0.066667f, -0.066667f, -0.066667f, -0.066666f, -0.066666f, +-0.066666f, -0.066666f, -0.066665f, -0.066665f, -0.066664f, -0.066663f, -0.066663f, -0.066662f, +-0.066661f, -0.066659f, -0.066658f, -0.066656f, -0.066654f, -0.066652f, -0.066650f, -0.066647f, +-0.066644f, -0.066641f, -0.066638f, -0.066634f, -0.066630f, -0.066625f, -0.066621f, -0.066615f, +-0.066610f, -0.066603f, -0.066597f, -0.066590f, -0.066582f, -0.066574f, -0.066565f, -0.066556f, +-0.066546f, -0.066535f, -0.066523f, -0.066511f, -0.066498f, -0.066484f, -0.066470f, -0.066454f, +-0.066438f, -0.066420f, -0.066402f, -0.066382f, -0.066361f, -0.066339f, -0.066316f, -0.066292f, +-0.066266f, -0.066238f, -0.066210f, -0.066179f, -0.066147f, -0.066113f, -0.066078f, -0.066040f, +-0.066001f, -0.065959f, -0.065916f, -0.065870f, -0.065821f, -0.065770f, -0.065717f, -0.065660f, +-0.065601f, -0.065538f, -0.065473f, -0.065404f, -0.065331f, -0.065255f, -0.065174f, -0.065089f, +-0.065000f, -0.064906f, -0.064808f, -0.064703f, -0.064594f, -0.064478f, -0.064356f, -0.064228f, +-0.064092f, -0.063949f, -0.063798f, -0.063639f, -0.063470f, -0.063292f, -0.063103f, -0.062903f, +-0.062691f, -0.062465f, -0.062226f, -0.061972f, -0.061702f, -0.061413f, -0.061106f, -0.060777f, +-0.060425f, -0.060048f, -0.059643f, -0.059207f, -0.058737f, -0.058228f, -0.057677f, -0.057076f, +-0.056421f, -0.055703f, -0.054911f, -0.054035f, -0.053059f, -0.051965f, -0.050726f, -0.049311f, +-0.047673f, -0.045747f, -0.043439f, -0.040598f, -0.036977f, -0.032128f, -0.025192f, -0.014656f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_2[256] = { +-0.133333f, -0.133333f, -0.133333f, -0.133333f, -0.133333f, -0.133333f, -0.133333f, -0.133332f, +-0.133332f, -0.133331f, -0.133330f, -0.133329f, -0.133328f, -0.133326f, -0.133324f, -0.133322f, +-0.133320f, -0.133317f, -0.133314f, -0.133310f, -0.133306f, -0.133301f, -0.133296f, -0.133290f, +-0.133284f, -0.133276f, -0.133269f, -0.133260f, -0.133251f, -0.133241f, -0.133230f, -0.133218f, +-0.133205f, -0.133191f, -0.133176f, -0.133160f, -0.133143f, -0.133124f, -0.133104f, -0.133083f, +-0.133060f, -0.133036f, -0.133010f, -0.132982f, -0.132953f, -0.132921f, -0.132888f, -0.132852f, +-0.132815f, -0.132775f, -0.132732f, -0.132688f, -0.132640f, -0.132590f, -0.132537f, -0.132480f, +-0.132421f, -0.132358f, -0.132292f, -0.132222f, -0.132148f, -0.132070f, -0.131988f, -0.131902f, +-0.131810f, -0.131714f, -0.131613f, -0.131506f, -0.131393f, -0.131274f, -0.131149f, -0.131018f, +-0.130879f, -0.130733f, -0.130579f, -0.130417f, -0.130246f, -0.130066f, -0.129876f, -0.129676f, +-0.129465f, -0.129243f, -0.129008f, -0.128761f, -0.128500f, -0.128224f, -0.127932f, -0.127624f, +-0.127299f, -0.126954f, -0.126590f, -0.126203f, -0.125794f, -0.125360f, -0.124899f, -0.124410f, +-0.123889f, -0.123335f, -0.122745f, -0.122115f, -0.121442f, -0.120723f, -0.119952f, -0.119125f, +-0.118235f, -0.117277f, -0.116243f, -0.115125f, -0.113911f, -0.112591f, -0.111150f, -0.109572f, +-0.107837f, -0.105920f, -0.103793f, -0.101419f, -0.098751f, -0.095733f, -0.092290f, -0.088326f, +-0.083713f, -0.078285f, -0.071824f, -0.064055f, -0.054668f, -0.043400f, -0.030217f, -0.015502f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_3[256] = { +-0.200000f, -0.200000f, -0.200000f, -0.200000f, -0.200000f, -0.199999f, -0.199999f, -0.199998f, +-0.199997f, -0.199996f, -0.199995f, -0.199993f, -0.199991f, -0.199988f, -0.199985f, -0.199981f, +-0.199977f, -0.199972f, -0.199967f, -0.199960f, -0.199953f, -0.199945f, -0.199937f, -0.199927f, +-0.199916f, -0.199904f, -0.199891f, -0.199877f, -0.199861f, -0.199844f, -0.199825f, -0.199805f, +-0.199783f, -0.199759f, -0.199734f, -0.199706f, -0.199677f, -0.199645f, -0.199611f, -0.199575f, +-0.199536f, -0.199494f, -0.199449f, -0.199402f, -0.199351f, -0.199297f, -0.199240f, -0.199179f, +-0.199114f, -0.199046f, -0.198973f, -0.198895f, -0.198813f, -0.198726f, -0.198634f, -0.198537f, +-0.198434f, -0.198325f, -0.198210f, -0.198089f, -0.197960f, -0.197825f, -0.197681f, -0.197530f, +-0.197370f, -0.197202f, -0.197024f, -0.196837f, -0.196639f, -0.196430f, -0.196210f, -0.195978f, +-0.195733f, -0.195474f, -0.195201f, -0.194914f, -0.194610f, -0.194289f, -0.193951f, -0.193594f, +-0.193216f, -0.192818f, -0.192396f, -0.191951f, -0.191479f, -0.190981f, -0.190453f, -0.189894f, +-0.189302f, -0.188674f, -0.188007f, -0.187300f, -0.186548f, -0.185748f, -0.184897f, -0.183990f, +-0.183023f, -0.181990f, -0.180886f, -0.179704f, -0.178437f, -0.177078f, -0.175616f, -0.174041f, +-0.172341f, -0.170504f, -0.168512f, -0.166349f, -0.163992f, -0.161418f, -0.158598f, -0.155499f, +-0.152081f, -0.148298f, -0.144093f, -0.139402f, -0.134148f, -0.128240f, -0.121576f, -0.114043f, +-0.105522f, -0.095903f, -0.085107f, -0.073109f, -0.059969f, -0.045841f, -0.030958f, -0.015591f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_4[256] = { +-0.266667f, -0.266667f, -0.266667f, -0.266666f, -0.266666f, -0.266666f, -0.266665f, -0.266664f, +-0.266663f, -0.266661f, -0.266659f, -0.266656f, -0.266653f, -0.266649f, -0.266644f, -0.266639f, +-0.266632f, -0.266625f, -0.266617f, -0.266607f, -0.266596f, -0.266584f, -0.266571f, -0.266556f, +-0.266540f, -0.266522f, -0.266502f, -0.266480f, -0.266457f, -0.266431f, -0.266402f, -0.266372f, +-0.266338f, -0.266303f, -0.266264f, -0.266222f, -0.266177f, -0.266128f, -0.266077f, -0.266021f, +-0.265961f, -0.265897f, -0.265829f, -0.265756f, -0.265679f, -0.265596f, -0.265508f, -0.265414f, +-0.265315f, -0.265209f, -0.265096f, -0.264977f, -0.264850f, -0.264716f, -0.264573f, -0.264423f, +-0.264263f, -0.264094f, -0.263915f, -0.263726f, -0.263526f, -0.263314f, -0.263090f, -0.262854f, +-0.262604f, -0.262340f, -0.262061f, -0.261766f, -0.261455f, -0.261126f, -0.260778f, -0.260411f, +-0.260023f, -0.259613f, -0.259180f, -0.258722f, -0.258238f, -0.257726f, -0.257185f, -0.256613f, +-0.256007f, -0.255366f, -0.254687f, -0.253968f, -0.253206f, -0.252398f, -0.251540f, -0.250630f, +-0.249664f, -0.248637f, -0.247544f, -0.246381f, -0.245143f, -0.243822f, -0.242413f, -0.240908f, +-0.239298f, -0.237575f, -0.235729f, -0.233747f, -0.231617f, -0.229325f, -0.226855f, -0.224189f, +-0.221306f, -0.218183f, -0.214794f, -0.211110f, -0.207097f, -0.202719f, -0.197933f, -0.192692f, +-0.186945f, -0.180635f, -0.173704f, -0.166089f, -0.157731f, -0.148574f, -0.138577f, -0.127713f, +-0.115983f, -0.103419f, -0.090082f, -0.076066f, -0.061485f, -0.046465f, -0.031134f, -0.015611f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_5[256] = { +-0.333333f, -0.333333f, -0.333333f, -0.333333f, -0.333333f, -0.333332f, -0.333331f, -0.333330f, +-0.333328f, -0.333325f, -0.333322f, -0.333318f, -0.333314f, -0.333308f, -0.333301f, -0.333294f, +-0.333284f, -0.333274f, -0.333262f, -0.333249f, -0.333234f, -0.333217f, -0.333198f, -0.333177f, +-0.333153f, -0.333128f, -0.333099f, -0.333068f, -0.333034f, -0.332997f, -0.332957f, -0.332913f, +-0.332865f, -0.332814f, -0.332758f, -0.332698f, -0.332633f, -0.332564f, -0.332489f, -0.332409f, +-0.332323f, -0.332231f, -0.332132f, -0.332027f, -0.331915f, -0.331795f, -0.331667f, -0.331531f, +-0.331386f, -0.331232f, -0.331068f, -0.330894f, -0.330710f, -0.330513f, -0.330305f, -0.330085f, +-0.329851f, -0.329603f, -0.329340f, -0.329062f, -0.328767f, -0.328455f, -0.328125f, -0.327775f, +-0.327406f, -0.327014f, -0.326600f, -0.326162f, -0.325699f, -0.325209f, -0.324690f, -0.324141f, +-0.323560f, -0.322946f, -0.322295f, -0.321606f, -0.320877f, -0.320104f, -0.319286f, -0.318419f, +-0.317500f, -0.316525f, -0.315491f, -0.314393f, -0.313228f, -0.311990f, -0.310674f, -0.309274f, +-0.307785f, -0.306199f, -0.304509f, -0.302706f, -0.300783f, -0.298728f, -0.296532f, -0.294183f, +-0.291667f, -0.288969f, -0.286075f, -0.282967f, -0.279625f, -0.276029f, -0.272155f, -0.267977f, +-0.263468f, -0.258598f, -0.253334f, -0.247642f, -0.241485f, -0.234825f, -0.227623f, -0.219844f, +-0.211450f, -0.202413f, -0.192708f, -0.182321f, -0.171248f, -0.159501f, -0.147103f, -0.134096f, +-0.120530f, -0.106468f, -0.091979f, -0.077137f, -0.062014f, -0.046678f, -0.031194f, -0.015618f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_6[256] = { +-0.400000f, -0.400000f, -0.400000f, -0.400000f, -0.399999f, -0.399998f, -0.399997f, -0.399995f, +-0.399992f, -0.399989f, -0.399985f, -0.399979f, -0.399973f, -0.399965f, -0.399956f, -0.399946f, +-0.399933f, -0.399919f, -0.399903f, -0.399885f, -0.399864f, -0.399840f, -0.399814f, -0.399785f, +-0.399753f, -0.399718f, -0.399679f, -0.399636f, -0.399589f, -0.399538f, -0.399482f, -0.399421f, +-0.399355f, -0.399284f, -0.399207f, -0.399123f, -0.399034f, -0.398937f, -0.398833f, -0.398721f, +-0.398601f, -0.398473f, -0.398335f, -0.398188f, -0.398031f, -0.397863f, -0.397684f, -0.397493f, +-0.397289f, -0.397073f, -0.396842f, -0.396597f, -0.396336f, -0.396059f, -0.395765f, -0.395452f, +-0.395120f, -0.394768f, -0.394395f, -0.393999f, -0.393579f, -0.393134f, -0.392662f, -0.392162f, +-0.391632f, -0.391071f, -0.390476f, -0.389846f, -0.389178f, -0.388471f, -0.387721f, -0.386926f, +-0.386084f, -0.385192f, -0.384246f, -0.383243f, -0.382179f, -0.381050f, -0.379852f, -0.378581f, +-0.377232f, -0.375798f, -0.374275f, -0.372655f, -0.370933f, -0.369102f, -0.367152f, -0.365075f, +-0.362862f, -0.360503f, -0.357987f, -0.355302f, -0.352434f, -0.349371f, -0.346095f, -0.342592f, +-0.338842f, -0.334828f, -0.330528f, -0.325920f, -0.320981f, -0.315687f, -0.310013f, -0.303931f, +-0.297415f, -0.290439f, -0.282976f, -0.275004f, -0.266499f, -0.257444f, -0.247826f, -0.237638f, +-0.226877f, -0.215553f, -0.203678f, -0.191275f, -0.178375f, -0.165012f, -0.151228f, -0.137068f, +-0.122577f, -0.107804f, -0.092794f, -0.077591f, -0.062236f, -0.046768f, -0.031219f, -0.015621f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_7[256] = { +-0.466667f, -0.466667f, -0.466667f, -0.466666f, -0.466665f, -0.466664f, -0.466662f, -0.466660f, +-0.466657f, -0.466652f, -0.466646f, -0.466639f, -0.466631f, -0.466621f, -0.466608f, -0.466594f, +-0.466578f, -0.466559f, -0.466537f, -0.466512f, -0.466484f, -0.466453f, -0.466418f, -0.466379f, +-0.466336f, -0.466288f, -0.466235f, -0.466178f, -0.466114f, -0.466045f, -0.465969f, -0.465887f, +-0.465798f, -0.465701f, -0.465596f, -0.465483f, -0.465361f, -0.465229f, -0.465087f, -0.464934f, +-0.464771f, -0.464595f, -0.464407f, -0.464205f, -0.463990f, -0.463759f, -0.463513f, -0.463251f, +-0.462971f, -0.462672f, -0.462354f, -0.462015f, -0.461654f, -0.461270f, -0.460862f, -0.460428f, +-0.459967f, -0.459477f, -0.458956f, -0.458403f, -0.457817f, -0.457194f, -0.456532f, -0.455831f, +-0.455086f, -0.454296f, -0.453458f, -0.452569f, -0.451625f, -0.450624f, -0.449562f, -0.448434f, +-0.447238f, -0.445968f, -0.444620f, -0.443189f, -0.441669f, -0.440054f, -0.438338f, -0.436516f, +-0.434578f, -0.432518f, -0.430327f, -0.427996f, -0.425515f, -0.422875f, -0.420063f, -0.417069f, +-0.413879f, -0.410479f, -0.406856f, -0.402994f, -0.398875f, -0.394484f, -0.389802f, -0.384810f, +-0.379489f, -0.373819f, -0.367779f, -0.361349f, -0.354510f, -0.347243f, -0.339529f, -0.331352f, +-0.322701f, -0.313562f, -0.303931f, -0.293803f, -0.283181f, -0.272071f, -0.260484f, -0.248436f, +-0.235946f, -0.223041f, -0.209746f, -0.196093f, -0.182113f, -0.167839f, -0.153304f, -0.138541f, +-0.123580f, -0.108453f, -0.093188f, -0.077810f, -0.062344f, -0.046811f, -0.031232f, -0.015623f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_8[256] = { +-0.533333f, -0.533333f, -0.533333f, -0.533333f, -0.533332f, -0.533330f, -0.533328f, -0.533325f, +-0.533320f, -0.533314f, -0.533307f, -0.533297f, -0.533286f, -0.533273f, -0.533257f, -0.533238f, +-0.533216f, -0.533191f, -0.533162f, -0.533130f, -0.533093f, -0.533051f, -0.533005f, -0.532953f, +-0.532896f, -0.532832f, -0.532762f, -0.532685f, -0.532601f, -0.532508f, -0.532407f, -0.532297f, +-0.532178f, -0.532048f, -0.531908f, -0.531756f, -0.531591f, -0.531414f, -0.531223f, -0.531018f, +-0.530797f, -0.530560f, -0.530306f, -0.530033f, -0.529741f, -0.529428f, -0.529094f, -0.528737f, +-0.528356f, -0.527949f, -0.527515f, -0.527052f, -0.526559f, -0.526033f, -0.525474f, -0.524878f, +-0.524244f, -0.523570f, -0.522852f, -0.522090f, -0.521279f, -0.520418f, -0.519502f, -0.518529f, +-0.517495f, -0.516397f, -0.515230f, -0.513991f, -0.512674f, -0.511276f, -0.509790f, -0.508211f, +-0.506534f, -0.504753f, -0.502859f, -0.500848f, -0.498710f, -0.496437f, -0.494022f, -0.491454f, +-0.488725f, -0.485822f, -0.482737f, -0.479456f, -0.475967f, -0.472257f, -0.468313f, -0.464120f, +-0.459664f, -0.454928f, -0.449897f, -0.444556f, -0.438887f, -0.432874f, -0.426502f, -0.419754f, +-0.412616f, -0.405075f, -0.397117f, -0.388734f, -0.379917f, -0.370659f, -0.360958f, -0.350815f, +-0.340232f, -0.329217f, -0.317779f, -0.305932f, -0.293690f, -0.281074f, -0.268103f, -0.254800f, +-0.241189f, -0.227294f, -0.213139f, -0.198750f, -0.184152f, -0.169367f, -0.154418f, -0.139327f, +-0.124115f, -0.108799f, -0.093398f, -0.077927f, -0.062402f, -0.046835f, -0.031238f, -0.015624f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_9[256] = { +-0.600000f, -0.600000f, -0.600000f, -0.599999f, -0.599998f, -0.599996f, -0.599993f, -0.599989f, +-0.599983f, -0.599975f, -0.599965f, -0.599953f, -0.599939f, -0.599921f, -0.599900f, -0.599875f, +-0.599847f, -0.599814f, -0.599776f, -0.599734f, -0.599685f, -0.599631f, -0.599570f, -0.599502f, +-0.599426f, -0.599342f, -0.599250f, -0.599148f, -0.599036f, -0.598914f, -0.598780f, -0.598634f, +-0.598475f, -0.598303f, -0.598116f, -0.597913f, -0.597694f, -0.597458f, -0.597203f, -0.596928f, +-0.596632f, -0.596314f, -0.595972f, -0.595606f, -0.595213f, -0.594792f, -0.594341f, -0.593859f, +-0.593343f, -0.592792f, -0.592204f, -0.591576f, -0.590905f, -0.590191f, -0.589428f, -0.588616f, +-0.587750f, -0.586829f, -0.585847f, -0.584802f, -0.583690f, -0.582507f, -0.581248f, -0.579909f, +-0.578484f, -0.576969f, -0.575359f, -0.573646f, -0.571826f, -0.569890f, -0.567833f, -0.565647f, +-0.563323f, -0.560853f, -0.558229f, -0.555441f, -0.552478f, -0.549332f, -0.545990f, -0.542441f, +-0.538673f, -0.534675f, -0.530432f, -0.525932f, -0.521161f, -0.516106f, -0.510752f, -0.505087f, +-0.499096f, -0.492766f, -0.486085f, -0.479041f, -0.471622f, -0.463820f, -0.455626f, -0.447034f, +-0.438039f, -0.428640f, -0.418836f, -0.408630f, -0.398026f, -0.387032f, -0.375656f, -0.363911f, +-0.351811f, -0.339370f, -0.326606f, -0.313536f, -0.300179f, -0.286555f, -0.272683f, -0.258583f, +-0.244274f, -0.229776f, -0.215106f, -0.200282f, -0.185322f, -0.170242f, -0.155055f, -0.139777f, +-0.124420f, -0.108997f, -0.093518f, -0.077994f, -0.062435f, -0.046848f, -0.031242f, -0.015624f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_10[256] = { +-0.666667f, -0.666667f, -0.666666f, -0.666666f, -0.666664f, -0.666661f, -0.666657f, -0.666652f, +-0.666644f, -0.666634f, -0.666622f, -0.666606f, -0.666587f, -0.666564f, -0.666537f, -0.666505f, +-0.666468f, -0.666425f, -0.666376f, -0.666320f, -0.666257f, -0.666185f, -0.666105f, -0.666016f, +-0.665917f, -0.665807f, -0.665685f, -0.665551f, -0.665404f, -0.665242f, -0.665065f, -0.664872f, +-0.664662f, -0.664434f, -0.664186f, -0.663917f, -0.663625f, -0.663310f, -0.662970f, -0.662604f, +-0.662208f, -0.661783f, -0.661326f, -0.660834f, -0.660307f, -0.659741f, -0.659135f, -0.658485f, +-0.657789f, -0.657045f, -0.656250f, -0.655400f, -0.654491f, -0.653522f, -0.652486f, -0.651382f, +-0.650204f, -0.648948f, -0.647610f, -0.646184f, -0.644665f, -0.643047f, -0.641325f, -0.639491f, +-0.637540f, -0.635465f, -0.633257f, -0.630910f, -0.628414f, -0.625761f, -0.622943f, -0.619948f, +-0.616769f, -0.613393f, -0.609811f, -0.606010f, -0.601981f, -0.597711f, -0.593187f, -0.588399f, +-0.583333f, -0.577978f, -0.572322f, -0.566353f, -0.560059f, -0.553431f, -0.546459f, -0.539134f, +-0.531447f, -0.523393f, -0.514967f, -0.506166f, -0.496987f, -0.487431f, -0.477501f, -0.467200f, +-0.456535f, -0.445512f, -0.434141f, -0.422433f, -0.410401f, -0.398057f, -0.385417f, -0.372494f, +-0.359306f, -0.345868f, -0.332196f, -0.318307f, -0.304217f, -0.289941f, -0.275495f, -0.260893f, +-0.246151f, -0.231281f, -0.216296f, -0.201208f, -0.186029f, -0.170770f, -0.155440f, -0.140049f, +-0.124605f, -0.109117f, -0.093591f, -0.078035f, -0.062455f, -0.046857f, -0.031245f, -0.015624f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_11[256] = { +-0.733333f, -0.733333f, -0.733333f, -0.733332f, -0.733330f, -0.733327f, -0.733321f, -0.733314f, +-0.733304f, -0.733292f, -0.733275f, -0.733255f, -0.733230f, -0.733201f, -0.733165f, -0.733124f, +-0.733075f, -0.733020f, -0.732955f, -0.732882f, -0.732800f, -0.732706f, -0.732602f, -0.732485f, +-0.732354f, -0.732210f, -0.732050f, -0.731873f, -0.731679f, -0.731466f, -0.731233f, -0.730978f, +-0.730700f, -0.730397f, -0.730067f, -0.729710f, -0.729322f, -0.728903f, -0.728449f, -0.727960f, +-0.727432f, -0.726862f, -0.726250f, -0.725591f, -0.724882f, -0.724122f, -0.723305f, -0.722430f, +-0.721492f, -0.720487f, -0.719412f, -0.718261f, -0.717032f, -0.715717f, -0.714313f, -0.712813f, +-0.711213f, -0.709506f, -0.707686f, -0.705745f, -0.703678f, -0.701476f, -0.699131f, -0.696636f, +-0.693982f, -0.691160f, -0.688161f, -0.684974f, -0.681591f, -0.678000f, -0.674191f, -0.670154f, +-0.665877f, -0.661350f, -0.656561f, -0.651499f, -0.646154f, -0.640515f, -0.634571f, -0.628313f, +-0.621732f, -0.614820f, -0.607570f, -0.599974f, -0.592029f, -0.583730f, -0.575074f, -0.566062f, +-0.556694f, -0.546972f, -0.536899f, -0.526482f, -0.515726f, -0.504639f, -0.493233f, -0.481515f, +-0.469498f, -0.457195f, -0.444618f, -0.431780f, -0.418696f, -0.405378f, -0.391842f, -0.378100f, +-0.364167f, -0.350056f, -0.335780f, -0.321352f, -0.306784f, -0.292087f, -0.277273f, -0.262352f, +-0.247335f, -0.232229f, -0.217046f, -0.201792f, -0.186475f, -0.171104f, -0.155684f, -0.140221f, +-0.124723f, -0.109194f, -0.093638f, -0.078062f, -0.062468f, -0.046862f, -0.031246f, -0.015625f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_12[256] = { +-0.800000f, -0.800000f, -0.800000f, -0.799998f, -0.799996f, -0.799991f, -0.799985f, -0.799975f, +-0.799963f, -0.799946f, -0.799925f, -0.799899f, -0.799867f, -0.799828f, -0.799782f, -0.799728f, +-0.799665f, -0.799592f, -0.799509f, -0.799413f, -0.799305f, -0.799183f, -0.799045f, -0.798892f, +-0.798720f, -0.798530f, -0.798319f, -0.798086f, -0.797830f, -0.797548f, -0.797238f, -0.796900f, +-0.796531f, -0.796128f, -0.795690f, -0.795213f, -0.794696f, -0.794136f, -0.793530f, -0.792874f, +-0.792166f, -0.791402f, -0.790579f, -0.789693f, -0.788739f, -0.787714f, -0.786613f, -0.785431f, +-0.784164f, -0.782805f, -0.781350f, -0.779792f, -0.778126f, -0.776344f, -0.774441f, -0.772407f, +-0.770238f, -0.767923f, -0.765456f, -0.762828f, -0.760029f, -0.757050f, -0.753883f, -0.750518f, +-0.746943f, -0.743150f, -0.739128f, -0.734866f, -0.730354f, -0.725583f, -0.720540f, -0.715218f, +-0.709606f, -0.703694f, -0.697475f, -0.690940f, -0.684083f, -0.676897f, -0.669377f, -0.661520f, +-0.653321f, -0.644781f, -0.635898f, -0.626674f, -0.617111f, -0.607214f, -0.596986f, -0.586434f, +-0.575567f, -0.564391f, -0.552918f, -0.541156f, -0.529117f, -0.516812f, -0.504253f, -0.491452f, +-0.478422f, -0.465174f, -0.451722f, -0.438077f, -0.424251f, -0.410255f, -0.396103f, -0.381803f, +-0.367368f, -0.352806f, -0.338128f, -0.323344f, -0.308461f, -0.293488f, -0.278434f, -0.263304f, +-0.248107f, -0.232849f, -0.217536f, -0.202174f, -0.186768f, -0.171323f, -0.155844f, -0.140335f, +-0.124801f, -0.109244f, -0.093669f, -0.078079f, -0.062477f, -0.046865f, -0.031247f, -0.015625f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_13[256] = { +-0.866667f, -0.866667f, -0.866666f, -0.866664f, -0.866661f, -0.866655f, -0.866647f, -0.866635f, +-0.866618f, -0.866597f, -0.866570f, -0.866535f, -0.866494f, -0.866443f, -0.866383f, -0.866313f, +-0.866230f, -0.866135f, -0.866025f, -0.865900f, -0.865757f, -0.865596f, -0.865415f, -0.865212f, +-0.864986f, -0.864734f, -0.864455f, -0.864146f, -0.863805f, -0.863430f, -0.863019f, -0.862568f, +-0.862075f, -0.861536f, -0.860950f, -0.860312f, -0.859619f, -0.858866f, -0.858051f, -0.857169f, +-0.856216f, -0.855186f, -0.854075f, -0.852878f, -0.851590f, -0.850204f, -0.848714f, -0.847114f, +-0.845398f, -0.843558f, -0.841586f, -0.839477f, -0.837220f, -0.834808f, -0.832233f, -0.829485f, +-0.826556f, -0.823435f, -0.820114f, -0.816581f, -0.812828f, -0.808845f, -0.804621f, -0.800146f, +-0.795410f, -0.790404f, -0.785119f, -0.779546f, -0.773676f, -0.767502f, -0.761016f, -0.754213f, +-0.747087f, -0.739635f, -0.731852f, -0.723737f, -0.715290f, -0.706510f, -0.697400f, -0.687961f, +-0.678199f, -0.668118f, -0.657724f, -0.647026f, -0.636029f, -0.624744f, -0.613180f, -0.601348f, +-0.589256f, -0.576917f, -0.564342f, -0.551541f, -0.538527f, -0.525309f, -0.511900f, -0.498310f, +-0.484549f, -0.470629f, -0.456560f, -0.442350f, -0.428010f, -0.413549f, -0.398974f, -0.384295f, +-0.369519f, -0.354653f, -0.339705f, -0.324681f, -0.309587f, -0.294429f, -0.279214f, -0.263945f, +-0.248628f, -0.233267f, -0.217867f, -0.202433f, -0.186966f, -0.171472f, -0.155953f, -0.140413f, +-0.124854f, -0.109279f, -0.093691f, -0.078091f, -0.062483f, -0.046868f, -0.031248f, -0.015625f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_14[256] = { +-0.933333f, -0.933333f, -0.933332f, -0.933330f, -0.933326f, -0.933319f, -0.933308f, -0.933292f, +-0.933271f, -0.933243f, -0.933207f, -0.933162f, -0.933107f, -0.933041f, -0.932963f, -0.932870f, +-0.932761f, -0.932635f, -0.932490f, -0.932324f, -0.932136f, -0.931922f, -0.931682f, -0.931413f, +-0.931111f, -0.930775f, -0.930403f, -0.929990f, -0.929534f, -0.929032f, -0.928480f, -0.927874f, +-0.927211f, -0.926487f, -0.925697f, -0.924836f, -0.923900f, -0.922883f, -0.921781f, -0.920587f, +-0.919295f, -0.917900f, -0.916394f, -0.914770f, -0.913022f, -0.911142f, -0.909121f, -0.906952f, +-0.904625f, -0.902133f, -0.899466f, -0.896614f, -0.893569f, -0.890320f, -0.886857f, -0.883171f, +-0.879252f, -0.875090f, -0.870674f, -0.865996f, -0.861046f, -0.855816f, -0.850296f, -0.844480f, +-0.838360f, -0.831930f, -0.825184f, -0.818118f, -0.810729f, -0.803014f, -0.794973f, -0.786603f, +-0.777908f, -0.768889f, -0.759549f, -0.749893f, -0.739926f, -0.729655f, -0.719086f, -0.708227f, +-0.697088f, -0.685677f, -0.674004f, -0.662078f, -0.649911f, -0.637512f, -0.624893f, -0.612063f, +-0.599033f, -0.585813f, -0.572414f, -0.558845f, -0.545117f, -0.531238f, -0.517218f, -0.503066f, +-0.488789f, -0.474396f, -0.459895f, -0.445293f, -0.430597f, -0.415813f, -0.400948f, -0.386007f, +-0.370997f, -0.355923f, -0.340789f, -0.325601f, -0.310363f, -0.295078f, -0.279752f, -0.264387f, +-0.248988f, -0.233557f, -0.218097f, -0.202612f, -0.187104f, -0.171576f, -0.156029f, -0.140467f, +-0.124891f, -0.109303f, -0.093706f, -0.078100f, -0.062487f, -0.046870f, -0.031248f, -0.015625f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; +float symmetry_table_15[256] = { +-1.000000f, -1.000000f, -0.999999f, -0.999996f, -0.999990f, -0.999981f, -0.999966f, -0.999946f, +-0.999918f, -0.999881f, -0.999834f, -0.999775f, -0.999702f, -0.999615f, -0.999511f, -0.999387f, +-0.999243f, -0.999076f, -0.998882f, -0.998661f, -0.998409f, -0.998124f, -0.997802f, -0.997440f, +-0.997035f, -0.996583f, -0.996081f, -0.995524f, -0.994908f, -0.994229f, -0.993482f, -0.992661f, +-0.991762f, -0.990778f, -0.989704f, -0.988534f, -0.987260f, -0.985876f, -0.984375f, -0.982748f, +-0.980989f, -0.979089f, -0.977038f, -0.974829f, -0.972452f, -0.969898f, -0.967157f, -0.964218f, +-0.961074f, -0.957712f, -0.954123f, -0.950296f, -0.946223f, -0.941893f, -0.937297f, -0.932426f, +-0.927270f, -0.921822f, -0.916075f, -0.910022f, -0.903657f, -0.896976f, -0.889974f, -0.882649f, +-0.875000f, -0.867024f, -0.858724f, -0.850101f, -0.841157f, -0.831897f, -0.822325f, -0.812447f, +-0.802270f, -0.791801f, -0.781047f, -0.770018f, -0.758723f, -0.747171f, -0.735373f, -0.723337f, +-0.711074f, -0.698593f, -0.685907f, -0.673023f, -0.659952f, -0.646704f, -0.633288f, -0.619714f, +-0.605989f, -0.592123f, -0.578125f, -0.564001f, -0.549760f, -0.535409f, -0.520954f, -0.506403f, +-0.491762f, -0.477036f, -0.462232f, -0.447354f, -0.432408f, -0.417399f, -0.402331f, -0.387208f, +-0.372035f, -0.356815f, -0.341552f, -0.326249f, -0.310909f, -0.295536f, -0.280132f, -0.264701f, +-0.249243f, -0.233762f, -0.218261f, -0.202740f, -0.187202f, -0.171650f, -0.156084f, -0.140506f, +-0.124918f, -0.109321f, -0.093716f, -0.078106f, -0.062490f, -0.046871f, -0.031249f, -0.015625f, +0.000000f, 0.015625f, 0.031249f, 0.046871f, 0.062490f, 0.078106f, 0.093716f, 0.109321f, +0.124918f, 0.140506f, 0.156084f, 0.171650f, 0.187202f, 0.202740f, 0.218261f, 0.233762f, +0.249243f, 0.264701f, 0.280132f, 0.295536f, 0.310909f, 0.326249f, 0.341552f, 0.356815f, +0.372035f, 0.387208f, 0.402331f, 0.417399f, 0.432408f, 0.447354f, 0.462232f, 0.477036f, +0.491762f, 0.506403f, 0.520954f, 0.535409f, 0.549760f, 0.564001f, 0.578125f, 0.592123f, +0.605989f, 0.619714f, 0.633288f, 0.646704f, 0.659952f, 0.673023f, 0.685907f, 0.698593f, +0.711074f, 0.723337f, 0.735373f, 0.747171f, 0.758723f, 0.770018f, 0.781047f, 0.791801f, +0.802270f, 0.812447f, 0.822325f, 0.831897f, 0.841157f, 0.850101f, 0.858724f, 0.867024f, +0.875000f, 0.882649f, 0.889974f, 0.896976f, 0.903657f, 0.910022f, 0.916075f, 0.921822f, +0.927270f, 0.932426f, 0.937297f, 0.941893f, 0.946223f, 0.950296f, 0.954123f, 0.957712f, +0.961074f, 0.964218f, 0.967157f, 0.969898f, 0.972452f, 0.974829f, 0.977038f, 0.979089f, +0.980989f, 0.982748f, 0.984375f, 0.985876f, 0.987260f, 0.988534f, 0.989704f, 0.990778f, +0.991762f, 0.992661f, 0.993482f, 0.994229f, 0.994908f, 0.995524f, 0.996081f, 0.996583f, +0.997035f, 0.997440f, 0.997802f, 0.998124f, 0.998409f, 0.998661f, 0.998882f, 0.999076f, +0.999243f, 0.999387f, 0.999511f, 0.999615f, 0.999702f, 0.999775f, 0.999834f, 0.999881f, +0.999918f, 0.999946f, 0.999966f, 0.999981f, 0.999990f, 0.999996f, 0.999999f, 1.000000f +}; diff --git a/plugins/community/repos/squinkylabs-plug1/test.mk b/plugins/community/repos/squinkylabs-plug1/test.mk index 1cbc7e3e..d747dcd6 100644 --- a/plugins/community/repos/squinkylabs-plug1/test.mk +++ b/plugins/community/repos/squinkylabs-plug1/test.mk @@ -6,6 +6,7 @@ TEST_SOURCES = $(wildcard test/*.cpp) TEST_SOURCES += $(wildcard dsp/**/*.cpp) TEST_SOURCES += $(wildcard dsp/third-party/falco/*.cpp) TEST_SOURCES += $(wildcard sqsrc/**/*.cpp) +TEST_SOURCES += dsp/third-party/src/minblep.cpp TEST_SOURCES += dsp/third-party/kiss_fft130/tools/kiss_fftr.c TEST_SOURCES += dsp/third-party/kiss_fft130/kiss_fft.c diff --git a/plugins/community/repos/squinkylabs-plug1/test/Analyzer.cpp b/plugins/community/repos/squinkylabs-plug1/test/Analyzer.cpp new file mode 100644 index 00000000..2a06b69b --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/test/Analyzer.cpp @@ -0,0 +1,375 @@ +#include + +#include "asserts.h" +#include "Analyzer.h" +#include "AudioMath.h" +#include "FFT.h" +#include "FFTData.h" +#include "SinOscillator.h" +#include "AudioMath.h" + + +int Analyzer::getMax(const FFTDataCpx& data) +{ + return getMaxExcluding(data, std::set()); +} + +int Analyzer::getMaxExcluding(const FFTDataCpx& data, int exclusion) +{ + std::set exclusions; + exclusions.insert(exclusion); + return getMaxExcluding(data, exclusions); +} + +int Analyzer::getMaxExcluding(const FFTDataCpx& data, std::set exclusions) +{ + int maxBin = -1; + float maxMag = 0; + for (int i = 0; i < data.size(); ++i) { + if (exclusions.find(i) == exclusions.end()) { + const float mag = std::abs(data.get(i)); + if (mag > maxMag) { + maxMag = mag; + maxBin = i; + } + } + } + return maxBin; +} + +static int getMaxExcluding(const FFTDataCpx&, int excludeBin); + +float Analyzer::getSlope(const FFTDataCpx& response, float fTest, float sampleRate) +{ + const int bin1 = FFT::freqToBin(fTest, sampleRate, response.size()); + const int bin2 = bin1 * 4; // two octaves + assert(bin2 < response.size()); + const float mag1 = response.getAbs(bin1); + const float mag2 = response.getAbs(bin2); + return float(AudioMath::db(mag2) - AudioMath::db(mag1)) / 2; +} + +std::tuple Analyzer::getMaxAndShoulders(const FFTDataCpx& data, float atten) +{ + assert(atten < 0); + int maxBin = getMax(data); + int iMax = data.size() / 2; + + assert(maxBin >= 0); + const double dbShoulder = atten + AudioMath::db(std::abs(data.get(maxBin))); + + int i; + int iShoulderLow = -1; + int iShoulderHigh = -1; + bool done; + for (done = false, i = maxBin; !done; ) { + const double db = AudioMath::db(std::abs(data.get(i))); + if (i >= iMax) { + done = true; + } else if (db <= dbShoulder) { + iShoulderHigh = i; + done = true; + } else { + i++; + } + } + for (done = false, i = maxBin; !done; ) { + const double db = AudioMath::db(std::abs(data.get(i))); + if (db <= dbShoulder) { + iShoulderLow = i; + done = true; + } else if (i <= 0) { + done = true; + } else { + i--; + } + } + // printf("out of loop, imax=%d, shoulders=%d,%d\n", maxBin, iShoulderLow, iShoulderHigh); + + return std::make_tuple(iShoulderLow, maxBin, iShoulderHigh); +} + +std::tuple Analyzer::getMaxAndShouldersFreq(const FFTDataCpx& data, float atten, float sampleRate) +{ + auto stats = getMaxAndShoulders(data, atten); + return std::make_tuple(FFT::bin2Freq(std::get<0>(stats), sampleRate, data.size()), + FFT::bin2Freq(std::get<1>(stats), sampleRate, data.size()), + FFT::bin2Freq(std::get<2>(stats), sampleRate, data.size()) + ); +} + + +// TODO: pass in cutoff +std::vector Analyzer::getFeatures(const FFTDataCpx& data, float sensitivityDb, float sampleRate, float dbMinCutoff) +{ + // TODO: pass this in + // const float dbMinCutoff = -100; + assert(sensitivityDb > 0); + std::vector ret; + float lastDb = 10000000000; + // only look at the below nyquist stuff + for (int i = 0; i < data.size() / 2; ++i) { + const float db = (float) AudioMath::db(std::abs(data.get(i))); + if ((std::abs(db - lastDb) >= sensitivityDb) && (db > dbMinCutoff)) { + double freq = FFT::bin2Freq(i, sampleRate, data.size()); + FPoint p(float(freq), db); + // printf("feature at bin %d, db=%f raw val=%f\n", i, db, std::abs(data.get(i))); + ret.push_back(p); + lastDb = db; + } + } + return ret; +} + + +std::vector Analyzer::getPeaks(const FFTDataCpx& data, float sampleRate, float minDb) +{ + std::vector ret; + + // only look at the below nyquist stuff + for (int i = 0; i < data.size() / 2; ++i) { + const double mag = std::abs(data.get(i)); + const double db = AudioMath::db(mag); + bool isPeak = false; + if (i < 2 || i >(data.size() / 2) - 2) { + isPeak = true; + } else { + + const double magBelow = std::abs(data.get(i - 1)); + const double magAbove = std::abs(data.get(i + 1)); + if (mag <= magBelow || mag <= magAbove) { + isPeak = false; + } else { + +#if 1 + double average = 0; + for (int j = 0; j < 5; ++j) { + average += std::abs(data.get(i + j - 2)); + } + average -= mag; // subtract out our contribution + average /= 4.0; + double a = std::abs(data.get(i - 2)); + double b = std::abs(data.get(i - 1)); + double c = std::abs(data.get(i - 0)); + double d = std::abs(data.get(i + 1)); + double e = std::abs(data.get(i + 2)); + isPeak = (mag > (average * 2)); +#else + //this way average db + double average = 0; + for (int j = 0; j < 5; ++j) { + average += AudioMath::db(std::abs(data.get(i + j - 2))); + } + isPeak = (db > (average + 3)); +#endif + //if (isPeak) printf("accepted peak at %f db, average was %f\n", db, average); + } + } + if (db < minDb) { + isPeak = false; + } + + if (isPeak) { + //if ((std::abs(db - lastDb) >= sensitivityDb) && (db > dbMinCutoff)) { + double freq = FFT::bin2Freq(i, sampleRate, data.size()); + FPoint p(float(freq), (float) db); + // printf("feature at bin %d, db=%f raw val=%f\n", i, db, std::abs(data.get(i))); + ret.push_back(p); + } + } + return ret; +} + +void Analyzer::getAndPrintFeatures(const FFTDataCpx& data, float sensitivityDb, float sampleRate, float dbMinCutoff) +{ + auto features = getFeatures(data, sensitivityDb, sampleRate, dbMinCutoff); + printf("there are %d features\n", (int) features.size()); + for (int i = 0; i < (int) features.size(); ++i) { + printf("feature: freq=%.2f, db=%.2f\n", features[i].freq, features[i].gainDb); + } +} + +void Analyzer::getAndPrintPeaks(const FFTDataCpx& data, float sampleRate, float minDb) +{ + auto peaks = getPeaks(data, sampleRate, minDb); + printf("there are %d peaks\n", (int) peaks.size()); + for (int i = 0; i < (int) peaks.size(); ++i) { + printf("peak: freq=%f, db=%f\n", peaks[i].freq, peaks[i].gainDb); + } +} + + +void Analyzer::getAndPrintFreqOfInterest(const FFTDataCpx& data, float sampleRate, const std::vector& freqOfInterest) +{ + for (double freq : freqOfInterest) { + int bin = FFT::freqToBin((float) freq, sampleRate, data.size()); + if (bin > 2 && bin < data.size() - 2) { + + ; + double a = AudioMath::db(std::abs(data.get(bin - 2))); + double b = AudioMath::db(std::abs(data.get(bin - 1))); + double c = AudioMath::db(std::abs(data.get(bin))); + double d = AudioMath::db(std::abs(data.get(bin + 1))); + double e = AudioMath::db(std::abs(data.get(bin + 2))); + + double db = std::max(e, std::max( + std::max(a, b), + std::max(c, d))); + + printf("freq=%.2f db=%f range:%.2f,%.2f,%.2f,%.2f,%.2f\n", freq, db, + a, b, c, d, e + ); + } + } + +#if 0 + for (int i = 0; i < data.size() / 2; ++i) { + double freq = FFT::bin2Freq(i, sampleRate, data.size()); + } +#endif + +} + +void Analyzer::getFreqResponse(FFTDataCpx& out, std::function func) +{ + /** + * testSignal is the time domain sweep + * testOutput if the time domain output of "func" + * testSpecrum is the FFT of testSignal + * spectrum is the FFT of testOutput + + */ + // First set up a test signal + const int numSamples = out.size(); + // std::vector testSignal(numSamples); + FFTDataReal testSignal(numSamples); + generateSweep(44100, testSignal.data(), numSamples, 20, 20000); + + // Run the test signal though func, capture output in fft real + FFTDataReal testOutput(numSamples); + for (int i = 0; i < out.size(); ++i) { + const float y = func(testSignal.get(i)); + testOutput.set(i, y); + } + + // then take the inverse fft + FFTDataCpx spectrum(numSamples); + FFT::forward(&spectrum, testOutput); + + + // take the forward FFT of the test signal + FFTDataCpx testSpectrum(numSamples); + FFT::forward(&testSpectrum, testSignal); + + for (int i = 0; i < numSamples; ++i) { + const cpx x = (std::abs(testSpectrum.get(i)) == 0) ? 0 : + spectrum.get(i) / testSpectrum.get(i); + out.set(i, x); + } + +#if 0 + for (int i = 0; i < numSamples; ++i) { + printf("%d, sig=%f out=%f mag(sig)=%f mag(out)=%f rsp=%f\n", + i, testSignal.get(i), testOutput.get(i), + std::abs(testSpectrum.get(i)), + std::abs(spectrum.get(i)), + std::abs(out.get(i)) + ); + } +#endif +} + + +double Analyzer::hamming(int iSample, int totalSamples) +{ + const double a0 = .53836; + double theta = AudioMath::Pi * 2.0 * double(iSample) / double(totalSamples - 1); + return a0 - (1.0 - a0) * std::cos(theta); +} + +void Analyzer::getSpectrum(FFTDataCpx& out, bool useWindow, std::function func) +{ + + const int numSamples = out.size(); + + // Run the test signal though func, capture output in fft real + FFTDataReal testOutput(numSamples); + for (int i = 0; i < out.size(); ++i) { + const float w = useWindow ? (float) hamming(i, out.size()) : 1; + const float y = float(func() * w); + testOutput.set(i, y); + } + + FFT::forward(&out, testOutput); + +#if 0 + for (int i = 0; i < numSamples; ++i) { + printf("%d, sig=%f out=%f mag(sig)=%f mag(out)=%f rsp=%f\n", + i, testSignal.get(i), testOutput.get(i), + std::abs(testSpectrum.get(i)), + std::abs(spectrum.get(i)), + std::abs(out.get(i)) + ); + } +#endif +} + +void Analyzer::generateSweep(float sampleRate, float* out, int numSamples, float minFreq, float maxFreq) +{ + assert(maxFreq > minFreq); + const double minLog = std::log2(minFreq); + const double maxLog = std::log2(maxFreq); + const double delta = (maxLog - minLog) / numSamples; + + SinOscillatorParams params; + SinOscillatorState state; + + double fLog = minLog; + for (int i = 0; i < numSamples; ++i, fLog += delta) { + const double freq = std::pow(2, fLog); + assert(freq < (sampleRate / 2)); + + SinOscillator::setFrequency(params, freq / sampleRate); + double val = SinOscillator::run(state, params); + + // ::printf("out[%d] = %f f=%f\n", i, val, freq); + out[i] = (float) val; + } +} + +double Analyzer::makeEvenPeriod(double desiredFreq, double sampleRate, int numSamples) +{ + assert(desiredFreq <= (sampleRate / 2.0)); + assert(numSamples > 2); + double desiredPeriodSamples = sampleRate / desiredFreq; + double periodsPerFrame = numSamples / desiredPeriodSamples; + + + //printf("desiredFreq = %f, desired period/ samp = %f, periods per frame = %f\n", desiredFreq, desiredPeriodSamples, periodsPerFrame); + + + double evenPeriodsPerFrame = std::floor(periodsPerFrame); + + double period = (double) numSamples / evenPeriodsPerFrame; + //printf("period = %f\n", period); + double freq = sampleRate / period; + //printf("freq = %f\n", freq); + assert(freq > .0001); // don't want zero + return (freq); +} + +void Analyzer::assertSingleFreq(const FFTDataCpx& spectrum, float expectedFreq, float sampleRate) +{ + assert(expectedFreq < (sampleRate / 2)); + int maxBin = Analyzer::getMax(spectrum); + double maxFreq = FFT::bin2Freq(maxBin, sampleRate, spectrum.size()); + + int nextMaxBin = Analyzer::getMaxExcluding(spectrum, maxBin); + float maxPower = std::abs(spectrum.get(maxBin)); + float nextMaxPower = std::abs(spectrum.get(nextMaxBin)); + + double spuriousDb = AudioMath::db(nextMaxPower / maxPower); + + assertClose(maxFreq, expectedFreq, 1); + assertLE(spuriousDb, 70); +} \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/test/Analyzer.h b/plugins/community/repos/squinkylabs-plug1/test/Analyzer.h new file mode 100644 index 00000000..3f80dfe9 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/test/Analyzer.h @@ -0,0 +1,83 @@ +#pragma once + +#include +#include +#include + +#include "FFTData.h" +#include "FFT.h" + + +class Analyzer +{ +public: + Analyzer() = delete; + + class FPoint + { + public: + FPoint(float f, float g) : freq(f), gainDb(g) + { + } + float freq; + float gainDb; + }; + + static std::vector getFeatures(const FFTDataCpx&, float sensitivityDb, float sampleRate, float minDb); + static std::vector getPeaks(const FFTDataCpx&, float sampleRate, float minDb); + static void getAndPrintFeatures(const FFTDataCpx&, float sensitivityDb, float sampleRate, float minDb); + static void getAndPrintPeaks(const FFTDataCpx&, float sampleRate, float minDb); + static void getAndPrintFreqOfInterest(const FFTDataCpx&, float sampleRate, const std::vector& freqOfInterest); + + static int getMax(const FFTDataCpx&); + static int getMaxExcluding(const FFTDataCpx&, std::set exclusions); + static int getMaxExcluding(const FFTDataCpx&, int exclusion); + + /** + * 0 = low freq bin # + * 1 = peak bin # + * 2 = high bin# + * dbAtten (typically -3 + */ + static std::tuple getMaxAndShoulders(const FFTDataCpx&, float dbAtten); + + /** + * 0 = low freq + * 1 = peak freq + * 2 = high freq + * dbAtten (typically -3 + */ + static std::tuple getMaxAndShouldersFreq(const FFTDataCpx&, float dbAtten, float sampleRate); + + /** + * Calculates the frequency response of func + * by calling it with a known test signal. + */ + static void getFreqResponse(FFTDataCpx& out, std::function func); + + /** + * Calculates the spectrum of func + * by calling it can capturing its output + */ + static void getSpectrum(FFTDataCpx& out, bool useWindow, std::function func); + + static float getSlope(const FFTDataCpx& response, float fTest, float sampleRate); + + static void generateSweep(float sampleRate, float* out, int numSamples, float minFreq, float maxFreq); + + /** + * Adjusts desiredFreq to a frequency that is close, but is an exact division of + * numSamples. + */ + static double makeEvenPeriod(double desiredFreq, double sampleRate, int numSamples); + + static double hamming(int iSample, int totalSamples); + + /** + * Assert that there is a single frequency in spectrum, and that it is close to + * expectedFreq. + * + * In other words, check that the signal was a reasonably pure sin. + */ + static void assertSingleFreq(const FFTDataCpx& spectrum, float expectedFreq, float sampleRate); +}; diff --git a/plugins/community/repos/squinkylabs-plug1/test/MeasureTime.h b/plugins/community/repos/squinkylabs-plug1/test/MeasureTime.h index 095f381c..5ef56340 100644 --- a/plugins/community/repos/squinkylabs-plug1/test/MeasureTime.h +++ b/plugins/community/repos/squinkylabs-plug1/test/MeasureTime.h @@ -37,13 +37,16 @@ public: /** * Executes function "func" and measures how long it takes. - * Will call func in a tight look lasting minTime seconds. + * Will call func in a tight loop lasting minTime seconds. * When done, prints out statistics. + * + * returns - percent used */ - static void run(const char * name, std::function func, float minTime) + static double run(double overhead, const char * name, std::function func, float minTime) { int64_t iterations; bool done = false; + double percent = 0; //keep increasing the number of iterations until we last at least minTime seconds for (iterations = 100; !done; iterations *= 2) { @@ -51,7 +54,8 @@ public: if (elapsed >= minTime) { double itersPerSec = iterations / elapsed; double full = 44100; - double percent = full * 100 / itersPerSec; + percent = full * 100 / itersPerSec; + percent -= overhead; printf("\nmeasure %s over time %f\n", name, minTime); printf("did %" PRId64 " iterations in %f seconds\n", iterations, elapsed); @@ -63,6 +67,7 @@ public: done = true; } } + return percent; } /** @@ -82,10 +87,9 @@ public: } }; - /** * Simple producer / consumer for test data. - * Serves up a precalculated list of random numbers. + * Serves up a pre-calculated list of random numbers. */ template class TestBuffers @@ -121,7 +125,6 @@ private: static T destData[size]; }; - template T TestBuffers::sourceData[size]; @@ -133,13 +136,3 @@ size_t TestBuffers::sourceIndex = 0; template size_t TestBuffers::destIndex = 512; - - -/** - * Simple timer implementation for running inside Visual Studio - */ - - - - - diff --git a/plugins/community/repos/squinkylabs-plug1/test/SqTime.h b/plugins/community/repos/squinkylabs-plug1/test/SqTime.h index f37b0e0d..44241525 100644 --- a/plugins/community/repos/squinkylabs-plug1/test/SqTime.h +++ b/plugins/community/repos/squinkylabs-plug1/test/SqTime.h @@ -31,7 +31,6 @@ public: private: static double frequency; }; -double SqTime::frequency = 0; #else #include diff --git a/plugins/community/repos/squinkylabs-plug1/test/TestSignal.h b/plugins/community/repos/squinkylabs-plug1/test/TestSignal.h index aa51b8ce..8a451510 100644 --- a/plugins/community/repos/squinkylabs-plug1/test/TestSignal.h +++ b/plugins/community/repos/squinkylabs-plug1/test/TestSignal.h @@ -83,7 +83,7 @@ inline double TestSignal::measureOutput(int numSamples, std::function fu for (int i = 0; i < numSamples; ++i) { buffer[i] = func(); } - + return getRMS(buffer.data(), numSamples); } \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/test/TimeStatsCollector.h b/plugins/community/repos/squinkylabs-plug1/test/TimeStatsCollector.h index 7941afa0..c03ce22d 100644 --- a/plugins/community/repos/squinkylabs-plug1/test/TimeStatsCollector.h +++ b/plugins/community/repos/squinkylabs-plug1/test/TimeStatsCollector.h @@ -19,7 +19,7 @@ public: */ void start() { - startTime = SqTime::seconds(); + startTime = SqTime::seconds(); } /** @@ -28,36 +28,36 @@ public: */ void stop(int numLoops) { - const double stop = SqTime::seconds(); - const double delta = stop - startTime; - numDataPoints += numLoops; - totalTimeFrame += delta; - if (delta < minTime) { - minTime = delta; - } else if (delta > maxTime) { - maxTime = delta; - } - - if (numDataPoints > 1000) { - const double avgTime = totalTimeFrame / numDataPoints; - const double srTime = (1.0 / 44100.0); - const double ratio = avgTime / srTime; + const double stop = SqTime::seconds(); + const double delta = stop - startTime; + numDataPoints += numLoops; + totalTimeFrame += delta; + if (delta < minTime) { + minTime = delta; + } else if (delta > maxTime) { + maxTime = delta; + } + + if (numDataPoints > 1000) { + const double avgTime = totalTimeFrame / numDataPoints; + const double srTime = (1.0 / 44100.0); + const double ratio = avgTime / srTime; totalTimeGlobal += avgTime; numFramesInTotal++; double runningAvg = totalTimeGlobal / (numFramesInTotal * srTime); printf("\nsrTime=%f avtTime=%f\n", srTime, avgTime); - printf("this block: %f%% min=%f max=%f globalmax=%f running avg=%f\n", + printf("this block: %f%% min=%f max=%f globalmax=%f running avg=%f\n", ratio * 100, minTime, maxTime, globalMax, runningAvg * 100); if (globalMax < maxTime) { globalMax = maxTime; } - numDataPoints=0; - totalTimeFrame = 0; - minTime = 1000000000; - maxTime = -100000000000; - } + numDataPoints = 0; + totalTimeFrame = 0; + minTime = 1000000000; + maxTime = -100000000000; + } } private: diff --git a/plugins/community/repos/squinkylabs-plug1/test/main.cpp b/plugins/community/repos/squinkylabs-plug1/test/main.cpp index 3629eaaf..64323b1f 100644 --- a/plugins/community/repos/squinkylabs-plug1/test/main.cpp +++ b/plugins/community/repos/squinkylabs-plug1/test/main.cpp @@ -28,15 +28,37 @@ extern void testFinalLeaks(); extern void testClockMult(); extern void testTremolo(); extern void testGateTrigger(); +extern void testAnalyzer(); +extern void testFilter(); +extern void testStochasticGrammar(); +extern void testGMR(); +extern void testLowpassFilter(); +extern void testPoly(); +extern void testVCO(); +extern void testFilterDesign(); +extern void testVCOAlias(); +extern void testSin(); +extern void testMinBLEPVCO(); +extern void testRateConversion(); +extern void testDelay(); +extern void testSpline(bool emit); +extern void testButterLookup(); int main(int argc, char ** argv) { bool runPerf = false; bool extended = false; + bool runShaperGen = false; if (argc > 1) { std::string arg = argv[1]; if (arg == "--ext") { extended = true; + } else if (arg == "--perf") { + runPerf = true; + } else if (arg == "--shaper") { + runShaperGen = true; + } else { + printf("%s is not a valid command line argument\n", arg.c_str()); } } #ifdef _PERF @@ -49,6 +71,11 @@ int main(int argc, char ** argv) // Want to be sure we are testing the case we care about. assert(sizeof(size_t) == 8); + if (runShaperGen) { + testSpline(true); + return 0; + } + testAudioMath(); testRingBuffer(); testGateTrigger(); @@ -59,25 +86,51 @@ int main(int argc, char ** argv) testBiquad(); testSaw(); testClockMult(); + testDelay(); + testPoly(); testSinOscillator(); + testMinBLEPVCO(); testHilbert(); - testStateVariable(); + testButterLookup(); + testSpline(false); + testVCO(); + + // testSin(); + + testFFT(); + testAnalyzer(); + testRateConversion(); + + + // printf("skipping lots of tests\n"); +#if 1 + testStateVariable(); testFFTCrossFader(); - testThread(extended); + if (extended) { + testThread(extended); + } + + testLowpassFilter(); + testFilter(); + + testStochasticGrammar(); + testGMR(); // after testing all the components, test composites. testTremolo(); testColoredNoise(); testFrequencyShifter(); testVocalAnimator(); - +#endif if (runPerf) { perfTest(); } + + testFilterDesign(); testFinalLeaks(); // When we run inside Visual Studio, don't exit debugger immediately diff --git a/plugins/community/repos/squinkylabs-plug1/test/perfTest.cpp b/plugins/community/repos/squinkylabs-plug1/test/perfTest.cpp index d1569819..9b039c1e 100644 --- a/plugins/community/repos/squinkylabs-plug1/test/perfTest.cpp +++ b/plugins/community/repos/squinkylabs-plug1/test/perfTest.cpp @@ -3,6 +3,9 @@ #include #include +#include "EvenVCO.h" +//#include "EvenVCO_orig.h" + #include "AudioMath.h" #include "BiquadParams.h" #include "BiquadFilter.h" @@ -15,6 +18,15 @@ #include "Tremolo.h" #include "VocalAnimator.h" #include "VocalFilter.h" +#include "LFN.h" +#include "GMR.h" +#include "CHB.h" +#include "FunVCOComposite.h" +#include "EV3.h" +#include "daveguide.h" +#include "Shaper.h" +#include "Super.h" + using Shifter = FrequencyShifter; using Animator = VocalAnimator; @@ -22,8 +34,13 @@ using VocFilter = VocalFilter; using Colors = ColoredNoise; using Trem = Tremolo; + #include "MeasureTime.h" +#if defined(_MSC_VER) || defined(ARCH_WIN) +double SqTime::frequency = 0; +#endif + // There are many tests that are disabled with #if 0. // In most cases they still work, but don't need to be run regularly @@ -34,9 +51,7 @@ static void test1() srand(57); const double scale = 1.0 / RAND_MAX; - MeasureTime::run("test1 (do nothing)", [&d, scale]() { - return TestBuffers::get(); - }, 1); + MeasureTime::run("test1 sin", []() { float x = std::sin(TestBuffers::get()); @@ -98,6 +113,26 @@ static void test1() } #endif +double overheadInOut = 0; +double overheadOutOnly = 0; + +static void setup() +{ +#ifdef _DEBUG +// assert(false); // don't run this in debug +#endif + double d = .1; + const double scale = 1.0 / RAND_MAX; + overheadInOut = MeasureTime::run(0.0, "test1 (do nothing i/o)", [&d, scale]() { + return TestBuffers::get(); + }, 1); + + overheadOutOnly = MeasureTime::run(0.0, "test1 (do nothing oo)", [&d, scale]() { + return 0.0f; + }, 1); + +} + template static void testHilbert() { @@ -137,7 +172,8 @@ static void testShifter() fs.inputs[Shifter::AUDIO_INPUT].value = 0; - MeasureTime::run("shifter", [&fs]() { + assert(overheadInOut >= 0); + MeasureTime::run(overheadInOut, "shifter", [&fs]() { fs.inputs[Shifter::AUDIO_INPUT].value = TestBuffers::get(); fs.step(); return fs.outputs[Shifter::SIN_OUTPUT].value; @@ -153,7 +189,7 @@ static void testAnimator() an.inputs[Shifter::AUDIO_INPUT].value = 0; - MeasureTime::run("animator", [&an]() { + MeasureTime::run(overheadInOut, "animator", [&an]() { an.inputs[Shifter::AUDIO_INPUT].value = TestBuffers::get(); an.step(); return an.outputs[Shifter::SIN_OUTPUT].value; @@ -170,15 +206,13 @@ static void testVocalFilter() an.inputs[Shifter::AUDIO_INPUT].value = 0; - MeasureTime::run("vocal filter", [&an]() { + MeasureTime::run(overheadInOut, "vocal filter", [&an]() { an.inputs[Shifter::AUDIO_INPUT].value = TestBuffers::get(); an.step(); return an.outputs[Shifter::SIN_OUTPUT].value; }, 1); } - - static void testColors() { Colors co; @@ -187,7 +221,7 @@ static void testColors() co.init(); - MeasureTime::run("colors", [&co]() { + MeasureTime::run(overheadInOut, "colors", [&co]() { co.step(); return co.outputs[Colors::AUDIO_OUTPUT].value; }, 1); @@ -201,13 +235,447 @@ static void testTremolo() tr.init(); - MeasureTime::run("trem", [&tr]() { + MeasureTime::run(overheadInOut, "trem", [&tr]() { tr.inputs[Trem::AUDIO_INPUT].value = TestBuffers::get(); tr.step(); return tr.outputs[Trem::AUDIO_OUTPUT].value; }, 1); } +static void testLFN() +{ + LFN lfn; + + lfn.setSampleTime(1.0f / 44100.f); + lfn.init(); + + MeasureTime::run(overheadOutOnly, "lfn", [&lfn]() { + lfn.step(); + return lfn.outputs[LFN::OUTPUT].value; + }, 1); +} + +#if 0 +static void testEvenOrig() +{ + EvenVCO_orig lfn; + + lfn.outputs[EvenVCO_orig::EVEN_OUTPUT].active = true; + lfn.outputs[EvenVCO_orig::SINE_OUTPUT].active = true; + lfn.outputs[EvenVCO_orig::TRI_OUTPUT].active = true; + lfn.outputs[EvenVCO_orig::SQUARE_OUTPUT].active = true; + lfn.outputs[EvenVCO_orig::SAW_OUTPUT].active = true; + + for (int i = 0; i < 100; ++i) lfn.step(); + + MeasureTime::run(overheadOutOnly, "Even orig", [&lfn]() { + lfn.inputs[EvenVCO_orig::PITCH1_INPUT].value = TestBuffers::get(); + lfn.step(); + return lfn.outputs[EvenVCO::EVEN_OUTPUT].value; + }, 1); +} +#endif + +static void testEven() +{ + EvenVCO lfn; + + + lfn.outputs[EvenVCO::EVEN_OUTPUT].active = true; + lfn.outputs[EvenVCO::SINE_OUTPUT].active = true; + lfn.outputs[EvenVCO::TRI_OUTPUT].active = true; + lfn.outputs[EvenVCO::SQUARE_OUTPUT].active = true; + lfn.outputs[EvenVCO::SAW_OUTPUT].active = true; + MeasureTime::run(overheadOutOnly, "Even, all outs", [&lfn]() { + lfn.step(); + return lfn.outputs[EvenVCO::EVEN_OUTPUT].value; + }, 1); +} + +static void testEvenEven() +{ + EvenVCO lfn; + + lfn.outputs[EvenVCO::EVEN_OUTPUT].active = true; + lfn.outputs[EvenVCO::SINE_OUTPUT].active = false; + lfn.outputs[EvenVCO::TRI_OUTPUT].active = false; + lfn.outputs[EvenVCO::SQUARE_OUTPUT].active = false; + lfn.outputs[EvenVCO::SAW_OUTPUT].active = false; + + MeasureTime::run(overheadOutOnly, "Even, even only", [&lfn]() { + lfn.step(); + return lfn.outputs[EvenVCO::EVEN_OUTPUT].value; + }, 1); +} + +static void testEvenSin() +{ + EvenVCO lfn; + + lfn.outputs[EvenVCO::EVEN_OUTPUT].active = false; + lfn.outputs[EvenVCO::SINE_OUTPUT].active = true; + lfn.outputs[EvenVCO::TRI_OUTPUT].active = false; + lfn.outputs[EvenVCO::SQUARE_OUTPUT].active = false; + lfn.outputs[EvenVCO::SAW_OUTPUT].active = false; + + MeasureTime::run(overheadOutOnly, "Even, sin only", [&lfn]() { + lfn.step(); + return lfn.outputs[EvenVCO::SAW_OUTPUT].value; + }, 1); +} + +static void testEvenSaw() +{ + EvenVCO lfn; + + lfn.outputs[EvenVCO::EVEN_OUTPUT].active = false; + lfn.outputs[EvenVCO::SINE_OUTPUT].active = false; + lfn.outputs[EvenVCO::TRI_OUTPUT].active = false; + lfn.outputs[EvenVCO::SQUARE_OUTPUT].active = false; + lfn.outputs[EvenVCO::SAW_OUTPUT].active = true; + + for (int i = 0; i < 100; ++i) lfn.step(); + + MeasureTime::run(overheadOutOnly, "Even, saw only", [&lfn]() { + lfn.inputs[EvenVCO::PITCH1_INPUT].value = TestBuffers::get(); + lfn.step(); + return lfn.outputs[EvenVCO::SAW_OUTPUT].value; + }, 1); +} + + +static void testEvenTri() +{ + EvenVCO lfn; + + lfn.outputs[EvenVCO::EVEN_OUTPUT].active = false; + lfn.outputs[EvenVCO::SINE_OUTPUT].active = false; + lfn.outputs[EvenVCO::TRI_OUTPUT].active = true; + lfn.outputs[EvenVCO::SQUARE_OUTPUT].active = false; + lfn.outputs[EvenVCO::SAW_OUTPUT].active = false; + + MeasureTime::run(overheadOutOnly, "Even, tri only", [&lfn]() { + lfn.step(); + return lfn.outputs[EvenVCO::TRI_OUTPUT].value; + }, 1); +} + +static void testEvenSq() +{ + EvenVCO lfn; + + lfn.outputs[EvenVCO::EVEN_OUTPUT].active = false; + lfn.outputs[EvenVCO::SINE_OUTPUT].active = false; + lfn.outputs[EvenVCO::TRI_OUTPUT].active = false; + lfn.outputs[EvenVCO::SQUARE_OUTPUT].active = true; + lfn.outputs[EvenVCO::SAW_OUTPUT].active = false; + + MeasureTime::run(overheadOutOnly, "Even, Sq only", [&lfn]() { + lfn.step(); + return lfn.outputs[EvenVCO::TRI_OUTPUT].value; + }, 1); +} + +static void testEvenSqSaw() +{ + EvenVCO lfn; + + lfn.outputs[EvenVCO::EVEN_OUTPUT].active = false; + lfn.outputs[EvenVCO::SINE_OUTPUT].active = false; + lfn.outputs[EvenVCO::TRI_OUTPUT].active = false; + lfn.outputs[EvenVCO::SQUARE_OUTPUT].active = true; + lfn.outputs[EvenVCO::SAW_OUTPUT].active = true; + + MeasureTime::run(overheadOutOnly, "Even, Sq Saw", [&lfn]() { + lfn.step(); + return lfn.outputs[EvenVCO::TRI_OUTPUT].value; + }, 1); +} + +static void testFun() +{ + FunVCOComposite lfn; + + for (int i = 0; i < lfn.NUM_OUTPUTS; ++i) { + lfn.outputs[i].active = true; + } + + lfn.setSampleRate(44100.f); + const bool isAnalog = false; + lfn.params[FunVCOComposite::MODE_PARAM].value = isAnalog ? 1.0f : 0.f; + + MeasureTime::run(overheadOutOnly, "Fun all on, digital", [&lfn]() { + lfn.step(); + return lfn.outputs[FunVCOComposite::TRI_OUTPUT].value + + lfn.outputs[FunVCOComposite::SAW_OUTPUT].value + + lfn.outputs[FunVCOComposite::SIN_OUTPUT].value + + lfn.outputs[FunVCOComposite::SQR_OUTPUT].value; + }, 1); +} + +static void testFunNone() +{ + FunVCOComposite lfn; + + for (int i = 0; i < lfn.NUM_OUTPUTS; ++i) { + lfn.outputs[i].active = false; + } + + lfn.setSampleRate(44100.f); + + MeasureTime::run(overheadOutOnly, "Fun all off", [&lfn]() { + lfn.step(); + return lfn.outputs[FunVCOComposite::TRI_OUTPUT].value; + }, 1); +} + +static void testFunSaw(bool isAnalog) +{ + FunVCOComposite lfn; + + lfn.outputs[FunVCOComposite::SIN_OUTPUT].active = false; + lfn.outputs[FunVCOComposite::TRI_OUTPUT].active = false; + lfn.outputs[FunVCOComposite::SQR_OUTPUT].active = false; + lfn.outputs[FunVCOComposite::SAW_OUTPUT].active = true; + + // oscillator.analog = TBase::params[MODE_PARAM].value > 0.0f; + lfn.params[FunVCOComposite::MODE_PARAM].value = isAnalog ? 1.0f : 0.f; + + lfn.setSampleRate(44100.f); + + std::string title = isAnalog ? "Fun Saw Analog" : "Fun Saw Digital"; + MeasureTime::run(overheadOutOnly, title.c_str(), [&lfn]() { + lfn.step(); + return lfn.outputs[FunVCOComposite::SAW_OUTPUT].value; + }, 1); +} + +static void testFunSin(bool isAnalog) +{ + FunVCOComposite lfn; + + lfn.outputs[FunVCOComposite::SIN_OUTPUT].active = true; + lfn.outputs[FunVCOComposite::TRI_OUTPUT].active = false; + lfn.outputs[FunVCOComposite::SQR_OUTPUT].active = false; + lfn.outputs[FunVCOComposite::SAW_OUTPUT].active = false; + + lfn.params[FunVCOComposite::MODE_PARAM].value = isAnalog ? 1.0f : 0.f; + + lfn.setSampleRate(44100.f); + + std::string title = isAnalog ? "Fun Sin Analog" : "Fun Sin Digital"; + MeasureTime::run(overheadOutOnly, title.c_str(), [&lfn]() { + lfn.step(); + return lfn.outputs[FunVCOComposite::SAW_OUTPUT].value; + }, 1); +} + +static void testFunSq() +{ + FunVCOComposite lfn; + + lfn.outputs[FunVCOComposite::SIN_OUTPUT].active = false; + lfn.outputs[FunVCOComposite::TRI_OUTPUT].active = false; + lfn.outputs[FunVCOComposite::SQR_OUTPUT].active = true; + lfn.outputs[FunVCOComposite::SAW_OUTPUT].active = false; + + lfn.setSampleRate(44100.f); + + MeasureTime::run(overheadOutOnly, "Fun sq", [&lfn]() { + lfn.step(); + return lfn.outputs[FunVCOComposite::SAW_OUTPUT].value; + }, 1); +} + +static void testCHB(bool econ) +{ + CHB chb; + +// chb.init(); + chb.setEconomy(econ); + + std::string name = "chb "; + name += econ ? "econ" : "full"; + MeasureTime::run(overheadOutOnly, name.c_str(), [&chb]() { + chb.step(); + return chb.outputs[CHB::MIX_OUTPUT].value; + }, 1); +} + +static void testCHBdef() +{ + CHB chb; + std::string name = "chbdef "; + MeasureTime::run(overheadOutOnly, name.c_str(), [&chb]() { + chb.step(); + return chb.outputs[CHB::MIX_OUTPUT].value; + }, 1); +} + +static void testEV3() +{ + EV3 ev3; + + // chb.init(); + + MeasureTime::run(overheadOutOnly, "ev3", [&ev3]() { + ev3.step(); + return ev3.outputs[EV3::MIX_OUTPUT].value; + }, 1); +} + +static void testGMR() +{ + GMR gmr; + + gmr.setSampleRate(44100); + gmr.init(); + + MeasureTime::run(overheadOutOnly, "gmr", [&gmr]() { + gmr.step(); + return gmr.outputs[GMR::TRIGGER_OUTPUT].value; + }, 1); +} + +static void testDG() +{ + Daveguide gmr; + + // gmr.setSampleRate(44100); + // gmr.init(); + + MeasureTime::run(overheadOutOnly, "dg", [&gmr]() { + gmr.step(); + return gmr.outputs[GMR::TRIGGER_OUTPUT].value; + }, 1); +} + +// 95 +// down to 67 for just the oversampler. +static void testShaper1a() +{ + Shaper gmr; + + // gmr.setSampleRate(44100); + // gmr.init(); + gmr.params[Shaper::PARAM_OVERSAMPLE].value = 0; + gmr.params[Shaper::PARAM_SHAPE].value = (float) Shaper::Shapes::FullWave; + + MeasureTime::run(overheadOutOnly, "shaper fw 16X", [&gmr]() { + gmr.inputs[Shaper::INPUT_AUDIO].value = TestBuffers::get(); + gmr.step(); + return gmr.outputs[Shaper::OUTPUT_AUDIO].value; + }, 1); +} + +static void testShaper1b() +{ + Shaper gmr; + + // gmr.setSampleRate(44100); + // gmr.init(); + gmr.params[Shaper::PARAM_SHAPE].value = (float) Shaper::Shapes::FullWave; + gmr.params[Shaper::PARAM_OVERSAMPLE].value = 1; + + MeasureTime::run(overheadOutOnly, "shaper fw 4X", [&gmr]() { + gmr.inputs[Shaper::INPUT_AUDIO].value = TestBuffers::get(); + gmr.step(); + return gmr.outputs[Shaper::OUTPUT_AUDIO].value; + }, 1); +} + +static void testSuper() +{ + Super gmr; + + + gmr.params[Shaper::PARAM_SHAPE].value = (float) Shaper::Shapes::FullWave; + gmr.params[Shaper::PARAM_OVERSAMPLE].value = 2; + + MeasureTime::run(overheadOutOnly, "super", [&gmr]() { + gmr.inputs[Shaper::INPUT_AUDIO].value = TestBuffers::get(); + gmr.step(); + return gmr.outputs[Shaper::OUTPUT_AUDIO].value; + }, 1); +} + +static void testShaper1c() +{ + Shaper gmr; + + // gmr.setSampleRate(44100); + // gmr.init(); + gmr.params[Shaper::PARAM_SHAPE].value = (float) Shaper::Shapes::FullWave; + gmr.params[Shaper::PARAM_OVERSAMPLE].value = 2; + + MeasureTime::run(overheadOutOnly, "shaper fw 1X", [&gmr]() { + gmr.inputs[Shaper::INPUT_AUDIO].value = TestBuffers::get(); + gmr.step(); + return gmr.outputs[Shaper::OUTPUT_AUDIO].value; + }, 1); +} + +// 284 +static void testShaper2() +{ + Shaper gmr; + + // gmr.setSampleRate(44100); + // gmr.init(); + gmr.params[Shaper::PARAM_SHAPE].value = (float) Shaper::Shapes::Crush; + + MeasureTime::run(overheadOutOnly, "shaper crush", [&gmr]() { + gmr.inputs[Shaper::INPUT_AUDIO].value = TestBuffers::get(); + gmr.step(); + return gmr.outputs[Shaper::OUTPUT_AUDIO].value; + }, 1); +} + +// 143 +static void testShaper3() +{ + Shaper gmr; + + // gmr.setSampleRate(44100); + // gmr.init(); + gmr.params[Shaper::PARAM_SHAPE].value = (float) Shaper::Shapes::AsymSpline; + + MeasureTime::run(overheadOutOnly, "shaper asy", [&gmr]() { + gmr.inputs[Shaper::INPUT_AUDIO].value = TestBuffers::get(); + gmr.step(); + return gmr.outputs[Shaper::OUTPUT_AUDIO].value; + }, 1); +} + +static void testShaper4() +{ + Shaper gmr; + + // gmr.setSampleRate(44100); + // gmr.init(); + gmr.params[Shaper::PARAM_SHAPE].value = (float) Shaper::Shapes::Fold; + + MeasureTime::run(overheadOutOnly, "folder", [&gmr]() { + gmr.inputs[Shaper::INPUT_AUDIO].value = TestBuffers::get(); + gmr.step(); + return gmr.outputs[Shaper::OUTPUT_AUDIO].value; + }, 1); +} + +static void testShaper5() +{ + Shaper gmr; + + // gmr.setSampleRate(44100); + // gmr.init(); + gmr.params[Shaper::PARAM_SHAPE].value = (float) Shaper::Shapes::Fold2; + + MeasureTime::run(overheadOutOnly, "folder II", [&gmr]() { + gmr.inputs[Shaper::INPUT_AUDIO].value = TestBuffers::get(); + gmr.step(); + return gmr.outputs[Shaper::OUTPUT_AUDIO].value; + }, 1); +} #if 0 static void testAttenuverters() { @@ -246,18 +714,136 @@ static void testAttenuverters() } #endif +#if 0 +static void testNoise(bool useDefault) +{ + + std::default_random_engine defaultGenerator{99}; + std::random_device rd{}; + std::mt19937 gen{rd()}; + std::normal_distribution distribution{0, 1.0}; + + std::string title = useDefault ? "default random" : "fancy random"; + + MeasureTime::run(overheadOutOnly, title.c_str(), [useDefault, &distribution, &defaultGenerator, &gen]() { + if (useDefault) return distribution(defaultGenerator); + else return distribution(gen); + }, 1); +} + + +static uint64_t xoroshiro128plus_state[2] = {}; + +static uint64_t rotl(const uint64_t x, int k) +{ + return (x << k) | (x >> (64 - k)); +} + +static uint64_t xoroshiro128plus_next(void) +{ + const uint64_t s0 = xoroshiro128plus_state[0]; + uint64_t s1 = xoroshiro128plus_state[1]; + const uint64_t result = s0 + s1; + + s1 ^= s0; + xoroshiro128plus_state[0] = rotl(s0, 55) ^ s1 ^ (s1 << 14); // a, b + xoroshiro128plus_state[1] = rotl(s1, 36); // c + + return result; +} + +float randomUniformX() +{ + // 24 bits of granularity is the best that can be done with floats while ensuring that the return value lies in [0.0, 1.0). + return (xoroshiro128plus_next() >> (64 - 24)) / powf(2, 24); +} + +float randomNormalX() +{ + // Box-Muller transform + float radius = sqrtf(-2.f * logf(1.f - randomUniformX())); + float theta = 2.f * M_PI * randomUniformX(); + return radius * sinf(theta); + + // // Central Limit Theorem + // const int n = 8; + // float sum = 0.0; + // for (int i = 0; i < n; i++) { + // sum += randomUniform(); + // } + // return (sum - n / 2.f) / sqrtf(n / 12.f); +} + +static void testNormal() +{ + MeasureTime::run(overheadOutOnly, "normal", []() { + return randomNormalX(); + }, 1); +} +#endif + void perfTest() { + printf("starting perf test\n"); + fflush(stdout); + setup(); #if 0 testAttenuverters(); testExpRange(); #endif + +#if 0 + testNoise(false); + testNoise(true); + testNormal(); +#endif + + testSuper(); + testShaper1a(); + testShaper1b(); + testShaper1c(); + testShaper2(); + testShaper3(); + testShaper4(); + testShaper5(); + + testEV3(); + testCHBdef(); + testFunSaw(true); +#if 0 + testFunSaw(false); + testFunSin(true); + testFunSin(false); + testFunSq(); + testFun(); + testFunNone(); +#endif + + // testEvenOrig(); + testEvenSaw(); +#if 0 + testEven(); + testEvenEven(); + testEvenSin(); + testEvenSaw(); + testEvenTri(); + testEvenSq(); + testEvenSqSaw(); +#endif + +#if 0 + testVocalFilter(); testAnimator(); testShifter(); testColors(); testTremolo(); + + testLFN(); + testGMR(); +#endif + // test1(); #if 0 diff --git a/plugins/community/repos/squinkylabs-plug1/test/testAnalyzer.cpp b/plugins/community/repos/squinkylabs-plug1/test/testAnalyzer.cpp new file mode 100644 index 00000000..79b4ca7d --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/test/testAnalyzer.cpp @@ -0,0 +1,101 @@ + +#include +#include + + +#include "Analyzer.h" +#include "asserts.h" +#include "AudioMath.h" + +#include "FFTData.h" +#include "SinOscillator.h" +#include "MeasureTime.h" + + +static void ana1() +{ + FFTDataCpx x(2); + x.set(0, cpx(float(AudioMath::gainFromDb(0)), 0)); + + auto data = Analyzer::getFeatures(x, 3, 44100, -100); + assert(data.size() == 1); + assertClose(data[0].gainDb, 0, .0001); + assertClose(data[0].freq, 0, .0001); +} + + +// test Analyzer::getFeatures with two bins of +// very different gains +static void ana2() +{ + FFTDataCpx x(4); + x.set(0, cpx(float(AudioMath::gainFromDb(0)), 0)); + x.set(1, cpx(float(AudioMath::gainFromDb(10)), 0)); + + auto data = Analyzer::getFeatures(x, 3, 44100, -100); + assert(data.size() == 2); + assertClose(data[0].gainDb, 0, .0001); + assertClose(data[0].freq, 0, .0001); + + assertClose(data[1].gainDb, 10, .0001); + + // bin 1 (last bin) is sr/2 ... sr + assertClose(data[1].freq, 44100 / 4, .0001); +} + +static void ana3() +{ + const int size = 1024; + std::vector buffer(size); + Analyzer::generateSweep(44100., buffer.data(), size, 20, 20000); + + auto minMax = AudioMath::getMinMax(buffer.data(), size); + assertClose(minMax.first, -1, .01); + assertClose(minMax.second, 1, .01); +} + +static void ana7() +{ + const int size = 64; + FFTDataCpx x(size); + std::function unity = [](float x) { + return x; + }; + Analyzer::getFreqResponse(x, unity); + + for (int i = 0; i < size / 2; ++i) { + assertClose(std::abs(x.get(i)), 1, .0001); + } +} + + +// test that we get a good spectrum from synchronous sin +static void testSyncSin() +{ + float desiredFreq = 500.0f; + int numSamples = 16 * 1024; + const float sampleRate = 44100.0f; + const double actualFreq = Analyzer::makeEvenPeriod(desiredFreq, sampleRate, numSamples); + + SinOscillatorParams sinParams; + SinOscillatorState sinState; + SinOscillator::setFrequency(sinParams, float(actualFreq / sampleRate)); + + FFTDataCpx spectrum(numSamples); + Analyzer::getSpectrum(spectrum, false, [&sinState, &sinParams]() { + return SinOscillator::run(sinState, sinParams); + }); + + Analyzer::assertSingleFreq(spectrum, float(actualFreq), sampleRate); +} + + + +void testAnalyzer() +{ + ana1(); + ana2(); + ana3(); + ana7(); + testSyncSin(); +} \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/test/testAudioMath.cpp b/plugins/community/repos/squinkylabs-plug1/test/testAudioMath.cpp index 5b8d1827..75a132c8 100644 --- a/plugins/community/repos/squinkylabs-plug1/test/testAudioMath.cpp +++ b/plugins/community/repos/squinkylabs-plug1/test/testAudioMath.cpp @@ -87,7 +87,7 @@ static void testScaler() static void testBipolarAudioScaler() { - AudioMath::ScaleFun f = AudioMath::makeBipolarAudioScaler(3, 4); + AudioMath::ScaleFun f = AudioMath::makeScalerWithBipolarAudioTrim(3, 4); // scale(cv, knob, trim // knob comes through only shifted @@ -107,12 +107,78 @@ static void testBipolarAudioScaler() assertEQ(f(-5, 0, -1), 4.); // trim quarter - should be audio knee - auto f2 = AudioMath::makeBipolarAudioScaler(-1, 1); + auto f2 = AudioMath::makeScalerWithBipolarAudioTrim(-1, 1); float x = f2(5, 0, .25); float y = (float) AudioMath::gainFromDb(LookupTableFactory::audioTaperKnee()); assertClose(x, y, .001); } +static void testAudioScaler() +{ + AudioMath::SimpleScaleFun f = AudioMath::makeSimpleScalerAudioTaper(3, 4); + + float kneeGain = (float) AudioMath::gainFromDb(LookupTableFactory::audioTaperKnee()); + + // knob comes through with taper + assertEQ(f(0, -5), 3.); + assertEQ(f(0, 5), 4.); + + // 1/4, is the knee + assertEQ(f(0, -2.5), 3.0f + kneeGain); + + // cv also come through, it trim up + assertEQ(f(-5, 0), 3.); + assertEQ(f(5, 0), 4.); + assertEQ(f(-2.5, 0), 3.0f + kneeGain); +} + + +static void testAudioScaler2() +{ + AudioMath::SimpleScaleFun f = AudioMath::makeSimpleScalerAudioTaper(0, 20); + + float kneeGain = (float) AudioMath::gainFromDb(LookupTableFactory::audioTaperKnee()); + + // knob comes through with taper + assertEQ(f(0, -5), 0); + assertEQ(f(0, 5), 20); + + // 1/4, is the knee + assertEQ(f(0, -2.5), 20 * kneeGain); + + // cv also come through, it trim up + assertEQ(f(-5, 0), 0); + assertEQ(f(5, 0), 20); + assertEQ(f(-2.5, 0), 20 * kneeGain); +} + + + +static void testFold() +{ + float last = 0; + const float deltaX = 0.05f; + for (float x = 0; x < 15; x += deltaX) { + float y = AudioMath::fold(x); + float absChange = std::abs(y - last); + last = y; + assertLE(absChange, deltaX + .0001f); + } +} + + +static void testFoldNegative() +{ + float last = 0; + const float deltaX = 0.05f; + for (float x = 0; x > -15; x -= deltaX) { + float y = AudioMath::fold(x); + float absChange = std::abs(y - last); + last = y; + assertLE(absChange, deltaX + .0001f); + } +} + void testAudioMath() { test0(); @@ -122,4 +188,8 @@ void testAudioMath() testAudioTaper(); testScaler(); testBipolarAudioScaler(); + testAudioScaler(); + testAudioScaler2(); + testFold(); + testFoldNegative(); } \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/test/testButterLookup.cpp b/plugins/community/repos/squinkylabs-plug1/test/testButterLookup.cpp new file mode 100644 index 00000000..1e8973b6 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/test/testButterLookup.cpp @@ -0,0 +1,101 @@ + + +#include "ButterworthLookup.h" +#include "BiquadFilter.h" +#include "BiquadState.h" +#include "Analyzer.h" +#include "FFT.h" + +#include "asserts.h" + +// test that the API does something +static void testButterLookup0() +{ + ButterworthLookup4PHP b; + BiquadParams params; + for (int i = 0; i < 10; ++i) { + params.setAtIndex(0, i); + } + b.get(params, .1f); + for (int i = 0; i < 10; ++i) { + assert(params.getAtIndex(i) != 0); + } +} + +const float sampleRate = 44100; +static void doHighTest(std::function filter, float Fc, float expectedSlope) +{ + const int numSamples = 16 * 1024; + FFTDataCpx response(numSamples); + Analyzer::getFreqResponse(response, filter); + + auto x = Analyzer::getMaxAndShoulders(response, -3); + printf("max and shoulders return %f, %f, %f\n", + FFT::bin2Freq(std::get<0>(x), sampleRate, numSamples), + FFT::bin2Freq(std::get<1>(x), sampleRate, numSamples), + FFT::bin2Freq(std::get<2>(x), sampleRate, numSamples) + + ); + + const double cutoff = FFT::bin2Freq(std::get<2>(x), sampleRate, numSamples); + + const float highestFreq = sampleRate / 2; + const int highestBin = numSamples / 2; + printf("highest freq = %f get0=%d get1=%d\n", highestFreq, std::get<0>(x), std::get<1>(x)); + /* + test1 + + max and shoulders return 99.591064, 21463.220215, -2.691650 + highest freq = 22050.000000 get0=37 get1=7974 + assertClose failed actual value =999.83 expected=1 + Assertion failed: false, file d:\vcvrack\x6\plugins\squinkyvcv\test\testbutterlookup.cpp, line 54 + + * 0 = low freq bin # + * 1 = peak bin # + * 2 = high bin# +*/ + + + fflush(stdout); + // Is the peak at zero? i.e. no ripple. + if (std::get<1>(x) == highestFreq) { + assertEQ(std::get<0>(x), -1); // no LF shoulder + } else { + // Some higher order filters have a tinny bit of ripple. + // make sure that the peak is about the same as the extreme (in this case nyquist + float peakMag = std::abs(response.get(std::get<1>(x))); + float maxMag = std::abs(response.get(highestBin)); + assertClose(peakMag / maxMag, 1, .01); + } + + assertClose(cutoff, Fc, 3); // 3db down at Fc + + double slope = Analyzer::getSlope(response, (float) Fc * 2, sampleRate); + assertClose(slope, expectedSlope, 1); // to get accurate we need to go to higher freq, etc... this is fine +} + +static void doHighTest(float fC) +{ + ButterworthLookup4PHP b; + BiquadParams params; + BiquadState state; + b.get(params, fC / sampleRate); + + auto filter = [&state, ¶ms](float x) { + return BiquadFilter::run(x, state, params); + }; + doHighTest(filter, fC, 24); + +} + + +static void testButterLookup1() +{ + doHighTest(100); +} + +void testButterLookup() +{ + testButterLookup0(); + testButterLookup1(); +} \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/test/testColoredNoise.cpp b/plugins/community/repos/squinkylabs-plug1/test/testColoredNoise.cpp index 50ffede8..81b65ecc 100644 --- a/plugins/community/repos/squinkylabs-plug1/test/testColoredNoise.cpp +++ b/plugins/community/repos/squinkylabs-plug1/test/testColoredNoise.cpp @@ -37,7 +37,7 @@ static void test1() for (bool done = false; !done; ) { cn.step(); const float output = cn.outputs[Noise::AUDIO_OUTPUT].value; - if (output > .1) {; + if (output > .1) { started = true; } assert(output < 10); @@ -80,7 +80,7 @@ static void test2() while (cn._msgCount() < 6) { cn.step(); } - cn.params[Noise::SLOPE_PARAM].value = -1.2f; + cn.params[Noise::SLOPE_PARAM].value = -1.2f; while (cn._msgCount() < 7) { cn.step(); } diff --git a/plugins/community/repos/squinkylabs-plug1/test/testDelay.cpp b/plugins/community/repos/squinkylabs-plug1/test/testDelay.cpp new file mode 100644 index 00000000..40d2541a --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/test/testDelay.cpp @@ -0,0 +1,169 @@ + + +#include "FractionalDelay.h" +#include "asserts.h" + + +// test that we can set up and get zero output +static void test0() +{ + FractionalDelay f(100); + f.setDelay(50); + float x = f.run(1); + assertEQ(x, 0); +} + +// test that the output is perhaps a delayed version of the input +static void test1() +{ + printf("test1\n\n"); + FractionalDelay f(100); + f.setDelay(10); + float x = 0; + for (int i = 0; i < 20; ++i) { + x = f.run(1); + } + assertEQ(x, 1); +} + +static void testTime(float delayTime, float expectedValue, float tolerance) +{ + int iTime = int(delayTime); + if ((delayTime - iTime) > .0000000001) { + iTime++; + } + + FractionalDelay f(100); + f.setDelay(delayTime); + float x = 0; + float lastX = 0; + for (int i = 0; i < 20; ++i) { + x = f.run(1); + // printf("in loop i=%d, x=%f\n", i, x); + if (x > .99f) { + assertEQ(i, iTime); // delay amount should be >= 10 < 11; + assertClose(lastX, expectedValue, tolerance); + return; + } + + lastX = x; + } + assert(false); // we should have exited already +} + +static void test2() +{ + testTime(10.0f, 0, .0001f); +} + + +static void test3() +{ + testTime(10.5f, 0.5f, .0001f); +} + +static void test4() +{ + testTime(10.25f, 0.75f, .1f); +} + + +static void test5() +{ + // MAKE THIS WORK + // testTime(10.05f, 0, .1f); +} + + +static void test6() +{ + testTime(10.75f, .25, .05f); +} + +static void test10() +{ + RecirculatingFractionalDelay d(1000); + d.setDelay(100); + assertEQ(d.run(0), 0); +} + + + +static void test11() +{ + RecirculatingFractionalDelay d(1000); + d.setDelay(100); + for (int i = 0; ; ++i) { + assertLT(i, 110); // too far with no sound + float x = d.run(1); + if (x > .5) { + return; //we found sound! + } + + } +} + +static void testRecirc(float delayTime, float feedback, float expectedDuration, bool expectDurationLonger) +{ + RecirculatingFractionalDelay d(1000); + d.setDelay(delayTime); + d.setFeedback(feedback); + + const int maxDur = 10000; + assert(expectedDuration < maxDur); + + // feed a blast into it + for (int i = 0; i < 10; ++i) { + d.run(1); + } + + int lastSound = 0; + for (int i = 0; ; ++i) { + if (i > maxDur) { + // we have hit end of test + // If we expect infinit, that's ok + if (!expectDurationLonger) { + assertLT(lastSound, expectedDuration); + } else { + assertGT(lastSound, expectedDuration); + } + break; + } + float x = d.run(0); + if (x > .1) { + lastSound = i; + } + if (lastSound > expectedDuration && !expectDurationLonger) { + assert(false); + } + + } +} + +static void test12() +{ + testRecirc(20, 0, 22, false); +} + + +static void test13() +{ + testRecirc(20, .9f, 100, true); +} + +void testDelay() +{ + test0(); + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); + + test10(); + test11(); + test12(); + + test13(); +} \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/test/testFFT.cpp b/plugins/community/repos/squinkylabs-plug1/test/testFFT.cpp index 636b63b6..6e0d180c 100644 --- a/plugins/community/repos/squinkylabs-plug1/test/testFFT.cpp +++ b/plugins/community/repos/squinkylabs-plug1/test/testFFT.cpp @@ -68,7 +68,7 @@ static void test3() for (int i = 0; i < 16; ++i) { cpx v = complex.get(i); float mag = std::abs(v); - + float expect = (i == 1) ? 1.f : 0.f; assertClose(mag, expect, .0001); } @@ -91,16 +91,16 @@ static void testRoundTrip() b = FFT::inverse(&realOut, complex); for (int i = 0; i < 16; ++i) { - + float expect = 1.f; // scaled DC (TODO: fix scaling) - assertEQ(realOut.get(i) , expect); + assertEQ(realOut.get(i), expect); } } static void testNoiseFormula() { - const int bins = 64 * 1024 ; + const int bins = 64 * 1024; std::unique_ptr data(new FFTDataCpx(bins)); assertEQ(data->size(), bins); @@ -114,7 +114,7 @@ static void testNoiseFormula() float phase = std::arg(x); const float expectedMag = (i == 0) ? 0.f : (i < (bins / 2)) ? 1.f : 0.f; - + assertClose(mag, expectedMag, .0001); phases.insert(phase); } @@ -138,7 +138,7 @@ static void testWhiteNoiseRT() std::unique_ptr noiseSpectrum2(new FFTDataCpx(bins)); for (int i = 0; i < bins; ++i) { - cpx x(0,0); + cpx x(0, 0); noiseSpectrum2->set(i, x); } @@ -147,14 +147,14 @@ static void testWhiteNoiseRT() FFT::inverse(noiseRealSignal.get(), *noiseSpectrum); FFT::forward(noiseSpectrum2.get(), *noiseRealSignal); - + float totalPhase = 0; float minPhase = 0; float maxPhase = 0; - for (int i = 0; i < bins/2; ++i) { + for (int i = 0; i < bins / 2; ++i) { float expected = (i == 0) ? 0.f : 1.f; cpx data = noiseSpectrum2->get(i); - + assertClose(std::abs(data), expected, .0001); const float phase = std::arg(data); totalPhase += phase; @@ -176,12 +176,11 @@ static void testNoiseRTSub(int bins) FFT::makeNoiseSpectrum(dataCpx.get(), ColoredNoiseSpec()); FFT::inverse(dataReal.get(), *dataCpx); - FFT::normalize(dataReal.get()); + FFT::normalize(dataReal.get(), 2); const float peak = getPeak(*dataReal); - assertClose( peak, 1.0f , .001); - + assertClose(peak, 2.0f, .001); } static void testNoiseRT() @@ -196,7 +195,7 @@ static void testNoiseRT() static void testPinkNoise() { - const int bins = 1024*4; + const int bins = 1024 * 4; std::unique_ptr data(new FFTDataCpx(bins)); assertEQ(data->size(), bins); @@ -211,8 +210,8 @@ static void testPinkNoise() // pick a starting bin above our 40 hz low freq corner const int baseBin = 16; //float freqBase = 44100 * baseBin / (float) bins; - const float freqBase = FFT::bin2Freq(baseBin, 44100, bins); - assertGT (freqBase, 80); + const double freqBase = FFT::bin2Freq(baseBin, 44100, bins); + assertGT(freqBase, 80); // mid-band, quadruple freq should reduce amp by 6db float mag16 = std::abs(data->get(baseBin)); @@ -236,11 +235,11 @@ static void testPinkNoise() static void testBlueNoise(float corner = 0) { - const int bins = 1024 * 4; + const int bins = 1024 * 4; std::unique_ptr data(new FFTDataCpx(bins)); assertEQ(data->size(), bins); - ColoredNoiseSpec spec; + ColoredNoiseSpec spec; spec.slope = 3; spec.sampleRate = 44100; diff --git a/plugins/community/repos/squinkylabs-plug1/test/testFilter.cpp b/plugins/community/repos/squinkylabs-plug1/test/testFilter.cpp new file mode 100644 index 00000000..f5137244 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/test/testFilter.cpp @@ -0,0 +1,100 @@ + +#include + +#include "FFT.h" +#include "GraphicEq.h" +#include "StateVariableFilter.h" +#include "SinOscillator.h" +#include "Analyzer.h" +#include "asserts.h" + +static void testPeak(std::function filter, float sampleRate, float expectedMax, float percentTolerance) +{ + + const int numSamples = 16 * 1024; + FFTDataCpx x(numSamples); + Analyzer::getFreqResponse(x, filter); + + int maxBin = Analyzer::getMax(x); + double maxFreq = (FFT::bin2Freq(maxBin, 44100, numSamples) + + FFT::bin2Freq(maxBin + 1, 44100, numSamples)) / 2; + + const float delta = expectedMax * percentTolerance / 100.f; // One percent accuracy + + // printf("expected: %f actual %f\n", expectedMax, maxFreq); + + assertClose(maxFreq, expectedMax, delta); +} + +template +static void testBandpass(float Fc, float tolerancePercent) +{ + StateVariableFilterState state; + StateVariableFilterParams params; + + const float sampleRate = 44100; + + params.setMode(StateVariableFilterParams::Mode::BandPass); + params.setFreq(Fc / sampleRate); + params.setQ(3); + + std::function filter = [&state, ¶ms](float x) { + auto y = StateVariableFilter::run(x, state, params); + return float(y); + }; + testPeak(filter, sampleRate, Fc, tolerancePercent); +} + +template +static void test1() +{ + testBandpass(10, 50); // coarse FFT resolution + testBandpass(100, 2); + testBandpass(1000, 3); + testBandpass(3000, 12); + testBandpass(6000, 35); // This filter just gets inaccurate at high freq - don't know why +} + +static void zero(GraphicEq2<5>& geq) +{ + for (int i = 0; i < 5; ++i) { + geq.setGain(i, 0); + } +} + +static void test2() +{ + const float sampleRate = 44100; + const int stages = 5; + GraphicEq2 geq; + + zero(geq); + geq.setGain(0, 1); + std::function filter = [&geq](float x) { + return geq.run(x); + }; + testPeak(filter, sampleRate, 100, 2); + + zero(geq); + geq.setGain(1, 1); + testPeak(filter, sampleRate, 200, 2); + + zero(geq); + geq.setGain(2, 1); + testPeak(filter, sampleRate, 400, 2); + + zero(geq); + geq.setGain(3, 1); + testPeak(filter, sampleRate, 800, 4); + + zero(geq); + geq.setGain(4, 1); + testPeak(filter, sampleRate, 1600, 10); +} + + +void testFilter() +{ + test1(); + test2(); +} \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/test/testFilterDesign.cpp b/plugins/community/repos/squinkylabs-plug1/test/testFilterDesign.cpp new file mode 100644 index 00000000..3e407d5b --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/test/testFilterDesign.cpp @@ -0,0 +1,250 @@ + +#include "Analyzer.h" +#include "asserts.h" +#include "BiquadFilter.h" +#include "BiquadParams.h" +#include "BiquadState.h" +#include "ButterworthFilterDesigner.h" +#include "ObjectCache.h" +//#include "FFT.h" + +#include + + +//std::function filter +const float sampleRate = 44100; + +class LowpassStats +{ +public: + double passBandStop = 0; // -3db point in passband + double stopBandStart = 0; // within 3db of stopBandAtten + double stopBandAttenuation = 0; +}; + + +static LowpassStats characterizeFilter(bool isLowpass, std::function filter, float stopBandGain = -200) +{ + LowpassStats ret; + bool inPassBand = true; + bool inStopBand = false; + + const int numSamples = 16 * 1024; + FFTDataCpx response(numSamples); + Analyzer::getFreqResponse(response, filter); + + const int passbandBin = isLowpass ? 0 : (numSamples/2) - 1; + const float gain0 = std::abs(response.get(passbandBin)); + + const double _3DbDown = AudioMath::db(gain0) - 3; + //printf("bin=%d gain=%f\n", passbandBin, gain0); + + assert(gain0 > .1); // sanity check + + ret.stopBandAttenuation = -1000; + float minDb = 100; + + const int binInc = isLowpass ? 1 : -1; + + for (int i = passbandBin; ; i += binInc) { + if (i < 0 || i >numSamples / 2) { + return ret; + } + const float db = (float) AudioMath::db(std::abs(response.get(i))); + const double freq = FFT::bin2Freq(i, sampleRate, numSamples); + + if (inPassBand && db < _3DbDown) { + ret.passBandStop = freq; + inPassBand = false; + } + // if we bounce back up from min, we are in stop band. + // only works for Ch I and elliptic + if (!inPassBand && !inStopBand && (db > (minDb + 3))) { + inStopBand = true; + ret.stopBandStart = freq; + } + // If we are below something passed in, we are in stop. + if (!inPassBand && !inStopBand && (db < stopBandGain)) { + inStopBand = true; + ret.stopBandStart = freq; + ret.stopBandAttenuation = db; + } + minDb = std::min(minDb, db); + if (inStopBand) { + ret.stopBandAttenuation = std::max(ret.stopBandAttenuation, db); + } + } + + return ret; + +} + +static LowpassStats characterizeHighpassFilter(std::function filter, float stopBandGain = -200) +{ + return characterizeFilter(false, filter, stopBandGain); +} + +static LowpassStats characterizeLowpassFilter(std::function filter, float stopBandGain = -200) +{ + return characterizeFilter(true, filter, stopBandGain); +} + +static void printStats(const char * label, const LowpassStats& stats) +{ + printf("%s band=%.2f,%.2f atten=%.2f\n", label, stats.passBandStop, stats.stopBandStart, stats.stopBandAttenuation); +} + +static void testButter4Hi() +{ + const float Fc = 100; + BiquadParams params; + BiquadState state; + + ButterworthFilterDesigner::designFourPoleHighpass( + params, Fc / sampleRate); + + std::function filter = [&state, ¶ms](float x) { + x = (float) BiquadFilter::run(x, state, params); + return x; + }; + + const LowpassStats stats = characterizeHighpassFilter(filter, -60); + assertClose(stats.stopBandStart, 100 / 317, 20); + assertClose(stats.passBandStop, 100, 5); +#ifdef _LOG + printStats("butter6/100", stats); +#endif +} + + + +static void testButter6() +{ + const float Fc = 100; + BiquadParams params; + BiquadState state; + + ButterworthFilterDesigner::designSixPoleLowpass( + params, Fc / sampleRate); + + std::function filter = [&state, ¶ms](float x) { + x = (float) BiquadFilter::run(x, state, params); + return x; + }; + + const LowpassStats stats = characterizeLowpassFilter(filter, -60); + assertClose(stats.stopBandStart, 317, 20); + assertClose(stats.passBandStop, 100, 5); +#ifdef _LOG + printStats("butter6/100", stats); +#endif +} + +static void testButter6Obj() +{ + const float fc = sampleRate / 64; + // BiquadParams params; + auto params = ObjectCache::get6PLPParams(1.f / 64.f); + BiquadState state; + + // ButterworthFilterDesigner::designSixPoleLowpass( + // params, Fc / sampleRate); + + std::function filter = [&state, ¶ms](float x) { + x = (float) BiquadFilter::run(x, state, *params); + return x; + }; + + + const LowpassStats stats = characterizeLowpassFilter(filter, -60); + assertClose(stats.stopBandStart, fc * 3.17, 20); + assertClose(stats.passBandStop, fc, 5); +#ifdef _LOG + printStats("butter6/obj64", stats); +#endif +} + +static void testButter8() +{ + const float Fc = 100; + BiquadParams params; + BiquadState state; + + ButterworthFilterDesigner::designEightPoleLowpass( + params, Fc / sampleRate); + + std::function filter = [&state, ¶ms](float x) { + x = (float) BiquadFilter::run(x, state, params); + return x; + }; + + const LowpassStats stats = characterizeLowpassFilter(filter, -60); + assertClose(stats.stopBandStart, Fc * 2.5, 20); + assertClose(stats.passBandStop, Fc, 5); +#ifdef _LOG + printStats("butter8/100", stats); +#endif +} + +static void designSixPoleCheby(BiquadParams& outParams, float frequency, float ripple) +{ + assert(frequency > 0 && frequency < .5); + using Filter = Dsp::ChebyILowPass<6, 1>; + Filter f; + f.SetupAs(frequency, ripple); + assert(f.GetStageCount() == 3); + BiquadFilter::fillFromStages(outParams, f.Stages(), f.GetStageCount()); +} + +static void testCheby6_1() +{ + const float Fc = 100; + BiquadParams params; + BiquadState state; + + designSixPoleCheby(params, Fc / sampleRate, 6); + + std::function filter = [&state, ¶ms](float x) { + x = (float) BiquadFilter::run(x, state, params); + return x; + }; + + const LowpassStats stats = characterizeLowpassFilter(filter, -60); + assertClose(stats.stopBandStart, Fc * 1.6, 20); + assertClose(stats.passBandStop, Fc, 5); +#ifdef _LOG + printStats("cheby6/100/6", stats); +#endif +} + +static void testCheby6_3() +{ + const float Fc = 100; + BiquadParams params; + BiquadState state; + + designSixPoleCheby(params, Fc / sampleRate, 3); + + std::function filter = [&state, ¶ms](float x) { + x = (float) BiquadFilter::run(x, state, params); + return x; + }; + + const LowpassStats stats = characterizeLowpassFilter(filter, -60); + assertClose(stats.stopBandStart, Fc * 1.9, 20); + assertClose(stats.passBandStop, Fc, 5); +#ifdef _LOG + printStats("cheby6/100/3", stats); +#endif +} + +void testFilterDesign() +{ + + testButter6(); + testButter4Hi(); + testButter6Obj(); + testButter8(); + testCheby6_1(); + testCheby6_3(); +} \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/test/testGMR.cpp b/plugins/community/repos/squinkylabs-plug1/test/testGMR.cpp new file mode 100644 index 00000000..0c7ba924 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/test/testGMR.cpp @@ -0,0 +1,45 @@ + +#include "asserts.h" +#include "GMR.h" +#include "TestComposite.h" + +#include + + +using G = GMR; + + +// test that we get triggers out +static void test0() +{ + G gmr; + std::set data; + + gmr.setSampleRate(44100); + gmr.init(); + + for (int i = 0; i < 10; ++i) { + + gmr.inputs[G::CLOCK_INPUT].value = 0; + for (int i = 0; i < 100; ++i) { + gmr.step(); + float out = gmr.outputs[G::TRIGGER_OUTPUT].value; + data.insert(out); + } + gmr.inputs[G::CLOCK_INPUT].value = 10; + for (int i = 0; i < 100; ++i) { + gmr.step(); + float out = gmr.outputs[G::TRIGGER_OUTPUT].value; + data.insert(out); + } + } + + assert(data.find(cGateOutHi) != data.end()); + assert(data.find(cGateOutLow) != data.end()); + assertEQ(data.size(), 2); +} + +void testGMR() +{ + test0(); +} \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/test/testGateTrigger.cpp b/plugins/community/repos/squinkylabs-plug1/test/testGateTrigger.cpp index ef4e7604..bfefa476 100644 --- a/plugins/community/repos/squinkylabs-plug1/test/testGateTrigger.cpp +++ b/plugins/community/repos/squinkylabs-plug1/test/testGateTrigger.cpp @@ -1,7 +1,10 @@ -#include +#include "asserts.h" #include "GateTrigger.h" #include "SchmidtTrigger.h" +#include "TriggerOutput.h" + +#include static void sc0() { @@ -10,7 +13,6 @@ static void sc0() assert(!b); } - static void sc1() { SchmidtTrigger sc(-10, 10); @@ -56,11 +58,10 @@ static void sc3() assert(b); } - // check that threshold accessors are sane void g_1() { - GateTrigger g; + GateTrigger g(true); assert(g.thlo() > 0); assert(g.thhi() > g.thlo()); @@ -68,7 +69,6 @@ void g_1() assert(g.thhi() > 1.f); } - void testAfterReset(GateTrigger& g) { g.go(10.f); // right after "reset", start with gate @@ -86,7 +86,7 @@ void testAfterReset(GateTrigger& g) void grst1() { - GateTrigger g; + GateTrigger g(true); testAfterReset(g); g.go(10.f); @@ -94,6 +94,40 @@ void grst1() testAfterReset(g); } +void to0() +{ + TriggerOutput t; + + // const int ghigh = DACVoltage::xcodeForGateHi(); + t.go(false); + assert(t.get() == 0); + + t.go(true); + assert(t.get() == cGateOutHi); + + int duration = 0; + for (int i = 0; i < 100000; ++i) { + t.go(true); + if (t.get() == 0) + break; + ++duration; + } + + const int micros = (duration * 1000 * 1000) / 44100; // TODO: rationalize + //printf("duration = %d micros = %d\n", duration, micros); + assert(micros > 4900); + assert(micros < 5100); +} + +// test that we work immediately on startup - no reset delay +void to1() +{ + TriggerOutput t; + t.go(true); + assertEQ(t.get(), cGateOutHi); +} + + void testGateTrigger() @@ -104,4 +138,6 @@ void testGateTrigger() sc3(); g_1(); grst1(); + to0(); + to1(); } diff --git a/plugins/community/repos/squinkylabs-plug1/test/testLookupTable.cpp b/plugins/community/repos/squinkylabs-plug1/test/testLookupTable.cpp index cb26abb6..535d49bd 100644 --- a/plugins/community/repos/squinkylabs-plug1/test/testLookupTable.cpp +++ b/plugins/community/repos/squinkylabs-plug1/test/testLookupTable.cpp @@ -6,6 +6,7 @@ #include "AudioMath.h" #include "LookupTable.h" #include "LookupTableFactory.h" +#include "ObjectCache.h" using namespace std; @@ -164,8 +165,8 @@ static void testExpSimpleLookup() LookupTableParams lookup; LookupTableFactory::makeExp2(lookup); - const double xMin = LookupTableFactory::expXMin(); - const double xMax = LookupTableFactory::expXMax(); + const double xMin = LookupTableFactory::exp2XMin(); + const double xMax = LookupTableFactory::exp2XMax(); assert(5 > xMin); assert(11 < xMax); assertClose(LookupTable::lookup(lookup, 5), std::pow(2, 5), .01); @@ -193,19 +194,110 @@ static void testExpRange() template static void testExpTolerance(T centsTolerance) { - const T xMin = (T) LookupTableFactory::expXMin(); - const T xMax = (T) LookupTableFactory::expXMax(); + const T xMin = (T) LookupTableFactory::exp2XMin(); + const T xMax = (T) LookupTableFactory::exp2XMax(); LookupTableParams table; LookupTableFactory::makeExp2(table); - for (T x = xMin; x <= xMax; x += T(.0001)) { + for (T x = xMin; x <= xMax; x += T(.001)) { T y = LookupTable::lookup(table, x); // and back double accurate = std::pow(2.0, x); - double errorCents = std::abs(1200.0 * std::log2(y / accurate)); + double errorCents = AudioMath::acents(y, accurate); assertClose(errorCents, 0, centsTolerance); } } + +template +static void testExpTolerance2(T centsTolerance, T hzTolerance) +{ + const T xMin = (T) LookupTableFactory::exp2XMin(); + const T xMax = (T) LookupTableFactory::exp2XMax(); + + LookupTableParams table; + LookupTableFactory::makeExp2(table); + for (T x = xMin; x <= xMax; x += T(.001)) { + T y = LookupTable::lookup(table, x); // and back + double accurate = std::pow(2.0, x); + double errorCents = AudioMath::acents(y, accurate); + assertClose(errorCents, 0, centsTolerance); + const double errorHz = std::abs(y - accurate); + assertClose(errorHz, 0, hzTolerance); + } +} + +template +static void testExp2HiTolerance(T centsTolerance, T hzTolerance) +{ + + const T xMin = (T) LookupTableFactory::exp2ExHighXMin(); + const T xMax = (T) LookupTableFactory::exp2ExHighXMax(); + + LookupTableParams table; + LookupTableFactory::makeExp2ExHigh(table); + + for (T x = xMin; x <= xMax; x += T(.001)) { + T y = LookupTable::lookup(table, x); // and back + double accurate = std::pow(2.0, x); + double errorCents = AudioMath::acents(y, accurate); + assertClose(errorCents, 0, centsTolerance); + const double errorHz = std::abs(y - accurate); + + // relax limits at high freq + T curHzTol = hzTolerance; + if (y > 10000) curHzTol *= 10; + if (y > 5000) curHzTol *= 4; + else if (y > 3000) curHzTol *= 2; + if (y > 2000) curHzTol *= 1.1f; + + assertClose(errorHz, 0, curHzTol); + } +} + +template +static void testExp2LowTolerance(T centsTolerance, T hzTolerance) +{ + const T xMin = (T) LookupTableFactory::exp2ExLowXMin(); + const T xMax = (T) LookupTableFactory::exp2ExLowXMax(); + + LookupTableParams table; + LookupTableFactory::makeExp2ExLow(table); + + for (T x = xMin; x <= xMax; x += T(.001)) { + T y = LookupTable::lookup(table, x); // and back + double accurate = std::pow(2.0, x); + double errorCents = AudioMath::acents(y, accurate); + assertClose(errorCents, 0, centsTolerance); + const double errorHz = std::abs(y - accurate); + assertClose(errorHz, 0, hzTolerance); + } +} + +template +static void testExp2CombinedTolerance(T centsTolerance, T hzTolerance) +{ + const T xMin = (T) LookupTableFactory::exp2ExLowXMin(); + const T xMax = (T) LookupTableFactory::exp2ExHighXMax(); + + // LookupTableParams table; + auto exp = ObjectCache::getExp2Ex(); + for (T x = xMin; x <= xMax; x += T(.001)) { + T y = exp(x); + double accurate = std::pow(2.0, x); + double errorCents = AudioMath::acents(y, accurate); + assertClose(errorCents, 0, centsTolerance); + const double errorHz = std::abs(y - accurate); + + T curHzTol = hzTolerance; + if (y > 10000) curHzTol *= 10; + if (y > 5000) curHzTol *= 4; + else if (y > 3000) curHzTol *= 2; + if (y > 2000) curHzTol *= 1.1f; + + assertClose(errorHz, 0, curHzTol); + } +} + template static void testBipolarSimpleLookup() { @@ -218,6 +310,16 @@ static void testBipolarSimpleLookup() } +template +static void testAudioTaperSimpleLookup() +{ + LookupTableParams lookup; + LookupTableFactory::makeAudioTaper(lookup); + + assertClose(LookupTable::lookup(lookup, 0), 0, .01); + assertClose(LookupTable::lookup(lookup, 1), 1, .01); +} + template static void testBipolarTolerance() { @@ -245,6 +347,24 @@ static void testBipolarTolerance() } } + +template +static void testAudioTaperTolerance() +{ + LookupTableParams lookup; + LookupTableFactory::makeAudioTaper(lookup); + const double toleratedError = 1 - AudioMath::gainFromDb(-.1);// let's go for one db. + assert(toleratedError > 0); + + auto refFuncPos = AudioMath::makeFunc_AudioTaper(LookupTableFactory::audioTaperKnee()); + + for (double x = 0; x < 1; x += .001) { + const T test = LookupTable::lookup(lookup, (T) x); + T ref = (T) refFuncPos(x); + assertClose(test, ref, toleratedError); + } +} + template static void test() { @@ -256,15 +376,22 @@ static void test() testDiscrete1(); testDiscrete2(); testExpSimpleLookup(); - testExpTolerance(100); // 1 semitone - testExpTolerance(10); - testExpTolerance(1); + + testExpTolerance(1); // 1 cent + + testExp2HiTolerance(.125, T(.05)); + testExp2LowTolerance(.125, T(.05)); + testExp2CombinedTolerance(.125, T(.5)); + testBipolarSimpleLookup(); testBipolarTolerance(); + testAudioTaperSimpleLookup(); + testAudioTaperTolerance(); } void testLookupTable() { - test(); + test(); + test(); } \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/test/testLowpassFilter.cpp b/plugins/community/repos/squinkylabs-plug1/test/testLowpassFilter.cpp new file mode 100644 index 00000000..d55607f8 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/test/testLowpassFilter.cpp @@ -0,0 +1,253 @@ + +#include "LowpassFilter.h" +#include "Decimator.h" +#include "Analyzer.h" +#include "asserts.h" +#include "LFN.h" +#include "TestComposite.h" + +template +static void test0() +{ + LowpassFilterState state; + LowpassFilterParams params; + LowpassFilter::setCutoff(params, T(.1)); + LowpassFilter::run(0, state, params); +} + +const float sampleRate = 44100; +template +static void doLowpassTest(std::function filter, T Fc, T expectedSlope) +{ + const int numSamples = 16 * 1024; + FFTDataCpx response(numSamples); + Analyzer::getFreqResponse(response, filter); + + auto x = Analyzer::getMaxAndShoulders(response, -3); + + const double cutoff = FFT::bin2Freq(std::get<2>(x), sampleRate, numSamples); + + // Is the peak at zero? i.e. no ripple. + if (std::get<1>(x) == 0) { + assertEQ(std::get<0>(x), -1); // no LF shoulder + } else { + // Some higher order filters have a tinny bit of ripple + float peakMag = std::abs(response.get(std::get<1>(x))); + float zeroMag = std::abs(response.get(0)); + assertClose(peakMag / zeroMag, 1, .0001); + } + + assertClose(cutoff, Fc, 3); // 3db down at Fc + + double slope = Analyzer::getSlope(response, (float) Fc * 2, sampleRate); + assertClose(slope, expectedSlope, 1); // to get accurate we need to go to higher freq, etc... this is fine +} + + +template +static void test1() +{ + const float Fc = 100; + + LowpassFilterState state; + LowpassFilterParams params; + LowpassFilter::setCutoff(params, Fc / sampleRate); + + std::function filter = [&state, ¶ms](float x) { + auto y = LowpassFilter::run(x, state, params); + return float(y); + }; + doLowpassTest(filter, Fc, -6); +} + +template +static void test2() +{ + const float Fc = 100; + BiquadParams params; + BiquadState state; + + ButterworthFilterDesigner::designTwoPoleLowpass( + params, Fc / sampleRate); + + std::function filter = [&state, ¶ms](float x) { + x = (float) BiquadFilter::run(x, state, params); + return x; + }; + doLowpassTest(filter, Fc, -12); +} + +template +static void test3() +{ + const float Fc = 100; + BiquadParams params; + BiquadState state; + + ButterworthFilterDesigner::designThreePoleLowpass( + params, Fc / sampleRate); + + std::function filter = [&state, ¶ms](float x) { + x = (float) BiquadFilter::run(x, state, params); + return x; + }; + doLowpassTest(filter, Fc, -18); +} + +template +static void test4() +{ + const float Fc = 100; + BiquadParams params; + BiquadState state; + + ButterworthFilterDesigner::designFourPoleLowpass( + params, Fc / sampleRate); + + std::function filter = [&state, ¶ms](float x) { + x = (float) BiquadFilter::run(x, state, params); + return x; + }; + doLowpassTest(filter, Fc, -24); +} +template +static void test5() +{ + const float Fc = 100; + BiquadParams params; + BiquadState state; + + ButterworthFilterDesigner::designFivePoleLowpass( + params, Fc / sampleRate); + + std::function filter = [&state, ¶ms](float x) { + x = (float) BiquadFilter::run(x, state, params); + return x; + }; + doLowpassTest(filter, Fc, -30); +} + +template +static void test6() +{ + const float Fc = 100; + BiquadParams params; + BiquadState state; + + ButterworthFilterDesigner::designSixPoleLowpass( + params, Fc / sampleRate); + + std::function filter = [&state, ¶ms](float x) { + x = (float) BiquadFilter::run(x, state, params); + return x; + }; + doLowpassTest(filter, Fc, -36); +} + +/******************************************************************************************************/ + +#if 0 // not ready for prime time +template +static void doEllipticTest(std::function filter, T Fc, T expectedSlope) +{ + const int numSamples = 16 * 1024; + //const int numSamples = 1024; + FFTDataCpx response(numSamples); + Analyzer::getFreqResponse(response, filter); + + auto x = Analyzer::getMaxAndShoulders(response, -3); + + const float cutoff = FFT::bin2Freq(std::get<2>(x), sampleRate, numSamples); + + + // Some higher order filters have a tinny bit of ripple + float peakMag = std::abs(response.get(std::get<1>(x))); + float zeroMag = std::abs(response.get(0)); + printf("mag at zero hz = %f, peak mag = %f, -3db at %f\n ", + zeroMag, peakMag, cutoff); + + Analyzer::getAndPrintFeatures(response, 1, sampleRate); + for (int i = 0; i < numSamples; ++i) { + + } + //assertClose(peakMag / zeroMag, 1, .0001); + + + // assertClose(cutoff, Fc, 3); // 3db down at Fc + + + double slope = Analyzer::getSlope(response, (float) Fc * 2, sampleRate); + // assertClose(slope, expectedSlope, 1); // to get accurate we need to go to higher freq, etc... this is fine + +} + +template +static void testElip1() +{ + const float Fc = 100; + BiquadParams params; + BiquadState state; + + T rippleDb = 3; + T attenDb = 100000; + T ripple = (T) AudioMath::gainFromDb(1); + ButterworthFilterDesigner::designEightPoleElliptic(params, Fc / sampleRate, rippleDb, attenDb); + + std::function filter = [&state, ¶ms](float x) { + x = (float) BiquadFilter::run(x, state, params); + return x; + }; + doEllipticTest(filter, Fc, -36); + +} +#endif + +template +void _testLowpassFilter() +{ + test0(); + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); +} + +/************ also test decimator here + */ + +static void decimate0() +{ + Decimator d; + d.setDecimationRate(2); + d.acceptData(5); + bool b = true; + auto x = d.clock(b); + assert(!b); + assertEQ(x, 5); +} + + +static void decimate1() +{ + Decimator d; + d.setDecimationRate(2); + d.acceptData(5); + bool b = true; + auto x = d.clock(b); + assert(!b); + assertEQ(x, 5); + + x = d.clock(b); + assert(b); + assertEQ(x, 5); +} + +void testLowpassFilter() +{ + _testLowpassFilter(); + _testLowpassFilter(); + decimate0(); + decimate1(); +} \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/test/testMinBLEPVCO.cpp b/plugins/community/repos/squinkylabs-plug1/test/testMinBLEPVCO.cpp new file mode 100644 index 00000000..2f2ebb53 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/test/testMinBLEPVCO.cpp @@ -0,0 +1,380 @@ +#include + +#if !defined(M_PI) +#define M_PI 3.14159265358979323846264338327950288 +#endif + +#include "asserts.h" +#include "EV3.h" + + + +#include "MinBLEPVCO.h" +#include "TestComposite.h" + +static float sampleTime = 1.0f / 44100.0f; + + +class TestMB +{ +public: + //static void setAllWaveforms(MinBLEPVCO* vco); + // static void test1(); + static void testSync2(); + static void testSync3(); + static void setPitch(EV3& ev3); +}; + +#if 0 +// puts non-zero in all the waveforms + void TestMB::setAllWaveforms(MinBLEPVCO* vco) +{ + // float * wave = vco->_getWaveforms(); + for (int i = 0; i < (int)MinBLEPVCO::Waveform::END; ++i) { + vco->waveformOutputs[i] = 1; + } +} +#endif + +#if 0 +void TestMB::test1() +{ + MinBLEPVCO vco; + setAllWaveforms(&vco); + assertNE(vco.getWaveform(MinBLEPVCO::Waveform::Saw), 0); + assertNE(vco.getWaveform(MinBLEPVCO::Waveform::Sin), 0); + assertNE(vco.getWaveform(MinBLEPVCO::Waveform::Square), 0); + assertNE(vco.getWaveform(MinBLEPVCO::Waveform::Tri), 0); + assertNE(vco.getWaveform(MinBLEPVCO::Waveform::Even), 0); + + + vco.zeroOutputsExcept(MinBLEPVCO::Waveform::Saw); + assertNE(vco.getWaveform(MinBLEPVCO::Waveform::Saw), 0); + assertEQ(vco.getWaveform(MinBLEPVCO::Waveform::Sin), 0); + assertEQ(vco.getWaveform(MinBLEPVCO::Waveform::Square), 0); + assertEQ(vco.getWaveform(MinBLEPVCO::Waveform::Tri), 0); + assertEQ(vco.getWaveform(MinBLEPVCO::Waveform::Even), 0); + + // special case for even and sin + setAllWaveforms(&vco); + vco.zeroOutputsExcept(MinBLEPVCO::Waveform::Even); + assertEQ(vco.getWaveform(MinBLEPVCO::Waveform::Saw), 0); + assertNE(vco.getWaveform(MinBLEPVCO::Waveform::Sin), 0); + assertEQ(vco.getWaveform(MinBLEPVCO::Waveform::Square), 0); + assertEQ(vco.getWaveform(MinBLEPVCO::Waveform::Tri), 0); + assertNE(vco.getWaveform(MinBLEPVCO::Waveform::Even), 0); + + setAllWaveforms(&vco); + vco.zeroOutputsExcept(MinBLEPVCO::Waveform::Square); + assertEQ(vco.getWaveform(MinBLEPVCO::Waveform::Saw), 0); + assertEQ(vco.getWaveform(MinBLEPVCO::Waveform::Sin), 0); + assertNE(vco.getWaveform(MinBLEPVCO::Waveform::Square), 0); + assertEQ(vco.getWaveform(MinBLEPVCO::Waveform::Tri), 0); + assertEQ(vco.getWaveform(MinBLEPVCO::Waveform::Even), 0); +} + + +static void test0() +{ + MinBLEPVCO vco; + + // Don't enable any waveforms + // vco.setSampleTime(sampleTime); + vco.setNormalizedFreq(1000 * sampleTime); + vco.step(); + + // should get nothing out. + assert(vco.getWaveform(MinBLEPVCO::Waveform::Sin) == 0); + assert(vco.getWaveform(MinBLEPVCO::Waveform::Square) == 0); + assert(vco.getWaveform(MinBLEPVCO::Waveform::Saw) == 0); + assert(vco.getWaveform(MinBLEPVCO::Waveform::Tri) == 0); + assert(vco.getWaveform(MinBLEPVCO::Waveform::Even) == 0); +} +#endif + + +static void testSaw1() +{ + MinBLEPVCO vco; + + vco.setNormalizedFreq(1000 * sampleTime, sampleTime); + vco.setWaveform(MinBLEPVCO::Waveform::Saw); + vco.step(); + + // should get saw out. + // assertEQ(vco.getWaveform(MinBLEPVCO::Waveform::Sin), 0); + // assertEQ(vco.getWaveform(MinBLEPVCO::Waveform::Square), 0); + // assertNE(vco.getWaveform(MinBLEPVCO::Waveform::Saw), 0); + // assertEQ(vco.getWaveform(MinBLEPVCO::Waveform::Tri), 0); + // assertEQ(vco.getWaveform(MinBLEPVCO::Waveform::Even), 0); + assertNE(vco.getOutput(), 0); + +} + +static void testSync1() +{ + MinBLEPVCO vco; + + vco.setNormalizedFreq(1000 * sampleTime, sampleTime); + vco.setWaveform(MinBLEPVCO::Waveform::Saw); + float lastOut = -1000; + vco.step(); + + // first make sure it's going up. + for (int i = 0; i < 10; ++i) { + vco.step(); + const float x = vco.getOutput(); + assertGT(x, lastOut); + lastOut = x; + } + + vco.onMasterSync(10); // set a reset to VCO + vco.step(); + const float x = vco.getOutput(); + assertLT(x, lastOut); +} + +void TestMB::setPitch(EV3& ev3) +{ + ev3.params[EV3::OCTAVE1_PARAM].value = 2; + ev3.params[EV3::OCTAVE2_PARAM].value = 3; + ev3.params[EV3::OCTAVE3_PARAM].value = 3; + + // raise 2,3 by an oct and a semitone + ev3.params[EV3::SEMI1_PARAM].value = 0; + ev3.params[EV3::SEMI2_PARAM].value = 1; + ev3.params[EV3::SEMI3_PARAM].value = 1; + + ev3.vcos[0].setWaveform(MinBLEPVCO::Waveform::Saw); + ev3.vcos[1].setWaveform(MinBLEPVCO::Waveform::Saw); + ev3.vcos[2].setWaveform(MinBLEPVCO::Waveform::Saw); + + ev3.params[EV3::SYNC2_PARAM].value = 1; + + ev3.vcos[0].name = "VCO1"; + ev3.vcos[1].name = "VCO2"; + ev3.vcos[2].name = "VCO3"; + +} + +void TestMB::testSync2() +{ + printf("***** testSync2*****\n"); + EV3 ev3; + setPitch(ev3); + + ev3.step(); + const float f0 = ev3._freq[0]; + const float f1 = ev3._freq[1]; + + assertClose(f0, 2093.02, .005); + assertClose(f1, 4434.95, .005); + + float last0 = -10; + float last1 = -10; + for (int i = 0; i < 100; ++i) { + ev3.step(); + + //printf("phase==%.2f phase1==%.2f ", ev3.vcos[0].phase, ev3.vcos[1].phase); + float x = ev3._out[0]; + // assert(x > last0); + //printf("%d delta0=%.2f",i, x - last0); + last0 = x; + + x = ev3._out[1]; + // assert(x > last1); + + printf(" delta1=%.2f", x - last1); + printf(" 0=%.2f 1=%.2f\n", last0, last1); + fflush(stdout); + last1 = x; + } + + // TODO: test the sync on/off + +} + + + +void TestMB::testSync3() +{ + EV3 ev3; + setPitch(ev3); + + ev3.step(); + const float f0 = ev3._freq[0]; + const float f1 = ev3._freq[1]; + + assertClose(f0, 2093.02, .01); + assertClose(f1, 4434.95, .01); + + float last0 = -10; + float last1 = -10; + for (int i = 0; i < 100; ++i) { + // printf("-------- sample %d -----------\n", i); + ev3.step(); + } + + // TODO: test the sync on/off + +} + + +static void testBlepx(float crossing, float jump) +{ + printf("BLEP crossing = %.2f, jump =%.2f\n", crossing, jump); + rack::MinBLEP<16> syncMinBLEP; + + syncMinBLEP.minblep = rack::minblep_16_32; + syncMinBLEP.oversample = 32; + + // syncMinBLEP.jump(-.5, -2); + syncMinBLEP.jump(crossing, jump); + for (int i = 0; i < 32; ++i) { + //float saw = -1.0 + 2.0*phase; + float x = syncMinBLEP.shift(); + printf("blep[%d] = %.2f\n", i, x); + } + +} + +static void testEnums() +{ + assertEQ((int) EV3::Waves::SIN, (int) MinBLEPVCO::Waveform::Sin); + assertEQ((int) EV3::Waves::TRI, (int) MinBLEPVCO::Waveform::Tri); + assertEQ((int) EV3::Waves::SAW, (int) MinBLEPVCO::Waveform::Saw); + assertEQ((int) EV3::Waves::SQUARE, (int) MinBLEPVCO::Waveform::Square); + assertEQ((int) EV3::Waves::EVEN, (int) MinBLEPVCO::Waveform::Even); +} + +static void testOutput(MinBLEPVCO::Waveform wf, bool expectFlat) +{ + MinBLEPVCO osc; + + osc.setWaveform(wf); + osc.setNormalizedFreq(.1f, 1.0f / 44100); // high freq + + bool hasChanged = false; + float last = -100; + for (int i = 0; i < 100; ++i) { + osc.step(); + float x = osc.getOutput(); + if (!expectFlat) assertNE(x, last); + if (last != x) hasChanged = true; + last = x; + } + assert(hasChanged); +} + +static void testOutputs() +{ + testOutput(MinBLEPVCO::Waveform::Saw, false); + testOutput(MinBLEPVCO::Waveform::Square, true); + testOutput(MinBLEPVCO::Waveform::Sin, false); + testOutput(MinBLEPVCO::Waveform::Tri, false); + testOutput(MinBLEPVCO::Waveform::Even, false); + +} + +static void testBlep() +{ + testBlepx(-.5, -2); + testBlepx(-.5, 1); + testBlepx(-.9f, .2f); +} + +static void testZero() +{ + MinBLEPVCO osc; + + osc.setWaveform(MinBLEPVCO::Waveform::Saw); + osc.setNormalizedFreq(.1f, 1.0f / 44100); // high freq + osc.step(); + osc.setWaveform(MinBLEPVCO::Waveform::END); + osc.step(); + float x = osc.getOutput(); + assertEQ(x, 0); +} + +static void testSyncOut(MinBLEPVCO::Waveform wf) +{ + MinBLEPVCO osc; + + int callbackCount = 0; + osc.setWaveform(wf); + osc.setNormalizedFreq(.1f, 1.0f / 44100); // high freq + osc.setSyncCallback([&callbackCount](float p) { + assert(p <= 0 && p >= -1); + callbackCount++; + }); + + for (int i = 0; i < 15; ++i) { + osc.step(); + + } + assertEQ(callbackCount, 1); + // assertGE(callbackCount, 1); // TODO: why does square do 3?? + +} + +static void testSyncOut() +{ + testSyncOut(MinBLEPVCO::Waveform::Saw); + testSyncOut(MinBLEPVCO::Waveform::Square); + testSyncOut(MinBLEPVCO::Waveform::Sin); + testSyncOut(MinBLEPVCO::Waveform::Tri); + testSyncOut(MinBLEPVCO::Waveform::Even); +} + + +static void testSyncIn(MinBLEPVCO::Waveform wf) +{ + MinBLEPVCO osc; + + osc.setWaveform(wf); + osc.setNormalizedFreq(.1f, 1.0f / 44100); // high freq + + for (int i = 0; i < 4; ++i) { + osc.step(); + } + osc.step(); + float x = osc.getOutput(); + osc.onMasterSync(0); + osc.step(); // this one catches the callback + osc.step(); // this one generates new sample (TODO: is this extra required?) + float y = osc.getOutput(); + assert(!AudioMath::closeTo(x, y, .1)); + + // TODO: pick freq that will make this more robust + +} + +static void testSyncIn() +{ + testSyncIn(MinBLEPVCO::Waveform::Saw); + testSyncIn(MinBLEPVCO::Waveform::Sin); + testSyncIn(MinBLEPVCO::Waveform::Tri); + testSyncIn(MinBLEPVCO::Waveform::Square); + testSyncIn(MinBLEPVCO::Waveform::Even); +} + +void testMinBLEPVCO() +{ + // A lot of these tests are from old API +// TestMB::test1(); + // printf("fix the minb tests\n"); + // test0(); + + testSaw1(); + //testSync1(); + + // this one doesn't work, either. + //TestMB::testSync2(); + TestMB::testSync3(); + // testBlep(); + testEnums(); + testOutputs(); + testZero(); + testSyncOut(); + testSyncIn(); +} \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/test/testObjectCache.cpp b/plugins/community/repos/squinkylabs-plug1/test/testObjectCache.cpp index c3d2a133..25378e79 100644 --- a/plugins/community/repos/squinkylabs-plug1/test/testObjectCache.cpp +++ b/plugins/community/repos/squinkylabs-plug1/test/testObjectCache.cpp @@ -21,7 +21,7 @@ static void testBipolar() { // simple test that bipolar audio scalers use cached lookups, and they work - AudioMath::ScaleFun f = AudioMath::makeBipolarAudioScaler(3, 4); + AudioMath::ScaleFun f = AudioMath::makeScalerWithBipolarAudioTrim(3, 4); assertEQ(f(0, -5, 0), 3.); assertEQ(_numLookupParams, 1); } @@ -32,6 +32,38 @@ static void testBipolar() assertEQ(_numLookupParams, 1); } +template +static void testAudioTaper() +{ + assertEQ(_numLookupParams, 0); + + auto test = ObjectCache::getAudioTaper(); + assertEQ(_numLookupParams, 1); + auto test2 = ObjectCache::getAudioTaper(); + assertEQ(_numLookupParams, 1); + test.reset(); + assertEQ(_numLookupParams, 1); + + test2.reset(); + assertEQ(_numLookupParams, 0); + + { + + // simple test that bipolar audio scalers use cached lookups, and they work + AudioMath::SimpleScaleFun f = AudioMath::makeSimpleScalerAudioTaper(3, 4); + assertEQ(f(0, -5), 3.); + assertEQ(_numLookupParams, 1); + assertEQ(f(5, 5), 4.); + } + assertEQ(_numLookupParams, 0); + + // make again + test = ObjectCache::getAudioTaper(); + assertEQ(_numLookupParams, 1); +} + + + template static void testSin() { @@ -49,7 +81,7 @@ static void testSin() { // // simple test that bipolar audio scalers use cached lookups, and they work - AudioMath::ScaleFun f = AudioMath::makeBipolarAudioScaler(3, 4); + AudioMath::ScaleFun f = AudioMath::makeScalerWithBipolarAudioTrim(3, 4); assertEQ(f(0, -5, 0), 3.); assertEQ(_numLookupParams, 1); } @@ -166,16 +198,39 @@ static void testTanh5() } } + +template +static void testExp2Ex() +{ + auto f = ObjectCache::getExp2Ex(); + assertEQ(_numLookupParams, 2); +} + +template +static void testLPF() +{ + // make sure we can get the two supported cutoffs + + auto f16 = ObjectCache::get6PLPParams(.25f / 16); + assert(f16); + auto f4 = ObjectCache::get6PLPParams(.25f / 4); + assert(f4); +} + + template static void test() { testBipolar(); + testAudioTaper(); testSin(); testExp2(); testExp2b(); testDb2Gain(); testDb2Gain2(); testTanh5(); + testExp2Ex(); + testLPF(); } void testObjectCache() diff --git a/plugins/community/repos/squinkylabs-plug1/test/testPoly.cpp b/plugins/community/repos/squinkylabs-plug1/test/testPoly.cpp new file mode 100644 index 00000000..9be3ddee --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/test/testPoly.cpp @@ -0,0 +1,67 @@ + +#include "asserts.h" +#include "poly.h" +#include "Analyzer.h" +#include "SinOscillator.h" + +static void test0() +{ + Poly poly; + float x = poly.run(0); + assertEQ(x, 0); + + x = poly.run(1); + assertEQ(x, 0); + + poly.setGain(0, 1); + x = poly.run(0); + assertEQ(x, 0); +} + +static void test1() +{ + Poly poly; + poly.setGain(0, 1); + float x = poly.run(1); + assertNE(x, 0); +} + +static void testPureTerm(int term) +{ + float desiredFreq = 500.0f; + int numSamples = 16 * 1024; + const float sampleRate = 44100.0f; + double actualFreq = Analyzer::makeEvenPeriod(desiredFreq, sampleRate, numSamples); + + SinOscillatorParams sinParams; + SinOscillatorState sinState; + SinOscillator::setFrequency(sinParams, float(actualFreq / sampleRate)); + + Poly poly; + poly.setGain(term, 1); + + FFTDataCpx spectrum(numSamples); + Analyzer::getSpectrum(spectrum, false, [&sinState, &sinParams, &poly]() { + float sin = SinOscillator::run(sinState, sinParams); + const float x = poly.run(sin); + return x; + }); + + const int order = term + 1; + Analyzer::assertSingleFreq(spectrum, float(actualFreq * order), sampleRate); +} + +static void testTerms() +{ + for (int i = 0; i < 10; ++i) { + testPureTerm(i); + } +} + +void testPoly() +{ + test0(); + test1(); + testTerms(); + +} \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/test/testRateConversion.cpp b/plugins/community/repos/squinkylabs-plug1/test/testRateConversion.cpp new file mode 100644 index 00000000..999835e1 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/test/testRateConversion.cpp @@ -0,0 +1,63 @@ + +#include "IIRUpsampler.h" +#include "IIRDecimator.h" + +#include "asserts.h" + +static void setup(IIRUpsampler& up, IIRDecimator& dec) +{ + // float cutoff = .25 / 16; + up.setup(16); + dec.setup(16); +} + +// test that the functions can be called +static void test0() +{ + float buffer[16]; + + IIRUpsampler up; + IIRDecimator dec; + setup(up, dec); + + up.process(buffer, 0); + dec.process(buffer); +} + +// test 0 -> 0 +static void test1() +{ + float buffer[16]; + + IIRUpsampler up; + IIRDecimator dec; + setup(up, dec); + + up.process(buffer, 0); + const float x = dec.process(buffer); + assertEQ(x, 0); +} + +// test 10 -> 10 +static void test2() +{ + float buffer[16]; + + IIRUpsampler up; + IIRDecimator dec; + setup(up, dec); + + float x; + for (int i = 0; i < 100; ++i) { + up.process(buffer, 10); + x = dec.process(buffer); + } + assertClose(x, 10, .001); +} + +void testRateConversion() +{ + test0(); + test1(); + test2(); +} \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/test/testRingBuffer.cpp b/plugins/community/repos/squinkylabs-plug1/test/testRingBuffer.cpp index faa031c4..894c0ad4 100644 --- a/plugins/community/repos/squinkylabs-plug1/test/testRingBuffer.cpp +++ b/plugins/community/repos/squinkylabs-plug1/test/testRingBuffer.cpp @@ -6,17 +6,17 @@ static void testConstruct() { - RingBuffer rb; + SqRingBuffer rb; assert(rb.empty()); assert(!rb.full()); - RingBuffer rb2; + SqRingBuffer rb2; } static void testSimpleAccess() { - RingBuffer rb; + SqRingBuffer rb; rb.push(55); assert(!rb.empty()); assert(!rb.full()); @@ -30,7 +30,7 @@ static void testSimpleAccess() static void testMultiAccess() { - RingBuffer rb; + SqRingBuffer rb; rb.push(1234); rb.push(5678); assert(!rb.empty()); @@ -51,7 +51,7 @@ static void testMultiAccess() static void testWrap() { - RingBuffer rb; + SqRingBuffer rb; rb.push(1234); rb.push(5678); rb.pop(); @@ -69,7 +69,7 @@ static void testWrap() static void testFull() { - RingBuffer rb; + SqRingBuffer rb; rb.push(1234); rb.push(5678); rb.pop(); @@ -93,7 +93,7 @@ static void testFull() static void testOne() { const char * p = "foo"; - RingBuffer rb; + SqRingBuffer rb; rb.push(p); assert(!rb.empty()); assert(rb.full()); diff --git a/plugins/community/repos/squinkylabs-plug1/test/testSaw.cpp b/plugins/community/repos/squinkylabs-plug1/test/testSaw.cpp index 3715db5d..3b1bff07 100644 --- a/plugins/community/repos/squinkylabs-plug1/test/testSaw.cpp +++ b/plugins/community/repos/squinkylabs-plug1/test/testSaw.cpp @@ -1,6 +1,7 @@ #include #include +#include "asserts.h" #include "MultiModOsc.h" #include "AudioMath.h" #include "SawOscillator.h" @@ -258,6 +259,80 @@ static void testSaw7() assert(AudioMath::closeTo(amplitude, 0.57735, .0001)); } +template +static void testSawFreq_old(bool neg) +{ + // assert(!neg); + const T freq = neg ? T(-.1) : T(.1); + SawOscillatorParams params; + SawOscillator::setFrequency(params, freq); + SawOscillatorState state; + + T lastValue = neg ? T(-100) : T(100); + for (int i = 0; i < 100; ++i) { + T saw = SawOscillator::runSaw(state, params); + + if (!neg) { + if (0 == (i % 10)) { + assertLT(saw, lastValue); + } else { + assertGT(saw, lastValue); + } + } else { +#if 0 + if (0 == (i % 10)) { + assertGT(saw, lastValue); + } else { + assertLT(saw, lastValue); + } +#endif + printf("i = %d out=%.2f\n", i, saw); + fflush(stdout); + } + lastValue = saw; + } + +} + +template +static void testSawFreq(bool neg) +{ + + int period = 10000; + if (sizeof(T) > 4) { + + period *= 1000; + } + T freq = T(1.0 / period); + if (neg) { + freq *= -1; + } + SawOscillatorParams params; + SawOscillator::setFrequency(params, freq); + SawOscillatorState state; + + T last = SawOscillator::runSaw(state, params); + bool done = false; + int count = 0; + for (; !done; ++count) { + if (count > period + 100) { + done = true; + assert(false); + } + T saw = SawOscillator::runSaw(state, params); + assertNE(saw, last); + bool moreThanLast = saw > last; + last = saw; + // if saw is turning going back + if (moreThanLast == neg) { + const int measuredPeriod = count + 1; + assertClose(measuredPeriod, period, 1.1); + done = true; + } + } +} + + template static void testSawT() { @@ -272,6 +347,8 @@ static void testSawT() testSaw6(); testTri7(); testSaw7(); + testSawFreq(false); + // testSawFreq(true); } void testSaw() diff --git a/plugins/community/repos/squinkylabs-plug1/test/testSin.cpp b/plugins/community/repos/squinkylabs-plug1/test/testSin.cpp new file mode 100644 index 00000000..ea39d698 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/test/testSin.cpp @@ -0,0 +1,225 @@ +/** + * THD+Noise tests for various sin generators + */ +#include "Analyzer.h" +#include "SinOscillator.h" + +#include + +#if 0 +const static int numSamples = 16 * 256 * 1024; +//const static int numSamples = 256; +const double sampleRate = 44100.0; +const double sampleTime = 1.0 / sampleRate; + +const double testFreq = 700; + + + +static void testOsc(const std::string& title, double freq, std::function osc) +{ + FFTDataCpx spectrum(numSamples); + Analyzer::getSpectrum(spectrum, false, osc); + + Analyzer::getAndPrintFeatures(spectrum, 3, sampleRate, -180); + + double signal = 0; + double noise = 0; + int matches = 0; + for (int i = 0; i < numSamples / 2; ++i) { + const double f = FFT::bin2Freq(i, sampleRate, numSamples); + const double mag = spectrum.getAbs(i); + + // if (f > 600 && f < 800) printf("%.2f: %f\n", f, mag); + // if (mag > .00000001) printf("%.2f: %e\n", f, mag); + if (freq == f) { + ++matches; + signal += mag; + } else { + noise += mag; + } + } + assert(matches == 1); + assert(noise > 0); + printf("%s SNR = %f (db)\n", title.c_str(), AudioMath::db(signal / noise)); +} + +template +static void testPhaseAcc() +{ + const double f = Analyzer::makeEvenPeriod(testFreq, sampleRate, numSamples); + SinOscillatorParams params; + SinOscillatorState state; + + std::string title = (sizeof(T) > 4) ? "double w lookup" : "float w lookup"; + SinOscillator::setFrequency(params, float(f * sampleTime)); + testOsc(title, f, [&state, ¶ms]() { + return (float) SinOscillator::run(state, params); + }); +} + + +static void testPhaseAcc2() +{ + const double f = Analyzer::makeEvenPeriod(testFreq, sampleRate, numSamples); + + std::string title = "phase acc std::sin"; + + double acc = 0; + testOsc(title, f, [f, &acc]() { + // return SinOscillator::run(state, params); + + const double inc = f * sampleTime; + acc += inc; + if (acc > 1) { + acc -= 1; + } + return float(std::sin(acc * AudioMath::Pi * 2)); + }); +} + +template +class Osc2 +{ +public: + void setFreq(float t) + { + assert(t > 0 && t < .51); + + // orig paper + // tapWeight = t * (1.0 / AudioMath::Pi); + // tapWeight = t * 2; + + + const T omega = 2.0 * AudioMath::Pi * t; + tapWeight = 2 * sin(omega / 2.0); + + // from kvre + //e= 2*sin(omega/2.0); // omega = 2*pi*freq/sr; + } + float run() + { + const T x = zX - tapWeight * zY; + + // orig paper + // const T y = (tapWeight * zX) + (1 - tapWeight * tapWeight) * zY; + + // smith (seems whack?) but seems to work as well as the above + const T y = (tapWeight * x) + zY; + + zX = x; + zY = y; + return float(zX); + } + + const double k = sqrt(2); + + T zX = 1.200049 / k; + T zY = .747444 / k; + T tapWeight = .1; +}; + + +template +class Osc3 +{ +public: + void setFreq(float t) + { + assert(t > 0 && t < .51); + // tapWeight = t * (1.0 / AudioMath::Pi); + tapWeight = 2 * sin(t); + } + float run() + { + const T x = zX + tapWeight * zY; + const T y = -(tapWeight * x) + zY; + + zX = x; + zY = y; + return float(zX); + } + + // const double k = sqrt(2); + + T zX = 1; + T zY = 1; + T tapWeight = .1; +}; + + +static void testOsc2() +{ + const double f = Analyzer::makeEvenPeriod(testFreq, sampleRate, numSamples); + // const double f = sampleRate / 2; + + printf("trying osc 2 at %f\n", f); + std::string title = "osc2"; + Osc2 osc; + osc.setFreq(float(f * sampleTime)); + for (int i = 0; i < 1000000; ++i) { + osc.run(); + } + + testOsc(title, f, [&osc]() { + return osc.run(); + }); +} + + +static void testOsc3() +{ + const double f = Analyzer::makeEvenPeriod(testFreq, sampleRate, numSamples); + // const double f = sampleRate / 2; + + std::string title = "osc3"; + Osc3 osc; + osc.setFreq(float(f * sampleTime)); + for (int i = 0; i < 1000000; ++i) { + osc.run(); + } + + testOsc(title, f, [&osc]() { + return osc.run(); + }); +} + +static void testOsc2Amp_sub(double freq) +{ + const int bufSize = 16 * 1024 * 16; + Osc2 osc; + osc.setFreq(float(freq * sampleTime)); + + for (int i = 0; i < 1000000; ++i) { + osc.run(); + } + + + std::vector data; + data.resize(bufSize); + for (int i = 0; i < bufSize; ++i) { + data[i] = osc.run(); + } + auto minMax = AudioMath::getMinMax(data.data(), bufSize); + printf("f= %.2f min/max = %f, %f. z = %f, %f\n", freq, minMax.first, minMax.second, osc.zX, osc.zY); + printf("sqr2 = %f\n", sqrt(2.0)); +} + +static void testOsc2Amp() +{ + testOsc2Amp_sub(1111); + testOsc2Amp_sub(40); + testOsc2Amp_sub(400); + testOsc2Amp_sub(4123); +} + +void testSin() +{ + // testPhaseAcc(); + // testPhaseAcc(); + testPhaseAcc2(); + // testOsc2(); + // testOsc3(); + // testOsc2Amp(); +} +#endif \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/test/testSinOscillator.cpp b/plugins/community/repos/squinkylabs-plug1/test/testSinOscillator.cpp index a34343ca..a4633928 100644 --- a/plugins/community/repos/squinkylabs-plug1/test/testSinOscillator.cpp +++ b/plugins/community/repos/squinkylabs-plug1/test/testSinOscillator.cpp @@ -119,7 +119,7 @@ static void testDistortion() } double errDb = AudioMath::db(err); // printf("THD = %f\n", errDb); - assertLT(errDb, -80); + assertLT(errDb, -90); } template diff --git a/plugins/community/repos/squinkylabs-plug1/test/testSpline.cpp b/plugins/community/repos/squinkylabs-plug1/test/testSpline.cpp new file mode 100644 index 00000000..cd480c8b --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/test/testSpline.cpp @@ -0,0 +1,315 @@ + +#include +#include +#include "asserts.h" +#include "LookupTable.h" +#include "AsymWaveShaper.h" +#include "ExtremeTester.h" +#include "Shaper.h" +#include "TestComposite.h" +#include "TestSignal.h" + + +using Spline = std::vector< std::pair >; + + + +static void gen() +{ + for (int i = 0; i < AsymWaveShaper::iSymmetryTables; ++i) { + float symmetry = float(i) / float(AsymWaveShaper::iSymmetryTables - 1); + AsymWaveShaper::genTable(i, symmetry); + } +} + + +static void testLook0() +{ + NonUniformLookup l; + l.add(0, 0); + l.add(1, 1); + l.add(2, 2); + l.add(3, 3); + + double x = l.lookup(2.5); + assertClose(x, 2.5, .0001); +} + +static void testLook1() +{ + NonUniformLookup l; + l.add(0, 0); + l.add(1, 1); + l.add(2, 4); + l.add(3, 3); + + double x = l.lookup(1.5); + assertClose(x, 2.5, .0001); +} + +static void testLook2() +{ + NonUniformLookup l; + l.add(0, 0); + l.add(1, 1); + l.add(2, 2); + l.add(3, 3); + + double x = l.lookup(.5); + assertClose(x, .5, .0001); +} + +static void testLook3() +{ + NonUniformLookup l; + l.add(0, 0); + l.add(1, 1); + l.add(2, 2); + l.add(3, 3); + + double x = l.lookup(2.5); + assertClose(x, 2.5, .0001); +} + +static void testLook4() +{ + NonUniformLookup l; + l.add(0, 0); + l.add(1, 1); + l.add(2, 2); + l.add(3, 3); + + double x = l.lookup(0); + assertClose(x, 0, .0001); +} + + +static void testGen0() +{ + AsymWaveShaper g; + const int index = 3;// symmetry + float x = g.lookup(0, index); + x = g.lookup(1, index); + x = g.lookup(-1, index); +} + +static void testDerivativeSub(int index, float delta) +{ + AsymWaveShaper ws; + + const float ya = ws.lookup(-delta, index); + const float y0 = ws.lookup(0, index); + const float yb = ws.lookup(delta, index); + const float slopeLeft = -ya / delta; + const float slopeRight = yb / delta; + + // printf("[%d] y0 = %f, slope left = %f, right =%f\n", index, y0, slopeLeft, slopeRight); + + // since I changed AsymWaveShaper to be points-1 this is worse + assertClose(y0, 0, .00001); + assertClose(slopeRight, 2, .01); + if (index != 0) { + assertClose(slopeLeft, 2, .3 + ); + } + +} +static void testDerivative() +{ + // 6 with .1 + for (int i = AsymWaveShaper::iSymmetryTables - 1; i >= 0; --i) { + testDerivativeSub(i, .0001f); + } +} + + + +static void testShaper0() +{ + Shaper gmr; + + int shapeMax = (int) Shaper::Shapes::Invalid; + for (int i = 0; i < shapeMax; ++i) { + gmr.params[Shaper::PARAM_SHAPE].value = (float) i; + std::string s = gmr.getString(Shaper::Shapes(i)); + assertGT(s.length(), 0); + assertLT(s.length(), 20); + gmr.params[Shaper::PARAM_OFFSET].value = -5; + for (int i = 0; i < 50; ++i) gmr.step(); + const float x = gmr.outputs[Shaper::OUTPUT_AUDIO].value; + gmr.params[Shaper::PARAM_OFFSET].value = 5; + for (int i = 0; i < 50; ++i) gmr.step(); + const float y = gmr.outputs[Shaper::OUTPUT_AUDIO].value; + + assertLT(x, 10); + assertLT(y, 10); + assertGT(x, -10); + assertGT(y, -10); + } +} + +static void testShaper1Sub(int shape, float gain, float targetRMS) +{ + Shaper gmr; + gmr.params[Shaper::PARAM_SHAPE].value = (float) shape; + gmr.params[Shaper::PARAM_GAIN].value = gain; // max gain + const int buffSize = 1 * 1024; + float buffer[buffSize]; + + TestSignal::generateSin(buffer, buffSize, 1.f / 40); + double rms = TestSignal::getRMS(buffer, buffSize); + //printf("signal=%f\n", rms); + for (int i = 0; i < buffSize; ++i) { + const float x = buffer[i]; + gmr.inputs[Shaper::INPUT_AUDIO].value = buffer[i]; + gmr.step(); + buffer[i] = gmr.outputs[Shaper::OUTPUT_AUDIO].value; + } + rms = TestSignal::getRMS(buffer, buffSize); + // const float targetRMS = 5 * .707f; + + const char* p = gmr.getString(Shaper::Shapes(shape)); + // printf("rms[%s] = %f target = %f ratio=%f\n", p, rms, targetRMS, targetRMS / rms); + + if (targetRMS > .01) { + assertClose(rms, targetRMS, .5); + } +} + +static void testShaper1() +{ + int shapeMax = (int) Shaper::Shapes::Invalid; + for (int i = 0; i < shapeMax; ++i) { + const float targetOutput = (i == (int) Shaper::Shapes::Crush) ? 0 : 5 * .707f; + + testShaper1Sub(i, 5, targetOutput); + testShaper1Sub(i, 0, 0); + } +} + +static void testSplineExtremes() +{ + printf("running testSplineExtremes\n"); fflush(stdout); + + Shaper sp; + + using fp = std::pair; + std::vector< std::pair > paramLimits; + + paramLimits.resize(sp.NUM_PARAMS); + + paramLimits[sp.PARAM_SHAPE] = fp(0.f, float(Shaper::Shapes::Invalid) - 1); + paramLimits[sp.PARAM_GAIN] = fp(-5.0f, 5.0f); + paramLimits[sp.PARAM_GAIN_TRIM] = fp(-1.f, 1.f); + paramLimits[sp.PARAM_OFFSET] = fp(-5.f, 5.f); + paramLimits[sp.PARAM_OFFSET_TRIM] = fp(-1.f, 1.f); + + ExtremeTester< Shaper>::test(sp, paramLimits, true, "shaper"); +} + +static void testShaper2d(Shaper::Shapes shape, float gain, float offset, float input) +{ + Shaper sh; + sh.params[Shaper::PARAM_SHAPE].value = (float) shape; + sh.params[Shaper::PARAM_GAIN].value = gain; + sh.params[Shaper::PARAM_OFFSET].value = offset; + for (int i = 0; i < 100; ++i) { + sh.inputs[Shaper::INPUT_AUDIO].value = input; + sh.step(); + const float out = sh.outputs[Shaper::OUTPUT_AUDIO].value; + + // brief ringing goes > 10 + assert(out < 20 && out > -20); + } + +} +static void testShaper2c(Shaper::Shapes shape, float gain, float offset) +{ + testShaper2d(shape, gain, offset, 0); + testShaper2d(shape, gain, offset, 5); + testShaper2d(shape, gain, offset, -5); +} + +static void testShaper2b(Shaper::Shapes shape, float gain) +{ + testShaper2c(shape, gain, 0.f); + testShaper2c(shape, gain, 5.f); + testShaper2c(shape, gain, -5.f); +} + +static void testShaper2a(Shaper::Shapes shape) +{ + testShaper2b(shape, 0); + testShaper2b(shape, -5); + testShaper2b(shape, 5); +} + +const int shapeMax = (int) Shaper::Shapes::Invalid; + +static void testShaper2() +{ + + for (int i = 0; i < shapeMax; ++i) { + testShaper2a(Shaper::Shapes(i)); + } +} + +// test for DC shift +static void testShaper3Sub(Shaper::Shapes shape) +{ + Shaper sh; + + sh.params[Shaper::PARAM_OVERSAMPLE].value = 2; // turn off oversampling + sh.params[Shaper::PARAM_SHAPE].value = (float) shape; + sh.params[Shaper::PARAM_GAIN].value = -3; // gain up a bit + sh.params[Shaper::PARAM_OFFSET].value = 0; // no offset + + sh.inputs[Shaper::INPUT_AUDIO].value = 0; + for (int i = 0; i < 100; ++i) { + + sh.step(); + } + const float out = sh.outputs[Shaper::OUTPUT_AUDIO].value; + if (shape != Shaper::Shapes::Crush) { + assertEQ(out, 0); + } else { + // crash had a dc offset issue + assertLT(out, 1); + assertGT(out, -1); + } +} + +static void testShaper3() +{ + // testShaper3Sub(Shaper::Shapes::Crush); + for (int i = 0; i < shapeMax; ++i) { + testShaper3Sub(Shaper::Shapes(i)); + } +} + + +void testSpline(bool doEmit) +{ + if (doEmit) { + gen(); + return; + } + testLook0(); + testLook1(); + testLook2(); + testLook3(); + testLook4(); + testGen0(); + testDerivative(); + testShaper0(); + + //printf("!! skipping testShaper1\n"); + testShaper1(); + testShaper2(); + testShaper3(); + + + //testSplineExtremes(); + printf("skipping shaper extremems becuase of bug in crush"); +} + diff --git a/plugins/community/repos/squinkylabs-plug1/test/testStochasticGrammar.cpp b/plugins/community/repos/squinkylabs-plug1/test/testStochasticGrammar.cpp new file mode 100644 index 00000000..68483759 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/test/testStochasticGrammar.cpp @@ -0,0 +1,498 @@ + +#include "GenerativeTriggerGenerator.h" +#include "StochasticGrammar.h" +#include "TriggerSequencer.h" + +#include +#include +#include +#include + +static const int numRules = fullRuleTableSize; + +typedef GKEY(*INITFN)(); +static ProductionRule rules[numRules]; + + + +// Test basic integrity of key data +static void test0() +{ + GKEY key; + GKEY outKeys[ProductionRuleKeys::bufferSize]; + for (key = sg_first; key <= sg_last; ++key) { + ProductionRuleKeys::breakDown(key, outKeys); + for (GKEY* p = outKeys; *p != sg_invalid; ++p) { + assert(*p >= sg_first); + assert(*p <= sg_last); + } + + std::string s = ProductionRuleKeys::toString(key); + assert(!s.empty()); + assert(s.length() < 256); + + int dur = ProductionRuleKeys::getDuration(key); + assert(dur > 0); + assert(dur <= PPQ * 8); + + } +} + +// test all the gkeys +void testAllKeys() +{ + const int siz = ProductionRuleKeys::bufferSize; + GKEY buffer[siz]; + + for (GKEY gk = sg_first; gk <= sg_last; ++gk) { + // printf("testing key %d\n", gk); + // printf("to string: %s\n", ProductionRuleKeys::toString(gk)); + const int dur = ProductionRuleKeys::getDuration(gk); + ProductionRuleKeys::breakDown(gk, buffer); + int sum = 0; + for (GKEY * p = buffer; *p != sg_invalid; ++p) { + // printf("adding to sum %d\n", ProductionRuleKeys::getDuration(*p)); + sum += ProductionRuleKeys::getDuration(*p); + + } + // printf("dur = %d sum = %d (should be the same)\n", dur, sum); + assert(dur == sum); + } +} + + + +/************************************************************************************** + * Make some simple grammars and test them + **************************************************************************************/ + +#ifdef _DEBUG +void gdt0() +{ + { + static ProductionRule rules[numRules]; + bool b = ProductionRule::isGrammarValid(rules, numRules, sg_invalid); + assert(!b); + } + { + // throw in a positive case + static ProductionRule rules[numRules]; + ProductionRule& r = rules[sg_w]; + r.entries[0].probability = 1.0f; + r.entries[0].code = sg_invalid; + + bool b = ProductionRule::isGrammarValid(rules, numRules, sg_w); + assert(b); + } + { + // terminal code wrong + static ProductionRule rules[numRules]; + ProductionRule& r = rules[sg_w]; + r.entries[0].probability = 1.0f; + r.entries[0].code = sg_q; + + bool b = ProductionRule::isGrammarValid(rules, numRules, sg_w); + assert(!b); + } + { + // bad order of probability + static ProductionRule rules[numRules]; + ProductionRule& r = rules[sg_w]; + r.entries[0].probability = 1.0f; + r.entries[0].code = sg_q; + + bool b = ProductionRule::isGrammarValid(rules, numRules, sg_w); + assert(!b); + } + { + // rule branches to nowhere + static ProductionRule rules[numRules]; + + // break w2 into w,w prob 100 + ProductionRule& r = rules[sg_w2]; + r.entries[0].probability = 1.0f; + r.entries[0].code = sg_ww; + bool b = ProductionRule::isGrammarValid(rules, numRules, sg_w); + assert(!b); + } +} +#endif + + +class TestEvaluator : public ProductionRule::EvaluationState +{ +public: + TestEvaluator(AudioMath::RandomUniformFunc xr) : ProductionRule::EvaluationState(xr) + { + } + + void writeSymbol(GKEY key) override + { + keys.push_back(key); + } + + int getNumSymbols() + { + //printf("final keys: "); + // for (size_t i = 0; i< keys.size(); ++i) printf("%s, ", ProductionRuleKeys::toString(keys[i])); + // printf("\n"); + return (int) keys.size(); + } +private: + std::vector keys; +}; + + +/** + * simplest possible grammar. + */ +static GKEY init0() +{ + // printf("called init0\n"); + // This rule always generate sg-w2 (two whole notes tied together) + ProductionRule& r = rules[sg_w2]; + + r.entries[0].probability = 1; + r.entries[0].code = sg_invalid; // terminate expansion + + + return sg_w2; +} + +/** + * Simple grammar with a rule but no random. + */ +static GKEY init1() +{ + + { + // start with w2 duration + ProductionRule& r = rules[sg_w2]; + + // break into w,w prob 100 + r.entries[0].probability = 1.0f; + r.entries[0].code = sg_ww; + } + + { + // now need rule for w hole + //printf("in init1 making 100 for %d\n", sg_w); + ProductionRule& r = rules[sg_w]; + r.entries[0].probability = 1.0f; + r.entries[1].code = sg_invalid; + } + //printf("leave init 1. rule 1 p0 = %f\n", rules[sg_w2].entries[0].probability); + return sg_w2; +} + + +/** + * Simple grammar with randomness initializer + */ +static GKEY init2() +{ + { + // start with w2 duration + ProductionRule& r = rules[sg_w2]; + + // break into w,w prob 50 + + r.entries[0].probability = .5f; + r.entries[0].code = sg_ww; + r.entries[1].probability = 1.0f; + r.entries[1].code = sg_invalid; // always terminate + } + + { + // now need rule for w hole + ProductionRule& r = rules[sg_w]; + r.entries[1].probability = 1.0f; + r.entries[1].code = sg_invalid; // always terminate + } + + return sg_w2; +} + +#ifdef _DEBUG +static void testGrammarSub(INITFN f) +{ + GKEY init = f(); + + + bool b = ProductionRule::isGrammarValid(rules, numRules, init); + assert(b); + + TestEvaluator es(AudioMath::random()); + es.rules = rules; + es.numRules = numRules; + ProductionRule::evaluate(es, init); + + assert(es.getNumSymbols() > 0); +} +#endif + + +/********************************************************************************************************* + * TriggerSequencer + **********************************************************************************************************/ + + // test event at zero fires at zero +static void ts0() +{ + TriggerSequencer::Event seq[] = + { + {TriggerSequencer::TRIGGER, 0}, + {TriggerSequencer::END, 100} + }; + TriggerSequencer ts(seq); + + ts.clock(); + assert(ts.getTrigger()); + + ts.clock(); + assert(!ts.getTrigger()); + +} + +// test trigger at 1 happens at 1 +static void ts1() +{ + TriggerSequencer::Event seq[] = + { + {TriggerSequencer::TRIGGER, 1}, + {TriggerSequencer::END, 0} + }; + TriggerSequencer ts(seq); + + ts.clock(); + assert(!ts.getTrigger()); + + ts.clock(); + assert(ts.getTrigger()); + + ts.clock(); + assert(!ts.getTrigger()); + + ts.clock(); + assert(!ts.getTrigger()); +} + + +// 4 clock loop: delay 4, trigger, end +static void ts2() +{ + TriggerSequencer::Event seq[] = + { + {TriggerSequencer::TRIGGER, 4}, + {TriggerSequencer::END, 0} + }; + TriggerSequencer ts(seq); + + bool firstTime = true; + // first time through, 4 clocks of nothing. then clock, 0,0,0 + for (int i = 0; i < 4; ++i) { + ts.clock(); + if (firstTime) { + assert(!ts.getTrigger()); assert(!ts.getEnd()); + firstTime = false; + } else { + //printf("second time around, t=%d e=%d\n", ts.getTrigger(), ts.getEnd()); + + // second time around we finally see the trigger + + assert(ts.getTrigger()); + + // second time around, need to clock the end of the last time + assert(ts.getEnd()); + ts.reset(seq); // start it up again + assert(!ts.getTrigger()); // resetting should not set us up for a trigger + } + ts.clock(); assert(!ts.getTrigger()); assert(!ts.getEnd()); + ts.clock(); assert(!ts.getTrigger()); assert(!ts.getEnd()); + + ts.clock(); assert(!ts.getTrigger()); + // assert(ts.getEnd()); + + // ts.reset(seq); + } +} + +// test trigger seq qith +// 4 clock loop: trigger, delay 4 end +static void ts3() +{ + TriggerSequencer::Event seq[] = + { + {TriggerSequencer::TRIGGER, 0}, + {TriggerSequencer::END, 4} + }; + TriggerSequencer ts(seq); + + + bool firstLoop = true; + for (int i = 0; i < 4; ++i) { + //printf("--- loop ----\n"); + + // 1 + + ts.clock(); + if (firstLoop) { + assert(ts.getTrigger()); + // we just primed loop at top, so it's got a ways + assert(!ts.getEnd()); + firstLoop = false; + } else { + // second time around, need to clock the end of the last time + assert(ts.getEnd()); + ts.reset(seq); // start it up again + assert(ts.getTrigger()); // resetting should have set us up for a trigger + } + // 2 + ts.clock(); assert(!ts.getTrigger()); assert(!ts.getEnd()); + // 3 + ts.clock(); assert(!ts.getTrigger()); assert(!ts.getEnd()); + // 4 + ts.clock(); + assert(!ts.getTrigger()); + assert(!ts.getEnd()); + } +} + +// test trigger seq with straight ahead 4/4 as generated by a grammar +static void ts4() +{ + TriggerSequencer::Event seq[] = + { + {TriggerSequencer::TRIGGER, 0}, + {TriggerSequencer::TRIGGER, 4}, + {TriggerSequencer::TRIGGER, 4}, + {TriggerSequencer::TRIGGER, 4}, + {TriggerSequencer::END, 4} + }; + TriggerSequencer ts(seq); + + + //bool firstLoop = true; + for (int i = 0; i < 100; ++i) { + bool firstTime = (i == 0); + // repeating pattern of trigg, no, no, no + for (int j = 0; j < 4; ++j) { + for (int k = 0; k < 4; ++k) { + // printf("test loop, i=%d, j=%d, k=%d\n", i, j, k); + ts.clock(); + + bool expectEnd = (k == 0) && (j == 0) && !firstTime; + assert(ts.getEnd() == expectEnd); + if (ts.getEnd()) { + ts.reset(seq); + } + assert(ts.getTrigger() == (k == 0)); + } + } + } +} + +/******************************************************************************* + ** StochasticGrammarDictionary + */ + +#ifdef _DEBUG +void gdt1() +{ + assert(StochasticGrammarDictionary::getNumGrammars() > 0); + for (int i = 0; i < StochasticGrammarDictionary::getNumGrammars(); ++i) { + StochasticGrammarDictionary::Grammar g = StochasticGrammarDictionary::getGrammar(i); + bool b = ProductionRule::isGrammarValid(g.rules, g.numRules, g.firstRule); + assert(b); + } +} +#endif + +/******************************************************************************************** +* GenerativeTriggerGenerator +**********************************************************************************************/ + +// test that we get some clocks and some not +static void gtg0() +{ + GKEY key = init1(); + GenerativeTriggerGenerator gtg(AudioMath::random(), rules, numRules, key); + bool yes = false; + bool no = false; + for (int i = 0; i < 100000; ++i) { + if (gtg.clock()) + yes = true; + else + no = true; + + if (yes && no) { + //printf("clocked at %d\n", i); + return; + } + } + assert(false); + +} + + +// test that we get everything in even quarter notes +static void gtg1() +{ + GKEY key = init1(); + std::set counts; + + GenerativeTriggerGenerator gtg(AudioMath::random(), rules, numRules, key); + + int ct = 0; + for (int i = 0; i < 10000; ++i) { + bool b = gtg.clock(); + if (b) { + //printf("clocked at %d\n", ct); + counts.insert(ct); + ct = 0; + } + ct++; + } + //counts.insert(50); + assert(!counts.empty()); + for (std::set::iterator it = counts.begin(); it != counts.end(); ++it) { + int c = *it; + + if ((c % PPQ) != 0) { + //printf("PPQ=%d, c modePPQ =%d\n", PPQ, (c % PPQ)); + //printf("2ppq = %d, 4ppq=%d\n", 2 * PPQ, 4 * PPQ); + assert(false); + } + } +} + + + + + + +void testStochasticGrammar() +{ + test0(); + testAllKeys(); + +#ifdef _DEBUG + gdt0(); + testGrammarSub(init0); + testGrammarSub(init1); + testGrammarSub(init2); +#endif + ts0(); + ts1(); + ts2(); + ts3(); + ts4(); + +#ifdef _DEBUG + gdt1(); +#endif + + gtg0(); + gtg1(); + +} \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/test/testTestSignal.cpp b/plugins/community/repos/squinkylabs-plug1/test/testTestSignal.cpp index 2da8fa46..c14e33e0 100644 --- a/plugins/community/repos/squinkylabs-plug1/test/testTestSignal.cpp +++ b/plugins/community/repos/squinkylabs-plug1/test/testTestSignal.cpp @@ -1,6 +1,7 @@ #include #include +#include "asserts.h" #include "AudioMath.h" #include "TestSignal.h" @@ -14,6 +15,7 @@ static void test1() T buffer[size]; TestSignal::generateSin(buffer, size, T(.01)); +#if 0 T min = 1, max = -1; for (int i = 0; i < size; ++i) { const T x = buffer[i]; @@ -30,6 +32,11 @@ static void test1() const T delta = T(.0000001); assert(AudioMath::closeTo(min, T(-1), delta)); assert(AudioMath::closeTo(max, T(1), delta)); +#endif + const T delta = T(.0000001); + auto x = AudioMath::getMinMax(buffer, size); + assertClose(x.first, -1, delta); + assertClose(x.second, 1, delta); } /** diff --git a/plugins/community/repos/squinkylabs-plug1/test/testThread.cpp b/plugins/community/repos/squinkylabs-plug1/test/testThread.cpp index 2f4737db..a28d9e48 100644 --- a/plugins/community/repos/squinkylabs-plug1/test/testThread.cpp +++ b/plugins/community/repos/squinkylabs-plug1/test/testThread.cpp @@ -122,7 +122,7 @@ static std::atomic slow; static std::atomic fast; //thread func -static void t4(bool iAmIt,int boost) +static void t4(bool iAmIt, int boost) { // printf("t4 called with %d\n", iAmIt); @@ -142,9 +142,9 @@ static void t4(bool iAmIt,int boost) default: assert(false); } - + fflush(stdout); - + } while (!stopNow) { for (int i = 0; i < 100000; ++i) { @@ -217,18 +217,18 @@ static void test5() void testThread(bool extended) { - assertEQ(ThreadSharedState::_dbgCount, 0); - assertEQ(ThreadMessage::_dbgCount, 0); - test0(); - test1(); - test2(); - test3(); - if (extended) { - test4(); - } + assertEQ(ThreadSharedState::_dbgCount, 0); + assertEQ(ThreadMessage::_dbgCount, 0); + test0(); + test1(); + test2(); + test3(); + if (extended) { + test4(); + } #ifdef ARCH_WIN - test5(); + test5(); #endif - assertEQ(ThreadSharedState::_dbgCount, 0); - assertEQ(ThreadMessage::_dbgCount, 0); + assertEQ(ThreadSharedState::_dbgCount, 0); + assertEQ(ThreadMessage::_dbgCount, 0); } \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/test/testVCO.cpp b/plugins/community/repos/squinkylabs-plug1/test/testVCO.cpp new file mode 100644 index 00000000..9ff0ec81 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/test/testVCO.cpp @@ -0,0 +1,237 @@ + +#include + +#include "Analyzer.h" +#include "asserts.h" +#include "CHB.h" +#include "EvenVCO.h" +#include "FunVCO.h" +#include "SawOscillator.h" +#include "TestComposite.h" + + +using EVCO = EvenVCO ; +//using FUN = VoltageControlledOscillator<16, 16>; +using CH = CHB; + +float desiredPitch(float octave, float tune, float cv1, float cv2, float mod) +{ + float pitch = 1.0f + roundf(octave) + tune / 12.0f; + pitch += cv1 + cv2; + pitch += mod / 4.0f; + + float freq = 261.626f * powf(2.0f, pitch); + // printf("theirs: pitch = %f exp = %f\n", pitch, freq); + return freq; +} + +float desiredPitchEv(const EVCO& vco) +{ +#if 1 + return desiredPitch( + vco.params[(int) EVCO::OCTAVE_PARAM].value, + vco.params[(int) EVCO::TUNE_PARAM].value, + vco.inputs[(int) EVCO::PITCH1_INPUT].value, + vco.inputs[(int) EVCO::PITCH2_INPUT].value, + vco.inputs[(int) EVCO::FM_INPUT].value + ); +#else + // This is just the original code as reference + float pitch = 1.0f + roundf(vco.params[(int) EVCO::OCTAVE_PARAM].value) + vco.params[(int) EVCO::TUNE_PARAM].value / 12.0f; + pitch += vco.inputs[(int) EVCO::PITCH1_INPUT].value + vco.inputs[(int) EVCO::PITCH2_INPUT].value; + pitch += vco.inputs[(int) EVCO::FM_INPUT].value / 4.0f; + + float freq = 261.626f * powf(2.0f, pitch); + // printf("theirs: pitch = %f exp = %f\n", pitch, freq); + return freq; +#endif +} + +float desiredPitchCh(const CH& vco) +{ + return desiredPitch( + vco.params[(int) CH::PARAM_OCTAVE].value, + vco.params[(int) CH::PARAM_TUNE].value, + vco.inputs[(int) CH::CV_INPUT].value, + 0, + vco.inputs[(int) CH::PITCH_MOD_INPUT].value + ); + +#if 0 + float pitch = 1.0f + roundf(vco.params[(int) CH::PARAM_OCTAVE].value) + vco.params[(int) CH::PARAM_TUNE].value / 12.0f; + pitch += vco.inputs[(int) CH::CV_INPUT].value; + pitch += vco.inputs[(int) CH::PITCH_MOD_INPUT].value / 4.0f; + + // TODO: atenuverter on FM + + float freq = 261.626f * powf(2.0f, pitch); + // printf("theirs: pitch = %f exp = %f\n", pitch, freq); + printf("in desiredPitchCh oct=%f, tune=%f cv=%f, mode=%f\n", + vco.params[(int) CH::PARAM_OCTAVE].value, + vco.params[(int) CH::PARAM_TUNE].value, + vco.inputs[(int) CH::CV_INPUT].value, + vco.inputs[(int) CH::PITCH_MOD_INPUT].value); + printf(" freq = %f\n", freq); + + + return freq; +#endif +} + +static void testxEv(float octave, float tune = 0, float pitch1 = 0, float pitch2 = 0, float fm = 0) +{ + EVCO vco; + + vco.params[(int) EVCO::OCTAVE_PARAM].value = octave; + vco.params[(int) EVCO::TUNE_PARAM].value = tune; + vco.inputs[(int) EVCO::PITCH1_INPUT].value = pitch1; + vco.inputs[(int) EVCO::PITCH2_INPUT].value = pitch2; + vco.inputs[(int) EVCO::FM_INPUT].value = fm; + + vco.outputs[(int) EVCO::SAW_OUTPUT].active = true; + vco.outputs[(int) EVCO::EVEN_OUTPUT].active = false; + vco.outputs[(int) EVCO::TRI_OUTPUT].active = false; + vco.outputs[(int) EVCO::SQUARE_OUTPUT].active = false; + vco.outputs[(int) EVCO::SINE_OUTPUT].active = false; + + vco.step(); + const float desired = desiredPitchEv(vco); + + // printf("test, oct=%f, freq=%.2f desired=%.2f\n", octave, vco._freq, desired); + if (desired > 20000) { + // lookup table doesn't go past 20k. that's fine + assertGE(vco._freq, 20000 - 1); + } else { + assertClose(vco._freq, desired, 1.5); // todo: make better tolerance + } +} + +static void testxCh(float octave, float tune = 0, float pitch1 = 0, float pitch2 = 0, float fm = 0) +{ + CH vco; + + assert(pitch2 == 0); // ch doesn't have one + + vco.params[(int) CH::PARAM_OCTAVE].value = octave; + vco.params[(int) CH::PARAM_TUNE].value = tune; + vco.inputs[(int) CH::CV_INPUT].value = pitch1; + // vco.inputs[(int) CH::PITCH2_INPUT].value = pitch2; + vco.inputs[(int) CH::PITCH_MOD_INPUT].value = fm; + + + vco.step(); + const float desired = desiredPitchCh(vco); + + // printf("test, oct=%f, freq=%.2f desired=%.2f\n", octave, vco._freq, desired); + if (desired > 20000) { + // lookup table doesn't go past 20k. that's fine + assertGE(vco._freq, 20000 - 1); + } else { + assertClose(vco._freq, desired, 1.5); // todo: make better tolerance + } +} + + +static void testInitEv() +{ + EVCO vco; + + vco.step(); + const float desired = desiredPitchEv(vco); + assertClose(vco._freq, desired, 1); // todo: tighten up +} + +static void testInitCh() +{ + CH vco; + + vco.step(); + const float desired = desiredPitchCh(vco); + assertClose(vco._freq, desired, 1); // todo: tighten up +} + +static void testOctavesEv() +{ + EVCO vco; + for (int octave = -5; octave <= 4; ++octave) { + testxEv(float(octave)); + } +} + +static void testOctavesCh() +{ + CH vco; + for (int octave = -5; octave <= 4; ++octave) { + testxCh(float(octave)); + } +} +// test that we go up to 20k +static void testMaxFreqEv() +{ + testxEv(4, 7, 0, 0); + testxEv(4, 7, 1, 0); + testxEv(4, 7, 0, 1); + +} + +static void testMaxFreqCh() +{ + testxCh(4, 7, 0, 0); + testxCh(4, 7, 1, 0); +} + +static void testMinFreqEv() +{ + testxEv(-5, -7, 0, 0); + testxEv(-5, -7, -2, 0); +} + +static void testMinFreqCh() +{ + testxCh(-5, -7, 0, 0); + testxCh(-5, -7, -2, 0); +} + +static void testTuneEv() +{ + testxEv(0, -7, 0, 0); + testxEv(0, 7, 0, 0); +} + + +static void testTuneCh() +{ + testxCh(0, -7, 0, 0); + testxCh(0, 7, 0, 0); +} + + +static void testClamp() +{ + assertEQ(std::clamp(12, 0, 14), 12); + assertEQ(std::clamp(12, 0, 10), 10); + + assertEQ(std::clamp(12, 13, 15), 13); +} + +#if 1 +void testVCO() +{ + testInitEv(); + testInitCh(); + testOctavesEv(); + testOctavesCh(); + testMaxFreqEv(); + testMaxFreqCh(); + testMinFreqEv(); + testMinFreqCh(); + testClamp(); + testTuneEv(); + testTuneCh(); +} +#else +void testVCO() +{ + testOctavesCh(); +} +#endif \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/test/testVCOAlias.cpp b/plugins/community/repos/squinkylabs-plug1/test/testVCOAlias.cpp new file mode 100644 index 00000000..e1c63219 --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/test/testVCOAlias.cpp @@ -0,0 +1,510 @@ + +#include "Analyzer.h" +#include "asserts.h" +#include "EvenVCO.h" +#include "FunVCO.h" +#include "SawOscillator.h" +#include "SinOscillator.h" +#include "TestComposite.h" + + +// globals for these tests +static const float sampleRate = 44100; +static bool expandBins = false; +static bool adjustBins = true; +#if 0 +static const int numSamples = 64 * 1024; +static const double expandThresholdDb = -10; +#else +static const int numSamples = 64 * 1024; +static const double expandThresholdDb = .01; // normally negative +#endif + + + + +static void testPitchQuantize() +{ + const double sampleRate = 44100; + const int numSamples = 16; + const double inputFreq = 44100.0 / 4.0; + double freq = Analyzer::makeEvenPeriod(inputFreq, sampleRate, numSamples); + + // check that quantized pitch is in the bin we expect. + assertEQ(freq, FFT::bin2Freq(4, sampleRate, numSamples)); + + // make saw osc at correct freq + SinOscillatorParams params; + SinOscillatorState state; + SinOscillator::setFrequency(params, 1.0 / 4.0); + + // check that spectrum has only a single freq + std::function func = [&state, ¶ms]() { + return float(30 * SinOscillator::run(state, params)); + }; + FFTDataCpx spectrum(numSamples); + + + Analyzer::getSpectrum(spectrum, false, func); + for (int i = 0; i < numSamples / 2; ++i) { + const float abs = spectrum.getAbs(i); + if (i == 4) { + assertGE(abs, .5); + } else { + assertLT(abs, 0.000000001); + } + } +} + +class AliasStats +{ +public: + float totalAliasDb; + float totalAliasAWeighted; + float maxAliasFreq; +}; + +/* + +Next: examine the spectrum. make sure all freq in spectrum are signal or alias +*/ + +class FrequencySets +{ +public: + FrequencySets(double fundamental, double sampleRate, const FFTDataCpx& spectrum); + void expandFrequencies(); + bool checkOverlap() const; + + std::set harmonics; + std::set alias; + + void adjustFrequencies(); + void dump(const char *) const; +private: + static void expandFrequencies(std::set&, const FFTDataCpx& spectrum); + bool adjustFrequencies1(); + static bool adjustFreqHelper(int bin, int tryBin, std::set& set, const FFTDataCpx& spectrum); + const FFTDataCpx& spectrum; +}; + +inline void FrequencySets::adjustFrequencies() +{ + int tries = 0; + while (adjustFrequencies1()) { + ++tries; + } + //printf("adjust moved %d\n", tries); +} + +inline bool FrequencySets::adjustFreqHelper(int bin, int tryBin, std::set& set, const FFTDataCpx& spectrum) +{ + bool ret = false; + if (tryBin < 0 || tryBin >= spectrum.size()) { + return false; + } + const double db = AudioMath::db(spectrum.getAbs(bin)); + const double dbm1 = AudioMath::db(spectrum.getAbs(tryBin)); + if (dbm1 > (db + 3)) { // only adjust a bin if it's a 3db improvement + const double f = FFT::bin2Freq(bin, sampleRate, spectrum.size()); + const double fNew = FFT::bin2Freq(tryBin, sampleRate, spectrum.size()); + auto x = set.erase(f); + assert(x == 1); + set.insert(fNew); + ret = true; + } + + return ret; + +} + +inline bool FrequencySets::adjustFrequencies1() +{ + for (auto f : harmonics) { + const int bin = FFT::freqToBin(f, sampleRate, spectrum.size()); + if (adjustFreqHelper(bin, bin - 1, harmonics, spectrum)) + return true; + if (adjustFreqHelper(bin, bin + 1, harmonics, spectrum)) + return true; + } + for (auto f : alias) { + const int bin = FFT::freqToBin(f, sampleRate, spectrum.size()); + if (adjustFreqHelper(bin, bin - 1, alias, spectrum)) + return true; + if (adjustFreqHelper(bin, bin + 1, alias, spectrum)) + return true; + } + return false; +} + + +inline FrequencySets::FrequencySets(double fundamental, double sampleRate, const FFTDataCpx& spectrum) : + spectrum(spectrum) +{ + const double nyquist = sampleRate / 2; + bool done = false; + for (int i = 1; !done; ++i) { + double freq = fundamental * i; + if (freq < nyquist) { + //harmonics.push_back(freq); + harmonics.insert(freq); + } else { + double over = freq - nyquist; + if (over < nyquist) { + freq = nyquist - over; + //alias.push_back(freq); + alias.insert(freq); + } else { + done = true; + } + } + } +} + + +inline void expandHelper(double& maxDb, bool& done, int& i, int deltaI, const FFTDataCpx& spectrum, std::set& f) +{ + if (i >= spectrum.size() || i < 0) { + done = true; + } else { + const double db = AudioMath::db(spectrum.getAbs(i)); + + if (db < (maxDb + expandThresholdDb)) { + done = true; + } else { + //const double oldFreq = FFT::bin2Freq(i, sampleRate, spectrum.size()); + const double newFreq = FFT::bin2Freq(i, sampleRate, spectrum.size()); + if (newFreq < 900 && newFreq > 800) + printf("inserting new freq %f db=%f m=%f\n ", newFreq, db, maxDb); + maxDb = std::max(maxDb, db); + f.insert(newFreq); + } + } + i += deltaI; +} + +inline void FrequencySets::expandFrequencies(std::set& f, const FFTDataCpx& spectrum) +{ + assert(expandBins); + for (double freq : f) { + if (int(freq) == 1064) { + int x = 5; + } + const int bin = FFT::freqToBin(freq, sampleRate, spectrum.size()); + double maxDb = AudioMath::db(spectrum.getAbs(bin)); + + // search upward + bool done; + int i; + for (i = bin + 1, done = false; !done; ) { + expandHelper(maxDb, done, i, 1, spectrum, f); + } + + for (i = bin - 1, done = false; !done; ) { + expandHelper(maxDb, done, i, -1, spectrum, f); + } + } +} + +inline void FrequencySets::dump(const char *label) const +{ + printf("**** %s ****\n", label); + for (auto f : harmonics) { + int bin = FFT::freqToBin(f, sampleRate, spectrum.size()); + printf("harm at %.2f db:%.2f\n", f, AudioMath::db(spectrum.getAbs(bin))); + } + for (auto f : alias) { + int bin = FFT::freqToBin(f, sampleRate, spectrum.size()); + printf("alias at %.2f db:%.2f\n", f, AudioMath::db(spectrum.getAbs(bin))); + } +} + +inline void FrequencySets::expandFrequencies() +{ + //dump("before expand freq", spectrum); + expandFrequencies(harmonics, spectrum); + expandFrequencies(alias, spectrum); + + + //dump("after expand freq", spectrum); + assert(checkOverlap()); + +} + +inline bool FrequencySets::checkOverlap() const +{ + std::vector overlap; + + std::set_intersection(harmonics.begin(), harmonics.end(), + alias.begin(), alias.end(), + std::back_inserter(overlap)); + if (!overlap.empty()) { + for (auto x : overlap) { + printf("overlap at %f\n", x); + } + } + return overlap.empty(); +} + + +/* + + +Ra = 12194**2 * f**4 / +(f**2 + 20.6 ** 2) sqrt((f2 + 107.2**2)(f2 + 737.9**2)) * (f2 + 12194**2) +A(f) = db(Ra) + 2 +*/ +double WeightA(double mag, double f) +{ + double num = (12194 * 12194) * f*f*f*f; + double den = (f*f + 20.6*20.6) * sqrt((f*f + 107.2*107.2) * (f*f + 737.9 * 737.9)) * (f*f + 12194 * 12194); + double Ra = num / den; + // printf("Ra(%f) = %f\n", f, Ra); + return Ra * mag; +} + +void testAlias(std::function func, double fundamental, int numSamples) +{ + // printf("test alias fundamental=%f,%f,%f\n", fundamental, fundamental * 2, fundamental * 3); + FFTDataCpx spectrum(numSamples); + Analyzer::getSpectrum(spectrum, false, func); + FrequencySets frequencies(fundamental, sampleRate, spectrum); + assert(frequencies.checkOverlap()); + + // frequencies.dump("init freq"); + if (adjustBins) + frequencies.adjustFrequencies(); + // frequencies.dump("after adjust"); + assert(frequencies.checkOverlap()); + + if (expandBins) + frequencies.expandFrequencies(); + assert(frequencies.checkOverlap()); + + //frequencies.dump("final freq"); + + double totalSignal = 0; + double totalAlias = 0; + double totalSignalA = 0; + double totalAliasA = 0; + double totalAliasOver5 = 0; + double totalAliasBelow5 = 0; + + // let's look at every spectrum line + for (int i = 1; i < numSamples / 2; ++i) { + const double freq = FFT::bin2Freq(i, sampleRate, numSamples); + const double mag = spectrum.getAbs(i); + // const double db = AudioMath::db(mag); + const double magA = WeightA(mag, freq); + + const bool isH2 = frequencies.harmonics.find(freq) != frequencies.harmonics.end(); + const bool isA2 = frequencies.alias.find(freq) != frequencies.alias.end(); + + assert(!isA2 || !isH2); + + const bool above5k = (freq >= 5000); + // const bool above10k = (freq > 10000); + + if (isH2) { + totalSignal += mag; + totalSignalA += magA; + } + if (isA2) { + totalAlias += mag; + totalAliasA += magA; + if (above5k) { + totalAliasOver5 += mag; + } else { + totalAliasBelow5 += mag; + } + } + } + + printf("total sig = %f alias = %f ratiodb=%f A=%f\n", + totalSignal, + totalAlias, + AudioMath::db(totalAlias / totalSignal), + 2 + AudioMath::db(totalAliasA / totalSignalA) + ); +} + +void printHeader(const char * label, double desired, double actual) +{ + printf("\n%s freq = %f, round %f\n", label, desired, actual); + printf("frame size = %d, expandThreshDb=%f \n", numSamples, expandThresholdDb); + printf("expand bins=%d, adjustBins=%d\n", expandBins, adjustBins); +} + +template +void testRawSaw(double normalizedFreq) +{ + const int numSamples = 64 * 1024; + // adjust the freq to even + + double freq = Analyzer::makeEvenPeriod(sampleRate * normalizedFreq, sampleRate, numSamples); + printHeader("Raw Saw", sampleRate * normalizedFreq, freq); + + // make saw osc at correct freq + SawOscillatorParams params; + SawOscillatorState state; + SawOscillator::setFrequency(params, (float) normalizedFreq); + testAlias([&state, ¶ms]() { + return (T) 30 * SawOscillator::runSaw(state, params); + }, freq, numSamples); + +} + +static void testEven(double normalizedFreq) +{ + + // adjust the freq to even + double freq = Analyzer::makeEvenPeriod(sampleRate * normalizedFreq, sampleRate, numSamples); + printHeader("EvenVCO", sampleRate * normalizedFreq, freq); + + using EVCO = EvenVCO ; + EVCO vco; + vco._testFreq = float(sampleRate * normalizedFreq); + vco.outputs[EVCO::SAW_OUTPUT].active = true; + vco.outputs[EVCO::SINE_OUTPUT].active = false; + vco.outputs[EVCO::TRI_OUTPUT].active = false; + vco.outputs[EVCO::SQUARE_OUTPUT].active = false; + vco.outputs[EVCO::EVEN_OUTPUT].active = false; + + testAlias([&vco]() { + vco.step(); + return 3 * vco.outputs[EVCO::SAW_OUTPUT].value; + }, freq, numSamples); + + fflush(stdout); +} + + +static void testAliasFunOrig(double normalizedFreq) +{ + // adjust the freq to even + double freq = Analyzer::makeEvenPeriod(sampleRate * normalizedFreq, sampleRate, numSamples); + printHeader("FunOrig", sampleRate * normalizedFreq, freq); + + VoltageControlledOscillatorOrig<16, 16> vco; + vco.freq = float(sampleRate * normalizedFreq); + vco.sampleTime = 1.0f / sampleRate; + + testAlias([&vco]() { + const float deltaTime = 1.0f / sampleRate; + vco.process(deltaTime, 0); + return 15 * vco.saw(); + }, freq, numSamples); +} + + +static void testAliasFun(double normalizedFreq) +{ + // adjust the freq to even + double freq = Analyzer::makeEvenPeriod(sampleRate * normalizedFreq, sampleRate, numSamples); + printHeader("Fun Mine", sampleRate * normalizedFreq, freq); + + VoltageControlledOscillator<16, 16> vco; + vco.freq = float(sampleRate * normalizedFreq); + vco.sampleTime = 1.0f / sampleRate; + + vco.sawEnabled = true; + vco.sinEnabled = false; + vco.sqEnabled = false; + vco.triEnabled = false; + vco.init(); + + testAlias([&vco]() { + const float deltaTime = 1.0f / sampleRate; + vco.process(deltaTime, 0); + return 15 * vco.saw(); + }, freq, numSamples); +} + + +/* +First try: +desired freq = 844.180682, round 842.486572 +test alias fundamental=842.486572,1684.973145,2527.459717 +total sig = 3.239564 alias = 0.100040 ratiodb=-30.206276 + +desired freq = 1688.361365, round 1687.664795 +test alias fundamental=1687.664795,3375.329590,5062.994385 +total sig = 6.824180 alias = 0.158808 ratiodb=-32.663559 + +desired freq = 3376.722729, round 3375.329590 +test alias fundamental=3375.329590,6750.659180,10125.988770 +total sig = 3.512697 alias = 0.166856 ratiodb=-26.465975 +Test passed. Press any key to continue... + +--- + +EvenVCO freq = 844.180682, round 843.832397 +frame size = 65536, expandThreshDb=0.000000 +expand bins=0, adjustBins=1 +adjust moved 6939 +total sig = 0.015563 alias = 0.015805 ratiodb=0.133833 + +Raw Saw freq = 844.180682, round 843.832397 +frame size = 65536, expandThreshDb=0.000000 +expand bins=0, adjustBins=1 +adjust moved 392 +total sig = 0.166494 alias = 0.041957 ratiodb=-11.971910 + +Raw Saw freq = 1688.361365, round 1688.337708 +frame size = 65536, expandThreshDb=0.000000 +expand bins=0, adjustBins=1 +adjust moved 0 +total sig = 14.344683 alias = 1.279057 ratiodb=-20.996020 + +Raw Saw freq = 3376.722729, round 3376.675415 +frame size = 65536, expandThreshDb=0.000000 +expand bins=0, adjustBins=1 +adjust moved 0 +total sig = 10.917767 alias = 1.400212 ratiodb=-17.838803 +Test passed. Press any key to continue... + +---- + +EvenVCO freq = 844.180682, round 843.832397 +frame size = 65536, expandThreshDb=0.000000 +expand bins=1, adjustBins=1 +adjust moved 6939 +total sig = 0.015563 alias = 30.322415 ratiodb=65.793436 + +Raw Saw freq = 844.180682, round 843.832397 +frame size = 65536, expandThreshDb=0.000000 +expand bins=1, adjustBins=1 +adjust moved 392 +total sig = 0.166494 alias = 0.368204 ratiodb=6.893811 + +Raw Saw freq = 1688.361365, round 1688.337708 +frame size = 65536, expandThreshDb=0.000000 +expand bins=1, adjustBins=1 +adjust moved 0 +total sig = 14.344683 alias = 3.708226 ratiodb=-11.750495 + +Raw Saw freq = 3376.722729, round 3376.675415 +frame size = 65536, expandThreshDb=0.000000 +expand bins=1, adjustBins=1 +adjust moved 0 +total sig = 10.917767 alias = 3.809961 ratiodb=-9.144265 +Test passed. Press any key to continue... + + + + +*/ + +void testVCOAlias() +{ + testPitchQuantize(); + + + for (int i = 2; i <= 8; i *= 2) { + float f = 1.0f / (i * 6.53f); + // testEven(f); + // testRawSaw(f); + // testAliasFunOrig(f); + testAliasFun(f); + } +} \ No newline at end of file diff --git a/plugins/community/repos/squinkylabs-plug1/text.txt b/plugins/community/repos/squinkylabs-plug1/text.txt new file mode 100644 index 00000000..fb279cbb --- /dev/null +++ b/plugins/community/repos/squinkylabs-plug1/text.txt @@ -0,0 +1,1056 @@ +test1 + + +: step +: final out saw=-0.95 mb=0.00 smb = 0.00 tot=-0.95 +***** testSync3***** + +VCO1: step +VCO1: final out saw=-0.91 mb=0.00 smb = 0.00 tot=-0.91 + +VCO2: step +VCO2: final out saw=-0.80 mb=0.00 smb = 0.00 tot=-0.80 + +VCO3: step +VCO3: final out saw=-0.80 mb=0.00 smb = 0.00 tot=-0.80 +-------- sample 0 ----------- + +VCO1: step +VCO1: final out saw=-0.81 mb=0.00 smb = 0.00 tot=-0.81 + +VCO2: step +VCO2: final out saw=-0.60 mb=0.00 smb = 0.00 tot=-0.60 + +VCO3: step +VCO3: final out saw=-0.60 mb=0.00 smb = 0.00 tot=-0.60 +-------- sample 1 ----------- + +VCO1: step +VCO1: final out saw=-0.72 mb=0.00 smb = 0.00 tot=-0.72 + +VCO2: step +VCO2: final out saw=-0.40 mb=0.00 smb = 0.00 tot=-0.40 + +VCO3: step +VCO3: final out saw=-0.40 mb=0.00 smb = 0.00 tot=-0.40 +-------- sample 2 ----------- + +VCO1: step +VCO1: final out saw=-0.62 mb=0.00 smb = 0.00 tot=-0.62 + +VCO2: step +VCO2: final out saw=-0.20 mb=0.00 smb = 0.00 tot=-0.20 + +VCO3: step +VCO3: final out saw=-0.20 mb=0.00 smb = 0.00 tot=-0.20 +-------- sample 3 ----------- + +VCO1: step +VCO1: final out saw=-0.53 mb=0.00 smb = 0.00 tot=-0.53 + +VCO2: step +VCO2: final out saw=0.01 mb=0.00 smb = 0.00 tot=0.01 + +VCO3: step +VCO3: final out saw=0.01 mb=0.00 smb = 0.00 tot=0.01 +-------- sample 4 ----------- + +VCO1: step +VCO1: final out saw=-0.43 mb=0.00 smb = 0.00 tot=-0.43 + +VCO2: step +VCO2: final out saw=0.21 mb=0.00 smb = 0.00 tot=0.21 + +VCO3: step +VCO3: final out saw=0.21 mb=0.00 smb = 0.00 tot=0.21 +-------- sample 5 ----------- + +VCO1: step +VCO1: final out saw=-0.34 mb=0.00 smb = 0.00 tot=-0.34 + +VCO2: step +VCO2: final out saw=0.41 mb=0.00 smb = 0.00 tot=0.41 + +VCO3: step +VCO3: final out saw=0.41 mb=0.00 smb = 0.00 tot=0.41 +-------- sample 6 ----------- + +VCO1: step +VCO1: final out saw=-0.24 mb=0.00 smb = 0.00 tot=-0.24 + +VCO2: step +VCO2: final out saw=0.61 mb=0.00 smb = 0.00 tot=0.61 + +VCO3: step +VCO3: final out saw=0.61 mb=0.00 smb = 0.00 tot=0.61 +-------- sample 7 ----------- + +VCO1: step +VCO1: final out saw=-0.15 mb=0.00 smb = 0.00 tot=-0.15 + +VCO2: step +VCO2: final out saw=0.81 mb=0.00 smb = 0.00 tot=0.81 + +VCO3: step +VCO3: final out saw=0.81 mb=0.00 smb = 0.00 tot=0.81 +-------- sample 8 ----------- + +VCO1: step +VCO1: final out saw=-0.05 mb=0.00 smb = 0.00 tot=-0.05 + +VCO2: step +VCO2: phase wrap 1.01->0.01 cross=-0.06 jump=-2.00 +VCO2: final out saw=-0.99 mb=2.00 smb = 0.00 tot=1.01 + +VCO3: step +VCO3: phase wrap 1.01->0.01 cross=-0.06 jump=-2.00 +VCO3: final out saw=-0.99 mb=2.00 smb = 0.00 tot=1.01 +-------- sample 9 ----------- + +VCO1: step +VCO1: final out saw=0.04 mb=0.00 smb = 0.00 tot=0.04 + +VCO2: step +VCO2: final out saw=-0.79 mb=1.96 smb = 0.00 tot=1.18 + +VCO3: step +VCO3: final out saw=-0.79 mb=1.96 smb = 0.00 tot=1.18 +-------- sample 10 ----------- + +VCO1: step +VCO1: final out saw=0.14 mb=0.00 smb = 0.00 tot=0.14 + +VCO2: step +VCO2: final out saw=-0.59 mb=1.39 smb = 0.00 tot=0.80 + +VCO3: step +VCO3: final out saw=-0.59 mb=1.39 smb = 0.00 tot=0.80 +-------- sample 11 ----------- + +VCO1: step +VCO1: final out saw=0.23 mb=0.00 smb = 0.00 tot=0.23 + +VCO2: step +VCO2: final out saw=-0.39 mb=-0.09 smb = 0.00 tot=-0.48 + +VCO3: step +VCO3: final out saw=-0.39 mb=-0.09 smb = 0.00 tot=-0.48 +-------- sample 12 ----------- + +VCO1: step +VCO1: final out saw=0.33 mb=0.00 smb = 0.00 tot=0.33 + +VCO2: step +VCO2: final out saw=-0.18 mb=-0.20 smb = 0.00 tot=-0.39 + +VCO3: step +VCO3: final out saw=-0.18 mb=-0.20 smb = 0.00 tot=-0.39 +-------- sample 13 ----------- + +VCO1: step +VCO1: final out saw=0.42 mb=0.00 smb = 0.00 tot=0.42 + +VCO2: step +VCO2: final out saw=0.02 mb=0.21 smb = 0.00 tot=0.23 + +VCO3: step +VCO3: final out saw=0.02 mb=0.21 smb = 0.00 tot=0.23 +-------- sample 14 ----------- + +VCO1: step +VCO1: final out saw=0.52 mb=0.00 smb = 0.00 tot=0.52 + +VCO2: step +VCO2: final out saw=0.22 mb=-0.18 smb = 0.00 tot=0.04 + +VCO3: step +VCO3: final out saw=0.22 mb=-0.18 smb = 0.00 tot=0.04 +-------- sample 15 ----------- + +VCO1: step +VCO1: final out saw=0.61 mb=0.00 smb = 0.00 tot=0.61 + +VCO2: step +VCO2: final out saw=0.42 mb=0.13 smb = 0.00 tot=0.55 + +VCO3: step +VCO3: final out saw=0.42 mb=0.13 smb = 0.00 tot=0.55 +-------- sample 16 ----------- + +VCO1: step +VCO1: final out saw=0.71 mb=0.00 smb = 0.00 tot=0.71 + +VCO2: step +VCO2: final out saw=0.62 mb=-0.11 smb = 0.00 tot=0.51 + +VCO3: step +VCO3: final out saw=0.62 mb=-0.11 smb = 0.00 tot=0.51 +-------- sample 17 ----------- + +VCO1: step +VCO1: final out saw=0.80 mb=0.00 smb = 0.00 tot=0.80 + +VCO2: step +VCO2: final out saw=0.82 mb=0.07 smb = 0.00 tot=0.89 + +VCO3: step +VCO3: final out saw=0.82 mb=0.07 smb = 0.00 tot=0.89 +-------- sample 18 ----------- + +VCO1: step +VCO1: final out saw=0.90 mb=0.00 smb = 0.00 tot=0.90 + +VCO2: step +VCO2: phase wrap 1.01->0.01 cross=-0.11 jump=-2.00 +VCO2: final out saw=-0.98 mb=1.94 smb = 0.00 tot=0.96 + +VCO3: step +VCO3: phase wrap 1.01->0.01 cross=-0.11 jump=-2.00 +VCO3: final out saw=-0.98 mb=1.94 smb = 0.00 tot=0.96 +-------- sample 19 ----------- + +VCO1: step +VCO1: final out saw=0.99 mb=0.00 smb = 0.00 tot=0.99 + +VCO2: step +VCO2: final out saw=-0.78 mb=1.99 smb = 0.00 tot=1.22 + +VCO3: step +VCO3: final out saw=-0.78 mb=1.99 smb = 0.00 tot=1.22 +-------- sample 20 ----------- + +VCO1: step +VCO1: phase wrap 1.04->0.04 cross=-0.93 jump=-2.00 +VCO1: calling slave with -0.93 +VCO1: final out saw=-0.91 mb=1.98 smb = 0.00 tot=1.07 + +VCO2: step +VCO2: got sync ph=0.21 nph=0.59 excess=0.09 send cross -0.93 jump 0.76 + +VCO3: step +VCO3: final out saw=-0.58 mb=1.29 smb = 0.00 tot=0.71 +-------- sample 21 ----------- + +VCO1: step +VCO1: final out saw=-0.82 mb=1.53 smb = 0.00 tot=0.71 + +VCO2: step +VCO2: final out saw=0.39 mb=1.29 smb = -0.75 tot=0.92 + +VCO3: step +VCO3: final out saw=-0.37 mb=-0.14 smb = 0.00 tot=-0.51 +-------- sample 22 ----------- + +VCO1: step +VCO1: final out saw=-0.72 mb=0.08 smb = 0.00 tot=-0.65 + +VCO2: step +VCO2: final out saw=0.59 mb=-0.14 smb = -0.58 tot=-0.13 + +VCO3: step +VCO3: final out saw=-0.17 mb=-0.18 smb = 0.00 tot=-0.35 +-------- sample 23 ----------- + +VCO1: step +VCO1: final out saw=-0.63 mb=-0.30 smb = 0.00 tot=-0.92 + +VCO2: step +VCO2: final out saw=0.79 mb=-0.18 smb = -0.03 tot=0.59 + +VCO3: step +VCO3: final out saw=0.03 mb=0.19 smb = 0.00 tot=0.22 +-------- sample 24 ----------- + +VCO1: step +VCO1: final out saw=-0.53 mb=0.24 smb = 0.00 tot=-0.29 + +VCO2: step +VCO2: final out saw=0.99 mb=0.19 smb = 0.11 tot=1.30 + +VCO3: step +VCO3: final out saw=0.23 mb=-0.19 smb = 0.00 tot=0.04 +-------- sample 25 ----------- + +VCO1: step +VCO1: final out saw=-0.44 mb=-0.18 smb = 0.00 tot=-0.62 + +VCO2: step +VCO2: phase wrap 1.10->0.10 cross=-0.96 jump=-2.00 +VCO2: final out saw=-0.81 mb=1.79 smb = -0.09 tot=0.89 + +VCO3: step +VCO3: final out saw=0.43 mb=0.13 smb = 0.00 tot=0.56 +-------- sample 26 ----------- + +VCO1: step +VCO1: final out saw=-0.34 mb=0.12 smb = 0.00 tot=-0.22 + +VCO2: step +VCO2: final out saw=-0.61 mb=1.63 smb = 0.07 tot=1.10 + +VCO3: step +VCO3: final out saw=0.63 mb=-0.10 smb = 0.00 tot=0.54 +-------- sample 27 ----------- + +VCO1: step +VCO1: final out saw=-0.25 mb=-0.09 smb = 0.00 tot=-0.34 + +VCO2: step +VCO2: final out saw=-0.41 mb=-0.06 smb = -0.04 tot=-0.51 + +VCO3: step +VCO3: final out saw=0.83 mb=0.11 smb = 0.00 tot=0.94 +-------- sample 28 ----------- + +VCO1: step +VCO1: final out saw=-0.15 mb=0.05 smb = 0.00 tot=-0.10 + +VCO2: step +VCO2: final out saw=-0.20 mb=-0.17 smb = 0.03 tot=-0.34 + +VCO3: step +VCO3: phase wrap 1.02->0.02 cross=-0.17 jump=-2.00 +VCO3: final out saw=-0.97 mb=1.90 smb = 0.00 tot=0.94 +-------- sample 29 ----------- + +VCO1: step +VCO1: final out saw=-0.06 mb=-0.04 smb = 0.00 tot=-0.10 + +VCO2: step +VCO2: final out saw=-0.00 mb=0.14 smb = -0.02 tot=0.12 + +VCO3: step +VCO3: final out saw=-0.76 mb=2.00 smb = 0.00 tot=1.24 +-------- sample 30 ----------- + +VCO1: step +VCO1: final out saw=0.04 mb=0.02 smb = 0.00 tot=0.06 + +VCO2: step +VCO2: final out saw=0.20 mb=-0.13 smb = 0.02 tot=0.09 + +VCO3: step +VCO3: final out saw=-0.56 mb=1.20 smb = 0.00 tot=0.64 +-------- sample 31 ----------- + +VCO1: step +VCO1: final out saw=0.13 mb=-0.02 smb = 0.00 tot=0.11 + +VCO2: step +VCO2: final out saw=0.40 mb=0.07 smb = -0.01 tot=0.47 + +VCO3: step +VCO3: final out saw=-0.36 mb=-0.20 smb = 0.00 tot=-0.56 +-------- sample 32 ----------- + +VCO1: step +VCO1: final out saw=0.23 mb=0.01 smb = 0.00 tot=0.23 + +VCO2: step +VCO2: final out saw=0.60 mb=-0.07 smb = 0.01 tot=0.54 + +VCO3: step +VCO3: final out saw=-0.16 mb=-0.13 smb = 0.00 tot=-0.29 +-------- sample 33 ----------- + +VCO1: step +VCO1: final out saw=0.32 mb=-0.01 smb = 0.00 tot=0.31 + +VCO2: step +VCO2: final out saw=0.80 mb=0.03 smb = -0.00 tot=0.83 + +VCO3: step +VCO3: final out saw=0.04 mb=0.16 smb = 0.00 tot=0.20 +-------- sample 34 ----------- + +VCO1: step +VCO1: final out saw=0.42 mb=0.00 smb = 0.00 tot=0.42 + +VCO2: step +VCO2: phase wrap 1.00->0.00 cross=-0.01 jump=-2.00 +VCO2: final out saw=-1.00 mb=1.96 smb = 0.00 tot=0.96 + +VCO3: step +VCO3: final out saw=0.24 mb=-0.17 smb = 0.00 tot=0.07 +-------- sample 35 ----------- + +VCO1: step +VCO1: final out saw=0.51 mb=-0.01 smb = 0.00 tot=0.50 + +VCO2: step +VCO2: final out saw=-0.80 mb=1.98 smb = -0.00 tot=1.19 + +VCO3: step +VCO3: final out saw=0.44 mb=0.13 smb = 0.00 tot=0.57 +-------- sample 36 ----------- + +VCO1: step +VCO1: final out saw=0.61 mb=-0.00 smb = 0.00 tot=0.61 + +VCO2: step +VCO2: final out saw=-0.59 mb=1.41 smb = 0.00 tot=0.82 + +VCO3: step +VCO3: final out saw=0.64 mb=-0.09 smb = 0.00 tot=0.55 +-------- sample 37 ----------- + +VCO1: step +VCO1: final out saw=0.70 mb=0.01 smb = 0.00 tot=0.71 + +VCO2: step +VCO2: final out saw=-0.39 mb=-0.01 smb = 0.00 tot=-0.41 + +VCO3: step +VCO3: final out saw=0.84 mb=0.11 smb = 0.00 tot=0.95 +-------- sample 38 ----------- + +VCO1: step +VCO1: final out saw=0.80 mb=0.04 smb = 0.00 tot=0.83 + +VCO2: step +VCO2: final out saw=-0.19 mb=-0.22 smb = -0.00 tot=-0.42 + +VCO3: step +VCO3: phase wrap 1.02->0.02 cross=-0.22 jump=-2.00 +VCO3: final out saw=-0.95 mb=1.90 smb = 0.00 tot=0.94 +-------- sample 39 ----------- + +VCO1: step +VCO1: final out saw=0.89 mb=-0.02 smb = 0.00 tot=0.87 + +VCO2: step +VCO2: final out saw=0.01 mb=0.19 smb = -0.01 tot=0.19 + +VCO3: step +VCO3: final out saw=-0.75 mb=1.99 smb = 0.00 tot=1.24 +-------- sample 40 ----------- + +VCO1: step +VCO1: final out saw=0.99 mb=0.01 smb = 0.00 tot=0.99 + +VCO2: step +VCO2: final out saw=0.21 mb=-0.18 smb = 0.01 tot=0.04 + +VCO3: step +VCO3: final out saw=-0.55 mb=1.12 smb = 0.00 tot=0.57 +-------- sample 41 ----------- + +VCO1: step +VCO1: phase wrap 1.04->0.04 cross=-0.86 jump=-2.00 +VCO1: calling slave with -0.86 +VCO1: final out saw=-0.92 mb=1.98 smb = 0.00 tot=1.06 + +VCO2: step +VCO2: got sync ph=0.71 nph=0.59 excess=0.09 send cross -0.86 jump -0.24 + +VCO3: step +VCO3: final out saw=-0.35 mb=-0.24 smb = 0.00 tot=-0.60 +-------- sample 42 ----------- + +VCO1: step +VCO1: final out saw=-0.82 mb=1.59 smb = 0.00 tot=0.76 + +VCO2: step +VCO2: final out saw=0.37 mb=0.12 smb = 0.23 tot=0.73 + +VCO3: step +VCO3: final out saw=-0.15 mb=-0.09 smb = 0.00 tot=-0.24 +-------- sample 43 ----------- + +VCO1: step +VCO1: final out saw=-0.73 mb=0.18 smb = 0.00 tot=-0.54 + +VCO2: step +VCO2: final out saw=0.58 mb=-0.09 smb = 0.19 tot=0.68 + +VCO3: step +VCO3: final out saw=0.05 mb=0.14 smb = 0.00 tot=0.19 +-------- sample 44 ----------- + +VCO1: step +VCO1: final out saw=-0.63 mb=-0.35 smb = 0.00 tot=-0.98 + +VCO2: step +VCO2: final out saw=0.78 mb=0.10 smb = 0.02 tot=0.90 + +VCO3: step +VCO3: final out saw=0.25 mb=-0.16 smb = 0.00 tot=0.09 +-------- sample 45 ----------- + +VCO1: step +VCO1: final out saw=-0.54 mb=0.25 smb = 0.00 tot=-0.28 + +VCO2: step +VCO2: final out saw=0.98 mb=-0.08 smb = -0.04 tot=0.85 + +VCO3: step +VCO3: final out saw=0.45 mb=0.12 smb = 0.00 tot=0.58 +-------- sample 46 ----------- + +VCO1: step +VCO1: final out saw=-0.44 mb=-0.18 smb = 0.00 tot=-0.63 + +VCO2: step +VCO2: phase wrap 1.09->0.09 cross=-0.89 jump=-2.00 +VCO2: final out saw=-0.82 mb=2.02 smb = 0.03 tot=1.23 + +VCO3: step +VCO3: final out saw=0.65 mb=-0.09 smb = 0.00 tot=0.56 +-------- sample 47 ----------- + +VCO1: step +VCO1: final out saw=-0.35 mb=0.11 smb = 0.00 tot=-0.24 + +VCO2: step +VCO2: final out saw=-0.62 mb=1.53 smb = -0.02 tot=0.89 + +VCO3: step +VCO3: final out saw=0.86 mb=0.10 smb = 0.00 tot=0.96 +-------- sample 48 ----------- + +VCO1: step +VCO1: final out saw=-0.25 mb=-0.08 smb = 0.00 tot=-0.33 + +VCO2: step +VCO2: final out saw=-0.42 mb=0.14 smb = 0.02 tot=-0.26 + +VCO3: step +VCO3: phase wrap 1.03->0.03 cross=-0.28 jump=-2.00 +VCO3: final out saw=-0.94 mb=1.89 smb = 0.00 tot=0.95 +-------- sample 49 ----------- + +VCO1: step +VCO1: final out saw=-0.16 mb=0.04 smb = 0.00 tot=-0.12 + +VCO2: step +VCO2: final out saw=-0.22 mb=-0.34 smb = -0.01 tot=-0.57 + +VCO3: step +VCO3: final out saw=-0.74 mb=1.98 smb = 0.00 tot=1.24 +-------- sample 50 ----------- + +VCO1: step +VCO1: final out saw=-0.06 mb=-0.03 smb = 0.00 tot=-0.10 + +VCO2: step +VCO2: final out saw=-0.02 mb=0.24 smb = 0.01 tot=0.23 + +VCO3: step +VCO3: final out saw=-0.54 mb=1.03 smb = 0.00 tot=0.49 +-------- sample 51 ----------- + +VCO1: step +VCO1: final out saw=0.03 mb=0.01 smb = 0.00 tot=0.04 + +VCO2: step +VCO2: final out saw=0.18 mb=-0.19 smb = -0.00 tot=-0.01 + +VCO3: step +VCO3: final out saw=-0.34 mb=-0.29 smb = 0.00 tot=-0.63 +-------- sample 52 ----------- + +VCO1: step +VCO1: final out saw=0.13 mb=-0.01 smb = 0.00 tot=0.11 + +VCO2: step +VCO2: final out saw=0.39 mb=0.10 smb = 0.00 tot=0.49 + +VCO3: step +VCO3: final out saw=-0.14 mb=-0.05 smb = 0.00 tot=-0.19 +-------- sample 53 ----------- + +VCO1: step +VCO1: final out saw=0.22 mb=-0.00 smb = 0.00 tot=0.22 + +VCO2: step +VCO2: final out saw=0.59 mb=-0.06 smb = -0.00 tot=0.52 + +VCO3: step +VCO3: final out saw=0.06 mb=0.11 smb = 0.00 tot=0.17 +-------- sample 54 ----------- + +VCO1: step +VCO1: final out saw=0.32 mb=-0.01 smb = 0.00 tot=0.31 + +VCO2: step +VCO2: final out saw=0.79 mb=0.07 smb = -0.00 tot=0.86 + +VCO3: step +VCO3: final out saw=0.26 mb=-0.15 smb = 0.00 tot=0.12 +-------- sample 55 ----------- + +VCO1: step +VCO1: final out saw=0.41 mb=-0.00 smb = 0.00 tot=0.41 + +VCO2: step +VCO2: final out saw=0.99 mb=-0.06 smb = -0.00 tot=0.93 + +VCO3: step +VCO3: final out saw=0.46 mb=0.11 smb = 0.00 tot=0.58 +-------- sample 56 ----------- + +VCO1: step +VCO1: final out saw=0.51 mb=-0.01 smb = 0.00 tot=0.50 + +VCO2: step +VCO2: phase wrap 1.09->0.09 cross=-0.94 jump=-2.00 +VCO2: final out saw=-0.81 mb=2.00 smb = -0.00 tot=1.19 + +VCO3: step +VCO3: final out saw=0.67 mb=-0.09 smb = 0.00 tot=0.58 +-------- sample 57 ----------- + +VCO1: step +VCO1: final out saw=0.60 mb=-0.00 smb = 0.00 tot=0.60 + +VCO2: step +VCO2: final out saw=-0.61 mb=1.49 smb = -0.00 tot=0.88 + +VCO3: step +VCO3: final out saw=0.87 mb=0.10 smb = 0.00 tot=0.97 +-------- sample 58 ----------- + +VCO1: step +VCO1: final out saw=0.70 mb=0.01 smb = 0.00 tot=0.71 + +VCO2: step +VCO2: final out saw=-0.41 mb=0.05 smb = -0.00 tot=-0.35 + +VCO3: step +VCO3: phase wrap 1.03->0.03 cross=-0.34 jump=-2.00 +VCO3: final out saw=-0.93 mb=1.89 smb = 0.00 tot=0.96 +-------- sample 59 ----------- + +VCO1: step +VCO1: final out saw=0.79 mb=0.04 smb = 0.00 tot=0.83 + +VCO2: step +VCO2: final out saw=-0.21 mb=-0.29 smb = 0.00 tot=-0.50 + +VCO3: step +VCO3: final out saw=-0.73 mb=1.97 smb = 0.00 tot=1.24 +-------- sample 60 ----------- + +VCO1: step +VCO1: final out saw=0.89 mb=-0.02 smb = 0.00 tot=0.87 + +VCO2: step +VCO2: final out saw=-0.01 mb=0.23 smb = 0.00 tot=0.23 + +VCO3: step +VCO3: final out saw=-0.53 mb=0.95 smb = 0.00 tot=0.42 +-------- sample 61 ----------- + +VCO1: step +VCO1: final out saw=0.98 mb=0.00 smb = 0.00 tot=0.98 + +VCO2: step +VCO2: final out saw=0.20 mb=-0.19 smb = -0.00 tot=0.01 + +VCO3: step +VCO3: final out saw=-0.33 mb=-0.32 smb = 0.00 tot=-0.65 +-------- sample 62 ----------- + +VCO1: step +VCO1: phase wrap 1.04->0.04 cross=-0.79 jump=-2.00 +VCO1: calling slave with -0.79 +VCO1: final out saw=-0.93 mb=1.99 smb = 0.00 tot=1.07 + +VCO2: step +VCO2: got sync ph=0.70 nph=0.58 excess=0.08 send cross -0.79 jump -0.24 + +VCO3: step +VCO3: final out saw=-0.13 mb=-0.00 smb = 0.00 tot=-0.13 +-------- sample 63 ----------- + +VCO1: step +VCO1: final out saw=-0.83 mb=1.65 smb = 0.00 tot=0.82 + +VCO2: step +VCO2: final out saw=0.36 mb=0.11 smb = 0.24 tot=0.71 + +VCO3: step +VCO3: final out saw=0.07 mb=0.08 smb = 0.00 tot=0.16 +-------- sample 64 ----------- + +VCO1: step +VCO1: final out saw=-0.74 mb=0.29 smb = 0.00 tot=-0.44 + +VCO2: step +VCO2: final out saw=0.56 mb=-0.08 smb = 0.20 tot=0.68 + +VCO3: step +VCO3: final out saw=0.27 mb=-0.13 smb = 0.00 tot=0.15 +-------- sample 65 ----------- + +VCO1: step +VCO1: final out saw=-0.64 mb=-0.39 smb = 0.00 tot=-1.03 + +VCO2: step +VCO2: final out saw=0.76 mb=0.09 smb = 0.03 tot=0.88 + +VCO3: step +VCO3: final out saw=0.48 mb=0.10 smb = 0.00 tot=0.58 +-------- sample 66 ----------- + +VCO1: step +VCO1: final out saw=-0.55 mb=0.25 smb = 0.00 tot=-0.29 + +VCO2: step +VCO2: final out saw=0.96 mb=-0.07 smb = -0.04 tot=0.85 + +VCO3: step +VCO3: final out saw=0.68 mb=-0.08 smb = 0.00 tot=0.60 +-------- sample 67 ----------- + +VCO1: step +VCO1: final out saw=-0.45 mb=-0.17 smb = 0.00 tot=-0.62 + +VCO2: step +VCO2: phase wrap 1.08->0.08 cross=-0.82 jump=-2.00 +VCO2: final out saw=-0.84 mb=2.01 smb = 0.03 tot=1.20 + +VCO3: step +VCO3: final out saw=0.88 mb=0.09 smb = 0.00 tot=0.97 +-------- sample 68 ----------- + +VCO1: step +VCO1: final out saw=-0.36 mb=0.09 smb = 0.00 tot=-0.27 + +VCO2: step +VCO2: final out saw=-0.63 mb=1.61 smb = -0.02 tot=0.96 + +VCO3: step +VCO3: phase wrap 1.04->0.04 cross=-0.39 jump=-2.00 +VCO3: final out saw=-0.92 mb=1.89 smb = 0.00 tot=0.97 +-------- sample 69 ----------- + +VCO1: step +VCO1: final out saw=-0.26 mb=-0.06 smb = 0.00 tot=-0.32 + +VCO2: step +VCO2: final out saw=-0.43 mb=0.24 smb = 0.01 tot=-0.19 + +VCO3: step +VCO3: final out saw=-0.72 mb=1.95 smb = 0.00 tot=1.23 +-------- sample 70 ----------- + +VCO1: step +VCO1: final out saw=-0.17 mb=0.02 smb = 0.00 tot=-0.14 + +VCO2: step +VCO2: final out saw=-0.23 mb=-0.37 smb = -0.01 tot=-0.61 + +VCO3: step +VCO3: final out saw=-0.52 mb=0.86 smb = 0.00 tot=0.34 +-------- sample 71 ----------- + +VCO1: step +VCO1: final out saw=-0.07 mb=-0.02 smb = 0.00 tot=-0.09 + +VCO2: step +VCO2: final out saw=-0.03 mb=0.24 smb = 0.00 tot=0.21 + +VCO3: step +VCO3: final out saw=-0.32 mb=-0.35 smb = 0.00 tot=-0.67 +-------- sample 72 ----------- + +VCO1: step +VCO1: final out saw=0.02 mb=-0.00 smb = 0.00 tot=0.02 + +VCO2: step +VCO2: final out saw=0.17 mb=-0.17 smb = -0.00 tot=0.00 + +VCO3: step +VCO3: final out saw=-0.12 mb=0.04 smb = 0.00 tot=-0.08 +-------- sample 73 ----------- + +VCO1: step +VCO1: final out saw=0.12 mb=-0.00 smb = 0.00 tot=0.12 + +VCO2: step +VCO2: final out saw=0.37 mb=0.08 smb = -0.00 tot=0.45 + +VCO3: step +VCO3: final out saw=0.08 mb=0.05 smb = 0.00 tot=0.14 +-------- sample 74 ----------- + +VCO1: step +VCO1: final out saw=0.21 mb=-0.01 smb = 0.00 tot=0.21 + +VCO2: step +VCO2: final out saw=0.57 mb=-0.04 smb = -0.00 tot=0.53 + +VCO3: step +VCO3: final out saw=0.29 mb=-0.11 smb = 0.00 tot=0.18 +-------- sample 75 ----------- + +VCO1: step +VCO1: final out saw=0.31 mb=-0.00 smb = 0.00 tot=0.31 + +VCO2: step +VCO2: final out saw=0.77 mb=0.06 smb = -0.00 tot=0.83 + +VCO3: step +VCO3: final out saw=0.49 mb=0.09 smb = 0.00 tot=0.58 +-------- sample 76 ----------- + +VCO1: step +VCO1: final out saw=0.40 mb=-0.01 smb = 0.00 tot=0.40 + +VCO2: step +VCO2: final out saw=0.97 mb=-0.04 smb = -0.00 tot=0.93 + +VCO3: step +VCO3: final out saw=0.69 mb=-0.07 smb = 0.00 tot=0.62 +-------- sample 77 ----------- + +VCO1: step +VCO1: final out saw=0.50 mb=-0.00 smb = 0.00 tot=0.50 + +VCO2: step +VCO2: phase wrap 1.09->0.09 cross=-0.87 jump=-2.00 +VCO2: final out saw=-0.82 mb=1.99 smb = -0.00 tot=1.16 + +VCO3: step +VCO3: final out saw=0.89 mb=0.08 smb = 0.00 tot=0.97 +-------- sample 78 ----------- + +VCO1: step +VCO1: final out saw=0.59 mb=-0.01 smb = 0.00 tot=0.59 + +VCO2: step +VCO2: final out saw=-0.62 mb=1.58 smb = -0.00 tot=0.95 + +VCO3: step +VCO3: phase wrap 1.05->0.05 cross=-0.45 jump=-2.00 +VCO3: final out saw=-0.91 mb=1.90 smb = 0.00 tot=0.99 +-------- sample 79 ----------- + +VCO1: step +VCO1: final out saw=0.69 mb=0.01 smb = 0.00 tot=0.70 + +VCO2: step +VCO2: final out saw=-0.42 mb=0.14 smb = -0.00 tot=-0.28 + +VCO3: step +VCO3: final out saw=-0.71 mb=1.93 smb = 0.00 tot=1.22 +-------- sample 80 ----------- + +VCO1: step +VCO1: final out saw=0.78 mb=0.04 smb = 0.00 tot=0.82 + +VCO2: step +VCO2: final out saw=-0.22 mb=-0.33 smb = 0.00 tot=-0.55 + +VCO3: step +VCO3: final out saw=-0.51 mb=0.77 smb = 0.00 tot=0.26 +-------- sample 81 ----------- + +VCO1: step +VCO1: final out saw=0.88 mb=-0.02 smb = 0.00 tot=0.86 + +VCO2: step +VCO2: final out saw=-0.02 mb=0.23 smb = 0.00 tot=0.22 + +VCO3: step +VCO3: final out saw=-0.31 mb=-0.37 smb = 0.00 tot=-0.68 +-------- sample 82 ----------- + +VCO1: step +VCO1: final out saw=0.97 mb=-0.00 smb = 0.00 tot=0.97 + +VCO2: step +VCO2: final out saw=0.18 mb=-0.17 smb = -0.00 tot=0.01 + +VCO3: step +VCO3: final out saw=-0.10 mb=0.07 smb = 0.00 tot=-0.03 +-------- sample 83 ----------- + +VCO1: step +VCO1: phase wrap 1.03->0.03 cross=-0.72 jump=-2.00 +VCO1: calling slave with -0.72 +VCO1: final out saw=-0.93 mb=2.00 smb = 0.00 tot=1.07 + +VCO2: step +VCO2: got sync ph=0.69 nph=0.57 excess=0.07 send cross -0.72 jump -0.24 + +VCO3: step +VCO3: final out saw=0.10 mb=0.02 smb = 0.00 tot=0.11 +-------- sample 84 ----------- + +VCO1: step +VCO1: final out saw=-0.84 mb=1.70 smb = 0.00 tot=0.86 + +VCO2: step +VCO2: final out saw=0.35 mb=0.09 smb = 0.24 tot=0.67 + +VCO3: step +VCO3: final out saw=0.30 mb=-0.08 smb = 0.00 tot=0.21 +-------- sample 85 ----------- + +VCO1: step +VCO1: final out saw=-0.74 mb=0.41 smb = 0.00 tot=-0.33 + +VCO2: step +VCO2: final out saw=0.55 mb=-0.06 smb = 0.20 tot=0.69 + +VCO3: step +VCO3: final out saw=0.50 mb=0.07 smb = 0.00 tot=0.57 +-------- sample 86 ----------- + +VCO1: step +VCO1: final out saw=-0.65 mb=-0.42 smb = 0.00 tot=-1.06 + +VCO2: step +VCO2: final out saw=0.75 mb=0.07 smb = 0.05 tot=0.86 + +VCO3: step +VCO3: final out saw=0.70 mb=-0.05 smb = 0.00 tot=0.65 +-------- sample 87 ----------- + +VCO1: step +VCO1: final out saw=-0.55 mb=0.24 smb = 0.00 tot=-0.31 + +VCO2: step +VCO2: final out saw=0.95 mb=-0.05 smb = -0.05 tot=0.85 + +VCO3: step +VCO3: final out saw=0.90 mb=0.07 smb = 0.00 tot=0.97 +-------- sample 88 ----------- + +VCO1: step +VCO1: final out saw=-0.46 mb=-0.14 smb = 0.00 tot=-0.60 + +VCO2: step +VCO2: phase wrap 1.08->0.08 cross=-0.75 jump=-2.00 +VCO2: final out saw=-0.85 mb=1.99 smb = 0.03 tot=1.17 + +VCO3: step +VCO3: phase wrap 1.05->0.05 cross=-0.51 jump=-2.00 +VCO3: final out saw=-0.90 mb=1.90 smb = 0.00 tot=1.01 +-------- sample 89 ----------- + +VCO1: step +VCO1: final out saw=-0.36 mb=0.06 smb = 0.00 tot=-0.30 + +VCO2: step +VCO2: final out saw=-0.65 mb=1.68 smb = -0.01 tot=1.02 + +VCO3: step +VCO3: final out saw=-0.70 mb=1.90 smb = 0.00 tot=1.20 +-------- sample 90 ----------- + +VCO1: step +VCO1: final out saw=-0.27 mb=-0.04 smb = 0.00 tot=-0.30 + +VCO2: step +VCO2: final out saw=-0.45 mb=0.34 smb = 0.01 tot=-0.10 + +VCO3: step +VCO3: final out saw=-0.50 mb=0.68 smb = 0.00 tot=0.19 +-------- sample 91 ----------- + +VCO1: step +VCO1: final out saw=-0.17 mb=0.00 smb = 0.00 tot=-0.17 + +VCO2: step +VCO2: final out saw=-0.25 mb=-0.39 smb = -0.00 tot=-0.64 + +VCO3: step +VCO3: final out saw=-0.29 mb=-0.38 smb = 0.00 tot=-0.68 +-------- sample 92 ----------- + +VCO1: step +VCO1: final out saw=-0.08 mb=-0.00 smb = 0.00 tot=-0.08 + +VCO2: step +VCO2: final out saw=-0.05 mb=0.23 smb = -0.00 tot=0.18 + +VCO3: step +VCO3: final out saw=-0.09 mb=0.11 smb = 0.00 tot=0.01 +-------- sample 93 ----------- + +VCO1: step +VCO1: final out saw=0.02 mb=-0.01 smb = 0.00 tot=0.00 + +VCO2: step +VCO2: final out saw=0.16 mb=-0.14 smb = 0.00 tot=0.02 + +VCO3: step +VCO3: final out saw=0.11 mb=-0.01 smb = 0.00 tot=0.09 +-------- sample 94 ----------- + +VCO1: step +VCO1: final out saw=0.11 mb=0.01 smb = 0.00 tot=0.12 + +VCO2: step +VCO2: final out saw=0.36 mb=0.05 smb = -0.00 tot=0.41 + +VCO3: step +VCO3: final out saw=0.31 mb=-0.06 smb = 0.00 tot=0.25 +-------- sample 95 ----------- + +VCO1: step +VCO1: final out saw=0.21 mb=-0.02 smb = 0.00 tot=0.19 + +VCO2: step +VCO2: final out saw=0.56 mb=-0.02 smb = 0.00 tot=0.54 + +VCO3: step +VCO3: final out saw=0.51 mb=0.06 smb = 0.00 tot=0.57 +-------- sample 96 ----------- + +VCO1: step +VCO1: final out saw=0.30 mb=0.01 smb = 0.00 tot=0.31 + +VCO2: step +VCO2: final out saw=0.76 mb=0.04 smb = -0.00 tot=0.79 + +VCO3: step +VCO3: final out saw=0.71 mb=-0.04 smb = 0.00 tot=0.67 +-------- sample 97 ----------- + +VCO1: step +VCO1: final out saw=0.40 mb=-0.01 smb = 0.00 tot=0.38 + +VCO2: step +VCO2: final out saw=0.96 mb=-0.02 smb = 0.00 tot=0.94 + +VCO3: step +VCO3: final out saw=0.91 mb=0.06 smb = 0.00 tot=0.97 +-------- sample 98 ----------- + +VCO1: step +VCO1: final out saw=0.49 mb=0.00 smb = 0.00 tot=0.49 + +VCO2: step +VCO2: phase wrap 1.08->0.08 cross=-0.80 jump=-2.00 +VCO2: final out saw=-0.84 mb=1.97 smb = -0.00 tot=1.13 + +VCO3: step +VCO3: phase wrap 1.06->0.06 cross=-0.56 jump=-2.00 +VCO3: final out saw=-0.89 mb=1.91 smb = 0.00 tot=1.03 +-------- sample 99 ----------- + +VCO1: step +VCO1: final out saw=0.59 mb=-0.01 smb = 0.00 tot=0.58 + +VCO2: step +VCO2: final out saw=-0.64 mb=1.65 smb = 0.00 tot=1.01 + +VCO3: step +VCO3: final out saw=-0.69 mb=1.87 smb = 0.00 tot=1.18 +0 ret = 0.000000 +1 ret = 1.000000 +-1 ret = -0.200000 +running testSplineExtremes +extreme test starting for shaper. test output .... +extreme test done +extreme test starting for shifter. test output .... +extreme test done +extreme test starting for vocal filter. .... +extreme test done +extreme test starting for vocal animator. .... +extreme test done +Tests passed. diff --git a/vst2_bin/CHANGELOG_VST.txt b/vst2_bin/CHANGELOG_VST.txt index b4564503..a54bcaa8 100644 --- a/vst2_bin/CHANGELOG_VST.txt +++ b/vst2_bin/CHANGELOG_VST.txt @@ -9,9 +9,20 @@ - add module dBiz.SuHa - add module dBiz.TROSC - add module dBiz.Util2 -o add module AudibleInstruments.Marbles -o add module AudibleInstruments.Plaits -o add module AudibleInstruments.Stages +- add module AudibleInstruments.Marbles +- add module AudibleInstruments.Plaits +- add module AudibleInstruments.Stages +- add module SquinkyLabs.Blank (crash) +- add module SquinkyLabs.CHB +- add module SquinkyLabs.DG +- add module SquinkyLabs.EV3 +- add module SquinkyLabs.EV +- add module SquinkyLabs.FunV (crash) +- add module SquinkyLabs.GMR +- add module SquinkyLabs.Gray +- add module SquinkyLabs.LFN +- add module SquinkyLabs.Shaper (crash) +- add module SquinkyLabs.Super ** October 12th, 2018 diff --git a/vst2_bin/log.txt b/vst2_bin/log.txt index d084a47e..2392f86d 100644 --- a/vst2_bin/log.txt +++ b/vst2_bin/log.txt @@ -1,124 +1,145 @@ [0.000 info src/main.cpp:67] VeeSeeVST Rack 0.6.1 [0.000 info src/main.cpp:70] Global directory: f:\git\VeeSeeVSTRack\vst2_bin\/ -[0.001 info src/main.cpp:71] Local directory: f:\git\VeeSeeVSTRack\vst2_bin\/ -[0.002 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin 21kHz 0.6.1 -[0.002 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin AmalgamatedHarmonics 0.6.1 -[0.003 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Alikins 0.6.1 -[0.003 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin alto777_LFSR 0.6.1 -[0.004 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin AS 0.6.9 -[0.004 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin AudibleInstruments 0.6.3 -[0.004 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Autodafe 0.6.1 -[0.005 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin BaconMusic 0.6.1 -[0.006 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Befaco 0.6.1 -[0.006 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Bidoo 0.6.1 -[0.006 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Bogaudio 0.6.7 -[0.006 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin CastleRocktronics 0.6.1 -[0.007 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin cf 0.6.1 -[0.007 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin com-soundchasing-stochasm 0.6.1 -[0.007 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin computerscare 0.6.1 -[0.008 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin DHE-Modules 0.6.1 -[0.008 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin DrumKit 0.6.1 -[0.009 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin ErraticInstruments 0.6.1 -[0.009 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin ESeries 0.6.1 -[0.009 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin FrankBussFormula 0.6.1 -[0.010 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin FrozenWasteland 0.6.1 -[0.010 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Fundamental 0.6.1 -[0.010 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Geodesics 0.6.1 -[0.011 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Gratrix 0.6.1 -[0.011 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin HetrickCV 0.6.1 -[0.011 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin huaba 0.6.1 -[0.012 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin ImpromptuModular 0.6.11 -[0.012 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin JE 0.6.1 -[0.013 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin JW-Modules 0.6.1 -[0.013 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Koralfx-Modules 0.6.1 -[0.013 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin LindenbergResearch 0.6.2b -[0.014 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin LOGinstruments 0.6.1 -[0.015 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin mental 0.6.1 -[0.015 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin ML_modules 0.6.1 -[0.015 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin moDllz 0.6.1 -[0.015 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin modular80 0.6.1 -[0.016 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin mscHack 0.6.1 -[0.016 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin mtsch-plugins 0.6.1 -[0.016 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin NauModular 0.6.1 -[0.017 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Nohmad 0.6.1 -[0.017 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Ohmer 0.6.1 -[0.018 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin PG-Instruments 0.6.1 -[0.018 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin PvC 0.6.1 -[0.018 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Qwelk 0.6.1 -[0.019 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin RJModules 0.6.1 -[0.019 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin SerialRacker 0.6.1 -[0.020 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin SonusModular 0.6.1 -[0.020 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Southpole 0.6.1 -[0.020 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Southpole-parasites 0.6.1 -[0.021 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin squinkylabs-plug1 0.6.1 -[0.021 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin SubmarineFree 0.6.1 -[0.021 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin SynthKit 0.6.1 -[0.022 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Template 0.6.1 -[0.022 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin TheXOR 0.6.1 -[0.022 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin trowaSoft 0.6.1 -[0.023 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin unless_modules 0.6.1 -[0.023 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Valley 0.6.1 -[0.024 info src/plugin.cpp:90] trying to load shared plugin file f:\git\VeeSeeVSTRack\vst2_bin\/plugins/bsp/plugin.dll.instr -[0.025 info src/plugin.cpp:160] Loaded plugin bsp 0.6.1 from f:\git\VeeSeeVSTRack\vst2_bin\/plugins/bsp/plugin.dll.instr -[0.026 info src/plugin.cpp:90] trying to load shared plugin file f:\git\VeeSeeVSTRack\vst2_bin\/plugins/dBiz/plugin.dll.instr -[0.027 info src/plugin.cpp:160] Loaded plugin dBiz 0.6.1 from f:\git\VeeSeeVSTRack\vst2_bin\/plugins/dBiz/plugin.dll.instr -[0.028 info src/plugin.cpp:90] trying to load shared plugin file f:\git\VeeSeeVSTRack\vst2_bin\/plugins/Template_shared/plugin.dll.instr -[0.029 info src/plugin.cpp:160] Loaded plugin Template_shared 0.6.1 from f:\git\VeeSeeVSTRack\vst2_bin\/plugins/Template_shared/plugin.dll.instr -[0.031 info src/settings.cpp:451] Loading settings f:\git\VeeSeeVSTRack\vst2_bin\/settings.json -[0.043 info src/window.cpp:725] Loaded font f:\git\VeeSeeVSTRack\vst2_bin\/res/fonts/DejaVuSans.ttf -[0.046 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_146097_cc.svg -[0.047 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_31859_cc.svg -[0.048 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_1343816_cc.svg -[0.048 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_1343811_cc.svg -[0.049 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_1084369_cc.svg -[0.050 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_1745061_cc.svg -[0.051 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_1240789_cc.svg -[0.051 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_305536_cc.svg -[0.052 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_468341_cc.svg -[0.053 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/idle_mode_icon_cc.svg -[0.054 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/settings_icon_cc.svg -[0.054 info src/settings.cpp:451] Loading settings f:\git\VeeSeeVSTRack\vst2_bin\/settings.json -[0.069 info src/app/RackWidget.cpp:216] Loading patch from string -[0.073 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/Core/AudioInterface.svg -[0.074 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/ScrewSilver.svg -[0.075 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/PJ301M.svg -[0.075 info src/window.cpp:725] Loaded font f:\git\VeeSeeVSTRack\vst2_bin\/res/fonts/ShareTechMono-Regular.ttf -[0.079 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/Core/MIDIToCVInterface.svg -[0.083 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/XCO.svg -[0.083 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/knob_68px.svg -[0.084 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/knob_16px.svg -[0.085 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/button_9px_0.svg -[0.085 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/button_9px_1.svg -[0.086 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/knob_38px.svg -[0.087 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/slider_switch_2_14px_0.svg -[0.088 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/slider_switch_2_14px_1.svg -[0.088 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/port.svg -[0.091 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Fundamental/res/VCA.svg -[0.092 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/RoundLargeBlackKnob.svg -[0.095 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/AS/res/ADSR.svg -[0.095 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/AS/res/as-hexscrew.svg -[0.096 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/AS/res/as-SlidePot.svg -[0.097 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/AS/res/as-SlidePotHandle.svg -[0.098 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/AS/res/as-PJ301M.svg -[0.107 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/21kHz/res/Panels/D_Inf.svg -[0.108 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/21kHz/res/Components/kHzScrew.svg -[0.109 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/21kHz/res/Components/kHzKnobSmall.svg -[0.109 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/21kHz/res/Components/kHzButton_0.svg -[0.110 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/21kHz/res/Components/kHzButton_1.svg -[0.111 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/21kHz/res/Components/kHzPort.svg -[0.114 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Autodafe/res/Multiple18.svg -[0.115 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/PJ3410.svg -[6.755 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/AudibleInstruments/res/Plaits.svg -[6.755 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/TL1105_0.svg -[6.757 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/TL1105_1.svg -[6.758 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/Rogan3PSWhite.svg -[6.759 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/Rogan1PSWhite.svg -[6.760 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/Trimpot.svg -[35.355 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/AudibleInstruments/res/Stages.svg -[35.356 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/LEDSlider.svg -[35.357 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/LEDSliderGreenHandle.svg -[64.962 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/AudibleInstruments/res/Marbles.svg -[64.963 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/CKD6_0.svg -[64.964 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/CKD6_1.svg -[64.966 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/Rogan2PSWhite.svg -[85.108 info src/app/RackWidget.cpp:178] Saving patch to string +[0.000 info src/main.cpp:71] Local directory: f:\git\VeeSeeVSTRack\vst2_bin\/ +[0.000 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin 21kHz 0.6.1 +[0.000 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin AmalgamatedHarmonics 0.6.1 +[0.000 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Alikins 0.6.1 +[0.000 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin alto777_LFSR 0.6.1 +[0.001 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin AS 0.6.9 +[0.001 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin AudibleInstruments 0.6.3 +[0.001 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Autodafe 0.6.1 +[0.001 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin BaconMusic 0.6.1 +[0.002 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Befaco 0.6.1 +[0.002 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Bidoo 0.6.1 +[0.002 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Bogaudio 0.6.7 +[0.002 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin CastleRocktronics 0.6.1 +[0.002 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin cf 0.6.1 +[0.002 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin com-soundchasing-stochasm 0.6.1 +[0.002 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin computerscare 0.6.1 +[0.002 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin DHE-Modules 0.6.1 +[0.002 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin DrumKit 0.6.1 +[0.003 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin ErraticInstruments 0.6.1 +[0.003 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin ESeries 0.6.1 +[0.003 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin FrankBussFormula 0.6.1 +[0.003 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin FrozenWasteland 0.6.1 +[0.003 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Fundamental 0.6.1 +[0.003 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Geodesics 0.6.1 +[0.003 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Gratrix 0.6.1 +[0.003 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin HetrickCV 0.6.1 +[0.003 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin huaba 0.6.1 +[0.003 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin ImpromptuModular 0.6.11 +[0.003 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin JE 0.6.1 +[0.003 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin JW-Modules 0.6.1 +[0.003 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Koralfx-Modules 0.6.1 +[0.004 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin LindenbergResearch 0.6.2b +[0.004 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin LOGinstruments 0.6.1 +[0.004 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin mental 0.6.1 +[0.004 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin ML_modules 0.6.1 +[0.004 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin moDllz 0.6.1 +[0.004 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin modular80 0.6.1 +[0.004 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin mscHack 0.6.1 +[0.004 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin mtsch-plugins 0.6.1 +[0.004 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin NauModular 0.6.1 +[0.004 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Nohmad 0.6.1 +[0.004 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Ohmer 0.6.1 +[0.005 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin PG-Instruments 0.6.1 +[0.005 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin PvC 0.6.1 +[0.005 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Qwelk 0.6.1 +[0.005 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin RJModules 0.6.1 +[0.005 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin SerialRacker 0.6.1 +[0.005 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin SonusModular 0.6.1 +[0.005 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Southpole 0.6.1 +[0.005 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Southpole-parasites 0.6.1 +[0.005 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin squinkylabs-plug1 0.6.9 +[0.006 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin SubmarineFree 0.6.1 +[0.006 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin SynthKit 0.6.1 +[0.006 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Template 0.6.1 +[0.006 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin TheXOR 0.6.1 +[0.006 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin trowaSoft 0.6.1 +[0.006 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin unless_modules 0.6.1 +[0.006 info src/plugin_static.cpp:140] vcvrack: Loaded static plugin Valley 0.6.1 +[0.007 info src/plugin.cpp:90] trying to load shared plugin file f:\git\VeeSeeVSTRack\vst2_bin\/plugins/bsp/plugin.dll.instr +[0.007 info src/plugin.cpp:160] Loaded plugin bsp 0.6.1 from f:\git\VeeSeeVSTRack\vst2_bin\/plugins/bsp/plugin.dll.instr +[0.007 info src/plugin.cpp:90] trying to load shared plugin file f:\git\VeeSeeVSTRack\vst2_bin\/plugins/dBiz/plugin.dll.instr +[0.008 info src/plugin.cpp:160] Loaded plugin dBiz 0.6.1 from f:\git\VeeSeeVSTRack\vst2_bin\/plugins/dBiz/plugin.dll.instr +[0.008 info src/plugin.cpp:90] trying to load shared plugin file f:\git\VeeSeeVSTRack\vst2_bin\/plugins/Template_shared/plugin.dll.instr +[0.009 info src/plugin.cpp:160] Loaded plugin Template_shared 0.6.1 from f:\git\VeeSeeVSTRack\vst2_bin\/plugins/Template_shared/plugin.dll.instr +[0.009 info src/settings.cpp:451] Loading settings f:\git\VeeSeeVSTRack\vst2_bin\/settings.json +[0.018 info src/window.cpp:725] Loaded font f:\git\VeeSeeVSTRack\vst2_bin\/res/fonts/DejaVuSans.ttf +[0.019 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_146097_cc.svg +[0.019 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_31859_cc.svg +[0.019 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_1343816_cc.svg +[0.020 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_1343811_cc.svg +[0.020 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_1084369_cc.svg +[0.020 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_1745061_cc.svg +[0.020 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_1240789_cc.svg +[0.020 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_305536_cc.svg +[0.020 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_468341_cc.svg +[0.021 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/idle_mode_icon_cc.svg +[0.021 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/settings_icon_cc.svg +[0.021 info src/settings.cpp:451] Loading settings f:\git\VeeSeeVSTRack\vst2_bin\/settings.json +[0.024 info src/app/RackWidget.cpp:216] Loading patch from string +[0.026 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/Core/AudioInterface.svg +[0.026 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/ScrewSilver.svg +[0.026 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/PJ301M.svg +[0.026 info src/window.cpp:725] Loaded font f:\git\VeeSeeVSTRack\vst2_bin\/res/fonts/ShareTechMono-Regular.ttf +[0.027 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/Core/MIDIToCVInterface.svg +[0.030 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/XCO.svg +[0.030 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/knob_68px.svg +[0.030 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/knob_16px.svg +[0.030 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/button_9px_0.svg +[0.030 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/button_9px_1.svg +[0.031 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/knob_38px.svg +[0.031 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/slider_switch_2_14px_0.svg +[0.031 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/slider_switch_2_14px_1.svg +[0.031 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/port.svg +[0.032 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Fundamental/res/VCA.svg +[0.032 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/RoundLargeBlackKnob.svg +[0.034 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/AS/res/ADSR.svg +[0.034 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/AS/res/as-hexscrew.svg +[0.034 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/AS/res/as-SlidePot.svg +[0.034 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/AS/res/as-SlidePotHandle.svg +[0.035 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/AS/res/as-PJ301M.svg +[0.038 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/21kHz/res/Panels/D_Inf.svg +[0.038 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/21kHz/res/Components/kHzScrew.svg +[0.039 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/21kHz/res/Components/kHzKnobSmall.svg +[0.039 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/21kHz/res/Components/kHzButton_0.svg +[0.039 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/21kHz/res/Components/kHzButton_1.svg +[0.039 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/21kHz/res/Components/kHzPort.svg +[0.040 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Autodafe/res/Multiple18.svg +[0.041 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/PJ3410.svg +[2.986 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/squinkylabs-plug1/res/booty_panel.svg +[2.986 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/Rogan3PSBlue.svg +[4.414 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/squinkylabs-plug1/res/chb_panel.svg +[4.414 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/Trimpot.svg +[4.414 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/squinkylabs-plug1/res/Blue30.svg +[4.415 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/squinkylabs-plug1/res/BluePush_1.svg +[4.415 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/squinkylabs-plug1/res/BluePush_0.svg +[6.315 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/squinkylabs-plug1/res/ev3_panel.svg +[6.315 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/squinkylabs-plug1/res/waveforms-6-08.svg +[6.315 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/squinkylabs-plug1/res/waveforms-6-07.svg +[6.316 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/squinkylabs-plug1/res/waveforms-6-06.svg +[6.316 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/squinkylabs-plug1/res/waveforms-6-05.svg +[6.316 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/squinkylabs-plug1/res/waveforms-6-02.svg +[6.316 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/squinkylabs-plug1/res/waveforms-6-01.svg +[6.316 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/squinkylabs-plug1/res/waveforms-6-04.svg +[6.317 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/squinkylabs-plug1/res/waveforms-6-03.svg +[6.317 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/squinkylabs-plug1/res/waveforms-6-12.svg +[6.317 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/squinkylabs-plug1/res/waveforms-6-11.svg +[6.317 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/squinkylabs-plug1/res/waveforms-6-10.svg +[6.317 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/squinkylabs-plug1/res/waveforms-6-09.svg +[10.517 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/squinkylabs-plug1/res/gray.svg +[13.194 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/squinkylabs-plug1/res/lfn_panel.svg +[13.194 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/Rogan1PSBlue.svg +[15.288 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/squinkylabs-plug1/res/blank_panel.svg +[16.920 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/squinkylabs-plug1/res/vocal_animator_panel.svg +[16.920 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/NKK_0.svg +[16.920 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/NKK_1.svg +[16.920 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/NKK_2.svg +[18.568 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/squinkylabs-plug1/res/formants_panel.svg +[18.568 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/RoundBlackKnob.svg +[20.741 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/squinkylabs-plug1/res/colors_panel.svg +[20.741 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/Rogan2PSWhite.svg +[27.641 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/squinkylabs-plug1/res/trem_panel.svg +[29.941 info src/window.cpp:780] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/squinkylabs-plug1/res/thread_booster_panel.svg +[33.512 info src/app/RackWidget.cpp:178] Saving patch to string diff --git a/vst2_bin/plugins/squinkylabs-plug1/LICENSE-dist.txt b/vst2_bin/plugins/squinkylabs-plug1/LICENSE-dist.txt index 636c5ee5..8aec0774 100644 --- a/vst2_bin/plugins/squinkylabs-plug1/LICENSE-dist.txt +++ b/vst2_bin/plugins/squinkylabs-plug1/LICENSE-dist.txt @@ -7,7 +7,17 @@ All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Copyright 2016 Andrew Belt + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. @@ -65,3 +75,40 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND Component Library graphics by Grayscale (http://grayscale.info/) Licensed under CC BY-NC 4.0 (https://creativecommons.org/licenses/by-nc/4.0/) + +# Befaco modules for VCV Rack + +Copyright 2016 Andrew Belt + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +Copyright 2016 Andrew Belt + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# VCV Fundamental modules +Copyright 2016 Andrew Belt + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/vst2_bin/plugins/squinkylabs-plug1/README.md b/vst2_bin/plugins/squinkylabs-plug1/README.md index 13a7bbcd..823e4797 100644 --- a/vst2_bin/plugins/squinkylabs-plug1/README.md +++ b/vst2_bin/plugins/squinkylabs-plug1/README.md @@ -4,10 +4,12 @@ This project is a growing collection of modules for the VCV Rack vritual modular You can find us on Facebook [here](https://www.facebook.com/SquinkyLabs). -## Manuals +## Manuals and Release Notes Here is the user's manual for our modules: [instruction manual](./docs/booty-shifter.md). It contains descriptions of all of them. +The [release notes](./docs/release-notes.md) describe recent changes. + ## Contributing Please use our GitHub issues page to report problems, request features, etc. If you don’t already have a GitHub account you will need to create one, as you must be logged in to post to GitHub. diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/README.md b/vst2_bin/plugins/squinkylabs-plug1/docs/README.md index 71d640ab..5dc58619 100644 --- a/vst2_bin/plugins/squinkylabs-plug1/docs/README.md +++ b/vst2_bin/plugins/squinkylabs-plug1/docs/README.md @@ -2,6 +2,8 @@ All of our plugins are free and open source. The [instruction manual](booty-shifter.md) describes all of the released modules. +The [release notes](release-notes.md) describe recent changes. + All of our released modules may be found in the [VCV Rack plugin manager] (https://vcvrack.com/plugins.html). This is by far the easiest way for most users to install our modules and keep them up to date. It is also quite easy to clone this repo and build them yourself. In order to do this, however, you must first download and build [VCV Rack itself](https://github.com/VCVRack/Rack). @@ -22,10 +24,6 @@ As with all third-party modules for VCV, you must: * `CD SquinkyVCV` * `make` -## Experimental modules - -At any given time, there may partially finished "experimental" modules in this repo. You can find up to date information on them [here](experimental.md). - ## Unit testing framework We have reasonably thorough tests for our code. Some of this might be of interest - it's [here](unit-test.md). \ No newline at end of file diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/alias-setup.png b/vst2_bin/plugins/squinkylabs-plug1/docs/alias-setup.png new file mode 100644 index 00000000..00aa607a Binary files /dev/null and b/vst2_bin/plugins/squinkylabs-plug1/docs/alias-setup.png differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/aliasing.md b/vst2_bin/plugins/squinkylabs-plug1/docs/aliasing.md new file mode 100644 index 00000000..d7a1fce0 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/docs/aliasing.md @@ -0,0 +1,39 @@ +# Some information about aliasing + +We’ve been thinking about aliasing a lot lately. It has been a major issue in many of our recent projects. With Fundamental VCO-1 we wanted to be sure that after we re-designed the anti-alias filters to use less CPU that the aliasing was still good. So we spent a lot of time writing unit tests to compare the two. + +With EV3, we wanted to add sync to the VCO without adding aliasing. It took us way longer than it should have to figure out exactly how to do that. Then with Shaper it was clear that it would generate a lot of aliasing if we didn’t oversample it. So we borrowed the oversampler from Fundamental VCO-1. + +Aliasing in digital audio happens when an algorithm attempts to create frequencies that are impossible to represent. You probably know that the highest frequency that can be contained in a digital signal is one-half the sampling rate. So at the standard 44,100 sample rate, the highest frequency that can be encoded is 22,050 Hz. + +If a DSP algorithm, like a VCV module, attempts to generate frequencies higher than than this, they will appear at a frequency below 22,050. In fact they will “fold back” - increasing frequencies alias and manifest themselves as decreasing frequencies below fs/2. + +Most people find that aliasing sounds terrible. Unlike harmonic distortion, which occurs at harmonic multiples of the pitch, the alias frequencies are not related to the non-aliased frequencies in a harmonious way. As an extreme example: if you play a high-pitched note and bend it up, the alias frequencies will bend in the opposite direction, so you hear you main signal bending up in pitch, with some “ghost tones” bending down in pitch. + +For more information on aliasing, especially how it sounds, there is a wealth of information on the internet. Spoiler alert: most people think if sounds bad and most commercial digital audio equipment goes to some lengths to suppress it. + +Now if you are new to this, and paying attention, you might be perplexed by the statement above that aliasing happens when a DSP algorithm attempts to generate sounds above 22,050. Why would any algorithm try to do this? Well, a simple example is a square wave. An analog square wave has an infinite number of harmonics going up to infinite frequencies (look it up if you don’t believe us). So a naive attempt to make a square wave VCO is going to generate a ton of aliasing. Similarly, a distortion or waveshaper module that does hard clipping will tend to create square waves and a lot of aliasing. + +![alias setup image](./alias-setup.png) + +Luckily for VCV Rack users it is easy to see if a particular module is aliasing. Pick a spectrum analyzer module. Our favorite is the Bogaudio one. To examine a VCO, just patch the VCO output to the analyzer’s input. To examine a waveshaper, patch a sine wave from a VCO into the input of the shaper, then patch the output of the shaper to the spectrum analyzer. + +Aliasing is far more severe at high frequencies, so set the VCO for somewhere between 1 kHz and 2kHz. At lower frequencies it might not be visible (or audible). If you set it at an exact division of the sampling rate the aliasing will blend in with the harmonics and you won’t see it. 1k and 2k are very close to exact divisions of 44,100, so pick something in between. Then adjust the VCO frequency up and down until you see the worst aliasing. You may see new frequency lines appear in between real harmonics. Or you may see that as you raise the pitch some of the lines go down - those are the alias frequencies folding over. + +This excellent video by the author of the Vult modules shows how to do it: [Can you hear the alias?](https://www.youtube.com/watch?v=LSIO5R0fuoU&feature=youtu.be&fbclid=IwAR1_wsaqiuLiYNO7Tn0c5smP3mLnwxgYJmD8MXcn7eNuhlb6To_XwzR3Kzc) + +As an example we will show the effect of changing the oversample rate in our “Shaper” module using the “Clip” setting. The gain is up pretty high. + +![clipping image](./clip-collage.png) + +This image shows the output of Shaper on the spectrum analyzer at 1X (left), 4X, and 16X (on the right). You can easily see the alising in the 1X image. At this setting there is no visible difference between 4X and 16X. No doubt there would be if the analyzer were more sensitive, but it’s worth noting here that at 1X the alias components are getting pretty high compared to the harmonics in the 10k region. But down in the 1-2k area the alias is below the fundamental by a bit more than 50 db. Also notice that there are alias components below the fundamental frequency. + +This next image is the same test, but with the “Folder” setting. Again, the gain is pretty high, and there is some offset to bring in even harmonics. + +![folding image](./fold-collage.png) + +Not surprisingly, this highly folded sine wave shows a **lot** of aliasing at 1X. Notice the line at 2 kHz - that’s an alias component that is louder than the fundamental. And now you can see many more alias components below the fundamental. + +Moving right to the 4X image, clearly most of the aliasing is gone. But there are still alias components adjacent to the harmonics, and they are only perhaps 25 db down from the good signal. Then in the 16X image things are as they should be. The lines are all at even multiples of 1.5 k, so they are harmonics, not aliasing. Note that the level of the harmonics is not falling off at all with increasing frequency. That is why this shape brings out so much aliasing. + +We hope you have learned how easy it is to evaluate the aliasing of a VCV module. Next time you are experimenting with a new module, why not take a look at the aliasing? Make your own opinion about aliasing. Your are fortunate to have the tools already. diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/booster.md b/vst2_bin/plugins/squinkylabs-plug1/docs/booster.md new file mode 100644 index 00000000..543b157e --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/docs/booster.md @@ -0,0 +1,21 @@ +# Thread Booster + +![thread booster image](./thread-booster.png) + +Thread booster raises the priority of VCV Rack's audio rendering thread. In many cases this decreases the annoying pops, ticks, and dropouts that many users are experiencing. + +Many users have reported that Thread Booster helps significantly. Others have reported that it does not help at all. No one has reported a detrimental effect. + +For a deeper dive into the Thread Booster, you should read [this document](./thread-booster.md). + +Thread Booster has a UI that lets you boost the priority of the audio thread. There are three arbitrary settings: normal, boosted, and real time. When the switch is in the bottom position, the plugin does nothing; the audio thread keeps its default priority. In the boost (middle) position, it sets the thread priority to the highest priority non-real-time setting. In the real-time position it attempts to set it to the highest possible priority, or near it. + +If setting the priority fails, the red error light lights up, and the priority stays where it was last. + +To use Thread Booster, just insert an instance into VCV Rack, then adjust the boost switch. In general we recommend the "real time" setting, if it is available on your computer. + +Once Thread booster is in your session, it will boost all the audio processing - it doesn't matter if other modules are added before or after - they all get boosted. + +Linux users - you must read [the detailed document](./thread-booster.md) to use this module. + +Note to users who downloaded the original version of Thread Booster: we've improved it a bit since then, especially on Linux and Windows. diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/booty-shifter-old.md b/vst2_bin/plugins/squinkylabs-plug1/docs/booty-shifter-old.md new file mode 100644 index 00000000..f21b268f --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/docs/booty-shifter-old.md @@ -0,0 +1,249 @@ +# Table of contents + +[Chebyshev Waveshaper VCO](../docs/chebyshev.md) Click on link to go to Chbeyshev manual. + +[Functional VCO-1](#fun) Is an improved version of the Fundamental-VCO1. + +[LFN](#lfn) Is a random voltage generator made by running low frequency noise through a graphic equalizer. + +[Chopper](#chopper) Is a tremolo powered by a clock-synchable LFO. The LFO is highly programmable to give a range of waveforms. + +[Thread Booster](#booster) reduces pops and clicks in VCV Rack by reprogramming VCV's audio engine. + +[Colors](#colors) is a colored noise generator. It can generate all the common **"colors"** of noise, including white, pink, red, blue, and violet. + +[Growler](#growler) is a "vocal animator." It imparts random vocal timbres on anything played through it. The pseudo-random LFOs all have discrete outputs. + +[Booty Shifter](#shifter) is an emulation of the legendary Moog/Bode frequency shifter. + +[Formants](#formants) is a programmable bank of filters that can synthesize various vowel sounds and morph between them. + +[Attenuverters](#atten) + +[CV ranges](#cv) + +The [release notes](release-notes.md) describe recent changes to our modules. + +# Functional VCO-1 + +![Functional image](../docs/functional.png) + +Functional VCO-1 works just like its namesake. The control layout is familiar, the sound is the same, but it uses about 1/4 as much CPU as the original. + +We believe VCV's Fundamental VCO is an unsung hero. It's one of the few VCOs that never has audible aliasing artifacts. You can sync it, and modulate all its inputs, but the sound never falls apart. + +We "forked" the code to Fundamental VCO-1 and modified it a little bit to make it much more CPU efficient. Now you may use a lot more of them without pops, clicks, and dropouts. + +If you would like the details of how we did this, you can [find them here](../docs/vco-optimization.md). + + +# LFN Low Frequency Noise Generator + +![LFN image](../docs/lfn.png) + +LFN stands for Low Frequency Noise. Technically it is a white noise generator run through a graphic equalizer at extremely low frequencies. People may find it easier to think of it as a random voltage source with unique control over the output. + +The top knob, which is unlabeled, sets the "base frequency" of LFN. + +The five other knobs, and the CV inputs beside them, control the gain of the graphic equalizers sections. Beside each EQ gain knob is a label indicating what frequency range that knob controls. + +For example, it the base frequency is 1.0, the EQ sections will be at 1Hz, 2Hz, 4Hz, 8Hz, and 16Hz. If the base frequency is 0.2, The EQ sections will be at 0.2Hz, 0.4Hz, 0.8Hz, 1.6Hz, and 3.2Hz. + +But instead of thinking about frequencies like 1Hz, which are a little difficult to imagine, think of the knobs as mixing very slow random voltages, with less slow ones. For example if LFN is driving a pitch quantizer into a VCO, turn all the knobs down to zero except the first, lowest, one. This will make a series of pitches slowly rising and falling. Then bring in a little of the faster channels. The pitch will still be slowly rising and falling, but will also quickly move up and down by smaller steps. + +A good way to learn what makes LFN tick is to set it slow and watch it on the scope. At the same time run it straight into a VCO. Experiment with different mixes of the slow knobs and the fast ones. + +As you would expect from Squinky Labs, the CPU usage of LFN is very low. In fact it is one of our leanest modules yet. So feel free to use as many instances as you like. + +# Chopper tremolo / programmable LFO +![chopper image](./chopper.png) + +In its simplest use, Chopper produces a very wide range of **tremolo** effects. The built-in LFO can produce a wide range of waveforms that cover many of the waveforms produced by the tremolo circuits built into **vintage guitar amplifiers**. + +The LFO is sent to an output so that it may modulate other modules. + +There is also a **clock synchronizer** and multiplier. + +To use Chopper as a tremolo, send a signal to the *in* jack, and listen to the *out* jack. Leave the *clock* control at the default *int* setting. Most of the knob settings will now affect the tremolo effect. + +## Chopper LFO + +![chopper LFO image](../docs/lfo-waveforms.png) + +To understand all the LFO settings, it helps to watch the outputs on a scope. + +The LFO starts as **skewed** sawtooth. In the middle position it is a symmetric triangle wave, at one end a positive sawtooth and at the other a negative sawtooth. The signal is sent to the **saw** output. + +The skewed saw then goes to a **waveshaper**. As the shape control is increased the LFO is gradually rounded and then flattened. The shaped LFO is send to the *lfo* output, and used internally to modulate the audio input. + +LFO Controls: + +* **Shape** Flattens the LFO waveform. +* **Skew** Dials in the amount of asymmetry in the LFO. +* **Depth** Shifts and scales the LFO. + +When used as a tremolo effect, you will hear **more tremolo** when these controls are turned up. + +## Chopper clock + +The LFO in Chopper may be synchronized with the ckin signal. There is a built-in **clock multiplier**. To use the synchronization, patch a clock to the ckin, and select x1 from the **clock** knob. To run at a multiple of the input clock, select x2, x3, or x4. + +When Chopper is being synched, the **Phase** control sets the phase difference between the external clock and the synchronized LFO. This may be used to "dial in" the tremolo so that it sounds exactly on the beat (or off the beat). + +There is also an internal LFO that is controlled by the **Rate** control. Set the clock control to *int* to use the internal clock. + +# Thread Booster + +![thread booster image](./thread-booster.png) + +Thread booster raises the priority of VCV Rack's audio rendering thread. In many cases this decreases the annoying pops, ticks, and dropouts that many users are experiencing. + +Many users have reported that Thread Booster helps significantly. Others have reported that it does not help at all. No one has reported a detrimental effect. + +For a deeper dive into the Thread Booster, you should read [this document](./thread-booster.md). + +Thread Booster has a UI that lets you boost the priority of the audio thread. There are three arbitrary settings: normal, boosted, and real time. When the switch is in the bottom position, the plugin does nothing; the audio thread keeps its default priority. In the boost (middle) position, it sets the thread priority to the highest priority non-real-time setting. In the real-time position it attempts to set it to the highest possible priority, or near it. + +If setting the priority fails, the red error light lights up, and the priority stays where it was last. + +To use Thread Booster, just insert an instance into VCV Rack, then adjust the boost switch. In general we recommend the "real time" setting, if it is available on your computer. + +Once Thread booster is in your session, it will boost all the audio processing - it doesn't matter if other modules are added before or after - they all get boosted. + +Linux users - you must read [the detailed document](./thread-booster.md) to use this module. + +Note to users who downloaded the original version of Thread Booster: we've improved it a bit since then, especially on Linux and Windows. + +# Colors variable slope noise generator + +![noise image](../docs/colors.png) + +Colors is a colored noise generator. It can generate all the common **"colors"** of noise, including white, pink, red, blue, and violet. It can also produce all the colors in between, as it has a **continuously variable slope**. + +Colors has a single control, "slope." This is the slope of the noise spectrum, from -8 dB/octave to +8 dB/octave. + +The slope of the noise is quite accurate in the mid-band, but at the extremes we flatten the slope to keep from boosting super-low frequencies too much, and to avoid putting out enormous amounts of highs. So the slope is flat below 40hz, and above 6kHz. + +## Things to be aware of + +When the **slope** changes, Color needs to do a lot of calculations. While this is normally not a problem, it’s possible that quickly changing the slope of many instances of Colors could cause pops and dropouts. + +The slope control does not respond instantly. If you turn the knob, you will hear the change, but if you were to modulate the CV very quickly you might notice the slowness. + +# Growler + +![vocal formant filter image](./growler.jpg) + +**Growler** is a re-creation of the Vocal Animator circuit invented by Bernie Hutchins, and published in Electronotes magazine in the late 70's. It continuously morphs between different vaguely voice like tones. + +**To get a good sound:** run any harmonically rich signal into the input, and something good will come out. Low frequency pulse waves and distorted sounds make great input. + +The controls do pretty much what you would expect: + +* **LFO** controls the speed of the modulation LFOs. +* **Fc** controls the average frequency of the multiple filters. +* **Q** controls the sharpness of the filters. +* **Depth** controls how much of the modulation LFOs are applied to the filters. + +## How Growler works +![growler scope](./growler.png) + +There are four **bandpass filters**, roughly tuned to some typical vocal formant frequencies: 522, 1340, 2570, and 3700 Hz. The filters are run in parallel, with their outputs summed together. + +The first three filter frequencies are modulated by an LFO comprised of **4 triangle wave LFOs** running at different frequencies. They are summed together in various combinations to drive each of the filters. + +Each **CV input stage** is the same: a knob that supplies a fixed offset and a CV input that is processed by an attenuverter. The processed CV is added to the knob voltage. See below for more on [Attenuverters](#atten) and [CV ranges](#cv). + +The **LFO** Rate control shifts the speed of all 4 LFOs while maintaining the ratio of their frequencies. + +The **Fc** control moves the frequencies of the first three filters, but not by equal amounts. The lowest filter moves at 1V/Oct, but the middle two move less. The top filter is fixed at 3700 Hz. + +The **Q** control does just what it says - controls the Q (resonance) of the filters. + +The **Modulation Depth** controls how much of the summed LFOs get to each filter. Again, the lower filters move farther, and the top filter is fixed. + +The smaller knobs next to the main knobs are **attenuverters**, which scale control voltages. For more on attenuverters, [see below](#atten) + +There are three LFO outputs next to the blinking LFOs. These may be used to modulate other modules, or as semi-random voltage sources. + +**Bass boost** switch. When it’s in the up position (on) there should be more bass. This is done by switching some or all of the filters from bandpass to lowpass. + +LFO **Matrix** switch. This is the unlabeled switch in the LFO section. When it’s down (default position) the LFOs are closely correlated. In the middle we try to make them a little bit more independent. When it’s in the up position the LFOs will often go in different directions. + +# Booty Shifter frequency shifter + +**Booty Shifter** is a frequency shifter inspired by the Moog/Bode frequency shifter module. + +![booty shifter image](./booty-shifter.png) + +The name "Booty Shifter" is a nod to the classic analog module, as well as to a black cat named Booty. + +Booty Shifter will take an audio input and shift the frequencies up or down. This is not like a pitch shift where harmonics will remain in tune; it is an absolute frequency shift in Hz, so in general **harmonics will go way out of tune.** It is similar to a ring-modulator, but less extreme and more versatile. + +## Getting good sounds from Booty Shifter + +Feed in music and shift the frequency a good amount. + +Feed in **speech or radio** and shift it. + +Feed the CV from a **sequencer** to sequence the mayhem. + +Shift **drums** up or down a little bit to re-tune them without the usual pitch-shifting artifacts. + +Small shifts in conjunction with delays can make a chorus-like effect to thicken music. + +## Inputs and outputs + +* **IN** is the audio input. +* **CV** is the pitch shift control voltage. -5V will give minimum shift, +5 will give maximum. +* **DN** is the down-shifted output. +* **UP** is the up-shifted output. + +## Controls + +**RANGE** sets the total shift range in Hz. For example, the 50 Hz setting means that the minimum shift is 50 Hz down, and the maximum is 50 Hz up. + +Range value **Exp is different**. Here minimum shift is 2 Hz, maximum is 2 kHz, with an exponential response. As of version 0.6.2 the response is an accurate 1 Volt per Octave. + +Shift **AMT** is added to the control voltage, with a range of -5..5. + +## Oddities and limitations + +If you shift the frequency up too far, it will alias. There is no anti-aliasing, so if the highest input frequency + shift amount > sample_rate / 2, you will get aliasing. Of course the Bode analog original did not alias. + +If you shift the input down a lot, frequencies will go **below zero and wrap around**. Taken far enough this will completely **reverse the spectrum** of the input. This was a prized feature of the Bode original. + +As you shift the input down, you may start to generate a lot of subsonic energy. A **High Pass filter** may clean this up. + +The down shift **frequency fold-over**, while true to the original, does cause problems when trying to pitch drum tracks down a lot. High pass filtering the input before it is down-shifted can control this. + +# Formants vocal filter + +![formants image](./formants.png) + +Like the **Vocal Animator**, this is a filter bank tuned to the formant frequencies of typical **singing voices**. Unlike Growler, however, the filters do not animate on their own. In addition, the filters are preset to frequencies, bandwidths, and gains that are taken from **measurements of human singers**. + +One of the easiest ways to **get a good sound** from Formants is to use it like a regular VCF. For example, control Fc with an ADSR. Then put a second modulation source into the vowel CV - something as simple as a slow LFO will add interest. + +Use it as a **filter bank**. Just set the knobs for a good sound and leave it fixed to add vocal tones to a pad. Again, modulating the vowel CV can easily give great results. + +Try to synthesize something like **singing** by sequencing the vowel CV of several formants. Leave the Fc in place, or move it slightly as the input pitches move. + +Controls: + +* **Fc** control moves all the filters up and down by the standard one "volt" per octave. +* **Vowel** control smoothly interpolates between 'a', 'e', 'i', 'o', and 'u'. +* **Model** control selects different vocal models: bass, tenor, countertenor, alto, and soprano. +* **Brightness** control gradually boosts the level of the higher formants. When it is all the way down, the filter gains are set by the singing models in the module, which typically fall off with increasing frequency. As this control is increased the gain of the high formant filters is brought up to match the F1 formant filter. + +The **LEDs across the top** indicate which formant is currently being "sung". + +## About Attenuverters + +The small knobs next to the bigger knobs are **attenuverters**. They scale and/or invert the control voltage inputs next to them. When they are turned all the way up the full CV comes through. As they are turned down less CV comes through. Straight up none passes. As they are turned down further the CV comes back, but inverted. + +Sometimes we use attenuverters with a *linear taper*, and sometimes we use an *audio taper*. If you find that on a particular module you do not like the response of the attenuverters, please log a github issue. + +## Control voltage ranges + +Our modules usually expect a control voltage range of **-5 to +5**. The associated offset knobs will also add -5 to +5. After attenuverters are applied to CV the knob value is added. After all that, the result is usually clipped to the -5 to +5 range. diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/booty-shifter.md b/vst2_bin/plugins/squinkylabs-plug1/docs/booty-shifter.md index 44c078a4..94cc1951 100644 --- a/vst2_bin/plugins/squinkylabs-plug1/docs/booty-shifter.md +++ b/vst2_bin/plugins/squinkylabs-plug1/docs/booty-shifter.md @@ -1,207 +1,40 @@ -# Table of contents +# The Squinky Labs modules for VCV Rack -[Chopper](#chopper) Is a tremolo powered by a clock-synchable LFO. The LFO is highly programmable to give a range of waveforms. +Below are short descriptions of our modules with links to more detailed manuals. -[Thread Booster](#booster) reduces pops and clicks in VCV Rack by reprogramming VCV's audio engine. +The [release notes](release-notes.md) describe recent changes to our modules. -[Colors](#colors) is a colored noise generator. It can generate all the common **"colors"** of noise, including white, pink, red, blue, and violet. +# Things that make sound -[Growler](#growler) is a "vocal animator." It imparts random vocal timbres on anything played through it. The pseudo-random LFOs all have discrete outputs. +![Intro 1 image](./intro-1-110.png) -[Booty Shifter](#shifter) is an emulation of the legendary Moog/Bode frequency shifter. +[EV3](./ev3.md) is three VCOs in a single module. Each of the three VCOs is a clone of Befaco's EvenVCO, with oscillator sync added. Like EvenVCO, it sounds good, uses little CPU, and has very little aliasing distortion. -[Formants](#formants) is a programmable bank of filters that can synthesize various vowel sounds and morph between them. +[Colors](./colors.md) is a colored noise generator. It can generate all the common **"colors"** of noise, including white, pink, red, blue, and violet. And all the colors in between. -[Attenuverters](#atten) +[Chebyshev Waveshaper VCO](../docs/chebyshev.md) can make sounds like no other VCO. It contains a VCO, ten polynomial wave-shapers, and one clipper/folder. Among other things, it is a harmonic oscillator. -[CV ranges](#cv) +[Functional VCO-1](./functional-vco-1.md) Is an improved version of the Fundamental VCO-1. Like Fundamental VCO-1, it will never alias, no matter what you throw at it. It is one of the few VCOs that can do sync, FM, and PWM without aliasing. Functional VCO-1 improves on Fundamental by lowering the CPU usage dramatically. -# Chopper tremolo / programmable LFO +# Things that process sound -In its simplest use, Chopper produces a very wide range of **tremolo** effects. The built-in LFO can produce a wide range of waveforms that cover many of the waveforms produced by the tremolo circuits built into **vintage guitar amplifiers**. +![Intro 2 image](./intro-2-110.png) -The LFO is sent to an output so that it may modulate other modules. +[Shaper](./shaper.md). Yet another wave shaper. But unlike most, this one has almost no aliasing distortion. And a few new shapes that sound nice. -There is also a **clock synchronizer** and multiplier. +[Chopper](./chopper.md) Is a tremolo powered by a clock-synchable LFO. The LFO is highly programmable to give a range of waveforms. A built-in clock multiplier enables easy rhythmic effects. -To use Chopper as a tremolo, send a signal to the *in* jack, and listen to the *out* jack. Leave the *clock* control at the default *int* setting. Most of the knob settings will now affect the tremolo effect. +[Growler](./growler.md) is a "vocal animator." It imparts random vocal timbres on anything played through it. The pseudo-random LFOs all have discrete outputs. -## Chopper LFO +[Booty Shifter](./shifter.md). An emulation of the legendary Moog/Bode frequency shifter. It is great for "warping" sounds run through it. -![chopper image](../docs/lfo-waveforms.png) +[Formants](./formants.md) is a programmable bank of filters that can synthesize various vowel sounds and morph between them easily. -To understand all the LFO settings, it helps to watch the outputs on a scope. +# Other things +![Intro 3 image](./intro-3-110.png) -The LFO starts as **skewed** sawtooth. In the middle position it is a symmetric triangle wave, at one end a positive sawtooth and at the other a negative sawtooth. The signal is sent to the **saw** output. +[Gray Code](./gray-code.md). Think of it as a semi-random clock divider. Or not. Gray codes have the cool property that only one bit changes at a time. Having only one “thing” change at a time can be interesting for music, so we are hoping you will find some good things to do with it. -The skewed saw then goes to a **waveshaper**. As the shape control is increased the LFO is gradually rounded and then flattened. The shaped LFO is send to the *lfo* output, and used internally to modulate the audio input. +[LFN](./lfn.md) is a random voltage generator made by running low frequency noise through a graphic equalizer. The equalizer gives a lot of easy control over the shape of the randomness. -LFO Controls: - -* **Shape** Flattens the LFO waveform. -* **Skew** Dials in the amount of asymmetry in the LFO. -* **Depth** Shifts and scales the LFO. - -When used as a tremolo effect, you will hear **more tremolo** when these controls are turned up. - -## Chopper clock - -The LFO in Chopper may be synchronized with the ckin signal. There is a built-in **clock multiplier**. To use the synchronization, patch a clock to the ckin, and select x1 from the **clock** knob. To run at a multiple of the input clock, select x2, x3, or x4. - -When Chopper is being synched, the **Phase** control sets the phase difference between the external clock and the synchronized LFO. This may be used to "dial in" the tremolo so that it sounds exactly on the beat (or off the beat). - -There is also an internal LFO that is controlled by the **Rate** control. Set the clock control to *int* to use the internal clock. - -# Thread Booster - -Thread booster raises the priority of VCV Rack's audio rendering thread. In many cases this decreases the annoying pops, ticks, and dropouts that many users are experiencing. - -Many users have reported that Thread Booster helps significantly. Others have reported that it does not help at all. No one has reported a detrimental effect. - -For a deeper dive into the Thread Booster, you should read [this document](./thread-booster.md). - -Thread Booster has a UI that lets you boost the priority of the audio thread. There are three arbitrary settings: normal, boosted, and real time. When the switch is in the bottom position, the plugin does nothing; the audio thread keeps its default priority. In the boost (middle) position, it sets the thread priority to the highest priority non-real-time setting. In the real-time position it attempts to set it to the highest possible priority, or near it. - -If setting the priority fails, the red error light lights up, and the priority stays where it was last. - -To use Thread Booster, just insert an instance into VCV Rack, then adjust the boost switch. In general we recommend the "real time" setting, if it is available on your computer. - -Once Thread booster is in your session, it will boost all the audio processing - it doesn't matter if other modules are added before or after - they all get boosted. - -Linux users - you must read [the detailed document](./thread-booster.md) to use this module. - -Note to users who downloaded the original version of Thread Booster: we've improved it a bit since then, especially on Linux and Windows. - -# Colors variable slope noise generator - -![noise image](../docs/colors.png) - -Colors is a colored noise generator. It can generate all the common **"colors"** of noise, including white, pink, red, blue, and violet. It can also produce all the colors in between, as it has a **continuously variable slope**. - -Colors has a single control, "slope." This is the slope of the noise spectrum, from -8 dB/octave to +8 dB/octave. - -The slope of the noise is quite accurate in the mid-band, but at the extremes we flatten the slope to keep from boosting super-low frequencies too much, and to avoid putting out enormous amounts of highs. So the slope is flat below 40hz, and above 6kHz. - -## Things to be aware of - -When the **slope** changes, Color needs to do a lot of calculations. While this is normally not a problem, it’s possible that quickly changing the slope of many instances of Colors could cause pops and dropouts. - -The slope control does not respond instantly. If you turn the knob, you will hear the change, but if you were to modulate the CV very quickly you might notice the slowness. - -# Growler - -![vocal formant filter image](./growler.jpg) - -**Growler** is a re-creation of the Vocal Animator circuit invented by Bernie Hutchins, and published in Electronotes magazine in the late 70's. It continuously morphs between different vaguely voice like tones. - -**To get a good sound:** run any harmonically rich signal into the input, and something good will come out. Low frequency pulse waves and distorted sounds make great input. - -The controls do pretty much what you would expect: - -* **LFO** controls the speed of the modulation LFOs. -* **Fc** controls the average frequency of the multiple filters. -* **Q** controls the sharpness of the filters. -* **Depth** controls how much of the modulation LFOs are applied to the filters. - -## How Growler works -![growler scope](./growler.png) - -There are four **bandpass filters**, roughly tuned to some typical vocal formant frequencies: 522, 1340, 2570, and 3700 Hz. The filters are run in parallel, with their outputs summed together. - -The first three filter frequencies are modulated by an LFO comprised of **4 triangle wave LFOs** running at different frequencies. They are summed together in various combinations to drive each of the filters. - -Each **CV input stage** is the same: a knob that supplies a fixed offset and a CV input that is processed by an attenuverter. The processed CV is added to the knob voltage. See below for more on [Attenuverters](#atten) and [CV ranges](#cv). - -The **LFO** Rate control shifts the speed of all 4 LFOs while maintaining the ratio of their frequencies. - -The **Fc** control moves the frequencies of the first three filters, but not by equal amounts. The lowest filter moves at 1V/Oct, but the middle two move less. The top filter is fixed at 3700 Hz. - -The **Q** control does just what it says - controls the Q (resonance) of the filters. - -The **Modulation Depth** controls how much of the summed LFOs get to each filter. Again, the lower filters move farther, and the top filter is fixed. - -The smaller knobs next to the main knobs are **attenuverters**, which scale control voltages. For more on attenuverters, [see below](#atten) - -There are three LFO outputs next to the blinking LFOs. These may be used to modulate other modules, or as semi-random voltage sources. - -**Bass boost** switch. When it’s in the up position (on) there should be more bass. This is done by switching some or all of the filters from bandpass to lowpass. - -LFO **Matrix** switch. This is the unlabeled switch in the LFO section. When it’s down (default position) the LFOs are closely correlated. In the middle we try to make them a little bit more independent. When it’s in the up position the LFOs will often go in different directions. - -# Booty Shifter frequency shifter - -**Booty Shifter** is a frequency shifter inspired by the Moog/Bode frequency shifter module. - -![booty shifter image](./booty-shifter.png) - -The name "Booty Shifter" is a nod to the classic analog module, as well as to a black cat named Booty. - -Booty Shifter will take an audio input and shift the frequencies up or down. This is not like a pitch shift where harmonics will remain in tune; it is an absolute frequency shift in Hz, so in general **harmonics will go way out of tune.** It is similar to a ring-modulator, but less extreme and more versatile. - -## Getting good sounds from Booty Shifter - -Feed in music and shift the frequency a good amount. - -Feed in **speech or radio** and shift it. - -Feed the CV from a **sequencer** to sequence the mayhem. - -Shift **drums** up or down a little bit to re-tune them without the usual pitch-shifting artifacts. - -Small shifts in conjunction with delays can make a chorus-like effect to thicken music. - -## Inputs and outputs - -* **IN** is the audio input. -* **CV** is the pitch shift control voltage. -5V will give minimum shift, +5 will give maximum. -* **DN** is the down-shifted output. -* **UP** is the up-shifted output. - -## Controls - -**RANGE** sets the total shift range in Hz. For example, the 50 Hz setting means that the minimum shift is 50 Hz down, and the maximum is 50 Hz up. - -Range value **Exp is different**. Here minimum shift is 2 Hz, maximum is 2 kHz, with an exponential response. As of version 0.6.2 the response is an accurate 1 Volt per Octave. - -Shift **AMT** is added to the control voltage, with a range of -5..5. - -## Oddities and limitations - -If you shift the frequency up too far, it will alias. There is no anti-aliasing, so if the highest input frequency + shift amount > sample_rate / 2, you will get aliasing. Of course the Bode analog original did not alias. - -If you shift the input down a lot, frequencies will go **below zero and wrap around**. Taken far enough this will completely **reverse the spectrum** of the input. This was a prized feature of the Bode original. - -As you shift the input down, you may start to generate a lot of subsonic energy. A **High Pass filter** may clean this up. - -The down shift **frequency fold-over**, while true to the original, does cause problems when trying to pitch drum tracks down a lot. High pass filtering the input before it is down-shifted can control this. - -# Formants vocal filter - -![formants image](./formants.png) - -Like the **Vocal Animator**, this is a filter bank tuned to the formant frequencies of typical **singing voices**. Unlike Growler, however, the filters do not animate on their own. In addition, the filters are preset to frequencies, bandwidths, and gains that are taken from **measurements of human singers**. - -One of the easiest ways to **get a good sound** from Formants is to use it like a regular VCF. For example, control Fc with an ADSR. Then put a second modulation source into the vowel CV - something as simple as a slow LFO will add interest. - -Use it as a **filter bank**. Just set the knobs for a good sound and leave it fixed to add vocal tones to a pad. Again, modulating the vowel CV can easily give great results. - -Try to synthesize something like **singing** by sequencing the vowel CV of several formants. Leave the Fc in place, or move it slightly as the input pitches move. - -Controls: - -* **Fc** control moves all the filters up and down by the standard one "volt" per octave. -* **Vowel** control smoothly interpolates between 'a', 'e', 'i', 'o', and 'u'. -* **Model** control selects different vocal models: bass, tenor, countertenor, alto, and soprano. -* **Brightness** control gradually boosts the level of the higher formants. When it is all the way down, the filter gains are set by the singing models in the module, which typically fall off with increasing frequency. As this control is increased the gain of the high formant filters is brought up to match the F1 formant filter. - -The **LEDs across the top** indicate which formant is currently being "sung". - -## About Attenuverters - -The small knobs next to the bigger knobs are **attenuverters**. They scale and/or invert the control voltage inputs next to them. When they are turned all the way up the full CV comes through. As they are turned down less CV comes through. Straight up none passes. As they are turned down further the CV comes back, but inverted. - -Sometimes we use attenuverters with a *linear taper*, and sometimes we use an *audio taper*. If you find that on a particular module you do not like the response of the attenuverters, please log a github issue. - -## Control voltage ranges - -Our modules usually expect a control voltage range of **-5 to +5**. The associated offset knobs will also add -5 to +5. After attenuverters are applied to CV the knob value is added. After all that, the result is usually clipped to the -5 to +5 range. +[Thread Booster](./booster.md) reduces pops and clicks in VCV Rack by reprogramming VCV's audio engine. \ No newline at end of file diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/both.png b/vst2_bin/plugins/squinkylabs-plug1/docs/both.png new file mode 100644 index 00000000..a0cf4e05 Binary files /dev/null and b/vst2_bin/plugins/squinkylabs-plug1/docs/both.png differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/chebyshev.md b/vst2_bin/plugins/squinkylabs-plug1/docs/chebyshev.md new file mode 100644 index 00000000..31334517 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/docs/chebyshev.md @@ -0,0 +1,145 @@ +# Chebyshev +Our waveshaper VCO. + +![Functional image](../docs/chebyshev.png) + +## Description of the module + +Chebyshev polynomials have been used to generate complex tones since the early days of computer music. This special math discovered by Mr. Chebyshev enables digital generation of waveforms with any overtone structure using very little computer power. In addition, it is easy and computationally inexpensive to vary the spectrum over time. + +Eventually, this form of synthesis fell out of favor, as FM could provide a wider variety of timbres with acceptable CPU usage. Now, however, the distinctive sound of this form of synthesis provides another unique source of sounds for VCV Rack users. + +The magic of the Chebyshev polynomials is that if a sine wave with amplitude one is put into a Chebyshev polynomial, the output will still be a sine wave, but multiplied in pitch by an integer. + +In our implementation we include the first ten Chebyshev polynomials to generate the first ten harmonics of the harmonic series. These are then mixed together based on knob settings and control voltages to give an output tone with complete control over ten harmonics. + +Many of the controls in this module allow different ways of mixing together these ten harmonics. In addition external signals may be shaped by the waveshaper, with folding or clipping applied. + +Like all our modules, Chebyshev's CPU usage is quite low. + +## Some tips + +It can be a great help learning this module if you patch the output to a Scope module and a frequency analyzer. As you adjust the controls it will be clearer what is going on. + +Also, note that we will repeat that when set up normally, each Chebyshev waveshaper is producing a different harmonic of the VCO output. Because of this, we will refer to these as "harmonic levels" and "waveshaper levels" interchangeably. + +And - each waveshaper is a perfect harmonic only when driven by a pure sine at exactly 1Vp-p. + +## Signal Flow + +First there is a sine wave VCO. It has the controls you would expect, as well as a through-zero linear FM input, which allows a minimal DX7-style FM. + +The VCO output then goes to a wave folder/clipper with gain controls. This allows for some distortion effects, and keeps the signal in a range that will keep the next stage happy. + +The output of the folder/clipper then goes to ten parallel Chebyshev waveshapers. The outputs of these are then mixed together through a specialized mixer. + +When everything is set in a typical manner, each of the Chebyshev waveshapers will be outputting a pure sine wave at an integer multiple of the fundamental frequency. Thus, each one will be a discrete harmonic. + +## Description of the controls + +### VCO + +The controls in the upper right are all for the sine wave VCO. +Octave transposes the pitch in even octaves. + +* **Tune** raises or lowers the pitch by up to a perfect fifth. +* **Mod** controls the modulation (exponential FM) depth of the signal patched to the Mod jack. +* **LFM** controls the linear FM depth of the signal patched to the LFM jack. +* **V/Oct** input is where the main control voltage is patched. + +Mod and LFM perform different functions. Mod, like the CV input, is an exponential control. If an LFO is patched into the Mod input and the Mod depth is adjusted for a vibrato of one semitone, that vibrato will be one semitone regardless of the base pitch. But if an audio rate signal is patched into the Mod input you will tend to get “clangorous” sounds with inharmonic overtones. + +LFM, on the other hand, allows through-zero linear FM. While this is not very good for vibrato it does create complex timbres where the harmonics are in tune, and that "in tuneness" will remain as the mod depth is changed. Exponential FM at audio rates can also be tuned, but the tuning will disappear as the mod depth changes, making it impossible to to generate dynamic harmonic sounds. + +### Folder/Clipper + +Chebyshev polynomials are poorly behaved if they see more than one (volt) at their input. So we use a folder/clipper to make sure this doesn’t happen. + +The controls and CV of the Folder/Clipper: + +* **Fold/Clip** switch. In clip mode, it is a simple hard clipper. In fold mode it’s a waveform folder. +* **Clip LED**. The LED will be green when there is signal, and red when the folder/clipper engages. +* *Gain*. Controls how hard the folder/clipper is driven. Gain knob and CV are combined. +* **Gain trim**. The small knob below the **gain** knob is an attenuator for the **gain** CV. New in 0.6.9. +* **EG**. Also combines with the gains. +* **Ext In.** When a signal is patched here it replaces the internal VCO, allowing any signal to be run through the waveshaper. + +Note that while you can get some cool effects with clipping and folding, they will tend to cause audible aliasing at higher frequencies. Use with care. + +In classic waveshaping synthesis an ADSR or similar would be connected to the EG input. By dynamically changing the level of the sine wave hitting the waveshapers a dynamic timbre will be generated. + +The output of the folder/clipper drives the Chebyshev waveshapers. The last group of controls all work together to determine how the waveshapers are mixed together. + +### Waveshaper controls + +There are a lot of controls that work together to determine how the waveshapers are mixed. When configured normally, that means these controls determine the ratios of all the harmonics of the VCO. + +The **small knobs** running up the left side individually control each waveshaper/harmonic, with fundamental on top, and harmonic 10 on the bottom. + +The input jacks next to them allow the levels of each harmonic to be voltage controlled. + +The **Preset** button toggles all ten harmonics between some good starting points, and also resets the Gain to be exactly 1. + +The **Even** control increases/decreases the level of all the even harmonics together. + +The **Odd** control increases/decreases the level of all the odd harmonics together. + +The **Slope** control will apply a gradual roll-off of the upper harmonics. When it is all the way down the roll-off is 18 decibels per octave. When it is all the way up it’s flat. + +Note that the level of the fundamental is not affected by either the Even or Odd control. + +The Odd, Even, and Slope controls may be thought of as subtractive. When they are all the way up, they have no effect, and you get the mix you would expect from the individual harmonic levels. When you turn these controls down they will reduce the levels of the corresponding harmonics. + +The **Preset** button toggles between two or three settings. It will always have a setting where the fundamental is full and all other harmonics off, and a setting where all harmonics are up full. In addition, if you started with your own setting of the harmonics, the preset button will eventually take you back there, but with the master gain set back to one. + +## Several patching ideas + +### Arbitrary waveform VCO + +Turn the Odd, Even, and Slope controls all the way up. Then the level of each harmonic is controller its own volume control. Mix the harmonics to get a pleasing sound, then use as a static timbre, or run it into a VCO. + +Or start with the individual harmonics all the way up, manipulate Even/Odd/Slope to start sculpting the harmonics. + +Don’t forget the Preset button - it’s your friend here. + +### Dynamic Waveshaping + +Use the built-in VCO. Adjust the harmonic mix to something nice and bright. Then connect an ADSR to the EG input. The ADSR will more or less control the brightness. Make sure the the clip LED is just on the edge of clipping when the EG input is at max. + +At very low levels the output will be primarily fundamental. At max level it will be determined by the waveshaper mix controls. The timbre will go from dull to bright as the EG input increases, but the evolution of the timbre will not be completely even, and definitely will be different than what you would get modulating a VCF with an ADSR. + +The evolution of timbres often sounds "brassy," like a brass instrument. Brass synthesis was indeed a common use of waveshaping synthesis before the era of affordable sampling and physical modelling. + +### Voltage controlled filter slope + +Most conventional VCFs allow a filter of a specific shape to be modulated up and down in frequency. A variable slope VCF lets the shape of the filter be modulated. Modulating the filter slope can be more "natural" sounding. + +In the case of Chebyshev we don’t have a filter, but by controlling the harmonic levels directly we can mimic one. Set up a nice bright sound, patch an ADSR into the Slope input, and try out a simulated variable slope filter. + +### Voltage control of spectrum + +The possibilities for timbral variation seem limitless if you take the time to patch controls signals into the harmonic level CV inputs. Use all the usual suspects here - clocks, LFOs, shift registers, sequencers. + +### FM oscillator + +The inclusion of the LFM input allows a simple form of FM synthesis - one operator FM. + +Use an external sine VCO, and patch it into the LFM input on Chebyshev. Turn up the LFM knob. Use the Preset button to turn up the fundamental and turn off the other waveshaper outputs. + +In FM speak, the external VCO is the modulator and the VCO in Chebyshev is the carrier. The result should be consonant if the frequency of the carrier is a small integer multiple of the modulator frequency. For example, set the modulator an octave lower than the carrier. + +Once again, as the modulation is increased more harmonics will be present, so use an external ADSR and VCA to modulate the level of the modulator sine before it’s patched into Chebyshev. + +Of course FM will work alongside the waveshapers, so feel free to go crazy with all the knobs. But don’t be disappointed if the results are harsh and strange. + +### Process external signals + +Run something other than a sine wave into Ext In, then process your signal with the folder and/or the Chebyshev waveshapers. Again, the output of the waveshapers can be pretty unpredictable as the input signal becomes more complex. + +## A note about Aliasing + +In the standard configuration there will be little, if any, aliasing. Since the highest harmonic is 10X the fundamental, the Chebyshev module can’t even start to alias until the fundamental gets to 2kHz. + +That said, there is no anti-aliasing in this module. The wavefolder can easily alias. Normally the LFM will not alias very much, but with high modulation depth and high pitches it will alias quite a lot. + +We have an informational article that talks more about aliasing [here](./aliasing.md). \ No newline at end of file diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/chebyshev.png b/vst2_bin/plugins/squinkylabs-plug1/docs/chebyshev.png new file mode 100644 index 00000000..1417d0fd Binary files /dev/null and b/vst2_bin/plugins/squinkylabs-plug1/docs/chebyshev.png differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/chopper.md b/vst2_bin/plugins/squinkylabs-plug1/docs/chopper.md new file mode 100644 index 00000000..f6293da6 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/docs/chopper.md @@ -0,0 +1,36 @@ +# Chopper tremolo / programmable LFO +![chopper image](./chopper.png) + +In its simplest use, Chopper produces a very wide range of **tremolo** effects. The built-in LFO can produce a wide range of waveforms that cover many of the waveforms produced by the tremolo circuits built into **vintage guitar amplifiers**. + +The LFO is sent to an output so that it may modulate other modules. + +There is also a **clock synchronizer** and multiplier. + +To use Chopper as a tremolo, send a signal to the *in* jack, and listen to the *out* jack. Leave the *clock* control at the default *int* setting. Most of the knob settings will now affect the tremolo effect. + +## Chopper LFO + +![chopper LFO image](../docs/lfo-waveforms.png) + +To understand all the LFO settings, it helps to watch the outputs on a scope. + +The LFO starts as **skewed** sawtooth. In the middle position it is a symmetric triangle wave, at one end a positive sawtooth and at the other a negative sawtooth. The signal is sent to the **saw** output. + +The skewed saw then goes to a **waveshaper**. As the shape control is increased the LFO is gradually rounded and then flattened. The shaped LFO is send to the *lfo* output, and used internally to modulate the audio input. + +LFO Controls: + +* **Shape** Flattens the LFO waveform. +* **Skew** Dials in the amount of asymmetry in the LFO. +* **Depth** Shifts and scales the LFO. + +When used as a tremolo effect, you will hear **more tremolo** when these controls are turned up. + +## Chopper clock + +The LFO in Chopper may be synchronized with the ckin signal. There is a built-in **clock multiplier**. To use the synchronization, patch a clock to the ckin, and select x1 from the **clock** knob. To run at a multiple of the input clock, select x2, x3, or x4. + +When Chopper is being synched, the **Phase** control sets the phase difference between the external clock and the synchronized LFO. This may be used to "dial in" the tremolo so that it sounds exactly on the beat (or off the beat). + +There is also an internal LFO that is controlled by the **Rate** control. Set the clock control to *int* to use the internal clock. diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/chopper.png b/vst2_bin/plugins/squinkylabs-plug1/docs/chopper.png new file mode 100644 index 00000000..8bb51887 Binary files /dev/null and b/vst2_bin/plugins/squinkylabs-plug1/docs/chopper.png differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/clip-collage.png b/vst2_bin/plugins/squinkylabs-plug1/docs/clip-collage.png new file mode 100644 index 00000000..3fa46cca Binary files /dev/null and b/vst2_bin/plugins/squinkylabs-plug1/docs/clip-collage.png differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/clip.png b/vst2_bin/plugins/squinkylabs-plug1/docs/clip.png new file mode 100644 index 00000000..8d6a96c3 Binary files /dev/null and b/vst2_bin/plugins/squinkylabs-plug1/docs/clip.png differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/colors.md b/vst2_bin/plugins/squinkylabs-plug1/docs/colors.md new file mode 100644 index 00000000..9e34a297 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/docs/colors.md @@ -0,0 +1,9 @@ +# Colors variable slope noise generator + +![noise image](../docs/colors.png) + +Colors is a colored noise generator. It can generate all the common **"colors"** of noise, including white, pink, red, blue, and violet. It can also produce all the colors in between, as it has a **continuously variable slope**. + +Colors has a single control, "slope." This is the slope of the noise spectrum, from -8 dB/octave to +8 dB/octave. + +The slope of the noise is quite accurate in the mid-band, but at the extremes we flatten the slope to keep from boosting super-low frequencies too much, and to avoid putting out enormous amounts of highs. So the slope is flat below 40hz, and above 6kHz. diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/crush.png b/vst2_bin/plugins/squinkylabs-plug1/docs/crush.png new file mode 100644 index 00000000..8c0e976d Binary files /dev/null and b/vst2_bin/plugins/squinkylabs-plug1/docs/crush.png differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/emitter-coupled.png b/vst2_bin/plugins/squinkylabs-plug1/docs/emitter-coupled.png new file mode 100644 index 00000000..1953a3dc Binary files /dev/null and b/vst2_bin/plugins/squinkylabs-plug1/docs/emitter-coupled.png differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/ev3-panel.png b/vst2_bin/plugins/squinkylabs-plug1/docs/ev3-panel.png new file mode 100644 index 00000000..00b1029f Binary files /dev/null and b/vst2_bin/plugins/squinkylabs-plug1/docs/ev3-panel.png differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/ev3.md b/vst2_bin/plugins/squinkylabs-plug1/docs/ev3.md new file mode 100644 index 00000000..739c80ba --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/docs/ev3.md @@ -0,0 +1,63 @@ +# EV3 triple VCO + +![ev3 image](./ev3-panel.png) + +## About EV3 + +EV3 is made from the sound generating part of Befaco’s EvenVCO, replicated three times. dWe added sync, which was never implemented in the EvenVCO. Then we did our usual overhaul to reduce CPU usage. + +The result is a module containing three VCOs that together use about the same amount of CPU as a single instance of EvenVCO. + +While the waveform generation and alias suppression is lifted directly from EvenVCO, the control selection and response of EV3 is our own design. While the three VCOs are easiest to use together, they may be used completely independently. + +EvenVCO remains an excellent choice for a VCO, but there are definitely some cases where you might choose EV3 instead: + +* It is very easy to make giant stacked oscillator patches, like we did in the old days. + +* One EV3 uses less panel space and CPU than three separate instances of EvenVCO. + +* The sync feature is a welcome addition. + +* The semitone pitch offset can inspire patches and harmonies that you might not find with the standard controls on most VCOs. + +* It is easy to patch up chords with EV3. + +That said, EV3 only offers one waveform output at a time per VCO, whereas EvenVCO makes them all available at the same time. +Using EV3 + +The Initial pitch is controlled by a stepped octave control, a stepped semitone control, and a fine tune. The octave and semitone are displayed as an octave number and a musical interval. + +VCO 2 and 3 have an option for hard sync. The sync input is always the saw output of VCO 1. + +There are independent outputs for each VCO, as well as a 3-to-1 mixer driving a mixed output. + +The CV connections are a bit unusual. If you patch one of the top inputs (VCO 1 inputs) it will drive all three. Each VCO will pick up its input from the first patched input. So, for example, VCO 2 will get its input from the second row, but if nothing is patched to this input it will pick up input from the first row (VCO 1). + +This makes for much less patching when stacking two or three VCO sections in a single voice. + +The controls in depth +The octave knob is at the top left. It is unlabeled, but does have the octave number displayed on top of it. It has a 10 octave range, just like EvenVCO. + +The semitone knob just to the right will add or subtract up to 12 semitones. The label above the knob displays the semitone offset as an interval in diatonic harmony. For example, 7 semitones up is labeled “5th”. Note that the intervals displayed are always an octave plus a transposition up. So lowering 5:0 by two semitones will give you 4:m7th – so it’s displayed as one octave down and a minor 7th up. Note that some of these intervals have more than one spelling. In these cases we made some arbitrary decisions: + +* One semitone up is called minor second, although some would call it a flat second. +* Six semitones up is called Diminished fifth, although it could be called an augmented fourth or a tritone. +* No transposition is displayed as zero, although it would be more correct to call it P1, or perhaps unison. + +The fine control transposes the VCO pitch up and down up to a semitone. + +The mod knob controls the amount of pitch modulation applied to the Fm input CV. This is of course exponential pitch modulation, suitable for pitch bending and vibrato, but not so much for FM synthesis which works linear FM. + +Below the blue knobs are two small black knobs to control the pulse width and pulse width modulation depth. These only have an effect when the square wave output is selected. Although they are only labeled on VCO1, they function just the same on VCO 2 and 3. + +The switches below the knobs are radio buttons that select the waveform for each VCO. The waveforms are sine, triangle, saw-tooth, square, even, and off. The even waveform is what gave the original EvenVCO its name. It is an unusual waveform that has only even harmonics, and not odd ones (not counting the fundamental, which is there). Selecting “no waveform” can be useful when you are patching and want to hear each VCO by itself – like a mute button on a mixing console. + +The CV inputs are at the bottom. The top row is for VCO1, the next for VCO2, and the last row is for VCO3. + +V/Oct is where the main pitch CV is patched, and sets the overall pitch of the VCO. Fm is a less sensitive pitch input used to modulate the pitch. As noted above, the sensitivity of this input is controlled by the Mod knob. The last column of CV inputs is for pulse width modulation. This only has an effect when the square wave is selected. It works in conjunction with the PW and PWM knobs. + +The output section has a column of three output level controls, one for each VCO. Then there are the three jack, one for each VCO output, and a mixed output. + +We have an informational article that talks more about aliasing. It shows you how to compare different modules using a spectrum analyzer. [Aliasing Story](./aliasing.md). + +If you would like some information on how we reduced the CPU usage of EvenVCO, you can [find it here](../docs/vco-optimization.md). \ No newline at end of file diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/fold-collage.png b/vst2_bin/plugins/squinkylabs-plug1/docs/fold-collage.png new file mode 100644 index 00000000..03dc7c77 Binary files /dev/null and b/vst2_bin/plugins/squinkylabs-plug1/docs/fold-collage.png differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/folder-II.png b/vst2_bin/plugins/squinkylabs-plug1/docs/folder-II.png new file mode 100644 index 00000000..542ea375 Binary files /dev/null and b/vst2_bin/plugins/squinkylabs-plug1/docs/folder-II.png differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/folder.png b/vst2_bin/plugins/squinkylabs-plug1/docs/folder.png new file mode 100644 index 00000000..80d2b809 Binary files /dev/null and b/vst2_bin/plugins/squinkylabs-plug1/docs/folder.png differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/formants.md b/vst2_bin/plugins/squinkylabs-plug1/docs/formants.md new file mode 100644 index 00000000..484718a2 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/docs/formants.md @@ -0,0 +1,20 @@ +# Formants vocal filter + +![formants image](./formants.png) + +Like the **Vocal Animator**, this is a filter bank tuned to the formant frequencies of typical **singing voices**. Unlike Growler, however, the filters do not animate on their own. In addition, the filters are preset to frequencies, bandwidths, and gains that are taken from **measurements of human singers**. + +One of the easiest ways to **get a good sound** from Formants is to use it like a regular VCF. For example, control Fc with an ADSR. Then put a second modulation source into the vowel CV - something as simple as a slow LFO will add interest. + +Use it as a **filter bank**. Just set the knobs for a good sound and leave it fixed to add vocal tones to a pad. Again, modulating the vowel CV can easily give great results. + +Try to synthesize something like **singing** by sequencing the vowel CV of several formants. Leave the Fc in place, or move it slightly as the input pitches move. + +Controls: + +* **Fc** control moves all the filters up and down by the standard one "volt" per octave. +* **Vowel** control smoothly interpolates between 'a', 'e', 'i', 'o', and 'u'. +* **Model** control selects different vocal models: bass, tenor, countertenor, alto, and soprano. +* **Brightness** control gradually boosts the level of the higher formants. When it is all the way down, the filter gains are set by the singing models in the module, which typically fall off with increasing frequency. As this control is increased the gain of the high formant filters is brought up to match the F1 formant filter. + +The **LEDs across the top** indicate which formant is currently being "sung". diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/full-wave.png b/vst2_bin/plugins/squinkylabs-plug1/docs/full-wave.png new file mode 100644 index 00000000..35f4986a Binary files /dev/null and b/vst2_bin/plugins/squinkylabs-plug1/docs/full-wave.png differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/functional-vco-1.md b/vst2_bin/plugins/squinkylabs-plug1/docs/functional-vco-1.md new file mode 100644 index 00000000..62a402b9 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/docs/functional-vco-1.md @@ -0,0 +1,13 @@ +# Functional VCO-1 + +![Functional image](../docs/functional.png) + +Functional VCO-1 works just like its namesake. The control layout is familiar, the sound is the same, but it uses about 1/4 as much CPU as the original. + +We believe VCV's Fundamental VCO is an unsung hero. It's one of the few VCOs that never has audible aliasing artifacts. You can sync it, and modulate all its inputs, but the sound never falls apart. + +We "forked" the code to Fundamental VCO-1 and modified it a little bit to make it much more CPU efficient. Now you may use a lot more of them without pops, clicks, and dropouts. + +If you would like the details of how we did this optimization, you can [find them here](../docs/vco-optimization.md). + +We have an informational article that talks more about aliasing. It shows you how to compare different modules using a spectrum analyzer. [Aliasing Story](./aliasing.md). diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/functional.png b/vst2_bin/plugins/squinkylabs-plug1/docs/functional.png new file mode 100644 index 00000000..2c927ee3 Binary files /dev/null and b/vst2_bin/plugins/squinkylabs-plug1/docs/functional.png differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/gray-code.md b/vst2_bin/plugins/squinkylabs-plug1/docs/gray-code.md new file mode 100644 index 00000000..49949695 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/docs/gray-code.md @@ -0,0 +1,18 @@ +# Gray Code: eclectic clock divider + +![gray code image](./gray-code.png) + +## About Gray Code +A cool feature of gray codes is that only one bit changes at a time. Having only one “thing” change at a time can be interesting for music, so we are hoping you will find some good things to do with it. + +WikiPedia has a very good article on gray codes: https://en.wikipedia.org/wiki/Gray_code + +Our Gray Code module has only one control. It selects between standard gray code and balanced gray codes. With a standard gray code, the lower bits change much more often than the high bits. You can see it counting up. With the balanced gray codes, all the bits change more or less the same amount, but of course no two ever change at the same time. + +Each bit of the 8-bit gray code comes out to an output jack. The LED beside the output shows when it goes high and low. + +There is an additional output that adds up all the bits with a binary weighting, kind of like a DAC. + +The external clock input must be driven with a clock - there is no internal clock. + +Now let your imagination run wild! \ No newline at end of file diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/gray-code.png b/vst2_bin/plugins/squinkylabs-plug1/docs/gray-code.png new file mode 100644 index 00000000..25d440cf Binary files /dev/null and b/vst2_bin/plugins/squinkylabs-plug1/docs/gray-code.png differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/growler.md b/vst2_bin/plugins/squinkylabs-plug1/docs/growler.md new file mode 100644 index 00000000..ef9e3397 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/docs/growler.md @@ -0,0 +1,39 @@ +# Growler + +![vocal formant filter image](./growler.jpg) + +**Growler** is a re-creation of the Vocal Animator circuit invented by Bernie Hutchins, and published in Electronotes magazine in the late 70's. It continuously morphs between different vaguely voice like tones. + +**To get a good sound:** run any harmonically rich signal into the input, and something good will come out. Low frequency pulse waves and distorted sounds make great input. + +The controls do pretty much what you would expect: + +* **LFO** controls the speed of the modulation LFOs. +* **Fc** controls the average frequency of the multiple filters. +* **Q** controls the sharpness of the filters. +* **Depth** controls how much of the modulation LFOs are applied to the filters. + +## How Growler works +![growler scope](./growler.png) + +There are four **bandpass filters**, roughly tuned to some typical vocal formant frequencies: 522, 1340, 2570, and 3700 Hz. The filters are run in parallel, with their outputs summed together. + +The first three filter frequencies are modulated by an LFO comprised of **4 triangle wave LFOs** running at different frequencies. They are summed together in various combinations to drive each of the filters. + +Each **CV input stage** is the same: a knob that supplies a fixed offset and a CV input that is processed by an attenuverter. The processed CV is added to the knob voltage. See below for more on [Attenuverters](#atten) and [CV ranges](#cv). + +The **LFO** Rate control shifts the speed of all 4 LFOs while maintaining the ratio of their frequencies. + +The **Fc** control moves the frequencies of the first three filters, but not by equal amounts. The lowest filter moves at 1V/Oct, but the middle two move less. The top filter is fixed at 3700 Hz. + +The **Q** control does just what it says - controls the Q (resonance) of the filters. + +The **Modulation Depth** controls how much of the summed LFOs get to each filter. Again, the lower filters move farther, and the top filter is fixed. + +The smaller knobs next to the main knobs are **attenuverters**, which scale control voltages. For more on attenuverters, [see below](#atten) + +There are three LFO outputs next to the blinking LFOs. These may be used to modulate other modules, or as semi-random voltage sources. + +**Bass boost** switch. When it’s in the up position (on) there should be more bass. This is done by switching some or all of the filters from bandpass to lowpass. + +LFO **Matrix** switch. This is the unlabeled switch in the LFO section. When it’s down (default position) the LFOs are closely correlated. In the middle we try to make them a little bit more independent. When it’s in the up position the LFOs will often go in different directions. diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/half-wave.png b/vst2_bin/plugins/squinkylabs-plug1/docs/half-wave.png new file mode 100644 index 00000000..fc599076 Binary files /dev/null and b/vst2_bin/plugins/squinkylabs-plug1/docs/half-wave.png differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/intro-1-110.png b/vst2_bin/plugins/squinkylabs-plug1/docs/intro-1-110.png new file mode 100644 index 00000000..6e225cda Binary files /dev/null and b/vst2_bin/plugins/squinkylabs-plug1/docs/intro-1-110.png differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/intro-2-110.png b/vst2_bin/plugins/squinkylabs-plug1/docs/intro-2-110.png new file mode 100644 index 00000000..a583ec64 Binary files /dev/null and b/vst2_bin/plugins/squinkylabs-plug1/docs/intro-2-110.png differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/intro-3-110.png b/vst2_bin/plugins/squinkylabs-plug1/docs/intro-3-110.png new file mode 100644 index 00000000..b98b1e7b Binary files /dev/null and b/vst2_bin/plugins/squinkylabs-plug1/docs/intro-3-110.png differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/lfn.md b/vst2_bin/plugins/squinkylabs-plug1/docs/lfn.md new file mode 100644 index 00000000..0d55c615 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/docs/lfn.md @@ -0,0 +1,17 @@ +# LFN Low Frequency Noise Generator + +![LFN image](../docs/lfn.png) + +LFN stands for Low Frequency Noise. Technically it is a white noise generator run through a graphic equalizer at extremely low frequencies. People may find it easier to think of it as a random voltage source with unique control over the output. + +The top knob, which is unlabeled, sets the "base frequency" of LFN. + +The five other knobs, and the CV inputs beside them, control the gain of the graphic equalizers sections. Beside each EQ gain knob is a label indicating what frequency range that knob controls. + +For example, it the base frequency is 1.0, the EQ sections will be at 1Hz, 2Hz, 4Hz, 8Hz, and 16Hz. If the base frequency is 0.2, The EQ sections will be at 0.2Hz, 0.4Hz, 0.8Hz, 1.6Hz, and 3.2Hz. + +But instead of thinking about frequencies like 1Hz, which are a little difficult to imagine, think of the knobs as mixing very slow random voltages, with less slow ones. For example if LFN is driving a pitch quantizer into a VCO, turn all the knobs down to zero except the first, lowest, one. This will make a series of pitches slowly rising and falling. Then bring in a little of the faster channels. The pitch will still be slowly rising and falling, but will also quickly move up and down by smaller steps. + +A good way to learn what makes LFN tick is to set it slow and watch it on the scope. At the same time run it straight into a VCO. Experiment with different mixes of the slow knobs and the fast ones. + +As you would expect from Squinky Labs, the CPU usage of LFN is very low. In fact it is one of our leanest modules yet. So feel free to use as many instances as you like. diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/lfn.png b/vst2_bin/plugins/squinkylabs-plug1/docs/lfn.png new file mode 100644 index 00000000..63598c42 Binary files /dev/null and b/vst2_bin/plugins/squinkylabs-plug1/docs/lfn.png differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/odd.png b/vst2_bin/plugins/squinkylabs-plug1/docs/odd.png new file mode 100644 index 00000000..a84919fe Binary files /dev/null and b/vst2_bin/plugins/squinkylabs-plug1/docs/odd.png differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/release-notes.md b/vst2_bin/plugins/squinkylabs-plug1/docs/release-notes.md new file mode 100644 index 00000000..a46b13a9 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/docs/release-notes.md @@ -0,0 +1,19 @@ +# Release notes for Squinky Labs modules + +## 0.6.9 + +Introduced three new modules: EV3, Gray Code, and Shaper. + +Added a trim control for external gain CV in Chebyshev. Previously saved patches may require that the gain trim be increased. + +Minor graphic tweaks to module panels. + +## 0.6.8 + +Introduced Chebyshev waveshaper VCO. + +Introduced release notes. + +Lowered the distortion in sin oscillator that is used in all modules. + +Re-ordered and re-worded module names in the browser. \ No newline at end of file diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/shaper-panel.png b/vst2_bin/plugins/squinkylabs-plug1/docs/shaper-panel.png new file mode 100644 index 00000000..ab5cc72f Binary files /dev/null and b/vst2_bin/plugins/squinkylabs-plug1/docs/shaper-panel.png differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/shaper.md b/vst2_bin/plugins/squinkylabs-plug1/docs/shaper.md new file mode 100644 index 00000000..ba66fa71 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/docs/shaper.md @@ -0,0 +1,105 @@ +# Shaper precision waveshaper + +![shaper image](./shaper-panel.png) + +## About Shaper +Shaper is a waveshaper offering many different shape options. Some of these shapes are commonly found in other wave shapers, and some are unique to Shaper. It can be used to modify the waveforms from a VCO, or to add distortion to some other sound. And, as usual, the creative user may use it to process control voltages, or other "left field" uses. + +A unique feature of Shaper is that it has very little aliasing, whereas most we have seen have a lot of aliasing. The other special thing about Shaper is that it has a few shapes that are good for "soft overdrive". + +Although there are many creative ways to use a wave shaper, the two most common are as a mangled waveform shaper, and as a distortion/overdrive effect. + +In the first use case, the waveshaper is often connected directly to the output of a VCO. This gives a large number of different sounds from the VCO. In this use, typically extreme settings are used, with folding being a classic example. + +In the second use case, as a distortion/overdrive effect, often less extreme settings are used. For example, it would be unusual to run a recording of singing through a wavefolder, but a gentle overdrive is pretty common. + +The switch with the labels 16X, 4X, and 1X controls the amount of oversampling. This is how Shaper keeps aliasing under control. Waveshapers by their nature generate a lot of harmonics at high frequencies, and these tend to “fold back” into the audio range as aliasing. Oversampling reduces this effect by doing all the processing at a higher sample rate, then removing the frequencies that are too high, and reducing the sample rate back down. The more oversampling, the less aliasing. + +At 16X, Shaper is oversampling by a factor of 16. So for a 44,100 sampling rate, Shaper would be working at 705kHz! This is the oversampling rate used by Fundamental VCO-1 and Functional VCO-1. At this setting it is very difficult to hear or measure any aliasing at all, although it is present in tiny amounts. + +In general we recommend 16X, but there are several reasons you might want to set it lower. Firstly, you may actually want aliasing. When using Shaper as an extreme mangler the extra grit and digital nasties may fit perfectly. The other reason is CPU usage. Although Shaper at X16 does not use a large amount of CPU, it uses a lot less at 4X or 1X. It’s pretty much proportional to the setting. At 16X Shaper does 16 times as much work as at 1X. + +If you are using one of the gentler settings of Shaper, 4X may have completely inaudible alisasing also. With some settings, however, we can measure significant aliasing at 4X. If you have plenty of CPU, just leave it at 16X or 1X. But if you are running out of CPU 4X can be a very workable and smooth sounding alternative. + +Aside from the Oversampling selector, there are three controls: + +*Gain* – boosts the input signal, which tends to cause more shaping, distorting, and mangling. Controlled by the gain knob, and the gain CV. There is an attenuator on the gain CV. + +*Offset* – shifts the signal before it hits the waveshaper. In general that will increase the level of even harmonics in the output. Many of the shapes will output no even harmonics at all if the offset is zero. Like the gain, the offset is controlled by a knob, and a CV with attenuator. +The offset is bidirectional, so there is no offset when the knob is straight up and the CV is zero. + +*Shape* – this is the big unlabeled button. It selects from the different shapes that Shaper can produce. The name of the selected shape is to the right of the knob. + +## Some notes on the different shapes + +### Smooth + +![smooth image](./smooth.png) + +Smooth is inspired by the asymmetrical distortion curve of a vacuum tube triode. It is by no means a model of a tube at all, but the shape is similar. +Smooth uses gentle curves, and wants to be used more as a distortion or thickener that a full on mangler. + +With the Smooth setting, the offset control doesn’t actually control the offset; it controls the amount of asymmetry in the output. So, like a normal offset it will bring in even harmonics, but the way the even harmonics come in at different levels is unique. + +Where normally there are the least even harmonics when the knob is in the middle, with Smooth there are no even harmonics when it’s all the way down. + +### Clip + +![clip image](./clip.png) + +Classic hard clipping. With the gain high the output will be a square wave. +Clip is not very useful for shaping a VCO output, since most VCOs already put out a square wave. But it is useful for generating a lot of distortion. It is similar to some guitar fuzz-boxes. + +### Emitter Coupled + +![ec image](./emitter-coupled.png) + +Models the saturation of an emitter coupled pair amplifier input, as was commonly found in the classic 3080 “OTA” chip that was used in many analog synthesizers and some phase shifters. Even when driven hard, Emitter Coupled will not distort as much as some of the other shapes, and definitely won’t mangle a sound. + +### Full Wave + +![fw image](./full-wave.png) + +A very simple shape, but one that has some unique characteristics. This is just the absolute value function we learned about in school. Its unique characteristic is that while it generates a lot of distortion harmonics, it does not compress the dynamic range of music played through it. + +It is called Full Wave because this is an idealized version of the shape that will come out of a full wave rectifier circuit. + +While the full wave shape is usually too harsh and "buzzy" to use as an overdrive/distortion, it does make a nice mangler. + +### Half Wave + +![hw image](./half-wave.png) + +Has much in common with the full wave shape, but its radical asymmetry guarantees that a lot of even harmonics will be in the output. + +Unlike most of the shapes, both of the rectifier shapes have the same harmonic content regardless of the gain setting. So here it only functions as a volume control. + +### Fold + +![fold image](./folder.png) + +The "wavefolder" is a legendary and classic synthesizer module. Both Buchla and Serge offer wavefolder modules, and they were (and remain) very popular. The wavefolder we have implemented in Shaper is not an attempt at an emulation of those classics, but is a mathematically simple folding of the input. + +One thing all wavefolders have in common is that the sound changes quite a bit as the gain increases, sounding something like a filter sweep, or even more like the sweep of a synched VCO. So one of the first things to try is modulating the gain input with an envelope or other modulation source. + +One thing our wavefolder has in common with the analog classics is that it has little or no aliasing. Wavefolding generates a huge amount of high harmonics, so a digital implementation that does not deal with the aliasing is going to sound different from Shaper (or an analog module). + +That said, musicians can undoubtedly find uses for the fully aliased version. We encourage you to try at 16X and at 1X. + +### Fold 2 + +![fold 2 image](./folder-II.png) + +Whereas “Fold” is a standard wavefolder, Fold2 is a little bit different. It is asymmetric no matter what offset is fed into it. It also generates more high harmonics than any other shape. + +### Crush + +![crush image](./crush.png) + +Crush simulates bit reduction by using a continuous voltage quantizer. As the gain is turned up it will pass 16 bits, down to 8 bits, and finally one bit. + +## More Info + +We have an informational article that talks more about aliasing. It goes into some specifics about Shaper, but also has some useful general information. + +[Aliasing Story](./aliasing.md). \ No newline at end of file diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/shifter.md b/vst2_bin/plugins/squinkylabs-plug1/docs/shifter.md new file mode 100644 index 00000000..048ea5c6 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/docs/shifter.md @@ -0,0 +1,46 @@ +# Booty Shifter frequency shifter + +**Booty Shifter** is a frequency shifter inspired by the Moog/Bode frequency shifter module. + +![booty shifter image](./booty-shifter.png) + +The name "Booty Shifter" is a nod to the classic analog module, as well as to a black cat named Booty. + +Booty Shifter will take an audio input and shift the frequencies up or down. This is not like a pitch shift where harmonics will remain in tune; it is an absolute frequency shift in Hz, so in general **harmonics will go way out of tune.** It is similar to a ring-modulator, but less extreme and more versatile. + +## Getting good sounds from Booty Shifter + +Feed in music and shift the frequency a good amount. + +Feed in **speech or radio** and shift it. + +Feed the CV from a **sequencer** to sequence the mayhem. + +Shift **drums** up or down a little bit to re-tune them without the usual pitch-shifting artifacts. + +Small shifts in conjunction with delays can make a chorus-like effect to thicken music. + +## Inputs and outputs + +* **IN** is the audio input. +* **CV** is the pitch shift control voltage. -5V will give minimum shift, +5 will give maximum. +* **DN** is the down-shifted output. +* **UP** is the up-shifted output. + +## Controls + +**RANGE** sets the total shift range in Hz. For example, the 50 Hz setting means that the minimum shift is 50 Hz down, and the maximum is 50 Hz up. + +Range value **Exp is different**. Here minimum shift is 2 Hz, maximum is 2 kHz, with an exponential response. As of version 0.6.2 the response is an accurate 1 Volt per Octave. + +Shift **AMT** is added to the control voltage, with a range of -5..5. + +## Oddities and limitations + +If you shift the frequency up too far, it will alias. There is no anti-aliasing, so if the highest input frequency + shift amount > sample_rate / 2, you will get aliasing. Of course the Bode analog original did not alias. + +If you shift the input down a lot, frequencies will go **below zero and wrap around**. Taken far enough this will completely **reverse the spectrum** of the input. This was a prized feature of the Bode original. + +As you shift the input down, you may start to generate a lot of subsonic energy. A **High Pass filter** may clean this up. + +The down shift **frequency fold-over**, while true to the original, does cause problems when trying to pitch drum tracks down a lot. High pass filtering the input before it is down-shifted can control this. diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/smooth.png b/vst2_bin/plugins/squinkylabs-plug1/docs/smooth.png new file mode 100644 index 00000000..77c0b178 Binary files /dev/null and b/vst2_bin/plugins/squinkylabs-plug1/docs/smooth.png differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/thread-booster.png b/vst2_bin/plugins/squinkylabs-plug1/docs/thread-booster.png new file mode 100644 index 00000000..64a5db75 Binary files /dev/null and b/vst2_bin/plugins/squinkylabs-plug1/docs/thread-booster.png differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/docs/vco-optimization.md b/vst2_bin/plugins/squinkylabs-plug1/docs/vco-optimization.md new file mode 100644 index 00000000..3eccca33 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/docs/vco-optimization.md @@ -0,0 +1,72 @@ +# Notes about the creation of Functional VCO-1 + +## About the Original + +Fundamental VCO-1 is a very high quality, excellent sounding VCO. It does exactly what it claims to do with very little digital artifacts. VCO-1 is a very popular module, but it does use a lot of CPU. For this reason it seemed like a good candidate for a CPU diet. VCV users continue to complain about popping and clicking with large patches, so we hope improvements to this popular module will help. + +Fundamental VCO-1 uses 16X oversampling to generate standard waveforms, in both "analog" and "digital" versions. The oversampling keeps the aliasing low, allows hard and soft sync with low aliasing, and suppresses aliasing from audio rate modulation. + +As such, Fundamental VCO-1 is a very good substitute for an analog VCO. The only down-side is that the 16X oversampling increases the CPU usage dramatically. + +In revamping this VCO, we wanted to try as much as possible to preserve the sound exactly like the original. We did not add any new features, or try to make anything "better". And when we put the code on a diet we did not want to lower the sound quality of this workhorse module. + +## Initial Measurements + +It is difficult to accurately measure the CPU usage of a VCV module. Since version 0.6 there have been CPU meters which are useful for getting an overall picture of CPU usage; but the CPU meters do not enable stable, accurate, and repeatable measurements. + +So we more or less run the plugins in an isolated test framework. This lets us get precise measurements. The down side is that the isolated system is different from running in VCV, and the numbers won’t correlate exactly. + +We use an arbitrary scale for our measurements, where "100" means that the plugin under test seems to be using 1% of the available CPU on one core of our quite old Intel Core i5 Windows-7 computer. + +Here are the initial measurements we took before any optimizations were done, along with some Squinky Labs modules for reference: + +* Fundamental VCO-1, all outputs patched, digital: 798 +* Fundamental VCO-1, saw only, digital: 489 +* Fundamental VCO-1, saw only, analog: 270 +* SL Formants: 84.1 +* SL Growler: 50.9 +* SL Chopper: 14.9 +* SL Booty Shifter: 11.2 +* SL Colors: 11.6 + +Fundamental VCO-1 uses a *lot* of CPU. Since it is so heavy in its CPU usage, we thought it would be easy to make it much faster. But it was not as easy as we had hoped. + +## General approach to optimization + +Every theory must be validated by experiment. So we look at the code, formulate a theory, throw together a simplified implementation of the theory, and compare before and after measurements. + +If the CPU usage goes down a lot, the experiment is a success, and we try to make a full implementation that preserves the drop in CPU usage without compromising the quality. + +Then repeat this process over and over until done. + +## What we did to Fundamental VCO-1 + +VCO-1 already had some optimization. Although the waveform generation runs for all waveforms all the time, the decimation filters are only run for outputs that are connected. The decimation filters are the same ones used by the VCV Rack audio engine, so we assume they are linear phase FIR filters, as is customary for high quality sample rate conversion. + +But, since this is an oversampling VCO, the waveform generation is running at 16X sample rate. Any extra work in this "inner loop" is going to be magnified by 16. + +That said, our experiments showed few surprises. + +* Since cosf() is called all the time in the 16X waveform generation loop, it was a no-brainer to replace it with the same sin lookup table we use in most of our modules. Likewise, we made sure that the cosf lookup is only called if the SIN output is connected. +* The powf() call is also slow and it was worthwhile getting rid of the powf call, although the gain was not enormous since powf is only called once per sample. +* We refactored the inner loop to make sure that the waveform generation is only done for waveforms whose output is patched. +* The decimation filters use a lot of CPU, so we replaced the stock ones with simple 6-pole butterworth lowpass filters. +* It would of course save CPU to reduce the oversampling rate, but we did not want to decrease the quality. + +Again, most of the software required was already in the code-base for our other modules. The one thing that was difficult here was devising test software to measure the aliasing. We wanted to be sure that our faster decimation filters were not increasing the level of aliasing. While this new alias test is not perfect, it did give us confidence that we were not increasing the aliasing with our substituted filters. + +## Results + +Before and after: + +* Fundamental VCO-1, all outputs patched, digital: 798 -> 187.8 (X4.2) +* Fundamental VCO-1, saw only, digital: 489 -> 83.7 (X5.8) +* Fundamental VCO-1, saw only, analog: 270 -> 83 (X2.3) + +## Addendum for EV3 VCO + +When we looked at EvenVCO, we found many of the same issues as we found in Fundamental VCO-1. The same extremely slow trig and exponential functions that we replaced with lookup tables. Work was being done for waveforms that weren't patched. + +But some other things were different. Since EvenVCO is a MinBLEP VCO, it does not need anti-alias filters (as such). The existing MinBLEP code is quite efficient. But in the VCO we did achieve significant speedup by getting rid of the one routine that generates all the waves, and instead have dedicated routines for each waveform. This eliminated a lot of conditional branching. + +While EvenVCO is already quite efficient, it was still worthwhile to make it faster. We believe out triple version uses about the same CPU as a single instance of EvenVCO. \ No newline at end of file diff --git a/vst2_bin/plugins/squinkylabs-plug1/gfx/ThreadBoost.xd b/vst2_bin/plugins/squinkylabs-plug1/gfx/ThreadBoost.xd deleted file mode 100644 index 2f9fcbfd..00000000 Binary files a/vst2_bin/plugins/squinkylabs-plug1/gfx/ThreadBoost.xd and /dev/null differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/gfx/booty-panel-design.xd b/vst2_bin/plugins/squinkylabs-plug1/gfx/booty-panel-design.xd deleted file mode 100644 index 80da6266..00000000 Binary files a/vst2_bin/plugins/squinkylabs-plug1/gfx/booty-panel-design.xd and /dev/null differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/gfx/chopper-panel-design.xd b/vst2_bin/plugins/squinkylabs-plug1/gfx/chopper-panel-design.xd deleted file mode 100644 index f27a2e25..00000000 Binary files a/vst2_bin/plugins/squinkylabs-plug1/gfx/chopper-panel-design.xd and /dev/null differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/gfx/formants_panel.xd b/vst2_bin/plugins/squinkylabs-plug1/gfx/formants_panel.xd deleted file mode 100644 index 53c45a4f..00000000 Binary files a/vst2_bin/plugins/squinkylabs-plug1/gfx/formants_panel.xd and /dev/null differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/gfx/noun_938401.svg b/vst2_bin/plugins/squinkylabs-plug1/gfx/noun_938401.svg deleted file mode 100644 index 8e711ed4..00000000 --- a/vst2_bin/plugins/squinkylabs-plug1/gfx/noun_938401.svg +++ /dev/null @@ -1,4 +0,0 @@ - \ No newline at end of file diff --git a/vst2_bin/plugins/squinkylabs-plug1/gfx/vocal-anim-panel.xd b/vst2_bin/plugins/squinkylabs-plug1/gfx/vocal-anim-panel.xd deleted file mode 100644 index ddd12e99..00000000 Binary files a/vst2_bin/plugins/squinkylabs-plug1/gfx/vocal-anim-panel.xd and /dev/null differ diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/Blue30.svg b/vst2_bin/plugins/squinkylabs-plug1/res/Blue30.svg new file mode 100644 index 00000000..ce7b8ad3 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/Blue30.svg @@ -0,0 +1,9 @@ + + Blue302 + + + + + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/BluePush_0.svg b/vst2_bin/plugins/squinkylabs-plug1/res/BluePush_0.svg new file mode 100644 index 00000000..7a292df1 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/BluePush_0.svg @@ -0,0 +1,10 @@ + + BluePush_0 + + + + + + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/BluePush_1.svg b/vst2_bin/plugins/squinkylabs-plug1/res/BluePush_1.svg new file mode 100644 index 00000000..30e3aa9c --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/BluePush_1.svg @@ -0,0 +1,10 @@ + + BluePush_1 + + + + + + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/BlueTrimmer.svg b/vst2_bin/plugins/squinkylabs-plug1/res/BlueTrimmer.svg new file mode 100644 index 00000000..81aefcd2 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/BlueTrimmer.svg @@ -0,0 +1,7 @@ + + TrimBlueOrig2 + + + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/NKKSmall_0.svg b/vst2_bin/plugins/squinkylabs-plug1/res/NKKSmall_0.svg new file mode 100644 index 00000000..946b5d2c --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/NKKSmall_0.svg @@ -0,0 +1,13 @@ + + Artboard 1 + + + + + + + + + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/NKKSmall_1.svg b/vst2_bin/plugins/squinkylabs-plug1/res/NKKSmall_1.svg new file mode 100644 index 00000000..b25bbfe9 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/NKKSmall_1.svg @@ -0,0 +1,13 @@ + + Artboard 1 + + + + + + + + + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/NKKSmall_2.svg b/vst2_bin/plugins/squinkylabs-plug1/res/NKKSmall_2.svg new file mode 100644 index 00000000..3f314d45 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/NKKSmall_2.svg @@ -0,0 +1,13 @@ + + Artboard 1 + + + + + + + + + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/blank_panel.svg b/vst2_bin/plugins/squinkylabs-plug1/res/blank_panel.svg index 4e30c038..9b9869fc 100644 --- a/vst2_bin/plugins/squinkylabs-plug1/res/blank_panel.svg +++ b/vst2_bin/plugins/squinkylabs-plug1/res/blank_panel.svg @@ -1,16 +1,20 @@ - + - + panel-6b-flat - + - - + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/chb_panel.svg b/vst2_bin/plugins/squinkylabs-plug1/res/chb_panel.svg new file mode 100644 index 00000000..13269abf --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/chb_panel.svg @@ -0,0 +1,32 @@ + + cb_panel + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/ev3_panel.svg b/vst2_bin/plugins/squinkylabs-plug1/res/ev3_panel.svg new file mode 100644 index 00000000..5df1b04a --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/ev3_panel.svg @@ -0,0 +1,26 @@ + + ev3-3-ghost + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/formants_panel.svg b/vst2_bin/plugins/squinkylabs-plug1/res/formants_panel.svg index de591576..6c24b440 100644 --- a/vst2_bin/plugins/squinkylabs-plug1/res/formants_panel.svg +++ b/vst2_bin/plugins/squinkylabs-plug1/res/formants_panel.svg @@ -7,7 +7,10 @@ formants_panel - + + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/fun_panel.svg b/vst2_bin/plugins/squinkylabs-plug1/res/fun_panel.svg new file mode 100644 index 00000000..02625237 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/fun_panel.svg @@ -0,0 +1,52 @@ + + + + + + + fun-panel + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/gray.svg b/vst2_bin/plugins/squinkylabs-plug1/res/gray.svg new file mode 100644 index 00000000..4688975e --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/gray.svg @@ -0,0 +1,30 @@ + + gray-narrow-ghost + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/lfn_panel.svg b/vst2_bin/plugins/squinkylabs-plug1/res/lfn_panel.svg new file mode 100644 index 00000000..8ac5fbdd --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/lfn_panel.svg @@ -0,0 +1,42 @@ + + + + + + + lfn_panel + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/saw_wave-1.svg b/vst2_bin/plugins/squinkylabs-plug1/res/saw_wave-1.svg new file mode 100644 index 00000000..266745c4 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/saw_wave-1.svg @@ -0,0 +1,4 @@ + + Artboard 1 + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/saw_wave.svg b/vst2_bin/plugins/squinkylabs-plug1/res/saw_wave.svg new file mode 100644 index 00000000..9fc992ac --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/saw_wave.svg @@ -0,0 +1,5 @@ + + ramp3 + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/saw_wave_on.svg b/vst2_bin/plugins/squinkylabs-plug1/res/saw_wave_on.svg new file mode 100644 index 00000000..127e3091 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/saw_wave_on.svg @@ -0,0 +1,5 @@ + + ramp3 + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/saw_wave_on1.svg b/vst2_bin/plugins/squinkylabs-plug1/res/saw_wave_on1.svg new file mode 100644 index 00000000..027e6318 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/saw_wave_on1.svg @@ -0,0 +1,4 @@ + + Artboard 1 + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/shaper.svg b/vst2_bin/plugins/squinkylabs-plug1/res/shaper.svg new file mode 100644 index 00000000..9d372b59 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/shaper.svg @@ -0,0 +1,35 @@ + + shaper2-ghost-01 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/thread_booster_panel.svg b/vst2_bin/plugins/squinkylabs-plug1/res/thread_booster_panel.svg index c73c52ef..07f92a01 100644 --- a/vst2_bin/plugins/squinkylabs-plug1/res/thread_booster_panel.svg +++ b/vst2_bin/plugins/squinkylabs-plug1/res/thread_booster_panel.svg @@ -7,7 +7,11 @@ thread_booster_panel + + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/trem_panel.svg b/vst2_bin/plugins/squinkylabs-plug1/res/trem_panel.svg index eb70aaa7..abff6e93 100644 --- a/vst2_bin/plugins/squinkylabs-plug1/res/trem_panel.svg +++ b/vst2_bin/plugins/squinkylabs-plug1/res/trem_panel.svg @@ -7,10 +7,9 @@ trem_panel - - - - + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/vocal_animator_panel.svg b/vst2_bin/plugins/squinkylabs-plug1/res/vocal_animator_panel.svg index a567e16f..1b2cafb4 100644 --- a/vst2_bin/plugins/squinkylabs-plug1/res/vocal_animator_panel.svg +++ b/vst2_bin/plugins/squinkylabs-plug1/res/vocal_animator_panel.svg @@ -7,9 +7,12 @@ vocal_animator_panel - - - + + + + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-01.svg b/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-01.svg new file mode 100644 index 00000000..c1c091c5 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-01.svg @@ -0,0 +1,5 @@ + + waveforms-6 + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-02.svg b/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-02.svg new file mode 100644 index 00000000..be844fe0 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-02.svg @@ -0,0 +1,5 @@ + + waveforms-6 + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-03.svg b/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-03.svg new file mode 100644 index 00000000..6f855985 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-03.svg @@ -0,0 +1,7 @@ + + waveforms-6 + + + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-04.svg b/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-04.svg new file mode 100644 index 00000000..f613f355 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-04.svg @@ -0,0 +1,7 @@ + + waveforms-6 + + + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-05.svg b/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-05.svg new file mode 100644 index 00000000..3abdedf7 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-05.svg @@ -0,0 +1,5 @@ + + waveforms-6 + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-06.svg b/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-06.svg new file mode 100644 index 00000000..0eb0f217 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-06.svg @@ -0,0 +1,5 @@ + + waveforms-6 + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-07.svg b/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-07.svg new file mode 100644 index 00000000..81284e26 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-07.svg @@ -0,0 +1,4 @@ + + waveforms-6 + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-08.svg b/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-08.svg new file mode 100644 index 00000000..21798e91 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-08.svg @@ -0,0 +1,4 @@ + + waveforms-6 + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-09.svg b/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-09.svg new file mode 100644 index 00000000..0f9cedf7 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-09.svg @@ -0,0 +1,5 @@ + + waveforms-6 + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-10.svg b/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-10.svg new file mode 100644 index 00000000..e606a0d2 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-10.svg @@ -0,0 +1,5 @@ + + waveforms-6 + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-11.svg b/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-11.svg new file mode 100644 index 00000000..b591d4e5 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-11.svg @@ -0,0 +1,7 @@ + + waveforms-6 + + + + + diff --git a/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-12.svg b/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-12.svg new file mode 100644 index 00000000..ee663399 --- /dev/null +++ b/vst2_bin/plugins/squinkylabs-plug1/res/waveforms-6-12.svg @@ -0,0 +1,7 @@ + + waveforms-6 + + + + +