@@ -0,0 +1 @@ | |||
Subproject commit 4a61ab9ce4b3690a7491a817cbadb79e3e14cb7f |
@@ -1,4 +0,0 @@ | |||
build/ | |||
dist/ | |||
.DS_Store | |||
plugin.dylib |
@@ -1,27 +0,0 @@ | |||
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. | |||
@@ -1,32 +0,0 @@ | |||
SLUG = Alikins | |||
VERSION = 0.6.0dev | |||
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../../dep/include -i../../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 |
@@ -1,100 +0,0 @@ | |||
# 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. | |||
#### 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. | |||
### MomentaryOnButtons | |||
A set of GUI buttons that send a +10V output while pressed. | |||
## License | |||
BSD 3-clause. See LICENSE.txt |
@@ -1,115 +0,0 @@ | |||
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 | |||
@@ -1,151 +0,0 @@ | |||
<?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> |
@@ -1,311 +0,0 @@ | |||
#include <stdio.h> | |||
#include <sstream> | |||
#include <iomanip> | |||
#include "alikins.hpp" | |||
#include "dsp/digital.hpp" | |||
// #include "util.hpp" | |||
/* 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, | |||
NUM_INPUTS | |||
}; | |||
enum OutputIds { | |||
IDLE_GATE_OUTPUT, | |||
TIME_OUTPUT, | |||
IDLE_START_OUTPUT, | |||
IDLE_END_OUTPUT, | |||
FRAME_COUNT_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; | |||
} else { | |||
idleGateOutput = 0.0; | |||
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 MsDisplayWidget : TransparentWidget { | |||
int *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, 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, 30.0), Port::INPUT, module, IdleSwitch::INPUT_SOURCE_INPUT)); | |||
addInput(Port::create<PJ301MPort>(Vec(37, 70.0), Port::INPUT, module, IdleSwitch::HEARTBEAT_INPUT)); | |||
addInput(Port::create<PJ301MPort>(Vec(70, 70.0), Port::INPUT, module, IdleSwitch::PULSE_INPUT)); | |||
// idle time display | |||
// FIXME: handle large IdleTimeoutMs (> 99999ms) better | |||
MsDisplayWidget *idle_time_display = new MsDisplayWidget(); | |||
idle_time_display->box.pos = Vec(20, 130); | |||
idle_time_display->box.size = Vec(70, 24); | |||
idle_time_display->value = &module->idleTimeoutMS; | |||
addChild(idle_time_display); | |||
addInput(Port::create<PJ301MPort>(Vec(10, 165.0), Port::INPUT, module, IdleSwitch::TIME_INPUT)); | |||
addParam(ParamWidget::create<Davies1900hBlackKnob>(Vec(38.86, 160.0), module, IdleSwitch::TIME_PARAM, 0.0, 10.0, 0.25)); | |||
addOutput(Port::create<PJ301MPort>(Vec(80, 165.0), Port::OUTPUT, module, IdleSwitch::TIME_OUTPUT)); | |||
MsDisplayWidget *time_remaining_display = new MsDisplayWidget(); | |||
time_remaining_display->box.pos = Vec(20, 235); | |||
time_remaining_display->box.size = Vec(70, 24); | |||
time_remaining_display->value = &module->idleTimeLeftMS; | |||
addChild(time_remaining_display); | |||
addOutput(Port::create<PJ301MPort>(Vec(10, 295.0), Port::OUTPUT, module, IdleSwitch::IDLE_START_OUTPUT)); | |||
addOutput(Port::create<PJ301MPort>(Vec(47.5, 295.0), Port::OUTPUT, module, IdleSwitch::IDLE_GATE_OUTPUT)); | |||
addOutput(Port::create<PJ301MPort>(Vec(85, 295.0), Port::OUTPUT, module, IdleSwitch::IDLE_END_OUTPUT)); | |||
} | |||
Model *modelIdleSwitch = Model::create<IdleSwitch, IdleSwitchWidget>( | |||
"Alikins", "IdleSwitch", "Idle Switch", SWITCH_TAG , UTILITY_TAG); |
@@ -1,143 +0,0 @@ | |||
#include <stdio.h> | |||
#include "alikins.hpp" | |||
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)); | |||
} | |||
} | |||
Model *modelMomentaryOnButtons = Model::create<MomentaryOnButtons, MomentaryOnButtonsWidget>( | |||
"Alikins", "MomentaryOnButtons", "Momentary On Buttons", UTILITY_TAG); |
@@ -1,19 +0,0 @@ | |||
#include "alikins.hpp" | |||
// The plugin-wide instance of the Plugin class | |||
Plugin *plugin; | |||
void init(rack::Plugin *p) { | |||
plugin = p; | |||
// This is the unique identifier for your plugin | |||
p->slug = TOSTRING(SLUG); | |||
p->version = TOSTRING(VERSION); | |||
p->website = "https://github.com/alikins/Alikins-rack-plugins"; | |||
p->manual = "https://github.com/alikins/Alikins-rack-plugins/blob/master/README.md"; | |||
p->addModel(modelIdleSwitch); | |||
p->addModel(modelMomentaryOnButtons); | |||
// Any other plugin initialization may go here. | |||
// As an alternative, consider lazy-loading assets and lookup tables when your module is created to reduce startup times of Rack. | |||
} |
@@ -1,15 +0,0 @@ | |||
#include "rack.hpp" | |||
const int MOMENTARY_BUTTONS = 13; | |||
const int INPUT_SOURCES = 1; | |||
using namespace rack; | |||
extern Plugin *plugin; | |||
//////////////////// | |||
// module widgets | |||
//////////////////// | |||
extern Model *modelIdleSwitch; | |||
extern Model *modelMomentaryOnButtons; |