wavesurfer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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);