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/dist/player.js ADDED
@@ -0,0 +1,40 @@
1
+ class Player {
2
+ constructor({ media }) {
3
+ this.isExternalMedia = false;
4
+ if (media) {
5
+ this.media = media;
6
+ this.isExternalMedia = true;
7
+ }
8
+ else {
9
+ this.media = document.createElement('audio');
10
+ }
11
+ }
12
+ on(event, callback) {
13
+ this.media.addEventListener(event, callback);
14
+ return () => this.media.removeEventListener(event, callback);
15
+ }
16
+ destroy() {
17
+ if (!this.isExternalMedia) {
18
+ this.media.remove();
19
+ }
20
+ }
21
+ loadUrl(src) {
22
+ this.media.src = src;
23
+ }
24
+ getCurrentTime() {
25
+ return this.media.currentTime;
26
+ }
27
+ play() {
28
+ this.media.play();
29
+ }
30
+ pause() {
31
+ this.media.pause();
32
+ }
33
+ isPlaying() {
34
+ return this.media.currentTime > 0 && !this.media.paused && !this.media.ended;
35
+ }
36
+ seekTo(time) {
37
+ this.media.currentTime = time;
38
+ }
39
+ }
40
+ export default Player;
@@ -0,0 +1,48 @@
1
+ import BasePlugin from '../base-plugin.js';
2
+ import { WaveSurferPluginParams } from '../index.js';
3
+ type Region = {
4
+ startTime: number;
5
+ endTime: number;
6
+ title: string;
7
+ start: number;
8
+ end: number;
9
+ element: HTMLElement;
10
+ };
11
+ type RegionsPluginEvents = {
12
+ 'region-created': {
13
+ region: Region;
14
+ };
15
+ 'region-updated': {
16
+ region: Region;
17
+ };
18
+ 'region-clicked': {
19
+ region: Region;
20
+ };
21
+ };
22
+ declare class RegionsPlugin extends BasePlugin<RegionsPluginEvents> {
23
+ private dragStart;
24
+ private container;
25
+ private regions;
26
+ private createdRegion;
27
+ private modifiedRegion;
28
+ private isResizingLeft;
29
+ private isMoving;
30
+ /** Create an instance of RegionsPlugin */
31
+ constructor(params: WaveSurferPluginParams);
32
+ /** Unmounts the regions */
33
+ destroy(): void;
34
+ private initContainer;
35
+ private handleMouseDown;
36
+ private handleMouseMove;
37
+ private handleMouseUp;
38
+ private createRegionElement;
39
+ private createRegion;
40
+ private addRegion;
41
+ private updateRegion;
42
+ private moveRegion;
43
+ /** Create a region at a given start and end time, with an optional title */
44
+ addRegionAtTime(startTime: number, endTime: number, title?: string): Region;
45
+ /** Set the background color of a region */
46
+ setRegionColor(region: Region, color: string): void;
47
+ }
48
+ export default RegionsPlugin;
@@ -0,0 +1,183 @@
1
+ import BasePlugin from '../base-plugin.js';
2
+ const MIN_WIDTH = 10;
3
+ class RegionsPlugin extends BasePlugin {
4
+ /** Create an instance of RegionsPlugin */
5
+ constructor(params) {
6
+ super(params);
7
+ this.dragStart = NaN;
8
+ this.regions = [];
9
+ this.createdRegion = null;
10
+ this.modifiedRegion = null;
11
+ this.isResizingLeft = false;
12
+ this.isMoving = false;
13
+ this.handleMouseDown = (e) => {
14
+ this.dragStart = e.clientX - this.container.getBoundingClientRect().left;
15
+ };
16
+ this.handleMouseMove = (e) => {
17
+ const dragEnd = e.clientX - this.container.getBoundingClientRect().left;
18
+ if (this.modifiedRegion && this.isMoving) {
19
+ this.moveRegion(this.modifiedRegion, dragEnd - this.dragStart);
20
+ this.dragStart = dragEnd;
21
+ return;
22
+ }
23
+ if (this.modifiedRegion) {
24
+ this.updateRegion(this.modifiedRegion, this.isResizingLeft ? dragEnd : undefined, this.isResizingLeft ? undefined : dragEnd);
25
+ return;
26
+ }
27
+ if (!isNaN(this.dragStart)) {
28
+ const dragEnd = e.clientX - this.container.getBoundingClientRect().left;
29
+ if (dragEnd - this.dragStart >= MIN_WIDTH) {
30
+ if (!this.createdRegion) {
31
+ this.renderer.getContainer().style.pointerEvents = 'none';
32
+ this.createdRegion = this.createRegion(this.dragStart, dragEnd);
33
+ }
34
+ else {
35
+ this.updateRegion(this.createdRegion, this.dragStart, dragEnd);
36
+ }
37
+ }
38
+ }
39
+ };
40
+ this.handleMouseUp = () => {
41
+ if (this.createdRegion) {
42
+ this.addRegion(this.createdRegion);
43
+ this.createdRegion = null;
44
+ }
45
+ this.modifiedRegion = null;
46
+ this.isMoving = false;
47
+ this.dragStart = NaN;
48
+ this.renderer.getContainer().style.pointerEvents = '';
49
+ };
50
+ this.container = this.initContainer();
51
+ const unsubscribeReady = this.wavesurfer.on('ready', () => {
52
+ const wrapper = this.renderer.getContainer();
53
+ wrapper.appendChild(this.container);
54
+ unsubscribeReady();
55
+ });
56
+ this.subscriptions.push(unsubscribeReady);
57
+ const wsContainer = this.renderer.getContainer();
58
+ wsContainer.addEventListener('mousedown', this.handleMouseDown);
59
+ document.addEventListener('mousemove', this.handleMouseMove);
60
+ document.addEventListener('mouseup', this.handleMouseUp);
61
+ }
62
+ /** Unmounts the regions */
63
+ destroy() {
64
+ var _a;
65
+ this.renderer.getContainer().removeEventListener('mousedown', this.handleMouseDown);
66
+ document.removeEventListener('mousemove', this.handleMouseMove);
67
+ document.removeEventListener('mouseup', this.handleMouseUp, true);
68
+ (_a = this.container) === null || _a === void 0 ? void 0 : _a.remove();
69
+ super.destroy();
70
+ }
71
+ initContainer() {
72
+ const div = document.createElement('div');
73
+ div.style.position = 'absolute';
74
+ div.style.top = '0';
75
+ div.style.left = '0';
76
+ div.style.width = '100%';
77
+ div.style.height = '100%';
78
+ div.style.zIndex = '3';
79
+ div.style.pointerEvents = 'none';
80
+ return div;
81
+ }
82
+ createRegionElement(start, end, title = '') {
83
+ const el = document.createElement('div');
84
+ el.style.position = 'absolute';
85
+ el.style.left = `${start}px`;
86
+ el.style.width = `${end - start}px`;
87
+ el.style.height = '100%';
88
+ el.style.backgroundColor = 'rgba(0, 0, 0, 0.1)';
89
+ el.style.transition = 'background-color 0.2s ease';
90
+ el.style.cursor = 'move';
91
+ el.style.pointerEvents = 'all';
92
+ el.title = title;
93
+ const leftHandle = document.createElement('div');
94
+ leftHandle.style.position = 'absolute';
95
+ leftHandle.style.left = '0';
96
+ leftHandle.style.width = '6px';
97
+ leftHandle.style.height = '100%';
98
+ leftHandle.style.borderLeft = '2px solid rgba(0, 0, 0, 0.5)';
99
+ leftHandle.style.cursor = 'ew-resize';
100
+ leftHandle.style.pointerEvents = 'all';
101
+ el.appendChild(leftHandle);
102
+ const rightHandle = document.createElement('div');
103
+ rightHandle.style.position = 'absolute';
104
+ rightHandle.style.right = '0';
105
+ rightHandle.style.width = '6px';
106
+ rightHandle.style.height = '100%';
107
+ rightHandle.style.borderRight = '2px solid rgba(0, 0, 0, 0.5)';
108
+ rightHandle.style.cursor = 'ew-resize';
109
+ rightHandle.style.pointerEvents = 'all';
110
+ el.appendChild(rightHandle);
111
+ leftHandle.addEventListener('mousedown', (e) => {
112
+ e.stopPropagation();
113
+ this.modifiedRegion = this.regions.find((r) => r.element === el) || null;
114
+ this.isResizingLeft = true;
115
+ this.isMoving = false;
116
+ });
117
+ rightHandle.addEventListener('mousedown', (e) => {
118
+ e.stopPropagation();
119
+ this.modifiedRegion = this.regions.find((r) => r.element === el) || null;
120
+ this.isResizingLeft = false;
121
+ this.isMoving = false;
122
+ });
123
+ el.addEventListener('mousedown', () => {
124
+ this.modifiedRegion = this.regions.find((r) => r.element === el) || null;
125
+ this.isMoving = true;
126
+ });
127
+ el.addEventListener('click', () => {
128
+ const region = this.regions.find((r) => r.element === el);
129
+ if (region) {
130
+ this.emit('region-clicked', { region });
131
+ }
132
+ });
133
+ this.container.appendChild(el);
134
+ return el;
135
+ }
136
+ createRegion(start, end, title = '') {
137
+ const duration = this.wavesurfer.getDuration();
138
+ const width = this.container.clientWidth;
139
+ return {
140
+ element: this.createRegionElement(start, end, title),
141
+ start,
142
+ end,
143
+ startTime: (start / width) * duration,
144
+ endTime: (end / width) * duration,
145
+ title,
146
+ };
147
+ }
148
+ addRegion(region) {
149
+ this.regions.push(region);
150
+ this.emit('region-created', { region });
151
+ }
152
+ updateRegion(region, start, end) {
153
+ if (start != null) {
154
+ region.start = start !== null && start !== void 0 ? start : region.start;
155
+ region.element.style.left = `${region.start}px`;
156
+ region.startTime = (start / this.container.clientWidth) * this.wavesurfer.getDuration();
157
+ }
158
+ if (end != null) {
159
+ region.end = end;
160
+ region.element.style.width = `${region.end - region.start}px`;
161
+ region.endTime = (end / this.container.clientWidth) * this.wavesurfer.getDuration();
162
+ }
163
+ this.emit('region-updated', { region });
164
+ }
165
+ moveRegion(region, delta) {
166
+ this.updateRegion(region, region.start + delta, region.end + delta);
167
+ }
168
+ /** Create a region at a given start and end time, with an optional title */
169
+ addRegionAtTime(startTime, endTime, title = '') {
170
+ const duration = this.wavesurfer.getDuration();
171
+ const width = this.container.clientWidth;
172
+ const start = (startTime / duration) * width;
173
+ const end = (endTime / duration) * width;
174
+ const region = this.createRegion(start, end, title);
175
+ this.addRegion(region);
176
+ return region;
177
+ }
178
+ /** Set the background color of a region */
179
+ setRegionColor(region, color) {
180
+ region.element.style.backgroundColor = color;
181
+ }
182
+ }
183
+ export default RegionsPlugin;
@@ -0,0 +1,34 @@
1
+ import EventEmitter from './event-emitter.js';
2
+ type RendererOptions = {
3
+ container: HTMLElement | string | null;
4
+ height: number;
5
+ waveColor: string;
6
+ progressColor: string;
7
+ minPxPerSec: number;
8
+ barWidth?: number;
9
+ barGap?: number;
10
+ barRadius?: number;
11
+ };
12
+ type RendererEvents = {
13
+ click: {
14
+ relativeX: number;
15
+ };
16
+ };
17
+ declare class Renderer extends EventEmitter<RendererEvents> {
18
+ private options;
19
+ private container;
20
+ private shadowRoot;
21
+ private mainCanvas;
22
+ private progressCanvas;
23
+ private cursor;
24
+ private ctx;
25
+ constructor(options: RendererOptions);
26
+ destroy(): void;
27
+ private renderLinePeaks;
28
+ private renderBarPeaks;
29
+ render(channelData: Float32Array[], duration: number, minPxPerSec?: number): void;
30
+ private createProgressMask;
31
+ renderProgress(progress: number, autoCenter?: boolean): void;
32
+ getContainer(): HTMLElement;
33
+ }
34
+ export default Renderer;
@@ -0,0 +1,193 @@
1
+ import EventEmitter from './event-emitter.js';
2
+ class Renderer extends EventEmitter {
3
+ constructor(options) {
4
+ super();
5
+ this.options = options;
6
+ let container = null;
7
+ if (typeof this.options.container === 'string') {
8
+ container = document.querySelector(this.options.container);
9
+ }
10
+ else if (this.options.container instanceof HTMLElement) {
11
+ container = this.options.container;
12
+ }
13
+ if (!container) {
14
+ throw new Error('Container not found');
15
+ }
16
+ const div = document.createElement('div');
17
+ const shadow = div.attachShadow({ mode: 'open' });
18
+ shadow.innerHTML = `
19
+ <style>
20
+ :host .scroll {
21
+ overflow-x: auto;
22
+ overflow-y: visible;
23
+ user-select: none;
24
+ width: 100%;
25
+ height: ${this.options.height}px;
26
+ position: relative;
27
+ }
28
+ :host .wrapper {
29
+ position: relative;
30
+ width: fit-content;
31
+ min-width: 100%;
32
+ height: 100%;
33
+ z-index: 2;
34
+ }
35
+ :host canvas {
36
+ display: block;
37
+ height: 100%;
38
+ min-width: 100%;
39
+ image-rendering: pixelated;
40
+ }
41
+ :host .progress {
42
+ position: absolute;
43
+ z-index: 2;
44
+ top: 0;
45
+ left: 0;
46
+ height: 100%;
47
+ pointer-events: none;
48
+ clip-path: inset(100%);
49
+ }
50
+ :host .cursor {
51
+ position: absolute;
52
+ z-index: 3;
53
+ top: 0;
54
+ left: 0;
55
+ height: 100%;
56
+ border-right: 1px solid ${this.options.progressColor};
57
+ }
58
+ </style>
59
+
60
+ <div class="scroll">
61
+ <div class="wrapper">
62
+ <canvas></canvas>
63
+ <canvas class="progress"></canvas>
64
+ <div class="cursor"></div>
65
+ </div>
66
+ </div>
67
+ `;
68
+ this.container = div;
69
+ this.shadowRoot = shadow;
70
+ this.mainCanvas = shadow.querySelector('canvas');
71
+ this.ctx = this.mainCanvas.getContext('2d', { desynchronized: true });
72
+ this.progressCanvas = shadow.querySelector('.progress');
73
+ this.cursor = shadow.querySelector('.cursor');
74
+ container.appendChild(div);
75
+ this.mainCanvas.addEventListener('click', (e) => {
76
+ const rect = this.mainCanvas.getBoundingClientRect();
77
+ const x = e.clientX - rect.left;
78
+ const relativeX = x / rect.width;
79
+ this.emit('click', { relativeX });
80
+ });
81
+ }
82
+ destroy() {
83
+ this.container.remove();
84
+ }
85
+ renderLinePeaks(channelData, width, height) {
86
+ const { ctx } = this;
87
+ ctx.clearRect(0, 0, width, height);
88
+ ctx.beginPath();
89
+ ctx.moveTo(0, height / 2);
90
+ // Channel 0 is left, 1 is right
91
+ const leftChannel = channelData[0];
92
+ const isMono = channelData.length === 1;
93
+ const rightChannel = isMono ? leftChannel : channelData[1];
94
+ // Draw left channel in the top half of the canvas
95
+ let prevX = -1;
96
+ let prevY = 0;
97
+ for (let i = 0, len = leftChannel.length; i < len; i++) {
98
+ const x = Math.round((i / leftChannel.length) * width);
99
+ const y = Math.round(((1 - leftChannel[i]) * height) / 2);
100
+ if (x !== prevX || y > prevY) {
101
+ ctx.lineTo(x, y);
102
+ prevX = x;
103
+ prevY = y;
104
+ }
105
+ }
106
+ // Draw right channel in the bottom half of the canvas
107
+ prevX = -1;
108
+ prevY = 0;
109
+ for (let i = rightChannel.length - 1; i >= 0; i--) {
110
+ const x = Math.round((i / rightChannel.length) * width);
111
+ const y = Math.round(((1 + rightChannel[i]) * height) / 2);
112
+ if (x !== prevX || (isMono ? y < -prevY : y > prevY)) {
113
+ ctx.lineTo(x, y);
114
+ prevX = x;
115
+ prevY = y;
116
+ }
117
+ }
118
+ ctx.strokeStyle = ctx.fillStyle = this.options.waveColor;
119
+ ctx.stroke();
120
+ ctx.fill();
121
+ }
122
+ renderBarPeaks(channelData, width, height) {
123
+ var _a, _b;
124
+ const { devicePixelRatio } = window;
125
+ const { ctx } = this;
126
+ ctx.clearRect(0, 0, width, height);
127
+ const barWidthOption = this.options.barWidth || 1;
128
+ const barWidth = barWidthOption * devicePixelRatio;
129
+ const barGap = ((_a = this.options.barGap) !== null && _a !== void 0 ? _a : barWidthOption / 2) * devicePixelRatio;
130
+ const barRadius = (_b = this.options.barRadius) !== null && _b !== void 0 ? _b : 0;
131
+ const leftChannel = channelData[0];
132
+ const isMono = channelData.length === 1;
133
+ const rightChannel = isMono ? leftChannel : channelData[1];
134
+ const barCount = Math.floor(width / (barWidth + barGap));
135
+ const leftChannelBars = new Float32Array(barCount);
136
+ const rightChannelBars = new Float32Array(barCount);
137
+ const barIndexScale = barCount / leftChannel.length;
138
+ const leftChannelLength = leftChannel.length;
139
+ for (let i = 0; i < leftChannelLength; i++) {
140
+ const barIndex = Math.round(i * barIndexScale);
141
+ leftChannelBars[barIndex] = Math.max(leftChannelBars[barIndex], leftChannel[i]);
142
+ rightChannelBars[barIndex] = (isMono ? Math.min : Math.max)(rightChannelBars[barIndex], rightChannel[i]);
143
+ }
144
+ ctx.beginPath();
145
+ for (let i = 0; i < barCount; i++) {
146
+ const leftBarHeight = Math.max(1, Math.round((leftChannelBars[i] * height) / 2));
147
+ const rightBarHeight = Math.max(1, Math.round(Math.abs(rightChannelBars[i] * height) / 2));
148
+ ctx.roundRect(i * (barWidth + barGap), height / 2 - leftBarHeight, barWidth, leftBarHeight + rightBarHeight, barRadius);
149
+ }
150
+ ctx.fillStyle = this.options.waveColor;
151
+ ctx.fill();
152
+ }
153
+ render(channelData, duration, minPxPerSec = this.options.minPxPerSec) {
154
+ const { devicePixelRatio } = window;
155
+ const width = Math.max(this.container.clientWidth * devicePixelRatio, duration * minPxPerSec);
156
+ const { height } = this.options;
157
+ this.mainCanvas.width = width;
158
+ this.mainCanvas.height = height;
159
+ this.mainCanvas.style.width = Math.round(width / devicePixelRatio) + 'px';
160
+ const renderingFn = this.options.barWidth ? this.renderBarPeaks : this.renderLinePeaks;
161
+ renderingFn.call(this, channelData, width, height);
162
+ this.createProgressMask();
163
+ }
164
+ createProgressMask() {
165
+ const progressCtx = this.progressCanvas.getContext('2d', { desynchronized: true });
166
+ if (!progressCtx) {
167
+ throw new Error('Failed to get canvas context');
168
+ }
169
+ this.progressCanvas.width = this.mainCanvas.width;
170
+ this.progressCanvas.height = this.mainCanvas.height;
171
+ this.progressCanvas.style.width = this.mainCanvas.style.width;
172
+ this.progressCanvas.style.height = this.mainCanvas.style.height;
173
+ progressCtx.drawImage(this.mainCanvas, 0, 0);
174
+ progressCtx.globalCompositeOperation = 'source-in';
175
+ progressCtx.fillStyle = this.options.progressColor;
176
+ progressCtx.fillRect(0, 0, this.progressCanvas.width, this.progressCanvas.height);
177
+ }
178
+ renderProgress(progress, autoCenter = false) {
179
+ this.progressCanvas.style.clipPath = `inset(0 ${100 - progress * 100}% 0 0)`;
180
+ this.cursor.style.left = `${progress * 100}%`;
181
+ if (autoCenter) {
182
+ const center = this.container.clientWidth / 2;
183
+ const fullWidth = this.mainCanvas.clientWidth;
184
+ if (fullWidth * progress >= center) {
185
+ this.container.scrollLeft = fullWidth * progress - center;
186
+ }
187
+ }
188
+ }
189
+ getContainer() {
190
+ return this.shadowRoot.querySelector('.scroll');
191
+ }
192
+ }
193
+ export default Renderer;
@@ -0,0 +1,10 @@
1
+ import EventEmitter from './event-emitter.js';
2
+ type TimerEvents = {
3
+ tick: void;
4
+ };
5
+ declare class Timer extends EventEmitter<TimerEvents> {
6
+ private unsubscribe;
7
+ constructor();
8
+ destroy(): void;
9
+ }
10
+ export default Timer;
package/dist/timer.js ADDED
@@ -0,0 +1,17 @@
1
+ import EventEmitter from './event-emitter.js';
2
+ class Timer extends EventEmitter {
3
+ constructor() {
4
+ super();
5
+ this.unsubscribe = () => undefined;
6
+ this.unsubscribe = this.on('tick', () => {
7
+ requestAnimationFrame(() => {
8
+ this.emit('tick');
9
+ });
10
+ });
11
+ this.emit('tick');
12
+ }
13
+ destroy() {
14
+ this.unsubscribe();
15
+ }
16
+ }
17
+ export default Timer;
Binary file
@@ -0,0 +1,19 @@
1
+ import WaveSurfer from '/dist/index.js'
2
+
3
+ const wavesurfer = WaveSurfer.create({
4
+ container: document.body,
5
+ waveColor: 'rgb(200, 0, 200)',
6
+ progressColor: 'rgb(100, 0, 100)',
7
+ url: 'https://wavesurfer-js.org/example/media/demo.wav',
8
+
9
+ // Set a bar width
10
+ barWidth: 2,
11
+ // Optionally, specify the spacing between bars
12
+ barGap: 1,
13
+ // And the bar radius
14
+ barRadius: 2,
15
+ })
16
+
17
+ wavesurfer.on('seek', function () {
18
+ wavesurfer.play()
19
+ })
@@ -0,0 +1,8 @@
1
+ import WaveSurfer from '/dist/index.js'
2
+
3
+ const wavesurfer = WaveSurfer.create({
4
+ container: document.body,
5
+ waveColor: 'rgb(200, 0, 200)',
6
+ progressColor: 'rgb(100, 0, 100)',
7
+ url: 'https://wavesurfer-js.org/example/media/demo.wav'
8
+ })
@@ -0,0 +1,28 @@
1
+ import WaveSurfer from '/dist/index.js'
2
+
3
+ // Create a canvas gradient
4
+ const ctx = document.createElement('canvas').getContext('2d')
5
+ const gradient = ctx.createLinearGradient(0, 0, 0, 150)
6
+ gradient.addColorStop(0, 'rgb(200, 0, 200)')
7
+ gradient.addColorStop(0.7, 'rgb(100, 0, 100)')
8
+ gradient.addColorStop(1, 'rgb(0, 0, 0)')
9
+
10
+ // Default style with a gradient
11
+ WaveSurfer.create({
12
+ container: document.body,
13
+ waveColor: gradient,
14
+ height: 200,
15
+ progressColor: 'rgba(0, 0, 100, 0.5)',
16
+ url: 'https://wavesurfer-js.org/example/media/demo.wav'
17
+ })
18
+
19
+
20
+ // SoundCloud-style bars
21
+ WaveSurfer.create({
22
+ container: document.body,
23
+ waveColor: gradient,
24
+ height: 200,
25
+ barWidth: 2,
26
+ progressColor: 'rgba(0, 0, 100, 0.5)',
27
+ url: 'https://wavesurfer-js.org/example/media/demo.wav'
28
+ })
@@ -0,0 +1,63 @@
1
+ import WaveSurfer from '/dist/index.js'
2
+ import RegionsPlugin from '/dist/plugins/regions.js'
3
+
4
+ // Create an instance of WaveSurfer
5
+ const ws = WaveSurfer.create({
6
+ container: document.body,
7
+ waveColor: 'rgb(200, 0, 200)',
8
+ progressColor: 'rgb(100, 0, 100)',
9
+ url: 'https://wavesurfer-js.org/example/media/demo.wav'
10
+ })
11
+
12
+ // Initialize the Regions plugin
13
+ const wsRegions = ws.registerPlugin(RegionsPlugin)
14
+
15
+ // Give regions a random color when they are created
16
+ const random = (min, max) => Math.random() * (max - min) + min
17
+ wsRegions.on('region-created', ({ region }) => {
18
+ wsRegions.setRegionColor(region, `rgba(${random(0, 255)}, ${random(0, 255)}, ${random(0, 255)}, 0.5)`)
19
+ })
20
+
21
+ // Create some regions at specific time ranges
22
+ ws.on('ready', () => {
23
+ wsRegions.addRegionAtTime(4, 7, 'First region')
24
+ wsRegions.addRegionAtTime(9, 10, 'Middle region')
25
+ wsRegions.addRegionAtTime(12, 17, 'Last region')
26
+ })
27
+
28
+ // Loop a region on click
29
+ let loop = true
30
+ let activeRegion = null
31
+
32
+ wsRegions.on('region-clicked', ({ region }) => {
33
+ ws.seekTo(region.startTime)
34
+ ws.play()
35
+ activeRegion = region
36
+ })
37
+
38
+ // Track the time
39
+ ws.on('audioprocess', ({ currentTime }) => {
40
+ // When the end of the region is reached
41
+ if (activeRegion && ws.isPlaying() && (currentTime >= activeRegion.endTime)) {
42
+ if (loop) {
43
+ // If looping, jump to the start of the region
44
+ ws.seekTo(activeRegion.startTime)
45
+ } else {
46
+ // Otherwise, stop playing
47
+ ws.pause()
48
+ ws.seekTo(activeRegion.endTime)
49
+ activeRegion = null
50
+ }
51
+ }
52
+ })
53
+
54
+ // Toggle looping with a checkbox
55
+ const p = document.createElement('p')
56
+ p.innerHTML = `
57
+ <label>
58
+ <input type="checkbox" checked="${loop}" style="vertical-align: text-top" />
59
+ Loop regions on click
60
+ </label>
61
+ `
62
+ document.body.appendChild(p)
63
+ document.querySelector('input').onclick = (e) => { loop = e.target.checked }
@@ -0,0 +1,19 @@
1
+ import WaveSurfer from '/dist/index.js'
2
+
3
+ // Create a video element and add it to the DOM.
4
+ // The video can also be defined in HTML, in which case we just get it by a selector.
5
+ const video = document.createElement('video')
6
+ video.crossOrigin = 'anonymous'
7
+ video.controls = true
8
+ video.style.width = '100%'
9
+ video.src = 'https://wavesurfer-js.org/example/media/nasa.mp4'
10
+ document.body.appendChild(video)
11
+
12
+ // Initialize wavesurfer.js
13
+ const ws = WaveSurfer.create({
14
+ container: document.body,
15
+ waveColor: 'rgb(200, 0, 200)',
16
+ progressColor: 'rgb(100, 0, 100)',
17
+ // Pass the video element in the `media` param
18
+ media: document.querySelector('video'),
19
+ })