@kvman/kvvideo 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.
Files changed (242) hide show
  1. package/README.md +1 -1
  2. package/dist/api.d.ts +26 -0
  3. package/dist/api.d.ts.map +1 -1
  4. package/dist/brand/brand.d.ts +17 -0
  5. package/dist/brand/brand.d.ts.map +1 -0
  6. package/dist/brand/brand.js +44 -0
  7. package/dist/brand/brand.js.map +1 -0
  8. package/dist/browser/launch.d.ts +2 -0
  9. package/dist/browser/launch.d.ts.map +1 -1
  10. package/dist/browser/launch.js +7 -1
  11. package/dist/browser/launch.js.map +1 -1
  12. package/dist/compose/ass-file.d.ts +5 -0
  13. package/dist/compose/ass-file.d.ts.map +1 -1
  14. package/dist/compose/ass-file.js +32 -3
  15. package/dist/compose/ass-file.js.map +1 -1
  16. package/dist/compose/background.d.ts +23 -0
  17. package/dist/compose/background.d.ts.map +1 -0
  18. package/dist/compose/background.js +31 -0
  19. package/dist/compose/background.js.map +1 -0
  20. package/dist/compose/camera.d.ts +32 -0
  21. package/dist/compose/camera.d.ts.map +1 -0
  22. package/dist/compose/camera.js +98 -0
  23. package/dist/compose/camera.js.map +1 -0
  24. package/dist/compose/caption-font.d.ts +2 -0
  25. package/dist/compose/caption-font.d.ts.map +1 -1
  26. package/dist/compose/caption-font.js +2 -1
  27. package/dist/compose/caption-font.js.map +1 -1
  28. package/dist/compose/compose-drawing.d.ts +19 -0
  29. package/dist/compose/compose-drawing.d.ts.map +1 -0
  30. package/dist/compose/compose-drawing.js +38 -0
  31. package/dist/compose/compose-drawing.js.map +1 -0
  32. package/dist/compose/compose-input.d.ts +139 -0
  33. package/dist/compose/compose-input.d.ts.map +1 -1
  34. package/dist/compose/compose-input.js +48 -9
  35. package/dist/compose/compose-input.js.map +1 -1
  36. package/dist/compose/compose-plan.d.ts +36 -0
  37. package/dist/compose/compose-plan.d.ts.map +1 -0
  38. package/dist/compose/compose-plan.js +55 -0
  39. package/dist/compose/compose-plan.js.map +1 -0
  40. package/dist/compose/compose-size.d.ts +50 -0
  41. package/dist/compose/compose-size.d.ts.map +1 -0
  42. package/dist/compose/compose-size.js +72 -0
  43. package/dist/compose/compose-size.js.map +1 -0
  44. package/dist/compose/drawn-images.d.ts +19 -0
  45. package/dist/compose/drawn-images.d.ts.map +1 -0
  46. package/dist/compose/drawn-images.js +75 -0
  47. package/dist/compose/drawn-images.js.map +1 -0
  48. package/dist/compose/final-pass.d.ts +34 -0
  49. package/dist/compose/final-pass.d.ts.map +1 -0
  50. package/dist/compose/final-pass.js +106 -0
  51. package/dist/compose/final-pass.js.map +1 -0
  52. package/dist/compose/frame-layout.d.ts +30 -0
  53. package/dist/compose/frame-layout.d.ts.map +1 -0
  54. package/dist/compose/frame-layout.js +33 -0
  55. package/dist/compose/frame-layout.js.map +1 -0
  56. package/dist/compose/overlays.d.ts +20 -0
  57. package/dist/compose/overlays.d.ts.map +1 -0
  58. package/dist/compose/overlays.js +30 -0
  59. package/dist/compose/overlays.js.map +1 -0
  60. package/dist/compose/page-again.d.ts +26 -0
  61. package/dist/compose/page-again.d.ts.map +1 -0
  62. package/dist/compose/page-again.js +50 -0
  63. package/dist/compose/page-again.js.map +1 -0
  64. package/dist/compose/player-copies.d.ts +5 -1
  65. package/dist/compose/player-copies.d.ts.map +1 -1
  66. package/dist/compose/player-copies.js +19 -16
  67. package/dist/compose/player-copies.js.map +1 -1
  68. package/dist/compose/register-compose.d.ts.map +1 -1
  69. package/dist/compose/register-compose.js +106 -20
  70. package/dist/compose/register-compose.js.map +1 -1
  71. package/dist/compose/render.d.ts +13 -4
  72. package/dist/compose/render.d.ts.map +1 -1
  73. package/dist/compose/render.js +48 -22
  74. package/dist/compose/render.js.map +1 -1
  75. package/dist/compose/scene-args.d.ts +32 -7
  76. package/dist/compose/scene-args.d.ts.map +1 -1
  77. package/dist/compose/scene-args.js +77 -16
  78. package/dist/compose/scene-args.js.map +1 -1
  79. package/dist/compose/scene-look.d.ts +31 -0
  80. package/dist/compose/scene-look.d.ts.map +1 -0
  81. package/dist/compose/scene-look.js +46 -0
  82. package/dist/compose/scene-look.js.map +1 -0
  83. package/dist/compose/scene-plan.d.ts +0 -2
  84. package/dist/compose/scene-plan.d.ts.map +1 -1
  85. package/dist/compose/scene-plan.js +7 -5
  86. package/dist/compose/scene-plan.js.map +1 -1
  87. package/dist/compose/sound-effects.d.ts +23 -0
  88. package/dist/compose/sound-effects.d.ts.map +1 -0
  89. package/dist/compose/sound-effects.js +40 -0
  90. package/dist/compose/sound-effects.js.map +1 -0
  91. package/dist/compose/srt.d.ts +3 -0
  92. package/dist/compose/srt.d.ts.map +1 -0
  93. package/dist/compose/srt.js +10 -0
  94. package/dist/compose/srt.js.map +1 -0
  95. package/dist/compose/transitions.d.ts +16 -0
  96. package/dist/compose/transitions.d.ts.map +1 -0
  97. package/dist/compose/transitions.js +38 -0
  98. package/dist/compose/transitions.js.map +1 -0
  99. package/dist/connector.d.ts +67 -14
  100. package/dist/connector.d.ts.map +1 -1
  101. package/dist/connector.js +27 -8
  102. package/dist/connector.js.map +1 -1
  103. package/dist/files/sidecar.d.ts +209 -0
  104. package/dist/files/sidecar.d.ts.map +1 -0
  105. package/dist/files/sidecar.js +66 -0
  106. package/dist/files/sidecar.js.map +1 -0
  107. package/dist/index.d.ts.map +1 -1
  108. package/dist/index.js +7 -1
  109. package/dist/index.js.map +1 -1
  110. package/dist/narration/alignment.d.ts +15 -0
  111. package/dist/narration/alignment.d.ts.map +1 -0
  112. package/dist/narration/alignment.js +21 -0
  113. package/dist/narration/alignment.js.map +1 -0
  114. package/dist/narration/captions.d.ts +22 -4
  115. package/dist/narration/captions.d.ts.map +1 -1
  116. package/dist/narration/captions.js +44 -16
  117. package/dist/narration/captions.js.map +1 -1
  118. package/dist/narration/join-args.d.ts +3 -0
  119. package/dist/narration/join-args.d.ts.map +1 -0
  120. package/dist/narration/join-args.js +19 -0
  121. package/dist/narration/join-args.js.map +1 -0
  122. package/dist/narration/narration-cache.d.ts +25 -0
  123. package/dist/narration/narration-cache.d.ts.map +1 -0
  124. package/dist/narration/narration-cache.js +36 -0
  125. package/dist/narration/narration-cache.js.map +1 -0
  126. package/dist/narration/piper.d.ts +2 -6
  127. package/dist/narration/piper.d.ts.map +1 -1
  128. package/dist/narration/piper.js +9 -15
  129. package/dist/narration/piper.js.map +1 -1
  130. package/dist/narration/pronunciations.d.ts +11 -0
  131. package/dist/narration/pronunciations.d.ts.map +1 -0
  132. package/dist/narration/pronunciations.js +40 -0
  133. package/dist/narration/pronunciations.js.map +1 -0
  134. package/dist/narration/register-narration.d.ts +8 -0
  135. package/dist/narration/register-narration.d.ts.map +1 -1
  136. package/dist/narration/register-narration.js +54 -26
  137. package/dist/narration/register-narration.js.map +1 -1
  138. package/dist/narration/register-voice.d.ts +27 -0
  139. package/dist/narration/register-voice.d.ts.map +1 -0
  140. package/dist/narration/register-voice.js +82 -0
  141. package/dist/narration/register-voice.js.map +1 -0
  142. package/dist/narration/sentence-speaker.d.ts +35 -0
  143. package/dist/narration/sentence-speaker.d.ts.map +1 -0
  144. package/dist/narration/sentence-speaker.js +44 -0
  145. package/dist/narration/sentence-speaker.js.map +1 -0
  146. package/dist/narration/speech-services.d.ts +23 -3
  147. package/dist/narration/speech-services.d.ts.map +1 -1
  148. package/dist/narration/speech-services.js +27 -10
  149. package/dist/narration/speech-services.js.map +1 -1
  150. package/dist/narration/voice-args.d.ts +3 -0
  151. package/dist/narration/voice-args.d.ts.map +1 -0
  152. package/dist/narration/voice-args.js +10 -0
  153. package/dist/narration/voice-args.js.map +1 -0
  154. package/dist/programs/media-duration.d.ts +12 -0
  155. package/dist/programs/media-duration.d.ts.map +1 -1
  156. package/dist/programs/media-duration.js +13 -0
  157. package/dist/programs/media-duration.js.map +1 -1
  158. package/dist/programs/run-program.d.ts +1 -0
  159. package/dist/programs/run-program.d.ts.map +1 -1
  160. package/dist/programs/run-program.js +1 -0
  161. package/dist/programs/run-program.js.map +1 -1
  162. package/dist/record/address.d.ts +1 -1
  163. package/dist/record/address.d.ts.map +1 -1
  164. package/dist/record/address.js +5 -7
  165. package/dist/record/address.js.map +1 -1
  166. package/dist/record/cursor-script.d.ts +2 -1
  167. package/dist/record/cursor-script.d.ts.map +1 -1
  168. package/dist/record/cursor-script.js +52 -14
  169. package/dist/record/cursor-script.js.map +1 -1
  170. package/dist/record/encoder-args.js +1 -1
  171. package/dist/record/motion.d.ts +19 -0
  172. package/dist/record/motion.d.ts.map +1 -0
  173. package/dist/record/motion.js +59 -0
  174. package/dist/record/motion.js.map +1 -0
  175. package/dist/record/narration-cues.d.ts +14 -0
  176. package/dist/record/narration-cues.d.ts.map +1 -0
  177. package/dist/record/narration-cues.js +12 -0
  178. package/dist/record/narration-cues.js.map +1 -0
  179. package/dist/record/page-ready.d.ts +4 -0
  180. package/dist/record/page-ready.d.ts.map +1 -0
  181. package/dist/record/page-ready.js +15 -0
  182. package/dist/record/page-ready.js.map +1 -0
  183. package/dist/record/page-route.d.ts +13 -0
  184. package/dist/record/page-route.d.ts.map +1 -0
  185. package/dist/record/page-route.js +64 -0
  186. package/dist/record/page-route.js.map +1 -0
  187. package/dist/record/pointer.d.ts +6 -7
  188. package/dist/record/pointer.d.ts.map +1 -1
  189. package/dist/record/pointer.js +11 -10
  190. package/dist/record/pointer.js.map +1 -1
  191. package/dist/record/recorder.d.ts +17 -7
  192. package/dist/record/recorder.d.ts.map +1 -1
  193. package/dist/record/recorder.js +48 -18
  194. package/dist/record/recorder.js.map +1 -1
  195. package/dist/record/register-record.d.ts.map +1 -1
  196. package/dist/record/register-record.js +47 -13
  197. package/dist/record/register-record.js.map +1 -1
  198. package/dist/record/run-step.d.ts +16 -3
  199. package/dist/record/run-step.d.ts.map +1 -1
  200. package/dist/record/run-step.js +43 -23
  201. package/dist/record/run-step.js.map +1 -1
  202. package/dist/record/screencast.js +1 -1
  203. package/dist/record/steps.d.ts +54 -1
  204. package/dist/record/steps.d.ts.map +1 -1
  205. package/dist/record/steps.js +34 -16
  206. package/dist/record/steps.js.map +1 -1
  207. package/dist/review/analysis.d.ts +19 -0
  208. package/dist/review/analysis.d.ts.map +1 -0
  209. package/dist/review/analysis.js +36 -0
  210. package/dist/review/analysis.js.map +1 -0
  211. package/dist/review/frames.d.ts +9 -0
  212. package/dist/review/frames.d.ts.map +1 -0
  213. package/dist/review/frames.js +28 -0
  214. package/dist/review/frames.js.map +1 -0
  215. package/dist/review/register-review.d.ts +24 -0
  216. package/dist/review/register-review.d.ts.map +1 -0
  217. package/dist/review/register-review.js +81 -0
  218. package/dist/review/register-review.js.map +1 -0
  219. package/dist/review/sheet-args.d.ts +11 -0
  220. package/dist/review/sheet-args.d.ts.map +1 -0
  221. package/dist/review/sheet-args.js +24 -0
  222. package/dist/review/sheet-args.js.map +1 -0
  223. package/dist/settings.d.ts +4 -0
  224. package/dist/settings.d.ts.map +1 -1
  225. package/dist/settings.js +4 -2
  226. package/dist/settings.js.map +1 -1
  227. package/dist/setup/ffmpeg-readiness.js +1 -1
  228. package/dist/setup/ffmpeg-readiness.js.map +1 -1
  229. package/dist/ui.d.ts.map +1 -1
  230. package/dist/ui.js +3 -2
  231. package/dist/ui.js.map +1 -1
  232. package/dist/web/components/player.css +1 -1
  233. package/dist/web/components/player.js +33 -21
  234. package/docs/guide.md +21 -9
  235. package/locales/ar.json +12 -1
  236. package/locales/en.json +12 -1
  237. package/package.json +11 -5
  238. package/sounds/SOURCES.md +13 -0
  239. package/sounds/click.wav +0 -0
  240. package/sounds/keys.wav +0 -0
  241. package/sounds/pop.wav +0 -0
  242. package/sounds/whoosh.wav +0 -0
