Osciloscope integrated but video exported is broken

This commit is contained in:
JorySeverijnse 2025-12-20 15:50:04 +01:00
parent 26584ea848
commit e227743728
4 changed files with 56 additions and 30 deletions

22
package-lock.json generated
View File

@ -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"
},

View File

@ -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 = () => {
<Route path="games/breakout" element={<Breakout />} />
<Route path="faq" element={<FAQ />} />
<Route path="music" element={<Music />} />
<Route path="oscilloscope" element={<Oscilloscope />} />
<Route path="ai" element={<AIChat />} />
<Route path="achievements" element={<Achievements />} />
<Route path="credits" element={<Credits />} />

View File

@ -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);

View File

@ -0,0 +1,26 @@
import { motion } from 'framer-motion';
import { Oscilloscope } from '@/components/Oscilloscope';
const OscilloscopePage = () => {
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5 }}
className="space-y-6"
>
<div className="text-center space-y-2">
<h1 className="font-minecraft text-3xl md:text-4xl text-primary text-glow-strong">
Audio Oscilloscope
</h1>
<p className="font-pixel text-foreground/80">
Visualize audio waveforms in real-time with microphone input or audio files.
</p>
</div>
<Oscilloscope />
</motion.div>
);
};
export default OscilloscopePage;