diff --git a/README.md b/README.md
index 5bf11a15..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.
@@ -70,13 +72,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 +96,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 +151,39 @@ 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;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
-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..9983e11d 100644
--- a/desktop/sources/index.html
+++ b/desktop/sources/index.html
@@ -6,7 +6,11 @@
-
+
+
+
+
+
@@ -30,7 +34,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/README.md b/desktop/sources/scripts/core/library/README.md
new file mode 100644
index 00000000..87547017
--- /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](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`)
+
+## Usage examples
\ No newline at end of file
diff --git a/desktop/sources/scripts/core/library/base.js b/desktop/sources/scripts/core/library/base.js
new file mode 100644
index 00000000..ba46b99f
--- /dev/null
+++ b/desktop/sources/scripts/core/library/base.js
@@ -0,0 +1,36 @@
+`use strict`
+
+/* global Operator */
+/* global library */
+
+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)
+
+ 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/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) {
+
+ }
+ }
+}
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
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) {