@@ -1,8 +1,4 @@
1
1
  import { type Program } from '../programs/run-program.ts';
2
- export type PiperPrograms = {
3
- piper: Program;
4
- ffmpeg: Program;
5
- };
6
- /** Narrates `text` with the voice into `<folder>/narration.mp3`, and answers that file. */
7
- export declare function piperNarration(programs: PiperPrograms, voice: string, text: string, folder: string, signal: AbortSignal): Promise<string>;
2
+ /** Speaks `sentence` with the voice into the WAV file `wav`. */
3
+ export declare function piperSentence(piper: Program, voice: string, sentence: string, wav: string, speed: number, signal: AbortSignal): Promise<void>;
8
4
  //# sourceMappingURL=piper.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"piper.d.ts","sourceRoot":"","sources":["../../src/narration/piper.ts"],"names":[],"mappings":"AACA,OAAO,EAAyB,KAAK,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAKjF,MAAM,MAAM,aAAa,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC;AAMhE,2FAA2F;AAC3F,wBAAsB,cAAc,CAAC,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ/I"}
1
+ {"version":3,"file":"piper.d.ts","sourceRoot":"","sources":["../../src/narration/piper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAMjF,gEAAgE;AAChE,wBAAsB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAMnJ"}
@@ -1,19 +1,13 @@
1
- import path from 'node:path';
2
1
  import { lastLines, runProgram } from "../programs/run-program.js";
3
2
  import { kvvideoProblem } from "../problems.js";
4
- function failed(reason) {
5
- return kvvideoProblem('NARRATION_FAILED', `piper couldn't narrate: ${reason}`, { service: 'piper', reason });
6
- }
7
- /** Narrates `text` with the voice into `<folder>/narration.mp3`, and answers that file. */
8
- export async function piperNarration(programs, voice, text, folder, signal) {
9
- const wav = path.join(folder, 'narration.wav');
10
- const mp3 = path.join(folder, 'narration.mp3');
11
- const spoken = await runProgram(programs.piper, ['-m', voice, '-f', wav, '--', text], { signal });
12
- if (spoken.exitCode !== 0)
13
- throw failed(lastLines(spoken.output));
14
- const encoded = await runProgram(programs.ffmpeg, ['-hide_banner', '-loglevel', 'error', '-i', wav, '-codec:a', 'libmp3lame', '-b:a', '128k', '-y', mp3], { signal });
15
- if (encoded.exitCode !== 0)
16
- throw failed(lastLines(encoded.output));
17
- return mp3;
3
+ // Piper on this computer (plan 16 §16.6; ADR 0107, 22): it speaks one sentence into a WAV, slower or faster by its
4
+ // length scale, the inverse of the speed.
5
+ /** Speaks `sentence` with the voice into the WAV file `wav`. */
6
+ export async function piperSentence(piper, voice, sentence, wav, speed, signal) {
7
+ const spoken = await runProgram(piper, ['-m', voice, '-f', wav, '--length-scale', String(Math.round((1 / speed) * 1000) / 1000), '--', sentence], { signal });
8
+ if (spoken.exitCode !== 0) {
9
+ const reason = lastLines(spoken.output);
10
+ throw kvvideoProblem('NARRATION_FAILED', `piper couldn't narrate: ${reason}`, { service: 'piper', reason });
11
+ }
18
12
  }
19
13
  //# sourceMappingURL=piper.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"piper.js","sourceRoot":"","sources":["../../src/narration/piper.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,UAAU,EAAgB,MAAM,4BAA4B,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAMhD,SAAS,MAAM,CAAC,MAAc;IAC5B,OAAO,cAAc,CAAC,kBAAkB,EAAE,2BAA2B,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;AAC/G,CAAC;AAED,2FAA2F;AAC3F,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,QAAuB,EAAE,KAAa,EAAE,IAAY,EAAE,MAAc,EAAE,MAAmB;IAC5H,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC/C,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAClG,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC;QAAE,MAAM,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAClE,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,cAAc,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IACtK,IAAI,OAAO,CAAC,QAAQ,KAAK,CAAC;QAAE,MAAM,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IACpE,OAAO,GAAG,CAAC;AACb,CAAC"}
1
+ {"version":3,"file":"piper.js","sourceRoot":"","sources":["../../src/narration/piper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAgB,MAAM,4BAA4B,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,mHAAmH;AACnH,0CAA0C;AAE1C,gEAAgE;AAChE,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAc,EAAE,KAAa,EAAE,QAAgB,EAAE,GAAW,EAAE,KAAa,EAAE,MAAmB;IAClI,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,gBAAgB,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAC9J,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACxC,MAAM,cAAc,CAAC,kBAAkB,EAAE,2BAA2B,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IAC9G,CAAC;AACH,CAAC"}
@@ -0,0 +1,11 @@
1
+ export type Pronunciation = {
2
+ word: string;
3
+ say: string;
4
+ };
5
+ /** A sentence as spoken, and where each of its characters (by code point) is in it. */
6
+ export type Spoken = {
7
+ text: string;
8
+ spokenIndex(index: number, edge: 'first' | 'last'): number;
9
+ };
10
+ export declare function spokenOf(sentence: string, pronunciations: readonly Pronunciation[]): Spoken;
11
+ //# sourceMappingURL=pronunciations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pronunciations.d.ts","sourceRoot":"","sources":["../../src/narration/pronunciations.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,aAAa,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1D,uFAAuF;AACvF,MAAM,MAAM,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAA;CAAE,CAAC;AAUlG,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,SAAS,aAAa,EAAE,GAAG,MAAM,CA2B3F"}
@@ -0,0 +1,40 @@
1
+ // The pronunciation list (plan 16 §16.2; ADR 0115, 15): each word is replaced by what the voice should say, as a whole
2
+ // word, ignoring case. The captions keep the text as written, so each character of the sentence keeps where it is in
3
+ // what is spoken: a replaced word's characters span the whole replacement.
4
+ const escaped = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
5
+ function matcher(pronunciations) {
6
+ if (pronunciations.length === 0)
7
+ return undefined;
8
+ const words = [...pronunciations].map((entry) => entry.word).sort((a, b) => b.length - a.length);
9
+ return new RegExp(`(?<![\\p{L}\\p{N}_])(?:${words.map(escaped).join('|')})(?![\\p{L}\\p{N}_])`, 'giu');
10
+ }
11
+ export function spokenOf(sentence, pronunciations) {
12
+ const says = new Map(pronunciations.map((entry) => [entry.word.toLowerCase(), entry.say]));
13
+ const spans = [];
14
+ let spoken = '';
15
+ let spokenLength = 0;
16
+ let from = 0;
17
+ const keep = (text) => {
18
+ for (const character of text) {
19
+ spans.push([spokenLength, spokenLength]);
20
+ spoken += character;
21
+ spokenLength += 1;
22
+ }
23
+ };
24
+ const pattern = matcher(pronunciations);
25
+ const matches = pattern === undefined ? [] : [...sentence.matchAll(pattern)];
26
+ for (const match of matches) {
27
+ keep(sentence.slice(from, match.index));
28
+ const say = says.get(match[0].toLowerCase()) ?? match[0];
29
+ const sayLength = [...say].length;
30
+ const replaced = [...match[0]].length;
31
+ for (let index = 0; index < replaced; index += 1)
32
+ spans.push([spokenLength, spokenLength + sayLength - 1]);
33
+ spoken += say;
34
+ spokenLength += sayLength;
35
+ from = match.index + match[0].length;
36
+ }
37
+ keep(sentence.slice(from));
38
+ return { text: spoken, spokenIndex: (index, edge) => spans[index]?.[edge === 'first' ? 0 : 1] ?? 0 };
39
+ }
40
+ //# sourceMappingURL=pronunciations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pronunciations.js","sourceRoot":"","sources":["../../src/narration/pronunciations.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,8 @@ import { z, type Ctx } from '@kvman/sdk';
2
2
  export declare const narrateInputSchema: z.ZodObject<{
3
3
  text: z.ZodString;
4
4
  file: z.ZodString;
5
+ tone: z.ZodExactOptional<z.ZodString>;
6
+ speed: z.ZodExactOptional<z.ZodNumber>;
5
7
  replace: z.ZodExactOptional<z.ZodBoolean>;
6
8
  }, z.core.$strict>;
7
9
  export declare const narrateOutputSchema: z.ZodObject<{
@@ -11,7 +13,13 @@ export declare const narrateOutputSchema: z.ZodObject<{
11
13
  text: z.ZodString;
12
14
  startMs: z.ZodNumber;
13
15
  endMs: z.ZodNumber;
16
+ words: z.ZodExactOptional<z.ZodArray<z.ZodObject<{
17
+ text: z.ZodString;
18
+ startMs: z.ZodNumber;
19
+ endMs: z.ZodNumber;
20
+ }, z.core.$strip>>>;
14
21
  }, z.core.$strip>>;
22
+ reused: z.ZodNumber;
15
23
  }, z.core.$strip>;
16
24
  export declare function registerNarration(ctx: Ctx): void;
17
25
  //# sourceMappingURL=register-narration.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"register-narration.d.ts","sourceRoot":"","sources":["../../src/narration/register-narration.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,KAAK,GAAG,EAAE,MAAM,YAAY,CAAC;AAgBzC,eAAO,MAAM,kBAAkB;;;;kBAI7B,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;iBAI9B,CAAC;AAuBH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAwBhD"}
1
+ {"version":3,"file":"register-narration.d.ts","sourceRoot":"","sources":["../../src/narration/register-narration.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,KAAK,GAAG,EAAE,MAAM,YAAY,CAAC;AAoBzC,eAAO,MAAM,kBAAkB;;;;;;kBAM7B,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;iBAA8J,CAAC;AAuB/L,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAgDhD"}
@@ -1,70 +1,98 @@
1
- import { writeFileSync } from 'node:fs';
2
1
  import path from 'node:path';
3
2
  import { z } from '@kvman/sdk';
4
3
  import { answerPath } from "../files/answer-path.js";
5
4
  import { placeOutput, workFolder } from "../files/place-output.js";
5
+ import { sidecarCaptionSchema, sidecarOutputPath, writeSidecar } from "../files/sidecar.js";
6
6
  import { outputPath } from "../files/workspace-paths.js";
7
7
  import { mediaDuration } from "../programs/media-duration.js";
8
8
  import { programOf } from "../programs/programs.js";
9
- import { kvvideoProblem } from "../problems.js";
9
+ import { lastLines, runProgram } from "../programs/run-program.js";
10
+ import { invalid, kvvideoProblem } from "../problems.js";
10
11
  import { keySecrets } from "../setup/narration-readiness.js";
11
- import { timedCaptions } from "./captions.js";
12
- import { piperNarration } from "./piper.js";
13
- import { speak, speechRequest } from "./speech-services.js";
14
- // `video narrate` (plan 16 §16.6): the chosen service speaks the text into an MP3 in the workspace, and the answer has
15
- // its length and its captions. The output is checked before anything runs or is paid for.
12
+ import { captionsOf, sentencesOf } from "./captions.js";
13
+ import { joinNarrationArgs, sentenceGapMs } from "./join-args.js";
14
+ import { spokenOf } from "./pronunciations.js";
15
+ import { saySentence } from "./sentence-speaker.js";
16
+ // `video narrate` (plan 16 §16.6; ADR 0115, 15, 17, 39 to 41): the text is said sentence by sentence, each from the
17
+ // cache or the chosen service, then joined with 250 ms between them into an MP3 in the workspace, with its captions
18
+ // in a sidecar beside it. Everything that can fail before a service is paid for is checked first.
16
19
  export const narrateInputSchema = z.strictObject({
17
20
  text: z.string().min(1).max(4000).describe('What the narrator says: one scene, a few sentences.'),
18
21
  file: z.string().min(1).max(1000).describe('The .mp3 file to write, in the workspace.'),
22
+ tone: z.string().min(1).max(300).describe('How it is said, as "warm and upbeat"; OpenAI only.').exactOptional(),
23
+ speed: z.number().min(0.7).max(1.2).describe('How fast it is said: 0.7 to 1.2, 1 by default.').exactOptional(),
19
24
  replace: z.boolean().describe('Write over an existing file.').exactOptional(),
20
25
  });
21
- export const narrateOutputSchema = z.object({
22
- file: z.string(),
23
- durationMs: z.number().int().nonnegative(),
24
- captions: z.array(z.object({ text: z.string(), startMs: z.number().int(), endMs: z.number().int() })),
25
- });
26
+ export const narrateOutputSchema = z.object({ file: z.string(), durationMs: z.number().int().nonnegative(), captions: z.array(sidecarCaptionSchema), reused: z.number().int().nonnegative() });
26
27
  async function chosenService(ctx) {
27
28
  const service = await ctx.settings.get('kvvideo.narration.service');
28
29
  if (service === null)
29
30
  throw kvvideoProblem('NARRATION_NOT_SET', 'No narration service is chosen; set kvvideo.narration.service.', { setting: 'kvvideo.narration.service' });
30
31
  return service;
31
32
  }
32
- async function narrateInto(ctx, service, text, folder) {
33
+ async function voiceOf(ctx, input) {
34
+ const service = await chosenService(ctx);
33
35
  const choice = { voice: await ctx.settings.get('kvvideo.narration.voice'), model: await ctx.settings.get('kvvideo.narration.model') };
36
+ const shared = { service, choice, tone: input.tone ?? null, speed: input.speed ?? 1, fetch, signal: ctx.job.signal };
37
+ const ffprobe = await programOf(ctx, 'ffprobe');
34
38
  if (service === 'piper') {
35
39
  if (choice.voice === null)
36
40
  throw kvvideoProblem('NARRATION_NOT_SET', "Piper has no voice; set kvvideo.narration.voice to a voice's .onnx file.", { setting: 'kvvideo.narration.voice' });
37
- return piperNarration({ piper: await programOf(ctx, 'piper'), ffmpeg: await programOf(ctx, 'ffmpeg') }, choice.voice, text, folder, ctx.job.signal);
41
+ return { ...shared, key: null, programs: { ffprobe, piper: await programOf(ctx, 'piper') } };
38
42
  }
39
43
  const secret = keySecrets[service];
40
44
  const key = await ctx.secrets.get(secret);
41
45
  if (key === undefined)
42
46
  throw kvvideoProblem('NARRATION_KEY_MISSING', `The ${service} key is missing; add the secret ${secret} on kvvideo's extension page.`, { service, secret });
43
- const audio = await speak(service, speechRequest(service, text, choice, key), key, fetch, ctx.job.signal);
44
- const file = path.join(folder, 'narration.mp3');
45
- writeFileSync(file, audio);
46
- return file;
47
+ return { ...shared, key, programs: { ffprobe, piper: null } };
47
48
  }
48
49
  export function registerNarration(ctx) {
49
50
  ctx.registerCommand('kvvideo.narration.make', {
50
- description: 'Narrates text with the chosen text-to-speech service into an MP3 file in the workspace, with its length and captions.',
51
+ description: 'Narrates text sentence by sentence with the chosen text-to-speech service into an MP3 file in the workspace, with its length and captions.',
51
52
  input: narrateInputSchema,
52
53
  output: narrateOutputSchema,
53
54
  public: true,
54
55
  retries: 0,
55
- timeoutMs: 120_000,
56
+ timeoutMs: 300_000,
56
57
  handle: async (input) => {
57
58
  const root = ctx.job.workspace.path;
58
- const output = outputPath(root, input.file, '.mp3', input.replace === true);
59
- const service = await chosenService(ctx);
59
+ const replace = input.replace === true;
60
+ const output = outputPath(root, input.file, '.mp3', replace);
61
+ const sidecar = sidecarOutputPath(output, replace, input.file);
62
+ const voice = await voiceOf(ctx, input);
63
+ const ffmpeg = await programOf(ctx, 'ffmpeg');
64
+ const pronunciations = await ctx.settings.get('kvvideo.narration.pronunciations');
65
+ const sentences = sentencesOf(input.text).map((text) => ({ text, spoken: spokenOf(text, pronunciations) }));
66
+ if (sentences.length === 0)
67
+ throw invalid('The text has nothing to say.', { text: input.text });
60
68
  const work = workFolder();
61
69
  try {
62
- const made = await narrateInto(ctx, service, input.text, work.folder);
63
- const duration = await mediaDuration(await programOf(ctx, 'ffprobe'), made, ctx.job.signal);
70
+ const placed = [];
71
+ const files = [];
72
+ let startMs = 0;
73
+ let reused = 0;
74
+ for (const [index, sentence] of sentences.entries()) {
75
+ ctx.job.progress({ key: 'kvvideo.progress.sentence', params: { n: index + 1, of: sentences.length } });
76
+ const text = { spoken: sentence.spoken.text, previous: sentences[index - 1]?.spoken.text ?? null, next: sentences[index + 1]?.spoken.text ?? null };
77
+ const said = await saySentence(ctx, voice, text, work.folder, index + 1);
78
+ if (said.reused)
79
+ reused += 1;
80
+ files.push(said.file);
81
+ placed.push({ text: sentence.text, spoken: sentence.spoken, startMs, durationMs: said.durationMs, alignment: said.alignment });
82
+ startMs += said.durationMs + sentenceGapMs;
83
+ }
84
+ const made = path.join(work.folder, 'narration.mp3');
85
+ const joined = await runProgram(ffmpeg, joinNarrationArgs(files, made), { signal: ctx.job.signal });
86
+ if (joined.exitCode !== 0)
87
+ throw kvvideoProblem('NARRATION_FAILED', `ffmpeg couldn't join the narration: ${lastLines(joined.output)}`, { service: voice.service, reason: lastLines(joined.output) });
88
+ const duration = await mediaDuration(voice.programs.ffprobe, made, ctx.job.signal);
64
89
  if (!duration.ok)
65
- throw kvvideoProblem('NARRATION_FAILED', `ffprobe couldn't read the narration: ${duration.reason}`, { service, reason: duration.reason });
90
+ throw kvvideoProblem('NARRATION_FAILED', `ffprobe couldn't read the narration: ${duration.reason}`, { service: voice.service, reason: duration.reason });
91
+ const captions = captionsOf(placed, duration.ms);
92
+ const madeSidecar = writeSidecar(work.folder, { kind: 'narration', durationMs: duration.ms, captions });
66
93
  placeOutput(made, output);
67
- return { file: answerPath(root, output), durationMs: duration.ms, captions: timedCaptions(input.text, duration.ms) };
94
+ placeOutput(madeSidecar, sidecar);
95
+ return { file: answerPath(root, output), durationMs: duration.ms, captions, reused };
68
96
  }
69
97
  finally {
70
98
  work.remove();
@@ -1 +1 @@
1
- {"version":3,"file":"register-narration.js","sourceRoot":"","sources":["../../src/narration/register-narration.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"register-narration.js","sourceRoot":"","sources":["../../src/narration/register-narration.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import { z, type Ctx } from '@kvman/sdk';
2
+ export declare const takeExtensions: string[];
3
+ export declare const voiceInputSchema: z.ZodObject<{
4
+ lines: z.ZodArray<z.ZodObject<{
5
+ take: z.ZodString;
6
+ text: z.ZodString;
7
+ }, z.core.$strict>>;
8
+ file: z.ZodString;
9
+ clean: z.ZodExactOptional<z.ZodBoolean>;
10
+ replace: z.ZodExactOptional<z.ZodBoolean>;
11
+ }, z.core.$strict>;
12
+ export declare const voiceOutputSchema: z.ZodObject<{
13
+ file: z.ZodString;
14
+ durationMs: z.ZodNumber;
15
+ captions: z.ZodArray<z.ZodObject<{
16
+ text: z.ZodString;
17
+ startMs: z.ZodNumber;
18
+ endMs: z.ZodNumber;
19
+ words: z.ZodExactOptional<z.ZodArray<z.ZodObject<{
20
+ text: z.ZodString;
21
+ startMs: z.ZodNumber;
22
+ endMs: z.ZodNumber;
23
+ }, z.core.$strip>>>;
24
+ }, z.core.$strip>>;
25
+ }, z.core.$strip>;
26
+ export declare function registerVoice(ctx: Ctx): void;
27
+ //# sourceMappingURL=register-voice.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-voice.d.ts","sourceRoot":"","sources":["../../src/narration/register-voice.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,KAAK,GAAG,EAAE,MAAM,YAAY,CAAC;AAkBzC,eAAO,MAAM,cAAc,UAAoD,CAAC;AAEhF,eAAO,MAAM,gBAAgB;;;;;;;;kBAK3B,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;iBAAsH,CAAC;AAMrJ,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CA+C5C"}
@@ -0,0 +1,82 @@
1
+ import path from 'node:path';
2
+ import { z } from '@kvman/sdk';
3
+ import { answerPath } from "../files/answer-path.js";
4
+ import { placeOutput, workFolder } from "../files/place-output.js";
5
+ import { sidecarCaptionSchema, sidecarOutputPath, writeSidecar } from "../files/sidecar.js";
6
+ import { inputPath, outputPath } from "../files/workspace-paths.js";
7
+ import { mediaDuration } from "../programs/media-duration.js";
8
+ import { programOf } from "../programs/programs.js";
9
+ import { lastLines, runProgram } from "../programs/run-program.js";
10
+ import { kvvideoProblem } from "../problems.js";
11
+ import { captionsOf, sentencesOf } from "./captions.js";
12
+ import { joinNarrationArgs, sentenceGapMs } from "./join-args.js";
13
+ import { spokenOf } from "./pronunciations.js";
14
+ import { takeArgs } from "./voice-args.js";
15
+ // `video voice` (plan 16 §16.7; ADR 0115, 28 and 50): the person's takes of a scene, as `ask record` wrote them, each
16
+ // cleaned (or only converted), joined as `narrate` joins sentences into an MP3 of the workspace, with each take's
17
+ // captions lasting its own length; a sidecar beside it. The takes are never changed.
18
+ export const takeExtensions = ['.webm', '.ogg', '.m4a', '.mp4', '.mp3', '.wav'];
19
+ export const voiceInputSchema = z.strictObject({
20
+ lines: z.array(z.strictObject({ take: z.string().min(1).max(1000).describe('A take of the person, as ask record wrote it.'), text: z.string().min(1).max(500).describe('What is said in the take.') })).min(1).max(20),
21
+ file: z.string().min(1).max(1000).describe('The .mp3 file to write, in the workspace.'),
22
+ clean: z.boolean().describe('false only converts the takes; true (the default) removes noise, trims silence, and evens the level.').exactOptional(),
23
+ replace: z.boolean().describe('Write over an existing file.').exactOptional(),
24
+ });
25
+ export const voiceOutputSchema = z.object({ file: z.string(), durationMs: z.number().int().nonnegative(), captions: z.array(sidecarCaptionSchema) });
26
+ function failed(take, reason) {
27
+ return kvvideoProblem('RENDER_FAILED', `ffmpeg couldn't make ${take === null ? 'the joined voice' : `take ${String(take)}`}: ${reason}`, { take, reason });
28
+ }
29
+ export function registerVoice(ctx) {
30
+ ctx.registerCommand('kvvideo.voice.make', {
31
+ description: "Makes a narration from the person's own takes: each cleaned, joined into an MP3 file in the workspace, with its length and captions.",
32
+ input: voiceInputSchema,
33
+ output: voiceOutputSchema,
34
+ public: true,
35
+ retries: 0,
36
+ timeoutMs: 300_000,
37
+ handle: async (input) => {
38
+ const root = ctx.job.workspace.path;
39
+ const replace = input.replace === true;
40
+ const takes = input.lines.map((line) => inputPath(root, line.take, takeExtensions));
41
+ const output = outputPath(root, input.file, '.mp3', replace);
42
+ const sidecar = sidecarOutputPath(output, replace, input.file);
43
+ const [ffmpeg, ffprobe] = [await programOf(ctx, 'ffmpeg'), await programOf(ctx, 'ffprobe')];
44
+ const work = workFolder();
45
+ try {
46
+ const cleaned = [];
47
+ const placed = [];
48
+ let startMs = 0;
49
+ for (const [index, take] of takes.entries()) {
50
+ ctx.job.progress({ key: 'kvvideo.progress.take', params: { n: index + 1, of: takes.length } });
51
+ const made = path.join(work.folder, `take-${String(index + 1)}.wav`);
52
+ const run = await runProgram(ffmpeg, takeArgs(take, input.clean !== false, made), { signal: ctx.job.signal });
53
+ if (run.exitCode !== 0)
54
+ throw failed(index + 1, lastLines(run.output));
55
+ const length = await mediaDuration(ffprobe, made, ctx.job.signal);
56
+ if (!length.ok)
57
+ throw failed(index + 1, length.reason);
58
+ const text = sentencesOf(input.lines[index]?.text ?? '').join(' ');
59
+ placed.push({ text, spoken: spokenOf(text, []), startMs, durationMs: length.ms, alignment: null });
60
+ cleaned.push(made);
61
+ startMs += length.ms + sentenceGapMs;
62
+ }
63
+ const joined = path.join(work.folder, 'voice.mp3');
64
+ const run = await runProgram(ffmpeg, joinNarrationArgs(cleaned, joined), { signal: ctx.job.signal });
65
+ if (run.exitCode !== 0)
66
+ throw failed(null, lastLines(run.output));
67
+ const duration = await mediaDuration(ffprobe, joined, ctx.job.signal);
68
+ if (!duration.ok)
69
+ throw failed(null, duration.reason);
70
+ const captions = captionsOf(placed, duration.ms);
71
+ const madeSidecar = writeSidecar(work.folder, { kind: 'narration', durationMs: duration.ms, captions });
72
+ placeOutput(joined, output);
73
+ placeOutput(madeSidecar, sidecar);
74
+ return { file: answerPath(root, output), durationMs: duration.ms, captions };
75
+ }
76
+ finally {
77
+ work.remove();
78
+ }
79
+ },
80
+ });
81
+ }
82
+ //# sourceMappingURL=register-voice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-voice.js","sourceRoot":"","sources":["../../src/narration/register-voice.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import type { Ctx } from '@kvman/sdk';
2
+ import type { Program } from '../programs/run-program.ts';
3
+ import type { NarrationService } from '../settings.ts';
4
+ import type { Alignment } from './alignment.ts';
5
+ import { type Fetch } from './speech-services.ts';
6
+ export type Voice = {
7
+ service: NarrationService;
8
+ choice: {
9
+ voice: string | null;
10
+ model: string | null;
11
+ };
12
+ key: string | null;
13
+ tone: string | null;
14
+ speed: number;
15
+ programs: {
16
+ ffprobe: Program;
17
+ piper: Program | null;
18
+ };
19
+ fetch: Fetch;
20
+ signal: AbortSignal;
21
+ };
22
+ export type SaidSentence = {
23
+ file: string;
24
+ durationMs: number;
25
+ alignment: Alignment | null;
26
+ reused: boolean;
27
+ };
28
+ /** The sentence's spoken text and its neighbours, as sent. */
29
+ export type SentenceText = {
30
+ spoken: string;
31
+ previous: string | null;
32
+ next: string | null;
33
+ };
34
+ export declare function saySentence(ctx: Ctx, voice: Voice, text: SentenceText, folder: string, index: number): Promise<SaidSentence>;
35
+ //# sourceMappingURL=sentence-speaker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sentence-speaker.d.ts","sourceRoot":"","sources":["../../src/narration/sentence-speaker.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAE1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAGhD,OAAO,EAAwC,KAAK,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAKxF,MAAM,MAAM,KAAK,GAAG;IAClB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACvD,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAA;KAAE,CAAC;IACtD,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC;AAE9G,8DAA8D;AAC9D,MAAM,MAAM,YAAY,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAyB5F,wBAAsB,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAalI"}
@@ -0,0 +1,44 @@
1
+ import { readFileSync, writeFileSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { mediaDuration } from "../programs/media-duration.js";
4
+ import { kvvideoProblem } from "../problems.js";
5
+ import { cachedSentence, keepSentence, sentenceKey } from "./narration-cache.js";
6
+ import { piperSentence } from "./piper.js";
7
+ import { resolvedChoice, speak, speechRequest } from "./speech-services.js";
8
+ function failed(service, reason) {
9
+ return kvvideoProblem('NARRATION_FAILED', `${service} couldn't narrate: ${reason}`, { service, reason });
10
+ }
11
+ async function measured(voice, file) {
12
+ const duration = await mediaDuration(voice.programs.ffprobe, file, voice.signal);
13
+ if (!duration.ok)
14
+ throw failed(voice.service, `ffprobe couldn't read the narration: ${duration.reason}`);
15
+ return duration.ms;
16
+ }
17
+ async function spokenNow(voice, text, file) {
18
+ if (voice.service === 'piper') {
19
+ if (voice.choice.voice === null || voice.programs.piper === null)
20
+ throw failed('piper', 'no voice');
21
+ await piperSentence(voice.programs.piper, voice.choice.voice, text.spoken, file, voice.speed, voice.signal);
22
+ return { type: 'audio/wav', durationMs: await measured(voice, file), alignment: null };
23
+ }
24
+ const key = voice.key ?? '';
25
+ const request = speechRequest(voice.service, text.spoken, voice.choice, key, { tone: voice.tone, speed: voice.speed, previous: text.previous, next: text.next });
26
+ const speech = await speak(voice.service, request, text.spoken, key, voice.fetch, voice.signal);
27
+ writeFileSync(file, speech.audio);
28
+ return { type: 'audio/mpeg', durationMs: await measured(voice, file), alignment: speech.alignment };
29
+ }
30
+ export async function saySentence(ctx, voice, text, folder, index) {
31
+ const choice = voice.service === 'piper' ? voice.choice : resolvedChoice(voice.service, voice.choice);
32
+ const key = sentenceKey({ service: voice.service, voice: choice.voice, model: choice.model, tone: voice.tone, speed: voice.speed, spoken: text.spoken });
33
+ const cached = await cachedSentence(ctx, key);
34
+ if (cached !== undefined) {
35
+ const file = path.join(folder, `sentence-${String(index)}.${cached.type === 'audio/wav' ? 'wav' : 'mp3'}`);
36
+ writeFileSync(file, cached.audio);
37
+ return { file, durationMs: cached.durationMs, alignment: cached.alignment, reused: true };
38
+ }
39
+ const file = path.join(folder, `sentence-${String(index)}.${voice.service === 'piper' ? 'wav' : 'mp3'}`);
40
+ const said = await spokenNow(voice, text, file);
41
+ await keepSentence(ctx, key, { audio: readFileSync(file), ...said });
42
+ return { file, durationMs: said.durationMs, alignment: said.alignment, reused: false };
43
+ }
44
+ //# sourceMappingURL=sentence-speaker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sentence-speaker.js","sourceRoot":"","sources":["../../src/narration/sentence-speaker.ts"],"names":[],"mappings":"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"}
@@ -1,19 +1,39 @@
1
+ import { type Alignment } from './alignment.ts';
1
2
  export type WebService = 'elevenlabs' | 'openai';
2
3
  export type SpeechRequest = {
3
4
  url: string;
4
5
  headers: Record<string, string>;
5
6
  body: string;
6
7
  };
8
+ /** How a sentence is said: the tone (OpenAI only), the speed, and its neighbours (ElevenLabs only). */
9
+ export type Direction = {
10
+ tone: string | null;
11
+ speed: number;
12
+ previous: string | null;
13
+ next: string | null;
14
+ };
15
+ export type Speech = {
16
+ audio: Buffer;
17
+ alignment: Alignment | null;
18
+ };
7
19
  export type Fetch = (url: string, init: {
8
20
  method: 'POST';
9
21
  headers: Record<string, string>;
10
22
  body: string;
11
23
  signal: AbortSignal;
12
24
  }) => Promise<Response>;
25
+ /** The voice and model a service uses: the chosen ones, else its defaults. */
26
+ export declare function resolvedChoice(service: WebService, choice: {
27
+ voice: string | null;
28
+ model: string | null;
29
+ }): {
30
+ voice: string;
31
+ model: string;
32
+ };
13
33
  export declare function speechRequest(service: WebService, text: string, choice: {
14
34
  voice: string | null;
15
35
  model: string | null;
16
- }, key: string): SpeechRequest;
17
- /** The audio a service answers, or `kvvideo/NARRATION_FAILED`. */
18
- export declare function speak(service: WebService, request: SpeechRequest, key: string, fetch: Fetch, signal: AbortSignal): Promise<Buffer>;
36
+ }, key: string, direction: Direction): SpeechRequest;
37
+ /** The speech a service answers for `spoken`, or `kvvideo/NARRATION_FAILED`. */
38
+ export declare function speak(service: WebService, request: SpeechRequest, spoken: string, key: string, fetch: Fetch, signal: AbortSignal): Promise<Speech>;
19
39
  //# sourceMappingURL=speech-services.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"speech-services.d.ts","sourceRoot":"","sources":["../../src/narration/speech-services.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,QAAQ,CAAC;AAEjD,MAAM,MAAM,aAAa,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3F,MAAM,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AASrJ,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EAAE,GAAG,EAAE,MAAM,GAAG,aAAa,CAenJ;AAQD,kEAAkE;AAClE,wBAAsB,KAAK,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAiBxI"}
1
+ {"version":3,"file":"speech-services.d.ts","sourceRoot":"","sources":["../../src/narration/speech-services.ts"],"names":[],"mappings":"AAEA,OAAO,EAA0C,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAOxF,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,QAAQ,CAAC;AAEjD,MAAM,MAAM,aAAa,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3F,uGAAuG;AACvG,MAAM,MAAM,SAAS,GAAG;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAE7G,MAAM,MAAM,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAAA;CAAE,CAAC;AAEpE,MAAM,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AASrJ,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAE5I;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,aAAa,CAgBzK;AAoBD,gFAAgF;AAChF,wBAAsB,KAAK,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAiBxJ"}