@tongue-core/engine 0.1.0-rc.5 → 0.1.0-rc.6
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/dist/assets/codecs/wav.d.ts +3 -0
- package/dist/assets/codecs/wav.js +26 -6
- package/dist/audio/mix.d.ts +7 -1
- package/dist/audio/mix.js +12 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/package.json +2 -2
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
import type { PcmAudio } from "./types.js";
|
|
2
2
|
export declare function readWav(bytes: Uint8Array): PcmAudio;
|
|
3
|
+
/** 32-bit float WAV: every sample kept exactly as it is, with no dither and no clipping. For audio
|
|
4
|
+
* between steps (a stem on its way to a processor), where 16 bits would round it each time. */
|
|
5
|
+
export declare function writeFloatWav(audio: PcmAudio): Uint8Array;
|
|
3
6
|
export declare function writeWav(audio: PcmAudio, seed?: number): Uint8Array;
|
|
@@ -96,7 +96,9 @@ function random(seed) {
|
|
|
96
96
|
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
97
97
|
};
|
|
98
98
|
}
|
|
99
|
-
|
|
99
|
+
/** A WAV header for `frames` of `channels` samples of `bits` each, in the given format; the
|
|
100
|
+
* samples go after it, at byte 44. */
|
|
101
|
+
function header(audio, format, bits) {
|
|
100
102
|
const channels = audio.channels.length;
|
|
101
103
|
const frames = audio.channels[0]?.length ?? 0;
|
|
102
104
|
if (channels === 0)
|
|
@@ -105,21 +107,39 @@ export function writeWav(audio, seed = 0x5eed) {
|
|
|
105
107
|
fail("channels have different lengths");
|
|
106
108
|
if (!Number.isInteger(audio.sampleRate) || audio.sampleRate <= 0)
|
|
107
109
|
fail(`bad sample rate ${audio.sampleRate}`);
|
|
108
|
-
const
|
|
110
|
+
const bytes = bits / 8;
|
|
111
|
+
const dataLength = frames * channels * bytes;
|
|
109
112
|
const out = new Uint8Array(44 + dataLength);
|
|
110
113
|
const view = new DataView(out.buffer);
|
|
111
114
|
out.set([0x52, 0x49, 0x46, 0x46]); // RIFF
|
|
112
115
|
view.setUint32(4, 36 + dataLength, true);
|
|
113
116
|
out.set([0x57, 0x41, 0x56, 0x45, 0x66, 0x6d, 0x74, 0x20], 8); // WAVEfmt␠
|
|
114
117
|
view.setUint32(16, 16, true);
|
|
115
|
-
view.setUint16(20,
|
|
118
|
+
view.setUint16(20, format, true);
|
|
116
119
|
view.setUint16(22, channels, true);
|
|
117
120
|
view.setUint32(24, audio.sampleRate, true);
|
|
118
|
-
view.setUint32(28, audio.sampleRate * channels *
|
|
119
|
-
view.setUint16(32, channels *
|
|
120
|
-
view.setUint16(34,
|
|
121
|
+
view.setUint32(28, audio.sampleRate * channels * bytes, true);
|
|
122
|
+
view.setUint16(32, channels * bytes, true);
|
|
123
|
+
view.setUint16(34, bits, true);
|
|
121
124
|
out.set([0x64, 0x61, 0x74, 0x61], 36); // data
|
|
122
125
|
view.setUint32(40, dataLength, true);
|
|
126
|
+
return { out, view, frames };
|
|
127
|
+
}
|
|
128
|
+
/** 32-bit float WAV: every sample kept exactly as it is, with no dither and no clipping. For audio
|
|
129
|
+
* between steps (a stem on its way to a processor), where 16 bits would round it each time. */
|
|
130
|
+
export function writeFloatWav(audio) {
|
|
131
|
+
const { out, view, frames } = header(audio, FORMAT_FLOAT, 32);
|
|
132
|
+
let at = 44;
|
|
133
|
+
for (let f = 0; f < frames; f++) {
|
|
134
|
+
for (const channel of audio.channels) {
|
|
135
|
+
view.setFloat32(at, channel[f] ?? 0, true);
|
|
136
|
+
at += 4;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return out;
|
|
140
|
+
}
|
|
141
|
+
export function writeWav(audio, seed = 0x5eed) {
|
|
142
|
+
const { view, out, frames } = header(audio, FORMAT_PCM, 16);
|
|
123
143
|
// TPDF dither: the difference of two uniform variables spans ±1 LSB with a triangular
|
|
124
144
|
// distribution, which decorrelates the rounding error from the signal.
|
|
125
145
|
const next = random(seed);
|
package/dist/audio/mix.d.ts
CHANGED
|
@@ -43,4 +43,10 @@ export interface WindowInput {
|
|
|
43
43
|
/** The part of the soundtrack between two frames: what a player mixes a few seconds at a time, so
|
|
44
44
|
* an hour-long film never needs its whole soundtrack in memory. Exactly that part of `mix`, except
|
|
45
45
|
* where a clip changes speed (time-stretching is padded, not continued from the window before). */
|
|
46
|
-
export declare function mixWindow(
|
|
46
|
+
export declare function mixWindow(input: WindowInput): PcmAudio;
|
|
47
|
+
/** The peak limiter the soundtrack ends with, on audio summed some other way (stems mixed
|
|
48
|
+
* separately, then added together). Per sample, so `limitAudio(busWindow(x))` is `mixWindow(x)`. */
|
|
49
|
+
export declare function limitAudio(audio: PcmAudio): PcmAudio;
|
|
50
|
+
/** What `mixWindow` sums before it limits: the clips and sounds between two frames, stereo at
|
|
51
|
+
* 48 kHz, unlimited. A stem (some of a film's clips, mixed on their own) is this over those clips. */
|
|
52
|
+
export declare function busWindow({ fps, clips, sounds, from, to }: WindowInput): PcmAudio;
|
package/dist/audio/mix.js
CHANGED
|
@@ -216,7 +216,17 @@ const STRETCH_PAD = 4 * WINDOW;
|
|
|
216
216
|
/** The part of the soundtrack between two frames: what a player mixes a few seconds at a time, so
|
|
217
217
|
* an hour-long film never needs its whole soundtrack in memory. Exactly that part of `mix`, except
|
|
218
218
|
* where a clip changes speed (time-stretching is padded, not continued from the window before). */
|
|
219
|
-
export function mixWindow(
|
|
219
|
+
export function mixWindow(input) {
|
|
220
|
+
return limitAudio(busWindow(input));
|
|
221
|
+
}
|
|
222
|
+
/** The peak limiter the soundtrack ends with, on audio summed some other way (stems mixed
|
|
223
|
+
* separately, then added together). Per sample, so `limitAudio(busWindow(x))` is `mixWindow(x)`. */
|
|
224
|
+
export function limitAudio(audio) {
|
|
225
|
+
return { sampleRate: audio.sampleRate, channels: audio.channels.map(limit) };
|
|
226
|
+
}
|
|
227
|
+
/** What `mixWindow` sums before it limits: the clips and sounds between two frames, stereo at
|
|
228
|
+
* 48 kHz, unlimited. A stem (some of a film's clips, mixed on their own) is this over those clips. */
|
|
229
|
+
export function busWindow({ fps, clips, sounds, from, to }) {
|
|
220
230
|
const start = Math.round((from / fps) * SAMPLE_RATE);
|
|
221
231
|
const length = Math.max(0, Math.ceil((to / fps) * SAMPLE_RATE) - start);
|
|
222
232
|
const left = new Float32Array(length), right = new Float32Array(length);
|
|
@@ -242,5 +252,5 @@ export function mixWindow({ fps, clips, sounds, from, to }) {
|
|
|
242
252
|
add(left, sound, at, gain);
|
|
243
253
|
add(right, sound, at, gain);
|
|
244
254
|
}
|
|
245
|
-
return { sampleRate: SAMPLE_RATE, channels: [
|
|
255
|
+
return { sampleRate: SAMPLE_RATE, channels: [left, right] };
|
|
246
256
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { FilmRenderer, sizeAtScale, type FrameOptions, type FrameSize, type DrawnFrame, type ImageUpload } from "./render/renderer.js";
|
|
2
2
|
export { collectSounds, audioClips } from "./render/sounds.js";
|
|
3
|
-
export { mix, mixWindow, SAMPLE_RATE, SOUND_SECONDS, type MixInput, type WindowInput, type ClipAudio } from "./audio/mix.js";
|
|
3
|
+
export { mix, mixWindow, busWindow, limitAudio, SAMPLE_RATE, SOUND_SECONDS, type MixInput, type WindowInput, type ClipAudio } from "./audio/mix.js";
|
|
4
4
|
export { loadAssets, AssetShelf, ImageNotLoaded, imageOf, videoOf, audioOf, parseVideoInfo, parseProxyInfo, qoiSize, imageSize, AssetError, type LoadedAssets, type LoadedImage, type LoadedVideo, type VideoInfo, type ProxyInfo, type Fetcher, type LoadOptions } from "./assets/store.js";
|
|
5
5
|
export { importedPath, importedFiles, videoInfoPath, videoPosterPath, videoProxyDir, videoProxyPath, PROXY_SEGMENT_SECONDS, PROXY_KEYFRAME_SECONDS, type AssetKey } from "./assets/paths.js";
|
|
6
6
|
export { demuxMp4, sampleData, Mp4Error, type Mp4Video, type Mp4Sample } from "./media/mp4.js";
|
|
@@ -15,7 +15,7 @@ export { decodePng } from "./assets/codecs/png.js";
|
|
|
15
15
|
export { decodeJpeg } from "./assets/codecs/jpeg.js";
|
|
16
16
|
export { encodeQoi, decodeQoi } from "./assets/codecs/qoi.js";
|
|
17
17
|
export { resizeImage } from "./assets/codecs/resize.js";
|
|
18
|
-
export { readWav, writeWav } from "./assets/codecs/wav.js";
|
|
18
|
+
export { readWav, writeWav, writeFloatWav } from "./assets/codecs/wav.js";
|
|
19
19
|
export { woff2ToSfnt } from "./text/woff2.js";
|
|
20
20
|
export { parseFont } from "./text/ttf.js";
|
|
21
21
|
export type { RgbaImage, PcmAudio } from "./assets/codecs/types.js";
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// raster), so the same frame is byte-identical in Bun, browsers and Workers.
|
|
4
4
|
export { FilmRenderer, sizeAtScale } from "./render/renderer.js";
|
|
5
5
|
export { collectSounds, audioClips } from "./render/sounds.js";
|
|
6
|
-
export { mix, mixWindow, SAMPLE_RATE, SOUND_SECONDS } from "./audio/mix.js";
|
|
6
|
+
export { mix, mixWindow, busWindow, limitAudio, SAMPLE_RATE, SOUND_SECONDS } from "./audio/mix.js";
|
|
7
7
|
export { loadAssets, AssetShelf, ImageNotLoaded, imageOf, videoOf, audioOf, parseVideoInfo, parseProxyInfo, qoiSize, imageSize, AssetError } from "./assets/store.js";
|
|
8
8
|
export { importedPath, importedFiles, videoInfoPath, videoPosterPath, videoProxyDir, videoProxyPath, PROXY_SEGMENT_SECONDS, PROXY_KEYFRAME_SECONDS } from "./assets/paths.js";
|
|
9
9
|
export { demuxMp4, sampleData, Mp4Error } from "./media/mp4.js";
|
|
@@ -16,6 +16,6 @@ export { decodePng } from "./assets/codecs/png.js";
|
|
|
16
16
|
export { decodeJpeg } from "./assets/codecs/jpeg.js";
|
|
17
17
|
export { encodeQoi, decodeQoi } from "./assets/codecs/qoi.js";
|
|
18
18
|
export { resizeImage } from "./assets/codecs/resize.js";
|
|
19
|
-
export { readWav, writeWav } from "./assets/codecs/wav.js";
|
|
19
|
+
export { readWav, writeWav, writeFloatWav } from "./assets/codecs/wav.js";
|
|
20
20
|
export { woff2ToSfnt } from "./text/woff2.js";
|
|
21
21
|
export { parseFont } from "./text/ttf.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tongue-core/engine",
|
|
3
|
-
"version": "0.1.0-rc.
|
|
3
|
+
"version": "0.1.0-rc.6",
|
|
4
4
|
"description": "Frame to pixels for Tongue films: evaluate, layout, paint and rasterize, deterministic everywhere.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"README.md"
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@tongue-core/film": "0.1.0-rc.
|
|
29
|
+
"@tongue-core/film": "0.1.0-rc.6"
|
|
30
30
|
},
|
|
31
31
|
"publishConfig": {
|
|
32
32
|
"access": "public"
|