diff --git a/CHANGELOG.md b/CHANGELOG.md index a79d0444..65837680 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,36 @@ # Changelog -## v2.NEXT +## v3.0 + +- **NEW**: grid integration / grid visualizer / grid control mode +- **NEW**: multiline copy/paste and editing +- **NEW**: new keybindings to move by words +- **NEW**: undo in script editing +- **NEW**: i2c support for ER-301 +- **NEW**: i2c support for 16n Faderbank +- **NEW**: i2c support for Matrixarchate +- **NEW**: i2c support for W/ +- **NEW**: new op: ? +- **NEW**: new ops: P.MIN, PN.MIN, P.MAX, PN.MAX, P.RND, PN.RND, P.+, PN.+, P.-, PN.-. P.+W, PN.+W, P.-W, PN.-W +- **NEW**: new Telex ops: TO.CV.CALIB, TO.ENV +- **NEW**: new Kria ops: KR.CV, KR.MUTE, KR.TMUTE, KR.CLK, ME.CV +- **NEW**: new aliases: $, RND, RRND, WRP, SCL +- **NEW**: telex, ansible, just friends, w/ added to the help screen +- **FIX**: i2c initialization delayed to account for ER-301 bootup +- **FIX**: last screen saved to flash +- **FIX**: knob jitter when loading/saving scenes reduced +- **FIX**: [duplicate commands not added to history](https://github.com/monome/teletype/issues/99) +- **FIX**: `SCALE` precision improved +- **FIX**: `PARAM` set properly when used in the init script +- **FIX**: `PARAM` and `IN` won't reset to 0 after `INIT.DATA` +- **FIX**: [`PN.HERE`, `P.POP`, `PN.POP` will update the tracker screen](https://github.com/monome/teletype/issues/151) +- **FIX**: [`P.RM` was 1-based, now 0-based](https://github.com/monome/teletype/issues/149) +- **FIX**: [`P.RM` / `PN.RM` will not change pattern length if deleting outside of length range](https://github.com/monome/teletype/issues/150) +- **FIX**: [`JI` op fixed](https://llllllll.co/t/teletype-the-ji-op/10553) +- **FIX**: [`TIME` and `LAST` are now 1ms accurate](https://github.com/monome/teletype/issues/144) +- **FIX**: [`RAND` / `RRAND` will properly work with large range values](https://github.com/monome/teletype/issues/143) +- **FIX**: [`L .. 32767` won't freeze](https://github.com/monome/teletype/issues/148) - **FIX**: I now accessible to child SCRIPTS -- **NEW**: new ops for Ansible: KR.CV, KR.MUTE, KR.TMUTE, KR.CLK, ME.CV -- **NEW**: new ops for W/: WS.PLAY, WS.REC, WS.LOOP, WS.CUE ## v2.2 - **NEW**: added a cheat sheet PDF diff --git a/docs/advanced.md b/docs/advanced.md index a453926f..6c360d71 100644 --- a/docs/advanced.md +++ b/docs/advanced.md @@ -73,7 +73,7 @@ IF X: CV 1 N 60; TR.P 1 IF Y: TR.P 1; TR.P 2; TR.P 3 ``` -Sub commands can also be used with `L`, though due to (current) limitations on how many separate numbers, `OP`s and `MOD`s are allowed in a single command this can be tricky (even if you can fit the text on a line). +Sub commands can also be used with `L`. ## Aliases @@ -110,7 +110,7 @@ Aliases are entirely optional, most `OP`s do not have aliases. Consult the `OP` ## Avoiding non-determinism -Although happy accidents in the modular world are one of it's many joys, when writing computer programs they can be incredibly frustrating. Here are some small tips to help keep things predictable (when you want to them to be): +Although happy accidents in the modular world are one of it's many joys, when writing computer programs they can be incredibly frustrating. Here are some small tips to help keep things predictable (when you want them to be): 1. **Don't use variables unless you need to.** @@ -127,3 +127,51 @@ Although happy accidents in the modular world are one of it's many joys, when wr 4. **Avoid using `A`, `B`, `C` and `D` to refer to the trigger outputs, instead use the numerical values directly.** As `A-D` are variables, they may no longer contain the values `1-4`, and while this was the recommend way to name triggers, it is no longer consider ideal. Newer versions of the Teletype hardware have replaced the labels on the trigger outputs, with the numbers `1` to `4`. + +## Grid integration + +Grid integration can be described very simply: it allows you to use grid with +teletype. However, there is more to it than just that. You can create custom +grid interfaces that can be tailored individually for each scene. Since it's +done with scripts you can dynamically change these interfaces at any point - +you could even create a dynamic interface that reacts to the scene itself or +incoming triggers or control voltages. + +You can simply use grid as an LED display to visualize your scene. Or make it +into an earthsea style keyboard. You can create sequencers, or control surfaces +to control other sequencers. The grid operators simplify building very complex +interfaces, while something simple like a bank of faders can be done with just +two lines of scripts. + +Grid integration consists of 3 main features: grid operators, Grid Visualizer, +and Grid Control mode. Grid operators allow you to draw on grid or create grid +controls, such as buttons and faders, that can trigger scripts when pressed. +As with any other operators you can execute them in Live screen or use them in +any of your scripts. + +Grid Visualizer provides a virtual grid within the Teletype itself: + +![Grid Visualizer](img/gridvisualizer.jpg) + +It can be very useful while developing a script as you don't have to switch +between the grid and the keyboard as often. To turn it on navigate to Live +screen and press `Alt-G` (press again to switch to Full View / turn it off). +You can also emulate button presses, which means it can even be used as an +alternative to grid if you don't have one, especially in full mode - try it +with one of the many [grid scenes](https://github.com/scanner-darkly/teletype/wiki/CODE-EXCHANGE) +already developed. For more information on how to use it please refer to +[the Grid Visualizer documentation](https://github.com/scanner-darkly/teletype/wiki/GRID-VISUALIZER). + +Grid Control Mode is a built in grid interface that allows you to use grid to +trigger and mute scripts, edit variables and tracker values, save and load +scenes, and more. It's available in addition to whatever grid interface you +develop - simply press the front panel button while the grid is attached. It can +serve as a simple way to use grid to control any scene even without using grid +ops, but it can also be very helpful when used together with a scripted grid +interface. For more information and diagrams please refer to +[the Grid Control documentation](https://github.com/scanner-darkly/teletype/wiki/GRID-CONTROL-MODE), + +If you do want to try and build your own grid interfaces +[the Grid Studies](https://github.com/scanner-darkly/teletype/wiki/GRID-INTEGRATION) +is the best place to start. + diff --git a/docs/img/gridvisualizer.jpg b/docs/img/gridvisualizer.jpg new file mode 100644 index 00000000..df49b3a1 Binary files /dev/null and b/docs/img/gridvisualizer.jpg differ diff --git a/docs/intro.md b/docs/intro.md index 1a1b3686..03f7ad7b 100644 --- a/docs/intro.md +++ b/docs/intro.md @@ -3,8 +3,7 @@ Teletype is a dynamic, musical event triggering platform. * [Teletype Studies](https://monome.org/docs/modular/teletype/studies-1) - guided series of tutorials -* [PDF command reference chart](https://monome.org/docs/modular/teletype/TT_commands_2.1.pdf) -— [PDF key reference chart](https://monome.org/docs/modular/teletype/TT_keys_card_1.3.pdf) +* [PDF command reference chart](https://monome.org/docs/modular/teletype/TT_commands_3.0.pdf) — [PDF scene recall sheet](https://monome.org/docs/modular/teletype/TT_scene_RECALL_sheet.pdf) — [Default scenes](http://monome.org/docs/modular/teletype/scenes-1.0/) diff --git a/docs/keys.md b/docs/keys.md index eb7e18d3..6ad0e131 100644 --- a/docs/keys.md +++ b/docs/keys.md @@ -10,7 +10,7 @@ These bindings work everywhere. | `` | preset read mode, or return to last mode | | `alt-` | preset write mode | | `win-` | clear delays, stack and slews | -| `shift-alt-?` | help text, or return to last mode | +| `shift-alt-?` / `alt-h` | help text, or return to last mode | | `` to `` | run corresponding script | | `` | run metro script | | `` | run init script | @@ -33,6 +33,8 @@ In most cases, the clipboard is shared between _live_, _edit_ and the 2 _preset_ |--------------------------------|-----------------------------------------| | `` / `ctrl-b` | move cursor left | | `` / `ctrl-f` | move cursor right | +| `ctrl-` | move left by one word | +| `ctrl-` | move right by one word | | `` / `ctrl-a` | move to beginning of line | | `` / `ctrl-e` | move to end of line | | `` / `ctrl-h` | backwards delete one character | @@ -46,16 +48,27 @@ In most cases, the clipboard is shared between _live_, _edit_ and the 2 _preset_ ## Live mode -| Key | Action | -|------------------|---------------------| -| `` / `C-n` | history next | -| `` / `C-p` | history previous | -| `` | execute command | -| `~` | toggle variables | -| `[` / `]` | switch to edit mode | +| Key | Action | +|----------------------|--------------------------| +| `` / `C-n` | history next | +| `` / `C-p` | history previous | +| `` | execute command | +| `~` | toggle variables | +| `[` / `]` | switch to edit mode | +| `alt-g` | toggle grid visualizer | +| `alt-` | move grid cursor | +| `alt-shift-` | select grid area | +| `alt-` | emulate grid press | +| `alt-/` | switch grid pages | +| `alt-\` | toggle grid control view | +| `alt-` | insert grid x/y/w/h | + +In full grid visualizer mode pressing `alt` is not required. ## Edit mode +In _edit_ mode multiple lines can be selected and used with the clipboard. + | Key | Action | |--------------------|---------------------------| | `` / `C-n` | line down | @@ -65,6 +78,12 @@ In most cases, the clipboard is shared between _live_, _edit_ and the 2 _preset_ | `` | enter command | | `shift-` | insert command | | `alt-/` | toggle line comment | +| `shift-` | expand selection up | +| `shift-` | expand selection down | +| `alt-` | delete selection | +| `alt-` | move selection up | +| `alt-` | move selection down | +| `ctrl-z` | undo (3 levels) | ## Tracker mode @@ -100,6 +119,8 @@ The tracker mode clipboard is independent of text and code clipboard. | `` | toggle non-zero to zero, and zero to 1 | | `0` to `9` | numeric entry | | `shift-2` (`@`) | toggle turtle display marker (`<`) | +| `ctrl-alt` | insert knob value scaled to 0..31 | +| `ctrl-shift` | insert knob value scaled to 0..1023 | ## Preset read mode diff --git a/docs/ops/controlflow.toml b/docs/ops/controlflow.toml index 89afca56..c39ac63a 100644 --- a/docs/ops/controlflow.toml +++ b/docs/ops/controlflow.toml @@ -159,6 +159,7 @@ short = "potentially execute command with probability `x` (0-100)" [SCRIPT] prototype = "SCRIPT" prototype_set = "SCRIPT x" +aliases = ["$"] short = "get current script number, or execute script `x` (1-8), recursion allowed" description = """ Execute script `x` (1-8), recursion allowed. @@ -209,7 +210,8 @@ prototype = "INIT.DATA" short = "clears all data held in all variables" description = """ -Clears the following variables and resets them to default values: A, B, C, D, CV slew, Drunk min/max, M, O, Q, R, T, TR, CV input, and the Paramter knob +Clears the following variables and resets them to default values: A, B, C, D, CV slew, Drunk min/max, M, O, Q, R, T, TR. +Does not affect the CV input (IN) or the Parameter knob (PARAM) values. """ ["INIT.P"] diff --git a/docs/ops/er301.md b/docs/ops/er301.md new file mode 100644 index 00000000..bb66d21f --- /dev/null +++ b/docs/ops/er301.md @@ -0,0 +1,3 @@ +## Orthogonal Devices ER-301 Sound Computer + +The ER-301 is a voltage-controllable canvas for digital signal processing algorithms available from Orthogonal Devices. It can communicate with the Teletype to send up to 100 triggers and 100 CV values per device. Up to three devices are software-selectable and correlate to outputs up to 300. \ No newline at end of file diff --git a/docs/ops/er301.toml b/docs/ops/er301.toml new file mode 100644 index 00000000..12d27885 --- /dev/null +++ b/docs/ops/er301.toml @@ -0,0 +1,36 @@ +["SC.TR"] +prototype = "SC.TR x y" +short = "Set trigger output for the ER-301 virtual output x to y (0-1)" + +["SC.TR.POL"] +prototype = "SC.TR.POL x y" +short = "Set polarity of trigger for the ER-301 virtual output x to y (0-1)" + +["SC.TR.TIME"] +prototype = "SC.TR.TIME x y" +short = "Set the pulse time for the ER-301 virtual trigger `x` to `y` in ms" + +["SC.TR.TOG"] +prototype = "SC.TR.TOG x" +short = "Flip the state for the ER-301 virtual trigger output `x`" + +["SC.TR.PULSE"] +prototype = "SC.TR.PULSE x" +aliases = ["SC.TR.P"] +short = "Pulse the ER-301 virtual trigger output `x`" + +["SC.CV"] +prototype = "SC.CV x y" +short = "CV target value for the ER-301 virtual output `x` to value `y`" + +["SC.CV.OFF"] +prototype = "SC.CV.OFF x y" +short = "CV offset added to the ER-301 virtual output `x`" + +["SC.CV.SET"] +prototype = "SC.CV.SET x" +short = "Set CV value for the ER-301 virtual output `x`" + +["SC.CV.SLEW"] +prototype = "SC.CV.SLEW x y" +short = "Set the CV slew time for the ER-301 virtual output `x` in ms" \ No newline at end of file diff --git a/docs/ops/fader.md b/docs/ops/fader.md new file mode 100644 index 00000000..b07fb237 --- /dev/null +++ b/docs/ops/fader.md @@ -0,0 +1,3 @@ +## 16n Faderbank + +The 16n Faderbank is an open-source controller that can be polled by the Teletype to read the positions of its 16 sliders. \ No newline at end of file diff --git a/docs/ops/fader.toml b/docs/ops/fader.toml new file mode 100644 index 00000000..5899732d --- /dev/null +++ b/docs/ops/fader.toml @@ -0,0 +1,4 @@ +["FADER"] +prototype = "FADER x" +aliases = ["FB"] +short = "reads the value of the `FADER` slider `x`; default return range is from 0 to 16383" \ No newline at end of file diff --git a/docs/ops/grid.md b/docs/ops/grid.md new file mode 100644 index 00000000..4f0e5c02 --- /dev/null +++ b/docs/ops/grid.md @@ -0,0 +1,38 @@ +## Grid +Grid operators allow creating scenes that can interact with grid connected to +teletype (important: grid must be powered externally, do not connect it directly +to teletype!). You can light up individual LEDs, draw shapes and create controls +(such as buttons and faders) that can be used to trigger and control scripts. +You can take advantage of grid operators even without an actual grid by using +the built in Grid Visualizer. + +For more information on grid integration see Advanced section and +[Grid Studies](https://github.com/scanner-darkly/teletype/wiki/GRID-INTEGRATION). + +As there are many operators let's review some naming conventions that apply to +the majority of them. All grid ops start with `G.`. For control related ops this +is followed by 3 letters specifying the control: `G.BTN` for buttons, `G.FDR` +for faders. To define a control you use the main ops `G.BTN` and `G.FDR`. To +define multiple controls replace the last letter with `X`: `G.BTX`, `G.FDX`. + +All ops that initialize controls use the same list of parameters: id, +coordinates, width, height, type, level, script. When creating multiple controls +there are two extra parameters: the number of columns and the number of rows. +Controls are created in the current group (set with `G.GRP`). To specify a +different group use the group versions of the 4 above ops - `G.GBT`, `G.GFD`, +`G.GBX`, `G.GFX` and specify the desired group as the first parameter. + +All controls share some common properties, referenced by adding a `.` and: + +* `EN`: `G.BTN.EN`, `G.FDR.EN` - enables or disables a control +* `V`: `G.BTN.V`, `G.FDR.V` - value, 1/0 for buttons, range value for faders +* `L`: `G.BTN.L`, `G.FDR.L` - level (brightness level for buttons and coarse faders, max value level for fine faders) +* `X`: `G.BTN.X`, `G.FDR.X` - the X coordinate +* `Y`: `G.BTN.Y`, `G.FDR.Y` - the Y coordinate + +To get/set properties for individual controls you normally specify the control +id as the first parameter: `G.FDR.V 5` will return the value of fader 5. Quite +often the actual id is not important, you just want to work with the latest +control pressed. As these are likely the ops to be used most often they are +offered as shortcuts without a `.`: `G.BTNV` returns the value of the last +button pressed, `G.FDRL 4` will set the level of the last fader pressed etc etc. diff --git a/docs/ops/grid.toml b/docs/ops/grid.toml new file mode 100644 index 00000000..6619c957 --- /dev/null +++ b/docs/ops/grid.toml @@ -0,0 +1,633 @@ +["G.RST"] +prototype = "G.RST" +short = "full grid reset" +description = """ +Full grid reset - hide all controls and reset their properties to the default +values, clear all LEDs, reset the dim level and the grid rotation. +""" + +["G.CLR"] +prototype = "G.CLR" +short = "clear all LEDs" +description = """ +Clear all LEDs set with `G.LED`, `G.REC` or `G.RCT`. +""" + +["G.DIM"] +prototype = "G.DIM level" +short = "set dim level" +description = """ +Set the dim level (0..14, higher values dim more). To remove set to 0. +""" + +["G.ROTATE"] +prototype = "G.ROTATE x" +short = "set grid rotation" +description = """ +Set the grid rotation (0 - no rotation, 1 - rotate by 180 degrees). +""" + +["G.KEY"] +prototype = "G.KEY x y action" +short = "emulate grid press" +description = """ +Emulate a grid key press at the specified coordinates (0-based). Set `action` +to 1 to emulate a press, 0 to emulate a release. You can also emulate a button +press with `G.BTN.PR` and a fader press with `G.FDR.PR`. +""" + +["G.GRP"] +prototype = "G.GRP" +prototype_set = "G.GRP id" +short = "get/set current group" +description = """ +Get or set the current group. Grid controls created without specifying a group +will be assigned to the current group. This op doesn't enable/disable groups - +use `G.GRP.EN` for that. The default current group is 0. 64 groups are +available. +""" + +["G.GRP.EN"] +prototype = "G.GRP.EN id" +prototype_set = "G.GRP.EN id x" +short = "enable/disable group or check if enabled" +description = """ +Enable or disable the specified group or check if it's currently enabled. +1 means enabled, 0 means disabled. Enabling or disabling a group enables / +disables all controls assigned to that group (disabled controls are not shown +and receive no input). This allows groups to be used as pages - initialize +controls in different groups, and then simply enable one group at a time. +""" + +["G.GRP.RST"] +prototype = "G.GRP.RST id" +short = "reset all group controls" +description = """ +Reset all controls associated with the specified group. This will disable +the controls and reset their properties to the default values. This will also +reset the fader scale range to 0..16383. +""" + +["G.GRP.SW"] +prototype = "G.GRP.SW id" +short = "switch groups" +description = """ +Switch groups. Enables the specified group, disables all others. +""" + +["G.GRP.SC"] +prototype = "G.GRP.SC id" +prototype_set = "G.GRP.SC id script" +short = "get/set group script" +description = """ +Assign a script to the specified group, or get the currently assigned script. +The script gets executed whenever a control associated with the group receives +input. It is possible to have different scripts assigned to a control and +the group it belongs to. Use 9 for Metro and 10 for Init. To unassign, set it +to 0. +""" + +["G.GRPI"] +prototype = "G.GRPI" +short = "get last group" +description = """ +Get the id of the last group that received input. This is useful when sharing +a script between multiple groups. +""" + +["G.LED"] +prototype = "G.LED x y" +prototype_set = "G.LED x y level" +short = "get/set LED" +description = """ +Set the LED level or get the current level at the specified coordinates. +Possible level range is 0..15 (on non varibright grids anything below 8 is +'off', 8 or above is 'on'). + +Grid controls get rendered first, and LEDs are rendered last. This means you can +use LEDs to accentuate certain areas of the UI. This also means that any LEDs +that are set will block whatever is underneath them, even with the level of 0. +In order to completely clear an LED set its level to -3. There are two other +special values for brightness: -1 will dim, and -2 will brighten what's +underneath. They can be useful to highlight the current sequence step, for +instance. +""" + +["G.LED.C"] +prototype = "G.LED.C x y" +short = "clear LED" +description = """ +Clear the LED at the specified coordinates. This is the same as setting +the brightness level to -3. To clear all LEDs use `G.CLR`. +""" + +["G.REC"] +prototype = "G.REC x y w h fill border" +short = "draw rectangle" +description = """ +Draw a rectangle with the specified width and height. `x` and `y` are +the coordinates of the top left corner. Coordinates are 0-based, with the 0,0 +point located at the top left corner of the grid. You can draw rectangles that +are partially outside of the visible area, and they will be properly cropped. + +`fill` and `border` specify the brightness levels for the inner area and +the one-LED-wide border respectively, 0..15 range. You can use the three special +brightness levels: -1 to dim, -2 to brighten and -3 for transparency (you could +draw just a frame by setting `fill` to -3, for instance). + +To draw lines, set the width or the height to 1. In this case only `border` +brightness level is used. +""" + +["G.RCT"] +prototype = "G.RCT x1 y1 x2 y2 fill border" +short = "draw rectangle" +description = """ +Same as `G.REC` but instead of specifying the width and height you specify +the coordinates of the top left corner and the bottom right corner. +""" + +["G.BTN"] +prototype = "G.BTN id x y w h type level script" +short = "initialize button" +description = """ +Initializes and enables a button with the specified id. 256 buttons are +available (ids are 0-based so the possible id range is 0..255. The button will +be assigned to the current group (set with `G.GRP`). Buttons can be +reinitialized at any point. + +`x` and `y` specify the coordinates of the top left corner, and `w` and `h` +specify width and height respectively. `type` determines whether the button is +latching (1) or momentary (0). `level` sets the "off" brightness level, possible +rand is -3..15 (the brightness level for pressed buttons is fixed at 13). + +`script` specifies the script to be executed when the button is pressed or +released (the latter only for momentary buttons). Use 9 for Metro and 10 for +Init. Use 0 if you don't need a script assigned. +""" + +["G.GBT"] +prototype = "G.GBT group id x y w h type level script" +short = "initialize button in group" +description = """ +Initialize and enable a button. Same as `G.BTN` but you can also choose which +group to assign the button too. +""" + +["G.BTX"] +prototype = "G.BTX id x y w h type level script columns rows" +short = "initialize multiple buttons" +description = """ +Initialize and enable a block of buttons in the current group with the specified +number of columns and rows . Ids are incremented sequentially by columns and +then by rows. +""" + +["G.GBX"] +prototype = "G.GBX group id x y w h type level script columns rows" +short = "initialize multiple buttons in group" +description = """ +Initialize and enable a block of buttons. Same as `G.BTX` but you can also +choose which group to assign the buttons too. +""" + +["G.BTN.EN"] +prototype = "G.BTN.EN id" +prototype_set = "G.BTN.EN id x" +short = "enable/disable button or check if enabled" +description = """ +Enable (set `x` to 1) or disable (set `x` to 0) a button with the specified id, +or check if it's currently enabled. Disabling a button hides it and stops it +from receiving input but keeps all the other properties (size/location etc) +intact. +""" + +["G.BTN.X"] +prototype = "G.BTN.X id" +prototype_set = "G.BTN.X id x" +short = "get/set button x coordinate" +description = """ +Get or set `x` coordinate for the specified button's top left corner. +""" + +["G.BTN.Y"] +prototype = "G.BTN.Y id" +prototype_set = "G.BTN.Y id y" +short = "get/set button y coordinate" +description = """ +Get or set `y` coordinate for the specified button's top left corner. +""" + +["G.BTN.V"] +prototype = "G.BTN.V id" +prototype_set = "G.BTN.V id value" +short = "get/set button value" +description = """ +Get or set the specified button's value. For buttons the value of 1 means +the button is pressed and 0 means it's not. If there is a script assigned +to the button it will not be triggered if you change the value - use +`G.BTN.PR` for that. + +Button values don't change when a button is disabled. Button values are stored +with the scene (both to flash and to USB sticks). +""" + +["G.BTN.L"] +prototype = "G.BTN.L id" +prototype_set = "G.BTN.L id level" +short = "get/set button level" +description = """ +Get or set the specified button's brightness level (-3..15). Please note you +can only set the level for unpressed buttons, the level for pressed buttons is +fixed at 13. +""" + +["G.BTNI"] +prototype = "G.BTNI" +short = "id of last pressed button" +description = """ +Get the id of the last pressed button. This is useful when multiple buttons are +assigned to the same script. +""" + +["G.BTNX"] +prototype = "G.BTNX" +prototype_set = "G.BTNX x" +short = "get/set x of last pressed button" +description = """ +Get or set `x` coordinate of the last pressed button's top left corner. This is +the same as `G.BTN.X G.BTNI`. +""" + +["G.BTNY"] +prototype = "G.BTNY" +prototype_set = "G.BTNY y" +short = "get/set y of last pressed button" +description = """ +Get or set `y` coordinate of the last pressed button's top left corner. This is +the same as `G.BTN.Y G.BTNI`. +""" + +["G.BTNV"] +prototype = "G.BTNV" +prototype_set = "G.BTNV value" +short = "get/set value of last pressed button" +description = """ +Get or set the value of the last pressed button. This is the same as +`G.BTN.V G.BTNI`. This op is especially useful with momentary buttons when you +want to react to presses or releases only - just put `IF EZ G.BTNV: BREAK` in +the beginning of the assigned script (this will ignore releases, to ignore +presses replace `NZ` with `EZ`). +""" + +["G.BTNL"] +prototype = "G.BTNL" +prototype_set = "G.BTNL level" +short = "get/set level of last pressed button" +description = """ +Get or set the brightness level of the last pressed button. This is the same as +`G.BTN.L G.BTNI`. +""" + +["G.BTN.SW"] +prototype = "G.BTN.SW id" +short = "switch button" +description = """ +Set the value of the specified button to 1 (pressed), set it to 0 (not pressed) +for all other buttons within the same group (useful for creating radio buttons). +""" + +["G.BTN.PR"] +prototype = "G.BTN.PR id action" +short = "emulate button press/release" +description = """ +Emulate pressing/releasing the specified button. Set `action` to `1` for press, +`0` for release (`action` is ignored for latching buttons). +""" + +["G.GBTN.V"] +prototype = "G.GBTN.V group value" +short = "set value for group buttons" +description = """ +Set the value for all buttons in the specified group. +""" + +["G.GBTN.L"] +prototype = "G.GBTN.L group odd_level even_level" +short = "set level for group buttons" +description = """ +Set the brightness level (0..15) for all buttons in the specified group. You can +use different values for odd and even buttons (based on their index within the +group, not their id) - this can be a good way to provide some visual guidance. +""" + +["G.GBTN.C"] +prototype = "G.GBTN.C group" +short = "get count of currently pressed" +description = """ +Get the total count of all the buttons in the specified group that are currently +pressed. +""" + +["G.GBTN.I"] +prototype = "G.GBTN.I group index" +short = "get id of pressed button" +description = """ +Get the id of a currently pressed button within the specified group by its index +(0-based). The index should be between 0 and C-1 where C is the total count of +all pressed buttons (you can get it using `G.GBTN.C`). +""" + +["G.GBTN.W"] +prototype = "G.GBTN.W group" +short = "get button block width" +description = """ +Get the width of the rectangle formed by pressed buttons within the specified +group. This is basically the distance between the leftmost and the rightmost +pressed buttons, inclusive. This op is useful for things like setting a loop's +length, for instance. To do so, check if there is more than one button pressed +(using `G.GBTN.C`) and if there is, use `G.GBTN.W` to set the length. +""" + +["G.GBTN.H"] +prototype = "G.GBTN.H group" +short = "get button block height" +description = """ +Get the height of the rectangle formed by pressed buttons within the specified +group (see `G.GBTN.W` for more details). +""" + +["G.GBTN.X1"] +prototype = "G.GBTN.X1 group" +short = "get leftmost pressed x" +description = """ +Get the X coordinate of the leftmost pressed button in the specified group. If +no buttons are currently pressed it will return -1. +""" + +["G.GBTN.X2"] +prototype = "G.GBTN.X2 group" +short = "get rightmost pressed x" +description = """ +Get the X coordinate of the rightmost pressed button in the specified group. If +no buttons are currently pressed it will return -1. +""" + +["G.GBTN.Y1"] +prototype = "G.GBTN.Y1 group" +short = "get highest pressed y" +description = """ +Get the Y coordinate of the highest pressed button in the specified group. If no +buttons are currently pressed it will return -1. +""" + +["G.GBTN.Y2"] +prototype = "G.GBTN.Y2 group" +short = "get lowest pressed y" +description = """ +Get the Y coordinate of the lowest pressed button in the specified group. If no +buttons are currently pressed it will return -1. +""" + +["G.FDR"] +prototype = "G.FDR id x y w h type level script" +short = "initialize fader" +description = """ +Initializes and enables a fader with the specified id. 64 faders are available +(ids are 0-based so the possible id range is 0..63). The fader will be assigned +to the current group (set with `G.GRP`). Faders can be reinitialized at any +point. + +`x` and `y` specify the coordinates of the top left corner, and `w` and `h` +specify width and height respectively. + +`type` determines the fader type and orientation. Possible values are: + +* 0 - coarse, horizontal bar +* 1 - coarse, vertical bar +* 2 - coarse, horizontal dot +* 3 - coarse, vertical dot +* 4 - fine, horizontal bar +* 5 - fine, vertical bar +* 6 - fine, horizontal dot +* 7 - fine, vertical dot + +Coarse faders have the possible range of 0..N-1 where N is width for horizontal +faders or height for vertical faders. Pressing anywhere within the fader area +sets the fader value accordingly. Fine faders allow selecting a bigger range +of values by mapping the range to the fader's height or width and dedicating +the edge buttons for incrementing/decrementing. Fine faders employ +varibrightness to reflect the current value. + +`level` has a different meaning for coarse and fine faders. For coarse faders +it selects the background brightness level (similar to buttons). For fine faders +this is the maximum value level (the minimum level being 0). In order to show +each value distinctly using varibright the maximum level possible is the number +of available buttons multiplied by 16 minus 1 (since range is 0-based). Remember +that 2 buttons are always reserved for increment/decrement. Using a larger +number is allowed - it will be automatically adjusted to what's possible. + +`script` specifies the script to be executed when the fader value is changed. +Use 9 for Metro and 10 for Init. Use 0 if you don't need a script assigned. +""" + +["G.GFD"] +prototype = "G.GFD grp id x y w h type level script" +short = "initialize fader in group" +description = """ +Initialize and enable a fader. Same as `G.FDR` but you can also choose which +group to assign the fader too. +""" + +["G.FDX"] +prototype = "G.FDX id x y w h type level script columns rows" +short = "initialize multiple faders" +description = """ +Initialize and enable a block of faders with the specified number of columns +and rows in the current group. Ids are incremented sequentially by columns and +then by rows. +""" + +["G.GFX"] +prototype = "G.GFX group id x y w h type level script columns rows" +short = "initialize multiple faders in group" +description = """ +Initialize and enable a block of faders. Same as `G.FDX` but you can also +choose which group to assign the faders too. +""" + +["G.FDR.EN"] +prototype = "G.FDR.EN id" +prototype_set = "G.FDR.EN id x" +short = "enable/disable fader or check if enabled" +description = """ +Enable (set `x` to 1) or disable (set `x` to 0) a fader with the specified id, +or check if it's currently enabled. Disabling a fader hides it and stops it +from receiving input but keeps all the other properties (size/location etc) +intact. +""" + +["G.FDR.X"] +prototype = "G.FDR.X id" +prototype_set = "G.FDR.X id x" +short = "get/set fader x coordinate" +description = """ +Get or set `x` coordinate for the specified fader's top left corner. +""" + +["G.FDR.Y"] +prototype = "G.FDR.Y id" +prototype_set = "G.FDR.Y id y" +short = "get/set fader y coordinate" +description = """ +Get or set `y` coordinate for the specified fader's top left corner. +""" + +["G.FDR.N"] +prototype = "G.FDR.N id" +prototype_set = "G.FDR.N id value" +short = "get/set fader value" +description = """ +Get or set the specified fader's value. The possible range for coarse faders is +0..N-1 where N is fader's width (for horizontal faders) or height (for vertical +faders). For fine faders the possible range is 0..N where N is the maximum level +set when the fader was initialized (see `G.FDR` for more details). + +Sometimes it's more convenient to map the possible fader range to a different +range (when using it to control a CV, for instance). Use `G.FDR.V` for that. + +If there is a script assigned to the fader it will not be triggered if you change +the value - use `G.FDR.PR` for that. + +Fader values don't change when a fader is disabled. Fader values are stored +with the scene (both to flash and to USB sticks). +""" + +["G.FDR.V"] +prototype = "G.FDR.V id" +prototype_set = "G.FDR.V id value" +short = "get/set scaled fader value" +description = """ +Get or set the specified fader's value mapped to a range set with `G.GFDR.RN`. +This op is very convenient for using faders to control a known range, such as CV +- simply create a fader and set a range and then assign values directly without +any additional calculations, like this: `CV 1 G.FDR.V 1`. +""" + +["G.FDR.L"] +prototype = "G.FDR.L id" +prototype_set = "G.FDR.L id level" +short = "get/set fader level" +description = """ +Get or set the specified fader's brightness level (for coarse faders), or the +maximum value level (for fine faders). +""" + +["G.FDRI"] +prototype = "G.FDRI" +short = "id of last pressed fader" +description = """ +Get the id of the last pressed fader. This is useful when multiple faders are +assigned to the same script. +""" + +["G.FDRX"] +prototype = "G.FDRX" +prototype_set = "G.FDRX x" +short = "get/set x of last pressed fader" +description = """ +Get or set `x` coordinate of the last pressed fader's top left corner. This is +the same as `G.FDR.X G.FDRI`. +""" + +["G.FDRY"] +prototype = "G.FDRY" +prototype_set = "G.FDRY y" +short = "get/set y of last pressed fader" +description = """ +Get or set `y` coordinate of the last pressed fader's top left corner. This is +the same as `G.BTN.Y G.BTNI`. +""" + +["G.FDRN"] +prototype = "G.FDRN" +prototype_set = "G.FDRN value" +short = "get/set value of last pressed fader" +description = """ +Get or set the value of the last pressed fader. This is the same as +`G.FDR.N G.FDRI`. See `G.FDR.N` for more details. +""" + +["G.FDRV"] +prototype = "G.FDRV" +prototype_set = "G.FDRV value" +short = "get/set scaled value of last pressed fader" +description = """ +Get or set the scaled value of the last pressed fader. This is the same as +`G.FDR.V G.FDRI`. See `G.FDR.V` for more details. +""" + +["G.FDRL"] +prototype = "G.FDRL" +prototype_set = "G.FDRL level" +short = "get/set level of last pressed fader" +description = """ +Get or set the brightness level (for coarse faders), or the maximum value level +(for fine faders) of the last pressed fader. This is the same as +`G.FDR.L G.BTNI`. For more details on levels see `G.FDR`. +""" + +["G.FDR.PR"] +prototype = "G.FDR.PR id value" +short = "emulate fader press" +description = """ +Emulate pressing the specified fader. Fader value will be set to the specified +value, and if there is a script assigned it will be executed. +""" + +["G.GFDR.N"] +prototype = "G.GFDR.N group value" +short = "set value for group faders" +description = """ +Set the value for all faders in the specified group. This can be useful for +resetting all faders in a group. See `G.FDR.N` for more details. +""" + +["G.GFDR.V"] +prototype = "G.GFDR.V group value" +short = "set scaled value for group faders" +description = """ +Set the scaled value for all faders in the specified group. This can be useful +for resetting all faders in a group. See `G.FDR.V` for more details. +""" + +["G.GFDR.L"] +prototype = "G.GFDR.L group odd_level even_level" +short = "set level for group faders" +description = """ +Set the brightness level (0..15) for all faders in the specified group. You can +use different values for odd and even faders (based on their index within the +group, not their id) - this can be a good way to provide some visual guidance. +""" + +["G.GFDR.RN"] +prototype = "G.GFDR.RN group min max" +short = "set range for group faders" +description = """ +Set the range to be used for `V` fader values (`G.FDR.V`, `G.FDRV`, `G.GFDR.V`). +While the `.N` ops provide the actual fader value sometimes it's more convenient +to map it to a different range so it can be used directly for something like +a CV without having to scale it each time. + +An example: let's say you create a coarse fader with the width of 8 which will +be used to control a CV output where the voltage must be in the 2V..5V range. +Using `G.FDR.N` you would need to do this: `CV 1 SCL 0 7 V 2 V 5 G.FDR.N 0`. +Instead you can set the range for scaling once: `G.GFDR.RN 0 V 2 V 5` (assuming +the fader is in group 0) and then simply do `CV 1 G.FDR.V 0`. + +The range is shared by all faders within the same group. If you need to use a +different range use a different group when initializing a fader. + +The default range is 0..16383. `G.RST` and `G.GRP.RST` reset ranges to the +default value. +""" diff --git a/docs/ops/hardware.toml b/docs/ops/hardware.toml index 444ecc60..2c575264 100644 --- a/docs/ops/hardware.toml +++ b/docs/ops/hardware.toml @@ -75,6 +75,10 @@ description = """ 7. Call IN and confirm that the result is 16383 """ +["IN.CAL.RESET"] +prototype = "IN.CAL.RESET" +short = "Resets the input CV calibration" + ["PARAM.CAL.MIN"] prototype = "PARAM.CAL.MIN" short = "Reads the Parameter Knob minimum position and assigns a zero value" @@ -86,13 +90,17 @@ description = """ ["PARAM.CAL.MAX"] prototype = "PARAM.CAL.MAX" -short = "Reads the Paramter Knob maximum position and assigns the maximum point" +short = "Reads the Parameter Knob maximum position and assigns the maximum point" description = """ 4. Turn the knob all the way to the right 5. Execute PARAM.CAL.MAX from the live terminal 6. Call PARAM and verify that the result is 16383 """ +["PARAM.CAL.RESET"] +prototype = "PARAM.CAL.RESET" +short = "Resets the Parameter Knob calibration" + ["TR"] prototype = "TR x" prototype_set = "TR x y" diff --git a/docs/ops/maths.toml b/docs/ops/maths.toml index ceb688bf..08c96483 100644 --- a/docs/ops/maths.toml +++ b/docs/ops/maths.toml @@ -26,16 +26,22 @@ short = "find the remainder after division of `x` by `y`" [RAND] prototype = "RAND x" +aliases = ["RND"] short = "generate a random number between `0` and `x` inclusive" [RRAND] prototype = "RRAND x y" +aliases = ["RRND"] short = "generate a random number between `x` and `y` inclusive" [TOSS] prototype = "TOSS" short = "randomly return `0` or `1`" +["?"] +prototype = "? x y z" +short = "if condition `x` is true return `y`, otherwise return `z`" + [MIN] prototype = "MIN x y" short = "return the minimum of `x` and `y`" @@ -50,6 +56,7 @@ short = "limit the value `x` to the range `y` to `z` inclusive" [WRAP] prototype = "WRAP x y z" +aliases = ["WRP"] short = "limit the value `x` to the range `y` to `z` inclusive, but with wrapping" [QT] @@ -102,12 +109,12 @@ short = "`x` is not `0`" [LSH] prototype = "LSH x y" aliases = ["<<"] -short = "left shift `x` by `y` bits, in effect multiply by `2` to the power of `x`" +short = "left shift `x` by `y` bits, in effect multiply `x` by `2` to the power of `y`" [RSH] prototype = "RSH x y" aliases = [">>"] -short = "right shift `x` by `y` bits, in effect divide by `2` to the power of `x`" +short = "right shift `x` by `y` bits, in effect divide `x` by `2` to the power of `y`" ["|"] prototype = "| x y" @@ -163,6 +170,7 @@ short = "just intonation helper, precision ratio divider normalised to 1V" [SCALE] prototype = "SCALE a b x y i" +aliases = ["SCL"] short = "scale `i` from range `a` to `b` to range `x` to `y`, i.e. `i * (y - x) / (b - a)`" [ER] diff --git a/docs/ops/matrixarchate.md b/docs/ops/matrixarchate.md new file mode 100644 index 00000000..6f088c55 --- /dev/null +++ b/docs/ops/matrixarchate.md @@ -0,0 +1,3 @@ +## Matrixarchate + +The SSSR Labs SM010 Matrixarchate is a 16x8 IO Sequenceable Matrix Signal Router. \ No newline at end of file diff --git a/docs/ops/matrixarchate.toml b/docs/ops/matrixarchate.toml new file mode 100644 index 00000000..d014a70d --- /dev/null +++ b/docs/ops/matrixarchate.toml @@ -0,0 +1,67 @@ +["MA.SELECT"] +prototype = "MA.SELECT x" +short = "select the default matrixarchate module, default `1`" + +["MA.STEP"] +prototype = "MA.STEP" +short = "advance program sequencer" + +["MA.RESET"] +prototype = "MA.RESET" +short = "reset program sequencer" + +["MA.PGM"] +prototype = "MA.PGM pgm" +short = "select the current program (1-based)" + +["MA.ON"] +prototype = "MA.ON x y" +short = "connect row `x` and column `y` in the current program (rows/columns are 0-based)" + +["MA.PON"] +prototype = "MA.PON pgm x y" +short = "connect row `x` and column `y` in program `pgm`" + +["MA.OFF"] +prototype = "MA.OFF x y" +short = "disconnect row `x` and column `y` in the current program" + +["MA.POFF"] +prototype = "MA.POFF x y pgm" +short = "connect row `x` and column `y` in program `pgm`" + +["MA.SET"] +prototype = "MA.SET x y state" +short = "set the connection at row `x` and column `y` to `state` (1 - on, 0 - off)" + +["MA.PSET"] +prototype = "MA.PSET pgm x y state" +short = "set the connection at row `x` and column `y` in program `pgm` to `state` (1 - on, 0 - off)" + +["MA.COL"] +prototype = "MA.COL col" +prototype_set = "MA.COL col value" +short = "get or set column `col` (as a 16 bit unsigned value where each bit represents a connection)" + +["MA.PCOL"] +prototype = "MA.PCOL pgm col" +prototype_set = "MA.PCOL pgm col value" +short = "get or set column `col` in program `pgm`" + +["MA.ROW"] +prototype = "MA.ROW row" +prototype_set = "MA.ROW row value" +short = "get or set row `row`" + +["MA.PROW"] +prototype = "MA.PROW pgm row" +prototype_set = "MA.PROW pgm row value" +short = "get or set row `row` in program `pgm`" + +["MA.CLR"] +prototype = "MA.CLR" +short = "clear all connections" + +["MA.PCLR"] +prototype = "MA.PCLR pgm" +short = "clear all connections in program `pgm`" diff --git a/docs/ops/patterns.md b/docs/ops/patterns.md index 04b1617f..17f46070 100644 --- a/docs/ops/patterns.md +++ b/docs/ops/patterns.md @@ -1,4 +1,4 @@ -##Patterns +## Patterns Patterns facilitate musical data manipulation– lists of numbers that can be used as sequences, chord sets, rhythms, or whatever you choose. Pattern memory consists four banks of 64 steps. Functions are provided for a variety of pattern creation, transformation, and playback. New in teletype 2.0, a second version of all Pattern ops have been added. The original `P` ops (`P`, `P.L`, `P.NEXT`, etc.) act upon the ‘working pattern’ as defined by `P.N`. By default the working pattern is assigned to pattern 0 (`P.N 0`), in order to execute a command on pattern 1 using `P` ops you would need to first reassign the working pattern to pattern 1 (`P.N 1`). diff --git a/docs/ops/patterns.toml b/docs/ops/patterns.toml index cf86308b..d2377037 100644 --- a/docs/ops/patterns.toml +++ b/docs/ops/patterns.toml @@ -184,3 +184,66 @@ short = "return and remove the value from the end of the working pattern (like a ["PN.POP"] prototype = "PN.POP x" short = "return and remove the value from the end of pattern `x` (like a stack), destructive to loop length" + + +["P.MIN"] +prototype = "P.MIN" +short = "find the first minimum value in the pattern between the START and END for the working pattern and return its index" + +["PN.MIN"] +prototype = "PN.MIN x" +short = "find the first minimum value in the pattern between the START and END for pattern `x` and return its index" + + +["P.MAX"] +prototype = "P.MAX" +short = "find the first maximum value in the pattern between the START and END for the working pattern and return its index" + +["PN.MAX"] +prototype = "PN.MAX x" +short = "find the first maximum value in the pattern between the START and END for pattern `x` and return its index" + + +["P.RND"] +prototype = "P.RND" +short = "return a value randomly selected between the start and the end position" + +["PN.RND"] +prototype = "PN.RND x" +short = "return a value randomly selected between the start and the end position of pattern `x`" + + +["P.+"] +prototype = "P.+ x y" +short = "increase the value of the working pattern at index `x` by `y`" + +["PN.+"] +prototype = "PN.+ x y z" +short = "increase the value of pattern `x` at index `y` by `z`" + +["P.-"] +prototype = "P.- x y" +short = "decrease the value of the working pattern at index `x` by `y`" + +["PN.-"] +prototype = "PN.- x y z" +short = "decrease the value of pattern `x` at index `y` by `z`" + +["P.+W"] +prototype = "P.+W x y a b" +short = "increase the value of the working pattern at index `x` by `y` and wrap it to `a`..`b` range" + +["PN.+W"] +prototype = "PN.+W x y z a b" +short = "increase the value of pattern `x` at index `y` by `z` and wrap it to `a`..`b` range" + +["P.-W"] +prototype = "P.-W x y a b" +short = "decrease the value of the working pattern at index `x` by `y` and wrap it to `a`..`b` range" + +["PN.-W"] +prototype = "PN.-W x y z a b" +short = "decrease the value of pattern `x` at index `y` by `z` and wrap it to `a`..`b` range" + + + diff --git a/docs/ops/telex_o.toml b/docs/ops/telex_o.toml index c61a4277..18a423f5 100644 --- a/docs/ops/telex_o.toml +++ b/docs/ops/telex_o.toml @@ -247,6 +247,33 @@ If a curve is too small for the range being covered, values above the range will """ +["TO.CV.CALIB"] +prototype = "TO.CV.CALIB x" +short = "Locks the current offset (`CV.OFF`) as a calibration offset and saves it to persist between power cycles for output `x`." +description = """ + +To calibrate your TXo outputs, follow these steps. Before you start, let your expander warm up for a few minutes. It won't take long - but you want to make sure that it is calibrated at a more representative temperature. + +Then, first adjust your offset (CV.OFF) until the output is at zero volts (0). For example: + +``` +CV.OFF 1 8 +``` + +Once that output measures at zero volts, you want to lock it in as the calibration by calling the following operator: + +``` +CV.CALIB 1 +``` + +You will find that the offset is now zero, but the output is at the value that you targeted during your prior adjustment. To reset to normal (and forget this calibration offset), use the `TO.CV.RESET` command. + +""" + +["TO.CV.RESET"] +prototype = "TO.CV.RESET x" +short = "Clears the calibration offset for output `x`." + ["TO.OSC"] prototype = "TO.OSC x y" short = "targets oscillation for CV output `x` to `y` with the portamento rate determined by the `TO.OSC.SLEW` value; `y` is 1v/oct translated from the standard range (1-16384); a value of `0` disables oscillation; `CV` amplitude is used as the peak for oscillation and needs to be `> 0` for it to be perceivable" @@ -298,7 +325,7 @@ prototype = "TO.OSC.FQ x y" short = "targets oscillation for CV output `x` to frequency `y` with the portamento rate determined by the `TO.OSC.SLEW` value; `y` is in Hz; a value of `0` disables oscillation; `CV` amplitude is used as the peak for oscillation and needs to be `> 0` for it to be perceivable" ["TO.OSC.FQ.SET"] -prototype = "TO.OSC.FQ x y" +prototype = "TO.OSC.FQ.SET x y" short = "sets oscillation for CV output `x` to frequency `y` (ignores `CV.OSC.SLEW`); `y` is in Hz; a value of `0` disables oscillation; `CV` amplitude is used as the peak for oscillation and needs to be `> 0` for it to be perceivable" ["TO.OSC.LFO"] @@ -359,13 +386,14 @@ description = """ ["TO.OSC.WAVE"] prototype = "TO.OSC.WAVE x y" -short = "set the waveform for output `x` to `y`; `y` values range `0-4999`; values translate to sine (0), triangle (1000), saw (2000), pulse (3000), or noise (4000); oscillator shape between values is a blend of the pure waveforms" +short = "set the waveform for output `x` to `y`; `y` values range `0-4500`. There are 45 different waveforms, values translate to sine (0), triangle (100), saw (200), pulse (300) all the way to random/noise (4500); oscillator shape between values is a blend of the pure waveforms" ["TO.OSC.RECT"] prototype = "TO.OSC.RECT x y" short = "rectifies the polarity of the oscillator for output `x` to `y`; range for `y` is -2 to 2; default is 0 (no rectification); 1 & -1 are partial rectification - omitting all values on the other side of the sign; 2 & -2 are full rectification - inverting values from the other pole" description = """ The rectification command performs a couple of levels of rectification based on how you have it set. The following values for `y` work as follows: + * `y = 2`: "full-positive" - inverts negative values, making them positive * `y = 1`: "half-positive" - omits all negative values (values below zero are set to zero) * `y = 0`: no rectification (default) @@ -440,6 +468,10 @@ This will initialize the `CV 1` output to have an envelope that will ramp to `+8 To return your `CV` output to normal function, either deactivate the envelope (`TO.ENV.ACT 1 0`) or reinitialize the output (`TO.CV.INIT 1`). """ +["TO.ENV"] +prototype = "TO.ENV x y" +short = "This parameter essentially allows output `x` to act as a gate between the 0 and 1 state. Changing this value from 0 to 1 causes the envelope to trigger the attack phase and hold at the peak CV value; changing this value from 1 to 0 causes the decay stage of the envelope to be triggered." + ["TO.ENV.TRIG"] prototype = "TO.ENV.TRIG x" short = "triggers the envelope at `CV` output `x` to cycle; `CV` amplitude is used as the peak for the envelope and needs to be `> 0` for the envelope to be perceivable" diff --git a/docs/ops/variables.toml b/docs/ops/variables.toml index 7f809802..c79468ed 100644 --- a/docs/ops/variables.toml +++ b/docs/ops/variables.toml @@ -57,9 +57,14 @@ short = "returns inverted state (`0` or `1`) on each read (also settable)" [I] prototype = "I" prototype_set = "I x" -short = """ -get / set the variable `I`, this variable is overwritten by `L`, but can be used -freely outside an `L` loop""" +short = """get / set the variable `I`""" +description=""" +Get / set the variable `I`, this variable is overwritten by `L`, but can be used +freely outside an `L` loop. Each script gets its own `I` variable, so if you call +a script from another script's loop you can still use and modify `I` without +affecting the calling loop. In this scenario the script getting called will have +its `I` value initialized with the calling loop's current `I` value. +""" [O] prototype = "O" diff --git a/docs/ops/wslash.md b/docs/ops/wslash.md index b89078b1..cdf16764 100644 --- a/docs/ops/wslash.md +++ b/docs/ops/wslash.md @@ -1,4 +1,4 @@ -W/ +## W/ -More extensively covered in the [W/ Documentation](https://www.whimsicalraps.com/pages/withtt). +More extensively covered in the [W/ Documentation](https://www.whimsicalraps.com/pages/w-type). diff --git a/docs/quickstart.md b/docs/quickstart.md index a793caba..be9067c8 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -147,6 +147,8 @@ The edit position is indicated by the brightest number. Very dim numbers indicat Use the square bracket keys `[` and `]` to decrease/increase the values. Backspace sets the value to 0. Entering numbers will overwrite a new value. You can cut/copy/paste with ALT-X-C-V. +Check the *Keys* section for a complete list of tracker shortcuts. + ## Scenes @@ -168,6 +170,24 @@ To facilitate performance without the need for the keyboard, scenes can be recal The *INIT* script (represented as `I`) is executed when a preset is recalled. This is a good place to set initial values of variables if needed, like metro time `M` or time enable `TIME.ACT` for example. +## USB Backup + +Teletype's scenes can be saved and loaded from a USB flash drive. When a flash +drive is inserted, Teletype will recognize it and go into disk mode. First, +all 32 scenes will be written to text files on the drive with names of the form `tt##s.txt`. For example, scene 5 will be saved to `tt05s.txt`. The screen will display `WRITE.......` as this is done. + +Once complete, Teletype will attempt to read any files named `tt##.txt` and load them into +memory. For example, a file named `tt13.txt` would be loaded as scene 13 on +Teletype. The screen will display `READ......` Once this process is complete, Teletype will return to LIVE mode and the drive can be safely removed. + +For best results, use an FAT-formatted USB flash drive. If Teletype does not +recognize a disk that is inserted within a few seconds, it may be best to try another. + +An +example of possible scenes to load, as well as the set of factory default scenes, can be +found at the [Teletype +Codex](https://github.com/monome-community/teletype-codex). + ## Commands ### Nomenclature diff --git a/docs/whats_new.md b/docs/whats_new.md index 3b51ceec..ad127078 100644 --- a/docs/whats_new.md +++ b/docs/whats_new.md @@ -1,5 +1,129 @@ # What's new? +## Version 3.0 + +### Major new features + +#### Grid Integration + +Grid integration allows you to use grid to visualize, control and execute teletype scripts. You can create your own UIs using grid ops, or control Teletype directly with the Grid Control mode. Built in Grid Visualizer allows designing and using grid scenes without a grid. For more information and examples of grid scenes please see the [Grid Studies](https://github.com/scanner-darkly/teletype/wiki/GRID-INTEGRATION). + +#### Improved script editing + +You can now select multiple lines when editing scripts by holding `shift`. You can move the current selection up and down with `alt-` and `alt-`. You can copy/cut/paste a multiline selection as well. To delete selected lines without copying into the clipboard use `alt-`. + +Three level undo is also now available with `ctrl-z` shortcut. + +#### Support for the Orthogonal Devices ER-301 Sound Computer over i2c + +You now can connect up to three ER-301s via i2c and address up to 100 virtual CV channels and 100 virtual TR channels per ER-301. (The outputs range 1-100, 101-200, and 201-300 respectively.) To function, this requires a slight mod to current in-market ER-301s and a specialized i2c cable that reorders two of the pins. Find more information [on the Orthogonal Devices ER-301 Wiki Teletype Integration Page](http://wiki.orthogonaldevices.com/index.php/ER-301/Teletype_Integration). + +#### Support for the 16n Faderbank via i2c + +The 16n Faderbank is an open-source sixteen fader controller with support for USB MIDI, standard MIDI, and i2c communication with the Teletype. It operates just like an IN or PARAM (or the TXi for that matter) in that you read values from the device. You use the operator FADER (or the alias FB) and the number of the slider you wish to poll (1-16). Know that longer cables may require that you use a powered bus board even if you only have one device on your Teletype's i2c bus. (You will know that you have a problem if your Teletype randomly hangs on reads.) + +#### Support for the SSSR Labs SM010 Matrixarchate via i2c + +The SSSR Labs SM010 Matrixarchate is a 16x8 IO Sequenceable Matrix Signal Router. Teletype integration allows you to switch programs and control connections. For a complete list of available ops refer to the manual. Information on how to connect the module can be found [in the SM010 manual](https://www.sssrlabs.com/store/sm010/). + +#### Support for W/ via i2c + +Support for controlling Whimsical Raps W/ module via i2c. See the respective section for a complete list of available ops and refer to https://www.whimsicalraps.com/pages/w-type for more details. + +### New operators + +`? x y z` is a ternary "if" operator, it will select between `y` and `z` based on the condition `x`. + +#### New pattern ops + +`P.MIN` `PN.MIN` `P.MAX` `PN.MAX` return the position for the first smallest/largest value in a pattern between the `START` and `END` points. + +`P.RND` / `PN.RND` return a randomly selected value in a pattern between the `START` and `END` points. + +`P.+` / `PN.+` / `P.-` / `PN.-` increment/decrement a pattern value by the specified amount. + +`P.+W` / `PN.+W` / `P.-W` / `PN.-W` same as above and wrap to the specified range. + +#### New Telex ops + +`TO.CV.CALIB` allows you to lock-in an offset across power cycles to calibrate your TELEX CV output (`TO.CV.RESET` removes the calibration). + +`TO.ENV` now accepts gate values (1/0) to trigger the attack and decay. + +#### New Kria ops + +`KR.CV x` get the current CV value for channel `x` + +`KR.MUTE x` `KR.MUTE x y` get/set mute state for channel `x` + +`KR.TMUTE x` toggle mute state for channel `x` + +`KR.CLK x` advance the clock for channel `x` + +#### Ops for ER-301, 16n Faderbank, SM010, W/ + +Too many to list, please refer to their respective sections. + +### New aliases + +`$` for `SCRIPT` + +`RND` / `RRND` `RAND` / `RRAND` + +`WRP` for `WRAP` + +`SCL` for `SCALE` + +### New keybindings + +Hold `shift` while making line selection in script editing to select multiple lines. Use `alt-` and `alt-` to move selected lines up and down. Copy/cut/paste shortcuts work with multiline selection as well. To delete selected lines without copying into the clipboard use `alt-`. + +While editing a line you can now use `ctrl-` / `ctrl-` to move by words. + +`ctrl-z` provides three level undo in script editing. + +Additional `Alt-H` shortcut is available to view the Help screen. + +`Alt-G` in Live mode will turn on the Grid Visualizer, which has its own shortcuts. Refer to the **Keys** section for a complete list. + +The keybindings to insert a scaled knob value in the Tracker mode were changed from `ctrl` to `ctrl-alt` and from `shift` to `ctrl-shift`. + +### Bug fixes + +i2c initialization delayed to account for ER-301 bootup + +last screen saved to flash + +knob jitter when loading/saving scenes reduced + +[duplicate commands not added to history](https://github.com/monome/teletype/issues/99) + +`SCALE` precision improved + +`PARAM` set properly when used in the init script + +`PARAM` and `IN` won't reset to 0 after `INIT.DATA` + +[`PN.HERE`, `P.POP`, `PN.POP` will update the tracker screen](https://github.com/monome/teletype/issues/151) + +[`P.RM` was 1-based, now 0-based](https://github.com/monome/teletype/issues/149) + +[`P.RM` / `PN.RM` will not change pattern length if deleting outside of length range](https://github.com/monome/teletype/issues/150) + +[`JI` op fixed](https://llllllll.co/t/teletype-the-ji-op/10553) + +[`TIME` and `LAST` are now 1ms accurate](https://github.com/monome/teletype/issues/144) + +[`RAND` / `RRAND` will properly work with large range values](https://github.com/monome/teletype/issues/143) + +[`L .. 32767` won't freeze](https://github.com/monome/teletype/issues/148) + +### New behavior + +Previously, when pasting the clipboard while in script editing the pasted line would replace the current line. It will now instead push the current line down. This might result in some lines being pushed beyond the script limits - if this happens, use `ctrl-z` to undo the change, delete some lines and then paste again. + +`I` would previously get initialized to 0 when executing a script. If you called a script from another script's loop this meant you had to use a variable to pass the loop's current `I` value to the called script. This is not needed anymore - when a script is called from another script its `I` value will be set to the current `I` value of the calling script. + ## Version 2.2 Teletype version 2.2 introduces Chaos and Bitwise operators, Live mode view of variables, INIT operator, ability to calibrate CV In and Param knob and set Min/Max scale values for both, a screensaver, Random Number Generator, and a number of fixes and improvements. diff --git a/libavr32 b/libavr32 index b49b2bfd..cde5d4fc 160000 --- a/libavr32 +++ b/libavr32 @@ -1 +1 @@ -Subproject commit b49b2bfda708ea1e05d5db8f3444dd01831b3850 +Subproject commit cde5d4fc86e7684ff512e4be6d9ddca787f8bbba diff --git a/module/config.mk b/module/config.mk index 264aa504..ade5461a 100644 --- a/module/config.mk +++ b/module/config.mk @@ -65,6 +65,7 @@ CSRCS = \ ../module/edit_mode.c \ ../module/flash.c \ ../module/gitversion.c \ + ../module/grid.c \ ../module/help_mode.c \ ../module/line_editor.c \ ../module/live_mode.c \ @@ -72,7 +73,6 @@ CSRCS = \ ../module/preset_r_mode.c \ ../module/preset_w_mode.c \ ../module/usb_disk_mode.c \ - ../module/screensaver_mode.c \ ../src/command.c \ ../src/every.c \ ../src/helpers.c \ @@ -88,10 +88,14 @@ CSRCS = \ ../src/ops/controlflow.c \ ../src/ops/delay.c \ ../src/ops/earthsea.c \ + ../src/ops/er301.c \ + ../src/ops/fader.c \ + ../src/ops/grid_ops.c \ ../src/ops/hardware.c \ ../src/ops/init.c \ ../src/ops/justfriends.c \ ../src/ops/maths.c \ + ../src/ops/matrixarchate.c \ ../src/ops/meadowphysics.c \ ../src/ops/metronome.c \ ../src/ops/orca.c \ @@ -114,6 +118,7 @@ CSRCS = \ ../libavr32/src/init_common.c \ ../libavr32/src/interrupts.c \ ../libavr32/src/kbd.c \ + ../libavr32/src/monome.c \ ../libavr32/src/region.c \ ../libavr32/src/screen.c \ ../libavr32/src/timers.c \ diff --git a/module/edit_mode.c b/module/edit_mode.c index 2d3e92c8..f362d67f 100644 --- a/module/edit_mode.c +++ b/module/edit_mode.c @@ -18,11 +18,18 @@ #include "conf_usb_host.h" // needed in order to include "usb_protocol_hid.h" #include "usb_protocol_hid.h" +#define UNDO_DEPTH 3 + static line_editor_t le; -static uint8_t line_no; +static uint8_t line_no1, line_no2; static uint8_t script; static error_t status; static char error_msg[TELE_ERROR_MSG_LENGTH]; +static tele_command_t undo_buffer[UNDO_DEPTH][SCRIPT_MAX_COMMANDS]; +static uint8_t undo_comments[UNDO_DEPTH][SCRIPT_MAX_COMMANDS]; +static uint8_t undo_length[UNDO_DEPTH]; +static uint8_t undo_line_no1[UNDO_DEPTH], undo_line_no2[UNDO_DEPTH]; +static uint8_t undo_count, undo_pos; static const uint8_t D_INPUT = 1 << 0; static const uint8_t D_LIST = 1 << 1; @@ -33,38 +40,118 @@ static uint8_t dirty; void set_edit_mode() { status = E_OK; error_msg[0] = 0; - line_no = 0; + line_no2 = line_no1 = 0; line_editor_set_command( - &le, ss_get_script_command(&scene_state, script, line_no)); + &le, ss_get_script_command(&scene_state, script, line_no1)); + undo_count = 0; dirty = D_ALL; } void set_edit_mode_script(uint8_t new_script) { script = new_script; if (script >= SCRIPT_COUNT) script = SCRIPT_COUNT - 2; + undo_count = 0; + dirty = D_ALL; +} + +uint8_t get_edit_script() { + return script; +} + +static void save_undo(void) { + if (++undo_count > 3) undo_count = 3; + undo_pos = (undo_pos + 1) % UNDO_DEPTH; + undo_line_no1[undo_pos] = line_no1; + undo_line_no2[undo_pos] = line_no2; + undo_length[undo_pos] = ss_get_script_len(&scene_state, script); + for (u8 l = 0; l < undo_length[undo_pos]; l++) { + undo_comments[undo_pos][l] = + ss_get_script_comment(&scene_state, script, l); + ss_copy_script_command(&undo_buffer[undo_pos][l], &scene_state, script, + l); + } +} + +static void undo(void) { + if (undo_count == 0) return; + undo_count--; + + ss_clear_script(&scene_state, script); + for (u8 l = 0; l < undo_length[undo_pos]; l++) { + ss_insert_script_command(&scene_state, script, l, + &undo_buffer[undo_pos][l]); + ss_set_script_comment(&scene_state, script, l, + undo_comments[undo_pos][l]); + } + + line_no1 = undo_line_no1[undo_pos]; + line_no2 = undo_line_no2[undo_pos]; + + undo_pos = (undo_pos + UNDO_DEPTH - 1) % UNDO_DEPTH; + + line_editor_set_command( + &le, ss_get_script_command(&scene_state, script, line_no1)); + dirty = D_ALL; +} + +void edit_mode_refresh() { dirty = D_ALL; } void process_edit_keys(uint8_t k, uint8_t m, bool is_held_key) { + // C-z: undo + if (match_ctrl(m, k, HID_Z)) { undo(); } // or C-n: line down - if (match_no_mod(m, k, HID_DOWN) || match_ctrl(m, k, HID_N)) { - if (line_no < (SCRIPT_MAX_COMMANDS - 1) && - line_no < ss_get_script_len(&scene_state, script)) { - line_no++; + else if (match_no_mod(m, k, HID_DOWN) || match_ctrl(m, k, HID_N)) { + if (line_no1 < (SCRIPT_MAX_COMMANDS - 1) && + line_no1 < ss_get_script_len(&scene_state, script)) { + line_no1++; line_editor_set_command( - &le, ss_get_script_command(&scene_state, script, line_no)); - dirty |= D_LIST; - dirty |= D_INPUT; + &le, ss_get_script_command(&scene_state, script, line_no1)); + dirty |= D_LIST | D_INPUT; + } + // reset selection + if (line_no2 != line_no1) { + line_no2 = line_no1; + dirty |= D_LIST | D_INPUT; } } // or C-p: line up else if (match_no_mod(m, k, HID_UP) || match_ctrl(m, k, HID_P)) { - if (line_no) { - line_no--; + if (line_no1) { + line_no1--; line_editor_set_command( - &le, ss_get_script_command(&scene_state, script, line_no)); - dirty |= D_LIST; - dirty |= D_INPUT; + &le, ss_get_script_command(&scene_state, script, line_no1)); + dirty |= D_LIST | D_INPUT; + } + // reset selection + if (line_no2 != line_no1) { + line_no2 = line_no1; + dirty |= D_LIST | D_INPUT; + } + } + // shift- or C-S-n: expand selection down + else if (match_shift(m, k, HID_DOWN) || match_shift_ctrl(m, k, HID_N)) { + if (line_no2 < (SCRIPT_MAX_COMMANDS - 1) && + line_no2 < max(1, ss_get_script_len(&scene_state, script)) - 1) { + line_no2++; + dirty |= D_LIST | D_INPUT; + } + } + // shift- or C-S-p: expand selection up + else if (match_shift(m, k, HID_UP) || match_shift_ctrl(m, k, HID_P)) { + if (line_no1 >= ss_get_script_len(&scene_state, script)) { + // if currently on blank line move up first + if (line_no1) { + line_no2 = --line_no1; + line_editor_set_command( + &le, ss_get_script_command(&scene_state, script, line_no1)); + dirty |= D_LIST | D_INPUT; + } + } + else if (line_no2) { + line_no2--; + dirty |= D_LIST | D_INPUT; } } // [: previous script @@ -75,12 +162,13 @@ void process_edit_keys(uint8_t k, uint8_t m, bool is_held_key) { script--; else script = SCRIPT_COUNT - 2; // due to TEMP_SCRIPT - if (line_no > ss_get_script_len(&scene_state, script)) - line_no = ss_get_script_len(&scene_state, script); + if (line_no1 > ss_get_script_len(&scene_state, script)) + line_no1 = ss_get_script_len(&scene_state, script); line_editor_set_command( - &le, ss_get_script_command(&scene_state, script, line_no)); - dirty |= D_LIST; - dirty |= D_INPUT; + &le, ss_get_script_command(&scene_state, script, line_no1)); + line_no2 = line_no1; + dirty |= D_LIST | D_INPUT; + undo_count = 0; } // ]: next script else if (match_no_mod(m, k, HID_CLOSE_BRACKET)) { @@ -88,31 +176,145 @@ void process_edit_keys(uint8_t k, uint8_t m, bool is_held_key) { error_msg[0] = 0; script++; if (script >= SCRIPT_COUNT - 1) script = 0; // due to TEMP_SCRIPT - if (line_no > ss_get_script_len(&scene_state, script)) - line_no = ss_get_script_len(&scene_state, script); + if (line_no1 > ss_get_script_len(&scene_state, script)) + line_no1 = ss_get_script_len(&scene_state, script); line_editor_set_command( - &le, ss_get_script_command(&scene_state, script, line_no)); - dirty |= D_LIST; - dirty |= D_INPUT; + &le, ss_get_script_command(&scene_state, script, line_no1)); + line_no2 = line_no1; + dirty |= D_LIST | D_INPUT; + undo_count = 0; + } + // alt-: move selected lines down + else if (match_alt(m, k, HID_DOWN)) { + u8 l1 = min(line_no1, line_no2); + u8 l2 = max(line_no1, line_no2); + if (l2 < SCRIPT_MAX_COMMANDS - 1 && + l2 < max(1, ss_get_script_len(&scene_state, script)) - 1) { + save_undo(); + tele_command_t temp; + ss_copy_script_command(&temp, &scene_state, script, l2 + 1); + ss_delete_script_command(&scene_state, script, l2 + 1); + ss_insert_script_command(&scene_state, script, l1, &temp); + line_no1++; + line_no2++; + dirty |= D_LIST | D_INPUT; + } + } + // alt-: move selected lines up + else if (match_alt(m, k, HID_UP)) { + u8 l1 = min(line_no1, line_no2); + u8 l2 = max(line_no1, line_no2); + if (l1 && l2 < ss_get_script_len(&scene_state, script)) { + save_undo(); + tele_command_t temp; + ss_copy_script_command(&temp, &scene_state, script, l1 - 1); + ss_delete_script_command(&scene_state, script, l1 - 1); + ss_insert_script_command(&scene_state, script, l2, &temp); + line_no1--; + line_no2--; + dirty |= D_LIST | D_INPUT; + } } // ctrl-x or alt-x: override line editors cut else if (match_ctrl(m, k, HID_X) || match_alt(m, k, HID_X)) { - line_editor_set_copy_buffer(line_editor_get(&le)); - ss_delete_script_command(&scene_state, script, line_no); - if (line_no > ss_get_script_len(&scene_state, script)) { - line_no = ss_get_script_len(&scene_state, script); + if (line_no1 == line_no2) { + if (line_no1 < ss_get_script_len(&scene_state, script)) { + save_undo(); + strcpy(copy_buffer[0], line_editor_get(&le)); + copy_buffer_len = 1; + ss_delete_script_command(&scene_state, script, line_no1); + } + } + else { + save_undo(); + u8 l1 = min(line_no1, line_no2); + u8 l2 = max(line_no1, line_no2); + copy_buffer_len = 0; + for (u8 l = l1; l <= l2; l++) + print_command(ss_get_script_command(&scene_state, script, l), + copy_buffer[copy_buffer_len++]); + for (s8 l = l2; l >= l1; l--) + ss_delete_script_command(&scene_state, script, l); + } + + if (line_no1 > ss_get_script_len(&scene_state, script)) { + line_no1 = ss_get_script_len(&scene_state, script); } line_editor_set_command( - &le, ss_get_script_command(&scene_state, script, line_no)); + &le, ss_get_script_command(&scene_state, script, line_no1)); + line_no2 = line_no1; - dirty |= D_LIST; - dirty |= D_INPUT; + dirty |= D_LIST | D_INPUT; + } + // ctrl-c or alt-c: override line editors copy for multi selection + else if (match_ctrl(m, k, HID_C) || match_alt(m, k, HID_C)) { + if (line_no1 == line_no2) { + // not a multi line selection, pass it to line editor + bool processed = line_editor_process_keys(&le, k, m, is_held_key); + if (processed) dirty |= D_INPUT; + } + else { + save_undo(); + copy_buffer_len = 0; + for (u8 l = min(line_no1, line_no2); l <= max(line_no1, line_no2); + l++) + print_command(ss_get_script_command(&scene_state, script, l), + copy_buffer[copy_buffer_len++]); + } + } + // ctrl-v or alt-v: override line editors paste for multi selection + else if (match_ctrl(m, k, HID_V) || match_alt(m, k, HID_V)) { + if (copy_buffer_len == 0) return; + + save_undo(); + u8 idx = min(line_no1, line_no2); + line_no1 = idx; + tele_command_t command; + command.comment = false; + for (u8 i = 0; i < copy_buffer_len; i++) { + if (parse(copy_buffer[i], &command, error_msg) != E_OK) continue; + if (validate(&command, error_msg) != E_OK) continue; + if (command.length == 0) continue; + + ss_insert_script_command(&scene_state, script, idx++, &command); + if (line_no2 < (SCRIPT_MAX_COMMANDS - 1)) line_no2++; + if (idx >= SCRIPT_MAX_COMMANDS) break; + } + line_no2 = idx > line_no1 ? idx - 1 : line_no1; + line_editor_set_command( + &le, ss_get_script_command(&scene_state, script, line_no1)); + dirty |= D_LIST | D_INPUT; + } + // alt-delete: remove selected lines + else if (match_alt(m, k, HID_DELETE)) { + u8 l1 = min(line_no1, line_no2); + u8 l2 = max(line_no1, line_no2); + if (l1 < ss_get_script_len(&scene_state, script)) { + save_undo(); + for (s8 l = l2; l >= l1; l--) + ss_delete_script_command(&scene_state, script, l); + if (line_no1 > ss_get_script_len(&scene_state, script)) + line_no1 = ss_get_script_len(&scene_state, script); + line_editor_set_command( + &le, ss_get_script_command(&scene_state, script, line_no1)); + line_no2 = line_no1; + dirty |= D_LIST | D_INPUT; + } } // : enter command else if (match_no_mod(m, k, HID_ENTER)) { + if (line_no1 != line_no2) { + line_no2 = line_no1; + line_editor_set_command( + &le, ss_get_script_command(&scene_state, script, line_no1)); + dirty |= D_LIST | D_INPUT; + return; + } + dirty |= D_MESSAGE; // something will happen tele_command_t command; + command.comment = false; status = parse(line_editor_get(&le), &command, error_msg); if (status != E_OK) @@ -122,27 +324,31 @@ void process_edit_keys(uint8_t k, uint8_t m, bool is_held_key) { if (status != E_OK) return; // quit, screen_refresh_edit will display the error message + save_undo(); if (command.length == 0) { // blank line, delete the command - ss_delete_script_command(&scene_state, script, line_no); - if (line_no > ss_get_script_len(&scene_state, script)) { - line_no = ss_get_script_len(&scene_state, script); + ss_delete_script_command(&scene_state, script, line_no1); + if (line_no1 > ss_get_script_len(&scene_state, script)) { + line_no1 = ss_get_script_len(&scene_state, script); } } else { - ss_overwrite_script_command(&scene_state, script, line_no, + ss_overwrite_script_command(&scene_state, script, line_no1, &command); - if (line_no < SCRIPT_MAX_COMMANDS - 1) { line_no++; } + if (line_no1 < SCRIPT_MAX_COMMANDS - 1) { line_no1++; } } line_editor_set_command( - &le, ss_get_script_command(&scene_state, script, line_no)); - dirty |= D_LIST; - dirty |= D_INPUT; + &le, ss_get_script_command(&scene_state, script, line_no1)); + line_no2 = line_no1; + dirty |= D_LIST | D_INPUT; } // shift-: insert command else if (match_shift(m, k, HID_ENTER)) { + if (line_no1 != line_no2) return; + dirty |= D_MESSAGE; // something will happen tele_command_t command; + command.comment = false; status = parse(line_editor_get(&le), &command, error_msg); if (status != E_OK) @@ -152,22 +358,34 @@ void process_edit_keys(uint8_t k, uint8_t m, bool is_held_key) { if (status != E_OK) return; // quit, screen_refresh_edit will display the error message + save_undo(); if (command.length > 0) { - ss_insert_script_command(&scene_state, script, line_no, &command); - if (line_no < (SCRIPT_MAX_COMMANDS - 1)) { line_no++; } + ss_insert_script_command(&scene_state, script, line_no1, &command); + if (line_no1 < (SCRIPT_MAX_COMMANDS - 1)) { line_no1++; } } line_editor_set_command( - &le, ss_get_script_command(&scene_state, script, line_no)); - dirty |= D_LIST; - dirty |= D_INPUT; + &le, ss_get_script_command(&scene_state, script, line_no1)); + line_no2 = line_no1; + dirty |= D_LIST | D_INPUT; } - // alt-slash comment toggle current line + // alt-slash comment toggle selected lines else if (match_alt(m, k, HID_SLASH)) { - ss_toggle_script_comment(&scene_state, script, line_no); + if (line_no1 >= ss_get_script_len(&scene_state, script)) return; + save_undo(); + for (u8 l = min(line_no1, line_no2); l <= max(line_no1, line_no2); l++) + ss_toggle_script_comment(&scene_state, script, l); dirty |= D_LIST; } else { // pass the key though to the line editor + if (line_no1 != line_no2) { + line_no2 = line_no1; + line_editor_set_command( + &le, ss_get_script_command(&scene_state, script, line_no1)); + dirty |= D_LIST | D_INPUT; + return; + } + bool processed = line_editor_process_keys(&le, k, m, is_held_key); if (processed) dirty |= D_INPUT; } @@ -179,6 +397,8 @@ void screen_mutes_updated() { uint8_t screen_refresh_edit() { uint8_t screen_dirty = 0; + u8 sel1 = min(line_no1, line_no2); + u8 sel2 = max(line_no1, line_no2); if (dirty & D_INPUT) { char prefix = script + '1'; @@ -187,12 +407,15 @@ uint8_t screen_refresh_edit() { else if (script == INIT_SCRIPT) prefix = 'I'; - line_editor_draw(&le, prefix, &line[7]); - // maybe find a better way than stomping it? - if (ss_get_mute(&scene_state, script)) { - char shaded[2] = { prefix, '\0' }; - font_string_region_clip(&line[7], shaded, 0, 0, 0x4, 0); - } + if (sel1 == sel2) + line_editor_draw(&le, prefix, &line[7]); + else + region_fill(&line[7], 0); + + char script_no[2] = { prefix, '\0' }; + font_string_region_clip(&line[7], script_no, 0, 0, + ss_get_mute(&scene_state, script) ? 4 : 15, 0); + screen_dirty |= (1 << 7); dirty &= ~D_INPUT; } @@ -220,14 +443,14 @@ uint8_t screen_refresh_edit() { dirty &= ~D_MESSAGE; } + char s[32]; if (dirty & D_LIST) { for (int i = 0; i < 6; i++) { - uint8_t a = line_no == i; + uint8_t a = i >= sel1 && i <= sel2; uint8_t fg = ss_get_script_comment(&scene_state, script, i) ? 0x7 : 0xf; region_fill(&line[i], a); if (ss_get_script_len(&scene_state, script) > i) { - char s[32]; print_command(ss_get_script_command(&scene_state, script, i), s); region_string(&line[i], s, 2, 0, fg, a, 0); diff --git a/module/edit_mode.h b/module/edit_mode.h index 951cffe1..f208ec7b 100644 --- a/module/edit_mode.h +++ b/module/edit_mode.h @@ -6,6 +6,8 @@ void set_edit_mode(void); void set_edit_mode_script(uint8_t new_script); +uint8_t get_edit_script(void); +void edit_mode_refresh(void); void process_edit_keys(uint8_t key, uint8_t mod_key, bool is_held_key); void screen_mutes_updated(void); uint8_t screen_refresh_edit(void); diff --git a/module/flash.c b/module/flash.c index 376714a2..ad7e5233 100644 --- a/module/flash.c +++ b/module/flash.c @@ -4,23 +4,33 @@ // asf #include "flashc.h" +#include "init_teletype.h" #include "print_funcs.h" // this #include "teletype.h" #define FIRSTRUN_KEY 0x22 +#define BUTTON_STATE_SIZE (GRID_BUTTON_COUNT >> 3) + +typedef struct { + uint8_t button_states[BUTTON_STATE_SIZE]; + uint8_t fader_states[GRID_FADER_COUNT]; +} grid_data_t; +static grid_data_t grid_data; // NVRAM data structure located in the flash array. typedef const struct { - scene_script_t scripts[SCRIPT_COUNT - 1]; // Exclude TEMP script + scene_script_t scripts[SCRIPT_COUNT - 1]; // Exclude TEMP script scene_pattern_t patterns[PATTERN_COUNT]; + grid_data_t grid_data; char text[SCENE_TEXT_LINES][SCENE_TEXT_CHARS]; } nvram_scene_t; typedef const struct { nvram_scene_t scenes[SCENE_SLOTS]; uint8_t last_scene; + tele_mode_t last_mode; uint8_t fresh; cal_data_t cal; } nvram_data_t; @@ -28,6 +38,9 @@ typedef const struct { static __attribute__((__section__(".flash_nvram"))) nvram_data_t f; +static void pack_grid(scene_state_t *scene); +static void unpack_grid(scene_state_t *scene); + void flash_prepare() { // if it's not empty return if (f.fresh == FIRSTRUN_KEY) return; @@ -48,6 +61,7 @@ void flash_prepare() { cal_data_t cal = { 0, 16383, 0, 16383 }; flashc_memcpy((void *)&f.cal, &cal, sizeof(cal), true); flash_update_last_saved_scene(0); + flash_update_last_mode(M_LIVE); flashc_memset8((void *)&f.fresh, FIRSTRUN_KEY, 1, true); } @@ -58,6 +72,9 @@ void flash_write(uint8_t preset_no, scene_state_t *scene, ss_scripts_size() - sizeof(scene_script_t), true); flashc_memcpy((void *)&f.scenes[preset_no].patterns, ss_patterns_ptr(scene), ss_patterns_size(), true); + pack_grid(scene); + flashc_memcpy((void *)&f.scenes[preset_no].grid_data, &grid_data, + sizeof(grid_data_t), true); flashc_memcpy((void *)&f.scenes[preset_no].text, text, SCENE_TEXT_LINES * SCENE_TEXT_CHARS, true); } @@ -69,8 +86,15 @@ void flash_read(uint8_t preset_no, scene_state_t *scene, ss_scripts_size() - sizeof(scene_script_t)); memcpy(ss_patterns_ptr(scene), &f.scenes[preset_no].patterns, ss_patterns_size()); + memcpy(&grid_data, &f.scenes[preset_no].grid_data, sizeof(grid_data_t)); + unpack_grid(scene); memcpy(text, &f.scenes[preset_no].text, SCENE_TEXT_LINES * SCENE_TEXT_CHARS); + // need to reset timestamps + uint32_t ticks = get_ticks(); + for (size_t i = 0; i < TEMP_SCRIPT; i++) + scene->scripts[i].last_time = ticks; + scene->variables.time = 0; } uint8_t flash_last_saved_scene() { @@ -85,6 +109,14 @@ const char *flash_scene_text(uint8_t preset_no, size_t line) { return f.scenes[preset_no].text[line]; } +tele_mode_t flash_last_mode() { + return f.last_mode; +} + +void flash_update_last_mode(tele_mode_t mode) { + flashc_memset8((void *)&f.last_mode, mode, sizeof(tele_mode_t), true); +} + void flash_update_cal(cal_data_t *cal) { flashc_memcpy((void *)&f.cal, cal, sizeof(cal_data_t), true); } @@ -92,3 +124,27 @@ void flash_update_cal(cal_data_t *cal) { void flash_get_cal(cal_data_t *cal) { *cal = f.cal; } + +static void pack_grid(scene_state_t *scene) { + uint8_t byte = 0; + uint8_t byte_count = 0; + for (uint16_t i = 0; i < GRID_BUTTON_COUNT; i++) { + byte |= (scene->grid.button[i].state != 0) << (i & 7); + if ((i & 7) == 7) { + grid_data.button_states[byte_count] = byte; + byte = 0; + if (++byte_count >= BUTTON_STATE_SIZE) break; + } + } + for (uint16_t i = 0; i < GRID_FADER_COUNT; i++) + grid_data.fader_states[i] = scene->grid.fader[i].value; +} + +static void unpack_grid(scene_state_t *scene) { + for (uint16_t i = 0; i < GRID_BUTTON_COUNT; i++) { + scene->grid.button[i].state = + 0 != (grid_data.button_states[i >> 3] & (1 << (i & 7))); + } + for (uint16_t i = 0; i < GRID_FADER_COUNT; i++) + scene->grid.fader[i].value = grid_data.fader_states[i]; +} \ No newline at end of file diff --git a/module/flash.h b/module/flash.h index eb2a36ef..4e99fbe7 100644 --- a/module/flash.h +++ b/module/flash.h @@ -17,6 +17,8 @@ void flash_write(uint8_t preset_no, scene_state_t *scene, uint8_t flash_last_saved_scene(void); void flash_update_last_saved_scene(uint8_t preset_no); const char *flash_scene_text(uint8_t preset_no, size_t line); +tele_mode_t flash_last_mode(void); +void flash_update_last_mode(tele_mode_t mode); void flash_update_cal(cal_data_t *); void flash_get_cal(cal_data_t *); diff --git a/module/globals.h b/module/globals.h index 774e0f89..5c69f472 100644 --- a/module/globals.h +++ b/module/globals.h @@ -29,11 +29,15 @@ typedef enum { M_PATTERN, M_PRESET_W, M_PRESET_R, - M_HELP, - M_SCREENSAVER + M_HELP } tele_mode_t; void set_mode(tele_mode_t mode); void set_last_mode(void); +void clear_delays_and_slews(scene_state_t *ss); + +// global copy buffer +extern char copy_buffer[SCENE_TEXT_LINES][SCENE_TEXT_CHARS]; +extern uint8_t copy_buffer_len; #endif diff --git a/module/grid.c b/module/grid.c new file mode 100644 index 00000000..bde39a21 --- /dev/null +++ b/module/grid.c @@ -0,0 +1,2025 @@ +#include "grid.h" +#include "edit_mode.h" +#include "flash.h" +#include "font.h" +#include "globals.h" +#include "live_mode.h" +#include "pattern_mode.h" +#include "preset_r_mode.h" +#include "state.h" +#include "teletype.h" +#include "teletype_io.h" +#include "timers.h" +#include "util.h" + +#define GRID_MAX_KEY_PRESSED 10 +#define GRID_KEY_HOLD_DELAY 700 +#define GRID_KEY_REPEAT_RATE 40 +#define GRID_KEY_REPEAT_RATE_CTL 120 +#define GRID_ON_BRIGHTNESS 13 +#define GRID_SCRIPT_TRIGGER 60 + +typedef enum { + G_LIVE_V, + G_LIVE_G, + G_LIVE_GF, + G_EDIT, + G_TRACKER, + G_PRESET +} grid_control_mode_t; + +// clang-format off + +static const u8 glyph[16][6] = { + { + 0b000000, + 0b000000, + 0b000000, + 0b000000, + 0b000000, + 0b000000 + }, + { + 0b000000, + 0b011110, + 0b010010, + 0b010010, + 0b011110, + 0b000000 + }, + { + 0b000000, + 0b011110, + 0b010010, + 0b010010, + 0b010010, + 0b010010 + }, + { + 0b010010, + 0b010010, + 0b010010, + 0b010010, + 0b010010, + 0b010010 + }, + { + 0b010010, + 0b010010, + 0b010010, + 0b010010, + 0b011110, + 0b000000 + }, + { + 0b000000, + 0b011111, + 0b010000, + 0b010000, + 0b011111, + 0b000000 + }, + { + 0b000000, + 0b111111, + 0b000000, + 0b000000, + 0b111111, + 0b000000 + }, + { + 0b000000, + 0b111110, + 0b000010, + 0b000010, + 0b111110, + 0b000000 + }, + { + 0b000000, + 0b011111, + 0b010000, + 0b010000, + 0b010000, + 0b010000 + }, + { + 0b000000, + 0b111111, + 0b000000, + 0b000000, + 0b000000, + 0b000000 + }, + { + 0b000000, + 0b111110, + 0b000010, + 0b000010, + 0b000010, + 0b000010 + }, + { + 0b010000, + 0b010000, + 0b010000, + 0b010000, + 0b010000, + 0b010000 + }, + { + 0b000010, + 0b000010, + 0b000010, + 0b000010, + 0b000010, + 0b000010 + }, + { + 0b010000, + 0b010000, + 0b010000, + 0b010000, + 0b011111, + 0b000000 + }, + { + 0b000000, + 0b000000, + 0b000000, + 0b000000, + 0b111111, + 0b000000 + }, + { + 0b000010, + 0b000010, + 0b000010, + 0b000010, + 0b111110, + 0b000000 + } +}; + +// clang-format on + +typedef struct { + u8 used; + u8 key; + u8 x; + u8 y; + scene_state_t *ss; + softTimer_t timer; +} hold_repeat_info; + +typedef struct { + u8 on; + scene_state_t *ss; + softTimer_t timer; +} script_trigger_info; + +static grid_control_mode_t tt_mode = G_LIVE_V, tt_last_mode = G_LIVE_V; +static u8 control_mode_on, tt_script, variable_edit, variable_changed; +static u8 preset_write, tracker_pressed, tracker_x, tracker_y; +static u8 tracker_changed, tracker_select, tracker_selected; +static u8 tracker_set_start, tracker_set_end; +static s16 tracker_last, variable_last; +static u16 size_x = 16, size_y = 8; +static u8 screen[GRID_MAX_DIMENSION][GRID_MAX_DIMENSION / 2]; +static hold_repeat_info held_keys[GRID_MAX_KEY_PRESSED]; +static u8 timers_uninitialized = 1; +static script_trigger_info script_triggers[11]; + +static void grid_control_refresh(scene_state_t *ss); +static u8 grid_control_process_key(scene_state_t *ss, u8 x, u8 y, u8 z, + u8 from_held); +static void hold_repeat_timer_callback(void *o); +static void grid_process_key_hold_repeat(scene_state_t *ss, u8 x, u8 y); +static void grid_screen_refresh_ctrl(scene_state_t *ss, u8 page, u8 x1, u8 y1, + u8 x2, u8 y2); +static void grid_screen_refresh_led(scene_state_t *ss, u8 full_grid, u8 page, + u8 x1, u8 y1, u8 x2, u8 y2); +static void grid_screen_refresh_info(scene_state_t *ss, u8 page, u8 x1, u8 y1, + u8 x2, u8 y2); +static bool grid_within_area(u8 x, u8 y, grid_common_t *gc); +static void grid_fill_area(u8 x, u8 y, u8 w, u8 h, s8 level); +static void grid_fill_area_scr(u8 x, u8 y, u8 w, u8 h, s8 level, u8 page); + +void grid_set_control_mode(u8 control, u8 mode, scene_state_t *ss) { + if (mode == M_LIVE) { + if (grid_mode == GRID_MODE_EDIT) + tt_mode = G_LIVE_G; + else if (grid_mode == GRID_MODE_FULL) + tt_mode = G_LIVE_GF; + else + tt_mode = G_LIVE_V; + } + else if (mode == M_EDIT) { + tt_mode = G_EDIT; + tt_script = get_edit_script(); + } + else if (mode == M_PATTERN) { + tt_mode = G_TRACKER; + } + else if (mode == M_PRESET_W || mode == M_PRESET_R) { + tt_mode = G_PRESET; + } + control_mode_on = control; + grid_clear_held_keys(); + ss->grid.grid_dirty = 1; +} + +void grid_control_refresh(scene_state_t *ss) { + size_x = monome_size_x(); + size_y = monome_size_y(); + + u16 d = size_y == 16 ? 128 : 0; + if (size_x == 16) d += 8; + + u8 mode_on = 15; + u8 mode_off = 7; + u8 exec = 15; + u8 trig = 7; + u8 kill = 11; + u8 tracker_in = 6; + u8 tracker_out = 2; + u8 tracker_on = 13; + u8 tracker_pos = 2; + u8 tracker_page_on = 11; + u8 tracker_page_off = 5; + u8 tracker_loop = 5; + u8 tracker_control = 11; + u8 preset_selected = 15; + u8 preset_unselected = 4; + u8 preset_scroll = 8; + u8 preset_load = 11; + u8 preset_save = 15; + u8 var_edit_on = 11; + u8 var_edit_off = 6; + u8 var_value_on = 11; + u8 var_value_off = 4; + u8 live_hist = 7; + u8 live_exec = 11; + u8 mute_on = 8; + u8 mute_off = 4; + u8 grid_page_on = 8; + u8 grid_page_off = 4; + u8 line_on = 8; + u8 line_off = 4; + u8 script_control = 8; + + if (!monome_is_vari()) { + mode_on = 15; + mode_off = 15; + exec = 15; + trig = 15; + kill = 15; + tracker_in = 0; + tracker_out = 0; + tracker_on = 15; + tracker_page_on = 15; + tracker_page_off = 0; + tracker_loop = 15; + tracker_control = 15; + preset_selected = 15; + preset_unselected = 15; + preset_scroll = 15; + preset_load = 15; + preset_save = 15; + var_edit_on = 15; + var_edit_off = 15; + var_value_on = 15; + var_value_off = 0; + live_hist = 15; + live_exec = 15; + mute_on = 15; + mute_off = 15; + grid_page_on = 15; + grid_page_off = 15; + line_on = 15; + line_off = 15; + script_control = 15; + } + + for (u16 i = 0; i < 8; i++) + for (u16 j = 0; j < 8; j++) monomeLedBuffer[d + i + (j << 4)] = 0; + + if (tt_mode == G_TRACKER) { + monomeLedBuffer[d + 7] = mode_on; + u8 offset = get_pattern_offset(), in, off, rem; + for (u16 j = 0; j < 8; j++) { + for (u16 i = 0; i < 4; i++) { + in = offset + j >= ss_get_pattern_start(ss, i) && + offset + j <= ss_get_pattern_end(ss, i); + monomeLedBuffer[d + i + 2 + (j << 4)] = + ss_get_pattern_val(ss, i, j + offset) + ? tracker_on + : (in ? tracker_in : tracker_out); + } + off = offset >> 3; + rem = (offset & 7) >> 1; + monomeLedBuffer[d + (j << 4)] = + j == off ? tracker_page_on - rem + : (j == off + 1 ? tracker_page_off + rem + : tracker_page_off); + } + for (u16 i = 0; i < 4; i++) { + u8 index = ss_get_pattern_idx(ss, i); + if (index >= offset && index <= offset + 7) { + monomeLedBuffer[d + i + 2 + (index << 4)] += tracker_pos; + } + } + + d += 32; + monomeLedBuffer[d + 7] = tracker_control; + d += 16; + monomeLedBuffer[d + 7] = + turtle_get_shown(&ss->turtle) ? tracker_control : tracker_loop; + d += 16; + monomeLedBuffer[d + 7] = tracker_loop; + d += 16; + monomeLedBuffer[d + 7] = tracker_loop; + d += 16; + monomeLedBuffer[d + 7] = tracker_control; + d += 16; + monomeLedBuffer[d + 7] = tracker_control; + return; + } + + // mode selection + monomeLedBuffer[d] = + tt_mode == G_EDIT && tt_script == 8 ? mode_on : mode_off; + monomeLedBuffer[d + 1] = + tt_mode == G_EDIT && tt_script == 9 ? mode_on : mode_off; + monomeLedBuffer[d + 3] = tt_mode == G_LIVE_V ? mode_on : mode_off; + monomeLedBuffer[d + 4] = + tt_mode == G_LIVE_G || tt_mode == G_LIVE_GF ? mode_on : mode_off; + monomeLedBuffer[d + 6] = tt_mode == G_PRESET ? mode_on : mode_off; + monomeLedBuffer[d + 7] = mode_off; + d += 16; + for (u16 i = 0; i < 8; i++) + monomeLedBuffer[d + i] = + tt_mode == G_EDIT && tt_script == i ? mode_on : mode_off; + d += 16; + + if (tt_mode == G_PRESET) { + for (u8 j = 0; j < 25; j += 8) { + for (u16 i = 0; i < 8; i++) + monomeLedBuffer[d + i] = i + j == preset_select + ? preset_selected + : preset_unselected; + d += 16; + } + + monomeLedBuffer[d + 7] = preset_scroll; + d += 16; + monomeLedBuffer[d + 2] = preset_load; + monomeLedBuffer[d + 4] = preset_save; + monomeLedBuffer[d + 7] = preset_scroll; + return; + } + + monomeLedBuffer[d + 16] = ss->variables.m_act ? mute_off : mute_on; + monomeLedBuffer[d + 17] = script_triggers[10].on ? exec : kill; + monomeLedBuffer[d + 32] = script_triggers[8].on ? exec : trig; + monomeLedBuffer[d + 33] = script_triggers[9].on ? exec : trig; + + if (tt_mode == G_LIVE_V) { + monomeLedBuffer[d + 3] = + variable_edit == 1 ? var_edit_on : var_edit_off; + monomeLedBuffer[d + 4] = + variable_edit == 2 ? var_edit_on : var_edit_off; + d += 16; + monomeLedBuffer[d + 3] = + variable_edit == 3 ? var_edit_on : var_edit_off; + monomeLedBuffer[d + 4] = + variable_edit == 4 ? var_edit_on : var_edit_off; + monomeLedBuffer[d + 6] = live_hist; + d += 16; + monomeLedBuffer[d + 3] = + variable_edit == 5 ? var_edit_on : var_edit_off; + monomeLedBuffer[d + 4] = + variable_edit == 6 ? var_edit_on : var_edit_off; + monomeLedBuffer[d + 6] = live_hist; + monomeLedBuffer[d + 7] = live_exec; + d += 16; + monomeLedBuffer[d + 3] = + variable_edit == 7 ? var_edit_on : var_edit_off; + monomeLedBuffer[d + 4] = + variable_edit == 8 ? var_edit_on : var_edit_off; + } + else if (tt_mode == G_LIVE_G || tt_mode == G_LIVE_GF) { + d += 16; + monomeLedBuffer[d + 7] = grid_page == 0 ? grid_page_on : grid_page_off; + d += 16; + monomeLedBuffer[d + 3] = ss->grid.rotate ? grid_page_on : grid_page_off; + monomeLedBuffer[d + 5] = + grid_show_controls ? grid_page_on : grid_page_off; + monomeLedBuffer[d + 7] = grid_page == 1 ? grid_page_on : grid_page_off; + d += 16; + } + else if (tt_mode == G_EDIT) { + d += 16; + monomeLedBuffer[d + 3] = + ss_get_script_comment(ss, tt_script, 0) ? line_off : line_on; + monomeLedBuffer[d + 4] = + ss_get_script_comment(ss, tt_script, 3) ? line_off : line_on; + monomeLedBuffer[d + 7] = script_control; + d += 16; + monomeLedBuffer[d + 3] = + ss_get_script_comment(ss, tt_script, 1) ? line_off : line_on; + monomeLedBuffer[d + 4] = + ss_get_script_comment(ss, tt_script, 4) ? line_off : line_on; + monomeLedBuffer[d + 7] = script_control; + d += 16; + monomeLedBuffer[d + 3] = + ss_get_script_comment(ss, tt_script, 2) ? line_off : line_on; + monomeLedBuffer[d + 4] = + ss_get_script_comment(ss, tt_script, 5) ? line_off : line_on; + } + d += 16; + + // script mutes + for (u16 i = 0; i < 8; i++) + monomeLedBuffer[d + i] = ss_get_mute(ss, i) ? mute_on : mute_off; + d += 16; + + // triggered scripts + for (u16 i = 0; i < 8; i++) + monomeLedBuffer[d + i] = script_triggers[i].on ? exec : trig; + + if (variable_edit) { + u8 ve = variable_edit - 1; + int16_t *v = &(ss->variables.a); + if (size_x == 8) { + if (v[ve] < 0) + for (u16 i = 0; i < 8; i++) + monomeLedBuffer[d + i] = var_value_off; + else if (v[ve] > 8) + for (u16 i = 0; i < 8; i++) + monomeLedBuffer[d + i] = var_value_on; + else + for (u16 i = 0; i < 8; i++) + monomeLedBuffer[d + i] = + i < v[ve] ? var_value_on : var_value_off; + } + else { + d -= 8; + if (v[ve] < 0) + for (u16 i = 0; i < 16; i++) + monomeLedBuffer[d + i] = var_value_off; + else if (v[ve] > 16) + for (u16 i = 0; i < 16; i++) + monomeLedBuffer[d + i] = var_value_on; + else + for (u16 i = 0; i < 16; i++) + monomeLedBuffer[d + i] = + i < v[ve] ? var_value_on : var_value_off; + } + } +} + +static void script_triggers_callback(void *o) { + script_trigger_info *st = o; + timer_remove(&st->timer); + st->on = 0; + st->ss->grid.grid_dirty = 1; +} + +void grid_metro_triggered(scene_state_t *ss) { + script_triggers[8].on = 1; + script_triggers[8].ss = ss; + timer_remove(&script_triggers[8].timer); + timer_add(&script_triggers[8].timer, + min(GRID_SCRIPT_TRIGGER, ss->variables.m >> 1), + &script_triggers_callback, (void *)&script_triggers[8]); + ss->grid.grid_dirty = 1; +} + +static void restore_last_mode(scene_state_t *ss) { + tt_mode = tt_last_mode; + if (tt_mode == G_EDIT) { + set_edit_mode_script(tt_script); + set_mode(M_EDIT); + } + else if (tt_mode == G_PRESET) { + set_mode(M_PRESET_R); + } + else if (tt_mode == G_LIVE_V) { + set_mode(M_LIVE); + set_live_submode(1); + } + else if (tt_mode == G_LIVE_G) { + set_mode(M_LIVE); + set_live_submode(2); + } + else if (tt_mode == G_LIVE_GF) { + set_mode(M_LIVE); + set_live_submode(3); + } + else if (tt_mode == G_TRACKER) { + tt_mode = G_TRACKER; + set_mode(M_PATTERN); + } + grid_clear_held_keys(); + ss->grid.grid_dirty = 1; +} + +static u8 grid_control_process_key(scene_state_t *ss, u8 x, u8 y, u8 z, + u8 from_held) { + if (size_y == 16) { + if (y < 8) return 0; + y -= 8; + } + + if (variable_edit && y == 7) { + if (!z || from_held) return 1; + + int16_t *v = &(ss->variables.a); + v[variable_edit - 1] = v[variable_edit - 1] == x + 1 ? 0 : x + 1; + variable_changed = 1; + set_vars_updated(); + ss->grid.grid_dirty = 1; + return 1; + } + + if (size_x == 16) { + if (x < 8) return 0; + x -= 8; + } + + // tracker + if (tt_mode == G_TRACKER) { + u8 offset = get_pattern_offset(); + + if (x == 7 && y == 4) tracker_set_start = z; + if (x == 7 && y == 5) tracker_set_end = z; + + if (tracker_pressed) { + s16 value = + ss_get_pattern_val(ss, tracker_x - 2, tracker_y + offset); + + if (x == tracker_x && y == tracker_y && !z) { + if (!tracker_changed) { + s16 value = ss_get_pattern_val( + ss, tracker_x - 2, tracker_y + get_pattern_offset()); + if (value) { + tracker_last = value; + value = 0; + } + else { + value = tracker_last ? tracker_last : 1; + } + ss_set_pattern_val(ss, tracker_x - 2, tracker_y + offset, + value); + } + tracker_pressed = 0; + tele_pattern_updated(); + ss->grid.grid_dirty = 1; + return 1; + } + + if (!z) return 1; + + u8 updated = 0; + if (y == tracker_y) { + if (x == tracker_x + 1) { + if (value < 32767) { + ss_set_pattern_val(ss, tracker_x - 2, + tracker_y + offset, value + 1); + updated = 1; + } + } + else if (x == tracker_x + 2) { + if (value < 32758) { + ss_set_pattern_val(ss, tracker_x - 2, + tracker_y + offset, value + 10); + updated = 1; + } + else if (value < 32767) { + ss_set_pattern_val(ss, tracker_x - 2, + tracker_y + offset, 32767); + updated = 1; + } + } + else if (x == tracker_x - 1) { + if (value > -32768) { + ss_set_pattern_val(ss, tracker_x - 2, + tracker_y + offset, value - 1); + updated = 1; + } + } + else if (x == tracker_x - 2) { + if (value > -32759) { + ss_set_pattern_val(ss, tracker_x - 2, + tracker_y + offset, value - 10); + updated = 1; + } + else if (value > -32768) { + ss_set_pattern_val(ss, tracker_x - 2, + tracker_y + offset, -32768); + updated = 1; + } + } + } + else if (x > 1 && x < 6) { + // set loop + if (from_held) return 1; + for (u8 i = min(tracker_x, x); i <= max(tracker_x, x); i++) { + ss_set_pattern_start(ss, i - 2, min(y, tracker_y) + offset); + ss_set_pattern_end(ss, i - 2, max(y, tracker_y) + offset); + ss_set_pattern_len(ss, i - 2, + max(y, tracker_y) + offset + 1); + } + updated = 1; + } + + if (updated) { + tracker_changed = 1; + tele_pattern_updated(); + ss->grid.grid_dirty = 1; + } + return 1; + } + + if (tracker_select || tracker_set_start || tracker_set_end) { + if (x == 7 && y == tracker_select && !z) { + if (y == 3 && !tracker_selected) { + turtle_set_shown(&ss->turtle, + !turtle_get_shown(&ss->turtle)); + tele_pattern_updated(); + ss->grid.grid_dirty = 1; + } + tracker_select = 0; + } + else if (x > 1 && x < 6 && z && !from_held) { + if (tracker_select == 2) { + // set current position + tracker_selected = 1; + ss_set_pattern_idx(ss, x - 2, offset + y); + tele_pattern_updated(); + ss->grid.grid_dirty = 1; + } + else if (tracker_select == 3) { + // set turtle position + tracker_selected = 1; + turtle_set_x(&ss->turtle, x - 2); + turtle_set_y(&ss->turtle, offset + y); + turtle_set_shown(&ss->turtle, 1); + tele_pattern_updated(); + ss->grid.grid_dirty = 1; + } + if (tracker_set_start) { + // set start + ss_set_pattern_start(ss, x - 2, y + offset); + tracker_selected = 1; + tele_pattern_updated(); + ss->grid.grid_dirty = 1; + } + if (tracker_set_end) { + // set end + ss_set_pattern_end(ss, x - 2, y + offset); + ss_set_pattern_len(ss, x - 2, y + offset + 1); + tracker_selected = 1; + tele_pattern_updated(); + ss->grid.grid_dirty = 1; + } + } + return 1; + } + + if (x > 1 && x < 6 && z) { + // pattern value selected + if (x != tracker_x || y != tracker_y) tracker_last = 0; + tracker_pressed = 1; + tracker_changed = 0; + tracker_x = x; + tracker_y = y; + set_pattern_selected_value(x - 2, y); + tele_pattern_updated(); + ss->grid.grid_dirty = 1; + } + else if (x == 7 && y == 0 && !from_held && z) { + // exit tracker + restore_last_mode(ss); + ss->grid.grid_dirty = 1; + } + else if (x == 0 && !from_held && z) { + // select page + set_pattern_offset(y << 3); + ss->grid.grid_dirty = 1; + } + else if (x == 7 && y == 6 && z) { + u8 offset = get_pattern_offset(); + if (offset) set_pattern_offset(offset - 1); + ss->grid.grid_dirty = 1; + } + else if (x == 7 && y == 7 && z) { + u8 offset = get_pattern_offset(); + if (offset < 56) set_pattern_offset(offset + 1); + ss->grid.grid_dirty = 1; + } + else if (x == 7 && y > 1 && y != 4 && y != 5 && z) { + tracker_select = y; + tracker_selected = 0; + } + return 1; + } + + // select page + if (y == 0) { + if (!z || from_held) return 1; + + switch (x) { + case 0: + case 1: + tt_mode = G_EDIT; + tt_script = x + 8; + set_edit_mode_script(tt_script); + set_mode(M_EDIT); + ss->grid.grid_dirty = 1; + break; + case 3: + if (tt_mode != G_LIVE_V) { + tt_mode = G_LIVE_V; + set_mode(M_LIVE); + set_live_submode(1); + ss->grid.grid_dirty = 1; + } + break; + case 4: + tt_mode = tt_mode == G_LIVE_G ? G_LIVE_GF : G_LIVE_G; + set_mode(M_LIVE); + set_live_submode(tt_mode == G_LIVE_G ? 2 : 3); + ss->grid.grid_dirty = 1; + break; + case 6: + tt_last_mode = tt_mode; + tt_mode = G_PRESET; + set_mode(M_PRESET_R); + ss->grid.grid_dirty = 1; + break; + case 7: + tt_last_mode = tt_mode; + tt_mode = G_TRACKER; + set_mode(M_PATTERN); + ss->grid.grid_dirty = 1; + break; + default: break; + } + grid_clear_held_keys(); + return 1; + } + + // select script for editing + if (y == 1) { + if (!z || from_held) return 1; + tt_mode = G_EDIT; + tt_script = x; + set_edit_mode_script(tt_script); + set_mode(M_EDIT); + ss->grid.grid_dirty = 1; + return 1; + } + + // presets + if (tt_mode == G_PRESET) { + if (!z) return 1; + if (y > 1 && y < 6 && !from_held) { + if (preset_write) { + preset_write = 0; + set_mode(M_PRESET_R); + } + process_preset_r_preset(x + ((y - 2) << 3)); + ss->grid.grid_dirty = 1; + } + else if (y == 6 && x == 7) { + if (preset_write) { + preset_write = 0; + set_mode(M_PRESET_R); + } + preset_line_up(); + ss->grid.grid_dirty = 1; + } + else if (y == 7 && x == 7) { + if (preset_write) { + preset_write = 0; + set_mode(M_PRESET_R); + } + preset_line_down(); + ss->grid.grid_dirty = 1; + } + else if (y == 7 && x == 2 && !from_held) { + if (preset_write) { + preset_write = 0; + set_mode(M_PRESET_R); + } + else { + process_preset_r_load(); + set_mode(M_PRESET_R); + restore_last_mode(ss); + } + ss->grid.grid_dirty = 1; + } + else if (y == 7 && x == 4 && !from_held) { + if (preset_write) { + flash_write(preset_select, ss, &scene_text); + flash_update_last_saved_scene(preset_select); + preset_write = 0; + restore_last_mode(ss); + } + else { + set_mode(M_PRESET_W); + preset_write = 1; + } + ss->grid.grid_dirty = 1; + } + else { + preset_write = 0; + set_mode(M_PRESET_R); + } + return 1; + } + + // mutes + if (y == 6) { + if (!z || from_held) return 1; + bool muted = ss_get_mute(ss, x); + ss_set_mute(ss, x, !muted); + screen_mutes_updated(); + ss->grid.grid_dirty = 1; + return 1; + } + + // trigger scripts + if (y == 7) { + if (!z) return 1; + script_triggers[x].on = 1; + script_triggers[x].ss = ss; + timer_remove(&script_triggers[x].timer); + timer_add(&script_triggers[x].timer, GRID_SCRIPT_TRIGGER, + &script_triggers_callback, (void *)&script_triggers[x]); + ss->grid.grid_dirty = 1; + run_script(ss, x); + return 1; + } + + if (variable_edit && y > 1 && y < 6) { + int16_t *v = &(ss->variables.a); + u8 ve = variable_edit - 1; + + if (!z && x > 2 && x < 5 && variable_edit == x - 2 + ((y - 2) << 1)) { + if (!variable_changed) { + if (v[ve]) { + variable_last = v[ve]; + v[ve] = 0; + } + else { + v[ve] = variable_last ? variable_last : 1; + } + } + variable_edit = 0; + set_vars_updated(); + ss->grid.grid_dirty = 1; + return 1; + } + if (!z) return 1; + + u8 v_x = 3 + (ve & 1); + if (x == v_x + 1) { + if (v[ve] < 32767) v[ve]++; + } + else if (x == v_x + 2) { + if (v[ve] < 32758) + v[ve] += 10; + else + v[ve] = 32767; + } + else if (x == v_x - 1) { + if (v[ve] > -32768) v[ve]--; + } + else if (x == v_x - 2) { + if (v[ve] > -32759) + v[ve] -= 10; + else + v[ve] = -32768; + } + + variable_changed = 1; + set_vars_updated(); + ss->grid.grid_dirty = 1; + return 1; + } + + // metro on/off + if (y == 3 && x == 0 && !from_held && !z) { + ss->variables.m_act = !ss->variables.m_act; + screen_mutes_updated(); + tele_metro_updated(); + ss->grid.grid_dirty = 1; + return 1; + } + + // kill slews/delays + if (y == 3 && x == 1 && !from_held && z) { + script_triggers[10].on = 1; + script_triggers[10].ss = ss; + timer_remove(&script_triggers[x].timer); + timer_add(&script_triggers[10].timer, GRID_SCRIPT_TRIGGER, + &script_triggers_callback, (void *)&script_triggers[10]); + clear_delays_and_slews(ss); + ss->grid.grid_dirty = 1; + return 1; + } + + // trigger metro/init + if (y == 4 && x < 2 && z) { + x += 8; + script_triggers[x].on = 1; + script_triggers[x].ss = ss; + timer_remove(&script_triggers[x].timer); + timer_add(&script_triggers[x].timer, GRID_SCRIPT_TRIGGER, + &script_triggers_callback, (void *)&script_triggers[x]); + ss->grid.grid_dirty = 1; + run_script(ss, x); + return 1; + } + + // live variables + if (tt_mode == G_LIVE_V) { + if (y > 1 && y < 6 && x > 2 && x < 5 && !from_held) { + variable_edit = z ? x - 2 + ((y - 2) << 1) : 0; + if (variable_edit) variable_changed = 0; + ss->grid.grid_dirty = 1; + return 1; + } + + if (!z) return 1; + + if (y == 3 && x == 6) { history_prev(); } + else if (y == 4 && x == 6) { + history_next(); + } + else if (y == 4 && x == 7 && !from_held) { + execute_line(); + } + + return 1; + } + + // live grid preview + if (tt_mode == G_LIVE_G || tt_mode == G_LIVE_GF) { + if (!z || from_held) return 1; + + if (y == 3 && x == 7) { + grid_page = 0; + set_grid_updated(); + ss->grid.grid_dirty = 1; + } + else if (y == 4 && x == 3) { + ss->grid.rotate = ss->grid.rotate == 0; + set_grid_updated(); + ss->grid.grid_dirty = 1; + } + else if (y == 4 && x == 5) { + grid_show_controls = !grid_show_controls; + set_grid_updated(); + ss->grid.grid_dirty = 1; + } + else if (y == 4 && x == 7) { + grid_page = 1; + set_grid_updated(); + ss->grid.grid_dirty = 1; + } + + return 1; + } + + // edit scripts + if (tt_mode == G_EDIT) { + if (!z || from_held) return 1; + + if (y > 2 && y < 6 && x > 2 && x < 5) { + u8 i = (x - 3) * 3 + y - 3; + if (i >= ss_get_script_len(ss, tt_script)) return 1; + ss_toggle_script_comment(ss, tt_script, i); + edit_mode_refresh(); + ss->grid.grid_dirty = 1; + } + else if (y == 3 && x == 7) { + for (u8 i = 0; i < ss_get_script_len(ss, tt_script); i++) + ss_set_script_comment(ss, tt_script, i, 1); + edit_mode_refresh(); + ss->grid.grid_dirty = 1; + } + else if (y == 4 && x == 7) { + for (u8 i = 0; i < ss_get_script_len(ss, tt_script); i++) + ss_set_script_comment(ss, tt_script, i, 0); + edit_mode_refresh(); + ss->grid.grid_dirty = 1; + } + return 1; + } + + return 1; +} + +void grid_process_key(scene_state_t *ss, u8 _x, u8 _y, u8 z, u8 emulated) { + if (timers_uninitialized) { + timers_uninitialized = 0; + for (u8 i = 0; i < GRID_MAX_KEY_PRESSED; i++) held_keys[i].used = 0; + } + + size_x = monome_size_x(); + size_y = monome_size_y(); + u8 x = SG.rotate && !emulated ? size_x - _x - 1 : _x; + u8 y = SG.rotate && !emulated ? size_y - _y - 1 : _y; + + if (SG.clear_held) { + grid_clear_held_keys(); + SG.clear_held = 0; + } + + if (control_mode_on ? !emulated : true) { + u8 key = (y << 4) | x; + if (z) { + for (u8 i = 0; i < GRID_MAX_KEY_PRESSED; i++) + if (!held_keys[i].used || held_keys[i].key == key) { + held_keys[i].used = 1; + held_keys[i].key = key; + held_keys[i].x = x; + held_keys[i].y = y; + held_keys[i].ss = ss; + timer_add(&held_keys[i].timer, GRID_KEY_HOLD_DELAY, + &hold_repeat_timer_callback, + (void *)&held_keys[i]); + break; + } + } + else { + for (u8 i = 0; i < GRID_MAX_KEY_PRESSED; i++) + if (held_keys[i].key == key) { + timer_remove(&held_keys[i].timer); + held_keys[i].used = 0; + } + } + } + + if (control_mode_on && !emulated) + if (grid_control_process_key(ss, x, y, z, 0)) return; + + u8 refresh = 0; + u8 scripts[SCRIPT_COUNT]; + for (u8 i = 0; i < SCRIPT_COUNT; i++) scripts[i] = 0; + + for (u8 i = 0; i < GRID_XYPAD_COUNT; i++) { + if (z && GXYC.enabled && SG.group[GXYC.group].enabled && + grid_within_area(x, y, &GXYC)) { + GXY.value_x = x - GXYC.x; + GXY.value_y = y - GXYC.y; + if (GXYC.script != -1) scripts[GXYC.script] = 1; + SG.latest_group = GXYC.group; + if (SG.group[GXYC.group].script != -1) + scripts[SG.group[GXYC.group].script] = 1; + refresh = 1; + } + } + + u16 value; + s8 held; + if (z) { + for (u8 i = 0; i < GRID_FADER_COUNT; i++) { + if (GFC.enabled && SG.group[GFC.group].enabled && + grid_within_area(x, y, &GFC)) { + held = -1; + if (GF.type & 1) { + for (u8 j = 0; j < GRID_MAX_KEY_PRESSED; j++) + if (held_keys[j].used && (held_keys[j].y != y) && + grid_within_area(held_keys[j].x, held_keys[j].y, + &GFC)) { + held = j; + break; + } + } + else { + for (u8 j = 0; j < GRID_MAX_KEY_PRESSED; j++) + if (held_keys[j].used && (held_keys[j].x != x) && + grid_within_area(held_keys[j].x, held_keys[j].y, + &GFC)) { + held = j; + break; + } + } + + switch (GF.type) { + case FADER_CH_BAR: + case FADER_CH_DOT: + if (held == -1) { + GF.slide = 0; + GF.value = x - GFC.x; + } + else { + GF.slide = 1; + GF.slide_acc = 0; + GF.slide_end = x - GFC.x; + GF.slide_delta = 16; + GF.slide_dir = GF.slide_end > GF.value; + } + break; + case FADER_CV_BAR: + case FADER_CV_DOT: + if (held == -1) { + GF.slide = 0; + GF.value = GFC.h + GFC.y - y - 1; + } + else { + GF.slide = 1; + GF.slide_acc = 0; + GF.slide_end = GFC.h + GFC.y - y - 1; + GF.slide_delta = 16; + GF.slide_dir = GF.slide_end > GF.value; + } + break; + case FADER_FH_BAR: + case FADER_FH_DOT: + if (held != -1 && + (held_keys[held].x == GFC.x || + held_keys[held].x == (GFC.x + GFC.w - 1))) + held = -1; + if (held == -1) { + GF.slide = 0; + if (x == GFC.x) { + if (GF.value) GF.value--; + } + else if (x == GFC.x + GFC.w - 1) { + if (GF.value < GFC.level) GF.value++; + } + else { + value = + ((((x - GFC.x - 1) << 1) + 1) * GFC.level) / + (GFC.w - 2); + GF.value = (value >> 1) + (value & 1); + } + } + else { + GF.slide = 1; + GF.slide_acc = 0; + if (x == GFC.x) + value = 0; + else if (x == (GFC.x + GFC.w - 1)) + value = GFC.level; + else { + value = + ((((x - GFC.x - 1) << 1) + 1) * GFC.level) / + (GFC.w - 2); + value = (value >> 1) + (value & 1); + } + GF.slide_end = value; + value = ((GFC.w - 2) << 4) / GFC.level; + if (value == 0) value = 1; + GF.slide_delta = value; + GF.slide_dir = GF.slide_end > GF.value; + } + break; + case FADER_FV_BAR: + case FADER_FV_DOT: + if (held != -1 && + (held_keys[held].y == GFC.y || + held_keys[held].y == (GFC.y + GFC.h - 1))) + held = -1; + if (held == -1) { + GF.slide = 0; + if (y == GFC.y) { + if (GF.value < GFC.level) GF.value++; + } + else if (y == GFC.y + GFC.h - 1) { + if (GF.value) GF.value--; + } + else { + value = ((((GFC.h + GFC.y - y - 2) << 1) + 1) * + GFC.level) / + (GFC.h - 2); + GF.value = (value >> 1) + (value & 1); + } + } + else { + GF.slide = 1; + GF.slide_acc = 0; + if (y == GFC.y) + value = GFC.level; + else if (y == (GFC.y + GFC.h - 1)) + value = 0; + else { + value = ((((GFC.h + GFC.y - y - 2) << 1) + 1) * + GFC.level) / + (GFC.h - 2); + value = (value >> 1) + (value & 1); + } + GF.slide_end = value; + value = ((GFC.h - 2) << 4) / GFC.level; + if (value == 0) value = 1; + GF.slide_delta = value; + GF.slide_dir = GF.slide_end > GF.value; + } + break; + } + + if (GFC.script != -1) scripts[GFC.script] = 1; + SG.latest_fader = i; + SG.latest_group = GFC.group; + if (SG.group[GFC.group].script != -1) + scripts[SG.group[GFC.group].script] = 1; + refresh = 1; + } + } + } + + for (u16 i = 0; i < GRID_BUTTON_COUNT; i++) { + if (GBC.enabled && SG.group[GBC.group].enabled && + grid_within_area(x, y, &GBC)) { + if (GB.latch) { + if (z) { + GB.state = !GB.state; + if (GBC.script != -1) scripts[GBC.script] = 1; + } + } + else { + GB.state = z; + if (GBC.script != -1) scripts[GBC.script] = 1; + } + SG.latest_button = i; + SG.latest_group = GBC.group; + if (SG.group[GBC.group].script != -1) + scripts[SG.group[GBC.group].script] = 1; + refresh = 1; + } + } + + for (u8 i = 0; i < SCRIPT_COUNT; i++) + if (scripts[i]) run_script(ss, i); + + if (refresh) SG.grid_dirty = SG.scr_dirty = 1; +} + +void grid_process_key_hold_repeat(scene_state_t *ss, u8 x, u8 y) { + if (control_mode_on) + if (grid_control_process_key(ss, x, y, 1, 1)) return; + + u8 refresh = 0; + u8 scripts[SCRIPT_COUNT]; + for (u8 i = 0; i < SCRIPT_COUNT; i++) scripts[i] = 0; + + u8 update = 0; + for (u8 i = 0; i < GRID_FADER_COUNT; i++) { + if (GFC.enabled && SG.group[GFC.group].enabled && + grid_within_area(x, y, &GFC)) { + update = 0; + if (GF.type == FADER_FH_BAR || GF.type == FADER_FH_DOT) { + if (x == GFC.x) { + if (GF.value) GF.value--; + update = 1; + } + else if (x == GFC.x + GFC.w - 1) { + if (GF.value < GFC.level) GF.value++; + update = 1; + } + } + else if (GF.type == FADER_FV_BAR || GF.type == FADER_FV_DOT) { + if (y == GFC.y) { + if (GF.value < GFC.level) GF.value++; + update = 1; + } + else if (y == GFC.y + GFC.h - 1) { + if (GF.value) GF.value--; + update = 1; + } + } + + if (update) { + if (GFC.script != -1) scripts[GFC.script] = 1; + SG.latest_fader = i; + SG.latest_group = GFC.group; + if (SG.group[GFC.group].script != -1) + scripts[SG.group[GFC.group].script] = 1; + refresh = 1; + } + } + } + + for (u8 i = 0; i < SCRIPT_COUNT; i++) + if (scripts[i]) run_script(ss, i); + + if (refresh) SG.grid_dirty = SG.scr_dirty = 1; +} + +void hold_repeat_timer_callback(void *o) { + hold_repeat_info *hr = o; + u8 is_hold = hr->used == 1; + if (is_hold) { + timer_reset_set(&hr->timer, control_mode_on && hr->x > 7 + ? GRID_KEY_REPEAT_RATE_CTL + : GRID_KEY_REPEAT_RATE); + hr->used = 2; + } + grid_process_key_hold_repeat(hr->ss, hr->x, hr->y); +} + +void grid_process_fader_slew(scene_state_t *ss) { + u8 refresh = 0; + u8 scripts[SCRIPT_COUNT]; + for (u8 i = 0; i < SCRIPT_COUNT; i++) scripts[i] = 0; + + for (u8 i = 0; i < GRID_FADER_COUNT; i++) { + if (!GF.slide) continue; + GF.slide_acc++; + if (GF.slide_acc >= GF.slide_delta) { + GF.slide_acc = 0; + if (GF.slide_dir) + GF.value++; + else + GF.value--; + if ((GF.slide_dir && GF.value >= GF.slide_end) || + (!GF.slide_dir && GF.value <= GF.slide_end)) { + GF.value = GF.slide_end; + GF.slide = 0; + } + SG.latest_fader = i; + SG.latest_group = GFC.group; + if (GFC.script != -1) run_script(ss, GFC.script); + if (SG.group[GFC.group].script != -1) + scripts[SG.group[GFC.group].script] = 1; + refresh = 1; + } + } + + for (u8 i = 0; i < SCRIPT_COUNT; i++) + if (scripts[i]) run_script(ss, i); + + if (refresh) SG.grid_dirty = SG.scr_dirty = 1; +} + +void grid_clear_held_keys() { + for (u8 i = 0; i < GRID_MAX_KEY_PRESSED; i++) { + held_keys[i].used = 0; + timer_remove(&held_keys[i].timer); + } +} + +bool grid_within_area(u8 x, u8 y, grid_common_t *gc) { + return x >= gc->x && x < (gc->x + gc->w) && y >= gc->y && + y < (gc->y + gc->h); +} + +void grid_refresh(scene_state_t *ss) { + size_x = monome_size_x(); + size_y = monome_size_y(); + + if (size_x == 0) size_x = 16; + if (size_y == 0) size_y = 8; + + grid_fill_area(0, 0, size_x, size_y, 0); + + u16 x, y; + for (u8 i = 0; i < GRID_XYPAD_COUNT; i++) { + if (GXYC.enabled && SG.group[GXYC.group].enabled) { + if (GXY.value_x || GXY.value_y) { + x = GXYC.x + GXY.value_x; + y = GXYC.y + GXY.value_y; + grid_fill_area(GXYC.x, y, GXYC.w, 1, GXYC.level); + grid_fill_area(x, GXYC.y, 1, GXYC.h, GXYC.level); + grid_fill_area(x, y, 1, 1, GRID_ON_BRIGHTNESS); + } + } + } + + u16 fv, ff, fp; + for (u8 i = 0; i < GRID_FADER_COUNT; i++) { + if (GFC.enabled && SG.group[GFC.group].enabled) { + switch (GF.type) { + case FADER_CH_BAR: + grid_fill_area(GFC.x, GFC.y, GF.value + 1, GFC.h, + GRID_ON_BRIGHTNESS); + grid_fill_area(GFC.x + GF.value + 1, GFC.y, + GFC.w - GF.value - 1, GFC.h, GFC.level); + break; + case FADER_CV_BAR: + grid_fill_area(GFC.x, GFC.y, GFC.w, GFC.h - GF.value - 1, + GFC.level); + grid_fill_area(GFC.x, GFC.y + GFC.h - GF.value - 1, GFC.w, + GF.value + 1, GRID_ON_BRIGHTNESS); + break; + case FADER_CH_DOT: + grid_fill_area(GFC.x, GFC.y, GFC.w, GFC.h, GFC.level); + grid_fill_area(GFC.x + GF.value, GFC.y, 1, GFC.h, + GRID_ON_BRIGHTNESS); + break; + case FADER_CV_DOT: + grid_fill_area(GFC.x, GFC.y, GFC.w, GFC.h, GFC.level); + grid_fill_area(GFC.x, GFC.y + GFC.h - GF.value - 1, GFC.w, + 1, GRID_ON_BRIGHTNESS); + break; + case FADER_FH_BAR: + fv = (((GFC.w - 2) << 4) * GF.value) / GFC.level; + ff = fv >> 4; + fp = fv & 15; + grid_fill_area(GFC.x, GFC.y, ff + 1, GFC.h, + GRID_ON_BRIGHTNESS); + if (fp) grid_fill_area(GFC.x + ff + 1, GFC.y, 1, GFC.h, fp); + grid_fill_area(GFC.x + GFC.w - 1, GFC.y, 1, GFC.h, + GRID_ON_BRIGHTNESS); + break; + case FADER_FV_BAR: + fv = (((GFC.h - 2) << 4) * GF.value) / GFC.level; + ff = fv >> 4; + fp = fv & 15; + grid_fill_area(GFC.x, GFC.y + GFC.h - ff - 1, GFC.w, ff + 1, + GRID_ON_BRIGHTNESS); + if (fp) + grid_fill_area(GFC.x, GFC.y + GFC.h - ff - 2, GFC.w, 1, + fp); + grid_fill_area(GFC.x, GFC.y, GFC.w, 1, GRID_ON_BRIGHTNESS); + break; + case FADER_FH_DOT: + grid_fill_area(GFC.x, GFC.y, 1, GFC.h, GRID_ON_BRIGHTNESS); + grid_fill_area(GFC.x + GFC.w - 1, GFC.y, 1, GFC.h, + GRID_ON_BRIGHTNESS); + fv = (((GFC.w - 2) << 4) * GF.value) / GFC.level; + ff = fv >> 4; + fp = fv & 15; + if (fp) + grid_fill_area(GFC.x + ff + 1, GFC.y, 1, GFC.h, fp); + else if (ff) + grid_fill_area(GFC.x + ff, GFC.y, 1, GFC.h, + GRID_ON_BRIGHTNESS); + break; + case FADER_FV_DOT: + grid_fill_area(GFC.x, GFC.y + GFC.h - 1, GFC.w, 1, + GRID_ON_BRIGHTNESS); + grid_fill_area(GFC.x, GFC.y, GFC.w, 1, GRID_ON_BRIGHTNESS); + fv = (((GFC.h - 2) << 4) * GF.value) / GFC.level; + ff = fv >> 4; + fp = fv & 15; + if (fp) + grid_fill_area(GFC.x, GFC.y + GFC.h - ff - 2, GFC.w, 1, + fp); + else if (ff) + grid_fill_area(GFC.x, GFC.y + GFC.h - ff - 1, GFC.w, 1, + GRID_ON_BRIGHTNESS); + break; + } + } + } + + for (u16 i = 0; i < GRID_BUTTON_COUNT; i++) + if (GBC.enabled && SG.group[GBC.group].enabled) + grid_fill_area(GBC.x, GBC.y, GBC.w, GBC.h, + GB.state ? GRID_ON_BRIGHTNESS : GBC.level); + + u16 led; + for (u16 i = 0; i < size_x; i++) + for (u16 j = 0; j < size_y; j++) { + led = (j << 4) + i; + if (led >= MONOME_MAX_LED_BYTES) continue; + + if (SG.leds[i][j] >= 0) + monomeLedBuffer[led] = SG.leds[i][j]; + else if (SG.leds[i][j] == LED_DIM) { + if (monomeLedBuffer[led] > 3) + monomeLedBuffer[led] -= 3; + else + monomeLedBuffer[led] = 0; + } + else if (SG.leds[i][j] == LED_BRI) { + if (monomeLedBuffer[led] > 12) + monomeLedBuffer[led] = 15; + else + monomeLedBuffer[led] += 3; + } + + if (monomeLedBuffer[led] < SG.dim) + monomeLedBuffer[led] = 0; + else + monomeLedBuffer[led] -= SG.dim; + } + + if (control_mode_on) grid_control_refresh(ss); + + u8 temp; + if (SG.rotate) { + if (size_x == 8) { + u16 a, b; + for (u16 row = 0; row < 4; row++) + for (u16 col = 0; col < 8; col++) { + a = (row << 4) + col; + b = ((7 - row) << 4) + 7 - col; + temp = monomeLedBuffer[a]; + monomeLedBuffer[a] = monomeLedBuffer[b]; + monomeLedBuffer[b] = temp; + } + } + else { + u16 total = size_x * size_y; + if (total > MONOME_MAX_LED_BYTES) total = MONOME_MAX_LED_BYTES; + for (u16 i = 0; i < (total >> 1); i++) { + temp = monomeLedBuffer[i]; + monomeLedBuffer[i] = monomeLedBuffer[total - i - 1]; + monomeLedBuffer[total - i - 1] = temp; + } + } + } + + SG.grid_dirty = 0; +} + +void grid_fill_area(u8 x, u8 y, u8 w, u8 h, s8 level) { + if (level == LED_OFF) return; + + u16 index; + u16 x_end = min(size_x, x + w); + u16 y_end = min(size_y, y + h); + + if (level == LED_DIM) { + for (u16 _x = x; _x < x_end; _x++) + for (u16 _y = y; _y < y_end; _y++) { + index = _x + (_y << 4); + if (index < MONOME_MAX_LED_BYTES) { + if (monomeLedBuffer[index] > 3) + monomeLedBuffer[index] -= 3; + else + monomeLedBuffer[index] = 0; + } + } + } + else if (level == LED_BRI) { + for (u16 _x = x; _x < x_end; _x++) + for (u16 _y = y; _y < y_end; _y++) { + index = _x + (_y << 4); + if (index < MONOME_MAX_LED_BYTES) { + if (monomeLedBuffer[index] > 12) + monomeLedBuffer[index] = 15; + else + monomeLedBuffer[index] += 3; + } + } + } + else { + for (u16 _x = x; _x < x_end; _x++) + for (u16 _y = y; _y < y_end; _y++) { + index = _x + (_y << 4); + if (index < MONOME_MAX_LED_BYTES) + monomeLedBuffer[index] = level; + } + } +} + +///////////////////////////////////////// screen functions + +void grid_screen_refresh(scene_state_t *ss, screen_grid_mode mode, u8 page, + u8 ctrl, u8 x1, u8 y1, u8 x2, u8 y2) { + switch (mode) { + case GRID_MODE_EDIT: + grid_screen_refresh_led(ss, 0, page, x1, y1, x2, y2); + if (ctrl) grid_screen_refresh_ctrl(ss, page, x1, y1, x2, y2); + grid_screen_refresh_info(ss, page, x1, y1, x2, y2); + break; + case GRID_MODE_FULL: + grid_screen_refresh_led(ss, 1, page, x1, y1, x2, y2); + break; + case GRID_MODE_OFF: + case GRID_MODE_LAST: break; + } + SG.scr_dirty = 0; +} + +void grid_screen_refresh_ctrl(scene_state_t *ss, u8 page, u8 x1, u8 y1, u8 x2, + u8 y2) { + grid_fill_area_scr(0, 0, GRID_MAX_DIMENSION, GRID_MAX_DIMENSION, 0, 0); + + u8 last_x, last_y; + + for (u16 i = 0; i < GRID_BUTTON_COUNT; i++) { + if (!SG.group[GBC.group].enabled || !GBC.enabled) continue; + last_x = GBC.x + GBC.w - 1; + last_y = GBC.y + GBC.h - 1; + if (GBC.w == 1 && GBC.h == 1) { + grid_fill_area_scr(GBC.x, GBC.y, 1, 1, 1, page); + } + else if (GBC.w == 1 && GBC.h > 1) { + grid_fill_area_scr(GBC.x, GBC.y, 1, 1, 2, page); + grid_fill_area_scr(GBC.x, GBC.y + 1, 1, GBC.h - 2, 3, page); + grid_fill_area_scr(GBC.x, last_y, 1, 1, 4, page); + } + else if (GBC.w > 1 && GBC.h == 1) { + grid_fill_area_scr(GBC.x, GBC.y, 1, 1, 5, page); + grid_fill_area_scr(GBC.x + 1, GBC.y, GBC.w - 2, 1, 6, page); + grid_fill_area_scr(last_x, GBC.y, 1, 1, 7, page); + } + else { + grid_fill_area_scr(GBC.x, GBC.y, 1, 1, 8, page); + grid_fill_area_scr(GBC.x + 1, GBC.y, GBC.w - 2, 1, 9, page); + grid_fill_area_scr(last_x, GBC.y, 1, 1, 10, page); + grid_fill_area_scr(GBC.x, GBC.y + 1, 1, GBC.h - 2, 11, page); + grid_fill_area_scr(last_x, GBC.y + 1, 1, GBC.h - 2, 12, page); + grid_fill_area_scr(GBC.x, last_y, 1, 1, 13, page); + grid_fill_area_scr(GBC.x + 1, last_y, GBC.w - 2, 1, 14, page); + grid_fill_area_scr(last_x, last_y, 1, 1, 15, page); + } + } + + for (u16 i = 0; i < GRID_FADER_COUNT; i++) { + if (!SG.group[GFC.group].enabled || !GFC.enabled) continue; + last_x = GFC.x + GFC.w - 1; + last_y = GFC.y + GFC.h - 1; + if (GFC.w == 1 && GFC.h == 1) { + grid_fill_area_scr(GFC.x, GFC.y, 1, 1, 1, page); + } + else if (GFC.w == 1 && GFC.h > 1) { + grid_fill_area_scr(GFC.x, GFC.y, 1, 1, 2, page); + grid_fill_area_scr(GFC.x, GFC.y + 1, 1, GFC.h - 2, 3, page); + grid_fill_area_scr(GFC.x, last_y, 1, 1, 4, page); + } + else if (GFC.w > 1 && GFC.h == 1) { + grid_fill_area_scr(GFC.x, GFC.y, 1, 1, 5, page); + grid_fill_area_scr(GFC.x + 1, GFC.y, GFC.w - 2, 1, 6, page); + grid_fill_area_scr(last_x, GFC.y, 1, 1, 7, page); + } + else { + grid_fill_area_scr(GFC.x, GFC.y, 1, 1, 8, page); + grid_fill_area_scr(GFC.x + 1, GFC.y, GFC.w - 2, 1, 9, page); + grid_fill_area_scr(last_x, GFC.y, 1, 1, 10, page); + grid_fill_area_scr(GFC.x, GFC.y + 1, 1, GFC.h - 2, 11, page); + grid_fill_area_scr(last_x, GFC.y + 1, 1, GFC.h - 2, 12, page); + grid_fill_area_scr(GFC.x, last_y, 1, 1, 13, page); + grid_fill_area_scr(GFC.x + 1, last_y, GFC.w - 2, 1, 14, page); + grid_fill_area_scr(last_x, last_y, 1, 1, 15, page); + } + } + + u8 l, _y, __y; + u16 d; + for (u16 y = 0; y < 48; y++) { + l = y >> 3; + d = 10 + ((y & 7) << 7); + _y = y / 6; + __y = y % 6; + for (u16 x = 0; x < 96; x++) + if ((1 << (5 - (x % 6))) & glyph[screen[x / 6][_y]][__y]) + line[l].data[x + d] = 10; + } + + return; +} + +void grid_screen_refresh_led(scene_state_t *ss, u8 full_grid, u8 page, u8 x1, + u8 y1, u8 x2, u8 y2) { + grid_fill_area_scr(0, 0, GRID_MAX_DIMENSION, GRID_MAX_DIMENSION, 0, 0); + + u16 x, y; + for (u8 i = 0; i < GRID_XYPAD_COUNT; i++) { + if (GXYC.enabled && SG.group[GXYC.group].enabled) { + if (GXY.value_x || GXY.value_y) { + x = GXYC.x + GXY.value_x; + y = GXYC.y + GXY.value_y; + grid_fill_area_scr(GXYC.x, y, GXYC.w, 1, GXYC.level, page); + grid_fill_area_scr(x, GXYC.y, 1, GXYC.h, GXYC.level, page); + grid_fill_area_scr(x, y, 1, 1, GRID_ON_BRIGHTNESS, page); + } + } + } + + u16 fv, ff, fp; + for (u8 i = 0; i < GRID_FADER_COUNT; i++) { + if (GFC.enabled && SG.group[GFC.group].enabled) { + switch (GF.type) { + case FADER_CH_BAR: + grid_fill_area_scr(GFC.x, GFC.y, GF.value + 1, GFC.h, + GRID_ON_BRIGHTNESS, page); + grid_fill_area_scr(GFC.x + GF.value + 1, GFC.y, + GFC.w - GF.value - 1, GFC.h, GFC.level, + page); + break; + case FADER_CV_BAR: + grid_fill_area_scr(GFC.x, GFC.y, GFC.w, + GFC.h - GF.value - 1, GFC.level, page); + grid_fill_area_scr(GFC.x, GFC.y + GFC.h - GF.value - 1, + GFC.w, GF.value + 1, GRID_ON_BRIGHTNESS, + page); + break; + case FADER_CH_DOT: + grid_fill_area_scr(GFC.x, GFC.y, GFC.w, GFC.h, GFC.level, + page); + grid_fill_area_scr(GFC.x + GF.value, GFC.y, 1, GFC.h, + GRID_ON_BRIGHTNESS, page); + break; + case FADER_CV_DOT: + grid_fill_area_scr(GFC.x, GFC.y, GFC.w, GFC.h, GFC.level, + page); + grid_fill_area_scr(GFC.x, GFC.y + GFC.h - GF.value - 1, + GFC.w, 1, GRID_ON_BRIGHTNESS, page); + break; + case FADER_FH_BAR: + fv = (((GFC.w - 2) << 4) * GF.value) / GFC.level; + ff = fv >> 4; + fp = fv & 15; + grid_fill_area_scr(GFC.x, GFC.y, ff + 1, GFC.h, + GRID_ON_BRIGHTNESS, page); + if (fp) + grid_fill_area_scr(GFC.x + ff + 1, GFC.y, 1, GFC.h, fp, + page); + grid_fill_area_scr(GFC.x + GFC.w - 1, GFC.y, 1, GFC.h, + GRID_ON_BRIGHTNESS, page); + break; + case FADER_FV_BAR: + fv = (((GFC.h - 2) << 4) * GF.value) / GFC.level; + ff = fv >> 4; + fp = fv & 15; + grid_fill_area_scr(GFC.x, GFC.y + GFC.h - ff - 1, GFC.w, + ff + 1, GRID_ON_BRIGHTNESS, page); + if (fp) + grid_fill_area_scr(GFC.x, GFC.y + GFC.h - ff - 2, GFC.w, + 1, fp, page); + grid_fill_area_scr(GFC.x, GFC.y, GFC.w, 1, + GRID_ON_BRIGHTNESS, page); + break; + case FADER_FH_DOT: + grid_fill_area_scr(GFC.x, GFC.y, 1, GFC.h, + GRID_ON_BRIGHTNESS, page); + grid_fill_area_scr(GFC.x + GFC.w - 1, GFC.y, 1, GFC.h, + GRID_ON_BRIGHTNESS, page); + fv = (((GFC.w - 2) << 4) * GF.value) / GFC.level; + ff = fv >> 4; + fp = fv & 15; + if (fp) + grid_fill_area_scr(GFC.x + ff + 1, GFC.y, 1, GFC.h, fp, + page); + else if (ff) + grid_fill_area_scr(GFC.x + ff, GFC.y, 1, GFC.h, + GRID_ON_BRIGHTNESS, page); + break; + case FADER_FV_DOT: + grid_fill_area_scr(GFC.x, GFC.y + GFC.h - 1, GFC.w, 1, + GRID_ON_BRIGHTNESS, page); + grid_fill_area_scr(GFC.x, GFC.y, GFC.w, 1, + GRID_ON_BRIGHTNESS, page); + fv = (((GFC.h - 2) << 4) * GF.value) / GFC.level; + ff = fv >> 4; + fp = fv & 15; + if (fp) + grid_fill_area_scr(GFC.x, GFC.y + GFC.h - ff - 2, GFC.w, + 1, fp, page); + else if (ff) + grid_fill_area_scr(GFC.x, GFC.y + GFC.h - ff - 1, GFC.w, + 1, GRID_ON_BRIGHTNESS, page); + break; + } + } + } + + for (u16 i = 0; i < GRID_BUTTON_COUNT; i++) + if (GBC.enabled && SG.group[GBC.group].enabled) + grid_fill_area_scr(GBC.x, GBC.y, GBC.w, GBC.h, + GB.state ? GRID_ON_BRIGHTNESS : GBC.level, page); + + u16 pd = page ? 8 : 0; + s8 l; + for (u16 i = 0; i < GRID_MAX_DIMENSION; i++) + for (u16 j = 0; j < GRID_MAX_DIMENSION / 2; j++) { + l = SG.leds[i][j + pd]; + if (l >= 0) + screen[i][j] = l; + else if (l == LED_DIM) { + if (screen[i][j] > 3) + screen[i][j] -= 3; + else + screen[i][j] = 0; + } + else if (l == LED_BRI) { + if (screen[i][j] > 12) + screen[i][j] = 15; + else + screen[i][j] += 3; + } + } + + u16 _y, cell, size, left; + if (full_grid) { + cell = 8; + size = 5; + left = 0; + for (int i = 0; i < 8; i++) region_fill(&line[i], 0); + } + else { + cell = 6; + size = 4; + left = 10; + for (int i = 0; i < 6; i++) region_fill(&line[i], 0); + } + + u8 _line; + u16 _data; + for (u16 x = 0; x < GRID_MAX_DIMENSION; x++) + for (u16 y = 0; y < GRID_MAX_DIMENSION / 2; y++) + for (u16 j = 0; j < size; j++) { + _y = y * cell + j + 1; + _line = _y >> 3; + for (u16 i = 0; i < size; i++) { + _data = left + x * cell + i + ((_y & 7) << 7) + 1; + if (_line > 7 || _data > 1023) continue; + if (screen[x][y] == 0) { + if (i == 0 || i == size - 1 || j == 0 || j == size - 1) + line[_line].data[_data] = 1; + } + else + line[_line].data[_data] = screen[x][y]; + } + } + + // draw selected area + + u8 _x1, _x2; + u16 _y1, _y2; + _x1 = min(x1, x2) * cell; + _y1 = min(y1, y2) * cell; + if (full_grid) { + _x2 = (max(x1, x2) + 1) * cell - 2; + _y2 = (max(y1, y2) + 1) * cell - 2; + } + else { + _x2 = (max(x1, x2) + 1) * cell - 1; + _y2 = (max(y1, y2) + 1) * cell - 1; + } + + u8 show_y1, show_y2; + show_y1 = show_y2 = true; + + u16 p = cell << 3; + if (page) { + if (_y2 < p) return; + if (_y1 < p) { + show_y1 = false; + _y1 = p; + }; + _y1 -= p; + _y2 -= p; + } + else { + if (_y1 >= p) return; + if (_y2 >= p) { + show_y2 = false; + _y2 = p - 1; + } + } + + if (show_y1) { + _line = _y1 >> 3; + _data = left + ((_y1 & 7) << 7); + for (u16 x = _x1; x <= _x2; x++) + line[_line].data[_data + x] = x & 1 ? 4 : 15; + } + if (show_y2) { + _line = _y2 >> 3; + _data = left + ((_y2 & 7) << 7); + if (full_grid) + for (u16 x = _x1 + 1; x <= _x2; x++) + line[_line].data[_data + x] = x & 1 ? 4 : 15; + else + for (u16 x = _x1 + 1; x <= _x2; x++) + line[_line].data[_data + x] = x & 1 ? 15 : 4; + } + + if (full_grid) + for (u16 y = _y1; y <= _y2; y++) { + line[y >> 3].data[left + ((y & 7) << 7) + _x1] = + line[y >> 3].data[left + ((y & 7) << 7) + _x2] = y & 1 ? 4 : 15; + } + else + for (u16 y = _y1; y <= _y2; y++) { + line[y >> 3].data[left + ((y & 7) << 7) + _x1] = y & 1 ? 4 : 15; + line[y >> 3].data[left + ((y & 7) << 7) + _x2] = y & 1 ? 15 : 4; + } +} + +static void grid_screen_refresh_info(scene_state_t *ss, u8 page, u8 x1, u8 y1, + u8 x2, u8 y2) { + char s[32]; + u8 area_x, area_y, area_w, area_h; + + if (x1 < x2) { + area_x = x1; + area_w = x2 + 1 - x1; + } + else { + area_x = x2; + area_w = x1 + 1 - x2; + } + if (y1 < y2) { + area_y = y1; + area_h = y2 + 1 - y1; + } + else { + area_y = y2; + area_h = y1 + 1 - y2; + } + + s[1] = 0; + s[0] = 'G'; + font_string_region_clip_right(&line[0], s, 127, 0, 1, 0); + s[0] = 'X'; + font_string_region_clip_right(&line[2], s, 127, 0, 1, 0); + s[0] = 'Y'; + font_string_region_clip_right(&line[3], s, 127, 0, 1, 0); + s[0] = 'W'; + font_string_region_clip_right(&line[4], s, 128, 0, 1, 0); + s[0] = 'H'; + font_string_region_clip_right(&line[5], s, 127, 0, 1, 0); + + itoa(ss->grid.current_group, s, 10); + font_string_region_clip_right(&line[0], s, 117, 0, 8, 0); + itoa(area_x, s, 10); + font_string_region_clip_right(&line[2], s, 117, 0, 8, 0); + itoa(area_y, s, 10); + font_string_region_clip_right(&line[3], s, 117, 0, 8, 0); + itoa(area_w, s, 10); + font_string_region_clip_right(&line[4], s, 117, 0, 8, 0); + itoa(area_h, s, 10); + font_string_region_clip_right(&line[5], s, 117, 0, 8, 0); + + for (u16 j = 0; j < 9; j += 2) line[j >> 3].data[119 + ((j & 7) << 7)] = 1; + for (u16 j = 17; j < 48; j += 2) + line[j >> 3].data[119 + ((j & 7) << 7)] = 1; + + // icons + + if (page == 0) { + for (u16 i = 0; i < 5; i++) + line[0].data[i] = line[0].data[i + 128] = 10; + line[0].data[0 + 256] = 1; + line[0].data[4 + 256] = 1; + line[0].data[0 + 384] = 1; + line[0].data[4 + 384] = 1; + for (u16 i = 512; i < 517; i++) line[0].data[i] = 1; + } + else { + for (u16 i = 0; i < 5; i++) line[0].data[i] = 1; + line[0].data[0 + 128] = 1; + line[0].data[4 + 128] = 1; + line[0].data[0 + 256] = 1; + line[0].data[4 + 256] = 1; + for (u16 i = 384; i < 389; i++) + line[0].data[i] = line[0].data[i + 128] = 10; + } + + u8 l = ss->grid.rotate ? 10 : 1; + line[1].data[0 + 0] = l; + line[1].data[1 + 0] = l; + line[1].data[2 + 0] = l; + line[1].data[2 + 128] = l; + line[1].data[0 + 256] = l; + line[1].data[2 + 256] = l; + line[1].data[4 + 256] = l; + line[1].data[1 + 384] = l; + line[1].data[2 + 384] = l; + line[1].data[3 + 384] = l; + line[1].data[2 + 512] = l; +} + +void grid_fill_area_scr(u8 x, u8 y, u8 w, u8 h, s8 level, u8 page) { + if (level == LED_OFF) return; + + u16 x_end = min(GRID_MAX_DIMENSION, x + w); + u16 y1, y2; + y1 = y; + y2 = min(GRID_MAX_DIMENSION, y + h) - 1; + + if (page) { + if (y2 < 8) return; + if (y1 < 8) y1 = 8; + y1 -= 8; + y2 -= 8; + } + else { + if (y1 >= 8) return; + if (y2 >= 8) y2 = 7; + } + + if (level == LED_DIM) { + for (u16 _x = x; _x < x_end; _x++) + for (u16 _y = y1; _y <= y2; _y++) + if (screen[_x][_y] > 3) + screen[_x][_y] -= 3; + else + screen[_x][_y] = 0; + } + else if (level == LED_BRI) { + for (u16 _x = x; _x < x_end; _x++) + for (u16 _y = y1; _y <= y2; _y++) + if (screen[_x][_y] > 12) + screen[_x][_y] = 15; + else + screen[_x][_y] += 3; + } + else { + for (u16 _x = x; _x < x_end; _x++) + for (u16 _y = y1; _y <= y2; _y++) screen[_x][_y] = level; + } +} diff --git a/module/grid.h b/module/grid.h new file mode 100644 index 00000000..302121be --- /dev/null +++ b/module/grid.h @@ -0,0 +1,31 @@ +#ifndef _GRID_H_ +#define _GRID_H_ + +#include "monome.h" +#include "state.h" + +#define SG ss->grid +#define GB ss->grid.button[i] +#define GBC ss->grid.button[i].common +#define GF ss->grid.fader[i] +#define GFC ss->grid.fader[i].common +#define GXY ss->grid.xypad[i] +#define GXYC ss->grid.xypad[i].common + +typedef enum { + GRID_MODE_OFF = 0, + GRID_MODE_EDIT, + GRID_MODE_FULL, + GRID_MODE_LAST +} screen_grid_mode; + +extern void grid_set_control_mode(u8 control, u8 mode, scene_state_t *ss); +extern void grid_metro_triggered(scene_state_t *ss); +extern void grid_refresh(scene_state_t *ss); +extern void grid_screen_refresh(scene_state_t *ss, screen_grid_mode mode, + u8 page, u8 ctrl, u8 x1, u8 y1, u8 x2, u8 y2); +extern void grid_process_key(scene_state_t *ss, u8 x, u8 y, u8 z, u8 emulated); +extern void grid_process_fader_slew(scene_state_t *ss); +extern void grid_clear_held_keys(void); + +#endif diff --git a/module/help_mode.c b/module/help_mode.c index 66c2d5c5..05f60f45 100644 --- a/module/help_mode.c +++ b/module/help_mode.c @@ -15,10 +15,10 @@ //////////////////////////////////////////////////////////////////////////////// // Help text /////////////////////////////////////////////////////////////////// -#define HELP_PAGES 8 +#define HELP_PAGES 13 -#define HELP1_LENGTH 47 -const char* help1[HELP1_LENGTH] = { "1/8 HELP", +#define HELP1_LENGTH 59 +const char* help1[HELP1_LENGTH] = { "1/13 HELP", "[ ] NAVIGATE HELP PAGES", "UP/DOWN TO SCROLL", " ", @@ -40,14 +40,26 @@ const char* help1[HELP1_LENGTH] = { "1/8 HELP", "ENTER|EXECUTE", "UP|PREVIOUS", "SH-BSP|CLEAR", + "CTRL-L/R|JUMP WORDS", "~|TOGGLE VARS", + "ALT-G|GRID VISUALIZER", + "ALT-ARROWS|MOVE IN GRID", + "ALT-SH-ARRS|SELECT AREA", + "ALT-SPACE|PRESS IN GRID", + "ALT-PRTSC|INSERT X Y W H", + "ALT-/|CHANGE GRID PAGE", + "ALT-\\|TOGGLE CONTROL VIEW", " ", "// EDIT", "[ ]|PREV, NEXT SCRIPT", "ENTER|ADD/OVERWRITE", + "CTRL-Z|UNDO", "SH-ENTER|INSERT", "SH-BSP|CLEAR", - "ALT-SLASH|DISABLE LINE", + "SH-UP/DOWN|SELECT LINES", + "ALT-SLASH|DISABLE LINE(S)", + "ALT-DELETE|DELETE LINE(S)", + "ALT-UP/DN|MOVE LINE(S)", " ", "// PATTERN", "ARROWS|NAVIGATE", @@ -67,7 +79,7 @@ const char* help1[HELP1_LENGTH] = { "1/8 HELP", "SHIFT-2|SHOW/HIDE TURTLE" }; #define HELP2_LENGTH 13 -const char* help2[HELP2_LENGTH] = { "2/8 VARIABLES", +const char* help2[HELP2_LENGTH] = { "2/13 VARIABLES", " ", "X, Y, Z|GENERAL PURPOSE", "T|USE FOR TIME", @@ -81,8 +93,8 @@ const char* help2[HELP2_LENGTH] = { "2/8 VARIABLES", "Q.N|SET Q LENGTH", "Q.AVG|AVERAGE OF ALL Q" }; -#define HELP3_LENGTH 22 -const char* help3[HELP3_LENGTH] = { "3/8 PARAMETERS", +#define HELP3_LENGTH 26 +const char* help3[HELP3_LENGTH] = { "3/13 PARAMETERS", " ", "TR A-D|SET TR VALUE (0,1)", "TR.TIME A-D|TR PULSE TIME", @@ -92,7 +104,11 @@ const char* help3[HELP3_LENGTH] = { "3/8 PARAMETERS", "CV.OFF 1-4|ADD CV OFFSET", " ", "IN|GET IN JACK VAL", + "IN.SCALE X Y", + " SCALE IN RANGE", "PARAM|GET KNOB VAL", + "PARAM.SCALE X Y", + " SCALE KNOB RANGE", " ", "M|METRO TIME (MS)", "M.ACT|ENABLE METRO (0/1)", @@ -105,8 +121,8 @@ const char* help3[HELP3_LENGTH] = { "3/8 PARAMETERS", "SCENE|GET/SET SCENE #", "LAST N|GET SCRIPT LAST RUN" }; -#define HELP4_LENGTH 10 -const char* help4[HELP4_LENGTH] = { "4/8 DATA AND TABLES", +#define HELP4_LENGTH 11 +const char* help4[HELP4_LENGTH] = { "4/13 DATA AND TABLES", " ", "ALL PARAMS HAVE 16B RANGE", "-32768 TO 32767", @@ -115,10 +131,11 @@ const char* help4[HELP4_LENGTH] = { "4/8 DATA AND TABLES", "N 0-127|CONVERT TO 1V/8VE", "V 0-10|VOLT LOOKUP", "VV 0-1000|V WITH 2 DECIMALS", - "BPM 2-MAX|MS PER BPM" }; + "BPM 2-MAX|MS PER BPM", + "EXP X|EXPO LOOKUP" }; -#define HELP5_LENGTH 46 -const char* help5[HELP5_LENGTH] = { "5/8 OPERATORS", +#define HELP5_LENGTH 56 +const char* help5[HELP5_LENGTH] = { "5/13 OPERATORS", " ", "RAND A|RANDOM 0 - A", "RRAND A B|RANDOM A - B", @@ -146,6 +163,7 @@ const char* help5[HELP5_LENGTH] = { "5/8 OPERATORS", "LT A B|A LESS THAN B", "EZ A|A EQUALS 0", "NZ A|A NOT EQUAL TO 0", + "? A B C|TERNARY IF", " ", "RSH A B|BITSHIFT A RIGHT B", "LSH A B|BITSHIFT A LEFT B", @@ -159,14 +177,23 @@ const char* help5[HELP5_LENGTH] = { "5/8 OPERATORS", " ", "LIM A B C|CLAMP A WITHIN B-C", "WRAP A B C|WRAP A AROUND B-C", + "SCALE A B X Y I", + " SCALE I FROM A..B TO X..Y", "QT A B|QUANTIZE A TO B*X", " ", + "// EUCLIDEAN OPERATOR", + "ER F L I", + " F = FILL (1-32)", + " L = LENGTH (1-32)", + " I = STEP (ANY)", + " RETURNS 0 OR 1", + " ", "// SPECIAL OPERATORS", "TR.TOG X|FLIP STATE OF TR X", "TR.PULSE X|PULSE TR X" }; #define HELP6_LENGTH 31 -const char* help6[HELP6_LENGTH] = { "6/8 PRE :", +const char* help6[HELP6_LENGTH] = { "6/13 PRE :", " ", "EACH PRE NEEDS A : FOLLOWED", "BY A COMMAND TO OPERATE ON", @@ -196,12 +223,10 @@ const char* help6[HELP6_LENGTH] = { "6/8 PRE :", "OTHER:|EXECUTE OTHERWISE", "SYNC X|SYNC TO STEP X", " ", - "BREAK|STOP EXECUTION" - -}; + "BREAK|STOP EXECUTION" }; -#define HELP7_LENGTH 26 -const char* help7[HELP7_LENGTH] = { "7/8 PATTERNS", +#define HELP7_LENGTH 37 +const char* help7[HELP7_LENGTH] = { "7/13 PATTERNS", " ", "// DIRECT ACCESS", "P A|GET VAL AT INDEX A", @@ -226,9 +251,156 @@ const char* help7[HELP7_LENGTH] = { "7/8 PATTERNS", "P.I A|GET/SET POSITION", "P.HERE A|GET/SET VAL AT P.I", "P.NEXT A|GET/SET NEXT POS", - "P.PREV A|GET/SET PREV POS" }; -#define HELP8_LENGTH 17 -const char* help8[HELP8_LENGTH] = { "8/8 TURTLE", + "P.PREV A|GET/SET PREV POS", + "P.RND A|GET RANDOM VALUE", + "P.MIN A|GET MIN VALUE", + "P.MAX A|GET MAX VALUE", + " ", + "// INCREMENT/DECREMENT", + "P.+ A B|INC VALUE A BY B", + "P.- A B|DEC VALUE A BY B", + "P.+W A B C D", + " |INC AND WRAP TO C..D", + "P.-W A B C D", + " |DEC AND WRAP TO C..D" }; +#define HELP8_LENGTH 135 +const char* help8[HELP8_LENGTH] = { "8/13 GRID", + " ", + "G.RST|RESET EVERYTHING", + "G.CLR|CLEAR ALL LEDS", + "G.DIM level|SET DIM LEVEL", + "G.ROTATE 1/0", + " |ROTATE GRID", + "G.KEY x y action", + " |EMULATE KEY PRESS", + " ", + "GROUPS", + "G.GRP id|CURRENT GROUP", + "G.GRP.EN id|ENABLE GROUP", + "G.GRP.RST id|RESET GROUP", + "G.GRP.SW id|SWITCH TO GROUP", + "G.GRP.SC id|ASSIGN SCRIPT", + "G.GRPI|GET LATEST GROUP", + " ", + "LEDS / RECTANGLES", + "-1 DIM", + "-2 BRIGHTEN", + "-3 CLEAR", + "G.LED x y l|DRAW DOT", + "G.LED.C x y|CLEAR DOT", + "G.REC x y w h fill border", + "G.RCT x1 y1 x2 y2 fill border", + " DRAW RECTANGLE", + " ", + "BUTTONS", + "G.BTN id x y w h ", + " latch level script", + "G.GBT grp id x y w h", + " latch level script", + "G.BTX id x y w h", + " lt lvl scr cols rows", + "G.GBX grp id x y w h lt", + " lt lvl scr cols rows", + " ", + "G.BTN.EN id", + " ENABLE BUTTON", + "G.BTN.X id|G.BTN.X id x", + " GET OR SET X COORDINATE", + "G.BTN.Y id|G.BTN.Y id y", + " GET OR SET Y COORDINATE", + "G.BTN.V id|G.BTN.V id val", + " GET OR SET VALUE", + "G.BTN.L id G.BTN.L id level", + " GET OR SET LEVEL", + "G.BTNI", + " ID OF LAST PRESSED", + "G.BTNX|G.BTNX x", + " GET/SET X OF LAST PR", + "G.BTNY|G.BTNY y", + " GET/SET Y OF LAST PR", + "G.BTNV|G.BTNV value", + " GET/SET VALUE OF LAST PR", + "G.BTNL G.BTNL level", + " GET/SET LEVEL OF LAST PRESSED", + "G.BTN.SW id", + " SWITCH BUTTON", + "G.BTN.PR id action", + " EMULATE BUTTON PRESS", + "G.GBTN.V group value", + " SET VALUE FOR ALL IN GROUP", + "G.GBTN.L grp odd_lvl even_lvl", + " SET LEVEL FOR ALL IN GROUP", + "G.GBTN.C group", + " GET COUNT OF ALL PRESSED", + "G.GBTN.I group index", + " GET IDS OF PRESSED BY INX", + "G.GBTN.W group", + " GET PRESSED WIDTH", + "G.GBTN.H group", + " GET PRESSED HEIGHT", + "G.GBTN.X1 group", + " GET LEFTMOST PRESSED X", + "G.GBTN.X2 group", + " GET RIGHTMOST PRESSED X", + "G.GBTN.Y1 group", + " GET HIGHEST PRESSED", + "G.GBTN.Y2 group", + " GET LOWEST PRESSED", + " ", + "FADERS", + "0 COARSE HORIZ BAR", + "1 COARSE VERT BAR", + "2 COARSE HORIZ DOT", + "3 COARSE VERT DOT", + "4 FINE HORIZ BAR", + "5 FINE VERT BAR", + "6 FINE HORIZ DOT", + "7 FINE VERT DOT", + "G.FDR id x y w h", + " type level script", + "G.GFD group id x y w h", + " type level script", + "G.FDX id x y w h", + " type lvl scr cols rows", + "G.GFX group id x y w h", + " type lvl scr cols rows", + " ", + "G.FDR.EN id|G.FDR.EN id 1/0", + " ENABLE FADER", + "G.FDR.X id|G.FDR.X id x", + " GET OR SET X COORDINATE", + "G.FDR.Y id|G.FDR.Y id y", + " GET OR SET Y COORDINATE", + "G.FDR.V id|G.FDR.V id value", + " GET OR SET VALUE", + "G.FDR.N id|G.FDR.N id value", + " GET OR SET VALUE (UNITS)", + "G.FDR.L id|G.FDR.L id level", + " GET OR SET LEVEL", + "G.FDRI", + " ID OF LAST PRESSED", + "G.FDRX|G.FDRX x", + " GET/SET X OF LAST PRESSED", + "G.FDRY|G.FDRY y", + " GET/SET Y OF LAST PRESSED", + "G.FDRV|G.FDRV value", + " GET/SET VALUE OF LAST PR", + "G.FDRN|G.FDRN value", + " AS ABOVE IN LED UNITS", + "G.FDRL G.FDRL level", + " GET/SET LEVEL OF LAST PR", + "G.FDR.PR id value", + " EMULATE FADER PRESS", + "G.GFDR.V group value", + " SET VALUE FOR ALL IN GROUP", + "G.GFDR.N group value", + " AS ABOVE IN LED UNITS", + "G.GFDR.L grp odd_lvl even_lvl", + " SET LEVEL FOR ALL IN GROUP", + "G.GFDR.RN group min max", + " SET FADER RANGE FOR .V" }; +#define HELP9_LENGTH 17 +const char* help9[HELP9_LENGTH] = { "9/13 TURTLE", " ", "// CRAWLS TRACKER DATA", "@|GET/SET DATA", @@ -246,16 +418,394 @@ const char* help8[HELP8_LENGTH] = { "8/8 TURTLE", "@SCRIPT N|GET/SET EDGE SCRIPT", "@SHOW 1/0|DISPLAY < ON TRACKER" }; +#define HELP10_LENGTH 36 +const char* help10[HELP10_LENGTH] = { "10/13 TELEX INPUT", + " ", + "TI.PARAM X|(TI.PRM)", + " GET KNOB VALUE", + "TI.PARAM.QT X", + " KNOB VALUE QUANTIZED", + "TI.PARAM.N X", + " KNOB AS SCALE NOTE", + "TI.PARAM.SCALE X", + " SELECT KNOB QUANT SCALE", + "TI.PARAM.MAP X Y Z", + " MAP KNOB VALUE TO Y..Z", + "TI.IN X", + " GET INPUT VAL", + "TI.IN.QT X", + " IN VALUE QUANTIZED", + "TI.IN.N X", + " IN AS SCALE NOTE", + "TI.IN.SCALE X", + " SELECT IN QUANT SCALE", + "TI.IN.MAP X Y Z", + " MAP IN VALUE TO Y..Z", + "TI.PARAM.INIT X", + " RESET KNOB X", + "TI.IN.INIT X", + " RESET IN X", + "TI.INIT D", + " RESET DEVICE D", + "TI.PARAM.CALIB X Y", + " CALIBRATE KNOB X", + "TI.IN.CALIB X Y", + " CALIBRATE IN X", + "TI.STORE D", + " STORE CALIB FOR DEVICE D", + "TI.RESET D", + " RESET CALIB FOR DEVICE D" }; + +#define HELP11_LENGTH 164 +const char* help11[HELP11_LENGTH] = { "11/13 TELEX OUTPUT", + " ", + "TO.TR X Y", + " SET TR VALUE (0/1)", + "TO.TR.TOG X", + " TOGGLE TR", + "TO.TR.P X", + " PULSE TR", + "TO.TR.P.DIV X Y", + " SET TR X CLOCK DIV TO Y", + "TO.TR.P.MUTE X Y", + " MUTE TR X (0/1)", + "TO.TR.TIME X Y", + " SET TR PULSE TIME (MS)", + "TO.TR.TIME.S X Y", + " SET TR PULSE TIME (SEC)", + "TO.TR.TIME.M", + " SET TR PULSE TIME (MIN)", + "TO.TR.WIDTH X Y", + " SET TR DUTY (%)", + "TO.TR.POL X Y", + " SET TR POLARITY", + "TO.TR.M.ACT X Y", + " ENABLE METRO (0/1)", + "TO.TR.M X Y", + " SET METRO RATE (MS)", + "TO.TR.M.S X Y", + " SET METRO RATE (SEC)", + "TO.TR.M.M X Y", + " SET METRO RATE (MIN)", + "TO.TR.M.BPM X Y", + " SET METRO RATE (BPM)", + "TO.TR.M.COUNT X Y", + " SET METRO REPEAT", + "TO.TR.M.MUL X Y", + " SET METRO MULT", + "TO.TR.M.SYNC X", + " SYNC METRO", + "TO.M.ACT D Y", + " ENABLE DEVICE METRO (0/1)", + "TO.M D Y", + " SET DEVICE METRO RATE (MS)", + "TO.M.S D Y", + " AS ABOVE IN SECONDS", + "TO.M.M D Y", + " AS ABOVE IN MINUTES", + "TO.M.BPM D Y", + " AS ABOVE IN BPM", + "TO.M.COUNT D Y", + " SET DEVICE METRO REPEAT", + "TO.M.SYNC D", + " SYNC DEVICE METRO", + "TO.CV X Y", + " SET CV WITH SLEW", + "TO.CV.SLEW X Y", + " SET CV SLEW (MS)", + "TO.CV.SLEW.S X Y", + " SET CV SLEW (SEC)", + "TO.CV.SLEW.M", + " SET CV SLEW (MIN)", + "TO.CV.SET X Y", + " SET CV WITH NO SLEW", + "TO.CV.OFF X Y", + " SET CV OFFSET", + "TO.CV.QT X Y", + " SET CV QUANTIZED", + "TO.CV.QT.SET X Y", + " AS ABOVE NO SLEW", + "TO.CV.N X Y", + " SET CV TO SCALE NOTE", + "TO.CV.N.SET X Y", + " AS ABOVE NO SLEW", + "TO.CV.LOG X Y", + " SET CV LOG MODE", + "TO.OSC X Y", + " SET CV OSC FREQ", + "TO.OSC.SET X Y", + " AS ABOVE NO SLEW", + "TO.OSC.QT X Y", + " SET CV OSC QUANTIZED", + "TO.OSC.QT.SET X Y", + " AS ABOVE NO SLEW", + "TO.OSC.N X Y", + " SET CV OSC TO SCALE NOTE", + "TO.OSC.N.SET X Y", + " AS ABOVE NO SLEW", + "TO.OSC.FQ X Y", + " SET CV OSC (HZ)", + "TO.OSC.LFO X Y", + " SET CV LFO RATE (MILLIHZ)", + "TO.OSC.LFO.SET X Y", + " AS ABOVE NO SLEW", + "TO.OSC.CYC X Y", + " SET CV OSC CYCLE (MS)", + "TO.OSC.CYC.SET X Y", + " AS ABOVE NO SLEW", + "TO.OSC.CYC.S X Y", + " SET CV OSC CYCLE (SEC)", + "TO.OSC.CYC.S.SET X Y", + " AS ABOVE NO SLEW", + "TO.OSC.CYC.M X Y", + " SET CV OSC CYCLE (MIN)", + "TO.OSC.CYC.M.SET X Y", + " AS ABOVE NO SLEW", + "TO.OSC.SCALE X Y", + " CV X QT SCALE Y", + "TO.OSC.WAVE X Y", + " SELECT CV OSC WAVEFORM", + "TO.OSC.RECT X Y", + " SET OSC RECTIFY (-2..2)", + "TO.OSC.WIDTH X Y", + " SET OSC PULSEWIDTH", + "TO.OSC.SYNC X", + " RESET OSC PHASE", + "TO.OSC.PHASE X Y", + " SET OSC PHASE", + "TO.OSC.SLEW X Y", + " SET OSC SLEW (MS)", + "TO.OSC.SLEW.S X Y", + " SET OSC SLEW (SEC)", + "TO.OSC.SLEW.M X Y", + " SET OSC SLEW (MIN)", + "TO.OSC.CTR X Y", + " SET OSC CENTER", + "TO.ENV.ACT X Y", + " ENABLE ENV (1/0)", + "TO.ENV X Y", + " SET ENV GATE (1/0)", + "TO.ENV.TRIG X Y", + " TRIGGER ENV", + "TO.ENV.ATT X Y", + " SET ENV ATTACK (MS)", + "TO.ENV.ATT.S X Y", + " SET ENV ATTACK (SEC)", + "TO.ENV.ATT.M X Y", + " SET ENV ATTACK (MIN)", + "TO.ENV.DEC X Y", + " SET ENV DECAY (MS)", + "TO.ENV.DEC.S X Y", + " SET ENV DECAY (SEC)", + "TO.ENV.DEC.M X Y", + " SET ENV DECAY (MIN)", + "TO.ENV.EOR X N", + " PULSE TR N AT END OF RISE", + " TR ON SAME DEVICE", + "TO.ENV.EOC X N", + " AS ABOVE FOR END OF CYCLE", + "TO.ENV.LOOP X Y", + " LOOP ENV Y TIMES", + " 0 TO LOOP INDEFINITELY", + "TO.TR.INIT X", + " RESET TR", + "TO.CV.INIT X", + " RESET CV", + "TO.INIT D", + " RESET DEVICE", + "TO.KILL D", + " CANCEL DEVICE TR PULSES", + " AND CV SLEW", + "TO.CV.CALIB X", + " SAVE TO.CV.OFF X", + " AS CALIB VALUE", + "TO.CV.RESET X", + " RESET CV CALIB" }; + +#define HELP12_LENGTH 115 +const char* help12[HELP12_LENGTH] = { "12/13 ANSIBLE", + " ", + "KR.PRE / KR.PRE X", + " RETURN/LOAD CURRENT PRESET", + "KR.PERIOD / KR.PERIOD X", + " GET/SET INTERNAL PERIOD", + "KR.PAT / KR.PAT X", + " GET/SET CURRENT PATTERN", + "KR.SCALE / KR.SCALE X", + " GET/SET CURRENT SCALE", + "KR.POS X Y / KR.POS X Y Z", + " GET/SET POSITION Z", + " FOR TRACK X PARAM Y", + "KR.L.ST X Y", + " GET LOOP START FOR", + " TRACK X, PARAM Y", + "KR.L.ST X Y Z", + " LOOP START FOR", + " TRACK X, PARAM Y TO Z", + "KR.L.LEN X Y", + " GET LENGTH OF", + " TRACK X, PARAM Y", + "KR.L.LEN X Y Z", + " SET LENGTH OF", + " TRACK X, PARAM Y TO Z", + "KR.RES X Y", + " RESET POSITION", + " FOR TRACK X, PARAM Y", + "KR.CV X", + " GET CV FOR CHANNEL X", + "KR.MUTE X / KR.MUTE X Y", + " GET/SET MUTE STATE", + " FOR CHANNEL X", + "KR.TRMUTE X", + " TOGGLE MUTE STATE", + " FOR CHANNEL X", + "KR.CLK X", + " ADVANCE THE CLOCK", + " FOR CHANNEL X", + " (MUST BE ENABLED!)", + "ME.PRE / ME.PRE X", + " RETURN/LOAD CURRENT PRESET", + "ME.SCALE / ME.SCALE X", + " GET/SET CURRENT SCALE", + "ME.PERIOD / ME.PERIOD X", + " GET/SET CLOCK PERIOD", + "ME.STOP X", + " STOP CHANNEL X (0 = ALL)", + "ME.RES X", + " RESET CHANNEL X (0 = ALL)", + " ALSO USED AS START", + "ME.CV X", + " GET CV FOR CHANNEL X", + "LV.PRE / LV.PRE X", + " RETURN/LOAD CURRENT PRESET", + "LV.RES X", + " RESET (0 ON NEXT CLK,1 NOW)", + "LV.POS / LV.POS X", + " GET/SET CURRENT POSITION", + "LV.L.ST / LV.L.ST X", + " GET/SET LOOP START", + "LV.L.LEN / LV.L.LEN X", + " GET/SET LOOP LENGTH", + "LV.L.DIR / LV.L.DIR X", + " GET/SET LOOP DIRECTION", + "LV.CV X", + " GET CV FOR CHANNEL X", + "CY.PRE / CY.PRE X", + " RETURN/LOAD CURRENT PRESET", + "CY.RES X", + " RESET CHANNEL X (0 = ALL)", + "CY.POS X / CY.POS X Y", + " GET/SET CHANNEL POSITION", + " X = 0 TO SET ALL", + " POSITION BETWEEN 0-255", + "CY.REV X", + " REVERSE CHANNEL X (0 = ALL)", + "MID.SLEW T", + " SET PITCH SLEW TIME", + " TO T IN MS", + "MID.SHIFT X", + " SHIFT PITCH BY TT PITCH X", + " (E.G. N 6, V -1)", + "ARP.HLD X", + " 0 DISABLES KEY HOLD", + " OTHER VALUES ENABLE", + "ARP.STY X", + " SET ARP STYLE (0-7)", + "ARP.GT V G", + " SET GATE LENGTH FOR VOICE V", + " TO G (0-127, SYNCED TO CLK)", + "ARP.SLEW V T", + " SET SLEW TIME FOR VOICE V", + " TO T IN MS", + "ARP.RPT V N S", + " SET VOICE PATTERN REPEAT", + " FOR VOICE V TO N TIMES", + " SHIFTED BY S SEMITONES", + "ARP.DIV V D", + " SET VOICE CLOCK DIVISOR", + " FOR VOICE V TO D (1-32)", + "ARP.FIL V F", + " SET VOICE EUCLIDEAN FILL", + " 1 FOR STRAIGHT CLOCK (1-32)", + "ARP.ROT V R", + " SET VOICE EUCLIDEAN", + " ROTATION (-32, 32)", + "ARP.ER V F D R", + " SET ALL EUCLIDEAN RHYTHM", + "ARP.RES V", + " RESET VOICE CLOCK/PATTERN", + " ON NEXT CLOCK TICK", + "ARP.SHIFT V X", + " SHIFT VOICE CV BY TT PITCH", + " (E.G. N 6, V -1)" }; + +#define HELP13_LENGTH 53 +const char* help13[HELP13_LENGTH] = { "13/13 JUST FRIENDS & W/", + " ", + "JF.TR X Y", + " TRIGGER CHANNEL X (0 = ALL)", + " WITH STATE Y (1 ON, 0 OFF)", + "JF.RMODE X", + " SET RUN STATE OF JF", + " (0 = OFF, NON-ZERO = ON)", + "JF.RUN X", + " SEND X TO RUN INPUT", + " (V -5 TO V 5)", + "JF.SHIFT X", + " TRANSPOSE JF BY X", + "JF.VTR X Y", + " LIKE JF.TR WITH VOLUME CTR", + " (V 0 TO V 5)", + "JF.TUNE X Y Z", + " ADJUST TUNING OF CHANNEL X", + " SET NUMERATOR TO Y", + " SET DENOMINATOR TO Z", + "JF.MODE X", + " NON-0 FOR ALTERNATE MODES", + "JF.VOX X Y Z", + " CREATE NOTE AT CHANNEL X", + " Y = PITCH, Z = VELOCITY", + "JF.NOTE X Y", + " ALLOCATED NOTE SEQUENCING", + " X = PITCH, Y = VELOCITY", + "JF.GOD X", + " REDEFINE C3 TO GOD NOTE", + " (0: A=440HZ, 1: A=432HZ)", + "JF.TICK X", + " SET TIMEBASE OF GEODE", + " 1-48 TICKS PER MEASURE", + " 49-255 BEATS PER MINUTE", + "JF.QT X", + " SET QUANTIZATION", + " 0 = NO QUANTIZATION", + " 1-32 SETS SUBDIVISION", + "WS.PLAY X", + " SET PLAYBACK STATE AND DIR", + " 0 = STOP, 1 = FWD, -1 = REV", + "WS.REC X", + " SET RECORDING MODE", + " 0 PLAYBACK ONLY, 1 OVERDUB", + " -1 OVERWRITE", + "WS.CUE X", + " MOVE TO CUEPOINT (RELATIVE)", + " 0 RETRIGGER CURRENT CUE", + " 1 JUMP TO NEXT CUE", + " -1 JUMP TO PREV CUE", + "WS.LOOP X", + " SET LOOP STATE ON/OFF" }; + //////////////////////////////////////////////////////////////////////////////// // Help mode /////////////////////////////////////////////////////////////////// -const char** help_pages[HELP_PAGES] = { help1, help2, help3, help4, - help5, help6, help7, help8 }; -const uint8_t help_length[HELP_PAGES] = { HELP1_LENGTH, HELP2_LENGTH, - HELP3_LENGTH, HELP4_LENGTH, - HELP5_LENGTH, HELP6_LENGTH, - HELP7_LENGTH, HELP8_LENGTH }; +const char** help_pages[HELP_PAGES] = { help1, help2, help3, help4, help5, + help6, help7, help8, help9, help10, + help11, help12, help13 }; +const uint8_t help_length[HELP_PAGES] = { + HELP1_LENGTH, HELP2_LENGTH, HELP3_LENGTH, HELP4_LENGTH, HELP5_LENGTH, + HELP6_LENGTH, HELP7_LENGTH, HELP8_LENGTH, HELP9_LENGTH, HELP10_LENGTH, + HELP11_LENGTH, HELP12_LENGTH, HELP13_LENGTH +}; static uint8_t page_no; static uint8_t offset; diff --git a/module/keyboard_helper.h b/module/keyboard_helper.h index 183f64c1..aaae4df9 100644 --- a/module/keyboard_helper.h +++ b/module/keyboard_helper.h @@ -49,6 +49,15 @@ static inline bool mod_only_shift_alt(uint8_t mod) { return (mod & either_sh) && (mod & either_alt); } +static inline bool mod_only_ctrl_alt(uint8_t mod) { + const uint8_t either_ctrl = + HID_MODIFIER_LEFT_CTRL | HID_MODIFIER_RIGHT_CTRL; + const uint8_t either_alt = HID_MODIFIER_LEFT_ALT | HID_MODIFIER_RIGHT_ALT; + // first check we only have shift and alt + if (mod & ~(either_ctrl | either_alt)) return false; + return (mod & either_ctrl) && (mod & either_alt); +} + static inline bool mod_only_win(uint8_t mod) { return mod == HID_MODIFIER_LEFT_UI || mod == HID_MODIFIER_RIGHT_UI || mod == (HID_MODIFIER_LEFT_UI | HID_MODIFIER_RIGHT_UI); @@ -76,6 +85,11 @@ static inline bool match_shift_alt(uint8_t mod, uint8_t key, return mod_only_shift_alt(mod) && key == required_key; } +static inline bool match_shift_ctrl(uint8_t mod, uint8_t key, + uint8_t required_key) { + return mod_only_shift_ctrl(mod) && key == required_key; +} + static inline bool match_win(uint8_t mod, uint8_t key, uint8_t required_key) { return mod_only_win(mod) && key == required_key; } diff --git a/module/line_editor.c b/module/line_editor.c index 2e4c654e..c0e65f70 100644 --- a/module/line_editor.c +++ b/module/line_editor.c @@ -3,6 +3,7 @@ #include // this +#include "globals.h" #include "keyboard_helper.h" // teletype @@ -17,9 +18,6 @@ #include "conf_usb_host.h" // needed in order to include "usb_protocol_hid.h" #include "usb_protocol_hid.h" -// global copy buffer -static char copy_buffer[LINE_EDITOR_SIZE]; - void line_editor_set(line_editor_t *le, const char value[LINE_EDITOR_SIZE]) { size_t length = strlen(value); if (length < LINE_EDITOR_SIZE) { @@ -66,6 +64,22 @@ bool line_editor_process_keys(line_editor_t *le, uint8_t k, uint8_t m, le->cursor = le->length; return true; } + // ctrl-: move cursor to previous word + else if (match_ctrl(m, k, HID_LEFT)) { + while (le->cursor) { + le->cursor--; + if (!le->cursor || le->buffer[le->cursor - 1] == ' ') break; + } + return true; + } + // ctrl-: move cursor to next word + else if (match_ctrl(m, k, HID_RIGHT)) { + while (le->cursor < le->length) { + le->cursor++; + if (le->buffer[le->cursor - 1] == ' ') break; + } + return true; + } // or ctrl-h: backwards delete one character else if (match_no_mod(m, k, HID_BACKSPACE) || match_ctrl(m, k, HID_H)) { if (le->cursor) { @@ -103,7 +117,7 @@ bool line_editor_process_keys(line_editor_t *le, uint8_t k, uint8_t m, return true; } // alt- or ctrl-w: delete from cursor to beginning of word - else if (match_alt(m, k, HID_DELETE) || match_ctrl(m, k, HID_W)) { + else if (match_alt(m, k, HID_BACKSPACE) || match_ctrl(m, k, HID_W)) { while (le->cursor) { // delete a character le->cursor--; @@ -120,18 +134,20 @@ bool line_editor_process_keys(line_editor_t *le, uint8_t k, uint8_t m, } // ctrl-x or alt-x: cut else if (match_ctrl(m, k, HID_X) || match_alt(m, k, HID_X)) { - strcpy(copy_buffer, le->buffer); + strcpy(copy_buffer[0], le->buffer); + copy_buffer_len = 1; line_editor_set(le, ""); return true; } // ctrl-c or alt-c: copy else if (match_ctrl(m, k, HID_C) || match_alt(m, k, HID_C)) { - strcpy(copy_buffer, le->buffer); + strcpy(copy_buffer[0], le->buffer); + copy_buffer_len = 1; return true; } // ctrl-v or alt-v: paste else if (match_ctrl(m, k, HID_V) || match_alt(m, k, HID_V)) { - line_editor_set(le, copy_buffer); + line_editor_set(le, copy_buffer[0]); return true; } else if (no_mod(m) || mod_only_shift(m)) { @@ -162,9 +178,5 @@ void line_editor_draw(line_editor_t *le, char prefix, region *reg) { strcat(s, " "); region_fill(reg, 0); - font_string_region_clip_hi(reg, s, 0, 0, 0xf, 0, le->cursor + 2); -} - -void line_editor_set_copy_buffer(const char *value) { - strcpy(copy_buffer, value); + font_string_region_clip_hid(reg, s, 0, 0, 0xf, 0, le->cursor + 2, 3); } diff --git a/module/line_editor.h b/module/line_editor.h index 6b4f48a2..902ada98 100644 --- a/module/line_editor.h +++ b/module/line_editor.h @@ -22,5 +22,4 @@ char *line_editor_get(line_editor_t *le); bool line_editor_process_keys(line_editor_t *le, uint8_t key, uint8_t mod_key, bool is_key_held); void line_editor_draw(line_editor_t *le, char prefix, region *reg); -void line_editor_set_copy_buffer(const char *value); #endif diff --git a/module/live_mode.c b/module/live_mode.c index 027116f6..2ac6805c 100644 --- a/module/live_mode.c +++ b/module/live_mode.c @@ -31,6 +31,12 @@ static process_result_t output; static error_t status; static char error_msg[TELE_ERROR_MSG_LENGTH]; static bool show_welcome_message; +static uint8_t grid_view_changed = 0; +static uint8_t grid_x1 = 0, grid_y1 = 0, grid_x2 = 0, grid_y2 = 0; +static uint8_t grid_pressed = 0; +screen_grid_mode grid_mode = GRID_MODE_OFF; +uint8_t grid_page = 0; +uint8_t grid_show_controls = 0; static const uint8_t D_INPUT = 1 << 0; static const uint8_t D_LIST = 1 << 1; @@ -89,6 +95,10 @@ void set_vars_updated() { dirty |= D_VARS; } +void set_grid_updated() { + grid_view_changed = true; +} + // main mode functions void init_live_mode() { status = E_OK; @@ -105,69 +115,290 @@ void set_live_mode() { history_line = -1; dirty = D_ALL; activity_prev = 0xFF; + grid_view_changed = true; + if (grid_mode == GRID_MODE_FULL) grid_mode = GRID_MODE_EDIT; } -void process_live_keys(uint8_t k, uint8_t m, bool is_held_key) { - // or C-n: history next - if (match_no_mod(m, k, HID_DOWN) || match_ctrl(m, k, HID_N)) { - if (history_line > 0) { - history_line--; - line_editor_set_command(&le, &history[history_line]); - } - else { - history_line = -1; - line_editor_set(&le, ""); - } - dirty |= D_INPUT; +void set_live_submode(u8 submode) { + line_editor_set(&le, ""); + history_line = -1; + if (submode == 0) { + show_vars = 0; + grid_mode = GRID_MODE_OFF; } - // or C-p: history previous - else if (match_no_mod(m, k, HID_UP) || match_ctrl(m, k, HID_P)) { - if (history_line < history_top) { - history_line++; - line_editor_set_command(&le, &history[history_line]); - dirty |= D_INPUT; - } + else if (submode == 1) { + show_vars = 1; + grid_mode = GRID_MODE_OFF; } - // : execute command - else if (match_no_mod(m, k, HID_ENTER)) { - dirty |= D_MESSAGE; // something will definitely happen - dirty |= D_INPUT; - - tele_command_t command; + else if (submode == 2) { + show_vars = 0; + grid_mode = GRID_MODE_EDIT; + } + else if (submode == 3) { + show_vars = 0; + grid_mode = GRID_MODE_FULL; + } + else + return; + dirty = D_ALL; + activity_prev = 0xFF; + grid_view_changed = true; +} - status = parse(line_editor_get(&le), &command, error_msg); - if (status != E_OK) - return; // quit, screen_refresh_live will display the error message +void history_next() { + if (history_line > 0) { + history_line--; + line_editor_set_command(&le, &history[history_line]); + } + else { + history_line = -1; + line_editor_set(&le, ""); + } + dirty |= D_INPUT; +} - status = validate(&command, error_msg); - if (status != E_OK) - return; // quit, screen_refresh_live will display the error message +void history_prev() { + if (history_line < history_top) { + history_line++; + line_editor_set_command(&le, &history[history_line]); + dirty |= D_INPUT; + } +} - if (command.length) { - // increase history_size up to a maximum - history_top++; - if (history_top >= MAX_HISTORY_SIZE) - history_top = MAX_HISTORY_SIZE - 1; - - // shuffle the history up - // should really use some sort of ring buffer - for (size_t i = history_top; i > 0; i--) { - memcpy(&history[i], &history[i - 1], sizeof(command)); +void execute_line() { + dirty |= D_MESSAGE; // something will definitely happen + dirty |= D_INPUT; + + tele_command_t command; + command.comment = false; + + status = parse(line_editor_get(&le), &command, error_msg); + if (status != E_OK) + return; // quit, screen_refresh_live will display the error message + + status = validate(&command, error_msg); + if (status != E_OK) + return; // quit, screen_refresh_live will display the error message + + if (command.length) { + s16 found = -1; + for (s16 i = history_top; i >= 0; i--) + if (command.length == history[i].length && + memcmp(&(command.data), &(history[i].data), + command.length * sizeof(tele_data_t)) == 0) { + found = i; + break; } - memcpy(&history[0], &command, sizeof(command)); - ss_clear_script(&scene_state, TEMP_SCRIPT); - ss_overwrite_script_command(&scene_state, TEMP_SCRIPT, 0, &command); - exec_state_t es; - es_init(&es); - es_push(&es); - es_variables(&es)->script_number = TEMP_SCRIPT; + if (found == -1) { + // increase history_size up to a maximum + if (history_top < MAX_HISTORY_SIZE - 1) history_top++; + found = history_top; + } - output = run_script_with_exec_state(&scene_state, &es, TEMP_SCRIPT); + // shuffle the history up + // should really use some sort of ring buffer + for (size_t i = found; i > 0; i--) { + memcpy(&history[i], &history[i - 1], sizeof(command)); } + memcpy(&history[0], &command, sizeof(command)); - history_line = -1; - line_editor_set(&le, ""); + ss_clear_script(&scene_state, TEMP_SCRIPT); + ss_overwrite_script_command(&scene_state, TEMP_SCRIPT, 0, &command); + exec_state_t es; + es_init(&es); + es_push(&es); + es_variables(&es)->script_number = TEMP_SCRIPT; + + output = run_script_with_exec_state(&scene_state, &es, TEMP_SCRIPT); + } + + history_line = -1; + line_editor_set(&le, ""); +} + +static void emulate_grid_release(scene_state_t *ss) { + grid_process_key(ss, grid_x1, grid_y1, 0, 1); + if (grid_x1 != grid_x2 || grid_y1 != grid_y2) + grid_process_key(ss, grid_x2, grid_y2, 0, 1); + grid_pressed = 0; +} + +void process_live_keys(uint8_t k, uint8_t m, bool is_held_key, bool is_release, + scene_state_t *ss) { + if (is_release) { + if (match_alt(m, k, HID_SPACEBAR) || + (grid_mode == GRID_MODE_FULL && match_no_mod(m, k, HID_SPACEBAR))) + emulate_grid_release(ss); + return; + } + + // or C-n: history next + if ((match_no_mod(m, k, HID_DOWN) || match_ctrl(m, k, HID_N)) && + grid_mode != GRID_MODE_FULL) { + history_next(); + } + // or C-p: history previous + else if ((match_no_mod(m, k, HID_UP) || match_ctrl(m, k, HID_P)) && + grid_mode != GRID_MODE_FULL) { + history_prev(); + } + // A-G: toggle grid view + else if (match_alt(m, k, HID_G) || + (grid_mode == GRID_MODE_FULL && match_no_mod(m, k, HID_G))) { + if (++grid_mode == GRID_MODE_LAST) { + grid_mode = GRID_MODE_OFF; + set_live_mode(); + } + grid_view_changed = true; + } + // A-: move grid cursor + else if (match_alt(m, k, HID_UP) || + (grid_mode == GRID_MODE_FULL && match_no_mod(m, k, HID_UP))) { + if (grid_pressed) emulate_grid_release(ss); + grid_y1 = (grid_y1 + GRID_MAX_DIMENSION - 1) % GRID_MAX_DIMENSION; + grid_x2 = grid_x1; + grid_y2 = grid_y1; + grid_view_changed = true; + } + // A-: move grid cursor + else if (match_alt(m, k, HID_DOWN) || + (grid_mode == GRID_MODE_FULL && match_no_mod(m, k, HID_DOWN))) { + if (grid_pressed) emulate_grid_release(ss); + grid_y1 = (grid_y1 + 1) % GRID_MAX_DIMENSION; + grid_x2 = grid_x1; + grid_y2 = grid_y1; + grid_view_changed = true; + } + // A-: move grid cursor + else if (match_alt(m, k, HID_LEFT) || + (grid_mode == GRID_MODE_FULL && match_no_mod(m, k, HID_LEFT))) { + if (grid_pressed) emulate_grid_release(ss); + grid_x1 = (grid_x1 + GRID_MAX_DIMENSION - 1) % GRID_MAX_DIMENSION; + grid_x2 = grid_x1; + grid_y2 = grid_y1; + grid_view_changed = true; + } + // A-: move grid cursor + else if (match_alt(m, k, HID_RIGHT) || + (grid_mode == GRID_MODE_FULL && match_no_mod(m, k, HID_RIGHT))) { + if (grid_pressed) emulate_grid_release(ss); + grid_x1 = (grid_x1 + 1) % GRID_MAX_DIMENSION; + grid_x2 = grid_x1; + grid_y2 = grid_y1; + grid_view_changed = true; + } + // A-S-: expand grid area up + else if (match_shift_alt(m, k, HID_UP) || + (grid_mode == GRID_MODE_FULL && match_shift(m, k, HID_UP))) { + if (grid_y2 > 0) { + if (grid_pressed) emulate_grid_release(ss); + grid_y2--; + grid_view_changed = true; + } + } + // A-S-: expand grid area down + else if (match_shift_alt(m, k, HID_DOWN) || + (grid_mode == GRID_MODE_FULL && match_shift(m, k, HID_DOWN))) { + if (grid_y2 < GRID_MAX_DIMENSION - 1) { + if (grid_pressed) emulate_grid_release(ss); + grid_y2++; + grid_view_changed = true; + } + } + // A-S-: expand grid area left + else if (match_shift_alt(m, k, HID_LEFT) || + (grid_mode == GRID_MODE_FULL && match_shift(m, k, HID_LEFT))) { + if (grid_x2 > 0) { + if (grid_pressed) emulate_grid_release(ss); + grid_x2--; + grid_view_changed = true; + } + } + // A-S-: expand grid area right + else if (match_shift_alt(m, k, HID_RIGHT) || + (grid_mode == GRID_MODE_FULL && match_shift(m, k, HID_RIGHT))) { + if (grid_x2 < GRID_MAX_DIMENSION - 1) { + if (grid_pressed) emulate_grid_release(ss); + grid_x2++; + grid_view_changed = true; + } + } + // A-: emulate grid press + else if (!is_held_key && (match_alt(m, k, HID_SPACEBAR) || + (grid_mode == GRID_MODE_FULL && + match_no_mod(m, k, HID_SPACEBAR)))) { + grid_process_key(ss, grid_x1, grid_y1, 1, 1); + if (grid_x1 != grid_x2 || grid_y1 != grid_y2) + grid_process_key(ss, grid_x2, grid_y2, 1, 1); + grid_pressed = 1; + } + // A-: insert coordinates / size + else if (!is_held_key && match_alt(m, k, HID_PRINTSCREEN) && + grid_mode == GRID_MODE_EDIT) { + u8 area_x, area_y, area_w, area_h; + if (grid_x1 < grid_x2) { + area_x = grid_x1; + area_w = grid_x2 + 1 - grid_x1; + } + else { + area_x = grid_x2; + area_w = grid_x1 + 1 - grid_x2; + } + if (grid_y1 < grid_y2) { + area_y = grid_y1; + area_h = grid_y2 + 1 - grid_y1; + } + else { + area_y = grid_y2; + area_h = grid_y1 + 1 - grid_y2; + } + if (area_x > 9) { + line_editor_process_keys(&le, HID_1, HID_MODIFIER_NONE, false); + area_x -= 10; + } + line_editor_process_keys(&le, area_x ? HID_1 + area_x - 1 : HID_0, + HID_MODIFIER_NONE, false); + line_editor_process_keys(&le, HID_SPACEBAR, HID_MODIFIER_NONE, false); + if (area_y > 9) { + line_editor_process_keys(&le, HID_1, HID_MODIFIER_NONE, false); + area_y -= 10; + } + line_editor_process_keys(&le, area_y ? HID_1 + area_y - 1 : HID_0, + HID_MODIFIER_NONE, false); + line_editor_process_keys(&le, HID_SPACEBAR, HID_MODIFIER_NONE, false); + if (area_w > 9) { + line_editor_process_keys(&le, HID_1, HID_MODIFIER_NONE, false); + area_w -= 10; + } + line_editor_process_keys(&le, area_w ? HID_1 + area_w - 1 : HID_0, + HID_MODIFIER_NONE, false); + line_editor_process_keys(&le, HID_SPACEBAR, HID_MODIFIER_NONE, false); + if (area_h > 9) { + line_editor_process_keys(&le, HID_1, HID_MODIFIER_NONE, false); + area_h -= 10; + } + line_editor_process_keys(&le, area_h ? HID_1 + area_h - 1 : HID_0, + HID_MODIFIER_NONE, false); + line_editor_process_keys(&le, HID_SPACEBAR, HID_MODIFIER_NONE, false); + dirty |= D_INPUT; + } + // A-: toggle grid page + else if (match_alt(m, k, HID_SLASH) || + (grid_mode == GRID_MODE_FULL && match_no_mod(m, k, HID_SLASH))) { + if (++grid_page > 1) grid_page = 0; + grid_view_changed = true; + } + // A-<\>: toggle control view + else if (match_alt(m, k, HID_BACKSLASH) || + (grid_mode == GRID_MODE_FULL && + match_no_mod(m, k, HID_BACKSLASH))) { + grid_show_controls = !grid_show_controls; + grid_view_changed = true; + } + // : execute command + else if (match_no_mod(m, k, HID_ENTER) && grid_mode != GRID_MODE_FULL) { + execute_line(); } // [ or ]: switch to edit mode else if (match_no_mod(m, k, HID_OPEN_BRACKET) || @@ -177,19 +408,35 @@ void process_live_keys(uint8_t k, uint8_t m, bool is_held_key) { // tilde: show the variables else if (match_no_mod(m, k, HID_TILDE)) { show_vars = !show_vars; + if (grid_mode != GRID_MODE_OFF) { + show_vars = 1; + grid_mode = GRID_MODE_OFF; + activity_prev = 0xFF; + } if (show_vars) dirty |= D_VARS; // combined with this... dirty |= D_LIST; // cheap flag to indicate mode just switched } - else { // pass the key though to the line editor + // pass the key though to the line editor + else if (grid_mode != GRID_MODE_FULL) { bool processed = line_editor_process_keys(&le, k, m, is_held_key); if (processed) dirty |= D_INPUT; } + show_welcome_message = false; } -uint8_t screen_refresh_live() { +uint8_t screen_refresh_live(scene_state_t *ss) { uint8_t screen_dirty = 0; + if (grid_mode != GRID_MODE_OFF && + (grid_view_changed || ss->grid.scr_dirty)) { + grid_view_changed = 0; + screen_dirty = 0b111111; + grid_screen_refresh(ss, grid_mode, grid_page, grid_show_controls, + grid_x1, grid_y1, grid_x2, grid_y2); + } + if (grid_mode == GRID_MODE_FULL) return 0b11111111; + if (dirty & D_INPUT) { line_editor_draw(&le, '>', &line[7]); screen_dirty |= (1 << 7); @@ -228,8 +475,9 @@ uint8_t screen_refresh_live() { dirty &= ~D_MESSAGE; } - if (show_vars && ((dirty & D_VARS) || (dirty & D_LIST))) { - int16_t* vp = + if (show_vars && ((dirty & D_VARS) || (dirty & D_LIST)) && + grid_mode == GRID_MODE_OFF) { + int16_t *vp = &scene_state.variables .a; // 8 int16_t all in a row, point at the first one // relies on variable ordering. see: src/state.h @@ -268,14 +516,14 @@ uint8_t screen_refresh_live() { dirty &= ~D_LIST; } - if (dirty & D_LIST) { + if (dirty & D_LIST && grid_mode == GRID_MODE_OFF) { for (int i = 1; i < 6; i++) region_fill(&line[i], 0); screen_dirty |= 0x3E; dirty &= ~D_LIST; } - if ((activity != activity_prev)) { + if (activity != activity_prev && grid_mode == GRID_MODE_OFF) { region_fill(&line[0], 0); // slew icon diff --git a/module/live_mode.h b/module/live_mode.h index 704ed12c..bb8e6f1b 100644 --- a/module/live_mode.h +++ b/module/live_mode.h @@ -1,6 +1,8 @@ #ifndef _LIVE_MODE_H_ #define _LIVE_MODE_H_ +#include "grid.h" +#include "state.h" #include "stdbool.h" #include "stdint.h" @@ -8,8 +10,17 @@ void set_slew_icon(bool display); void set_metro_icon(bool display); void init_live_mode(void); void set_live_mode(void); -void process_live_keys(uint8_t key, uint8_t mod_key, bool is_held_key); -uint8_t screen_refresh_live(void); +void set_live_submode(u8 submode); +void set_grid_updated(void); +void history_next(void); +void history_prev(void); +void execute_line(void); +void process_live_keys(uint8_t key, uint8_t mod_key, bool is_held_key, + bool is_release, scene_state_t *ss); +uint8_t screen_refresh_live(scene_state_t *ss); void set_vars_updated(void); +extern uint8_t grid_mode; +extern uint8_t grid_page; +extern uint8_t grid_show_controls; #endif diff --git a/module/main.c b/module/main.c index 7ba52a33..b79ef504 100644 --- a/module/main.c +++ b/module/main.c @@ -24,6 +24,7 @@ #include "init_teletype.h" #include "interrupts.h" #include "kbd.h" +#include "monome.h" #include "region.h" #include "screen.h" #include "timers.h" @@ -35,13 +36,13 @@ #include "edit_mode.h" #include "flash.h" #include "globals.h" +#include "grid.h" #include "help_mode.h" #include "keyboard_helper.h" #include "live_mode.h" #include "pattern_mode.h" #include "preset_r_mode.h" #include "preset_w_mode.h" -#include "screensaver_mode.h" #include "teletype.h" #include "teletype_io.h" #include "usb_disk_mode.h" @@ -49,11 +50,7 @@ #ifdef TELETYPE_PROFILE #include "profile.h" -profile_t - prof_Script[SCRIPT_COUNT], - prof_Delay[DELAY_SIZE], - prof_CV, - prof_ADC, +profile_t prof_Script[SCRIPT_COUNT], prof_Delay[DELAY_SIZE], prof_CV, prof_ADC, prof_ScreenRefresh; void tele_profile_script(size_t s) { @@ -71,6 +68,7 @@ void tele_profile_delay(uint8_t d) { #define RATE_CLOCK 10 #define RATE_CV 6 +#define SS_TIMEOUT 90 /* minutes */ * 60 * 100 //////////////////////////////////////////////////////////////////////////////// @@ -79,14 +77,14 @@ void tele_profile_delay(uint8_t d) { scene_state_t scene_state; char scene_text[SCENE_TEXT_LINES][SCENE_TEXT_CHARS]; uint8_t preset_select; -region line[8] = { { .w = 128, .h = 8, .x = 0, .y = 0 }, - { .w = 128, .h = 8, .x = 0, .y = 8 }, - { .w = 128, .h = 8, .x = 0, .y = 16 }, - { .w = 128, .h = 8, .x = 0, .y = 24 }, - { .w = 128, .h = 8, .x = 0, .y = 32 }, - { .w = 128, .h = 8, .x = 0, .y = 40 }, - { .w = 128, .h = 8, .x = 0, .y = 48 }, - { .w = 128, .h = 8, .x = 0, .y = 56 } }; +region line[8] = { + {.w = 128, .h = 8, .x = 0, .y = 0 }, {.w = 128, .h = 8, .x = 0, .y = 8 }, + {.w = 128, .h = 8, .x = 0, .y = 16 }, {.w = 128, .h = 8, .x = 0, .y = 24 }, + {.w = 128, .h = 8, .x = 0, .y = 32 }, {.w = 128, .h = 8, .x = 0, .y = 40 }, + {.w = 128, .h = 8, .x = 0, .y = 48 }, {.w = 128, .h = 8, .x = 0, .y = 56 } +}; +char copy_buffer[SCENE_TEXT_LINES][SCENE_TEXT_CHARS]; +uint8_t copy_buffer_len = 0; //////////////////////////////////////////////////////////////////////////////// @@ -95,6 +93,8 @@ region line[8] = { { .w = 128, .h = 8, .x = 0, .y = 0 }, static tele_mode_t mode = M_LIVE; static tele_mode_t last_mode = M_LIVE; static uint32_t ss_counter = 0; +static u8 grid_connected = 0; +static u8 grid_control_mode = 0; static uint16_t adc[4]; @@ -112,16 +112,19 @@ static aout_t aout[4]; static bool metro_timer_enabled; static uint8_t front_timer; static uint8_t mod_key = 0, hold_key, hold_key_count = 0; -static uint64_t last_in_tick = 0; +static uint64_t last_adc_tick = 0; // timers -static softTimer_t clockTimer = { .next = NULL, .prev = NULL }; -static softTimer_t refreshTimer = { .next = NULL, .prev = NULL }; -static softTimer_t keyTimer = { .next = NULL, .prev = NULL }; -static softTimer_t cvTimer = { .next = NULL, .prev = NULL }; -static softTimer_t adcTimer = { .next = NULL, .prev = NULL }; -static softTimer_t hidTimer = { .next = NULL, .prev = NULL }; -static softTimer_t metroTimer = { .next = NULL, .prev = NULL }; +static softTimer_t clockTimer = {.next = NULL, .prev = NULL }; +static softTimer_t refreshTimer = {.next = NULL, .prev = NULL }; +static softTimer_t keyTimer = {.next = NULL, .prev = NULL }; +static softTimer_t cvTimer = {.next = NULL, .prev = NULL }; +static softTimer_t adcTimer = {.next = NULL, .prev = NULL }; +static softTimer_t hidTimer = {.next = NULL, .prev = NULL }; +static softTimer_t metroTimer = {.next = NULL, .prev = NULL }; +static softTimer_t monomePollTimer = {.next = NULL, .prev = NULL }; +static softTimer_t monomeRefreshTimer = {.next = NULL, .prev = NULL }; +static softTimer_t gridFaderTimer = {.next = NULL, .prev = NULL }; //////////////////////////////////////////////////////////////////////////////// @@ -135,6 +138,9 @@ static void keyTimer_callback(void* o); static void adcTimer_callback(void* o); static void hidTimer_callback(void* o); static void metroTimer_callback(void* o); +static void monome_poll_timer_callback(void* obj); +static void monome_refresh_timer_callback(void* obj); +static void grid_fader_timer_callback(void* obj); // event handler prototypes static void handler_None(int32_t data); @@ -157,11 +163,17 @@ static void assign_msc_event_handlers(void); static void check_events(void); // key handling -static void process_keypress(uint8_t key, uint8_t mod_key, bool is_held_key); +static void process_keypress(uint8_t key, uint8_t mod_key, bool is_held_key, + bool is_release); static bool process_global_keys(uint8_t key, uint8_t mod_key, bool is_held_key); +// start/stop monome polling/refresh timers +void timers_set_monome(void); +void timers_unset_monome(void); + // other static void render_init(void); +static void exit_screensaver(void); //////////////////////////////////////////////////////////////////////////////// @@ -221,35 +233,66 @@ void cvTimer_callback(void* o) { } void clockTimer_callback(void* o) { - event_t e = { .type = kEventTimer, .data = 0 }; + event_t e = {.type = kEventTimer, .data = 0 }; event_post(&e); } void refreshTimer_callback(void* o) { - event_t e = { .type = kEventScreenRefresh, .data = 0 }; + event_t e = {.type = kEventScreenRefresh, .data = 0 }; event_post(&e); } void keyTimer_callback(void* o) { - event_t e = { .type = kEventKeyTimer, .data = 0 }; + event_t e = {.type = kEventKeyTimer, .data = 0 }; event_post(&e); } void adcTimer_callback(void* o) { - event_t e = { .type = kEventPollADC, .data = 0 }; + event_t e = {.type = kEventPollADC, .data = 0 }; event_post(&e); } void hidTimer_callback(void* o) { - event_t e = { .type = kEventHidTimer, .data = 0 }; + event_t e = {.type = kEventHidTimer, .data = 0 }; event_post(&e); } void metroTimer_callback(void* o) { - event_t e = { .type = kEventAppCustom, .data = 0 }; + event_t e = {.type = kEventAppCustom, .data = 0 }; event_post(&e); } +// monome polling callback +static void monome_poll_timer_callback(void* obj) { + // asynchronous, non-blocking read + // UHC callback spawns appropriate events + ftdi_read(); +} + +// monome refresh callback +static void monome_refresh_timer_callback(void* obj) { + if (grid_connected && scene_state.grid.grid_dirty) { + static event_t e; + e.type = kEventMonomeRefresh; + event_post(&e); + } +} + +// monome: start polling +void timers_set_monome(void) { + timer_add(&monomePollTimer, 20, &monome_poll_timer_callback, NULL); + timer_add(&monomeRefreshTimer, 30, &monome_refresh_timer_callback, NULL); +} + +// monome stop polling +void timers_unset_monome(void) { + timer_remove(&monomePollTimer); + timer_remove(&monomeRefreshTimer); +} + +void grid_fader_timer_callback(void* o) { + grid_process_fader_slew(&scene_state); +} //////////////////////////////////////////////////////////////////////////////// // event handlers @@ -257,15 +300,23 @@ void metroTimer_callback(void* o) { void handler_None(int32_t data) {} void handler_Front(int32_t data) { - ss_counter = 0; - if (mode == M_SCREENSAVER) { - set_last_mode(); + if (ss_counter >= SS_TIMEOUT) { + exit_screensaver(); return; } + ss_counter = 0; + if (data == 0) { + if (grid_connected) { + grid_control_mode = !grid_control_mode; + if (grid_control_mode && mode == M_HELP) set_mode(M_LIVE); + grid_set_control_mode(grid_control_mode, mode, &scene_state); + return; + } + if (mode != M_PRESET_R) { front_timer = 0; - set_preset_r_mode(adc[1]); + set_preset_r_mode(adc[1] >> 7); set_mode(M_PRESET_R); } else @@ -288,9 +339,9 @@ void handler_PollADC(int32_t data) { ss_set_in(&scene_state, adc[0] << 2); - if (mode == M_SCREENSAVER && (adc[1] >> 8 != last_knob >> 8)) { - ss_counter = 0; - set_last_mode(); + if (ss_counter >= SS_TIMEOUT && (adc[1] >> 8 != last_knob >> 8)) { + exit_screensaver(); + return; } last_knob = adc[1]; @@ -298,8 +349,12 @@ void handler_PollADC(int32_t data) { process_pattern_knob(adc[1], mod_key); ss_set_param(&scene_state, adc[1] << 2); } - else if (mode == M_PRESET_R) { - process_preset_r_knob(adc[1], mod_key); + else if (mode == M_PRESET_R && !(grid_connected && grid_control_mode)) { + uint8_t preset = adc[1] >> 6; + uint8_t deadzone = preset & 1; + preset >>= 1; + if (!deadzone || abs(preset - get_preset()) > 1) + process_preset_r_preset(preset); } else { ss_set_param(&scene_state, adc[1] << 2); @@ -311,8 +366,8 @@ void handler_PollADC(int32_t data) { void handler_KeyTimer(int32_t data) { if (front_timer) { - if (front_timer == 1) { - if (mode == M_PRESET_R) { process_preset_r_long_front(); } + if (front_timer == 1 && !grid_connected) { + if (mode == M_PRESET_R) { process_preset_r_load(); } front_timer = 0; } else @@ -321,7 +376,7 @@ void handler_KeyTimer(int32_t data) { if (hold_key) { if (hold_key_count > 4) - process_keypress(hold_key, mod_key, true); + process_keypress(hold_key, mod_key, true, false); else hold_key_count++; } @@ -343,8 +398,9 @@ void handler_HidTimer(int32_t data) { if (frame[i] == 0) { mod_key = frame[0]; if (i == 2) { - hold_key = 0; hold_key_count = 0; + process_keypress(hold_key, mod_key, false, true); + hold_key = 0; } break; @@ -353,7 +409,7 @@ void handler_HidTimer(int32_t data) { if (frame_compare(frame[i]) == false) { hold_key = frame[i]; hold_key_count = 0; - process_keypress(hold_key, mod_key, false); + process_keypress(hold_key, mod_key, false, false); } } @@ -386,9 +442,7 @@ void handler_MscConnect(int32_t data) { } void handler_Trigger(int32_t data) { - if (!ss_get_mute(&scene_state, data)) { - run_script(&scene_state, data); - } + if (!ss_get_mute(&scene_state, data)) { run_script(&scene_state, data); } } void handler_ScreenRefresh(int32_t data) { @@ -402,22 +456,35 @@ void handler_ScreenRefresh(int32_t data) { case M_PRESET_W: screen_dirty = screen_refresh_preset_w(); break; case M_PRESET_R: screen_dirty = screen_refresh_preset_r(); break; case M_HELP: screen_dirty = screen_refresh_help(); break; - case M_LIVE: screen_dirty = screen_refresh_live(); break; + case M_LIVE: screen_dirty = screen_refresh_live(&scene_state); break; case M_EDIT: screen_dirty = screen_refresh_edit(); break; - case M_SCREENSAVER: screen_dirty = screen_refresh_screensaver(); break; } + u8 grid = 0; for (size_t i = 0; i < 8; i++) - if (screen_dirty & (1 << i)) { region_draw(&line[i]); } + if (screen_dirty & (1 << i)) { + grid = 1; + if (ss_counter < SS_TIMEOUT) region_draw(&line[i]); + } + if (grid_control_mode && grid) scene_state.grid.grid_dirty = 1; + #ifdef TELETYPE_PROFILE profile_update(&prof_ScreenRefresh); #endif } void handler_EventTimer(int32_t data) { - ss_counter++; - if (ss_counter > SS_TIMEOUT) set_mode(M_SCREENSAVER); tele_tick(&scene_state, RATE_CLOCK); + + if (ss_counter < SS_TIMEOUT) { + ss_counter++; + if (ss_counter == SS_TIMEOUT) { + u8 empty = 0; + for (int i = 0; i < 64; i++) + for (int j = 0; j < 64; j++) + screen_draw_region(i << 1, j, 2, 1, &empty); + } + } } void handler_AppCustom(int32_t data) { @@ -426,11 +493,55 @@ void handler_AppCustom(int32_t data) { if (ss_get_script_len(&scene_state, METRO_SCRIPT)) { set_metro_icon(true); run_script(&scene_state, METRO_SCRIPT); + if (grid_connected && grid_control_mode) + grid_metro_triggered(&scene_state); } else set_metro_icon(false); } +static void handler_FtdiConnect(s32 data) { + ftdi_setup(); +} +static void handler_FtdiDisconnect(s32 data) { + grid_connected = 0; + timers_unset_monome(); +} + +static void handler_MonomeConnect(s32 data) { + hold_key = 0; + timers_set_monome(); + grid_connected = 1; + + if (grid_control_mode && mode == M_HELP) set_mode(M_LIVE); + grid_set_control_mode(grid_control_mode, mode, &scene_state); + + scene_state.grid.grid_dirty = 1; + grid_clear_held_keys(); +} + +static void handler_MonomePoll(s32 data) { + monome_read_serial(); +} + +static void handler_MonomeRefresh(s32 data) { + grid_refresh(&scene_state); + monomeFrameDirty = 0b1111; + (*monome_refresh)(); +} + +static void handler_MonomeGridKey(s32 data) { + if (grid_control_mode && ss_counter >= SS_TIMEOUT) { + exit_screensaver(); + return; + } + if (grid_control_mode) ss_counter = 0; + + u8 x, y, z; + monome_grid_key_parse_event_data(data, &x, &y, &z); + grid_process_key(&scene_state, x, y, z, 0); +} + //////////////////////////////////////////////////////////////////////////////// // event queue @@ -455,6 +566,13 @@ void assign_main_event_handlers() { app_event_handlers[kEventScreenRefresh] = &handler_ScreenRefresh; app_event_handlers[kEventTimer] = &handler_EventTimer; app_event_handlers[kEventAppCustom] = &handler_AppCustom; + app_event_handlers[kEventFtdiConnect] = &handler_FtdiConnect; + app_event_handlers[kEventFtdiDisconnect] = &handler_FtdiDisconnect; + app_event_handlers[kEventMonomeConnect] = &handler_MonomeConnect; + app_event_handlers[kEventMonomeDisconnect] = &handler_None; + app_event_handlers[kEventMonomePoll] = &handler_MonomePoll; + app_event_handlers[kEventMonomeRefresh] = &handler_MonomeRefresh; + app_event_handlers[kEventMonomeGridKey] = &handler_MonomeGridKey; } static void assign_msc_event_handlers(void) { @@ -476,7 +594,6 @@ void check_events(void) { // defined in globals.h void set_mode(tele_mode_t m) { - if (m == mode && m == M_SCREENSAVER) return; last_mode = mode; switch (m) { case M_LIVE: @@ -496,45 +613,50 @@ void set_mode(tele_mode_t m) { mode = M_PRESET_W; break; case M_PRESET_R: - set_preset_r_mode(adc[1]); + set_preset_r_mode(adc[1] >> 7); mode = M_PRESET_R; break; case M_HELP: set_help_mode(); mode = M_HELP; break; - case M_SCREENSAVER: - set_screensaver_mode(); - mode = M_SCREENSAVER; - break; } + if (mode != M_HELP) flash_update_last_mode(mode); } // defined in globals.h void set_last_mode() { if (mode == last_mode) return; - if (mode == M_SCREENSAVER) - set_mode(last_mode); - else if (last_mode == M_LIVE || last_mode == M_EDIT || - last_mode == M_PATTERN) + if (last_mode == M_LIVE || last_mode == M_EDIT || last_mode == M_PATTERN) set_mode(last_mode); else set_mode(M_LIVE); } +// defined in globals.h +void clear_delays_and_slews(scene_state_t* ss) { + clear_delays(ss); + for (int i = 0; i < 4; i++) { aout[i].step = 1; } +} //////////////////////////////////////////////////////////////////////////////// // key handling -void process_keypress(uint8_t key, uint8_t mod_key, bool is_held_key) { +void process_keypress(uint8_t key, uint8_t mod_key, bool is_held_key, + bool is_release) { // reset inactivity counter + if (ss_counter >= SS_TIMEOUT) { + exit_screensaver(); + return; + } ss_counter = 0; - if (mode == M_SCREENSAVER) { - set_last_mode(); -#if SS_DROP_KEYSTROKE + + // release is a special case for live mode + if (is_release) { + if (mode == M_LIVE) + process_live_keys(key, mod_key, is_held_key, true, &scene_state); return; -#endif } // first try global keys @@ -543,7 +665,9 @@ void process_keypress(uint8_t key, uint8_t mod_key, bool is_held_key) { switch (mode) { case M_EDIT: process_edit_keys(key, mod_key, is_held_key); break; - case M_LIVE: process_live_keys(key, mod_key, is_held_key); break; + case M_LIVE: + process_live_keys(key, mod_key, is_held_key, false, &scene_state); + break; case M_PATTERN: process_pattern_keys(key, mod_key, is_held_key); break; case M_PRESET_W: process_preset_w_keys(key, mod_key, is_held_key); @@ -552,7 +676,6 @@ void process_keypress(uint8_t key, uint8_t mod_key, bool is_held_key) { process_preset_r_keys(key, mod_key, is_held_key); break; case M_HELP: process_help_keys(key, mod_key, is_held_key); break; - case M_SCREENSAVER: break; // impossible } } @@ -586,14 +709,11 @@ bool process_global_keys(uint8_t k, uint8_t m, bool is_held_key) { } // win-: clear delays, stack and slews else if (match_win(m, k, HID_ESCAPE)) { - if (!is_held_key) { - clear_delays(&scene_state); - for (int i = 0; i < 4; i++) { aout[i].step = 1; } - } + if (!is_held_key) clear_delays_and_slews(&scene_state); return true; } // -?: help text, or return to last mode - else if (match_shift_alt(m, k, HID_SLASH)) { + else if (match_shift_alt(m, k, HID_SLASH) || match_alt(m, k, HID_H)) { if (mode == M_HELP) set_last_mode(); else { @@ -617,13 +737,13 @@ bool process_global_keys(uint8_t k, uint8_t m, bool is_held_key) { return true; } // ctrl- through ctrl- mute triggers - // ctrl- toggle metro else if (mod_only_ctrl(m) && k >= HID_F1 && k <= HID_F8) { bool muted = ss_get_mute(&scene_state, (k - HID_F1)); ss_set_mute(&scene_state, (k - HID_F1), !muted); screen_mutes_updated(); return true; } + // ctrl- toggle metro else if (mod_only_ctrl(m) && k == HID_F9) { scene_state.variables.m_act = !scene_state.variables.m_act; tele_metro_updated(); @@ -666,10 +786,19 @@ void render_init(void) { region_alloc(&line[7]); } +void exit_screensaver(void) { + ss_counter = 0; + set_mode(mode); +} + //////////////////////////////////////////////////////////////////////////////// // teletype_io.h +uint32_t tele_get_ticks() { + return get_ticks(); +} + void tele_metro_updated() { uint32_t metro_time = scene_state.variables.m; @@ -694,10 +823,12 @@ void tele_metro_updated() { set_metro_icon(true); else set_metro_icon(false); + + if (grid_connected && grid_control_mode) scene_state.grid.grid_dirty = 1; } void tele_metro_reset() { - if (metro_timer_enabled) { timer_reset(&metroTimer); } + if (metro_timer_enabled) timer_reset(&metroTimer); } void tele_tr(uint8_t i, int16_t v) { @@ -737,11 +868,12 @@ void tele_cv_off(uint8_t i, int16_t v) { aout[i].off = v; } -void tele_update_in(void) { - if (get_ticks() == last_in_tick) return; - last_in_tick = get_ticks(); +void tele_update_adc(u8 force) { + if (!force && get_ticks() == last_adc_tick) return; + last_adc_tick = get_ticks(); adc_convert(&adc); ss_set_in(&scene_state, adc[0] << 2); + ss_set_param(&scene_state, adc[1] << 2); } void tele_ii_tx(uint8_t addr, uint8_t* data, uint8_t l) { @@ -776,6 +908,11 @@ void tele_save_calibration() { flash_update_cal(&scene_state.cal); } +void grid_key_press(uint8_t x, uint8_t y, uint8_t z) { + grid_process_key(&scene_state, x, y, z, 1); +} + + //////////////////////////////////////////////////////////////////////////////// // main @@ -796,7 +933,12 @@ int main(void) { cpu_irq_enable(); init_usb_host(); + init_monome(); init_oled(); + + // wait to allow for any i2c devices to fully initalise + delay_ms(1500); + init_i2c_master(); print_dbg("\r\n\r\n// teletype! //////////////////////////////// "); @@ -831,6 +973,10 @@ int main(void) { timer_add(&keyTimer, 71, &keyTimer_callback, NULL); timer_add(&adcTimer, 61, &adcTimer_callback, NULL); timer_add(&refreshTimer, 63, &refreshTimer_callback, NULL); + timer_add(&gridFaderTimer, 25, &grid_fader_timer_callback, NULL); + + // update IN and PARAM in case Init uses them + tele_update_adc(1); // manually call tele_metro_updated to sync metro to scene_state metro_timer_enabled = false; @@ -846,11 +992,7 @@ int main(void) { aout[3].slew = 1; init_live_mode(); - set_mode(M_LIVE); - - // wait 50ms before running the init script to allow for any i2c devices to - // fully initalise - delay_ms(50); + set_mode(flash_last_mode()); run_script(&scene_state, INIT_SCRIPT); scene_state.initializing = false; @@ -881,7 +1023,6 @@ int main(void) { print_dbg_ulong(profile_delta_us(&prof_ADC)); print_dbg("\r\nScreen Refresh:\t"); print_dbg_ulong(profile_delta_us(&prof_ScreenRefresh)); - } #endif } diff --git a/module/pattern_mode.c b/module/pattern_mode.c index 7bdcd651..72f24a83 100644 --- a/module/pattern_mode.c +++ b/module/pattern_mode.c @@ -17,7 +17,7 @@ #include "conf_usb_host.h" // needed in order to include "usb_protocol_hid.h" #include "usb_protocol_hid.h" -static int16_t copy_buffer; +static int16_t value_copy_buffer; static uint8_t pattern; // which pattern are we editting static uint8_t base; // base + offset determine what we are editting static uint8_t offset; @@ -39,17 +39,44 @@ void set_pattern_mode() { edit_buffer = 0; } +uint8_t get_pattern_offset() { + return offset; +} + +void set_pattern_offset(uint8_t o) { + base = 0; + offset = o; + dirty = true; +} + +void set_pattern_selected_value(uint8_t p, uint8_t offset) { + pattern = p; + base = offset; + dirty = true; +} + +void pattern_up() { + editing_number = false; + if (base) + base--; + else if (offset) + offset--; + dirty = true; +} + +void pattern_down() { + editing_number = false; + base++; + if (base == 8) { + base = 7; + if (offset < 56) { offset++; } + } + dirty = true; +} + void process_pattern_keys(uint8_t k, uint8_t m, bool is_held_key) { // : move down - if (match_no_mod(m, k, HID_DOWN)) { - editing_number = false; - base++; - if (base == 8) { - base = 7; - if (offset < 56) { offset++; } - } - dirty = true; - } + if (match_no_mod(m, k, HID_DOWN)) { pattern_down(); } // alt-: move a page down else if (match_alt(m, k, HID_DOWN)) { editing_number = false; @@ -63,12 +90,7 @@ void process_pattern_keys(uint8_t k, uint8_t m, bool is_held_key) { } // : move up else if (match_no_mod(m, k, HID_UP)) { - editing_number = false; - if (base) - base--; - else if (offset) - offset--; - dirty = true; + pattern_up(); } // alt-: move a page up else if (match_alt(m, k, HID_UP)) { @@ -206,7 +228,8 @@ void process_pattern_keys(uint8_t k, uint8_t m, bool is_held_key) { // alt-x: cut value (n.b. ctrl-x not supported) else if (match_alt(m, k, HID_X)) { editing_number = false; - copy_buffer = ss_get_pattern_val(&scene_state, pattern, base + offset); + value_copy_buffer = + ss_get_pattern_val(&scene_state, pattern, base + offset); for (int i = base + offset; i < 63; i++) { int16_t v = ss_get_pattern_val(&scene_state, pattern, i + 1); ss_set_pattern_val(&scene_state, pattern, i, v); @@ -221,15 +244,16 @@ void process_pattern_keys(uint8_t k, uint8_t m, bool is_held_key) { // alt-c: copy value (n.b. ctrl-c not supported) else if (match_alt(m, k, HID_C)) { if (editing_number) - copy_buffer = edit_buffer; + value_copy_buffer = edit_buffer; else - copy_buffer = + value_copy_buffer = ss_get_pattern_val(&scene_state, pattern, base + offset); } // alt-v: paste value (n.b. ctrl-v not supported) else if (match_alt(m, k, HID_V)) { editing_number = false; - ss_set_pattern_val(&scene_state, pattern, base + offset, copy_buffer); + ss_set_pattern_val(&scene_state, pattern, base + offset, + value_copy_buffer); dirty = true; } // shift-alt-v: insert value @@ -243,7 +267,8 @@ void process_pattern_keys(uint8_t k, uint8_t m, bool is_held_key) { if (l >= base + offset && l < 63) { ss_set_pattern_len(&scene_state, pattern, l + 1); } - ss_set_pattern_val(&scene_state, pattern, base + offset, copy_buffer); + ss_set_pattern_val(&scene_state, pattern, base + offset, + value_copy_buffer); dirty = true; } // shift-l: set length to current position @@ -383,7 +408,7 @@ void process_pattern_keys(uint8_t k, uint8_t m, bool is_held_key) { } void process_pattern_knob(uint16_t knob, uint8_t m) { - if (mod_only_ctrl(m)) { + if (mod_only_ctrl_alt(m)) { ss_set_pattern_val(&scene_state, pattern, base + offset, knob >> 7); dirty = true; } diff --git a/module/pattern_mode.h b/module/pattern_mode.h index 1b9133f3..512e400a 100644 --- a/module/pattern_mode.h +++ b/module/pattern_mode.h @@ -8,5 +8,10 @@ void set_pattern_mode(void); void process_pattern_keys(uint8_t key, uint8_t mod_key, bool is_held_key); void process_pattern_knob(uint16_t knob, uint8_t mod_key); uint8_t screen_refresh_pattern(void); +uint8_t get_pattern_offset(void); +void set_pattern_offset(uint8_t offset); +void set_pattern_selected_value(uint8_t pattern, uint8_t offset); +void pattern_up(void); +void pattern_down(void); #endif diff --git a/module/preset_r_mode.c b/module/preset_r_mode.c index e0bd4628..aa124fdf 100644 --- a/module/preset_r_mode.c +++ b/module/preset_r_mode.c @@ -15,44 +15,55 @@ #include "usb_protocol_hid.h" static uint8_t offset; -static uint8_t knob_last; +static uint8_t preset_last; static bool dirty; static void do_preset_read(void); -void set_preset_r_mode(uint16_t knob) { - knob_last = knob >> 7; +uint8_t get_preset() { + return preset_last; +} + +void set_preset_r_mode(uint8_t preset) { + preset_last = preset; offset = 0; dirty = true; } -void process_preset_r_knob(uint16_t knob, uint8_t mod_key) { - uint8_t knob_now = knob >> 7; - if (knob_now != knob_last) { - preset_select = knob_now; - knob_last = knob_now; +void process_preset_r_preset(uint8_t preset) { + if (preset != preset_last) { + preset_select = preset; + preset_last = preset; dirty = true; } } -void process_preset_r_long_front() { +void process_preset_r_load() { do_preset_read(); } +void preset_line_down() { + if (offset < SCENE_TEXT_LINES - 8) { + offset++; + dirty = true; + } +} + +void preset_line_up() { + if (offset) { + offset--; + dirty = true; + } +} + void process_preset_r_keys(uint8_t k, uint8_t m, bool is_held_key) { // or C-n: line down if (match_no_mod(m, k, HID_DOWN) || match_ctrl(m, k, HID_N)) { - if (offset < SCENE_TEXT_LINES - 8) { - offset++; - dirty = true; - } + preset_line_down(); } // or C-p: line up else if (match_no_mod(m, k, HID_UP) || match_ctrl(m, k, HID_P)) { - if (offset) { - offset--; - dirty = true; - } + preset_line_up(); } // or [: preset down else if (match_no_mod(m, k, HID_LEFT) || @@ -95,6 +106,7 @@ uint8_t screen_refresh_preset_r() { }; void do_preset_read() { + ss_grid_init(&scene_state); flash_read(preset_select, &scene_state, &scene_text); flash_update_last_saved_scene(preset_select); ss_set_scene(&scene_state, preset_select); diff --git a/module/preset_r_mode.h b/module/preset_r_mode.h index b6aa68ad..010c69b1 100644 --- a/module/preset_r_mode.h +++ b/module/preset_r_mode.h @@ -4,9 +4,12 @@ #include #include -void set_preset_r_mode(uint16_t knob); -void process_preset_r_knob(uint16_t knob, uint8_t mod_key); -void process_preset_r_long_front(void); +uint8_t get_preset(void); +void set_preset_r_mode(uint8_t preset); +void process_preset_r_preset(uint8_t preset); +void preset_line_down(void); +void preset_line_up(void); +void process_preset_r_load(void); void process_preset_r_keys(uint8_t key, uint8_t mod_key, bool is_held_key); uint8_t screen_refresh_preset_r(void); diff --git a/module/profile.h b/module/profile.h index f3c05a4c..9b0b3c1c 100644 --- a/module/profile.h +++ b/module/profile.h @@ -1,5 +1,5 @@ -#include "sysclk.h" #include "stdbool.h" +#include "sysclk.h" typedef struct { uint32_t last; @@ -11,7 +11,7 @@ static inline void profile_update(profile_t *p) { uint32_t count = Get_system_register(AVR32_COUNT); if (count < p->last) - p->delta = INT32_MAX - p->last + count; + p->delta = INT32_MAX - p->last + count; else p->delta = count - p->last; diff --git a/module/screensaver_mode.c b/module/screensaver_mode.c deleted file mode 100644 index d182ce91..00000000 --- a/module/screensaver_mode.c +++ /dev/null @@ -1,18 +0,0 @@ -#include "screensaver_mode.h" -#include "globals.h" -#include "region.h" - -static bool blank; - -void set_screensaver_mode() { - for (int i = 0; i < 8; i++) region_fill(&line[i], 0); - blank = false; -} - -uint8_t screen_refresh_screensaver() { - if (!blank) { - blank = true; - return 0xFF; - } - return 0; -} diff --git a/module/screensaver_mode.h b/module/screensaver_mode.h deleted file mode 100644 index bef715cb..00000000 --- a/module/screensaver_mode.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef SCREENSAVER_H -#define SCREENSAVER_H -#include - -#define SS_TIMEOUT 90 /* minutes */ * 60 * 100 -//#define SS_TIMEOUT 1000 // 10 seconds -#define SS_DROP_KEYSTROKE 1 // drops the keystroke that caused wake-up - -uint8_t screen_refresh_screensaver(void); -void set_screensaver_mode(void); - -#endif diff --git a/module/usb_disk_mode.c b/module/usb_disk_mode.c index a7127f1a..67d98039 100644 --- a/module/usb_disk_mode.c +++ b/module/usb_disk_mode.c @@ -26,6 +26,14 @@ #include "usb_protocol_msc.h" +static uint8_t grid_state = 0; +static uint16_t grid_count = 0; +static uint8_t grid_num = 0; + +static void grid_usb_write(scene_state_t *scene); +static void grid_usb_read(scene_state_t *scene, char c); + + void tele_usb_disk() { char input_buffer[32]; print_dbg("\r\nusb"); @@ -102,7 +110,7 @@ void tele_usb_disk() { char blank = 0; for (int l = 0; l < SCENE_TEXT_LINES; l++) { if (strlen(text[l])) { - file_write_buf((uint8_t*)text[l], strlen(text[l])); + file_write_buf((uint8_t *)text[l], strlen(text[l])); file_putc('\n'); blank = 0; } @@ -127,7 +135,7 @@ void tele_usb_disk() { for (int l = 0; l < ss_get_script_len(&scene, s); l++) { file_putc('\n'); print_command(ss_get_script_command(&scene, s, l), input); - file_write_buf((uint8_t*)input, strlen(input)); + file_write_buf((uint8_t *)input, strlen(input)); } } @@ -139,7 +147,7 @@ void tele_usb_disk() { for (int b = 0; b < 4; b++) { itoa(ss_get_pattern_len(&scene, b), input, 10); - file_write_buf((uint8_t*)input, strlen(input)); + file_write_buf((uint8_t *)input, strlen(input)); if (b == 3) file_putc('\n'); else @@ -148,7 +156,7 @@ void tele_usb_disk() { for (int b = 0; b < 4; b++) { itoa(ss_get_pattern_wrap(&scene, b), input, 10); - file_write_buf((uint8_t*)input, strlen(input)); + file_write_buf((uint8_t *)input, strlen(input)); if (b == 3) file_putc('\n'); else @@ -157,7 +165,7 @@ void tele_usb_disk() { for (int b = 0; b < 4; b++) { itoa(ss_get_pattern_start(&scene, b), input, 10); - file_write_buf((uint8_t*)input, strlen(input)); + file_write_buf((uint8_t *)input, strlen(input)); if (b == 3) file_putc('\n'); else @@ -166,7 +174,7 @@ void tele_usb_disk() { for (int b = 0; b < 4; b++) { itoa(ss_get_pattern_end(&scene, b), input, 10); - file_write_buf((uint8_t*)input, strlen(input)); + file_write_buf((uint8_t *)input, strlen(input)); if (b == 3) file_putc('\n'); else @@ -178,7 +186,7 @@ void tele_usb_disk() { for (int l = 0; l < 64; l++) { for (int b = 0; b < 4; b++) { itoa(ss_get_pattern_val(&scene, b, l), input, 10); - file_write_buf((uint8_t*)input, strlen(input)); + file_write_buf((uint8_t *)input, strlen(input)); if (b == 3) file_putc('\n'); else @@ -186,6 +194,8 @@ void tele_usb_disk() { } } + grid_usb_write(&scene); + file_close(); lun_state |= (1 << lun); // LUN test is done. @@ -253,6 +263,10 @@ void tele_usb_disk() { s = 9; else if (c == 'P') s = 10; + else if (c == 'G') { + grid_state = grid_num = grid_count = 0; + s = 11; + } else { s = c - 49; if (s < 0 || s > 7) s = -1; @@ -288,6 +302,7 @@ void tele_usb_disk() { if (c == '\n') { if (p && l < SCRIPT_MAX_COMMANDS) { tele_command_t temp; + temp.comment = false; error_t status; char error_msg[TELE_ERROR_MSG_LENGTH]; status = parse(input, &temp, error_msg); @@ -376,6 +391,10 @@ void tele_usb_disk() { p++; } } + // GRID + else if (s == 11) { + grid_usb_read(&scene, c); + } } @@ -398,3 +417,45 @@ void tele_usb_disk() { nav_exit(); } + +char fvalue[36]; +static void grid_usb_write(scene_state_t *scene) { + file_putc('\n'); + file_putc('#'); + file_putc('G'); + file_putc('\n'); + for (uint16_t i = 0; i < GRID_BUTTON_COUNT; i++) { + file_putc('0' + scene->grid.button[i].state); + if ((i & 15) == 15) file_putc('\n'); + } + file_putc('\n'); + for (uint16_t i = 0; i < GRID_FADER_COUNT; i++) { + itoa(scene->grid.fader[i].value, fvalue, 10); + file_write_buf((uint8_t *)fvalue, strlen(fvalue)); + file_putc((i & 15) == 15 ? '\n' : '\t'); + } +} + +static void grid_usb_read(scene_state_t *scene, char c) { + if (grid_state == 0) { + if (c >= '0' && c <= '9') { + scene->grid.button[grid_count].state = c != '0'; + if (++grid_count >= GRID_BUTTON_COUNT) { + grid_count = 0; + grid_state = 1; + if (!file_eof()) file_getc(); + if (!file_eof()) file_getc(); // eat \n\n + } + } + } + else if (grid_state == 1) { + if (c >= '0' && c <= '9') { grid_num = grid_num * 10 + c - '0'; } + else if (c == '\t' || c == '\n') { + if (grid_count < GRID_FADER_COUNT) { + scene->grid.fader[grid_count].value = grid_num; + grid_num = 0; + grid_count++; + } + } + } +} \ No newline at end of file diff --git a/simulator/Makefile b/simulator/Makefile index 2981f4f3..67cb679d 100644 --- a/simulator/Makefile +++ b/simulator/Makefile @@ -10,7 +10,8 @@ OBJ = tt.o ../src/teletype.o ../src/command.o ../src/helpers.o \ ../src/ops/metronome.o ../src/ops/maths.o ../src/ops/orca.o \ ../src/ops/patterns.o ../src/ops/queue.o ../src/ops/stack.o \ ../src/ops/telex.o ../src/ops/variables.o ../src/ops/whitewhale.o \ - ../src/ops/wslash.o ../src/ops/init.o \ + ../src/ops/init.o ../src/ops/grid_ops.o ../src/ops/er301.o \ + ../src/ops/fader.o ../src/ops/matrixarchate.o ../src/ops/wslash.o \ ../libavr32/src/euclidean/euclidean.o ../libavr32/src/euclidean/data.o \ ../libavr32/src/util.o diff --git a/simulator/tt.c b/simulator/tt.c index 5c7a49b0..6a0f2dfe 100644 --- a/simulator/tt.c +++ b/simulator/tt.c @@ -11,6 +11,10 @@ #include "util.h" +uint32_t tele_get_ticks() { + return 0; +} + void tele_metro_updated() { printf("METRO UPDATED"); printf("\n"); @@ -36,8 +40,8 @@ void tele_cv_slew(uint8_t i, int16_t v) { printf("\n"); } -void tele_update_in(void) { - printf("UPDATE IN"); +void tele_update_adc(uint8_t force) { + printf("UPDATE ADC force:%s", force ? "true" : "false"); printf("\n"); } @@ -102,6 +106,11 @@ void tele_save_calibration() {} void tele_profile_script(size_t s) {} void tele_profile_delay(uint8_t d) {} +void grid_key_press(uint8_t x, uint8_t y, uint8_t z) { + printf("GRID KEY PRESS x:%" PRIu8 " y:%" PRIu8 " z:%" PRIu8, x, y, z); + printf("\n"); +} + int main() { char *in; time_t t; diff --git a/src/command.h b/src/command.h index ff6fc991..82a7de20 100644 --- a/src/command.h +++ b/src/command.h @@ -1,6 +1,7 @@ #ifndef _COMMAND_H_ #define _COMMAND_H_ +#include #include #define COMMAND_MAX_LENGTH 16 @@ -16,6 +17,7 @@ typedef struct { uint8_t length; int8_t separator; tele_data_t data[COMMAND_MAX_LENGTH]; + bool comment; } tele_command_t; void copy_command(tele_command_t *dst, const tele_command_t *src); diff --git a/src/match_token.rl b/src/match_token.rl index bcde2146..9bc38d1a 100644 --- a/src/match_token.rl +++ b/src/match_token.rl @@ -109,6 +109,20 @@ "PN.PUSH" => { MATCH_OP(E_OP_PN_PUSH); }; "P.POP" => { MATCH_OP(E_OP_P_POP); }; "PN.POP" => { MATCH_OP(E_OP_PN_POP); }; + "P.MIN" => { MATCH_OP(E_OP_P_MIN); }; + "PN.MIN" => { MATCH_OP(E_OP_PN_MIN); }; + "P.MAX" => { MATCH_OP(E_OP_P_MAX); }; + "PN.MAX" => { MATCH_OP(E_OP_PN_MAX); }; + "P.RND" => { MATCH_OP(E_OP_P_RND); }; + "PN.RND" => { MATCH_OP(E_OP_PN_RND); }; + "P.+" => { MATCH_OP(E_OP_P_ADD); }; + "PN.+" => { MATCH_OP(E_OP_PN_ADD); }; + "P.-" => { MATCH_OP(E_OP_P_SUB); }; + "PN.-" => { MATCH_OP(E_OP_PN_SUB); }; + "P.+W" => { MATCH_OP(E_OP_P_ADDW); }; + "PN.+W" => { MATCH_OP(E_OP_PN_ADDW); }; + "P.-W" => { MATCH_OP(E_OP_P_SUBW); }; + "PN.-W" => { MATCH_OP(E_OP_PN_SUBW); }; # queue "Q" => { MATCH_OP(E_OP_Q); }; @@ -147,7 +161,9 @@ "DIV" => { MATCH_OP(E_OP_DIV); }; "MOD" => { MATCH_OP(E_OP_MOD); }; "RAND" => { MATCH_OP(E_OP_RAND); }; + "RND" => { MATCH_OP(E_OP_RND); }; "RRAND" => { MATCH_OP(E_OP_RRAND); }; + "RRND" => { MATCH_OP(E_OP_RRND); }; "R" => { MATCH_OP(E_OP_R); }; "R.MIN" => { MATCH_OP(E_OP_R_MIN); }; "R.MAX" => { MATCH_OP(E_OP_R_MAX); }; @@ -156,6 +172,7 @@ "MAX" => { MATCH_OP(E_OP_MAX); }; "LIM" => { MATCH_OP(E_OP_LIM); }; "WRAP" => { MATCH_OP(E_OP_WRAP); }; + "WRP" => { MATCH_OP(E_OP_WRP); }; "QT" => { MATCH_OP(E_OP_QT); }; "AVG" => { MATCH_OP(E_OP_AVG); }; "EQ" => { MATCH_OP(E_OP_EQ); }; @@ -174,6 +191,7 @@ "OR" => { MATCH_OP(E_OP_OR); }; "JI" => { MATCH_OP(E_OP_JI); }; "SCALE" => { MATCH_OP(E_OP_SCALE); }; + "SCL" => { MATCH_OP(E_OP_SCL); }; "N" => { MATCH_OP(E_OP_N); }; "V" => { MATCH_OP(E_OP_V); }; "VV" => { MATCH_OP(E_OP_VV); }; @@ -190,6 +208,7 @@ "CHAOS" => { MATCH_OP(E_OP_CHAOS); }; "CHAOS.R" => { MATCH_OP(E_OP_CHAOS_R); }; "CHAOS.ALG" => { MATCH_OP(E_OP_CHAOS_ALG); }; + "?" => { MATCH_OP(E_OP_TIF); }; "+" => { MATCH_OP(E_OP_SYM_PLUS); }; "-" => { MATCH_OP(E_OP_SYM_DASH); }; "*" => { MATCH_OP(E_OP_SYM_STAR); }; @@ -215,6 +234,7 @@ # controlflow "SCRIPT" => { MATCH_OP(E_OP_SCRIPT); }; + "$" => { MATCH_OP(E_OP_SYM_DOLLAR); }; "KILL" => { MATCH_OP(E_OP_KILL); }; "SCENE" => { MATCH_OP(E_OP_SCENE); }; "BREAK" => { MATCH_OP(E_OP_BREAK); }; @@ -423,7 +443,11 @@ "TO.ENV.EOR" => { MATCH_OP(E_OP_TO_ENV_EOR); }; "TO.ENV.EOC" => { MATCH_OP(E_OP_TO_ENV_EOC); }; - "TO.ENV.LOOP" => { MATCH_OP(E_OP_TO_ENV_LOOP); }; + "TO.ENV.LOOP" => { MATCH_OP(E_OP_TO_ENV_LOOP); }; + + "TO.ENV" => { MATCH_OP(E_OP_TO_ENV); }; + "TO.CV.CALIB" => { MATCH_OP(E_OP_TO_CV_CALIB); }; + "TO.CV.RESET" => { MATCH_OP(E_OP_TO_CV_RESET); }; "TI.PARAM" => { MATCH_OP(E_OP_TI_PARAM); }; "TI.PARAM.QT" => { MATCH_OP(E_OP_TI_PARAM_QT); }; @@ -451,13 +475,102 @@ "TI.PRM.MAP" => { MATCH_OP(E_OP_TI_PRM_MAP); }; "TI.PRM.INIT" => { MATCH_OP(E_OP_TI_PRM_INIT); }; + # fader + "FADER" => { MATCH_OP(E_OP_FADER); }; + "FB" => { MATCH_OP(E_OP_FB); }; + + # ER301 + "SC.TR" => { MATCH_OP(E_OP_SC_TR); }; + "SC.TR.TOG" => { MATCH_OP(E_OP_SC_TR_TOG); }; + "SC.TR.PULSE" => { MATCH_OP(E_OP_SC_TR_PULSE); }; + "SC.TR.TIME" => { MATCH_OP(E_OP_SC_TR_TIME); }; + "SC.TR.POL" => { MATCH_OP(E_OP_SC_TR_POL); }; + + "SC.TR.P" => { MATCH_OP(E_OP_SC_TR_P); }; + + "SC.CV" => { MATCH_OP(E_OP_SC_CV); }; + "SC.CV.SLEW" => { MATCH_OP(E_OP_SC_CV_SLEW); }; + "SC.CV.SET" => { MATCH_OP(E_OP_SC_CV_SET); }; + "SC.CV.OFF" => { MATCH_OP(E_OP_SC_CV_OFF); }; + + # grid + "G.RST" => { MATCH_OP(E_OP_G_RST); }; + "G.CLR" => { MATCH_OP(E_OP_G_CLR); }; + "G.ROTATE" => { MATCH_OP(E_OP_G_ROTATE); }; + "G.DIM" => { MATCH_OP(E_OP_G_DIM); }; + "G.KEY" => { MATCH_OP(E_OP_G_KEY); }; + + "G.GRP" => { MATCH_OP(E_OP_G_GRP); }; + "G.GRP.EN" => { MATCH_OP(E_OP_G_GRP_EN); }; + "G.GRP.RST" => { MATCH_OP(E_OP_G_GRP_RST); }; + "G.GRP.SW" => { MATCH_OP(E_OP_G_GRP_SW); }; + "G.GRP.SC" => { MATCH_OP(E_OP_G_GRP_SC); }; + "G.GRPI" => { MATCH_OP(E_OP_G_GRPI); }; + + "G.LED" => { MATCH_OP(E_OP_G_LED); }; + "G.LED.C" => { MATCH_OP(E_OP_G_LED_C); }; + "G.REC" => { MATCH_OP(E_OP_G_REC); }; + "G.RCT" => { MATCH_OP(E_OP_G_RCT); }; + + "G.BTN" => { MATCH_OP(E_OP_G_BTN); }; + "G.BTX" => { MATCH_OP(E_OP_G_BTX); }; + "G.GBT" => { MATCH_OP(E_OP_G_GBT); }; + "G.GBX" => { MATCH_OP(E_OP_G_GBX); }; + "G.BTN.EN" => { MATCH_OP(E_OP_G_BTN_EN); }; + "G.BTN.V" => { MATCH_OP(E_OP_G_BTN_V); }; + "G.BTN.L" => { MATCH_OP(E_OP_G_BTN_L); }; + "G.BTN.X" => { MATCH_OP(E_OP_G_BTN_X); }; + "G.BTN.Y" => { MATCH_OP(E_OP_G_BTN_Y); }; + "G.BTNI" => { MATCH_OP(E_OP_G_BTNI); }; + "G.BTNV" => { MATCH_OP(E_OP_G_BTNV); }; + "G.BTNL" => { MATCH_OP(E_OP_G_BTNL); }; + "G.BTNX" => { MATCH_OP(E_OP_G_BTNX); }; + "G.BTNY" => { MATCH_OP(E_OP_G_BTNY); }; + "G.BTN.PR" => { MATCH_OP(E_OP_G_BTN_PR); }; + "G.BTN.SW" => { MATCH_OP(E_OP_G_BTN_SW); }; + "G.GBTN.V" => { MATCH_OP(E_OP_G_GBTN_V); }; + "G.GBTN.L" => { MATCH_OP(E_OP_G_GBTN_L); }; + "G.GBTN.C" => { MATCH_OP(E_OP_G_GBTN_C); }; + "G.GBTN.I" => { MATCH_OP(E_OP_G_GBTN_I); }; + "G.GBTN.W" => { MATCH_OP(E_OP_G_GBTN_W); }; + "G.GBTN.H" => { MATCH_OP(E_OP_G_GBTN_H); }; + "G.GBTN.X1" => { MATCH_OP(E_OP_G_GBTN_X1); }; + "G.GBTN.X2" => { MATCH_OP(E_OP_G_GBTN_X2); }; + "G.GBTN.Y1" => { MATCH_OP(E_OP_G_GBTN_Y1); }; + "G.GBTN.Y2" => { MATCH_OP(E_OP_G_GBTN_Y2); }; + + "G.FDR" => { MATCH_OP(E_OP_G_FDR); }; + "G.FDX" => { MATCH_OP(E_OP_G_FDX); }; + "G.GFD" => { MATCH_OP(E_OP_G_GFD); }; + "G.GFX" => { MATCH_OP(E_OP_G_GFX); }; + "G.FDR.EN" => { MATCH_OP(E_OP_G_FDR_EN); }; + "G.FDR.V" => { MATCH_OP(E_OP_G_FDR_V); }; + "G.FDR.N" => { MATCH_OP(E_OP_G_FDR_N); }; + "G.FDR.L" => { MATCH_OP(E_OP_G_FDR_L); }; + "G.FDR.X" => { MATCH_OP(E_OP_G_FDR_X); }; + "G.FDR.Y" => { MATCH_OP(E_OP_G_FDR_Y); }; + "G.FDRI" => { MATCH_OP(E_OP_G_FDRI); }; + "G.FDRV" => { MATCH_OP(E_OP_G_FDRV); }; + "G.FDRN" => { MATCH_OP(E_OP_G_FDRN); }; + "G.FDRL" => { MATCH_OP(E_OP_G_FDRL); }; + "G.FDRX" => { MATCH_OP(E_OP_G_FDRX); }; + "G.FDRY" => { MATCH_OP(E_OP_G_FDRY); }; + "G.FDR.PR" => { MATCH_OP(E_OP_G_FDR_PR); }; + "G.GFDR.V" => { MATCH_OP(E_OP_G_GFDR_V); }; + "G.GFDR.N" => { MATCH_OP(E_OP_G_GFDR_N); }; + "G.GFDR.L" => { MATCH_OP(E_OP_G_GFDR_L); }; + "G.GFDR.RN" => { MATCH_OP(E_OP_G_GFDR_RN); }; + + "G.XYP" => { MATCH_OP(E_OP_G_XYP); }; + "G.XYP.X" => { MATCH_OP(E_OP_G_XYP_X); }; + "G.XYP.Y" => { MATCH_OP(E_OP_G_XYP_Y); }; + # w/ "WS.PLAY" => { MATCH_OP(E_OP_WS_PLAY); }; "WS.REC" => { MATCH_OP(E_OP_WS_REC); }; "WS.CUE" => { MATCH_OP(E_OP_WS_CUE); }; "WS.LOOP" => { MATCH_OP(E_OP_WS_LOOP); }; - # MODS # controlflow "IF" => { MATCH_MOD(E_MOD_IF); }; @@ -473,6 +586,24 @@ "PROB" => { MATCH_MOD(E_MOD_PROB); }; "DEL" => { MATCH_MOD(E_MOD_DEL); }; + # matrixarchate + "MA.SELECT" => { MATCH_OP(E_OP_MA_SELECT); }; + "MA.STEP" => { MATCH_OP(E_OP_MA_STEP); }; + "MA.RESET" => { MATCH_OP(E_OP_MA_RESET); }; + "MA.PGM" => { MATCH_OP(E_OP_MA_PGM); }; + "MA.ON" => { MATCH_OP(E_OP_MA_ON); }; + "MA.PON" => { MATCH_OP(E_OP_MA_PON); }; + "MA.OFF" => { MATCH_OP(E_OP_MA_OFF); }; + "MA.POFF" => { MATCH_OP(E_OP_MA_POFF); }; + "MA.SET" => { MATCH_OP(E_OP_MA_SET); }; + "MA.PSET" => { MATCH_OP(E_OP_MA_PSET); }; + "MA.COL" => { MATCH_OP(E_OP_MA_COL); }; + "MA.PCOL" => { MATCH_OP(E_OP_MA_PCOL); }; + "MA.ROW" => { MATCH_OP(E_OP_MA_ROW); }; + "MA.PROW" => { MATCH_OP(E_OP_MA_PROW); }; + "MA.CLR" => { MATCH_OP(E_OP_MA_CLR); }; + "MA.PCLR" => { MATCH_OP(E_OP_MA_PCLR); }; + # stack "S" => { MATCH_MOD(E_MOD_S); }; *|; diff --git a/src/ops/ansible.c b/src/ops/ansible.c index 25a83c0e..a2c4fbf9 100644 --- a/src/ops/ansible.c +++ b/src/ops/ansible.c @@ -37,14 +37,14 @@ static void op_KR_RES_get(const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); static void op_KR_CV_get(const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); -static void op_KR_MUTE_get(const void *data, scene_state_t *ss, exec_state_t *es, - command_state_t *cs); -static void op_KR_MUTE_set(const void *data, scene_state_t *ss, exec_state_t *es, - command_state_t *cs); -static void op_KR_TMUTE_get(const void *data, scene_state_t *ss, exec_state_t *es, - command_state_t *cs); +static void op_KR_MUTE_get(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); +static void op_KR_MUTE_set(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); +static void op_KR_TMUTE_get(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); static void op_KR_CLK_get(const void *data, scene_state_t *ss, exec_state_t *es, - command_state_t *cs); + command_state_t *cs); static void op_ME_PRE_get(const void *data, scene_state_t *ss, exec_state_t *es, @@ -348,16 +348,18 @@ static void op_KR_CV_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), cs_push(cs, (d[0] << 8) + d[1]); } -static void op_KR_MUTE_set(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), - exec_state_t *NOTUSED(es), command_state_t *cs) { +static void op_KR_MUTE_set(const void *NOTUSED(data), + scene_state_t *NOTUSED(ss), + exec_state_t *NOTUSED(es), command_state_t *cs) { int16_t a = cs_pop(cs); int16_t b = cs_pop(cs); uint8_t d[] = { II_KR_MUTE, a, b }; tele_ii_tx(II_KR_ADDR, d, 3); } -static void op_KR_MUTE_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), - exec_state_t *NOTUSED(es), command_state_t *cs) { +static void op_KR_MUTE_get(const void *NOTUSED(data), + scene_state_t *NOTUSED(ss), + exec_state_t *NOTUSED(es), command_state_t *cs) { int16_t a = cs_pop(cs); uint8_t d[] = { II_KR_MUTE | II_GET, a }; uint8_t addr = II_KR_ADDR; @@ -367,8 +369,9 @@ static void op_KR_MUTE_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss) cs_push(cs, d[0]); } -static void op_KR_TMUTE_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), - exec_state_t *NOTUSED(es), command_state_t *cs) { +static void op_KR_TMUTE_get(const void *NOTUSED(data), + scene_state_t *NOTUSED(ss), + exec_state_t *NOTUSED(es), command_state_t *cs) { int16_t a = cs_pop(cs); uint8_t d[] = { II_KR_TMUTE, a }; tele_ii_tx(II_KR_ADDR, d, 2); diff --git a/src/ops/controlflow.c b/src/ops/controlflow.c index 73762127..ced11e4e 100644 --- a/src/ops/controlflow.c +++ b/src/ops/controlflow.c @@ -60,6 +60,8 @@ const tele_mod_t mod_OTHER = MAKE_MOD(OTHER, mod_OTHER_func, 0); const tele_op_t op_SCRIPT = MAKE_GET_SET_OP(SCRIPT, op_SCRIPT_get, op_SCRIPT_set, 0, true); +const tele_op_t op_SYM_DOLLAR = + MAKE_ALIAS_OP($, op_SCRIPT_get, op_SCRIPT_set, 0, true); const tele_op_t op_KILL = MAKE_GET_OP(KILL, op_KILL_get, 0, false); const tele_op_t op_SCENE = MAKE_GET_SET_OP(SCENE, op_SCENE_get, op_SCENE_set, 0, true); @@ -118,16 +120,20 @@ static void mod_L_func(scene_state_t *ss, exec_state_t *es, command_state_t *cs, // using a pointer means that the loop contents can a interact with the // iterator, allowing users to roll back a loop or advance it faster int16_t *i = &es_variables(es)->i; + *i = a; // Forward loop if (a < b) { // continue the loop whenever the _pointed-to_ I meets the condition // this means that I can be interacted with inside the loop command - for (*i = a; *i <= b; (*i)++) { - // the increment statement has careful syntax, because the - // ++ operator has precedence over the dereference * operator + + // iterate with higher precision to account for b == 32767 + for (int32_t l = a; l <= b; l++) { process_command(ss, es, post_command); if (es_variables(es)->breaking) break; + // the increment statement has careful syntax, because the + // ++ operator has precedence over the dereference * operator + (*i)++; } if (!es_variables(es)->breaking) @@ -135,8 +141,10 @@ static void mod_L_func(scene_state_t *ss, exec_state_t *es, command_state_t *cs, } // Reverse loop (also works for equal values (either loop would)) else { - for (*i = a; *i >= b && !es_variables(es)->breaking; (*i)--) + for (int32_t l = a; l >= b && !es_variables(es)->breaking; l--) { process_command(ss, es, post_command); + (*i)--; + } if (!es_variables(es)->breaking) (*i)++; } } diff --git a/src/ops/controlflow.h b/src/ops/controlflow.h index 08057461..acc76cac 100644 --- a/src/ops/controlflow.h +++ b/src/ops/controlflow.h @@ -20,5 +20,6 @@ extern const tele_op_t op_BREAK; extern const tele_op_t op_BRK; extern const tele_op_t op_SYNC; +extern const tele_op_t op_SYM_DOLLAR; #endif diff --git a/src/ops/er301.c b/src/ops/er301.c new file mode 100644 index 00000000..b24bf80e --- /dev/null +++ b/src/ops/er301.c @@ -0,0 +1,109 @@ +#include "ops/er301.h" + +#include "helpers.h" +#include "ii.h" +#include "teletype.h" +#include "teletype_io.h" +#include "telex.h" + +static void op_SC_TR_get(const void *data, scene_state_t *ss, exec_state_t *es, + command_state_t *cs); +static void op_SC_TR_TOG_get(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); +static void op_SC_TR_PULSE_get(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); +static void op_SC_TR_TIME_get(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); +static void op_SC_TR_POL_get(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); + +static void op_SC_CV_get(const void *data, scene_state_t *ss, exec_state_t *es, + command_state_t *cs); +static void op_SC_CV_SLEW_get(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); +static void op_SC_CV_SET_get(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); +static void op_SC_CV_OFF_get(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); + +const tele_op_t op_SC_TR = MAKE_GET_OP(SC.TR, op_SC_TR_get, 2, false); +const tele_op_t op_SC_TR_TOG = + MAKE_GET_OP(SC.TR.TOG, op_SC_TR_TOG_get, 1, false); +const tele_op_t op_SC_TR_PULSE = + MAKE_GET_OP(SC.TR.PULSE, op_SC_TR_PULSE_get, 1, false); +const tele_op_t op_SC_TR_TIME = + MAKE_GET_OP(SC.TR.TIME, op_SC_TR_TIME_get, 2, false); +const tele_op_t op_SC_TR_POL = + MAKE_GET_OP(SC.TR.POL, op_SC_TR_POL_get, 2, false); + +const tele_op_t op_SC_TR_P = + MAKE_ALIAS_OP(SC.TR.P, op_SC_TR_PULSE_get, NULL, 1, false); + +const tele_op_t op_SC_CV = MAKE_GET_OP(SC.CV, op_SC_CV_get, 2, false); +const tele_op_t op_SC_CV_SLEW = + MAKE_GET_OP(SC.CV.SLEW, op_SC_CV_SLEW_get, 2, false); +const tele_op_t op_SC_CV_SET = + MAKE_GET_OP(SC.CV.SET, op_SC_CV_SET_get, 2, false); +const tele_op_t op_SC_CV_OFF = + MAKE_GET_OP(SC.CV.OFF, op_SC_CV_OFF_get, 2, false); + + +void ERSend(uint8_t command, uint16_t output, int16_t value, bool set) { + // zero-index the output + output -= 1; + // return if out of range + if (output < 0 || output > 299) return; + // convert the output to the device and the port + uint8_t port = output % 100; + uint8_t device = output / 100; + uint8_t address = ER301_1 + device; + // put the package in the i2c mail + SendIt(address, command, port, value, set); +} +void ERSet(uint8_t command, command_state_t *cs) { + uint16_t output = cs_pop(cs); + int16_t value = cs_pop(cs); + ERSend(command, output, value, true); +} +void ERCommand(uint8_t command, uint16_t output) { + ERSend(command, output, 0, false); +} + + +static void op_SC_TR_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + ERSet(TO_TR, cs); +} +static void op_SC_TR_TOG_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + ERCommand(TO_TR_TOG, cs_pop(cs)); +} +static void op_SC_TR_PULSE_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + ERCommand(TO_TR_PULSE, cs_pop(cs)); +} +static void op_SC_TR_TIME_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + ERSet(TO_TR_TIME, cs); +} +static void op_SC_TR_POL_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + ERSet(TO_TR_POL, cs); +} + +static void op_SC_CV_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + ERSet(TO_CV, cs); +} +static void op_SC_CV_SLEW_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + ERSet(TO_CV_SLEW, cs); +} +static void op_SC_CV_SET_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + ERSet(TO_CV_SET, cs); +} +static void op_SC_CV_OFF_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + ERSet(TO_CV_OFF, cs); +} \ No newline at end of file diff --git a/src/ops/er301.h b/src/ops/er301.h new file mode 100644 index 00000000..81b40f38 --- /dev/null +++ b/src/ops/er301.h @@ -0,0 +1,40 @@ +#ifndef _OPS_ER301_H_ +#define _OPS_ER301_H_ + +#include "ops/op.h" + +extern const tele_op_t op_SC_TR; +extern const tele_op_t op_SC_TR_TOG; +extern const tele_op_t op_SC_TR_PULSE; +extern const tele_op_t op_SC_TR_TIME; +extern const tele_op_t op_SC_TR_POL; + +extern const tele_op_t op_SC_TR_P; + +extern const tele_op_t op_SC_CV; +extern const tele_op_t op_SC_CV_SLEW; +extern const tele_op_t op_SC_CV_SET; +extern const tele_op_t op_SC_CV_OFF; + +void ERSend(uint8_t command, uint16_t output, int16_t value, bool set); +void ERSet(uint8_t command, command_state_t *cs); +void ERCommand(uint8_t command, uint16_t output); + +// not using these defines +// using the ones from the telex to make +// testing super-easy + +/* +#define SC_TR 0x00 +#define SC_TR_TOG 0x01 +#define SC_TR_TIME 0x02 +#define SC_TR_PULSE 0x05 +#define SC_TR_POL 0x06 + +#define SC_CV 0x10 +#define SC_CV_SET 0x11 +#define SC_CV_SLEW 0x12 +#define SC_CV_OFF 0x15 +*/ + +#endif \ No newline at end of file diff --git a/src/ops/fader.c b/src/ops/fader.c new file mode 100644 index 00000000..4e0dbbf7 --- /dev/null +++ b/src/ops/fader.c @@ -0,0 +1,34 @@ +#include "ops/fader.h" + +#include "helpers.h" +#include "ii.h" +#include "teletype.h" +#include "teletype_io.h" +#include "telex.h" + + +static void op_FADER_get(const void *data, scene_state_t *ss, exec_state_t *es, + command_state_t *cs); + + +const tele_op_t op_FADER = MAKE_GET_OP(FADER, op_FADER_get, 1, true); + +const tele_op_t op_FB = MAKE_ALIAS_OP(FB, op_FADER_get, NULL, 1, true); + + +static void op_FADER_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + uint16_t input = cs_pop(cs); + // zero-index the input + input -= 1; + // return if out of range + if (input < 0 || input > 15) { + cs_push(cs, 0); + return; + } + // convert the input to the device and the port + uint8_t port = input % 16; + uint8_t device = input / 16; + uint8_t address = FADER + device; + ReceiveIt(address, port, cs); +} \ No newline at end of file diff --git a/src/ops/fader.h b/src/ops/fader.h new file mode 100644 index 00000000..2ac8e035 --- /dev/null +++ b/src/ops/fader.h @@ -0,0 +1,9 @@ +#ifndef _OPS_FADER_H_ +#define _OPS_FADER_H_ + +#include "ops/op.h" + +extern const tele_op_t op_FADER; +extern const tele_op_t op_FB; + +#endif \ No newline at end of file diff --git a/src/ops/grid_ops.c b/src/ops/grid_ops.c new file mode 100644 index 00000000..a8c37b27 --- /dev/null +++ b/src/ops/grid_ops.c @@ -0,0 +1,1572 @@ +#include "ops/grid_ops.h" +#include "helpers.h" +#include "teletype.h" +#include "teletype_io.h" + +#define SG ss->grid +#define GB ss->grid.button[i] +#define GBC ss->grid.button[i].common +#define GF ss->grid.fader[i] +#define GFC ss->grid.fader[i].common +#define GXY ss->grid.xypad[i] +#define GXYC ss->grid.xypad[i].common + +#define GET_AND_CLAMP(value, min, max) \ + s16 value = cs_pop(cs); \ + if (value < (s16)(min)) \ + value = (min); \ + else if (value > (s16)(max)) \ + value = (max); + +#define GET_LEVEL(level) \ + s16 level = cs_pop(cs); \ + if (level < (s16)LED_OFF) \ + level = LED_OFF; \ + else if (level > (s16)15) \ + level = 15; + +#define CLAMP_X_Y_W_H(op) \ + if (x < (s16)0) { \ + w += x; \ + x = 0; \ + } \ + else if (x >= (s16)GRID_MAX_DIMENSION) \ + op; \ + if (w + x > (s16)GRID_MAX_DIMENSION) w = GRID_MAX_DIMENSION - x; \ + if (y < (s16)0) { \ + h += y; \ + y = 0; \ + } \ + else if (y >= (s16)GRID_MAX_DIMENSION) \ + op; \ + if (h + y > (s16)GRID_MAX_DIMENSION) h = GRID_MAX_DIMENSION - y; + +#define min(a, b) ((s32)(a) < (s32)(b) ? (s32)(a) : (s32)(b)) +#define max(a, b) ((s32)(a) > (s32)(b) ? (s32)(a) : (s32)(b)) + +// clang-format off + +static void grid_common_init(grid_common_t *gc); +static s32 scale(s32 a, s32 b, s32 x, s32 y, s32 value); +static void grid_rectangle(scene_state_t *ss, s16 x, s16 y, s16 w, s16 h, u8 fill, u8 border); +static void grid_init_button(scene_state_t *ss, s16 group, s16 i, s16 x, s16 y, s16 w, s16 h, s16 latch, s16 level, s16 script); +static void grid_init_fader(scene_state_t *ss, s16 group, s16 i, s16 x, s16 y, s16 w, s16 h, s16 type, s16 level, s16 script); +static s16 grid_fader_max_value(scene_state_t *ss, u16 i); +static s16 grid_fader_clamp_level(s16 level, s16 type, s16 w, s16 h); + +static void op_G_RST_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_CLR_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_ROTATE_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_DIM_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_KEY_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); + +static void op_G_GRP_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_GRP_set (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_GRP_EN_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_GRP_EN_set (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_GRP_RST_get(const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_GRP_SW_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_GRP_SC_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_GRP_SC_set (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_GRPI_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); + +static void op_G_LED_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_LED_set (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_LED_C_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_REC_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_RCT_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); + +static void op_G_BTN_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_BTX_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_GBT_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_GBX_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_BTN_EN_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_BTN_EN_set (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_BTN_V_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_BTN_V_set (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_BTN_L_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_BTN_L_set (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_BTN_X_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_BTN_X_set (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_BTN_Y_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_BTN_Y_set (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_BTNI_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_BTNV_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_BTNV_set (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_BTNL_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_BTNL_set (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_BTNX_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_BTNX_set (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_BTNY_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_BTNY_set (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_BTN_SW_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_BTN_PR_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_GBTN_V_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_GBTN_L_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_GBTN_C_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_GBTN_I_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_GBTN_W_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_GBTN_H_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_GBTN_X1_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_GBTN_X2_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_GBTN_Y1_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_GBTN_Y2_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); + +static void op_G_FDR_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_FDX_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_GFD_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_GFX_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_FDR_EN_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_FDR_EN_set (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_FDR_V_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_FDR_V_set (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_FDR_N_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_FDR_N_set (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_FDR_L_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_FDR_L_set (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_FDR_X_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_FDR_X_set (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_FDR_Y_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_FDR_Y_set (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_FDRI_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_FDRV_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_FDRV_set (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_FDRN_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_FDRN_set (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_FDRL_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_FDRL_set (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_FDRX_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_FDRX_set (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_FDRY_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_FDRY_set (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_FDR_PR_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_GFDR_V_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_GFDR_N_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_GFDR_L_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_GFDR_RN_get(const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); + +static void op_G_XYP_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_XYP_X_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_G_XYP_Y_get (const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); + +const tele_op_t op_G_RST = MAKE_GET_OP(G.RST, op_G_RST_get, 0, false); +const tele_op_t op_G_ROTATE = MAKE_GET_OP(G.ROTATE, op_G_ROTATE_get, 1, false); +const tele_op_t op_G_DIM = MAKE_GET_OP(G.DIM, op_G_DIM_get, 1, false); +const tele_op_t op_G_CLR = MAKE_GET_OP(G.CLR, op_G_CLR_get, 0, false); +const tele_op_t op_G_KEY = MAKE_GET_OP(G.KEY, op_G_KEY_get, 3, false); + +const tele_op_t op_G_GRP = MAKE_GET_SET_OP(G.GRP, op_G_GRP_get, op_G_GRP_set, 0, true); +const tele_op_t op_G_GRP_EN = MAKE_GET_SET_OP(G.GRP.EN, op_G_GRP_EN_get, op_G_GRP_EN_set, 1, true); +const tele_op_t op_G_GRP_RST = MAKE_GET_OP(G.GRP.RST, op_G_GRP_RST_get, 1, false); +const tele_op_t op_G_GRP_SW = MAKE_GET_OP(G.GRP.SW, op_G_GRP_SW_get, 1, false); +const tele_op_t op_G_GRP_SC = MAKE_GET_SET_OP(G.GRP.SC, op_G_GRP_SC_get, op_G_GRP_SC_set, 1, true); +const tele_op_t op_G_GRPI = MAKE_GET_OP(G.GRPI, op_G_GRPI_get, 0, true); + +const tele_op_t op_G_LED = MAKE_GET_SET_OP(G.LED, op_G_LED_get, op_G_LED_set, 2, true); +const tele_op_t op_G_LED_C = MAKE_GET_OP(G.LED.C, op_G_LED_C_get, 2, false); +const tele_op_t op_G_REC = MAKE_GET_OP(G.REC, op_G_REC_get, 6, false); +const tele_op_t op_G_RCT = MAKE_GET_OP(G.RCT, op_G_RCT_get, 6, false); + +const tele_op_t op_G_BTN = MAKE_GET_OP(G.BTN, op_G_BTN_get, 8, false); +const tele_op_t op_G_BTX = MAKE_GET_OP(G.BTX, op_G_BTX_get, 10, false); +const tele_op_t op_G_GBT = MAKE_GET_OP(G.GBT, op_G_GBT_get, 9, false); +const tele_op_t op_G_GBX = MAKE_GET_OP(G.GBX, op_G_GBX_get, 11, false); +const tele_op_t op_G_BTN_EN = MAKE_GET_SET_OP(G.BTN.EN, op_G_BTN_EN_get, op_G_BTN_EN_set, 1, true); +const tele_op_t op_G_BTN_V = MAKE_GET_SET_OP(G.BTN.V, op_G_BTN_V_get, op_G_BTN_V_set, 1, true); +const tele_op_t op_G_BTN_L = MAKE_GET_SET_OP(G.BTN.L, op_G_BTN_L_get, op_G_BTN_L_set, 1, true); +const tele_op_t op_G_BTN_X = MAKE_GET_SET_OP(G.BTN.X, op_G_BTN_X_get, op_G_BTN_X_set, 1, true); +const tele_op_t op_G_BTN_Y = MAKE_GET_SET_OP(G.BTN.Y, op_G_BTN_Y_get, op_G_BTN_Y_set, 1, true); +const tele_op_t op_G_BTNI = MAKE_GET_OP(G.BTNI, op_G_BTNI_get, 0, true); +const tele_op_t op_G_BTNV = MAKE_GET_SET_OP(G.BTNV, op_G_BTNV_get, op_G_BTNV_set, 0, true); +const tele_op_t op_G_BTNL = MAKE_GET_SET_OP(G.BTNL, op_G_BTNL_get, op_G_BTNL_set, 0, true); +const tele_op_t op_G_BTNX = MAKE_GET_SET_OP(G.BTNX, op_G_BTNX_get, op_G_BTNX_set, 0, true); +const tele_op_t op_G_BTNY = MAKE_GET_SET_OP(G.BTNY, op_G_BTNY_get, op_G_BTNY_set, 0, true); +const tele_op_t op_G_BTN_SW = MAKE_GET_OP(G.BTN.SW, op_G_BTN_SW_get, 1, false); +const tele_op_t op_G_BTN_PR = MAKE_GET_OP(G.BTN.PR, op_G_BTN_PR_get, 2, false); +const tele_op_t op_G_GBTN_V = MAKE_GET_OP(G.GBTN.V, op_G_GBTN_V_get, 2, false); +const tele_op_t op_G_GBTN_L = MAKE_GET_OP(G.GBTN.L, op_G_GBTN_L_get, 3, false); +const tele_op_t op_G_GBTN_C = MAKE_GET_OP(G.GBTN.C, op_G_GBTN_C_get, 1, true); +const tele_op_t op_G_GBTN_I = MAKE_GET_OP(G.GBTN.I, op_G_GBTN_I_get, 2, true); +const tele_op_t op_G_GBTN_W = MAKE_GET_OP(G.GBTN.W, op_G_GBTN_W_get, 1, true); +const tele_op_t op_G_GBTN_H = MAKE_GET_OP(G.GBTN.H, op_G_GBTN_H_get, 1, true); +const tele_op_t op_G_GBTN_X1 = MAKE_GET_OP(G.GBTN.X1, op_G_GBTN_X1_get, 1, true); +const tele_op_t op_G_GBTN_X2 = MAKE_GET_OP(G.GBTN.X2, op_G_GBTN_X2_get, 1, true); +const tele_op_t op_G_GBTN_Y1 = MAKE_GET_OP(G.GBTN.Y1, op_G_GBTN_Y1_get, 1, true); +const tele_op_t op_G_GBTN_Y2 = MAKE_GET_OP(G.GBTN.Y2, op_G_GBTN_Y2_get, 1, true); + +const tele_op_t op_G_FDR = MAKE_GET_OP(G.FDR, op_G_FDR_get, 8, false); +const tele_op_t op_G_FDX = MAKE_GET_OP(G.FDX, op_G_FDX_get, 10, false); +const tele_op_t op_G_GFD = MAKE_GET_OP(G.GFD, op_G_GFD_get, 9, false); +const tele_op_t op_G_GFX = MAKE_GET_OP(G.GFX, op_G_GFX_get, 11, false); +const tele_op_t op_G_FDR_EN = MAKE_GET_SET_OP(G.FDR.EN, op_G_FDR_EN_get, op_G_FDR_EN_set, 1, true); +const tele_op_t op_G_FDR_V = MAKE_GET_SET_OP(G.FDR.V, op_G_FDR_V_get, op_G_FDR_V_set, 1, true); +const tele_op_t op_G_FDR_N = MAKE_GET_SET_OP(G.FDR.N, op_G_FDR_N_get, op_G_FDR_N_set, 1, true); +const tele_op_t op_G_FDR_L = MAKE_GET_SET_OP(G.FDR.L, op_G_FDR_L_get, op_G_FDR_L_set, 1, true); +const tele_op_t op_G_FDR_X = MAKE_GET_SET_OP(G.FDR.X, op_G_FDR_X_get, op_G_FDR_X_set, 1, true); +const tele_op_t op_G_FDR_Y = MAKE_GET_SET_OP(G.FDR.Y, op_G_FDR_Y_get, op_G_FDR_Y_set, 1, true); +const tele_op_t op_G_FDRI = MAKE_GET_OP(G.FDRI, op_G_FDRI_get, 0, true); +const tele_op_t op_G_FDRV = MAKE_GET_SET_OP(G.FDRV, op_G_FDRV_get, op_G_FDRV_set, 0, true); +const tele_op_t op_G_FDRN = MAKE_GET_SET_OP(G.FDRN, op_G_FDRN_get, op_G_FDRN_set, 0, true); +const tele_op_t op_G_FDRL = MAKE_GET_SET_OP(G.FDRL, op_G_FDRL_get, op_G_FDRL_set, 0, true); +const tele_op_t op_G_FDRX = MAKE_GET_SET_OP(G.FDRX, op_G_FDRX_get, op_G_FDRX_set, 0, true); +const tele_op_t op_G_FDRY = MAKE_GET_SET_OP(G.FDRY, op_G_FDRY_get, op_G_FDRY_set, 0, true); +const tele_op_t op_G_FDR_PR = MAKE_GET_OP(G.FDR.PR, op_G_FDR_PR_get, 2, false); +const tele_op_t op_G_GFDR_V = MAKE_GET_OP(G.GFDR.V, op_G_GFDR_V_get, 2, false); +const tele_op_t op_G_GFDR_N = MAKE_GET_OP(G.GFDR.N, op_G_GFDR_N_get, 2, false); +const tele_op_t op_G_GFDR_L = MAKE_GET_OP(G.GFDR.L, op_G_GFDR_L_get, 3, false); +const tele_op_t op_G_GFDR_RN = MAKE_GET_OP(G.GFDR.RN, op_G_GFDR_RN_get, 3, false); + +const tele_op_t op_G_XYP = MAKE_GET_OP(G.XYP, op_G_XYP_get, 7, false); +const tele_op_t op_G_XYP_X = MAKE_GET_OP(G.XYP.X, op_G_XYP_X_get, 1, true); +const tele_op_t op_G_XYP_Y = MAKE_GET_OP(G.XYP.Y, op_G_XYP_Y_get, 1, true); + +// clang-format on + +static void op_G_RST_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), + command_state_t *NOTUSED(cs)) { + SG.rotate = 0; + SG.dim = 0; + + SG.current_group = 0; + SG.latest_group = 0; + SG.latest_button = 0; + SG.latest_fader = 0; + + for (u8 i = 0; i < GRID_MAX_DIMENSION; i++) + for (u8 j = 0; j < GRID_MAX_DIMENSION; j++) SG.leds[i][j] = LED_OFF; + + for (u8 i = 0; i < GRID_GROUP_COUNT; i++) { + SG.group[i].enabled = true; + SG.group[i].script = -1; + SG.group[i].fader_min = 0; + SG.group[i].fader_max = 16383; + } + + for (u16 i = 0; i < GRID_BUTTON_COUNT; i++) { + grid_common_init(&(GBC)); + GB.latch = 0; + GB.state = 0; + } + + for (u8 i = 0; i < GRID_FADER_COUNT; i++) { + grid_common_init(&(GFC)); + GF.type = FADER_CH_BAR; + GF.value = 0; + GF.slide = 0; + } + + for (u8 i = 0; i < GRID_XYPAD_COUNT; i++) { + grid_common_init(&(GXYC)); + GXY.value_x = 0; + GXY.value_y = 0; + } + + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_CLR_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), + command_state_t *NOTUSED(cs)) { + for (u8 i = 0; i < GRID_MAX_DIMENSION; i++) + for (u8 j = 0; j < GRID_MAX_DIMENSION; j++) SG.leds[i][j] = LED_OFF; + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_ROTATE_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 rotate = cs_pop(cs); + SG.rotate = rotate != 0; + SG.scr_dirty = SG.grid_dirty = SG.clear_held = 1; +} + +static void op_G_DIM_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + GET_AND_CLAMP(dim, 0, 14); + SG.dim = dim; + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_KEY_get(const void *data, scene_state_t *ss, exec_state_t *es, + command_state_t *cs) { + s16 x = cs_pop(cs); + s16 y = cs_pop(cs); + s16 action = cs_pop(cs); + if (x < 0 || y < 0 || x >= GRID_MAX_DIMENSION || y >= GRID_MAX_DIMENSION) + return; + grid_key_press(x, y, action != 0); +} + +static void op_G_GRP_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + cs_push(cs, SG.current_group); +} + +static void op_G_GRP_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 group = cs_pop(cs); + if (group < (s16)0 || group >= (s16)GRID_GROUP_COUNT) return; + SG.current_group = group; + SG.scr_dirty = 1; +} + +static void op_G_GRP_EN_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 group = cs_pop(cs); + cs_push(cs, group < (s16)0 || group >= (s16)GRID_GROUP_COUNT + ? 0 + : SG.group[group].enabled); +} + +static void op_G_GRP_EN_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 group = cs_pop(cs); + s16 en = cs_pop(cs); + if (group < (s16)0 || group >= (s16)GRID_GROUP_COUNT) return; + SG.group[group].enabled = en != 0; + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_GRP_RST_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 group = cs_pop(cs); + if (group < (s16)0 || group >= (s16)GRID_GROUP_COUNT) return; + + SG.group[group].enabled = true; + SG.group[group].script = -1; + SG.group[group].fader_min = 0; + SG.group[group].fader_max = 16383; + + for (u16 i = 0; i < GRID_BUTTON_COUNT; i++) + if (GBC.group == group) { + grid_common_init(&(GBC)); + GB.latch = 0; + GB.state = 0; + } + + for (u8 i = 0; i < GRID_FADER_COUNT; i++) + if (GFC.group == group) { + grid_common_init(&(GFC)); + GF.type = FADER_CH_BAR; + GF.value = 0; + GF.slide = 0; + } + + for (u8 i = 0; i < GRID_XYPAD_COUNT; i++) + if (GXYC.group == group) { + grid_common_init(&(GXYC)); + GXY.value_x = 0; + GXY.value_y = 0; + } + + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_GRP_SW_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 group = cs_pop(cs); + if (group < (s16)0 || group >= (s16)GRID_GROUP_COUNT) return; + + for (u8 i = 0; i < GRID_GROUP_COUNT; i++) SG.group[i].enabled = false; + SG.group[group].enabled = true; + + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_GRP_SC_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 group = cs_pop(cs); + cs_push(cs, group < (s16)0 || group >= (s16)GRID_GROUP_COUNT + ? -1 + : SG.group[group].script + 1); +} + +static void op_G_GRP_SC_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 group = cs_pop(cs); + s16 script = cs_pop(cs) - 1; + + if (group < (s16)0 || group >= (s16)GRID_GROUP_COUNT) return; + if (script < 0 || script > INIT_SCRIPT) script = -1; + + SG.group[group].script = script; +} + +static void op_G_GRPI_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + cs_push(cs, SG.latest_group); +} + +static void op_G_LED_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 x = cs_pop(cs); + s16 y = cs_pop(cs); + + if (x < (s16)0 || x >= (s16)GRID_MAX_DIMENSION) + cs_push(cs, LED_OFF); + else if (y < (s16)0 || y >= (s16)GRID_MAX_DIMENSION) + cs_push(cs, LED_OFF); + else + cs_push(cs, SG.leds[x][y]); +} + +static void op_G_LED_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 x = cs_pop(cs); + s16 y = cs_pop(cs); + GET_LEVEL(level); + + if (x < (s16)0 || x >= (s16)GRID_MAX_DIMENSION) return; + if (y < (s16)0 || y >= (s16)GRID_MAX_DIMENSION) return; + + SG.leds[x][y] = level; + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_LED_C_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 x = cs_pop(cs); + s16 y = cs_pop(cs); + + if (x < (s16)0 || x >= (s16)GRID_MAX_DIMENSION) return; + if (y < (s16)0 || y >= (s16)GRID_MAX_DIMENSION) return; + + SG.leds[x][y] = LED_OFF; + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_REC_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 x = cs_pop(cs); + s16 y = cs_pop(cs); + s16 w = cs_pop(cs); + s16 h = cs_pop(cs); + GET_LEVEL(fill); + GET_LEVEL(border); + grid_rectangle(ss, x, y, w, h, fill, border); +} + +static void op_G_RCT_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 x1 = cs_pop(cs); + s16 y1 = cs_pop(cs); + s16 x2 = cs_pop(cs); + s16 y2 = cs_pop(cs); + GET_LEVEL(fill); + GET_LEVEL(border); + grid_rectangle(ss, x1, y1, x2 - x1 + 1, y2 - y1 + 1, fill, border); +} + +static void op_G_BTN_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs); + s16 x = cs_pop(cs); + s16 y = cs_pop(cs); + s16 w = cs_pop(cs); + s16 h = cs_pop(cs); + s16 latch = cs_pop(cs); + s16 level = cs_pop(cs); + s16 script = cs_pop(cs) - 1; + grid_init_button(ss, SG.current_group, i, x, y, w, h, latch, level, script); + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_GBT_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 group = cs_pop(cs); + s16 i = cs_pop(cs); + s16 x = cs_pop(cs); + s16 y = cs_pop(cs); + s16 w = cs_pop(cs); + s16 h = cs_pop(cs); + s16 latch = cs_pop(cs); + s16 level = cs_pop(cs); + s16 script = cs_pop(cs) - 1; + grid_init_button(ss, group, i, x, y, w, h, latch, level, script); + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_BTX_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 id = cs_pop(cs); + s16 _x = cs_pop(cs); + s16 _y = cs_pop(cs); + s16 _w = cs_pop(cs); + s16 _h = cs_pop(cs); + s16 latch = cs_pop(cs); + s16 level = cs_pop(cs); + s16 script = cs_pop(cs) - 1; + s16 count_x = cs_pop(cs); + s16 count_y = cs_pop(cs); + + if (count_x <= (s16)0) return; + if (count_x > (s16)GRID_MAX_DIMENSION) count_x = GRID_MAX_DIMENSION; + if (count_y <= (s16)0) return; + if (count_y > (s16)GRID_MAX_DIMENSION) count_y = GRID_MAX_DIMENSION; + + u16 i; + s16 x, y, w, h; + for (s16 cy = 0; cy < count_y; cy++) + for (s16 cx = 0; cx < count_x; cx++) { + i = id + cy * count_x + cx; + x = _x + _w * cx; + w = _w; + y = _y + _h * cy; + h = _h; + grid_init_button(ss, SG.current_group, i, x, y, w, h, latch, level, + script); + } + + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_GBX_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 group = cs_pop(cs); + s16 id = cs_pop(cs); + s16 _x = cs_pop(cs); + s16 _y = cs_pop(cs); + s16 _w = cs_pop(cs); + s16 _h = cs_pop(cs); + s16 latch = cs_pop(cs); + s16 level = cs_pop(cs); + s16 script = cs_pop(cs) - 1; + s16 count_x = cs_pop(cs); + s16 count_y = cs_pop(cs); + + if (count_x <= (s16)0) return; + if (count_x > (s16)GRID_MAX_DIMENSION) count_x = GRID_MAX_DIMENSION; + if (count_y <= (s16)0) return; + if (count_y > (s16)GRID_MAX_DIMENSION) count_y = GRID_MAX_DIMENSION; + + u16 i; + s16 x, y, w, h; + for (s16 cy = 0; cy < count_y; cy++) + for (s16 cx = 0; cx < count_x; cx++) { + i = id + cy * count_x + cx; + x = _x + _w * cx; + w = _w; + y = _y + _h * cy; + h = _h; + grid_init_button(ss, group, i, x, y, w, h, latch, level, script); + } + + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_BTN_EN_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs); + cs_push(cs, i < (s16)0 || i >= (s16)GRID_BUTTON_COUNT ? 0 : GBC.enabled); +} + +static void op_G_BTN_EN_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs); + s16 en = cs_pop(cs); + + if (i < (s16)0 || i >= (s16)GRID_BUTTON_COUNT) return; + GBC.enabled = en; + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_BTN_V_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs); + cs_push(cs, i < (s16)0 || i >= (s16)GRID_BUTTON_COUNT ? 0 : GB.state); +} + +static void op_G_BTN_V_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs); + s16 value = cs_pop(cs); + if (i < (s16)0 || i >= (s16)GRID_BUTTON_COUNT) return; + GB.state = value != 0; + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_BTN_L_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs); + cs_push(cs, i < (s16)0 || i >= (s16)GRID_BUTTON_COUNT ? 0 : GBC.level); +} + +static void op_G_BTN_L_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs); + GET_LEVEL(level); + if (i < (s16)0 || i >= (s16)GRID_BUTTON_COUNT) return; + GBC.level = level; + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_BTN_X_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs); + cs_push(cs, i < (s16)0 || i >= (s16)GRID_BUTTON_COUNT ? 0 : GBC.x); +} + +static void op_G_BTN_X_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs); + s16 x = cs_pop(cs); + + if (i < (s16)0 || i >= (s16)GRID_BUTTON_COUNT) return; + s16 y = GBC.y; + s16 w = GBC.w; + s16 h = GBC.h; + CLAMP_X_Y_W_H(return ); + + GBC.x = x; + GBC.y = y; + GBC.w = w; + GBC.h = h; + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_BTN_Y_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs); + cs_push(cs, i < (s16)0 || i >= (s16)GRID_BUTTON_COUNT ? 0 : GBC.y); +} + +static void op_G_BTN_Y_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs); + s16 y = cs_pop(cs); + + if (i < (s16)0 || i >= (s16)GRID_BUTTON_COUNT) return; + s16 x = GBC.x; + s16 w = GBC.w; + s16 h = GBC.h; + CLAMP_X_Y_W_H(return ); + + GBC.x = x; + GBC.y = y; + GBC.w = w; + GBC.h = h; + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_BTNI_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + cs_push(cs, SG.latest_button); +} + +static void op_G_BTNV_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + cs_push(cs, SG.button[SG.latest_button].state); +} + +static void op_G_BTNV_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 value = cs_pop(cs); + SG.button[SG.latest_button].state = value != 0; + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_BTNL_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + cs_push(cs, SG.button[SG.latest_button].common.level); +} + +static void op_G_BTNL_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + GET_LEVEL(level); + SG.button[SG.latest_button].common.level = level; + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_BTNX_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + cs_push(cs, SG.button[SG.latest_button].common.x); +} + +static void op_G_BTNX_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 x = cs_pop(cs); + u16 i = SG.latest_button; + + s16 y = GBC.y; + s16 w = GBC.w; + s16 h = GBC.h; + CLAMP_X_Y_W_H(return ); + + GBC.x = x; + GBC.y = y; + GBC.w = w; + GBC.h = h; + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_BTNY_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + cs_push(cs, SG.button[SG.latest_button].common.y); +} + +static void op_G_BTNY_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 y = cs_pop(cs); + u16 i = SG.latest_button; + + s16 x = GBC.x; + s16 w = GBC.w; + s16 h = GBC.h; + CLAMP_X_Y_W_H(return ); + + GBC.x = x; + GBC.y = y; + GBC.w = w; + GBC.h = h; + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_BTN_SW_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 id = cs_pop(cs); + if (id < (s16)0 || id >= (s16)GRID_BUTTON_COUNT) return; + + for (u16 i = 0; i < GRID_BUTTON_COUNT; i++) + if (GBC.group == SG.button[id].common.group) GB.state = 0; + + SG.button[id].state = 1; + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_BTN_PR_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *es, command_state_t *cs) { + s16 i = cs_pop(cs); + s16 action = cs_pop(cs); + + if (i < (s16)0 || i >= (s16)GRID_BUTTON_COUNT) return; + + GB.state = GB.latch ? !GB.state : action != 0; + SG.latest_button = i; + SG.latest_group = GBC.group; + + if (GBC.script != -1) { + es_push(es); + if (!es->overflow) run_script_with_exec_state(ss, es, GBC.script); + es_pop(es); + } + + if (SG.group[GBC.group].script != -1) { + es_push(es); + if (!es->overflow) + run_script_with_exec_state(ss, es, SG.group[GBC.group].script); + es_pop(es); + } + + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_GBTN_V_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 group = cs_pop(cs); + s16 value = cs_pop(cs); + + if (group < (s16)0 || group > (s16)GRID_GROUP_COUNT) return; + value = value != 0; + + for (u16 i = 0; i < GRID_BUTTON_COUNT; i++) + if (GBC.group == group) GB.state = value; + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_GBTN_L_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 group = cs_pop(cs); + GET_LEVEL(odd); + GET_LEVEL(even); + + if (group < (s16)0 || group > (s16)GRID_GROUP_COUNT) return; + + u8 is_odd = 0; + for (u16 i = 0; i < GRID_BUTTON_COUNT; i++) + if (GBC.group == group) { + GBC.level = is_odd ? odd : even; + is_odd = !is_odd; + } + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_GBTN_C_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 group = cs_pop(cs); + if (group < (s16)0 || group > (s16)GRID_GROUP_COUNT) { + cs_push(cs, 0); + return; + } + + s16 count = 0; + for (u16 i = 0; i < GRID_BUTTON_COUNT; i++) + if (GBC.group == group && GB.state) count++; + cs_push(cs, count); +} + +static void op_G_GBTN_I_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 group = cs_pop(cs); + s16 index = cs_pop(cs); + if (group < (s16)0 || group > (s16)GRID_GROUP_COUNT) { + cs_push(cs, -1); + return; + } + + s16 id = -1; + s16 count = -1; + for (u16 i = 0; i < GRID_BUTTON_COUNT; i++) { + if (GBC.group == group && GB.state) count++; + if (count == index) { + id = i; + break; + } + } + + cs_push(cs, id); +} + +static void op_G_GBTN_W_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 group = cs_pop(cs); + if (group < (s16)0 || group > (s16)GRID_GROUP_COUNT) { + cs_push(cs, 0); + return; + } + + s16 x1 = 32767, x2 = -32768; + u8 atleastone = 0; + for (u16 i = 0; i < GRID_BUTTON_COUNT; i++) { + if (GBC.group == group && GB.state) { + if (GBC.x < x1) x1 = GBC.x; + if (GBC.x > x2) x2 = GBC.x; + atleastone = 1; + } + } + + cs_push(cs, atleastone ? x2 - x1 + 1 : 0); +} + +static void op_G_GBTN_H_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 group = cs_pop(cs); + if (group < (s16)0 || group > (s16)GRID_GROUP_COUNT) { + cs_push(cs, 0); + return; + } + + s16 y1 = 32767, y2 = -32768; + u8 atleastone = 0; + for (u16 i = 0; i < GRID_BUTTON_COUNT; i++) { + if (GBC.group == group && GB.state) { + if (GBC.y < y1) y1 = GBC.y; + if (GBC.y > y2) y2 = GBC.y; + atleastone = 1; + } + } + + cs_push(cs, atleastone ? y2 - y1 + 1 : 0); +} + +static void op_G_GBTN_X1_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 group = cs_pop(cs); + if (group < (s16)0 || group > (s16)GRID_GROUP_COUNT) { + cs_push(cs, -1); + return; + } + + s16 x1 = 32767; + u8 atleastone = 0; + for (u16 i = 0; i < GRID_BUTTON_COUNT; i++) { + if (GBC.group == group && GB.state) { + if (GBC.x < x1) x1 = GBC.x; + atleastone = 1; + } + } + + cs_push(cs, atleastone ? x1 : -1); +} + +static void op_G_GBTN_X2_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 group = cs_pop(cs); + if (group < (s16)0 || group > (s16)GRID_GROUP_COUNT) { + cs_push(cs, -1); + return; + } + + s16 x2 = -32768; + u8 atleastone = 0; + for (u16 i = 0; i < GRID_BUTTON_COUNT; i++) { + if (GBC.group == group && GB.state) { + if (GBC.x > x2) x2 = GBC.x; + atleastone = 1; + } + } + + cs_push(cs, atleastone ? x2 : -1); +} + +static void op_G_GBTN_Y1_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 group = cs_pop(cs); + if (group < (s16)0 || group > (s16)GRID_GROUP_COUNT) { + cs_push(cs, -1); + return; + } + + s16 y1 = 32767; + u8 atleastone = 0; + for (u16 i = 0; i < GRID_BUTTON_COUNT; i++) { + if (GBC.group == group && GB.state) { + if (GBC.y < y1) y1 = GBC.y; + atleastone = 1; + } + } + + cs_push(cs, atleastone ? y1 : -1); +} + +static void op_G_GBTN_Y2_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 group = cs_pop(cs); + if (group < (s16)0 || group > (s16)GRID_GROUP_COUNT) { + cs_push(cs, -1); + return; + } + + s16 y2 = -32768; + u8 atleastone = 0; + for (u16 i = 0; i < GRID_BUTTON_COUNT; i++) { + if (GBC.group == group && GB.state) { + if (GBC.y > y2) y2 = GBC.y; + atleastone = 1; + } + } + + cs_push(cs, atleastone ? y2 : -1); +} + +static void op_G_FDR_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs); + s16 x = cs_pop(cs); + s16 y = cs_pop(cs); + s16 w = cs_pop(cs); + s16 h = cs_pop(cs); + s16 type = cs_pop(cs); + s16 level = cs_pop(cs); + s16 script = cs_pop(cs) - 1; + grid_init_fader(ss, SG.current_group, i, x, y, w, h, type, level, script); + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_GFD_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 group = cs_pop(cs); + s16 i = cs_pop(cs); + s16 x = cs_pop(cs); + s16 y = cs_pop(cs); + s16 w = cs_pop(cs); + s16 h = cs_pop(cs); + s16 type = cs_pop(cs); + s16 level = cs_pop(cs); + s16 script = cs_pop(cs) - 1; + grid_init_fader(ss, group, i, x, y, w, h, type, level, script); + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_FDX_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 id = cs_pop(cs); + s16 x = cs_pop(cs); + s16 y = cs_pop(cs); + s16 w = cs_pop(cs); + s16 h = cs_pop(cs); + s16 type = cs_pop(cs); + s16 level = cs_pop(cs); + s16 script = cs_pop(cs) - 1; + s16 count_x = cs_pop(cs); + s16 count_y = cs_pop(cs); + + if (count_x <= (s16)0) return; + if (count_x > (s16)GRID_MAX_DIMENSION) count_x = GRID_MAX_DIMENSION; + if (count_y <= (s16)0) return; + if (count_y > (s16)GRID_MAX_DIMENSION) count_y = GRID_MAX_DIMENSION; + + for (u16 cy = 0; cy < count_y; cy++) + for (u16 cx = 0; cx < count_x; cx++) + grid_init_fader(ss, SG.current_group, id + cy * count_x + cx, + x + w * cx, y + h * cy, w, h, type, level, script); + + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_GFX_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 group = cs_pop(cs); + s16 id = cs_pop(cs); + s16 x = cs_pop(cs); + s16 y = cs_pop(cs); + s16 w = cs_pop(cs); + s16 h = cs_pop(cs); + s16 type = cs_pop(cs); + s16 level = cs_pop(cs); + s16 script = cs_pop(cs) - 1; + s16 count_x = cs_pop(cs); + s16 count_y = cs_pop(cs); + + if (count_x <= (s16)0) return; + if (count_x > (s16)GRID_MAX_DIMENSION) count_x = GRID_MAX_DIMENSION; + if (count_y <= (s16)0) return; + if (count_y > (s16)GRID_MAX_DIMENSION) count_y = GRID_MAX_DIMENSION; + + for (u16 cy = 0; cy < count_y; cy++) + for (u16 cx = 0; cx < count_x; cx++) + grid_init_fader(ss, group, id + cy * count_x + cx, x + w * cx, + y + h * cy, w, h, type, level, script); + + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_FDR_EN_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs); + cs_push(cs, i < (s16)0 || i >= (s16)GRID_FADER_COUNT ? 0 : GFC.enabled); +} + +static void op_G_FDR_EN_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs); + s16 en = cs_pop(cs); + + if (i < (s16)0 || i >= (s16)GRID_FADER_COUNT) return; + GFC.enabled = en; + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_FDR_V_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs); + if (i < (s16)0 || i >= (s16)GRID_FADER_COUNT) { + cs_push(cs, 0); + return; + } + + s16 value = + scale(0, grid_fader_max_value(ss, i), SG.group[GFC.group].fader_min, + SG.group[GFC.group].fader_max, GF.value); + cs_push(cs, value); +} + +static void op_G_FDR_V_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs); + s16 value = cs_pop(cs); + + if (i < (s16)0 || i >= (s16)GRID_FADER_COUNT) return; + if (value < SG.group[GFC.group].fader_min) + value = SG.group[GFC.group].fader_min; + else if (value > SG.group[GFC.group].fader_max) + value = SG.group[GFC.group].fader_max; + + GF.value = + scale(SG.group[GFC.group].fader_min, SG.group[GFC.group].fader_max, 0, + grid_fader_max_value(ss, i), value); + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_FDR_N_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs); + cs_push(cs, i < (s16)0 || i >= (s16)GRID_FADER_COUNT ? 0 : GF.value); +} + +static void op_G_FDR_N_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs); + s16 value = cs_pop(cs); + + if (i < (s16)0 || i >= (s16)GRID_FADER_COUNT) return; + + s16 maxvalue = grid_fader_max_value(ss, i); + if (value < (s16)0) + value = 0; + else if (value > maxvalue) + value = maxvalue; + + GF.value = value; + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_FDR_L_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs); + cs_push(cs, i < (s16)0 || i >= (s16)GRID_FADER_COUNT ? 0 : GFC.level); +} + +static void op_G_FDR_L_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs); + s16 level = cs_pop(cs); + if (i < (s16)0 || i >= (s16)GRID_FADER_COUNT) return; + + level = grid_fader_clamp_level(level, GF.type, GFC.w, GFC.h); + if (GF.type > FADER_COARSE) + GF.value = scale(0, GFC.level, 0, level, GF.value); + GFC.level = level; + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_FDR_X_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs); + cs_push(cs, i < (s16)0 || i >= (s16)GRID_FADER_COUNT ? 0 : GFC.x); +} + +static void op_G_FDR_X_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs); + s16 x = cs_pop(cs); + + if (i < (s16)0 || i >= (s16)GRID_FADER_COUNT) return; + s16 y = GFC.y; + s16 w = GFC.w; + s16 h = GFC.h; + CLAMP_X_Y_W_H(return ); + + GFC.x = x; + GFC.y = y; + GFC.w = w; + GFC.h = h; + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_FDR_Y_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs); + cs_push(cs, i < (s16)0 || i >= (s16)GRID_FADER_COUNT ? 0 : GFC.y); +} + +static void op_G_FDR_Y_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs); + s16 y = cs_pop(cs); + + if (i < (s16)0 || i >= (s16)GRID_FADER_COUNT) return; + s16 x = GFC.x; + s16 w = GFC.w; + s16 h = GFC.h; + CLAMP_X_Y_W_H(return ); + + GFC.x = x; + GFC.y = y; + GFC.w = w; + GFC.h = h; + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_FDRI_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + cs_push(cs, SG.latest_fader); +} + +static void op_G_FDRV_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + u8 i = SG.latest_fader; + s16 value = + scale(0, grid_fader_max_value(ss, i), SG.group[GFC.group].fader_min, + SG.group[GFC.group].fader_max, GF.value); + cs_push(cs, value); +} + +static void op_G_FDRV_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 value = cs_pop(cs); + s16 i = SG.latest_fader; + + if (value < SG.group[GFC.group].fader_min) + value = SG.group[GFC.group].fader_min; + else if (value > SG.group[GFC.group].fader_max) + value = SG.group[GFC.group].fader_max; + + GF.value = + scale(SG.group[GFC.group].fader_min, SG.group[GFC.group].fader_max, 0, + grid_fader_max_value(ss, i), value); + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_FDRN_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + cs_push(cs, SG.fader[SG.latest_fader].value); +} + +static void op_G_FDRN_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 value = cs_pop(cs); + s16 i = SG.latest_fader; + + s16 maxvalue = grid_fader_max_value(ss, i); + if (value < (s16)0) + value = 0; + else if (value > maxvalue) + value = maxvalue; + + GF.value = value; + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_FDRL_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + cs_push(cs, SG.fader[SG.latest_fader].common.level); +} + +static void op_G_FDRL_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 level = cs_pop(cs); + u16 i = SG.latest_fader; + + level = grid_fader_clamp_level(level, GF.type, GFC.w, GFC.h); + if (GF.type > FADER_COARSE) + GF.value = scale(0, GFC.level, 0, level, GF.value); + GFC.level = level; + + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_FDRX_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + cs_push(cs, SG.fader[SG.latest_fader].common.x); +} + +static void op_G_FDRX_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 x = cs_pop(cs); + s16 i = SG.latest_fader; + + s16 y = GFC.y; + s16 w = GFC.w; + s16 h = GFC.h; + CLAMP_X_Y_W_H(return ); + + GFC.x = x; + GFC.y = y; + GFC.w = w; + GFC.h = h; + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_FDRY_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + cs_push(cs, SG.fader[SG.latest_fader].common.y); +} + +static void op_G_FDRY_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 y = cs_pop(cs); + s16 i = SG.latest_fader; + + s16 x = GFC.x; + s16 w = GFC.w; + s16 h = GFC.h; + CLAMP_X_Y_W_H(return ); + + GFC.x = x; + GFC.y = y; + GFC.w = w; + GFC.h = h; + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_FDR_PR_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *es, command_state_t *cs) { + s16 i = cs_pop(cs); + s16 value = cs_pop(cs) - 1; + + if (i < (s16)0 || i >= (s16)GRID_FADER_COUNT) return; + + s16 maxvalue = grid_fader_max_value(ss, i); + if (value < (s16)0) + value = 0; + else if (value > maxvalue) + value = maxvalue; + + GF.value = value; + SG.latest_fader = i; + SG.latest_group = GFC.group; + + if (GFC.script != -1) { + es_push(es); + if (!es->overflow) run_script_with_exec_state(ss, es, GFC.script); + es_pop(es); + } + + if (SG.group[GFC.group].script != -1) { + es_push(es); + if (!es->overflow) + run_script_with_exec_state(ss, es, SG.group[GFC.group].script); + es_pop(es); + } + + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_GFDR_V_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 group = cs_pop(cs); + s16 value = cs_pop(cs); + + if (group < (s16)0 || group > (s16)GRID_GROUP_COUNT) return; + if (value < SG.group[group].fader_min) + value = SG.group[group].fader_min; + else if (value > SG.group[group].fader_max) + value = SG.group[group].fader_max; + + for (u16 i = 0; i < GRID_FADER_COUNT; i++) + if (GFC.group == group) + GF.value = + scale(SG.group[group].fader_min, SG.group[group].fader_max, 0, + grid_fader_max_value(ss, i), value); + + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_GFDR_N_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 group = cs_pop(cs); + s16 value = cs_pop(cs); + + if (group < (s16)0 || group > (s16)GRID_GROUP_COUNT) return; + if (value < (s16)0) value = 0; + + for (u16 i = 0; i < GRID_FADER_COUNT; i++) + if (GFC.group == group) + GF.value = min(grid_fader_max_value(ss, i), value); + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_GFDR_L_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 group = cs_pop(cs); + s16 odd = cs_pop(cs); + s16 even = cs_pop(cs); + + if (group < (s16)0 || group > (s16)GRID_GROUP_COUNT) return; + + u8 is_odd = 0; + s16 level; + for (u16 i = 0; i < GRID_FADER_COUNT; i++) + if (GFC.group == group) { + level = grid_fader_clamp_level(is_odd ? odd : even, GF.type, GFC.w, + GFC.h); + if (GF.type > FADER_COARSE) + GF.value = scale(0, GFC.level, 0, level, GF.value); + GFC.level = level; + is_odd = !is_odd; + } + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_GFDR_RN_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 group = cs_pop(cs); + s16 min = cs_pop(cs); + s16 max = cs_pop(cs); + + if (group < (s16)0 || group > (s16)GRID_GROUP_COUNT) return; + SG.group[group].fader_min = min; + SG.group[group].fader_max = max; +} + +static void op_G_XYP_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs); + s16 x = cs_pop(cs); + s16 y = cs_pop(cs); + s16 w = cs_pop(cs); + s16 h = cs_pop(cs); + GET_LEVEL(level); + s16 script = cs_pop(cs) - 1; + + if (i < (s16)0 || i >= (s16)GRID_XYPAD_COUNT) return; + if (script < 0 || script > INIT_SCRIPT) script = -1; + CLAMP_X_Y_W_H(return ); + + GXYC.enabled = true; + GXYC.group = SG.current_group; + GXYC.x = x; + GXYC.y = y; + GXYC.w = w; + GXYC.h = h; + GXYC.level = level; + GXYC.script = script; + GXY.value_x = 0; + GXY.value_y = 0; + + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void op_G_XYP_X_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs); + cs_push(cs, i < (s16)0 || i >= (s16)GRID_XYPAD_COUNT ? 0 : GXY.value_x); +} + +static void op_G_XYP_Y_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs); + cs_push(cs, i < (s16)0 || i >= (s16)GRID_XYPAD_COUNT ? 0 : GXY.value_y); +} + +void grid_common_init(grid_common_t *gc) { + gc->enabled = false; + gc->group = 0; + gc->x = gc->y = 0; + gc->w = gc->h = 1; + gc->level = 5; + gc->script = -1; +} + +// helpers + +s32 scale(s32 a, s32 b, s32 x, s32 y, s32 value) { + if (a == b) return x; + s32 result = (value - a) * (y - x) * 2 / (b - a); + result = (result / 2) + (result & 1); // rounding + return result + x; +} + +void grid_rectangle(scene_state_t *ss, s16 x, s16 y, s16 w, s16 h, u8 fill, + u8 border) { + for (u16 col = max(0, x + (s32)1); + col < min(GRID_MAX_DIMENSION, x + w - (s32)1); col++) + for (u16 row = max(0, y + (s32)1); + row < min(GRID_MAX_DIMENSION, y + h - (s32)1); row++) + SG.leds[col][row] = fill; + + if (y >= (s16)0 && y < (s16)GRID_MAX_DIMENSION) + for (u16 col = max(0, x); col < min(GRID_MAX_DIMENSION, (s32)x + w); + col++) + SG.leds[col][y] = border; + + s16 row = y + h - (s16)1; + if (row >= (s16)0 && row < (s16)GRID_MAX_DIMENSION) + for (u16 col = max(0, x); col < min(GRID_MAX_DIMENSION, (s32)x + w); + col++) + SG.leds[col][row] = border; + + if (x >= (s16)0 && x < (s16)GRID_MAX_DIMENSION) + for (u16 row = max(0, y); row < min(GRID_MAX_DIMENSION, (s32)y + h); + row++) + SG.leds[x][row] = border; + + s16 col = x + w - 1; + if (col >= (s16)0 && col < (s16)GRID_MAX_DIMENSION) + for (u16 row = max(0, y); row < min(GRID_MAX_DIMENSION, (s32)y + h); + row++) + SG.leds[col][row] = border; + + SG.scr_dirty = SG.grid_dirty = 1; +} + +static void grid_init_button(scene_state_t *ss, s16 group, s16 i, s16 x, s16 y, + s16 w, s16 h, s16 latch, s16 level, s16 script) { + if (group < (s16)0 || group >= (s16)GRID_GROUP_COUNT) return; + if (i < (s16)0 || i >= (s16)GRID_BUTTON_COUNT) return; + + if (x >= (s16)GRID_MAX_DIMENSION || y >= (s16)GRID_MAX_DIMENSION) return; + if (x < (s16)0) { + w += x; + x = 0; + } + if (w + x > (s16)GRID_MAX_DIMENSION) w = GRID_MAX_DIMENSION - x; + if (y < (s16)0) { + h += y; + y = 0; + } + if (h + y > (s16)GRID_MAX_DIMENSION) h = GRID_MAX_DIMENSION - y; + if (w == 0 || h == 0) return; + + if (level < (s16)LED_OFF) + level = LED_OFF; + else if (level > (s16)15) + level = 15; + + if (script < 0 || script > INIT_SCRIPT) script = -1; + + GBC.enabled = true; + GBC.group = group; + GBC.x = x; + GBC.y = y; + GBC.w = w; + + GBC.h = h; + GBC.level = level; + GBC.script = script; + GB.latch = latch != 0; + if (!GB.latch) GB.state = 0; +} + +static void grid_init_fader(scene_state_t *ss, s16 group, s16 i, s16 x, s16 y, + s16 w, s16 h, s16 type, s16 level, s16 script) { + if (group < (s16)0 || group >= (s16)GRID_GROUP_COUNT) return; + if (i < (s16)0 || i >= (s16)GRID_FADER_COUNT) return; + + if (x >= (s16)GRID_MAX_DIMENSION || y >= (s16)GRID_MAX_DIMENSION) return; + if (x < (s16)0) { + w += x; + x = 0; + } + if (w + x > (s16)GRID_MAX_DIMENSION) w = GRID_MAX_DIMENSION - x; + if (y < (s16)0) { + h += y; + y = 0; + } + if (h + y > (s16)GRID_MAX_DIMENSION) h = GRID_MAX_DIMENSION - y; + if (w == 0 || h == 0) return; + + if (type < FADER_CH_BAR || type > FADER_FV_DOT) type = FADER_CH_BAR; + + level = grid_fader_clamp_level(level, type, w, h); + + if (script < 0 || script > INIT_SCRIPT) script = -1; + + GFC.enabled = true; + GFC.group = group; + GFC.x = x; + GFC.y = y; + GFC.w = w; + GFC.h = h; + GFC.level = level; + GFC.script = script; + GF.type = type; +} + +static s16 grid_fader_max_value(scene_state_t *ss, u16 i) { + switch (GF.type) { + case FADER_CH_BAR: + case FADER_CH_DOT: return GFC.w - 1; + case FADER_CV_BAR: + case FADER_CV_DOT: return GFC.h - 1; + case FADER_FH_BAR: + case FADER_FV_BAR: + case FADER_FH_DOT: + case FADER_FV_DOT: return GFC.level; + } + return 0; +} + +static s16 grid_fader_clamp_level(s16 level, s16 type, s16 w, s16 h) { + if (type > FADER_COARSE) { + u8 size = (type == FADER_FH_BAR || type == FADER_FH_DOT) ? w : h; + s16 maxlevel = ((size - 2) << 4) - 1; + if (level < 0 || size < 3) return 0; + if (level > maxlevel) return maxlevel; + } + else { + if (level < (s16)LED_OFF) return LED_OFF; + if (level > (s16)15) return 15; + } + return level; +} \ No newline at end of file diff --git a/src/ops/grid_ops.h b/src/ops/grid_ops.h new file mode 100644 index 00000000..3e4637c6 --- /dev/null +++ b/src/ops/grid_ops.h @@ -0,0 +1,77 @@ +#ifndef _OPS_GRID_H_ +#define _OPS_GRID_H_ + +#include "ops/op.h" + +extern const tele_op_t op_G_RST; +extern const tele_op_t op_G_CLR; +extern const tele_op_t op_G_ROTATE; +extern const tele_op_t op_G_DIM; +extern const tele_op_t op_G_KEY; + +extern const tele_op_t op_G_GRP; +extern const tele_op_t op_G_GRP_EN; +extern const tele_op_t op_G_GRP_RST; +extern const tele_op_t op_G_GRP_SW; +extern const tele_op_t op_G_GRP_SC; +extern const tele_op_t op_G_GRPI; + +extern const tele_op_t op_G_LED; +extern const tele_op_t op_G_LED_C; +extern const tele_op_t op_G_REC; +extern const tele_op_t op_G_RCT; + +extern const tele_op_t op_G_BTN; +extern const tele_op_t op_G_BTX; +extern const tele_op_t op_G_GBT; +extern const tele_op_t op_G_GBX; +extern const tele_op_t op_G_BTN_EN; +extern const tele_op_t op_G_BTN_V; +extern const tele_op_t op_G_BTN_L; +extern const tele_op_t op_G_BTN_X; +extern const tele_op_t op_G_BTN_Y; +extern const tele_op_t op_G_BTNI; +extern const tele_op_t op_G_BTNV; +extern const tele_op_t op_G_BTNL; +extern const tele_op_t op_G_BTNX; +extern const tele_op_t op_G_BTNY; +extern const tele_op_t op_G_BTN_SW; +extern const tele_op_t op_G_BTN_PR; +extern const tele_op_t op_G_GBTN_V; +extern const tele_op_t op_G_GBTN_L; +extern const tele_op_t op_G_GBTN_C; +extern const tele_op_t op_G_GBTN_I; +extern const tele_op_t op_G_GBTN_W; +extern const tele_op_t op_G_GBTN_H; +extern const tele_op_t op_G_GBTN_X1; +extern const tele_op_t op_G_GBTN_X2; +extern const tele_op_t op_G_GBTN_Y1; +extern const tele_op_t op_G_GBTN_Y2; + +extern const tele_op_t op_G_FDR; +extern const tele_op_t op_G_FDX; +extern const tele_op_t op_G_GFD; +extern const tele_op_t op_G_GFX; +extern const tele_op_t op_G_FDR_EN; +extern const tele_op_t op_G_FDR_V; +extern const tele_op_t op_G_FDR_N; +extern const tele_op_t op_G_FDR_L; +extern const tele_op_t op_G_FDR_X; +extern const tele_op_t op_G_FDR_Y; +extern const tele_op_t op_G_FDRI; +extern const tele_op_t op_G_FDRV; +extern const tele_op_t op_G_FDRN; +extern const tele_op_t op_G_FDRL; +extern const tele_op_t op_G_FDRX; +extern const tele_op_t op_G_FDRY; +extern const tele_op_t op_G_GFDR_V; +extern const tele_op_t op_G_GFDR_N; +extern const tele_op_t op_G_GFDR_L; +extern const tele_op_t op_G_GFDR_RN; +extern const tele_op_t op_G_FDR_PR; + +extern const tele_op_t op_G_XYP; +extern const tele_op_t op_G_XYP_X; +extern const tele_op_t op_G_XYP_Y; + +#endif diff --git a/src/ops/hardware.c b/src/ops/hardware.c index ceecabc9..d9205d3f 100644 --- a/src/ops/hardware.c +++ b/src/ops/hardware.c @@ -215,7 +215,7 @@ static void op_CV_OFF_set(const void *NOTUSED(data), scene_state_t *ss, static void op_IN_get(const void *NOTUSED(data), scene_state_t *ss, exec_state_t *NOTUSED(es), command_state_t *cs) { - tele_update_in(); + tele_update_adc(0); cs_push(cs, ss_get_in(ss)); } @@ -247,6 +247,7 @@ static void op_IN_CAL_RESET_set(const void *NOTUSED(data), scene_state_t *ss, static void op_PARAM_get(const void *NOTUSED(data), scene_state_t *ss, exec_state_t *NOTUSED(es), command_state_t *cs) { + tele_update_adc(0); cs_push(cs, ss_get_param(ss)); } diff --git a/src/ops/init.c b/src/ops/init.c index 972834b4..38ae1c02 100644 --- a/src/ops/init.c +++ b/src/ops/init.c @@ -62,7 +62,7 @@ static void op_INIT_get(const void *NOTUSED(data), scene_state_t *ss, // At boot, all data is zeroed memset(ss, 0, sizeof(scene_state_t)); ss_init(ss); - + ss->cal = caldata; // Once calibration data is loaded, the scales need to be reset ss_update_param_scale(ss); @@ -98,13 +98,17 @@ static void op_INIT_SCRIPT_ALL_get(const void *NOTUSED(data), scene_state_t *ss, static void op_INIT_P_get(const void *NOTUSED(data), scene_state_t *ss, exec_state_t *NOTUSED(es), command_state_t *cs) { int16_t v = cs_pop(cs); - if (v >= 0 && v < 4) ss_pattern_init(ss, v); + if (v >= 0 && v < 4) { + ss_pattern_init(ss, v); + tele_pattern_updated(); + } } static void op_INIT_P_ALL_get(const void *NOTUSED(data), scene_state_t *ss, exec_state_t *NOTUSED(es), command_state_t *NOTUSED(cs)) { ss_patterns_init(ss); + tele_pattern_updated(); } static void op_INIT_CV_get(const void *NOTUSED(data), scene_state_t *ss, @@ -165,6 +169,8 @@ static void op_INIT_TIME_get(const void *NOTUSED(data), scene_state_t *ss, command_state_t *NOTUSED(cs)) { clear_delays(ss); ss->variables.time = 0; - for (uint8_t i = 0; i < TEMP_SCRIPT; i++) ss->scripts[i].last_time = 0; + uint32_t ticks = tele_get_ticks(); + for (uint8_t i = 0; i < TEMP_SCRIPT; i++) ss->scripts[i].last_time = ticks; + ss->variables.time = 0; ss_sync_every(ss, 0); } diff --git a/src/ops/maths.c b/src/ops/maths.c index 7a4a0b5f..e368ad4a 100644 --- a/src/ops/maths.c +++ b/src/ops/maths.c @@ -22,7 +22,7 @@ static void op_RAND_get(const void *data, scene_state_t *ss, exec_state_t *es, static void op_RRAND_get(const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); static void op_R_get(const void *data, scene_state_t *ss, exec_state_t *es, - command_state_t *cs); + command_state_t *cs); static void op_R_MIN_get(const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); static void op_R_MIN_set(const void *data, scene_state_t *ss, exec_state_t *es, @@ -113,6 +113,8 @@ static void op_CHAOS_ALG_get(const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); static void op_CHAOS_ALG_set(const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_TIF_get(const void *data, scene_state_t *ss, exec_state_t *es, + command_state_t *cs); // clang-format off const tele_op_t op_ADD = MAKE_GET_OP(ADD , op_ADD_get , 2, true); @@ -121,7 +123,9 @@ const tele_op_t op_MUL = MAKE_GET_OP(MUL , op_MUL_get , 2, true); const tele_op_t op_DIV = MAKE_GET_OP(DIV , op_DIV_get , 2, true); const tele_op_t op_MOD = MAKE_GET_OP(MOD , op_MOD_get , 2, true); const tele_op_t op_RAND = MAKE_GET_OP(RAND , op_RAND_get , 1, true); +const tele_op_t op_RND = MAKE_GET_OP(RND , op_RAND_get , 1, true); const tele_op_t op_RRAND = MAKE_GET_OP(RRAND , op_RRAND_get , 2, true); +const tele_op_t op_RRND = MAKE_GET_OP(RRND , op_RRAND_get , 2, true); const tele_op_t op_R = MAKE_GET_OP(R , op_R_get , 0, true); const tele_op_t op_R_MIN = MAKE_GET_SET_OP(R.MIN, op_R_MIN_get, op_R_MIN_set, 0, true); const tele_op_t op_R_MAX = MAKE_GET_SET_OP(R.MAX, op_R_MAX_get, op_R_MAX_set, 0, true); @@ -130,6 +134,7 @@ const tele_op_t op_MIN = MAKE_GET_OP(MIN , op_MIN_get , 2, true); const tele_op_t op_MAX = MAKE_GET_OP(MAX , op_MAX_get , 2, true); const tele_op_t op_LIM = MAKE_GET_OP(LIM , op_LIM_get , 3, true); const tele_op_t op_WRAP = MAKE_GET_OP(WRAP , op_WRAP_get , 3, true); +const tele_op_t op_WRP = MAKE_GET_OP(WRP , op_WRAP_get , 3, true); const tele_op_t op_QT = MAKE_GET_OP(QT , op_QT_get , 2, true); const tele_op_t op_AVG = MAKE_GET_OP(AVG , op_AVG_get , 2, true); const tele_op_t op_EQ = MAKE_GET_OP(EQ , op_EQ_get , 2, true); @@ -148,6 +153,7 @@ const tele_op_t op_AND = MAKE_GET_OP(AND , op_AND_get , 2, true); const tele_op_t op_OR = MAKE_GET_OP(OR , op_OR_get , 2, true); const tele_op_t op_JI = MAKE_GET_OP(JI , op_JI_get , 2, true); const tele_op_t op_SCALE = MAKE_GET_OP(SCALE , op_SCALE_get , 5, true); +const tele_op_t op_SCL = MAKE_GET_OP(SCL , op_SCALE_get , 5, true); const tele_op_t op_N = MAKE_GET_OP(N , op_N_get , 1, true); const tele_op_t op_V = MAKE_GET_OP(V , op_V_get , 1, true); const tele_op_t op_VV = MAKE_GET_OP(VV , op_VV_get , 1, true); @@ -163,6 +169,7 @@ const tele_op_t op_BCLR = MAKE_GET_OP(BCLR , op_BCLR_get , 2, true); const tele_op_t op_CHAOS = MAKE_GET_SET_OP(CHAOS, op_CHAOS_get, op_CHAOS_set, 0, true); const tele_op_t op_CHAOS_R = MAKE_GET_SET_OP(CHAOS.R, op_CHAOS_R_get, op_CHAOS_R_set, 0, true); const tele_op_t op_CHAOS_ALG = MAKE_GET_SET_OP(CHAOS.ALG, op_CHAOS_ALG_get, op_CHAOS_ALG_set, 0, true); +const tele_op_t op_TIF = MAKE_GET_OP(?, op_TIF_get, 3, true); const tele_op_t op_XOR = MAKE_ALIAS_OP(XOR, op_NE_get, NULL, 2, true); @@ -223,17 +230,16 @@ static void op_MOD_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), static void op_RAND_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), exec_state_t *NOTUSED(es), command_state_t *cs) { int16_t a = cs_pop(cs); - if (a == -1) - cs_push(cs, 0); + if (a < 0) + cs_push(cs, -(rand() % (1 - a))); + else if (a == 32767) + cs_push(cs, rand()); else cs_push(cs, rand() % (a + 1)); } -static void op_RRAND_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), - exec_state_t *NOTUSED(es), command_state_t *cs) { - int16_t a, b, min, max, range; - a = cs_pop(cs); - b = cs_pop(cs); +static int16_t push_random(int16_t a, int16_t b) { + int16_t min, max; if (a < b) { min = a; max = b; @@ -242,28 +248,26 @@ static void op_RRAND_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), min = b; max = a; } - range = max - min + 1; - if (range == 0) - cs_push(cs, a); - else - cs_push(cs, rand() % range + min); + int64_t range = max - min + 1; + if (range == 0 || min == max) return min; + + int64_t rrand = ((int64_t)rand() << 15) + rand(); + rrand = rrand % range + min; + return rrand; +} + +static void op_RRAND_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), + exec_state_t *NOTUSED(es), command_state_t *cs) { + int16_t a, b; + a = cs_pop(cs); + b = cs_pop(cs); + cs_push(cs, push_random(a, b)); } static void op_R_get(const void *NOTUSED(data), scene_state_t *ss, - exec_state_t *NOTUSED(es), command_state_t *cs) { - int16_t min = ss->variables.r_min; - int16_t max = ss->variables.r_max; - if (max < min) { - int16_t temp = min; - min = max; - max = temp; - } - int16_t range = max - min + 1; - if (range == 0) - cs_push(cs, min); - else - cs_push(cs, rand() % range + min); + exec_state_t *NOTUSED(es), command_state_t *cs) { + cs_push(cs, push_random(ss->variables.r_min, ss->variables.r_max)); } static void op_R_MIN_get(const void *NOTUSED(data), scene_state_t *ss, @@ -469,26 +473,27 @@ static void op_OR_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), static void op_JI_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), exec_state_t *NOTUSED(es), command_state_t *cs) { - const uint8_t prime[6] = {2, 3, 5, 7, 11, 13 }; + const uint8_t prime[6] = { 2, 3, 5, 7, 11, 13 }; const int16_t ji_const[6] = { 6554, 10388, 15218, 18399, 22673, 24253 }; int32_t result = 0; int16_t n = abs(cs_pop(cs)); int16_t d = abs(cs_pop(cs)); - + /* code for generation of ji_const - + int16_t ji_find_prime_constant( uint16_t prime ) { float r = 1638.0 * logf( (float)prime ) / log( 2.0 ); - r *= 4.0; // this corresponds to the inverse of the bitshift applied at rounding & scaling + r *= 4.0; // this corresponds to the inverse of + the bitshift applied at rounding & scaling return( (int16_t)( r + 0.5 ) ); } */ - + if (n == 0 || d == 0) { // early return if zeroes cs_push(cs, 0); return; } - + for (uint8_t p = 0; p <= 6; p++) { // find num factors if (n == 1) { break; } // succeed if all primes found if (p == 6) { // failed to find solution @@ -515,13 +520,13 @@ static void op_JI_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), quotient = d / prime[p]; } } - result = ( result + 2 ) >> 2; // round & scale + result = (result + 2) >> 2; // round & scale cs_push(cs, result); } static void op_SCALE_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), exec_state_t *NOTUSED(es), command_state_t *cs) { - int16_t a, b, x, y, i; + int32_t a, b, x, y, i; a = cs_pop(cs); b = cs_pop(cs); x = cs_pop(cs); @@ -533,7 +538,10 @@ static void op_SCALE_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), return; } - cs_push(cs, (i - a) * (y - x) / (b - a) + x); + int32_t result = (i - a) * (y - x) * 2 / (b - a); + result = result / 2 + (result & 1); // rounding + + cs_push(cs, result + x); } static void op_N_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), @@ -681,3 +689,11 @@ static void op_CHAOS_ALG_set(const void *NOTUSED(data), exec_state_t *NOTUSED(es), command_state_t *cs) { chaos_set_alg(cs_pop(cs)); } + +static void op_TIF_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 condition = cs_pop(cs); + s16 a = cs_pop(cs); + s16 b = cs_pop(cs); + cs_push(cs, condition ? a : b); +} diff --git a/src/ops/maths.h b/src/ops/maths.h index 5598b1d2..c595f7bc 100644 --- a/src/ops/maths.h +++ b/src/ops/maths.h @@ -9,7 +9,9 @@ extern const tele_op_t op_MUL; extern const tele_op_t op_DIV; extern const tele_op_t op_MOD; extern const tele_op_t op_RAND; +extern const tele_op_t op_RND; extern const tele_op_t op_RRAND; +extern const tele_op_t op_RRND; extern const tele_op_t op_R; extern const tele_op_t op_R_MIN; extern const tele_op_t op_R_MAX; @@ -18,6 +20,7 @@ extern const tele_op_t op_MIN; extern const tele_op_t op_MAX; extern const tele_op_t op_LIM; extern const tele_op_t op_WRAP; +extern const tele_op_t op_WRP; extern const tele_op_t op_QT; extern const tele_op_t op_AVG; extern const tele_op_t op_EQ; @@ -36,6 +39,7 @@ extern const tele_op_t op_AND; extern const tele_op_t op_OR; extern const tele_op_t op_JI; extern const tele_op_t op_SCALE; +extern const tele_op_t op_SCL; extern const tele_op_t op_N; extern const tele_op_t op_V; extern const tele_op_t op_VV; @@ -51,6 +55,7 @@ extern const tele_op_t op_BCLR; extern const tele_op_t op_CHAOS; extern const tele_op_t op_CHAOS_R; extern const tele_op_t op_CHAOS_ALG; +extern const tele_op_t op_TIF; // ternary if extern const tele_op_t op_XOR; // XOR alias NE diff --git a/src/ops/matrixarchate.c b/src/ops/matrixarchate.c new file mode 100644 index 00000000..b510c7c5 --- /dev/null +++ b/src/ops/matrixarchate.c @@ -0,0 +1,302 @@ +#include "ops/matrixarchate.h" + +#include "helpers.h" +#include "ii.h" +#include "teletype.h" +#include "teletype_io.h" + +static void op_MA_SELECT_get(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); +static void op_MA_SELECT_set(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); + +static void op_MA_STEP_get(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); +static void op_MA_RESET_get(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); +static void op_MA_PGM_get(const void *data, scene_state_t *ss, exec_state_t *es, + command_state_t *cs); + +static void op_MA_ON_get(const void *data, scene_state_t *ss, exec_state_t *es, + command_state_t *cs); +static void op_MA_PON_get(const void *data, scene_state_t *ss, exec_state_t *es, + command_state_t *cs); +static void op_MA_OFF_get(const void *data, scene_state_t *ss, exec_state_t *es, + command_state_t *cs); +static void op_MA_POFF_get(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); +static void op_MA_SET_get(const void *data, scene_state_t *ss, exec_state_t *es, + command_state_t *cs); +static void op_MA_PSET_get(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); + +static void op_MA_COL_get(const void *data, scene_state_t *ss, exec_state_t *es, + command_state_t *cs); +static void op_MA_COL_set(const void *data, scene_state_t *ss, exec_state_t *es, + command_state_t *cs); +static void op_MA_PCOL_get(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); +static void op_MA_PCOL_set(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); +static void op_MA_ROW_get(const void *data, scene_state_t *ss, exec_state_t *es, + command_state_t *cs); +static void op_MA_ROW_set(const void *data, scene_state_t *ss, exec_state_t *es, + command_state_t *cs); +static void op_MA_PROW_get(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); +static void op_MA_PROW_set(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); + +static void op_MA_CLR_get(const void *data, scene_state_t *ss, exec_state_t *es, + command_state_t *cs); +static void op_MA_PCLR_get(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); + +const tele_op_t op_MA_SELECT = + MAKE_GET_SET_OP(MA.SELECT, op_MA_SELECT_get, op_MA_SELECT_set, 0, true); + +const tele_op_t op_MA_STEP = MAKE_GET_OP(MA.STEP, op_MA_STEP_get, 0, false); +const tele_op_t op_MA_RESET = MAKE_GET_OP(MA.RESET, op_MA_RESET_get, 0, false); +const tele_op_t op_MA_PGM = MAKE_GET_OP(MA.PGM, op_MA_PGM_get, 1, false); + +const tele_op_t op_MA_ON = MAKE_GET_OP(MA.ON, op_MA_ON_get, 2, false); +const tele_op_t op_MA_PON = MAKE_GET_OP(MA.PON, op_MA_PON_get, 3, false); +const tele_op_t op_MA_OFF = MAKE_GET_OP(MA.OFF, op_MA_OFF_get, 2, false); +const tele_op_t op_MA_POFF = MAKE_GET_OP(MA.POFF, op_MA_POFF_get, 3, false); +const tele_op_t op_MA_SET = MAKE_GET_OP(MA.SET, op_MA_SET_get, 3, false); +const tele_op_t op_MA_PSET = MAKE_GET_OP(MA.PSET, op_MA_PSET_get, 4, false); + +const tele_op_t op_MA_COL = + MAKE_GET_SET_OP(MA.COL, op_MA_COL_get, op_MA_COL_set, 1, true); +const tele_op_t op_MA_PCOL = + MAKE_GET_SET_OP(MA.PCOL, op_MA_PCOL_get, op_MA_PCOL_set, 2, true); +const tele_op_t op_MA_ROW = + MAKE_GET_SET_OP(MA.ROW, op_MA_ROW_get, op_MA_ROW_set, 1, true); +const tele_op_t op_MA_PROW = + MAKE_GET_SET_OP(MA.PROW, op_MA_PROW_get, op_MA_PROW_set, 2, true); + +const tele_op_t op_MA_CLR = MAKE_GET_OP(MA.CLR, op_MA_CLR_get, 0, false); +const tele_op_t op_MA_PCLR = MAKE_GET_OP(MA.PCLR, op_MA_PCLR_get, 1, false); + +u8 selected_ma = 0; + +static void ma_set(s16 row, s16 column, s16 value) { + if (row < 0 || row > 15 || column < 0 || column > 7) return; + uint8_t d[] = { value ? 0b10010000 : 0b10000000, (row << 3) + column, 128 }; + tele_ii_tx(MATRIXARCHATE + selected_ma, d, 3); +} + +static void ma_set_pgm(s16 program, s16 row, s16 column, s16 value) { + if (program < 0 || program > 59 || row < 0 || row > 15 || column < 0 || + column > 7) + return; + uint8_t d[] = { value ? 0b10010000 : 0b10000000, (row << 3) + column, + program }; + tele_ii_tx(MATRIXARCHATE + selected_ma, d, 3); +} + +static void ma_set_col(s16 column, u16 value) { + if (column < 0 || column > 7) return; + uint8_t d[] = { 0b10110000, column, 128, value & 255, value >> 8 }; + tele_ii_tx(MATRIXARCHATE + selected_ma, d, 5); +} + +static void ma_set_col_pgm(s16 program, s16 column, u16 value) { + if (program < 0 || program > 59 || column < 0 || column > 7) return; + uint8_t d[] = { 0b10110000, column, program, value & 255, value >> 8 }; + tele_ii_tx(MATRIXARCHATE + selected_ma, d, 5); +} + +static void ma_set_row(s16 row, u16 value) { + if (row < 0 || row > 15) return; + uint8_t d[] = { 0b10110000, row | 128, 128, value & 255, value >> 8 }; + tele_ii_tx(MATRIXARCHATE + selected_ma, d, 5); +} + +static void ma_set_row_pgm(s16 program, s16 row, u16 value) { + if (program < 0 || program > 59 || row < 0 || row > 15) return; + uint8_t d[] = { 0b10110000, row | 128, program, value & 255, value >> 8 }; + tele_ii_tx(MATRIXARCHATE + selected_ma, d, 5); +} + +static void op_MA_SELECT_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + cs_push(cs, selected_ma + 1); +} + +static void op_MA_SELECT_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 i = cs_pop(cs) - 1; + if (i < 0 || i > 2) return; + selected_ma = i; +} + +static void op_MA_STEP_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + uint8_t d[] = { 0b11111000 }; + tele_ii_tx(MATRIXARCHATE + selected_ma, d, 1); +} + +static void op_MA_RESET_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + uint8_t d[] = { 0b11111101 }; + tele_ii_tx(MATRIXARCHATE + selected_ma, d, 1); +} + +static void op_MA_PGM_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 program = cs_pop(cs) - 1; + if (program < 0 || program > 59) return; + uint8_t d[] = { 0b11000000, program }; + tele_ii_tx(MATRIXARCHATE + selected_ma, d, 2); +} + +static void op_MA_ON_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 row = cs_pop(cs); + s16 column = cs_pop(cs); + ma_set(row, column, 1); +} + +static void op_MA_PON_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 program = cs_pop(cs) - 1; + s16 row = cs_pop(cs); + s16 column = cs_pop(cs); + ma_set_pgm(program, row, column, 1); +} + +static void op_MA_OFF_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 row = cs_pop(cs); + s16 column = cs_pop(cs); + ma_set(row, column, 0); +} + +static void op_MA_POFF_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 program = cs_pop(cs) - 1; + s16 row = cs_pop(cs); + s16 column = cs_pop(cs); + ma_set_pgm(program, row, column, 0); +} + +static void op_MA_SET_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 row = cs_pop(cs); + s16 column = cs_pop(cs); + s16 value = cs_pop(cs); + ma_set(row, column, value); +} + +static void op_MA_PSET_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 program = cs_pop(cs) - 1; + s16 row = cs_pop(cs); + s16 column = cs_pop(cs); + s16 value = cs_pop(cs); + ma_set_pgm(program, row, column, value); +} + +static void op_MA_COL_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 column = cs_pop(cs); + u16 value = 0; + if (column >= 0 && column <= 7) { + uint8_t d[] = { 0b11110101, column, 128 }; + tele_ii_tx(MATRIXARCHATE + selected_ma, d, 3); + d[0] = 0; + d[1] = 0; + tele_ii_rx(MATRIXARCHATE + selected_ma, d, 2); + value = (d[1] << 8) + d[0]; + } + cs_push(cs, value); +} + +static void op_MA_COL_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 column = cs_pop(cs); + u16 value = cs_pop(cs); + ma_set_col(column, value); +} + +static void op_MA_PCOL_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 program = cs_pop(cs) - 1; + s16 column = cs_pop(cs); + u16 value = 0; + if (column >= 0 && column <= 7 && program >= 0 && program <= 59) { + uint8_t d[] = { 0b11110101, column, program }; + tele_ii_tx(MATRIXARCHATE + selected_ma, d, 3); + d[0] = 0; + d[1] = 0; + tele_ii_rx(MATRIXARCHATE + selected_ma, d, 2); + value = (d[1] << 8) + d[0]; + } + cs_push(cs, value); +} + +static void op_MA_PCOL_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 program = cs_pop(cs) - 1; + s16 column = cs_pop(cs); + u16 value = cs_pop(cs); + ma_set_col_pgm(program, column, value); +} + +static void op_MA_ROW_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 row = cs_pop(cs); + u16 value = 0; + if (row >= 0 && row <= 15) { + uint8_t d[] = { 0b11110101, row | 128, 128 }; + tele_ii_tx(MATRIXARCHATE + selected_ma, d, 3); + d[0] = 0; + d[1] = 0; + tele_ii_rx(MATRIXARCHATE + selected_ma, d, 2); + value = (d[1] << 8) + d[0]; + } + cs_push(cs, value); +} + +static void op_MA_ROW_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 row = cs_pop(cs); + u16 value = cs_pop(cs); + ma_set_row(row, value); +} + +static void op_MA_PROW_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 program = cs_pop(cs) - 1; + s16 row = cs_pop(cs); + u16 value = 0; + if (row >= 0 && row <= 15 && program >= 0 && program <= 59) { + uint8_t d[] = { 0b11110101, row | 128, program }; + tele_ii_tx(MATRIXARCHATE + selected_ma, d, 3); + d[0] = 0; + d[1] = 0; + tele_ii_rx(MATRIXARCHATE + selected_ma, d, 2); + value = (d[1] << 8) + d[0]; + } + cs_push(cs, value); +} + +static void op_MA_PROW_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 program = cs_pop(cs) - 1; + s16 row = cs_pop(cs); + u16 value = cs_pop(cs); + ma_set_row_pgm(program, row, value); +} + +static void op_MA_CLR_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + for (u8 i = 0; i < 8; i++) ma_set_col(i, 0); +} + +static void op_MA_PCLR_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + s16 program = cs_pop(cs) - 1; + for (u8 i = 0; i < 8; i++) ma_set_col_pgm(program, i, 0); +} diff --git a/src/ops/matrixarchate.h b/src/ops/matrixarchate.h new file mode 100644 index 00000000..85c90eb4 --- /dev/null +++ b/src/ops/matrixarchate.h @@ -0,0 +1,23 @@ +#ifndef _OPS_MATRIXARCHATE_H_ +#define _OPS_MATRIXARCHATE_H_ + +#include "ops/op.h" + +extern const tele_op_t op_MA_SELECT; +extern const tele_op_t op_MA_STEP; +extern const tele_op_t op_MA_RESET; +extern const tele_op_t op_MA_PGM; +extern const tele_op_t op_MA_ON; +extern const tele_op_t op_MA_PON; +extern const tele_op_t op_MA_OFF; +extern const tele_op_t op_MA_POFF; +extern const tele_op_t op_MA_SET; +extern const tele_op_t op_MA_PSET; +extern const tele_op_t op_MA_COL; +extern const tele_op_t op_MA_PCOL; +extern const tele_op_t op_MA_ROW; +extern const tele_op_t op_MA_PROW; +extern const tele_op_t op_MA_CLR; +extern const tele_op_t op_MA_PCLR; + +#endif \ No newline at end of file diff --git a/src/ops/op.c b/src/ops/op.c index f6998168..70ec2cb2 100644 --- a/src/ops/op.c +++ b/src/ops/op.c @@ -9,10 +9,14 @@ #include "ops/controlflow.h" #include "ops/delay.h" #include "ops/earthsea.h" +#include "ops/er301.h" +#include "ops/fader.h" +#include "ops/grid_ops.h" #include "ops/hardware.h" #include "ops/init.h" #include "ops/justfriends.h" #include "ops/maths.h" +#include "ops/matrixarchate.h" #include "ops/meadowphysics.h" #include "ops/metronome.h" #include "ops/orca.h" @@ -56,7 +60,9 @@ const tele_op_t *tele_ops[E_OP__LENGTH] = { &op_P_START, &op_PN_START, &op_P_END, &op_PN_END, &op_P_I, &op_PN_I, &op_P_HERE, &op_PN_HERE, &op_P_NEXT, &op_PN_NEXT, &op_P_PREV, &op_PN_PREV, &op_P_INS, &op_PN_INS, &op_P_RM, &op_PN_RM, &op_P_PUSH, &op_PN_PUSH, - &op_P_POP, &op_PN_POP, + &op_P_POP, &op_PN_POP, &op_P_MIN, &op_PN_MIN, &op_P_MAX, &op_PN_MAX, + &op_P_RND, &op_PN_RND, &op_P_ADD, &op_PN_ADD, &op_P_SUB, &op_PN_SUB, + &op_P_ADDW, &op_PN_ADDW, &op_P_SUBW, &op_PN_SUBW, // queue &op_Q, &op_Q_AVG, &op_Q_N, @@ -69,23 +75,25 @@ const tele_op_t *tele_ops[E_OP__LENGTH] = { &op_MUTE, &op_STATE, // maths - &op_ADD, &op_SUB, &op_MUL, &op_DIV, &op_MOD, &op_RAND, &op_RRAND, &op_R, &op_R_MIN, &op_R_MAX, &op_TOSS, - &op_MIN, &op_MAX, &op_LIM, &op_WRAP, &op_QT, &op_AVG, &op_EQ, &op_NE, - &op_LT, &op_GT, &op_LTE, &op_GTE, &op_NZ, &op_EZ, &op_RSH, &op_LSH, &op_EXP, - &op_ABS, &op_AND, &op_OR, &op_JI, &op_SCALE, &op_N, &op_V, &op_VV, &op_ER, - &op_BPM, &op_BIT_OR, &op_BIT_AND, &op_BIT_NOT, &op_BIT_XOR, &op_BSET, - &op_BGET, &op_BCLR, &op_XOR, &op_CHAOS, &op_CHAOS_R, &op_CHAOS_ALG, - &op_SYM_PLUS, &op_SYM_DASH, &op_SYM_STAR, &op_SYM_FORWARD_SLASH, - &op_SYM_PERCENTAGE, &op_SYM_EQUAL_x2, &op_SYM_EXCLAMATION_EQUAL, - &op_SYM_LEFT_ANGLED, &op_SYM_RIGHT_ANGLED, &op_SYM_LEFT_ANGLED_EQUAL, - &op_SYM_RIGHT_ANGLED_EQUAL, &op_SYM_EXCLAMATION, &op_SYM_LEFT_ANGLED_x2, - &op_SYM_RIGHT_ANGLED_x2, &op_SYM_AMPERSAND_x2, &op_SYM_PIPE_x2, + &op_ADD, &op_SUB, &op_MUL, &op_DIV, &op_MOD, &op_RAND, &op_RND, &op_RRAND, + &op_RRND, &op_R, &op_R_MIN, &op_R_MAX, &op_TOSS, &op_MIN, &op_MAX, &op_LIM, + &op_WRAP, &op_WRP, &op_QT, &op_AVG, &op_EQ, &op_NE, &op_LT, &op_GT, &op_LTE, + &op_GTE, &op_NZ, &op_EZ, &op_RSH, &op_LSH, &op_EXP, &op_ABS, &op_AND, + &op_OR, &op_JI, &op_SCALE, &op_SCL, &op_N, &op_V, &op_VV, &op_ER, &op_BPM, + &op_BIT_OR, &op_BIT_AND, &op_BIT_NOT, &op_BIT_XOR, &op_BSET, &op_BGET, + &op_BCLR, &op_XOR, &op_CHAOS, &op_CHAOS_R, &op_CHAOS_ALG, &op_SYM_PLUS, + &op_SYM_DASH, &op_SYM_STAR, &op_SYM_FORWARD_SLASH, &op_SYM_PERCENTAGE, + &op_SYM_EQUAL_x2, &op_SYM_EXCLAMATION_EQUAL, &op_SYM_LEFT_ANGLED, + &op_SYM_RIGHT_ANGLED, &op_SYM_LEFT_ANGLED_EQUAL, &op_SYM_RIGHT_ANGLED_EQUAL, + &op_SYM_EXCLAMATION, &op_SYM_LEFT_ANGLED_x2, &op_SYM_RIGHT_ANGLED_x2, + &op_SYM_AMPERSAND_x2, &op_SYM_PIPE_x2, &op_TIF, // stack &op_S_ALL, &op_S_POP, &op_S_CLR, &op_S_L, // controlflow - &op_SCRIPT, &op_KILL, &op_SCENE, &op_BREAK, &op_BRK, &op_SYNC, + &op_SCRIPT, &op_SYM_DOLLAR, &op_KILL, &op_SCENE, &op_BREAK, &op_BRK, + &op_SYNC, // delay &op_DEL_CLR, @@ -109,11 +117,11 @@ const tele_op_t *tele_ops[E_OP__LENGTH] = { // ansible &op_KR_PRE, &op_KR_PAT, &op_KR_SCALE, &op_KR_PERIOD, &op_KR_POS, - &op_KR_L_ST, &op_KR_L_LEN, &op_KR_RES, &op_KR_CV, &op_KR_MUTE, &op_KR_TMUTE, &op_KR_CLK, - &op_ME_PRE, &op_ME_RES, &op_ME_STOP, - &op_ME_SCALE, &op_ME_PERIOD, &op_ME_CV, &op_LV_PRE, &op_LV_RES, &op_LV_POS, - &op_LV_L_ST, &op_LV_L_LEN, &op_LV_L_DIR, &op_LV_CV, &op_CY_PRE, &op_CY_RES, - &op_CY_POS, &op_CY_REV, &op_CY_CV, &op_MID_SHIFT, &op_MID_SLEW, &op_ARP_STY, + &op_KR_L_ST, &op_KR_L_LEN, &op_KR_RES, &op_KR_CV, &op_KR_MUTE, &op_KR_TMUTE, + &op_KR_CLK, &op_ME_PRE, &op_ME_RES, &op_ME_STOP, &op_ME_SCALE, + &op_ME_PERIOD, &op_ME_CV, &op_LV_PRE, &op_LV_RES, &op_LV_POS, &op_LV_L_ST, + &op_LV_L_LEN, &op_LV_L_DIR, &op_LV_CV, &op_CY_PRE, &op_CY_RES, &op_CY_POS, + &op_CY_REV, &op_CY_CV, &op_MID_SHIFT, &op_MID_SLEW, &op_ARP_STY, &op_ARP_HLD, &op_ARP_RPT, &op_ARP_GT, &op_ARP_DIV, &op_ARP_RES, &op_ARP_SHIFT, &op_ARP_SLEW, &op_ARP_FIL, &op_ARP_ROT, &op_ARP_ER, @@ -156,6 +164,8 @@ const tele_op_t *tele_ops[E_OP__LENGTH] = { &op_TO_ENV_DEC, &op_TO_ENV_DEC_S, &op_TO_ENV_DEC_M, &op_TO_ENV_TRIG, &op_TO_ENV_EOR, &op_TO_ENV_EOC, &op_TO_ENV_LOOP, + &op_TO_ENV, &op_TO_CV_CALIB, &op_TO_CV_RESET, + &op_TI_PARAM, &op_TI_PARAM_QT, &op_TI_PARAM_N, &op_TI_PARAM_SCALE, &op_TI_PARAM_MAP, &op_TI_IN, &op_TI_IN_QT, &op_TI_IN_N, &op_TI_IN_SCALE, &op_TI_IN_MAP, &op_TI_PARAM_CALIB, &op_TI_IN_CALIB, &op_TI_STORE, @@ -164,7 +174,33 @@ const tele_op_t *tele_ops[E_OP__LENGTH] = { &op_TI_PARAM_INIT, &op_TI_IN_INIT, &op_TI_INIT, &op_TI_PRM, &op_TI_PRM_QT, &op_TI_PRM_N, &op_TI_PRM_SCALE, &op_TI_PRM_MAP, - &op_TI_PRM_INIT + &op_TI_PRM_INIT, + + // fader + &op_FADER, &op_FB, + + // ER301 + &op_SC_TR, &op_SC_TR_TOG, &op_SC_TR_PULSE, &op_SC_TR_TIME, &op_SC_TR_POL, + &op_SC_CV, &op_SC_CV_SLEW, &op_SC_CV_SET, &op_SC_CV_OFF, &op_SC_TR_P, + + // grid + &op_G_RST, &op_G_CLR, &op_G_ROTATE, &op_G_DIM, &op_G_KEY, &op_G_GRP, + &op_G_GRP_EN, &op_G_GRP_RST, &op_G_GRP_SW, &op_G_GRP_SC, &op_G_GRPI, + &op_G_LED, &op_G_LED_C, &op_G_REC, &op_G_RCT, &op_G_BTN, &op_G_BTX, + &op_G_GBT, &op_G_GBX, &op_G_BTN_EN, &op_G_BTN_V, &op_G_BTN_L, &op_G_BTN_X, + &op_G_BTN_Y, &op_G_BTNI, &op_G_BTNV, &op_G_BTNL, &op_G_BTNX, &op_G_BTNY, + &op_G_BTN_SW, &op_G_BTN_PR, &op_G_GBTN_V, &op_G_GBTN_L, &op_G_FDR, + &op_G_FDX, &op_G_GFD, &op_G_GFX, &op_G_FDR_EN, &op_G_FDR_V, &op_G_FDR_N, + &op_G_FDR_L, &op_G_FDR_X, &op_G_FDR_Y, &op_G_FDRI, &op_G_FDRV, &op_G_FDRN, + &op_G_FDRL, &op_G_FDRX, &op_G_FDRY, &op_G_FDR_PR, &op_G_GFDR_V, + &op_G_GFDR_N, &op_G_GFDR_L, &op_G_GFDR_RN, &op_G_XYP, &op_G_XYP_X, + &op_G_XYP_Y, &op_G_GBTN_C, &op_G_GBTN_I, &op_G_GBTN_W, &op_G_GBTN_H, + &op_G_GBTN_X1, &op_G_GBTN_X2, &op_G_GBTN_Y1, &op_G_GBTN_Y2, + + // matrixarchate + &op_MA_SELECT, &op_MA_STEP, &op_MA_RESET, &op_MA_PGM, &op_MA_ON, &op_MA_PON, + &op_MA_OFF, &op_MA_POFF, &op_MA_SET, &op_MA_PSET, &op_MA_COL, &op_MA_PCOL, + &op_MA_ROW, &op_MA_PROW, &op_MA_CLR, &op_MA_PCLR }; ///////////////////////////////////////////////////////////////// diff --git a/src/ops/op_enum.h b/src/ops/op_enum.h index e9a428b6..b4246552 100644 --- a/src/ops/op_enum.h +++ b/src/ops/op_enum.h @@ -88,6 +88,20 @@ typedef enum { E_OP_PN_PUSH, E_OP_P_POP, E_OP_PN_POP, + E_OP_P_MIN, + E_OP_PN_MIN, + E_OP_P_MAX, + E_OP_PN_MAX, + E_OP_P_RND, + E_OP_PN_RND, + E_OP_P_ADD, + E_OP_PN_ADD, + E_OP_P_SUB, + E_OP_PN_SUB, + E_OP_P_ADDW, + E_OP_PN_ADDW, + E_OP_P_SUBW, + E_OP_PN_SUBW, E_OP_Q, E_OP_Q_AVG, E_OP_Q_N, @@ -120,7 +134,9 @@ typedef enum { E_OP_DIV, E_OP_MOD, E_OP_RAND, + E_OP_RND, E_OP_RRAND, + E_OP_RRND, E_OP_R, E_OP_R_MIN, E_OP_R_MAX, @@ -129,6 +145,7 @@ typedef enum { E_OP_MAX, E_OP_LIM, E_OP_WRAP, + E_OP_WRP, E_OP_QT, E_OP_AVG, E_OP_EQ, @@ -147,6 +164,7 @@ typedef enum { E_OP_OR, E_OP_JI, E_OP_SCALE, + E_OP_SCL, E_OP_N, E_OP_V, E_OP_VV, @@ -179,11 +197,13 @@ typedef enum { E_OP_SYM_RIGHT_ANGLED_x2, E_OP_SYM_AMPERSAND_x2, E_OP_SYM_PIPE_x2, + E_OP_TIF, E_OP_S_ALL, E_OP_S_POP, E_OP_S_CLR, E_OP_S_L, E_OP_SCRIPT, + E_OP_SYM_DOLLAR, E_OP_KILL, E_OP_SCENE, E_OP_BREAK, @@ -372,6 +392,9 @@ typedef enum { E_OP_TO_ENV_EOR, E_OP_TO_ENV_EOC, E_OP_TO_ENV_LOOP, + E_OP_TO_ENV, + E_OP_TO_CV_CALIB, + E_OP_TO_CV_RESET, E_OP_TI_PARAM, E_OP_TI_PARAM_QT, E_OP_TI_PARAM_N, @@ -395,6 +418,99 @@ typedef enum { E_OP_TI_PRM_SCALE, E_OP_TI_PRM_MAP, E_OP_TI_PRM_INIT, + E_OP_FADER, + E_OP_FB, + E_OP_SC_TR, + E_OP_SC_TR_TOG, + E_OP_SC_TR_PULSE, + E_OP_SC_TR_TIME, + E_OP_SC_TR_POL, + E_OP_SC_CV, + E_OP_SC_CV_SLEW, + E_OP_SC_CV_SET, + E_OP_SC_CV_OFF, + E_OP_SC_TR_P, + E_OP_G_RST, + E_OP_G_CLR, + E_OP_G_ROTATE, + E_OP_G_DIM, + E_OP_G_KEY, + E_OP_G_GRP, + E_OP_G_GRP_EN, + E_OP_G_GRP_RST, + E_OP_G_GRP_SW, + E_OP_G_GRP_SC, + E_OP_G_GRPI, + E_OP_G_LED, + E_OP_G_LED_C, + E_OP_G_REC, + E_OP_G_RCT, + E_OP_G_BTN, + E_OP_G_BTX, + E_OP_G_GBT, + E_OP_G_GBX, + E_OP_G_BTN_EN, + E_OP_G_BTN_V, + E_OP_G_BTN_L, + E_OP_G_BTN_X, + E_OP_G_BTN_Y, + E_OP_G_BTNI, + E_OP_G_BTNV, + E_OP_G_BTNL, + E_OP_G_BTNX, + E_OP_G_BTNY, + E_OP_G_BTN_SW, + E_OP_G_BTN_PR, + E_OP_G_GBTN_V, + E_OP_G_GBTN_L, + E_OP_G_FDR, + E_OP_G_FDX, + E_OP_G_GFD, + E_OP_G_GFX, + E_OP_G_FDR_EN, + E_OP_G_FDR_V, + E_OP_G_FDR_N, + E_OP_G_FDR_L, + E_OP_G_FDR_X, + E_OP_G_FDR_Y, + E_OP_G_FDRI, + E_OP_G_FDRV, + E_OP_G_FDRN, + E_OP_G_FDRL, + E_OP_G_FDRX, + E_OP_G_FDRY, + E_OP_G_FDR_PR, + E_OP_G_GFDR_V, + E_OP_G_GFDR_N, + E_OP_G_GFDR_L, + E_OP_G_GFDR_RN, + E_OP_G_XYP, + E_OP_G_XYP_X, + E_OP_G_XYP_Y, + E_OP_G_GBTN_C, + E_OP_G_GBTN_I, + E_OP_G_GBTN_W, + E_OP_G_GBTN_H, + E_OP_G_GBTN_X1, + E_OP_G_GBTN_X2, + E_OP_G_GBTN_Y1, + E_OP_G_GBTN_Y2, + E_OP_MA_SELECT, + E_OP_MA_STEP, + E_OP_MA_RESET, + E_OP_MA_PGM, + E_OP_MA_ON, + E_OP_MA_PON, + E_OP_MA_OFF, + E_OP_MA_POFF, + E_OP_MA_SET, + E_OP_MA_PSET, + E_OP_MA_COL, + E_OP_MA_PCOL, + E_OP_MA_ROW, + E_OP_MA_PROW, + E_OP_MA_CLR, + E_OP_MA_PCLR, E_OP__LENGTH, } tele_op_idx_t; diff --git a/src/ops/patterns.c b/src/ops/patterns.c index 0f7adf26..92d0eaf2 100644 --- a/src/ops/patterns.c +++ b/src/ops/patterns.c @@ -1,5 +1,7 @@ #include "ops/patterns.h" +#include // rand + #include "helpers.h" #include "teletype.h" #include "teletype_io.h" @@ -35,6 +37,20 @@ static int16_t normalise_idx(scene_state_t *ss, const int16_t pn, int16_t idx) { return idx; } +static int16_t wrap(int16_t value, int16_t a, int16_t b) { + int16_t c, i = value; + if (a < b) { + c = b - a + 1; + while (i >= b) i -= c; + while (i < a) i += c; + } + else { + c = a - b + 1; + while (i >= a) i -= c; + while (i < b) i += c; + } + return i; +} //////////////////////////////////////////////////////////////////////////////// // P.N ///////////////////////////////////////////////////////////////////////// @@ -82,6 +98,7 @@ static void p_set(scene_state_t *ss, int16_t pn, int16_t idx, int16_t val) { pn = normalise_pn(pn); idx = normalise_idx(ss, pn, idx); ss_set_pattern_val(ss, pn, idx, val); + tele_pattern_updated(); } static void op_P_set(const void *NOTUSED(data), scene_state_t *ss, @@ -90,7 +107,6 @@ static void op_P_set(const void *NOTUSED(data), scene_state_t *ss, int16_t a = cs_pop(cs); int16_t b = cs_pop(cs); p_set(ss, pn, a, b); - tele_pattern_updated(); } static void op_PN_set(const void *NOTUSED(data), scene_state_t *ss, @@ -99,7 +115,6 @@ static void op_PN_set(const void *NOTUSED(data), scene_state_t *ss, int16_t a = cs_pop(cs); int16_t b = cs_pop(cs); p_set(ss, pn, a, b); - tele_pattern_updated(); } // Make ops @@ -132,6 +147,7 @@ static void p_l_set(scene_state_t *ss, int16_t pn, int16_t l) { ss_set_pattern_len(ss, pn, PATTERN_LENGTH); else ss_set_pattern_len(ss, pn, l); + tele_pattern_updated(); } static void op_P_L_set(const void *NOTUSED(data), scene_state_t *ss, @@ -139,7 +155,6 @@ static void op_P_L_set(const void *NOTUSED(data), scene_state_t *ss, int16_t pn = ss->variables.p_n; int16_t a = cs_pop(cs); p_l_set(ss, pn, a); - tele_pattern_updated(); } static void op_PN_L_set(const void *NOTUSED(data), scene_state_t *ss, @@ -147,7 +162,6 @@ static void op_PN_L_set(const void *NOTUSED(data), scene_state_t *ss, int16_t pn = cs_pop(cs); int16_t a = cs_pop(cs); p_l_set(ss, pn, a); - tele_pattern_updated(); } // Make ops @@ -204,6 +218,13 @@ static void op_P_START_get(const void *NOTUSED(data), scene_state_t *ss, cs_push(cs, ss_get_pattern_start(ss, pn)); } +static void op_PN_START_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + int16_t pn = normalise_pn(cs_pop(cs)); + cs_push(cs, ss_get_pattern_start(ss, pn)); +} + +// Set static void op_P_START_set(const void *NOTUSED(data), scene_state_t *ss, exec_state_t *NOTUSED(es), command_state_t *cs) { int16_t pn = normalise_pn(ss->variables.p_n); @@ -212,13 +233,6 @@ static void op_P_START_set(const void *NOTUSED(data), scene_state_t *ss, tele_pattern_updated(); } -// Set -static void op_PN_START_get(const void *NOTUSED(data), scene_state_t *ss, - exec_state_t *NOTUSED(es), command_state_t *cs) { - int16_t pn = normalise_pn(cs_pop(cs)); - cs_push(cs, ss_get_pattern_start(ss, pn)); -} - static void op_PN_START_set(const void *NOTUSED(data), scene_state_t *ss, exec_state_t *NOTUSED(es), command_state_t *cs) { int16_t pn = normalise_pn(cs_pop(cs)); @@ -301,6 +315,7 @@ static void p_i_set(scene_state_t *ss, int16_t pn, int16_t i) { ss_set_pattern_idx(ss, pn, len - 1); else ss_set_pattern_idx(ss, pn, i); + tele_pattern_updated(); } static void op_P_I_set(const void *NOTUSED(data), scene_state_t *ss, @@ -308,7 +323,6 @@ static void op_P_I_set(const void *NOTUSED(data), scene_state_t *ss, int16_t pn = ss->variables.p_n; int16_t a = cs_pop(cs); p_i_set(ss, pn, a); - tele_pattern_updated(); } static void op_PN_I_set(const void *NOTUSED(data), scene_state_t *ss, @@ -316,7 +330,6 @@ static void op_PN_I_set(const void *NOTUSED(data), scene_state_t *ss, int16_t pn = cs_pop(cs); int16_t a = cs_pop(cs); p_i_set(ss, pn, a); - tele_pattern_updated(); } // Make ops @@ -347,6 +360,7 @@ static void op_P_HERE_set(const void *NOTUSED(data), scene_state_t *ss, int16_t pn = normalise_pn(ss->variables.p_n); int16_t a = cs_pop(cs); ss_set_pattern_val(ss, pn, ss_get_pattern_idx(ss, pn), a); + tele_pattern_updated(); } static void op_PN_HERE_set(const void *NOTUSED(data), scene_state_t *ss, @@ -354,6 +368,7 @@ static void op_PN_HERE_set(const void *NOTUSED(data), scene_state_t *ss, int16_t pn = normalise_pn(cs_pop(cs)); int16_t a = cs_pop(cs); ss_set_pattern_val(ss, pn, ss_get_pattern_idx(ss, pn), a); + tele_pattern_updated(); } // Make ops @@ -559,12 +574,14 @@ static int16_t p_rm_get(scene_state_t *ss, int16_t pn, int16_t idx) { idx = normalise_idx(ss, pn, idx); int16_t ret = ss_get_pattern_val(ss, pn, idx); - for (int16_t i = idx; i < len; i++) { - int16_t v = ss_get_pattern_val(ss, pn, i + 1); - ss_set_pattern_val(ss, pn, i, v); - } + if (idx < len) { + for (int16_t i = idx; i < len; i++) { + int16_t v = ss_get_pattern_val(ss, pn, i + 1); + ss_set_pattern_val(ss, pn, i, v); + } - ss_set_pattern_len(ss, pn, len - 1); + ss_set_pattern_len(ss, pn, len - 1); + } return ret; } @@ -576,7 +593,7 @@ static void op_P_RM_get(const void *NOTUSED(data), scene_state_t *ss, exec_state_t *NOTUSED(es), command_state_t *cs) { int16_t pn = ss->variables.p_n; int16_t a = cs_pop(cs); - cs_push(cs, p_rm_get(ss, pn, a - 1)); // a is 1-indexed + cs_push(cs, p_rm_get(ss, pn, a)); tele_pattern_updated(); } @@ -605,6 +622,8 @@ static void p_push_get(scene_state_t *ss, int16_t pn, int16_t val) { ss_set_pattern_val(ss, pn, len, val); ss_set_pattern_len(ss, pn, len + 1); } + + tele_pattern_updated(); } static void op_P_PUSH_get(const void *NOTUSED(data), scene_state_t *ss, @@ -612,7 +631,6 @@ static void op_P_PUSH_get(const void *NOTUSED(data), scene_state_t *ss, int16_t pn = ss->variables.p_n; int16_t a = cs_pop(cs); p_push_get(ss, pn, a); - tele_pattern_updated(); } static void op_PN_PUSH_get(const void *NOTUSED(data), scene_state_t *ss, @@ -620,7 +638,6 @@ static void op_PN_PUSH_get(const void *NOTUSED(data), scene_state_t *ss, int16_t pn = cs_pop(cs); int16_t a = cs_pop(cs); p_push_get(ss, pn, a); - tele_pattern_updated(); } // Make ops @@ -658,3 +675,227 @@ static void op_PN_POP_get(const void *NOTUSED(data), scene_state_t *ss, // Make ops const tele_op_t op_P_POP = MAKE_GET_OP(P.POP, op_P_POP_get, 0, true); const tele_op_t op_PN_POP = MAKE_GET_OP(PN.POP, op_PN_POP_get, 1, true); + + +//////////////////////////////////////////////////////////////////////////////// +// P.MIN /////////////////////////////////////////////////////////////////////// + +// Get +static int16_t p_min_get(scene_state_t *ss, int16_t pn) { + pn = normalise_pn(pn); + + int16_t start = ss_get_pattern_start(ss, pn); + int16_t end = ss_get_pattern_end(ss, pn); + + int16_t pos = start; + int16_t val = ss_get_pattern_val(ss, pn, pos); + int16_t temp = 0; + + for (int16_t i = start + 1; i <= end; i++) { + temp = ss_get_pattern_val(ss, pn, i); + if (temp < val) { + pos = i; + val = temp; + } + } + + return pos; +} + +static void op_P_MIN_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + cs_push(cs, p_min_get(ss, ss->variables.p_n)); +} + +static void op_PN_MIN_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + int16_t pn = cs_pop(cs); + cs_push(cs, p_min_get(ss, pn)); +} + +// Make ops +const tele_op_t op_P_MIN = MAKE_GET_OP(P.MIN, op_P_MIN_get, 0, true); +const tele_op_t op_PN_MIN = MAKE_GET_OP(PN.MIN, op_PN_MIN_get, 1, true); + + +//////////////////////////////////////////////////////////////////////////////// +// P.MAX /////////////////////////////////////////////////////////////////////// + +// Get +static int16_t p_max_get(scene_state_t *ss, int16_t pn) { + pn = normalise_pn(pn); + + int16_t start = ss_get_pattern_start(ss, pn); + int16_t end = ss_get_pattern_end(ss, pn); + + int16_t pos = start; + int16_t val = ss_get_pattern_val(ss, pn, pos); + int16_t temp = 0; + + for (int16_t i = start + 1; i <= end; i++) { + temp = ss_get_pattern_val(ss, pn, i); + if (temp > val) { + pos = i; + val = temp; + } + } + + return pos; +} + +static void op_P_MAX_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + cs_push(cs, p_max_get(ss, ss->variables.p_n)); +} + +static void op_PN_MAX_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + int16_t pn = cs_pop(cs); + cs_push(cs, p_max_get(ss, pn)); +} + +// Make ops +const tele_op_t op_P_MAX = MAKE_GET_OP(P.MAX, op_P_MAX_get, 0, true); +const tele_op_t op_PN_MAX = MAKE_GET_OP(PN.MAX, op_PN_MAX_get, 1, true); + +//////////////////////////////////////////////////////////////////////////////// +// P.RND /////////////////////////////////////////////////////////////////////// + +static int16_t p_rnd_get(scene_state_t *ss, int16_t pn) { + pn = normalise_pn(pn); + int16_t start = ss_get_pattern_start(ss, pn); + int16_t end = ss_get_pattern_end(ss, pn); + if (end < start) return 0; + return ss_get_pattern_val(ss, pn, rand() % (end - start + 1) + start); +} + +static void op_P_RND_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + cs_push(cs, p_rnd_get(ss, ss->variables.p_n)); +} + +static void op_PN_RND_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + int16_t pn = cs_pop(cs); + cs_push(cs, p_rnd_get(ss, pn)); +} + +// Make ops +const tele_op_t op_P_RND = MAKE_GET_OP(P.RND, op_P_RND_get, 0, true); +const tele_op_t op_PN_RND = MAKE_GET_OP(PN.RND, op_PN_RND_get, 1, true); + +//////////////////////////////////////////////////////////////////////////////// +// P.+ P.+W //////////////////////////////////////////////////////////////////// + +static void p_add_get(scene_state_t *ss, int16_t pn, int16_t idx, int16_t delta, + uint8_t wrap_value, int16_t min, int16_t max) { + pn = normalise_pn(pn); + idx = normalise_idx(ss, pn, idx); + int16_t value = ss_get_pattern_val(ss, pn, idx) + delta; + if (wrap_value) value = wrap(value, min, max); + ss_set_pattern_val(ss, pn, idx, value); +} + +static void op_P_ADD_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + int16_t idx = cs_pop(cs); + int16_t delta = cs_pop(cs); + p_add_get(ss, ss->variables.p_n, idx, delta, 0, 0, 0); + tele_pattern_updated(); +} + +static void op_PN_ADD_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + int16_t pn = cs_pop(cs); + int16_t idx = cs_pop(cs); + int16_t delta = cs_pop(cs); + p_add_get(ss, pn, idx, delta, 0, 0, 0); + tele_pattern_updated(); +} + +static void op_P_ADDW_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + int16_t idx = cs_pop(cs); + int16_t delta = cs_pop(cs); + int16_t min = cs_pop(cs); + int16_t max = cs_pop(cs); + p_add_get(ss, ss->variables.p_n, idx, delta, 1, min, max); + tele_pattern_updated(); +} + +static void op_PN_ADDW_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + int16_t pn = cs_pop(cs); + int16_t idx = cs_pop(cs); + int16_t delta = cs_pop(cs); + int16_t min = cs_pop(cs); + int16_t max = cs_pop(cs); + p_add_get(ss, pn, idx, delta, 1, min, max); + tele_pattern_updated(); +} + +// Make ops +// clang-format off +const tele_op_t op_P_ADD = MAKE_GET_OP(P.+, op_P_ADD_get, 2, false); +const tele_op_t op_PN_ADD = MAKE_GET_OP(PN.+, op_PN_ADD_get, 3, false); +const tele_op_t op_P_ADDW = MAKE_GET_OP(P.+W, op_P_ADDW_get, 4, false); +const tele_op_t op_PN_ADDW = MAKE_GET_OP(PN.+W, op_PN_ADDW_get, 5, false); +// clang-format on + +//////////////////////////////////////////////////////////////////////////////// +// P.- P.-W //////////////////////////////////////////////////////////////////// + +static void p_sub_get(scene_state_t *ss, int16_t pn, int16_t idx, int16_t delta, + uint8_t wrap_value, int16_t min, int16_t max) { + pn = normalise_pn(pn); + idx = normalise_idx(ss, pn, idx); + int16_t value = ss_get_pattern_val(ss, pn, idx) - delta; + if (wrap_value) value = wrap(value, min, max); + ss_set_pattern_val(ss, pn, idx, value); +} + +static void op_P_SUB_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + int16_t idx = cs_pop(cs); + int16_t delta = cs_pop(cs); + p_sub_get(ss, ss->variables.p_n, idx, delta, 0, 0, 0); + tele_pattern_updated(); +} + +static void op_PN_SUB_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + int16_t pn = cs_pop(cs); + int16_t idx = cs_pop(cs); + int16_t delta = cs_pop(cs); + p_sub_get(ss, pn, idx, delta, 0, 0, 0); + tele_pattern_updated(); +} + +static void op_P_SUBW_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + int16_t idx = cs_pop(cs); + int16_t delta = cs_pop(cs); + int16_t min = cs_pop(cs); + int16_t max = cs_pop(cs); + p_sub_get(ss, ss->variables.p_n, idx, delta, 1, min, max); + tele_pattern_updated(); +} + +static void op_PN_SUBW_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + int16_t pn = cs_pop(cs); + int16_t idx = cs_pop(cs); + int16_t delta = cs_pop(cs); + int16_t min = cs_pop(cs); + int16_t max = cs_pop(cs); + p_sub_get(ss, pn, idx, delta, 1, min, max); + tele_pattern_updated(); +} + +// Make ops +// clang-format off +const tele_op_t op_P_SUB = MAKE_GET_OP(P.-, op_P_SUB_get, 2, false); +const tele_op_t op_PN_SUB = MAKE_GET_OP(PN.-, op_PN_SUB_get, 3, false); +const tele_op_t op_P_SUBW = MAKE_GET_OP(P.-W, op_P_SUBW_get, 4, false); +const tele_op_t op_PN_SUBW = MAKE_GET_OP(PN.-W, op_PN_SUBW_get, 5, false); +// clang-format on diff --git a/src/ops/patterns.h b/src/ops/patterns.h index 87748884..0ab67fb3 100644 --- a/src/ops/patterns.h +++ b/src/ops/patterns.h @@ -31,5 +31,20 @@ extern const tele_op_t op_P_PUSH; extern const tele_op_t op_PN_PUSH; extern const tele_op_t op_P_POP; extern const tele_op_t op_PN_POP; +extern const tele_op_t op_P_MIN; +extern const tele_op_t op_PN_MIN; +extern const tele_op_t op_P_MAX; +extern const tele_op_t op_PN_MAX; + +extern const tele_op_t op_P_RND; +extern const tele_op_t op_PN_RND; +extern const tele_op_t op_P_ADD; +extern const tele_op_t op_PN_ADD; +extern const tele_op_t op_P_ADDW; +extern const tele_op_t op_PN_ADDW; +extern const tele_op_t op_P_SUB; +extern const tele_op_t op_PN_SUB; +extern const tele_op_t op_P_SUBW; +extern const tele_op_t op_PN_SUBW; #endif diff --git a/src/ops/telex.c b/src/ops/telex.c index 1447e014..ec4b239b 100644 --- a/src/ops/telex.c +++ b/src/ops/telex.c @@ -173,6 +173,14 @@ static void op_TO_TR_INIT_get(const void *data, scene_state_t *ss, static void op_TO_INIT_get(const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_TO_ENV_get(const void *data, scene_state_t *ss, exec_state_t *es, + command_state_t *cs); + +static void op_TO_CV_CALIB_get(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); +static void op_TO_CV_RESET_get(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); + // TXi Methods static void op_TI_PARAM_get(const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); @@ -303,6 +311,11 @@ const tele_op_t op_TO_CV_INIT = MAKE_GET_OP(TO.CV.INIT , op_TO_ const tele_op_t op_TO_TR_INIT = MAKE_GET_OP(TO.TR.INIT , op_TO_TR_INIT_get , 1, false); const tele_op_t op_TO_INIT = MAKE_GET_OP(TO.INIT , op_TO_INIT_get , 1, false); +const tele_op_t op_TO_ENV = MAKE_GET_OP(TO.ENV , op_TO_ENV_get , 2, false); + +const tele_op_t op_TO_CV_CALIB = MAKE_GET_OP(TO.CV.CALIB , op_TO_CV_CALIB_get , 1, false); +const tele_op_t op_TO_CV_RESET = MAKE_GET_OP(TO.CV.RESET , op_TO_CV_RESET_get , 1, false); + // TXo Ailiases const tele_op_t op_TO_TR_P = MAKE_ALIAS_OP(TO.TR.P , op_TO_TR_PULSE_get , NULL, 1, false); const tele_op_t op_TO_TR_P_DIV = MAKE_ALIAS_OP(TO.TR.P.DIV , op_TO_TR_PULSE_DIV_get , NULL, 2, false); @@ -341,15 +354,9 @@ const tele_op_t op_TI_PRM_INIT = MAKE_ALIAS_OP(TI.PRM.INIT , op_TI_ // clang-format on // telex helpers -void TXSend(uint8_t model, uint8_t command, uint8_t output, int16_t value, +void SendIt(uint8_t address, uint8_t command, uint8_t port, int16_t value, bool set) { - // zero-index the output - output -= 1; - // convert the output to the device and the port - uint8_t port = output & 3; - uint8_t device = output >> 2; - uint8_t address = model + device; - // init and fill the buffer (make the buffer smaller if we are not sending a + // init and fill the buffer (make the buffer smaller if we are not sending a // payload) uint8_t buffer[set ? 4 : 2]; buffer[0] = command; @@ -361,6 +368,20 @@ void TXSend(uint8_t model, uint8_t command, uint8_t output, int16_t value, } tele_ii_tx(address, buffer, set ? 4 : 2); } + +void TXSend(uint8_t model, uint8_t command, uint8_t output, int16_t value, + bool set) { + // zero-index the output + output -= 1; + // return if out of range + if (output < 0 || output > 31) return; + // convert the output to the device and the port + uint8_t port = output & 3; + uint8_t device = output >> 2; + uint8_t address = model + device; + // put the package in the i2c mail + SendIt(address, command, port, value, set); +} void TXCmd(uint8_t model, uint8_t command, uint8_t output) { TXSend(model, command, output, 0, false); } @@ -374,16 +395,7 @@ void TXDeviceSet(uint8_t model, uint8_t command, command_state_t *cs) { int16_t value = cs_pop(cs); TXSend(model, command, output, value, true); } -void TXReceive(uint8_t model, command_state_t *cs, uint8_t mode, bool shift) { - // zero-index the output - uint8_t input = cs_pop(cs) - 1; - // send the port, device and address - uint8_t port = input & 3; - uint8_t device = input >> 2; - uint8_t address = model + device; - // inputs are numbered 0-7 for each device - shift is for the second half - // mode pushes it up so it can read quantized values and note numbers - port += (shift ? 4 : 0) + (mode << 3); +void ReceiveIt(uint8_t address, uint8_t port, command_state_t *cs) { // tell the device what value you are going to query uint8_t buffer[2]; buffer[0] = port; @@ -395,6 +407,25 @@ void TXReceive(uint8_t model, command_state_t *cs, uint8_t mode, bool shift) { int16_t value = (buffer[0] << 8) + buffer[1]; cs_push(cs, value); } +void TXReceive(uint8_t model, command_state_t *cs, uint8_t mode, bool shift) { + // zero-index the output + uint8_t input = cs_pop(cs) - 1; + // return if out of range + if (input < 0 || input > 31) { + // need to put a zero on the stack for tests to pass + cs_push(cs, 0); + return; + } + // send the port, device and address + uint8_t port = input & 3; + uint8_t device = input >> 2; + uint8_t address = model + device; + // inputs are numbered 0-7 for each device - shift is for the second half + // mode pushes it up so it can read quantized values and note numbers + port += (shift ? 4 : 0) + (mode << 3); + // summon the package from the i2c mail + ReceiveIt(address, port, cs); +} uint8_t DeviceToOutput(int16_t device) { return ((device - 1) * 4) + 1; } @@ -757,6 +788,20 @@ static void op_TO_INIT_get(const void *NOTUSED(data), scene_state_t *ss, TXCmd(TO, TO_INIT, DeviceToOutput(cs_pop(cs))); } + +static void op_TO_ENV_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + TXSet(TO, TO_ENV, cs); +} +static void op_TO_CV_CALIB_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + TXCmd(TO, TO_CV_CALIB, cs_pop(cs)); +} +static void op_TO_CV_RESET_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + TXCmd(TO, TO_CV_RESET, cs_pop(cs)); +} + // TXi static void op_TI_PARAM_get(const void *NOTUSED(data), scene_state_t *ss, exec_state_t *NOTUSED(es), command_state_t *cs) { diff --git a/src/ops/telex.h b/src/ops/telex.h index 85664963..5f82a8ec 100644 --- a/src/ops/telex.h +++ b/src/ops/telex.h @@ -99,6 +99,11 @@ extern const tele_op_t op_TO_TR_P_DIV; extern const tele_op_t op_TO_TR_P_MUTE; extern const tele_op_t op_TO_TR_P_MUL; +extern const tele_op_t op_TO_ENV; + +extern const tele_op_t op_TO_CV_CALIB; +extern const tele_op_t op_TO_CV_RESET; + // TXi Operators extern const tele_op_t op_TI_PARAM; @@ -130,10 +135,13 @@ extern const tele_op_t op_TI_PRM_MAP; extern const tele_op_t op_TI_PRM_INIT; // helpers +void SendIt(uint8_t address, uint8_t command, uint8_t port, int16_t value, + bool set); void TXSend(uint8_t model, uint8_t command, uint8_t output, int16_t value, bool set); void TXCmd(uint8_t model, uint8_t command, uint8_t output); void TXSet(uint8_t model, uint8_t command, command_state_t *cs); +void ReceiveIt(uint8_t address, uint8_t port, command_state_t *cs); void TXDeviceSet(uint8_t model, uint8_t command, command_state_t *cs); void TXReceive(uint8_t model, command_state_t *cs, uint8_t mode, bool shift); uint8_t DeviceToOutput(int16_t device); @@ -194,7 +202,7 @@ void PRMInit(uint8_t input); #define TO_M_COUNT 0x1E #define TO_KILL 0x20 -#define TO_RESET 0x21 +// #define TO_RESET 0x21 #define TO_TR_INIT 0x22 #define TO_CV_INIT 0x23 #define TO_INIT 0x24 @@ -250,6 +258,11 @@ void PRMInit(uint8_t input); #define TO_ENV_EOC 0x6B #define TO_ENV_LOOP 0x6C +#define TO_ENV 0x6D + +#define TO_CV_CALIB 0x6E +#define TO_CV_RESET 0x6F + // TELEXi #define TI 0x68 diff --git a/src/ops/variables.c b/src/ops/variables.c index bf16adb1..9f899a78 100644 --- a/src/ops/variables.c +++ b/src/ops/variables.c @@ -5,6 +5,7 @@ #include "helpers.h" #include "ops/op.h" #include "teletype.h" +#include "teletype_io.h" static void op_LAST_get(const void *data, scene_state_t *ss, exec_state_t *es, @@ -25,6 +26,14 @@ static void op_I_get(const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); static void op_I_set(const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); +static void op_TIME_get(const void *data, scene_state_t *ss, exec_state_t *es, + command_state_t *cs); +static void op_TIME_set(const void *data, scene_state_t *ss, exec_state_t *es, + command_state_t *cs); +static void op_TIME_ACT_get(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); +static void op_TIME_ACT_set(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); // clang-format off const tele_op_t op_A = MAKE_SIMPLE_VARIABLE_OP(A , variables.a ); @@ -39,8 +48,8 @@ const tele_op_t op_O_MAX = MAKE_SIMPLE_VARIABLE_OP(O.MAX , variables.o_ const tele_op_t op_O_MIN = MAKE_SIMPLE_VARIABLE_OP(O.MIN , variables.o_min ); const tele_op_t op_O_WRAP = MAKE_SIMPLE_VARIABLE_OP(O.WRAP , variables.o_wrap ); const tele_op_t op_T = MAKE_SIMPLE_VARIABLE_OP(T , variables.t ); -const tele_op_t op_TIME = MAKE_SIMPLE_VARIABLE_OP(TIME , variables.time ); -const tele_op_t op_TIME_ACT = MAKE_SIMPLE_VARIABLE_OP(TIME.ACT , variables.time_act ); +const tele_op_t op_TIME = MAKE_GET_SET_OP(TIME, op_TIME_get, op_TIME_set, 0, true); +const tele_op_t op_TIME_ACT = MAKE_GET_SET_OP(TIME.ACT, op_TIME_ACT_get, op_TIME_ACT_set, 0, true); const tele_op_t op_LAST = MAKE_GET_OP(LAST , op_LAST_get, 1, true); const tele_op_t op_X = MAKE_SIMPLE_VARIABLE_OP(X , variables.x ); const tele_op_t op_Y = MAKE_SIMPLE_VARIABLE_OP(Y , variables.y ); @@ -52,6 +61,35 @@ const tele_op_t op_O = MAKE_GET_SET_OP(O , op_O_get , op_O_set , 0, const tele_op_t op_I = MAKE_GET_SET_OP(I , op_I_get, op_I_set, 0, true); // clang-format on +static void op_TIME_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + int64_t delta = ss->variables.time_act + ? tele_get_ticks() - ss->variables.time + : ss->variables.time; + cs_push(cs, delta & 0x7fff); +} + +static void op_TIME_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + int16_t new_time = cs_pop(cs); + ss->variables.time = + ss->variables.time_act ? tele_get_ticks() - new_time : new_time; +} + +static void op_TIME_ACT_get(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + cs_push(cs, ss->variables.time_act ? 1 : 0); +} + +static void op_TIME_ACT_set(const void *NOTUSED(data), scene_state_t *ss, + exec_state_t *NOTUSED(es), command_state_t *cs) { + int16_t act = cs_pop(cs); + if (act && ss->variables.time_act) return; + if (!act && !ss->variables.time_act) return; + ss->variables.time_act = act ? 1 : 0; + ss->variables.time = tele_get_ticks() - ss->variables.time; +} + static void op_LAST_get(const void *NOTUSED(data), scene_state_t *ss, exec_state_t *NOTUSED(es), command_state_t *cs) { int16_t script_number = cs_pop(cs) - 1; diff --git a/src/ops/wslash.c b/src/ops/wslash.c index 2f3cfbcf..e1311e94 100644 --- a/src/ops/wslash.c +++ b/src/ops/wslash.c @@ -4,14 +4,14 @@ #include "ii.h" #include "teletype_io.h" -static void op_WS_REC_get(const void *data, scene_state_t *ss, - exec_state_t *es, command_state_t *cs); -static void op_WS_REC_set(const void *data, scene_state_t *ss, - exec_state_t *es, command_state_t *cs); -static void op_WS_PLAY_get(const void *data, scene_state_t *ss, exec_state_t *es, - command_state_t *cs); -static void op_WS_PLAY_set(const void *data, scene_state_t *ss, exec_state_t *es, - command_state_t *cs); +static void op_WS_REC_get(const void *data, scene_state_t *ss, exec_state_t *es, + command_state_t *cs); +static void op_WS_REC_set(const void *data, scene_state_t *ss, exec_state_t *es, + command_state_t *cs); +static void op_WS_PLAY_get(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); +static void op_WS_PLAY_set(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); static void op_WS_LOOP_get(const void *data, scene_state_t *ss, exec_state_t *es, command_state_t *cs); static void op_WS_LOOP_set(const void *data, scene_state_t *ss, @@ -29,8 +29,7 @@ const tele_op_t op_WS_CUE = MAKE_GET_SET_OP(WS.CUE , op_WS_CUE_get , op_WS_CUE_ // clang-format on -static void op_WS_REC_set(const void *NOTUSED(data), - scene_state_t *NOTUSED(ss), +static void op_WS_REC_set(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), exec_state_t *NOTUSED(es), command_state_t *cs) { int16_t a = cs_pop(cs); uint8_t d[] = { WS_REC, a }; @@ -53,8 +52,9 @@ static void op_WS_PLAY_set(const void *NOTUSED(data), uint8_t d[] = { WS_PLAY, a }; tele_ii_tx(WS_ADDR, d, 2); } -static void op_WS_PLAY_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), - exec_state_t *NOTUSED(es), command_state_t *cs) { +static void op_WS_PLAY_get(const void *NOTUSED(data), + scene_state_t *NOTUSED(ss), + exec_state_t *NOTUSED(es), command_state_t *cs) { uint8_t d[] = { WS_PLAY | II_GET }; uint8_t addr = WS_ADDR; tele_ii_tx(addr, d, 1); @@ -70,8 +70,9 @@ static void op_WS_LOOP_set(const void *NOTUSED(data), uint8_t d[] = { WS_LOOP, a }; tele_ii_tx(WS_ADDR, d, 2); } -static void op_WS_LOOP_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), - exec_state_t *NOTUSED(es), command_state_t *cs) { +static void op_WS_LOOP_get(const void *NOTUSED(data), + scene_state_t *NOTUSED(ss), + exec_state_t *NOTUSED(es), command_state_t *cs) { uint8_t d[] = { WS_LOOP | II_GET }; uint8_t addr = WS_ADDR; tele_ii_tx(addr, d, 1); @@ -80,8 +81,7 @@ static void op_WS_LOOP_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss) cs_push(cs, d[0]); } -static void op_WS_CUE_set(const void *NOTUSED(data), - scene_state_t *NOTUSED(ss), +static void op_WS_CUE_set(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), exec_state_t *NOTUSED(es), command_state_t *cs) { int16_t a = cs_pop(cs); uint8_t d[] = { WS_CUE, a }; diff --git a/src/state.c b/src/state.c index b06a2df9..0106e9b3 100644 --- a/src/state.c +++ b/src/state.c @@ -11,11 +11,16 @@ void ss_init(scene_state_t *ss) { ss->initializing = true; ss_variables_init(ss); ss_patterns_init(ss); + ss_grid_init(ss); ss->delay.count = 0; for (size_t i = 0; i < TR_COUNT; i++) { ss->tr_pulse_timer[i] = 0; } ss->stack_op.top = 0; memset(&ss->scripts, 0, ss_scripts_size()); turtle_init(&ss->turtle); + uint32_t ticks = tele_get_ticks(); + for (size_t i = 0; i < TEMP_SCRIPT; i++) ss->scripts[i].last_time = ticks; + ss->variables.time = 0; + ss->variables.time_act = 1; } void ss_variables_init(scene_state_t *ss) { @@ -46,6 +51,7 @@ void ss_variables_init(scene_state_t *ss) { }; memcpy(&ss->variables, &default_variables, sizeof(default_variables)); + tele_update_adc(1); ss_update_param_scale(ss); ss_update_in_scale(ss); } @@ -66,6 +72,59 @@ void ss_pattern_init(scene_state_t *ss, size_t pattern_no) { for (size_t i = 0; i < PATTERN_LENGTH; i++) { p->val[i] = 0; } } +// grid + +void ss_grid_init(scene_state_t *ss) { + ss->grid.rotate = 0; + ss->grid.dim = 0; + + ss->grid.current_group = 0; + ss->grid.latest_group = 0; + ss->grid.latest_button = 0; + ss->grid.latest_fader = 0; + + for (u8 i = 0; i < GRID_MAX_DIMENSION; i++) + for (u8 j = 0; j < GRID_MAX_DIMENSION; j++) + ss->grid.leds[i][j] = LED_OFF; + + for (u8 i = 0; i < GRID_GROUP_COUNT; i++) { + ss->grid.group[i].enabled = true; + ss->grid.group[i].script = -1; + ss->grid.group[i].fader_min = 0; + ss->grid.group[i].fader_max = 16383; + } + + for (u16 i = 0; i < GRID_BUTTON_COUNT; i++) { + ss_grid_common_init(&(ss->grid.button[i].common)); + ss->grid.button[i].latch = 0; + ss->grid.button[i].state = 0; + } + + for (u8 i = 0; i < GRID_FADER_COUNT; i++) { + ss_grid_common_init(&(ss->grid.fader[i].common)); + ss->grid.fader[i].type = FADER_CH_BAR; + ss->grid.fader[i].value = 0; + ss->grid.fader[i].slide = 0; + } + + for (u8 i = 0; i < GRID_XYPAD_COUNT; i++) { + ss_grid_common_init(&(ss->grid.xypad[i].common)); + ss->grid.xypad[i].value_x = 0; + ss->grid.xypad[i].value_y = 0; + } + + ss->grid.grid_dirty = ss->grid.scr_dirty = ss->grid.clear_held = true; +} + +void ss_grid_common_init(grid_common_t *gc) { + gc->enabled = false; + gc->group = 0; + gc->x = gc->y = 0; + gc->w = gc->h = 1; + gc->level = 5; + gc->script = -1; +} + // Hardware void ss_set_in(scene_state_t *ss, int16_t value) { @@ -168,6 +227,11 @@ const tele_command_t *ss_get_script_command(scene_state_t *ss, return &ss->scripts[script_idx].c[c_idx]; } +void ss_copy_script_command(tele_command_t *dest, scene_state_t *ss, + script_number_t script_idx, size_t c_idx) { + memcpy(dest, &ss->scripts[script_idx].c[c_idx], sizeof(tele_command_t)); +} + // private static void ss_set_script_command(scene_state_t *ss, script_number_t script_idx, size_t c_idx, const tele_command_t *cmd) { @@ -176,13 +240,18 @@ static void ss_set_script_command(scene_state_t *ss, script_number_t script_idx, bool ss_get_script_comment(scene_state_t *ss, script_number_t script_idx, size_t c_idx) { - return ss->scripts[script_idx].comment[c_idx]; + return ss->scripts[script_idx].c[c_idx].comment; +} + +void ss_set_script_comment(scene_state_t *ss, script_number_t script_idx, + size_t c_idx, uint8_t on) { + ss->scripts[script_idx].c[c_idx].comment = on; } void ss_toggle_script_comment(scene_state_t *ss, script_number_t script_idx, size_t c_idx) { - ss->scripts[script_idx].comment[c_idx] = - !ss->scripts[script_idx].comment[c_idx]; + ss->scripts[script_idx].c[c_idx].comment = + !ss->scripts[script_idx].c[c_idx].comment; } void ss_overwrite_script_command(scene_state_t *ss, script_number_t script_idx, @@ -233,9 +302,9 @@ void ss_insert_script_command(scene_state_t *ss, script_number_t script_idx, void ss_delete_script_command(scene_state_t *ss, script_number_t script_idx, size_t command_idx) { - if (command_idx >= SCRIPT_MAX_COMMANDS) return; - uint8_t script_len = ss_get_script_len(ss, script_idx); + if (command_idx >= SCRIPT_MAX_COMMANDS || command_idx >= script_len) return; + if (script_len && ss_get_script_command(ss, script_idx, command_idx)->length) { script_len--; @@ -249,6 +318,7 @@ void ss_delete_script_command(scene_state_t *ss, script_number_t script_idx, tele_command_t blank_command; blank_command.length = 0; + blank_command.comment = false; ss_set_script_command(ss, script_idx, script_len, &blank_command); } } @@ -266,17 +336,14 @@ size_t ss_scripts_size() { } int16_t ss_get_script_last(scene_state_t *ss, script_number_t idx) { - int16_t now = ss->variables.time; if (idx < TT_SCRIPT_1) return 0; if (idx > INIT_SCRIPT) return 0; - int16_t last = ss->scripts[idx].last_time; - if (now < last) - return (INT16_MAX - last) + (now - INT16_MIN); // I must be dense? - return now - last; + uint32_t delta = (tele_get_ticks() - ss->scripts[idx].last_time) & 0x7fff; + return delta; } void ss_update_script_last(scene_state_t *ss, script_number_t idx) { - ss->scripts[idx].last_time = ss->variables.time; + ss->scripts[idx].last_time = tele_get_ticks(); } every_count_t *ss_get_every(scene_state_t *ss, script_number_t idx, @@ -437,7 +504,7 @@ size_t es_push(exec_state_t *es) { if (es->exec_depth > 0) { es->variables[es->exec_depth].if_else_condition = es->variables[es->exec_depth - 1].if_else_condition; - es->variables[es->exec_depth].i = + es->variables[es->exec_depth].i = es->variables[es->exec_depth - 1].i; } else { diff --git a/src/state.h b/src/state.h index cb36196c..1dfc8294 100644 --- a/src/state.h +++ b/src/state.h @@ -9,8 +9,9 @@ #include "every.h" #include "scale.h" #include "turtle.h" +#include "types.h" -#define STACK_SIZE 8 +#define STACK_SIZE 16 #define CV_COUNT 4 #define Q_LENGTH 64 #define TR_COUNT 4 @@ -24,6 +25,27 @@ #define EXEC_DEPTH 8 #define WHILE_DEPTH 10000 +#define GRID_GROUP_COUNT 64 +#define GRID_MAX_DIMENSION 16 +#define GRID_BUTTON_COUNT 256 +#define GRID_FADER_COUNT 64 +#define GRID_XYPAD_COUNT 8 +#define LED_DIM -1 +#define LED_BRI -2 +#define LED_OFF -3 +// H - horizontal, V - vertical +// C - coarse, F - fine +// H must be even, V must be odd +#define FADER_CH_BAR 0 +#define FADER_CV_BAR 1 +#define FADER_CH_DOT 2 +#define FADER_CV_DOT 3 +#define FADER_COARSE FADER_CV_DOT +#define FADER_FH_BAR 4 +#define FADER_FV_BAR 5 +#define FADER_FH_DOT 6 +#define FADER_FV_DOT 7 + #define METRO_MIN_MS 25 #define METRO_MIN_UNSUPPORTED_MS 2 @@ -31,7 +53,7 @@ // SCENE STATE ///////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -//clang-format off +// clang-format off typedef struct { // Maintaining this order allows for efficient access to the group // WARNING: DO NOT CHANGE THE ORDER OF VARIABLES a THROUGH t @@ -68,8 +90,8 @@ typedef struct { int16_t r_min; int16_t r_max; int16_t scene; - int16_t time; - int16_t time_act; + int64_t time; + uint8_t time_act; int16_t tr[TR_COUNT]; int16_t tr_pol[TR_COUNT]; int16_t tr_time[TR_COUNT]; @@ -78,7 +100,7 @@ typedef struct { scale_data_t param_range; scale_t param_scale; } scene_variables_t; -//clang-format on +// clang-format on typedef struct { int16_t idx; @@ -106,11 +128,70 @@ typedef struct { typedef struct { uint8_t l; tele_command_t c[SCRIPT_MAX_COMMANDS]; - bool comment[SCRIPT_MAX_COMMANDS]; every_count_t every[SCRIPT_MAX_COMMANDS]; - int16_t last_time; + uint32_t last_time; } scene_script_t; +typedef struct { + u8 enabled; + u8 group; + u8 x, y; + u8 w, h; + s16 level; + s8 script; +} grid_common_t; + +typedef struct { + u8 enabled; + s8 script; + s16 fader_min; + s16 fader_max; +} grid_group_t; + +typedef struct { + grid_common_t common; + u8 latch; + u8 state; +} grid_button_t; + +typedef struct { + grid_common_t common; + u8 type; + u8 value; + u8 slide; + u8 slide_acc; + u8 slide_end; + u8 slide_delta; + u8 slide_dir; +} grid_fader_t; + +typedef struct { + grid_common_t common; + u8 value_x; + u8 value_y; +} grid_xypad_t; + +typedef struct { + u8 grid_dirty; + u8 scr_dirty; + u8 clear_held; + + u8 rotate; + u8 dim; + + u8 current_group; + u8 latest_group; + u8 latest_button; + u8 latest_fader; + + s8 leds[GRID_MAX_DIMENSION][GRID_MAX_DIMENSION]; + grid_group_t group[GRID_GROUP_COUNT]; + + grid_button_t button[GRID_BUTTON_COUNT]; + grid_fader_t fader[GRID_FADER_COUNT]; + grid_xypad_t xypad[GRID_XYPAD_COUNT]; +} scene_grid_t; + typedef struct { bool initializing; scene_variables_t variables; @@ -121,6 +202,7 @@ typedef struct { scene_script_t scripts[SCRIPT_COUNT]; scene_turtle_t turtle; bool every_last; + scene_grid_t grid; cal_data_t cal; } scene_state_t; @@ -128,6 +210,8 @@ extern void ss_init(scene_state_t *ss); extern void ss_variables_init(scene_state_t *ss); extern void ss_patterns_init(scene_state_t *ss); extern void ss_pattern_init(scene_state_t *ss, size_t pattern_no); +extern void ss_grid_init(scene_state_t *ss); +extern void ss_grid_common_init(grid_common_t *gc); extern void ss_set_in(scene_state_t *ss, int16_t value); extern void ss_set_param(scene_state_t *ss, int16_t value); @@ -159,8 +243,12 @@ uint8_t ss_get_script_len(scene_state_t *ss, script_number_t idx); const tele_command_t *ss_get_script_command(scene_state_t *ss, script_number_t script_idx, size_t c_idx); +void ss_copy_script_command(tele_command_t *dest, scene_state_t *ss, + script_number_t script_idx, size_t c_idx); bool ss_get_script_comment(scene_state_t *ss, script_number_t script_idx, size_t c_idx); +void ss_set_script_comment(scene_state_t *ss, script_number_t script_idx, + size_t c_idx, uint8_t on); void ss_toggle_script_comment(scene_state_t *ss, script_number_t script_idx, size_t c_idx); void ss_overwrite_script_command(scene_state_t *ss, script_number_t script_idx, @@ -243,9 +331,7 @@ typedef struct { int16_t top; } command_state_stack_t; -typedef struct { - command_state_stack_t stack; -} command_state_t; +typedef struct { command_state_stack_t stack; } command_state_t; extern void cs_init(command_state_t *cs); extern int16_t cs_stack_size(command_state_t *cs); diff --git a/src/teletype.c b/src/teletype.c index 9af16f8a..d52ca3be 100644 --- a/src/teletype.c +++ b/src/teletype.c @@ -147,7 +147,7 @@ process_result_t run_script_with_exec_state(scene_state_t *ss, exec_state_t *es, #ifdef TELETYPE_PROFILE tele_profile_script(script_no); #endif - process_result_t result = { .has_value = false, .value = 0 }; + process_result_t result = {.has_value = false, .value = 0 }; es_set_script_number(es, script_no); @@ -274,6 +274,7 @@ process_result_t process_command(scene_state_t *ss, exec_state_t *es, } else if (word_type == MOD) { tele_command_t post_command; + post_command.comment = false; copy_post_command(&post_command, c); tele_mods[word_value]->func(ss, es, &cs, &post_command); } @@ -284,11 +285,11 @@ process_result_t process_command(scene_state_t *ss, exec_state_t *es, // --------- // sometimes we have single value left of the stack, if so return it if (cs_stack_size(&cs)) { - process_result_t o = { .has_value = true, .value = cs_pop(&cs) }; + process_result_t o = {.has_value = true, .value = cs_pop(&cs) }; return o; } else { - process_result_t o = { .has_value = false, .value = 0 }; + process_result_t o = {.has_value = false, .value = 0 }; return o; } } @@ -298,10 +299,6 @@ process_result_t process_command(scene_state_t *ss, exec_state_t *es, // TICK ///////////////////////////////////////////////////////// void tele_tick(scene_state_t *ss, uint8_t time) { - // time is the basic resolution of all code henceforth called - // hardware 2.0: get an RTC! - if (ss->variables.time_act) ss->variables.time += time; - // could be a while() if there is reason to expect a user to cascade moves // with SCRIPTs without the tick delay if (ss->turtle.stepped && ss->turtle.script_number != TEMP_SCRIPT) { diff --git a/src/teletype_io.h b/src/teletype_io.h index 1eef8295..3574cc23 100644 --- a/src/teletype_io.h +++ b/src/teletype_io.h @@ -7,6 +7,9 @@ // These functions are for interacting with the teletype hardware, each target // must provide it's own implementation +// used for TIME and LAST +extern uint32_t tele_get_ticks(void); + // called when M or M.ACT are updated extern void tele_metro_updated(void); @@ -17,7 +20,7 @@ extern void tele_tr(uint8_t i, int16_t v); extern void tele_cv(uint8_t i, int16_t v, uint8_t s); extern void tele_cv_slew(uint8_t i, int16_t v); -extern void tele_update_in(void); +extern void tele_update_adc(uint8_t force); // inform target if there are delays extern void tele_has_delays(bool has_delays); @@ -46,4 +49,7 @@ void tele_profile_script(size_t); void tele_profile_delay(uint8_t); #endif +// emulate grid key press +extern void grid_key_press(uint8_t x, uint8_t y, uint8_t z); + #endif diff --git a/src/turtle.c b/src/turtle.c index 30c3a972..28c35d57 100644 --- a/src/turtle.c +++ b/src/turtle.c @@ -4,21 +4,19 @@ #define max(X, Y) ((X) > (Y) ? (X) : (Y)) void turtle_init(scene_turtle_t *st) { - scene_turtle_t t = { .fence = { .x1 = 0, .y1 = 0, .x2 = 3, .y2 = 63 }, - .mode = TURTLE_BUMP, - .heading = 180, - .speed = 100, - .stepped = false, - .script_number = TEMP_SCRIPT }; + scene_turtle_t t = {.fence = {.x1 = 0, .y1 = 0, .x2 = 3, .y2 = 63 }, + .mode = TURTLE_BUMP, + .heading = 180, + .speed = 100, + .stepped = false, + .script_number = TEMP_SCRIPT }; memcpy(st, &t, sizeof(t)); turtle_set_x(st, 0); turtle_set_y(st, 0); st->last = st->position; } -typedef struct { - QT x1, y1, x2, y2; -} Q_fence_t; +typedef struct { QT x1, y1, x2, y2; } Q_fence_t; static inline Q_fence_t normalize_fence(turtle_fence_t in, turtle_mode_t mode) { Q_fence_t out; diff --git a/tests/Makefile b/tests/Makefile index 9fe0308a..7e763ec6 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -10,12 +10,15 @@ tests: main.o \ ../src/every.o ../src/match_token.o ../src/scanner.o \ ../src/state.o ../src/table.o ../src/turtle.o ../src/chaos.o \ ../src/ops/op.o ../src/ops/ansible.c ../src/ops/controlflow.o \ - ../src/ops/delay.o ../src/ops/earthsea.o ../src/ops/hardware.o \ + ../src/ops/delay.o ../src/ops/earthsea.o \ + ../src/ops/er301.o ../src/ops/fader.o \ + ../src/ops/hardware.o \ ../src/ops/justfriends.o ../src/ops/meadowphysics.o \ ../src/ops/metronome.o ../src/ops/maths.o ../src/ops/orca.o \ ../src/ops/patterns.o ../src/ops/queue.o ../src/ops/stack.o \ - ../src/ops/telex.o ../src/ops/variables.o ../src/ops/whitewhale.c \ - ../src/ops/wslash.o ../src/ops/turtle.o ../src/ops/init.o \ + ../src/ops/telex.o ../src/ops/variables.o ../src/ops/whitewhale.o \ + ../src/ops/turtle.o ../src/ops/init.o ../src/ops/grid_ops.o \ + ../src/ops/matrixarchate.o ../src/ops/wslash.o \ ../libavr32/src/euclidean/data.o ../libavr32/src/euclidean/euclidean.o \ ../libavr32/src/util.o $(CC) -o $@ $^ $(CFLAGS) diff --git a/tests/main.c b/tests/main.c index dc9c82cf..7ed472ef 100644 --- a/tests/main.c +++ b/tests/main.c @@ -11,12 +11,15 @@ #include "process_tests.h" #include "turtle_tests.h" +uint32_t tele_get_ticks() { + return 0; +} void tele_metro_updated() {} void tele_metro_reset() {} void tele_tr(uint8_t i, int16_t v) {} void tele_cv(uint8_t i, int16_t v, uint8_t s) {} void tele_cv_slew(uint8_t i, int16_t v) {} -void tele_update_in(void) {} +void tele_update_adc(uint8_t force) {} void tele_has_delays(bool i) {} void tele_has_stack(bool i) {} void tele_cv_off(uint8_t i, int16_t v) {} @@ -32,8 +35,11 @@ void tele_profile_delay(uint8_t d) {} bool tele_get_input_state(uint8_t n) { return false; } + void tele_save_calibration() {} +void grid_key_press(uint8_t x, uint8_t y, uint8_t z) {} + GREATEST_MAIN_DEFS(); int main(int argc, char **argv) { diff --git a/tests/op_mod_tests.c b/tests/op_mod_tests.c index c69cb2a8..fdf76f77 100644 --- a/tests/op_mod_tests.c +++ b/tests/op_mod_tests.c @@ -109,10 +109,10 @@ TEST mod_stack_size() { for (int j = 0; j < mod->params + stack_extra; j++) cs_push(&cs, 0); // execute func - const tele_command_t sub_command = { .length = 1, - .separator = 0, - .data = { { .tag = OP, - .value = E_OP_A } } }; + const tele_command_t sub_command = {.length = 1, + .separator = 0, + .data = { {.tag = OP, + .value = E_OP_A } } }; mod->func(&ss, &es, &cs, &sub_command); // check that the stack has the correct number of items in it diff --git a/tests/process_tests.c b/tests/process_tests.c index 3125aa8d..5f86210a 100644 --- a/tests/process_tests.c +++ b/tests/process_tests.c @@ -11,7 +11,7 @@ // correct (allows contiuation of state) TEST process_helper_state(scene_state_t* ss, size_t n, char* lines[], int16_t answer) { - process_result_t result = { .has_value = false, .value = 0 }; + process_result_t result = {.has_value = false, .value = 0 }; exec_state_t es; es_init(&es); es_push(&es); diff --git a/tests/turtle_tests.c b/tests/turtle_tests.c index cba7a621..088b0461 100644 --- a/tests/turtle_tests.c +++ b/tests/turtle_tests.c @@ -34,7 +34,7 @@ static const char *error_message(error_t e) { TEST process_helper_state(scene_state_t *ss, size_t n, char *lines[], int16_t answer) { count++; - process_result_t result = { .has_value = false, .value = 0 }; + process_result_t result = {.has_value = false, .value = 0 }; exec_state_t es; memset(&es, 0, sizeof(es)); es_init(&es); @@ -216,7 +216,7 @@ TEST test_turtle_bounce() { char *test10b[4] = { "@BOUNCE 1", "@F 0 0 1 1", "@STEP", "@DIR" }; CHECK_CALL(process_helper(4, test10b, 180)); - // The following tests reveal the charade that is the length between fences +// The following tests reveal the charade that is the length between fences #if 0 char *test10c[4] = { "@BOUNCE 1", "@F 0 0 1 1", "L 1 2: @STEP", "@DIR" }; CHECK_CALL(process_helper(4, test10c, 0)); diff --git a/utils/cheatsheet.py b/utils/cheatsheet.py index abd3cdd4..af12e13e 100755 --- a/utils/cheatsheet.py +++ b/utils/cheatsheet.py @@ -19,9 +19,9 @@ DOCS_DIR = ROOT_DIR / "docs" OP_DOCS_DIR = DOCS_DIR / "ops" FONTS_DIR = ROOT_DIR / "utils" / "fonts" -_VERSION_ = get_tt_version() -_VERSION_STR_ = "Teletype " + \ - _VERSION_["tag"] + " " + _VERSION_["hash"] + " Cheatsheet" +TT_VERSION = get_tt_version() +VERSION_STR = " ".join(["Teletype", TT_VERSION["tag"], TT_VERSION["hash"], + "Documentation"]) # We want to run inject_latex in parallel as it's quite slow, and we must run @@ -45,7 +45,8 @@ def inject_latex(value): lstrip_blocks=True ) -# determines the order in which sections are displayed +# determines the order in which sections are displayed, +# final column indicates that a new page is inserted _after_ that section OPS_SECTIONS = [ ("variables", "Variables", False), ("hardware", "Hardware", False), @@ -57,13 +58,17 @@ def inject_latex(value): ("stack", "Stack", False), ("queue", "Queue", False), ("turtle", "Turtle", True), + ("grid", "Grid", True), ("ansible", "Ansible", False), ("whitewhale", "Whitewhale", False), ("meadowphysics", "Meadowphysics", False), ("earthsea", "Earthsea", False), - ("orca", "Orca", False), - ("justfriends", "Just Friends", True), - ("wslash", "W/", True), + ("orca", "Orca", True), + ("justfriends", "Just Friends", False), + ("wslash", "W/", False), + ("er301", "ER-301", False), + ("fader", "Fader", False), + ("matrixarchate", "Matrixarchate", True), ("telex_i", "TELEXi", False), ("telex_o", "TELEXo", False) ] @@ -71,9 +76,11 @@ def inject_latex(value): def latex_safe(s): # backslash must be first, otherwise it will duplicate itself - unsafe = ["\\", "&", "%", "$", "#", "_", "{", "}", "~", "^"] + unsafe = ["\\", "&", "%", "$", "#", "_", "{", "}", "^"] for u in unsafe: s = s.replace(u, "\\" + u) + # ~ is special + s = s.replace("~", "\\~{}") return s @@ -82,18 +89,18 @@ def cheatsheet_tex(): print(f"Using ops docs directory: {OP_DOCS_DIR}") print() - output = _VERSION_STR_ + "\n\n" + output = VERSION_STR + "\n\n" for (section, title, new_page) in OPS_SECTIONS: toml_file = Path(OP_DOCS_DIR, section + ".toml") if toml_file.exists() and toml_file.is_file(): - output += f"\group{{{ title }}}\n\n" + output += f"\\group{{{ title }}}\n\n" print(f"Reading {toml_file}") # n.b. Python 3.6 dicts maintain insertion order ops = toml.loads(toml_file.read_text()) validate_toml(ops) ops_array = pool.map(inject_latex, ops.values()) for op in ops_array: - prototype = op["prototype"] + prototype = latex_safe(op["prototype"]) if "prototype_set" in op: prototype += " / " + op["prototype_set"] output += "\\begin{op}" @@ -105,7 +112,7 @@ def cheatsheet_tex(): output += "\\end{op}" output += "\n\n" if new_page: - output += "\pagebreak\n\n" + output += "\\pagebreak\n\n" return output diff --git a/utils/common/__init__.py b/utils/common/__init__.py index b7415447..40e7454e 100644 --- a/utils/common/__init__.py +++ b/utils/common/__init__.py @@ -54,6 +54,7 @@ def _convert_struct_name_to_op_name(name): "SYM_STAR": "*", "SYM_FORWARD_SLASH": "/", "SYM_PERCENTAGE": "%", + "SYM_DOLLAR": "$", "SYM_EQUAL_x2": "==", "SYM_EXCLAMATION_EQUAL": "!=", "SYM_LEFT_ANGLED": "<", @@ -65,6 +66,10 @@ def _convert_struct_name_to_op_name(name): "SYM_RIGHT_ANGLED_x2": ">>", "SYM_AMPERSAND_x2": "&&", "SYM_PIPE_x2": "||", + "BIT_OR": "|", + "BIT_AND": "&", + "BIT_NOT": "~", + "BIT_XOR": "^", "M_SYM_EXCLAMATION": "M!", "TURTLE": "@", "TURTLE_X": "@X", diff --git a/utils/docs.py b/utils/docs.py index 98544747..9ad5eddb 100755 --- a/utils/docs.py +++ b/utils/docs.py @@ -6,7 +6,6 @@ import jinja2 import pypandoc import pytoml as toml -import subprocess from common import list_ops, list_mods, validate_toml, get_tt_version @@ -19,8 +18,9 @@ DOCS_DIR = ROOT_DIR / "docs" OP_DOCS_DIR = DOCS_DIR / "ops" FONTS_DIR = ROOT_DIR / "utils" / "fonts" -_VERSION_ = get_tt_version() -_VERSION_STR_ = "Teletype " + _VERSION_["tag"] + " " + _VERSION_["hash"] + " Documentation" +TT_VERSION = get_tt_version() +VERSION_STR = " ".join(["Teletype", TT_VERSION["tag"], TT_VERSION["hash"], + "Documentation"]) env = jinja2.Environment( autoescape=False, @@ -43,6 +43,7 @@ "stack", "queue", "turtle", + "grid", "ansible", "whitewhale", "meadowphysics", @@ -50,7 +51,11 @@ "orca", "justfriends", "telex_i", - "telex_o" + "telex_o", + "er301", + "fader", + "wslash", + "matrixarchate" ] @@ -66,6 +71,7 @@ def deep_merge_dict(source, destination): def common_md(): + print(f"Pandoc version: {pypandoc.get_pandoc_version()}") print(f"Using docs directory: {DOCS_DIR}") print(f"Using ops docs directory: {OP_DOCS_DIR}") print() @@ -74,8 +80,8 @@ def common_md(): op_extended_template = env.get_template("op_extended.jinja2.md") output = "" - output += Path(DOCS_DIR / "intro.md").read_text() \ - .replace("VERSION", _VERSION_["tag"][1:]) + "\n\n" + output += Path(DOCS_DIR / "intro.md") \ + .read_text().replace("VERSION", TT_VERSION["tag"][1:]) + "\n\n" output += Path(DOCS_DIR / "whats_new.md").read_text() + "\n\n" output += Path(DOCS_DIR / "quickstart.md").read_text() + "\n\n" output += Path(DOCS_DIR / "keys.md").read_text() + "\n\n" @@ -153,7 +159,7 @@ def main(): if ext == ".md": p.write_text(output) elif ext == ".html": - output = "# " + _VERSION_STR_ + "\n\n" + output + output = "# " + VERSION_STR + "\n\n" + output pypandoc.convert_text( output, format=input_format, @@ -164,12 +170,17 @@ def main(): "--toc", "--toc-depth=2", "--css=" + str(TEMPLATE_DIR / "docs.css"), - "--template=" + str(TEMPLATE_DIR / "template.html5")]) + "--template=" + str(TEMPLATE_DIR / + "template.html5")]) elif ext == ".pdf" or ext == ".tex": latex_preamble = env.get_template("latex_preamble.jinja2.md") latex = latex_preamble \ - .render(title=_VERSION_STR_, fonts_dir=FONTS_DIR) + "\n\n" + .render(title=VERSION_STR, fonts_dir=FONTS_DIR) + "\n\n" latex += output + pandoc_version = int(pypandoc.get_pandoc_version()[0]) + engine = ("--pdf-engine=xelatex" + if pandoc_version >= 2 + else "--latex-engine=xelatex") pypandoc.convert_text( latex, format=input_format, @@ -179,7 +190,7 @@ def main(): "--column=80", "--toc", "--toc-depth=2", - "--latex-engine=xelatex", + engine, "--variable=papersize:A4"]) diff --git a/utils/templates/latex_preamble.jinja2.md b/utils/templates/latex_preamble.jinja2.md index 6b71b0c6..9c285203 100644 --- a/utils/templates/latex_preamble.jinja2.md +++ b/utils/templates/latex_preamble.jinja2.md @@ -21,6 +21,7 @@ header-includes: - \usepackage{titlesec} - \titleformat{\chapter}{\normalfont\LARGE\bfseries}{\thechapter.}{1em}{} - \titlespacing*{\chapter}{0pt}{3.5ex plus 1ex minus .2ex}{2.3ex plus .2ex} +- \usepackage{etoolbox} - \AtBeginEnvironment{longtable}{\small}{}{} - \renewcommand\arraystretch{1.3} ---