@argo-video/cli 0.1.0 → 0.1.1
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 +21 -0
- package/README.md +2 -2
- package/dist/asset-server.d.ts +7 -0
- package/dist/asset-server.d.ts.map +1 -0
- package/dist/asset-server.js +66 -0
- package/dist/asset-server.js.map +1 -0
- package/dist/captions.d.ts +17 -0
- package/dist/captions.d.ts.map +1 -0
- package/dist/captions.js +23 -0
- package/dist/captions.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +87 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +44 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +74 -0
- package/dist/config.js.map +1 -0
- package/dist/export.d.ts +18 -0
- package/dist/export.d.ts.map +1 -0
- package/dist/export.js +64 -0
- package/dist/export.js.map +1 -0
- package/dist/fixtures.d.ts +13 -0
- package/dist/fixtures.d.ts.map +1 -0
- package/dist/fixtures.js +36 -0
- package/dist/fixtures.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +2 -0
- package/dist/init.d.ts.map +1 -0
- package/{src/init.ts → dist/init.js} +39 -54
- package/dist/init.js.map +1 -0
- package/dist/narration.d.ts +9 -0
- package/dist/narration.d.ts.map +1 -0
- package/dist/narration.js +27 -0
- package/dist/narration.js.map +1 -0
- package/dist/overlays/index.d.ts +8 -0
- package/dist/overlays/index.d.ts.map +1 -0
- package/dist/overlays/index.js +34 -0
- package/dist/overlays/index.js.map +1 -0
- package/dist/overlays/manifest.d.ts +5 -0
- package/dist/overlays/manifest.d.ts.map +1 -0
- package/dist/overlays/manifest.js +52 -0
- package/dist/overlays/manifest.js.map +1 -0
- package/dist/overlays/motion.d.ts +4 -0
- package/dist/overlays/motion.d.ts.map +1 -0
- package/dist/overlays/motion.js +25 -0
- package/dist/overlays/motion.js.map +1 -0
- package/dist/overlays/templates.d.ts +7 -0
- package/dist/overlays/templates.d.ts.map +1 -0
- package/dist/overlays/templates.js +98 -0
- package/dist/overlays/templates.js.map +1 -0
- package/dist/overlays/types.d.ts +42 -0
- package/dist/overlays/types.d.ts.map +1 -0
- package/dist/overlays/types.js +25 -0
- package/dist/overlays/types.js.map +1 -0
- package/dist/overlays/zones.d.ts +15 -0
- package/dist/overlays/zones.d.ts.map +1 -0
- package/dist/overlays/zones.js +69 -0
- package/dist/overlays/zones.js.map +1 -0
- package/dist/pipeline.d.ts +3 -0
- package/dist/pipeline.d.ts.map +1 -0
- package/dist/pipeline.js +93 -0
- package/dist/pipeline.js.map +1 -0
- package/dist/record.d.ts +14 -0
- package/dist/record.d.ts.map +1 -0
- package/dist/record.js +100 -0
- package/dist/record.js.map +1 -0
- package/dist/tts/align.d.ts +17 -0
- package/dist/tts/align.d.ts.map +1 -0
- package/dist/tts/align.js +40 -0
- package/dist/tts/align.js.map +1 -0
- package/dist/tts/cache.d.ts +31 -0
- package/dist/tts/cache.d.ts.map +1 -0
- package/dist/tts/cache.js +51 -0
- package/dist/tts/cache.js.map +1 -0
- package/dist/tts/engine.d.ts +41 -0
- package/dist/tts/engine.d.ts.map +1 -0
- package/dist/tts/engine.js +108 -0
- package/dist/tts/engine.js.map +1 -0
- package/dist/tts/generate.d.ts +20 -0
- package/dist/tts/generate.d.ts.map +1 -0
- package/dist/tts/generate.js +58 -0
- package/dist/tts/generate.js.map +1 -0
- package/dist/tts/kokoro.d.ts +13 -0
- package/dist/tts/kokoro.d.ts.map +1 -0
- package/dist/tts/kokoro.js +46 -0
- package/dist/tts/kokoro.js.map +1 -0
- package/package.json +13 -1
- package/.claude/settings.local.json +0 -34
- package/DESIGN.md +0 -261
- package/docs/enhancement-proposal.md +0 -262
- package/docs/superpowers/plans/2026-03-12-argo.md +0 -208
- package/docs/superpowers/plans/2026-03-12-editorial-overlay-system.md +0 -1560
- package/docs/superpowers/plans/2026-03-13-npm-rename-skill-showcase.md +0 -499
- package/docs/superpowers/specs/2026-03-13-npm-rename-skill-showcase-design.md +0 -109
- package/skills/argo-demo-creator.md +0 -355
- package/src/asset-server.ts +0 -81
- package/src/captions.ts +0 -36
- package/src/cli.ts +0 -97
- package/src/config.ts +0 -125
- package/src/export.ts +0 -93
- package/src/fixtures.ts +0 -50
- package/src/index.ts +0 -41
- package/src/narration.ts +0 -31
- package/src/overlays/index.ts +0 -54
- package/src/overlays/manifest.ts +0 -68
- package/src/overlays/motion.ts +0 -27
- package/src/overlays/templates.ts +0 -121
- package/src/overlays/types.ts +0 -73
- package/src/overlays/zones.ts +0 -82
- package/src/pipeline.ts +0 -120
- package/src/record.ts +0 -123
- package/src/tts/align.ts +0 -75
- package/src/tts/cache.ts +0 -65
- package/src/tts/engine.ts +0 -147
- package/src/tts/generate.ts +0 -83
- package/src/tts/kokoro.ts +0 -51
- package/tests/asset-server.test.ts +0 -67
- package/tests/captions.test.ts +0 -76
- package/tests/cli.test.ts +0 -131
- package/tests/config.test.ts +0 -150
- package/tests/e2e/fake-server.ts +0 -45
- package/tests/e2e/record.e2e.test.ts +0 -131
- package/tests/export.test.ts +0 -155
- package/tests/fixtures.test.ts +0 -74
- package/tests/init.test.ts +0 -77
- package/tests/narration.test.ts +0 -120
- package/tests/overlays/index.test.ts +0 -73
- package/tests/overlays/manifest.test.ts +0 -120
- package/tests/overlays/motion.test.ts +0 -34
- package/tests/overlays/templates.test.ts +0 -69
- package/tests/overlays/types.test.ts +0 -36
- package/tests/overlays/zones.test.ts +0 -49
- package/tests/pipeline.test.ts +0 -177
- package/tests/record.test.ts +0 -87
- package/tests/tts/align.test.ts +0 -118
- package/tests/tts/cache.test.ts +0 -110
- package/tests/tts/engine.test.ts +0 -204
- package/tests/tts/generate.test.ts +0 -177
- package/tests/tts/kokoro.test.ts +0 -25
- package/tsconfig.json +0 -19
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zones.js","sourceRoot":"","sources":["../../src/overlays/zones.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA0B9C,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,SAAS,GAAG,UAAU,GAAG,WAAW,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC,GAC1F,OAAO,CAAC,MAAM,CAAC,CAkFjB"}
|
package/dist/pipeline.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { execFileSync } from 'node:child_process';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { generateClips } from './tts/generate.js';
|
|
5
|
+
import { record } from './record.js';
|
|
6
|
+
import { alignClips } from './tts/align.js';
|
|
7
|
+
import { parseWavHeader, createWavBuffer } from './tts/engine.js';
|
|
8
|
+
import { exportVideo, checkFfmpeg } from './export.js';
|
|
9
|
+
function getVideoDurationMs(videoPath) {
|
|
10
|
+
let raw;
|
|
11
|
+
try {
|
|
12
|
+
raw = execFileSync('ffprobe', ['-v', 'error', '-show_entries', 'format=duration', '-of', 'csv=p=0', videoPath], { encoding: 'utf-8' }).trim();
|
|
13
|
+
}
|
|
14
|
+
catch (err) {
|
|
15
|
+
throw new Error(`Failed to get video duration from ${videoPath}. ` +
|
|
16
|
+
`Ensure ffprobe is installed (it usually comes with ffmpeg). ` +
|
|
17
|
+
`Original error: ${err.message}`);
|
|
18
|
+
}
|
|
19
|
+
const durationMs = Math.round(parseFloat(raw) * 1000);
|
|
20
|
+
if (isNaN(durationMs) || durationMs <= 0) {
|
|
21
|
+
throw new Error(`ffprobe returned invalid duration "${raw}" for ${videoPath}. The video file may be corrupt.`);
|
|
22
|
+
}
|
|
23
|
+
return durationMs;
|
|
24
|
+
}
|
|
25
|
+
export async function runPipeline(demoName, config) {
|
|
26
|
+
if (!config.baseURL) {
|
|
27
|
+
throw new Error('baseURL is required but not set. Set it in argo.config.js or pass --config.');
|
|
28
|
+
}
|
|
29
|
+
if (!config.tts.engine) {
|
|
30
|
+
throw new Error('TTS engine is not configured. Ensure config.tts.engine is set.');
|
|
31
|
+
}
|
|
32
|
+
checkFfmpeg();
|
|
33
|
+
const argoDir = join('.argo', demoName);
|
|
34
|
+
// Step 1: Generate TTS clips
|
|
35
|
+
console.log('Step 1/4: Generating TTS clips...');
|
|
36
|
+
const clipResults = await generateClips({
|
|
37
|
+
manifestPath: `${config.demosDir}/${demoName}.voiceover.json`,
|
|
38
|
+
demoName,
|
|
39
|
+
engine: config.tts.engine,
|
|
40
|
+
projectRoot: '.',
|
|
41
|
+
defaults: { voice: config.tts.defaultVoice, speed: config.tts.defaultSpeed },
|
|
42
|
+
});
|
|
43
|
+
if (clipResults.length === 0) {
|
|
44
|
+
throw new Error(`No TTS clips were generated from ${config.demosDir}/${demoName}.voiceover.json. ` +
|
|
45
|
+
`Ensure the manifest contains at least one entry.`);
|
|
46
|
+
}
|
|
47
|
+
// Step 2: Record browser demo
|
|
48
|
+
console.log('Step 2/4: Recording browser demo...');
|
|
49
|
+
const { timingPath } = await record(demoName, {
|
|
50
|
+
demosDir: config.demosDir,
|
|
51
|
+
baseURL: config.baseURL,
|
|
52
|
+
video: { width: config.video.width, height: config.video.height },
|
|
53
|
+
});
|
|
54
|
+
// Step 3: Align clips with timing
|
|
55
|
+
console.log('Step 3/4: Aligning narration with video...');
|
|
56
|
+
const timing = JSON.parse(readFileSync(timingPath, 'utf-8'));
|
|
57
|
+
// Load WAV clips into memory
|
|
58
|
+
const clips = clipResults.map((cr) => {
|
|
59
|
+
const wavBuf = readFileSync(cr.clipPath);
|
|
60
|
+
const header = parseWavHeader(wavBuf);
|
|
61
|
+
// Extract Float32 samples from the data chunk
|
|
62
|
+
const sampleCount = header.dataSize / 4; // 32-bit float = 4 bytes
|
|
63
|
+
const samples = new Float32Array(sampleCount);
|
|
64
|
+
for (let i = 0; i < sampleCount && header.dataOffset + i * 4 + 3 < wavBuf.length; i++) {
|
|
65
|
+
samples[i] = wavBuf.readFloatLE(header.dataOffset + i * 4);
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
scene: cr.scene,
|
|
69
|
+
durationMs: header.durationMs,
|
|
70
|
+
samples,
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
// Use actual video duration for alignment
|
|
74
|
+
const videoPath = join(argoDir, 'video.webm');
|
|
75
|
+
const totalDurationMs = getVideoDurationMs(videoPath);
|
|
76
|
+
const aligned = alignClips(timing, clips, totalDurationMs);
|
|
77
|
+
const alignedWav = createWavBuffer(aligned.samples, 24_000);
|
|
78
|
+
const alignedPath = join(argoDir, 'narration-aligned.wav');
|
|
79
|
+
writeFileSync(alignedPath, alignedWav);
|
|
80
|
+
// Step 4: Export final video
|
|
81
|
+
console.log('Step 4/4: Exporting final video...');
|
|
82
|
+
const outputPath = await exportVideo({
|
|
83
|
+
demoName,
|
|
84
|
+
argoDir: '.argo',
|
|
85
|
+
outputDir: config.outputDir,
|
|
86
|
+
preset: config.export.preset,
|
|
87
|
+
crf: config.export.crf,
|
|
88
|
+
fps: config.video.fps,
|
|
89
|
+
});
|
|
90
|
+
console.log(`Done! Video saved to: ${outputPath}`);
|
|
91
|
+
return outputPath;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=pipeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"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"}
|
package/dist/record.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface RecordOptions {
|
|
2
|
+
demosDir: string;
|
|
3
|
+
baseURL: string;
|
|
4
|
+
video: {
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
export interface RecordResult {
|
|
10
|
+
videoPath: string;
|
|
11
|
+
timingPath: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function record(demoName: string, options: RecordOptions): Promise<RecordResult>;
|
|
14
|
+
//# sourceMappingURL=record.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../src/record.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1C;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAyCD,wBAAsB,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAkE5F"}
|
package/dist/record.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { mkdirSync, readdirSync, copyFileSync, existsSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { startAssetServer } from './asset-server.js';
|
|
5
|
+
import { loadOverlayManifest, hasImageAssets } from './overlays/manifest.js';
|
|
6
|
+
function findVideoInResults(testResultsDir) {
|
|
7
|
+
if (!existsSync(testResultsDir))
|
|
8
|
+
return undefined;
|
|
9
|
+
for (const entry of readdirSync(testResultsDir, { recursive: true })) {
|
|
10
|
+
const name = typeof entry === 'string' ? entry : entry.toString();
|
|
11
|
+
if (name.endsWith('.webm')) {
|
|
12
|
+
return path.join(testResultsDir, name);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
function createPlaywrightConfig(options, outputDir) {
|
|
18
|
+
const demosDir = path.resolve(options.demosDir);
|
|
19
|
+
const { width, height } = options.video;
|
|
20
|
+
return `import { defineConfig } from '@playwright/test';
|
|
21
|
+
|
|
22
|
+
export default defineConfig({
|
|
23
|
+
preserveOutput: 'always',
|
|
24
|
+
outputDir: ${JSON.stringify(outputDir)},
|
|
25
|
+
projects: [
|
|
26
|
+
{
|
|
27
|
+
name: 'demos',
|
|
28
|
+
testDir: ${JSON.stringify(demosDir)},
|
|
29
|
+
testMatch: '**/*.demo.ts',
|
|
30
|
+
use: {
|
|
31
|
+
baseURL: ${JSON.stringify(options.baseURL)},
|
|
32
|
+
viewport: { width: ${width}, height: ${height} },
|
|
33
|
+
video: {
|
|
34
|
+
mode: 'on',
|
|
35
|
+
size: { width: ${width}, height: ${height} },
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
});
|
|
41
|
+
`;
|
|
42
|
+
}
|
|
43
|
+
export async function record(demoName, options) {
|
|
44
|
+
const argoDir = path.join('.argo', demoName);
|
|
45
|
+
mkdirSync(argoDir, { recursive: true });
|
|
46
|
+
const videoPath = path.join(argoDir, 'video.webm');
|
|
47
|
+
const timingPath = path.join(argoDir, '.timing.json');
|
|
48
|
+
const testResultsDir = path.resolve('test-results');
|
|
49
|
+
const recordConfigPath = path.join(argoDir, 'playwright.record.config.mjs');
|
|
50
|
+
writeFileSync(recordConfigPath, createPlaywrightConfig(options, testResultsDir), 'utf-8');
|
|
51
|
+
// Clean test-results to avoid picking up stale videos
|
|
52
|
+
rmSync(testResultsDir, { recursive: true, force: true });
|
|
53
|
+
// Start asset server if overlay manifest has image assets
|
|
54
|
+
let assetServer;
|
|
55
|
+
const overlayManifestPath = path.join(options.demosDir, `${demoName}.overlays.json`);
|
|
56
|
+
const overlayEntries = await loadOverlayManifest(overlayManifestPath);
|
|
57
|
+
if (overlayEntries && hasImageAssets(overlayEntries)) {
|
|
58
|
+
const assetDir = path.join(options.demosDir, 'assets');
|
|
59
|
+
assetServer = await startAssetServer(assetDir);
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
return await new Promise((resolve, reject) => {
|
|
63
|
+
execFile('npx', ['playwright', 'test', '--config', recordConfigPath, '--grep', demoName, '--project', 'demos'], {
|
|
64
|
+
env: {
|
|
65
|
+
...process.env,
|
|
66
|
+
ARGO_DEMO_NAME: demoName,
|
|
67
|
+
ARGO_OUTPUT_DIR: argoDir,
|
|
68
|
+
BASE_URL: options.baseURL,
|
|
69
|
+
ARGO_ASSET_URL: assetServer?.url ?? '',
|
|
70
|
+
},
|
|
71
|
+
}, (error, stdout, stderr) => {
|
|
72
|
+
if (error) {
|
|
73
|
+
const output = [stdout, stderr].filter(Boolean).join('\n');
|
|
74
|
+
reject(new Error(`Playwright recording failed:\n${output}`));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
// Copy the video from test-results/ to .argo/<demo>/video.webm
|
|
78
|
+
const found = findVideoInResults(testResultsDir);
|
|
79
|
+
if (!found) {
|
|
80
|
+
reject(new Error(`No video recording found in test-results/. ` +
|
|
81
|
+
`Ensure playwright.config.ts has video: 'on' or video: { mode: 'on' }.`));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
copyFileSync(found, videoPath);
|
|
85
|
+
// Verify timing file was written by the narration fixture
|
|
86
|
+
if (!existsSync(timingPath)) {
|
|
87
|
+
reject(new Error(`No timing file found at ${timingPath}. ` +
|
|
88
|
+
`Ensure the demo uses the argo test fixture with narration.mark() calls.`));
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
resolve({ videoPath, timingPath });
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
if (assetServer)
|
|
97
|
+
await assetServer.close();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=record.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record.js","sourceRoot":"","sources":["../src/record.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type SceneTiming = Record<string, number>;
|
|
2
|
+
export interface ClipInfo {
|
|
3
|
+
scene: string;
|
|
4
|
+
durationMs: number;
|
|
5
|
+
samples: Float32Array;
|
|
6
|
+
}
|
|
7
|
+
export interface Placement {
|
|
8
|
+
scene: string;
|
|
9
|
+
startMs: number;
|
|
10
|
+
endMs: number;
|
|
11
|
+
}
|
|
12
|
+
export interface AlignResult {
|
|
13
|
+
placements: Placement[];
|
|
14
|
+
samples: Float32Array;
|
|
15
|
+
}
|
|
16
|
+
export declare function alignClips(timing: SceneTiming, clips: ClipInfo[], totalDurationMs: number, sampleRate?: number): AlignResult;
|
|
17
|
+
//# sourceMappingURL=align.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"align.d.ts","sourceRoot":"","sources":["../../src/tts/align.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEjD,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,YAAY,CAAC;CACvB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,OAAO,EAAE,YAAY,CAAC;CACvB;AAID,wBAAgB,UAAU,CACxB,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,QAAQ,EAAE,EACjB,eAAe,EAAE,MAAM,EACvB,UAAU,SAAS,GAClB,WAAW,CAgDb"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const OVERLAP_GAP_MS = 100;
|
|
2
|
+
export function alignClips(timing, clips, totalDurationMs, sampleRate = 24_000) {
|
|
3
|
+
// 1. Filter to clips with matching scenes
|
|
4
|
+
const matched = clips.filter((c) => c.scene in timing);
|
|
5
|
+
const unmatched = clips.filter((c) => !(c.scene in timing));
|
|
6
|
+
if (unmatched.length > 0) {
|
|
7
|
+
const names = unmatched.map((c) => c.scene).join(', ');
|
|
8
|
+
console.warn(`Warning: ${unmatched.length} clip(s) have no matching scene in timing and will be skipped: ${names}. ` +
|
|
9
|
+
`Check that voiceover manifest scene names match narration.mark() calls.`);
|
|
10
|
+
}
|
|
11
|
+
// 2. Sort by scene timestamp ascending
|
|
12
|
+
matched.sort((a, b) => timing[a.scene] - timing[b.scene]);
|
|
13
|
+
// 3. Place each clip, preventing overlap
|
|
14
|
+
const placements = [];
|
|
15
|
+
let previousEndMs = 0;
|
|
16
|
+
for (const clip of matched) {
|
|
17
|
+
let startMs = timing[clip.scene];
|
|
18
|
+
// If this would overlap the previous clip, push forward
|
|
19
|
+
if (placements.length > 0 && startMs < previousEndMs) {
|
|
20
|
+
startMs = previousEndMs + OVERLAP_GAP_MS;
|
|
21
|
+
}
|
|
22
|
+
const endMs = startMs + clip.durationMs;
|
|
23
|
+
placements.push({ scene: clip.scene, startMs, endMs });
|
|
24
|
+
previousEndMs = endMs;
|
|
25
|
+
}
|
|
26
|
+
// 4. Create silence buffer
|
|
27
|
+
const totalSamples = Math.round((totalDurationMs / 1000) * sampleRate);
|
|
28
|
+
const output = new Float32Array(totalSamples);
|
|
29
|
+
// 5. Mix each clip's samples into output
|
|
30
|
+
for (let i = 0; i < placements.length; i++) {
|
|
31
|
+
const placement = placements[i];
|
|
32
|
+
const clip = matched[i];
|
|
33
|
+
const startSample = Math.round((placement.startMs / 1000) * sampleRate);
|
|
34
|
+
for (let j = 0; j < clip.samples.length && startSample + j < totalSamples; j++) {
|
|
35
|
+
output[startSample + j] += clip.samples[j];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return { placements, samples: output };
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=align.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"align.js","sourceRoot":"","sources":["../../src/tts/align.ts"],"names":[],"mappings":"AAmBA,MAAM,cAAc,GAAG,GAAG,CAAC;AAE3B,MAAM,UAAU,UAAU,CACxB,MAAmB,EACnB,KAAiB,EACjB,eAAuB,EACvB,UAAU,GAAG,MAAM;IAEnB,0CAA0C;IAC1C,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,MAAM,CAAC,CAAC;IACvD,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,MAAM,CAAC,CAAC,CAAC;IAC5D,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvD,OAAO,CAAC,IAAI,CACV,YAAY,SAAS,CAAC,MAAM,kEAAkE,KAAK,IAAI;YACvG,yEAAyE,CAC1E,CAAC;IACJ,CAAC;IAED,uCAAuC;IACvC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAE1D,yCAAyC;IACzC,MAAM,UAAU,GAAgB,EAAE,CAAC;IACnC,IAAI,aAAa,GAAG,CAAC,CAAC;IAEtB,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,IAAI,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAEjC,wDAAwD;QACxD,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,GAAG,aAAa,EAAE,CAAC;YACrD,OAAO,GAAG,aAAa,GAAG,cAAc,CAAC;QAC3C,CAAC;QAED,MAAM,KAAK,GAAG,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC;QACxC,UAAU,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QACvD,aAAa,GAAG,KAAK,CAAC;IACxB,CAAC;IAED,2BAA2B;IAC3B,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,eAAe,GAAG,IAAI,CAAC,GAAG,UAAU,CAAC,CAAC;IACvE,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,YAAY,CAAC,CAAC;IAE9C,yCAAyC;IACzC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3C,MAAM,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;QAChC,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACxB,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,UAAU,CAAC,CAAC;QAExE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,WAAW,GAAG,CAAC,GAAG,YAAY,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/E,MAAM,CAAC,WAAW,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;IAED,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content-addressed clip cache for Argo TTS output.
|
|
3
|
+
*/
|
|
4
|
+
export interface ManifestEntry {
|
|
5
|
+
scene: string;
|
|
6
|
+
text: string;
|
|
7
|
+
voice?: string;
|
|
8
|
+
speed?: number;
|
|
9
|
+
}
|
|
10
|
+
export declare class ClipCache {
|
|
11
|
+
private readonly projectRoot;
|
|
12
|
+
constructor(projectRoot: string);
|
|
13
|
+
/**
|
|
14
|
+
* Returns the full file path for a cached clip.
|
|
15
|
+
*/
|
|
16
|
+
getClipPath(demoName: string, entry: ManifestEntry): string;
|
|
17
|
+
/**
|
|
18
|
+
* Checks whether a clip is already cached on disk.
|
|
19
|
+
*/
|
|
20
|
+
isCached(demoName: string, entry: ManifestEntry): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Returns the cached WAV buffer, or null if not cached.
|
|
23
|
+
*/
|
|
24
|
+
getCachedClip(demoName: string, entry: ManifestEntry): Buffer | null;
|
|
25
|
+
/**
|
|
26
|
+
* Writes a WAV buffer to the cache, creating directories as needed.
|
|
27
|
+
*/
|
|
28
|
+
cacheClip(demoName: string, entry: ManifestEntry, wavBuffer: Buffer): void;
|
|
29
|
+
private computeHash;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/tts/cache.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;gBAEzB,WAAW,EAAE,MAAM;IAI/B;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,MAAM;IAK3D;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO;IAIzD;;OAEG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI;IAQpE;;OAEG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAM1E,OAAO,CAAC,WAAW;CAOpB"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content-addressed clip cache for Argo TTS output.
|
|
3
|
+
*/
|
|
4
|
+
import crypto from 'node:crypto';
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
export class ClipCache {
|
|
8
|
+
projectRoot;
|
|
9
|
+
constructor(projectRoot) {
|
|
10
|
+
this.projectRoot = projectRoot;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Returns the full file path for a cached clip.
|
|
14
|
+
*/
|
|
15
|
+
getClipPath(demoName, entry) {
|
|
16
|
+
const hash = this.computeHash(entry);
|
|
17
|
+
return path.join(this.projectRoot, '.argo', demoName, 'clips', `${hash}.wav`);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Checks whether a clip is already cached on disk.
|
|
21
|
+
*/
|
|
22
|
+
isCached(demoName, entry) {
|
|
23
|
+
return fs.existsSync(this.getClipPath(demoName, entry));
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Returns the cached WAV buffer, or null if not cached.
|
|
27
|
+
*/
|
|
28
|
+
getCachedClip(demoName, entry) {
|
|
29
|
+
const clipPath = this.getClipPath(demoName, entry);
|
|
30
|
+
if (!fs.existsSync(clipPath)) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
return fs.readFileSync(clipPath);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Writes a WAV buffer to the cache, creating directories as needed.
|
|
37
|
+
*/
|
|
38
|
+
cacheClip(demoName, entry, wavBuffer) {
|
|
39
|
+
const clipPath = this.getClipPath(demoName, entry);
|
|
40
|
+
fs.mkdirSync(path.dirname(clipPath), { recursive: true });
|
|
41
|
+
fs.writeFileSync(clipPath, wavBuffer);
|
|
42
|
+
}
|
|
43
|
+
computeHash(entry) {
|
|
44
|
+
const { scene, text, voice, speed } = entry;
|
|
45
|
+
return crypto
|
|
46
|
+
.createHash('sha256')
|
|
47
|
+
.update(JSON.stringify({ scene, text, voice, speed }))
|
|
48
|
+
.digest('hex');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/tts/cache.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAS7B,MAAM,OAAO,SAAS;IACH,WAAW,CAAS;IAErC,YAAY,WAAmB;QAC7B,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IACjC,CAAC;IAED;;OAEG;IACH,WAAW,CAAC,QAAgB,EAAE,KAAoB;QAChD,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QACrC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,IAAI,MAAM,CAAC,CAAC;IAChF,CAAC;IAED;;OAEG;IACH,QAAQ,CAAC,QAAgB,EAAE,KAAoB;QAC7C,OAAO,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED;;OAEG;IACH,aAAa,CAAC,QAAgB,EAAE,KAAoB;QAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QACnD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC;IAED;;OAEG;IACH,SAAS,CAAC,QAAgB,EAAE,KAAoB,EAAE,SAAiB;QACjE,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QACnD,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1D,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IACxC,CAAC;IAEO,WAAW,CAAC,KAAoB;QACtC,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;QAC5C,OAAO,MAAM;aACV,UAAU,CAAC,QAAQ,CAAC;aACpB,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;aACrD,MAAM,CAAC,KAAK,CAAC,CAAC;IACnB,CAAC;CACF"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TTS Engine interface and WAV utilities for Argo.
|
|
3
|
+
*/
|
|
4
|
+
export interface TTSEngineOptions {
|
|
5
|
+
voice?: string;
|
|
6
|
+
speed?: number;
|
|
7
|
+
lang?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface TTSEngine {
|
|
10
|
+
generate(text: string, options: TTSEngineOptions): Promise<Buffer>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Creates a valid WAV file buffer from Float32Array samples.
|
|
14
|
+
* Format: mono, 32-bit IEEE float, given sample rate.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createWavBuffer(samples: Float32Array, sampleRate?: number): Buffer;
|
|
17
|
+
export interface WavHeader {
|
|
18
|
+
sampleRate: number;
|
|
19
|
+
numChannels: number;
|
|
20
|
+
bitsPerSample: number;
|
|
21
|
+
audioFormat: number;
|
|
22
|
+
dataSize: number;
|
|
23
|
+
dataOffset: number;
|
|
24
|
+
durationMs: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Parses a WAV file header. Searches for the 'data' chunk rather than
|
|
28
|
+
* assuming a fixed offset.
|
|
29
|
+
*/
|
|
30
|
+
export declare function parseWavHeader(wav: Buffer): WavHeader;
|
|
31
|
+
/**
|
|
32
|
+
* Creates a mock TTS engine that produces silent WAV buffers of the given
|
|
33
|
+
* duration and records all calls for test assertions.
|
|
34
|
+
*/
|
|
35
|
+
export declare function createMockTTSEngine(durationMs?: number): TTSEngine & {
|
|
36
|
+
calls: Array<{
|
|
37
|
+
text: string;
|
|
38
|
+
options: TTSEngineOptions;
|
|
39
|
+
}>;
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/tts/engine.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACpE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,EAAE,UAAU,SAAQ,GAAG,MAAM,CAoCjF;AAED,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAuDrD;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,SAAM,GACf,SAAS,GAAG;IAAE,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,gBAAgB,CAAA;KAAE,CAAC,CAAA;CAAE,CAa3E"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TTS Engine interface and WAV utilities for Argo.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Creates a valid WAV file buffer from Float32Array samples.
|
|
6
|
+
* Format: mono, 32-bit IEEE float, given sample rate.
|
|
7
|
+
*/
|
|
8
|
+
export function createWavBuffer(samples, sampleRate = 24000) {
|
|
9
|
+
const numChannels = 1;
|
|
10
|
+
const bitsPerSample = 32;
|
|
11
|
+
const bytesPerSample = bitsPerSample / 8;
|
|
12
|
+
const blockAlign = numChannels * bytesPerSample;
|
|
13
|
+
const byteRate = sampleRate * blockAlign;
|
|
14
|
+
const dataSize = samples.length * bytesPerSample;
|
|
15
|
+
const headerSize = 44;
|
|
16
|
+
const buf = Buffer.alloc(headerSize + dataSize);
|
|
17
|
+
// RIFF header
|
|
18
|
+
buf.write('RIFF', 0, 'ascii');
|
|
19
|
+
buf.writeUInt32LE(headerSize + dataSize - 8, 4);
|
|
20
|
+
buf.write('WAVE', 8, 'ascii');
|
|
21
|
+
// fmt chunk
|
|
22
|
+
buf.write('fmt ', 12, 'ascii');
|
|
23
|
+
buf.writeUInt32LE(16, 16); // fmt chunk size
|
|
24
|
+
buf.writeUInt16LE(3, 20); // audioFormat = 3 (IEEE float)
|
|
25
|
+
buf.writeUInt16LE(numChannels, 22);
|
|
26
|
+
buf.writeUInt32LE(sampleRate, 24);
|
|
27
|
+
buf.writeUInt32LE(byteRate, 28);
|
|
28
|
+
buf.writeUInt16LE(blockAlign, 32);
|
|
29
|
+
buf.writeUInt16LE(bitsPerSample, 34);
|
|
30
|
+
// data chunk
|
|
31
|
+
buf.write('data', 36, 'ascii');
|
|
32
|
+
buf.writeUInt32LE(dataSize, 40);
|
|
33
|
+
// sample data
|
|
34
|
+
for (let i = 0; i < samples.length; i++) {
|
|
35
|
+
buf.writeFloatLE(samples[i], headerSize + i * bytesPerSample);
|
|
36
|
+
}
|
|
37
|
+
return buf;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Parses a WAV file header. Searches for the 'data' chunk rather than
|
|
41
|
+
* assuming a fixed offset.
|
|
42
|
+
*/
|
|
43
|
+
export function parseWavHeader(wav) {
|
|
44
|
+
if (wav.length < 44) {
|
|
45
|
+
throw new Error('Buffer too small to be a valid WAV file');
|
|
46
|
+
}
|
|
47
|
+
if (wav.toString('ascii', 0, 4) !== 'RIFF') {
|
|
48
|
+
throw new Error('Not a valid WAV file: missing RIFF header');
|
|
49
|
+
}
|
|
50
|
+
if (wav.toString('ascii', 8, 12) !== 'WAVE') {
|
|
51
|
+
throw new Error('Not a valid WAV file: missing WAVE marker');
|
|
52
|
+
}
|
|
53
|
+
// Validate and parse fmt chunk (expected at byte 12)
|
|
54
|
+
if (wav.toString('ascii', 12, 16) !== 'fmt ') {
|
|
55
|
+
throw new Error('Not a valid WAV file: fmt chunk not found at expected offset');
|
|
56
|
+
}
|
|
57
|
+
const audioFormat = wav.readUInt16LE(20);
|
|
58
|
+
const numChannels = wav.readUInt16LE(22);
|
|
59
|
+
const sampleRate = wav.readUInt32LE(24);
|
|
60
|
+
const bitsPerSample = wav.readUInt16LE(34);
|
|
61
|
+
// Search for 'data' chunk
|
|
62
|
+
let offset = 12; // after 'WAVE'
|
|
63
|
+
let dataSize = 0;
|
|
64
|
+
let dataOffset = 0;
|
|
65
|
+
while (offset < wav.length - 8) {
|
|
66
|
+
const chunkId = wav.toString('ascii', offset, offset + 4);
|
|
67
|
+
const chunkSize = wav.readUInt32LE(offset + 4);
|
|
68
|
+
if (chunkId === 'data') {
|
|
69
|
+
dataSize = chunkSize;
|
|
70
|
+
dataOffset = offset + 8;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
offset += 8 + chunkSize;
|
|
74
|
+
}
|
|
75
|
+
if (dataOffset === 0) {
|
|
76
|
+
throw new Error('No data chunk found in WAV file');
|
|
77
|
+
}
|
|
78
|
+
const bytesPerSample = bitsPerSample / 8;
|
|
79
|
+
const totalSamples = dataSize / (bytesPerSample * numChannels);
|
|
80
|
+
const durationMs = (totalSamples / sampleRate) * 1000;
|
|
81
|
+
return {
|
|
82
|
+
sampleRate,
|
|
83
|
+
numChannels,
|
|
84
|
+
bitsPerSample,
|
|
85
|
+
audioFormat,
|
|
86
|
+
dataSize,
|
|
87
|
+
dataOffset,
|
|
88
|
+
durationMs,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Creates a mock TTS engine that produces silent WAV buffers of the given
|
|
93
|
+
* duration and records all calls for test assertions.
|
|
94
|
+
*/
|
|
95
|
+
export function createMockTTSEngine(durationMs = 500) {
|
|
96
|
+
const calls = [];
|
|
97
|
+
return {
|
|
98
|
+
calls,
|
|
99
|
+
async generate(text, options) {
|
|
100
|
+
calls.push({ text, options });
|
|
101
|
+
const sampleRate = 24000;
|
|
102
|
+
const numSamples = Math.round((durationMs / 1000) * sampleRate);
|
|
103
|
+
const samples = new Float32Array(numSamples); // zeros = silence
|
|
104
|
+
return createWavBuffer(samples, sampleRate);
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/tts/engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TTS clip generation with manifest parsing and cache integration.
|
|
3
|
+
*/
|
|
4
|
+
import type { TTSEngine } from './engine.js';
|
|
5
|
+
export interface GenerateClipsOptions {
|
|
6
|
+
manifestPath: string;
|
|
7
|
+
demoName: string;
|
|
8
|
+
engine: TTSEngine;
|
|
9
|
+
projectRoot: string;
|
|
10
|
+
defaults?: {
|
|
11
|
+
voice?: string;
|
|
12
|
+
speed?: number;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export interface ClipResult {
|
|
16
|
+
scene: string;
|
|
17
|
+
clipPath: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function generateClips(options: GenerateClipsOptions): Promise<ClipResult[]>;
|
|
20
|
+
//# sourceMappingURL=generate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/tts/generate.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAG7C,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,SAAS,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/C;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CA6DxF"}
|