UNPKG

soundbank-chunk

Version:

A group of triggerable sound descriptors to be positioned on a loop-grid and played with soundbank.

154 lines (126 loc) 3.92 kB
var Observ = require('observ') var ObservStuct = require('observ-struct') var ObservVarhash = require('observ-varhash') var ArrayGrid = require('array-grid') var computedNextTick = require('./lib/computed-next-tick') var computed = require('observ/computed') var getGlobalIdFallback = require('./lib/get-global-id.js') var obtainWithIds = require('./lib/obtain-with-ids.js') var randomColor = require('./lib/random-color.js') module.exports = RangeChunk function RangeChunk(opts){ // opts: soundbank (required), getGlobalId var obs = ObservStuct({ id: Observ(), title: Observ(), scale: Observ(), offset: Observ(), triggerSlots: Observ(), slots: Observ([]), shape: Observ(), stride: Observ(), triggers: Observ([]), inputs: Observ([]), outputs: Observ([]), routes: ObservVarhash({}), flags: ObservVarhash({}), selectedSlotId: Observ(), volume: Observ(1), color: Observ() }) var releases = [] var getGlobalId = opts.getGlobalId || getGlobalIdFallback var resolvedIds = computed([obs.id, obs.shape], function(id, shape){ var result = [] shape = shape || [1, 1] var length = (shape[0] || 1) * (shape[1] || 1) for (var i=0;i<length;i++){ result.push(lookupGlobal(String(i))) } return result }) var resolvedSlots = computedNextTick([ obs.id, obs.volume, obs.slots, obs.routes, obs.scale, obs.offset, obs.triggerSlots, obs.shape ], function(id, volume, slots, routes, scale, offset, triggerSlots, shape){ if (!routes) routes = {} if (!Array.isArray(slots)) slots = [] if (!Array.isArray(triggerSlots)) triggerSlots = [] shape = shape || [1, 1] offset = offset || 0 var result = [] for (var i=0;i<slots.length;i++){ var slot = slots[i] var instance = result[i] = obtainWithIds(slot, lookupGlobal) if (routes[slot.id]){ instance.output = routes[slot.id] } if (slot.id === 'output' && volume != null){ instance.volume = orOne(slot.volume) * orOne(volume) } } var length = (shape[0] || 1) * (shape[1] || 1) for (var i=0;i<length;i++){ var slot = obtainWithIds(triggerSlots[0], lookupGlobal) slot.id = lookupGlobal(String(i)) slot.output = routes[String(i)] || lookupGlobal('output') slot.offset = i + offset result.push(slot) } return result }) var usedSlots = {} releases.push(resolvedSlots(function(slots){ if (obs.id()){ slots.forEach(function(descriptor){ // naive and slow implementation, but soundbank performs deepEqual checks on update // hoepfully this will make things OK :/ usedSlots[descriptor.id] = true opts.soundbank.update(descriptor) }) // clean up unused slots for (var id in usedSlots){ if (usedSlots[id] === false){ ;delete usedSlots[id] opts.soundbank.remove(id) } else { usedSlots[id] = false } } } })) obs.grid = computed([resolvedIds, obs.shape, obs.stride], ArrayGrid) obs.controllerContext = computedNextTick([obs.id, obs.grid, obs.flags, obs.color, obs.selectedSlotId], function(id, grid, flags, color, selectedSlotId){ return { id: id, grid: grid, flags: flags, selectedSlotId: selectedSlotId, color: color || randomColor([255,255,255]) } }) obs.forceUpdate = function(){ resolvedSlots.refresh() } obs.destroy = function(){ releases.forEach(invoke) Object.keys(usedSlots).forEach(function(id){ opts.soundbank.remove(id) ;delete usedSlots[id] }) } return obs // scoped function lookupGlobal(localId){ return getGlobalId(obs.id(), localId) } } function orOne(number){ if (typeof number === 'number' && isFinite(number)){ return number } else { return 1 } } function invoke(f){ return f() }