From e22774372887a9435955c47cd74656979c537248 Mon Sep 17 00:00:00 2001 From: JorySeverijnse Date: Sat, 20 Dec 2025 15:50:04 +0100 Subject: [PATCH] Osciloscope integrated but video exported is broken --- package-lock.json | 22 +----------------- src/App.tsx | 2 ++ src/hooks/useOfflineVideoExport.ts | 36 ++++++++++++++++++++++-------- src/pages/Oscilloscope.tsx | 26 +++++++++++++++++++++ 4 files changed, 56 insertions(+), 30 deletions(-) create mode 100644 src/pages/Oscilloscope.tsx diff --git a/package-lock.json b/package-lock.json index c308a0b..db10636 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2370,7 +2370,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -3076,7 +3075,6 @@ "integrity": "sha512-bJFoMATwIGaxxx8VJPeM8TonI8t579oRvgAuT8zFugJsJZgzqv0Fu8Mhp68iecjzG7cnN3mO2dJQ5uUM2EFrgQ==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -3100,7 +3098,6 @@ "integrity": "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -3112,7 +3109,6 @@ "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "devOptional": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^18.0.0" } @@ -3169,7 +3165,6 @@ "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.38.0", "@typescript-eslint/types": "8.38.0", @@ -3402,7 +3397,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3607,7 +3601,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001726", "electron-to-chromium": "^1.5.173", @@ -3987,7 +3980,6 @@ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", "license": "MIT", - "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/kossnocorp" @@ -4082,8 +4074,7 @@ "version": "8.6.0", "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz", "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/embla-carousel-react": { "version": "8.6.0", @@ -4183,7 +4174,6 @@ "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -5496,7 +5486,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -5702,7 +5691,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -5729,7 +5717,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -5743,7 +5730,6 @@ "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.61.1.tgz", "integrity": "sha512-2vbXUFDYgqEgM2RcXcAT2PwDW/80QARi+PKmHy5q2KhuKvOlG8iIYgf7eIlIANR5trW9fJbP4r5aub3a4egsew==", "license": "MIT", - "peer": true, "engines": { "node": ">=18.0.0" }, @@ -6034,7 +6020,6 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -6349,7 +6334,6 @@ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", "license": "MIT", - "peer": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -6456,7 +6440,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -6520,7 +6503,6 @@ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6699,7 +6681,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.6.tgz", "integrity": "sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -6800,7 +6781,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, diff --git a/src/App.tsx b/src/App.tsx index 64207e4..7befdc1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -30,6 +30,7 @@ const Pacman = lazy(() => import("./pages/Pacman")); const Snake = lazy(() => import("./pages/Snake")); const Breakout = lazy(() => import("./pages/Breakout")); const Music = lazy(() => import("./pages/Music")); +const Oscilloscope = lazy(() => import("./pages/Oscilloscope")); const AIChat = lazy(() => import("./pages/AIChat")); const Achievements = lazy(() => import("./pages/Achievements")); const Credits = lazy(() => import("./pages/Credits")); @@ -125,6 +126,7 @@ const AppContent = () => { } /> } /> } /> + } /> } /> } /> } /> diff --git a/src/hooks/useOfflineVideoExport.ts b/src/hooks/useOfflineVideoExport.ts index bc7efae..f3d4083 100644 --- a/src/hooks/useOfflineVideoExport.ts +++ b/src/hooks/useOfflineVideoExport.ts @@ -351,7 +351,7 @@ export const useOfflineVideoExport = () => { }; }; -// Memory-efficient muxing using real-time playback +// Improved muxing with better synchronization async function muxAudioVideo( videoBlob: Blob, audioFile: File, @@ -367,9 +367,9 @@ async function muxAudioVideo( video.src = videoUrl; video.muted = true; - video.playbackRate = 4; // Speed up playback + video.playbackRate = 1; // Normal playback speed audio.src = audioUrl; - audio.playbackRate = 4; + audio.playbackRate = 1; const cleanup = () => { URL.revokeObjectURL(videoUrl); @@ -428,21 +428,39 @@ async function muxAudioVideo( reject(new Error('Muxing failed')); }; + let lastVideoTime = 0; const drawLoop = () => { - if (video.ended || audio.ended) { - setTimeout(() => recorder.stop(), 100); + if (video.paused || video.ended) { + if (video.ended || audio.ended) { + setTimeout(() => recorder.stop(), 100); + return; + } + requestAnimationFrame(drawLoop); return; } - ctx.drawImage(video, 0, 0); + + // Only draw when video has progressed + if (video.currentTime !== lastVideoTime) { + lastVideoTime = video.currentTime; + ctx.drawImage(video, 0, 0); + } requestAnimationFrame(drawLoop); }; recorder.start(100); + + // Ensure both start at the same time video.currentTime = 0; audio.currentTime = 0; - video.play(); - audio.play(); - drawLoop(); + + // Wait for both to be ready to play + Promise.all([video.play(), audio.play()]).then(() => { + drawLoop(); + }).catch(err => { + console.error('Playback failed:', err); + cleanup(); + reject(err); + }); }).catch(err => { cleanup(); console.warn('Muxing failed, returning video only:', err); diff --git a/src/pages/Oscilloscope.tsx b/src/pages/Oscilloscope.tsx new file mode 100644 index 0000000..a2a5f6b --- /dev/null +++ b/src/pages/Oscilloscope.tsx @@ -0,0 +1,26 @@ +import { motion } from 'framer-motion'; +import { Oscilloscope } from '@/components/Oscilloscope'; + +const OscilloscopePage = () => { + return ( + +
+

+ Audio Oscilloscope +

+

+ Visualize audio waveforms in real-time with microphone input or audio files. +

+
+ + +
+ ); +}; + +export default OscilloscopePage; \ No newline at end of file