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