From 5aa74e61a850ccf60792667f60f3ef48b21b0525 Mon Sep 17 00:00:00 2001 From: Xiaohan-Tian <157918347+Xiaohan-Tian@users.noreply.github.com> Date: Mon, 6 Jul 2026 22:56:22 -0700 Subject: [PATCH] feat: implemented v1 audio-to-midi conversion (polyphonic only, algorithm-based) --- public/test-data/audio-to-midi-test01.mp3 | Bin 0 -> 164831 bytes src/components/common/DialogProvider.css | 32 ++ src/components/common/DialogProvider.test.tsx | 118 ++++++ src/components/common/DialogProvider.tsx | 246 +++++++++++- src/components/common/index.ts | 21 +- src/components/piano-roll/PianoRoll.tsx | 225 ++++++++++- .../piano-roll/PianoRollToolbar.test.tsx | 40 ++ .../piano-roll/PianoRollToolbar.tsx | 22 +- src/i18n/messages/en_us.ts | 14 + src/i18n/messages/fr_fr.ts | 14 + src/i18n/messages/zh_cn.ts | 14 + src/i18n/messages/zh_hk.ts | 14 + .../audio-to-midi.integration.test.ts | 67 ++++ src/util/audioToMidi.test.ts | 118 ++++++ src/util/audioToMidi.ts | 161 ++++++++ src/util/audioToMidiCore.ts | 358 ++++++++++++++++++ src/util/dialogUtil.ts | 49 +++ src/workers/audioToMidiWorker.ts | 22 ++ 18 files changed, 1518 insertions(+), 17 deletions(-) create mode 100644 public/test-data/audio-to-midi-test01.mp3 create mode 100644 src/test/integration/audio-to-midi.integration.test.ts create mode 100644 src/util/audioToMidi.test.ts create mode 100644 src/util/audioToMidi.ts create mode 100644 src/util/audioToMidiCore.ts create mode 100644 src/workers/audioToMidiWorker.ts diff --git a/public/test-data/audio-to-midi-test01.mp3 b/public/test-data/audio-to-midi-test01.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..ff72a491d1406d1c0243f63de1a22be02928838f GIT binary patch literal 164831 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 z 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%0Dv^yN}Hq(-L^y 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 K6yU!T?EeGVsplmC literal 0 HcmV?d00001 diff --git a/src/components/common/DialogProvider.css b/src/components/common/DialogProvider.css index 903e79b..ab49e9a 100644 --- a/src/components/common/DialogProvider.css +++ b/src/components/common/DialogProvider.css @@ -153,6 +153,17 @@ gap: 16px; } +.dialog-two-column-row { + display: flex; + align-items: flex-start; + gap: 12px; +} + +.dialog-half-width-group { + flex: 1 1 0; + min-width: 0; +} + .dialog-hint-card { margin-top: 12px; background-color: #252525; @@ -221,6 +232,27 @@ accent-color: #5a9fd4; } +.dialog-compact-checkbox-row { + flex: 1 1 0; + min-width: 0; +} + +.dialog-compact-checkbox-row span { + line-height: 1.3; +} + +.dialog-compact-input { + margin-top: 0; +} + +.dialog-inline-sep { + color: #b0b0b0; + font-size: 13px; + font-weight: 500; + line-height: 1; + flex: 0 0 auto; +} + .dialog-footer { display: flex; justify-content: flex-end; diff --git a/src/components/common/DialogProvider.test.tsx b/src/components/common/DialogProvider.test.tsx index 68af2b2..2f38353 100644 --- a/src/components/common/DialogProvider.test.tsx +++ b/src/components/common/DialogProvider.test.tsx @@ -19,6 +19,7 @@ vi.mock('../../core/config/ConfigManager', () => ({ import DialogProvider from './DialogProvider'; import { + showAudioToMidiOptions, showChoice, showChordDetectionOptions, showMidiChordDetectionOptions, @@ -170,6 +171,123 @@ describe('DialogProvider MIDI chord detection dialog', () => { }); }); +describe('DialogProvider audio-to-MIDI dialog', () => { + it('opens with the expected defaults and resolves cancel to null', async () => { + let resolved: unknown = 'pending'; + + render( + + + , + ); + + fireEvent.click(screen.getByRole('button', { name: 'Open audio-to-midi' })); + + expect(screen.getByText('Convert to MIDI')).toBeInTheDocument(); + expect(screen.getByText('Experimental Feature')).toBeInTheDocument(); + expect(screen.getByText(/best results, switch to spectrogram view/i)).toBeInTheDocument(); + expect(screen.getByLabelText('Monophonic only (v1)')).toBeChecked(); + expect(screen.getByLabelText('Monophonic only (v1)')).toBeDisabled(); + expect(screen.getByLabelText('Use current Floor dB')).toBeChecked(); + expect(screen.getByLabelText('Manual Floor dB')).toBeDisabled(); + expect(screen.getByLabelText('Convert loop-range audio clip only')).toBeChecked(); + expect(screen.getByLabelText('Convert loop-range audio clip only')).toBeDisabled(); + + fireEvent.click(screen.getByRole('button', { name: 'Cancel' })); + finishDialogCloseAnimation(); + + await waitFor(() => expect(resolved).toBeNull()); + }); + + it('returns adjusted conversion options when confirmed', async () => { + let resolved: unknown = null; + + render( + + + , + ); + + fireEvent.click(screen.getByRole('button', { name: 'Open audio-to-midi' })); + fireEvent.click(screen.getByLabelText('Use current Floor dB')); + fireEvent.change(screen.getByLabelText('Manual Floor dB'), { target: { value: '-16' } }); + fireEvent.change(screen.getByLabelText('Pitch range start'), { target: { value: '21' } }); + fireEvent.change(screen.getByLabelText('Pitch range end'), { target: { value: '38' } }); + fireEvent.change(screen.getByLabelText('Quantize note start'), { target: { value: '1/8' } }); + fireEvent.change(screen.getByLabelText('Quantize note length'), { target: { value: '1/8' } }); + fireEvent.click(screen.getByLabelText('Convert loop-range audio clip only')); + fireEvent.click(screen.getByLabelText('Group adjacent pitches to the strongest bin')); + fireEvent.change(screen.getByLabelText('Target MIDI track'), { target: { value: 'track-2' } }); + fireEvent.click(screen.getByRole('button', { name: 'OK' })); + finishDialogCloseAnimation(); + + await waitFor(() => expect(resolved).toEqual({ + monophonic: true, + useCurrentFloorDb: false, + manualFloorDb: -16, + pitchRangeStart: 21, + pitchRangeEnd: 38, + quantizeNoteStart: '1/8', + quantizeNoteLength: '1/8', + convertLoopRangeOnly: false, + groupAdjacentPitchesToHighest: false, + targetTrackId: 'track-2', + })); + }); +}); + describe('DialogProvider tempo detection dialog', () => { it('opens the tempo detection modal with the expected defaults and resolves cancel to null', async () => { let resolved: unknown = 'pending'; diff --git a/src/components/common/DialogProvider.tsx b/src/components/common/DialogProvider.tsx index ac2bdc7..cd161ca 100644 --- a/src/components/common/DialogProvider.tsx +++ b/src/components/common/DialogProvider.tsx @@ -2,9 +2,11 @@ import React, { useState, useCallback, useRef } from 'react'; import './DialogProvider.css'; import { FaTimes } from 'react-icons/fa'; import { ConfigManager } from '../../core/config/ConfigManager'; +import { KGPianoRollState } from '../../core/state/KGPianoRollState'; import { useI18n } from '../../i18n/useI18n'; import { registerDialogFns } from '../../util/dialogUtil'; import type { + AudioToMidiOptionsResult, ChoiceOption, ChordDetectionOptionsResult, ConfirmOptions, @@ -16,7 +18,7 @@ import type { } from '../../util/dialogUtil'; interface DialogInfo { - type: 'alert' | 'confirm' | 'prompt' | 'timesig' | 'choice' | 'chord-detection' | 'midi-chord-detection' | 'tempo-detection' | 'tempo-apply'; + type: 'alert' | 'confirm' | 'prompt' | 'timesig' | 'choice' | 'chord-detection' | 'midi-chord-detection' | 'tempo-detection' | 'tempo-apply' | 'audio-to-midi'; message: string; options?: ConfirmOptions | PromptOptions; defaultValue?: string; @@ -25,6 +27,9 @@ interface DialogInfo { defaultChordDetectionOptions?: ChordDetectionOptionsResult; defaultMidiChordDetectionOptions?: MidiChordDetectionOptionsResult; defaultTempoDetectionOptions?: TempoDetectionOptionsResult; + defaultAudioToMidiOptions?: AudioToMidiOptionsResult; + audioToMidiTargetTracks?: ChoiceOption[]; + audioToMidiLoopModeEnabled?: boolean; } const DEFAULT_AUDIO_CHORD_DETECTION_OPTIONS: ChordDetectionOptionsResult = { @@ -45,6 +50,19 @@ const DEFAULT_TEMPO_DETECTION_OPTIONS: TempoDetectionOptionsResult = { maxTempo: 180, }; +const DEFAULT_AUDIO_TO_MIDI_OPTIONS: AudioToMidiOptionsResult = { + monophonic: true, + useCurrentFloorDb: true, + manualFloorDb: -25, + pitchRangeStart: 12, + pitchRangeEnd: 107, + quantizeNoteStart: '1/16', + quantizeNoteLength: '1/16', + convertLoopRangeOnly: true, + groupAdjacentPitchesToHighest: true, + targetTrackId: '', +}; + const DialogProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { const { t } = useI18n(); const [dialog, setDialog] = useState(null); @@ -55,6 +73,8 @@ const DialogProvider: React.FC<{ children: React.ReactNode }> = ({ children }) = const [chordDetectionOptions, setChordDetectionOptions] = useState(DEFAULT_AUDIO_CHORD_DETECTION_OPTIONS); const [midiChordDetectionOptions, setMidiChordDetectionOptions] = useState(DEFAULT_MIDI_CHORD_DETECTION_OPTIONS); const [tempoDetectionOptions, setTempoDetectionOptions] = useState(DEFAULT_TEMPO_DETECTION_OPTIONS); + const [audioToMidiOptions, setAudioToMidiOptions] = useState(DEFAULT_AUDIO_TO_MIDI_OPTIONS); + const [audioToMidiTargetTracks, setAudioToMidiTargetTracks] = useState([]); const [autoAlignRegionToBeat, setAutoAlignRegionToBeat] = useState(false); // eslint-disable-next-line @typescript-eslint/no-explicit-any const resolveRef = useRef<((value: any) => void) | null>(null); @@ -142,6 +162,30 @@ const DialogProvider: React.FC<{ children: React.ReactNode }> = ({ children }) = }); }, []); + const openAudioToMidiOptions = useCallback(( + message: string, + targetTracks: ChoiceOption[], + loopModeEnabled: boolean, + defaultValue?: AudioToMidiOptionsResult, + ): Promise => { + return new Promise((resolve) => { + resolveRef.current = resolve; + const fallbackTrackId = targetTracks[0]?.value ?? ''; + setAudioToMidiTargetTracks(targetTracks); + setAudioToMidiOptions({ + ...(defaultValue ?? DEFAULT_AUDIO_TO_MIDI_OPTIONS), + targetTrackId: defaultValue?.targetTrackId || fallbackTrackId, + }); + setDialog({ + type: 'audio-to-midi', + message, + defaultAudioToMidiOptions: defaultValue, + audioToMidiTargetTracks: targetTracks, + audioToMidiLoopModeEnabled: loopModeEnabled, + }); + }); + }, []); + const close = useCallback((value: unknown) => { pendingValueRef.current = value; setIsClosing(true); @@ -158,6 +202,8 @@ const DialogProvider: React.FC<{ children: React.ReactNode }> = ({ children }) = setChordDetectionOptions(DEFAULT_AUDIO_CHORD_DETECTION_OPTIONS); setMidiChordDetectionOptions(DEFAULT_MIDI_CHORD_DETECTION_OPTIONS); setTempoDetectionOptions(DEFAULT_TEMPO_DETECTION_OPTIONS); + setAudioToMidiOptions(DEFAULT_AUDIO_TO_MIDI_OPTIONS); + setAudioToMidiTargetTracks([]); setAutoAlignRegionToBeat(false); if (resolveRef.current) { resolveRef.current(pendingValueRef.current); @@ -180,6 +226,7 @@ const DialogProvider: React.FC<{ children: React.ReactNode }> = ({ children }) = openMidiChordDetectionOptions, openTempoDetectionOptions, openTempoApply, + openAudioToMidiOptions, ); } @@ -195,6 +242,7 @@ const DialogProvider: React.FC<{ children: React.ReactNode }> = ({ children }) = const isMidiChordDetection = dialog.type === 'midi-chord-detection'; const isTempoDetection = dialog.type === 'tempo-detection'; const isTempoApply = dialog.type === 'tempo-apply'; + const isAudioToMidi = dialog.type === 'audio-to-midi'; const promptOptions = isPrompt ? (dialog.options as PromptOptions | undefined) : undefined; const isKGOneEnabled = (ConfigManager.instance().get('general.kgone.enabled') as boolean | undefined) ?? false; @@ -203,10 +251,12 @@ const DialogProvider: React.FC<{ children: React.ReactNode }> = ({ children }) = : isTimeSig ? t('dialog.title.timeSignature') : isTempoDetection - ? t('dialog.title.tempoDetection') - : isTempoApply - ? t('dialog.title.applyTempo') - : (isChordDetection || isMidiChordDetection) + ? t('dialog.title.tempoDetection') + : isTempoApply + ? t('dialog.title.applyTempo') + : isAudioToMidi + ? t('dialog.title.audioToMidi') + : (isChordDetection || isMidiChordDetection) ? t('dialog.title.chordDetection') : isPrompt ? t('dialog.title.input') @@ -218,11 +268,11 @@ const DialogProvider: React.FC<{ children: React.ReactNode }> = ({ children }) = const handleOverlayClick = (e: React.MouseEvent) => { if (e.target === e.currentTarget && mouseDownOnOverlay.current) { - close(isAlert ? undefined : (isPrompt || isTimeSig || isChoice || isChordDetection || isTempoDetection || isTempoApply) ? null : false); + close(isAlert ? undefined : (isPrompt || isTimeSig || isChoice || isChordDetection || isMidiChordDetection || isTempoDetection || isTempoApply || isAudioToMidi) ? null : false); } }; - const handleCancel = () => close(isAlert ? undefined : (isPrompt || isTimeSig || isChoice || isChordDetection || isTempoDetection || isTempoApply) ? null : false); + const handleCancel = () => close(isAlert ? undefined : (isPrompt || isTimeSig || isChoice || isChordDetection || isMidiChordDetection || isTempoDetection || isTempoApply || isAudioToMidi) ? null : false); const handleConfirm = () => { if (isAlert) { close(undefined); return; } @@ -243,6 +293,10 @@ const DialogProvider: React.FC<{ children: React.ReactNode }> = ({ children }) = close(tempoDetectionOptions); return; } + if (isAudioToMidi) { + close(audioToMidiOptions); + return; + } if (isTempoApply) { close({ action: dialog.choices?.[dialog.choices.length - 1]?.value ?? '', @@ -274,14 +328,30 @@ const DialogProvider: React.FC<{ children: React.ReactNode }> = ({ children }) = setTempoDetectionOptions(current => ({ ...current, [key]: value })); }; + const updateAudioToMidiOption = ( + key: K, + value: AudioToMidiOptionsResult[K], + ) => { + setAudioToMidiOptions(current => ({ ...current, [key]: value })); + }; + const detectionHintText = isChordDetection ? t('dialog.chordHint.audio') : isMidiChordDetection ? t('dialog.chordHint.midi') : isTempoDetection ? t('dialog.chordHint.tempo') + : isAudioToMidi + ? t('dialog.audioToMidiHint') : null; + const audioToMidiPitchOptions = Array.from({ length: 96 }, (_, index) => { + const pitch = 12 + index; + const noteNames = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']; + const noteName = `${noteNames[pitch % 12]}${Math.floor(pitch / 12) - 1}`; + return { value: String(pitch), label: noteName }; + }); + return ( <> {children} @@ -298,7 +368,9 @@ const DialogProvider: React.FC<{ children: React.ReactNode }> = ({ children }) =

-

{dialog.message}

+ {(!isAudioToMidi || dialog.message.trim().length > 0) && ( +

{dialog.message}

+ )} {detectionHintText && (
{t('dialog.experimentalFeature')}
@@ -347,6 +419,160 @@ const DialogProvider: React.FC<{ children: React.ReactNode }> = ({ children }) = />
)} + {isAudioToMidi && ( +
+
+ + +
+
+
+ + {audioToMidiOptions.manualFloorDb} dB +
+ updateAudioToMidiOption('manualFloorDb', Number(e.target.value))} + disabled={audioToMidiOptions.useCurrentFloorDb} + /> +
+
+
+ +
+
+ + {t('dialog.to')} + +
+
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+ + +
+
+ +
+ +
+
+ )} {isChordDetection && (
@@ -545,14 +771,14 @@ const DialogProvider: React.FC<{ children: React.ReactNode }> = ({ children }) = diff --git a/src/components/common/index.ts b/src/components/common/index.ts index d9ba3a7..38fa196 100644 --- a/src/components/common/index.ts +++ b/src/components/common/index.ts @@ -7,5 +7,22 @@ export { default as OpenProjectModal } from './OpenProjectModal'; export { default as DialogProvider } from './DialogProvider'; export { default as TrackCreateDialog } from './TrackCreateDialog'; export { default as FloatingPopup } from './FloatingPopup'; -export { showAlert, showChordDetectionOptions, showConfirm, showPrompt, showTempoApply, showTempoDetectionOptions, showTimeSigPrompt } from '../../util/dialogUtil'; -export type { ChordDetectionOptionsResult, ConfirmOptions, PromptOptions, TempoApplyResult, TempoDetectionOptionsResult, TimeSigResult } from '../../util/dialogUtil'; +export { + showAlert, + showAudioToMidiOptions, + showChordDetectionOptions, + showConfirm, + showPrompt, + showTempoApply, + showTempoDetectionOptions, + showTimeSigPrompt, +} from '../../util/dialogUtil'; +export type { + AudioToMidiOptionsResult, + ChordDetectionOptionsResult, + ConfirmOptions, + PromptOptions, + TempoApplyResult, + TempoDetectionOptionsResult, + TimeSigResult, +} from '../../util/dialogUtil'; diff --git a/src/components/piano-roll/PianoRoll.tsx b/src/components/piano-roll/PianoRoll.tsx index 16b9cae..b13e4d6 100644 --- a/src/components/piano-roll/PianoRoll.tsx +++ b/src/components/piano-roll/PianoRoll.tsx @@ -10,6 +10,7 @@ import PianoRollHeader from './PianoRollHeader'; import PianoRollToolbar from './PianoRollToolbar'; import NoteAttributeBar from './NoteAttributeBar'; import PianoRollContent from './PianoRollContent'; +import { LoadingOverlay } from '../common'; import { KGCore } from '../../core/KGCore'; import { KGMidiNote } from '../../core/midi/KGMidiNote'; import { KGMidiTrack, type InstrumentType } from '../../core/track/KGMidiTrack'; @@ -21,11 +22,18 @@ import { type PianoRollSnapValue, } from '../../core/state/KGPianoRollState'; import { ConfigManager } from '../../core/config/ConfigManager'; -import { beatsToBar } from '../../util/midiUtil'; -import { ReplaceChordRegionsInRangeCommand, UpdateRegionCommand } from '../../core/commands'; +import { beatsToBar, type RawMidiNote } from '../../util/midiUtil'; +import { ImportMidiClipCommand, ReplaceChordRegionsInRangeCommand, UpdateRegionCommand } from '../../core/commands'; import { KGAudioInterface } from '../../core/audio-interface/KGAudioInterface'; import { KGAudioFileStorage } from '../../core/io/KGAudioFileStorage'; -import { showAlert, showChordDetectionOptions, showMidiChordDetectionOptions, showTempoApply, showTempoDetectionOptions } from '../../util/dialogUtil'; +import { + showAlert, + showAudioToMidiOptions, + showChordDetectionOptions, + showMidiChordDetectionOptions, + showTempoApply, + showTempoDetectionOptions, +} from '../../util/dialogUtil'; import { matchesKeyboardShortcut } from '../../util/osUtil'; import { resolveChordGuideItems } from '../../util/chordGuideDataUtil'; import { @@ -59,6 +67,7 @@ import { type MidiChordDetectionOptions, } from '../../util/midiChordDetection'; import type { AudioChordDetectionWorkerMessage } from '../../workers/audioChordDetectionWorker'; +import type { AudioToMidiWorkerResult } from '../../workers/audioToMidiWorker'; import type { PianoRollAutomationType } from './pianoRollAutomation'; import type { SheetMeasureMetric } from './sheetNotationTypes'; import { getSheetPlayheadPixel, getSheetQuantizationOptions, parseSheetQuantization } from './sheetNotation'; @@ -70,6 +79,11 @@ import { } from './pianoRollViewport'; import { getNextChordGuideSelection, resolveChordGuideContext, type ChordGuideFunction } from './chordGuideUtil'; import { useI18n } from '../../i18n/useI18n'; +import { + buildAudioToMidiAnalysisSpan, + convertDetectedAudioNotesToRawMidiNotes, +} from '../../util/audioToMidi'; +import { TrackType } from '../../core/track/KGTrack'; interface PianoRollProps { onClose: () => void; @@ -84,6 +98,13 @@ interface PianoRollProps { projectName?: string; } +type AudioToMidiWorkerHandle = { + onmessage: ((event: MessageEvent) => void) | null; + onerror: ((this: AbstractWorker, ev: ErrorEvent) => unknown) | null; + postMessage: Worker['postMessage']; + terminate: () => void; +}; + const PianoRoll: React.FC = ({ onClose, regionId, @@ -101,7 +122,32 @@ const PianoRoll: React.FC = ({ const isAudioWaveform = currentMode === 'audio-waveform'; const isAudioOnly = isAudioWaveform || isSpectrogram; const isHybrid = currentMode === 'hybrid'; - const { maxBars, tracks, updateTrack, updateRegionProperties, timeSignature, showChatBox, showKGOnePanel, showEventListPanel, showInstrumentSelection, keySignature, selectedMode, setSelectedMode, playheadPosition, isPlaying, autoScrollEnabled, bpm, pianoRollScrollRequest, selectedNoteIds, selectedRegionIds, automationRedrawVersion, refreshProjectState, setBpm } = useProjectStore(); + const { + maxBars, + tracks, + updateTrack, + updateRegionProperties, + timeSignature, + showChatBox, + showKGOnePanel, + showEventListPanel, + showInstrumentSelection, + keySignature, + selectedMode, + setSelectedMode, + playheadPosition, + isPlaying, + autoScrollEnabled, + bpm, + pianoRollScrollRequest, + selectedNoteIds, + selectedRegionIds, + automationRedrawVersion, + refreshProjectState, + setBpm, + isLooping, + loopingRange, + } = useProjectStore(); const { t } = useI18n(); // Tool state for piano roll @@ -115,6 +161,7 @@ const PianoRoll: React.FC = ({ const [isDetectingChords, setIsDetectingChords] = useState(false); const [detectChordProgressPercent, setDetectChordProgressPercent] = useState(0); const [isDetectingTempo, setIsDetectingTempo] = useState(false); + const [isConvertingToMidi, setIsConvertingToMidi] = useState(false); // Piano roll zoom (1x–8x); updates --region-grid-beat-width CSS variable const [pianoRollZoom, setPianoRollZoom] = useState(() => KGPianoRollState.instance().getPianoRollZoom()); @@ -164,6 +211,14 @@ const PianoRoll: React.FC = ({ return tracks.find(track => track.getId().toString() === activeRegion.getTrackId()) ?? null; }, [activeRegion, tracks]); + const availableMidiTracks = useMemo( + () => tracks.filter(track => track.getType() === TrackType.MIDI), + [tracks], + ); + const sourceAudioTrack = useMemo( + () => audioRegion ? tracks.find(track => track.getId().toString() === audioRegion.getTrackId()) ?? null : null, + [audioRegion, tracks], + ); const activeInstrument = useMemo(() => ( parentMidiTrack instanceof KGMidiTrack ? parentMidiTrack.getInstrument() : 'acoustic_grand_piano' ), [parentMidiTrack]); @@ -514,6 +569,165 @@ const PianoRoll: React.FC = ({ } }, [activeEditableRegionId, selectedRegionIds, tracks, updateRegionProperties]); + const getMonoPcmForAudioRegion = useCallback(async () => { + if (!audioRegion || !projectName || !trackId) { + throw new Error('Audio-to-MIDI conversion requires an active audio region with track and project context.'); + } + + let audioBuffer = KGAudioInterface.instance().getAudioBuffer(trackId, audioRegion.getAudioFileId()); + if (!audioBuffer) { + const rawBuffer = await KGAudioFileStorage.loadAudioFile(projectName, audioRegion.getAudioFileId()); + const audioContext = Tone.getContext().rawContext as AudioContext; + audioBuffer = await audioContext.decodeAudioData(rawBuffer); + } + + const monoPcm = new Float32Array(audioBuffer.length); + for (let channelIndex = 0; channelIndex < audioBuffer.numberOfChannels; channelIndex++) { + const channelData = audioBuffer.getChannelData(channelIndex); + for (let sampleIndex = 0; sampleIndex < audioBuffer.length; sampleIndex++) { + monoPcm[sampleIndex] += channelData[sampleIndex] / audioBuffer.numberOfChannels; + } + } + + return { + pcm: monoPcm, + sampleRate: audioBuffer.sampleRate, + }; + }, [audioRegion, projectName, trackId]); + + const handleConvertToMidi = useCallback(async () => { + if (!audioRegion) { + await showAlert('Open an audio region before converting it to MIDI.'); + return; + } + + if (!projectName || !trackId) { + await showAlert('Open an audio region in the piano roll before converting it to MIDI.'); + return; + } + + if (availableMidiTracks.length === 0) { + return; + } + + const targetTrackChoices = availableMidiTracks.map(track => ({ + label: track.getName(), + value: track.getId().toString(), + })); + const dialogResult = await showAudioToMidiOptions( + '', + targetTrackChoices, + isLooping, + { + monophonic: true, + useCurrentFloorDb: true, + manualFloorDb: spectrogramThresholdDb, + pitchRangeStart: 12, + pitchRangeEnd: 107, + quantizeNoteStart: '1/16', + quantizeNoteLength: '1/16', + convertLoopRangeOnly: true, + groupAdjacentPitchesToHighest: true, + targetTrackId: targetTrackChoices[0]?.value ?? '', + }, + ); + if (!dialogResult) { + return; + } + + const project = KGCore.instance().getCurrentProject(); + const analysisSpan = buildAudioToMidiAnalysisSpan(project, audioRegion, { + loopModeEnabled: isLooping, + convertLoopRangeOnly: dialogResult.convertLoopRangeOnly, + loopingRange, + }); + if (!analysisSpan) { + await showAlert('The loop range does not overlap with the selected audio region.'); + return; + } + + const targetTrack = availableMidiTracks.find(track => track.getId().toString() === dialogResult.targetTrackId); + if (!targetTrack) { + await showAlert('Please choose a valid MIDI target track.'); + return; + } + + const floorDb = dialogResult.useCurrentFloorDb + ? spectrogramThresholdDb + : dialogResult.manualFloorDb; + + let worker: AudioToMidiWorkerHandle | null = null; + setIsConvertingToMidi(true); + + try { + const { pcm, sampleRate } = await getMonoPcmForAudioRegion(); + const detectedNotes = await new Promise((resolve, reject) => { + worker = new Worker( + new URL('../../workers/audioToMidiWorker.ts', import.meta.url), + { type: 'module' }, + ) as AudioToMidiWorkerHandle; + + worker.onmessage = (event: MessageEvent) => { + if (event.data.type !== 'result') { + return; + } + + resolve(convertDetectedAudioNotesToRawMidiNotes( + project, + analysisSpan, + event.data.notes, + { + quantizeNoteStart: dialogResult.quantizeNoteStart as PianoRollQuantizePositionValue, + quantizeNoteLength: dialogResult.quantizeNoteLength as PianoRollQuantizeLengthValue, + }, + )); + }; + worker.onerror = () => reject(new Error('Audio-to-MIDI worker failed.')); + worker.postMessage({ + pcm, + sampleRate, + startSeconds: analysisSpan.startSeconds, + endSeconds: analysisSpan.endSeconds, + floorDb, + pitchRangeStart: dialogResult.pitchRangeStart, + pitchRangeEnd: dialogResult.pitchRangeEnd, + groupAdjacentPitchesToHighest: dialogResult.groupAdjacentPitchesToHighest, + }, [pcm.buffer]); + }); + + const regionLengthBeats = analysisSpan.regionEndBeat - analysisSpan.regionStartBeat; + const importCommand = new ImportMidiClipCommand( + targetTrack.getId().toString(), + targetTrack.getTrackIndex(), + analysisSpan.regionStartBeat, + regionLengthBeats, + detectedNotes, + `Converted from ${sourceAudioTrack?.getName() ?? audioRegion.getName()}`, + ); + KGCore.instance().executeCommand(importCommand); + refreshProjectState(); + } catch (error) { + console.error('Error converting audio to MIDI:', error); + await showAlert('Failed to convert this audio region to MIDI.'); + } finally { + const cleanupWorker = worker as AudioToMidiWorkerHandle | null; + cleanupWorker?.terminate(); + setIsConvertingToMidi(false); + } + }, [ + audioRegion, + availableMidiTracks, + getMonoPcmForAudioRegion, + isLooping, + loopingRange, + projectName, + refreshProjectState, + sourceAudioTrack, + spectrogramThresholdDb, + t, + trackId, + ]); + const handleDetectChords = useCallback(async () => { if (!audioRegion && !activeRegion) { await showAlert('Open a MIDI or audio region before detecting chords.'); @@ -1619,6 +1833,8 @@ const PianoRoll: React.FC = ({ detectingChords={isDetectingChords} onDetectTempo={audioRegion ? handleDetectTempo : undefined} detectingTempo={isDetectingTempo} + onConvertToMidi={audioRegion ? handleConvertToMidi : undefined} + convertToMidiDisabled={availableMidiTracks.length === 0} selectedRegionColor={selectedRegionColor} onRegionColorSelect={activeEditableRegionId ? (color) => { void handleRegionColorSelect(color); } : undefined} /> @@ -1669,6 +1885,7 @@ const PianoRoll: React.FC = ({ >
+
); }; diff --git a/src/components/piano-roll/PianoRollToolbar.test.tsx b/src/components/piano-roll/PianoRollToolbar.test.tsx index 2623a81..4f1fbd9 100644 --- a/src/components/piano-roll/PianoRollToolbar.test.tsx +++ b/src/components/piano-roll/PianoRollToolbar.test.tsx @@ -325,6 +325,46 @@ describe('PianoRollToolbar', () => { expect(screen.queryByText('Detect tempo...')).not.toBeInTheDocument(); }); + it('shows the convert to MIDI action as the last audio option and triggers it', () => { + const onConvertToMidi = vi.fn(); + + renderWithLocale( + + ); + + fireEvent.click(screen.getByTitle('More options')); + const options = screen.getAllByText(/Detect chords...|Convert to MIDI.../); + expect(options[options.length - 1]).toHaveTextContent('Convert to MIDI...'); + fireEvent.click(screen.getByText('Convert to MIDI...')); + + expect(onConvertToMidi).toHaveBeenCalledTimes(1); + }); + + it('disables the convert to MIDI action when no MIDI tracks are available', () => { + const onConvertToMidi = vi.fn(); + + renderWithLocale( + + ); + + fireEvent.click(screen.getByTitle('More options')); + const convertItem = screen.getByText('Convert to MIDI...'); + expect(convertItem).toHaveAttribute('aria-disabled', 'true'); + fireEvent.click(convertItem); + expect(onConvertToMidi).not.toHaveBeenCalled(); + }); + it('shows only the sheet controls when sheet mode is enabled', () => { renderWithLocale( void | Promise; detectingTempo?: boolean; + onConvertToMidi?: () => void | Promise; + convertToMidiDisabled?: boolean; selectedRegionColor?: string; onRegionColorSelect?: (color: string | null) => void; } @@ -94,6 +96,8 @@ const PianoRollToolbar: React.FC = ({ detectingChords = false, onDetectTempo, detectingTempo = false, + onConvertToMidi, + convertToMidiDisabled = false, selectedRegionColor, onRegionColorSelect, }) => { @@ -102,7 +106,7 @@ const PianoRollToolbar: React.FC = ({ const showAudioOnlyControls = mode === 'audio-waveform' && !sheetMusicViewEnabled; const showSpectrogramOnlyControls = mode === 'spectrogram' && !sheetMusicViewEnabled; const showSpecControls = !sheetMusicViewEnabled && (mode === 'spectrogram' || mode === 'hybrid'); - const showSpecMenu = !sheetMusicViewEnabled && (!!onDetectChords || !!onDetectTempo); + const showSpecMenu = !sheetMusicViewEnabled && (!!onDetectChords || !!onDetectTempo || !!onConvertToMidi); const automationOptions = React.useMemo(() => getTranslatedAutomationOptions(t), [t]); const snapOptions = React.useMemo( () => KGPianoRollState.SNAP_OPTIONS.map(option => ({ label: t(option.labelKey), value: option.value })), @@ -447,6 +451,22 @@ const PianoRollToolbar: React.FC = ({ {detectingTempo ? t('pianoRoll.detectingTempo') : t('pianoRoll.detectTempo')}
)} + {onConvertToMidi && ( +
{ + if (convertToMidiDisabled) { + return; + } + setShowRegionColorPalette(false); + setShowMoreMenu(false); + void onConvertToMidi(); + }} + aria-disabled={convertToMidiDisabled} + > + {t('pianoRoll.convertToMidi')} +
+ )} )} diff --git a/src/i18n/messages/en_us.ts b/src/i18n/messages/en_us.ts index ba27541..342c5bd 100644 --- a/src/i18n/messages/en_us.ts +++ b/src/i18n/messages/en_us.ts @@ -193,6 +193,7 @@ export const enUsMessages: TranslationMessages = { 'dialog.title.tempoDetection': 'Tempo Detection', 'dialog.title.applyTempo': 'Apply Tempo', 'dialog.title.chordDetection': 'Chord Detection', + 'dialog.title.audioToMidi': 'Convert to MIDI', 'dialog.title.input': 'Input', 'dialog.title.confirm': 'Confirm', 'dialog.close': 'Close dialog', @@ -203,6 +204,7 @@ export const enUsMessages: TranslationMessages = { 'dialog.chordHint.audio': 'Chord analysis is still experimental. Harmonic content, arrangement density, and transient-heavy material can affect accuracy. For more reliable results, start with the default settings, then refine sensitivity and stability until the detected harmony best matches the musical phrasing.', 'dialog.chordHint.midi': 'Chord analysis is still experimental. Voicing density, overlaps, and ornamental notes can influence the result. For more reliable chord labels, begin with the default settings, then adjust note suppression and harmonic focus to match the musical role of the passage.', 'dialog.chordHint.tempo': 'Tempo analysis is still experimental. Rubato phrasing, sparse transients, and layered percussion can reduce accuracy. Start with the default BPM range, then narrow the analysis window to the most plausible tempo span for the material if the first pass is not musically convincing.', + 'dialog.audioToMidiHint': 'Audio-to-MIDI conversion is still experimental. For best results, switch to spectrogram view, adjust Floor until only the true note ridges remain visible and most ghost pitches disappear, then enable Use current Floor dB before running the conversion.', 'dialog.sourceHint.kgone': 'For the most dependable chord labels, analyze a stem with vocals and percussion reduced or removed. If K.G.One Music Studio server integration is available, run Separator with the "Vocal, Drums, Bass, Guitar, Piano, and Others" model and use the Piano or Others stem for analysis.', 'dialog.sourceHint.local': 'For the most dependable chord labels, analyze a stem with vocals and percussion reduced or removed. If you are using the local separator, choose the "Vocal, Drums, Bass, and Others" model and use the Others stem for analysis.', 'dialog.label.sensitivity': 'Sensitivity', @@ -219,7 +221,18 @@ export const enUsMessages: TranslationMessages = { 'dialog.label.minimumBpm': 'Minimum BPM', 'dialog.label.maximumBpm': 'Maximum BPM', 'dialog.label.autoAlignRegionToBeat': 'Auto-align region to beat', + 'dialog.label.monophonicOnly': 'Monophonic only (v1)', + 'dialog.label.useCurrentFloorDb': 'Use current Floor dB', + 'dialog.label.manualFloorDb': 'Manual Floor dB', + 'dialog.label.pitchRange': 'Pitch range', + 'dialog.label.quantizeNoteStart': 'Quantize note start', + 'dialog.label.quantizeNoteLength': 'Quantize note length', + 'dialog.label.convertLoopRangeOnly': 'Convert loop-range audio clip only', + 'dialog.label.groupAdjacentPitchesToHighest': 'Group adjacent pitches to the strongest bin', + 'dialog.label.targetTrack': 'Target MIDI track', + 'dialog.to': 'to', 'dialog.message.chordDetection': 'Tune audio chord detection settings before processing.', + 'dialog.message.audioToMidi': 'Tune audio-to-MIDI conversion settings before processing.', 'dialog.message.tempoDetection': 'Tune audio tempo detection settings before processing.', 'dialog.message.tempoApply': 'Detected tempo: {bpm} BPM. Choose how to apply it.\n\nUpdate Current Tempo changes the active tempo at this clip location. Insert Tempo Change adds a new tempo region at the nearest bar before the clip starts.', 'dialog.action.updateCurrentTempo': 'Update Current Tempo', @@ -354,6 +367,7 @@ export const enUsMessages: TranslationMessages = { 'pianoRoll.detectingChords': 'Detecting chords...', 'pianoRoll.detectTempo': 'Detect tempo...', 'pianoRoll.detectingTempo': 'Detecting tempo...', + 'pianoRoll.convertToMidi': 'Convert to MIDI...', 'percussion.short.35': 'Ac.Bass', 'percussion.short.36': 'BassDrum', 'percussion.short.37': 'SideStick', diff --git a/src/i18n/messages/fr_fr.ts b/src/i18n/messages/fr_fr.ts index 4bd5ec3..f609080 100644 --- a/src/i18n/messages/fr_fr.ts +++ b/src/i18n/messages/fr_fr.ts @@ -190,6 +190,7 @@ export const frFrMessages: TranslationMessages = { 'dialog.title.tempoDetection': 'Détection du tempo', 'dialog.title.applyTempo': 'Appliquer le tempo', 'dialog.title.chordDetection': 'Détection d\'accords', + 'dialog.title.audioToMidi': 'Convertir en MIDI', 'dialog.title.input': 'Saisie', 'dialog.title.confirm': 'Confirmation', 'dialog.close': 'Fermer la boîte de dialogue', @@ -200,6 +201,7 @@ export const frFrMessages: TranslationMessages = { 'dialog.chordHint.audio': 'L\'analyse d\'accords reste expérimentale. Le contenu harmonique, la densité de l\'arrangement et les matériaux riches en transitoires peuvent affecter la précision. Pour de meilleurs résultats, commencez avec les réglages par défaut puis affinez la sensibilité et la stabilité jusqu\'à ce que l\'harmonie détectée corresponde à la phrase musicale.', 'dialog.chordHint.midi': 'L\'analyse d\'accords reste expérimentale. La densité des voicings, les chevauchements et les notes d\'ornement peuvent influencer le résultat. Pour des étiquettes d\'accord plus fiables, partez des réglages par défaut puis ajustez la suppression des notes courtes et le focus harmonique selon le rôle musical du passage.', 'dialog.chordHint.tempo': 'L\'analyse du tempo reste expérimentale. Le rubato, les transitoires clairsemées et les couches de percussions peuvent réduire la précision. Commencez avec la plage de BPM par défaut, puis resserrez la fenêtre d\'analyse vers la zone de tempo la plus plausible si le premier résultat n\'est pas convaincant musicalement.', + 'dialog.audioToMidiHint': 'La conversion audio-vers-MIDI reste expérimentale. Pour de meilleurs résultats, passez en vue spectrogramme, ajustez Floor jusqu\'à ne laisser visibles que les vraies notes et à faire disparaître autant que possible les hauteurs fantômes, puis activez Utiliser le Floor dB actuel avant la conversion.', 'dialog.sourceHint.kgone': 'Pour des étiquettes d\'accord plus fiables, analysez de préférence un stem où les voix et les percussions ont été atténuées ou retirées. Si l\'intégration du serveur K.G.One Music Studio est disponible, lancez Separator avec le modèle "Vocal, Drums, Bass, Guitar, Piano, and Others" et utilisez de préférence le stem Piano ou Others.', 'dialog.sourceHint.local': 'Pour des étiquettes d\'accord plus fiables, analysez de préférence un stem où les voix et les percussions ont été atténuées ou retirées. Si vous utilisez le séparateur local, choisissez le modèle "Vocal, Drums, Bass, and Others" et analysez le stem Others.', 'dialog.label.sensitivity': 'Sensibilité', @@ -216,7 +218,18 @@ export const frFrMessages: TranslationMessages = { 'dialog.label.minimumBpm': 'BPM minimum', 'dialog.label.maximumBpm': 'BPM maximum', 'dialog.label.autoAlignRegionToBeat': 'Aligner automatiquement la région sur le temps', + 'dialog.label.monophonicOnly': 'Monophonique uniquement (v1)', + 'dialog.label.useCurrentFloorDb': 'Utiliser le Floor dB actuel', + 'dialog.label.manualFloorDb': 'Floor dB manuel', + 'dialog.label.pitchRange': 'Plage de hauteurs', + 'dialog.label.quantizeNoteStart': 'Quantifier le début des notes', + 'dialog.label.quantizeNoteLength': 'Quantifier la durée des notes', + 'dialog.label.convertLoopRangeOnly': 'Convertir uniquement l\'audio dans la plage de boucle', + 'dialog.label.groupAdjacentPitchesToHighest': 'Regrouper les hauteurs adjacentes vers le bin le plus fort', + 'dialog.label.targetTrack': 'Piste MIDI cible', + 'dialog.to': 'à', 'dialog.message.chordDetection': 'Ajustez les réglages de détection d\'accords audio avant le traitement.', + 'dialog.message.audioToMidi': 'Ajustez les réglages de conversion audio-vers-MIDI avant le traitement.', 'dialog.message.tempoDetection': 'Ajustez les réglages de détection du tempo audio avant le traitement.', 'dialog.message.tempoApply': 'Tempo détecté : {bpm} BPM. Choisissez comment l\'appliquer.\n\nMettre à jour le tempo courant modifie le tempo actif à l\'emplacement de ce clip. Insérer un changement de tempo ajoute une nouvelle région de tempo à la mesure la plus proche avant le début du clip.', 'dialog.action.updateCurrentTempo': 'Mettre à jour le tempo courant', @@ -332,6 +345,7 @@ export const frFrMessages: TranslationMessages = { 'pianoRoll.detectingChords': 'Détection des accords...', 'pianoRoll.detectTempo': 'Détecter le tempo...', 'pianoRoll.detectingTempo': 'Détection du tempo...', + 'pianoRoll.convertToMidi': 'Convertir en MIDI...', 'toolbar.export.kgstudio': 'Exporter au format KGStudio', 'toolbar.export.midi': 'Exporter au format MIDI', 'toolbar.export.wav': 'Exporter en WAV', diff --git a/src/i18n/messages/zh_cn.ts b/src/i18n/messages/zh_cn.ts index e02dbb6..aaec418 100644 --- a/src/i18n/messages/zh_cn.ts +++ b/src/i18n/messages/zh_cn.ts @@ -191,6 +191,7 @@ export const zhCnMessages: TranslationMessages = { 'dialog.title.tempoDetection': '速度检测', 'dialog.title.applyTempo': '应用速度', 'dialog.title.chordDetection': '和弦检测', + 'dialog.title.audioToMidi': '转换为 MIDI', 'dialog.title.input': '输入', 'dialog.title.confirm': '确认', 'dialog.close': '关闭对话框', @@ -201,6 +202,7 @@ export const zhCnMessages: TranslationMessages = { 'dialog.chordHint.audio': '和弦分析仍属实验性功能。和声内容、编配密度以及瞬态较强的素材都可能影响准确率。建议先使用默认设置,再逐步微调灵敏度和稳定性,让检测结果更贴近实际乐句。', 'dialog.chordHint.midi': '和弦分析仍属实验性功能。和弦堆叠密度、重叠以及装饰音都会影响结果。建议先使用默认设置,再调整短音抑制和和声关注方式,以匹配该段音乐的和声作用。', 'dialog.chordHint.tempo': '速度分析仍属实验性功能。自由速度、瞬态稀疏以及多层打击乐都可能降低准确率。建议先使用默认 BPM 范围,如果第一次结果不理想,再把分析范围收窄到更可能的速度区间。', + 'dialog.audioToMidiHint': 'Audio-to-MIDI 转换仍属实验性功能。建议先切换到频谱视图,调整 Floor,直到只剩下真正的音符轮廓而大部分鬼影音高消失,再启用使用当前 Floor dB 后执行转换。', 'dialog.sourceHint.kgone': '为了获得更可靠的和弦标签,建议分析已经削弱或移除了人声和打击乐的 stem。如果已启用 K.G.One Music Studio 服务器集成,请使用带有 “Vocal, Drums, Bass, Guitar, Piano, and Others” 的 Separator 模型,并优先分析 Piano 或 Others stem。', 'dialog.sourceHint.local': '为了获得更可靠的和弦标签,建议分析已经削弱或移除了人声和打击乐的 stem。如果您使用本地分轨器,请选择 “Vocal, Drums, Bass, and Others” 模型,并使用 Others stem 进行分析。', 'dialog.label.sensitivity': '灵敏度', @@ -217,7 +219,18 @@ export const zhCnMessages: TranslationMessages = { 'dialog.label.minimumBpm': '最小 BPM', 'dialog.label.maximumBpm': '最大 BPM', 'dialog.label.autoAlignRegionToBeat': '自动将片段对齐到拍点', + 'dialog.label.monophonicOnly': '仅限单音 (v1)', + 'dialog.label.useCurrentFloorDb': '使用当前 Floor dB', + 'dialog.label.manualFloorDb': '手动 Floor dB', + 'dialog.label.pitchRange': '音高范围', + 'dialog.label.quantizeNoteStart': '量化音符起点', + 'dialog.label.quantizeNoteLength': '量化音符长度', + 'dialog.label.convertLoopRangeOnly': '仅转换 loop 范围内的音频片段', + 'dialog.label.groupAdjacentPitchesToHighest': '将相邻音高归并到最强的音高格', + 'dialog.label.targetTrack': '目标 MIDI 轨', + 'dialog.to': '到', 'dialog.message.chordDetection': '在处理前调整音频和弦检测设置。', + 'dialog.message.audioToMidi': '在处理前调整 Audio-to-MIDI 转换设置。', 'dialog.message.tempoDetection': '在处理前调整音频速度检测设置。', 'dialog.message.tempoApply': '检测到速度:{bpm} BPM。请选择应用方式。\n\n"更新当前速度"将修改此片段位置的有效速度。"插入速度变更"将在片段起始点前最近的小节处添加新的速度区域。', 'dialog.action.updateCurrentTempo': '更新当前速度', @@ -352,6 +365,7 @@ export const zhCnMessages: TranslationMessages = { 'pianoRoll.detectingChords': '正在检测和弦...', 'pianoRoll.detectTempo': '检测速度...', 'pianoRoll.detectingTempo': '正在检测速度...', + 'pianoRoll.convertToMidi': '转换为 MIDI...', 'percussion.short.35': '原底鼓', 'percussion.short.36': '底鼓', 'percussion.short.37': '边击', diff --git a/src/i18n/messages/zh_hk.ts b/src/i18n/messages/zh_hk.ts index b61c6d1..a02672d 100644 --- a/src/i18n/messages/zh_hk.ts +++ b/src/i18n/messages/zh_hk.ts @@ -191,6 +191,7 @@ export const zhHkMessages: TranslationMessages = { 'dialog.title.tempoDetection': '速度檢測', 'dialog.title.applyTempo': '應用速度', 'dialog.title.chordDetection': '和弦檢測', + 'dialog.title.audioToMidi': '轉換為 MIDI', 'dialog.title.input': '輸入', 'dialog.title.confirm': '確認', 'dialog.close': '關閉對話框', @@ -201,6 +202,7 @@ export const zhHkMessages: TranslationMessages = { 'dialog.chordHint.audio': '和弦分析仍屬實驗性功能。和聲內容、編配密度以及瞬態較強的素材都可能影響準確率。建議先使用預設設定,再逐步微調靈敏度和穩定性,讓檢測結果更貼近實際樂句。', 'dialog.chordHint.midi': '和弦分析仍屬實驗性功能。和弦堆疊密度、重疊以及裝飾音都會影響結果。建議先使用預設設定,再調整短音抑制和和聲關注方式,以配合該段音樂的和聲作用。', 'dialog.chordHint.tempo': '速度分析仍屬實驗性功能。自由速度、瞬態稀疏以及多層打擊樂都可能降低準確率。建議先使用預設 BPM 範圍,如果第一次結果不理想,再把分析範圍收窄到更可能的速度區間。', + 'dialog.audioToMidiHint': 'Audio-to-MIDI 轉換仍屬實驗性功能。建議先切換到頻譜視圖,調整 Floor,直到只剩下真正的音符輪廓而大部分鬼影音高消失,再啟用使用目前 Floor dB 後執行轉換。', 'dialog.sourceHint.kgone': '為了獲得更可靠的和弦標籤,建議分析已經削弱或移除了人聲和打擊樂的 stem。如果已啟用 K.G.One Music Studio 伺服器整合,請使用帶有 "Vocal, Drums, Bass, Guitar, Piano, and Others" 的 Separator 模型,並優先分析 Piano 或 Others stem。', 'dialog.sourceHint.local': '為了獲得更可靠的和弦標籤,建議分析已經削弱或移除了人聲和打擊樂的 stem。如果您使用本地分軌器,請選擇 "Vocal, Drums, Bass, and Others" 模型,並使用 Others stem 進行分析。', 'dialog.label.sensitivity': '靈敏度', @@ -217,7 +219,18 @@ export const zhHkMessages: TranslationMessages = { 'dialog.label.minimumBpm': '最小 BPM', 'dialog.label.maximumBpm': '最大 BPM', 'dialog.label.autoAlignRegionToBeat': '自動將片段對齊到拍點', + 'dialog.label.monophonicOnly': '僅限單音 (v1)', + 'dialog.label.useCurrentFloorDb': '使用目前 Floor dB', + 'dialog.label.manualFloorDb': '手動 Floor dB', + 'dialog.label.pitchRange': '音高範圍', + 'dialog.label.quantizeNoteStart': '量化音符起點', + 'dialog.label.quantizeNoteLength': '量化音符長度', + 'dialog.label.convertLoopRangeOnly': '僅轉換 loop 範圍內的音訊片段', + 'dialog.label.groupAdjacentPitchesToHighest': '將相鄰音高群組到最強的音高格', + 'dialog.label.targetTrack': '目標 MIDI 軌', + 'dialog.to': '至', 'dialog.message.chordDetection': '在處理前調整音訊和弦檢測設定。', + 'dialog.message.audioToMidi': '在處理前調整 Audio-to-MIDI 轉換設定。', 'dialog.message.tempoDetection': '在處理前調整音訊速度檢測設定。', 'dialog.message.tempoApply': '檢測到速度:{bpm} BPM。請選擇應用方式。\n\n"更新當前速度"會修改此片段位置的有效速度。"插入速度變更"會在片段起始點前最近的小節加入新的速度區域。', 'dialog.action.updateCurrentTempo': '更新當前速度', @@ -352,6 +365,7 @@ export const zhHkMessages: TranslationMessages = { 'pianoRoll.detectingChords': '正在檢測和弦...', 'pianoRoll.detectTempo': '檢測速度...', 'pianoRoll.detectingTempo': '正在檢測速度...', + 'pianoRoll.convertToMidi': '轉換為 MIDI...', 'percussion.short.35': '原底鼓', 'percussion.short.36': '底鼓', 'percussion.short.37': '邊擊', diff --git a/src/test/integration/audio-to-midi.integration.test.ts b/src/test/integration/audio-to-midi.integration.test.ts new file mode 100644 index 0000000..4da0c1a --- /dev/null +++ b/src/test/integration/audio-to-midi.integration.test.ts @@ -0,0 +1,67 @@ +import { execFileSync, spawnSync } from 'node:child_process'; +import path from 'node:path'; +import { describe, expect, it } from 'vitest'; +import { pitchToNoteNameString } from '../../util/midiUtil'; +import { detectMonophonicNotesFromAudio } from '../../util/audioToMidi'; + +const FIXTURE_PATH = path.resolve(process.cwd(), 'public/test-data/audio-to-midi-test01.mp3'); +const ffmpegAvailable = spawnSync('ffmpeg', ['-version'], { stdio: 'ignore' }).status === 0; +const runIfFfmpeg = ffmpegAvailable ? it : it.skip; + +function decodeMp3ToMonoPcm(pathname: string): { sampleRate: number; pcm: Float32Array } { + const wav = execFileSync( + 'ffmpeg', + ['-v', 'error', '-i', pathname, '-ac', '1', '-f', 'wav', 'pipe:1'], + { maxBuffer: 64 * 1024 * 1024 }, + ); + + const readString = (offset: number, length: number) => wav.toString('ascii', offset, offset + length); + if (readString(0, 4) !== 'RIFF' || readString(8, 4) !== 'WAVE') { + throw new Error('ffmpeg did not return a RIFF/WAVE stream'); + } + + let offset = 12; + let sampleRate = 44100; + let pcmData = Buffer.alloc(0); + + while (offset + 8 <= wav.length) { + const chunkId = readString(offset, 4); + const chunkSize = wav.readUInt32LE(offset + 4); + const chunkStart = offset + 8; + + if (chunkId === 'fmt ') { + sampleRate = wav.readUInt32LE(chunkStart + 4); + } else if (chunkId === 'data') { + pcmData = wav.subarray(chunkStart, chunkStart + chunkSize); + break; + } + + offset = chunkStart + chunkSize + (chunkSize % 2); + } + + const pcm = new Float32Array(pcmData.length / 2); + for (let sampleIndex = 0; sampleIndex < pcm.length; sampleIndex++) { + pcm[sampleIndex] = pcmData.readInt16LE(sampleIndex * 2) / 32768; + } + + return { sampleRate, pcm }; +} + +describe('audio-to-MIDI fixture regression', () => { + runIfFfmpeg('extracts exactly A1 then D2 from the dedicated mp3 fixture at -16 dB', () => { + const { sampleRate, pcm } = decodeMp3ToMonoPcm(FIXTURE_PATH); + const notes = detectMonophonicNotesFromAudio({ + pcm, + sampleRate, + startSeconds: 0, + endSeconds: pcm.length / sampleRate, + floorDb: -16, + pitchRangeStart: 12, + pitchRangeEnd: 107, + groupAdjacentPitchesToHighest: true, + }); + + expect(notes).toHaveLength(2); + expect(notes.map(note => pitchToNoteNameString(note.pitch))).toEqual(['A1', 'D2']); + }); +}); diff --git a/src/util/audioToMidi.test.ts b/src/util/audioToMidi.test.ts new file mode 100644 index 0000000..3610f6d --- /dev/null +++ b/src/util/audioToMidi.test.ts @@ -0,0 +1,118 @@ +import { describe, expect, it } from 'vitest'; +import { KGProject } from '../core/KGProject'; +import { KGAudioRegion } from '../core/region/KGAudioRegion'; +import { + buildAudioToMidiAnalysisSpan, + convertDetectedAudioNotesToRawMidiNotes, + detectMonophonicNotesFromAudio, +} from './audioToMidi'; + +function createSineWavePcm( + frequency: number, + durationSeconds: number, + sampleRate: number, + amplitude: number = 0.8, +): Float32Array { + const sampleCount = Math.floor(durationSeconds * sampleRate); + const pcm = new Float32Array(sampleCount); + for (let i = 0; i < sampleCount; i++) { + pcm[i] = amplitude * Math.sin((2 * Math.PI * frequency * i) / sampleRate); + } + return pcm; +} + +describe('audioToMidi analysis span helpers', () => { + it('uses the exact source audio region bounds when loop mode is off', () => { + const project = new KGProject('Test', 32, 0, 120); + const region = new KGAudioRegion('audio-1', '1', 0, 'Audio', 8, 4, 'file-1', 'test.wav', 10, 1.5); + + const span = buildAudioToMidiAnalysisSpan(project, region, { + loopModeEnabled: false, + convertLoopRangeOnly: true, + loopingRange: [0, 0], + }); + + expect(span).toEqual({ + regionStartBeat: 8, + regionEndBeat: 12, + startSeconds: 1.5, + endSeconds: 3.5, + }); + }); + + it('uses the exact loop overlap when loop-only conversion is enabled', () => { + const project = new KGProject('Test', 32, 0, 120); + const region = new KGAudioRegion('audio-1', '1', 0, 'Audio', 8, 8, 'file-1', 'test.wav', 10, 0.5); + + const span = buildAudioToMidiAnalysisSpan(project, region, { + loopModeEnabled: true, + convertLoopRangeOnly: true, + loopingRange: [3, 4], + }); + + expect(span).toEqual({ + regionStartBeat: 12, + regionEndBeat: 16, + startSeconds: 2.5, + endSeconds: 4.5, + }); + }); +}); + +describe('audioToMidi note conversion helpers', () => { + it('drops raw notes shorter than the selected quantized note length and quantizes survivors', () => { + const project = new KGProject('Test', 32, 0, 120); + const rawNotes = convertDetectedAudioNotesToRawMidiNotes( + project, + { + regionStartBeat: 8, + regionEndBeat: 12, + startSeconds: 0, + endSeconds: 2, + }, + [ + { startOffsetSeconds: 0.0, endOffsetSeconds: 0.24, pitch: 33, heat: 0.9 }, + { startOffsetSeconds: 0.5, endOffsetSeconds: 1.45, pitch: 38, heat: 0.7 }, + ], + { + quantizeNoteStart: '1/16', + quantizeNoteLength: '1/4', + }, + ); + + expect(rawNotes).toEqual([ + { + startBeat: 1, + endBeat: 3, + pitch: 38, + velocity: 102, + }, + ]); + }); +}); + +describe('audioToMidi synthetic detection', () => { + it('detects sequential monophonic notes from a simple sine-wave fixture', () => { + const sampleRate = 44100; + const a1 = createSineWavePcm(55, 0.7, sampleRate); + const silence = new Float32Array(Math.floor(0.08 * sampleRate)); + const d2 = createSineWavePcm(73.41619197935188, 0.7, sampleRate); + const pcm = new Float32Array(a1.length + silence.length + d2.length); + pcm.set(a1, 0); + pcm.set(silence, a1.length); + pcm.set(d2, a1.length + silence.length); + + const notes = detectMonophonicNotesFromAudio({ + pcm, + sampleRate, + startSeconds: 0, + endSeconds: pcm.length / sampleRate, + floorDb: -30, + pitchRangeStart: 21, + pitchRangeEnd: 38, + groupAdjacentPitchesToHighest: true, + }); + + expect(notes.map(note => note.pitch)).toEqual([33, 38]); + }); +}); diff --git a/src/util/audioToMidi.ts b/src/util/audioToMidi.ts new file mode 100644 index 0000000..f048309 --- /dev/null +++ b/src/util/audioToMidi.ts @@ -0,0 +1,161 @@ +import { KGProject } from '../core/KGProject'; +import { KGAudioRegion } from '../core/region/KGAudioRegion'; +import type { + PianoRollQuantizeLengthValue, + PianoRollQuantizePositionValue, +} from '../core/state/KGPianoRollState'; +import type { RawMidiNote } from './midiUtil'; +import { + beatRangeToSeconds, + beatToSeconds, + secondsToBeat, +} from './globalTrackUtil'; + +export { + detectMonophonicNotesFromAudio, + type AudioToMidiDetectedNote, + type AudioToMidiDetectionRequest, +} from './audioToMidiCore'; + +import type { AudioToMidiDetectedNote } from './audioToMidiCore'; + +export interface AudioToMidiAnalysisSpan { + regionStartBeat: number; + regionEndBeat: number; + startSeconds: number; + endSeconds: number; +} + +export interface AudioToMidiConversionOptions { + floorDb: number; + pitchRangeStart: number; + pitchRangeEnd: number; + quantizeNoteStart: PianoRollQuantizePositionValue; + quantizeNoteLength: PianoRollQuantizeLengthValue; + groupAdjacentPitchesToHighest: boolean; +} + +export function buildAudioToMidiAnalysisSpan( + project: KGProject, + audioRegion: KGAudioRegion, + options: { + loopModeEnabled: boolean; + convertLoopRangeOnly: boolean; + loopingRange: [number, number]; + }, +): AudioToMidiAnalysisSpan | null { + const regionStartBeat = audioRegion.getStartFromBeat(); + const regionEndBeat = regionStartBeat + audioRegion.getLength(); + if (regionEndBeat <= regionStartBeat) { + return null; + } + + if (options.loopModeEnabled && options.convertLoopRangeOnly) { + const beatsPerBar = project.getTimeSignature().numerator; + const loopStartBeat = options.loopingRange[0] * beatsPerBar; + const loopEndBeat = (options.loopingRange[1] + 1) * beatsPerBar; + const overlapStartBeat = Math.max(regionStartBeat, loopStartBeat); + const overlapEndBeat = Math.min(regionEndBeat, loopEndBeat); + if (overlapEndBeat <= overlapStartBeat) { + return null; + } + + return { + regionStartBeat: overlapStartBeat, + regionEndBeat: overlapEndBeat, + startSeconds: audioRegion.getClipStartOffsetSeconds() + beatRangeToSeconds(project, regionStartBeat, overlapStartBeat), + endSeconds: audioRegion.getClipStartOffsetSeconds() + beatRangeToSeconds(project, regionStartBeat, overlapEndBeat), + }; + } + + return { + regionStartBeat, + regionEndBeat, + startSeconds: audioRegion.getClipStartOffsetSeconds(), + endSeconds: audioRegion.getClipStartOffsetSeconds() + beatRangeToSeconds(project, regionStartBeat, regionEndBeat), + }; +} + +export function quantizationValueToBeats(value: string): number { + const denominator = Number.parseInt(value.split('/')[1] ?? '', 10); + if (!Number.isFinite(denominator) || denominator <= 0) { + throw new Error(`Invalid quantization value: ${value}`); + } + + return 4 / denominator; +} + +function clamp(value: number, min: number, max: number): number { + return Math.max(min, Math.min(max, value)); +} + +export function convertDetectedAudioNotesToRawMidiNotes( + project: KGProject, + span: AudioToMidiAnalysisSpan, + detectedNotes: AudioToMidiDetectedNote[], + options: Pick, +): RawMidiNote[] { + const regionStartAbsoluteSeconds = beatToSeconds(project, span.regionStartBeat); + const regionLengthBeats = Math.max(0, span.regionEndBeat - span.regionStartBeat); + const quantizedStartStep = quantizationValueToBeats(options.quantizeNoteStart); + const quantizedLengthStep = quantizationValueToBeats(options.quantizeNoteLength); + + const converted = detectedNotes + .map(note => { + const startBeatAbsolute = secondsToBeat(project, regionStartAbsoluteSeconds + note.startOffsetSeconds); + const endBeatAbsolute = secondsToBeat(project, regionStartAbsoluteSeconds + note.endOffsetSeconds); + const rawStartBeat = startBeatAbsolute - span.regionStartBeat; + const rawEndBeat = endBeatAbsolute - span.regionStartBeat; + const rawDurationBeats = rawEndBeat - rawStartBeat; + if (rawDurationBeats < quantizedLengthStep) { + return null; + } + + const quantizedStartBeat = Math.round(rawStartBeat / quantizedStartStep) * quantizedStartStep; + const quantizedDurationBeats = Math.max( + quantizedLengthStep, + Math.round(rawDurationBeats / quantizedLengthStep) * quantizedLengthStep, + ); + const quantizedEndBeat = quantizedStartBeat + quantizedDurationBeats; + if (quantizedStartBeat >= regionLengthBeats) { + return null; + } + + const clampedStartBeat = clamp(quantizedStartBeat, 0, regionLengthBeats); + const clampedEndBeat = clamp( + Math.max(clampedStartBeat + quantizedLengthStep, quantizedEndBeat), + clampedStartBeat + Math.min(quantizedLengthStep, Math.max(regionLengthBeats - clampedStartBeat, 0)), + regionLengthBeats, + ); + if (clampedEndBeat <= clampedStartBeat) { + return null; + } + + return { + startBeat: clampedStartBeat, + endBeat: clampedEndBeat, + pitch: note.pitch, + velocity: clamp(Math.round(45 + note.heat * 82), 1, 127), + } satisfies RawMidiNote; + }) + .filter((note): note is RawMidiNote => note !== null) + .sort((left, right) => left.startBeat - right.startBeat || left.pitch - right.pitch); + + const merged: RawMidiNote[] = []; + for (const note of converted) { + const previous = merged[merged.length - 1]; + if ( + previous && + previous.pitch === note.pitch && + note.startBeat <= previous.endBeat + 1e-6 + ) { + previous.endBeat = Math.max(previous.endBeat, note.endBeat); + previous.velocity = Math.max(previous.velocity, note.velocity); + continue; + } + + merged.push({ ...note }); + } + + return merged; +} diff --git a/src/util/audioToMidiCore.ts b/src/util/audioToMidiCore.ts new file mode 100644 index 0000000..124cc1d --- /dev/null +++ b/src/util/audioToMidiCore.ts @@ -0,0 +1,358 @@ +import FFT from 'fft.js'; +import { + SPECTROGRAM_FULL_MAX_MIDI_PITCH, + SPECTROGRAM_FULL_MIN_MIDI_PITCH, + getSpectrogramAnalysisResolution, + getSpectrogramPitchBinCount, +} from './spectrogramUtil'; + +const FFT_SIZE = 16384; +const HOP_SIZE = 512; +const MAX_HEIGHT_RESOLUTION = 5; +const MIN_ANALYSIS_FREQUENCY = 20; +const MAX_ANALYSIS_FREQUENCY = 5000; + +export interface AudioToMidiDetectionRequest { + pcm: Float32Array; + sampleRate: number; + startSeconds: number; + endSeconds: number; + floorDb: number; + pitchRangeStart: number; + pitchRangeEnd: number; + groupAdjacentPitchesToHighest: boolean; +} + +export interface AudioToMidiDetectedNote { + startOffsetSeconds: number; + endOffsetSeconds: number; + pitch: number; + heat: number; +} + +const HANN_WINDOW = (() => { + const window = new Float32Array(FFT_SIZE); + for (let i = 0; i < FFT_SIZE; i++) { + window[i] = 0.5 * (1 - Math.cos((2 * Math.PI * i) / (FFT_SIZE - 1))); + } + return window; +})(); + +function midiToFrequency(midiPitch: number): number { + return 440 * Math.pow(2, (midiPitch - 69) / 12); +} + +function frequencyToMidiPitch(frequency: number): number | null { + if (!Number.isFinite(frequency) || frequency <= 0) { + return null; + } + return 69 + 12 * Math.log2(frequency / 440); +} + +function mapMidiPitchToPosition(midiPitch: number, resolution: number): number | null { + const pitchOffset = midiPitch - SPECTROGRAM_FULL_MIN_MIDI_PITCH; + if (pitchOffset < 0 || pitchOffset > SPECTROGRAM_FULL_MAX_MIDI_PITCH) { + return null; + } + + const scaled = pitchOffset * resolution + (resolution - 1) / 2; + const maxBin = getSpectrogramPitchBinCount(resolution) - 1; + return Math.max(0, Math.min(maxBin, scaled)); +} + +function estimateParabolicPeakOffset(leftMagnitude: number, centerMagnitude: number, rightMagnitude: number): number { + const denominator = leftMagnitude - 2 * centerMagnitude + rightMagnitude; + if (!Number.isFinite(denominator) || Math.abs(denominator) < Number.EPSILON) { + return 0; + } + + const offset = 0.5 * (leftMagnitude - rightMagnitude) / denominator; + return Math.max(-0.5, Math.min(0.5, offset)); +} + +function estimatePeakFrequency(bin: number, hzPerBin: number, magnitudes: Float32Array): number { + const centerFrequency = bin * hzPerBin; + if (bin <= 1 || bin >= magnitudes.length - 1) { + return centerFrequency; + } + + const leftMagnitude = magnitudes[bin - 1]; + const centerMagnitude = magnitudes[bin]; + const rightMagnitude = magnitudes[bin + 1]; + + if (centerMagnitude <= leftMagnitude || centerMagnitude <= rightMagnitude) { + return centerFrequency; + } + + return (bin + estimateParabolicPeakOffset(leftMagnitude, centerMagnitude, rightMagnitude)) * hzPerBin; +} + +function getFrequencyBandEdges(centerFrequency: number, hzPerBin: number): { lowerFrequency: number; upperFrequency: number } { + const halfBandwidth = hzPerBin / 2; + return { + lowerFrequency: Math.max(Number.EPSILON, centerFrequency - halfBandwidth), + upperFrequency: centerFrequency + halfBandwidth, + }; +} + +function paintMagnitudeAcrossPitchSpan( + target: Float32Array, + pitchBins: number, + startPosition: number, + endPosition: number, + centerPosition: number, + magnitude: number, +): void { + const clampedStart = Math.max(0, Math.min(startPosition, pitchBins - 1)); + const clampedEnd = Math.max(0, Math.min(endPosition, pitchBins - 1)); + const startBin = Math.max(0, Math.floor(clampedStart)); + const endBin = Math.min(pitchBins - 1, Math.ceil(clampedEnd)); + const spanWidth = Math.max(clampedEnd - clampedStart, 1); + + for (let targetBin = startBin; targetBin <= endBin; targetBin++) { + const cellStart = targetBin - 0.5; + const cellEnd = targetBin + 0.5; + const overlap = Math.max(0, Math.min(clampedEnd, cellEnd) - Math.max(clampedStart, cellStart)); + if (overlap <= 0) { + continue; + } + + const overlapWeight = Math.min(1, overlap); + const centerDistance = Math.abs(targetBin - centerPosition); + const centerWeight = Math.max(0, 1 - centerDistance / (spanWidth + 1)); + const weight = Math.max(overlapWeight * (0.6 + 0.4 * centerWeight), overlapWeight * 0.35); + const weightedMagnitude = magnitude * weight; + + if (weightedMagnitude > target[targetBin]) { + target[targetBin] = weightedMagnitude; + } + } +} + +function collapseAdjacentPitchClusters(activeBins: number[], frameBins: Float32Array): number[] { + if (activeBins.length === 0) { + return []; + } + + const grouped: number[] = []; + let clusterStart = 0; + + while (clusterStart < activeBins.length) { + let clusterEnd = clusterStart; + let strongestBin = activeBins[clusterStart]; + let strongestValue = frameBins[strongestBin]; + + while ( + clusterEnd + 1 < activeBins.length && + activeBins[clusterEnd + 1] <= activeBins[clusterEnd] + 1 + ) { + clusterEnd += 1; + const candidateBin = activeBins[clusterEnd]; + const candidateValue = frameBins[candidateBin]; + if (candidateValue > strongestValue) { + strongestBin = candidateBin; + strongestValue = candidateValue; + } + } + + grouped.push(strongestBin); + clusterStart = clusterEnd + 1; + } + + return grouped; +} + +function normalizePitchBinToMidiPitch(bin: number, resolution: number): number { + return Math.round((bin - ((resolution - 1) / 2)) / resolution); +} + +export function detectMonophonicNotesFromAudio(request: AudioToMidiDetectionRequest): AudioToMidiDetectedNote[] { + const { + pcm, + sampleRate, + startSeconds, + endSeconds, + floorDb, + pitchRangeStart, + pitchRangeEnd, + groupAdjacentPitchesToHighest, + } = request; + + const clampedStartSeconds = Math.max(0, startSeconds); + const clampedEndSeconds = Math.max(clampedStartSeconds, endSeconds); + const startSample = Math.max(0, Math.floor(clampedStartSeconds * sampleRate)); + const endSample = Math.min(pcm.length, Math.ceil(clampedEndSeconds * sampleRate)); + const sampleCount = Math.max(0, endSample - startSample); + if (sampleCount <= 0) { + return []; + } + + const analysisResolution = getSpectrogramAnalysisResolution(MAX_HEIGHT_RESOLUTION); + const pitchBins = getSpectrogramPitchBinCount(analysisResolution); + const fft = new FFT(FFT_SIZE); + const complexOut = fft.createComplexArray() as number[]; + const inputPadded = new Float32Array(FFT_SIZE); + const hzPerBin = sampleRate / FFT_SIZE; + const totalHops = Math.max(1, Math.ceil((sampleCount - FFT_SIZE) / HOP_SIZE) + 1); + const frameBinValues: Float32Array[] = []; + let globalMax = 0; + + const minFrequency = Math.max(MIN_ANALYSIS_FREQUENCY, midiToFrequency(Math.max(0, pitchRangeStart - 2))); + const maxFrequency = Math.min(MAX_ANALYSIS_FREQUENCY, midiToFrequency(Math.min(127, pitchRangeEnd + 2))); + + for (let hop = 0; hop < totalHops; hop++) { + const frameBins = new Float32Array(pitchBins); + const offset = hop * HOP_SIZE; + inputPadded.fill(0); + const available = Math.min(FFT_SIZE, sampleCount - offset); + + for (let i = 0; i < available; i++) { + inputPadded[i] = pcm[startSample + offset + i] * HANN_WINDOW[i]; + } + + fft.realTransform(complexOut, inputPadded as unknown as number[]); + fft.completeSpectrum(complexOut); + + const numBins = FFT_SIZE / 2; + const magnitudes = new Float32Array(numBins); + for (let bin = 1; bin < numBins; bin++) { + const re = complexOut[2 * bin]; + const im = complexOut[2 * bin + 1]; + magnitudes[bin] = Math.sqrt(re * re + im * im); + } + + for (let bin = 1; bin < numBins; bin++) { + const centerFrequency = estimatePeakFrequency(bin, hzPerBin, magnitudes); + const { lowerFrequency, upperFrequency } = getFrequencyBandEdges(centerFrequency, hzPerBin); + if (upperFrequency < minFrequency || lowerFrequency > maxFrequency) { + continue; + } + + const magnitude = magnitudes[bin]; + if (magnitude <= 0) { + continue; + } + + const lowerMidi = frequencyToMidiPitch(Math.max(lowerFrequency, minFrequency)); + const upperMidi = frequencyToMidiPitch(Math.min(upperFrequency, maxFrequency)); + const centerMidi = frequencyToMidiPitch(centerFrequency); + if (lowerMidi === null || upperMidi === null || centerMidi === null) { + continue; + } + + const startPosition = mapMidiPitchToPosition(lowerMidi, analysisResolution); + const endPosition = mapMidiPitchToPosition(upperMidi, analysisResolution); + const centerPosition = mapMidiPitchToPosition(centerMidi, analysisResolution); + if (startPosition === null || endPosition === null || centerPosition === null) { + continue; + } + + paintMagnitudeAcrossPitchSpan( + frameBins, + pitchBins, + Math.min(startPosition, endPosition), + Math.max(startPosition, endPosition), + centerPosition, + magnitude, + ); + } + + for (let i = 0; i < frameBins.length; i++) { + if (frameBins[i] > globalMax) { + globalMax = frameBins[i]; + } + } + + frameBinValues.push(frameBins); + } + + if (globalMax <= 0) { + return []; + } + + const linearThreshold = Math.pow(10, floorDb / 20); + const hopDurationSeconds = HOP_SIZE / sampleRate; + const notes: AudioToMidiDetectedNote[] = []; + let current: AudioToMidiDetectedNote | null = null; + let currentHeatSum = 0; + let currentFrameCount = 0; + + for (let frameIndex = 0; frameIndex < frameBinValues.length; frameIndex++) { + const normalizedBins = frameBinValues[frameIndex].slice(); + const activeBins: number[] = []; + + for (let i = 0; i < normalizedBins.length; i++) { + normalizedBins[i] = normalizedBins[i] / globalMax; + if (normalizedBins[i] >= linearThreshold) { + activeBins.push(i); + } + } + + const candidateBins = groupAdjacentPitchesToHighest + ? collapseAdjacentPitchClusters(activeBins, normalizedBins) + : activeBins; + + let winningPitch: number | null = null; + let winningHeat = 0; + + for (const bin of candidateBins) { + const heat = normalizedBins[bin]; + if (heat <= winningHeat) { + continue; + } + + const midiPitch = normalizePitchBinToMidiPitch(bin, analysisResolution); + if (midiPitch < pitchRangeStart || midiPitch > pitchRangeEnd) { + continue; + } + + winningPitch = midiPitch; + winningHeat = heat; + } + + const frameStartSeconds = frameIndex * hopDurationSeconds; + const frameEndSeconds = Math.min((frameIndex + 1) * hopDurationSeconds, clampedEndSeconds - clampedStartSeconds); + + if (winningPitch === null) { + if (current) { + current.heat = currentHeatSum / Math.max(1, currentFrameCount); + notes.push(current); + current = null; + currentHeatSum = 0; + currentFrameCount = 0; + } + continue; + } + + if (current && current.pitch === winningPitch) { + current.endOffsetSeconds = frameEndSeconds; + currentHeatSum += winningHeat; + currentFrameCount += 1; + continue; + } + + if (current) { + current.heat = currentHeatSum / Math.max(1, currentFrameCount); + notes.push(current); + } + + // Future polyphonic support should preserve the strongest bin inside an + // adjacent cluster (for example C4 over weaker B3/D4 neighbors) before + // selecting additional simultaneous note candidates. + current = { + startOffsetSeconds: frameStartSeconds, + endOffsetSeconds: frameEndSeconds, + pitch: winningPitch, + heat: winningHeat, + }; + currentHeatSum = winningHeat; + currentFrameCount = 1; + } + + if (current) { + current.heat = currentHeatSum / Math.max(1, currentFrameCount); + notes.push(current); + } + + return notes; +} diff --git a/src/util/dialogUtil.ts b/src/util/dialogUtil.ts index 2d18ced..3a9ad54 100644 --- a/src/util/dialogUtil.ts +++ b/src/util/dialogUtil.ts @@ -37,6 +37,19 @@ export interface TempoApplyResult { autoAlignRegionToBeat: boolean; } +export interface AudioToMidiOptionsResult { + monophonic: boolean; + useCurrentFloorDb: boolean; + manualFloorDb: number; + pitchRangeStart: number; + pitchRangeEnd: number; + quantizeNoteStart: string; + quantizeNoteLength: string; + convertLoopRangeOnly: boolean; + groupAdjacentPitchesToHighest: boolean; + targetTrackId: string; +} + export interface ChoiceOption { label: string; value: string; @@ -51,6 +64,12 @@ let _showChordDetectionOptionsFn: ((message: string, defaultValue?: ChordDetecti let _showMidiChordDetectionOptionsFn: ((message: string, defaultValue?: MidiChordDetectionOptionsResult) => Promise) | null = null; let _showTempoDetectionOptionsFn: ((message: string, defaultValue?: TempoDetectionOptionsResult) => Promise) | null = null; let _showTempoApplyFn: ((message: string, choices: ChoiceOption[]) => Promise) | null = null; +let _showAudioToMidiOptionsFn: (( + message: string, + targetTracks: ChoiceOption[], + loopModeEnabled: boolean, + defaultValue?: AudioToMidiOptionsResult, +) => Promise) | null = null; export function registerDialogFns( alertFn: (message: string) => Promise, @@ -62,6 +81,12 @@ export function registerDialogFns( midiChordDetectionOptionsFn?: (message: string, defaultValue?: MidiChordDetectionOptionsResult) => Promise, tempoDetectionOptionsFn?: (message: string, defaultValue?: TempoDetectionOptionsResult) => Promise, tempoApplyFn?: (message: string, choices: ChoiceOption[]) => Promise, + audioToMidiOptionsFn?: ( + message: string, + targetTracks: ChoiceOption[], + loopModeEnabled: boolean, + defaultValue?: AudioToMidiOptionsResult, + ) => Promise, ) { _showAlertFn = alertFn; _showConfirmFn = confirmFn; @@ -72,6 +97,7 @@ export function registerDialogFns( if (midiChordDetectionOptionsFn) _showMidiChordDetectionOptionsFn = midiChordDetectionOptionsFn; if (tempoDetectionOptionsFn) _showTempoDetectionOptionsFn = tempoDetectionOptionsFn; if (tempoApplyFn) _showTempoApplyFn = tempoApplyFn; + if (audioToMidiOptionsFn) _showAudioToMidiOptionsFn = audioToMidiOptionsFn; } export function showAlert(message: string): Promise { @@ -166,3 +192,26 @@ export function showTempoApply(message: string, choices: ChoiceOption[]): Promis } return _showTempoApplyFn(message, choices); } + +export function showAudioToMidiOptions( + message: string, + targetTracks: ChoiceOption[], + loopModeEnabled: boolean, + defaultValue?: AudioToMidiOptionsResult, +): Promise { + if (!_showAudioToMidiOptionsFn) { + return Promise.resolve(defaultValue ?? { + monophonic: true, + useCurrentFloorDb: true, + manualFloorDb: -25, + pitchRangeStart: 12, + pitchRangeEnd: 107, + quantizeNoteStart: '1/16', + quantizeNoteLength: '1/16', + convertLoopRangeOnly: true, + groupAdjacentPitchesToHighest: true, + targetTrackId: targetTracks[0]?.value ?? '', + }); + } + return _showAudioToMidiOptionsFn(message, targetTracks, loopModeEnabled, defaultValue); +} diff --git a/src/workers/audioToMidiWorker.ts b/src/workers/audioToMidiWorker.ts new file mode 100644 index 0000000..7cc41f0 --- /dev/null +++ b/src/workers/audioToMidiWorker.ts @@ -0,0 +1,22 @@ +import { + detectMonophonicNotesFromAudio, + type AudioToMidiDetectedNote, + type AudioToMidiDetectionRequest, +} from '../util/audioToMidiCore'; + +export interface AudioToMidiWorkerResult { + type: 'result'; + notes: AudioToMidiDetectedNote[]; +} + +type WorkerScopeLike = typeof globalThis & { + onmessage: ((event: MessageEvent) => void) | null; + postMessage: (message: AudioToMidiWorkerResult) => void; +}; + +const workerScope = self as WorkerScopeLike; + +workerScope.onmessage = (event: MessageEvent) => { + const notes = detectMonophonicNotesFromAudio(event.data); + workerScope.postMessage({ type: 'result', notes }); +};