wavesurfer.js 7.0.0-alpha.0 → 7.0.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -15
- package/dist/base-plugin.d.ts +4 -3
- package/dist/base-plugin.js +3 -2
- package/dist/decoder.d.ts +2 -4
- package/dist/decoder.js +16 -16
- package/dist/event-emitter.d.ts +3 -1
- package/dist/event-emitter.js +6 -2
- package/dist/index.d.ts +43 -18
- package/dist/index.js +105 -44
- package/dist/player-webaudio.js +5 -4
- package/dist/player.d.ts +11 -2
- package/dist/player.js +50 -4
- package/dist/plugins/multitrack.d.ts +46 -0
- package/dist/plugins/multitrack.js +274 -0
- package/dist/plugins/regions.d.ts +11 -6
- package/dist/plugins/regions.js +106 -74
- package/dist/plugins/timeline.d.ts +29 -0
- package/dist/plugins/timeline.js +119 -0
- package/dist/plugins/xmultitrack.d.ts +44 -0
- package/dist/plugins/xmultitrack.js +260 -0
- package/dist/react/useWavesurfer.d.ts +5 -0
- package/dist/react/useWavesurfer.js +20 -0
- package/dist/renderer.d.ts +9 -5
- package/dist/renderer.js +137 -91
- package/dist/timer.d.ts +2 -1
- package/dist/timer.js +6 -1
- package/dist/wavesurfer.Multitrack.min.js +1 -0
- package/dist/wavesurfer.Regions.min.js +1 -0
- package/dist/wavesurfer.Timeline.min.js +1 -0
- package/dist/wavesurfer.min.js +1 -0
- package/package.json +19 -5
- package/.eslintrc.json +0 -24
- package/.prettierrc +0 -8
- package/examples/audio.ogg +0 -0
- package/examples/bars.js +0 -19
- package/examples/basic.js +0 -8
- package/examples/gradient.js +0 -28
- package/examples/regions.js +0 -63
- package/examples/video.js +0 -19
- package/examples/webaudio.js +0 -14
- package/src/base-plugin.ts +0 -20
- package/src/decoder.ts +0 -41
- package/src/event-emitter.ts +0 -35
- package/src/fetcher.ts +0 -7
- package/src/index.ts +0 -252
- package/src/player-webaudio.ts +0 -34
- package/src/player.ts +0 -50
- package/src/plugins/regions.ts +0 -240
- package/src/renderer.ts +0 -250
- package/src/timer.ts +0 -27
- package/tsconfig.json +0 -105
- package/tutorial/index.html +0 -47
- package/tutorial/src/editor.js +0 -70
- package/tutorial/src/init.js +0 -66
- package/tutorial/src/url.js +0 -25
- package/tutorial/style.css +0 -211
- package/yarn-error.log +0 -1049
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import BasePlugin from '../base-plugin.js';
|
|
2
|
+
const defaultOptions = {
|
|
3
|
+
height: 20,
|
|
4
|
+
};
|
|
5
|
+
class TimelinePlugin extends BasePlugin {
|
|
6
|
+
constructor(params, options) {
|
|
7
|
+
var _a;
|
|
8
|
+
super(params, options);
|
|
9
|
+
this.options = Object.assign({}, defaultOptions, options);
|
|
10
|
+
this.container = (_a = this.options.container) !== null && _a !== void 0 ? _a : this.container;
|
|
11
|
+
this.wrapper = this.initWrapper();
|
|
12
|
+
this.container.appendChild(this.wrapper);
|
|
13
|
+
this.subscriptions.push(this.wavesurfer.on('decode', ({ duration }) => {
|
|
14
|
+
this.initTimeline(duration);
|
|
15
|
+
}));
|
|
16
|
+
}
|
|
17
|
+
/** Unmount */
|
|
18
|
+
destroy() {
|
|
19
|
+
this.wrapper.remove();
|
|
20
|
+
super.destroy();
|
|
21
|
+
}
|
|
22
|
+
initWrapper() {
|
|
23
|
+
return document.createElement('div');
|
|
24
|
+
}
|
|
25
|
+
formatTime(seconds) {
|
|
26
|
+
if (seconds / 60 > 1) {
|
|
27
|
+
// calculate minutes and seconds from seconds count
|
|
28
|
+
const minutes = Math.round(seconds / 60);
|
|
29
|
+
seconds = Math.round(seconds % 60);
|
|
30
|
+
const paddedSeconds = `${seconds < 10 ? '0' : ''}${seconds}`;
|
|
31
|
+
return `${minutes}:${paddedSeconds}`;
|
|
32
|
+
}
|
|
33
|
+
const rounded = Math.round(seconds * 1000) / 1000;
|
|
34
|
+
return `${rounded}`;
|
|
35
|
+
}
|
|
36
|
+
// Return how many seconds should be between each notch
|
|
37
|
+
defaultTimeInterval(pxPerSec) {
|
|
38
|
+
if (pxPerSec >= 25) {
|
|
39
|
+
return 1;
|
|
40
|
+
}
|
|
41
|
+
else if (pxPerSec * 5 >= 25) {
|
|
42
|
+
return 5;
|
|
43
|
+
}
|
|
44
|
+
else if (pxPerSec * 15 >= 25) {
|
|
45
|
+
return 15;
|
|
46
|
+
}
|
|
47
|
+
return Math.ceil(0.5 / pxPerSec) * 60;
|
|
48
|
+
}
|
|
49
|
+
// Return the cadence of notches that get labels in the primary color.
|
|
50
|
+
defaultPrimaryLabelInterval(pxPerSec) {
|
|
51
|
+
if (pxPerSec >= 25) {
|
|
52
|
+
return 10;
|
|
53
|
+
}
|
|
54
|
+
else if (pxPerSec * 5 >= 25) {
|
|
55
|
+
return 6;
|
|
56
|
+
}
|
|
57
|
+
else if (pxPerSec * 15 >= 25) {
|
|
58
|
+
return 4;
|
|
59
|
+
}
|
|
60
|
+
return 4;
|
|
61
|
+
}
|
|
62
|
+
// Return the cadence of notches that get labels in the secondary color.
|
|
63
|
+
defaultSecondaryLabelInterval(pxPerSec) {
|
|
64
|
+
if (pxPerSec >= 25) {
|
|
65
|
+
return 5;
|
|
66
|
+
}
|
|
67
|
+
else if (pxPerSec * 5 >= 25) {
|
|
68
|
+
return 2;
|
|
69
|
+
}
|
|
70
|
+
else if (pxPerSec * 15 >= 25) {
|
|
71
|
+
return 2;
|
|
72
|
+
}
|
|
73
|
+
return 2;
|
|
74
|
+
}
|
|
75
|
+
initTimeline(duration) {
|
|
76
|
+
var _a, _b, _c;
|
|
77
|
+
const width = Math.round(this.wrapper.scrollWidth * devicePixelRatio);
|
|
78
|
+
const pxPerSec = width / duration;
|
|
79
|
+
const timeInterval = (_a = this.options.timeInterval) !== null && _a !== void 0 ? _a : this.defaultTimeInterval(pxPerSec);
|
|
80
|
+
const primaryLabelInterval = (_b = this.options.primaryLabelInterval) !== null && _b !== void 0 ? _b : this.defaultPrimaryLabelInterval(pxPerSec);
|
|
81
|
+
const secondaryLabelInterval = (_c = this.options.secondaryLabelInterval) !== null && _c !== void 0 ? _c : this.defaultSecondaryLabelInterval(pxPerSec);
|
|
82
|
+
const timeline = document.createElement('div');
|
|
83
|
+
timeline.setAttribute('style', `
|
|
84
|
+
height: ${this.options.height}px;
|
|
85
|
+
overflow: hidden;
|
|
86
|
+
display: flex;
|
|
87
|
+
justify-content: space-between;
|
|
88
|
+
align-items: flex-end;
|
|
89
|
+
font-size: ${this.options.height / 2}px;
|
|
90
|
+
`);
|
|
91
|
+
const notchEl = document.createElement('div');
|
|
92
|
+
notchEl.setAttribute('style', `
|
|
93
|
+
width: 1px;
|
|
94
|
+
height: 50%;
|
|
95
|
+
display: flex;
|
|
96
|
+
flex-direction: column;
|
|
97
|
+
justify-content: flex-end;
|
|
98
|
+
overflow: visible;
|
|
99
|
+
border-left: 1px solid currentColor;
|
|
100
|
+
opacity: 0.25;
|
|
101
|
+
`);
|
|
102
|
+
for (let i = 0; i < duration; i += timeInterval) {
|
|
103
|
+
const notch = notchEl.cloneNode();
|
|
104
|
+
const isPrimary = i % primaryLabelInterval === 0;
|
|
105
|
+
const isSecondary = i % secondaryLabelInterval === 0;
|
|
106
|
+
if (isPrimary || isSecondary) {
|
|
107
|
+
notch.style.height = '100%';
|
|
108
|
+
notch.style.textIndent = '3px';
|
|
109
|
+
notch.textContent = this.formatTime(i);
|
|
110
|
+
if (isPrimary)
|
|
111
|
+
notch.style.opacity = '1';
|
|
112
|
+
}
|
|
113
|
+
timeline.appendChild(notch);
|
|
114
|
+
}
|
|
115
|
+
this.wrapper.appendChild(timeline);
|
|
116
|
+
this.emit('ready');
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
export default TimelinePlugin;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type WaveSurferOptions } from '../index.js';
|
|
2
|
+
type MultitrackTracks = Array<{
|
|
3
|
+
id: string | number;
|
|
4
|
+
url?: string;
|
|
5
|
+
peaks?: WaveSurferOptions['peaks'];
|
|
6
|
+
draggable?: boolean;
|
|
7
|
+
startPosition: number;
|
|
8
|
+
startCue?: number;
|
|
9
|
+
endCue?: number;
|
|
10
|
+
markers?: Array<{
|
|
11
|
+
id: string | number;
|
|
12
|
+
time: number;
|
|
13
|
+
label?: string;
|
|
14
|
+
color?: string;
|
|
15
|
+
}>;
|
|
16
|
+
}>;
|
|
17
|
+
type MultitrackOptions = {
|
|
18
|
+
container: HTMLElement;
|
|
19
|
+
minPxPerSec: number;
|
|
20
|
+
} & WaveSurferOptions;
|
|
21
|
+
declare class MultiTrack {
|
|
22
|
+
private tracks;
|
|
23
|
+
private options;
|
|
24
|
+
private audios;
|
|
25
|
+
private wavesurfers;
|
|
26
|
+
private durations;
|
|
27
|
+
private currentTime;
|
|
28
|
+
private maxDuration;
|
|
29
|
+
private rendering;
|
|
30
|
+
private isDragging;
|
|
31
|
+
static create(tracks: MultitrackTracks, options: MultitrackOptions): MultiTrack;
|
|
32
|
+
constructor(tracks: MultitrackTracks, options: MultitrackOptions);
|
|
33
|
+
private initAudios;
|
|
34
|
+
private initWavesurfers;
|
|
35
|
+
private updatePosition;
|
|
36
|
+
private onSeek;
|
|
37
|
+
private onDrag;
|
|
38
|
+
private findCurrentTracks;
|
|
39
|
+
play(): void;
|
|
40
|
+
pause(): void;
|
|
41
|
+
isPlaying(): boolean;
|
|
42
|
+
destroy(): void;
|
|
43
|
+
}
|
|
44
|
+
export default MultiTrack;
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import WaveSurfer from '../index.js';
|
|
2
|
+
import RegionsPlugin from './regions.js';
|
|
3
|
+
class MultiTrack {
|
|
4
|
+
static create(tracks, options) {
|
|
5
|
+
return new MultiTrack(tracks, options);
|
|
6
|
+
}
|
|
7
|
+
constructor(tracks, options) {
|
|
8
|
+
this.audios = [];
|
|
9
|
+
this.wavesurfers = [];
|
|
10
|
+
this.durations = [];
|
|
11
|
+
this.currentTime = 0;
|
|
12
|
+
this.maxDuration = 0;
|
|
13
|
+
this.isDragging = false;
|
|
14
|
+
this.tracks = tracks.map((track) => (Object.assign(Object.assign({}, track), { startPosition: track.startPosition || 0 })));
|
|
15
|
+
this.options = options;
|
|
16
|
+
this.rendering = initRendering(this.tracks, this.options);
|
|
17
|
+
this.initAudios().then((durations) => {
|
|
18
|
+
this.durations = durations;
|
|
19
|
+
const maxDuration = this.tracks.reduce((max, track, index) => {
|
|
20
|
+
return Math.max(max, track.startPosition + durations[index]);
|
|
21
|
+
}, 0);
|
|
22
|
+
this.rendering.setMainWidth(maxDuration * this.options.minPxPerSec);
|
|
23
|
+
this.rendering.addClickHandler((position) => {
|
|
24
|
+
this.onSeek(position * maxDuration);
|
|
25
|
+
});
|
|
26
|
+
this.maxDuration = maxDuration;
|
|
27
|
+
});
|
|
28
|
+
this.initWavesurfers();
|
|
29
|
+
this.rendering.containers.forEach((container, index) => {
|
|
30
|
+
const drag = initDragging(container, (delta) => this.onDrag(index, delta));
|
|
31
|
+
this.wavesurfers[index].once('destroy', () => {
|
|
32
|
+
drag === null || drag === void 0 ? void 0 : drag.destroy();
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
initAudios() {
|
|
37
|
+
const emptyTrackUrl = 'data:audio/wav;base64,UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA';
|
|
38
|
+
this.audios = this.tracks.map((track) => new Audio(track.url || emptyTrackUrl));
|
|
39
|
+
return Promise.all(this.audios.map((audio) => {
|
|
40
|
+
return new Promise((resolve) => {
|
|
41
|
+
audio.addEventListener('canplay', () => {
|
|
42
|
+
resolve(audio.duration);
|
|
43
|
+
}, { once: true });
|
|
44
|
+
});
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
initWavesurfers() {
|
|
48
|
+
const wavesurfers = this.tracks.map((track, index) => {
|
|
49
|
+
// Create a wavesurfer instance
|
|
50
|
+
const ws = WaveSurfer.create(Object.assign(Object.assign({}, this.options), { container: this.rendering.containers[index], media: this.audios[index], peaks: track.peaks, cursorColor: 'transparent', fillParent: false, interactive: false }));
|
|
51
|
+
ws.on('timeupdate', ({ currentTime }) => {
|
|
52
|
+
if (ws.isPlaying() && currentTime + track.startPosition > this.currentTime) {
|
|
53
|
+
this.updatePosition(track.startPosition + currentTime);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
const wsRegions = ws.registerPlugin(RegionsPlugin, {
|
|
57
|
+
draggable: false,
|
|
58
|
+
resizable: false,
|
|
59
|
+
dragSelection: false,
|
|
60
|
+
});
|
|
61
|
+
ws.once('decode', () => {
|
|
62
|
+
// Render start and end cues
|
|
63
|
+
if (track.startCue) {
|
|
64
|
+
wsRegions.add(track.startCue, track.startCue, 'start');
|
|
65
|
+
}
|
|
66
|
+
if (track.endCue) {
|
|
67
|
+
wsRegions.add(track.endCue, track.endCue, 'end');
|
|
68
|
+
}
|
|
69
|
+
// Render markers
|
|
70
|
+
;
|
|
71
|
+
(track.markers || []).forEach((marker) => {
|
|
72
|
+
wsRegions.add(marker.time, marker.time, marker.label, marker.color);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
return ws;
|
|
76
|
+
});
|
|
77
|
+
this.wavesurfers = wavesurfers;
|
|
78
|
+
}
|
|
79
|
+
updatePosition(time) {
|
|
80
|
+
const precisionSeconds = 0.3;
|
|
81
|
+
this.currentTime = time;
|
|
82
|
+
this.rendering.updateCursor(time / this.maxDuration);
|
|
83
|
+
const isPaused = !this.isPlaying();
|
|
84
|
+
// Update the current time of each audio
|
|
85
|
+
this.tracks.forEach((track, index) => {
|
|
86
|
+
const audio = this.audios[index];
|
|
87
|
+
const duration = this.durations[index];
|
|
88
|
+
const newTime = time - track.startPosition;
|
|
89
|
+
if (Math.abs(audio.currentTime - newTime) > precisionSeconds) {
|
|
90
|
+
audio.currentTime = newTime;
|
|
91
|
+
}
|
|
92
|
+
// If the position is out of the track bounds, pause it
|
|
93
|
+
if (isPaused || newTime < 0 || newTime > duration) {
|
|
94
|
+
!audio.paused && audio.pause();
|
|
95
|
+
}
|
|
96
|
+
else if (!isPaused) {
|
|
97
|
+
// If the position is in the track bounds, play it
|
|
98
|
+
audio.paused && audio.play();
|
|
99
|
+
}
|
|
100
|
+
// Unmute if cue is reached
|
|
101
|
+
const newVolume = newTime >= (track.startCue || 0) && newTime < (track.endCue || Infinity) ? 1 : 0;
|
|
102
|
+
if (newVolume !== audio.volume)
|
|
103
|
+
audio.volume = newVolume;
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
onSeek(time) {
|
|
107
|
+
if (this.isDragging)
|
|
108
|
+
return;
|
|
109
|
+
const wasPlaying = this.isPlaying();
|
|
110
|
+
this.updatePosition(time);
|
|
111
|
+
if (wasPlaying)
|
|
112
|
+
this.play();
|
|
113
|
+
}
|
|
114
|
+
onDrag(index, delta) {
|
|
115
|
+
const track = this.tracks[index];
|
|
116
|
+
if (!track.draggable)
|
|
117
|
+
return;
|
|
118
|
+
this.isDragging = true;
|
|
119
|
+
setTimeout(() => (this.isDragging = false), 300);
|
|
120
|
+
const newTime = track.startPosition + delta * this.maxDuration;
|
|
121
|
+
track.startPosition = newTime;
|
|
122
|
+
this.rendering.setContainerOffsets();
|
|
123
|
+
}
|
|
124
|
+
findCurrentTracks() {
|
|
125
|
+
// Find the audios at the current time
|
|
126
|
+
const indexes = [];
|
|
127
|
+
this.tracks.forEach((track, index) => {
|
|
128
|
+
if (track.url &&
|
|
129
|
+
this.currentTime >= track.startPosition &&
|
|
130
|
+
this.currentTime < track.startPosition + this.durations[index]) {
|
|
131
|
+
indexes.push(index);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
if (indexes.length === 0) {
|
|
135
|
+
const minStartTime = Math.min(...this.tracks.filter((t) => t.url).map((track) => track.startPosition));
|
|
136
|
+
indexes.push(this.tracks.findIndex((track) => track.startPosition === minStartTime));
|
|
137
|
+
}
|
|
138
|
+
return indexes;
|
|
139
|
+
}
|
|
140
|
+
play() {
|
|
141
|
+
const indexes = this.findCurrentTracks();
|
|
142
|
+
indexes.forEach((index) => {
|
|
143
|
+
var _a;
|
|
144
|
+
(_a = this.audios[index]) === null || _a === void 0 ? void 0 : _a.play();
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
pause() {
|
|
148
|
+
this.audios.forEach((audio) => audio.pause());
|
|
149
|
+
}
|
|
150
|
+
isPlaying() {
|
|
151
|
+
return this.audios.some((audio) => !audio.paused);
|
|
152
|
+
}
|
|
153
|
+
destroy() {
|
|
154
|
+
this.rendering.destroy();
|
|
155
|
+
this.audios.forEach((audio) => {
|
|
156
|
+
audio.pause();
|
|
157
|
+
audio.src = '';
|
|
158
|
+
});
|
|
159
|
+
this.wavesurfers.forEach((ws) => {
|
|
160
|
+
ws.destroy();
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function initRendering(tracks, options) {
|
|
165
|
+
// Create a common container for all tracks
|
|
166
|
+
let scroll = document.createElement('div');
|
|
167
|
+
scroll.setAttribute('style', 'width: 100%; overflow-x: scroll; overflow-y: hidden; user-select: none;');
|
|
168
|
+
const wrapper = document.createElement('div');
|
|
169
|
+
wrapper.style.position = 'relative';
|
|
170
|
+
scroll.appendChild(wrapper);
|
|
171
|
+
options.container.appendChild(scroll);
|
|
172
|
+
// Create a common cursor
|
|
173
|
+
const cursor = document.createElement('div');
|
|
174
|
+
cursor.setAttribute('style', 'width: 1px; height: 100%; position: absolute; z-index: 10; top: 0; left: 0');
|
|
175
|
+
cursor.style.backgroundColor = options.cursorColor || options.progressColor || '#000';
|
|
176
|
+
wrapper.appendChild(cursor);
|
|
177
|
+
// Create containers for each track
|
|
178
|
+
const containers = tracks.map(() => {
|
|
179
|
+
const container = document.createElement('div');
|
|
180
|
+
container.style.width = 'fit-content';
|
|
181
|
+
wrapper.appendChild(container);
|
|
182
|
+
return container;
|
|
183
|
+
});
|
|
184
|
+
// Set the positions of each container
|
|
185
|
+
const setContainerOffsets = () => {
|
|
186
|
+
containers.forEach((container, i) => {
|
|
187
|
+
const offset = (tracks[i].startPosition * options.minPxPerSec) / devicePixelRatio;
|
|
188
|
+
container.style.transform = `translateX(${offset}px)`;
|
|
189
|
+
if (tracks[i].draggable)
|
|
190
|
+
container.style.cursor = 'ew-resize';
|
|
191
|
+
});
|
|
192
|
+
};
|
|
193
|
+
setContainerOffsets();
|
|
194
|
+
return {
|
|
195
|
+
containers,
|
|
196
|
+
// Set the start offset
|
|
197
|
+
setContainerOffsets,
|
|
198
|
+
// Set the container width
|
|
199
|
+
setMainWidth: (width) => {
|
|
200
|
+
wrapper.style.width = `${width / devicePixelRatio}px`;
|
|
201
|
+
},
|
|
202
|
+
// Update cursor position
|
|
203
|
+
updateCursor: (position) => {
|
|
204
|
+
cursor.style.left = `${Math.min(100, position * 100)}%`;
|
|
205
|
+
},
|
|
206
|
+
// Click to seek
|
|
207
|
+
addClickHandler: (onClick) => {
|
|
208
|
+
wrapper.addEventListener('click', (e) => {
|
|
209
|
+
const rect = wrapper.getBoundingClientRect();
|
|
210
|
+
const x = e.clientX - rect.left;
|
|
211
|
+
const position = x / wrapper.offsetWidth;
|
|
212
|
+
onClick(position);
|
|
213
|
+
});
|
|
214
|
+
},
|
|
215
|
+
// Destroy the container
|
|
216
|
+
destroy: () => {
|
|
217
|
+
scroll.remove();
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
function initDragging(container, onDrag) {
|
|
222
|
+
const wrapper = container.parentElement;
|
|
223
|
+
if (!wrapper)
|
|
224
|
+
return;
|
|
225
|
+
// Dragging tracks to set position
|
|
226
|
+
let dragStart = null;
|
|
227
|
+
// Drag start
|
|
228
|
+
container.addEventListener('mousedown', (e) => {
|
|
229
|
+
const rect = wrapper.getBoundingClientRect();
|
|
230
|
+
dragStart = e.clientX - rect.left;
|
|
231
|
+
});
|
|
232
|
+
// Drag end
|
|
233
|
+
const onMouseUp = (e) => {
|
|
234
|
+
if (dragStart != null) {
|
|
235
|
+
e.stopPropagation();
|
|
236
|
+
dragStart = null;
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
// Drag move
|
|
240
|
+
const onMouseMove = (e) => {
|
|
241
|
+
if (dragStart == null)
|
|
242
|
+
return;
|
|
243
|
+
const rect = wrapper.getBoundingClientRect();
|
|
244
|
+
const x = e.clientX - rect.left;
|
|
245
|
+
const diff = x - dragStart;
|
|
246
|
+
if (diff > 1 || diff < -1) {
|
|
247
|
+
dragStart = x;
|
|
248
|
+
onDrag(diff / wrapper.offsetWidth);
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
document.body.addEventListener('mouseup', onMouseUp);
|
|
252
|
+
document.body.addEventListener('mousemove', onMouseMove);
|
|
253
|
+
return {
|
|
254
|
+
destroy: () => {
|
|
255
|
+
document.body.removeEventListener('mouseup', onMouseUp);
|
|
256
|
+
document.body.removeEventListener('mousemove', onMouseMove);
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
export default MultiTrack;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import WaveSurfer from '../index.js';
|
|
3
|
+
const { useEffect, useState } = React;
|
|
4
|
+
// A React hook to use WaveSurfer
|
|
5
|
+
export const useWavesurfer = (containerRef, options) => {
|
|
6
|
+
const [wavesurfer, setWavesurfer] = useState(null);
|
|
7
|
+
// Initialize wavesurfer when the container mounts
|
|
8
|
+
// or any of the props change
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
if (!containerRef.current)
|
|
11
|
+
return;
|
|
12
|
+
const ws = WaveSurfer.create(Object.assign(Object.assign({}, options), { container: containerRef.current }));
|
|
13
|
+
setWavesurfer(ws);
|
|
14
|
+
return () => {
|
|
15
|
+
ws.destroy();
|
|
16
|
+
};
|
|
17
|
+
}, [options, containerRef]);
|
|
18
|
+
return wavesurfer;
|
|
19
|
+
};
|
|
20
|
+
export default useWavesurfer;
|
package/dist/renderer.d.ts
CHANGED
|
@@ -4,7 +4,9 @@ type RendererOptions = {
|
|
|
4
4
|
height: number;
|
|
5
5
|
waveColor: string;
|
|
6
6
|
progressColor: string;
|
|
7
|
+
cursorColor?: string;
|
|
7
8
|
minPxPerSec: number;
|
|
9
|
+
fillParent: boolean;
|
|
8
10
|
barWidth?: number;
|
|
9
11
|
barGap?: number;
|
|
10
12
|
barRadius?: number;
|
|
@@ -17,18 +19,20 @@ type RendererEvents = {
|
|
|
17
19
|
declare class Renderer extends EventEmitter<RendererEvents> {
|
|
18
20
|
private options;
|
|
19
21
|
private container;
|
|
20
|
-
private
|
|
22
|
+
private scrollContainer;
|
|
21
23
|
private mainCanvas;
|
|
22
24
|
private progressCanvas;
|
|
23
25
|
private cursor;
|
|
24
26
|
private ctx;
|
|
27
|
+
private timeout;
|
|
25
28
|
constructor(options: RendererOptions);
|
|
29
|
+
getContainer(): HTMLElement;
|
|
26
30
|
destroy(): void;
|
|
27
|
-
private
|
|
28
|
-
private
|
|
29
|
-
render(channelData: Float32Array[], duration: number, minPxPerSec?: number): void;
|
|
31
|
+
private delay;
|
|
32
|
+
private renderPeaks;
|
|
30
33
|
private createProgressMask;
|
|
34
|
+
render(audioData: AudioBuffer): void;
|
|
35
|
+
zoom(audioData: AudioBuffer, minPxPerSec: number): void;
|
|
31
36
|
renderProgress(progress: number, autoCenter?: boolean): void;
|
|
32
|
-
getContainer(): HTMLElement;
|
|
33
37
|
}
|
|
34
38
|
export default Renderer;
|