fix: allow user to create audio region by triggering audio upload pop-up

This commit is contained in:
Xiaohan-Tian
2026-04-16 21:05:05 -07:00
parent cb027b6c20
commit 9d9190413d
+94 -3
View File
@@ -1,8 +1,8 @@
import React, { useRef } from 'react';
import React, { useRef, useState } from 'react';
import { KGTrack, TrackType } from '../../core/track/KGTrack';
import { KGMidiRegion } from '../../core/region/KGMidiRegion';
import TrackGridItem from './TrackGridItem';
import { Playhead } from '../common';
import { Playhead, FileImportModal } from '../common';
import type { RegionUI } from '../interfaces';
import { DEBUG_MODE, REGION_CONSTANTS } from '../../constants';
import { KGMainContentState } from '../../core/state/KGMainContentState';
@@ -48,6 +48,8 @@ const TrackGridPanel: React.FC<TrackGridPanelProps> = ({
onExternalDropComplete,
}) => {
const gridContainerRef = useRef<HTMLDivElement>(null);
const [showAudioImportModal, setShowAudioImportModal] = useState(false);
const pendingAudioImportRef = useRef<{ barNumber: number; trackIndex: number } | null>(null);
// Utility function to create a region at a specific position
const createRegionAtPosition = (e: React.MouseEvent<HTMLDivElement>, trackIndex: number) => {
@@ -74,8 +76,13 @@ const TrackGridPanel: React.FC<TrackGridPanelProps> = ({
const track = tracks[trackIndex];
const trackId = track.getId().toString();
// Don't allow manual region creation on audio tracks
// For audio tracks, show the file import modal instead of creating a blank region
if (track.getType() === TrackType.Wave) {
const snap = KGMainContentState.instance().isSnappingEnabled();
const rawBar = relativeX / barWidth + 1;
const snappedBarNumber = Math.max(1, snap ? Math.round(rawBar) : rawBar);
pendingAudioImportRef.current = { barNumber: snappedBarNumber, trackIndex };
setShowAudioImportModal(true);
return;
}
@@ -135,6 +142,81 @@ const TrackGridPanel: React.FC<TrackGridPanelProps> = ({
}
};
// Handle audio file import after the user picks a file from the modal
const handleAudioFileImport = async (file: File) => {
setShowAudioImportModal(false);
const pending = pendingAudioImportRef.current;
if (!pending) return;
pendingAudioImportRef.current = null;
const { barNumber, trackIndex } = pending;
const track = tracks[trackIndex];
if (!track) return;
const beatsPerBar = timeSignature.numerator;
const fileId = KGAudioFileStorage.generateAudioFileId(file.name);
try {
const arrayBuffer = await file.arrayBuffer();
const toneBuffer = new Tone.ToneAudioBuffer();
await new Promise<void>((resolve, reject) => {
const audioContext = Tone.getContext().rawContext as AudioContext;
audioContext.decodeAudioData(
arrayBuffer.slice(0),
(decoded) => { toneBuffer.set(decoded); resolve(); },
(err) => reject(err)
);
});
const audioDurationSeconds = toneBuffer.duration;
await KGAudioFileStorage.storeAudioFile(projectName, fileId, file);
KGAudioInterface.instance().loadAudioBufferForTrack(
track.getId().toString(),
fileId,
toneBuffer
);
const bpm = KGCore.instance().getCurrentProject().getBpm();
const durationInBeats = audioDurationSeconds * (bpm / 60);
const insertBeat = (barNumber - 1) * beatsPerBar;
const lengthInBars = Math.max(1, Math.ceil(durationInBeats / beatsPerBar));
const prevMaxBars = maxBars;
const newMaxBars = Math.max(maxBars, barNumber + lengthInBars - 1);
const cmd = new ImportAudioCommand(
track.getId() as unknown as number,
trackIndex,
fileId,
file.name,
audioDurationSeconds,
insertBeat,
durationInBeats,
prevMaxBars,
newMaxBars
);
KGCore.instance().executeCommand(cmd);
const created = cmd.getCreatedRegion();
if (created && onExternalDropComplete) {
const regionUI: RegionUI = {
id: created.getId(),
trackId: track.getId().toString(),
trackIndex,
barNumber,
length: lengthInBars,
name: created.getName(),
};
onExternalDropComplete(trackIndex, regionUI);
}
if (DEBUG_MODE.TRACK_GRID_PANEL) {
console.log(`[TrackGrid] Imported audio "${file.name}" to track ${trackIndex}, bar ${barNumber}, ${audioDurationSeconds.toFixed(2)}s`);
}
} catch (err) {
console.error('[TrackGrid] Audio import from click failed:', err);
}
};
// Handle double click on track grid to create region
const handleTrackGridDoubleClick = (e: React.MouseEvent<HTMLDivElement>, trackIndex: number) => {
// Only allow double-click creation in pointer mode
@@ -572,6 +654,15 @@ const TrackGridPanel: React.FC<TrackGridPanelProps> = ({
onKGOneClipDrop={handleExternalDrop}
/>
))}
<FileImportModal
isVisible={showAudioImportModal}
onClose={() => setShowAudioImportModal(false)}
onFileImport={handleAudioFileImport}
acceptedTypes={['.wav', '.mp3', '.ogg', '.flac', '.aac']}
title="Import Audio"
description="Drag and drop your audio file here"
/>
</div>
);
};