@kano/stem-daw 0.1.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 +253 -0
- package/dist/chat-actions-54Z6URC4.js +7 -0
- package/dist/chat-actions-54Z6URC4.js.map +1 -0
- package/dist/chunk-56PWIP7O.js +1029 -0
- package/dist/chunk-56PWIP7O.js.map +1 -0
- package/dist/chunk-AAVC7KUW.js +145 -0
- package/dist/chunk-AAVC7KUW.js.map +1 -0
- package/dist/chunk-KCOOE2OP.js +1764 -0
- package/dist/chunk-KCOOE2OP.js.map +1 -0
- package/dist/chunk-LO74ZJ4H.js +23923 -0
- package/dist/chunk-LO74ZJ4H.js.map +1 -0
- package/dist/chunk-OFGZURP6.js +247 -0
- package/dist/chunk-OFGZURP6.js.map +1 -0
- package/dist/chunk-OYNES5W3.js +3085 -0
- package/dist/chunk-OYNES5W3.js.map +1 -0
- package/dist/chunk-QQ5NZTHT.js +336 -0
- package/dist/chunk-QQ5NZTHT.js.map +1 -0
- package/dist/chunk-TBXCZFAY.js +13713 -0
- package/dist/chunk-TBXCZFAY.js.map +1 -0
- package/dist/chunk-U44X6QP5.js +281 -0
- package/dist/chunk-U44X6QP5.js.map +1 -0
- package/dist/chunk-UKMELGZL.js +27 -0
- package/dist/chunk-UKMELGZL.js.map +1 -0
- package/dist/components/DAWView.d.ts +19 -0
- package/dist/components/DAWView.js +11 -0
- package/dist/components/DAWView.js.map +1 -0
- package/dist/daw-controller-BjRWcTol.d.ts +339 -0
- package/dist/engine/daw-controller.d.ts +3 -0
- package/dist/engine/daw-controller.js +5 -0
- package/dist/engine/daw-controller.js.map +1 -0
- package/dist/engine/daw-import-stem-fm-config.d.ts +224 -0
- package/dist/engine/daw-import-stem-fm-config.js +7 -0
- package/dist/engine/daw-import-stem-fm-config.js.map +1 -0
- package/dist/fetchStationTracks-SKFT4V3U.js +3 -0
- package/dist/fetchStationTracks-SKFT4V3U.js.map +1 -0
- package/dist/index.d.ts +308 -0
- package/dist/index.js +332 -0
- package/dist/index.js.map +1 -0
- package/dist/interface-DaRj7RkY.d.ts +66 -0
- package/dist/interfaces-5ZlG0Y4Y.d.ts +549 -0
- package/dist/media-session-XTP6PP7Q.js +3 -0
- package/dist/media-session-XTP6PP7Q.js.map +1 -0
- package/dist/note-detection-PPLM7R2H.js +148 -0
- package/dist/note-detection-PPLM7R2H.js.map +1 -0
- package/dist/sampler-audio-B7MBG3YN.js +3 -0
- package/dist/sampler-audio-B7MBG3YN.js.map +1 -0
- package/dist/sampler-store-QPHANXYP.js +3 -0
- package/dist/sampler-store-QPHANXYP.js.map +1 -0
- package/dist/services/track-search-api.d.ts +152 -0
- package/dist/services/track-search-api.js +4 -0
- package/dist/services/track-search-api.js.map +1 -0
- package/dist/store/daw-auth-store.d.ts +31 -0
- package/dist/store/daw-auth-store.js +3 -0
- package/dist/store/daw-auth-store.js.map +1 -0
- package/dist/store/daw-session-store.d.ts +255 -0
- package/dist/store/daw-session-store.js +3 -0
- package/dist/store/daw-session-store.js.map +1 -0
- package/dist/vite/index.d.ts +46 -0
- package/dist/vite/index.js +94 -0
- package/dist/vite/index.js.map +1 -0
- package/dist/workers/analysis-worker.js +379 -0
- package/dist/workers/buffer-player-processor-202602.lavv8e32-ts.js +1 -0
- package/dist/workers/daw-stem-processor.js +228 -0
- package/dist/workers/manifest.json +10 -0
- package/dist/workers/phase-vocoder3.js +920 -0
- package/dist/workers/realtime-pitch-shift-processor.js +2 -0
- package/package.json +151 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import { D as DAWTrack } from './interfaces-5ZlG0Y4Y.js';
|
|
2
|
+
export { a4 as CamelotKey, a6 as CamelotMatchType, C as ClipEdge, i as ClipboardEntry, q as DAWBarChunk, k as DAWChannel, d as DAWSession, b as DAWToolMode, w as DAWTrackAudioNodes, a as DEFAULT_PITCH_SETTINGS, H as IBarMapping, I as IBeat, Z as ITempoMatch, M as MuteRegion, N as NOTE_COLORS, e as NOTE_EMOJI_PALETTE, o as PianoRollNote, P as PitchEngineSettings, a2 as PitchShiftStrategy, R as ResampledRegion, v as SEGMENT_COLORS, u as SEGMENT_LABELS, r as SEGMENT_TYPE, A as STEM_BUFFER_KEYS, z as STEM_COLORS, x as STEM_LABELS, y as STEM_TYPES, l as SamplerRegion, n as SamplerSequenceEntry, m as SamplerSlice, E as SectionRange, t as SegmentTypeValue, f as SessionNote, h as StemClip, S as StemId, c as StemSelectionRange, Y as TempoRatio, a9 as TransposeSuggestion, U as barDurationToBpm, J as barToSample, V as bpmToBarDuration, _ as calculateTempoMatch, a7 as camelotMatchType, K as composeBarsFromBeats, L as composeBarsFromBeatsSmoothed, $ as computeBarSpeedRatio, j as computeClips, a3 as computePitchShift, aa as computeTransposeSuggestions, B as createDefaultChannels, G as findFirstSectionOfType, g as getSelectionBoundsBars, W as keyToSemitones, T as medianBarDuration, p as parseStemIdKey, a0 as playbackRateToSemitones, Q as resolveSampleLocOfDownbeatFromBarIndex, O as resolveTimeOfDownbeatFromBarIndex, F as segmentInfoToSections, X as semitonesToKey, a1 as semitonesToPitchFactor, s as stemIdKey, a5 as toCamelot, a8 as transposeKey } from './interfaces-5ZlG0Y4Y.js';
|
|
3
|
+
import { I as ITrackForSequence } from './interface-DaRj7RkY.js';
|
|
4
|
+
export { D as DAWController, L as LoadProgress, S as StemAudioBuffers, l as loadTrackStems } from './daw-controller-BjRWcTol.js';
|
|
5
|
+
export { ImportStemFmCallbacks, StemFmEffectEvent, StemFmEvent, StemFmMixConfig, StemFmModifierEvent, StemFmModifierType, StemFmSession, StemFmTriggerOrigin, importStemFmMixConfig, isStemFmMixConfig, planClipsFromStemFmConfig } from './engine/daw-import-stem-fm-config.js';
|
|
6
|
+
export { DAWView } from './components/DAWView.js';
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import { SimilarityMode } from './services/track-search-api.js';
|
|
9
|
+
export { fetchTrackForDAW } from './services/track-search-api.js';
|
|
10
|
+
export { useDAWSessionStore } from './store/daw-session-store.js';
|
|
11
|
+
export { useDAWAuthStore } from './store/daw-auth-store.js';
|
|
12
|
+
import 'zustand';
|
|
13
|
+
import 'zustand/middleware';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* This panel responsible for putting the Mix JSON Object into AEV3Context
|
|
17
|
+
*
|
|
18
|
+
* Migrate all related code from V1 to V2
|
|
19
|
+
* And make another cleaner V1 version up and able to run with exported code
|
|
20
|
+
*
|
|
21
|
+
* 1.
|
|
22
|
+
*
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
type STEM_FIELDS_SUPPORTED = 'mp4';
|
|
26
|
+
|
|
27
|
+
interface ByteSampleSegment {
|
|
28
|
+
sampleStart: number;
|
|
29
|
+
sampleEnd: number;
|
|
30
|
+
sampleLength: number;
|
|
31
|
+
startTime: number;
|
|
32
|
+
endTime: number;
|
|
33
|
+
duration: number;
|
|
34
|
+
path: string;
|
|
35
|
+
byteRangeStart: number;
|
|
36
|
+
byteRangeEnd: number;
|
|
37
|
+
_mp4TrackId: number;
|
|
38
|
+
tracks: {
|
|
39
|
+
[_mp4TrackId: number]: {
|
|
40
|
+
decodeByteStart: number;
|
|
41
|
+
decodeByteLength: number;
|
|
42
|
+
frames: {
|
|
43
|
+
sampleStart: number;
|
|
44
|
+
sampleEnds: number;
|
|
45
|
+
rawData: Uint8Array;
|
|
46
|
+
}[];
|
|
47
|
+
timeLog: {
|
|
48
|
+
a_extractbytes: number;
|
|
49
|
+
b_extractmoof: number;
|
|
50
|
+
c_packageAACFrames: number;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
signatures: {
|
|
55
|
+
sequenceIdx: number;
|
|
56
|
+
sequenceId: string;
|
|
57
|
+
segmentIdx: number;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
type FetchGroupErrorCause = {
|
|
62
|
+
detailMessage: string;
|
|
63
|
+
http_status: number;
|
|
64
|
+
segmentIdx: number;
|
|
65
|
+
sequenceId: number;
|
|
66
|
+
sequenceIdx: number;
|
|
67
|
+
type: 'mp4';
|
|
68
|
+
url: string;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Bivariant listener so handlers may narrow `{ type; detail }` while dispatch uses
|
|
73
|
+
* `{ type: string; detail: unknown }` (required under strictFunctionTypes).
|
|
74
|
+
*/
|
|
75
|
+
type EmitterListener = {
|
|
76
|
+
bivarianceHack(event: {
|
|
77
|
+
type: string;
|
|
78
|
+
detail: unknown;
|
|
79
|
+
}): void;
|
|
80
|
+
}['bivarianceHack'];
|
|
81
|
+
declare class EventEmitter {
|
|
82
|
+
events: Record<string, EmitterListener[]>;
|
|
83
|
+
constructor();
|
|
84
|
+
on(eventName: string, listener: EmitterListener): void;
|
|
85
|
+
off(eventName: string, listener: EmitterListener): void;
|
|
86
|
+
dispatchEvent(eventName: string, detail?: unknown): void;
|
|
87
|
+
clearEventEmitter(): void;
|
|
88
|
+
showListenersAttached(): Record<string, number>;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* CAUTION: this is obviously a technical debt to build to just to speed up testing of MP4/AAC
|
|
93
|
+
*
|
|
94
|
+
* Goal is to replace quantised AAC with unquantise MP4 contains four stems of AACs
|
|
95
|
+
*/
|
|
96
|
+
|
|
97
|
+
interface IAudioSourceSequence {
|
|
98
|
+
manifest_url: string;
|
|
99
|
+
segments: IMP4SequenceChunk[];
|
|
100
|
+
headerBuffer: ArrayBuffer;
|
|
101
|
+
stemType: string;
|
|
102
|
+
sequenceId: string;
|
|
103
|
+
signature: string;
|
|
104
|
+
}
|
|
105
|
+
interface IMP4SequenceChunk {
|
|
106
|
+
segmentId: string;
|
|
107
|
+
nextSegmentId: string;
|
|
108
|
+
isLastSegment: boolean;
|
|
109
|
+
byteSampleSegment: ByteSampleSegment;
|
|
110
|
+
cached: {
|
|
111
|
+
arrayBuffer: ArrayBuffer | undefined;
|
|
112
|
+
cacheTimeMS: number;
|
|
113
|
+
isDownloading: boolean;
|
|
114
|
+
cacheStartTime: number;
|
|
115
|
+
firstError: string;
|
|
116
|
+
_error: FetchGroupErrorCause[];
|
|
117
|
+
};
|
|
118
|
+
parent: IAudioSourceSequence;
|
|
119
|
+
}
|
|
120
|
+
declare class AudioSourceSequence extends EventEmitter {
|
|
121
|
+
static AUDIO_SOURCE_MANIFEST_PARSED_CREATED: string;
|
|
122
|
+
static AUDIO_SOURCE_MANIFEST_PARSED_FAILED: string;
|
|
123
|
+
static LOADED_ALL_SEQUENCES_SEGMENTS_REQUESTED: string;
|
|
124
|
+
static LOADED_SINGLE_SEQUENCE_SEGMENTS_REQUESTED: string;
|
|
125
|
+
static UNLOADED_ALL_SEQUENCES_SEGMENTS_REQUESTED: string;
|
|
126
|
+
static PROGRESS_SINGLE_STEM_INDIVIDUAL_SEGMENT_LOADED: string;
|
|
127
|
+
static PROGRESS_MULTIPLE_STEMS_INDIVIDUAL_SEGMENT_LOADED: string;
|
|
128
|
+
static SEGMENT_LOADING_ERROR: string;
|
|
129
|
+
static SEGMENTS_BATCH_LOADING_ERROR: string;
|
|
130
|
+
static SAMPLE_RANGE_REQUESTED_NOT_EXISTED: string;
|
|
131
|
+
static SINGLE_ARRAY_BUFFER_LOAGING_PERCENT: string;
|
|
132
|
+
static GROUP_ARRAY_BUFFER_LOAGING_PERCENT: string;
|
|
133
|
+
isLoading: boolean;
|
|
134
|
+
isInitialised: 'INITIALISED' | 'NOT_YET' | 'FLAWED';
|
|
135
|
+
isDecoding: boolean;
|
|
136
|
+
trackId: string;
|
|
137
|
+
trackData: ITrackForSequence;
|
|
138
|
+
eTagOfTailSegment: string | undefined;
|
|
139
|
+
lastModifiedOfTailSegment: string | undefined;
|
|
140
|
+
dateOfTailSegment: string | undefined;
|
|
141
|
+
isMP4TailFlawed: boolean;
|
|
142
|
+
errorOnInit: string;
|
|
143
|
+
diffETagDetected: boolean;
|
|
144
|
+
sequences: IAudioSourceSequence[];
|
|
145
|
+
sourcePlaying: {
|
|
146
|
+
[sequenceIdx: number]: {
|
|
147
|
+
sampleEndOfPlayingChunk: number;
|
|
148
|
+
sequenceIdx: number;
|
|
149
|
+
sequenceId: string;
|
|
150
|
+
};
|
|
151
|
+
};
|
|
152
|
+
interval_caching: ReturnType<typeof setInterval> | undefined;
|
|
153
|
+
interval_evicting: ReturnType<typeof setInterval> | undefined;
|
|
154
|
+
constructor();
|
|
155
|
+
_log(...msg: unknown[]): void;
|
|
156
|
+
destroy(): void;
|
|
157
|
+
initialise(trackData: ITrackForSequence, field: STEM_FIELDS_SUPPORTED, token: string, diffETagDetected: boolean): Promise<unknown>;
|
|
158
|
+
preload(preloadAtMoofBoxIndex?: number, updatePlayheadAtTargetIndex?: boolean, signature?: string): Promise<boolean>;
|
|
159
|
+
preloadWithRange(preloadAtMoofIndex: number, preloadEndAtMoofIndex: number, signature?: string): Promise<boolean>;
|
|
160
|
+
preloadWithSampleRange(preloadAtSample48000: number, preloadEndAtSample48000: number, signature?: string): Promise<boolean>;
|
|
161
|
+
preloadWithTimeRange(preloadAtStartTime: number, preloadDuration: number, signature?: string): Promise<boolean>;
|
|
162
|
+
preloadSneakyVer(preloadAtIndex: number, count?: number): Promise<boolean>;
|
|
163
|
+
updateSampleLocJustPlayed(sequenceIdx: number, samplePlayed: number, signature: string): boolean;
|
|
164
|
+
updateSampleLocJustPlayedUntilManifestResolved(sequenceIdx: number, samplePlayed: number, signature: string): void;
|
|
165
|
+
getSegmentsWithSampleLoc(sequenceId: string, sample: number): number;
|
|
166
|
+
getSegmentsIdxWithSampleLoc(sequenceIdx: number, sample_: number): number;
|
|
167
|
+
getPlayingChunksInfo(): {
|
|
168
|
+
segmentId: string;
|
|
169
|
+
segmentIdx: number;
|
|
170
|
+
sequenceId: string;
|
|
171
|
+
sequenceIdx: number;
|
|
172
|
+
}[];
|
|
173
|
+
getMinimumPlayingIndex(): number;
|
|
174
|
+
isSegmentIdxCached(segmentIdx: number, sequenceIdx?: number): boolean;
|
|
175
|
+
isMoofSegmentCached(segment: IMP4SequenceChunk): boolean;
|
|
176
|
+
evictArrayBufferOnAllSequencesWithMoofIndex(moofBoxIndex: number): void;
|
|
177
|
+
evictArrayBufferAccordingToSampleRange(sequenceIdx: number, sampleStart48000: number, sampleEnd48000: number, _signature: string): void;
|
|
178
|
+
/**
|
|
179
|
+
* Trigger caching of MP4 segments, ARRAY_BUFFER_PRELOADED will be fired or error will be printed
|
|
180
|
+
* the arrayBuffer will be updated directly to sequences's items
|
|
181
|
+
*
|
|
182
|
+
* When this function resolved as true, should promise the Moof Boxes are accessible
|
|
183
|
+
*
|
|
184
|
+
* @param startIndex start (including) the MP4 segment's index
|
|
185
|
+
* @param endIndex end (including) the MP4 segment's index
|
|
186
|
+
* @param sessionId will be included in the dispatcher of events
|
|
187
|
+
*/
|
|
188
|
+
loadAllSequencesWithMoofIndex(moofStartIndex: number, moofEndIndex: number, sessionId: string, isPreloadCommand?: boolean): Promise<boolean>;
|
|
189
|
+
private loadMultipleMoofSegments;
|
|
190
|
+
getAudioBuffer(sequenceIdx: number, sampleStart48000: number, sampleEnd48000: number, decodingQueueIndex: number, signature: string): Promise<[AudioBuffer | null, number, number, number, number, number, string, string]>;
|
|
191
|
+
getIsInitialised(): "INITIALISED" | "NOT_YET" | "FLAWED";
|
|
192
|
+
getSegmentInfo(sequenceIdx: number, sampleStart48000_: number, sampleEnd48000: number): [IAudioSourceSequence, IMP4SequenceChunk[], number, number];
|
|
193
|
+
fetchCacheRecord(sampleStart: number, sampleEnd: number): {
|
|
194
|
+
totalCacheTime: string;
|
|
195
|
+
cacheTimes: string;
|
|
196
|
+
firstErrorOfEachMoov: string;
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Bridge between AudioSourceSequence (cache layer) and the DAW decode pipeline.
|
|
202
|
+
* Decoupled from AEV3Controller - takes the AudioSourceSequence directly.
|
|
203
|
+
*/
|
|
204
|
+
|
|
205
|
+
declare const GET_AUDIO_BUFFER_MESSAGE_NOT_INIT = "audio_source_not_initialised";
|
|
206
|
+
declare const GET_AUDIO_BUFFER_MESSAGE_NOT_CACHED = "moof_not_cached_yet";
|
|
207
|
+
declare const GET_AUDIO_BUFFER_MESSAGE_ERROR = "moof_box_error";
|
|
208
|
+
interface AudioBufferResult {
|
|
209
|
+
audioBuffer: AudioBuffer | null;
|
|
210
|
+
totalTimeElapsed: number;
|
|
211
|
+
error: string | null;
|
|
212
|
+
message: string;
|
|
213
|
+
signature: string;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Request an AudioBuffer for a specific sample range from a given AudioSourceSequence.
|
|
217
|
+
* This is the decoupled equivalent of getAudioBufferUtil from the streaming engine.
|
|
218
|
+
*/
|
|
219
|
+
declare const getAudioBufferFromSource: (audioSourceSequence: AudioSourceSequence | null, sequenceIdx: number, sampleStart: number, sampleEnd: number, decodingQueueIndex: number, signature: string) => Promise<AudioBufferResult>;
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* DAWTimeline – Unified timeline with sticky ruler, collapsible muted stems,
|
|
223
|
+
* and independently positionable stem blocks.
|
|
224
|
+
*
|
|
225
|
+
* The ruler is a separate sticky element at the top of the scroll container so
|
|
226
|
+
* it always stays visible. Muted stems collapse to a thin lane showing just the
|
|
227
|
+
* icon. Drag on the ruler to create loop regions.
|
|
228
|
+
*/
|
|
229
|
+
|
|
230
|
+
interface DAWTimelineProps {
|
|
231
|
+
onBarClick?: (bar: number) => void;
|
|
232
|
+
/** Render an "Add another song" row directly under the bottom track. */
|
|
233
|
+
onAddTrack?: () => void;
|
|
234
|
+
/**
|
|
235
|
+
* When `true`, the timeline's left track-names column is pinned to
|
|
236
|
+
* LEFT_COL_COMPACT regardless of scroll/hover state. Used by the
|
|
237
|
+
* shell (`DAWView`) to force compact whenever a side panel is open,
|
|
238
|
+
* so the panel and the track-names column never visually fight for
|
|
239
|
+
* horizontal space — you're either in "panel open + compact strip"
|
|
240
|
+
* or "panel closed + full strip", never the squeezed in-between.
|
|
241
|
+
*/
|
|
242
|
+
forceCompact?: boolean;
|
|
243
|
+
}
|
|
244
|
+
declare const DAWTimeline: React.FC<DAWTimelineProps>;
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* DAWTransportBar - Compact transport bar.
|
|
248
|
+
*
|
|
249
|
+
* Primary row: Stop | Play/Pause | Undo/Redo | Tools | Position | BPM | Add Track | ··· overflow
|
|
250
|
+
* Overflow menu: Loop, Metronome, Reset
|
|
251
|
+
*
|
|
252
|
+
* Responsive: tool labels hide at narrow widths, BPM slider shrinks, gaps tighten,
|
|
253
|
+
* Export compacts to icon-only then moves to overflow menu, central LCD trims labels.
|
|
254
|
+
*/
|
|
255
|
+
|
|
256
|
+
interface DAWTransportBarProps {
|
|
257
|
+
onAddTrack?: () => void;
|
|
258
|
+
onImportProject?: (file: File) => void;
|
|
259
|
+
onExport?: () => void;
|
|
260
|
+
/**
|
|
261
|
+
* Slot rendered at the far-right of the bar — carries chrome that
|
|
262
|
+
* used to live in the standalone top nav (account dropdown,
|
|
263
|
+
* "what's new" pill, etc.). Kept as an opaque ReactNode so the
|
|
264
|
+
* transport bar doesn't need to know what those bits are or how
|
|
265
|
+
* they're wired.
|
|
266
|
+
*/
|
|
267
|
+
rightExtras?: React.ReactNode;
|
|
268
|
+
}
|
|
269
|
+
declare const DAWTransportBar: React.FC<DAWTransportBarProps>;
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* DAWTrackHeader – Compact sidebar panel for a single track.
|
|
273
|
+
* Each stem has its own volume slider — that's the primary control.
|
|
274
|
+
* No track-level master volume; stem volumes ARE the mix.
|
|
275
|
+
*/
|
|
276
|
+
|
|
277
|
+
interface DAWTrackHeaderProps {
|
|
278
|
+
track: DAWTrack;
|
|
279
|
+
}
|
|
280
|
+
declare const DAWTrackHeader: React.FC<DAWTrackHeaderProps>;
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* DAWAddTrackPanel – Inspirational entry-point for adding tracks.
|
|
284
|
+
* Rich category cards with gradient visuals, search, similarity matching,
|
|
285
|
+
* and URL import.
|
|
286
|
+
*/
|
|
287
|
+
|
|
288
|
+
type TabId = 'similar' | 'suggestions' | 'search' | 'url';
|
|
289
|
+
interface DAWAddTrackPanelProps {
|
|
290
|
+
onClose: () => void;
|
|
291
|
+
initialTab?: TabId;
|
|
292
|
+
}
|
|
293
|
+
declare const DAWAddTrackPanel: React.FC<DAWAddTrackPanelProps>;
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* DAWSuggestedStemsPanel – Sidebar of similar tracks from the same station
|
|
297
|
+
* (refresh-mix style). Draggable/clickable cards to add stems to the DAW.
|
|
298
|
+
*/
|
|
299
|
+
|
|
300
|
+
interface DAWSuggestedStemsPanelProps {
|
|
301
|
+
mixUrl: string;
|
|
302
|
+
onAddTrack?: () => void;
|
|
303
|
+
similarityMode?: SimilarityMode;
|
|
304
|
+
onSimilarityModeChange?: (mode: SimilarityMode) => void;
|
|
305
|
+
}
|
|
306
|
+
declare const DAWSuggestedStemsPanel: React.FC<DAWSuggestedStemsPanelProps>;
|
|
307
|
+
|
|
308
|
+
export { type AudioBufferResult, DAWAddTrackPanel, DAWSuggestedStemsPanel, DAWTimeline, DAWTrack, DAWTrackHeader, DAWTransportBar, GET_AUDIO_BUFFER_MESSAGE_ERROR, GET_AUDIO_BUFFER_MESSAGE_NOT_CACHED, GET_AUDIO_BUFFER_MESSAGE_NOT_INIT, getAudioBufferFromSource };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { useThemeMode, theme } from './chunk-LO74ZJ4H.js';
|
|
2
|
+
export { DAWAddTrackPanel, DAWSuggestedStemsPanel, DAWTimeline, DAWTransportBar, DAWView } from './chunk-LO74ZJ4H.js';
|
|
3
|
+
import './chunk-QQ5NZTHT.js';
|
|
4
|
+
import './chunk-OFGZURP6.js';
|
|
5
|
+
import './chunk-AAVC7KUW.js';
|
|
6
|
+
export { importStemFmMixConfig, isStemFmMixConfig, planClipsFromStemFmConfig } from './chunk-U44X6QP5.js';
|
|
7
|
+
import { DAWController } from './chunk-OYNES5W3.js';
|
|
8
|
+
export { DAWController, loadTrackStems } from './chunk-OYNES5W3.js';
|
|
9
|
+
export { fetchTrackForDAW } from './chunk-56PWIP7O.js';
|
|
10
|
+
import './chunk-TBXCZFAY.js';
|
|
11
|
+
import { useDAWSessionStore, STEM_COLORS } from './chunk-KCOOE2OP.js';
|
|
12
|
+
export { DEFAULT_PITCH_SETTINGS, NOTE_COLORS, NOTE_EMOJI_PALETTE, PitchShiftStrategy, SEGMENT_COLORS, SEGMENT_LABELS, SEGMENT_TYPE, STEM_BUFFER_KEYS, STEM_COLORS, STEM_LABELS, STEM_TYPES, barDurationToBpm, barToSample, bpmToBarDuration, calculateTempoMatch, camelotMatchType, composeBarsFromBeats, composeBarsFromBeatsSmoothed, computeBarSpeedRatio, computeClips, computePitchShift, computeTransposeSuggestions, createDefaultChannels, findFirstSectionOfType, getSelectionBoundsBars, keyToSemitones, medianBarDuration, parseStemIdKey, playbackRateToSemitones, resolveSampleLocOfDownbeatFromBarIndex, resolveTimeOfDownbeatFromBarIndex, segmentInfoToSections, semitonesToKey, semitonesToPitchFactor, stemIdKey, toCamelot, transposeKey, useDAWSessionStore } from './chunk-KCOOE2OP.js';
|
|
13
|
+
export { useDAWAuthStore } from './chunk-UKMELGZL.js';
|
|
14
|
+
import { useState, useCallback, useRef } from 'react';
|
|
15
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
16
|
+
|
|
17
|
+
// src/daw/primitives/audio-buffer-bridge.ts
|
|
18
|
+
var GET_AUDIO_BUFFER_MESSAGE_NOT_INIT = "audio_source_not_initialised";
|
|
19
|
+
var GET_AUDIO_BUFFER_MESSAGE_NOT_CACHED = "moof_not_cached_yet";
|
|
20
|
+
var GET_AUDIO_BUFFER_MESSAGE_ERROR = "moof_box_error";
|
|
21
|
+
var getAudioBufferFromSource = async (audioSourceSequence, sequenceIdx, sampleStart, sampleEnd, decodingQueueIndex, signature) => {
|
|
22
|
+
if (!audioSourceSequence) {
|
|
23
|
+
return {
|
|
24
|
+
audioBuffer: null,
|
|
25
|
+
totalTimeElapsed: -1,
|
|
26
|
+
error: "AudioSourceSequence not provided",
|
|
27
|
+
message: GET_AUDIO_BUFFER_MESSAGE_NOT_INIT,
|
|
28
|
+
signature
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const result = await audioSourceSequence.getAudioBuffer(
|
|
33
|
+
sequenceIdx,
|
|
34
|
+
sampleStart,
|
|
35
|
+
sampleEnd,
|
|
36
|
+
decodingQueueIndex,
|
|
37
|
+
signature
|
|
38
|
+
);
|
|
39
|
+
const [audioBuffer, totalTimeElapsed, , , , , resultSignature, message] = result;
|
|
40
|
+
return {
|
|
41
|
+
audioBuffer,
|
|
42
|
+
totalTimeElapsed,
|
|
43
|
+
error: audioBuffer === null ? GET_AUDIO_BUFFER_MESSAGE_NOT_CACHED : null,
|
|
44
|
+
message,
|
|
45
|
+
signature: resultSignature
|
|
46
|
+
};
|
|
47
|
+
} catch (err) {
|
|
48
|
+
if (err?.toString().includes("sequence is not initialised yet")) {
|
|
49
|
+
return {
|
|
50
|
+
audioBuffer: null,
|
|
51
|
+
totalTimeElapsed: -1,
|
|
52
|
+
error: GET_AUDIO_BUFFER_MESSAGE_NOT_INIT,
|
|
53
|
+
message: GET_AUDIO_BUFFER_MESSAGE_NOT_INIT,
|
|
54
|
+
signature
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
audioBuffer: null,
|
|
59
|
+
totalTimeElapsed: -1,
|
|
60
|
+
error: err?.toString() ?? "unknown error",
|
|
61
|
+
message: GET_AUDIO_BUFFER_MESSAGE_ERROR,
|
|
62
|
+
signature
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
var STEM_ICONS = ["\u266A", "\u{1F3A4}", "\u{1F3B5}", "\u2B21"];
|
|
67
|
+
var DAWTrackHeader = ({ track }) => {
|
|
68
|
+
useThemeMode();
|
|
69
|
+
const [showBeatNudge, setShowBeatNudge] = useState(false);
|
|
70
|
+
const handleRemove = useCallback(() => {
|
|
71
|
+
DAWController.getInstance().removeTrack(track.id);
|
|
72
|
+
}, [track.id]);
|
|
73
|
+
return /* @__PURE__ */ jsxs("div", { className: "border-b px-3 py-2", style: { borderColor: "rgba(255,255,255,0.04)" }, children: [
|
|
74
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
|
|
75
|
+
track.artwork && /* @__PURE__ */ jsx("img", { src: track.artwork, alt: "", className: "w-5 h-5 rounded-sm object-cover" }),
|
|
76
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium text-white truncate flex-1", children: track.displayName }),
|
|
77
|
+
/* @__PURE__ */ jsx("span", { className: "text-[9px]", style: { color: "rgba(255,255,255,0.3)" }, children: Math.round(track.nativeBpm) }),
|
|
78
|
+
/* @__PURE__ */ jsx(
|
|
79
|
+
"button",
|
|
80
|
+
{
|
|
81
|
+
onClick: () => setShowBeatNudge((v) => !v),
|
|
82
|
+
className: "text-[8px] font-medium rounded transition-all",
|
|
83
|
+
style: {
|
|
84
|
+
padding: "1px 4px",
|
|
85
|
+
background: showBeatNudge ? "rgba(255,214,0,0.12)" : "rgba(255,255,255,0.04)",
|
|
86
|
+
color: showBeatNudge ? "#FFD600" : "rgba(255,255,255,0.25)",
|
|
87
|
+
border: showBeatNudge ? "1px solid rgba(255,214,0,0.2)" : "1px solid transparent"
|
|
88
|
+
},
|
|
89
|
+
title: "Beat grid nudge",
|
|
90
|
+
children: "Grid"
|
|
91
|
+
}
|
|
92
|
+
),
|
|
93
|
+
/* @__PURE__ */ jsx(
|
|
94
|
+
"button",
|
|
95
|
+
{
|
|
96
|
+
onClick: handleRemove,
|
|
97
|
+
className: "text-[9px] transition-colors ml-1",
|
|
98
|
+
style: { color: "rgba(255,255,255,0.25)" },
|
|
99
|
+
children: "\u2715"
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
] }),
|
|
103
|
+
showBeatNudge && /* @__PURE__ */ jsx(BeatGridNudge, { trackId: track.id, currentOffset: track.beatGridOffsetBeats }),
|
|
104
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-1", children: track.channels.map((ch) => /* @__PURE__ */ jsx(
|
|
105
|
+
StemStrip,
|
|
106
|
+
{
|
|
107
|
+
trackId: track.id,
|
|
108
|
+
stemIndex: ch.stemIndex,
|
|
109
|
+
label: ch.label,
|
|
110
|
+
volume: ch.volume,
|
|
111
|
+
muted: ch.muted,
|
|
112
|
+
visible: ch.visible
|
|
113
|
+
},
|
|
114
|
+
ch.stemIndex
|
|
115
|
+
)) })
|
|
116
|
+
] });
|
|
117
|
+
};
|
|
118
|
+
var NUDGE_AMOUNTS = [
|
|
119
|
+
{ label: "-1", value: -1, title: "Shift grid back 1 beat" },
|
|
120
|
+
{ label: "-\xBD", value: -0.5, title: "Shift grid back half a beat" },
|
|
121
|
+
{ label: "-\xBC", value: -0.25, title: "Shift grid back a quarter beat" },
|
|
122
|
+
{ label: "+\xBC", value: 0.25, title: "Shift grid forward a quarter beat" },
|
|
123
|
+
{ label: "+\xBD", value: 0.5, title: "Shift grid forward half a beat" },
|
|
124
|
+
{ label: "+1", value: 1, title: "Shift grid forward 1 beat" }
|
|
125
|
+
];
|
|
126
|
+
var BeatGridNudge = ({ trackId, currentOffset }) => {
|
|
127
|
+
const setBeatGridOffset = useDAWSessionStore((s) => s.setTrackBeatGridOffset);
|
|
128
|
+
const handleNudge = useCallback((delta) => {
|
|
129
|
+
const newOffset = currentOffset + delta;
|
|
130
|
+
setBeatGridOffset(trackId, newOffset);
|
|
131
|
+
DAWController.getInstance().reloadTrackBarMap(trackId);
|
|
132
|
+
}, [trackId, currentOffset, setBeatGridOffset]);
|
|
133
|
+
const handleReset = useCallback(() => {
|
|
134
|
+
setBeatGridOffset(trackId, 0);
|
|
135
|
+
DAWController.getInstance().reloadTrackBarMap(trackId);
|
|
136
|
+
}, [trackId, setBeatGridOffset]);
|
|
137
|
+
const displayOffset = currentOffset === 0 ? "0" : `${currentOffset > 0 ? "+" : ""}${currentOffset % 1 === 0 ? currentOffset : currentOffset.toFixed(2)} beat${Math.abs(currentOffset) !== 1 ? "s" : ""}`;
|
|
138
|
+
return /* @__PURE__ */ jsxs(
|
|
139
|
+
"div",
|
|
140
|
+
{
|
|
141
|
+
className: "mb-2 rounded-lg",
|
|
142
|
+
style: {
|
|
143
|
+
padding: "6px 8px",
|
|
144
|
+
background: "rgba(255,255,255,0.02)",
|
|
145
|
+
border: "1px solid rgba(255,255,255,0.04)"
|
|
146
|
+
},
|
|
147
|
+
children: [
|
|
148
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 mb-1.5", children: [
|
|
149
|
+
/* @__PURE__ */ jsx("span", { className: "text-[7px] font-semibold tracking-wider uppercase", style: { color: "rgba(255,255,255,0.25)" }, children: "Beat Grid" }),
|
|
150
|
+
/* @__PURE__ */ jsx("span", { className: "text-[8px] font-mono flex-1", style: { color: currentOffset !== 0 ? "#FFD600" : "rgba(255,255,255,0.2)" }, children: displayOffset }),
|
|
151
|
+
currentOffset !== 0 && /* @__PURE__ */ jsx(
|
|
152
|
+
"button",
|
|
153
|
+
{
|
|
154
|
+
onClick: handleReset,
|
|
155
|
+
className: "text-[7px] font-semibold rounded transition-all",
|
|
156
|
+
style: {
|
|
157
|
+
padding: "1px 5px",
|
|
158
|
+
background: "rgba(239,68,68,0.08)",
|
|
159
|
+
color: "rgba(251,113,133,0.7)",
|
|
160
|
+
border: "1px solid rgba(239,68,68,0.15)"
|
|
161
|
+
},
|
|
162
|
+
children: "Reset"
|
|
163
|
+
}
|
|
164
|
+
)
|
|
165
|
+
] }),
|
|
166
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: NUDGE_AMOUNTS.map((nudge) => /* @__PURE__ */ jsx(
|
|
167
|
+
"button",
|
|
168
|
+
{
|
|
169
|
+
onClick: () => handleNudge(nudge.value),
|
|
170
|
+
title: nudge.title,
|
|
171
|
+
className: "flex-1 text-[8px] font-semibold rounded transition-all",
|
|
172
|
+
style: {
|
|
173
|
+
padding: "3px 0",
|
|
174
|
+
background: "rgba(255,255,255,0.04)",
|
|
175
|
+
color: "rgba(255,255,255,0.4)",
|
|
176
|
+
border: "1px solid rgba(255,255,255,0.06)"
|
|
177
|
+
},
|
|
178
|
+
onMouseEnter: (e) => {
|
|
179
|
+
e.currentTarget.style.background = "rgba(255,255,255,0.08)";
|
|
180
|
+
e.currentTarget.style.color = "rgba(255,255,255,0.7)";
|
|
181
|
+
},
|
|
182
|
+
onMouseLeave: (e) => {
|
|
183
|
+
e.currentTarget.style.background = "rgba(255,255,255,0.04)";
|
|
184
|
+
e.currentTarget.style.color = "rgba(255,255,255,0.4)";
|
|
185
|
+
},
|
|
186
|
+
children: nudge.label
|
|
187
|
+
},
|
|
188
|
+
nudge.value
|
|
189
|
+
)) })
|
|
190
|
+
]
|
|
191
|
+
}
|
|
192
|
+
);
|
|
193
|
+
};
|
|
194
|
+
var StemStrip = ({
|
|
195
|
+
trackId,
|
|
196
|
+
stemIndex,
|
|
197
|
+
label,
|
|
198
|
+
volume,
|
|
199
|
+
muted,
|
|
200
|
+
visible
|
|
201
|
+
}) => {
|
|
202
|
+
const store = useDAWSessionStore();
|
|
203
|
+
const color = STEM_COLORS[stemIndex];
|
|
204
|
+
const icon = STEM_ICONS[stemIndex];
|
|
205
|
+
const volPercent = Math.round(volume * 100);
|
|
206
|
+
const toggleVisible = useCallback(() => {
|
|
207
|
+
store.setChannelVisible(trackId, stemIndex, !visible);
|
|
208
|
+
if (visible) store.setChannelMuted(trackId, stemIndex, true);
|
|
209
|
+
else store.setChannelMuted(trackId, stemIndex, false);
|
|
210
|
+
DAWController.getInstance().syncGainsForTrack(trackId);
|
|
211
|
+
}, [trackId, stemIndex, visible]);
|
|
212
|
+
const toggleMute = useCallback(() => {
|
|
213
|
+
store.setChannelMuted(trackId, stemIndex, !muted);
|
|
214
|
+
DAWController.getInstance().syncGainsForTrack(trackId);
|
|
215
|
+
}, [trackId, stemIndex, muted]);
|
|
216
|
+
const handleVolume = useCallback((e) => {
|
|
217
|
+
store.setChannelVolume(trackId, stemIndex, parseFloat(e.target.value));
|
|
218
|
+
DAWController.getInstance().syncGainsForTrack(trackId);
|
|
219
|
+
}, [trackId, stemIndex]);
|
|
220
|
+
return /* @__PURE__ */ jsxs(
|
|
221
|
+
"div",
|
|
222
|
+
{
|
|
223
|
+
className: "flex items-center gap-1.5 rounded py-0.5 transition-opacity",
|
|
224
|
+
style: { opacity: visible ? 1 : 0.25 },
|
|
225
|
+
children: [
|
|
226
|
+
/* @__PURE__ */ jsx(
|
|
227
|
+
"button",
|
|
228
|
+
{
|
|
229
|
+
onClick: toggleVisible,
|
|
230
|
+
className: "w-5 h-5 flex items-center justify-center rounded flex-shrink-0 text-[10px] transition-all",
|
|
231
|
+
style: {
|
|
232
|
+
backgroundColor: visible ? color : "transparent",
|
|
233
|
+
border: visible ? "none" : `1px solid ${color}33`,
|
|
234
|
+
color: visible ? "#000" : color + "66"
|
|
235
|
+
},
|
|
236
|
+
title: visible ? `Remove ${label}` : `Add ${label}`,
|
|
237
|
+
children: icon
|
|
238
|
+
}
|
|
239
|
+
),
|
|
240
|
+
visible ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
241
|
+
/* @__PURE__ */ jsx(VolumeFader, { color, volume, onChange: handleVolume }),
|
|
242
|
+
/* @__PURE__ */ jsx("span", { className: "text-[8px] w-5 text-right tabular-nums flex-shrink-0 font-mono", style: { color: "rgba(255,255,255,0.3)" }, children: volPercent }),
|
|
243
|
+
/* @__PURE__ */ jsx(
|
|
244
|
+
"button",
|
|
245
|
+
{
|
|
246
|
+
onClick: toggleMute,
|
|
247
|
+
className: "text-[7px] font-semibold w-4 h-4 flex items-center justify-center flex-shrink-0 transition-all",
|
|
248
|
+
style: {
|
|
249
|
+
backgroundColor: muted ? "rgba(255,69,58,0.1)" : "rgba(255,255,255,0.04)",
|
|
250
|
+
color: muted ? "rgba(255,105,97,0.85)" : "rgba(255,255,255,0.3)",
|
|
251
|
+
borderRadius: 6
|
|
252
|
+
},
|
|
253
|
+
title: muted ? `Unmute ${label}` : `Mute ${label}`,
|
|
254
|
+
children: "M"
|
|
255
|
+
}
|
|
256
|
+
)
|
|
257
|
+
] }) : /* @__PURE__ */ jsx("span", { className: "text-[9px] truncate", style: { color: "rgba(255,255,255,0.4)" }, children: label })
|
|
258
|
+
]
|
|
259
|
+
}
|
|
260
|
+
);
|
|
261
|
+
};
|
|
262
|
+
var VolumeFader = ({ color, volume, onChange }) => {
|
|
263
|
+
useThemeMode();
|
|
264
|
+
const trackRef = useRef(null);
|
|
265
|
+
const pct = Math.max(0, Math.min(1, volume));
|
|
266
|
+
const updateFromPointer = (clientX) => {
|
|
267
|
+
const rect = trackRef.current?.getBoundingClientRect();
|
|
268
|
+
if (!rect) return;
|
|
269
|
+
const p = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
|
|
270
|
+
onChange({ target: { value: String(Math.round(p * 100) / 100) } });
|
|
271
|
+
};
|
|
272
|
+
return /* @__PURE__ */ jsxs(
|
|
273
|
+
"div",
|
|
274
|
+
{
|
|
275
|
+
ref: trackRef,
|
|
276
|
+
style: {
|
|
277
|
+
flex: 1,
|
|
278
|
+
height: 14,
|
|
279
|
+
position: "relative",
|
|
280
|
+
display: "flex",
|
|
281
|
+
alignItems: "center",
|
|
282
|
+
cursor: "pointer"
|
|
283
|
+
},
|
|
284
|
+
onPointerDown: (e) => {
|
|
285
|
+
e.preventDefault();
|
|
286
|
+
e.target.setPointerCapture(e.pointerId);
|
|
287
|
+
updateFromPointer(e.clientX);
|
|
288
|
+
},
|
|
289
|
+
onPointerMove: (e) => {
|
|
290
|
+
if (e.buttons > 0) updateFromPointer(e.clientX);
|
|
291
|
+
},
|
|
292
|
+
children: [
|
|
293
|
+
/* @__PURE__ */ jsx("div", { style: {
|
|
294
|
+
position: "absolute",
|
|
295
|
+
left: 0,
|
|
296
|
+
right: 0,
|
|
297
|
+
top: "50%",
|
|
298
|
+
transform: "translateY(-50%)",
|
|
299
|
+
height: 2,
|
|
300
|
+
background: theme.border.default,
|
|
301
|
+
borderRadius: 1
|
|
302
|
+
} }),
|
|
303
|
+
/* @__PURE__ */ jsx("div", { style: {
|
|
304
|
+
position: "absolute",
|
|
305
|
+
left: 0,
|
|
306
|
+
width: `${pct * 100}%`,
|
|
307
|
+
top: "50%",
|
|
308
|
+
transform: "translateY(-50%)",
|
|
309
|
+
height: 2,
|
|
310
|
+
background: color,
|
|
311
|
+
opacity: 0.9,
|
|
312
|
+
borderRadius: 1
|
|
313
|
+
} }),
|
|
314
|
+
/* @__PURE__ */ jsx("div", { style: {
|
|
315
|
+
position: "absolute",
|
|
316
|
+
left: `${pct * 100}%`,
|
|
317
|
+
top: "50%",
|
|
318
|
+
transform: "translate(-50%, -50%)",
|
|
319
|
+
width: 3,
|
|
320
|
+
height: 12,
|
|
321
|
+
borderRadius: 1.5,
|
|
322
|
+
background: color,
|
|
323
|
+
zIndex: 2
|
|
324
|
+
} })
|
|
325
|
+
]
|
|
326
|
+
}
|
|
327
|
+
);
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
export { DAWTrackHeader, GET_AUDIO_BUFFER_MESSAGE_ERROR, GET_AUDIO_BUFFER_MESSAGE_NOT_CACHED, GET_AUDIO_BUFFER_MESSAGE_NOT_INIT, getAudioBufferFromSource };
|
|
331
|
+
//# sourceMappingURL=index.js.map
|
|
332
|
+
//# sourceMappingURL=index.js.map
|