wavesurfer.js 7.11.0 → 7.12.0

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 (91) hide show
  1. package/README.md +1 -1
  2. package/dist/__tests__/memory-leaks.test.d.ts +7 -0
  3. package/dist/__tests__/memory-leaks.test.js +184 -0
  4. package/dist/__tests__/player.test.js +107 -0
  5. package/dist/__tests__/renderer-utils.test.d.ts +1 -0
  6. package/dist/__tests__/renderer-utils.test.js +373 -0
  7. package/dist/decoder.js +12 -4
  8. package/dist/draggable.d.ts +4 -0
  9. package/dist/draggable.js +20 -5
  10. package/dist/fetcher.js +14 -23
  11. package/dist/player.d.ts +21 -0
  12. package/dist/player.js +86 -0
  13. package/dist/plugins/envelope.cjs +1 -1
  14. package/dist/plugins/envelope.esm.js +1 -1
  15. package/dist/plugins/envelope.js +1 -1
  16. package/dist/plugins/envelope.min.js +1 -1
  17. package/dist/plugins/hover.cjs +1 -1
  18. package/dist/plugins/hover.d.ts +0 -3
  19. package/dist/plugins/hover.esm.js +1 -1
  20. package/dist/plugins/hover.js +1 -1
  21. package/dist/plugins/hover.min.js +1 -1
  22. package/dist/plugins/minimap.cjs +1 -1
  23. package/dist/plugins/minimap.esm.js +1 -1
  24. package/dist/plugins/minimap.js +1 -1
  25. package/dist/plugins/minimap.min.js +1 -1
  26. package/dist/plugins/record.cjs +1 -1
  27. package/dist/plugins/record.esm.js +1 -1
  28. package/dist/plugins/record.js +1 -1
  29. package/dist/plugins/record.min.js +1 -1
  30. package/dist/plugins/regions.cjs +1 -1
  31. package/dist/plugins/regions.esm.js +1 -1
  32. package/dist/plugins/regions.js +1 -1
  33. package/dist/plugins/regions.min.js +1 -1
  34. package/dist/plugins/timeline.cjs +1 -1
  35. package/dist/plugins/timeline.d.ts +1 -1
  36. package/dist/plugins/timeline.esm.js +1 -1
  37. package/dist/plugins/timeline.js +1 -1
  38. package/dist/plugins/timeline.min.js +1 -1
  39. package/dist/plugins/zoom.cjs +1 -1
  40. package/dist/plugins/zoom.d.ts +10 -1
  41. package/dist/plugins/zoom.esm.js +1 -1
  42. package/dist/plugins/zoom.js +1 -1
  43. package/dist/plugins/zoom.min.js +1 -1
  44. package/dist/reactive/__tests__/drag-stream.test.d.ts +1 -0
  45. package/dist/reactive/__tests__/drag-stream.test.js +199 -0
  46. package/dist/reactive/__tests__/event-stream-emitter.test.d.ts +1 -0
  47. package/dist/reactive/__tests__/event-stream-emitter.test.js +230 -0
  48. package/dist/reactive/__tests__/event-streams.test.d.ts +1 -0
  49. package/dist/reactive/__tests__/event-streams.test.js +278 -0
  50. package/dist/reactive/__tests__/media-event-bridge.test.d.ts +1 -0
  51. package/dist/reactive/__tests__/media-event-bridge.test.js +202 -0
  52. package/dist/reactive/__tests__/render-scheduler.test.d.ts +1 -0
  53. package/dist/reactive/__tests__/render-scheduler.test.js +216 -0
  54. package/dist/reactive/__tests__/scroll-stream.test.d.ts +1 -0
  55. package/dist/reactive/__tests__/scroll-stream.test.js +191 -0
  56. package/dist/reactive/__tests__/state-event-emitter.test.d.ts +1 -0
  57. package/dist/reactive/__tests__/state-event-emitter.test.js +234 -0
  58. package/dist/reactive/__tests__/store.test.d.ts +1 -0
  59. package/dist/reactive/__tests__/store.test.js +271 -0
  60. package/dist/reactive/drag-stream.d.ts +48 -0
  61. package/dist/reactive/drag-stream.js +146 -0
  62. package/dist/reactive/event-stream-emitter.d.ts +104 -0
  63. package/dist/reactive/event-stream-emitter.js +151 -0
  64. package/dist/reactive/event-streams.d.ts +65 -0
  65. package/dist/reactive/event-streams.js +146 -0
  66. package/dist/reactive/media-event-bridge.d.ts +51 -0
  67. package/dist/reactive/media-event-bridge.js +152 -0
  68. package/dist/reactive/render-scheduler.d.ts +43 -0
  69. package/dist/reactive/render-scheduler.js +75 -0
  70. package/dist/reactive/scroll-stream.d.ts +93 -0
  71. package/dist/reactive/scroll-stream.js +131 -0
  72. package/dist/reactive/state-event-emitter.d.ts +105 -0
  73. package/dist/reactive/state-event-emitter.js +213 -0
  74. package/dist/reactive/store.d.ts +66 -0
  75. package/dist/reactive/store.js +115 -0
  76. package/dist/renderer-utils.d.ts +126 -0
  77. package/dist/renderer-utils.js +252 -0
  78. package/dist/renderer.d.ts +4 -4
  79. package/dist/renderer.js +133 -197
  80. package/dist/state/__tests__/wavesurfer-state.test.d.ts +1 -0
  81. package/dist/state/__tests__/wavesurfer-state.test.js +268 -0
  82. package/dist/state/wavesurfer-state.d.ts +90 -0
  83. package/dist/state/wavesurfer-state.js +124 -0
  84. package/dist/types.d.ts +167 -1
  85. package/dist/wavesurfer.cjs +1 -1
  86. package/dist/wavesurfer.d.ts +18 -0
  87. package/dist/wavesurfer.esm.js +1 -1
  88. package/dist/wavesurfer.js +46 -8
  89. package/dist/wavesurfer.min.js +1 -1
  90. package/dist/webaudio.js +4 -0
  91. package/package.json +6 -6
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Reactive primitives for managing state in WaveSurfer
3
+ *
4
+ * This module provides signal-based reactivity similar to SolidJS signals.
5
+ * Signals are reactive values that notify subscribers when they change.
6
+ */
7
+ /**
8
+ * Create a reactive signal that notifies subscribers when its value changes
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const count = signal(0)
13
+ * count.subscribe(val => console.log('Count:', val))
14
+ * count.set(5) // Logs: Count: 5
15
+ * ```
16
+ */
17
+ export function signal(initialValue) {
18
+ let _value = initialValue;
19
+ const subscribers = new Set();
20
+ return {
21
+ get value() {
22
+ return _value;
23
+ },
24
+ set(newValue) {
25
+ // Only update and notify if value actually changed
26
+ if (!Object.is(_value, newValue)) {
27
+ _value = newValue;
28
+ subscribers.forEach((fn) => fn(_value));
29
+ }
30
+ },
31
+ update(fn) {
32
+ this.set(fn(_value));
33
+ },
34
+ subscribe(callback) {
35
+ subscribers.add(callback);
36
+ return () => subscribers.delete(callback);
37
+ },
38
+ };
39
+ }
40
+ /**
41
+ * Create a computed value that automatically updates when its dependencies change
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * const count = signal(0)
46
+ * const doubled = computed(() => count.value * 2, [count])
47
+ * console.log(doubled.value) // 0
48
+ * count.set(5)
49
+ * console.log(doubled.value) // 10
50
+ * ```
51
+ */
52
+ export function computed(fn, dependencies) {
53
+ const result = signal(fn());
54
+ // Subscribe to all dependencies immediately
55
+ // This ensures the computed value stays in sync even if no one is subscribed to it
56
+ dependencies.forEach((dep) => dep.subscribe(() => {
57
+ const newValue = fn();
58
+ // Update the result signal, which will notify our subscribers if value changed
59
+ if (!Object.is(result.value, newValue)) {
60
+ ;
61
+ result.set(newValue);
62
+ }
63
+ }));
64
+ // Return a read-only signal that proxies the result
65
+ return {
66
+ get value() {
67
+ return result.value;
68
+ },
69
+ subscribe(callback) {
70
+ // Just subscribe to result changes
71
+ return result.subscribe(callback);
72
+ },
73
+ };
74
+ }
75
+ /**
76
+ * Run a side effect automatically when dependencies change
77
+ *
78
+ * @param fn - Effect function. Can return a cleanup function.
79
+ * @param dependencies - Signals that trigger the effect when they change
80
+ * @returns Unsubscribe function that stops the effect and runs cleanup
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * const count = signal(0)
85
+ * effect(() => {
86
+ * console.log('Count is:', count.value)
87
+ * return () => console.log('Cleanup')
88
+ * }, [count])
89
+ * count.set(5) // Logs: Cleanup, Count is: 5
90
+ * ```
91
+ */
92
+ export function effect(fn, dependencies) {
93
+ let cleanup;
94
+ const run = () => {
95
+ // Run cleanup from previous execution
96
+ if (cleanup) {
97
+ cleanup();
98
+ cleanup = undefined;
99
+ }
100
+ // Run effect and capture new cleanup
101
+ cleanup = fn();
102
+ };
103
+ // Subscribe to all dependencies
104
+ const unsubscribes = dependencies.map((dep) => dep.subscribe(run));
105
+ // Run effect immediately
106
+ run();
107
+ // Return function that unsubscribes and runs cleanup
108
+ return () => {
109
+ if (cleanup) {
110
+ cleanup();
111
+ cleanup = undefined;
112
+ }
113
+ unsubscribes.forEach((unsub) => unsub());
114
+ };
115
+ }
@@ -0,0 +1,126 @@
1
+ import type { WaveSurferOptions } from './wavesurfer.js';
2
+ export type ChannelData = Array<Float32Array | number[]>;
3
+ export type BarSegment = {
4
+ x: number;
5
+ y: number;
6
+ width: number;
7
+ height: number;
8
+ };
9
+ export type LinePath = Array<{
10
+ x: number;
11
+ y: number;
12
+ }>;
13
+ export declare const DEFAULT_HEIGHT = 128;
14
+ export declare const MAX_CANVAS_WIDTH = 8000;
15
+ export declare const MAX_NODES = 10;
16
+ export declare function clampToUnit(value: number): number;
17
+ export declare function calculateBarRenderConfig({ width, height, length, options, pixelRatio, }: {
18
+ width: number;
19
+ height: number;
20
+ length: number;
21
+ options: WaveSurferOptions;
22
+ pixelRatio: number;
23
+ }): {
24
+ halfHeight: number;
25
+ barWidth: number;
26
+ barGap: number;
27
+ barRadius: number;
28
+ barMinHeight: number;
29
+ barIndexScale: number;
30
+ barSpacing: number;
31
+ };
32
+ export declare function calculateBarHeights({ maxTop, maxBottom, halfHeight, vScale, barMinHeight, barAlign, }: {
33
+ maxTop: number;
34
+ maxBottom: number;
35
+ halfHeight: number;
36
+ vScale: number;
37
+ barMinHeight?: number;
38
+ barAlign?: WaveSurferOptions['barAlign'];
39
+ }): {
40
+ topHeight: number;
41
+ totalHeight: number;
42
+ };
43
+ export declare function resolveBarYPosition({ barAlign, halfHeight, topHeight, totalHeight, canvasHeight, }: {
44
+ barAlign: WaveSurferOptions['barAlign'];
45
+ halfHeight: number;
46
+ topHeight: number;
47
+ totalHeight: number;
48
+ canvasHeight: number;
49
+ }): number;
50
+ export declare function calculateBarSegments({ channelData, barIndexScale, barSpacing, barWidth, halfHeight, vScale, canvasHeight, barAlign, barMinHeight, }: {
51
+ channelData: ChannelData;
52
+ barIndexScale: number;
53
+ barSpacing: number;
54
+ barWidth: number;
55
+ halfHeight: number;
56
+ vScale: number;
57
+ canvasHeight: number;
58
+ barAlign: WaveSurferOptions['barAlign'];
59
+ barMinHeight: number;
60
+ }): BarSegment[];
61
+ export declare function getRelativePointerPosition(rect: DOMRect, clientX: number, clientY: number): [number, number];
62
+ export declare function resolveChannelHeight({ optionsHeight, optionsSplitChannels, parentHeight, numberOfChannels, defaultHeight, }: {
63
+ optionsHeight?: WaveSurferOptions['height'];
64
+ optionsSplitChannels?: WaveSurferOptions['splitChannels'];
65
+ parentHeight: number;
66
+ numberOfChannels: number;
67
+ defaultHeight?: number;
68
+ }): number;
69
+ export declare function getPixelRatio(devicePixelRatio?: number): number;
70
+ export declare function shouldRenderBars(options: WaveSurferOptions): boolean;
71
+ export declare function resolveColorValue(color: WaveSurferOptions['waveColor'], devicePixelRatio: number): string | CanvasGradient;
72
+ export declare function calculateWaveformLayout({ duration, minPxPerSec, parentWidth, fillParent, pixelRatio, }: {
73
+ duration: number;
74
+ minPxPerSec?: number;
75
+ parentWidth: number;
76
+ fillParent?: boolean;
77
+ pixelRatio: number;
78
+ }): {
79
+ scrollWidth: number;
80
+ isScrollable: boolean;
81
+ useParentWidth: boolean;
82
+ width: number;
83
+ };
84
+ export declare function clampWidthToBarGrid(width: number, options: WaveSurferOptions): number;
85
+ export declare function calculateSingleCanvasWidth({ clientWidth, totalWidth, options, }: {
86
+ clientWidth: number;
87
+ totalWidth: number;
88
+ options: WaveSurferOptions;
89
+ }): number;
90
+ export declare function sliceChannelData({ channelData, offset, clampedWidth, totalWidth, }: {
91
+ channelData: ChannelData;
92
+ offset: number;
93
+ clampedWidth: number;
94
+ totalWidth: number;
95
+ }): ChannelData;
96
+ export declare function shouldClearCanvases(currentNodeCount: number): boolean;
97
+ export declare function getLazyRenderRange({ scrollLeft, totalWidth, numCanvases, }: {
98
+ scrollLeft: number;
99
+ totalWidth: number;
100
+ numCanvases: number;
101
+ }): number[];
102
+ export declare function calculateVerticalScale({ channelData, barHeight, normalize, maxPeak, }: {
103
+ channelData: ChannelData;
104
+ barHeight?: WaveSurferOptions['barHeight'];
105
+ normalize?: WaveSurferOptions['normalize'];
106
+ maxPeak?: WaveSurferOptions['maxPeak'];
107
+ }): number;
108
+ export declare function calculateLinePaths({ channelData, width, height, vScale, }: {
109
+ channelData: ChannelData;
110
+ width: number;
111
+ height: number;
112
+ vScale: number;
113
+ }): LinePath[];
114
+ /**
115
+ * @deprecated Use calculateScrollPercentages from './reactive/scroll-stream.js' instead.
116
+ * This function is maintained for backward compatibility but will be removed in a future version.
117
+ */
118
+ export declare function calculateScrollPercentages({ scrollLeft, clientWidth, scrollWidth, }: {
119
+ scrollLeft: number;
120
+ clientWidth: number;
121
+ scrollWidth: number;
122
+ }): {
123
+ startX: number;
124
+ endX: number;
125
+ };
126
+ export declare function roundToHalfAwayFromZero(value: number): number;
@@ -0,0 +1,252 @@
1
+ export const DEFAULT_HEIGHT = 128;
2
+ export const MAX_CANVAS_WIDTH = 8000;
3
+ export const MAX_NODES = 10;
4
+ export function clampToUnit(value) {
5
+ if (value < 0)
6
+ return 0;
7
+ if (value > 1)
8
+ return 1;
9
+ return value;
10
+ }
11
+ export function calculateBarRenderConfig({ width, height, length, options, pixelRatio, }) {
12
+ const halfHeight = height / 2;
13
+ const barWidth = options.barWidth ? options.barWidth * pixelRatio : 1;
14
+ const barGap = options.barGap ? options.barGap * pixelRatio : options.barWidth ? barWidth / 2 : 0;
15
+ const barRadius = options.barRadius || 0;
16
+ const barMinHeight = options.barMinHeight ? options.barMinHeight * pixelRatio : 0;
17
+ const spacing = barWidth + barGap || 1;
18
+ const barIndexScale = length > 0 ? width / spacing / length : 0;
19
+ return {
20
+ halfHeight,
21
+ barWidth,
22
+ barGap,
23
+ barRadius,
24
+ barMinHeight,
25
+ barIndexScale,
26
+ barSpacing: spacing,
27
+ };
28
+ }
29
+ export function calculateBarHeights({ maxTop, maxBottom, halfHeight, vScale, barMinHeight = 0, barAlign, }) {
30
+ let topHeight = Math.round(maxTop * halfHeight * vScale);
31
+ const bottomHeight = Math.round(maxBottom * halfHeight * vScale);
32
+ let totalHeight = topHeight + bottomHeight || 1;
33
+ if (totalHeight < barMinHeight) {
34
+ totalHeight = barMinHeight;
35
+ if (!barAlign) {
36
+ topHeight = totalHeight / 2;
37
+ }
38
+ }
39
+ return { topHeight, totalHeight };
40
+ }
41
+ export function resolveBarYPosition({ barAlign, halfHeight, topHeight, totalHeight, canvasHeight, }) {
42
+ if (barAlign === 'top')
43
+ return 0;
44
+ if (barAlign === 'bottom')
45
+ return canvasHeight - totalHeight;
46
+ return halfHeight - topHeight;
47
+ }
48
+ export function calculateBarSegments({ channelData, barIndexScale, barSpacing, barWidth, halfHeight, vScale, canvasHeight, barAlign, barMinHeight, }) {
49
+ const topChannel = channelData[0] || [];
50
+ const bottomChannel = channelData[1] || topChannel;
51
+ const length = topChannel.length;
52
+ const segments = [];
53
+ let prevX = 0;
54
+ let maxTop = 0;
55
+ let maxBottom = 0;
56
+ for (let i = 0; i <= length; i++) {
57
+ const x = Math.round(i * barIndexScale);
58
+ if (x > prevX) {
59
+ const { topHeight, totalHeight } = calculateBarHeights({
60
+ maxTop,
61
+ maxBottom,
62
+ halfHeight,
63
+ vScale,
64
+ barMinHeight,
65
+ barAlign,
66
+ });
67
+ const y = resolveBarYPosition({
68
+ barAlign,
69
+ halfHeight,
70
+ topHeight,
71
+ totalHeight,
72
+ canvasHeight,
73
+ });
74
+ segments.push({
75
+ x: prevX * barSpacing,
76
+ y,
77
+ width: barWidth,
78
+ height: totalHeight,
79
+ });
80
+ prevX = x;
81
+ maxTop = 0;
82
+ maxBottom = 0;
83
+ }
84
+ const magnitudeTop = Math.abs(topChannel[i] || 0);
85
+ const magnitudeBottom = Math.abs(bottomChannel[i] || 0);
86
+ if (magnitudeTop > maxTop)
87
+ maxTop = magnitudeTop;
88
+ if (magnitudeBottom > maxBottom)
89
+ maxBottom = magnitudeBottom;
90
+ }
91
+ return segments;
92
+ }
93
+ export function getRelativePointerPosition(rect, clientX, clientY) {
94
+ const x = clientX - rect.left;
95
+ const y = clientY - rect.top;
96
+ const relativeX = x / rect.width;
97
+ const relativeY = y / rect.height;
98
+ return [relativeX, relativeY];
99
+ }
100
+ export function resolveChannelHeight({ optionsHeight, optionsSplitChannels, parentHeight, numberOfChannels, defaultHeight = DEFAULT_HEIGHT, }) {
101
+ if (optionsHeight == null)
102
+ return defaultHeight;
103
+ const numericHeight = Number(optionsHeight);
104
+ if (!isNaN(numericHeight))
105
+ return numericHeight;
106
+ if (optionsHeight === 'auto') {
107
+ const height = parentHeight || defaultHeight;
108
+ if (optionsSplitChannels === null || optionsSplitChannels === void 0 ? void 0 : optionsSplitChannels.every((channel) => !channel.overlay)) {
109
+ return height / numberOfChannels;
110
+ }
111
+ return height;
112
+ }
113
+ return defaultHeight;
114
+ }
115
+ export function getPixelRatio(devicePixelRatio) {
116
+ return Math.max(1, devicePixelRatio || 1);
117
+ }
118
+ export function shouldRenderBars(options) {
119
+ return Boolean(options.barWidth || options.barGap || options.barAlign);
120
+ }
121
+ export function resolveColorValue(color, devicePixelRatio) {
122
+ if (!Array.isArray(color))
123
+ return color || '';
124
+ if (color.length === 0)
125
+ return '#999';
126
+ if (color.length < 2)
127
+ return color[0] || '';
128
+ const canvasElement = document.createElement('canvas');
129
+ const ctx = canvasElement.getContext('2d');
130
+ const gradientHeight = canvasElement.height * devicePixelRatio;
131
+ const gradient = ctx.createLinearGradient(0, 0, 0, gradientHeight || devicePixelRatio);
132
+ const colorStopPercentage = 1 / (color.length - 1);
133
+ color.forEach((value, index) => {
134
+ gradient.addColorStop(index * colorStopPercentage, value);
135
+ });
136
+ return gradient;
137
+ }
138
+ export function calculateWaveformLayout({ duration, minPxPerSec = 0, parentWidth, fillParent, pixelRatio, }) {
139
+ const scrollWidth = Math.ceil(duration * minPxPerSec);
140
+ const isScrollable = scrollWidth > parentWidth;
141
+ const useParentWidth = Boolean(fillParent && !isScrollable);
142
+ const width = (useParentWidth ? parentWidth : scrollWidth) * pixelRatio;
143
+ return {
144
+ scrollWidth,
145
+ isScrollable,
146
+ useParentWidth,
147
+ width,
148
+ };
149
+ }
150
+ export function clampWidthToBarGrid(width, options) {
151
+ if (!shouldRenderBars(options))
152
+ return width;
153
+ const barWidth = options.barWidth || 0.5;
154
+ const barGap = options.barGap || barWidth / 2;
155
+ const totalBarWidth = barWidth + barGap;
156
+ if (totalBarWidth === 0)
157
+ return width;
158
+ return Math.floor(width / totalBarWidth) * totalBarWidth;
159
+ }
160
+ export function calculateSingleCanvasWidth({ clientWidth, totalWidth, options, }) {
161
+ const baseWidth = Math.min(MAX_CANVAS_WIDTH, clientWidth, totalWidth);
162
+ return clampWidthToBarGrid(baseWidth, options);
163
+ }
164
+ export function sliceChannelData({ channelData, offset, clampedWidth, totalWidth, }) {
165
+ return channelData.map((channel) => {
166
+ const start = Math.floor((offset / totalWidth) * channel.length);
167
+ const end = Math.floor(((offset + clampedWidth) / totalWidth) * channel.length);
168
+ return channel.slice(start, end);
169
+ });
170
+ }
171
+ export function shouldClearCanvases(currentNodeCount) {
172
+ return currentNodeCount > MAX_NODES;
173
+ }
174
+ export function getLazyRenderRange({ scrollLeft, totalWidth, numCanvases, }) {
175
+ if (totalWidth === 0)
176
+ return [0];
177
+ const viewPosition = scrollLeft / totalWidth;
178
+ const startCanvas = Math.floor(viewPosition * numCanvases);
179
+ return [startCanvas - 1, startCanvas, startCanvas + 1];
180
+ }
181
+ export function calculateVerticalScale({ channelData, barHeight, normalize, maxPeak, }) {
182
+ var _a;
183
+ const baseScale = barHeight || 1;
184
+ if (!normalize)
185
+ return baseScale;
186
+ const firstChannel = channelData[0];
187
+ if (!firstChannel || firstChannel.length === 0)
188
+ return baseScale;
189
+ // Use fixed max peak if provided, otherwise calculate from data
190
+ let max = maxPeak !== null && maxPeak !== void 0 ? maxPeak : 0;
191
+ if (!maxPeak) {
192
+ for (let i = 0; i < firstChannel.length; i++) {
193
+ const value = (_a = firstChannel[i]) !== null && _a !== void 0 ? _a : 0;
194
+ const magnitude = Math.abs(value);
195
+ if (magnitude > max)
196
+ max = magnitude;
197
+ }
198
+ }
199
+ if (!max)
200
+ return baseScale;
201
+ return baseScale / max;
202
+ }
203
+ export function calculateLinePaths({ channelData, width, height, vScale, }) {
204
+ const halfHeight = height / 2;
205
+ const primaryChannel = channelData[0] || [];
206
+ const secondaryChannel = channelData[1] || primaryChannel;
207
+ const channels = [primaryChannel, secondaryChannel];
208
+ return channels.map((channel, index) => {
209
+ const length = channel.length;
210
+ const hScale = length ? width / length : 0;
211
+ const baseY = halfHeight;
212
+ const direction = index === 0 ? -1 : 1;
213
+ const path = [{ x: 0, y: baseY }];
214
+ let prevX = 0;
215
+ let max = 0;
216
+ for (let i = 0; i <= length; i++) {
217
+ const x = Math.round(i * hScale);
218
+ if (x > prevX) {
219
+ const heightDelta = Math.round(max * halfHeight * vScale) || 1;
220
+ const y = baseY + heightDelta * direction;
221
+ path.push({ x: prevX, y });
222
+ prevX = x;
223
+ max = 0;
224
+ }
225
+ const value = Math.abs(channel[i] || 0);
226
+ if (value > max)
227
+ max = value;
228
+ }
229
+ path.push({ x: prevX, y: baseY });
230
+ return path;
231
+ });
232
+ }
233
+ /**
234
+ * @deprecated Use calculateScrollPercentages from './reactive/scroll-stream.js' instead.
235
+ * This function is maintained for backward compatibility but will be removed in a future version.
236
+ */
237
+ export function calculateScrollPercentages({ scrollLeft, clientWidth, scrollWidth, }) {
238
+ if (scrollWidth === 0) {
239
+ return { startX: 0, endX: 1 };
240
+ }
241
+ const startX = scrollLeft / scrollWidth;
242
+ const endX = (scrollLeft + clientWidth) / scrollWidth;
243
+ return {
244
+ startX: Math.max(0, Math.min(1, startX)),
245
+ endX: Math.max(0, Math.min(1, endX)),
246
+ };
247
+ }
248
+ export function roundToHalfAwayFromZero(value) {
249
+ const scaled = value * 2;
250
+ const rounded = scaled < 0 ? Math.floor(scaled) : Math.ceil(scaled);
251
+ return rounded / 2;
252
+ }
@@ -9,10 +9,9 @@ type RendererEvents = {
9
9
  scroll: [relativeStart: number, relativeEnd: number, scrollLeft: number, scrollRight: number];
10
10
  render: [];
11
11
  rendered: [];
12
+ resize: [];
12
13
  };
