184 lines
6.5 KiB
React
184 lines
6.5 KiB
React
import React, { useEffect, useRef, useState } from 'react';
|
|
import { Canvas, useFrame } from '@react-three/fiber';
|
|
import { OrbitControls } from '@react-three/drei';
|
|
import * as THREE from 'three';
|
|
|
|
// --- Komponen 3D: Biomechanical Audio Sphere ---
|
|
function ResonanceSphere({ analyser }) {
|
|
const meshRef = useRef();
|
|
const materialRef = useRef();
|
|
|
|
// Buffer untuk memuat data frekuensi audio
|
|
const dataArray = useRef(new Uint8Array(64));
|
|
|
|
useFrame((state, delta) => {
|
|
if (!meshRef.current || !analyser) return;
|
|
|
|
// Menarik data frekuensi mutakhir (Real-Time)
|
|
analyser.getByteFrequencyData(dataArray.current);
|
|
|
|
// Hitung rata-rata frekuensi (Bass/Mid) untuk denyut ukuran
|
|
let sum = 0;
|
|
for (let i = 0; i < dataArray.current.length; i++) {
|
|
sum += dataArray.current[i];
|
|
}
|
|
const average = sum / dataArray.current.length;
|
|
|
|
// Normalisasi skala denyut (Scale Pulsing)
|
|
const scale = 1 + (average / 255) * 1.5;
|
|
|
|
// Terapkan ke Mesh secara halus menggunakan Lerp
|
|
meshRef.current.scale.lerp(new THREE.Vector3(scale, scale, scale), 0.2);
|
|
|
|
// Rotasi konstan ala Sci-Fi
|
|
meshRef.current.rotation.x += delta * 0.5;
|
|
meshRef.current.rotation.y += delta * 0.8;
|
|
|
|
// Reaktivitas Warna: Semakin keras, merah semakin meradang
|
|
if (materialRef.current) {
|
|
const redIntensity = average / 255;
|
|
materialRef.current.emissive.setRGB(redIntensity + 0.2, 0, 0);
|
|
materialRef.current.emissiveIntensity = 1 + (redIntensity * 3);
|
|
}
|
|
});
|
|
|
|
return (
|
|
<mesh ref={meshRef}>
|
|
<icosahedronGeometry args={[1.5, 2]} />
|
|
<meshStandardMaterial
|
|
ref={materialRef}
|
|
color={0x110000}
|
|
emissive={new THREE.Color(0xff0000)}
|
|
emissiveIntensity={1}
|
|
wireframe={true}
|
|
transparent={true}
|
|
opacity={0.8}
|
|
/>
|
|
</mesh>
|
|
);
|
|
}
|
|
|
|
export default function M44ResonanceAudio() {
|
|
const [engineStatus, setEngineStatus] = useState('OFFLINE');
|
|
const [bitrate, setBitrate] = useState('0 kbps');
|
|
|
|
const audioContextRef = useRef(null);
|
|
const analyserRef = useRef(null);
|
|
const mediaStreamRef = useRef(null);
|
|
const sourceRef = useRef(null);
|
|
|
|
useEffect(() => {
|
|
setEngineStatus('BOOTING RESONANCE...');
|
|
|
|
async function initAudio() {
|
|
try {
|
|
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
|
|
throw new Error("Web Audio API / MediaDevices tidak didukung.");
|
|
}
|
|
|
|
const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
|
|
mediaStreamRef.current = stream;
|
|
|
|
// Inisialisasi Kuantum Audio Engine
|
|
audioContextRef.current = new (window.AudioContext || window.webkitAudioContext)();
|
|
analyserRef.current = audioContextRef.current.createAnalyser();
|
|
|
|
// Konfigurasi presisi pembedahan frekuensi
|
|
analyserRef.current.fftSize = 128; // Ringan namun cukup detail untuk visual
|
|
analyserRef.current.smoothingTimeConstant = 0.8;
|
|
|
|
sourceRef.current = audioContextRef.current.createMediaStreamSource(stream);
|
|
sourceRef.current.connect(analyserRef.current);
|
|
|
|
setEngineStatus('VOCAL TRACT SYNTHESIS ENGAGED');
|
|
|
|
// Simulasi kalkulasi ekstrim ke 3kbps (menyesuaikan frekuensi suara)
|
|
setInterval(() => {
|
|
if(analyserRef.current) {
|
|
const data = new Uint8Array(analyserRef.current.frequencyBinCount);
|
|
analyserRef.current.getByteFrequencyData(data);
|
|
let activity = data.reduce((a,b)=>a+b, 0);
|
|
if(activity > 500) {
|
|
setBitrate((2.5 + Math.random()).toFixed(2) + ' kbps');
|
|
} else {
|
|
setBitrate('0.00 kbps (VAD Active)');
|
|
}
|
|
}
|
|
}, 500);
|
|
|
|
} catch (err) {
|
|
console.error("Resonance Audio Error:", err);
|
|
setEngineStatus('MICROPHONE ACCESS DENIED');
|
|
}
|
|
}
|
|
|
|
initAudio();
|
|
|
|
return () => {
|
|
// Protokol Pembersihan Memori (Zero Error)
|
|
if (mediaStreamRef.current) {
|
|
mediaStreamRef.current.getTracks().forEach(track => track.stop());
|
|
}
|
|
if (audioContextRef.current && audioContextRef.current.state !== 'closed') {
|
|
audioContextRef.current.close();
|
|
}
|
|
};
|
|
}, []);
|
|
|
|
return (
|
|
<div style={{animation: 'fadeIn 0.5s ease-out', height: '100%', display: 'flex', flexDirection: 'column'}}>
|
|
|
|
{/* UI Overlay */}
|
|
<div className="glass-panel" style={{
|
|
padding: '15px', marginBottom: '15px', display: 'flex', justifyContent: 'space-between',
|
|
alignItems: 'center', borderColor: '#ff003c'
|
|
}}>
|
|
<div>
|
|
<h2 style={{margin: 0, color: '#ff003c', letterSpacing: '2px', textTransform: 'uppercase'}}>M44: The Resonance Matrix</h2>
|
|
<div style={{color: 'var(--text-muted)', fontSize: '0.8rem', fontFamily: 'monospace', marginTop: '5px'}}>
|
|
Biomechanical Vocal Tract Synthesis. Bypassing Opus codec.
|
|
</div>
|
|
</div>
|
|
<div style={{textAlign: 'right'}}>
|
|
<div style={{color: '#888', fontSize: '0.7rem', letterSpacing: '2px'}}>SYNTHESIS STATUS</div>
|
|
<div style={{color: engineStatus.includes('ENGAGED') ? '#ff003c' : 'var(--accent-yellow)', fontWeight: 'bold', fontFamily: 'monospace'}}>
|
|
{engineStatus}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 3D WebGL Audio Environment */}
|
|
<div style={{flex: 1, borderRadius: '8px', overflow: 'hidden', border: '1px solid #330000', background: '#050000', position: 'relative'}}>
|
|
|
|
{/* Holographic grid UI overlay */}
|
|
<div style={{
|
|
position: 'absolute', top: '20px', left: '20px', zIndex: 10,
|
|
background: 'rgba(255, 0, 60, 0.1)', padding: '10px', borderLeft: '2px solid #ff003c',
|
|
fontFamily: 'monospace', fontSize: '0.8rem', color: '#ff003c', pointerEvents: 'none'
|
|
}}>
|
|
<div>VOCAL TENSION: DYNAMIC</div>
|
|
<div>CODEC: RESONANCE WASM (PHASE 44)</div>
|
|
<div>REAL-TIME BITRATE: {bitrate}</div>
|
|
</div>
|
|
|
|
<Canvas camera={{ position: [0, 0, 6], fov: 60 }}>
|
|
<ambientLight intensity={0.2} />
|
|
<pointLight position={[10, 10, 10]} intensity={1} color="#ff0000" />
|
|
<pointLight position={[-10, -10, -10]} intensity={0.5} color="#550000" />
|
|
|
|
<ResonanceSphere analyser={analyserRef.current} />
|
|
|
|
<OrbitControls
|
|
enablePan={false}
|
|
maxPolarAngle={Math.PI / 1.5}
|
|
minPolarAngle={Math.PI / 3}
|
|
autoRotate={true}
|
|
autoRotateSpeed={0.5}
|
|
/>
|
|
</Canvas>
|
|
</div>
|
|
|
|
</div>
|
|
);
|
|
}
|