@drop-ai/core 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +655 -237
- package/dist/actions/ActionRegistry.d.ts.map +1 -1
- package/dist/actions/ActionRegistry.js +7 -2
- package/dist/actions/ActionRegistry.js.map +1 -1
- package/dist/actions/types.d.ts +7 -1
- package/dist/actions/types.d.ts.map +1 -1
- package/dist/analysis/AudioAnalyzer.d.ts +71 -0
- package/dist/analysis/AudioAnalyzer.d.ts.map +1 -0
- package/dist/analysis/AudioAnalyzer.js +569 -0
- package/dist/analysis/AudioAnalyzer.js.map +1 -0
- package/dist/audio/AudioEngine.d.ts +14 -1
- package/dist/audio/AudioEngine.d.ts.map +1 -1
- package/dist/audio/AudioEngine.js +115 -35
- package/dist/audio/AudioEngine.js.map +1 -1
- package/dist/audio/Auditioner.d.ts +104 -0
- package/dist/audio/Auditioner.d.ts.map +1 -0
- package/dist/audio/Auditioner.js +239 -0
- package/dist/audio/Auditioner.js.map +1 -0
- package/dist/audio/BWFMetadata.d.ts +116 -0
- package/dist/audio/BWFMetadata.d.ts.map +1 -0
- package/dist/audio/BWFMetadata.js +394 -0
- package/dist/audio/BWFMetadata.js.map +1 -0
- package/dist/audio/ChannelSplitter.d.ts +37 -0
- package/dist/audio/ChannelSplitter.d.ts.map +1 -0
- package/dist/audio/ChannelSplitter.js +82 -0
- package/dist/audio/ChannelSplitter.js.map +1 -0
- package/dist/audio/ExportAnalyzer.d.ts +39 -0
- package/dist/audio/ExportAnalyzer.d.ts.map +1 -0
- package/dist/audio/ExportAnalyzer.js +76 -0
- package/dist/audio/ExportAnalyzer.js.map +1 -0
- package/dist/audio/LufsNormalizer.d.ts +37 -0
- package/dist/audio/LufsNormalizer.d.ts.map +1 -0
- package/dist/audio/LufsNormalizer.js +217 -0
- package/dist/audio/LufsNormalizer.js.map +1 -0
- package/dist/audio/OfflineExporter.d.ts +1 -6
- package/dist/audio/OfflineExporter.d.ts.map +1 -1
- package/dist/audio/OfflineExporter.js +31 -10
- package/dist/audio/OfflineExporter.js.map +1 -1
- package/dist/audio/RoutingGraph.d.ts +134 -0
- package/dist/audio/RoutingGraph.d.ts.map +1 -0
- package/dist/audio/RoutingGraph.js +547 -0
- package/dist/audio/RoutingGraph.js.map +1 -0
- package/dist/audio/SampleRateConverter.d.ts +21 -0
- package/dist/audio/SampleRateConverter.d.ts.map +1 -0
- package/dist/audio/SampleRateConverter.js +84 -0
- package/dist/audio/SampleRateConverter.js.map +1 -0
- package/dist/audio/SilencePadding.d.ts +29 -0
- package/dist/audio/SilencePadding.d.ts.map +1 -0
- package/dist/audio/SilencePadding.js +86 -0
- package/dist/audio/SilencePadding.js.map +1 -0
- package/dist/audio/SourceCache.d.ts +13 -16
- package/dist/audio/SourceCache.d.ts.map +1 -1
- package/dist/audio/SourceCache.js +21 -33
- package/dist/audio/SourceCache.js.map +1 -1
- package/dist/audio/engine/Declick.d.ts +98 -0
- package/dist/audio/engine/Declick.d.ts.map +1 -0
- package/dist/audio/engine/Declick.js +204 -0
- package/dist/audio/engine/Declick.js.map +1 -0
- package/dist/audio/engine/DiskIO.d.ts +172 -0
- package/dist/audio/engine/DiskIO.d.ts.map +1 -0
- package/dist/audio/engine/DiskIO.js +384 -0
- package/dist/audio/engine/DiskIO.js.map +1 -0
- package/dist/audio/engine/LatencyCompensator.d.ts +46 -0
- package/dist/audio/engine/LatencyCompensator.d.ts.map +1 -0
- package/dist/audio/engine/LatencyCompensator.js +84 -0
- package/dist/audio/engine/LatencyCompensator.js.map +1 -0
- package/dist/audio/engine/MultiTrackRecorder.d.ts +146 -0
- package/dist/audio/engine/MultiTrackRecorder.d.ts.map +1 -0
- package/dist/audio/engine/MultiTrackRecorder.js +359 -0
- package/dist/audio/engine/MultiTrackRecorder.js.map +1 -0
- package/dist/audio/engine/PlaylistEngine.d.ts.map +1 -1
- package/dist/audio/engine/PlaylistEngine.js +9 -2
- package/dist/audio/engine/PlaylistEngine.js.map +1 -1
- package/dist/audio/engine/PunchRecordManager.d.ts +113 -0
- package/dist/audio/engine/PunchRecordManager.d.ts.map +1 -0
- package/dist/audio/engine/PunchRecordManager.js +191 -0
- package/dist/audio/engine/PunchRecordManager.js.map +1 -0
- package/dist/audio/engine/RoutingGraph.d.ts +135 -0
- package/dist/audio/engine/RoutingGraph.d.ts.map +1 -0
- package/dist/audio/engine/RoutingGraph.js +436 -0
- package/dist/audio/engine/RoutingGraph.js.map +1 -0
- package/dist/audio/engine/SidechainRouter.d.ts +139 -0
- package/dist/audio/engine/SidechainRouter.d.ts.map +1 -0
- package/dist/audio/engine/SidechainRouter.js +292 -0
- package/dist/audio/engine/SidechainRouter.js.map +1 -0
- package/dist/audio/engine/XrunTracker.d.ts +71 -0
- package/dist/audio/engine/XrunTracker.d.ts.map +1 -0
- package/dist/audio/engine/XrunTracker.js +118 -0
- package/dist/audio/engine/XrunTracker.js.map +1 -0
- package/dist/audio/export/CDMarkerExporter.d.ts +62 -0
- package/dist/audio/export/CDMarkerExporter.d.ts.map +1 -0
- package/dist/audio/export/CDMarkerExporter.js +164 -0
- package/dist/audio/export/CDMarkerExporter.js.map +1 -0
- package/dist/audio/export/ExportGraphBuilder.d.ts +121 -0
- package/dist/audio/export/ExportGraphBuilder.d.ts.map +1 -0
- package/dist/audio/export/ExportGraphBuilder.js +522 -0
- package/dist/audio/export/ExportGraphBuilder.js.map +1 -0
- package/dist/audio/export/ExportPresetManager.d.ts +87 -0
- package/dist/audio/export/ExportPresetManager.d.ts.map +1 -0
- package/dist/audio/export/ExportPresetManager.js +306 -0
- package/dist/audio/export/ExportPresetManager.js.map +1 -0
- package/dist/commands/handlers/RegionHandler.d.ts.map +1 -1
- package/dist/commands/handlers/RegionHandler.js +25 -1
- package/dist/commands/handlers/RegionHandler.js.map +1 -1
- package/dist/commands/handlers/TrackHandler.js +1 -1
- package/dist/commands/handlers/TrackHandler.js.map +1 -1
- package/dist/commands/impl/FreezeTrackCommand.d.ts.map +1 -1
- package/dist/commands/impl/FreezeTrackCommand.js +37 -36
- package/dist/commands/impl/FreezeTrackCommand.js.map +1 -1
- package/dist/commands/impl/SetRegionTimeDomainCommand.d.ts.map +1 -1
- package/dist/commands/impl/SetRegionTimeDomainCommand.js +3 -3
- package/dist/commands/impl/SetRegionTimeDomainCommand.js.map +1 -1
- package/dist/commands/impl/ToggleLoopCommand.d.ts +1 -0
- package/dist/commands/impl/ToggleLoopCommand.d.ts.map +1 -1
- package/dist/commands/impl/ToggleLoopCommand.js +10 -0
- package/dist/commands/impl/ToggleLoopCommand.js.map +1 -1
- package/dist/commands/impl/TrimRegionCommand.d.ts +14 -1
- package/dist/commands/impl/TrimRegionCommand.d.ts.map +1 -1
- package/dist/commands/impl/TrimRegionCommand.js +138 -33
- package/dist/commands/impl/TrimRegionCommand.js.map +1 -1
- package/dist/commands/impl/TrimRegionToPlayheadCommand.d.ts +27 -0
- package/dist/commands/impl/TrimRegionToPlayheadCommand.d.ts.map +1 -0
- package/dist/commands/impl/TrimRegionToPlayheadCommand.js +69 -0
- package/dist/commands/impl/TrimRegionToPlayheadCommand.js.map +1 -0
- package/dist/commands/impl/TrimRegionToRangeCommand.d.ts +28 -0
- package/dist/commands/impl/TrimRegionToRangeCommand.d.ts.map +1 -0
- package/dist/commands/impl/TrimRegionToRangeCommand.js +69 -0
- package/dist/commands/impl/TrimRegionToRangeCommand.js.map +1 -0
- package/dist/commands/impl/TrimToAdjacentRegionCommand.d.ts +27 -0
- package/dist/commands/impl/TrimToAdjacentRegionCommand.d.ts.map +1 -0
- package/dist/commands/impl/TrimToAdjacentRegionCommand.js +77 -0
- package/dist/commands/impl/TrimToAdjacentRegionCommand.js.map +1 -0
- package/dist/commands/types.d.ts +19 -0
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/commands/types.js +21 -0
- package/dist/commands/types.js.map +1 -1
- package/dist/domain/Crossfade.d.ts +78 -0
- package/dist/domain/Crossfade.d.ts.map +1 -0
- package/dist/domain/Crossfade.js +216 -0
- package/dist/domain/Crossfade.js.map +1 -0
- package/dist/domain/ExportConfig.d.ts +98 -1
- package/dist/domain/ExportConfig.d.ts.map +1 -1
- package/dist/domain/ExportConfig.js +154 -1
- package/dist/domain/ExportConfig.js.map +1 -1
- package/dist/domain/ExportPreset.d.ts +62 -0
- package/dist/domain/ExportPreset.d.ts.map +1 -0
- package/dist/domain/ExportPreset.js +79 -0
- package/dist/domain/ExportPreset.js.map +1 -0
- package/dist/domain/ImportStatus.d.ts +40 -0
- package/dist/domain/ImportStatus.d.ts.map +1 -0
- package/dist/domain/ImportStatus.js +86 -0
- package/dist/domain/ImportStatus.js.map +1 -0
- package/dist/domain/Playlist.d.ts +72 -0
- package/dist/domain/Playlist.d.ts.map +1 -1
- package/dist/domain/Playlist.js +231 -6
- package/dist/domain/Playlist.js.map +1 -1
- package/dist/domain/Region.d.ts +76 -0
- package/dist/domain/Region.d.ts.map +1 -1
- package/dist/domain/Region.js +234 -1
- package/dist/domain/Region.js.map +1 -1
- package/dist/domain/Route.d.ts +43 -3
- package/dist/domain/Route.d.ts.map +1 -1
- package/dist/domain/Route.js +92 -6
- package/dist/domain/Route.js.map +1 -1
- package/dist/domain/Session.d.ts +31 -0
- package/dist/domain/Session.d.ts.map +1 -1
- package/dist/domain/Session.js +70 -0
- package/dist/domain/Session.js.map +1 -1
- package/dist/domain/Source.d.ts +145 -1
- package/dist/domain/Source.d.ts.map +1 -1
- package/dist/domain/Source.js +144 -1
- package/dist/domain/Source.js.map +1 -1
- package/dist/domain/ThawList.d.ts +37 -0
- package/dist/domain/ThawList.d.ts.map +1 -0
- package/dist/domain/ThawList.js +73 -0
- package/dist/domain/ThawList.js.map +1 -0
- package/dist/domain/Track.d.ts +93 -1
- package/dist/domain/Track.d.ts.map +1 -1
- package/dist/domain/Track.js +136 -0
- package/dist/domain/Track.js.map +1 -1
- package/dist/domain/TriggerBox.d.ts +123 -0
- package/dist/domain/TriggerBox.d.ts.map +1 -0
- package/dist/domain/TriggerBox.js +430 -0
- package/dist/domain/TriggerBox.js.map +1 -0
- package/dist/domain/VCATrack.d.ts +64 -1
- package/dist/domain/VCATrack.d.ts.map +1 -1
- package/dist/domain/VCATrack.js +128 -1
- package/dist/domain/VCATrack.js.map +1 -1
- package/dist/domain/VideoExportConfig.d.ts +117 -0
- package/dist/domain/VideoExportConfig.d.ts.map +1 -0
- package/dist/domain/VideoExportConfig.js +244 -0
- package/dist/domain/VideoExportConfig.js.map +1 -0
- package/dist/domain/VideoExportStatus.d.ts +89 -0
- package/dist/domain/VideoExportStatus.d.ts.map +1 -0
- package/dist/domain/VideoExportStatus.js +192 -0
- package/dist/domain/VideoExportStatus.js.map +1 -0
- package/dist/domain/VideoMetadata.d.ts +46 -0
- package/dist/domain/VideoMetadata.d.ts.map +1 -0
- package/dist/domain/VideoMetadata.js +2 -0
- package/dist/domain/VideoMetadata.js.map +1 -0
- package/dist/domain/index.d.ts +5 -0
- package/dist/domain/index.d.ts.map +1 -1
- package/dist/domain/index.js +5 -0
- package/dist/domain/index.js.map +1 -1
- package/dist/domain/temporal/TempoMap.d.ts +199 -2
- package/dist/domain/temporal/TempoMap.d.ts.map +1 -1
- package/dist/domain/temporal/TempoMap.js +464 -30
- package/dist/domain/temporal/TempoMap.js.map +1 -1
- package/dist/index.d.ts +43 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/ThawList.d.ts +100 -0
- package/dist/lib/ThawList.d.ts.map +1 -0
- package/dist/lib/ThawList.js +153 -0
- package/dist/lib/ThawList.js.map +1 -0
- package/dist/midi/MidiFileParser.d.ts +33 -0
- package/dist/midi/MidiFileParser.d.ts.map +1 -0
- package/dist/midi/MidiFileParser.js +246 -0
- package/dist/midi/MidiFileParser.js.map +1 -0
- package/dist/midi/MidiFileWriter.d.ts +31 -0
- package/dist/midi/MidiFileWriter.d.ts.map +1 -0
- package/dist/midi/MidiFileWriter.js +160 -0
- package/dist/midi/MidiFileWriter.js.map +1 -0
- package/dist/midi/MidiImporter.d.ts +37 -0
- package/dist/midi/MidiImporter.d.ts.map +1 -0
- package/dist/midi/MidiImporter.js +62 -0
- package/dist/midi/MidiImporter.js.map +1 -0
- package/dist/plugins/PluginPresetManager.d.ts +86 -0
- package/dist/plugins/PluginPresetManager.d.ts.map +1 -0
- package/dist/plugins/PluginPresetManager.js +140 -0
- package/dist/plugins/PluginPresetManager.js.map +1 -0
- package/dist/processing/IO.d.ts +15 -1
- package/dist/processing/IO.d.ts.map +1 -1
- package/dist/processing/IO.js +29 -1
- package/dist/processing/IO.js.map +1 -1
- package/dist/processing/InternalSend.d.ts +104 -0
- package/dist/processing/InternalSend.d.ts.map +1 -0
- package/dist/processing/InternalSend.js +175 -0
- package/dist/processing/InternalSend.js.map +1 -0
- package/dist/processing/MeterDSP.d.ts +166 -0
- package/dist/processing/MeterDSP.d.ts.map +1 -0
- package/dist/processing/MeterDSP.js +754 -0
- package/dist/processing/MeterDSP.js.map +1 -0
- package/dist/processing/Panner.d.ts +145 -0
- package/dist/processing/Panner.d.ts.map +1 -0
- package/dist/processing/Panner.js +281 -0
- package/dist/processing/Panner.js.map +1 -0
- package/dist/processing/PluginInsert.d.ts +56 -1
- package/dist/processing/PluginInsert.d.ts.map +1 -1
- package/dist/processing/PluginInsert.js +180 -2
- package/dist/processing/PluginInsert.js.map +1 -1
- package/dist/processing/Processor.d.ts +38 -0
- package/dist/processing/Processor.d.ts.map +1 -1
- package/dist/processing/Processor.js +60 -1
- package/dist/processing/Processor.js.map +1 -1
- package/dist/processing/SurroundPanner.d.ts +122 -0
- package/dist/processing/SurroundPanner.d.ts.map +1 -0
- package/dist/processing/SurroundPanner.js +376 -0
- package/dist/processing/SurroundPanner.js.map +1 -0
- package/dist/processing/TruePeakLimiter.d.ts +34 -0
- package/dist/processing/TruePeakLimiter.d.ts.map +1 -0
- package/dist/processing/TruePeakLimiter.js +144 -0
- package/dist/processing/TruePeakLimiter.js.map +1 -0
- package/dist/storage/ExportPresetStorage.d.ts +33 -0
- package/dist/storage/ExportPresetStorage.d.ts.map +1 -0
- package/dist/storage/ExportPresetStorage.js +121 -0
- package/dist/storage/ExportPresetStorage.js.map +1 -0
- package/dist/storage/SessionArchive.d.ts +73 -0
- package/dist/storage/SessionArchive.d.ts.map +1 -0
- package/dist/storage/SessionArchive.js +211 -0
- package/dist/storage/SessionArchive.js.map +1 -0
- package/dist/storage/SessionTemplate.d.ts +74 -5
- package/dist/storage/SessionTemplate.d.ts.map +1 -1
- package/dist/storage/SessionTemplate.js +247 -53
- package/dist/storage/SessionTemplate.js.map +1 -1
- package/dist/utils/BwfMetadataWriter.d.ts +42 -0
- package/dist/utils/BwfMetadataWriter.d.ts.map +1 -0
- package/dist/utils/BwfMetadataWriter.js +131 -0
- package/dist/utils/BwfMetadataWriter.js.map +1 -0
- package/dist/utils/FilenameTemplate.d.ts +40 -0
- package/dist/utils/FilenameTemplate.d.ts.map +1 -0
- package/dist/utils/FilenameTemplate.js +78 -0
- package/dist/utils/FilenameTemplate.js.map +1 -0
- package/dist/utils/Logger.d.ts +28 -0
- package/dist/utils/Logger.d.ts.map +1 -0
- package/dist/utils/Logger.js +46 -0
- package/dist/utils/Logger.js.map +1 -0
- package/dist/utils/Mp4ChapterGenerator.d.ts +17 -0
- package/dist/utils/Mp4ChapterGenerator.d.ts.map +1 -0
- package/dist/utils/Mp4ChapterGenerator.js +33 -0
- package/dist/utils/Mp4ChapterGenerator.js.map +1 -0
- package/dist/utils/OggEncoder.d.ts +20 -20
- package/dist/utils/OggEncoder.d.ts.map +1 -1
- package/dist/utils/OggEncoder.js +114 -50
- package/dist/utils/OggEncoder.js.map +1 -1
- package/dist/utils/TocGenerator.d.ts +17 -0
- package/dist/utils/TocGenerator.d.ts.map +1 -0
- package/dist/utils/TocGenerator.js +47 -0
- package/dist/utils/TocGenerator.js.map +1 -0
- package/package.json +7 -8
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard MIDI File (SMF) Parser
|
|
3
|
+
* Ardour reference: libs/evoral/SMF.cc
|
|
4
|
+
*
|
|
5
|
+
* Parses SMF Type 0 (single track) and Type 1 (multi-track) files.
|
|
6
|
+
* Converts MIDI ticks to frames based on tempo.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Parse a Standard MIDI File from an ArrayBuffer.
|
|
10
|
+
*/
|
|
11
|
+
export function parseMidiFile(data, sampleRate = 44100) {
|
|
12
|
+
const view = new DataView(data);
|
|
13
|
+
let offset = 0;
|
|
14
|
+
// Read header chunk "MThd"
|
|
15
|
+
const headerTag = readString(view, offset, 4);
|
|
16
|
+
if (headerTag !== 'MThd') {
|
|
17
|
+
throw new Error('Not a valid MIDI file: missing MThd header');
|
|
18
|
+
}
|
|
19
|
+
offset += 4;
|
|
20
|
+
const headerLength = view.getUint32(offset, false);
|
|
21
|
+
offset += 4;
|
|
22
|
+
const format = view.getUint16(offset, false);
|
|
23
|
+
offset += 2;
|
|
24
|
+
const numTracks = view.getUint16(offset, false);
|
|
25
|
+
offset += 2;
|
|
26
|
+
const division = view.getUint16(offset, false);
|
|
27
|
+
offset += 2;
|
|
28
|
+
// Skip any remaining header bytes
|
|
29
|
+
offset += headerLength - 6;
|
|
30
|
+
let ticksPerBeat;
|
|
31
|
+
if (division & 0x8000) {
|
|
32
|
+
// SMPTE-based: frames per second + ticks per frame
|
|
33
|
+
const fps = -(((division >> 8) & 0xFF) << 24 >> 24); // sign-extend
|
|
34
|
+
const tpf = division & 0xFF;
|
|
35
|
+
ticksPerBeat = fps * tpf; // approximate
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
ticksPerBeat = division;
|
|
39
|
+
}
|
|
40
|
+
// Parse track chunks
|
|
41
|
+
let globalTempo = 120; // Default BPM
|
|
42
|
+
const tracks = [];
|
|
43
|
+
for (let t = 0; t < numTracks && offset < data.byteLength; t++) {
|
|
44
|
+
const trackTag = readString(view, offset, 4);
|
|
45
|
+
if (trackTag !== 'MTrk') {
|
|
46
|
+
throw new Error(`Expected MTrk at offset ${offset}, got ${trackTag}`);
|
|
47
|
+
}
|
|
48
|
+
offset += 4;
|
|
49
|
+
const trackLength = view.getUint32(offset, false);
|
|
50
|
+
offset += 4;
|
|
51
|
+
const trackEnd = offset + trackLength;
|
|
52
|
+
const track = parseTrack(view, offset, trackEnd, ticksPerBeat, sampleRate);
|
|
53
|
+
// Extract tempo if present (first track in Type 1)
|
|
54
|
+
if (track.tempo > 0) {
|
|
55
|
+
globalTempo = track.tempo;
|
|
56
|
+
}
|
|
57
|
+
tracks.push({
|
|
58
|
+
name: track.name || `Track ${t + 1}`,
|
|
59
|
+
notes: track.notes,
|
|
60
|
+
channel: track.channel,
|
|
61
|
+
});
|
|
62
|
+
offset = trackEnd;
|
|
63
|
+
}
|
|
64
|
+
// Apply tempo to convert ticks to frames
|
|
65
|
+
const secondsPerTick = 60 / (globalTempo * ticksPerBeat);
|
|
66
|
+
for (const track of tracks) {
|
|
67
|
+
for (const note of track.notes) {
|
|
68
|
+
note.startFrame = Math.round(note.startTick * secondsPerTick * sampleRate);
|
|
69
|
+
note.durationFrames = Math.round(note.durationTicks * secondsPerTick * sampleRate);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
format,
|
|
74
|
+
numTracks,
|
|
75
|
+
ticksPerBeat,
|
|
76
|
+
tempo: globalTempo,
|
|
77
|
+
tracks,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function parseTrack(view, start, end, ticksPerBeat, sampleRate) {
|
|
81
|
+
let offset = start;
|
|
82
|
+
let tick = 0;
|
|
83
|
+
let name = '';
|
|
84
|
+
let tempo = 0;
|
|
85
|
+
let primaryChannel = 0;
|
|
86
|
+
// Track active notes for Note-On/Note-Off pairing
|
|
87
|
+
const activeNotes = new Map();
|
|
88
|
+
const notes = [];
|
|
89
|
+
let prevStatus = 0;
|
|
90
|
+
while (offset < end) {
|
|
91
|
+
// Read delta time (variable-length quantity)
|
|
92
|
+
const [deltaTime, bytesRead] = readVLQ(view, offset);
|
|
93
|
+
offset += bytesRead;
|
|
94
|
+
tick += deltaTime;
|
|
95
|
+
if (offset >= end)
|
|
96
|
+
break;
|
|
97
|
+
let statusByte = view.getUint8(offset);
|
|
98
|
+
// Running status
|
|
99
|
+
if (statusByte < 0x80) {
|
|
100
|
+
statusByte = prevStatus;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
offset++;
|
|
104
|
+
prevStatus = statusByte;
|
|
105
|
+
}
|
|
106
|
+
const type = statusByte & 0xF0;
|
|
107
|
+
const channel = statusByte & 0x0F;
|
|
108
|
+
switch (type) {
|
|
109
|
+
case 0x90: { // Note On
|
|
110
|
+
const pitch = view.getUint8(offset++);
|
|
111
|
+
const velocity = view.getUint8(offset++);
|
|
112
|
+
if (velocity === 0) {
|
|
113
|
+
// Note On with velocity 0 = Note Off
|
|
114
|
+
finishNote(activeNotes, notes, channel, pitch, tick);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
const key = `${channel}-${pitch}`;
|
|
118
|
+
// End any existing note on same key
|
|
119
|
+
finishNote(activeNotes, notes, channel, pitch, tick);
|
|
120
|
+
activeNotes.set(key, { pitch, velocity, channel, startTick: tick });
|
|
121
|
+
primaryChannel = channel;
|
|
122
|
+
}
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
case 0x80: { // Note Off
|
|
126
|
+
const pitch = view.getUint8(offset++);
|
|
127
|
+
offset++; // velocity (ignored)
|
|
128
|
+
finishNote(activeNotes, notes, channel, pitch, tick);
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
case 0xA0: // Aftertouch
|
|
132
|
+
case 0xB0: // Control Change
|
|
133
|
+
case 0xE0: // Pitch Bend
|
|
134
|
+
offset += 2;
|
|
135
|
+
break;
|
|
136
|
+
case 0xC0: // Program Change
|
|
137
|
+
case 0xD0: // Channel Pressure
|
|
138
|
+
offset += 1;
|
|
139
|
+
break;
|
|
140
|
+
case 0xF0: {
|
|
141
|
+
if (statusByte === 0xFF) {
|
|
142
|
+
// Meta event
|
|
143
|
+
const metaType = view.getUint8(offset++);
|
|
144
|
+
const [metaLength, metaLenBytes] = readVLQ(view, offset);
|
|
145
|
+
offset += metaLenBytes;
|
|
146
|
+
if (metaType === 0x03) {
|
|
147
|
+
// Track Name
|
|
148
|
+
name = readString(view, offset, metaLength);
|
|
149
|
+
}
|
|
150
|
+
else if (metaType === 0x51) {
|
|
151
|
+
// Tempo (microseconds per quarter note)
|
|
152
|
+
if (metaLength >= 3) {
|
|
153
|
+
const uspqn = (view.getUint8(offset) << 16) |
|
|
154
|
+
(view.getUint8(offset + 1) << 8) |
|
|
155
|
+
view.getUint8(offset + 2);
|
|
156
|
+
tempo = 60000000 / uspqn;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else if (metaType === 0x2F) {
|
|
160
|
+
// End of Track
|
|
161
|
+
offset += metaLength;
|
|
162
|
+
// Close any remaining active notes
|
|
163
|
+
for (const [key, note] of activeNotes) {
|
|
164
|
+
notes.push({
|
|
165
|
+
pitch: note.pitch,
|
|
166
|
+
velocity: note.velocity,
|
|
167
|
+
channel: note.channel,
|
|
168
|
+
startTick: note.startTick,
|
|
169
|
+
durationTicks: tick - note.startTick,
|
|
170
|
+
startFrame: 0,
|
|
171
|
+
durationFrames: 0,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
activeNotes.clear();
|
|
175
|
+
return { name, notes, channel: primaryChannel, tempo };
|
|
176
|
+
}
|
|
177
|
+
offset += metaLength;
|
|
178
|
+
}
|
|
179
|
+
else if (statusByte === 0xF0 || statusByte === 0xF7) {
|
|
180
|
+
// SysEx
|
|
181
|
+
const [sysexLen, sysexLenBytes] = readVLQ(view, offset);
|
|
182
|
+
offset += sysexLenBytes + sysexLen;
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
// Other system messages
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
default:
|
|
191
|
+
// Unknown — skip
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// Close remaining notes
|
|
196
|
+
for (const [key, note] of activeNotes) {
|
|
197
|
+
notes.push({
|
|
198
|
+
pitch: note.pitch,
|
|
199
|
+
velocity: note.velocity,
|
|
200
|
+
channel: note.channel,
|
|
201
|
+
startTick: note.startTick,
|
|
202
|
+
durationTicks: tick - note.startTick,
|
|
203
|
+
startFrame: 0,
|
|
204
|
+
durationFrames: 0,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
return { name, notes, channel: primaryChannel, tempo };
|
|
208
|
+
}
|
|
209
|
+
function finishNote(activeNotes, notes, channel, pitch, tick) {
|
|
210
|
+
const key = `${channel}-${pitch}`;
|
|
211
|
+
const active = activeNotes.get(key);
|
|
212
|
+
if (active) {
|
|
213
|
+
notes.push({
|
|
214
|
+
pitch: active.pitch,
|
|
215
|
+
velocity: active.velocity,
|
|
216
|
+
channel: active.channel,
|
|
217
|
+
startTick: active.startTick,
|
|
218
|
+
durationTicks: tick - active.startTick,
|
|
219
|
+
startFrame: 0,
|
|
220
|
+
durationFrames: 0,
|
|
221
|
+
});
|
|
222
|
+
activeNotes.delete(key);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function readVLQ(view, offset) {
|
|
226
|
+
let value = 0;
|
|
227
|
+
let bytesRead = 0;
|
|
228
|
+
while (offset + bytesRead < view.byteLength) {
|
|
229
|
+
const byte = view.getUint8(offset + bytesRead);
|
|
230
|
+
value = (value << 7) | (byte & 0x7F);
|
|
231
|
+
bytesRead++;
|
|
232
|
+
if ((byte & 0x80) === 0)
|
|
233
|
+
break;
|
|
234
|
+
if (bytesRead > 4)
|
|
235
|
+
break; // VLQ can't be more than 4 bytes
|
|
236
|
+
}
|
|
237
|
+
return [value, bytesRead];
|
|
238
|
+
}
|
|
239
|
+
function readString(view, offset, length) {
|
|
240
|
+
let str = '';
|
|
241
|
+
for (let i = 0; i < length && offset + i < view.byteLength; i++) {
|
|
242
|
+
str += String.fromCharCode(view.getUint8(offset + i));
|
|
243
|
+
}
|
|
244
|
+
return str;
|
|
245
|
+
}
|
|
246
|
+
//# sourceMappingURL=MidiFileParser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MidiFileParser.js","sourceRoot":"","sources":["../../src/midi/MidiFileParser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard MIDI File (SMF) Writer
|
|
3
|
+
* Ardour reference: libs/ardour/smf_source.cc / export_smf_writer.cc
|
|
4
|
+
*
|
|
5
|
+
* Generates SMF Type 1 files from MidiRegion/MidiNote data.
|
|
6
|
+
*/
|
|
7
|
+
export interface MidiWriteNote {
|
|
8
|
+
pitch: number;
|
|
9
|
+
velocity: number;
|
|
10
|
+
channel: number;
|
|
11
|
+
startFrame: number;
|
|
12
|
+
durationFrames: number;
|
|
13
|
+
}
|
|
14
|
+
export interface MidiWriteTrack {
|
|
15
|
+
name: string;
|
|
16
|
+
notes: MidiWriteNote[];
|
|
17
|
+
}
|
|
18
|
+
export interface MidiWriteOptions {
|
|
19
|
+
ticksPerBeat?: number;
|
|
20
|
+
tempo?: number;
|
|
21
|
+
sampleRate?: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Write a Standard MIDI File (Type 1) to an ArrayBuffer.
|
|
25
|
+
*/
|
|
26
|
+
export declare function writeMidiFile(tracks: MidiWriteTrack[], options?: MidiWriteOptions): ArrayBuffer;
|
|
27
|
+
/**
|
|
28
|
+
* Convenience: create a MIDI file Blob from tracks.
|
|
29
|
+
*/
|
|
30
|
+
export declare function createMidiBlob(tracks: MidiWriteTrack[], options?: MidiWriteOptions): Blob;
|
|
31
|
+
//# sourceMappingURL=MidiFileWriter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MidiFileWriter.d.ts","sourceRoot":"","sources":["../../src/midi/MidiFileWriter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,aAAa;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,aAAa,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,wBAAgB,aAAa,CACzB,MAAM,EAAE,cAAc,EAAE,EACxB,OAAO,GAAE,gBAAqB,GAC/B,WAAW,CA0Cb;AAqJD;;GAEG;AACH,wBAAgB,cAAc,CAC1B,MAAM,EAAE,cAAc,EAAE,EACxB,OAAO,CAAC,EAAE,gBAAgB,GAC3B,IAAI,CAGN"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard MIDI File (SMF) Writer
|
|
3
|
+
* Ardour reference: libs/ardour/smf_source.cc / export_smf_writer.cc
|
|
4
|
+
*
|
|
5
|
+
* Generates SMF Type 1 files from MidiRegion/MidiNote data.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Write a Standard MIDI File (Type 1) to an ArrayBuffer.
|
|
9
|
+
*/
|
|
10
|
+
export function writeMidiFile(tracks, options = {}) {
|
|
11
|
+
var _a, _b, _c;
|
|
12
|
+
const ticksPerBeat = (_a = options.ticksPerBeat) !== null && _a !== void 0 ? _a : 480;
|
|
13
|
+
const tempo = (_b = options.tempo) !== null && _b !== void 0 ? _b : 120;
|
|
14
|
+
const sampleRate = (_c = options.sampleRate) !== null && _c !== void 0 ? _c : 44100;
|
|
15
|
+
// Convert frames to ticks
|
|
16
|
+
const secondsPerTick = 60 / (tempo * ticksPerBeat);
|
|
17
|
+
const framesToTicks = (frames) => Math.round(frames / (secondsPerTick * sampleRate));
|
|
18
|
+
const trackChunks = [];
|
|
19
|
+
// Track 0: tempo track
|
|
20
|
+
trackChunks.push(buildTempoTrack(tempo, ticksPerBeat));
|
|
21
|
+
// Subsequent tracks: note data
|
|
22
|
+
for (const track of tracks) {
|
|
23
|
+
trackChunks.push(buildNoteTrack(track, framesToTicks));
|
|
24
|
+
}
|
|
25
|
+
// Build file header
|
|
26
|
+
const numTracks = trackChunks.length;
|
|
27
|
+
const headerChunk = buildHeaderChunk(1, numTracks, ticksPerBeat);
|
|
28
|
+
// Calculate total size
|
|
29
|
+
let totalSize = headerChunk.byteLength;
|
|
30
|
+
for (const chunk of trackChunks) {
|
|
31
|
+
totalSize += chunk.byteLength;
|
|
32
|
+
}
|
|
33
|
+
// Combine all chunks
|
|
34
|
+
const result = new Uint8Array(totalSize);
|
|
35
|
+
let offset = 0;
|
|
36
|
+
result.set(new Uint8Array(headerChunk), offset);
|
|
37
|
+
offset += headerChunk.byteLength;
|
|
38
|
+
for (const chunk of trackChunks) {
|
|
39
|
+
result.set(chunk, offset);
|
|
40
|
+
offset += chunk.byteLength;
|
|
41
|
+
}
|
|
42
|
+
return result.buffer;
|
|
43
|
+
}
|
|
44
|
+
function buildHeaderChunk(format, numTracks, ticksPerBeat) {
|
|
45
|
+
const buffer = new ArrayBuffer(14);
|
|
46
|
+
const view = new DataView(buffer);
|
|
47
|
+
// "MThd"
|
|
48
|
+
view.setUint8(0, 0x4D);
|
|
49
|
+
view.setUint8(1, 0x54);
|
|
50
|
+
view.setUint8(2, 0x68);
|
|
51
|
+
view.setUint8(3, 0x64);
|
|
52
|
+
// Header length (always 6)
|
|
53
|
+
view.setUint32(4, 6, false);
|
|
54
|
+
// Format
|
|
55
|
+
view.setUint16(8, format, false);
|
|
56
|
+
// Number of tracks
|
|
57
|
+
view.setUint16(10, numTracks, false);
|
|
58
|
+
// Division (ticks per beat)
|
|
59
|
+
view.setUint16(12, ticksPerBeat, false);
|
|
60
|
+
return buffer;
|
|
61
|
+
}
|
|
62
|
+
function buildTempoTrack(tempo, ticksPerBeat) {
|
|
63
|
+
const events = [];
|
|
64
|
+
// Delta time 0
|
|
65
|
+
events.push(0x00);
|
|
66
|
+
// Set Tempo meta event (FF 51 03 tt tt tt)
|
|
67
|
+
const uspqn = Math.round(60000000 / tempo);
|
|
68
|
+
events.push(0xFF, 0x51, 0x03);
|
|
69
|
+
events.push((uspqn >> 16) & 0xFF);
|
|
70
|
+
events.push((uspqn >> 8) & 0xFF);
|
|
71
|
+
events.push(uspqn & 0xFF);
|
|
72
|
+
// End of Track (delta=0)
|
|
73
|
+
events.push(0x00, 0xFF, 0x2F, 0x00);
|
|
74
|
+
return wrapTrackChunk(new Uint8Array(events));
|
|
75
|
+
}
|
|
76
|
+
function buildNoteTrack(track, framesToTicks) {
|
|
77
|
+
const events = [];
|
|
78
|
+
// Track Name meta event
|
|
79
|
+
if (track.name) {
|
|
80
|
+
const nameBytes = new TextEncoder().encode(track.name);
|
|
81
|
+
events.push({
|
|
82
|
+
tick: 0,
|
|
83
|
+
data: [0xFF, 0x03, ...writeVLQ(nameBytes.length), ...nameBytes],
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
// Note events
|
|
87
|
+
for (const note of track.notes) {
|
|
88
|
+
const startTick = framesToTicks(note.startFrame);
|
|
89
|
+
const endTick = framesToTicks(note.startFrame + note.durationFrames);
|
|
90
|
+
const channel = Math.max(0, Math.min(15, note.channel));
|
|
91
|
+
// Note On
|
|
92
|
+
events.push({
|
|
93
|
+
tick: startTick,
|
|
94
|
+
data: [0x90 | channel, note.pitch & 0x7F, note.velocity & 0x7F],
|
|
95
|
+
});
|
|
96
|
+
// Note Off
|
|
97
|
+
events.push({
|
|
98
|
+
tick: endTick,
|
|
99
|
+
data: [0x80 | channel, note.pitch & 0x7F, 0x40], // release velocity 64
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
// Sort by tick, Note Off before Note On at same tick
|
|
103
|
+
events.sort((a, b) => {
|
|
104
|
+
if (a.tick !== b.tick)
|
|
105
|
+
return a.tick - b.tick;
|
|
106
|
+
// Note Off (0x80) should come before Note On (0x90)
|
|
107
|
+
const aIsOff = (a.data[0] & 0xF0) === 0x80 ? 0 : 1;
|
|
108
|
+
const bIsOff = (b.data[0] & 0xF0) === 0x80 ? 0 : 1;
|
|
109
|
+
return aIsOff - bIsOff;
|
|
110
|
+
});
|
|
111
|
+
// Convert to byte stream with delta times
|
|
112
|
+
const bytes = [];
|
|
113
|
+
let prevTick = 0;
|
|
114
|
+
for (const event of events) {
|
|
115
|
+
const delta = Math.max(0, event.tick - prevTick);
|
|
116
|
+
bytes.push(...writeVLQ(delta));
|
|
117
|
+
bytes.push(...event.data);
|
|
118
|
+
prevTick = event.tick;
|
|
119
|
+
}
|
|
120
|
+
// End of Track
|
|
121
|
+
bytes.push(0x00, 0xFF, 0x2F, 0x00);
|
|
122
|
+
return wrapTrackChunk(new Uint8Array(bytes));
|
|
123
|
+
}
|
|
124
|
+
function wrapTrackChunk(trackData) {
|
|
125
|
+
const chunk = new Uint8Array(8 + trackData.length);
|
|
126
|
+
const view = new DataView(chunk.buffer);
|
|
127
|
+
// "MTrk"
|
|
128
|
+
chunk[0] = 0x4D;
|
|
129
|
+
chunk[1] = 0x54;
|
|
130
|
+
chunk[2] = 0x72;
|
|
131
|
+
chunk[3] = 0x6B;
|
|
132
|
+
// Track length
|
|
133
|
+
view.setUint32(4, trackData.length, false);
|
|
134
|
+
// Track data
|
|
135
|
+
chunk.set(trackData, 8);
|
|
136
|
+
return chunk;
|
|
137
|
+
}
|
|
138
|
+
function writeVLQ(value) {
|
|
139
|
+
if (value < 0)
|
|
140
|
+
value = 0;
|
|
141
|
+
if (value < 0x80)
|
|
142
|
+
return [value];
|
|
143
|
+
const bytes = [];
|
|
144
|
+
let v = value;
|
|
145
|
+
bytes.unshift(v & 0x7F);
|
|
146
|
+
v >>= 7;
|
|
147
|
+
while (v > 0) {
|
|
148
|
+
bytes.unshift((v & 0x7F) | 0x80);
|
|
149
|
+
v >>= 7;
|
|
150
|
+
}
|
|
151
|
+
return bytes;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Convenience: create a MIDI file Blob from tracks.
|
|
155
|
+
*/
|
|
156
|
+
export function createMidiBlob(tracks, options) {
|
|
157
|
+
const buffer = writeMidiFile(tracks, options);
|
|
158
|
+
return new Blob([buffer], { type: 'audio/midi' });
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=MidiFileWriter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MidiFileWriter.js","sourceRoot":"","sources":["../../src/midi/MidiFileWriter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MIDI Importer — bridges MidiFileParser output to domain models.
|
|
3
|
+
* Creates MidiClip/MidiRegion/Track from parsed MIDI file data.
|
|
4
|
+
*/
|
|
5
|
+
import { MidiFileData } from './MidiFileParser';
|
|
6
|
+
import { MidiClip } from '../domain/MidiClip';
|
|
7
|
+
import { MidiRegion } from '../domain/MidiRegion';
|
|
8
|
+
export interface MidiImportResult {
|
|
9
|
+
/** Parsed file metadata */
|
|
10
|
+
fileData: MidiFileData;
|
|
11
|
+
/** One MidiClip per track in the file */
|
|
12
|
+
clips: MidiClip[];
|
|
13
|
+
/** One MidiRegion per track, positioned at startFrame */
|
|
14
|
+
regions: MidiRegion[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Import a MIDI file from an ArrayBuffer.
|
|
18
|
+
*
|
|
19
|
+
* @param data Raw .mid file bytes
|
|
20
|
+
* @param sampleRate Session sample rate (for tick-to-frame conversion)
|
|
21
|
+
* @param startFrame Where to place the imported regions on the timeline
|
|
22
|
+
*/
|
|
23
|
+
export declare function importMidiFile(data: ArrayBuffer, sampleRate?: number, startFrame?: number): MidiImportResult;
|
|
24
|
+
/**
|
|
25
|
+
* Export MidiRegions to MIDI file data for the writer.
|
|
26
|
+
*/
|
|
27
|
+
export declare function exportMidiRegions(regions: MidiRegion[], sampleRate?: number): {
|
|
28
|
+
name: string;
|
|
29
|
+
notes: Array<{
|
|
30
|
+
pitch: number;
|
|
31
|
+
velocity: number;
|
|
32
|
+
channel: number;
|
|
33
|
+
startFrame: number;
|
|
34
|
+
durationFrames: number;
|
|
35
|
+
}>;
|
|
36
|
+
}[];
|
|
37
|
+
//# sourceMappingURL=MidiImporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MidiImporter.d.ts","sourceRoot":"","sources":["../../src/midi/MidiImporter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAgC,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD,MAAM,WAAW,gBAAgB;IAC7B,2BAA2B;IAC3B,QAAQ,EAAE,YAAY,CAAC;IACvB,yCAAyC;IACzC,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,yDAAyD;IACzD,OAAO,EAAE,UAAU,EAAE,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC1B,IAAI,EAAE,WAAW,EACjB,UAAU,GAAE,MAAc,EAC1B,UAAU,GAAE,MAAU,GACvB,gBAAgB,CA0DlB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC7B,OAAO,EAAE,UAAU,EAAE,EACrB,UAAU,GAAE,MAAc,GAC3B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,EAAE,CAWpI"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MIDI Importer — bridges MidiFileParser output to domain models.
|
|
3
|
+
* Creates MidiClip/MidiRegion/Track from parsed MIDI file data.
|
|
4
|
+
*/
|
|
5
|
+
import { parseMidiFile } from './MidiFileParser';
|
|
6
|
+
import { MidiClip } from '../domain/MidiClip';
|
|
7
|
+
import { MidiNote } from '../domain/MidiNote';
|
|
8
|
+
import { MidiRegion } from '../domain/MidiRegion';
|
|
9
|
+
/**
|
|
10
|
+
* Import a MIDI file from an ArrayBuffer.
|
|
11
|
+
*
|
|
12
|
+
* @param data Raw .mid file bytes
|
|
13
|
+
* @param sampleRate Session sample rate (for tick-to-frame conversion)
|
|
14
|
+
* @param startFrame Where to place the imported regions on the timeline
|
|
15
|
+
*/
|
|
16
|
+
export function importMidiFile(data, sampleRate = 44100, startFrame = 0) {
|
|
17
|
+
const fileData = parseMidiFile(data, sampleRate);
|
|
18
|
+
const clips = [];
|
|
19
|
+
const regions = [];
|
|
20
|
+
for (let i = 0; i < fileData.tracks.length; i++) {
|
|
21
|
+
const track = fileData.tracks[i];
|
|
22
|
+
// Skip empty tracks (common in Type 1 — track 0 is often tempo-only)
|
|
23
|
+
if (track.notes.length === 0)
|
|
24
|
+
continue;
|
|
25
|
+
const clipId = crypto.randomUUID();
|
|
26
|
+
const regionId = crypto.randomUUID();
|
|
27
|
+
// Create MidiNotes from parsed data
|
|
28
|
+
const midiNotes = track.notes.map(n => {
|
|
29
|
+
return new MidiNote(crypto.randomUUID(), n.pitch, n.velocity, n.startFrame, n.durationFrames, n.channel);
|
|
30
|
+
});
|
|
31
|
+
// Create MidiClip
|
|
32
|
+
const clip = new MidiClip(clipId, track.name, midiNotes);
|
|
33
|
+
clips.push(clip);
|
|
34
|
+
// Create MidiRegion
|
|
35
|
+
const clipDuration = clip.getDuration();
|
|
36
|
+
const region = new MidiRegion(regionId, track.name, startFrame, clipDuration);
|
|
37
|
+
// Add notes to region
|
|
38
|
+
for (const note of midiNotes) {
|
|
39
|
+
// Clone notes for the region (they're separate from clip notes)
|
|
40
|
+
const regionNote = new MidiNote(crypto.randomUUID(), note.pitch, note.velocity, note.startFrame, note.durationFrames, note.channel);
|
|
41
|
+
region.addNote(regionNote);
|
|
42
|
+
}
|
|
43
|
+
regions.push(region);
|
|
44
|
+
}
|
|
45
|
+
return { fileData, clips, regions };
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Export MidiRegions to MIDI file data for the writer.
|
|
49
|
+
*/
|
|
50
|
+
export function exportMidiRegions(regions, sampleRate = 44100) {
|
|
51
|
+
return regions.map(region => ({
|
|
52
|
+
name: region.name,
|
|
53
|
+
notes: region.getNotes().map(n => ({
|
|
54
|
+
pitch: n.pitch,
|
|
55
|
+
velocity: n.velocity,
|
|
56
|
+
channel: n.channel,
|
|
57
|
+
startFrame: n.startFrame,
|
|
58
|
+
durationFrames: n.durationFrames,
|
|
59
|
+
})),
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=MidiImporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MidiImporter.js","sourceRoot":"","sources":["../../src/midi/MidiImporter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Signal } from '../lib/Signal';
|
|
2
|
+
import { Plugin } from './Plugin';
|
|
3
|
+
/**
|
|
4
|
+
* A named snapshot of plugin parameter values that can be saved, recalled,
|
|
5
|
+
* and applied to a compatible plugin instance.
|
|
6
|
+
*/
|
|
7
|
+
export interface PluginPreset {
|
|
8
|
+
/** Unique preset identifier. */
|
|
9
|
+
id: string;
|
|
10
|
+
/** Human-readable preset name. */
|
|
11
|
+
name: string;
|
|
12
|
+
/** Descriptor ID of the plugin this preset targets (e.g. 'internal-reverb'). */
|
|
13
|
+
pluginId: string;
|
|
14
|
+
/** Optional version string the preset was created against. */
|
|
15
|
+
pluginVersion?: string;
|
|
16
|
+
/** Parameter name -> value map. */
|
|
17
|
+
parameters: Record<string, number>;
|
|
18
|
+
/** Arbitrary key/value metadata (author, notes, tags, etc.). */
|
|
19
|
+
metadata?: Record<string, string>;
|
|
20
|
+
/** Whether this preset ships with the application (factory) or was
|
|
21
|
+
* created by the user. */
|
|
22
|
+
isFactory: boolean;
|
|
23
|
+
/** ISO-8601 creation timestamp. */
|
|
24
|
+
createdAt: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Manages plugin presets across all plugin types.
|
|
28
|
+
*
|
|
29
|
+
* Presets are organized by `pluginId` so they can be quickly looked up when
|
|
30
|
+
* a user opens a plugin UI. The manager distinguishes between factory
|
|
31
|
+
* presets (read-only, shipped with the application) and user presets.
|
|
32
|
+
*
|
|
33
|
+
* Signal-based notification is provided via `presetsChanged`, which emits
|
|
34
|
+
* the affected `pluginId` whenever presets are added, removed, or updated.
|
|
35
|
+
*/
|
|
36
|
+
export declare class PluginPresetManager {
|
|
37
|
+
/** pluginId -> ordered array of presets */
|
|
38
|
+
private _presets;
|
|
39
|
+
/**
|
|
40
|
+
* Emitted whenever presets change for a given plugin. The payload is the
|
|
41
|
+
* affected `pluginId`.
|
|
42
|
+
*/
|
|
43
|
+
readonly presetsChanged: Signal<string>;
|
|
44
|
+
/**
|
|
45
|
+
* Save a new user preset for the specified plugin.
|
|
46
|
+
*
|
|
47
|
+
* @returns The newly created preset (with generated `id` and timestamp).
|
|
48
|
+
*/
|
|
49
|
+
savePreset(pluginId: string, name: string, parameters: Record<string, number>, metadata?: Record<string, string>, pluginVersion?: string): PluginPreset;
|
|
50
|
+
/**
|
|
51
|
+
* Delete a preset by its unique ID. Factory presets cannot be deleted.
|
|
52
|
+
*
|
|
53
|
+
* @returns `true` if the preset was found and deleted.
|
|
54
|
+
*/
|
|
55
|
+
deletePreset(presetId: string): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Get all presets (factory + user) for a given plugin type.
|
|
58
|
+
*/
|
|
59
|
+
getPresetsForPlugin(pluginId: string): PluginPreset[];
|
|
60
|
+
/**
|
|
61
|
+
* Get a single preset by its unique ID, searching across all plugins.
|
|
62
|
+
*/
|
|
63
|
+
getPreset(presetId: string): PluginPreset | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* Apply a preset to a live Plugin instance by calling `setParameter()`
|
|
66
|
+
* for each stored parameter value.
|
|
67
|
+
*/
|
|
68
|
+
applyPreset(presetId: string, plugin: Plugin): void;
|
|
69
|
+
/**
|
|
70
|
+
* Register one or more factory presets for a plugin type.
|
|
71
|
+
*
|
|
72
|
+
* Factory presets are read-only and are prepended to the preset list so
|
|
73
|
+
* they appear before user presets in UIs.
|
|
74
|
+
*/
|
|
75
|
+
registerFactoryPresets(pluginId: string, presets: Omit<PluginPreset, 'id' | 'createdAt' | 'isFactory'>[]): void;
|
|
76
|
+
/**
|
|
77
|
+
* Serialize all presets (factory + user) to a JSON string.
|
|
78
|
+
*/
|
|
79
|
+
serialize(): string;
|
|
80
|
+
/**
|
|
81
|
+
* Restore a PluginPresetManager from a JSON string produced by
|
|
82
|
+
* `serialize()`.
|
|
83
|
+
*/
|
|
84
|
+
static deserialize(json: string): PluginPresetManager;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=PluginPresetManager.d.ts.map
|