This repository has been archived by the owner on Apr 29, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.9db1c55b.js.map
1 lines (1 loc) · 25.2 KB
/
main.9db1c55b.js.map
1
{"version":3,"sources":["virtual-audio.js","main.js"],"names":["defer","f","setTimeout","AudioContext","window","webkitAudioContext","VirtualAudioGraph","context","opts","$context","$nodes","vPrev","autostart","resume","vGraph","vCurr","prepare","diff","removed","forEach","patch","type","_destroyNode","key","_removeProperty","data","_disconnect","created","_createNode","_setProperty","_connect","updated","suspend","properties","$node","createAnalyser","createBufferSource","destination","createBiquadFilter","createChannelMerger","createChannelSplitter","createConstantSource","createConvolver","maxDelayTime","find","label","createDelay","value","createDynamicsCompressor","createGain","feedforward","feedback","createIIRFilter","mediaElement","createMediaElementSource","document","querySelector","createMediaStreamDestination","createOscillator","createPanner","createStereoPanner","createWaveShaper","console","warn","prop","start","stop","disconnect","linearRampToValueAtTime","currentTime","method","target","time","default","a","b","param","connect","flatten","graph","nodes","depth","node","i","connections","base","length","oldNodes","newNodes","Object","patches","newNode","oldNode","push","connection","split","j","Math","max","oldProp","newProp","oldConnection","newConnection","values","audio","addEventListener","state","App","Elm","Main","init","ports","updateAudio","subscribe","update"],"mappings":"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","file":"main.9db1c55b.js","sourceRoot":"src","sourcesContent":["const defer = f => setTimeout(f, 0)\nconst AudioContext = window.AudioContext || window.webkitAudioContext\n\nexport default class VirtualAudioGraph {\n // Static Methods ============================================================\n //\n static prepare(graph = []) {\n // The first step in preparing the graph is to key each virtual node.\n // This is how we perform a diff between graphs and calculate what has\n // changed each update.\n const key = (graph, base = '') => {\n graph.forEach((node, i) => {\n // RefNodes always have a key, and they also\n // cannot have connections or properties\n // so we can just return early and move on.\n if (node.type === 'RefNode') return\n // Assign the node a key if it didn't already have one.\n // This is how we track changes to the graph in a slightly\n // more organised way\n if (!node.key) node.key = `${base}_${i}`\n\n // Recursively assign keys to this nodes connections.\n if (node.connections && node.connections.length > 0) {\n key(node.connections, node.key)\n }\n })\n\n return graph\n }\n\n // It is often most natural to represent the audio graph as a list\n // of trees, using RefNodes to \"jump\" between chains of node\n // connections. This isns't the easiest data structure to deal with\n // however, so the next step in preparation is the flatten the graph\n // into a single array.\n const flatten = (graph, nodes = {}, depth = 0) => {\n graph.forEach((node, i) => {\n // Don't push RefNodes to the flat graph.\n if (node.type !== 'RefNode') nodes[node.key] = node\n if (node.connections) flatten(node.connections, nodes, depth + 1)\n // If we're deeper than the root of the graph, replace\n // this node with a reference to itself by key.\n if (depth > 0) graph[i] = { type: 'RefNode', key: node.key }\n })\n\n return nodes\n }\n\n return flatten(key(graph))\n }\n\n //\n static diff(oldNodes, newNodes) {\n const patches = { created: [], updated: [], removed: [] }\n\n for (const newNode of Object.values(newNodes)) {\n const oldNode = oldNodes[newNode.key]\n\n // A node with newNode.key does not exist in the old graph, so this must\n // mean we've created a brand new node.\n if (!oldNode) {\n patches.created.push({ type: 'node', key: newNode.key, data: newNode })\n\n newNode.connections.forEach(connection => {\n patches.created.push({ type: 'connection', key: newNode.key, data: connection.key.split('.') })\n })\n\n // A node with the same key exists in both graphs, but the type has changed\n // (eg osc -> gain) so we need to recreate the node.\n } else if (oldNode.type !== newNode.type) {\n patches.updated.push({ type: 'node', key: newNode.key, data: newNode })\n\n newNode.connections.forEach(connection => {\n patches.created.push({ type: 'connection', key: newNode.key, data: connection.key.split('.') })\n })\n\n // A node with the same key exists in both graphs and the node hasn't\n // fundamentally changed, so now we check whether properties or connections\n // have changed.\n } else {\n // Checking properties...\n for (let j = 0; j < Math.max(oldNode.properties.length, newNode.properties.length); j++) {\n const oldProp = oldNode.properties[j]\n const newProp = newNode.properties[j]\n\n //\n if (!oldProp) {\n patches.created.push({ type: 'property', key: oldNode.key, data: newProp })\n } else if (!newProp) {\n patches.removed.push({ type: 'property', key: oldNode.key, data: oldProp })\n } else if (oldProp.label !== newProp.label) {\n patches.removed.push({ type: 'property', key: oldNode.key, data: oldProp })\n patches.created.push({ type: 'property', key: oldNode.key, data: newProp })\n } else if (oldProp.value !== newProp.value) {\n patches.updated.push({ type: 'property', key: oldNode.key, data: newProp })\n }\n }\n\n // Checking connections...\n for (let j = 0; j < Math.max(oldNode.connections.length, newNode.connections.length); j++) {\n const oldConnection = oldNode.connections[j]\n const newConnection = newNode.connections[j]\n\n //\n if (!oldConnection) {\n patches.created.push({ type: 'connection', key: oldNode.key, data: newConnection.key.split('.') })\n } else if (!newConnection) {\n patches.removed.push({ type: 'connection', key: oldNode.key, data: oldConnection.key.split('.') })\n } else if (oldConnection.key !== newConnection.key) {\n patches.removed.push({ type: 'connection', key: oldNode.key, data: oldConnection.key.split('.') })\n patches.created.push({ type: 'connection', key: oldNode.key, data: newConnection.key.split('.') })\n }\n }\n }\n\n delete oldNodes[newNode.key]\n }\n\n for (const oldNode of Object.values(oldNodes)) {\n patches.removed.push({ type: 'node', key: oldNode.key, data: oldNode })\n }\n\n return patches\n }\n\n // Constructor ===============================================================\n //\n constructor(context = new AudioContext(), opts = {}) {\n // Borrowing a convetion from virtual dom libraries, the $ sign //is used to\n // indicate \"real\" Web Audio bits, and the v- prefix is used to indicate\n // virtual elements.\n\n // $context is a reference to the `AudioContext` either passed in or created\n // on construction.\n this.$context = context\n // A reference to the real graph of audio nodes\n this.$nodes = {}\n // We keep track of the prebious graph so we can perform a diff and work out\n // what has changed between updates.\n this.vPrev = {}\n\n // In most modern browsers an Audio Context starts in a suspended state and\n // requires some user interaction before it can be resumed. Still, we can\n // attempt to resume the context ourselves in the developer passes in the\n // `autostart` option.\n if (opts.autostart) this.resume()\n }\n\n // Public Methods ============================================================\n //\n update(vGraph = []) {\n // The accompanying library of virtual node functions\n // encourages a nested tree-like approach to describing\n // audio graphs. This isn't the easiest structure to deal\n // with, however, so a preparation step serves to wrestle\n // the graph into a more suitable shape.\n const vCurr = VirtualAudioGraph.prepare(vGraph)\n\n // A diff tracks everything that has been removed, created,\n // and updated between updates. We perform this step so we\n // only touch the audio nodes that need to be changed in some\n // way.\n const diff = VirtualAudioGraph.diff(this.vPrev, vCurr)\n\n // Remove nodes and properties from the graph.\n diff.removed.forEach(patch => {\n switch (patch.type) {\n case 'node':\n this._destroyNode(patch.key)\n break\n case 'property':\n this._removeProperty(patch.key, patch.data)\n break\n case 'connection':\n this._disconnect(patch.key, patch.data)\n break\n }\n })\n\n // Create new nodes and add new properties to\n // the graph.\n diff.created.forEach(patch => {\n switch (patch.type) {\n case 'node':\n this._createNode(patch.key, patch.data)\n break\n case 'property':\n this._setProperty(patch.key, patch.data)\n break\n case 'connection':\n defer(() => this._connect(patch.key, patch.data))\n break\n }\n })\n\n // Update existing nodes and properties in the\n // graph.\n diff.updated.forEach(patch => {\n switch (patch.type) {\n case 'node':\n this._destroyNode(patch.key)\n this._createNode(patch.key, patch.data)\n break\n case 'property':\n this._setProperty(patch.key, patch.data)\n break\n case 'connection':\n // Connections can't be updated\n break\n }\n })\n\n // Store the current graph for next time.\n this.vPrev = vCurr\n }\n\n // A thin wrapper of the `AudioContext.suspend()` method. This\n // bassically exists so developers don't have to reach in and\n // touch the \"real\" audio context directly.\n suspend() {\n this.$context.suspend()\n }\n\n // A thin wrapper of the `AudioContext.resume()` method. This\n // bassically exists so developers don't have to reach in and\n // touch the \"real\" audio context directly.\n resume() {\n this.$context.resume()\n }\n\n // Private Methods ===========================================================\n //\n _createNode(key, { type, properties }) {\n let $node = null\n\n //\n switch (type) {\n case 'AnalyserNode':\n $node = this.$context.createAnalyser()\n break\n case 'AudioBufferSourceNode':\n $node = this.$context.createBufferSource()\n break\n case 'AudioDestinationNode':\n $node = this.$context.destination\n break\n case 'BiquadFilterNode':\n $node = this.$context.createBiquadFilter()\n break\n case 'ChannelMergerNode':\n $node = this.$context.createChannelMerger()\n break\n case 'ChannelSplitterNode':\n $node = this.$context.createChannelSplitter()\n break\n case 'ConstantSourceNode':\n $node = this.$context.createConstantSource()\n break\n case 'ConvolverNode':\n $node = this.$context.createConvolver()\n break\n case 'DelayNode':\n const maxDelayTime = properties.find(({ label }) => label === 'maxDelayTime')\n $node = this.$context.createDelay((maxDelayTime && maxDelayTime.value) || 1)\n break\n case 'DynamicsCompressorNode':\n $node = this.$context.createDynamicsCompressor()\n break\n case 'GainNode':\n $node = this.$context.createGain()\n break\n case 'IIRFilterNode':\n const feedforward = properties.find(({ label }) => label === 'feedforward')\n const feedback = properties.find(({ label }) => label === 'feedback')\n $node = this.$context.createIIRFilter(\n (feedforward && feedforward.value) || [0],\n (feedback && feedback.value) || [1]\n )\n break\n case 'MediaElementAudioSourceNode':\n const mediaElement = properties.find(({ label }) => label === 'mediaElement')\n $node = this.$context.createMediaElementSource(\n document.querySelector(mediaElement.value)\n )\n break\n case 'MediaStreamAudioDestinationNode':\n $node = this.$context.createMediaStreamDestination()\n break\n // TODO: How should I handle creating / grabbing the media stream?\n // case 'MediaStreamAudioSourceNode':\n // $node = this.$context.createMediaStreamSource(\n\n // )\n // break\n case 'OscillatorNode':\n $node = this.$context.createOscillator()\n break\n case 'PannerNode':\n $node = this.$context.createPanner()\n break\n case 'StereoPannerNode':\n $node = this.$context.createStereoPanner()\n break\n case 'WaveShaperNode':\n $node = this.$context.createWaveShaper()\n break\n //\n default:\n console.warn(`Invalide node type of: ${type}. Defaulting to GainNode to avoid crashing the AudioContext.`)\n $node = this.$context.createGain()\n }\n\n this.$nodes[key] = $node\n\n //\n properties.forEach(prop => this._setProperty(key, prop))\n\n // Certain nodes like oscillators must be started before they will produce\n // noise. We make the assumption that these nodes should always start\n // immediately after they have been created, so if a `start` method exists we\n // call it.\n if ($node.start) $node.start()\n }\n\n //\n _destroyNode(key) {\n const $node = this.$nodes[key]\n\n // Certain nodes like oscillators can be stopped. It probably doesn't make\n // much of a difference calling this method, but we do just in case!\n if ($node.stop) $node.stop()\n\n // Calling disconnect with no arguments will disconnect this node from\n // everything.\n $node.disconnect()\n\n // Finally remove the node from the graph and let the GC do its job.\n delete this.$nodes[key]\n }\n\n //\n _setProperty(key, { type, label, value }) {\n const $node = this.$nodes[key]\n\n switch (type) {\n case 'NodeProperty':\n $node[label] = value\n break\n case 'AudioParam':\n $node[label].linearRampToValueAtTime(value, this.$context.currentTime + 0.01)\n break\n case 'ScheduledUpdate':\n $node[label][value.method](value.target, value.time)\n break\n }\n }\n\n //\n _removeProperty(key, { type, label, value }) {\n const $node = this.$nodes[key]\n\n switch (type) {\n case 'NodeProperty':\n break\n case 'AudioParam':\n $node[label].value = $node[label].default\n break\n case 'ScheduledUpdate':\n // TODO: work out how to cancel scheduled updates\n break\n }\n }\n\n //\n _connect(a, [b, param = null]) {\n if (b) this.$nodes[a].connect(param ? this.$nodes[b][param] : this.$nodes[b])\n }\n\n //\n _disconnect(a, [b, param = null]) {\n if (b) this.$nodes[a].disconnect(param ? this.$nodes[b][param] : this.$nodes[b])\n }\n}","/* global AudioContext */\nimport { Elm } from './Main.elm'\nimport VirtualAudioGraph from './virtual-audio'\n\nconst context = new AudioContext()\nconst audio = new VirtualAudioGraph(context)\n\n// Chrome autplay policy demans some user interaction\n// takes place before the AudioContext can be resumed.\nwindow.addEventListener('click', () => {\n\tif (context.state === 'suspended') context.resume()\n})\n\nconst App = Elm.Main.init({\n\tnode: document.querySelector('#app')\n})\n\nApp.ports.updateAudio.subscribe(graph => {\n\taudio.update(graph)\n})"]}