@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
package/README.md
CHANGED
|
@@ -1,316 +1,734 @@
|
|
|
1
|
-
#
|
|
1
|
+
# drop-daw
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Headless DAW (Digital Audio Workstation) engine for TypeScript/JavaScript. Provides domain models, command system, audio engine, automation, plugins, and MIDI — all with zero browser or framework dependencies.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
```bash
|
|
6
|
+
npm install drop-daw
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Features
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+
- **Headless** — No browser, no React, no framework required. Runs in Node.js, Electron, or any JS runtime.
|
|
12
|
+
- **Command Pattern** — All mutations go through `CommandExecutor` with built-in Undo/Redo.
|
|
13
|
+
- **Dependency Injection** — Bring your own audio backend via the `AudioProvider` interface.
|
|
14
|
+
- **60+ Commands** — Transport, tracks, regions, automation, plugins, MIDI, markers, ranges, export.
|
|
15
|
+
- **20+ Built-in Plugins** — EQ, compressor, reverb, delay, saturation, de-esser, and more.
|
|
16
|
+
- **Signal-based Events** — Type-safe reactive event system (Qt Signal/Slot pattern).
|
|
17
|
+
- **Serialization** — Full session save/load with JSON snapshots.
|
|
18
|
+
- **TypeScript-first** — Complete type definitions included.
|
|
8
19
|
|
|
9
|
-
|
|
10
|
-
- **Zustand 커플링**: `EditorHandler`, `defaultActions`, `dawQueryTool`이 `useDAWStore`를 직접 import
|
|
11
|
-
- **테스트 격리 불가**: Node.js CLI나 headless 환경에서 코어 로직만 테스트하기 어려움
|
|
12
|
-
- **재사용 불가**: 다른 프레임워크(Electron, React Native 등)에서 코어 로직 사용 불가
|
|
20
|
+
---
|
|
13
21
|
|
|
14
|
-
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import {
|
|
26
|
+
AudioEngine,
|
|
27
|
+
Session,
|
|
28
|
+
Track,
|
|
29
|
+
TrackType,
|
|
30
|
+
CommandExecutor,
|
|
31
|
+
CommandType,
|
|
32
|
+
} from 'drop-daw';
|
|
33
|
+
|
|
34
|
+
// 1. Implement AudioProvider interface for your platform
|
|
35
|
+
class MyAudioBackend implements AudioProvider {
|
|
36
|
+
// ... implement the interface methods
|
|
37
|
+
}
|
|
15
38
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
- 향후 npm publish 가능
|
|
39
|
+
// 2. Initialize the engine with your backend
|
|
40
|
+
const engine = AudioEngine.getInstance(new MyAudioBackend());
|
|
19
41
|
|
|
20
|
-
|
|
42
|
+
// 3. Work with the session directly
|
|
43
|
+
const track = engine.session.addTrack('Vocals', TrackType.AUDIO);
|
|
21
44
|
|
|
45
|
+
// 4. Or use the command system (with undo/redo support)
|
|
46
|
+
await CommandExecutor.getInstance().execute({
|
|
47
|
+
type: CommandType.ADD_TRACK,
|
|
48
|
+
payload: { name: 'Guitar', trackType: 'audio' },
|
|
49
|
+
});
|
|
22
50
|
```
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
│ │ │ │ useDAWStore │ │ - EditorHandler │ │
|
|
29
|
-
│ │ │ │ │ │ - defaultActions │ │
|
|
30
|
-
│ │ │ │ │ │ - dawQueryTool │ │
|
|
31
|
-
│ └────┬─────┘ └──────┬───────┘ └──────────┬───────────┘ │
|
|
32
|
-
│ │ │ │ │
|
|
33
|
-
│ └──────────────┼─────────────────────┘ │
|
|
34
|
-
│ │ │
|
|
35
|
-
├──────────────────────┼───────────────────────────────────┤
|
|
36
|
-
│ ▼ │
|
|
37
|
-
│ @drop-ai/core (packages/core/) │
|
|
38
|
-
│ ┌────────┐ ┌────────┐ ┌──────────┐ ┌────────────────┐ │
|
|
39
|
-
│ │Domain │ │Commands│ │Audio │ │Automation │ │
|
|
40
|
-
│ │Session │ │Executor│ │Engine │ │List, Curve, │ │
|
|
41
|
-
│ │Track │ │History │ │Backend(I)│ │Mode │ │
|
|
42
|
-
│ │Region │ │55 Impls│ │Exporter │ └────────────────┘ │
|
|
43
|
-
│ │Playlist│ │15 Hdlrs│ │Meter │ ┌────────────────┐ │
|
|
44
|
-
│ │Source │ └────────┘ └──────────┘ │Plugins │ │
|
|
45
|
-
│ │Range │ ┌────────┐ ┌──────────┐ │EQ, Reverb, │ │
|
|
46
|
-
│ │Marker │ │Lib │ │Processing│ │Compressor, ... │ │
|
|
47
|
-
│ │MIDI │ │Signal │ │Gain, Pan │ └────────────────┘ │
|
|
48
|
-
│ └────────┘ └────────┘ └──────────┘ │
|
|
49
|
-
└─────────────────────────────────────────────────────────┘
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Architecture
|
|
55
|
+
|
|
50
56
|
```
|
|
57
|
+
┌──────────────────────────────────────────────────┐
|
|
58
|
+
│ Your App │
|
|
59
|
+
│ (React, Electron, Node.js CLI, etc.) │
|
|
60
|
+
│ │
|
|
61
|
+
│ ┌─────────────┐ ┌────────────────────────────┐ │
|
|
62
|
+
│ │ UI Layer │ │ AudioProvider (impl) │ │
|
|
63
|
+
│ │ (optional) │ │ e.g. Web Audio, PortAudio │ │
|
|
64
|
+
│ └──────┬──────┘ └─────────────┬──────────────┘ │
|
|
65
|
+
├─────────┼───────────────────────┼────────────────┤
|
|
66
|
+
│ ▼ ▼ │
|
|
67
|
+
│ ┌─────────────┐ ┌────────────────────────────┐ │
|
|
68
|
+
│ │ Command │ │ AudioEngine │ │
|
|
69
|
+
│ │ Executor │ │ (session + backend) │ │
|
|
70
|
+
│ └──────┬──────┘ └────────────────────────────┘ │
|
|
71
|
+
│ ▼ │
|
|
72
|
+
│ ┌───────────────────────────────────────┐ │
|
|
73
|
+
│ │ Domain Layer │ │
|
|
74
|
+
│ │ Session → Track → Playlist → Region │ │
|
|
75
|
+
│ │ Automation, Plugins, MIDI, Markers │ │
|
|
76
|
+
│ └───────────────────────────────────────┘ │
|
|
77
|
+
│ │
|
|
78
|
+
│ drop-daw │
|
|
79
|
+
└──────────────────────────────────────────────────┘
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Core Concepts
|
|
85
|
+
|
|
86
|
+
### Session
|
|
87
|
+
|
|
88
|
+
The root container for a DAW project. Manages tracks, sources, markers, ranges, tempo, and transport state.
|
|
51
89
|
|
|
52
|
-
## 모듈 구조 (193 파일)
|
|
53
|
-
|
|
54
|
-
### `domain/` — 도메인 모델 (42 파일)
|
|
55
|
-
|
|
56
|
-
DAW의 핵심 데이터 구조. Ardour의 도메인 모델을 참고하여 설계됨.
|
|
57
|
-
|
|
58
|
-
| 클래스 | 설명 |
|
|
59
|
-
|--------|------|
|
|
60
|
-
| `Session` | 프로젝트 루트. 트랙, 소스, 마커, 레인지, 템포맵 관리 |
|
|
61
|
-
| `Track` | 오디오/MIDI/Bus/AUX 트랙. Route(IO + 프로세서 체인) 포함 |
|
|
62
|
-
| `Region` | 타임라인 위의 오디오 구간. start, length, sourceStart, fades |
|
|
63
|
-
| `Playlist` | 트랙 내 Region 컬렉션. 레이어, 크로스페이드 지원 |
|
|
64
|
-
| `Source` | 오디오 파일 참조. URL, duration, sampleRate |
|
|
65
|
-
| `Range` | 루프/펀치/선택 범위 (start, end) |
|
|
66
|
-
| `Marker` | 타임라인 마커 (위치 + 이름) |
|
|
67
|
-
| `MidiNote` / `MidiRegion` | MIDI 데이터 |
|
|
68
|
-
| `SendBus` | 센드 버스 (pre/post fader) |
|
|
69
|
-
| `GridSettings` | 그리드 타입, 스냅 모드 |
|
|
70
|
-
| `TempoMap` | BPM 변경 이벤트 관리 |
|
|
71
|
-
| `CrossfadeEngine` | 리전 겹침 시 자동 크로스페이드 계산 |
|
|
72
|
-
| `TransportFSM` | 재생/정지/녹음 상태 머신 |
|
|
73
|
-
| `MixerScene` | 믹서 스냅샷 저장/불러오기 |
|
|
74
|
-
|
|
75
|
-
모든 도메인 객체는 `Signal` 기반 이벤트를 발행한다:
|
|
76
90
|
```typescript
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
91
|
+
import { Session, TrackType } from 'drop-daw';
|
|
92
|
+
|
|
93
|
+
const session = new Session('My Song', undefined, 44100);
|
|
94
|
+
|
|
95
|
+
// Tracks
|
|
96
|
+
const vocal = session.addTrack('Vocals', TrackType.AUDIO);
|
|
97
|
+
const bass = session.addTrack('Bass', TrackType.AUDIO);
|
|
98
|
+
const synth = session.addTrack('Synth', TrackType.MIDI);
|
|
99
|
+
const reverb = session.addAuxTrack('Reverb Bus');
|
|
100
|
+
|
|
101
|
+
// Transport
|
|
102
|
+
session.setTempo(120);
|
|
103
|
+
session.setTimeSignature(4, 4);
|
|
104
|
+
|
|
105
|
+
// Markers
|
|
106
|
+
session.addMarker('Chorus', 44100 * 30); // at 30 seconds
|
|
107
|
+
|
|
108
|
+
// Ranges
|
|
109
|
+
const loopRange = session.addRange('Loop A', 44100 * 10, 44100 * 20);
|
|
110
|
+
session.setLoopRange(loopRange.id);
|
|
111
|
+
session.setLoopEnabled(true);
|
|
112
|
+
|
|
113
|
+
// Serialization
|
|
114
|
+
const snapshot = session.toJSON();
|
|
115
|
+
const restored = Session.fromJSON(snapshot);
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Track
|
|
119
|
+
|
|
120
|
+
Represents an audio, MIDI, aux, bus, folder, or VCA track. Each track has a `Route` (signal chain) and a `Playlist` (region container).
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
const track = session.addTrack('Lead Guitar', TrackType.AUDIO);
|
|
124
|
+
|
|
125
|
+
track.setMute(false);
|
|
126
|
+
track.setSolo(true);
|
|
127
|
+
track.setArmed(true);
|
|
128
|
+
track.setColor('#ff6b6b');
|
|
129
|
+
track.setMonitorMode(MonitorMode.AUTO);
|
|
130
|
+
|
|
131
|
+
// React to changes
|
|
132
|
+
track.muteChanged.connect((muted) => console.log('Mute:', muted));
|
|
133
|
+
track.soloChanged.connect((soloed) => console.log('Solo:', soloed));
|
|
80
134
|
```
|
|
81
135
|
|
|
82
|
-
|
|
136
|
+
**Track Types:**
|
|
83
137
|
|
|
84
|
-
|
|
138
|
+
| Type | Description |
|
|
139
|
+
|------|-------------|
|
|
140
|
+
| `AUDIO` | Standard audio track with region playback |
|
|
141
|
+
| `MIDI` | MIDI track with note data and virtual instruments |
|
|
142
|
+
| `AUX` | Auxiliary bus for send effects (reverb, delay) |
|
|
143
|
+
| `BUS` | Mix bus for subgroup routing |
|
|
144
|
+
| `FOLDER` | Folder track for organizing child tracks |
|
|
145
|
+
| `VCA` | VCA fader for linked level control |
|
|
85
146
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
- **55개 커맨드 구현체**: AddTrack, MoveRegion, SplitAtPlayhead, SetTrackVolume 등
|
|
147
|
+
### Region
|
|
148
|
+
|
|
149
|
+
A segment of audio on the timeline, referencing a `Source`.
|
|
90
150
|
|
|
91
151
|
```typescript
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
152
|
+
import { Region } from 'drop-daw';
|
|
153
|
+
|
|
154
|
+
const region = new Region(
|
|
155
|
+
'region-1', // id
|
|
156
|
+
'source-1', // sourceId
|
|
157
|
+
0, // start (frames)
|
|
158
|
+
44100 * 10, // length (10 seconds at 44.1kHz)
|
|
159
|
+
0, // sourceStart
|
|
160
|
+
'Vocal Take 1' // name
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
region.setFadeIn(4410); // 100ms fade in
|
|
164
|
+
region.setFadeOut(4410); // 100ms fade out
|
|
165
|
+
region.move(44100 * 5); // move to 5 seconds
|
|
166
|
+
region.resize(44100 * 8); // resize to 8 seconds
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### AudioEngine
|
|
170
|
+
|
|
171
|
+
Central controller connecting the session to an audio backend. Uses the singleton pattern with dependency injection.
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
import { AudioEngine, AudioProvider } from 'drop-daw';
|
|
175
|
+
|
|
176
|
+
// Initialize with your backend implementation
|
|
177
|
+
const engine = AudioEngine.getInstance(myBackend);
|
|
178
|
+
await engine.initialize();
|
|
179
|
+
|
|
180
|
+
// Transport
|
|
181
|
+
await engine.start();
|
|
182
|
+
engine.pause();
|
|
183
|
+
engine.seek(10.5); // seek to 10.5 seconds
|
|
184
|
+
engine.stop();
|
|
185
|
+
|
|
186
|
+
// Metering
|
|
187
|
+
const meter = engine.getMeterData('track-1');
|
|
188
|
+
console.log('Peak:', meter.peak, 'RMS:', meter.rms);
|
|
189
|
+
|
|
190
|
+
// Export
|
|
191
|
+
await engine.exportAudio(engine.getExportConfig(), engine.getExportStatus());
|
|
192
|
+
|
|
193
|
+
// Swap backend at runtime
|
|
194
|
+
engine.setBackend(new DifferentBackend());
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### AudioProvider Interface
|
|
198
|
+
|
|
199
|
+
Implement this interface to connect drop-daw to any audio system.
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
import { AudioProvider } from 'drop-daw';
|
|
203
|
+
|
|
204
|
+
class WebAudioBackend implements AudioProvider {
|
|
205
|
+
async initialize(): Promise<void> { /* ... */ }
|
|
206
|
+
|
|
207
|
+
// Transport
|
|
208
|
+
start(): void { /* ... */ }
|
|
209
|
+
stop(): void { /* ... */ }
|
|
210
|
+
pause(): void { /* ... */ }
|
|
211
|
+
seek(time: number): void { /* ... */ }
|
|
212
|
+
|
|
213
|
+
// Track management
|
|
214
|
+
createTrack(trackId, name, inputId, outputId): void { /* ... */ }
|
|
215
|
+
deleteTrack(trackId): void { /* ... */ }
|
|
216
|
+
|
|
217
|
+
// Region scheduling
|
|
218
|
+
scheduleRegion(trackId, region): void { /* ... */ }
|
|
219
|
+
updateRegions(trackId, regions): void { /* ... */ }
|
|
220
|
+
removeRegion(trackId, regionId): void { /* ... */ }
|
|
221
|
+
|
|
222
|
+
// Metering
|
|
223
|
+
getMeterData(trackId): MeterData { /* ... */ }
|
|
224
|
+
getMasterMeterData(): MeterData { /* ... */ }
|
|
225
|
+
|
|
226
|
+
// ... and more (see full interface in source)
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
**Included backend examples (in the drop.ai app, not in this package):**
|
|
231
|
+
|
|
232
|
+
| Backend | Environment | Description |
|
|
233
|
+
|---------|-------------|-------------|
|
|
234
|
+
| `ToneAudioProvider` | Browser | Tone.js + Web Audio API |
|
|
235
|
+
| `HeadlessAudioProvider` | Node.js | No-op backend for CLI/testing |
|
|
236
|
+
| `MockAudioProvider` | Test | Mock backend for unit tests |
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Command System
|
|
241
|
+
|
|
242
|
+
All state mutations can go through the command system, providing validation (via Zod), handler routing, and undo/redo support.
|
|
243
|
+
|
|
244
|
+
### Executing Commands
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
import { CommandExecutor, CommandType } from 'drop-daw';
|
|
248
|
+
|
|
249
|
+
const executor = CommandExecutor.getInstance();
|
|
250
|
+
|
|
251
|
+
// Add a track
|
|
252
|
+
await executor.execute({
|
|
253
|
+
type: CommandType.ADD_TRACK,
|
|
254
|
+
payload: { name: 'Vocals', trackType: 'audio' },
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
// Set volume
|
|
258
|
+
await executor.execute({
|
|
259
|
+
type: CommandType.SET_VOLUME,
|
|
260
|
+
payload: { trackId: 'track-1', volume: 0.75 },
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// Split region at playhead
|
|
264
|
+
await executor.execute({
|
|
265
|
+
type: CommandType.SPLIT_AT_PLAYHEAD,
|
|
266
|
+
payload: { trackId: 'track-1', frame: 44100 * 15 },
|
|
96
267
|
});
|
|
97
268
|
|
|
98
|
-
//
|
|
99
|
-
|
|
269
|
+
// Add a plugin
|
|
270
|
+
await executor.execute({
|
|
271
|
+
type: CommandType.ADD_PLUGIN,
|
|
272
|
+
payload: { trackId: 'track-1', pluginId: 'internal-eq6' },
|
|
273
|
+
});
|
|
100
274
|
```
|
|
101
275
|
|
|
102
|
-
###
|
|
276
|
+
### Undo / Redo
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
const history = executor.history;
|
|
280
|
+
|
|
281
|
+
await history.undo();
|
|
282
|
+
await history.redo();
|
|
283
|
+
await history.undoMultiple(3); // undo 3 steps
|
|
103
284
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
- **`PlaylistEngine`**: 샘플 정확도 리전 렌더링 (AudioWorklet용)
|
|
285
|
+
console.log(history.canUndo); // true
|
|
286
|
+
console.log(history.nextUndoLabel); // "Add Track"
|
|
287
|
+
|
|
288
|
+
// Transaction: group multiple commands into one undo step
|
|
289
|
+
history.beginTransaction('Move and resize');
|
|
290
|
+
// ... execute multiple commands ...
|
|
291
|
+
await history.commitTransaction();
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Registering Custom Handlers
|
|
115
295
|
|
|
116
296
|
```typescript
|
|
117
|
-
|
|
118
|
-
|
|
297
|
+
import { CommandHandler, CommandResult } from 'drop-daw';
|
|
298
|
+
|
|
299
|
+
class MyCustomHandler implements CommandHandler {
|
|
300
|
+
readonly handledTypes = ['MY_CUSTOM_COMMAND'];
|
|
301
|
+
|
|
302
|
+
async execute(type: string, payload: any): Promise<CommandResult> {
|
|
303
|
+
// your logic here
|
|
304
|
+
return { success: true };
|
|
305
|
+
}
|
|
306
|
+
}
|
|
119
307
|
|
|
120
|
-
|
|
121
|
-
const engine = AudioEngine.getInstance(new HeadlessAudioProvider());
|
|
308
|
+
CommandExecutor.getInstance().registerHandler(new MyCustomHandler());
|
|
122
309
|
```
|
|
123
310
|
|
|
124
|
-
###
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
`PluginManager`로 등록/생성:
|
|
311
|
+
### Command Types
|
|
312
|
+
|
|
313
|
+
**Transport:** `PLAY`, `PAUSE`, `STOP`, `SEEK`, `SET_TEMPO`, `SET_TIME_SIGNATURE`, `TOGGLE_METRONOME`, `START_RECORDING`, `STOP_RECORDING`
|
|
314
|
+
|
|
315
|
+
**Tracks:** `ADD_TRACK`, `REMOVE_TRACK`, `SET_VOLUME`, `SET_PAN`, `MUTE_TRACK`, `SOLO_TRACK`, `ARM_TRACK`, `SET_TRACK_MONITOR`
|
|
316
|
+
|
|
317
|
+
**Regions:** `ADD_REGION`, `REMOVE_REGION`, `MOVE_REGION`, `RESIZE_REGION`, `SPLIT_AT_PLAYHEAD`, `DUPLICATE_REGION`, `COPY_REGION`, `PASTE_REGION`, `MERGE_REGIONS`, `TRIM_REGION`, `SET_REGION_FADES`, `REVERSE_REGION`, `NORMALIZE_REGION`, `STRIP_SILENCE`, `TIME_STRETCH_REGION`, `LOCK_REGION`, `GROUP_REGIONS`, `UNGROUP_REGIONS`
|
|
318
|
+
|
|
319
|
+
**Automation:** `ADD_AUTOMATION`, `MOVE_AUTOMATION_POINT`, `REMOVE_AUTOMATION_POINT`
|
|
320
|
+
|
|
321
|
+
**Plugins:** `ADD_PLUGIN`, `REMOVE_PLUGIN`, `SET_PLUGIN_PARAMETER`
|
|
322
|
+
|
|
323
|
+
**Ranges:** `ADD_RANGE`, `REMOVE_RANGE`, `SET_RANGE`, `SET_LOOP_RANGE`, `SET_PUNCH_RANGE`, `TOGGLE_LOOP`
|
|
324
|
+
|
|
325
|
+
**Markers:** `ADD_MARKER`, `REMOVE_MARKER`, `MOVE_MARKER`
|
|
326
|
+
|
|
327
|
+
**IO/Routing:** `CONNECT_IO`, `DISCONNECT_IO`, `ADD_SEND_BUS`, `REMOVE_SEND_BUS`, `SET_SEND_LEVEL`
|
|
328
|
+
|
|
329
|
+
**Session:** `EXPORT`, `NEW_SESSION`, `LOAD_SESSION`, `SAVE_SESSION`, `SAVE_SNAPSHOT`
|
|
330
|
+
|
|
331
|
+
**History:** `UNDO`, `REDO`, `SELECTION_UNDO`, `SELECTION_REDO`
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Plugins
|
|
336
|
+
|
|
337
|
+
20+ built-in audio effect plugins, managed by `PluginManager`.
|
|
338
|
+
|
|
153
339
|
```typescript
|
|
340
|
+
import { PluginManager } from 'drop-daw';
|
|
341
|
+
|
|
154
342
|
const manager = PluginManager.getInstance();
|
|
155
|
-
|
|
343
|
+
|
|
344
|
+
// List all available plugins
|
|
345
|
+
const plugins = manager.getAvailablePlugins();
|
|
346
|
+
plugins.forEach(p => console.log(p.id, p.name));
|
|
347
|
+
|
|
348
|
+
// Create a plugin instance
|
|
349
|
+
const eq = manager.createPlugin('internal-eq6');
|
|
350
|
+
eq.setParameter('band1-freq', 100);
|
|
351
|
+
eq.setParameter('band1-gain', 3.0);
|
|
352
|
+
|
|
353
|
+
// Get/set full state (presets)
|
|
354
|
+
const state = eq.getState();
|
|
355
|
+
eq.setState(state);
|
|
356
|
+
|
|
357
|
+
// React to parameter changes
|
|
358
|
+
eq.parameterChanged.connect(({ id, value }) => {
|
|
359
|
+
console.log(`${id} changed to ${value}`);
|
|
360
|
+
});
|
|
156
361
|
```
|
|
157
362
|
|
|
158
|
-
###
|
|
363
|
+
### Available Plugins
|
|
364
|
+
|
|
365
|
+
| ID | Name | Description |
|
|
366
|
+
|----|------|-------------|
|
|
367
|
+
| `internal-eq6` | Parametric EQ | 6-band parametric equalizer |
|
|
368
|
+
| `internal-compressor` | Compressor | Dynamics compressor |
|
|
369
|
+
| `internal-multiband-comp` | Multiband Compressor | Multi-band dynamics |
|
|
370
|
+
| `internal-expander` | Expander | Expander/gate |
|
|
371
|
+
| `internal-gate` | Gate | Noise gate |
|
|
372
|
+
| `internal-deesser` | De-Esser | Sibilance reduction |
|
|
373
|
+
| `internal-reverb` | Reverb | Algorithmic reverb |
|
|
374
|
+
| `internal-convolver` | Convolution Reverb | IR-based reverb |
|
|
375
|
+
| `internal-delay` | Delay | Simple delay |
|
|
376
|
+
| `internal-sync-delay` | Sync Delay | BPM-synced delay |
|
|
377
|
+
| `internal-chorus` | Chorus | Chorus effect |
|
|
378
|
+
| `internal-phaser` | Phaser | Phaser effect |
|
|
379
|
+
| `internal-tremolo` | Tremolo | Tremolo effect |
|
|
380
|
+
| `internal-vibrato` | Vibrato | Vibrato effect |
|
|
381
|
+
| `internal-autopan` | Auto Pan | Automatic panning |
|
|
382
|
+
| `internal-distortion` | Distortion | Distortion/overdrive |
|
|
383
|
+
| `internal-tape-sat` | Tape Saturation | Analog tape emulation |
|
|
384
|
+
| `internal-filter` | Filter | Multi-mode filter |
|
|
385
|
+
| `internal-eq3` | 3-Band EQ | Simple 3-band EQ |
|
|
386
|
+
| `internal-gain` | Gain | Utility gain |
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## Automation
|
|
391
|
+
|
|
392
|
+
Per-parameter automation with multiple recording modes.
|
|
393
|
+
|
|
394
|
+
```typescript
|
|
395
|
+
import { AutomationList, AutomationMode } from 'drop-daw';
|
|
159
396
|
|
|
160
|
-
|
|
397
|
+
const automation = new AutomationList();
|
|
161
398
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
- `MeterProcessor` (피크/RMS 미터링)
|
|
167
|
-
- `PluginInsert` (플러그인 삽입 슬롯)
|
|
399
|
+
// Add points
|
|
400
|
+
automation.addPoint(0, 0.5); // value 0.5 at time 0
|
|
401
|
+
automation.addPoint(2.0, 1.0); // value 1.0 at time 2s
|
|
402
|
+
automation.addPoint(4.0, 0.0); // value 0.0 at time 4s
|
|
168
403
|
|
|
169
|
-
|
|
404
|
+
// Read interpolated value
|
|
405
|
+
const value = automation.getValueAt(1.0); // ~0.75 (linear interpolation)
|
|
170
406
|
|
|
171
|
-
|
|
407
|
+
// Modes
|
|
408
|
+
automation.mode = AutomationMode.READ; // playback only
|
|
409
|
+
automation.mode = AutomationMode.WRITE; // overwrite
|
|
410
|
+
automation.mode = AutomationMode.TOUCH; // write while touching, snap back on release
|
|
411
|
+
automation.mode = AutomationMode.LATCH; // write while touching, hold last value
|
|
412
|
+
|
|
413
|
+
// Range operations
|
|
414
|
+
const copied = automation.copy(1.0, 3.0);
|
|
415
|
+
automation.paste(copied, 5.0);
|
|
416
|
+
automation.eraseRange(1.0, 3.0);
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## Signal (Event System)
|
|
422
|
+
|
|
423
|
+
Type-safe event emitter inspired by Qt's Signal/Slot pattern. Used throughout the domain layer.
|
|
172
424
|
|
|
173
425
|
```typescript
|
|
426
|
+
import { Signal } from 'drop-daw';
|
|
427
|
+
|
|
174
428
|
const signal = new Signal<number>();
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
sub.
|
|
429
|
+
|
|
430
|
+
// Subscribe
|
|
431
|
+
const sub = signal.connect((value) => {
|
|
432
|
+
console.log('Received:', value);
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
// Emit
|
|
436
|
+
signal.emit(42); // logs "Received: 42"
|
|
437
|
+
|
|
438
|
+
// Unsubscribe
|
|
439
|
+
sub.dispose();
|
|
440
|
+
|
|
441
|
+
// Or disconnect by reference
|
|
442
|
+
const handler = (v: number) => console.log(v);
|
|
443
|
+
signal.connect(handler);
|
|
444
|
+
signal.disconnect(handler);
|
|
445
|
+
|
|
446
|
+
// Clear all listeners
|
|
447
|
+
signal.clear();
|
|
178
448
|
```
|
|
179
449
|
|
|
180
|
-
###
|
|
450
|
+
### Common Signals
|
|
181
451
|
|
|
182
|
-
|
|
452
|
+
```typescript
|
|
453
|
+
// Session
|
|
454
|
+
session.trackAdded.connect((track) => { /* ... */ });
|
|
455
|
+
session.trackRemoved.connect((trackId) => { /* ... */ });
|
|
456
|
+
session.tempoChanged.connect((bpm) => { /* ... */ });
|
|
457
|
+
session.playingChanged.connect((isPlaying) => { /* ... */ });
|
|
458
|
+
session.selectionChanged.connect((selectedIds) => { /* ... */ });
|
|
459
|
+
session.markerAdded.connect((marker) => { /* ... */ });
|
|
460
|
+
|
|
461
|
+
// Track
|
|
462
|
+
track.muteChanged.connect((muted) => { /* ... */ });
|
|
463
|
+
track.soloChanged.connect((soloed) => { /* ... */ });
|
|
464
|
+
track.armChanged.connect((armed) => { /* ... */ });
|
|
465
|
+
|
|
466
|
+
// CommandExecutor
|
|
467
|
+
executor.commandExecuted.connect(({ type, payload }) => { /* ... */ });
|
|
468
|
+
|
|
469
|
+
// CommandHistory
|
|
470
|
+
history.historyChanged.connect(() => { /* ... */ });
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
---
|
|
183
474
|
|
|
184
|
-
|
|
185
|
-
|
|
475
|
+
## Actions & Key Bindings
|
|
476
|
+
|
|
477
|
+
Map keyboard shortcuts to commands.
|
|
186
478
|
|
|
187
479
|
```typescript
|
|
480
|
+
import { ActionRegistry, ActionCategory } from 'drop-daw';
|
|
481
|
+
|
|
188
482
|
const registry = ActionRegistry.getInstance();
|
|
189
|
-
registry.registerDefaults(defaultActions); // 앱에서 등록
|
|
190
|
-
await registry.execute('transport.togglePlay');
|
|
191
|
-
```
|
|
192
483
|
|
|
193
|
-
|
|
484
|
+
// Register default actions (typically done at app bootstrap)
|
|
485
|
+
registry.registerDefaults([
|
|
486
|
+
{
|
|
487
|
+
id: 'transport.play',
|
|
488
|
+
label: 'Play/Pause',
|
|
489
|
+
category: ActionCategory.TRANSPORT,
|
|
490
|
+
defaultKey: 'Space',
|
|
491
|
+
commandFactory: () => ({ type: CommandType.PLAY, payload: {} }),
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
id: 'edit.undo',
|
|
495
|
+
label: 'Undo',
|
|
496
|
+
category: ActionCategory.EDIT,
|
|
497
|
+
defaultKey: 'Ctrl+Z',
|
|
498
|
+
commandFactory: () => ({ type: CommandType.UNDO, payload: {} }),
|
|
499
|
+
},
|
|
500
|
+
]);
|
|
501
|
+
|
|
502
|
+
// Execute by action ID
|
|
503
|
+
await registry.execute('transport.play');
|
|
504
|
+
|
|
505
|
+
// Query
|
|
506
|
+
const actions = registry.getActionsByCategory();
|
|
507
|
+
const key = registry.getEffectiveKey('transport.play'); // 'Space'
|
|
508
|
+
```
|
|
194
509
|
|
|
195
|
-
|
|
196
|
-
- `KeyBindings`: 커스텀 키 바인딩 오버라이드
|
|
510
|
+
### Custom Key Bindings
|
|
197
511
|
|
|
198
|
-
|
|
512
|
+
```typescript
|
|
513
|
+
import { KeyBindings } from 'drop-daw';
|
|
199
514
|
|
|
200
|
-
|
|
201
|
-
- `AutoSave`: 주기적 자동 저장
|
|
202
|
-
- `SessionTemplate`: 새 세션 템플릿
|
|
515
|
+
const bindings = KeyBindings.getInstance();
|
|
203
516
|
|
|
204
|
-
|
|
517
|
+
bindings.setBinding('transport.play', 'Enter');
|
|
518
|
+
bindings.getBinding('transport.play'); // 'Enter'
|
|
519
|
+
bindings.removeBinding('transport.play'); // back to default
|
|
520
|
+
bindings.resetToDefaults();
|
|
521
|
+
```
|
|
205
522
|
|
|
206
|
-
|
|
207
|
-
- `dawCommandTool`: AI가 커맨드를 실행하는 도구
|
|
208
|
-
- `dawSystemPrompt`: DAW 컨텍스트 시스템 프롬프트
|
|
523
|
+
---
|
|
209
524
|
|
|
210
|
-
##
|
|
525
|
+
## Preferences
|
|
211
526
|
|
|
212
|
-
|
|
527
|
+
Persistent settings with defaults.
|
|
213
528
|
|
|
214
529
|
```typescript
|
|
215
|
-
import {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
import { ToneAudioProvider } from '../audio/ToneAudioProvider';
|
|
219
|
-
import { EditorHandler } from '../app-commands/EditorHandler';
|
|
220
|
-
import { defaultActions } from '../app-actions/defaultActions';
|
|
530
|
+
import { Preferences } from 'drop-daw';
|
|
531
|
+
|
|
532
|
+
const prefs = Preferences.getInstance();
|
|
221
533
|
|
|
222
|
-
|
|
223
|
-
|
|
534
|
+
prefs.set('sampleRate', 48000);
|
|
535
|
+
prefs.set('audioBufferSize', 256);
|
|
536
|
+
prefs.set('theme', 'dark');
|
|
537
|
+
prefs.set('snapToGrid', true);
|
|
538
|
+
prefs.set('historyDepth', 100);
|
|
224
539
|
|
|
225
|
-
|
|
226
|
-
CommandExecutor.getInstance().registerHandler(new EditorHandler());
|
|
540
|
+
const sr = prefs.get('sampleRate'); // 48000
|
|
227
541
|
|
|
228
|
-
//
|
|
229
|
-
|
|
542
|
+
// React to changes
|
|
543
|
+
prefs.preferenceChanged.connect(() => {
|
|
544
|
+
console.log('Settings updated');
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
// Reset
|
|
548
|
+
prefs.resetToDefaults();
|
|
230
549
|
```
|
|
231
550
|
|
|
232
|
-
|
|
551
|
+
**Available Keys:** `audioBufferSize`, `sampleRate`, `theme`, `autoSaveInterval`, `snapToGrid`, `gridSubdivision`, `meterType`, `showMinimap`, `followPlayhead`, `countInBars`, `historyDepth`, `saveHistory`, `saveHistoryDepth`
|
|
552
|
+
|
|
553
|
+
---
|
|
554
|
+
|
|
555
|
+
## Storage
|
|
556
|
+
|
|
557
|
+
Session persistence via IndexedDB (browser) or localStorage fallback.
|
|
233
558
|
|
|
234
559
|
```typescript
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
560
|
+
import { SessionStorage } from 'drop-daw';
|
|
561
|
+
|
|
562
|
+
const storage = SessionStorage.getInstance();
|
|
563
|
+
|
|
564
|
+
// Save
|
|
565
|
+
await storage.saveSession(session);
|
|
566
|
+
|
|
567
|
+
// List
|
|
568
|
+
const sessions = await storage.listSessions();
|
|
569
|
+
// [{ id: '...', name: 'My Song', modified: Date }]
|
|
570
|
+
|
|
571
|
+
// Load
|
|
572
|
+
const snapshot = await storage.loadSession(sessions[0].id);
|
|
573
|
+
const restored = Session.fromJSON(snapshot);
|
|
574
|
+
|
|
575
|
+
// Snapshots (named save points)
|
|
576
|
+
const snapId = await storage.saveSnapshot(session.id, 'Before mixing', session.toJSON());
|
|
577
|
+
const snap = await storage.loadSnapshot(snapId);
|
|
578
|
+
|
|
579
|
+
// Delete
|
|
580
|
+
await storage.deleteSession(session.id);
|
|
243
581
|
```
|
|
244
582
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
| `KeyBindings` / `Preferences` | `localStorage` 직접 접근 | `typeof localStorage` 가드 |
|
|
267
|
-
| `ExportHandler` | `document.createElement` 직접 호출 | `typeof document` 가드 |
|
|
268
|
-
| `ai/tools` | `dawQueryTool` 내장 | 앱에서 별도 관리 |
|
|
269
|
-
|
|
270
|
-
## 설정 파일
|
|
271
|
-
|
|
272
|
-
### pnpm-workspace.yaml
|
|
273
|
-
```yaml
|
|
274
|
-
packages:
|
|
275
|
-
- "packages/*"
|
|
583
|
+
---
|
|
584
|
+
|
|
585
|
+
## Processing Chain
|
|
586
|
+
|
|
587
|
+
Each track has a `Route` containing an ordered chain of processors.
|
|
588
|
+
|
|
589
|
+
```typescript
|
|
590
|
+
import {
|
|
591
|
+
GainProcessor,
|
|
592
|
+
PanProcessor,
|
|
593
|
+
PluginInsert,
|
|
594
|
+
SendProcessor,
|
|
595
|
+
MeterProcessor,
|
|
596
|
+
} from 'drop-daw';
|
|
597
|
+
|
|
598
|
+
// Processor types available in the chain:
|
|
599
|
+
// GainProcessor — Fader / Trim level control
|
|
600
|
+
// PanProcessor — Stereo panning and width
|
|
601
|
+
// PluginInsert — Plugin effect slot
|
|
602
|
+
// SendProcessor — Pre/Post fader send to aux bus
|
|
603
|
+
// MeterProcessor — Peak/RMS metering
|
|
276
604
|
```
|
|
277
605
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
606
|
+
---
|
|
607
|
+
|
|
608
|
+
## Usage Examples
|
|
609
|
+
|
|
610
|
+
### Node.js CLI
|
|
611
|
+
|
|
612
|
+
```typescript
|
|
613
|
+
import { AudioEngine, Session, CommandExecutor, CommandType } from 'drop-daw';
|
|
614
|
+
|
|
615
|
+
// Headless backend (no audio output)
|
|
616
|
+
class HeadlessBackend implements AudioProvider {
|
|
617
|
+
async initialize() {}
|
|
618
|
+
start() {}
|
|
619
|
+
stop() {}
|
|
620
|
+
// ... no-op implementations
|
|
284
621
|
}
|
|
622
|
+
|
|
623
|
+
const engine = AudioEngine.getInstance(new HeadlessBackend());
|
|
624
|
+
const session = engine.session;
|
|
625
|
+
|
|
626
|
+
session.addTrack('Track 1', TrackType.AUDIO);
|
|
627
|
+
console.log('Tracks:', session.tracks.map(t => t.name));
|
|
628
|
+
|
|
629
|
+
const snapshot = session.toJSON();
|
|
630
|
+
// Save to file, process offline, etc.
|
|
285
631
|
```
|
|
286
632
|
|
|
287
|
-
###
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
633
|
+
### Electron App
|
|
634
|
+
|
|
635
|
+
```typescript
|
|
636
|
+
import { AudioEngine, AudioProvider } from 'drop-daw';
|
|
637
|
+
|
|
638
|
+
class ElectronAudioBackend implements AudioProvider {
|
|
639
|
+
// Use PortAudio or native audio APIs
|
|
640
|
+
// ...
|
|
295
641
|
}
|
|
642
|
+
|
|
643
|
+
const engine = AudioEngine.getInstance(new ElectronAudioBackend());
|
|
644
|
+
await engine.initialize();
|
|
296
645
|
```
|
|
297
646
|
|
|
298
|
-
|
|
647
|
+
### React Integration
|
|
299
648
|
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
|
|
649
|
+
```typescript
|
|
650
|
+
import { useEffect, useState } from 'react';
|
|
651
|
+
import { AudioEngine, Session } from 'drop-daw';
|
|
303
652
|
|
|
304
|
-
|
|
305
|
-
|
|
653
|
+
function useDAWEngine(backend: AudioProvider) {
|
|
654
|
+
const [engine] = useState(() => AudioEngine.getInstance(backend));
|
|
306
655
|
|
|
307
|
-
|
|
308
|
-
|
|
656
|
+
useEffect(() => {
|
|
657
|
+
const sub = engine.session.tempoChanged.connect((bpm) => {
|
|
658
|
+
// update React state
|
|
659
|
+
});
|
|
660
|
+
return () => sub.dispose();
|
|
661
|
+
}, [engine]);
|
|
662
|
+
|
|
663
|
+
return engine;
|
|
664
|
+
}
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
---
|
|
668
|
+
|
|
669
|
+
## API Reference
|
|
670
|
+
|
|
671
|
+
### Singletons
|
|
672
|
+
|
|
673
|
+
| Class | Access | Description |
|
|
674
|
+
|-------|--------|-------------|
|
|
675
|
+
| `AudioEngine` | `getInstance(backend?)` | Audio engine with session and transport |
|
|
676
|
+
| `CommandExecutor` | `getInstance()` | Command dispatch with validation |
|
|
677
|
+
| `PluginManager` | `getInstance()` | Plugin factory |
|
|
678
|
+
| `ActionRegistry` | `getInstance()` | Keyboard shortcut ↔ command mapping |
|
|
679
|
+
| `Preferences` | `getInstance()` | User settings |
|
|
680
|
+
| `KeyBindings` | `getInstance()` | Custom key binding overrides |
|
|
681
|
+
| `SessionStorage` | `getInstance()` | Session persistence |
|
|
682
|
+
|
|
683
|
+
### Domain Models
|
|
684
|
+
|
|
685
|
+
| Class | Description |
|
|
686
|
+
|-------|-------------|
|
|
687
|
+
| `Session` | Project root — tracks, sources, markers, ranges, tempo |
|
|
688
|
+
| `Track` | Audio/MIDI/AUX/BUS/FOLDER/VCA track |
|
|
689
|
+
| `Region` | Audio segment on the timeline |
|
|
690
|
+
| `Playlist` | Region collection within a track |
|
|
691
|
+
| `Source` | Audio file reference (URL, duration, sampleRate) |
|
|
692
|
+
| `Range` | Loop/punch/selection range |
|
|
693
|
+
| `Marker` | Timeline marker (position + name) |
|
|
694
|
+
| `MidiNote` | Single MIDI note event |
|
|
695
|
+
| `MidiRegion` | MIDI note collection on the timeline |
|
|
696
|
+
| `SendBus` | Pre/post fader send bus |
|
|
697
|
+
| `Route` | Signal chain (input → processors → output) |
|
|
698
|
+
| `TempoMap` | BPM change events over time |
|
|
699
|
+
| `GridSettings` | Grid type and snap mode |
|
|
700
|
+
| `CrossfadeEngine` | Automatic crossfade calculation |
|
|
701
|
+
| `MixerScene` | Mixer snapshot save/restore |
|
|
702
|
+
| `TrackGroup` | Track grouping for linked control |
|
|
703
|
+
|
|
704
|
+
### Type Definitions
|
|
705
|
+
|
|
706
|
+
```typescript
|
|
707
|
+
type FrameCount = number // sample frame position
|
|
708
|
+
type SampleRate = number // e.g. 44100, 48000, 96000
|
|
709
|
+
type Gain = number // 0.0 to 1.0+
|
|
710
|
+
type DB = number // decibels
|
|
711
|
+
|
|
712
|
+
type TrackId = string
|
|
713
|
+
type RegionId = string
|
|
714
|
+
type SourceId = string
|
|
715
|
+
type RangeId = string
|
|
716
|
+
type ProcessorId = string
|
|
717
|
+
type RouteId = string
|
|
309
718
|
```
|
|
310
719
|
|
|
311
|
-
|
|
720
|
+
---
|
|
721
|
+
|
|
722
|
+
## Dependencies
|
|
723
|
+
|
|
724
|
+
| Package | Type | Description |
|
|
725
|
+
|---------|------|-------------|
|
|
726
|
+
| `zod` | runtime | Command payload validation |
|
|
727
|
+
| `lamejs` | peer (optional) | MP3 export support |
|
|
728
|
+
| `soundtouchjs` | peer (optional) | Time-stretch / pitch-shift |
|
|
729
|
+
|
|
730
|
+
---
|
|
731
|
+
|
|
732
|
+
## License
|
|
312
733
|
|
|
313
|
-
|
|
314
|
-
- [ ] npm publish (`private: false`)
|
|
315
|
-
- [ ] Electron 앱에서 `@drop-ai/core` 재사용
|
|
316
|
-
- [ ] 코어 전용 단위 테스트 (`packages/core/test/`)
|
|
734
|
+
MIT
|