├── .gitignore ├── res ├── LEDCalculator.ttf ├── cntr_DK.svg ├── cntr_LT.svg ├── WhatNote.svg ├── Vactrolyzer.svg └── AlanVolts.svg ├── src ├── turing-module-widget.hh ├── recorder-module-widget.hh ├── vactrolyzer-module-widget.hh ├── adrift-module-widget.hh ├── turing-pulse-module-widget.hh ├── turing-volts-module-widget.hh ├── turing-digital-module-widget.hh ├── turing-vactrol-module-widget.hh ├── components.hh ├── whatnote-module-widget.hh ├── recorder-module.cc ├── bit-spigot.hh ├── vactrolyzer-module.hh ├── recorder-module.hh ├── turing-volts-module.hh ├── test_filenames.py ├── cmwc.hh ├── whatnote-module.hh ├── bit-spigot.cc ├── vactrolyzer-module.cc ├── turing-vactrol-module.hh ├── turing-digital-module.hh ├── turing-pulse-module.hh ├── turing-volts-module.cc ├── cmwc.cc ├── turing-module.hh ├── antipop.hh ├── turing-volts-module-widget.cc ├── whatnote-module.cc ├── skylights.hh ├── adrift-module.hh ├── vactrolyzer-module-widget.cc ├── modules.py ├── vtl5c3.hh ├── turing-pulse-module-widget.cc ├── recorder-module-widget.cc ├── adrift-module-widget.cc ├── turing-digital-module-widget.cc ├── turing-vactrol-module-widget.cc ├── turing-digital-module.cc ├── turing-vactrol-module.cc ├── turing-module-widget.cc ├── turing-pulse-module.cc ├── skylights.cc ├── tags.py ├── whatnote-module-widget.cc ├── adrift-module.cc └── turing-module.cc ├── Makefile ├── LICENSE.txt ├── plugin.json └── README.org /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | /build 3 | /dist 4 | /plugin.dylib 5 | /plugin.dll 6 | /plugin.so 7 | .DS_Store 8 | -------------------------------------------------------------------------------- /res/LEDCalculator.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skrylar/skylights-vcv/HEAD/res/LEDCalculator.ttf -------------------------------------------------------------------------------- /src/turing-module-widget.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "skylights.hh" 4 | 5 | struct turing_module_widget : ModuleWidget { 6 | turing_module_widget(Module *module); 7 | }; 8 | -------------------------------------------------------------------------------- /src/recorder-module-widget.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "skylights.hh" 4 | 5 | struct recorder_module_widget : ModuleWidget { 6 | recorder_module_widget(Module *module); 7 | }; 8 | -------------------------------------------------------------------------------- /src/vactrolyzer-module-widget.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "skylights.hh" 4 | 5 | struct vactrolyzer_module_widget : ModuleWidget { 6 | vactrolyzer_module_widget(Module *module); 7 | }; 8 | -------------------------------------------------------------------------------- /src/adrift-module-widget.hh: -------------------------------------------------------------------------------- 1 | #include "skylights.hh" 2 | 3 | struct adrift_module_widget : ModuleWidget { 4 | adrift_module_widget(Module *module); 5 | virtual ~adrift_module_widget(); 6 | }; 7 | -------------------------------------------------------------------------------- /src/turing-pulse-module-widget.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "skylights.hh" 4 | 5 | struct turing_pulse_module_widget : ModuleWidget { 6 | turing_pulse_module_widget(Module *module); 7 | }; 8 | -------------------------------------------------------------------------------- /src/turing-volts-module-widget.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "skylights.hh" 4 | 5 | struct turing_volts_module_widget : ModuleWidget { 6 | turing_volts_module_widget(Module *module); 7 | }; 8 | -------------------------------------------------------------------------------- /src/turing-digital-module-widget.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "skylights.hh" 4 | 5 | struct turing_digital_module_widget : ModuleWidget { 6 | turing_digital_module_widget(Module *module); 7 | }; 8 | -------------------------------------------------------------------------------- /src/turing-vactrol-module-widget.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "skylights.hh" 4 | 5 | struct turing_vactrol_module_widget : ModuleWidget { 6 | turing_vactrol_module_widget(Module *module); 7 | }; 8 | -------------------------------------------------------------------------------- /src/components.hh: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | struct DavidLTPort : public SVGPort { 5 | DavidLTPort() { 6 | setSvg( 7 | APP->window->loadSvg(asset::plugin(pluginInstance, "res/cntr_LT.svg"))); 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /src/whatnote-module-widget.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "skylights.hh" 4 | 5 | struct whatnote_module_widget : ModuleWidget { 6 | std::shared_ptr font; 7 | 8 | whatnote_module_widget(Module *module); 9 | 10 | void draw(const DrawArgs &args) override; 11 | }; 12 | -------------------------------------------------------------------------------- /src/recorder-module.cc: -------------------------------------------------------------------------------- 1 | #include "recorder-module.hh" 2 | 3 | void recorder_module::process(const ProcessArgs &args) {} 4 | 5 | recorder_module::recorder_module() : Module() { 6 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); 7 | } 8 | 9 | recorder_module::~recorder_module() {} 10 | -------------------------------------------------------------------------------- /src/bit-spigot.hh: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "cmwc.hh" 5 | 6 | // uses the CPU's random number generator to obtain a bank of random 7 | // bits, which we can then query as needed. since this is all done by 8 | // the CPU, we don't seed it or anything like that. 9 | class bit_spigot { 10 | uint32_t m_data; 11 | unsigned char m_taps; 12 | cmwc m_prng; 13 | 14 | public: 15 | bit_spigot(); 16 | 17 | void reset(); 18 | bool next(); 19 | }; 20 | -------------------------------------------------------------------------------- /src/vactrolyzer-module.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "skylights.hh" 4 | #include "vtl5c3.hh" 5 | 6 | struct vactrolyzer_module : Module { 7 | enum ParamIds { NUM_PARAMS }; 8 | enum InputIds { I_VAC1, I_VAC2, NUM_INPUTS }; 9 | enum OutputIds { O_VAC1, O_VAC2, NUM_OUTPUTS }; 10 | enum LightIds { L_VAC1, L_VAC2, NUM_LIGHTS }; 11 | 12 | vactrolyzer_module(); 13 | virtual ~vactrolyzer_module(); 14 | 15 | vtl5c3 m_vactrol[2]; 16 | 17 | void onSampleRateChange() override; 18 | void process(const ProcessArgs &args) override; 19 | }; 20 | -------------------------------------------------------------------------------- /src/recorder-module.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "skylights.hh" 4 | 5 | #define AUDIO_OUTPUTS 0 6 | #define AUDIO_INPUTS 8 7 | #define TRACKS 4 8 | 9 | struct recorder_module : Module { 10 | enum ParamIds { NUM_PARAMS }; 11 | enum InputIds { ENUMS(AUDIO_INPUT, AUDIO_INPUTS), NUM_INPUTS }; 12 | enum OutputIds { ENUMS(AUDIO_OUTPUT, AUDIO_OUTPUTS), NUM_OUTPUTS }; 13 | enum LightIds { ENUMS(ARM_LIGHT, TRACKS), NUM_LIGHTS }; 14 | 15 | recorder_module(); 16 | virtual ~recorder_module(); 17 | 18 | void process(const ProcessArgs &args) override; 19 | }; 20 | -------------------------------------------------------------------------------- /src/turing-volts-module.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "skylights.hh" 4 | 5 | struct turing_volts_module : Module { 6 | enum ParamIds { P_VOL1, P_VOL2, P_VOL3, P_VOL4, P_VOL5, NUM_PARAMS }; 7 | enum InputIds { I_EXPANDER, NUM_INPUTS }; 8 | enum OutputIds { O_VOLTAGE, NUM_OUTPUTS }; 9 | enum LightIds { 10 | L_LIGHT1, 11 | L_LIGHT2, 12 | L_LIGHT3, 13 | L_LIGHT4, 14 | L_LIGHT5, 15 | NUM_LIGHTS 16 | }; 17 | 18 | turing_volts_module(); 19 | virtual ~turing_volts_module(); 20 | 21 | void process(const ProcessArgs &args) override; 22 | }; 23 | -------------------------------------------------------------------------------- /src/test_filenames.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import glob 3 | import re 4 | 5 | header_regex = re.compile("([a-z0-9]+-)*([a-z0-9]+).hh") 6 | source_regex = re.compile("([a-z0-9]+-)*([a-z0-9]+).cc") 7 | 8 | class TestFilenameConvention(unittest.TestCase): 9 | def test_headers(self): 10 | headers = glob.glob("*.hh") 11 | 12 | for f in headers: 13 | self.assertTrue(header_regex.match(f), f) 14 | 15 | def test_sources(self): 16 | sources = glob.glob("*.cc") 17 | 18 | for f in sources: 19 | self.assertTrue(source_regex.match(f), f) 20 | -------------------------------------------------------------------------------- /src/cmwc.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | // complimentary multiply with carry, pseudo random number generator 6 | // adapted from the public source code from wikipedia 7 | // https://en.wikipedia.org/wiki/Multiply-with-carry_pseudorandom_number_generator 8 | class cmwc { 9 | static const size_t CMWC_CYCLE = 4096; // as Marsaglia recommends 10 | static const size_t CMWC_C_MAX = 809430660; // as Marsaglia recommends 11 | 12 | uint32_t Q[CMWC_CYCLE]; 13 | uint32_t c; // must be limited with CMWC_C_MAX 14 | unsigned i; 15 | 16 | public: 17 | void seed(unsigned int value); 18 | uint32_t next(); 19 | }; 20 | -------------------------------------------------------------------------------- /src/whatnote-module.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "skylights.hh" 4 | 5 | #define AUDIO_OUTPUTS 0 6 | #define AUDIO_INPUTS 1 7 | 8 | struct whatnote_module : Module { 9 | enum ParamIds { NUM_PARAMS }; 10 | enum InputIds { ENUMS(AUDIO_INPUT, AUDIO_INPUTS), NUM_INPUTS }; 11 | enum OutputIds { NUM_OUTPUTS }; 12 | enum LightIds { NUM_LIGHTS }; 13 | 14 | whatnote_module(); 15 | virtual ~whatnote_module(); 16 | 17 | int octave; // what octave did we just read 18 | int semitone; // what semitone did we just read 19 | int cents; // how many cents are left? 20 | double voltage; // what was the last sampled voltage 21 | 22 | void process(const ProcessArgs &args) override; 23 | }; 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # If RACK_DIR is not defined when calling the Makefile, default to two directories above 2 | RACK_DIR ?= ../.. 3 | 4 | # FLAGS will be passed to both the C and C++ compiler 5 | FLAGS += 6 | CFLAGS += 7 | CXXFLAGS += 8 | 9 | # Careful about linking to shared libraries, since you can't assume much about the user's environment and library search path. 10 | # Static libraries are fine. 11 | LDFLAGS += 12 | 13 | # Add .cpp and .c files to the build 14 | SOURCES += $(wildcard src/*.cc) 15 | 16 | # Add files to the ZIP package when running `make dist` 17 | # The compiled plugin is automatically added. 18 | DISTRIBUTABLES += $(wildcard LICENSE*) res 19 | 20 | # Include the VCV Rack plugin Makefile framework 21 | include $(RACK_DIR)/plugin.mk 22 | -------------------------------------------------------------------------------- /src/bit-spigot.cc: -------------------------------------------------------------------------------- 1 | #include "bit-spigot.hh" 2 | #include 3 | //#include 4 | 5 | bit_spigot::bit_spigot() { 6 | // TODO grab from some OS-specific decent entropy source 7 | m_prng.seed(time(NULL)); 8 | reset(); 9 | } 10 | 11 | void bit_spigot::reset() { 12 | #if 0 13 | // this uses a hardware random number generator, but is only 14 | // available if you are using an intel cpu 15 | 16 | unsigned int x; 17 | _rdrand32_step(&x); 18 | 19 | m_data = x; 20 | m_taps = sizeof(unsigned int) * 8; 21 | #endif 22 | 23 | m_data = m_prng.next(); 24 | m_taps = 31; 25 | } 26 | 27 | bool bit_spigot::next() { 28 | if (m_taps == 0) 29 | reset(); 30 | bool result = m_data & 0x1; 31 | m_data >>= 1; 32 | m_taps--; 33 | return result; 34 | } 35 | -------------------------------------------------------------------------------- /src/vactrolyzer-module.cc: -------------------------------------------------------------------------------- 1 | #include "vactrolyzer-module.hh" 2 | 3 | void vactrolyzer_module::process(const ProcessArgs &args) { 4 | for (size_t i = 0; i < 2; i++) { 5 | outputs[O_VAC1 + i].value = 6 | m_vactrol[i].step(std::max((float)0.0, inputs[I_VAC1 + i].getVoltage())); 7 | lights[L_VAC1 + i].value = fabs(outputs[O_VAC1 + i].getVoltage()); 8 | } 9 | } 10 | 11 | vactrolyzer_module::vactrolyzer_module() : Module() { 12 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); 13 | onSampleRateChange(); 14 | } 15 | 16 | void vactrolyzer_module::onSampleRateChange() { 17 | double s = APP->engine->getSampleRate(); 18 | for (size_t i = 0; i < 2; i++) { 19 | m_vactrol[i].set_samplerate(s); 20 | } 21 | } 22 | 23 | vactrolyzer_module::~vactrolyzer_module() {} 24 | -------------------------------------------------------------------------------- /src/turing-vactrol-module.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "skylights.hh" 4 | #include "vtl5c3.hh" 5 | 6 | struct turing_vactrol_module : Module { 7 | enum ParamIds { P_VOL1, P_VOL2, P_VOL3, P_VOL4, NUM_PARAMS }; 8 | enum InputIds { 9 | I_EXPANDER, 10 | I_INPUT1, 11 | I_INPUT2, 12 | I_INPUT3, 13 | I_INPUT4, 14 | NUM_INPUTS 15 | }; 16 | enum OutputIds { O_LEFT, O_RIGHT, NUM_OUTPUTS }; 17 | enum LightIds { 18 | L_GATE1, 19 | L_GATE2, 20 | L_GATE3, 21 | L_GATE4, 22 | L_GATE5, 23 | L_GATE6, 24 | L_GATE7, 25 | L_GATE8, 26 | NUM_LIGHTS 27 | }; 28 | 29 | vtl5c3 m_vactrol[8]; 30 | 31 | void onSampleRateChange() override; 32 | 33 | turing_vactrol_module(); 34 | virtual ~turing_vactrol_module(); 35 | 36 | void process(const ProcessArgs &args) override; 37 | }; 38 | -------------------------------------------------------------------------------- /src/turing-digital-module.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "antipop.hh" 4 | #include "skylights.hh" 5 | 6 | struct turing_digital_module : Module { 7 | enum ParamIds { P_VOL1, P_VOL2, P_VOL3, P_VOL4, NUM_PARAMS }; 8 | enum InputIds { 9 | I_EXPANDER, 10 | I_INPUT1, 11 | I_INPUT2, 12 | I_INPUT3, 13 | I_INPUT4, 14 | NUM_INPUTS 15 | }; 16 | enum OutputIds { O_LEFT, O_RIGHT, NUM_OUTPUTS }; 17 | enum LightIds { 18 | L_GATE1, 19 | L_GATE2, 20 | L_GATE3, 21 | L_GATE4, 22 | L_GATE5, 23 | L_GATE6, 24 | L_GATE7, 25 | L_GATE8, 26 | NUM_LIGHTS 27 | }; 28 | 29 | antipop_t m_antipop[8]; 30 | 31 | void onSampleRateChange() override; 32 | 33 | turing_digital_module(); 34 | virtual ~turing_digital_module(); 35 | 36 | void process(const ProcessArgs &args) override; 37 | }; 38 | -------------------------------------------------------------------------------- /src/turing-pulse-module.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "skylights.hh" 4 | 5 | struct turing_pulse_module : Module { 6 | enum ParamIds { NUM_PARAMS }; 7 | enum InputIds { I_EXPANDER, I_PULSE, NUM_INPUTS }; 8 | enum OutputIds { 9 | O_GATE1, 10 | O_GATE2, 11 | O_GATE3, 12 | O_GATE4, 13 | O_GATE5, 14 | O_GATE6, 15 | O_GATE7, 16 | O_GATE1P2, 17 | O_GATE2P4, 18 | O_GATE4P7, 19 | O_GATE1P2P4P7, 20 | NUM_OUTPUTS 21 | }; 22 | enum LightIds { 23 | L_GATE1, 24 | L_GATE2, 25 | L_GATE3, 26 | L_GATE4, 27 | L_GATE5, 28 | L_GATE6, 29 | L_GATE7, 30 | L_GATE1P2, 31 | L_GATE2P4, 32 | L_GATE4P7, 33 | L_GATE1P2P4P7, 34 | NUM_LIGHTS 35 | }; 36 | 37 | turing_pulse_module(); 38 | virtual ~turing_pulse_module(); 39 | 40 | void process(const ProcessArgs &args) override; 41 | }; 42 | -------------------------------------------------------------------------------- /src/turing-volts-module.cc: -------------------------------------------------------------------------------- 1 | #include "turing-volts-module.hh" 2 | 3 | void turing_volts_module::process(const ProcessArgs &args) { 4 | uint16_t seq = 5 | (uint16_t)ceil((inputs[I_EXPANDER].getVoltage() / 10.0) * 65535.0); 6 | 7 | double signal = 0; 8 | 9 | for (size_t i = 0; i < 5; i++) { 10 | double here = 11 | (((seq & (1 << i)) > 0.0) ? 1.0 : 0.0) * params[P_VOL1 + i].getValue(); 12 | lights[L_LIGHT1 + i].value = fabs(here); 13 | signal += here; 14 | } 15 | 16 | outputs[O_VOLTAGE].setVoltage(signal * 2.0); 17 | } 18 | 19 | turing_volts_module::turing_volts_module() : Module() { 20 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); 21 | for (size_t i = 0; i < 5; i++) { 22 | configParam(turing_volts_module::P_VOL1 + i, -1.0, 1.0, 0.0, ""); 23 | } 24 | } 25 | 26 | turing_volts_module::~turing_volts_module() {} 27 | -------------------------------------------------------------------------------- /src/cmwc.cc: -------------------------------------------------------------------------------- 1 | #include "cmwc.hh" 2 | #include 3 | 4 | static uint32_t rand32(void) { 5 | uint32_t result = rand(); 6 | return result << 16 | rand(); 7 | } 8 | 9 | void cmwc::seed(unsigned int seed) { 10 | srand(seed); 11 | for (size_t i = 0; i < CMWC_CYCLE; i++) 12 | this->Q[i] = rand32(); 13 | do 14 | this->c = rand32(); 15 | while (this->c >= CMWC_C_MAX); 16 | this->i = CMWC_CYCLE - 1; 17 | } 18 | 19 | uint32_t cmwc::next() { 20 | uint64_t const a = 18782; // as Marsaglia recommends 21 | uint32_t const m = 0xfffffffe; // as Marsaglia recommends 22 | uint64_t t; 23 | uint32_t x; 24 | 25 | this->i = (this->i + 1) & (CMWC_CYCLE - 1); 26 | t = a * this->Q[this->i] + this->c; 27 | /* Let c = t / 0xfffffff, x = t mod 0xffffffff */ 28 | this->c = t >> 32; 29 | x = t + this->c; 30 | if (x < this->c) { 31 | x++; 32 | this->c++; 33 | } 34 | return this->Q[this->i] = m - x; 35 | } 36 | -------------------------------------------------------------------------------- /src/turing-module.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "bit-spigot.hh" 4 | #include "dsp/digital.hpp" 5 | #include "skylights.hh" 6 | 7 | struct turing_module : Module { 8 | enum ParamIds { P_WRITE, P_LENGTH, P_MODE, P_POLE, P_SCALE, NUM_PARAMS }; 9 | enum InputIds { I_CLOCK, I_MODE, NUM_INPUTS }; 10 | enum OutputIds { O_VOLTAGE, O_EXPANSION, O_GATE, O_PULSE, NUM_OUTPUTS }; 11 | enum LightIds { 12 | L_LIGHT1, 13 | L_LIGHT2, 14 | L_LIGHT3, 15 | L_LIGHT4, 16 | L_LIGHT5, 17 | L_LIGHT6, 18 | L_LIGHT7, 19 | L_LIGHT8, 20 | NUM_LIGHTS 21 | }; 22 | 23 | uint16_t m_sequence; 24 | bit_spigot m_spigot; 25 | rack::dsp::SchmittTrigger m_clock_trigger; 26 | 27 | turing_module(); 28 | virtual ~turing_module(); 29 | 30 | json_t *dataToJson() override; 31 | void dataFromJson(json_t *root) override; 32 | 33 | void process(const ProcessArgs &args) override; 34 | void onReset() override; 35 | void onRandomize() override; 36 | }; 37 | -------------------------------------------------------------------------------- /src/antipop.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // allows you to turn off signal chains while smoothing over any DC 4 | // offsets that would result in popping. does this by treating the 5 | // gate status as a moving average and processing signals until the 6 | // average is fully closed (zero.) 7 | struct antipop_t { 8 | double integrator; 9 | double gate; 10 | 11 | antipop_t() : integrator(0.0), gate(0.0) {} 12 | 13 | antipop_t(double samplerate) : gate(0.0) { change_sample_rate(samplerate); } 14 | 15 | void change_sample_rate(double samplerate) { 16 | // based on Stephen Nahmias 17 | integrator = 2.0 / ((samplerate * 0.05) + 1.0); 18 | } 19 | 20 | inline bool is_open() { 21 | // NB I suspect "zero" is one of the few numbers that floats 22 | // actually CAN store reliably. Feel free to be mad when I'm 23 | // wrong and this bites someone in the ass. 24 | return gate > 0.0; 25 | } 26 | 27 | inline double step(double passthrough, double signal) { 28 | // update gate EWMA 29 | gate = (integrator * passthrough) + ((1.0 - integrator) * gate); 30 | // filter signal 31 | return signal * gate; 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /src/turing-volts-module-widget.cc: -------------------------------------------------------------------------------- 1 | #include "turing-volts-module-widget.hh" 2 | #include "turing-volts-module.hh" 3 | 4 | turing_volts_module_widget::turing_volts_module_widget(Module *module) 5 | : ModuleWidget() { 6 | setModule(module); 7 | setPanel( 8 | APP->window->loadSvg(asset::plugin(pluginInstance, "res/AlanVolts.svg"))); 9 | 10 | addChild(createWidget(Vec(RACK_GRID_WIDTH, 0))); 11 | addChild(createWidget( 12 | Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); 13 | 14 | addInput(createInput(Vec(25, 45), module, 15 | turing_volts_module::I_EXPANDER)); 16 | 17 | addOutput(createOutput(Vec(25, 300), module, 18 | turing_volts_module::O_VOLTAGE)); 19 | 20 | for (size_t i = 0; i < 5; i++) { 21 | addParam(createParam(Vec(15, 80 + (40 * i)), module, 22 | turing_volts_module::P_VOL1 + i)); 23 | 24 | addChild(createLight>( 25 | Vec(50, 90 + (40 * i)), module, turing_volts_module::L_LIGHT1 + i)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/whatnote-module.cc: -------------------------------------------------------------------------------- 1 | #include "whatnote-module.hh" 2 | 3 | #include 4 | 5 | const double SEMITONE = 1.0 / 12.0; 6 | 7 | void whatnote_module::process(const ProcessArgs &args) { 8 | voltage = inputs[0].getVoltage(); 9 | 10 | // its not valid, so don't analyze it 11 | if (voltage < -10.0 || voltage > 10.0) { 12 | octave = -11.0; 13 | return; 14 | } 15 | 16 | double y; 17 | double x = modf(voltage, &y); // semitones/cents are fractional part 18 | octave = (int)y + 4; // octage is integer part 19 | 20 | // and find semitones in there 21 | if (x < 0.0) { 22 | octave -= 1.0; 23 | x = 1.0 + x; 24 | } 25 | 26 | double z; 27 | double w = modf(x / SEMITONE, &z); 28 | 29 | semitone = z; 30 | cents = (int)round(w * 100.0); 31 | if (cents == 100) { 32 | semitone = (semitone + 1) % 12; 33 | cents = 0; 34 | } 35 | 36 | assert(semitone >= 0); 37 | assert(semitone < 12); 38 | } 39 | 40 | whatnote_module::whatnote_module() 41 | : Module(), octave(0), semitone(0), cents(0), voltage(0) { 42 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); 43 | } 44 | 45 | whatnote_module::~whatnote_module() {} 46 | -------------------------------------------------------------------------------- /src/skylights.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // we don't control these, so don't complain to me about them 4 | #ifdef __clang__ 5 | #pragma clang diagnostic push 6 | #pragma clang diagnostic ignored "-Weverything" 7 | #elif __GNUC__ 8 | #pragma GCC diagnostic push 9 | #pragma GCC diagnostic ignored "-Wall" 10 | #pragma GCC diagnostic ignored "-Wextra" 11 | #endif 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include "rack.hpp" 18 | 19 | // re-entering our zone of concern 20 | #ifdef __clang__ 21 | #pragma clang diagnostic pop 22 | #elif __GNUC__ 23 | #pragma GCC diagnostic pop 24 | #endif 25 | 26 | using namespace rack; 27 | 28 | // Forward-declare the Plugin, defined in skcorder.cc 29 | extern Plugin *pluginInstance; 30 | 31 | #include "components.hh" 32 | 33 | // Forward-declare each Model, defined in each module source file 34 | extern Model *recorder_model; 35 | extern Model *whatnote_model; 36 | 37 | /*[[[cog 38 | from modules import simple_plugins 39 | for p in simple_plugins: 40 | cog.outl('extern Model* {}_model;'.format(p['name'])) 41 | ]]]*/ 42 | extern Model* adrift_model; 43 | extern Model* turing_model; 44 | extern Model* turing_pulse_model; 45 | extern Model* turing_volts_model; 46 | extern Model* turing_digital_model; 47 | extern Model* turing_vactrol_model; 48 | extern Model* vactrolyzer_model; 49 | /*[[[end]]]*/ 50 | -------------------------------------------------------------------------------- /src/adrift-module.hh: -------------------------------------------------------------------------------- 1 | #include "bit-spigot.hh" 2 | #include "skylights.hh" 3 | #include 4 | 5 | struct adrift_module : public Module { 6 | static const size_t channels = 6; 7 | 8 | enum ParamIds { 9 | P_ATTENUATOR, 10 | P_BIP0, 11 | P_BIP1, 12 | P_BIP2, 13 | P_BIP3, 14 | P_BIP4, 15 | P_BIP5, 16 | NUM_PARAMS 17 | }; 18 | 19 | enum InputIds { 20 | I_TRIG_ALL, 21 | I_TRIG0, 22 | I_TRIG1, 23 | I_TRIG2, 24 | I_TRIG3, 25 | I_TRIG4, 26 | I_TRIG5, 27 | I_CV0, 28 | I_CV1, 29 | I_CV2, 30 | I_CV3, 31 | I_CV4, 32 | I_CV5, 33 | NUM_INPUTS 34 | }; 35 | 36 | enum OutputIds { 37 | O_OUT0, 38 | O_OUT1, 39 | O_OUT2, 40 | O_OUT3, 41 | O_OUT4, 42 | O_OUT5, 43 | NUM_OUTPUTS 44 | }; 45 | 46 | enum LightIds { NUM_LIGHTS }; 47 | 48 | rack::dsp::SchmittTrigger m_reset_all; 49 | rack::dsp::SchmittTrigger m_reset[channels]; 50 | double noise[channels]; 51 | 52 | bit_spigot m_noise_source; 53 | 54 | adrift_module(); 55 | virtual ~adrift_module(); 56 | 57 | void noisify_all(); 58 | void noisify(int channel); 59 | 60 | void process(const ProcessArgs &args) override; 61 | void onSampleRateChange() override; 62 | void onReset() override; 63 | void onRandomize() override; 64 | 65 | json_t *dataToJson() override; 66 | void dataFromJson(json_t *root) override; 67 | }; 68 | -------------------------------------------------------------------------------- /src/vactrolyzer-module-widget.cc: -------------------------------------------------------------------------------- 1 | #include "vactrolyzer-module-widget.hh" 2 | #include "vactrolyzer-module.hh" 3 | 4 | vactrolyzer_module_widget::vactrolyzer_module_widget(Module *module) 5 | : ModuleWidget() { 6 | setModule(module); 7 | setPanel(APP->window->loadSvg( 8 | asset::plugin(pluginInstance, "res/Vactrolyzer.svg"))); 9 | 10 | double center_x = box.size.x / 4; 11 | 12 | addChild(createWidget(Vec(center_x, 0))); 13 | addChild(createWidget( 14 | Vec(center_x, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); 15 | 16 | addInput(createInput(Vec(center_x - 5, 70), module, 17 | vactrolyzer_module::I_VAC1)); 18 | addChild(createLight>(Vec(center_x + 3, 100), module, 19 | vactrolyzer_module::L_VAC1)); 20 | addOutput(createOutput(Vec(center_x - 5, 130), module, 21 | vactrolyzer_module::O_VAC1)); 22 | 23 | addInput(createInput(Vec(center_x - 5, 70 + 113), module, 24 | vactrolyzer_module::I_VAC2)); 25 | addChild(createLight>( 26 | Vec(center_x + 3, 100 + 113), module, vactrolyzer_module::L_VAC2)); 27 | addOutput(createOutput(Vec(center_x - 5, 130 + 113), module, 28 | vactrolyzer_module::O_VAC2)); 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | - The VCV plugin template used was provided under CC-0. 2 | 3 | --- 4 | 5 | Copyright 2019, Joshua A. Cearley 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions 9 | are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright 12 | notice, this list of conditions and the following disclaimer. 13 | 14 | 2. Redistributions in binary form must reproduce the above copyright 15 | notice, this list of conditions and the following disclaimer in the 16 | documentation and/or other materials provided with the distribution. 17 | 18 | 3. Neither the name of the copyright holder nor the names of its 19 | contributors may be used to endorse or promote products derived from 20 | this software without specific prior written permission. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 25 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 26 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 28 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | 34 | -------------------------------------------------------------------------------- /src/modules.py: -------------------------------------------------------------------------------- 1 | from tags import ModuleTag 2 | 3 | simple_plugins = [ 4 | { 5 | "name": "adrift", 6 | "plugin": "Skylights", 7 | "slug": "SkAdrift", 8 | "friendly_name": "Adrift (Analogue Drift)", 9 | "tags": [ModuleTag.SAMPLE_AND_HOLD_TAG], 10 | }, 11 | { 12 | "name": "turing", 13 | "plugin": "Skylights", 14 | "slug": "SkTuringV2", 15 | "friendly_name": "Alan (Turing Machine)", 16 | "tags": [ModuleTag.SEQUENCER_TAG], 17 | }, 18 | { 19 | "name": "turing_pulse", 20 | "plugin": "Skylights", 21 | "slug": "SkTuringPulse", 22 | "friendly_name": "Alan (Pulse Expander)", 23 | "tags": [ModuleTag.UTILITY_TAG], 24 | }, 25 | { 26 | "name": "turing_volts", 27 | "plugin": "Skylights", 28 | "slug": "SkTuringVolts", 29 | "friendly_name": "Alan (Volts Expander)", 30 | "tags": [ModuleTag.UTILITY_TAG], 31 | }, 32 | { 33 | "name": "turing_digital", 34 | "plugin": "Skylights", 35 | "slug": "SkTuringVactrol", 36 | "friendly_name": "Alan (Digital Mix Expander)", 37 | "tags": [ModuleTag.UTILITY_TAG], 38 | }, 39 | { 40 | "name": "turing_vactrol", 41 | "plugin": "Skylights", 42 | "slug": "SkTuringVactrolAnalogue", 43 | "friendly_name": "Alan (Vactrol Mix Expander)", 44 | "tags": [ModuleTag.UTILITY_TAG], 45 | }, 46 | { 47 | "name": "vactrolyzer", 48 | "plugin": "Skylights", 49 | "slug": "SkVactrolyzer", 50 | "friendly_name": "Vactrolyzer", 51 | "tags": [ModuleTag.UTILITY_TAG], 52 | }, 53 | ] 54 | -------------------------------------------------------------------------------- /src/vtl5c3.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 by Joshua Cearley 2 | // Creative Commons: CC-BY-SA 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | struct vtl5c3 { 10 | double m_up_eta; 11 | double m_down_eta; 12 | double m_value; 13 | 14 | vtl5c3() : m_up_eta(0.0), m_down_eta(0.0), m_value(0.0) {} 15 | 16 | // if you don't set the sample rate, you are going to have a bad time 17 | void set_samplerate(double rate) { 18 | assert(rate >= 8000.0); 19 | 20 | double inverse = 1.0 / rate; 21 | double a = 209.616712; 22 | double b = 0.000880319056; 23 | double c = 48113.5069; 24 | 25 | m_down_eta = (pow(inverse, 2) * c) + (inverse * a) + b; 26 | 27 | a = 2746.38887; 28 | b = 0.000319227063; 29 | c = -3665711.27; 30 | 31 | m_up_eta = (pow(inverse, 2) * c) + (inverse * a) + b; 32 | } 33 | 34 | // emulates the response curve of the VTL5C3; was derived by curve 35 | // fitting against the spec sheet provided by the manufacturer 36 | inline double curve(double x) { 37 | static const double a = 19977.0579; 38 | static const double b = 4.72586603; 39 | static const double c = 22.9420751; 40 | double resistance = (a * (std::pow(2.71828, (-b * x)))) + c; 41 | return (1.0 - (resistance / 20000.0)); 42 | } 43 | 44 | // input is within [0, 1] instead of miliamps 45 | double step(double input) { 46 | // figure out which ramp we're using 47 | if (input > m_value) { 48 | double np = 1.0 - m_up_eta; 49 | m_value = (m_up_eta * input) + (np * m_value); 50 | } else { 51 | double np = 1.0 - m_down_eta; 52 | m_value = (m_down_eta * input) + (np * m_value); 53 | } 54 | return curve(m_value); 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /src/turing-pulse-module-widget.cc: -------------------------------------------------------------------------------- 1 | #include "turing-pulse-module-widget.hh" 2 | #include "turing-pulse-module.hh" 3 | 4 | turing_pulse_module_widget::turing_pulse_module_widget(Module *module) 5 | : ModuleWidget() { 6 | setModule(module); 7 | setPanel(APP->window->loadSvg( 8 | asset::plugin(pluginInstance, "res/AlanPulses.svg"))); 9 | 10 | addChild(createWidget(Vec(RACK_GRID_WIDTH, 0))); 11 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); 12 | addChild(createWidget( 13 | Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); 14 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 15 | RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); 16 | 17 | addInput(createInput(Vec(25, 45), module, 18 | turing_pulse_module::I_EXPANDER)); 19 | 20 | addInput(createInput(Vec(65, 45), module, 21 | turing_pulse_module::I_PULSE)); 22 | 23 | for (size_t i = 0; i < 7; i++) { 24 | addOutput(createOutput(Vec(15, 80 + (30 * i)), module, 25 | turing_pulse_module::O_GATE1 + i)); 26 | 27 | addChild(createLight>( 28 | Vec(43, 88 + (30 * i)), module, turing_pulse_module::L_GATE1 + i)); 29 | } 30 | 31 | for (size_t i = 0; i < 4; i++) { 32 | addOutput(createOutput(Vec(95, 80 + (60 * i)), module, 33 | turing_pulse_module::O_GATE1P2 + i)); 34 | 35 | addChild(createLight>( 36 | Vec(123, 88 + (60 * i)), module, turing_pulse_module::L_GATE1P2 + i)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/recorder-module-widget.cc: -------------------------------------------------------------------------------- 1 | #include "recorder-module-widget.hh" 2 | #include "recorder-module.hh" 3 | 4 | recorder_module_widget::recorder_module_widget(Module *module) 5 | : ModuleWidget() { 6 | setModule(module); 7 | setPanel( 8 | APP->window->loadSvg(asset::plugin(pluginInstance, "res/Recorder.svg"))); 9 | 10 | addChild(createWidget(Vec(RACK_GRID_WIDTH, 0))); 11 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); 12 | addChild(createWidget( 13 | Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); 14 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 15 | RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); 16 | 17 | addInput(createInput(mm2px(Vec(3.7069211, 10.530807)), module, 18 | recorder_module::AUDIO_INPUT + 0)); 19 | addInput(createInput(mm2px(Vec(3.7069211, 23.530807)), module, 20 | recorder_module::AUDIO_INPUT + 1)); 21 | addInput(createInput(mm2px(Vec(3.7069211, 36.530807)), module, 22 | recorder_module::AUDIO_INPUT + 2)); 23 | addInput(createInput(mm2px(Vec(3.7069211, 49.530807)), module, 24 | recorder_module::AUDIO_INPUT + 3)); 25 | } 26 | 27 | // Specify the Module and ModuleWidget subclass, human-readable 28 | // author name for categorization per plugin, module slug (should never 29 | // change), human-readable module name, and any number of tags 30 | // (found in `include/tags.hpp`) separated by commas. 31 | // Model *recorder_model = createModel("Skylights", "SkRecorder", "SK Recorder", 33 | // RECORDING_TAG); 34 | -------------------------------------------------------------------------------- /src/adrift-module-widget.cc: -------------------------------------------------------------------------------- 1 | #include "adrift-module-widget.hh" 2 | #include "adrift-module.hh" 3 | 4 | adrift_module_widget::adrift_module_widget(Module *module) : ModuleWidget() { 5 | setModule(module); 6 | setPanel( 7 | APP->window->loadSvg(asset::plugin(pluginInstance, "res/Adrift.svg"))); 8 | 9 | addChild(createWidget(Vec(RACK_GRID_WIDTH, 0))); 10 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); 11 | addChild(createWidget( 12 | Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); 13 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 14 | RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); 15 | 16 | // all 17 | addInput(createInput(mm2px(Vec(6, 22)), module, 18 | adrift_module::I_TRIG_ALL)); 19 | 20 | // turbulence 21 | addParam(createParam(mm2px(Vec(23, 22)), module, 22 | adrift_module::P_ATTENUATOR)); 23 | 24 | for (size_t i = 0; i < adrift_module::channels; i++) { 25 | const int shift = 32; 26 | 27 | // input 28 | addInput(createInput(Vec(7, 155 + (shift * i)), module, 29 | adrift_module::I_CV0 + i)); 30 | 31 | // sail 32 | addInput(createInput(Vec(41, 155 + (shift * i)), module, 33 | adrift_module::I_TRIG0 + i)); 34 | 35 | // bipolar toggle 36 | addParam(createParam(Vec(85, 157 + (shift * i)), module, 37 | adrift_module::P_BIP0 + i)); 38 | 39 | addOutput(createOutput(Vec(117, 155 + (shift * i)), module, 40 | adrift_module::O_OUT0 + i)); 41 | } 42 | } 43 | 44 | adrift_module_widget::~adrift_module_widget() {} 45 | -------------------------------------------------------------------------------- /src/turing-digital-module-widget.cc: -------------------------------------------------------------------------------- 1 | #include "turing-digital-module-widget.hh" 2 | #include "turing-digital-module.hh" 3 | 4 | turing_digital_module_widget::turing_digital_module_widget(Module *module) 5 | : ModuleWidget() { 6 | setModule(module); 7 | setPanel(APP->window->loadSvg( 8 | asset::plugin(pluginInstance, "res/AlanDigital.svg"))); 9 | 10 | addChild(createWidget(Vec(RACK_GRID_WIDTH, 0))); 11 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); 12 | addChild(createWidget( 13 | Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); 14 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 15 | RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); 16 | 17 | addInput(createInput(Vec(10, 40), module, 18 | turing_digital_module::I_EXPANDER)); 19 | 20 | for (size_t i = 0; i < 4; i++) { 21 | addInput(createInput(Vec(10, 100 + (30 * i)), module, 22 | turing_digital_module::I_INPUT1 + i)); 23 | } 24 | 25 | size_t y = 0; 26 | for (size_t i = 0; i < 4; i++) { 27 | addParam(createParam(Vec(60, 80 + (50 * i)), module, 28 | turing_digital_module::P_VOL1 + i)); 29 | 30 | addChild(createLight>( 31 | Vec(63, 65 + (50 * i)), module, turing_digital_module::L_GATE1 + y++)); 32 | addChild(createLight>( 33 | Vec(78, 65 + (50 * i)), module, turing_digital_module::L_GATE1 + y++)); 34 | } 35 | 36 | addOutput(createOutput(Vec(10, 300), module, 37 | turing_digital_module::O_LEFT)); 38 | addOutput(createOutput(Vec(40, 300), module, 39 | turing_digital_module::O_RIGHT)); 40 | } 41 | -------------------------------------------------------------------------------- /src/turing-vactrol-module-widget.cc: -------------------------------------------------------------------------------- 1 | #include "turing-vactrol-module-widget.hh" 2 | #include "turing-vactrol-module.hh" 3 | 4 | turing_vactrol_module_widget::turing_vactrol_module_widget(Module *module) 5 | : ModuleWidget() { 6 | setModule(module); 7 | setPanel(APP->window->loadSvg( 8 | asset::plugin(pluginInstance, "res/AlanVactrol.svg"))); 9 | 10 | addChild(createWidget(Vec(RACK_GRID_WIDTH, 0))); 11 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); 12 | addChild(createWidget( 13 | Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); 14 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 15 | RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); 16 | 17 | addInput(createInput(Vec(10, 40), module, 18 | turing_vactrol_module::I_EXPANDER)); 19 | 20 | for (size_t i = 0; i < 4; i++) { 21 | addInput(createInput(Vec(10, 100 + (30 * i)), module, 22 | turing_vactrol_module::I_INPUT1 + i)); 23 | } 24 | 25 | size_t y = 0; 26 | for (size_t i = 0; i < 4; i++) { 27 | addParam(createParam(Vec(60, 80 + (50 * i)), module, 28 | turing_vactrol_module::P_VOL1 + i)); 29 | 30 | addChild(createLight>( 31 | Vec(63, 65 + (50 * i)), module, turing_vactrol_module::L_GATE1 + y++)); 32 | addChild(createLight>( 33 | Vec(78, 65 + (50 * i)), module, turing_vactrol_module::L_GATE1 + y++)); 34 | } 35 | 36 | addOutput(createOutput(Vec(10, 300), module, 37 | turing_vactrol_module::O_LEFT)); 38 | addOutput(createOutput(Vec(40, 300), module, 39 | turing_vactrol_module::O_RIGHT)); 40 | } 41 | -------------------------------------------------------------------------------- /src/turing-digital-module.cc: -------------------------------------------------------------------------------- 1 | #include "turing-digital-module.hh" 2 | 3 | void turing_digital_module::process(const ProcessArgs &args) { 4 | uint16_t seq = 5 | (uint16_t)ceil((inputs[I_EXPANDER].getVoltage() / 10.0) * 65535.0); 6 | 7 | lights[L_GATE1].value = (seq & 1) > 0 ? 1.0 : 0.0; 8 | lights[L_GATE2].value = (seq & 2) > 0 ? 1.0 : 0.0; 9 | lights[L_GATE3].value = (seq & 4) > 0 ? 1.0 : 0.0; 10 | lights[L_GATE4].value = (seq & 8) > 0 ? 1.0 : 0.0; 11 | lights[L_GATE5].value = (seq & 16) > 0 ? 1.0 : 0.0; 12 | lights[L_GATE6].value = (seq & 32) > 0 ? 1.0 : 0.0; 13 | lights[L_GATE7].value = (seq & 64) > 0 ? 1.0 : 0.0; 14 | lights[L_GATE8].value = (seq & 128) > 0 ? 1.0 : 0.0; 15 | 16 | outputs[O_LEFT].setVoltage(0.0); 17 | outputs[O_RIGHT].setVoltage(0.0); 18 | 19 | size_t o = 0; // stores which of the eight steps we're working with 20 | for (size_t i = 0; i < 4; i++) { 21 | double gate; 22 | gate = (seq & (1 << o)) ? 1.0 : 0.0; 23 | outputs[O_LEFT].value += 24 | m_antipop[o].step(gate, params[P_VOL1 + i].getValue() * 25 | inputs[I_INPUT1 + i].getVoltage()); 26 | 27 | o++; 28 | 29 | gate = (seq & (1 << o)) ? 1.0 : 0.0; 30 | outputs[O_RIGHT].value += 31 | m_antipop[o].step(gate, params[P_VOL1 + i].getValue() * 32 | inputs[I_INPUT1 + i].getVoltage()); 33 | 34 | o++; 35 | } 36 | } 37 | 38 | turing_digital_module::turing_digital_module() : Module() { 39 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); 40 | onSampleRateChange(); 41 | for (size_t i = 0; i < 4; i++) { 42 | configParam(turing_digital_module::P_VOL1 + i, -1.0, 1.0, 0.0, ""); 43 | } 44 | } 45 | 46 | void turing_digital_module::onSampleRateChange() { 47 | double s = APP->engine->getSampleRate(); 48 | for (size_t i = 0; i < 8; i++) { 49 | m_antipop[i] = antipop_t(s); 50 | } 51 | } 52 | 53 | turing_digital_module::~turing_digital_module() {} 54 | -------------------------------------------------------------------------------- /src/turing-vactrol-module.cc: -------------------------------------------------------------------------------- 1 | #include "turing-vactrol-module.hh" 2 | 3 | void turing_vactrol_module::process(const ProcessArgs &args) { 4 | uint16_t seq = 5 | (uint16_t)ceil((inputs[I_EXPANDER].getVoltage() / 10.0) * 65535.0); 6 | 7 | lights[L_GATE1].value = (seq & 1) > 0 ? 1.0 : 0.0; 8 | lights[L_GATE2].value = (seq & 2) > 0 ? 1.0 : 0.0; 9 | lights[L_GATE3].value = (seq & 4) > 0 ? 1.0 : 0.0; 10 | lights[L_GATE4].value = (seq & 8) > 0 ? 1.0 : 0.0; 11 | lights[L_GATE5].value = (seq & 16) > 0 ? 1.0 : 0.0; 12 | lights[L_GATE6].value = (seq & 32) > 0 ? 1.0 : 0.0; 13 | lights[L_GATE7].value = (seq & 64) > 0 ? 1.0 : 0.0; 14 | lights[L_GATE8].value = (seq & 128) > 0 ? 1.0 : 0.0; 15 | 16 | outputs[O_LEFT].setVoltage(0.0); 17 | outputs[O_RIGHT].setVoltage(0.0); 18 | 19 | size_t o = 0; // stores which of the eight steps we're working with 20 | for (size_t i = 0; i < 4; i++) { 21 | double gate; 22 | gate = (seq & (1 << o)) ? 1.0 : 0.0; 23 | outputs[O_LEFT].value += 24 | m_vactrol[o].step(gate) * 25 | (params[P_VOL1 + i].getValue() * inputs[I_INPUT1 + i].getVoltage()); 26 | 27 | o++; 28 | 29 | gate = (seq & (1 << o)) ? 1.0 : 0.0; 30 | outputs[O_RIGHT].value += 31 | m_vactrol[o].step(gate) * 32 | (params[P_VOL1 + i].getValue() * inputs[I_INPUT1 + i].getVoltage()); 33 | 34 | o++; 35 | } 36 | } 37 | 38 | turing_vactrol_module::turing_vactrol_module() : Module() { 39 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); 40 | 41 | for (size_t i = 0; i < 8; i++) { 42 | m_vactrol[i] = vtl5c3(); 43 | } 44 | 45 | onSampleRateChange(); 46 | for (size_t i = 0; i < 4; i++) { 47 | configParam(turing_vactrol_module::P_VOL1 + i, -1.0, 1.0, 0.0, ""); 48 | } 49 | } 50 | 51 | void turing_vactrol_module::onSampleRateChange() { 52 | double s = APP->engine->getSampleRate(); 53 | for (size_t i = 0; i < 8; i++) { 54 | m_vactrol[i].set_samplerate(s); 55 | } 56 | } 57 | 58 | turing_vactrol_module::~turing_vactrol_module() {} 59 | -------------------------------------------------------------------------------- /src/turing-module-widget.cc: -------------------------------------------------------------------------------- 1 | #include "turing-module-widget.hh" 2 | #include "turing-module.hh" 3 | 4 | turing_module_widget::turing_module_widget(Module *module) : ModuleWidget() { 5 | setModule(module); 6 | setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Alan.svg"))); 7 | 8 | addChild(createWidget(Vec(RACK_GRID_WIDTH, 0))); 9 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); 10 | addChild(createWidget( 11 | Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); 12 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 13 | RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); 14 | 15 | addInput( 16 | createInput(Vec(63, 167), module, turing_module::I_MODE)); 17 | 18 | addInput( 19 | createInput(Vec(4.5, 173), module, turing_module::I_CLOCK)); 20 | 21 | addOutput(createOutput(Vec(64, 317), module, 22 | turing_module::O_VOLTAGE)); 23 | 24 | addOutput(createOutput(Vec(116, 317), module, 25 | turing_module::O_EXPANSION)); 26 | 27 | addOutput( 28 | createOutput(Vec(90, 274), module, turing_module::O_PULSE)); 29 | 30 | addOutput( 31 | createOutput(Vec(90, 317), module, turing_module::O_GATE)); 32 | 33 | addParam(createParam(Vec(47, 80), module, 34 | turing_module::P_MODE)); 35 | addParam(createParam(Vec(34, 320), module, 36 | turing_module::P_SCALE)); 37 | addParam(createParam(Vec(61, 215), module, 38 | turing_module::P_LENGTH)); 39 | addParam(createParam(Vec(10, 320), module, turing_module::P_POLE)); 40 | addParam(createParam(Vec(121, 175), module, turing_module::P_WRITE)); 41 | 42 | for (size_t i = 0; i < 8; i++) { 43 | addChild(createLight>(Vec(18 + (15 * i), 50), module, 44 | turing_module::L_LIGHT1 + i)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "slug": "Skylights", 3 | "name": "Skylights", 4 | "brand": "Skrylar", 5 | "version": "1.0.2", 6 | "license": "GPL-3.0", 7 | "author": "Skrylar", 8 | "authorEmail": "joshua.cearley@gmail.com", 9 | "authorUrl": "https://github.com/Skrylar", 10 | "pluginUrl": "https://github.com/Skrylar/skylights-vcv", 11 | "manualUrl": "https://github.com/Skrylar/skylights-vcv", 12 | "sourceUrl": "https://github.com/Skrylar/skylights-vcv", 13 | "donateUrl": "https://www.ko-fi.com/V7V6RG3E", 14 | "modules": [ 15 | { 16 | "slug": "SkAdrift", 17 | "name": "Adrift (Analogue Drift)", 18 | "description": "Randomized Sample and Hold, simulates minor analogue drift", 19 | "tags": [ 20 | "Sample and hold" 21 | ] 22 | }, 23 | { 24 | "slug": "SkTuringV2", 25 | "name": "Alan (Turing Machine)", 26 | "description": "A Whitwell turing machine.", 27 | "tags": [ 28 | "Sequencer" 29 | ] 30 | }, 31 | { 32 | "slug": "SkTuringPulse", 33 | "name": "Alan (Pulse Expander)", 34 | "description": "Turing Machine pulse expander", 35 | "tags": [ 36 | "Utility" 37 | ] 38 | }, 39 | { 40 | "slug": "SkTuringVolts", 41 | "name": "Alan (Volts Expander)", 42 | "description": "Turing Machine volts expander", 43 | "tags": [ 44 | "Utility" 45 | ] 46 | }, 47 | { 48 | "slug": "SkTuringVactrol", 49 | "name": "Alan (Digital Mix Expander)", 50 | "description": "A vactrol mix expander using digital signal attenuation.", 51 | "tags": [ 52 | "Utility" 53 | ] 54 | }, 55 | { 56 | "slug": "SkTuringVactrolAnalogue", 57 | "name": "Alan (Vactrol Mix Expander)", 58 | "description": "A vactrol mix expander using an emulated vactrol chip.", 59 | "tags": [ 60 | "Utility" 61 | ] 62 | }, 63 | { 64 | "slug": "SkWhatnoteCV", 65 | "name": "What Note?", 66 | "description": "A CV tuner.", 67 | "tags": [ 68 | "Tuner", 69 | "Utility" 70 | ] 71 | }, 72 | { 73 | "slug": "SkVactrolyzer", 74 | "name": "Vactrolyzer", 75 | "description": "An analogue modeled Vactrol.", 76 | "tags": [ 77 | "Utility" 78 | ] 79 | } 80 | ] 81 | } 82 | -------------------------------------------------------------------------------- /src/turing-pulse-module.cc: -------------------------------------------------------------------------------- 1 | #include "turing-pulse-module.hh" 2 | 3 | #include 4 | void turing_pulse_module::process(const ProcessArgs &args) { 5 | uint16_t seq = 6 | (uint16_t)ceil((inputs[I_EXPANDER].getVoltage() / 10.0) * 65535.0); 7 | 8 | outputs[O_GATE1].value = ((seq & 1) == 1 ? 10.0 : 0.0); 9 | outputs[O_GATE2].value = ((seq & 2) == 2 ? 10.0 : 0.0); 10 | outputs[O_GATE3].value = ((seq & 4) == 4 ? 10.0 : 0.0); 11 | outputs[O_GATE4].value = ((seq & 8) == 8 ? 10.0 : 0.0); 12 | outputs[O_GATE5].value = ((seq & 16) == 16 ? 10.0 : 0.0); 13 | outputs[O_GATE6].value = ((seq & 32) == 32 ? 10.0 : 0.0); 14 | outputs[O_GATE7].value = ((seq & 64) == 64 ? 10.0 : 0.0); 15 | outputs[O_GATE1P2].value = ((seq & 3) == 3 ? 10.0 : 0.0); 16 | outputs[O_GATE2P4].value = ((seq & 10) == 10 ? 10.0 : 0.0); 17 | outputs[O_GATE4P7].value = ((seq & 72) == 72 ? 10.0 : 0.0); 18 | outputs[O_GATE1P2P4P7].setVoltage(((seq & 75) == 75 ? 10.0 : 0.0)); 19 | 20 | if (inputs[I_PULSE].isConnected()) { 21 | outputs[O_GATE1].value *= inputs[I_PULSE].getVoltage(); 22 | outputs[O_GATE2].value *= inputs[I_PULSE].getVoltage(); 23 | outputs[O_GATE3].value *= inputs[I_PULSE].getVoltage(); 24 | outputs[O_GATE4].value *= inputs[I_PULSE].getVoltage(); 25 | outputs[O_GATE5].value *= inputs[I_PULSE].getVoltage(); 26 | outputs[O_GATE6].value *= inputs[I_PULSE].getVoltage(); 27 | outputs[O_GATE7].value *= inputs[I_PULSE].getVoltage(); 28 | outputs[O_GATE1P2].value *= inputs[I_PULSE].getVoltage(); 29 | outputs[O_GATE2P4].value *= inputs[I_PULSE].getVoltage(); 30 | outputs[O_GATE4P7].value *= inputs[I_PULSE].getVoltage(); 31 | outputs[O_GATE1P2P4P7].value *= inputs[I_PULSE].getVoltage(); 32 | } 33 | 34 | lights[L_GATE1].value = outputs[O_GATE1].value; 35 | lights[L_GATE2].value = outputs[O_GATE2].value; 36 | lights[L_GATE3].value = outputs[O_GATE3].value; 37 | lights[L_GATE4].value = outputs[O_GATE4].value; 38 | lights[L_GATE5].value = outputs[O_GATE5].value; 39 | lights[L_GATE6].value = outputs[O_GATE6].value; 40 | lights[L_GATE7].value = outputs[O_GATE7].value; 41 | lights[L_GATE1P2].value = outputs[O_GATE1P2].value; 42 | lights[L_GATE2P4].value = outputs[O_GATE2P4].value; 43 | lights[L_GATE4P7].value = outputs[O_GATE4P7].value; 44 | lights[L_GATE1P2P4P7].value = outputs[O_GATE1P2P4P7].value; 45 | } 46 | 47 | turing_pulse_module::turing_pulse_module() : Module() { 48 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); 49 | } 50 | 51 | turing_pulse_module::~turing_pulse_module() {} 52 | -------------------------------------------------------------------------------- /src/skylights.cc: -------------------------------------------------------------------------------- 1 | #include "skylights.hh" 2 | #include "recorder-module.hh" 3 | 4 | // make sure our weird header libs don't bit rot 5 | #include "vtl5c3.hh" 6 | 7 | Plugin *pluginInstance; 8 | 9 | /*[[[cog 10 | from modules import simple_plugins 11 | for p in simple_plugins: 12 | pn = p['name'].replace('_', '-') 13 | cog.outl('#include "{0}-module.hh"'.format(pn)) 14 | cog.outl('#include "{0}-module-widget.hh"'.format(pn)) 15 | ]]]*/ 16 | #include "adrift-module.hh" 17 | #include "adrift-module-widget.hh" 18 | #include "turing-module.hh" 19 | #include "turing-module-widget.hh" 20 | #include "turing-pulse-module.hh" 21 | #include "turing-pulse-module-widget.hh" 22 | #include "turing-volts-module.hh" 23 | #include "turing-volts-module-widget.hh" 24 | #include "turing-digital-module.hh" 25 | #include "turing-digital-module-widget.hh" 26 | #include "turing-vactrol-module.hh" 27 | #include "turing-vactrol-module-widget.hh" 28 | #include "vactrolyzer-module.hh" 29 | #include "vactrolyzer-module-widget.hh" 30 | /*[[[end]]]*/ 31 | 32 | /*[[[cog 33 | for p in simple_plugins: 34 | cog.out('Model* {0}_model = createModel<{0}_module, {0}_module_widget>("{1}"'.format(p['name'], p['slug'])) 35 | cog.outl(');') 36 | ]]]*/ 37 | Model* adrift_model = createModel("SkAdrift"); 38 | Model* turing_model = createModel("SkTuringV2"); 39 | Model* turing_pulse_model = createModel("SkTuringPulse"); 40 | Model* turing_volts_model = createModel("SkTuringVolts"); 41 | Model* turing_digital_model = createModel("SkTuringVactrol"); 42 | Model* turing_vactrol_model = createModel("SkTuringVactrolAnalogue"); 43 | Model* vactrolyzer_model = createModel("SkVactrolyzer"); 44 | /*[[[end]]]*/ 45 | 46 | void init(Plugin *p) { 47 | pluginInstance = p; 48 | p->slug = TOSTRING(SLUG); 49 | p->version = TOSTRING(VERSION); 50 | 51 | // Add all Models defined throughout the plugin 52 | // p->addModel(recorder_model); 53 | p->addModel(whatnote_model); 54 | 55 | /*[[[cog 56 | for p in simple_plugins: 57 | cog.outl('p->addModel({}_model);'.format(p['name'])) 58 | ]]]*/ 59 | p->addModel(adrift_model); 60 | p->addModel(turing_model); 61 | p->addModel(turing_pulse_model); 62 | p->addModel(turing_volts_model); 63 | p->addModel(turing_digital_model); 64 | p->addModel(turing_vactrol_model); 65 | p->addModel(vactrolyzer_model); 66 | /*[[[end]]]*/ 67 | } 68 | -------------------------------------------------------------------------------- /src/tags.py: -------------------------------------------------------------------------------- 1 | import enum 2 | 3 | 4 | class ModuleTag(enum.IntEnum): 5 | """ 6 | Tags which categorize a module within Rack. 7 | """ 8 | 9 | """[[[cog 10 | tags = ['NO_TAG', 'AMPLIFIER_TAG', 'ARPEGGIATOR_TAG', 'ATTENUATOR_TAG', 11 | 'BLANK_TAG', 'CHORUS_TAG', 'CLOCK_MODULATOR_TAG', 'CLOCK_TAG', 12 | 'COMPRESSOR_TAG', 'CONTROLLER_TAG', 'DELAY_TAG', 'DIGITAL_TAG', 13 | 'DISTORTION_TAG', 'DRUM_TAG', 'DUAL_TAG', 'DYNAMICS_TAG', 14 | 'EFFECT_TAG', 'ENVELOPE_FOLLOWER_TAG', 'ENVELOPE_GENERATOR_TAG', 15 | 'EQUALIZER_TAG', 'EXTERNAL_TAG', 'FILTER_TAG', 'FLANGER_TAG', 16 | 'FUNCTION_GENERATOR_TAG', 'GRANULAR_TAG', 'LFO_TAG', 'LIMITER_TAG', 17 | 'LOGIC_TAG', 'LOW_PASS_GATE_TAG', 'MIDI_TAG', 'MIXER_TAG', 18 | 'MULTIPLE_TAG', 'NOISE_TAG', 'OSCILLATOR_TAG', 'PANNING_TAG', 19 | 'PHASER_TAG', 'PHYSICAL_MODELING_TAG', 'QUAD_TAG', 'QUANTIZER_TAG', 20 | 'RANDOM_TAG', 'RECORDING_TAG', 'REVERB_TAG', 'RING_MODULATOR_TAG', 21 | 'SAMPLE_AND_HOLD_TAG', 'SAMPLER_TAG', 'SEQUENCER_TAG', 'SLEW_LIMITER_TAG', 22 | 'SWITCH_TAG', 'SYNTH_VOICE_TAG', 'TUNER_TAG', 'UTILITY_TAG', 'VISUAL_TAG', 23 | 'VOCODER_TAG', 'WAVESHAPER_TAG', 'NUM_TAGS'] 24 | for i, t in enumerate(tags): 25 | cog.outl('{} = {}'.format(t, i)) 26 | ]]]""" 27 | NO_TAG = 0 28 | AMPLIFIER_TAG = 1 29 | ARPEGGIATOR_TAG = 2 30 | ATTENUATOR_TAG = 3 31 | BLANK_TAG = 4 32 | CHORUS_TAG = 5 33 | CLOCK_MODULATOR_TAG = 6 34 | CLOCK_TAG = 7 35 | COMPRESSOR_TAG = 8 36 | CONTROLLER_TAG = 9 37 | DELAY_TAG = 10 38 | DIGITAL_TAG = 11 39 | DISTORTION_TAG = 12 40 | DRUM_TAG = 13 41 | DUAL_TAG = 14 42 | DYNAMICS_TAG = 15 43 | EFFECT_TAG = 16 44 | ENVELOPE_FOLLOWER_TAG = 17 45 | ENVELOPE_GENERATOR_TAG = 18 46 | EQUALIZER_TAG = 19 47 | EXTERNAL_TAG = 20 48 | FILTER_TAG = 21 49 | FLANGER_TAG = 22 50 | FUNCTION_GENERATOR_TAG = 23 51 | GRANULAR_TAG = 24 52 | LFO_TAG = 25 53 | LIMITER_TAG = 26 54 | LOGIC_TAG = 27 55 | LOW_PASS_GATE_TAG = 28 56 | MIDI_TAG = 29 57 | MIXER_TAG = 30 58 | MULTIPLE_TAG = 31 59 | NOISE_TAG = 32 60 | OSCILLATOR_TAG = 33 61 | PANNING_TAG = 34 62 | PHASER_TAG = 35 63 | PHYSICAL_MODELING_TAG = 36 64 | QUAD_TAG = 37 65 | QUANTIZER_TAG = 38 66 | RANDOM_TAG = 39 67 | RECORDING_TAG = 40 68 | REVERB_TAG = 41 69 | RING_MODULATOR_TAG = 42 70 | SAMPLE_AND_HOLD_TAG = 43 71 | SAMPLER_TAG = 44 72 | SEQUENCER_TAG = 45 73 | SLEW_LIMITER_TAG = 46 74 | SWITCH_TAG = 47 75 | SYNTH_VOICE_TAG = 48 76 | TUNER_TAG = 49 77 | UTILITY_TAG = 50 78 | VISUAL_TAG = 51 79 | VOCODER_TAG = 52 80 | WAVESHAPER_TAG = 53 81 | NUM_TAGS = 54 82 | # [[[end]]] 83 | -------------------------------------------------------------------------------- /src/whatnote-module-widget.cc: -------------------------------------------------------------------------------- 1 | #include "whatnote-module-widget.hh" 2 | #include "whatnote-module.hh" 3 | 4 | whatnote_module_widget::whatnote_module_widget(Module *module) 5 | : ModuleWidget() { 6 | setModule(module); 7 | font = APP->window->loadFont( 8 | asset::plugin(pluginInstance, "res/LEDCalculator.ttf")); 9 | 10 | setPanel( 11 | APP->window->loadSvg(asset::plugin(pluginInstance, "res/WhatNote.svg"))); 12 | 13 | addChild(createWidget(Vec(RACK_GRID_WIDTH, 0))); 14 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); 15 | addChild(createWidget( 16 | Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); 17 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 18 | RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); 19 | 20 | addInput(createInput(Vec(57.5, 273), module, 21 | whatnote_module::AUDIO_INPUT + 0)); 22 | } 23 | 24 | void whatnote_module_widget::draw(const DrawArgs &args) { 25 | whatnote_module *mod = reinterpret_cast(module); 26 | 27 | static const char *semitone_labels[12] = {"C", "C#", "D", "D#", "E", "F", 28 | "F#", "G", "G#", "A", "A#", "B"}; 29 | 30 | ModuleWidget::draw(args); 31 | if (!mod) 32 | return; 33 | 34 | char buffer[128]; 35 | 36 | nvgFillColor(args.vg, nvgRGBA(0x00, 0x00, 0x00, 0xFF)); 37 | nvgFontFaceId(args.vg, font->handle); 38 | nvgTextLetterSpacing(args.vg, 0); 39 | nvgTextAlign(args.vg, NVG_ALIGN_CENTER); 40 | 41 | // the big font 42 | nvgFontSize(args.vg, 20); 43 | 44 | if (mod->octave >= -10) { 45 | snprintf(reinterpret_cast(&buffer), 128, "%s%d", 46 | semitone_labels[mod->semitone], mod->octave); 47 | 48 | nvgTextBox(args.vg, 25, 164, 85, buffer, 0); 49 | 50 | // the little fonts 51 | nvgFontSize(args.vg, 14); 52 | 53 | if (mod->cents > 0) { 54 | snprintf(reinterpret_cast(&buffer), 128, "+%d", mod->cents); 55 | } else { 56 | snprintf(reinterpret_cast(&buffer), 128, "%d", mod->cents); 57 | } 58 | 59 | nvgTextBox(args.vg, 25, 182, 85, buffer, 0); 60 | } else { 61 | nvgTextBox(args.vg, 25, 164, 85, "--", 0); 62 | 63 | // the little fonts 64 | nvgFontSize(args.vg, 14); 65 | } 66 | 67 | snprintf(reinterpret_cast(&buffer), 128, "%.2f V", mod->voltage); 68 | 69 | nvgTextBox(args.vg, 25, 198, 85, buffer, 0); 70 | } 71 | 72 | // Specify the Module and ModuleWidget subclass, human-readable 73 | // author name for categorization per plugin, module slug (should never 74 | // change), human-readable module name, and any number of tags 75 | // (found in `include/tags.hpp`) separated by commas. 76 | Model *whatnote_model = 77 | createModel("SkWhatnoteCV"); 78 | -------------------------------------------------------------------------------- /src/adrift-module.cc: -------------------------------------------------------------------------------- 1 | #include "adrift-module.hh" 2 | 3 | adrift_module::adrift_module() : Module() { 4 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); 5 | configParam(adrift_module::P_ATTENUATOR, -1.0, 1.0, 0.0, ""); 6 | for (size_t i = 0; i < adrift_module::channels; i++) { 7 | // bipolar toggle 8 | configParam(adrift_module::P_BIP0 + i, 0.0, 1.0, 0.0, ""); 9 | } 10 | } 11 | 12 | adrift_module::~adrift_module() {} 13 | 14 | void adrift_module::noisify_all() { 15 | for (size_t i = 0; i < channels; i++) { 16 | noisify(i); 17 | } 18 | } 19 | 20 | void adrift_module::noisify(int channel) { 21 | // there is very likely a more efficient way to pull this off 22 | uint8_t dac; 23 | 24 | dac = (m_noise_source.next() ? 0 : 1) << 0; 25 | dac += (m_noise_source.next() ? 0 : 1) << 1; 26 | dac += (m_noise_source.next() ? 0 : 1) << 2; 27 | dac += (m_noise_source.next() ? 0 : 1) << 3; 28 | dac += (m_noise_source.next() ? 0 : 1) << 4; 29 | dac += (m_noise_source.next() ? 0 : 1) << 5; 30 | dac += (m_noise_source.next() ? 0 : 1) << 6; 31 | dac += (m_noise_source.next() ? 0 : 1) << 7; 32 | 33 | noise[channel] = ((double)dac) / 255.0; 34 | } 35 | 36 | void adrift_module::process(const ProcessArgs &args) { 37 | if (m_reset_all.process(inputs[I_TRIG_ALL].getVoltage())) { 38 | noisify_all(); 39 | } else { 40 | for (size_t i = 0; i < channels; i++) { 41 | if (m_reset[i].process(inputs[I_TRIG0 + i].getVoltage())) { 42 | noisify(i); 43 | } 44 | } 45 | } 46 | 47 | for (size_t i = 0; i < channels; i++) { 48 | outputs[O_OUT0 + i].value = 49 | inputs[I_CV0 + i].getVoltage() + 50 | ((noise[i] - (0.5 * params[P_BIP0 + i].getValue())) * 51 | params[P_ATTENUATOR].getValue()); 52 | } 53 | } 54 | 55 | void adrift_module::onSampleRateChange() {} 56 | 57 | void adrift_module::onReset() { noisify_all(); } 58 | 59 | void adrift_module::onRandomize() { noisify_all(); } 60 | 61 | json_t *adrift_module::dataToJson() { 62 | auto map = json_object(); 63 | auto array = json_array(); 64 | 65 | for (size_t i = 0; i < channels; i++) { 66 | json_array_set_new(array, i, json_integer(noise[i])); 67 | } 68 | 69 | json_object_set_new(map, "noise", array); 70 | return map; 71 | } 72 | 73 | void adrift_module::dataFromJson(json_t *root) { 74 | if (!root) 75 | return; 76 | 77 | auto array_schrodinger = json_object_get(root, "noise"); 78 | 79 | if (json_is_array(array_schrodinger)) { 80 | auto array = array_schrodinger; 81 | 82 | for (size_t i = 0; i < channels; i++) { 83 | auto schrodingers_float = json_array_get(array, i); 84 | if (json_is_number(schrodingers_float)) { 85 | noise[i] = json_integer_value(schrodingers_float); 86 | } else { 87 | noisify(i); 88 | } 89 | } 90 | } else { 91 | // _ _ _ _ _ 92 | // __| | __ _ _ __ ___ _ __ (_) |_ __ _| | | 93 | // / _` |/ _` | '_ ` _ \| '_ \ | | __| / _` | | | 94 | // | (_| | (_| | | | | | | | | | | | |_ | (_| | | | 95 | // \__,_|\__,_|_| |_| |_|_| |_| |_|\__| \__,_|_|_| 96 | noisify_all(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/turing-module.cc: -------------------------------------------------------------------------------- 1 | #include "turing-module.hh" 2 | 3 | #include 4 | 5 | const double SEMITONE = 1.0 / 12.0; 6 | 7 | void turing_module::process(const ProcessArgs &args) { 8 | double mode; 9 | if (inputs[I_MODE].isConnected()) 10 | mode = inputs[I_MODE].getVoltage(); 11 | else 12 | mode = params[P_MODE].getValue(); 13 | 14 | bool hot = m_sequence & 0x1; 15 | outputs[O_GATE].setVoltage(hot ? 10.0 : 0.0); 16 | outputs[O_PULSE].value = std::min( 17 | outputs[O_GATE].value * inputs[I_CLOCK].getVoltage(), (float)10.0); 18 | 19 | // check for clock advance 20 | auto was_high = m_clock_trigger.isHigh(); 21 | m_clock_trigger.process(inputs[I_CLOCK].getVoltage()); 22 | if (!was_high && was_high != m_clock_trigger.isHigh()) { 23 | // clock was advanced 24 | 25 | // write knob always zeroes our input 26 | if (params[P_WRITE].getValue() > 0.9) 27 | hot = false; 28 | else if (mode > 0.9) { 29 | // leave hot alone 30 | } else if (mode > 0.55) { 31 | // inverts about 13% of the time 32 | const size_t TRIES = 3; 33 | bool should_flip = true; 34 | for (size_t i = 0; i <= TRIES; i++) { 35 | should_flip = (should_flip == m_spigot.next()); 36 | if (!should_flip) 37 | break; 38 | } 39 | hot = should_flip ? !hot : hot; 40 | } else if (mode > 0.10) { 41 | // 50/50 invert 42 | bool should_invert = m_spigot.next(); 43 | hot = should_invert ? !hot : hot; 44 | } else { 45 | // always invert 46 | hot = !hot; 47 | } 48 | 49 | // compute an advance mask based on step length, [2, 16) 50 | uint16_t mask = 0; 51 | size_t steps = 0; 52 | for (double i = 0; i < params[P_LENGTH].getValue(); i += 1) { 53 | mask <<= 1; 54 | mask |= 0x1; 55 | steps++; 56 | } 57 | 58 | uint16_t seq = m_sequence & mask; 59 | seq >>= 1; 60 | seq |= (hot ? 1 : 0) << (steps - 1); 61 | m_sequence &= ~mask; 62 | m_sequence += seq; 63 | 64 | uint8_t signal_d = m_sequence & 0xFF; 65 | double signal_a = (((double)signal_d) / 255.0); 66 | outputs[O_VOLTAGE].value = 67 | (signal_a * params[P_SCALE].getValue()) // signal scaled by scale knob 68 | - (5.0 * params[P_POLE].getValue()); // shift to bi-polar on request 69 | 70 | // expander is always 10v unipolar 71 | outputs[O_EXPANSION].setVoltage((((double)m_sequence) / 65535.0) * 10.0); 72 | 73 | for (size_t i = 0; i < 8; i++) { 74 | lights[L_LIGHT8 - i].value = ((m_sequence & (1 << i)) > 0) ? 1.0 : 0.0; 75 | } 76 | } 77 | } 78 | 79 | turing_module::turing_module() : Module(), m_sequence(0) { 80 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); 81 | configParam(turing_module::P_MODE, 0.0, 1.0, 1.0, ""); 82 | configParam(turing_module::P_SCALE, 0.0, 10.0, 1.0, ""); 83 | configParam(turing_module::P_LENGTH, 2.0, 16.0, 8.0, ""); 84 | configParam(turing_module::P_POLE, 0.0, 1.0, 0.0, ""); 85 | configParam(turing_module::P_WRITE, 0.0, 1.0, 0.0, ""); 86 | } 87 | 88 | turing_module::~turing_module() {} 89 | 90 | json_t *turing_module::dataToJson() { 91 | auto map = json_object(); 92 | 93 | json_object_set_new(map, "sequence", json_integer(m_sequence)); 94 | 95 | return map; 96 | } 97 | 98 | void turing_module::dataFromJson(json_t *root) { 99 | if (!root) 100 | return; 101 | 102 | auto seqo = json_object_get(root, "sequence"); 103 | if (json_is_number(seqo)) { 104 | m_sequence = (uint16_t)json_integer_value(seqo); 105 | } 106 | } 107 | 108 | void turing_module::onReset() { 109 | Module::onReset(); 110 | m_sequence = 0; 111 | } 112 | 113 | void turing_module::onRandomize() { 114 | Module::onRandomize(); 115 | m_sequence = 0; 116 | for (size_t i = 0; i < 16; i++) { 117 | m_sequence += m_spigot.next() << i; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /res/cntr_DK.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 32 | 35 | 39 | 43 | 44 | 54 | 57 | 61 | 65 | 66 | 76 | 79 | 83 | 87 | 88 | 97 | 100 | 104 | 108 | 109 | 118 | 121 | 125 | 129 | 130 | 131 | 153 | 155 | 156 | 158 | image/svg+xml 159 | 161 | 162 | 163 | 164 | 165 | 170 | 174 | 180 | 186 | 192 | 197 | 202 | 207 | 213 | 214 | 215 | 216 | -------------------------------------------------------------------------------- /res/cntr_LT.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 32 | 35 | 39 | 43 | 44 | 54 | 57 | 61 | 65 | 66 | 76 | 79 | 83 | 87 | 88 | 98 | 101 | 105 | 109 | 110 | 120 | 123 | 127 | 131 | 132 | 133 | 155 | 157 | 158 | 160 | image/svg+xml 161 | 163 | 164 | 165 | 166 | 167 | 172 | 176 | 183 | 190 | 197 | 202 | 207 | 212 | 217 | 218 | 219 | 220 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | 2 | Skylights is a random collection of modules that I thought would be fun to have. 3 | 4 | They are not profitable to work on, so they are maintained or developed on a whim. 5 | 6 | [[https://www.ko-fi.com/V7V6RG3E][You are still free to send a tip or commission a module. ]] 7 | 8 | [[https://www.ko-fi.com/V7V6RG3E][https://www.ko-fi.com/img/donate_sm.png]] 9 | 10 | * Modules 11 | 12 | ** What Note? 13 | 14 | #+begin_note 15 | This module is stylized as "WHATNOTE?" even though technically, it is 16 | two words. 17 | #+end_note 18 | 19 | The =What Note?= is a CV tuner with a digital display. It shows the 20 | semitone, octave, cents and absolute voltage of the signal which 21 | enters its port. 22 | 23 | ** Turing Machines 24 | *** ALAN 25 | The =ALAN= is an SK version of [[https://musicthing.co.uk/collateral/TuringRev1Docs.pdf][Tom Whitwell's Turing Machine]]. I wrote 26 | it because the original Turing Machine is open hardware, and so the 27 | virtual version should be open source too[fn::There is also a weird 28 | rite of passage that one build their own Turing Machine when they 29 | start building Eurorack components, as the Turing Machine is a common 30 | first project despite its complexity.]. 31 | 32 | | Port | What it does | 33 | |-----------+------------------------------------------------------------------------------| 34 | | Lock Knob | Controls the probability that bits in the sequence are flipped. | 35 | | Out | Outputs the last eight bits of the sequence, run through an 8-bit DAC. | 36 | | Gate | Outputs high if the final bit is lit (represented by a bar through the LED.) | 37 | | Write | Forces new bits to always be zero. | 38 | | Pole | Sets whether the DAC emits a unipolar or bipolar signal. | 39 | | Scale | Scales the outgoing signal from the DAC. | 40 | 41 | ALAN sequences are also exported over the [[* XPND Ports][XPND interface]]. This allows 42 | one or more expanders to access the sequence and provide linked 43 | functionality. 44 | 45 | **** Lock knob 46 | Use of the lock knob is the primary way to control a Turing Machine 47 | sequencer. Rotating the knob places it in to a different "mode" which 48 | determines how much variation will be introduced to the existing 49 | sequence over time. 50 | 51 | - Beta side :: sequence loops indefinitely. 52 | - 55-90% :: around one in eight bits is changed. 53 | - 10-54% :: approximately every other bit is flipped. 54 | - Alpha side :: every bit is flipped. 55 | 56 | There is a CV port beneath the lock kob. When a patch cable is 57 | connected to this, the knob is ignored and the control voltage takes 58 | its place. CV signals should be within [0V, 10V]. 59 | 60 | **** Write 61 | When the write switch is set, bits are always set to zero when they 62 | scroll past the right side of the display. This can be used to 63 | manually clean up a busy sequence or to clear the sequencer after 64 | enough steps pass. 65 | 66 | **** Gate 67 | When the bit on the far right of the display is set (the light is 68 | blue), gate will output a high signal. Otherwise gate will output a 69 | low signal. 70 | 71 | **** Pole 72 | The pole switch allows you to set the module output between unipolar 73 | [0V, 10V] or bipolar [-5V, 5V] modes. 74 | 75 | **** Scale 76 | The scale knob allows you to attenuate or boost the output signal. By 77 | default this is set to 1, which covers a single octave in unipolar 78 | mode. 79 | 80 | **** Out 81 | Out will perform an analogue to digital encoding of the 8 visible bits 82 | on the display, scale the signal through the =Scale= knob and perform 83 | a polarity shift depending on the =UNI/BI= switch. 84 | 85 | **** Persistence 86 | 87 | While a turing machine sequencer is not designed to hold on to state 88 | forever, ALAN does write its internal sequence to JSON. This means: 89 | 90 | - A locked sequence is saved along with your =.vcv= file, and, 91 | - You can right click and use =Save preset= or =Open preset= to 92 | keep a library of locked sequences. 93 | 94 | **** Patching ideas 95 | Turing sequencers can drive an entire synthesizer cabinet; an external 96 | clock feeds pulses in, while =Out= and =Gate= produce notes or 97 | rhythms. Connecting =Out= to a note quantizer and further to a VCO can 98 | produce melodies over time. Using two turing sequencers, you can have 99 | a separately generated melody and rhythm that are independently 100 | evolvable. 101 | 102 | When an oscillator is used as the clock, turing sequencers are driven 103 | at audio rates and behave similar to 8-bit wavetable 104 | synthesizers. Placing the lock knob will then control the amount of 105 | "grit" present in the sound, from 8-bit white noise to a more normal 106 | waveform. 107 | 108 | *** Volts (Turing Expander) 109 | 110 | | Port | What it does | 111 | |-------+--------------------------------------------------------------------------| 112 | | In | Feeds signals to the mixer. | 113 | | Knobs | Attenuverts the respective input signal. Clamped to the range [-1V, 1V]. | 114 | | Out | Stereo output of the mixer. Clamped to the range [-5V, 5V]. | 115 | 116 | Volts is an expander for the ALAN based on [[https://musicthing.co.uk/pages/volts.html][Tom Whitwell's own "Volts" 117 | expansion module]]. 118 | 119 | Volts reads the last eight bits of a sequence generated by ALAN. This 120 | means you need an ALAN (or [[* XPND Ports][ALAN XPND-compatible]] module) in conjunction 121 | with a Volts. Each bit corresponds to an LED on the faceplate. 122 | 123 | Input signals are fed in through the audio ports, then attenuverted 124 | based on their corresponding knobs. Each knob also has two LEDs that 125 | display if that channel will be added to the left, right, or both 126 | output channels. 127 | 128 | *** Pulses (Turing Expander) 129 | 130 | **** Controls 131 | 132 | ***** CLK 133 | When a clock signal is connected, outputs from this module only happen 134 | both at the correct steps and when the incoming clock signal is 135 | high. If you do not have a cable plugged in to this port the outputs 136 | operate as gates instead of pulses. 137 | 138 | ***** XPND 139 | See [[* XPND Ports][XPND Ports]]. 140 | 141 | *** Vactrol (Turing Expander) 142 | The Vactrol mixer accepts four incoming signals, passes them through 143 | four independent attenuvators, then outputs them to the left and/or 144 | right output channels depending on the state of a turing machine 145 | connected via the =XPND= port. 146 | 147 | **** Ports 148 | ***** XPND 149 | See [[* XPND Ports][XPND Ports]]. 150 | 151 | ***** IN 152 | Single channel audio signals going in to the mixer. 153 | 154 | ***** OUT 155 | The left and right output channels from the mixer. 156 | 157 | **** Controls 158 | Each knob controls an internal attenuvator; the knobs from top to 159 | bottom represent the same input as the input jacks from top to bottom. 160 | 161 | *** XPND Ports 162 | The expansion port encodes the internal 16-bit sequence of an ALAN to 163 | a [0V, 10V] control voltage. =XPND= does not obey any scale or pole 164 | settings. You /can/ use this to drive other modules in your patch, 165 | but its primary purpose is connecting to expanders. 166 | 167 | Internally an unsigned 16-bit integer is converted to a double and 168 | divided against 65,535.0 and then normalized to 10V. Expansion modules 169 | perform these steps in reverse to regain access to individual rhythm 170 | bits. 171 | 172 | Note that Volts only reads the five least significant bits of the 173 | sequence regardless of however many are live. 174 | 175 | ** ADRIFT 176 | 177 | | Port | What it does | 178 | |------------+-------------------------------------------------| 179 | | All | Triggers all channels to sample new noise. | 180 | | Turbulence | Controls amount of noise added to each channel. | 181 | | In | CV to have noise added. | 182 | | Sail | Triggers a single channel to sample new noise. | 183 | | BIP | Whether noise is bipolar. | 184 | | Out | Input CV with attenuverted noise added. | 185 | 186 | ADRIFT is based on the idea of using a sample and hold, connected to a 187 | noise source, as a means of adding minor amounts of flavor to 188 | notes. It simplifies the need for multiple Kinks, VCAs and mixers when 189 | all you want is just a little bit of drift to your attacks. 190 | 191 | | Detuners | Sample & Holds | 192 | |------------------------------------------------------------------------+----------------------------------------| 193 | | Continue to change V/Oct CVs throughout a note. | Lock the noise added during a note. | 194 | | Usually allow a single input with multiple separately detuned outputs. | Usually a single input, single output. | 195 | 196 | =Sail= is always handled prior to coloring and outputting signals 197 | throughout each channel. This means you can connect the =Gate= or 198 | =Retrig= from note inputs to =Sail= and attacks will appropriately 199 | receive detuned input. 200 | 201 | ADRIFT does not sample new noise unless explicitly told to (via a high 202 | pulse to either =All= or a =Sail= port.) Retriggering while a note is 203 | probably fun but will be abrupt--you will need to bring your own slew 204 | limiter if smoothness is desired. 205 | 206 | ** Vactrolyzer 207 | 208 | The vactrolyzer has only =in= and =out= ports, two pairs. Any signal 209 | which is sent to the input is run through an analogue modelled vactrol 210 | and the resulting signal is sent to the output. 211 | 212 | These are used in low pass gates or certain unusual pieces of 213 | equipment (like the Turing Machine's stereo mixer) to add a natural 214 | pluck and lag to on/off signals. It can be used as a kind of slew 215 | limiter for simpler control signals but is based on the response curve 216 | of the actual VTL5C3 chip (which is not configurable.) 217 | 218 | A scaler/amplifier may be needed as a second stage to bring the output 219 | up to whatever range you need. 220 | 221 | Vactrols are typically low voltage and their response curves are not 222 | linear. They do not accept negative control voltages and over-volting 223 | them causes distortions or failure of the device. In the simulation 224 | overvoltage simply returns an output of 1V and negative voltages are 225 | ignored (it will output 0V.) 226 | 227 | ** Internal notes 228 | 229 | The analogue modeling works as follows: 230 | 231 | - Incoming voltages from Rack are adapted to the chip's actual 232 | operating range. 233 | - An "exponentially weighted moving average" is taken but it has two 234 | weight factors: increase and decrease. Each one was tuned with 235 | genetic programming to match the light on and light off values from 236 | the chip's spec sheet. So an on signal "pulls" the LED towards 237 | brightness in the sensor chamber and an off signal allows it to 238 | fall. 239 | - This weighted value is then run through a small formula which was 240 | curve fit against the data sheet, to simulate non-linear output. 241 | 242 | * Licenses 243 | Skylights itself is available under the BSD license. 244 | 245 | Custom graphics were designed by github user [[https://github.com/infamedavid][@infamedavid (David 246 | Rodriguez)]], provided under CC-BY. 247 | 248 | Skylights is based on the Rack plugin template, which was provided 249 | under CC-0. 250 | -------------------------------------------------------------------------------- /res/WhatNote.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 24 | 26 | 32 | 33 | 35 | 39 | 40 | 42 | 46 | 53 | 54 | 55 | 62 | 67 | 68 | 70 | 76 | 77 | 79 | 83 | 84 | 86 | 92 | 93 | 95 | 99 | 100 | 102 | 106 | 113 | 114 | 115 | 122 | 127 | 128 | 130 | 136 | 137 | 139 | 143 | 144 | 145 | 175 | 177 | 178 | 180 | image/svg+xml 181 | 183 | 184 | 185 | 186 | 187 | 192 | 207 | 212 | 217 | 222 | 223 | 228 | 233 | 238 | 243 | 248 | 253 | 258 | 263 | 268 | 273 | 274 | 280 | 287 | 292 | 297 | 302 | 307 | 312 | 317 | 322 | 327 | 328 | 329 | 330 | -------------------------------------------------------------------------------- /res/Vactrolyzer.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 23 | 25 | 28 | 32 | 36 | 37 | 40 | 44 | 48 | 49 | 52 | 56 | 60 | 61 | 71 | 81 | 91 | 101 | 104 | 108 | 112 | 113 | 123 | 133 | 142 | 145 | 149 | 153 | 154 | 163 | 166 | 170 | 174 | 175 | 184 | 193 | 194 | 224 | 229 | 234 | 239 | 244 | 249 | 254 | 259 | 260 | 262 | 263 | 265 | image/svg+xml 266 | 268 | 269 | 270 | 271 | 272 | 278 | 283 | 288 | 293 | 298 | 303 | 308 | 313 | 319 | 323 | 327 | 328 | 334 | 340 | 346 | 350 | 355 | 361 | 364 | 369 | 374 | 375 | 381 | 385 | 390 | 395 | 396 | 402 | 403 | 414 | 422 | 427 | 431 | 435 | 439 | 443 | 444 | 448 | 452 | 456 | 457 | 465 | 470 | 475 | 480 | 485 | 490 | 491 | 496 | 501 | 506 | 507 | 508 | 690 | 691 | -------------------------------------------------------------------------------- /res/AlanVolts.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 23 | 25 | 28 | 32 | 36 | 37 | 40 | 44 | 48 | 49 | 52 | 56 | 60 | 61 | 71 | 81 | 91 | 101 | 104 | 108 | 112 | 113 | 123 | 133 | 142 | 145 | 149 | 153 | 154 | 163 | 166 | 170 | 174 | 175 | 184 | 193 | 194 | 223 | 228 | 233 | 238 | 243 | 248 | 253 | 258 | 259 | 261 | 262 | 264 | image/svg+xml 265 | 267 | 268 | 269 | 270 | 271 | 277 | 282 | 287 | 292 | 297 | 302 | 307 | 312 | 317 | 323 | 329 | 335 | 339 | 344 | 350 | 353 | 358 | 363 | 364 | 370 | 374 | 379 | 384 | 385 | 391 | 392 | 396 | 401 | 406 | 411 | 416 | 421 | 422 | 426 | 431 | 436 | 441 | 446 | 447 | 451 | 456 | 461 | 466 | 471 | 476 | 481 | 486 | 491 | 492 | 495 | 503 | 508 | 513 | 518 | 523 | 524 | 525 | 526 | 707 | 708 | --------------------------------------------------------------------------------