├── .gitignore ├── DESIGN.md ├── LICENSE.md ├── README.md ├── TUTORIAL.md ├── desktop ├── core │ ├── library.js │ ├── library │ │ ├── _bang.js │ │ ├── _comment.js │ │ ├── _midi.js │ │ ├── _null.js │ │ ├── _osc.js │ │ ├── _udp.js │ │ ├── a.js │ │ ├── b.js │ │ ├── c.js │ │ ├── d.js │ │ ├── e.js │ │ ├── f.js │ │ ├── g.js │ │ ├── h.js │ │ ├── i.js │ │ ├── j.js │ │ ├── k.js │ │ ├── l.js │ │ ├── m.js │ │ ├── n.js │ │ ├── o.js │ │ ├── p.js │ │ ├── q.js │ │ ├── r.js │ │ ├── s.js │ │ ├── t.js │ │ ├── u.js │ │ ├── v.js │ │ ├── w.js │ │ ├── x.js │ │ ├── y.js │ │ └── z.js │ ├── operator.js │ └── orca.js ├── icon.icns ├── icon.ico ├── icon.png ├── icon.svg ├── main.js ├── package-lock.json ├── package.json └── sources │ ├── index.html │ ├── links │ ├── fonts.css │ ├── main.css │ ├── reset.css │ └── theme.css │ ├── media │ └── fonts │ │ ├── input_mono_medium.ttf │ │ └── input_mono_regular.ttf │ └── scripts │ ├── cursor.js │ ├── events.js │ ├── history.js │ ├── io.js │ ├── io.midi.js │ ├── io.osc-standalone.js │ ├── io.osc.js │ ├── io.udp.js │ ├── keyboard.js │ ├── lib │ ├── controller.js │ └── theme.js │ ├── source.js │ └── terminal.js ├── docs.js ├── examples ├── _midi.orca ├── _osc.orca ├── _udp.orca ├── bang.orca ├── benchmark.orca ├── boolean.orca ├── cycle.orca ├── delay.orca ├── if+else.orca ├── increment.orca ├── kombine.orca ├── octave.orca ├── pendulum.orca ├── popcorn.orca ├── projects │ ├── dotgrid.orca │ ├── midi-step-sequencer.orca │ └── sonicpi.orca ├── read+write.orca ├── subtraction.orca ├── timing.orca ├── triage.orca ├── variable.orca └── wave.orca ├── listener.js └── resources ├── glyph.grid ├── glyph.png ├── glyph.svg ├── logo.grid ├── logo.png ├── logo.svg ├── preview.hardware.jpg └── preview.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | builds/ 3 | .DS_Store 4 | */.DS_Store 5 | -------------------------------------------------------------------------------- /DESIGN.md: -------------------------------------------------------------------------------- 1 | # Design 2 | 3 | | Families | Glyphs 4 | | ---------- | ----------- 5 | | Directions | N S E W Z 6 | | Math | A F I M R 7 | | Writers | G P X 8 | | Readers | O Q T 9 | | Jumpers | J Y 10 | | Timers | C D 11 | | Variables | K V 12 | | Misc | B H L U 13 | | Special | * # ; : = 14 | 15 | ## Unstable Glyphs 16 | 17 | - `V` could be made into two special glyphs, one for reading and one for writing. 18 | 19 | ## Attributes 20 | 21 | | ID# | Glyph | Rating | Stable | Outputs 22 | | --- | ----- | ------ | ------ | ------- 23 | | 10 | A | +++++ | X | Any 24 | | 11 | B | ++++ | X | * 25 | | 12 | C | +++++ | X | Any 26 | | 13 | D | +++++ | X | * 27 | | 14 | E | +++++ | X | 28 | | 15 | F | +++++ | X | * 29 | | 16 | G | +++++ | X | Any - Multi - Unlocked 30 | | 17 | H | +++++ | X | Any 31 | | 18 | I | +++++ | X | Any 32 | | 19 | J | +++++ | X | Any 33 | | 20 | K | ++++ | | 34 | | 21 | L | ++ | | 35 | | 22 | M | +++++ | X | Any 36 | | 23 | N | +++++ | X | 37 | | 24 | O | +++++ | X | Any 38 | | 25 | P | +++++ | X | Any - Unlocked 39 | | 26 | Q | +++++ | X | Any - Multi 40 | | 27 | R | +++++ | X | Any 41 | | 28 | S | +++++ | X | 42 | | 29 | T | +++++ | X | Any 43 | | 30 | U | +++++ | X | 44 | | 31 | V | ++++ | | Any 45 | | 32 | W | +++++ | X | 46 | | 33 | X | +++++ | X | Any - Unlocked 47 | | 34 | Y | +++++ | X | Any 48 | | 35 | Z | +++ | X | 49 | | 36 | * | +++++ | X | 50 | | 37 | # | +++++ | X | 51 | | 39 | : | +++++ | X | 52 | | 38 | ; | +++++ | X | 53 | | 40 | = | +++++ | X | 54 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Hundredrabbits 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ORCΛ 2 | 3 | 4 | 5 | **Each letter of the alphabet is an operation**, lowercase letters operate on bang(`*`), uppercase letters operate each frame. Have a look at some project created with [#ORCΛ](https://twitter.com/hashtag/ORCΛ), or some [example files](https://github.com/hundredrabbits/Orca/tree/master/examples). Here's an [introduction video](https://www.youtube.com/watch?v=RaI_TuISSJE). You can see the [design notes](DESIGN.md) for specs and upcoming features. If you need some help, visit the [chatroom](https://talk.lurk.org/channel/orca). 6 | 7 | For a portable version of Orca, built entirely in C, visit [Orca-c](http://github.com/hundredrabbits/Orca-c). 8 | 9 | ## Install & Run 10 | 11 | You can download [builds](https://hundredrabbits.itch.io/orca) for **OSX, Windows and Linux**, or if you wish to build it yourself, follow these steps: 12 | 13 | ``` 14 | git clone https://github.com/hundredrabbits/Orca.git 15 | cd Orca/desktop/ 16 | npm install 17 | npm start 18 | ``` 19 | 20 | 21 | 22 | ## Operators 23 | 24 | - `A` **add**(a, b): Outputs the sum of inputs. 25 | - `B` **bool**(val): Bangs if input is not empty, or 0. 26 | - `C` **clock**('rate, mod): Outputs a constant value based on the runtime frame. 27 | - `D` **delay**('rate, mod): Bangs on a fraction of the runtime frame. 28 | - `E` **east**: Moves eastward, or bangs. 29 | - `F` **if**(a, b): Bangs if both inputs are equal. 30 | - `G` **generator**('x, 'y, 'len): Writes distant operators with offset. 31 | - `H` **halt**: Stops southward operators from operating. 32 | - `I` **increment**(min, max): Increments southward operator. 33 | - `J` **jumper**(val): Outputs the northward operator. 34 | - `K` **konkat**('len): Outputs multiple variables. 35 | - `L` **loop**('len): Loops a number of eastward operators. 36 | - `M` **modulo**(val, mod): Outputs the modulo of input. 37 | - `N` **north**: Moves Northward, or bangs. 38 | - `O` **offset**('x, 'y, val): Reads a distant operator with offset. 39 | - `P` **push**('len, 'key, val): Writes an eastward operator with offset. 40 | - `Q` **query**('x, 'y, 'len): Reads distant operators with offset. 41 | - `R` **random**(min, max): Outputs a random value. 42 | - `S` **south**: Moves southward, or bangs. 43 | - `T` **track**('len, 'key, val): Reads an eastward operator with offset. 44 | - `U` **uturn**('n, 'e, 's, 'w): Reverses movement of inputs. 45 | - `V` **variable**('write, read): Reads and write globally available variables. 46 | - `W` **west**: Moves westward, or bangs. 47 | - `X` **teleport**('x, 'y, val): Writes a distant operator with offset. 48 | - `Y` **jymper**(val): Outputs the westward operator. 49 | - `Z` **zoom**: Moves eastwardly, respawns west on collision. 50 | - `*` **bang**: Bangs neighboring operators. 51 | - `#` **comment**: Comments a line, or characters until the next hash. 52 | - `:` **midi**('channel, 'octave, 'note, velocity, length): Sends a MIDI note. 53 | - `;` **udp**: Sends a UDP message. 54 | - `=` **osc**(path): Sends a OSC message. 55 | 56 | ## Controls 57 | 58 | ### Terminal Controls 59 | 60 | - `enter` toggle insert/write. 61 | - `space` toggle play/pause. 62 | - `>` increase BPM. 63 | - `<` decrease BPM. 64 | - `shift+arrowKey` Expand cursor. 65 | - `ctrl+arrowKey` Leap cursor. 66 | - `alt+arrowKey` Move selection. 67 | 68 | ### Edit 69 | 70 | - `ctrl+c` copy selection. 71 | - `ctrl+x` cut selection. 72 | - `ctrl+v` paste selection. 73 | - `ctrl+z` undo. 74 | - `ctrl+shift+z` redo. 75 | 76 | ### Grid Controls 77 | 78 | - `]` increase grid size vertically. 79 | - `[` decrease grid size vertically. 80 | - `}` increase grid size horizontally. 81 | - `{` decrease grid size horizontally. 82 | - `ctrl/meta+]` increase program size vertically. 83 | - `ctrl/meta+[` decrease program size vertically. 84 | - `ctrl/meta+}` increase program size horizontally. 85 | - `ctrl/meta+{` decrease program size horizontally. 86 | 87 | ### Window 88 | 89 | - `ctrl+=` Zoom In. 90 | - `ctrl+-` Zoom Out. 91 | - `ctrl+0` Zoom Reset. 92 | - `tab` Toggle interface. 93 | - `backquote` Toggle background. 94 | 95 | To open the console, press `ctrl+.`. 96 | 97 | ## MIDI 98 | 99 | The [MIDI](https://en.wikipedia.org/wiki/MIDI) operator `:` takes up to 5 inputs('channel, 'octave, 'note, velocity, length). 100 | 101 | For example, `:25C`, is a **C note, on the 5th octave, through the 3rd MIDI channel**, `:04c`, is a **C# note, on the 4th octave, through the 1st MIDI channel**. Velocity is an optional value from `0`(0/127) to `f`(127/127). Note length is a value from `0`(1/16) to `f`(16/16), which is a ratio of a full bar, *f* being `16/16`(a full bar), *8* being `1/2`(half), *4* being `1/4`(quarter). See it in action with [midi.orca](https://github.com/hundredrabbits/Orca/blob/master/examples/_midi.orca). 102 | 103 | #### List Midi Devices 104 | 105 | In console, type `terminal.io.midi.list()` to see the list of available midi devices. 106 | 107 | #### Select Midi Device 108 | 109 | In console, type `terminal.io.midi.select(1)` to select the second midi device. 110 | 111 | ## UDP 112 | 113 | The [UDP](https://nodejs.org/api/dgram.html#dgram_socket_send_msg_offset_length_port_address_callback) operator `;` locks each consecutive eastwardly ports. For example, `;hello`, will send the string "hello", on bang, to the port `49160` on `localhost` 114 | 115 | You can use the [listener.js](https://github.com/hundredrabbits/Orca/blob/master/listener.js) to test UDP messages. See it in action with [udp.orca](https://github.com/hundredrabbits/Orca/blob/master/examples/_udp.orca). 116 | 117 | #### Select UDP Port 118 | 119 | In console, type `terminal.io.udp.select(49160)` to select the **49160** udp port. 120 | 121 | ## OSC 122 | 123 | The [OSC](https://github.com/MylesBorins/node-osc) operator `=` locks each consecutive eastwardly ports. 124 | 125 | First char is used for path, nexts are sent as integers using [base36 Table](https://github.com/hundredrabbits/Orca#base36-table). For example, `=1abc` will send `10`, `11` and `12` to `/1`, via the port `49162` on `localhost`; `=a123` will send `1`, `2` and `3`, to the path `/a`. You can use the [listener.js](https://github.com/hundredrabbits/Orca/blob/master/examples/listener.js) to test OSC messages. See it in action with [osc.orca](https://github.com/hundredrabbits/Orca/blob/master/examples/_osc.orca) or try it with [SonicPi](https://github.com/hundredrabbits/Orca/blob/master/TUTORIAL.md#sonicpi). 126 | 127 | #### Select OSC Port 128 | 129 | In console, type `terminal.io.osc.select(49162)` to select the **49162** osc port. 130 | 131 | 132 | 133 | ## Base36 Table 134 | 135 | Orca operates on a base of 36 increments. Operators using numeric values will typically also operate on letters and convert them into values as per the following table. For instance `Dp` will bang every *24th frame*. 136 | 137 | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | 138 | | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | 139 | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 140 | | **C** | **D** | **E** | **F** | **G** | **H** | **I** | **J** | **K** | **L** | **M** | **N** | 141 | | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 142 | | **O** | **P** | **Q** | **R** | **S** | **T** | **U** | **V** | **W** | **X** | **Y** | **Z** | 143 | | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 144 | 145 | ## Extras 146 | 147 | - This application supports the [Ecosystem Theme](https://github.com/hundredrabbits/Themes). 148 | - Support this project through [Patreon](https://patreon.com/100). 149 | - See the [License](LICENSE.md) file for license rights and limitations (MIT). 150 | - Pull Requests are welcome! 151 | -------------------------------------------------------------------------------- /TUTORIAL.md: -------------------------------------------------------------------------------- 1 | # Tutorial 2 | 3 | If this is your first time hearing about Orca, watch this [introduction video](https://www.youtube.com/watch?v=RaI_TuISSJE). If you are on Windows, use something like [loopMidi](http://www.tobias-erichsen.de/software/loopmidi.html) to help routing midi signal across applications. 4 | 5 | | Families | Glyphs 6 | | ---------- | ----------- 7 | | Directions | N S E W Z 8 | | [Math](https://www.youtube.com/watch?v=CR1TMGYhCoE) | A F I M R 9 | | Writers | G P X 10 | | Readers | O Q T 11 | | [Jumpers](https://www.youtube.com/watch?v=CR1TMGYhCoE) | J Y 12 | | Timers | C D 13 | | Variables | K V 14 | | Misc | B H L U 15 | | Special | * # ; : = 16 | 17 | ## SonicPi 18 | 19 | To send [OSC messages](https://github.com/hundredrabbits/Orca#osc) to [SonicPi](http://sonic-pi.net), select [port 4559](https://github.com/hundredrabbits/Orca#osc). SonicPi listens to the address defined in `sync`, to send to the `live_loop`, bang the OSC node `=`, like `=a`. Have a look at [sonicpi.orca](https://github.com/hundredrabbits/Orca/blob/master/examples/projects/sonicpi.orca) to see it in action. 20 | 21 | ``` 22 | live_loop :drum do 23 | use_real_time 24 | sync "/osc/a" 25 | sample :bd_haus, rate: 1 26 | end 27 | ``` 28 | 29 | ## Dotgrid 30 | 31 | To send [UDP messages](https://github.com/hundredrabbits/Orca#udp) to [Dotgrid](http://github.com/hundredrabbits/Dotgrid), select [port 49160](https://github.com/hundredrabbits/Orca#udp). To draw lines on Dotgrid, you need to bang the UDP node `;` with different [commands](https://github.com/hundredrabbits/Dotgrid/blob/master/desktop/sources/scripts/listener.js). Have a look at [dotgrid.orca](https://github.com/hundredrabbits/Orca/blob/master/examples/projects/dotgrid.orca) to see it in action. 32 | 33 | - `;0`, clear layer **#1**. 34 | - `;0l1234`, add a line from `1,2` to `3,4`. 35 | - `;`, redraw. 36 | 37 | Here's a list of supported operations. 38 | 39 | ``` 40 | ;0 // Clear Layer 1 41 | ;0l1234 // Add Line from 1,2 to 3,4 42 | ;0c1234 // Add Clockwise Arc from 1,2 to 3,4 43 | ;0r1234 // Add Reverse Arc from 1,2 to 3,4 44 | ; // Redraw 45 | ``` 46 | 47 | ## Ableton Live 48 | 49 | To send [Midi notes](https://github.com/hundredrabbits/Orca#midi) to [Ableton Live](https://www.ableton.com/en/) instruments, bang the Midi node `:`, like `:03C` to send to _Channel 1, Octave 3, Note C_. Have a look at [midi.orca](https://github.com/hundredrabbits/Orca/blob/master/examples/_midi.orca) to see it in action. 50 | 51 | - Launch Ableton Live. 52 | - Create a new midi instrument track. 53 | - Select `IAC Driver(Bus 1)`(OSX), or `LoopMidi`(Windows), in the instrument's inputs dropdown. 54 | - Activate the **In** toggle. 55 | 56 | The midi instrument should begin receiving midi notes as soon as the Orca window is **in focus**. 57 | 58 | # Patterns 59 | 60 | Here's a collection of recurring patterns in the design of Orca machines. 61 | 62 | ## J Funnel 63 | 64 | Move two horizontal values next to each other. 65 | 66 | ``` 67 | .1Y12. 68 | ...JJ. 69 | ..A12. 70 | ..3... 71 | ``` 72 | 73 | ## X Stack 74 | 75 | Move two vertical values next to each other. 76 | 77 | ``` 78 | .21X1.. 79 | .10X2.. 80 | ...A21. 81 | ...3... 82 | ``` 83 | 84 | ## Y Projector 85 | 86 | A very simple projector using a yumper. 87 | 88 | ``` 89 | ..D4.. 90 | .H*... 91 | .Ey.E. 92 | ...... 93 | ``` 94 | -------------------------------------------------------------------------------- /desktop/core/library.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | '0': require('./library/_null'), 5 | '1': require('./library/_null'), 6 | '2': require('./library/_null'), 7 | '3': require('./library/_null'), 8 | '4': require('./library/_null'), 9 | '5': require('./library/_null'), 10 | '6': require('./library/_null'), 11 | '7': require('./library/_null'), 12 | '8': require('./library/_null'), 13 | '9': require('./library/_null'), 14 | 'a': require('./library/a'), 15 | 'b': require('./library/b'), 16 | 'c': require('./library/c'), 17 | 'd': require('./library/d'), 18 | 'e': require('./library/e'), 19 | 'f': require('./library/f'), 20 | 'g': require('./library/g'), 21 | 'h': require('./library/h'), 22 | 'i': require('./library/i'), 23 | 'j': require('./library/j'), 24 | 'k': require('./library/k'), 25 | 'l': require('./library/l'), 26 | 'm': require('./library/m'), 27 | 'n': require('./library/n'), 28 | 'o': require('./library/o'), 29 | 'p': require('./library/p'), 30 | 'q': require('./library/q'), 31 | 'r': require('./library/r'), 32 | 's': require('./library/s'), 33 | 't': require('./library/t'), 34 | 'u': require('./library/u'), 35 | 'v': require('./library/v'), 36 | 'w': require('./library/w'), 37 | 'x': require('./library/x'), 38 | 'y': require('./library/y'), 39 | 'z': require('./library/z'), 40 | '*': require('./library/_bang'), 41 | '#': require('./library/_comment'), 42 | ':': require('./library/_midi'), 43 | ';': require('./library/_udp'), 44 | '=': require('./library/_osc') 45 | } 46 | -------------------------------------------------------------------------------- /desktop/core/library/_bang.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorBang (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, '*', true) 7 | 8 | this.name = 'bang' 9 | this.info = 'Bangs neighboring operators.' 10 | this.draw = false 11 | 12 | this.haste = function () { 13 | this.passive = true 14 | this.erase() 15 | } 16 | } 17 | 18 | module.exports = OperatorBang 19 | -------------------------------------------------------------------------------- /desktop/core/library/_comment.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorComment (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, '#', true) 7 | 8 | this.name = 'comment' 9 | this.info = 'Comments a line, or characters until the next hash.' 10 | this.draw = false 11 | 12 | this.haste = function () { 13 | for (let x = this.x + 1; x <= orca.w; x++) { 14 | orca.lock(x, this.y) 15 | if (orca.glyphAt(x, this.y) === this.glyph) { break } 16 | } 17 | this.passive = false 18 | this.lock() 19 | } 20 | } 21 | 22 | module.exports = OperatorComment 23 | -------------------------------------------------------------------------------- /desktop/core/library/_midi.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorMidi (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, ':', true) 7 | 8 | this.name = 'midi' 9 | this.info = 'Sends a MIDI note.' 10 | 11 | this.ports.haste.channel = { x: 1, y: 0 } 12 | this.ports.haste.octave = { x: 2, y: 0 } 13 | this.ports.haste.note = { x: 3, y: 0 } 14 | this.ports.input.velocity = { x: 4, y: 0 } 15 | this.ports.input.length = { x: 5, y: 0 } 16 | 17 | this.run = function () { 18 | if (!this.bang()) { return } 19 | 20 | let rawChannel = this.listen(this.ports.haste.channel) 21 | let rawOctave = this.listen(this.ports.haste.octave, true) 22 | let rawNote = this.listen(this.ports.haste.note) 23 | let rawVelocity = this.listen(this.ports.input.velocity) 24 | let rawLength = this.listen(this.ports.input.length) 25 | 26 | if (rawChannel === '.' || orca.valueOf(rawChannel) > 15 || rawOctave === 0 || rawOctave > 8 || rawNote === '.' || rawVelocity === '0') { return } 27 | 28 | // 0 - 16 29 | const channel = clamp(orca.valueOf(rawChannel), 0, 15) 30 | // 1 - 9 31 | const octave = clamp(rawNote === 'b' ? rawOctave + 1 : rawOctave, 1, 9) 32 | // 0 - 11 33 | const note = ['C', 'c', 'D', 'd', 'E', 'F', 'f', 'G', 'g', 'A', 'a', 'B'].indexOf(rawNote === 'e' ? 'F' : rawNote === 'b' ? 'C' : rawNote) 34 | // 0 - F(127) 35 | const velocity = rawVelocity === '.' ? 127 : parseInt((clamp(orca.valueOf(rawVelocity), 0, 15) / 15) * 127) 36 | // 0 - F(15) 37 | const length = clamp(orca.valueOf(rawLength), 1, 15) 38 | 39 | if (note < 0) { console.warn(`Unknown note:${rawNote}`); return } 40 | 41 | this.draw = false 42 | 43 | terminal.io.midi.send(channel, octave, note, velocity, length) 44 | } 45 | 46 | function clamp (v, min, max) { return v < min ? min : v > max ? max : v } 47 | } 48 | 49 | module.exports = OperatorMidi 50 | -------------------------------------------------------------------------------- /desktop/core/library/_null.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorNull (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, '.', passive) 7 | 8 | this.name = 'null' 9 | this.info = 'empty' 10 | } 11 | 12 | module.exports = OperatorNull 13 | -------------------------------------------------------------------------------- /desktop/core/library/_osc.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorOsc (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, '=', true) 7 | 8 | this.name = 'osc' 9 | this.info = 'Sends a OSC message.' 10 | 11 | this.ports.input.path = { x: 1, y: 0 } 12 | 13 | this.haste = function () { 14 | this.path = this.listen(this.ports.input.path) 15 | this.msg = '' 16 | for (let x = 2; x <= 36; x++) { 17 | const g = orca.glyphAt(this.x + x, this.y) 18 | if (g === '.') { break } 19 | orca.lock(this.x + x, this.y) 20 | this.msg += g 21 | } 22 | } 23 | 24 | this.run = function () { 25 | if (!this.path || this.path === '' || !this.bang()) { return } 26 | this.draw = false 27 | terminal.io.osc.send('/' + this.path, this.msg) 28 | } 29 | } 30 | 31 | module.exports = OperatorOsc 32 | -------------------------------------------------------------------------------- /desktop/core/library/_udp.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorUdp (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, ';', true) 7 | 8 | this.name = 'udp' 9 | this.info = 'Sends a UDP message.' 10 | 11 | this.haste = function () { 12 | this.msg = '' 13 | for (let x = 1; x <= 36; x++) { 14 | const g = orca.glyphAt(this.x + x, this.y) 15 | if (g === '.') { break } 16 | orca.lock(this.x + x, this.y) 17 | this.msg += g 18 | } 19 | } 20 | 21 | this.run = function () { 22 | if (!this.bang()) { return } 23 | this.draw = false 24 | terminal.io.udp.send(this.msg) 25 | } 26 | } 27 | 28 | module.exports = OperatorUdp 29 | -------------------------------------------------------------------------------- /desktop/core/library/a.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorA (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, 'a', passive) 7 | 8 | this.name = 'add' 9 | this.info = 'Outputs the sum of inputs.' 10 | 11 | this.ports.input.a = { x: 1, y: 0 } 12 | this.ports.input.b = { x: 2, y: 0 } 13 | this.ports.output = { x: 0, y: 1 } 14 | 15 | this.run = function () { 16 | const a = this.listen(this.ports.input.a, true) 17 | const b = this.listen(this.ports.input.b, true) 18 | const res = orca.keyOf(a + b) 19 | this.output(`${res}`) 20 | } 21 | } 22 | 23 | module.exports = OperatorA 24 | -------------------------------------------------------------------------------- /desktop/core/library/b.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorB (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, 'b', passive) 7 | 8 | this.name = 'bool' 9 | this.info = 'Bangs if input is not empty, or 0.' 10 | 11 | this.ports.input.val = { x: 1, y: 0, unlock: true } 12 | this.ports.output = { x: 0, y: 1 } 13 | 14 | this.run = function () { 15 | const val = this.listen(this.ports.input.val) 16 | const res = val !== '.' && val !== '0' ? '*' : '.' 17 | this.output(`${res}`) 18 | } 19 | } 20 | 21 | module.exports = OperatorB 22 | -------------------------------------------------------------------------------- /desktop/core/library/c.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorC (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, 'c', passive) 7 | 8 | this.name = 'clock' 9 | this.info = 'Outputs a constant value based on the runtime frame.' 10 | 11 | this.ports.haste.rate = { x: -1, y: 0 } 12 | this.ports.input.mod = { x: 1, y: 0 } 13 | this.ports.output = { x: 0, y: 1 } 14 | 15 | this.run = function () { 16 | const mod = this.listen(this.ports.input.mod, true) 17 | const rate = this.listen(this.ports.haste.rate, true, 1) 18 | const val = (Math.floor(orca.f / rate) % (mod || 10)) 19 | const res = orca.keyOf(val) 20 | this.output(`${res}`) 21 | } 22 | } 23 | 24 | module.exports = OperatorC 25 | -------------------------------------------------------------------------------- /desktop/core/library/d.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorD (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, 'd', passive) 7 | 8 | this.name = 'delay' 9 | this.info = 'Bangs on a fraction of the runtime frame.' 10 | 11 | this.ports.haste.rate = { x: -1, y: 0 } 12 | this.ports.input.mod = { x: 1, y: 0 } 13 | this.ports.output = { x: 0, y: 1 } 14 | 15 | this.run = function () { 16 | const mod = this.listen(this.ports.input.mod, true) 17 | const rate = this.listen(this.ports.haste.rate, true, 1) 18 | const val = (Math.floor(orca.f) % ((mod || 10) * rate)) 19 | const res = val === 0 ? '*' : '.' 20 | this.output(`${res}`) 21 | } 22 | } 23 | 24 | module.exports = OperatorD 25 | -------------------------------------------------------------------------------- /desktop/core/library/e.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorE (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, 'e', passive) 7 | 8 | this.name = 'east' 9 | this.info = 'Moves eastward, or bangs.' 10 | this.draw = false 11 | 12 | this.haste = function () { 13 | this.move(1, 0) 14 | this.passive = false 15 | } 16 | } 17 | 18 | module.exports = OperatorE 19 | -------------------------------------------------------------------------------- /desktop/core/library/f.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorF (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, 'f', passive) 7 | 8 | this.name = 'if' 9 | this.info = 'Bangs if both inputs are equal.' 10 | 11 | this.ports.input.a = { x: 1, y: 0 } 12 | this.ports.input.b = { x: 2, y: 0 } 13 | this.ports.output = { x: 0, y: 1 } 14 | 15 | this.run = function () { 16 | const a = this.listen(this.ports.input.a) 17 | const b = this.listen(this.ports.input.b) 18 | const res = a === b ? '*' : '.' 19 | this.output(`${res}`) 20 | } 21 | } 22 | 23 | module.exports = OperatorF 24 | -------------------------------------------------------------------------------- /desktop/core/library/g.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorG (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, 'g', passive) 7 | 8 | this.name = 'generator' 9 | this.info = 'Writes distant operators with offset.' 10 | 11 | this.ports.haste.x = { x: -3, y: 0 } 12 | this.ports.haste.y = { x: -2, y: 0 } 13 | this.ports.haste.len = { x: -1, y: 0 } 14 | 15 | this.haste = function () { 16 | this.ports.input = [] 17 | this.len = this.listen(this.ports.haste.len, true) 18 | if (this.len < 1) { return } 19 | const x = this.listen(this.ports.haste.x, true) 20 | const y = this.listen(this.ports.haste.y, true) + 1 21 | this.ports.output = { x: x, y: y, unlock: true } 22 | for (let i = 0; i < this.len; i++) { 23 | this.ports.input.push({ x: i + 1, y: 0 }) 24 | } 25 | } 26 | 27 | this.run = function () { 28 | // Read 29 | let str = '' 30 | for (const id in this.ports.input) { 31 | str += this.listen(this.ports.input[id]) 32 | } 33 | // Write 34 | for (let i = 0; i < str.length; i++) { 35 | orca.write(this.x + this.ports.output.x + i, this.y + this.ports.output.y, str[i]) 36 | } 37 | } 38 | } 39 | 40 | module.exports = OperatorG 41 | -------------------------------------------------------------------------------- /desktop/core/library/h.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorH (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, 'h', passive) 7 | 8 | this.name = 'halt' 9 | this.info = 'Stops southward operators from operating.' 10 | 11 | this.ports.output = { x: 0, y: 1 } 12 | 13 | this.haste = function () { 14 | orca.lock(this.x + this.ports.output.x, this.y + this.ports.output.y) 15 | } 16 | } 17 | 18 | module.exports = OperatorH 19 | -------------------------------------------------------------------------------- /desktop/core/library/i.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorI (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, 'i', passive) 7 | 8 | this.name = 'increment' 9 | this.info = 'Increments southward operator.' 10 | 11 | this.ports.input.min = { x: 1, y: 0 } 12 | this.ports.input.max = { x: 2, y: 0 } 13 | this.ports.output = { x: 0, y: 1 } 14 | 15 | this.run = function () { 16 | const min = this.listen(this.ports.input.min, true) 17 | const max = this.listen(this.ports.input.max, true) 18 | const val = this.listen(this.ports.output, true) 19 | 20 | if (min === max) { return } 21 | 22 | const real = { min: min < max ? min : max, max: min > max ? min : max } 23 | const next = val + (min < max ? 1 : -1) 24 | const res = next >= real.max ? real.min : next < real.min ? real.max - 1 : next 25 | 26 | this.output(`${orca.keyOf(res)}`) 27 | } 28 | } 29 | 30 | module.exports = OperatorI 31 | -------------------------------------------------------------------------------- /desktop/core/library/j.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorJ (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, 'j', passive) 7 | 8 | this.name = 'jumper' 9 | this.info = 'Outputs the northward operator.' 10 | 11 | this.ports.input.val = { x: 0, y: -1 } 12 | this.ports.output = { x: 0, y: 1 } 13 | 14 | this.haste = function () { 15 | orca.lock(this.x, this.y + 1) 16 | } 17 | 18 | this.run = function () { 19 | const val = this.listen(this.ports.input.val) 20 | this.output(val) 21 | } 22 | } 23 | 24 | module.exports = OperatorJ 25 | -------------------------------------------------------------------------------- /desktop/core/library/k.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorK (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, 'k', passive) 7 | 8 | this.name = 'konkat' 9 | this.info = 'Outputs multiple variables.' 10 | 11 | this.ports.haste.len = { x: -1, y: 0 } 12 | 13 | this.haste = function () { 14 | this.len = this.listen(this.ports.haste.len, true) 15 | for (let x = 1; x <= this.len; x++) { 16 | orca.lock(this.x + x, this.y) 17 | const g = orca.glyphAt(this.x + x, this.y) 18 | if (g !== '.') { 19 | orca.lock(this.x + x, this.y + 1) 20 | } 21 | } 22 | } 23 | 24 | this.run = function () { 25 | const a = [] 26 | for (let x = 1; x <= this.len; x++) { 27 | const g = orca.glyphAt(this.x + x, this.y) 28 | orca.write(this.x + x, this.y + 1, query(g)) 29 | } 30 | } 31 | 32 | function query (name) { 33 | if (name === '.') { return } 34 | for (const id in orca.runtime) { 35 | const operator = orca.runtime[id] 36 | if (orca.lockAt(operator.x, operator.y)) { continue } 37 | if (operator.glyph !== 'v' && operator.glyph !== 'V') { continue } 38 | if (operator.storage.write !== name) { continue } 39 | return operator.storage.read 40 | } 41 | return '.' 42 | } 43 | } 44 | 45 | module.exports = OperatorK 46 | -------------------------------------------------------------------------------- /desktop/core/library/l.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorL (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, 'l', passive) 7 | 8 | this.name = 'loop' 9 | this.info = 'Loops a number of eastward operators.' 10 | 11 | this.ports.haste.len = { x: -1, y: 0 } 12 | 13 | this.haste = function () { 14 | this.len = this.listen(this.ports.haste.len, true) 15 | for (let x = 1; x <= this.len; x++) { 16 | orca.lock(this.x + x, this.y) 17 | } 18 | } 19 | 20 | this.run = function () { 21 | if (!this.len || this.len < 1) { return } 22 | const a = [] 23 | for (let x = 1; x <= this.len; x++) { 24 | a.push(orca.glyphAt(this.x + x, this.y)) 25 | } 26 | a.push(a.shift()) 27 | for (const id in a) { 28 | const x = parseInt(id) + 1 29 | orca.write(this.x + x, this.y, a[id]) 30 | } 31 | } 32 | } 33 | 34 | module.exports = OperatorL 35 | -------------------------------------------------------------------------------- /desktop/core/library/m.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorM (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, 'm', passive) 7 | 8 | this.name = 'modulo' 9 | this.info = 'Outputs the modulo of input.' 10 | 11 | this.ports.input.val = { x: 1, y: 0 } 12 | this.ports.input.mod = { x: 2, y: 0 } 13 | this.ports.output = { x: 0, y: 1 } 14 | 15 | this.run = function () { 16 | const val = this.listen(this.ports.input.val, true) 17 | const mod = this.listen(this.ports.input.mod, true) 18 | const key = val % (mod !== 0 ? mod : 1) 19 | const res = orca.keyOf(key) 20 | this.output(`${res}`) 21 | } 22 | } 23 | 24 | module.exports = OperatorM 25 | -------------------------------------------------------------------------------- /desktop/core/library/n.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorN (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, 'n', passive) 7 | 8 | this.name = 'north' 9 | this.info = 'Moves Northward, or bangs.' 10 | this.draw = false 11 | 12 | this.haste = function () { 13 | this.move(0, -1) 14 | this.passive = false 15 | } 16 | } 17 | 18 | module.exports = OperatorN 19 | -------------------------------------------------------------------------------- /desktop/core/library/o.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorO (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, 'o', passive) 7 | 8 | this.name = 'offset' 9 | this.info = 'Reads a distant operator with offset.' 10 | 11 | this.ports.haste.x = { x: -2, y: 0 } 12 | this.ports.haste.y = { x: -1, y: 0 } 13 | this.ports.input.val = { x: 1, y: 0 } 14 | this.ports.output = { x: 0, y: 1 } 15 | 16 | this.haste = function () { 17 | const x = this.listen(this.ports.haste.x, true) + 1 18 | const y = this.listen(this.ports.haste.y, true) 19 | this.ports.input.val = { x: x, y: y } 20 | } 21 | 22 | this.run = function () { 23 | const res = this.listen(this.ports.input.val) 24 | this.output(`${res}`) 25 | } 26 | } 27 | 28 | module.exports = OperatorO 29 | -------------------------------------------------------------------------------- /desktop/core/library/p.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorP (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, 'p', passive) 7 | 8 | this.name = 'push' 9 | this.info = 'Writes an eastward operator with offset.' 10 | 11 | this.ports.haste.len = { x: -1, y: 0 } 12 | this.ports.haste.key = { x: -2, y: 0 } 13 | this.ports.input.val = { x: 1, y: 0 } 14 | 15 | this.haste = function () { 16 | this.len = this.listen(this.ports.haste.len, true) 17 | if (this.len < 1) { return } 18 | this.key = this.listen(this.ports.haste.key, true) 19 | this.ports.output = { x: (this.key % this.len), y: 1, unlock: true } 20 | for (let x = 0; x < this.len; x++) { 21 | orca.lock(this.x + x, this.y + 1) 22 | } 23 | } 24 | 25 | this.run = function () { 26 | const res = this.listen(this.ports.input.val) 27 | this.output(`${res}`) 28 | } 29 | } 30 | 31 | module.exports = OperatorP 32 | -------------------------------------------------------------------------------- /desktop/core/library/q.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorQ (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, 'q', passive) 7 | 8 | this.name = 'query' 9 | this.info = 'Reads distant operators with offset.' 10 | 11 | this.ports.haste.x = { x: -3, y: 0 } 12 | this.ports.haste.y = { x: -2, y: 0 } 13 | this.ports.haste.len = { x: -1, y: 0 } 14 | 15 | this.haste = function () { 16 | this.ports.input = [] 17 | this.len = this.listen(this.ports.haste.len, true) 18 | if (this.len < 1) { return } 19 | const x = this.listen(this.ports.haste.x, true) 20 | const y = this.listen(this.ports.haste.y, true) 21 | this.ports.output = { x: 0, y: 1 } 22 | for (let i = 1; i <= this.len; i++) { 23 | this.ports.input.push({ x: i + x, y: y }) 24 | orca.lock(this.x + this.ports.output.x + i - this.len, this.y + 1) 25 | } 26 | } 27 | 28 | this.run = function () { 29 | // Read 30 | let str = '' 31 | for (const id in this.ports.input) { 32 | str += this.listen(this.ports.input[id]) 33 | } 34 | // Write 35 | for (let i = 0; i < str.length; i++) { 36 | orca.write(this.x + this.ports.output.x + i - str.length + 1, this.y + this.ports.output.y, str[i]) 37 | } 38 | } 39 | } 40 | 41 | module.exports = OperatorQ 42 | -------------------------------------------------------------------------------- /desktop/core/library/r.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorR (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, 'r', passive) 7 | 8 | this.name = 'random' 9 | this.info = 'Outputs a random value.' 10 | 11 | this.ports.input.min = { x: 1, y: 0 } 12 | this.ports.input.max = { x: 2, y: 0 } 13 | this.ports.output = { x: 0, y: 1 } 14 | 15 | this.run = function () { 16 | const min = this.listen(this.ports.input.min, true) 17 | const max = this.listen(this.ports.input.max, true) 18 | const val = parseInt((Math.random() * (max - min)) + min) 19 | const res = orca.keyOf(val) 20 | this.output(`${res}`) 21 | } 22 | } 23 | 24 | module.exports = OperatorR 25 | -------------------------------------------------------------------------------- /desktop/core/library/s.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorS (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, 's', passive) 7 | 8 | this.name = 'south' 9 | this.info = 'Moves southward, or bangs.' 10 | this.draw = false 11 | 12 | this.haste = function () { 13 | this.move(0, 1) 14 | this.passive = false 15 | } 16 | } 17 | 18 | module.exports = OperatorS 19 | -------------------------------------------------------------------------------- /desktop/core/library/t.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorT (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, 't', passive) 7 | 8 | this.name = 'track' 9 | this.info = 'Reads an eastward operator with offset.' 10 | 11 | this.ports.input.val = { x: 1, y: 0 } 12 | this.ports.haste.len = { x: -1, y: 0 } 13 | this.ports.haste.key = { x: -2, y: 0 } 14 | 15 | this.haste = function () { 16 | this.len = this.listen(this.ports.haste.len, true) 17 | if (this.len < 1) { return } 18 | this.ports.output = { x: 0, y: 1 } 19 | this.key = this.listen(this.ports.haste.key, true) 20 | for (let x = 1; x <= this.len; x++) { 21 | orca.lock(this.x + x, this.y) 22 | } 23 | this.ports.input.val = { x: (this.key % this.len) + 1, y: 0 } 24 | } 25 | 26 | this.run = function () { 27 | const res = this.listen(this.ports.input.val) 28 | this.output(`${res}`) 29 | } 30 | } 31 | 32 | module.exports = OperatorT 33 | -------------------------------------------------------------------------------- /desktop/core/library/u.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorU (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, 'u', passive) 7 | 8 | this.name = 'uturn' 9 | this.info = 'Reverses movement of inputs.' 10 | 11 | this.ports.haste.n = { x: 0, y: -1, unlock: true } 12 | this.ports.haste.e = { x: 1, y: 0, unlock: true } 13 | this.ports.haste.s = { x: 0, y: 1, unlock: true } 14 | this.ports.haste.w = { x: -1, y: 0, unlock: true } 15 | 16 | this.run = function () { 17 | for (const id in this.ports.haste) { 18 | this.flip(id, this.ports.haste[id]) 19 | } 20 | } 21 | 22 | this.flip = function (id, vector) { 23 | const pos = { x: this.x + vector.x, y: this.y + vector.y } 24 | const g = orca.glyphAt(pos.x, pos.y).toLowerCase() 25 | 26 | if (g === '.') { return } 27 | if (!this.ports.haste[g]) { return } 28 | 29 | orca.write(pos.x, pos.y, id.toUpperCase()) 30 | } 31 | } 32 | 33 | module.exports = OperatorU 34 | -------------------------------------------------------------------------------- /desktop/core/library/v.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | // TODO 5 | function OperatorV (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, 'v', passive) 7 | 8 | this.name = 'variable' 9 | this.info = 'Reads and write globally available variables.' 10 | 11 | this.ports.haste.write = { x: -1, y: 0 } 12 | this.ports.input.read = { x: 1, y: 0 } 13 | this.ports.output = { x: 0, y: 1 } 14 | 15 | this.storage = { write: null, read: null } 16 | 17 | this.haste = function () { 18 | this.storage.write = this.listen(this.ports.haste.write) 19 | this.storage.read = this.listen(this.ports.input.read) 20 | 21 | if (this.storage.write !== '.' && this.storage.read !== '.') { 22 | this.ports.output = null 23 | } 24 | } 25 | 26 | this.run = function () { 27 | if (this.storage.write !== '.' || this.storage.read === '.') { return } 28 | this.output(query(this.storage.read)) 29 | } 30 | 31 | function query (name) { 32 | if (name === '.') { return '.' } 33 | for (const id in orca.runtime) { 34 | const operator = orca.runtime[id] 35 | if (orca.lockAt(operator.x, operator.y)) { continue } 36 | if (operator.glyph !== 'v' && operator.glyph !== 'V') { continue } 37 | if (operator.storage.write !== name) { continue } 38 | return operator.storage.read 39 | } 40 | return '.' 41 | } 42 | } 43 | 44 | module.exports = OperatorV 45 | -------------------------------------------------------------------------------- /desktop/core/library/w.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorW (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, 'w', passive) 7 | 8 | this.name = 'west' 9 | this.info = 'Moves westward, or bangs.' 10 | this.draw = false 11 | 12 | this.haste = function () { 13 | this.move(-1, 0) 14 | this.passive = false 15 | } 16 | } 17 | 18 | module.exports = OperatorW 19 | -------------------------------------------------------------------------------- /desktop/core/library/x.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorX (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, 'x', passive) 7 | 8 | this.name = 'teleport' 9 | this.info = 'Writes a distant operator with offset.' 10 | 11 | this.ports.haste.x = { x: -2, y: 0 } 12 | this.ports.haste.y = { x: -1, y: 0 } 13 | this.ports.input.val = { x: 1, y: 0 } 14 | this.ports.output = { x: 0, y: 1, unlock: true } 15 | 16 | this.haste = function () { 17 | const x = this.listen(this.ports.haste.x, true) 18 | const y = this.listen(this.ports.haste.y, true) + 1 19 | this.ports.output = { x: x, y: y, unlock: true } 20 | } 21 | 22 | this.run = function () { 23 | const res = this.listen(this.ports.input.val) 24 | this.output(`${res}`) 25 | } 26 | } 27 | 28 | module.exports = OperatorX 29 | -------------------------------------------------------------------------------- /desktop/core/library/y.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorY (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, 'y', passive) 7 | 8 | this.name = 'jymper' 9 | this.info = 'Outputs the westward operator.' 10 | 11 | this.ports.input.val = { x: -1, y: 0 } 12 | this.ports.output = { x: 1, y: 0 } 13 | 14 | this.haste = function () { 15 | orca.lock(this.x + 1, this.y) 16 | } 17 | 18 | this.run = function () { 19 | const val = this.listen(this.ports.input.val) 20 | this.output(val) 21 | } 22 | } 23 | 24 | module.exports = OperatorY 25 | -------------------------------------------------------------------------------- /desktop/core/library/z.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Operator = require('../operator') 4 | 5 | function OperatorZ (orca, x, y, passive) { 6 | Operator.call(this, orca, x, y, 'z', passive) 7 | 8 | this.name = 'zoom' 9 | this.info = 'Moves eastwardly, respawns west on collision.' 10 | this.draw = false 11 | 12 | this.haste = function () { 13 | if (orca.glyphAt(this.x + 1, this.y) === '.') { this.move(1, 0); return } 14 | for (var x = this.x; x >= 0; x--) { 15 | const g = orca.glyphAt(x - 1, this.y) 16 | if (g === '.' && x !== 0) { continue } 17 | orca.write(x, this.y, 'Z') 18 | this.erase() 19 | break 20 | } 21 | } 22 | } 23 | 24 | module.exports = OperatorZ 25 | -------------------------------------------------------------------------------- /desktop/core/operator.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function Operator (orca, x, y, glyph = '.', passive = false) { 4 | this.name = 'unknown' 5 | this.x = x 6 | this.y = y 7 | this.passive = passive 8 | this.draw = true 9 | this.glyph = passive ? glyph.toUpperCase() : glyph 10 | this.info = '--' 11 | this.ports = { input: {}, haste: {}, bang: !passive } 12 | 13 | // Actions 14 | 15 | this.listen = function (port, toValue = false, min = 0, max = 36) { 16 | if (!port) { return toValue ? 0 : '.' } 17 | const g = orca.glyphAt(this.x + port.x, this.y + port.y) 18 | return toValue ? clamp(orca.valueOf(g), min, max) : g 19 | } 20 | 21 | this.output = function (g) { 22 | if (!this.ports.output) { return } 23 | orca.write(this.x + this.ports.output.x, this.y + this.ports.output.y, g) 24 | } 25 | 26 | // Phases 27 | 28 | this.permissions = function () { 29 | for (const id in this.ports.haste) { 30 | const port = this.ports.haste[id] 31 | if (!port.unlock) { 32 | orca.lock(this.x + port.x, this.y + port.y) 33 | } 34 | } 35 | for (const id in this.ports.input) { 36 | const port = this.ports.input[id] 37 | if (!port.unlock) { 38 | orca.lock(this.x + port.x, this.y + port.y) 39 | } 40 | } 41 | if (this.ports.output && !this.ports.output.unlock) { 42 | orca.lock(this.x + this.ports.output.x, this.y + this.ports.output.y) 43 | } 44 | } 45 | 46 | this.haste = function () { 47 | } 48 | 49 | this.run = function () { 50 | } 51 | 52 | // Helpers 53 | 54 | this.lock = function () { 55 | orca.lock(this.x, this.y) 56 | } 57 | 58 | this.replace = function (g) { 59 | orca.write(this.x, this.y, g) 60 | } 61 | 62 | this.erase = function () { 63 | this.replace('.') 64 | } 65 | 66 | this.explode = function () { 67 | this.replace('*') 68 | this.lock() 69 | } 70 | 71 | this.move = function (x, y) { 72 | const offset = { x: this.x + x, y: this.y + y } 73 | if (!orca.inBounds(offset.x, offset.y)) { this.explode(); return } 74 | const collider = orca.glyphAt(offset.x, offset.y) 75 | if (collider === this.glyph) { return } 76 | if (collider !== '*' && collider !== '.' && collider !== this.glyph) { this.explode(); return } 77 | this.erase() 78 | this.x += x 79 | this.y += y 80 | this.replace(this.glyph) 81 | this.lock() 82 | } 83 | 84 | this.bang = function () { 85 | if (orca.glyphAt(this.x + 1, this.y) === '*') { return { x: 1, y: 0 } } 86 | if (orca.glyphAt(this.x - 1, this.y) === '*') { return { x: -1, y: 0 } } 87 | if (orca.glyphAt(this.x, this.y + 1) === '*') { return { x: 0, y: 1 } } 88 | if (orca.glyphAt(this.x, this.y - 1) === '*') { return { x: 0, y: -1 } } 89 | return false 90 | } 91 | 92 | // Docs 93 | 94 | this._ports = function () { 95 | let ports = '' 96 | if (Object.keys(this.ports.haste).length > 0) { 97 | for (const name in this.ports.haste) { 98 | ports += `'${name}, ` 99 | } 100 | } 101 | if (Object.keys(this.ports.input).length > 0) { 102 | for (const name in this.ports.input) { 103 | ports += `${name}, ` 104 | } 105 | } 106 | return ports !== '' ? '(' + ports.substr(0, ports.length - 2) + ')' : '' 107 | } 108 | 109 | this.docs = function () { 110 | return `\`${this.glyph.toUpperCase()}\` **${this.name}**${this._ports()}: ${this.info}` 111 | } 112 | 113 | function clamp (v, min, max) { return v < min ? min : v > max ? max : v } 114 | } 115 | 116 | module.exports = Operator 117 | -------------------------------------------------------------------------------- /desktop/core/orca.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const library = require('./library') 4 | 5 | function Orca (terminal, host = null) { 6 | this.w = 65 // Default Width 7 | this.h = 25 // Default Height 8 | this.s = '' // String 9 | this.f = host ? host.f : 0 // Frame 10 | 11 | this.host = host 12 | 13 | this.terminal = terminal 14 | this.keys = Object.keys(library).slice(0, 36) 15 | this.locks = [] 16 | this.ports = {} 17 | this.runtime = [] 18 | 19 | this.run = function () { 20 | this.runtime = this.parse() 21 | this.operate(this.runtime) 22 | this.f += 1 23 | } 24 | 25 | this.reset = function (w = this.w, h = this.h) { 26 | this.w = w 27 | this.h = h 28 | this.s = new Array((this.h * this.w) + 1).join('.') 29 | } 30 | 31 | this.load = function (w, h, s, f = 0) { 32 | this.w = w 33 | this.h = h 34 | this.f = f 35 | this.s = this.clean(s) 36 | return this 37 | } 38 | 39 | this.write = function (x, y, g) { 40 | if (!g) { return } 41 | if (g.length !== 1) { return } 42 | if (!this.inBounds(x, y)) { return } 43 | if (!this.isAllowed(g)) { return } 44 | if (this.glyphAt(x, y) === g) { return } 45 | const index = this.indexAt(x, y) 46 | this.s = this.s.substr(0, index) + g + this.s.substr(index + g.length) 47 | } 48 | 49 | this.clean = function (str) { 50 | let s = `${str}` 51 | s = s.replace(/\n/g, '').trim() 52 | s = s.substr(0, this.w * this.h) 53 | return s 54 | } 55 | 56 | // Operators 57 | 58 | this.parse = function () { 59 | const a = [] 60 | for (let y = 0; y < this.h; y++) { 61 | for (let x = 0; x < this.w; x++) { 62 | const g = this.glyphAt(x, y) 63 | const operator = this.cast(g, x, y) 64 | if (operator) { 65 | a.push(operator) 66 | } 67 | } 68 | } 69 | return a 70 | } 71 | 72 | this.cast = function (g, x, y) { 73 | if (g === '.') { return } 74 | if (!library[g.toLowerCase()]) { return } 75 | const passive = g === g.toUpperCase() && this.valueOf(g) > 9 76 | return new library[g.toLowerCase()](this, x, y, passive) 77 | } 78 | 79 | this.operate = function (operators) { 80 | this.release() 81 | for (const id in operators) { 82 | const operator = operators[id] 83 | if (this.lockAt(operator.x, operator.y)) { continue } 84 | if (operator.passive || operator.bang()) { 85 | operator.haste() 86 | operator.permissions() 87 | } 88 | } 89 | for (const id in operators) { 90 | const operator = operators[id] 91 | if (this.lockAt(operator.x, operator.y)) { continue } 92 | if (operator.passive || operator.bang()) { 93 | operator.run() 94 | } 95 | } 96 | } 97 | 98 | // Locks 99 | 100 | this.release = function () { 101 | this.locks = [] 102 | } 103 | 104 | this.unlock = function (x, y) { 105 | const index = this.locks.indexOf(`${x}:${y}`) 106 | this.locks.splice(index, 1) 107 | } 108 | 109 | this.lock = function (x, y) { 110 | if (this.lockAt(x, y)) { return } 111 | this.locks.push(`${x}:${y}`) 112 | } 113 | 114 | // IO 115 | 116 | this.input = function (g) { 117 | this.write(0, 0, g) 118 | } 119 | 120 | this.output = function () { 121 | return this.s.charAt(this.s.length - 1) 122 | } 123 | 124 | // Helpers 125 | 126 | this.inBounds = function (x, y) { 127 | return x >= 0 && x < this.w && y >= 0 && y < this.h 128 | } 129 | 130 | this.isAllowed = function (g) { 131 | return g === '.' || !!library[`${g}`.toLowerCase()] 132 | } 133 | 134 | this.keyOf = function (val) { 135 | return this.keys[val % 36] 136 | } 137 | 138 | this.valueOf = function (g) { 139 | return clamp(this.keys.indexOf(`${g}`.toLowerCase()), 0, 35) 140 | } 141 | 142 | this.indexAt = function (x, y) { 143 | return x + (this.w * y) 144 | } 145 | 146 | this.posAt = function (index) { 147 | return { x: index % this.w, y: parseInt(index / this.w) } 148 | } 149 | 150 | this.glyphAt = function (x, y, req = null) { 151 | return this.s.charAt(this.indexAt(x, y)) 152 | } 153 | 154 | this.lockAt = function (x, y) { 155 | return this.locks.indexOf(`${x}:${y}`) > -1 156 | } 157 | 158 | // Tools 159 | 160 | this.format = function () { 161 | const a = [] 162 | for (let y = 0; y < this.h; y++) { 163 | a.push(this.s.substr(y * this.w, this.w)) 164 | } 165 | return a.reduce((acc, val) => { 166 | return `${acc}${val}\n` 167 | }, '') 168 | } 169 | 170 | this.toString = function () { 171 | return this.format().trim() 172 | } 173 | 174 | this.reset() 175 | 176 | function clamp (v, min, max) { return v < min ? min : v > max ? max : v } 177 | } 178 | 179 | module.exports = Orca 180 | -------------------------------------------------------------------------------- /desktop/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamstah/Orca/cd4f96db307dbdcc1d4f687f7f70ed47dfd0bbe7/desktop/icon.icns -------------------------------------------------------------------------------- /desktop/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamstah/Orca/cd4f96db307dbdcc1d4f687f7f70ed47dfd0bbe7/desktop/icon.ico -------------------------------------------------------------------------------- /desktop/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamstah/Orca/cd4f96db307dbdcc1d4f687f7f70ed47dfd0bbe7/desktop/icon.png -------------------------------------------------------------------------------- /desktop/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /desktop/main.js: -------------------------------------------------------------------------------- 1 | const { app, BrowserWindow, webFrame, Menu } = require('electron') 2 | const path = require('path') 3 | const url = require('url') 4 | const shell = require('electron').shell 5 | 6 | let isShown = true 7 | 8 | app.win = null 9 | 10 | app.on('ready', () => { 11 | app.win = new BrowserWindow({ 12 | width: 710, 13 | height: 450, 14 | minWidth: 320, 15 | minHeight: 320, 16 | frame: process.platform === 'win32', 17 | resizable: true, 18 | icon: __dirname + '/icon.ico', 19 | transparent: process.platform !== 'win32', 20 | skipTaskbar: process.platform !== 'win32', 21 | autoHideMenuBar: process.platform !== 'win32' 22 | }) 23 | 24 | app.win.loadURL(`file://${__dirname}/sources/index.html`) 25 | // app.inspect() 26 | 27 | app.win.on('closed', () => { 28 | win = null 29 | app.quit() 30 | }) 31 | 32 | app.win.on('hide', function () { 33 | isShown = false 34 | }) 35 | 36 | app.win.on('show', function () { 37 | isShown = true 38 | }) 39 | 40 | app.on('window-all-closed', () => { 41 | app.quit() 42 | }) 43 | 44 | app.on('activate', () => { 45 | if (app.win === null) { 46 | createWindow() 47 | } else { 48 | app.win.show() 49 | } 50 | }) 51 | }) 52 | 53 | app.inspect = function () { 54 | app.win.toggleDevTools() 55 | } 56 | 57 | app.toggleFullscreen = function () { 58 | app.win.setFullScreen(!app.win.isFullScreen()) 59 | } 60 | 61 | app.toggleVisible = function () { 62 | if (process.platform === 'win32') { 63 | if (!app.win.isMinimized()) { app.win.minimize() } else { app.win.restore() } 64 | } else { 65 | if (isShown && !app.win.isFullScreen()) { app.win.hide() } else { app.win.show() } 66 | } 67 | } 68 | 69 | app.injectMenu = function (menu) { 70 | try { 71 | Menu.setApplicationMenu(Menu.buildFromTemplate(menu)) 72 | } catch (err) { 73 | console.warn('Cannot inject menu.') 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /desktop/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Orca", 3 | "version": "0.1.0", 4 | "main": "main.js", 5 | "scripts": { 6 | "start": "electron .", 7 | "time": "node time.js", 8 | "docs": "node ../docs.js", 9 | "fix": "standard --fix", 10 | "clean": "rm -r ~/Desktop/Orca-darwin-x64/ ; rm -r ~/Desktop/Orca-linux-x64/ ; rm -r ~/Desktop/Orca-win32-x64/ ; echo 'cleaned build location'", 11 | "build_osx": "electron-packager . Orca --platform=darwin --arch=x64 --out ~/Desktop/ --overwrite --icon=icon.icns ; echo 'Built for OSX'", 12 | "build_linux": "electron-packager . Orca --platform=linux --arch=x64 --out ~/Desktop/ --overwrite --icon=icon.ico ; echo 'Built for LINUX'", 13 | "build_win": "electron-packager . Orca --platform=win32 --arch=x64 --out ~/Desktop/ --overwrite --icon=icon.ico ; echo 'Built for WIN'", 14 | "build": "npm run clean ; npm run build_osx ; npm run build_linux ; npm run build_win", 15 | "push_osx": "~/butler push ~/Desktop/Orca-darwin-x64/ hundredrabbits/orca:osx-64", 16 | "push_linux": "~/butler push ~/Desktop/Orca-linux-x64/ hundredrabbits/orca:linux-64", 17 | "push_win": "~/butler push ~/Desktop/Orca-win32-x64/ hundredrabbits/orca:windows-64", 18 | "push_status": "~/butler status hundredrabbits/orca", 19 | "push": "npm run build ; npm run push_osx ; npm run push_linux ; npm run push_win ; npm run clean ; npm run push_status" 20 | }, 21 | "devDependencies": { 22 | "electron": "^3.1.1", 23 | "electron-packager": "^12.2.0", 24 | "electron-rebuild": "^1.8.2" 25 | }, 26 | "dependencies": { 27 | "node-osc": "^3.0.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /desktop/sources/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Orca 13 | 14 | 15 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /desktop/sources/links/fonts.css: -------------------------------------------------------------------------------- 1 | /* Input */ 2 | 3 | @font-face { 4 | font-family: 'input_mono_regular'; 5 | src: url('../media/fonts/input_mono_regular.ttf') format('truetype'); 6 | font-weight: normal; 7 | font-style: normal; 8 | } 9 | 10 | @font-face { 11 | font-family: 'input_mono_medium'; 12 | src: url('../media/fonts/input_mono_medium.ttf') format('truetype'); 13 | font-weight: normal; 14 | font-style: normal; 15 | } -------------------------------------------------------------------------------- /desktop/sources/links/main.css: -------------------------------------------------------------------------------- 1 | body { font-family: 'input_mono_regular'; padding:30px; overflow: hidden; -webkit-app-region: drag; transition: background-color 500ms; } 2 | body.transparent { background:transparent !important; } 3 | canvas { display: block;white-space: pre;font-size: 11px;line-height: 10px; text-transform: uppercase; width:300px; margin:0px auto; -webkit-app-region: drag; opacity: 0; transition: opacity 1000ms} 4 | canvas.ready { opacity: 1 } 5 | -------------------------------------------------------------------------------- /desktop/sources/links/reset.css: -------------------------------------------------------------------------------- 1 | * { margin:0;padding:0;border:0;outline:0;text-decoration:none;font-weight:inherit;font-style:inherit;color:inherit;font-size:100%;font-family:inherit;vertical-align:baseline;list-style:none;border-collapse:collapse;border-spacing:0; -webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;} -------------------------------------------------------------------------------- /desktop/sources/links/theme.css: -------------------------------------------------------------------------------- 1 | body { background:var(--background) !important } 2 | -------------------------------------------------------------------------------- /desktop/sources/media/fonts/input_mono_medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamstah/Orca/cd4f96db307dbdcc1d4f687f7f70ed47dfd0bbe7/desktop/sources/media/fonts/input_mono_medium.ttf -------------------------------------------------------------------------------- /desktop/sources/media/fonts/input_mono_regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamstah/Orca/cd4f96db307dbdcc1d4f687f7f70ed47dfd0bbe7/desktop/sources/media/fonts/input_mono_regular.ttf -------------------------------------------------------------------------------- /desktop/sources/scripts/cursor.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function Cursor (terminal) { 4 | this.x = 0 5 | this.y = 0 6 | this.w = 1 7 | this.h = 1 8 | this.mode = 0 9 | this.block = [] 10 | 11 | this.move = function (x, y) { 12 | this.x = clamp(this.x + x, 0, terminal.orca.w - 1) 13 | this.y = clamp(this.y - y, 0, terminal.orca.h - 1) 14 | terminal.update() 15 | } 16 | 17 | this.scale = function (x, y) { 18 | this.w = clamp(this.w + x, 1, terminal.orca.w - this.x) 19 | this.h = clamp(this.h - y, 1, terminal.orca.h - this.y) 20 | terminal.update() 21 | } 22 | 23 | this.drag = function (x, y) { 24 | this.cut() 25 | this.move(x, y) 26 | this.paste() 27 | } 28 | 29 | this.reset = function (pos = false) { 30 | if (pos) { 31 | this.x = 0 32 | this.y = 0 33 | } 34 | this.move(0, 0) 35 | this.w = 1 36 | this.h = 1 37 | this.mode = 0 38 | } 39 | 40 | this.selectAll = function () { 41 | this.x = 0 42 | this.y = 0 43 | this.w = terminal.orca.w 44 | this.h = terminal.orca.h 45 | this.mode = 0 46 | terminal.update() 47 | } 48 | 49 | this.copy = function () { 50 | this.block = this.getBlock() 51 | } 52 | 53 | this.cut = function () { 54 | this.copy() 55 | this.erase() 56 | } 57 | 58 | this.paste = function () { 59 | this.writeBlock(this.toRect(), this.block) 60 | } 61 | 62 | this.read = function () { 63 | return terminal.orca.glyphAt(this.x, this.y) 64 | } 65 | 66 | this.write = function (g) { 67 | if (this.mode === 2) { 68 | terminal.io.sendKey(event.key) 69 | return 70 | } 71 | terminal.orca.write(this.x, this.y, g) 72 | if (this.mode === 1) { 73 | this.move(1, 0) 74 | } 75 | terminal.history.record() 76 | } 77 | 78 | this.erase = function () { 79 | if (this.w === 1 && this.h === 1 && terminal.orca.glyphAt(this.x, this.y) === '.') { this.move(-1, 0); return } // Backspace Effect 80 | this.eraseBlock(this.x, this.y, this.w, this.h) 81 | terminal.history.record() 82 | } 83 | 84 | this.goto = function (str) { 85 | const i = terminal.orca.s.indexOf(str) 86 | if (i < 0) { return } 87 | const pos = terminal.orca.posAt(i) 88 | this.x = pos.x + 1 89 | this.y = pos.y 90 | } 91 | 92 | this.toggleMode = function (val) { 93 | this.mode = this.mode === 0 ? val : 0 94 | } 95 | 96 | this.inspect = function (name = true, ports = false) { 97 | if (this.w > 1 || this.h > 1) { return 'multi' } 98 | const port = terminal.portAt(this.x, this.y) 99 | if (port) { return `${port.name}` } 100 | if (terminal.orca.lockAt(this.x, this.y)) { return 'locked' } 101 | return 'empty' 102 | } 103 | 104 | // Block 105 | 106 | this.getBlock = function (rect = this.toRect()) { 107 | const block = [] 108 | for (let _y = rect.y; _y < rect.y + rect.h; _y++) { 109 | const line = [] 110 | for (let _x = rect.x; _x < rect.x + rect.w; _x++) { 111 | line.push(terminal.orca.glyphAt(_x, _y)) 112 | } 113 | block.push(line) 114 | } 115 | return block 116 | } 117 | 118 | this.writeBlock = function (rect, block) { 119 | if (!block || block.length === 0) { return } 120 | let _y = rect.y 121 | for (const lineId in block) { 122 | let _x = rect.x 123 | for (const glyphId in block[lineId]) { 124 | terminal.orca.write(_x, _y, block[lineId][glyphId]) 125 | _x++ 126 | } 127 | _y++ 128 | } 129 | terminal.history.record() 130 | } 131 | 132 | this.eraseBlock = function (x, y, w, h) { 133 | for (let _y = y; _y < y + h; _y++) { 134 | for (let _x = x; _x < x + w; _x++) { 135 | terminal.orca.write(_x, _y, '.') 136 | } 137 | } 138 | terminal.history.record() 139 | } 140 | 141 | this.toRect = function () { 142 | return { x: this.x, y: this.y, w: this.w, h: this.h } 143 | } 144 | 145 | function clamp (v, min, max) { return v < min ? min : v > max ? max : v } 146 | } 147 | 148 | module.exports = Cursor 149 | -------------------------------------------------------------------------------- /desktop/sources/scripts/events.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | window.addEventListener('dragover', function (e) { 4 | e.stopPropagation() 5 | e.preventDefault() 6 | e.dataTransfer.dropEffect = 'copy' 7 | }) 8 | 9 | window.addEventListener('drop', function (e) { 10 | e.preventDefault() 11 | e.stopPropagation() 12 | 13 | const file = e.dataTransfer.files[0] 14 | const path = file.path ? file.path : file.name 15 | 16 | if (!path || path.indexOf('.orca') < 0) { console.log('Orca', 'Not a orca file'); return } 17 | 18 | terminal.source.path = path 19 | terminal.source.read(path) 20 | }) 21 | 22 | window.onresize = (event) => { 23 | terminal.align() 24 | } 25 | -------------------------------------------------------------------------------- /desktop/sources/scripts/history.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function History (terminal, orca = terminal.orca) { 4 | this.index = 0 5 | this.frames = [] 6 | 7 | this.record = function () { 8 | if (this.index === this.frames.length) { 9 | this.append() 10 | } else { 11 | this.fork() 12 | } 13 | this.index = this.frames.length 14 | } 15 | 16 | this.undo = function () { 17 | if (this.index === 0) { console.warn('History', 'Reached beginning'); return } 18 | 19 | this.index = clamp(this.index - 1, 0, this.frames.lengt - 1) 20 | this.apply(this.frames[this.index]) 21 | } 22 | 23 | this.redo = function () { 24 | if (this.index > this.frames.length - 1) { console.warn('History', 'Reached end'); return } 25 | 26 | this.index = clamp(this.index + 1, 0, this.frames.lengt - 1) 27 | this.apply(this.frames[this.index]) 28 | } 29 | 30 | this.apply = function (f) { 31 | if (!f || f.length !== terminal.orca.s.length) { return } 32 | terminal.orca.s = this.frames[this.index] 33 | } 34 | 35 | this.reset = function () { 36 | this.index = 0 37 | this.frames = [] 38 | } 39 | 40 | this.append = function () { 41 | this.frames.push(terminal.orca.s) 42 | } 43 | 44 | this.fork = function () { 45 | this.frames = this.frames.slice(0, this.index + 1) 46 | this.frames.push(terminal.orca.s) 47 | } 48 | 49 | function clamp (v, min, max) { return v < min ? min : v > max ? max : v } 50 | } 51 | 52 | module.exports = History 53 | -------------------------------------------------------------------------------- /desktop/sources/scripts/io.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Midi = require('./io.midi') 4 | const Udp = require('./io.udp') 5 | const Osc = require('./io.osc') 6 | 7 | function IO (terminal) { 8 | this.midi = new Midi(terminal) 9 | this.udp = new Udp(terminal) 10 | this.osc = new Osc(terminal) 11 | 12 | this.start = function () { 13 | this.midi.start() 14 | this.udp.start() 15 | this.osc.start() 16 | this.clear() 17 | } 18 | 19 | this.clear = function () { 20 | this.midi.clear() 21 | this.udp.clear() 22 | this.osc.clear() 23 | } 24 | 25 | this.run = function () { 26 | this.midi.run() 27 | this.udp.run() 28 | this.osc.run() 29 | } 30 | 31 | this.length = function () { 32 | return this.midi.stack.length + this.udp.stack.length + this.osc.stack.length 33 | } 34 | 35 | this.toString = function () { 36 | let text = '' 37 | for (let i = 0; i < this.length(); i++) { 38 | text += '|' 39 | } 40 | while (text.length - 1 <= terminal.size.grid.w) { 41 | text += '-' 42 | } 43 | return text 44 | } 45 | } 46 | 47 | module.exports = IO 48 | -------------------------------------------------------------------------------- /desktop/sources/scripts/io.midi.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function Midi (terminal) { 4 | this.index = 0 5 | this.devices = [] 6 | this.stack = [] 7 | 8 | this.start = function () { 9 | console.info('Starting Midi..') 10 | this.setup() 11 | } 12 | 13 | this.clear = function () { 14 | this.stack = [] 15 | } 16 | 17 | this.run = function () { 18 | for (const id in this.stack) { 19 | this.play(this.stack[id], this.device()) 20 | } 21 | } 22 | 23 | this.update = function () { 24 | terminal.controller.clearCat('default', 'Midi') 25 | const devices = terminal.io.midi.list() 26 | for (const id in devices) { 27 | terminal.controller.add('default', 'Midi', `${devices[id].name} ${terminal.io.midi.index === parseInt(id) ? ' — Active' : ''}`, () => { terminal.io.midi.select(id) }, '') 28 | } 29 | if (devices.length < 1) { 30 | terminal.controller.add('default', 'Midi', `No Device Available`) 31 | } 32 | if (devices.length > 1) { 33 | terminal.controller.add('default', 'Midi', `Next Device`, () => { terminal.io.midi.next(id) }, 'CmdOrCtrl+Shift+M') 34 | } 35 | terminal.controller.commit() 36 | } 37 | 38 | // Midi 39 | 40 | this.send = function (channel, octave, note, velocity, length) { 41 | this.stack.push([channel, octave, note, velocity, length]) 42 | } 43 | 44 | this.play = function (data = this.stack, device) { 45 | const channel = convertChannel(data[0]) 46 | const note = convertNote(data[1], data[2]) 47 | const velocity = data[3] 48 | const length = window.performance.now() + convertLength(data[4], terminal.bpm) 49 | 50 | if (!device) { console.warn('No midi device!'); return } 51 | 52 | device.send([channel[0], note, velocity]) 53 | device.send([channel[1], note, velocity], length) 54 | } 55 | 56 | this.select = function (id) { 57 | if (!this.devices[id]) { return } 58 | this.index = parseInt(id) 59 | this.update() 60 | console.log(`Midi Device: ${this.device().name}`) 61 | return this.device() 62 | } 63 | 64 | this.device = function () { 65 | return this.devices[this.index] 66 | } 67 | 68 | this.list = function () { 69 | return this.devices 70 | } 71 | 72 | this.next = function () { 73 | this.select((this.index + 1) % this.devices.length) 74 | } 75 | 76 | // Setup 77 | 78 | this.setup = function () { 79 | if (!navigator.requestMIDIAccess) { return } 80 | navigator.requestMIDIAccess({ sysex: false }).then(this.access, (err) => { 81 | console.warn('No Midi', err) 82 | }) 83 | } 84 | 85 | this.access = function (midiAccess) { 86 | const iter = midiAccess.outputs.values() 87 | for (let i = iter.next(); i && !i.done; i = iter.next()) { 88 | terminal.io.midi.devices.push(i.value) 89 | } 90 | terminal.io.midi.select(0) 91 | } 92 | 93 | this.toString = function () { 94 | return this.devices.length > 0 ? `${this.devices[this.index].name}` : 'No Midi' 95 | } 96 | 97 | function convertChannel (id) { 98 | // return [id + 144, id + 128].toString(16); 99 | if (id === 0) { return [0x90, 0x80] } // ch1 100 | if (id === 1) { return [0x91, 0x81] } // ch2 101 | if (id === 2) { return [0x92, 0x82] } // ch3 102 | if (id === 3) { return [0x93, 0x83] } // ch4 103 | if (id === 4) { return [0x94, 0x84] } // ch5 104 | if (id === 5) { return [0x95, 0x85] } // ch6 105 | if (id === 6) { return [0x96, 0x86] } // ch7 106 | if (id === 7) { return [0x97, 0x87] } // ch8 107 | if (id === 8) { return [0x98, 0x88] } // ch9 108 | if (id === 9) { return [0x99, 0x89] } // ch10 109 | if (id === 10) { return [0x9A, 0x8A] } // ch11 110 | if (id === 11) { return [0x9B, 0x8B] } // ch12 111 | if (id === 12) { return [0x9C, 0x8C] } // ch13 112 | if (id === 13) { return [0x9D, 0x8D] } // ch14 113 | if (id === 14) { return [0x9E, 0x8E] } // ch15 114 | if (id === 15) { return [0x9F, 0x8F] } // ch16 115 | } 116 | 117 | function convertNote (octave, note) { 118 | return 24 + (octave * 12) + note // 60 = C3 119 | } 120 | 121 | function convertLength (val, bpm) { 122 | return (60000 / bpm) * (val / 15) 123 | } 124 | 125 | function clamp (v, min, max) { return v < min ? min : v > max ? max : v } 126 | } 127 | 128 | module.exports = Midi 129 | -------------------------------------------------------------------------------- /desktop/sources/scripts/io.osc-standalone.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function Osc (terminal) { 4 | this.index = 0 5 | this.routes = [] 6 | this.stack = [] 7 | 8 | this.start = function () { 9 | console.info('Starting OSC..') 10 | this.setup() 11 | } 12 | 13 | this.clear = function () { 14 | this.stack = [] 15 | } 16 | 17 | this.run = function () { 18 | for (const id in this.stack) { 19 | this.play(this.stack[id]) 20 | } 21 | } 22 | 23 | this.send = function (msg) { 24 | this.stack.push(msg) 25 | } 26 | 27 | this.play = function (data) { 28 | // see io.osc.js 29 | 30 | // const args = data.split('.').filter(d => d !== '') 31 | // if (args.length !== 0) { 32 | // const key = args[0] 33 | // const def = this.config.defs[key] 34 | // const pattern = def.pattern 35 | 36 | // if(pattern.length !== args.length - 1) { 37 | // console.log(`Number of arguments provided does not match the pattern length of this def`) 38 | // return 39 | // } 40 | 41 | // const values = [] 42 | // for(let i = 0, l = pattern.length; i < l; i++) { 43 | // const type = pattern[i] 44 | // if (type !== 'f' && type !== 'i' && type !== 's') { 45 | // console.log(`Don't know how to send OSC argument with type '${type}'`) 46 | // return 47 | // } 48 | 49 | // const value = 50 | // type === 'f' ? parseInt(split[1]) / 10.0 51 | // : type === 'i' ? parseInt(split[1]) 52 | // /* type === 's' ? */ : split[1] 53 | 54 | // function nextMultipleOf4 (x) { 55 | // const rem = x % 4 56 | // return rem === 0 ? x : (x + 4 - rem) 57 | // } 58 | 59 | // function writeOscString (val, buf, pos) { 60 | // let localPos = pos 61 | // for (let i = 0; i < val.length; i++) { 62 | // const ch = val.charCodeAt(i) 63 | // localPos = buf.writeUInt8(ch & 0xFF, localPos) 64 | // } 65 | // // Add length, terminating 0 and pad to multiple of 4 66 | // return nextMultipleOf4(pos + val.length + 1) 67 | // } 68 | 69 | // let msglen = 0 70 | // { // Calculate message length 71 | // // Zero-terminated address string 72 | // msglen += nextMultipleOf4(address.length + 1) 73 | // // Type tag with two args arg: comma, letter (key), letter (value), terminating 0 74 | // msglen += 4 75 | // // Zero-terminated key string 76 | // msglen += nextMultipleOf4(key.length + 1) 77 | // if (type === 'f' || type === 'i') { 78 | // // 32-bit float or int 79 | // msglen += 4 80 | // } else if (type === 's') { 81 | // // Zero terminated value string 82 | // msglen += nextMultipleOf4(value.length + 1) 83 | // } 84 | // } 85 | 86 | // // Get buffer cleared to 0 87 | // const buf = Buffer.alloc(msglen) 88 | // let pos = 0 89 | 90 | // pos = writeOscString(address, buf, pos) 91 | // pos = writeOscString(`,s${type}`, buf, pos) 92 | // pos = writeOscString(key, buf, pos) 93 | 94 | // if (type === 'f') { 95 | // pos = buf.writeFloatBE(value, pos) 96 | // } else if (type === 'i') { 97 | // pos = buf.writeInt32BE(value, pos) 98 | // } else if (type === 's') { 99 | // pos = writeOscString(value, buf, pos) 100 | // } 101 | // } 102 | 103 | // this.port.send(buf, def.port, def.address, (err) => { 104 | // if (err) { console.log(err) } 105 | // }) 106 | } 107 | 108 | this.setup = function () { 109 | this.config = require('../../core/bridge/oscConfig') 110 | this.clients = {} 111 | for (const key in this.config.defs) { 112 | const def = this.config.defs[key] 113 | const address = def.address 114 | const port = def.port 115 | if (!this.clients[`${address}:${port}`]) { 116 | this.clients[`${address}:${port}`] = new osc.Client(address, port) 117 | console.log(`OSC client ${address}:${port} created`) 118 | } 119 | } 120 | } 121 | } 122 | 123 | module.exports = Osc 124 | -------------------------------------------------------------------------------- /desktop/sources/scripts/io.osc.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const osc = require('node-osc') 4 | 5 | function Osc (terminal) { 6 | this.stack = [] 7 | this.port = 49162 8 | this.ip = '127.0.0.1' 9 | 10 | this.start = function () { 11 | console.info('Starting OSC..') 12 | this.setup() 13 | } 14 | 15 | this.clear = function () { 16 | this.stack = [] 17 | } 18 | 19 | this.run = function () { 20 | for (const id in this.stack) { 21 | this.play(this.stack[id]) 22 | } 23 | } 24 | 25 | this.send = function (path, msg) { 26 | this.stack.push({ path, msg }) 27 | } 28 | 29 | this.play = function ({ path, msg }) { 30 | const oscMsg = new osc.Message(path) 31 | for (var i = 0; i < msg.length; i++) { 32 | oscMsg.append(terminal.orca.valueOf(msg.charAt(i))) 33 | } 34 | this.client.send(oscMsg, (err) => { 35 | if (err) { console.warn(err) } 36 | }) 37 | } 38 | 39 | this.select = function (port) { 40 | if (port < 1000) { console.warn('Unavailable port'); return } 41 | this.port = port 42 | this.setup() 43 | console.log(`OSC Port: ${this.port}`) 44 | return this.port 45 | } 46 | 47 | this.setup = function () { 48 | if (this.client) { this.client.kill() } 49 | this.client = new osc.Client(this.ip, this.port) 50 | } 51 | } 52 | 53 | module.exports = Osc 54 | -------------------------------------------------------------------------------- /desktop/sources/scripts/io.udp.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const dgram = require('dgram') 4 | 5 | function Udp (terminal) { 6 | this.index = 0 7 | this.stack = [] 8 | this.server = null 9 | this.port = 49160 10 | this.ip = '127.0.0.1' 11 | 12 | this.start = function () { 13 | console.info('UDP Starting..') 14 | this.setup() 15 | } 16 | 17 | this.clear = function () { 18 | this.stack = [] 19 | } 20 | 21 | this.run = function () { 22 | for (const id in this.stack) { 23 | this.play(this.stack[id]) 24 | } 25 | } 26 | 27 | this.send = function (msg) { 28 | this.stack.push(msg) 29 | } 30 | 31 | this.play = function (data) { 32 | this.server.send(Buffer.from(`${data}`), this.port, this.ip, (err) => { 33 | if (err) { console.log(err) } 34 | }) 35 | } 36 | 37 | this.select = function (port = 49160) { 38 | if (port < 1000) { console.warn('Unavailable port'); return } 39 | this.port = port 40 | console.log(`UDP Port: ${this.port}`) 41 | return this.port 42 | } 43 | 44 | this.setup = function () { 45 | this.server = dgram.createSocket('udp4') 46 | } 47 | } 48 | 49 | module.exports = Udp 50 | -------------------------------------------------------------------------------- /desktop/sources/scripts/keyboard.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function Keyboard (terminal) { 4 | this.locks = [] 5 | this.history = '' 6 | 7 | this.onKeyDown = function (event) { 8 | // Reset 9 | if ((event.metaKey || event.ctrlKey) && event.key === 'Backspace') { 10 | terminal.reset() 11 | event.preventDefault() 12 | return 13 | } 14 | 15 | if (event.key === 'c' && (event.metaKey || event.ctrlKey)) { terminal.cursor.copy(); event.preventDefault(); return } 16 | if (event.key === 'x' && (event.metaKey || event.ctrlKey)) { terminal.cursor.cut(); event.preventDefault(); return } 17 | if (event.key === 'v' && (event.metaKey || event.ctrlKey)) { terminal.cursor.paste(); event.preventDefault(); return } 18 | if (event.key === 'a' && (event.metaKey || event.ctrlKey)) { terminal.cursor.selectAll(); event.preventDefault(); return } 19 | 20 | // Undo/Redo 21 | if (event.key === 'z' && (event.metaKey || event.ctrlKey) && event.shiftKey) { terminal.history.redo(); event.preventDefault(); return } 22 | if (event.key === 'z' && (event.metaKey || event.ctrlKey)) { terminal.history.undo(); event.preventDefault(); return } 23 | 24 | if (event.keyCode === 38) { terminal.keyboard.onArrowUp(event.shiftKey, (event.metaKey || event.ctrlKey), event.altKey); return } 25 | if (event.keyCode === 40) { terminal.keyboard.onArrowDown(event.shiftKey, (event.metaKey || event.ctrlKey), event.altKey); return } 26 | if (event.keyCode === 37) { terminal.keyboard.onArrowLeft(event.shiftKey, (event.metaKey || event.ctrlKey), event.altKey); return } 27 | if (event.keyCode === 39) { terminal.keyboard.onArrowRight(event.shiftKey, (event.metaKey || event.ctrlKey), event.altKey); return } 28 | 29 | if (event.metaKey) { return } 30 | if (event.ctrlKey) { return } 31 | 32 | if (event.key === 'Backquote') { terminal.toggleBackground(); return } 33 | if (event.key === 'Tab') { terminal.toggleInterface(); return } 34 | if (event.key === 'Enter') { terminal.cursor.toggleMode(1); return } 35 | if (event.key === 'Backspace' || event.key === '.') { terminal.cursor.erase(); return } 36 | if (event.key === ' ') { terminal.pause(); event.preventDefault(); return } 37 | if (event.key === 'Escape') { terminal.clear(); terminal.isPaused = false; terminal.cursor.reset(); return } 38 | 39 | if (event.key === ']') { terminal.modGrid(1, 0); event.preventDefault(); return } 40 | if (event.key === '[') { terminal.modGrid(-1, 0); event.preventDefault(); return } 41 | if (event.key === '}') { terminal.modGrid(0, 1); event.preventDefault(); return } 42 | if (event.key === '{') { terminal.modGrid(0, -1); event.preventDefault(); return } 43 | if (event.key === '>') { terminal.modSpeed(1); event.preventDefault(); return } 44 | if (event.key === '<') { terminal.modSpeed(-1); event.preventDefault(); return } 45 | 46 | if (event.key.length === 1) { 47 | terminal.cursor.write(event.key) 48 | terminal.update() 49 | } 50 | } 51 | 52 | this.onKeyUp = function (event) { 53 | terminal.update() 54 | } 55 | 56 | this.onArrowUp = function (mod = false, skip = false, drag = false) { 57 | const leap = skip ? terminal.size.grid.h : 1 58 | if (drag) { 59 | terminal.cursor.drag(0, leap) 60 | } else if (mod) { 61 | terminal.cursor.scale(0, leap) 62 | } else { 63 | terminal.cursor.move(0, leap) 64 | } 65 | } 66 | 67 | this.onArrowDown = function (mod = false, skip = false, drag = false) { 68 | const leap = skip ? terminal.size.grid.h : 1 69 | if (drag) { 70 | terminal.cursor.drag(0, -leap) 71 | } else if (mod) { 72 | terminal.cursor.scale(0, -leap) 73 | } else { 74 | terminal.cursor.move(0, -leap) 75 | } 76 | } 77 | 78 | this.onArrowLeft = function (mod = false, skip = false, drag = false) { 79 | const leap = skip ? terminal.size.grid.w : 1 80 | if (drag) { 81 | terminal.cursor.drag(-leap, 0) 82 | } else if (mod) { 83 | terminal.cursor.scale(-leap, 0) 84 | } else { 85 | terminal.cursor.move(-leap, 0) 86 | } 87 | } 88 | 89 | this.onArrowRight = function (mod = false, skip = false, drag = false) { 90 | const leap = skip ? terminal.size.grid.w : 1 91 | if (drag) { 92 | terminal.cursor.drag(leap, 0) 93 | } else if (mod) { 94 | terminal.cursor.scale(leap, 0) 95 | } else { 96 | terminal.cursor.move(leap, 0) 97 | } 98 | } 99 | 100 | document.onkeydown = (event) => { this.onKeyDown(event) } 101 | document.onkeyup = (event) => { this.onKeyUp(event) } 102 | } 103 | 104 | module.exports = Keyboard 105 | -------------------------------------------------------------------------------- /desktop/sources/scripts/lib/controller.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function Controller () { 4 | const fs = require('fs') 5 | const { dialog, app } = require('electron').remote 6 | 7 | this.menu = { default: {} } 8 | this.mode = 'default' 9 | 10 | this.app = require('electron').remote.app 11 | 12 | this.start = function () { 13 | } 14 | 15 | this.add = function (mode, cat, label, fn, accelerator) { 16 | if (!this.menu[mode]) { this.menu[mode] = {} } 17 | if (!this.menu[mode][cat]) { this.menu[mode][cat] = {} } 18 | this.menu[mode][cat][label] = { fn: fn, accelerator: accelerator } 19 | console.log(`${mode}/${cat}/${label} ${accelerator ? '<' + accelerator + '>' : ''}`) 20 | } 21 | 22 | this.add_role = function (mode, cat, label) { 23 | if (!this.menu[mode]) { this.menu[mode] = {} } 24 | if (!this.menu[mode][cat]) { this.menu[mode][cat] = {} } 25 | this.menu[mode][cat][label] = { role: label } 26 | } 27 | 28 | this.clearCat = function (mode, cat) { 29 | if (this.menu[mode]) { this.menu[mode][cat] = {} } 30 | } 31 | 32 | this.set = function (mode = 'default') { 33 | this.mode = mode 34 | this.commit() 35 | } 36 | 37 | this.format = function () { 38 | const f = [] 39 | const m = this.menu[this.mode] 40 | for (const cat in m) { 41 | const submenu = [] 42 | for (const name in m[cat]) { 43 | const option = m[cat][name] 44 | if (option.role) { 45 | submenu.push({ role: option.role }) 46 | } else { 47 | submenu.push({ label: name, accelerator: option.accelerator, click: option.fn }) 48 | } 49 | } 50 | f.push({ label: cat, submenu: submenu }) 51 | } 52 | return f 53 | } 54 | 55 | this.commit = function () { 56 | this.app.injectMenu(this.format()) 57 | } 58 | 59 | this.accelerator_for_key = function (key, menu) { 60 | const acc = { basic: null, ctrl: null } 61 | for (cat in menu) { 62 | const options = menu[cat] 63 | for (const id in options.submenu) { 64 | const option = options.submenu[id]; if (option.role) { continue } 65 | acc.basic = (option.accelerator.toLowerCase() === key.toLowerCase()) ? option.label.toUpperCase().replace('TOGGLE ', '').substr(0, 8).trim() : acc.basic 66 | acc.ctrl = (option.accelerator.toLowerCase() === ('CmdOrCtrl+' + key).toLowerCase()) ? option.label.toUpperCase().replace('TOGGLE ', '').substr(0, 8).trim() : acc.ctrl 67 | } 68 | } 69 | return acc 70 | } 71 | } 72 | 73 | module.exports = new Controller() 74 | -------------------------------------------------------------------------------- /desktop/sources/scripts/lib/theme.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function Theme (_default) { 4 | const themer = this 5 | 6 | this.active = _default 7 | 8 | this.el = document.createElement('style') 9 | this.el.type = 'text/css' 10 | 11 | this.install = function (host = document.body, callback) { 12 | console.log('Theme', 'Installing..') 13 | host.appendChild(this.el) 14 | this.callback = callback 15 | } 16 | 17 | this.start = function () { 18 | console.log('Theme', 'Starting..') 19 | if (isJson(localStorage.theme)) { 20 | const storage = JSON.parse(localStorage.theme) 21 | if (validate(storage)) { 22 | console.log('Theme', 'Found theme in localStorage!') 23 | this.load(storage) 24 | return 25 | } 26 | } 27 | this.load(_default) 28 | } 29 | 30 | this.load = function (data) { 31 | const theme = parse(data) 32 | if (!validate(theme)) { console.warn('Theme', 'Not a theme', theme); return } 33 | console.log('Theme', `Loading theme with background ${theme.background}.`) 34 | this.el.innerHTML = `:root { --background: ${theme.background}; --f_high: ${theme.f_high}; --f_med: ${theme.f_med}; --f_low: ${theme.f_low}; --f_inv: ${theme.f_inv}; --b_high: ${theme.b_high}; --b_med: ${theme.b_med}; --b_low: ${theme.b_low}; --b_inv: ${theme.b_inv}; }` 35 | localStorage.setItem('theme', JSON.stringify(theme)) 36 | this.active = theme 37 | if (this.callback) { 38 | this.callback() 39 | } 40 | } 41 | 42 | this.reset = function () { 43 | this.load(_default) 44 | } 45 | 46 | function parse (any) { 47 | if (any && any.background) { return any } else if (any && any.data) { return any.data } else if (any && isJson(any)) { return JSON.parse(any) } else if (any && isHtml(any)) { return extract(any) } 48 | return null 49 | } 50 | 51 | // Drag 52 | 53 | this.drag = function (e) { 54 | e.stopPropagation() 55 | e.preventDefault() 56 | e.dataTransfer.dropEffect = 'copy' 57 | } 58 | 59 | this.drop = function (e) { 60 | e.preventDefault() 61 | e.stopPropagation() 62 | const file = e.dataTransfer.files[0] 63 | if (!file || !file.name) { console.warn('Theme', 'Unnamed file.'); return } 64 | if (file.name.indexOf('.thm') < 0 && file.name.indexOf('.svg') < 0) { console.warn('Theme', 'Skipped, not a theme'); return } 65 | const reader = new FileReader() 66 | reader.onload = function (e) { 67 | themer.load(e.target.result) 68 | } 69 | reader.readAsText(file) 70 | } 71 | 72 | this.open = function () { 73 | const fs = require('fs') 74 | const { dialog, app } = require('electron').remote 75 | let paths = dialog.showOpenDialog(app.win, { properties: ['openFile'], filters: [{ name: 'Themes', extensions: ['svg'] }] }) 76 | if (!paths) { console.log('Nothing to load') } 77 | fs.readFile(paths[0], 'utf8', function (err, data) { 78 | if (err) throw err 79 | themer.load(data) 80 | }) 81 | } 82 | 83 | window.addEventListener('dragover', this.drag) 84 | window.addEventListener('drop', this.drop) 85 | 86 | // Helpers 87 | 88 | function validate (json) { 89 | if (!json) { return false } 90 | if (!json.background) { return false } 91 | if (!json.f_high) { return false } 92 | if (!json.f_med) { return false } 93 | if (!json.f_low) { return false } 94 | if (!json.f_inv) { return false } 95 | if (!json.b_high) { return false } 96 | if (!json.b_med) { return false } 97 | if (!json.b_low) { return false } 98 | if (!json.b_inv) { return false } 99 | return true 100 | } 101 | 102 | function extract (text) { 103 | const svg = new DOMParser().parseFromString(text, 'text/xml') 104 | try { 105 | return { 106 | 'background': svg.getElementById('background').getAttribute('fill'), 107 | 'f_high': svg.getElementById('f_high').getAttribute('fill'), 108 | 'f_med': svg.getElementById('f_med').getAttribute('fill'), 109 | 'f_low': svg.getElementById('f_low').getAttribute('fill'), 110 | 'f_inv': svg.getElementById('f_inv').getAttribute('fill'), 111 | 'b_high': svg.getElementById('b_high').getAttribute('fill'), 112 | 'b_med': svg.getElementById('b_med').getAttribute('fill'), 113 | 'b_low': svg.getElementById('b_low').getAttribute('fill'), 114 | 'b_inv': svg.getElementById('b_inv').getAttribute('fill') 115 | } 116 | } catch (err) { 117 | console.warn('Theme', 'Incomplete SVG Theme', err) 118 | } 119 | } 120 | 121 | function isJson (text) { 122 | try { JSON.parse(text); return true } catch (error) { return false } 123 | } 124 | 125 | function isHtml (text) { 126 | try { new DOMParser().parseFromString(text, 'text/xml'); return true } catch (error) { return false } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /desktop/sources/scripts/source.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function Source (terminal) { 4 | const Orca = require('../../core/orca') 5 | const fs = require('fs') 6 | const { dialog, app } = require('electron').remote 7 | 8 | this.path = null 9 | 10 | this.new = function () { 11 | console.log('New') 12 | 13 | this.path = null 14 | 15 | terminal.orca = new Orca(terminal) 16 | terminal.resize() 17 | terminal.history.reset() 18 | } 19 | 20 | this.open = function () { 21 | console.log('Open') 22 | let paths = dialog.showOpenDialog(app.win, { properties: ['openFile'], filters: [{ name: 'Orca Machines', extensions: ['orca'] }] }) 23 | if (!paths) { console.log('Nothing to load') } 24 | this.path = paths[0] 25 | this.read(paths[0]) 26 | } 27 | 28 | this.save = function (as = false) { 29 | console.log('Save') 30 | if (this.path && !as) { 31 | this.write(this.path) 32 | } else { 33 | dialog.showSaveDialog((path) => { 34 | if (path === undefined) { return } 35 | if (path.indexOf('.orca') < 0) { path += '.orca' } 36 | terminal.source.write(path) 37 | terminal.source.path = path 38 | }) 39 | } 40 | } 41 | 42 | this.revert = function () { 43 | console.log('Revert') 44 | this.read(this.path) 45 | } 46 | 47 | // I/O 48 | 49 | this.write = function (path) { 50 | fs.writeFile(path, this.generate(), (err) => { 51 | if (err) { alert('An error ocurred updating the file' + err.message); console.log(err) } 52 | }) 53 | } 54 | 55 | this.read = function (path) { 56 | fs.readFile(path, 'utf8', (err, data) => { 57 | if (err) throw err 58 | terminal.load(this.parse(data)) 59 | terminal.history.record() 60 | }) 61 | } 62 | 63 | // Converters 64 | 65 | this.generate = function (orca = terminal.orca) { 66 | return `${orca}` 67 | } 68 | 69 | this.parse = function (text) { 70 | const lines = text.split('\n') 71 | const w = lines[0].length 72 | const h = lines.length 73 | const s = lines.join('\n').trim() 74 | return new Orca(terminal).load(w, h, s) 75 | } 76 | 77 | // Etc 78 | 79 | this.name = function () { 80 | const parts = this.path.replace(/\\/g, '/').split('/') 81 | return parts[parts.length - 1].replace('.orca', '').trim() 82 | } 83 | 84 | this.toString = function () { 85 | return this.path ? this.name() : 'blank' 86 | } 87 | } 88 | 89 | module.exports = Source 90 | -------------------------------------------------------------------------------- /desktop/sources/scripts/terminal.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function Terminal (tile = { w: 20, h: 30 }) { 4 | const Orca = require('../../core/orca') 5 | const Cursor = require('./cursor') 6 | const Source = require('./source') 7 | const History = require('./history') 8 | const Keyboard = require('./keyboard') 9 | const IO = require('./io') 10 | 11 | this.library = require('../../core/library') 12 | 13 | this.io = new IO(this) 14 | this.cursor = new Cursor(this) 15 | this.source = new Source(this) 16 | this.history = new History(this) 17 | this.keyboard = new Keyboard(this) 18 | this.controller = new Controller() 19 | 20 | // Themes 21 | this.theme = new Theme({ background: '#000000', f_high: '#ffffff', f_med: '#777777', f_low: '#444444', f_inv: '#000000', b_high: '#eeeeee', b_med: '#72dec2', b_low: '#444444', b_inv: '#ffb545' }) 22 | 23 | this.el = document.createElement('canvas') 24 | this.context = this.el.getContext('2d') 25 | this.size = { width: 0, height: 0, ratio: 0.5, grid: { w: 8, h: 8 } } 26 | this.isPaused = false 27 | this.showInterface = true 28 | this.timer = null 29 | this.bpm = 120 30 | 31 | this.install = function (host) { 32 | host.appendChild(this.el) 33 | this.theme.install(host) 34 | } 35 | 36 | this.start = function () { 37 | this.theme.start() 38 | this.io.start() 39 | this.source.new() 40 | this.history.record() 41 | this.setSpeed(120) 42 | this.resize() 43 | this.update() 44 | this.el.className = 'ready' 45 | } 46 | 47 | this.run = function () { 48 | if (this.isPaused) { return } 49 | this.io.clear() 50 | this.orca.run() 51 | this.io.run() 52 | this.update() 53 | } 54 | 55 | this.pause = function () { 56 | this.isPaused = !this.isPaused 57 | console.log(this.isPaused ? 'Paused' : 'Unpaused') 58 | this.update() 59 | } 60 | 61 | this.load = function (orca, frame = 0) { 62 | this.history.reset() 63 | this.orca = orca 64 | this.resize() 65 | this.update() 66 | } 67 | 68 | this.update = function () { 69 | this.clear() 70 | this.ports = this.findPorts() 71 | this.drawProgram() 72 | this.drawInterface() 73 | } 74 | 75 | this.reset = function () { 76 | this.theme.reset() 77 | } 78 | 79 | // 80 | 81 | this.setSpeed = function (bpm) { 82 | this.bpm = clamp(bpm, 60, 300) 83 | console.log(`Changed speed to ${this.bpm}.`) 84 | clearInterval(this.timer) 85 | this.timer = setInterval(() => { this.run() }, (60000 / bpm) / 4) 86 | this.update() 87 | } 88 | 89 | this.setGrid = function (w, h) { 90 | this.size.grid.w = w 91 | this.size.grid.h = h 92 | this.update() 93 | } 94 | 95 | this.setSize = function (w, h) { 96 | if (w < 17 || h < 17) { return } 97 | 98 | let block = `${this.orca}` 99 | if (h > this.orca.h) { 100 | block = `${block}${`\n${'.'.repeat(this.orca.w)}`.repeat((h - this.orca.h))}` 101 | } else if (h < this.orca.h) { 102 | block = `${block}`.split('\n').slice(0, (h - this.orca.h)).join('\n').trim() 103 | } 104 | 105 | if (w > this.orca.w) { 106 | block = `${block}`.split('\n').map((val) => { return val + ('.').repeat((w - this.orca.w)) }).join('\n').trim() 107 | } else if (w < this.orca.w) { 108 | block = `${block}`.split('\n').map((val) => { return val.substr(0, val.length + (w - this.orca.w)) }).join('\n').trim() 109 | } 110 | 111 | this.orca.load(w, h, block, this.orca.f) 112 | this.resize() 113 | } 114 | 115 | this.modSpeed = function (mod = 0) { 116 | this.setSpeed(this.bpm + mod) 117 | } 118 | 119 | this.modGrid = function (x = 0, y = 0) { 120 | const w = clamp(this.size.grid.w + x, 4, 16) 121 | const h = clamp(this.size.grid.h + y, 4, 16) 122 | this.setGrid(w, h) 123 | } 124 | 125 | this.modSize = function (x = 0, y = 0) { 126 | const w = ((parseInt(this.orca.w / this.size.grid.w) + x) * this.size.grid.w) + 1 127 | const h = ((parseInt(this.orca.h / this.size.grid.h) + y) * this.size.grid.h) + 1 128 | this.setSize(w, h) 129 | } 130 | 131 | this.modZoom = function (mod = 0, set = false) { 132 | const { webFrame } = require('electron') 133 | const currentZoomFactor = webFrame.getZoomFactor() 134 | webFrame.setZoomFactor(set ? mod : currentZoomFactor + mod) 135 | } 136 | 137 | this.toggleInterface = function () { 138 | this.showInterface = this.showInterface !== true 139 | } 140 | 141 | this.toggleBackground = function () { 142 | document.body.className = document.body.className === 'transparent' ? '' : 'transparent' 143 | } 144 | 145 | // 146 | 147 | this.isCursor = function (x, y) { 148 | return x === this.cursor.x && y === this.cursor.y 149 | } 150 | 151 | this.isSelection = function (x, y) { 152 | return !!(x >= this.cursor.x && x < this.cursor.x + this.cursor.w && y >= this.cursor.y && y < this.cursor.y + this.cursor.h) 153 | } 154 | 155 | this.portAt = function (x, y, req = null) { 156 | return this.ports[`${x}:${y}`] 157 | } 158 | 159 | this.findPorts = function () { 160 | const h = {} 161 | for (const id in this.orca.runtime) { 162 | const g = this.orca.runtime[id] 163 | if (this.orca.lockAt(g.x, g.y)) { continue } 164 | if (!h[`${g.x}:${g.y}`]) { 165 | h[`${g.x}:${g.y}`] = { type: g.passive && g.draw ? 'passive' : 'none', name: `${g.name}` } 166 | } 167 | for (const id in g.ports.haste) { 168 | const port = g.ports.haste[id] 169 | h[`${g.x + port.x}:${g.y + port.y}`] = { type: 'haste', name: `${g.glyph}'${id}` } 170 | } 171 | for (const id in g.ports.input) { 172 | const port = g.ports.input[id] 173 | h[`${g.x + port.x}:${g.y + port.y}`] = { type: 'input', name: `${g.glyph}:${id}` } 174 | } 175 | if (g.ports.output) { h[`${g.x + g.ports.output.x}:${g.y + g.ports.output.y}`] = { type: 'output', name: `${g.glyph}.out` } } 176 | } 177 | if (this.orca.host) { 178 | h[`0:0`] = { type: 'input', name: `${this.orca.id}:input` } 179 | h[`${this.orca.w - 1}:${this.orca.h - 1}`] = { type: 'output', name: `${this.orca.id}.output` } 180 | } 181 | return h 182 | } 183 | 184 | // Canvas 185 | 186 | this.clear = function () { 187 | this.context.clearRect(0, 0, this.size.width, this.size.height) 188 | } 189 | 190 | this.guide = function (x, y) { 191 | const g = this.orca.glyphAt(x, y) 192 | if (g !== '.') { return g } 193 | if (x % this.size.grid.w === 0 && y % this.size.grid.h === 0) { return '+' } 194 | return g 195 | } 196 | 197 | this.drawProgram = function () { 198 | for (let y = 0; y < this.orca.h; y++) { 199 | for (let x = 0; x < this.orca.w; x++) { 200 | const port = this.ports[`${x}:${y}`] 201 | const glyph = this.guide(x, y) 202 | const isCursor = this.isCursor(x, y) 203 | if (this.showInterface === false && glyph === '.' && !isCursor) { continue } 204 | const styles = { isSelection: this.isSelection(x, y), isCursor: isCursor, isPort: port ? port.type : false, isLocked: this.orca.lockAt(x, y) } 205 | this.drawSprite(x, y, glyph, styles) 206 | } 207 | } 208 | } 209 | 210 | this.drawInterface = function () { 211 | if (this.showInterface !== true) { return } 212 | 213 | const col = this.size.grid.w 214 | // Cursor 215 | this.write(`${this.cursor.x},${this.cursor.y}`, col * 0, 1, this.size.grid.w) 216 | this.write(`${this.cursor.w}:${this.cursor.h}`, col * 1, 1, this.size.grid.w) 217 | this.write(`${this.cursor.inspect()}`, col * 2, 1, this.size.grid.w) 218 | this.write(`${this.source}${this.cursor.mode === 2 ? '^' : this.cursor.mode === 1 ? '+' : ''}`, col * 3, 1, this.size.grid.w) 219 | this.write(`${this.io.midi}`, col * 4, 1, this.size.grid.w * 2) 220 | 221 | // Grid 222 | this.write(`${this.orca.w}x${this.orca.h}`, col * 0, 0, this.size.grid.w) 223 | this.write(`${this.size.grid.w}/${this.size.grid.h}`, col * 1, 0, this.size.grid.w) 224 | this.write(`${this.orca.f}f${this.isPaused ? '*' : ''}`, col * 2, 0, this.size.grid.w) 225 | this.write(`${this.bpm}${this.orca.f % 4 === 0 ? '*' : ''}`, col * 3, 0, this.size.grid.w) 226 | this.write(`${this.io}`, col * 4, 0, this.size.grid.w) 227 | } 228 | 229 | this.drawSprite = function (x, y, g, styles = { isCursor: false, isSelection: false, isPort: false, f: null, b: null }) { 230 | const ctx = this.context 231 | 232 | ctx.textBaseline = 'bottom' 233 | ctx.textAlign = 'center' 234 | ctx.font = `${tile.h * 0.75}px input_mono_medium` 235 | 236 | // Highlight Variables 237 | if (g === 'V' && this.cursor.read() === 'V') { 238 | ctx.fillStyle = this.theme.active.b_inv 239 | ctx.fillRect(x * tile.w, (y) * tile.h, tile.w, tile.h) 240 | ctx.fillStyle = this.theme.active.background 241 | } else if (styles.f && styles.b && this.theme.active[styles.f] && this.theme.active[styles.b]) { 242 | ctx.fillStyle = this.theme.active[styles.b] 243 | ctx.fillRect(x * tile.w, (y) * tile.h, tile.w, tile.h) 244 | ctx.fillStyle = this.theme.active[styles.f] 245 | } else if (styles.isSelection) { 246 | ctx.fillStyle = this.theme.active.b_inv 247 | ctx.fillRect(x * tile.w, (y) * tile.h, tile.w, tile.h) 248 | ctx.fillStyle = this.theme.active.f_inv 249 | } else if (styles.isPort) { 250 | if (styles.isPort === 'output') { // Output 251 | ctx.fillStyle = this.theme.active.b_high 252 | ctx.fillRect(x * tile.w, (y) * tile.h, tile.w, tile.h) 253 | ctx.fillStyle = this.theme.active.f_low 254 | } else if (styles.isPort === 'input') { // Input 255 | ctx.fillStyle = this.theme.active.b_high 256 | } else if (styles.isPort === 'passive') { // Passive 257 | ctx.fillStyle = this.theme.active.b_med 258 | ctx.fillRect(x * tile.w, (y) * tile.h, tile.w, tile.h) 259 | ctx.fillStyle = this.theme.active.f_low 260 | } else if (styles.isPort === 'haste') { // Haste 261 | ctx.fillStyle = this.theme.active.background 262 | ctx.fillRect(x * tile.w, (y) * tile.h, tile.w, tile.h) 263 | ctx.fillStyle = this.theme.active.b_med 264 | } else { 265 | ctx.fillStyle = this.theme.active.background 266 | ctx.fillRect(x * tile.w, (y) * tile.h, tile.w, tile.h) 267 | ctx.fillStyle = this.theme.active.f_high 268 | } 269 | } else if (styles.isLocked) { 270 | ctx.fillStyle = this.theme.active.f_med 271 | } else { 272 | ctx.fillStyle = this.theme.active.f_low 273 | } 274 | ctx.fillText(styles.isCursor && (g === '.' || g === '+') ? (!this.isPaused ? '@' : '~') : g, (x + 0.5) * tile.w, (y + 1) * tile.h) 275 | } 276 | 277 | this.write = function (text, offsetX, offsetY, limit) { 278 | let x = 0 279 | while (x < text.length && x < limit - 1) { 280 | const c = text.substr(x, 1) 281 | this.drawSprite(offsetX + x, this.orca.h + offsetY, c, { f: 'f_high', b: 'background' }) 282 | x += 1 283 | } 284 | } 285 | 286 | this.align = function () { 287 | this.el.style.marginTop = (((window.innerHeight - (terminal.size.height * terminal.size.ratio)) / 2) - 20) + 'px' 288 | } 289 | 290 | this.resize = function (resizeWindow = true) { 291 | this.size.width = tile.w * this.orca.w 292 | this.size.height = tile.h * this.orca.h + (tile.h * 3) 293 | this.el.width = this.size.width 294 | this.el.height = this.size.height + tile.h 295 | this.el.style.width = (this.size.width * this.size.ratio) + 'px' 296 | this.el.style.height = (this.size.height * this.size.ratio) + 'px' 297 | 298 | this.align() 299 | 300 | if (resizeWindow === true) { 301 | const { remote } = require('electron') 302 | const win = remote.getCurrentWindow() 303 | const width = parseInt((this.size.width * this.size.ratio) + 60) 304 | const height = parseInt((this.size.height * this.size.ratio) + 30) 305 | const size = win.getSize() 306 | if (width > size[0]) { 307 | win.setSize(width, height, true) 308 | } 309 | } 310 | } 311 | 312 | function clamp (v, min, max) { return v < min ? min : v > max ? max : v } 313 | } 314 | 315 | module.exports = Terminal 316 | -------------------------------------------------------------------------------- /docs.js: -------------------------------------------------------------------------------- 1 | const lib = require('./desktop/core/library') 2 | 3 | console.log("## Operators\n") 4 | 5 | for(const id in lib){ 6 | const operator = new lib[id]() 7 | if(operator.glyph === '.'){ continue; } 8 | console.log(`- ${operator.docs()}`) 9 | } 10 | 11 | console.log("\n") -------------------------------------------------------------------------------- /examples/_midi.orca: -------------------------------------------------------------------------------- 1 | ......................................... 2 | .#.MIDI.#................................ 3 | ......................................... 4 | ......................................... 5 | ......................................... 6 | .............################............ 7 | .............#..............#............ 8 | ..4C4........#..Channel..1..#............ 9 | .D414TCDFE...#..Octave...5..#............ 10 | ..:05D.......#..Notes.CDFE..#............ 11 | .............#..............#............ 12 | .............################............ 13 | ......................................... 14 | ......................................... 15 | ......................................... 16 | ......................................... 17 | ......................................... -------------------------------------------------------------------------------- /examples/_osc.orca: -------------------------------------------------------------------------------- 1 | ......................................... 2 | .#.OSC.#................................. 3 | ......................................... 4 | .#.VALUES.#.............................. 5 | ......................................... 6 | .D8...................................... 7 | .*=a123.................................. 8 | ......................................... 9 | .#.EMPTY.#............................... 10 | ......................................... 11 | .D8...................................... 12 | .*=b..................................... 13 | ......................................... 14 | ......................................... 15 | ......................................... 16 | ......................................... 17 | ......................................... -------------------------------------------------------------------------------- /examples/_udp.orca: -------------------------------------------------------------------------------- 1 | ......................................... 2 | .#.UDP.#................................. 3 | ......................................... 4 | ......................................... 5 | ....Cf......Cf........................... 6 | ...F10.....F12........................... 7 | ..B.H.....B.H............................ 8 | ...xS......xS............................ 9 | ......................................... 10 | ...S..................................... 11 | ..5;HELLO.4;ORCA......................... 12 | ......................................... 13 | ......................................... 14 | ......................................... 15 | ......................................... 16 | ......................................... 17 | ......................................... -------------------------------------------------------------------------------- /examples/bang.orca: -------------------------------------------------------------------------------- 1 | ......................................... 2 | .#.BANG.#................................ 3 | ......................................... 4 | ......................................... 5 | ......................................... 6 | ......................................... 7 | ......................................... 8 | ..............2D.H....................... 9 | ................xE....................... 10 | ........................#.BANG.#......... 11 | ......................................... 12 | ......................................... 13 | ......................................... 14 | ......................................... 15 | ......................................... 16 | ......................................... 17 | ......................................... -------------------------------------------------------------------------------- /examples/benchmark.orca: -------------------------------------------------------------------------------- 1 | ......................................... 2 | .#.BENCHMARK.#........................... 3 | ......................................... 4 | .4C4......C4......3Ka.b.3Ka.b............ 5 | ..24T02S..24T02S....SC0...SD0............ 6 | ..aVS.....bV2......cV0................... 7 | ......................................... 8 | .2Kab..2Kab..2Kab..2Kab..2Kab............ 9 | ..AS0...FS0...IS0...MS0...RS0............ 10 | ..s...........r.....0.....h.............. 11 | ......................................... 12 | ......................................... 13 | ......................................... 14 | ......................................... 15 | ......................................... 16 | ......................................... 17 | ......................................... -------------------------------------------------------------------------------- /examples/boolean.orca: -------------------------------------------------------------------------------- 1 | ......................................... 2 | ..#.BOOLEAN.#............................ 3 | ......................................... 4 | ..#.OR.#..#.NOR.#..#AND.#..#.XOR.#....... 5 | ......................................... 6 | ....R01......R01....6C2.......R01........ 7 | ..A11......A01.....1X1......1X0.......... 8 | ..23T011...13T100...4C2......4C2......... 9 | ....1........0.....A10......A00.......... 10 | ...................13T001...03T010....... 11 | .....................0......F10.......... 12 | ..#.NOT.#................................ 13 | ......................................... 14 | ..4C2.................................... 15 | ...02T10................................. 16 | .....1................................... 17 | ......................................... -------------------------------------------------------------------------------- /examples/cycle.orca: -------------------------------------------------------------------------------- 1 | ......................................... 2 | .#.CYCLE.#............................... 3 | ......................................... 4 | ......................................... 5 | ......................................... 6 | ......................U.................. 7 | ...............U......................... 8 | ................N........................ 9 | ......................................... 10 | ......................................... 11 | .......................U................. 12 | ................U........................ 13 | ......................................... 14 | ......................................... 15 | ......................................... 16 | ......................................... 17 | ......................................... -------------------------------------------------------------------------------- /examples/delay.orca: -------------------------------------------------------------------------------- 1 | ......................................... 2 | .#.DELAY.#............................... 3 | ......................................... 4 | ......................................... 5 | ......................................... 6 | ......................................... 7 | ........C4.....4C4.....4C4.....4C8....... 8 | ........1.......1.......1.......5........ 9 | ......................................... 10 | ......................................... 11 | ........D2......D4.....4D4.....4D8....... 12 | ......................................... 13 | ......................................... 14 | ......................................... 15 | ......................................... 16 | ......................................... 17 | ......................................... -------------------------------------------------------------------------------- /examples/if+else.orca: -------------------------------------------------------------------------------- 1 | ......................................... 2 | .#.IF.ELSE.#............................. 3 | ......................................... 4 | ..4C8..2C4............................... 5 | .aV2..bV0...#.ASSIGN.#................... 6 | ......................................... 7 | ..2Kab...V1.............................. 8 | ...F13......#.IF.#....................... 9 | .1V.Y.................................... 10 | .....J...V0.............................. 11 | ....F....*..#.ELSE.#..................... 12 | ..0V*.................................... 13 | ......................................... 14 | ......................................... 15 | ......................................... 16 | ......................................... 17 | ......................................... -------------------------------------------------------------------------------- /examples/increment.orca: -------------------------------------------------------------------------------- 1 | ......................................... 2 | .#.INCREMENT.#........................... 3 | ......................................... 4 | ..03O.................................... 5 | .30Xc.................................... 6 | .....Ac2...Va............................ 7 | ...aV0...8Ly02468ac...................... 8 | ......................................... 9 | ..03O.................................... 10 | .30Xi.................................... 11 | .....Ai3...Vb............................ 12 | ...bV0...8Lx0369cfi...................... 13 | ......................................... 14 | ......................................... 15 | ......................................... 16 | ......................................... 17 | ......................................... -------------------------------------------------------------------------------- /examples/kombine.orca: -------------------------------------------------------------------------------- 1 | ......................................... 2 | .#.KOMBINE.#............................. 3 | ......................................... 4 | .4C4..................................... 5 | ..04T3454................................ 6 | ..oV3.#.OCTAVE.#......................... 7 | ......................................... 8 | .2C4..................................... 9 | ..04TCDEF...............3Konv............ 10 | ..nVC.#.NOTE.#............4F3............ 11 | ......................................... 12 | .1C4..................................... 13 | ..14T0123................................ 14 | ..vV0.#.VELOCITY.#....................... 15 | ......................................... 16 | ......................................... 17 | ......................................... -------------------------------------------------------------------------------- /examples/octave.orca: -------------------------------------------------------------------------------- 1 | ......................................... 2 | .#.OCTAVE.#.............................. 3 | ......................................... 4 | ......................................... 5 | ......................................... 6 | ......................................... 7 | .............4Cc......................... 8 | .............34cTCcDdEFfGgAaB............ 9 | ..............aVE........................ 10 | ......................................... 11 | ..............D4..Va..................... 12 | ...............:03E...................... 13 | ......................................... 14 | ......................................... 15 | ......................................... 16 | ......................................... 17 | ......................................... -------------------------------------------------------------------------------- /examples/pendulum.orca: -------------------------------------------------------------------------------- 1 | ......................................... 2 | .#.PENDULUM.#............................ 3 | ......................................... 4 | ......................................... 5 | ......................................... 6 | ............U........W.....U............. 7 | ....................J.................... 8 | ...................B.H................... 9 | ....................xS................... 10 | ......................................... 11 | ......................................... 12 | ......................................... 13 | ......................................... 14 | ......................................... 15 | ......................................... 16 | ......................................... 17 | ......................................... -------------------------------------------------------------------------------- /examples/popcorn.orca: -------------------------------------------------------------------------------- 1 | ......................................... 2 | .#.POPCORN.#............................. 3 | ......................................... 4 | ...C8.........8C4........................ 5 | .2H18T13579bdf.16T2345................... 6 | ...xV1.........yV3....................... 7 | ......................................... 8 | ..Vx.Vy.................................. 9 | ..fYf2................................... 10 | ....JJ................................... 11 | ...Hf22Q................................. 12 | ...*:04C................................. 13 | ........#5C4a5C4G4d4G4C..#............... 14 | ........#5C4a5C4G4d4G4C..#............... 15 | ........#5C5D5d5D5d5C5D5C#............... 16 | ........#5D4a5C4a5C4g5C..#............... 17 | ......................................... -------------------------------------------------------------------------------- /examples/projects/dotgrid.orca: -------------------------------------------------------------------------------- 1 | ......................................... 2 | .#.DOTGRID.#............................. 3 | ......................................... 4 | .#.CLEAR.#...#.LINE.#..#.DRAW.#.......... 5 | ......................................... 6 | ..D2........D4........................... 7 | ..*.........*Y*.......................... 8 | ..;1........J6;1ca4ga.................... 9 | ............*Y*.......................... 10 | ............J6;1cgaag.................... 11 | ............*Y*.......................... 12 | ............J6;1cag4a.................... 13 | ............*Y*..........D2.............. 14 | .............6;1c4aa4..6;*............... 15 | ......................................... 16 | ......................................... 17 | ......................................... -------------------------------------------------------------------------------- /examples/projects/midi-step-sequencer.orca: -------------------------------------------------------------------------------- 1 | ......................................... 2 | .#.note.sequencer.#...................... 3 | ......................................... 4 | ......................................... 5 | ..U...................................... 6 | .....W.U................................. 7 | ......................................... 8 | ..S...................................... 9 | ......................................... 10 | ......................................... 11 | ..S...NYNYNYN11G0........................ 12 | .......Y.Y.Y.01G1....................V1.. 13 | ..........J....0....................A21.. 14 | .......15X.........................M34... 15 | .................................1V3..... 16 | ...........V1.#.notes.#.................. 17 | .U.........24TABCE....................... 18 | ......U...:03CCF......................... 19 | ..........N.............................. 20 | ......................................... 21 | ......................................... 22 | ......................................... 23 | ......................................... 24 | ......................................... 25 | ......................................... -------------------------------------------------------------------------------- /examples/projects/sonicpi.orca: -------------------------------------------------------------------------------- 1 | ......................................... 2 | .#.SONICPI.#............................. 3 | ......................................... 4 | ......................................... 5 | ..C8..................................... 6 | ..28T*................................... 7 | ......................................... 8 | ....=a................................... 9 | ......................................... 10 | ......................................... 11 | .4C8..................................... 12 | ..68T..*...*............................. 13 | ....*.................................... 14 | ....=b................................... 15 | ......................................... 16 | ......................................... 17 | ......................................... -------------------------------------------------------------------------------- /examples/read+write.orca: -------------------------------------------------------------------------------- 1 | ......................................... 2 | .#.READ.WRITE.#.......................... 3 | ......................................... 4 | ......................................... 5 | ......................................... 6 | ......C4.........C2..........C4.......... 7 | ......10O1230...014Q1234.....14T1230..... 8 | ........1.......12345678.......1......... 9 | ......................................... 10 | ......................................... 11 | ......C4.C4......C2..........C4.C4....... 12 | ......10X1......014G1234.....14P1........ 13 | ........1230.......1234........1230...... 14 | ...................1234.................. 15 | ......................................... 16 | ......................................... 17 | ......................................... -------------------------------------------------------------------------------- /examples/subtraction.orca: -------------------------------------------------------------------------------- 1 | ......................................... 2 | .#.SUBTRACTION.#......................... 3 | ......................................... 4 | .13X9.#.A.#.............................. 5 | .01X4.#.B.#.............................. 6 | ......................................... 7 | ...4zT0zyxwvutsrqponmlkjihgfedcba98765432 8 | ...A9w................................... 9 | .rV5..................................... 10 | ............Vr........................... 11 | .#.RESULT.#.5............................ 12 | ......................................... 13 | ......................................... 14 | ......................................... 15 | ......................................... 16 | ......................................... -------------------------------------------------------------------------------- /examples/timing.orca: -------------------------------------------------------------------------------- 1 | ......................................... 2 | .#.TIMING.#.............................. 3 | ......................................... 4 | ..C8.C2........C8.D2..................... 5 | ..5.X1.........5.X....................... 6 | ...#10101010#...#.*.*.*.*#............... 7 | ..C8.C3........C8.D3..................... 8 | ..5.X1.........5.X....................... 9 | ...#01201012#...#*..*.*..#............... 10 | ..C8.C3........C8.D4..................... 11 | ..5.X1.........5.X....................... 12 | ...#01201012#...#...*...*#............... 13 | ..C8.C4........C8.D5..................... 14 | ..5.X1.........5.X....................... 15 | ...#12301230#...#..*..*..#............... 16 | ......................................... 17 | ......................................... -------------------------------------------------------------------------------- /examples/triage.orca: -------------------------------------------------------------------------------- 1 | ......................................... 2 | ..#.TRIAGE.#............................. 3 | ......................................... 4 | ..D2....D6......D6......D6............... 5 | ...H..................................... 6 | ..xE....u.......u.......u................ 7 | ...E.E.E.E.E...E.E.....E................. 8 | ............................U............ 9 | .......U...E.....E.....E................. 10 | ............................S.U.......... 11 | ...............U.E.....E.....E........... 12 | ........................S.......U........ 13 | .......................U.....E........... 14 | ......................................... 15 | ............................*.*.*........ 16 | ............................0.0.0........ 17 | ......................................... -------------------------------------------------------------------------------- /examples/variable.orca: -------------------------------------------------------------------------------- 1 | ......................................... 2 | .#.VARIABLES.#........................... 3 | ......................................... 4 | .#.WRITE.#............................... 5 | ......................................... 6 | .aV1.bV2.cV3............................. 7 | ......................................... 8 | .#.READ.#................................ 9 | ......................................... 10 | ..Va..Vb..Vc............................. 11 | ..1...2...3.............................. 12 | ......................................... 13 | ......................................... 14 | ......................................... 15 | ......................................... 16 | ......................................... 17 | ......................................... -------------------------------------------------------------------------------- /examples/wave.orca: -------------------------------------------------------------------------------- 1 | ......................................... 2 | .#.WAVE.#......Va........................ 3 | ...............6XE....................... 4 | ......................................... 5 | .8C8............................:04F..... 6 | ..2..C6.........................:04E..... 7 | ..J..56T123432..................:04D..... 8 | ..2Y2Y23........................:04C..... 9 | ......JJ........................:03B..... 10 | .....A23........................:03A..... 11 | ...aV5..........................:03G..... 12 | ................................:03F..... 13 | ................................:03E..... 14 | ................................:03D..... 15 | ................................:03C..... 16 | ......................................... 17 | ......................................... -------------------------------------------------------------------------------- /listener.js: -------------------------------------------------------------------------------- 1 | const UDP_PORT = 49160 2 | const OSC_PORT = 49162 3 | 4 | const dgram = require('dgram'); 5 | const udpserver = dgram.createSocket('udp4'); 6 | 7 | const osc = require('./desktop/node_modules/node-osc') 8 | const oscserver = new osc.Server(OSC_PORT, '127.0.0.1') 9 | 10 | console.log(`Started Listener\n\nUDP:${UDP_PORT}\nOSC:${OSC_PORT}\n`) 11 | 12 | // Error 13 | 14 | udpserver.on('error', (err) => { 15 | console.log(`UDP server:\n${err.stack}`); 16 | udpserver.close(); 17 | }) 18 | 19 | oscserver.on('error', (err) => { 20 | console.log(`OSC server:\n${err.stack}`); 21 | oscserver.close(); 22 | }) 23 | 24 | // Message 25 | 26 | udpserver.on('message', (msg, rinfo) => { 27 | console.log(`UDP server: ${msg} from ${rinfo.address}:${rinfo.port}`); 28 | }) 29 | 30 | oscserver.on('message', (msg, rinfo) => { 31 | console.log(`OSC server: ${msg} from ${rinfo.address}:${rinfo.port} at /${msg[0]}`) 32 | }) 33 | 34 | udpserver.bind(UDP_PORT) -------------------------------------------------------------------------------- /resources/glyph.grid: -------------------------------------------------------------------------------- 1 | { 2 | "settings": { 3 | "size": { 4 | "width": 870, 5 | "height": 300 6 | } 7 | }, 8 | "layers": [ 9 | [], 10 | [ 11 | { 12 | "type": "arc_c", 13 | "vertices": [ 14 | { 15 | "x": 165, 16 | "y": 90 17 | }, 18 | { 19 | "x": 240, 20 | "y": 165 21 | }, 22 | { 23 | "x": 165, 24 | "y": 240 25 | }, 26 | { 27 | "x": 90, 28 | "y": 165 29 | }, 30 | { 31 | "x": 165, 32 | "y": 90 33 | } 34 | ] 35 | }, 36 | { 37 | "type": "close", 38 | "vertices": [] 39 | }, 40 | { 41 | "type": "arc_c_full", 42 | "vertices": [ 43 | { 44 | "x": 600, 45 | "y": 165 46 | }, 47 | { 48 | "x": 525, 49 | "y": 90 50 | } 51 | ] 52 | }, 53 | { 54 | "type": "arc_c_full", 55 | "vertices": [ 56 | { 57 | "x": 630, 58 | "y": 165 59 | }, 60 | { 61 | "x": 705, 62 | "y": 240 63 | } 64 | ] 65 | }, 66 | { 67 | "type": "arc_c_full", 68 | "vertices": [ 69 | { 70 | "x": 345, 71 | "y": 240 72 | }, 73 | { 74 | "x": 420, 75 | "y": 165 76 | } 77 | ] 78 | } 79 | ], 80 | [ 81 | { 82 | "type": "line", 83 | "vertices": [ 84 | { 85 | "x": 255, 86 | "y": 75 87 | }, 88 | { 89 | "x": 255, 90 | "y": 255 91 | } 92 | ] 93 | }, 94 | { 95 | "type": "line", 96 | "vertices": [ 97 | { 98 | "x": 435, 99 | "y": 75 100 | }, 101 | { 102 | "x": 435, 103 | "y": 255 104 | } 105 | ] 106 | }, 107 | { 108 | "type": "line", 109 | "vertices": [ 110 | { 111 | "x": 615, 112 | "y": 75 113 | }, 114 | { 115 | "x": 615, 116 | "y": 255 117 | } 118 | ] 119 | }, 120 | { 121 | "type": "line", 122 | "vertices": [ 123 | { 124 | "x": 795, 125 | "y": 75 126 | }, 127 | { 128 | "x": 795, 129 | "y": 255 130 | } 131 | ] 132 | }, 133 | { 134 | "type": "line", 135 | "vertices": [ 136 | { 137 | "x": 75, 138 | "y": 75 139 | }, 140 | { 141 | "x": 75, 142 | "y": 255 143 | } 144 | ] 145 | } 146 | ] 147 | ], 148 | "styles": [ 149 | { 150 | "thickness": 31, 151 | "strokeLinecap": "round", 152 | "strokeLinejoin": "round", 153 | "color": "#000", 154 | "fill": "none", 155 | "mirror_style": 0 156 | }, 157 | { 158 | "thickness": 30, 159 | "strokeLinecap": "butt", 160 | "strokeLinejoin": "bevel", 161 | "color": "#000", 162 | "fill": "none", 163 | "mirror_style": 0 164 | }, 165 | { 166 | "thickness": 1, 167 | "strokeLinecap": "round", 168 | "strokeLinejoin": "round", 169 | "color": "#ddd", 170 | "fill": "none", 171 | "mirror_style": 0 172 | } 173 | ] 174 | } -------------------------------------------------------------------------------- /resources/glyph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamstah/Orca/cd4f96db307dbdcc1d4f687f7f70ed47dfd0bbe7/resources/glyph.png -------------------------------------------------------------------------------- /resources/glyph.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/logo.grid: -------------------------------------------------------------------------------- 1 | { 2 | "settings": { 3 | "size": { 4 | "width": 855, 5 | "height": 300 6 | } 7 | }, 8 | "layers": [ 9 | [ 10 | { 11 | "type": "arc_r", 12 | "vertices": [ 13 | { 14 | "x": 585, 15 | "y": 120 16 | }, 17 | { 18 | "x": 570, 19 | "y": 105 20 | }, 21 | { 22 | "x": 480, 23 | "y": 105 24 | }, 25 | { 26 | "x": 465, 27 | "y": 120 28 | }, 29 | { 30 | "x": 465, 31 | "y": 180 32 | }, 33 | { 34 | "x": 480, 35 | "y": 195 36 | }, 37 | { 38 | "x": 570, 39 | "y": 195 40 | }, 41 | { 42 | "x": 585, 43 | "y": 180 44 | } 45 | ] 46 | }, 47 | { 48 | "type": "arc_c", 49 | "vertices": [ 50 | { 51 | "x": 285, 52 | "y": 105 53 | }, 54 | { 55 | "x": 390, 56 | "y": 105 57 | }, 58 | { 59 | "x": 405, 60 | "y": 120 61 | }, 62 | { 63 | "x": 405, 64 | "y": 135 65 | }, 66 | { 67 | "x": 390, 68 | "y": 150 69 | }, 70 | { 71 | "x": 285, 72 | "y": 150 73 | } 74 | ] 75 | }, 76 | { 77 | "type": "arc_c", 78 | "vertices": [ 79 | { 80 | "x": 390, 81 | "y": 150 82 | }, 83 | { 84 | "x": 405, 85 | "y": 165 86 | }, 87 | { 88 | "x": 405, 89 | "y": 195 90 | } 91 | ] 92 | }, 93 | { 94 | "type": "arc_c", 95 | "vertices": [ 96 | { 97 | "x": 645, 98 | "y": 105 99 | }, 100 | { 101 | "x": 750, 102 | "y": 105 103 | }, 104 | { 105 | "x": 765, 106 | "y": 120 107 | }, 108 | { 109 | "x": 765, 110 | "y": 195 111 | } 112 | ] 113 | }, 114 | { 115 | "type": "arc_c", 116 | "vertices": [ 117 | { 118 | "x": 765, 119 | "y": 135 120 | }, 121 | { 122 | "x": 750, 123 | "y": 150 124 | } 125 | ] 126 | }, 127 | { 128 | "type": "arc_r", 129 | "vertices": [ 130 | { 131 | "x": 750, 132 | "y": 150 133 | }, 134 | { 135 | "x": 660, 136 | "y": 150 137 | }, 138 | { 139 | "x": 645, 140 | "y": 165 141 | }, 142 | { 143 | "x": 645, 144 | "y": 165 145 | }, 146 | { 147 | "x": 645, 148 | "y": 180 149 | }, 150 | { 151 | "x": 660, 152 | "y": 195 153 | }, 154 | { 155 | "x": 750, 156 | "y": 195 157 | }, 158 | { 159 | "x": 765, 160 | "y": 180 161 | } 162 | ] 163 | }, 164 | { 165 | "type": "line", 166 | "vertices": [ 167 | { 168 | "x": 285, 169 | "y": 105 170 | }, 171 | { 172 | "x": 285, 173 | "y": 195 174 | } 175 | ] 176 | }, 177 | { 178 | "type": "arc_c", 179 | "vertices": [ 180 | { 181 | "x": 225, 182 | "y": 180 183 | }, 184 | { 185 | "x": 210, 186 | "y": 195 187 | }, 188 | { 189 | "x": 120, 190 | "y": 195 191 | }, 192 | { 193 | "x": 105, 194 | "y": 180 195 | }, 196 | { 197 | "x": 105, 198 | "y": 120 199 | }, 200 | { 201 | "x": 120, 202 | "y": 105 203 | }, 204 | { 205 | "x": 210, 206 | "y": 105 207 | }, 208 | { 209 | "x": 225, 210 | "y": 120 211 | } 212 | ] 213 | }, 214 | { 215 | "type": "close", 216 | "vertices": [] 217 | } 218 | ], 219 | [], 220 | [ 221 | { 222 | "type": "line", 223 | "vertices": [ 224 | { 225 | "x": 255, 226 | "y": 75 227 | }, 228 | { 229 | "x": 255, 230 | "y": 225 231 | } 232 | ] 233 | }, 234 | { 235 | "type": "line", 236 | "vertices": [ 237 | { 238 | "x": 435, 239 | "y": 75 240 | }, 241 | { 242 | "x": 435, 243 | "y": 225 244 | } 245 | ] 246 | }, 247 | { 248 | "type": "line", 249 | "vertices": [ 250 | { 251 | "x": 615, 252 | "y": 75 253 | }, 254 | { 255 | "x": 615, 256 | "y": 225 257 | } 258 | ] 259 | } 260 | ] 261 | ], 262 | "styles": [ 263 | { 264 | "thickness": 12, 265 | "strokeLinecap": "square", 266 | "strokeLinejoin": "round", 267 | "color": "#000", 268 | "fill": "none", 269 | "mirror_style": 0 270 | }, 271 | { 272 | "thickness": 5, 273 | "strokeLinecap": "round", 274 | "strokeLinejoin": "round", 275 | "color": "#f00", 276 | "fill": "none", 277 | "mirror_style": 0 278 | }, 279 | { 280 | "thickness": 2, 281 | "strokeLinecap": "round", 282 | "strokeLinejoin": "round", 283 | "color": "#ddd", 284 | "fill": "none", 285 | "mirror_style": 0 286 | } 287 | ] 288 | } -------------------------------------------------------------------------------- /resources/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamstah/Orca/cd4f96db307dbdcc1d4f687f7f70ed47dfd0bbe7/resources/logo.png -------------------------------------------------------------------------------- /resources/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/preview.hardware.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamstah/Orca/cd4f96db307dbdcc1d4f687f7f70ed47dfd0bbe7/resources/preview.hardware.jpg -------------------------------------------------------------------------------- /resources/preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamstah/Orca/cd4f96db307dbdcc1d4f687f7f70ed47dfd0bbe7/resources/preview.jpg --------------------------------------------------------------------------------