@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.
Files changed (67) hide show
  1. package/README.md +253 -0
  2. package/dist/chat-actions-54Z6URC4.js +7 -0
  3. package/dist/chat-actions-54Z6URC4.js.map +1 -0
  4. package/dist/chunk-56PWIP7O.js +1029 -0
  5. package/dist/chunk-56PWIP7O.js.map +1 -0
  6. package/dist/chunk-AAVC7KUW.js +145 -0
  7. package/dist/chunk-AAVC7KUW.js.map +1 -0
  8. package/dist/chunk-KCOOE2OP.js +1764 -0
  9. package/dist/chunk-KCOOE2OP.js.map +1 -0
  10. package/dist/chunk-LO74ZJ4H.js +23923 -0
  11. package/dist/chunk-LO74ZJ4H.js.map +1 -0
  12. package/dist/chunk-OFGZURP6.js +247 -0
  13. package/dist/chunk-OFGZURP6.js.map +1 -0
  14. package/dist/chunk-OYNES5W3.js +3085 -0
  15. package/dist/chunk-OYNES5W3.js.map +1 -0
  16. package/dist/chunk-QQ5NZTHT.js +336 -0
  17. package/dist/chunk-QQ5NZTHT.js.map +1 -0
  18. package/dist/chunk-TBXCZFAY.js +13713 -0
  19. package/dist/chunk-TBXCZFAY.js.map +1 -0
  20. package/dist/chunk-U44X6QP5.js +281 -0
  21. package/dist/chunk-U44X6QP5.js.map +1 -0
  22. package/dist/chunk-UKMELGZL.js +27 -0
  23. package/dist/chunk-UKMELGZL.js.map +1 -0
  24. package/dist/components/DAWView.d.ts +19 -0
  25. package/dist/components/DAWView.js +11 -0
  26. package/dist/components/DAWView.js.map +1 -0
  27. package/dist/daw-controller-BjRWcTol.d.ts +339 -0
  28. package/dist/engine/daw-controller.d.ts +3 -0
  29. package/dist/engine/daw-controller.js +5 -0
  30. package/dist/engine/daw-controller.js.map +1 -0
  31. package/dist/engine/daw-import-stem-fm-config.d.ts +224 -0
  32. package/dist/engine/daw-import-stem-fm-config.js +7 -0
  33. package/dist/engine/daw-import-stem-fm-config.js.map +1 -0
  34. package/dist/fetchStationTracks-SKFT4V3U.js +3 -0
  35. package/dist/fetchStationTracks-SKFT4V3U.js.map +1 -0
  36. package/dist/index.d.ts +308 -0
  37. package/dist/index.js +332 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/interface-DaRj7RkY.d.ts +66 -0
  40. package/dist/interfaces-5ZlG0Y4Y.d.ts +549 -0
  41. package/dist/media-session-XTP6PP7Q.js +3 -0
  42. package/dist/media-session-XTP6PP7Q.js.map +1 -0
  43. package/dist/note-detection-PPLM7R2H.js +148 -0
  44. package/dist/note-detection-PPLM7R2H.js.map +1 -0
  45. package/dist/sampler-audio-B7MBG3YN.js +3 -0
  46. package/dist/sampler-audio-B7MBG3YN.js.map +1 -0
  47. package/dist/sampler-store-QPHANXYP.js +3 -0
  48. package/dist/sampler-store-QPHANXYP.js.map +1 -0
  49. package/dist/services/track-search-api.d.ts +152 -0
  50. package/dist/services/track-search-api.js +4 -0
  51. package/dist/services/track-search-api.js.map +1 -0
  52. package/dist/store/daw-auth-store.d.ts +31 -0
  53. package/dist/store/daw-auth-store.js +3 -0
  54. package/dist/store/daw-auth-store.js.map +1 -0
  55. package/dist/store/daw-session-store.d.ts +255 -0
  56. package/dist/store/daw-session-store.js +3 -0
  57. package/dist/store/daw-session-store.js.map +1 -0
  58. package/dist/vite/index.d.ts +46 -0
  59. package/dist/vite/index.js +94 -0
  60. package/dist/vite/index.js.map +1 -0
  61. package/dist/workers/analysis-worker.js +379 -0
  62. package/dist/workers/buffer-player-processor-202602.lavv8e32-ts.js +1 -0
  63. package/dist/workers/daw-stem-processor.js +228 -0
  64. package/dist/workers/manifest.json +10 -0
  65. package/dist/workers/phase-vocoder3.js +920 -0
  66. package/dist/workers/realtime-pitch-shift-processor.js +2 -0
  67. package/package.json +151 -0
@@ -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