Esempio n. 1
0
function select (fn, data, opts) {
  var optionElements = opts.childNodes || MutantMap(opts.options, (option) => {
    return computed([option], optionElement)
  })

  return h('select', {
    'className': opts.flex ? '-flex' : '',
    'name': 'value',
    hooks: [
      SelectedValueHandler(fn, data, opts)
    ]
  }, [
    when(opts.includeBlank,
      h('option', {rawValue: null}, opts.includeBlank === true ? 'None' : opts.includeBlank)
    ),
    optionElements
  ])
}
Esempio n. 2
0
function TemplateSlot (context, shape) {
  var obs = Value({})

  // handle defaultValue
  var set = obs.set
  obs.set = function (v) {
    set(v == null ? {} : v)
  }

  var templateContext = Object.create(context)
  templateContext.template = true

  obs.context = context
  obs.node = null

  var releases = []
  var itemReleases = new Map()
  var broadcastAdd = null
  var broadcastRemove = null

  obs.nodeName = computed([obs], x => (x && x.node) || false)

  var ids = ShapeSlots(shape)
  var throttledValue = throttle(obs, 40)

  obs.slots = MutantMap(ids, function (id, invalidateOn) {
    var ctor = resolveNode(context.nodes, obs.nodeName())
    invalidateOn(obs.nodeName)

    if (ctor) {
      var result = ctor(context)
      var value = computed([throttledValue, {
        id: String(id),
        value: id,
        scale: '$inherit'
      }], obtainWithParams)
      var releases = [ watch(value, result.set) ]
      if (result.destroy) {
        releases.push(result.destroy)
      }
      itemReleases.set(result, releases)
      broadcastAdd(result)
      return result
    }
  }, {
    maxTime: 5,
    onRemove: function (item) {
      if (item != null) {
        if (item.destroy) {
          item.destroy()
        }
        if (itemReleases.has(item)) {
          itemReleases.get(item).forEach(fn => fn())
          itemReleases.delete(item)
          broadcastRemove(item)
        }
      }
    }
  })

  obs.slots.onAdd = Event(b => { broadcastAdd = b })
  obs.slots.onRemove = Event(b => { broadcastRemove = b })

  obs.slots.onNodeChange = Event(function (broadcast) {
    obs.slots.onAdd(broadcast)
    obs.slots.onRemove(broadcast)
  })

  var releaseSlots = watch(obs.slots, function () {
    // hold slots open until destroy
  })

  watch(obs.nodeName, function (nodeName) {
    var ctor = resolveNode(context.nodes, nodeName)

    // clean up last
    obs.node = null
    while (releases.length) {
      releases.pop()()
    }

    if (ctor) {
      var instance = ctor(templateContext)
      releases.push(doubleBind(obs, instance))

      if (instance.destroy) {
        releases.push(instance.destroy)
      }

      obs.node = instance
    }
  })

  obs.destroy = function () {
    releaseSlots()
    while (releases.length) {
      releases.pop()()
    }
    Array.from(itemReleases.values()).forEach(function (releases) {
      releases.forEach(fn => fn())
    })
    itemReleases.clear()
  }

  return obs
}
Esempio n. 3
0
function ExternalRouter (context, defaultValue, volume) {
  var obs = Dict()
  var releases = []
  obs.context = context

  var refreshing = false
  var destroyed = false

  var connections = new Map()
  var gains = new Map()

  var set = obs.set
  obs.set = function (v) {
    set(v == null ? defaultValue : v)
  }

  if (typeof volume === 'function') {
    volume(function (value) {
      gains.forEach(function (item) {
        item.gain.value = value
      })
    })
  }

  obs.refresh = function () {
    if (!refreshing) {
      refreshing = true
      setImmediate(refresh)
    }
  }

  releases.push(obs(obs.refresh))

  var destinationIds = MutantMap(toCollection(context.chunkLookup), (item, invalidateOn) => {
    if (item.value) {
      if (item.value.loaded) {
        invalidateOn(item.value.loaded)
      }
      return item.value.id
    }
  })

  if (context.chunkLookup) {
    releases.push(watch(destinationIds, obs.refresh))
  }

  obs.destroy = function () {
    destroyed = true
    Array.from(gains.keys()).forEach(function (key) {
      gains.get(key).disconnect()
      gains.delete(key)
    })

    while (releases.length) {
      releases.pop()()
    }
  }

  return obs

  // scoped

  function refresh () {
    if (destroyed) return false
    refreshing = false
    var usedGains = []
    var routes = obs() || {}
    Object.keys(routes).forEach(function (from) {
      var target = routes[from]
      var source = context.slotLookup.get(from)

      if (typeof target === 'string') {
        target = [target]
      }

      if (source && Array.isArray(target)) {
        if (!gains.has(source)) {
          var node = context.audio.createGain()
          gains.set(source, node)
          node.gain.value = typeof resolve(volume) === 'number' ? resolve(volume) : 1
          source.connect(node)
        }

        usedGains.push(gains.get(source))

        if (!connections.has(source)) {
          connections.set(source, [])
        }

        var destinations = target.map(function (to) {
          if (to && typeof to === 'string') {
            if (to === '$default') {
              return context.output
            } else {
              to = to.split('#')
              var destinationChunk = context.chunkLookup.get(to[0])
              var destinationSlot = destinationChunk && destinationChunk.getSlot(to[1])
              if (destinationSlot && destinationSlot.input) {
                return destinationSlot.input
              }
            }
          }
        }).filter(present)

        destinations.forEach(function (output) {
          if (!connections.get(source).includes(output)) {
            gains.get(source).connect(output)
            connections.get(source).push(output)
          }
        })

        connections.set(source, connections.get(source).filter(function (output) {
          if (!destinations.includes(output)) {
            gains.get(source).disconnect(output)
            return false
          } else {
            return true
          }
        }))
      }
    })

    // remove old unused routes
    Array.from(gains.keys()).forEach(function (key) {
      if (!usedGains.includes(gains.get(key))) {
        gains.get(key).disconnect()
        gains.delete(key)
      }
    })
  }
}
Esempio n. 4
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
}