Пример #1
0
function ChunkColors (chunkLookup, selected, targets, shape) {
  var highlighted = Observ()
  var timer = null

  highlighted.clear = function () {
    highlighted.set(null)
  }

  selected(function (id) {
    clearTimeout(timer)
    highlighted.set(id)
    timer = setTimeout(highlighted.clear, 400)
  })

  return computed([chunkLookup, targets, shape, highlighted], function computeMapGridValues (chunkLookup, targets, shape, highlighted) {
    var result = ArrayGrid([], shape)
    for (var r = 0; r < shape[0]; r++) {
      for (var c = 0; c < shape[1]; c++) {
        var id = getChunkId(targets[result.index(r, c)])
        var chunk = chunkLookup[id]
        if (chunk) {
          var color = saturate(notBlack(chunk.color, [5, 5, 5]), 2)
          if (highlighted === id) {
            color = normalize(color, 1)
          } else {
            color = normalize(color, 0.2)
          }
          result.set(r, c, color)
        }
      }
    }
    return result
  })
}
Пример #2
0
module.exports = function activeIndexes (obs) {
  return computed([obs], function (grid) {
    var result = []
    grid.data.forEach(pushIndexIfPresent, result)
    return result
  })
}
Пример #3
0
function createInput () {
  const keyboard = Keyboard()

  const changeSlide = computed([keyboard.keyDown], (num) => {
    return keycode(num) || 'void'
  })

  return changeSlide
}
Пример #4
0
function DiffData (first, last) {
  return {
    first: first,
    last: last,
    patch: Computed([first, last], function (b, p) {
      return patcher.computePatch(b, p)
    })
  }
}
Пример #5
0
function link(model, uri, text, expected) {
    return ["li", [
        ["a", {
            className: computed([model.route], function (route) {
                return route === expected ? "selected" : ""
            }),
            href: uri
        }, text]
    ]]
}
Пример #6
0
function mapGridValues(grid, trueValue){

  function mapColor(value){
    if (value){
      return trueValue
    }
  }

  return computed([grid], function computeMapGridValues(source){
    if (source){
      return ArrayGrid(source.data.map(mapColor), source.shape, source.stride)
    }
  })
}
Пример #7
0
function withResolved (obj, keys) {
  var result = computed(keys.map(function (k) { return obj[k] }).concat(obj), function (args) {
    var value = extend(arguments[arguments.length - 1])
    keys.forEach(function (key, i) {
      value[key] = arguments[i]
    })
    return value
  })

  for (var k in obj) {
    if (k !== 'set') {
      result[k] = obj[k]
    }
  }

  result.node = obj
  return result
}
Пример #8
0
function statsSection(model) {
    return ["footer.footer", {
        hidden: either(model.todosLength, false, true)
    }, [
        ["span.todo-count", [
            ["strong", model.todosLeft],
            computed([model.todosLength], function (len) {
                return len === 1 ? " item" : " items"
            }),
            " left"
        ]],
        ["ul.filters", [
            link(model, "#/", "All", "all"),
            link(model, "#/active", "Active", "active"),
            link(model, "#/completed", "Completed", "completed")
        ]]
    ]]
}
Пример #9
0
/*  Display := (
        db: SurfaceDB,
        viewModel: Object<String, Observable>
    ) => { view: DOMElement }
*/
function Display(database, viewModel) {
    var layer = database.layer("main")

    layer = simpleCache(layer, {
        deltaX: 32,
        deltaY: 32
    })
    layer = clip(layer)

    var canvas = CanvasRender(layer, {
        width: WIDTH,
        height: HEIGHT,
        blankColor: SKY_COLOR,
        grass: {
            width: CONSTANTS.SIZE,
            height: CONSTANTS.SIZE,
            color: GRASS_COLOR,
            outline: BLACK
        },
        dirt: {
            width: CONSTANTS.SIZE,
            height: CONSTANTS.SIZE,
            color: DIRT_COLOR,
            outline: BLACK
        },
        rock: {
            width: CONSTANTS.SIZE,
            height: CONSTANTS.SIZE,
            color: ROCK_COLOR,
            outline: null
        },
        entity: {
            width: CONSTANTS.SIZE,
            height: CONSTANTS.PLAYER_HEIGHT,
            color: CONSTANTS.PLAYER_COLOR,
            outline: "blue"
        }
    }, computed([viewModel.camera], SurfaceDB.Rectangle))

    return { view: canvas }
}
Пример #10
0
module.exports = function (obs) {
  // hack for mixer params
  var params = ['A', 'B', 'C']
  obs.params = computed([obs], function (values) {
    var usedParams = []
    JSON.stringify(values, function (key, value) {
      if (value && value.node === 'linkParam') {
        var index = params.indexOf(value.param)
        if (~index) {
          usedParams[index] = value.param
        }
      }
      return value
    })
    return usedParams
  })

  obs.paramValues = ObservVarhash({})
  obs.context.paramLookup = ParamLookup(obs.context.paramLookup, params, obs.paramValues)
  return obs.context.paramLookup.destroy
}
Пример #11
0
function todoItem(todo, model) {
    var className = computed([
        todo.completed, todo.editing
    ], function (completed, editing) {
        return (completed ? "completed " : "") +
            (editing ? "editing" : "")
    })

    return ["li", {
        className: className,
        // when events occur from jsonml-event
        // you can access the nearest bound model with
        // `ev.meta`
        meta: eventMeta(todo)
    }, [
        ["div.view", [
            ["input.toggle", {
                type: "checkbox",
                checked: todo.completed,
                change: event(model.events.toggle)
            }],
            ["label", {
                dblclick: event(model.events.editing)
            }, todo.title],
            ["button.destroy", {
                click: event(model.events.destroy)
            }]
        ]],
        ["input.edit", {
            value: todo.title,
            // focus primitive, when observable is triggered
            // it calls .focus() on this element
            focus: focus(todo.editing),
            submit: event(model.events.edit),
            blur: event(model.events.edit)
        }]
    ]]
}
Пример #12
0
function Highlighting (grid, selected) {
  var highlighted = Observ()
  var timer = null

  highlighted.clear = function () {
    highlighted.set(null)
  }

  selected(function (id) {
    clearTimeout(timer)
    highlighted.set(id)
    timer = setTimeout(highlighted.clear, 400)
  })

  return computed([grid, highlighted], function (grid, highlighted) {
    return ArrayGrid(grid.data.map(function (val) {
      var id = getChunkId(val)
      if (id === highlighted) {
        return true
      }
    }), grid.shape)
  })
}
Пример #13
0
 var doubles = arr.map(function (o) {
     return computed([o], function (o) { return o * 2 })
 })
