@bendyline/squisq 1.5.2 → 2.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.
Files changed (263) hide show
  1. package/README.md +63 -10
  2. package/dist/{Doc-BBVGq1_9.d.ts → Doc-ylqWKDc_.d.ts} +248 -37
  3. package/dist/{ImageEditDoc-FuyGtt6o.d.ts → ImageEditDoc-D828IXJ1.d.ts} +1 -1
  4. package/dist/annotationCoercion-Dr_DY9Hx.d.ts +86 -0
  5. package/dist/{chunk-VWUFZ6ZG.js → chunk-3E5F2XMR.js} +2 -29
  6. package/dist/chunk-3E5F2XMR.js.map +1 -0
  7. package/dist/chunk-4X3JQXNM.js +35 -0
  8. package/dist/chunk-4X3JQXNM.js.map +1 -0
  9. package/dist/{chunk-P3K7NLHF.js → chunk-5DKBFXPY.js} +31 -121
  10. package/dist/chunk-5DKBFXPY.js.map +1 -0
  11. package/dist/{chunk-DLXZMS5K.js → chunk-5J4HKRJF.js} +51 -3
  12. package/dist/chunk-5J4HKRJF.js.map +1 -0
  13. package/dist/chunk-5UYI3RJB.js +2503 -0
  14. package/dist/chunk-5UYI3RJB.js.map +1 -0
  15. package/dist/{chunk-BEQODJWV.js → chunk-66QO7XFB.js} +11 -291
  16. package/dist/chunk-66QO7XFB.js.map +1 -0
  17. package/dist/{chunk-KKNUBQ6Y.js → chunk-6PJRB2CF.js} +156 -91
  18. package/dist/chunk-6PJRB2CF.js.map +1 -0
  19. package/dist/{chunk-HWVFJAAH.js → chunk-AMIR72JP.js} +28 -10
  20. package/dist/chunk-AMIR72JP.js.map +1 -0
  21. package/dist/chunk-FVJUUTEM.js +74 -0
  22. package/dist/chunk-FVJUUTEM.js.map +1 -0
  23. package/dist/{chunk-5TYS5V3G.js → chunk-GF3BVNLU.js} +4127 -3538
  24. package/dist/chunk-GF3BVNLU.js.map +1 -0
  25. package/dist/{chunk-Q3ROPT5H.js → chunk-GZY66SDN.js} +1 -1
  26. package/dist/{chunk-Q3ROPT5H.js.map → chunk-GZY66SDN.js.map} +1 -1
  27. package/dist/chunk-JOQYPRDZ.js +127 -0
  28. package/dist/chunk-JOQYPRDZ.js.map +1 -0
  29. package/dist/{chunk-A4O7GIWE.js → chunk-LYNJEOAC.js} +10 -73
  30. package/dist/chunk-LYNJEOAC.js.map +1 -0
  31. package/dist/{chunk-LDQ2HJIX.js → chunk-PXLVXTLW.js} +252 -54
  32. package/dist/chunk-PXLVXTLW.js.map +1 -0
  33. package/dist/{chunk-TQWLI6S2.js → chunk-Q7LROV2H.js} +4 -2
  34. package/dist/{chunk-TQWLI6S2.js.map → chunk-Q7LROV2H.js.map} +1 -1
  35. package/dist/{chunk-T5UK6YOB.js → chunk-QTSJOY54.js} +60 -17
  36. package/dist/chunk-QTSJOY54.js.map +1 -0
  37. package/dist/chunk-QVANWDPH.js +2358 -0
  38. package/dist/chunk-QVANWDPH.js.map +1 -0
  39. package/dist/{chunk-GPLTCSXO.js → chunk-R2GC6RLD.js} +7 -7
  40. package/dist/chunk-R2GC6RLD.js.map +1 -0
  41. package/dist/chunk-R2YZTCLO.js +292 -0
  42. package/dist/chunk-R2YZTCLO.js.map +1 -0
  43. package/dist/chunk-S7A5D7ZJ.js +174 -0
  44. package/dist/chunk-S7A5D7ZJ.js.map +1 -0
  45. package/dist/chunk-T3TI7GSZ.js +884 -0
  46. package/dist/chunk-T3TI7GSZ.js.map +1 -0
  47. package/dist/{chunk-LTN6I7QW.js → chunk-TUWMK62B.js} +55 -4
  48. package/dist/chunk-TUWMK62B.js.map +1 -0
  49. package/dist/{chunk-IMSCRLLV.js → chunk-VR7UPJ7C.js} +35 -25
  50. package/dist/chunk-VR7UPJ7C.js.map +1 -0
  51. package/dist/doc/index.d.ts +2334 -5
  52. package/dist/doc/index.js +127 -33
  53. package/dist/generate/index.d.ts +7 -2
  54. package/dist/generate/index.js +1 -1
  55. package/dist/imageEdit/index.d.ts +9 -6
  56. package/dist/imageEdit/index.js +3 -3
  57. package/dist/index.d.ts +12 -10
  58. package/dist/index.js +246 -80
  59. package/dist/jsonForm/index.d.ts +7 -3
  60. package/dist/jsonForm/index.js +9 -4
  61. package/dist/markdown/index.d.ts +6 -70
  62. package/dist/markdown/index.js +13 -9
  63. package/dist/narration/index.d.ts +618 -0
  64. package/dist/narration/index.js +82 -0
  65. package/dist/recommend/index.d.ts +15 -1
  66. package/dist/recommend/index.js +2 -2
  67. package/dist/schemas/index.d.ts +28 -6
  68. package/dist/schemas/index.js +15 -13
  69. package/dist/spatial/index.d.ts +0 -1
  70. package/dist/spatial/index.js +1 -1
  71. package/dist/storage/index.js +1 -1
  72. package/dist/{themeLibrary-CehcJhzz.d.ts → themeLibrary-RjVgLpUi.d.ts} +10 -11
  73. package/dist/timing/index.js +5 -3
  74. package/dist/transform/index.d.ts +35 -24
  75. package/dist/transform/index.js +9 -10
  76. package/dist/{types-DlAZ7MW4.d.ts → types-BwMPl2CB.d.ts} +1 -1
  77. package/dist/{types-D98li1gV.d.ts → types-C4kiRn_k.d.ts} +3 -1
  78. package/dist/versions/index.d.ts +2 -1
  79. package/dist/versions/index.js +2 -2
  80. package/package.json +6 -6
  81. package/src/__tests__/applyNarrationTiming.test.ts +190 -0
  82. package/src/__tests__/applyRenderStyle.test.ts +3 -3
  83. package/src/__tests__/asciiDiagramBattle.test.ts +246 -0
  84. package/src/__tests__/asciiDiagramDetect.test.ts +162 -0
  85. package/src/__tests__/asciiDiagramMapping.test.ts +38 -0
  86. package/src/__tests__/asciiDiagramParse.test.ts +233 -0
  87. package/src/__tests__/asciiDiagramPipeline.test.ts +273 -0
  88. package/src/__tests__/asciiDiagramRender.test.ts +162 -0
  89. package/src/__tests__/asciiDiagramRepair.test.ts +84 -0
  90. package/src/__tests__/asciiTimelineDetect.test.ts +157 -0
  91. package/src/__tests__/asciiTimelineParse.test.ts +205 -0
  92. package/src/__tests__/asciiTimelinePipeline.test.ts +349 -0
  93. package/src/__tests__/asciiTimelineRender.test.ts +376 -0
  94. package/src/__tests__/attrTokens.test.ts +5 -0
  95. package/src/__tests__/contentContainer.test.ts +43 -0
  96. package/src/__tests__/customTemplatesFrontmatter.test.ts +0 -15
  97. package/src/__tests__/dataTable.test.ts +25 -15
  98. package/src/__tests__/diagramBlock.test.ts +3 -3
  99. package/src/__tests__/diagramText.test.ts +26 -0
  100. package/src/__tests__/docToMarkdownMedia.test.ts +83 -0
  101. package/src/__tests__/drawingBlock.test.ts +3 -3
  102. package/src/__tests__/fixtures/asciiDiagramBattle.ts +512 -0
  103. package/src/__tests__/fixtures/asciiDiagrams.ts +287 -0
  104. package/src/__tests__/fixtures/asciiTimelines.ts +35 -0
  105. package/src/__tests__/fixtures/treeviewFixtures.ts +243 -0
  106. package/src/__tests__/getLayers.test.ts +32 -18
  107. package/src/__tests__/getLayersFallback.test.ts +8 -8
  108. package/src/__tests__/imageEditVersions.test.ts +2 -0
  109. package/src/__tests__/jsonForm.pointer.test.ts +47 -1
  110. package/src/__tests__/layoutBlock.test.ts +3 -3
  111. package/src/__tests__/majorApiTypes.test.ts +18 -0
  112. package/src/__tests__/markdown.test.ts +41 -0
  113. package/src/__tests__/matchFontFamily.test.ts +114 -0
  114. package/src/__tests__/materializeBlockLayers.test.ts +135 -0
  115. package/src/__tests__/materializeTestUtils.ts +17 -0
  116. package/src/__tests__/narrationAlign.test.ts +150 -0
  117. package/src/__tests__/narrationFeatures.test.ts +71 -0
  118. package/src/__tests__/narrationNuclei.test.ts +45 -0
  119. package/src/__tests__/narrationPacing.test.ts +189 -0
  120. package/src/__tests__/narrationScript.test.ts +149 -0
  121. package/src/__tests__/narrationSessionRepro.test.ts +62 -0
  122. package/src/__tests__/narrationSidecar.test.ts +88 -0
  123. package/src/__tests__/narrationSyllables.test.ts +65 -0
  124. package/src/__tests__/narrationTestSignals.ts +167 -0
  125. package/src/__tests__/narrationVad.test.ts +66 -0
  126. package/src/__tests__/resolveDocTheme.test.ts +16 -2
  127. package/src/__tests__/rootExports.test.ts +18 -0
  128. package/src/__tests__/scopedContentContainer.test.ts +20 -0
  129. package/src/__tests__/shapeGeometry.test.ts +12 -0
  130. package/src/__tests__/templateAnnotationParse.test.ts +10 -0
  131. package/src/__tests__/templateInputs.test.ts +69 -0
  132. package/src/__tests__/templateMetadata.test.ts +2 -1
  133. package/src/__tests__/templates.test.ts +143 -17
  134. package/src/__tests__/themeCompile.test.ts +49 -22
  135. package/src/__tests__/transformProvenance.test.ts +160 -0
  136. package/src/__tests__/transformV2.test.ts +40 -14
  137. package/src/__tests__/treeviewBattle.test.ts +76 -0
  138. package/src/__tests__/treeviewDetect.test.ts +168 -0
  139. package/src/__tests__/treeviewParse.test.ts +118 -0
  140. package/src/__tests__/treeviewPipeline.test.ts +183 -0
  141. package/src/__tests__/treeviewRender.test.ts +91 -0
  142. package/src/__tests__/versions.test.ts +44 -0
  143. package/src/doc/applyNarrationTiming.ts +244 -0
  144. package/src/doc/asciiDiagram/chars.ts +210 -0
  145. package/src/doc/asciiDiagram/detect.ts +175 -0
  146. package/src/doc/asciiDiagram/index.ts +38 -0
  147. package/src/doc/asciiDiagram/mapping.ts +225 -0
  148. package/src/doc/asciiDiagram/parse.ts +1095 -0
  149. package/src/doc/asciiDiagram/render.ts +963 -0
  150. package/src/doc/asciiDiagram/repair.ts +400 -0
  151. package/src/doc/asciiDiagram/types.ts +65 -0
  152. package/src/doc/asciiTimeline/detect.ts +138 -0
  153. package/src/doc/asciiTimeline/index.ts +26 -0
  154. package/src/doc/asciiTimeline/mapping.ts +43 -0
  155. package/src/doc/asciiTimeline/parse.ts +870 -0
  156. package/src/doc/asciiTimeline/render.ts +427 -0
  157. package/src/doc/asciiTimeline/types.ts +73 -0
  158. package/src/doc/asciiTimeline/wrappedFlow.ts +427 -0
  159. package/src/doc/audioMapping.ts +60 -46
  160. package/src/doc/customTemplatesFrontmatter.ts +0 -41
  161. package/src/doc/docToMarkdown.ts +70 -2
  162. package/src/doc/index.ts +76 -4
  163. package/src/doc/markdownToDoc.ts +135 -1
  164. package/src/doc/materializeBlockLayers.ts +304 -0
  165. package/src/doc/mediaAnnotations.ts +20 -0
  166. package/src/doc/resolveDocTheme.ts +8 -7
  167. package/src/doc/templateInputs.ts +185 -2
  168. package/src/doc/templates/__tests__/customTemplateRegistry.test.ts +22 -8
  169. package/src/doc/templates/diagramBlock.ts +237 -38
  170. package/src/doc/templates/fallbackBlock.ts +1 -1
  171. package/src/doc/templates/index.ts +134 -279
  172. package/src/doc/templates/inputDescriptors.ts +20 -1
  173. package/src/doc/templates/metadata.ts +10 -0
  174. package/src/doc/templates/persistentLayers.ts +12 -126
  175. package/src/doc/templates/registry.ts +88 -0
  176. package/src/doc/templates/templateNames.ts +22 -0
  177. package/src/doc/templates/timelineBlock.ts +613 -0
  178. package/src/doc/templates/treeBlock.ts +131 -0
  179. package/src/doc/treeview/chars.ts +72 -0
  180. package/src/doc/treeview/detect.ts +145 -0
  181. package/src/doc/treeview/index.ts +30 -0
  182. package/src/doc/treeview/mapping.ts +95 -0
  183. package/src/doc/treeview/parse.ts +201 -0
  184. package/src/doc/treeview/render.ts +41 -0
  185. package/src/doc/treeview/types.ts +43 -0
  186. package/src/doc/utils/applyRenderStyle.ts +2 -9
  187. package/src/doc/utils/diagramText.ts +78 -0
  188. package/src/doc/utils/shapeGeometry.ts +67 -5
  189. package/src/doc/validate.ts +1 -1
  190. package/src/generate/templateMapper.ts +7 -0
  191. package/src/imageEdit/export.ts +6 -5
  192. package/src/imageEdit/versionPaths.ts +13 -26
  193. package/src/imageEdit/versions.ts +21 -100
  194. package/src/index.ts +1 -0
  195. package/src/internal/immutable.ts +58 -0
  196. package/src/jsonForm/index.ts +9 -1
  197. package/src/jsonForm/pointer.ts +68 -23
  198. package/src/markdown/attrTokens.ts +7 -5
  199. package/src/markdown/index.ts +1 -0
  200. package/src/markdown/stringify.ts +4 -9
  201. package/src/markdown/utils.ts +36 -14
  202. package/src/narration/align.ts +514 -0
  203. package/src/narration/features.ts +231 -0
  204. package/src/narration/index.ts +86 -0
  205. package/src/narration/nuclei.ts +118 -0
  206. package/src/narration/pacing.ts +211 -0
  207. package/src/narration/script.ts +239 -0
  208. package/src/narration/session.ts +100 -0
  209. package/src/narration/sidecar.ts +187 -0
  210. package/src/narration/syllables.ts +85 -0
  211. package/src/narration/trace.ts +50 -0
  212. package/src/narration/types.ts +299 -0
  213. package/src/narration/vad.ts +131 -0
  214. package/src/recommend/numberHighlight.ts +25 -0
  215. package/src/recommend/templates.ts +108 -12
  216. package/src/schemas/BlockTemplates.ts +156 -7
  217. package/src/schemas/Doc.ts +72 -9
  218. package/src/schemas/Media.ts +12 -2
  219. package/src/schemas/Theme.ts +45 -25
  220. package/src/schemas/Viewport.ts +2 -2
  221. package/src/schemas/fontStacks.ts +79 -0
  222. package/src/schemas/themeCompile.ts +15 -0
  223. package/src/schemas/themeConstants.ts +2 -0
  224. package/src/schemas/themeLibrary.ts +25 -19
  225. package/src/schemas/themeValidator.ts +1 -1
  226. package/src/spatial/Geohash.ts +0 -3
  227. package/src/storage/ContentContainer.ts +17 -9
  228. package/src/storage/ScopedContentContainer.ts +45 -15
  229. package/src/transform/applyTransform.ts +5 -5
  230. package/src/transform/index.ts +3 -1
  231. package/src/transform/registry.ts +169 -49
  232. package/src/transform/templateSelector.ts +21 -2
  233. package/src/transform/timingAllocator.ts +152 -21
  234. package/src/transform/types.ts +14 -1
  235. package/src/versions/gitignore.ts +34 -0
  236. package/src/versions/index.ts +2 -0
  237. package/src/versions/operations.ts +18 -104
  238. package/src/versions/paths.ts +13 -28
  239. package/src/versions/snapshotStore.ts +202 -0
  240. package/src/versions/types.ts +3 -1
  241. package/dist/chunk-5TYS5V3G.js.map +0 -1
  242. package/dist/chunk-A4O7GIWE.js.map +0 -1
  243. package/dist/chunk-BEQODJWV.js.map +0 -1
  244. package/dist/chunk-DLXZMS5K.js.map +0 -1
  245. package/dist/chunk-GPLTCSXO.js.map +0 -1
  246. package/dist/chunk-HWVFJAAH.js.map +0 -1
  247. package/dist/chunk-IMSCRLLV.js.map +0 -1
  248. package/dist/chunk-JELIRVDP.js +0 -24
  249. package/dist/chunk-JELIRVDP.js.map +0 -1
  250. package/dist/chunk-KKNUBQ6Y.js.map +0 -1
  251. package/dist/chunk-LDQ2HJIX.js.map +0 -1
  252. package/dist/chunk-LTN6I7QW.js.map +0 -1
  253. package/dist/chunk-P3K7NLHF.js.map +0 -1
  254. package/dist/chunk-T5UK6YOB.js.map +0 -1
  255. package/dist/chunk-UY7KGQ5R.js +0 -192
  256. package/dist/chunk-UY7KGQ5R.js.map +0 -1
  257. package/dist/chunk-VLJJHDAC.js +0 -218
  258. package/dist/chunk-VLJJHDAC.js.map +0 -1
  259. package/dist/chunk-VWUFZ6ZG.js.map +0 -1
  260. package/dist/story/index.d.ts +0 -1801
  261. package/dist/story/index.js +0 -279
  262. package/src/doc/getLayers.ts +0 -195
  263. /package/dist/{story → narration}/index.js.map +0 -0
