@checksub_team/peaks_timeline 2.1.0 → 2.2.0-alpha.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checksub_team/peaks_timeline",
3
- "version": "2.1.0",
3
+ "version": "2.2.0-alpha.0",
4
4
  "description": "JavaScript UI component for displaying audio waveforms",
5
5
  "main": "./peaks.js",
6
6
  "types": "./peaks.js.d.ts",
package/peaks.js CHANGED
@@ -16723,7 +16723,7 @@ module.exports = function (Utils, Konva) {
16723
16723
  PlayheadLayer.prototype._createPlayheadText = function (color) {
16724
16724
  this._playheadText = new Konva.Text({
16725
16725
  x: 13,
16726
- y: 3,
16726
+ y: 10,
16727
16727
  text: '00:00:00',
16728
16728
  fontSize: 11,
16729
16729
  fontFamily: 'sans-serif',
@@ -16737,21 +16737,28 @@ module.exports = function (Utils, Konva) {
16737
16737
  this._syncPlayhead(time);
16738
16738
  };
16739
16739
  PlayheadLayer.prototype._syncPlayhead = function (time) {
16740
- if (this._time === time) {
16741
- return;
16740
+ const pixelHasChanged = this._updatePlayheadPixel(time);
16741
+ const timeHasChanged = this._time !== time;
16742
+ if (timeHasChanged) {
16743
+ this._updateActiveSegmentsAndSources(time);
16744
+ this._updatePlayheadText(time);
16742
16745
  }
16743
16746
  this._time = time;
16744
- this._updatePlayheadPixel(time);
16745
- this._updateActiveSegmentsAndSources(time);
16746
- this._updatePlayheadText(time);
16747
- this._playheadLayer.batchDraw();
16747
+ if (pixelHasChanged || timeHasChanged) {
16748
+ this._playheadLayer.batchDraw();
16749
+ }
16748
16750
  };
16749
16751
  PlayheadLayer.prototype._updatePlayheadPixel = function (time) {
16750
16752
  const pixelIndex = this._view.timeToPixels(time);
16751
16753
  const frameOffset = this._view.timeToPixels(this._view.getTimeOffset());
16752
16754
  this._playheadPixel = pixelIndex;
16753
16755
  const playheadX = this._playheadPixel - frameOffset;
16754
- this._playheadGroup.setAttr('x', playheadX);
16756
+ const oldX = this._playheadGroup.getAttr('x');
16757
+ const shouldUpdateX = oldX !== playheadX;
16758
+ if (shouldUpdateX) {
16759
+ this._playheadGroup.setAttr('x', playheadX);
16760
+ }
16761
+ return shouldUpdateX;
16755
16762
  };
16756
16763
  PlayheadLayer.prototype._updateActiveSegmentsAndSources = function (time) {
16757
16764
  const lineGroups = this._lines.getLineGroupsById();
@@ -16795,7 +16802,6 @@ module.exports = function (Utils, Konva) {
16795
16802
  PlayheadLayer.prototype._updatePlayheadText = function (time) {
16796
16803
  var text = Utils.formatTime(time, false);
16797
16804
  this._playheadText.setText(text);
16798
- this._peaks.emit('playhead.moved', this._playheadText.getAbsolutePosition().x, this._playheadText.width());
16799
16805
  };
16800
16806
  PlayheadLayer.prototype.getPlayheadOffset = function () {
16801
16807
  return this._playheadPixel - this._view.getFrameOffset();
@@ -19822,6 +19828,7 @@ module.exports = function (Utils, Konva) {
19822
19828
  WaveformShape.prototype._sceneFunc = function (context) {
19823
19829
  var width = this._view.getWidth();
19824
19830
  var waveformData = this._layer.getLoadedData(this._url).data;
19831
+ var targetSpeed = this._source.targetSpeed || 1;
19825
19832
  var startPixel = 0, startOffset = 0;
19826
19833
  if (this._source) {
19827
19834
  startPixel = this._view.timeToPixels(this._source.mediaStartTime) + Math.max(this._view.getFrameOffset() - this._view.timeToPixels(this._source.startTime), 0);
@@ -19829,7 +19836,9 @@ module.exports = function (Utils, Konva) {
19829
19836
  }
19830
19837
  var endPixel = width;
19831
19838
  if (this._source) {
19832
- endPixel = Math.min(this._view.timeToPixels(this._source.mediaEndTime) - Math.max(this._view.timeToPixels(this._source.endTime) - this._view.getFrameOffset() - this._view.getWidth(), 0), waveformData.length);
19839
+ var effectiveMediaDuration = (this._source.endTime - this._source.startTime) * targetSpeed;
19840
+ var effectiveMediaEndTime = Math.min(this._source.mediaStartTime + effectiveMediaDuration, this._source.duration || Infinity);
19841
+ endPixel = Math.min(this._view.timeToPixels(effectiveMediaEndTime) - Math.max(this._view.timeToPixels(this._source.endTime) - this._view.getFrameOffset() - this._view.getWidth(), 0), waveformData.length);
19833
19842
  }
19834
19843
  this._drawWaveform(context, waveformData, startPixel, startOffset, endPixel, this._height);
19835
19844
  };
@@ -19846,16 +19855,19 @@ module.exports = function (Utils, Konva) {
19846
19855
  }
19847
19856
  };
19848
19857
  WaveformShape.prototype._drawChannel = function (context, channel, startPixel, startOffset, endPixel, top, height) {
19849
- var x, val;
19858
+ var x, val, displayX;
19850
19859
  var amplitudeScale = this._view.getAmplitudeScale();
19860
+ var targetSpeed = this._source && this._source.targetSpeed ? this._source.targetSpeed : 1;
19851
19861
  context.beginPath();
19852
19862
  for (x = Math.floor(startPixel); x < Math.ceil(endPixel); x++) {
19853
19863
  val = channel.min_sample(x);
19854
- context.lineTo(x - startOffset + 0.5, top + scaleY(val, height, amplitudeScale) + 0.5);
19864
+ displayX = (x - startPixel) / targetSpeed + startPixel;
19865
+ context.lineTo(displayX - startOffset + 0.5, top + scaleY(val, height, amplitudeScale) + 0.5);
19855
19866
  }
19856
19867
  for (x = Math.ceil(endPixel) - 1; x >= Math.floor(startPixel); x--) {
19857
19868
  val = channel.max_sample(x);
19858
- context.lineTo(x - startOffset + 0.5, top + scaleY(val, height, amplitudeScale) + 0.5);
19869
+ displayX = (x - startPixel) / targetSpeed + startPixel;
19870
+ context.lineTo(displayX - startOffset + 0.5, top + scaleY(val, height, amplitudeScale) + 0.5);
19859
19871
  }
19860
19872
  context.closePath();
19861
19873
  context.fillShape(this);
@@ -20985,8 +20997,13 @@ module.exports = function (Utils) {
20985
20997
  } else if (!Utils.isBoolean(options.loading)) {
20986
20998
  throw new TypeError('peaks.sources.' + context + ': loading must be a boolean');
20987
20999
  }
21000
+ if (Utils.isNullOrUndefined(options.targetSpeed)) {
21001
+ options.targetSpeed = 1;
21002
+ } else if (!Utils.isNumber(options.targetSpeed) || options.targetSpeed <= 0) {
21003
+ throw new TypeError('peaks.sources.' + context + ': targetSpeed must be a positive number');
21004
+ }
20988
21005
  }
20989
- function Source(peaks, id, lineId, originId, elementId, title, titleAlignments, url, previewUrl, binaryUrl, kind, subkind, duration, startTime, endTime, mediaStartTime, mediaEndTime, color, backgroundColor, hoverBackgroundColor, selectedBackgroundColor, borderColor, selectedBorderColor, warningColor, warningWidth, volumeSliderColor, volumeSliderWidth, volumeSliderDraggingWidth, textFont, textFontSize, textColor, textBackgroundColor, textPosition, textAutoScroll, borderWidth, borderRadius, wrapped, draggable, orderable, resizable, cuttable, deletable, wrapping, previewHeight, binaryHeight, indicators, markers, buttons, markerColor, markerWidth, volume, volumeRange, loading, ...customParams) {
21006
+ function Source(peaks, id, lineId, originId, elementId, title, titleAlignments, url, previewUrl, binaryUrl, kind, subkind, duration, startTime, endTime, mediaStartTime, mediaEndTime, color, backgroundColor, hoverBackgroundColor, selectedBackgroundColor, borderColor, selectedBorderColor, warningColor, warningWidth, volumeSliderColor, volumeSliderWidth, volumeSliderDraggingWidth, textFont, textFontSize, textColor, textBackgroundColor, textPosition, textAutoScroll, borderWidth, borderRadius, wrapped, draggable, orderable, resizable, cuttable, deletable, wrapping, previewHeight, binaryHeight, indicators, markers, buttons, markerColor, markerWidth, volume, volumeRange, loading, targetSpeed, ...customParams) {
20990
21007
  var opts = {
20991
21008
  title: title,
20992
21009
  titleAlignments: titleAlignments,
@@ -21035,7 +21052,8 @@ module.exports = function (Utils) {
21035
21052
  markerWidth: markerWidth,
21036
21053
  volume: volume,
21037
21054
  volumeRange: volumeRange,
21038
- loading: loading
21055
+ loading: loading,
21056
+ targetSpeed: targetSpeed
21039
21057
  };
21040
21058
  validateSource(peaks, opts, 'add()');
21041
21059
  this._peaks = peaks;
@@ -21091,6 +21109,7 @@ module.exports = function (Utils) {
21091
21109
  this._volume = opts.volume;
21092
21110
  this._volumeRange = opts.volumeRange;
21093
21111
  this._loading = opts.loading;
21112
+ this._targetSpeed = opts.targetSpeed;
21094
21113
  this._minSize = peaks.options.minSourceSize;
21095
21114
  this._selected = false;
21096
21115
  for (var i = 0; i < customParams.length; i += 2) {
@@ -21515,6 +21534,12 @@ module.exports = function (Utils) {
21515
21534
  set: function (selected) {
21516
21535
  this._selected = selected;
21517
21536
  }
21537
+ },
21538
+ targetSpeed: {
21539
+ enumerable: true,
21540
+ get: function () {
21541
+ return this._targetSpeed;
21542
+ }
21518
21543
  }
21519
21544
  });
21520
21545
  Source.prototype.updateTimes = function (newStartTime, newEndTime) {
@@ -21650,7 +21675,8 @@ module.exports = function (Utils) {
21650
21675
  markerWidth: this.markerWidth,
21651
21676
  volume: this.volume,
21652
21677
  volumeRange: this.volumeRange,
21653
- loading: this.loading
21678
+ loading: this.loading,
21679
+ targetSpeed: this.targetSpeed
21654
21680
  };
21655
21681
  Utils.extend(opts, options);
21656
21682
  validateSource(this._peaks, opts, 'update()');
@@ -21700,6 +21726,7 @@ module.exports = function (Utils) {
21700
21726
  this._volume = opts.volume;
21701
21727
  this._volumeRange = opts.volumeRange;
21702
21728
  this._loading = opts.loading;
21729
+ this._targetSpeed = opts.targetSpeed;
21703
21730
  if (options && typeof options === 'object') {
21704
21731
  for (var key in options) {
21705
21732
  if (Object.prototype.hasOwnProperty.call(options, key) && key.startsWith('custom_')) {
@@ -22013,7 +22040,8 @@ module.exports = function (Source, Utils) {
22013
22040
  markerWidth: sourceToCut.markerWidth,
22014
22041
  volume: sourceToCut.volume,
22015
22042
  volumeRange: sourceToCut.volumeRange,
22016
- loading: sourceToCut.loading
22043
+ loading: sourceToCut.loading,
22044
+ targetSpeed: sourceToCut.targetSpeed
22017
22045
  };
22018
22046
  for (var key in sourceToCut) {
22019
22047
  if (key.startsWith('custom_')) {
@@ -22043,7 +22071,7 @@ module.exports = function (Source, Utils) {
22043
22071
  customParams.push(key, value);
22044
22072
  }
22045
22073
  });
22046
- var source = new Source(this._peaks, options.id || this._getNextSourceId(), options.lineId, options.originId, options.elementId, options.title, options.titleAlignments, options.url, options.previewUrl, options.binaryUrl, options.kind, options.subkind, options.duration, options.startTime, options.endTime, options.mediaStartTime, options.mediaEndTime, options.color, options.backgroundColor, options.hoverBackgroundColor, options.selectedBackgroundColor, options.borderColor, options.selectedBorderColor, options.warningColor, options.warningWidth, options.volumeSliderColor, options.volumeSliderWidth, options.volumeSliderDraggingWidth, options.textFont, options.textFontSize, options.textColor, options.textBackgroundColor, options.textPosition, options.textAutoScroll, options.borderWidth, options.borderRadius, options.wrapped, options.draggable, options.orderable, options.resizable, options.cuttable, options.deletable, options.wrapping, options.previewHeight, options.binaryHeight, options.indicators, options.markers, options.buttons, options.markerColor, options.markerWidth, options.volume, options.volumeRange, options.loading, ...customParams);
22074
+ var source = new Source(this._peaks, options.id || this._getNextSourceId(), options.lineId, options.originId, options.elementId, options.title, options.titleAlignments, options.url, options.previewUrl, options.binaryUrl, options.kind, options.subkind, options.duration, options.startTime, options.endTime, options.mediaStartTime, options.mediaEndTime, options.color, options.backgroundColor, options.hoverBackgroundColor, options.selectedBackgroundColor, options.borderColor, options.selectedBorderColor, options.warningColor, options.warningWidth, options.volumeSliderColor, options.volumeSliderWidth, options.volumeSliderDraggingWidth, options.textFont, options.textFontSize, options.textColor, options.textBackgroundColor, options.textPosition, options.textAutoScroll, options.borderWidth, options.borderRadius, options.wrapped, options.draggable, options.orderable, options.resizable, options.cuttable, options.deletable, options.wrapping, options.previewHeight, options.binaryHeight, options.indicators, options.markers, options.buttons, options.markerColor, options.markerWidth, options.volume, options.volumeRange, options.loading, options.targetSpeed, ...customParams);
22047
22075
  return source;
22048
22076
  };
22049
22077
  SourceHandler.prototype.getSources = function () {
@@ -196,7 +196,7 @@ define([
196
196
  // Create with default y, the real value is set in fitToView().
197
197
  this._playheadText = new Konva.Text({
198
198
  x: 13,
199
- y: 3,
199
+ y: 10,
200
200
  text: '00:00:00',
201
201
  fontSize: 11,
202
202
  fontFamily: 'sans-serif',
@@ -226,15 +226,18 @@ define([
226
226
  */
227
227
 
228
228
  PlayheadLayer.prototype._syncPlayhead = function(time) {
229
- if (this._time === time) {
230
- return;
229
+ const pixelHasChanged = this._updatePlayheadPixel(time);
230
+ const timeHasChanged = this._time !== time;
231
+
232
+ if (timeHasChanged) {
233
+ this._updateActiveSegmentsAndSources(time);
234
+ this._updatePlayheadText(time);
231
235
  }
232
236
 
233
237
  this._time = time;
234
- this._updatePlayheadPixel(time);
235
- this._updateActiveSegmentsAndSources(time);
236
- this._updatePlayheadText(time);
237
- this._playheadLayer.batchDraw();
238
+ if (pixelHasChanged || timeHasChanged) {
239
+ this._playheadLayer.batchDraw();
240
+ }
238
241
  };
239
242
 
240
243
  /**
@@ -249,8 +252,15 @@ define([
249
252
 
250
253
  this._playheadPixel = pixelIndex;
251
254
  const playheadX = this._playheadPixel - frameOffset;
255
+ const oldX = this._playheadGroup.getAttr('x');
256
+
257
+ const shouldUpdateX = oldX !== playheadX;
258
+
259
+ if (shouldUpdateX) {
260
+ this._playheadGroup.setAttr('x', playheadX);
261
+ }
252
262
 
253
- this._playheadGroup.setAttr('x', playheadX);
263
+ return shouldUpdateX;
254
264
  };
255
265
 
256
266
  /**
@@ -328,11 +338,6 @@ define([
328
338
  var text = Utils.formatTime(time, false);
329
339
 
330
340
  this._playheadText.setText(text);
331
- this._peaks.emit(
332
- 'playhead.moved',
333
- this._playheadText.getAbsolutePosition().x,
334
- this._playheadText.width()
335
- );
336
341
  };
337
342
 
338
343
  /**
@@ -72,6 +72,7 @@ define(['../utils', 'konva'], function(Utils, Konva) {
72
72
  WaveformShape.prototype._sceneFunc = function(context) {
73
73
  var width = this._view.getWidth();
74
74
  var waveformData = this._layer.getLoadedData(this._url).data;
75
+ var targetSpeed = this._source.targetSpeed || 1.0;
75
76
 
76
77
  var startPixel = 0, startOffset = 0;
77
78
 
@@ -87,8 +88,16 @@ define(['../utils', 'konva'], function(Utils, Konva) {
87
88
  var endPixel = width;
88
89
 
89
90
  if (this._source) {
91
+ // Calculate the effective media end time based on targetSpeed
92
+ // If speed is 2.0, we can fit 2x more audio in the same visual space
93
+ var effectiveMediaDuration = (this._source.endTime - this._source.startTime) * targetSpeed;
94
+ var effectiveMediaEndTime = Math.min(
95
+ this._source.mediaStartTime + effectiveMediaDuration,
96
+ this._source.duration || Infinity
97
+ );
98
+
90
99
  endPixel = Math.min(
91
- this._view.timeToPixels(this._source.mediaEndTime) - Math.max(
100
+ this._view.timeToPixels(effectiveMediaEndTime) - Math.max(
92
101
  this._view.timeToPixels(this._source.endTime)
93
102
  - this._view.getFrameOffset()
94
103
  - this._view.getWidth(),
@@ -151,22 +160,30 @@ define(['../utils', 'konva'], function(Utils, Konva) {
151
160
 
152
161
  WaveformShape.prototype._drawChannel = function(context, channel,
153
162
  startPixel, startOffset, endPixel, top, height) {
154
- var x, val;
163
+ var x, val, displayX;
155
164
 
156
165
  var amplitudeScale = this._view.getAmplitudeScale();
166
+ var targetSpeed = this._source && this._source.targetSpeed ? this._source.targetSpeed : 1.0;
157
167
 
158
168
  context.beginPath();
159
169
 
160
170
  for (x = Math.floor(startPixel); x < Math.ceil(endPixel); x++) {
161
171
  val = channel.min_sample(x);
162
172
 
163
- context.lineTo(x - startOffset + 0.5, top + scaleY(val, height, amplitudeScale) + 0.5);
173
+ // Scale the x position by dividing by targetSpeed to compress/expand the waveform
174
+ // targetSpeed = 2.0 means the waveform is drawn at half width (compressed)
175
+ // targetSpeed = 0.5 means the waveform is drawn at double width (expanded)
176
+ displayX = (x - startPixel) / targetSpeed + startPixel;
177
+
178
+ context.lineTo(displayX - startOffset + 0.5, top + scaleY(val, height, amplitudeScale) + 0.5);
164
179
  }
165
180
 
166
181
  for (x = Math.ceil(endPixel) - 1; x >= Math.floor(startPixel); x--) {
167
182
  val = channel.max_sample(x);
168
183
 
169
- context.lineTo(x - startOffset + 0.5, top + scaleY(val, height, amplitudeScale) + 0.5);
184
+ displayX = (x - startPixel) / targetSpeed + startPixel;
185
+
186
+ context.lineTo(displayX - startOffset + 0.5, top + scaleY(val, height, amplitudeScale) + 0.5);
170
187
  }
171
188
 
172
189
  context.closePath();
@@ -368,6 +368,13 @@ define([
368
368
  else if (!Utils.isBoolean(options.loading)) {
369
369
  throw new TypeError('peaks.sources.' + context + ': loading must be a boolean');
370
370
  }
371
+
372
+ if (Utils.isNullOrUndefined(options.targetSpeed)) {
373
+ options.targetSpeed = 1.0;
374
+ }
375
+ else if (!Utils.isNumber(options.targetSpeed) || options.targetSpeed <= 0) {
376
+ throw new TypeError('peaks.sources.' + context + ': targetSpeed must be a positive number');
377
+ }
371
378
  }
372
379
 
373
380
  /**
@@ -427,6 +434,8 @@ define([
427
434
  * @param {Number} volume Current volume level of the source.
428
435
  * @param {Array<Number>} volumeRange Array containing min and max volume values.
429
436
  * @param {Boolean} loading If <code>true</code> the source is currently loading.
437
+ * @param {Number} targetSpeed The playback speed multiplier for the source (default 1.0).
438
+ * Values > 1.0 speed up playback (compress waveform), values < 1.0 slow down (expand waveform).
430
439
  */
431
440
 
432
441
  function Source(peaks, id, lineId, originId, elementId, title, titleAlignments, url, previewUrl, binaryUrl, kind,
@@ -435,7 +444,7 @@ define([
435
444
  volumeSliderWidth, volumeSliderDraggingWidth, textFont, textFontSize, textColor, textBackgroundColor, textPosition,
436
445
  textAutoScroll, borderWidth, borderRadius, wrapped, draggable, orderable, resizable,
437
446
  cuttable, deletable, wrapping, previewHeight, binaryHeight, indicators, markers, buttons, markerColor,
438
- markerWidth, volume, volumeRange, loading, ...customParams) {
447
+ markerWidth, volume, volumeRange, loading, targetSpeed, ...customParams) {
439
448
  var opts = {
440
449
  title: title,
441
450
  titleAlignments: titleAlignments,
@@ -484,7 +493,8 @@ define([
484
493
  markerWidth: markerWidth,
485
494
  volume: volume,
486
495
  volumeRange: volumeRange,
487
- loading: loading
496
+ loading: loading,
497
+ targetSpeed: targetSpeed
488
498
  };
489
499
 
490
500
  validateSource(peaks, opts, 'add()');
@@ -542,6 +552,7 @@ define([
542
552
  this._volume = opts.volume;
543
553
  this._volumeRange = opts.volumeRange;
544
554
  this._loading = opts.loading;
555
+ this._targetSpeed = opts.targetSpeed;
545
556
  this._minSize = peaks.options.minSourceSize;
546
557
  this._selected = false;
547
558
 
@@ -997,6 +1008,12 @@ define([
997
1008
  set: function(selected) {
998
1009
  this._selected = selected;
999
1010
  }
1011
+ },
1012
+ targetSpeed: {
1013
+ enumerable: true,
1014
+ get: function() {
1015
+ return this._targetSpeed;
1016
+ }
1000
1017
  }
1001
1018
  });
1002
1019
 
@@ -1164,7 +1181,8 @@ define([
1164
1181
  markerWidth: this.markerWidth,
1165
1182
  volume: this.volume,
1166
1183
  volumeRange: this.volumeRange,
1167
- loading: this.loading
1184
+ loading: this.loading,
1185
+ targetSpeed: this.targetSpeed
1168
1186
  };
1169
1187
 
1170
1188
  Utils.extend(opts, options);
@@ -1217,6 +1235,7 @@ define([
1217
1235
  this._volume = opts.volume;
1218
1236
  this._volumeRange = opts.volumeRange;
1219
1237
  this._loading = opts.loading;
1238
+ this._targetSpeed = opts.targetSpeed;
1220
1239
 
1221
1240
  if (options && typeof options === 'object') {
1222
1241
  for (var key in options) {
@@ -109,7 +109,8 @@ define([
109
109
  markerWidth: sourceToCut.markerWidth,
110
110
  volume: sourceToCut.volume,
111
111
  volumeRange: sourceToCut.volumeRange,
112
- loading: sourceToCut.loading
112
+ loading: sourceToCut.loading,
113
+ targetSpeed: sourceToCut.targetSpeed
113
114
  };
114
115
 
115
116
  for (var key in sourceToCut) {
@@ -223,6 +224,7 @@ define([
223
224
  options.volume,
224
225
  options.volumeRange,
225
226
  options.loading,
227
+ options.targetSpeed,
226
228
  ...customParams
227
229
  );
228
230