Feature: beat detection and visualization
This commit is contained in:
parent
451d4ea261
commit
6566416ebd
@ -176,7 +176,7 @@ export class Dancers extends SceneFeature {
|
||||
}
|
||||
} else {
|
||||
const musicTime = state.clock.getElapsedTime();
|
||||
if (state.music && state.music.beatIntensity > 0.8 && Math.random() < 0.5 && musicTime > 10) {
|
||||
if (state.music && state.music.isLoudEnough && state.music.beatIntensity > 0.8 && Math.random() < 0.5 && musicTime > 10) {
|
||||
dancerObj.isJumping = true;
|
||||
dancerObj.jumpStartTime = time;
|
||||
}
|
||||
|
||||
@ -245,7 +245,7 @@ export class MedievalMusicians extends SceneFeature {
|
||||
} else {
|
||||
let currentJumpChance = jumpChance * deltaTime; // Base chance over time
|
||||
const musicTime = state.clock.getElapsedTime();
|
||||
if (state.music && state.music.beatIntensity > 0.8 && musicTime > 15) {
|
||||
if (state.music && state.music.isLoudEnough && state.music.beatIntensity > 0.8 && musicTime > 15) {
|
||||
currentJumpChance = 0.1; // High, fixed chance on the beat
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import * as THREE from 'three';
|
||||
import { state } from '../state.js';
|
||||
import { SceneFeature } from './SceneFeature.js';
|
||||
import sceneFeatureManager from './SceneFeatureManager.js';
|
||||
@ -6,11 +5,19 @@ import sceneFeatureManager from './SceneFeatureManager.js';
|
||||
export class MusicPlayer extends SceneFeature {
|
||||
constructor() {
|
||||
super();
|
||||
this.audioContext = null;
|
||||
this.analyser = null;
|
||||
this.source = null;
|
||||
this.dataArray = null;
|
||||
this.loudnessHistory = [];
|
||||
sceneFeatureManager.register(this);
|
||||
}
|
||||
|
||||
init() {
|
||||
state.music.player = document.getElementById('audioPlayer');
|
||||
state.music.loudness = 0;
|
||||
state.music.isLoudEnough = false;
|
||||
|
||||
const loadButton = document.getElementById('loadMusicButton');
|
||||
const fileInput = document.getElementById('musicFileInput');
|
||||
const uiContainer = document.getElementById('ui-container');
|
||||
@ -24,6 +31,17 @@ export class MusicPlayer extends SceneFeature {
|
||||
fileInput.addEventListener('change', (event) => {
|
||||
const file = event.target.files[0];
|
||||
if (file) {
|
||||
// Setup Web Audio API if not already done
|
||||
if (!this.audioContext) {
|
||||
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||||
this.analyser = this.audioContext.createAnalyser();
|
||||
this.analyser.fftSize = 128; // Lower resolution is fine for loudness
|
||||
this.source = this.audioContext.createMediaElementSource(state.music.player);
|
||||
this.source.connect(this.analyser);
|
||||
this.analyser.connect(this.audioContext.destination);
|
||||
this.dataArray = new Uint8Array(this.analyser.frequencyBinCount);
|
||||
}
|
||||
|
||||
// Hide the main button
|
||||
loadButton.style.display = 'none';
|
||||
|
||||
@ -84,8 +102,29 @@ export class MusicPlayer extends SceneFeature {
|
||||
}
|
||||
|
||||
update(deltaTime) {
|
||||
// The music player updates itself via events,
|
||||
// but this could be used for real-time analysis in the future.
|
||||
if (!state.partyStarted || !this.analyser) return;
|
||||
|
||||
this.analyser.getByteFrequencyData(this.dataArray);
|
||||
|
||||
// --- Calculate current loudness ---
|
||||
let sum = 0;
|
||||
for (let i = 0; i < this.dataArray.length; i++) {
|
||||
sum += this.dataArray[i];
|
||||
}
|
||||
const average = sum / this.dataArray.length;
|
||||
state.music.loudness = average / 255; // Normalize to 0-1 range
|
||||
|
||||
// --- Track loudness over the last 2 seconds ---
|
||||
this.loudnessHistory.push(state.music.loudness);
|
||||
if (this.loudnessHistory.length > 120) { // Assuming ~60fps, 2 seconds of history
|
||||
this.loudnessHistory.shift();
|
||||
}
|
||||
|
||||
// --- Determine if it's loud enough to jump ---
|
||||
const avgLoudness = this.loudnessHistory.reduce((a, b) => a + b, 0) / this.loudnessHistory.length;
|
||||
const quietThreshold = 0.1; // Adjust this value based on testing
|
||||
|
||||
state.music.isLoudEnough = avgLoudness > quietThreshold;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -16,6 +16,7 @@ export class MusicVisualizer extends SceneFeature {
|
||||
measureDuration: (60 / 120) * 4,
|
||||
beatIntensity: 0,
|
||||
measurePulse: 0,
|
||||
isLoudEnough: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -155,7 +155,7 @@ export class PartyGuests extends SceneFeature {
|
||||
}
|
||||
} else {
|
||||
let currentJumpChance = jumpChance * deltaTime; // Base chance over time
|
||||
if (state.music && state.music.beatIntensity > 0.8) {
|
||||
if (state.music && state.music.isLoudEnough && state.music.beatIntensity > 0.8) {
|
||||
currentJumpChance = 0.1; // High, fixed chance on the beat
|
||||
}
|
||||
|
||||
|
||||
@ -58,7 +58,7 @@ export class StageTorches extends SceneFeature {
|
||||
torchGroup.add(pointLight);
|
||||
|
||||
// --- Particle System for Fire ---
|
||||
const particleCount = 50;
|
||||
const particleCount = 100;
|
||||
const particles = new THREE.BufferGeometry();
|
||||
const positions = [];
|
||||
const particleData = [];
|
||||
@ -107,7 +107,10 @@ export class StageTorches extends SceneFeature {
|
||||
this.torches.forEach(torch => {
|
||||
let measurePulse = 0;
|
||||
if (state.music) {
|
||||
measurePulse = state.music.measurePulse * 4.0; // Make flames jump higher
|
||||
measurePulse = state.music.measurePulse * 2.0; // Make flames jump higher
|
||||
}
|
||||
if (state.music.isLoudEnough) {
|
||||
measurePulse += 2;
|
||||
}
|
||||
|
||||
// --- Animate Particles ---
|
||||
@ -119,11 +122,11 @@ export class StageTorches extends SceneFeature {
|
||||
const yVelocity = data.velocity.y;
|
||||
if (data.life <= 0 || positions[i * 3 + 1] < 0) {
|
||||
// Reset particle
|
||||
positions[i * 3] = 0;
|
||||
positions[i * 3] = (Math.random() - 0.5) * 0.2;
|
||||
positions[i * 3 + 1] = 1;
|
||||
positions[i * 3 + 2] = 0;
|
||||
positions[i * 3 + 2] = (Math.random() - 0.5) * 0.2;
|
||||
data.life = Math.random() * 1.0;
|
||||
data.velocity.y = Math.random() * 1.5 + measurePulse;
|
||||
data.velocity.y = Math.random() * 1.2 + measurePulse;
|
||||
} else {
|
||||
// Update position
|
||||
positions[i * 3] += data.velocity.x * deltaTime;
|
||||
@ -141,6 +144,9 @@ export class StageTorches extends SceneFeature {
|
||||
let beatPulse = 0;
|
||||
if (state.music) {
|
||||
beatPulse = state.music.beatIntensity * 1.5;
|
||||
if (state.music.isLoudEnough) {
|
||||
beatPulse += 2;
|
||||
}
|
||||
}
|
||||
|
||||
torch.light.intensity = baseIntensity + flicker + beatPulse;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user