// Room Acoustics Calculator Logic class RoomAcousticsCalculator { constructor() { this.speedOfSound = 343; // m/s this.roomDimensions = { length: 5, width: 4, height: 3 }; this.subwooferPosition = { x: 2.5, y: 2, z: 1.5 }; this.modes = []; this.init(); } init() { this.calculateModes(); } setRoomDimensions(dimensions) { this.roomDimensions = dimensions; this.calculateModes(); } setSubwooferPosition(position) { this.subwooferPosition = position; this.calculatePressureDistribution(); } calculateModes(maxFrequency = 200) { const { length, width, height } = this.roomDimensions; this.modes = []; // Maximum mode numbers based on max frequency const maxN = Math.ceil((2 * maxFrequency * length) / this.speedOfSound); const maxM = Math.ceil((2 * maxFrequency * width) / this.speedOfSound); const maxP = Math.ceil((2 * maxFrequency * height) / this.speedOfSound); for (let n = 0; n <= maxN; n++) { for (let m = 0; m <= maxM; m++) { for (let p = 0; p <= maxP; p++) { if (n === 0 && m === 0 && p === 0) continue; const frequency = (this.speedOfSound / 2) * Math.sqrt( Math.pow(n / length, 2) + Math.pow(m / width, 2) + Math.pow(p / height, 2) ); if (frequency <= maxFrequency) { let modeType = 'oblique'; if ((n > 0 && m === 0 && p === 0) || (n === 0 && m > 0 && p === 0) || (n === 0 && m === 0 && p > 0)) { modeType = 'axial'; } else if ((n > 0 && m > 0 && p === 0) || (n > 0 && m === 0 && p > 0) || (n === 0 && m > 0 && p > 0)) { modeType = 'tangential'; } this.modes.push({ n, m, p, frequency: parseFloat(frequency.toFixed(2)), type: modeType }); } } } } // Sort by frequency this.modes.sort((a, b) => a.frequency - b.frequency); return this.modes; } calculatePressureAtPosition(x, y, z) { const { length, width, height } = this.roomDimensions; let pressure = 0; // Simplified pressure calculation based on mode shapes this.modes.forEach(mode => { const { n, m, p, frequency } = mode; if (frequency >= 20 && frequency <= 200) { // Mode shape function const modeShape = Math.cos((n * Math.PI * x) / length) * Math.cos((m * Math.PI * y) / width) * Math.cos((p * Math.PI * z) / height); // Pressure contribution (simplified) pressure += Math.abs(modeShape) / (frequency * frequency); } }); return pressure; } calculatePressureDistribution() { // This would normally generate a full 3D pressure map // For now we'll just return pressure at subwoofer position return this.calculatePressureAtPosition( this.subwooferPosition.x, this.subwooferPosition.y, this.subwooferPosition.z ); } getCriticalFrequencies() { const { length, width, height } = this.roomDimensions; return [ { name: "Length Axial", frequency: parseFloat(((this.speedOfSound / 2) / length).toFixed(2)), dimension: "length" }, { name: "Width Axial", frequency: parseFloat(((this.speedOfSound / 2) / width).toFixed(2)), dimension: "width" }, { name: "Height Axial", frequency: parseFloat(((this.speedOfSound / 2) / height).toFixed(2)), dimension: "height" } ]; } } // Global calculator instance const calculator = new RoomAcousticsCalculator(); // Utility functions function updateResults() { document.dispatchEvent(new CustomEvent('calculatorUpdated', { detail: { modes: calculator.modes, criticalFrequencies: calculator.getCriticalFrequencies(), pressure: calculator.calculatePressureDistribution() } })); } // Initialize when DOM is loaded document.addEventListener('DOMContentLoaded', () => { updateResults(); }); // Export for use in components window.calculator = calculator; window.updateResults = updateResults;