@audio/encode-wavpack 1.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.
- package/LICENSE +29 -0
- package/LICENSE.wavpack +25 -0
- package/README.md +49 -0
- package/audio.d.ts +15 -0
- package/audio.js +19 -0
- package/package.json +63 -0
- package/src/wavpack.wasm.js +0 -0
- package/wavpack-encode.d.ts +36 -0
- package/wavpack-encode.js +153 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, audiojs
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice,
|
|
9
|
+
this list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
22
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
23
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
24
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
25
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
26
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
27
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
28
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
29
|
+
POSSIBILITY OF SUCH DAMAGE.
|
package/LICENSE.wavpack
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Copyright (c) 1998 - 2025 David Bryant
|
|
2
|
+
All rights reserved.
|
|
3
|
+
|
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
|
6
|
+
|
|
7
|
+
* Redistributions of source code must retain the above copyright notice,
|
|
8
|
+
this list of conditions and the following disclaimer.
|
|
9
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
|
10
|
+
this list of conditions and the following disclaimer in the
|
|
11
|
+
documentation and/or other materials provided with the distribution.
|
|
12
|
+
* Neither the name of the copyright holder nor the names of its
|
|
13
|
+
contributors may be used to endorse or promote products derived from
|
|
14
|
+
this software without specific prior written permission.
|
|
15
|
+
|
|
16
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
17
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
18
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
19
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
|
|
20
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
21
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
22
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
23
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
24
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
25
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# @audio/encode-wavpack [](https://www.npmjs.com/package/@audio/encode-wavpack) [](https://github.com/krishnized/license)
|
|
2
|
+
|
|
3
|
+
Encode PCM audio samples to WavPack (.wv)
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
npm install @audio/encode-wavpack
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
import wavpack from '@audio/encode-wavpack'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
[libwavpack](https://github.com/dbry/WavPack) (the reference WavPack 5 implementation, David Bryant) compiled to a single-file WASM ES module, encode-only (no file-input/decode side, no DSD — a leaner build than the full library). Lossless by default; hybrid/lossy on request. Block boundaries are decided purely by cumulative sample count, so output is byte-identical no matter how the input is chunked across `encode()` calls.
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
let enc = await wavpack({ sampleRate: 48000, channels: 2, bitDepth: 16 })
|
|
17
|
+
let chunk = enc.encode(channelData) // → Uint8Array (any WavPack blocks this chunk completed)
|
|
18
|
+
let tail = enc.flush() // → Uint8Array (final block + APEv2 tag; frees the encoder)
|
|
19
|
+
// concatenate chunk + tail for the complete .wv file
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
| Option | Default | |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| `sampleRate` | — | Required |
|
|
25
|
+
| `channels` | `1` | |
|
|
26
|
+
| `bitDepth` | `16` | `16`, `24`, `32` (integer) or `'float'` (32-bit IEEE-754) |
|
|
27
|
+
| `hybrid` | `false` | Bitrate-per-sample (< 24, e.g. `4` ≈ 4 bits/sample) or kbps (≥ 24) — enables lossy hybrid mode. No `.wvc` correction file. |
|
|
28
|
+
| `extraProcessing` | `0` | `0`-`6`, libwavpack's extra-processing level (`xmode`) |
|
|
29
|
+
| `highQuality` | `false` | |
|
|
30
|
+
| `veryHigh` | `false` | Implies `highQuality` |
|
|
31
|
+
| `fast` | `false` | |
|
|
32
|
+
| `blockSamples` | library default | Fixed WavPack block size, 16-131072 samples |
|
|
33
|
+
| `meta` | — | APEv2 tags: `title`, `artist`, `album`, `albumartist`, `composer`, `genre`, `year`, `track`, `disc`, `comment`, `copyright`, `isrc`, `publisher`, `software`, `lyrics`, `pictures: [{mime, description, data}]` (written as `Cover Art (Front)`) |
|
|
34
|
+
|
|
35
|
+
### Streaming
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
let enc = await wavpack({ sampleRate: 44100, channels: 1, bitDepth: 16 })
|
|
39
|
+
let a = enc.encode(chunk1) // → Uint8Array (blocks completed so far, maybe empty)
|
|
40
|
+
let b = enc.encode(chunk2)
|
|
41
|
+
let c = enc.flush() // → Uint8Array (final block + tag)
|
|
42
|
+
// complete file = concat(a, b, c)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`encode()` returns whatever WavPack blocks were completed by that call — often nothing, since a block only closes once it has enough samples (`blockSamples`, or a size the library picks from the sample rate). `flush()` finalizes the last partial block, writes the APEv2 tag if `meta` was given, and frees the encoder — it must be the last call.
|
|
46
|
+
|
|
47
|
+
## License
|
|
48
|
+
|
|
49
|
+
[ॐ](https://github.com/krishnized/license/) · [BSD-3-Clause](./LICENSE), inherited from the bundled [libwavpack](./LICENSE.wavpack).
|
package/audio.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Generated from the audio.js manifest (params metadata is the source of truth).
|
|
2
|
+
// Regenerate: node tools/dts.js in @audio/compile. Do not edit by hand.
|
|
3
|
+
|
|
4
|
+
/** Automatable number — scalar, `t => value` fn, or breakpoint curve {t, v} */
|
|
5
|
+
type Auto = number | ((t: number) => number) | { t: number[], v: number[] }
|
|
6
|
+
/** Per-block param values as delivered by hosts (numbers arrive as 1-length Float32Array) */
|
|
7
|
+
type Live = Record<string, Float32Array | string | boolean>
|
|
8
|
+
type Ctx = { sampleRate: number, maxBlockSize: number, maxChannels: number, currentTime: number, duration?: number, events?: readonly any[], emit?: (name: string, ...args: any[]) => void, [k: string]: unknown }
|
|
9
|
+
type Process = (inputs: Float32Array[][], outputs: Float32Array[][], params: Live) => void
|
|
10
|
+
|
|
11
|
+
/** Codec plugin 'wv' — extends audio()'s openable formats / save() targets */
|
|
12
|
+
export declare const wv: {
|
|
13
|
+
codec: 'wv'
|
|
14
|
+
encode(opts: { sampleRate: number, channels?: number }): ((chunk?: Float32Array[]) => Uint8Array) | Promise<(chunk?: Float32Array[]) => Uint8Array>
|
|
15
|
+
}
|
package/audio.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// audio.js manifest — codec atom, encode half: opts → callable encoder
|
|
2
|
+
// (enc(chunk) → bytes, enc() → flush+free), adapting the package's
|
|
3
|
+
// { encode, flush, free } streaming shape. Hosts merge with the decode half
|
|
4
|
+
// (@audio/decode-wavpack) by format name.
|
|
5
|
+
|
|
6
|
+
import init from './wavpack-encode.js'
|
|
7
|
+
|
|
8
|
+
export const wv = {
|
|
9
|
+
codec: 'wv',
|
|
10
|
+
encode: async (opts) => {
|
|
11
|
+
const c = await init(opts)
|
|
12
|
+
return (chunk) => {
|
|
13
|
+
if (chunk) return c.encode(chunk)
|
|
14
|
+
const out = c.flush()
|
|
15
|
+
c.free?.()
|
|
16
|
+
return out
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@audio/encode-wavpack",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Encode PCM audio samples to WavPack (.wv) with libwavpack WASM",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "wavpack-encode.js",
|
|
7
|
+
"types": "wavpack-encode.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./wavpack-encode.js",
|
|
10
|
+
"./audio": {
|
|
11
|
+
"types": "./audio.d.ts",
|
|
12
|
+
"default": "./audio.js"
|
|
13
|
+
},
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"wavpack-encode.js",
|
|
18
|
+
"wavpack-encode.d.ts",
|
|
19
|
+
"src/wavpack.wasm.js",
|
|
20
|
+
"LICENSE",
|
|
21
|
+
"LICENSE.wavpack",
|
|
22
|
+
"audio.js",
|
|
23
|
+
"audio.d.ts"
|
|
24
|
+
],
|
|
25
|
+
"keywords": [
|
|
26
|
+
"wavpack",
|
|
27
|
+
"wv",
|
|
28
|
+
"hybrid",
|
|
29
|
+
"lossless",
|
|
30
|
+
"audio",
|
|
31
|
+
"encode",
|
|
32
|
+
"encoder",
|
|
33
|
+
"wasm",
|
|
34
|
+
"libwavpack"
|
|
35
|
+
],
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"license": "BSD-3-Clause",
|
|
40
|
+
"author": "audiojs",
|
|
41
|
+
"homepage": "https://github.com/audiojs/encode/tree/main/packages/encode-wavpack",
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "git+https://github.com/audiojs/encode.git",
|
|
45
|
+
"directory": "packages/encode-wavpack"
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=18"
|
|
49
|
+
},
|
|
50
|
+
"bugs": "https://github.com/audiojs/encode/issues",
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "bash build.sh",
|
|
53
|
+
"prepack": "npm run build",
|
|
54
|
+
"test": "node test.js"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"tst": "^9.4.0",
|
|
58
|
+
"@audio/decode-wavpack": "^1.0.0"
|
|
59
|
+
},
|
|
60
|
+
"audio": "./audio.js",
|
|
61
|
+
"sideEffects": false,
|
|
62
|
+
"funding": "https://github.com/sponsors/audiojs"
|
|
63
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface WavpackMeta {
|
|
2
|
+
title?: string; artist?: string; album?: string; albumartist?: string; composer?: string;
|
|
3
|
+
genre?: string; year?: string | number; track?: string | number; disc?: string | number;
|
|
4
|
+
comment?: string; copyright?: string; isrc?: string; publisher?: string; software?: string;
|
|
5
|
+
lyrics?: string;
|
|
6
|
+
pictures?: { mime?: string; description?: string; data: Uint8Array }[];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface WavpackEncodeOptions {
|
|
10
|
+
sampleRate: number;
|
|
11
|
+
channels?: number;
|
|
12
|
+
/** 16, 24, 32-bit integer, or 'float' (32-bit IEEE-754). Default 16. */
|
|
13
|
+
bitDepth?: 16 | 24 | 32 | 'float';
|
|
14
|
+
/** Lossy hybrid mode: bitrate-per-sample (< 24, e.g. 4 = ~4 bits/sample) or kbps (>= 24). No .wvc correction file. */
|
|
15
|
+
hybrid?: false | number;
|
|
16
|
+
/** CONFIG_EXTRA_MODE level, 0-6. */
|
|
17
|
+
extraProcessing?: number;
|
|
18
|
+
/** CONFIG_HIGH_FLAG */
|
|
19
|
+
highQuality?: boolean;
|
|
20
|
+
/** CONFIG_VERY_HIGH_FLAG */
|
|
21
|
+
veryHigh?: boolean;
|
|
22
|
+
/** CONFIG_FAST_FLAG */
|
|
23
|
+
fast?: boolean;
|
|
24
|
+
/** Fixed WavPack block size in samples (16-131072). Library default if omitted. */
|
|
25
|
+
blockSamples?: number;
|
|
26
|
+
/** APEv2 tags. */
|
|
27
|
+
meta?: WavpackMeta;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface StreamEncoder {
|
|
31
|
+
encode(channels: Float32Array[]): Uint8Array;
|
|
32
|
+
flush(): Uint8Array;
|
|
33
|
+
free(): void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default function wavpack(opts: WavpackEncodeOptions): Promise<StreamEncoder>;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WavPack encoder — libwavpack compiled to WASM (single-file module, host-neutral), encode-only
|
|
3
|
+
* build (no file-input/decode side, no DSD).
|
|
4
|
+
*
|
|
5
|
+
* @param {Object} opts
|
|
6
|
+
* @param {number} opts.sampleRate - required
|
|
7
|
+
* @param {number} [opts.channels=1]
|
|
8
|
+
* @param {16|24|32|'float'} [opts.bitDepth=16]
|
|
9
|
+
* @param {false|number} [opts.hybrid=false] - bitrate-per-sample (< 24, e.g. 4 = ~4 bits/sample)
|
|
10
|
+
* or kbps (>= 24); enables lossy hybrid mode (CONFIG_HYBRID_FLAG). No .wvc correction file.
|
|
11
|
+
* @param {number} [opts.extraProcessing=0] - 0-6, CONFIG_EXTRA_MODE level (xmode)
|
|
12
|
+
* @param {boolean} [opts.highQuality] - CONFIG_HIGH_FLAG
|
|
13
|
+
* @param {boolean} [opts.veryHigh] - CONFIG_VERY_HIGH_FLAG (implies highQuality)
|
|
14
|
+
* @param {boolean} [opts.fast] - CONFIG_FAST_FLAG
|
|
15
|
+
* @param {number} [opts.blockSamples] - fixed WavPack block size, 16-131072 samples (library default if omitted)
|
|
16
|
+
* @param {object} [opts.meta] - APEv2 tags: title, artist, album, albumartist, composer, genre,
|
|
17
|
+
* year, track, disc, comment, copyright, isrc, publisher, software, lyrics, pictures[]
|
|
18
|
+
* @returns {{ encode, flush, free }}
|
|
19
|
+
*
|
|
20
|
+
* encode(channels: Float32Array[]) -> Uint8Array (any WavPack blocks completed by this chunk)
|
|
21
|
+
* flush() -> Uint8Array (final partial block + APEv2 tag, if any; frees the encoder)
|
|
22
|
+
* free() -> void
|
|
23
|
+
*/
|
|
24
|
+
import createWavpackEncoder from './src/wavpack.wasm.js'
|
|
25
|
+
|
|
26
|
+
const BIT_DEPTHS = new Set([8, 16, 24, 32])
|
|
27
|
+
const APE_MAP = {
|
|
28
|
+
title: 'Title', artist: 'Artist', album: 'Album', albumartist: 'Album Artist',
|
|
29
|
+
composer: 'Composer', genre: 'Genre', year: 'Year', track: 'Track', disc: 'Disc',
|
|
30
|
+
comment: 'Comment', copyright: 'Copyright', isrc: 'ISRC', publisher: 'Publisher',
|
|
31
|
+
software: 'Encoder', lyrics: 'Lyrics',
|
|
32
|
+
}
|
|
33
|
+
const MIME_EXT = { 'image/jpeg': 'jpg', 'image/jpg': 'jpg', 'image/png': 'png', 'image/gif': 'gif', 'image/webp': 'webp' }
|
|
34
|
+
|
|
35
|
+
let modP
|
|
36
|
+
function getMod() {
|
|
37
|
+
if (modP) return modP
|
|
38
|
+
let p = createWavpackEncoder()
|
|
39
|
+
modP = p
|
|
40
|
+
return p.catch(e => { modP = null; throw e })
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default async function wavpack(opts = {}) {
|
|
44
|
+
let sampleRate = opts.sampleRate
|
|
45
|
+
if (!sampleRate) throw Error('sampleRate is required')
|
|
46
|
+
let channels = opts.channels || 1
|
|
47
|
+
let isFloat = opts.bitDepth === 'float'
|
|
48
|
+
let bitDepth = isFloat ? 32 : (opts.bitDepth || 16)
|
|
49
|
+
if (!isFloat && !BIT_DEPTHS.has(bitDepth)) throw Error('bitDepth must be 16, 24, 32 or \'float\'')
|
|
50
|
+
let xmode = opts.extraProcessing || 0
|
|
51
|
+
if (xmode < 0 || xmode > 6) throw Error('extraProcessing must be 0-6')
|
|
52
|
+
let quality = opts.veryHigh ? 2 : opts.highQuality ? 1 : 0
|
|
53
|
+
let hybrid = opts.hybrid ? 1 : 0
|
|
54
|
+
let bitrate = opts.hybrid || 0
|
|
55
|
+
|
|
56
|
+
let m = await getMod()
|
|
57
|
+
let h = m._we_create(sampleRate, channels, bitDepth, isFloat ? 1 : 0, hybrid, bitrate, xmode, quality, opts.fast ? 1 : 0, opts.blockSamples || 0)
|
|
58
|
+
if (!h || !m._we_ok(h)) throw Error(readError(m, h))
|
|
59
|
+
if (opts.meta) writeMeta(m, h, opts.meta)
|
|
60
|
+
|
|
61
|
+
let div = isFloat ? 0 : 2 ** (bitDepth - 1), max = div - 1
|
|
62
|
+
let freed = false
|
|
63
|
+
|
|
64
|
+
return { encode: encodeChunk, flush, free }
|
|
65
|
+
|
|
66
|
+
function encodeChunk(chData) {
|
|
67
|
+
if (freed) throw Error('Encoder already freed')
|
|
68
|
+
let n = chData[0] ? chData[0].length : 0
|
|
69
|
+
if (!n) return EMPTY
|
|
70
|
+
let dst = m._we_input(h, n)
|
|
71
|
+
if (!dst) throw Error(readError(m, h))
|
|
72
|
+
if (isFloat) {
|
|
73
|
+
let f32 = new Float32Array(m.HEAPU8.buffer, dst, n * channels)
|
|
74
|
+
for (let i = 0, k = 0; i < n; i++) for (let c = 0; c < channels; c++) f32[k++] = chData[c][i]
|
|
75
|
+
} else {
|
|
76
|
+
let i32 = m.HEAP32, k = dst >> 2
|
|
77
|
+
for (let i = 0; i < n; i++) for (let c = 0; c < channels; c++) {
|
|
78
|
+
let v = chData[c][i]
|
|
79
|
+
let s = v < 0 ? v * div : v * max
|
|
80
|
+
i32[k++] = s < -div ? -div : s > max ? max : Math.round(s)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (!m._we_pack(h, n)) throw Error(readError(m, h))
|
|
84
|
+
return drain()
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function flush() {
|
|
88
|
+
if (freed) throw Error('Encoder already freed')
|
|
89
|
+
if (!m._we_flush(h)) throw Error(readError(m, h))
|
|
90
|
+
let out = drain()
|
|
91
|
+
free()
|
|
92
|
+
return out
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function free() {
|
|
96
|
+
if (freed) return
|
|
97
|
+
freed = true
|
|
98
|
+
m._we_destroy(h)
|
|
99
|
+
h = 0
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function drain() {
|
|
103
|
+
let len = m._we_output_len(h)
|
|
104
|
+
if (!len) return EMPTY
|
|
105
|
+
let ptr = m._we_output_ptr(h)
|
|
106
|
+
let out = m.HEAPU8.slice(ptr, ptr + len)
|
|
107
|
+
m._we_output_reset(h)
|
|
108
|
+
return out
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const EMPTY = new Uint8Array(0)
|
|
113
|
+
const TE = new TextEncoder()
|
|
114
|
+
|
|
115
|
+
function writeMeta(m, h, meta) {
|
|
116
|
+
for (let k in APE_MAP) {
|
|
117
|
+
let v = meta[k]
|
|
118
|
+
if (v == null || v === '') continue
|
|
119
|
+
let bytes = TE.encode(String(v))
|
|
120
|
+
let ok = withCStr(m, APE_MAP[k], item => withBytes(m, bytes, val => m._we_tag_text(h, item, val, bytes.length)))
|
|
121
|
+
if (!ok) throw Error(readError(m, h))
|
|
122
|
+
}
|
|
123
|
+
if (meta.pictures) for (let p of meta.pictures) {
|
|
124
|
+
let name = TE.encode((p.description || 'cover') + '.' + (MIME_EXT[p.mime] || 'bin') + '\0')
|
|
125
|
+
let value = new Uint8Array(name.length + p.data.length)
|
|
126
|
+
value.set(name); value.set(p.data, name.length)
|
|
127
|
+
let ok = withCStr(m, 'Cover Art (Front)', item => withBytes(m, value, val => m._we_tag_binary(h, item, val, value.length)))
|
|
128
|
+
if (!ok) throw Error(readError(m, h))
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Copy `bytes` to a scratch WASM allocation, run `fn(ptr)`, free it, return fn's result.
|
|
133
|
+
function withBytes(m, bytes, fn) {
|
|
134
|
+
let ptr = m._malloc(bytes.length || 1)
|
|
135
|
+
m.HEAPU8.set(bytes, ptr)
|
|
136
|
+
try { return fn(ptr) } finally { m._free(ptr) }
|
|
137
|
+
}
|
|
138
|
+
// Same, but NUL-terminated (for char* item-name arguments).
|
|
139
|
+
function withCStr(m, str, fn) {
|
|
140
|
+
let bytes = TE.encode(str)
|
|
141
|
+
let ptr = m._malloc(bytes.length + 1)
|
|
142
|
+
m.HEAPU8.set(bytes, ptr)
|
|
143
|
+
m.HEAPU8[ptr + bytes.length] = 0
|
|
144
|
+
try { return fn(ptr) } finally { m._free(ptr) }
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function readError(m, h) {
|
|
148
|
+
if (!h) return 'WavPack: encoder allocation failed'
|
|
149
|
+
let ptr = m._we_error(h)
|
|
150
|
+
let bytes = m.HEAPU8.subarray(ptr, ptr + 128)
|
|
151
|
+
let nul = bytes.indexOf(0)
|
|
152
|
+
return new TextDecoder().decode(bytes.subarray(0, nul < 0 ? 128 : nul)) || 'WavPack: encoder error'
|
|
153
|
+
}
|