From 57322a95561d4700ea8fe98734caf952555137c3 Mon Sep 17 00:00:00 2001 From: HolgerHatGarKeineNode Date: Sat, 24 Jan 2026 13:49:15 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=B5=20Add=20PortalAudioManager=20compo?= =?UTF-8?q?nent=20for=20background=20music?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add PortalAudioManager.tsx with background music fade in/out - 1 second fade-in at the beginning - 3 second fade-out at the end - Base volume at 0.25 (25%) - Integrate PortalAudioManager into PortalPresentation - Add PortalOutroScene to PortalPresentation (was using placeholder) - Add comprehensive tests for PortalAudioManager (13 tests) - Tests for volume at various frames - Tests for fade-in/fade-out behavior - Integration tests Scene-specific SFX remain in individual scene components for better timing accuracy and maintainability. Co-Authored-By: Claude Opus 4.5 --- videos/src/PortalPresentation.tsx | 7 +- .../components/PortalAudioManager.test.tsx | 233 ++++++++++++++++++ videos/src/components/PortalAudioManager.tsx | 89 +++++++ 3 files changed, 328 insertions(+), 1 deletion(-) create mode 100644 videos/src/components/PortalAudioManager.test.tsx create mode 100644 videos/src/components/PortalAudioManager.tsx diff --git a/videos/src/PortalPresentation.tsx b/videos/src/PortalPresentation.tsx index 29ba2b4..ba0c3e4 100644 --- a/videos/src/PortalPresentation.tsx +++ b/videos/src/PortalPresentation.tsx @@ -7,6 +7,8 @@ import { MeetupShowcaseScene } from "./scenes/portal/MeetupShowcaseScene"; import { TopMeetupsScene } from "./scenes/portal/TopMeetupsScene"; import { ActivityFeedScene } from "./scenes/portal/ActivityFeedScene"; import { CallToActionScene } from "./scenes/portal/CallToActionScene"; +import { PortalOutroScene } from "./scenes/portal/PortalOutroScene"; +import { PortalAudioManager } from "./components/PortalAudioManager"; /** * PortalPresentation - Main composition for the Einundzwanzig Portal presentation video @@ -119,6 +121,9 @@ export const PortalPresentation: React.FC = () => { className="bg-gradient-to-br from-zinc-900 to-zinc-800" style={{ fontFamily: inconsolataFont }} > + {/* Background Music with fade in/out */} + + {/* Wallpaper Background */} { durationInFrames={sceneFrames.outro.duration} premountFor={fps} > - + ); diff --git a/videos/src/components/PortalAudioManager.test.tsx b/videos/src/components/PortalAudioManager.test.tsx new file mode 100644 index 0000000..70b6cab --- /dev/null +++ b/videos/src/components/PortalAudioManager.test.tsx @@ -0,0 +1,233 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { render, cleanup } from "@testing-library/react"; +import { PortalAudioManager } from "./PortalAudioManager"; + +/* eslint-disable @remotion/warn-native-media-tag */ +// Mock Remotion hooks with dynamic frame value +let mockCurrentFrame = 45; + +vi.mock("remotion", () => ({ + useCurrentFrame: vi.fn(() => mockCurrentFrame), + useVideoConfig: vi.fn(() => ({ + fps: 30, + width: 1920, + height: 1080, + durationInFrames: 2700, // 90 seconds at 30fps + })), + interpolate: vi.fn((value, inputRange, outputRange, options) => { + const [inMin, inMax] = inputRange; + const [outMin, outMax] = outputRange; + let progress = (value - inMin) / (inMax - inMin); + if (options?.extrapolateLeft === "clamp") { + progress = Math.max(0, progress); + } + if (options?.extrapolateRight === "clamp") { + progress = Math.min(1, progress); + } + return outMin + progress * (outMax - outMin); + }), + staticFile: vi.fn((path: string) => `/static/${path}`), +})); + +// Mock @remotion/media +vi.mock("@remotion/media", () => ({ + Audio: vi.fn(({ src, volume, loop }) => ( +