diff options
author | Devine Lu Linvega <[email protected]> | 2019-04-11 09:32:08 +0900 |
---|---|---|
committer | Devine Lu Linvega <[email protected]> | 2019-04-11 09:32:08 +0900 |
commit | 16b555f389c28ec421319e2cd57ebcbe53b574b5 (patch) | |
tree | 866cf99ff94d5f049d09b5953e9628a2c1461df5 | |
parent | 8a90b983ae5750a5f115e80084baa00e8ebdde7a (diff) | |
download | Orca-16b555f389c28ec421319e2cd57ebcbe53b574b5.tar.gz Orca-16b555f389c28ec421319e2cd57ebcbe53b574b5.zip |
Rebuilt the clock, added BPM animateTo
-rw-r--r-- | TUTORIAL.md | 8 | ||||
-rw-r--r-- | desktop/core/io.js (renamed from desktop/sources/scripts/io.js) | 8 | ||||
-rw-r--r-- | desktop/core/io/cc.js (renamed from desktop/sources/scripts/io.cc.js) | 0 | ||||
-rw-r--r-- | desktop/core/io/midi.js (renamed from desktop/sources/scripts/io.midi.js) | 4 | ||||
-rw-r--r-- | desktop/core/io/osc.js (renamed from desktop/sources/scripts/io.osc.js) | 0 | ||||
-rw-r--r-- | desktop/core/io/udp.js (renamed from desktop/sources/scripts/io.udp.js) | 2 | ||||
-rw-r--r-- | desktop/sources/index.html | 14 | ||||
-rw-r--r-- | desktop/sources/scripts/clock.js | 89 | ||||
-rw-r--r-- | desktop/sources/scripts/io.midi.clock.js | 85 | ||||
-rw-r--r-- | desktop/sources/scripts/io.osc-standalone.js | 123 | ||||
-rw-r--r-- | desktop/sources/scripts/keyboard.js | 6 | ||||
-rw-r--r-- | desktop/sources/scripts/lib/theme.js | 2 | ||||
-rw-r--r-- | desktop/sources/scripts/source.js | 12 | ||||
-rw-r--r-- | desktop/sources/scripts/terminal.js | 85 |
14 files changed, 89 insertions, 349 deletions
diff --git a/TUTORIAL.md b/TUTORIAL.md index 34d81eb..393d1db 100644 --- a/TUTORIAL.md +++ b/TUTORIAL.md @@ -6,14 +6,6 @@ If this is your first time trying out **Orca**, watch this [introduction video]( If you are on Windows, use something like [loopMidi](http://www.tobias-erichsen.de/software/loopmidi.html) to help routing midi signal across applications. -#### Using MIDI beat clock instead of the built in clock - -Orca comes with its own internal clock but you can configure it to receive its clock signal from a MIDI input. -Press `Ctrl+Space` to cycle through available clocks (built in or MIDI inputs). -The MIDI clock listens for the START and STOP signals from the midi device to run. - -*Warning*: Note length when using the MIDI clock is currently based on note length at 120 BPM. - ## MIDI CC #### Easy Binding diff --git a/desktop/sources/scripts/io.js b/desktop/core/io.js index a92710a..3f3e19a 100644 --- a/desktop/sources/scripts/io.js +++ b/desktop/core/io.js @@ -1,9 +1,9 @@ 'use strict' -const Midi = require('./io.midi') -const MidiCC = require('./io.cc') -const Udp = require('./io.udp') -const Osc = require('./io.osc') +const Midi = require('./io/midi') +const MidiCC = require('./io/cc') +const Udp = require('./io/udp') +const Osc = require('./io/osc') function IO (terminal) { this.midi = new Midi(terminal) diff --git a/desktop/sources/scripts/io.cc.js b/desktop/core/io/cc.js index 5355d65..5355d65 100644 --- a/desktop/sources/scripts/io.cc.js +++ b/desktop/core/io/cc.js diff --git a/desktop/sources/scripts/io.midi.js b/desktop/core/io/midi.js index 271336d..593ac3c 100644 --- a/desktop/sources/scripts/io.midi.js +++ b/desktop/core/io/midi.js @@ -1,17 +1,13 @@ 'use strict' -const MidiClock = require('./io.midi.clock') - function Midi (terminal) { this.index = 0 this.devices = [] this.stack = [] - this.clock = new MidiClock(terminal) this.start = function () { console.info('Midi Starting..') this.setup() - this.clock.start() } this.clear = function () { diff --git a/desktop/sources/scripts/io.osc.js b/desktop/core/io/osc.js index c761ced..c761ced 100644 --- a/desktop/sources/scripts/io.osc.js +++ b/desktop/core/io/osc.js diff --git a/desktop/sources/scripts/io.udp.js b/desktop/core/io/udp.js index cb595ec..2a4f371 100644 --- a/desktop/sources/scripts/io.udp.js +++ b/desktop/core/io/udp.js @@ -71,7 +71,7 @@ function Udp (terminal) { const val = `${msg}`.substr(1) const int = parseInt(`${msg}`.substr(1)) if (key === 'p') { - terminal.play() + terminal.clock.play() } else if (key === 's') { terminal.stop() } else if (key === 'r') { diff --git a/desktop/sources/index.html b/desktop/sources/index.html index 8f573cc..59eae2f 100644 --- a/desktop/sources/index.html +++ b/desktop/sources/index.html @@ -43,17 +43,17 @@ terminal.controller.add("default","Edit","Undo",() => { terminal.history.undo() },"CmdOrCtrl+Z") terminal.controller.add("default","Edit","Redo",() => { terminal.history.redo() },"CmdOrCtrl+Shift+Z") - terminal.controller.add("default","Program","Play/Pause",() => { terminal.togglePlay() },"Space") - terminal.controller.add("default","Program","Reset Frame",() => { terminal.orca.f = 0 },"CmdOrCtrl+Shift+N") - terminal.controller.add("default","Program","Prev Frame",() => { terminal.prevFrame() },"CmdOrCtrl+Shift+F") - terminal.controller.add("default","Program","Next Frame",() => { terminal.nextFrame() },"CmdOrCtrl+F") - terminal.controller.add("default","Program","Incr. Speed",() => { terminal.modSpeed(1) },">") - terminal.controller.add("default","Program","Decr. Speed",() => { terminal.modSpeed(-1) },"<") terminal.controller.add("default","Program","Incr. Col",() => { terminal.modGrid(1,0) },"]") terminal.controller.add("default","Program","Decr. Col",() => { terminal.modGrid(-1,0) },"[") terminal.controller.add("default","Program","Incr. Row",() => { terminal.modGrid(0,1) },"}") terminal.controller.add("default","Program","Decr. Row",() => { terminal.modGrid(0,-1) },"{") - terminal.controller.add("default","Program","Next Clock", () => { terminal.nextClock() }, "Ctrl+Space") + + terminal.controller.add("default","Clock","Play/Pause",() => { terminal.clock.togglePlay() },"Space") + terminal.controller.add("default","Clock","Reset Frame",() => { terminal.clock.resetFrame() },"CmdOrCtrl+Shift+N") + terminal.controller.add("default","Clock","Incr. Speed",() => { terminal.clock.modSpeed(1) },">") + terminal.controller.add("default","Clock","Decr. Speed",() => { terminal.clock.modSpeed(-1) },"<") + terminal.controller.add("default","Clock","Incr. Speed(10x)",() => { terminal.clock.modSpeed(10,true) },"cmdOrCtrl+>") + terminal.controller.add("default","Clock","Decr. Speed(10x)",() => { terminal.clock.modSpeed(-10,true) },"cmdOrCtrl+<") terminal.controller.add("default","View","Zoom In",() => { terminal.modZoom(0.25) },"CmdOrCtrl+=") terminal.controller.add("default","View","Zoom Out",() => { terminal.modZoom(-0.25) },"CmdOrCtrl+-") diff --git a/desktop/sources/scripts/clock.js b/desktop/sources/scripts/clock.js index e1c0fda..b2b826f 100644 --- a/desktop/sources/scripts/clock.js +++ b/desktop/sources/scripts/clock.js @@ -1,56 +1,87 @@ 'use strict' -class Clock { - constructor (bpm, callback) { - this.bpm = 0 - this.callback = () => {} - this.timer = null - this.running = false - this.setBpm(bpm) +function Clock (terminal) { + this.isPaused = true + this.timer = null + + this.speed = { value: 120, target: 120 } + + this.start = function () { + this.setTimer(120) + this.play() } - setCallback (callback) { - this.callback = callback + this.update = function () { + if (this.speed.target === this.speed.value) { return } + + this.setTimer(this.speed.value) + + if (this.speed.value < this.speed.target) { this.speed.value++ } + if (this.speed.value > this.speed.target) { this.speed.value-- } } - canSetBpm () { - return true + this.togglePlay = function () { + if (this.isPaused === true) { + this.play() + } else { + this.stop() + } } - getBpm () { - return this.bpm + this.play = function () { + if (!this.isPaused) { console.warn('Already playing'); return } + console.log('Play') + this.isPaused = false + this.setTimer(this.speed.target) } - setBpm (bpm) { - this.bpm = bpm - this.reset() + this.stop = function () { + if (this.isPaused) { console.warn('Already stopped'); return } + console.log('Stop') + terminal.io.midi.silence() + this.isPaused = true + this.clearTimer() } - reset () { + this.clearTimer = function () { if (this.timer) { clearInterval(this.timer) } + } - if (this.running) { - this.timer = setInterval(() => { this.callback() }, (60000 / this.bpm) / 4) - } + this.setTimer = function (bpm) { + this.speed.value = bpm + this.clearTimer() + this.timer = setInterval(() => { terminal.run(); this.update() }, (60000 / bpm) / 4) } - setRunning (running) { - this.running = running - this.reset() + this.setSpeed = function (bpm, animate = false) { + if (animate) { + this.speed.target = bpm + } else { + this.setTimer(bpm) + } } - start () { - this.setRunning(true) + this.modSpeed = function (mod = 0, animate = false) { + if (animate === true) { + this.speed.target += mod + } else { + this.setTimer(this.speed.value + mod) + this.speed.target = this.speed.value + terminal.update() + } } - stop () { - this.setRunning(false) + this.resetFrame = function () { + terminal.orca.f = 0 } - toString () { - return `${this.bpm}` + this.toString = function () { + const diff = this.speed.target - this.speed.value + const _offset = diff > 0 ? `+${diff}` : diff < 0 ? diff : '' + const _beat = diff === 0 && terminal.orca.f % 4 === 0 ? '*' : '' + return `${this.speed.value}${_offset}${_beat}` } } diff --git a/desktop/sources/scripts/io.midi.clock.js b/desktop/sources/scripts/io.midi.clock.js deleted file mode 100644 index e9bd786..0000000 --- a/desktop/sources/scripts/io.midi.clock.js +++ /dev/null @@ -1,85 +0,0 @@ -'use strict' - -class WrappedClock { - constructor (device) { - this.device = device - this.running = false - this.callback = () => {} - this.count = 0 - this.started = false - this.signals = { - CLOCK: 0xF8, - START: 0xFA, - STOP: 0xFC - } - } - - canSetBpm () { - return false - } - - setCallback (callback) { - this.callback = callback - } - - setRunning (running) { - this.running = running - this.reset() - } - - reset () { - if (this.running) { - this.device.onmidimessage = (message) => { this.onMIDIMessage(message) } - } else { - this.device.onmidimessage = null - } - } - - toString () { - return `${this.device.name}` - } - - onMIDIMessage (message) { - switch (message.data[0]) { - case this.signals.CLOCK: - this.count = (this.count + 1) % 6 - if (this.count === 0 && this.started) { - this.callback() - } - break - case this.signals.START: - this.started = true - break - case this.signals.STOP: - this.started = false - break - } - } -} - -class MidiClock { - constructor (terminal) { - this.terminal = terminal - } - - start () { - console.info('Midi Clock Starting..') - this.setup() - } - - setup () { - if (!navigator.requestMIDIAccess) { return } - navigator.requestMIDIAccess({ sysex: false }).then((midiAccess) => { this.onMIDIAccess(midiAccess) }, (err) => { - console.warn('No Midi', err) - }) - } - - onMIDIAccess (midiAccess) { - const iter = midiAccess.inputs.values() - for (let i = iter.next(); i && !i.done; i = iter.next()) { - this.terminal.clocks.push(new WrappedClock(i.value)) - } - } -} - -module.exports = MidiClock diff --git a/desktop/sources/scripts/io.osc-standalone.js b/desktop/sources/scripts/io.osc-standalone.js deleted file mode 100644 index f8b3fd1..0000000 --- a/desktop/sources/scripts/io.osc-standalone.js +++ /dev/null @@ -1,123 +0,0 @@ -'use strict' - -function Osc (terminal) { - this.index = 0 - this.routes = [] - this.stack = [] - - this.start = function () { - console.info('Starting OSC..') - this.setup() - } - - this.clear = function () { - this.stack = [] - } - - this.run = function () { - for (const id in this.stack) { - this.play(this.stack[id]) - } - } - - this.send = function (msg) { - this.stack.push(msg) - } - - this.play = function (data) { - // see io.osc.js - - // const args = data.split('.').filter(d => d !== '') - // if (args.length !== 0) { - // const key = args[0] - // const def = this.config.defs[key] - // const pattern = def.pattern - - // if(pattern.length !== args.length - 1) { - // console.log(`Number of arguments provided does not match the pattern length of this def`) - // return - // } - - // const values = [] - // for(let i = 0, l = pattern.length; i < l; i++) { - // const type = pattern[i] - // if (type !== 'f' && type !== 'i' && type !== 's') { - // console.log(`Don't know how to send OSC argument with type '${type}'`) - // return - // } - - // const value = - // type === 'f' ? parseInt(split[1]) / 10.0 - // : type === 'i' ? parseInt(split[1]) - // /* type === 's' ? */ : split[1] - - // function nextMultipleOf4 (x) { - // const rem = x % 4 - // return rem === 0 ? x : (x + 4 - rem) - // } - - // function writeOscString (val, buf, pos) { - // let localPos = pos - // for (let i = 0; i < val.length; i++) { - // const ch = val.charCodeAt(i) - // localPos = buf.writeUInt8(ch & 0xFF, localPos) - // } - // // Add length, terminating 0 and pad to multiple of 4 - // return nextMultipleOf4(pos + val.length + 1) - // } - - // let msglen = 0 - // { // Calculate message length - // // Zero-terminated address string - // msglen += nextMultipleOf4(address.length + 1) - // // Type tag with two args arg: comma, letter (key), letter (value), terminating 0 - // msglen += 4 - // // Zero-terminated key string - // msglen += nextMultipleOf4(key.length + 1) - // if (type === 'f' || type === 'i') { - // // 32-bit float or int - // msglen += 4 - // } else if (type === 's') { - // // Zero terminated value string - // msglen += nextMultipleOf4(value.length + 1) - // } - // } - - // // Get buffer cleared to 0 - // const buf = Buffer.alloc(msglen) - // let pos = 0 - - // pos = writeOscString(address, buf, pos) - // pos = writeOscString(`,s${type}`, buf, pos) - // pos = writeOscString(key, buf, pos) - - // if (type === 'f') { - // pos = buf.writeFloatBE(value, pos) - // } else if (type === 'i') { - // pos = buf.writeInt32BE(value, pos) - // } else if (type === 's') { - // pos = writeOscString(value, buf, pos) - // } - // } - - // this.port.send(buf, def.port, def.address, (err) => { - // if (err) { console.log(err) } - // }) - } - - this.setup = function () { - this.config = require('../../core/bridge/oscConfig') - this.clients = {} - for (const key in this.config.defs) { - const def = this.config.defs[key] - const address = def.address - const port = def.port - if (!this.clients[`${address}:${port}`]) { - this.clients[`${address}:${port}`] = new osc.Client(address, port) - console.log(`OSC client ${address}:${port} created`) - } - } - } -} - -module.exports = Osc diff --git a/desktop/sources/scripts/keyboard.js b/desktop/sources/scripts/keyboard.js index e7444cc..90a6139 100644 --- a/desktop/sources/scripts/keyboard.js +++ b/desktop/sources/scripts/keyboard.js @@ -29,15 +29,15 @@ function Keyboard (terminal) { if (event.ctrlKey) { return } if (event.key === 'Backspace' || event.key === '.') { terminal.cursor.erase(true); return } - if (event.key === ' ' && terminal.cursor.mode === 0) { terminal.togglePlay(); event.preventDefault(); return } + if (event.key === ' ' && terminal.cursor.mode === 0) { terminal.clock.togglePlay(); event.preventDefault(); return } if (event.key === 'Escape') { terminal.clear(); terminal.isPaused = false; terminal.cursor.reset(); return } if (event.key === ']') { terminal.modGrid(1, 0); event.preventDefault(); return } if (event.key === '[') { terminal.modGrid(-1, 0); event.preventDefault(); return } if (event.key === '}') { terminal.modGrid(0, 1); event.preventDefault(); return } if (event.key === '{') { terminal.modGrid(0, -1); event.preventDefault(); return } - if (event.key === '>') { terminal.modSpeed(1); event.preventDefault(); return } - if (event.key === '<') { terminal.modSpeed(-1); event.preventDefault(); return } + if (event.key === '>') { terminal.clock.modSpeed(1); event.preventDefault(); return } + if (event.key === '<') { terminal.clock.modSpeed(-1); event.preventDefault(); return } if (event.key.length === 1) { terminal.cursor.write(event.key) diff --git a/desktop/sources/scripts/lib/theme.js b/desktop/sources/scripts/lib/theme.js index 6fb3c41..87e3ab3 100644 --- a/desktop/sources/scripts/lib/theme.js +++ b/desktop/sources/scripts/lib/theme.js @@ -29,7 +29,7 @@ function Theme (_default) { this.load = function (data) { const theme = parse(data) if (!validate(theme)) { console.warn('Theme', 'Not a theme', theme); return } - console.log('Theme', `Load theme, background: ${theme.background}.`) + console.log('Theme', `Loaded theme!`) this.el.innerHTML = `:root { --background: ${theme.background}; --f_high: ${theme.f_high}; --f_med: ${theme.f_med}; --f_low: ${theme.f_low}; --f_inv: ${theme.f_inv}; --b_high: ${theme.b_high}; --b_med: ${theme.b_med}; --b_low: ${theme.b_low}; --b_inv: ${theme.b_inv}; }` localStorage.setItem('theme', JSON.stringify(theme)) this.active = theme diff --git a/desktop/sources/scripts/source.js b/desktop/sources/scripts/source.js index 9c7912e..4eef97d 100644 --- a/desktop/sources/scripts/source.js +++ b/desktop/sources/scripts/source.js @@ -11,23 +11,23 @@ function Source (terminal) { } this.new = function () { - console.log('Source', 'New File') + console.log('Source', 'Make a new file..') this.path = null terminal.orca.reset() terminal.resize() terminal.history.reset() - terminal.play() + terminal.clock.play() } this.open = function () { - console.log('Source', 'Open File') + console.log('Source', 'Open a file..') let paths = dialog.showOpenDialog(app.win, { properties: ['openFile'], filters: [{ name: 'Orca Machines', extensions: ['orca'] }] }) if (!paths) { console.log('Nothing to load') } this.read(paths[0]) } this.save = function (as = false) { - console.log('Source', 'Save File') + console.log('Source', 'Save a file..') if (this.path && !as) { this.write(this.path) } else { @@ -35,7 +35,7 @@ function Source (terminal) { } } this.saveAs = function () { - console.log('Source', 'Save File As') + console.log('Source', 'Save a file as..') dialog.showSaveDialog((path) => { if (path === undefined) { return } if (path.indexOf('.orca') < 0) { path += '.orca' } @@ -46,7 +46,7 @@ function Source (terminal) { this.revert = function () { if (!this.path) { return } - console.log('Source', 'Revert File') + console.log('Source', 'Revert a file..') this.read(this.path) } diff --git a/desktop/sources/scripts/terminal.js b/desktop/sources/scripts/terminal.js index 6b96068..74837be 100644 --- a/desktop/sources/scripts/terminal.js +++ b/desktop/sources/scripts/terminal.js @@ -2,24 +2,24 @@ function Terminal () { const Orca = require('../../core/orca') + const IO = require('../../core/io') const Cursor = require('./cursor') const Source = require('./source') const History = require('./history') const Keyboard = require('./keyboard') - const IO = require('./io') const Clock = require('./clock') this.library = require('../../core/library') + this.history = new History() + this.controller = new Controller() + this.orca = new Orca() this.io = new IO(this) this.cursor = new Cursor(this) this.source = new Source(this) this.keyboard = new Keyboard(this) - this.history = new History() - this.controller = new Controller() - this.clocks = [new Clock(120)] - this.selectedClock = 0 + this.clock = new Clock(this) // Themes this.theme = new Theme({ background: '#000000', f_high: '#ffffff', f_med: '#777777', f_low: '#444444', f_inv: '#000000', b_high: '#eeeeee', b_med: '#72dec2', b_low: '#444444', b_inv: '#ffb545' }) @@ -32,8 +32,6 @@ function Terminal () { this.tile = { w: 10, h: 15 } this.scale = window.devicePixelRatio - this.isPaused = false - this.install = function (host) { host.appendChild(this.el) this.theme.install(host) @@ -45,7 +43,7 @@ function Terminal () { this.source.start() this.history.bind(this.orca, 's') this.history.record(this.orca.s) - this.nextClock() + this.clock.start() this.update() this.el.className = 'ready' } @@ -57,21 +55,6 @@ function Terminal () { this.update() } - this.play = function () { - console.log('Play') - this.isPaused = false - this.update() - this.clock().setRunning(true) - } - - this.stop = function () { - console.log('Stop') - this.io.midi.silence() - this.isPaused = true - this.update() - this.clock().setRunning(false) - } - this.load = function (orca, frame = 0) { this.history.reset() this.orca = orca @@ -94,46 +77,6 @@ function Terminal () { this.theme.reset() } - this.prevFrame = function () { - this.orca.f -= 2 - this.stop() - this.run() - } - - this.nextFrame = function () { - this.stop() - this.run() - } - - // Clock - - this.clock = function () { - return this.clocks[this.selectedClock] - } - - this.nextClock = function () { - const previousClock = this.clock() - if (previousClock) { - previousClock.setRunning(false) - previousClock.setCallback(() => {}) - } - this.selectedClock = (this.selectedClock + 1) % this.clocks.length - this.clock().setRunning(!this.isPaused) - this.clock().setCallback(() => this.run()) - - console.log('Select clock:', this.clock()) - this.update() - } - - this.setSpeed = function (bpm) { - if (this.clock().canSetBpm()) { - bpm = clamp(bpm, 60, 300) - console.log(`Change Speed: ${bpm}.`) - this.clock().setBpm(bpm) - this.update() - } - } - this.setGrid = function (w, h) { this.grid.w = w this.grid.h = h @@ -151,20 +94,6 @@ function Terminal () { this.resize(true) } - this.togglePlay = function () { - if (this.isPaused === true) { - this.play() - } else { - this.stop() - } - } - - this.modSpeed = function (mod = 0) { - if (this.clock().canSetBpm()) { - this.setSpeed(this.clock().getBpm() + mod) - } - } - this.modGrid = function (x = 0, y = 0) { const w = clamp(this.grid.w + x, 4, 16) const h = clamp(this.grid.h + y, 4, 16) @@ -246,7 +175,7 @@ function Terminal () { this.write(`${this.orca.w}x${this.orca.h}`, col * 0, 0, this.grid.w) this.write(`${this.grid.w}/${this.grid.h}`, col * 1, 0, this.grid.w) this.write(`${this.source}`, col * 2, 0, this.grid.w) - this.write(`${this.clock()}${this.orca.f % 4 === 0 ? '*' : ''}`, col * 3, 0, this.grid.w) + this.write(`${this.clock}`, col * 3, 0, this.grid.w) this.write(`${this.io.inspect(this.grid.w)}`, col * 4, 0, this.grid.w) if (this.orca.f < 25) { |