feat: implemented global tempo (bpm) track

This commit is contained in:
Xiaohan-Tian
2026-05-23 23:18:47 -07:00
parent 8eb3f5d84d
commit 7d275802c9
23 changed files with 1700 additions and 131 deletions
+17 -5
View File
@@ -10,6 +10,8 @@ import { useProjectStore } from '../../stores/projectStore';
import { KGMainContentState } from '../../core/state/KGMainContentState';
import type { AudioRecordingPeak } from '../../core/audio-interface/KGAudioRecorder';
import type { RegionPreviewContentStyle } from '../interfaces';
import { KGCore } from '../../core/KGCore';
import { beatRangeToSeconds } from '../../util/globalTrackUtil';
const DRAG_START_THRESHOLD_PX = 4;
@@ -48,6 +50,7 @@ interface RegionItemProps {
isPreview?: boolean;
isAudioRegion?: boolean;
previewContentStyle?: RegionPreviewContentStyle;
redrawVersion?: number;
}
const RegionItem: React.FC<RegionItemProps> = ({
@@ -76,6 +79,7 @@ const RegionItem: React.FC<RegionItemProps> = ({
isPreview = false,
isAudioRegion = false,
previewContentStyle,
redrawVersion = 0,
}) => {
// Get selection state and time signature from store
const { selectedRegionIds, timeSignature, bpm } = useProjectStore();
@@ -273,10 +277,18 @@ const RegionItem: React.FC<RegionItemProps> = ({
const clipStartOffsetSeconds = audioRegion ? audioRegion.getClipStartOffsetSeconds() : 0;
const clipStartSample = Math.floor(clipStartOffsetSeconds * sampleRate);
// Calculate visible duration from region length in beats
const secondsPerBeat = 60 / bpm;
const currentProject = KGCore.instance().getCurrentProject();
const regionLengthBeats = audioRegion ? audioRegion.getLength() : 0;
const visibleDurationSeconds = regionLengthBeats * secondsPerBeat;
const visibleDurationSeconds = audioRegion
? Math.min(
beatRangeToSeconds(
currentProject,
audioRegion.getStartFromBeat(),
audioRegion.getStartFromBeat() + regionLengthBeats
),
Math.max(0, audioRegion.getAudioDurationSeconds() - clipStartOffsetSeconds)
)
: 0;
const visibleSamples = Math.floor(visibleDurationSeconds * sampleRate);
// Clamp to buffer boundaries
@@ -383,7 +395,7 @@ const RegionItem: React.FC<RegionItemProps> = ({
} else {
renderNotesOnCanvas();
}
}, [midiRegion, audioRegion, audioBuffer, previewWaveformPeaks, timeSignature, bpm, id, noteUpdateTrigger, barNumber, length, previewContentStyle?.width]);
}, [midiRegion, audioRegion, audioBuffer, previewWaveformPeaks, timeSignature, bpm, id, noteUpdateTrigger, barNumber, length, previewContentStyle?.width, redrawVersion]);
// Re-render canvas when region content size changes
useEffect(() => {
@@ -406,7 +418,7 @@ const RegionItem: React.FC<RegionItemProps> = ({
resizeObserver.unobserve(previewContentRef.current);
}
};
}, [midiRegion, audioRegion, audioBuffer, previewWaveformPeaks, timeSignature, bpm, previewContentStyle?.width]);
}, [midiRegion, audioRegion, audioBuffer, previewWaveformPeaks, timeSignature, bpm, previewContentStyle?.width, redrawVersion]);
// Handle mouse movement to detect edge proximity
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {