├── .github └── workflows │ └── test.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── LICENSE ├── README.md ├── config ├── eslint │ ├── config.json │ ├── src.json │ └── test.json ├── karma │ └── config-unit.js ├── lint-staged │ └── config.json ├── prettier │ └── config.json ├── rollup │ └── bundle.mjs ├── tslint │ └── src.json └── webpack │ ├── worker-es2019.js │ └── worker-es5.mjs ├── package-lock.json ├── package.json ├── src ├── module.ts ├── tsconfig.json └── worker │ ├── 3rdpartylicenses.txt │ └── worker.ts └── test └── unit └── module.js /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | 15 | strategy: 16 | matrix: 17 | node-version: [20.x] 18 | target: [chrome, firefox, safari] 19 | type: [unit] 20 | max-parallel: 3 21 | 22 | steps: 23 | - name: Checkout repository 24 | uses: actions/checkout@v4 25 | 26 | - name: Install Node.js ${{ matrix.node-version }} 27 | uses: actions/setup-node@v4 28 | with: 29 | node-version: ${{ matrix.node-version }} 30 | 31 | - name: Cache node modules 32 | uses: actions/cache@v4 33 | with: 34 | path: ~/.npm 35 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 36 | restore-keys: | 37 | ${{ runner.os }}-node- 38 | 39 | - name: Install dependencies 40 | run: npm ci 41 | 42 | - env: 43 | BROWSER_STACK_ACCESS_KEY: ${{ secrets.BROWSER_STACK_ACCESS_KEY }} 44 | BROWSER_STACK_USERNAME: ${{ secrets.BROWSER_STACK_USERNAME }} 45 | TARGET: ${{ matrix.target }} 46 | TYPE: ${{ matrix.type }} 47 | name: Run ${{ matrix.type }} tests 48 | run: npm test 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | node_modules/ 3 | /build/ 4 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | commitlint --edit --extends @commitlint/config-angular 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | lint-staged --config config/lint-staged/config.json && npm run lint 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Christoph Guttandin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # extendable-media-recorder-wav-encoder 2 | 3 | **A Wave file encoder for the extendable-media-recorder package.** 4 | 5 | [![version](https://img.shields.io/npm/v/extendable-media-recorder-wav-encoder.svg?style=flat-square)](https://www.npmjs.com/package/extendable-media-recorder-wav-encoder) 6 | -------------------------------------------------------------------------------- /config/eslint/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true 4 | }, 5 | "extends": "eslint-config-holy-grail", 6 | "rules": { 7 | "no-sync": "off", 8 | "node/no-missing-require": "off" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /config/eslint/src.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true 4 | }, 5 | "extends": "eslint-config-holy-grail" 6 | } 7 | -------------------------------------------------------------------------------- /config/eslint/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "mocha": true 5 | }, 6 | "extends": "eslint-config-holy-grail", 7 | "globals": { 8 | "expect": "readonly" 9 | }, 10 | "rules": { 11 | "no-unused-expressions": "off", 12 | "node/file-extension-in-import": "off", 13 | "node/no-missing-require": "off" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /config/karma/config-unit.js: -------------------------------------------------------------------------------- 1 | const { env } = require('process'); 2 | const { DefinePlugin } = require('webpack'); 3 | 4 | module.exports = (config) => { 5 | config.set({ 6 | basePath: '../../', 7 | 8 | browserDisconnectTimeout: 100000, 9 | 10 | browserNoActivityTimeout: 100000, 11 | 12 | client: { 13 | mocha: { 14 | bail: true, 15 | timeout: 20000 16 | } 17 | }, 18 | 19 | concurrency: 1, 20 | 21 | files: [ 22 | { 23 | included: false, 24 | pattern: 'src/**', 25 | served: false, 26 | watched: true 27 | }, 28 | 'test/unit/**/*.js' 29 | ], 30 | 31 | frameworks: ['mocha', 'sinon-chai'], 32 | 33 | preprocessors: { 34 | 'test/unit/**/*.js': 'webpack' 35 | }, 36 | 37 | reporters: ['dots'], 38 | 39 | webpack: { 40 | mode: 'development', 41 | module: { 42 | rules: [ 43 | { 44 | test: /\.ts?$/, 45 | use: { 46 | loader: 'ts-loader', 47 | options: { 48 | compilerOptions: { 49 | declaration: false, 50 | declarationMap: false 51 | } 52 | } 53 | } 54 | } 55 | ] 56 | }, 57 | plugins: [ 58 | new DefinePlugin({ 59 | 'process.env': { 60 | CI: JSON.stringify(env.CI) 61 | } 62 | }) 63 | ], 64 | resolve: { 65 | extensions: ['.js', '.ts'], 66 | fallback: { util: false } 67 | } 68 | }, 69 | 70 | webpackMiddleware: { 71 | noInfo: true 72 | } 73 | }); 74 | 75 | if (env.CI) { 76 | config.set({ 77 | browserStack: { 78 | accessKey: env.BROWSER_STACK_ACCESS_KEY, 79 | build: `${env.GITHUB_RUN_ID}/unit-${env.TARGET}`, 80 | forceLocal: true, 81 | localIdentifier: `${Math.floor(Math.random() * 1000000)}`, 82 | project: env.GITHUB_REPOSITORY, 83 | username: env.BROWSER_STACK_USERNAME, 84 | video: false 85 | }, 86 | 87 | browsers: 88 | env.TARGET === 'chrome' 89 | ? ['ChromeBrowserStack'] 90 | : env.TARGET === 'firefox' 91 | ? ['FirefoxBrowserStack'] 92 | : env.TARGET === 'safari' 93 | ? ['SafariBrowserStack'] 94 | : ['ChromeBrowserStack', 'FirefoxBrowserStack', 'SafariBrowserStack'], 95 | 96 | captureTimeout: 300000, 97 | 98 | customLaunchers: { 99 | ChromeBrowserStack: { 100 | base: 'BrowserStack', 101 | browser: 'chrome', 102 | captureTimeout: 300, 103 | os: 'OS X', 104 | os_version: 'Big Sur' // eslint-disable-line camelcase 105 | }, 106 | FirefoxBrowserStack: { 107 | base: 'BrowserStack', 108 | browser: 'firefox', 109 | captureTimeout: 300, 110 | os: 'Windows', 111 | os_version: '10' // eslint-disable-line camelcase 112 | }, 113 | SafariBrowserStack: { 114 | base: 'BrowserStack', 115 | browser: 'safari', 116 | captureTimeout: 300, 117 | os: 'OS X', 118 | os_version: 'Big Sur' // eslint-disable-line camelcase 119 | } 120 | } 121 | }); 122 | } else { 123 | config.set({ 124 | browsers: ['ChromeCanaryHeadless', 'ChromeHeadless', 'FirefoxDeveloperHeadless', 'FirefoxHeadless', 'Safari'] 125 | }); 126 | } 127 | }; 128 | -------------------------------------------------------------------------------- /config/lint-staged/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "*": "prettier --config config/prettier/config.json --ignore-unknown --write" 3 | } 4 | -------------------------------------------------------------------------------- /config/prettier/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "printWidth": 140, 4 | "quoteProps": "consistent", 5 | "singleQuote": true, 6 | "tabWidth": 4, 7 | "trailingComma": "none" 8 | } 9 | -------------------------------------------------------------------------------- /config/rollup/bundle.mjs: -------------------------------------------------------------------------------- 1 | import { join, resolve as resolvePath } from 'path'; 2 | import { readFile, readFileSync, readlink, stat } from 'fs'; 3 | import babel from '@rollup/plugin-babel'; 4 | import { fs } from 'memfs'; 5 | import replace from '@rollup/plugin-replace'; 6 | import webpack from 'webpack'; 7 | // eslint-disable-next-line node/file-extension-in-import 8 | import webpackConfig from '../webpack/worker-es5.mjs'; 9 | 10 | const workerFile = readFileSync('src/worker/worker.ts', 'utf8'); 11 | const result = /export\sconst\sworker\s=\s`(?.*)`;/g.exec(workerFile); 12 | 13 | if (result === null) { 14 | throw new Error('The worker file could not be parsed.'); 15 | } 16 | 17 | const virtualPath = resolvePath(import.meta.dirname, '../../src/worker.js'); 18 | const workerString = result.groups.workerString; 19 | 20 | // eslint-disable-next-line import/no-default-export 21 | export default new Promise((resolve, reject) => { 22 | const compiler = webpack(webpackConfig); 23 | 24 | compiler.inputFileSystem = { 25 | readFile(path, ...args) { 26 | if (path === virtualPath) { 27 | args.pop()(null, "import 'extendable-media-recorder-wav-encoder-worker';"); 28 | 29 | return; 30 | } 31 | 32 | return readFile(path, ...args); 33 | }, 34 | readlink(path, callback) { 35 | if (path === virtualPath) { 36 | return readlink(import.meta.filename, callback); 37 | } 38 | 39 | return readlink(path, callback); 40 | }, 41 | stat(path, ...args) { 42 | if (path === virtualPath) { 43 | args.pop()(null, { 44 | isFile() { 45 | return true; 46 | } 47 | }); 48 | 49 | return; 50 | } 51 | 52 | return stat(path, ...args); 53 | } 54 | }; 55 | compiler.outputFileSystem = { ...fs, join }; 56 | compiler.run((err, stats) => { 57 | if (err !== null) { 58 | reject(err); 59 | } else if (stats.hasErrors() || stats.hasWarnings()) { 60 | reject(new Error(stats.toString({ errorDetails: true, warnings: true }))); 61 | } else { 62 | const transpiledWorkerString = fs // eslint-disable-line node/no-sync 63 | .readFileSync('/worker.js', 'utf8') 64 | .replace(/\\/g, '\\\\') 65 | .replace(/`/g, '\\`') 66 | .replace(/\${/g, '\\${'); 67 | 68 | resolve({ 69 | input: 'build/es2019/module.js', 70 | output: { 71 | file: 'build/es5/bundle.js', 72 | format: 'umd', 73 | name: 'extendableMediaRecorderWavEncoder' 74 | }, 75 | plugins: [ 76 | replace({ 77 | delimiters: ['`', '`'], 78 | include: 'build/es2019/worker/worker.js', 79 | values: { 80 | // V8 does only accept substrings with a maximum length of 32767 characters. Otherwise it throws a SyntaxError. 81 | [workerString.slice(0, 32767)]: `\`${transpiledWorkerString}\``, 82 | [workerString.slice(32767)]: '' 83 | } 84 | }), 85 | babel({ 86 | babelHelpers: 'runtime', 87 | exclude: 'node_modules/**', 88 | plugins: ['@babel/plugin-external-helpers', '@babel/plugin-transform-runtime'], 89 | presets: [ 90 | [ 91 | '@babel/preset-env', 92 | { 93 | modules: false 94 | } 95 | ] 96 | ] 97 | }) 98 | ] 99 | }); 100 | } 101 | }); 102 | }); 103 | -------------------------------------------------------------------------------- /config/tslint/src.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint-config-holy-grail" 3 | } 4 | -------------------------------------------------------------------------------- /config/webpack/worker-es2019.js: -------------------------------------------------------------------------------- 1 | const { resolve } = require('path'); 2 | const TerserPlugin = require('terser-webpack-plugin'); 3 | const { RawSource } = require('webpack-sources'); 4 | 5 | module.exports = { 6 | entry: { 7 | worker: './node_modules/extendable-media-recorder-wav-encoder-worker/build/es2019/module.js' 8 | }, 9 | mode: 'production', 10 | module: { 11 | rules: [ 12 | { 13 | test: /\.js$/, 14 | use: { 15 | loader: 'babel-loader', 16 | options: { 17 | plugins: ['@babel/plugin-external-helpers', '@babel/plugin-transform-runtime'], 18 | presets: [ 19 | [ 20 | '@babel/preset-env', 21 | { 22 | include: ['transform-template-literals'], 23 | targets: { 24 | browsers: [ 25 | 'last 2 Chrome major versions', 26 | 'last 2 ChromeAndroid major versions', 27 | 'last 2 Edge major versions', 28 | 'last 2 Firefox major versions', 29 | 'last 2 FirefoxAndroid major versions', 30 | 'last 2 iOS major versions', 31 | 'last 2 Opera major versions', 32 | 'last 2 Safari major versions' 33 | ] 34 | } 35 | } 36 | ] 37 | ] 38 | } 39 | } 40 | } 41 | ] 42 | }, 43 | optimization: { 44 | minimizer: [ 45 | new TerserPlugin({ 46 | extractComments: { 47 | banner: false, 48 | condition: /^\**!|@preserve|@license|@cc_on/, 49 | filename: '3rdpartylicenses.txt' 50 | }, 51 | test: /\.ts$/ 52 | }) 53 | ] 54 | }, 55 | output: { 56 | filename: '[name].ts', 57 | path: resolve('src/worker/') 58 | }, 59 | plugins: [ 60 | { 61 | apply(compiler) { 62 | compiler.hooks.compilation.tap('WrapperPlugin', (compilation) => { 63 | compilation.hooks.processAssets.tap( 64 | { 65 | name: 'WrapperPlugin', 66 | stage: 700 67 | }, 68 | () => { 69 | for (const chunk of compilation.chunks) { 70 | for (const file of chunk.files) { 71 | compilation.updateAsset(file, (asset) => { 72 | const workerString = asset.source().replace(/\\/g, '\\\\').replace(/\${/g, '\\${'); 73 | 74 | return new RawSource(`// This is the minified and stringified code of the extendable-media-recorder-wav-encoder-worker package. 75 | export const worker = \`${workerString}\`; // tslint:disable-line:max-line-length 76 | `); 77 | }); 78 | } 79 | } 80 | } 81 | ); 82 | }); 83 | } 84 | } 85 | ], 86 | target: 'webworker' 87 | }; 88 | -------------------------------------------------------------------------------- /config/webpack/worker-es5.mjs: -------------------------------------------------------------------------------- 1 | import TerserPlugin from 'terser-webpack-plugin'; 2 | 3 | // eslint-disable-next-line import/no-default-export 4 | export default { 5 | entry: { 6 | worker: './src/worker.js' 7 | }, 8 | mode: 'production', 9 | module: { 10 | rules: [ 11 | { 12 | exclude: /node_modules\/(?!dashify)/, 13 | test: /\.js$/, 14 | use: { 15 | loader: 'babel-loader', 16 | options: { 17 | plugins: ['@babel/plugin-external-helpers', '@babel/plugin-transform-runtime'], 18 | presets: ['@babel/preset-env'] 19 | } 20 | } 21 | } 22 | ] 23 | }, 24 | optimization: { 25 | minimizer: [ 26 | new TerserPlugin({ 27 | extractComments: { 28 | banner: false, 29 | condition: /^\**!|@preserve|@license|@cc_on/, 30 | filename: '3rdpartylicenses.txt' 31 | } 32 | }) 33 | ] 34 | }, 35 | output: { 36 | filename: '[name].js', 37 | path: '/' 38 | }, 39 | resolve: { 40 | mainFields: ['browser', 'main'] 41 | }, 42 | target: 'webworker' 43 | }; 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Christoph Guttandin", 3 | "bugs": { 4 | "url": "https://github.com/chrisguttandin/extendable-media-recorder-wav-encoder/issues" 5 | }, 6 | "config": { 7 | "commitizen": { 8 | "path": "cz-conventional-changelog" 9 | } 10 | }, 11 | "dependencies": { 12 | "@babel/runtime": "^7.27.6", 13 | "extendable-media-recorder-wav-encoder-broker": "^7.0.119", 14 | "extendable-media-recorder-wav-encoder-worker": "^8.0.116", 15 | "tslib": "^2.8.1" 16 | }, 17 | "description": "A Wave file encoder for the extendable-media-recorder package.", 18 | "devDependencies": { 19 | "@babel/core": "^7.27.4", 20 | "@babel/plugin-external-helpers": "^7.27.1", 21 | "@babel/plugin-transform-runtime": "^7.27.4", 22 | "@babel/preset-env": "^7.27.2", 23 | "@commitlint/cli": "^19.8.1", 24 | "@commitlint/config-angular": "^19.8.1", 25 | "@rollup/plugin-babel": "^6.0.4", 26 | "@rollup/plugin-replace": "^6.0.2", 27 | "babel-loader": "^10.0.0", 28 | "chai": "^4.3.10", 29 | "commitizen": "^4.3.1", 30 | "cz-conventional-changelog": "^3.3.0", 31 | "eslint": "^8.57.0", 32 | "eslint-config-holy-grail": "^60.0.35", 33 | "husky": "^9.1.7", 34 | "karma": "^6.4.4", 35 | "karma-browserstack-launcher": "^1.6.0", 36 | "karma-chrome-launcher": "^3.2.0", 37 | "karma-firefox-launcher": "^2.1.3", 38 | "karma-mocha": "^2.0.1", 39 | "karma-sinon-chai": "^2.0.2", 40 | "karma-webkit-launcher": "^2.6.0", 41 | "karma-webpack": "^5.0.1", 42 | "lint-staged": "^16.1.1", 43 | "memfs": "^4.17.2", 44 | "mocha": "^11.6.0", 45 | "prettier": "^3.5.3", 46 | "rimraf": "^6.0.1", 47 | "rollup": "^4.43.0", 48 | "sinon": "^17.0.2", 49 | "sinon-chai": "^3.7.0", 50 | "terser-webpack-plugin": "^5.3.14", 51 | "ts-loader": "^9.5.2", 52 | "tsconfig-holy-grail": "^15.0.2", 53 | "tslint": "^6.1.3", 54 | "tslint-config-holy-grail": "^56.0.6", 55 | "typescript": "^5.8.3", 56 | "webpack": "^5.99.9", 57 | "webpack-cli": "^6.0.1" 58 | }, 59 | "files": [ 60 | "build/es2019/", 61 | "build/es5/", 62 | "src/" 63 | ], 64 | "homepage": "https://github.com/chrisguttandin/extendable-media-recorder-wav-encoder", 65 | "license": "MIT", 66 | "main": "build/es5/bundle.js", 67 | "module": "build/es2019/module.js", 68 | "name": "extendable-media-recorder-wav-encoder", 69 | "repository": { 70 | "type": "git", 71 | "url": "https://github.com/chrisguttandin/extendable-media-recorder-wav-encoder.git" 72 | }, 73 | "scripts": { 74 | "build": "rimraf build/* && webpack --config config/webpack/worker-es2019.js && tsc --project src/tsconfig.json && rollup --config config/rollup/bundle.mjs", 75 | "lint": "npm run lint:config && npm run lint:src && npm run lint:test", 76 | "lint:config": "eslint --config config/eslint/config.json --ext .cjs --ext .js --ext .mjs --report-unused-disable-directives config/", 77 | "lint:src": "tslint --config config/tslint/src.json --project src/tsconfig.json src/*.ts src/**/*.ts", 78 | "lint:test": "eslint --config config/eslint/test.json --ext .js --report-unused-disable-directives test/", 79 | "prepare": "husky", 80 | "prepublishOnly": "npm run build", 81 | "test": "npm run lint && npm run build && npm run test:unit", 82 | "test:unit": "if [ \"$TYPE\" = \"\" -o \"$TYPE\" = \"unit\" ]; then karma start config/karma/config-unit.js --single-run; fi" 83 | }, 84 | "types": "build/es2019/module.d.ts", 85 | "version": "7.0.129" 86 | } 87 | -------------------------------------------------------------------------------- /src/module.ts: -------------------------------------------------------------------------------- 1 | import { load } from 'extendable-media-recorder-wav-encoder-broker'; 2 | import { worker } from './worker/worker'; 3 | 4 | const blob: Blob = new Blob([worker], { type: 'application/javascript; charset=utf-8' }); 5 | 6 | const url: string = URL.createObjectURL(blob); 7 | 8 | const extendableMediaRecorderWavEncoder = load(url); 9 | 10 | export const characterize = extendableMediaRecorderWavEncoder.characterize; 11 | 12 | export const connect = extendableMediaRecorderWavEncoder.connect; 13 | 14 | export const disconnect = extendableMediaRecorderWavEncoder.disconnect; 15 | 16 | export const encode = extendableMediaRecorderWavEncoder.encode; 17 | 18 | export const isSupported = extendableMediaRecorderWavEncoder.isSupported; 19 | 20 | export const record = extendableMediaRecorderWavEncoder.record; 21 | 22 | URL.revokeObjectURL(url); 23 | -------------------------------------------------------------------------------- /src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "isolatedModules": true 4 | }, 5 | "extends": "tsconfig-holy-grail/src/tsconfig-browser" 6 | } 7 | -------------------------------------------------------------------------------- /src/worker/3rdpartylicenses.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | * dashify 3 | * 4 | * Copyright (c) 2015-2017, Jon Schlinkert. 5 | * Released under the MIT License. 6 | */ 7 | -------------------------------------------------------------------------------- /src/worker/worker.ts: -------------------------------------------------------------------------------- 1 | // This is the minified and stringified code of the extendable-media-recorder-wav-encoder-worker package. 2 | export const worker = `(()=>{var e={455:function(e,t){!function(e){"use strict";var t=function(e){return function(t){var r=e(t);return t.add(r),r}},r=function(e){return function(t,r){return e.set(t,r),r}},n=void 0===Number.MAX_SAFE_INTEGER?9007199254740991:Number.MAX_SAFE_INTEGER,s=536870912,a=2*s,o=function(e,t){return function(r){var o=t.get(r),i=void 0===o?r.size:on)throw new Error("Congratulations, you created a collection of unique numbers which uses all available integers!");for(;r.has(i);)i=Math.floor(Math.random()*n);return e(r,i)}},i=new WeakMap,c=r(i),l=o(c,i),u=t(l);e.addUniqueNumber=u,e.generateUniqueNumber=l}(t)}},t={};function r(n){var s=t[n];if(void 0!==s)return s.exports;var a=t[n]={exports:{}};return e[n].call(a.exports,a,a.exports,r),a.exports}(()=>{"use strict";const e=-32603,t=-32602,n=-32601,s=(e,t)=>Object.assign(new Error(e),{status:t}),a=t=>s('The handler of the method called "'.concat(t,'" returned an unexpected result.'),e),o=(t,r)=>async({data:{id:o,method:i,params:c}})=>{const l=r[i];try{if(void 0===l)throw(e=>s('The requested method called "'.concat(e,'" is not supported.'),n))(i);const r=void 0===c?l():l(c);if(void 0===r)throw(t=>s('The handler of the method called "'.concat(t,'" returned no required result.'),e))(i);const u=r instanceof Promise?await r:r;if(null===o){if(void 0!==u.result)throw a(i)}else{if(void 0===u.result)throw a(i);const{result:e,transferables:r=[]}=u;t.postMessage({id:o,result:e},r)}}catch(e){const{message:r,status:n=-32603}=e;t.postMessage({error:{code:n,message:r},id:o})}};var i=r(455);const c=new Map,l=(e,r,n)=>({...r,connect:({port:t})=>{t.start();const n=e(t,r),s=(0,i.generateUniqueNumber)(c);return c.set(s,(()=>{n(),t.close(),c.delete(s)})),{result:s}},disconnect:({portId:e})=>{const r=c.get(e);if(void 0===r)throw(e=>s('The specified parameter called "portId" with the given value "'.concat(e,'" does not identify a port connected to this worker.'),t))(e);return r(),{result:null}},isSupported:async()=>{if(await new Promise((e=>{const t=new ArrayBuffer(0),{port1:r,port2:n}=new MessageChannel;r.onmessage=({data:t})=>e(null!==t),n.postMessage(t,[t])}))){const e=n();return{result:e instanceof Promise?await e:e}}return{result:!1}}}),u=(e,t,r=()=>!0)=>{const n=l(u,t,r),s=o(e,n);return e.addEventListener("message",s),()=>e.removeEventListener("message",s)},d=e=>e.reduce(((e,t)=>e+t.length),0),h=(e,t)=>{const r=[];let n=0;e:for(;nt){const s=n-t;r.forEach(((t,r)=>{const n=t.pop(),a=n.length-s;t.push(n.subarray(0,a)),e[r].unshift(n.subarray(a))}))}return r},f=new Map,m=(e=>(t,r,n)=>{const s=e.get(t);if(void 0===s){const s={channelDataArrays:n.map((e=>[e])),isComplete:!0,sampleRate:r};return e.set(t,s),s}return s.channelDataArrays.forEach(((e,t)=>e.push(n[t]))),s})(f),p=((e,t)=>(r,n,s,a)=>{const o=s>>3,i="subsequent"===n?0:44,c=r.length,l=e(r[0]),u=new ArrayBuffer(l*c*o+i),d=new DataView(u);return"subsequent"!==n&&t(d,s,c,"complete"===n?l:Number.POSITIVE_INFINITY,a),r.forEach(((e,t)=>{let r=i+t*o;e.forEach((e=>{const t=e.length;for(let n=0;n{const a=t>>3,o=Math.min(n*r*a,4294967251);e.setUint32(0,1380533830),e.setUint32(4,o+36,!0),e.setUint32(8,1463899717),e.setUint32(12,1718449184),e.setUint32(16,16,!0),e.setUint16(20,1,!0),e.setUint16(22,r,!0),e.setUint32(24,s,!0),e.setUint32(28,s*r*a,!0),e.setUint16(32,r*a,!0),e.setUint16(34,t,!0),e.setUint32(36,1684108385),e.setUint32(40,o,!0)})),v=new Map;u(self,{characterize:()=>({result:/^audio\\/wav$/}),encode:({recordingId:e,timeslice:t})=>{const r=v.get(e);void 0!==r&&(v.delete(e),r.reject(new Error("Another request was made to initiate an encoding.")));const n=f.get(e);if(null!==t){if(void 0===n||d(n.channelDataArrays[0])*(1e3/n.sampleRate){v.set(e,{reject:n,resolve:r,timeslice:t})}));const r=h(n.channelDataArrays,Math.ceil(t*(n.sampleRate/1e3))),s=p(r,n.isComplete?"initial":"subsequent",16,n.sampleRate);return n.isComplete=!1,{result:s,transferables:s}}if(void 0!==n){const t=p(n.channelDataArrays,n.isComplete?"complete":"subsequent",16,n.sampleRate);return f.delete(e),{result:t,transferables:t}}return{result:[],transferables:[]}},record:({recordingId:e,sampleRate:t,typedArrays:r})=>{const n=m(e,t,r),s=v.get(e);if(void 0!==s&&d(n.channelDataArrays[0])*(1e3/t)>=s.timeslice){const r=h(n.channelDataArrays,Math.ceil(s.timeslice*(t/1e3))),a=p(r,n.isComplete?"initial":"subsequent",16,t);n.isComplete=!1,v.delete(e),s.resolve({result:a,transferables:a})}return{result:null}}})})()})();`; // tslint:disable-line:max-line-length 3 | -------------------------------------------------------------------------------- /test/unit/module.js: -------------------------------------------------------------------------------- 1 | import { connect, disconnect, isSupported } from '../../src/module'; 2 | 3 | describe('module', () => { 4 | describe('characterize()', () => { 5 | // @todo 6 | }); 7 | 8 | describe('connect()', () => { 9 | it('should connect a port', () => { 10 | return connect().then((port) => { 11 | expect(port).to.be.an.instanceOf(MessagePort); 12 | }); 13 | }); 14 | }); 15 | 16 | describe('disconnect()', () => { 17 | let port; 18 | 19 | beforeEach(() => connect().then((prt) => (port = prt))); 20 | 21 | it('should disconnect a port', () => { 22 | return disconnect(port); 23 | }); 24 | }); 25 | 26 | describe('encode()', () => { 27 | // @todo 28 | }); 29 | 30 | describe('isSupported()', () => { 31 | it('should check the browser support', () => { 32 | return isSupported(); 33 | }); 34 | }); 35 | 36 | describe('record()', () => { 37 | // @todo 38 | }); 39 | }); 40 | --------------------------------------------------------------------------------