@@ -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; |