Visualizer, yaaay!
This commit is contained in:
5 files changed
+117
-266
No files matched your search
Vendored
+1
-135
@@ -1,135 +1 @@
|
||||
/******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(installedModules[moduleId]) {
|
||||
/******/ return installedModules[moduleId].exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = installedModules[moduleId] = {
|
||||
/******/ i: moduleId,
|
||||
/******/ l: false,
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.l = true;
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/
|
||||
/******/ // expose the modules object (__webpack_modules__)
|
||||
/******/ __webpack_require__.m = modules;
|
||||
/******/
|
||||
/******/ // expose the module cache
|
||||
/******/ __webpack_require__.c = installedModules;
|
||||
/******/
|
||||
/******/ // define getter function for harmony exports
|
||||
/******/ __webpack_require__.d = function(exports, name, getter) {
|
||||
/******/ if(!__webpack_require__.o(exports, name)) {
|
||||
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // define __esModule on exports
|
||||
/******/ __webpack_require__.r = function(exports) {
|
||||
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
|
||||
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
||||
/******/ }
|
||||
/******/ Object.defineProperty(exports, '__esModule', { value: true });
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // create a fake namespace object
|
||||
/******/ // mode & 1: value is a module id, require it
|
||||
/******/ // mode & 2: merge all properties of value into the ns
|
||||
/******/ // mode & 4: return value when already ns object
|
||||
/******/ // mode & 8|1: behave like require
|
||||
/******/ __webpack_require__.t = function(value, mode) {
|
||||
/******/ if(mode & 1) value = __webpack_require__(value);
|
||||
/******/ if(mode & 8) return value;
|
||||
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
|
||||
/******/ var ns = Object.create(null);
|
||||
/******/ __webpack_require__.r(ns);
|
||||
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
|
||||
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
|
||||
/******/ return ns;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||
/******/ __webpack_require__.n = function(module) {
|
||||
/******/ var getter = module && module.__esModule ?
|
||||
/******/ function getDefault() { return module['default']; } :
|
||||
/******/ function getModuleExports() { return module; };
|
||||
/******/ __webpack_require__.d(getter, 'a', getter);
|
||||
/******/ return getter;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Object.prototype.hasOwnProperty.call
|
||||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
||||
/******/
|
||||
/******/ // __webpack_public_path__
|
||||
/******/ __webpack_require__.p = "";
|
||||
/******/
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 0);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ({
|
||||
|
||||
/***/ "./src/main.js":
|
||||
/*!*********************!*\
|
||||
!*** ./src/main.js ***!
|
||||
\*********************/
|
||||
/*! no exports provided */
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _setup_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./setup.js */ \"./src/setup.js\");\n\n\ndocument.getElementById('choose').addEventListener('click', () => {\n document.getElementById('file').click()\n})\n\ndocument.querySelector('input').addEventListener('input', function () {\n //@ts-ignore\n fileGuard(this.files)\n})\n\nwindow.addEventListener('drop', async ev => {\n ev.preventDefault()\n fileGuard(ev.dataTransfer.files)\n})\n\n// Prevent Chrome from replacing the document when a file's dropped over it\ndocument.addEventListener('dragover', ev => {\n ev.preventDefault()\n})\n\n\n/**\n * @param {FileList} files\n */\nfunction fileGuard(files) {\n const candidates = [...files]\n const file =\n candidates.find(file => file.type.startsWith('audio/')) ||\n candidates.find(file => file.type.startsWith('video/'))\n if (!file) {\n console.log(candidates.map(f => f.type))\n alert(\"Be sure to drop an audio file your browser supports :)\")\n return\n }\n\n init(file)\n}\n\nconst select = document.querySelector('select')\nselect.addEventListener('input', () => {\n Object(_setup_js__WEBPACK_IMPORTED_MODULE_0__[\"useProcessor\"])(select.value)\n})\n\n/**\n * @param {File} audioFile\n */\nasync function init(audioFile) {\n const audioEl = document.querySelector('audio')\n audioEl.src = URL.createObjectURL(audioFile)\n Object(_setup_js__WEBPACK_IMPORTED_MODULE_0__[\"useProcessor\"])(select.value)\n\n _setup_js__WEBPACK_IMPORTED_MODULE_0__[\"audio\"].resume().then(() => audioEl.play())\n\n document.title = `D♭ • ${audioFile.name}`\n\n if (navigator.mediaSession && typeof MediaMetadata !== 'undefined') {\n navigator.mediaSession.metadata = new MediaMetadata({\n title: audioFile.name,\n album: \"D♭\"\n });\n navigator.mediaSession.setActionHandler('play', () => {\n audioEl.play()\n })\n navigator.mediaSession.setActionHandler('pause', () => {\n audioEl.pause()\n })\n navigator.mediaSession.setActionHandler('seekbackward', () => {\n audioEl.currentTime -= 5\n })\n navigator.mediaSession.setActionHandler('seekforward', () => {\n audioEl.currentTime += 5\n })\n }\n}\n\n\n//# sourceURL=webpack:///./src/main.js?");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./src/processors.worklet.js":
|
||||
/*!***********************************!*\
|
||||
!*** ./src/processors.worklet.js ***!
|
||||
\***********************************/
|
||||
/*! no static exports found */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
eval("module.exports = __webpack_require__.p + \"processors.worklet.js\";\n\n//# sourceURL=webpack:///./src/processors.worklet.js?");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./src/setup.js":
|
||||
/*!**********************!*\
|
||||
!*** ./src/setup.js ***!
|
||||
\**********************/
|
||||
/*! exports provided: useProcessor, audio */
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"useProcessor\", function() { return useProcessor; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"audio\", function() { return audio; });\n/* harmony import */ var _processors_worklet_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./processors.worklet.js */ \"./src/processors.worklet.js\");\n/* harmony import */ var _processors_worklet_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_processors_worklet_js__WEBPACK_IMPORTED_MODULE_0__);\n//@ts-ignore\n\n\nconst audioEl = document.querySelector('audio')\nconst audio = new AudioContext()\nconst audioSrcNode = audio.createMediaElementSource(audioEl)\n\n// Add the modules\naudio.audioWorklet.addModule('/dist/' + _processors_worklet_js__WEBPACK_IMPORTED_MODULE_0___default.a)\n\nlet dFlat = null\n// let dFlat = null new AudioWorkletNode(audio, 'subtract-overlap', {\n// processorOptions: {\n// maxChannels: audio.destination.maxChannelCount\n// }\n// })\n\n// audioSrcNode.connect(dFlat)\n// dFlat.connect(audio.destination)\n\n/**\n * Swaps out the processor\n * @param {string} name The name of the processor to use\n */\nfunction useProcessor(name) {\n if (dFlat) {\n dFlat.disconnect()\n audioSrcNode.disconnect()\n }\n\n dFlat = new AudioWorkletNode(audio, name, {\n processorOptions: {\n maxChannels: audio.destination.maxChannelCount\n }\n })\n audioSrcNode.connect(dFlat)\n dFlat.connect(audio.destination)\n}\n\n\n\n\n// const audioEl = document.querySelector('audio')\n// const audio = new AudioContext()\n// await audio.audioWorklet.addModule('/dist/' + workletUrl)\n\n// const dFlat = new AudioWorkletNode(audio, 'subtract-overlap', {\n// // const dFlat = new AudioWorkletNode(audio, 'bit-crusher', {\n// processorOptions: {\n// maxChannels: audio.destination.maxChannelCount\n// }\n// })\n// const audioSrcNode = audio.createMediaElementSource(audioEl)\n\n// audioSrcNode.connect(dFlat)\n// dFlat.connect(audio.destination)\n// audioEl.src = URL.createObjectURL(audioFile)\n\n\n//# sourceURL=webpack:///./src/setup.js?");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 0:
|
||||
/*!***************************!*\
|
||||
!*** multi ./src/main.js ***!
|
||||
\***************************/
|
||||
/*! no static exports found */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
eval("module.exports = __webpack_require__(/*! ./src/main.js */\"./src/main.js\");\n\n\n//# sourceURL=webpack:///multi_./src/main.js?");
|
||||
|
||||
/***/ })
|
||||
|
||||
/******/ });
|
||||
!function(e){var t={};function n(i){if(t[i])return t[i].exports;var o=t[i]={i:i,l:!1,exports:{}};return e[i].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(i,o,function(t){return e[t]}.bind(null,o));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=1)}([function(e,t,n){e.exports=n.p+"processors.worklet.js"},function(e,t,n){e.exports=n(2)},function(e,t,n){"use strict";n.r(t);var i=n(0),o=n.n(i);const r=document.querySelector("audio"),a=new AudioContext,c=function(e,t){const n=e.createAnalyser(),i=new Uint8Array(n.frequencyBinCount),o=document.querySelector("canvas"),r=o.getContext("2d"),a=document.getElementById("visualizerSelect");let c=0;return a.addEventListener("input",()=>{c=+a.value}),requestAnimationFrame((function e(){if(!t.paused)switch(o.width=window.innerWidth*window.devicePixelRatio,o.height=window.innerHeight*window.devicePixelRatio,c){case 0:!function(){r.clearRect(0,0,o.width,o.height),r.fillStyle="rgba(255, 255, 255, .72)",r.strokeStyle="rgba(255, 255, 255, .72)",r.lineWidth=2*window.devicePixelRatio,n.getByteTimeDomainData(i);const e=o.width/n.frequencyBinCount;let t=0,a=0,c=0;for(;a<n.frequencyBinCount;a++)c=i[a]/128*(o.height/2),r.lineTo(t,c),t+=e;r.stroke()}();break;case 1:!function(){r.clearRect(0,0,o.width,o.height),r.fillStyle="rgba(255, 255, 255, .72)",r.strokeStyle="rgba(255, 255, 255, .72)",r.lineWidth=2*window.devicePixelRatio,n.getByteFrequencyData(i);const e=o.width/n.frequencyBinCount;let t=0,a=n.frequencyBinCount,c=0;for(;0<a;a--)c=o.height-i[a]/128*(o.height/2),r.lineTo(t,c),t+=e;r.stroke()}()}requestAnimationFrame(e)})),n}(a,r),d=a.createMediaElementSource(r);let u;function s(e){u&&(u.disconnect(),d.disconnect(),c.disconnect()),u=new AudioWorkletNode(a,e,{processorOptions:{maxChannels:a.destination.maxChannelCount}}),d.connect(u),u.connect(c),c.connect(a.destination)}function l(e){const t=[...e],n=t.find(e=>e.type.startsWith("audio/"))||t.find(e=>e.type.startsWith("video/"));if(!n)return console.log(t.map(e=>e.type)),void alert("Be sure to drop an audio file your browser supports :)");!async function(e){const t=document.querySelector("audio");t.src=URL.createObjectURL(e),s(f.value),a.resume().then(()=>t.play()),document.title="D♭ • "+e.name,navigator.mediaSession&&"undefined"!=typeof MediaMetadata&&(navigator.mediaSession.metadata=new MediaMetadata({title:e.name,album:"D♭"}),navigator.mediaSession.setActionHandler("play",()=>{t.play()}),navigator.mediaSession.setActionHandler("pause",()=>{t.pause()}),navigator.mediaSession.setActionHandler("seekbackward",()=>{t.currentTime-=5}),navigator.mediaSession.setActionHandler("seekforward",()=>{t.currentTime+=5}))}(n)}a.audioWorklet.addModule("/dist/"+o.a),document.getElementById("choose").addEventListener("click",()=>{document.getElementById("file").click()}),document.querySelector("input").addEventListener("input",(function(){l(this.files)})),window.addEventListener("drop",async e=>{e.preventDefault(),l(e.dataTransfer.files)}),document.addEventListener("dragover",e=>{e.preventDefault()});const f=document.querySelector("select");f.addEventListener("input",()=>{s(f.value)})}]);
|
||||
Vendored
+1
-100
@@ -1,100 +1 @@
|
||||
/******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(installedModules[moduleId]) {
|
||||
/******/ return installedModules[moduleId].exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = installedModules[moduleId] = {
|
||||
/******/ i: moduleId,
|
||||
/******/ l: false,
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.l = true;
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/
|
||||
/******/ // expose the modules object (__webpack_modules__)
|
||||
/******/ __webpack_require__.m = modules;
|
||||
/******/
|
||||
/******/ // expose the module cache
|
||||
/******/ __webpack_require__.c = installedModules;
|
||||
/******/
|
||||
/******/ // define getter function for harmony exports
|
||||
/******/ __webpack_require__.d = function(exports, name, getter) {
|
||||
/******/ if(!__webpack_require__.o(exports, name)) {
|
||||
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // define __esModule on exports
|
||||
/******/ __webpack_require__.r = function(exports) {
|
||||
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
|
||||
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
||||
/******/ }
|
||||
/******/ Object.defineProperty(exports, '__esModule', { value: true });
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // create a fake namespace object
|
||||
/******/ // mode & 1: value is a module id, require it
|
||||
/******/ // mode & 2: merge all properties of value into the ns
|
||||
/******/ // mode & 4: return value when already ns object
|
||||
/******/ // mode & 8|1: behave like require
|
||||
/******/ __webpack_require__.t = function(value, mode) {
|
||||
/******/ if(mode & 1) value = __webpack_require__(value);
|
||||
/******/ if(mode & 8) return value;
|
||||
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
|
||||
/******/ var ns = Object.create(null);
|
||||
/******/ __webpack_require__.r(ns);
|
||||
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
|
||||
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
|
||||
/******/ return ns;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||
/******/ __webpack_require__.n = function(module) {
|
||||
/******/ var getter = module && module.__esModule ?
|
||||
/******/ function getDefault() { return module['default']; } :
|
||||
/******/ function getModuleExports() { return module; };
|
||||
/******/ __webpack_require__.d(getter, 'a', getter);
|
||||
/******/ return getter;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Object.prototype.hasOwnProperty.call
|
||||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
||||
/******/
|
||||
/******/ // __webpack_public_path__
|
||||
/******/ __webpack_require__.p = "";
|
||||
/******/
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = "./src/processors.worklet.js");
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ({
|
||||
|
||||
/***/ "./src/processors.worklet.js":
|
||||
/*!***********************************!*\
|
||||
!*** ./src/processors.worklet.js ***!
|
||||
\***********************************/
|
||||
/*! no static exports found */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
eval("/// <reference path=\"./types.d.ts\" />\n\nconst carry = Symbol()\n\nclass NoopProcessor extends AudioWorkletProcessor {\n /**\n * @param {Float32Array[][]} inputs\n * @param {Float32Array[][]} outputs\n */\n process(inputs, outputs) {\n const input = inputs[0]\n const output = outputs[0]\n\n for (let channel = 0; channel < input.length; channel++) {\n output[channel].set(input[channel])\n }\n return true\n }\n}\nregisterProcessor('noop', NoopProcessor)\n\nclass Mono extends AudioWorkletProcessor {\n /**\n * @param {Float32Array[][]} inputs\n * @param {Float32Array[][]} outputs\n */\n process(inputs, outputs) {\n // This processor has only one input and one output.\n const input = inputs[0]\n const output = outputs[0]\n\n for (let channel = 0; channel < input.length; channel++) {\n for (let sample = 0; sample < input[0].length; sample++) {\n // Mono: (L + R) / 2\n // More generally: 1/n * (\\sum_{i = 0}^n C_i)\n output[0][sample] += input[channel][sample] * (1 / input.length)\n }\n if (channel !== 0) {\n output[channel].set(output[0])\n }\n }\n\n return true\n }\n}\nregisterProcessor('mono', Mono)\n\nclass DerivativeProcessor extends AudioWorkletProcessor {\n constructor(options) {\n super()\n this[carry] = new Float32Array(options.processorOptions.maxChannels)\n }\n \n /**\n * @param {Float32Array[][]} inputs\n * @param {Float32Array[][]} outputs\n */\n process(inputs, outputs) {\n const input = inputs[0]\n const output = outputs[0]\n\n for (let channel = 0; channel < input.length; channel++) {\n output[channel][0] = this[carry][channel]\n for (let sample = 1; sample < input[channel].length; sample++) {\n // dy/dx: (s2 - s1) / time\n output[channel][sample] = input[channel][sample] - input[channel][sample - 1]\n }\n this[carry][channel] = input[channel][input.length - 1]\n }\n\n return true\n }\n}\nregisterProcessor('derivative', DerivativeProcessor)\n\nclass IntegralProcessor extends AudioWorkletProcessor {\n\n constructor(options) {\n super()\n this[carry] = new Float32Array(options.processorOptions.maxChannels)\n }\n \n /**\n * @param {Float32Array[][]} inputs\n * @param {Float32Array[][]} outputs\n */\n process(inputs, outputs) {\n const input = inputs[0]\n const output = outputs[0]\n\n for (let channel = 0; channel < input.length; channel++) {\n output[channel][0] = (this[carry][channel] + input[channel][0]) / 2\n for (let sample = 1; sample < input[channel].length; sample++) {\n // Integral of two samples: (time * (s1 + s2)) / 2\n output[channel][sample] = (input[channel][sample - 1] + input[channel][sample]) / 2\n }\n this[carry][channel] = input[channel][input.length - 1]\n }\n\n return true\n }\n}\nregisterProcessor('integral', IntegralProcessor)\n\n\nclass BitCrusher extends AudioWorkletProcessor {\n /**\n * @param {Float32Array[][]} inputs\n * @param {Float32Array[][]} outputs\n */\n process(inputs, outputs) {\n const input = inputs[0]\n const output = outputs[0]\n\n let phase = 1\n const step = 2 * Math.pow(.5, 8)\n\n for (let channel = 0; channel < input.length; channel++) {\n for (let sample = 0; sample < input[channel].length; sample++) {\n output[channel][sample] = input[channel][sample]\n }\n }\n\n return true\n }\n}\nregisterProcessor('bit-crusher', BitCrusher)\n\n\n/**\n * Inverts the amplitude of every even-numbered channel and merges them to mono.\n * Quick-and-dirty way of removing vocals\n */\nclass SubtractOverlap extends AudioWorkletProcessor {\n /**\n * @param {Float32Array[][]} inputs\n * @param {Float32Array[][]} outputs\n */\n process(inputs, outputs) {\n // This processor has only one input and one output.\n const input = inputs[0]\n const output = outputs[0]\n\n for (let channel = 0; channel < input.length; channel++) {\n for (let sample = 0; sample < input[channel].length; sample++) {\n output[0][sample] += (channel % 2 === 0 ? 1 : -1) * input[channel][sample]\n }\n if (channel !== 0) {\n output[channel].set(output[0])\n }\n }\n\n return true\n }\n}\nregisterProcessor('subtract-overlap', SubtractOverlap)\n\n/**\n * Like `subtract-overlap`, but isolates the overlap instead of removing it.\n * Quick-and-dirty way to isolate vocals\n */\nclass IsolateOverlap extends AudioWorkletProcessor {\n /**\n
Line truncated
|
||||
|
||||
/***/ })
|
||||
|
||||
/******/ });
|
||||
!function(e){var r={};function t(o){if(r[o])return r[o].exports;var s=r[o]={i:o,l:!1,exports:{}};return e[o].call(s.exports,s,s.exports,t),s.l=!0,s.exports}t.m=e,t.c=r,t.d=function(e,r,o){t.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:o})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,r){if(1&r&&(e=t(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(t.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var s in e)t.d(o,s,function(r){return e[r]}.bind(null,s));return o},t.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(r,"a",r),r},t.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},t.p="",t(t.s=0)}([function(e,r){const t=Symbol();class o extends AudioWorkletProcessor{process(e,r){const t=e[0],o=r[0];for(let e=0;e<t.length;e++)o[e].set(t[e]);return!0}}registerProcessor("noop",o);class s extends AudioWorkletProcessor{process(e,r){const t=e[0],o=r[0];for(let e=0;e<t.length;e++){for(let r=0;r<t[0].length;r++)o[0][r]+=t[e][r]*(1/t.length);0!==e&&o[e].set(o[0])}return!0}}registerProcessor("mono",s);class n extends AudioWorkletProcessor{constructor(e){super(),this[t]=new Float32Array(e.processorOptions.maxChannels)}process(e,r){const o=e[0],s=r[0];for(let e=0;e<o.length;e++){s[e][0]=this[t][e];for(let r=1;r<o[e].length;r++)s[e][r]=o[e][r]-o[e][r-1];this[t][e]=o[e][o.length-1]}return!0}}registerProcessor("derivative",n);class l extends AudioWorkletProcessor{constructor(e){super(),this[t]=new Float32Array(e.processorOptions.maxChannels)}process(e,r){const o=e[0],s=r[0];for(let e=0;e<o.length;e++){s[e][0]=(this[t][e]+o[e][0])/2;for(let r=1;r<o[e].length;r++)s[e][r]=(o[e][r-1]+o[e][r])/2;this[t][e]=o[e][o.length-1]}return!0}}registerProcessor("integral",l);class c extends AudioWorkletProcessor{process(e,r){const t=e[0],o=r[0];Math.pow(.5,8);for(let e=0;e<t.length;e++)for(let r=0;r<t[e].length;r++)o[e][r]=t[e][r];return!0}}registerProcessor("bit-crusher",c);class u extends AudioWorkletProcessor{process(e,r){const t=e[0],o=r[0];for(let e=0;e<t.length;e++){for(let r=0;r<t[e].length;r++)o[0][r]+=(e%2==0?1:-1)*t[e][r];0!==e&&o[e].set(o[0])}return!0}}registerProcessor("subtract-overlap",u);class i extends AudioWorkletProcessor{process(e,r){const t=e[0],o=r[0];for(let e=0;e<t.length;e++)for(let r=0;r<t[e].length;r++)o[0][r]+=(e%2==0?1:-1)*t[e][r];for(let e=t.length-1;0<=e;e--)for(let r=0;r<t[e].length;r++)o[e][r]=t[e][r]-o[0][r];return!0}}registerProcessor("isolate-overlap",i)}]);
|
||||
+17
-1
@@ -22,7 +22,6 @@
|
||||
line-height: 1.15;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
background-color: #1e1e1e;
|
||||
color: white;
|
||||
}
|
||||
body { display: flex; }
|
||||
@@ -74,11 +73,20 @@
|
||||
border-color: #448aff;
|
||||
}
|
||||
|
||||
canvas {
|
||||
position: fixed;
|
||||
top: 0; left: 0; bottom: 0; right: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 840px;
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
z-index: 1;
|
||||
}
|
||||
.credits {
|
||||
text-align: center;
|
||||
@@ -102,6 +110,7 @@
|
||||
});"></script> -->
|
||||
</head>
|
||||
<body>
|
||||
<canvas></canvas>
|
||||
<div class="container">
|
||||
<p>
|
||||
Drag and drop an audio file anywhere or <button id="choose">choose one from your filesystem.</button>
|
||||
@@ -118,6 +127,13 @@
|
||||
<option value="mono">mono</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Visualizer:
|
||||
<select id="visualizerSelect">
|
||||
<option value="0" selected>Oscilloscope</option>
|
||||
<option value="1">Linear spectrogram</option>
|
||||
</select>
|
||||
</label>
|
||||
</p>
|
||||
<audio controls></audio>
|
||||
|
||||
|
||||
+9
-30
@@ -1,22 +1,16 @@
|
||||
//@ts-ignore
|
||||
import workletUrl from './processors.worklet.js'
|
||||
import processorUrl from './processors.worklet.js'
|
||||
import visFactory from './visualizer.js'
|
||||
|
||||
const audioEl = document.querySelector('audio')
|
||||
const audio = new AudioContext()
|
||||
const visualizer = visFactory(audio, audioEl)
|
||||
const audioSrcNode = audio.createMediaElementSource(audioEl)
|
||||
|
||||
let dFlat;
|
||||
|
||||
// Add the modules
|
||||
audio.audioWorklet.addModule('/dist/' + workletUrl)
|
||||
|
||||
let dFlat = null
|
||||
// let dFlat = null new AudioWorkletNode(audio, 'subtract-overlap', {
|
||||
// processorOptions: {
|
||||
// maxChannels: audio.destination.maxChannelCount
|
||||
// }
|
||||
// })
|
||||
|
||||
// audioSrcNode.connect(dFlat)
|
||||
// dFlat.connect(audio.destination)
|
||||
audio.audioWorklet.addModule('/dist/' + processorUrl)
|
||||
|
||||
/**
|
||||
* Swaps out the processor
|
||||
@@ -26,6 +20,7 @@ export function useProcessor(name) {
|
||||
if (dFlat) {
|
||||
dFlat.disconnect()
|
||||
audioSrcNode.disconnect()
|
||||
visualizer.disconnect()
|
||||
}
|
||||
|
||||
dFlat = new AudioWorkletNode(audio, name, {
|
||||
@@ -34,24 +29,8 @@ export function useProcessor(name) {
|
||||
}
|
||||
})
|
||||
audioSrcNode.connect(dFlat)
|
||||
dFlat.connect(audio.destination)
|
||||
dFlat.connect(visualizer)
|
||||
visualizer.connect(audio.destination)
|
||||
}
|
||||
|
||||
export { audio }
|
||||
|
||||
|
||||
// const audioEl = document.querySelector('audio')
|
||||
// const audio = new AudioContext()
|
||||
// await audio.audioWorklet.addModule('/dist/' + workletUrl)
|
||||
|
||||
// const dFlat = new AudioWorkletNode(audio, 'subtract-overlap', {
|
||||
// // const dFlat = new AudioWorkletNode(audio, 'bit-crusher', {
|
||||
// processorOptions: {
|
||||
// maxChannels: audio.destination.maxChannelCount
|
||||
// }
|
||||
// })
|
||||
// const audioSrcNode = audio.createMediaElementSource(audioEl)
|
||||
|
||||
// audioSrcNode.connect(dFlat)
|
||||
// dFlat.connect(audio.destination)
|
||||
// audioEl.src = URL.createObjectURL(audioFile)
|
||||
@@ -0,0 +1,89 @@
|
||||
/// <reference path="./types.d.ts" />
|
||||
|
||||
/**
|
||||
* @param {AudioContext} audioContext
|
||||
* @param {HTMLAudioElement} audioElement
|
||||
*/
|
||||
export default function visFactory(audioContext, audioElement) {
|
||||
const analyzer = audioContext.createAnalyser()
|
||||
const samples = new Uint8Array(analyzer.frequencyBinCount)
|
||||
|
||||
const canvas = document.querySelector('canvas')
|
||||
const ctx = canvas.getContext('2d')
|
||||
|
||||
/** @type {HTMLSelectElement} */
|
||||
// @ts-ignore
|
||||
const dropdown = document.getElementById('visualizerSelect')
|
||||
|
||||
let mode = 0
|
||||
|
||||
dropdown.addEventListener('input', () => {
|
||||
mode = +dropdown.value
|
||||
})
|
||||
|
||||
// Animation loop
|
||||
requestAnimationFrame(function rAF() {
|
||||
if (!audioElement.paused) {
|
||||
canvas.width = window.innerWidth * window.devicePixelRatio
|
||||
canvas.height = window.innerHeight * window.devicePixelRatio
|
||||
|
||||
switch (mode) {
|
||||
case 0: drawTime()
|
||||
break
|
||||
case 1: drawFreq()
|
||||
break
|
||||
}
|
||||
}
|
||||
requestAnimationFrame(rAF)
|
||||
})
|
||||
|
||||
function drawTime() {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
||||
|
||||
ctx.fillStyle = 'rgba(255, 255, 255, .72)'
|
||||
ctx.strokeStyle = 'rgba(255, 255, 255, .72)'
|
||||
ctx.lineWidth = 2 * window.devicePixelRatio
|
||||
|
||||
analyzer.getByteTimeDomainData(samples)
|
||||
|
||||
const increment = canvas.width / analyzer.frequencyBinCount
|
||||
|
||||
let x = 0,
|
||||
i = 0,
|
||||
y = 0;
|
||||
|
||||
// ctx.beginPath()
|
||||
for (; i < analyzer.frequencyBinCount; i++) {
|
||||
y = (samples[i] / 128) * (canvas.height / 2)
|
||||
ctx.lineTo(x, y)
|
||||
x += increment
|
||||
}
|
||||
ctx.stroke()
|
||||
}
|
||||
|
||||
function drawFreq() {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
||||
|
||||
ctx.fillStyle = 'rgba(255, 255, 255, .72)'
|
||||
ctx.strokeStyle = 'rgba(255, 255, 255, .72)'
|
||||
ctx.lineWidth = 2 * window.devicePixelRatio
|
||||
|
||||
analyzer.getByteFrequencyData(samples)
|
||||
|
||||
const increment = canvas.width / analyzer.frequencyBinCount
|
||||
|
||||
let x = 0,
|
||||
i = analyzer.frequencyBinCount,
|
||||
y = 0;
|
||||
|
||||
// ctx.beginPath()
|
||||
for (; 0 < i; i--) {
|
||||
y = canvas.height - (samples[i] / 128) * (canvas.height / 2)
|
||||
ctx.lineTo(x, y)
|
||||
x += increment
|
||||
}
|
||||
ctx.stroke()
|
||||
}
|
||||
|
||||
return analyzer
|
||||
}
|
||||
Reference in new issue
Block a user