├── .gitignore
├── .gitmodules
├── LICENSE.txt
├── Makefile
├── README.md
├── manual
├── CV.md
├── CV.pdf
├── CVSeq.md
├── CVSeq.pdf
├── Carbon.md
├── Carbon.pdf
├── DTMF.md
├── DTMF.pdf
├── Eq.md
├── Eq.pdf
├── K.md
├── K.pdf
├── M.md
├── M.pdf
├── Mixer.md
├── Mixer.pdf
├── MixerCV.md
├── MixerCV.pdf
├── Noise.md
├── Noise.pdf
├── Not.md
├── Not.pdf
├── Oscar2.md
├── Oscar2.pdf
├── Pan.md
├── Pan.pdf
├── Shift.md
├── Shift.pdf
├── Tine.md
├── Tine.pdf
├── Tsunami.md
├── Tsunami.pdf
├── X.md
├── X.pdf
└── images
│ ├── CV-full.png
│ ├── CV-small.png
│ ├── CV.png
│ ├── CVSeq-full.png
│ ├── CVSeq-small.png
│ ├── CVSeq.png
│ ├── Carbon-full.png
│ ├── Carbon-small.png
│ ├── Carbon.png
│ ├── DTMF-full.png
│ ├── DTMF-small.png
│ ├── DTMF.png
│ ├── Eq-full.png
│ ├── Eq-small.png
│ ├── Eq.png
│ ├── K-full.png
│ ├── K-small.png
│ ├── K.png
│ ├── M-full.png
│ ├── M-small.png
│ ├── M.png
│ ├── Mixer-full.png
│ ├── Mixer-small.png
│ ├── Mixer.png
│ ├── MixerCV-full.png
│ ├── MixerCV-small.png
│ ├── MixerCV.png
│ ├── Noise-full.png
│ ├── Noise-small.png
│ ├── Noise.png
│ ├── Not-full.png
│ ├── Not-small.png
│ ├── Not.png
│ ├── Oscar2-full.png
│ ├── Oscar2-small.png
│ ├── Oscar2.png
│ ├── Pan-full.png
│ ├── Pan-small.png
│ ├── Pan.png
│ ├── SVModular.png
│ ├── Shift-full.png
│ ├── Shift-small.png
│ ├── Shift.png
│ ├── Tine-full.png
│ ├── Tine-small.png
│ ├── Tine.png
│ ├── Tsunami-full.png
│ ├── Tsunami-small.png
│ ├── Tsunami.png
│ ├── X-full.png
│ ├── X-small.png
│ └── X.png
├── plugin.json
├── res
├── CDSlider.svg
├── CDSliderHandle.svg
├── CV.svg
├── CVSeq.svg
├── Carbon.svg
├── DTMF.svg
├── Eq.svg
├── K.svg
├── Knob.svg
├── KnobSmall.svg
├── M.svg
├── Mixer.svg
├── MixerCV.svg
├── Noise.svg
├── Not.svg
├── Oscar2.svg
├── Pan.svg
├── Port.svg
├── Shift.svg
├── Tine.svg
├── Tsunami.svg
├── X.svg
└── digit.ttf
└── src
├── CharredDesert.cpp
├── CharredDesert.hpp
├── LFO.cpp
├── LFO.hpp
├── controller
├── Biquad.cpp
├── Biquad.h
├── CV.cpp
├── CV.hpp
├── CVSeq.cpp
├── CVSeq.hpp
├── Carbon.cpp
├── Carbon.hpp
├── DTMF.cpp
├── DTMF.hpp
├── Eq.cpp
├── Eq.hpp
├── K.cpp
├── K.hpp
├── M.cpp
├── M.hpp
├── Mixer.cpp
├── Mixer.hpp
├── MixerCV.cpp
├── MixerCV.hpp
├── Noise.cpp
├── Noise.hpp
├── Not.cpp
├── Not.hpp
├── Oscar2.cpp
├── Oscar2.hpp
├── Pan.cpp
├── Pan.hpp
├── Shift.cpp
├── Shift.hpp
├── Tine.cpp
├── Tine.hpp
├── Tsunami.cpp
├── Tsunami.hpp
├── X.cpp
├── X.hpp
├── filters.cpp
└── filters.hpp
├── model
├── Compressor.hpp
├── Delay.hpp
└── MessageBus.hpp
└── view
├── CV.cpp
├── CVSeq.cpp
├── Carbon.cpp
├── DTMF.cpp
├── Eq.cpp
├── K.cpp
├── M.cpp
├── Mixer.cpp
├── MixerCV.cpp
├── Noise.cpp
├── Not.cpp
├── Oscar2.cpp
├── Pan.cpp
├── Shift.cpp
├── Tine.cpp
├── Tsunami.cpp
├── X.cpp
└── components.hpp
/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 | dist
3 | dep
4 | plugin.dylib
5 | plugin.dll
6 | .DS_Store
7 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "deps/SynthDevKit"]
2 | path = deps/SynthDevKit
3 | url = https://github.com/JerrySievert/SynthDevKit.git
4 | [submodule "arptest"]
5 | path = arptest
6 | url = https://github.com/JerrySievert/arptest.git
7 | [submodule "deps/rack-components"]
8 | path = deps/rack-components
9 | url = https://github.com/JerrySievert/rack-components
10 | branch = v1.0
11 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Creative Commons Legal Code
2 |
3 | CC0 1.0 Universal
4 |
5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
12 | HEREUNDER.
13 |
14 | Statement of Purpose
15 |
16 | The laws of most jurisdictions throughout the world automatically confer
17 | exclusive Copyright and Related Rights (defined below) upon the creator
18 | and subsequent owner(s) (each and all, an "owner") of an original work of
19 | authorship and/or a database (each, a "Work").
20 |
21 | Certain owners wish to permanently relinquish those rights to a Work for
22 | the purpose of contributing to a commons of creative, cultural and
23 | scientific works ("Commons") that the public can reliably and without fear
24 | of later claims of infringement build upon, modify, incorporate in other
25 | works, reuse and redistribute as freely as possible in any form whatsoever
26 | and for any purposes, including without limitation commercial purposes.
27 | These owners may contribute to the Commons to promote the ideal of a free
28 | culture and the further production of creative, cultural and scientific
29 | works, or to gain reputation or greater distribution for their Work in
30 | part through the use and efforts of others.
31 |
32 | For these and/or other purposes and motivations, and without any
33 | expectation of additional consideration or compensation, the person
34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she
35 | is an owner of Copyright and Related Rights in the Work, voluntarily
36 | elects to apply CC0 to the Work and publicly distribute the Work under its
37 | terms, with knowledge of his or her Copyright and Related Rights in the
38 | Work and the meaning and intended legal effect of CC0 on those rights.
39 |
40 | 1. Copyright and Related Rights. A Work made available under CC0 may be
41 | protected by copyright and related or neighboring rights ("Copyright and
42 | Related Rights"). Copyright and Related Rights include, but are not
43 | limited to, the following:
44 |
45 | i. the right to reproduce, adapt, distribute, perform, display,
46 | communicate, and translate a Work;
47 | ii. moral rights retained by the original author(s) and/or performer(s);
48 | iii. publicity and privacy rights pertaining to a person's image or
49 | likeness depicted in a Work;
50 | iv. rights protecting against unfair competition in regards to a Work,
51 | subject to the limitations in paragraph 4(a), below;
52 | v. rights protecting the extraction, dissemination, use and reuse of data
53 | in a Work;
54 | vi. database rights (such as those arising under Directive 96/9/EC of the
55 | European Parliament and of the Council of 11 March 1996 on the legal
56 | protection of databases, and under any national implementation
57 | thereof, including any amended or successor version of such
58 | directive); and
59 | vii. other similar, equivalent or corresponding rights throughout the
60 | world based on applicable law or treaty, and any national
61 | implementations thereof.
62 |
63 | 2. Waiver. To the greatest extent permitted by, but not in contravention
64 | of, applicable law, Affirmer hereby overtly, fully, permanently,
65 | irrevocably and unconditionally waives, abandons, and surrenders all of
66 | Affirmer's Copyright and Related Rights and associated claims and causes
67 | of action, whether now known or unknown (including existing as well as
68 | future claims and causes of action), in the Work (i) in all territories
69 | worldwide, (ii) for the maximum duration provided by applicable law or
70 | treaty (including future time extensions), (iii) in any current or future
71 | medium and for any number of copies, and (iv) for any purpose whatsoever,
72 | including without limitation commercial, advertising or promotional
73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
74 | member of the public at large and to the detriment of Affirmer's heirs and
75 | successors, fully intending that such Waiver shall not be subject to
76 | revocation, rescission, cancellation, termination, or any other legal or
77 | equitable action to disrupt the quiet enjoyment of the Work by the public
78 | as contemplated by Affirmer's express Statement of Purpose.
79 |
80 | 3. Public License Fallback. Should any part of the Waiver for any reason
81 | be judged legally invalid or ineffective under applicable law, then the
82 | Waiver shall be preserved to the maximum extent permitted taking into
83 | account Affirmer's express Statement of Purpose. In addition, to the
84 | extent the Waiver is so judged Affirmer hereby grants to each affected
85 | person a royalty-free, non transferable, non sublicensable, non exclusive,
86 | irrevocable and unconditional license to exercise Affirmer's Copyright and
87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the
88 | maximum duration provided by applicable law or treaty (including future
89 | time extensions), (iii) in any current or future medium and for any number
90 | of copies, and (iv) for any purpose whatsoever, including without
91 | limitation commercial, advertising or promotional purposes (the
92 | "License"). The License shall be deemed effective as of the date CC0 was
93 | applied by Affirmer to the Work. Should any part of the License for any
94 | reason be judged legally invalid or ineffective under applicable law, such
95 | partial invalidity or ineffectiveness shall not invalidate the remainder
96 | of the License, and in such case Affirmer hereby affirms that he or she
97 | will not (i) exercise any of his or her remaining Copyright and Related
98 | Rights in the Work or (ii) assert any associated claims and causes of
99 | action with respect to the Work, in either case contrary to Affirmer's
100 | express Statement of Purpose.
101 |
102 | 4. Limitations and Disclaimers.
103 |
104 | a. No trademark or patent rights held by Affirmer are waived, abandoned,
105 | surrendered, licensed or otherwise affected by this document.
106 | b. Affirmer offers the Work as-is and makes no representations or
107 | warranties of any kind concerning the Work, express, implied,
108 | statutory or otherwise, including without limitation warranties of
109 | title, merchantability, fitness for a particular purpose, non
110 | infringement, or the absence of latent or other defects, accuracy, or
111 | the present or absence of errors, whether or not discoverable, all to
112 | the greatest extent permissible under applicable law.
113 | c. Affirmer disclaims responsibility for clearing rights of other persons
114 | that may apply to the Work or any use thereof, including without
115 | limitation any person's Copyright and Related Rights in the Work.
116 | Further, Affirmer disclaims responsibility for obtaining any necessary
117 | consents, permissions or other rights required for any use of the
118 | Work.
119 | d. Affirmer understands and acknowledges that Creative Commons is not a
120 | party to this document and has no duty or obligation with respect to
121 | this CC0 or use of the Work.
122 |
123 |
124 | In addition, the flame SVG used on the module faces has its own license:
125 |
126 | Flame SVG (c) by Matt Hawdon
127 |
128 | Flame SVG is licensed under a
129 | Creative Commons Attribution-NonCommercial 3.0 Unported License.
130 |
131 | You should have received a copy of the license along with this
132 | work. If not, see .
133 |
134 | Drop-shadow code in components.hpp has the following license:
135 |
136 | Copyright (c) 2017-2018, Lindenberg Research / Patrick Lindenberg
137 | All rights reserved.
138 |
139 | Redistribution and use in source and binary forms, with or without
140 | modification, are permitted provided that the following conditions are met:
141 |
142 | * Commercial redistribution of the code, or parts, in any form
143 | must be granted by the author.
144 |
145 | * Redistributions of source code must retain the above copyright notice, this
146 | list of conditions and the following disclaimer.
147 |
148 | * Redistributions in binary form must reproduce the above copyright notice,
149 | this list of conditions and the following disclaimer in the documentation
150 | and/or other materials provided with the distribution.
151 |
152 | * Neither the name of Lindenberg Research nor the names of its
153 | contributors may be used to endorse or promote products derived from
154 | this software without specific prior written permission.
155 |
156 |
157 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
158 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
159 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
160 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
161 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
162 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
163 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
164 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
165 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
166 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
167 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # FLAGS will be passed to both the C and C++ compiler
2 | FLAGS +=
3 | CFLAGS +=
4 | CXXFLAGS +=
5 |
6 | # Careful about linking to libraries, since you can't assume much about the user's environment and library search path.
7 | # Static libraries are fine.
8 | LDFLAGS +=
9 |
10 | # Controllers
11 | CONTROLLERS += $(wildcard src/controller/*.cpp)
12 |
13 | # SynthDevKit
14 | SYNTHDEVKIT += $(wildcard deps/SynthDevKit/src/*.cpp)
15 |
16 | # Views
17 | VIEWS += $(wildcard src/view/*.cpp)
18 |
19 | # Add .cpp and .c files to the build
20 | SOURCES += $(wildcard src/*.cpp) $(CONTROLLERS) $(VIEWS) $(SYNTHDEVKIT)
21 |
22 | # Add files to the ZIP package when running `make dist`
23 | # The compiled plugin is automatically added.
24 | DISTRIBUTABLES += $(wildcard LICENSE*) res deps/rack-components/res
25 |
26 | # Must include the VCV plugin Makefile framework
27 | RACK_DIR ?= ../..
28 | include $(RACK_DIR)/plugin.mk
29 |
30 | # Sources to test for ArpTest - this will usually only include your controllers
31 | TEST_SOURCES += $(CONTROLLERS) $(SYNTHDEVKIT)
32 |
33 | # Add any tests
34 | TEST_SOURCES += $(wildcard tests/*.cpp)
35 |
36 | # By default, ARPTEST_DIR is arptest in your current directory, but you can override it
37 | # In this example, arptest lives one path down
38 | ARPTEST_DIR ?= ./arptest
39 |
40 | include $(ARPTEST_DIR)/build.mk
41 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Charred Desert
2 |
3 | Interesting modules for [VCVRack](https://github.com/vcvrack/rack).
4 |
5 | The name is an homage to [FrozenWasteland](https://github.com/almosteric/FrozenWasteland),
6 | @almosteric's esoteric module set.
7 |
8 | This is a growing work in progress, with only a couple of utility modules currently.
9 | More modules will be added, so if you are interested in following along, either
10 | star the repo, or keep checking back.
11 |
12 | See: https://legitimatesounding.com/sv-modular/charred-desert/index.html for more information.
13 |
14 | ## Building
15 |
16 | ```
17 | $ git clone https://github.com/JerrySievert/CharredDesert.git
18 | $ cd CharredDesert
19 | $ git submodule update --init
20 | $ make
21 | ```
22 |
--------------------------------------------------------------------------------
/manual/CV.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "CV Manual"
3 | author: [SV Modular]
4 | date: "October, 2019"
5 | subject: "Charred Desert"
6 | keywords: [CharredDesert, VCVRack, Manual]
7 | lang: "en"
8 | titlepage: true
9 | logo: "images/SVModular.png"
10 | ...
11 |
12 | # CV Users Manual
13 |
14 | 
15 |
16 | CV provides a constant voltage between `0 volts` and `10 volts` when active.
17 |
18 | CV is divided into two parts, each providing the same functionality, making it
19 | two _Miniature_ modules.
20 |
21 | The `ON` switch will light up when the voltage is active, otherwise the
22 | output will be a constant `0 volts`.
23 |
24 | The `VAL` parameter adjusts the voltage level from `0 volts` to `10 volts`.
25 |
26 | `OUT` provides the output of the module.
27 |
--------------------------------------------------------------------------------
/manual/CV.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/CV.pdf
--------------------------------------------------------------------------------
/manual/CVSeq.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "CV-Seq Manual"
3 | author: [SV Modular]
4 | date: "October, 2019"
5 | subject: "Charred Desert"
6 | keywords: [CharredDesert, VCVRack, Manual]
7 | lang: "en"
8 | titlepage: true
9 | logo: "images/SVModular.png"
10 | ...
11 |
12 | # CV-SEQ
13 |
14 | 
15 |
16 | **CV-SEQ** is a four-step sequencer built for CV output. Unlike note-based
17 | sequencers, which output in the range of `-5`, *CV-SEQ* operates on CV standards,
18 | outputting `0` through `10`. This is helpful in automating CV tasks, allowing
19 | for precision steps driven by a clock. *CV-SEQ* outputs whatever value is set
20 | by the combination of CV and knob for the duration of the _beat_. If any of
21 | these values change, the output will change as well.
22 |
23 | **CV-SEQ** has 5 inputs and 1 output. The primary input is the `CLK`, or clock;
24 | this provides the step time, or _beat_ that triggers changing which value to
25 | output. The `CLK` triggers on a value of `1.7`.
26 |
27 | `OUT` outputs the current step value.
28 |
29 | Each step is controlled by a pair consisting of a CV input and a knob, representing
30 | the values of `0` to `10`. When the CV input is in use, its input is added to
31 | value represented by the knob. Thus, if the CV input is receiving `3.4`, and
32 | the knob is set to `2.1`, the output will be `3.4`, the value of the input and
33 | of the knob together.
34 |
35 | Each triggered value will output its value for the duration of the _beat_, and
36 | the LED will light up for each trigger position.
37 |
--------------------------------------------------------------------------------
/manual/CVSeq.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/CVSeq.pdf
--------------------------------------------------------------------------------
/manual/Carbon.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Carbon Manual"
3 | author: [SV Modular]
4 | date: "October, 2019"
5 | subject: "Charred Desert"
6 | keywords: [CharredDesert, VCVRack, Manual]
7 | lang: "en"
8 | titlepage: true
9 | logo: "images/SVModular.png"
10 | ...
11 |
12 | # Carbon
13 |
14 | 
15 |
16 | **Carbon** is a filter made to emulate a Moog resonance filter. It has frequency
17 | and resonance inputs also controllable by CV.
18 |
19 |
20 | **Carbon** has 3 inputs and 1 output. The primary input is `IN`, which provides
21 | audio to be filtered.
22 |
23 | `OUT` outputs filtered value.
24 |
25 | A frequency is chosen by knob and CV input. Knob values range from `20hz` to `7000hz`,
26 | whereas CV inputs will multiply by `1000` (against `0` to `10` values). These are added
27 | together and clamped to `0` minimum, and `7000` maximum.
28 |
29 | Resonance is controlled by the `REZ` parameter and CV input. The knob provides
30 | values between `0` and `4`, and the CV input scales by dividing by `10`. These
31 | are added for the final value, ranging from `0` to `4`.
32 |
--------------------------------------------------------------------------------
/manual/Carbon.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/Carbon.pdf
--------------------------------------------------------------------------------
/manual/DTMF.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "DTMF Manual"
3 | author: [SV Modular]
4 | date: "October, 2019"
5 | subject: "Charred Desert"
6 | keywords: [CharredDesert, VCVRack, Manual]
7 | lang: "en"
8 | titlepage: true
9 | logo: "images/SVModular.png"
10 | ...
11 |
12 | # DTMF
13 |
14 | 
15 |
16 |
17 | **DTMF** plays dual tone multi-frequency sounds, the tones you hear when you
18 | press the buttons on a phone.
19 |
20 | **DTMF** has 2 inputs: `GATE` and `V/OCT`, and a single `OUT`.
21 |
22 | `GATE` is a CV input that acts as an ON/OFF switch for the tones. When the `GATE`
23 | receives a value of at least `1.7`, it starts playing whichever tone is specified
24 | via the `V/OCT` input.
25 |
26 | There are 16 tones to choose from, each with a different note in octaves 4 and 5:
27 |
28 | |Note|Tone|Note|Tone|Note|Tone|Note|Tone|
29 | |----|----|----|----|----|----|----|----|
30 | |C-4 | 1 |C#-4| 2 |D-4 | 3 |D#-4| A |
31 | |E-4 | 4 |F-4 | 5 |F#-4| 5 |G-4 | B |
32 | |G#-4| 7 |A-4 | 8 |A#-4| 9 |B-4 | C |
33 | |C-5 | * |C#-5| 0 |D-5 | # |D#-5| D |
34 |
--------------------------------------------------------------------------------
/manual/DTMF.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/DTMF.pdf
--------------------------------------------------------------------------------
/manual/Eq.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "EQ Manual"
3 | author: [SV Modular]
4 | date: "October, 2019"
5 | subject: "Charred Desert"
6 | keywords: [CharredDesert, VCVRack, Manual]
7 | lang: "en"
8 | titlepage: true
9 | logo: "images/SVModular.png"
10 | ...
11 |
12 | # EQ
13 |
14 | 
15 |
16 |
17 | **EQ** is a simple 7 function Equalizer, with `FREQ` and `Q`. **EQ** is meant
18 | to act either as a band-based equalizer or as part of a chain of equalizers.
19 |
20 | **EQ** has one `IN` input, and a single `OUT` output. `FREQ` allows for the
21 | selection of frequency, `Q` for the width of the band to be equalized; lower `Q`
22 | means a larger band, whereas higher `Q` is a smaller band of frequencies.
23 |
24 | The `TYPE` allows you to select which type of frequency filter is being used:
25 |
26 | * low pass
27 | * high pass
28 | * band pass
29 | * notch
30 | * low shelf
31 | * high shelf
32 |
--------------------------------------------------------------------------------
/manual/Eq.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/Eq.pdf
--------------------------------------------------------------------------------
/manual/K.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "K Manual"
3 | author: [SV Modular]
4 | date: "October, 2019"
5 | subject: "Charred Desert"
6 | keywords: [CharredDesert, VCVRack, Manual]
7 | lang: "en"
8 | titlepage: true
9 | logo: "images/SVModular.png"
10 | ...
11 |
12 | # K
13 |
14 | 
15 |
16 |
17 | **K** is a simple and small compressor.
18 |
19 | **K** has one `IN` input, and a single `OUT` output. `THR` or Threshold specifies
20 | the voltage threshold for the compressor to kick in.
21 |
22 | `RATIO` specifies the ratio to scale any values above the threshold.
23 |
24 | `ATT` or Attack specifies the attack value in milliseconds to allow for the
25 | compression envelope to activate.
26 |
27 | `REL` or Release specifies the release value in milliseconds to allow for the
28 | compression envelope to activate.
29 |
--------------------------------------------------------------------------------
/manual/K.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/K.pdf
--------------------------------------------------------------------------------
/manual/M.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "M Manual"
3 | author: [SV Modular]
4 | date: "October, 2019"
5 | subject: "Charred Desert"
6 | keywords: [CharredDesert, VCVRack, Manual]
7 | lang: "en"
8 | titlepage: true
9 | logo: "images/SVModular.png"
10 | ...
11 |
12 | # M
13 |
14 | 
15 |
16 |
17 | **M** is a small footprint mixer consisting of two parallel modules.
18 |
19 | Each `IN` input is tied to the half-module, allowing for two inputs to be
20 | mixed.
21 |
22 | `OUT` provides the mixed output, while `MIX` allows for the amount of each
23 | signal to be mixed in.
24 |
--------------------------------------------------------------------------------
/manual/M.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/M.pdf
--------------------------------------------------------------------------------
/manual/Mixer.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Mixer Manual"
3 | author: [SV Modular]
4 | date: "October, 2019"
5 | subject: "Charred Desert"
6 | keywords: [CharredDesert, VCVRack, Manual]
7 | lang: "en"
8 | titlepage: true
9 | logo: "images/SVModular.png"
10 | ...
11 |
12 | # Mixer
13 |
14 | 
15 |
16 |
17 | **Mixer** is an 8 input mixing module.
18 |
19 | Each channel consists of an `INPUT`, `LED` volume indicator, `VOLUME` adjustment,
20 | `PAN` for stereo mixing, `SOLO` for specifying the soloing of a track, and
21 | `MUTE` to mute a track.
22 |
--------------------------------------------------------------------------------
/manual/Mixer.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/Mixer.pdf
--------------------------------------------------------------------------------
/manual/MixerCV.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "MixerCV Manual"
3 | author: [SV Modular]
4 | date: "October, 2019"
5 | subject: "Charred Desert"
6 | keywords: [CharredDesert, VCVRack, Manual]
7 | lang: "en"
8 | titlepage: true
9 | logo: "images/SVModular.png"
10 | ...
11 |
12 | # MixerCV
13 |
14 | 
15 |
16 |
17 | **MixerCV** is an 8 input mixing module with the addition of CV inputs to
18 | automate parameters, as well as Send/Receive functionality on a per-track
19 | basis.
20 |
21 | Each channel consists of an `INPUT`, `LED` volume indicator, `VOLUME` adjustment,
22 | `PAN` for stereo mixing, `SOLO` for specifying the soloing of a track, and
23 | `MUTE` to mute a track.
24 |
25 | In addition, `SEND` allows for each track to send to an external effect module,
26 | along with `RECV` to receive the output. The `MIX` or "wetness" of this effect
27 | can be set with the `MIX` knob, as well as with the `MIX` CV input.
28 |
--------------------------------------------------------------------------------
/manual/MixerCV.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/MixerCV.pdf
--------------------------------------------------------------------------------
/manual/Noise.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Noise Manual"
3 | author: [SV Modular]
4 | date: "October, 2019"
5 | subject: "Charred Desert"
6 | keywords: [CharredDesert, VCVRack, Manual]
7 | lang: "en"
8 | titlepage: true
9 | logo: "images/SVModular.png"
10 | ...
11 |
12 | # Noise
13 |
14 | 
15 |
16 | **NOISE** is a simple noise generator, offering _white_ noise and _pink_ noise.
17 |
18 |
19 | **NOISE** has a single input, `GATE`, which allows noise to be generated. When
20 | `GATE` accepts a signal at or above `1.7`, it generates the selected noise and
21 | outputs via `OUT`.
22 |
23 | Noise can be chosen between _white_ and _pink_.
24 |
--------------------------------------------------------------------------------
/manual/Noise.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/Noise.pdf
--------------------------------------------------------------------------------
/manual/Not.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Not Manual"
3 | author: [SV Modular]
4 | date: "October, 2019"
5 | subject: "Charred Desert"
6 | keywords: [CharredDesert, VCVRack, Manual]
7 | lang: "en"
8 | titlepage: true
9 | logo: "images/SVModular.png"
10 | ...
11 |
12 | # Not
13 |
14 | 
15 |
16 | **NOT** provides inversion functionality, either as a waveform or as CV.
17 |
18 |
19 | **NOT** has one `IN` input, and a single `OUT` output, as well as a type
20 | selection. Types vary the functionality of the module: _CV_ offers a binary
21 | (on/off) output. When the input is `1.7` or above, `1.7` is output, otherwise
22 | `0` is output. When _V/OCT_ is selected, the inverse is output; an input of `3.4`
23 | outputs `-3.4`, and an input of `-1.8` outputs `1.8`.
24 |
--------------------------------------------------------------------------------
/manual/Not.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/Not.pdf
--------------------------------------------------------------------------------
/manual/Oscar2.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Oscar^2 Manual"
3 | author: [SV Modular]
4 | date: "October, 2019"
5 | subject: "Charred Desert"
6 | keywords: [CharredDesert, VCVRack, Manual]
7 | lang: "en"
8 | titlepage: true
9 | logo: "images/SVModular.png"
10 | ...
11 |
12 | # Oscar^2
13 |
14 | 
15 |
16 | **OSCAR^2** is an advanced waveform oscillator. It provides two oscillators
17 | that are combined with each other with adjustable parameters.
18 |
19 | **OSCAR^2** has a single `V/OCT` input for note selection, and an `OUT` port for
20 | the resulting waveform.
21 |
22 | Waveforms can be selected via the `SHAPE` selection. This allows for the waveform
23 | to be selected with a combination of CV and knob. The knob allows for you to
24 | select from:
25 |
26 | * Sine
27 | * Triangle
28 | * Sawtooth
29 | * Square
30 |
31 | When CV is added, its value is added to the selection, so if the CV input is `1.3`,
32 | and the knob has chosen _Sine_, then _Triangle_ is chosen. The result will be
33 | displayed.
34 |
35 | `SHIFT` allows for you to shift the waveforms starting position, from `0` to `10`,
36 | where `0` is not shifted at all, and `10` is shifted 100% to the right. The
37 | `SHIFT` CV value can be added to the knob value.
38 |
39 | **OCTAVE** is a nine octave selection. The knob value provides the octave, and
40 | the CV value can be added to this.
41 |
42 | **FINE** provides for fine tuning.
43 |
44 | **RANDOM** changes the possibility of a waveform drawing being skipped. Instead
45 | of outputing a waveform, `0` is output.
46 |
47 | The resulting output is a combination of the two waveforms, mixed by the `MIX` input.
48 |
--------------------------------------------------------------------------------
/manual/Oscar2.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/Oscar2.pdf
--------------------------------------------------------------------------------
/manual/Pan.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Pan Manual"
3 | author: [SV Modular]
4 | date: "October, 2019"
5 | subject: "Charred Desert"
6 | keywords: [CharredDesert, VCVRack, Manual]
7 | lang: "en"
8 | titlepage: true
9 | logo: "images/SVModular.png"
10 | ...
11 |
12 | # Pan
13 |
14 | 
15 |
16 | **PAN** allows for a single input to pan between two outputs.
17 |
18 | **PAN** takes a value from `IN` and allows a selection via CV and knob to
19 | control which output (or both) to send the value. Each `OUT` output provides
20 | one side of the panned output.
21 |
--------------------------------------------------------------------------------
/manual/Pan.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/Pan.pdf
--------------------------------------------------------------------------------
/manual/Shift.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Shift Manual"
3 | author: [SV Modular]
4 | date: "October, 2019"
5 | subject: "Charred Desert"
6 | keywords: [CharredDesert, VCVRack, Manual]
7 | lang: "en"
8 | titlepage: true
9 | logo: "images/SVModular.png"
10 | ...
11 |
12 | # Shift
13 |
14 | 
15 |
16 | **SHIFT** takes a signal, and shifts it in either a positive or negative direction.
17 | One use for this is to take a waveform oscillator with values between `-5` and `5`,
18 | and turn it into a CV source with values from `0` and `10`.
19 |
20 |
21 | **SHIFT** has an input marked `IN`, an output marked `OUT`, and the ability to
22 | choose the amount to shift via CV and a knob. A choice can be made to either
23 | clip the values between `-5` and `5`, or to output the natural value.
24 |
--------------------------------------------------------------------------------
/manual/Shift.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/Shift.pdf
--------------------------------------------------------------------------------
/manual/Tine.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Tine Manual"
3 | author: [SV Modular]
4 | date: "October, 2019"
5 | subject: "Charred Desert"
6 | keywords: [CharredDesert, VCVRack, Manual]
7 | lang: "en"
8 | titlepage: true
9 | logo: "images/SVModular.png"
10 | ...
11 |
12 | # Tine
13 |
14 | 
15 |
16 | **TINE** takes a signal and a `MOD` or Modulator (or Carrier) and outputs two
17 | signals.
18 |
19 | The `POLARITY` switch changes between bipolar and unipolar for the signal input.
20 |
21 | `SPLIT` determines the voltage level at which to split the signal. When the
22 | modulator voltage is below `SPLIT` value, the signal is routed to the `LOW`
23 | output. When the modulator voltage is greater or equal to the `SPLIT` voltage,
24 | the `AUD` input is sent to the `HIGH` output.
25 |
26 | There are attenuators for both the `HIGH` and `LOW` outputs as well.
27 |
--------------------------------------------------------------------------------
/manual/Tine.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/Tine.pdf
--------------------------------------------------------------------------------
/manual/Tsunami.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Tsunami Manual"
3 | author: [SV Modular]
4 | date: "October, 2019"
5 | subject: "Charred Desert"
6 | keywords: [CharredDesert, VCVRack, Manual]
7 | lang: "en"
8 | titlepage: true
9 | logo: "images/SVModular.png"
10 | ...
11 |
12 | # Tsunami
13 |
14 | 
15 |
16 | **Tsunami** is a wave multiplier. It takes an input via the `IN` jack and
17 | multiplies eight times using an offset set via the *Shift* parameter and CV.
18 |
19 | This offset wave is then added to the mix and the current value of the mix,
20 | from top to bottom, is output in each *OUT* jack.
21 |
22 | In addition, the *POLY OUT* output is a poly output with each channel set to the
23 | unmixed, shifted output.
24 |
--------------------------------------------------------------------------------
/manual/Tsunami.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/Tsunami.pdf
--------------------------------------------------------------------------------
/manual/X.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "X Manual"
3 | author: [SV Modular]
4 | date: "October, 2019"
5 | subject: "Charred Desert"
6 | keywords: [CharredDesert, VCVRack, Manual]
7 | lang: "en"
8 | titlepage: true
9 | logo: "images/SVModular.png"
10 | ...
11 |
12 | # X
13 |
14 | 
15 |
16 | **X** is an audio exciter. It takes an input via the `IN` jack and "excites"
17 | that audio, sending it to the `OUT` jack.
18 |
19 | The `MIX` parameter controls the "wetness" and "dryness" of the signal.
20 |
--------------------------------------------------------------------------------
/manual/X.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/X.pdf
--------------------------------------------------------------------------------
/manual/images/CV-full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/CV-full.png
--------------------------------------------------------------------------------
/manual/images/CV-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/CV-small.png
--------------------------------------------------------------------------------
/manual/images/CV.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/CV.png
--------------------------------------------------------------------------------
/manual/images/CVSeq-full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/CVSeq-full.png
--------------------------------------------------------------------------------
/manual/images/CVSeq-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/CVSeq-small.png
--------------------------------------------------------------------------------
/manual/images/CVSeq.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/CVSeq.png
--------------------------------------------------------------------------------
/manual/images/Carbon-full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Carbon-full.png
--------------------------------------------------------------------------------
/manual/images/Carbon-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Carbon-small.png
--------------------------------------------------------------------------------
/manual/images/Carbon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Carbon.png
--------------------------------------------------------------------------------
/manual/images/DTMF-full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/DTMF-full.png
--------------------------------------------------------------------------------
/manual/images/DTMF-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/DTMF-small.png
--------------------------------------------------------------------------------
/manual/images/DTMF.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/DTMF.png
--------------------------------------------------------------------------------
/manual/images/Eq-full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Eq-full.png
--------------------------------------------------------------------------------
/manual/images/Eq-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Eq-small.png
--------------------------------------------------------------------------------
/manual/images/Eq.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Eq.png
--------------------------------------------------------------------------------
/manual/images/K-full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/K-full.png
--------------------------------------------------------------------------------
/manual/images/K-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/K-small.png
--------------------------------------------------------------------------------
/manual/images/K.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/K.png
--------------------------------------------------------------------------------
/manual/images/M-full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/M-full.png
--------------------------------------------------------------------------------
/manual/images/M-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/M-small.png
--------------------------------------------------------------------------------
/manual/images/M.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/M.png
--------------------------------------------------------------------------------
/manual/images/Mixer-full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Mixer-full.png
--------------------------------------------------------------------------------
/manual/images/Mixer-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Mixer-small.png
--------------------------------------------------------------------------------
/manual/images/Mixer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Mixer.png
--------------------------------------------------------------------------------
/manual/images/MixerCV-full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/MixerCV-full.png
--------------------------------------------------------------------------------
/manual/images/MixerCV-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/MixerCV-small.png
--------------------------------------------------------------------------------
/manual/images/MixerCV.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/MixerCV.png
--------------------------------------------------------------------------------
/manual/images/Noise-full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Noise-full.png
--------------------------------------------------------------------------------
/manual/images/Noise-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Noise-small.png
--------------------------------------------------------------------------------
/manual/images/Noise.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Noise.png
--------------------------------------------------------------------------------
/manual/images/Not-full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Not-full.png
--------------------------------------------------------------------------------
/manual/images/Not-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Not-small.png
--------------------------------------------------------------------------------
/manual/images/Not.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Not.png
--------------------------------------------------------------------------------
/manual/images/Oscar2-full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Oscar2-full.png
--------------------------------------------------------------------------------
/manual/images/Oscar2-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Oscar2-small.png
--------------------------------------------------------------------------------
/manual/images/Oscar2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Oscar2.png
--------------------------------------------------------------------------------
/manual/images/Pan-full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Pan-full.png
--------------------------------------------------------------------------------
/manual/images/Pan-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Pan-small.png
--------------------------------------------------------------------------------
/manual/images/Pan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Pan.png
--------------------------------------------------------------------------------
/manual/images/SVModular.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/SVModular.png
--------------------------------------------------------------------------------
/manual/images/Shift-full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Shift-full.png
--------------------------------------------------------------------------------
/manual/images/Shift-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Shift-small.png
--------------------------------------------------------------------------------
/manual/images/Shift.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Shift.png
--------------------------------------------------------------------------------
/manual/images/Tine-full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Tine-full.png
--------------------------------------------------------------------------------
/manual/images/Tine-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Tine-small.png
--------------------------------------------------------------------------------
/manual/images/Tine.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Tine.png
--------------------------------------------------------------------------------
/manual/images/Tsunami-full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Tsunami-full.png
--------------------------------------------------------------------------------
/manual/images/Tsunami-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Tsunami-small.png
--------------------------------------------------------------------------------
/manual/images/Tsunami.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/Tsunami.png
--------------------------------------------------------------------------------
/manual/images/X-full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/X-full.png
--------------------------------------------------------------------------------
/manual/images/X-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/X-small.png
--------------------------------------------------------------------------------
/manual/images/X.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/manual/images/X.png
--------------------------------------------------------------------------------
/plugin.json:
--------------------------------------------------------------------------------
1 | {
2 | "slug": "CharredDesert",
3 | "name": "CharredDesert",
4 | "version": "1.3.1",
5 | "license": "CC0-1.0",
6 | "author": "Jerry Sievert",
7 | "authorEmail": "contact@svmodular.com",
8 | "authorUrl": "https://svmodular.com/index.html",
9 | "pluginUrl": "https://svmodular.com/plugin/vcv/charred-desert.html",
10 | "manualUrl": "https://svmodular.com/plugin/vcv/charred-desert.html",
11 | "sourceUrl": "https://github.com/SVModular/CharredDesert",
12 | "brand": "SV Modular",
13 | "modules": [
14 | {
15 | "slug": "DTMF",
16 | "name": "DTMF",
17 | "description": "DTMF Tone Generator",
18 | "tags": [
19 | "Envelope generator"
20 | ],
21 | "manualUrl": "https://svmodular.s3-us-west-2.amazonaws.com/manual/charred-desert/DTMF.pdf"
22 | },
23 | {
24 | "slug": "Noise",
25 | "name": "Noise",
26 | "description": "White and Pink Noise Generator",
27 | "tags": [
28 | "Envelope generator",
29 | "Noise"
30 | ],
31 | "manualUrl": "https://svmodular.s3-us-west-2.amazonaws.com/manual/charred-desert/Noise.pdf"
32 | },
33 | {
34 | "slug": "CVSequencer",
35 | "name": "CV Sequencer",
36 | "description": "CV Sequencer",
37 | "tags": [
38 | "Logic",
39 | "Sequencer"
40 | ],
41 | "manualUrl": "https://svmodular.s3-us-west-2.amazonaws.com/manual/charred-desert/CVSeq.pdf"
42 | },
43 | {
44 | "slug": "Not",
45 | "name": "Not",
46 | "description": "CV and Voltage Inverter",
47 | "tags": [
48 | "Logic"
49 | ],
50 | "manualUrl": "https://svmodular.s3-us-west-2.amazonaws.com/manual/charred-desert/Not.pdf"
51 | },
52 | {
53 | "slug": "Pan",
54 | "name": "Pan",
55 | "description": "Stereo Panner",
56 | "tags": [
57 | "Panning"
58 | ],
59 | "manualUrl": "https://svmodular.s3-us-west-2.amazonaws.com/manual/charred-desert/Pan.pdf"
60 | },
61 | {
62 | "slug": "Shift",
63 | "name": "Shift",
64 | "description": "CV and Waveform Shifter/Attenuator",
65 | "tags": [
66 | "Logic",
67 | "Attenuator"
68 | ],
69 | "manualUrl": "https://svmodular.s3-us-west-2.amazonaws.com/manual/charred-desert/Shift.pdf"
70 | },
71 | {
72 | "slug": "Oscar2",
73 | "name": "Oscar^2",
74 | "description": "Oscillator Madness",
75 | "tags": [
76 | "VCO",
77 | "Waveshaper"
78 | ],
79 | "manualUrl": "https://svmodular.s3-us-west-2.amazonaws.com/manual/charred-desert/Oscar2.pdf"
80 | },
81 | {
82 | "slug": "Eq",
83 | "name": "Eq",
84 | "description": "CV Controlled Equalizer",
85 | "tags": [
86 | "Equalizer"
87 | ],
88 | "manualUrl": "https://svmodular.s3-us-west-2.amazonaws.com/manual/charred-desert/Eq.pdf"
89 | },
90 | {
91 | "slug": "Carbon",
92 | "name": "Carbon",
93 | "description": "Moog-like Filter",
94 | "tags": [
95 | "VCF"
96 | ],
97 | "manualUrl": "https://svmodular.s3-us-west-2.amazonaws.com/manual/charred-desert/Carbon.pdf"
98 | },
99 | {
100 | "slug": "Mixer",
101 | "name": "Mixer",
102 | "description": "8 Channel Stereo Mixer",
103 | "tags": [
104 | "Mixer"
105 | ],
106 | "manualUrl": "https://svmodular.s3-us-west-2.amazonaws.com/manual/charred-desert/Mixer.pdf"
107 | },
108 | {
109 | "slug": "MixerCV",
110 | "name": "Mixer CV",
111 | "description": "CV Controlled 8 Channel Mixer",
112 | "tags": [
113 | "Mixer"
114 | ],
115 | "manualUrl": "https://svmodular.s3-us-west-2.amazonaws.com/manual/charred-desert/MixerCV.pdf"
116 | },
117 | {
118 | "slug": "CV",
119 | "name": "CV",
120 | "description": "Simple CV Manipulator",
121 | "tags": [
122 | "Logic"
123 | ],
124 | "manualUrl": "https://svmodular.s3-us-west-2.amazonaws.com/manual/charred-desert/CV.pdf"
125 | },
126 | {
127 | "slug": "M",
128 | "name": "M",
129 | "description": "Mini 2 Channel Mixer",
130 | "tags": [
131 | "Mixer"
132 | ],
133 | "manualUrl": "https://svmodular.s3-us-west-2.amazonaws.com/manual/charred-desert/M.pdf"
134 | },
135 | {
136 | "slug": "Tine",
137 | "name": "Tine",
138 | "description": "Voltage Based Splitter",
139 | "tags": [
140 | "Attenuator",
141 | "Mixer"
142 | ],
143 | "manualUrl": "https://svmodular.s3-us-west-2.amazonaws.com/manual/charred-desert/Tine.pdf"
144 | },
145 | {
146 | "slug": "K",
147 | "name": "K",
148 | "description": "K is for Compressor",
149 | "tags": [
150 | "Attenuator",
151 | "Compressor"
152 | ],
153 | "manualUrl": "https://svmodular.s3-us-west-2.amazonaws.com/manual/charred-desert/K.pdf"
154 | },
155 | {
156 | "slug": "X",
157 | "name": "X",
158 | "description": "Audio Exciter",
159 | "tags": [
160 | "Effect"
161 | ],
162 | "manualUrl": "https://svmodular.s3-us-west-2.amazonaws.com/manual/charred-desert/X.pdf"
163 | },
164 | {
165 | "slug": "Tsunami",
166 | "name": "Tsunami",
167 | "description": "Tsunami Wave Multiplier",
168 | "tags": [
169 | "Effect",
170 | "Delay",
171 | "Waveshaper"
172 | ],
173 | "manualUrl": "https://svmodular.s3-us-west-2.amazonaws.com/manual/charred-desert/Tsunami.pdf"
174 | }
175 | ]
176 | }
177 |
--------------------------------------------------------------------------------
/res/CDSlider.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
57 |
--------------------------------------------------------------------------------
/res/CDSliderHandle.svg:
--------------------------------------------------------------------------------
1 |
2 |
76 |
--------------------------------------------------------------------------------
/res/DTMF.svg:
--------------------------------------------------------------------------------
1 |
2 |
227 |
--------------------------------------------------------------------------------
/res/Knob.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/res/KnobSmall.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/res/Noise.svg:
--------------------------------------------------------------------------------
1 |
2 |
264 |
--------------------------------------------------------------------------------
/res/Not.svg:
--------------------------------------------------------------------------------
1 |
2 |
240 |
--------------------------------------------------------------------------------
/res/Port.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
194 |
--------------------------------------------------------------------------------
/res/digit.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SVModular/CharredDesert/c8d43f7ef698b5c847764c07a446be65337c7d2e/res/digit.ttf
--------------------------------------------------------------------------------
/src/CharredDesert.cpp:
--------------------------------------------------------------------------------
1 | #include "CharredDesert.hpp"
2 |
3 | // The plugin-wide instance of the Plugin class
4 | Plugin *pluginInstance;
5 |
6 | void init(rack::Plugin *p) {
7 | pluginInstance = p;
8 |
9 | p->addModel(modelDTMF);
10 | p->addModel(modelNoise);
11 | p->addModel(modelCVSeq);
12 | p->addModel(modelNot);
13 | p->addModel(modelPan);
14 | p->addModel(modelShift);
15 | p->addModel(modelOscar2);
16 | p->addModel(modelEq);
17 | p->addModel(modelCarbon);
18 | p->addModel(modelMixer);
19 | p->addModel(modelMixerCV);
20 | p->addModel(modelCV);
21 | p->addModel(modelM);
22 | p->addModel(modelTine);
23 | p->addModel(modelK);
24 | p->addModel(modelX);
25 | p->addModel(modelTsunami);
26 | }
27 |
--------------------------------------------------------------------------------
/src/CharredDesert.hpp:
--------------------------------------------------------------------------------
1 | #include "rack.hpp"
2 |
3 | using namespace rack;
4 |
5 | extern Plugin *pluginInstance;
6 |
7 | struct Payload {
8 | float values[8];
9 | };
10 |
11 |
12 | ////////////////////
13 | // module widgets
14 | ////////////////////
15 |
16 | extern Model *modelDTMF;
17 | extern Model *modelNoise;
18 | extern Model *modelNot;
19 | extern Model *modelPan;
20 | extern Model *modelShift;
21 | extern Model *modelCVSeq;
22 | extern Model *modelOscar2;
23 | extern Model *modelEq;
24 | extern Model *modelCarbon;
25 | extern Model *modelMixer;
26 | extern Model *modelMixerCV;
27 | extern Model *modelCV;
28 | extern Model *modelM;
29 | extern Model *modelTine;
30 | extern Model *modelK;
31 | extern Model *modelX;
32 | extern Model *modelTsunami;
33 |
--------------------------------------------------------------------------------
/src/LFO.cpp:
--------------------------------------------------------------------------------
1 | #include "LFO.hpp"
2 |
3 | #include
4 | #include
5 |
6 | LowFrequencyOscillator::LowFrequencyOscillator() {
7 | phase = 0.0;
8 | pw = 0.5;
9 | freq = 1.0;
10 | offset = false;
11 | invert = false;
12 | shift = 0.0;
13 | shiftVal = 0.0;
14 | random = 0.0f;
15 | skip = false;
16 | cv = new SynthDevKit::CV(1.7f);
17 | srand(time(NULL));
18 | }
19 |
20 | void LowFrequencyOscillator::setPitch(float pitch) {
21 | pitch = fminf(pitch, 8.0);
22 | freq = powf(2.0, pitch);
23 | }
24 |
25 | void LowFrequencyOscillator::setFrequency(float frequency) {
26 | freq = frequency;
27 | }
28 |
29 | void LowFrequencyOscillator::setPulseWidth(float pw_) {
30 | const float pwMin = 0.01;
31 | pw = clamp(pw_, pwMin, 1.0f - pwMin);
32 | }
33 |
34 | void LowFrequencyOscillator::setShift(float shift_) {
35 | shift = clamp(shift_, 0.0f, freq);
36 | }
37 |
38 | void LowFrequencyOscillator::setRandom(float random_) {
39 | random = clamp(random_, 0.0f, 10.0f);
40 | }
41 |
42 | void LowFrequencyOscillator::setInvert(bool invert_) {
43 | invert = invert_;
44 | }
45 |
46 | void LowFrequencyOscillator::setReset(float reset) {
47 | cv->update(reset);
48 | if (cv->newTrigger()) {
49 | phase = 0.0;
50 | shiftVal = 0.0;
51 | }
52 | }
53 |
54 | void LowFrequencyOscillator::hardReset() {
55 | phase = 0.0;
56 | shiftVal = 0.0;
57 | }
58 |
59 | void LowFrequencyOscillator::step(float dt) {
60 | if (shiftVal <= shift) {
61 | shiftVal++;
62 | return;
63 | }
64 |
65 | float deltaPhase = fminf(freq * dt, 0.5);
66 | phase += deltaPhase;
67 | if (phase >= 1.0) {
68 | phase -= 1.0;
69 | if (random && random >= (rand() % 50)) {
70 | skip = true;
71 | } else {
72 | skip = false;
73 | }
74 | }
75 | }
76 |
77 | float LowFrequencyOscillator::sin() {
78 | if (skip) {
79 | return 0.0f;
80 | }
81 |
82 | if (offset) {
83 | return 1.0 - cosf(2 * M_PI * phase) * (invert ? -1.0 : 1.0);
84 | } else {
85 | return sinf(2 * M_PI * phase) * (invert ? -1.0 : 1.0);
86 | }
87 | }
88 |
89 | float LowFrequencyOscillator::tri(float x) {
90 | return 4.0 * fabsf(x - roundf(x));
91 | }
92 |
93 | float LowFrequencyOscillator::tri() {
94 | if (skip) {
95 | return 0.0f;
96 | }
97 |
98 | if (offset) {
99 | return tri(invert ? phase - 0.5 : phase);
100 | } else {
101 | return -1.0 + tri(invert ? phase - 0.25 : phase - 0.75);
102 | }
103 | }
104 |
105 | float LowFrequencyOscillator::saw(float x) {
106 | return 2.0 * (x - roundf(x));
107 | }
108 |
109 | float LowFrequencyOscillator::saw() {
110 | if (skip) {
111 | return 0.0f;
112 | }
113 |
114 | if (offset) {
115 | return invert ? 2.0 * (1.0 - phase) : 2.0 * phase;
116 | } else {
117 | return saw(phase) * (invert ? -1.0 : 1.0);
118 | }
119 | }
120 |
121 | float LowFrequencyOscillator::sqr() {
122 | if (skip) {
123 | return 0.0f;
124 | }
125 |
126 | float sqr = (phase < pw) ^ invert ? 1.0 : -1.0;
127 | return offset ? sqr + 1.0 : sqr;
128 | }
129 |
130 | float LowFrequencyOscillator::progress() {
131 | return phase;
132 | }
133 |
--------------------------------------------------------------------------------
/src/LFO.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../deps/SynthDevKit/src/CV.hpp"
4 | #include
5 |
6 | #define clamp(val, min, max) (val < min ? min : (val > max ? max : val))
7 |
8 | class LowFrequencyOscillator {
9 | private:
10 | float phase;
11 | float pw;
12 | float freq;
13 | float shift;
14 | float shiftVal;
15 | float random;
16 | bool skip;
17 | bool offset;
18 | bool invert;
19 | SynthDevKit::CV *cv;
20 |
21 | public:
22 | LowFrequencyOscillator();
23 | void setPitch(float);
24 | void setFrequency(float);
25 | void setPulseWidth(float);
26 | void setShift(float);
27 | void setRandom(float);
28 | void setInvert(bool);
29 | void setReset(float);
30 | void hardReset();
31 | void step(float);
32 | float sin();
33 | float tri(float);
34 | float tri();
35 | float saw(float);
36 | float saw();
37 | float sqr();
38 | float progress();
39 | };
40 |
--------------------------------------------------------------------------------
/src/controller/Biquad.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Biquad.cpp
3 | //
4 | // Created by Nigel Redmon on 11/24/12
5 | // EarLevel Engineering: earlevel.com
6 | // Copyright 2012 Nigel Redmon
7 | //
8 | // For a complete explanation of the Biquad code:
9 | // http://www.earlevel.com/main/2012/11/26/biquad-c-source-code/
10 | //
11 | // License:
12 | //
13 | // This source code is provided as is, without warranty.
14 | // You may copy and distribute verbatim copies of this document.
15 | // You may modify and use this source code to create binary code
16 | // for your own purposes, free or commercial.
17 | //
18 |
19 | #include "Biquad.h"
20 | #include
21 |
22 | Biquad::Biquad() {
23 | type = bq_type_lowpass;
24 | a0 = 1.0;
25 | a1 = a2 = b1 = b2 = 0.0;
26 | Fc = 0.50;
27 | Q = 0.707;
28 | peakGain = 0.0;
29 | z1 = z2 = 0.0;
30 | }
31 |
32 | Biquad::Biquad(int type, double Fc, double Q, double peakGainDB) {
33 | setBiquad(type, Fc, Q, peakGainDB);
34 | z1 = z2 = 0.0;
35 | }
36 |
37 | Biquad::~Biquad() {
38 | }
39 |
40 | void Biquad::setType(int type) {
41 | this->type = type;
42 | }
43 |
44 | void Biquad::setQ(double Q) {
45 | this->Q = Q;
46 | }
47 |
48 | void Biquad::setFc(double Fc) {
49 | this->Fc = Fc;
50 | }
51 |
52 | void Biquad::setPeakGain(double peakGainDB) {
53 | this->peakGain = peakGainDB;
54 | }
55 |
56 | void Biquad::setBiquad(int type, double Fc, double Q, double peakGainDB) {
57 | this->type = type;
58 | this->Q = Q;
59 | this->Fc = Fc;
60 | setPeakGain(peakGainDB);
61 | }
62 |
63 | void Biquad::calcBiquad(void) {
64 | double norm;
65 | double V = pow(10, fabs(peakGain) / 20.0);
66 | double K = tan(M_PI * Fc);
67 | switch (this->type) {
68 | case bq_type_lowpass:
69 | norm = 1 / (1 + K / Q + K * K);
70 | a0 = K * K * norm;
71 | a1 = 2 * a0;
72 | a2 = a0;
73 | b1 = 2 * (K * K - 1) * norm;
74 | b2 = (1 - K / Q + K * K) * norm;
75 | break;
76 |
77 | case bq_type_highpass:
78 | norm = 1 / (1 + K / Q + K * K);
79 | a0 = 1 * norm;
80 | a1 = -2 * a0;
81 | a2 = a0;
82 | b1 = 2 * (K * K - 1) * norm;
83 | b2 = (1 - K / Q + K * K) * norm;
84 | break;
85 |
86 | case bq_type_bandpass:
87 | norm = 1 / (1 + K / Q + K * K);
88 | a0 = K / Q * norm;
89 | a1 = 0;
90 | a2 = -a0;
91 | b1 = 2 * (K * K - 1) * norm;
92 | b2 = (1 - K / Q + K * K) * norm;
93 | break;
94 |
95 | case bq_type_notch:
96 | norm = 1 / (1 + K / Q + K * K);
97 | a0 = (1 + K * K) * norm;
98 | a1 = 2 * (K * K - 1) * norm;
99 | a2 = a0;
100 | b1 = a1;
101 | b2 = (1 - K / Q + K * K) * norm;
102 | break;
103 |
104 | case bq_type_peak:
105 | if (peakGain >= 0) { // boost
106 | norm = 1 / (1 + 1 / Q * K + K * K);
107 | a0 = (1 + V / Q * K + K * K) * norm;
108 | a1 = 2 * (K * K - 1) * norm;
109 | a2 = (1 - V / Q * K + K * K) * norm;
110 | b1 = a1;
111 | b2 = (1 - 1 / Q * K + K * K) * norm;
112 | } else { // cut
113 | norm = 1 / (1 + V / Q * K + K * K);
114 | a0 = (1 + 1 / Q * K + K * K) * norm;
115 | a1 = 2 * (K * K - 1) * norm;
116 | a2 = (1 - 1 / Q * K + K * K) * norm;
117 | b1 = a1;
118 | b2 = (1 - V / Q * K + K * K) * norm;
119 | }
120 | break;
121 | case bq_type_lowshelf:
122 | if (peakGain >= 0) { // boost
123 | norm = 1 / (1 + sqrt(2) * K + K * K);
124 | a0 = (1 + sqrt(2 * V) * K + V * K * K) * norm;
125 | a1 = 2 * (V * K * K - 1) * norm;
126 | a2 = (1 - sqrt(2 * V) * K + V * K * K) * norm;
127 | b1 = 2 * (K * K - 1) * norm;
128 | b2 = (1 - sqrt(2) * K + K * K) * norm;
129 | } else { // cut
130 | norm = 1 / (1 + sqrt(2 * V) * K + V * K * K);
131 | a0 = (1 + sqrt(2) * K + K * K) * norm;
132 | a1 = 2 * (K * K - 1) * norm;
133 | a2 = (1 - sqrt(2) * K + K * K) * norm;
134 | b1 = 2 * (V * K * K - 1) * norm;
135 | b2 = (1 - sqrt(2 * V) * K + V * K * K) * norm;
136 | }
137 | break;
138 | case bq_type_highshelf:
139 | if (peakGain >= 0) { // boost
140 | norm = 1 / (1 + sqrt(2) * K + K * K);
141 | a0 = (V + sqrt(2 * V) * K + K * K) * norm;
142 | a1 = 2 * (K * K - V) * norm;
143 | a2 = (V - sqrt(2 * V) * K + K * K) * norm;
144 | b1 = 2 * (K * K - 1) * norm;
145 | b2 = (1 - sqrt(2) * K + K * K) * norm;
146 | } else { // cut
147 | norm = 1 / (V + sqrt(2 * V) * K + K * K);
148 | a0 = (1 + sqrt(2) * K + K * K) * norm;
149 | a1 = 2 * (K * K - 1) * norm;
150 | a2 = (1 - sqrt(2) * K + K * K) * norm;
151 | b1 = 2 * (K * K - V) * norm;
152 | b2 = (V - sqrt(2 * V) * K + K * K) * norm;
153 | }
154 | break;
155 | }
156 |
157 | return;
158 | }
159 |
--------------------------------------------------------------------------------
/src/controller/Biquad.h:
--------------------------------------------------------------------------------
1 | //
2 | // Biquad.h
3 | //
4 | // Created by Nigel Redmon on 11/24/12
5 | // EarLevel Engineering: earlevel.com
6 | // Copyright 2012 Nigel Redmon
7 | //
8 | // For a complete explanation of the Biquad code:
9 | // http://www.earlevel.com/main/2012/11/26/biquad-c-source-code/
10 | //
11 | // License:
12 | //
13 | // This source code is provided as is, without warranty.
14 | // You may copy and distribute verbatim copies of this document.
15 | // You may modify and use this source code to create binary code
16 | // for your own purposes, free or commercial.
17 | //
18 |
19 | // NOTE: Some local modifications were made that changes some behavior
20 | // from the above article - most notably: calcBiquad() was moved from
21 | // protected to public, and must be called manually on any changes before
22 | // process() is called.
23 |
24 | #ifndef Biquad_h
25 | #define Biquad_h
26 |
27 | enum {
28 | bq_type_lowpass = 0,
29 | bq_type_highpass,
30 | bq_type_bandpass,
31 | bq_type_notch,
32 | bq_type_peak,
33 | bq_type_lowshelf,
34 | bq_type_highshelf
35 | };
36 |
37 | class Biquad {
38 | public:
39 | Biquad();
40 | Biquad(int type, double Fc, double Q, double peakGainDB);
41 | ~Biquad();
42 | void setType(int type);
43 | void setQ(double Q);
44 | void setFc(double Fc);
45 | void setPeakGain(double peakGainDB);
46 | void setBiquad(int type, double Fc, double Q, double peakGain);
47 | float process(float in);
48 | void calcBiquad(void);
49 |
50 | protected:
51 | int type;
52 | double a0, a1, a2, b1, b2;
53 | double Fc, Q, peakGain;
54 | double z1, z2;
55 | };
56 |
57 | inline float Biquad::process(float in) {
58 | double out = in * a0 + z1;
59 | z1 = in * a1 + z2 - b1 * out;
60 | z2 = in * a2 - b2 * out;
61 | return out;
62 | }
63 |
64 | #endif // Biquad_h
65 |
--------------------------------------------------------------------------------
/src/controller/CV.cpp:
--------------------------------------------------------------------------------
1 | #include "CV.hpp"
2 | #include
3 |
4 | CVModule::CVModule() {
5 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
6 | for (int i = 0; i < CV_COUNT; i++) {
7 | on[i] = false;
8 | cv[i] = new SynthDevKit::CV(0.5f);
9 | configParam(CVModule::SWITCH + i, 0.0f, 1.0f, 0.0f);
10 | configParam(CVModule::KNOB + i, 0.0f, 10.0f, 0.0f);
11 | }
12 | }
13 |
14 | void CVModule::process(const ProcessArgs &args) {
15 | for (int i = 0; i < CV_COUNT; i++) {
16 | cv[i]->update(params[SWITCH + i].getValue());
17 | if (cv[i]->newTrigger()) {
18 | on[i] = !on[i];
19 | }
20 |
21 | if (on[i]) {
22 | lights[OUT_LIGHT + i].value = 1.0f;
23 | outputs[OUT + i].setVoltage(params[KNOB + i].getValue());
24 | } else {
25 | lights[OUT_LIGHT + i].value = 0.0f;
26 | outputs[OUT + i].setVoltage(0.0f);
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/controller/CV.hpp:
--------------------------------------------------------------------------------
1 | #include "../CharredDesert.hpp"
2 |
3 | #include "../../deps/SynthDevKit/src/CV.hpp"
4 |
5 | #define CV_COUNT 2
6 |
7 | struct CVModule : Module {
8 | enum ParamIds { KNOB, SWITCH = CV_COUNT, NUM_PARAMS = CV_COUNT * 2 };
9 | enum InputIds { NUM_INPUTS };
10 | enum OutputIds { OUT, NUM_OUTPUTS = CV_COUNT };
11 | enum LightIds { OUT_LIGHT, NUM_LIGHTS = CV_COUNT };
12 |
13 | CVModule();
14 |
15 | void process(const ProcessArgs &args) override;
16 |
17 | bool on[CV_COUNT];
18 |
19 | SynthDevKit::CV *cv[CV_COUNT];
20 |
21 | json_t *dataToJson() override {
22 | json_t *rootJ = json_object();
23 |
24 | json_t *arr = json_array();
25 |
26 | for (int i = 0; i < CV_COUNT; i++) {
27 | json_array_append(arr, json_boolean(on[i]));
28 | }
29 |
30 | json_object_set_new(rootJ, "switches", arr);
31 |
32 | return rootJ;
33 | }
34 |
35 | void dataFromJson(json_t *rootJ) override {
36 | json_t *switchJ = json_object_get(rootJ, "switches");
37 |
38 | if (switchJ && json_is_array(switchJ)) {
39 | for (int i = 0; i < CV_COUNT; i++) {
40 | json_t *v = json_array_get(switchJ, i);
41 | if (v) {
42 | on[i] = json_boolean_value(v);
43 | }
44 | }
45 | }
46 | }
47 |
48 | };
49 |
--------------------------------------------------------------------------------
/src/controller/CVSeq.cpp:
--------------------------------------------------------------------------------
1 | #include "CVSeq.hpp"
2 |
3 | #define ADD_CV(a, b) clamp(a.value + b.value, 0.0f, 10.0f)
4 |
5 | CVSeqModule::CVSeqModule() {
6 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
7 | cv = new SynthDevKit::CV(1.7f);
8 | currentStep = 0;
9 | configParam(CVSeqModule::KNOB1, 0.0f, 10.0f, 0.0f);
10 | configParam(CVSeqModule::KNOB2, 0.0f, 10.0f, 0.0f);
11 | configParam(CVSeqModule::KNOB3, 0.0f, 10.0f, 0.0f);
12 | configParam(CVSeqModule::KNOB4, 0.0f, 10.0f, 0.0f);
13 | }
14 |
15 | void CVSeqModule::process(const ProcessArgs &args) {
16 | float cv_in = inputs[CV_INPUT].getVoltage();
17 | float current = 0.0f;
18 |
19 | cv->update(cv_in);
20 |
21 | if (cv->newTrigger()) {
22 | current = ADD_CV(inputs[currentStep], params[currentStep]);
23 | // current = params[currentStep].getValue();
24 | outputs[CV_OUTPUT].setVoltage(current);
25 | for (int i = 0; i < 4; i++) {
26 | if (i == currentStep) {
27 | lights[i].value = 1.0;
28 | } else {
29 | lights[i].value = 0.0;
30 | }
31 | }
32 | currentStep++;
33 |
34 | if (currentStep == 4) {
35 | currentStep = 0;
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/controller/CVSeq.hpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include "../../deps/SynthDevKit/src/CV.hpp"
4 | #include "../CharredDesert.hpp"
5 |
6 | struct CVSeqModule : Module {
7 | enum ParamIds { KNOB1, KNOB2, KNOB3, KNOB4, NUM_PARAMS };
8 | enum InputIds {
9 | KNOB1_INPUT,
10 | KNOB2_INPUT,
11 | KNOB3_INPUT,
12 | KNOB4_INPUT,
13 | CV_INPUT,
14 | NUM_INPUTS
15 | };
16 | enum OutputIds { CV_OUTPUT, NUM_OUTPUTS };
17 | enum LightIds { LED1, LED2, LED3, LED4, NUM_LIGHTS };
18 |
19 | CVSeqModule();
20 | void process(const ProcessArgs &args) override;
21 |
22 | SynthDevKit::CV *cv;
23 | uint8_t currentStep;
24 | };
25 |
--------------------------------------------------------------------------------
/src/controller/Carbon.cpp:
--------------------------------------------------------------------------------
1 | #include "Carbon.hpp"
2 |
3 | #include
4 |
5 | CarbonModule::CarbonModule() {
6 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
7 | configParam(CarbonModule::FREQ_PARAM, 20.0f, 6000.0f, (6000 - 20) / 2);
8 | configParam(CarbonModule::REZ_PARAM, 0.0f, 4.0f, 2.0f);
9 | frequency = 0.0f;
10 | filter.clear();
11 | }
12 |
13 | void CarbonModule::process(const ProcessArgs &args) {
14 | // update the display no matter what
15 | frequency = clamp((inputs[FREQ_INPUT].isConnected() ? inputs[FREQ_INPUT].getVoltage() * 1000 : 0) + params[FREQ_PARAM].getValue(), 20.0f, 6000.0f);
16 |
17 | if (inputs[AUDIO_INPUT].isConnected() && outputs[AUDIO_OUTPUT].isConnected()) {
18 | float audio_in = inputs[AUDIO_INPUT].getVoltage() / 5.0f;
19 | float res = clamp((inputs[REZ_INPUT].isConnected() ? inputs[REZ_INPUT].getVoltage() / 10 : 0) + params[REZ_PARAM].getValue(), 0.1f, 4.0f);
20 |
21 | filter.setSamplerate(args.sampleRate);
22 | filter.setCoefficients(frequency, res);
23 |
24 | float out = 0.0f;
25 |
26 | filter.process(&audio_in, &out, 1);
27 |
28 | // filter can sometimes get unstable at high frequencies, if it does, reset
29 | if (std::isnan(out)) {
30 | out = 0.0f;
31 | filter.clear();
32 | }
33 | outputs[AUDIO_OUTPUT].setVoltage(5.0f * out);
34 | } else {
35 | outputs[AUDIO_OUTPUT].setVoltage(0.0f);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/controller/Carbon.hpp:
--------------------------------------------------------------------------------
1 | #include "../CharredDesert.hpp"
2 | #include "filters.hpp"
3 |
4 | struct CarbonModule : Module {
5 | enum ParamIds { FREQ_PARAM, REZ_PARAM, NUM_PARAMS };
6 | enum InputIds { FREQ_INPUT, REZ_INPUT, AUDIO_INPUT, NUM_INPUTS };
7 | enum OutputIds { AUDIO_OUTPUT, NUM_OUTPUTS };
8 | enum LightIds { NUM_LIGHTS };
9 |
10 | CarbonModule();
11 | void process(const ProcessArgs &args) override;
12 | MoogFilter filter;
13 | float frequency = 0.0f;
14 | };
15 |
--------------------------------------------------------------------------------
/src/controller/DTMF.cpp:
--------------------------------------------------------------------------------
1 | #include "DTMF.hpp"
2 |
3 | DTMFModule::DTMFModule() {
4 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
5 | cv = new SynthDevKit::CV(1.7f);
6 | dtmf = new SynthDevKit::DTMF(44100);
7 | }
8 |
9 | char DTMFModule::getTone(float current) {
10 | for (int i = 0; i < 16; i++) {
11 | if ((notes[i] - 0.02) <= current && (notes[i] + 0.02) >= current) {
12 | return tones[i];
13 | }
14 | }
15 |
16 | return ' ';
17 | }
18 |
19 | void DTMFModule::process(const ProcessArgs &args) {
20 | float cv_in = inputs[CV_INPUT].getVoltage();
21 | float voct_in = inputs[VOCT_INPUT].getVoltage();
22 |
23 | cv->update(cv_in);
24 |
25 | if (cv->newTrigger()) {
26 | dtmf->reset();
27 | }
28 |
29 | if (cv->isHigh()) {
30 | char tone = getTone(voct_in);
31 | dtmf->setTone(tone);
32 |
33 | outputs[AUDIO_OUTPUT].setVoltage(dtmf->stepValue());
34 |
35 | if (outputs[AUDIO_OUTPUT].value == 0) {
36 | lights[ON_LED].value = 0;
37 | } else {
38 | lights[ON_LED].value = 1;
39 | }
40 | } else {
41 | outputs[AUDIO_OUTPUT].setVoltage(0);
42 | lights[ON_LED].value = 0;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/controller/DTMF.hpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include "../../deps/SynthDevKit/src/CV.hpp"
4 | #include "../../deps/SynthDevKit/src/DTMF.hpp"
5 | #include "../CharredDesert.hpp"
6 |
7 | struct DTMFModule : Module {
8 | enum ParamIds { NUM_PARAMS };
9 | enum InputIds { VOCT_INPUT, CV_INPUT, NUM_INPUTS };
10 | enum OutputIds { AUDIO_OUTPUT, NUM_OUTPUTS };
11 | enum LightIds { ON_LED, NUM_LIGHTS };
12 |
13 | DTMFModule();
14 |
15 | void process(const ProcessArgs &args) override;
16 |
17 | char getTone(float);
18 |
19 | SynthDevKit::CV *cv;
20 | SynthDevKit::DTMF *dtmf;
21 | float notes[16] = {0, 0.08, 0.17, 0.25, 0.33, 0.42, 0.5, 0.58,
22 | 0.67, 0.75, 0.83, 0.92, 1.0, 1.08, 1.17, 1.25};
23 | char tones[16] = {'1', '2', '3', 'A', '4', '5', '6', 'B',
24 | '7', '8', '9', 'C', '*', '0', '#', 'D'};
25 | };
26 |
--------------------------------------------------------------------------------
/src/controller/Eq.cpp:
--------------------------------------------------------------------------------
1 | #include "Eq.hpp"
2 |
3 | EqModule::EqModule() {
4 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
5 | configParam(EqModule::FREQ_PARAM, 30.0f, 14000.0f, 7000.0f);
6 | configParam(EqModule::TYPE_PARAM, 0.0f, 6.0f, 0.0f);
7 | configParam(EqModule::Q_PARAM, 0.1f, 6.0f, 0.1f);
8 | sampleRate = APP->engine->getSampleRate();
9 | filter = new Biquad(bq_type_lowpass, frequency / sampleRate, q, 6);
10 | filter->calcBiquad();
11 | }
12 |
13 | float EqModule::paramValue (uint16_t param, uint16_t input, float low, float high) {
14 | float current = params[param].getValue();
15 |
16 | if (inputs[input].isConnected()) {
17 | // high - low, divided by one tenth input voltage, plus the current value
18 | current += ((inputs[input].getVoltage() / 10) * (high - low));
19 | }
20 |
21 | return clamp(current, low, high);
22 | }
23 |
24 | void EqModule::process(const ProcessArgs &args) {
25 | float audio_in = inputs[AUDIO_INPUT].getVoltage();
26 | float freq_param = paramValue(FREQ_PARAM, FREQ_CV_INPUT, 30, 14000);
27 | uint8_t filter_type = (uint8_t)params[TYPE_PARAM].getValue();
28 | float q_param = paramValue(Q_PARAM, Q_CV_INPUT, 0.1f, 6.0f);
29 |
30 | if (args.sampleRate != sampleRate || filter_type != filterType ||
31 | freq_param != frequency || q_param != q) {
32 | // reset the tracking variables
33 | frequency = freq_param;
34 | q = q_param;
35 | filterType = filter_type;
36 | sampleRate = args.sampleRate;
37 |
38 | filter->setType(filterType);
39 | filter->setQ((double)q);
40 | filter->setFc(frequency / sampleRate);
41 | filter->calcBiquad();
42 | }
43 |
44 | if (outputs[AUDIO_OUTPUT].isConnected()) {
45 | outputs[AUDIO_OUTPUT].setVoltage(5.0f * filter->process(audio_in / 5.0f));
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/controller/Eq.hpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include "../CharredDesert.hpp"
4 | #include "Biquad.h"
5 |
6 | struct EqModule : Module {
7 | enum ParamIds { FREQ_PARAM, TYPE_PARAM, Q_PARAM, NUM_PARAMS };
8 | enum InputIds { AUDIO_INPUT, FREQ_CV_INPUT, Q_CV_INPUT, NUM_INPUTS };
9 | enum OutputIds { AUDIO_OUTPUT, NUM_OUTPUTS };
10 | enum LightIds { NUM_LIGHTS };
11 |
12 | EqModule();
13 | void process(const ProcessArgs &args) override;
14 | float paramValue (uint16_t, uint16_t, float, float);
15 | float frequency = 7000.0f;
16 | float sampleRate;
17 | uint8_t filterType = 0;
18 | float q = 0.1f;
19 | Biquad *filter;
20 | };
21 |
--------------------------------------------------------------------------------
/src/controller/K.cpp:
--------------------------------------------------------------------------------
1 | #include "K.hpp"
2 |
3 | #define VALUE(a) (a.isConnected() ? a.getVoltage() : 0.0f)
4 |
5 | KModule::KModule() {
6 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
7 | configParam(RATIO, 1.0f, 10.0f, 5.0f);
8 | configParam(THRESHOLD, 1.0f, 30.0f, 10.0f);
9 | configParam(ATTACK, 1.0, 100.0, 10.0);
10 | configParam(RELEASE, 1.0, 100.0, 10.0);
11 | }
12 |
13 | void KModule::process(const ProcessArgs &args) {
14 | float r = params[RATIO].getValue();
15 | float t = params[THRESHOLD].getValue();
16 | float at = params[ATTACK].getValue();
17 | float rt = params[RELEASE].getValue();
18 |
19 | if (sampleRate != args.sampleRate || ratio != r || threshold != t || attackTime != at || releaseTime != rt) {
20 | sampleRate = args.sampleRate;
21 | ratio = r;
22 | threshold = t;
23 | attackTime = at;
24 | releaseTime = rt;
25 |
26 | compressor.setCoefficients(attackTime, releaseTime, threshold, ratio, sampleRate);
27 | }
28 |
29 | float in = inputs[IN].getVoltage();
30 | float out = compressor.process(in);
31 | lights[ACTIVE].value = (in == out) ? 0.0f : 1.0f;
32 |
33 | outputs[OUT].setVoltage(out);
34 | }
35 |
--------------------------------------------------------------------------------
/src/controller/K.hpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include "../CharredDesert.hpp"
4 |
5 | #include "../model/Compressor.hpp"
6 |
7 | struct KModule : Module {
8 | enum ParamIds { THRESHOLD, RATIO, ATTACK, RELEASE, NUM_PARAMS };
9 | enum InputIds { IN, NUM_INPUTS };
10 | enum OutputIds { OUT, NUM_OUTPUTS };
11 | enum LightIds { ACTIVE, NUM_LIGHTS };
12 |
13 | KModule();
14 |
15 | void process(const ProcessArgs &args) override;
16 | Compressor compressor;
17 | float sampleRate = 0;
18 | float ratio = 0;
19 | float attackTime = 0;
20 | float releaseTime = 0;
21 | float threshold = 0;
22 | };
23 |
--------------------------------------------------------------------------------
/src/controller/M.cpp:
--------------------------------------------------------------------------------
1 | #include "M.hpp"
2 |
3 | #define VALUE(a) (a.isConnected() ? a.getVoltage() : 0.0f)
4 |
5 | MModule::MModule() {
6 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
7 | for (int i = 0; i < M_COUNT; i++) {
8 | configParam(MModule::KNOB + i, 0.0f, 1.0f, 0.5f);
9 | }
10 | }
11 |
12 | void MModule::process(const ProcessArgs &args) {
13 | for (int i = 0; i < M_COUNT; i++) {
14 | float mix = params[KNOB + i].getValue();
15 |
16 | float out = (1 - mix) * VALUE(inputs[IN1 + i]) + mix * VALUE(inputs[IN2 + i]);
17 |
18 | outputs[OUT + i].setVoltage(out);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/controller/M.hpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include "../CharredDesert.hpp"
4 |
5 | #include "../../deps/SynthDevKit/src/CV.hpp"
6 |
7 | #define M_COUNT 2
8 |
9 | struct MModule : Module {
10 | enum ParamIds { KNOB, NUM_PARAMS = M_COUNT };
11 | enum InputIds { IN1, IN2 = M_COUNT, NUM_INPUTS = M_COUNT * 2 };
12 | enum OutputIds { OUT, NUM_OUTPUTS = M_COUNT };
13 | enum LightIds { NUM_LIGHTS };
14 |
15 | MModule();
16 |
17 | void process(const ProcessArgs &args) override;
18 | };
19 |
--------------------------------------------------------------------------------
/src/controller/Mixer.cpp:
--------------------------------------------------------------------------------
1 | #include "Mixer.hpp"
2 |
3 | MixerModule::MixerModule() {
4 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
5 | for (int i = 0; i < MIXER_CHANNELS; i++) {
6 | channel_led_l[i] = 0.0f;
7 | channel_led_r[i] = 0.0f;
8 | mute[i] = false;
9 | solo[i] = false;
10 |
11 | mute_button[i] = new SynthDevKit::CV(0.5f);
12 | solo_button[i] = new SynthDevKit::CV(0.5f);
13 |
14 | configParam(MixerModule::VOLUME_SLIDER + i, 0.0f, 1.2f, 1.0f);
15 | configParam(MixerModule::PAN_PARAM + i, 0.0f, 1.0f, 0.5f);
16 | configParam(MixerModule::SOLO_PARAM + i, 0.0f, 1.0f, 0.0f);
17 | configParam(MixerModule::MUTE_PARAM + i, 0.0f, 1.0f, 0.0f);
18 | }
19 |
20 | configParam(MixerModule::VOLUME_L_MAIN, 0.0f, 1.2f, 1.0f);
21 | configParam(MixerModule::VOLUME_R_MAIN, 0.0f, 1.2f, 1.0f);
22 | configParam(MixerModule::MUTE_L_PARAM, 0.0f, 1.0f, 0.0f);
23 | configParam(MixerModule::MUTE_R_PARAM, 0.0f, 1.0f, 0.0f);
24 |
25 | master_led_l = 0.0f;
26 | master_led_r = 0.0f;
27 | master_mute_l = false;
28 | master_mute_r = false;
29 |
30 | mute_l = new SynthDevKit::CV(0.5f);
31 | mute_r = new SynthDevKit::CV(0.5f);
32 | }
33 |
34 | void MixerModule::process(const ProcessArgs &args) {
35 | bool has_solo = false;
36 | float output_l[MIXER_CHANNELS];
37 | float output_r[MIXER_CHANNELS];
38 | float master_l = 0.0f;
39 | float master_r = 0.0f;
40 |
41 | master_led_l = 0.0f;
42 | master_led_r = 0.0f;
43 |
44 | // check for solo buttons first and handle the button presses
45 | for (int i = 0; i < MIXER_CHANNELS; i++) {
46 | mute_button[i]->update(params[MUTE_PARAM + i].getValue());
47 | solo_button[i]->update(params[SOLO_PARAM + i].getValue());
48 |
49 | if (solo_button[i]->newTrigger()) {
50 | solo[i] = !solo[i];
51 | }
52 |
53 | if (mute_button[i]->newTrigger()) {
54 | mute[i] = !mute[i];
55 | }
56 |
57 | // if any are solo, there's a solo
58 | if (solo[i]) {
59 | has_solo = true;
60 | }
61 | }
62 |
63 | // iterate through the channels
64 | for (int i = 0; i < MIXER_CHANNELS; i++) {
65 | output_l[i] = 0.0f;
66 | output_r[i] = 0.0f;
67 |
68 | // if the input is not active, or the input is muted
69 | if (!inputs[INPUT + i].isConnected() || mute[i] || (has_solo && !solo[i])) {
70 | // set everything to 0
71 | output_l[i] = 0.0f;
72 | output_r[i] = 0.0f;
73 | } else {
74 | // do the math
75 | float input = inputs[INPUT + i].getVoltage();
76 |
77 | // get the volume slider value
78 | float volume = params[VOLUME_SLIDER + i].getValue();
79 |
80 | input *= volume;
81 |
82 | // figure out the left and right percentages
83 | float pan = params[PAN_PARAM + i].getValue();
84 |
85 | output_l[i] = output_r[i] = input;
86 |
87 | // determine the left/right mixes
88 | if (pan < 0.5f) {
89 | output_r[i] = (2.0f * pan) * output_r[i];
90 | }
91 |
92 | if (pan > 0.5f) {
93 | output_l[i] = (2.0f * (1.0f - pan)) * output_l[i];
94 | }
95 |
96 | // add the output to master
97 | // check if there is a solo, and it's not this one
98 | if (has_solo) {
99 | if (solo[i]) {
100 | master_l += output_l[i];
101 | master_r += output_r[i];
102 | }
103 | } else {
104 | master_l += output_l[i];
105 | master_r += output_r[i];
106 | }
107 | }
108 |
109 | lights[SOLO_LIGHT + i].value = solo[i] ? 1.0f : 0.0f;
110 | lights[MUTE_LIGHT + i].value = mute[i] ? 1.0f : 0.0f;
111 |
112 | // the led's
113 | channel_led_l[i] = fabsf(output_l[i]);
114 | channel_led_r[i] = fabsf(output_r[i]);
115 | }
116 |
117 | // check if muted
118 | mute_l->update(params[MUTE_L_PARAM].getValue());
119 | mute_r->update(params[MUTE_R_PARAM].getValue());
120 |
121 | if (mute_l->newTrigger()) {
122 | master_mute_l = !master_mute_l;
123 | }
124 |
125 | if (mute_r->newTrigger()) {
126 | master_mute_r = !master_mute_r;
127 | }
128 |
129 | if (master_mute_l) {
130 | master_l = 0.0f;
131 | } else {
132 | // apply the left volume
133 | float volume = params[VOLUME_L_MAIN].getValue();
134 | master_l = master_l * volume;
135 | }
136 |
137 | if (master_mute_r) {
138 | master_r = 0.0f;
139 | } else {
140 | // apply the left volume
141 | float volume = params[VOLUME_R_MAIN].getValue();
142 | master_r = master_r * volume;
143 | }
144 |
145 | lights[MUTE_L_MAIN].value = master_mute_l ? 1.0f : 0.0f;
146 | lights[MUTE_R_MAIN].value = master_mute_r ? 1.0f : 0.0f;
147 |
148 | // apply master volume to the led's
149 | master_led_l = fabsf(master_l);
150 | master_led_r = fabsf(master_r);
151 |
152 | // and to the output
153 | outputs[MAIN_L_OUT].setVoltage(master_l);
154 | outputs[MAIN_R_OUT].setVoltage(master_r);
155 | }
156 |
--------------------------------------------------------------------------------
/src/controller/Mixer.hpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include "../CharredDesert.hpp"
4 | #include "../../deps/SynthDevKit/src/CV.hpp"
5 |
6 | #define MIXER_CHANNELS 8
7 |
8 | struct MixerModule : Module {
9 | enum ParamIds {
10 | VOLUME_SLIDER,
11 | PAN_PARAM = MIXER_CHANNELS,
12 | SOLO_PARAM = MIXER_CHANNELS * 2,
13 | MUTE_PARAM = MIXER_CHANNELS * 3,
14 | VOLUME_L_MAIN = MIXER_CHANNELS * 4,
15 | VOLUME_R_MAIN,
16 | MUTE_L_PARAM,
17 | MUTE_R_PARAM,
18 | NUM_PARAMS
19 | };
20 | enum InputIds {
21 | INPUT,
22 | NUM_INPUTS = MIXER_CHANNELS
23 | };
24 | enum OutputIds {
25 | MAIN_L_OUT,
26 | MAIN_R_OUT,
27 | NUM_OUTPUTS
28 | };
29 |
30 | enum LightIds {
31 | SOLO_LIGHT,
32 | MUTE_LIGHT = MIXER_CHANNELS,
33 | MUTE_L_MAIN = MIXER_CHANNELS * 2,
34 | MUTE_R_MAIN,
35 | NUM_LIGHTS
36 | };
37 |
38 | MixerModule();
39 | void process(const ProcessArgs &args) override;
40 |
41 | float channel_led_l[MIXER_CHANNELS];
42 | float channel_led_r[MIXER_CHANNELS];
43 |
44 | float master_led_l = 0.0f;
45 | float master_led_r = 0.0f;
46 |
47 | bool mute[MIXER_CHANNELS];
48 | bool master_mute_l;
49 | bool master_mute_r;
50 |
51 | bool solo[MIXER_CHANNELS];
52 |
53 | SynthDevKit::CV *solo_button[MIXER_CHANNELS];
54 | SynthDevKit::CV *mute_button[MIXER_CHANNELS];
55 | SynthDevKit::CV *mute_l;
56 | SynthDevKit::CV *mute_r;
57 |
58 | json_t *dataToJson() override {
59 | json_t *rootJ = json_object();
60 |
61 | json_t *m = json_array();
62 | json_t *s = json_array();
63 |
64 | for (int i = 0; i < MIXER_CHANNELS; i++) {
65 | json_array_append(m, json_boolean(mute[i]));
66 | json_array_append(s, json_boolean(solo[i]));
67 | }
68 |
69 | json_object_set_new(rootJ, "mute", m);
70 | json_object_set_new(rootJ, "solo", s);
71 | json_object_set_new(rootJ, "mute_l", json_boolean(master_mute_l));
72 | json_object_set_new(rootJ, "mute_r", json_boolean(master_mute_r));
73 |
74 | return rootJ;
75 | }
76 |
77 | void dataFromJson(json_t *rootJ) override {
78 | json_t *m = json_object_get(rootJ, "mute");
79 | json_t *s = json_object_get(rootJ, "solo");
80 |
81 | for (int i = 0; i < MIXER_CHANNELS; i++) {
82 | if (m && json_is_array(m)) {
83 | json_t *m1 = json_array_get(m, i);
84 | if (m1) {
85 | mute[i] = json_boolean_value(m1);
86 | }
87 | }
88 |
89 | if (s && json_is_array(s)) {
90 | json_t *s1 = json_array_get(s, i);
91 | if (s1) {
92 | solo[i] = json_boolean_value(s1);
93 | }
94 | }
95 | }
96 |
97 | json_t *v = json_object_get(rootJ, "mute_l");
98 | if (v) {
99 | master_mute_l = json_boolean_value(v);
100 | }
101 |
102 | v = json_object_get(rootJ, "mute_r");
103 | if (v) {
104 | master_mute_r = json_boolean_value(v);
105 | }
106 | }
107 | };
108 |
--------------------------------------------------------------------------------
/src/controller/MixerCV.cpp:
--------------------------------------------------------------------------------
1 | #include "MixerCV.hpp"
2 |
3 | MixerCVModule::MixerCVModule() {
4 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
5 | for (int i = 0; i < MIXER_CHANNELS; i++) {
6 | channel_led_l[i] = 0.0f;
7 | channel_led_r[i] = 0.0f;
8 | mute[i] = false;
9 | solo[i] = false;
10 |
11 | mute_button[i] = new SynthDevKit::CV(0.5f);
12 | solo_button[i] = new SynthDevKit::CV(0.5f);
13 | channel_solo[i] = new SynthDevKit::CV(1.7f);
14 | channel_mute[i] = new SynthDevKit::CV(1.7f);
15 |
16 | configParam(MixerCVModule::VOLUME_SLIDER + i, 0.0f, 1.2f, 1.0f);
17 | configParam(MixerCVModule::PAN_PARAM + i, 0.0f, 1.0f, 0.5f);
18 | configParam(MixerCVModule::SOLO_PARAM + i, 0.0f, 1.0f, 0.0f);
19 | configParam(MixerCVModule::MUTE_PARAM + i, 0.0f, 1.0f, 0.0f);
20 | configParam(MixerCVModule::MIX_PARAM + i, 0.0f, 1.0f, 0.0f);
21 | }
22 |
23 | configParam(MixerCVModule::VOLUME_L_MAIN, 0.0f, 1.2f, 1.0f);
24 | configParam(MixerCVModule::VOLUME_R_MAIN, 0.0f, 1.2f, 1.0f);
25 | configParam(MixerCVModule::MUTE_L_PARAM, 0.0f, 1.0f, 0.0f);
26 | configParam(MixerCVModule::MUTE_R_PARAM, 0.0f, 1.0f, 0.0f);
27 | configParam(MixerCVModule::MAIN_L_MIX, 0.0f, 1.0f, 0.0f);
28 | configParam(MixerCVModule::MAIN_R_MIX, 0.0f, 1.0f, 0.0f);
29 |
30 | master_led_l = 0.0f;
31 | master_led_r = 0.0f;
32 | master_mute_l = false;
33 | master_mute_r = false;
34 |
35 | mute_l = new SynthDevKit::CV(1.7f);
36 | mute_r = new SynthDevKit::CV(1.7f);
37 | mute_l_param = new SynthDevKit::CV(0.5f);
38 | mute_r_param = new SynthDevKit::CV(0.5f);
39 | }
40 |
41 | void MixerCVModule::process(const ProcessArgs &args) {
42 | bool has_solo = false;
43 | float output_l[MIXER_CHANNELS];
44 | float output_r[MIXER_CHANNELS];
45 | float master_l = 0.0f;
46 | float master_r = 0.0f;
47 |
48 | master_led_l = 0.0f;
49 | master_led_r = 0.0f;
50 |
51 | // check for solo buttons first and handle the button presses and cv
52 | for (int i = 0; i < MIXER_CHANNELS; i++) {
53 | channel_solo[i]->update(inputs[SOLO_CV + i].getVoltage());
54 | channel_mute[i]->update(inputs[MUTE_CV + i].getVoltage());
55 |
56 | mute_button[i]->update(params[MUTE_PARAM + i].getValue());
57 | solo_button[i]->update(params[SOLO_PARAM + i].getValue());
58 |
59 | // buttons first
60 | if (solo_button[i]->newTrigger()) {
61 | solo[i] = !solo[i];
62 | }
63 |
64 | if (mute_button[i]->newTrigger()) {
65 | mute[i] = !mute[i];
66 | }
67 |
68 | // then cv
69 | if (channel_solo[i]->newTrigger()) {
70 | solo[i] = !solo[i];
71 | }
72 |
73 | if (channel_mute[i]->newTrigger()) {
74 | mute[i] = !mute[i];
75 | }
76 |
77 | // if any are solo, there's a solo
78 | if (solo[i]) {
79 | has_solo = true;
80 | }
81 |
82 | lights[SOLO_LIGHT + i].value = solo[i] ? 1.0f : 0.0f;
83 | lights[MUTE_LIGHT + i].value = mute[i] ? 1.0f : 0.0f;
84 | }
85 |
86 | // iterate through the channels
87 | for (int i = 0; i < MIXER_CHANNELS; i++) {
88 | output_l[i] = 0.0f;
89 | output_r[i] = 0.0f;
90 |
91 | // if the input is not active, or the input is muted
92 | if (mute[i] || (has_solo && !solo[i])) {
93 | // set everything to 0
94 | output_l[i] = 0.0f;
95 | output_r[i] = 0.0f;
96 | } else {
97 | // get the input
98 | float input = (inputs[INPUT + i].isConnected() ? inputs[INPUT + i].getVoltage() : 0.0f);
99 |
100 | // send the input if there's something listening
101 | if (outputs[SEND + i].isConnected()) {
102 | outputs[SEND + i].setVoltage(input);
103 | }
104 |
105 | // if there in recv, process it
106 | if (inputs[RECV + i].isConnected()) {
107 | // get the mix
108 | float mix = clamp((inputs[MIX_CV + i].isConnected() ? inputs[MIX_CV + i].getVoltage() / 10 : 0.0f) + params[MIX_PARAM + i].getValue(), 0.0f, 1.0f);
109 |
110 | // calculate the new "input"
111 | input = ((1 - mix) * input) + (mix * inputs[RECV + i].getVoltage());
112 | }
113 |
114 | // get the volume slider value
115 | float volume = params[VOLUME_SLIDER + i].getValue();
116 |
117 | // add any cv value
118 | volume = clamp((inputs[VOLUME_CV + i].isConnected() ? inputs[VOLUME_CV + i].getVoltage() / 10 : 0.0f) + volume, 0.0f, 1.2f);
119 |
120 | input *= volume;
121 |
122 | // figure out the left and right percentages
123 | float pan = params[PAN_PARAM + i].getValue();
124 |
125 | // add any cv value
126 | pan = clamp((inputs[PAN_CV + i].isConnected() ? inputs[PAN_CV + i].getVoltage() : 0) / 10 + pan, 0.0f, 1.0f);
127 |
128 | output_l[i] = output_r[i] = input;
129 |
130 | // determine the left/right mixes
131 | if (pan < 0.5f) {
132 | output_r[i] = (2.0f * pan) * output_r[i];
133 | }
134 |
135 | if (pan > 0.5f) {
136 | output_l[i] = (2.0f * (1.0f - pan)) * output_l[i];
137 | }
138 |
139 | // add the output to master
140 | // check if there is a solo, and it's not this one
141 | if (has_solo) {
142 | if (solo[i]) {
143 | master_l += output_l[i];
144 | master_r += output_r[i];
145 | }
146 | } else {
147 | master_l += output_l[i];
148 | master_r += output_r[i];
149 | }
150 | }
151 |
152 | // the led's
153 | channel_led_l[i] = fabsf(output_l[i]);
154 | channel_led_r[i] = fabsf(output_r[i]);
155 | }
156 |
157 | // check if muted
158 | mute_l_param->update(params[MUTE_L_PARAM].getValue());
159 |
160 | if (mute_l_param->newTrigger()) {
161 | master_mute_l = !master_mute_l;
162 | }
163 |
164 | if (inputs[MAIN_L_MUTE].isConnected()) {
165 | mute_l->update(inputs[MAIN_L_MUTE].getVoltage());
166 |
167 | if (mute_l->newTrigger()) {
168 | master_mute_l = !master_mute_l;
169 | }
170 | }
171 |
172 | if (master_mute_l) {
173 | master_l = 0.0f;
174 | } else {
175 | // if there's something to send to, send
176 | if (outputs[MAIN_L_SEND].isConnected()) {
177 | outputs[MAIN_L_SEND].setVoltage(master_l);
178 | }
179 |
180 | // if there is something to receive, calculate the new value
181 | if (inputs[MAIN_L_RECV].isConnected()) {
182 | // get the mix
183 | float mix = clamp((inputs[MAIN_MIX_L_CV].isConnected() ? inputs[MAIN_MIX_L_CV].getVoltage() : 0.0f) + params[MAIN_L_MIX].getValue(), 0.0f, 1.0f);
184 |
185 | // calculate the new "input"
186 | master_l = ((1 - mix) * master_l) + (mix * inputs[MAIN_L_RECV].getVoltage());
187 | }
188 |
189 | // apply the left volume
190 | float volume = params[VOLUME_L_MAIN].getValue();
191 | master_l = master_l * volume;
192 | }
193 |
194 | mute_r_param->update(params[MUTE_R_PARAM].getValue());
195 |
196 | if (mute_r_param->newTrigger()) {
197 | master_mute_r = !master_mute_r;
198 | }
199 |
200 | if (inputs[MAIN_R_MUTE].isConnected()) {
201 | mute_r->update(inputs[MAIN_R_MUTE].getVoltage());
202 |
203 | if (mute_r->newTrigger()) {
204 | master_mute_r = !master_mute_r;
205 | }
206 | }
207 |
208 | if (master_mute_r) {
209 | master_r = 0.0f;
210 | } else {
211 | // if there's something to send to, send
212 | if (outputs[MAIN_R_SEND].isConnected()) {
213 | outputs[MAIN_R_SEND].setVoltage(master_l);
214 | }
215 |
216 | // if there is something to receive, calculate the new value
217 | if (inputs[MAIN_R_RECV].isConnected()) {
218 | // get the mix
219 | float mix = clamp((inputs[MAIN_MIX_R_CV].isConnected() ? inputs[MAIN_MIX_R_CV].getVoltage() : 0.0f) + params[MAIN_R_MIX].getValue(), 0.0f, 1.0f);
220 |
221 | // calculate the new "input"
222 | master_r = ((1 - mix) * master_r) + (mix * inputs[MAIN_R_RECV].getVoltage());
223 | }
224 |
225 | // apply the left volume
226 | float volume = params[VOLUME_R_MAIN].getValue();
227 | master_r = master_r * volume;
228 | }
229 |
230 |
231 | lights[MUTE_L_MAIN].value = master_mute_l ? 1.0f : 0.0f;
232 | lights[MUTE_R_MAIN].value = master_mute_r ? 1.0f : 0.0f;
233 |
234 | // apply master volume to the led's
235 | master_led_l = fabsf(master_l);
236 | master_led_r = fabsf(master_r);
237 |
238 | // and to the output
239 | outputs[MAIN_L_OUT].setVoltage(master_l);
240 | outputs[MAIN_R_OUT].setVoltage(master_r);
241 | }
242 |
--------------------------------------------------------------------------------
/src/controller/MixerCV.hpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include "../CharredDesert.hpp"
4 | #include "../../deps/SynthDevKit/src/CV.hpp"
5 |
6 | #define MIXER_CHANNELS 8
7 |
8 | struct MixerCVModule : Module {
9 | enum ParamIds {
10 | VOLUME_SLIDER,
11 | PAN_PARAM = MIXER_CHANNELS,
12 | SOLO_PARAM = MIXER_CHANNELS * 2,
13 | MUTE_PARAM = MIXER_CHANNELS * 3,
14 | MIX_PARAM = MIXER_CHANNELS * 4,
15 | VOLUME_L_MAIN = MIXER_CHANNELS * 5,
16 | VOLUME_R_MAIN,
17 | MUTE_L_PARAM,
18 | MUTE_R_PARAM,
19 | MAIN_L_MIX,
20 | MAIN_R_MIX,
21 | NUM_PARAMS
22 | };
23 | enum InputIds {
24 | RECV,
25 | MIX_CV = MIXER_CHANNELS,
26 | MUTE_CV = MIXER_CHANNELS * 2,
27 | SOLO_CV = MIXER_CHANNELS * 3,
28 | PAN_CV = MIXER_CHANNELS * 4,
29 | VOLUME_CV = MIXER_CHANNELS * 5,
30 | INPUT = MIXER_CHANNELS * 6,
31 | MAIN_L_RECV = MIXER_CHANNELS * 7,
32 | MAIN_R_RECV,
33 | MAIN_L_MUTE,
34 | MAIN_R_MUTE,
35 | MAIN_MIX_L_CV,
36 | MAIN_MIX_R_CV,
37 | NUM_INPUTS
38 | };
39 | enum OutputIds {
40 | SEND,
41 | MAIN_L_OUT = MIXER_CHANNELS,
42 | MAIN_R_OUT,
43 | MAIN_L_SEND,
44 | MAIN_R_SEND,
45 | NUM_OUTPUTS
46 | };
47 |
48 | enum LightIds {
49 | SOLO_LIGHT,
50 | MUTE_LIGHT = MIXER_CHANNELS,
51 | MUTE_L_MAIN = MIXER_CHANNELS * 2,
52 | MUTE_R_MAIN,
53 | NUM_LIGHTS
54 | };
55 |
56 | MixerCVModule();
57 | void process(const ProcessArgs &args) override;
58 |
59 | float channel_led_l[MIXER_CHANNELS];
60 | float channel_led_r[MIXER_CHANNELS];
61 |
62 | float master_led_l = 0.0f;
63 | float master_led_r = 0.0f;
64 |
65 | bool mute[MIXER_CHANNELS];
66 | bool master_mute_l;
67 | bool master_mute_r;
68 |
69 | bool solo[MIXER_CHANNELS];
70 |
71 | SynthDevKit::CV *solo_button[MIXER_CHANNELS];
72 | SynthDevKit::CV *mute_button[MIXER_CHANNELS];
73 | SynthDevKit::CV *mute_l;
74 | SynthDevKit::CV *mute_r;
75 | SynthDevKit::CV *mute_l_param;
76 | SynthDevKit::CV *mute_r_param;
77 |
78 | SynthDevKit::CV *channel_mute[MIXER_CHANNELS];
79 | SynthDevKit::CV *channel_solo[MIXER_CHANNELS];
80 |
81 | json_t *dataToJson() override {
82 | json_t *rootJ = json_object();
83 |
84 | json_t *m = json_array();
85 | json_t *s = json_array();
86 |
87 | for (int i = 0; i < MIXER_CHANNELS; i++) {
88 | json_array_append(m, json_boolean(mute[i]));
89 | json_array_append(s, json_boolean(solo[i]));
90 | }
91 |
92 | json_object_set_new(rootJ, "mute", m);
93 | json_object_set_new(rootJ, "solo", s);
94 | json_object_set_new(rootJ, "mute_l", json_boolean(master_mute_l));
95 | json_object_set_new(rootJ, "mute_r", json_boolean(master_mute_r));
96 |
97 | return rootJ;
98 | }
99 |
100 | void dataFromJson(json_t *rootJ) override {
101 | json_t *m = json_object_get(rootJ, "mute");
102 | json_t *s = json_object_get(rootJ, "solo");
103 |
104 | for (int i = 0; i < MIXER_CHANNELS; i++) {
105 | if (m && json_is_array(m)) {
106 | json_t *m1 = json_array_get(m, i);
107 | if (m1) {
108 | mute[i] = json_boolean_value(m1);
109 | }
110 | }
111 |
112 | if (s && json_is_array(s)) {
113 | json_t *s1 = json_array_get(s, i);
114 | if (s1) {
115 | solo[i] = json_boolean_value(s1);
116 | }
117 | }
118 | }
119 |
120 | json_t *v = json_object_get(rootJ, "mute_l");
121 | if (v) {
122 | master_mute_l = json_boolean_value(v);
123 | } else {
124 | master_mute_l = false;
125 | }
126 |
127 | v = json_object_get(rootJ, "mute_r");
128 | if (v) {
129 | master_mute_r = json_boolean_value(v);
130 | } else {
131 | master_mute_l = false;
132 | }
133 | }
134 | };
135 |
--------------------------------------------------------------------------------
/src/controller/Noise.cpp:
--------------------------------------------------------------------------------
1 | #include "Noise.hpp"
2 |
3 | NoiseModule::NoiseModule() {
4 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
5 | wn = new SynthDevKit::WhiteNoise(0);
6 | pn = new SynthDevKit::PinkNoise(0);
7 | cv = new SynthDevKit::CV(1.7f);
8 |
9 | configParam(NoiseModule::NOISE_SWITCH, 0.0, 1.0, 1.0);
10 | }
11 |
12 | void NoiseModule::process(const ProcessArgs &args) {
13 | float cv_in = inputs[CV_INPUT].getVoltage();
14 |
15 | cv->update(cv_in);
16 |
17 | if (cv->isHigh()) {
18 | if (params[NOISE_SWITCH].getValue()) {
19 | outputs[AUDIO_OUTPUT].setVoltage(wn->stepValue());
20 | } else {
21 | outputs[AUDIO_OUTPUT].setVoltage(pn->stepValue());
22 | }
23 |
24 | lights[ON_LED].value = 1;
25 | } else {
26 | outputs[AUDIO_OUTPUT].setVoltage(0);
27 | lights[ON_LED].value = 0;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/controller/Noise.hpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include "../../deps/SynthDevKit/src/CV.hpp"
4 | #include "../../deps/SynthDevKit/src/PinkNoise.hpp"
5 | #include "../../deps/SynthDevKit/src/WhiteNoise.hpp"
6 | #include "../CharredDesert.hpp"
7 |
8 | struct NoiseModule : Module {
9 | enum ParamIds { NOISE_SWITCH, NUM_PARAMS };
10 | enum InputIds { CV_INPUT, NUM_INPUTS };
11 | enum OutputIds { AUDIO_OUTPUT, NUM_OUTPUTS };
12 | enum LightIds { ON_LED, NUM_LIGHTS };
13 |
14 | NoiseModule();
15 |
16 | void process(const ProcessArgs &args) override;
17 |
18 | SynthDevKit::WhiteNoise *wn;
19 | SynthDevKit::PinkNoise *pn;
20 | SynthDevKit::CV *cv;
21 | };
22 |
--------------------------------------------------------------------------------
/src/controller/Not.cpp:
--------------------------------------------------------------------------------
1 | #include "Not.hpp"
2 |
3 | NotModule::NotModule() {
4 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
5 | configParam(NotModule::SWITCH, 0.0, 1.0, 1.0);
6 | }
7 |
8 | void NotModule::process(const ProcessArgs &args) {
9 | float in = inputs[INPUT].getVoltage();
10 |
11 | if (params[SWITCH].getValue() == 0) {
12 | outputs[OUTPUT].setVoltage(-in);
13 | } else {
14 | if (in >= 1.7f) {
15 | outputs[OUTPUT].setVoltage(0);
16 | } else {
17 | outputs[OUTPUT].setVoltage(1.7f);
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/controller/Not.hpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include "../CharredDesert.hpp"
4 |
5 | struct NotModule : Module {
6 | enum ParamIds { SWITCH, NUM_PARAMS };
7 | enum InputIds { INPUT, NUM_INPUTS };
8 | enum OutputIds { OUTPUT, NUM_OUTPUTS };
9 | enum LightIds { NUM_LIGHTS };
10 |
11 | NotModule();
12 |
13 | void process(const ProcessArgs &args) override;
14 | };
15 |
--------------------------------------------------------------------------------
/src/controller/Oscar2.cpp:
--------------------------------------------------------------------------------
1 | #include "Oscar2.hpp"
2 |
3 | static float calculateFrequency(float voltage) {
4 | return 261.626f * powf(2.0f, voltage);
5 | }
6 |
7 | static float calculateMix(float input, float param) {
8 | return clamp((input + param), 0.0f, 10.0f);
9 | }
10 |
11 | static float calculateShift(float frequency, float percent) {
12 | return (frequency * (percent / 100));
13 | }
14 |
15 | static float valueForWave(LowFrequencyOscillator *osc, uint8_t wave) {
16 | if (wave == 0) {
17 | return osc->sin();
18 | } else if (wave == 1) {
19 | return osc->tri();
20 | } else if (wave == 2) {
21 | return osc->saw();
22 | } else if (wave == 3) {
23 | return osc->sqr();
24 | } else {
25 | return 0.0f;
26 | }
27 | }
28 |
29 | Oscar2Module::Oscar2Module() {
30 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
31 | osc1 = new LowFrequencyOscillator;
32 | osc2 = new LowFrequencyOscillator;
33 |
34 | configParam(Oscar2Module::SHAPE_PARAM1, 0.0f, 3.0f, 0.0f);
35 | configParam(Oscar2Module::SHIFT_PARAM1, 0.0f, 10.0f, 0.0f);
36 | configParam(Oscar2Module::OCTAVE_PARAM1, -4.0f, 4.0f, 0.0f);
37 | configParam(Oscar2Module::FINE_PARAM1, -1.0f, 1.0f, 0.0f);
38 | configParam(Oscar2Module::RANDOM_PARAM1, 0.0f, 5.0f, 0.0f);
39 | configParam(Oscar2Module::INVERT_PARAM1, 0.0f, 1.0f, 1.0f);
40 | configParam(Oscar2Module::SHAPE_PARAM2, 0.0f, 3.0f, 0.0f);
41 | configParam(Oscar2Module::SHIFT_PARAM2, 0.0f, 10.0f, 0.0f);
42 | configParam(Oscar2Module::OCTAVE_PARAM2, -4.0f, 4.0f, 0.0f);
43 | configParam(Oscar2Module::FINE_PARAM2, -1.0f, 1.0f, 0.0f);
44 | configParam(Oscar2Module::RANDOM_PARAM2, 0.0f, 5.0f, 0.0f);
45 | configParam(Oscar2Module::INVERT_PARAM2, 0.0f, 1.0f, 1.0f);
46 | configParam(Oscar2Module::MIX_PARAM, 0.0f, 10.0f, 5.0f);
47 |
48 | }
49 |
50 | void Oscar2Module::process(const ProcessArgs &args) {
51 | float freq = inputs[FREQ_INPUT].getVoltage();
52 |
53 | osc1->setInvert(params[INVERT_PARAM1].getValue() ? false : true);
54 | osc2->setInvert(params[INVERT_PARAM2].getValue() ? false : true);
55 |
56 | float w1 = clamp(params[SHAPE_PARAM1].getValue() + inputs[SHAPE_INPUT1].getVoltage(),
57 | 0.0f, 3.0f);
58 | wave1 = (uint8_t)w1;
59 |
60 | float s1 = clamp(params[SHIFT_PARAM1].getValue() + inputs[SHIFT_INPUT1].getVoltage(),
61 | 0.0f, 10.0f) *
62 | 10;
63 |
64 | if (s1 != shift1) {
65 | osc1->setShift(calculateShift(calculateFrequency(freq), s1));
66 | osc1->hardReset();
67 | osc2->hardReset();
68 | shift1 = s1;
69 | }
70 |
71 | float octave1 = clamp(params[OCTAVE_PARAM1].getValue() + inputs[OCTAVE_INPUT1].getVoltage(), -5.0f, 5.0f);
72 | float fine1 = params[FINE_PARAM1].getValue() + (inputs[FINE_INPUT1].getVoltage() / 2);
73 |
74 | float freq1 = clamp(freq + octave1 + fine1, -5.0f, 5.0f);
75 | osc1->setFrequency(calculateFrequency(freq1));
76 |
77 | float rand1 =
78 | clamp(params[RANDOM_PARAM1].getValue() + (inputs[RANDOM_INPUT1].getVoltage() / 2),
79 | 0.0f, 5.0f);
80 | osc1->setRandom(rand1);
81 |
82 | float w2 = clamp(params[SHAPE_PARAM2].getValue() + inputs[SHAPE_INPUT2].getVoltage(),
83 | 0.0f, 3.0f);
84 | wave2 = (uint8_t)w2;
85 |
86 | float s2 = clamp(params[SHIFT_PARAM2].getValue() + inputs[SHIFT_INPUT2].getVoltage(),
87 | 0.0f, 10.0f) *
88 | 10;
89 |
90 | if (s2 != shift2) {
91 | osc2->setShift(calculateShift(calculateFrequency(freq), s1));
92 | osc2->hardReset();
93 | osc1->hardReset();
94 | shift2 = s2;
95 | }
96 |
97 | float octave2 = params[OCTAVE_PARAM2].getValue() + inputs[OCTAVE_INPUT2].getVoltage();
98 | float fine2 = params[FINE_PARAM2].getValue() + (inputs[FINE_INPUT2].getVoltage() / 2);
99 |
100 | float freq2 = clamp(freq + octave2 + fine2, -5.0f, 5.0f);
101 | osc2->setFrequency(calculateFrequency(freq2));
102 |
103 | float rand2 =
104 | clamp(params[RANDOM_PARAM2].getValue() + (inputs[RANDOM_INPUT2].getVoltage() / 2),
105 | 0.0f, 5.0f);
106 | osc2->setRandom(rand2);
107 |
108 | osc1->step(args.sampleTime);
109 | osc2->step(args.sampleTime);
110 |
111 | float left = valueForWave(osc1, wave1);
112 | float right = valueForWave(osc2, wave2);
113 |
114 | float mixValue = calculateMix(inputs[MIX_INPUT].getVoltage(), params[MIX_PARAM].getValue());
115 | float mix = (right * (mixValue / 10)) + (left * (1 - (mixValue / 10)));
116 |
117 | outputs[AUDIO_OUTPUT].setVoltage(5.0f * mix);
118 | }
119 |
--------------------------------------------------------------------------------
/src/controller/Oscar2.hpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include "../CharredDesert.hpp"
4 |
5 | #include "../../deps/SynthDevKit/src/CV.hpp"
6 |
7 | #include "../LFO.hpp"
8 |
9 | struct Oscar2Module : Module {
10 | enum ParamIds {
11 | SHAPE_PARAM1,
12 | SHAPE_PARAM2,
13 | SHIFT_PARAM1,
14 | SHIFT_PARAM2,
15 | OCTAVE_PARAM1,
16 | OCTAVE_PARAM2,
17 | FINE_PARAM1,
18 | FINE_PARAM2,
19 | RANDOM_PARAM1,
20 | RANDOM_PARAM2,
21 | MIX_PARAM,
22 | INVERT_PARAM1,
23 | INVERT_PARAM2,
24 | NUM_PARAMS
25 | };
26 | enum InputIds {
27 | SHAPE_INPUT1,
28 | SHAPE_INPUT2,
29 | SHIFT_INPUT1,
30 | SHIFT_INPUT2,
31 | OCTAVE_INPUT1,
32 | OCTAVE_INPUT2,
33 | FINE_INPUT1,
34 | FINE_INPUT2,
35 | RANDOM_INPUT1,
36 | RANDOM_INPUT2,
37 | MIX_INPUT,
38 | FREQ_INPUT,
39 | NUM_INPUTS
40 | };
41 | enum OutputIds { AUDIO_OUTPUT, NUM_OUTPUTS };
42 | enum LightIds { NUM_LIGHTS };
43 |
44 | Oscar2Module();
45 |
46 | void process(const ProcessArgs &args) override;
47 |
48 | float shift1 = 0.0f;
49 | float shift2 = 0.0f;
50 | uint8_t wave1 = 0;
51 | uint8_t wave2 = 0;
52 | LowFrequencyOscillator *osc1;
53 | LowFrequencyOscillator *osc2;
54 | float value = 0.0f;
55 | };
56 |
--------------------------------------------------------------------------------
/src/controller/Pan.cpp:
--------------------------------------------------------------------------------
1 | #include "Pan.hpp"
2 |
3 | PanModule::PanModule() {
4 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
5 | configParam(PanModule::PAN_PARAM, -5.0f, 5.0f, 0.0f);
6 | }
7 |
8 | #define ADD_CV(a, b) clamp(a.value + b.value, -5.0f, 5.0f)
9 |
10 | void PanModule::process(const ProcessArgs &args) {
11 | float audio_in = inputs[AUDIO_INPUT].getVoltage();
12 | float pan_in = ADD_CV(inputs[PAN_INPUT], params[PAN_PARAM]);
13 |
14 | // figure out the percentages to apply
15 | float apply1 = (clamp(pan_in, -5.0f, 5.0f) + 5.0f) * 10;
16 | float apply2 = 100.0f - apply1;
17 |
18 | outputs[AUDIO_OUTPUT1].setVoltage(((audio_in * apply1) / 100.0f));
19 | outputs[AUDIO_OUTPUT2].setVoltage(((audio_in * apply2) / 100.0f));
20 | }
21 |
--------------------------------------------------------------------------------
/src/controller/Pan.hpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include "../CharredDesert.hpp"
4 |
5 | struct PanModule : Module {
6 | enum ParamIds { PAN_PARAM, NUM_PARAMS };
7 | enum InputIds { AUDIO_INPUT, PAN_INPUT, NUM_INPUTS };
8 | enum OutputIds { AUDIO_OUTPUT1, AUDIO_OUTPUT2, NUM_OUTPUTS };
9 | enum LightIds { NUM_LIGHTS };
10 |
11 | PanModule();
12 | void process(const ProcessArgs &args) override;
13 | };
14 |
--------------------------------------------------------------------------------
/src/controller/Shift.cpp:
--------------------------------------------------------------------------------
1 | #include "Shift.hpp"
2 |
3 | ShiftModule::ShiftModule() {
4 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
5 | configParam(ShiftModule::SWITCH, 0.0f, 1.0f, 0.0f);
6 | configParam(ShiftModule::KNOB, -5.0f, 5.0f, 0.0f);
7 | }
8 |
9 | #define ADD_CV(a, b) (a.value + b.value)
10 |
11 | void ShiftModule::process(const ProcessArgs &args) {
12 | float in = inputs[INPUT].getVoltage();
13 |
14 | float shift = ADD_CV(inputs[SHIFT], params[KNOB]);
15 |
16 | if (params[SWITCH].getValue()) {
17 | outputs[OUTPUT].setVoltage(clamp(in + shift, -5.0f, 5.0f));
18 | } else {
19 | outputs[OUTPUT].setVoltage(in + shift);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/controller/Shift.hpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include "../CharredDesert.hpp"
4 |
5 | struct ShiftModule : Module {
6 | enum ParamIds { SWITCH, KNOB, NUM_PARAMS };
7 | enum InputIds { SHIFT, INPUT, NUM_INPUTS };
8 | enum OutputIds { OUTPUT, NUM_OUTPUTS };
9 | enum LightIds { NUM_LIGHTS };
10 |
11 | ShiftModule();
12 | void process(const ProcessArgs &args) override;
13 | };
14 |
--------------------------------------------------------------------------------
/src/controller/Tine.cpp:
--------------------------------------------------------------------------------
1 | #include "Tine.hpp"
2 |
3 | #define VALUE(a) (a.isConnected() ? a.getVoltage() : 0.0f)
4 |
5 | TineModule::TineModule() {
6 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
7 | configParam(POLARITY, 0, 1, 0);
8 | configParam(SPLIT, 0, 10, 5);
9 | configParam(LOWER_ATT, -10, 10, 0);
10 | configParam(UPPER_ATT, -10, 10, 0);
11 | }
12 |
13 |
14 | float TineModule::paramValue (uint16_t param, uint16_t input, float low, float high) {
15 | float current = params[param].getValue();
16 |
17 | if (inputs[input].isConnected()) {
18 | // high - low, divided by one tenth input voltage, plus the current value
19 | current += ((inputs[input].getVoltage() / 10) * (high - low));
20 | }
21 |
22 | return clamp(current, low, high);
23 | }
24 |
25 |
26 | void TineModule::process(const ProcessArgs &args) {
27 | outputs[LOWER_OUT].setVoltage(0);
28 | outputs[UPPER_OUT].setVoltage(0);
29 | lights[LOWER_LIGHT].value = 0;
30 | lights[UPPER_LIGHT].value = 0;
31 |
32 | if (inputs[MODIFIER_IN].isConnected()) {
33 | bool uni = params[POLARITY].getValue() ? true : false;
34 |
35 | float split = paramValue(SPLIT, SPLIT_CV, 0, 10);
36 | float lower = paramValue(LOWER_ATT, LOWER_ATT_CV, -10, 10);
37 | float upper = paramValue(UPPER_ATT, UPPER_ATT_CV, -10, 10);
38 |
39 | if (!uni) {
40 | split -= 5.0f;
41 | }
42 |
43 | float modifier = inputs[MODIFIER_IN].getVoltage();
44 | float audio = inputs[AUDIO_IN].isConnected() ? inputs[AUDIO_IN].getVoltage() : modifier;
45 |
46 | if (modifier < split) {
47 | outputs[LOWER_OUT].setVoltage(audio + lower);
48 | lights[LOWER_LIGHT].value = 1;
49 | } else {
50 | outputs[UPPER_OUT].setVoltage(audio + upper);
51 | lights[UPPER_LIGHT].value = 1;
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/controller/Tine.hpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "../CharredDesert.hpp"
5 |
6 | struct TineModule : Module {
7 | enum ParamIds {
8 | POLARITY,
9 | SPLIT,
10 | LOWER_ATT,
11 | UPPER_ATT,
12 | NUM_PARAMS
13 | };
14 | enum InputIds {
15 | AUDIO_IN,
16 | MODIFIER_IN,
17 | SPLIT_CV,
18 | LOWER_ATT_CV,
19 | UPPER_ATT_CV,
20 | NUM_INPUTS
21 | };
22 | enum OutputIds {
23 | LOWER_OUT,
24 | UPPER_OUT,
25 | NUM_OUTPUTS
26 | };
27 | enum LightIds {
28 | LOWER_LIGHT,
29 | UPPER_LIGHT,
30 | NUM_LIGHTS
31 | };
32 |
33 | TineModule( );
34 | float paramValue (uint16_t, uint16_t, float, float);
35 |
36 | void process(const ProcessArgs &args) override;
37 |
38 | std::string id;
39 | };
40 |
--------------------------------------------------------------------------------
/src/controller/Tsunami.cpp:
--------------------------------------------------------------------------------
1 | #include "Tsunami.hpp"
2 |
3 |
4 | TsunamiModule::TsunamiModule() {
5 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
6 |
7 | sampleRate = 44100;
8 | for (uint8_t i = 0; i < SHIFT_COUNT; i++) {
9 | configParam(TsunamiModule::LEVEL + i, 0.0f, 1.0f, 0.5f);
10 | configParam(TsunamiModule::SHIFT + i, 0.0f, 1.0f, float(i) / 10.0);
11 |
12 | delay[i].setMax(uint64_t(sampleRate / 10.0));
13 | uint64_t nDelay = uint64_t((sampleRate / 10.0) * (float(i) / 10.0));
14 | delay[i].setDelay(nDelay);
15 | }
16 |
17 | configParam(TsunamiModule::MASTER_LEVEL, 0.0f, 1.0f, 0.5f);
18 |
19 | }
20 |
21 | float TsunamiModule::paramValue (uint16_t param, uint16_t input, float low, float high) {
22 | float current = params[param].getValue();
23 |
24 | if (inputs[input].isConnected()) {
25 | // high - low, divided by one tenth input voltage, plus the current value
26 | current += ((inputs[input].getVoltage() / 10) * (high - low));
27 | }
28 |
29 | return clamp(current, low, high);
30 | }
31 |
32 | void TsunamiModule::process(const ProcessArgs &args) {
33 | if (sampleRate != args.sampleRate) {
34 | sampleRate = args.sampleRate;
35 | for (uint8_t i = 0; i < SHIFT_COUNT; i++) {
36 | delay[i].setMax(uint64_t(sampleRate / 10.0));
37 | }
38 | }
39 |
40 | float in = inputs[MASTER_IN].getVoltage();
41 | float master_level = params[MASTER_LEVEL].getValue();
42 | in *= master_level;
43 |
44 | float mix = 0.0f;
45 | for (int i = 0; i < SHIFT_COUNT; i++) {
46 | float level = params[LEVEL + i].getValue();
47 | float shift = paramValue(SHIFT + i, CV + i, 0.0, 1);
48 | uint64_t nDelay = uint64_t((sampleRate / 10.0) * shift);
49 | if (nDelay != delay[i].delay) {
50 | delay[i].setDelay(nDelay);
51 | }
52 |
53 | float out = delay[i].step(in);
54 |
55 | mix += (out * level);
56 |
57 | outputs[OUT + i].setVoltage(mix);
58 | outputs[POLY_OUT].setVoltage(out * level, i);
59 | }
60 |
61 | outputs[MASTER_OUT].setVoltage(mix);
62 | outputs[POLY_OUT].setChannels(SHIFT_COUNT);
63 | }
64 |
--------------------------------------------------------------------------------
/src/controller/Tsunami.hpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include "../CharredDesert.hpp"
4 |
5 | #include "../model/Delay.hpp"
6 |
7 | #define SHIFT_COUNT 8
8 |
9 | struct TsunamiModule : Module {
10 | enum ParamIds { LEVEL, SHIFT = LEVEL + SHIFT_COUNT, MASTER_LEVEL = SHIFT + SHIFT_COUNT, NUM_PARAMS };
11 | enum InputIds { CV, IN = CV + SHIFT_COUNT, MASTER_IN = IN + SHIFT_COUNT, NUM_INPUTS };
12 | enum OutputIds { OUT, POLY_OUT = OUT + SHIFT_COUNT, MASTER_OUT, NUM_OUTPUTS };
13 | enum LightIds { NUM_LIGHTS };
14 |
15 | TsunamiModule();
16 |
17 | void process(const ProcessArgs &args) override;
18 | float paramValue (uint16_t, uint16_t, float, float);
19 |
20 | float sampleRate;
21 |
22 | Delay delay[SHIFT_COUNT];
23 | };
24 |
--------------------------------------------------------------------------------
/src/controller/X.cpp:
--------------------------------------------------------------------------------
1 | #include "X.hpp"
2 |
3 | #include
4 |
5 | #define VALUE(a) (a.isConnected() ? a.getVoltage() : 0.0f)
6 |
7 | XModule::XModule() {
8 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
9 | for (int i = 0; i < X_COUNT; i++) {
10 | configParam(XModule::KNOB + i, 0.0f, 1.0f, 0.5f);
11 | }
12 | }
13 |
14 | float XModule::paramValue (uint16_t param, uint16_t input, float low, float high) {
15 | float current = params[param].getValue();
16 |
17 | if (inputs[input].isConnected()) {
18 | // high - low, divided by one tenth input voltage, plus the current value
19 | current += ((inputs[input].getVoltage() / 10) * (high - low));
20 | }
21 |
22 | return clamp(current, low, high);
23 | }
24 |
25 |
26 | void XModule::process(const ProcessArgs &args) {
27 | for (int i = 0; i < X_COUNT; i++) {
28 | float mix = paramValue(KNOB + i, MIX + i, 0, 1);
29 | float x = inputs[IN + i].getVoltage();
30 |
31 | float out = (1 - mix) * VALUE(inputs[IN + i]) + mix * tanh(x);
32 |
33 | outputs[OUT + i].setVoltage(out);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/controller/X.hpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include "../CharredDesert.hpp"
4 |
5 | #include "../../deps/SynthDevKit/src/CV.hpp"
6 |
7 | #define X_COUNT 2
8 |
9 | struct XModule : Module {
10 | enum ParamIds { KNOB, NUM_PARAMS = X_COUNT };
11 | enum InputIds { IN, MIX = X_COUNT, NUM_INPUTS = X_COUNT * 2 };
12 | enum OutputIds { OUT, NUM_OUTPUTS = X_COUNT };
13 | enum LightIds { NUM_LIGHTS };
14 |
15 | XModule();
16 | float paramValue (uint16_t, uint16_t, float, float);
17 | void process(const ProcessArgs &args) override;
18 | };
19 |
--------------------------------------------------------------------------------
/src/controller/filters.cpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include "filters.hpp"
4 |
5 | void MoogFilter::clear() {
6 | f = 0;
7 | pc = 0;
8 | q = 0;
9 | bf0 = 0;
10 | bf1 = 0;
11 | bf2 = 0;
12 | bf3 = 0;
13 | bf4 = 0;
14 | t1 = 0;
15 | t2 = 0;
16 | }
17 |
18 | void MoogFilter::setCoefficients(float f_, float r_) {
19 | float frequency = f_ / (0.5 * sample_rate);
20 | float resonance = r_;
21 |
22 | if (frequency < 0)
23 | frequency = 0;
24 | if (frequency > 0.6)
25 | frequency = 0.6;
26 |
27 | q = 1.0f - frequency;
28 | pc = frequency + 0.8f * frequency * q;
29 | f = pc + pc - 1.0f;
30 | q = resonance * (1.0f + 0.5f * q * (1.0f - q + 5.6f * q * q));
31 | }
32 |
33 | void MoogFilter::process(float *input, float *output, int samples) {
34 | for (uint16_t i = 0; i < samples; i++) {
35 | float in = input[i];
36 | in -= q * bf4; // feedback
37 | t1 = bf1;
38 | bf1 = (in + bf0) * pc - bf1 * f;
39 | t2 = bf2;
40 | bf2 = (bf1 + t1) * pc - bf2 * f;
41 | t1 = bf3;
42 | bf3 = (bf2 + t2) * pc - bf3 * f;
43 | bf4 = (bf3 + t1) * pc - bf4 * f;
44 | bf4 = bf4 - bf4 * bf4 * bf4 * 0.166667f; // clipping
45 | bf0 = in;
46 |
47 | // Lowpass output: bf4
48 | // Highpass output: in - bf4;
49 | // Bandpass output: 3.0f * (bf3 - bf4);
50 | output[i] = bf4;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/controller/filters.hpp:
--------------------------------------------------------------------------------
1 | class MoogFilter {
2 | public:
3 | void clear();
4 | void setType(int t) {
5 | type = t;
6 | }
7 | void setSamplerate(float r) {
8 | sample_rate = r;
9 | }
10 | void setCoefficients(float f, float r);
11 | void process(float *input, float *output, int samples);
12 |
13 | private:
14 | float f, pc, q;
15 | float bf0, bf1, bf2, bf3, bf4;
16 | float t1, t2;
17 |
18 | int type = 0;
19 | float sample_rate;
20 | };
21 |
--------------------------------------------------------------------------------
/src/model/Compressor.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | struct EnvelopeDetection {
6 | EnvelopeDetection () {
7 | prev = 0;
8 | }
9 |
10 | void setCoefficients(float atttackTime, float releaseTime, float sampleRate) {
11 | this->sampleRate = sampleRate;
12 | this->attackTime = exp(-1 / ((sampleRate / 1000) * attackTime));
13 | this->releaseTime = exp(-1 / ((sampleRate / 1000) * releaseTime));
14 | }
15 |
16 | float process(float input) {
17 | input = fabs(input);
18 |
19 | if (input > prev) {
20 | prev *= attackTime;
21 | prev += (1 - attackTime) * input;
22 | } else{
23 | prev *= releaseTime;
24 | prev += (1 - releaseTime) * input;
25 | }
26 |
27 | #if 0
28 | prev = simd::ifelse(input > prev,
29 | (prev * attackTime) + ((1 - attackTime) * input),
30 | (prev * releaseTime) + ((1 - releaseTime) * input));
31 | #endif
32 | return prev;
33 | }
34 |
35 | float attackTime;
36 | float releaseTime;
37 | float sampleRate;
38 | float prev;
39 | };
40 |
41 |
42 | struct Compressor {
43 | void setRatio(float ratio) {
44 | this->ratio = 1.0 / ratio;
45 | }
46 | void setThreshold(float threshold) {
47 | this->threshold = threshold;
48 | }
49 |
50 | void setCoefficients(float attackTime, float releaseTime, float threshold, float ratio, float sampleRate) {
51 | this->envelope.setCoefficients(attackTime, releaseTime, sampleRate);
52 | this->ratio = 1.0 / ratio;
53 | this->threshold = threshold;
54 | }
55 |
56 | float process(float input) {
57 | float level = envelope.process(input);
58 |
59 | if (level <= threshold) {
60 | return input;
61 | } else {
62 | //Calculate new level after compression then apply to input
63 | float compLevel = ratio * (level - threshold) + threshold;
64 |
65 | return input * compLevel / level;
66 | }
67 | }
68 |
69 | EnvelopeDetection envelope;
70 | float ratio;
71 | float threshold;
72 | };
73 |
--------------------------------------------------------------------------------
/src/model/Delay.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | template struct Delay {
6 | Delay(uint64_t length = 65536) {
7 | buffer = new T[length];
8 | delay = 0;
9 | inPoint = 0;
10 | outPoint = 0;
11 | this->length = length;
12 |
13 | clear();
14 | }
15 |
16 | ~Delay( ) {
17 | delete buffer;
18 | }
19 |
20 | void setDelay(uint64_t delay) {
21 | if (inPoint >= delay) {
22 | outPoint = inPoint - delay;
23 | } else {
24 | outPoint = length + inPoint - delay;
25 | }
26 |
27 | this->delay = delay;
28 | }
29 |
30 | void setMax(uint64_t delay) {
31 | if (delay > length) {
32 | delete buffer;
33 | buffer = new T[delay];
34 |
35 | for (uint64_t i = 0; i < delay; i++) {
36 | buffer[i] = 0;
37 | }
38 | }
39 |
40 | length = delay;
41 | }
42 |
43 | void clear( ) {
44 | for (uint64_t i = 0; i < length; i++) {
45 | buffer[i] = 0;
46 | }
47 |
48 | last = 0.0;
49 | }
50 |
51 | T nextOut( ) {
52 | return buffer[outPoint];
53 | }
54 |
55 | T step(T in) {
56 | buffer[inPoint] = in;
57 | inPoint++;
58 |
59 | if (inPoint == length) {
60 | inPoint = 0;
61 | }
62 |
63 | last = buffer[outPoint];
64 | outPoint++;
65 |
66 | if (outPoint == length) {
67 | outPoint = 0;
68 | }
69 |
70 | return last;
71 | }
72 |
73 | T *buffer;
74 | uint64_t length;
75 | uint64_t delay;
76 | uint64_t inPoint;
77 | uint64_t outPoint;
78 | T last;
79 | };
80 |
--------------------------------------------------------------------------------
/src/model/MessageBus.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | template
10 | class Message {
11 | public:
12 | std::string id;
13 | T value;
14 | };
15 |
16 | template
17 | class MessageBus {
18 | public:
19 | MessageBus() { }
20 |
21 | void send(std::string id, Message *message) {
22 | std::lock_guard lock(memberMutex);
23 |
24 | if (members[id]) {
25 | if (memberData[id]) {
26 | Message *old = memberData[id];
27 | memberData.erase(id);
28 |
29 | delete old;
30 | }
31 |
32 | memberData[id] = message;
33 | } else {
34 | fprintf(stderr, "Unknown member sent data to the MessageBus: %s\n", id.c_str());
35 | }
36 | }
37 |
38 | std::vector *currentValues() {
39 | std::vector *data = new std::vector();
40 |
41 | std::lock_guard lock(memberMutex);
42 |
43 | for (const auto &e : memberData) {
44 | Message *message = (Message *) e.second;
45 | data->push_back(message->value);
46 | }
47 |
48 | return data;
49 | }
50 |
51 | std::string registerMember() {
52 | std::lock_guard lock(memberMutex);
53 |
54 | std::string id = std::to_string(busId);
55 | busId++;
56 |
57 | members[id] = true;
58 |
59 | return id;
60 | }
61 |
62 | void deregisterMember(std::string id) {
63 | std::lock_guard lock(memberMutex);
64 |
65 | if (members[id]) {
66 | if (memberData[id]) {
67 | Message *old = memberData[id];
68 | delete old;
69 | memberData.erase(id);
70 | }
71 |
72 | members.erase(id);
73 | } else {
74 | fprintf(stderr, "Unknown member tried to deregister: %s\n", id.c_str());
75 | }
76 | }
77 |
78 | private:
79 | std::mutex memberMutex;
80 | std::unordered_map *> memberData;
81 | std::unordered_map members;
82 | // this would be better handled with a uuid
83 | uint64_t busId = 0;
84 | };
85 |
--------------------------------------------------------------------------------
/src/view/CV.cpp:
--------------------------------------------------------------------------------
1 | #include "../controller/CV.hpp"
2 |
3 | #include "components.hpp"
4 |
5 | struct CVWidget : ModuleWidget {
6 | CVWidget(CVModule *module);
7 | };
8 |
9 | CVWidget::CVWidget(CVModule *module) {
10 | setModule(module);
11 | box.size = Vec(2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
12 |
13 | setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/CV.svg")));
14 |
15 |
16 | for (int i = 0; i < CV_COUNT; i++) {
17 | addParam(createParam(Vec(4, 35 + (190 * i)), module,
18 | CVModule::SWITCH + i));
19 |
20 | addChild(createLight>(
21 | Vec(5.2, 37 + (190 * i)), module, CVModule::OUT_LIGHT + i));
22 |
23 | addParam(createParam(Vec(5, 85 + (190 * i)), module, CVModule::KNOB + i));
24 |
25 | addOutput(createOutput(Vec(3, 140 + (190 * i)), module,
26 | CVModule::OUT + i));
27 | }
28 | }
29 |
30 | Model *modelCV = createModel("CV");
31 |
--------------------------------------------------------------------------------
/src/view/CVSeq.cpp:
--------------------------------------------------------------------------------
1 | #include "../controller/CVSeq.hpp"
2 |
3 | #include "components.hpp"
4 |
5 | struct CVSeqWidget : ModuleWidget {
6 | CVSeqWidget(CVSeqModule *module);
7 | };
8 |
9 | CVSeqWidget::CVSeqWidget(CVSeqModule *module) {
10 | setModule(module);
11 | box.size = Vec(4 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
12 |
13 | setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/CVSeq.svg")));
14 |
15 |
16 | addInput(createInput(Vec(4, 35), module,
17 | CVSeqModule::CV_INPUT));
18 | addOutput(createOutput(Vec(32, 35), module,
19 | CVSeqModule::CV_OUTPUT));
20 |
21 | addInput(createInput(Vec(4, 85), module,
22 | CVSeqModule::KNOB1_INPUT));
23 | addParam(createParam(
24 | Vec(28.5, 79.5), module, CVSeqModule::KNOB1));
25 |
26 | addInput(createInput(Vec(4, 135), module,
27 | CVSeqModule::KNOB2_INPUT));
28 | addParam(createParam(
29 | Vec(28.5, 129.5), module, CVSeqModule::KNOB2));
30 |
31 | addInput(createInput(Vec(4, 185), module,
32 | CVSeqModule::KNOB3_INPUT));
33 | addParam(createParam(
34 | Vec(28.5, 179.5), module, CVSeqModule::KNOB3));
35 |
36 | addInput(createInput(Vec(4, 235), module,
37 | CVSeqModule::KNOB4_INPUT));
38 | addParam(createParam(
39 | Vec(28.5, 229.5), module, CVSeqModule::KNOB4));
40 |
41 | addChild(createLight>(
42 | Vec(36, 109), module, CVSeqModule::LED1));
43 | addChild(createLight>(
44 | Vec(36, 159), module, CVSeqModule::LED2));
45 | addChild(createLight>(
46 | Vec(36, 209), module, CVSeqModule::LED3));
47 | addChild(createLight>(
48 | Vec(36, 259), module, CVSeqModule::LED4));
49 | }
50 |
51 | Model *modelCVSeq = createModel("CVSequencer");
52 |
--------------------------------------------------------------------------------
/src/view/Carbon.cpp:
--------------------------------------------------------------------------------
1 | #include "../controller/Carbon.hpp"
2 |
3 | #include "components.hpp"
4 |
5 | struct CarbonWidget : ModuleWidget {
6 | CarbonWidget(CarbonModule *module);
7 | };
8 |
9 | CarbonWidget::CarbonWidget(CarbonModule *module) {
10 | setModule(module);
11 | box.size = Vec(4 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
12 |
13 | setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Carbon.svg")));
14 |
15 |
16 | {
17 | FrequencyDisplay *frequency = new FrequencyDisplay();
18 | if (module) {
19 | frequency->value = &module->frequency;
20 | }
21 | frequency->box.pos = Vec(5.5, 46);
22 | frequency->box.size = Vec(40, 18);
23 | addChild(frequency);
24 | }
25 |
26 | addInput(createInput(Vec(17.5, 35), module,
27 | CarbonModule::AUDIO_INPUT));
28 |
29 | addParam(createParam(
30 | Vec(28.5, 104.5), module, CarbonModule::FREQ_PARAM));
31 |
32 | addInput(createInput(Vec(4, 110), module,
33 | CarbonModule::FREQ_INPUT));
34 |
35 | addParam(createParam(
36 | Vec(28.5, 154.5), module, CarbonModule::REZ_PARAM));
37 |
38 | addInput(createInput(Vec(4, 160), module,
39 | CarbonModule::REZ_INPUT));
40 |
41 | addOutput(createOutput(Vec(17.5, 210), module, CarbonModule::AUDIO_OUTPUT));
42 | }
43 |
44 | Model *modelCarbon = createModel("Carbon");
45 |
--------------------------------------------------------------------------------
/src/view/DTMF.cpp:
--------------------------------------------------------------------------------
1 | #include "../controller/DTMF.hpp"
2 |
3 | #include "components.hpp"
4 |
5 | struct DTMFWidget : ModuleWidget {
6 | DTMFWidget(DTMFModule *module);
7 | };
8 |
9 | DTMFWidget::DTMFWidget(DTMFModule *module) {
10 | setModule(module);
11 | box.size = Vec(3 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
12 |
13 | setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/DTMF.svg")));
14 |
15 |
16 | addInput(createInput(Vec(10, 35), module,
17 | DTMFModule::CV_INPUT));
18 | addInput(createInput(Vec(10, 85), module,
19 | DTMFModule::VOCT_INPUT));
20 | addOutput(createOutput(Vec(10, 135), module,
21 | DTMFModule::AUDIO_OUTPUT));
22 | addChild(createLight>(
23 | Vec(18, 209), module, DTMFModule::ON_LED));
24 | }
25 |
26 | Model *modelDTMF = createModel("DTMF");
27 |
--------------------------------------------------------------------------------
/src/view/Eq.cpp:
--------------------------------------------------------------------------------
1 | #include "../controller/Eq.hpp"
2 |
3 | #include "components.hpp"
4 |
5 | struct EqWidget : ModuleWidget {
6 | EqWidget(EqModule *module);
7 | };
8 |
9 | EqWidget::EqWidget(EqModule *module) {
10 | setModule(module);
11 | box.size = Vec(4 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
12 |
13 | setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Eq.svg")));
14 |
15 | {
16 | FrequencyDisplay *frequency = new FrequencyDisplay();
17 | if (module) {
18 | frequency->value = &module->frequency;
19 | }
20 | frequency->box.pos = Vec(5.5, 46);
21 | frequency->box.size = Vec(40, 18);
22 | addChild(frequency);
23 | }
24 |
25 | {
26 | EqTypeDisplay *type = new EqTypeDisplay();
27 | if (module) {
28 | type->value = &module->filterType;
29 | }
30 | type->box.pos = Vec(5.5, 81);
31 | type->box.size = Vec(40, 18);
32 | addChild(type);
33 | }
34 |
35 |
36 | addParam(createParam(
37 | Vec(28.5, 104.5), module, EqModule::FREQ_PARAM));
38 |
39 | addInput(createInput(Vec(4, 110), module,
40 | EqModule::FREQ_CV_INPUT));
41 | addParam(createParam(
42 | Vec(17.5, 179.5), module, EqModule::TYPE_PARAM));
43 | addParam(createParam(Vec(28.5, 229.5), module,
44 | EqModule::Q_PARAM));
45 |
46 | addInput(createInput(Vec(4, 235), module,
47 | EqModule::Q_CV_INPUT));
48 |
49 |
50 | addInput(createInput(Vec(0, 35), module,
51 | EqModule::AUDIO_INPUT));
52 | addOutput(createOutput(Vec(35, 35), module,
53 | EqModule::AUDIO_OUTPUT));
54 | }
55 |
56 | Model *modelEq = createModel("Eq");
57 |
--------------------------------------------------------------------------------
/src/view/K.cpp:
--------------------------------------------------------------------------------
1 | #include "../controller/K.hpp"
2 |
3 | #include "components.hpp"
4 |
5 | struct KWidget : ModuleWidget {
6 | KWidget(KModule *module);
7 | };
8 |
9 | KWidget::KWidget(KModule *module) {
10 | setModule(module);
11 | box.size = Vec(2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
12 |
13 | setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/K.svg")));
14 |
15 |
16 | addParam(createParam(Vec(5, 47), module, KModule::THRESHOLD));
17 | addParam(createParam(Vec(5, 87), module, KModule::RATIO));
18 | addParam(createParam(Vec(5, 127), module, KModule::ATTACK));
19 | addParam(createParam(Vec(5, 167), module, KModule::RELEASE));
20 |
21 | addInput(createInput(Vec(3, 210), module,
22 | KModule::IN));
23 |
24 |
25 | addOutput(createOutput(Vec(3, 250), module,
26 | KModule::OUT));
27 |
28 | addChild(createLight>(
29 | Vec(10, 297), module, KModule::ACTIVE));
30 |
31 | }
32 |
33 | Model *modelK = createModel("K");
34 |
--------------------------------------------------------------------------------
/src/view/M.cpp:
--------------------------------------------------------------------------------
1 | #include "../controller/M.hpp"
2 |
3 | #include "components.hpp"
4 |
5 | struct MWidget : ModuleWidget {
6 | MWidget(MModule *module);
7 | };
8 |
9 | MWidget::MWidget(MModule *module) {
10 | setModule(module);
11 | box.size = Vec(2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
12 |
13 | setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/M.svg")));
14 |
15 |
16 | for (int i = 0; i < M_COUNT; i++) {
17 | addInput(createInput(Vec(3, 30 + (190 * i)), module,
18 | MModule::IN1 + i));
19 |
20 | addInput(createInput(Vec(3, 65 + (190 * i)), module,
21 | MModule::IN2 + i));
22 |
23 | addParam(createParam(Vec(5, 102 + (190 * i)), module, MModule::KNOB + i));
24 |
25 | addOutput(createOutput(Vec(3, 140 + (190 * i)), module,
26 | MModule::OUT + i));
27 | }
28 | }
29 |
30 | Model *modelM = createModel("M");
31 |
--------------------------------------------------------------------------------
/src/view/Mixer.cpp:
--------------------------------------------------------------------------------
1 | #include "../controller/Mixer.hpp"
2 |
3 | #include
4 |
5 |
6 | #include "components.hpp"
7 |
8 | struct MixerWidget : ModuleWidget {
9 | MixerWidget(MixerModule *module);
10 | };
11 |
12 | MixerWidget::MixerWidget(MixerModule *module) {
13 | setModule(module);
14 | box.size = Vec(28 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
15 |
16 | setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Mixer.svg")));
17 |
18 |
19 | for (int i = 0; i < MIXER_CHANNELS; i++) {
20 | // input for each channel
21 | addInput(
22 | createInput(Vec(10 + (45 * i), 20), module, MixerModule::INPUT + i));
23 |
24 | // left volume LED
25 | {
26 | CDLEDSmallDisplay *led = new CDLEDSmallDisplay();
27 | if (module) {
28 | led->value = &module->channel_led_l[i];
29 | }
30 | led->box.pos = Vec(11 + (i * 45), 59);
31 | led->box.size = Vec(8, 80);
32 | addChild(led);
33 | }
34 |
35 | // right volume LED
36 | {
37 | CDLEDSmallDisplay *led = new CDLEDSmallDisplay();
38 | if (module) {
39 | led->value = &module->channel_led_r[i];
40 | }
41 | led->box.pos = Vec(25.5 + (i * 45), 59);
42 | led->box.size = Vec(8, 80);
43 | addChild(led);
44 | }
45 |
46 | // volume slider
47 | addParam(createParam(Vec(10.65 + (i * 45), 145), module,
48 | MixerModule::VOLUME_SLIDER + i));
49 |
50 | // pan knob
51 | addParam(createParam(
52 | Vec(10 + (i * 45), 265), module, MixerModule::PAN_PARAM + i));
53 |
54 | // solo button
55 | addParam(createParam(Vec(11 + (i * 45), 310), module,
56 | MixerModule::SOLO_PARAM + i));
57 | addChild(createLight>(
58 | Vec(13.2 + (i * 45), 312), module, MixerModule::SOLO_LIGHT + i));
59 |
60 | // mute button
61 | addParam(createParam(Vec(11 + (i * 45), 343), module,
62 | MixerModule::MUTE_PARAM + i));
63 | addChild(createLight>(
64 | Vec(13.2 + (i * 45), 345), module, MixerModule::MUTE_LIGHT + i));
65 |
66 | }
67 |
68 | // main outputs
69 | addOutput(createOutput(Vec(364, 20), module,
70 | MixerModule::MAIN_L_OUT));
71 | addOutput(createOutput(Vec(392, 20), module,
72 | MixerModule::MAIN_R_OUT));
73 |
74 | // main LED
75 | // left master LED
76 | {
77 | CDLEDWideDisplay *led = new CDLEDWideDisplay();
78 | if (module) {
79 | led->value = &module->master_led_l;
80 | }
81 | led->box.pos = Vec(367, 59);
82 | led->box.size = Vec(16, 110);
83 | addChild(led);
84 | }
85 |
86 | // right master LED
87 | {
88 | CDLEDWideDisplay *led = new CDLEDWideDisplay();
89 | if (module) {
90 | led->value = &module->master_led_r;
91 | }
92 | led->box.pos = Vec(397, 59);
93 | led->box.size = Vec(16, 110);
94 | addChild(led);
95 | }
96 |
97 | // main volume
98 | addParam(createParam(Vec(363.35, 173), module,
99 | MixerModule::VOLUME_L_MAIN));
100 | addParam(createParam(Vec(393.35, 173), module,
101 | MixerModule::VOLUME_R_MAIN));
102 |
103 | // mute
104 | addParam(createParam(Vec(363, 287), module,
105 | MixerModule::MUTE_L_PARAM));
106 | addChild(createLight>(
107 | Vec(365.2, 289), module, MixerModule::MUTE_L_MAIN));
108 |
109 | addParam(createParam(Vec(393, 287), module,
110 | MixerModule::MUTE_R_PARAM));
111 | addChild(createLight>(
112 | Vec(395.2, 289), module, MixerModule::MUTE_R_MAIN));
113 |
114 |
115 | }
116 |
117 | Model *modelMixer = createModel("Mixer");
118 |
--------------------------------------------------------------------------------
/src/view/MixerCV.cpp:
--------------------------------------------------------------------------------
1 | #include "../controller/MixerCV.hpp"
2 |
3 | #include
4 |
5 |
6 | #include "components.hpp"
7 |
8 | struct MixerCVWidget : ModuleWidget {
9 | MixerCVWidget(MixerCVModule *module);
10 | };
11 |
12 | MixerCVWidget::MixerCVWidget(MixerCVModule *module) {
13 | setModule(module);
14 | box.size = Vec(47 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
15 |
16 | setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/MixerCV.svg")));
17 |
18 |
19 | for (int i = 0; i < MIXER_CHANNELS; i++) {
20 | // input for each channel
21 | addInput(
22 | createInput(Vec(10 + (45 * i), 20), module, MixerCVModule::INPUT + i));
23 |
24 | // left volume LED
25 | {
26 | CDLEDSmallDisplay *led = new CDLEDSmallDisplay();
27 | if (module) {
28 | led->value = &module->channel_led_l[i];
29 | }
30 | led->box.pos = Vec(11 + (i * 45), 59);
31 | led->box.size = Vec(8, 80);
32 | addChild(led);
33 | }
34 |
35 | // right volume LED
36 | {
37 | CDLEDSmallDisplay *led = new CDLEDSmallDisplay();
38 | if (module) {
39 | led->value = &module->channel_led_r[i];
40 | }
41 | led->box.pos = Vec(25.5 + (i * 45), 59);
42 | led->box.size = Vec(8, 80);
43 | addChild(led);
44 | }
45 |
46 | // volume slider
47 | addParam(createParam(Vec(10.65 + (i * 45), 145), module,
48 | MixerCVModule::VOLUME_SLIDER + i));
49 |
50 | // pan knob
51 | addParam(createParam(
52 | Vec(10 + (i * 45), 265), module, MixerCVModule::PAN_PARAM + i));
53 |
54 | // solo button
55 | addParam(createParam(Vec(11 + (i * 45), 310), module,
56 | MixerCVModule::SOLO_PARAM + i));
57 | addChild(createLight>(
58 | Vec(13.2 + (i * 45), 312), module, MixerCVModule::SOLO_LIGHT + i));
59 |
60 | // mute button
61 | addParam(createParam(Vec(11 + (i * 45), 343), module,
62 | MixerCVModule::MUTE_PARAM + i));
63 | addChild(createLight>(
64 | Vec(13.2 + (i * 45), 345), module, MixerCVModule::MUTE_LIGHT + i));
65 |
66 | }
67 |
68 | // main outputs
69 | addOutput(createOutput(Vec(364, 20), module,
70 | MixerCVModule::MAIN_L_OUT));
71 | addOutput(createOutput(Vec(392, 20), module,
72 | MixerCVModule::MAIN_R_OUT));
73 |
74 | // main LED
75 | // left master LED
76 | {
77 | CDLEDWideDisplay *led = new CDLEDWideDisplay();
78 | if (module) {
79 | led->value = &module->master_led_l;
80 | }
81 | led->box.pos = Vec(367, 59);
82 | led->box.size = Vec(16, 110);
83 | addChild(led);
84 | }
85 |
86 | // right master LED
87 | {
88 | CDLEDWideDisplay *led = new CDLEDWideDisplay();
89 | if (module) {
90 | led->value = &module->master_led_r;
91 | }
92 | led->box.pos = Vec(397, 59);
93 | led->box.size = Vec(16, 110);
94 | addChild(led);
95 | }
96 |
97 | // main volume
98 | addParam(createParam(Vec(363.35, 173), module,
99 | MixerCVModule::VOLUME_L_MAIN));
100 | addParam(createParam(Vec(393.35, 173), module,
101 | MixerCVModule::VOLUME_R_MAIN));
102 |
103 | // mute
104 | addParam(createParam(Vec(363, 287), module,
105 | MixerCVModule::MUTE_L_PARAM));
106 | addChild(createLight>(
107 | Vec(365.2, 289), module, MixerCVModule::MUTE_L_MAIN));
108 |
109 | addParam(createParam(Vec(393, 287), module,
110 | MixerCVModule::MUTE_R_PARAM));
111 | addChild(createLight>(
112 | Vec(395.2, 289), module, MixerCVModule::MUTE_R_MAIN));
113 |
114 |
115 | for (int i = 0; i < MIXER_CHANNELS; i++) {
116 | // send output
117 | addOutput(createOutput(Vec(422 + (i * 30), 20), module,
118 | MixerCVModule::SEND + i));
119 |
120 | // recv input
121 | addInput(createInput(Vec(422 + (i * 30), 66), module,
122 | MixerCVModule::RECV + i));
123 |
124 | // mix
125 | addParam(createParam(Vec(425 + (i * 30), 112), module,
126 | MixerCVModule::MIX_PARAM + i));
127 |
128 | addInput(createInput(Vec(422 + (i * 30), 158), module,
129 | MixerCVModule::MIX_CV + i));
130 |
131 | // pan / volume cv
132 | addInput(createInput(Vec(422 + (i * 30), 204), module,
133 | MixerCVModule::VOLUME_CV + i));
134 | addInput(createInput(Vec(422 + (i * 30), 250), module,
135 | MixerCVModule::PAN_CV + i));
136 |
137 | // mute / solo cv
138 | addInput(createInput(Vec(422 + (i * 30), 296), module,
139 | MixerCVModule::SOLO_CV + i));
140 | addInput(createInput(Vec(422 + (i * 30), 342), module,
141 | MixerCVModule::MUTE_CV + i));
142 |
143 | }
144 |
145 | // main send l
146 | addOutput(createOutput(Vec(669.5, 20), module,
147 | MixerCVModule::MAIN_L_SEND));
148 |
149 | // main recv l
150 | addInput(createInput(Vec(669.5, 50), module,
151 | MixerCVModule::MAIN_L_RECV));
152 |
153 | // mix l
154 | addParam(createParam(Vec(670, 85), module,
155 | MixerCVModule::MAIN_L_MIX));
156 | // mix cv l
157 | addInput(createInput(Vec(669.5, 122), module,
158 | MixerCVModule::MAIN_MIX_L_CV));
159 |
160 | // mute l
161 | addInput(createInput(Vec(669.5, 154), module,
162 | MixerCVModule::MAIN_L_MUTE));
163 |
164 | // main send r
165 | addOutput(createOutput(Vec(669.5, 210), module,
166 | MixerCVModule::MAIN_R_SEND));
167 |
168 | // main recv r
169 | addInput(createInput(Vec(669.5, 240), module,
170 | MixerCVModule::MAIN_R_RECV));
171 |
172 | // mix r
173 | addParam(createParam(Vec(670, 275), module,
174 | MixerCVModule::MAIN_R_MIX));
175 |
176 | // main cv r
177 | addInput(createInput(Vec(669.5, 312), module,
178 | MixerCVModule::MAIN_MIX_R_CV));
179 |
180 | // main mute r
181 | addInput(createInput(Vec(669.5, 344), module,
182 | MixerCVModule::MAIN_R_MUTE));
183 | }
184 |
185 | Model *modelMixerCV = createModel("MixerCV");
186 |
--------------------------------------------------------------------------------
/src/view/Noise.cpp:
--------------------------------------------------------------------------------
1 | #include "../controller/Noise.hpp"
2 |
3 | #include "components.hpp"
4 |
5 | struct NoiseWidget : ModuleWidget {
6 | NoiseWidget(NoiseModule *module);
7 | };
8 |
9 | NoiseWidget::NoiseWidget(NoiseModule *module) {
10 | setModule(module);
11 | box.size = Vec(3 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
12 |
13 | setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Noise.svg")));
14 |
15 | addInput(createInput(Vec(10, 35), module,
16 | NoiseModule::CV_INPUT));
17 | addParam(createParam(Vec(15, 95), module,
18 | NoiseModule::NOISE_SWITCH));
19 | addOutput(createOutput(Vec(10, 135), module,
20 | NoiseModule::AUDIO_OUTPUT));
21 | addChild(createLight>(
22 | Vec(18, 209), module, NoiseModule::ON_LED));
23 | }
24 |
25 | Model *modelNoise = createModel("Noise");
26 |
--------------------------------------------------------------------------------
/src/view/Not.cpp:
--------------------------------------------------------------------------------
1 | #include "../controller/Not.hpp"
2 |
3 | #include "components.hpp"
4 |
5 | struct NotWidget : ModuleWidget {
6 | NotWidget(NotModule *module);
7 | };
8 |
9 | NotWidget::NotWidget(NotModule *module) {
10 | setModule(module);
11 | box.size = Vec(3 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
12 |
13 | setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Not.svg")));
14 |
15 |
16 | addInput(
17 | createInput(Vec(10, 35), module, NotModule::INPUT));
18 | addParam(createParam(Vec(15, 95), module, NotModule::SWITCH));
19 | addOutput(createOutput(Vec(10, 135), module,
20 | NotModule::OUTPUT));
21 | }
22 |
23 | Model *modelNot = createModel("Not");
24 |
--------------------------------------------------------------------------------
/src/view/Oscar2.cpp:
--------------------------------------------------------------------------------
1 | #include "../controller/Oscar2.hpp"
2 |
3 | #include "components.hpp"
4 |
5 | struct Oscar2Widget : ModuleWidget {
6 | Oscar2Widget(Oscar2Module *module);
7 | };
8 |
9 | Oscar2Widget::Oscar2Widget(Oscar2Module *module) {
10 | setModule(module);
11 | box.size = Vec(10 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
12 |
13 | setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Oscar2.svg")));
14 |
15 | // wave selection - left
16 | {
17 | WaveSelect *waveSelect = new WaveSelect();
18 | if (module) {
19 | waveSelect->value = &module->wave1;
20 | }
21 | waveSelect->box.pos = Vec(9.5, 33);
22 | waveSelect->box.size = Vec(10, 10);
23 | addChild(waveSelect);
24 | }
25 |
26 | // shape - left
27 | addInput(createInput(Vec(4, 85), module,
28 | Oscar2Module::SHAPE_INPUT1));
29 | addParam(createParam(
30 | Vec(28.5, 79.5), module, Oscar2Module::SHAPE_PARAM1));
31 |
32 | // shift - left
33 | addInput(createInput(Vec(4, 135), module,
34 | Oscar2Module::SHIFT_INPUT1));
35 | addParam(createParam(
36 | Vec(28.5, 129.5), module, Oscar2Module::SHIFT_PARAM1));
37 |
38 | // octave - left
39 | addInput(createInput(Vec(4, 185), module,
40 | Oscar2Module::OCTAVE_INPUT1));
41 | addParam(createParam(Vec(28.5, 179.5), module,
42 | Oscar2Module::OCTAVE_PARAM1));
43 |
44 | // fine - left
45 | addInput(createInput(Vec(4, 235), module,
46 | Oscar2Module::FINE_INPUT1));
47 | addParam(createParam(
48 | Vec(28.5, 229.5), module, Oscar2Module::FINE_PARAM1));
49 |
50 | // random - left
51 | addInput(createInput(Vec(4, 285), module,
52 | Oscar2Module::RANDOM_INPUT1));
53 | addParam(createParam(
54 | Vec(28.5, 279.5), module, Oscar2Module::RANDOM_PARAM1));
55 |
56 | // invert - left
57 | addParam(createParam(
58 | Vec(67, 108), module, Oscar2Module::INVERT_PARAM1));
59 |
60 | // wave selection - right
61 | {
62 | WaveSelect *waveSelect = new WaveSelect();
63 | if (module) {
64 | waveSelect->value = &module->wave2;
65 | }
66 | waveSelect->box.pos = Vec(55, 33);
67 | waveSelect->box.size = Vec(10, 10);
68 | addChild(waveSelect);
69 | }
70 |
71 | // shape - right
72 | addInput(createInput(Vec(94, 85), module,
73 | Oscar2Module::SHAPE_INPUT2));
74 | addParam(createParam(
75 | Vec(118.5, 79.5), module, Oscar2Module::SHAPE_PARAM2));
76 |
77 | // shift - right
78 | addInput(createInput(Vec(94, 135), module,
79 | Oscar2Module::SHIFT_INPUT2));
80 | addParam(createParam(Vec(118.5, 129.5), module,
81 | Oscar2Module::SHIFT_PARAM2));
82 |
83 | // octave - right
84 | addInput(createInput(Vec(94, 185), module,
85 | Oscar2Module::OCTAVE_INPUT2));
86 | addParam(createParam(Vec(118.5, 179.5), module,
87 | Oscar2Module::OCTAVE_PARAM2));
88 |
89 | // fine - right
90 | addInput(createInput(Vec(94, 235), module,
91 | Oscar2Module::FINE_INPUT2));
92 | addParam(createParam(
93 | Vec(118.5, 229.5), module, Oscar2Module::FINE_PARAM2));
94 |
95 | // random - right
96 | addInput(createInput(Vec(94, 285), module,
97 | Oscar2Module::RANDOM_INPUT2));
98 | addParam(createParam(Vec(118.5, 279.5), module,
99 | Oscar2Module::RANDOM_PARAM2));
100 |
101 | // invert - right
102 | addParam(createParam(
103 | Vec(67, 158), module, Oscar2Module::INVERT_PARAM2));
104 |
105 | // mix
106 | addInput(createInput(Vec(62.5, 227), module,
107 | Oscar2Module::MIX_INPUT));
108 | addParam(createParam(
109 | Vec(62.5, 252.5), module, Oscar2Module::MIX_PARAM));
110 |
111 | // v/oct
112 | addInput(createInput(Vec(22.5, 330), module,
113 | Oscar2Module::FREQ_INPUT));
114 |
115 | // mix out
116 | addOutput(createOutput(Vec(102.5, 330), module,
117 | Oscar2Module::AUDIO_OUTPUT));
118 | }
119 |
120 | Model *modelOscar2 = createModel("Oscar2");
121 |
--------------------------------------------------------------------------------
/src/view/Pan.cpp:
--------------------------------------------------------------------------------
1 | #include "../controller/Pan.hpp"
2 |
3 | #include "components.hpp"
4 |
5 | struct PanWidget : ModuleWidget {
6 | PanWidget(PanModule *module);
7 | };
8 |
9 | PanWidget::PanWidget(PanModule *module) {
10 | setModule(module);
11 | box.size = Vec(4 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
12 |
13 | setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Pan.svg")));
14 |
15 |
16 | addInput(createInput(Vec(17.5, 35), module,
17 | PanModule::AUDIO_INPUT));
18 | addParam(createParam(
19 | Vec(28.5, 79.5), module, PanModule::PAN_PARAM));
20 | addInput(createInput(Vec(4, 85), module,
21 | PanModule::PAN_INPUT));
22 | addOutput(createOutput(Vec(17.5, 135), module,
23 | PanModule::AUDIO_OUTPUT1));
24 | addOutput(createOutput(Vec(17.5, 185), module,
25 | PanModule::AUDIO_OUTPUT2));
26 | }
27 |
28 | Model *modelPan = createModel("Pan");
29 |
--------------------------------------------------------------------------------
/src/view/Shift.cpp:
--------------------------------------------------------------------------------
1 | #include "../controller/Shift.hpp"
2 |
3 | #include "components.hpp"
4 |
5 | struct ShiftWidget : ModuleWidget {
6 | ShiftWidget(ShiftModule *module);
7 | };
8 |
9 | ShiftWidget::ShiftWidget(ShiftModule *module) {
10 | setModule(module);
11 | box.size = Vec(4 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
12 |
13 | setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Shift.svg")));
14 |
15 |
16 | addInput(createInput(Vec(17.5, 35), module,
17 | ShiftModule::INPUT));
18 | addParam(createParam(Vec(22.5, 95), module, ShiftModule::SWITCH));
19 |
20 | addParam(createParam(
21 | Vec(28.5, 154.5), module, ShiftModule::KNOB));
22 | addInput(createInput(Vec(4, 160), module,
23 | ShiftModule::SHIFT));
24 | addOutput(createOutput(Vec(17.5, 211), module,
25 | ShiftModule::OUTPUT));
26 | }
27 |
28 | Model *modelShift = createModel("Shift");
29 |
--------------------------------------------------------------------------------
/src/view/Tine.cpp:
--------------------------------------------------------------------------------
1 | #include "../controller/Tine.hpp"
2 |
3 | #include "components.hpp"
4 |
5 | struct TineWidget : ModuleWidget {
6 | TineWidget(TineModule *module);
7 | };
8 |
9 | TineWidget::TineWidget(TineModule *module) {
10 | setModule(module);
11 | box.size = Vec(4 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
12 |
13 | setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Tine.svg")));
14 |
15 |
16 | addInput(createInput(Vec(4, 35), module,
17 | TineModule::AUDIO_IN));
18 | addInput(createInput(Vec(32, 35), module,
19 | TineModule::MODIFIER_IN));
20 |
21 | addParam(createParam(Vec(23, 85), module, TineModule::POLARITY));
22 |
23 | addInput(createInput(Vec(4, 135), module,
24 | TineModule::SPLIT_CV));
25 | addParam(createParam(
26 | Vec(28.5, 129.5), module, TineModule::SPLIT));
27 |
28 | addInput(createInput(Vec(4, 185), module,
29 | TineModule::LOWER_ATT_CV));
30 | addParam(createParam(
31 | Vec(28.5, 179.5), module, TineModule::LOWER_ATT));
32 |
33 | addInput(createInput(Vec(4, 235), module,
34 | TineModule::UPPER_ATT_CV));
35 | addParam(createParam(
36 | Vec(28.5, 229.5), module, TineModule::UPPER_ATT));
37 |
38 | addOutput(createOutput(Vec(4, 285), module,
39 | TineModule::LOWER_OUT));
40 | addOutput(createOutput(Vec(32, 285), module,
41 | TineModule::UPPER_OUT));
42 |
43 | addChild(createLight>(
44 | Vec(12, 177), module, TineModule::LOWER_LIGHT));
45 | addChild(createLight>(
46 | Vec(12, 227), module, TineModule::UPPER_LIGHT));
47 | }
48 |
49 | Model *modelTine = createModel("Tine");
50 |
--------------------------------------------------------------------------------
/src/view/Tsunami.cpp:
--------------------------------------------------------------------------------
1 | #include "../controller/Tsunami.hpp"
2 |
3 | #include "components.hpp"
4 |
5 | struct TsunamiWidget : ModuleWidget {
6 | TsunamiWidget(TsunamiModule *module);
7 | };
8 |
9 | TsunamiWidget::TsunamiWidget(TsunamiModule *module) {
10 | setModule(module);
11 | box.size = Vec(2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
12 |
13 | setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Tsunami.svg")));
14 |
15 |
16 | for (int i = 0; i < SHIFT_COUNT; i++) {
17 | addInput(createInput(Vec(10, 30 + (35 * i)), module,
18 | TsunamiModule::CV + i));
19 |
20 | addParam(createParam(Vec(57, 30 + (35 * i)), module, TsunamiModule::SHIFT + i));
21 |
22 | addParam(createParam(Vec(102, 30 + (35 * i)), module,
23 | TsunamiModule::LEVEL + i));
24 |
25 | addOutput(createOutput(Vec(145, 30 + (35 * i)), module,
26 | TsunamiModule::OUT + i));
27 | }
28 |
29 | addInput(createInput(Vec(10, 310), module, TsunamiModule::MASTER_IN));
30 | addParam(createParam(Vec(57, 310), module, TsunamiModule::MASTER_LEVEL));
31 | addOutput(createOutput(Vec(100, 310), module, TsunamiModule::POLY_OUT));
32 | addOutput(createOutput(Vec(145, 310), module, TsunamiModule::MASTER_OUT));
33 | }
34 |
35 | Model *modelTsunami = createModel("Tsunami");
36 |
--------------------------------------------------------------------------------
/src/view/X.cpp:
--------------------------------------------------------------------------------
1 | #include "../controller/X.hpp"
2 |
3 | #include "components.hpp"
4 |
5 | struct XWidget : ModuleWidget {
6 | XWidget(XModule *module);
7 | };
8 |
9 | XWidget::XWidget(XModule *module) {
10 | setModule(module);
11 | box.size = Vec(2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
12 |
13 | setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/X.svg")));
14 |
15 |
16 | for (int i = 0; i < X_COUNT; i++) {
17 | addInput(createInput(Vec(3, 30 + (190 * i)), module,
18 | XModule::IN + i));
19 |
20 | addOutput(createOutput(Vec(3, 140 + (190 * i)), module,
21 | XModule::OUT + i));
22 |
23 | addParam(createParam(Vec(5, 102 + (190 * i)), module, XModule::KNOB + i));
24 |
25 | addInput(createInput(Vec(3, 65 + (190 * i)), module,
26 | XModule::MIX + i));
27 | }
28 | }
29 |
30 | Model *modelX = createModel("X");
31 |
--------------------------------------------------------------------------------