Permalink
Browse files

Merge branch 'midi'

  • Loading branch information...
eelco committed May 17, 2016
2 parents edf7c5c + 39158d7 commit d88e2f3de7306cb0f10fba325bda23462fda0d86
Showing with 97 additions and 1 deletion.
  1. +2 −1 framer/Framer.coffee
  2. +53 −0 framer/MIDIControl.coffee
  3. +42 −0 framer/MIDIInput.coffee
@@ -15,6 +15,7 @@ Framer.Animation = (require "./Animation").Animation
Framer.AnimationGroup = (require "./AnimationGroup").AnimationGroup
Framer.Screen = (require "./Screen").Screen
Framer.Align = (require "./Align").Align
Framer.MIDIControl = (require "./MIDIControl").MIDIControl
Framer.print = (require "./Print").print
# Components
@@ -62,4 +63,4 @@ Framer.CurrentContext = Framer.DefaultContext
window.Canvas = new (require "./Canvas").Canvas
Framer.Extras.MobileScrollFix.enable() if Utils.isMobile()
Framer.Extras.ErrorDisplay.enable() if not Utils.isFramerStudio()
Framer.Extras.TouchEmulator.enable() if not Utils.isTouch()
Framer.Extras.TouchEmulator.enable() if not Utils.isTouch()
@@ -0,0 +1,53 @@
{_} = require "./Underscore"
{BaseClass} = require "./BaseClass"
{Events} = require "./Events"
{MIDIInput} = require "./MIDIInput"
Events.MIDIControlValueChange = "MIDIControlValueChange"
class MIDIControl extends BaseClass
@define "min", @simpleProperty("min", 0)
@define "max", @simpleProperty("max", 127)
@define "control", @simpleProperty("control", null)
@define "channel", @simpleProperty("channel", null)
# Not supported yet, needs MIDIInput that opens all inputs
# @define "source", @simpleProperty("source", null)
constructor: (options={}) ->
super options
MIDIInput.enabled = true
MIDIInput.onCommand (timeStamp, data) =>
[b1, b2, b3] = data
# Mask the bytes to get the info we want
command = b1 & 0xf0
channel = (b1 & 0x0f) + 1 # 1-16
data1 = b2 & 0x7f
data2 = b3 & 0x7f
# 0xb0 control change
# 0x90 note on
# 0x80 note off
return unless command in [0xb0, 0x90, 0x80]
return if @channel? and @channel isnt channel
return if @control? and @control isnt data1
info =
channel: channel
control: data1
if command in [0x90, 0x80]
info = _.defaults info
type: "note"
@emit(Events.MIDIControlValueChange, @_modulate(data2), info)
_modulate: (value) ->
Utils.modulate(value, [0, 127], [@min, @max])
onValueChange: (cb) -> @on(Events.MIDIControlValueChange, cb)
exports.MIDIControl = MIDIControl
@@ -0,0 +1,42 @@
{BaseClass} = require "./BaseClass"
{Events} = require "./Events"
Events.MIDICommand = "midiCommand"
class MIDIInput extends BaseClass
@define "enabled",
get: -> @_input or @_request
set: (value) ->
return unless value != @enabled
return @_requestRejected() if not navigator.requestMIDIAccess
if value
@_request = navigator.requestMIDIAccess().then @_requestResolved, @_requestRejected
else
@_input?.close()
@_request = null
@_input = null
# Success handlers
_requestResolved: (access) =>
# Pick the last one
access.inputs.forEach (input) =>
@_input = input
@_input.onmidimessage = @_onmidimessage
# Failure handlers
_requestRejected: (error) =>
throw Error "Requesting MIDI access failed: #{error ? "not supported by browser"}"
# Event handlers
_onmidimessage: (message) =>
@emit(Events.MIDICommand, message.timeStamp, message.data)
# Event shortcuts
onCommand: (cb) -> @on(Events.MIDICommand, cb)
exports.MIDIInput = new MIDIInput

0 comments on commit d88e2f3

Please sign in to comment.