mirror of
https://github.com/R0m1k3/Socialflow.git
synced 2026-10-17 04:09:51 +02:00
fix(voix): plus de fins de phrases coupées ni de bruits parasites
Fins de phrases coupées : - Le fondu final ne commence plus avant la fin de la voix (il rognait la dernière phrase quand la vidéo finissait peu après) ; il se raccourcit au besoin. Même règle côté Python, aperçu et rendu Remotion - SRT : découpe au milieu de la zone la plus calme de chaque pause, mesurée sur l'enveloppe sonore (tranches de 10 ms), avec un seuil relatif au niveau de la voix qui respecte les fins douces ; 300 ms gardées après le dernier son Bruits parasites : - Normalisation de la voix en deux passes, en mode linéaire : le mode dynamique relevait souffles et bruits entre les phrases (mesuré : -64 dB avant, -75 dB après dans les pauses) - Réduction de bruit douce (afftdn), compresseur sans gain ajouté, fondus de 5 ms aux extrémités ; mix final moins « pompant » (LRA 20) - Voix Gemini vérifiée : les bruits ajoutés avant le premier ou après le dernier mot sont retirés Vérification de lecture comparée lettre à lettre : les petites erreurs de Whisper ne déclenchent plus de régénérations inutiles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018Ze4bs7tpF1KGWUk6ZZSZ4
This commit is contained in:
16 files changed
+338
-152
No files matched your search
+10
-1
@@ -24,7 +24,16 @@ describe("computeReelTiming", () => {
|
||||
|
||||
it("n'a ni logo ni fondu sans effet de fin", () => {
|
||||
const timing = computeReelTiming({ videoDuration: 12, hasOutro: true, endingEffect: false });
|
||||
expect(timing).toEqual({ total: 12, logoStart: null, fadeStart: null });
|
||||
expect(timing).toEqual({ total: 12, logoStart: null, fadeStart: null, fadeDuration: null });
|
||||
});
|
||||
});
|
||||
|
||||
describe("fondu final", () => {
|
||||
it("ne commence jamais avant la fin de la voix (mêmes valeurs que Python)", () => {
|
||||
const timing = computeReelTiming({ videoDuration: 4, voiceDuration: 5, hasOutro: false, endingEffect: true });
|
||||
expect(timing.total).toBe(7.8);
|
||||
expect(timing.fadeStart).toBeGreaterThanOrEqual(7);
|
||||
expect(timing.fadeDuration).toBeGreaterThanOrEqual(0.3);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+9
-1
@@ -32,12 +32,15 @@ const VOICE_TAIL = 0.8;
|
||||
const OUTRO_MIN = 2.5;
|
||||
const LOGO_SECONDS = 5;
|
||||
export const FADE_SECONDS = 2;
|
||||
/** Fondu raccourci quand la voix finit tard, mais jamais sec. */
|
||||
const MIN_FADE = 0.3;
|
||||
|
||||
export interface ReelTiming {
|
||||
total: number;
|
||||
/** Début de l'effet de fin (grand logo), ou null sans effet de fin. */
|
||||
logoStart: number | null;
|
||||
fadeStart: number | null;
|
||||
fadeDuration: number | null;
|
||||
}
|
||||
|
||||
/** Durée finale : la vidéo s'allonge (dernière image figée) si la voix dépasse. */
|
||||
@@ -57,10 +60,15 @@ export function computeReelTiming(input: {
|
||||
if (hasOutro) total = Math.max(total, speechEnd + OUTRO_MIN);
|
||||
}
|
||||
total = Math.round(total * 1000) / 1000;
|
||||
// Le fondu final ne commence jamais avant la fin de la voix
|
||||
let fadeStart = Math.max(0, total - FADE_SECONDS);
|
||||
if (input.voiceDuration) fadeStart = Math.max(fadeStart, Math.min(speechEnd + 0.15, total - MIN_FADE));
|
||||
fadeStart = Math.round(fadeStart * 1000) / 1000;
|
||||
return {
|
||||
total,
|
||||
logoStart: hasOutro ? Math.max(0, total - LOGO_SECONDS, speechEnd) : null,
|
||||
fadeStart: input.endingEffect ? Math.max(0, total - FADE_SECONDS) : null,
|
||||
fadeStart: input.endingEffect ? fadeStart : null,
|
||||
fadeDuration: input.endingEffect ? Math.max(MIN_FADE, total - fadeStart) : null,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user