@waveform-playlist/core 5.0.0-alpha.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/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Naomi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,284 @@
1
+ /**
2
+ * Clip-Based Model Types
3
+ *
4
+ * These types support a professional multi-track editing model where:
5
+ * - Each track can contain multiple audio clips
6
+ * - Clips can be positioned anywhere on the timeline
7
+ * - Clips have independent trim points (offset/duration)
8
+ * - Gaps between clips are silent
9
+ * - Clips can overlap (for crossfades)
10
+ */
11
+
12
+ /**
13
+ * Generic effects function type for track-level audio processing.
14
+ *
15
+ * The actual implementation receives Tone.js audio nodes. Using generic types
16
+ * here to avoid circular dependencies with the playout package.
17
+ *
18
+ * @param graphEnd - The end of the track's audio graph (Tone.js Gain node)
19
+ * @param destination - Where to connect the effects output (Tone.js ToneAudioNode)
20
+ * @param isOffline - Whether rendering offline (for export)
21
+ * @returns Optional cleanup function called when track is disposed
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * const trackEffects: TrackEffectsFunction = (graphEnd, destination, isOffline) => {
26
+ * const reverb = new Tone.Reverb({ decay: 1.5 });
27
+ * graphEnd.connect(reverb);
28
+ * reverb.connect(destination);
29
+ *
30
+ * return () => {
31
+ * reverb.dispose();
32
+ * };
33
+ * };
34
+ * ```
35
+ */
36
+ type TrackEffectsFunction = (graphEnd: unknown, destination: unknown, isOffline: boolean) => void | (() => void);
37
+ /**
38
+ * Represents a single audio clip on the timeline
39
+ *
40
+ * IMPORTANT: All positions/durations are stored as SAMPLE COUNTS (integers)
41
+ * to avoid floating-point precision errors. Convert to seconds only when
42
+ * needed for playback using: seconds = samples / sampleRate
43
+ */
44
+ interface AudioClip {
45
+ /** Unique identifier for this clip */
46
+ id: string;
47
+ /** The audio buffer containing the audio data */
48
+ audioBuffer: AudioBuffer;
49
+ /** Position on timeline where this clip starts (in samples at timeline sampleRate) */
50
+ startSample: number;
51
+ /** Duration of this clip (in samples) - how much of the audio buffer to play */
52
+ durationSamples: number;
53
+ /** Offset into the audio buffer where playback starts (in samples) - the "trim start" point */
54
+ offsetSamples: number;
55
+ /** Optional fade in effect */
56
+ fadeIn?: Fade;
57
+ /** Optional fade out effect */
58
+ fadeOut?: Fade;
59
+ /** Clip-specific gain/volume multiplier (0.0 to 1.0+) */
60
+ gain: number;
61
+ /** Optional label/name for this clip */
62
+ name?: string;
63
+ /** Optional color for visual distinction */
64
+ color?: string;
65
+ }
66
+ /**
67
+ * Represents a track containing multiple audio clips
68
+ */
69
+ interface ClipTrack {
70
+ /** Unique identifier for this track */
71
+ id: string;
72
+ /** Display name for this track */
73
+ name: string;
74
+ /** Array of audio clips on this track */
75
+ clips: AudioClip[];
76
+ /** Whether this track is muted */
77
+ muted: boolean;
78
+ /** Whether this track is soloed */
79
+ soloed: boolean;
80
+ /** Track volume (0.0 to 1.0+) */
81
+ volume: number;
82
+ /** Stereo pan (-1.0 = left, 0 = center, 1.0 = right) */
83
+ pan: number;
84
+ /** Optional track color for visual distinction */
85
+ color?: string;
86
+ /** Track height in pixels (for UI) */
87
+ height?: number;
88
+ /** Optional effects function for this track */
89
+ effects?: TrackEffectsFunction;
90
+ }
91
+ /**
92
+ * Represents the entire timeline/project
93
+ */
94
+ interface Timeline {
95
+ /** All tracks in the timeline */
96
+ tracks: ClipTrack[];
97
+ /** Total timeline duration in seconds */
98
+ duration: number;
99
+ /** Sample rate for all audio (typically 44100 or 48000) */
100
+ sampleRate: number;
101
+ /** Optional project name */
102
+ name?: string;
103
+ /** Optional tempo (BPM) for grid snapping */
104
+ tempo?: number;
105
+ /** Optional time signature for grid snapping */
106
+ timeSignature?: {
107
+ numerator: number;
108
+ denominator: number;
109
+ };
110
+ }
111
+ /**
112
+ * Options for creating a new audio clip (using sample counts)
113
+ */
114
+ interface CreateClipOptions {
115
+ audioBuffer: AudioBuffer;
116
+ startSample: number;
117
+ durationSamples?: number;
118
+ offsetSamples?: number;
119
+ gain?: number;
120
+ name?: string;
121
+ color?: string;
122
+ fadeIn?: Fade;
123
+ fadeOut?: Fade;
124
+ }
125
+ /**
126
+ * Options for creating a new audio clip (using seconds for convenience)
127
+ */
128
+ interface CreateClipOptionsSeconds {
129
+ audioBuffer: AudioBuffer;
130
+ startTime: number;
131
+ duration?: number;
132
+ offset?: number;
133
+ gain?: number;
134
+ name?: string;
135
+ color?: string;
136
+ fadeIn?: Fade;
137
+ fadeOut?: Fade;
138
+ }
139
+ /**
140
+ * Options for creating a new track
141
+ */
142
+ interface CreateTrackOptions {
143
+ name: string;
144
+ clips?: AudioClip[];
145
+ muted?: boolean;
146
+ soloed?: boolean;
147
+ volume?: number;
148
+ pan?: number;
149
+ color?: string;
150
+ height?: number;
151
+ }
152
+ /**
153
+ * Creates a new AudioClip with sensible defaults (using sample counts)
154
+ */
155
+ declare function createClip(options: CreateClipOptions): AudioClip;
156
+ /**
157
+ * Creates a new AudioClip from time-based values (convenience function)
158
+ * Converts seconds to samples using the audioBuffer's sampleRate
159
+ */
160
+ declare function createClipFromSeconds(options: CreateClipOptionsSeconds): AudioClip;
161
+ /**
162
+ * Creates a new ClipTrack with sensible defaults
163
+ */
164
+ declare function createTrack(options: CreateTrackOptions): ClipTrack;
165
+ /**
166
+ * Creates a new Timeline with sensible defaults
167
+ */
168
+ declare function createTimeline(tracks: ClipTrack[], sampleRate?: number, options?: {
169
+ name?: string;
170
+ tempo?: number;
171
+ timeSignature?: {
172
+ numerator: number;
173
+ denominator: number;
174
+ };
175
+ }): Timeline;
176
+ /**
177
+ * Utility: Get all clips within a sample range
178
+ */
179
+ declare function getClipsInRange(track: ClipTrack, startSample: number, endSample: number): AudioClip[];
180
+ /**
181
+ * Utility: Get all clips at a specific sample position
182
+ */
183
+ declare function getClipsAtSample(track: ClipTrack, sample: number): AudioClip[];
184
+ /**
185
+ * Utility: Check if two clips overlap
186
+ */
187
+ declare function clipsOverlap(clip1: AudioClip, clip2: AudioClip): boolean;
188
+ /**
189
+ * Utility: Sort clips by startSample
190
+ */
191
+ declare function sortClipsByTime(clips: AudioClip[]): AudioClip[];
192
+ /**
193
+ * Utility: Find gaps between clips (silent regions)
194
+ */
195
+ interface Gap {
196
+ startSample: number;
197
+ endSample: number;
198
+ durationSamples: number;
199
+ }
200
+ declare function findGaps(track: ClipTrack): Gap[];
201
+
202
+ interface WaveformConfig {
203
+ sampleRate: number;
204
+ samplesPerPixel: number;
205
+ waveHeight?: number;
206
+ waveOutlineColor?: string;
207
+ waveFillColor?: string;
208
+ waveProgressColor?: string;
209
+ }
210
+ interface AudioBuffer$1 {
211
+ length: number;
212
+ duration: number;
213
+ numberOfChannels: number;
214
+ sampleRate: number;
215
+ getChannelData(channel: number): Float32Array;
216
+ }
217
+ interface Track {
218
+ id: string;
219
+ name: string;
220
+ src?: string | AudioBuffer$1;
221
+ gain: number;
222
+ muted: boolean;
223
+ soloed: boolean;
224
+ stereoPan: number;
225
+ startTime: number;
226
+ endTime?: number;
227
+ fadeIn?: Fade;
228
+ fadeOut?: Fade;
229
+ cueIn?: number;
230
+ cueOut?: number;
231
+ }
232
+ /**
233
+ * Simple fade configuration
234
+ */
235
+ interface Fade {
236
+ /** Duration of the fade in seconds */
237
+ duration: number;
238
+ /** Type of fade curve (default: 'linear') */
239
+ type?: FadeType;
240
+ }
241
+ type FadeType = 'logarithmic' | 'linear' | 'sCurve' | 'exponential';
242
+ interface PlaylistConfig {
243
+ samplesPerPixel?: number;
244
+ waveHeight?: number;
245
+ container?: HTMLElement;
246
+ isAutomaticScroll?: boolean;
247
+ timescale?: boolean;
248
+ colors?: {
249
+ waveOutlineColor?: string;
250
+ waveFillColor?: string;
251
+ waveProgressColor?: string;
252
+ };
253
+ controls?: {
254
+ show?: boolean;
255
+ width?: number;
256
+ };
257
+ zoomLevels?: number[];
258
+ }
259
+ interface PlayoutState {
260
+ isPlaying: boolean;
261
+ isPaused: boolean;
262
+ cursor: number;
263
+ duration: number;
264
+ }
265
+ interface TimeSelection {
266
+ start: number;
267
+ end: number;
268
+ }
269
+ declare enum InteractionState {
270
+ Cursor = "cursor",
271
+ Select = "select",
272
+ Shift = "shift",
273
+ FadeIn = "fadein",
274
+ FadeOut = "fadeout"
275
+ }
276
+
277
+ declare function samplesToSeconds(samples: number, sampleRate: number): number;
278
+ declare function secondsToSamples(seconds: number, sampleRate: number): number;
279
+ declare function samplesToPixels(samples: number, samplesPerPixel: number): number;
280
+ declare function pixelsToSamples(pixels: number, samplesPerPixel: number): number;
281
+ declare function pixelsToSeconds(pixels: number, samplesPerPixel: number, sampleRate: number): number;
282
+ declare function secondsToPixels(seconds: number, samplesPerPixel: number, sampleRate: number): number;
283
+
284
+ export { type AudioBuffer$1 as AudioBuffer, type AudioClip, type ClipTrack, type CreateClipOptions, type CreateClipOptionsSeconds, type CreateTrackOptions, type Fade, type FadeType, type Gap, InteractionState, type PlaylistConfig, type PlayoutState, type TimeSelection, type Timeline, type Track, type TrackEffectsFunction, type WaveformConfig, clipsOverlap, createClip, createClipFromSeconds, createTimeline, createTrack, findGaps, getClipsAtSample, getClipsInRange, pixelsToSamples, pixelsToSeconds, samplesToPixels, samplesToSeconds, secondsToPixels, secondsToSamples, sortClipsByTime };
@@ -0,0 +1,284 @@
1
+ /**
2
+ * Clip-Based Model Types
3
+ *
4
+ * These types support a professional multi-track editing model where:
5
+ * - Each track can contain multiple audio clips
6
+ * - Clips can be positioned anywhere on the timeline
7
+ * - Clips have independent trim points (offset/duration)
8
+ * - Gaps between clips are silent
9
+ * - Clips can overlap (for crossfades)
10
+ */
11
+
12
+ /**
13
+ * Generic effects function type for track-level audio processing.
14
+ *
15
+ * The actual implementation receives Tone.js audio nodes. Using generic types
16
+ * here to avoid circular dependencies with the playout package.
17
+ *
18
+ * @param graphEnd - The end of the track's audio graph (Tone.js Gain node)
19
+ * @param destination - Where to connect the effects output (Tone.js ToneAudioNode)
20
+ * @param isOffline - Whether rendering offline (for export)
21
+ * @returns Optional cleanup function called when track is disposed
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * const trackEffects: TrackEffectsFunction = (graphEnd, destination, isOffline) => {
26
+ * const reverb = new Tone.Reverb({ decay: 1.5 });
27
+ * graphEnd.connect(reverb);
28
+ * reverb.connect(destination);
29
+ *
30
+ * return () => {
31
+ * reverb.dispose();
32
+ * };
33
+ * };
34
+ * ```
35
+ */
36
+ type TrackEffectsFunction = (graphEnd: unknown, destination: unknown, isOffline: boolean) => void | (() => void);
37
+ /**
38
+ * Represents a single audio clip on the timeline
39
+ *
40
+ * IMPORTANT: All positions/durations are stored as SAMPLE COUNTS (integers)
41
+ * to avoid floating-point precision errors. Convert to seconds only when
42
+ * needed for playback using: seconds = samples / sampleRate
43
+ */
44
+ interface AudioClip {
45
+ /** Unique identifier for this clip */
46
+ id: string;
47
+ /** The audio buffer containing the audio data */
48
+ audioBuffer: AudioBuffer;
49
+ /** Position on timeline where this clip starts (in samples at timeline sampleRate) */
50
+ startSample: number;
51
+ /** Duration of this clip (in samples) - how much of the audio buffer to play */
52
+ durationSamples: number;
53
+ /** Offset into the audio buffer where playback starts (in samples) - the "trim start" point */
54
+ offsetSamples: number;
55
+ /** Optional fade in effect */
56
+ fadeIn?: Fade;
57
+ /** Optional fade out effect */
58
+ fadeOut?: Fade;
59
+ /** Clip-specific gain/volume multiplier (0.0 to 1.0+) */
60
+ gain: number;
61
+ /** Optional label/name for this clip */
62
+ name?: string;
63
+ /** Optional color for visual distinction */
64
+ color?: string;
65
+ }
66
+ /**
67
+ * Represents a track containing multiple audio clips
68
+ */
69
+ interface ClipTrack {
70
+ /** Unique identifier for this track */
71
+ id: string;
72
+ /** Display name for this track */
73
+ name: string;
74
+ /** Array of audio clips on this track */
75
+ clips: AudioClip[];
76
+ /** Whether this track is muted */
77
+ muted: boolean;
78
+ /** Whether this track is soloed */
79
+ soloed: boolean;
80
+ /** Track volume (0.0 to 1.0+) */
81
+ volume: number;
82
+ /** Stereo pan (-1.0 = left, 0 = center, 1.0 = right) */
83
+ pan: number;
84
+ /** Optional track color for visual distinction */
85
+ color?: string;
86
+ /** Track height in pixels (for UI) */
87
+ height?: number;
88
+ /** Optional effects function for this track */
89
+ effects?: TrackEffectsFunction;
90
+ }
91
+ /**
92
+ * Represents the entire timeline/project
93
+ */
94
+ interface Timeline {
95
+ /** All tracks in the timeline */
96
+ tracks: ClipTrack[];
97
+ /** Total timeline duration in seconds */
98
+ duration: number;
99
+ /** Sample rate for all audio (typically 44100 or 48000) */
100
+ sampleRate: number;
101
+ /** Optional project name */
102
+ name?: string;
103
+ /** Optional tempo (BPM) for grid snapping */
104
+ tempo?: number;
105
+ /** Optional time signature for grid snapping */
106
+ timeSignature?: {
107
+ numerator: number;
108
+ denominator: number;
109
+ };
110
+ }
111
+ /**
112
+ * Options for creating a new audio clip (using sample counts)
113
+ */
114
+ interface CreateClipOptions {
115
+ audioBuffer: AudioBuffer;
116
+ startSample: number;
117
+ durationSamples?: number;
118
+ offsetSamples?: number;
119
+ gain?: number;
120
+ name?: string;
121
+ color?: string;
122
+ fadeIn?: Fade;
123
+ fadeOut?: Fade;
124
+ }
125
+ /**
126
+ * Options for creating a new audio clip (using seconds for convenience)
127
+ */
128
+ interface CreateClipOptionsSeconds {
129
+ audioBuffer: AudioBuffer;
130
+ startTime: number;
131
+ duration?: number;
132
+ offset?: number;
133
+ gain?: number;
134
+ name?: string;
135
+ color?: string;
136
+ fadeIn?: Fade;
137
+ fadeOut?: Fade;
138
+ }
139
+ /**
140
+ * Options for creating a new track
141
+ */
142
+ interface CreateTrackOptions {
143
+ name: string;
144
+ clips?: AudioClip[];
145
+ muted?: boolean;
146
+ soloed?: boolean;
147
+ volume?: number;
148
+ pan?: number;
149
+ color?: string;
150
+ height?: number;
151
+ }
152
+ /**
153
+ * Creates a new AudioClip with sensible defaults (using sample counts)
154
+ */
155
+ declare function createClip(options: CreateClipOptions): AudioClip;
156
+ /**
157
+ * Creates a new AudioClip from time-based values (convenience function)
158
+ * Converts seconds to samples using the audioBuffer's sampleRate
159
+ */
160
+ declare function createClipFromSeconds(options: CreateClipOptionsSeconds): AudioClip;
161
+ /**
162
+ * Creates a new ClipTrack with sensible defaults
163
+ */
164
+ declare function createTrack(options: CreateTrackOptions): ClipTrack;
165
+ /**
166
+ * Creates a new Timeline with sensible defaults
167
+ */
168
+ declare function createTimeline(tracks: ClipTrack[], sampleRate?: number, options?: {
169
+ name?: string;
170
+ tempo?: number;
171
+ timeSignature?: {
172
+ numerator: number;
173
+ denominator: number;
174
+ };
175
+ }): Timeline;
176
+ /**
177
+ * Utility: Get all clips within a sample range
178
+ */
179
+ declare function getClipsInRange(track: ClipTrack, startSample: number, endSample: number): AudioClip[];
180
+ /**
181
+ * Utility: Get all clips at a specific sample position
182
+ */
183
+ declare function getClipsAtSample(track: ClipTrack, sample: number): AudioClip[];
184
+ /**
185
+ * Utility: Check if two clips overlap
186
+ */
187
+ declare function clipsOverlap(clip1: AudioClip, clip2: AudioClip): boolean;
188
+ /**
189
+ * Utility: Sort clips by startSample
190
+ */
191
+ declare function sortClipsByTime(clips: AudioClip[]): AudioClip[];
192
+ /**
193
+ * Utility: Find gaps between clips (silent regions)
194
+ */
195
+ interface Gap {
196
+ startSample: number;
197
+ endSample: number;
198
+ durationSamples: number;
199
+ }
200
+ declare function findGaps(track: ClipTrack): Gap[];
201
+
202
+ interface WaveformConfig {
203
+ sampleRate: number;
204
+ samplesPerPixel: number;
205
+ waveHeight?: number;
206
+ waveOutlineColor?: string;
207
+ waveFillColor?: string;
208
+ waveProgressColor?: string;
209
+ }
210
+ interface AudioBuffer$1 {
211
+ length: number;
212
+ duration: number;
213
+ numberOfChannels: number;
214
+ sampleRate: number;
215
+ getChannelData(channel: number): Float32Array;
216
+ }
217
+ interface Track {
218
+ id: string;
219
+ name: string;
220
+ src?: string | AudioBuffer$1;
221
+ gain: number;
222
+ muted: boolean;
223
+ soloed: boolean;
224
+ stereoPan: number;
225
+ startTime: number;
226
+ endTime?: number;
227
+ fadeIn?: Fade;
228
+ fadeOut?: Fade;
229
+ cueIn?: number;
230
+ cueOut?: number;
231
+ }
232
+ /**
233
+ * Simple fade configuration
234
+ */
235
+ interface Fade {
236
+ /** Duration of the fade in seconds */
237
+ duration: number;
238
+ /** Type of fade curve (default: 'linear') */
239
+ type?: FadeType;
240
+ }
241
+ type FadeType = 'logarithmic' | 'linear' | 'sCurve' | 'exponential';
242
+ interface PlaylistConfig {
243
+ samplesPerPixel?: number;
244
+ waveHeight?: number;
245
+ container?: HTMLElement;
246
+ isAutomaticScroll?: boolean;
247
+ timescale?: boolean;
248
+ colors?: {
249
+ waveOutlineColor?: string;
250
+ waveFillColor?: string;
251
+ waveProgressColor?: string;
252
+ };
253
+ controls?: {
254
+ show?: boolean;
255
+ width?: number;
256
+ };
257
+ zoomLevels?: number[];
258
+ }
259
+ interface PlayoutState {
260
+ isPlaying: boolean;
261
+ isPaused: boolean;
262
+ cursor: number;
263
+ duration: number;
264
+ }
265
+ interface TimeSelection {
266
+ start: number;
267
+ end: number;
268
+ }
269
+ declare enum InteractionState {
270
+ Cursor = "cursor",
271
+ Select = "select",
272
+ Shift = "shift",
273
+ FadeIn = "fadein",
274
+ FadeOut = "fadeout"
275
+ }
276
+
277
+ declare function samplesToSeconds(samples: number, sampleRate: number): number;
278
+ declare function secondsToSamples(seconds: number, sampleRate: number): number;
279
+ declare function samplesToPixels(samples: number, samplesPerPixel: number): number;
280
+ declare function pixelsToSamples(pixels: number, samplesPerPixel: number): number;
281
+ declare function pixelsToSeconds(pixels: number, samplesPerPixel: number, sampleRate: number): number;
282
+ declare function secondsToPixels(seconds: number, samplesPerPixel: number, sampleRate: number): number;
283
+
284
+ export { type AudioBuffer$1 as AudioBuffer, type AudioClip, type ClipTrack, type CreateClipOptions, type CreateClipOptionsSeconds, type CreateTrackOptions, type Fade, type FadeType, type Gap, InteractionState, type PlaylistConfig, type PlayoutState, type TimeSelection, type Timeline, type Track, type TrackEffectsFunction, type WaveformConfig, clipsOverlap, createClip, createClipFromSeconds, createTimeline, createTrack, findGaps, getClipsAtSample, getClipsInRange, pixelsToSamples, pixelsToSeconds, samplesToPixels, samplesToSeconds, secondsToPixels, secondsToSamples, sortClipsByTime };
package/dist/index.js ADDED
@@ -0,0 +1,223 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ InteractionState: () => InteractionState,
24
+ clipsOverlap: () => clipsOverlap,
25
+ createClip: () => createClip,
26
+ createClipFromSeconds: () => createClipFromSeconds,
27
+ createTimeline: () => createTimeline,
28
+ createTrack: () => createTrack,
29
+ findGaps: () => findGaps,
30
+ getClipsAtSample: () => getClipsAtSample,
31
+ getClipsInRange: () => getClipsInRange,
32
+ pixelsToSamples: () => pixelsToSamples,
33
+ pixelsToSeconds: () => pixelsToSeconds,
34
+ samplesToPixels: () => samplesToPixels,
35
+ samplesToSeconds: () => samplesToSeconds,
36
+ secondsToPixels: () => secondsToPixels,
37
+ secondsToSamples: () => secondsToSamples,
38
+ sortClipsByTime: () => sortClipsByTime
39
+ });
40
+ module.exports = __toCommonJS(index_exports);
41
+
42
+ // src/types/clip.ts
43
+ function createClip(options) {
44
+ const {
45
+ audioBuffer,
46
+ startSample,
47
+ durationSamples = audioBuffer.length,
48
+ // Full buffer by default
49
+ offsetSamples = 0,
50
+ gain = 1,
51
+ name,
52
+ color,
53
+ fadeIn,
54
+ fadeOut
55
+ } = options;
56
+ return {
57
+ id: generateId(),
58
+ audioBuffer,
59
+ startSample,
60
+ durationSamples,
61
+ offsetSamples,
62
+ gain,
63
+ name,
64
+ color,
65
+ fadeIn,
66
+ fadeOut
67
+ };
68
+ }
69
+ function createClipFromSeconds(options) {
70
+ const {
71
+ audioBuffer,
72
+ startTime,
73
+ duration = audioBuffer.duration,
74
+ offset = 0,
75
+ gain = 1,
76
+ name,
77
+ color,
78
+ fadeIn,
79
+ fadeOut
80
+ } = options;
81
+ const sampleRate = audioBuffer.sampleRate;
82
+ return createClip({
83
+ audioBuffer,
84
+ startSample: Math.round(startTime * sampleRate),
85
+ durationSamples: Math.round(duration * sampleRate),
86
+ offsetSamples: Math.round(offset * sampleRate),
87
+ gain,
88
+ name,
89
+ color,
90
+ fadeIn,
91
+ fadeOut
92
+ });
93
+ }
94
+ function createTrack(options) {
95
+ const {
96
+ name,
97
+ clips = [],
98
+ muted = false,
99
+ soloed = false,
100
+ volume = 1,
101
+ pan = 0,
102
+ color,
103
+ height
104
+ } = options;
105
+ return {
106
+ id: generateId(),
107
+ name,
108
+ clips,
109
+ muted,
110
+ soloed,
111
+ volume,
112
+ pan,
113
+ color,
114
+ height
115
+ };
116
+ }
117
+ function createTimeline(tracks, sampleRate = 44100, options) {
118
+ const durationSamples = tracks.reduce((maxSamples, track) => {
119
+ const trackSamples = track.clips.reduce((max, clip) => {
120
+ return Math.max(max, clip.startSample + clip.durationSamples);
121
+ }, 0);
122
+ return Math.max(maxSamples, trackSamples);
123
+ }, 0);
124
+ const duration = durationSamples / sampleRate;
125
+ return {
126
+ tracks,
127
+ duration,
128
+ sampleRate,
129
+ name: options?.name,
130
+ tempo: options?.tempo,
131
+ timeSignature: options?.timeSignature
132
+ };
133
+ }
134
+ function generateId() {
135
+ return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
136
+ }
137
+ function getClipsInRange(track, startSample, endSample) {
138
+ return track.clips.filter((clip) => {
139
+ const clipEnd = clip.startSample + clip.durationSamples;
140
+ return clip.startSample < endSample && clipEnd > startSample;
141
+ });
142
+ }
143
+ function getClipsAtSample(track, sample) {
144
+ return track.clips.filter((clip) => {
145
+ const clipEnd = clip.startSample + clip.durationSamples;
146
+ return sample >= clip.startSample && sample < clipEnd;
147
+ });
148
+ }
149
+ function clipsOverlap(clip1, clip2) {
150
+ const clip1End = clip1.startSample + clip1.durationSamples;
151
+ const clip2End = clip2.startSample + clip2.durationSamples;
152
+ return clip1.startSample < clip2End && clip1End > clip2.startSample;
153
+ }
154
+ function sortClipsByTime(clips) {
155
+ return [...clips].sort((a, b) => a.startSample - b.startSample);
156
+ }
157
+ function findGaps(track) {
158
+ if (track.clips.length === 0) return [];
159
+ const sorted = sortClipsByTime(track.clips);
160
+ const gaps = [];
161
+ for (let i = 0; i < sorted.length - 1; i++) {
162
+ const currentClipEnd = sorted[i].startSample + sorted[i].durationSamples;
163
+ const nextClipStart = sorted[i + 1].startSample;
164
+ if (nextClipStart > currentClipEnd) {
165
+ gaps.push({
166
+ startSample: currentClipEnd,
167
+ endSample: nextClipStart,
168
+ durationSamples: nextClipStart - currentClipEnd
169
+ });
170
+ }
171
+ }
172
+ return gaps;
173
+ }
174
+
175
+ // src/types/index.ts
176
+ var InteractionState = /* @__PURE__ */ ((InteractionState2) => {
177
+ InteractionState2["Cursor"] = "cursor";
178
+ InteractionState2["Select"] = "select";
179
+ InteractionState2["Shift"] = "shift";
180
+ InteractionState2["FadeIn"] = "fadein";
181
+ InteractionState2["FadeOut"] = "fadeout";
182
+ return InteractionState2;
183
+ })(InteractionState || {});
184
+
185
+ // src/utils/conversions.ts
186
+ function samplesToSeconds(samples, sampleRate) {
187
+ return samples / sampleRate;
188
+ }
189
+ function secondsToSamples(seconds, sampleRate) {
190
+ return Math.ceil(seconds * sampleRate);
191
+ }
192
+ function samplesToPixels(samples, samplesPerPixel) {
193
+ return Math.floor(samples / samplesPerPixel);
194
+ }
195
+ function pixelsToSamples(pixels, samplesPerPixel) {
196
+ return Math.floor(pixels * samplesPerPixel);
197
+ }
198
+ function pixelsToSeconds(pixels, samplesPerPixel, sampleRate) {
199
+ return pixels * samplesPerPixel / sampleRate;
200
+ }
201
+ function secondsToPixels(seconds, samplesPerPixel, sampleRate) {
202
+ return Math.ceil(seconds * sampleRate / samplesPerPixel);
203
+ }
204
+ // Annotate the CommonJS export names for ESM import in node:
205
+ 0 && (module.exports = {
206
+ InteractionState,
207
+ clipsOverlap,
208
+ createClip,
209
+ createClipFromSeconds,
210
+ createTimeline,
211
+ createTrack,
212
+ findGaps,
213
+ getClipsAtSample,
214
+ getClipsInRange,
215
+ pixelsToSamples,
216
+ pixelsToSeconds,
217
+ samplesToPixels,
218
+ samplesToSeconds,
219
+ secondsToPixels,
220
+ secondsToSamples,
221
+ sortClipsByTime
222
+ });
223
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/types/clip.ts","../src/types/index.ts","../src/utils/conversions.ts"],"sourcesContent":["export * from './types';\nexport * from './utils';\n","/**\n * Clip-Based Model Types\n *\n * These types support a professional multi-track editing model where:\n * - Each track can contain multiple audio clips\n * - Clips can be positioned anywhere on the timeline\n * - Clips have independent trim points (offset/duration)\n * - Gaps between clips are silent\n * - Clips can overlap (for crossfades)\n */\n\nimport { Fade } from './index';\n\n/**\n * Generic effects function type for track-level audio processing.\n *\n * The actual implementation receives Tone.js audio nodes. Using generic types\n * here to avoid circular dependencies with the playout package.\n *\n * @param graphEnd - The end of the track's audio graph (Tone.js Gain node)\n * @param destination - Where to connect the effects output (Tone.js ToneAudioNode)\n * @param isOffline - Whether rendering offline (for export)\n * @returns Optional cleanup function called when track is disposed\n *\n * @example\n * ```typescript\n * const trackEffects: TrackEffectsFunction = (graphEnd, destination, isOffline) => {\n * const reverb = new Tone.Reverb({ decay: 1.5 });\n * graphEnd.connect(reverb);\n * reverb.connect(destination);\n *\n * return () => {\n * reverb.dispose();\n * };\n * };\n * ```\n */\nexport type TrackEffectsFunction = (\n graphEnd: unknown,\n destination: unknown,\n isOffline: boolean\n) => void | (() => void);\n\n/**\n * Represents a single audio clip on the timeline\n *\n * IMPORTANT: All positions/durations are stored as SAMPLE COUNTS (integers)\n * to avoid floating-point precision errors. Convert to seconds only when\n * needed for playback using: seconds = samples / sampleRate\n */\nexport interface AudioClip {\n /** Unique identifier for this clip */\n id: string;\n\n /** The audio buffer containing the audio data */\n audioBuffer: AudioBuffer;\n\n /** Position on timeline where this clip starts (in samples at timeline sampleRate) */\n startSample: number;\n\n /** Duration of this clip (in samples) - how much of the audio buffer to play */\n durationSamples: number;\n\n /** Offset into the audio buffer where playback starts (in samples) - the \"trim start\" point */\n offsetSamples: number;\n\n /** Optional fade in effect */\n fadeIn?: Fade;\n\n /** Optional fade out effect */\n fadeOut?: Fade;\n\n /** Clip-specific gain/volume multiplier (0.0 to 1.0+) */\n gain: number;\n\n /** Optional label/name for this clip */\n name?: string;\n\n /** Optional color for visual distinction */\n color?: string;\n}\n\n/**\n * Represents a track containing multiple audio clips\n */\nexport interface ClipTrack {\n /** Unique identifier for this track */\n id: string;\n\n /** Display name for this track */\n name: string;\n\n /** Array of audio clips on this track */\n clips: AudioClip[];\n\n /** Whether this track is muted */\n muted: boolean;\n\n /** Whether this track is soloed */\n soloed: boolean;\n\n /** Track volume (0.0 to 1.0+) */\n volume: number;\n\n /** Stereo pan (-1.0 = left, 0 = center, 1.0 = right) */\n pan: number;\n\n /** Optional track color for visual distinction */\n color?: string;\n\n /** Track height in pixels (for UI) */\n height?: number;\n\n /** Optional effects function for this track */\n effects?: TrackEffectsFunction;\n}\n\n/**\n * Represents the entire timeline/project\n */\nexport interface Timeline {\n /** All tracks in the timeline */\n tracks: ClipTrack[];\n\n /** Total timeline duration in seconds */\n duration: number;\n\n /** Sample rate for all audio (typically 44100 or 48000) */\n sampleRate: number;\n\n /** Optional project name */\n name?: string;\n\n /** Optional tempo (BPM) for grid snapping */\n tempo?: number;\n\n /** Optional time signature for grid snapping */\n timeSignature?: {\n numerator: number;\n denominator: number;\n };\n}\n\n/**\n * Options for creating a new audio clip (using sample counts)\n */\nexport interface CreateClipOptions {\n audioBuffer: AudioBuffer;\n startSample: number; // Position on timeline (in samples)\n durationSamples?: number; // Defaults to full buffer duration (in samples)\n offsetSamples?: number; // Defaults to 0\n gain?: number; // Defaults to 1.0\n name?: string;\n color?: string;\n fadeIn?: Fade;\n fadeOut?: Fade;\n}\n\n/**\n * Options for creating a new audio clip (using seconds for convenience)\n */\nexport interface CreateClipOptionsSeconds {\n audioBuffer: AudioBuffer;\n startTime: number; // Position on timeline (in seconds)\n duration?: number; // Defaults to full buffer duration (in seconds)\n offset?: number; // Defaults to 0 (in seconds)\n gain?: number; // Defaults to 1.0\n name?: string;\n color?: string;\n fadeIn?: Fade;\n fadeOut?: Fade;\n}\n\n/**\n * Options for creating a new track\n */\nexport interface CreateTrackOptions {\n name: string;\n clips?: AudioClip[];\n muted?: boolean;\n soloed?: boolean;\n volume?: number;\n pan?: number;\n color?: string;\n height?: number;\n}\n\n/**\n * Creates a new AudioClip with sensible defaults (using sample counts)\n */\nexport function createClip(options: CreateClipOptions): AudioClip {\n const {\n audioBuffer,\n startSample,\n durationSamples = audioBuffer.length, // Full buffer by default\n offsetSamples = 0,\n gain = 1.0,\n name,\n color,\n fadeIn,\n fadeOut,\n } = options;\n\n return {\n id: generateId(),\n audioBuffer,\n startSample,\n durationSamples,\n offsetSamples,\n gain,\n name,\n color,\n fadeIn,\n fadeOut,\n };\n}\n\n/**\n * Creates a new AudioClip from time-based values (convenience function)\n * Converts seconds to samples using the audioBuffer's sampleRate\n */\nexport function createClipFromSeconds(options: CreateClipOptionsSeconds): AudioClip {\n const {\n audioBuffer,\n startTime,\n duration = audioBuffer.duration,\n offset = 0,\n gain = 1.0,\n name,\n color,\n fadeIn,\n fadeOut,\n } = options;\n\n const sampleRate = audioBuffer.sampleRate;\n\n return createClip({\n audioBuffer,\n startSample: Math.round(startTime * sampleRate),\n durationSamples: Math.round(duration * sampleRate),\n offsetSamples: Math.round(offset * sampleRate),\n gain,\n name,\n color,\n fadeIn,\n fadeOut,\n });\n}\n\n/**\n * Creates a new ClipTrack with sensible defaults\n */\nexport function createTrack(options: CreateTrackOptions): ClipTrack {\n const {\n name,\n clips = [],\n muted = false,\n soloed = false,\n volume = 1.0,\n pan = 0,\n color,\n height,\n } = options;\n\n return {\n id: generateId(),\n name,\n clips,\n muted,\n soloed,\n volume,\n pan,\n color,\n height,\n };\n}\n\n/**\n * Creates a new Timeline with sensible defaults\n */\nexport function createTimeline(\n tracks: ClipTrack[],\n sampleRate: number = 44100,\n options?: {\n name?: string;\n tempo?: number;\n timeSignature?: { numerator: number; denominator: number };\n }\n): Timeline {\n // Calculate total duration from all clips across all tracks (in seconds)\n const durationSamples = tracks.reduce((maxSamples, track) => {\n const trackSamples = track.clips.reduce((max, clip) => {\n return Math.max(max, clip.startSample + clip.durationSamples);\n }, 0);\n return Math.max(maxSamples, trackSamples);\n }, 0);\n\n const duration = durationSamples / sampleRate;\n\n return {\n tracks,\n duration,\n sampleRate,\n name: options?.name,\n tempo: options?.tempo,\n timeSignature: options?.timeSignature,\n };\n}\n\n/**\n * Generates a unique ID for clips and tracks\n */\nfunction generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n}\n\n/**\n * Utility: Get all clips within a sample range\n */\nexport function getClipsInRange(\n track: ClipTrack,\n startSample: number,\n endSample: number\n): AudioClip[] {\n return track.clips.filter((clip) => {\n const clipEnd = clip.startSample + clip.durationSamples;\n // Clip overlaps with range if:\n // - Clip starts before range ends AND\n // - Clip ends after range starts\n return clip.startSample < endSample && clipEnd > startSample;\n });\n}\n\n/**\n * Utility: Get all clips at a specific sample position\n */\nexport function getClipsAtSample(track: ClipTrack, sample: number): AudioClip[] {\n return track.clips.filter((clip) => {\n const clipEnd = clip.startSample + clip.durationSamples;\n return sample >= clip.startSample && sample < clipEnd;\n });\n}\n\n/**\n * Utility: Check if two clips overlap\n */\nexport function clipsOverlap(clip1: AudioClip, clip2: AudioClip): boolean {\n const clip1End = clip1.startSample + clip1.durationSamples;\n const clip2End = clip2.startSample + clip2.durationSamples;\n\n return clip1.startSample < clip2End && clip1End > clip2.startSample;\n}\n\n/**\n * Utility: Sort clips by startSample\n */\nexport function sortClipsByTime(clips: AudioClip[]): AudioClip[] {\n return [...clips].sort((a, b) => a.startSample - b.startSample);\n}\n\n/**\n * Utility: Find gaps between clips (silent regions)\n */\nexport interface Gap {\n startSample: number;\n endSample: number;\n durationSamples: number;\n}\n\nexport function findGaps(track: ClipTrack): Gap[] {\n if (track.clips.length === 0) return [];\n\n const sorted = sortClipsByTime(track.clips);\n const gaps: Gap[] = [];\n\n for (let i = 0; i < sorted.length - 1; i++) {\n const currentClipEnd = sorted[i].startSample + sorted[i].durationSamples;\n const nextClipStart = sorted[i + 1].startSample;\n\n if (nextClipStart > currentClipEnd) {\n gaps.push({\n startSample: currentClipEnd,\n endSample: nextClipStart,\n durationSamples: nextClipStart - currentClipEnd,\n });\n }\n }\n\n return gaps;\n}\n","export interface WaveformConfig {\n sampleRate: number;\n samplesPerPixel: number;\n waveHeight?: number;\n waveOutlineColor?: string;\n waveFillColor?: string;\n waveProgressColor?: string;\n}\n\nexport interface AudioBuffer {\n length: number;\n duration: number;\n numberOfChannels: number;\n sampleRate: number;\n getChannelData(channel: number): Float32Array;\n}\n\nexport interface Track {\n id: string;\n name: string;\n src?: string | AudioBuffer; // Support both URL strings and AudioBuffer objects\n gain: number;\n muted: boolean;\n soloed: boolean;\n stereoPan: number;\n startTime: number;\n endTime?: number;\n fadeIn?: Fade;\n fadeOut?: Fade;\n cueIn?: number;\n cueOut?: number;\n}\n\n/**\n * Simple fade configuration\n */\nexport interface Fade {\n /** Duration of the fade in seconds */\n duration: number;\n /** Type of fade curve (default: 'linear') */\n type?: FadeType;\n}\n\nexport type FadeType = 'logarithmic' | 'linear' | 'sCurve' | 'exponential';\n\nexport interface PlaylistConfig {\n samplesPerPixel?: number;\n waveHeight?: number;\n container?: HTMLElement;\n isAutomaticScroll?: boolean;\n timescale?: boolean;\n colors?: {\n waveOutlineColor?: string;\n waveFillColor?: string;\n waveProgressColor?: string;\n };\n controls?: {\n show?: boolean;\n width?: number;\n };\n zoomLevels?: number[];\n}\n\nexport interface PlayoutState {\n isPlaying: boolean;\n isPaused: boolean;\n cursor: number;\n duration: number;\n}\n\nexport interface TimeSelection {\n start: number;\n end: number;\n}\n\nexport enum InteractionState {\n Cursor = 'cursor',\n Select = 'select',\n Shift = 'shift',\n FadeIn = 'fadein',\n FadeOut = 'fadeout',\n}\n\n// Export clip-based model types\nexport * from './clip';\n","export function samplesToSeconds(samples: number, sampleRate: number): number {\n return samples / sampleRate;\n}\n\nexport function secondsToSamples(seconds: number, sampleRate: number): number {\n return Math.ceil(seconds * sampleRate);\n}\n\nexport function samplesToPixels(samples: number, samplesPerPixel: number): number {\n return Math.floor(samples / samplesPerPixel);\n}\n\nexport function pixelsToSamples(pixels: number, samplesPerPixel: number): number {\n return Math.floor(pixels * samplesPerPixel);\n}\n\nexport function pixelsToSeconds(\n pixels: number,\n samplesPerPixel: number,\n sampleRate: number\n): number {\n return (pixels * samplesPerPixel) / sampleRate;\n}\n\nexport function secondsToPixels(\n seconds: number,\n samplesPerPixel: number,\n sampleRate: number\n): number {\n return Math.ceil((seconds * sampleRate) / samplesPerPixel);\n}\n"],"mappings":"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","names":["InteractionState"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,181 @@
1
+ // src/types/clip.ts
2
+ function createClip(options) {
3
+ const {
4
+ audioBuffer,
5
+ startSample,
6
+ durationSamples = audioBuffer.length,
7
+ // Full buffer by default
8
+ offsetSamples = 0,
9
+ gain = 1,
10
+ name,
11
+ color,
12
+ fadeIn,
13
+ fadeOut
14
+ } = options;
15
+ return {
16
+ id: generateId(),
17
+ audioBuffer,
18
+ startSample,
19
+ durationSamples,
20
+ offsetSamples,
21
+ gain,
22
+ name,
23
+ color,
24
+ fadeIn,
25
+ fadeOut
26
+ };
27
+ }
28
+ function createClipFromSeconds(options) {
29
+ const {
30
+ audioBuffer,
31
+ startTime,
32
+ duration = audioBuffer.duration,
33
+ offset = 0,
34
+ gain = 1,
35
+ name,
36
+ color,
37
+ fadeIn,
38
+ fadeOut
39
+ } = options;
40
+ const sampleRate = audioBuffer.sampleRate;
41
+ return createClip({
42
+ audioBuffer,
43
+ startSample: Math.round(startTime * sampleRate),
44
+ durationSamples: Math.round(duration * sampleRate),
45
+ offsetSamples: Math.round(offset * sampleRate),
46
+ gain,
47
+ name,
48
+ color,
49
+ fadeIn,
50
+ fadeOut
51
+ });
52
+ }
53
+ function createTrack(options) {
54
+ const {
55
+ name,
56
+ clips = [],
57
+ muted = false,
58
+ soloed = false,
59
+ volume = 1,
60
+ pan = 0,
61
+ color,
62
+ height
63
+ } = options;
64
+ return {
65
+ id: generateId(),
66
+ name,
67
+ clips,
68
+ muted,
69
+ soloed,
70
+ volume,
71
+ pan,
72
+ color,
73
+ height
74
+ };
75
+ }
76
+ function createTimeline(tracks, sampleRate = 44100, options) {
77
+ const durationSamples = tracks.reduce((maxSamples, track) => {
78
+ const trackSamples = track.clips.reduce((max, clip) => {
79
+ return Math.max(max, clip.startSample + clip.durationSamples);
80
+ }, 0);
81
+ return Math.max(maxSamples, trackSamples);
82
+ }, 0);
83
+ const duration = durationSamples / sampleRate;
84
+ return {
85
+ tracks,
86
+ duration,
87
+ sampleRate,
88
+ name: options?.name,
89
+ tempo: options?.tempo,
90
+ timeSignature: options?.timeSignature
91
+ };
92
+ }
93
+ function generateId() {
94
+ return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
95
+ }
96
+ function getClipsInRange(track, startSample, endSample) {
97
+ return track.clips.filter((clip) => {
98
+ const clipEnd = clip.startSample + clip.durationSamples;
99
+ return clip.startSample < endSample && clipEnd > startSample;
100
+ });
101
+ }
102
+ function getClipsAtSample(track, sample) {
103
+ return track.clips.filter((clip) => {
104
+ const clipEnd = clip.startSample + clip.durationSamples;
105
+ return sample >= clip.startSample && sample < clipEnd;
106
+ });
107
+ }
108
+ function clipsOverlap(clip1, clip2) {
109
+ const clip1End = clip1.startSample + clip1.durationSamples;
110
+ const clip2End = clip2.startSample + clip2.durationSamples;
111
+ return clip1.startSample < clip2End && clip1End > clip2.startSample;
112
+ }
113
+ function sortClipsByTime(clips) {
114
+ return [...clips].sort((a, b) => a.startSample - b.startSample);
115
+ }
116
+ function findGaps(track) {
117
+ if (track.clips.length === 0) return [];
118
+ const sorted = sortClipsByTime(track.clips);
119
+ const gaps = [];
120
+ for (let i = 0; i < sorted.length - 1; i++) {
121
+ const currentClipEnd = sorted[i].startSample + sorted[i].durationSamples;
122
+ const nextClipStart = sorted[i + 1].startSample;
123
+ if (nextClipStart > currentClipEnd) {
124
+ gaps.push({
125
+ startSample: currentClipEnd,
126
+ endSample: nextClipStart,
127
+ durationSamples: nextClipStart - currentClipEnd
128
+ });
129
+ }
130
+ }
131
+ return gaps;
132
+ }
133
+
134
+ // src/types/index.ts
135
+ var InteractionState = /* @__PURE__ */ ((InteractionState2) => {
136
+ InteractionState2["Cursor"] = "cursor";
137
+ InteractionState2["Select"] = "select";
138
+ InteractionState2["Shift"] = "shift";
139
+ InteractionState2["FadeIn"] = "fadein";
140
+ InteractionState2["FadeOut"] = "fadeout";
141
+ return InteractionState2;
142
+ })(InteractionState || {});
143
+
144
+ // src/utils/conversions.ts
145
+ function samplesToSeconds(samples, sampleRate) {
146
+ return samples / sampleRate;
147
+ }
148
+ function secondsToSamples(seconds, sampleRate) {
149
+ return Math.ceil(seconds * sampleRate);
150
+ }
151
+ function samplesToPixels(samples, samplesPerPixel) {
152
+ return Math.floor(samples / samplesPerPixel);
153
+ }
154
+ function pixelsToSamples(pixels, samplesPerPixel) {
155
+ return Math.floor(pixels * samplesPerPixel);
156
+ }
157
+ function pixelsToSeconds(pixels, samplesPerPixel, sampleRate) {
158
+ return pixels * samplesPerPixel / sampleRate;
159
+ }
160
+ function secondsToPixels(seconds, samplesPerPixel, sampleRate) {
161
+ return Math.ceil(seconds * sampleRate / samplesPerPixel);
162
+ }
163
+ export {
164
+ InteractionState,
165
+ clipsOverlap,
166
+ createClip,
167
+ createClipFromSeconds,
168
+ createTimeline,
169
+ createTrack,
170
+ findGaps,
171
+ getClipsAtSample,
172
+ getClipsInRange,
173
+ pixelsToSamples,
174
+ pixelsToSeconds,
175
+ samplesToPixels,
176
+ samplesToSeconds,
177
+ secondsToPixels,
178
+ secondsToSamples,
179
+ sortClipsByTime
180
+ };
181
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types/clip.ts","../src/types/index.ts","../src/utils/conversions.ts"],"sourcesContent":["/**\n * Clip-Based Model Types\n *\n * These types support a professional multi-track editing model where:\n * - Each track can contain multiple audio clips\n * - Clips can be positioned anywhere on the timeline\n * - Clips have independent trim points (offset/duration)\n * - Gaps between clips are silent\n * - Clips can overlap (for crossfades)\n */\n\nimport { Fade } from './index';\n\n/**\n * Generic effects function type for track-level audio processing.\n *\n * The actual implementation receives Tone.js audio nodes. Using generic types\n * here to avoid circular dependencies with the playout package.\n *\n * @param graphEnd - The end of the track's audio graph (Tone.js Gain node)\n * @param destination - Where to connect the effects output (Tone.js ToneAudioNode)\n * @param isOffline - Whether rendering offline (for export)\n * @returns Optional cleanup function called when track is disposed\n *\n * @example\n * ```typescript\n * const trackEffects: TrackEffectsFunction = (graphEnd, destination, isOffline) => {\n * const reverb = new Tone.Reverb({ decay: 1.5 });\n * graphEnd.connect(reverb);\n * reverb.connect(destination);\n *\n * return () => {\n * reverb.dispose();\n * };\n * };\n * ```\n */\nexport type TrackEffectsFunction = (\n graphEnd: unknown,\n destination: unknown,\n isOffline: boolean\n) => void | (() => void);\n\n/**\n * Represents a single audio clip on the timeline\n *\n * IMPORTANT: All positions/durations are stored as SAMPLE COUNTS (integers)\n * to avoid floating-point precision errors. Convert to seconds only when\n * needed for playback using: seconds = samples / sampleRate\n */\nexport interface AudioClip {\n /** Unique identifier for this clip */\n id: string;\n\n /** The audio buffer containing the audio data */\n audioBuffer: AudioBuffer;\n\n /** Position on timeline where this clip starts (in samples at timeline sampleRate) */\n startSample: number;\n\n /** Duration of this clip (in samples) - how much of the audio buffer to play */\n durationSamples: number;\n\n /** Offset into the audio buffer where playback starts (in samples) - the \"trim start\" point */\n offsetSamples: number;\n\n /** Optional fade in effect */\n fadeIn?: Fade;\n\n /** Optional fade out effect */\n fadeOut?: Fade;\n\n /** Clip-specific gain/volume multiplier (0.0 to 1.0+) */\n gain: number;\n\n /** Optional label/name for this clip */\n name?: string;\n\n /** Optional color for visual distinction */\n color?: string;\n}\n\n/**\n * Represents a track containing multiple audio clips\n */\nexport interface ClipTrack {\n /** Unique identifier for this track */\n id: string;\n\n /** Display name for this track */\n name: string;\n\n /** Array of audio clips on this track */\n clips: AudioClip[];\n\n /** Whether this track is muted */\n muted: boolean;\n\n /** Whether this track is soloed */\n soloed: boolean;\n\n /** Track volume (0.0 to 1.0+) */\n volume: number;\n\n /** Stereo pan (-1.0 = left, 0 = center, 1.0 = right) */\n pan: number;\n\n /** Optional track color for visual distinction */\n color?: string;\n\n /** Track height in pixels (for UI) */\n height?: number;\n\n /** Optional effects function for this track */\n effects?: TrackEffectsFunction;\n}\n\n/**\n * Represents the entire timeline/project\n */\nexport interface Timeline {\n /** All tracks in the timeline */\n tracks: ClipTrack[];\n\n /** Total timeline duration in seconds */\n duration: number;\n\n /** Sample rate for all audio (typically 44100 or 48000) */\n sampleRate: number;\n\n /** Optional project name */\n name?: string;\n\n /** Optional tempo (BPM) for grid snapping */\n tempo?: number;\n\n /** Optional time signature for grid snapping */\n timeSignature?: {\n numerator: number;\n denominator: number;\n };\n}\n\n/**\n * Options for creating a new audio clip (using sample counts)\n */\nexport interface CreateClipOptions {\n audioBuffer: AudioBuffer;\n startSample: number; // Position on timeline (in samples)\n durationSamples?: number; // Defaults to full buffer duration (in samples)\n offsetSamples?: number; // Defaults to 0\n gain?: number; // Defaults to 1.0\n name?: string;\n color?: string;\n fadeIn?: Fade;\n fadeOut?: Fade;\n}\n\n/**\n * Options for creating a new audio clip (using seconds for convenience)\n */\nexport interface CreateClipOptionsSeconds {\n audioBuffer: AudioBuffer;\n startTime: number; // Position on timeline (in seconds)\n duration?: number; // Defaults to full buffer duration (in seconds)\n offset?: number; // Defaults to 0 (in seconds)\n gain?: number; // Defaults to 1.0\n name?: string;\n color?: string;\n fadeIn?: Fade;\n fadeOut?: Fade;\n}\n\n/**\n * Options for creating a new track\n */\nexport interface CreateTrackOptions {\n name: string;\n clips?: AudioClip[];\n muted?: boolean;\n soloed?: boolean;\n volume?: number;\n pan?: number;\n color?: string;\n height?: number;\n}\n\n/**\n * Creates a new AudioClip with sensible defaults (using sample counts)\n */\nexport function createClip(options: CreateClipOptions): AudioClip {\n const {\n audioBuffer,\n startSample,\n durationSamples = audioBuffer.length, // Full buffer by default\n offsetSamples = 0,\n gain = 1.0,\n name,\n color,\n fadeIn,\n fadeOut,\n } = options;\n\n return {\n id: generateId(),\n audioBuffer,\n startSample,\n durationSamples,\n offsetSamples,\n gain,\n name,\n color,\n fadeIn,\n fadeOut,\n };\n}\n\n/**\n * Creates a new AudioClip from time-based values (convenience function)\n * Converts seconds to samples using the audioBuffer's sampleRate\n */\nexport function createClipFromSeconds(options: CreateClipOptionsSeconds): AudioClip {\n const {\n audioBuffer,\n startTime,\n duration = audioBuffer.duration,\n offset = 0,\n gain = 1.0,\n name,\n color,\n fadeIn,\n fadeOut,\n } = options;\n\n const sampleRate = audioBuffer.sampleRate;\n\n return createClip({\n audioBuffer,\n startSample: Math.round(startTime * sampleRate),\n durationSamples: Math.round(duration * sampleRate),\n offsetSamples: Math.round(offset * sampleRate),\n gain,\n name,\n color,\n fadeIn,\n fadeOut,\n });\n}\n\n/**\n * Creates a new ClipTrack with sensible defaults\n */\nexport function createTrack(options: CreateTrackOptions): ClipTrack {\n const {\n name,\n clips = [],\n muted = false,\n soloed = false,\n volume = 1.0,\n pan = 0,\n color,\n height,\n } = options;\n\n return {\n id: generateId(),\n name,\n clips,\n muted,\n soloed,\n volume,\n pan,\n color,\n height,\n };\n}\n\n/**\n * Creates a new Timeline with sensible defaults\n */\nexport function createTimeline(\n tracks: ClipTrack[],\n sampleRate: number = 44100,\n options?: {\n name?: string;\n tempo?: number;\n timeSignature?: { numerator: number; denominator: number };\n }\n): Timeline {\n // Calculate total duration from all clips across all tracks (in seconds)\n const durationSamples = tracks.reduce((maxSamples, track) => {\n const trackSamples = track.clips.reduce((max, clip) => {\n return Math.max(max, clip.startSample + clip.durationSamples);\n }, 0);\n return Math.max(maxSamples, trackSamples);\n }, 0);\n\n const duration = durationSamples / sampleRate;\n\n return {\n tracks,\n duration,\n sampleRate,\n name: options?.name,\n tempo: options?.tempo,\n timeSignature: options?.timeSignature,\n };\n}\n\n/**\n * Generates a unique ID for clips and tracks\n */\nfunction generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n}\n\n/**\n * Utility: Get all clips within a sample range\n */\nexport function getClipsInRange(\n track: ClipTrack,\n startSample: number,\n endSample: number\n): AudioClip[] {\n return track.clips.filter((clip) => {\n const clipEnd = clip.startSample + clip.durationSamples;\n // Clip overlaps with range if:\n // - Clip starts before range ends AND\n // - Clip ends after range starts\n return clip.startSample < endSample && clipEnd > startSample;\n });\n}\n\n/**\n * Utility: Get all clips at a specific sample position\n */\nexport function getClipsAtSample(track: ClipTrack, sample: number): AudioClip[] {\n return track.clips.filter((clip) => {\n const clipEnd = clip.startSample + clip.durationSamples;\n return sample >= clip.startSample && sample < clipEnd;\n });\n}\n\n/**\n * Utility: Check if two clips overlap\n */\nexport function clipsOverlap(clip1: AudioClip, clip2: AudioClip): boolean {\n const clip1End = clip1.startSample + clip1.durationSamples;\n const clip2End = clip2.startSample + clip2.durationSamples;\n\n return clip1.startSample < clip2End && clip1End > clip2.startSample;\n}\n\n/**\n * Utility: Sort clips by startSample\n */\nexport function sortClipsByTime(clips: AudioClip[]): AudioClip[] {\n return [...clips].sort((a, b) => a.startSample - b.startSample);\n}\n\n/**\n * Utility: Find gaps between clips (silent regions)\n */\nexport interface Gap {\n startSample: number;\n endSample: number;\n durationSamples: number;\n}\n\nexport function findGaps(track: ClipTrack): Gap[] {\n if (track.clips.length === 0) return [];\n\n const sorted = sortClipsByTime(track.clips);\n const gaps: Gap[] = [];\n\n for (let i = 0; i < sorted.length - 1; i++) {\n const currentClipEnd = sorted[i].startSample + sorted[i].durationSamples;\n const nextClipStart = sorted[i + 1].startSample;\n\n if (nextClipStart > currentClipEnd) {\n gaps.push({\n startSample: currentClipEnd,\n endSample: nextClipStart,\n durationSamples: nextClipStart - currentClipEnd,\n });\n }\n }\n\n return gaps;\n}\n","export interface WaveformConfig {\n sampleRate: number;\n samplesPerPixel: number;\n waveHeight?: number;\n waveOutlineColor?: string;\n waveFillColor?: string;\n waveProgressColor?: string;\n}\n\nexport interface AudioBuffer {\n length: number;\n duration: number;\n numberOfChannels: number;\n sampleRate: number;\n getChannelData(channel: number): Float32Array;\n}\n\nexport interface Track {\n id: string;\n name: string;\n src?: string | AudioBuffer; // Support both URL strings and AudioBuffer objects\n gain: number;\n muted: boolean;\n soloed: boolean;\n stereoPan: number;\n startTime: number;\n endTime?: number;\n fadeIn?: Fade;\n fadeOut?: Fade;\n cueIn?: number;\n cueOut?: number;\n}\n\n/**\n * Simple fade configuration\n */\nexport interface Fade {\n /** Duration of the fade in seconds */\n duration: number;\n /** Type of fade curve (default: 'linear') */\n type?: FadeType;\n}\n\nexport type FadeType = 'logarithmic' | 'linear' | 'sCurve' | 'exponential';\n\nexport interface PlaylistConfig {\n samplesPerPixel?: number;\n waveHeight?: number;\n container?: HTMLElement;\n isAutomaticScroll?: boolean;\n timescale?: boolean;\n colors?: {\n waveOutlineColor?: string;\n waveFillColor?: string;\n waveProgressColor?: string;\n };\n controls?: {\n show?: boolean;\n width?: number;\n };\n zoomLevels?: number[];\n}\n\nexport interface PlayoutState {\n isPlaying: boolean;\n isPaused: boolean;\n cursor: number;\n duration: number;\n}\n\nexport interface TimeSelection {\n start: number;\n end: number;\n}\n\nexport enum InteractionState {\n Cursor = 'cursor',\n Select = 'select',\n Shift = 'shift',\n FadeIn = 'fadein',\n FadeOut = 'fadeout',\n}\n\n// Export clip-based model types\nexport * from './clip';\n","export function samplesToSeconds(samples: number, sampleRate: number): number {\n return samples / sampleRate;\n}\n\nexport function secondsToSamples(seconds: number, sampleRate: number): number {\n return Math.ceil(seconds * sampleRate);\n}\n\nexport function samplesToPixels(samples: number, samplesPerPixel: number): number {\n return Math.floor(samples / samplesPerPixel);\n}\n\nexport function pixelsToSamples(pixels: number, samplesPerPixel: number): number {\n return Math.floor(pixels * samplesPerPixel);\n}\n\nexport function pixelsToSeconds(\n pixels: number,\n samplesPerPixel: number,\n sampleRate: number\n): number {\n return (pixels * samplesPerPixel) / sampleRate;\n}\n\nexport function secondsToPixels(\n seconds: number,\n samplesPerPixel: number,\n sampleRate: number\n): number {\n return Math.ceil((seconds * sampleRate) / samplesPerPixel);\n}\n"],"mappings":"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","names":["InteractionState"]}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@waveform-playlist/core",
3
+ "version": "5.0.0-alpha.0",
4
+ "description": "Core types, interfaces and utilities for waveform-playlist",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "sideEffects": false,
16
+ "keywords": [
17
+ "waveform",
18
+ "audio",
19
+ "webaudio",
20
+ "waveform-playlist",
21
+ "types"
22
+ ],
23
+ "author": "Naomi Aro",
24
+ "license": "MIT",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/naomiaro/waveform-playlist.git",
28
+ "directory": "packages/core"
29
+ },
30
+ "homepage": "https://naomiaro.github.io/waveform-playlist",
31
+ "bugs": {
32
+ "url": "https://github.com/naomiaro/waveform-playlist/issues"
33
+ },
34
+ "files": [
35
+ "dist",
36
+ "README.md"
37
+ ],
38
+ "devDependencies": {
39
+ "tsup": "^8.0.1",
40
+ "typescript": "^5.3.3"
41
+ },
42
+ "dependencies": {},
43
+ "scripts": {
44
+ "build": "tsup",
45
+ "dev": "tsup --watch",
46
+ "typecheck": "tsc --noEmit"
47
+ }
48
+ }