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,385 @@
1
+ 'use strict';
2
+
3
+ WaveSurfer.WebAudio = {
4
+ scriptBufferSize: 256,
5
+ PLAYING_STATE: 0,
6
+ PAUSED_STATE: 1,
7
+ FINISHED_STATE: 2,
8
+
9
+ supportsWebAudio: function () {
10
+ return !!(window.AudioContext || window.webkitAudioContext);
11
+ },
12
+
13
+ getAudioContext: function () {
14
+ if (!WaveSurfer.WebAudio.audioContext) {
15
+ WaveSurfer.WebAudio.audioContext = new (
16
+ window.AudioContext || window.webkitAudioContext
17
+ );
18
+ }
19
+ return WaveSurfer.WebAudio.audioContext;
20
+ },
21
+
22
+ getOfflineAudioContext: function (sampleRate) {
23
+ if (!WaveSurfer.WebAudio.offlineAudioContext) {
24
+ WaveSurfer.WebAudio.offlineAudioContext = new (
25
+ window.OfflineAudioContext || window.webkitOfflineAudioContext
26
+ )(1, 2, sampleRate);
27
+ }
28
+ return WaveSurfer.WebAudio.offlineAudioContext;
29
+ },
30
+
31
+ init: function (params) {
32
+ this.params = params;
33
+ this.ac = params.audioContext || this.getAudioContext();
34
+
35
+ this.lastPlay = this.ac.currentTime;
36
+ this.startPosition = 0;
37
+ this.scheduledPause = null;
38
+
39
+ this.states = [
40
+ Object.create(WaveSurfer.WebAudio.state.playing),
41
+ Object.create(WaveSurfer.WebAudio.state.paused),
42
+ Object.create(WaveSurfer.WebAudio.state.finished)
43
+ ];
44
+
45
+ this.setState(this.PAUSED_STATE);
46
+
47
+ this.createVolumeNode();
48
+ this.createScriptNode();
49
+ this.createAnalyserNode();
50
+ this.setPlaybackRate(this.params.audioRate);
51
+ },
52
+
53
+ disconnectFilters: function () {
54
+ if (this.filters) {
55
+ this.filters.forEach(function (filter) {
56
+ filter && filter.disconnect();
57
+ });
58
+ this.filters = null;
59
+ // Reconnect direct path
60
+ this.analyser.connect(this.gainNode);
61
+ }
62
+ },
63
+
64
+ setState: function (state) {
65
+ if (this.state !== this.states[state]) {
66
+ this.state = this.states[state];
67
+ this.state.init.call(this);
68
+ }
69
+ },
70
+
71
+ // Unpacked filters
72
+ setFilter: function () {
73
+ this.setFilters([].slice.call(arguments));
74
+ },
75
+
76
+ /**
77
+ * @param {Array} filters Packed ilters array
78
+ */
79
+ setFilters: function (filters) {
80
+ // Remove existing filters
81
+ this.disconnectFilters();
82
+
83
+ // Insert filters if filter array not empty
84
+ if (filters && filters.length) {
85
+ this.filters = filters;
86
+
87
+ // Disconnect direct path before inserting filters
88
+ this.analyser.disconnect();
89
+
90
+ // Connect each filter in turn
91
+ filters.reduce(function (prev, curr) {
92
+ prev.connect(curr);
93
+ return curr;
94
+ }, this.analyser).connect(this.gainNode);
95
+ }
96
+
97
+ },
98
+
99
+ createScriptNode: function () {
100
+ var my = this;
101
+ var bufferSize = this.scriptBufferSize;
102
+ if (this.ac.createScriptProcessor) {
103
+ this.scriptNode = this.ac.createScriptProcessor(bufferSize);
104
+ } else {
105
+ this.scriptNode = this.ac.createJavaScriptNode(bufferSize);
106
+ }
107
+ this.scriptNode.connect(this.ac.destination);
108
+
109
+ this.scriptNode.onaudioprocess = function () {
110
+ var time = my.getCurrentTime();
111
+
112
+ if (my.buffer && time >= my.getDuration()) {
113
+ my.setState(my.FINISHED_STATE);
114
+ } else if (my.buffer && time >= my.scheduledPause) {
115
+ my.setState(my.PAUSED_STATE);
116
+ } else if (my.state === my.states[my.PLAYING_STATE]) {
117
+ my.fireEvent('audioprocess', time);
118
+ }
119
+ };
120
+ },
121
+
122
+ createAnalyserNode: function () {
123
+ this.analyser = this.ac.createAnalyser();
124
+ this.analyser.connect(this.gainNode);
125
+ },
126
+
127
+ /**
128
+ * Create the gain node needed to control the playback volume.
129
+ */
130
+ createVolumeNode: function () {
131
+ // Create gain node using the AudioContext
132
+ if (this.ac.createGain) {
133
+ this.gainNode = this.ac.createGain();
134
+ } else {
135
+ this.gainNode = this.ac.createGainNode();
136
+ }
137
+ // Add the gain node to the graph
138
+ this.gainNode.connect(this.ac.destination);
139
+ },
140
+
141
+ /**
142
+ * Set the gain to a new value.
143
+ *
144
+ * @param {Number} newGain The new gain, a floating point value
145
+ * between 0 and 1. 0 being no gain and 1 being maximum gain.
146
+ */
147
+ setVolume: function (newGain) {
148
+ this.gainNode.gain.value = newGain;
149
+ },
150
+
151
+ /**
152
+ * Get the current gain.
153
+ *
154
+ * @returns {Number} The current gain, a floating point value
155
+ * between 0 and 1. 0 being no gain and 1 being maximum gain.
156
+ */
157
+ getVolume: function () {
158
+ return this.gainNode.gain.value;
159
+ },
160
+
161
+ decodeArrayBuffer: function (arraybuffer, callback, errback) {
162
+ if (!this.offlineAc) {
163
+ this.offlineAc = this.getOfflineAudioContext(this.ac ? this.ac.sampleRate : 44100);
164
+ }
165
+ this.offlineAc.decodeAudioData(arraybuffer, (function (data) {
166
+ callback(data);
167
+ }).bind(this), errback);
168
+ },
169
+
170
+ /**
171
+ * @returns {Array} Array of peaks or array of arrays of peaks.
172
+ */
173
+ getPeaks: function (length) {
174
+ var sampleSize = this.buffer.length / length;
175
+ var sampleStep = ~~(sampleSize / 10) || 1;
176
+ var channels = this.buffer.numberOfChannels;
177
+ var splitPeaks = [];
178
+ var mergedPeaks = [];
179
+
180
+ for (var c = 0; c < channels; c++) {
181
+ var peaks = splitPeaks[c] = [];
182
+ var chan = this.buffer.getChannelData(c);
183
+
184
+ for (var i = 0; i < length; i++) {
185
+ var start = ~~(i * sampleSize);
186
+ var end = ~~(start + sampleSize);
187
+ var max = 0;
188
+ for (var j = start; j < end; j += sampleStep) {
189
+ var value = chan[j];
190
+ if (value > max) {
191
+ max = value;
192
+ // faster than Math.abs
193
+ } else if (-value > max) {
194
+ max = -value;
195
+ }
196
+ }
197
+ peaks[i] = max;
198
+
199
+ if (c == 0 || max > mergedPeaks[i]) {
200
+ mergedPeaks[i] = max;
201
+ }
202
+ }
203
+ }
204
+
205
+ return this.params.splitChannels ? splitPeaks : mergedPeaks;
206
+ },
207
+
208
+ getPlayedPercents: function () {
209
+ return this.state.getPlayedPercents.call(this);
210
+ },
211
+
212
+ disconnectSource: function () {
213
+ if (this.source) {
214
+ this.source.disconnect();
215
+ }
216
+ },
217
+
218
+ destroy: function () {
219
+ if (!this.isPaused()) {
220
+ this.pause();
221
+ }
222
+ this.unAll();
223
+ this.buffer = null;
224
+ this.disconnectFilters();
225
+ this.disconnectSource();
226
+ this.gainNode.disconnect();
227
+ this.scriptNode.disconnect();
228
+ this.analyser.disconnect();
229
+ },
230
+
231
+ load: function (buffer) {
232
+ this.startPosition = 0;
233
+ this.lastPlay = this.ac.currentTime;
234
+ this.buffer = buffer;
235
+ this.createSource();
236
+ },
237
+
238
+ createSource: function () {
239
+ this.disconnectSource();
240
+ this.source = this.ac.createBufferSource();
241
+
242
+ //adjust for old browsers.
243
+ this.source.start = this.source.start || this.source.noteGrainOn;
244
+ this.source.stop = this.source.stop || this.source.noteOff;
245
+
246
+ this.source.playbackRate.value = this.playbackRate;
247
+ this.source.buffer = this.buffer;
248
+ this.source.connect(this.analyser);
249
+ },
250
+
251
+ isPaused: function () {
252
+ return this.state !== this.states[this.PLAYING_STATE];
253
+ },
254
+
255
+ getDuration: function () {
256
+ if (!this.buffer) {
257
+ return 0;
258
+ }
259
+ return this.buffer.duration;
260
+ },
261
+
262
+ seekTo: function (start, end) {
263
+ this.scheduledPause = null;
264
+
265
+ if (start == null) {
266
+ start = this.getCurrentTime();
267
+ if (start >= this.getDuration()) {
268
+ start = 0;
269
+ }
270
+ }
271
+ if (end == null) {
272
+ end = this.getDuration();
273
+ }
274
+
275
+ this.startPosition = start;
276
+ this.lastPlay = this.ac.currentTime;
277
+
278
+ if (this.state === this.states[this.FINISHED_STATE]) {
279
+ this.setState(this.PAUSED_STATE);
280
+ }
281
+
282
+ return { start: start, end: end };
283
+ },
284
+
285
+ getPlayedTime: function () {
286
+ return (this.ac.currentTime - this.lastPlay) * this.playbackRate;
287
+ },
288
+
289
+ /**
290
+ * Plays the loaded audio region.
291
+ *
292
+ * @param {Number} start Start offset in seconds,
293
+ * relative to the beginning of a clip.
294
+ * @param {Number} end When to stop
295
+ * relative to the beginning of a clip.
296
+ */
297
+ play: function (start, end) {
298
+ // need to re-create source on each playback
299
+ this.createSource();
300
+
301
+ var adjustedTime = this.seekTo(start, end);
302
+
303
+ start = adjustedTime.start;
304
+ end = adjustedTime.end;
305
+
306
+ this.scheduledPause = end;
307
+
308
+ this.source.start(0, start, end - start);
309
+
310
+ this.setState(this.PLAYING_STATE);
311
+ },
312
+
313
+ /**
314
+ * Pauses the loaded audio.
315
+ */
316
+ pause: function () {
317
+ this.scheduledPause = null;
318
+
319
+ this.startPosition += this.getPlayedTime();
320
+ this.source && this.source.stop(0);
321
+
322
+ this.setState(this.PAUSED_STATE);
323
+ },
324
+
325
+ /**
326
+ * Returns the current time in seconds relative to the audioclip's duration.
327
+ */
328
+ getCurrentTime: function () {
329
+ return this.state.getCurrentTime.call(this);
330
+ },
331
+
332
+ /**
333
+ * Set the audio source playback rate.
334
+ */
335
+ setPlaybackRate: function (value) {
336
+ value = value || 1;
337
+ if (this.isPaused()) {
338
+ this.playbackRate = value;
339
+ } else {
340
+ this.pause();
341
+ this.playbackRate = value;
342
+ this.play();
343
+ }
344
+ }
345
+ };
346
+
347
+ WaveSurfer.WebAudio.state = {};
348
+
349
+ WaveSurfer.WebAudio.state.playing = {
350
+ init: function () {
351
+ },
352
+ getPlayedPercents: function () {
353
+ var duration = this.getDuration();
354
+ return (this.getCurrentTime() / duration) || 0;
355
+ },
356
+ getCurrentTime: function () {
357
+ return this.startPosition + this.getPlayedTime();
358
+ }
359
+ };
360
+
361
+ WaveSurfer.WebAudio.state.paused = {
362
+ init: function () {
363
+ },
364
+ getPlayedPercents: function () {
365
+ var duration = this.getDuration();
366
+ return (this.getCurrentTime() / duration) || 0;
367
+ },
368
+ getCurrentTime: function () {
369
+ return this.startPosition;
370
+ }
371
+ };
372
+
373
+ WaveSurfer.WebAudio.state.finished = {
374
+ init: function () {
375
+ this.fireEvent('finish');
376
+ },
377
+ getPlayedPercents: function () {
378
+ return 1;
379
+ },
380
+ getCurrentTime: function () {
381
+ return this.getDuration();
382
+ }
383
+ };
384
+
385
+ WaveSurfer.util.extend(WaveSurfer.WebAudio, WaveSurfer.Observer);
@@ -0,0 +1,6 @@
1
+ //= require plugin/wavesurfer.elan
2
+ //= require plugin/wavesurfer.microphone
3
+ //= require plugin/wavesurfer.minimap
4
+ //= require plugin/wavesurfer.regions
5
+ //= require plugin/wavesurfer.spectrogram
6
+ //= require plugin/wavesurfer.timeline
@@ -0,0 +1,6 @@
1
+ //= require src/wavesurfer
2
+ //= require src/util
3
+ //= require src/webaudio
4
+ //= require src/mediaelement
5
+ //= require src/drawer
6
+ //= require src/drawer.canvas
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wavesurfer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Viktor Oleksyn
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-04-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: wavesurfer.js for the Rails asset pipeline
42
+ email:
43
+ - bartezic@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - lib/wavesurfer.rb
49
+ - lib/wavesurfer/version.rb
50
+ - vendor/assets/javascripts/plugin/wavesurfer.elan.js
51
+ - vendor/assets/javascripts/plugin/wavesurfer.microphone.js
52
+ - vendor/assets/javascripts/plugin/wavesurfer.minimap.js
53
+ - vendor/assets/javascripts/plugin/wavesurfer.regions.js
54
+ - vendor/assets/javascripts/plugin/wavesurfer.spectrogram.js
55
+ - vendor/assets/javascripts/plugin/wavesurfer.timeline.js
56
+ - vendor/assets/javascripts/src/drawer.canvas.js
57
+ - vendor/assets/javascripts/src/drawer.js
58
+ - vendor/assets/javascripts/src/mediaelement.js
59
+ - vendor/assets/javascripts/src/util.js
60
+ - vendor/assets/javascripts/src/wavesurfer.js
61
+ - vendor/assets/javascripts/src/webaudio.js
62
+ - vendor/assets/javascripts/wavesurfer-plugins.js
63
+ - vendor/assets/javascripts/wavesurfer.js
64
+ homepage: https://github.com/bartezic/wavesurfer
65
+ licenses:
66
+ - MIT
67
+ metadata: {}
68
+ post_install_message:
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubyforge_project:
84
+ rubygems_version: 2.2.2
85
+ signing_key:
86
+ specification_version: 4
87
+ summary: wavesurfer.js for the Rails asset pipeline.
88
+ test_files: []