@tongue-core/project 0.1.0-rc.4 → 0.1.0-rc.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -109,13 +109,9 @@ export declare const extractFilm: (project: LoadedProject, options?: ExtractOpti
109
109
  readonly fit: "cover" | "contain";
110
110
  readonly blend?: "multiply" | "screen" | "overlay" | "soft-light" | "color-dodge" | "normal" | undefined;
111
111
  } | {
112
- readonly asset: string;
113
112
  readonly in: number;
114
113
  readonly out: number;
115
- readonly speed: number;
116
- readonly gain: number;
117
- readonly fadeIn: number;
118
- readonly fadeOut: number;
114
+ readonly kind: "audio";
119
115
  readonly id: string;
120
116
  readonly start: number;
121
117
  readonly duration: number;
@@ -124,7 +120,18 @@ export declare const extractFilm: (project: LoadedProject, options?: ExtractOpti
124
120
  readonly file: string;
125
121
  readonly line: number;
126
122
  } | null;
127
- readonly kind: "audio";
123
+ readonly asset: string;
124
+ readonly speed: number;
125
+ readonly gain: number;
126
+ readonly fadeIn: number;
127
+ readonly fadeOut: number;
128
+ readonly voice?: {
129
+ readonly words: readonly {
130
+ readonly text: string;
131
+ readonly start: number;
132
+ readonly end: number;
133
+ }[];
134
+ } | undefined;
128
135
  })[];
