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