@@ -22,7 +22,13 @@ Tested in | |||
# Downloads | |||
The current release can be found in the [vst2_bin/](vst2_bin/) folder. | |||
Here's a snapshot of it: [veeseevstrack_0_6_1_win64_bin-08Jul2018b.7z](dist/veeseevstrack_0_6_1_win64_bin-08Jul2018b.7z) (64bit) | |||
Here's a snapshot of it: [veeseevstrack_0_6_1_win64_bin-09Jul2018.7z](dist/veeseevstrack_0_6_1_win64_bin-09Jul2018.7z) (64bit) | |||
**WARNING** DON'T TRY TO USE THE INSTRUMENT AND EFFECT PLUGINS IN THE SAME PROJECT OR YOUR DAW WILL CRASH.** | |||
(the effect plugin can used be as an instrument, you just have to send it MIDI events) | |||
# Changelog | |||
see [vst2_bin/CHANGELOG_VST.txt](vst2_bin/CHANGELOG_VST.txt) | |||
# Demo Video | |||
@@ -33,6 +39,12 @@ Here's a demo video of it: https://vimeo.com/277703414 | |||
# Add-on modules | |||
The following add-on modules are statically linked with the VST plugin: | |||
- Alikins.IdleSwitch | |||
- Alikins.MomentaryOnButtons | |||
- Alikins.BigMuteButton | |||
- Alikins.ColorPanel | |||
- Alikins.GateLength | |||
- Alikins.SpecificValue | |||
- AS.ADSR | |||
- AS.AtNuVrTr | |||
- AS.BPMCalc | |||
@@ -82,6 +94,17 @@ The following add-on modules are statically linked with the VST plugin: | |||
- AudibleInstruments.Blinds | |||
- AudibleInstruments.Veils | |||
- AudibleInstruments.Frames | |||
- BaconMusic.Glissinator | |||
- BaconMusic.PolyGnome | |||
- BaconMusic.QuantEyes | |||
- BaconMusic.SampleDelay | |||
- BaconMusic.SortaChorus | |||
- BaconMusic.ChipNoise | |||
- BaconMusic.ChipWaves | |||
- BaconMusic.ChipYourWave | |||
- BaconMusic.KarplusStrongPoly | |||
- BaconMusic.ALingADing | |||
- BaconMusic.Bitulator | |||
- Befaco.ABC | |||
- Befaco.DualAtenuverter | |||
- Befaco.EvenVCO | |||
@@ -458,9 +481,6 @@ It is not permitted to redistribute it and Steinberg has discontinued it. | |||
I heard that the `aeffect.h` / `aeffectx.h` files are still included in the VST3 SDK. | |||
# Changelog | |||
see [vst2_bin/CHANGELOG_VST.txt](vst2_bin/CHANGELOG_VST.txt) | |||
# VCV Rack | |||
For more info about VCV rack, see https://vcvrack.com/ | |||
@@ -14,6 +14,7 @@ lib: | |||
.PHONY: | |||
vst: lib | |||
rm -f src/vst2_main.o | |||
make -f makefile_vst_instr.msvc bin -j $(NUM_JOBS) | |||
rm -f src/vst2_main.o | |||
make -f makefile_vst_effect.msvc bin -j $(NUM_JOBS) | |||
@@ -0,0 +1,27 @@ | |||
BSD 3-Clause License | |||
Redistribution and use in source and binary forms, with or without | |||
modification, are permitted provided that the following conditions are met: | |||
* Redistributions of source code must retain the above copyright notice, this | |||
list of conditions and the following disclaimer. | |||
* Redistributions in binary form must reproduce the above copyright notice, | |||
this list of conditions and the following disclaimer in the documentation | |||
and/or other materials provided with the distribution. | |||
* Neither the name of the copyright holder nor the names of its | |||
contributors may be used to endorse or promote products derived from | |||
this software without specific prior written permission. | |||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
@@ -0,0 +1,32 @@ | |||
SLUG = Alikins | |||
VERSION = 0.6.0 | |||
RACK_DIR ?= ../.. | |||
# FLAGS will be passed to both the C and C++ compiler | |||
FLAGS += | |||
CFLAGS += | |||
CXXFLAGS += | |||
# Careful about linking to libraries, since you can't assume much about the user's environment and library search path. | |||
# Static libraries are fine. | |||
LDFLAGS += | |||
# Add .cpp and .c files to the build | |||
SOURCES = $(wildcard src/*.cpp) | |||
# Must include the VCV plugin Makefile framework | |||
include $(RACK_DIR)/plugin.mk | |||
# http://cppcheck.sourceforge.net/ | |||
cppcheck: | |||
cppcheck -i$(RACK_DIR)/dep/include -i$(RACK_DIR)/include --enable=style -DVERSION=0.5.1 --quiet src/ | |||
# https://github.com/google/styleguide | |||
cpplint: | |||
cpplint --headers=hpp --filter=-whitespace/line_length,-legal/copyright,-whitespace/blank_line src/*.cpp src/*.hpp | |||
DISTRIBUTABLES += $(wildcard LICENSE*) res | |||
.PHONY: cppcheck cpplint |
@@ -0,0 +1,153 @@ | |||
# VCV Rack plugin | |||
Plugins for use with VCV Rack virtual module synthesizer (https://github.com/VCVRack/Rack) | |||
 | |||
## Modules | |||
### IdleSwitch | |||
#### What | |||
An 'idle loop' inspired module for detecting when an input | |||
is idle and turns on a gate until it sees activity again. | |||
If no input events are seen at "Input Source" within the timeout period | |||
emit a gate on "Idle Gate Output" that lasts until there are input events | |||
again. Then reset the timeout period. | |||
Sort of metaphoricaly like an idle handler or timeout in event based | |||
programming like GUI main loops. | |||
The timeout period is set by the value | |||
of the "Time before idle" param. | |||
##### Input event | |||
An "Input event" is a gate or trigger (or a CV or audio signal | |||
that looks sufficently like a gate or trigger). | |||
After a input event, the Idle Gate Output will remain on until | |||
an input event is seen at Input Source. When there is an input event, the Idle | |||
Gate Output is turned off until the expiration of the "Time before idle" or | |||
the next "Reset idle". | |||
##### Reset timer | |||
If there is a "Reset timer" source, when it gets an event, the timeout period | |||
is reset. But unlike an "Input event", a "Reset timer" event does does not | |||
reset the idle status. | |||
After "Reset time" event, the "Idle Gate Output" will remain on until | |||
an input event is seen at Input Source. When there is an input event, the Idle | |||
Gate Output is turned off until the expiration of the "Time before idle" or | |||
the next "Reset idle". | |||
To use the eventloop/gui main loop analogy, a "Reset idle" event is equilivent to | |||
running an idle handler directly (or running a mainloop iteration with no non-idle | |||
events pending). | |||
##### Clock input | |||
If a cable is hooked to the "Clock Input" then the pulse from the clock | |||
will determine with the idle time expires. When there is a clock pulse, | |||
if there were no input events since the previous clock pulse that is | |||
considered idle. | |||
The "Time before idle" display will reflect the time since the last | |||
clock pulse. The "Time remaining" display will always show 0 for now, | |||
at least until it can predict the future. | |||
##### Time input and output | |||
The "Time input" expects a CV output between 0 and 10V. The voltage | |||
maps directly do the number of seconds for the idle timeout. | |||
Eg. a 5.0V CV to "Time input" sets "Time before idle" to 5000ms (5.0 seconds) | |||
The "Time output" sends a CV output between 0 and 10V indicating | |||
the "Time before idle" value. | |||
The "Time output" can be used to connect one or more IdleSwitch modules | |||
together so they share the same "Time before idle". | |||
In "Clock input" mode, the "Time Output" will correspond with the | |||
"Time before idle" display until the time is over 10s, then the | |||
"Time output" will max out at 10V. | |||
##### Gate Output and idle start and end trigger outputs | |||
Once the idle timeout has expired, the "gate output" output | |||
will go high (+10V) and stay high until there are new input | |||
source events. ie, it turns on when there is no actively for | |||
the "Time before idle" length of time. | |||
The "start out" output will send a +10V trigger at the start | |||
of idle, at the start of the "gate out" gate. | |||
The "end out" output will send a +10V trigger at the end of | |||
idle, at the end of the "gate out" gate. This also corresponds | |||
to the same time as when a new "Input source" event is detected. | |||
"start of" trigger == "we have gone idle" | |||
"end of" trigger == "we got input source events (activity) and am no | |||
longer idle. | |||
##### Switched input and output | |||
The "Input" is switched between "When idle" and | |||
"Off idle" outputs based on idle state. | |||
The "Input" port on the bottom left can be any input. | |||
The "Input" will be routed to the "When idle" output if idle. | |||
Otherwise, the "Input" is routed to the "Off idle" output. | |||
Note the "Input" does not have to be related to the input connected | |||
to the "Input source". "Input source" typically needs to be triggers | |||
or gates, but "Input" can be any signal. | |||
#### Why | |||
Original intentional was to use in combo with a human player and midi/cv keyboard. | |||
As long as the human is playing, the IdleSwitch output is 'off', but if they go | |||
idle for some time period the output is turned on. For example, a patch may plain | |||
loud drone when idle, but would turn the drone off or down when the human played | |||
and then turn it back on when it stopped. Or maybe it could be used to start an | |||
drum fill... | |||
The "Reset idle" input allows this be kind of synced to a clock, beat, or sequence. | |||
In the dronevexample above, the drone would then only come back in on a beat. | |||
And perhaps most importantly, it can be used to do almost random output and | |||
make weird noises. | |||
### ColorPanel | |||
A CV controlled RGB/HSL color "blank" panel. | |||
Supports selectable input ranges (0V to 10V or -5V to 5V) via right click context menu | |||
The color input mode is also selectable via the context menu. | |||
Module is resizable. | |||
### BigMuteButton | |||
A big mute button. | |||
Use just before output to the audio interface module. | |||
When clicked, will mute the output output. Will unmute on next | |||
click. | |||
Easier/quicker than adjusting mixer levels, or system volume, or finding | |||
the tiny mute button on mixers. | |||
### MomentaryOnButtons | |||
A set of GUI buttons that send a +10V output while pressed. | |||
## License | |||
BSD 3-clause. See LICENSE.txt |
@@ -0,0 +1,203 @@ | |||
plugin ideas: | |||
- Mult with labels | |||
- so I can route a distance output to mult and label | |||
it as 'main seq gate output' to avoid tracing cables | |||
- seq with start/stop/reset triggers (ie, one shot sequencer) | |||
(or 'play', 'pause', 'go to begin') | |||
- solve 'how do I run a sequence once?' FAQ | |||
- event log | |||
- visualization of past triggers/gates | |||
- time series or sparkline-ish | |||
- more or less a scope but without y values and | |||
a very long time | |||
- could do diff colors for gates/triggers | |||
- or gates up and triggers down | |||
- wide module | |||
- multiple time view zooms | |||
- 1x, 2x, 5x, 10, 20x, 50x, etc | |||
- mostly for appearance or troubleshooting/debugging event stuff | |||
- could evolve to supporting playing the the history back | |||
- 1x speed or faster/slower | |||
- scrubbing? | |||
- multiple channels of events | |||
- table module | |||
- "an oscillator is a phasor and a table" | |||
- phasor input (0.0 -> 1.0) or other scaled | |||
- value output | |||
- on step, read phasor value, look for a match | |||
in the table, output the value | |||
- ui | |||
- just a scrollable list? | |||
- columns | |||
- phasor | |||
- start range | |||
- end range | |||
- output value | |||
- buttons to add delete entry | |||
- load table from json in context menu | |||
- uses: | |||
- feed a sawtooth osc output into it to | |||
create rudimentary wave table | |||
- arbitrary quantizer | |||
- building block | |||
- fancy options | |||
- entries that map a range of phasor input | |||
values to a range of output values | |||
- map ranges could be linear/log etc | |||
- linear | |||
- slope | |||
- offset | |||
- sub ranges could be cos/sine/exp/some distribution etc | |||
- (similar to the old GIMP color gradient editor) | |||
- likely could use gimp gradient (ggr) files as a type of table | |||
- modulating the table offset or value amplitudes | |||
- credits module | |||
- widget that shows author, create/mod date, urls, license, other metainfo | |||
- would save into vcv file as module json data | |||
- use a rack_credits.json in ~/ or rack doc root to set the default | |||
- list of author/credit metadata | |||
- would get appended to if modified by new author | |||
- how to only update on 'save' or 'save as' ? | |||
- don't want to append on every autosave.vcv write | |||
- track a single 'last_edit' to persist on every toJson | |||
- then on load/fromJSON, add last_edit to author list | |||
- if there is a way for plugin to distinquish a 'real save' vs | |||
an 'autosave', then could use that. | |||
- how to tell when a patch has been 'modified'? | |||
- needed to avoid adding info on every save or autosave | |||
- slightly klugey way: | |||
- if plugin knows the patch it is in, could load patch .vcv/json | |||
data and compare to 'previous'. | |||
- chksum would be simplest | |||
- but could do a smarter/deeper compare diving into the data | |||
- uses: | |||
- a patch with the module includes the author info | |||
- if patch is shared (via patchstorage for ex), and modified | |||
by a different author, the module json would include info | |||
for the original author and modification author | |||
- just as a display widget when sharing a screen capture | |||
- concerns: | |||
- could probably be better implemented as a feature of the core vcv serialization | |||
- 'Equal and opposite' | |||
- two eqs with 'opposite' settings and an effects loop in between | |||
- before eq, after eq | |||
- same bands and q's, but opposite gain | |||
- ie, if 'before eq' boosts bass and treble by 10db and cuts mids by 12db | |||
then the 'after eq' would cut bass and treble by 10db and boost mids by 12db | |||
- an effects send and return in between | |||
- munge audio signal, send it to effect (a distortion for ex) and unmunge on the way out | |||
- the before/after transforms would automatically mirror each other | |||
- otherwise pointless since it could be done with other modules | |||
- other variations could exist for other reversible-ish transforms | |||
- dont have to be perfect or lossless | |||
- compress/expand | |||
- bitcrush / 'bit extropalate' (is that a thing?) | |||
- forward/reverse delay? | |||
- pitch shift (probably most interesting, but | |||
- transport gui module | |||
- play/stop/pause/fast forward/reverse/record buttons | |||
- gate and trig outputs for them | |||
- just buttons, but with a explicit purpose | |||
- uses | |||
- generative patches that need a 'press play to start' | |||
- specific values | |||
- module with just text entrie fields for entering a float value | |||
and an output for that value | |||
- possibly with text widget in combo with a dial or inc/dec | |||
buttons (spinbox widget) | |||
- or 'click and drag to change value' | |||
- up/down for small value | |||
- left/right for multiplier (middle = 1x, left = 0..1.0x, right >1.0x multi) | |||
- 'up and to the left' for fine grained adjustments | |||
- fancy | |||
- maybe automatically also show CV v/oct freq or note equiv | |||
- maybe gate out and button to click to send value and gate output together | |||
- Seq with per step gate length and rest length between steps | |||
- 'rest' steps basically | |||
- A cv 1v/oct display -> freq hz -> period secs -> bpm. | |||
- ( could display v passed through and/or let you specify as text) | |||
- CV out | |||
- button to send gate | |||
- basically a keyboard with text/entry widgets to let you specify | |||
CV voltage/hz/period/note name | |||
- other possible things to display/select | |||
- midi note number | |||
- # of steps from a root | |||
- interval from a root ( third, flat fifth, etc) | |||
- staff / note | |||
- bpm (for a LFO or general nerdery) | |||
- generic voltage quantizer | |||
- based on 'table' module | |||
- except input CV is 'rounded' to edge of maps | |||
- see 'fancy options' for table module above | |||
- 'Attached file' module. | |||
- GUI file picker | |||
- add base64 of file to data to store in plugin Json. | |||
- Could be a list | |||
- 'Maze Table' | |||
- wave table, but with a maze | |||
- instead of phasor indexing an array, would "index" into | |||
a path along the maze graph | |||
- could have breath first and depth first paths as options | |||
- use DAG style mazes (one and only one path out, no loops or cycles) | |||
- though cycles in maze could be analog for loops in waveform playback | |||
- maybe something hilbert curve esque? | |||
- scmhidtt trigger / pulse generator banks | |||
- just ui to a set of SchmittTriggers | |||
- each strip | |||
- input | |||
- attentuator for process()'ed value | |||
- output trigger | |||
- for generator strip | |||
- trigger input | |||
- knob/cv in/param for duration | |||
- gate output | |||
- Matrix clock | |||
- N x N matrix | |||
- each row defaults to a clock division | |||
- each column defaults to clock multiply | |||
- 0,0 defaults to 1x1 quarter note for tempo | |||
- rows default to 1,2,3,4,6,8,12,16, division | |||
- ditto for columns multi | |||
- but row/columns mult/div could be changed to anything | |||
- XY pad matrix mixer | |||
- matrix mixer with a JL-Modules/kaosspad/reactor style XY pad | |||
at each cross point | |||
- 1,1 -> top output and right output | |||
- 1,0 -> just top output | |||
- -1,-1 -> inverted output to top and right output | |||
- Matrix Mixer with multiple scenes that morph/interpolate | |||
- more or less like Bidoo ACNE except values morph | |||
between scenes insted of | |||
- 2.5 dimension matrix / mixer | |||
- 8x8 matrix but with an output at | |||
each crossing point | |||
- possibly with knob/param for output level | |||
of each output | |||
- maybe a per cross mute/solo | |||
- Much like the movies, the first one was the best. | |||
- Notes module with CV/trigger control and multiple messages | |||
- like a plain notes module, but can react to input from | |||
patch | |||
- use case would be describing stages of a generative patch | |||
- or for adding info to screencast videos | |||
- Gate Divider | |||
- like gate length, but instead of generating one gate, it | |||
generates multiple gates that add up to gate length | |||
- how to seperate? | |||
- some fixed inbetween interval ("kerf") | |||
- divide into on and off gates | |||
- divide a 1s gate by 1 -> one .5s gate then a .5s 'rest' | |||
(and vice versa / inverse 10 or 01) | |||
- divide by 3 -> one .3333s gate, one .3333s rest, one .3333 gate | |||
( ie, 101 or 010) | |||
- divide by 4 -> .25 gate, .25 rest, .25 gate, .25rest | |||
(1010, 0101) | |||
- divide by 5 -> .20 on, .20 off, .20 on, .20 off, .20 on | |||
(10101, 01010) | |||
- kind of similar to Walsh codes | |||
" Experimental Electronic Music Devices Employing Walsh Functions", | |||
"BERNARD A. HUTCHINS, JR" | |||
"JOURNAL OF THE AUDIO ENGINEERING SOCIETY, October 1973" | |||
(https://pdfs.semanticscholar.org/6b9c/30f94551aaf05f077aa491f31b196898cd90.pdf) | |||
- "Sequency" - theres a module name if I ever heard one. |
@@ -0,0 +1,8 @@ | |||
ALL_OBJ= \ | |||
./src/alikins.o \ | |||
./src/BigMuteButton.o \ | |||
./src/ColorPanel.o \ | |||
./src/GateLength.o \ | |||
./src/IdleSwitch.o \ | |||
./src/MomentaryOnButtons.o \ | |||
./src/SpecificValue.o |
@@ -0,0 +1,7 @@ | |||
SLUG=Alikins | |||
include ../../../../dep/yac/install_msvc.mk | |||
include make.objects | |||
include ../../../build_plugin.mk |
@@ -0,0 +1,151 @@ | |||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |||
<!-- Created with Inkscape (http://www.inkscape.org/) --> | |||
<svg | |||
xmlns:dc="http://purl.org/dc/elements/1.1/" | |||
xmlns:cc="http://creativecommons.org/ns#" | |||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | |||
xmlns:svg="http://www.w3.org/2000/svg" | |||
xmlns="http://www.w3.org/2000/svg" | |||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | |||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | |||
width="75" | |||
height="380" | |||
viewBox="0 0 19.843751 100.54167" | |||
version="1.1" | |||
id="svg8" | |||
inkscape:version="0.92.2 5c3e80d, 2017-08-06" | |||
sodipodi:docname="MomentaryOnButtons.svg" | |||
style="enable-background:new"> | |||
<defs | |||
id="defs2" /> | |||
<sodipodi:namedview | |||
id="base" | |||
pagecolor="#ffffff" | |||
bordercolor="#666666" | |||
borderopacity="1.0" | |||
inkscape:pageopacity="0.0" | |||
inkscape:pageshadow="2" | |||
inkscape:zoom="1.6921053" | |||
inkscape:cx="37.5" | |||
inkscape:cy="190" | |||
inkscape:document-units="mm" | |||
inkscape:current-layer="layer1" | |||
showgrid="false" | |||
units="px" | |||
inkscape:snap-bbox="false" | |||
inkscape:snap-page="true" | |||
inkscape:bbox-nodes="true" | |||
inkscape:snap-bbox-edge-midpoints="true" | |||
inkscape:window-width="1440" | |||
inkscape:window-height="856" | |||
inkscape:window-x="11" | |||
inkscape:window-y="2" | |||
inkscape:window-maximized="0" | |||
inkscape:measure-start="0,0" | |||
inkscape:measure-end="0,0" | |||
inkscape:snap-text-baseline="true" | |||
inkscape:snap-nodes="false" | |||
inkscape:snap-others="false" | |||
inkscape:snap-grids="true" /> | |||
<metadata | |||
id="metadata5"> | |||
<rdf:RDF> | |||
<cc:Work | |||
rdf:about=""> | |||
<dc:format>image/svg+xml</dc:format> | |||
<dc:type | |||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | |||
<dc:title /> | |||
</cc:Work> | |||
</rdf:RDF> | |||
</metadata> | |||
<g | |||
inkscape:label="Layer 1" | |||
inkscape:groupmode="layer" | |||
id="layer1" | |||
transform="translate(0,-196.45832)" | |||
style="opacity:0.81999966"> | |||
<path | |||
style="opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.39687499;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" | |||
d="M 0,196.45831 H 15.875 V 296.99999 H 0 Z" | |||
id="rect817" | |||
inkscape:connector-curvature="0" /> | |||
<rect | |||
style="fill:#99ff55;fill-opacity:0.36097562;stroke:#000000;stroke-width:0.22707526;stroke-miterlimit:4;stroke-dasharray:0, 2.49782804;stroke-dashoffset:0;stroke-opacity:0.00485439" | |||
id="rect4596" | |||
width="6.5781407" | |||
height="96.39846" | |||
x="6.7771721" | |||
y="196.41748" /> | |||
<text | |||
xml:space="preserve" | |||
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332" | |||
x="2.0959258" | |||
y="199.32983" | |||
id="text4602"><tspan | |||
sodipodi:role="line" | |||
id="tspan4600" | |||
x="2.0959258" | |||
y="208.6936" | |||
style="stroke-width:0.26458332" /></text> | |||
<rect | |||
style="opacity:1;fill:#e08976;fill-opacity:0.37931034;stroke:#000000;stroke-width:0.24057104;stroke-miterlimit:4;stroke-dasharray:0, 2.64628129;stroke-dashoffset:0;stroke-opacity:0" | |||
id="rect4598" | |||
width="5.266654" | |||
height="96.089233" | |||
x="1.639776" | |||
y="196.67091" /> | |||
<g | |||
aria-label="Momentary" | |||
transform="matrix(0.76786881,0,0,1.3023058,0.9381804,0)" | |||
style="font-style:normal;font-weight:normal;font-size:3.08836555px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#e08934;fill-opacity:0.97647059;stroke:none;stroke-width:0.07720914" | |||
id="text4606"> | |||
<path | |||
d="m 1.7166386,151.74845 h 0.4539053 l 0.5745445,1.53212 0.5775606,-1.53212 h 0.4539053 v 2.25143 H 3.47948 v -1.97698 l -0.5805765,1.54418 H 2.5927813 L 2.0122048,152.0229 v 1.97698 H 1.7166386 Z" | |||
style="fill:#e08934;fill-opacity:0.97647059;stroke-width:0.07720914" | |||
id="path4608" | |||
inkscape:connector-curvature="0" /> | |||
<path | |||
d="m 5.0251709,152.50546 q -0.2231827,0 -0.3528699,0.17493 -0.1296872,0.17341 -0.1296872,0.47652 0,0.30311 0.1281792,0.47803 0.1296872,0.17342 0.3543779,0.17342 0.2216747,0 0.3513619,-0.17492 0.1296872,-0.17493 0.1296872,-0.47653 0,-0.30009 -0.1296872,-0.47502 -0.1296872,-0.17643 -0.3513619,-0.17643 z m 0,-0.23525 q 0.3619178,0 0.5685126,0.23525 0.2065948,0.23525 0.2065948,0.65145 0,0.4147 -0.2065948,0.65145 -0.2065948,0.23525 -0.5685126,0.23525 -0.3634258,0 -0.5700206,-0.23525 -0.2050868,-0.23675 -0.2050868,-0.65145 0,-0.4162 0.2050868,-0.65145 0.2065948,-0.23525 0.5700206,-0.23525 z" | |||
style="fill:#e08934;fill-opacity:0.97647059;stroke-width:0.07720914" | |||
id="path4610" | |||
inkscape:connector-curvature="0" /> | |||
<path | |||
d="m 7.5736757,152.63515 q 0.1040514,-0.18699 0.2488185,-0.27597 0.1447672,-0.089 0.340806,-0.089 0.2638984,0 0.4071576,0.18549 0.1432591,0.18397 0.1432591,0.52478 v 1.0194 H 8.4347386 v -1.01036 q 0,-0.24278 -0.085955,-0.36041 -0.085956,-0.11762 -0.2623904,-0.11762 -0.2156428,0 -0.340806,0.14326 -0.1251633,0.14326 -0.1251633,0.39057 v 0.95456 H 7.3414451 v -1.01036 q 0,-0.24429 -0.085955,-0.36041 -0.085955,-0.11762 -0.2654064,-0.11762 -0.2126267,0 -0.33779,0.14477 -0.1251632,0.14326 -0.1251632,0.38906 v 0.95456 H 6.2481516 V 152.3109 H 6.52713 v 0.26239 q 0.095003,-0.15532 0.2277066,-0.22922 0.1327032,-0.0739 0.3151701,-0.0739 0.1839749,0 0.3121542,0.0935 0.1296872,0.0935 0.1915148,0.27144 z" | |||
style="fill:#e08934;fill-opacity:0.97647059;stroke-width:0.07720914" | |||
id="path4612" | |||
inkscape:connector-curvature="0" /> | |||
<path | |||
d="m 10.713313,153.08604 v 0.13571 H 9.4375527 q 0.018096,0.28652 0.171911,0.43732 0.1553231,0.14929 0.4312853,0.14929 0.159847,0 0.309138,-0.0392 0.150799,-0.0392 0.298582,-0.11763 v 0.26239 q -0.149291,0.0633 -0.306122,0.0965 -0.156831,0.0332 -0.318186,0.0332 -0.4041414,0 -0.6408959,-0.23525 -0.2352466,-0.23524 -0.2352466,-0.63637 0,-0.4147 0.2231826,-0.65748 0.2246907,-0.2443 0.6047044,-0.2443 0.3408055,0 0.5383525,0.22017 0.199055,0.21866 0.199055,0.59566 z m -0.27747,-0.0814 q -0.003,-0.2277 -0.128179,-0.36342 -0.123656,-0.13572 -0.3287425,-0.13572 -0.2322306,0 -0.3724738,0.13119 -0.1387352,0.1312 -0.159847,0.36946 z" | |||
style="fill:#e08934;fill-opacity:0.97647059;stroke-width:0.07720914" | |||
id="path4614" | |||
inkscape:connector-curvature="0" /> | |||
<path | |||
d="m 12.572666,152.98048 v 1.0194 h -0.277471 v -1.01036 q 0,-0.23977 -0.09349,-0.3589 -0.0935,-0.11913 -0.280486,-0.11913 -0.224691,0 -0.354378,0.14326 -0.129688,0.14326 -0.129688,0.39057 v 0.95456 H 11.15817 v -1.68895 h 0.278978 v 0.26239 q 0.09953,-0.15231 0.233739,-0.22771 0.135719,-0.0754 0.312154,-0.0754 0.291042,0 0.440334,0.18096 0.149291,0.17945 0.149291,0.52931 z" | |||
style="fill:#e08934;fill-opacity:0.97647059;stroke-width:0.07720914" | |||
id="path4616" | |||
inkscape:connector-curvature="0" /> | |||
<path | |||
d="m 13.403569,151.83139 v 0.47954 h 0.571528 v 0.21564 h -0.571528 v 0.91686 q 0,0.20659 0.0558,0.26541 0.0573,0.0588 0.230723,0.0588 h 0.28501 v 0.23223 h -0.28501 q -0.321202,0 -0.443349,-0.11913 -0.122148,-0.12064 -0.122148,-0.43732 v -0.91686 h -0.203579 v -0.21564 h 0.203579 v -0.47954 z" | |||
style="fill:#e08934;fill-opacity:0.97647059;stroke-width:0.07720914" | |||
id="path4618" | |||
inkscape:connector-curvature="0" /> | |||
<path | |||
d="m 15.109107,153.15088 q -0.336282,0 -0.465969,0.0769 -0.129688,0.0769 -0.129688,0.26239 0,0.14778 0.09651,0.23524 0.09802,0.086 0.265406,0.086 0.230723,0 0.369458,-0.16286 0.140243,-0.16437 0.140243,-0.43581 v -0.0618 z m 0.553433,-0.11461 v 0.96361 h -0.277471 v -0.25636 q -0.095,0.15381 -0.236754,0.22771 -0.141752,0.0724 -0.346838,0.0724 -0.259375,0 -0.41319,-0.14477 -0.152307,-0.14627 -0.152307,-0.39057 0,-0.28501 0.190007,-0.42977 0.191515,-0.14477 0.570021,-0.14477 h 0.389061 v -0.0272 q 0,-0.19151 -0.126671,-0.29556 -0.125163,-0.10556 -0.35287,-0.10556 -0.144767,0 -0.281994,0.0347 -0.137227,0.0347 -0.263899,0.10405 v -0.25635 q 0.152307,-0.0588 0.295567,-0.0875 0.143259,-0.0302 0.278978,-0.0302 0.366442,0 0.547401,0.19001 0.180959,0.19001 0.180959,0.57605 z" | |||
style="fill:#e08934;fill-opacity:0.97647059;stroke-width:0.07720914" | |||
id="path4620" | |||
inkscape:connector-curvature="0" /> | |||
<path | |||
d="m 17.214262,152.5703 q -0.04675,-0.0271 -0.102544,-0.0392 -0.05429,-0.0136 -0.120639,-0.0136 -0.235246,0 -0.361918,0.15382 -0.125163,0.1523 -0.125163,0.43882 v 0.88972 H 16.22502 v -1.68895 h 0.278978 v 0.26239 q 0.08746,-0.15382 0.227707,-0.22771 0.140243,-0.0754 0.340806,-0.0754 0.02865,0 0.06334,0.005 0.03468,0.003 0.07691,0.0105 z" | |||
style="fill:#e08934;fill-opacity:0.97647059;stroke-width:0.07720914" | |||
id="path4622" | |||
inkscape:connector-curvature="0" /> | |||
<path | |||
d="m 18.211044,154.15671 q -0.117624,0.3016 -0.229215,0.39358 -0.111591,0.092 -0.298582,0.092 h -0.221675 v -0.23223 h 0.162863 q 0.114608,0 0.177943,-0.0543 0.06334,-0.0543 0.140243,-0.25635 l 0.04976,-0.12668 -0.68312,-1.6618 h 0.294058 l 0.527797,1.321 0.527797,-1.321 h 0.294058 z" | |||
style="fill:#e08934;fill-opacity:0.97647059;stroke-width:0.07720914" | |||
id="path4624" | |||
inkscape:connector-curvature="0" /> | |||
</g> | |||
</g> | |||
</svg> |
@@ -0,0 +1,99 @@ | |||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |||
<!-- Created with Inkscape (http://www.inkscape.org/) --> | |||
<svg | |||
xmlns:dc="http://purl.org/dc/elements/1.1/" | |||
xmlns:cc="http://creativecommons.org/ns#" | |||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | |||
xmlns:svg="http://www.w3.org/2000/svg" | |||
xmlns="http://www.w3.org/2000/svg" | |||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | |||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | |||
id="svg1383" | |||
version="1.1" | |||
viewBox="0 0 13.758334 13.758334" | |||
height="52" | |||
width="52" | |||
inkscape:version="0.92.2 5c3e80d, 2017-08-06" | |||
sodipodi:docname="PurpleTrimpot.svg"> | |||
<sodipodi:namedview | |||
inkscape:window-maximized="0" | |||
inkscape:window-y="4" | |||
inkscape:window-x="-9" | |||
inkscape:window-height="851" | |||
inkscape:window-width="1440" | |||
units="px" | |||
showgrid="false" | |||
inkscape:current-layer="layer1" | |||
inkscape:document-units="px" | |||
inkscape:cy="32.696773" | |||
inkscape:cx="-11.691577" | |||
inkscape:zoom="6.6458333" | |||
inkscape:pageshadow="2" | |||
inkscape:pageopacity="0.0" | |||
borderopacity="1.0" | |||
bordercolor="#666666" | |||
pagecolor="#ffffff" | |||
id="base" /> | |||
<defs | |||
id="defs1377" /> | |||
<metadata | |||
id="metadata1380"> | |||
<rdf:RDF> | |||
<cc:Work | |||
rdf:about=""> | |||
<dc:format>image/svg+xml</dc:format> | |||
<dc:type | |||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | |||
<dc:title></dc:title> | |||
</cc:Work> | |||
</rdf:RDF> | |||
</metadata> | |||
<g | |||
transform="translate(0,-283.24164)" | |||
id="layer1" | |||
inkscape:groupmode="layer" | |||
inkscape:label="Layer 1" | |||
style="display:inline"> | |||
<g | |||
id="g3842" | |||
transform="matrix(1.0833334,0,0,1.0833334,1.3314724e-8,-24.750039)"> | |||
<circle | |||
style="opacity:0.86499999;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.28937858;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:54.20000076;stroke-opacity:0.83009709" | |||
id="path1928" | |||
cx="6.3499999" | |||
cy="290.64999" | |||
r="6.3499999" /> | |||
<rect | |||
y="0.67853534" | |||
x="5.7470388" | |||
height="5.1807055" | |||
width="1.2059226" | |||
id="rect2030" | |||
style="display:inline;opacity:0.86499999;fill:#cf57ff;fill-opacity:1;fill-rule:evenodd;stroke:#351313;stroke-width:0.05291667;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:54.20000076;stroke-opacity:0.98173517" | |||
transform="translate(0,284.29998)" /> | |||
</g> | |||
</g> | |||
<g | |||
transform="translate(6.35,7.4083334)" | |||
inkscape:label="Gear16" | |||
id="g1991" | |||
style="display:none"> | |||
<path | |||
d="m 4.68617,-0.53218 0.34647,-0.03934 0.33145,0.0432 0.69557,0.30411 V 0.22421 L 5.36409,0.52832 5.03264,0.57152 4.68617,0.53218 4.53311,1.30165 4.86827,1.39789 5.15795,1.56465 5.68419,2.11179 5.51259,2.52607 4.7536,2.54085 4.43084,2.45393 4.1258,2.28499 3.68993,2.93732 3.96274,3.15448 4.16656,3.41941 4.44336,4.12628 4.12628,4.44336 3.41941,4.16656 3.15448,3.96274 2.93732,3.68993 2.28499,4.1258 2.45393,4.43084 2.54085,4.7536 2.52607,5.51259 2.11179,5.68419 1.56465,5.15795 1.39789,4.86827 1.30165,4.53311 0.53218,4.68617 0.57152,5.03264 0.52832,5.36409 0.22421,6.05966 H -0.22421 L -0.52832,5.36409 -0.57152,5.03264 -0.53218,4.68617 -1.30165,4.53311 -1.39789,4.86827 -1.56465,5.15795 -2.11179,5.68419 -2.52607,5.51259 -2.54085,4.7536 -2.45393,4.43084 -2.28499,4.1258 -2.93732,3.68993 -3.15448,3.96274 -3.41941,4.16656 -4.12628,4.44336 -4.44336,4.12628 -4.16656,3.41941 -3.96274,3.15448 -3.68993,2.93732 -4.1258,2.28499 -4.43084,2.45393 -4.7536,2.54085 -5.51259,2.52607 -5.68419,2.11179 -5.15795,1.56465 -4.86827,1.39789 -4.53311,1.30165 -4.68617,0.53218 -5.03264,0.57152 -5.36409,0.52832 -6.05966,0.22421 v -0.44842 l 0.69557,-0.30411 0.33145,-0.0432 0.34647,0.03934 0.15306,-0.76947 -0.33516,-0.09624 -0.28968,-0.16676 -0.52624,-0.54714 0.1716,-0.41428 0.75899,-0.01478 0.32276,0.08692 0.30504,0.16894 0.43587,-0.65233 -0.27281,-0.21716 -0.20382,-0.26493 -0.2768,-0.70687 0.31708,-0.31708 0.70687,0.2768 0.26493,0.20382 0.21716,0.27281 0.65233,-0.43587 -0.16894,-0.30504 -0.08692,-0.32276 0.01478,-0.75899 0.41428,-0.1716 0.54714,0.52624 0.16676,0.28968 0.09624,0.33516 0.76947,-0.15306 -0.03934,-0.34647 0.0432,-0.33145 0.30411,-0.69557 h 0.44842 l 0.30411,0.69557 0.0432,0.33145 -0.03934,0.34647 0.76947,0.15306 0.09624,-0.33516 0.16676,-0.28968 0.54714,-0.52624 0.41428,0.1716 0.01478,0.75899 -0.08692,0.32276 -0.16894,0.30504 0.65233,0.43587 0.21716,-0.27281 0.26493,-0.20382 0.70687,-0.2768 0.31708,0.31708 -0.2768,0.70687 -0.20382,0.26493 -0.27281,0.21716 0.43587,0.65233 0.30504,-0.16894 0.32276,-0.08692 0.75899,0.01478 0.1716,0.41428 -0.52624,0.54714 -0.28968,0.16676 -0.33516,0.09624 z" | |||
style="fill:none;stroke:#000000;stroke-width:0.26458332" | |||
id="path1989" | |||
inkscape:connector-curvature="0" /> | |||
</g> | |||
<g | |||
id="g2015" | |||
inkscape:label="Gear16" | |||
transform="rotate(-164.64789,3.6742468,3.2762409)" | |||
style="display:none"> | |||
<path | |||
id="path2013" | |||
style="fill:none;stroke:#000000;stroke-width:0.26458332" | |||
d="m 4.4731195,-1.4948601 0.3307196,-0.1105155 0.3331888,-0.026656 0.7435981,0.1528474 0.093232,0.4386209 -0.6171421,0.44208162 -0.3152253,0.11116831 -0.347078,0.0335548 0.010267,0.7844782 L 5.0525243,0.3551725 5.3705454,0.45806054 5.9990426,0.88383277 5.9173262,1.3247374 5.1779949,1.4969973 4.8442163,1.4790823 4.5107175,1.3772554 4.2199993,2.1059529 4.5319979,2.2616471 4.786446,2.4784112 5.2041638,3.1122844 4.9599374,3.4883601 4.2109642,3.3645754 3.909447,3.2202914 3.6403121,2.998593 3.0928595,3.5605652 3.3215292,3.8238148 3.4736553,4.12145 3.6170012,4.8669272 3.2474519,5.120911 2.6028567,4.7199274 2.379513,4.4712489 2.2156924,4.1634224 1.4948601,4.4731195 1.6053756,4.8038391 1.632032,5.1370279 1.4791846,5.880626 1.0405637,5.9738578 0.59848208,5.3567157 0.48731377,5.0414904 0.45375894,4.6944124 -0.33071926,4.7046789 -0.3551725,5.0525243 -0.45806054,5.3705454 -0.88383277,5.9990426 -1.3247374,5.9173262 -1.4969973,5.1779949 -1.4790823,4.8442163 -1.3772554,4.5107175 -2.1059529,4.2199993 -2.2616471,4.5319979 -2.4784112,4.786446 -3.1122844,5.2041638 -3.4883601,4.9599374 -3.3645754,4.2109642 -3.2202914,3.909447 -2.998593,3.6403121 -3.5605652,3.0928595 -3.8238148,3.3215292 -4.12145,3.4736553 -4.8669272,3.6170012 -5.120911,3.2474519 -4.7199274,2.6028567 -4.4712489,2.379513 -4.1634224,2.2156924 -4.4731195,1.4948601 -4.8038391,1.6053756 -5.1370279,1.632032 -5.880626,1.4791846 -5.973858,1.0405637 l 0.6171421,-0.44208162 0.3152253,-0.11116831 0.347078,-0.0335548 -0.010267,-0.7844782 -0.3478454,-0.0244532 -0.3180211,-0.10288804 -0.6284972,-0.42577223 0.081716,-0.4409046 0.7393313,-0.1722599 0.3337786,0.017915 0.3334988,0.1018269 0.2907182,-0.7286975 -0.3119986,-0.1556942 -0.2544481,-0.2167641 -0.4177178,-0.6338732 0.2442264,-0.3760757 0.7489732,0.1237847 0.3015172,0.144284 0.2691349,0.2216984 0.5474526,-0.5619722 -0.2286697,-0.2632496 -0.1521261,-0.2976352 -0.1433459,-0.7454772 0.3695493,-0.2539838 0.6445952,0.4009836 0.2233437,0.2486785 0.1638206,0.3078265 0.7208323,-0.3096971 -0.1105155,-0.3307196 -0.026656,-0.3331888 0.1528474,-0.7435981 0.4386209,-0.093232 0.44208162,0.6171421 0.11116831,0.3152253 0.0335548,0.347078 0.7844782,-0.010267 0.0244532,-0.3478454 0.10288804,-0.3180211 0.42577223,-0.6284972 0.4409046,0.081716 0.1722599,0.7393313 -0.017915,0.3337786 -0.1018269,0.3334988 0.7286975,0.2907182 0.155695,-0.3119976 L 2.4784112,-4.786446 3.1122844,-5.2041638 3.4883601,-4.9599374 3.3645754,-4.2109642 3.2202914,-3.909447 2.998593,-3.6403121 3.5605652,-3.0928595 3.8238148,-3.3215292 4.12145,-3.4736553 4.8669272,-3.6170012 5.120911,-3.2474519 4.7199274,-2.6028567 4.4712489,-2.379513 4.1634224,-2.2156924 Z" | |||
inkscape:connector-curvature="0" /> | |||
</g> | |||
</svg> |
@@ -0,0 +1,94 @@ | |||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |||
<!-- Created with Inkscape (http://www.inkscape.org/) --> | |||
<svg | |||
xmlns:dc="http://purl.org/dc/elements/1.1/" | |||
xmlns:cc="http://creativecommons.org/ns#" | |||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | |||
xmlns:svg="http://www.w3.org/2000/svg" | |||
xmlns="http://www.w3.org/2000/svg" | |||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | |||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | |||
id="svg1383" | |||
version="1.1" | |||
viewBox="0 0 4.7624999 4.7624999" | |||
height="18" | |||
width="18" | |||
inkscape:version="0.92.2 5c3e80d, 2017-08-06" | |||
sodipodi:docname="SmallPurpleTrimpot.svg"> | |||
<sodipodi:namedview | |||
inkscape:window-maximized="0" | |||
inkscape:window-y="0" | |||
inkscape:window-x="7" | |||
inkscape:window-height="851" | |||
inkscape:window-width="1440" | |||
units="px" | |||
showgrid="false" | |||
inkscape:current-layer="layer1" | |||
inkscape:document-units="px" | |||
inkscape:cy="32.696773" | |||
inkscape:cx="15.994944" | |||
inkscape:zoom="6.6458333" | |||
inkscape:pageshadow="2" | |||
inkscape:pageopacity="0.0" | |||
borderopacity="1.0" | |||
bordercolor="#666666" | |||
pagecolor="#ffffff" | |||
id="base" /> | |||
<defs | |||
id="defs1377" /> | |||
<metadata | |||
id="metadata1380"> | |||
<rdf:RDF> | |||
<cc:Work | |||
rdf:about=""> | |||
<dc:format>image/svg+xml</dc:format> | |||
<dc:type | |||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | |||
<dc:title></dc:title> | |||
</cc:Work> | |||
</rdf:RDF> | |||
</metadata> | |||
<g | |||
transform="translate(0,-292.23748)" | |||
id="layer1" | |||
inkscape:groupmode="layer" | |||
inkscape:label="Layer 1" | |||
style="display:inline"> | |||
<circle | |||
r="2.3812499" | |||
cy="294.61871" | |||
cx="2.3812499" | |||
id="path1928" | |||
style="opacity:0.86499999;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.10851697;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:54.20000076;stroke-opacity:0.83009709" /> | |||
<rect | |||
style="display:inline;opacity:0.86499999;fill:#cf57ff;fill-opacity:1;fill-rule:evenodd;stroke:#351313;stroke-width:0.01984375;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:54.20000076;stroke-opacity:0.98173517" | |||
id="rect2030" | |||
width="0.45222098" | |||
height="1.9427645" | |||
x="2.1551394" | |||
y="292.49191" /> | |||
</g> | |||
<g | |||
transform="translate(6.35,-1.5875)" | |||
inkscape:label="Gear16" | |||
id="g1991" | |||
style="display:none"> | |||
<path | |||
d="m 4.68617,-0.53218 0.34647,-0.03934 0.33145,0.0432 0.69557,0.30411 V 0.22421 L 5.36409,0.52832 5.03264,0.57152 4.68617,0.53218 4.53311,1.30165 4.86827,1.39789 5.15795,1.56465 5.68419,2.11179 5.51259,2.52607 4.7536,2.54085 4.43084,2.45393 4.1258,2.28499 3.68993,2.93732 3.96274,3.15448 4.16656,3.41941 4.44336,4.12628 4.12628,4.44336 3.41941,4.16656 3.15448,3.96274 2.93732,3.68993 2.28499,4.1258 2.45393,4.43084 2.54085,4.7536 2.52607,5.51259 2.11179,5.68419 1.56465,5.15795 1.39789,4.86827 1.30165,4.53311 0.53218,4.68617 0.57152,5.03264 0.52832,5.36409 0.22421,6.05966 H -0.22421 L -0.52832,5.36409 -0.57152,5.03264 -0.53218,4.68617 -1.30165,4.53311 -1.39789,4.86827 -1.56465,5.15795 -2.11179,5.68419 -2.52607,5.51259 -2.54085,4.7536 -2.45393,4.43084 -2.28499,4.1258 -2.93732,3.68993 -3.15448,3.96274 -3.41941,4.16656 -4.12628,4.44336 -4.44336,4.12628 -4.16656,3.41941 -3.96274,3.15448 -3.68993,2.93732 -4.1258,2.28499 -4.43084,2.45393 -4.7536,2.54085 -5.51259,2.52607 -5.68419,2.11179 -5.15795,1.56465 -4.86827,1.39789 -4.53311,1.30165 -4.68617,0.53218 -5.03264,0.57152 -5.36409,0.52832 -6.05966,0.22421 v -0.44842 l 0.69557,-0.30411 0.33145,-0.0432 0.34647,0.03934 0.15306,-0.76947 -0.33516,-0.09624 -0.28968,-0.16676 -0.52624,-0.54714 0.1716,-0.41428 0.75899,-0.01478 0.32276,0.08692 0.30504,0.16894 0.43587,-0.65233 -0.27281,-0.21716 -0.20382,-0.26493 -0.2768,-0.70687 0.31708,-0.31708 0.70687,0.2768 0.26493,0.20382 0.21716,0.27281 0.65233,-0.43587 -0.16894,-0.30504 -0.08692,-0.32276 0.01478,-0.75899 0.41428,-0.1716 0.54714,0.52624 0.16676,0.28968 0.09624,0.33516 0.76947,-0.15306 -0.03934,-0.34647 0.0432,-0.33145 0.30411,-0.69557 h 0.44842 l 0.30411,0.69557 0.0432,0.33145 -0.03934,0.34647 0.76947,0.15306 0.09624,-0.33516 0.16676,-0.28968 0.54714,-0.52624 0.41428,0.1716 0.01478,0.75899 -0.08692,0.32276 -0.16894,0.30504 0.65233,0.43587 0.21716,-0.27281 0.26493,-0.20382 0.70687,-0.2768 0.31708,0.31708 -0.2768,0.70687 -0.20382,0.26493 -0.27281,0.21716 0.43587,0.65233 0.30504,-0.16894 0.32276,-0.08692 0.75899,0.01478 0.1716,0.41428 -0.52624,0.54714 -0.28968,0.16676 -0.33516,0.09624 z" | |||
style="fill:none;stroke:#000000;stroke-width:0.26458332" | |||
id="path1989" | |||
inkscape:connector-curvature="0" /> | |||
</g> | |||
<g | |||
id="g2015" | |||
inkscape:label="Gear16" | |||
transform="rotate(-164.64789,3.0680186,-1.2216757)" | |||
style="display:none"> | |||
<path | |||
id="path2013" | |||
style="fill:none;stroke:#000000;stroke-width:0.26458332" | |||
d="m 4.4731195,-1.4948601 0.3307196,-0.1105155 0.3331888,-0.026656 0.7435981,0.1528474 0.093232,0.4386209 -0.6171421,0.44208162 -0.3152253,0.11116831 -0.347078,0.0335548 0.010267,0.7844782 L 5.0525243,0.3551725 5.3705454,0.45806054 5.9990426,0.88383277 5.9173262,1.3247374 5.1779949,1.4969973 4.8442163,1.4790823 4.5107175,1.3772554 4.2199993,2.1059529 4.5319979,2.2616471 4.786446,2.4784112 5.2041638,3.1122844 4.9599374,3.4883601 4.2109642,3.3645754 3.909447,3.2202914 3.6403121,2.998593 3.0928595,3.5605652 3.3215292,3.8238148 3.4736553,4.12145 3.6170012,4.8669272 3.2474519,5.120911 2.6028567,4.7199274 2.379513,4.4712489 2.2156924,4.1634224 1.4948601,4.4731195 1.6053756,4.8038391 1.632032,5.1370279 1.4791846,5.880626 1.0405637,5.9738578 0.59848208,5.3567157 0.48731377,5.0414904 0.45375894,4.6944124 -0.33071926,4.7046789 -0.3551725,5.0525243 -0.45806054,5.3705454 -0.88383277,5.9990426 -1.3247374,5.9173262 -1.4969973,5.1779949 -1.4790823,4.8442163 -1.3772554,4.5107175 -2.1059529,4.2199993 -2.2616471,4.5319979 -2.4784112,4.786446 -3.1122844,5.2041638 -3.4883601,4.9599374 -3.3645754,4.2109642 -3.2202914,3.909447 -2.998593,3.6403121 -3.5605652,3.0928595 -3.8238148,3.3215292 -4.12145,3.4736553 -4.8669272,3.6170012 -5.120911,3.2474519 -4.7199274,2.6028567 -4.4712489,2.379513 -4.1634224,2.2156924 -4.4731195,1.4948601 -4.8038391,1.6053756 -5.1370279,1.632032 -5.880626,1.4791846 -5.973858,1.0405637 l 0.6171421,-0.44208162 0.3152253,-0.11116831 0.347078,-0.0335548 -0.010267,-0.7844782 -0.3478454,-0.0244532 -0.3180211,-0.10288804 -0.6284972,-0.42577223 0.081716,-0.4409046 0.7393313,-0.1722599 0.3337786,0.017915 0.3334988,0.1018269 0.2907182,-0.7286975 -0.3119986,-0.1556942 -0.2544481,-0.2167641 -0.4177178,-0.6338732 0.2442264,-0.3760757 0.7489732,0.1237847 0.3015172,0.144284 0.2691349,0.2216984 0.5474526,-0.5619722 -0.2286697,-0.2632496 -0.1521261,-0.2976352 -0.1433459,-0.7454772 0.3695493,-0.2539838 0.6445952,0.4009836 0.2233437,0.2486785 0.1638206,0.3078265 0.7208323,-0.3096971 -0.1105155,-0.3307196 -0.026656,-0.3331888 0.1528474,-0.7435981 0.4386209,-0.093232 0.44208162,0.6171421 0.11116831,0.3152253 0.0335548,0.347078 0.7844782,-0.010267 0.0244532,-0.3478454 0.10288804,-0.3180211 0.42577223,-0.6284972 0.4409046,0.081716 0.1722599,0.7393313 -0.017915,0.3337786 -0.1018269,0.3334988 0.7286975,0.2907182 0.155695,-0.3119976 L 2.4784112,-4.786446 3.1122844,-5.2041638 3.4883601,-4.9599374 3.3645754,-4.2109642 3.2202914,-3.909447 2.998593,-3.6403121 3.5605652,-3.0928595 3.8238148,-3.3215292 4.12145,-3.4736553 4.8669272,-3.6170012 5.120911,-3.2474519 4.7199274,-2.6028567 4.4712489,-2.379513 4.1634224,-2.2156924 Z" | |||
inkscape:connector-curvature="0" /> | |||
</g> | |||
</svg> |
@@ -0,0 +1,167 @@ | |||
#include "alikins.hpp" | |||
#include "dsp/digital.hpp" | |||
namespace rack_plugin_Alikins { | |||
struct BigMuteButton : Module { | |||
enum ParamIds { | |||
BIG_MUTE_BUTTON_PARAM, | |||
NUM_PARAMS | |||
}; | |||
enum InputIds { | |||
LEFT_INPUT, | |||
RIGHT_INPUT, | |||
NUM_INPUTS | |||
}; | |||
enum OutputIds { | |||
LEFT_OUTPUT, | |||
RIGHT_OUTPUT, | |||
NUM_OUTPUTS | |||
}; | |||
enum LightIds { | |||
NUM_LIGHTS | |||
}; | |||
float gain_mult = 1.0f; | |||
enum FadeState { | |||
UNMUTED_STEADY, | |||
MUTED_STEADY, | |||
MUTED_FADE_DOWN, | |||
UNMUTED_FADE_UP, | |||
INITIAL | |||
}; | |||
// FadeState state = UNMUTED_STEADY; | |||
FadeState state = INITIAL; | |||
SchmittTrigger muteOnTrigger; | |||
SchmittTrigger muteOffTrigger; | |||
float gmult2 = 1.0f; | |||
float crossfade_mix = 0.005f; | |||
BigMuteButton() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {} | |||
void step() override; | |||
void onReset() override { | |||
state = UNMUTED_STEADY; | |||
} | |||
}; | |||
void BigMuteButton::step() { | |||
// INITIAL state, choose next state based on current value of BIG_MUTE_BUTTON_PARAM | |||
// since BIG_MUTE_BUTTON_PARAM should be based on either the default, or for a saved | |||
// patch, the value saved to the params JSON. | |||
if (muteOnTrigger.process(params[BIG_MUTE_BUTTON_PARAM].value)) { | |||
// debug("MUTE ON"); | |||
state = MUTED_FADE_DOWN; | |||
gmult2 = 1.0f; | |||
} | |||
if (muteOffTrigger.process(!params[BIG_MUTE_BUTTON_PARAM].value)) { | |||
// debug("MUTE OFF"); | |||
state = UNMUTED_FADE_UP; | |||
gmult2 = 0.0f; | |||
} | |||
switch(state) { | |||
case INITIAL: | |||
state = (params[BIG_MUTE_BUTTON_PARAM].value == 0.0f) ? UNMUTED_STEADY : MUTED_STEADY; | |||
break; | |||
case MUTED_STEADY: | |||
gmult2 = 0.0f; | |||
break; | |||
case UNMUTED_STEADY: | |||
gmult2 = 1.0f; | |||
break; | |||
case MUTED_FADE_DOWN: | |||
if (isNear(gmult2, 0.0f)) { | |||
state = MUTED_STEADY; | |||
// debug("faded down crossfade to 0.0"); | |||
gmult2 = 0.0f; | |||
break; | |||
} | |||
gmult2 = crossfade(gmult2, 0.0f, crossfade_mix); | |||
break; | |||
case UNMUTED_FADE_UP: | |||
if (isNear(gmult2, 1.0f)) { | |||
state = UNMUTED_STEADY; | |||
// debug("faded up crossfade to 1.0"); | |||
gmult2 = 1.0f; | |||
break; | |||
} | |||
gmult2 = crossfade(gmult2, 1.0f, crossfade_mix); | |||
break; | |||
} | |||
gmult2 = clamp(gmult2, 0.0f, 1.0f); | |||
outputs[LEFT_OUTPUT].value = inputs[LEFT_INPUT].value * gmult2; | |||
outputs[RIGHT_OUTPUT].value = inputs[RIGHT_INPUT].value * gmult2; | |||
// debug("state: %d, gmult2: %f", state, gmult2); | |||
// TODO: to eliminate worse case DC thump, also apply a RC filter of some sort? | |||
} | |||
struct BigSwitch : SVGSwitch, ToggleSwitch { | |||
BigSwitch() { | |||
addFrame(SVG::load(assetPlugin(plugin, "res/BigMuteButtonMute.svg"))); | |||
addFrame(SVG::load(assetPlugin(plugin, "res/BigMuteButtonUnmute.svg"))); | |||
} | |||
}; | |||
struct BigMuteButtonWidget : ModuleWidget { | |||
BigMuteButtonWidget(BigMuteButton *module); | |||
}; | |||
BigMuteButtonWidget::BigMuteButtonWidget(BigMuteButton *module) : ModuleWidget(module) { | |||
box.size = Vec(6 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT); | |||
setPanel(SVG::load(assetPlugin(plugin, "res/BigMuteButton.svg"))); | |||
addParam(ParamWidget::create<BigSwitch>(Vec(0.0f, 0.0f), | |||
module, | |||
BigMuteButton::BIG_MUTE_BUTTON_PARAM, | |||
0.0f, 1.0f, 0.0f)); | |||
addInput(Port::create<PJ301MPort>(Vec(4.0f, 302.0f), | |||
Port::INPUT, | |||
module, | |||
BigMuteButton::LEFT_INPUT)); | |||
addInput(Port::create<PJ301MPort>(Vec(4.0f, 330.0f), | |||
Port::INPUT, | |||
module, | |||
BigMuteButton::RIGHT_INPUT)); | |||
addOutput(Port::create<PJ301MPort>(Vec(60.0f, 302.0f), | |||
Port::OUTPUT, | |||
module, | |||
BigMuteButton::LEFT_OUTPUT)); | |||
addOutput(Port::create<PJ301MPort>(Vec(60.0f, 330.0f), | |||
Port::OUTPUT, | |||
module, | |||
BigMuteButton::RIGHT_OUTPUT)); | |||
addChild(Widget::create<ScrewSilver>(Vec(0.0, 0))); | |||
addChild(Widget::create<ScrewSilver>(Vec(box.size.x-15, 0))); | |||
addChild(Widget::create<ScrewSilver>(Vec(30, 365))); | |||
} | |||
} // namespace rack_plugin_Alikins | |||
using namespace rack_plugin_Alikins; | |||
RACK_PLUGIN_MODEL_INIT(Alikins, BigMuteButton) { | |||
Model *modelBigMuteButton = Model::create<BigMuteButton, BigMuteButtonWidget>( | |||
"Alikins", "BigMuteButton", "Big Mute Button", UTILITY_TAG); | |||
return modelBigMuteButton; | |||
} |
@@ -0,0 +1,359 @@ | |||
#include "global_pre.hpp" | |||
#include "alikins.hpp" | |||
#include "util/math.hpp" | |||
#include "global_ui.hpp" | |||
namespace rack_plugin_Alikins { | |||
struct ColorPanel : Module { | |||
enum ParamIds { | |||
RED_PARAM, | |||
GREEN_PARAM, | |||
BLUE_PARAM, | |||
NUM_PARAMS | |||
}; | |||
enum InputIds { | |||
RED_INPUT, | |||
GREEN_INPUT, | |||
BLUE_INPUT, | |||
NUM_INPUTS | |||
}; | |||
enum OutputIds { | |||
NUM_OUTPUTS | |||
}; | |||
enum LightIds { | |||
NUM_LIGHTS | |||
}; | |||
float red = 0.0f; | |||
float green = 0.0f; | |||
float blue = 0.0f; | |||
enum InputRange { | |||
ZERO_TEN, | |||
MINUS_PLUS_FIVE | |||
}; | |||
InputRange inputRange = MINUS_PLUS_FIVE; | |||
const float in_min[2] = {0.0, -5.0}; | |||
const float in_max[2] = {10.0, 5.0}; | |||
enum ColorMode { | |||
RGB_MODE, | |||
HSL_MODE, | |||
}; | |||
ColorMode colorMode = HSL_MODE; | |||
ColorPanel() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {} | |||
void step() override; | |||
json_t *toJson() override; | |||
void fromJson(json_t *rootJ) override; | |||
}; | |||
json_t* ColorPanel::toJson() { | |||
json_t *rootJ = json_object(); | |||
json_object_set_new(rootJ, "inputRange", json_integer(inputRange)); | |||
json_object_set_new(rootJ, "colorMode", json_integer(colorMode)); | |||
return rootJ; | |||
} | |||
void ColorPanel::fromJson(json_t *rootJ) { | |||
json_t *inputRangeJ = json_object_get(rootJ, "inputRange"); | |||
if (inputRangeJ) { | |||
inputRange = (InputRange) json_integer_value(inputRangeJ); | |||
} | |||
json_t *colorModeJ = json_object_get(rootJ, "colorMode"); | |||
if (colorModeJ) { | |||
colorMode = (ColorMode) json_integer_value(colorModeJ); | |||
} | |||
} | |||
void ColorPanel::step() { | |||
if (inputs[RED_INPUT].active) { | |||
float in_value = clamp(inputs[RED_INPUT].value, in_min[inputRange], in_max[inputRange]); | |||
red = rescale(in_value, in_min[inputRange], in_max[inputRange], 0.0f, 1.0f); | |||
} | |||
if (inputs[GREEN_INPUT].active) { | |||
float in_value = clamp(inputs[GREEN_INPUT].value, in_min[inputRange], in_max[inputRange]); | |||
green = rescale(in_value, in_min[inputRange], in_max[inputRange], 0.0f, 1.0f); | |||
} | |||
if (inputs[BLUE_INPUT].active) { | |||
float in_value = clamp(inputs[BLUE_INPUT].value, in_min[inputRange], in_max[inputRange]); | |||
blue = rescale(in_value, in_min[inputRange], in_max[inputRange], 0.0f, 1.0f); | |||
} | |||
} | |||
// From Rack/src/core/Blank.cpp | |||
struct ColorPanelModuleResizeHandle : Widget { | |||
bool right = false; | |||
float dragX; | |||
Rect originalBox; | |||
ColorPanelModuleResizeHandle() { | |||
box.size = Vec(RACK_GRID_WIDTH * 1, RACK_GRID_HEIGHT); | |||
dragX = 0.0f; | |||
} | |||
void onMouseDown(EventMouseDown &e) override { | |||
if (e.button == 0) { | |||
e.consumed = true; | |||
e.target = this; | |||
} | |||
} | |||
void onDragStart(EventDragStart &e) override { | |||
dragX = rack::global_ui->app.gRackWidget->lastMousePos.x; | |||
ModuleWidget *m = getAncestorOfType<ModuleWidget>(); | |||
originalBox = m->box; | |||
} | |||
void onDragMove(EventDragMove &e) override { | |||
ModuleWidget *m = getAncestorOfType<ModuleWidget>(); | |||
float newDragX = rack::global_ui->app.gRackWidget->lastMousePos.x; | |||
float deltaX = newDragX - dragX; | |||
Rect newBox = originalBox; | |||
const float minWidth = 6 * RACK_GRID_WIDTH; | |||
if (right) { | |||
newBox.size.x += deltaX; | |||
newBox.size.x = fmaxf(newBox.size.x, minWidth); | |||
newBox.size.x = roundf(newBox.size.x / RACK_GRID_WIDTH) * RACK_GRID_WIDTH; | |||
} else { | |||
newBox.size.x -= deltaX; | |||
newBox.size.x = fmaxf(newBox.size.x, minWidth); | |||
newBox.size.x = roundf(newBox.size.x / RACK_GRID_WIDTH) * RACK_GRID_WIDTH; | |||
newBox.pos.x = originalBox.pos.x + originalBox.size.x - newBox.size.x; | |||
} | |||
rack::global_ui->app.gRackWidget->requestModuleBox(m, newBox); | |||
} | |||
}; | |||
struct ColorFrame : TransparentWidget { | |||
ColorPanel *module; | |||
ColorPanel::ColorMode colorMode; | |||
// std::vector<CreditData*> vcredits; | |||
float red = 0.5f; | |||
float green = 0.5f; | |||
float blue = 0.5f; | |||
ColorFrame() { | |||
} | |||
void step() override { | |||
Widget::step(); | |||
red = module->red; | |||
green = module->green; | |||
blue = module->blue; | |||
colorMode = module->colorMode; | |||
} | |||
void draw(NVGcontext *vg) override { | |||
// FIXME: not really red, green, blue anymore | |||
// could include alpha | |||
// debug("RgbPanel.draw red=%f, green=%f, blue=%f", red, green, blue); | |||
NVGcolor panelColor = nvgRGBf(red, green, blue); | |||
if (module->colorMode == ColorPanel::HSL_MODE) { | |||
panelColor = nvgHSL(red, green, blue); | |||
} | |||
nvgBeginPath(vg); | |||
nvgRect(vg, 0.0, 0.0, box.size.x, box.size.y); | |||
nvgFillColor(vg, panelColor); | |||
nvgFill(vg); | |||
} | |||
}; | |||
struct ColorPanelWidget : ModuleWidget { | |||
ColorPanelWidget(ColorPanel *module); | |||
Widget *rightHandle; | |||
ColorFrame *panel; | |||
Menu *createContextMenu() override; | |||
void step() override; | |||
json_t *toJson() override; | |||
void fromJson(json_t *rootJ) override; | |||
}; | |||
ColorPanelWidget::ColorPanelWidget(ColorPanel *module) : ModuleWidget(module) { | |||
box.size = Vec(6 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT); | |||
{ | |||
panel = new ColorFrame(); | |||
panel->box.size = box.size; | |||
panel->module = module; | |||
addChild(panel); | |||
} | |||
ColorPanelModuleResizeHandle *leftHandle = new ColorPanelModuleResizeHandle(); | |||
ColorPanelModuleResizeHandle *rightHandle = new ColorPanelModuleResizeHandle(); | |||
rightHandle->right = true; | |||
this->rightHandle = rightHandle; | |||
addChild(leftHandle); | |||
addChild(rightHandle); | |||
/* | |||
addChild(Widget::create<ScrewSilver>(Vec(15, 0))); | |||
addChild(Widget::create<ScrewSilver>(Vec(15, 365))); | |||
topRightScrew = Widget::create<ScrewSilver>(Vec(box.size.x - 30, 0)); | |||
bottomRightScrew = Widget::create<ScrewSilver>(Vec(box.size.x - 30, 365)); | |||
addChild(topRightScrew); | |||
addChild(bottomRightScrew); | |||
*/ | |||
debug("box.size (%f, %f)", box.size.x, box.size.y); | |||
//debug("port box.size %f %f", Port::PJ301MPort.box.size.x, PJ301MPort.box.size.y); | |||
// this type of port is 24.672108 x 24.672136 by default | |||
// FIXME: how do I figure that out before creating the instance?i | |||
float port_width = 24.672108f; | |||
float empty_space = box.size.x - (3.0f * port_width); | |||
float interstitial = empty_space / 5.0f; | |||
float x_pos = interstitial; | |||
addInput(Port::create<PJ301MPort>(Vec(x_pos, 340.0f), | |||
Port::INPUT, | |||
module, | |||
ColorPanel::RED_INPUT)); | |||
x_pos = x_pos + interstitial + port_width;; | |||
addInput(Port::create<PJ301MPort>(Vec(x_pos, 340.0f), | |||
Port::INPUT, | |||
module, | |||
ColorPanel::GREEN_INPUT)); | |||
x_pos = x_pos + interstitial + port_width;; | |||
addInput(Port::create<PJ301MPort>(Vec(x_pos, 340.0f), | |||
Port::INPUT, | |||
module, | |||
ColorPanel::BLUE_INPUT)); | |||
} | |||
void ColorPanelWidget::step() { | |||
panel->box.size = box.size; | |||
rightHandle->box.pos.x = box.size.x - rightHandle->box.size.x; | |||
// debug("box.size (%f, %f)", box.size.x, box.size.y); | |||
ModuleWidget::step(); | |||
} | |||
json_t *ColorPanelWidget::toJson() { | |||
json_t *rootJ = ModuleWidget::toJson(); | |||
json_object_set_new(rootJ, "width", json_real(box.size.x)); | |||
return rootJ; | |||
} | |||
void ColorPanelWidget::fromJson(json_t *rootJ) { | |||
ModuleWidget::fromJson(rootJ); | |||
json_t *widthJ = json_object_get(rootJ, "width"); | |||
if (widthJ) | |||
box.size.x = json_number_value(widthJ); | |||
} | |||
struct ColorModeItem : MenuItem { | |||
ColorPanel *colorPanel; | |||
ColorPanel::ColorMode colorMode; | |||
void onAction(EventAction &e) override { | |||
colorPanel->colorMode = colorMode; | |||
}; | |||
void step() override { | |||
rightText = (colorPanel->colorMode == colorMode)? "âś”" : ""; | |||
}; | |||
}; | |||
struct InputRangeItem : MenuItem { | |||
ColorPanel *colorPanel; | |||
ColorPanel::InputRange inputRange; | |||
void onAction(EventAction &e) override { | |||
colorPanel->inputRange = inputRange; | |||
}; | |||
void step() override { | |||
rightText = (colorPanel->inputRange == inputRange)? "âś”" : ""; | |||
}; | |||
}; | |||
Menu *ColorPanelWidget::createContextMenu() { | |||
Menu *menu = ModuleWidget::createContextMenu(); | |||
MenuLabel *spacerLabel = new MenuLabel(); | |||
menu->addChild(spacerLabel); | |||
ColorPanel *colorPanel = dynamic_cast<ColorPanel*>(module); | |||
assert(colorPanel); | |||
MenuLabel *colorModeLabel = new MenuLabel(); | |||
colorModeLabel->text = "ColorMode"; | |||
menu->addChild(colorModeLabel); | |||
// FIXME: colorModeItem looks too much like colorModelItem | |||
ColorModeItem *colorModeItem = new ColorModeItem(); | |||
colorModeItem->text = "RGB"; | |||
colorModeItem->colorPanel = colorPanel; | |||
colorModeItem->colorMode = ColorPanel::RGB_MODE; | |||
menu->addChild(colorModeItem); | |||
ColorModeItem *hslModeItem = new ColorModeItem(); | |||
hslModeItem->text = "HSL"; | |||
hslModeItem->colorPanel = colorPanel; | |||
hslModeItem->colorMode = ColorPanel::HSL_MODE; | |||
menu->addChild(hslModeItem); | |||
MenuLabel *modeLabel2 = new MenuLabel(); | |||
modeLabel2->text = "Input Range"; | |||
menu->addChild(modeLabel2); | |||
InputRangeItem *zeroTenItem = new InputRangeItem(); | |||
zeroTenItem->text = "0 - +10V (uni)"; | |||
zeroTenItem->colorPanel = colorPanel; | |||
zeroTenItem->inputRange = ColorPanel::ZERO_TEN; | |||
menu->addChild(zeroTenItem); | |||
InputRangeItem *fiveFiveItem = new InputRangeItem(); | |||
fiveFiveItem->text = "-5 - +5V (bi)"; | |||
fiveFiveItem->colorPanel = colorPanel; | |||
fiveFiveItem->inputRange = ColorPanel::MINUS_PLUS_FIVE;; | |||
menu->addChild(fiveFiveItem); | |||
return menu; | |||
} | |||
} // namespace rack_plugin_Alikins | |||
using namespace rack_plugin_Alikins; | |||
RACK_PLUGIN_MODEL_INIT(Alikins, ColorPanel) { | |||
Model *modelColorPanel = Model::create<ColorPanel, ColorPanelWidget>( | |||
"Alikins", "ColorPanel", "Color Panel", VISUAL_TAG); | |||
return modelColorPanel; | |||
} |
@@ -0,0 +1,140 @@ | |||
#include "dsp/digital.hpp" | |||
#include "alikins.hpp" | |||
#include "MsDisplayWidget.hpp" | |||
namespace rack_plugin_Alikins { | |||
struct GateLength : Module { | |||
enum ParamIds { | |||
GATE_LENGTH_PARAM1, | |||
GATE_LENGTH_PARAM2, | |||
GATE_LENGTH_PARAM3, | |||
GATE_LENGTH_PARAM4, | |||
GATE_LENGTH_PARAM5, | |||
NUM_PARAMS | |||
}; | |||
enum InputIds { | |||
TRIGGER_INPUT1, | |||
TRIGGER_INPUT2, | |||
TRIGGER_INPUT3, | |||
TRIGGER_INPUT4, | |||
TRIGGER_INPUT5, | |||
GATE_LENGTH_INPUT1, | |||
GATE_LENGTH_INPUT2, | |||
GATE_LENGTH_INPUT3, | |||
GATE_LENGTH_INPUT4, | |||
GATE_LENGTH_INPUT5, | |||
NUM_INPUTS | |||
}; | |||
enum OutputIds { | |||
GATE_OUTPUT1, | |||
GATE_OUTPUT2, | |||
GATE_OUTPUT3, | |||
GATE_OUTPUT4, | |||
GATE_OUTPUT5, | |||
NUM_OUTPUTS | |||
}; | |||
enum LightIds { | |||
NUM_LIGHTS | |||
}; | |||
float gate_length[GATE_LENGTH_INPUTS]; | |||
SchmittTrigger inputOnTrigger[GATE_LENGTH_INPUTS]; | |||
PulseGenerator gateGenerator[GATE_LENGTH_INPUTS]; | |||
GateLength() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {} | |||
void step() override; | |||
void onReset() override { | |||
} | |||
}; | |||
void GateLength::step() { | |||
// FIXME: add way to support >10.0s gate length | |||
float sample_time = engineGetSampleTime(); | |||
for (int i = 0; i < GATE_LENGTH_INPUTS; i++) { | |||
gate_length[i] = clamp(params[GATE_LENGTH_PARAM1 + i].value + inputs[GATE_LENGTH_INPUT1 + i].value, 0.0f, 10.0f); | |||
if (inputOnTrigger[i].process(inputs[TRIGGER_INPUT1 + i].value)) { | |||
// debug("GL INPUT ON TRIGGER %d gate_length: %f", i, gate_length[i]); | |||
gateGenerator[i].trigger(gate_length[i]); | |||
} | |||
outputs[GATE_OUTPUT1 + i].value = gateGenerator[i].process(sample_time) ? 10.0f : 0.0f; | |||
} | |||
} | |||
struct GateLengthWidget : ModuleWidget { | |||
GateLengthWidget(GateLength *module); | |||
}; | |||
GateLengthWidget::GateLengthWidget(GateLength *module) : ModuleWidget(module) { | |||
box.size = Vec(4 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT); | |||
setPanel(SVG::load(assetPlugin(plugin, "res/GateLength.svg"))); | |||
float y_pos = 2.0f; | |||
for (int i = 0; i < GATE_LENGTH_INPUTS; i++) { | |||
float x_pos = 4.0f; | |||
y_pos += 39.0f; | |||
addInput(Port::create<PJ301MPort>(Vec(x_pos, y_pos), | |||
Port::INPUT, | |||
module, | |||
GateLength::TRIGGER_INPUT1 + i)); | |||
x_pos += 30.0f; | |||
MsDisplayWidget *gate_length_display = new MsDisplayWidget(); | |||
gate_length_display->box.pos = Vec(x_pos, y_pos + 1.0f); | |||
gate_length_display->box.size = Vec(84, 24); | |||
gate_length_display->value = &module->gate_length[i]; | |||
addChild(gate_length_display); | |||
// FIXME: use new sequential box hbox/vbox thing | |||
x_pos += 84.0f; | |||
x_pos += 4.0f; | |||
addOutput(Port::create<PJ301MPort>(Vec(x_pos, y_pos), | |||
Port::OUTPUT, | |||
module, | |||
GateLength::GATE_OUTPUT1 + i)); | |||
x_pos = 4.0f; | |||
y_pos += 26.0f; | |||
addInput(Port::create<PJ301MPort>(Vec(x_pos, y_pos), | |||
Port::INPUT, | |||
module, | |||
GateLength::GATE_LENGTH_INPUT1 + i)); | |||
x_pos += 30.0f; | |||
addParam(ParamWidget::create<Trimpot>(Vec(x_pos, y_pos + 3.0f), | |||
module, | |||
GateLength::GATE_LENGTH_PARAM1 + i, | |||
0.0f, 10.0f, 0.1f)); | |||
} | |||
addChild(Widget::create<ScrewSilver>(Vec(0.0f, 0.0f))); | |||
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 15.0f, 0.0f))); | |||
addChild(Widget::create<ScrewSilver>(Vec(0.0f, 365.0f))); | |||
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 15.0f, 365.0f))); | |||
} | |||
} // namespace rack_plugin_Alikins | |||
using namespace rack_plugin_Alikins; | |||
RACK_PLUGIN_MODEL_INIT(Alikins, GateLength) { | |||
Model *modelGateLength = Model::create<GateLength, GateLengthWidget>( | |||
"Alikins", "GateLength", "Gate Length", UTILITY_TAG); | |||
return modelGateLength; | |||
} |
@@ -0,0 +1,328 @@ | |||
#include <stdio.h> | |||
#include <sstream> | |||
#include <iomanip> | |||
#include "alikins.hpp" | |||
#include "dsp/digital.hpp" | |||
// #include "util.hpp" | |||
namespace rack_plugin_Alikins { | |||
/* IdleSwitch | |||
* | |||
* What: | |||
* | |||
* If no input events are seen at Input Source within the timeout period | |||
* emit a gate on Idle Gate Output that lasts until there are input events | |||
* again. Then reset the timeout period. | |||
* | |||
* Sort of metaphoricaly like an idle handler or timeout in event based | |||
* programming like GUI main loops. | |||
* | |||
* The timeout period is set by the value | |||
* of the 'Time before idle' param. | |||
* | |||
* If there is a 'Reset idle' source, when it gets an event, the timeout period | |||
* is reset. After a reset event, the Idle Gate Output will remain on until | |||
* an input event is seen at Input Source. When there is an input event, the Idle | |||
* Gate Output is turned off until the expiration of the 'Time before idle' or | |||
* the next 'Reset idle'. | |||
* | |||
* To use the eventloop/gui main loop analogy, a 'Reset idle' event is equilivent to | |||
* running an idle handler directly (or running a mainloop iteration with no non-idle | |||
* events pending). | |||
* | |||
* Why: | |||
* | |||
* Original intentional was to use in combo with a human player and midi/cv keyboard. | |||
* As long as the human is playing, the IdleSwitch output is 'off', but if they go | |||
* idle for some time period the output is turned on. For example, a patch may plain | |||
* loud drone when idle, but would turn the drone off or down when the human played | |||
* and then turn it back on when it stopped. Or maybe it could be used to start an | |||
* drum fill... | |||
* | |||
* The 'Reset idle' input allows this be kind of synced to a clock, beat, or sequence. | |||
* In the dronevexample above, the drone would then only come back in on a beat. | |||
* | |||
* And perhaps most importantly, it can be used to do almost random output and | |||
* make weird noises. | |||
*/ | |||
/* TODO | |||
* - is there a 'standard' for communicating lengths of time (like delay time)? | |||
* - idle start trigger | |||
* - idle end trigger | |||
* - switch for output to be high for idle or low for idle | |||
* - time display widget for timeout length | |||
* - Fine/Course params fors for timeout | |||
* - idle timeout countdown display for remaining time before timeout | |||
* - gui 'progress' widget? | |||
*/ | |||
struct IdleSwitch : Module { | |||
enum ParamIds { | |||
TIME_PARAM, | |||
NUM_PARAMS | |||
}; | |||
enum InputIds { | |||
INPUT_SOURCE_INPUT, | |||
HEARTBEAT_INPUT, | |||
TIME_INPUT, | |||
PULSE_INPUT, | |||
SWITCHED_INPUT, | |||
NUM_INPUTS | |||
}; | |||
enum OutputIds { | |||
IDLE_GATE_OUTPUT, | |||
TIME_OUTPUT, | |||
IDLE_START_OUTPUT, | |||
IDLE_END_OUTPUT, | |||
FRAME_COUNT_OUTPUT, | |||
ON_WHEN_IDLE_OUTPUT, | |||
OFF_WHEN_IDLE_OUTPUT, | |||
NUM_OUTPUTS | |||
}; | |||
enum LightIds { | |||
NUM_LIGHTS | |||
}; | |||
int idleTimeoutMS = 140; | |||
int idleTimeLeftMS = 0; | |||
SchmittTrigger inputTrigger; | |||
// FIXME: these names are confusing | |||
SchmittTrigger heartbeatTrigger; | |||
// clock mode stuff | |||
SchmittTrigger pulseTrigger; | |||
int pulseFrame = 0; | |||
bool waiting_for_pulse = false; | |||
bool pulse_mode = false; | |||
PulseGenerator idleStartPulse; | |||
PulseGenerator idleEndPulse; | |||
// FIXME: not really counts | |||
int frameCount = 0; | |||
int maxFrameCount = 0; | |||
float idleGateOutput = 0.0; | |||
float deltaTime = 0; | |||
bool is_idle = false; | |||
IdleSwitch() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {} | |||
void step() override; | |||
}; | |||
void IdleSwitch::step() { | |||
bool pulse_seen = false; | |||
bool time_exceeded = false; | |||
pulse_mode = inputs[PULSE_INPUT].active; | |||
float sampleRate = engineGetSampleRate(); | |||
// Compute the length of our idle time based on the knob + time cv | |||
// -or- | |||
// base it one the time since the last clock pulse | |||
if (pulse_mode) { | |||
if (inputTrigger.process(inputs[PULSE_INPUT].value)) { | |||
// keep track of which frame we got a pulse | |||
// FIXME: without a max time, frameCount can wrap? | |||
// update pulseFrame to point to current frame count | |||
pulseFrame = frameCount; | |||
waiting_for_pulse = true; | |||
pulse_seen = true; | |||
} | |||
deltaTime = fmax(frameCount - pulseFrame, 0) / sampleRate; | |||
// if we are waiting, maxframeCount is the time since last pulse and increasing | |||
maxFrameCount = frameCount; | |||
} else { | |||
deltaTime = params[TIME_PARAM].value; | |||
if (inputs[TIME_INPUT].active) { | |||
deltaTime += clamp(inputs[TIME_INPUT].value, 0.0f, 10.0f); | |||
} | |||
// TODO: refactor into submethods if not subclass | |||
maxFrameCount = (int)ceilf(deltaTime * sampleRate); | |||
} | |||
idleTimeoutMS = std::round(deltaTime*1000); | |||
// debug("is_idle: %d pulse_mode: %d pulse_frame: %d frameCount: %d maxFrameCount: %d ", is_idle, pulse_mode, pulseFrame, frameCount, maxFrameCount); | |||
// debug("is_idle: %d pulse_mode: %d w_f_pulse: %d pulse_seen: %d pulseFrame: %d frameCount: %d deltaTime: %f", | |||
// is_idle, pulse_mode, waiting_for_pulse, pulse_seen, pulseFrame, frameCount, deltaTime); | |||
if (inputs[HEARTBEAT_INPUT].active && | |||
heartbeatTrigger.process(inputs[HEARTBEAT_INPUT].value)) { | |||
frameCount = 0; | |||
} | |||
// time_left_s is always 0 for pulse mode until we predict the future | |||
float frames_left = fmax(maxFrameCount - frameCount, 0); | |||
float time_left_s = frames_left / sampleRate; | |||
// TODO: simplify the start/end/gate on logic... really only a few states to check | |||
// the start of idle (not idle -> idle trans) | |||
if ((frameCount > maxFrameCount) || (waiting_for_pulse && pulse_seen)) { | |||
time_exceeded = true; | |||
if (!is_idle) { | |||
idleStartPulse.trigger(0.01); | |||
} | |||
} | |||
// stay idle once we start until there is an input event | |||
is_idle = (is_idle || time_exceeded); | |||
if (is_idle) { | |||
idleGateOutput = 10.0; | |||
outputs[ON_WHEN_IDLE_OUTPUT].value = inputs[SWITCHED_INPUT].value; | |||
outputs[OFF_WHEN_IDLE_OUTPUT].value = 0.0f; | |||
} else { | |||
idleGateOutput = 0.0; | |||
outputs[ON_WHEN_IDLE_OUTPUT].value = 0.0f; | |||
outputs[OFF_WHEN_IDLE_OUTPUT].value = inputs[SWITCHED_INPUT].value; | |||
is_idle = false; | |||
// if we arent idle yet, the idleTimeLeft is changing and we need to update time remaining display | |||
// update idletimeLeftMS which drives the digit display widget | |||
idleTimeLeftMS = time_left_s*1000; | |||
} | |||
frameCount++; | |||
if (inputs[INPUT_SOURCE_INPUT].active && | |||
inputTrigger.process(inputs[INPUT_SOURCE_INPUT].value)) { | |||
// only end idle if we are already idle (idle->not idle transition) | |||
if (is_idle) { | |||
idleEndPulse.trigger(0.01); | |||
} | |||
is_idle = false; | |||
waiting_for_pulse = false; | |||
frameCount = 0; | |||
pulseFrame = 0; | |||
} | |||
// once clock input works, could add an output to indicate how long between clock | |||
// If in pulse mode, deltaTime can be larger than 10s internal, but the max output | |||
// to "Time output" is 10V. ie, after 10s the "Time Output" stops increasing. | |||
outputs[TIME_OUTPUT].value = clamp(deltaTime, 0.0f, 10.0f); | |||
outputs[IDLE_GATE_OUTPUT].value = idleGateOutput; | |||
outputs[IDLE_START_OUTPUT].value = idleStartPulse.process(1.0/engineGetSampleRate()) ? 10.0 : 0.0; | |||
outputs[IDLE_END_OUTPUT].value = idleEndPulse.process(1.0/engineGetSampleRate()) ? 10.0 : 0.0; | |||
} | |||
// From AS DelayPlus.cpp https://github.com/AScustomWorks/AS | |||
struct IdleSwitchMsDisplayWidget : TransparentWidget { | |||
int *value; | |||
std::shared_ptr<Font> font; | |||
IdleSwitchMsDisplayWidget() { | |||
font = Font::load(assetPlugin(plugin, "res/Segment7Standard.ttf")); | |||
} | |||
void draw(NVGcontext *vg) override { | |||
// Background | |||
// these go to... | |||
NVGcolor backgroundColor = nvgRGB(0x11, 0x11, 0x11); | |||
NVGcolor borderColor = nvgRGB(0xff, 0xff, 0xff); | |||
nvgBeginPath(vg); | |||
nvgRoundedRect(vg, 0.0, 0.0, box.size.x, box.size.y, 5.0); | |||
nvgFillColor(vg, backgroundColor); | |||
nvgFill(vg); | |||
nvgStrokeWidth(vg, 3.0); | |||
nvgStrokeColor(vg, borderColor); | |||
nvgStroke(vg); | |||
// text | |||
nvgFontSize(vg, 18); | |||
nvgFontFaceId(vg, font->handle); | |||
nvgTextLetterSpacing(vg, 2.5); | |||
std::stringstream to_display; | |||
to_display << std::right << std::setw(5) << *value; | |||
Vec textPos = Vec(0.5f, 19.0f); | |||
NVGcolor textColor = nvgRGB(0x65, 0xf6, 0x78); | |||
nvgFillColor(vg, textColor); | |||
nvgText(vg, textPos.x, textPos.y, to_display.str().c_str(), NULL); | |||
} | |||
}; | |||
struct IdleSwitchWidget : ModuleWidget { | |||
IdleSwitchWidget(IdleSwitch *module); | |||
}; | |||
IdleSwitchWidget::IdleSwitchWidget(IdleSwitch *module) : ModuleWidget(module) { | |||
setPanel(SVG::load(assetPlugin(plugin, "res/IdleSwitch.svg"))); | |||
addChild(Widget::create<ScrewSilver>(Vec(5, 0))); | |||
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 20, 365))); | |||
addInput(Port::create<PJ301MPort>(Vec(37, 20.0), Port::INPUT, module, IdleSwitch::INPUT_SOURCE_INPUT)); | |||
addInput(Port::create<PJ301MPort>(Vec(37, 60.0), Port::INPUT, module, IdleSwitch::HEARTBEAT_INPUT)); | |||
addInput(Port::create<PJ301MPort>(Vec(70, 60.0), Port::INPUT, module, IdleSwitch::PULSE_INPUT)); | |||
// idle time display | |||
// FIXME: handle large IdleTimeoutMs (> 99999ms) better | |||
IdleSwitchMsDisplayWidget *idle_time_display = new IdleSwitchMsDisplayWidget(); | |||
idle_time_display->box.pos = Vec(20, 115); | |||
idle_time_display->box.size = Vec(70, 24); | |||
idle_time_display->value = &module->idleTimeoutMS; | |||
addChild(idle_time_display); | |||
addInput(Port::create<PJ301MPort>(Vec(10, 155.0), Port::INPUT, module, IdleSwitch::TIME_INPUT)); | |||
addParam(ParamWidget::create<Davies1900hBlackKnob>(Vec(38.86, 150.0), module, IdleSwitch::TIME_PARAM, 0.0, 10.0, 0.25)); | |||
addOutput(Port::create<PJ301MPort>(Vec(80, 155.0), Port::OUTPUT, module, IdleSwitch::TIME_OUTPUT)); | |||
IdleSwitchMsDisplayWidget *time_remaining_display = new IdleSwitchMsDisplayWidget(); | |||
time_remaining_display->box.pos = Vec(20, 225); | |||
time_remaining_display->box.size = Vec(70, 24); | |||
time_remaining_display->value = &module->idleTimeLeftMS; | |||
addChild(time_remaining_display); | |||
addOutput(Port::create<PJ301MPort>(Vec(10, 263.0), Port::OUTPUT, module, IdleSwitch::IDLE_START_OUTPUT)); | |||
addOutput(Port::create<PJ301MPort>(Vec(47.5, 263.0), Port::OUTPUT, module, IdleSwitch::IDLE_GATE_OUTPUT)); | |||
addOutput(Port::create<PJ301MPort>(Vec(85, 263.0), Port::OUTPUT, module, IdleSwitch::IDLE_END_OUTPUT)); | |||
addInput(Port::create<PJ301MPort>(Vec(10.0f, 315.0f), Port::INPUT, module, IdleSwitch::SWITCHED_INPUT)); | |||
addOutput(Port::create<PJ301MPort>(Vec(47.5f, 315.0f), Port::OUTPUT, module, IdleSwitch::ON_WHEN_IDLE_OUTPUT)); | |||
addOutput(Port::create<PJ301MPort>(Vec(85.0f, 315.0f), Port::OUTPUT, module, IdleSwitch::OFF_WHEN_IDLE_OUTPUT)); | |||
} | |||
} // namespace rack_plugin_Alikins | |||
using namespace rack_plugin_Alikins; | |||
RACK_PLUGIN_MODEL_INIT(Alikins, IdleSwitch) { | |||
Model *modelIdleSwitch = Model::create<IdleSwitch, IdleSwitchWidget>( | |||
"Alikins", "IdleSwitch", "Idle Switch", SWITCH_TAG , UTILITY_TAG); | |||
return modelIdleSwitch; | |||
} |
@@ -0,0 +1,152 @@ | |||
#include <stdio.h> | |||
#include "alikins.hpp" | |||
namespace rack_plugin_Alikins { | |||
struct MomentaryOnButtons : Module { | |||
enum ParamIds { | |||
BUTTON1_PARAM, | |||
BUTTON2_PARAM, | |||
BUTTON3_PARAM, | |||
BUTTON4_PARAM, | |||
BUTTON5_PARAM, | |||
BUTTON6_PARAM, | |||
BUTTON7_PARAM, | |||
BUTTON8_PARAM, | |||
BUTTON9_PARAM, | |||
BUTTON10_PARAM, | |||
BUTTON11_PARAM, | |||
BUTTON12_PARAM, | |||
BUTTON13_PARAM, | |||
NUM_PARAMS | |||
}; | |||
enum InputIds { | |||
BUTTON1_INPUT, | |||
BUTTON2_INPUT, | |||
BUTTON3_INPUT, | |||
BUTTON4_INPUT, | |||
BUTTON5_INPUT, | |||
BUTTON6_INPUT, | |||
BUTTON7_INPUT, | |||
BUTTON8_INPUT, | |||
BUTTON9_INPUT, | |||
BUTTON10_INPUT, | |||
BUTTON11_INPUT, | |||
BUTTON12_INPUT, | |||
BUTTON13_INPUT, | |||
NUM_INPUTS | |||
}; | |||
enum OutputIds { | |||
BUTTON1_OUTPUT, | |||
BUTTON2_OUTPUT, | |||
BUTTON3_OUTPUT, | |||
BUTTON4_OUTPUT, | |||
BUTTON5_OUTPUT, | |||
BUTTON6_OUTPUT, | |||
BUTTON7_OUTPUT, | |||
BUTTON8_OUTPUT, | |||
BUTTON9_OUTPUT, | |||
BUTTON10_OUTPUT, | |||
BUTTON11_OUTPUT, | |||
BUTTON12_OUTPUT, | |||
BUTTON13_OUTPUT, | |||
NUM_OUTPUTS | |||
}; | |||
enum LightIds { | |||
BLINK1_LIGHT, | |||
BLINK2_LIGHT, | |||
BLINK3_LIGHT, | |||
BLINK4_LIGHT, | |||
BLINK5_LIGHT, | |||
BLINK6_LIGHT, | |||
BLINK7_LIGHT, | |||
BLINK8_LIGHT, | |||
BLINK9_LIGHT, | |||
BLINK10_LIGHT, | |||
BLINK11_LIGHT, | |||
BLINK12_LIGHT, | |||
BLINK13_LIGHT, | |||
NUM_LIGHTS | |||
}; | |||
MomentaryOnButtons() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {} | |||
void step() override; | |||
// For more advanced Module features, read Rack's engine.hpp header file | |||
// - toJson, fromJson: serialization of internal data | |||
// - onSampleRateChange: event triggered by a change of sample rate | |||
// - reset, randomize: implements special behavior when user clicks these from the context menu | |||
}; | |||
void MomentaryOnButtons::step() { | |||
for (int i = 0; i < MOMENTARY_BUTTONS; i++) { | |||
lights[BLINK1_LIGHT + i].setBrightness(0.0); | |||
outputs[BUTTON1_OUTPUT + i].value = 0.0; | |||
if (params[BUTTON1_PARAM + i].value) { | |||
outputs[BUTTON1_OUTPUT + i].value = 5.0; | |||
lights[BLINK1_LIGHT + i].setBrightness(1.0); | |||
} | |||
} | |||
} | |||
struct MomentaryOnButtonsWidget : ModuleWidget { | |||
MomentaryOnButtonsWidget(MomentaryOnButtons *module); | |||
}; | |||
MomentaryOnButtonsWidget::MomentaryOnButtonsWidget(MomentaryOnButtons *module) : ModuleWidget(module) { | |||
box.size = Vec(4 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT); | |||
int x_offset = 0; | |||
int y_offset = 26; | |||
int x_start = 0; | |||
int y_start = 24; | |||
int x_pos = 0; | |||
int y_pos = 0; | |||
int light_radius = 7; | |||
{ | |||
SVGPanel *panel = new SVGPanel(); | |||
panel->box.size = box.size; | |||
panel->setBackground(SVG::load(assetPlugin(plugin, "res/MomentaryOnButtons.svg"))); | |||
addChild(panel); | |||
} | |||
/* | |||
addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0))); | |||
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); | |||
addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
*/ | |||
for (int i = 0; i < MOMENTARY_BUTTONS; i++) { | |||
x_pos = x_start + x_offset; | |||
y_pos = y_start + (i * y_offset); | |||
addParam(ParamWidget::create<LEDButton>(Vec(x_pos + light_radius, y_pos + 3), module, MomentaryOnButtons::BUTTON1_PARAM + i, 0.0, 1.0, 0.0)); | |||
addChild(ModuleLightWidget::create<MediumLight<RedLight>>(Vec(x_pos + 5 + light_radius, y_pos + light_radius), module, MomentaryOnButtons::BLINK1_LIGHT + i)); | |||
addOutput(Port::create<PJ301MPort>(Vec(x_pos + 20 + light_radius, y_pos), Port::OUTPUT, module, MomentaryOnButtons::BUTTON1_OUTPUT + i)); | |||
} | |||
} | |||
} // namespace rack_plugin_Alikins | |||
using namespace rack_plugin_Alikins; | |||
RACK_PLUGIN_MODEL_INIT(Alikins, MomentaryOnButtons) { | |||
Model *modelMomentaryOnButtons = Model::create<MomentaryOnButtons, MomentaryOnButtonsWidget>( | |||
"Alikins", "MomentaryOnButtons", "Momentary On Buttons", UTILITY_TAG); | |||
return modelMomentaryOnButtons; | |||
} | |||
@@ -0,0 +1,49 @@ | |||
#include <sstream> | |||
#include <iomanip> | |||
// From AS DelayPlus.cpp https://github.com/AScustomWorks/AS | |||
struct MsDisplayWidget : TransparentWidget { | |||
float *value; | |||
std::shared_ptr<Font> font; | |||
MsDisplayWidget() { | |||
font = Font::load(assetPlugin(plugin, "res/Segment7Standard.ttf")); | |||
} | |||
void draw(NVGcontext *vg) override { | |||
// Background | |||
// these go to... | |||
NVGcolor backgroundColor = nvgRGB(0x11, 0x11, 0x11); | |||
NVGcolor borderColor = nvgRGB(0xff, 0xff, 0xff); | |||
nvgBeginPath(vg); | |||
nvgRoundedRect(vg, 0.0, 0.0, box.size.x, box.size.y, 3.0); | |||
nvgFillColor(vg, backgroundColor); | |||
nvgFill(vg); | |||
nvgStrokeWidth(vg, 1.0f); | |||
nvgStrokeColor(vg, borderColor); | |||
nvgStroke(vg); | |||
// text | |||
nvgFontSize(vg, 14); | |||
nvgFontFaceId(vg, font->handle); | |||
nvgTextLetterSpacing(vg, 2.5); | |||
std::stringstream to_display; | |||
// to_display << std::setiosflags(std::ios::fixed) << std::right << std::setw(5) << std::setprecision(4) << *value; | |||
to_display << std::setiosflags(std::ios::fixed) << std::left << std::setprecision(4) << *value; | |||
Vec textPos = Vec(1.0f, 19.0f); | |||
NVGcolor textColor = nvgRGB(0x65, 0xf6, 0x78); | |||
nvgFillColor(vg, textColor); | |||
nvgText(vg, textPos.x, textPos.y, to_display.str().c_str(), NULL); | |||
} | |||
}; | |||
@@ -0,0 +1,653 @@ | |||
#include "global_pre.hpp" | |||
#include <stdio.h> | |||
#include <string> | |||
#include <vector> | |||
#include <unordered_map> | |||
#include "alikins.hpp" | |||
#include "ui.hpp" | |||
#include "global_ui.hpp" | |||
namespace rack_plugin_Alikins { | |||
struct SpecificValue : Module | |||
{ | |||
enum ParamIds | |||
{ | |||
VALUE1_PARAM, | |||
OCTAVE_PARAM, | |||
NUM_PARAMS | |||
}; | |||
enum InputIds | |||
{ | |||
VALUE1_INPUT, | |||
NUM_INPUTS | |||
}; | |||
enum OutputIds | |||
{ | |||
VALUE1_OUTPUT, | |||
NUM_OUTPUTS | |||
}; | |||
enum LightIds | |||
{ | |||
NUM_LIGHTS | |||
}; | |||
SpecificValue() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {} | |||
float A440_octave = 4.0f; | |||
void step() override; | |||
// TODO: toJson/fromJson for saving values | |||
float volt_value; | |||
float hz_value; | |||
float period_value; | |||
float cents_value; | |||
}; | |||
struct NoteInfo { | |||
std::string name; | |||
float offset_cents; | |||
}; | |||
// TODO: mv to header | |||
float A440_VOLTAGE = 4.75f; | |||
int A440_MIDI_NUMBER = 69; | |||
// TODO: support other enharmonic names Db G♠A♯? | |||
std::vector<std::string> note_name_vec = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}; | |||
std::unordered_map<std::string, float> gen_note_name_map() { | |||
float volt = -10.0f; | |||
std::string fs = note_name_vec[4]; | |||
std::unordered_map<std::string, float> note_name_map; | |||
// FIXME: add a map of note name (including enharmonic) to voltage offset from C | |||
// then just iterate over it for each octave | |||
for (int i = -10; i <= 10; i++) | |||
{ | |||
for (int j = 0; j < 12; j++) | |||
{ | |||
// debug("oct=%d note=%s volt=%f ", i, note_name_vec[j].c_str(), volt); | |||
note_name_map[stringf("%s%d", note_name_vec[j].c_str(), i)] = volt; | |||
volt += (1.0f / 12.0f); | |||
} | |||
} | |||
return note_name_map; | |||
} | |||
std::unordered_map<std::string, float> note_name_to_volts_map = gen_note_name_map(); | |||
// FIXME: can/should be inline | |||
// FIXME: likely should be a NoteInfo type/struct/object | |||
// These are assuming A440 == A4 == 4.75v | |||
float freq_to_cv(float freq, float a440_octave) { | |||
float volts = log2f(freq / 440.0f * powf(2.0f, A440_VOLTAGE)) - a440_octave; | |||
// debug("freq_to_vc freq=%f a440_octave=%f volts=%f A440_voltage=%f", freq, a440_octave, volts, A440_VOLTAGE); | |||
return volts; | |||
} | |||
float cv_to_freq(float volts, float a440_octave) { | |||
float freq = 440.0f / powf(2.0f, A440_VOLTAGE) * powf(2.0f, volts + a440_octave); | |||
// debug("cv_to_freq freq=%f a440_octave=%f volts=%f A440_voltage=%f", freq, a440_octave, volts, A440_VOLTAGE); | |||
return freq; | |||
} | |||
// can return negative | |||
float volts_of_nearest_note(float volts) { | |||
float res = roundf( (volts * 12.0f) ) / 12.0f; | |||
return res; | |||
} | |||
int volts_to_note(float volts) { | |||
int res = abs(static_cast<int>( roundf( (volts * 12.0f) ) ) ) % 12; | |||
// debug("volts_to_note volts=%f res=%d", volts, res); | |||
return res; | |||
} | |||
int volts_to_octave(float volts, float a440_octave) { | |||
// debug("a440_octave=%f", a440_octave); | |||
int octave = floor(volts + a440_octave); | |||
// debug("volts_to_octaves volts=%f, a440_octave=%f, octave=%d", volts, a440_octave, octave); | |||
return octave; | |||
} | |||
float volts_to_note_cents(float volts, float a440_octave) { | |||
float nearest_note = volts_of_nearest_note(volts); | |||
float cent_volt = 1.0f / 12.0f / 100.0f; | |||
float offset_cents = (volts-nearest_note)/cent_volt; | |||
// debug("volts: %f volts_of_nearest: %f volts-volts_nearest: %f offset_cents %f", | |||
// volts, nearest_note, volts-nearest_note, offset_cents); | |||
return offset_cents; | |||
} | |||
int volts_to_midi(float volts, float a440_octave) { | |||
int midi_note = floor(volts * 12.0f + a440_octave) + 21; | |||
return midi_note; | |||
} | |||
void SpecificValue::step() | |||
{ | |||
A440_octave = params[OCTAVE_PARAM].value; | |||
if (inputs[VALUE1_INPUT].active) { | |||
params[VALUE1_PARAM].value = inputs[VALUE1_INPUT].value; | |||
} | |||
volt_value = params[VALUE1_PARAM].value; | |||
outputs[VALUE1_OUTPUT].value = volt_value; | |||
} | |||
struct FloatField : TextField | |||
{ | |||
float value; | |||
SpecificValue *module; | |||
FloatField(SpecificValue *_module); | |||
void onAction(EventAction &e) override; | |||
void onChange(EventChange &e) override; | |||
float textToVolts(std::string field_text); | |||
std::string voltsToText(float param_volts); | |||
}; | |||
FloatField::FloatField(SpecificValue *_module) | |||
{ | |||
module = _module; | |||
value = module->params[SpecificValue::VALUE1_PARAM].value; | |||
text = voltsToText(value); | |||
} | |||
// TODO: this is really data stuff, so could be in type/struct/class for the data (volt, freq/hz, period/seconds, note_name) | |||
// and instanced and provided to a generic ValueField widget that has-a data type converter thingy | |||
float FloatField::textToVolts(std::string field_text) { | |||
return atof(field_text.c_str()); | |||
} | |||
std::string FloatField::voltsToText(float param_volts){ | |||
return stringf("%0.3f", param_volts); | |||
} | |||
void FloatField::onChange(EventChange &e) { | |||
//debug("FloatField onChange text=%s param=%f", text.c_str(), module->params[SpecificValue::VALUE1_PARAM].value); | |||
if (this != rack::global_ui->widgets.gFocusedWidget) { | |||
std::string new_text = voltsToText(module->params[SpecificValue::VALUE1_PARAM].value); | |||
setText(new_text); | |||
} | |||
} | |||
void FloatField::onAction(EventAction &e) | |||
{ | |||
//debug("FloatField onAction text=%s", text.c_str()); | |||
//update text first? | |||
TextField::onAction(e); | |||
float volts = textToVolts(text); | |||
//debug("FloatField setting volts=%f text=%s", volts, text.c_str()); | |||
module->params[SpecificValue::VALUE1_PARAM].value = volts; | |||
//debug("FloatField onAction2 text=%s volts=%f module->volt_values=%f", | |||
// text.c_str(), volts, module->volt_value); | |||
} | |||
struct HZFloatField : TextField | |||
{ | |||
float value; | |||
SpecificValue *module; | |||
HZFloatField(SpecificValue *_module); | |||
void onChange(EventChange &e) override; | |||
void onAction(EventAction &e) override; | |||
float textToVolts(std::string field_text); | |||
std::string voltsToText(float param_volts); | |||
}; | |||
HZFloatField::HZFloatField(SpecificValue *_module) | |||
{ | |||
module = _module; | |||
} | |||
float HZFloatField::textToVolts(std::string field_text) { | |||
float freq = strtof(text.c_str(), NULL); | |||
return freq_to_cv(freq, module->A440_octave); | |||
} | |||
std::string HZFloatField::voltsToText(float param_volts){ | |||
float freq = cv_to_freq(param_volts, module->A440_octave); | |||
std::string new_text = stringf("%0.*f", freq < 100 ? 4 : 3, freq); | |||
return new_text; | |||
} | |||
void HZFloatField::onChange(EventChange &e) { | |||
//debug("HZFloatField onChange text=%s param=%f", text.c_str(), module->params[SpecificValue::VALUE1_PARAM].value); | |||
//TextField::onChange(e); | |||
if (this != rack::global_ui->widgets.gFocusedWidget) | |||
{ | |||
std::string new_text = voltsToText(module->params[SpecificValue::VALUE1_PARAM].value); | |||
setText(new_text); | |||
} | |||
} | |||
void HZFloatField::onAction(EventAction &e) | |||
{ | |||
//debug("HZFloatField onAction text=%s", text.c_str()); | |||
//update text first? | |||
TextField::onAction(e); | |||
float volts = textToVolts(text); | |||
//debug("HZ FloatField onAction about to set VALUE*_PARAM to volts: %f", volts); | |||
module->params[SpecificValue::VALUE1_PARAM].value = volts; | |||
} | |||
struct SecondsFloatField : TextField { | |||
float value; | |||
SpecificValue *module; | |||
SecondsFloatField(SpecificValue *_module); | |||
void onAction(EventAction &e) override; | |||
void onChange(EventChange &e) override; | |||
float textToVolts(std::string field_text); | |||
std::string voltsToText(float param_volts); | |||
}; | |||
SecondsFloatField::SecondsFloatField(SpecificValue *_module) | |||
{ | |||
module = _module; | |||
} | |||
float SecondsFloatField::textToVolts(std::string field_text) { | |||
float period = strtof(text.c_str(), NULL); | |||
float freq = 1.0f / period; | |||
return freq_to_cv(freq, module->A440_octave); | |||
} | |||
std::string SecondsFloatField::voltsToText(float param_volts){ | |||
float period = 1.0f / cv_to_freq(param_volts, module->A440_octave); | |||
std::string new_text = stringf("%0.*f", period < 100 ? 4 : 3, period); | |||
return new_text; | |||
} | |||
void SecondsFloatField::onChange(EventChange &e) { | |||
//debug("SecondsFloatField onChange text=%s param=%f", text.c_str(), module->params[SpecificValue::VALUE1_PARAM].value); | |||
//TextField::onChange(e); | |||
if (this != rack::global_ui->widgets.gFocusedWidget) | |||
{ | |||
std::string new_text = voltsToText(module->params[SpecificValue::VALUE1_PARAM].value); | |||
setText(new_text); | |||
} | |||
} | |||
void SecondsFloatField::onAction(EventAction &e) { | |||
//debug("SecondsFloatField onAction text=%s", text.c_str()); | |||
//update text first? | |||
TextField::onAction(e); | |||
float volts = textToVolts(text); | |||
//debug("SecondsFloatField onAction about to set VALUE*_PARAM to volts: %f", volts); | |||
module->params[SpecificValue::VALUE1_PARAM].value = volts; | |||
} | |||
struct CentsField : TextField { | |||
float value; | |||
SpecificValue *module; | |||
CentsField(SpecificValue *_module); | |||
void onChange(EventChange &e) override; | |||
void onAction(EventAction &e) override; | |||
}; | |||
CentsField::CentsField(SpecificValue *_module) { | |||
module = _module; | |||
} | |||
void CentsField::onChange(EventChange &e) { | |||
// debug("CentsField onChange"); | |||
float cents = volts_to_note_cents(module->params[SpecificValue::VALUE1_PARAM].value, | |||
module->params[SpecificValue::OCTAVE_PARAM].value); | |||
// debug("CentsField onChange cents: %f", cents); | |||
if (this != rack::global_ui->widgets.gFocusedWidget || fabs(cents) >= 0.50f) | |||
{ | |||
float cents = volts_to_note_cents(module->params[SpecificValue::VALUE1_PARAM].value, | |||
module->params[SpecificValue::OCTAVE_PARAM].value); | |||
std::string new_text = stringf("% 0.2f", cents); | |||
setText(new_text); | |||
} | |||
} | |||
void CentsField::onAction(EventAction &e) { | |||
TextField::onAction(e); | |||
float cents = strtof(text.c_str(), NULL); | |||
// figure what to tweak the current volts | |||
float cent_volt = 1.0f / 12.0f / 100.0f; | |||
float delta_volt = cents * cent_volt; | |||
float nearest_note_voltage = volts_of_nearest_note(module->params[SpecificValue::VALUE1_PARAM].value); | |||
//debug("volts: %f nearest_volts: %f", module->params[SpecificValue::VALUE1_PARAM].value, nearest_note_voltage); | |||
//debug("delta_volt: %+f nearest_note_voltage+delta_volt: %f", delta_volt, nearest_note_voltage, | |||
// nearest_note_voltage + delta_volt); | |||
module->params[SpecificValue::VALUE1_PARAM].value = nearest_note_voltage + delta_volt; | |||
} | |||
struct NoteNameField : TextField { | |||
float value; | |||
SpecificValue *module; | |||
NoteNameField(SpecificValue *_module); | |||
void onChange(EventChange &e) override; | |||
void onAction(EventAction &e) override; | |||
}; | |||
NoteNameField::NoteNameField(SpecificValue *_module) | |||
{ | |||
module = _module; | |||
} | |||
void NoteNameField::onChange(EventChange &e) { | |||
//debug("NoteNameField onChange text=%s param=%f", text.c_str(), module->params[SpecificValue::VALUE1_PARAM].value); | |||
//TextField::onChange(e); | |||
if (this != rack::global_ui->widgets.gFocusedWidget) | |||
{ | |||
float cv_volts = module->params[SpecificValue::VALUE1_PARAM].value; | |||
int octave = volts_to_octave(cv_volts, module->params[SpecificValue::OCTAVE_PARAM].value); | |||
int note_number = volts_to_note(cv_volts); | |||
// float semi_cents = volts_to_note_and_cents(cv_volts, module->params[SpecificValue::OCTAVE_PARAM].value); | |||
// note_info = volts_to_note_info(cv_volts, module->params[SpecificValue::OCTAVE_PARAM].value); | |||
// TODO: modf for oct/fract part, need to get +/- cents from chromatic notes | |||
std::string new_text = stringf("%s%d", note_name_vec[note_number].c_str(), octave); | |||
// debug("foo %f bar %f", ) | |||
setText(new_text); | |||
} | |||
} | |||
void NoteNameField::onAction(EventAction &e) { | |||
//debug("NoteNameField onAction"); | |||
TextField::onAction(e); | |||
// FIXME: Haven't tested but seems like this does a lot. | |||
// FIXME: I suspect just a array of structs with name/freq in it and a linear search makes more sense | |||
// but lets c++ stuff | |||
auto search = note_name_to_volts_map.find(text); | |||
if(search != note_name_to_volts_map.end()) { | |||
/* | |||
debug("note_name_to_volts_map[%s] = %f (%f) %f", text.c_str(), | |||
note_name_to_volts_map[text], | |||
(note_name_to_volts_map[text] - module->A440_octave), | |||
module->A440_octave ); | |||
*/ | |||
module->params[SpecificValue::VALUE1_PARAM].value = note_name_to_volts_map[text] - module->A440_octave; | |||
return; | |||
} | |||
else { | |||
// TODO: change the text color to indicate bogus name? | |||
debug("%s was NOT A VALID note name", text.c_str()); | |||
return; | |||
} | |||
} | |||
struct SmallPurpleTrimpot : Trimpot { | |||
SmallPurpleTrimpot(); | |||
}; | |||
SmallPurpleTrimpot::SmallPurpleTrimpot() : Trimpot() { | |||
setSVG(SVG::load(assetPlugin(plugin, "res/SmallPurpleTrimpot.svg"))); | |||
shadow->blurRadius = 0.0; | |||
shadow->opacity = 0.10; | |||
shadow->box.pos = Vec(0.0, box.size.y * 0.1); | |||
} | |||
struct PurpleTrimpot : Trimpot { | |||
Module *module; | |||
bool initialized = false; | |||
PurpleTrimpot(); | |||
void step() override; | |||
void reset() override; | |||
void randomize() override; | |||
}; | |||
PurpleTrimpot::PurpleTrimpot() : Trimpot() { | |||
setSVG(SVG::load(assetPlugin(plugin, "res/PurpleTrimpot.svg"))); | |||
shadow->blurRadius = 0.0; | |||
shadow->opacity = 0.10; | |||
shadow->box.pos = Vec(0.0, box.size.y * 0.05); | |||
} | |||
// FIXME: if we are getting moving inputs and we are hovering | |||
// over the trimpot, we kind of jitter arround. | |||
// maybe run this via an onChange()? | |||
void PurpleTrimpot::step() { | |||
//debug("paramId=%d this->initialized: %d initialized: %d this->value: %f value: %f param.value: %f", | |||
// paramId, this->initialized, initialized, this->value, value, module->params[paramId].value); | |||
if (this->value != module->params[paramId].value) { | |||
if (this != rack::global_ui->widgets.gHoveredWidget && this->initialized) { | |||
// this->value = module->params[paramId].value; | |||
setValue(module->params[paramId].value); | |||
} else { | |||
module->params[paramId].value = this->value; | |||
this->initialized |= true; | |||
} | |||
EventChange e; | |||
onChange(e); | |||
} | |||
Trimpot::step(); | |||
} | |||
void PurpleTrimpot::reset() { | |||
this->initialized = false; | |||
Trimpot::reset(); | |||
} | |||
void PurpleTrimpot::randomize() { | |||
reset(); | |||
setValue(rescale(randomUniform(), 0.0f, 1.0f, minValue, maxValue)); | |||
} | |||
struct SpecificValueWidget : ModuleWidget | |||
{ | |||
SpecificValueWidget(SpecificValue *module); | |||
void step() override; | |||
void onChange(EventChange &e) override; | |||
float prev_volts = 0.0f; | |||
float prev_octave = 4.0f; | |||
float prev_input = 0.0f; | |||
FloatField *volts_field; | |||
HZFloatField *hz_field; | |||
SecondsFloatField *period_field; | |||
NoteNameField *note_name_field; | |||
CentsField *cents_field; | |||
}; | |||
SpecificValueWidget::SpecificValueWidget(SpecificValue *module) : ModuleWidget(module) | |||
{ | |||
setPanel(SVG::load(assetPlugin(plugin, "res/SpecificValue.svg"))); | |||
// TODO: widget with these children? | |||
float y_baseline = 45.0f; | |||
Vec volt_field_size = Vec(70.0f, 22.0f); | |||
Vec hz_field_size = Vec(70.0, 22.0f); | |||
Vec seconds_field_size = Vec(70.0, 22.0f); | |||
float x_pos = 10.0f; | |||
// debug("adding field %d", i); | |||
y_baseline = 45.0f; | |||
volts_field = new FloatField(module); | |||
volts_field->box.pos = Vec(x_pos, y_baseline); | |||
volts_field->box.size = volt_field_size; | |||
volts_field->value = module->params[SpecificValue::VALUE1_PARAM].value; | |||
addChild(volts_field); | |||
y_baseline = 90.0f; | |||
float h_pos = x_pos; | |||
hz_field = new HZFloatField(module); | |||
hz_field->box.pos = Vec(x_pos, y_baseline); | |||
hz_field->box.size = hz_field_size; | |||
hz_field->value = module->hz_value; | |||
addChild(hz_field); | |||
y_baseline = 135.0f; | |||
period_field = new SecondsFloatField(module); | |||
period_field->box.pos = Vec(h_pos, y_baseline); | |||
period_field->box.size = seconds_field_size; | |||
period_field->value = module->period_value; | |||
addChild(period_field); | |||
y_baseline = 180.0f; | |||
note_name_field = new NoteNameField(module); | |||
note_name_field->box.pos = Vec(x_pos, y_baseline); | |||
note_name_field->box.size = Vec(70.0f, 22.0f); | |||
note_name_field->value = module->volt_value; | |||
addChild(note_name_field); | |||
y_baseline += note_name_field->box.size.y; | |||
y_baseline += 5.0f; | |||
// y_baseline += 20.0f; | |||
cents_field = new CentsField(module); | |||
cents_field->box.pos = Vec(x_pos, y_baseline); | |||
cents_field->box.size = Vec(55.0f, 22.0f); | |||
cents_field->value = module->cents_value; | |||
addChild(cents_field); | |||
// y_baseline += period_field->box.size.y; | |||
y_baseline += 20.0f; | |||
float middle = box.size.x / 2.0f; | |||
float in_port_x = 15.0f; | |||
y_baseline += 24.0f + 12.0f; | |||
Port *value_in_port = Port::create<PJ301MPort>( | |||
Vec(in_port_x, y_baseline), | |||
Port::INPUT, | |||
module, | |||
SpecificValue::VALUE1_INPUT); | |||
//value_in_port->box.pos = Vec(middle - value_in_port->box.size.x / 2, y_baseline); | |||
value_in_port->box.pos = Vec(2.0f, y_baseline); | |||
inputs.push_back(value_in_port); | |||
addChild(value_in_port); | |||
// octave trimpot | |||
SmallPurpleTrimpot *octaveTrimpot = ParamWidget::create<SmallPurpleTrimpot>( | |||
Vec(middle, y_baseline + 2.5f), | |||
module, | |||
SpecificValue::OCTAVE_PARAM, | |||
0.0f, 8.0f, 4.0f); | |||
params.push_back(octaveTrimpot); | |||
octaveTrimpot->box.pos = Vec(middle - octaveTrimpot->box.size.x / 2, y_baseline + 2.5f); | |||
octaveTrimpot->snap = true; | |||
addChild(octaveTrimpot); | |||
float out_port_x = middle + 24.0f; | |||
Port *value_out_port = Port::create<PJ301MPort>( | |||
Vec(out_port_x, y_baseline), | |||
Port::OUTPUT, | |||
module, | |||
SpecificValue::VALUE1_OUTPUT); | |||
outputs.push_back(value_out_port); | |||
value_out_port->box.pos = Vec(box.size.x - value_out_port->box.size.x - 2.0f, y_baseline); | |||
addChild(value_out_port); | |||
y_baseline += value_out_port->box.size.y; | |||
y_baseline += 16.0f; | |||
PurpleTrimpot *trimpot = ParamWidget::create<PurpleTrimpot>( | |||
Vec(middle - 24.0f, y_baseline + 2.5f), | |||
module, | |||
SpecificValue::VALUE1_PARAM, | |||
-10.0f, 10.0f, 0.0f); | |||
//debug(" trimpot: dv: %f v: %f p.value: %f", trimpot->defaultValue, trimpot->value, | |||
// module->params[SpecificValue::VALUE1_PARAM].value); | |||
params.push_back(trimpot); | |||
addChild(trimpot); | |||
} | |||
void SpecificValueWidget::step() { | |||
ModuleWidget::step(); | |||
if (prev_volts != module->params[SpecificValue::VALUE1_PARAM].value || | |||
prev_octave != module->params[SpecificValue::OCTAVE_PARAM].value || | |||
prev_input != module->params[SpecificValue::VALUE1_INPUT].value) { | |||
// debug("SpVWidget step - emitting EventChange / onChange prev_volts=%f param=%f", | |||
// prev_volts, module->params[SpecificValue::VALUE1_PARAM].value); | |||
prev_volts = module->params[SpecificValue::VALUE1_PARAM].value; | |||
prev_octave = module->params[SpecificValue::OCTAVE_PARAM].value; | |||
prev_input = module->params[SpecificValue::VALUE1_INPUT].value; | |||
EventChange e; | |||
onChange(e); | |||
} | |||
} | |||
void SpecificValueWidget::onChange(EventChange &e) { | |||
// debug("SpvWidget onChange"); | |||
ModuleWidget::onChange(e); | |||
volts_field->onChange(e); | |||
hz_field->onChange(e); | |||
period_field->onChange(e); | |||
note_name_field->onChange(e); | |||
cents_field->onChange(e); | |||
} | |||
} // namespace rack_plugin_Alikins | |||
using namespace rack_plugin_Alikins; | |||
RACK_PLUGIN_MODEL_INIT(Alikins, SpecificValue) { | |||
Model *modelSpecificValue = Model::create<SpecificValue, SpecificValueWidget>( | |||
"Alikins", "SpecificValue", "Specific Values", UTILITY_TAG); | |||
return modelSpecificValue; | |||
} |
@@ -0,0 +1,22 @@ | |||
#include "alikins.hpp" | |||
RACK_PLUGIN_MODEL_DECLARE(Alikins, IdleSwitch); | |||
RACK_PLUGIN_MODEL_DECLARE(Alikins, MomentaryOnButtons); | |||
RACK_PLUGIN_MODEL_DECLARE(Alikins, BigMuteButton); | |||
RACK_PLUGIN_MODEL_DECLARE(Alikins, ColorPanel); | |||
RACK_PLUGIN_MODEL_DECLARE(Alikins, GateLength); | |||
RACK_PLUGIN_MODEL_DECLARE(Alikins, SpecificValue); | |||
RACK_PLUGIN_INIT(Alikins) { | |||
RACK_PLUGIN_INIT_ID(); | |||
RACK_PLUGIN_INIT_WEBSITE("https://github.com/alikins/Alikins-rack-plugins"); | |||
RACK_PLUGIN_INIT_MANUAL("https://github.com/alikins/Alikins-rack-plugins/blob/master/README.md"); | |||
RACK_PLUGIN_MODEL_ADD(Alikins, IdleSwitch); | |||
RACK_PLUGIN_MODEL_ADD(Alikins, MomentaryOnButtons); | |||
RACK_PLUGIN_MODEL_ADD(Alikins, BigMuteButton); | |||
RACK_PLUGIN_MODEL_ADD(Alikins, ColorPanel); | |||
RACK_PLUGIN_MODEL_ADD(Alikins, GateLength); | |||
RACK_PLUGIN_MODEL_ADD(Alikins, SpecificValue); | |||
} |
@@ -0,0 +1,12 @@ | |||
#include "rack.hpp" | |||
const int MOMENTARY_BUTTONS = 13; | |||
const int INPUT_SOURCES = 1; | |||
const int GATE_LENGTH_INPUTS = 5; | |||
using namespace rack; | |||
RACK_PLUGIN_DECLARE(Alikins); | |||
#ifdef USE_VST2 | |||
#define plugin "Alikins" | |||
#endif // USE_VST2 |
@@ -0,0 +1,20 @@ | |||
{ | |||
"slug": "BaconMusic", | |||
"name": "Bacon Music", | |||
"author": "@baconpaul", | |||
"license": "Apache-2.0", | |||
"version": "0.5.0", | |||
"homepage": "https://github.com/baconpaul/BaconPlugs", | |||
"manual": "https://github.com/baconpaul/BaconPlugs/blob/master/README.md", | |||
"source": "https://github.com/baconpaul/BaconPlugs", | |||
"downloads": { | |||
"lin": { | |||
"download": "https://github.com/baconpaul/BaconPlugs/files/1611795/BaconMusic-0.5.0-lin.zip", | |||
"sha256": "89338dda1f75b3dfc0044df2177a93e397b35ac41e7dabd9d4c382502bcf8c78" | |||
}, | |||
"mac": { | |||
"download": "https://github.com/baconpaul/BaconPlugs/files/1611780/BaconMusic-0.5.0-mac.zip", | |||
"sha256": "67a2b6c5f9f9a8974ffdfac359f8d2ceed639afb18373302e6e952379c18f171" | |||
} | |||
} | |||
} |
@@ -0,0 +1,26 @@ | |||
Copyright 2017-2018, Paul Walker | |||
Licensed under the Apache License, Version 2.0 (the "License"); | |||
you may not use this file except in compliance with the License. | |||
You may obtain a copy of the License at | |||
http://www.apache.org/licenses/LICENSE-2.0 | |||
Unless required by applicable law or agreed to in writing, software | |||
distributed under the License is distributed on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
See the License for the specific language governing permissions and | |||
limitations under the License. | |||
--------------------------------------------------------------------- | |||
The file "res/Keypunch029.json" which implements the KeyPunch029 font | |||
used in the dot matrix widget is Copyright (c) 2017 Stewart C. Russell | |||
and is released under the SIL Open Font License 1.1 | |||
http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL) | |||
The original work can be found at the below location: | |||
http://scruss.com/blog/2017/03/21/keypunch029-for-all-your-punched-card-font-needs/. |
@@ -0,0 +1,45 @@ | |||
SLUG = BaconMusic | |||
VERSION = 0.6.2 | |||
RELEASE_BRANCH = release_0.6.2 | |||
# FLAGS will be passed to both the C and C++ compiler | |||
FLAGS += | |||
CFLAGS += | |||
CXXFLAGS += -Werror | |||
# Careful about linking to shared libraries, since you can't assume much about the user's environment and library search path. | |||
# Static libraries are fine. | |||
LDFLAGS += | |||
# Add .cpp and .c files to the build | |||
SOURCES += $(wildcard src/*.cpp) | |||
# Add files to the ZIP package when running `make dist` | |||
# The compiled plugin is automatically added. | |||
DISTRIBUTABLES += $(wildcard LICENSE*) res docs patches README.md | |||
# Include the VCV plugin Makefile framework | |||
RACK_DIR ?= ../.. | |||
include $(RACK_DIR)/plugin.mk | |||
shadist: dist | |||
openssl sha256 dist/$(SLUG)-$(VERSION)-$(ARCH).zip > dist/$(SLUG)-$(VERSION)-$(ARCH).zip.sha256 | |||
COMMUNITY_ISSUE=https://github.com/VCVRack/community/issues/433 | |||
issue_blurb: dist | |||
git diff --exit-code | |||
git diff --cached --exit-code | |||
@echo | |||
@echo "Paste this into github issue " $(COMMUNITY_ISSUE) | |||
@echo | |||
@echo "* Version: v$(VERSION)" | |||
@echo "* Transaction: " `git rev-parse HEAD` | |||
@echo "* Branch: " `git rev-parse --abbrev-ref HEAD` | |||
push_git: | |||
@echo "Pushing current branch to git and dropbox" | |||
git push dropbox `git rev-parse --abbrev-ref HEAD` | |||
git push github `git rev-parse --abbrev-ref HEAD` |
@@ -0,0 +1,286 @@ | |||
# The Bacon Music VCVRack Modules | |||
"Bacon Music" is my set of EuroRack style plugins for | |||
[VCVRack](http://www.vcvrack.com). The modules are mostly inspired by me noodling around, and | |||
they sort of fall into a few groups: | |||
* Control voltage manipulation on 1v/oct signals to do things like glissando and musical quantization and a polyrhytmic clock; | |||
* Classic synth algorithms, including an implementation of the NES oscillators and a Karplus Strong implementation. | |||
* and finally, not very useful modulations and distortions and stuff. | |||
All the source is here, released under an Apache 2.0 license. You are free to use | |||
these modules as you see fit. If you happen to use them to make music you want to share, please | |||
do let me know, either by raising an issue on this github or by tagging me on twitter (@baconpaul) or | |||
soundcloud (@baconpaul). | |||
As of the release of Rack 0.6, these plugins will be built as part of the | |||
[v2 community distribution](https://github.com/VCVRack/community/tree/v2) so they should just be | |||
available for you to try. A massive thanks to the [Rack community team](https://github.com/VCVRack/community/issues/248) | |||
for maintaining these builds. | |||
I'm happy to hear any feedback and bug reports. The best way | |||
to reach me is to just open a github issue [right here on github](https://github.com/baconpaul/BaconPlugs/issues). | |||
Finally, all the [sample patches I used to make the screen-shots](https://github.com/baconpaul/BaconPlugs/tree/master/patches) are | |||
on the github here. | |||
I hope you enjoy the plugins! | |||
## Control Voltage Manipulation and Clocks | |||
### HarMoNee | |||
HarMoNee is a plugin which takes a 1v/oct CV signal and outputs two signals, | |||
one which is the original, and the second which is modified by a musical amount, | |||
like a minor 3rd. It spans plus or minus one octave, and is controlled by toggles. | |||
The toggles are additive. So if you want a fourth, choose a major third and a half step | |||
both. You get the idea. | |||
<a href="https://baconpaul.github.io/audio/HarMoNee.mp3"> | |||
<img src="docs/HarMoNee.png" alt="ExampleQuanteyes Patch"> | |||
<br> | |||
<img src="docs/SpeakerIconSmall.png" alt="Hear HarMoNee Sample"> | |||
</a> | |||
### Glissinator | |||
Glissinator takes a control voltage which is undergoing change and smooths out that | |||
change with a linear glissando. It is not triggered by a gate, just by differences | |||
in the input CV. It never jumps discontinuously, so if the CV changes "target" value | |||
mid-gliss, the whole thing turns around. The slider will give you between 0 and 1 seconds | |||
of gliss time. There is a gate output which is +10v when the module is glissing and | |||
0 when it isn't. Note that the Glissinator is a constant time gliss, not a constant | |||
slope gliss (which is what a slew limiter would do). A future version may have a switch | |||
to pick between the modes. | |||
Here's a sample patch. | |||
<a href="https://baconpaul.github.io/audio/Glissinator.mp3"> | |||
<img src="docs/Glissinator.png" alt="ExampleQuanteyes Patch"> | |||
<br> | |||
<img src="docs/SpeakerIconSmall.png" alt="Hear Glissinator Sample"> | |||
</a> | |||
### QuantEyes | |||
QuantEyes takes a CV signal and clamps it to certain values 1/12 of a volt apart. | |||
Functionally this means that CV signals which are changing on input will be clamped to | |||
a chromatic scale on output if all the notes are activated. But you can also deactivate | |||
certain notes to allow you to pick scales to which you quantize. | |||
Since quantizing to scales could be useful for multiple things driving oscillators, | |||
you can apply this quantization to up to 3 inputs using the same scale. | |||
Finally, you can choose where the "root" note is in CV space. The default is that | |||
1 volt is the "R" note, but if you set root to 3, then 1 3/12 volts would be R. If you don't | |||
understand this, send in a changing signal, select only the R note in the set of LED buttons, | |||
and then twiddle the root note. | |||
Here's a (pretty cool sounding, I think) patch which combines QuantEyes with the | |||
Glissinator and HarMoNee modules. | |||
<a href="https://baconpaul.github.io/audio/QuantEyes.mp3"> | |||
<img src="docs/QuantEyes.png" alt="ExampleQuanteyes Patch"> | |||
<br> | |||
<img src="docs/SpeakerIconSmall.png" alt="Hear QuantEyes Sample"> | |||
</a> | |||
### PolyGnome | |||
PolyGnome is a polyrhytmic clock generator. It can output up to 5 clock signals with strict | |||
fractional relationships between them. There is one clock which is the "1/1" clock controlled by | |||
the speed settings implemented exactly like the clock rate controls in SEQ3. Then there are 4 | |||
other clocks which are fractionally adjusted clocks. This way you can do a 1/1 vs a 5/3 vs a 4/5 | |||
polyrhythm easily in your rack. | |||
Here's an example which uses this to drive 3 independent oscillator / envelope sets all of which have | |||
pitch set through the QuantEyes module. | |||
<a href="https://baconpaul.github.io/audio/PolyGnome.mp3"> | |||
<img src="docs/PolyGnome.png" alt="Example PolyGnome Patch"> | |||
<br> | |||
<img src="docs/SpeakerIconSmall.png" alt="Hear PolyGnome Sample"> | |||
</a> | |||
## Classic and 8-bit algorithms | |||
### ChipWaves | |||
ChipWaves implements the NES triangle and pulse wave generator. It would have been | |||
impossible to implement without the careful description of the algorithms at | |||
[the NES Dev Wiki](http://wiki.nesdev.com/w/index.php/APU_Triangle). | |||
The NES has two tonal oscillators, a triangle wave and a pulse. The pulse has 4 | |||
different duty cycles. The triangle is a fixed wave. | |||
They are tuned by wavelength with values up to 2^11 clock cycles. Rather than | |||
expose this very digital interface, though, I've set up the inputs to be tuned | |||
to CV in exactly the same way as VCO-1. So the conversion from 1v/oct signal | |||
to the 2^11 different wavelengths based on the simulated clock frequency (I chose NTSC) | |||
is all done for you. | |||
Basically, it just works like an oscillator. Drop it in and go chip crazy. The sample | |||
patch runs it mixed along with a VCO-1 so I could check tuning. Here's how I did it. | |||
<a href="https://baconpaul.github.io/audio/ChipWaves.mp3"> | |||
<img src="docs/ChipWaves.png" alt="Example ChipWaves Patch"> | |||
<br> | |||
<img src="docs/SpeakerIconSmall.png" alt="Hear ChipWaves Sample"> | |||
</a> | |||
### ChipNoise | |||
ChipNoise implements the NES noise generator without the NES envelope. It would have been | |||
impossible to implement without the careful description of the noise algorithm at | |||
[the NES Dev WIKI](http://wiki.nesdev.com/w/index.php/APU_Noise). I also appreciate the | |||
[lengthy conversation with @alto77](https://github.com/baconpaul/BaconPlugs/issues/6) which helped | |||
identify a bug in the 0.6.1 release and add a new feature. | |||
The NES noise system has 16 different frequencies; and two modes. The two modes generate either | |||
a long pseudo-random pattern or a set of short pseudo-random patterns. That long pattern is just | |||
long, but the short patterns are either 93 or 31 bits long. There are 351 distinct 93 bit patterns | |||
and a single 31 bit pattern. | |||
The sequence controls allow you to pick these patterns. If set at "long" then you choose the longest | |||
pattern. If set at short, then either you have the 31 long pattern or one of the 93 patterns. Which of | |||
the 93 patterns you pick is chosen by the "which 93 seq" knob. | |||
This is a lot of information. If you just play with it you'll get the idea. | |||
Here's a simple patch. | |||
<a href="https://baconpaul.github.io/audio/ChipNoise.mp3"> | |||
<img src="docs/ChipNoise.png" alt="Example ChipNoise Patch"> | |||
<br> | |||
<img src="docs/SpeakerIconSmall.png" alt="Hear ChipNoise Sample"> | |||
</a> | |||
### ChipYourWaves | |||
ChipYourWaves is a chip-like oscillator which doesn't actually exist in the old NES hardware. | |||
The way the NES triangle wave generator works is it oscillates across a 32-sample-wide waveform of values | |||
between 0 and 15, with 7/8 being the "zero point". This is set up as a ramp 0 1 2 ... 15 15 14 ... 0 | |||
and then tuned and oscillated. If you want this waveform, just use the ChipWaves module up above. | |||
But I got to thinking. That waveform is something we could change and make even buzzier grunky chip-like | |||
sounds. Perhaps. So I wrote a module which is the same internal implementation as the NES triangle wave | |||
generator in ChipWaves but which has an editor which lets you pick each of the 32 values. Then you can | |||
hear it. And see if you like it. | |||
To set the values just click or drag on the LED vertical. It's pretty intuitive when you use it, I think. But | |||
so you get an idea, here's the sample patch which sets a fixed frequency and hooks it up to the scope so you can | |||
see the generated waveform is, indeed, the bits you draw in the LED-like controls. | |||
 | |||
Oh if you have any idea what to put in all that blank space at the top of the module, by the way, please do | |||
just raise a github issue and let me know! | |||
### KarplusStrongPoly | |||
The [Karplus-Strong algorithm](https://en.m.wikipedia.org/wiki/Karplus–Strong_string_synthesis) is one of the | |||
earlier methods to simulate plucked string instruments. The KarplusStrongPoly module implements a polyphonic | |||
voides implementation of this. The module maps to the algorithm fairly cleanly. It's probably easier that you | |||
just play with it, using the sample patch shown below. | |||
<a href="https://baconpaul.github.io/audio/KarplusStrongPoly.mp3"> | |||
<img src="docs/KarplusStrongPoly.png" alt="Example ChipNoise Patch"> | |||
<br> | |||
<img src="docs/SpeakerIconSmall.png" alt="Hear ChipNoise Sample"> | |||
</a> | |||
There's one really important thing to know about this module. Unlike more traditional voltage controlled oscillators | |||
which always produce output and are then fed into envelopes and stuff, the KarplusStrongPoly module needs to be | |||
triggered with a gate signal to produce any sound. When it is triggered it will snap all the parameters set on | |||
the front panel and play that voice until it fades. The system is configured to play upto 32 voices and will | |||
voice steal beyond that. But since Rack adds a 1 sample delay to all its signals as they go through each module, | |||
if you trigger from SEQ-3 and use a frequency you have modified, the trigger will "beat" the modified signal. | |||
So adding a few sample delay to your trigger may be approrpriate. There's a really simple SampleDelay module | |||
which ships with this plugin set if you want to do that. | |||
I've only implemented one filter so far, so the only control which does anything in the filter space is the "A" | |||
knob and CV input. If/as I add more that will get way more rich, kind of like initial packet is now. | |||
Finally I think the algorithm is stable under all possible front panel configurations. There's certainly | |||
regimes of parameters in the C++ which can break the synthesis, though. So if you get an | |||
odd or growing sound, let me know the configuration which did it in a github issue and I'll put a check | |||
in the widget to synth snap appropriately. | |||
## Distortions and Modulations and so on | |||
### ALingADing | |||
ALingADing is a simulation of a Ring Modulator based on [this paper by Julian Parker](http://recherche.ircam.fr/pub/dafx11/Papers/66_e.pdf) and | |||
then taking some shortcuts. | |||
Rather than following Parker's use of a few polynomials to simulate his diode, | |||
I basically use an implementation of a softmax, eye-balling the parameters to roughly meet the figure in his | |||
paper. The only control is a wet/dry mix (where wet is the signal modulated by carrier | |||
and dry is just the signal). Sloppy, sure, but it sounds kinda cool. Here's a sample patch. | |||
<a href="https://baconpaul.github.io/audio/ALingADing.mp3"> | |||
<img src="docs/ALingADing.png" alt="ExampleQuanteyes Patch"> | |||
<br> | |||
<img src="docs/SpeakerIconSmall.png" alt="Here QuantEyes"> | |||
</a> | |||
### Bitulator | |||
Bitulator is really just me screwing around with some math on the input. It has two | |||
functions. Firstly, it "quantizes" to a smaller number of "bits", but does it in a | |||
weird and sloppy way of basically making sure there are only N values possible in the | |||
output. Apply this to a sine wave with a low value of N and you get sort of stacked squares. | |||
Secondly it has a gross digital clipping amplifier. Basically signal is the clamp of input times | |||
param. Apply this to a sine wave and turn it up and you get pretty much a perfect square. | |||
Combine them for grunky grunk noise. Dumb, but fun. Here's a sample patch. | |||
 | |||
### SampleDelay | |||
This is an incredibly simple module. All it does is add an n-sample digital delay at the | |||
engine clock speed. Radically non-analog, I know. But it's useful since Rack adds a 1 sample | |||
delay in each module hop to do things like triggering the Karplus Strong poly. | |||
 | |||
## Credits and Comments | |||
The Keypunch font used in the textual display LED widget comes from | |||
[Stewart C. Russell's blog](http://scruss.com/blog/2017/03/21/keypunch029-for-all-your-punched-card-font-needs/). | |||
The font is Copyright 2017 Stewart C. Russell and is released under | |||
the [SIL Open Font License 1.1](http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL). | |||
The slug name ('BaconMusic') is different than the repo name ('BaconPlugs'), somewhat confusingly. | |||
When I made my git repo I had no idea really how anything worked or if I'd write anything. | |||
I was thinking "Hey I'm writing a collection of plugins for this software right". When I went | |||
with my first release, Andrew Rust pointed out that "BaconPlugs" wasn't a very good name for my | |||
plugin and it's collected modules. He did it very politely, of course, and so I changed it to "Bacon Music" | |||
for the slug name. The repo is still called BaconPlugs though, because that's more trouble to change than | |||
I can handle. | |||
## License | |||
Copyright © 2017-2018 Paul Walker | |||
Licensed under the Apache License, Version 2.0 (the "License"); | |||
you may not use this file except in compliance with the License. | |||
You may obtain a copy of the License at | |||
http://www.apache.org/licenses/LICENSE-2.0 | |||
Unless required by applicable law or agreed to in writing, software | |||
distributed under the License is distributed on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
See the License for the specific language governing permissions and | |||
limitations under the License. | |||
@@ -0,0 +1,18 @@ | |||
# Code of Conduct | |||
The [Contrubitor Covenant](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html) is an excellent code of | |||
conduct, and one by which I'd like to hope I carry myself. Please read it and follow its guidance when | |||
working with this project. | |||
BaconPlugs is mostly a one-person project, but I do welcome and appreciate all feedback. | |||
Please use the | |||
github issue mechanism to start discussions or leave comments. Your comments will be more gladly | |||
recieved if they are constructive, polite, | |||
and empathetic, and I will aspire to be constructive, polite, and empathetic in my responses. | |||
I cannot, however, guarantee that my responses will always be very *timely*. | |||
This is just a little side project for me! | |||
I hope that you are also as excited to make music using open tools as I am, and hope that | |||
you want to make that a positive, inclusive experience. | |||
@@ -0,0 +1,19 @@ | |||
# Contribution guide | |||
I welcome contributions to these plugins and am happy to review pull requests with changes. | |||
I'm mostly writing this document so I can get a "checkmark" on the github opensource project | |||
health checklist. I don't think any of you will do big development on this plug set, but I am | |||
happy if you want to! | |||
Here's some thoughts on how that would work, but the project is small enough that we can just sort of sort it | |||
in an issue if you are interested in doing something big. | |||
* Before you start developing, please open a github issue to chat with me about the change you | |||
want to make. While you don't have to do this, it's probably best since then at least I'm not | |||
surprised by your PR. *GitHub Issues are by far the best way to reach me about this project*. | |||
* The plugin is licensed under Apache 2. If you aren't comfortable working under that license, | |||
then we should chat. I'm happy for you to retain copyright for your changes if you want and | |||
they are substantial (vs something tiny I could just fix myself if you mentioned it). | |||
* Please see the [code of conduct](CODE_OF_CONDUCT.md) for community guidelines. | |||
@@ -0,0 +1,14 @@ | |||
Things for future releases perhaps? | |||
Plugins: | |||
* LSFR more general noise maker (sort of started in the ChipSym) | |||
* CleverClipper (branch "CleverClipper" already exists on mac and in dbox) | |||
* Lilypond or ABC-like music phrase generator | |||
* This paper looks fun https://pdfs.semanticscholar.org/68fa/415703af07042d4301ac6cb3722c87f5bc43.pdf | |||
* Karplus Strong Mono | |||
* Gates in QuantEyes | |||
* Add appropriate BaconHelpButtons to everything | |||
* Keep or kill that sloppy chorus | |||
@@ -0,0 +1,34 @@ | |||
# BaconMusic v0.6.2 Release Notes | |||
* Added sound samples for most of the modules in the README. | |||
* ChipNoise | |||
* Fixed an important bit length bug that @alto777 found. | |||
* Added control of short sequence selection; either the 31 long or which of the 93s. | |||
* For more, see https://github.com/baconpaul/BaconPlugs/issues/6 | |||
* Update doc for ChipNoise | |||
* KarplusStrongPoly | |||
* A new module which implements KarplusStrong plucked instrument synthesis polyphonically. | |||
* QuantEyes | |||
* Don't trigger lights if there is no input | |||
* PolyGnome | |||
* Added this new widget which allows for exact fractional polyrhythms in clocks. | |||
* Glissinator | |||
* Fixed a bug found by @alto777 where a rapid decrease in the shift time would lock the module | |||
* Added a gate output for when you are in the gliss | |||
* Refactored to allow standalone tests of the stepper | |||
* SampleDelay | |||
* Just a teensy utility plugin to do sample accurate delays between 1 and 99 samples | |||
* I added a DMP Text Widget using Stewart C. Russell's Keypunch029 font (see README.md for license and links). | |||
* As well as a single digit, there's a multi digit sevent segment light. Take a look at ChipNoise for how to use it. | |||
* Background got some new features, like colored labels and filled rounded rects in the API | |||
(required) | |||
* Code Review all diffs from master | |||
@@ -0,0 +1,16 @@ | |||
ALL_OBJ= \ | |||
src/ALingADing.o \ | |||
src/BaconPlugs.o \ | |||
src/Bitulator.o \ | |||
src/BlankWidget.o \ | |||
src/ChipNoise.o \ | |||
src/ChipWaves.o \ | |||
src/ChipYourWave.o \ | |||
src/Components.o \ | |||
src/Glissinator.o \ | |||
src/HarMoNee.o \ | |||
src/KarplusStrongPoly.o \ | |||
src/PolyGnome.o \ | |||
src/QuantEyes.o \ | |||
src/SampleDelay.o \ | |||
src/SortaChorus.o |
@@ -0,0 +1,9 @@ | |||
SLUG=BaconMusic | |||
include ../../../../dep/yac/install_msvc.mk | |||
EXTRAFLAGS+= -DBUILD_SORTACHORUS | |||
include make.objects | |||
include ../../../build_plugin.mk |
@@ -0,0 +1,505 @@ | |||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |||
<!-- Created with Inkscape (http://www.inkscape.org/) --> | |||
<svg | |||
xmlns:dc="http://purl.org/dc/elements/1.1/" | |||
xmlns:cc="http://creativecommons.org/ns#" | |||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | |||
xmlns:svg="http://www.w3.org/2000/svg" | |||
xmlns="http://www.w3.org/2000/svg" | |||
xmlns:xlink="http://www.w3.org/1999/xlink" | |||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | |||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | |||
width="20" | |||
height="41" | |||
viewBox="0 0 20 41.000001" | |||
id="svg2" | |||
version="1.1" | |||
inkscape:version="0.91 r13725" | |||
sodipodi:docname="BaconSliderHandle.svg"> | |||
<defs | |||
id="defs4" /> | |||
<sodipodi:namedview | |||
id="base" | |||
pagecolor="#ffffff" | |||
bordercolor="#666666" | |||
borderopacity="1.0" | |||
inkscape:pageopacity="0.0" | |||
inkscape:pageshadow="2" | |||
inkscape:zoom="11.313708" | |||
inkscape:cx="-2.4107142" | |||
inkscape:cy="5" | |||
inkscape:document-units="px" | |||
inkscape:current-layer="layer4" | |||
showgrid="false" | |||
units="px" | |||
inkscape:window-width="1813" | |||
inkscape:window-height="1289" | |||
inkscape:window-x="93" | |||
inkscape:window-y="47" | |||
inkscape:window-maximized="0" /> | |||
<metadata | |||
id="metadata7"> | |||
<rdf:RDF> | |||
<cc:Work | |||
rdf:about=""> | |||
<dc:format>image/svg+xml</dc:format> | |||
<dc:type | |||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | |||
<dc:title /> | |||
</cc:Work> | |||
</rdf:RDF> | |||
</metadata> | |||
<g | |||
inkscape:label="Background" | |||
inkscape:groupmode="layer" | |||
id="layer1" | |||
transform="translate(0,-1011.3621)" | |||
style="display:none"> | |||
<image | |||
y="1011.4246" | |||
x="0.0625" | |||
id="image4238" | |||
xlink:href="data:image/png;base64,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 | |||
lyteDr0SvJoczftD/o8Drw1fn/rT9c+uscixoTfCN1Nvd7xTeVf7fsn7jvHA8acTKROTH/I/qnys | |||
+2T3qfNzxOcXk+lfcF9Kvhp9bfvm9+3xVMrUlIAlZM2MAijE4fh4AN4icwIlCgBqDwDEFbPz8oxB | |||
szP+DIH/xLMz9YzZAFDVDkBoNgD+SCxFoj7isq4ABCIe6gpgKyup/8NE8VaWs1qkFmQ0KZ6aeofM | |||
mzgjAL72T01NtkxNfa1Bin0IQPvE7Jw+bXJHAXA9x4i08r8Tnwj+1f4CYSsHOyGy924AAAAJcEhZ | |||
cwAAFiUAABYlAUlSJPAAAAIEaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8eDp4bXBtZXRhIHht | |||
bG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA1LjQuMCI+CiAgIDxyZGY6 | |||
UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5z | |||
IyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5z | |||
OmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIgogICAgICAgICAgICB4bWxuczp0 | |||
aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDxleGlmOlBpeGVs | |||
WURpbWVuc2lvbj4yNzg8L2V4aWY6UGl4ZWxZRGltZW5zaW9uPgogICAgICAgICA8ZXhpZjpQaXhl | |||
bFhEaW1lbnNpb24+MTMwPC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPHRpZmY6T3Jp | |||
ZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAg | |||
IDwvcmRmOlJERj4KPC94OnhtcG1ldGE+ChDqyCAAADZqSURBVHgB7Z1Zl9w6sp2zqlLDGXq0ve6D | |||
X+z//5fs5Sdfr9t9ZpVUo6q8v70jSGRKOspMSaW+3UAViSkQQO7NAEEQJM/u7+8fN3aPm8dHBSu2 | |||
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 | |||
UOJGK9U7AAAAAElFTkSuQmCC | |||
" | |||
preserveAspectRatio="none" | |||
height="40.75" | |||
width="19.875" /> | |||
</g> | |||
<g | |||
inkscape:groupmode="layer" | |||
id="layer3" | |||
inkscape:label="RedFill"> | |||
<rect | |||
style="fill:#888888;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.84399998;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" | |||
id="rect4271" | |||
width="20.240932" | |||
height="41.365746" | |||
x="0.088388346" | |||
y="0.076194942" /> | |||
</g> | |||
<g | |||
inkscape:groupmode="layer" | |||
id="layer4" | |||
inkscape:label="BlackBar"> | |||
<rect | |||
style="fill:#222222;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.84399998;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" | |||
id="rect4274" | |||
width="20.152542" | |||
height="5.833632" | |||
x="0.088388398" | |||
y="17.400312" /> | |||
<path | |||
style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" | |||
d="m 0.1767767,21.554563 19.8873783,0 0.08839,0" | |||
id="path4276" | |||
inkscape:connector-curvature="0" /> | |||
</g> | |||
<g | |||
inkscape:groupmode="layer" | |||
id="layer2" | |||
inkscape:label="Images" | |||
style="display:inline"> | |||
<rect | |||
style="fill:#a7a7a7;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.84399998;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" | |||
id="rect4242" | |||
width="20.166054" | |||
height="5.3428516" | |||
x="0.0625" | |||
y="36" /> | |||
<rect | |||
style="fill:#fcfbfc;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.84399998;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" | |||
id="rect4244" | |||
width="20.125" | |||
height="2.0946698" | |||
x="0.125" | |||
y="33.780331" /> | |||
<rect | |||
style="fill:#f0f0f0;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.84399998;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" | |||
id="rect4246" | |||
width="20.25" | |||
height="3.4937184" | |||
x="0.088388346" | |||
y="29.484835" /> | |||
<rect | |||
style="fill:#ececec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.84399998;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" | |||
id="rect4248" | |||
width="20.082108" | |||
height="3.670495" | |||
x="0.10983495" | |||
y="24.969669" /> | |||
<rect | |||
style="fill:#e0e0e0;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.84399998;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" | |||
id="rect4250" | |||
width="20.125" | |||
height="2.625" | |||
x="0.051776696" | |||
y="21.625" /> | |||
<rect | |||
style="fill:#d0d0d0;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.84399998;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" | |||
id="rect4252" | |||
width="20.140165" | |||
height="2.5" | |||
x="0.073223308" | |||
y="16.801777" /> | |||
<rect | |||
style="fill:#b00000;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:0.84399998;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" | |||
id="rect4254" | |||
width="19.75" | |||
height="2.625" | |||
x="0.5" | |||
y="12.375" /> | |||
<rect | |||
style="fill:#a6a6a6;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.84399998;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" | |||
id="rect4256" | |||
width="20.25" | |||
height="3.125" | |||
x="-0.051776696" | |||
y="8" /> | |||
<rect | |||
style="fill:#a0a0a0;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.84399998;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" | |||
id="rect4258" | |||
width="20.301777" | |||
height="1.25" | |||
x="-0.125" | |||
y="5.875" /> | |||
<rect | |||
style="fill:#f0f0f0;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.84399998;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" | |||
id="rect4260" | |||
width="20.1875" | |||
height="5.5624785" | |||
x="0.0625" | |||
y="0.062521584" /> | |||
<rect | |||
style="fill:#bcbcff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:0.84399998;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" | |||
id="rect4262" | |||
width="20.417707" | |||
height="3.0935922" | |||
x="0" | |||
y="12.008622" /> | |||
<rect | |||
style="fill:#bcbcff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:0.84399998;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" | |||
id="rect4264" | |||
width="6.5407376" | |||
height="1.767767" | |||
x="0.26516503" | |||
y="13.864778" /> | |||
<rect | |||
style="fill:#bcbcff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:0.84399998;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" | |||
id="rect4266" | |||
width="16.705399" | |||
height="3.7123106" | |||
x="0.35355338" | |||
y="12.185399" /> | |||
<rect | |||
style="fill:#bcbcbc;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.84399998;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" | |||
id="rect4268" | |||
width="20.240932" | |||
height="4.154253" | |||
x="0.088388346" | |||
y="11.831845" /> | |||
</g> | |||
</svg> |
@@ -0,0 +1,360 @@ | |||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |||
<!-- Created with Inkscape (http://www.inkscape.org/) --> | |||
<svg | |||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | |||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | |||
xmlns="http://www.w3.org/2000/svg" | |||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | |||
xmlns:cc="http://creativecommons.org/ns#" | |||
xmlns:xlink="http://www.w3.org/1999/xlink" | |||
xmlns:dc="http://purl.org/dc/elements/1.1/" | |||
xmlns:svg="http://www.w3.org/2000/svg" | |||
xmlns:ns1="http://sozi.baierouge.fr" | |||
id="svg4888" | |||
sodipodi:docname="error_button.svg" | |||
viewBox="0 0 400 400" | |||
version="1.1" | |||
inkscape:version="0.48.0 r9654" | |||
> | |||
<defs | |||
id="defs4890" | |||
> | |||
<linearGradient | |||
id="linearGradient8435" | |||
y2="367.88" | |||
gradientUnits="userSpaceOnUse" | |||
y1="287.49" | |||
x2="315.47" | |||
x1="241.41" | |||
inkscape:collect="always" | |||
> | |||
<stop | |||
id="stop4158-3" | |||
style="stop-color:#ffffff" | |||
offset="0" | |||
/> | |||
<stop | |||
id="stop4160-1" | |||
style="stop-color:#ffffff;stop-opacity:0" | |||
offset="1" | |||
/> | |||
</linearGradient | |||
> | |||
<filter | |||
id="filter6126-9" | |||
color-interpolation-filters="sRGB" | |||
inkscape:collect="always" | |||
> | |||
<feGaussianBlur | |||
id="feGaussianBlur6128-8" | |||
stdDeviation="0.53035713" | |||
inkscape:collect="always" | |||
/> | |||
</filter | |||
> | |||
<linearGradient | |||
id="linearGradient8437" | |||
y2="391.45" | |||
gradientUnits="userSpaceOnUse" | |||
y1="300.86" | |||
x2="342" | |||
x1="275.61" | |||
inkscape:collect="always" | |||
> | |||
<stop | |||
id="stop7173" | |||
style="stop-color:#00112b" | |||
offset="0" | |||
/> | |||
<stop | |||
id="stop7175" | |||
style="stop-color:#0055d4" | |||
offset="1" | |||
/> | |||
</linearGradient | |||
> | |||
<radialGradient | |||
id="radialGradient8439" | |||
gradientUnits="userSpaceOnUse" | |||
cx="312.78" | |||
cy="386.57" | |||
r="53.036" | |||
gradientTransform="matrix(-.59327 -.59327 .71505 -.71505 243.27 849.03)" | |||
inkscape:collect="always" | |||
> | |||
<stop | |||
id="stop7113-50" | |||
style="stop-color:#ffffff;stop-opacity:.40816" | |||
offset="0" | |||
/> | |||
<stop | |||
id="stop7115-28" | |||
style="stop-color:#ffffff;stop-opacity:0" | |||
offset="1" | |||
/> | |||
</radialGradient | |||
> | |||
<filter | |||
id="filter10443" | |||
color-interpolation-filters="sRGB" | |||
inkscape:collect="always" | |||
> | |||
<feGaussianBlur | |||
id="feGaussianBlur10445" | |||
stdDeviation="1.3973845" | |||
inkscape:collect="always" | |||
/> | |||
</filter | |||
> | |||
<linearGradient | |||
id="linearGradient6858" | |||
y2="338.82" | |||
gradientUnits="userSpaceOnUse" | |||
x2="300.27" | |||
gradientTransform="matrix(2.7754 0 0 2.7754 -606.45 -52.271)" | |||
y1="286.67" | |||
x1="255.32" | |||
inkscape:collect="always" | |||
> | |||
<stop | |||
id="stop4150-0" | |||
style="stop-color:#ffffff" | |||
offset="0" | |||
/> | |||
<stop | |||
id="stop4152-2" | |||
style="stop-color:#ffffff;stop-opacity:0" | |||
offset="1" | |||
/> | |||
</linearGradient | |||
> | |||
</defs | |||
> | |||
<sodipodi:namedview | |||
id="base" | |||
bordercolor="#666666" | |||
inkscape:pageshadow="2" | |||
inkscape:window-y="-8" | |||
pagecolor="#ffffff" | |||
inkscape:window-height="988" | |||
inkscape:window-maximized="1" | |||
inkscape:zoom="0.70710678" | |||
inkscape:window-x="-8" | |||
showgrid="false" | |||
borderopacity="1.0" | |||
inkscape:current-layer="layer1" | |||
inkscape:cx="304.89409" | |||
inkscape:cy="341.52186" | |||
inkscape:window-width="1680" | |||
inkscape:pageopacity="0.0" | |||
inkscape:document-units="px" | |||
/> | |||
<g | |||
id="layer1" | |||
inkscape:label="Layer 1" | |||
inkscape:groupmode="layer" | |||
transform="translate(0 -652.36)" | |||
> | |||
<g | |||
id="g6872" | |||
> | |||
<path | |||
id="path7262" | |||
sodipodi:rx="53.035713" | |||
sodipodi:ry="53.035713" | |||
style="fill:#999999" | |||
sodipodi:type="arc" | |||
d="m356.79 346.11a53.036 53.036 0 1 1 -106.07 0 53.036 53.036 0 1 1 106.07 0z" | |||
transform="matrix(2.8283 0 0 2.8283 -653.01 -129.79)" | |||
sodipodi:cy="346.11218" | |||
sodipodi:cx="303.75" | |||
/> | |||
<path | |||
id="path7264" | |||
sodipodi:rx="53.035713" | |||
sodipodi:ry="53.035713" | |||
style="fill:url(#linearGradient8435)" | |||
sodipodi:type="arc" | |||
d="m356.79 346.11a53.036 53.036 0 1 1 -106.07 0 53.036 53.036 0 1 1 106.07 0z" | |||
transform="matrix(2.7878 0 0 2.7878 -640.71 -115.77)" | |||
sodipodi:cy="346.11218" | |||
sodipodi:cx="303.75" | |||
/> | |||
<path | |||
id="path7266" | |||
sodipodi:rx="53.035713" | |||
sodipodi:ry="53.035713" | |||
style="filter:url(#filter6126-9);fill:#ececec" | |||
sodipodi:type="arc" | |||
d="m356.79 346.11a53.036 53.036 0 1 1 -106.07 0 53.036 53.036 0 1 1 106.07 0z" | |||
transform="matrix(2.5378 0 0 2.5378 -564.77 -29.245)" | |||
sodipodi:cy="346.11218" | |||
sodipodi:cx="303.75" | |||
/> | |||
<path | |||
id="path7268" | |||
sodipodi:rx="53.035713" | |||
sodipodi:ry="53.035713" | |||
style="filter:url(#filter6126-9);fill:#999999" | |||
sodipodi:type="arc" | |||
d="m356.79 346.11a53.036 53.036 0 1 1 -106.07 0 53.036 53.036 0 1 1 106.07 0z" | |||
transform="matrix(2.4356 0 0 2.4356 -533.74 6.1126)" | |||
sodipodi:cy="346.11218" | |||
sodipodi:cx="303.75" | |||
/> | |||
<path | |||
id="path7270" | |||
sodipodi:rx="53.035713" | |||
sodipodi:ry="53.035713" | |||
style="fill:url(#linearGradient8437)" | |||
sodipodi:type="arc" | |||
d="m356.79 346.11a53.036 53.036 0 1 1 -106.07 0 53.036 53.036 0 1 1 106.07 0z" | |||
transform="matrix(2.3412 0 0 2.3412 -505.05 38.806)" | |||
sodipodi:cy="346.11218" | |||
sodipodi:cx="303.75" | |||
/> | |||
<path | |||
id="path7272" | |||
sodipodi:rx="53.035713" | |||
sodipodi:ry="53.035713" | |||
style="fill:url(#radialGradient8439)" | |||
sodipodi:type="arc" | |||
d="m356.79 346.11a53.036 53.036 0 1 1 -106.07 0 53.036 53.036 0 1 1 106.07 0z" | |||
transform="matrix(2.2028 0 0 2.2028 -459.36 92.554)" | |||
sodipodi:cy="346.11218" | |||
sodipodi:cx="303.75" | |||
/> | |||
<path | |||
id="path7274" | |||
d="m203.16 732.39c-38.727 0.87355-76.074 21.376-96.845 57.353-22.341 38.696-19.505 85.022 3.3539 120.07 2.4783-77.078 61.391-135.13 134.58-130.89 23.993 1.3918 46.64 9.2967 66.408 22.052-9.4642-21.643-25.615-40.536-47.626-53.244-18.845-10.88-39.582-15.802-59.868-15.344z" | |||
style="opacity:.31771;fill:url(#linearGradient6858)" | |||
inkscape:connector-curvature="0" | |||
/> | |||
<g | |||
id="text7350" | |||
style="filter:url(#filter10443);fill:#1a1a1a" | |||
transform="matrix(2.9051 0 0 2.9051 -1408.6 -93.103)" | |||
> | |||
<path | |||
id="path6867" | |||
style="" | |||
d="m546.92 335.78c0.12959-2.1773 0.24624-3.7326 0.34993-4.6657 0.10367-0.93312 0.24623-1.7431 0.42769-2.4301 0.18143-0.68688 0.40824-1.296 0.68042-1.8274 0.27215-0.53135 0.66096-1.1275 1.1664-1.7885 0.50544-0.66095 1.147-1.4062 1.9246-2.2357 0.77761-0.82943 1.8533-1.944 3.2271-3.3438l1.2831-1.322c0.44063-0.41469 0.83592-0.84238 1.1859-1.2831 0.34991-0.44062 0.6156-0.82294 0.79707-1.147 0.18142-0.32397 0.31102-0.62206 0.38881-0.89426 0.0777-0.27213 0.11662-0.55078 0.11664-0.83595-0.00002-1.3219-0.71284-2.3199-2.1385-2.9938-1.4257-0.67389-3.4475-1.0109-6.0654-1.0109-3.6548 0.00004-7.8799 0.77766-12.675 2.3329l1.2053-6.0654c0.1296-0.69981 0.28513-1.5617 0.46658-2.5856 0.18144-1.0238 0.3888-2.3522 0.62209-3.9853 5.7025-0.82941 10.926-1.2441 15.669-1.2442 3.1364 0.00005 5.9812 0.23982 8.5344 0.7193 2.5532 0.47958 4.7823 1.3026 6.6875 2.4689 1.9051 1.1665 3.3632 2.5532 4.3741 4.1603s1.5163 3.3309 1.5164 5.1712c-0.00004 0.77766-0.0778 1.5682-0.23328 2.3718-0.15557 0.80358-0.37589 1.5682-0.66098 2.294-0.28517 0.72581-0.70638 1.5358-1.2636 2.4301-0.55734 0.8943-1.2118 1.795-1.9635 2.7022-0.75173 0.90726-1.3674 1.6006-1.8468 2.0801-0.47957 0.47956-1.3868 1.3544-2.7217 2.6245s-2.3523 2.2681-3.0522 2.9938c-0.69989 0.7258-1.2701 1.4127-1.7108 2.0607-0.44068 0.64804-0.78413 1.3414-1.0304 2.0801-0.24628 0.73876-0.47308 1.808-0.68042 3.2077-2.618-0.0777-4.9768-0.11663-7.0764-0.11665-2.0478 0.00002-4.5491 0.0259-7.504 0.0778zm-1.7885 16.758 0.38881-4.6657 0.50546-6.2987c0.0259-0.28512 0.0648-0.98498 0.11664-2.0996 3.2919 0.0519 5.9877 0.0778 8.0873 0.0778 2.2551 0.00001 4.899-0.0259 7.9317-0.0778-0.25924 2.955-0.42772 5.4693-0.50546 7.5429-0.0778 2.0737-0.16851 3.914-0.27216 5.5211-3.4993-0.0518-6.1173-0.0778-7.854-0.0778-1.8404 0-4.6398 0.0259-8.3983 0.0778z" | |||
/> | |||
</g | |||
> | |||
<g | |||
id="text8454" | |||
style="fill:#ffffff" | |||
transform="translate(-100)" | |||
> | |||
<path | |||
id="path6870" | |||
d="m281.32 881.31c0.36286-6.0965 0.68946-10.451 0.9798-13.064 0.29028-2.6128 0.68946-4.8808 1.1975-6.8042 0.50801-1.9232 1.1431-3.6288 1.9052-5.1168 0.76204-1.4878 1.8507-3.1571 3.266-5.0079 1.4152-1.8507 3.2115-3.9373 5.3889-6.2599 2.1773-2.3224 5.1893-5.4433 9.036-9.3626l3.5926-3.7015c1.2338-1.1612 2.3406-2.3587 3.3204-3.5926 0.97975-1.2337 1.7237-2.3042 2.2318-3.2116 0.50798-0.90713 0.87087-1.7418 1.0887-2.504 0.21767-0.76196 0.32654-1.5422 0.3266-2.3406-0.00006-3.7014-1.996-6.4956-5.9877-8.3828-3.9918-1.8869-9.6529-2.8304-16.983-2.8305-10.234 0.00012-22.064 2.1775-35.491 6.532l3.3749-16.983c0.36288-1.9595 0.79835-4.3727 1.3064-7.2397 0.50804-2.8667 1.0887-6.5863 1.7419-11.159 15.967-2.3224 30.592-3.4836 43.873-3.4837 8.7819 0.00015 16.747 0.67149 23.896 2.014 7.1488 1.3428 13.391 3.6472 18.725 6.913 5.3344 3.2662 9.4169 7.1491 12.248 11.649 2.8304 4.5 4.2457 9.3264 4.2458 14.479-0.00012 2.1775-0.21785 4.3911-0.65321 6.6409-0.43558 2.25-1.0525 4.3911-1.8507 6.4232-0.79847 2.0323-1.9779 4.3004-3.5382 6.8042-1.5605 2.504-3.3931 5.0261-5.4978 7.5663-2.1049 2.5403-3.8286 4.4818-5.1712 5.8244-1.3428 1.3428-3.883 3.7923-7.6207 7.3485-3.7378 3.5564-6.5865 6.3506-8.5461 8.3828-1.9597 2.0322-3.5564 3.9556-4.7902 5.77-1.2339 1.8145-2.1956 3.756-2.885 5.8244-0.68956 2.0685-1.3246 5.0624-1.9052 8.9815-7.3304-0.21768-13.935-0.32655-19.814-0.3266-5.7337 0.00005-12.737 0.0726-21.011 0.21774zm-5.0079 46.922 1.0887-13.064 1.4153-17.636c0.0726-0.79833 0.18141-2.7579 0.3266-5.8788 9.2174 0.14519 16.765 0.21777 22.644 0.21773 6.3142 0.00004 13.717-0.0725 22.209-0.21773-0.72585 8.2739-1.1976 15.314-1.4153 21.12-0.21781 5.8062-0.47183 10.959-0.76207 15.459-9.7981-0.14515-17.128-0.21773-21.991-0.21773-5.1531 0-12.992 0.0726-23.515 0.21773z" | |||
inkscape:connector-curvature="0" | |||
/> | |||
</g | |||
> | |||
</g | |||
> | |||
</g | |||
> | |||
<metadata | |||
> | |||
<rdf:RDF | |||
> | |||
<cc:Work | |||
> | |||
<dc:format | |||
>image/svg+xml</dc:format | |||
> | |||
<dc:type | |||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" | |||
/> | |||
<cc:license | |||
rdf:resource="http://creativecommons.org/licenses/publicdomain/" | |||
/> | |||
<dc:publisher | |||
> | |||
<cc:Agent | |||
rdf:about="http://openclipart.org/" | |||
> | |||
<dc:title | |||
>Openclipart</dc:title | |||
> | |||
</cc:Agent | |||
> | |||
</dc:publisher | |||
> | |||
<dc:title | |||
>Question Button</dc:title | |||
> | |||
<dc:date | |||
>2011-02-24T17:49:39</dc:date | |||
> | |||
<dc:description | |||
/> | |||
<dc:source | |||
>https://openclipart.org/detail/122449/question-button-by-ricardomaia</dc:source | |||
> | |||
<dc:creator | |||
> | |||
<cc:Agent | |||
> | |||
<dc:title | |||
>ricardomaia</dc:title | |||
> | |||
</cc:Agent | |||
> | |||
</dc:creator | |||
> | |||
<dc:subject | |||
> | |||
<rdf:Bag | |||
> | |||
<rdf:li | |||
>blue</rdf:li | |||
> | |||
<rdf:li | |||
>button</rdf:li | |||
> | |||
<rdf:li | |||
>circle</rdf:li | |||
> | |||
<rdf:li | |||
>confirm</rdf:li | |||
> | |||
<rdf:li | |||
>help</rdf:li | |||
> | |||
<rdf:li | |||
>question</rdf:li | |||
> | |||
<rdf:li | |||
>round</rdf:li | |||
> | |||
</rdf:Bag | |||
> | |||
</dc:subject | |||
> | |||
</cc:Work | |||
> | |||
<cc:License | |||
rdf:about="http://creativecommons.org/licenses/publicdomain/" | |||
> | |||
<cc:permits | |||
rdf:resource="http://creativecommons.org/ns#Reproduction" | |||
/> | |||
<cc:permits | |||
rdf:resource="http://creativecommons.org/ns#Distribution" | |||
/> | |||
<cc:permits | |||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" | |||
/> | |||
</cc:License | |||
> | |||
</rdf:RDF | |||
> | |||
</metadata | |||
> | |||
</svg | |||
> |
@@ -0,0 +1,249 @@ | |||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |||
<!-- Created with Inkscape (http://www.inkscape.org/) --> | |||
<svg | |||
xmlns:dc="http://purl.org/dc/elements/1.1/" | |||
xmlns:cc="http://creativecommons.org/ns#" | |||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | |||
xmlns:svg="http://www.w3.org/2000/svg" | |||
xmlns="http://www.w3.org/2000/svg" | |||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | |||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | |||
id="svg4888" | |||
sodipodi:docname="HelpActiveSmall.svg" | |||
viewBox="0 0 20 20" | |||
version="1.1" | |||
inkscape:version="0.91 r13725" | |||
width="20" | |||
height="20"> | |||
<defs | |||
id="defs4890"> | |||
<linearGradient | |||
id="linearGradient8435" | |||
y2="367.88" | |||
gradientUnits="userSpaceOnUse" | |||
y1="287.48999" | |||
x2="315.47" | |||
x1="241.41" | |||
inkscape:collect="always"> | |||
<stop | |||
id="stop4158-3" | |||
style="stop-color:#ffffff" | |||
offset="0" /> | |||
<stop | |||
id="stop4160-1" | |||
style="stop-color:#ffffff;stop-opacity:0" | |||
offset="1" /> | |||
</linearGradient> | |||
<filter | |||
id="filter6126-9" | |||
inkscape:collect="always" | |||
style="color-interpolation-filters:sRGB"> | |||
<feGaussianBlur | |||
id="feGaussianBlur6128-8" | |||
stdDeviation="0.53035713" | |||
inkscape:collect="always" /> | |||
</filter> | |||
<linearGradient | |||
id="linearGradient8437" | |||
y2="391.45001" | |||
gradientUnits="userSpaceOnUse" | |||
y1="300.85999" | |||
x2="342" | |||
x1="275.60999" | |||
inkscape:collect="always"> | |||
<stop | |||
id="stop7173" | |||
style="stop-color:#00112b" | |||
offset="0" /> | |||
<stop | |||
id="stop7175" | |||
style="stop-color:#0055d4" | |||
offset="1" /> | |||
</linearGradient> | |||
<radialGradient | |||
id="radialGradient8439" | |||
gradientUnits="userSpaceOnUse" | |||
cx="312.78" | |||
cy="386.57001" | |||
r="53.035999" | |||
gradientTransform="matrix(-0.59327,-0.59327,0.71505,-0.71505,243.27,849.03)" | |||
inkscape:collect="always"> | |||
<stop | |||
id="stop7113-50" | |||
style="stop-color:#ffffff;stop-opacity:.40816" | |||
offset="0" /> | |||
<stop | |||
id="stop7115-28" | |||
style="stop-color:#ffffff;stop-opacity:0" | |||
offset="1" /> | |||
</radialGradient> | |||
<filter | |||
id="filter10443" | |||
inkscape:collect="always" | |||
style="color-interpolation-filters:sRGB"> | |||
<feGaussianBlur | |||
id="feGaussianBlur10445" | |||
stdDeviation="1.3973845" | |||
inkscape:collect="always" /> | |||
</filter> | |||
<linearGradient | |||
id="linearGradient6858" | |||
y2="338.82001" | |||
gradientUnits="userSpaceOnUse" | |||
x2="300.26999" | |||
gradientTransform="matrix(2.7754,0,0,2.7754,-606.45,-52.271)" | |||
y1="286.67001" | |||
x1="255.32001" | |||
inkscape:collect="always"> | |||
<stop | |||
id="stop4150-0" | |||
style="stop-color:#ffffff" | |||
offset="0" /> | |||
<stop | |||
id="stop4152-2" | |||
style="stop-color:#ffffff;stop-opacity:0" | |||
offset="1" /> | |||
</linearGradient> | |||
</defs> | |||
<sodipodi:namedview | |||
id="base" | |||
bordercolor="#666666" | |||
inkscape:pageshadow="2" | |||
inkscape:window-y="1" | |||
pagecolor="#ffffff" | |||
inkscape:window-height="1845" | |||
inkscape:window-maximized="1" | |||
inkscape:zoom="50.862829" | |||
inkscape:window-x="0" | |||
showgrid="false" | |||
borderopacity="1.0" | |||
inkscape:current-layer="layer1" | |||
inkscape:cx="10.925276" | |||
inkscape:cy="9.494275" | |||
inkscape:window-width="3360" | |||
inkscape:pageopacity="0.0" | |||
inkscape:document-units="px" /> | |||
<g | |||
id="layer1" | |||
inkscape:label="Layer 1" | |||
inkscape:groupmode="layer" | |||
transform="translate(0,-1032.36)"> | |||
<g | |||
id="g6872" | |||
transform="matrix(0.05,0,0,0.05,-0.42108026,999.761)"> | |||
<circle | |||
id="path7262" | |||
style="fill:#999999" | |||
transform="matrix(2.8283,0,0,2.8283,-653.01,-129.79)" | |||
cx="303.75" | |||
cy="346.11218" | |||
r="53.035713" /> | |||
<circle | |||
id="path7264" | |||
style="fill:url(#linearGradient8435)" | |||
transform="matrix(2.7878,0,0,2.7878,-640.71,-115.77)" | |||
cx="303.75" | |||
cy="346.11218" | |||
r="53.035713" /> | |||
<circle | |||
id="path7266" | |||
style="fill:#ececec;filter:url(#filter6126-9)" | |||
transform="matrix(2.5378,0,0,2.5378,-564.77,-29.245)" | |||
cx="303.75" | |||
cy="346.11218" | |||
r="53.035713" /> | |||
<circle | |||
id="path7268" | |||
style="fill:#999999;filter:url(#filter6126-9)" | |||
transform="matrix(2.4356,0,0,2.4356,-533.74,6.1126)" | |||
cx="303.75" | |||
cy="346.11218" | |||
r="53.035713" /> | |||
<circle | |||
id="path7270" | |||
style="fill:url(#linearGradient8437)" | |||
transform="matrix(2.3412,0,0,2.3412,-505.05,38.806)" | |||
cx="303.75" | |||
cy="346.11218" | |||
r="53.035713" /> | |||
<circle | |||
id="path7272" | |||
style="fill:url(#radialGradient8439)" | |||
transform="matrix(2.2028,0,0,2.2028,-459.36,92.554)" | |||
cx="303.75" | |||
cy="346.11218" | |||
r="53.035713" /> | |||
<path | |||
id="path7274" | |||
d="m 203.16,732.39 c -38.727,0.87355 -76.074,21.376 -96.845,57.353 -22.341,38.696 -19.505,85.022 3.3539,120.07 2.4783,-77.078 61.391,-135.13 134.58,-130.89 23.993,1.3918 46.64,9.2967 66.408,22.052 -9.4642,-21.643 -25.615,-40.536 -47.626,-53.244 -18.845,-10.88 -39.582,-15.802 -59.868,-15.344 z" | |||
style="opacity:0.31771001;fill:url(#linearGradient6858)" | |||
inkscape:connector-curvature="0" /> | |||
<g | |||
id="text7350" | |||
style="fill:#1a1a1a;filter:url(#filter10443)" | |||
transform="matrix(2.9051,0,0,2.9051,-1408.6,-93.103)"> | |||
<path | |||
id="path6867" | |||
d="m 546.92,335.78 c 0.12959,-2.1773 0.24624,-3.7326 0.34993,-4.6657 0.10367,-0.93312 0.24623,-1.7431 0.42769,-2.4301 0.18143,-0.68688 0.40824,-1.296 0.68042,-1.8274 0.27215,-0.53135 0.66096,-1.1275 1.1664,-1.7885 0.50544,-0.66095 1.147,-1.4062 1.9246,-2.2357 0.77761,-0.82943 1.8533,-1.944 3.2271,-3.3438 l 1.2831,-1.322 c 0.44063,-0.41469 0.83592,-0.84238 1.1859,-1.2831 0.34991,-0.44062 0.6156,-0.82294 0.79707,-1.147 0.18142,-0.32397 0.31102,-0.62206 0.38881,-0.89426 0.0777,-0.27213 0.11662,-0.55078 0.11664,-0.83595 -2e-5,-1.3219 -0.71284,-2.3199 -2.1385,-2.9938 -1.4257,-0.67389 -3.4475,-1.0109 -6.0654,-1.0109 -3.6548,4e-5 -7.8799,0.77766 -12.675,2.3329 l 1.2053,-6.0654 c 0.1296,-0.69981 0.28513,-1.5617 0.46658,-2.5856 0.18144,-1.0238 0.3888,-2.3522 0.62209,-3.9853 5.7025,-0.82941 10.926,-1.2441 15.669,-1.2442 3.1364,5e-5 5.9812,0.23982 8.5344,0.7193 2.5532,0.47958 4.7823,1.3026 6.6875,2.4689 1.9051,1.1665 3.3632,2.5532 4.3741,4.1603 1.0109,1.6071 1.5163,3.3309 1.5164,5.1712 -4e-5,0.77766 -0.0778,1.5682 -0.23328,2.3718 -0.15557,0.80358 -0.37589,1.5682 -0.66098,2.294 -0.28517,0.72581 -0.70638,1.5358 -1.2636,2.4301 -0.55734,0.8943 -1.2118,1.795 -1.9635,2.7022 -0.75173,0.90726 -1.3674,1.6006 -1.8468,2.0801 -0.47957,0.47956 -1.3868,1.3544 -2.7217,2.6245 -1.3349,1.2701 -2.3523,2.2681 -3.0522,2.9938 -0.69989,0.7258 -1.2701,1.4127 -1.7108,2.0607 -0.44068,0.64804 -0.78413,1.3414 -1.0304,2.0801 -0.24628,0.73876 -0.47308,1.808 -0.68042,3.2077 -2.618,-0.0777 -4.9768,-0.11663 -7.0764,-0.11665 -2.0478,2e-5 -4.5491,0.0259 -7.504,0.0778 z m -1.7885,16.758 0.38881,-4.6657 0.50546,-6.2987 c 0.0259,-0.28512 0.0648,-0.98498 0.11664,-2.0996 3.2919,0.0519 5.9877,0.0778 8.0873,0.0778 2.2551,10e-6 4.899,-0.0259 7.9317,-0.0778 -0.25924,2.955 -0.42772,5.4693 -0.50546,7.5429 -0.0778,2.0737 -0.16851,3.914 -0.27216,5.5211 -3.4993,-0.0518 -6.1173,-0.0778 -7.854,-0.0778 -1.8404,0 -4.6398,0.0259 -8.3983,0.0778 z" | |||
inkscape:connector-curvature="0" /> | |||
</g> | |||
<g | |||
id="text8454" | |||
style="fill:#ffffff" | |||
transform="translate(-100,0)"> | |||
<path | |||
id="path6870" | |||
d="m 281.32,881.31 c 0.36286,-6.0965 0.68946,-10.451 0.9798,-13.064 0.29028,-2.6128 0.68946,-4.8808 1.1975,-6.8042 0.50801,-1.9232 1.1431,-3.6288 1.9052,-5.1168 0.76204,-1.4878 1.8507,-3.1571 3.266,-5.0079 1.4152,-1.8507 3.2115,-3.9373 5.3889,-6.2599 2.1773,-2.3224 5.1893,-5.4433 9.036,-9.3626 l 3.5926,-3.7015 c 1.2338,-1.1612 2.3406,-2.3587 3.3204,-3.5926 0.97975,-1.2337 1.7237,-2.3042 2.2318,-3.2116 0.50798,-0.90713 0.87087,-1.7418 1.0887,-2.504 0.21767,-0.76196 0.32654,-1.5422 0.3266,-2.3406 -6e-5,-3.7014 -1.996,-6.4956 -5.9877,-8.3828 -3.9918,-1.8869 -9.6529,-2.8304 -16.983,-2.8305 -10.234,1.2e-4 -22.064,2.1775 -35.491,6.532 l 3.3749,-16.983 c 0.36288,-1.9595 0.79835,-4.3727 1.3064,-7.2397 0.50804,-2.8667 1.0887,-6.5863 1.7419,-11.159 15.967,-2.3224 30.592,-3.4836 43.873,-3.4837 8.7819,1.5e-4 16.747,0.67149 23.896,2.014 7.1488,1.3428 13.391,3.6472 18.725,6.913 5.3344,3.2662 9.4169,7.1491 12.248,11.649 2.8304,4.5 4.2457,9.3264 4.2458,14.479 -1.2e-4,2.1775 -0.21785,4.3911 -0.65321,6.6409 -0.43558,2.25 -1.0525,4.3911 -1.8507,6.4232 -0.79847,2.0323 -1.9779,4.3004 -3.5382,6.8042 -1.5605,2.504 -3.3931,5.0261 -5.4978,7.5663 -2.1049,2.5403 -3.8286,4.4818 -5.1712,5.8244 -1.3428,1.3428 -3.883,3.7923 -7.6207,7.3485 -3.7378,3.5564 -6.5865,6.3506 -8.5461,8.3828 -1.9597,2.0322 -3.5564,3.9556 -4.7902,5.77 -1.2339,1.8145 -2.1956,3.756 -2.885,5.8244 -0.68956,2.0685 -1.3246,5.0624 -1.9052,8.9815 -7.3304,-0.21768 -13.935,-0.32655 -19.814,-0.3266 -5.7337,5e-5 -12.737,0.0726 -21.011,0.21774 z m -5.0079,46.922 1.0887,-13.064 1.4153,-17.636 c 0.0726,-0.79833 0.18141,-2.7579 0.3266,-5.8788 9.2174,0.14519 16.765,0.21777 22.644,0.21773 6.3142,4e-5 13.717,-0.0725 22.209,-0.21773 -0.72585,8.2739 -1.1976,15.314 -1.4153,21.12 -0.21781,5.8062 -0.47183,10.959 -0.76207,15.459 -9.7981,-0.14515 -17.128,-0.21773 -21.991,-0.21773 -5.1531,0 -12.992,0.0726 -23.515,0.21773 z" | |||
inkscape:connector-curvature="0" /> | |||
</g> | |||
</g> | |||
</g> | |||
<metadata | |||
id="metadata34"> | |||
<rdf:RDF> | |||
<cc:Work> | |||
<dc:format>image/svg+xml</dc:format> | |||
<dc:type | |||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | |||
<cc:license | |||
rdf:resource="http://creativecommons.org/publicdomain/zero/1.0/" /> | |||
<dc:publisher> | |||
<cc:Agent | |||
rdf:about="http://openclipart.org/"> | |||
<dc:title>Openclipart</dc:title> | |||
</cc:Agent> | |||
</dc:publisher> | |||
<dc:title></dc:title> | |||
<dc:date>2011-02-24T17:49:39</dc:date> | |||
<dc:description /> | |||
<dc:source>https://openclipart.org/detail/122449/question-button-by-ricardomaia</dc:source> | |||
<dc:creator> | |||
<cc:Agent> | |||
<dc:title>ricardomaia</dc:title> | |||
</cc:Agent> | |||
</dc:creator> | |||
<dc:subject> | |||
<rdf:Bag> | |||
<rdf:li>blue</rdf:li> | |||
<rdf:li>button</rdf:li> | |||
<rdf:li>circle</rdf:li> | |||
<rdf:li>confirm</rdf:li> | |||
<rdf:li>help</rdf:li> | |||
<rdf:li>question</rdf:li> | |||
<rdf:li>round</rdf:li> | |||
</rdf:Bag> | |||
</dc:subject> | |||
</cc:Work> | |||
<cc:License | |||
rdf:about="http://creativecommons.org/publicdomain/zero/1.0/"> | |||
<cc:permits | |||
rdf:resource="http://creativecommons.org/ns#Reproduction" /> | |||
<cc:permits | |||
rdf:resource="http://creativecommons.org/ns#Distribution" /> | |||
<cc:permits | |||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" /> | |||
</cc:License> | |||
</rdf:RDF> | |||
</metadata> | |||
</svg> |
@@ -0,0 +1,249 @@ | |||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |||
<!-- Created with Inkscape (http://www.inkscape.org/) --> | |||
<svg | |||
xmlns:dc="http://purl.org/dc/elements/1.1/" | |||
xmlns:cc="http://creativecommons.org/ns#" | |||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | |||
xmlns:svg="http://www.w3.org/2000/svg" | |||
xmlns="http://www.w3.org/2000/svg" | |||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | |||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | |||
id="svg4888" | |||
sodipodi:docname="HelpGraySmall.svg" | |||
viewBox="0 0 20 20" | |||
version="1.1" | |||
inkscape:version="0.91 r13725" | |||
width="20" | |||
height="20"> | |||
<sodipodi:namedview | |||
id="base" | |||
bordercolor="#666666" | |||
inkscape:pageshadow="2" | |||
inkscape:window-y="1" | |||
pagecolor="#ffffff" | |||
inkscape:window-height="1845" | |||
inkscape:window-maximized="1" | |||
inkscape:zoom="50.862829" | |||
inkscape:window-x="0" | |||
showgrid="false" | |||
borderopacity="1.0" | |||
inkscape:current-layer="layer1" | |||
inkscape:cx="10.925276" | |||
inkscape:cy="9.494275" | |||
inkscape:window-width="3360" | |||
inkscape:pageopacity="0.0" | |||
inkscape:document-units="px" /> | |||
<defs | |||
id="defs4890"> | |||
<filter | |||
style="color-interpolation-filters:sRGB" | |||
inkscape:collect="always" | |||
id="filter6126-9"> | |||
<feGaussianBlur | |||
inkscape:collect="always" | |||
stdDeviation="0.53035713" | |||
id="feGaussianBlur6128-8" /> | |||
</filter> | |||
<filter | |||
style="color-interpolation-filters:sRGB" | |||
inkscape:collect="always" | |||
id="filter10443"> | |||
<feGaussianBlur | |||
inkscape:collect="always" | |||
stdDeviation="1.3973845" | |||
id="feGaussianBlur10445" /> | |||
</filter> | |||
<linearGradient | |||
inkscape:collect="always" | |||
x1="241.41" | |||
x2="315.47" | |||
y1="287.48999" | |||
gradientUnits="userSpaceOnUse" | |||
y2="367.88" | |||
id="linearGradient8435-662"> | |||
<stop | |||
offset="0" | |||
style="stop-color:#ffffff" | |||
id="stop4200" /> | |||
<stop | |||
offset="1" | |||
style="stop-color:#ffffff;stop-opacity:0" | |||
id="stop4202" /> | |||
</linearGradient> | |||
<linearGradient | |||
inkscape:collect="always" | |||
x1="275.60999" | |||
x2="342" | |||
y1="300.85999" | |||
gradientUnits="userSpaceOnUse" | |||
y2="391.45001" | |||
id="linearGradient8437-613"> | |||
<stop | |||
offset="0" | |||
style="stop-color:#0f0f0f" | |||
id="stop4205" /> | |||
<stop | |||
offset="1" | |||
style="stop-color:#4a4a4a" | |||
id="stop4207" /> | |||
</linearGradient> | |||
<radialGradient | |||
inkscape:collect="always" | |||
gradientTransform="matrix(-0.59327,-0.59327,0.71505,-0.71505,243.27,849.03)" | |||
r="53.035999" | |||
cy="386.57001" | |||
cx="312.78" | |||
gradientUnits="userSpaceOnUse" | |||
id="radialGradient8439-369"> | |||
<stop | |||
offset="0" | |||
style="stop-color:#ffffff;stop-opacity:.40816" | |||
id="stop4210" /> | |||
<stop | |||
offset="1" | |||
style="stop-color:#ffffff;stop-opacity:0" | |||
id="stop4212" /> | |||
</radialGradient> | |||
<linearGradient | |||
inkscape:collect="always" | |||
x1="255.32001" | |||
y1="286.67001" | |||
gradientTransform="matrix(2.7754,0,0,2.7754,-606.45,-52.271)" | |||
x2="300.26999" | |||
gradientUnits="userSpaceOnUse" | |||
y2="338.82001" | |||
id="linearGradient6858-674"> | |||
<stop | |||
offset="0" | |||
style="stop-color:#ffffff" | |||
id="stop4215" /> | |||
<stop | |||
offset="1" | |||
style="stop-color:#ffffff;stop-opacity:0" | |||
id="stop4217" /> | |||
</linearGradient> | |||
</defs> | |||
<g | |||
transform="translate(0,-1032.36)" | |||
inkscape:groupmode="layer" | |||
inkscape:label="Layer 1" | |||
id="layer1"> | |||
<g | |||
transform="matrix(0.05,0,0,0.05,-0.42108026,999.761)" | |||
id="g6872"> | |||
<circle | |||
r="53.035713" | |||
cy="346.11218" | |||
cx="303.75" | |||
transform="matrix(2.8283,0,0,2.8283,-653.01,-129.79)" | |||
style="fill:#999999" | |||
id="path7262" /> | |||
<circle | |||
r="53.035713" | |||
cy="346.11218" | |||
cx="303.75" | |||
transform="matrix(2.7878,0,0,2.7878,-640.71,-115.77)" | |||
style="fill:url(#linearGradient8435-662)" | |||
id="path7264" /> | |||
<circle | |||
r="53.035713" | |||
cy="346.11218" | |||
cx="303.75" | |||
transform="matrix(2.5378,0,0,2.5378,-564.77,-29.245)" | |||
style="fill:#ececec;filter:url(#filter6126-9)" | |||
id="path7266" /> | |||
<circle | |||
r="53.035713" | |||
cy="346.11218" | |||
cx="303.75" | |||
transform="matrix(2.4356,0,0,2.4356,-533.74,6.1126)" | |||
style="fill:#999999;filter:url(#filter6126-9)" | |||
id="path7268" /> | |||
<circle | |||
r="53.035713" | |||
cy="346.11218" | |||
cx="303.75" | |||
transform="matrix(2.3412,0,0,2.3412,-505.05,38.806)" | |||
style="fill:url(#linearGradient8437-613)" | |||
id="path7270" /> | |||
<circle | |||
r="53.035713" | |||
cy="346.11218" | |||
cx="303.75" | |||
transform="matrix(2.2028,0,0,2.2028,-459.36,92.554)" | |||
style="fill:url(#radialGradient8439-369)" | |||
id="path7272" /> | |||
<path | |||
inkscape:connector-curvature="0" | |||
style="opacity:0.31771001;fill:url(#linearGradient6858-674)" | |||
d="m 203.16,732.39 c -38.727,0.87355 -76.074,21.376 -96.845,57.353 -22.341,38.696 -19.505,85.022 3.3539,120.07 2.4783,-77.078 61.391,-135.13 134.58,-130.89 23.993,1.3918 46.64,9.2967 66.408,22.052 -9.4642,-21.643 -25.615,-40.536 -47.626,-53.244 -18.845,-10.88 -39.582,-15.802 -59.868,-15.344 z" | |||
id="path7274" /> | |||
<g | |||
transform="matrix(2.9051,0,0,2.9051,-1408.6,-93.103)" | |||
style="fill:#1a1a1a;filter:url(#filter10443)" | |||
id="text7350"> | |||
<path | |||
inkscape:connector-curvature="0" | |||
d="m 546.92,335.78 c 0.12959,-2.1773 0.24624,-3.7326 0.34993,-4.6657 0.10367,-0.93312 0.24623,-1.7431 0.42769,-2.4301 0.18143,-0.68688 0.40824,-1.296 0.68042,-1.8274 0.27215,-0.53135 0.66096,-1.1275 1.1664,-1.7885 0.50544,-0.66095 1.147,-1.4062 1.9246,-2.2357 0.77761,-0.82943 1.8533,-1.944 3.2271,-3.3438 l 1.2831,-1.322 c 0.44063,-0.41469 0.83592,-0.84238 1.1859,-1.2831 0.34991,-0.44062 0.6156,-0.82294 0.79707,-1.147 0.18142,-0.32397 0.31102,-0.62206 0.38881,-0.89426 0.0777,-0.27213 0.11662,-0.55078 0.11664,-0.83595 -2e-5,-1.3219 -0.71284,-2.3199 -2.1385,-2.9938 -1.4257,-0.67389 -3.4475,-1.0109 -6.0654,-1.0109 -3.6548,4e-5 -7.8799,0.77766 -12.675,2.3329 l 1.2053,-6.0654 c 0.1296,-0.69981 0.28513,-1.5617 0.46658,-2.5856 0.18144,-1.0238 0.3888,-2.3522 0.62209,-3.9853 5.7025,-0.82941 10.926,-1.2441 15.669,-1.2442 3.1364,5e-5 5.9812,0.23982 8.5344,0.7193 2.5532,0.47958 4.7823,1.3026 6.6875,2.4689 1.9051,1.1665 3.3632,2.5532 4.3741,4.1603 1.0109,1.6071 1.5163,3.3309 1.5164,5.1712 -4e-5,0.77766 -0.0778,1.5682 -0.23328,2.3718 -0.15557,0.80358 -0.37589,1.5682 -0.66098,2.294 -0.28517,0.72581 -0.70638,1.5358 -1.2636,2.4301 -0.55734,0.8943 -1.2118,1.795 -1.9635,2.7022 -0.75173,0.90726 -1.3674,1.6006 -1.8468,2.0801 -0.47957,0.47956 -1.3868,1.3544 -2.7217,2.6245 -1.3349,1.2701 -2.3523,2.2681 -3.0522,2.9938 -0.69989,0.7258 -1.2701,1.4127 -1.7108,2.0607 -0.44068,0.64804 -0.78413,1.3414 -1.0304,2.0801 -0.24628,0.73876 -0.47308,1.808 -0.68042,3.2077 -2.618,-0.0777 -4.9768,-0.11663 -7.0764,-0.11665 -2.0478,2e-5 -4.5491,0.0259 -7.504,0.0778 z m -1.7885,16.758 0.38881,-4.6657 0.50546,-6.2987 c 0.0259,-0.28512 0.0648,-0.98498 0.11664,-2.0996 3.2919,0.0519 5.9877,0.0778 8.0873,0.0778 2.2551,10e-6 4.899,-0.0259 7.9317,-0.0778 -0.25924,2.955 -0.42772,5.4693 -0.50546,7.5429 -0.0778,2.0737 -0.16851,3.914 -0.27216,5.5211 -3.4993,-0.0518 -6.1173,-0.0778 -7.854,-0.0778 -1.8404,0 -4.6398,0.0259 -8.3983,0.0778 z" | |||
id="path6867" /> | |||
</g> | |||
<g | |||
transform="translate(-100,0)" | |||
style="fill:#ffffff" | |||
id="text8454"> | |||
<path | |||
inkscape:connector-curvature="0" | |||
d="m 281.32,881.31 c 0.36286,-6.0965 0.68946,-10.451 0.9798,-13.064 0.29028,-2.6128 0.68946,-4.8808 1.1975,-6.8042 0.50801,-1.9232 1.1431,-3.6288 1.9052,-5.1168 0.76204,-1.4878 1.8507,-3.1571 3.266,-5.0079 1.4152,-1.8507 3.2115,-3.9373 5.3889,-6.2599 2.1773,-2.3224 5.1893,-5.4433 9.036,-9.3626 l 3.5926,-3.7015 c 1.2338,-1.1612 2.3406,-2.3587 3.3204,-3.5926 0.97975,-1.2337 1.7237,-2.3042 2.2318,-3.2116 0.50798,-0.90713 0.87087,-1.7418 1.0887,-2.504 0.21767,-0.76196 0.32654,-1.5422 0.3266,-2.3406 -6e-5,-3.7014 -1.996,-6.4956 -5.9877,-8.3828 -3.9918,-1.8869 -9.6529,-2.8304 -16.983,-2.8305 -10.234,1.2e-4 -22.064,2.1775 -35.491,6.532 l 3.3749,-16.983 c 0.36288,-1.9595 0.79835,-4.3727 1.3064,-7.2397 0.50804,-2.8667 1.0887,-6.5863 1.7419,-11.159 15.967,-2.3224 30.592,-3.4836 43.873,-3.4837 8.7819,1.5e-4 16.747,0.67149 23.896,2.014 7.1488,1.3428 13.391,3.6472 18.725,6.913 5.3344,3.2662 9.4169,7.1491 12.248,11.649 2.8304,4.5 4.2457,9.3264 4.2458,14.479 -1.2e-4,2.1775 -0.21785,4.3911 -0.65321,6.6409 -0.43558,2.25 -1.0525,4.3911 -1.8507,6.4232 -0.79847,2.0323 -1.9779,4.3004 -3.5382,6.8042 -1.5605,2.504 -3.3931,5.0261 -5.4978,7.5663 -2.1049,2.5403 -3.8286,4.4818 -5.1712,5.8244 -1.3428,1.3428 -3.883,3.7923 -7.6207,7.3485 -3.7378,3.5564 -6.5865,6.3506 -8.5461,8.3828 -1.9597,2.0322 -3.5564,3.9556 -4.7902,5.77 -1.2339,1.8145 -2.1956,3.756 -2.885,5.8244 -0.68956,2.0685 -1.3246,5.0624 -1.9052,8.9815 -7.3304,-0.21768 -13.935,-0.32655 -19.814,-0.3266 -5.7337,5e-5 -12.737,0.0726 -21.011,0.21774 z m -5.0079,46.922 1.0887,-13.064 1.4153,-17.636 c 0.0726,-0.79833 0.18141,-2.7579 0.3266,-5.8788 9.2174,0.14519 16.765,0.21777 22.644,0.21773 6.3142,4e-5 13.717,-0.0725 22.209,-0.21773 -0.72585,8.2739 -1.1976,15.314 -1.4153,21.12 -0.21781,5.8062 -0.47183,10.959 -0.76207,15.459 -9.7981,-0.14515 -17.128,-0.21773 -21.991,-0.21773 -5.1531,0 -12.992,0.0726 -23.515,0.21773 z" | |||
id="path6870" /> | |||
</g> | |||
</g> | |||
</g> | |||
<metadata | |||
id="metadata34"> | |||
<rdf:RDF> | |||
<cc:Work> | |||
<dc:format>image/svg+xml</dc:format> | |||
<dc:type | |||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | |||
<cc:license | |||
rdf:resource="http://creativecommons.org/publicdomain/zero/1.0/" /> | |||
<dc:publisher> | |||
<cc:Agent | |||
rdf:about="http://openclipart.org/"> | |||
<dc:title>Openclipart</dc:title> | |||
</cc:Agent> | |||
</dc:publisher> | |||
<dc:title></dc:title> | |||
<dc:date>2011-02-24T17:49:39</dc:date> | |||
<dc:description /> | |||
<dc:source>https://openclipart.org/detail/122449/question-button-by-ricardomaia</dc:source> | |||
<dc:creator> | |||
<cc:Agent> | |||
<dc:title>ricardomaia</dc:title> | |||
</cc:Agent> | |||
</dc:creator> | |||
<dc:subject> | |||
<rdf:Bag> | |||
<rdf:li>blue</rdf:li> | |||
<rdf:li>button</rdf:li> | |||
<rdf:li>circle</rdf:li> | |||
<rdf:li>confirm</rdf:li> | |||
<rdf:li>help</rdf:li> | |||
<rdf:li>question</rdf:li> | |||
<rdf:li>round</rdf:li> | |||
</rdf:Bag> | |||
</dc:subject> | |||
</cc:Work> | |||
<cc:License | |||
rdf:about="http://creativecommons.org/publicdomain/zero/1.0/"> | |||
<cc:permits | |||
rdf:resource="http://creativecommons.org/ns#Reproduction" /> | |||
<cc:permits | |||
rdf:resource="http://creativecommons.org/ns#Distribution" /> | |||
<cc:permits | |||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" /> | |||
</cc:License> | |||
</rdf:RDF> | |||
</metadata> | |||
</svg> |
@@ -0,0 +1,669 @@ | |||
{ | |||
"!" : [ | |||
"...#.", | |||
"...#.", | |||
"...#.", | |||
"...#.", | |||
"...#.", | |||
".....", | |||
"...#." | |||
], | |||
"\"" : [ | |||
".#.#.", | |||
".#.#.", | |||
".#.#.", | |||
".....", | |||
".....", | |||
".....", | |||
"....." | |||
], | |||
"#" : [ | |||
".#.#.", | |||
".#.#.", | |||
"##.##", | |||
".....", | |||
"##.##", | |||
".#.#.", | |||
".#.#." | |||
], | |||
"$" : [ | |||
"..#..", | |||
".####", | |||
"#....", | |||
".###.", | |||
"....#", | |||
"####.", | |||
"..#.." | |||
], | |||
"%" : [ | |||
"##..#", | |||
"##..#", | |||
"...#.", | |||
"..#..", | |||
".#...", | |||
"#..##", | |||
"#..##" | |||
], | |||
"&" : [ | |||
".#...", | |||
"#.#..", | |||
"#.#..", | |||
".#...", | |||
"#.#.#", | |||
"#..#.", | |||
".##.#" | |||
], | |||
"'" : [ | |||
".##..", | |||
".##..", | |||
".##..", | |||
".....", | |||
".....", | |||
".....", | |||
"....." | |||
], | |||
"(" : [ | |||
"...#.", | |||
"..#..", | |||
".#...", | |||
".#...", | |||
".#...", | |||
"..#..", | |||
"...#." | |||
], | |||
")" : [ | |||
".#...", | |||
"..#..", | |||
"...#.", | |||
"...#.", | |||
"...#.", | |||
"..#..", | |||
".#..." | |||
], | |||
"*" : [ | |||
"#.#.#", | |||
".###.", | |||
"#####", | |||
".###.", | |||
"#.#.#", | |||
".....", | |||
"....." | |||
], | |||
"+" : [ | |||
".....", | |||
"..#..", | |||
"..#..", | |||
"#####", | |||
"..#..", | |||
"..#..", | |||
"....." | |||
], | |||
"," : [ | |||
".....", | |||
".....", | |||
".....", | |||
".##..", | |||
".##..", | |||
"..#..", | |||
".#..." | |||
], | |||
"-" : [ | |||
".....", | |||
".....", | |||
".....", | |||
".....", | |||
"#####", | |||
".....", | |||
"....." | |||
], | |||
"." : [ | |||
".....", | |||
".....", | |||
".....", | |||
".....", | |||
".....", | |||
".##..", | |||
".##.." | |||
], | |||
"/" : [ | |||
".....", | |||
"....#", | |||
"...#.", | |||
"..#..", | |||
".#...", | |||
"#....", | |||
"....." | |||
], | |||
"0" : [ | |||
"..##.", | |||
".#..#", | |||
".#..#", | |||
".#..#", | |||
".#..#", | |||
".#..#", | |||
"..##." | |||
], | |||
"1" : [ | |||
"..#..", | |||
".##..", | |||
"..#..", | |||
"..#..", | |||
"..#..", | |||
"..#..", | |||
".###." | |||
], | |||
"2" : [ | |||
".###.", | |||
"#...#", | |||
"....#", | |||
".###.", | |||
"#....", | |||
"#....", | |||
"#####" | |||
], | |||
"3" : [ | |||
".###.", | |||
"#...#", | |||
"....#", | |||
"..##.", | |||
"....#", | |||
"#...#", | |||
".###." | |||
], | |||
"4" : [ | |||
"...#.", | |||
"..##.", | |||
".#.#.", | |||
"#..#.", | |||
"#####", | |||
"...#.", | |||
"...#." | |||
], | |||
"5" : [ | |||
"#####", | |||
"#....", | |||
"####.", | |||
"....#", | |||
"....#", | |||
"#...#", | |||
".###." | |||
], | |||
"6" : [ | |||
"..##.", | |||
".#...", | |||
"#....", | |||
"####.", | |||
"#...#", | |||
"#...#", | |||
".###." | |||
], | |||
"7" : [ | |||
"#####", | |||
"....#", | |||
"...#.", | |||
"..#..", | |||
".#...", | |||
".#...", | |||
".#..." | |||
], | |||
"8" : [ | |||
".###.", | |||
"#...#", | |||
"#...#", | |||
".###.", | |||
"#...#", | |||
"#...#", | |||
".###." | |||
], | |||
"9" : [ | |||
".###.", | |||
"#...#", | |||
"#...#", | |||
".####", | |||
"....#", | |||
"...#.", | |||
".##.." | |||
], | |||
":" : [ | |||
".....", | |||
".##..", | |||
".##..", | |||
".....", | |||
".##..", | |||
".##..", | |||
"....." | |||
], | |||
";" : [ | |||
".##..", | |||
".##..", | |||
".....", | |||
".##..", | |||
".##..", | |||
"..#..", | |||
".#..." | |||
], | |||
"<" : [ | |||
"....#", | |||
"...#.", | |||
"..#..", | |||
".#...", | |||
"..#..", | |||
"...#.", | |||
"....#" | |||
], | |||
"=" : [ | |||
".....", | |||
".....", | |||
"#####", | |||
".....", | |||
"#####", | |||
".....", | |||
"....." | |||
], | |||
">" : [ | |||
"#....", | |||
".#...", | |||
"..#..", | |||
"...#.", | |||
"..#..", | |||
".#...", | |||
"#...." | |||
], | |||
"?" : [ | |||
".##..", | |||
"#..#.", | |||
"...#.", | |||
"..#..", | |||
"..#..", | |||
".....", | |||
"..#.." | |||
], | |||
"@" : [ | |||
".###.", | |||
"#...#", | |||
"....#", | |||
".##.#", | |||
"#.#.#", | |||
"#.#.#", | |||
".###." | |||
], | |||
"A" : [ | |||
"..#..", | |||
".#.#.", | |||
"#...#", | |||
"#...#", | |||
"#####", | |||
"#...#", | |||
"#...#" | |||
], | |||
"B" : [ | |||
"####.", | |||
".#..#", | |||
".#..#", | |||
".###.", | |||
".#..#", | |||
".#..#", | |||
"####." | |||
], | |||
"C" : [ | |||
".###.", | |||
"#...#", | |||
"#....", | |||
"#....", | |||
"#....", | |||
"#...#", | |||
".###." | |||
], | |||
"D" : [ | |||
"####.", | |||
".#..#", | |||
".#..#", | |||
".#..#", | |||
".#..#", | |||
".#..#", | |||
"####." | |||
], | |||
"E" : [ | |||
"#####", | |||
"#....", | |||
"#....", | |||
"###..", | |||
"#....", | |||
"#....", | |||
"#####" | |||
], | |||
"F" : [ | |||
"#####", | |||
"#....", | |||
"#....", | |||
"###..", | |||
"#....", | |||
"#....", | |||
"#...." | |||
], | |||
"G" : [ | |||
".####", | |||
"#....", | |||
"#....", | |||
"#..##", | |||
"#...#", | |||
"#...#", | |||
".####" | |||
], | |||
"H" : [ | |||
"#...#", | |||
"#...#", | |||
"#...#", | |||
"#####", | |||
"#...#", | |||
"#...#", | |||
"#...#" | |||
], | |||
"I" : [ | |||
".###.", | |||
"..#..", | |||
"..#..", | |||
"..#..", | |||
"..#..", | |||
"..#..", | |||
".###." | |||
], | |||
"J" : [ | |||
"....#", | |||
"....#", | |||
"....#", | |||
"....#", | |||
"....#", | |||
"#...#", | |||
".###." | |||
], | |||
"K" : [ | |||
"#...#", | |||
"#..#.", | |||
"#.#..", | |||
"##...", | |||
"#.#..", | |||
"#..#.", | |||
"#...#" | |||
], | |||
"L" : [ | |||
"#....", | |||
"#....", | |||
"#....", | |||
"#....", | |||
"#....", | |||
"#....", | |||
"#####" | |||
], | |||
"M" : [ | |||
"#...#", | |||
"##.##", | |||
"#.#.#", | |||
"#.#.#", | |||
"#...#", | |||
"#...#", | |||
"#...#" | |||
], | |||
"N" : [ | |||
"#...#", | |||
"##..#", | |||
"#.#.#", | |||
"#..##", | |||
"#...#", | |||
"#...#", | |||
"#...#" | |||
], | |||
"O" : [ | |||
"#####", | |||
"#...#", | |||
"#...#", | |||
"#...#", | |||
"#...#", | |||
"#...#", | |||
"#####" | |||
], | |||
"P" : [ | |||
"####.", | |||
"#...#", | |||
"#...#", | |||
"####.", | |||
"#....", | |||
"#....", | |||
"#...." | |||
], | |||
"Q" : [ | |||
".###.", | |||
"#...#", | |||
"#...#", | |||
"#...#", | |||
"#.#.#", | |||
"#..#.", | |||
".##.#" | |||
], | |||
"R" : [ | |||
"####.", | |||
"#...#", | |||
"#...#", | |||
"####.", | |||
"#.#..", | |||
"#..#.", | |||
"#...#" | |||
], | |||
"S" : [ | |||
".###.", | |||
"#...#", | |||
".#...", | |||
"..#..", | |||
"...#.", | |||
"#...#", | |||
".###." | |||
], | |||
"T" : [ | |||
"#####", | |||
"..#..", | |||
"..#..", | |||
"..#..", | |||
"..#..", | |||
"..#..", | |||
"..#.." | |||
], | |||
"U" : [ | |||
"#...#", | |||
"#...#", | |||
"#...#", | |||
"#...#", | |||
"#...#", | |||
"#...#", | |||
".###." | |||
], | |||
"V" : [ | |||
"#...#", | |||
"#...#", | |||
"#...#", | |||
".#.#.", | |||
".#.#.", | |||
"..#..", | |||
"..#.." | |||
], | |||
"W" : [ | |||
"#...#", | |||
"#...#", | |||
"#...#", | |||
"#...#", | |||
"#.#.#", | |||
"##.##", | |||
"#...#" | |||
], | |||
"X" : [ | |||
"#...#", | |||
"#...#", | |||
".#.#.", | |||
"..#..", | |||
".#.#.", | |||
"#...#", | |||
"#...#" | |||
], | |||
"Y" : [ | |||
"#...#", | |||
"#...#", | |||
".#.#.", | |||
"..#..", | |||
"..#..", | |||
"..#..", | |||
"..#.." | |||
], | |||
"Z" : [ | |||
"#####", | |||
"....#", | |||
"...#.", | |||
"..#..", | |||
".#...", | |||
"#....", | |||
"#####" | |||
], | |||
"\\" : [ | |||
".....", | |||
"#....", | |||
".#...", | |||
"..#..", | |||
"...#.", | |||
"....#", | |||
"....." | |||
], | |||
"_" : [ | |||
".....", | |||
".....", | |||
".....", | |||
".....", | |||
".....", | |||
".....", | |||
"#####" | |||
], | |||
"|" : [ | |||
".....", | |||
"..#..", | |||
"..#..", | |||
"..#..", | |||
"..#..", | |||
"..#..", | |||
"....." | |||
], | |||
"¢" : [ | |||
"..#..", | |||
".###.", | |||
"#....", | |||
"#....", | |||
"#....", | |||
".###.", | |||
"..#.." | |||
], | |||
"¬" : [ | |||
".....", | |||
".....", | |||
".....", | |||
".....", | |||
"#####", | |||
"....#", | |||
"....#" | |||
], | |||
" " : [ | |||
".....", | |||
".....", | |||
".....", | |||
".....", | |||
".....", | |||
".....", | |||
"....." | |||
], | |||
"[" : [ | |||
"###..", | |||
"#....", | |||
"#....", | |||
"#....", | |||
"#....", | |||
"#....", | |||
"###.." | |||
], | |||
"]" : [ | |||
"..###", | |||
"....#", | |||
"....#", | |||
"....#", | |||
"....#", | |||
"....#", | |||
"..###" | |||
], | |||
"^" : [ | |||
"..#..", | |||
".#.#.", | |||
"#...#", | |||
".....", | |||
".....", | |||
".....", | |||
"....." | |||
], | |||
"`" : [ | |||
"#....", | |||
".#...", | |||
"..#..", | |||
".....", | |||
".....", | |||
".....", | |||
"....." | |||
], | |||
"{" : [ | |||
"..##.", | |||
".#...", | |||
".#...", | |||
"#....", | |||
".#...", | |||
".#...", | |||
"..##." | |||
], | |||
"}" : [ | |||
".##..", | |||
"...#.", | |||
"...#.", | |||
"....#", | |||
"...#.", | |||
"...#.", | |||
".##.." | |||
], | |||
"~" : [ | |||
".....", | |||
".....", | |||
".#...", | |||
"#.#.#", | |||
"...#.", | |||
".....", | |||
"....." | |||
], | |||
"€" : [ | |||
"..###", | |||
".#...", | |||
"###..", | |||
".#...", | |||
"###..", | |||
".#...", | |||
"..###" | |||
], | |||
"ÂŁ" : [ | |||
"..###", | |||
".#...", | |||
".#...", | |||
"###..", | |||
".#...", | |||
".#...", | |||
"#####" | |||
], | |||
"⌑" : [ | |||
"#...#", | |||
".###.", | |||
".#.#.", | |||
".#.#.", | |||
".#.#.", | |||
".###.", | |||
"#...#" | |||
] | |||
} | |||
@@ -0,0 +1,3 @@ | |||
Wood Grain from https://publicdomainvectors.org/en/free-clipart/Wood-grain/43630.html | |||
KeyPunch from http://scruss.com/blog/tag/font/ | |||
Monitoria font: http://www.1001fonts.com/monitorica-font.html |
@@ -0,0 +1,115 @@ | |||
#include "BaconPlugs.hpp" | |||
/* | |||
** Based heavily on http://recherche.ircam.fr/pub/dafx11/Papers/66_e.pdf | |||
*/ | |||
namespace rack_plugin_BaconMusic { | |||
struct ALingADing : Module { | |||
enum ParamIds { | |||
WET_DRY_MIX, // TODO: Implement this | |||
NUM_PARAMS | |||
}; | |||
enum InputIds { | |||
SIGNAL_INPUT, | |||
CARRIER_INPUT, | |||
NUM_INPUTS | |||
}; | |||
enum OutputIds { | |||
MODULATED_OUTPUT, | |||
NUM_OUTPUTS | |||
}; | |||
enum LightIds { | |||
NUM_LIGHTS | |||
}; | |||
ALingADing() : Module( NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS ) { | |||
params[ WET_DRY_MIX ].value = 1.0; | |||
} | |||
inline float diode_sim( float in ) | |||
{ | |||
if( in < 0 ) return 0; | |||
else return 0.2 * log( 1.0 + exp( 10 * ( in - 1 ) ) ); | |||
} | |||
void step() override | |||
{ | |||
float vin = inputs[ SIGNAL_INPUT ].value; | |||
float vc = inputs[ CARRIER_INPUT ].value; | |||
float wd = params[ WET_DRY_MIX ].value; | |||
float A = 0.5 * vin + vc; | |||
float B = vc - 0.5 * vin; | |||
float dPA = diode_sim( A ); | |||
float dMA = diode_sim( -A ); | |||
float dPB = diode_sim( B ); | |||
float dMB = diode_sim( -B ); | |||
float res = dPA + dMA - dPB - dMB; | |||
outputs[ MODULATED_OUTPUT ].value = wd * res + ( 1.0 - wd ) * vin; | |||
} | |||
}; | |||
struct ALingADingWidget : ModuleWidget { | |||
ALingADingWidget(ALingADing *module); | |||
}; | |||
ALingADingWidget::ALingADingWidget(ALingADing *module) : ModuleWidget( module ) | |||
{ | |||
box.size = Vec( SCREW_WIDTH * 5, RACK_HEIGHT ); | |||
BaconBackground *bg = new BaconBackground( box.size, "ALingADing" ); | |||
addChild( bg->wrappedInFramebuffer() ); | |||
bg->addPlugLabel( Vec( 7, 70 ), BaconBackground::SIG_IN, "sig" ); | |||
addInput( Port::create< PJ301MPort >( Vec( 7, 70 ), | |||
Port::INPUT, | |||
module, | |||
ALingADing::SIGNAL_INPUT ) ); | |||
bg->addPlugLabel( Vec( box.size.x-24-7, 70 ), BaconBackground::SIG_IN, "car" ); | |||
addInput( Port::create< PJ301MPort >( Vec( box.size.x-24 - 7, 70 ), // That 24 makes no sense but hey | |||
Port::INPUT, | |||
module, | |||
ALingADing::CARRIER_INPUT ) ); | |||
bg->addLabel( Vec( bg->cx(), 140 ), "Mix", 14, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE ); | |||
bg->addLabel( Vec( bg->cx() + 10, 140 + 72 ), | |||
"Wet", 13, NVG_ALIGN_LEFT | NVG_ALIGN_TOP ); | |||
bg->addLabel( Vec( bg->cx() - 10, 140 + 72 ), | |||
"Dry", 13, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP ); | |||
addParam( ParamWidget::create< RoundHugeBlackKnob >( Vec( bg->cx( 56 ), 150 ), | |||
module, | |||
ALingADing::WET_DRY_MIX, | |||
0, 1, 1 )); | |||
Vec outP = Vec( bg->cx( 24 ), RACK_HEIGHT - 15 - 43 ); | |||
bg->addPlugLabel( outP, BaconBackground::SIG_OUT, "out" ); | |||
addOutput( Port::create< PJ301MPort >( outP, | |||
Port::OUTPUT, | |||
module, | |||
ALingADing::MODULATED_OUTPUT ) ); | |||
} | |||
} // namespace rack_plugin_BaconMusic | |||
using namespace rack_plugin_BaconMusic; | |||
RACK_PLUGIN_MODEL_INIT(BaconMusic, ALingADing) { | |||
Model *modelALingADing = Model::create<ALingADing,ALingADingWidget>("Bacon Music", "ALingADing", "ALingADing", RING_MODULATOR_TAG); | |||
return modelALingADing; | |||
} |
@@ -0,0 +1,45 @@ | |||
#include "BaconPlugs.hpp" | |||
// RACK_PLUGIN_MODEL_DECLARE(BaconMusic, HarMoNee); // crashes | |||
RACK_PLUGIN_MODEL_DECLARE(BaconMusic, Glissinator); | |||
RACK_PLUGIN_MODEL_DECLARE(BaconMusic, PolyGnome); | |||
RACK_PLUGIN_MODEL_DECLARE(BaconMusic, QuantEyes); | |||
RACK_PLUGIN_MODEL_DECLARE(BaconMusic, SampleDelay); | |||
#ifdef BUILD_SORTACHORUS | |||
RACK_PLUGIN_MODEL_DECLARE(BaconMusic, SortaChorus); | |||
#endif | |||
RACK_PLUGIN_MODEL_DECLARE(BaconMusic, ChipNoise); | |||
RACK_PLUGIN_MODEL_DECLARE(BaconMusic, ChipWaves); | |||
RACK_PLUGIN_MODEL_DECLARE(BaconMusic, ChipYourWave); | |||
RACK_PLUGIN_MODEL_DECLARE(BaconMusic, KarplusStrongPoly); | |||
RACK_PLUGIN_MODEL_DECLARE(BaconMusic, ALingADing); | |||
RACK_PLUGIN_MODEL_DECLARE(BaconMusic, Bitulator); | |||
RACK_PLUGIN_INIT(BaconMusic) { | |||
RACK_PLUGIN_INIT_ID(); | |||
RACK_PLUGIN_INIT_WEBSITE("https://github.com/baconpaul/Bacon Music"); | |||
// RACK_PLUGIN_MODEL_ADD(BaconMusic, HarMoNee); // crashes | |||
RACK_PLUGIN_MODEL_ADD(BaconMusic, Glissinator); | |||
RACK_PLUGIN_MODEL_ADD(BaconMusic, PolyGnome); | |||
RACK_PLUGIN_MODEL_ADD(BaconMusic, QuantEyes); | |||
RACK_PLUGIN_MODEL_ADD(BaconMusic, SampleDelay); | |||
#ifdef BUILD_SORTACHORUS | |||
RACK_PLUGIN_MODEL_ADD(BaconMusic, SortaChorus); | |||
#endif | |||
RACK_PLUGIN_MODEL_ADD(BaconMusic, ChipNoise); | |||
RACK_PLUGIN_MODEL_ADD(BaconMusic, ChipWaves); | |||
RACK_PLUGIN_MODEL_ADD(BaconMusic, ChipYourWave); | |||
RACK_PLUGIN_MODEL_ADD(BaconMusic, KarplusStrongPoly); | |||
RACK_PLUGIN_MODEL_ADD(BaconMusic, ALingADing); | |||
RACK_PLUGIN_MODEL_ADD(BaconMusic, Bitulator); | |||
} |
@@ -0,0 +1,23 @@ | |||
#ifndef INCLUDE_BACONPLUGS_HPP | |||
#define INCLUDE_BACONPLUGS_HPP | |||
#include "rack.hpp" | |||
#include <map> | |||
#include <vector> | |||
#include <string> | |||
using namespace rack; | |||
#define SCREW_WIDTH 15 | |||
#define RACK_HEIGHT 380 | |||
RACK_PLUGIN_DECLARE(BaconMusic); | |||
#ifdef USE_VST2 | |||
#define plugin "BaconMusic" | |||
#endif // USE_VST2 | |||
#include "Components.hpp" | |||
#endif |
@@ -0,0 +1,155 @@ | |||
#include "BaconPlugs.hpp" | |||
/* | |||
** ToDo: | |||
** Add lights for on/off | |||
** Add a 7 segment display for step count | |||
*/ | |||
namespace rack_plugin_BaconMusic { | |||
struct Bitulator : Module { | |||
enum ParamIds { | |||
WET_DRY_MIX, | |||
STEP_COUNT, | |||
AMP_LEVEL, | |||
BITULATE, | |||
CLIPULATE, | |||
NUM_PARAMS | |||
}; | |||
enum InputIds { | |||
SIGNAL_INPUT, | |||
NUM_INPUTS | |||
}; | |||
enum OutputIds { | |||
CRUNCHED_OUTPUT, | |||
NUM_OUTPUTS | |||
}; | |||
enum LightIds { | |||
BITULATING_LIGHT, | |||
CRUNCHING_LIGHT, | |||
NUM_LIGHTS | |||
}; | |||
Bitulator() : Module( NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS ) { | |||
params[ WET_DRY_MIX ].value = 1.0; | |||
params[ STEP_COUNT ].value = 6; | |||
params[ AMP_LEVEL ].value = 1; | |||
params[ BITULATE ].value = 1; | |||
params[ CLIPULATE ].value = 1; | |||
lights[ BITULATING_LIGHT ].value = 1; | |||
lights[ CRUNCHING_LIGHT ].value = 1; | |||
} | |||
void step() override | |||
{ | |||
float vin = inputs[ SIGNAL_INPUT ].value; | |||
float wd = params[ WET_DRY_MIX ].value; | |||
// Signals are +/-5V signals of course. So | |||
float res = 0; | |||
if( params[ BITULATE ].value > 0 ) { | |||
float qi = params[ STEP_COUNT ].value / 2; | |||
float crunch = (int)( (vin/5.0) * qi ) / qi * 5.0; | |||
res = crunch; | |||
lights[ BITULATING_LIGHT ].value = 1; | |||
} | |||
else | |||
{ | |||
res = vin; | |||
lights[ BITULATING_LIGHT ].value = 0; | |||
} | |||
if( params[ CLIPULATE ].value > 0 ) { | |||
float al = params[ AMP_LEVEL ].value; | |||
res = clamp( res * al, -5.0f, 5.0f ); | |||
lights[ CRUNCHING_LIGHT ].value = 1; | |||
} | |||
else { | |||
lights[ CRUNCHING_LIGHT ].value = 0; | |||
} | |||
outputs[ CRUNCHED_OUTPUT ].value = wd * res + ( 1.0 - wd ) * vin; | |||
} | |||
}; | |||
struct BitulatorWidget : ModuleWidget { | |||
BitulatorWidget( Bitulator *model ); | |||
}; | |||
BitulatorWidget::BitulatorWidget( Bitulator *model ) : ModuleWidget( model ) | |||
{ | |||
box.size = Vec( SCREW_WIDTH * 6, RACK_HEIGHT ); | |||
BaconBackground *bg = new BaconBackground( box.size, "Bitulator" ); | |||
addChild( bg->wrappedInFramebuffer() ); | |||
int wdpos = 40; | |||
bg->addLabel( Vec( bg->cx(), wdpos ), "Mix", 14, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE ); | |||
bg->addLabel( Vec( bg->cx() + 10, wdpos + 72 ), "Wet", 13, NVG_ALIGN_LEFT | NVG_ALIGN_TOP ); | |||
bg->addLabel( Vec( bg->cx() - 10, wdpos + 72 ), "Dry", 13, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP ); | |||
addParam( ParamWidget::create< RoundHugeBlackKnob >( Vec( bg->cx( 56 ), wdpos + 10 ), | |||
module, | |||
Bitulator::WET_DRY_MIX, | |||
0, 1, 1 )); | |||
Vec cr( 5, 140 ), rs( box.size.x-10, 70 ); | |||
bg->addRoundedBorder( cr, rs ); | |||
bg->addLabel( Vec( bg->cx(), cr.y+3 ), "Quantize", 14, NVG_ALIGN_CENTER|NVG_ALIGN_TOP ); | |||
addChild( ModuleLightWidget::create< SmallLight< BlueLight > >( cr.plus( Vec( 5, 5 ) ), module, Bitulator::BITULATING_LIGHT ) ); | |||
addParam( ParamWidget::create< CKSS >( cr.plus( Vec( 5, 25 ) ), module, Bitulator::BITULATE, 0, 1, 1 ) ); | |||
Vec knobPos = Vec( cr.x + rs.x - 36 - 12, cr.y + 18 ); | |||
Vec knobCtr = knobPos.plus( Vec( 18, 18 ) ); | |||
addParam( ParamWidget::create< RoundLargeBlackKnob >( knobPos, | |||
module, | |||
Bitulator::STEP_COUNT, | |||
2, 16, 6 )); | |||
bg->addLabel( knobCtr.plus( Vec( 8, 21 ) ), "smth", 10, NVG_ALIGN_LEFT | NVG_ALIGN_TOP ); | |||
bg->addLabel( knobCtr.plus( Vec( -8, 21 ) ), "crnch", 10, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP ); | |||
cr = Vec( 5, 215 ); | |||
bg->addRoundedBorder( cr, rs ); | |||
bg->addLabel( Vec( bg->cx( 5 ), cr.y+3 ), "Amp'n'Clip", 14, NVG_ALIGN_CENTER|NVG_ALIGN_TOP ); | |||
addChild( ModuleLightWidget::create< SmallLight< BlueLight > >( cr.plus( Vec( 5, 5 ) ), module, Bitulator::CRUNCHING_LIGHT ) ); | |||
addParam( ParamWidget::create< CKSS >( cr.plus( Vec( 5, 25 ) ), module, Bitulator::CLIPULATE, 0, 1, 1 ) ); | |||
knobPos = Vec( cr.x + rs.x - 36 - 12, cr.y + 18 ); | |||
knobCtr = knobPos.plus( Vec( 18, 18 ) ); | |||
addParam( ParamWidget::create< RoundLargeBlackKnob >( knobPos, | |||
module, | |||
Bitulator::AMP_LEVEL, | |||
1, 10, 1 ) ); | |||
bg->addLabel( knobCtr.plus( Vec( 12, 21 ) ), "11", 10, NVG_ALIGN_LEFT | NVG_ALIGN_TOP ); | |||
bg->addLabel( knobCtr.plus( Vec( -8, 21 ) ), "one", 10, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP ); | |||
Vec inP = Vec( 10, RACK_HEIGHT - 15 - 43 ); | |||
Vec outP = Vec( box.size.x - 24 - 10, RACK_HEIGHT - 15 - 43 ); | |||
bg->addPlugLabel( inP, BaconBackground::SIG_IN, "in" ); | |||
addInput( Port::create< PJ301MPort >( inP, Port::INPUT, | |||
module, | |||
Bitulator::SIGNAL_INPUT ) ); | |||
bg->addPlugLabel( outP, BaconBackground::SIG_OUT, "out" ); | |||
addOutput( Port::create< PJ301MPort >( outP, Port::OUTPUT, | |||
module, | |||
Bitulator::CRUNCHED_OUTPUT ) ); | |||
} | |||
} // namespace rack_plugin_BaconMusic | |||
using namespace rack_plugin_BaconMusic; | |||
RACK_PLUGIN_MODEL_INIT(BaconMusic, Bitulator) { | |||
Model *modelBitulator = Model::create< Bitulator, BitulatorWidget >("Bacon Music", "Bitulator", "Bitulator", DISTORTION_TAG); | |||
return modelBitulator; | |||
} |
@@ -0,0 +1,45 @@ | |||
#if 0 | |||
#include "BaconPlugs.hpp" | |||
struct MODULE_NAME : virtual Module { | |||
enum ParamIds { | |||
NUM_PARAMS | |||
}; | |||
enum InputIds { | |||
NUM_INPUTS | |||
}; | |||
enum OutputIds { | |||
NUM_OUTPUTS | |||
}; | |||
enum LightIds { | |||
NUM_LIGHTS | |||
}; | |||
MODULE_NAME() : Module( NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS ) | |||
{ | |||
} | |||
void step() override | |||
{ | |||
} | |||
}; | |||
struct WIDGET_NAME : ModuleWidget { | |||
WIDGET_NAME( MODULE_NAME *module); | |||
}; | |||
WIDGET_NAME::WIDGET_NAME( MODULE_NAME *module ) : ModuleWidget( module ) | |||
{ | |||
box.size = Vec( SCREW_WIDTH * 8, RACK_HEIGHT ); | |||
BaconBackground *bg = new BaconBackground( box.size, "MODULE_NAME" ); | |||
addChild( bg->wrappedInFramebuffer()); | |||
} | |||
Model *modelMODULE_NAME = Model::create<MODULE_NAME, WIDGET_NAME>("Bacon Music", "MODULE_NAME", "MODULE_NAME", RACK_REPLACE_WITH_TAG); | |||
#endif |
@@ -0,0 +1,70 @@ | |||
#ifndef BUFFERED_DRAW_FUNCTION_INCLUDE | |||
#define BUFFERED_DRAW_FUNCTION_INCLUDE | |||
#include "rack.hpp" | |||
#include <functional> | |||
#include <vector> | |||
using namespace rack; | |||
template < typename T > | |||
struct BufferedDrawFunctionWidget : virtual FramebufferWidget | |||
{ | |||
typedef std::function< void( T *, NVGcontext *) > drawfn_t; | |||
T *that; | |||
drawfn_t drawf; | |||
struct InternalBDW : TransparentWidget | |||
{ | |||
T* that; | |||
drawfn_t drawf; | |||
InternalBDW( Rect box_, T* that_, drawfn_t draw_ ) : that( that_ ), drawf( draw_ ) | |||
{ | |||
box = box_; | |||
} | |||
void draw( NVGcontext *vg ) override | |||
{ | |||
drawf( that, vg ); | |||
} | |||
}; | |||
BufferedDrawFunctionWidget( Vec pos, Vec sz, T* that_, drawfn_t draw_ ) : that( that_ ), drawf( draw_ ) | |||
{ | |||
box.pos = pos; box.size = sz; | |||
auto kidBox = Rect( Vec( 0, 0 ), box.size ); | |||
InternalBDW *kid = new InternalBDW( kidBox, that, drawf ); | |||
addChild( kid ); | |||
} | |||
}; | |||
struct BufferedDrawLambdaWidget : virtual FramebufferWidget | |||
{ | |||
typedef std::function< void( NVGcontext *) > drawfn_t; | |||
drawfn_t drawf; | |||
struct InternalBDW : TransparentWidget | |||
{ | |||
drawfn_t drawf; | |||
InternalBDW( Rect box_, drawfn_t draw_ ) : drawf( draw_ ) | |||
{ | |||
box = box_; | |||
} | |||
void draw( NVGcontext *vg ) override | |||
{ | |||
drawf( vg ); | |||
} | |||
}; | |||
BufferedDrawLambdaWidget( Vec pos, Vec sz, drawfn_t draw_ ) : drawf( draw_ ) | |||
{ | |||
box.pos = pos; box.size = sz; | |||
auto kidBox = Rect( Vec( 0, 0 ), box.size ); | |||
InternalBDW *kid = new InternalBDW( kidBox, drawf ); | |||
addChild( kid ); | |||
} | |||
}; | |||
#endif |
@@ -0,0 +1,177 @@ | |||
#include "BaconPlugs.hpp" | |||
#include "ChipSym.hpp" | |||
namespace rack_plugin_BaconMusic { | |||
struct ChipNoise : virtual Module { | |||
enum ParamIds { | |||
NOISE_LENGTH, | |||
LONG_MODE, | |||
SHORT_LEN, | |||
PERIOD_93, | |||
NUM_PARAMS | |||
}; | |||
enum InputIds { | |||
NOISE_LENGTH_INPUT, | |||
NUM_INPUTS | |||
}; | |||
enum OutputIds { | |||
NOISE_OUTPUT, | |||
NUM_OUTPUTS | |||
}; | |||
enum LightIds { | |||
NOISE_FROM_INPUT, | |||
NOISE_FROM_KNOB, | |||
NOISE_LENGTH_LIGHT, | |||
PERIOD_93_LIGHT, | |||
USING_93, | |||
NUM_LIGHTS | |||
}; | |||
ChipSym::NESNoise noise; | |||
int prior_shortlen; | |||
bool prior_longmode; | |||
ChipNoise() : Module( NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS ), | |||
noise( -5.0, 5.0, engineGetSampleRate() ) | |||
{ | |||
params[ LONG_MODE ].value = 1; | |||
params[ NOISE_LENGTH ].value = 9; | |||
params[ SHORT_LEN ].value = 1; | |||
params[ PERIOD_93 ].value = 1; | |||
prior_shortlen = 1; | |||
prior_longmode = false; | |||
} | |||
void step() override | |||
{ | |||
lights[ NOISE_FROM_KNOB ].value = !inputs[ NOISE_LENGTH_INPUT ].active; | |||
lights[ NOISE_FROM_INPUT ].value = inputs[ NOISE_LENGTH_INPUT ].active; | |||
unsigned int nl = (unsigned int)clamp( params[ NOISE_LENGTH ].value, 0.0f, 15.0f ); | |||
if( inputs[ NOISE_LENGTH_INPUT ].active ) | |||
nl = (unsigned int)clamp( inputs[ NOISE_LENGTH_INPUT ].value * 1.5, 0.0f, 15.0f ); | |||
lights[ NOISE_LENGTH_LIGHT ].value = nl; | |||
noise.setPeriod(nl); | |||
int p93 = (int)params[ PERIOD_93 ].value; | |||
lights[ PERIOD_93_LIGHT ].value = p93; | |||
if( params[ LONG_MODE ].value == 0 && params[ SHORT_LEN ].value == 1 ) | |||
{ | |||
noise.set93Key( p93 ); | |||
lights[ USING_93 ].value = 1; | |||
} | |||
else | |||
{ | |||
lights[ USING_93 ].value = 0; | |||
} | |||
bool tmpNoiseFlag = ( params[ LONG_MODE ].value == 0 ); | |||
if ( tmpNoiseFlag != prior_longmode ) | |||
{ | |||
prior_longmode = tmpNoiseFlag; | |||
noise.setModeFlag( prior_longmode ); | |||
} | |||
if( params[ SHORT_LEN ].value != prior_shortlen ) | |||
{ | |||
prior_shortlen = params[SHORT_LEN].value; | |||
if( prior_shortlen == 1 ) | |||
{ | |||
noise.setShortLength( ChipSym::NESNoise::SHORT_93 ); | |||
} | |||
else | |||
{ | |||
noise.setShortLength( ChipSym::NESNoise::SHORT_31 ); | |||
} | |||
} | |||
outputs[ NOISE_OUTPUT ].value = noise.step(); | |||
} | |||
}; | |||
struct ChipNoiseWidget : ModuleWidget { | |||
ChipNoiseWidget( ChipNoise *module); | |||
}; | |||
ChipNoiseWidget::ChipNoiseWidget( ChipNoise *module ) : ModuleWidget( module ) | |||
{ | |||
box.size = Vec( SCREW_WIDTH * 6, RACK_HEIGHT ); | |||
BaconBackground *bg = new BaconBackground( box.size, "ChipNoise" ); | |||
addChild( bg->wrappedInFramebuffer()); | |||
// Control the noise length | |||
bg->addRoundedBorder( Vec( 8, 45 ), Vec( SCREW_WIDTH * 6 - 16, 75 ) ); | |||
bg->addLabel( Vec( bg->cx() + 7, 55 ), "wave", 11, NVG_ALIGN_LEFT | NVG_ALIGN_TOP ); | |||
bg->addLabel( Vec( bg->cx() + 5, 66 ), "length", 11, NVG_ALIGN_LEFT | NVG_ALIGN_TOP ); | |||
Vec inP = Vec( 16, 53 ); | |||
addInput( Port::create< PJ301MPort >( inP, | |||
Port::INPUT, | |||
module, | |||
ChipNoise::NOISE_LENGTH_INPUT ) ); | |||
addChild( ModuleLightWidget::create< SmallLight< BlueLight > >( inP.minus( Vec( 4, 4 ) ), module, ChipNoise::NOISE_FROM_INPUT ) ); | |||
int ybot = 120; | |||
addParam( ParamWidget::create< RoundSmallBlackKnob >( Vec( 16, ybot - 3 - 28 ), | |||
module, | |||
ChipNoise::NOISE_LENGTH, | |||
0, 15, 7 ) ); | |||
addChild( ModuleLightWidget::create< SmallLight< BlueLight > >( Vec( 16-4, ybot - 3 - 28 -4 ), module, ChipNoise::NOISE_FROM_KNOB ) ); | |||
addChild( MultiDigitSevenSegmentLight< BlueLight, 2, 2 >::create( Vec( 47, ybot - 5 - 24 ), | |||
module, | |||
ChipNoise::NOISE_LENGTH_LIGHT ) ); | |||
bg->addRoundedBorder( Vec( 8, 135 ), Vec( SCREW_WIDTH * 6 - 16, 160 ) ); | |||
bg->addLabel( Vec( bg->cx(), 155 ), "Sequence", 13, NVG_ALIGN_CENTER | NVG_ALIGN_BOTTOM ); | |||
addParam( ParamWidget::create< NKK >( Vec( bg->cx() - 32, 175 ), module, ChipNoise::LONG_MODE, 0, 1, 1 ) ); | |||
addParam( ParamWidget::create< NKK >( Vec( bg->cx() + 2, 175 ), module, ChipNoise::SHORT_LEN, 0, 1, 1 ) ); | |||
bg->addLabel( Vec( bg->cx() + 16 - 32, 160 ), "long", 11, NVG_ALIGN_CENTER | NVG_ALIGN_TOP ); | |||
bg->addLabel( Vec( bg->cx() + 16 - 32, 223 ), "short", 11, NVG_ALIGN_CENTER| NVG_ALIGN_TOP ); | |||
bg->addLabel( Vec( bg->cx() + 16 + 2, 160 ), "93", 11, NVG_ALIGN_CENTER | NVG_ALIGN_TOP ); | |||
bg->addLabel( Vec( bg->cx() + 16 + 2, 223 ), "31", 11, NVG_ALIGN_CENTER| NVG_ALIGN_TOP ); | |||
bg->addLabel( Vec( bg->cx(), 258 ), "Which 93 seq", 11, NVG_ALIGN_CENTER | NVG_ALIGN_BOTTOM ); | |||
addChild( MultiDigitSevenSegmentLight< BlueLight, 2, 3 >::create( Vec( 50 - 14, 262 ), | |||
module, | |||
ChipNoise::PERIOD_93_LIGHT ) ); | |||
addParam( ParamWidget::create< RoundSmallBlackKnob >( Vec( 11, 262 ), | |||
module, | |||
ChipNoise::PERIOD_93, | |||
0, 351, 17 ) ); | |||
addChild( ModuleLightWidget::create< SmallLight< BlueLight > >( Vec( 12, 249 ), module, ChipNoise::USING_93 ) ); | |||
// Output port | |||
Vec outP = Vec( bg->cx( 24 ), RACK_HEIGHT - 15 - 43 ); | |||
bg->addPlugLabel( outP, BaconBackground::SIG_OUT, "out" ); | |||
addOutput( Port::create< PJ301MPort >( outP, | |||
Port::OUTPUT, | |||
module, | |||
ChipNoise::NOISE_OUTPUT ) ); | |||
} | |||
} // namespace rack_plugin_BaconMusic | |||
using namespace rack_plugin_BaconMusic; | |||
RACK_PLUGIN_MODEL_INIT(BaconMusic, ChipNoise) { | |||
Model *modelChipNoise = Model::create<ChipNoise, ChipNoiseWidget>("Bacon Music", "ChipNoise", "ChipNoise", NOISE_TAG ); | |||
return modelChipNoise; | |||
} |
@@ -0,0 +1,391 @@ | |||
/* | |||
** All my chip simulator noobits. But remember this is just me screwing around. | |||
*/ | |||
#include <math.h> | |||
#include <iostream> | |||
#include <vector> | |||
namespace ChipSym | |||
{ | |||
class CPUStepper | |||
{ | |||
private: | |||
int sampleRateInHz; | |||
double chipFrequencyInMHZ; | |||
unsigned int ticksPerSample; | |||
double tickFractionPerSample; | |||
double accruedTickFraction; | |||
public: | |||
CPUStepper( unsigned int _sampleRateInHz, double _chipFrequencyInMHZ ) | |||
: sampleRateInHz( _sampleRateInHz ), chipFrequencyInMHZ( _chipFrequencyInMHZ ), accruedTickFraction( 0 ) | |||
{ | |||
double tpsD = chipFrequencyInMHZ * 1000000 / sampleRateInHz; | |||
double tpsdi; | |||
tickFractionPerSample = modf( tpsD, &tpsdi ); | |||
ticksPerSample = (unsigned int)tpsdi; | |||
} | |||
/* | |||
** Take one step and tell me how many CPU ticks I would have seen if I am sampling at sampleRate. | |||
** This won't be a constant of course since sometimes we get an extra to catch up | |||
*/ | |||
unsigned int nextStepCPUTicks() | |||
{ | |||
accruedTickFraction += tickFractionPerSample; | |||
if( accruedTickFraction > 1 ) | |||
{ | |||
accruedTickFraction -= 1; | |||
return ticksPerSample + 1; | |||
} | |||
return ticksPerSample; | |||
} | |||
}; | |||
static double NESNTSCCPURate = 1.789773; | |||
class NESBase | |||
{ | |||
protected: | |||
int digWavelength; // this is the 2^11 which sets frequency time | |||
int t, currPos; | |||
float wfMin, wfMax, wfMinToMax; | |||
public: | |||
NESBase( float imin, float imax ) | |||
: | |||
wfMin( imin ), wfMax( imax ) | |||
{ | |||
digWavelength = 1 << 7; // Callibrate this later | |||
t = digWavelength; | |||
currPos = 0; | |||
wfMinToMax = wfMax - wfMin; | |||
} | |||
void setDigWavelength( int df ) // 0 -> 2^11 | |||
{ | |||
digWavelength = df; | |||
} | |||
}; | |||
class NESTriangle : public NESBase // http://wiki.nesdev.com/w/index.php/APU_Triangle | |||
{ | |||
protected: | |||
float waveForm[ 32 ]; | |||
CPUStepper cpu; | |||
public: | |||
NESTriangle( float imin, float imax, unsigned int sampleRate ) | |||
: | |||
NESBase( imin, imax ), cpu( sampleRate, NESNTSCCPURate ) | |||
{ | |||
for( int i=0; i<16; ++i ) { | |||
waveForm[ 15 - i ] = i / 15.0f; | |||
waveForm[ 16 + i ] = i / 15.0f; | |||
} | |||
} | |||
float step() | |||
{ | |||
int ticks = cpu.nextStepCPUTicks(); | |||
t -= ticks; | |||
if( t < 0 ) | |||
{ | |||
currPos ++; | |||
t += digWavelength; | |||
if( currPos >= 32 ) currPos = 0; | |||
} | |||
return waveForm[ currPos ] * wfMinToMax + wfMin; | |||
} | |||
void setWavelengthInSeconds( float seconds ) | |||
{ | |||
setDigWavelength( (unsigned int)( seconds * NESNTSCCPURate * 1000 * 1000 / 32 ) ); | |||
} | |||
}; | |||
class NESArbitraryWaveform : public NESTriangle { | |||
public: | |||
NESArbitraryWaveform( float imin, float imax, unsigned int sampleRate ) : NESTriangle( imin, imax, sampleRate ) { } | |||
void setWaveformPoint( unsigned int pos, // 0->31 | |||
unsigned int val ) // 0->15 | |||
{ | |||
waveForm[ pos ] = val / 15.0f; | |||
} | |||
unsigned int getWaveformPoint( unsigned int pos ) { return waveForm[ pos ] * 15.0f; } | |||
}; | |||
class NESPulse : public NESBase // http://wiki.nesdev.com/w/index.php/APU_Pulse | |||
{ | |||
private: | |||
int dutyCycle; | |||
float **waveForms; | |||
int nDutyCycles; | |||
int wfLength; | |||
CPUStepper cpu; | |||
public: | |||
NESPulse( float imin, float imax, int sampleRate ) | |||
: | |||
NESBase( imin, imax ), cpu( sampleRate, NESNTSCCPURate / 2 ) | |||
{ | |||
wfLength = 8; | |||
nDutyCycles = 4; | |||
dutyCycle = 1; | |||
waveForms = new float*[ 4 ]; | |||
for( int i=0; i<nDutyCycles; ++i ) | |||
{ | |||
waveForms[ i ] = new float[ wfLength ]; | |||
for (int j=0; j<wfLength; ++j ) waveForms[ i ][ j ] = ( i == nDutyCycles - 1 ) ? 1 : 0; | |||
// Really, read that website for this stuff. | |||
switch( i ) | |||
{ | |||
case 0: | |||
waveForms[ i ][ 1 ] = 1; | |||
break; | |||
case 1: | |||
waveForms[ i ][ 1 ] = 1; | |||
waveForms[ i ][ 2 ] = 1; | |||
break; | |||
case 2: | |||
waveForms[ i ][ 1 ] = 1; | |||
waveForms[ i ][ 2 ] = 1; | |||
waveForms[ i ][ 3 ] = 1; | |||
waveForms[ i ][ 4 ] = 1; | |||
break; | |||
case 3: | |||
waveForms[ i ][ 1 ] = 0; | |||
waveForms[ i ][ 2 ] = 0; | |||
break; | |||
} | |||
} | |||
} | |||
void setWavelengthInSeconds( float seconds ) | |||
{ | |||
setDigWavelength( (unsigned int)( seconds * NESNTSCCPURate * 1000 * 1000 / 2.0 / 8.0) ); | |||
} | |||
void setDutyCycle( int dc ) | |||
{ | |||
dutyCycle = dc; | |||
} | |||
float step() | |||
{ | |||
int ticks = cpu.nextStepCPUTicks(); | |||
t -= ticks; | |||
if( t < 0 ) | |||
{ | |||
currPos ++; | |||
t += digWavelength; | |||
if( currPos >= wfLength ) currPos = 0; | |||
} | |||
return waveForms[ dutyCycle ][ currPos ] * wfMinToMax + wfMin; | |||
} | |||
}; | |||
class NESNoise : public NESBase | |||
{ | |||
public: | |||
typedef enum ShortPeriods | |||
{ | |||
SHORT_31, | |||
SHORT_93 | |||
} ShortPeriods; | |||
private: | |||
CPUStepper cpu; | |||
unsigned short shiftRegister; | |||
unsigned short currentOutput; | |||
unsigned short xorBit; | |||
unsigned short curr93key; | |||
std::vector< unsigned short > starts_for_93s; | |||
ShortPeriods currShortPeriods; | |||
public: | |||
NESNoise( float imin, float imax, int sampleRate ) | |||
: | |||
NESBase( imin, imax ), cpu( sampleRate, NESNTSCCPURate / 2 ) | |||
{ | |||
setPeriod( 8 ); | |||
shiftRegister = 0x07; | |||
currentOutput = shiftRegister & 1; | |||
xorBit = 1; | |||
curr93key = 17; | |||
init93(); | |||
currShortPeriods = SHORT_93; | |||
} | |||
void init93() | |||
{ | |||
// To generate this, see ../standalone/chipNoisePeriod.cpp | |||
unsigned short calc_start_for_93s[] = { | |||
1, 3, 5, 7, 11, 13, 15, 17, 19, 21, 23, 25, 29, 31, 33, | |||
35, 37, 39, 41, 43, 47, 49, 51, 53, 55, 57, 59, 61, 66, 68, | |||
70, 74, 76, 78, 80, 84, 86, 88, 90, 92, 94, 96, 98, 102, 104, | |||
106, 108, 110, 112, 114, 116, 120, 122, 124, 126, 129, 131, 133, 135, 139, | |||
141, 143, 145, 147, 149, 151, 153, 157, 159, 161, 163, 165, 167, 169, 171, | |||
175, 177, 179, 181, 183, 185, 187, 189, 194, 196, 198, 200, 202, 204, 206, | |||
208, 212, 214, 218, 220, 222, 224, 226, 230, 232, 234, 236, 238, 240, 242, | |||
244, 248, 250, 252, 254, 259, 263, 267, 271, 273, 275, 277, 279, 281, 285, | |||
287, 291, 295, 299, 303, 305, 307, 309, 311, 313, 315, 317, 322, 326, 330, | |||
334, 336, 340, 342, 344, 346, 348, 350, 354, 358, 362, 366, 368, 370, 372, | |||
376, 378, 380, 382, 387, 389, 395, 397, 399, 401, 403, 405, 407, 409, 413, | |||
415, 417, 419, 421, 423, 425, 427, 431, 435, 437, 443, 445, 450, 452, 454, | |||
456, 458, 460, 464, 468, 470, 472, 474, 478, 482, 486, 488, 490, 492, 494, | |||
498, 500, 506, 535, 543, 547, 551, 555, 559, 563, 565, 571, 573, 598, 606, | |||
610, 614, 618, 622, 626, 628, 634, 636, 645, 647, 653, 655, 661, 663, 669, | |||
671, 673, 677, 679, 681, 687, 689, 691, 693, 699, 701, 710, 716, 718, 724, | |||
726, 732, 734, 742, 744, 750, 752, 756, 760, 762, 764, 775, 779, 785, 787, | |||
789, 793, 797, 799, 803, 811, 815, 819, 821, 823, 827, 829, 834, 838, 842, | |||
852, 858, 860, 862, 870, 874, 880, 882, 884, 888, 892, 894, 901, 907, 913, | |||
915, 919, 927, 931, 933, 935, 947, 962, 970, 982, 988, 990, 994, 1000, 1004, | |||
1012, 1018, 1057, 1059, 1061, 1063, 1065, 1067, 1075, 1077, 1079, 1081, 1083, 1100, 1110, | |||
1116, 1130, 1132, 1134, 1138, 1144, 1185, 1187, 1189, 1191, 1195, 1201, 1209, 1211, 1228, | |||
1238, 1244, 1264, 1274, 1293, 1311, 1313, 1335, 1354, 1392, 1415, 1419, 1425, 1455, 1467, | |||
1510, 1524, 1569, 1620, 1638, 1671, 1868, 0 }; | |||
unsigned short* curr = calc_start_for_93s; | |||
while( *curr != 0 ) | |||
{ | |||
starts_for_93s.push_back( *curr ); | |||
++curr; | |||
} | |||
} | |||
void set93Key( int sp ) | |||
{ | |||
if( sp != curr93key ) | |||
{ | |||
setRegister( starts_for_93s[ sp ] ); | |||
} | |||
curr93key = sp; | |||
} | |||
void setModeFlag( bool mf ) | |||
{ | |||
if( mf ) | |||
{ | |||
xorBit = 6; | |||
// set register based on cached shortlen | |||
setShortLength( currShortPeriods ); | |||
} | |||
else xorBit = 1; | |||
} | |||
void setShortLength( ShortPeriods p ) | |||
{ | |||
currShortPeriods = p; | |||
if( p == SHORT_31 ) | |||
{ | |||
setRegister( 0x0737 ); | |||
} | |||
else | |||
{ | |||
// A little state so we don't always land on the same one | |||
setRegister( starts_for_93s[ curr93key ] ); | |||
} | |||
} | |||
void setPeriod( unsigned int c ) // 0 - 15 | |||
{ | |||
if( c > 15 ) c = 8; | |||
switch( c ) { | |||
case 0: | |||
digWavelength = 4; break; | |||
case 1: | |||
digWavelength = 8; break; | |||
case 2: | |||
digWavelength = 16; break; | |||
case 3: | |||
digWavelength = 32; break; | |||
case 4: | |||
digWavelength = 64; break; | |||
case 5: | |||
digWavelength = 96; break; | |||
case 6: | |||
digWavelength = 128; break; | |||
case 7: | |||
digWavelength = 160; break; | |||
case 8: | |||
digWavelength = 202; break; | |||
case 9: | |||
digWavelength = 254; break; | |||
case 10: | |||
digWavelength = 380; break; | |||
case 11: | |||
digWavelength = 508; break; | |||
case 12: | |||
digWavelength = 762; break; | |||
case 13: | |||
digWavelength = 1016; break; | |||
case 14: | |||
digWavelength = 2034; break; | |||
case 15: | |||
digWavelength = 4068; break; | |||
} | |||
} | |||
void advanceRegister() | |||
{ | |||
// Do the LFSR Shift | |||
unsigned short bit = ((shiftRegister >> 0) ^ (shiftRegister >> xorBit)) & 1; | |||
shiftRegister = (shiftRegister >> 1) | (bit << 14); // thanks https://github.com/baconpaul/BaconPlugs/issues/6 | |||
} | |||
float step() | |||
{ | |||
int ticks = cpu.nextStepCPUTicks(); | |||
t -= ticks; | |||
if( t < 0 ) | |||
{ | |||
t += digWavelength; | |||
advanceRegister(); | |||
currentOutput = shiftRegister & 1; | |||
} | |||
return currentOutput * wfMinToMax + wfMin; | |||
} | |||
void setRegister( unsigned short r ) { shiftRegister = r; } | |||
unsigned short getRegister() { return shiftRegister; } | |||
}; | |||
#if 0 | |||
class LFSRGeneralImpl | |||
{ | |||
public: | |||
typedef std::bitset< 24 > bits; | |||
private: | |||
public: | |||
void setActivetBits( size_t aBits ) // < 24 please | |||
{ | |||
} | |||
void setTapsAsInt( unsigned int taps ) // so 1 << 16 & 1 << 14 & 1 << 7 type thing | |||
{ | |||
} | |||
}; | |||
#endif | |||
}; |
@@ -0,0 +1,128 @@ | |||
#include "BaconPlugs.hpp" | |||
#include "ChipSym.hpp" | |||
namespace rack_plugin_BaconMusic { | |||
struct ChipWaves : virtual Module { | |||
enum ParamIds { | |||
FREQ_KNOB, | |||
PULSE_CYCLE, | |||
NUM_PARAMS | |||
}; | |||
enum InputIds { | |||
FREQ_CV, | |||
NUM_INPUTS | |||
}; | |||
enum OutputIds { | |||
PULSE_OUTPUT, | |||
TRI_OUTPUT, | |||
NUM_OUTPUTS | |||
}; | |||
enum LightIds { | |||
PULSE_CYCLE_LIGHT, | |||
NUM_LIGHTS | |||
}; | |||
ChipSym::NESPulse npulse; | |||
ChipSym::NESTriangle ntri; | |||
ChipWaves() : Module( NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS ), | |||
npulse( -5.0, 5.0, engineGetSampleRate() ), | |||
ntri( -5.0, 5.0, engineGetSampleRate() ) | |||
{ | |||
npulse.setDigWavelength( 2<<9 ); | |||
ntri.setDigWavelength( 2<<8 ); | |||
} | |||
float digWFInSeconds( float pitchKnob, float pitchCV ) | |||
{ | |||
// This is the frequency tuning used in Fundamental/VCO so lets be consistent | |||
float pitch = pitchKnob + pitchCV; | |||
float freq = 261.626f * powf( 2.0f, pitch / 12.0f ); | |||
// OK so now we have the frequency. We need the wavelength though. Simple | |||
float wl = 1.0f / freq; | |||
return wl; | |||
} | |||
void step() override | |||
{ | |||
float dwf = digWFInSeconds( params[ FREQ_KNOB ].value, 12.0f * inputs[ FREQ_CV ].value ); | |||
ntri.setWavelengthInSeconds( dwf ); | |||
npulse.setWavelengthInSeconds( dwf ); | |||
int dc = clamp( (int)(params[ PULSE_CYCLE ].value ), 0, 3 ); | |||
npulse.setDutyCycle( dc ); | |||
lights[ PULSE_CYCLE_LIGHT ].value = dc; | |||
if( outputs[ TRI_OUTPUT ].active ) | |||
outputs[ TRI_OUTPUT ].value = ntri.step(); | |||
if( outputs[ PULSE_OUTPUT ].active ) | |||
outputs[ PULSE_OUTPUT ].value = npulse.step(); | |||
} | |||
}; | |||
struct ChipWavesWidget : ModuleWidget { | |||
ChipWavesWidget( ChipWaves *module); | |||
}; | |||
ChipWavesWidget::ChipWavesWidget( ChipWaves *module ) : ModuleWidget( module ) | |||
{ | |||
box.size = Vec( SCREW_WIDTH * 8, RACK_HEIGHT ); | |||
BaconBackground *bg = new BaconBackground( box.size, "ChipWaves" ); | |||
addChild( bg->wrappedInFramebuffer()); | |||
Vec outP = Vec( bg->cx( 24 ) + 25, RACK_HEIGHT - 15 - 43 ); | |||
bg->addPlugLabel( outP, BaconBackground::SIG_OUT, "pulse" ); | |||
addOutput( Port::create< PJ301MPort >( outP, | |||
Port::OUTPUT, | |||
module, | |||
ChipWaves::PULSE_OUTPUT ) ); | |||
Vec outT = Vec( bg->cx( 24 ) - 25, RACK_HEIGHT - 15 - 43 ); | |||
bg->addPlugLabel( outT, BaconBackground::SIG_OUT, "tri" ); | |||
addOutput( Port::create< PJ301MPort >( outT, | |||
Port::OUTPUT, | |||
module, | |||
ChipWaves::TRI_OUTPUT ) ); | |||
Vec fcv = Vec( bg->cx( 24 ) + 35, 160 ); | |||
bg->addPlugLabel( fcv, BaconBackground::SIG_IN, "v/o" ); | |||
addInput( Port::create< PJ301MPort >( fcv, | |||
Port::INPUT, | |||
module, | |||
ChipWaves::FREQ_CV ) ); | |||
bg->addRoundedBorder( Vec( 10, 140 ), Vec ( 63, 49 ) ); | |||
bg->addLabel( Vec( 40, 144 ), "Duty Cycle", 12, NVG_ALIGN_CENTER | NVG_ALIGN_TOP ); | |||
int ybot = 140 + 24 + 5 + 20; | |||
addParam( ParamWidget::create< RoundSmallBlackKnob >( Vec( 16, ybot - 3 - 28 ), | |||
module, | |||
ChipWaves::PULSE_CYCLE, | |||
0, 3, 0 ) ); | |||
addChild( ModuleLightWidget::create< SevenSegmentLight< BlueLight, 2 > >( Vec( 47, ybot - 5 - 24 ), | |||
module, | |||
ChipWaves::PULSE_CYCLE_LIGHT ) ); | |||
bg->addLabel( Vec( bg->cx(), 45 ), "Freq", 14, NVG_ALIGN_CENTER | NVG_ALIGN_BOTTOM ); | |||
addParam( ParamWidget::create< RoundHugeBlackKnob >( Vec( bg->cx( 56 ), 50 ), module, | |||
ChipWaves::FREQ_KNOB, -54.0f, 54.0f, 0.0f ) ); | |||
} | |||
} // namespace rack_plugin_BaconMusic | |||
using namespace rack_plugin_BaconMusic; | |||
RACK_PLUGIN_MODEL_INIT(BaconMusic, ChipWaves) { | |||
Model *modelChipWaves = Model::create<ChipWaves, ChipWavesWidget>("Bacon Music", "ChipWaves", "ChipWaves", OSCILLATOR_TAG ); | |||
return modelChipWaves; | |||
} |
@@ -0,0 +1,108 @@ | |||
#include "BaconPlugs.hpp" | |||
#include "ChipSym.hpp" | |||
namespace rack_plugin_BaconMusic { | |||
struct ChipYourWave : virtual Module { | |||
enum ParamIds { | |||
FREQ_KNOB, | |||
WAVEFORM_START, | |||
NUM_PARAMS = WAVEFORM_START + 32 | |||
}; | |||
enum InputIds { | |||
FREQ_CV, | |||
NUM_INPUTS | |||
}; | |||
enum OutputIds { | |||
WAVE_OUTPUT, | |||
NUM_OUTPUTS | |||
}; | |||
enum LightIds { | |||
NUM_LIGHTS | |||
}; | |||
ChipSym::NESArbitraryWaveform narb; | |||
ChipYourWave() : Module( NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS ), | |||
narb( -5.0, 5.0, engineGetSampleRate() ) | |||
{ | |||
narb.setDigWavelength( 2<<8 ); | |||
} | |||
float digWFInSeconds( float pitchKnob, float pitchCV ) | |||
{ | |||
// This is the frequency tuning used in Fundamental/VCO so lets be consistent | |||
float pitch = pitchKnob + pitchCV; | |||
float freq = 261.626f * powf( 2.0f, pitch / 12.0f ); | |||
// OK so now we have the frequency. We need the wavelength though. Simple | |||
float wl = 1.0f / freq; | |||
return wl; | |||
} | |||
void step() override | |||
{ | |||
float dwf = digWFInSeconds( params[ FREQ_KNOB ].value, 12.0f * inputs[ FREQ_CV ].value ); | |||
narb.setWavelengthInSeconds( dwf ); | |||
for( int i=0; i<32; ++i ) | |||
narb.setWaveformPoint( i, params[ WAVEFORM_START + i ].value ); | |||
if( outputs[ WAVE_OUTPUT ].active ) | |||
outputs[ WAVE_OUTPUT ].value = narb.step(); | |||
} | |||
}; | |||
struct ChipYourWaveWidget : ModuleWidget { | |||
ChipYourWaveWidget( ChipYourWave *module); | |||
}; | |||
ChipYourWaveWidget::ChipYourWaveWidget( ChipYourWave *module ) : ModuleWidget( module ) | |||
{ | |||
box.size = Vec( SCREW_WIDTH * 23, RACK_HEIGHT ); | |||
BaconBackground *bg = new BaconBackground( box.size, "ChipYourWave" ); | |||
addChild( bg->wrappedInFramebuffer()); | |||
Vec outP = Vec( box.size.x - 40, 45 + 30 ); | |||
bg->addPlugLabel( outP, BaconBackground::SIG_OUT, "out" ); | |||
addOutput( Port::create< PJ301MPort >( outP, | |||
Port::OUTPUT, | |||
module, | |||
ChipYourWave::WAVE_OUTPUT ) ); | |||
bg->addLabel( Vec( 50, 45 ), "Freq", 14, NVG_ALIGN_LEFT | NVG_ALIGN_BOTTOM ); | |||
addParam( ParamWidget::create< RoundHugeBlackKnob >( Vec( 10, 50 ), module, | |||
ChipYourWave::FREQ_KNOB, -54.0f, 54.0f, 0.0f ) ); | |||
Vec fcv = Vec( 56 + 20, 45 + 30 ); | |||
bg->addPlugLabel( fcv, BaconBackground::SIG_IN, "v/o" ); | |||
addInput( Port::create< PJ301MPort >( fcv, | |||
Port::INPUT, | |||
module, | |||
ChipYourWave::FREQ_CV ) ); | |||
bg->addLabel( Vec( bg->cx(), 135 ), "Draw your Digital Waveform Here", 14, NVG_ALIGN_CENTER | NVG_ALIGN_BOTTOM ); | |||
for( int i=0; i<32; ++i ) | |||
{ | |||
addParam( ParamWidget::create< NStepDraggableLEDWidget< 16, RedGreenFromMiddleColorModel >>( Vec( 10 + 10 * i, 140 ), module, | |||
ChipYourWave::WAVEFORM_START + i, | |||
0, 15, | |||
module->narb.getWaveformPoint( i )) ); | |||
} | |||
} | |||
} // namespace rack_plugin_BaconMusic | |||
using namespace rack_plugin_BaconMusic; | |||
RACK_PLUGIN_MODEL_INIT(BaconMusic, ChipYourWave) { | |||
Model *modelChipYourWave = Model::create<ChipYourWave, ChipYourWaveWidget>("Bacon Music", "ChipYourWave", "ChipYourWave", OSCILLATOR_TAG ); | |||
return modelChipYourWave; | |||
} |
@@ -0,0 +1,295 @@ | |||
#include "BaconPlugs.hpp" | |||
#include <jansson.h> | |||
struct InternalFontMgr | |||
{ | |||
static std::map< std::string, int > fontMap; | |||
static int get( NVGcontext *vg, std::string resName ) | |||
{ | |||
if( fontMap.find( resName ) == fontMap.end() ) | |||
{ | |||
fontMap[ resName ] = nvgCreateFont( vg, resName.c_str(), assetPlugin( plugin, resName.c_str() ).c_str() ); | |||
} | |||
return fontMap[ resName ]; | |||
} | |||
}; | |||
std::map< std::string, int > InternalFontMgr::fontMap; | |||
struct InternalRoundedBorder : virtual TransparentWidget | |||
{ | |||
bool doFill; | |||
NVGcolor fillColor; | |||
InternalRoundedBorder( Vec pos, Vec sz, NVGcolor fc ) | |||
{ | |||
box.pos = pos; | |||
box.size = sz; | |||
doFill = true; | |||
fillColor = fc; | |||
} | |||
InternalRoundedBorder( Vec pos, Vec sz ) | |||
{ | |||
box.pos = pos; | |||
box.size = sz; | |||
doFill = false; | |||
} | |||
void draw( NVGcontext *vg ) override | |||
{ | |||
nvgBeginPath( vg ); | |||
nvgRoundedRect( vg, 0, 0, box.size.x, box.size.y, 5 ); | |||
if( doFill ) | |||
{ | |||
nvgFillColor( vg, fillColor ); | |||
nvgFill( vg ); | |||
} | |||
nvgStrokeColor( vg, COLOR_BLACK ); | |||
nvgStroke( vg ); | |||
} | |||
}; | |||
struct InternalTextLabel : virtual TransparentWidget | |||
{ | |||
int memFont = -1; | |||
std::string label; | |||
int pxSize; | |||
int align; | |||
NVGcolor color; | |||
InternalTextLabel( Vec pos, const char* lab, int px, int al, NVGcolor col ) : label( lab ), pxSize( px ), align( al ), color( col ) | |||
{ | |||
box.pos = pos; | |||
} | |||
void draw( NVGcontext *vg ) override { | |||
if( memFont < 0 ) | |||
memFont = InternalFontMgr::get( vg, "res/Monitorica-Bd.ttf" ); | |||
nvgBeginPath( vg ); | |||
nvgFontFaceId( vg, memFont ); | |||
nvgFontSize( vg, pxSize ); | |||
nvgFillColor( vg, color ); | |||
nvgTextAlign( vg, align ); | |||
nvgText( vg, 0, 0, label.c_str(), NULL ); | |||
} | |||
}; | |||
struct InternalPlugLabel : virtual TransparentWidget | |||
{ | |||
int memFont = -1; | |||
BaconBackground::LabelStyle st; | |||
BaconBackground::LabelAt at; | |||
std::string label; | |||
InternalPlugLabel( Vec portPos, BaconBackground::LabelAt l, BaconBackground::LabelStyle s, const char* ilabel ); | |||
void draw( NVGcontext *vg ) override; | |||
}; | |||
void BaconBackground::draw( NVGcontext *vg ) | |||
{ | |||
if( memFont < 0 ) | |||
memFont = InternalFontMgr::get( vg, "res/Monitorica-Bd.ttf" ); | |||
nvgBeginPath( vg ); | |||
nvgRect( vg, 0, 0, box.size.x, box.size.y ); | |||
nvgFillColor( vg, BaconBackground::bg ); | |||
nvgFill( vg ); | |||
nvgBeginPath( vg ); | |||
nvgMoveTo( vg, 0, 0 ); | |||
nvgLineTo( vg, box.size.x, 0 ); | |||
nvgLineTo( vg, box.size.x, box.size.y ); | |||
nvgLineTo( vg, 0, box.size.y ); | |||
nvgLineTo( vg, 0, 0 ); | |||
nvgStrokeColor( vg, BaconBackground::bgOutline ); | |||
nvgStroke( vg ); | |||
nvgFontFaceId( vg, memFont ); | |||
nvgFontSize( vg, 14 ); | |||
nvgFillColor( vg, COLOR_BLACK ); | |||
nvgStrokeColor( vg, COLOR_BLACK ); | |||
nvgTextAlign( vg, NVG_ALIGN_CENTER|NVG_ALIGN_BOTTOM ); | |||
nvgText( vg, box.size.x / 2, box.size.y - 5, "Bacon Music", NULL ); | |||
nvgFontFaceId( vg, memFont ); | |||
nvgFontSize( vg, 16 ); | |||
nvgFillColor( vg, COLOR_BLACK ); | |||
nvgStrokeColor( vg, COLOR_BLACK ); | |||
nvgTextAlign( vg, NVG_ALIGN_CENTER|NVG_ALIGN_TOP ); | |||
nvgText( vg, box.size.x / 2, 5, title.c_str(), NULL ); | |||
for( auto w : children ) | |||
{ | |||
nvgTranslate( vg, w->box.pos.x, w->box.pos.y ); | |||
w->draw( vg ); | |||
nvgTranslate( vg, -w->box.pos.x, -w->box.pos.y ); | |||
} | |||
for( auto it = rects.begin(); it != rects.end(); ++it ) | |||
{ | |||
col_rect_t tu = *it; | |||
Rect r = std::get< 0 >(tu); | |||
NVGcolor c = std::get< 1 >(tu); | |||
bool f = std::get< 2 >(tu); | |||
nvgBeginPath( vg ); | |||
nvgRect( vg, r.pos.x, r.pos.y, r.size.x, r.size.y ); | |||
if( f ) | |||
{ | |||
nvgFillColor( vg, c ); | |||
nvgFill( vg ); | |||
} | |||
else | |||
{ | |||
nvgStrokeColor( vg, c ); | |||
nvgStroke( vg ); | |||
} | |||
} | |||
} | |||
InternalPlugLabel::InternalPlugLabel( Vec portPos, BaconBackground::LabelAt l, BaconBackground::LabelStyle s, const char* ilabel ) | |||
: | |||
st( s ), at( l ), label( ilabel ) | |||
{ | |||
box.size.x = 24 + 5; | |||
box.size.y = 24 + 5 + 20; | |||
// switch on position but for now just do above | |||
box.pos.x = portPos.x - 2.5; | |||
box.pos.y = portPos.y - 2.5 - 17; | |||
} | |||
void InternalPlugLabel::draw( NVGcontext *vg ) | |||
{ | |||
if( memFont < 0 ) | |||
memFont = InternalFontMgr::get( vg, "res/Monitorica-Bd.ttf" ); | |||
NVGcolor txtCol = COLOR_BLACK; | |||
switch( st ) { | |||
case( BaconBackground::SIG_IN ) : | |||
{ | |||
nvgBeginPath( vg ); | |||
nvgRoundedRect( vg, 0, 0, box.size.x, box.size.y, 5 ); | |||
nvgStrokeColor( vg, COLOR_BLACK ); | |||
nvgStroke( vg ); | |||
break; | |||
} | |||
case( BaconBackground::SIG_OUT ) : | |||
{ | |||
nvgBeginPath( vg ); | |||
nvgRoundedRect( vg, 0, 0, box.size.x, box.size.y, 5 ); | |||
nvgFillColor( vg, BaconBackground::highlight ); | |||
nvgFill( vg ); | |||
nvgStrokeColor( vg, COLOR_BLACK ); | |||
nvgStroke( vg ); | |||
txtCol = COLOR_WHITE; | |||
break; | |||
} | |||
case( BaconBackground::OTHER ) : | |||
{ | |||
nvgBeginPath( vg ); | |||
nvgRoundedRect( vg, 0, 0, box.size.x, box.size.y, 5 ); | |||
nvgStrokeColor( vg, COLOR_RED ); | |||
nvgStroke( vg ); | |||
break; | |||
} | |||
} | |||
nvgFontFaceId( vg, memFont ); | |||
nvgFontSize( vg, 13 ); | |||
nvgFillColor( vg, txtCol ); | |||
nvgTextAlign( vg, NVG_ALIGN_CENTER|NVG_ALIGN_TOP ); | |||
nvgText( vg, box.size.x / 2, 3, label.c_str(), NULL ); | |||
} | |||
BaconBackground *BaconBackground::addLabel( Vec pos, const char* lab, int px, int align, NVGcolor col ) | |||
{ | |||
addChild( new InternalTextLabel( pos, lab, px, align, col ) ); | |||
return this; | |||
} | |||
BaconBackground *BaconBackground::addPlugLabel( Vec plugPos, LabelAt l, LabelStyle s, const char* ilabel ) | |||
{ | |||
addChild( new InternalPlugLabel( plugPos, l, s, ilabel ) ); | |||
return this; | |||
} | |||
BaconBackground *BaconBackground::addRoundedBorder( Vec pos, Vec sz ) | |||
{ | |||
addChild( new InternalRoundedBorder( pos, sz ) ); | |||
return this; | |||
} | |||
BaconBackground *BaconBackground::addRoundedBorder( Vec pos, Vec sz, NVGcolor fill ) | |||
{ | |||
addChild( new InternalRoundedBorder( pos, sz, fill ) ); | |||
return this; | |||
} | |||
NVGcolor BaconBackground::bg = nvgRGBA( 220, 220, 210, 255 ); | |||
NVGcolor BaconBackground::bgOutline = nvgRGBA( 180, 180, 170, 255 ); | |||
NVGcolor BaconBackground::highlight = nvgRGBA( 90, 90, 60, 255 ); | |||
BaconBackground::BaconBackground( Vec size, const char* lab ) : title( lab ) | |||
{ | |||
box.pos = Vec( 0, 0 ); | |||
box.size = size; | |||
} | |||
FramebufferWidget* BaconBackground::wrappedInFramebuffer() | |||
{ | |||
FramebufferWidget *fb = new FramebufferWidget(); | |||
fb->box = box; | |||
fb->addChild( this ); | |||
return fb; | |||
} | |||
BaconBackground *BaconBackground::addLabelsForHugeKnob( Vec topLabelPos, | |||
const char* knobLabel, | |||
const char* zeroLabel, | |||
const char* oneLabel, | |||
Vec &putKnobHere ) | |||
{ | |||
addLabel( topLabelPos, knobLabel, 14, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE ); | |||
addLabel( Vec( topLabelPos.x + 10, topLabelPos.y + 72 ), | |||
oneLabel, 13, NVG_ALIGN_LEFT | NVG_ALIGN_TOP ); | |||
addLabel( Vec( topLabelPos.x - 10, topLabelPos.y + 72 ), | |||
zeroLabel, 13, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP ); | |||
putKnobHere.y = topLabelPos.y + 10; | |||
putKnobHere.x = topLabelPos.x - 28; | |||
return this; | |||
} | |||
BaconBackground *BaconBackground::addLabelsForLargeKnob( Vec topLabelPos, | |||
const char* knobLabel, | |||
const char* zeroLabel, | |||
const char* oneLabel, | |||
Vec &putKnobHere ) | |||
{ | |||
addLabel( topLabelPos, knobLabel, 14, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE ); | |||
addLabel( Vec( topLabelPos.x + 10, topLabelPos.y + 48 ), | |||
oneLabel, 13, NVG_ALIGN_LEFT | NVG_ALIGN_TOP ); | |||
addLabel( Vec( topLabelPos.x - 10, topLabelPos.y + 48 ), | |||
zeroLabel, 13, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP ); | |||
putKnobHere.y = topLabelPos.y + 10; | |||
putKnobHere.x = topLabelPos.x - 18; | |||
return this; | |||
} |
@@ -0,0 +1,592 @@ | |||
#ifndef INCLUDE_COMPONENTS_HPP | |||
#define INCLUDE_COMPONENTS_HPP | |||
#include "rack.hpp" | |||
#include <map> | |||
#include <vector> | |||
#include <string> | |||
#include <tuple> | |||
#include <functional> | |||
#include <locale> | |||
#include <thread> | |||
#include "GraduatedFader.hpp" | |||
#include "BufferedDrawFunction.hpp" | |||
using namespace rack; | |||
template <typename T, int px = 4> | |||
struct SevenSegmentLight : T { | |||
int lx, ly, ppl; | |||
std::vector< Rect > unscaledLoc; | |||
int elementsByNum[ 10 ][ 7 ] = { | |||
{ 1, 1, 1, 1, 1, 1, 0 }, | |||
{ 0, 1, 1, 0, 0, 0, 0 }, | |||
{ 1, 1, 0, 1, 1, 0, 1 }, | |||
{ 1, 1, 1, 1, 0, 0, 1 }, | |||
{ 0, 1, 1, 0, 0, 1, 1 }, | |||
{ 1, 0, 1, 1, 0, 1, 1 }, | |||
{ 1, 0, 1, 1, 1, 1, 1 }, | |||
{ 1, 1, 1, 0, 0, 0, 0 }, | |||
{ 1, 1, 1, 1, 1, 1, 1 }, | |||
{ 1, 1, 1, 1, 0, 1, 1 } | |||
}; | |||
const static int sx = px * 6 + 2; | |||
const static int sy = px * 10 + 2; // match with lx-1 and ly-1 below | |||
int pvalue; | |||
int decimalPos; | |||
BufferedDrawFunctionWidget<SevenSegmentLight<T, px>> *buffer; | |||
SevenSegmentLight( ) | |||
{ | |||
lx = 7; | |||
ly = 11; | |||
ppl = px; | |||
pvalue = 0; | |||
this->box.size = Vec( sx, sy ); | |||
decimalPos = 1; | |||
// https://en.wikipedia.org/wiki/Seven-segment_display#/media/File:7_segment_display_labeled.svg | |||
unscaledLoc.push_back( Rect( Vec( 2, 1 ), Vec( 3, 1 ) ) ); | |||
unscaledLoc.push_back( Rect( Vec( 5, 2 ), Vec( 1, 3 ) ) ); | |||
unscaledLoc.push_back( Rect( Vec( 5, 6 ), Vec( 1, 3 ) ) ); | |||
unscaledLoc.push_back( Rect( Vec( 2, 9 ), Vec( 3, 1 ) ) ); | |||
unscaledLoc.push_back( Rect( Vec( 1, 6 ), Vec( 1, 3 ) ) ); | |||
unscaledLoc.push_back( Rect( Vec( 1, 2 ), Vec( 1, 3 ) ) ); | |||
unscaledLoc.push_back( Rect( Vec( 2, 5 ), Vec( 3, 1 ) ) ); | |||
buffer = new BufferedDrawFunctionWidget<SevenSegmentLight<T,px>>( Vec( 0, 0 ), this->box.size, | |||
this, | |||
&SevenSegmentLight<T,px>::drawSegments ); | |||
this->addChild( buffer ); | |||
} | |||
void draw( NVGcontext *vg ) override | |||
{ | |||
float fvalue = this->module->lights[ this->firstLightId ].value; | |||
int value = int( fvalue / decimalPos ) % 10; | |||
if( value != pvalue ) | |||
{ | |||
buffer->dirty = true; | |||
} | |||
pvalue = value; | |||
buffer->draw( vg ); | |||
} | |||
void drawSegments( NVGcontext *vg ) | |||
{ | |||
// This is now buffered to only be called when the value has changed | |||
int w = this->box.size.x; | |||
int h = this->box.size.y; | |||
nvgBeginPath( vg ); | |||
nvgRect( vg, 0, 0, w, h ); | |||
nvgFillColor( vg, nvgRGBA( 25, 35, 25, 255 ) ); | |||
nvgFill( vg ); | |||
int i=0; | |||
// float fvalue = this->module->lights[ this->firstLightId ].value; | |||
// int value = clamp( fvalue, 0.0f, 9.0f ); | |||
int *ebn = elementsByNum[ pvalue ]; | |||
NVGcolor oncol = this->baseColors[ 0 ]; | |||
for( auto it = unscaledLoc.begin(); it < unscaledLoc.end(); ++it ) | |||
{ | |||
float y = it->pos.y - 0.5; | |||
float x = it->pos.x - 0.5; | |||
int ew = it->size.x; | |||
int eh = it->size.y; | |||
nvgBeginPath( vg ); | |||
// New version with corners | |||
float x0 = x * ppl + 1; | |||
float y0 = y * ppl + 1; | |||
float w = ew * ppl; | |||
float h = eh * ppl; | |||
float tri = ppl / 2; | |||
if( eh == 1 ) | |||
{ | |||
// This is a sideways element | |||
nvgMoveTo( vg, x0, y0 ); | |||
nvgLineTo( vg, x0 + w, y0 ); | |||
nvgLineTo( vg, x0 + w + tri, y0 + tri ); | |||
nvgLineTo( vg, x0 + w, y0 + h); | |||
nvgLineTo( vg, x0, y0 + h); | |||
nvgLineTo( vg, x0 - tri, y0 + tri ); | |||
nvgClosePath( vg ); | |||
} | |||
else | |||
{ | |||
nvgMoveTo( vg, x0, y0 ); | |||
nvgLineTo( vg, x0, y0 + h ); | |||
nvgLineTo( vg, x0 + tri, y0 + h + tri ); | |||
nvgLineTo( vg, x0 + w, y0 + h); | |||
nvgLineTo( vg, x0 + w, y0); | |||
nvgLineTo( vg, x0 + tri, y0 - tri ); | |||
} | |||
// Old version nvgRect( vg, x * ppl + 1, y * ppl + 1, ew * ppl, eh * ppl ); | |||
if( ebn[ i ] > 0 ) | |||
{ | |||
nvgFillColor( vg, oncol ); | |||
nvgFill( vg ); | |||
} | |||
else | |||
{ | |||
nvgFillColor( vg, nvgRGBA( 50, 70, 50, 255 ) ); | |||
nvgFill( vg ); | |||
} | |||
++i; | |||
} | |||
} | |||
static SevenSegmentLight< T, px > *create(Vec pos, Module *module, int firstLightId, int decimal) { | |||
auto *o = ModuleLightWidget::create<SevenSegmentLight<T,px>>(pos, module, firstLightId); | |||
o->decimalPos = decimal; | |||
return o; | |||
} | |||
}; | |||
template <typename colorClass, int px, int digits> | |||
struct MultiDigitSevenSegmentLight : ModuleLightWidget | |||
{ | |||
typedef SevenSegmentLight< colorClass, px > LtClass; | |||
MultiDigitSevenSegmentLight() : ModuleLightWidget() | |||
{ | |||
this->box.size = Vec( digits * LtClass::sx, LtClass::sy ); | |||
} | |||
static MultiDigitSevenSegmentLight< colorClass, px, digits > *create(Vec pos, Module *module, int firstLightId) { | |||
auto *o = ModuleLightWidget::create<MultiDigitSevenSegmentLight<colorClass, px ,digits>>(pos, module, firstLightId); | |||
o->layout(); | |||
return o; | |||
} | |||
void layout() | |||
{ | |||
int dp = 1; | |||
for( int i=0; i<digits-1; ++i ) dp *= 10; | |||
for( int i=0; i<digits; ++i ) | |||
{ | |||
addChild( LtClass::create( Vec( i * LtClass::sx, 0 ), module, firstLightId, dp ) ); | |||
dp /= 10; | |||
} | |||
} | |||
void draw( NVGcontext *vg ) override | |||
{ | |||
for( auto it = children.begin(); it != children.end(); ++it ) | |||
{ | |||
nvgSave( vg ); | |||
nvgTranslate( vg, (*it)->box.pos.x, (*it)->box.pos.y ); | |||
(*it)->draw( vg ); | |||
nvgRestore( vg ); | |||
} | |||
} | |||
}; | |||
struct BaconBackground : virtual TransparentWidget | |||
{ | |||
static NVGcolor bg; | |||
static NVGcolor bgOutline; | |||
static NVGcolor highlight; | |||
typedef std::tuple< Rect, NVGcolor, bool > col_rect_t; | |||
std::vector< col_rect_t > rects; | |||
int memFont = -1; | |||
std::string title; | |||
enum LabelAt { | |||
ABOVE, | |||
BELOW, | |||
LEFT, | |||
RIGHT | |||
}; | |||
enum LabelStyle { | |||
SIG_IN, | |||
SIG_OUT, | |||
OTHER | |||
}; | |||
int cx() { return box.size.x / 2; } | |||
int cx( int w ) { return (box.size.x-w) / 2; } | |||
BaconBackground( Vec size, const char* lab ); | |||
~BaconBackground() { } | |||
BaconBackground *addLabel( Vec pos, const char* lab, int px ) | |||
{ | |||
return addLabel( pos, lab, px, NVG_ALIGN_CENTER | NVG_ALIGN_BOTTOM, COLOR_BLACK ); | |||
} | |||
BaconBackground *addLabel( Vec pos, const char* lab, int px, int align ) | |||
{ | |||
return addLabel( pos, lab, px, align, COLOR_BLACK ); | |||
} | |||
BaconBackground *addLabel( Vec pos, const char* lab, int px, int align, NVGcolor col ); | |||
BaconBackground *addPlugLabel( Vec plugPos, LabelStyle s, const char* ilabel ) { | |||
return addPlugLabel( plugPos, LabelAt::ABOVE, s, ilabel ); | |||
} | |||
BaconBackground *addPlugLabel( Vec plugPos, LabelAt l, LabelStyle s, const char* ilabel ); | |||
BaconBackground *addRoundedBorder( Vec pos, Vec sz ); | |||
BaconBackground *addRoundedBorder( Vec pos, Vec sz, NVGcolor fill ); | |||
BaconBackground *addLabelsForHugeKnob( Vec topLabelPos, const char* knobLabel, const char* zeroLabel, const char* oneLabel, Vec &putKnobHere ); | |||
BaconBackground *addLabelsForLargeKnob( Vec topLabelPos, const char* knobLabel, const char* zeroLabel, const char* oneLabel, Vec &putKnobHere ); | |||
BaconBackground *addFilledRect( Vec pos, Vec sz, NVGcolor fill ) | |||
{ | |||
Rect r; | |||
r.pos = pos; r.size = sz; | |||
rects.push_back( col_rect_t( r, fill, true ) ); | |||
return this; | |||
} | |||
BaconBackground *addRect( Vec pos, Vec sz, NVGcolor fill ) | |||
{ | |||
Rect r; | |||
r.pos = pos; r.size = sz; | |||
rects.push_back( col_rect_t( r, fill, false ) ); | |||
return this; | |||
} | |||
void draw( NVGcontext *vg ) override; | |||
FramebufferWidget *wrappedInFramebuffer(); | |||
}; | |||
struct BaconHelpButton : public SVGButton | |||
{ | |||
std::string url; | |||
BaconHelpButton( std::string urli ) : url( urli ) | |||
{ | |||
box.pos = Vec( 0, 0 ); | |||
box.size = Vec( 20, 20 ); | |||
setSVGs( SVG::load( assetPlugin( plugin, "res/HelpActiveSmall.svg" ) ), NULL ); | |||
url = "https://github.com/baconpaul/BaconPlugs/blob/"; | |||
#ifdef RELEASE_BRANCH | |||
url += TO_STRING( RELEASE_BRANCH ); | |||
#else | |||
url += "master/"; | |||
#endif | |||
url += urli; | |||
info( "Help button configured to: %s", url.c_str() ); | |||
} | |||
void onAction( EventAction &e ) override { | |||
std::thread t( [this]() { | |||
systemOpenBrowser(url.c_str() ); | |||
} ); | |||
t.detach(); | |||
} | |||
}; | |||
template< int NSteps, typename ColorModel > | |||
struct NStepDraggableLEDWidget : public ParamWidget | |||
{ | |||
BufferedDrawFunctionWidget<NStepDraggableLEDWidget<NSteps, ColorModel>> *buffer; | |||
bool dragging; | |||
Vec lastDragPos; | |||
ColorModel cm; | |||
NStepDraggableLEDWidget() | |||
{ | |||
box.size = Vec( 10, 200 ); | |||
dragging = false; | |||
lastDragPos = Vec( -1, -1 ); | |||
buffer = new BufferedDrawFunctionWidget<NStepDraggableLEDWidget<NSteps, ColorModel>>( Vec( 0, 0 ), this->box.size, | |||
this, | |||
&NStepDraggableLEDWidget<NSteps, ColorModel>::drawSegments ); | |||
} | |||
int getStep() | |||
{ | |||
float pvalue = this->module->params[ this->paramId ].value; | |||
int step = (int)pvalue; | |||
return step; | |||
} | |||
int impStep( float yp ) | |||
{ | |||
float py = (box.size.y - yp)/box.size.y; | |||
return (int)( py * NSteps ); | |||
} | |||
void draw( NVGcontext *vg ) override | |||
{ | |||
buffer->draw( vg ); | |||
} | |||
void valueByMouse( float ey ) | |||
{ | |||
if( impStep( ey ) != getStep() ) | |||
{ | |||
buffer->dirty = true; | |||
setValue( impStep( ey ) ); | |||
} | |||
} | |||
void onMouseDown( EventMouseDown &e ) override | |||
{ | |||
ParamWidget::onMouseDown( e ); | |||
valueByMouse( e.pos.y ); | |||
dragging = true; | |||
} | |||
void onMouseUp( EventMouseUp &e ) override | |||
{ | |||
ParamWidget::onMouseUp( e ); | |||
valueByMouse( e.pos.y ); | |||
dragging = false; | |||
lastDragPos = Vec( -1, -1 ); | |||
} | |||
void onMouseMove( EventMouseMove &e ) override | |||
{ | |||
ParamWidget::onMouseMove( e ); | |||
if( dragging && ( e.pos.x != lastDragPos.x || e.pos.y != lastDragPos.y )) | |||
{ | |||
valueByMouse( e.pos.y ); | |||
lastDragPos = e.pos; | |||
} | |||
} | |||
void onMouseLeave( EventMouseLeave &e ) override | |||
{ | |||
ParamWidget::onMouseLeave( e ); | |||
dragging = false; | |||
lastDragPos = Vec( -1, -1 ); | |||
} | |||
void drawSegments( NVGcontext *vg ) | |||
{ | |||
// This is now buffered to only be called when the value has changed | |||
int w = this->box.size.x; | |||
int h = this->box.size.y; | |||
nvgBeginPath( vg ); | |||
nvgRect( vg, 0, 0, w, h ); | |||
nvgFillColor( vg, nvgRGB( 40, 40, 40 ) ); | |||
nvgFill( vg ); | |||
float dy = box.size.y / NSteps; | |||
for( int i=0; i<NSteps; ++i ) | |||
{ | |||
nvgBeginPath( vg ); | |||
nvgRect( vg, 1, i * dy + 1, w - 2, dy - 2 ); | |||
nvgFillColor( vg, cm.elementColor( NSteps - 1 - i , NSteps, getStep() ) ); | |||
nvgFill( vg ); | |||
} | |||
} | |||
}; | |||
struct GreenFromZeroColorModel | |||
{ | |||
NVGcolor GREEN, BLACK; | |||
GreenFromZeroColorModel() : GREEN( nvgRGB( 10, 255, 10 ) ), BLACK( nvgRGB( 10, 10, 10 ) ) { } | |||
NVGcolor elementColor( int stepNo, int NSteps, int value ) | |||
{ | |||
if( stepNo <= value ) | |||
return nvgRGB( 10, 155 + 1.0f * stepNo / NSteps * 100, 10 ); | |||
else | |||
return BLACK; | |||
} | |||
}; | |||
struct RedGreenFromMiddleColorModel | |||
{ | |||
NVGcolor GREEN, BLACK, RED; | |||
RedGreenFromMiddleColorModel() : GREEN( nvgRGB( 10, 255, 10 ) ), BLACK( nvgRGB( 10, 10, 10 ) ), RED( nvgRGB( 255, 10, 10 ) ) { } | |||
NVGcolor elementColor( int stepNo, int NSteps, int value ) | |||
{ | |||
// This has the 'midpoint' be 0 so we want to compare with NSteps/2 | |||
if( value < NSteps / 2 ) { | |||
// We are in the bottom half. | |||
if( stepNo < value || stepNo >= NSteps / 2) return BLACK; | |||
else | |||
{ | |||
int distance = NSteps / 2 - stepNo; | |||
return nvgRGB( 155 + 1.0f * distance / ( NSteps / 2 ) * 100 , 10, 10 ); | |||
} | |||
} else { | |||
if( stepNo > value || stepNo < NSteps / 2) return BLACK; | |||
else | |||
{ | |||
int distance = stepNo - NSteps / 2; | |||
return nvgRGB( 10, 155 + 1.0f * distance / ( NSteps / 2 ) * 100 , 10 ); | |||
} | |||
} | |||
} | |||
}; | |||
#include <iostream> | |||
// Think hard about dirty state management ... later | |||
// Pixel Sizing | |||
// Share fontdata | |||
struct DotMatrixLightTextWidget : public Component // Thanks http://scruss.com/blog/tag/font/ | |||
{ | |||
typedef std::function< std::string( Module * )> stringGetter; | |||
typedef std::function< bool( Module * )> stringDirtyGetter; | |||
BufferedDrawFunctionWidget<DotMatrixLightTextWidget> *buffer; | |||
int charCount; | |||
std::string currentText; | |||
typedef std::map< char, std::vector< bool > > fontData_t; | |||
fontData_t fontData; | |||
float ledSize, padSize; | |||
DotMatrixLightTextWidget() : Component(), buffer( NULL ), currentText( "" ) | |||
{ | |||
} | |||
void setup() | |||
{ | |||
ledSize = 2; | |||
padSize = 1; | |||
box.size = Vec( charCount * ( 5 * ledSize + padSize ) + 2 * padSize, 7 * ledSize + 4.5 * padSize ); // 5 x 7 data structure | |||
buffer = new BufferedDrawFunctionWidget< DotMatrixLightTextWidget >( Vec( 0, 0 ), this->box.size, this, | |||
&DotMatrixLightTextWidget::drawText ); | |||
info( "BaconMusic loading DMP json: %s", assetPlugin( plugin, "res/Keypunch029.json" ).c_str() ); | |||
json_t *json; | |||
json_error_t error; | |||
json = json_load_file(assetPlugin( plugin, "res/Keypunch029.json" ).c_str(), 0, &error); | |||
if(!json) { | |||
info( "JSON FILE not loaded\n" ); | |||
} | |||
const char* key; | |||
json_t *value; | |||
json_object_foreach( json, key, value ) { | |||
fontData_t::mapped_type valmap; | |||
size_t index; | |||
json_t *aval; | |||
json_array_foreach( value, index, aval ) { | |||
std::string s( json_string_value( aval ) ); | |||
for( const char* c = s.c_str(); *c != 0; ++c ) { | |||
valmap.push_back( *c == '#' ); | |||
} | |||
} | |||
fontData[ key[ 0 ] ] = valmap; | |||
} | |||
} | |||
// create takes a function | |||
static DotMatrixLightTextWidget *create( Vec pos, Module *module, int charCount, stringDirtyGetter dgf, stringGetter gf ) | |||
{ | |||
DotMatrixLightTextWidget *r = Component::create<DotMatrixLightTextWidget>( pos, module ); | |||
r->getfn = gf; | |||
r->dirtyfn = dgf; | |||
r->charCount = charCount; | |||
r->setup(); | |||
return r; | |||
} | |||
stringDirtyGetter dirtyfn; | |||
stringGetter getfn; | |||
void draw( NVGcontext *vg ) override | |||
{ | |||
if( dirtyfn( this->module ) ) | |||
{ | |||
currentText = getfn( this->module ); | |||
buffer->dirty = true; | |||
} | |||
if( buffer ) | |||
buffer->draw( vg ); | |||
} | |||
void drawChar( NVGcontext *vg, Vec pos, char c ) | |||
{ | |||
#define UPPERCASE(a) ((char)( ((a)>='a'&&(a)<='z') ? ((a)&~32) : (a) )) | |||
// fontData_t::iterator k = fontData.find( (cstd::toupper( c ) ); | |||
fontData_t::iterator k = fontData.find( UPPERCASE(c) ); | |||
if( k != fontData.end() ) { | |||
fontData_t::mapped_type blist = k->second; | |||
int row=0, col=0; | |||
for( auto v = blist.begin(); v != blist.end(); ++v ) | |||
{ | |||
if( *v ) | |||
{ | |||
float xo = (col+0.5) * ledSize + pos.x; | |||
float yo = (row+0.5) * ledSize + pos.y; | |||
nvgBeginPath( vg ); | |||
// nvgRect( vg, xo, yo, ledSize, ledSize ); | |||
nvgCircle( vg, xo + ledSize/2.0f, yo + ledSize/2.0f, ledSize/2.0f * 1.1 ); | |||
nvgFillColor( vg, nvgRGBA( 25, 35, 25, 255 ) ); | |||
nvgFill( vg ); | |||
nvgBeginPath( vg ); | |||
// nvgRect( vg, xo, yo, ledSize, ledSize ); | |||
nvgCircle( vg, xo + ledSize/2.0f, yo + ledSize/2.0f, ledSize/2.0f ); | |||
nvgFillColor( vg, COLOR_BLUE ); // Thanks for having such a nice blue, Rack!! | |||
nvgFill( vg ); | |||
} | |||
col++; | |||
if( col == 5 ) { | |||
col = 0; | |||
row ++; | |||
} | |||
} | |||
} | |||
else { | |||
} | |||
} | |||
void drawText( NVGcontext *vg ) | |||
{ | |||
nvgBeginPath( vg ); | |||
nvgRect( vg, 0, 0, box.size.x, box.size.y ); | |||
nvgFillColor( vg, nvgRGBA( 15, 15, 55, 255 ) ); | |||
nvgFill( vg ); | |||
Vec cpos = Vec( padSize, padSize ); | |||
for( const char* c = currentText.c_str(); *c != 0; ++c ) { | |||
drawChar( vg, cpos, *c ); | |||
cpos.x += ledSize * 5 + padSize; | |||
} | |||
} | |||
void onZoom( EventZoom &e ) override | |||
{ | |||
buffer->dirty = true; | |||
} | |||
}; | |||
#include "SizeTable.hpp" | |||
#endif |
@@ -0,0 +1,58 @@ | |||
#include "BaconPlugs.hpp" | |||
#include "Glissinator.hpp" | |||
namespace rack_plugin_BaconMusic { | |||
struct GlissinatorWidget : ModuleWidget { | |||
typedef Glissinator< Module > G; | |||
GlissinatorWidget( Glissinator<Module> *model ); | |||
}; | |||
GlissinatorWidget::GlissinatorWidget( Glissinator<Module> *model ) : ModuleWidget( model ) | |||
{ | |||
box.size = Vec( SCREW_WIDTH * 5, RACK_HEIGHT ); | |||
BaconBackground *bg = new BaconBackground( box.size, "Glissinator" ); | |||
addChild( bg->wrappedInFramebuffer() ); | |||
// FIXME - spacing | |||
// addChild( new BaconHelpButton( "README.md#glissinator" ) ); | |||
ParamWidget *slider = ParamWidget::create< GraduatedFader< 230 > >( Vec( bg->cx( 29 ), 23 ), | |||
module, | |||
G::GLISS_TIME, | |||
0, | |||
1, | |||
0.1 ); | |||
addParam( slider ); | |||
Vec inP = Vec( 7, RACK_HEIGHT - 15 - 43 ); | |||
Vec outP = Vec( box.size.x - 24 - 7, RACK_HEIGHT - 15 - 43 ); | |||
bg->addPlugLabel( inP, BaconBackground::SIG_IN, "in" ); | |||
addInput( Port::create< PJ301MPort >( inP, Port::INPUT, | |||
module, | |||
G::SOURCE_INPUT ) ); | |||
bg->addPlugLabel( outP, BaconBackground::SIG_OUT, "out" ); | |||
addOutput( Port::create< PJ301MPort >( outP, Port::OUTPUT, | |||
module, | |||
G::SLID_OUTPUT ) ); | |||
bg->addRoundedBorder( Vec( 5, RACK_HEIGHT - 120 ), Vec( box.size.x - 10, 38 ), BaconBackground::highlight ); | |||
bg->addLabel( Vec( 10, RACK_HEIGHT - 102 ), "gliss", 11, NVG_ALIGN_LEFT | NVG_ALIGN_BOTTOM, COLOR_WHITE ); | |||
bg->addLabel( Vec( 10, RACK_HEIGHT - 90 ), "gate", 11, NVG_ALIGN_LEFT | NVG_ALIGN_BOTTOM, COLOR_WHITE ); | |||
addChild( ModuleLightWidget::create< SmallLight< BlueLight > >( Vec( bg->cx() - 4 , RACK_HEIGHT - 120 + 38 / 2 - 3 ), | |||
module, G::SLIDING_LIGHT ) ); | |||
addOutput( Port::create< PJ301MPort >( Vec( bg->cx() + 5, RACK_HEIGHT - 114 ), Port::OUTPUT, module, G::GLISSING_GATE ) ); | |||
} | |||
} // namespace rack_plugin_BaconMusic | |||
using namespace rack_plugin_BaconMusic; | |||
RACK_PLUGIN_MODEL_INIT(BaconMusic, Glissinator) { | |||
Model *modelGlissinator = Model::create<Glissinator<Module>,GlissinatorWidget>("Bacon Music", "Glissinator", "Glissinator", EFFECT_TAG); | |||
return modelGlissinator; | |||
} |
@@ -0,0 +1,119 @@ | |||
namespace rack_plugin_BaconMusic { | |||
template <typename TBase> | |||
struct Glissinator : public TBase { | |||
enum ParamIds { | |||
GLISS_TIME, | |||
NUM_PARAMS | |||
}; | |||
enum InputIds { | |||
SOURCE_INPUT, | |||
NUM_INPUTS | |||
}; | |||
enum OutputIds { | |||
SLID_OUTPUT, | |||
GLISSING_GATE, | |||
NUM_OUTPUTS | |||
}; | |||
enum LightIds { | |||
SLIDING_LIGHT, | |||
NUM_LIGHTS | |||
}; | |||
float priorIn; | |||
float targetIn; | |||
int offsetCount; | |||
// Hey thanks https://stackoverflow.com/a/4643091 | |||
using TBase::params; | |||
using TBase::inputs; | |||
using TBase::outputs; | |||
using TBase::lights; | |||
Glissinator() : TBase( NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS ) { | |||
offsetCount = -1; | |||
params[ GLISS_TIME ].value = 0.1; | |||
} | |||
void step() override | |||
{ | |||
float glist_sec = params[ GLISS_TIME ].value; | |||
int shift_time = engineGetSampleRate() * glist_sec; | |||
if( shift_time < 10 ) shift_time = 10; | |||
float thisIn = inputs[ SOURCE_INPUT ].value; | |||
// This means I am being intialized | |||
if( offsetCount < 0 ) | |||
{ | |||
priorIn = thisIn; | |||
offsetCount = 0; | |||
} | |||
bool inGliss = offsetCount != 0; | |||
float thisOut = thisIn; | |||
// When I begin the cycle, the shift_time may be a different shift_time than the | |||
// prior cycle. This is not a problem unless the shift time is now shorter than | |||
// the offset_time. If that's the case we have basically finished the gliss. | |||
// This check used to be at the end of the loop but that lead to one bad value | |||
// even with the >= | |||
if( offsetCount >= shift_time ) | |||
{ | |||
offsetCount = 0; | |||
priorIn = thisIn; | |||
targetIn = thisIn; | |||
inGliss = false; | |||
} | |||
// I am not glissing | |||
if( ! inGliss ) | |||
{ | |||
// But I have a new target, so start glissing by setting offset count to 1. | |||
if( thisIn != priorIn ) | |||
{ | |||
targetIn = thisIn; | |||
offsetCount = 1; | |||
inGliss = true; | |||
} | |||
} | |||
// I am glissing (note this is NOT in an else since inGliss can be reset above) | |||
if( inGliss ) | |||
{ | |||
// OK this means my note has changed underneath me so I have to simulate my | |||
// starting point. | |||
if( thisIn != targetIn ) | |||
{ | |||
// This "-1" is here because we want to know the LAST known step - so at the prior | |||
// offset count. Without this a turnaround will tick above the turnaround point for one | |||
// sample. | |||
float lastKnown = ( ( shift_time - (offsetCount-1) ) * priorIn + | |||
(offsetCount-1) * targetIn) / shift_time; | |||
targetIn = thisIn; | |||
priorIn = lastKnown; | |||
offsetCount = 0; | |||
} | |||
// Then the output is just the weighted sum of the prior input and this input. | |||
thisOut = ( ( shift_time - offsetCount ) * priorIn + | |||
offsetCount * thisIn ) / shift_time; | |||
// and step along one. | |||
offsetCount ++; | |||
} | |||
lights[ SLIDING_LIGHT ].value = inGliss ? 1 : 0; | |||
outputs[ SLID_OUTPUT ].value = thisOut; | |||
outputs[ GLISSING_GATE ].value = inGliss ? 10 : 0; | |||
} | |||
}; | |||
} // namespace rack_plugin_BaconMusic |
@@ -0,0 +1,87 @@ | |||
#include "rack.hpp" | |||
#include "BufferedDrawFunction.hpp" | |||
template <int H> | |||
struct GraduatedFader : SVGFader | |||
{ | |||
int slider_height = 41; | |||
int slider_width = 20; | |||
int widget_width = 28; | |||
FramebufferWidget *notches; | |||
GraduatedFader() | |||
{ | |||
background->svg = NULL; | |||
background->wrap(); | |||
background->box.pos = Vec( 0, 0 ); | |||
handle->svg = SVG::load( assetPlugin( plugin, "res/BaconSliderHandle.svg" ) ); | |||
handle->wrap(); | |||
maxHandlePos = Vec( (widget_width-slider_width)/2, 0 ); | |||
minHandlePos = Vec( (widget_width-slider_width)/2, (H-slider_height) ); | |||
box.size = Vec( widget_width, H ); | |||
notches = new BufferedDrawFunctionWidget<GraduatedFader<H>>( Vec( 0, 0 ), box.size, this, | |||
&GraduatedFader<H>::drawBackground ); | |||
} | |||
void draw( NVGcontext *vg ) override | |||
{ | |||
notches->draw( vg ); | |||
SVGFader::draw( vg ); | |||
} | |||
void drawBackground( NVGcontext *vg ) | |||
{ | |||
int nStrokes = 10; | |||
int slideTop = slider_height / 2; | |||
int slideHeight = H - slider_height; | |||
int slideBump = 5; | |||
int slotWidth = 1; | |||
#ifdef DEBUG_NOTCHES | |||
nvgBeginPath( vg ); | |||
nvgRect( vg, 0, 0, widget_width, H ); | |||
nvgFillColor( vg, COLOR_RED ); | |||
nvgFill( vg ); | |||
#endif | |||
float dx = (1.0 * slideHeight) / nStrokes; | |||
// Firest the gray highlights | |||
for( int i=0; i<= nStrokes; ++i ) | |||
{ | |||
nvgBeginPath( vg ); | |||
nvgRect( vg, 1, slideTop + dx * i, widget_width-2, 1 ); | |||
nvgFillColor( vg, nvgRGBA( 200, 200, 200, 255 ) ); | |||
nvgFill( vg ); | |||
} | |||
// and now the black notches | |||
for( int i=0; i<= nStrokes; ++i ) | |||
{ | |||
nvgBeginPath( vg ); | |||
nvgRect( vg, 1, slideTop + dx * i-1, widget_width-2, 1.5 ); | |||
nvgFillColor( vg, nvgRGBA( 100, 100, 100, 255 )); | |||
nvgFill( vg ); | |||
} | |||
// OK so now we want to draw the vertical line | |||
nvgBeginPath( vg ); | |||
nvgRect( vg, widget_width/2 - slotWidth, slideTop - slideBump, 2 * slotWidth + 1, slideHeight + 2 * slideBump ); | |||
nvgFillColor( vg, COLOR_BLACK ); | |||
nvgFill( vg ); | |||
} | |||
void onZoom( EventZoom &e ) override | |||
{ | |||
// Need this because I don't add it as a child, since the base class does something funky with that | |||
notches->onZoom( e ); | |||
SVGFader::onZoom( e ); | |||
} | |||
}; | |||
@@ -0,0 +1,217 @@ | |||
#include "BaconPlugs.hpp" | |||
namespace rack_plugin_BaconMusic { | |||
struct HarMoNee : Module { | |||
enum ParamIds { | |||
UP_OR_DOWN, | |||
HALF_STEP, | |||
WHOLE_STEP, | |||
MINOR_THIRD, | |||
MAJOR_THIRD, | |||
FIFTH, | |||
OCTAVE, | |||
NUM_PARAMS | |||
}; | |||
enum InputIds { | |||
SOURCE_INPUT, | |||
NUM_INPUTS | |||
}; | |||
enum OutputIds { | |||
ECHO_OUTPUT, | |||
INCREASED_OUTPUT, | |||
NUM_OUTPUTS | |||
}; | |||
enum LightIds { | |||
UP_LIGHT, | |||
DOWN_LIGHT, | |||
HALF_STEP_LIGHT, | |||
WHOLE_STEP_LIGHT, | |||
MINOR_THIRD_LIGHT, | |||
MAJOR_THIRD_LIGHT, | |||
FIFTH_LIGHT, | |||
OCTAVE_LIGHT, | |||
DIGIT_LIGHT, | |||
NUM_LIGHTS | |||
}; | |||
std::vector< float > offsets; | |||
float priorOffset; | |||
float targetOffset; | |||
int offsetCount; | |||
HarMoNee() : Module( NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS ) { | |||
for( int i=0; i<OCTAVE; ++i ) offsets.push_back( 0 ); | |||
offsets[ HALF_STEP ] = 1; | |||
offsets[ WHOLE_STEP ] = 2; | |||
offsets[ MINOR_THIRD ] = 3; | |||
offsets[ MAJOR_THIRD ] = 4; | |||
offsets[ FIFTH ] = 7; | |||
offsets[ OCTAVE ] = 12; | |||
priorOffset = 0; | |||
targetOffset = 0; | |||
offsetCount = 0; | |||
} | |||
void step() override; | |||
}; | |||
void HarMoNee::step() { | |||
/* TODO | |||
Display the shift | |||
Tests | |||
*/ | |||
float in = inputs[ SOURCE_INPUT ].value; | |||
float echo = in; | |||
float offsetI = 0; | |||
float uod = ( params[ UP_OR_DOWN ].value > 0 ) ? 1.0 : -1.0; | |||
if( uod > 0 ) | |||
{ | |||
lights[ UP_LIGHT ].value = 1; lights[ DOWN_LIGHT ].value = 0; | |||
} | |||
else | |||
{ | |||
lights[ DOWN_LIGHT ].value = 1; lights[ UP_LIGHT ].value = 0; | |||
} | |||
int ld = HALF_STEP_LIGHT - HALF_STEP; | |||
for( int i=HALF_STEP; i <= OCTAVE; ++i ) | |||
{ | |||
if( params[ i ].value > 0 ) | |||
{ | |||
lights[ i + ld ].value = 1.0; | |||
offsetI += offsets[ i ]; | |||
} | |||
else | |||
{ | |||
lights[ i + ld ].value = 0.0; | |||
} | |||
} | |||
lights[ DIGIT_LIGHT ].value = offsetI; | |||
offsetI = uod * offsetI / 12.0; | |||
int shift_time = 44000 / 5; | |||
/* Glissando state management | |||
- priorOffset is the place we are starting the glide from | |||
- targetOffset is where we are headed | |||
- offsetI is where the switches are set | |||
- offsetCount is how far we are in. | |||
when we aren't in a glissando offsetCount will be 0 and | |||
all three will be the same. offsetCount being | |||
non-zero is the same as in-gliss. | |||
*/ | |||
bool inGliss = offsetCount != 0; | |||
if( ! inGliss ) | |||
{ | |||
// We are not sliding. Should we be? | |||
if( offsetI != priorOffset ) | |||
{ | |||
targetOffset = offsetI; | |||
offsetCount = 1; | |||
inGliss = true; | |||
} | |||
} | |||
if( inGliss ) | |||
{ | |||
// If the target == the offset we haven't changed anything so | |||
// just march along linear time | |||
if( offsetI != targetOffset ) | |||
{ | |||
float lastKnown = ( ( shift_time - offsetCount ) * priorOffset + | |||
offsetCount * targetOffset ) / shift_time; | |||
targetOffset = offsetI; | |||
priorOffset = lastKnown; | |||
offsetCount = 0; | |||
} | |||
offsetI = ( ( shift_time - offsetCount ) * priorOffset + | |||
offsetCount * offsetI ) / shift_time; | |||
offsetCount ++; | |||
} | |||
// Finally if we are done, reset it all to zero | |||
if( offsetCount == shift_time ) | |||
{ | |||
offsetCount = 0; | |||
priorOffset = offsetI; | |||
targetOffset = offsetI; | |||
} | |||
float increased = in + offsetI; | |||
outputs[ ECHO_OUTPUT ].value = echo; | |||
outputs[ INCREASED_OUTPUT ].value = increased; | |||
} | |||
struct HarMoNeeWidget : ModuleWidget { | |||
HarMoNeeWidget(HarMoNee *model); | |||
}; | |||
HarMoNeeWidget::HarMoNeeWidget( HarMoNee *model ) : ModuleWidget( model ) | |||
{ | |||
box.size = Vec( SCREW_WIDTH*8 , RACK_HEIGHT ); | |||
BaconBackground *bg = new BaconBackground( box.size, "HarMoNee" ); | |||
addChild( bg->wrappedInFramebuffer() ); | |||
Vec iPos( 12, 100 ); | |||
bg->addPlugLabel( iPos, BaconBackground::SIG_IN, "in" ); | |||
addInput( Port::create< PJ301MPort >( iPos, Port::INPUT, module, HarMoNee::SOURCE_INPUT ) ); | |||
iPos.y += 60; | |||
bg->addPlugLabel( iPos, BaconBackground::SIG_OUT, "root" ); | |||
addOutput( Port::create<PJ301MPort>(iPos, Port::OUTPUT, module, HarMoNee::ECHO_OUTPUT ) ); | |||
iPos.y += 60; | |||
bg->addPlugLabel( iPos, BaconBackground::SIG_OUT, "harm" ); | |||
addOutput( Port::create<PJ301MPort>(iPos, Port::OUTPUT, module, HarMoNee::INCREASED_OUTPUT ) ); | |||
// NKK is 32 x 44 | |||
addParam( ParamWidget::create< NKK >( Vec( 80, 26 ), module, HarMoNee::UP_OR_DOWN, 0, 1, 1 ) ); | |||
bg->addLabel( Vec( 74, 26+22-4-5-5 ), "up", 12, NVG_ALIGN_CENTER | NVG_ALIGN_BOTTOM ); | |||
addChild( ModuleLightWidget::create< MediumLight< GreenLight >>( Vec( 70, 26 + 22 - 4 - 5 ), module, HarMoNee::UP_LIGHT ) ); | |||
bg->addLabel( Vec( 74, 26+22-4+5+8+7 ), "dn", 12, NVG_ALIGN_CENTER | NVG_ALIGN_TOP ); | |||
addChild( ModuleLightWidget::create< MediumLight< RedLight >>( Vec( 70, 26 + 22 - 4 + 5 ), module, HarMoNee::DOWN_LIGHT ) ); | |||
addChild( MultiDigitSevenSegmentLight< BlueLight, 4, 2 >::create( Vec( 10, 30 ), | |||
module, | |||
HarMoNee::DIGIT_LIGHT ) ); | |||
int x = 80; int y = 26 + 45; float v = -1; | |||
int ld = HarMoNee::HALF_STEP_LIGHT - HarMoNee::HALF_STEP; | |||
const char* labels[] = { "1/2", "W", "m3", "III", "V", "O" }; | |||
for( int i = HarMoNee::HALF_STEP; i <= HarMoNee::OCTAVE; ++i ) | |||
{ | |||
if( i == HarMoNee::OCTAVE ) { v = 1; } { v = -1; } | |||
addParam( ParamWidget::create<NKK>( Vec( x, y ), module, i, 0, 1, v ) ); | |||
bg->addLabel( Vec( 66, y+22 ), labels[ i - HarMoNee::HALF_STEP ], | |||
14, NVG_ALIGN_RIGHT | NVG_ALIGN_MIDDLE ); | |||
addChild( ModuleLightWidget::create< MediumLight< BlueLight > >( Vec( 70, y + 22 - 5 ), module, i + ld ) ); | |||
y += 45; | |||
} | |||
} | |||
} // namespace rack_plugin_BaconMusic | |||
using namespace rack_plugin_BaconMusic; | |||
RACK_PLUGIN_MODEL_INIT(BaconMusic, HarMoNee) { | |||
Model *modelHarMoNee = Model::create<HarMoNee,HarMoNeeWidget>("Bacon Music", "HarMoNee", "HarMoNee", TUNER_TAG); | |||
return modelHarMoNee; | |||
} |
@@ -0,0 +1,217 @@ | |||
// An implementation of the KSSYnth class in the ipython notebook in ../pynb/KarplusStrong.ipynb | |||
#include <string> | |||
#include <vector> | |||
#include <math.h> | |||
#include <cstdlib> | |||
class KSSynth { | |||
public: | |||
typedef enum InitPacket { | |||
RANDOM, | |||
SQUARE, | |||
SAW, | |||
NOISYSAW, | |||
SIN, | |||
SINCHIRP // if you add remember to fix the count below... | |||
} InitPacket; | |||
std::string initPacketName( InitPacket p ) | |||
{ | |||
switch(p) | |||
{ | |||
case RANDOM: return "random"; | |||
case SQUARE: return "square"; | |||
case SAW: return "saw"; | |||
case NOISYSAW: return "noisysaw"; | |||
case SIN: return "sin"; | |||
case SINCHIRP: return "sinchirp"; | |||
} | |||
return ""; | |||
} | |||
int numInitPackets() | |||
{ | |||
return (int)SINCHIRP + 1; | |||
} | |||
typedef enum FilterType { | |||
WEIGHTED_ONE_SAMPLE | |||
} FilterType; | |||
std::string filterTypeName( FilterType p ) | |||
{ | |||
switch(p) | |||
{ | |||
case WEIGHTED_ONE_SAMPLE: return "wgt 1samp"; | |||
} | |||
return ""; | |||
} | |||
int numFilterTypes() | |||
{ | |||
return WEIGHTED_ONE_SAMPLE + 1; | |||
} | |||
public: | |||
// here's my interior state | |||
InitPacket packet; | |||
FilterType filter; | |||
float filtParamA, filtParamB, filtParamC, filtAtten; | |||
float sumDelaySquared; // This is updated about 100 times a second, not per sample | |||
bool active; | |||
//private: | |||
float freq; | |||
int burstLen; | |||
float wfMin, wfMax; | |||
int sampleRate, sampleRateBy100; | |||
float filtAttenScaled; | |||
long pos; | |||
std::vector< float > delay; | |||
public: | |||
KSSynth( float minv, float maxv, int sampleRateIn ) | |||
: | |||
packet( RANDOM ), | |||
filter( WEIGHTED_ONE_SAMPLE ), | |||
filtParamA( 0.5f ), | |||
filtParamB( 0.0f ), | |||
filtParamC( 0.0f ), | |||
filtAtten( 3.0f ), | |||
active( false ), | |||
wfMin( minv ), wfMax( maxv ), | |||
sampleRate( sampleRateIn ), | |||
sampleRateBy100( (int)( sampleRate / 100 ) ), | |||
pos( 0 ) | |||
{ | |||
setFreq( 220 ); | |||
} | |||
void setFreq( float f ) | |||
{ | |||
freq = f; | |||
burstLen = (int)( ( 1.0f * sampleRate / freq + 0.5 ) * 2 ); | |||
filtAttenScaled = filtAtten / 100 / ( freq / 440 ); | |||
delay.resize( burstLen ); | |||
} | |||
void trigger( float f ) | |||
{ | |||
// remember: Interally we work on the range [-1.0f, 1.0f] to match the python (but different from | |||
// the ChipSym which works on [ 0 1.0f ] | |||
active = true; | |||
setFreq( f ); | |||
pos = 1; | |||
switch( packet ) | |||
{ | |||
case RANDOM: | |||
{ | |||
for( int i=0; i<burstLen; ++i ) | |||
{ | |||
delay[ i ] = (float)rand() * 1.0 / RAND_MAX; | |||
delay[ i ] = delay[ i ] * 2.0 - 1.0; | |||
} | |||
break; | |||
} | |||
case SQUARE: | |||
{ | |||
int xo = (int)burstLen / 2.0; | |||
for( int i=0; i<burstLen; ++i ) | |||
{ | |||
delay[ i ] = ( i <= xo ? 1.0 : -1.0 ); | |||
} | |||
break; | |||
} | |||
case SAW: | |||
{ | |||
for( int i=0; i<burstLen; ++i ) | |||
{ | |||
delay[ i ] = ( i * 2.0f / burstLen ) - 1.0; | |||
} | |||
break; | |||
} | |||
case NOISYSAW: | |||
{ | |||
for( int i=0; i<burstLen; ++i ) | |||
{ | |||
delay[ i ] = ( i * 1.0f / burstLen ) - 0.5; | |||
delay[ i ] += (float)rand() * 1.0f / RAND_MAX - 0.5; | |||
} | |||
break; | |||
} | |||
case SIN: | |||
{ | |||
float scale = 2.0 * M_PI / burstLen; | |||
for( int i=0; i<burstLen; ++i ) | |||
{ | |||
delay[ i ] = sin( i * scale ); | |||
} | |||
break; | |||
} | |||
case SINCHIRP: | |||
{ | |||
for( int i=0; i<burstLen; ++i ) | |||
{ | |||
float ls = 1.0f * i / burstLen; | |||
float lse = exp( ls * 2 ) * 3; | |||
delay[ i ] = sin( lse * 2 * M_PI ); | |||
} | |||
break; | |||
} | |||
} | |||
updateSumDelaySquared(); | |||
} | |||
void updateSumDelaySquared() | |||
{ | |||
sumDelaySquared = 0; | |||
for( int i=0; i<burstLen; ++i ) | |||
{ | |||
sumDelaySquared += delay[ i ] * delay[ i ]; | |||
} | |||
sumDelaySquared /= burstLen; | |||
} | |||
float step() | |||
{ | |||
if( ! active ) return 0; | |||
int dpos = pos % burstLen; | |||
int dpnext = ( pos + 1 ) % burstLen; | |||
int dpfill = ( pos - 1 ) % burstLen; | |||
if( pos % sampleRateBy100 == 0 ) | |||
{ | |||
updateSumDelaySquared(); | |||
if( sumDelaySquared < 1e-7 ) // think about this threshold some | |||
active = false; | |||
} | |||
pos++; | |||
float filtval = 0.5; | |||
switch( filter ) | |||
{ | |||
case WEIGHTED_ONE_SAMPLE: | |||
float ftw = filtParamA; | |||
float fta = filtAttenScaled; | |||
filtval = ( ftw * delay[ dpos ] + ( 1.0 - ftw ) * delay[ dpnext ] ) * ( 1.0 - fta ); | |||
break; | |||
} | |||
delay[ dpfill ] = filtval; | |||
return ( ( filtval + 1.0 ) * 0.5 ) * ( wfMax - wfMin ) + wfMin; | |||
} | |||
}; |
@@ -0,0 +1,411 @@ | |||
#include "BaconPlugs.hpp" | |||
#include <sstream> | |||
#include <vector> | |||
#include <string> | |||
#include "dsp/digital.hpp" | |||
#include "KSSynth.hpp" | |||
namespace rack_plugin_BaconMusic { | |||
struct KarplusStrongPoly : virtual Module { | |||
enum ParamIds { | |||
INITIAL_PACKET, | |||
FILTER_TYPE, | |||
FREQ_KNOB, | |||
ATTEN_KNOB, | |||
FILTER_KNOB_A, | |||
FILTER_KNOB_B, | |||
FILTER_KNOB_C, | |||
NUM_PARAMS | |||
}; | |||
enum InputIds { | |||
TRIGGER_GATE, | |||
INITIAL_PACKET_INPUT, | |||
FILTER_INPUT, | |||
FREQ_CV, | |||
ATTEN_CV, | |||
FILTER_CV_A, | |||
FILTER_CV_B, | |||
FILTER_CV_C, | |||
NUM_INPUTS | |||
}; | |||
enum OutputIds { | |||
SYNTH_OUTPUT, | |||
NUM_OUTPUTS | |||
}; | |||
enum LightIds { | |||
LIGHT_PACKET_KNOB, | |||
LIGHT_PACKET_CV, | |||
LIGHT_FILTER_KNOB, | |||
LIGHT_FILTER_CV, | |||
LIGHT_FILTER_A, | |||
LIGHT_FILTER_B, | |||
LIGHT_FILTER_C, | |||
NUM_LIGHTS | |||
}; | |||
SchmittTrigger voiceTrigger; | |||
std::vector< KSSynth *> voices; | |||
const static int nVoices = 32; | |||
KarplusStrongPoly() : Module( NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS ) | |||
{ | |||
for( int i=0; i<nVoices; ++i ) voices.push_back( new KSSynth(-2.0f, 2.0f, engineGetSampleRate() ) ); | |||
initPacketStringDirty = true; | |||
currentInitialPacket = KSSynth::RANDOM; | |||
initPacketString = voices[ 0 ]->initPacketName( currentInitialPacket ); | |||
filterStringDirty = true; | |||
currentFilter = KSSynth::WEIGHTED_ONE_SAMPLE; | |||
filterString = voices[ 0 ]->filterTypeName( currentFilter ); | |||
} | |||
virtual ~KarplusStrongPoly() | |||
{ | |||
for( auto syn : voices ) | |||
delete syn; | |||
} | |||
int getNumPackets() { return voices[ 0 ]->numInitPackets(); } | |||
KSSynth::InitPacket currentInitialPacket; | |||
int getNumFilters() { return voices[ 0 ]->numFilterTypes(); } | |||
KSSynth::FilterType currentFilter; | |||
void step() override | |||
{ | |||
int nextInitialPacket = currentInitialPacket; | |||
float ipi, ipiS; | |||
lights[ LIGHT_PACKET_CV ].value = inputs[INITIAL_PACKET_INPUT].active; | |||
lights[ LIGHT_PACKET_KNOB ].value = !inputs[INITIAL_PACKET_INPUT].active; | |||
lights[ LIGHT_FILTER_CV ].value = inputs[FILTER_INPUT].active; | |||
lights[ LIGHT_FILTER_KNOB ].value = !inputs[FILTER_INPUT].active; | |||
// For now, since we only have one filter, hardcode this | |||
lights[ LIGHT_FILTER_A ].value = 1; | |||
lights[ LIGHT_FILTER_B ].value = 0; | |||
lights[ LIGHT_FILTER_C ].value = 0; | |||
if( inputs[ INITIAL_PACKET_INPUT ].active ) | |||
{ | |||
ipi = inputs[ INITIAL_PACKET_INPUT ].value; | |||
ipiS = ipi * getNumPackets() / 10.0; | |||
nextInitialPacket = (int)(ipiS); | |||
} | |||
else | |||
{ | |||
nextInitialPacket = (int)( params[ INITIAL_PACKET ].value ); | |||
} | |||
if( nextInitialPacket != currentInitialPacket ) | |||
{ | |||
initPacketStringDirty = true; | |||
currentInitialPacket = (KSSynth::InitPacket)( nextInitialPacket ); | |||
initPacketString = voices[ 0 ]->initPacketName( currentInitialPacket ); | |||
} | |||
// Check a trigger here and find a voice | |||
bool newVoice = false; | |||
if( voiceTrigger.process( inputs[ TRIGGER_GATE ].value ) ) | |||
{ | |||
newVoice = true; | |||
} | |||
if( newVoice ) | |||
{ | |||
// find voice | |||
KSSynth *voice = NULL; | |||
for( auto syn: voices ) | |||
if( ! syn->active ) | |||
{ | |||
voice = syn; | |||
break; | |||
} | |||
if( voice == NULL ) | |||
{ | |||
// info( "All voices are active: Running voice steal" ); | |||
voice = voices[ 0 ]; | |||
float ds = voice->sumDelaySquared; | |||
for( auto syn: voices ) | |||
{ | |||
if( syn->sumDelaySquared < ds ) | |||
{ | |||
ds = syn->sumDelaySquared; | |||
voice = syn; | |||
} | |||
} | |||
} | |||
// Capture parameters onto this voice and trigger it | |||
float pitch = params[ FREQ_KNOB ].value + 12.0f * inputs[ FREQ_CV ].value; | |||
float freq = 261.262f * powf( 2.0f, pitch / 12.0f ); | |||
// For now, since we only have one filter, hardcode this | |||
voice->filtParamA = clamp( params[ FILTER_KNOB_A ].value + inputs[ FILTER_CV_A ].value * 0.1, 0.0f, 1.0f ); | |||
voice->filtParamB = 0; | |||
voice->filtParamC = 0; | |||
float atten = params[ ATTEN_KNOB ].value + inputs[ ATTEN_CV ].value; | |||
voice->packet = currentInitialPacket; | |||
voice->filtAtten = atten; | |||
voice->trigger( freq ); | |||
} | |||
float out = 0.0f; | |||
for( auto syn : voices ) | |||
if( syn->active ) | |||
out += syn->step(); | |||
outputs[ SYNTH_OUTPUT ].value = out; | |||
} | |||
bool initPacketStringDirty; | |||
std::string initPacketString; | |||
static bool getInitialPacketStringDirty( Module *that ) | |||
{ | |||
return dynamic_cast<KarplusStrongPoly *>(that)->initPacketStringDirty; | |||
} | |||
static std::string getInitialPacketString( Module *that ) | |||
{ | |||
dynamic_cast<KarplusStrongPoly *>(that)->initPacketStringDirty = false; | |||
return dynamic_cast<KarplusStrongPoly *>(that)->initPacketString; | |||
} | |||
bool filterStringDirty; | |||
std::string filterString; | |||
static bool getFilterStringDirty( Module *that ) | |||
{ | |||
return dynamic_cast<KarplusStrongPoly *>(that)->filterStringDirty; | |||
} | |||
static std::string getFilterString( Module *that ) | |||
{ | |||
dynamic_cast<KarplusStrongPoly *>(that)->filterStringDirty = false; | |||
return dynamic_cast<KarplusStrongPoly *>(that)->filterString; | |||
} | |||
}; | |||
struct KarplusStrongPolyWidget : ModuleWidget { | |||
KarplusStrongPolyWidget( KarplusStrongPoly *module); | |||
}; | |||
KarplusStrongPolyWidget::KarplusStrongPolyWidget( KarplusStrongPoly *module ) : ModuleWidget( module ) | |||
{ | |||
box.size = Vec( SCREW_WIDTH * 15, RACK_HEIGHT ); | |||
BaconBackground *bg = new BaconBackground( box.size, "KarplusStrongPoly" ); | |||
addChild( bg->wrappedInFramebuffer()); | |||
float outy; | |||
float yh; | |||
int margin = 4; | |||
float gap = 13; | |||
int obuf = 10; | |||
outy = 35; | |||
float scale = 1.0; | |||
bool last = false; | |||
auto brd = [&](float ys) | |||
{ | |||
// Add a downward pointing triangle here which means I need a draw glyph | |||
if( ! last ) | |||
{ | |||
int w = 70; | |||
addChild( new BufferedDrawLambdaWidget( Vec( bg->cx() - w/2, outy + ys + margin ), | |||
Vec( w, gap ), | |||
[=](NVGcontext *vg) | |||
{ | |||
nvgBeginPath( vg ); | |||
nvgMoveTo( vg, 0, 0 ); | |||
nvgLineTo( vg, w/2, gap ); | |||
nvgLineTo( vg, w, 0 ); | |||
nvgClosePath( vg ); | |||
nvgStrokeColor( vg, COLOR_BLACK ); | |||
nvgStroke( vg ); | |||
nvgFillColor( vg, nvgRGB( 240 * scale, 240 * scale, 200 * scale ) ); | |||
nvgFill( vg ); | |||
} | |||
) | |||
); | |||
} | |||
bg->addRoundedBorder( Vec( obuf, outy - margin ), | |||
Vec( box.size.x - 2 * obuf, ys + 2 * margin ), | |||
nvgRGB( 240*scale, 240*scale, 200*scale ) ); | |||
scale *= 0.92; | |||
}; | |||
auto cl = [&](std::string lab, float ys) | |||
{ | |||
bg->addLabel( Vec( obuf + margin, outy + ys / 2 ), lab.c_str(), 13, NVG_ALIGN_MIDDLE | NVG_ALIGN_LEFT ); | |||
}; | |||
yh = SizeTable<PJ301MPort>::Y; | |||
brd( yh ); | |||
cl( "Trigger", yh ); | |||
addInput( Port::create< PJ301MPort >( Vec( box.size.x - obuf - margin - SizeTable<PJ301MPort>::X, outy ), | |||
Port::INPUT, | |||
module, | |||
KarplusStrongPoly::TRIGGER_GATE ) ); | |||
outy += yh + 2 * margin + gap; | |||
yh = SizeTable<RoundBlackKnob >::Y; | |||
brd( yh ); | |||
cl( "Freq", yh ); | |||
int xp = box.size.x - margin - obuf - SizeTable<PJ301MPort>::X; | |||
addInput( Port::create< PJ301MPort >( Vec( xp, outy + diffY2c< RoundBlackKnob, PJ301MPort >() ), | |||
Port::INPUT, | |||
module, | |||
KarplusStrongPoly::FREQ_CV ) ); | |||
xp -= SizeTable<RoundBlackKnob>::X + margin; | |||
addParam( ParamWidget::create< RoundBlackKnob >( Vec( xp, outy ), module, | |||
KarplusStrongPoly::FREQ_KNOB, | |||
-54.0f, 54.0f, 0.0f ) ); | |||
outy += yh + 2 * margin + gap; | |||
yh = SizeTable<RoundBlackSnapKnob>::Y; | |||
brd( yh ); | |||
cl( "Packet", yh ); | |||
xp = 55; | |||
addChild( ModuleLightWidget::create< SmallLight< BlueLight > >( Vec( xp - 2, outy - 2 ), | |||
module, | |||
KarplusStrongPoly::LIGHT_PACKET_KNOB ) ); | |||
addParam( ParamWidget::create< RoundBlackSnapKnob >( Vec( xp, outy ), | |||
module, | |||
KarplusStrongPoly::INITIAL_PACKET, | |||
0, | |||
module->getNumPackets()-1, 0 ) ); | |||
xp += SizeTable<RoundBlackSnapKnob>::X + margin; | |||
addChild( ModuleLightWidget::create< SmallLight< BlueLight > >( Vec( xp - 2, outy - 2 + diffY2c<RoundBlackSnapKnob,PJ301MPort>()), | |||
module, | |||
KarplusStrongPoly::LIGHT_PACKET_CV ) ); | |||
addInput( Port::create<PJ301MPort>( Vec( xp, outy + diffY2c<RoundBlackSnapKnob,PJ301MPort>() ), | |||
Port::INPUT, module, KarplusStrongPoly::INITIAL_PACKET_INPUT ) ); | |||
xp += SizeTable<PJ301MPort>::X + margin; | |||
addChild( DotMatrixLightTextWidget::create( Vec( xp, outy + diffY2c<RoundBlackSnapKnob,DotMatrixLightTextWidget>() ), | |||
module, 8, | |||
KarplusStrongPoly::getInitialPacketStringDirty, | |||
KarplusStrongPoly::getInitialPacketString ) ); | |||
outy += yh + 2 * margin + gap; | |||
yh = SizeTable<RoundBlackSnapKnob>::Y + SizeTable<RoundBlackKnob>::Y + margin; | |||
brd( yh ); | |||
cl( "Filter", SizeTable<RoundBlackKnob>::Y ); | |||
xp = 55; | |||
addChild( ModuleLightWidget::create< SmallLight< BlueLight > >( Vec( xp - 2, outy - 2 ), | |||
module, | |||
KarplusStrongPoly::LIGHT_FILTER_KNOB ) ); | |||
addParam( ParamWidget::create< RoundBlackSnapKnob >( Vec( xp, outy ), | |||
module, | |||
KarplusStrongPoly::FILTER_TYPE, | |||
0, | |||
module->getNumFilters()-1, 0 ) ); | |||
xp += SizeTable<RoundBlackSnapKnob>::X + margin; | |||
addChild( ModuleLightWidget::create< SmallLight< BlueLight > >( Vec( xp - 2, outy - 2 + diffY2c<RoundBlackSnapKnob,PJ301MPort>()), | |||
module, | |||
KarplusStrongPoly::LIGHT_FILTER_CV ) ); | |||
addInput( Port::create<PJ301MPort>( Vec( xp, outy + diffY2c<RoundBlackSnapKnob,PJ301MPort>() ), | |||
Port::INPUT, module, KarplusStrongPoly::FILTER_INPUT ) ); | |||
xp += SizeTable<PJ301MPort>::X + margin; | |||
addChild( DotMatrixLightTextWidget::create( Vec( xp, outy + diffY2c<RoundBlackSnapKnob,DotMatrixLightTextWidget>() ), | |||
module, 8, | |||
KarplusStrongPoly::getFilterStringDirty, | |||
KarplusStrongPoly::getFilterString ) ); | |||
outy += SizeTable<RoundBlackKnob>::Y + 2 * margin; | |||
xp = obuf + 2.5 * margin; | |||
for( int i=0; i<3; ++i ) | |||
{ | |||
addChild( ModuleLightWidget::create< SmallLight< BlueLight> >( Vec( xp - 2, outy - 2 ), | |||
module, | |||
KarplusStrongPoly::LIGHT_FILTER_A + i ) ); | |||
bg->addLabel( Vec( xp, outy + SizeTable<RoundSmallBlackKnob>::Y ), | |||
i == 0 ? "A" : i == 1 ? "B" : "C", | |||
12, | |||
NVG_ALIGN_BOTTOM | NVG_ALIGN_RIGHT ); | |||
xp += 3; | |||
addParam( ParamWidget::create< RoundSmallBlackKnob >( Vec( xp, outy ), | |||
module, | |||
KarplusStrongPoly::FILTER_KNOB_A + i, | |||
0, 1, 0.5 ) ); | |||
xp += SizeTable<RoundSmallBlackKnob>::X + margin; | |||
addInput( Port::create<PJ301MPort>( Vec( xp, outy + diffY2c<RoundSmallBlackKnob,PJ301MPort>() ), | |||
Port::INPUT, module, KarplusStrongPoly::FILTER_CV_A + i ) ); | |||
xp += SizeTable<PJ301MPort>::X + 3.5 * margin; | |||
} | |||
outy += yh - SizeTable<RoundBlackKnob>::Y + gap; | |||
yh = SizeTable< RoundBlackKnob >::Y; | |||
brd( yh ); | |||
cl( "Atten", yh ); | |||
xp = box.size.x - margin - obuf - SizeTable<PJ301MPort>::X; | |||
addInput( Port::create< PJ301MPort >( Vec( xp, outy + diffY2c< RoundBlackKnob, PJ301MPort >() ), | |||
Port::INPUT, | |||
module, | |||
KarplusStrongPoly::ATTEN_CV ) ); | |||
xp -= SizeTable<RoundBlackKnob>::X + margin; | |||
addParam( ParamWidget::create< RoundBlackKnob >( Vec( xp, outy ), module, | |||
KarplusStrongPoly::ATTEN_KNOB, | |||
0.1, 4, 1.95 ) ); | |||
outy += yh + 2 * margin + gap; | |||
last = true; | |||
brd( SizeTable<PJ301MPort>::Y ); | |||
cl( "Output", SizeTable<PJ301MPort>::Y ); | |||
addOutput( Port::create< PJ301MPort >( Vec( box.size.x - obuf - margin - SizeTable<PJ301MPort>::X, outy ), | |||
Port::OUTPUT, | |||
module, | |||
KarplusStrongPoly::SYNTH_OUTPUT ) ); | |||
} | |||
} // namespace rack_plugin_BaconMusic | |||
using namespace rack_plugin_BaconMusic; | |||
RACK_PLUGIN_MODEL_INIT(BaconMusic, KarplusStrongPoly) { | |||
Model *modelKarplusStrongPoly = Model::create<KarplusStrongPoly, KarplusStrongPolyWidget>("Bacon Music", "KarplusStrongPoly", "KarplusStrongPoly", OSCILLATOR_TAG ); | |||
return modelKarplusStrongPoly; | |||
} |
@@ -0,0 +1,179 @@ | |||
#include "BaconPlugs.hpp" | |||
#include <vector> | |||
#include <algorithm> | |||
#define NUM_CLOCKS 4 | |||
namespace rack_plugin_BaconMusic { | |||
struct PolyGnome : virtual Module { | |||
enum ParamIds { | |||
CLOCK_PARAM, | |||
CLOCK_NUMERATOR_1, | |||
CLOCK_DENOMINATOR_1 = CLOCK_NUMERATOR_1 + NUM_CLOCKS, | |||
NUM_PARAMS = CLOCK_DENOMINATOR_1 + NUM_CLOCKS, | |||
}; | |||
enum InputIds { | |||
CLOCK_INPUT, | |||
NUM_INPUTS, | |||
}; | |||
enum OutputIds { | |||
CLOCK_GATE_0, | |||
NUM_OUTPUTS = CLOCK_GATE_0 + NUM_CLOCKS + 1 // the "1" is for the 1/4 note clock which isn't parameterized | |||
}; | |||
enum LightIds { | |||
LIGHT_NUMERATOR_1, | |||
LIGHT_DENOMINATOR_1 = LIGHT_NUMERATOR_1 + NUM_CLOCKS, | |||
NUM_LIGHTS = LIGHT_DENOMINATOR_1 + NUM_CLOCKS | |||
}; | |||
float phase; | |||
long phase_longpart; | |||
PolyGnome() : Module( NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS ) | |||
{ | |||
phase = 0.0f; | |||
phase_longpart = 274; | |||
} | |||
inline int numi( int i ) { return (int)params[ CLOCK_NUMERATOR_1 + i ].value; } | |||
inline int deni( int i ) { return (int)params[ CLOCK_DENOMINATOR_1 + i ].value; } | |||
void step() override | |||
{ | |||
float clockTime = powf(2.0f, params[CLOCK_PARAM].value + inputs[CLOCK_INPUT].value); | |||
phase += clockTime * engineGetSampleTime(); | |||
while( phase > 1 ) | |||
{ | |||
phase = phase - 1; | |||
phase_longpart ++; | |||
} | |||
/* Alright we have to stop that longpart from getting too big otherwise it will swamp | |||
the fractional parts but we have to reset it when all the clocks are firing at once | |||
otherwise one of the clocks will stutter. So figure out the product of my fractions. | |||
Probably we should use the common prime factors so we can get an earlier step | |||
but lets leave it for now. | |||
*/ | |||
int sd = 1; | |||
int sn = 1; | |||
for( int i=0; i<NUM_CLOCKS; ++i ) | |||
{ | |||
if( outputs[ CLOCK_GATE_0 + i + 1 ].active ) | |||
{ | |||
sd *= deni( i ); | |||
sn *= numi( i ); | |||
} | |||
} | |||
int commonp = sd * sn; // so we know at least that the clocks will intersect at this tick. | |||
while( phase_longpart > commonp ) | |||
{ | |||
phase_longpart -= commonp; | |||
} | |||
for( int i=0; i<NUM_CLOCKS+1 ; ++i ) | |||
{ | |||
bool gateIn = false; | |||
float frac; | |||
if( i == 0 ) | |||
frac = 1; | |||
else | |||
frac = deni( i - 1 ) / ( 1.0f * numi( i - 1 ) ); | |||
// Note that we have two parts which comprise the phase number now, the float and the long. | |||
// The addition can overflow, though which is why I mod the phase_longpart with a larger number | |||
float lphase = phase * frac; | |||
double liphase = phase_longpart * frac; | |||
double ipart; | |||
// I still worry a bit this + may overflow if you let this run long enough and blow out the precision in the decimal | |||
float fractPhase = modf( lphase + liphase, &ipart ); | |||
gateIn = (fractPhase < 0.5f); | |||
outputs[ CLOCK_GATE_0 + i ].value = gateIn ? 10.0f : 0.0f; | |||
} | |||
for( int i=0; i<NUM_CLOCKS; ++i ) | |||
{ | |||
lights[ LIGHT_NUMERATOR_1 + i ].value = (int)params[ CLOCK_NUMERATOR_1 + i ].value; | |||
lights[ LIGHT_DENOMINATOR_1 + i ].value = (int)params[ CLOCK_DENOMINATOR_1 + i ].value; | |||
} | |||
} | |||
}; | |||
struct PolyGnomeWidget : ModuleWidget { | |||
PolyGnomeWidget( PolyGnome *module); | |||
}; | |||
PolyGnomeWidget::PolyGnomeWidget( PolyGnome *module ) : ModuleWidget( module ) | |||
{ | |||
box.size = Vec( SCREW_WIDTH * 14, RACK_HEIGHT ); | |||
BaconBackground *bg = new BaconBackground( box.size, "PolyGnome" ); | |||
addChild( bg->wrappedInFramebuffer()); | |||
bg->addLabel( Vec( 17, 45 ), "Clock", 13, NVG_ALIGN_LEFT | NVG_ALIGN_TOP ); | |||
addParam( ParamWidget::create< RoundSmallBlackKnob >( Vec( 55, 40 ), | |||
module, | |||
PolyGnome::CLOCK_PARAM, | |||
-2.0f, 6.0f, 2.0f ) ); | |||
addInput( Port::create< PJ301MPort >( Vec( 85, 40 ), | |||
Port::INPUT, | |||
module, | |||
PolyGnome::CLOCK_INPUT ) ); | |||
for( size_t i=0; i<= NUM_CLOCKS; ++i ) | |||
{ | |||
Vec outP = Vec( box.size.x - 45, 100 + 48 * i ); | |||
if( i == 0 ) | |||
{ | |||
bg->addLabel( Vec( 17, outP.y + 21 ), "Unit (1/1) clock", 13, NVG_ALIGN_LEFT | NVG_ALIGN_BOTTOM ); | |||
} | |||
else | |||
{ | |||
int yoff = 2; | |||
// knob light knob light | |||
addParam( ParamWidget::create< RoundSmallBlackKnob >( Vec( 17, outP.y + yoff ), | |||
module, | |||
PolyGnome::CLOCK_NUMERATOR_1 + (i-1), | |||
1, 30, 1 ) ); | |||
addChild( MultiDigitSevenSegmentLight< BlueLight, 2, 2 >::create( Vec( 48, outP.y + yoff ), | |||
module, | |||
PolyGnome::LIGHT_NUMERATOR_1 + (i-1) ) ); | |||
int mv = 47 + 20 + 14 - 16; | |||
addParam( ParamWidget::create< RoundSmallBlackKnob >( Vec( 16 + mv, outP.y + yoff ), | |||
module, | |||
PolyGnome::CLOCK_DENOMINATOR_1 + (i-1), | |||
1, 16, 1 ) ); | |||
addChild( MultiDigitSevenSegmentLight< BlueLight, 2, 2 >::create( Vec( 47 + mv, outP.y + yoff ), | |||
module, | |||
PolyGnome::LIGHT_DENOMINATOR_1 + (i-1) ) ); | |||
} | |||
addOutput( Port::create< PJ301MPort >( outP, | |||
Port::OUTPUT, | |||
module, | |||
PolyGnome::CLOCK_GATE_0 + i ) ); | |||
bg->addRoundedBorder( Vec( 12, outP.y - 4 ), Vec( box.size.x - 24, 36 ) ); | |||
} | |||
} | |||
} // namespace rack_plugin_BaconMusic | |||
using namespace rack_plugin_BaconMusic; | |||
RACK_PLUGIN_MODEL_INIT(BaconMusic, PolyGnome) { | |||
Model *modelPolyGnome = Model::create<PolyGnome, PolyGnomeWidget>("Bacon Music", "PolyGnome", "PolyGnome", CLOCK_TAG ); | |||
return modelPolyGnome; | |||
} | |||
@@ -0,0 +1,193 @@ | |||
#include "BaconPlugs.hpp" | |||
#include "dsp/digital.hpp" | |||
#define SCALE_LENGTH 12 | |||
namespace rack_plugin_BaconMusic { | |||
struct QuantEyes : virtual Module { | |||
enum ParamIds { | |||
ROOT_STEP, | |||
SCALE_PARAM, | |||
NUM_PARAMS = SCALE_PARAM + SCALE_LENGTH | |||
}; | |||
enum InputIds { | |||
CV_INPUT, | |||
CV_INPUT_2, | |||
CV_INPUT_3, | |||
NUM_INPUTS | |||
}; | |||
enum OutputIds { | |||
QUANTIZED_OUT, | |||
QUANTIZED_OUT_2, | |||
QUANTIZED_OUT_3, | |||
NUM_OUTPUTS | |||
}; | |||
enum LightIds { | |||
ROOT_LIGHT, | |||
ACTIVE_NOTE_LIGHTS, | |||
SCALE_LIGHTS = ACTIVE_NOTE_LIGHTS + 3 * SCALE_LENGTH, | |||
NUM_LIGHTS = SCALE_LIGHTS + SCALE_LENGTH | |||
}; | |||
int scaleState[ SCALE_LENGTH ]; | |||
SchmittTrigger scaleTriggers[ SCALE_LENGTH ]; | |||
QuantEyes() : Module( NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS ) | |||
{ | |||
for( int i=0; i<SCALE_LENGTH; ++i ) scaleState[ i ] = 1; | |||
} | |||
void step() override | |||
{ | |||
int root = clamp( params[ ROOT_STEP ].value, 0.0f, 12.0f ); | |||
lights[ ROOT_LIGHT ].value = root; | |||
for( int i=0; i<SCALE_LENGTH; ++i ) | |||
{ | |||
if( scaleTriggers[ i ].process( params[ SCALE_PARAM + i ].value ) ) | |||
{ | |||
scaleState[ i ] = ! scaleState[ i ]; | |||
} | |||
lights[ SCALE_LIGHTS + i ].value = scaleState[ i ]; | |||
lights[ ACTIVE_NOTE_LIGHTS + i ].value = 0; | |||
lights[ ACTIVE_NOTE_LIGHTS + i + SCALE_LENGTH ].value = 0; | |||
lights[ ACTIVE_NOTE_LIGHTS + i + SCALE_LENGTH * 2 ].value = 0; | |||
} | |||
for( int i=0; i<3; ++i ) | |||
{ | |||
if( inputs[ CV_INPUT + i ].active ) | |||
{ | |||
float in = inputs[ CV_INPUT + i ].value; | |||
double octave, note; | |||
note = modf( in, &octave ); | |||
float noteF = ( floor( note * SCALE_LENGTH ) + root ); | |||
int noteI = (int)noteF % SCALE_LENGTH; | |||
if( noteF > SCALE_LENGTH-1 ) octave += 1.0; | |||
while( scaleState[ noteI ] == 0 && noteI > 0 ) noteI --; | |||
lights[ ACTIVE_NOTE_LIGHTS + i * SCALE_LENGTH + noteI ].value = 1; | |||
float out = 1.0 * noteI / SCALE_LENGTH + octave; | |||
outputs[ QUANTIZED_OUT + i ].value = out; | |||
} | |||
} | |||
} | |||
json_t *toJson() override { | |||
json_t *rootJ = json_object(); | |||
json_t *scaleJ = json_array(); | |||
for( int i=0; i<SCALE_LENGTH; ++i ) | |||
{ | |||
json_t *noteJ = json_integer( scaleState[ i ] ); | |||
json_array_append_new( scaleJ, noteJ ); | |||
} | |||
json_object_set_new( rootJ, "scaleState", scaleJ ); | |||
return rootJ; | |||
} | |||
void fromJson( json_t *rootJ ) override { | |||
json_t* scaleJ = json_object_get( rootJ, "scaleState" ); | |||
if( scaleJ ) | |||
for( int i=0; i<SCALE_LENGTH; ++i ) | |||
{ | |||
json_t *noteJ = json_array_get( scaleJ, i ); | |||
if( noteJ ) | |||
scaleState[ i ] = json_integer_value( noteJ ); | |||
} | |||
} | |||
}; | |||
struct QuantEyesWidget : ModuleWidget { | |||
QuantEyesWidget( QuantEyes *model ); | |||
}; | |||
QuantEyesWidget::QuantEyesWidget( QuantEyes *model ) : ModuleWidget( model ) | |||
{ | |||
box.size = Vec( SCREW_WIDTH * 11, RACK_HEIGHT ); | |||
BaconBackground *bg = new BaconBackground( box.size, "QuantEyes" ); | |||
addChild( bg->wrappedInFramebuffer()); | |||
int rx = 15, ry = 30, sp = 22, slope = 8; | |||
for( int i=0; i<SCALE_LENGTH; ++i ) | |||
{ | |||
char d[ 24 ]; | |||
sprintf( d, "%d", i+1 ); | |||
if( i==0 ) d[ 0 ] = 'R'; | |||
int x0 = rx + (i + 0.5) * slope; | |||
int yp0 = (SCALE_LENGTH - i - 1) * sp; | |||
bg->addLabel( Vec( rx - 3, yp0 + ry + sp / 2), d, 12, NVG_ALIGN_RIGHT | NVG_ALIGN_MIDDLE ); | |||
addParam( ParamWidget::create< LEDButton >( Vec( x0, yp0 + ry ), module, QuantEyes::SCALE_PARAM + i, 0, 1, 0 ) ); | |||
addChild( ModuleLightWidget::create< MediumLight< BlueLight > >( Vec( x0 + 4, yp0 + ry + 4 ), module, QuantEyes::SCALE_LIGHTS + i ) ); | |||
addChild( ModuleLightWidget::create< SmallLight< GreenLight > >( Vec( x0 + 20, yp0 + ry + 6 ), module, QuantEyes::ACTIVE_NOTE_LIGHTS + i ) ); | |||
addChild( ModuleLightWidget::create< SmallLight< GreenLight > >( Vec( x0 + 28, yp0 + ry + 6 ), module, QuantEyes::ACTIVE_NOTE_LIGHTS + i + 12 ) ); | |||
addChild( ModuleLightWidget::create< SmallLight< GreenLight > >( Vec( x0 + 36, yp0 + ry + 6 ), module, QuantEyes::ACTIVE_NOTE_LIGHTS + i + 24 ) ); | |||
auto c = nvgRGBA( 225, 225, 225, 255 ); | |||
if( i == 1 || i == 3 || i == 6 || i == 8 || i == 10 ) | |||
c = nvgRGBA( 110, 110, 110, 255 ); | |||
bg->addFilledRect( Vec( rx , yp0 + ry + 7), Vec( i * slope + 39 , 4.5 ), c ); | |||
bg->addRect( Vec( rx , yp0 + ry + 7), Vec( i * slope + 39 , 4.5 ), nvgRGBA( 70, 70, 70, 255 ) ); | |||
} | |||
int xpospl = box.size.x - 24 - 9; | |||
Vec inP = Vec( xpospl - 32, RACK_HEIGHT - 60 ); | |||
Vec outP = Vec( xpospl, RACK_HEIGHT - 60 ); | |||
for( int i=0; i<3; ++i ) | |||
{ | |||
Vec off( 0, -55 * (2-i) ); | |||
char buf[ 20 ]; | |||
sprintf( buf, "in %d", i+1 ); | |||
bg->addPlugLabel( inP.plus(off), BaconBackground::SIG_IN, buf ); | |||
addInput( Port::create< PJ301MPort >( inP.plus(off), Port::INPUT, | |||
module, | |||
QuantEyes::CV_INPUT + i ) ); | |||
sprintf( buf, "out %d", i+1 ); | |||
bg->addPlugLabel( outP.plus(off), BaconBackground::SIG_OUT, buf ); | |||
addOutput( Port::create< PJ301MPort >( outP.plus(off), Port::OUTPUT, | |||
module, | |||
QuantEyes::QUANTIZED_OUT + i) ); | |||
} | |||
bg->addRoundedBorder( Vec( 10, box.size.y - 78 ), Vec ( 70, 49 ) ); | |||
bg->addLabel( Vec( 45, box.size.y - 74 ), "Root CV", 12, NVG_ALIGN_CENTER | NVG_ALIGN_TOP ); | |||
int ybot = box.size.y - 78 + 24 + 5 + 20; | |||
addParam( ParamWidget::create< RoundSmallBlackKnob >( Vec( 16, ybot - 3 - 28 ), | |||
module, | |||
QuantEyes::ROOT_STEP, | |||
0, 12, 0 ) ); | |||
addChild( MultiDigitSevenSegmentLight< BlueLight, 2, 2 >::create( Vec( 47, ybot - 5 - 24 ), | |||
module, | |||
QuantEyes::ROOT_LIGHT ) ); | |||
} | |||
} // namespace rack_plugin_BaconMusic | |||
using namespace rack_plugin_BaconMusic; | |||
RACK_PLUGIN_MODEL_INIT(BaconMusic, QuantEyes) { | |||
Model *modelQuantEyes = Model::create< QuantEyes, QuantEyesWidget > ("Bacon Music", "QuantEyes", "QuantEyes", QUANTIZER_TAG); | |||
return modelQuantEyes; | |||
} |
@@ -0,0 +1,60 @@ | |||
#include "BaconPlugs.hpp" | |||
#include "SampleDelay.hpp" | |||
namespace rack_plugin_BaconMusic { | |||
struct SampleDelayWidget : ModuleWidget { | |||
typedef SampleDelay< Module > SD; | |||
SampleDelayWidget( SD *module); | |||
}; | |||
SampleDelayWidget::SampleDelayWidget( SD *module ) : ModuleWidget( module ) | |||
{ | |||
box.size = Vec( SCREW_WIDTH * 5, RACK_HEIGHT ); | |||
BaconBackground *bg = new BaconBackground( box.size, "SampDelay" ); | |||
addChild( bg->wrappedInFramebuffer()); | |||
int outy = 30; | |||
int gap = 10; | |||
int margin = 3; | |||
// plug label is 29 x 49 | |||
Vec ppos = Vec( bg->cx( SizeTable<PJ301MPort>::X ), outy + 20 ); | |||
bg->addPlugLabel( ppos, BaconBackground::SIG_IN, "in" ); | |||
addInput( Port::create< PJ301MPort >( ppos, Port::INPUT, | |||
module, SD::SIGNAL_IN ) ); | |||
outy += 49 + gap + margin; | |||
bg->addRoundedBorder( Vec( bg->cx() - 14 * 1.5 - margin, outy - margin ), | |||
Vec( 14 * 3 + 2 * margin , 14 + SizeTable<RoundBlackSnapKnob>::Y + 2 * margin + 22 + margin + 2 * margin) ); | |||
bg->addLabel( Vec( bg->cx(), outy ), "# samples", 11, NVG_ALIGN_CENTER | NVG_ALIGN_TOP ); | |||
outy += 14; | |||
addParam( ParamWidget::create< RoundBlackSnapKnob >( Vec( bg->cx( SizeTable< RoundBlackSnapKnob >::X ), outy ), | |||
module, | |||
SD::DELAY_KNOB, | |||
1, 99, 1 ) ); | |||
outy += SizeTable<RoundBlackSnapKnob>::Y + 2 * margin; | |||
addChild( MultiDigitSevenSegmentLight<BlueLight, 2, 3>::create( Vec( bg->cx() - 14 * 1.5, outy ), | |||
module, | |||
SD::DELAY_VALUE_LIGHT ) ); | |||
outy += 22 + gap + margin; | |||
ppos = Vec( bg->cx( SizeTable<PJ301MPort>::X ), outy + 20 ); | |||
bg->addPlugLabel( ppos, BaconBackground::SIG_OUT, "out" ); | |||
addOutput( Port::create< PJ301MPort >( ppos, Port::OUTPUT, | |||
module, SD::SIGNAL_OUT ) ); | |||
} | |||
} // namespace rack_plugin_BaconMusic | |||
using namespace rack_plugin_BaconMusic; | |||
RACK_PLUGIN_MODEL_INIT(BaconMusic, SampleDelay) { | |||
Model *modelSampleDelay = Model::create<SampleDelayWidget::SD, SampleDelayWidget>("Bacon Music", "SampleDelay", "SampleDelay", DELAY_TAG ); | |||
return modelSampleDelay; | |||
} |
@@ -0,0 +1,62 @@ | |||
#include <vector> | |||
#include <algorithm> | |||
namespace rack_plugin_BaconMusic { | |||
template< typename TBase > | |||
struct SampleDelay : virtual TBase { | |||
enum ParamIds { | |||
DELAY_KNOB, | |||
NUM_PARAMS | |||
}; | |||
enum InputIds { | |||
SIGNAL_IN, | |||
NUM_INPUTS | |||
}; | |||
enum OutputIds { | |||
SIGNAL_OUT, | |||
NUM_OUTPUTS | |||
}; | |||
enum LightIds { | |||
DELAY_VALUE_LIGHT, | |||
NUM_LIGHTS | |||
}; | |||
using TBase::params; | |||
using TBase::inputs; | |||
using TBase::outputs; | |||
using TBase::lights; | |||
std::vector< float > ring; | |||
size_t ringSize; | |||
size_t pos; | |||
SampleDelay() : TBase( NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS ) | |||
{ | |||
params[ DELAY_KNOB ].value = 1; | |||
ringSize = 100; | |||
ring.resize( ringSize ); | |||
std::fill( ring.begin(), ring.end(), 0 ); | |||
pos = 0; | |||
} | |||
void step() override | |||
{ | |||
int del = params[ DELAY_KNOB ].value - 1; | |||
int dpos = ( (int)pos - del ); | |||
if( dpos < 0 ) dpos += ringSize; | |||
ring[ pos ] = inputs[ SIGNAL_IN ].value; | |||
outputs[ SIGNAL_OUT ].value = ring[ dpos ]; | |||
lights[ DELAY_VALUE_LIGHT ].value = del + 1; | |||
pos++; | |||
if( pos >= ringSize ) pos = 0; | |||
} | |||
}; | |||
} // namespace rack_plugin_BaconMusic |
@@ -0,0 +1,67 @@ | |||
#ifndef INCLUDE_SIZE_TABLE | |||
#define INCLUDE_SIZE_TABLE | |||
// Wish there was a better way to do this | |||
template< typename T > | |||
struct SizeTable | |||
{ | |||
}; | |||
template<> | |||
struct SizeTable< PJ301MPort > | |||
{ | |||
constexpr static const float X = 24.6721; | |||
constexpr static const float Y = 24.6721; | |||
}; | |||
template<> | |||
struct SizeTable< RoundHugeBlackKnob > | |||
{ | |||
constexpr static const float X = 56.1034; | |||
constexpr static const float Y = 56.1034; | |||
}; | |||
template<> | |||
struct SizeTable< RoundLargeBlackKnob > | |||
{ | |||
constexpr static const float X = 37.5; | |||
constexpr static const float Y = 37.5; | |||
}; | |||
template<> | |||
struct SizeTable< RoundBlackSnapKnob > | |||
{ | |||
constexpr static const float X = 29.5287; | |||
constexpr static const float Y = 29.5287; | |||
}; | |||
template<> | |||
struct SizeTable< RoundBlackKnob > | |||
{ | |||
constexpr static const float X = 29.5287; | |||
constexpr static const float Y = 29.5287; | |||
}; | |||
template<> | |||
struct SizeTable< DotMatrixLightTextWidget > | |||
{ | |||
constexpr static const float X = 11; | |||
constexpr static const float Y = 18.5; | |||
}; | |||
template<> | |||
struct SizeTable< RoundSmallBlackKnob > | |||
{ | |||
constexpr static const float X = 23.6206; | |||
constexpr static const float Y = 23.6206; | |||
}; | |||
// Diff Y to center | |||
template< typename T1, typename T2 > | |||
float diffY2c() | |||
{ | |||
return ( SizeTable<T1>::Y - SizeTable<T2>::Y ) / 2; | |||
} | |||
#endif |
@@ -0,0 +1,141 @@ | |||
#ifdef BUILD_SORTACHORUS | |||
#include "BaconPlugs.hpp" | |||
#include <algorithm> | |||
#include <cmath> | |||
#include <iostream> | |||
namespace rack_plugin_BaconMusic { | |||
struct SortaChorus : virtual Module { | |||
private: | |||
// Borrowing from RingBuffer which this sort of is, S must be a power of 2 | |||
template <typename T, size_t S> | |||
struct ChorusBuffer | |||
{ | |||
T data[ S ]; | |||
size_t SZ = S; | |||
size_t currp = 0; | |||
ChorusBuffer() | |||
{ | |||
std::fill( data, data + S, 0 ); | |||
} | |||
size_t mask(size_t i) const { | |||
return i & (S - 1); | |||
} | |||
void push( T t ) | |||
{ | |||
size_t i = mask( currp++ ); | |||
data[ i ] = t; | |||
} | |||
// todo interpolate since this will sound like poop | |||
T readBack( size_t off ) | |||
{ | |||
size_t pos = ( currp + ( S - 1 ) - off ) & ( S - 1 ); | |||
return data[ pos ]; | |||
} | |||
}; | |||
ChorusBuffer< float, 16384 > buf; | |||
float tNow; | |||
public: | |||
enum ParamIds { | |||
DEPTH, | |||
SPEED, | |||
SHAPE, | |||
NOISE, | |||
NUM_PARAMS | |||
}; | |||
enum InputIds { | |||
SIGNAL_INPUT, | |||
NUM_INPUTS | |||
}; | |||
enum OutputIds { | |||
CHORUSED_OUTPUT, | |||
NUM_OUTPUTS | |||
}; | |||
enum LightIds { | |||
NUM_LIGHTS | |||
}; | |||
SortaChorus() : Module( NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS ) | |||
{ | |||
tNow = 0; | |||
} | |||
void step() override | |||
{ | |||
buf.push( inputs[ SIGNAL_INPUT ].value ); | |||
// crude for now - basically a saw. | |||
tNow += engineGetSampleTime(); | |||
float intpart; | |||
float fractpart = modf( tNow, &intpart ); | |||
if( (int)intpart % 2 == 0 ) fractpart = 1.0 - fractpart; | |||
float chSig = buf.readBack( (size_t)( fractpart * buf.SZ / 20 ) ); | |||
float depth = params[ DEPTH ].value; | |||
outputs[ CHORUSED_OUTPUT ].value = ( inputs[ SIGNAL_INPUT ].value + depth * chSig ) / ( 1.0 + depth ); | |||
} | |||
}; | |||
struct SortaChorusWidget : ModuleWidget { | |||
SortaChorusWidget( SortaChorus *module); | |||
}; | |||
SortaChorusWidget::SortaChorusWidget( SortaChorus *module ) : ModuleWidget( module ) | |||
{ | |||
box.size = Vec( SCREW_WIDTH * 10, RACK_HEIGHT ); | |||
BaconBackground *bg = new BaconBackground( box.size, "SortaChorus" ); | |||
addChild( bg->wrappedInFramebuffer()); | |||
Vec wdPos( 40, 40 ), knobPos; | |||
bg->addLabelsForLargeKnob( wdPos, "Depth", "None", "Lots", knobPos ); | |||
addParam( ParamWidget::create< RoundLargeBlackKnob >( knobPos, module, | |||
SortaChorus::DEPTH, | |||
0, 2.0, 0.5 ) ); | |||
wdPos.x = box.size.x - 40; | |||
bg->addLabelsForLargeKnob( wdPos, "Speed", "Slow", "Fast", knobPos ); | |||
addParam( ParamWidget::create< RoundLargeBlackKnob >( knobPos, module, | |||
SortaChorus::SPEED, | |||
0, 1.0, 0.5 ) ); | |||
Vec inP = Vec( bg->cx( 24 ) - 30, RACK_HEIGHT - 15 - 43 ); | |||
bg->addPlugLabel( inP, BaconBackground::SIG_IN, "in" ); | |||
addInput( Port::create< PJ301MPort >( inP, | |||
Port::INPUT, | |||
module, | |||
SortaChorus::SIGNAL_INPUT ) ); | |||
Vec outP = Vec( bg->cx( 24 ) + 30, RACK_HEIGHT - 15 - 43 ); | |||
bg->addPlugLabel( outP, BaconBackground::SIG_OUT, "out" ); | |||
addOutput( Port::create< PJ301MPort >( outP, | |||
Port::OUTPUT, | |||
module, | |||
SortaChorus::CHORUSED_OUTPUT ) ); | |||
} | |||
} // namespace rack_plugin_BaconMusic | |||
using namespace rack_plugin_BaconMusic; | |||
RACK_PLUGIN_MODEL_INIT(BaconMusic, SortaChorus) { | |||
Model *modelSortaChorus = Model::create<SortaChorus, SortaChorusWidget>("Bacon Music", "SortaChorus", "SortaChorus", CHORUS_TAG ); | |||
return modelSortaChorus; | |||
} | |||
#endif |
@@ -0,0 +1,128 @@ | |||
#include "standalone_helpers.hpp" | |||
#include "../src/Glissinator.hpp" | |||
#include <iostream> | |||
int main( int argch, char **argv ) | |||
{ | |||
std::cout << "Test Glissinator\n"; | |||
typedef Glissinator< StandaloneModule > G; | |||
for( int io = 0; io <= 1; ++io ) | |||
{ | |||
G g; | |||
g.params[ G::GLISS_TIME ].value = 0.1; | |||
g.inputs[ G::SOURCE_INPUT ].value = 1.00 + io; | |||
g.inputs[ G::SOURCE_INPUT ].active = true; | |||
g.outputs[ G::SLID_OUTPUT ].active = true; | |||
G::results_t ov; | |||
g.multiStep( 100, ov ); | |||
g.inputs[ G::SOURCE_INPUT ].value = 2.00 - io; | |||
g.multiStep( engineGetSampleRate() * 0.15, ov ); | |||
// So this should be monotonically increasing. | |||
auto hd = ov.begin() + 1, pv = ov.begin(); | |||
while( hd != ov.end() ) | |||
{ | |||
if( io == 0 ) | |||
assert( (*hd)[ G::SLID_OUTPUT ].value >= (*pv)[ G::SLID_OUTPUT ].value ); | |||
else | |||
assert( (*hd)[ G::SLID_OUTPUT ].value <= (*pv)[ G::SLID_OUTPUT ].value ); | |||
assert( (*hd)[ G::SLID_OUTPUT ].value >= 1 ); | |||
assert( (*hd)[ G::SLID_OUTPUT ].value <= 2 ); | |||
++hd; | |||
++pv; | |||
} | |||
std::cout << "PASSED: Simple case is monotonic " << 1 + io << " -> " << 2 - io << "\n"; | |||
} | |||
// The turnadound-half-way-through test | |||
for( int io = 0; io <= 1; ++io ) | |||
{ | |||
G g; | |||
g.params[ G::GLISS_TIME ].value = 0.1; | |||
g.inputs[ G::SOURCE_INPUT ].value = 1.00 + io; | |||
g.inputs[ G::SOURCE_INPUT ].active = true; | |||
g.outputs[ G::SLID_OUTPUT ].active = true; | |||
G::results_t ov; | |||
g.multiStep( 100, ov ); | |||
g.inputs[ G::SOURCE_INPUT ].value = 2.00 - io; | |||
g.multiStep( engineGetSampleRate() * 0.07, ov ); | |||
float maxO = g.outputs[ G::SLID_OUTPUT ].value; | |||
g.inputs[ G::SOURCE_INPUT ].value = 1.00 + io; | |||
g.multiStep( engineGetSampleRate() * 0.07, ov ); | |||
// So this should no longer be monotonic strictly but should monotone up to max | |||
// and then down from | |||
bool goingUp = (io == 0)?true:false; | |||
bool hitMax = false; | |||
auto hd = ov.begin() + 1, pv = ov.begin(); | |||
while( hd != ov.end() ) | |||
{ | |||
if( goingUp ) | |||
assert( (*hd)[ G::SLID_OUTPUT ].value >= (*pv)[ G::SLID_OUTPUT ].value ); | |||
else | |||
assert( (*hd)[ G::SLID_OUTPUT ].value <= (*pv)[ G::SLID_OUTPUT ].value ); | |||
if( io == 0 ) | |||
{ | |||
assert( (*hd)[ G::SLID_OUTPUT ].value >= 1 ); | |||
assert( (*hd)[ G::SLID_OUTPUT ].value <= maxO ); | |||
} | |||
else | |||
{ | |||
assert( (*hd)[ G::SLID_OUTPUT ].value <= 2 ); | |||
assert( (*hd)[ G::SLID_OUTPUT ].value >= maxO ); | |||
} | |||
if( (*hd)[G::SLID_OUTPUT].value == maxO && ! hitMax ) { goingUp = ! goingUp; hitMax = true; } // max can repeat | |||
++hd; | |||
++pv; | |||
} | |||
std::cout << "PASSED: Turnaround case is bi-monotonic " << 1 + io << " -> " << 2 - io << "\n"; | |||
} | |||
{ | |||
G g; | |||
// OK so now lets test that gliss time bug. If we reset the gliss time most of the way through | |||
// a gliss, the 0.6.1 version runs away. | |||
g.params[ G::GLISS_TIME ].value = 0.1; | |||
g.inputs[ G::SOURCE_INPUT ].value = 1.00; | |||
g.inputs[ G::SOURCE_INPUT ].active = true; | |||
g.outputs[ G::SLID_OUTPUT ].active = true; | |||
G::results_t ov; | |||
g.multiStep( 100, ov ); | |||
g.inputs[ G::SOURCE_INPUT ].value = 2.00 ; | |||
g.multiStep( engineGetSampleRate() * 0.07, ov ); | |||
g.params[ G::GLISS_TIME ].value = 0; | |||
g.multiStep( 1, ov ); | |||
auto hd = ov.begin() + 1, pv = ov.begin(); | |||
while( hd != ov.end() ) | |||
{ | |||
assert( (*hd)[ G::SLID_OUTPUT ].value >= (*pv)[ G::SLID_OUTPUT ].value ); | |||
assert( (*hd)[ G::SLID_OUTPUT ].value >= 1 ); | |||
assert( (*hd)[ G::SLID_OUTPUT ].value <= 2 ); | |||
++hd; | |||
++pv; | |||
} | |||
std::cout << "PASSED: Jump case is still monotonic and bounded\n"; | |||
} | |||
} |
@@ -0,0 +1,21 @@ | |||
# SUPER crude makefiles for my standalone tests. | |||
# Basically use it as | |||
# make run T=docsaw | |||
# and that will build docsaw.cpp which depends on every header everywhere and voila. | |||
HDRS = $(wildcard ../src/*.hpp) $(wildcard *.hpp) | |||
CPPEXE = c++ -I../../../dep/include -L../../../dep/lib -lrtaudio -std=c++11 -stdlib=libc++ -Werror | |||
RUN_PREFIX = DYLD_LIBRARY_PATH=../../../dep/lib | |||
exe_%: %.cpp $(HDRS) | |||
$(CPPEXE) -o $@ $< | |||
run: exe_$(T) | |||
$(RUN_PREFIX) ./$< | |||
clean: | |||
rm -f exe* | |||
@@ -0,0 +1,27 @@ | |||
#include "standalone_helpers.hpp" | |||
#include "../src/SampleDelay.hpp" | |||
#include <iostream> | |||
int main( int argc, char **argv ) | |||
{ | |||
typedef SampleDelay< StandaloneModule > SD; | |||
{ | |||
SD sd; | |||
int dk = 37; | |||
sd.params[ SD::DELAY_KNOB ].value = dk; | |||
sd.inputs[ SD::SIGNAL_IN ].active = 1; | |||
SD::results_t ov; | |||
int ns = 1000; | |||
for( int i=0; i<ns; ++i ) | |||
{ | |||
sd.inputs[ SD::SIGNAL_IN ].value = i * 1.0f / ns; | |||
sd.step(); | |||
ov.push_back( sd.outputs ); | |||
} | |||
for( int i=dk; i<ns; ++i ) | |||
std::cout << i << " " << ov[ i-1 ][ SD::SIGNAL_OUT ].value << " " << 1.0f * (i-dk)/ ns << "\n"; | |||
} | |||
} |
@@ -0,0 +1,116 @@ | |||
#include "../src/ChipSym.hpp" | |||
#include <iostream> | |||
#include <iomanip> | |||
#include <map> | |||
#include <set> | |||
#include <cassert> | |||
int main( int argc, char ** argv ) | |||
{ | |||
std::cout << "Testing chip periods" << std::endl; | |||
ChipSym::NESNoise noise( 0.0f, 1.0f, 44100 ); | |||
noise.setPeriod( 2 ); | |||
noise.setModeFlag( true ); | |||
for( int i=0; i<5; ++i ) | |||
{ | |||
// So lets start by testing if 31 gives us 31 and 93 gives us 93 | |||
int ct = 0; | |||
noise.setShortLength( ChipSym::NESNoise::SHORT_31 ); | |||
unsigned short target = noise.getRegister(); | |||
do { ct ++; noise.advanceRegister(); } while( noise.getRegister() != target ); | |||
assert( ct == 31 ); | |||
noise.setShortLength( ChipSym::NESNoise::SHORT_93 ); | |||
target = noise.getRegister(); | |||
ct = 0; | |||
do { ct ++; noise.advanceRegister(); } while( noise.getRegister() != target && ct < 100 ); | |||
assert( ct == 93 ); | |||
} | |||
std::map< int, std::pair< unsigned short, int > > resultMap; | |||
std::map< unsigned short, int > sequenceMap; | |||
int sequenceID = 0; | |||
for( unsigned short iRegister=1; iRegister < 0x7FFF; iRegister ++ ) | |||
{ | |||
bool newSeq = false; | |||
noise.setRegister( iRegister ); | |||
// step off my initial point into the sequence | |||
while( noise.getRegister() == iRegister ) noise.advanceRegister(); | |||
unsigned short target = noise.getRegister(); | |||
bool gotR = false; | |||
int stepC = 0; | |||
if( sequenceMap.find( noise.getRegister() ) == sequenceMap.end() ) { | |||
sequenceID ++; | |||
newSeq = true; | |||
} | |||
while( ! gotR ) | |||
{ | |||
++stepC; | |||
if( newSeq ) | |||
sequenceMap[ noise.getRegister() ] = sequenceID; | |||
noise.advanceRegister(); | |||
if( noise.getRegister() == target ) | |||
{ | |||
if( resultMap.find( stepC ) == resultMap.end() ) | |||
{ | |||
resultMap[ stepC ] = std::pair< unsigned short, int >( target, 1 ); | |||
} | |||
else | |||
resultMap[ stepC ].second ++; | |||
gotR = true; | |||
} | |||
} | |||
} | |||
std::cout << "Unique sequence count is " << sequenceID << " " << sequenceMap.size() << "\n"; | |||
// Invert sequenceMap | |||
std::map< int, std::set< unsigned short > > inverseSequenceMap; | |||
for( auto smapKey = sequenceMap.begin(); smapKey != sequenceMap.end(); ++smapKey ) | |||
{ | |||
inverseSequenceMap[ smapKey->second ].insert( smapKey->first ); | |||
} | |||
std::map< int, int > lenMap; | |||
lenMap[ 93 ] = 0; | |||
lenMap[ 31 ] = 0; | |||
for( auto imapKey = inverseSequenceMap.begin(); imapKey != inverseSequenceMap.end(); ++imapKey ) | |||
{ | |||
lenMap[ imapKey->second.size() ] ++; | |||
} | |||
std::cout << "Count[ 93 ] = " << lenMap[ 93 ] << "\nCount[ 31 ] = " << lenMap[ 31 ] << "\n"; | |||
for( auto imapKey = inverseSequenceMap.begin(); imapKey != inverseSequenceMap.end(); ++imapKey ) | |||
{ | |||
std::cout << "Seq: " << std::setw( 4 ) << std::setfill( ' ' ) << std::setbase( 10 ) << imapKey->first << " Len = " << imapKey->second.size() << " "; | |||
int ct = 0; | |||
for( auto secKey = imapKey->second.begin(); secKey != imapKey->second.end() && ct < 8; ++secKey, ++ct ) | |||
{ | |||
std::cout << " 0x" << std::setw( 4 ) << std::setbase( 16 ) << std::setfill( '0' ) << *secKey; | |||
} | |||
std::cout << "\n"; | |||
} | |||
// Finally dump the c structure | |||
std::cout << "unsigned short start_for_93s[] = {\n "; | |||
int ct = 0; | |||
for( auto imapKey = inverseSequenceMap.begin(); imapKey != inverseSequenceMap.end(); ++imapKey, ++ct ) | |||
{ | |||
if( imapKey->second.size() == 93 ) | |||
std::cout << std::setbase( 10 ) << *( imapKey->second.begin() ) << ", "; | |||
if( (ct+1) % 15 == 0 ) | |||
std::cout << "\n "; | |||
} | |||
std::cout << " 0 };\n"; | |||
} |
@@ -0,0 +1,87 @@ | |||
#include "../src/ChipSym.hpp" | |||
#include "standalone_helpers.hpp" | |||
template <typename T> | |||
struct NESGen : StepHandler | |||
{ | |||
T ngen; | |||
NESGen() : ngen( -0.2, 0.2, 44100 ) {} | |||
virtual int dostep( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, | |||
double streamTime, RtAudioStreamStatus status ) override | |||
{ | |||
unsigned int i, j; | |||
double *buffer = (double *) outputBuffer; | |||
for ( i=0; i<nBufferFrames; i++ ) { | |||
*buffer++ = ngen.step(); | |||
} | |||
return 0; | |||
} | |||
}; | |||
int main( int argc, char** argv ) | |||
{ | |||
ChipSym::CPUStepper cp( 44100, 1.789773 ); | |||
std::cout << "Do some NES CPU Tick pulls at 44.1k\n"; | |||
for( uint i=0; i<5; ++i ) | |||
{ | |||
std::cout << i * 10 << "\t"; | |||
for( uint j=0; j<10; ++j ) | |||
{ | |||
std::cout << cp.nextStepCPUTicks() << " "; | |||
} | |||
std::cout << "\n"; | |||
} | |||
#if 0 | |||
std::cout << "Running the NES tri gen\n"; | |||
NESGen< ChipSym::NESTriangle > triGen; | |||
int df = 2<<8; | |||
triGen.ngen.setDigWavelength( df ); | |||
triGen.playAudioUntilEnterPressed(); | |||
triGen.ngen.setDigWavelength( df / 2 ); | |||
triGen.playAudioUntilEnterPressed(); | |||
NESGen< ChipSym::NESPulse > pulsGen; | |||
df = 2<<9; | |||
pulsGen.ngen.setDigWavelength( df ); | |||
pulsGen.playAudioUntilEnterPressed(); | |||
pulsGen.ngen.setDigWavelength( df / 2 ); | |||
pulsGen.playAudioUntilEnterPressed(); | |||
NESGen< ChipSym::NESNoise > noiseGen; | |||
noiseGen.playAudioUntilEnterPressed(); | |||
noiseGen.ngen.setModeFlag( true ); | |||
noiseGen.playAudioUntilEnterPressed(); | |||
noiseGen.ngen.setModeFlag( false ); | |||
noiseGen.ngen.setPeriod( 3 ); | |||
noiseGen.playAudioUntilEnterPressed(); | |||
noiseGen.ngen.setModeFlag( false ); | |||
noiseGen.ngen.setPeriod( 11 ); | |||
noiseGen.playAudioUntilEnterPressed(); | |||
#endif | |||
NESGen< ChipSym::NESArbitraryWaveform > arbGen; | |||
int df = 2<<7; | |||
arbGen.ngen.setDigWavelength( df ); | |||
arbGen.playAudioUntilEnterPressed(); | |||
for( uint i=0; i<32; ++i ) | |||
{ | |||
arbGen.ngen.setWaveformPoint( i, 7 * ( sin ( 2 * 3.14159 * i / 32 ) + 1 ) ); | |||
} | |||
arbGen.playAudioUntilEnterPressed(); | |||
return 0; | |||
} |
@@ -0,0 +1,34 @@ | |||
/* | |||
** Just showing how to use the standalone helpers to make a mono generator which gets | |||
** punped to default output quickly and easily | |||
*/ | |||
#include "standalone_helpers.hpp" | |||
#include <iostream> | |||
#include <cstdlib> | |||
struct SawGen : StepHandler | |||
{ | |||
double lastValue; | |||
SawGen() : lastValue( 0 ) {} | |||
virtual int dostep( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, | |||
double streamTime, RtAudioStreamStatus status ) override | |||
{ | |||
unsigned int i, j; | |||
double *buffer = (double *) outputBuffer; | |||
for ( i=0; i<nBufferFrames; i++ ) { | |||
*buffer++ = lastValue; | |||
lastValue += 0.015; | |||
if ( lastValue >= 1.0 ) lastValue -= 2.0; | |||
} | |||
return 0; | |||
} | |||
}; | |||
int main() | |||
{ | |||
SawGen sg; | |||
sg.playAudioUntilEnterPressed(); | |||
} |
@@ -0,0 +1,44 @@ | |||
#include "../src/KSSynth.hpp" | |||
#include "standalone_helpers.hpp" | |||
#include <iostream> | |||
struct KSGen : StepHandler | |||
{ | |||
KSSynth s; | |||
KSGen() : s( -0.9, 0.9, 44100 ) { } | |||
virtual int dostep( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, | |||
double streamTime, RtAudioStreamStatus status ) override | |||
{ | |||
unsigned int i, j; | |||
double *buffer = (double *) outputBuffer; | |||
for ( i=0; i<nBufferFrames; i++ ) { | |||
*buffer++ = s.step(); | |||
} | |||
if( ! s.active ) | |||
return 1; | |||
return 0; | |||
} | |||
}; | |||
int main( int argc, char **argv ) | |||
{ | |||
KSGen gen; | |||
std::cout << "packets " << gen.s.numInitPackets() << "\n"; | |||
gen.s.filtAtten = 3.0; | |||
gen.s.filtParamA = 0.3; | |||
gen.s.packet = KSSynth::RANDOM; | |||
float freq = 440; | |||
for( int i=0; i<=12; ++i ) | |||
{ | |||
float mul = pow( 2.0, i / 12.0f ); | |||
gen.s.trigger( freq * mul ); | |||
gen.playAudioUntilStepsDone(); | |||
} | |||
} |
@@ -0,0 +1,123 @@ | |||
#include "RtAudio.h" | |||
#include <unistd.h> | |||
int engineGetSampleRate() { return 44100; } | |||
struct StepHandler | |||
{ | |||
virtual ~StepHandler() { }; | |||
virtual int dostep( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, | |||
double streamTime, RtAudioStreamStatus status ) = 0; | |||
static int step( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, | |||
double streamTime, RtAudioStreamStatus status, void* userData ) | |||
{ | |||
StepHandler *sh = (StepHandler *)userData; | |||
return sh->dostep( outputBuffer, inputBuffer, nBufferFrames, streamTime, status ); | |||
} | |||
RtAudio startDac() | |||
{ | |||
RtAudio dac; | |||
if ( dac.getDeviceCount() < 1 ) { | |||
std::cout << "\nNo audio devices found!\n"; | |||
exit( 0 ); | |||
} | |||
RtAudio::StreamParameters parameters; | |||
parameters.deviceId = dac.getDefaultOutputDevice(); | |||
parameters.nChannels = 1; | |||
parameters.firstChannel = 0; | |||
unsigned int sampleRate = 44100; | |||
unsigned int bufferFrames = 256; // 256 sample frames | |||
try { | |||
dac.openStream( ¶meters, NULL, RTAUDIO_FLOAT64, | |||
sampleRate, &bufferFrames, &StepHandler::step, (void *)this ); | |||
dac.startStream(); | |||
} | |||
catch ( RtAudioError& e ) { | |||
e.printMessage(); | |||
exit( 0 ); | |||
} | |||
return dac; | |||
} | |||
void stopDac( RtAudio dac ) | |||
{ | |||
try { | |||
// Stop the stream | |||
dac.stopStream(); | |||
} | |||
catch (RtAudioError& e) { | |||
e.printMessage(); | |||
} | |||
if ( dac.isStreamOpen() ) dac.closeStream(); | |||
} | |||
int playAudioUntilStepsDone() | |||
{ | |||
RtAudio dac = startDac(); | |||
while( dac.isStreamRunning() ) | |||
{ | |||
usleep( 100 ); | |||
} | |||
if ( dac.isStreamOpen() ) dac.closeStream(); | |||
return 0; | |||
} | |||
int playAudioUntilEnterPressed() | |||
{ | |||
RtAudio dac = startDac(); | |||
char input; | |||
std::cout << "\nPlaying ... press <enter> to quit.\n"; | |||
std::cin.get( input ); | |||
stopDac( dac ); | |||
return 0; | |||
} | |||
}; | |||
struct StandaloneModule | |||
{ | |||
struct thing | |||
{ | |||
float value; | |||
bool active; | |||
}; | |||
typedef std::vector< thing > values_t; | |||
typedef std::vector< values_t > results_t; | |||
void multiStep( size_t stepCount, results_t &into ) | |||
{ | |||
for( size_t i=0; i<stepCount; ++i ) | |||
{ | |||
step(); | |||
into.push_back( outputs ); | |||
} | |||
} | |||
values_t params; | |||
values_t lights; | |||
values_t inputs; | |||
values_t outputs; | |||
StandaloneModule( int nparam, int ninp, int nout, int nlight ) | |||
{ | |||
params.resize( nparam ); | |||
lights.resize( nlight ); | |||
inputs.resize( ninp ); | |||
outputs.resize( nout ); | |||
} | |||
virtual void step() { }; | |||
}; |
@@ -11,8 +11,10 @@ endef | |||
.PHONY: | |||
bin: | |||
# $(foreach pname,$(PLUGINS),$(eval ($(call run_make,$(pname),bin)))) | |||
$(call run_make,Alikins,bin) | |||
$(call run_make,AS,bin) | |||
$(call run_make,AudibleInstruments,bin) | |||
$(call run_make,BaconMusic,bin) | |||
$(call run_make,Befaco,bin) | |||
$(call run_make,Bidoo,bin) | |||
$(call run_make,Bogaudio,bin) | |||
@@ -47,8 +49,10 @@ bin: | |||
clean: | |||
# $(foreach pname,$(PLUGINS),$(eval $(call run_make,$(pname),clean))) | |||
# $(foreach pname,$(PLUGINS),$(eval echo $(pname))) | |||
$(call run_make,Alikins,clean) | |||
$(call run_make,AS,clean) | |||
$(call run_make,AudibleInstruments,clean) | |||
$(call run_make,BaconMusic,clean) | |||
$(call run_make,Befaco,clean) | |||
$(call run_make,Bidoo,clean) | |||
$(call run_make,Bogaudio,clean) | |||
@@ -568,8 +568,10 @@ Model *pluginGetModel(std::string pluginSlug, std::string modelSlug) { | |||
#ifdef USE_VST2 | |||
extern "C" { | |||
extern void init_plugin_Alikins (rack::Plugin *p); | |||
extern void init_plugin_AS (rack::Plugin *p); | |||
extern void init_plugin_AudibleInstruments (rack::Plugin *p); | |||
extern void init_plugin_BaconMusic (rack::Plugin *p); | |||
extern void init_plugin_Befaco (rack::Plugin *p); | |||
extern void init_plugin_Bidoo (rack::Plugin *p); | |||
extern void init_plugin_Bogaudio (rack::Plugin *p); | |||
@@ -628,8 +630,10 @@ static void vst2_load_static_rack_plugin(const char *_name, InitCallback _initCa | |||
} | |||
void vst2_load_static_rack_plugins(void) { | |||
vst2_load_static_rack_plugin("Alikins", &init_plugin_Alikins); | |||
vst2_load_static_rack_plugin("AS", &init_plugin_AS); | |||
vst2_load_static_rack_plugin("AudibleInstruments", &init_plugin_AudibleInstruments); | |||
vst2_load_static_rack_plugin("BaconMusic", &init_plugin_BaconMusic); | |||
vst2_load_static_rack_plugin("Befaco", &init_plugin_Befaco); | |||
vst2_load_static_rack_plugin("Bidoo", &init_plugin_Bidoo); | |||
vst2_load_static_rack_plugin("Bogaudio", &init_plugin_Bogaudio); | |||
@@ -1,3 +1,23 @@ | |||
** July 9th, 2018 | |||
- add module Alikins.IdleSwitch | |||
- add module Alikins.MomentaryOnButtons | |||
- add module Alikins.BigMuteButton | |||
- add module Alikins.ColorPanel | |||
- add module Alikins.GateLength | |||
- add module Alikins.SpecificValue | |||
- add module BaconMusic.Glissinator | |||
- add module BaconMusic.PolyGnome | |||
- add module BaconMusic.QuantEyes | |||
- add module BaconMusic.SampleDelay | |||
- add module BaconMusic.SortaChorus | |||
- add module BaconMusic.ChipNoise | |||
- add module BaconMusic.ChipWaves | |||
- add module BaconMusic.ChipYourWave | |||
- add module BaconMusic.KarplusStrongPoly | |||
- add module BaconMusic.ALingADing | |||
- add module BaconMusic.Bitulator | |||
** July 8th, 2018 | |||
- add module DHE-Modules.BoosterStage | |||
- add module DHE-Modules.Cubic | |||
@@ -1,4 +1,4 @@ | |||
VeeSeeVST Rack VST 2.4 Plugin -- July 2nd, 2018 | |||
VeeSeeVST Rack VST 2.4 Plugin -- July 9th, 2018 | |||
=============================================== | |||
!!!------------------------------------------------------------------------------ | |||
@@ -15,6 +15,7 @@ This is a quick'n'dirty adaption of VCV Rack 0.6.1 for the VST2 format. | |||
+ supports up to 8 audio inputs | |||
+ supports VST program chunks (=> patches are saved with the DAW's project file or as .fxp files) | |||
+ supports VST host timing (audioMasterGetTime / kVstTempoValid / kVstTransportPlaying, see Core.MIDI-1 module) | |||
+ supports VST parameters (send / recv) | |||
- does not support plugin DLLs due to VCV Rack's architecture which prevents this when it is run as a plugin itself | |||
- future releases will contain additional (open source) add-ons modules | |||
@@ -26,6 +27,12 @@ Tested in | |||
- Propellerhead Reason 10 | |||
The VST2 plugin includes the following add-on modules: | |||
- Alikins.IdleSwitch | |||
- Alikins.MomentaryOnButtons | |||
- Alikins.BigMuteButton | |||
- Alikins.ColorPanel | |||
- Alikins.GateLength | |||
- Alikins.SpecificValue | |||
- AS.ADSR | |||
- AS.AtNuVrTr | |||
- AS.BPMCalc | |||
@@ -75,6 +82,17 @@ The VST2 plugin includes the following add-on modules: | |||
- AudibleInstruments.Blinds | |||
- AudibleInstruments.Veils | |||
- AudibleInstruments.Frames | |||
- BaconMusic.Glissinator | |||
- BaconMusic.PolyGnome | |||
- BaconMusic.QuantEyes | |||
- BaconMusic.SampleDelay | |||
- BaconMusic.SortaChorus | |||
- BaconMusic.ChipNoise | |||
- BaconMusic.ChipWaves | |||
- BaconMusic.ChipYourWave | |||
- BaconMusic.KarplusStrongPoly | |||
- BaconMusic.ALingADing | |||
- BaconMusic.Bitulator | |||
- Befaco.ABC | |||
- Befaco.DualAtenuverter | |||
- Befaco.EvenVCO | |||
@@ -1,69 +1,71 @@ | |||
[0.000 info src/main.cpp:63] VeeSeeVST Rack 0.6.1 | |||
[0.000 info src/main.cpp:66] Global directory: f:\git\VeeSeeVSTRack\vst2_bin\/ | |||
[0.000 info src/main.cpp:67] Local directory: f:\git\VeeSeeVSTRack\vst2_bin\/ | |||
[0.000 info src/plugin.cpp:627] vcvrack: Loaded static plugin AS 0.6.1 | |||
[0.000 info src/plugin.cpp:627] vcvrack: Loaded static plugin AudibleInstruments 0.6.1 | |||
[0.001 info src/plugin.cpp:627] vcvrack: Loaded static plugin Befaco 0.6.1 | |||
[0.002 info src/plugin.cpp:627] vcvrack: Loaded static plugin Bidoo 0.6.1 | |||
[0.002 info src/plugin.cpp:627] vcvrack: Loaded static plugin Bogaudio 0.6.1 | |||
[0.002 info src/plugin.cpp:627] vcvrack: Loaded static plugin cf 0.6.1 | |||
[0.002 info src/plugin.cpp:627] vcvrack: Loaded static plugin DHE-Modules 0.6.1 | |||
[0.003 info src/plugin.cpp:627] vcvrack: Loaded static plugin ErraticInstruments 0.6.1 | |||
[0.003 info src/plugin.cpp:627] vcvrack: Loaded static plugin ESeries 0.6.1 | |||
[0.003 info src/plugin.cpp:627] vcvrack: Loaded static plugin FrozenWasteland 0.6.1 | |||
[0.003 info src/plugin.cpp:627] vcvrack: Loaded static plugin Fundamental 0.6.1 | |||
[0.003 info src/plugin.cpp:627] vcvrack: Loaded static plugin HetrickCV 0.6.1 | |||
[0.003 info src/plugin.cpp:627] vcvrack: Loaded static plugin huaba 0.6.1 | |||
[0.004 info src/plugin.cpp:627] vcvrack: Loaded static plugin Koralfx-Modules 0.6.1 | |||
[0.004 info src/plugin.cpp:627] vcvrack: Loaded static plugin LindenbergResearch 0.6.1 | |||
[0.005 info src/plugin.cpp:627] vcvrack: Loaded static plugin LOGinstruments 0.6.1 | |||
[0.005 info src/plugin.cpp:627] vcvrack: Loaded static plugin ML_modules 0.6.1 | |||
[0.005 info src/plugin.cpp:627] vcvrack: Loaded static plugin modular80 0.6.1 | |||
[0.005 info src/plugin.cpp:627] vcvrack: Loaded static plugin mscHack 0.6.1 | |||
[0.005 info src/plugin.cpp:627] vcvrack: Loaded static plugin mtsch-plugins 0.6.1 | |||
[0.005 info src/plugin.cpp:627] vcvrack: Loaded static plugin NauModular 0.6.1 | |||
[0.006 info src/plugin.cpp:627] vcvrack: Loaded static plugin Qwelk 0.6.1 | |||
[0.006 info src/plugin.cpp:627] vcvrack: Loaded static plugin SonusModular 0.6.1 | |||
[0.006 info src/plugin.cpp:627] vcvrack: Loaded static plugin Southpole-parasites 0.6.1 | |||
[0.006 info src/plugin.cpp:627] vcvrack: Loaded static plugin squinkylabs-plug1 0.6.1 | |||
[0.006 info src/plugin.cpp:627] vcvrack: Loaded static plugin SubmarineFree 0.6.1 | |||
[0.006 info src/plugin.cpp:627] vcvrack: Loaded static plugin Template 0.6.1 | |||
[0.007 info src/plugin.cpp:627] vcvrack: Loaded static plugin trowaSoft 0.6.1 | |||
[0.007 info src/plugin.cpp:627] vcvrack: Loaded static plugin Valley 0.6.1 | |||
[0.007 info src/plugin.cpp:627] vcvrack: Loaded static plugin VultModules 0.6.1 | |||
[0.000 info src/plugin.cpp:629] vcvrack: Loaded static plugin Alikins 0.6.1 | |||
[0.000 info src/plugin.cpp:629] vcvrack: Loaded static plugin AS 0.6.1 | |||
[0.001 info src/plugin.cpp:629] vcvrack: Loaded static plugin AudibleInstruments 0.6.1 | |||
[0.001 info src/plugin.cpp:629] vcvrack: Loaded static plugin BaconMusic 0.6.1 | |||
[0.002 info src/plugin.cpp:629] vcvrack: Loaded static plugin Befaco 0.6.1 | |||
[0.002 info src/plugin.cpp:629] vcvrack: Loaded static plugin Bidoo 0.6.1 | |||
[0.002 info src/plugin.cpp:629] vcvrack: Loaded static plugin Bogaudio 0.6.1 | |||
[0.003 info src/plugin.cpp:629] vcvrack: Loaded static plugin cf 0.6.1 | |||
[0.003 info src/plugin.cpp:629] vcvrack: Loaded static plugin DHE-Modules 0.6.1 | |||
[0.003 info src/plugin.cpp:629] vcvrack: Loaded static plugin ErraticInstruments 0.6.1 | |||
[0.003 info src/plugin.cpp:629] vcvrack: Loaded static plugin ESeries 0.6.1 | |||
[0.003 info src/plugin.cpp:629] vcvrack: Loaded static plugin FrozenWasteland 0.6.1 | |||
[0.003 info src/plugin.cpp:629] vcvrack: Loaded static plugin Fundamental 0.6.1 | |||
[0.004 info src/plugin.cpp:629] vcvrack: Loaded static plugin HetrickCV 0.6.1 | |||
[0.004 info src/plugin.cpp:629] vcvrack: Loaded static plugin huaba 0.6.1 | |||
[0.004 info src/plugin.cpp:629] vcvrack: Loaded static plugin Koralfx-Modules 0.6.1 | |||
[0.004 info src/plugin.cpp:629] vcvrack: Loaded static plugin LindenbergResearch 0.6.1 | |||
[0.004 info src/plugin.cpp:629] vcvrack: Loaded static plugin LOGinstruments 0.6.1 | |||
[0.004 info src/plugin.cpp:629] vcvrack: Loaded static plugin ML_modules 0.6.1 | |||
[0.004 info src/plugin.cpp:629] vcvrack: Loaded static plugin modular80 0.6.1 | |||
[0.005 info src/plugin.cpp:629] vcvrack: Loaded static plugin mscHack 0.6.1 | |||
[0.005 info src/plugin.cpp:629] vcvrack: Loaded static plugin mtsch-plugins 0.6.1 | |||
[0.005 info src/plugin.cpp:629] vcvrack: Loaded static plugin NauModular 0.6.1 | |||
[0.005 info src/plugin.cpp:629] vcvrack: Loaded static plugin Qwelk 0.6.1 | |||
[0.005 info src/plugin.cpp:629] vcvrack: Loaded static plugin SonusModular 0.6.1 | |||
[0.006 info src/plugin.cpp:629] vcvrack: Loaded static plugin Southpole-parasites 0.6.1 | |||
[0.006 info src/plugin.cpp:629] vcvrack: Loaded static plugin squinkylabs-plug1 0.6.1 | |||
[0.006 info src/plugin.cpp:629] vcvrack: Loaded static plugin SubmarineFree 0.6.1 | |||
[0.006 info src/plugin.cpp:629] vcvrack: Loaded static plugin Template 0.6.1 | |||
[0.006 info src/plugin.cpp:629] vcvrack: Loaded static plugin trowaSoft 0.6.1 | |||
[0.006 info src/plugin.cpp:629] vcvrack: Loaded static plugin Valley 0.6.1 | |||
[0.006 info src/plugin.cpp:629] vcvrack: Loaded static plugin VultModules 0.6.1 | |||
[0.007 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_146097_cc.svg | |||
[0.008 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_31859_cc.svg | |||
[0.008 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_1343816_cc.svg | |||
[0.007 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_31859_cc.svg | |||
[0.007 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_1343816_cc.svg | |||
[0.008 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_1343811_cc.svg | |||
[0.008 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_1084369_cc.svg | |||
[0.008 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_1745061_cc.svg | |||
[0.009 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_1240789_cc.svg | |||
[0.009 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_305536_cc.svg | |||
[0.010 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_468341_cc.svg | |||
[0.200 info src/window.cpp:703] Loaded font f:\git\VeeSeeVSTRack\vst2_bin\/res/fonts/DejaVuSans.ttf | |||
[0.202 info src/settings.cpp:185] Loading settings f:\git\VeeSeeVSTRack\vst2_bin\/settings.json | |||
[0.303 info src/app/RackWidget.cpp:192] Loading patch from string | |||
[0.304 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/Core/AudioInterface.svg | |||
[0.305 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/ScrewSilver.svg | |||
[0.305 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/PJ301M.svg | |||
[0.305 info src/window.cpp:703] Loaded font f:\git\VeeSeeVSTRack\vst2_bin\/res/fonts/ShareTechMono-Regular.ttf | |||
[0.306 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/Core/MIDIToCVInterface.svg | |||
[0.309 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/XCO.svg | |||
[0.309 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/knob_68px.svg | |||
[0.309 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/knob_16px.svg | |||
[0.309 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/button_9px_0.svg | |||
[0.310 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/button_9px_1.svg | |||
[0.310 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/knob_38px.svg | |||
[0.310 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/slider_switch_2_14px_0.svg | |||
[0.310 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/slider_switch_2_14px_1.svg | |||
[0.310 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/port.svg | |||
[0.311 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Fundamental/res/VCA.svg | |||
[0.311 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/RoundLargeBlackKnob.svg | |||
[0.312 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Fundamental/res/VCF.svg | |||
[0.312 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/RoundHugeBlackKnob.svg | |||
[0.313 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/AS/res/ADSR.svg | |||
[0.314 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/AS/res/as-hexscrew.svg | |||
[0.314 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/AS/res/as-SlidePot.svg | |||
[0.314 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/AS/res/as-SlidePotHandle.svg | |||
[0.315 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/AS/res/as-PJ301M.svg | |||
[10.130 info src/app/RackWidget.cpp:154] Saving patch to string | |||
[0.008 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_1240789_cc.svg | |||
[0.008 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_305536_cc.svg | |||
[0.008 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/icons/noun_468341_cc.svg | |||
[0.179 info src/window.cpp:703] Loaded font f:\git\VeeSeeVSTRack\vst2_bin\/res/fonts/DejaVuSans.ttf | |||
[0.181 info src/settings.cpp:185] Loading settings f:\git\VeeSeeVSTRack\vst2_bin\/settings.json | |||
[0.281 info src/app/RackWidget.cpp:192] Loading patch from string | |||
[0.283 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/Core/AudioInterface.svg | |||
[0.283 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/ScrewSilver.svg | |||
[0.283 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/PJ301M.svg | |||
[0.283 info src/window.cpp:703] Loaded font f:\git\VeeSeeVSTRack\vst2_bin\/res/fonts/ShareTechMono-Regular.ttf | |||
[0.285 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/Core/MIDIToCVInterface.svg | |||
[0.287 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/XCO.svg | |||
[0.287 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/knob_68px.svg | |||
[0.288 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/knob_16px.svg | |||
[0.288 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/button_9px_0.svg | |||
[0.288 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/button_9px_1.svg | |||
[0.288 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/knob_38px.svg | |||
[0.289 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/slider_switch_2_14px_0.svg | |||
[0.289 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/slider_switch_2_14px_1.svg | |||
[0.289 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Bogaudio/res/port.svg | |||
[0.290 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Fundamental/res/VCA.svg | |||
[0.290 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/RoundLargeBlackKnob.svg | |||
[0.291 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/Fundamental/res/VCF.svg | |||
[0.291 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\/res/ComponentLibrary/RoundHugeBlackKnob.svg | |||
[0.292 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/AS/res/ADSR.svg | |||
[0.292 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/AS/res/as-hexscrew.svg | |||
[0.293 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/AS/res/as-SlidePot.svg | |||
[0.293 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/AS/res/as-SlidePotHandle.svg | |||
[0.293 info src/window.cpp:754] Loaded SVG f:\git\VeeSeeVSTRack\vst2_bin\plugins/AS/res/as-PJ301M.svg | |||
[17.704 info src/app/RackWidget.cpp:154] Saving patch to string |
@@ -0,0 +1,27 @@ | |||
BSD 3-Clause License | |||
Redistribution and use in source and binary forms, with or without | |||
modification, are permitted provided that the following conditions are met: | |||
* Redistributions of source code must retain the above copyright notice, this | |||
list of conditions and the following disclaimer. | |||
* Redistributions in binary form must reproduce the above copyright notice, | |||
this list of conditions and the following disclaimer in the documentation | |||
and/or other materials provided with the distribution. | |||
* Neither the name of the copyright holder nor the names of its | |||
contributors may be used to endorse or promote products derived from | |||
this software without specific prior written permission. | |||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
@@ -0,0 +1,153 @@ | |||
# VCV Rack plugin | |||
Plugins for use with VCV Rack virtual module synthesizer (https://github.com/VCVRack/Rack) | |||
 | |||
## Modules | |||
### IdleSwitch | |||
#### What | |||
An 'idle loop' inspired module for detecting when an input | |||
is idle and turns on a gate until it sees activity again. | |||
If no input events are seen at "Input Source" within the timeout period | |||
emit a gate on "Idle Gate Output" that lasts until there are input events | |||
again. Then reset the timeout period. | |||
Sort of metaphoricaly like an idle handler or timeout in event based | |||
programming like GUI main loops. | |||
The timeout period is set by the value | |||
of the "Time before idle" param. | |||
##### Input event | |||
An "Input event" is a gate or trigger (or a CV or audio signal | |||
that looks sufficently like a gate or trigger). | |||
After a input event, the Idle Gate Output will remain on until | |||
an input event is seen at Input Source. When there is an input event, the Idle | |||
Gate Output is turned off until the expiration of the "Time before idle" or | |||
the next "Reset idle". | |||
##### Reset timer | |||
If there is a "Reset timer" source, when it gets an event, the timeout period | |||
is reset. But unlike an "Input event", a "Reset timer" event does does not | |||
reset the idle status. | |||
After "Reset time" event, the "Idle Gate Output" will remain on until | |||
an input event is seen at Input Source. When there is an input event, the Idle | |||
Gate Output is turned off until the expiration of the "Time before idle" or | |||
the next "Reset idle". | |||
To use the eventloop/gui main loop analogy, a "Reset idle" event is equilivent to | |||
running an idle handler directly (or running a mainloop iteration with no non-idle | |||
events pending). | |||
##### Clock input | |||
If a cable is hooked to the "Clock Input" then the pulse from the clock | |||
will determine with the idle time expires. When there is a clock pulse, | |||
if there were no input events since the previous clock pulse that is | |||
considered idle. | |||
The "Time before idle" display will reflect the time since the last | |||
clock pulse. The "Time remaining" display will always show 0 for now, | |||
at least until it can predict the future. | |||
##### Time input and output | |||
The "Time input" expects a CV output between 0 and 10V. The voltage | |||
maps directly do the number of seconds for the idle timeout. | |||
Eg. a 5.0V CV to "Time input" sets "Time before idle" to 5000ms (5.0 seconds) | |||
The "Time output" sends a CV output between 0 and 10V indicating | |||
the "Time before idle" value. | |||
The "Time output" can be used to connect one or more IdleSwitch modules | |||
together so they share the same "Time before idle". | |||
In "Clock input" mode, the "Time Output" will correspond with the | |||
"Time before idle" display until the time is over 10s, then the | |||
"Time output" will max out at 10V. | |||
##### Gate Output and idle start and end trigger outputs | |||
Once the idle timeout has expired, the "gate output" output | |||
will go high (+10V) and stay high until there are new input | |||
source events. ie, it turns on when there is no actively for | |||
the "Time before idle" length of time. | |||
The "start out" output will send a +10V trigger at the start | |||
of idle, at the start of the "gate out" gate. | |||
The "end out" output will send a +10V trigger at the end of | |||
idle, at the end of the "gate out" gate. This also corresponds | |||
to the same time as when a new "Input source" event is detected. | |||
"start of" trigger == "we have gone idle" | |||
"end of" trigger == "we got input source events (activity) and am no | |||
longer idle. | |||
##### Switched input and output | |||
The "Input" is switched between "When idle" and | |||
"Off idle" outputs based on idle state. | |||
The "Input" port on the bottom left can be any input. | |||
The "Input" will be routed to the "When idle" output if idle. | |||
Otherwise, the "Input" is routed to the "Off idle" output. | |||
Note the "Input" does not have to be related to the input connected | |||
to the "Input source". "Input source" typically needs to be triggers | |||
or gates, but "Input" can be any signal. | |||
#### Why | |||
Original intentional was to use in combo with a human player and midi/cv keyboard. | |||
As long as the human is playing, the IdleSwitch output is 'off', but if they go | |||
idle for some time period the output is turned on. For example, a patch may plain | |||
loud drone when idle, but would turn the drone off or down when the human played | |||
and then turn it back on when it stopped. Or maybe it could be used to start an | |||
drum fill... | |||
The "Reset idle" input allows this be kind of synced to a clock, beat, or sequence. | |||
In the dronevexample above, the drone would then only come back in on a beat. | |||
And perhaps most importantly, it can be used to do almost random output and | |||
make weird noises. | |||
### ColorPanel | |||
A CV controlled RGB/HSL color "blank" panel. | |||
Supports selectable input ranges (0V to 10V or -5V to 5V) via right click context menu | |||
The color input mode is also selectable via the context menu. | |||
Module is resizable. | |||
### BigMuteButton | |||
A big mute button. | |||
Use just before output to the audio interface module. | |||
When clicked, will mute the output output. Will unmute on next | |||
click. | |||
Easier/quicker than adjusting mixer levels, or system volume, or finding | |||
the tiny mute button on mixers. | |||
### MomentaryOnButtons | |||
A set of GUI buttons that send a +10V output while pressed. | |||
## License | |||
BSD 3-clause. See LICENSE.txt |
@@ -0,0 +1,203 @@ | |||
plugin ideas: | |||
- Mult with labels | |||
- so I can route a distance output to mult and label | |||
it as 'main seq gate output' to avoid tracing cables | |||
- seq with start/stop/reset triggers (ie, one shot sequencer) | |||
(or 'play', 'pause', 'go to begin') | |||
- solve 'how do I run a sequence once?' FAQ | |||
- event log | |||
- visualization of past triggers/gates | |||
- time series or sparkline-ish | |||
- more or less a scope but without y values and | |||
a very long time | |||
- could do diff colors for gates/triggers | |||
- or gates up and triggers down | |||
- wide module | |||
- multiple time view zooms | |||
- 1x, 2x, 5x, 10, 20x, 50x, etc | |||
- mostly for appearance or troubleshooting/debugging event stuff | |||
- could evolve to supporting playing the the history back | |||
- 1x speed or faster/slower | |||
- scrubbing? | |||
- multiple channels of events | |||
- table module | |||
- "an oscillator is a phasor and a table" | |||
- phasor input (0.0 -> 1.0) or other scaled | |||
- value output | |||
- on step, read phasor value, look for a match | |||
in the table, output the value | |||
- ui | |||
- just a scrollable list? | |||
- columns | |||
- phasor | |||
- start range | |||
- end range | |||
- output value | |||
- buttons to add delete entry | |||
- load table from json in context menu | |||
- uses: | |||
- feed a sawtooth osc output into it to | |||
create rudimentary wave table | |||
- arbitrary quantizer | |||
- building block | |||
- fancy options | |||
- entries that map a range of phasor input | |||
values to a range of output values | |||
- map ranges could be linear/log etc | |||
- linear | |||
- slope | |||
- offset | |||
- sub ranges could be cos/sine/exp/some distribution etc | |||
- (similar to the old GIMP color gradient editor) | |||
- likely could use gimp gradient (ggr) files as a type of table | |||
- modulating the table offset or value amplitudes | |||
- credits module | |||
- widget that shows author, create/mod date, urls, license, other metainfo | |||
- would save into vcv file as module json data | |||
- use a rack_credits.json in ~/ or rack doc root to set the default | |||
- list of author/credit metadata | |||
- would get appended to if modified by new author | |||
- how to only update on 'save' or 'save as' ? | |||
- don't want to append on every autosave.vcv write | |||
- track a single 'last_edit' to persist on every toJson | |||
- then on load/fromJSON, add last_edit to author list | |||
- if there is a way for plugin to distinquish a 'real save' vs | |||
an 'autosave', then could use that. | |||
- how to tell when a patch has been 'modified'? | |||
- needed to avoid adding info on every save or autosave | |||
- slightly klugey way: | |||
- if plugin knows the patch it is in, could load patch .vcv/json | |||
data and compare to 'previous'. | |||
- chksum would be simplest | |||
- but could do a smarter/deeper compare diving into the data | |||
- uses: | |||
- a patch with the module includes the author info | |||
- if patch is shared (via patchstorage for ex), and modified | |||
by a different author, the module json would include info | |||
for the original author and modification author | |||
- just as a display widget when sharing a screen capture | |||
- concerns: | |||
- could probably be better implemented as a feature of the core vcv serialization | |||
- 'Equal and opposite' | |||
- two eqs with 'opposite' settings and an effects loop in between | |||
- before eq, after eq | |||
- same bands and q's, but opposite gain | |||
- ie, if 'before eq' boosts bass and treble by 10db and cuts mids by 12db | |||
then the 'after eq' would cut bass and treble by 10db and boost mids by 12db | |||
- an effects send and return in between | |||
- munge audio signal, send it to effect (a distortion for ex) and unmunge on the way out | |||
- the before/after transforms would automatically mirror each other | |||
- otherwise pointless since it could be done with other modules | |||
- other variations could exist for other reversible-ish transforms | |||
- dont have to be perfect or lossless | |||
- compress/expand | |||
- bitcrush / 'bit extropalate' (is that a thing?) | |||
- forward/reverse delay? | |||
- pitch shift (probably most interesting, but | |||
- transport gui module | |||
- play/stop/pause/fast forward/reverse/record buttons | |||
- gate and trig outputs for them | |||
- just buttons, but with a explicit purpose | |||
- uses | |||
- generative patches that need a 'press play to start' | |||
- specific values | |||
- module with just text entrie fields for entering a float value | |||
and an output for that value | |||
- possibly with text widget in combo with a dial or inc/dec | |||
buttons (spinbox widget) | |||
- or 'click and drag to change value' | |||
- up/down for small value | |||
- left/right for multiplier (middle = 1x, left = 0..1.0x, right >1.0x multi) | |||
- 'up and to the left' for fine grained adjustments | |||
- fancy | |||
- maybe automatically also show CV v/oct freq or note equiv | |||
- maybe gate out and button to click to send value and gate output together | |||
- Seq with per step gate length and rest length between steps | |||
- 'rest' steps basically | |||
- A cv 1v/oct display -> freq hz -> period secs -> bpm. | |||
- ( could display v passed through and/or let you specify as text) | |||
- CV out | |||
- button to send gate | |||
- basically a keyboard with text/entry widgets to let you specify | |||
CV voltage/hz/period/note name | |||
- other possible things to display/select | |||
- midi note number | |||
- # of steps from a root | |||
- interval from a root ( third, flat fifth, etc) | |||
- staff / note | |||
- bpm (for a LFO or general nerdery) | |||
- generic voltage quantizer | |||
- based on 'table' module | |||
- except input CV is 'rounded' to edge of maps | |||
- see 'fancy options' for table module above | |||
- 'Attached file' module. | |||
- GUI file picker | |||
- add base64 of file to data to store in plugin Json. | |||
- Could be a list | |||
- 'Maze Table' | |||
- wave table, but with a maze | |||
- instead of phasor indexing an array, would "index" into | |||
a path along the maze graph | |||
- could have breath first and depth first paths as options | |||
- use DAG style mazes (one and only one path out, no loops or cycles) | |||
- though cycles in maze could be analog for loops in waveform playback | |||
- maybe something hilbert curve esque? | |||
- scmhidtt trigger / pulse generator banks | |||
- just ui to a set of SchmittTriggers | |||
- each strip | |||
- input | |||
- attentuator for process()'ed value | |||
- output trigger | |||
- for generator strip | |||
- trigger input | |||
- knob/cv in/param for duration | |||
- gate output | |||
- Matrix clock | |||
- N x N matrix | |||
- each row defaults to a clock division | |||
- each column defaults to clock multiply | |||
- 0,0 defaults to 1x1 quarter note for tempo | |||
- rows default to 1,2,3,4,6,8,12,16, division | |||
- ditto for columns multi | |||
- but row/columns mult/div could be changed to anything | |||
- XY pad matrix mixer | |||
- matrix mixer with a JL-Modules/kaosspad/reactor style XY pad | |||
at each cross point | |||
- 1,1 -> top output and right output | |||
- 1,0 -> just top output | |||
- -1,-1 -> inverted output to top and right output | |||
- Matrix Mixer with multiple scenes that morph/interpolate | |||
- more or less like Bidoo ACNE except values morph | |||
between scenes insted of | |||
- 2.5 dimension matrix / mixer | |||
- 8x8 matrix but with an output at | |||
each crossing point | |||
- possibly with knob/param for output level | |||
of each output | |||
- maybe a per cross mute/solo | |||
- Much like the movies, the first one was the best. | |||
- Notes module with CV/trigger control and multiple messages | |||
- like a plain notes module, but can react to input from | |||
patch | |||
- use case would be describing stages of a generative patch | |||
- or for adding info to screencast videos | |||
- Gate Divider | |||
- like gate length, but instead of generating one gate, it | |||
generates multiple gates that add up to gate length | |||
- how to seperate? | |||
- some fixed inbetween interval ("kerf") | |||
- divide into on and off gates | |||
- divide a 1s gate by 1 -> one .5s gate then a .5s 'rest' | |||
(and vice versa / inverse 10 or 01) | |||
- divide by 3 -> one .3333s gate, one .3333s rest, one .3333 gate | |||
( ie, 101 or 010) | |||
- divide by 4 -> .25 gate, .25 rest, .25 gate, .25rest | |||
(1010, 0101) | |||
- divide by 5 -> .20 on, .20 off, .20 on, .20 off, .20 on | |||
(10101, 01010) | |||
- kind of similar to Walsh codes | |||
" Experimental Electronic Music Devices Employing Walsh Functions", | |||
"BERNARD A. HUTCHINS, JR" | |||
"JOURNAL OF THE AUDIO ENGINEERING SOCIETY, October 1973" | |||
(https://pdfs.semanticscholar.org/6b9c/30f94551aaf05f077aa491f31b196898cd90.pdf) | |||
- "Sequency" - theres a module name if I ever heard one. |