129
136
  readonly transitions: readonly ({
130
137
  readonly from: string;
@@ -107,13 +107,9 @@ export declare const assembleFilm: (doc: {
107
107
  readonly fit: "cover" | "contain";
108
108
  readonly blend?: "multiply" | "screen" | "overlay" | "soft-light" | "color-dodge" | "normal" | undefined;
109
109
  } | {
110
- readonly asset: string;
111
110
  readonly in: number;
112
111
  readonly out: number;
113
- readonly speed: number;
114
- readonly gain: number;
115
- readonly fadeIn: number;
116
- readonly fadeOut: number;
112
+ readonly kind: "audio";
117
113
  readonly id: string;
118
114
  readonly start: number;
119
115
  readonly duration: number;
@@ -122,7 +118,18 @@ export declare const assembleFilm: (doc: {
122
118
  readonly file: string;
123
119
  readonly line: number;
124
120
  } | null;
125
- readonly kind: "audio";
121
+ readonly asset: string;
122
+ readonly speed: number;
123
+ readonly gain: number;
124
+ readonly fadeIn: number;
125
+ readonly fadeOut: number;
126
+ readonly voice?: {
127
+ readonly words: readonly {
128
+ readonly text: string;
129
+ readonly start: number;
130
+ readonly end: number;
131
+ }[];
132
+ } | undefined;
126
133
  })[];
127
134
  readonly transitions: readonly ({
128
135
  readonly from: string;
@@ -322,13 +329,9 @@ export declare const sceneFilmDoc: (doc: {
322
329
  readonly fit: "cover" | "contain";
323
330
  readonly blend?: "multiply" | "screen" | "overlay" | "soft-light" | "color-dodge" | "normal" | undefined;
324
331
  } | {
325
- readonly asset: string;
326
332
  readonly in: number;
327
333
  readonly out: number;
328
- readonly speed: number;
329
- readonly gain: number;
330
- readonly fadeIn: number;
331
- readonly fadeOut: number;
334
+ readonly kind: "audio";
332
335
  readonly id: string;
333
336
  readonly start: number;
334
337
  readonly duration: number;
@@ -337,7 +340,18 @@ export declare const sceneFilmDoc: (doc: {
337
340
  readonly file: string;
338
341
  readonly line: number;
339
342
  } | null;
340
- readonly kind: "audio";
343
+ readonly asset: string;
344
+ readonly speed: number;
345
+ readonly gain: number;
346
+ readonly fadeIn: number;
347
+ readonly fadeOut: number;
348
+ readonly voice?: {
349
+ readonly words: readonly {
350
+ readonly text: string;
351
+ readonly start: number;
352
+ readonly end: number;
353
+ }[];
354
+ } | undefined;
341
355
  })[];
342
356
  readonly transitions: readonly ({
343
357
  readonly from: string;
@@ -534,13 +548,9 @@ export declare const sceneFilmDoc: (doc: {
534
548
  readonly fit: "cover" | "contain";
535
549
  readonly blend?: "multiply" | "screen" | "overlay" | "soft-light" | "color-dodge" | "normal" | undefined;
536
550
  } | {
537
- readonly asset: string;
538
551
  readonly in: number;
539
552
  readonly out: number;
540
- readonly speed: number;
541
- readonly gain: number;
542
- readonly fadeIn: number;
543
- readonly fadeOut: number;
553
+ readonly kind: "audio";
544
554
  readonly id: string;
545
555
  readonly start: number;
546
556
  readonly duration: number;
@@ -549,7 +559,18 @@ export declare const sceneFilmDoc: (doc: {
549
559
  readonly file: string;
550
560
  readonly line: number;
551
561
  } | null;
552
- readonly kind: "audio";
562
+ readonly asset: string;
563
+ readonly speed: number;
564
+ readonly gain: number;
565
+ readonly fadeIn: number;
566
+ readonly fadeOut: number;
567
+ readonly voice?: {
568
+ readonly words: readonly {
569
+ readonly text: string;
570
+ readonly start: number;
571
+ readonly end: number;
572
+ }[];
573
+ } | undefined;
553
574
  })[];
554
575
  readonly transitions: readonly ({
555
576
  readonly from: string;
@@ -1,4 +1,4 @@
1
- import { audio, Clip, Film, font, fromJson, image, SchemaError, Track, Transition, video, } from "@tongue-core/film";
1
+ import { audio, Clip, Film, font, fromJson, image, narration, SchemaError, Track, Transition, video, } from "@tongue-core/film";
2
2
  import { setOrigin } from "@tongue-core/film/model";
3
3
  import { Effect } from "effect";
4
4
  import { DocError } from "../errors.js";
@@ -62,7 +62,7 @@ function kitOf(doc, reader) {
62
62
  return doc.kit === null || !declaring ? undefined : reader.read(reader.scene(declaring.id, "kit").kitSchema, doc.kit, "kit");
63
63
  }
64
64
  const isKind = (asset, kind) => asset.kind === kind;
65
- function clipNode(clip, path, reader) {
65
+ function clipNode(clip, path, reader, fps) {
66
66
  const placed = { id: clip.id, from: clip.start, duration: clip.duration };
67
67
  switch (clip.kind) {
68
68
  case "scene": {
@@ -73,8 +73,15 @@ function clipNode(clip, path, reader) {
73
73
  return Clip({ ...placed, video: reader.asset("video", clip.asset, `${path}.asset`), in: clip.in, out: clip.out, speed: clip.speed, reverse: clip.reverse, freeze: clip.freeze, fit: clip.fit, opacity: clip.opacity, blend: clip.blend ?? "normal" });
74
74
  case "image":
75
75
  return Clip({ ...placed, image: reader.asset("image", clip.asset, `${path}.asset`), fit: clip.fit, opacity: clip.opacity, blend: clip.blend ?? "normal" });
76
- case "audio":
77
- return Clip({ ...placed, audio: reader.asset("audio", clip.asset, `${path}.asset`), in: clip.in, out: clip.out, speed: clip.speed, gain: clip.gain, fadeIn: clip.fadeIn, fadeOut: clip.fadeOut });
76
+ case "audio": {
77
+ const asset = reader.asset("audio", clip.asset, `${path}.asset`);
78
+ // Narration is the clip's words, in frames from its start: a voiceover over the file from `in`.
79
+ if (clip.voice !== undefined) {
80
+ const voice = narration(asset, { fps, frames: clip.duration, words: clip.voice.words }, clip.in);
81
+ return Clip({ ...placed, voice, gain: clip.gain, fadeIn: clip.fadeIn, fadeOut: clip.fadeOut });
82
+ }
83
+ return Clip({ ...placed, audio: asset, in: clip.in, out: clip.out, speed: clip.speed, gain: clip.gain, fadeIn: clip.fadeIn, fadeOut: clip.fadeOut });
84
+ }
78
85
  }
79
86
  }
80
87
  function transitionNode(t) {
@@ -93,11 +100,11 @@ function placedAt(node, origin) {
93
100
  return node;
94
101
  }
95
102
  /** A track's clips in order, each transition just before the clip it brings in. */
96
- function trackNode(track, path, reader) {
103
+ function trackNode(track, path, reader, fps) {
97
104
  const into = new Map(track.transitions.map((t) => [t.to, t]));
98
105
  const children = track.clips.flatMap((clip, i) => {
99
106
  const t = into.get(clip.id);
100
- const node = placedAt(clipNode(clip, `${path}.clips[${i}]`, reader), clip.origin);
107
+ const node = placedAt(clipNode(clip, `${path}.clips[${i}]`, reader, fps), clip.origin);
101
108
  return t ? [placedAt(transitionNode(t), t.origin), node] : [node];
102
109
  });
103
110
  return Track({ id: track.id, children });
@@ -117,7 +124,7 @@ export const assembleFilm = Effect.fn("assembleFilm")(function* (doc, scenes) {
117
124
  kit: kitOf(doc, reader),
118
125
  tail: doc.film.tail,
119
126
  assets,
120
- children: doc.tracks.map((track, i) => trackNode(track, `tracks[${i}]`, reader)),
127
+ children: doc.tracks.map((track, i) => trackNode(track, `tracks[${i}]`, reader, doc.film.fps)),
121
128
  });
122
129
  },
123
130
  catch: (e) => (e instanceof DocError ? e : docError("film.json couldn't be assembled", [{ path: "", message: e instanceof Error ? e.message : String(e) }])),
@@ -101,13 +101,9 @@ export declare const decodeFilmDoc: (json: unknown) => Effect.Effect<{
101
101
  readonly fit: "cover" | "contain";
102
102
  readonly blend?: "multiply" | "screen" | "overlay" | "soft-light" | "color-dodge" | "normal" | undefined;
103
103
  } | {
104
- readonly asset: string;
105
104
  readonly in: number;
106
105
  readonly out: number;
107
- readonly speed: number;
108
- readonly gain: number;
109
- readonly fadeIn: number;
110
- readonly fadeOut: number;
106
+ readonly kind: "audio";
111
107
  readonly id: string;
112
108
  readonly start: number;
113
109
  readonly duration: number;
@@ -116,7 +112,18 @@ export declare const decodeFilmDoc: (json: unknown) => Effect.Effect<{
116
112
  readonly file: string;
117
113
  readonly line: number;
118
114
  } | null;
119
- readonly kind: "audio";
115
+ readonly asset: string;
116
+ readonly speed: number;
117
+ readonly gain: number;
118
+ readonly fadeIn: number;
119
+ readonly fadeOut: number;
120
+ readonly voice?: {
121
+ readonly words: readonly {
122
+ readonly text: string;
123
+ readonly start: number;
124
+ readonly end: number;
125
+ }[];
126
+ } | undefined;
120
127
  })[];
121
128
  readonly transitions: readonly ({
122
129
  readonly from: string;
@@ -315,13 +322,9 @@ export declare const parseFilmDoc: (text: string) => Effect.Effect<{
315
322
  readonly fit: "cover" | "contain";
316
323
  readonly blend?: "multiply" | "screen" | "overlay" | "soft-light" | "color-dodge" | "normal" | undefined;
317
324
  } | {
318
- readonly asset: string;
319
325
  readonly in: number;
320
326
  readonly out: number;
321
- readonly speed: number;
322
- readonly gain: number;
323
- readonly fadeIn: number;
324
- readonly fadeOut: number;
327
+ readonly kind: "audio";
325
328
  readonly id: string;
326
329
  readonly start: number;
327
330
  readonly duration: number;
@@ -330,7 +333,18 @@ export declare const parseFilmDoc: (text: string) => Effect.Effect<{
330
333
  readonly file: string;
331
334
  readonly line: number;
332
335
  } | null;
333
- readonly kind: "audio";
336
+ readonly asset: string;
337
+ readonly speed: number;
338
+ readonly gain: number;
339
+ readonly fadeIn: number;
340
+ readonly fadeOut: number;
341
+ readonly voice?: {
342
+ readonly words: readonly {
343
+ readonly text: string;
344
+ readonly start: number;
345
+ readonly end: number;
346
+ }[];
347
+ } | undefined;
334
348
  })[];
335
349
  readonly transitions: readonly ({
336
350
  readonly from: string;
package/dist/doc/check.js CHANGED
@@ -25,6 +25,8 @@ function checkTrack(track, at, scenes, assets, out) {
25
25
  out.push({ path: `${path}.asset`, message: `there is no ${clip.kind} asset named "${clip.asset}" in assets` });
26
26
  if ((clip.kind === "video" || clip.kind === "audio") && !(clip.out > clip.in))
27
27
  out.push({ path: `${path}.out`, message: `must be after in (${clip.in} s)` });
28
+ if (clip.kind === "audio" && clip.voice !== undefined)
29
+ checkWords(clip.voice.words, clip.duration, `${path}.voice.words`, out);
28
30
  if (clip.kind === "scene" && (typeof clip.props !== "object" || clip.props === null || Array.isArray(clip.props)))
29
31
  out.push({ path: `${path}.props`, message: "must be an object" });
30
32
  previous = clip;
@@ -64,6 +66,19 @@ function checkTrack(track, at, scenes, assets, out) {
64
66
  out.push({ path: `${path}.frames`, message: `lasts ${t.frames} frames, longer than "${b.id}" (${b.duration})` });
65
67
  });
66
68
  }
69
+ /** Narration's words: each ends after it starts, inside the clip, and they come in order. */
70
+ function checkWords(words, duration, at, out) {
71
+ let previous = 0;
72
+ words.forEach((word, i) => {
73
+ if (word.end < word.start)
74
+ out.push({ path: `${at}[${i}].end`, message: `"${word.text}" ends at ${word.end}, before it starts (${word.start})` });
75
+ if (word.end > duration + EPSILON)
76
+ out.push({ path: `${at}[${i}].end`, message: `"${word.text}" ends at ${word.end}, after the clip (${duration} frames)` });
77
+ if (word.start < previous - EPSILON)
78
+ out.push({ path: `${at}[${i}].start`, message: `"${word.text}" starts at ${word.start}, before the word before it (${previous}); words come in order` });
79
+ previous = Math.max(previous, word.start);
80
+ });
81
+ }
67
82
  /** Every problem with how the document holds together (none: it does). */
68
83
  export function filmDocProblems(doc) {
69
84
  const out = [];
@@ -121,6 +121,22 @@ export declare const ImageClipDoc: Schema.Struct<{
121
121
  readonly kind: Schema.Literal<"image">;
122
122
  }>;
123
123
  export type ImageClipDoc = typeof ImageClipDoc.Type;
124
+ /** A spoken word: its text, and when it starts and ends, in frames from the clip's start. */
125
+ export declare const WordDoc: Schema.Struct<{
126
+ readonly text: Schema.String;
127
+ readonly start: Schema.Finite;
128
+ readonly end: Schema.Finite;
129
+ }>;
130
+ export type WordDoc = typeof WordDoc.Type;
131
+ /** The narration an audio clip plays (a `<Clip voice>`): its words. Left out of a plain audio clip. */
132
+ export declare const VoiceDoc: Schema.Struct<{
133
+ readonly words: Schema.$Array<Schema.Struct<{
134
+ readonly text: Schema.String;
135
+ readonly start: Schema.Finite;
136
+ readonly end: Schema.Finite;
137
+ }>>;
138
+ }>;
139
+ export type VoiceDoc = typeof VoiceDoc.Type;
124
140
  export declare const AudioClipDoc: Schema.Struct<{
125
141
  readonly asset: Schema.String;
126
142
  readonly in: Schema.Finite;
@@ -131,6 +147,14 @@ export declare const AudioClipDoc: Schema.Struct<{
131
147
  /** Frames. */
132
148
  readonly fadeIn: Schema.Finite;
133
149
  readonly fadeOut: Schema.Finite;
150
+ /** Its words, when it plays narration: a player can show what is being said. */
151
+ readonly voice: Schema.optionalKey<Schema.Struct<{
152
+ readonly words: Schema.$Array<Schema.Struct<{
153
+ readonly text: Schema.String;
154
+ readonly start: Schema.Finite;
155
+ readonly end: Schema.Finite;
156
+ }>>;
157
+ }>>;
134
158
  readonly id: Schema.String;
135
159
  readonly start: Schema.Finite;
136
160
  readonly duration: Schema.Finite;
@@ -200,6 +224,14 @@ export declare const ClipDoc: Schema.Union<readonly [Schema.Struct<{
200
224
  /** Frames. */
201
225
  readonly fadeIn: Schema.Finite;
202
226
  readonly fadeOut: Schema.Finite;
227
+ /** Its words, when it plays narration: a player can show what is being said. */
228
+ readonly voice: Schema.optionalKey<Schema.Struct<{
229
+ readonly words: Schema.$Array<Schema.Struct<{
230
+ readonly text: Schema.String;
231
+ readonly start: Schema.Finite;
232
+ readonly end: Schema.Finite;
233
+ }>>;
234
+ }>>;
203
235
  readonly id: Schema.String;
204
236
  readonly start: Schema.Finite;
205
237
  readonly duration: Schema.Finite;
@@ -478,6 +510,14 @@ export declare const TrackDoc: Schema.Struct<{
478
510
  /** Frames. */
479
511
  readonly fadeIn: Schema.Finite;
480
512
  readonly fadeOut: Schema.Finite;
513
+ /** Its words, when it plays narration: a player can show what is being said. */
514
+ readonly voice: Schema.optionalKey<Schema.Struct<{
515
+ readonly words: Schema.$Array<Schema.Struct<{
516
+ readonly text: Schema.String;
517
+ readonly start: Schema.Finite;
518
+ readonly end: Schema.Finite;
519
+ }>>;
520
+ }>>;
481
521
  readonly id: Schema.String;
482
522
  readonly start: Schema.Finite;
483
523
  readonly duration: Schema.Finite;
@@ -714,6 +754,14 @@ export declare const FilmDoc: Schema.Struct<{
714
754
  /** Frames. */
715
755
  readonly fadeIn: Schema.Finite;
716
756
  readonly fadeOut: Schema.Finite;
757
+ /** Its words, when it plays narration: a player can show what is being said. */
758
+ readonly voice: Schema.optionalKey<Schema.Struct<{
759
+ readonly words: Schema.$Array<Schema.Struct<{
760
+ readonly text: Schema.String;
761
+ readonly start: Schema.Finite;
762
+ readonly end: Schema.Finite;
763
+ }>>;
764
+ }>>;
717
765
  readonly id: Schema.String;
718
766
  readonly start: Schema.Finite;
719
767
  readonly duration: Schema.Finite;
@@ -98,6 +98,10 @@ export const VideoClipDoc = Schema.Struct({
98
98
  blend: Schema.optionalKey(Blend),
99
99
  });
100
100
  export const ImageClipDoc = Schema.Struct({ kind: Schema.Literal("image"), ...placement, asset: Name, fit: Fit, opacity: Unit, blend: Schema.optionalKey(Blend) });
101
+ /** A spoken word: its text, and when it starts and ends, in frames from the clip's start. */
102
+ export const WordDoc = Schema.Struct({ text: Schema.String, start: Frames, end: Frames });
103
+ /** The narration an audio clip plays (a `<Clip voice>`): its words. Left out of a plain audio clip. */
104
+ export const VoiceDoc = Schema.Struct({ words: Schema.Array(WordDoc) });
101
105
  export const AudioClipDoc = Schema.Struct({
102
106
  kind: Schema.Literal("audio"),
103
107
  ...placement,
@@ -110,6 +114,8 @@ export const AudioClipDoc = Schema.Struct({
110
114
  /** Frames. */
111
115
  fadeIn: Frames,
112
116
  fadeOut: Frames,
117
+ /** Its words, when it plays narration: a player can show what is being said. */
118
+ voice: Schema.optionalKey(VoiceDoc),
113
119
  });
114
120
  export const ClipDoc = Schema.Union([SceneClipDoc, VideoClipDoc, ImageClipDoc, AudioClipDoc]);
115
121
  // ---------------------------------------------------------------- transitions
@@ -106,13 +106,9 @@ export declare const filmDocOf: (plan: FilmPlan, background: string, scenes: rea
106
106
  readonly fit: "cover" | "contain";
107
107
  readonly blend?: "multiply" | "screen" | "overlay" | "soft-light" | "color-dodge" | "normal" | undefined;
108
108
  } | {
109
- readonly asset: string;
110
109
  readonly in: number;
111
110
  readonly out: number;
112
- readonly speed: number;
113
- readonly gain: number;
114
- readonly fadeIn: number;
115
- readonly fadeOut: number;
111
+ readonly kind: "audio";
116
112
  readonly id: string;
117
113
  readonly start: number;
118
114
  readonly duration: number;
@@ -121,7 +117,18 @@ export declare const filmDocOf: (plan: FilmPlan, background: string, scenes: rea
121
117
  readonly file: string;
122
118
  readonly line: number;
123
119
  } | null;
124
- readonly kind: "audio";
120
+ readonly asset: string;
121
+ readonly speed: number;
122
+ readonly gain: number;
123
+ readonly fadeIn: number;
124
+ readonly fadeOut: number;
125
+ readonly voice?: {
126
+ readonly words: readonly {
127
+ readonly text: string;
128
+ readonly start: number;
129
+ readonly end: number;
130
+ }[];
131
+ } | undefined;
125
132
  })[];
126
133
  readonly transitions: readonly ({
127
134
  readonly from: string;
@@ -38,10 +38,10 @@ function clipDoc(clip, ids) {
38
38
  return { kind: "video", ...placed, asset: s.asset.name, in: s.in, out: s.out, speed: s.speed, reverse: s.reverse, freeze: s.freeze, fit: s.fit, opacity: clip.opacity, ...blended };
39
39
  case "image":
40
40
  return { kind: "image", ...placed, asset: s.asset.name, fit: s.fit, opacity: clip.opacity, ...blended };
41
- case "audio":
42
- if (s.voice)
43
- throw new Refusal(clip.id, `Clip "${clip.id}" plays narration, which film.json can't hold yet; render this film from film.tsx.`);
44
- return { kind: "audio", ...placed, asset: s.asset.name, in: s.in, out: s.out, speed: s.speed, gain: s.gain, fadeIn: s.fadeIn, fadeOut: s.fadeOut };
41
+ case "audio": {
42
+ const voice = s.voice === null ? {} : { voice: { words: s.voice.words.map((w) => ({ text: w.text, start: w.start, end: w.end })) } };
43
+ return { kind: "audio", ...placed, asset: s.asset.name, in: s.in, out: s.out, speed: s.speed, gain: s.gain, fadeIn: s.fadeIn, fadeOut: s.fadeOut, ...voice };
44
+ }
45
45
  case "inline":
46
46
  throw new Refusal(clip.id, `Clip "${clip.id}" is inline markup, which film.json can't hold; make it a scene in scenes/.`);
47
47
  }
@@ -1,4 +1,4 @@
1
- export { FilmDoc, FilmFacts, TrackDoc, ClipDoc, SceneClipDoc, VideoClipDoc, ImageClipDoc, AudioClipDoc, TransitionDoc, FadeDoc, SlideDoc, MorphDoc, SceneDoc, AssetDoc, Origin, Id, EaseName, Fit, Direction, FILM_FORMAT, FILM_VERSION, formatFilmDoc, } from "./film-doc.js";
1
+ export { FilmDoc, FilmFacts, TrackDoc, ClipDoc, SceneClipDoc, VideoClipDoc, ImageClipDoc, AudioClipDoc, VoiceDoc, WordDoc, TransitionDoc, FadeDoc, SlideDoc, MorphDoc, SceneDoc, AssetDoc, Origin, Id, EaseName, Fit, Direction, FILM_FORMAT, FILM_VERSION, formatFilmDoc, } from "./film-doc.js";
2
2
  export { decodeFilmDoc, parseFilmDoc, filmDocProblems } from "./check.js";
3
3
  export { assembleFilm, assetsOf, assetLookup, sceneFilmDoc, type SceneModules } from "./assemble.js";
4
4
  export { filmDocOf, type ProjectScene } from "./from-plan.js";
package/dist/doc/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // @tongue-core/project/doc: film.json. Its schema and types (what timeline UIs program against), the
2
2
  // checks that it holds together, turning a planned film into it, and turning it back into a film.
3
3
  // Light on purpose: no compiler, nothing that touches files.
4
- export { FilmDoc, FilmFacts, TrackDoc, ClipDoc, SceneClipDoc, VideoClipDoc, ImageClipDoc, AudioClipDoc, TransitionDoc, FadeDoc, SlideDoc, MorphDoc, SceneDoc, AssetDoc, Origin, Id, EaseName, Fit, Direction, FILM_FORMAT, FILM_VERSION, formatFilmDoc, } from "./film-doc.js";
4
+ export { FilmDoc, FilmFacts, TrackDoc, ClipDoc, SceneClipDoc, VideoClipDoc, ImageClipDoc, AudioClipDoc, VoiceDoc, WordDoc, TransitionDoc, FadeDoc, SlideDoc, MorphDoc, SceneDoc, AssetDoc, Origin, Id, EaseName, Fit, Direction, FILM_FORMAT, FILM_VERSION, formatFilmDoc, } from "./film-doc.js";
5
5
  export { decodeFilmDoc, parseFilmDoc, filmDocProblems } from "./check.js";
6
6
  export { assembleFilm, assetsOf, assetLookup, sceneFilmDoc } from "./assemble.js";
7
7
  export { filmDocOf } from "./from-plan.js";
@@ -100,13 +100,9 @@ export declare const planFilmDoc: (doc: {
100
100
  readonly fit: "cover" | "contain";
101
101
  readonly blend?: "multiply" | "screen" | "overlay" | "soft-light" | "color-dodge" | "normal" | undefined;
102
102
  } | {
103
- readonly asset: string;
104
103
  readonly in: number;
105
104
  readonly out: number;
106
- readonly speed: number;
107
- readonly gain: number;
108
- readonly fadeIn: number;
109
- readonly fadeOut: number;
105
+ readonly kind: "audio";
110
106
  readonly id: string;
111
107
  readonly start: number;
112
108
  readonly duration: number;
@@ -115,7 +111,18 @@ export declare const planFilmDoc: (doc: {
115
111
  readonly file: string;
116
112
  readonly line: number;
117
113
  } | null;
118
- readonly kind: "audio";
114
+ readonly asset: string;
115
+ readonly speed: number;
116
+ readonly gain: number;
117
+ readonly fadeIn: number;
118
+ readonly fadeOut: number;
119
+ readonly voice?: {
120
+ readonly words: readonly {
121
+ readonly text: string;
122
+ readonly start: number;
123
+ readonly end: number;
124
+ }[];
125
+ } | undefined;
119
126
  })[];
120
127
  readonly transitions: readonly ({
121
128
  readonly from: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tongue-core/project",
3
- "version": "0.1.0-rc.4",
3
+ "version": "0.1.0-rc.6",
4
4
  "description": "Film projects: their shape, building one into film.json and a bundle per scene, film.json's schema and checks, and turning it back into a film. No Bun, Node or ffmpeg: runs in Bun, browsers and Workers.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -40,7 +40,7 @@
40
40
  "README.md"
41
41
  ],
42
42
  "dependencies": {
43
- "@tongue-core/film": "0.1.0-rc.4",
43
+ "@tongue-core/film": "0.1.0-rc.6",
44
44
  "sucrase": "3.35.1"
45
45
  },
46
46
  "peerDependencies": {