Visualizer, yaaay!

This commit is contained in:
MindfulMinun committed 2020-05-28 01:45:36 -05:00
1 parent 09afa41a44
commit bcc2741017
5 files changed
+117 -266

No files matched your search

+1 -135
View File
@@ -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)})}]);
+1 -100
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+89
View File
@@ -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
}