Skip to content

Commit

Permalink
feat(ableton-link): add ableton link support to orca
Browse files Browse the repository at this point in the history
  • Loading branch information
mcartagenah committed Oct 7, 2020
1 parent 0050b53 commit 4af0b12
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 23 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ 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`.
- `link` Enables/Disables Ableton Link

## Base36 Table

Expand Down
27 changes: 27 additions & 0 deletions desktop/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"electron-packager": "^14.2.1"
},
"dependencies": {
"abletonlink-addon": "^0.2.9",
"node-osc": "^4.1.8"
},
"standard": {
Expand Down
41 changes: 41 additions & 0 deletions desktop/sources/scripts/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
/* global Clock */
/* global Theme */

const AbletonLink = require("abletonlink-addon")

function Client () {
this.version = 176
this.library = library
Expand All @@ -26,6 +28,29 @@ function Client () {
this.commander = new Commander(this)
this.clock = new Clock(this)

// Ableton Link
this.link = new AbletonLink();

this.link.setTempoCallback((newTempo) => {
newTempo = this.link.getTempo(true)
if (this.clock.isLinkEnabled && this.clock.speed.value != newTempo) {
this.clock.setSpeed(newTempo, newTempo, true)
this.clock.setFrame(0)
this.update()
};
});

this.link.setStartStopCallback((startStopState) => {
console.log("startstop: " + startStopState);
if (startStopState && this.clock.isPaused) {
this.clock.play(false, true, true)
} else if (!startStopState && !this.clock.isPaused) {
this.clock.stop(false, true, false)
this.clock.setFrame(0)
this.update()
}
});

// Settings
this.scale = window.devicePixelRatio
this.grid = { w: 8, h: 8 }
Expand Down Expand Up @@ -117,6 +142,7 @@ function Client () {
this.acels.set('Midi', 'Next Input Device', 'CmdOrCtrl+,', () => { this.clock.setFrame(0); this.io.midi.selectNextInput() })
this.acels.set('Midi', 'Next Output Device', 'CmdOrCtrl+.', () => { this.clock.setFrame(0); this.io.midi.selectNextOutput() })
this.acels.set('Midi', 'Refresh Devices', 'CmdOrCtrl+Shift+M', () => { this.io.midi.refresh() })
this.acels.set('Midi', 'Toggle Ableton Link', 'CmdOrCtrl+Shift+L', () => { this.toggleLink() })

this.acels.set('Communication', 'Choose OSC Port', 'alt+O', () => { this.commander.start('osc:') })
this.acels.set('Communication', 'Choose UDP Port', 'alt+U', () => { this.commander.start('udp:') })
Expand Down Expand Up @@ -160,6 +186,21 @@ function Client () {
this.update()
}

this.toggleLink = () => {
if (this.clock.isLinkEnabled) {
this.link.disable()
this.link.disableStartStopSync()
} else {
this.link.enable()
this.link.enableStartStopSync()
this.clock.setSpeed(this.link.getTempo(true), this.link.getTempo(true), true)
if (!this.link.isPlaying()) {
this.clock.stop(false, true)
}
}
this.clock.isLinkEnabled = !this.clock.isLinkEnabled
}

this.update = () => {
if (document.hidden === true) { return }
this.clear()
Expand Down
41 changes: 37 additions & 4 deletions desktop/sources/scripts/clock.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ function Clock (client) {
this.isPaused = true
this.timer = null
this.isPuppet = false
this.isLinkEnabled = false

this.speed = { value: 120, target: 120 }

Expand All @@ -34,6 +35,15 @@ function Clock (client) {
if (value) { this.speed.value = clamp(value, 60, 300) }
if (target) { this.speed.target = clamp(target, 60, 300) }
if (setTimer === true) { this.setTimer(this.speed.value) }
if (this.isLinkEnabled) { this.setFrame(0) }
}

this.setSpeedLink = (value) => {
client.link.setTempo(value)
if (!client.link.isPlaying()) {
this.setFrame(0)
client.update()
}
}

this.modSpeed = function (mod = 0, animate = false) {
Expand All @@ -56,8 +66,14 @@ function Clock (client) {
client.update()
}

this.play = function (msg = false, midiStart = false) {
console.log('Clock', 'Play', msg, midiStart)
this.play = function (msg = false, midiStart = false, linkStart = false) {
console.log('Clock', 'Play', msg, midiStart, linkStart)
if (this.isLinkEnabled && this.isPaused && !linkStart) {
this.isPaused = false
this.setSpeed(this.speed.target, this.speed.target, true)
client.link.play()
return
}
if (this.isPaused === false && !midiStart) { return }
this.isPaused = false
if (this.isPuppet === true) {
Expand All @@ -73,8 +89,17 @@ function Clock (client) {
}
}

this.stop = function (msg = false) {
this.stop = function (msg = false, linkStop = false) {
console.log('Clock', 'Stop')
console.log(this.isLinkEnabled, this.isPaused, linkStop)
if (this.isLinkEnabled && !this.isPaused && !linkStop) {
this.isPaused = true
this.clearTimer()
client.link.stop()
client.io.midi.allNotesOff()
client.io.midi.silence()
return
}
if (this.isPaused === true) { return }
this.isPaused = true
if (this.isPuppet === true) {
Expand Down Expand Up @@ -159,10 +184,18 @@ function Clock (client) {

// UI

this.getUIMessage = function (offset) {
if (this.isLinkEnabled) {
return `link${this.speed.value}${offset}`
} else {
return this.isPuppet === true ? 'midi' : `${this.speed.value}${offset}`
}
}

this.toString = function () {
const diff = this.speed.target - this.speed.value
const _offset = Math.abs(diff) > 5 ? (diff > 0 ? `+${diff}` : diff) : ''
const _message = this.isPuppet === true ? 'midi' : `${this.speed.value}${_offset}`
const _message = this.getUIMessage(_offset)
const _beat = diff === 0 && client.orca.f % 4 === 0 ? '*' : ''
return `${_message}${_beat}`
}
Expand Down
19 changes: 17 additions & 2 deletions desktop/sources/scripts/commander.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,24 @@ function Commander (client) {
play: (p) => { client.clock.play() },
stop: (p) => { client.clock.stop() },
run: (p) => { client.run() },
link: (p) => { client.toggleLink() },
// Time
apm: (p) => { client.clock.setSpeed(null, p.int) },
bpm: (p) => { client.clock.setSpeed(p.int, p.int, true) },
apm: (p) => {
if (client.clock.isLinkEnabled) {
client.clock.setSpeed(null, p.int)
client.clock.setSpeedLink(p.int)
} else {
client.clock.setSpeed(null, p.int)
}
},
bpm: (p) => {
if (client.clock.isLinkEnabled) {
client.clock.setSpeed(p.int, p.int, true)
client.clock.setSpeedLink(p.int)
} else {
client.clock.setSpeed(p.int, p.int, true)
}
},
frame: (p) => { client.clock.setFrame(p.int) },
rewind: (p) => { client.clock.setFrame(client.orca.f - p.int) },
skip: (p) => { client.clock.setFrame(client.orca.f + p.int) },
Expand Down
36 changes: 19 additions & 17 deletions desktop/sources/scripts/core/io/midi.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,23 +122,25 @@ function Midi (client) {
}

this.receive = function (msg) {
switch (msg.data[0]) {
// Clock
case 0xF8:
client.clock.tap()
break
case 0xFA:
console.log('MIDI', 'Start Received')
client.clock.play(false, true)
break
case 0xFB:
console.log('MIDI', 'Continue Received')
client.clock.play()
break
case 0xFC:
console.log('MIDI', 'Stop Received')
client.clock.stop()
break
if (!client.clock.isLinkEnabled) {
switch (msg.data[0]) {
// Clock
case 0xF8:
client.clock.tap()
break
case 0xFA:
console.log('MIDI', 'Start Received')
client.clock.play(false, true)
break
case 0xFB:
console.log('MIDI', 'Continue Received')
client.clock.play()
break
case 0xFC:
console.log('MIDI', 'Stop Received')
client.clock.stop()
break
}
}
}

Expand Down

0 comments on commit 4af0b12

Please sign in to comment.