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.
- checksums.yaml +7 -0
- data/lib/wavesurfer.rb +6 -0
- data/lib/wavesurfer/version.rb +3 -0
- data/vendor/assets/javascripts/plugin/wavesurfer.elan.js +252 -0
- data/vendor/assets/javascripts/plugin/wavesurfer.microphone.js +173 -0
- data/vendor/assets/javascripts/plugin/wavesurfer.minimap.js +200 -0
- data/vendor/assets/javascripts/plugin/wavesurfer.regions.js +390 -0
- data/vendor/assets/javascripts/plugin/wavesurfer.spectrogram.js +210 -0
- data/vendor/assets/javascripts/plugin/wavesurfer.timeline.js +196 -0
- data/vendor/assets/javascripts/src/drawer.canvas.js +134 -0
- data/vendor/assets/javascripts/src/drawer.js +193 -0
- data/vendor/assets/javascripts/src/mediaelement.js +160 -0
- data/vendor/assets/javascripts/src/util.js +132 -0
- data/vendor/assets/javascripts/src/wavesurfer.js +440 -0
- data/vendor/assets/javascripts/src/webaudio.js +385 -0
- data/vendor/assets/javascripts/wavesurfer-plugins.js +6 -0
- data/vendor/assets/javascripts/wavesurfer.js +6 -0
- metadata +88 -0
@@ -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);
|
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: []
|