feat: implemented piano roll hybrid mode to allow user edit MIDI notes while observing spectrogram as reference
This commit is contained in:
@@ -32,6 +32,7 @@ interface PianoGridProps {
|
||||
bpm?: number;
|
||||
spectrogramThresholdDb?: number;
|
||||
spectrogramPower?: number;
|
||||
mode?: 'midi-edit' | 'spectrogram' | 'hybrid';
|
||||
}
|
||||
|
||||
interface CursorPosition {
|
||||
|
||||
@@ -77,10 +77,17 @@
|
||||
/* Ensure toolbar and its dropdowns appear above piano roll content */
|
||||
}
|
||||
|
||||
.piano-roll-toolbar .tool-button {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
/* Override pointer-events for piano roll toolbar sections */
|
||||
.piano-roll-toolbar .toolbar-left,
|
||||
.piano-roll-toolbar .toolbar-right {
|
||||
pointer-events: auto;
|
||||
/* max-width: 70%; */
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.piano-roll-toolbar .quant-button {
|
||||
@@ -101,6 +108,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.spectrogram-control-label {
|
||||
@@ -121,7 +129,7 @@
|
||||
.spectrogram-threshold-value {
|
||||
font-size: 10px;
|
||||
color: #e0e0e0;
|
||||
min-width: 44px;
|
||||
min-width: 30px;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
@@ -131,7 +139,7 @@
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
color: #e0e0e0;
|
||||
text-transform: uppercase;
|
||||
/* text-transform: uppercase; */
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
border-radius: 3px;
|
||||
|
||||
@@ -23,7 +23,7 @@ interface PianoRollProps {
|
||||
regionId: string | null;
|
||||
initialPosition?: { x: number; y: number };
|
||||
initialSize?: { width: number; height: number };
|
||||
mode?: 'midi-edit' | 'spectrogram';
|
||||
mode?: 'midi-edit' | 'spectrogram' | 'hybrid';
|
||||
audioRegion?: KGAudioRegion;
|
||||
trackId?: string;
|
||||
projectName?: string;
|
||||
@@ -40,6 +40,7 @@ const PianoRoll: React.FC<PianoRollProps> = ({
|
||||
projectName,
|
||||
}) => {
|
||||
const isSpectrogram = mode === 'spectrogram';
|
||||
const isHybrid = mode === 'hybrid';
|
||||
const { maxBars, tracks, updateTrack, timeSignature, showChatBox, showInstrumentSelection, keySignature, selectedMode, setSelectedMode, playheadPosition, isPlaying, autoScrollEnabled, bpm } = useProjectStore();
|
||||
|
||||
// Tool state for piano roll
|
||||
@@ -870,6 +871,11 @@ const PianoRoll: React.FC<PianoRollProps> = ({
|
||||
// Get the title for the piano roll based on the active region
|
||||
const getPianoRollTitle = () => {
|
||||
if (isSpectrogram) return audioRegion ? `SPECTROGRAM — ${audioRegion.getName()}` : 'SPECTROGRAM';
|
||||
if (isHybrid) {
|
||||
const midiName = activeRegion?.getName() ?? 'MIDI';
|
||||
const audioName = audioRegion?.getName() ?? 'Audio';
|
||||
return `${midiName} + ${audioName}`;
|
||||
}
|
||||
if (!activeRegion) return "EDIT NOTE CLIP";
|
||||
|
||||
// Calculate the bar and beat position of the region
|
||||
|
||||
@@ -27,7 +27,7 @@ interface PianoRollContentProps {
|
||||
selectedMode: string;
|
||||
keySignature: KeySignature;
|
||||
chordGuide: string;
|
||||
mode?: 'midi-edit' | 'spectrogram';
|
||||
mode?: 'midi-edit' | 'spectrogram' | 'hybrid';
|
||||
audioRegion?: KGAudioRegion;
|
||||
trackId?: string;
|
||||
projectName?: string;
|
||||
|
||||
@@ -24,7 +24,7 @@ interface PianoRollToolbarProps {
|
||||
chordGuide: string;
|
||||
onChordGuideChange: (value: string) => void;
|
||||
blinkButton?: string | null;
|
||||
mode?: 'midi-edit' | 'spectrogram';
|
||||
mode?: 'midi-edit' | 'spectrogram' | 'hybrid';
|
||||
thresholdDb?: number;
|
||||
onThresholdChange?: (db: number) => void;
|
||||
power?: number;
|
||||
@@ -51,11 +51,27 @@ const PianoRollToolbar: React.FC<PianoRollToolbarProps> = ({
|
||||
onPowerChange,
|
||||
}) => {
|
||||
const isSpectrogram = mode === 'spectrogram';
|
||||
const showMidiControls = mode !== 'spectrogram'; // midi-edit and hybrid
|
||||
const showSpecControls = mode === 'spectrogram' || mode === 'hybrid';
|
||||
|
||||
return (
|
||||
<div className="piano-roll-toolbar">
|
||||
{!isSpectrogram && (
|
||||
{showMidiControls && (
|
||||
<div className="toolbar-left">
|
||||
<button
|
||||
className={`tool-button ${activeTool === 'pointer' ? 'active' : ''}`}
|
||||
onClick={() => onToolSelect('pointer')}
|
||||
title="Pointer Tool"
|
||||
>
|
||||
<FaMousePointer />
|
||||
</button>
|
||||
<button
|
||||
className={`tool-button ${activeTool === 'pencil' ? 'active' : ''}`}
|
||||
onClick={() => onToolSelect('pencil')}
|
||||
title="Pencil Tool"
|
||||
>
|
||||
<FaPencilAlt />
|
||||
</button>
|
||||
<KGDropdown
|
||||
options={Object.entries(KGCore.FUNCTIONAL_CHORDS_DATA).map(([id, data]) => ({ label: data.name, value: id }))}
|
||||
value={selectedMode}
|
||||
@@ -80,27 +96,8 @@ const PianoRollToolbar: React.FC<PianoRollToolbarProps> = ({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isSpectrogram && (
|
||||
<div className="toolbar-center">
|
||||
<button
|
||||
className={`tool-button ${activeTool === 'pointer' ? 'active' : ''}`}
|
||||
onClick={() => onToolSelect('pointer')}
|
||||
title="Pointer Tool"
|
||||
>
|
||||
<FaMousePointer />
|
||||
</button>
|
||||
<button
|
||||
className={`tool-button ${activeTool === 'pencil' ? 'active' : ''}`}
|
||||
onClick={() => onToolSelect('pencil')}
|
||||
title="Pencil Tool"
|
||||
>
|
||||
<FaPencilAlt />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="toolbar-right">
|
||||
{!isSpectrogram && (
|
||||
{showMidiControls && (
|
||||
<>
|
||||
<KGDropdown
|
||||
options={KGPianoRollState.SNAP_OPTIONS}
|
||||
@@ -127,7 +124,7 @@ const PianoRollToolbar: React.FC<PianoRollToolbarProps> = ({
|
||||
</>
|
||||
)}
|
||||
|
||||
{isSpectrogram && (
|
||||
{showSpecControls && (
|
||||
<div className="spectrogram-toolbar-controls">
|
||||
<span className="spectrogram-control-label">Floor</span>
|
||||
<input
|
||||
|
||||
Reference in New Issue
Block a user