diff --git a/party-stage/src/core/video-player.js b/party-stage/src/core/video-player.js index 35a0694..865d849 100644 --- a/party-stage/src/core/video-player.js +++ b/party-stage/src/core/video-player.js @@ -1,6 +1,6 @@ import * as THREE from 'three'; import { state } from '../state.js'; -import { turnTvScreenOff, turnTvScreenOn } from '../scene/projection-screen.js'; +import { turnTvScreenOff, turnTvScreenOn, showStandbyScreen } from '../scene/projection-screen.js'; import sceneFeatureManager from '../scene/SceneFeatureManager.js'; // Register a feature to handle party start @@ -188,5 +188,6 @@ export function loadVideoFile(event) { } else { console.info("Tapes loaded. Waiting for party start..."); if (state.loadTapeButton) state.loadTapeButton.innerText = "Tapes Ready"; + showStandbyScreen(); } } \ No newline at end of file diff --git a/party-stage/src/scene/music-player.js b/party-stage/src/scene/music-player.js index c21ee83..d042d58 100644 --- a/party-stage/src/scene/music-player.js +++ b/party-stage/src/scene/music-player.js @@ -1,6 +1,7 @@ import { state } from '../state.js'; import { SceneFeature } from './SceneFeature.js'; import sceneFeatureManager from './SceneFeatureManager.js'; +import { showStandbyScreen } from './projection-screen.js'; export class MusicPlayer extends SceneFeature { constructor() { @@ -46,8 +47,12 @@ export class MusicPlayer extends SceneFeature { loadButton.style.display = 'none'; // Show metadata - songTitleElement.textContent = file.name.replace(/\.[^/.]+$/, ""); // Show filename without extension - metadataContainer.classList.remove('hidden'); + const songName = file.name.replace(/\.[^/.]+$/, ""); + songTitleElement.textContent = songName; // Show filename without extension + state.music.songTitle = songName; + + showStandbyScreen(); + metadataContainer.classList.add('hidden'); const url = URL.createObjectURL(file); state.music.player.src = url; diff --git a/party-stage/src/scene/projection-screen.js b/party-stage/src/scene/projection-screen.js index af5ba1f..07cd687 100644 --- a/party-stage/src/scene/projection-screen.js +++ b/party-stage/src/scene/projection-screen.js @@ -12,7 +12,7 @@ void main() { } `; -const ledCountX = 128; +const ledCountX = 256; const ledCountY = ledCountX * (9 / 16); const screenFragmentShader = ` @@ -234,6 +234,62 @@ export class ProjectionScreen extends SceneFeature { // --- Exported Control Functions --- +export function showStandbyScreen() { + if (projectionScreenInstance) projectionScreenInstance.isVisualizerActive = false; + + const canvas = document.createElement('canvas'); + canvas.width = 1024; + canvas.height = 576; + const ctx = canvas.getContext('2d'); + + // Draw Color Bars + const colors = ['#ffffff', '#ffff00', '#00ffff', '#00ff00', '#ff00ff', '#ff0000', '#0000ff']; + const barWidth = canvas.width / colors.length; + colors.forEach((color, i) => { + ctx.fillStyle = color; + ctx.fillRect(i * barWidth, 0, barWidth, canvas.height); + }); + + // Semi-transparent overlay + ctx.fillStyle = 'rgba(0, 0, 0, 0.7)'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + // Text settings + ctx.fillStyle = '#ffffff'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + + // Song Title + let text = "PLEASE STAND BY"; + if (state.music && state.music.songTitle) { + text = state.music.songTitle.toUpperCase(); + } + + ctx.font = 'bold 60px monospace'; + if (ctx.measureText(text).width > canvas.width * 0.9) { + ctx.font = 'bold 40px monospace'; + } + ctx.fillText(text, canvas.width / 2, canvas.height / 2 - 20); + + // Subtext + ctx.font = '30px monospace'; + ctx.fillStyle = '#cccccc'; + ctx.fillText("WAITING FOR PARTY START", canvas.width / 2, canvas.height / 2 + 50); + + const texture = new THREE.CanvasTexture(canvas); + + if (state.tvScreen.material) state.tvScreen.material.dispose(); + + state.tvScreen.material = new THREE.MeshBasicMaterial({ + map: texture, + side: THREE.DoubleSide + }); + state.tvScreen.visible = true; + state.tvScreenPowered = true; + + state.screenLight.intensity = 0.5; +} + export function turnTvScreenOn() { if (projectionScreenInstance) projectionScreenInstance.isVisualizerActive = false;