wavesurfer 0.0.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.
@@ -0,0 +1,196 @@
1
+ (function (root, factory) {
2
+ if (typeof define === 'function' && define.amd) {
3
+ define(['wavesurfer'], factory);
4
+ } else {
5
+ root.WaveSurfer.Timeline = factory(root.WaveSurfer);
6
+ }
7
+ }(this, function (WaveSurfer) {
8
+ 'use strict';
9
+
10
+ WaveSurfer.Timeline = {
11
+ init: function (params) {
12
+ this.params = params;
13
+ var wavesurfer = this.wavesurfer = params.wavesurfer;
14
+
15
+ if (!this.wavesurfer) {
16
+ throw Error('No WaveSurfer intance provided');
17
+ }
18
+
19
+ var drawer = this.drawer = this.wavesurfer.drawer;
20
+
21
+ this.container = 'string' == typeof params.container ?
22
+ document.querySelector(params.container) : params.container;
23
+
24
+ if (!this.container) {
25
+ throw Error('No container for WaveSurfer timeline');
26
+ }
27
+
28
+ this.width = drawer.width;
29
+ this.height = this.params.height || 20;
30
+ this.notchPercentHeight = this.params.notchPercentHeight || 90;
31
+ this.primaryColor = this.params.primaryColor || '#000';
32
+ this.secondaryColor = this.params.secondaryColor || '#c0c0c0';
33
+ this.primaryFontColor = this.params.primaryFontColor || '#000';
34
+ this.secondaryFontColor = this.params.secondaryFontColor || '#000';
35
+ this.fontFamily = this.params.fontFamily || 'Arial';
36
+ this.fontSize = this.params.fontSize || 10;
37
+
38
+ this.createWrapper();
39
+ this.createCanvas();
40
+ this.render();
41
+
42
+ wavesurfer.drawer.wrapper.onscroll = this.updateScroll.bind(this);
43
+ wavesurfer.on('redraw', this.render.bind(this));
44
+ wavesurfer.on('destroy', this.destroy.bind(this));
45
+ },
46
+
47
+ destroy: function () {
48
+ this.unAll();
49
+ if (this.wrapper) {
50
+ this.wrapper.parentNode.removeChild(this.wrapper);
51
+ this.wrapper = null;
52
+ }
53
+ },
54
+
55
+ createWrapper: function () {
56
+ var prevTimeline = this.container.querySelector('timeline');
57
+ if (prevTimeline) {
58
+ this.container.removeChild(prevTimeline);
59
+ }
60
+
61
+ var wsParams = this.wavesurfer.params;
62
+ this.wrapper = this.container.appendChild(
63
+ document.createElement('timeline')
64
+ );
65
+ this.drawer.style(this.wrapper, {
66
+ display: 'block',
67
+ position: 'relative',
68
+ userSelect: 'none',
69
+ webkitUserSelect: 'none',
70
+ height: this.height + 'px'
71
+ });
72
+
73
+ if (wsParams.fillParent || wsParams.scrollParent) {
74
+ this.drawer.style(this.wrapper, {
75
+ width: '100%',
76
+ overflowX: 'hidden',
77
+ overflowY: 'hidden'
78
+ });
79
+ }
80
+
81
+ var my = this;
82
+ this.wrapper.addEventListener('click', function (e) {
83
+ e.preventDefault();
84
+ var relX = 'offsetX' in e ? e.offsetX : e.layerX;
85
+ my.fireEvent('click', (relX / my.wrapper.scrollWidth) || 0);
86
+ });
87
+ },
88
+
89
+ createCanvas: function () {
90
+ var canvas = this.canvas = this.wrapper.appendChild(
91
+ document.createElement('canvas')
92
+ );
93
+
94
+ this.timeCc = canvas.getContext('2d');
95
+
96
+ this.wavesurfer.drawer.style(canvas, {
97
+ position: 'absolute',
98
+ zIndex: 4
99
+ });
100
+ },
101
+
102
+ render: function () {
103
+ this.updateCanvasStyle();
104
+ this.drawTimeCanvas();
105
+ },
106
+
107
+ updateCanvasStyle: function () {
108
+ var width = this.drawer.wrapper.scrollWidth;
109
+ this.canvas.width = width * this.wavesurfer.params.pixelRatio;
110
+ this.canvas.height = this.height * this.wavesurfer.params.pixelRatio;
111
+ this.canvas.style.width = width + 'px';
112
+ this.canvas.style.height = this.height + 'px';
113
+ },
114
+
115
+ drawTimeCanvas: function() {
116
+ var backend = this.wavesurfer.backend,
117
+ wsParams = this.wavesurfer.params,
118
+ duration = backend.getDuration();
119
+
120
+ if (wsParams.fillParent && !wsParams.scrollParent) {
121
+ var width = this.drawer.getWidth();
122
+ } else {
123
+ width = this.drawer.wrapper.scrollWidth * wsParams.pixelRatio;
124
+ }
125
+ var pixelsPerSecond = width/duration;
126
+
127
+ if (duration > 0) {
128
+ var curPixel = 0,
129
+ curSeconds = 0,
130
+ totalSeconds = parseInt(duration, 10) + 1,
131
+ formatTime = function(seconds) {
132
+ if (seconds/60 > 1) {
133
+ var minutes = parseInt(seconds / 60),
134
+ seconds = parseInt(seconds % 60);
135
+ seconds = (seconds < 10) ? '0' + seconds : seconds;
136
+ return '' + minutes + ':' + seconds;
137
+ } else {
138
+ return seconds;
139
+ }
140
+ };
141
+
142
+ if (pixelsPerSecond * 1 >= 25) {
143
+ var timeInterval = 1;
144
+ var primaryLabelInterval = 10;
145
+ var secondaryLabelInterval = 5;
146
+ } else if (pixelsPerSecond * 5 >= 25) {
147
+ var timeInterval = 5;
148
+ var primaryLabelInterval = 6;
149
+ var secondaryLabelInterval = 2;
150
+ } else if (pixelsPerSecond * 15 >= 25) {
151
+ var timeInterval = 15;
152
+ var primaryLabelInterval = 4;
153
+ var secondaryLabelInterval = 2;
154
+ } else {
155
+ var timeInterval = 60;
156
+ var primaryLabelInterval = 4;
157
+ var secondaryLabelInterval = 2;
158
+ }
159
+
160
+ var height1 = this.height - 4,
161
+ height2 = (this.height * (this.notchPercentHeight / 100.0)) - 4,
162
+ fontSize = this.fontSize * wsParams.pixelRatio;
163
+
164
+ for (var i = 0; i < totalSeconds/timeInterval; i++) {
165
+ if (i % primaryLabelInterval == 0) {
166
+ this.timeCc.fillStyle = this.primaryColor;
167
+ this.timeCc.fillRect(curPixel, 0, 1, height1);
168
+ this.timeCc.font = fontSize + 'px ' + this.fontFamily;
169
+ this.timeCc.fillStyle = this.primaryFontColor;
170
+ this.timeCc.fillText(formatTime(curSeconds), curPixel + 5, height1);
171
+ } else if (i % secondaryLabelInterval == 0) {
172
+ this.timeCc.fillStyle = this.secondaryColor;
173
+ this.timeCc.fillRect(curPixel, 0, 1, height1);
174
+ this.timeCc.font = fontSize + 'px ' + this.fontFamily;
175
+ this.timeCc.fillStyle = this.secondaryFontColor;
176
+ this.timeCc.fillText(formatTime(curSeconds), curPixel + 5, height1);
177
+ } else {
178
+ this.timeCc.fillStyle = this.secondaryColor;
179
+ this.timeCc.fillRect(curPixel, 0, 1, height2);
180
+ }
181
+
182
+ curSeconds += timeInterval;
183
+ curPixel += pixelsPerSecond * timeInterval;
184
+ }
185
+ }
186
+ },
187
+
188
+ updateScroll: function () {
189
+ this.wrapper.scrollLeft = this.drawer.wrapper.scrollLeft;
190
+ }
191
+ };
192
+
193
+ WaveSurfer.util.extend(WaveSurfer.Timeline, WaveSurfer.Observer);
194
+
195
+ return WaveSurfer.Timeline;
196
+ }));
@@ -0,0 +1,134 @@
1
+ 'use strict';
2
+
3
+ WaveSurfer.Drawer.Canvas = Object.create(WaveSurfer.Drawer);
4
+
5
+ WaveSurfer.util.extend(WaveSurfer.Drawer.Canvas, {
6
+ createElements: function () {
7
+ var waveCanvas = this.wrapper.appendChild(
8
+ this.style(document.createElement('canvas'), {
9
+ position: 'absolute',
10
+ zIndex: 1,
11
+ top: 0,
12
+ bottom: 0
13
+ })
14
+ );
15
+ this.waveCc = waveCanvas.getContext('2d');
16
+
17
+ this.progressWave = this.wrapper.appendChild(
18
+ this.style(document.createElement('wave'), {
19
+ position: 'absolute',
20
+ zIndex: 2,
21
+ top: 0,
22
+ bottom: 0,
23
+ overflow: 'hidden',
24
+ width: '0',
25
+ display: 'none',
26
+ boxSizing: 'border-box',
27
+ borderRightStyle: 'solid',
28
+ borderRightWidth: this.params.cursorWidth + 'px',
29
+ borderRightColor: this.params.cursorColor
30
+ })
31
+ );
32
+
33
+ if (this.params.waveColor != this.params.progressColor) {
34
+ var progressCanvas = this.progressWave.appendChild(
35
+ document.createElement('canvas')
36
+ );
37
+ this.progressCc = progressCanvas.getContext('2d');
38
+ }
39
+ },
40
+
41
+ updateSize: function () {
42
+ var width = Math.round(this.width / this.params.pixelRatio);
43
+
44
+ this.waveCc.canvas.width = this.width;
45
+ this.waveCc.canvas.height = this.height;
46
+ this.style(this.waveCc.canvas, { width: width + 'px'});
47
+
48
+ this.style(this.progressWave, { display: 'block'});
49
+
50
+ if (this.progressCc) {
51
+ this.progressCc.canvas.width = this.width;
52
+ this.progressCc.canvas.height = this.height;
53
+ this.style(this.progressCc.canvas, { width: width + 'px'});
54
+ }
55
+
56
+ this.clearWave();
57
+ },
58
+
59
+ clearWave: function () {
60
+ this.waveCc.clearRect(0, 0, this.width, this.height);
61
+ if (this.progressCc) {
62
+ this.progressCc.clearRect(0, 0, this.width, this.height);
63
+ }
64
+ },
65
+
66
+ drawWave: function (peaks, channelIndex) {
67
+ // Split channels
68
+ if (peaks[0] instanceof Array) {
69
+ var channels = peaks;
70
+ if (this.params.splitChannels) {
71
+ this.setHeight(channels.length * this.params.height * this.params.pixelRatio);
72
+ channels.forEach(this.drawWave, this);
73
+ return;
74
+ } else {
75
+ peaks = channels[0];
76
+ }
77
+ }
78
+
79
+ // A half-pixel offset makes lines crisp
80
+ var $ = 0.5 / this.params.pixelRatio;
81
+ // A margin between split waveforms
82
+ var height = this.params.height * this.params.pixelRatio;
83
+ var offsetY = height * channelIndex || 0;
84
+ var halfH = height / 2;
85
+ var length = peaks.length;
86
+ var scale = 1;
87
+ if (this.params.fillParent && this.width != length) {
88
+ scale = this.width / length;
89
+ }
90
+ var max = 1;
91
+ if (this.params.normalize) {
92
+ max = Math.max.apply(Math, peaks);
93
+ }
94
+
95
+ this.waveCc.fillStyle = this.params.waveColor;
96
+ if (this.progressCc) {
97
+ this.progressCc.fillStyle = this.params.progressColor;
98
+ }
99
+
100
+ [ this.waveCc, this.progressCc ].forEach(function (cc) {
101
+ if (!cc) { return; }
102
+
103
+ cc.beginPath();
104
+ cc.moveTo($, halfH + offsetY);
105
+
106
+ for (var i = 0; i < length; i++) {
107
+ var h = Math.round(peaks[i] / max * halfH);
108
+ cc.lineTo(i * scale + $, halfH + h + offsetY);
109
+ }
110
+
111
+ cc.lineTo(this.width + $, halfH + offsetY);
112
+ cc.moveTo($, halfH + offsetY);
113
+
114
+ for (var i = 0; i < length; i++) {
115
+ var h = Math.round(peaks[i] / max * halfH);
116
+ cc.lineTo(i * scale + $, halfH - h + offsetY);
117
+ }
118
+
119
+ cc.lineTo(this.width + $, halfH + offsetY);
120
+ cc.closePath();
121
+ cc.fill();
122
+
123
+ // Always draw a median line
124
+ cc.fillRect(0, halfH + offsetY - $, this.width, $);
125
+ }, this);
126
+ },
127
+
128
+ updateProgress: function (progress) {
129
+ var pos = Math.round(
130
+ this.width * progress
131
+ ) / this.params.pixelRatio;
132
+ this.style(this.progressWave, { width: pos + 'px' });
133
+ }
134
+ });
@@ -0,0 +1,193 @@
1
+ 'use strict';
2
+
3
+ WaveSurfer.Drawer = {
4
+ init: function (container, params) {
5
+ this.container = container;
6
+ this.params = params;
7
+
8
+ this.width = 0;
9
+ this.height = params.height * this.params.pixelRatio;
10
+
11
+ this.lastPos = 0;
12
+
13
+ this.createWrapper();
14
+ this.createElements();
15
+ },
16
+
17
+ createWrapper: function () {
18
+ this.wrapper = this.container.appendChild(
19
+ document.createElement('wave')
20
+ );
21
+
22
+ this.style(this.wrapper, {
23
+ display: 'block',
24
+ position: 'relative',
25
+ userSelect: 'none',
26
+ webkitUserSelect: 'none',
27
+ height: this.params.height + 'px'
28
+ });
29
+
30
+ if (this.params.fillParent || this.params.scrollParent) {
31
+ this.style(this.wrapper, {
32
+ width: '100%',
33
+ overflowX: this.params.hideScrollbar ? 'hidden' : 'auto',
34
+ overflowY: 'hidden'
35
+ });
36
+ }
37
+
38
+ this.setupWrapperEvents();
39
+ },
40
+
41
+ handleEvent: function (e) {
42
+ e.preventDefault();
43
+ var bbox = this.wrapper.getBoundingClientRect();
44
+ return ((e.clientX - bbox.left + this.wrapper.scrollLeft) / this.wrapper.scrollWidth) || 0;
45
+ },
46
+
47
+ setupWrapperEvents: function () {
48
+ var my = this;
49
+
50
+ this.wrapper.addEventListener('click', function (e) {
51
+ var scrollbarHeight = my.wrapper.offsetHeight - my.wrapper.clientHeight;
52
+ if (scrollbarHeight != 0) {
53
+ // scrollbar is visible. Check if click was on it
54
+ var bbox = my.wrapper.getBoundingClientRect();
55
+ if (e.clientY >= bbox.bottom - scrollbarHeight) {
56
+ // ignore mousedown as it was on the scrollbar
57
+ return;
58
+ }
59
+ }
60
+
61
+ if (my.params.interact) {
62
+ my.fireEvent('click', e, my.handleEvent(e));
63
+ }
64
+ });
65
+
66
+ this.wrapper.addEventListener('scroll', function (e) {
67
+ my.fireEvent('scroll', e);
68
+ });
69
+ },
70
+
71
+ drawPeaks: function (peaks, length) {
72
+ this.resetScroll();
73
+ this.setWidth(length);
74
+ this.drawWave(peaks);
75
+ },
76
+
77
+ style: function (el, styles) {
78
+ Object.keys(styles).forEach(function (prop) {
79
+ if (el.style[prop] !== styles[prop]) {
80
+ el.style[prop] = styles[prop];
81
+ }
82
+ });
83
+ return el;
84
+ },
85
+
86
+ resetScroll: function () {
87
+ if (this.wrapper !== null) {
88
+ this.wrapper.scrollLeft = 0;
89
+ }
90
+ },
91
+
92
+ recenter: function (percent) {
93
+ var position = this.wrapper.scrollWidth * percent;
94
+ this.recenterOnPosition(position, true);
95
+ },
96
+
97
+ recenterOnPosition: function (position, immediate) {
98
+ var scrollLeft = this.wrapper.scrollLeft;
99
+ var half = ~~(this.wrapper.clientWidth / 2);
100
+ var target = position - half;
101
+ var offset = target - scrollLeft;
102
+ var maxScroll = this.wrapper.scrollWidth - this.wrapper.clientWidth;
103
+
104
+ if (maxScroll == 0) {
105
+ // no need to continue if scrollbar is not there
106
+ return;
107
+ }
108
+
109
+ // if the cursor is currently visible...
110
+ if (!immediate && -half <= offset && offset < half) {
111
+ // we'll limit the "re-center" rate.
112
+ var rate = 5;
113
+ offset = Math.max(-rate, Math.min(rate, offset));
114
+ target = scrollLeft + offset;
115
+ }
116
+
117
+ // limit target to valid range (0 to maxScroll)
118
+ target = Math.max(0, Math.min(maxScroll, target));
119
+ // no use attempting to scroll if we're not moving
120
+ if (target != scrollLeft) {
121
+ this.wrapper.scrollLeft = target;
122
+ }
123
+
124
+ },
125
+
126
+ getWidth: function () {
127
+ return Math.round(this.container.clientWidth * this.params.pixelRatio);
128
+ },
129
+
130
+ setWidth: function (width) {
131
+ if (width == this.width) { return; }
132
+
133
+ this.width = width;
134
+
135
+ if (this.params.fillParent || this.params.scrollParent) {
136
+ this.style(this.wrapper, {
137
+ width: ''
138
+ });
139
+ } else {
140
+ this.style(this.wrapper, {
141
+ width: ~~(this.width / this.params.pixelRatio) + 'px'
142
+ });
143
+ }
144
+
145
+ this.updateSize();
146
+ },
147
+
148
+ setHeight: function (height) {
149
+ if (height == this.height) { return; }
150
+ this.height = height;
151
+ this.style(this.wrapper, {
152
+ height: ~~(this.height / this.params.pixelRatio) + 'px'
153
+ });
154
+ this.updateSize();
155
+ },
156
+
157
+ progress: function (progress) {
158
+ var minPxDelta = 1 / this.params.pixelRatio;
159
+ var pos = Math.round(progress * this.width) * minPxDelta;
160
+
161
+ if (pos < this.lastPos || pos - this.lastPos >= minPxDelta) {
162
+ this.lastPos = pos;
163
+
164
+ if (this.params.scrollParent) {
165
+ var newPos = ~~(this.wrapper.scrollWidth * progress);
166
+ this.recenterOnPosition(newPos);
167
+ }
168
+
169
+ this.updateProgress(progress);
170
+ }
171
+ },
172
+
173
+ destroy: function () {
174
+ this.unAll();
175
+ if (this.wrapper) {
176
+ this.container.removeChild(this.wrapper);
177
+ this.wrapper = null;
178
+ }
179
+ },
180
+
181
+ /* Renderer-specific methods */
182
+ createElements: function () {},
183
+
184
+ updateSize: function () {},
185
+
186
+ drawWave: function (peaks, max) {},
187
+
188
+ clearWave: function () {},
189
+
190
+ updateProgress: function (position) {}
191
+ };
192
+
193
+ WaveSurfer.util.extend(WaveSurfer.Drawer, WaveSurfer.Observer);