@@ -0,0 +1,884 @@
1
+ import {
2
+ expectedSyllablesAt,
3
+ wordPosAtExpectedSyllables
4
+ } from "./chunk-GF3BVNLU.js";
5
+
6
+ // src/narration/types.ts
7
+ var DEFAULT_FEATURE_CONFIG = Object.freeze({
8
+ frameSize: 1024,
9
+ hopSec: 0.02,
10
+ bandLowHz: 250,
11
+ bandHighHz: 3e3
12
+ });
13
+ var DEFAULT_VAD_CONFIG = Object.freeze({
14
+ warmupMs: 200,
15
+ alphaDown: 0.3,
16
+ alphaUp: 5e-3,
17
+ enterRatio: 3,
18
+ exitRatio: 2.5,
19
+ enterFrames: 2,
20
+ hangoverMs: 300,
21
+ noiseRebaselineSec: 1.5,
22
+ floorMin: 1e-9
23
+ });
24
+ var DEFAULT_NUCLEI_CONFIG = Object.freeze({
25
+ // Continuous reading blurs adjacent syllables together: a 10 Hz envelope
26
+ // over-smooths 5–7 Hz syllable structure and a 1.6× prominence gate
27
+ // rejects the shallow dips between connected syllables — both cause the
28
+ // detector to UNDER-count, which makes the prompter lag. A higher
29
+ // envelope cutoff preserves per-syllable peaks and a gentler prominence
30
+ // gate accepts them; the min-spacing gate still rejects true doublets.
31
+ envelopeHz: 16,
32
+ minInterOnsetMs: 110,
33
+ prominenceRatio: 1.3,
34
+ minRelPeak: 0.1,
35
+ peakRefTauSec: 3
36
+ });
37
+ var DEFAULT_PACING_CONFIG = Object.freeze({
38
+ baseWpm: 150,
39
+ voiceBlend: 0.6,
40
+ minRateMult: 0.5,
41
+ maxRateMult: 3,
42
+ rateEmaTauSec: 0.7,
43
+ velSlewTauSec: 0.15,
44
+ haltTauSec: 0.085,
45
+ kP: 0.14,
46
+ kI: 0.02,
47
+ intClamp: 4,
48
+ maxCorrection: 0.6,
49
+ sylWindowWords: 8,
50
+ resyncSyllables: 8
51
+ });
52
+ var DEFAULT_ALIGN_CONFIG = Object.freeze({
53
+ hopSec: 0.01,
54
+ gapMinSec: 0.35,
55
+ bandRadiusSec: 1.5,
56
+ bandRadiusNoTraceSec: 3,
57
+ matchTimeWeight: 1,
58
+ pauseToSylCost: 2,
59
+ sylToGapCost: 1.5,
60
+ deleteCost: 0.8,
61
+ insertCost: 0.8,
62
+ blockSnapWindowSec: 0.8,
63
+ tailPadSec: 0.4
64
+ });
65
+
66
+ // src/narration/features.ts
67
+ function highpassCoeffs(sampleRate, freqHz) {
68
+ const w0 = 2 * Math.PI * freqHz / sampleRate;
69
+ const cosW0 = Math.cos(w0);
70
+ const alpha = Math.sin(w0) / (2 * Math.SQRT1_2);
71
+ const a0 = 1 + alpha;
72
+ return {
73
+ b0: (1 + cosW0) / 2 / a0,
74
+ b1: -(1 + cosW0) / a0,
75
+ b2: (1 + cosW0) / 2 / a0,
76
+ a1: -2 * cosW0 / a0,
77
+ a2: (1 - alpha) / a0
78
+ };
79
+ }
80
+ function lowpassCoeffs(sampleRate, freqHz) {
81
+ const w0 = 2 * Math.PI * freqHz / sampleRate;
82
+ const cosW0 = Math.cos(w0);
83
+ const alpha = Math.sin(w0) / (2 * Math.SQRT1_2);
84
+ const a0 = 1 + alpha;
85
+ return {
86
+ b0: (1 - cosW0) / 2 / a0,
87
+ b1: (1 - cosW0) / a0,
88
+ b2: (1 - cosW0) / 2 / a0,
89
+ a1: -2 * cosW0 / a0,
90
+ a2: (1 - alpha) / a0
91
+ };
92
+ }
93
+ function createBandpass(sampleRate, lowHz = DEFAULT_FEATURE_CONFIG.bandLowHz, highHz = DEFAULT_FEATURE_CONFIG.bandHighHz) {
94
+ return {
95
+ coeffs: { hp: highpassCoeffs(sampleRate, lowHz), lp: lowpassCoeffs(sampleRate, highHz) },
96
+ hp: { x1: 0, x2: 0, y1: 0, y2: 0 },
97
+ lp: { x1: 0, x2: 0, y1: 0, y2: 0 }
98
+ };
99
+ }
100
+ function biquadRun(coeffs, state, input, output) {
101
+ let { x1, x2, y1, y2 } = state;
102
+ const { b0, b1, b2, a1, a2 } = coeffs;
103
+ for (let i = 0; i < input.length; i++) {
104
+ const x0 = input[i];
105
+ const y0 = b0 * x0 + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2;
106
+ output[i] = y0;
107
+ x2 = x1;
108
+ x1 = x0;
109
+ y2 = y1;
110
+ y1 = y0;
111
+ }
112
+ return { x1, x2, y1, y2 };
113
+ }
114
+ function bandpassRun(state, input, output) {
115
+ const hp = biquadRun(state.coeffs.hp, state.hp, input, output);
116
+ const lp = biquadRun(state.coeffs.lp, state.lp, output, output);
117
+ return { coeffs: state.coeffs, hp, lp };
118
+ }
119
+ function createFeatureState(sampleRate, config) {
120
+ const merged = { ...DEFAULT_FEATURE_CONFIG, ...config };
121
+ return {
122
+ sampleRate,
123
+ config: merged,
124
+ hopSamples: Math.max(1, Math.round(merged.hopSec * sampleRate)),
125
+ bandpass: createBandpass(sampleRate, merged.bandLowHz, merged.bandHighHz),
126
+ rawCarry: new Float32Array(0),
127
+ bandCarry: new Float32Array(0),
128
+ carryStartSample: 0
129
+ };
130
+ }
131
+ function concatF32(a, b) {
132
+ if (a.length === 0) return b;
133
+ const out = new Float32Array(a.length + b.length);
134
+ out.set(a, 0);
135
+ out.set(b, a.length);
136
+ return out;
137
+ }
138
+ function frameOf(raw, band, start, size, startSample, sampleRate) {
139
+ let sumSq = 0;
140
+ let bandSumSq = 0;
141
+ let crossings = 0;
142
+ let prev = raw[start];
143
+ for (let i = 0; i < size; i++) {
144
+ const r = raw[start + i];
145
+ const b = band[start + i];
146
+ sumSq += r * r;
147
+ bandSumSq += b * b;
148
+ if (i > 0 && (r >= 0 && prev < 0 || r < 0 && prev >= 0)) crossings++;
149
+ prev = r;
150
+ }
151
+ return {
152
+ tSec: (startSample + size / 2) / sampleRate,
153
+ rms: Math.sqrt(sumSq / size),
154
+ bandEnergy: bandSumSq / size,
155
+ zcr: size > 1 ? crossings / (size - 1) : 0
156
+ };
157
+ }
158
+ function featureStep(state, hop) {
159
+ const banded = new Float32Array(hop.length);
160
+ const bandpass = bandpassRun(state.bandpass, hop, banded);
161
+ const raw = concatF32(state.rawCarry, hop);
162
+ const band = concatF32(state.bandCarry, banded);
163
+ const { frameSize } = state.config;
164
+ const frames = [];
165
+ let cursor = 0;
166
+ while (raw.length - cursor >= frameSize) {
167
+ frames.push(
168
+ frameOf(raw, band, cursor, frameSize, state.carryStartSample + cursor, state.sampleRate)
169
+ );
170
+ cursor += state.hopSamples;
171
+ }
172
+ return {
173
+ state: {
174
+ ...state,
175
+ bandpass,
176
+ // slice(), not subarray(): the carry must OWN its memory. `raw` can
177
+ // alias the caller's hop buffer, and callers may recycle those
178
+ // (e.g. Chrome invalidates buffers transferred out of an
179
+ // AudioWorklet after the message handler returns) — a retained
180
+ // view would silently read zeros on the next step.
181
+ rawCarry: raw.slice(cursor),
182
+ bandCarry: band.slice(cursor),
183
+ carryStartSample: state.carryStartSample + cursor
184
+ },
185
+ frames
186
+ };
187
+ }
188
+ function extractFrameFeatures(pcm, sampleRate, config) {
189
+ const state = createFeatureState(sampleRate, config);
190
+ return featureStep(state, pcm).frames;
191
+ }
192
+
193
+ // src/narration/vad.ts
194
+ function createVadState(config) {
195
+ const merged = { ...DEFAULT_VAD_CONFIG, ...config };
196
+ return {
197
+ noiseFloor: merged.floorMin,
198
+ speaking: false,
199
+ enterStreak: 0,
200
+ subExitSince: null,
201
+ noDipSince: null,
202
+ warmupUntilSec: merged.warmupMs / 1e3,
203
+ seeded: false,
204
+ lastTSec: 0
205
+ };
206
+ }
207
+ function vadStep(state, frame, config) {
208
+ const c = { ...DEFAULT_VAD_CONFIG, ...config };
209
+ const energy = Math.max(0, frame.bandEnergy);
210
+ let floor = state.seeded ? state.noiseFloor : Math.max(energy, c.floorMin);
211
+ if (state.seeded) {
212
+ if (!state.speaking) {
213
+ const alpha = energy < floor ? c.alphaDown : c.alphaUp;
214
+ floor += (energy - floor) * alpha;
215
+ } else if (energy < floor) {
216
+ floor += (energy - floor) * c.alphaDown;
217
+ }
218
+ }
219
+ if (floor < c.floorMin) floor = c.floorMin;
220
+ let speaking = state.speaking;
221
+ let enterStreak = state.enterStreak;
222
+ let subExitSince = state.subExitSince;
223
+ let noDipSince = state.noDipSince;
224
+ if (frame.tSec < state.warmupUntilSec) {
225
+ speaking = false;
226
+ enterStreak = 0;
227
+ subExitSince = null;
228
+ noDipSince = null;
229
+ } else if (!speaking) {
230
+ noDipSince = null;
231
+ if (energy > floor * c.enterRatio) {
232
+ enterStreak += 1;
233
+ if (enterStreak >= c.enterFrames) {
234
+ speaking = true;
235
+ subExitSince = null;
236
+ noDipSince = frame.tSec;
237
+ }
238
+ } else {
239
+ enterStreak = 0;
240
+ }
241
+ } else {
242
+ if (energy >= floor * c.exitRatio) {
243
+ subExitSince = null;
244
+ } else {
245
+ if (subExitSince === null) subExitSince = frame.tSec;
246
+ if (frame.tSec - subExitSince >= c.hangoverMs / 1e3) {
247
+ speaking = false;
248
+ subExitSince = null;
249
+ noDipSince = null;
250
+ enterStreak = 0;
251
+ }
252
+ }
253
+ if (speaking) {
254
+ if (energy <= floor * c.enterRatio) {
255
+ noDipSince = frame.tSec;
256
+ } else if (noDipSince !== null && frame.tSec - noDipSince >= c.noiseRebaselineSec) {
257
+ floor = Math.max(energy, c.floorMin);
258
+ speaking = false;
259
+ subExitSince = null;
260
+ noDipSince = null;
261
+ enterStreak = 0;
262
+ }
263
+ }
264
+ }
265
+ return {
266
+ noiseFloor: floor,
267
+ speaking,
268
+ enterStreak,
269
+ subExitSince,
270
+ noDipSince,
271
+ warmupUntilSec: state.warmupUntilSec,
272
+ seeded: true,
273
+ lastTSec: frame.tSec
274
+ };
275
+ }
276
+
277
+ // src/narration/nuclei.ts
278
+ function createNucleiState(_config) {
279
+ return {
280
+ env: 0,
281
+ prevEnv: 0,
282
+ prevPrevEnv: 0,
283
+ prevTSec: 0,
284
+ valley: Number.POSITIVE_INFINITY,
285
+ lastOnsetTSec: Number.NEGATIVE_INFINITY,
286
+ peakRef: 0,
287
+ lastTSec: 0,
288
+ framesSeen: 0
289
+ };
290
+ }
291
+ function nucleiStep(state, frame, speaking, config) {
292
+ const c = { ...DEFAULT_NUCLEI_CONFIG, ...config };
293
+ const amp = Math.sqrt(Math.max(0, frame.bandEnergy));
294
+ const dt = state.framesSeen > 0 ? Math.max(0, frame.tSec - state.lastTSec) : 0;
295
+ const env = state.framesSeen > 0 ? state.env + (amp - state.env) * (1 - Math.exp(-2 * Math.PI * c.envelopeHz * dt)) : amp;
296
+ const peakRef = Math.max(env, state.peakRef * Math.exp(-(dt || 0) / c.peakRefTauSec));
297
+ let onset = null;
298
+ let valley = state.framesSeen > 0 ? state.valley : env;
299
+ let lastOnsetTSec = state.lastOnsetTSec;
300
+ if (state.framesSeen >= 2 && state.prevEnv >= state.prevPrevEnv && state.prevEnv > env && speaking && state.prevTSec - lastOnsetTSec >= c.minInterOnsetMs / 1e3 && state.prevEnv >= valley * c.prominenceRatio && state.prevEnv >= peakRef * c.minRelPeak) {
301
+ onset = state.prevTSec;
302
+ lastOnsetTSec = state.prevTSec;
303
+ valley = state.prevEnv;
304
+ }
305
+ valley = Math.min(valley, env);
306
+ return {
307
+ state: {
308
+ env,
309
+ prevEnv: env,
310
+ prevPrevEnv: state.framesSeen > 0 ? state.prevEnv : env,
311
+ prevTSec: frame.tSec,
312
+ valley,
313
+ lastOnsetTSec,
314
+ peakRef,
315
+ lastTSec: frame.tSec,
316
+ framesSeen: state.framesSeen + 1
317
+ },
318
+ onset
319
+ };
320
+ }
321
+ function detectSyllableOnsets(frames, vadFlags, config) {
322
+ let state = createNucleiState(config);
323
+ const onsets = [];
324
+ for (let i = 0; i < frames.length; i++) {
325
+ const step = nucleiStep(state, frames[i], vadFlags[i] ?? false, config);
326
+ state = step.state;
327
+ if (step.onset !== null) onsets.push(step.onset);
328
+ }
329
+ return onsets;
330
+ }
331
+
332
+ // src/narration/pacing.ts
333
+ function createPacingState(startWordPos = 0) {
334
+ return {
335
+ wordPos: startWordPos,
336
+ velocityWps: 0,
337
+ emaSylPerSec: 0,
338
+ lastOnsetTSec: null,
339
+ cumDetectedSyl: 0,
340
+ anchorExpectedSyl: 0,
341
+ errIntegral: 0,
342
+ silenceSec: 0,
343
+ lastTSec: null,
344
+ halted: true
345
+ };
346
+ }
347
+ function clamp(value, min, max) {
348
+ return value < min ? min : value > max ? max : value;
349
+ }
350
+ function localSyllablesPerWord(script, wordPos, windowWords) {
351
+ const n = script.tokens.length;
352
+ if (n === 0) return 1.4;
353
+ const start = clamp(Math.floor(wordPos), 0, n - 1);
354
+ let sum = 0;
355
+ let count = 0;
356
+ for (let i = start; i < n && count < windowWords; i++) {
357
+ if (!script.tokens[i].spoken) continue;
358
+ sum += script.tokens[i].syllables;
359
+ count += 1;
360
+ }
361
+ return count > 0 ? Math.max(1, sum / count) : 1.4;
362
+ }
363
+ function pacingStep(state, tick, script, config) {
364
+ const c = { ...DEFAULT_PACING_CONFIG, ...config };
365
+ const baseWps = c.baseWpm / 60;
366
+ const dt = clamp(tick.tSec - (state.lastTSec ?? tick.tSec), 0, 0.25);
367
+ let emaSylPerSec = state.emaSylPerSec;
368
+ let lastOnsetTSec = state.lastOnsetTSec;
369
+ let cumDetectedSyl = state.cumDetectedSyl;
370
+ for (let k = 0; k < tick.onsets; k++) {
371
+ if (lastOnsetTSec !== null) {
372
+ const ioi = tick.tSec - lastOnsetTSec;
373
+ if (ioi > 0) {
374
+ const instRate = clamp(1 / ioi, 0.5, 12);
375
+ const alpha = 1 - Math.exp(-ioi / c.rateEmaTauSec);
376
+ emaSylPerSec = emaSylPerSec === 0 ? instRate : emaSylPerSec + (instRate - emaSylPerSec) * alpha;
377
+ }
378
+ }
379
+ lastOnsetTSec = tick.tSec;
380
+ cumDetectedSyl += 1;
381
+ }
382
+ const sylPerWordLocal = localSyllablesPerWord(script, state.wordPos, c.sylWindowWords);
383
+ const voiceWps = emaSylPerSec > 0 ? emaSylPerSec / sylPerWordLocal : baseWps;
384
+ const cruise = clamp(
385
+ c.voiceBlend * voiceWps + (1 - c.voiceBlend) * baseWps,
386
+ baseWps * c.minRateMult,
387
+ baseWps * c.maxRateMult
388
+ );
389
+ const err = expectedSyllablesAt(script, state.wordPos) - (state.anchorExpectedSyl + cumDetectedSyl);
390
+ let errIntegral = clamp(Math.min(0, state.errIntegral + err * dt), -c.intClamp, 0);
391
+ const boost = err < 0 ? clamp(c.kP * -err + c.kI * -errIntegral, 0, c.maxCorrection) : 0;
392
+ let velocityWps = state.velocityWps;
393
+ let silenceSec = state.silenceSec;
394
+ if (tick.speaking) {
395
+ silenceSec = 0;
396
+ const target = Math.max(0, cruise * (1 + boost));
397
+ velocityWps += (target - velocityWps) * (1 - Math.exp(-dt / c.velSlewTauSec));
398
+ } else {
399
+ silenceSec += dt;
400
+ velocityWps *= Math.exp(-dt / c.haltTauSec);
401
+ if (velocityWps < 0.02) velocityWps = 0;
402
+ }
403
+ let wordPos = state.wordPos;
404
+ if (err < -c.resyncSyllables) {
405
+ wordPos = Math.max(
406
+ wordPos,
407
+ wordPosAtExpectedSyllables(script, state.anchorExpectedSyl + cumDetectedSyl)
408
+ );
409
+ errIntegral = 0;
410
+ }
411
+ wordPos = clamp(wordPos + velocityWps * dt, 0, script.tokens.length);
412
+ return {
413
+ wordPos,
414
+ velocityWps,
415
+ emaSylPerSec,
416
+ lastOnsetTSec,
417
+ cumDetectedSyl,
418
+ anchorExpectedSyl: state.anchorExpectedSyl,
419
+ errIntegral,
420
+ silenceSec,
421
+ lastTSec: tick.tSec,
422
+ halted: !tick.speaking && velocityWps === 0
423
+ };
424
+ }
425
+ function reanchorPacing(state, wordPos, script) {
426
+ const pos = clamp(wordPos, 0, script.tokens.length);
427
+ return {
428
+ ...state,
429
+ wordPos: pos,
430
+ anchorExpectedSyl: expectedSyllablesAt(script, pos),
431
+ cumDetectedSyl: 0,
432
+ errIntegral: 0
433
+ };
434
+ }
435
+
436
+ // src/narration/session.ts
437
+ function createNarrationSession(sampleRate, script, config) {
438
+ const merged = config ?? {};
439
+ return {
440
+ script,
441
+ config: merged,
442
+ features: createFeatureState(sampleRate, merged.feature),
443
+ vad: createVadState(merged.vad),
444
+ nuclei: createNucleiState(merged.nuclei),
445
+ pacing: createPacingState(0),
446
+ lastFrame: null,
447
+ tSec: 0
448
+ };
449
+ }
450
+ function narrationSessionStep(state, hop) {
451
+ const { state: features, frames } = featureStep(state.features, hop);
452
+ let vad = state.vad;
453
+ let nuclei = state.nuclei;
454
+ let pacing = state.pacing;
455
+ let lastFrame = state.lastFrame;
456
+ for (const frame of frames) {
457
+ vad = vadStep(vad, frame, state.config.vad);
458
+ const nucleiResult = nucleiStep(nuclei, frame, vad.speaking, state.config.nuclei);
459
+ nuclei = nucleiResult.state;
460
+ pacing = pacingStep(
461
+ pacing,
462
+ { tSec: frame.tSec, speaking: vad.speaking, onsets: nucleiResult.onset !== null ? 1 : 0 },
463
+ state.script,
464
+ state.config.pacing
465
+ );
466
+ lastFrame = frame;
467
+ }
468
+ return {
469
+ ...state,
470
+ features,
471
+ vad,
472
+ nuclei,
473
+ pacing,
474
+ lastFrame,
475
+ tSec: state.tSec + hop.length / state.features.sampleRate
476
+ };
477
+ }
478
+ function reanchorSession(state, wordPos) {
479
+ return { ...state, pacing: reanchorPacing(state.pacing, wordPos, state.script) };
480
+ }
481
+
482
+ // src/narration/trace.ts
483
+ function traceWordPosAt(trace, tSec) {
484
+ const samples = trace.samples;
485
+ if (samples.length === 0) return 0;
486
+ const tMs = tSec * 1e3;
487
+ if (tMs <= samples[0].tMs) return samples[0].wordPos;
488
+ const last = samples[samples.length - 1];
489
+ if (tMs >= last.tMs) return last.wordPos;
490
+ let lo = 0;
491
+ let hi = samples.length - 1;
492
+ while (lo < hi) {
493
+ const mid = lo + hi + 1 >> 1;
494
+ if (samples[mid].tMs <= tMs) lo = mid;
495
+ else hi = mid - 1;
496
+ }
497
+ const a = samples[lo];
498
+ const b = samples[lo + 1];
499
+ const span = b.tMs - a.tMs;
500
+ if (span <= 0) return a.wordPos;
501
+ return a.wordPos + (tMs - a.tMs) / span * (b.wordPos - a.wordPos);
502
+ }
503
+ function downsampleTrace(trace, maxSamples) {
504
+ const samples = trace.samples;
505
+ if (maxSamples < 2 || samples.length <= maxSamples) return { samples: samples.slice() };
506
+ const out = [];
507
+ const stride = (samples.length - 1) / (maxSamples - 1);
508
+ for (let i = 0; i < maxSamples; i++) {
509
+ out.push(samples[Math.round(i * stride)]);
510
+ }
511
+ return { samples: out };
512
+ }
513
+
514
+ // src/narration/align.ts
515
+ var MAX_BAND_RADIUS_SEC = 60;
516
+ function alignNarration(input, config) {
517
+ const c = { ...DEFAULT_ALIGN_CONFIG, ...config };
518
+ const { pcm, sampleRate, script } = input;
519
+ const takeDuration = pcm.length / Math.max(1, sampleRate);
520
+ if (script.tokens.length === 0 || takeDuration <= 0) {
521
+ return { words: [], blocks: [], detectedSyllables: 0, cost: 0 };
522
+ }
523
+ const frames = extractFrameFeatures(pcm, sampleRate, { hopSec: c.hopSec });
524
+ const vadFlags = runVad(frames);
525
+ const onsets = detectSyllableOnsets(frames, vadFlags);
526
+ const gaps = findSilenceGaps(frames, vadFlags, c.gapMinSec);
527
+ const lastSpeechEnd = findLastSpeechEnd(frames, vadFlags);
528
+ if (onsets.length === 0) {
529
+ return proportionalAlignment(
530
+ script,
531
+ lastSpeechEnd > 0 ? lastSpeechEnd : takeDuration,
532
+ takeDuration,
533
+ c,
534
+ 0
535
+ );
536
+ }
537
+ const events = [
538
+ ...onsets.map((t) => ({ kind: "syl", tSec: t, startSec: t, endSec: t })),
539
+ ...gaps
540
+ ].sort((a, b) => a.tSec - b.tSec);
541
+ const bandRadius = Math.min(
542
+ MAX_BAND_RADIUS_SEC,
543
+ input.trace && input.trace.samples.length >= 2 ? c.bandRadiusSec : Math.max(c.bandRadiusNoTraceSec, 0.1 * takeDuration)
544
+ );
545
+ const slots = buildExpectedSlots(script, takeDuration, input.trace);
546
+ const { matchedEventBySlot, cost } = bandedDtw(slots, events, bandRadius, c);
547
+ const words = deriveWordTimings(script, slots, events, matchedEventBySlot);
548
+ const blocks = deriveBlockRanges(script, words, gaps, takeDuration, lastSpeechEnd, c);
549
+ return { words, blocks, detectedSyllables: onsets.length, cost };
550
+ }
551
+ function runVad(frames) {
552
+ let vad = createVadState();
553
+ const flags = new Array(frames.length);
554
+ for (let i = 0; i < frames.length; i++) {
555
+ vad = vadStep(vad, frames[i]);
556
+ flags[i] = vad.speaking;
557
+ }
558
+ return flags;
559
+ }
560
+ function findSilenceGaps(frames, vadFlags, gapMinSec) {
561
+ const gaps = [];
562
+ let runStart = -1;
563
+ for (let i = 0; i <= frames.length; i++) {
564
+ const silent = i < frames.length ? !vadFlags[i] : false;
565
+ if (silent && runStart === -1) {
566
+ runStart = i;
567
+ } else if (!silent && runStart !== -1) {
568
+ const startSec = frames[runStart].tSec;
569
+ const endSec = frames[i - 1].tSec;
570
+ if (endSec - startSec >= gapMinSec) {
571
+ gaps.push({ kind: "gap", tSec: (startSec + endSec) / 2, startSec, endSec });
572
+ }
573
+ runStart = -1;
574
+ }
575
+ }
576
+ return gaps;
577
+ }
578
+ function findLastSpeechEnd(frames, vadFlags) {
579
+ for (let i = frames.length - 1; i >= 0; i--) {
580
+ if (vadFlags[i]) return frames[i].tSec;
581
+ }
582
+ return 0;
583
+ }
584
+ function buildExpectedSlots(script, takeDuration, trace) {
585
+ const slots = [];
586
+ for (let k = 0; k < script.tokens.length; k++) {
587
+ const token = script.tokens[k];
588
+ for (let s = 0; s < token.syllables; s++) {
589
+ slots.push({ kind: "syl", tokenIndex: k, slotIndex: s, priorSec: 0 });
590
+ }
591
+ if (token.pauseAfter >= 2) {
592
+ slots.push({ kind: "pause", tokenIndex: k, slotIndex: -1, priorSec: 0 });
593
+ }
594
+ }
595
+ const useTrace = trace !== void 0 && trace.samples.length >= 2;
596
+ if (useTrace) {
597
+ assignTracePriors(slots, script, trace, takeDuration);
598
+ } else {
599
+ const total = Math.max(1, script.totalSyllables);
600
+ for (const slot of slots) {
601
+ const cumBefore = slot.kind === "pause" ? script.cumulativeSyllables[slot.tokenIndex + 1] : script.cumulativeSyllables[slot.tokenIndex] + slot.slotIndex;
602
+ slot.priorSec = cumBefore / total * takeDuration;
603
+ }
604
+ }
605
+ let prev = 0;
606
+ for (const slot of slots) {
607
+ if (slot.priorSec < prev) slot.priorSec = prev;
608
+ if (slot.priorSec > takeDuration) slot.priorSec = takeDuration;
609
+ prev = slot.priorSec;
610
+ }
611
+ return slots;
612
+ }
613
+ function assignTracePriors(slots, script, trace, takeDuration) {
614
+ const times = [];
615
+ const positions = [];
616
+ let runningMax = 0;
617
+ for (const sample of trace.samples) {
618
+ runningMax = Math.max(runningMax, sample.wordPos);
619
+ times.push(sample.tMs / 1e3);
620
+ positions.push(runningMax);
621
+ }
622
+ let ptr = 0;
623
+ for (const slot of slots) {
624
+ const token = script.tokens[slot.tokenIndex];
625
+ const target = slot.kind === "pause" ? slot.tokenIndex + 1 : slot.tokenIndex + slot.slotIndex / Math.max(1, token.syllables);
626
+ while (ptr < positions.length - 1 && positions[ptr + 1] < target) ptr++;
627
+ let t;
628
+ if (positions[ptr] >= target) {
629
+ t = times[ptr];
630
+ } else if (ptr >= positions.length - 1) {
631
+ t = times[times.length - 1];
632
+ } else {
633
+ const span = positions[ptr + 1] - positions[ptr];
634
+ const frac = span > 0 ? (target - positions[ptr]) / span : 0;
635
+ t = times[ptr] + frac * (times[ptr + 1] - times[ptr]);
636
+ }
637
+ slot.priorSec = Math.min(Math.max(0, t), takeDuration);
638
+ }
639
+ }
640
+ function matchCost(slot, event, bandRadius, c) {
641
+ if (slot.kind === "syl") {
642
+ if (event.kind === "syl") {
643
+ return c.matchTimeWeight * Math.abs(event.tSec - slot.priorSec) / Math.max(1e-6, bandRadius);
644
+ }
645
+ return c.sylToGapCost;
646
+ }
647
+ return event.kind === "gap" ? 0 : c.pauseToSylCost;
648
+ }
649
+ function bandedDtw(slots, events, bandRadius, c) {
650
+ const N = slots.length;
651
+ const M = events.length;
652
+ const INF = Number.POSITIVE_INFINITY;
653
+ const eventTimes = events.map((e) => e.tSec);
654
+ const lo = new Int32Array(N + 1);
655
+ const hi = new Int32Array(N + 1);
656
+ for (let i2 = 1; i2 <= N; i2++) {
657
+ const prior = slots[i2 - 1].priorSec;
658
+ lo[i2] = lowerBound(eventTimes, prior - bandRadius);
659
+ hi[i2] = upperBound(eventTimes, prior + bandRadius);
660
+ }
661
+ lo[0] = 0;
662
+ hi[0] = N >= 1 ? hi[1] : M;
663
+ for (let i2 = 1; i2 <= N; i2++) {
664
+ if (lo[i2] < lo[i2 - 1]) lo[i2] = lo[i2 - 1];
665
+ if (lo[i2] > hi[i2 - 1] + 1) lo[i2] = hi[i2 - 1] + 1;
666
+ if (lo[i2] > M) lo[i2] = M;
667
+ if (hi[i2] < hi[i2 - 1]) hi[i2] = hi[i2 - 1];
668
+ if (hi[i2] < lo[i2]) hi[i2] = lo[i2];
669
+ if (hi[i2] > M) hi[i2] = M;
670
+ }
671
+ hi[N] = M;
672
+ if (lo[N] > M) lo[N] = M;
673
+ const ptrRows = new Array(N + 1);
674
+ let prevCost = new Float64Array(hi[0] - lo[0] + 1);
675
+ {
676
+ const row = new Uint8Array(hi[0] - lo[0] + 1);
677
+ for (let j2 = lo[0]; j2 <= hi[0]; j2++) {
678
+ prevCost[j2 - lo[0]] = j2 * c.insertCost;
679
+ row[j2 - lo[0]] = 2;
680
+ }
681
+ ptrRows[0] = row;
682
+ }
683
+ for (let i2 = 1; i2 <= N; i2++) {
684
+ const width = hi[i2] - lo[i2] + 1;
685
+ const cost = new Float64Array(width).fill(INF);
686
+ const ptr = new Uint8Array(width);
687
+ const slot = slots[i2 - 1];
688
+ for (let j2 = lo[i2]; j2 <= hi[i2]; j2++) {
689
+ const idx = j2 - lo[i2];
690
+ let best = INF;
691
+ let bestPtr = 1;
692
+ if (j2 >= 1 && j2 - 1 >= lo[i2 - 1] && j2 - 1 <= hi[i2 - 1]) {
693
+ const v = prevCost[j2 - 1 - lo[i2 - 1]] + matchCost(slot, events[j2 - 1], bandRadius, c);
694
+ if (v < best) {
695
+ best = v;
696
+ bestPtr = 0;
697
+ }
698
+ }
699
+ if (j2 >= lo[i2 - 1] && j2 <= hi[i2 - 1]) {
700
+ const v = prevCost[j2 - lo[i2 - 1]] + c.deleteCost;
701
+ if (v < best) {
702
+ best = v;
703
+ bestPtr = 1;
704
+ }
705
+ }
706
+ if (j2 - 1 >= lo[i2]) {
707
+ const v = cost[idx - 1] + c.insertCost;
708
+ if (v < best) {
709
+ best = v;
710
+ bestPtr = 2;
711
+ }
712
+ }
713
+ cost[idx] = best;
714
+ ptr[idx] = bestPtr;
715
+ }
716
+ ptrRows[i2] = ptr;
717
+ prevCost = cost;
718
+ }
719
+ const matchedEventBySlot = new Int32Array(N).fill(-1);
720
+ let i = N;
721
+ let j = M;
722
+ const finalCost = prevCost[M - lo[N]] ?? 0;
723
+ while (i > 0 || j > 0) {
724
+ const row = ptrRows[i];
725
+ const idx = j - lo[i];
726
+ const move = idx >= 0 && idx < row.length ? row[idx] : i > 0 ? 1 : 2;
727
+ if (move === 0 && i > 0 && j > 0) {
728
+ matchedEventBySlot[i - 1] = j - 1;
729
+ i--;
730
+ j--;
731
+ } else if (move === 1 && i > 0) {
732
+ i--;
733
+ } else if (j > 0) {
734
+ j--;
735
+ } else if (i > 0) {
736
+ i--;
737
+ } else {
738
+ break;
739
+ }
740
+ }
741
+ return { matchedEventBySlot, cost: Number.isFinite(finalCost) ? finalCost : 0 };
742
+ }
743
+ function lowerBound(values, target) {
744
+ let lo = 0;
745
+ let hi = values.length;
746
+ while (lo < hi) {
747
+ const mid = lo + hi >> 1;
748
+ if (values[mid] < target) lo = mid + 1;
749
+ else hi = mid;
750
+ }
751
+ return lo;
752
+ }
753
+ function upperBound(values, target) {
754
+ let lo = 0;
755
+ let hi = values.length;
756
+ while (lo < hi) {
757
+ const mid = lo + hi >> 1;
758
+ if (values[mid] <= target) lo = mid + 1;
759
+ else hi = mid;
760
+ }
761
+ return lo;
762
+ }
763
+ function deriveWordTimings(script, slots, events, matchedEventBySlot) {
764
+ const n = script.tokens.length;
765
+ const raw = new Array(n);
766
+ for (let s = 0; s < slots.length; s++) {
767
+ const eventIdx = matchedEventBySlot[s];
768
+ if (eventIdx < 0) continue;
769
+ const slot = slots[s];
770
+ if (slot.kind !== "syl") continue;
771
+ if (raw[slot.tokenIndex] !== void 0) continue;
772
+ const event = events[eventIdx];
773
+ raw[slot.tokenIndex] = event.kind === "gap" ? event.endSec : event.tSec;
774
+ }
775
+ const words = new Array(n);
776
+ let prevKnownIdx = -1;
777
+ let prevKnownT = 0;
778
+ for (let k = 0; k < n; k++) {
779
+ if (raw[k] === void 0) continue;
780
+ const t = raw[k];
781
+ for (let u = prevKnownIdx + 1; u < k; u++) {
782
+ const frac = (u - prevKnownIdx) / (k - prevKnownIdx);
783
+ const base = prevKnownIdx >= 0 ? prevKnownT : t;
784
+ words[u] = {
785
+ tokenIndex: u,
786
+ tSec: base + (t - base) * (prevKnownIdx >= 0 ? frac : 0),
787
+ interpolated: true
788
+ };
789
+ }
790
+ words[k] = { tokenIndex: k, tSec: t, interpolated: false };
791
+ prevKnownIdx = k;
792
+ prevKnownT = t;
793
+ }
794
+ for (let u = prevKnownIdx + 1; u < n; u++) {
795
+ words[u] = { tokenIndex: u, tSec: prevKnownT, interpolated: true };
796
+ }
797
+ if (prevKnownIdx === -1) {
798
+ for (let u = 0; u < n; u++) words[u] = { tokenIndex: u, tSec: 0, interpolated: true };
799
+ }
800
+ let running = 0;
801
+ for (const w of words) {
802
+ running = Math.max(running, w.tSec);
803
+ w.tSec = running;
804
+ }
805
+ return words;
806
+ }
807
+ function deriveBlockRanges(script, words, gaps, takeDuration, lastSpeechEnd, c) {
808
+ const blocks = [];
809
+ const gapEnds = gaps.map((g) => g.endSec);
810
+ let prevStart = 0;
811
+ for (let b = 0; b < script.blocks.length; b++) {
812
+ const range = script.blocks[b];
813
+ const firstWord = words[range.tokenStart];
814
+ let startSec = b === 0 ? 0 : firstWord?.tSec ?? prevStart;
815
+ if (b > 0) {
816
+ const idx = upperBound(gapEnds, startSec) - 1;
817
+ if (idx >= 0 && startSec - gapEnds[idx] <= c.blockSnapWindowSec && gapEnds[idx] > prevStart) {
818
+ startSec = gapEnds[idx];
819
+ }
820
+ if (startSec < prevStart) startSec = prevStart;
821
+ }
822
+ blocks.push({
823
+ blockId: range.blockId,
824
+ ...range.heading !== void 0 ? { heading: range.heading } : {},
825
+ blockIndex: b,
826
+ charStart: range.charStart,
827
+ charEnd: range.charEnd,
828
+ startSec,
829
+ endSec: startSec
830
+ });
831
+ prevStart = startSec;
832
+ }
833
+ for (let b = 0; b < blocks.length - 1; b++) {
834
+ blocks[b].endSec = blocks[b + 1].startSec;
835
+ }
836
+ if (blocks.length > 0) {
837
+ const last = blocks[blocks.length - 1];
838
+ let end = Math.min(
839
+ takeDuration,
840
+ (lastSpeechEnd > 0 ? lastSpeechEnd : takeDuration) + c.tailPadSec
841
+ );
842
+ if (takeDuration - end <= 1) end = takeDuration;
843
+ last.endSec = Math.max(last.startSec, end);
844
+ }
845
+ return blocks;
846
+ }
847
+ function proportionalAlignment(script, effectiveDuration, takeDuration, c, detectedSyllables) {
848
+ const total = Math.max(1, script.totalSyllables);
849
+ const words = script.tokens.map((_, k) => ({
850
+ tokenIndex: k,
851
+ tSec: script.cumulativeSyllables[k] / total * effectiveDuration,
852
+ interpolated: true
853
+ }));
854
+ const blocks = deriveBlockRanges(script, words, [], takeDuration, effectiveDuration, c);
855
+ return { words, blocks, detectedSyllables, cost: 0 };
856
+ }
857
+
858
+ export {
859
+ DEFAULT_FEATURE_CONFIG,
860
+ DEFAULT_VAD_CONFIG,
861
+ DEFAULT_NUCLEI_CONFIG,
862
+ DEFAULT_PACING_CONFIG,
863
+ DEFAULT_ALIGN_CONFIG,
864
+ createBandpass,
865
+ bandpassRun,
866
+ createFeatureState,
867
+ featureStep,
868
+ extractFrameFeatures,
869
+ createVadState,
870
+ vadStep,
871
+ createNucleiState,
872
+ nucleiStep,
873
+ detectSyllableOnsets,
874
+ createPacingState,
875
+ pacingStep,
876
+ reanchorPacing,
877
+ createNarrationSession,
878
+ narrationSessionStep,
879
+ reanchorSession,
880
+ traceWordPosAt,
881
+ downsampleTrace,
882
+ alignNarration
883
+ };
884
+ //# sourceMappingURL=chunk-T3TI7GSZ.js.map