UNPKG

@ahanapediatrics/react-volume-meter

Version:
293 lines (279 loc) 11.3 kB
module.exports = /******/ (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/index.ts"); /******/ }) /************************************************************************/ /******/ ({ /***/ "./node_modules/react/index.js": /*!*************************************!*\ !*** ./node_modules/react/index.js ***! \*************************************/ /*! no static exports found */ /***/ (function(module, exports) { throw new Error("Module build failed: Error: Final loader (./node_modules/awesome-typescript-loader/dist/entry.js) didn't return a Buffer or String\n at runLoaders (/Users/danrumney/WebstormProjects/react-volume-meter/node_modules/webpack/lib/NormalModule.js:318:18)\n at /Users/danrumney/WebstormProjects/react-volume-meter/node_modules/loader-runner/lib/LoaderRunner.js:373:3\n at iterateNormalLoaders (/Users/danrumney/WebstormProjects/react-volume-meter/node_modules/loader-runner/lib/LoaderRunner.js:214:10)\n at iterateNormalLoaders (/Users/danrumney/WebstormProjects/react-volume-meter/node_modules/loader-runner/lib/LoaderRunner.js:221:10)\n at /Users/danrumney/WebstormProjects/react-volume-meter/node_modules/loader-runner/lib/LoaderRunner.js:236:3\n at context.callback (/Users/danrumney/WebstormProjects/react-volume-meter/node_modules/loader-runner/lib/LoaderRunner.js:111:13)\n at process._tickCallback (internal/process/next_tick.js:68:7)"); /***/ }), /***/ "./src/components/VolumeMeter.tsx": /*!****************************************!*\ !*** ./src/components/VolumeMeter.tsx ***! \****************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; result["default"] = mod; return result; }; Object.defineProperty(exports, "__esModule", { value: true }); const react_1 = __importStar(__webpack_require__(/*! react */ "./node_modules/react/index.js")); var VmShape; (function (VmShape) { VmShape[VmShape["VM_STEPPED"] = 0] = "VM_STEPPED"; VmShape[VmShape["VM_FLAT"] = 1] = "VM_FLAT"; })(VmShape = exports.VmShape || (exports.VmShape = {})); class MeterRenderer { constructor(ctx, { height, width, shape = VmShape.VM_STEPPED, blocks = 5, }) { this.canvasCtx = ctx; this.height = height; this.width = width; this.shape = shape; this.blocks = blocks; this.barWidth = width / (blocks + 1); this.blockMaxima = Array(blocks).fill(0).map((_, i) => (i + 1) / blocks); } start() { this.prevVolume = 0; } stop() { this.prevVolume = 0; this.draw(0); } clear() { const { canvasCtx, width, height, } = this; canvasCtx.clearRect(0, 0, width, height); } draw(volume) { const { prevVolume, canvasCtx, barWidth, height, shape, blocks, } = this; const vol = Math.max(volume, prevVolume * 0.95); this.prevVolume = vol; this.clear(); const volPerBlock = 1 / blocks; this.blockMaxima.forEach((blockMax, i) => { let color = 'grey'; if (vol > 0.8 && blockMax >= 1) { color = 'red'; } else if (vol > blockMax) { color = 'green'; } canvasCtx.fillStyle = color; const x = barWidth * (blocks + 1) / blocks * i; const y = height - (shape === VmShape.VM_STEPPED ? height * (i + 1) * (1 / (blocks + 1)) : height); canvasCtx.fillRect(x, y, barWidth, height - y); if (vol < 1 && blockMax < vol) { canvasCtx.fillStyle = 'green'; canvasCtx.fillRect(x, y, // ((v % B) / B) is a sawtooth with period B and amplitude 1 ((vol % (volPerBlock)) / (volPerBlock)) * (barWidth), height - y); } }); } } class VolumeMeter extends react_1.Component { constructor() { super(...arguments); this.canvas = react_1.default.createRef(); this.getCanvasContext = () => { const canvas = this.canvas.current; if (!canvas) { throw new Error('Cannot get a reference to the canvas'); } const canvasCtx = canvas.getContext('2d'); if (!canvasCtx) { throw new Error('2D context not avaiable'); } return canvasCtx; }; this.changedSource = (oldSrc, newSrc) => { if (!oldSrc && !newSrc) { return false; } if (!oldSrc) { return true; } if (!newSrc) { return true; } return oldSrc.mediaStream.id !== newSrc.mediaStream.id; }; this.start = () => { this.drawer.start(); const drawLoop = () => { const { enabled } = this.props; if (!enabled) { return; } const volume = this.getVolume(); this.drawer.draw(volume); this.rafId = window.requestAnimationFrame(drawLoop); }; drawLoop(); }; this.stop = () => { this.drawer.stop(); window.cancelAnimationFrame(this.rafId); }; this.setupAnalyzer = () => { const { audioContext, src, enabled } = this.props; if (!src) { return; } this.analyser = audioContext.createAnalyser(); src.connect(this.analyser); this.array = new Uint8Array(this.analyser.frequencyBinCount); if (enabled) { this.start(); } }; this.getVolume = () => { this.analyser.getByteTimeDomainData(this.array); return this.array.reduce((max, vol) => Math.max(max, Math.abs(vol - 128))) / 128; }; } componentDidMount() { const { width, height, shape, blocks = 5, audioContext, src, } = this.props; const canvasCtx = this.getCanvasContext(); this.drawer = new MeterRenderer(canvasCtx, { width, height, shape, blocks, }); this.drawer.stop(); if (audioContext && src) { this.setupAnalyzer(); } } componentDidUpdate(prevProps) { const { src, enabled } = this.props; if (this.changedSource(prevProps.src, src)) { this.stop(); this.setupAnalyzer(); } if (!enabled && prevProps.enabled) { this.stop(); } if (this.analyser && enabled && !prevProps.enabled) { this.start(); } } componentWillUnmount() { this.stop(); } render() { const { width, height, style } = this.props; return (react_1.default.createElement("canvas", { className: "volume-meter", ref: this.canvas, width: width, height: height, style: style })); } } VolumeMeter.defaultProps = { enabled: true, blocks: 5, }; exports.VolumeMeter = VolumeMeter; /***/ }), /***/ "./src/index.ts": /*!**********************!*\ !*** ./src/index.ts ***! \**********************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; function __export(m) { for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; } Object.defineProperty(exports, "__esModule", { value: true }); __export(__webpack_require__(/*! ./components/VolumeMeter */ "./src/components/VolumeMeter.tsx")); /***/ }) /******/ }); //# sourceMappingURL=index.ts.js.map