wavesurfer.js 6.5.1 → 7.0.0-alpha.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.
Files changed (132) hide show
  1. package/.eslintrc.json +24 -0
  2. package/.prettierrc +8 -0
  3. package/README.md +26 -117
  4. package/dist/base-plugin.d.ts +10 -0
  5. package/dist/base-plugin.js +13 -0
  6. package/dist/decoder.d.ts +10 -0
  7. package/dist/decoder.js +43 -0
  8. package/dist/event-emitter.d.ts +11 -0
  9. package/dist/event-emitter.js +21 -0
  10. package/dist/fetcher.d.ts +4 -0
  11. package/dist/fetcher.js +17 -0
  12. package/dist/index.d.ts +92 -0
  13. package/dist/index.js +166 -0
  14. package/dist/player-webaudio.d.ts +8 -0
  15. package/dist/player-webaudio.js +31 -0
  16. package/dist/player.d.ts +16 -0
  17. package/dist/player.js +40 -0
  18. package/dist/plugins/regions.d.ts +48 -0
  19. package/dist/plugins/regions.js +183 -0
  20. package/dist/renderer.d.ts +34 -0
  21. package/dist/renderer.js +193 -0
  22. package/dist/timer.d.ts +10 -0
  23. package/dist/timer.js +17 -0
  24. package/examples/audio.ogg +0 -0
  25. package/examples/bars.js +19 -0
  26. package/examples/basic.js +8 -0
  27. package/examples/gradient.js +28 -0
  28. package/examples/regions.js +63 -0
  29. package/examples/video.js +19 -0
  30. package/examples/webaudio.js +14 -0
  31. package/package.json +19 -99
  32. package/src/base-plugin.ts +20 -0
  33. package/src/decoder.ts +41 -0
  34. package/src/event-emitter.ts +35 -0
  35. package/src/fetcher.ts +7 -0
  36. package/src/index.ts +252 -0
  37. package/src/player-webaudio.ts +34 -0
  38. package/src/player.ts +50 -0
  39. package/src/plugins/regions.ts +240 -0
  40. package/src/renderer.ts +250 -0
  41. package/src/timer.ts +27 -0
  42. package/tsconfig.json +105 -0
  43. package/tutorial/index.html +47 -0
  44. package/tutorial/src/editor.js +70 -0
  45. package/tutorial/src/init.js +66 -0
  46. package/tutorial/src/url.js +25 -0
  47. package/tutorial/style.css +211 -0
  48. package/yarn-error.log +1049 -0
  49. package/LICENSE +0 -29
  50. package/dist/plugin/wavesurfer.cursor.js +0 -418
  51. package/dist/plugin/wavesurfer.cursor.js.map +0 -1
  52. package/dist/plugin/wavesurfer.cursor.min.js +0 -7
  53. package/dist/plugin/wavesurfer.cursor.min.js.map +0 -1
  54. package/dist/plugin/wavesurfer.elan.js +0 -381
  55. package/dist/plugin/wavesurfer.elan.js.map +0 -1
  56. package/dist/plugin/wavesurfer.elan.min.js +0 -7
  57. package/dist/plugin/wavesurfer.elan.min.js.map +0 -1
  58. package/dist/plugin/wavesurfer.markers.js +0 -520
  59. package/dist/plugin/wavesurfer.markers.js.map +0 -1
  60. package/dist/plugin/wavesurfer.markers.min.js +0 -7
  61. package/dist/plugin/wavesurfer.markers.min.js.map +0 -1
  62. package/dist/plugin/wavesurfer.mediasession.js +0 -181
  63. package/dist/plugin/wavesurfer.mediasession.js.map +0 -1
  64. package/dist/plugin/wavesurfer.mediasession.min.js +0 -7
  65. package/dist/plugin/wavesurfer.mediasession.min.js.map +0 -1
  66. package/dist/plugin/wavesurfer.microphone.js +0 -466
  67. package/dist/plugin/wavesurfer.microphone.js.map +0 -1
  68. package/dist/plugin/wavesurfer.microphone.min.js +0 -7
  69. package/dist/plugin/wavesurfer.microphone.min.js.map +0 -1
  70. package/dist/plugin/wavesurfer.minimap.js +0 -440
  71. package/dist/plugin/wavesurfer.minimap.js.map +0 -1
  72. package/dist/plugin/wavesurfer.minimap.min.js +0 -7
  73. package/dist/plugin/wavesurfer.minimap.min.js.map +0 -1
  74. package/dist/plugin/wavesurfer.playhead.js +0 -300
  75. package/dist/plugin/wavesurfer.playhead.js.map +0 -1
  76. package/dist/plugin/wavesurfer.playhead.min.js +0 -7
  77. package/dist/plugin/wavesurfer.playhead.min.js.map +0 -1
  78. package/dist/plugin/wavesurfer.regions.js +0 -1335
  79. package/dist/plugin/wavesurfer.regions.js.map +0 -1
  80. package/dist/plugin/wavesurfer.regions.min.js +0 -7
  81. package/dist/plugin/wavesurfer.regions.min.js.map +0 -1
  82. package/dist/plugin/wavesurfer.spectrogram.js +0 -736
  83. package/dist/plugin/wavesurfer.spectrogram.js.map +0 -1
  84. package/dist/plugin/wavesurfer.spectrogram.min.js +0 -7
  85. package/dist/plugin/wavesurfer.spectrogram.min.js.map +0 -1
  86. package/dist/plugin/wavesurfer.timeline.js +0 -649
  87. package/dist/plugin/wavesurfer.timeline.js.map +0 -1
  88. package/dist/plugin/wavesurfer.timeline.min.js +0 -7
  89. package/dist/plugin/wavesurfer.timeline.min.js.map +0 -1
  90. package/dist/wavesurfer-html-init.js +0 -381
  91. package/dist/wavesurfer-html-init.js.map +0 -1
  92. package/dist/wavesurfer-html-init.min.js +0 -7
  93. package/dist/wavesurfer-html-init.min.js.map +0 -1
  94. package/dist/wavesurfer.js +0 -6145
  95. package/dist/wavesurfer.js.map +0 -1
  96. package/dist/wavesurfer.min.js +0 -7
  97. package/dist/wavesurfer.min.js.map +0 -1
  98. package/src/drawer.canvasentry.js +0 -427
  99. package/src/drawer.js +0 -413
  100. package/src/drawer.multicanvas.js +0 -617
  101. package/src/html-init.js +0 -241
  102. package/src/mediaelement-webaudio.js +0 -77
  103. package/src/mediaelement.js +0 -443
  104. package/src/peakcache.js +0 -127
  105. package/src/plugin/cursor/index.js +0 -350
  106. package/src/plugin/elan/index.js +0 -307
  107. package/src/plugin/markers/index.js +0 -436
  108. package/src/plugin/mediasession/index.js +0 -94
  109. package/src/plugin/microphone/index.js +0 -415
  110. package/src/plugin/minimap/index.js +0 -403
  111. package/src/plugin/playhead/index.js +0 -226
  112. package/src/plugin/regions/index.js +0 -478
  113. package/src/plugin/regions/region.js +0 -875
  114. package/src/plugin/spectrogram/fft.js +0 -222
  115. package/src/plugin/spectrogram/index.js +0 -522
  116. package/src/plugin/timeline/index.js +0 -600
  117. package/src/util/absMax.js +0 -16
  118. package/src/util/clamp.js +0 -11
  119. package/src/util/fetch.js +0 -222
  120. package/src/util/frame.js +0 -13
  121. package/src/util/get-id.js +0 -22
  122. package/src/util/index.js +0 -14
  123. package/src/util/max.js +0 -16
  124. package/src/util/min.js +0 -16
  125. package/src/util/observer.js +0 -144
  126. package/src/util/orientation.js +0 -98
  127. package/src/util/prevent-click.js +0 -19
  128. package/src/util/request-animation-frame.js +0 -15
  129. package/src/util/silence-mode.js +0 -42
  130. package/src/util/style.js +0 -16
  131. package/src/wavesurfer.js +0 -1797
  132. package/src/webaudio.js +0 -779
