diff --git a/remotion/src/folge08/laternenlied.ts b/remotion/src/folge08/laternenlied.ts new file mode 100644 index 0000000..1134d0b --- /dev/null +++ b/remotion/src/folge08/laternenlied.ts @@ -0,0 +1,71 @@ +import {SPRACHE, type Sprache} from '../baukasten/sprache'; +import type {LiedZeile} from '../lieder/karaoke'; + +// Laternenlied (Suno, eine Fassung mit DE, ES und FR nacheinander, 644eb1d0…, siehe +// drehbuecher/lieder/suno-auswahl.md). Je Sprache ist der Abschnitt mit der zweimal gesungenen Strophe +// ausgeschnitten nach public/musik/folge08/laternenlied-.mp3 (auf −14 LUFS, weich ein- und +// ausgeblendet). Wortzeiten am 06.11.2026 mit faster-whisper gemessen, hier relativ zum Schnittanfang. + +type Lied = {datei: string; dauer: number; zeilen: LiedZeile[]}; + +// Wortzeiten im ganzen Lied → relativ zum Schnittanfang +const ab = (start: number, zeilen: string[]): LiedZeile[] => + zeilen.map((z) => + z + .split(' ') + .map((w) => { + const [text, t] = w.split('@'); + return `${text}@${(Number(t) - start).toFixed(2)}`; + }) + .join(' '), + ); + +const LIEDER: Record = { + // Schnitt 9,0–34,0 s + de: { + datei: 'musik/folge08/laternenlied-de.mp3', + dauer: 25.0, + zeilen: ab(9.0, [ + 'Mein@10.96 kleines@11.34 Licht,@12.06 so@12.4 rund@12.84 und@13.16 warm,@13.52', + 'ich@13.96 trag@14.16 es@14.56 durch@14.9 den@15.26 Garten.@15.68', + 'Der@16.74 Mond@17.0 schaut@17.24 schon,@17.64 die@18.02 Sterne@18.44 auch,@18.94', + 'die@19.4 wollen@19.88 auf@20.38 uns@20.84 warten.@21.18', + 'Mein@22.36 kleines@22.6 Licht,@23.24 so@23.62 rund@23.98 und@24.36 warm,@24.66', + 'ich@25.14 trag@25.34 es@25.74 durch@26.06 den@26.4 Garten.@26.76', + 'Der@27.8 Mond@28.18 schaut@28.42 schon,@28.82 die@29.2 Sterne@29.6 auch,@30.1', + 'die@30.58 wollen@30.96 auf@31.56 uns@32.04 warten.@32.36', + ]), + }, + // Schnitt 43,4–67,2 s + es: { + datei: 'musik/folge08/laternenlied-es.mp3', + dauer: 23.8, + zeilen: ab(43.4, [ + 'Mi@44.6 farolito@45.04 redondo,@46.04', + 'lo@47.2 llevo@47.62 por@48.06 el@48.52 jardín.@48.8', + 'La@50.3 luna@50.5 y@51.0 las@51.25 estrellas@51.9', + 'nos@52.6 esperan@53.08 por@53.95 allí.@54.38', + 'Mi@55.3 farolito@56.05 redondo,@57.17', + 'lo@58.32 llevo@58.75 por@59.2 el@59.66 jardín.@60.04', + 'La@61.5 luna@61.68 y@62.18 las@62.43 estrellas@63.07', + 'nos@63.82 esperan@64.26 por@65.1 allí.@65.62', + ]), + }, + // Schnitt 76,8–101,2 s + fr: { + datei: 'musik/folge08/laternenlied-fr.mp3', + dauer: 24.4, + zeilen: ab(76.8, [ + 'Ma@78.36 lanterne@78.78 est@79.4 ronde@79.82 et@80.26 chaude,@80.58', + 'je@81.22 la@81.44 porte@81.96 au@82.34 grand@82.6 jardin.@82.82', + 'Lune@83.7 et@84.16 étoiles,@84.74 tout@85.36 là-haut,@85.64', + "elles@86.94 m'attendent@87.1 en@88.04 chemin.@88.4", + 'Ma@89.7 lanterne@89.92 est@90.4 ronde@90.74 et@91.28 chaude,@91.62', + 'je@92.24 la@92.62 porte@93.12 au@93.44 grand@93.74 jardin.@93.96', + 'Lune@95.14 et@95.52 étoiles,@95.84 tout@96.5 là-haut,@96.84', + "elles@98.08 m'attendent@98.24 en@99.24 chemin.@99.6", + ]), + }, +}; + +export const LATERNENLIED = LIEDER[SPRACHE]; diff --git a/remotion/src/folge08/szene3.tsx b/remotion/src/folge08/szene3.tsx index 24e0250..95f068f 100644 --- a/remotion/src/folge08/szene3.tsx +++ b/remotion/src/folge08/szene3.tsx @@ -6,11 +6,14 @@ import {Blende, Vignette, atmen, kamFahrt} from '../baukasten/helfer'; import {Funkeln} from '../baukasten/natur'; import {BODEN} from '../baukasten/orte'; import {Untertitel} from '../baukasten/untertitel'; +import {Karaoke} from '../lieder/karaoke'; +import {LATERNENLIED} from './laternenlied'; import {plane} from './plan'; import {Abenddunkel, GartenAbend, Laterne, laterneInNoasHand} from './teile'; // Szene 3: Es wird dunkel. Abendgarten, rechts die Hauswand mit erleuchtetem Fenster. -// Noa knipst das Lämpchen an (kein Feuer) und singt den Laternenvers; die Laterne pendelt sanft. +// Noa knipst das Lämpchen an (kein Feuer); dann erklingt das gesungene Laternenlied (Suno, je Sprache +// die zweimal gesungene Strophe, laternenlied.ts) mit Mitlesetext. Noa wiegt sich, die Laterne pendelt im Takt. // Pino tippelt zu ihr (baukasten/gang.ts, Füße rutschen nicht). Lulu steht schon an der Hauswand – // sie geht nicht durchs Bild, weil ihr Geh-Clip kein echtes Gehen ist. @@ -23,16 +26,9 @@ export const S3 = plane('s3', [ {id: 'klick', dauer: 1.4}, {id: 'e2', wer: 'ERZ'}, {id: 'p1', wer: 'PINO'}, - {id: 'v1', wer: 'NOA'}, - {id: 'v2', wer: 'NOA'}, - {id: 'v3', wer: 'NOA'}, - {id: 'v4', wer: 'NOA', pause: 0.6}, - {id: 'e3', wer: 'ERZ', pause: 0.8}, - {id: 'w1', wer: 'NOA'}, - {id: 'w2', wer: 'NOA'}, - {id: 'w3', wer: 'NOA'}, - {id: 'w4', wer: 'NOA'}, - {id: 'bild1', dauer: 1.8}, + {id: 'e3', wer: 'ERZ'}, // „Kannst du mitsingen?“ + {id: 'lied', dauer: LATERNENLIED.dauer}, + {id: 'bild1', dauer: 1.2}, ]); export const Szene3: React.FC = () => { @@ -41,12 +37,14 @@ export const Szene3: React.FC = () => { const an = z.klick.start + 12; const licht = interpolate(f, [an, an + 8], [0, 1], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'}); - const singt = f >= z.v1.start && f < z.w4.ende; - const schwung = singt ? Math.sin((f - z.v1.start) * 0.09) * 7 : Math.sin(f * 0.05) * 2; + // 84 BPM: die Laterne pendelt im Takt (ein Hin-und-her pro Takt) + const singt = f >= z.lied.start && f < z.lied.ende; + const takt = (f - z.lied.start) * ((2 * Math.PI * 84) / 60 / 30 / 2); + const schwung = singt ? Math.sin(takt) * 7 : Math.sin(f * 0.05) * 2; const lat = laterneInNoasHand(NOA_X); - // Pino kommt beim Vers dicht an Noas Stiefel (ganze Schritte, Füße bleiben am Boden) - const PINO = gang('pino', z.v1.start + 6, 300, NOA_X - 120, 2.5); + // Pino kommt beim Lied dicht an Noas Stiefel (ganze Schritte, Füße bleiben am Boden) + const PINO = gang('pino', z.lied.start + 20, 220, NOA_X - 300, 2.5); // bleibt links der Laterne stehen, verdeckt sie nicht const kam = kamFahrt(f, [ [0, 1000, 540, 1.0], @@ -55,7 +53,7 @@ export const Szene3: React.FC = () => { [z.e2.ende, 720, 640, 1.7], [z.p1.start + 6, lat.x, lat.y, 2.3], // „Mein Igel leuchtet!“ [z.p1.ende + 10, 820, 640, 1.35], - [z.w1.start, 900, 600, 1.2], + [z.lied.start + 90, 900, 600, 1.2], [dauer, 920, 600, 1.15], ]); @@ -71,7 +69,7 @@ export const Szene3: React.FC = () => { x={NOA_X} y={BODEN} stauchY={atmen(f, 2)} - drehung={singt ? Math.sin((f - z.v1.start) * 0.045) * 1.5 : 0} + drehung={singt ? Math.sin(takt) * 1.6 : 0} /> @@ -82,7 +80,7 @@ export const Szene3: React.FC = () => { pose={[ [0, 'pino_A6_blick-rechts'], [an, 'pino_A4_staunen'], - [z.v1.start, 'pino_A6_blick-rechts'], + [z.lied.start, 'pino_A6_blick-rechts'], [PINO.ende, 'pino_A3_lachen'], ]} x={PINO.x(f)} @@ -93,12 +91,9 @@ export const Szene3: React.FC = () => { - {/* Zwischenlösung bis zum gesungenen Laternenlied (Suno): Instrumental aus Folge 1 unter dem Vers */} - -