@waveform-playlist/media-element-playout 5.0.0-alpha.14

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.
package/README.md ADDED
@@ -0,0 +1,137 @@
1
+ # @waveform-playlist/media-element-playout
2
+
3
+ A lightweight, HTMLMediaElement-based playout engine for waveform-playlist with **pitch-preserving playback rate control**.
4
+
5
+ ## Features
6
+
7
+ - **Pitch-preserving playback rate** (0.5x - 2.0x) - uses browser's built-in time-stretching
8
+ - **Pre-computed peaks** - no AudioBuffer decoding required, instant visualization
9
+ - **Lightweight** - no Tone.js dependency
10
+ - **Simple API** - designed for single-track playback use cases
11
+
12
+ ## When to Use
13
+
14
+ Use `MediaElementPlayout` when you need:
15
+ - Playback speed control for language learning, podcasts, etc.
16
+ - Single-track playback with minimal overhead
17
+ - Quick load times with pre-computed peaks
18
+
19
+ Use `TonePlayout` from `@waveform-playlist/playout` when you need:
20
+ - Multi-track mixing and editing
21
+ - Clip-level effects and fades
22
+ - Precise sample-accurate timing
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ npm install @waveform-playlist/media-element-playout
28
+ ```
29
+
30
+ ## Usage
31
+
32
+ ```typescript
33
+ import { MediaElementPlayout } from '@waveform-playlist/media-element-playout';
34
+ import WaveformData from 'waveform-data';
35
+
36
+ // Load pre-computed peaks
37
+ const response = await fetch('/audio/podcast.dat');
38
+ const arrayBuffer = await response.arrayBuffer();
39
+ const peaks = WaveformData.create(arrayBuffer);
40
+
41
+ // Create playout
42
+ const playout = new MediaElementPlayout({
43
+ masterVolume: 1.0,
44
+ playbackRate: 1.0,
45
+ });
46
+
47
+ // Add a track
48
+ playout.addTrack({
49
+ source: '/audio/podcast.mp3', // URL or Blob URL
50
+ peaks: peaks,
51
+ name: 'Podcast Episode 1',
52
+ });
53
+
54
+ // Control playback
55
+ playout.play(0); // Play from beginning
56
+ playout.setPlaybackRate(0.75); // Slow down to 75% speed (pitch preserved)
57
+ playout.pause();
58
+ playout.seekTo(30); // Seek to 30 seconds
59
+ playout.play(); // Resume
60
+
61
+ // Clean up
62
+ playout.dispose();
63
+ ```
64
+
65
+ ## API
66
+
67
+ ### MediaElementPlayout
68
+
69
+ ```typescript
70
+ interface MediaElementPlayoutOptions {
71
+ masterVolume?: number; // 0.0 to 1.0 (default: 1.0)
72
+ playbackRate?: number; // 0.5 to 2.0 (default: 1.0)
73
+ }
74
+
75
+ class MediaElementPlayout {
76
+ // Lifecycle
77
+ init(): Promise<void>; // No-op for media element
78
+ dispose(): void;
79
+
80
+ // Track management
81
+ addTrack(options: MediaElementTrackOptions): MediaElementTrack;
82
+ removeTrack(trackId: string): void;
83
+ getTrack(trackId: string): MediaElementTrack | undefined;
84
+
85
+ // Playback
86
+ play(when?: number, offset?: number, duration?: number): void;
87
+ pause(): void;
88
+ stop(): void;
89
+ seekTo(time: number): void;
90
+ getCurrentTime(): number;
91
+
92
+ // Volume & Rate
93
+ setMasterVolume(volume: number): void;
94
+ setPlaybackRate(rate: number): void; // 0.5 to 2.0, pitch preserved
95
+
96
+ // State
97
+ readonly isPlaying: boolean;
98
+ readonly duration: number;
99
+ readonly playbackRate: number;
100
+ }
101
+ ```
102
+
103
+ ### MediaElementTrack
104
+
105
+ ```typescript
106
+ interface MediaElementTrackOptions {
107
+ source: string | HTMLAudioElement; // URL or audio element
108
+ peaks: WaveformDataObject; // Pre-computed peaks
109
+ id?: string;
110
+ name?: string;
111
+ volume?: number;
112
+ playbackRate?: number;
113
+ }
114
+ ```
115
+
116
+ ## Generating Peaks
117
+
118
+ Use [audiowaveform](https://github.com/bbc/audiowaveform) or [waveform-data.js](https://github.com/bbc/waveform-data.js) to pre-compute peaks:
119
+
120
+ ```bash
121
+ # Generate peaks file with audiowaveform
122
+ audiowaveform -i audio.mp3 -o peaks.dat -b 16
123
+ ```
124
+
125
+ ## Browser Support
126
+
127
+ Pitch-preserving playback rate is supported in:
128
+ - Chrome 77+
129
+ - Firefox 20+
130
+ - Safari 14.1+
131
+ - Edge 79+
132
+
133
+ Older browsers will still work but may change pitch with speed.
134
+
135
+ ## License
136
+
137
+ MIT
@@ -0,0 +1,235 @@
1
+ import { WaveformDataObject } from '@waveform-playlist/core';
2
+
3
+ interface MediaElementTrackOptions {
4
+ /** The audio source - can be a URL, Blob URL, or HTMLAudioElement */
5
+ source: string | HTMLAudioElement;
6
+ /** Pre-computed waveform data for visualization (required - no AudioBuffer decoding) */
7
+ peaks: WaveformDataObject;
8
+ /** Track ID */
9
+ id?: string;
10
+ /** Track name for display */
11
+ name?: string;
12
+ /** Initial volume (0.0 to 1.0) */
13
+ volume?: number;
14
+ /** Initial playback rate (0.5 to 2.0, pitch preserved) */
15
+ playbackRate?: number;
16
+ }
17
+ /**
18
+ * Single-track playback using HTMLAudioElement.
19
+ *
20
+ * Benefits over AudioBuffer/Tone.js:
21
+ * - Pitch-preserving playback rate (0.5x - 2.0x) via browser's built-in algorithm
22
+ * - No AudioBuffer decoding required (uses pre-computed peaks for visualization)
23
+ * - Simpler, lighter-weight for single-track use cases
24
+ *
25
+ * Limitations:
26
+ * - Single track only (no multi-track mixing)
27
+ * - No clip-level effects or fades (track-level volume only)
28
+ * - Relies on browser's time-stretching quality
29
+ */
30
+ declare class MediaElementTrack {
31
+ private audioElement;
32
+ private ownsElement;
33
+ private _peaks;
34
+ private _id;
35
+ private _name;
36
+ private _playbackRate;
37
+ private onStopCallback?;
38
+ private onTimeUpdateCallback?;
39
+ constructor(options: MediaElementTrackOptions);
40
+ private handleEnded;
41
+ private handleTimeUpdate;
42
+ /**
43
+ * Start playback from a specific time
44
+ */
45
+ play(offset?: number): void;
46
+ /**
47
+ * Pause playback
48
+ */
49
+ pause(): void;
50
+ /**
51
+ * Stop playback and reset to beginning
52
+ */
53
+ stop(): void;
54
+ /**
55
+ * Seek to a specific time
56
+ */
57
+ seekTo(time: number): void;
58
+ /**
59
+ * Set volume (0.0 to 1.0)
60
+ */
61
+ setVolume(volume: number): void;
62
+ /**
63
+ * Set playback rate (0.5 to 2.0, pitch preserved)
64
+ */
65
+ setPlaybackRate(rate: number): void;
66
+ /**
67
+ * Set muted state
68
+ */
69
+ setMuted(muted: boolean): void;
70
+ /**
71
+ * Set callback for when playback ends
72
+ */
73
+ setOnStopCallback(callback: () => void): void;
74
+ /**
75
+ * Set callback for time updates
76
+ */
77
+ setOnTimeUpdateCallback(callback: (time: number) => void): void;
78
+ /**
79
+ * Clean up resources
80
+ */
81
+ dispose(): void;
82
+ get id(): string;
83
+ get name(): string;
84
+ get peaks(): WaveformDataObject;
85
+ get currentTime(): number;
86
+ get duration(): number;
87
+ get isPlaying(): boolean;
88
+ get volume(): number;
89
+ get playbackRate(): number;
90
+ get muted(): boolean;
91
+ /**
92
+ * Get the underlying audio element (for advanced use cases)
93
+ */
94
+ get element(): HTMLAudioElement;
95
+ }
96
+
97
+ interface MediaElementPlayoutOptions {
98
+ /** Initial master volume (0.0 to 1.0) */
99
+ masterVolume?: number;
100
+ /** Initial playback rate (0.5 to 2.0) */
101
+ playbackRate?: number;
102
+ }
103
+ /**
104
+ * Single-track playout engine using HTMLAudioElement.
105
+ *
106
+ * This is a lightweight alternative to TonePlayout for single-track use cases
107
+ * that need pitch-preserving playback rate control.
108
+ *
109
+ * Key features:
110
+ * - Pitch-preserving playback rate (0.5x - 2.0x)
111
+ * - Uses pre-computed peaks (no AudioBuffer required)
112
+ * - Simpler API for single-track playback
113
+ *
114
+ * Limitations:
115
+ * - Single track only - will warn if multiple tracks added
116
+ * - No clip-level effects or crossfades
117
+ * - No multi-track mixing
118
+ *
119
+ * For multi-track editing, use TonePlayout from @waveform-playlist/playout instead.
120
+ */
121
+ declare class MediaElementPlayout {
122
+ private track;
123
+ private _masterVolume;
124
+ private _playbackRate;
125
+ private _isPlaying;
126
+ private onPlaybackCompleteCallback?;
127
+ constructor(options?: MediaElementPlayoutOptions);
128
+ /**
129
+ * Initialize the playout engine.
130
+ * For MediaElementPlayout this is a no-op (no AudioContext to start).
131
+ */
132
+ init(): Promise<void>;
133
+ /**
134
+ * Add a track to the playout.
135
+ * Note: Only one track is supported. Adding a second track will dispose the first.
136
+ */
137
+ addTrack(options: MediaElementTrackOptions): MediaElementTrack;
138
+ /**
139
+ * Remove a track by ID.
140
+ */
141
+ removeTrack(trackId: string): void;
142
+ /**
143
+ * Get a track by ID.
144
+ */
145
+ getTrack(trackId: string): MediaElementTrack | undefined;
146
+ /**
147
+ * Start playback.
148
+ * @param _when - Ignored (HTMLAudioElement doesn't support scheduled start)
149
+ * @param offset - Start position in seconds
150
+ * @param duration - Duration to play in seconds (optional)
151
+ */
152
+ play(_when?: number, offset?: number, duration?: number): void;
153
+ /**
154
+ * Pause playback.
155
+ */
156
+ pause(): void;
157
+ /**
158
+ * Stop playback and reset to start.
159
+ */
160
+ stop(): void;
161
+ /**
162
+ * Seek to a specific time.
163
+ */
164
+ seekTo(time: number): void;
165
+ /**
166
+ * Get current playback time.
167
+ */
168
+ getCurrentTime(): number;
169
+ /**
170
+ * Set master volume.
171
+ */
172
+ setMasterVolume(volume: number): void;
173
+ /**
174
+ * Set playback rate (0.5 to 2.0, pitch preserved).
175
+ */
176
+ setPlaybackRate(rate: number): void;
177
+ /**
178
+ * Set mute state for a track.
179
+ */
180
+ setMute(trackId: string, muted: boolean): void;
181
+ /**
182
+ * Set solo state for a track.
183
+ * Note: With single track, solo is effectively the same as unmute.
184
+ */
185
+ setSolo(_trackId: string, _soloed: boolean): void;
186
+ /**
187
+ * Set callback for when playback completes.
188
+ */
189
+ setOnPlaybackComplete(callback: () => void): void;
190
+ /**
191
+ * Clean up resources.
192
+ */
193
+ dispose(): void;
194
+ get isPlaying(): boolean;
195
+ get masterVolume(): number;
196
+ get playbackRate(): number;
197
+ get duration(): number;
198
+ get sampleRate(): number;
199
+ }
200
+
201
+ /**
202
+ * Common interface for playout engines.
203
+ *
204
+ * Both TonePlayout and MediaElementPlayout implement this interface,
205
+ * allowing the browser package to work with either engine.
206
+ */
207
+ interface PlayoutEngine {
208
+ init(): Promise<void>;
209
+ dispose(): void;
210
+ play(when?: number, offset?: number, duration?: number): void;
211
+ pause(): void;
212
+ stop(): void;
213
+ seekTo(time: number): void;
214
+ getCurrentTime(): number;
215
+ setMasterVolume(volume: number): void;
216
+ setMute?(trackId: string, muted: boolean): void;
217
+ setSolo?(trackId: string, soloed: boolean): void;
218
+ setOnPlaybackComplete(callback: () => void): void;
219
+ readonly isPlaying: boolean;
220
+ readonly duration: number;
221
+ readonly sampleRate: number;
222
+ }
223
+ /**
224
+ * Extended interface for engines that support playback rate.
225
+ */
226
+ interface PlaybackRateEngine extends PlayoutEngine {
227
+ setPlaybackRate(rate: number): void;
228
+ readonly playbackRate: number;
229
+ }
230
+ /**
231
+ * Type guard to check if an engine supports playback rate.
232
+ */
233
+ declare function supportsPlaybackRate(engine: PlayoutEngine): engine is PlaybackRateEngine;
234
+
235
+ export { MediaElementPlayout, type MediaElementPlayoutOptions, MediaElementTrack, type MediaElementTrackOptions, type PlaybackRateEngine, type PlayoutEngine, supportsPlaybackRate };
@@ -0,0 +1,235 @@
1
+ import { WaveformDataObject } from '@waveform-playlist/core';
2
+
3
+ interface MediaElementTrackOptions {
4
+ /** The audio source - can be a URL, Blob URL, or HTMLAudioElement */
5
+ source: string | HTMLAudioElement;
6
+ /** Pre-computed waveform data for visualization (required - no AudioBuffer decoding) */
7
+ peaks: WaveformDataObject;
8
+ /** Track ID */
9
+ id?: string;
10
+ /** Track name for display */
11
+ name?: string;
12
+ /** Initial volume (0.0 to 1.0) */
13
+ volume?: number;
14
+ /** Initial playback rate (0.5 to 2.0, pitch preserved) */
15
+ playbackRate?: number;
16
+ }
17
+ /**
18
+ * Single-track playback using HTMLAudioElement.
19
+ *
20
+ * Benefits over AudioBuffer/Tone.js:
21
+ * - Pitch-preserving playback rate (0.5x - 2.0x) via browser's built-in algorithm
22
+ * - No AudioBuffer decoding required (uses pre-computed peaks for visualization)
23
+ * - Simpler, lighter-weight for single-track use cases
24
+ *
25
+ * Limitations:
26
+ * - Single track only (no multi-track mixing)
27
+ * - No clip-level effects or fades (track-level volume only)
28
+ * - Relies on browser's time-stretching quality
29
+ */
30
+ declare class MediaElementTrack {
31
+ private audioElement;
32
+ private ownsElement;
33
+ private _peaks;
34
+ private _id;
35
+ private _name;
36
+ private _playbackRate;
37
+ private onStopCallback?;
38
+ private onTimeUpdateCallback?;
39
+ constructor(options: MediaElementTrackOptions);
40
+ private handleEnded;
41
+ private handleTimeUpdate;
42
+ /**
43
+ * Start playback from a specific time
44
+ */
45
+ play(offset?: number): void;
46
+ /**
47
+ * Pause playback
48
+ */
49
+ pause(): void;
50
+ /**
51
+ * Stop playback and reset to beginning
52
+ */
53
+ stop(): void;
54
+ /**
55
+ * Seek to a specific time
56
+ */
57
+ seekTo(time: number): void;
58
+ /**
59
+ * Set volume (0.0 to 1.0)
60
+ */
61
+ setVolume(volume: number): void;
62
+ /**
63
+ * Set playback rate (0.5 to 2.0, pitch preserved)
64
+ */
65
+ setPlaybackRate(rate: number): void;
66
+ /**
67
+ * Set muted state
68
+ */
69
+ setMuted(muted: boolean): void;
70
+ /**
71
+ * Set callback for when playback ends
72
+ */
73
+ setOnStopCallback(callback: () => void): void;
74
+ /**
75
+ * Set callback for time updates
76
+ */
77
+ setOnTimeUpdateCallback(callback: (time: number) => void): void;
78
+ /**
79
+ * Clean up resources
80
+ */
81
+ dispose(): void;
82
+ get id(): string;
83
+ get name(): string;
84
+ get peaks(): WaveformDataObject;
85
+ get currentTime(): number;
86
+ get duration(): number;
87
+ get isPlaying(): boolean;
88
+ get volume(): number;
89
+ get playbackRate(): number;
90
+ get muted(): boolean;
91
+ /**
92
+ * Get the underlying audio element (for advanced use cases)
93
+ */
94
+ get element(): HTMLAudioElement;
95
+ }
96
+
97
+ interface MediaElementPlayoutOptions {
98
+ /** Initial master volume (0.0 to 1.0) */
99
+ masterVolume?: number;
100
+ /** Initial playback rate (0.5 to 2.0) */
101
+ playbackRate?: number;
102
+ }
103
+ /**
104
+ * Single-track playout engine using HTMLAudioElement.
105
+ *
106
+ * This is a lightweight alternative to TonePlayout for single-track use cases
107
+ * that need pitch-preserving playback rate control.
108
+ *
109
+ * Key features:
110
+ * - Pitch-preserving playback rate (0.5x - 2.0x)
111
+ * - Uses pre-computed peaks (no AudioBuffer required)
112
+ * - Simpler API for single-track playback
113
+ *
114
+ * Limitations:
115
+ * - Single track only - will warn if multiple tracks added
116
+ * - No clip-level effects or crossfades
117
+ * - No multi-track mixing
118
+ *
119
+ * For multi-track editing, use TonePlayout from @waveform-playlist/playout instead.
120
+ */
121
+ declare class MediaElementPlayout {
122
+ private track;
123
+ private _masterVolume;
124
+ private _playbackRate;
125
+ private _isPlaying;
126
+ private onPlaybackCompleteCallback?;
127
+ constructor(options?: MediaElementPlayoutOptions);
128
+ /**
129
+ * Initialize the playout engine.
130
+ * For MediaElementPlayout this is a no-op (no AudioContext to start).
131
+ */
132
+ init(): Promise<void>;
133
+ /**
134
+ * Add a track to the playout.
135
+ * Note: Only one track is supported. Adding a second track will dispose the first.
136
+ */
137
+ addTrack(options: MediaElementTrackOptions): MediaElementTrack;
138
+ /**
139
+ * Remove a track by ID.
140
+ */
141
+ removeTrack(trackId: string): void;
142
+ /**
143
+ * Get a track by ID.
144
+ */
145
+ getTrack(trackId: string): MediaElementTrack | undefined;
146
+ /**
147
+ * Start playback.
148
+ * @param _when - Ignored (HTMLAudioElement doesn't support scheduled start)
149
+ * @param offset - Start position in seconds
150
+ * @param duration - Duration to play in seconds (optional)
151
+ */
152
+ play(_when?: number, offset?: number, duration?: number): void;
153
+ /**
154
+ * Pause playback.
155
+ */
156
+ pause(): void;
157
+ /**
158
+ * Stop playback and reset to start.
159
+ */
160
+ stop(): void;
161
+ /**
162
+ * Seek to a specific time.
163
+ */
164
+ seekTo(time: number): void;
165
+ /**
166
+ * Get current playback time.
167
+ */
168
+ getCurrentTime(): number;
169
+ /**
170
+ * Set master volume.
171
+ */
172
+ setMasterVolume(volume: number): void;
173
+ /**
174
+ * Set playback rate (0.5 to 2.0, pitch preserved).
175
+ */
176
+ setPlaybackRate(rate: number): void;
177
+ /**
178
+ * Set mute state for a track.
179
+ */
180
+ setMute(trackId: string, muted: boolean): void;
181
+ /**
182
+ * Set solo state for a track.
183
+ * Note: With single track, solo is effectively the same as unmute.
184
+ */
185
+ setSolo(_trackId: string, _soloed: boolean): void;
186
+ /**
187
+ * Set callback for when playback completes.
188
+ */
189
+ setOnPlaybackComplete(callback: () => void): void;
190
+ /**
191
+ * Clean up resources.
192
+ */
193
+ dispose(): void;
194
+ get isPlaying(): boolean;
195
+ get masterVolume(): number;
196
+ get playbackRate(): number;
197
+ get duration(): number;
198
+ get sampleRate(): number;
199
+ }
200
+
201
+ /**
202
+ * Common interface for playout engines.
203
+ *
204
+ * Both TonePlayout and MediaElementPlayout implement this interface,
205
+ * allowing the browser package to work with either engine.
206
+ */
207
+ interface PlayoutEngine {
208
+ init(): Promise<void>;
209
+ dispose(): void;
210
+ play(when?: number, offset?: number, duration?: number): void;
211
+ pause(): void;
212
+ stop(): void;
213
+ seekTo(time: number): void;
214
+ getCurrentTime(): number;
215
+ setMasterVolume(volume: number): void;
216
+ setMute?(trackId: string, muted: boolean): void;
217
+ setSolo?(trackId: string, soloed: boolean): void;
218
+ setOnPlaybackComplete(callback: () => void): void;
219
+ readonly isPlaying: boolean;
220
+ readonly duration: number;
221
+ readonly sampleRate: number;
222
+ }
223
+ /**
224
+ * Extended interface for engines that support playback rate.
225
+ */
226
+ interface PlaybackRateEngine extends PlayoutEngine {
227
+ setPlaybackRate(rate: number): void;
228
+ readonly playbackRate: number;
229
+ }
230
+ /**
231
+ * Type guard to check if an engine supports playback rate.
232
+ */
233
+ declare function supportsPlaybackRate(engine: PlayoutEngine): engine is PlaybackRateEngine;
234
+
235
+ export { MediaElementPlayout, type MediaElementPlayoutOptions, MediaElementTrack, type MediaElementTrackOptions, type PlaybackRateEngine, type PlayoutEngine, supportsPlaybackRate };