From 1248348d461860c5009ce506986116ac098d3deb Mon Sep 17 00:00:00 2001 From: unthingable Date: Sun, 25 Oct 2020 23:59:33 +0100 Subject: [PATCH 1/4] add incrementally loadable language libraries --- README.md | 27 +++---- desktop/sources/index.html | 6 +- desktop/sources/scripts/client.js | 13 +++- desktop/sources/scripts/commander.js | 25 +++++- desktop/sources/scripts/core/library/base.js | 20 +++++ .../core/{library.js => library/default.js} | 76 ++++++++++--------- .../sources/scripts/core/library/library.js | 3 + desktop/sources/scripts/core/orca.js | 5 +- 8 files changed, 117 insertions(+), 58 deletions(-) create mode 100644 desktop/sources/scripts/core/library/base.js rename desktop/sources/scripts/core/{library.js => library/default.js} (89%) create mode 100644 desktop/sources/scripts/core/library/library.js diff --git a/README.md b/README.md index 5bf11a15..4adf8db0 100644 --- a/README.md +++ b/README.md @@ -70,13 +70,13 @@ To display the list of operators inside of Orca, use `CmdOrCtrl+G`. ## MIDI -The [MIDI](https://en.wikipedia.org/wiki/MIDI) operator `:` takes up to 5 inputs('channel, 'octave, 'note, velocity, length). +The [MIDI](https://en.wikipedia.org/wiki/MIDI) operator `:` takes up to 5 inputs('channel, 'octave, 'note, velocity, length). 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 `g`(127/127). Note length is the number of frames during which a note remains active. See it in action with [midi.orca](https://git.sr.ht/~rabbits/orca-examples/tree/master/basics/_midi.orca). ## MIDI MONO -The [MONO](https://en.wikipedia.org/wiki/Monophony) operator `%` takes up to 5 inputs('channel, 'octave, 'note, velocity, length). +The [MONO](https://en.wikipedia.org/wiki/Monophony) operator `%` takes up to 5 inputs('channel, 'octave, 'note, velocity, length). This operator is very similar to the default Midi operator, but **each new note will stop the previously playing note**, would its length overlap with the new one. Making certain that only a single note is ever played at once, this is ideal for monophonic analog synthetisers that might struggle to dealing with chords and note overlaps. @@ -94,9 +94,9 @@ It sends two different values **between 0-127**, where the value is calculated a ## MIDI BANK SELECT / PROGRAM CHANGE -This is a command (see below) rather than an operator and it combines the [MIDI program change and bank select functions](https://www.sweetwater.com/sweetcare/articles/6-what-msb-lsb-refer-for-changing-banks-andprograms/). +This is a command (see below) rather than an operator and it combines the [MIDI program change and bank select functions](https://www.sweetwater.com/sweetcare/articles/6-what-msb-lsb-refer-for-changing-banks-andprograms/). -The syntax is `pg:channel;msb;lsb;program`. Channel is 0-15, msb/lsb/program are 0-127, but program will automatically be translated to 1-128 by the MIDI driver. `program` typically correspondes to a "patch" selection on a synth. Note that `msb` may also be identified as "bank" and `lsb` as "sub" in some applications (like Ableton Live). +The syntax is `pg:channel;msb;lsb;program`. Channel is 0-15, msb/lsb/program are 0-127, but program will automatically be translated to 1-128 by the MIDI driver. `program` typically correspondes to a "patch" selection on a synth. Note that `msb` may also be identified as "bank" and `lsb` as "sub" in some applications (like Ableton Live). `msb` and `lsb` can be left blank if you only want to send a simple program change. For example, `pg:0;;;63` will set the synth to patch number 64 (without changing the bank) @@ -149,30 +149,31 @@ All commands have a shorthand equivalent to their first two characters, for exam - `midi:1;2` Set Midi output device to `#1`, and input device to `#2`. - `udp:1234;5678` Set UDP output port to `1234`, and input port to `5678`. - `osc:1234` Set OSC output port to `1234`. +- `lang:clr;default` Incrementally load language interpreters (`clr` clears the language library entirely). Multiple languages can be combined. ## Base36 Table -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 `Do` will bang every *24th frame*. +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 `Do` will bang every *24th frame*. -| **0** | **1** | **2** | **3** | **4** | **5** | **6** | **7** | **8** | **9** | **A** | **B** | -| :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | +| **0** | **1** | **2** | **3** | **4** | **5** | **6** | **7** | **8** | **9** | **A** | **B** | +| :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | | **C** | **D** | **E** | **F** | **G** | **H** | **I** | **J** | **K** | **L** | **M** | **N** | | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -| **O** | **P** | **Q** | **R** | **S** | **T** | **U** | **V** | **W** | **X** | **Y** | **Z** | +| **O** | **P** | **Q** | **R** | **S** | **T** | **U** | **V** | **W** | **X** | **Y** | **Z** | | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | ## Transpose Table The midi operator interprets any letter above the chromatic scale as a transpose value, for instance `3H`, is equivalent to `4A`. -| **0** | **1** | **2** | **3** | **4** | **5** | **6** | **7** | **8** | **9** | **A** | **B** | -| :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | +| **0** | **1** | **2** | **3** | **4** | **5** | **6** | **7** | **8** | **9** | **A** | **B** | +| :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | A0 | B0 | | **C** | **D** | **E** | **F** | **G** | **H** | **I** | **J** | **K** | **L** | **M** | **N** | -| C0 | D0 | E0 | F0 | G0 | A0 | B0 | C1 | D1 | E1 | F1 | G1 | -| **O** | **P** | **Q** | **R** | **S** | **T** | **U** | **V** | **W** | **X** | **Y** | **Z** | -| A1 | B1 | C2 | D2 | E2 | F2 | G2 | A2 | B2 | C3 | D3 | E3 | +| C0 | D0 | E0 | F0 | G0 | A0 | B0 | C1 | D1 | E1 | F1 | G1 | +| **O** | **P** | **Q** | **R** | **S** | **T** | **U** | **V** | **W** | **X** | **Y** | **Z** | +| A1 | B1 | C2 | D2 | E2 | F2 | G2 | A2 | B2 | C3 | D3 | E3 | ## Companion Applications diff --git a/desktop/sources/index.html b/desktop/sources/index.html index 29628bb2..81334acd 100644 --- a/desktop/sources/index.html +++ b/desktop/sources/index.html @@ -6,7 +6,9 @@ - + + + @@ -30,7 +32,7 @@ client.install(document.body) - window.addEventListener('load', () => { + window.addEventListener('load', () => { client.start() client.acels.inject('Orca') }) diff --git a/desktop/sources/scripts/client.js b/desktop/sources/scripts/client.js index f1e47062..37b39ed1 100644 --- a/desktop/sources/scripts/client.js +++ b/desktop/sources/scripts/client.js @@ -12,8 +12,9 @@ /* global Theme */ function Client () { - this.version = 176 - this.library = library + this.version = 177 + this.libraryName = 'default' + this.library = library[this.libraryName] this.theme = new Theme(this) this.acels = new Acels(this) @@ -331,7 +332,13 @@ function Client () { } else { this.write(this.orca.f < 25 ? `ver${this.version}` : `${Object.keys(this.source.cache).length} mods`, this.grid.w * 0, this.orca.h + 1, this.grid.w) this.write(`${this.orca.w}x${this.orca.h}`, this.grid.w * 1, this.orca.h + 1, this.grid.w) - this.write(`${this.grid.w}/${this.grid.h}${this.tile.w !== 10 ? ' ' + (this.tile.w / 10).toFixed(1) : ''}`, this.grid.w * 2, this.orca.h + 1, this.grid.w) + this.write( + this.orca.f < 25 + ? `${this.libraryName}` + : `${this.grid.w}/${this.grid.h}${this.tile.w !== 10 ? ' ' + (this.tile.w / 10).toFixed(1) : ''}`, + this.grid.w * 2, + this.orca.h + 1, + this.grid.w) this.write(`${this.clock}`, this.grid.w * 3, this.orca.h + 1, this.grid.w, this.clock.isPuppet ? 3 : this.io.midi.isClock ? 11 : this.clock.isPaused ? 20 : 2) this.write(`${display(Object.keys(this.orca.variables).join(''), this.orca.f, this.grid.w - 1)}`, this.grid.w * 4, this.orca.h + 1, this.grid.w - 1) this.write(this.orca.f < 250 ? `> ${this.io.midi.toOutputString()}` : '', this.grid.w * 5, this.orca.h + 1, this.grid.w * 4) diff --git a/desktop/sources/scripts/commander.js b/desktop/sources/scripts/commander.js index 71ee1dd1..a65e2f32 100644 --- a/desktop/sources/scripts/commander.js +++ b/desktop/sources/scripts/commander.js @@ -1,5 +1,7 @@ 'use strict' +/* global library */ + function Commander (client) { this.isActive = false this.query = '' @@ -70,7 +72,28 @@ function Commander (client) { }, write: (p) => { client.orca.writeBlock(p._x || client.cursor.x, p._y || client.cursor.y, p._str) - } + }, + // Language + lang: (p) => { + p.parts.forEach(l => { + if (l === 'clr') { + console.log('Clearing lang'), + client.library = {} + client.libraryName = '---' + } else { + if (l in library) { + console.log(`Incrementally loading lang: ${l}`) + client.library = Object.assign({}, client.library, library[l]) + client.libraryName = l + } else { + console.error(`Lang ${l} is not defined`) + return + } + } + client.orca.library = client.library + client.clock.setFrame(0) + }) + }, } // Make shorthands diff --git a/desktop/sources/scripts/core/library/base.js b/desktop/sources/scripts/core/library/base.js new file mode 100644 index 00000000..02955888 --- /dev/null +++ b/desktop/sources/scripts/core/library/base.js @@ -0,0 +1,20 @@ +`use strict` + +/* global Operator */ +/* global library */ + +library.base = {} + +for (let i = 0; i <= 9; i++) { + library.base[`${i}`] = function OperatorNull (orca, x, y, passive) { + Operator.call(this, orca, x, y, '.', false) + + this.name = 'null' + this.info = 'empty' + + // Overwrite run, to disable draw. + this.run = function (force = false) { + + } + } +} \ No newline at end of file diff --git a/desktop/sources/scripts/core/library.js b/desktop/sources/scripts/core/library/default.js similarity index 89% rename from desktop/sources/scripts/core/library.js rename to desktop/sources/scripts/core/library/default.js index 9319ec18..fe0947ed 100644 --- a/desktop/sources/scripts/core/library.js +++ b/desktop/sources/scripts/core/library/default.js @@ -2,10 +2,12 @@ /* global Operator */ /* global client */ +/* global library */ -const library = {} +library.default = {} +var defaultLib = library.default -library.a = function OperatorA (orca, x, y, passive) { +defaultLib.a = function OperatorA (orca, x, y, passive) { Operator.call(this, orca, x, y, 'a', passive) this.name = 'add' @@ -22,7 +24,7 @@ library.a = function OperatorA (orca, x, y, passive) { } } -library.b = function OperatorL (orca, x, y, passive) { +defaultLib.b = function OperatorL (orca, x, y, passive) { Operator.call(this, orca, x, y, 'b', passive) this.name = 'subtract' @@ -39,7 +41,7 @@ library.b = function OperatorL (orca, x, y, passive) { } } -library.c = function OperatorC (orca, x, y, passive) { +defaultLib.c = function OperatorC (orca, x, y, passive) { Operator.call(this, orca, x, y, 'c', passive) this.name = 'clock' @@ -57,7 +59,7 @@ library.c = function OperatorC (orca, x, y, passive) { } } -library.d = function OperatorD (orca, x, y, passive) { +defaultLib.d = function OperatorD (orca, x, y, passive) { Operator.call(this, orca, x, y, 'd', passive) this.name = 'delay' @@ -75,7 +77,7 @@ library.d = function OperatorD (orca, x, y, passive) { } } -library.e = function OperatorE (orca, x, y, passive) { +defaultLib.e = function OperatorE (orca, x, y, passive) { Operator.call(this, orca, x, y, 'e', passive) this.name = 'east' @@ -88,7 +90,7 @@ library.e = function OperatorE (orca, x, y, passive) { } } -library.f = function OperatorF (orca, x, y, passive) { +defaultLib.f = function OperatorF (orca, x, y, passive) { Operator.call(this, orca, x, y, 'f', passive) this.name = 'if' @@ -105,7 +107,7 @@ library.f = function OperatorF (orca, x, y, passive) { } } -library.g = function OperatorG (orca, x, y, passive) { +defaultLib.g = function OperatorG (orca, x, y, passive) { Operator.call(this, orca, x, y, 'g', passive) this.name = 'generator' @@ -130,7 +132,7 @@ library.g = function OperatorG (orca, x, y, passive) { } } -library.h = function OperatorH (orca, x, y, passive) { +defaultLib.h = function OperatorH (orca, x, y, passive) { Operator.call(this, orca, x, y, 'h', passive) this.name = 'halt' @@ -144,7 +146,7 @@ library.h = function OperatorH (orca, x, y, passive) { } } -library.i = function OperatorI (orca, x, y, passive) { +defaultLib.i = function OperatorI (orca, x, y, passive) { Operator.call(this, orca, x, y, 'i', passive) this.name = 'increment' @@ -162,7 +164,7 @@ library.i = function OperatorI (orca, x, y, passive) { } } -library.j = function OperatorJ (orca, x, y, passive) { +defaultLib.j = function OperatorJ (orca, x, y, passive) { Operator.call(this, orca, x, y, 'j', passive) this.name = 'jumper' @@ -177,7 +179,7 @@ library.j = function OperatorJ (orca, x, y, passive) { } } -library.k = function OperatorK (orca, x, y, passive) { +defaultLib.k = function OperatorK (orca, x, y, passive) { Operator.call(this, orca, x, y, 'k', passive) this.name = 'konkat' @@ -201,7 +203,7 @@ library.k = function OperatorK (orca, x, y, passive) { } } -library.l = function OperatorL (orca, x, y, passive) { +defaultLib.l = function OperatorL (orca, x, y, passive) { Operator.call(this, orca, x, y, 'l', passive) this.name = 'lesser' @@ -218,7 +220,7 @@ library.l = function OperatorL (orca, x, y, passive) { } } -library.m = function OperatorM (orca, x, y, passive) { +defaultLib.m = function OperatorM (orca, x, y, passive) { Operator.call(this, orca, x, y, 'm', passive) this.name = 'multiply' @@ -235,7 +237,7 @@ library.m = function OperatorM (orca, x, y, passive) { } } -library.n = function OperatorN (orca, x, y, passive) { +defaultLib.n = function OperatorN (orca, x, y, passive) { Operator.call(this, orca, x, y, 'n', passive) this.name = 'north' @@ -248,7 +250,7 @@ library.n = function OperatorN (orca, x, y, passive) { } } -library.o = function OperatorO (orca, x, y, passive) { +defaultLib.o = function OperatorO (orca, x, y, passive) { Operator.call(this, orca, x, y, 'o', passive) this.name = 'read' @@ -266,7 +268,7 @@ library.o = function OperatorO (orca, x, y, passive) { } } -library.p = function OperatorP (orca, x, y, passive) { +defaultLib.p = function OperatorP (orca, x, y, passive) { Operator.call(this, orca, x, y, 'p', passive) this.name = 'push' @@ -287,7 +289,7 @@ library.p = function OperatorP (orca, x, y, passive) { } } -library.q = function OperatorQ (orca, x, y, passive) { +defaultLib.q = function OperatorQ (orca, x, y, passive) { Operator.call(this, orca, x, y, 'q', passive) this.name = 'query' @@ -312,7 +314,7 @@ library.q = function OperatorQ (orca, x, y, passive) { } } -library.r = function OperatorR (orca, x, y, passive) { +defaultLib.r = function OperatorR (orca, x, y, passive) { Operator.call(this, orca, x, y, 'r', passive) this.name = 'random' @@ -330,7 +332,7 @@ library.r = function OperatorR (orca, x, y, passive) { } } -library.s = function OperatorS (orca, x, y, passive) { +defaultLib.s = function OperatorS (orca, x, y, passive) { Operator.call(this, orca, x, y, 's', passive) this.name = 'south' @@ -343,7 +345,7 @@ library.s = function OperatorS (orca, x, y, passive) { } } -library.t = function OperatorT (orca, x, y, passive) { +defaultLib.t = function OperatorT (orca, x, y, passive) { Operator.call(this, orca, x, y, 't', passive) this.name = 'track' @@ -364,7 +366,7 @@ library.t = function OperatorT (orca, x, y, passive) { } } -library.u = function OperatorU (orca, x, y, passive) { +defaultLib.u = function OperatorU (orca, x, y, passive) { Operator.call(this, orca, x, y, 'u', passive) this.name = 'uclid' @@ -382,7 +384,7 @@ library.u = function OperatorU (orca, x, y, passive) { } } -library.v = function OperatorV (orca, x, y, passive) { +defaultLib.v = function OperatorV (orca, x, y, passive) { Operator.call(this, orca, x, y, 'v', passive) this.name = 'variable' @@ -405,7 +407,7 @@ library.v = function OperatorV (orca, x, y, passive) { } } -library.w = function OperatorW (orca, x, y, passive) { +defaultLib.w = function OperatorW (orca, x, y, passive) { Operator.call(this, orca, x, y, 'w', passive) this.name = 'west' @@ -418,7 +420,7 @@ library.w = function OperatorW (orca, x, y, passive) { } } -library.x = function OperatorX (orca, x, y, passive) { +defaultLib.x = function OperatorX (orca, x, y, passive) { Operator.call(this, orca, x, y, 'x', passive) this.name = 'write' @@ -436,7 +438,7 @@ library.x = function OperatorX (orca, x, y, passive) { } } -library.y = function OperatorY (orca, x, y, passive) { +defaultLib.y = function OperatorY (orca, x, y, passive) { Operator.call(this, orca, x, y, 'y', passive) this.name = 'jymper' @@ -451,7 +453,7 @@ library.y = function OperatorY (orca, x, y, passive) { } } -library.z = function OperatorZ (orca, x, y, passive) { +defaultLib.z = function OperatorZ (orca, x, y, passive) { Operator.call(this, orca, x, y, 'z', passive) this.name = 'lerp' @@ -472,7 +474,7 @@ library.z = function OperatorZ (orca, x, y, passive) { // Specials -library['*'] = function OperatorBang (orca, x, y, passive) { +defaultLib['*'] = function OperatorBang (orca, x, y, passive) { Operator.call(this, orca, x, y, '*', true) this.name = 'bang' @@ -485,7 +487,7 @@ library['*'] = function OperatorBang (orca, x, y, passive) { } } -library['#'] = function OperatorComment (orca, x, y, passive) { +defaultLib['#'] = function OperatorComment (orca, x, y, passive) { Operator.call(this, orca, x, y, '#', true) this.name = 'comment' @@ -503,7 +505,7 @@ library['#'] = function OperatorComment (orca, x, y, passive) { // IO -library.$ = function OperatorSelf (orca, x, y, passive) { +defaultLib.$ = function OperatorSelf (orca, x, y, passive) { Operator.call(this, orca, x, y, '*', true) this.name = 'self' @@ -526,7 +528,7 @@ library.$ = function OperatorSelf (orca, x, y, passive) { } } -library[':'] = function OperatorMidi (orca, x, y, passive) { +defaultLib[':'] = function OperatorMidi (orca, x, y, passive) { Operator.call(this, orca, x, y, ':', true) this.name = 'midi' @@ -561,7 +563,7 @@ library[':'] = function OperatorMidi (orca, x, y, passive) { } } -library['!'] = function OperatorCC (orca, x, y) { +defaultLib['!'] = function OperatorCC (orca, x, y) { Operator.call(this, orca, x, y, '!', true) this.name = 'cc' @@ -591,7 +593,7 @@ library['!'] = function OperatorCC (orca, x, y) { } } -library['?'] = function OperatorPB (orca, x, y) { +defaultLib['?'] = function OperatorPB (orca, x, y) { Operator.call(this, orca, x, y, '?', true) this.name = 'pb' @@ -621,7 +623,7 @@ library['?'] = function OperatorPB (orca, x, y) { } } -library['%'] = function OperatorMono (orca, x, y, passive) { +defaultLib['%'] = function OperatorMono (orca, x, y, passive) { Operator.call(this, orca, x, y, '%', true) this.name = 'mono' @@ -656,7 +658,7 @@ library['%'] = function OperatorMono (orca, x, y, passive) { } } -library['='] = function OperatorOsc (orca, x, y, passive) { +defaultLib['='] = function OperatorOsc (orca, x, y, passive) { Operator.call(this, orca, x, y, '=', true) this.name = 'osc' @@ -688,7 +690,7 @@ library['='] = function OperatorOsc (orca, x, y, passive) { } } -library[';'] = function OperatorUdp (orca, x, y, passive) { +defaultLib[';'] = function OperatorUdp (orca, x, y, passive) { Operator.call(this, orca, x, y, ';', true) this.name = 'udp' @@ -717,7 +719,7 @@ library[';'] = function OperatorUdp (orca, x, y, passive) { // Add numbers for (let i = 0; i <= 9; i++) { - library[`${i}`] = function OperatorNull (orca, x, y, passive) { + defaultLib[`${i}`] = function OperatorNull (orca, x, y, passive) { Operator.call(this, orca, x, y, '.', false) this.name = 'null' diff --git a/desktop/sources/scripts/core/library/library.js b/desktop/sources/scripts/core/library/library.js new file mode 100644 index 00000000..c1a79116 --- /dev/null +++ b/desktop/sources/scripts/core/library/library.js @@ -0,0 +1,3 @@ +'use strict' + +const library = {} \ No newline at end of file diff --git a/desktop/sources/scripts/core/orca.js b/desktop/sources/scripts/core/orca.js index e7530d7d..02159964 100644 --- a/desktop/sources/scripts/core/orca.js +++ b/desktop/sources/scripts/core/orca.js @@ -1,6 +1,7 @@ 'use strict' function Orca (library) { + this.library = library this.keys = '0123456789abcdefghijklmnopqrstuvwxyz'.split('') this.w = 1 // Default Width @@ -63,7 +64,7 @@ function Orca (library) { for (let x = 0; x < this.w; x++) { const g = this.glyphAt(x, y) if (g === '.' || !this.isAllowed(g)) { continue } - a.push(new library[g.toLowerCase()](this, x, y, g === g.toUpperCase())) + a.push(new this.library[g.toLowerCase()](this, x, y, g === g.toUpperCase())) } } return a @@ -146,7 +147,7 @@ function Orca (library) { } this.isAllowed = function (g) { - return g === '.' || !!library[`${g}`.toLowerCase()] + return g === '.' || !!this.library[`${g}`.toLowerCase()] } this.isSpecial = function (g) { From 3cc6a1fedd0c83f8059c94b68662d5ea05083b73 Mon Sep 17 00:00:00 2001 From: unthingable Date: Mon, 26 Oct 2020 00:28:02 +0100 Subject: [PATCH 2/4] add comments to base lang --- desktop/sources/scripts/core/library/base.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/desktop/sources/scripts/core/library/base.js b/desktop/sources/scripts/core/library/base.js index 02955888..ba46b99f 100644 --- a/desktop/sources/scripts/core/library/base.js +++ b/desktop/sources/scripts/core/library/base.js @@ -5,6 +5,22 @@ library.base = {} +library.base['#'] = function OperatorComment (orca, x, y, passive) { + Operator.call(this, orca, x, y, '#', true) + + this.name = 'comment' + this.info = 'Halts line' + this.draw = false + + this.operation = function () { + for (let x = this.x + 1; x <= orca.w; x++) { + orca.lock(x, this.y) + if (orca.glyphAt(x, this.y) === this.glyph) { break } + } + orca.lock(this.x, this.y) + } +} + for (let i = 0; i <= 9; i++) { library.base[`${i}`] = function OperatorNull (orca, x, y, passive) { Operator.call(this, orca, x, y, '.', false) From 6672d47dffd6157b5a7734de640e5748c003ae7c Mon Sep 17 00:00:00 2001 From: unthingable Date: Mon, 26 Oct 2020 12:36:22 +0100 Subject: [PATCH 3/4] add documentation and sborca collection --- README.md | 12 +++++- desktop/sources/index.html | 1 + .../sources/scripts/core/library/README.md | 15 ++++++++ .../sources/scripts/core/library/sborca.js | 38 +++++++++++++++++++ .../sources/scripts/core/library/sborca.md | 9 +++++ 5 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 desktop/sources/scripts/core/library/README.md create mode 100644 desktop/sources/scripts/core/library/sborca.js create mode 100644 desktop/sources/scripts/core/library/sborca.md diff --git a/README.md b/README.md index 4adf8db0..c7490a77 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,8 @@ npm start ## Operators +See `Language` section for alternative language definitions. + To display the list of operators inside of Orca, use `CmdOrCtrl+G`. - `A` **add**(*a* b): Outputs sum of inputs. @@ -149,7 +151,15 @@ All commands have a shorthand equivalent to their first two characters, for exam - `midi:1;2` Set Midi output device to `#1`, and input device to `#2`. - `udp:1234;5678` Set UDP output port to `1234`, and input port to `5678`. - `osc:1234` Set OSC output port to `1234`. -- `lang:clr;default` Incrementally load language interpreters (`clr` clears the language library entirely). Multiple languages can be combined. +- `lang:clr;default;etc` Incrementally load language libraries (`clr` clears the language library entirely). Multiple languages can be combined. + +### Language + +Orca language is a "library" of operators (everything that is not a command) that together define its behavior. Orca has the ability to dynamically load and combine multiple operator libraries at runtime, effectively allowing you to reconfigure the language. For example, you may want this for playing older compositions that are no longer compatible with the current Orca, or to experiment with alternative operators without affecting mainline Orca. Language reconfiguration lets you tailor the language to your individual composition. + +Orca starts with a `default` library, additional libraries can be loaded and combined via the `lang` command. Library loading is incremental, that is, operators defined in the new library are added to runtime, replacing existing operators. A given library may define all operators or only some. To completely clear the runtime library and start with the blank slate use the `clr` library. + +You can see available libraries and their documentation [here](https://github.com/hundredrabbits/Orca/blob/master/desktop/sources/scripts/library). ## Base36 Table diff --git a/desktop/sources/index.html b/desktop/sources/index.html index 81334acd..05b42f58 100644 --- a/desktop/sources/index.html +++ b/desktop/sources/index.html @@ -9,6 +9,7 @@ + diff --git a/desktop/sources/scripts/core/library/README.md b/desktop/sources/scripts/core/library/README.md new file mode 100644 index 00000000..fc6a6c70 --- /dev/null +++ b/desktop/sources/scripts/core/library/README.md @@ -0,0 +1,15 @@ +Orca language is a "library" of operators (everything that is not a command) that together define its behavior. Orca has the ability to dynamically load and combine multiple operator libraries at runtime, effectively allowing you to reconfigure the language. For example, you may want this for playing older compositions that are no longer compatible with the current Orca, or to experiment with alternative operators without affecting mainline Orca. Language reconfiguration lets you tailor the language to your individual composition. + +Orca starts with a `default` library, additional libraries can be loaded and combined via the `lang` command. Library loading is incremental, that is, operators defined in the new library are added to runtime, replacing existing operators. A given library may define all operators or only some. To completely clear the runtime library and start with the blank slate use the `clr` library. + +## Available libraries + +"Complete" means you get a fully functioning Orca by loading just that library, "incremental" means it only defines a subset of operators and you need something loaded before that to get a full Orca (usually `default`). + +* `clr`: a special library that unloads all definitions +* `default`: mainline Orca language, loaded at startup (complete) +* `orca157`: Orca before the BFL breaking change (complete) (TODO) +* `base`: not a very useful library by itself, defines the basics such as comments and numbers +* `sb*`: Sborca collection of alternative operators (see `sborca.md`) + +## Usage examples \ No newline at end of file diff --git a/desktop/sources/scripts/core/library/sborca.js b/desktop/sources/scripts/core/library/sborca.js new file mode 100644 index 00000000..fb102108 --- /dev/null +++ b/desktop/sources/scripts/core/library/sborca.js @@ -0,0 +1,38 @@ +`use strict` + +/* global Operator */ +/* global library */ + +library.sbz = { + 'z': function OperatorZ (orca, x, y, passive) { + Operator.call(this, orca, x, y, 'z', passive) + + this.name = 'lerp' + this.info = 'Transitions operand to target by duration' + + this.ports.stepped = { x: -2, y: 0, default: '0' } + this.ports.duration = { x: -1, y: 0, default: '8' } + this.ports.target = { x: 1, y: 0 } + this.ports.output = { x: 0, y: 1, sensitive: true, reader: true, output: true } + + this.operation = function (force = false) { + const duration = this.listen(this.ports.duration, true) + const target = this.listen(this.ports.target, true) + const val = this.listen(this.ports.output, true) + var stepped = this.listen(this.ports.stepped, true) + if (stepped >= duration) { + stepped = duration + this.output(stepped, this.ports.stepped) + } + if (val !== target) { + if (stepped === duration) { stepped = 0 } // previous iteration done, ok to restart + const steps = duration - stepped + const remaining = target - val + const mod = Math.round(remaining / steps) + this.output(orca.keyOf(val + mod), this.ports.output) + this.output(orca.keyOf(stepped + 1), this.ports.stepped) + } + } + } +} + diff --git a/desktop/sources/scripts/core/library/sborca.md b/desktop/sources/scripts/core/library/sborca.md new file mode 100644 index 00000000..4064e2bf --- /dev/null +++ b/desktop/sources/scripts/core/library/sborca.md @@ -0,0 +1,9 @@ +Sborca collection of alternative operators + +## `sbz` (incremental) + +Defines: + +* `Z` **lerp**(*step* *duration* target): duration-based variant of `Z` + +TODO \ No newline at end of file From 593a16dacad630e92f100371b2cebff80a09480b Mon Sep 17 00:00:00 2001 From: unthingable Date: Mon, 26 Oct 2020 18:24:03 +0100 Subject: [PATCH 4/4] add orca157 library (pre BFL change, untested) --- desktop/sources/index.html | 1 + .../sources/scripts/core/library/README.md | 2 +- .../sources/scripts/core/library/orca157.js | 745 ++++++++++++++++++ 3 files changed, 747 insertions(+), 1 deletion(-) create mode 100644 desktop/sources/scripts/core/library/orca157.js diff --git a/desktop/sources/index.html b/desktop/sources/index.html index 05b42f58..9983e11d 100644 --- a/desktop/sources/index.html +++ b/desktop/sources/index.html @@ -9,6 +9,7 @@ + diff --git a/desktop/sources/scripts/core/library/README.md b/desktop/sources/scripts/core/library/README.md index fc6a6c70..87547017 100644 --- a/desktop/sources/scripts/core/library/README.md +++ b/desktop/sources/scripts/core/library/README.md @@ -8,7 +8,7 @@ Orca starts with a `default` library, additional libraries can be loaded and com * `clr`: a special library that unloads all definitions * `default`: mainline Orca language, loaded at startup (complete) -* `orca157`: Orca before the BFL breaking change (complete) (TODO) +* `orca157`: Orca [before the BFL breaking change](https://github.com/hundredrabbits/Orca/commit/4fd9ad72aafbb3f0c71139fd36ae421f1d8f352a) (complete) * `base`: not a very useful library by itself, defines the basics such as comments and numbers * `sb*`: Sborca collection of alternative operators (see `sborca.md`) diff --git a/desktop/sources/scripts/core/library/orca157.js b/desktop/sources/scripts/core/library/orca157.js new file mode 100644 index 00000000..642231e1 --- /dev/null +++ b/desktop/sources/scripts/core/library/orca157.js @@ -0,0 +1,745 @@ +'use strict' + +/* global Operator */ +/* global client */ +/* global library */ + +library.orca157 = {} + +library.orca157.a = function OperatorA (orca, x, y, passive) { + Operator.call(this, orca, x, y, 'a', passive) + + this.name = 'add' + this.info = 'Outputs sum of inputs' + + this.ports.a = { x: -1, y: 0 } + this.ports.b = { x: 1, y: 0 } + this.ports.output = { x: 0, y: 1, sensitive: true } + + this.operation = function (force = false) { + const a = this.listen(this.ports.a, true) + const b = this.listen(this.ports.b, true) + return orca.keyOf(a + b) + } +} + +library.orca157.b = function OperatorB (orca, x, y, passive) { + Operator.call(this, orca, x, y, 'b', passive) + + this.name = 'bounce' + this.info = 'Outputs values between inputs' + + this.ports.rate = { x: -1, y: 0, clamp: { min: 1 } } + this.ports.mod = { x: 1, y: 0, default: '8' } + this.ports.output = { x: 0, y: 1, sensitive: true } + + this.operation = function (force = false) { + const rate = this.listen(this.ports.rate, true) + const mod = this.listen(this.ports.mod, true) - 1 + const key = Math.floor(orca.f / rate) % (mod * 2) + return orca.keyOf(key <= mod ? key : mod - (key - mod)) + } +} + +library.orca157.c = function OperatorC (orca, x, y, passive) { + Operator.call(this, orca, x, y, 'c', passive) + + this.name = 'clock' + this.info = 'Outputs modulo of frame' + + this.ports.rate = { x: -1, y: 0, clamp: { min: 1 } } + this.ports.mod = { x: 1, y: 0, default: '8' } + this.ports.output = { x: 0, y: 1, sensitive: true } + + this.operation = function (force = false) { + const rate = this.listen(this.ports.rate, true) + const mod = this.listen(this.ports.mod, true) + const val = Math.floor(orca.f / rate) % mod + return orca.keyOf(val) + } +} + +library.orca157.d = function OperatorD (orca, x, y, passive) { + Operator.call(this, orca, x, y, 'd', passive) + + this.name = 'delay' + this.info = 'Bangs on modulo of frame' + + this.ports.rate = { x: -1, y: 0, clamp: { min: 1 } } + this.ports.mod = { x: 1, y: 0, default: '8' } + this.ports.output = { x: 0, y: 1, bang: true } + + this.operation = function (force = false) { + const rate = this.listen(this.ports.rate, true) + const mod = this.listen(this.ports.mod, true) + const res = orca.f % (mod * rate) + return res === 0 || mod === 1 + } +} + +library.orca157.e = function OperatorE (orca, x, y, passive) { + Operator.call(this, orca, x, y, 'e', passive) + + this.name = 'east' + this.info = 'Moves eastward, or bangs' + this.draw = false + + this.operation = function () { + this.move(1, 0) + this.passive = false + } +} + +library.orca157.f = function OperatorF (orca, x, y, passive) { + Operator.call(this, orca, x, y, 'f', passive) + + this.name = 'if' + this.info = 'Bangs if inputs are equal' + + this.ports.a = { x: -1, y: 0 } + this.ports.b = { x: 1, y: 0 } + this.ports.output = { x: 0, y: 1, bang: true } + + this.operation = function (force = false) { + const a = this.listen(this.ports.a) + const b = this.listen(this.ports.b) + return a === b && a !== '.' + } +} + +library.orca157.g = function OperatorG (orca, x, y, passive) { + Operator.call(this, orca, x, y, 'g', passive) + + this.name = 'generator' + this.info = 'Writes operands with offset' + + this.ports.x = { x: -3, y: 0 } + this.ports.y = { x: -2, y: 0 } + this.ports.len = { x: -1, y: 0, clamp: { min: 1 } } + + this.operation = function (force = false) { + const len = this.listen(this.ports.len, true) + const x = this.listen(this.ports.x, true) + const y = this.listen(this.ports.y, true) + 1 + for (let offset = 0; offset < len; offset++) { + const inPort = { x: offset + 1, y: 0 } + const outPort = { x: x + offset, y: y, output: true } + this.addPort(`in${offset}`, inPort) + this.addPort(`out${offset}`, outPort) + const res = this.listen(inPort) + this.output(`${res}`, outPort) + } + } +} + +library.orca157.h = function OperatorH (orca, x, y, passive) { + Operator.call(this, orca, x, y, 'h', passive) + + this.name = 'halt' + this.info = 'Halts southward operand' + + this.ports.output = { x: 0, y: 1, reader: true } + + this.operation = function (force = false) { + orca.lock(this.x + this.ports.output.x, this.y + this.ports.output.y) + return this.listen(this.ports.output, true) + } +} + +library.orca157.i = function OperatorI (orca, x, y, passive) { + Operator.call(this, orca, x, y, 'i', passive) + + this.name = 'increment' + this.info = 'Increments southward operand' + + this.ports.step = { x: -1, y: 0, default: '1' } + this.ports.mod = { x: 1, y: 0 } + this.ports.output = { x: 0, y: 1, sensitive: true, reader: true } + + this.operation = function (force = false) { + const step = this.listen(this.ports.step, true) + const mod = this.listen(this.ports.mod, true) + const val = this.listen(this.ports.output, true) + return orca.keyOf((val + step) % (mod > 0 ? mod : 36)) + } +} + +library.orca157.j = function OperatorJ (orca, x, y, passive) { + Operator.call(this, orca, x, y, 'j', passive) + + this.name = 'jumper' + this.info = 'Outputs northward operand' + + this.ports.val = { x: 0, y: -1 } + this.ports.output = { x: 0, y: 1 } + + this.operation = function (force = false) { + orca.lock(this.x, this.y + 1) + return this.listen(this.ports.val) + } +} + +library.orca157.k = function OperatorK (orca, x, y, passive) { + Operator.call(this, orca, x, y, 'k', passive) + + this.name = 'konkat' + this.info = 'Reads multiple variables' + + this.ports.len = { x: -1, y: 0, clamp: { min: 1 } } + + this.operation = function (force = false) { + this.len = this.listen(this.ports.len, true) + for (let offset = 0; offset < this.len; offset++) { + const key = orca.glyphAt(this.x + offset + 1, this.y) + orca.lock(this.x + offset + 1, this.y) + if (key === '.') { continue } + const inPort = { x: offset + 1, y: 0 } + const outPort = { x: offset + 1, y: 1, output: true } + this.addPort(`in${offset}`, inPort) + this.addPort(`out${offset}`, outPort) + const res = orca.valueIn(key) + this.output(`${res}`, outPort) + } + } +} + +library.orca157.l = function OperatorL (orca, x, y, passive) { + Operator.call(this, orca, x, y, 'l', passive) + + this.name = 'loop' + this.info = 'Moves eastward operands' + + this.ports.step = { x: -2, y: 0, default: '1' } + this.ports.len = { x: -1, y: 0 } + this.ports.val = { x: 1, y: 0 } + this.ports.output = { x: 0, y: 1 } + + this.operation = function (force = false) { + const len = this.listen(this.ports.len, true) + const step = this.listen(this.ports.step, true) + const index = orca.indexAt(this.x + 1, this.y) + const seg = orca.s.substr(index, len) + const res = seg.substr(len - step, step) + seg.substr(0, len - step) + for (let offset = 0; offset <= len; offset++) { + if (offset > 0) { + orca.lock(this.x + offset, this.y) + } + orca.write(this.x + offset + 1, this.y, res.charAt(offset)) + } + return this.listen(this.ports.val) + } +} + +library.orca157.m = function OperatorM (orca, x, y, passive) { + Operator.call(this, orca, x, y, 'm', passive) + + this.name = 'multiply' + this.info = 'Outputs product of inputs' + + this.ports.a = { x: -1, y: 0 } + this.ports.b = { x: 1, y: 0 } + this.ports.output = { x: 0, y: 1, sensitive: true } + + this.operation = function (force = false) { + const a = this.listen(this.ports.a, true) + const b = this.listen(this.ports.b, true) + return orca.keyOf(a * b) + } +} + +library.orca157.n = function OperatorN (orca, x, y, passive) { + Operator.call(this, orca, x, y, 'n', passive) + + this.name = 'north' + this.info = 'Moves Northward, or bangs' + this.draw = false + + this.operation = function () { + this.move(0, -1) + this.passive = false + } +} + +library.orca157.o = function OperatorO (orca, x, y, passive) { + Operator.call(this, orca, x, y, 'o', passive) + + this.name = 'read' + this.info = 'Reads operand with offset' + + this.ports.x = { x: -2, y: 0 } + this.ports.y = { x: -1, y: 0 } + this.ports.output = { x: 0, y: 1 } + + this.operation = function (force = false) { + const x = this.listen(this.ports.x, true) + const y = this.listen(this.ports.y, true) + this.addPort('read', { x: x + 1, y: y }) + return this.listen(this.ports.read) + } +} + +library.orca157.p = function OperatorP (orca, x, y, passive) { + Operator.call(this, orca, x, y, 'p', passive) + + this.name = 'push' + this.info = 'Writes eastward operand' + + this.ports.key = { x: -2, y: 0 } + this.ports.len = { x: -1, y: 0, clamp: { min: 1 } } + this.ports.val = { x: 1, y: 0 } + + this.operation = function (force = false) { + const len = this.listen(this.ports.len, true) + const key = this.listen(this.ports.key, true) + for (let offset = 0; offset < len; offset++) { + orca.lock(this.x + offset, this.y + 1) + } + this.ports.output = { x: (key % len), y: 1 } + return this.listen(this.ports.val) + } +} + +library.orca157.q = function OperatorQ (orca, x, y, passive) { + Operator.call(this, orca, x, y, 'q', passive) + + this.name = 'query' + this.info = 'Reads operands with offset' + + this.ports.x = { x: -3, y: 0 } + this.ports.y = { x: -2, y: 0 } + this.ports.len = { x: -1, y: 0, clamp: { min: 1 } } + + this.operation = function (force = false) { + const len = this.listen(this.ports.len, true) + const x = this.listen(this.ports.x, true) + const y = this.listen(this.ports.y, true) + for (let offset = 0; offset < len; offset++) { + const inPort = { x: x + offset + 1, y: y } + const outPort = { x: offset - len + 1, y: 1, output: true } + this.addPort(`in${offset}`, inPort) + this.addPort(`out${offset}`, outPort) + const res = this.listen(inPort) + this.output(`${res}`, outPort) + } + } +} + +library.orca157.r = function OperatorR (orca, x, y, passive) { + Operator.call(this, orca, x, y, 'r', passive) + + this.name = 'random' + this.info = 'Outputs random value' + + this.ports.min = { x: -1, y: 0 } + this.ports.max = { x: 1, y: 0 } + this.ports.output = { x: 0, y: 1, sensitive: true } + + this.operation = function (force = false) { + const min = this.listen(this.ports.min, true) + const max = this.listen(this.ports.max, true) + const val = parseInt((Math.random() * ((max > 0 ? max : 36) - min)) + min) + return orca.keyOf(val) + } +} + +library.orca157.s = function OperatorS (orca, x, y, passive) { + Operator.call(this, orca, x, y, 's', passive) + + this.name = 'south' + this.info = 'Moves southward, or bangs' + this.draw = false + + this.operation = function () { + this.move(0, 1) + this.passive = false + } +} + +library.orca157.t = function OperatorT (orca, x, y, passive) { + Operator.call(this, orca, x, y, 't', passive) + + this.name = 'track' + this.info = 'Reads eastward operand' + + this.ports.key = { x: -2, y: 0 } + this.ports.len = { x: -1, y: 0, clamp: { min: 1 } } + this.ports.output = { x: 0, y: 1 } + + this.operation = function (force = false) { + const len = this.listen(this.ports.len, true) + const key = this.listen(this.ports.key, true) + for (let offset = 0; offset < len; offset++) { + orca.lock(this.x + offset + 1, this.y) + } + this.ports.val = { x: (key % len) + 1, y: 0 } + return this.listen(this.ports.val) + } +} + +library.orca157.u = function OperatorU (orca, x, y, passive) { + Operator.call(this, orca, x, y, 'u', passive) + + this.name = 'uclid' + this.info = 'Bangs on Euclidean rhythm' + + this.ports.step = { x: -1, y: 0, clamp: { min: 0 }, default: '1' } + this.ports.max = { x: 1, y: 0, clamp: { min: 1 }, default: '8' } + this.ports.output = { x: 0, y: 1, bang: true } + + this.operation = function (force = false) { + const step = this.listen(this.ports.step, true) + const max = this.listen(this.ports.max, true) + const bucket = (step * (orca.f + max - 1)) % max + step + return bucket >= max + } +} + +library.orca157.v = function OperatorV (orca, x, y, passive) { + Operator.call(this, orca, x, y, 'v', passive) + + this.name = 'variable' + this.info = 'Reads and writes variable' + + this.ports.write = { x: -1, y: 0 } + this.ports.read = { x: 1, y: 0 } + + this.operation = function (force = false) { + const write = this.listen(this.ports.write) + const read = this.listen(this.ports.read) + if (write === '.' && read !== '.') { + this.addPort('output', { x: 0, y: 1 }) + } + if (write !== '.') { + orca.variables[write] = read + return + } + return orca.valueIn(read) + } +} + +library.orca157.w = function OperatorW (orca, x, y, passive) { + Operator.call(this, orca, x, y, 'w', passive) + + this.name = 'west' + this.info = 'Moves westward, or bangs' + this.draw = false + + this.operation = function () { + this.move(-1, 0) + this.passive = false + } +} + +library.orca157.x = function OperatorX (orca, x, y, passive) { + Operator.call(this, orca, x, y, 'x', passive) + + this.name = 'write' + this.info = 'Writes operand with offset' + + this.ports.x = { x: -2, y: 0 } + this.ports.y = { x: -1, y: 0 } + this.ports.val = { x: 1, y: 0 } + + this.operation = function (force = false) { + const x = this.listen(this.ports.x, true) + const y = this.listen(this.ports.y, true) + 1 + this.addPort('output', { x: x, y: y }) + return this.listen(this.ports.val) + } +} + +library.orca157.y = function OperatorY (orca, x, y, passive) { + Operator.call(this, orca, x, y, 'y', passive) + + this.name = 'jymper' + this.info = 'Outputs westward operand' + + this.ports.val = { x: -1, y: 0 } + this.ports.output = { x: 1, y: 0 } + + this.operation = function (force = false) { + orca.lock(this.x + 1, this.y) + return this.listen(this.ports.val) + } +} + +library.orca157.z = function OperatorZ (orca, x, y, passive) { + Operator.call(this, orca, x, y, 'z', passive) + + this.name = 'lerp' + this.info = 'Transitions operand to target' + + this.ports.rate = { x: -1, y: 0, default: '1' } + this.ports.target = { x: 1, y: 0 } + this.ports.output = { x: 0, y: 1, sensitive: true, reader: true } + + this.operation = function (force = false) { + const rate = this.listen(this.ports.rate, true) + const target = this.listen(this.ports.target, true) + const val = this.listen(this.ports.output, true) + const mod = val <= target - rate ? rate : val >= target + rate ? -rate : target - val + return orca.keyOf(val + mod) + } +} + +// Specials + +library.orca157['*'] = function OperatorBang (orca, x, y, passive) { + Operator.call(this, orca, x, y, '*', true) + + this.name = 'bang' + this.info = 'Bangs neighboring operands' + this.draw = false + + this.run = function (force = false) { + this.draw = false + this.erase() + } +} + +library.orca157['#'] = function OperatorComment (orca, x, y, passive) { + Operator.call(this, orca, x, y, '#', true) + + this.name = 'comment' + this.info = 'Halts line' + this.draw = false + + this.operation = function () { + for (let x = this.x + 1; x <= orca.w; x++) { + orca.lock(x, this.y) + if (orca.glyphAt(x, this.y) === this.glyph) { break } + } + orca.lock(this.x, this.y) + } +} + +// IO + +library.orca157.$ = function OperatorSelf (orca, x, y, passive) { + Operator.call(this, orca, x, y, '*', true) + + this.name = 'self' + this.info = 'Sends ORCA command' + + this.run = function (force = false) { + let msg = '' + for (let x = 1; x <= 36; x++) { + const g = orca.glyphAt(this.x + x, this.y) + orca.lock(this.x + x, this.y) + if (g === '.') { break } + msg += g + } + + if (!this.hasNeighbor('*') && force === false) { return } + if (msg === '') { return } + + this.draw = false + client.commander.trigger(`${msg}`) + } +} + +library.orca157[':'] = function OperatorMidi (orca, x, y, passive) { + Operator.call(this, orca, x, y, ':', true) + + this.name = 'midi' + this.info = 'Sends MIDI note' + this.ports.channel = { x: 1, y: 0 } + this.ports.octave = { x: 2, y: 0, clamp: { min: 0, max: 8 } } + this.ports.note = { x: 3, y: 0 } + this.ports.velocity = { x: 4, y: 0, default: 'f', clamp: { min: 0, max: 16 } } + this.ports.length = { x: 5, y: 0, default: '1', clamp: { min: 0, max: 32 } } + + this.operation = function (force = false) { + if (!this.hasNeighbor('*') && force === false) { return } + if (this.listen(this.ports.channel) === '.') { return } + if (this.listen(this.ports.octave) === '.') { return } + if (this.listen(this.ports.note) === '.') { return } + if (!isNaN(this.listen(this.ports.note))) { return } + + const channel = this.listen(this.ports.channel, true) + if (channel > 15) { return } + const octave = this.listen(this.ports.octave, true) + const note = this.listen(this.ports.note) + const velocity = this.listen(this.ports.velocity, true) + const length = this.listen(this.ports.length, true) + + client.io.midi.push(channel, octave, note, velocity, length) + + if (force === true) { + client.io.midi.run() + } + + this.draw = false + } +} + +library.orca157['!'] = function OperatorCC (orca, x, y) { + Operator.call(this, orca, x, y, '!', true) + + this.name = 'cc' + this.info = 'Sends MIDI control change' + this.ports.channel = { x: 1, y: 0 } + this.ports.knob = { x: 2, y: 0, clamp: { min: 0 } } + this.ports.value = { x: 3, y: 0, clamp: { min: 0 } } + + this.operation = function (force = false) { + if (!this.hasNeighbor('*') && force === false) { return } + if (this.listen(this.ports.channel) === '.') { return } + if (this.listen(this.ports.knob) === '.') { return } + + const channel = this.listen(this.ports.channel, true) + if (channel > 15) { return } + const knob = this.listen(this.ports.knob, true) + const rawValue = this.listen(this.ports.value, true) + const value = Math.ceil((127 * rawValue) / 35) + + client.io.cc.stack.push({ channel, knob, value, type: 'cc' }) + + this.draw = false + + if (force === true) { + client.io.cc.run() + } + } +} + +library.orca157['?'] = function OperatorPB (orca, x, y) { + Operator.call(this, orca, x, y, '?', true) + + this.name = 'pb' + this.info = 'Sends MIDI pitch bend' + this.ports.channel = { x: 1, y: 0, clamp: { min: 0, max: 15 } } + this.ports.lsb = { x: 2, y: 0, clamp: { min: 0 } } + this.ports.msb = { x: 3, y: 0, clamp: { min: 0 } } + + this.operation = function (force = false) { + if (!this.hasNeighbor('*') && force === false) { return } + if (this.listen(this.ports.channel) === '.') { return } + if (this.listen(this.ports.lsb) === '.') { return } + + const channel = this.listen(this.ports.channel, true) + const rawlsb = this.listen(this.ports.lsb, true) + const lsb = Math.ceil((127 * rawlsb) / 35) + const rawmsb = this.listen(this.ports.msb, true) + const msb = Math.ceil((127 * rawmsb) / 35) + + client.io.cc.stack.push({ channel, lsb, msb, type: 'pb' }) + + this.draw = false + + if (force === true) { + client.io.cc.run() + } + } +} + +library.orca157['%'] = function OperatorMono (orca, x, y, passive) { + Operator.call(this, orca, x, y, '%', true) + + this.name = 'mono' + this.info = 'Sends MIDI monophonic note' + this.ports.channel = { x: 1, y: 0 } + this.ports.octave = { x: 2, y: 0, clamp: { min: 0, max: 8 } } + this.ports.note = { x: 3, y: 0 } + this.ports.velocity = { x: 4, y: 0, default: 'f', clamp: { min: 0, max: 16 } } + this.ports.length = { x: 5, y: 0, default: '1', clamp: { min: 0, max: 32 } } + + this.operation = function (force = false) { + if (!this.hasNeighbor('*') && force === false) { return } + if (this.listen(this.ports.channel) === '.') { return } + if (this.listen(this.ports.octave) === '.') { return } + if (this.listen(this.ports.note) === '.') { return } + if (!isNaN(this.listen(this.ports.note))) { return } + + const channel = this.listen(this.ports.channel, true) + if (channel > 15) { return } + const octave = this.listen(this.ports.octave, true) + const note = this.listen(this.ports.note) + const velocity = this.listen(this.ports.velocity, true) + const length = this.listen(this.ports.length, true) + + client.io.mono.push(channel, octave, note, velocity, length) + + if (force === true) { + client.io.mono.run() + } + + this.draw = false + } +} + +library.orca157['='] = function OperatorOsc (orca, x, y, passive) { + Operator.call(this, orca, x, y, '=', true) + + this.name = 'osc' + this.info = 'Sends OSC message' + + this.ports.path = { x: 1, y: 0 } + + this.operation = function (force = false) { + let msg = '' + for (let x = 2; x <= 36; x++) { + const g = orca.glyphAt(this.x + x, this.y) + orca.lock(this.x + x, this.y) + if (g === '.') { break } + msg += g + } + + if (!this.hasNeighbor('*') && force === false) { return } + if (msg === '') { return } + + const path = this.listen(this.ports.path) + + if (!path || path === '.') { return } + + this.draw = false + client.io.osc.push('/' + path, msg) + + if (force === true) { + client.io.osc.run() + } + } +} + +library.orca157[';'] = function OperatorUdp (orca, x, y, passive) { + Operator.call(this, orca, x, y, ';', true) + + this.name = 'udp' + this.info = 'Sends UDP message' + + this.operation = function (force = false) { + let msg = '' + for (let x = 1; x <= 36; x++) { + const g = orca.glyphAt(this.x + x, this.y) + orca.lock(this.x + x, this.y) + if (g === '.') { break } + msg += g + } + + if (!this.hasNeighbor('*') && force === false) { return } + if (msg === '') { return } + + this.draw = false + client.io.udp.push(msg) + + if (force === true) { + client.io.udp.run() + } + } +} + +// Add numbers + +for (let i = 0; i <= 9; i++) { + library.orca157[`${i}`] = function OperatorNull (orca, x, y, passive) { + Operator.call(this, orca, x, y, '.', false) + + this.name = 'null' + this.info = 'empty' + + // Overwrite run, to disable draw. + this.run = function (force = false) { + + } + } +}