audio-decode 2.2.2 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,31 +1,22 @@
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
1
  name: test
5
2
 
6
3
  on:
7
4
  push:
8
- branches: [ "master" ]
5
+ branches: [master]
9
6
  pull_request:
10
- branches: [ "master" ]
7
+ branches: [master]
11
8
 
12
9
  jobs:
13
- build:
14
-
10
+ test:
15
11
  runs-on: ubuntu-latest
16
-
17
12
  strategy:
18
13
  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
-
14
+ node-version: [20.x, 22.x]
22
15
  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
16
+ - uses: actions/checkout@v4
17
+ - uses: actions/setup-node@v4
18
+ with:
19
+ node-version: ${{ matrix.node-version }}
20
+ cache: npm
21
+ - run: npm ci
22
+ - run: npm test
package/.work/todo.md ADDED
@@ -0,0 +1,9 @@
1
+ ## Missing codecs
2
+
3
+ - [ ] aiff decoder
4
+ - [ ] aac (raw ADTS) decoder
5
+ - [ ] wma decoder
6
+ - [ ] amr decoder
7
+ - [ ] webm audio decoder
8
+ - [ ] caf decoder
9
+ - [ ] midi decoder
package/audio-decode.d.ts CHANGED
@@ -1,14 +1,32 @@
1
- // audio-decode.d.ts
2
-
3
- export default function audioDecode(buf: ArrayBuffer | Uint8Array): Promise<AudioBuffer>;
1
+ export interface AudioData {
2
+ channelData: Float32Array[];
3
+ sampleRate: number;
4
+ }
4
5
 
5
- export interface Decoders {
6
- oga: (buf: Uint8Array) => Promise<AudioBuffer>;
7
- mp3: (buf: Uint8Array) => Promise<AudioBuffer>;
8
- flac: (buf: Uint8Array) => Promise<AudioBuffer>;
9
- opus: (buf: Uint8Array) => Promise<AudioBuffer>;
10
- wav: (buf: Uint8Array) => Promise<AudioBuffer>;
11
- qoa: (buf: Uint8Array) => Promise<AudioBuffer>;
6
+ export interface StreamDecoder {
7
+ /** Feed a chunk of encoded audio data. */
8
+ decode(chunk: Uint8Array): Promise<AudioData>;
9
+ /** Flush remaining buffered data and free resources. */
10
+ decode(): Promise<AudioData>;
11
+ /** Free resources without flushing. */
12
+ free(): void;
12
13
  }
13
14
 
14
- export const decoders: Decoders;
15
+ /** Whole-file decode: auto-detects format. */
16
+ export default function decode(buf: ArrayBuffer | Uint8Array): Promise<AudioData>;
17
+
18
+ /** Decode a ReadableStream of audio chunks. */
19
+ export function decodeStream(
20
+ stream: ReadableStream<Uint8Array> | AsyncIterable<Uint8Array>,
21
+ format: 'mp3' | 'flac' | 'opus' | 'oga' | 'm4a' | 'wav' | 'qoa'
22
+ ): AsyncGenerator<AudioData>;
23
+
24
+ export declare const decoders: {
25
+ mp3(): Promise<StreamDecoder>;
26
+ flac(): Promise<StreamDecoder>;
27
+ opus(): Promise<StreamDecoder>;
28
+ oga(): Promise<StreamDecoder>;
29
+ m4a(): Promise<StreamDecoder>;
30
+ wav(): Promise<StreamDecoder>;
31
+ qoa(): Promise<StreamDecoder>;
32
+ };
package/audio-decode.js CHANGED
@@ -1,92 +1,159 @@
1
1
  /**
2
- * Web-Audio-API decoder
3
- * @module audio-decode
2
+ * Audio decoder: whole-file and streaming
3
+ * @module audio-decode
4
+ *
5
+ * let { channelData, sampleRate } = await decode(mp3buf)
6
+ *
7
+ * let dec = await decoders.mp3()
8
+ * let { channelData, sampleRate } = await dec.decode(chunk)
9
+ * await dec.decode() // flush + free
4
10
  */
5
11
 
6
12
  import getType from 'audio-type';
7
- import AudioBufferShim from 'audio-buffer';
8
13
 
9
- const AudioBuffer = globalThis.AudioBuffer || AudioBufferShim;
14
+ const EMPTY = Object.freeze({ channelData: Object.freeze([]), sampleRate: 0 })
10
15
 
11
16
  /**
12
- * Decode an audio buffer.
13
- *
14
- * @param {ArrayBuffer | Uint8Array} buf - The audio data to decode.
15
- * @returns {Promise<AudioBuffer>} 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
+ * Whole-file decode: auto-detects format
18
+ * @param {ArrayBuffer|Uint8Array} src - encoded audio data
19
+ * @returns {Promise<{channelData: Float32Array[], sampleRate: number}>}
17
20
  */
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);
21
+ export default async function decode(src) {
22
+ if (!src || typeof src === 'string' || !(src.buffer || src.byteLength || src.length))
23
+ throw TypeError('Expected ArrayBuffer or Uint8Array')
24
+ let buf = new Uint8Array(src.buffer || src)
23
25
 
24
- if (!type) throw Error('Cannot detect audio format');
26
+ let type = getType(buf)
27
+ if (!type) throw Error('Unknown audio format')
28
+ if (!decoders[type]) throw Error('No decoder for ' + type)
25
29
 
26
- if (!decoders[type]) throw Error('Missing decoder for ' + type + ' format')
30
+ let dec = await decoders[type]()
31
+ try {
32
+ let result = await dec.decode(buf)
33
+ let flushed = await dec.decode()
34
+ return merge(result, flushed)
35
+ } catch (e) {
36
+ dec.free()
37
+ throw e
38
+ }
39
+ }
27
40
 