13
14
  declare class Renderer extends EventEmitter<RendererEvents> {
14
- private static MAX_CANVAS_WIDTH;
15
- private static MAX_NODES;
16
15
  private options;
17
16
  private parent;
18
17
  private container;
@@ -29,13 +28,13 @@ declare class Renderer extends EventEmitter<RendererEvents> {
29
28
  private isDragging;
30
29
  private subscriptions;
31
30
  private unsubscribeOnScroll;
32
- private dragUnsubscribe;
31
+ private dragStream;
32
+ private scrollStream;
33
33
  constructor(options: WaveSurferOptions, audioElement?: HTMLElement);
34
34
  private parentFromOptionsContainer;
35
35
  private initEvents;
36
36
  private onContainerResize;
37
37
  private initDrag;
38
- private getHeight;
39
38
  private initHtml;
40
39
  /** Wavesurfer itself calls this method. Do not call it manually. */
41
40
  setOptions(options: WaveSurferOptions): void;
@@ -46,6 +45,7 @@ declare class Renderer extends EventEmitter<RendererEvents> {
46
45
  setScrollPercentage(percent: number): void;
47
46
  destroy(): void;
48
47
  private createDelay;
48
+ private getHeight;
49
49
  private convertColorValues;
50
50
  private getPixelRatio;
51
51
  private renderBarWaveform;