package/src/webaudio.js DELETED
@@ -1,779 +0,0 @@
1
- import * as util from './util';
2
-
3
- // using constants to prevent someone writing the string wrong
4
- const PLAYING = 'playing';
5
- const PAUSED = 'paused';
6
- const FINISHED = 'finished';
7
-
8
- /**
9
- * WebAudio backend
10
- *
11
- * @extends {Observer}
12
- */
13
- export default class WebAudio extends util.Observer {
14
- /** scriptBufferSize: size of the processing buffer */
15
- static scriptBufferSize = 256;
16
- /** audioContext: allows to process audio with WebAudio API */
17
- audioContext = null;
18
- /** @private */
19
- offlineAudioContext = null;
20
- /** @private */
21
- stateBehaviors = {
22
- [PLAYING]: {
23
- init() {
24
- this.addOnAudioProcess();
25
- },
26
- getPlayedPercents() {
27
- const duration = this.getDuration();
28
- return this.getCurrentTime() / duration || 0;
29
- },
30
- getCurrentTime() {
31
- return this.startPosition + this.getPlayedTime();
32
- }
33
- },
34
- [PAUSED]: {
35
- init() {
36
- this.removeOnAudioProcess();
37
- },
38
- getPlayedPercents() {
39
- const duration = this.getDuration();
40
- return this.getCurrentTime() / duration || 0;
41
- },
42
- getCurrentTime() {
43
- return this.startPosition;
44
- }
45
- },
46
- [FINISHED]: {
47
- init() {
48
- this.removeOnAudioProcess();
49
- this.fireEvent('finish');
50
- },
51
- getPlayedPercents() {
52
- return 1;
53
- },
54
- getCurrentTime() {
55
- return this.getDuration();
56
- }
57
- }
58
- };
59
-
60
- /**
61
- * Does the browser support this backend
62
- *
63
- * @return {boolean} Whether or not this browser supports this backend
64
- */
65
- supportsWebAudio() {
66
- return !!(window.AudioContext || window.webkitAudioContext);
67
- }
68
-
69
- /**
70
- * Get the audio context used by this backend or create one
71
- *
72
- * @return {AudioContext} Existing audio context, or creates a new one
73
- */
74
- getAudioContext() {
75
- if (!window.WaveSurferAudioContext) {
76
- window.WaveSurferAudioContext = new (window.AudioContext ||
77
- window.webkitAudioContext)();
78
- }
79
- return window.WaveSurferAudioContext;
80
- }
81
-
82
- /**
83
- * Get the offline audio context used by this backend or create one
84
- *
85
- * @param {number} sampleRate The sample rate to use
86
- * @return {OfflineAudioContext} Existing offline audio context, or creates
87
- * a new one
88
- */
89
- getOfflineAudioContext(sampleRate) {
90
- if (!window.WaveSurferOfflineAudioContext) {
91
- window.WaveSurferOfflineAudioContext = new (window.OfflineAudioContext ||
92
- window.webkitOfflineAudioContext)(1, 2, sampleRate);
93
- }
94
- return window.WaveSurferOfflineAudioContext;
95
- }
96
-
97
- /**
98
- * Construct the backend
99
- *
100
- * @param {WavesurferParams} params Wavesurfer parameters
101
- */
102
- constructor(params) {
103
- super();
104
- /** @private */
105
- this.params = params;
106
- /** ac: Audio Context instance */
107
- this.ac =
108
- params.audioContext ||
109
- (this.supportsWebAudio() ? this.getAudioContext() : {});
110
- /**@private */
111
- this.lastPlay = this.ac.currentTime;
112
- /** @private */
113
- this.startPosition = 0;
114
- /** @private */
115
- this.scheduledPause = null;
116
- /** @private */
117
- this.states = {
118
- [PLAYING]: Object.create(this.stateBehaviors[PLAYING]),
119
- [PAUSED]: Object.create(this.stateBehaviors[PAUSED]),
120
- [FINISHED]: Object.create(this.stateBehaviors[FINISHED])
121
- };
122
- /** @private */
123
- this.buffer = null;
124
- /** @private */
125
- this.filters = [];
126
- /** gainNode: allows to control audio volume */
127
- this.gainNode = null;
128
- /** @private */
129
- this.mergedPeaks = null;
130
- /** @private */
131
- this.offlineAc = null;
132
- /** @private */
133
- this.peaks = null;
134
- /** @private */
135
- this.playbackRate = 1;
136
- /** analyser: provides audio analysis information */
137
- this.analyser = null;
138
- /** scriptNode: allows processing audio */
139
- this.scriptNode = null;
140
- /** @private */
141
- this.source = null;
142
- /** @private */
143
- this.splitPeaks = [];
144
- /** @private */
145
- this.state = null;
146
- /** @private */
147
- this.explicitDuration = params.duration;
148
- /** @private */
149
- this.sinkStreamDestination = null;
150
- /** @private */
151
- this.sinkAudioElement = null;
152
- /**
153
- * Boolean indicating if the backend was destroyed.
154
- */
155
- this.destroyed = false;
156
- }
157
-
158
- /**
159
- * Initialise the backend, called in `wavesurfer.createBackend()`
160
- */
161
- init() {
162
- this.createVolumeNode();
163
- this.createScriptNode();
164
- this.createAnalyserNode();
165
-
166
- this.setState(PAUSED);
167
- this.setPlaybackRate(this.params.audioRate);
168
- this.setLength(0);
169
- }
170
-
171
- /** @private */
172
- disconnectFilters() {
173
- if (this.filters) {
174
- this.filters.forEach(filter => {
175
- filter && filter.disconnect();
176
- });
177
- this.filters = null;
178
- // Reconnect direct path
179
- this.analyser.connect(this.gainNode);
180
- }
181
- }
182
-
183
- /**
184
- * @private
185
- *
186
- * @param {string} state The new state
187
- */
188
- setState(state) {
189
- if (this.state !== this.states[state]) {
190
- this.state = this.states[state];
191
- this.state.init.call(this);
192
- }
193
- }
194
-
195
- /**
196
- * Unpacked `setFilters()`
197
- *
198
- * @param {...AudioNode} filters One or more filters to set
199
- */
200
- setFilter(...filters) {
201
- this.setFilters(filters);
202
- }
203
-
204
- /**
205
- * Insert custom Web Audio nodes into the graph
206
- *
207
- * @param {AudioNode[]} filters Packed filters array
208
- * @example
209
- * const lowpass = wavesurfer.backend.ac.createBiquadFilter();
210
- * wavesurfer.backend.setFilter(lowpass);
211
- */
212
- setFilters(filters) {
213
- // Remove existing filters
214
- this.disconnectFilters();
215
-
216
- // Insert filters if filter array not empty
217
- if (filters && filters.length) {
218
- this.filters = filters;
219
-
220
- // Disconnect direct path before inserting filters
221
- this.analyser.disconnect();
222
-
223
- // Connect each filter in turn
224
- filters
225
- .reduce((prev, curr) => {
226
- prev.connect(curr);
227
- return curr;
228
- }, this.analyser)
229
- .connect(this.gainNode);
230
- }
231
- }
232
- /** Create ScriptProcessorNode to process audio */
233
- createScriptNode() {
234
- if (this.params.audioScriptProcessor) {
235
- this.scriptNode = this.params.audioScriptProcessor;
236
- } else {
237
- if (this.ac.createScriptProcessor) {
238
- this.scriptNode = this.ac.createScriptProcessor(
239
- WebAudio.scriptBufferSize
240
- );
241
- } else {
242
- this.scriptNode = this.ac.createJavaScriptNode(
243
- WebAudio.scriptBufferSize
244
- );
245
- }
246
- }
247
- this.scriptNode.connect(this.ac.destination);
248
- }
249
-
250
- /** @private */
251
- addOnAudioProcess() {
252
- this.scriptNode.onaudioprocess = () => {
253
- const time = this.getCurrentTime();
254
-
255
- if (time >= this.getDuration()) {
256
- this.setState(FINISHED);
257
- this.fireEvent('pause');
258
- } else if (time >= this.scheduledPause) {
259
- this.pause();
260
- } else if (this.state === this.states[PLAYING]) {
261
- this.fireEvent('audioprocess', time);
262
- }
263
- };
264
- }
265
-
266
- /** @private */
267
- removeOnAudioProcess() {
268
- this.scriptNode.onaudioprocess = null;
269
- }
270
- /** Create analyser node to perform audio analysis */
271
- createAnalyserNode() {
272
- this.analyser = this.ac.createAnalyser();
273
- this.analyser.connect(this.gainNode);
274
- }
275
-
276
- /**
277
- * Create the gain node needed to control the playback volume.
278
- *
279
- */
280
- createVolumeNode() {
281
- // Create gain node using the AudioContext
282
- if (this.ac.createGain) {
283
- this.gainNode = this.ac.createGain();
284
- } else {
285
- this.gainNode = this.ac.createGainNode();
286
- }
287
- // Add the gain node to the graph
288
- this.gainNode.connect(this.ac.destination);
289
- }
290
-
291
- /**
292
- * Set the sink id for the media player
293
- *
294
- * @param {string} deviceId String value representing audio device id.
295
- * @returns {Promise} A Promise that resolves to `undefined` when there
296
- * are no errors.
297
- */
298
- setSinkId(deviceId) {
299
- if (deviceId) {
300
- /**
301
- * The webaudio API doesn't currently support setting the device
302
- * output. Here we create an HTMLAudioElement, connect the
303
- * webaudio stream to that element and setSinkId there.
304
- */
305
- if (!this.sinkAudioElement) {
306
- this.sinkAudioElement = new window.Audio();
307
- // autoplay is necessary since we're not invoking .play()
308
- this.sinkAudioElement.autoplay = true;
309
- }
310
- if (!this.sinkAudioElement.setSinkId) {
311
- return Promise.reject(
312
- new Error('setSinkId is not supported in your browser')
313
- );
314
- }
315
- if (!this.sinkStreamDestination) {
316
- this.sinkStreamDestination = this.ac.createMediaStreamDestination();
317
- }
318
- this.gainNode.disconnect();
319
- this.gainNode.connect(this.sinkStreamDestination);
320
- this.sinkAudioElement.srcObject = this.sinkStreamDestination.stream;
321
-
322
- return this.sinkAudioElement.setSinkId(deviceId);
323
- } else {
324
- return Promise.reject(new Error('Invalid deviceId: ' + deviceId));
325
- }
326
- }
327
-
328
- /**
329
- * Set the audio volume
330
- *
331
- * @param {number} value A floating point value between 0 and 1.
332
- */
333
- setVolume(value) {
334
- this.gainNode.gain.setValueAtTime(value, this.ac.currentTime);
335
- }
336
-
337
- /**
338
- * Get the current volume
339
- *
340
- * @return {number} value A floating point value between 0 and 1.
341
- */
342
- getVolume() {
343
- return this.gainNode.gain.value;
344
- }
345
-
346
- /**
347
- * Decode an array buffer and pass data to a callback
348
- *
349
- * @private
350
- * @param {ArrayBuffer} arraybuffer The array buffer to decode
351
- * @param {function} callback The function to call on complete.
352
- * @param {function} errback The function to call on error.
353
- */
354
- decodeArrayBuffer(arraybuffer, callback, errback) {
355
- if (!this.offlineAc) {
356
- this.offlineAc = this.getOfflineAudioContext(
357
- this.ac && this.ac.sampleRate ? this.ac.sampleRate : 44100
358
- );
359
- }
360
- if ('webkitAudioContext' in window) {
361
- // Safari: no support for Promise-based decodeAudioData enabled
362
- // Enable it in Safari using the Experimental Features > Modern WebAudio API option
363
- this.offlineAc.decodeAudioData(
364
- arraybuffer,
365
- data => callback(data),
366
- errback
367
- );
368
- } else {
369
- this.offlineAc.decodeAudioData(arraybuffer).then(
370
- (data) => callback(data)
371
- ).catch(
372
- (err) => errback(err)
373
- );
374
- }
375
- }
376
-
377
- /**
378
- * Set pre-decoded peaks
379
- *
380
- * @param {number[]|Number.<Array[]>} peaks Peaks data
381
- * @param {?number} duration Explicit duration
382
- */
383
- setPeaks(peaks, duration) {
384
- if (duration != null) {
385
- this.explicitDuration = duration;
386
- }
387
- this.peaks = peaks;
388
- }
389
-
390
- /**
391
- * Set the rendered length (different from the length of the audio)
392
- *
393
- * @param {number} length The rendered length
394
- */
395
- setLength(length) {
396
- // No resize, we can preserve the cached peaks.
397
- if (this.mergedPeaks && length == 2 * this.mergedPeaks.length - 1 + 2) {
398
- return;
399
- }
400
-
401
- this.splitPeaks = [];
402
- this.mergedPeaks = [];
403
- // Set the last element of the sparse array so the peak arrays are
404
- // appropriately sized for other calculations.
405
- const channels = this.buffer ? this.buffer.numberOfChannels : 1;
406
- let c;
407
- for (c = 0; c < channels; c++) {
408
- this.splitPeaks[c] = [];
409
- this.splitPeaks[c][2 * (length - 1)] = 0;
410
- this.splitPeaks[c][2 * (length - 1) + 1] = 0;
411
- }
412
- this.mergedPeaks[2 * (length - 1)] = 0;
413
- this.mergedPeaks[2 * (length - 1) + 1] = 0;
414
- }
415
-
416
- /**
417
- * Compute the max and min value of the waveform when broken into <length> subranges.
418
- *
419
- * @param {number} length How many subranges to break the waveform into.
420
- * @param {number} first First sample in the required range.
421
- * @param {number} last Last sample in the required range.
422
- * @return {number[]|Number.<Array[]>} Array of 2*<length> peaks or array of arrays of
423
- * peaks consisting of (max, min) values for each subrange.
424
- */
425
- getPeaks(length, first, last) {
426
- if (this.peaks) {
427
- return this.peaks;
428
- }
429
- if (!this.buffer) {
430
- return [];
431
- }
432
-
433
- first = first || 0;
434
- last = last || length - 1;
435
-
436
- this.setLength(length);
437
-
438
- if (!this.buffer) {
439
- return this.params.splitChannels
440
- ? this.splitPeaks
441
- : this.mergedPeaks;
442
- }
443
-
444
- /**
445
- * The following snippet fixes a buffering data issue on the Safari
446
- * browser which returned undefined It creates the missing buffer based
447
- * on 1 channel, 4096 samples and the sampleRate from the current
448
- * webaudio context 4096 samples seemed to be the best fit for rendering
449
- * will review this code once a stable version of Safari TP is out
450
- */
451
- if (!this.buffer.length) {
452
- const newBuffer = this.createBuffer(1, 4096, this.sampleRate);
453
- this.buffer = newBuffer.buffer;
454
- }
455
-
456
- const sampleSize = this.buffer.length / length;
457
- const sampleStep = ~~(sampleSize / 10) || 1;
458
- const channels = this.buffer.numberOfChannels;
459
- let c;
460
-
461
- for (c = 0; c < channels; c++) {
462
- const peaks = this.splitPeaks[c];
463
- const chan = this.buffer.getChannelData(c);
464
- let i;
465
-
466
- for (i = first; i <= last; i++) {
467
- const start = ~~(i * sampleSize);
468
- const end = ~~(start + sampleSize);
469
- /**
470
- * Initialize the max and min to the first sample of this
471
- * subrange, so that even if the samples are entirely
472
- * on one side of zero, we still return the true max and
473
- * min values in the subrange.
474
- */
475
- let min = chan[start];
476
- let max = min;
477
- let j;
478
-
479
- for (j = start; j < end; j += sampleStep) {
480
- const value = chan[j];
481
-
482
- if (value > max) {
483
- max = value;
484
- }
485
-
486
- if (value < min) {
487
- min = value;
488
- }
489
- }
490
-
491
- peaks[2 * i] = max;
492
- peaks[2 * i + 1] = min;
493
-
494
- if (c == 0 || max > this.mergedPeaks[2 * i]) {
495
- this.mergedPeaks[2 * i] = max;
496
- }
497
-
498
- if (c == 0 || min < this.mergedPeaks[2 * i + 1]) {
499
- this.mergedPeaks[2 * i + 1] = min;
500
- }
501
- }
502
- }
503
-
504
- return this.params.splitChannels ? this.splitPeaks : this.mergedPeaks;
505
- }
506
-
507
- /**
508
- * Get the position from 0 to 1
509
- *
510
- * @return {number} Position
511
- */
512
- getPlayedPercents() {
513
- return this.state.getPlayedPercents.call(this);
514
- }
515
-
516
- /** @private */
517
- disconnectSource() {
518
- if (this.source) {
519
- this.source.disconnect();
520
- }
521
- }
522
- /**
523
- * Destroy all references with WebAudio, disconnecting audio nodes and closing Audio Context
524
- */
525
- destroyWebAudio() {
526
- this.disconnectFilters();
527
- this.disconnectSource();
528
- this.gainNode.disconnect();
529
- this.scriptNode.disconnect();
530
- this.analyser.disconnect();
531
-
532
- // close the audioContext if closeAudioContext option is set to true
533
- if (this.params.closeAudioContext) {
534
- // check if browser supports AudioContext.close()
535
- if (
536
- typeof this.ac.close === 'function' &&
537
- this.ac.state != 'closed'
538
- ) {
539
- this.ac.close();
540
- }
541
- // clear the reference to the audiocontext
542
- this.ac = null;
543
- // clear the actual audiocontext, either passed as param or the
544
- // global singleton
545
- if (!this.params.audioContext) {
546
- window.WaveSurferAudioContext = null;
547
- } else {
548
- this.params.audioContext = null;
549
- }
550
- // clear the offlineAudioContext
551
- window.WaveSurferOfflineAudioContext = null;
552
- }
553
-
554
- // disconnect resources used by setSinkId
555
- if (this.sinkStreamDestination) {
556
- this.sinkAudioElement.pause();
557
- this.sinkAudioElement.srcObject = null;
558
- this.sinkStreamDestination.disconnect();
559
- this.sinkStreamDestination = null;
560
- }
561
- }
562
- /**
563
- * This is called when wavesurfer is destroyed
564
- */
565
- destroy() {
566
- if (!this.isPaused()) {
567
- this.pause();
568
- }
569
- this.unAll();
570
- this.buffer = null;
571
- this.destroyed = true;
572
-
573
- this.destroyWebAudio();
574
- }
575
-
576
- /**
577
- * Loaded a decoded audio buffer
578
- *
579
- * @param {Object} buffer Decoded audio buffer to load
580
- */
581
- load(buffer) {
582
- this.startPosition = 0;
583
- this.lastPlay = this.ac.currentTime;
584
- this.buffer = buffer;
585
- this.createSource();
586
- }
587
-
588
- /** @private */
589
- createSource() {
590
- this.disconnectSource();
591
- this.source = this.ac.createBufferSource();
592
-
593
- // adjust for old browsers
594
- this.source.start = this.source.start || this.source.noteGrainOn;
595
- this.source.stop = this.source.stop || this.source.noteOff;
596
-
597
- this.setPlaybackRate(this.playbackRate);
598
- this.source.buffer = this.buffer;
599
- this.source.connect(this.analyser);
600
- }
601
-
602
- /**
603
- * @private
604
- *
605
- * some browsers require an explicit call to #resume before they will play back audio
606
- */
607
- resumeAudioContext() {
608
- if (this.ac.state == 'suspended') {
609
- this.ac.resume && this.ac.resume();
610
- }
611
- }
612
-
613
- /**
614
- * Used by `wavesurfer.isPlaying()` and `wavesurfer.playPause()`
615
- *
616
- * @return {boolean} Whether or not this backend is currently paused
617
- */
618
- isPaused() {
619
- return this.state !== this.states[PLAYING];
620
- }
621
-
622
- /**
623
- * Used by `wavesurfer.getDuration()`
624
- *
625
- * @return {number} Duration of loaded buffer
626
- */
627
- getDuration() {
628
- if (this.explicitDuration) {
629
- return this.explicitDuration;
630
- }
631
- if (!this.buffer) {
632
- return 0;
633
- }
634
- return this.buffer.duration;
635
- }
636
-
637
- /**
638
- * Used by `wavesurfer.seekTo()`
639
- *
640
- * @param {number} start Position to start at in seconds
641
- * @param {number} end Position to end at in seconds
642
- * @return {{start: number, end: number}} Object containing start and end
643
- * positions
644
- */
645
- seekTo(start, end) {
646
- if (!this.buffer) {
647
- return;
648
- }
649
-
650
- this.scheduledPause = null;
651
-
652
- if (start == null) {
653
- start = this.getCurrentTime();
654
- if (start >= this.getDuration()) {
655
- start = 0;
656
- }
657
- }
658
- if (end == null) {
659
- end = this.getDuration();
660
- }
661
-
662
- this.startPosition = start;
663
- this.lastPlay = this.ac.currentTime;
664
-
665
- if (this.state === this.states[FINISHED]) {
666
- this.setState(PAUSED);
667
- }
668
-
669
- return {
670
- start: start,
671
- end: end
672
- };
673
- }
674
-
675
- /**
676
- * Get the playback position in seconds
677
- *
678
- * @return {number} The playback position in seconds
679
- */
680
- getPlayedTime() {
681
- return (this.ac.currentTime - this.lastPlay) * this.playbackRate;
682
- }
683
-
684
- /**
685
- * Plays the loaded audio region.
686
- *
687
- * @param {number} start Start offset in seconds, relative to the beginning
688
- * of a clip.
689
- * @param {number} end When to stop relative to the beginning of a clip.
690
- */
691
- play(start, end) {
692
- if (!this.buffer) {
693
- return;
694
- }
695
-
696
- // need to re-create source on each playback
697
- this.createSource();
698
-
699
- const adjustedTime = this.seekTo(start, end);
700
-
701
- start = adjustedTime.start;
702
- end = adjustedTime.end;
703
-
704
- this.scheduledPause = end;
705
-
706
- this.source.start(0, start);
707
-
708
- this.resumeAudioContext();
709
-
710
- this.setState(PLAYING);
711
-
712
- this.fireEvent('play');
713
- }
714
-
715
- /**
716
- * Pauses the loaded audio.
717
- */
718
- pause() {
719
- this.scheduledPause = null;
720
-
721
- this.startPosition += this.getPlayedTime();
722
- try {
723
- this.source && this.source.stop(0);
724
- } catch (err) {
725
- // Calling stop can throw the following 2 errors:
726
- // - RangeError (The value specified for when is negative.)
727
- // - InvalidStateNode (The node has not been started by calling start().)
728
- // We can safely ignore both errors, because:
729
- // - The range is surely correct
730
- // - The node might not have been started yet, in which case we just want to carry on without causing any trouble.
731
- }
732
-
733
- this.setState(PAUSED);
734
-
735
- this.fireEvent('pause');
736
- }
737
-
738
- /**
739
- * Returns the current time in seconds relative to the audio-clip's
740
- * duration.
741
- *
742
- * @return {number} The current time in seconds
743
- */
744
- getCurrentTime() {
745
- return this.state.getCurrentTime.call(this);
746
- }
747
-
748
- /**
749
- * Returns the current playback rate. (0=no playback, 1=normal playback)
750
- *
751
- * @return {number} The current playback rate
752
- */
753
- getPlaybackRate() {
754
- return this.playbackRate;
755
- }
756
-
757
- /**
758
- * Set the audio source playback rate.
759
- *
760
- * @param {number} value The playback rate to use
761
- */
762
- setPlaybackRate(value) {
763
- this.playbackRate = value || 1;
764
- this.source && this.source.playbackRate.setValueAtTime(
765
- this.playbackRate,
766
- this.ac.currentTime
767
- );
768
- }
769
-
770
- /**
771
- * Set a point in seconds for playback to stop at.
772
- *
773
- * @param {number} end Position to end at
774
- * @version 3.3.0
775
- */
776
- setPlayEnd(end) {
777
- this.scheduledPause = end;
778
- }
779
- }