Пример #14
0
function SlicerChunk (parentContext) {

  var context = Object.create(parentContext)
  var output = context.output = context.audio.createGain()
  context.output.connect(parentContext.output)

  var queueRefreshSlices = noargs(debounce(refreshSlices, 200))

  var slots = NodeArray(context)
  context.slotLookup = lookup(slots, 'id')

  var obs = BaseChunk(context, {
    sample: Sample(context),
    eq: EQ(context),

    sliceMode: Property('divide'),
    stretch: Property(false),
    tempo: Property(100),

    outputs: Property(['output']),
    routes: ExternalRouter(context, {output: '$default'}),
    volume: Property(1)
  }, {
    includedAllTriggers: true
  })

  var releaseMixerParams = applyMixerParams(obs)
  obs.overrideVolume = Property(1)

  var volume = computed([obs.volume, obs.overrideVolume], function (a, b) {
    return a * b
  })

  var watchApplied = obs.sample.resolvedBuffer(function (value) {
    if (value) {
      watchApplied()
      setImmediate(function () {
        obs.shape(queueRefreshSlices)
        obs.sample.resolvedBuffer(queueRefreshSlices)
        obs.sliceMode(queueRefreshSlices)
        obs.sample.mode(queueRefreshSlices)
        throttle(obs.sample.offset, 1000)(queueRefreshSlices)

        if (!obs.sample.slices()) { // ensure slices have been generated
          queueRefreshSlices()
        }
      })
    }
  })

   obs.sample.resolvedBuffer(function (value) {
     // without this everything breaks :( no idea why :(
   })

  var computedSlots = computedNextTick([
    obs.sample, obs.stretch, obs.tempo, obs.eq, volume, obs.sample.resolvedBuffer
  ], function (sample, stretch, tempo, eq, volume, buffer) {
    var result = (sample.slices || []).map(function (offset, i) {
      if (stretch && buffer) {

        var originalDuration = getOffsetDuration(buffer.duration, offset)
        var stretchedDuration = tempo / 60 * originalDuration

        return {
          node: 'slot',
          id: String(i),
          output: 'output',
          volume: volume,
          sources: [
            extend(sample, {
              node: 'source/granular',
              mode: 'oneshot',
              attack: 0.1,
              hold: 1,
              release: 0.1,
              duration: stretchedDuration,
              sync: true,
              offset: offset
            })
          ]
        }
      } else {
        return {
          node: 'slot',
          id: String(i),
          output: 'output',
          volume: volume,
          sources: [
            extend(sample, {
              node: 'source/sample',
              mode: 'oneshot',
              offset: offset
            })
          ]
        }
      }

    })

    result.unshift({
      node: 'slot',
      id: 'output',
      processors: [
        extend(eq, {node: 'processor/eq'})
      ]
    })

    return result
  })

  watch(computedSlots, slots.set)
  slots.onUpdate(obs.routes.refresh)

  obs.destroy = function(){
    releaseMixerParams()
    destroyAll(obs)
  }

  return obs

  // scoped
  function refreshSlices (cb) {
    var shape = obs.shape()
    var buffer = obs.sample.resolvedBuffer()
    var sliceMode = obs.sliceMode()
    var triggerMode = obs.sample.mode()
    var offset = obs.sample.offset()
    var count = shape[0] * shape[1]
    var playToEnd = triggerMode === 'full'
    if (sliceMode === 'peak' || sliceMode === 'transient') {
      if (buffer) {
        detectPeaks(buffer.getChannelData(0), count, offset, function (peaks) {
          obs.sample.slices.set(sliceOffsets(peaks, offset, playToEnd))
          cb && cb()
        })
      } else {
        cb && cb()
      }
    } else if (sliceMode === 'snap') {
      if (buffer) {
        gridSlicePeaks(buffer.getChannelData(0), count, offset, function (peaks) {
          obs.sample.slices.set(sliceOffsets(peaks, offset, playToEnd))
          cb && cb()
        })
      } else {
        cb && cb()
      }
    } else {
      obs.sample.slices.set(divideSlices(count, offset, playToEnd))
      cb && setImmediate(cb)
    }
  }

}
Пример #15
0
function SynthChunk (parentContext) {
  var context = Object.create(parentContext)
  context.output = context.audio.createGain()
  context.output.connect(parentContext.output)

  var slots = NodeArray(context)
  context.slotLookup = lookup(slots, 'id')

  var obs = BaseChunk(context, {
    osc1: Osc(context),
    osc2: Osc(context, {
      optional: true
    }),
    osc3: Osc(context, {
      optional: true,
      allowMultiply: true
    }),
    offset: Param(context, 0),
    amp: Param(context, 1),
    filter: Filter(context),
    eq: EQ(context),
    outputs: Property(['output']),
    volume: Property(1),
    routes: ExternalRouter(context, {output: '$default'})
  })

  var releaseMixerParams = applyMixerParams(obs)
  obs.overrideVolume = Property(1)

  var volume = computed([obs.volume, obs.overrideVolume], function (a, b) {
    return a * b
  })

  context.offset = obs.offset

  obs.amp.triggerable = true

  var scale = Property({
    offset: 0,
    notes: [0,2,4,5,7,9,11]
  })

  if (context.globalScale) {
    var releaseGlobalScale = watch(context.globalScale, scale.set)
  }

  var computedSlots = computed([
    obs.shape, obs.osc1, obs.osc2, obs.osc3, obs.filter, obs.amp, obs.eq, volume, scale
  ], function (shape, osc1, osc2, osc3, filter, amp, eq, volume, scale) {
    var length = shape[0] * shape[1]

    var result = [{
      node: 'slot',
      id: 'output',
      volume: volume,
      processors: [
        extend(eq, {node: 'processor/eq'}),
      ]
    }]

    for (var i = 0; i < length; i++) {
      var noteOffset = {
        node: 'modulator/scale',
        value: i,
        scale: scale
      }

      var sources = [
        extend(osc1, {node: 'source/oscillator' })
      ]

      var processors = [
        extend(filter, {node: 'processor/filter'}),
        { node: 'processor/gain',
          gain: amp
        }
      ]

      if (osc2.enabled) {
        sources.push(extend(osc2, {
          node: 'source/oscillator'
        }))
      }

      if (osc3.enabled) {
        if (osc3.multiply) {
          processors.unshift({
            node: 'processor/ring-modulator',
            carrier: osc3
          })
        } else {
          sources.push(extend(osc3,
            {node: 'source/oscillator'
          }))
        }
      }

      result.push({
        node: 'slot',
        id: String(i),
        output: 'output',
        noteOffset: noteOffset,
        sources: sources,
        processors: processors
      })
    }

    return result
  })

  computedSlots(slots.set)

  //throttleWatch(computedSlots, 50, function (value) {
  //  slots.set(value)
//
  //  // HACK: bump shape to trigger update of slot mapping
  //  obs.shape.set(obs.shape())
  //})

  slots.onUpdate(obs.routes.refresh)

  obs.destroy = function () {
    releaseMixerParams()
    destroyAll(obs)
    slots.destroy()
    releaseGlobalScale && releaseGlobalScale()
    releaseGlobalScale = null
  }
  
  return obs
}
Пример #16
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
}
Пример #17
0
module.exports = function(context){

  var loopGrid = LoopGrid(context)
  var looper = Looper(loopGrid)

  var scheduler = context.scheduler
  var gridMapping = getLaunchpadGridMapping()
  loopGrid.shape.set(gridMapping.shape)

  var shiftHeld = false
  var activatedAt = 0

  var midiPort = MidiPort(context, function (port, lastPort) {
    // turn off on switch
    lastPort && lastPort.write([176, 0, 0])
    if (port) {
      port.write([176, 0, 0])
      activatedAt = Date.now()
    }
  })

  // extend loop-grid instance
  var obs = ObservStruct({
    port: midiPort,
    loopLength: loopGrid.loopLength,
    chunkPositions: ObservVarhash({})
  })

  obs.gridState = ObservStruct({
    active: loopGrid.active,
    playing: loopGrid.playing,
    recording: looper.recording,
    triggers: loopGrid.grid
  })

  obs.activeInput = computed([midiPort.stream], function (value) {
    return !!value
  })

  watch(looper, loopGrid.loops.set)

  obs.context = context
  obs.playback = loopGrid
  obs.looper = looper
  obs.repeatLength = Observ(2)

  var flags = computeFlags(context.chunkLookup, obs.chunkPositions, loopGrid.shape)

  watch( // compute targets from chunks
    computeTargets(context.chunkLookup, obs.chunkPositions, loopGrid.shape),
    loopGrid.targets.set
  )

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

  // loop transforms
  var transforms = {
    selector: Selector(gridMapping.shape, gridMapping.stride),
    holder: Holder(looper.transform),
    mover: Mover(looper.transform),
    repeater: Repeater(looper.transformTop),
    suppressor: Suppressor(looper.transform, gridMapping.shape, gridMapping.stride)
  }

  var outputLayers = ObservGridStack([

    // recording
    mapGridValue(looper.recording, stateLights.redLow),

    // active
    mapGridValue(loopGrid.active, stateLights.greenLow),

    // flash selected chunk
    mapGridValue(Highlighting(loopGrid.grid, context.setup.selectedChunkId), stateLights.amberLow),

    // selected
    mapGridValue(transforms.selector, stateLights.green),

    // suppressing
    mapGridValue(transforms.suppressor, stateLights.red),

    // playing
    mapGridValue(loopGrid.playing, stateLights.amber)

  ])

  var controllerGrid = ObservMidi(midiPort.stream, gridMapping, outputLayers)
  var inputGrabber = GrabGrid(controllerGrid)

  var noRepeat = computeIndexesWhereContains(flags, 'noRepeat')
  var grabInputExcludeNoRepeat = function (listener) {
    return inputGrabber(listener, { exclude: noRepeat })
  }

  var inputGrid = Observ()
  watch(inputGrabber, inputGrid.set)
  var activeIndexes = computeActiveIndexes(inputGrid)

  // trigger notes at bottom of input stack
  var output = DittyGridStream(inputGrid, loopGrid.grid, context.scheduler)
  output.on('data', loopGrid.triggerEvent)

  obs.currentlyPressed = computed([controllerGrid, loopGrid.grid], function (value, grid) {
    return grid.data.filter(function (name, index) {
      if (value.data[index]) {
        return true
      }
    })
  })

  // midi button mapping
  var buttons = MidiButtons(midiPort.stream, {
    store: '176/104',
    flatten: '176/105',
    undo: '176/106',
    redo: '176/107',
    hold: '176/108',
    suppress: '176/109',
    snap2: '176/110',
    select: '176/111'
  })

  var releaseLoopLengthLights = []

  watchButtons(buttons, {

    store: function(value){
      if (value){
        this.flash(stateLights.green)
        looper.store()
      }
    },

    flatten: function(value){
      if (value){
        var active = activeIndexes()
        if (looper.isTransforming() || active.length){
          looper.transform(holdActive, active)
          looper.flatten()
          transforms.selector.stop()
          this.flash(stateLights.green, 100)
        } else {
          this.flash(stateLights.red, 100)
          transforms.suppressor.start(transforms.selector.selectedIndexes())
          looper.flatten()
          transforms.suppressor.stop()
          transforms.selector.stop()
        }
      }
    },

    undo: function(value){
      if (value){
        if (shiftHeld){ // halve loopLength
          var current = obs.loopLength() || 1
          obs.loopLength.set(current/2)
          this.flash(stateLights.green, 100)
        } else {
          looper.undo()
          this.flash(stateLights.red, 100)
          buttons.store.flash(stateLights.red)
        }
      }
    },

    redo: function(value){
      if (value){
        if (shiftHeld){ // double loopLength
          var current = obs.loopLength() || 1
          obs.loopLength.set(current*2)
          this.flash(stateLights.green, 100)
        } else {
          looper.redo()
          this.flash(stateLights.red, 100)
          buttons.store.flash(stateLights.red)
        }
      }
    },

    hold: function(value){
      if (value){
        var turnOffLight = this.light(stateLights.yellow)
        transforms.holder.start(
          scheduler.getCurrentPosition(),
          transforms.selector.selectedIndexes(),
          turnOffLight
        )
      } else {
        transforms.holder.stop()
      }
    },

    suppress: function(value){
      if (value){
        var turnOffLight = this.light(stateLights.red)
        transforms.suppressor.start(transforms.selector.selectedIndexes(), turnOffLight)
      } else {
        transforms.suppressor.stop()
      }
    },

    swapTarget: function (value) {
      if (value) {
        getPortSiblings(obs, context.setup.controllers)[1].grabInput()
      } else if (Date.now() - activatedAt > 500) {
        getPortSiblings(obs, context.setup.controllers)[0].grabInput()
      }
    },

    select: function(value){
      if (value){
        var turnOffLight = this.light(stateLights.green)
        transforms.selector.start(inputGrabber, function done(){
          transforms.mover.stop()
          transforms.selector.clear()
          turnOffLight()
        })
      } else {
        if (transforms.selector.selectedIndexes().length){
          transforms.mover.start(inputGrabber, transforms.selector.selectedIndexes())
        } else {
          transforms.selector.stop()
        }
      }
    }
  })

  // shift button (share select button)
  watch(buttons.select, function(value){
    if (value){
      shiftHeld = true

      // turn on loop length lights
      releaseLoopLengthLights.push(
        buttons.undo.light(stateLights.greenLow),
        buttons.redo.light(stateLights.greenLow)
      )

    } else {
      shiftHeld = false

      // turn off loop length lights
      while (releaseLoopLengthLights.length){
        releaseLoopLengthLights.pop()()
      }
    }
  })

  // light up undo buttons by default
  buttons.undo.light(stateLights.redLow)
  buttons.redo.light(stateLights.redLow)

  buttons.store.light(stateLights.amberLow)


  var willFlatten = computed([activeIndexes, looper.transforms], function (indexes, transforms) {
    return !!indexes.length || !!transforms.length
  })

  // light up store button when transforming (flatten mode)
  var releaseFlattenLight = null
  watch(willFlatten, function(value){
    if (value && !releaseFlattenLight){
      releaseFlattenLight = buttons.flatten.light(stateLights.greenLow)
    } else if (!value && releaseFlattenLight){
      releaseFlattenLight()
      releaseFlattenLight = null
    }
  })


  var repeatButtons = MidiButtons(midiPort.stream, {
    0: '144/8',
    1: '144/24',
    2: '144/40',
    3: '144/56',
    4: '144/72',
    5: '144/88',
    6: '144/104',
    7: '144/120'
  })

  // repeater
  var releaseRepeatLight = null
  mapWatchDiff(repeatStates, repeatButtons, obs.repeatLength.set)
  watch(obs.repeatLength, function(value){
    var button = repeatButtons[repeatStates.indexOf(value)]
    if (button){
      if (releaseRepeatLight) releaseRepeatLight()
      releaseRepeatLight = button.light(stateLights.amberLow)
    }
    transforms.holder.setLength(value)
    if (value < 2){
      transforms.repeater.start(grabInputExcludeNoRepeat, value)
    } else {
      transforms.repeater.stop()
    }
  })


  // visual metronome / loop position
  var releaseBeatLight = null
  var currentBeatLight = null
  var currentBeat = null

  watch(loopGrid.loopPosition, function(value){
    var beat = Math.floor(value[0])
    var index = Math.floor(value[0] / value[1] * 8)
    var button = repeatButtons[index]

    if (index != currentBeatLight){
      if (button){
        releaseBeatLight&&releaseBeatLight()
        releaseBeatLight = button.light(stateLights.greenLow, 0)
      }
      currentBeatLight = index
    }

    if (beat != currentBeat){
      button.flash(stateLights.green)
      currentBeat = beat
    }
  })

  // cleanup / disconnect from keyboard on destroy

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

  return obs
}
Пример #18
0
module.exports = function applyParams (obs) {
  // HACK: make ring modulator effect work on output channel
  var triggeredSlots = []
  obs.slots(function (slots) {
    obs.slots.forEach(function (slot) {
      var id = slot.id()
      if (!isFinite(id) && !~triggeredSlots.indexOf(id)) {
        triggeredSlots.push(slot.id)
        slot.triggerOn(obs.context.audio.currentTime)
      }
    })
  })

  var paramOverrideStack = ObservArray([])
  obs.overrideParams = function (params) {
    paramOverrideStack.push(params)
    return function release () {
      var index = paramOverrideStack.indexOf(params)
      if (~index) {
        paramOverrideStack.splice(index, 1)
      }
    }
  }

  var raw = {}

  var paramLookup = computed([obs.params, obs.paramValues, paramOverrideStack], function (params, values, overrides) {
    var result = {}
    var rawResult = {}
    for (var i = 0; i < params.length; i++) {
      var key = params[i]
      var override = paramOverrideStack.get(paramOverrideStack.getLength() - 1)
      if (override && override[i] != null) {
        result[key] = typeof override[i] === 'function' ? override[i]() : override[i] || 0
        rawResult[key] = override[i]
      } else {
        result[key] = values && values[key] || 0
        rawResult[key] = obs.paramValues.get(key)
      }
    }
    raw = rawResult
    return result
  })

  paramLookup.get = function(key) {
    return raw[key]
  }

  paramLookup.keys = function(key) {
    return Object.keys(raw)
  }

  obs.context.paramLookup = paramLookup

  obs.resolveAvailableParam = function(id){
    var base = id
    var items = obs.params()
    var incr = 0

    while (~items.indexOf(id)){
      incr += 1
      id = base + ' ' + (incr + 1)
    }

    return id
  }
}
Пример #19
0
function LoopGrid(opts, additionalProperties){
 
  // required options:
  var player = opts.player
  var recorder = opts.recorder

  // optional:
  var shape = opts.shape || [8,8]
  var scheduler = opts.scheduler || null
  var triggerOutput = opts.triggerOutput || null



  var obs = ObservStruct(xtend({
    node: Observ(), // this is useful for loop-drop-setup :)
    chunkPositions: ObservVarhash({})
  }, additionalProperties))

  obs.transforms = ObservArray([])

  var soundChunkLookup = {}

  var undos = []
  var redos = []

  var baseLoops = {}
  var currentLoops = {}
  var releases = obs._releases = []

  releases.push(

    // update playback when transforms change
    obs.transforms(refreshCurrent)

  )

  obs.chunkState = Observ([])
  obs.flags = Observ({})
  obs.triggerIds = Observ([])
  obs.loopLength = Observ(8)


  obs.grid = computed([obs.chunkPositions, opts.chunkLookup], function(chunkPositions, chunkLookup){
    var result = Grid([], shape)
    var flags = Grid([], shape)
    var triggerIds = []
    var chunkState = []

    if (chunkPositions){
      soundChunkLookup = {}
      Object.keys(chunkPositions).forEach(function(chunkId){
        var chunk = chunkLookup[chunkId]
        var origin = chunkPositions[chunkId]
        if (chunk && origin){
          chunkState.push({
            id: chunkId, 
            origin: origin, 
            shape: chunk.grid.shape, 
            stride: chunk.grid.stride, 
            node: chunk.node,
            color: chunk.color
          })
          result.place(origin[0], origin[1], chunk.grid)
          for (var k in chunk.flags){
            if (Array.isArray(chunk.flags[k]) && chunk.flags[k].length){
              var index = result.data.indexOf(k)
              if (~index){
                flags.data[index] = chunk.flags[k]
              }
            }
          }
          for (var i=0;i<chunk.grid.data.length;i++){
            if (chunk.grid.data[i] != null){
              triggerIds.push(chunk.grid.data[i])
              soundChunkLookup[chunk.grid.data[i]] = chunk.id
            }
          }
        }
      })
    }

    obs.chunkState.set(chunkState)
    obs.flags.set(flags)
    obs.triggerIds.set(triggerIds)

    return result
  })

  if (triggerOutput){
    obs.playing = computedDittyGrid(triggerOutput, obs.grid)
  }

  if (player){
    obs.active = computedActiveGrid(player, obs.grid)
  }

  if (scheduler){
    obs.loopPosition = computedLoopPosition(scheduler, obs.loopLength)
  }

  if (scheduler && triggerOutput){
    obs.recording = computedRecording(scheduler, triggerOutput, obs.grid, obs.loopLength)
  }

  if (obs.playing && obs.active && obs.recording){

    // for binding to grid visual interface
    obs.gridState = computed([
      obs.grid, obs.playing, obs.active, obs.recording, obs.loopPosition, obs.loopLength
    ], function(grid, playing, active, recording, loopPosition, loopLength){
      var length = grid.data.length
      var result = []
      for (var i=0;i<length;i++){
        if (grid.data[i]){
          result[i] = {
            id: grid.data[i],
            isPlaying: playing.data[i],
            isActive: active.data[i],
            isRecording: recording.data[i]
          }
        }
      }

      return {
        loopPosition: loopPosition,
        loopLength: loopLength,
        grid: Grid(result, grid.shape, grid.stride),
        chunks: obs.chunkState()
      }
      
    })

  }

  obs.destroy = function(){
    if (obs.playing) obs.playing.destroy()
    if (obs.active) obs.active.destroy()
    if (obs.loopPosition) obs.loopPosition.destroy()
    if (obs.recording) obs.recording.destroy()
    releases.forEach(invoke)
    releases = []
  }

  // grab loops from recorder for all sounds currently in grid and set loop
  obs.store = function(length, start){

    // defaults
    length = length || obs.loopLength()
    start = start == null && opts.scheduler ? 
      opts.scheduler.getCurrentPosition() - length : start

    undos.push(baseLoops)

    var snapshot = obs.triggerIds().reduce(function(result, id){
      result[id] = {
        events: recorder.getLoop(id, start, length), 
        length: length
      }
      return result
    }, {})
    setBase(snapshot)
  }

  obs.transform = function(func, args){
    // transform relative to grid
    var t = {
      func: func,
      args: Array.prototype.slice.call(arguments, 1)
    }

    obs.transforms.push(t)

    return function release(){
      var index = obs.transforms.indexOf(t)
      if (~index){
        obs.transforms.splice(index, 1)
      }
    }
  }

  obs.isTransforming = function(){
    return !!obs.transforms.getLength()
  }

  obs.flatten = function(){
    // flatten transforms
    undos.push(baseLoops)
    targetLoops = currentLoops
    obs.transforms.set([])
    setBase(targetLoops)
  }

  obs.undo = function(){
    var snapshot = undos.pop()
    if (snapshot){
      redos.push(baseLoops)
      setBase(snapshot)
    }
  }

  obs.redo = function(){
    var snapshot = redos.pop()
    if (snapshot){
      undos.push(baseLoops)
      setBase(snapshot)
    }
  }

  return obs

  
  // scoped
  function refreshCurrent(){
    currentLoops = gridTransform(baseLoops, obs.transforms())
    obs.triggerIds().forEach(function(id){
      var channel = currentLoops[id]
      if (channel && channel.events && channel.events.length){
        player.set(id, channel.events, channel.length)
      } else {
        player.set(id, null)
      }
      currentLoops[id] = channel
    })
  }

  function cloneBaseLoop(id){
    if (baseLoops[id] && baseLoops[id].events){
      return {
        events: baseLoops[id].events.concat(),
        length: baseLoops[id].length
      }
    } else {
      return null 
    }
    return baseLoops[id]
  }

  function wrapToLookup(result, value, index){
    var id = obs.grid().data[index]
    if (id != null){
      result[id] = value
    }
    return result
  }

  function performTransform(input, f){
    return f.func.apply(this, [input].concat(f.args||[]))
  }

  function gridTransform(input, transforms){
    if (transforms && transforms.length){
      var soundGrid = obs.grid()
      var data = soundGrid.data.map(cloneBaseLoop)
      var playbackGrid = Grid(data, soundGrid.shape, soundGrid.stride)

      // perform transform
      soundGrid = transforms.reduce(performTransform, playbackGrid)

      // turn back into loop lookup
      return soundGrid.data.reduce(wrapToLookup, {})
    } else {
      return input
    }
  }

  function setBase(snapshot){
    baseLoops = {}
    obs.triggerIds().forEach(function(id){
      baseLoops[id] = snapshot[id]
    })
    refreshCurrent()
  }
}
Пример #20
0
module.exports = function(context){

  var loopGrid = LoopGrid(context)
  var looper = Looper(loopGrid)

  var scheduler = context.scheduler
  var gridMapping = getPushGridMapping()
  loopGrid.shape.set(gridMapping.shape)

  var shiftHeld = false

  // controller midi port
  var midiPort = MidiPort(context, function (stream, lastStream) {
    if (lastStream) turnOffAllLights(lastStream)
    if (stream) {
      turnOffAllLights(stream)
      initDisplay()
    }
  })

  // Push display
  var display = new PushDisplay(midiPort.stream)

  // extend loop-grid instance
  var obs = ObservStruct({
    port: midiPort,
    loopLength: loopGrid.loopLength,
    chunkPositions: ObservVarhash({})
  })

  obs.gridState = ObservStruct({
    active: loopGrid.active,
    playing: loopGrid.playing,
    recording: looper.recording,
    triggers: loopGrid.grid
  })

  watch(looper, loopGrid.loops.set)

  obs.context = context
  obs.playback = loopGrid
  obs.looper = looper
  obs.repeatLength = Observ(2)

  var flags = computeFlags(context.chunkLookup, obs.chunkPositions, loopGrid.shape)

  watch( // compute targets from chunks
    computeTargets(context.chunkLookup, obs.chunkPositions, loopGrid.shape),
    loopGrid.targets.set
  )

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

  // loop transforms
  var transforms = {
    selector: Selector(gridMapping.shape, gridMapping.stride),
    holder: Holder(looper.transform),
    mover: Mover(looper.transform),
    repeater: Repeater(looper.transformTop),
    suppressor: Suppressor(looper.transform, gridMapping.shape, gridMapping.stride)
  }

  var outputLayers = ObservGridStack([

    // recording
    mapGridValue(looper.recording, pushColors.pads.redLow),

    // active
    mapGridValue(loopGrid.active, pushColors.pads.greenLow),

    // selected
    mapGridValue(transforms.selector, pushColors.pads.blue),

    // suppressing
    mapGridValue(transforms.suppressor, pushColors.pads.red),

    // playing
    mapGridValue(loopGrid.playing, pushColors.pads.limeHi)

  ])

  var controllerGrid = ObservMidi(midiPort.stream, gridMapping, outputLayers)
  var inputGrabber = GrabGrid(controllerGrid)

  var noRepeat = computeIndexesWhereContains(flags, 'noRepeat')
  var freezeSuppress = computeIndexesWhereContains(flags, 'freezeSuppress')

  var grabInputExcludeNoRepeat = function (listener) {
    return inputGrabber(listener, { exclude: noRepeat })
  }

  var inputGrid = Observ()
  watch(inputGrabber, inputGrid.set)
  var activeIndexes = computeActiveIndexes(inputGrid)

  // trigger notes at bottom of input stack
  var output = DittyGridStream(inputGrid, loopGrid.grid, context.scheduler)
  output.on('data', loopGrid.triggerEvent)

  // midi command button mapping
  // On Push this is the row right above the pads
  var buttons = MidiButtons(midiPort.stream, {
    store: '176/102',
    flatten: '176/103',
    undo: '176/104',
    redo: '176/105',
    hold: '176/106',
    suppress: '176/107',
    snap2: '176/108',
    select: '176/109'
  })

  var releaseLoopLengthLights = []

  watchButtons(buttons, {

    store: function(value){
      if (value){
        this.flash(pushColors.pads.green)
        looper.store()
      }
    },

    flatten: function(value){
      if (value){
        var active = activeIndexes()
        if (looper.isTransforming() || active.length){
          looper.transform(holdActive, active)
          looper.flatten()
          transforms.selector.stop()
          this.flash(pushColors.pads.green, 100)
        } else {
          this.flash(pushColors.pads.red, 100)
          transforms.suppressor.start(scheduler.getCurrentPosition(), transforms.selector.selectedIndexes())
          looper.flatten()
          transforms.suppressor.stop()
          transforms.selector.stop()
        }
      }
    },

    undo: function(value){
      if (value){
        if (shiftHeld){ // halve loopLength
          var current = obs.loopLength() || 1
          obs.loopLength.set(current/2)
          this.flash(pushColors.pads.green, 100)
        } else {
          looper.undo()
          this.flash(pushColors.pads.red, 100)
          buttons.store.flash(pushColors.pads.red)
        }
      }
    },

    redo: function(value){
      if (value){
        if (shiftHeld){ // double loopLength
          var current = obs.loopLength() || 1
          obs.loopLength.set(current*2)
          this.flash(pushColors.pads.green, 100)
        } else {
          looper.redo()
          this.flash(pushColors.pads.red, 100)
          buttons.store.flash(pushColors.pads.red)
        }
      }
    },

    hold: function(value){
      if (value){
        var turnOffLight = this.light(pushColors.pads.yellow)
        transforms.holder.start(
          scheduler.getCurrentPosition(),
          transforms.selector.selectedIndexes(),
          turnOffLight
        )
      } else {
        transforms.holder.stop()
      }
    },

    suppress: function (value) {
      if (value) {
        var turnOffLight = this.light(pushColors.pads.red)
        transforms.suppressor.start(scheduler.getCurrentPosition(), transforms.selector.selectedIndexes(), freezeSuppress(), turnOffLight)
      } else {
        transforms.suppressor.stop()
      }
    },

    select: function(value){
      if (value){
        var turnOffLight = this.light(pushColors.pads.green)
        transforms.selector.start(inputGrabber, function done(){
          transforms.mover.stop()
          transforms.selector.clear()
          turnOffLight()
        })
      } else {
        if (transforms.selector.selectedIndexes().length){
          transforms.mover.start(inputGrabber, transforms.selector.selectedIndexes())
        } else {
          transforms.selector.stop()
        }
      }
    }
  })

  // shift button (share select button)
  watch(buttons.select, function(value){
    if (value){
      shiftHeld = true

      // turn on loop length lights
      releaseLoopLengthLights.push(
        buttons.undo.light(pushColors.pads.pink),
        buttons.redo.light(pushColors.pads.pink)
      )

      // Update display
      display
      .setCell(3, 2, "Halve")
      .setCell(3, 3, "Double")
      .update();

    } else {
      shiftHeld = false

      // turn off loop length lights
      while (releaseLoopLengthLights.length){
        releaseLoopLengthLights.pop()()
      }

      // Update display
      display
      .setCell(3, 2, "Undo")
      .setCell(3, 3, "Redo")
      .update();
    }
  })

  // light up undo buttons by default
  buttons.undo.light(pushColors.pads.cyan)
  buttons.redo.light(pushColors.pads.cyan)

  buttons.store.light(pushColors.pads.amberLow)


  var willFlatten = computed([activeIndexes, looper.transforms], function (indexes, transforms) {
    return !!indexes.length || !!transforms.length
  })

  // light up store button when transforming (flatten mode)
  var releaseFlattenLight = null
  watch(willFlatten, function(value){
    if (value && !releaseFlattenLight){
      releaseFlattenLight = buttons.flatten.light(pushColors.pads.greenLow)
    } else if (!value && releaseFlattenLight){
      releaseFlattenLight()
      releaseFlattenLight = null
    }
  })

  // Push side buttons - labels don't match, left here for reference
  // var repeatButtons = MidiButtons(duplexPort, {
  //   0: '176/43',
  //   1: '176/42',
  //   2: '176/41',
  //   3: '176/40',
  //   4: '176/39',
  //   5: '176/38',
  //   6: '176/37',
  //   7: '176/36'
  // })

  // Push top row buttons
  var repeatButtons = MidiButtons(midiPort.stream, {
    0: '176/20',
    1: '176/21',
    2: '176/22',
    3: '176/23',
    4: '176/24',
    5: '176/25',
    6: '176/26',
    7: '176/27'
  })

  // repeater
  var releaseRepeatLight = null
  mapWatchDiff(repeatStates, repeatButtons, obs.repeatLength.set)
  watch(obs.repeatLength, function(value){
    var button = repeatButtons[repeatStates.indexOf(value)]
    if (button){
      if (releaseRepeatLight) releaseRepeatLight()
      releaseRepeatLight = button.light(pushColors.buttons.amberLow)
    }
    transforms.holder.setLength(value)
    if (value < 2){
      transforms.repeater.start(grabInputExcludeNoRepeat, value)
    } else {
      transforms.repeater.stop()
    }
  })


  // visual metronome / loop position
  var releaseBeatLight = null
  var currentBeatLight = null
  var currentBeat = null

  watch(loopGrid.loopPosition, function(value){
    var beat = Math.floor(value[0])
    var index = Math.floor(value[0] / value[1] * 8)
    var button = repeatButtons[index]

    if (index != currentBeatLight){
      if (button){
        releaseBeatLight&&releaseBeatLight()
        releaseBeatLight = button.light(pushColors.buttons.greenLow, 0)
      }
      currentBeatLight = index
    }

    if (beat != currentBeat){
      button.flash(pushColors.buttons.greenHi)
      currentBeat = beat
    }
  })

  // cleanup / disconnect from keyboard on destroy

  obs.destroy = function(){
    midiPort.destroy()
    display.init()
    output.destroy()
    loopGrid.destroy()
  }

  return obs

  // scoped

  function initDisplay() {
    // Clear screen
    display.init();

    // Top line
    display
    .setCell(0, 3, "    Loop")
    .setCell(0, 4, "Drop");

    // Repeats
    display
    .setCell(2, 0, "Trigger")
    .setCell(2, 1, "   1")
    .setCell(2, 2, "  2/3")
    .setCell(2, 3, "  1/2")
    .setCell(2, 4, "  1/3")
    .setCell(2, 5, "  1/4")
    .setCell(2, 6, "  1/6")
    .setCell(2, 7, "  1/8");

    // Buttons
    display
    .setCell(3, 0, "RecLoop")
    .setCell(3, 1, "Clr/Flat")
    .setCell(3, 2, "Undo")
    .setCell(3, 3, "Redo")
    .setCell(3, 4, "BeatHold")
    .setCell(3, 5, "Suppress")
    .setCell(3, 6, "SwapTrgt")
    .setCell(3, 7, "Select");

    display.update();
  }

  function turnOffAllLights (port) {
    var LOW_PAD = 36, // Bottom left
        HI_PAD = 99,  // Top Right
        LOW_REPEAT = 10,
        HI_REPEAT = 27,
        LOW_BUTTON = 102,
        HI_BUTTON = 109;

    // Clear notes
    for (var pad = LOW_PAD; pad <= HI_PAD; pad++) {
      port.write([128, pad, 0]);
    }

    // Clear repeat buttons
    for (var button = LOW_REPEAT; button <= HI_REPEAT; button++) {
      port.write([176, button, 0]);
    }

    // Clear buttons
    for (var button = LOW_BUTTON; button <= HI_BUTTON; button++) {
      port.write([176, button, 0]);
    }
  }
}
Пример #21
0
function BaseChunk (context, extraProperties, opts) {

  var obs = ObservStruct(extend({
    id: Observ(),
    shape: Property([1,4]),
    flags: Property([]),
    chokeAll: Property(false),
    chokeGroup: Property(),
    color: Property([255,255,255])
  }, extraProperties))

  if (context.setup) {
    obs.selected = computed([obs.id, context.setup.selectedChunkId], function (id, selectedId) {
      return id === selectedId
    })
  }

  obs.context = context

  obs.triggerOn = function(id, at){
    var slot = context.slotLookup.get(id)
    var shape = obs.shape()
    var length = shape[0] * shape[1]

    if (obs.chokeGroup()) {
      var chokeGroup = resolve(obs.chokeGroup)
      context.setup.chunks.forEach(function (chunk) {
        if (chunk && chunk !== obs && resolve(chunk.chokeGroup) === chokeGroup) {
          chokeAll(chunk)
        }
      })
    }

    if (obs.chokeAll()) {
      chokeAll(obs, at)
    }

    if (slot) {
      if (slot().chokeGroup) {
        obs.triggers().forEach(function (id) {
          var otherSlot = context.slotLookup.get(id)
          if (otherSlot && otherSlot !== slot && otherSlot().chokeGroup === slot().chokeGroup) {
            otherSlot.choke && otherSlot.choke(at)
          }
        })
      }
      slot.triggerOn(at)
    }
  }

  obs.triggerOff = function(id, at){
    var slot = context.slotLookup.get(id)
    if (slot){
      slot.triggerOff(at)
    }
  }

  obs.choke = function (id, at) {
    var slot = context.slotLookup.get(id)
    if (slot && slot.choke){
      slot.choke(at)
    }
  }

  obs.getSlot = function(id){
    return context.slotLookup.get(id)
  }

  obs.triggers = computed([obs.id, obs.shape, context.slotLookup], function(id, shape){
    var length = shape[0] * shape[1]
    var result = []
    for (var i=0;i<length;i++){
      if (obs.getSlot(String(i)) || (opts && opts.includedAllTriggers)) {
        result.push(String(i))
      } else {
        result.push(null)
      }
    }
    return result
  })

  obs.grid = computed([obs.triggers, obs.shape], ArrayGrid)

  obs.resolvedGrid = computed([obs.triggers, obs.shape], function(triggers, shape){
    return ArrayGrid(triggers.map(getGlobalId), shape)
  })

  return obs

  // scoped

  function getGlobalId(id){
    if (id){
      return obs.id() + '/' + id
    }
  }
}
Пример #22
0
function Setup(parentContext){

  var context = Object.create(parentContext)
  var audioContext = context.audio
  var refreshingParamCount = false

  var node = ObservStruct({
    controllers: NodeArray(context),
    chunks: NodeArray(context),
    selectedChunkId: Observ(),
    volume: Property(1),
    globalScale: Property({
      offset: 0,
      notes: [0,2,4,5,7,9,11]
    })
  })

  node.overrideVolume = Property(1)
  node.overrideLowPass = Property(0)
  node.overrideHighPass = Property(0)

  node._type = 'LoopDropSetup'

  context.setup = node
  context.globalScale = node.globalScale

  // main output
  context.output = audioContext.createGain()

  // mixer FX
  var outputLowPass = audioContext.createBiquadFilter()
  outputLowPass.Q.value = 0
  var outputHighPass = audioContext.createBiquadFilter()
  outputHighPass.Q.value = 0
  outputHighPass.type = 'highpass'

  context.output.connect(outputLowPass)
  outputLowPass.connect(outputHighPass)
  node.output = YankSilence(audioContext, outputHighPass)
  node.output.connect(parentContext.output)

  context.active = node.output.active

  watch(computed([node.volume, node.overrideVolume], (a, b) => a * b), function (value) {
    node.output.gain.value = value
  })

  watch(node.overrideLowPass, function (value) {
    outputLowPass.frequency.setTargetAtTime(interpolate(value, 20000, 20, 'exp'), audioContext.currentTime, 0.01)
  })

  watch(node.overrideHighPass, function (value) {
    outputHighPass.frequency.setTargetAtTime(interpolate(value, 20, 15000, 'exp'), audioContext.currentTime, 0.01)
  })

  node.onTrigger = Event(function (b) {
    context.triggerEvent = b
  })

  node.onTrigger(function (event) {
    if (event.id) {
      node.output.trigger()
      var split = event.id.split('/')
      var chunk = context.chunkLookup.get(split[0])
      var slotId = split[1]
      if (chunk) {
        if (event.event === 'start' && event.time >= context.audio.currentTime - 0.001) {
          chunk.triggerOn(slotId, event.time)
        } else if (event.event === 'stop') {
          chunk.triggerOff(slotId, event.time)
        }
      }
    }
  })

  node.chunks.resolveAvailable = function(id){
    var base = id
    var lookup = context.chunkLookup()
    var incr = 0

    while (lookup[id]){
      incr += 1
      id = base + ' ' + (incr + 1)
    }

    return id
  }

  // deprecated: use chunks.resolveAvailable
  node.resolveAvailableChunk = node.chunks.resolveAvailable

  node.destroy = function(){
    destroyAll(node)
    context.paramLookup.destroy()
  }

  // maps and lookup
  node.controllers.resolved = map(node.controllers, resolve)
  node.chunks.resolved = map(node.chunks, resolve)
  node.chunks.lookup = lookup(node.chunks, function(x){
    var descriptor = get(x)
    return descriptor && descriptor.id || undefined
  })

  // enforce controller types
  node.controllers.onUpdate(function (update) {
    update.slice(2).forEach(function (controller) {
      if (controller.port) {
        assignAvailablePort(controller)
      }
    })
  })

  context.chunkLookup = lookup(node.chunks, function(x){
    if (x){
      var data = x.resolved ? x.resolved() : x()
      return data && data.id || undefined
    }
  }, resolve, resolveInner)


  // extend param lookup
  var lookups = []
  if (context.paramLookup) {
    lookups.push(context.paramLookup)
  }

  lookups.push(
    lookup(node.chunks, function(x){
      if (x && x.onSchedule){
        return x.id()
      }
    }, resolve, resolveInner)
  )

  context.paramLookup = merge(lookups)
  node.context = context

  node.resolved = ObservStruct({
    selectedChunkId: node.selectedChunkId,
    controllers: node.controllers.resolved,
    chunks: node.chunks.resolved,
    paramCount: Observ(0)
  })

  context.paramLookup(refreshParamCount)

  node.grabInput = function(){
    var length = node.controllers.getLength()
    for (var i=0;i<length;i++){
      var controller = node.controllers.get(i)
      if (controller.grabInput){
        controller.grabInput()
      }
    }

    // now focus the selected chunk
    if (node.selectedChunkId){
      var chunkId = node.selectedChunkId()
      for (var i=0;i<length;i++){
        var controller = node.controllers.get(i)
        var chunkPositions = controller().chunkPositions || {}
        if (controller.grabInput && chunkPositions[chunkId]){
          controller.grabInput()
        }
      }
    }
  }

  node.updateChunkReferences = function (oldId, newId) {
    node.controllers.forEach(function (controller) {
      if (controller.chunkPositions && controller.chunkPositions()) {
        var value = controller.chunkPositions()[oldId]
        if (value && controller.chunkPositions.put) {
          controller.chunkPositions.delete(oldId)
          if (newId) {
            controller.chunkPositions.put(newId, value)
          }
        }
      }

      if (controller.chunkIds && controller.chunkIds()) {
        controller.chunkIds.set(controller.chunkIds().map(x => x === oldId ? newId : x))
      }
    })

    updateParamReferences(node.chunks, oldId, newId)

    // check for routes matching chunk id
    node.chunks.forEach(function (chunk) {
      updateRouteReferences(chunk, oldId, newId)
    })

    if (node.selectedChunkId() === oldId){
      node.selectedChunkId.set(newId)
    }
  }

  return node

  // scoped

  function refreshParamCount () {
    if (!refreshingParamCount) {
      refreshingParamCount = true
      process.nextTick(refreshParamCountNow)
    }
  }

  function refreshParamCountNow () {
    refreshingParamCount = false
    var count = Object.keys(context.paramLookup()).length
    if (count !== node.resolved.paramCount()) {
      node.resolved.paramCount.set(count)
    }
  }
}
Пример #23
0
function ModulatorChunk(parentContext){

  var context = Object.create(parentContext)

  var obs = ObservStruct({
    id: Observ(),
    slots: NodeArray(context),
    minimised: Property(false),
    shape: Property([1,1]),
    color: Property([0,0,0]),
    flags: Property([])
  })

  obs._type = 'ModulatorChunk'

  obs.context = context
  context.chunk = obs

  context.slotLookup = lookup(obs.slots, 'id')

  var broadcastSchedule = null
  obs.onSchedule = Event(function(b){
    broadcastSchedule = b
  })

  var currentTransform = null

  obs.slots.onUpdate(function(){

    if (currentTransform){
      currentTransform.destroy()
      currentTransform = null
    }

    var transforms = [0]

    obs.slots.forEach(function(slot){
      if (slot.onSchedule){
        transforms.push({param: slot, transform: operation})
      }
    })

    currentTransform = Transform(context, transforms)
    currentTransform.onSchedule(broadcastSchedule)

  })

  obs.triggers = computed([obs.id, obs.shape], function(id, shape){
    var length = shape[0] * shape[1]
    var result = []
    for (var i=0;i<length;i++){
      result.push(String(i))
    }
    return result
  })

  obs.grid = computed([obs.triggers, obs.shape], ArrayGrid)

  obs.triggerOn = function(id, at){
    var slot = context.slotLookup.get(id)
    if (slot){
      slot.triggerOn(at)
    }
  }

  obs.triggerOff = function(id, at){
    var slot = context.slotLookup.get(id)
    if (slot){
      slot.triggerOff(at)
    }
  }

  obs.destroy = function(){
    if (currentTransform){
      currentTransform.destroy()
      currentTransform = null
    }
  }

  obs.resolvedGrid = computed([obs.triggers, obs.shape], function(triggers, shape){
    return ArrayGrid(triggers.map(getGlobalId), shape)
  })

  return obs

  // scoped

  function operation(base, value){
    return (parseFloat(base)||0) + (parseFloat(value)||0)
  }

  function getGlobalId(id){
    if (id){
      return obs.id() + '/' + id
    }
  }

}
Пример #24
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
          }
        }
      })
    }
  }
}