wavesurfer.js 7.0.0-beta.9 → 7.0.1
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 +56 -24
- package/dist/base-plugin.d.ts +6 -3
- package/dist/base-plugin.js +1 -0
- package/dist/decoder.js +17 -6
- package/dist/draggable.js +10 -7
- package/dist/fetcher.d.ts +1 -3
- package/dist/fetcher.js +13 -6
- package/dist/player.d.ts +2 -2
- package/dist/player.js +10 -8
- package/dist/plugins/base-plugin.d.ts +16 -0
- package/dist/plugins/decoder.d.ts +9 -0
- package/dist/plugins/draggable.d.ts +1 -0
- package/dist/plugins/envelope.cjs +1 -0
- package/dist/plugins/envelope.d.ts +12 -4
- package/dist/plugins/envelope.esm.js +1 -0
- package/dist/plugins/envelope.js +34 -10
- package/dist/plugins/envelope.min.js +1 -0
- package/dist/plugins/event-emitter.d.ts +19 -0
- package/dist/plugins/fetcher.d.ts +5 -0
- package/dist/plugins/hover.cjs +1 -0
- package/dist/plugins/hover.d.ts +35 -0
- package/dist/plugins/hover.esm.js +1 -0
- package/dist/plugins/hover.js +101 -0
- package/dist/plugins/hover.min.js +1 -0
- package/dist/plugins/minimap.cjs +1 -0
- package/dist/plugins/minimap.d.ts +2 -2
- package/dist/plugins/minimap.esm.js +1 -0
- package/dist/plugins/minimap.js +8 -13
- package/dist/plugins/minimap.min.js +1 -0
- package/dist/plugins/player.d.ts +45 -0
- package/dist/plugins/plugins/envelope.d.ts +79 -0
- package/dist/plugins/plugins/hover.d.ts +35 -0
- package/dist/plugins/plugins/minimap.d.ts +39 -0
- package/dist/plugins/plugins/record.d.ts +31 -0
- package/dist/plugins/plugins/regions.d.ts +118 -0
- package/dist/plugins/plugins/spectrogram.d.ts +76 -0
- package/dist/plugins/plugins/timeline.d.ts +47 -0
- package/dist/plugins/record.cjs +1 -0
- package/dist/plugins/record.d.ts +7 -4
- package/dist/plugins/record.esm.js +1 -0
- package/dist/plugins/record.js +73 -66
- package/dist/plugins/record.min.js +1 -0
- package/dist/plugins/regions.cjs +1 -0
- package/dist/plugins/regions.d.ts +28 -4
- package/dist/plugins/regions.esm.js +1 -0
- package/dist/plugins/regions.js +74 -61
- package/dist/plugins/regions.min.js +1 -0
- package/dist/plugins/renderer.d.ts +44 -0
- package/dist/plugins/spectrogram.cjs +1 -0
- package/dist/plugins/spectrogram.d.ts +10 -3
- package/dist/plugins/spectrogram.esm.js +1 -0
- package/dist/plugins/spectrogram.js +166 -20
- package/dist/plugins/spectrogram.min.js +1 -0
- package/dist/plugins/timeline.cjs +1 -0
- package/dist/plugins/timeline.d.ts +5 -3
- package/dist/plugins/timeline.esm.js +1 -0
- package/dist/plugins/timeline.js +27 -21
- package/dist/plugins/timeline.min.js +1 -0
- package/dist/plugins/timer.d.ts +11 -0
- package/dist/plugins/wavesurfer.d.ts +156 -0
- package/dist/renderer.js +11 -7
- package/dist/wavesurfer.cjs +1 -0
- package/dist/wavesurfer.d.ts +5 -3
- package/dist/wavesurfer.esm.js +1 -0
- package/dist/wavesurfer.js +62 -41
- package/dist/wavesurfer.min.js +1 -0
- package/package.json +21 -22
- package/dist/plugins/envelope.min.cjs +0 -1
- package/dist/plugins/minimap.min.cjs +0 -1
- package/dist/plugins/record.min.cjs +0 -1
- package/dist/plugins/regions.min.cjs +0 -1
- package/dist/plugins/spectrogram-fft.d.ts +0 -9
- package/dist/plugins/spectrogram-fft.js +0 -150
- package/dist/plugins/spectrogram.min.cjs +0 -1
- package/dist/plugins/timeline.min.cjs +0 -1
- package/dist/wavesurfer.min.cjs +0 -1
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# <img src="https://user-images.githubusercontent.com/381895/226091100-f5567a28-7736-4d37-8f84-e08f297b7e1a.png" alt="logo" height="60" valign="middle" /> wavesurfer.js
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/wavesurfer.js)
|
|
4
4
|
|
|
5
5
|
## New TypeScript version
|
|
6
6
|
|
|
7
|
-
wavesurfer.js v7
|
|
7
|
+
wavesurfer.js v7 is a TypeScript rewrite of wavesurfer.js that brings several improvements:
|
|
8
8
|
|
|
9
9
|
* Typed API for better development experience
|
|
10
10
|
* Enhanced decoding and rendering performance
|
|
@@ -21,30 +21,43 @@ wavesurfer.js v7 beta is a TypeScript rewrite of wavesurfer.js that brings sever
|
|
|
21
21
|
Try it out:
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
npm install --save wavesurfer.js
|
|
24
|
+
npm install --save wavesurfer.js
|
|
25
25
|
```
|
|
26
26
|
```js
|
|
27
27
|
import WaveSurfer from 'wavesurfer.js'
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
Alternatively, import it from a CDN as a ES6 module
|
|
30
|
+
Alternatively, import it from a CDN as a ES6 module:
|
|
31
31
|
|
|
32
32
|
```html
|
|
33
33
|
<script type="module">
|
|
34
|
-
import WaveSurfer from 'https://unpkg.com/wavesurfer.js
|
|
35
|
-
|
|
36
|
-
const wavesurfer = WaveSurfer.create({
|
|
34
|
+
import WaveSurfer from 'https://unpkg.com/wavesurfer.js'
|
|
35
|
+
|
|
36
|
+
const wavesurfer = WaveSurfer.create({
|
|
37
|
+
container: '#waveform',
|
|
38
|
+
waveColor: '#4F4A85',
|
|
39
|
+
progressColor: '#383351',
|
|
40
|
+
url: '/audio.mp3',
|
|
41
|
+
})
|
|
37
42
|
</script>
|
|
38
43
|
```
|
|
39
44
|
|
|
40
45
|
Or, as a UMD script tag which exports the library as a global `WaveSurfer` variable:
|
|
41
46
|
```html
|
|
42
|
-
<script src="https://unpkg.com/wavesurfer.js
|
|
47
|
+
<script type="text/javascript" src="https://unpkg.com/wavesurfer.js/dist/wavesurfer.min.js"></script>
|
|
43
48
|
```
|
|
44
49
|
|
|
45
|
-
To import
|
|
50
|
+
To import one of the plugins, e.g. the Timeline plugin:
|
|
46
51
|
```js
|
|
47
|
-
import Timeline from '
|
|
52
|
+
import Timeline from 'wavesurfer.js/dist/plugins/timeline.js'
|
|
53
|
+
|
|
54
|
+
// or with a CDN:
|
|
55
|
+
|
|
56
|
+
import Timeline from 'https://unpkg.com/wavesurfer.js/dist/plugins/timeline.js'
|
|
57
|
+
|
|
58
|
+
// or as a script tag
|
|
59
|
+
|
|
60
|
+
<script type="text/javascript" src="https://unpkg.com/wavesurfer.js/dist/plugins/timeline.min.js"></script>
|
|
48
61
|
```
|
|
49
62
|
|
|
50
63
|
TypeScript types are included in the package, so there's no need to install `@types/wavesurfer.js`.
|
|
@@ -64,12 +77,13 @@ The "official" plugins have been completely rewritten and enhanced:
|
|
|
64
77
|
* [Minimap](https://wavesurfer-js.org/examples/#minimap.js) – a small waveform that serves as a scrollbar for the main waveform
|
|
65
78
|
* [Envelope](https://wavesurfer-js.org/examples/#envelope.js) – a graphical interface to add fade-in and -out effects and control volume
|
|
66
79
|
* [Record](https://wavesurfer-js.org/examples/#record.js) – records audio from the microphone and renders a waveform
|
|
67
|
-
* [Spectrogram](https://wavesurfer-js.org/examples/#spectrogram.js) – visualization of an audio frequency spectrum
|
|
80
|
+
* [Spectrogram](https://wavesurfer-js.org/examples/#spectrogram.js) – visualization of an audio frequency spectrum (written by @akreal)
|
|
81
|
+
* [Hover](https://wavesurfer-js.org/examples/#hover.js) – shows a vertical line and timestmap on waveform hover
|
|
68
82
|
|
|
69
83
|
## CSS styling
|
|
70
84
|
|
|
71
85
|
wavesurfer.js v7 is rendered into a Shadow DOM tree. This isolates its CSS from the rest of the web page.
|
|
72
|
-
However, it's still possible to style various wavesurfer.js elements
|
|
86
|
+
However, it's still possible to style various wavesurfer.js elements with CSS via the `::part()` pseudo-selector.
|
|
73
87
|
For example:
|
|
74
88
|
|
|
75
89
|
```css
|
|
@@ -90,12 +104,12 @@ Most options, events, and methods are similar to those in previous versions.
|
|
|
90
104
|
|
|
91
105
|
### Notable differences
|
|
92
106
|
* The `backend` option is removed – [HTML5 audio (or video) is the only playback mechanism](https://github.com/katspaugh/wavesurfer.js/discussions/2762#discussioncomment-5669347). However, you can still connect wavesurfer to Web Audio via `MediaElementSourceNode`. See this [example](https://wavesurfer-js.org/examples/#webaudio.js).
|
|
93
|
-
* The Markers plugin is removed – use the Regions plugin with just a `startTime`.
|
|
94
|
-
* No Microphone
|
|
95
|
-
*
|
|
107
|
+
* The Markers plugin is removed – you should use the Regions plugin with just a `startTime`.
|
|
108
|
+
* No Microphone plugin – superseded by the new Record plugin with more features.
|
|
109
|
+
* The Cursor plugin is replaced by the Hover plugin.
|
|
96
110
|
|
|
97
111
|
### Removed options
|
|
98
|
-
* `backend`, `audioContext`, `closeAudioContext
|
|
112
|
+
* `backend`, `audioContext`, `closeAudioContext`, `audioScriptProcessor` – there's no Web Audio backend, so no AudioContext
|
|
99
113
|
* `autoCenterImmediately` – `autoCenter` is now always immediate unless the audio is playing
|
|
100
114
|
* `backgroundColor`, `hideCursor` – this can be easily set via CSS
|
|
101
115
|
* `mediaType`, `mediaControls` – you should instead pass an entire media element in the `media` option. [Example](https://wavesurfer-js.org/examples/#video.js).
|
|
@@ -106,19 +120,21 @@ Most options, events, and methods are similar to those in previous versions.
|
|
|
106
120
|
* `scrollParent` – the container will scroll if `minPxPerSec` is set to a higher value
|
|
107
121
|
* `skipLength` – there's no `skipForward` and `skipBackward` methods anymore
|
|
108
122
|
* `splitChannelsOptions` – you should now use `splitChannels` to pass the channel options. Pass `height: 0` to hide a channel. See [this example](https://wavesurfer-js.org/examples/#split-channels.js).
|
|
109
|
-
* `
|
|
123
|
+
* `drawingContextAttributes`, `maxCanvasWidth`, `forceDecode` – removed to reduce code complexity
|
|
124
|
+
* `xhr` - please use `fetchParams` instead
|
|
125
|
+
* `barMinHeight` - the minimum bar height is now 1 pixel by default
|
|
110
126
|
|
|
111
127
|
### Removed methods
|
|
112
128
|
* `getFilters`, `setFilter` – as there's no Web Audio "backend"
|
|
113
129
|
* `drawBuffer` – to redraw the waveform, use `setOptions` instead and pass new rendering options
|
|
114
|
-
* `cancelAjax` –
|
|
130
|
+
* `cancelAjax` – you can pass an [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) in `fetchParams`
|
|
115
131
|
* `loadBlob` – use `URL.createObjectURL()` to convert a blob to a URL and call `load(url)` instead
|
|
116
132
|
* `skipForward`, `skipBackward`, `setPlayEnd` – can be implemented using `setTime(time)`
|
|
117
|
-
* `exportPCM` is
|
|
133
|
+
* `exportPCM` is replaced with `getDecodedData` that returns a decoded audio buffer
|
|
118
134
|
* `toggleMute` is now called `setMuted(true | false)`
|
|
119
135
|
* `setHeight`, `setWaveColor`, `setCursorColor`, etc. – use `setOptions` with the corresponding params instead. E.g., `wavesurfer.setOptions({ height: 300, waveColor: '#abc' })`
|
|
120
136
|
|
|
121
|
-
See the complete [documentation of the new API](http://wavesurfer-js.org/docs
|
|
137
|
+
See the complete [documentation of the new API](http://wavesurfer-js.org/docs).
|
|
122
138
|
|
|
123
139
|
## Questions
|
|
124
140
|
|
|
@@ -127,7 +143,18 @@ Have a question about integrating wavesurfer.js on your website? Feel free to as
|
|
|
127
143
|
### FAQ
|
|
128
144
|
|
|
129
145
|
* **Q**: Does wavesurfer support large files?
|
|
130
|
-
* **A**: Since wavesurfer decodes audio entirely in the browser, large
|
|
146
|
+
* **A**: Since wavesurfer decodes audio entirely in the browser using Web Audio, large clips may fail to decode due to memory constraints. We recommend using pre-decoded peaks for large files (see [this example](https://wavesurfer-js.org/examples/#predecoded.js)). You can use a tool like [bbc/audiowaveform](https://github.com/bbc/audiowaveform) to generate peaks.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
* **Q**: What about streaming audio?
|
|
151
|
+
* **A**: Streaming isn't supported because wavesurfer needs to download the entire audio file to decode and render it.
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
* **Q**: There is a mismatch between my audio and the waveform.
|
|
156
|
+
* **A**: If you're using a VBR (variable bit rate) mp3 file, there might be a mismatch between the audio and the waveform. This can be fixed by converting your file to CBR (constant bit rate). See [this issue](https://github.com/katspaugh/wavesurfer.js/issues/2890#issuecomment-1601067822) for details.
|
|
157
|
+
|
|
131
158
|
|
|
132
159
|
## Development
|
|
133
160
|
|
|
@@ -150,16 +177,21 @@ This command will open http://localhost:9090 in your browser with live reload, a
|
|
|
150
177
|
## Tests
|
|
151
178
|
|
|
152
179
|
The tests are written in the Cypress framework. They are a mix of e2e and visual regression tests.
|
|
153
|
-
To run the test suite locally:
|
|
154
180
|
|
|
181
|
+
To run the test suite locally, first build the project:
|
|
182
|
+
```
|
|
183
|
+
yarn build
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Then launch the tests:
|
|
155
187
|
```
|
|
156
188
|
yarn cypress
|
|
157
189
|
```
|
|
158
190
|
|
|
159
191
|
## Feedback
|
|
160
192
|
|
|
161
|
-
We appreciate your feedback and contributions!
|
|
193
|
+
We appreciate your feedback and contributions!
|
|
162
194
|
|
|
163
|
-
If you encounter any issues or have suggestions for improvements, please don't hesitate to
|
|
195
|
+
If you encounter any issues or have suggestions for improvements, please don't hesitate to post in our [forum](https://github.com/wavesurfer-js/wavesurfer.js/discussions/categories/q-a).
|
|
164
196
|
|
|
165
197
|
We hope you enjoy using wavesurfer.ts and look forward to hearing about your experiences with the library!
|
package/dist/base-plugin.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import EventEmitter
|
|
1
|
+
import EventEmitter from './event-emitter.js';
|
|
2
2
|
import type WaveSurfer from './wavesurfer.js';
|
|
3
|
-
export type
|
|
4
|
-
|
|
3
|
+
export type BasePluginEvents = {
|
|
4
|
+
destroy: [];
|
|
5
|
+
};
|
|
6
|
+
export type GenericPlugin = BasePlugin<BasePluginEvents, unknown>;
|
|
7
|
+
export declare class BasePlugin<EventTypes extends BasePluginEvents, Options> extends EventEmitter<EventTypes> {
|
|
5
8
|
protected wavesurfer?: WaveSurfer;
|
|
6
9
|
protected subscriptions: (() => void)[];
|
|
7
10
|
protected options: Options;
|
package/dist/base-plugin.js
CHANGED
package/dist/decoder.js
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
1
10
|
/** Decode an array buffer into an audio buffer */
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
11
|
+
function decode(audioData, sampleRate) {
|
|
12
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
13
|
+
const audioCtx = new AudioContext({ sampleRate });
|
|
14
|
+
const decode = audioCtx.decodeAudioData(audioData);
|
|
15
|
+
decode.finally(() => audioCtx.close());
|
|
16
|
+
return decode;
|
|
17
|
+
});
|
|
7
18
|
}
|
|
8
19
|
/** Normalize peaks to -1..1 */
|
|
9
20
|
function normalize(channelData) {
|
|
@@ -36,7 +47,7 @@ function createBuffer(channelData, duration) {
|
|
|
36
47
|
length: channelData[0].length,
|
|
37
48
|
sampleRate: channelData[0].length / duration,
|
|
38
49
|
numberOfChannels: channelData.length,
|
|
39
|
-
getChannelData: (i) => channelData
|
|
50
|
+
getChannelData: (i) => channelData === null || channelData === void 0 ? void 0 : channelData[i],
|
|
40
51
|
copyFromChannel: AudioBuffer.prototype.copyFromChannel,
|
|
41
52
|
copyToChannel: AudioBuffer.prototype.copyToChannel,
|
|
42
53
|
};
|
package/dist/draggable.js
CHANGED
|
@@ -5,6 +5,9 @@ export function makeDraggable(element, onDrag, onStart, onEnd, threshold = 5) {
|
|
|
5
5
|
if (!element)
|
|
6
6
|
return unsub;
|
|
7
7
|
const down = (e) => {
|
|
8
|
+
// Ignore the right mouse button
|
|
9
|
+
if (e.button === 2)
|
|
10
|
+
return;
|
|
8
11
|
e.preventDefault();
|
|
9
12
|
e.stopPropagation();
|
|
10
13
|
let startX = e.clientX;
|
|
@@ -16,11 +19,11 @@ export function makeDraggable(element, onDrag, onStart, onEnd, threshold = 5) {
|
|
|
16
19
|
const x = e.clientX;
|
|
17
20
|
const y = e.clientY;
|
|
18
21
|
if (isDragging || Math.abs(x - startX) >= threshold || Math.abs(y - startY) >= threshold) {
|
|
22
|
+
const { left, top } = element.getBoundingClientRect();
|
|
19
23
|
if (!isDragging) {
|
|
20
24
|
isDragging = true;
|
|
21
|
-
onStart
|
|
25
|
+
onStart === null || onStart === void 0 ? void 0 : onStart(startX - left, startY - top);
|
|
22
26
|
}
|
|
23
|
-
const { left, top } = element.getBoundingClientRect();
|
|
24
27
|
onDrag(x - startX, y - startY, x - left, y - top);
|
|
25
28
|
startX = x;
|
|
26
29
|
startY = y;
|
|
@@ -34,10 +37,14 @@ export function makeDraggable(element, onDrag, onStart, onEnd, threshold = 5) {
|
|
|
34
37
|
};
|
|
35
38
|
const up = () => {
|
|
36
39
|
if (isDragging) {
|
|
37
|
-
onEnd
|
|
40
|
+
onEnd === null || onEnd === void 0 ? void 0 : onEnd();
|
|
38
41
|
}
|
|
39
42
|
unsub();
|
|
40
43
|
};
|
|
44
|
+
document.addEventListener('pointermove', move);
|
|
45
|
+
document.addEventListener('pointerup', up);
|
|
46
|
+
document.addEventListener('pointerleave', up);
|
|
47
|
+
document.addEventListener('click', click, true);
|
|
41
48
|
unsub = () => {
|
|
42
49
|
document.removeEventListener('pointermove', move);
|
|
43
50
|
document.removeEventListener('pointerup', up);
|
|
@@ -46,10 +53,6 @@ export function makeDraggable(element, onDrag, onStart, onEnd, threshold = 5) {
|
|
|
46
53
|
document.removeEventListener('click', click, true);
|
|
47
54
|
}, 10);
|
|
48
55
|
};
|
|
49
|
-
document.addEventListener('pointermove', move);
|
|
50
|
-
document.addEventListener('pointerup', up);
|
|
51
|
-
document.addEventListener('pointerleave', up);
|
|
52
|
-
document.addEventListener('click', click, true);
|
|
53
56
|
};
|
|
54
57
|
element.addEventListener('pointerdown', down);
|
|
55
58
|
return () => {
|
package/dist/fetcher.d.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
declare function
|
|
2
|
-
declare function fetchBlob(url: string): Promise<Blob>;
|
|
1
|
+
declare function fetchBlob(url: string, init?: RequestInit): Promise<Blob>;
|
|
3
2
|
declare const Fetcher: {
|
|
4
|
-
fetchArrayBuffer: typeof fetchArrayBuffer;
|
|
5
3
|
fetchBlob: typeof fetchBlob;
|
|
6
4
|
};
|
|
7
5
|
export default Fetcher;
|
package/dist/fetcher.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
function fetchBlob(url, init) {
|
|
11
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
12
|
+
return fetch(url, init).then((response) => response.blob());
|
|
13
|
+
});
|
|
6
14
|
}
|
|
7
15
|
const Fetcher = {
|
|
8
|
-
fetchArrayBuffer,
|
|
9
16
|
fetchBlob,
|
|
10
17
|
};
|
|
11
18
|
export default Fetcher;
|
package/dist/player.d.ts
CHANGED
|
@@ -6,13 +6,13 @@ type PlayerOptions = {
|
|
|
6
6
|
};
|
|
7
7
|
declare class Player<T extends GeneralEventTypes> extends EventEmitter<T> {
|
|
8
8
|
protected media: HTMLMediaElement;
|
|
9
|
-
private isExternalMedia;
|
|
10
9
|
constructor(options: PlayerOptions);
|
|
11
10
|
protected onMediaEvent(event: keyof HTMLMediaElementEventMap, callback: () => void, options?: AddEventListenerOptions): () => void;
|
|
12
11
|
protected onceMediaEvent(event: keyof HTMLMediaElementEventMap, callback: () => void): () => void;
|
|
12
|
+
private getSrc;
|
|
13
13
|
private revokeSrc;
|
|
14
14
|
protected setSrc(url: string, blob?: Blob): void;
|
|
15
|
-
destroy(): void;
|
|
15
|
+
protected destroy(): void;
|
|
16
16
|
/** Start playing the audio */
|
|
17
17
|
play(): Promise<void>;
|
|
18
18
|
/** Pause the audio */
|
package/dist/player.js
CHANGED
|
@@ -2,10 +2,8 @@ import EventEmitter from './event-emitter.js';
|
|
|
2
2
|
class Player extends EventEmitter {
|
|
3
3
|
constructor(options) {
|
|
4
4
|
super();
|
|
5
|
-
this.isExternalMedia = false;
|
|
6
5
|
if (options.media) {
|
|
7
6
|
this.media = options.media;
|
|
8
|
-
this.isExternalMedia = true;
|
|
9
7
|
}
|
|
10
8
|
else {
|
|
11
9
|
this.media = document.createElement('audio');
|
|
@@ -26,26 +24,30 @@ class Player extends EventEmitter {
|
|
|
26
24
|
onceMediaEvent(event, callback) {
|
|
27
25
|
return this.onMediaEvent(event, callback, { once: true });
|
|
28
26
|
}
|
|
27
|
+
getSrc() {
|
|
28
|
+
return this.media.currentSrc || this.media.src || '';
|
|
29
|
+
}
|
|
29
30
|
revokeSrc() {
|
|
30
|
-
const src = this.
|
|
31
|
+
const src = this.getSrc();
|
|
31
32
|
if (src.startsWith('blob:')) {
|
|
32
|
-
URL.revokeObjectURL(
|
|
33
|
+
URL.revokeObjectURL(src);
|
|
33
34
|
}
|
|
34
35
|
}
|
|
35
36
|
setSrc(url, blob) {
|
|
36
|
-
const src = this.
|
|
37
|
+
const src = this.getSrc();
|
|
37
38
|
if (src === url)
|
|
38
39
|
return;
|
|
39
40
|
this.revokeSrc();
|
|
40
41
|
const newSrc = blob instanceof Blob ? URL.createObjectURL(blob) : url;
|
|
41
42
|
this.media.src = newSrc;
|
|
43
|
+
this.media.load();
|
|
42
44
|
}
|
|
43
45
|
destroy() {
|
|
44
46
|
this.media.pause();
|
|
45
47
|
this.revokeSrc();
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
this.media.src = '';
|
|
49
|
+
// Load resets the media element to its initial state
|
|
50
|
+
this.media.load();
|
|
49
51
|
}
|
|
50
52
|
/** Start playing the audio */
|
|
51
53
|
play() {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import EventEmitter from './event-emitter.js';
|
|
2
|
+
import type WaveSurfer from './wavesurfer.js';
|
|
3
|
+
export type BasePluginEvents = {
|
|
4
|
+
destroy: [];
|
|
5
|
+
};
|
|
6
|
+
export type GenericPlugin = BasePlugin<BasePluginEvents, unknown>;
|
|
7
|
+
export declare class BasePlugin<EventTypes extends BasePluginEvents, Options> extends EventEmitter<EventTypes> {
|
|
8
|
+
protected wavesurfer?: WaveSurfer;
|
|
9
|
+
protected subscriptions: (() => void)[];
|
|
10
|
+
protected options: Options;
|
|
11
|
+
constructor(options: Options);
|
|
12
|
+
onInit(): void;
|
|
13
|
+
init(wavesurfer: WaveSurfer): void;
|
|
14
|
+
destroy(): void;
|
|
15
|
+
}
|
|
16
|
+
export default BasePlugin;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Decode an array buffer into an audio buffer */
|
|
2
|
+
declare function decode(audioData: ArrayBuffer, sampleRate: number): Promise<AudioBuffer>;
|
|
3
|
+
/** Create an audio buffer from pre-decoded audio data */
|
|
4
|
+
declare function createBuffer(channelData: Array<Float32Array | number[]>, duration: number): AudioBuffer;
|
|
5
|
+
declare const Decoder: {
|
|
6
|
+
decode: typeof decode;
|
|
7
|
+
createBuffer: typeof createBuffer;
|
|
8
|
+
};
|
|
9
|
+
export default Decoder;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function makeDraggable(element: HTMLElement | null, onDrag: (dx: number, dy: number, x: number, y: number) => void, onStart?: (x: number, y: number) => void, onEnd?: () => void, threshold?: number): () => void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";class t{constructor(){this.listeners={}}on(t,e){return this.listeners[t]||(this.listeners[t]=new Set),this.listeners[t].add(e),()=>this.un(t,e)}once(t,e){const i=this.on(t,e),n=this.on(t,(()=>{i(),n()}));return i}un(t,e){this.listeners[t]&&(e?this.listeners[t].delete(e):delete this.listeners[t])}unAll(){this.listeners={}}emit(t,...e){this.listeners[t]&&this.listeners[t].forEach((t=>t(...e)))}}class e extends t{constructor(t){super(),this.subscriptions=[],this.options=t}onInit(){}init(t){this.wavesurfer=t,this.onInit()}destroy(){this.emit("destroy"),this.subscriptions.forEach((t=>t()))}}const i={fadeInStart:0,fadeOutEnd:0,fadeInEnd:0,fadeOutStart:0,lineWidth:4,lineColor:"rgba(0, 0, 255, 0.5)",dragPointSize:10,dragPointFill:"rgba(255, 255, 255, 0.8)",dragPointStroke:"rgba(255, 255, 255, 0.8)"};class n extends t{constructor(t,e){super(),this.top=0,this.padding=t.dragPointSize/2+1;const i=document.createElementNS("http://www.w3.org/2000/svg","svg");i.setAttribute("width","100%"),i.setAttribute("height","100%"),i.setAttribute("viewBox","0 0 0 0"),i.setAttribute("preserveAspectRatio","none"),i.setAttribute("style","position: absolute; left: 0; top: 0; z-index: 4; pointer-events: none;"),i.setAttribute("part","envelope"),this.svg=i;const n=document.createElementNS("http://www.w3.org/2000/svg","polyline");n.setAttribute("points","0,0 0,0 0,0 0,0"),n.setAttribute("stroke",t.lineColor),n.setAttribute("stroke-width",t.lineWidth),n.setAttribute("fill","none"),n.setAttribute("style","pointer-events: none;"),n.setAttribute("part","polyline"),i.appendChild(n);const s=document.createElementNS("http://www.w3.org/2000/svg","line");s.setAttribute("stroke","transparent"),s.setAttribute("stroke-width",(3*t.lineWidth).toString()),s.setAttribute("style","cursor: ns-resize; pointer-events: all;"),s.setAttribute("part","line"),i.appendChild(s),[0,1].forEach((()=>{const e=document.createElementNS("http://www.w3.org/2000/svg","circle");e.setAttribute("r",(t.dragPointSize/2).toString()),e.setAttribute("fill",t.dragPointFill),e.setAttribute("stroke",t.dragPointStroke||t.dragPointFill),e.setAttribute("stroke-width","2"),e.setAttribute("style","cursor: ew-resize; pointer-events: all;"),e.setAttribute("part","circle"),i.appendChild(e)})),e.appendChild(i);{const t=t=>{const e=this.top+t,{height:n}=i.viewBox.baseVal;if(e<-.5||e>n)return;const s=Math.min(1,Math.max(0,(n-e)/n));this.emit("line-move",s)},e=(t,e)=>{const s=n.points.getItem(t).x+e,{width:o}=i.viewBox.baseVal;this.emit("point-move",t,s/o)};this.makeDraggable(s,((e,i)=>t(i)));const o=this.svg.querySelectorAll("circle");Array.from(o).forEach(((t,i)=>{this.makeDraggable(t,(t=>e(i+1,t)))}))}}makeDraggable(t,e){!function(t,e,i,n,s=5){let o=()=>{};if(!t)return o;const r=r=>{if(2===r.button)return;r.preventDefault(),r.stopPropagation();let a=r.clientX,u=r.clientY,d=!1;const h=n=>{n.preventDefault(),n.stopPropagation();const o=n.clientX,r=n.clientY;if(d||Math.abs(o-a)>=s||Math.abs(r-u)>=s){const{left:n,top:s}=t.getBoundingClientRect();d||(d=!0,null==i||i(a-n,u-s)),e(o-a,r-u,o-n,r-s),a=o,u=r}},l=t=>{d&&(t.preventDefault(),t.stopPropagation())},p=()=>{d&&(null==n||n()),o()};document.addEventListener("pointermove",h),document.addEventListener("pointerup",p),document.addEventListener("pointerleave",p),document.addEventListener("click",l,!0),o=()=>{document.removeEventListener("pointermove",h),document.removeEventListener("pointerup",p),document.removeEventListener("pointerleave",p),setTimeout((()=>{document.removeEventListener("click",l,!0)}),10)}};t.addEventListener("pointerdown",r)}(t,e)}update({x1:t,x2:e,x3:i,x4:n,y:s}){const o=this.svg.clientWidth,r=this.svg.clientHeight;this.top=r-s*r;const a=Math.max(this.padding,Math.min(this.top,r-this.padding));this.svg.setAttribute("viewBox",`0 0 ${o} ${r}`);const u=this.svg.querySelector("polyline"),{points:d}=u;d.getItem(0).x=t*o,d.getItem(0).y=r,d.getItem(1).x=e*o,d.getItem(1).y=a,d.getItem(2).x=i*o,d.getItem(2).y=a,d.getItem(3).x=n*o,d.getItem(3).y=r;const h=this.svg.querySelector("line");h.setAttribute("x1",d.getItem(1).x.toString()),h.setAttribute("x2",d.getItem(2).x.toString()),h.setAttribute("y1",a.toString()),h.setAttribute("y2",a.toString());const l=this.svg.querySelectorAll("circle");Array.from(l).forEach(((t,e)=>{const i=d.getItem(e+1);t.setAttribute("cx",i.x.toString()),t.setAttribute("cy",i.y.toString())}))}destroy(){this.svg.remove()}}class s extends e{constructor(t){var e;super(t),this.polyline=null,this.audioContext=null,this.gainNode=null,this.volume=1,this.isFadingIn=!1,this.isFadingOut=!1,this.naturalVolumeExponent=1.5,this.options=Object.assign({},i,t),this.options.lineColor=this.options.lineColor||i.lineColor,this.options.dragPointFill=this.options.dragPointFill||i.dragPointFill,this.options.dragPointStroke=this.options.dragPointStroke||i.dragPointStroke,this.volume=null!==(e=this.options.volume)&&void 0!==e?e:1}static create(t){return new s(t)}destroy(){var t;null===(t=this.polyline)||void 0===t||t.destroy(),super.destroy()}onInit(){if(!this.wavesurfer)throw Error("WaveSurfer is not initialized");this.initWebAudio(),this.initSvg(),this.initFadeEffects();const t=this.wavesurfer.getMediaElement(),e=()=>this.setVolume(t.volume);t.addEventListener("volumechange",e),this.subscriptions.push((()=>t.removeEventListener("volumechange",e)),this.wavesurfer.on("redraw",(()=>{var t;const e=null===(t=this.wavesurfer)||void 0===t?void 0:t.getDuration();e&&(this.options.fadeInStart=this.options.fadeInStart||0,this.options.fadeOutEnd=this.options.fadeOutEnd||e,this.options.fadeInEnd=this.options.fadeInEnd||this.options.fadeInStart,this.options.fadeOutStart=this.options.fadeOutStart||this.options.fadeOutEnd,this.renderPolyline())})))}initSvg(){if(!this.wavesurfer)return;const t=this.wavesurfer.getWrapper();this.polyline=new n(this.options,t),this.subscriptions.push(this.polyline.on("line-move",(t=>{this.setVolume(this.naturalVolume(t))})),this.polyline.on("point-move",((t,e)=>{var i;const n=e*((null===(i=this.wavesurfer)||void 0===i?void 0:i.getDuration())||0);if(1===t){if(n<this.options.fadeInStart||n>this.options.fadeOutStart)return;this.options.fadeInEnd=n,this.emit("fade-in-change",n)}else if(2===t){if(n>this.options.fadeOutEnd||n<this.options.fadeInEnd)return;this.options.fadeOutStart=n,this.emit("fade-out-change",n)}this.renderPolyline()})))}renderPolyline(){if(!this.polyline||!this.wavesurfer)return;const t=this.wavesurfer.getDuration();t&&this.polyline.update({x1:this.options.fadeInStart/t,x2:this.options.fadeInEnd/t,x3:this.options.fadeOutStart/t,x4:this.options.fadeOutEnd/t,y:this.invertNaturalVolume(this.volume)})}initWebAudio(){var t,e;const i=null===(t=this.wavesurfer)||void 0===t?void 0:t.getMediaElement();if(!i)return null;this.volume=null!==(e=this.options.volume)&&void 0!==e?e:i.volume;const n=new window.AudioContext;this.gainNode=n.createGain(),this.setGainValue();n.createMediaElementSource(i).connect(this.gainNode),this.gainNode.connect(n.destination),this.audioContext=n}invertNaturalVolume(t){const e=1e-4;return Math.pow((t-e)/.9999,1/this.naturalVolumeExponent)}naturalVolume(t){const e=1e-4;return e+.9999*Math.pow(t,this.naturalVolumeExponent)}setGainValue(){this.gainNode&&(this.gainNode.gain.value=this.volume)}initFadeEffects(){if(!this.audioContext||!this.wavesurfer)return;const t=this.wavesurfer.on("timeupdate",(t=>{var e;if(!this.audioContext||!this.gainNode)return;if(!(null===(e=this.wavesurfer)||void 0===e?void 0:e.isPlaying()))return;if("suspended"===this.audioContext.state&&this.audioContext.resume(),!this.isFadingIn&&t>=this.options.fadeInStart&&t<=this.options.fadeInEnd)return this.isFadingIn=!0,this.gainNode.gain.setValueAtTime(0,this.audioContext.currentTime),void this.gainNode.gain.linearRampToValueAtTime(this.volume,this.audioContext.currentTime+(this.options.fadeInEnd-t));if(!this.isFadingOut&&t>=this.options.fadeOutStart&&t<=this.options.fadeOutEnd)return this.isFadingOut=!0,this.gainNode.gain.setValueAtTime(this.volume,this.audioContext.currentTime),void this.gainNode.gain.linearRampToValueAtTime(0,this.audioContext.currentTime+(this.options.fadeOutEnd-t));let i=!1;this.isFadingIn&&(t<this.options.fadeInStart||t>this.options.fadeInEnd)&&(this.isFadingIn=!1,i=!0),this.isFadingOut&&(t<this.options.fadeOutStart||t>=this.options.fadeOutEnd)&&(this.isFadingOut=!1,i=!0),i&&(this.gainNode.gain.cancelScheduledValues(this.audioContext.currentTime),this.setGainValue())}));this.subscriptions.push(t)}getCurrentVolume(){return this.gainNode?this.gainNode.gain.value:this.volume}setStartTime(t,e=!1){if(e){const e=this.options.fadeInEnd-this.options.fadeInStart;this.options.fadeInEnd=t+e}this.options.fadeInStart=t,this.renderPolyline()}setEndTime(t,e=!1){if(e){const e=this.options.fadeOutEnd-this.options.fadeOutStart;this.options.fadeOutStart=t-e}this.options.fadeOutEnd=t,this.renderPolyline()}setFadeInEnd(t){this.options.fadeInEnd=t,this.renderPolyline()}setFadeOutStart(t){this.options.fadeOutStart=t,this.renderPolyline()}setVolume(t){this.volume=t,this.setGainValue(),this.renderPolyline(),this.emit("volume-change",t)}}module.exports=s;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Envelope is a visual UI for controlling the audio volume and add fade-in and fade-out effects.
|
|
3
3
|
*/
|
|
4
|
-
import BasePlugin from '../base-plugin.js';
|
|
4
|
+
import BasePlugin, { type BasePluginEvents } from '../base-plugin.js';
|
|
5
5
|
export type EnvelopePluginOptions = {
|
|
6
6
|
fadeInStart?: number;
|
|
7
7
|
fadeInEnd?: number;
|
|
@@ -26,7 +26,7 @@ declare const defaultOptions: {
|
|
|
26
26
|
dragPointStroke: string;
|
|
27
27
|
};
|
|
28
28
|
type Options = EnvelopePluginOptions & typeof defaultOptions;
|
|
29
|
-
export type EnvelopePluginEvents = {
|
|
29
|
+
export type EnvelopePluginEvents = BasePluginEvents & {
|
|
30
30
|
'fade-in-change': [time: number];
|
|
31
31
|
'fade-out-change': [time: number];
|
|
32
32
|
'volume-change': [volume: number];
|
|
@@ -60,11 +60,19 @@ declare class EnvelopePlugin extends BasePlugin<EnvelopePluginEvents, EnvelopePl
|
|
|
60
60
|
* @param moveFadeInEnd Whether to move the drag point to the new time (default: false)
|
|
61
61
|
*/
|
|
62
62
|
setStartTime(time: number, moveFadeInEnd?: boolean): void;
|
|
63
|
-
/** Set the fade-
|
|
64
|
-
* @param time The time (in seconds) to set the fade-
|
|
63
|
+
/** Set the fade-out end time.
|
|
64
|
+
* @param time The time (in seconds) to set the fade-out end time to
|
|
65
65
|
* @param moveFadeOutStart Whether to move the drag point to the new time (default: false)
|
|
66
66
|
*/
|
|
67
67
|
setEndTime(time: number, moveFadeOutStart?: boolean): void;
|
|
68
|
+
/** Set the fade-in end time.
|
|
69
|
+
* @param time The time (in seconds) to set the fade-in end time to
|
|
70
|
+
*/
|
|
71
|
+
setFadeInEnd(time: number): void;
|
|
72
|
+
/** Set the fade-out start time.
|
|
73
|
+
* @param time The time (in seconds) to set the fade-out start time to
|
|
74
|
+
*/
|
|
75
|
+
setFadeOutStart(time: number): void;
|
|
68
76
|
/** Set the volume of the audio */
|
|
69
77
|
setVolume(volume: number): void;
|
|
70
78
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class t{constructor(){this.listeners={}}on(t,e){return this.listeners[t]||(this.listeners[t]=new Set),this.listeners[t].add(e),()=>this.un(t,e)}once(t,e){const i=this.on(t,e),n=this.on(t,(()=>{i(),n()}));return i}un(t,e){this.listeners[t]&&(e?this.listeners[t].delete(e):delete this.listeners[t])}unAll(){this.listeners={}}emit(t,...e){this.listeners[t]&&this.listeners[t].forEach((t=>t(...e)))}}class e extends t{constructor(t){super(),this.subscriptions=[],this.options=t}onInit(){}init(t){this.wavesurfer=t,this.onInit()}destroy(){this.emit("destroy"),this.subscriptions.forEach((t=>t()))}}const i={fadeInStart:0,fadeOutEnd:0,fadeInEnd:0,fadeOutStart:0,lineWidth:4,lineColor:"rgba(0, 0, 255, 0.5)",dragPointSize:10,dragPointFill:"rgba(255, 255, 255, 0.8)",dragPointStroke:"rgba(255, 255, 255, 0.8)"};class n extends t{constructor(t,e){super(),this.top=0,this.padding=t.dragPointSize/2+1;const i=document.createElementNS("http://www.w3.org/2000/svg","svg");i.setAttribute("width","100%"),i.setAttribute("height","100%"),i.setAttribute("viewBox","0 0 0 0"),i.setAttribute("preserveAspectRatio","none"),i.setAttribute("style","position: absolute; left: 0; top: 0; z-index: 4; pointer-events: none;"),i.setAttribute("part","envelope"),this.svg=i;const n=document.createElementNS("http://www.w3.org/2000/svg","polyline");n.setAttribute("points","0,0 0,0 0,0 0,0"),n.setAttribute("stroke",t.lineColor),n.setAttribute("stroke-width",t.lineWidth),n.setAttribute("fill","none"),n.setAttribute("style","pointer-events: none;"),n.setAttribute("part","polyline"),i.appendChild(n);const s=document.createElementNS("http://www.w3.org/2000/svg","line");s.setAttribute("stroke","transparent"),s.setAttribute("stroke-width",(3*t.lineWidth).toString()),s.setAttribute("style","cursor: ns-resize; pointer-events: all;"),s.setAttribute("part","line"),i.appendChild(s),[0,1].forEach((()=>{const e=document.createElementNS("http://www.w3.org/2000/svg","circle");e.setAttribute("r",(t.dragPointSize/2).toString()),e.setAttribute("fill",t.dragPointFill),e.setAttribute("stroke",t.dragPointStroke||t.dragPointFill),e.setAttribute("stroke-width","2"),e.setAttribute("style","cursor: ew-resize; pointer-events: all;"),e.setAttribute("part","circle"),i.appendChild(e)})),e.appendChild(i);{const t=t=>{const e=this.top+t,{height:n}=i.viewBox.baseVal;if(e<-.5||e>n)return;const s=Math.min(1,Math.max(0,(n-e)/n));this.emit("line-move",s)},e=(t,e)=>{const s=n.points.getItem(t).x+e,{width:o}=i.viewBox.baseVal;this.emit("point-move",t,s/o)};this.makeDraggable(s,((e,i)=>t(i)));const o=this.svg.querySelectorAll("circle");Array.from(o).forEach(((t,i)=>{this.makeDraggable(t,(t=>e(i+1,t)))}))}}makeDraggable(t,e){!function(t,e,i,n,s=5){let o=()=>{};if(!t)return o;const r=r=>{if(2===r.button)return;r.preventDefault(),r.stopPropagation();let a=r.clientX,u=r.clientY,d=!1;const h=n=>{n.preventDefault(),n.stopPropagation();const o=n.clientX,r=n.clientY;if(d||Math.abs(o-a)>=s||Math.abs(r-u)>=s){const{left:n,top:s}=t.getBoundingClientRect();d||(d=!0,null==i||i(a-n,u-s)),e(o-a,r-u,o-n,r-s),a=o,u=r}},l=t=>{d&&(t.preventDefault(),t.stopPropagation())},p=()=>{d&&(null==n||n()),o()};document.addEventListener("pointermove",h),document.addEventListener("pointerup",p),document.addEventListener("pointerleave",p),document.addEventListener("click",l,!0),o=()=>{document.removeEventListener("pointermove",h),document.removeEventListener("pointerup",p),document.removeEventListener("pointerleave",p),setTimeout((()=>{document.removeEventListener("click",l,!0)}),10)}};t.addEventListener("pointerdown",r)}(t,e)}update({x1:t,x2:e,x3:i,x4:n,y:s}){const o=this.svg.clientWidth,r=this.svg.clientHeight;this.top=r-s*r;const a=Math.max(this.padding,Math.min(this.top,r-this.padding));this.svg.setAttribute("viewBox",`0 0 ${o} ${r}`);const u=this.svg.querySelector("polyline"),{points:d}=u;d.getItem(0).x=t*o,d.getItem(0).y=r,d.getItem(1).x=e*o,d.getItem(1).y=a,d.getItem(2).x=i*o,d.getItem(2).y=a,d.getItem(3).x=n*o,d.getItem(3).y=r;const h=this.svg.querySelector("line");h.setAttribute("x1",d.getItem(1).x.toString()),h.setAttribute("x2",d.getItem(2).x.toString()),h.setAttribute("y1",a.toString()),h.setAttribute("y2",a.toString());const l=this.svg.querySelectorAll("circle");Array.from(l).forEach(((t,e)=>{const i=d.getItem(e+1);t.setAttribute("cx",i.x.toString()),t.setAttribute("cy",i.y.toString())}))}destroy(){this.svg.remove()}}class s extends e{constructor(t){var e;super(t),this.polyline=null,this.audioContext=null,this.gainNode=null,this.volume=1,this.isFadingIn=!1,this.isFadingOut=!1,this.naturalVolumeExponent=1.5,this.options=Object.assign({},i,t),this.options.lineColor=this.options.lineColor||i.lineColor,this.options.dragPointFill=this.options.dragPointFill||i.dragPointFill,this.options.dragPointStroke=this.options.dragPointStroke||i.dragPointStroke,this.volume=null!==(e=this.options.volume)&&void 0!==e?e:1}static create(t){return new s(t)}destroy(){var t;null===(t=this.polyline)||void 0===t||t.destroy(),super.destroy()}onInit(){if(!this.wavesurfer)throw Error("WaveSurfer is not initialized");this.initWebAudio(),this.initSvg(),this.initFadeEffects();const t=this.wavesurfer.getMediaElement(),e=()=>this.setVolume(t.volume);t.addEventListener("volumechange",e),this.subscriptions.push((()=>t.removeEventListener("volumechange",e)),this.wavesurfer.on("redraw",(()=>{var t;const e=null===(t=this.wavesurfer)||void 0===t?void 0:t.getDuration();e&&(this.options.fadeInStart=this.options.fadeInStart||0,this.options.fadeOutEnd=this.options.fadeOutEnd||e,this.options.fadeInEnd=this.options.fadeInEnd||this.options.fadeInStart,this.options.fadeOutStart=this.options.fadeOutStart||this.options.fadeOutEnd,this.renderPolyline())})))}initSvg(){if(!this.wavesurfer)return;const t=this.wavesurfer.getWrapper();this.polyline=new n(this.options,t),this.subscriptions.push(this.polyline.on("line-move",(t=>{this.setVolume(this.naturalVolume(t))})),this.polyline.on("point-move",((t,e)=>{var i;const n=e*((null===(i=this.wavesurfer)||void 0===i?void 0:i.getDuration())||0);if(1===t){if(n<this.options.fadeInStart||n>this.options.fadeOutStart)return;this.options.fadeInEnd=n,this.emit("fade-in-change",n)}else if(2===t){if(n>this.options.fadeOutEnd||n<this.options.fadeInEnd)return;this.options.fadeOutStart=n,this.emit("fade-out-change",n)}this.renderPolyline()})))}renderPolyline(){if(!this.polyline||!this.wavesurfer)return;const t=this.wavesurfer.getDuration();t&&this.polyline.update({x1:this.options.fadeInStart/t,x2:this.options.fadeInEnd/t,x3:this.options.fadeOutStart/t,x4:this.options.fadeOutEnd/t,y:this.invertNaturalVolume(this.volume)})}initWebAudio(){var t,e;const i=null===(t=this.wavesurfer)||void 0===t?void 0:t.getMediaElement();if(!i)return null;this.volume=null!==(e=this.options.volume)&&void 0!==e?e:i.volume;const n=new window.AudioContext;this.gainNode=n.createGain(),this.setGainValue();n.createMediaElementSource(i).connect(this.gainNode),this.gainNode.connect(n.destination),this.audioContext=n}invertNaturalVolume(t){const e=1e-4;return Math.pow((t-e)/.9999,1/this.naturalVolumeExponent)}naturalVolume(t){const e=1e-4;return e+.9999*Math.pow(t,this.naturalVolumeExponent)}setGainValue(){this.gainNode&&(this.gainNode.gain.value=this.volume)}initFadeEffects(){if(!this.audioContext||!this.wavesurfer)return;const t=this.wavesurfer.on("timeupdate",(t=>{var e;if(!this.audioContext||!this.gainNode)return;if(!(null===(e=this.wavesurfer)||void 0===e?void 0:e.isPlaying()))return;if("suspended"===this.audioContext.state&&this.audioContext.resume(),!this.isFadingIn&&t>=this.options.fadeInStart&&t<=this.options.fadeInEnd)return this.isFadingIn=!0,this.gainNode.gain.setValueAtTime(0,this.audioContext.currentTime),void this.gainNode.gain.linearRampToValueAtTime(this.volume,this.audioContext.currentTime+(this.options.fadeInEnd-t));if(!this.isFadingOut&&t>=this.options.fadeOutStart&&t<=this.options.fadeOutEnd)return this.isFadingOut=!0,this.gainNode.gain.setValueAtTime(this.volume,this.audioContext.currentTime),void this.gainNode.gain.linearRampToValueAtTime(0,this.audioContext.currentTime+(this.options.fadeOutEnd-t));let i=!1;this.isFadingIn&&(t<this.options.fadeInStart||t>this.options.fadeInEnd)&&(this.isFadingIn=!1,i=!0),this.isFadingOut&&(t<this.options.fadeOutStart||t>=this.options.fadeOutEnd)&&(this.isFadingOut=!1,i=!0),i&&(this.gainNode.gain.cancelScheduledValues(this.audioContext.currentTime),this.setGainValue())}));this.subscriptions.push(t)}getCurrentVolume(){return this.gainNode?this.gainNode.gain.value:this.volume}setStartTime(t,e=!1){if(e){const e=this.options.fadeInEnd-this.options.fadeInStart;this.options.fadeInEnd=t+e}this.options.fadeInStart=t,this.renderPolyline()}setEndTime(t,e=!1){if(e){const e=this.options.fadeOutEnd-this.options.fadeOutStart;this.options.fadeOutStart=t-e}this.options.fadeOutEnd=t,this.renderPolyline()}setFadeInEnd(t){this.options.fadeInEnd=t,this.renderPolyline()}setFadeOutStart(t){this.options.fadeOutStart=t,this.renderPolyline()}setVolume(t){this.volume=t,this.setGainValue(),this.renderPolyline(),this.emit("volume-change",t)}}export{s as default};
|
package/dist/plugins/envelope.js
CHANGED
|
@@ -121,6 +121,7 @@ class Polyline extends EventEmitter {
|
|
|
121
121
|
}
|
|
122
122
|
class EnvelopePlugin extends BasePlugin {
|
|
123
123
|
constructor(options) {
|
|
124
|
+
var _a;
|
|
124
125
|
super(options);
|
|
125
126
|
this.polyline = null;
|
|
126
127
|
this.audioContext = null;
|
|
@@ -134,13 +135,14 @@ class EnvelopePlugin extends BasePlugin {
|
|
|
134
135
|
this.options.lineColor = this.options.lineColor || defaultOptions.lineColor;
|
|
135
136
|
this.options.dragPointFill = this.options.dragPointFill || defaultOptions.dragPointFill;
|
|
136
137
|
this.options.dragPointStroke = this.options.dragPointStroke || defaultOptions.dragPointStroke;
|
|
137
|
-
this.volume = this.options.volume
|
|
138
|
+
this.volume = (_a = this.options.volume) !== null && _a !== void 0 ? _a : 1;
|
|
138
139
|
}
|
|
139
140
|
static create(options) {
|
|
140
141
|
return new EnvelopePlugin(options);
|
|
141
142
|
}
|
|
142
143
|
destroy() {
|
|
143
|
-
|
|
144
|
+
var _a;
|
|
145
|
+
(_a = this.polyline) === null || _a === void 0 ? void 0 : _a.destroy();
|
|
144
146
|
super.destroy();
|
|
145
147
|
}
|
|
146
148
|
/** Called by wavesurfer, don't call manually */
|
|
@@ -151,8 +153,13 @@ class EnvelopePlugin extends BasePlugin {
|
|
|
151
153
|
this.initWebAudio();
|
|
152
154
|
this.initSvg();
|
|
153
155
|
this.initFadeEffects();
|
|
154
|
-
|
|
155
|
-
|
|
156
|
+
// Sync the plugin's volume with the media element volume
|
|
157
|
+
const media = this.wavesurfer.getMediaElement();
|
|
158
|
+
const onMediaVolumeChange = () => this.setVolume(media.volume);
|
|
159
|
+
media.addEventListener('volumechange', onMediaVolumeChange);
|
|
160
|
+
this.subscriptions.push(() => media.removeEventListener('volumechange', onMediaVolumeChange), this.wavesurfer.on('redraw', () => {
|
|
161
|
+
var _a;
|
|
162
|
+
const duration = (_a = this.wavesurfer) === null || _a === void 0 ? void 0 : _a.getDuration();
|
|
156
163
|
if (!duration)
|
|
157
164
|
return;
|
|
158
165
|
this.options.fadeInStart = this.options.fadeInStart || 0;
|
|
@@ -170,7 +177,8 @@ class EnvelopePlugin extends BasePlugin {
|
|
|
170
177
|
this.subscriptions.push(this.polyline.on('line-move', (relativeY) => {
|
|
171
178
|
this.setVolume(this.naturalVolume(relativeY));
|
|
172
179
|
}), this.polyline.on('point-move', (index, relativeX) => {
|
|
173
|
-
|
|
180
|
+
var _a;
|
|
181
|
+
const duration = ((_a = this.wavesurfer) === null || _a === void 0 ? void 0 : _a.getDuration()) || 0;
|
|
174
182
|
const newTime = relativeX * duration;
|
|
175
183
|
// Fade-in end point
|
|
176
184
|
if (index === 1) {
|
|
@@ -204,10 +212,11 @@ class EnvelopePlugin extends BasePlugin {
|
|
|
204
212
|
});
|
|
205
213
|
}
|
|
206
214
|
initWebAudio() {
|
|
207
|
-
|
|
215
|
+
var _a, _b;
|
|
216
|
+
const audio = (_a = this.wavesurfer) === null || _a === void 0 ? void 0 : _a.getMediaElement();
|
|
208
217
|
if (!audio)
|
|
209
218
|
return null;
|
|
210
|
-
this.volume = this.options.volume
|
|
219
|
+
this.volume = (_b = this.options.volume) !== null && _b !== void 0 ? _b : audio.volume;
|
|
211
220
|
// Create an AudioContext
|
|
212
221
|
const audioContext = new window.AudioContext();
|
|
213
222
|
// Create a GainNode for controlling the volume
|
|
@@ -241,9 +250,10 @@ class EnvelopePlugin extends BasePlugin {
|
|
|
241
250
|
if (!this.audioContext || !this.wavesurfer)
|
|
242
251
|
return;
|
|
243
252
|
const unsub = this.wavesurfer.on('timeupdate', (currentTime) => {
|
|
253
|
+
var _a;
|
|
244
254
|
if (!this.audioContext || !this.gainNode)
|
|
245
255
|
return;
|
|
246
|
-
if (!this.wavesurfer
|
|
256
|
+
if (!((_a = this.wavesurfer) === null || _a === void 0 ? void 0 : _a.isPlaying()))
|
|
247
257
|
return;
|
|
248
258
|
if (this.audioContext.state === 'suspended') {
|
|
249
259
|
this.audioContext.resume();
|
|
@@ -303,8 +313,8 @@ class EnvelopePlugin extends BasePlugin {
|
|
|
303
313
|
this.options.fadeInStart = time;
|
|
304
314
|
this.renderPolyline();
|
|
305
315
|
}
|
|
306
|
-
/** Set the fade-
|
|
307
|
-
* @param time The time (in seconds) to set the fade-
|
|
316
|
+
/** Set the fade-out end time.
|
|
317
|
+
* @param time The time (in seconds) to set the fade-out end time to
|
|
308
318
|
* @param moveFadeOutStart Whether to move the drag point to the new time (default: false)
|
|
309
319
|
*/
|
|
310
320
|
setEndTime(time, moveFadeOutStart = false) {
|
|
@@ -315,6 +325,20 @@ class EnvelopePlugin extends BasePlugin {
|
|
|
315
325
|
this.options.fadeOutEnd = time;
|
|
316
326
|
this.renderPolyline();
|
|
317
327
|
}
|
|
328
|
+
/** Set the fade-in end time.
|
|
329
|
+
* @param time The time (in seconds) to set the fade-in end time to
|
|
330
|
+
*/
|
|
331
|
+
setFadeInEnd(time) {
|
|
332
|
+
this.options.fadeInEnd = time;
|
|
333
|
+
this.renderPolyline();
|
|
334
|
+
}
|
|
335
|
+
/** Set the fade-out start time.
|
|
336
|
+
* @param time The time (in seconds) to set the fade-out start time to
|
|
337
|
+
*/
|
|
338
|
+
setFadeOutStart(time) {
|
|
339
|
+
this.options.fadeOutStart = time;
|
|
340
|
+
this.renderPolyline();
|
|
341
|
+
}
|
|
318
342
|
/** Set the volume of the audio */
|
|
319
343
|
setVolume(volume) {
|
|
320
344
|
this.volume = volume;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):((t="undefined"!=typeof globalThis?globalThis:t||self).WaveSurfer=t.WaveSurfer||{},t.WaveSurfer.Envelope=e())}(this,(function(){"use strict";class t{constructor(){this.listeners={}}on(t,e){return this.listeners[t]||(this.listeners[t]=new Set),this.listeners[t].add(e),()=>this.un(t,e)}once(t,e){const i=this.on(t,e),n=this.on(t,(()=>{i(),n()}));return i}un(t,e){this.listeners[t]&&(e?this.listeners[t].delete(e):delete this.listeners[t])}unAll(){this.listeners={}}emit(t,...e){this.listeners[t]&&this.listeners[t].forEach((t=>t(...e)))}}class e extends t{constructor(t){super(),this.subscriptions=[],this.options=t}onInit(){}init(t){this.wavesurfer=t,this.onInit()}destroy(){this.emit("destroy"),this.subscriptions.forEach((t=>t()))}}const i={fadeInStart:0,fadeOutEnd:0,fadeInEnd:0,fadeOutStart:0,lineWidth:4,lineColor:"rgba(0, 0, 255, 0.5)",dragPointSize:10,dragPointFill:"rgba(255, 255, 255, 0.8)",dragPointStroke:"rgba(255, 255, 255, 0.8)"};class n extends t{constructor(t,e){super(),this.top=0,this.padding=t.dragPointSize/2+1;const i=document.createElementNS("http://www.w3.org/2000/svg","svg");i.setAttribute("width","100%"),i.setAttribute("height","100%"),i.setAttribute("viewBox","0 0 0 0"),i.setAttribute("preserveAspectRatio","none"),i.setAttribute("style","position: absolute; left: 0; top: 0; z-index: 4; pointer-events: none;"),i.setAttribute("part","envelope"),this.svg=i;const n=document.createElementNS("http://www.w3.org/2000/svg","polyline");n.setAttribute("points","0,0 0,0 0,0 0,0"),n.setAttribute("stroke",t.lineColor),n.setAttribute("stroke-width",t.lineWidth),n.setAttribute("fill","none"),n.setAttribute("style","pointer-events: none;"),n.setAttribute("part","polyline"),i.appendChild(n);const s=document.createElementNS("http://www.w3.org/2000/svg","line");s.setAttribute("stroke","transparent"),s.setAttribute("stroke-width",(3*t.lineWidth).toString()),s.setAttribute("style","cursor: ns-resize; pointer-events: all;"),s.setAttribute("part","line"),i.appendChild(s),[0,1].forEach((()=>{const e=document.createElementNS("http://www.w3.org/2000/svg","circle");e.setAttribute("r",(t.dragPointSize/2).toString()),e.setAttribute("fill",t.dragPointFill),e.setAttribute("stroke",t.dragPointStroke||t.dragPointFill),e.setAttribute("stroke-width","2"),e.setAttribute("style","cursor: ew-resize; pointer-events: all;"),e.setAttribute("part","circle"),i.appendChild(e)})),e.appendChild(i);{const t=t=>{const e=this.top+t,{height:n}=i.viewBox.baseVal;if(e<-.5||e>n)return;const s=Math.min(1,Math.max(0,(n-e)/n));this.emit("line-move",s)},e=(t,e)=>{const s=n.points.getItem(t).x+e,{width:o}=i.viewBox.baseVal;this.emit("point-move",t,s/o)};this.makeDraggable(s,((e,i)=>t(i)));const o=this.svg.querySelectorAll("circle");Array.from(o).forEach(((t,i)=>{this.makeDraggable(t,(t=>e(i+1,t)))}))}}makeDraggable(t,e){!function(t,e,i,n,s=5){let o=()=>{};if(!t)return o;const r=r=>{if(2===r.button)return;r.preventDefault(),r.stopPropagation();let a=r.clientX,u=r.clientY,d=!1;const l=n=>{n.preventDefault(),n.stopPropagation();const o=n.clientX,r=n.clientY;if(d||Math.abs(o-a)>=s||Math.abs(r-u)>=s){const{left:n,top:s}=t.getBoundingClientRect();d||(d=!0,null==i||i(a-n,u-s)),e(o-a,r-u,o-n,r-s),a=o,u=r}},h=t=>{d&&(t.preventDefault(),t.stopPropagation())},p=()=>{d&&(null==n||n()),o()};document.addEventListener("pointermove",l),document.addEventListener("pointerup",p),document.addEventListener("pointerleave",p),document.addEventListener("click",h,!0),o=()=>{document.removeEventListener("pointermove",l),document.removeEventListener("pointerup",p),document.removeEventListener("pointerleave",p),setTimeout((()=>{document.removeEventListener("click",h,!0)}),10)}};t.addEventListener("pointerdown",r)}(t,e)}update({x1:t,x2:e,x3:i,x4:n,y:s}){const o=this.svg.clientWidth,r=this.svg.clientHeight;this.top=r-s*r;const a=Math.max(this.padding,Math.min(this.top,r-this.padding));this.svg.setAttribute("viewBox",`0 0 ${o} ${r}`);const u=this.svg.querySelector("polyline"),{points:d}=u;d.getItem(0).x=t*o,d.getItem(0).y=r,d.getItem(1).x=e*o,d.getItem(1).y=a,d.getItem(2).x=i*o,d.getItem(2).y=a,d.getItem(3).x=n*o,d.getItem(3).y=r;const l=this.svg.querySelector("line");l.setAttribute("x1",d.getItem(1).x.toString()),l.setAttribute("x2",d.getItem(2).x.toString()),l.setAttribute("y1",a.toString()),l.setAttribute("y2",a.toString());const h=this.svg.querySelectorAll("circle");Array.from(h).forEach(((t,e)=>{const i=d.getItem(e+1);t.setAttribute("cx",i.x.toString()),t.setAttribute("cy",i.y.toString())}))}destroy(){this.svg.remove()}}class s extends e{constructor(t){var e;super(t),this.polyline=null,this.audioContext=null,this.gainNode=null,this.volume=1,this.isFadingIn=!1,this.isFadingOut=!1,this.naturalVolumeExponent=1.5,this.options=Object.assign({},i,t),this.options.lineColor=this.options.lineColor||i.lineColor,this.options.dragPointFill=this.options.dragPointFill||i.dragPointFill,this.options.dragPointStroke=this.options.dragPointStroke||i.dragPointStroke,this.volume=null!==(e=this.options.volume)&&void 0!==e?e:1}static create(t){return new s(t)}destroy(){var t;null===(t=this.polyline)||void 0===t||t.destroy(),super.destroy()}onInit(){if(!this.wavesurfer)throw Error("WaveSurfer is not initialized");this.initWebAudio(),this.initSvg(),this.initFadeEffects();const t=this.wavesurfer.getMediaElement(),e=()=>this.setVolume(t.volume);t.addEventListener("volumechange",e),this.subscriptions.push((()=>t.removeEventListener("volumechange",e)),this.wavesurfer.on("redraw",(()=>{var t;const e=null===(t=this.wavesurfer)||void 0===t?void 0:t.getDuration();e&&(this.options.fadeInStart=this.options.fadeInStart||0,this.options.fadeOutEnd=this.options.fadeOutEnd||e,this.options.fadeInEnd=this.options.fadeInEnd||this.options.fadeInStart,this.options.fadeOutStart=this.options.fadeOutStart||this.options.fadeOutEnd,this.renderPolyline())})))}initSvg(){if(!this.wavesurfer)return;const t=this.wavesurfer.getWrapper();this.polyline=new n(this.options,t),this.subscriptions.push(this.polyline.on("line-move",(t=>{this.setVolume(this.naturalVolume(t))})),this.polyline.on("point-move",((t,e)=>{var i;const n=e*((null===(i=this.wavesurfer)||void 0===i?void 0:i.getDuration())||0);if(1===t){if(n<this.options.fadeInStart||n>this.options.fadeOutStart)return;this.options.fadeInEnd=n,this.emit("fade-in-change",n)}else if(2===t){if(n>this.options.fadeOutEnd||n<this.options.fadeInEnd)return;this.options.fadeOutStart=n,this.emit("fade-out-change",n)}this.renderPolyline()})))}renderPolyline(){if(!this.polyline||!this.wavesurfer)return;const t=this.wavesurfer.getDuration();t&&this.polyline.update({x1:this.options.fadeInStart/t,x2:this.options.fadeInEnd/t,x3:this.options.fadeOutStart/t,x4:this.options.fadeOutEnd/t,y:this.invertNaturalVolume(this.volume)})}initWebAudio(){var t,e;const i=null===(t=this.wavesurfer)||void 0===t?void 0:t.getMediaElement();if(!i)return null;this.volume=null!==(e=this.options.volume)&&void 0!==e?e:i.volume;const n=new window.AudioContext;this.gainNode=n.createGain(),this.setGainValue();n.createMediaElementSource(i).connect(this.gainNode),this.gainNode.connect(n.destination),this.audioContext=n}invertNaturalVolume(t){const e=1e-4;return Math.pow((t-e)/.9999,1/this.naturalVolumeExponent)}naturalVolume(t){const e=1e-4;return e+.9999*Math.pow(t,this.naturalVolumeExponent)}setGainValue(){this.gainNode&&(this.gainNode.gain.value=this.volume)}initFadeEffects(){if(!this.audioContext||!this.wavesurfer)return;const t=this.wavesurfer.on("timeupdate",(t=>{var e;if(!this.audioContext||!this.gainNode)return;if(!(null===(e=this.wavesurfer)||void 0===e?void 0:e.isPlaying()))return;if("suspended"===this.audioContext.state&&this.audioContext.resume(),!this.isFadingIn&&t>=this.options.fadeInStart&&t<=this.options.fadeInEnd)return this.isFadingIn=!0,this.gainNode.gain.setValueAtTime(0,this.audioContext.currentTime),void this.gainNode.gain.linearRampToValueAtTime(this.volume,this.audioContext.currentTime+(this.options.fadeInEnd-t));if(!this.isFadingOut&&t>=this.options.fadeOutStart&&t<=this.options.fadeOutEnd)return this.isFadingOut=!0,this.gainNode.gain.setValueAtTime(this.volume,this.audioContext.currentTime),void this.gainNode.gain.linearRampToValueAtTime(0,this.audioContext.currentTime+(this.options.fadeOutEnd-t));let i=!1;this.isFadingIn&&(t<this.options.fadeInStart||t>this.options.fadeInEnd)&&(this.isFadingIn=!1,i=!0),this.isFadingOut&&(t<this.options.fadeOutStart||t>=this.options.fadeOutEnd)&&(this.isFadingOut=!1,i=!0),i&&(this.gainNode.gain.cancelScheduledValues(this.audioContext.currentTime),this.setGainValue())}));this.subscriptions.push(t)}getCurrentVolume(){return this.gainNode?this.gainNode.gain.value:this.volume}setStartTime(t,e=!1){if(e){const e=this.options.fadeInEnd-this.options.fadeInStart;this.options.fadeInEnd=t+e}this.options.fadeInStart=t,this.renderPolyline()}setEndTime(t,e=!1){if(e){const e=this.options.fadeOutEnd-this.options.fadeOutStart;this.options.fadeOutStart=t-e}this.options.fadeOutEnd=t,this.renderPolyline()}setFadeInEnd(t){this.options.fadeInEnd=t,this.renderPolyline()}setFadeOutStart(t){this.options.fadeOutStart=t,this.renderPolyline()}setVolume(t){this.volume=t,this.setGainValue(),this.renderPolyline(),this.emit("volume-change",t)}}return s}));
|