28
- return decoders[type](buf)
29
- };
41
+ /**
42
+ * Decode a ReadableStream of audio chunks
43
+ * @param {ReadableStream} stream - stream of Uint8Array chunks
44
+ * @param {string} format - codec name (mp3, flac, opus, oga, m4a, wav, qoa)
45
+ * @returns {AsyncGenerator<{channelData: Float32Array[], sampleRate: number}>}
46
+ */
47
+ export async function* decodeStream(stream, format) {
48
+ if (!decoders[format]) throw Error('No decoder for ' + format)
49
+ let dec = await decoders[format]()
50
+ try {
51
+ for await (let chunk of stream) {
52
+ let result = await dec.decode(chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk))
53
+ if (result.channelData.length) yield result
54
+ }
55
+ let flushed = await dec.decode()
56
+ if (flushed.channelData.length) yield flushed
57
+ } finally {
58
+ dec.free()
59
+ }
60
+ }
30
61
 
62
+ // codec registry: each returns an initialized StreamDecoder
31
63
  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))
64
+ async mp3() {
65
+ const { MPEGDecoder } = await import('mpg123-decoder')
66
+ let dec = new MPEGDecoder()
67
+ await dec.ready
68
+ return streamDecoder(chunk => dec.decode(chunk), null, () => dec.free())
39
69
  },
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))
70
+
71
+ async flac() {
72
+ const { FLACDecoder } = await import('@wasm-audio-decoders/flac')
73
+ let dec = new FLACDecoder()
74
+ await dec.ready
75
+ return streamDecoder(chunk => dec.decode(chunk), () => dec.flush(), () => dec.free())
48
76
  },
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))
77
+
78
+ async opus() {
79
+ const { OggOpusDecoder } = await import('ogg-opus-decoder')
80
+ let dec = new OggOpusDecoder()
81
+ await dec.ready
82
+ return streamDecoder(chunk => dec.decode(chunk), () => dec.flush(), () => dec.free())
57
83
  },
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))
84
+
85
+ async oga() {
86
+ const { OggVorbisDecoder } = await import('@wasm-audio-decoders/ogg-vorbis')
87
+ let dec = new OggVorbisDecoder()
88
+ await dec.ready
89
+ return streamDecoder(chunk => dec.decode(chunk), () => dec.flush(), () => dec.free())
66
90
  },
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))
91
+
92
+ async m4a() {
93
+ const { decoder } = await import('aac-decode')
94
+ let dec = await decoder()
95
+ return streamDecoder(chunk => dec.decode(chunk), () => dec.flush(), () => dec.free())
96
+ },
97
+
98
+ async wav() {
99
+ let { default: { decode: wavDecode } } = await import('node-wav')
100
+ return streamDecoder(chunk => wavDecode(chunk))
101
+ },
102
+
103
+ async qoa() {
104
+ let { decode } = await import('qoa-format')
105
+ return streamDecoder(chunk => decode(chunk))
74
106
  },
