feat: complete Phase 10 - add phase correlation, LUFS, and audio statistics

Implemented remaining Phase 10 analysis tools:

**Phase Correlation Meter (10.3)**
- Real-time stereo phase correlation display
- Pearson correlation coefficient calculation
- Color-coded indicator (-1 to +1 scale)
- Visual feedback: Mono-like, Good Stereo, Wide Stereo, Phase Issues

**LUFS Loudness Meter (10.3)**
- Momentary, Short-term, and Integrated LUFS measurements
- Simplified K-weighting approximation
- Vertical bar display with -70 to 0 LUFS range
- -23 LUFS broadcast standard reference line
- Real-time history tracking (10 seconds)

**Audio Statistics (10.4)**
- Project info: track count, duration, sample rate, channels, bit depth
- Level analysis: peak, RMS, dynamic range, headroom
- Real-time buffer analysis from all tracks
- Color-coded warnings for clipping and low headroom

**Integration**
- Added 5-button toggle in master column (FFT, SPEC, PHS, LUFS, INFO)
- All analyzers share consistent 192px width layout
- Theme-aware styling for light/dark modes
- Compact button labels for space efficiency

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-19 02:00:41 +01:00
parent 461a800bb6
commit 355bade08f
4 changed files with 555 additions and 10 deletions

View File

@@ -6,6 +6,9 @@ import { PlaybackControls } from './PlaybackControls';
import { MasterControls } from '@/components/controls/MasterControls';
import { FrequencyAnalyzer } from '@/components/analysis/FrequencyAnalyzer';
import { Spectrogram } from '@/components/analysis/Spectrogram';
import { PhaseCorrelationMeter } from '@/components/analysis/PhaseCorrelationMeter';
import { LUFSMeter } from '@/components/analysis/LUFSMeter';
import { AudioStatistics } from '@/components/analysis/AudioStatistics';
import { ThemeToggle } from '@/components/layout/ThemeToggle';
import { CommandPalette } from '@/components/ui/CommandPalette';
import { GlobalSettingsDialog } from '@/components/settings/GlobalSettingsDialog';
@@ -49,7 +52,7 @@ export function AudioEditor() {
const [settingsDialogOpen, setSettingsDialogOpen] = React.useState(false);
const [exportDialogOpen, setExportDialogOpen] = React.useState(false);
const [isExporting, setIsExporting] = React.useState(false);
const [analyzerView, setAnalyzerView] = React.useState<'frequency' | 'spectrogram'>('frequency');
const [analyzerView, setAnalyzerView] = React.useState<'frequency' | 'spectrogram' | 'phase' | 'lufs' | 'stats'>('frequency');
const { addToast } = useToast();
@@ -1067,37 +1070,72 @@ export function AudioEditor() {
</div>
{/* Analyzer Toggle */}
<div className="flex gap-1 bg-muted/20 border border-border/50 rounded-md p-1 max-w-[192px] mx-auto">
<div className="grid grid-cols-5 gap-0.5 bg-muted/20 border border-border/50 rounded-md p-0.5 max-w-[192px] mx-auto">
<button
onClick={() => setAnalyzerView('frequency')}
className={`flex-1 px-2 py-1 rounded text-[10px] font-bold uppercase tracking-wider transition-all ${
className={`px-1 py-1 rounded text-[9px] font-bold uppercase tracking-wider transition-all ${
analyzerView === 'frequency'
? 'bg-accent text-accent-foreground shadow-sm'
: 'text-muted-foreground hover:text-foreground'
}`}
title="Frequency Analyzer"
>
FFT
</button>
<button
onClick={() => setAnalyzerView('spectrogram')}
className={`flex-1 px-2 py-1 rounded text-[10px] font-bold uppercase tracking-wider transition-all ${
className={`px-1 py-1 rounded text-[9px] font-bold uppercase tracking-wider transition-all ${
analyzerView === 'spectrogram'
? 'bg-accent text-accent-foreground shadow-sm'
: 'text-muted-foreground hover:text-foreground'
}`}
title="Spectrogram"
>
Spectrum
SPEC
</button>
<button
onClick={() => setAnalyzerView('phase')}
className={`px-1 py-1 rounded text-[9px] font-bold uppercase tracking-wider transition-all ${
analyzerView === 'phase'
? 'bg-accent text-accent-foreground shadow-sm'
: 'text-muted-foreground hover:text-foreground'
}`}
title="Phase Correlation"
>
PHS
</button>
<button
onClick={() => setAnalyzerView('lufs')}
className={`px-1 py-1 rounded text-[9px] font-bold uppercase tracking-wider transition-all ${
analyzerView === 'lufs'
? 'bg-accent text-accent-foreground shadow-sm'
: 'text-muted-foreground hover:text-foreground'
}`}
title="LUFS Loudness"
>
LUFS
</button>
<button
onClick={() => setAnalyzerView('stats')}
className={`px-1 py-1 rounded text-[9px] font-bold uppercase tracking-wider transition-all ${
analyzerView === 'stats'
? 'bg-accent text-accent-foreground shadow-sm'
: 'text-muted-foreground hover:text-foreground'
}`}
title="Audio Statistics"
>
INFO
</button>
</div>
{/* Analyzer Display */}
<div className="flex-1 min-h-[360px] flex items-start justify-center">
<div className="w-[192px]">
{analyzerView === 'frequency' ? (
<FrequencyAnalyzer analyserNode={masterAnalyser} />
) : (
<Spectrogram analyserNode={masterAnalyser} />
)}
{analyzerView === 'frequency' && <FrequencyAnalyzer analyserNode={masterAnalyser} />}
{analyzerView === 'spectrogram' && <Spectrogram analyserNode={masterAnalyser} />}
{analyzerView === 'phase' && <PhaseCorrelationMeter analyserNode={masterAnalyser} />}
{analyzerView === 'lufs' && <LUFSMeter analyserNode={masterAnalyser} />}
{analyzerView === 'stats' && <AudioStatistics tracks={tracks} />}
</div>
</div>
</aside>