fix: move computing spectrogram text to the center of current viewport of piano roll window
This commit is contained in:
@@ -34,6 +34,7 @@ interface PianoGridProps {
|
|||||||
spectrogramPower?: number;
|
spectrogramPower?: number;
|
||||||
pianoRollZoom?: number;
|
pianoRollZoom?: number;
|
||||||
mode?: 'midi-edit' | 'spectrogram' | 'hybrid';
|
mode?: 'midi-edit' | 'spectrogram' | 'hybrid';
|
||||||
|
onSpectrogramLoadingChange?: (loading: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CursorPosition {
|
interface CursorPosition {
|
||||||
@@ -62,6 +63,7 @@ const PianoGrid: React.FC<PianoGridProps> = ({
|
|||||||
spectrogramThresholdDb = -25,
|
spectrogramThresholdDb = -25,
|
||||||
spectrogramPower = 0.5,
|
spectrogramPower = 0.5,
|
||||||
pianoRollZoom = 1,
|
pianoRollZoom = 1,
|
||||||
|
onSpectrogramLoadingChange,
|
||||||
}) => {
|
}) => {
|
||||||
const [cursorPosition, setCursorPosition] = useState<CursorPosition | null>(null);
|
const [cursorPosition, setCursorPosition] = useState<CursorPosition | null>(null);
|
||||||
const [isModifierPressed, setIsModifierPressed] = useState(false);
|
const [isModifierPressed, setIsModifierPressed] = useState(false);
|
||||||
@@ -243,6 +245,7 @@ const PianoGrid: React.FC<PianoGridProps> = ({
|
|||||||
thresholdDb={spectrogramThresholdDb}
|
thresholdDb={spectrogramThresholdDb}
|
||||||
power={spectrogramPower}
|
power={spectrogramPower}
|
||||||
zoom={pianoRollZoom}
|
zoom={pianoRollZoom}
|
||||||
|
onLoadingChange={onSpectrogramLoadingChange}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -159,13 +159,37 @@
|
|||||||
color: #ff4444;
|
color: #ff4444;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.piano-roll-content-outer {
|
||||||
|
position: relative;
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.piano-roll-content {
|
.piano-roll-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex: 1;
|
height: 100%;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.spectrogram-loading-overlay {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spectrogram-loading-label {
|
||||||
|
padding: 6px 10px;
|
||||||
|
background: rgba(0, 0, 0, 0.6);
|
||||||
|
color: #aaa;
|
||||||
|
font-size: 11px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
.piano-grid-header {
|
.piano-grid-header {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(var(--max-number-of-bars), var(--region-grid-bar-width));
|
grid-template-columns: repeat(var(--max-number-of-bars), var(--region-grid-bar-width));
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useMemo, useState, useRef, useEffect } from 'react';
|
import React, { useMemo, useState, useRef, useEffect, useCallback } from 'react';
|
||||||
import { DEBUG_MODE } from '../../constants';
|
import { DEBUG_MODE } from '../../constants';
|
||||||
import { KGMidiRegion } from '../../core/region/KGMidiRegion';
|
import { KGMidiRegion } from '../../core/region/KGMidiRegion';
|
||||||
import { KGMidiNote } from '../../core/midi/KGMidiNote';
|
import { KGMidiNote } from '../../core/midi/KGMidiNote';
|
||||||
@@ -60,6 +60,11 @@ const PianoRollContent: React.FC<PianoRollContentProps> = ({
|
|||||||
pianoRollZoom = 1,
|
pianoRollZoom = 1,
|
||||||
}) => {
|
}) => {
|
||||||
const isSpectrogram = mode === 'spectrogram';
|
const isSpectrogram = mode === 'spectrogram';
|
||||||
|
const [spectrogramLoading, setSpectrogramLoading] = useState(false);
|
||||||
|
const handleSpectrogramLoadingChange = useCallback((loading: boolean) => {
|
||||||
|
setSpectrogramLoading(loading);
|
||||||
|
}, []);
|
||||||
|
|
||||||
// Get KGCore instance
|
// Get KGCore instance
|
||||||
const core = KGCore.instance();
|
const core = KGCore.instance();
|
||||||
|
|
||||||
@@ -257,38 +262,48 @@ const PianoRollContent: React.FC<PianoRollContentProps> = ({
|
|||||||
}, [isRecording, recordingNotes, activeRegion]);
|
}, [isRecording, recordingNotes, activeRegion]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className="piano-roll-content-outer">
|
||||||
className="piano-roll-content"
|
<div
|
||||||
ref={contentRef}
|
className="piano-roll-content"
|
||||||
>
|
ref={contentRef}
|
||||||
<PianoGridHeader maxBars={maxBars} timeSignature={timeSignature} />
|
>
|
||||||
|
<PianoGridHeader maxBars={maxBars} timeSignature={timeSignature} />
|
||||||
<div className="piano-roll-body">
|
|
||||||
<PianoKeys activeRegion={activeRegion} />
|
<div className="piano-roll-body">
|
||||||
|
<PianoKeys activeRegion={activeRegion} />
|
||||||
<PianoGrid
|
|
||||||
gridRef={pianoGridRef}
|
<PianoGrid
|
||||||
onDoubleClick={isSpectrogram ? () => {} : handleGridDoubleClick}
|
gridRef={pianoGridRef}
|
||||||
onClick={isSpectrogram ? () => {} : handleCombinedClick}
|
onDoubleClick={isSpectrogram ? () => {} : handleGridDoubleClick}
|
||||||
onMouseDown={isSpectrogram ? () => {} : handleBackgroundMouseDown}
|
onClick={isSpectrogram ? () => {} : handleCombinedClick}
|
||||||
isBoxSelecting={isSpectrogram ? false : isBoxSelectingRef.current}
|
onMouseDown={isSpectrogram ? () => {} : handleBackgroundMouseDown}
|
||||||
selectionBox={isSpectrogram ? { startX: 0, startY: 0, endX: 0, endY: 0 } : selectionBoxRef.current}
|
isBoxSelecting={isSpectrogram ? false : isBoxSelectingRef.current}
|
||||||
regionStartBeat={activeRegion?.getStartFromBeat() || 0}
|
selectionBox={isSpectrogram ? { startX: 0, startY: 0, endX: 0, endY: 0 } : selectionBoxRef.current}
|
||||||
selectedMode={selectedMode}
|
regionStartBeat={activeRegion?.getStartFromBeat() || 0}
|
||||||
keySignature={keySignature}
|
selectedMode={selectedMode}
|
||||||
chordGuide={chordGuide}
|
keySignature={keySignature}
|
||||||
audioRegion={audioRegion}
|
chordGuide={chordGuide}
|
||||||
trackId={trackId}
|
audioRegion={audioRegion}
|
||||||
projectName={projectName}
|
trackId={trackId}
|
||||||
bpm={bpm}
|
projectName={projectName}
|
||||||
spectrogramThresholdDb={spectrogramThresholdDb}
|
bpm={bpm}
|
||||||
spectrogramPower={spectrogramPower}
|
spectrogramThresholdDb={spectrogramThresholdDb}
|
||||||
pianoRollZoom={pianoRollZoom}
|
spectrogramPower={spectrogramPower}
|
||||||
>
|
pianoRollZoom={pianoRollZoom}
|
||||||
{memoizedNotes}
|
onSpectrogramLoadingChange={handleSpectrogramLoadingChange}
|
||||||
{!isSpectrogram && recordingNoteOverlays}
|
>
|
||||||
</PianoGrid>
|
{memoizedNotes}
|
||||||
|
{!isSpectrogram && recordingNoteOverlays}
|
||||||
|
</PianoGrid>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{spectrogramLoading && (
|
||||||
|
<div className="spectrogram-loading-overlay">
|
||||||
|
<div className="spectrogram-loading-label">
|
||||||
|
Computing spectrogram…
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ interface SpectrogramCanvasProps {
|
|||||||
thresholdDb: number;
|
thresholdDb: number;
|
||||||
power: number;
|
power: number;
|
||||||
zoom: number;
|
zoom: number;
|
||||||
|
onLoadingChange?: (loading: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PITCH_BINS = 128;
|
const PITCH_BINS = 128;
|
||||||
@@ -55,6 +56,7 @@ const SpectrogramCanvas: React.FC<SpectrogramCanvasProps> = ({
|
|||||||
thresholdDb,
|
thresholdDb,
|
||||||
power,
|
power,
|
||||||
zoom,
|
zoom,
|
||||||
|
onLoadingChange,
|
||||||
}) => {
|
}) => {
|
||||||
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
@@ -70,6 +72,8 @@ const SpectrogramCanvas: React.FC<SpectrogramCanvasProps> = ({
|
|||||||
const zoomRef = useRef(zoom);
|
const zoomRef = useRef(zoom);
|
||||||
useEffect(() => { zoomRef.current = zoom; }, [zoom]);
|
useEffect(() => { zoomRef.current = zoom; }, [zoom]);
|
||||||
|
|
||||||
|
useEffect(() => { onLoadingChange?.(loading); }, [loading, onLoadingChange]);
|
||||||
|
|
||||||
const renderSpectrogram = useCallback((
|
const renderSpectrogram = useCallback((
|
||||||
result: SpectrogramResult,
|
result: SpectrogramResult,
|
||||||
sampleRate: number,
|
sampleRate: number,
|
||||||
@@ -240,35 +244,16 @@ const SpectrogramCanvas: React.FC<SpectrogramCanvasProps> = ({
|
|||||||
}, [audioRegion, trackId, projectName, bpm]);
|
}, [audioRegion, trackId, projectName, bpm]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<canvas
|
||||||
<canvas
|
ref={canvasRef}
|
||||||
ref={canvasRef}
|
style={{
|
||||||
style={{
|
position: 'absolute',
|
||||||
position: 'absolute',
|
top: 0,
|
||||||
top: 0,
|
left: 0,
|
||||||
left: 0,
|
zIndex: 0,
|
||||||
zIndex: 0,
|
pointerEvents: 'none',
|
||||||
pointerEvents: 'none',
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
|
||||||
{loading && (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
zIndex: 1,
|
|
||||||
padding: '6px 10px',
|
|
||||||
background: 'rgba(0,0,0,0.6)',
|
|
||||||
color: '#aaa',
|
|
||||||
fontSize: '11px',
|
|
||||||
pointerEvents: 'none',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Computing spectrogram…
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user