75
- async qoa(buf) {
76
- let { decode } = decoders.qoa
77
- if (!decode) {
78
- decoders.qoa.decode = decode = (await import('qoa-format')).decode
107
+ }
108
+
109
+ /**
110
+ * StreamDecoder: unified interface
111
+ * .decode(chunk) — decode data, returns { channelData, sampleRate }
112
+ * .decode() — flush remaining + free resources
113
+ * .free() — free resources without flushing
114
+ */
115
+ function streamDecoder(onDecode, onFlush, onFree) {
116
+ let done = false
117
+ return {
118
+ async decode(chunk) {
119
+ if (chunk?.length) {
120
+ if (done) throw Error('Decoder already freed')
121
+ try { return norm(await onDecode(chunk)) }
122
+ catch (e) { done = true; onFree?.(); throw e }
123
+ }
124
+ if (done) return EMPTY
125
+ done = true
126
+ try {
127
+ let result = onFlush ? norm(await onFlush()) : EMPTY
128
+ onFree?.()
129
+ return result
130
+ } catch (e) { onFree?.(); throw e }
131
+ },
132
+ free() {
133
+ if (done) return
134
+ done = true
135
+ onFree?.()
79
136
  }
80
- return buf && createBuffer(await decode(buf))
81
137
  }
82
138
  }
83
139
 
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
140
+ // extract { channelData, sampleRate } from codec result
141
+ function norm(r) {
142
+ if (!r?.channelData?.length) return EMPTY
143
+ return { channelData: r.channelData, sampleRate: r.sampleRate }
144
+ }
145
+
146
+ // merge two decode results
147
+ function merge(a, b) {
148
+ if (!b?.channelData?.length) return a
149
+ if (!a?.channelData?.length) return b
150
+ return {
151
+ channelData: a.channelData.map((ch, i) => {
152
+ let merged = new Float32Array(ch.length + b.channelData[i].length)
153
+ merged.set(ch)
154
+ merged.set(b.channelData[i], ch.length)
155
+ return merged
156
+ }),
157
+ sampleRate: a.sampleRate
158
+ }
92
159
  }
Binary file
package/package.json CHANGED
@@ -1,26 +1,28 @@
1
1
  {
2
2
  "name": "audio-decode",
3
- "version": "2.2.2",
3
+ "version": "4.0.0",
4
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
5
  "type": "module",
6
+ "main": "audio-decode.js",
9
7
  "types": "audio-decode.d.ts",
8
+ "exports": {
9
+ ".": "./audio-decode.js",
10
+ "./package.json": "./package.json"
11
+ },
10
12
  "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",
13
+ "@wasm-audio-decoders/flac": "^0.2.10",
14
+ "@wasm-audio-decoders/ogg-vorbis": "^0.1.20",
15
+ "aac-decode": "^1.0.0",
16
+ "audio-type": "^2.4.0",
17
+ "mpg123-decoder": "^1.0.3",
16
18
  "node-wav": "^0.0.2",
17
- "ogg-opus-decoder": "^1.6.12",
19
+ "ogg-opus-decoder": "^1.7.3",
18
20
  "qoa-format": "^1.0.1"
19
21
  },
20
22
  "devDependencies": {
21
23
  "audio-lena": "^2.3.0",
22
24
  "base64-arraybuffer": "^1.0.2",
23
- "tst": "^7.3.0"
25
+ "tst": "^9.2.2"
24
26
  },
25
27
  "scripts": {
26
28
  "test": "node test.js"
@@ -30,20 +32,21 @@
30
32
  "url": "git+https://github.com/audiojs/audio-decode.git"
31
33
  },
32
34
  "keywords": [
33
- "audiojs",
34
35
  "audio",
35
- "dsp",
36
36
  "decode",
37
- "audio decode",
38
- "audio decoder",
39
- "web audio decoder",
37
+ "decoder",
40
38
  "codec",
41
39
  "mp3",
42
40
  "wav",
43
41
  "ogg",
44
42
  "vorbis",
45
43
  "opus",
46
- "web-audio"
44
+ "flac",
45
+ "aac",
46
+ "m4a",
47
+ "qoa",
48
+ "pcm",
49
+ "streaming"
47
50
  ],
48
51
  "author": "ΔY <dfcreative@gmail.com>",
49
52
  "license": "MIT",
package/readme.md CHANGED
@@ -1,49 +1,80 @@
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)
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)
2
2
 
