wavesurfer.js 7.0.0-alpha.47 → 7.0.0-alpha.48

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 CHANGED
@@ -22,7 +22,7 @@ Or, as a script tag which exposes `WaveSurfer` as a global variable:
22
22
 
23
23
  To import a plugin, e.g. the Timeline plugin:
24
24
  ```
25
- import Timeline from 'https://unpkg.com/wavesurfer.js@alpha/plugins/timeline.js'
25
+ import Timeline from 'https://unpkg.com/wavesurfer.js@alpha/dist/plugins/timeline.js'
26
26
  ```
27
27
 
28
28
  TypeScript types are included in the package, so there's no need to install `@types/wavesurfer.js`.
@@ -38,12 +38,33 @@ wavesurfer.js v7 (aka wavesurfer.ts) brings several improvements:
38
38
  ## Plugins
39
39
  The "official" plugins have been completely rewritten and enhanced:
40
40
 
41
- * [Regions](https://wavesurfer-ts.pages.dev/tutorial/#/examples/regions.js) – now also replaces the old Markers plugin
42
- * [Timeline](https://wavesurfer-ts.pages.dev/tutorial/#/examples/timeline.js) – displays notches and time labels below the waveform
43
- * [Minimap](https://wavesurfer-ts.pages.dev/tutorial/#/examples/minimap.js) – a small waveform that serves as a scrollbar for the main waveform
44
- * [Envelope](https://wavesurfer-ts.pages.dev/tutorial/#/examples/envelope.js) – a graphical interface to add fade-in and -out effects and control volume
45
- * [Record](https://wavesurfer-ts.pages.dev/tutorial/#/examples/record.js) – records audio from the microphone and renders a waveform
46
- * [Spectrogram](https://wavesurfer-ts.pages.dev/tutorial/#/examples/spectrogram.js) – visualization of an audio frequency spectrum
41
+ * [Regions](https://wavesurfer.pages.dev/examples/#regions.js) – now also replaces the old Markers plugin
42
+ * [Timeline](https://wavesurfer.pages.dev/examples/#timeline.js) – displays notches and time labels below the waveform
43
+ * [Minimap](https://wavesurfer.pages.dev/examples/#minimap.js) – a small waveform that serves as a scrollbar for the main waveform
44
+ * [Envelope](https://wavesurfer.pages.dev/examples/#envelope.js) – a graphical interface to add fade-in and -out effects and control volume
45
+ * [Record](https://wavesurfer.pages.dev/examples/#record.js) – records audio from the microphone and renders a waveform
46
+ * [Spectrogram](https://wavesurfer.pages.dev/examples/#spectrogram.js) – visualization of an audio frequency spectrum
47
+
48
+ ## CSS styling
49
+
50
+ wavesurfer.js v7 is rendered into a Shadow DOM tree. This isolates its CSS from the rest of the web page.
51
+ However, it's still possible to style various wavesurfer.js elements via CSS using the `::part()` pseudo-selector.
52
+ For example:
53
+
54
+ ```css
55
+ #waveform ::part(cursor):before {
56
+ content: '🏄';
57
+ }
58
+ #waveform ::part(region) {
59
+ font-family: fantasy;
60
+ }
61
+ ```
62
+
63
+ You can see which elements you can style in the DOM inspector – they will have a `part` attribute.
64
+
65
+ <img width="466" alt="DOM inspector screenshot" src="https://github.com/katspaugh/wavesurfer.ts/assets/381895/fcfb4e4d-9572-4931-811f-9615b7e3aa85">
66
+
67
+ See [this example](https://wavesurfer.pages.dev/examples/#styling.js) for play around with styling.
47
68
 
48
69
  ## Documentation
49
70
  See the documentation on wavesurfer.js [methods](https://wavesurfer-ts.pages.dev/docs/classes/wavesurfer.WaveSurfer), [options](https://wavesurfer-ts.pages.dev/docs/types/wavesurfer.WaveSurferOptions) and [events](https://wavesurfer-ts.pages.dev/docs/types/wavesurfer.WaveSurferEvents) on our website.
@@ -53,7 +74,7 @@ See the documentation on wavesurfer.js [methods](https://wavesurfer-ts.pages.dev
53
74
  Most options, events, and methods are similar to those in previous versions.
54
75
 
55
76
  ### Notable differences
56
- * The `backend` option is removed – HTML5 audio (or video) is the only playback mechanism. However, you can still connect wavesurfer to Web Audio via `MediaElementSourceNode`. See this [example](https://wavesurfer-ts.pages.dev/tutorial/#/examples/webaudio.js).
77
+ * The `backend` option is removed – HTML5 audio (or video) is the only playback mechanism. However, you can still connect wavesurfer to Web Audio via `MediaElementSourceNode`. See this [example](https://wavesurfer.pages.dev/examples/#webaudio.js).
57
78
  * The Markers plugin is removed – use the Regions plugin with `startTime` equal to `endTime`.
58
79
  * No Microphone plugn – superseded by the new Record plugin with more features.
59
80
  * No Cursor and Playhead plugins yet – to be done.
@@ -68,7 +89,7 @@ Most options, events, and methods are similar to those in previous versions.
68
89
  * `toggleMute` is now called `setMuted(true | false)`
69
90
  * `setHeight`, `setWaveColor`, `setCursorColor`, etc. – use `setOptions` with the corresponding params instead. E.g., `wavesurfer.setOptions({ height: 300, waveColor: '#abc' })`
70
91
 
71
- See the complete [documentation of the new API](https://wavesurfer-ts.pages.dev/docs/classes/wavesurfer.WaveSurfer).
92
+ See the complete [documentation of the new API](https://wavesurfer-ts.pages.dev/docs/modules/wavesurfer).
72
93
 
73
94
  ## Development
74
95
 
package/dist/decoder.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /** Decode an array buffer into an audio buffer */
2
- declare function decode(audioData: ArrayBuffer, sampleRate?: number): Promise<AudioBuffer>;
2
+ declare function decode(audioData: ArrayBuffer, sampleRate: number): Promise<AudioBuffer>;
3
3
  /** Create an audio buffer from pre-decoded audio data */
4
4
  declare function createBuffer(channelData: Float32Array[] | Array<number[]>, duration: number): AudioBuffer;
5
5
  declare const Decoder: {
package/dist/decoder.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /** Decode an array buffer into an audio buffer */
2
- async function decode(audioData, sampleRate = 8000) {
2
+ async function decode(audioData, sampleRate) {
3
3
  const audioCtx = new AudioContext({ sampleRate });
4
4
  const decode = audioCtx.decodeAudioData(audioData);
5
5
  decode.finally(() => audioCtx.close());
package/dist/fetcher.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- declare class Fetcher {
2
- load(url: string): Promise<ArrayBuffer>;
3
- }
1
+ declare function load(url: string): Promise<ArrayBuffer>;
2
+ declare const Fetcher: {
3
+ load: typeof load;
4
+ };
4
5
  export default Fetcher;
package/dist/fetcher.js CHANGED
@@ -1,6 +1,7 @@
1
- class Fetcher {
2
- async load(url) {
3
- return fetch(url).then((response) => response.arrayBuffer());
4
- }
1
+ async function load(url) {
2
+ return fetch(url).then((response) => response.arrayBuffer());
5
3
  }
4
+ const Fetcher = {
5
+ load,
6
+ };
6
7
  export default Fetcher;
@@ -25,14 +25,15 @@ declare const defaultOptions: {
25
25
  dragPointFill: string;
26
26
  dragPointStroke: string;
27
27
  };
28
+ type Options = EnvelopePluginOptions & typeof defaultOptions;
28
29
  export type EnvelopePluginEvents = {
29
30
  'fade-in-change': [time: number];
30
31
  'fade-out-change': [time: number];
31
32
  'volume-change': [volume: number];
32
33
  };
33
34
  declare class EnvelopePlugin extends BasePlugin<EnvelopePluginEvents, EnvelopePluginOptions> {
34
- protected options: EnvelopePluginOptions & typeof defaultOptions;
35
- private svg;
35
+ protected options: Options;
36
+ private polyline;
36
37
  private audioContext;
37
38
  private gainNode;
38
39
  private volume;
@@ -41,24 +42,29 @@ declare class EnvelopePlugin extends BasePlugin<EnvelopePluginEvents, EnvelopePl
41
42
  private readonly naturalVolumeExponent;
42
43
  constructor(options: EnvelopePluginOptions);
43
44
  static create(options: EnvelopePluginOptions): EnvelopePlugin;
45
+ destroy(): void;
44
46
  /** Called by wavesurfer, don't call manually */
45
47
  onInit(): void;
46
- private makeDraggable;
47
- private renderPolyline;
48
48
  private initSvg;
49
- destroy(): void;
49
+ private renderPolyline;
50
50
  private initWebAudio;
51
51
  private invertNaturalVolume;
52
52
  private naturalVolume;
53
53
  private setGainValue;
54
- private onVolumeDrag;
55
54
  private initFadeEffects;
56
55
  /** Get the current audio volume */
57
56
  getCurrentVolume(): number;
58
- /** Set the fade-in start time */
59
- setStartTime(time: number, moveDragPoint?: boolean): void;
60
- /** Set the fade-out end time */
61
- setEndTime(time: number, moveDragPoint?: boolean): void;
57
+ /**
58
+ * Set the fade-in start time.
59
+ * @param time The time (in seconds) to set the fade-in start time to
60
+ * @param moveFadeInEnd Whether to move the drag point to the new time (default: false)
61
+ */
62
+ setStartTime(time: number, moveFadeInEnd?: boolean): void;
63
+ /** Set the fade-in end time.
64
+ * @param time The time (in seconds) to set the fade-in end time to
65
+ * @param moveFadeOutStart Whether to move the drag point to the new time (default: false)
66
+ */
67
+ setEndTime(time: number, moveFadeOutStart?: boolean): void;
62
68
  /** Set the volume of the audio */
63
69
  setVolume(volume: number): void;
64
70
  }
@@ -2,6 +2,7 @@
2
2
  * Envelope is a visual UI for controlling the audio volume and add fade-in and fade-out effects.
3
3
  */
4
4
  import BasePlugin from '../base-plugin.js';
5
+ import EventEmitter from '../event-emitter.js';
5
6
  const defaultOptions = {
6
7
  fadeInStart: 0,
7
8
  fadeOutEnd: 0,
@@ -13,58 +14,85 @@ const defaultOptions = {
13
14
  dragPointFill: 'rgba(255, 255, 255, 0.8)',
14
15
  dragPointStroke: 'rgba(255, 255, 255, 0.8)',
15
16
  };
16
- class EnvelopePlugin extends BasePlugin {
17
- constructor(options) {
18
- super(options);
19
- this.svg = null;
20
- this.audioContext = null;
21
- this.gainNode = null;
22
- this.volume = 1;
23
- this.isFadingIn = false;
24
- this.isFadingOut = false;
25
- // Adjust the exponent to change the curve of the volume control
26
- this.naturalVolumeExponent = 1.5;
27
- this.options = Object.assign({}, defaultOptions, options);
28
- this.options.lineColor = this.options.lineColor || defaultOptions.lineColor;
29
- this.options.dragPointFill = this.options.dragPointFill || defaultOptions.dragPointFill;
30
- this.options.dragPointStroke = this.options.dragPointStroke || defaultOptions.dragPointStroke;
31
- this.volume = this.options.volume ?? 1;
32
- }
33
- static create(options) {
34
- return new EnvelopePlugin(options);
35
- }
36
- /** Called by wavesurfer, don't call manually */
37
- onInit() {
38
- if (!this.wavesurfer) {
39
- throw Error('WaveSurfer is not initialized');
17
+ class Polyline extends EventEmitter {
18
+ constructor(options, wrapper) {
19
+ super();
20
+ this.top = 0;
21
+ // An padding to make the envelope fit into the SVG
22
+ this.padding = options.dragPointSize / 2 + 1;
23
+ // SVG element
24
+ const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
25
+ svg.setAttribute('width', '100%');
26
+ svg.setAttribute('height', '100%');
27
+ svg.setAttribute('viewBox', `0 0 ${wrapper.clientWidth} ${wrapper.clientHeight}`);
28
+ svg.setAttribute('preserveAspectRatio', 'none');
29
+ svg.setAttribute('style', 'position: absolute; left: 0; top: 0; z-index: 4; pointer-events: none;');
30
+ svg.setAttribute('part', 'envelope');
31
+ this.svg = svg;
32
+ // A polyline representing the envelope
33
+ const polyline = document.createElementNS('http://www.w3.org/2000/svg', 'polyline');
34
+ polyline.setAttribute('points', '0,0 0,0 0,0 0,0');
35
+ polyline.setAttribute('stroke', options.lineColor);
36
+ polyline.setAttribute('stroke-width', options.lineWidth);
37
+ polyline.setAttribute('fill', 'none');
38
+ polyline.setAttribute('style', 'pointer-events: none;');
39
+ polyline.setAttribute('part', 'polyline');
40
+ svg.appendChild(polyline);
41
+ // Draggable top line
42
+ const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
43
+ line.setAttribute('stroke', 'transparent');
44
+ line.setAttribute('stroke-width', (options.lineWidth * 3).toString());
45
+ line.setAttribute('style', 'cursor: ns-resize; pointer-events: all;');
46
+ line.setAttribute('part', 'line');
47
+ svg.appendChild(line);
48
+ [0, 1].forEach(() => {
49
+ const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
50
+ circle.setAttribute('r', (options.dragPointSize / 2).toString());
51
+ circle.setAttribute('fill', options.dragPointFill);
52
+ circle.setAttribute('stroke', options.dragPointStroke || options.dragPointFill);
53
+ circle.setAttribute('stroke-width', '2');
54
+ circle.setAttribute('style', 'cursor: ew-resize; pointer-events: all;');
55
+ circle.setAttribute('part', 'circle');
56
+ svg.appendChild(circle);
57
+ });
58
+ wrapper.appendChild(svg);
59
+ // Init dtagging
60
+ {
61
+ // On top line drag
62
+ const onDragY = (dy) => {
63
+ const newTop = this.top + dy;
64
+ const { height } = svg.viewBox.baseVal;
65
+ if (newTop < -0.5 || newTop > height)
66
+ return;
67
+ const relativeY = Math.min(1, Math.max(0, (height - newTop) / height));
68
+ this.emit('line-move', relativeY);
69
+ };
70
+ // On points drag
71
+ const onDragX = (index, dx) => {
72
+ const point = polyline.points.getItem(index);
73
+ const newX = point.x + dx;
74
+ const { width } = svg.viewBox.baseVal;
75
+ this.emit('point-move', index, newX / width);
76
+ };
77
+ // Draggable top line of the polyline
78
+ this.makeDraggable(line, (_, dy) => onDragY(dy));
79
+ // Make each point draggable
80
+ const draggables = this.svg.querySelectorAll('circle');
81
+ Array.from(draggables).forEach((draggable, index) => {
82
+ this.makeDraggable(draggable, (dx) => onDragX(index + 1, dx));
83
+ });
40
84
  }
41
- this.subscriptions.push(this.wavesurfer.once('decode', (duration) => {
42
- this.options.fadeInStart = this.options.fadeInStart || 0;
43
- this.options.fadeOutEnd = this.options.fadeOutEnd || duration;
44
- this.options.fadeInEnd = this.options.fadeInEnd || this.options.fadeInStart;
45
- this.options.fadeOutStart = this.options.fadeOutStart || this.options.fadeOutEnd;
46
- this.initWebAudio();
47
- this.initSvg();
48
- this.initFadeEffects();
49
- }));
50
- let delay;
51
- this.subscriptions.push(this.wavesurfer.on('zoom', () => {
52
- if (delay)
53
- clearTimeout(delay);
54
- delay = setTimeout(() => {
55
- this.svg?.remove();
56
- this.initSvg();
57
- }, 100);
58
- }));
59
85
  }
60
86
  makeDraggable(draggable, onDrag) {
87
+ draggable.addEventListener('click', (e) => {
88
+ e.preventDefault();
89
+ e.stopPropagation();
90
+ });
61
91
  draggable.addEventListener('mousedown', (e) => {
92
+ e.preventDefault();
93
+ e.stopPropagation();
62
94
  let x = e.clientX;
63
95
  let y = e.clientY;
64
- const wasInteractive = this.wavesurfer?.options.interact || true;
65
- let delay;
66
- // Make the wavesurfer ignore clicks when we're dragging
67
- this.wavesurfer?.toggleInteraction(false);
68
96
  const move = (e) => {
69
97
  const dx = e.clientX - x;
70
98
  const dy = e.clientY - y;
@@ -75,145 +103,124 @@ class EnvelopePlugin extends BasePlugin {
75
103
  const up = () => {
76
104
  document.removeEventListener('mousemove', move);
77
105
  document.removeEventListener('mouseup', up);
78
- // Restore interactive state
79
- if (delay)
80
- clearTimeout(delay);
81
- delay = setTimeout(() => {
82
- this.wavesurfer?.toggleInteraction(wasInteractive);
83
- }, 100);
84
106
  };
85
107
  document.addEventListener('mousemove', move);
86
108
  document.addEventListener('mouseup', up);
87
- e.preventDefault();
88
- e.stopPropagation();
89
109
  });
90
110
  }
91
- renderPolyline() {
92
- if (!this.svg || !this.wavesurfer)
93
- return;
94
- const polyline = this.svg.querySelector('polyline');
95
- const points = polyline.points;
111
+ update({ x1, x2, x3, x4, y }) {
96
112
  const width = this.svg.clientWidth;
97
113
  const height = this.svg.clientHeight;
98
- const duration = this.wavesurfer.getDuration();
99
- const offset = this.options.dragPointSize / 2;
100
- const top = height - this.invertNaturalVolume(this.volume) * height + offset;
114
+ this.top = height - y * height;
115
+ const top = Math.max(this.padding, Math.min(this.top, height - this.padding));
116
+ this.svg.setAttribute('viewBox', `0 0 ${width} ${height}`);
117
+ const polyline = this.svg.querySelector('polyline');
118
+ const { points } = polyline;
119
+ points.getItem(0).x = x1 * width;
120
+ points.getItem(0).y = height;
121
+ points.getItem(1).x = x2 * width;
101
122
  points.getItem(1).y = top;
123
+ points.getItem(2).x = x3 * width;
102
124
  points.getItem(2).y = top;
103
- points.getItem(0).x = (this.options.fadeInStart / duration) * width;
104
- points.getItem(3).x = (this.options.fadeOutEnd / duration) * width;
125
+ points.getItem(3).x = x4 * width;
126
+ points.getItem(3).y = height;
105
127
  const line = this.svg.querySelector('line');
106
128
  line.setAttribute('x1', points.getItem(1).x.toString());
107
129
  line.setAttribute('x2', points.getItem(2).x.toString());
108
130
  line.setAttribute('y1', top.toString());
109
131
  line.setAttribute('y2', top.toString());
110
132
  const circles = this.svg.querySelectorAll('circle');
111
- for (let i = 0; i < circles.length; i++) {
112
- const circle = circles[i];
113
- const point = polyline.points.getItem(i + 1);
133
+ Array.from(circles).forEach((circle, i) => {
134
+ const point = points.getItem(i + 1);
114
135
  circle.setAttribute('cx', point.x.toString());
115
- circle.setAttribute('cy', top.toString());
136
+ circle.setAttribute('cy', point.y.toString());
137
+ });
138
+ }
139
+ destroy() {
140
+ this.svg.remove();
141
+ }
142
+ }
143
+ class EnvelopePlugin extends BasePlugin {
144
+ constructor(options) {
145
+ super(options);
146
+ this.polyline = null;
147
+ this.audioContext = null;
148
+ this.gainNode = null;
149
+ this.volume = 1;
150
+ this.isFadingIn = false;
151
+ this.isFadingOut = false;
152
+ // Adjust the exponent to change the curve of the volume control
153
+ this.naturalVolumeExponent = 1.5;
154
+ this.options = Object.assign({}, defaultOptions, options);
155
+ this.options.lineColor = this.options.lineColor || defaultOptions.lineColor;
156
+ this.options.dragPointFill = this.options.dragPointFill || defaultOptions.dragPointFill;
157
+ this.options.dragPointStroke = this.options.dragPointStroke || defaultOptions.dragPointStroke;
158
+ this.volume = this.options.volume ?? 1;
159
+ }
160
+ static create(options) {
161
+ return new EnvelopePlugin(options);
162
+ }
163
+ destroy() {
164
+ this.polyline?.destroy();
165
+ super.destroy();
166
+ }
167
+ /** Called by wavesurfer, don't call manually */
168
+ onInit() {
169
+ if (!this.wavesurfer) {
170
+ throw Error('WaveSurfer is not initialized');
116
171
  }
172
+ this.initWebAudio();
173
+ this.initSvg();
174
+ this.initFadeEffects();
175
+ this.subscriptions.push(this.wavesurfer.on('decode', (duration) => {
176
+ this.options.fadeInStart = this.options.fadeInStart || 0;
177
+ this.options.fadeOutEnd = this.options.fadeOutEnd || duration;
178
+ this.options.fadeInEnd = this.options.fadeInEnd || this.options.fadeInStart;
179
+ this.options.fadeOutStart = this.options.fadeOutStart || this.options.fadeOutEnd;
180
+ this.renderPolyline();
181
+ }));
182
+ this.subscriptions.push(this.wavesurfer.on('redraw', () => this.renderPolyline()));
117
183
  }
118
184
  initSvg() {
119
185
  if (!this.wavesurfer)
120
186
  return;
121
187
  const wrapper = this.wavesurfer.getWrapper();
122
- const width = wrapper.clientWidth;
123
- const height = wrapper.clientHeight;
124
- const duration = this.wavesurfer.getDuration();
125
- // SVG element
126
- const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
127
- svg.setAttribute('width', '100%');
128
- svg.setAttribute('height', '100%');
129
- svg.setAttribute('viewBox', `0 0 ${width} ${height}`);
130
- svg.setAttribute('preserveAspectRatio', 'none');
131
- svg.setAttribute('style', 'position: absolute; left: 0; top: 0; z-index: 4; pointer-events: none;');
132
- this.svg = svg;
133
- // A polyline representing the envelope
134
- const polyline = document.createElementNS('http://www.w3.org/2000/svg', 'polyline');
135
- polyline.setAttribute('points', '0,0 0,0 0,0 0,0');
136
- polyline.setAttribute('stroke', this.options.lineColor);
137
- polyline.setAttribute('stroke-width', this.options.lineWidth);
138
- polyline.setAttribute('fill', 'none');
139
- polyline.setAttribute('style', 'pointer-events: none');
140
- svg.appendChild(polyline);
141
- // Draggable top line
142
- const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
143
- line.setAttribute('stroke', 'none');
144
- line.setAttribute('stroke-width', (this.options.lineWidth * 3).toString());
145
- line.setAttribute('style', 'cursor: ns-resize; pointer-events: all;');
146
- svg.appendChild(line);
147
- const points = polyline.points;
148
- const offset = this.options.dragPointSize / 2;
149
- points.getItem(0).x = (this.options.fadeInStart / duration) * width;
150
- points.getItem(0).y = height;
151
- points.getItem(1).x = (this.options.fadeInEnd / duration) * width;
152
- points.getItem(1).y = 0;
153
- points.getItem(2).x = (this.options.fadeOutStart / duration) * width;
154
- points.getItem(2).y = 0;
155
- points.getItem(3).x = (this.options.fadeOutEnd / duration) * width;
156
- points.getItem(3).y = height;
157
- // Drag points
158
- const dragPoints = [1, 2];
159
- dragPoints.forEach(() => {
160
- const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
161
- circle.setAttribute('r', (this.options.dragPointSize / 2).toString());
162
- circle.setAttribute('fill', this.options.dragPointFill);
163
- circle.setAttribute('stroke', this.options.dragPointStroke || this.options.dragPointFill);
164
- circle.setAttribute('stroke-width', '2');
165
- circle.setAttribute('style', 'cursor: ew-resize; pointer-events: all;');
166
- svg.appendChild(circle);
167
- });
168
- this.wavesurfer.getWrapper().appendChild(svg);
169
- // Initial polyline
170
- this.renderPolyline();
171
- // On top line drag
172
- const onDragY = (dy) => {
173
- const newTop = points.getItem(1).y + dy - offset;
174
- if (newTop < -0.5 || newTop > height)
175
- return;
176
- const newVolume = Math.min(1, Math.max(0, (height - newTop) / height));
177
- this.onVolumeDrag(newVolume);
178
- };
179
- // On points drag
180
- const onDragX = (dx, dy, index) => {
181
- const point = polyline.points.getItem(index);
182
- const newX = point.x + dx;
183
- const newTime = (newX / width) * duration;
184
- if ((index === 1 && newTime > this.options.fadeOutStart) || newTime < this.options.fadeInStart)
185
- return;
186
- if ((index === 2 && newTime < this.options.fadeInEnd) || newTime > this.options.fadeOutEnd)
187
- return;
188
- point.x = newX;
188
+ this.polyline = new Polyline(this.options, wrapper);
189
+ this.subscriptions.push(this.polyline.on('line-move', (relativeY) => {
190
+ this.setVolume(this.naturalVolume(relativeY));
191
+ }), this.polyline.on('point-move', (index, relativeX) => {
192
+ const duration = this.wavesurfer?.getDuration() || 0;
193
+ const newTime = relativeX * duration;
194
+ // Fade-in end point
189
195
  if (index === 1) {
196
+ if (newTime < this.options.fadeInStart || newTime > this.options.fadeOutStart)
197
+ return;
190
198
  this.options.fadeInEnd = newTime;
191
199
  this.emit('fade-in-change', newTime);
192
200
  }
193
201
  else if (index === 2) {
202
+ // Fade-out start point
203
+ if (newTime > this.options.fadeOutEnd || newTime < this.options.fadeInEnd)
204
+ return;
194
205
  this.options.fadeOutStart = newTime;
195
206
  this.emit('fade-out-change', newTime);
196
207
  }
197
- // Also allow dragging points vertically
198
- if (dy > 1 || dy < -1) {
199
- onDragY(dy);
200
- }
201
- else {
202
- this.renderPolyline();
203
- }
204
- };
205
- // Draggable top line of the polyline
206
- this.makeDraggable(line, (_, dy) => onDragY(dy));
207
- // Make each point draggable
208
- const draggables = svg.querySelectorAll('circle');
209
- for (let i = 0; i < draggables.length; i++) {
210
- const index = i + 1;
211
- this.makeDraggable(draggables[i], (dx, dy) => onDragX(dx, dy, index));
212
- }
208
+ this.renderPolyline();
209
+ }));
213
210
  }
214
- destroy() {
215
- this.svg?.remove();
216
- super.destroy();
211
+ renderPolyline() {
212
+ if (!this.polyline || !this.wavesurfer)
213
+ return;
214
+ const duration = this.wavesurfer.getDuration();
215
+ if (!duration)
216
+ return;
217
+ this.polyline.update({
218
+ x1: this.options.fadeInStart / duration,
219
+ x2: this.options.fadeInEnd / duration,
220
+ x3: this.options.fadeOutStart / duration,
221
+ x4: this.options.fadeOutEnd / duration,
222
+ y: this.invertNaturalVolume(this.volume),
223
+ });
217
224
  }
218
225
  initWebAudio() {
219
226
  const audio = this.wavesurfer?.getMediaElement();
@@ -249,9 +256,6 @@ class EnvelopePlugin extends BasePlugin {
249
256
  this.gainNode.gain.value = this.volume;
250
257
  }
251
258
  }
252
- onVolumeDrag(volume) {
253
- this.setVolume(this.naturalVolume(volume));
254
- }
255
259
  initFadeEffects() {
256
260
  if (!this.audioContext || !this.wavesurfer)
257
261
  return;
@@ -305,22 +309,29 @@ class EnvelopePlugin extends BasePlugin {
305
309
  getCurrentVolume() {
306
310
  return this.gainNode ? this.gainNode.gain.value : this.volume;
307
311
  }
308
- /** Set the fade-in start time */
309
- setStartTime(time, moveDragPoint) {
310
- const rampLength = this.options.fadeInEnd - this.options.fadeInStart;
311
- this.options.fadeInStart = time;
312
- if (moveDragPoint) {
312
+ /**
313
+ * Set the fade-in start time.
314
+ * @param time The time (in seconds) to set the fade-in start time to
315
+ * @param moveFadeInEnd Whether to move the drag point to the new time (default: false)
316
+ */
317
+ setStartTime(time, moveFadeInEnd = false) {
318
+ if (moveFadeInEnd) {
319
+ const rampLength = this.options.fadeInEnd - this.options.fadeInStart;
313
320
  this.options.fadeInEnd = time + rampLength;
314
321
  }
322
+ this.options.fadeInStart = time;
315
323
  this.renderPolyline();
316
324
  }
317
- /** Set the fade-out end time */
318
- setEndTime(time, moveDragPoint) {
319
- const rampLength = this.options.fadeOutEnd - this.options.fadeOutStart;
320
- this.options.fadeOutEnd = time;
321
- if (moveDragPoint) {
325
+ /** Set the fade-in end time.
326
+ * @param time The time (in seconds) to set the fade-in end time to
327
+ * @param moveFadeOutStart Whether to move the drag point to the new time (default: false)
328
+ */
329
+ setEndTime(time, moveFadeOutStart = false) {
330
+ if (moveFadeOutStart) {
331
+ const rampLength = this.options.fadeOutEnd - this.options.fadeOutStart;
322
332
  this.options.fadeOutStart = time - rampLength;
323
333
  }
334
+ this.options.fadeOutEnd = time;
324
335
  this.renderPolyline();
325
336
  }
326
337
  /** Set the volume of the audio */
@@ -1 +1 @@
1
- !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Envelope=e():t.Envelope=e()}(WaveSurfer,(()=>(()=>{"use strict";var t={284:(t,e,i)=>{i.d(e,{Z:()=>o});var s=i(139);class n extends s.Z{constructor(t){super(),this.subscriptions=[],this.options=t}onInit(){}init(t){this.wavesurfer=t,this.onInit()}destroy(){this.subscriptions.forEach((t=>t()))}}const o=n},139:(t,e,i)=>{i.d(e,{Z:()=>s});const s=class{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),s=this.on(t,(()=>{i(),s()}));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)))}}}},e={};function i(s){var n=e[s];if(void 0!==n)return n.exports;var o=e[s]={exports:{}};return t[s](o,o.exports,i),o.exports}i.d=(t,e)=>{for(var s in e)i.o(e,s)&&!i.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:e[s]})},i.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e);var s={};return(()=>{i.d(s,{default:()=>o});var t=i(284);const e={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.Z{constructor(t){super(t),this.svg=null,this.audioContext=null,this.gainNode=null,this.volume=1,this.isFadingIn=!1,this.isFadingOut=!1,this.naturalVolumeExponent=1.5,this.options=Object.assign({},e,t),this.options.lineColor=this.options.lineColor||e.lineColor,this.options.dragPointFill=this.options.dragPointFill||e.dragPointFill,this.options.dragPointStroke=this.options.dragPointStroke||e.dragPointStroke,this.volume=this.options.volume??1}static create(t){return new n(t)}onInit(){if(!this.wavesurfer)throw Error("WaveSurfer is not initialized");let t;this.subscriptions.push(this.wavesurfer.once("decode",(t=>{this.options.fadeInStart=this.options.fadeInStart||0,this.options.fadeOutEnd=this.options.fadeOutEnd||t,this.options.fadeInEnd=this.options.fadeInEnd||this.options.fadeInStart,this.options.fadeOutStart=this.options.fadeOutStart||this.options.fadeOutEnd,this.initWebAudio(),this.initSvg(),this.initFadeEffects()}))),this.subscriptions.push(this.wavesurfer.on("zoom",(()=>{t&&clearTimeout(t),t=setTimeout((()=>{this.svg?.remove(),this.initSvg()}),100)})))}makeDraggable(t,e){t.addEventListener("mousedown",(t=>{let i=t.clientX,s=t.clientY;const n=this.wavesurfer?.options.interact||!0;let o;this.wavesurfer?.toggleInteraction(!1);const r=t=>{const n=t.clientX-i,o=t.clientY-s;i=t.clientX,s=t.clientY,e(n,o)},a=()=>{document.removeEventListener("mousemove",r),document.removeEventListener("mouseup",a),o&&clearTimeout(o),o=setTimeout((()=>{this.wavesurfer?.toggleInteraction(n)}),100)};document.addEventListener("mousemove",r),document.addEventListener("mouseup",a),t.preventDefault(),t.stopPropagation()}))}renderPolyline(){if(!this.svg||!this.wavesurfer)return;const t=this.svg.querySelector("polyline"),e=t.points,i=this.svg.clientWidth,s=this.svg.clientHeight,n=this.wavesurfer.getDuration(),o=this.options.dragPointSize/2,r=s-this.invertNaturalVolume(this.volume)*s+o;e.getItem(1).y=r,e.getItem(2).y=r,e.getItem(0).x=this.options.fadeInStart/n*i,e.getItem(3).x=this.options.fadeOutEnd/n*i;const a=this.svg.querySelector("line");a.setAttribute("x1",e.getItem(1).x.toString()),a.setAttribute("x2",e.getItem(2).x.toString()),a.setAttribute("y1",r.toString()),a.setAttribute("y2",r.toString());const u=this.svg.querySelectorAll("circle");for(let e=0;e<u.length;e++){const i=u[e],s=t.points.getItem(e+1);i.setAttribute("cx",s.x.toString()),i.setAttribute("cy",r.toString())}}initSvg(){if(!this.wavesurfer)return;const t=this.wavesurfer.getWrapper(),e=t.clientWidth,i=t.clientHeight,s=this.wavesurfer.getDuration(),n=document.createElementNS("http://www.w3.org/2000/svg","svg");n.setAttribute("width","100%"),n.setAttribute("height","100%"),n.setAttribute("viewBox",`0 0 ${e} ${i}`),n.setAttribute("preserveAspectRatio","none"),n.setAttribute("style","position: absolute; left: 0; top: 0; z-index: 4; pointer-events: none;"),this.svg=n;const o=document.createElementNS("http://www.w3.org/2000/svg","polyline");o.setAttribute("points","0,0 0,0 0,0 0,0"),o.setAttribute("stroke",this.options.lineColor),o.setAttribute("stroke-width",this.options.lineWidth),o.setAttribute("fill","none"),o.setAttribute("style","pointer-events: none"),n.appendChild(o);const r=document.createElementNS("http://www.w3.org/2000/svg","line");r.setAttribute("stroke","none"),r.setAttribute("stroke-width",(3*this.options.lineWidth).toString()),r.setAttribute("style","cursor: ns-resize; pointer-events: all;"),n.appendChild(r);const a=o.points,u=this.options.dragPointSize/2;a.getItem(0).x=this.options.fadeInStart/s*e,a.getItem(0).y=i,a.getItem(1).x=this.options.fadeInEnd/s*e,a.getItem(1).y=0,a.getItem(2).x=this.options.fadeOutStart/s*e,a.getItem(2).y=0,a.getItem(3).x=this.options.fadeOutEnd/s*e,a.getItem(3).y=i,[1,2].forEach((()=>{const t=document.createElementNS("http://www.w3.org/2000/svg","circle");t.setAttribute("r",(this.options.dragPointSize/2).toString()),t.setAttribute("fill",this.options.dragPointFill),t.setAttribute("stroke",this.options.dragPointStroke||this.options.dragPointFill),t.setAttribute("stroke-width","2"),t.setAttribute("style","cursor: ew-resize; pointer-events: all;"),n.appendChild(t)})),this.wavesurfer.getWrapper().appendChild(n),this.renderPolyline();const h=t=>{const e=a.getItem(1).y+t-u;if(e<-.5||e>i)return;const s=Math.min(1,Math.max(0,(i-e)/i));this.onVolumeDrag(s)},d=(t,i,n)=>{const r=o.points.getItem(n),a=r.x+t,u=a/e*s;1===n&&u>this.options.fadeOutStart||u<this.options.fadeInStart||2===n&&u<this.options.fadeInEnd||u>this.options.fadeOutEnd||(r.x=a,1===n?(this.options.fadeInEnd=u,this.emit("fade-in-change",u)):2===n&&(this.options.fadeOutStart=u,this.emit("fade-out-change",u)),i>1||i<-1?h(i):this.renderPolyline())};this.makeDraggable(r,((t,e)=>h(e)));const l=n.querySelectorAll("circle");for(let t=0;t<l.length;t++){const e=t+1;this.makeDraggable(l[t],((t,i)=>d(t,i,e)))}}destroy(){this.svg?.remove(),super.destroy()}initWebAudio(){const t=this.wavesurfer?.getMediaElement();if(!t)return null;this.volume=this.options.volume??t.volume;const e=new window.AudioContext;this.gainNode=e.createGain(),this.setGainValue(),e.createMediaElementSource(t).connect(this.gainNode),this.gainNode.connect(e.destination),this.audioContext=e}invertNaturalVolume(t){return Math.pow((t-1e-4)/.9999,1/this.naturalVolumeExponent)}naturalVolume(t){return 1e-4+.9999*Math.pow(t,this.naturalVolumeExponent)}setGainValue(){this.gainNode&&(this.gainNode.gain.value=this.volume)}onVolumeDrag(t){this.setVolume(this.naturalVolume(t))}initFadeEffects(){if(!this.audioContext||!this.wavesurfer)return;const t=this.wavesurfer.on("timeupdate",(t=>{if(!this.audioContext||!this.gainNode)return;if(!this.wavesurfer?.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 e=!1;this.isFadingIn&&(t<this.options.fadeInStart||t>this.options.fadeInEnd)&&(this.isFadingIn=!1,e=!0),this.isFadingOut&&(t<this.options.fadeOutStart||t>=this.options.fadeOutEnd)&&(this.isFadingOut=!1,e=!0),e&&(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){const i=this.options.fadeInEnd-this.options.fadeInStart;this.options.fadeInStart=t,e&&(this.options.fadeInEnd=t+i),this.renderPolyline()}setEndTime(t,e){const i=this.options.fadeOutEnd-this.options.fadeOutStart;this.options.fadeOutEnd=t,e&&(this.options.fadeOutStart=t-i),this.renderPolyline()}setVolume(t){this.volume=t,this.setGainValue(),this.renderPolyline(),this.emit("volume-change",t)}}const o=n})(),s.default})()));
1
+ !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Envelope=e():t.Envelope=e()}(WaveSurfer,(()=>(()=>{"use strict";var t={284:(t,e,i)=>{i.d(e,{Z:()=>o});var s=i(139);class n extends s.Z{constructor(t){super(),this.subscriptions=[],this.options=t}onInit(){}init(t){this.wavesurfer=t,this.onInit()}destroy(){this.subscriptions.forEach((t=>t()))}}const o=n},139:(t,e,i)=>{i.d(e,{Z:()=>s});const s=class{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),s=this.on(t,(()=>{i(),s()}));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)))}}}},e={};function i(s){var n=e[s];if(void 0!==n)return n.exports;var o=e[s]={exports:{}};return t[s](o,o.exports,i),o.exports}i.d=(t,e)=>{for(var s in e)i.o(e,s)&&!i.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:e[s]})},i.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e);var s={};return(()=>{i.d(s,{default:()=>a});var t=i(284),e=i(139);const n={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 o extends e.Z{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 ${e.clientWidth} ${e.clientHeight}`),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 s=document.createElementNS("http://www.w3.org/2000/svg","polyline");s.setAttribute("points","0,0 0,0 0,0 0,0"),s.setAttribute("stroke",t.lineColor),s.setAttribute("stroke-width",t.lineWidth),s.setAttribute("fill","none"),s.setAttribute("style","pointer-events: none;"),s.setAttribute("part","polyline"),i.appendChild(s);const n=document.createElementNS("http://www.w3.org/2000/svg","line");n.setAttribute("stroke","transparent"),n.setAttribute("stroke-width",(3*t.lineWidth).toString()),n.setAttribute("style","cursor: ns-resize; pointer-events: all;"),n.setAttribute("part","line"),i.appendChild(n),[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:s}=i.viewBox.baseVal;if(e<-.5||e>s)return;const n=Math.min(1,Math.max(0,(s-e)/s));this.emit("line-move",n)},e=(t,e)=>{const n=s.points.getItem(t).x+e,{width:o}=i.viewBox.baseVal;this.emit("point-move",t,n/o)};this.makeDraggable(n,((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){t.addEventListener("click",(t=>{t.preventDefault(),t.stopPropagation()})),t.addEventListener("mousedown",(t=>{t.preventDefault(),t.stopPropagation();let i=t.clientX,s=t.clientY;const n=t=>{const n=t.clientX-i,o=t.clientY-s;i=t.clientX,s=t.clientY,e(n,o)},o=()=>{document.removeEventListener("mousemove",n),document.removeEventListener("mouseup",o)};document.addEventListener("mousemove",n),document.addEventListener("mouseup",o)}))}update({x1:t,x2:e,x3:i,x4:s,y:n}){const o=this.svg.clientWidth,r=this.svg.clientHeight;this.top=r-n*r;const a=Math.max(this.padding,Math.min(this.top,r-this.padding));this.svg.setAttribute("viewBox",`0 0 ${o} ${r}`);const h=this.svg.querySelector("polyline"),{points:u}=h;u.getItem(0).x=t*o,u.getItem(0).y=r,u.getItem(1).x=e*o,u.getItem(1).y=a,u.getItem(2).x=i*o,u.getItem(2).y=a,u.getItem(3).x=s*o,u.getItem(3).y=r;const d=this.svg.querySelector("line");d.setAttribute("x1",u.getItem(1).x.toString()),d.setAttribute("x2",u.getItem(2).x.toString()),d.setAttribute("y1",a.toString()),d.setAttribute("y2",a.toString());const l=this.svg.querySelectorAll("circle");Array.from(l).forEach(((t,e)=>{const i=u.getItem(e+1);t.setAttribute("cx",i.x.toString()),t.setAttribute("cy",i.y.toString())}))}destroy(){this.svg.remove()}}class r extends t.Z{constructor(t){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({},n,t),this.options.lineColor=this.options.lineColor||n.lineColor,this.options.dragPointFill=this.options.dragPointFill||n.dragPointFill,this.options.dragPointStroke=this.options.dragPointStroke||n.dragPointStroke,this.volume=this.options.volume??1}static create(t){return new r(t)}destroy(){this.polyline?.destroy(),super.destroy()}onInit(){if(!this.wavesurfer)throw Error("WaveSurfer is not initialized");this.initWebAudio(),this.initSvg(),this.initFadeEffects(),this.subscriptions.push(this.wavesurfer.on("decode",(t=>{this.options.fadeInStart=this.options.fadeInStart||0,this.options.fadeOutEnd=this.options.fadeOutEnd||t,this.options.fadeInEnd=this.options.fadeInEnd||this.options.fadeInStart,this.options.fadeOutStart=this.options.fadeOutStart||this.options.fadeOutEnd,this.renderPolyline()}))),this.subscriptions.push(this.wavesurfer.on("redraw",(()=>this.renderPolyline())))}initSvg(){if(!this.wavesurfer)return;const t=this.wavesurfer.getWrapper();this.polyline=new o(this.options,t),this.subscriptions.push(this.polyline.on("line-move",(t=>{this.setVolume(this.naturalVolume(t))})),this.polyline.on("point-move",((t,e)=>{const i=e*(this.wavesurfer?.getDuration()||0);if(1===t){if(i<this.options.fadeInStart||i>this.options.fadeOutStart)return;this.options.fadeInEnd=i,this.emit("fade-in-change",i)}else if(2===t){if(i>this.options.fadeOutEnd||i<this.options.fadeInEnd)return;this.options.fadeOutStart=i,this.emit("fade-out-change",i)}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(){const t=this.wavesurfer?.getMediaElement();if(!t)return null;this.volume=this.options.volume??t.volume;const e=new window.AudioContext;this.gainNode=e.createGain(),this.setGainValue(),e.createMediaElementSource(t).connect(this.gainNode),this.gainNode.connect(e.destination),this.audioContext=e}invertNaturalVolume(t){return Math.pow((t-1e-4)/.9999,1/this.naturalVolumeExponent)}naturalVolume(t){return 1e-4+.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=>{if(!this.audioContext||!this.gainNode)return;if(!this.wavesurfer?.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 e=!1;this.isFadingIn&&(t<this.options.fadeInStart||t>this.options.fadeInEnd)&&(this.isFadingIn=!1,e=!0),this.isFadingOut&&(t<this.options.fadeOutStart||t>=this.options.fadeOutEnd)&&(this.isFadingOut=!1,e=!0),e&&(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()}setVolume(t){this.volume=t,this.setGainValue(),this.renderPolyline(),this.emit("volume-change",t)}}const a=r})(),s.default})()));