├── .eslintrc.json ├── .github └── workflows │ └── test.js.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── audio-decode.d.ts ├── audio-decode.js ├── package-lock.json ├── package.json ├── readme.md ├── tea.yaml ├── test-case.js ├── test.html └── test.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "node": true, 5 | "commonjs": true, 6 | "es6": true 7 | }, 8 | "extends": "eslint:recommended", 9 | "rules": { 10 | "strict": 2, 11 | "indent": 0, 12 | "linebreak-style": 0, 13 | "quotes": 0, 14 | "semi": 0, 15 | "no-cond-assign": 1, 16 | "no-constant-condition": 1, 17 | "no-duplicate-case": 1, 18 | "no-empty": 1, 19 | "no-ex-assign": 1, 20 | "no-extra-boolean-cast": 1, 21 | "no-extra-semi": 1, 22 | "no-fallthrough": 1, 23 | "no-func-assign": 1, 24 | "no-global-assign": 1, 25 | "no-implicit-globals": 2, 26 | "no-inner-declarations": ["error", "functions"], 27 | "no-irregular-whitespace": 2, 28 | "no-loop-func": 1, 29 | "no-multi-str": 1, 30 | "no-mixed-spaces-and-tabs": 1, 31 | "no-proto": 1, 32 | "no-sequences": 1, 33 | "no-throw-literal": 1, 34 | "no-unmodified-loop-condition": 1, 35 | "no-useless-call": 1, 36 | "no-void": 1, 37 | "no-with": 2, 38 | "wrap-iife": 1, 39 | "no-redeclare": 1, 40 | "no-unused-vars": ["error", { "vars": "all", "args": "none" }], 41 | "no-sparse-arrays": 1 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /.github/workflows/test.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs 3 | 4 | name: test 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [14.x, 16.x, 18.x] 20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 21 | 22 | steps: 23 | - uses: actions/checkout@v3 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v3 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | cache: 'npm' 29 | - run: npm ci 30 | - run: npm run build --if-present 31 | - run: npm test 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - 'node' 5 | - '6' 6 | - '5' 7 | - '4' 8 | matrix: 9 | fast_finish: true 10 | allow_failures: 11 | - node_js: "4" 12 | - node_js: "5" 13 | 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2018 Dmitry Ivanov 3 | 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, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 21 | OR OTHER DEALINGS IN THE SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /audio-decode.d.ts: -------------------------------------------------------------------------------- 1 | // audio-decode.d.ts 2 | 3 | export default function audioDecode(buf: ArrayBuffer | Uint8Array): Promise; 4 | 5 | export interface Decoders { 6 | oga: (buf?: Uint8Array) => Promise; 7 | mp3: (buf?: Uint8Array) => Promise; 8 | flac: (buf?: Uint8Array) => Promise; 9 | opus: (buf?: Uint8Array) => Promise; 10 | wav: (buf?: Uint8Array) => Promise; 11 | qoa: (buf?: Uint8Array) => Promise; 12 | } 13 | 14 | export const decoders: Decoders; 15 | -------------------------------------------------------------------------------- /audio-decode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Web-Audio-API decoder 3 | * @module audio-decode 4 | */ 5 | 6 | import getType from 'audio-type'; 7 | import AudioBufferShim from 'audio-buffer'; 8 | 9 | const AudioBuffer = globalThis.AudioBuffer || AudioBufferShim; 10 | 11 | /** 12 | * Decode an audio buffer. 13 | * 14 | * @param {ArrayBuffer | Uint8Array} buf - The audio data to decode. 15 | * @returns {Promise} A promise that resolves to the decoded audio buffer. 16 | * @throws {Error} Throws an error if the decode target is invalid or if the audio format is not supported. 17 | */ 18 | export default async function audioDecode(buf) { 19 | if (!buf && !(buf.length || buf.buffer)) throw Error('Bad decode target') 20 | buf = new Uint8Array(buf.buffer || buf) 21 | 22 | let type = getType(buf); 23 | 24 | if (!type) throw Error('Cannot detect audio format'); 25 | 26 | if (!decoders[type]) throw Error('Missing decoder for ' + type + ' format') 27 | 28 | return decoders[type](buf) 29 | }; 30 | 31 | export const decoders = { 32 | async oga(buf) { 33 | let { decoder } = decoders.oga 34 | if (!decoder) { 35 | let { OggVorbisDecoder } = await import('@wasm-audio-decoders/ogg-vorbis') 36 | await (decoders.oga.decoder = decoder = new OggVorbisDecoder()).ready; 37 | } else await decoder.reset() 38 | return buf && createBuffer(await decoder.decodeFile(buf)) 39 | }, 40 | async mp3(buf) { 41 | let { decoder } = decoders.mp3 42 | if (!decoder) { 43 | const { MPEGDecoder } = await import('mpg123-decoder') 44 | await (decoders.mp3.decoder = decoder = new MPEGDecoder()).ready; 45 | } 46 | else await decoder.reset() 47 | return buf && createBuffer(await decoder.decode(buf)) 48 | }, 49 | async flac(buf) { 50 | let { decoder } = decoders.flac 51 | if (!decoder) { 52 | const { FLACDecoder } = await import('@wasm-audio-decoders/flac') 53 | await (decoders.flac.decoder = decoder = new FLACDecoder()).ready 54 | } 55 | else await decoder.reset() 56 | return buf && createBuffer(await decoder.decode(buf)) 57 | }, 58 | async opus(buf) { 59 | let { decoder } = decoders.opus 60 | if (!decoder) { 61 | const { OggOpusDecoder } = await import('ogg-opus-decoder') 62 | await (decoders.opus.decoder = decoder = new OggOpusDecoder()).ready 63 | } 64 | else await decoder.reset() 65 | return buf && createBuffer(await decoder.decodeFile(buf)) 66 | }, 67 | async wav(buf) { 68 | let { decode } = decoders.wav 69 | if (!decode) { 70 | let module = await import('node-wav') 71 | decode = decoders.wav.decode = module.default.decode 72 | } 73 | return buf && createBuffer(await decode(buf)) 74 | }, 75 | async qoa(buf) { 76 | let { decode } = decoders.qoa 77 | if (!decode) { 78 | decoders.qoa.decode = decode = (await import('qoa-format')).decode 79 | } 80 | return buf && createBuffer(await decode(buf)) 81 | } 82 | } 83 | 84 | function createBuffer({ channelData, sampleRate }) { 85 | let audioBuffer = new AudioBuffer({ 86 | sampleRate, 87 | length: channelData[0].length, 88 | numberOfChannels: channelData.length 89 | }) 90 | for (let ch = 0; ch < channelData.length; ch++) audioBuffer.getChannelData(ch).set(channelData[ch]) 91 | return audioBuffer 92 | } 93 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "audio-decode", 3 | "version": "2.2.3", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "audio-decode", 9 | "version": "2.2.3", 10 | "license": "MIT", 11 | "dependencies": { 12 | "@wasm-audio-decoders/flac": "^0.2.4", 13 | "@wasm-audio-decoders/ogg-vorbis": "^0.1.15", 14 | "audio-buffer": "^5.0.0", 15 | "audio-type": "^2.2.1", 16 | "mpg123-decoder": "^1.0.0", 17 | "node-wav": "^0.0.2", 18 | "ogg-opus-decoder": "^1.6.12", 19 | "qoa-format": "^1.0.1" 20 | }, 21 | "devDependencies": { 22 | "audio-lena": "^2.3.0", 23 | "base64-arraybuffer": "^1.0.2", 24 | "tst": "^7.3.0" 25 | } 26 | }, 27 | "node_modules/@eshaz/web-worker": { 28 | "version": "1.2.2", 29 | "resolved": "https://registry.npmjs.org/@eshaz/web-worker/-/web-worker-1.2.2.tgz", 30 | "integrity": "sha512-WxXiHFmD9u/owrzempiDlBB1ZYqiLnm9s6aPc8AlFQalq2tKmqdmMr9GXOupDgzXtqnBipj8Un0gkIm7Sjf8mw==" 31 | }, 32 | "node_modules/@thi.ng/bitstream": { 33 | "version": "2.2.16", 34 | "resolved": "https://registry.npmjs.org/@thi.ng/bitstream/-/bitstream-2.2.16.tgz", 35 | "integrity": "sha512-5x97A5qjo/WAhEXQCoZLRsF2vVR3tISJhxkx59ynXGhURLg8bWS1NzFoUWqyV6c5E5x3Sw8Vhsi1HPPFmbn6rg==", 36 | "funding": [ 37 | { 38 | "type": "github", 39 | "url": "https://github.com/sponsors/postspectacular" 40 | }, 41 | { 42 | "type": "patreon", 43 | "url": "https://patreon.com/thing_umbrella" 44 | } 45 | ], 46 | "dependencies": { 47 | "@thi.ng/errors": "^2.2.12" 48 | }, 49 | "engines": { 50 | "node": ">=12.7" 51 | } 52 | }, 53 | "node_modules/@thi.ng/errors": { 54 | "version": "2.2.12", 55 | "resolved": "https://registry.npmjs.org/@thi.ng/errors/-/errors-2.2.12.tgz", 56 | "integrity": "sha512-z33uxNB7tha1p5N0FJGTdWyWunL7rcU+xMdbML8Jo3h9msoVI+0aQD172w+JahOYE+u5l27bD0+LwWen+qDXPw==", 57 | "funding": [ 58 | { 59 | "type": "github", 60 | "url": "https://github.com/sponsors/postspectacular" 61 | }, 62 | { 63 | "type": "patreon", 64 | "url": "https://patreon.com/thing_umbrella" 65 | } 66 | ], 67 | "engines": { 68 | "node": ">=12.7" 69 | } 70 | }, 71 | "node_modules/@wasm-audio-decoders/common": { 72 | "version": "9.0.5", 73 | "resolved": "https://registry.npmjs.org/@wasm-audio-decoders/common/-/common-9.0.5.tgz", 74 | "integrity": "sha512-b9JNh9sPAvn8PVIizNh9D60WkfQong/u9ea873H47u7zvVDLctxYIp2aZw9CQqXaQdk7JB3MoU5UHiseO40swg==", 75 | "dependencies": { 76 | "@eshaz/web-worker": "1.2.2", 77 | "simple-yenc": "^1.0.4" 78 | } 79 | }, 80 | "node_modules/@wasm-audio-decoders/flac": { 81 | "version": "0.2.4", 82 | "resolved": "https://registry.npmjs.org/@wasm-audio-decoders/flac/-/flac-0.2.4.tgz", 83 | "integrity": "sha512-bsUlwIjd5y+IAEyILCQdi8y0LocKEkZ0enA8ljDL+NVVwN+5Rv5Xkm/HcdUxnB7MtekxN2cNcTsv1zkb2aZyWg==", 84 | "dependencies": { 85 | "@wasm-audio-decoders/common": "9.0.5", 86 | "codec-parser": "2.4.3" 87 | }, 88 | "funding": { 89 | "type": "individual", 90 | "url": "https://github.com/sponsors/eshaz" 91 | } 92 | }, 93 | "node_modules/@wasm-audio-decoders/ogg-vorbis": { 94 | "version": "0.1.15", 95 | "resolved": "https://registry.npmjs.org/@wasm-audio-decoders/ogg-vorbis/-/ogg-vorbis-0.1.15.tgz", 96 | "integrity": "sha512-skAN3NIrRzMkVouyfyq3gYT/op/K9iutMZr7kr5/9fnIaCnpYdrdbv69X8PZ6y3K2J5zy5KuGno5kzH8yGLOOg==", 97 | "dependencies": { 98 | "@wasm-audio-decoders/common": "9.0.5", 99 | "codec-parser": "2.4.3" 100 | }, 101 | "funding": { 102 | "type": "individual", 103 | "url": "https://github.com/sponsors/eshaz" 104 | } 105 | }, 106 | "node_modules/atob": { 107 | "version": "2.1.2", 108 | "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", 109 | "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", 110 | "dev": true, 111 | "bin": { 112 | "atob": "bin/atob.js" 113 | }, 114 | "engines": { 115 | "node": ">= 4.5.0" 116 | } 117 | }, 118 | "node_modules/audio-buffer": { 119 | "version": "5.0.0", 120 | "resolved": "https://registry.npmjs.org/audio-buffer/-/audio-buffer-5.0.0.tgz", 121 | "integrity": "sha512-gsDyj1wwUp8u7NBB+eW6yhLb9ICf+0eBmDX8NGaAS00w8/fLqFdxUlL5Ge/U8kB64DlQhdonxYC59dXy1J7H/w==" 122 | }, 123 | "node_modules/audio-lena": { 124 | "version": "2.3.0", 125 | "resolved": "https://registry.npmjs.org/audio-lena/-/audio-lena-2.3.0.tgz", 126 | "integrity": "sha512-7gonKRzriM6QWpbEREpfbXHop/Z1zdM0zFwly/KNR49RB4dx2+UmjDSe3nUp/vS4F5OfZGS9kXrVbD5+fclmpQ==", 127 | "dev": true, 128 | "dependencies": { 129 | "atob": "^2.0.3" 130 | } 131 | }, 132 | "node_modules/audio-type": { 133 | "version": "2.2.1", 134 | "resolved": "https://registry.npmjs.org/audio-type/-/audio-type-2.2.1.tgz", 135 | "integrity": "sha512-En9AY6EG1qYqEy5L/quryzbA4akBpJrnBZNxeKTqGHC2xT9Qc4aZ8b7CcbOMFTTc/MGdoNyp+SN4zInZNKxMYA==", 136 | "engines": { 137 | "node": ">=14" 138 | } 139 | }, 140 | "node_modules/base64-arraybuffer": { 141 | "version": "1.0.2", 142 | "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", 143 | "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", 144 | "dev": true, 145 | "engines": { 146 | "node": ">= 0.6.0" 147 | } 148 | }, 149 | "node_modules/codec-parser": { 150 | "version": "2.4.3", 151 | "resolved": "https://registry.npmjs.org/codec-parser/-/codec-parser-2.4.3.tgz", 152 | "integrity": "sha512-3dAvFtdpxn4YLstqsB2ZiJXXNg7n1j7R5ONeDuk+2kBkb39PwrCRytOFHlSWA8q5jCjW3PumeMv9q37bFHsijg==" 153 | }, 154 | "node_modules/mpg123-decoder": { 155 | "version": "1.0.0", 156 | "resolved": "https://registry.npmjs.org/mpg123-decoder/-/mpg123-decoder-1.0.0.tgz", 157 | "integrity": "sha512-WV+pyuMUhRqv7s8S6p/Ii4KQHdBD1pb3yaABxcKJRsNp+HQ/Y6z2iIBIaOZu0JMHPTOoICYt0REDZ7XfLu+n/g==", 158 | "dependencies": { 159 | "@wasm-audio-decoders/common": "9.0.5" 160 | }, 161 | "funding": { 162 | "type": "individual", 163 | "url": "https://github.com/sponsors/eshaz" 164 | } 165 | }, 166 | "node_modules/node-wav": { 167 | "version": "0.0.2", 168 | "resolved": "https://registry.npmjs.org/node-wav/-/node-wav-0.0.2.tgz", 169 | "integrity": "sha512-M6Rm/bbG6De/gKGxOpeOobx/dnGuP0dz40adqx38boqHhlWssBJZgLCPBNtb9NkrmnKYiV04xELq+R6PFOnoLA==", 170 | "engines": { 171 | "node": ">=4.4.0" 172 | } 173 | }, 174 | "node_modules/ogg-opus-decoder": { 175 | "version": "1.6.12", 176 | "resolved": "https://registry.npmjs.org/ogg-opus-decoder/-/ogg-opus-decoder-1.6.12.tgz", 177 | "integrity": "sha512-6MY/rgFegJABKVE7LS10lmVoy8dFhvLDbIlcymgMnn0qZG0YHqcUU+bW+MkVyhhWN3H0vqtkRlPHGOXU6yR5YQ==", 178 | "dependencies": { 179 | "@wasm-audio-decoders/common": "9.0.5", 180 | "codec-parser": "2.4.3", 181 | "opus-decoder": "0.7.6" 182 | }, 183 | "funding": { 184 | "type": "individual", 185 | "url": "https://github.com/sponsors/eshaz" 186 | } 187 | }, 188 | "node_modules/opus-decoder": { 189 | "version": "0.7.6", 190 | "resolved": "https://registry.npmjs.org/opus-decoder/-/opus-decoder-0.7.6.tgz", 191 | "integrity": "sha512-5QYSl1YQYbSzWL7vM4dJoyrLC804xIvBFjfKTZZ6/z/EgmdFouOTT+8PDM2V18vzgnhRNPDuyB2aTfl/2hvMRA==", 192 | "dependencies": { 193 | "@wasm-audio-decoders/common": "9.0.5" 194 | }, 195 | "funding": { 196 | "type": "individual", 197 | "url": "https://github.com/sponsors/eshaz" 198 | } 199 | }, 200 | "node_modules/qoa-format": { 201 | "version": "1.0.1", 202 | "resolved": "https://registry.npmjs.org/qoa-format/-/qoa-format-1.0.1.tgz", 203 | "integrity": "sha512-dMB0Z6XQjdpz/Cw4Rf6RiBpQvUSPCfYlQMWvmuWlWkAT7nDQD29cVZ1SwDUB6DYJSitHENwbt90lqfI+7bvMcw==", 204 | "dependencies": { 205 | "@thi.ng/bitstream": "^2.2.12" 206 | } 207 | }, 208 | "node_modules/simple-yenc": { 209 | "version": "1.0.4", 210 | "resolved": "https://registry.npmjs.org/simple-yenc/-/simple-yenc-1.0.4.tgz", 211 | "integrity": "sha512-5gvxpSd79e9a3V4QDYUqnqxeD4HGlhCakVpb6gMnDD7lexJggSBJRBO5h52y/iJrdXRilX9UCuDaIJhSWm5OWw==", 212 | "funding": { 213 | "type": "individual", 214 | "url": "https://github.com/sponsors/eshaz" 215 | } 216 | }, 217 | "node_modules/tst": { 218 | "version": "7.3.0", 219 | "resolved": "https://registry.npmjs.org/tst/-/tst-7.3.0.tgz", 220 | "integrity": "sha512-c0HQtijwZRlYImcd+mbdJiy4zNpq/YF/TbdUIU+sS8A2Psr29VlPVmD/U2AaNqjheIXDWC2lV7G0GCHRQciLkw==", 221 | "dev": true 222 | } 223 | }, 224 | "dependencies": { 225 | "@eshaz/web-worker": { 226 | "version": "1.2.2", 227 | "resolved": "https://registry.npmjs.org/@eshaz/web-worker/-/web-worker-1.2.2.tgz", 228 | "integrity": "sha512-WxXiHFmD9u/owrzempiDlBB1ZYqiLnm9s6aPc8AlFQalq2tKmqdmMr9GXOupDgzXtqnBipj8Un0gkIm7Sjf8mw==" 229 | }, 230 | "@thi.ng/bitstream": { 231 | "version": "2.2.16", 232 | "resolved": "https://registry.npmjs.org/@thi.ng/bitstream/-/bitstream-2.2.16.tgz", 233 | "integrity": "sha512-5x97A5qjo/WAhEXQCoZLRsF2vVR3tISJhxkx59ynXGhURLg8bWS1NzFoUWqyV6c5E5x3Sw8Vhsi1HPPFmbn6rg==", 234 | "requires": { 235 | "@thi.ng/errors": "^2.2.12" 236 | } 237 | }, 238 | "@thi.ng/errors": { 239 | "version": "2.2.12", 240 | "resolved": "https://registry.npmjs.org/@thi.ng/errors/-/errors-2.2.12.tgz", 241 | "integrity": "sha512-z33uxNB7tha1p5N0FJGTdWyWunL7rcU+xMdbML8Jo3h9msoVI+0aQD172w+JahOYE+u5l27bD0+LwWen+qDXPw==" 242 | }, 243 | "@wasm-audio-decoders/common": { 244 | "version": "9.0.5", 245 | "resolved": "https://registry.npmjs.org/@wasm-audio-decoders/common/-/common-9.0.5.tgz", 246 | "integrity": "sha512-b9JNh9sPAvn8PVIizNh9D60WkfQong/u9ea873H47u7zvVDLctxYIp2aZw9CQqXaQdk7JB3MoU5UHiseO40swg==", 247 | "requires": { 248 | "@eshaz/web-worker": "1.2.2", 249 | "simple-yenc": "^1.0.4" 250 | } 251 | }, 252 | "@wasm-audio-decoders/flac": { 253 | "version": "0.2.4", 254 | "resolved": "https://registry.npmjs.org/@wasm-audio-decoders/flac/-/flac-0.2.4.tgz", 255 | "integrity": "sha512-bsUlwIjd5y+IAEyILCQdi8y0LocKEkZ0enA8ljDL+NVVwN+5Rv5Xkm/HcdUxnB7MtekxN2cNcTsv1zkb2aZyWg==", 256 | "requires": { 257 | "@wasm-audio-decoders/common": "9.0.5", 258 | "codec-parser": "2.4.3" 259 | } 260 | }, 261 | "@wasm-audio-decoders/ogg-vorbis": { 262 | "version": "0.1.15", 263 | "resolved": "https://registry.npmjs.org/@wasm-audio-decoders/ogg-vorbis/-/ogg-vorbis-0.1.15.tgz", 264 | "integrity": "sha512-skAN3NIrRzMkVouyfyq3gYT/op/K9iutMZr7kr5/9fnIaCnpYdrdbv69X8PZ6y3K2J5zy5KuGno5kzH8yGLOOg==", 265 | "requires": { 266 | "@wasm-audio-decoders/common": "9.0.5", 267 | "codec-parser": "2.4.3" 268 | } 269 | }, 270 | "atob": { 271 | "version": "2.1.2", 272 | "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", 273 | "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", 274 | "dev": true 275 | }, 276 | "audio-buffer": { 277 | "version": "5.0.0", 278 | "resolved": "https://registry.npmjs.org/audio-buffer/-/audio-buffer-5.0.0.tgz", 279 | "integrity": "sha512-gsDyj1wwUp8u7NBB+eW6yhLb9ICf+0eBmDX8NGaAS00w8/fLqFdxUlL5Ge/U8kB64DlQhdonxYC59dXy1J7H/w==" 280 | }, 281 | "audio-lena": { 282 | "version": "2.3.0", 283 | "resolved": "https://registry.npmjs.org/audio-lena/-/audio-lena-2.3.0.tgz", 284 | "integrity": "sha512-7gonKRzriM6QWpbEREpfbXHop/Z1zdM0zFwly/KNR49RB4dx2+UmjDSe3nUp/vS4F5OfZGS9kXrVbD5+fclmpQ==", 285 | "dev": true, 286 | "requires": { 287 | "atob": "^2.0.3" 288 | } 289 | }, 290 | "audio-type": { 291 | "version": "2.2.1", 292 | "resolved": "https://registry.npmjs.org/audio-type/-/audio-type-2.2.1.tgz", 293 | "integrity": "sha512-En9AY6EG1qYqEy5L/quryzbA4akBpJrnBZNxeKTqGHC2xT9Qc4aZ8b7CcbOMFTTc/MGdoNyp+SN4zInZNKxMYA==" 294 | }, 295 | "base64-arraybuffer": { 296 | "version": "1.0.2", 297 | "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", 298 | "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", 299 | "dev": true 300 | }, 301 | "codec-parser": { 302 | "version": "2.4.3", 303 | "resolved": "https://registry.npmjs.org/codec-parser/-/codec-parser-2.4.3.tgz", 304 | "integrity": "sha512-3dAvFtdpxn4YLstqsB2ZiJXXNg7n1j7R5ONeDuk+2kBkb39PwrCRytOFHlSWA8q5jCjW3PumeMv9q37bFHsijg==" 305 | }, 306 | "mpg123-decoder": { 307 | "version": "1.0.0", 308 | "resolved": "https://registry.npmjs.org/mpg123-decoder/-/mpg123-decoder-1.0.0.tgz", 309 | "integrity": "sha512-WV+pyuMUhRqv7s8S6p/Ii4KQHdBD1pb3yaABxcKJRsNp+HQ/Y6z2iIBIaOZu0JMHPTOoICYt0REDZ7XfLu+n/g==", 310 | "requires": { 311 | "@wasm-audio-decoders/common": "9.0.5" 312 | } 313 | }, 314 | "node-wav": { 315 | "version": "0.0.2", 316 | "resolved": "https://registry.npmjs.org/node-wav/-/node-wav-0.0.2.tgz", 317 | "integrity": "sha512-M6Rm/bbG6De/gKGxOpeOobx/dnGuP0dz40adqx38boqHhlWssBJZgLCPBNtb9NkrmnKYiV04xELq+R6PFOnoLA==" 318 | }, 319 | "ogg-opus-decoder": { 320 | "version": "1.6.12", 321 | "resolved": "https://registry.npmjs.org/ogg-opus-decoder/-/ogg-opus-decoder-1.6.12.tgz", 322 | "integrity": "sha512-6MY/rgFegJABKVE7LS10lmVoy8dFhvLDbIlcymgMnn0qZG0YHqcUU+bW+MkVyhhWN3H0vqtkRlPHGOXU6yR5YQ==", 323 | "requires": { 324 | "@wasm-audio-decoders/common": "9.0.5", 325 | "codec-parser": "2.4.3", 326 | "opus-decoder": "0.7.6" 327 | } 328 | }, 329 | "opus-decoder": { 330 | "version": "0.7.6", 331 | "resolved": "https://registry.npmjs.org/opus-decoder/-/opus-decoder-0.7.6.tgz", 332 | "integrity": "sha512-5QYSl1YQYbSzWL7vM4dJoyrLC804xIvBFjfKTZZ6/z/EgmdFouOTT+8PDM2V18vzgnhRNPDuyB2aTfl/2hvMRA==", 333 | "requires": { 334 | "@wasm-audio-decoders/common": "9.0.5" 335 | } 336 | }, 337 | "qoa-format": { 338 | "version": "1.0.1", 339 | "resolved": "https://registry.npmjs.org/qoa-format/-/qoa-format-1.0.1.tgz", 340 | "integrity": "sha512-dMB0Z6XQjdpz/Cw4Rf6RiBpQvUSPCfYlQMWvmuWlWkAT7nDQD29cVZ1SwDUB6DYJSitHENwbt90lqfI+7bvMcw==", 341 | "requires": { 342 | "@thi.ng/bitstream": "^2.2.12" 343 | } 344 | }, 345 | "simple-yenc": { 346 | "version": "1.0.4", 347 | "resolved": "https://registry.npmjs.org/simple-yenc/-/simple-yenc-1.0.4.tgz", 348 | "integrity": "sha512-5gvxpSd79e9a3V4QDYUqnqxeD4HGlhCakVpb6gMnDD7lexJggSBJRBO5h52y/iJrdXRilX9UCuDaIJhSWm5OWw==" 349 | }, 350 | "tst": { 351 | "version": "7.3.0", 352 | "resolved": "https://registry.npmjs.org/tst/-/tst-7.3.0.tgz", 353 | "integrity": "sha512-c0HQtijwZRlYImcd+mbdJiy4zNpq/YF/TbdUIU+sS8A2Psr29VlPVmD/U2AaNqjheIXDWC2lV7G0GCHRQciLkw==", 354 | "dev": true 355 | } 356 | } 357 | } 358 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "audio-decode", 3 | "version": "2.2.3", 4 | "description": "Decode audio data in node or browser", 5 | "main": "audio-decode.js", 6 | "module": "audio-decode.js", 7 | "browser": "audio-decode.js", 8 | "type": "module", 9 | "types": "audio-decode.d.ts", 10 | "dependencies": { 11 | "@wasm-audio-decoders/flac": "^0.2.4", 12 | "@wasm-audio-decoders/ogg-vorbis": "^0.1.15", 13 | "audio-buffer": "^5.0.0", 14 | "audio-type": "^2.2.1", 15 | "mpg123-decoder": "^1.0.0", 16 | "node-wav": "^0.0.2", 17 | "ogg-opus-decoder": "^1.6.12", 18 | "qoa-format": "^1.0.1" 19 | }, 20 | "devDependencies": { 21 | "audio-lena": "^2.3.0", 22 | "base64-arraybuffer": "^1.0.2", 23 | "tst": "^7.3.0" 24 | }, 25 | "scripts": { 26 | "test": "node test.js" 27 | }, 28 | "repository": { 29 | "type": "git", 30 | "url": "git+https://github.com/audiojs/audio-decode.git" 31 | }, 32 | "keywords": [ 33 | "audiojs", 34 | "audio", 35 | "dsp", 36 | "decode", 37 | "audio decode", 38 | "audio decoder", 39 | "web audio decoder", 40 | "codec", 41 | "mp3", 42 | "wav", 43 | "ogg", 44 | "vorbis", 45 | "opus", 46 | "web-audio" 47 | ], 48 | "author": "ΔY ", 49 | "license": "MIT", 50 | "bugs": { 51 | "url": "https://github.com/audiojs/audio-decode/issues" 52 | }, 53 | "homepage": "https://github.com/audiojs/audio-decode#readme" 54 | } 55 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # audio-decode [![test](https://github.com/audiojs/audio-decode/actions/workflows/test.js.yml/badge.svg)](https://github.com/audiojs/audio-decode/actions/workflows/test.js.yml) [![stable](https://img.shields.io/badge/stability-unstable-green.svg)](http://github.com/badges/stability-badges) 2 | 3 | Decode audio data from supported format to [AudioBuffer](https://github.com/audiojs/audio-buffer). 4 | 5 | Supported formats: 6 | 7 | * [x] `wav` 8 | * [x] `mp3` 9 | * [x] `ogg vorbis` 10 | * [x] `flac` 11 | * [x] `opus` 12 | * [ ] `alac` 13 | * [ ] `aac` 14 | * [ ] `m4a` 15 | * [x] [`qoa`](https://github.com/phoboslab/qoa) 16 | 17 | [![npm install audio-decode](https://nodei.co/npm/audio-decode.png?mini=true)](https://npmjs.org/package/audio-decode/) 18 | 19 | ```js 20 | import decodeAudio from 'audio-decode'; 21 | import buffer from 'audio-lena/mp3'; 22 | 23 | let audioBuffer = await decode(buffer); 24 | ``` 25 | 26 | `buffer` type can be: _ArrayBuffer_, _Uint8Array_ or _Buffer_. 27 | 28 | `decode` is lazy: first call prepares decoder. 29 | 30 | To get more granular control over individual decoders, use `decoders`: 31 | 32 | ```js 33 | import decode, {decoders} from 'audio-decode'; 34 | 35 | await decoders.mp3(); // load & compile decoder 36 | const audioBuffer = await decoders.mp3(mp3buf); // decode 37 | ``` 38 | 39 | ## See also 40 | 41 | * [wasm-audio-decoders](https://github.com/eshaz/wasm-audio-decoders) – best in class compact & fast WASM audio decoders. 42 | * [Web Audio Decoders](https://developer.mozilla.org/en-US/docs/Web/API/AudioDecoder) – native decoders API, hope one day will be fixed or alternatively polyfilled. 43 | * [decodeAudioData](https://github.com/eshaz/wasm-audio-decoders) – default in-browser decoding method. 44 | * [ffmpeg.wasm](https://github.com/ffmpegwasm/ffmpeg.wasm) – ultimate encoding/decoding library (8.5Mb of code). 45 | 46 | ## License 47 | 48 | [MIT](LICENSE)  •  🕉 49 | 50 | -------------------------------------------------------------------------------- /tea.yaml: -------------------------------------------------------------------------------- 1 | # https://tea.xyz/what-is-this-file 2 | --- 3 | version: 1.0.0 4 | codeOwners: 5 | - '0x18CEa38f92a0E9b028ea27DD935B36A55Cf83b06' 6 | quorum: 1 7 | -------------------------------------------------------------------------------- /test-case.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs/promises' 2 | 3 | async function main(options) { 4 | const audioDecode = await import('./audio-decode.js'); 5 | const decode = audioDecode.default; 6 | await audioDecode.decoders.mp3(); 7 | const buffer = await fs.readFile('test.mp3'); 8 | const decodedBuffer = await decode(buffer); 9 | } 10 | 11 | main().catch(console.error) -------------------------------------------------------------------------------- /test.html: -------------------------------------------------------------------------------- 1 | 2 | 15 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | 2 | import decodeAudio, { decoders } from './audio-decode.js'; 3 | import wav from 'audio-lena/wav.js'; 4 | import mp3 from 'audio-lena/mp3.js'; 5 | import ogg from 'audio-lena/ogg.js'; 6 | import flac from 'audio-lena/flac.js'; 7 | import opus from 'audio-lena/opus.js'; 8 | import t, { is, throws } from 'tst'; 9 | import b64 from 'base64-arraybuffer' 10 | 11 | 12 | //as a callback 13 | t('wav buffer', async function (t) { 14 | console.time('wav first') 15 | await decoders.wav() 16 | console.timeEnd('wav first') 17 | 18 | console.time('wav second') 19 | let audioBuffer = await decodeAudio(wav) 20 | console.timeEnd('wav second') 21 | is(audioBuffer.duration | 0, 12, 'wav duration') 22 | }); 23 | 24 | t('mp3 buffer', async function (t) { 25 | console.time('mp3 first') 26 | await decoders.mp3() 27 | console.timeEnd('mp3 first') 28 | 29 | console.time('mp3 second') 30 | let audioBuffer = await decodeAudio(mp3) 31 | console.timeEnd('mp3 second') 32 | is(audioBuffer.duration | 0, 12, 'mp3 duration') 33 | }); 34 | 35 | t('ogg buffer', async function (t) { 36 | console.time('ogg first') 37 | let audioBuffer = await decodeAudio(ogg) 38 | console.timeEnd('ogg first') 39 | is(audioBuffer.duration | 0, 12, 'ogg duration') 40 | 41 | console.time('ogg second') 42 | audioBuffer = await decodeAudio(ogg) 43 | console.timeEnd('ogg second') 44 | is(audioBuffer.duration | 0, 12, 'ogg duration') 45 | }); 46 | 47 | t('flac buffer', async function (t) { 48 | console.time('flac first') 49 | let audioBuffer = await decodeAudio(flac) 50 | console.timeEnd('flac first') 51 | is(audioBuffer.duration | 0, 12, 'flac duration') 52 | 53 | console.time('flac second') 54 | audioBuffer = await decodeAudio(flac) 55 | console.timeEnd('flac second') 56 | is(audioBuffer.duration | 0, 12, 'flac duration') 57 | }); 58 | 59 | t('opus buffer', async function (t) { 60 | console.time('opus first') 61 | let audioBuffer = await decodeAudio(opus) 62 | console.timeEnd('opus first') 63 | is(audioBuffer.duration | 0, 12, 'opus duration') 64 | 65 | console.time('opus second') 66 | audioBuffer = await decodeAudio(opus) 67 | console.timeEnd('opus second') 68 | is(audioBuffer.duration | 0, 12, 'opus duration') 69 | }); 70 | 71 | t('qoa buffer', async function (t) { 72 | const qoa = b64.decode(`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X3a67RrprrpprrbbFtttsttssNgONBfwBa04JhCKKCCKKSQQGCBBBBBBBJIYkgkkAgkEEBSSCQQQSCCSFBFBAAgBEAoPmU3ySkzWBAv9eBrLEIZdBcWMcHZUPTQCGFshFtlJAQR2SOW2lT20BxbbbabbbSIQEEUUgiiggAyi2UyQKGbrBTIAhYDvtc8DX3SL/3Rb/wcD9pDv//fFA+vi738BU0AJokRJBFFFNgyU22uumwQGDZKKaTaIabYEAttKntklFA0kCEEmhR2xA1gJt+sGu1sLxT/2m/7wzQ/7Xf945o77Btc+sRCmxRoLHosottttJBCCKCSSCCQQFBJJJIIJBBIUkggkggggggm2202m22aGDaBSbTJKZJIEAJIK6mL76Qa59Dt/v9otD91Bj3givgwGimCgkAUppARW0RQmWgRyCAAIKbJPLYQAptI08kggkAiQYce87oAtD0t+++1+//8SSWWSy2WWWxbZZZZZZZbLEtttlltssssP/+7//b//+Qb//Jd/9rr/D+21+/hj30EBVw0gmw0yWQrYTHFTVf/bC3/uv///2/8SwwyyyWWyyQ///v///9t/FhhllkklllkWWWWWWWSSSQ/VH94rfPbaAQC7gBQACBgAAAAFAAQAA+3RDv3eIxoxBiktOAByaBAFoogEtIplpg2UO2yik2yRDTZZaabSYbYNJFttAttssg220k2220m2DIKbbabbbLYQggggkkggghCCCSQSSSSQIAAAAAAAAAAUEkkkkkkkkhSSSCSSSSSSFBBIIIIJBBAUEggEEAAAAAy022yym2ykCbZJbbbLTbQMstNpEttpJg22SGmmUUGWBIYaKFBaZJYNIIEFFCigkATFQCpbBZtvB2/++3v//38WWWWW2yyyyxLLLZZZZbLLIkkkkkkkkkkSyyyyy2222xbbLLZZZZZZFllsssstllkWWGGGGGGWWQ/P//f//9vvD/u3///u//0Db/7W37aaOQrtNdvbFFdoBqnssNjhgJoGAEgiAtIEpAkQQSigWGUiAJLSKSbbRbYNpNNttpNttAi02iU22mWmDTKSTSZTTaIBJtMoppEhhAUUCUkQSi0iBSaQIJRQQJIMEghIoUIsEAik0SUUkWGWDZQabTKLTKIJNNplNtlFJgmkU22EkmWUBRZJNOPOVPAIAQAEQSAgQwDBQYoAAYYKANdVMtmmhisKWW3XwxXSQwVXFTVVRQCIBoQJowYYwRQBDBBIM4MskASkUCGWSUEGBIAIQwopKEEAggAAEo448wzTDjlVTjTDAgJqpgIAMAAAxRU1TSwS2wdFFFBQDVTQBNdEJphoxpoDBAgAEkAUEgBDAADYqZLHBWENtmrrihgK+e7bb627bQtNNtZDNFYoDnuljm192/8KaXXa3b/a2wrrrDLbDTVFANEssMElhogGKGnGGQESGAaADVRRBJBTBVeONMWOAAAEkigAgg04YQRAKCIYQRAAAJIgoIIBopAAQEEkAIlAkgSkSUk0SUUkBRSaZRSaaSYJtpJJJhDhlAUU02mW20y0CbSLTbbbbbYItNtttNtpJgm2UWmmGWmmBSTTRYZaaLQNpEotMscMoAgSSiSEUjjQAQJxwJEBppgGWCiCKnGGWAYbdrdHbrrrD20cd20123sG6+/bbffXaw7vHfv9fvv/Cv3+9+/+930P3z337ae/XwHdttNPdbZrB1sUVmhks0MFV11ZVQwAiAIACJFTQaaIBABoKCJRSSIEpJFFJNMotAmky0mUSyykBKLRRSSRQRYJIpJNMsNJEggiUQg0ygSEARBKRQJBJQQAkokAkkkY8QgABCCIIQQAAOONMAIpqy4GmmiACWQCWAqqYopJFHXFAU10tut1tu0LTT3X333/ew9/trtvtbrtB00cd1t121sHb3W6662zXQdbbddNHddNAU1kgsUMElsGGqGWWmKGAABBDjTJaaYYBgAANXNEgowCiEEEAAkkgASSCKcCRQSSCBBJRIJKLCIFlkklhhAlFAUSSigQUUSSAZaKLLLCJRQIIMssspNMIgkWGEUkiUWWBRKLSRZaTCIJJIopJIlJMgykWkiUkyyiDLLGHDKRRZYIpJplhhlhAgUUUCUUCCSSBBBKCGGCCBAIIgE4YEABAgIaaADTQAIYBpzzyxhhlhgKKGGKKWCWWAqopbLFNLFVAU0MNttlt10GKOKWy2622wrbLLNHlTJYBgMUVMMsUMEGWGmGWWGKKgaYYqaADTjTAFhpiihhltkCmqiAAASAEwVjjTVDRAacBwogBIJxpCAAjBCmmCgAUASCKKAQIIQQAABBAIQJIIQBAgnAkAAhAgASCAIIAIJBBNMMNEEEONMAwwWW2Sy22QJbbLJZbFVDBmhhuut111sGa622W26KXQNbbddddd77Cu222v29110LXTbba6+++QprrrbddZbdBuiimlkUUtkC22WCGGGiiAKCCQQSiQSUBRJKKJJLDKIFJJIspJlFpAyykk0y022mCabTSbbaabYNtNNtptNtpg2222m22m02DbTbbaabbbQNNttNtttttg202222m22mDTbbSbbTSaYNJJMtJFlpIAiiSSSUQRQgCBIJJIooIpoGnGmCU1V1UwDLLDDLYqpbAsstuls01ugO7XXbfe6++wtdr77r79tdC222v3199+8Lbb/7fff/7wv/979/vvv9Dvv3+9+++20L77a/fb37ew7rttrrtvbbC088c910110G6Oa2y222KgpbDDNHDYppBhhlkNVUMkgGGGimmAUQwwRRIaaKaaAQAAgBFNOOFFEIEgAAgAQAEQVTRQAASQAIAoAAgAAggIIAAAgAQUEAAAQgCKCAAAQgAIoJAAIJIAIEkEUgkAAEIAQQSSCEUGeCCKRJRRQQYZQIossopJMspAkkkWWki02kDLLTLLTaTLQJtMtNtNNNtA0022002002DbaabbaaaaYJNNpJlptMtA2kk000mk0kCSSSRRZSKJQIsIYIkghBjAKCCCAQgjjhBAAghFNNFAECGmqKWyKuKAYo66qopoorB001uutt11sKee663z3W7Qtdddfdb77tA2vuuvvuuu0Ha2666y3a2wdbrrbrrrrrCutt11stt0sC3W22222WOArZbrbbJbbZBmttlt0thigKKmGxVQxxVQVDLDTDLVlVANNMMMAhksEAQw00SSQQRQVjAYYIBDBBABqooEEAoqoKmmmGiA1U0QQIKKIYYcaYAgAANNAAgAAEAQEAAAAgAQACAAAQQQCEAIIoJAAIIIAEIEggoIEEIAhQECQQCggSAJRQJKKJJRQFAkolBEopBAEiiSUiiUUiCKDDKKKSSRIIsoklFEklFAiSUiiEEiSSBKKCCKJJJKIBALuAFAAIGAACAAMABQAF8fkSz+MTHu8IIFFEEkokhACiSCCQQCCSAIIJRIJJJBAEkkEEkkEEkgCCEECCEECCAJIIBIQooJAEUUgggEIEAACCKaKABABRAAgAIggAAEEAAGqmmmGCAQBBABDTTRDBAEAhpppqppoGCCAAwQCCSQABDBBBDBBBAMEEEMEEEMMA0ww00ww00wDDTTDIaaqqBpphgNUNhkMAySGGGQ0wywDDDJIYZbFFAUVUNmiiimgKGW2yzTSyywbZbNHHVHFNAttttlt0000G26Ka22222wbdNbbbbbZpClsUVtssttkGKaaKKaaKug4oZLFDDDLDBMMMBypggAgCGiiGigAAAgCKaKARTQQAAAJBBAJBBGEIEAgkggkIEAAQgQQQiSCEAQIIQQJCCJIAkkkEkkoEIAgSEESSQiiSBJJJBJRJJJIEkkIMoEookgSUUCQgSSQSCCBJJQJCJQQBBEkhFEkklAUCSUECQiSQBJJJIJJJBJQBAklBAkIIEAQQgQQgiSSUBJCCJIIJJJAEkoIYEkggkgSCSSSQCQhhBBJIBBIJBBIEAAEgiiggAAAAABBQTTTRBNNNFAhogNMAQAGmiCGqmgYYYYLFDIYoCqhhksMMsloKGwwWKKKKKQoooopbHHFFAUUtijikttsG22222y22zQNbbbbbbLNbBlmjiksUdUMCx1VQyWGCSQIIYYYBDDDTAMMNNMNNEEMEUU00UQHLCgAIAABBAAAIAoAAAFEEFOMI00UAAUUAQQAABDRAIIAAABpogggAAEEAAAGnGiAQAABDTjTTjjRBABFNFAAAAIwCgEEEAEIY4QQCKIRhQAQQBBAIIABBBAIAggAEEEAgggRQSQgSSSQSBJCBJKCBJJIEEIkkgkkgkAgiEOCQggiiBIRKCBJKJJIEkkkookkokgSSSSQSSSSSBIIJCCBCCIQAkhBAkkkgggSSCCSQQSSSAIJIIIIIBFAAggAggAAAEQRRQCaaAIIBBEENVMMMNNMCw0wxVVVQ2AYZZZYopZLLBssMttlimlkGWaKSy2WWWQbZYoooopppBltssUUUVUMGKGW2WWWWywDFFFVFFFDFAUMMUUVUUVUAwyySSyWGGAoYJYqoYYqqBphhgglhppgGGmiCAU000wTRBAABBAABAEAAAgBEAAAAAAAEAAAAAAAARRRQQRQCAoAAIIAABAIAgkEEEIEAggCCMMCCASSCAIJAIIJBAIIAgEEEEAgggAAQQCCCCAAQBAABBAABBAAAEEAAAggAEAQAAQRQQQhQAIBBABBBFAIAEEEEAAEEEQQCAAACCAACAIABBBFBBBAAgkEEEkAEEAQThQCKAQAAABBAAAAAAAIAAAAAigEUUwRQAAAAAAACAAAIoAAAIooCgE0QjHGiEwjRAARRQCAAAAIoooBEAIAAACnGmnHGmgccaIBAAIKKBppogNWWNMEAQCGmmmmmmgaYaaIaaaYYBppqppppgkEAQ00wSGmmGAYIJJDTBJDTAMMMEhgggkME0www0wSGCAJJBBDDDTBJANMMMEEMNNEAQGiAwwCmmgaaYIKaIIAAAggAAEEAAEEAAAQQiAU44wTTjjTTjjRBBFNNEAAAAAACCAAAAAAAAQTjRAKKIIAAAggAAEFAAgoCimmiAQQAAAIIAAIIAABBBFEAggANONME00QGmiAQQAAAIIAAIIABAEAAAgooAEAAAAQUUAAAigIAAAAAAAAAAAAAAAAAAIoAEUAiggggAgCCKCCCAQQQAIIJBBBBBAIAggAEEEEEEAQCCCCCCAQABBAAIIIoBAAAAEEAAAAAAAARQCKKaKAAAAAAAAAAAAAAAAAAAAU44wRAAAAAAAAAAAAAAAAAAAAAAAAAUUU0UQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACnHGmigACmgIAAAAAKaKaBppoggpogkAAQ00QCCAU0wBBDVTTTBIIAhhhqyqpgkkCSGqmCSWGCQDDDDDDDDDDAMMMMMMMMMMAwwwwwwwwwwDDDDDDDDDDAMMMMMMNNMMACCCGCA1U0wTTBBDDAIDTBNNEEAAAApwHGiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEUAiiiAEAAAAQACAAAAAAAAAAIABBAAAAAAAAAAAAAAAAAAAAACAABBAAABAAIAEY44UEAAEARQQQCKCCAQBBAAAIIBGFAEEEEEEEEEggECQQQSCCCCAIIIIIIJBBAEEEEEEEEEEAQQSCQQSCCCAIIIIIIIBBAEAAggAAEAAARQAAAAAAAAAAAAAAAAAEEEUQCCCmmmiAJBAIJDTTDDANVMEkkkkkMAwQwwww1UwQJDDDVVVVTBAsNMMEMMMMMAwwww1UwSSQJJIYYaqqYYAhhhhgkNNVUEww1UwGmA0wTTTAIIIaaaBppppggggggCCCGmmmCCGgaaaaaaIDTRAAggFNEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAu4AOBwW4AAEAAQABAAH0shPQ5kAgKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKaIBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU0QCAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAACigKKKKKKKKKKAopxoggAAAEE0QGmiA00QAIIAAAKaIAAAAAFNEAppooCCAAACAACmgaIAIKKIBBAAAgAAgEAoooCiiiAAACmiAIBTRBAAABBBFFEAAAEAAEE40QAQUUUUQRRTjRAAAKaAgFNEApogEAAAUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACmiAAIAAIBAKIBTBNNNNNNNNNME000000000wRBBAaaaaIIAggggggAFNEAAQQACCnGiAAAAAKaIAKaAgAAAAApogAAAAAAAAAU0QAABTRAIAAKAgAAAgoopogAQQCmiCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU0QAU0QBBAaaaaIBBAAgggggNNNME000000000wTTTTTRBBAKBpppogAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==`) 73 | 74 | console.time('qoa first') 75 | let audioBuffer = await decodeAudio(qoa) 76 | console.timeEnd('qoa first') 77 | 78 | console.time('qoa second') 79 | audioBuffer = await decodeAudio(qoa) 80 | console.timeEnd('qoa second') 81 | is(Math.floor(audioBuffer.duration * 100) / 100, 0.82, 'qoa duration') 82 | }) 83 | 84 | t('malformed data', async t => { 85 | let log = [] 86 | try { 87 | let x = await decodeAudio(new Float32Array(10)) 88 | } catch (e) { log.push('arr') } 89 | 90 | try { 91 | let x = await decodeAudio(null) 92 | } catch (e) { log.push('null') } 93 | 94 | try { 95 | let x = await decodeAudio(Promise.resolve()) 96 | } catch (e) { log.push('nonbuf') } 97 | 98 | is(log, ['arr', 'null', 'nonbuf']) 99 | }) 100 | 101 | t.skip('sequence #38', async t => { 102 | let flacBuffer = await decoders.flac(new Uint8Array(flac)) 103 | is(flacBuffer.duration | 0, 12, 'flac duration') 104 | 105 | let mp3Buffer = await decoders.mp3(new Uint8Array(mp3)) 106 | is(mp3Buffer.duration | 0, 12, 'mp3 duration') 107 | }) 108 | --------------------------------------------------------------------------------