3
- Decode audio data from supported format to [AudioBuffer](https://github.com/audiojs/audio-buffer).
3
+ Decode audio data in node or browser. Returns `{ channelData, sampleRate }`, where `channelData` is an array of `Float32Array` PCM channels.
4
4
 
5
- Supported formats:
5
+ ESM-only package:
6
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)
7
+ ```js
8
+ import decode from 'audio-decode';
9
+ ```
10
+
11
+ Supported formats: `wav`, `mp3`, `ogg vorbis`, `flac`, `opus`, `m4a`/`aac`, [`qoa`](https://github.com/phoboslab/qoa).
16
12
 
17
13
  [![npm install audio-decode](https://nodei.co/npm/audio-decode.png?mini=true)](https://npmjs.org/package/audio-decode/)
18
14
 
15
+ ### Whole-file decode
16
+
17
+ Auto-detects format. Input can be _ArrayBuffer_, _Uint8Array_, or _Buffer_.
18
+
19
19
  ```js
20
- import decodeAudio from 'audio-decode';
21
- import buffer from 'audio-lena/mp3';
20
+ import decode from 'audio-decode';
22
21
 
23
- let audioBuffer = await decode(buffer);
22
+ const { channelData, sampleRate } = await decode(mp3buf);
24
23
  ```
25
24
 
26
- `buffer` type can be: _ArrayBuffer_, _Uint8Array_ or _Buffer_.
25
+ ### Chunked decoding
26
+
27
+ Use `decoders` for chunk-by-chunk decoding when you already know the codec.
27
28
 
28
- Decoder's code is lazy: first run loads decoder's sources and compiles module before decoding.
29
+ ```js
30
+ import { decoders } from 'audio-decode';
31
+
32
+ const decoder = await decoders.mp3();
33
+ const a = await decoder.decode(chunk1); // { channelData, sampleRate }
34
+ const b = await decoder.decode(chunk2);
35
+ const c = await decoder.decode(); // flush + free
36
+ ```
29
37
 
30
- To get more granular control over individual decoders, use `decoders`:
38
+ Call `.free()` to release resources without flushing:
31
39
 
32
40
  ```js
33
- import decode, {decoders} from 'audio-decode';
41
+ decoder.free();
42
+ ```
43
+
44
+ ### Stream decoding
45
+
46
+ Decode a `ReadableStream` or async iterable when you already know the codec:
34
47
 
35
- await decoders.mp3(); // load & compile decoder
36
- const audioBuffer = await decoders.mp3(mp3buf); // decode
48
+ ```js
49
+ import { decodeStream } from 'audio-decode';
50
+
51
+ for await (const { channelData, sampleRate } of decodeStream(stream, 'mp3')) {
52
+ // process each decoded chunk
53
+ }
54
+ ```
55
+
56
+ Available stream codec keys: `mp3`, `flac`, `opus`, `oga`, `m4a`, `wav`, `qoa`.
57
+
58
+ AAC-in-M4A/MP4 stream decoding is available through `decoders.m4a()` and `decodeStream(stream, 'm4a')`.
59
+
60
+ There is no separate `decoders.aac()` or `decodeStream(stream, 'aac')` alias.
61
+
62
+ ### Custom decoders
63
+
64
+ The `decoders` registry is extensible:
65
+
66
+ ```js
67
+ import { decoders } from 'audio-decode';
68
+ decoders.myformat = async () => ({ decode: chunk => ..., free() {} });
37
69
  ```
38
70
 
39
71
  ## See also
40
72
 
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).
73
+ * [wasm-audio-decoders](https://github.com/eshaz/wasm-audio-decoders) – compact & fast WASM audio decoders.
74
+ * [AudioDecoder](https://developer.mozilla.org/en-US/docs/Web/API/AudioDecoder) – native WebCodecs decoder API.
75
+ * [decodeAudioData](https://developer.mozilla.org/en-US/docs/Web/API/BaseAudioContext/decodeAudioData) – built-in browser decoding method.
76
+ * [ffmpeg.wasm](https://github.com/ffmpegwasm/ffmpeg.wasm) – full encoding/decoding library.
45
77
 
46
78
  ## License
47
79
 
48
80
  [MIT](LICENSE)&nbsp;&nbsp;•&nbsp;&nbsp;<a href="https://github.com/krishnized/license/">🕉</a>
49
-
package/test.js CHANGED
@@ -1,107 +1,293 @@
1
-
2
- import decodeAudio, { decoders } from './audio-decode.js';
1
+ import decode, { decoders, decodeStream } from './audio-decode.js';
3
2
  import wav from 'audio-lena/wav.js';
4
3
  import mp3 from 'audio-lena/mp3.js';
5
4
  import ogg from 'audio-lena/ogg.js';
6
5
  import flac from 'audio-lena/flac.js';
7
6
  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+MTHu8IIFFEEkokhACiSCCQQCCSAIIJRIJJJBAEkkEEkkEEkgCCEECCEECCAJIIBIQooJAEUUgggEIEAACCKaKABABRAAgAIggAAEEAAGqmmmGCAQBBABDTTRDBAEAhpppqppoGCCAAwQCCSQABDBBBDBBBAMEEEMEEEMMA0ww00ww00wDDTTDIaaqq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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAu4AOBwW4AAEAAQABAAH0shPQ5kAgKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKaIBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU0QCAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAACigKKKKKKKKKKAopxoggAAAEE0QGmiA00QAIIAAAKaIAAAAAFNEAppooCCAAACAACmgaIAIKKIBBAAAgAAgEAoooCiiiAAACmiAIBTRBAAABBBFFEAAAEAAEE40QAQUUUUQRRTjRAAAKaAgFNEApogEAAAUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACmiAAIAAIBAKIBTBNNNNNNNNNME000000000wRBBAaaaaIIAggggggAFNEAAQQACCnGiAAAAAKaIAKaAgAAAAApogAAAAAAAAAU0QAABTRAIAAKAgAAAgoopogAQQCmiCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU0QAU0QBBAaaaaIBBAAgggggNNNME000000000wTTTTTRBBAKBpppogAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==`)
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')
7
+ import t, { is } from 'tst';
8
+ import { readFileSync } from 'fs';
9
+
10
+ const qoa = readFileSync(new URL('./fixtures/qoa-sample.qoa', import.meta.url))
11
+ const m4a = readFileSync(new URL('../audio-lena/lena.m4a', import.meta.url))
12
+
13
+ const dur = r => r.channelData[0].length / r.sampleRate
14
+ const rms = f32 => { let s = 0; for (let i = 0; i < f32.length; i++) s += f32[i] * f32[i]; return Math.sqrt(s / f32.length) }
15
+ const near = (a, b, tol = 0.02) => Math.abs(a - b) < tol
16
+
17
+ // -- whole-file decode with content verification --
18
+
19
+ t('wav', async () => {
20
+ let r = await decode(wav)
21
+ is(r.channelData.length, 1)
22
+ is(r.sampleRate, 44100)
23
+ is(near(dur(r), 12.27), true, 'duration')
24
+ is(near(rms(r.channelData[0]), 0.13, 0.01), true, 'rms')
25
+ })
26
+
27
+ t('mp3', async () => {
28
+ let r = await decode(mp3)
29
+ is(r.channelData.length, 2)
30
+ is(r.sampleRate, 44100)
31
+ is(near(dur(r), 12.27), true, 'duration')
32
+ is(near(rms(r.channelData[0]), 0.13, 0.01), true, 'rms')
33
+ })
34
+
35
+ t('ogg vorbis', async () => {
36
+ let r = await decode(ogg)
37
+ is(r.sampleRate, 44100)
38
+ is(near(dur(r), 12.27), true, 'duration')
39
+ is(near(rms(r.channelData[0]), 0.13, 0.01), true, 'rms')
40
+ })
41
+
42
+ t('flac', async () => {
43
+ let r = await decode(flac)
44
+ is(r.sampleRate, 44100)
45
+ is(near(dur(r), 12.27), true, 'duration')
46
+ // flac is lossless, must match wav exactly
47
+ is(near(rms(r.channelData[0]), 0.1298, 0.001), true, 'rms lossless')
48
+ })
49
+
50
+ t('opus', async () => {
51
+ let r = await decode(opus)
52
+ is(r.sampleRate, 48000)
53
+ is(near(dur(r), 12.27), true, 'duration')
54
+ is(near(rms(r.channelData[0]), 0.12, 0.02), true, 'rms')
55
+ })
56
+
57
+ t('m4a', async () => {
58
+ let r = await decode(m4a)
59
+ is(r.channelData.length, 2)
60
+ is(r.sampleRate, 44100)
61
+ is(near(dur(r), 12.27), true, 'duration')
62
+ is(rms(r.channelData[0]) > 0.05, true, 'has audio content')
63
+ })
64
+
65
+ t('qoa', async () => {
66
+ let r = await decode(qoa)
67
+ is(near(dur(r), 0.82, 0.05), true, 'duration')
68
+ is(r.channelData.length >= 1, true, 'channels')
69
+ })
70
+
71
+ t('uint8array input', async () => {
72
+ let r = await decode(new Uint8Array(wav))
73
+ is(near(dur(r), 12.27), true)
74
+ })
75
+
76
+ t('buffer input', async () => {
77
+ let r = await decode(Buffer.from(wav))
78
+ is(near(dur(r), 12.27), true)
79
+ })
80
+
81
+ // -- streaming via decoders --
82
+
83
+ t('stream mp3', async () => {
84
+ let dec = await decoders.mp3()
85
+ let r = await dec.decode(new Uint8Array(mp3))
86
+ is(r.channelData.length > 0, true)
87
+ is(r.channelData[0].length > 0, true)
88
+ is(r.sampleRate, 44100)
89
+ await dec.decode()
90
+ })
91
+
92
+ t('stream wav', async () => {
93
+ let dec = await decoders.wav()
94
+ let r = await dec.decode(new Uint8Array(wav))
95
+ is(r.channelData[0].length > 0, true)
96
+ is(r.sampleRate, 44100)
97
+ })
98
+
99
+ t('stream flac', async () => {
100
+ let dec = await decoders.flac()
101
+ let r = await dec.decode(new Uint8Array(flac))
102
+ is(r.channelData[0].length > 0, true)
103
+ await dec.decode()
104
+ })
105
+
106
+ t('stream opus', async () => {
107
+ let dec = await decoders.opus()
108
+ let r = await dec.decode(new Uint8Array(opus))
109
+ is(r.channelData[0].length > 0, true)
110
+ await dec.decode()
111
+ })
112
+
113
+ t('stream oga', async () => {
114
+ let dec = await decoders.oga()
115
+ let r = await dec.decode(new Uint8Array(ogg))
116
+ is(r.channelData[0].length > 0, true)
117
+ await dec.decode()
118
+ })
119
+
120
+ t('stream m4a', async () => {
121
+ let dec = await decoders.m4a()
122
+ let r = await dec.decode(new Uint8Array(m4a))
123
+ is(r.channelData.length > 0, true)
124
+ is(r.channelData[0].length > 0, true)
125
+ is(r.sampleRate, 44100)
126
+ await dec.decode()
127
+ })
128
+
129
+ // -- flush / free lifecycle --
130
+
131
+ t('double flush', async () => {
132
+ let dec = await decoders.mp3()
133
+ await dec.decode(new Uint8Array(mp3))
134
+ await dec.decode()
135
+ let r = await dec.decode()
136
+ is(r.channelData.length, 0)
137
+ is(r.sampleRate, 0)
138
+ })
139
+
140
+ t('free without flush', async () => {
141
+ let dec = await decoders.mp3()
142
+ await dec.decode(new Uint8Array(mp3))
143
+ dec.free()
144
+ let r = await dec.decode()
145
+ is(r.channelData.length, 0)
146
+ })
147
+
148
+ t('decode after free throws', async () => {
149
+ let dec = await decoders.mp3()
150
+ dec.free()
151
+ let threw = false
152
+ try { await dec.decode(new Uint8Array(mp3)) } catch { threw = true }
153
+ is(threw, true)
154
+ })
155
+
156
+ t('double free is safe', async () => {
157
+ let dec = await decoders.flac()
158
+ dec.free()
159
+ dec.free()
160
+ })
161
+
162
+ // -- EMPTY immutability --
163
+
164
+ t('empty result is immutable', async () => {
165
+ let dec = await decoders.mp3()
166
+ await dec.decode()
167
+ let r = await dec.decode()
168
+ let threw = false
169
+ try { r.sampleRate = 999 } catch { threw = true }
170
+ is(threw, true)
171
+ try { r.channelData.push(new Float32Array(1)) } catch { threw = true }
172
+ is(threw, true)
173
+ })
174
+
175
+ // -- input validation --
176
+
177
+ t('rejects string', async () => {
178
+ let threw = false
179
+ try { await decode('hello') } catch (e) { threw = e instanceof TypeError }
180
+ is(threw, true)
181
+ })
182
+
183
+ t('rejects null', async () => {
184
+ let threw = false
185
+ try { await decode(null) } catch { threw = true }
186
+ is(threw, true)
187
+ })
188
+
189
+ t('rejects number', async () => {
190
+ let threw = false
191
+ try { await decode(42) } catch { threw = true }
192
+ is(threw, true)
193
+ })
194
+
195
+ t('rejects non-audio buffer', async () => {
196
+ let threw = false
197
+ try { await decode(new Uint8Array(100)) } catch (e) { threw = e.message.includes('Unknown audio') }
198
+ is(threw, true)
199
+ })
200
+
201
+ // -- concurrent decoders --
202
+
203
+ t('concurrent decoding', async () => {
204
+ let [r1, r2, r3] = await Promise.all([
205
+ decode(mp3),
206
+ decode(flac),
207
+ decode(ogg),
208
+ ])
209
+ is(near(dur(r1), 12.27), true, 'mp3 concurrent')
210
+ is(near(dur(r2), 12.27), true, 'flac concurrent')
211
+ is(near(dur(r3), 12.27), true, 'ogg concurrent')
212
+ })
213
+
214
+ t('concurrent stream decoders', async () => {
215
+ let [d1, d2] = await Promise.all([decoders.mp3(), decoders.flac()])
216
+ let [r1, r2] = await Promise.all([
217
+ d1.decode(new Uint8Array(mp3)),
218
+ d2.decode(new Uint8Array(flac)),
219
+ ])
220
+ is(r1.channelData[0].length > 0, true)
221
+ is(r2.channelData[0].length > 0, true)
222
+ await Promise.all([d1.decode(), d2.decode()])
223
+ })
224
+
225
+ // -- zero-length / edge cases --
226
+
227
+ t('zero-length buffer', async () => {
228
+ let threw = false
229
+ try { await decode(new ArrayBuffer(0)) } catch { threw = true }
230
+ is(threw, true)
231
+ })
232
+
233
+ t('minimal invalid buffer', async () => {
234
+ let threw = false
235
+ try { await decode(new Uint8Array([0])) } catch { threw = true }
236
+ is(threw, true)
237
+ })
238
+
239
+ // -- decodeStream --
240
+
241
+ t('decodeStream mp3', async () => {
242
+ let chunks = [new Uint8Array(mp3)]
243
+ async function* gen() { for (let c of chunks) yield c }
244
+ let total = 0
245
+ for await (let r of decodeStream(gen(), 'mp3')) {
246
+ is(r.sampleRate > 0, true)
247
+ total += r.channelData[0].length
248
+ }
249
+ is(total > 0, true, 'decoded samples')
250
+ })
251
+
252
+ t('decodeStream ReadableStream', async () => {
253
+ let data = new Uint8Array(wav)
254
+ let stream = new ReadableStream({
255
+ start(ctrl) { ctrl.enqueue(data); ctrl.close() }
256
+ })
257
+ let total = 0
258
+ for await (let r of decodeStream(stream, 'wav')) {
259
+ is(r.sampleRate, 44100)
260
+ total += r.channelData[0].length
261
+ }
262
+ is(total > 0, true)
263
+ })
264
+
265
+ t('decodeStream m4a', async () => {
266
+ async function* gen() { yield new Uint8Array(m4a) }
267
+ let total = 0
268
+ for await (let r of decodeStream(gen(), 'm4a')) {
269
+ is(r.sampleRate, 44100)
270
+ total += r.channelData[0].length
271
+ }
272
+ is(total > 0, true)
273
+ })
274
+
275
+ t('decodeStream unknown format', async () => {
276
+ let threw = false
277
+ try { for await (let _ of decodeStream([], 'xyz')) {} } catch { threw = true }
278
+ is(threw, true)
279
+ })
280
+
281
+ // -- decoders extensibility --
282
+
283
+ t('custom decoder registration', async () => {
284
+ decoders.test = async () => ({
285
+ decode: async (chunk) => ({ channelData: [new Float32Array(chunk.length)], sampleRate: 8000 }),
286
+ free() {}
287
+ })
288
+ let dec = await decoders.test()
289
+ let r = await dec.decode(new Uint8Array(10))
290
+ is(r.sampleRate, 8000)
291
+ is(r.channelData[0].length, 10)
292
+ delete decoders.test
107
293
  })
package/.eslintrc.json DELETED
@@ -1,43 +0,0 @@
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
- }
package/.travis.yml DELETED
@@ -1,13 +0,0 @@
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
-