Example #1
0
module.exports = function (context) {
  var activatedAt = Date.now()
  var unloadState = { lastSuppressId: null, lastSuppressAt: 0 }

  var midiPort = MidiPort(context, function (port, lastPort) {
    // turn off on switch
    lastPort && lastPort.write(turnOffAll)

    if (port) {
      activatedAt = Date.now()
      port.write(turnOffAll)
      var values = []
      for (var i = 0; i < 8; i++) {
        if (i >= 2) {
          values.push(i, light(1, 1))
        } else {
          values.push(i, light(2, 2))
        }
        values.push(i + 8, light(1, 0))
        values.push(i + 16, light(0, 1))
      }
      port.write(setLed.concat(values, 247))
    }
  })

  var obs = ObservStruct({
    port: midiPort
  })

  // grab the midi for the current port
  obs.grabInput = function () {
    midiPort.grab()
  }

  obs.context = context

  var project = context.project
  var onTrigger = AnyTrigger(project.items)

  var knobs = {
    3: Observ(0),
    4: Observ(0),
    5: Observ(0),
    6: Observ(0),
    7: Observ(0),
    8: Observ(0)
  }

  var params = []

  Object.keys(knobs).forEach(function (key) {
    var id = 'Launch Control XL > ' + key
    params.push(id)
    context.paramLookup.put(id, MidiParam(context, id, knobs[key]))
  })

  var paramState = []
  watchKnobs(midiPort.stream, mappings.row1.concat(mappings.row2, mappings.row3), function (id, data) {
    var state = paramState[id] = paramState[id] || {}
    var index = Math.floor(id / 8)
    if (index === 0) {
      if (id === 0) {
        project.tempo.set(scaleInterpolate(project.tempo() - 60, data, state) + 60)
      } else if (id === 1) {
        project.swing.set(scaleInterpolate(project.swing() * 128, data, state) / 128)
      } else {
        knobs[id + 1].set(scaleInterpolate(knobs[id + 1](), data, state))
      }
    } else {
      var item = project.items.get(id % 8)
      if (isSetup(item)) {
        var setup = item.node
        var param = index === 1 ? setup.overrideLowPass : setup.overrideHighPass
        var currentValue = getValue(param())
        param.set(scaleInterpolate(currentValue * 128, data, state) / 128)
      }
    }
  })

  var sliderState = []
  watchKnobs(midiPort.stream, mappings.sliders, function (id, data) {
    var state = sliderState[id] = sliderState[id] || {}
    var item = project.items.get(id)
    if (isSetup(item)) {
      var setup = item.node
      var volume = setup.overrideVolume
      var currentPosition = Math.pow(volume(), 1 / Math.E) * 108
      var newPosition = scaleInterpolate(currentPosition, data, state)
      volume.set(Math.pow(newPosition / 108, Math.E))
    }
  }, 108)

  var selectedId = 0
  var buttonBase = computed([project.selected, project.items], function (selected, items) {
    var result = []
    for (var i = 0; i < 8; i++) {
      var item = project.items.get(i)
      if (item) {
        if (item.path === selected) {
          selectedId = i
          result.push(light(2, 3))
        } else {
          result.push(light(0, 1))
        }
      } else {
        result.push(0)
      }
    }
    return result
  })

  var stopButtonBase = Observ([light(1,0),light(1,0),light(1,0),light(1,0),light(1,0),light(1,0),light(1,0),light(1,0)])
  var buttonFlash = FlashArray()
  onTrigger(function (index) {
    if (index === selectedId) {
      buttonFlash.flash(index, light(3, 3), 40)
    } else {
      buttonFlash.flash(index, light(0, 3), 40)
    }
  })

  var buttons = ObservMidi(midiPort.stream, mappings.trackFocus, ArrayStack([
    buttonBase,
    buttonFlash
  ]))

  buttons(function (values) {
    var result = null

    values.forEach(function (val, i) {
      if (val) {
        result = i
      }
    })

    if (result != null) {
      var item = project.items.get(result)
      if (item) {
        project.selected.set(item.path)
      }
    }
  })

  var stopAllButtons = ObservMidi(midiPort.stream, mappings.trackControl, ArrayStack([
    stopButtonBase,
    buttonFlash
  ]))

  stopAllButtons(function (values) {
    var result = null

    values.forEach(function (val, i) {
      if (val) {
        result = i
      }
    })

    if (result != null) {
      var item = project.items.get(result)

      if (item && item.node) {
        if (unloadState.lastSuppressId === result && unloadState.lastSuppressAt > Date.now() - 500) {
          unloadState.lastSuppressAt = 0
          item.close()
        } else {
          unloadState.lastSuppressId = result
          unloadState.lastSuppressAt = Date.now()
          suppressNode(item.node, true)
        }
      }
    }
  })

  // CONTROL BUTTONS:
  var controlButtons = LightStack(midiPort.stream, {
    mode: mappings.device
  })

  controlButtons.mode(function (value) {
    if (value || Date.now() - activatedAt > 300) {
      midiPort.override.set(false)
      var activeItem = findItemByPath(project.items, project.selected())
      if (isSetup(activeItem)) {
        activeItem.node.grabInput()
      } else {
        midiPort.previous()
      }
    }
  })

  controlButtons.mode.light(light(2, 2))

  obs.grabInput = function () {
    midiPort.grab()
  }

  obs.destroy = function () {
    midiPort.destroy()
    params.forEach(function (id) {
      context.paramLookup.delete(id)
    })
  }

  return obs

  // scoped

  function suppressNode (node, flatten) {
    if (node && node.controllers) {
      node.controllers.forEach(function (controller) {
        if (controller.looper) {
          var release = controller.looper.transform(function (grid) {
            grid.data = []
            return grid
          })

          if (flatten) {
            controller.looper.flatten()
          } else {
            return release
          }
        }
      })
    }
  }
}
Example #2
0
module.exports = function (context) {
  var midiPort = MidiPort(context, function (port, lastPort) {
    // turn off on switch
    lastPort && lastPort.write(turnOffAll)
    port && port.write(turnOffAll)
  })

  var obs = ObservStruct({
    port: midiPort,
    chunkIds: Property([])
  })

  var params = []
  for (var i = 0; i < 8; i++) {
    params[i] = [
      Param(context, 0),
      Param(context, 0),
      Param(context, 0)
    ]
  }

  var paramReleases = []
  obs.chunkIds(refreshParamLinks)
  context.chunkLookup(refreshParamLinks)
  function refreshParamLinks () {
    while (paramReleases.length) {
      paramReleases.pop()()
    }
    obs.chunkIds().forEach(function (id, i) {
      var chunk = context.chunkLookup.get(id)
      if (chunk && chunk.overrideParams) {
        paramReleases.push(chunk.overrideParams(params[i]))
      }
    })
  }

  // grab the midi for the current port
  obs.grabInput = function () {
    midiPort.grab()
  }

  obs.context = context

  var setup = context.setup

  var paramState = []
  watchKnobs(midiPort.stream, mappings.row1.concat(mappings.row2, mappings.row3), function (id, data) {
    var param = params[id % 8][Math.floor(id / 8)]
    var chunk = setup.context.chunkLookup.get(obs.chunkIds()[id % 8])
    if (chunk && chunk.overrideParams && chunk.params) {
      param.set(scaleInterpolate(param() * 128, data, paramState[id] = paramState[id] || {}) / 128)
    }
  })

  var sliderState = []
  watchKnobs(midiPort.stream, mappings.sliders, function (id, data) {
    var chunk = setup.chunks.lookup.get(obs.chunkIds()[id])
    if (chunk) {
      var volume = chunk.overrideVolume || chunk.node && chunk.node.overrideVolume
      if (volume) {
        var currentPosition = Math.pow(volume(), 1 / Math.E) * 108
        var newPosition = scaleInterpolate(currentPosition, data, sliderState[id] = sliderState[id] || {})
        volume.set(Math.pow(newPosition / 108, Math.E))
      }
    }
  }, 108)

  var pressed = PressedChunks(setup.controllers)

  var knobLights = computed([obs.chunkIds, setup.context.chunkLookup, pressed, setup.selectedChunkId], function (chunkIds, lookup, pressed, selected) {
    var result = []
    for (var i = 0; i < 8; i++) {
      var chunk = setup.context.chunkLookup.get(chunkIds[i])
      if (chunk && chunk.params) {
        var onValue = pressed[chunkIds[i]]
          ? light(0, 2)
          : selected === chunkIds[i]
            ? light(1, 1)
            : light(2, 0)
        result[0 + i] = chunk.params()[0] ? onValue : 0
        result[8 + i] = chunk.params()[1] ? onValue : 0
        result[16 + i] = chunk.params()[2] ? onValue : 0
      } else {
        result[0 + i] = result[8 + i] = result[16 + i] = 0
      }
    }
    return result
  })

  setLights(knobLights, midiPort.stream)

  var buttonBase = computed([setup.selectedChunkId, obs.chunkIds, pressed], function (selected, chunkIds, pressed) {
    var result = []
    for (var i = 0; i < 8; i++) {
      var chunkId = chunkIds[i]
      if (chunkId) {
        if (chunkId === selected) {
          result.push(light(2, 3))
        } else if (pressed[chunkId]) {
          result.push(light(0, 1))
        } else {
          result.push(light(1, 0))
        }
      } else {
        result.push(0)
      }
    }
    return result
  })

  var buttonFlash = FlashArray()
  setup.onTrigger(function (event) {
    if (event.id) {
      var chunkId = event.id.split('/')[0]
      var index = obs.chunkIds().indexOf(chunkId)
      if (event.event === 'start') {
        if (chunkId === setup.selectedChunkId()) {
          buttonFlash.flash(index, light(3, 3), 40)
        } else {
          buttonFlash.flash(index, light(3, 0), 40)
        }
      }
    }
  })

  var buttons = ObservMidi(midiPort.stream, mappings.trackFocus, ArrayStack([
    buttonBase,
    buttonFlash
  ]))

  buttons(function (values) {
    var result = null

    values.forEach(function (val, i) {
      if (val) {
        result = i
      }
    })

    if (result != null) {
      var id = obs.chunkIds()[result]
      if (id) {
        setup.selectedChunkId.set(id)
        setup.context.actions.scrollToSelectedChunk()
      }
    }
  })

  // CONTROL BUTTONS:
  var controlButtons = LightStack(midiPort.stream, {
    mode: mappings.device
  })

  controlButtons.mode(function (value) {
    if (value) {
      context.project.globalControllers.forEach(function (controller) {
        if (controller && controller.port && controller.port() === obs.port() && controller.grabInput) {
          controller.grabInput()
          controller.port.override.set(true)
        }
      })
    }
  })

  obs.destroy = function () {
    midiPort.destroy()
  }

  return obs
}
Example #3
0
module.exports = function (context) {
  var midiPort = MidiPort(context, function (port, lastPort) {
    // turn off on switch
    lastPort && lastPort.write(turnOffAll)
    port && port.write(turnOffAll)
  })

  var obs = ObservStruct({
    port: midiPort,
    chunkIds: Property([])
  })

  var releases = []
  var params = []
  var paramLoopers = []

  var recordingIndexes = Dict()
  var playingIndexes = Dict()
  var recordStarts = {}

  for (var i = 0; i < 8; i++) {
    params[i] = [
      Value(0),
      Value(0),
      Value(0)
    ]

    paramLoopers[i] = [
      ParamLooper(context, params[i][0]),
      ParamLooper(context, params[i][1]),
      ParamLooper(context, params[i][2])
    ]

    recordingIndexes.put(i, computed(paramLoopers[i].map(x => x.recording), (...args) => args.some(Boolean)))
    playingIndexes.put(i, computed(paramLoopers[i].map(x => x.playing), (...args) => args.some(Boolean)))
  }

  var bindingReleases = new Map()
  var bindings = MutantMap(obs.chunkIds, (id, invalidateOn) => {
    var item = context.chunkLookup.get(id)
    var index = obs.chunkIds().indexOf(id)
    invalidateOn(computed([context.chunkLookup, obs.chunkIds], (_, chunkIds) => {
      // rebind when chunk is changed
      return item !== context.chunkLookup.get(id) || chunkIds.indexOf(id) !== index
    }))
    if (item) {
      bindingReleases.set(item, item.overrideParams(paramLoopers[index]))
    }
    return item
  }, {
    onRemove: function (item) {
      if (bindingReleases.has(item)) {
        bindingReleases.get(item)()
        bindingReleases.delete(item)
      }
    }
  })

  releases.push(watch(bindings))

  // grab the midi for the current port
  obs.grabInput = function () {
    midiPort.grab()
  }

  obs.context = context

  var setup = context.setup

  watchKnobs(midiPort.stream, mappings.row1.concat(mappings.row2, mappings.row3), function (id, data) {
    var param = params[id % 8][Math.floor(id / 8)]
    var chunk = setup.context.chunkLookup.get(obs.chunkIds()[id % 8])
    if (chunk && chunk.overrideParams && chunk.params) {
      param.set(data / 128)
    }
  })

  var sliderState = []
  watchKnobs(midiPort.stream, mappings.sliders, function (id, data) {
    var chunk = setup.context.chunkLookup.get(obs.chunkIds()[id])
    if (chunk && chunk.overrideVolume) {
      var currentPosition = Math.pow(chunk.overrideVolume(), 1 / Math.E) * 108
      var newPosition = scaleInterpolate(currentPosition, data, sliderState[id] = sliderState[id] || {})
      chunk.overrideVolume.set(Math.pow(newPosition / 108, Math.E))
    }
  }, 108)

  var pressed = computed(MutantMap(setup.controllers, function (controller) {
    return controller && controller.currentlyPressed
  }), function (items) {
    return items.reduce(function (result, pressed) {
      if (pressed) {
        pressed.map(x => x && x.split('/')[0]).reduce(addIfUnique, result)
      }
      return result
    }, [])
  })

  var knobLights = computed([obs.chunkIds, setup.context.chunkLookup, pressed, setup.selectedChunkId], function (chunkIds, lookup, pressed, selected) {
    var result = []
    if (setup.context) {
      for (var i = 0; i < 8; i++) {
        var chunk = setup.context.chunkLookup.get(chunkIds[i])
        if (chunk && chunk.params) {
          var onValue = pressed.includes(chunkIds[i])
            ? light(0, 2)
            : selected === chunkIds[i]
              ? light(1, 1)
              : light(2, 0)
          result[0 + i] = chunk.params()[0] ? onValue : 0
          result[8 + i] = chunk.params()[1] ? onValue : 0
          result[16 + i] = chunk.params()[2] ? onValue : 0
        } else {
          result[0 + i] = result[8 + i] = result[16 + i] = 0
        }
      }
    }
    return result
  })

  setLights(knobLights, midiPort.stream)

  var buttonBase = computed([setup.selectedChunkId, obs.chunkIds, pressed], function (selected, chunkIds, pressed) {
    var result = []
    for (var i = 0; i < 8; i++) {
      var chunkId = chunkIds[i]
      if (chunkId) {
        if (chunkId === selected) {
          result.push(light(2, 3))
        } else if (pressed.includes(chunkId)) {
          result.push(light(0, 1))
        } else {
          result.push(light(1, 0))
        }
      } else {
        result.push(0)
      }
    }
    return result
  })

  var buttonFlash = FlashArray()
  setup.onTrigger(function (event) {
    if (event.id) {
      var chunkId = event.id.split('/')[0]
      var index = obs.chunkIds().indexOf(chunkId)
      if (event.event === 'start') {
        if (chunkId === setup.selectedChunkId()) {
          buttonFlash.flash(index, light(3, 3), 40)
        } else {
          buttonFlash.flash(index, light(3, 0), 40)
        }
      }
    }
  })

  var buttons = ObservMidi(midiPort.stream, mappings.trackFocus, ArrayStack([
    buttonBase,
    buttonFlash
  ]))

  buttons(function (values) {
    var result = null

    values.forEach(function (val, i) {
      if (val) {
        result = i
      }
    })

    if (result != null) {
      var id = obs.chunkIds()[result]
      if (id) {
        setup.selectedChunkId.set(id)
        setup.context.actions.scrollToSelectedChunk()
      }
    }
  })

  var recordButtonBase = computed([recordingIndexes, playingIndexes], function (recordingIndexes, playingIndexes) {
    var result = []
    for (var i = 0; i < 8; i++) {
      if (recordingIndexes[i]) {
        result[i] = light(3, 0)
      } else if (playingIndexes[i]) {
        result[i] = light(0, 3)
      } else {
        result[i] = 0
      }
    }
    return result
  })

  var recordButtons = ObservMidi(midiPort.stream, mappings.trackControl, recordButtonBase)
  recordButtons(function (values) {
    values.forEach(function (val, i) {
      paramLoopers[i].forEach(looper => looper.recording.set(!!val))

      if (val) {
        recordStarts[i] = Date.now()
      } else if (Date.now() - recordStarts[i] < 400) {
        paramLoopers[i].forEach(looper => looper.set(0))
      }
    })
  })

  // CONTROL BUTTONS:
  var controlButtons = LightStack(midiPort.stream, {
    mode: mappings.device
  })

  controlButtons.mode(function (value) {
    if (value) {
      context.project.globalControllers.forEach(function (controller) {
        if (controller && controller.port && controller.port() === obs.port() && controller.grabInput) {
          controller.grabInput()
          controller.port.override.set(true)
        }
      })
    }
  })

  obs.destroy = function () {
    while (releases.length) {
      releases.pop()()
    }
    for (var fn of bindingReleases.values()) {
      fn()
    }
    bindingReleases.clear()
    midiPort.destroy()
    paramLoopers.forEach(items => items.forEach(param => param.destroy()))
  }

  return obs
}