Compare commits

...

2 Commits

Author SHA1 Message Date
d03080d3d2 feat: apply enableSpectrogram and sampleRate settings to editor
Applied performance and audio settings to actual editor behavior:
- enableSpectrogram: conditionally show/hide spectrogram analyzer option
- sampleRate: sync recording sample rate with global audio settings

Changes:
- Switch analyzer view away from spectrogram when setting is disabled
- Adjust analyzer toggle grid columns based on spectrogram visibility
- Sync recording hook's sampleRate with global settings via useEffect

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-19 18:29:05 +01:00
484e3261c5 feat: apply defaultTrackHeight and defaultZoom settings
- Modified createTrack and createTrackFromBuffer to accept height parameter
- Updated useMultiTrack to pass height when creating tracks
- Applied settings.ui.defaultTrackHeight when adding new tracks
- Applied settings.editor.defaultZoom for initial zoom state
- Removed duplicate useSettings hook declaration in AudioEditor
- New tracks now use configured default height from settings

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-19 18:23:41 +01:00
3 changed files with 50 additions and 35 deletions

View File

@@ -50,9 +50,19 @@ import {
import { getAudioContext } from '@/lib/audio/context';
export function AudioEditor() {
// Settings hook
const {
settings,
updateAudioSettings,
updateUISettings,
updateEditorSettings,
updatePerformanceSettings,
resetCategory,
} = useSettings();
const [importDialogOpen, setImportDialogOpen] = React.useState(false);
const [selectedTrackId, setSelectedTrackId] = React.useState<string | null>(null);
const [zoom, setZoom] = React.useState(1);
const [zoom, setZoom] = React.useState(settings.editor.defaultZoom);
const [masterVolume, setMasterVolume] = React.useState(0.8);
const [masterPan, setMasterPan] = React.useState(0);
const [isMasterMuted, setIsMasterMuted] = React.useState(false);
@@ -66,6 +76,13 @@ export function AudioEditor() {
const [exportDialogOpen, setExportDialogOpen] = React.useState(false);
const [isExporting, setIsExporting] = React.useState(false);
const [analyzerView, setAnalyzerView] = React.useState<'frequency' | 'spectrogram' | 'phase' | 'lufs' | 'stats'>('frequency');
// Switch away from spectrogram if it gets disabled
React.useEffect(() => {
if (analyzerView === 'spectrogram' && !settings.performance.enableSpectrogram) {
setAnalyzerView('frequency');
}
}, [analyzerView, settings.performance.enableSpectrogram]);
const [projectsDialogOpen, setProjectsDialogOpen] = React.useState(false);
const [projects, setProjects] = React.useState<ProjectMetadata[]>([]);
const [currentProjectId, setCurrentProjectId] = React.useState<string | null>(null);
@@ -90,15 +107,10 @@ export function AudioEditor() {
setSampleRate,
} = useRecording();
// Settings hook
const {
settings,
updateAudioSettings,
updateUISettings,
updateEditorSettings,
updatePerformanceSettings,
resetCategory,
} = useSettings();
// Sync recording sample rate with global settings
React.useEffect(() => {
setSampleRate(settings.audio.sampleRate);
}, [settings.audio.sampleRate, setSampleRate]);
// Multi-track hooks
const {
@@ -122,19 +134,19 @@ export function AudioEditor() {
// Wrap addTrack to auto-select first track when adding to empty project
const addTrack = React.useCallback((name?: string) => {
const shouldAutoSelect = shouldAutoSelectRef.current;
const track = addTrackOriginal(name);
const track = addTrackOriginal(name, settings.ui.defaultTrackHeight);
if (shouldAutoSelect) {
setSelectedTrackId(track.id);
shouldAutoSelectRef.current = false; // Only auto-select once
}
return track;
}, [addTrackOriginal]);
}, [addTrackOriginal, settings.ui.defaultTrackHeight]);
// Wrap addTrackFromBuffer to auto-select first track when adding to empty project
const addTrackFromBuffer = React.useCallback((buffer: AudioBuffer, name?: string) => {
console.log(`[AudioEditor] addTrackFromBuffer wrapper called: ${name}, shouldAutoSelect: ${shouldAutoSelectRef.current}`);
const shouldAutoSelect = shouldAutoSelectRef.current;
const track = addTrackFromBufferOriginal(buffer, name);
const track = addTrackFromBufferOriginal(buffer, name, settings.ui.defaultTrackHeight);
console.log(`[AudioEditor] Track created: ${track.name} (${track.id})`);
if (shouldAutoSelect) {
console.log(`[AudioEditor] Auto-selecting track: ${track.id}`);
@@ -142,7 +154,7 @@ export function AudioEditor() {
shouldAutoSelectRef.current = false; // Only auto-select once
}
return track;
}, [addTrackFromBufferOriginal]);
}, [addTrackFromBufferOriginal, settings.ui.defaultTrackHeight]);
// Track which parameters are being touched (for touch/latch modes)
const [touchedParameters, setTouchedParameters] = React.useState<Set<string>>(new Set());
@@ -1606,7 +1618,7 @@ export function AudioEditor() {
</div>
{/* Analyzer Toggle */}
<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">
<div className={`grid ${settings.performance.enableSpectrogram ? 'grid-cols-5' : 'grid-cols-4'} 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={`px-1 py-1 rounded text-[9px] font-bold uppercase tracking-wider transition-all ${
@@ -1618,17 +1630,19 @@ export function AudioEditor() {
>
FFT
</button>
<button
onClick={() => setAnalyzerView('spectrogram')}
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"
>
SPEC
</button>
{settings.performance.enableSpectrogram && (
<button
onClick={() => setAnalyzerView('spectrogram')}
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"
>
SPEC
</button>
)}
<button
onClick={() => setAnalyzerView('phase')}
className={`px-1 py-1 rounded text-[9px] font-bold uppercase tracking-wider transition-all ${
@@ -1668,7 +1682,7 @@ export function AudioEditor() {
<div className="flex-1 min-h-[360px] flex items-start justify-center">
<div className="w-[178px]">
{analyzerView === 'frequency' && <FrequencyAnalyzer analyserNode={masterAnalyser} />}
{analyzerView === 'spectrogram' && <Spectrogram analyserNode={masterAnalyser} />}
{analyzerView === 'spectrogram' && settings.performance.enableSpectrogram && <Spectrogram analyserNode={masterAnalyser} />}
{analyzerView === 'phase' && <PhaseCorrelationMeter analyserNode={masterAnalyser} />}
{analyzerView === 'lufs' && <LUFSMeter analyserNode={masterAnalyser} />}
{analyzerView === 'stats' && <AudioStatistics tracks={tracks} />}

View File

@@ -17,7 +17,7 @@ export function generateTrackId(): string {
/**
* Create a new empty track
*/
export function createTrack(name?: string, color?: TrackColor): Track {
export function createTrack(name?: string, color?: TrackColor, height?: number): Track {
const colors: TrackColor[] = ['blue', 'green', 'purple', 'orange', 'pink', 'indigo', 'yellow', 'red'];
const randomColor = colors[Math.floor(Math.random() * colors.length)];
@@ -30,7 +30,7 @@ export function createTrack(name?: string, color?: TrackColor): Track {
id: trackId,
name: trackName,
color: TRACK_COLORS[color || randomColor],
height: DEFAULT_TRACK_HEIGHT,
height: height ?? DEFAULT_TRACK_HEIGHT,
audioBuffer: null,
volume: 0.8,
pan: 0,
@@ -70,11 +70,12 @@ export function createTrack(name?: string, color?: TrackColor): Track {
export function createTrackFromBuffer(
buffer: AudioBuffer,
name?: string,
color?: TrackColor
color?: TrackColor,
height?: number
): Track {
// Ensure name is a string before passing to createTrack
const trackName = typeof name === 'string' && name.trim() ? name.trim() : undefined;
const track = createTrack(trackName, color);
const track = createTrack(trackName, color, height);
track.audioBuffer = buffer;
return track;
}

View File

@@ -6,14 +6,14 @@ export function useMultiTrack() {
// Note: localStorage persistence disabled in favor of IndexedDB project management
const [tracks, setTracks] = useState<Track[]>([]);
const addTrack = useCallback((name?: string) => {
const track = createTrack(name);
const addTrack = useCallback((name?: string, height?: number) => {
const track = createTrack(name, undefined, height);
setTracks((prev) => [...prev, track]);
return track;
}, []);
const addTrackFromBuffer = useCallback((buffer: AudioBuffer, name?: string) => {
const track = createTrackFromBuffer(buffer, name);
const addTrackFromBuffer = useCallback((buffer: AudioBuffer, name?: string, height?: number) => {
const track = createTrackFromBuffer(buffer, name, undefined, height);
setTracks((prev) => [...prev, track]);
return track;
}, []);