waveform_data_js_rails 1.5.1.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.
data/COPYING.LESSER ADDED
@@ -0,0 +1,165 @@
1
+ GNU LESSER GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+
9
+ This version of the GNU Lesser General Public License incorporates
10
+ the terms and conditions of version 3 of the GNU General Public
11
+ License, supplemented by the additional permissions listed below.
12
+
13
+ 0. Additional Definitions.
14
+
15
+ As used herein, "this License" refers to version 3 of the GNU Lesser
16
+ General Public License, and the "GNU GPL" refers to version 3 of the GNU
17
+ General Public License.
18
+
19
+ "The Library" refers to a covered work governed by this License,
20
+ other than an Application or a Combined Work as defined below.
21
+
22
+ An "Application" is any work that makes use of an interface provided
23
+ by the Library, but which is not otherwise based on the Library.
24
+ Defining a subclass of a class defined by the Library is deemed a mode
25
+ of using an interface provided by the Library.
26
+
27
+ A "Combined Work" is a work produced by combining or linking an
28
+ Application with the Library. The particular version of the Library
29
+ with which the Combined Work was made is also called the "Linked
30
+ Version".
31
+
32
+ The "Minimal Corresponding Source" for a Combined Work means the
33
+ Corresponding Source for the Combined Work, excluding any source code
34
+ for portions of the Combined Work that, considered in isolation, are
35
+ based on the Application, and not on the Linked Version.
36
+
37
+ The "Corresponding Application Code" for a Combined Work means the
38
+ object code and/or source code for the Application, including any data
39
+ and utility programs needed for reproducing the Combined Work from the
40
+ Application, but excluding the System Libraries of the Combined Work.
41
+
42
+ 1. Exception to Section 3 of the GNU GPL.
43
+
44
+ You may convey a covered work under sections 3 and 4 of this License
45
+ without being bound by section 3 of the GNU GPL.
46
+
47
+ 2. Conveying Modified Versions.
48
+
49
+ If you modify a copy of the Library, and, in your modifications, a
50
+ facility refers to a function or data to be supplied by an Application
51
+ that uses the facility (other than as an argument passed when the
52
+ facility is invoked), then you may convey a copy of the modified
53
+ version:
54
+
55
+ a) under this License, provided that you make a good faith effort to
56
+ ensure that, in the event an Application does not supply the
57
+ function or data, the facility still operates, and performs
58
+ whatever part of its purpose remains meaningful, or
59
+
60
+ b) under the GNU GPL, with none of the additional permissions of
61
+ this License applicable to that copy.
62
+
63
+ 3. Object Code Incorporating Material from Library Header Files.
64
+
65
+ The object code form of an Application may incorporate material from
66
+ a header file that is part of the Library. You may convey such object
67
+ code under terms of your choice, provided that, if the incorporated
68
+ material is not limited to numerical parameters, data structure
69
+ layouts and accessors, or small macros, inline functions and templates
70
+ (ten or fewer lines in length), you do both of the following:
71
+
72
+ a) Give prominent notice with each copy of the object code that the
73
+ Library is used in it and that the Library and its use are
74
+ covered by this License.
75
+
76
+ b) Accompany the object code with a copy of the GNU GPL and this license
77
+ document.
78
+
79
+ 4. Combined Works.
80
+
81
+ You may convey a Combined Work under terms of your choice that,
82
+ taken together, effectively do not restrict modification of the
83
+ portions of the Library contained in the Combined Work and reverse
84
+ engineering for debugging such modifications, if you also do each of
85
+ the following:
86
+
87
+ a) Give prominent notice with each copy of the Combined Work that
88
+ the Library is used in it and that the Library and its use are
89
+ covered by this License.
90
+
91
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
92
+ document.
93
+
94
+ c) For a Combined Work that displays copyright notices during
95
+ execution, include the copyright notice for the Library among
96
+ these notices, as well as a reference directing the user to the
97
+ copies of the GNU GPL and this license document.
98
+
99
+ d) Do one of the following:
100
+
101
+ 0) Convey the Minimal Corresponding Source under the terms of this
102
+ License, and the Corresponding Application Code in a form
103
+ suitable for, and under terms that permit, the user to
104
+ recombine or relink the Application with a modified version of
105
+ the Linked Version to produce a modified Combined Work, in the
106
+ manner specified by section 6 of the GNU GPL for conveying
107
+ Corresponding Source.
108
+
109
+ 1) Use a suitable shared library mechanism for linking with the
110
+ Library. A suitable mechanism is one that (a) uses at run time
111
+ a copy of the Library already present on the user's computer
112
+ system, and (b) will operate properly with a modified version
113
+ of the Library that is interface-compatible with the Linked
114
+ Version.
115
+
116
+ e) Provide Installation Information, but only if you would otherwise
117
+ be required to provide such information under section 6 of the
118
+ GNU GPL, and only to the extent that such information is
119
+ necessary to install and execute a modified version of the
120
+ Combined Work produced by recombining or relinking the
121
+ Application with a modified version of the Linked Version. (If
122
+ you use option 4d0, the Installation Information must accompany
123
+ the Minimal Corresponding Source and Corresponding Application
124
+ Code. If you use option 4d1, you must provide the Installation
125
+ Information in the manner specified by section 6 of the GNU GPL
126
+ for conveying Corresponding Source.)
127
+
128
+ 5. Combined Libraries.
129
+
130
+ You may place library facilities that are a work based on the
131
+ Library side by side in a single library together with other library
132
+ facilities that are not Applications and are not covered by this
133
+ License, and convey such a combined library under terms of your
134
+ choice, if you do both of the following:
135
+
136
+ a) Accompany the combined library with a copy of the same work based
137
+ on the Library, uncombined with any other library facilities,
138
+ conveyed under the terms of this License.
139
+
140
+ b) Give prominent notice with the combined library that part of it
141
+ is a work based on the Library, and explaining where to find the
142
+ accompanying uncombined form of the same work.
143
+
144
+ 6. Revised Versions of the GNU Lesser General Public License.
145
+
146
+ The Free Software Foundation may publish revised and/or new versions
147
+ of the GNU Lesser General Public License from time to time. Such new
148
+ versions will be similar in spirit to the present version, but may
149
+ differ in detail to address new problems or concerns.
150
+
151
+ Each version is given a distinguishing version number. If the
152
+ Library as you received it specifies that a certain numbered version
153
+ of the GNU Lesser General Public License "or any later version"
154
+ applies to it, you have the option of following the terms and
155
+ conditions either of that published version or of any later version
156
+ published by the Free Software Foundation. If the Library as you
157
+ received it does not specify a version number of the GNU Lesser
158
+ General Public License, you may choose any version of the GNU Lesser
159
+ General Public License ever published by the Free Software Foundation.
160
+
161
+ If the Library as you received it specifies that a proxy can decide
162
+ whether future versions of the GNU Lesser General Public License shall
163
+ apply, that proxy's public statement of acceptance of any version is
164
+ permanent authorization for you to choose that version for the
165
+ Library.
data/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # waveform-data-js-rails
2
+
3
+ waveform-data-js-rails wraps the [waveform-data.js](https://github.com/bbcrd/waveform-data.js/) library in a rails engine for simple use with the asset pipeline provided by Rails 4.2 and higher. The gem includes the development (non-minified)
4
+ javascript source for ease of exploration. The asset pipeline will minify in production.
5
+
6
+ waveform-data.js is a JavaScript library for creating zoomable, browsable and segmentable representations of audio waveforms. It's part of a [BBC R&D Browser-based audio waveform visualisation software family](http://waveform.prototyping.bbc.co.uk/).
7
+
8
+ ## Usage
9
+
10
+ Add the following to your gemfile:
11
+
12
+ gem 'waveform_data_js_rails'
13
+
14
+ Add the following directive to your Javascript manifest file (application.js):
15
+
16
+ //= require waveform-data
17
+
18
+ Full documentation for `waveform-data.js` can be [found in its README](https://github.com/bbcrd/waveform-data.js/blob/master/README.md).
19
+
20
+ ## Versioning
21
+
22
+ waveform-data-js-rails 1.5.1.0 == waveform-data-js 1.5.1
23
+
24
+ Every attempt is made to mirror the currently shipping waveform-data.js version number wherever possible. The major, minor, and patch version numbers will always represent the waveform-data.js version. Should a gem bug be discovered, a 4th version identifier will be added and incremented.
25
+
26
+ ## License
27
+
28
+ Licensed under the GNU Lesser General Public License v3; see COPYING and COPYING.LESSER for details.
29
+
30
+ ## Authorship
31
+
32
+ Gem wrapped by [Tom Armitage](mailto:tom@infovore.org). Original code by [Thomas Parisot](mailto:thomas.parisot@bbc.co.uk).
@@ -0,0 +1,5 @@
1
+ require "waveform_data_js_rails/version"
2
+ module WaveformDataJsRails
3
+ class Engine < ::Rails::Engine
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module WaveformDataJsRails
2
+ VERSION = "1.5.1.0"
3
+ end
@@ -0,0 +1,1406 @@
1
+ !function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.WaveformData=e():"undefined"!=typeof global?global.WaveformData=e():"undefined"!=typeof self&&(self.WaveformData=e())}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
2
+ "use strict";
3
+
4
+ /**
5
+ * ArrayBuffer adapter consumes binary waveform data (data format version 1).
6
+ * It is used as a data abstraction layer by `WaveformData`.
7
+ *
8
+ * This is supposed to be the fastest adapter ever:
9
+ * * **Pros**: working directly in memory, everything is done by reference (including the offsetting)
10
+ * * **Cons**: binary data are hardly readable without data format knowledge (and this is why this adapter exists).
11
+ *
12
+ * Also, it is recommended to use the `fromResponseData` factory.
13
+ *
14
+ * @see WaveformDataArrayBufferAdapter.fromResponseData
15
+ * @param {DataView} response_data
16
+ * @constructor
17
+ */
18
+ var WaveformDataArrayBufferAdapter = module.exports = function WaveformDataArrayBufferAdapter(response_data){
19
+ this.data = response_data;
20
+ };
21
+
22
+ /**
23
+ * Detects if a set of data is suitable for the ArrayBuffer adapter.
24
+ * It is used internally by `WaveformData.create` so you should not bother using it.
25
+ *
26
+ * @static
27
+ * @param {Mixed} data
28
+ * @returns {boolean}
29
+ */
30
+ WaveformDataArrayBufferAdapter.isCompatible = function isCompatible(data){
31
+ return data && typeof data === "object" && "byteLength" in data;
32
+ };
33
+
34
+ /**
35
+ * Setup factory to create an adapter based on heterogeneous input formats.
36
+ *
37
+ * It is the preferred way to build an adapter instance.
38
+ *
39
+ * ```javascript
40
+ * var arrayBufferAdapter = WaveformData.adapters.arraybuffer;
41
+ * var xhr = new XMLHttpRequest();
42
+ *
43
+ * // .dat file generated by audiowaveform program
44
+ * xhr.open("GET", "http://example.com/waveforms/track.dat");
45
+ * xhr.responseType = "arraybuffer";
46
+ * xhr.addEventListener("load", function onResponse(progressEvent){
47
+ * var responseData = progressEvent.target.response;
48
+ *
49
+ * // doing stuff with the raw data ...
50
+ * // you only have access to WaveformDataArrayBufferAdapter API
51
+ * var adapter = arrayBufferAdapter.fromResponseData(responseData);
52
+ *
53
+ * // or making things easy by using WaveformData ...
54
+ * // you have access WaveformData API
55
+ * var waveform = new WaveformData(responseData, arrayBufferAdapter);
56
+ * });
57
+ *
58
+ * xhr.send();
59
+ * ```
60
+
61
+ * @static
62
+ * @param {ArrayBuffer} response_data
63
+ * @return {WaveformDataArrayBufferAdapter}
64
+ */
65
+ WaveformDataArrayBufferAdapter.fromResponseData = function fromArrayBufferResponseData(response_data){
66
+ return new WaveformDataArrayBufferAdapter(new DataView(response_data));
67
+ };
68
+
69
+ /**
70
+ * @namespace WaveformDataArrayBufferAdapter
71
+ */
72
+ WaveformDataArrayBufferAdapter.prototype = {
73
+ /**
74
+ * Returns the data format version number.
75
+ *
76
+ * @return {Integer} Version number of the consumed data format.
77
+ */
78
+ get version(){
79
+ return this.data.getInt32(0, true);
80
+ },
81
+ /**
82
+ * Indicates if the response body is encoded in 8bits.
83
+ *
84
+ * **Notice**: currently the adapter only deals with 8bits encoded data.
85
+ * You should favor that too because of the smaller data network fingerprint.
86
+ *
87
+ * @return {boolean} True if data are declared to be 8bits encoded.
88
+ */
89
+ get is_8_bit(){
90
+ return !!this.data.getUint32(4, true);
91
+ },
92
+ /**
93
+ * Indicates if the response body is encoded in 16bits.
94
+ *
95
+ * @return {boolean} True if data are declared to be 16bits encoded.
96
+ */
97
+ get is_16_bit(){
98
+ return !this.is_8_bit;
99
+ },
100
+ /**
101
+ * Returns the number of samples per second.
102
+ *
103
+ * @return {Integer} Number of samples per second.
104
+ */
105
+ get sample_rate(){
106
+ return this.data.getInt32(8, true);
107
+ },
108
+ /**
109
+ * Returns the scale (number of samples per pixel).
110
+ *
111
+ * @return {Integer} Number of samples per pixel.
112
+ */
113
+ get scale(){
114
+ return this.data.getInt32(12, true);
115
+ },
116
+ /**
117
+ * Returns the length of the waveform data (number of data points).
118
+ *
119
+ * @return {Integer} Length of the waveform data.
120
+ */
121
+ get length(){
122
+ return this.data.getUint32(16, true);
123
+ },
124
+ /**
125
+ * Returns a value at a specific offset.
126
+ *
127
+ * @param {Integer} index
128
+ * @return {number} waveform value
129
+ */
130
+ at: function at_sample(index){
131
+ return Math.round(this.data.getInt8(20 + index));
132
+ }
133
+ };
134
+
135
+ },{}],2:[function(require,module,exports){
136
+ "use strict";
137
+
138
+ module.exports = {
139
+ "arraybuffer": require("./arraybuffer.js"),
140
+ "object": require("./object.js")
141
+ };
142
+ },{"./arraybuffer.js":1,"./object.js":3}],3:[function(require,module,exports){
143
+ "use strict";
144
+
145
+ /**
146
+ * Object adapter consumes stringified JSON or JSON waveform data (data format version 1).
147
+ * It is used as a data abstraction layer by `WaveformData`.
148
+ *
149
+ * This is supposed to be a fallback for browsers not supporting ArrayBuffer:
150
+ * * **Pros**: easy to debug response_data and quite self describing.
151
+ * * **Cons**: slower than ArrayBuffer, more memory consumption.
152
+ *
153
+ * Also, it is recommended to use the `fromResponseData` factory.
154
+ *
155
+ * @see WaveformDataObjectAdapter.fromResponseData
156
+ * @param {String|Object} response_data JSON or stringified JSON
157
+ * @constructor
158
+ */
159
+ var WaveformDataObjectAdapter = module.exports = function WaveformDataObjectAdapter(response_data){
160
+ this.data = response_data;
161
+ };
162
+
163
+ /**
164
+ * Detects if a set of data is suitable for the Object adapter.
165
+ * It is used internally by `WaveformData.create` so you should not bother using it.
166
+ *
167
+ * @static
168
+ * @param {Mixed} data
169
+ * @returns {boolean}
170
+ */
171
+ WaveformDataObjectAdapter.isCompatible = function isCompatible(data){
172
+ return data && (typeof data === "object" && "sample_rate" in data) || (typeof data === "string" && "sample_rate" in JSON.parse(data));
173
+ };
174
+
175
+ /**
176
+ * Setup factory to create an adapter based on heterogeneous input formats.
177
+ *
178
+ * It is the preferred way to build an adapter instance.
179
+ *
180
+ * ```javascript
181
+ * var objectAdapter = WaveformData.adapters.object;
182
+ * var xhr = new XMLHttpRequest();
183
+ *
184
+ * // .dat file generated by audiowaveform program
185
+ * xhr.open("GET", "http://example.com/waveforms/track.json");
186
+ * xhr.responseType = "json";
187
+ * xhr.addEventListener("load", function onResponse(progressEvent){
188
+ * var responseData = progressEvent.target.response;
189
+ *
190
+ * // doing stuff with the raw data ...
191
+ * // you only have access to WaveformDataObjectAdapter API
192
+ * var adapter = objectAdapter.fromResponseData(responseData);
193
+ *
194
+ * // or making things easy by using WaveformData ...
195
+ * // you have access WaveformData API
196
+ * var waveform = new WaveformData(responseData, objectAdapter);
197
+ * });
198
+ *
199
+ * xhr.send();
200
+ * ```
201
+
202
+ * @static
203
+ * @param {String|Object} response_data JSON or stringified JSON
204
+ * @return {WaveformDataObjectAdapter}
205
+ */
206
+ WaveformDataObjectAdapter.fromResponseData = function fromJSONResponseData(response_data){
207
+ if (typeof response_data === "string"){
208
+ return new WaveformDataObjectAdapter(JSON.parse(response_data));
209
+ }
210
+ else{
211
+ return new WaveformDataObjectAdapter(response_data);
212
+ }
213
+ };
214
+ /**
215
+ * @namespace WaveformDataObjectAdapter
216
+ */
217
+ WaveformDataObjectAdapter.prototype = {
218
+ /**
219
+ * Returns the data format version number.
220
+ *
221
+ * @return {Integer} Version number of the consumed data format.
222
+ */
223
+ get version(){
224
+ return this.data.version || 1;
225
+ },
226
+ /**
227
+ * Indicates if the response body is encoded in 8bits.
228
+ *
229
+ * **Notice**: currently the adapter only deals with 8bits encoded data.
230
+ * You should favor that too because of the smaller data network fingerprint.
231
+ *
232
+ * @return {boolean} True if data are declared to be 8bits encoded.
233
+ */
234
+ get is_8_bit(){
235
+ return this.data.bits === 8;
236
+ },
237
+ /**
238
+ * Indicates if the response body is encoded in 16bits.
239
+ *
240
+ * @return {boolean} True if data are declared to be 16bits encoded.
241
+ */
242
+ get is_16_bit(){
243
+ return !this.is_8_bit;
244
+ },
245
+ /**
246
+ * Returns the number of samples per second.
247
+ *
248
+ * @return {Integer} Number of samples per second.
249
+ */
250
+ get sample_rate(){
251
+ return this.data.sample_rate;
252
+ },
253
+ /**
254
+ * Returns the scale (number of samples per pixel).
255
+ *
256
+ * @return {Integer} Number of samples per pixel.
257
+ */
258
+ get scale(){
259
+ return this.data.samples_per_pixel;
260
+ },
261
+ /**
262
+ * Returns the length of the waveform data (number of data points).
263
+ *
264
+ * @return {Integer} Length of the waveform data.
265
+ */
266
+ get length(){
267
+ return this.data.length;
268
+ },
269
+ /**
270
+ * Returns a value at a specific offset.
271
+ *
272
+ * @param {Integer} index
273
+ * @return {number} waveform value
274
+ */
275
+ at: function at_sample(index){
276
+ return Math.round(this.data.data[index]);
277
+ }
278
+ };
279
+
280
+ },{}],4:[function(require,module,exports){
281
+ 'use strict';
282
+
283
+ var WaveformData = require('../core.js');
284
+ /**
285
+ * This callback is executed once the audio has been decoded by the browser and resampled by waveform-data.
286
+ *
287
+ * @callback onAudioResampled
288
+ * @param {WaveformData} waveform_data Waveform instance of the browser decoded audio
289
+ * @param {AudioBuffer} audio_buffer Decoded audio buffer
290
+ */
291
+
292
+ /**
293
+ * AudioBuffer-based WaveformData generator
294
+ *
295
+ * @param {Object.<{scale: Number, scale_adjuster: Number}>} options
296
+ * @param {onAudioResampled} callback
297
+ * @returns {Function.<AudioBuffer>}
298
+ */
299
+ module.exports = function getAudioDecoder(options, callback){
300
+ var scale = options.scale;
301
+ var scale_adjuster = options.scale_adjuster;
302
+
303
+ return function onAudioDecoded(audio_buffer){
304
+ var data_length = Math.floor(audio_buffer.length / scale);
305
+ var offset = 20;
306
+ var data_object = new DataView(new ArrayBuffer(offset + data_length * 2));
307
+ var left_channel, right_channel;
308
+ var min_value = Infinity, max_value = -Infinity, scale_counter = scale;
309
+ var buffer_length = audio_buffer.length;
310
+
311
+ data_object.setInt32(0, 1, true); //version
312
+ data_object.setUint32(4, 1, true); //is 8 bit
313
+ data_object.setInt32(8, audio_buffer.sampleRate, true); //sample rate
314
+ data_object.setInt32(12, scale, true); //scale
315
+ data_object.setInt32(16, data_length, true); //length
316
+
317
+ left_channel = audio_buffer.getChannelData(0);
318
+ right_channel = audio_buffer.getChannelData(0);
319
+
320
+ for (var i = 0; i < buffer_length; i++){
321
+ var sample = (left_channel[i] + right_channel[i]) / 2 * scale_adjuster;
322
+
323
+ if (sample < min_value){
324
+ min_value = sample;
325
+ if (min_value < -128) {
326
+ min_value = -128;
327
+ }
328
+ }
329
+
330
+ if (sample > max_value){
331
+ max_value = sample;
332
+ if (max_value > 127) {
333
+ max_value = 127;
334
+ }
335
+ }
336
+
337
+ if (--scale_counter === 0){
338
+ data_object.setInt8(offset++, Math.floor(min_value));
339
+ data_object.setInt8(offset++, Math.floor(max_value));
340
+ min_value = Infinity; max_value = -Infinity; scale_counter = scale;
341
+ }
342
+ }
343
+
344
+ callback(new WaveformData(data_object.buffer, WaveformData.adapters.arraybuffer), audio_buffer);
345
+ };
346
+ };
347
+
348
+ },{"../core.js":6}],5:[function(require,module,exports){
349
+ "use strict";
350
+
351
+ var audioContext = require('audio-context');
352
+ var audioDecoder = require('./audiodecoder.js');
353
+
354
+ /**
355
+ * Creates a working WaveformData based on binary audio data.
356
+ *
357
+ * This is still quite experimental and the result will mostly depend of the
358
+ * support state of the running browser.
359
+ *
360
+ * ```javascript
361
+ * var xhr = new XMLHttpRequest();
362
+ *
363
+ * // URL of a CORS MP3/Ogg file
364
+ * xhr.open("GET", "http://example.com/audio/track.ogg");
365
+ * xhr.responseType = "arraybuffer";
366
+ *
367
+ * xhr.addEventListener("load", function onResponse(progressEvent){
368
+ * WaveformData.builders.webaudio(progressEvent.target.response, onProcessed(waveform){
369
+ * console.log(waveform.duration);
370
+ * });
371
+ * });
372
+ *
373
+ * xhr.send();
374
+ * ```
375
+ *
376
+ * @todo use the errorCallback for `decodeAudioData` to handle possible failures
377
+ * @todo use a Web Worker to offload processing of the binary data
378
+ * @todo or use `SourceBuffer.appendBuffer` and `ProgressEvent` to stream the decoding
379
+ * @todo abstract the number of channels, because it is assumed the audio file is stereo
380
+ * @param {ArrayBuffer} raw_response
381
+ * @param {callback} what to do once the decoding is done
382
+ * @constructor
383
+ */
384
+ function fromAudioObjectBuilder(raw_response, options, callback){
385
+ var defaultOptions = {
386
+ scale: 512,
387
+ scale_adjuster: 127
388
+ };
389
+
390
+ if (typeof options === 'function') {
391
+ callback = options;
392
+ options = {};
393
+ }
394
+ else {
395
+ options = options || {};
396
+ }
397
+
398
+ options.scale = options.scale || defaultOptions.scale;
399
+ options.scale_adjuster = options.scale_adjuster || defaultOptions.scale_adjuster;
400
+
401
+ /*
402
+ * The result will vary on the codec implentation of the browser.
403
+ * We don't handle the case where the browser is unable to handle the decoding.
404
+ *
405
+ * @see https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#dfn-decodeAudioData
406
+ *
407
+ * Adapted from BlockFile::CalcSummary in Audacity, with permission.
408
+ * @see https://code.google.com/p/audacity/source/browse/audacity-src/trunk/src/BlockFile.cpp
409
+ */
410
+ audioContext.decodeAudioData(raw_response, audioDecoder(options, callback));
411
+ }
412
+
413
+ fromAudioObjectBuilder.getAudioContext = function getAudioContext(){
414
+ return audioContext;
415
+ };
416
+
417
+ module.exports = fromAudioObjectBuilder;
418
+ },{"./audiodecoder.js":4,"audio-context":9}],6:[function(require,module,exports){
419
+ "use strict";
420
+
421
+ var WaveformDataSegment = require("./segment.js");
422
+ var WaveformDataPoint = require("./point.js");
423
+
424
+ /**
425
+ * Facade to iterate on audio waveform response.
426
+ *
427
+ * ```javascript
428
+ * var waveform = new WaveformData({ ... }, WaveformData.adapters.object);
429
+ *
430
+ * var json_waveform = new WaveformData(xhr.responseText, WaveformData.adapters.object);
431
+ *
432
+ * var arraybuff_waveform = new WaveformData(getArrayBufferData(), WaveformData.adapters.arraybuffer);
433
+ * ```
434
+ *
435
+ * ## Offsets
436
+ *
437
+ * An **offset** is a non-destructive way to iterate on a subset of data.
438
+ *
439
+ * It is the easiest way to **navigate** through data without having to deal with complex calculations.
440
+ * Simply iterate over the data to display them.
441
+ *
442
+ * *Notice*: the default offset is the entire set of data.
443
+ *
444
+ * @param {String|ArrayBuffer|Mixed} response_data Waveform data, to be consumed by the related adapter.
445
+ * @param {WaveformData.adapter|Function} adapter Backend adapter used to manage access to the data.
446
+ * @constructor
447
+ */
448
+ var WaveformData = module.exports = function WaveformData(response_data, adapter){
449
+ /**
450
+ * Backend adapter used to manage access to the data.
451
+ *
452
+ * @type {Object}
453
+ */
454
+ this.adapter = adapter.fromResponseData(response_data);
455
+
456
+ /**
457
+ * Defined segments.
458
+ *
459
+ * ```javascript
460
+ * var waveform = new WaveformData({ ... }, WaveformData.adapters.object);
461
+ *
462
+ * console.log(waveform.segments.speakerA); // -> undefined
463
+ *
464
+ * waveform.set_segment(30, 90, "speakerA");
465
+ *
466
+ * console.log(waveform.segments.speakerA.start); // -> 30
467
+ * ```
468
+ *
469
+ * @type {Object} A hash of `WaveformDataSegment` objects.
470
+ */
471
+ this.segments = {};
472
+
473
+ /**
474
+ * Defined points.
475
+ *
476
+ * ```javascript
477
+ * var waveform = new WaveformData({ ... }, WaveformData.adapters.object);
478
+ *
479
+ * console.log(waveform.points.speakerA); // -> undefined
480
+ *
481
+ * waveform.set_point(30, "speakerA");
482
+ *
483
+ * console.log(waveform.points.speakerA.timeStamp); // -> 30
484
+ * ```
485
+ *
486
+ * @type {Object} A hash of `WaveformDataPoint` objects.
487
+ */
488
+ this.points = {};
489
+
490
+ this.offset(0, this.adapter.length);
491
+ };
492
+
493
+ /**
494
+ * Creates an instance of WaveformData by guessing the adapter from the data type.
495
+ * As an icing sugar, it will also do the detection job from an XMLHttpRequest response.
496
+ *
497
+ * ```javascript
498
+ * var xhr = new XMLHttpRequest();
499
+ * xhr.open("GET", "http://example.com/waveforms/track.dat");
500
+ * xhr.responseType = "arraybuffer";
501
+ *
502
+ * xhr.addEventListener("load", function onResponse(progressEvent){
503
+ * var waveform = WaveformData.create(progressEvent.target);
504
+ *
505
+ * console.log(waveform.duration);
506
+ * });
507
+ *
508
+ * xhr.send();
509
+ * ```
510
+ *
511
+ * @static
512
+ * @throws TypeError
513
+ * @param {XMLHttpRequest|Mixed} data
514
+ * @return {WaveformData}
515
+ */
516
+ WaveformData.create = function createFromResponseData(data){
517
+ var adapter = null;
518
+ var xhrData = null;
519
+
520
+ if (data && typeof data === "object" && ("responseText" in data || "response" in data)){
521
+ xhrData = ("responseType" in data) ? data.response : (data.responseText || data.response);
522
+ }
523
+
524
+ Object.keys(WaveformData.adapters).some(function(adapter_id){
525
+ if (WaveformData.adapters[adapter_id].isCompatible(xhrData || data)){
526
+ adapter = WaveformData.adapters[adapter_id];
527
+ return true;
528
+ }
529
+ });
530
+
531
+ if (adapter === null){
532
+ throw new TypeError("Could not detect a WaveformData adapter from the input.");
533
+ }
534
+
535
+ return new WaveformData(xhrData || data, adapter);
536
+ };
537
+
538
+ /**
539
+ * Public API for the Waveform Data manager.
540
+ *
541
+ * @namespace WaveformData
542
+ */
543
+ WaveformData.prototype = {
544
+ /**
545
+ * Clamp an offset of data upon the whole response body.
546
+ * Pros: it's just a reference, not a new array. So it's fast.
547
+ *
548
+ * ```javascript
549
+ * var waveform = WaveformData.create({ ... });
550
+ *
551
+ * console.log(waveform.offset_length); // -> 150
552
+ * console.log(waveform.min[0]); // -> -12
553
+ *
554
+ * waveform.offset(20, 50);
555
+ *
556
+ * console.log(waveform.min.length); // -> 30
557
+ * console.log(waveform.min[0]); // -> -9
558
+ * ```
559
+ *
560
+ * @param {Integer} start New beginning of the offset. (inclusive)
561
+ * @param {Integer} end New ending of the offset (exclusive)
562
+ */
563
+ offset: function(start, end){
564
+ var data_length = this.adapter.length;
565
+
566
+ if (end < 0){
567
+ throw new RangeError("End point must be non-negative [" + Number(end) + " < 0]");
568
+ }
569
+
570
+ if (end <= start){
571
+ throw new RangeError("We can't end prior to the starting point [" + Number(end) + " <= " + Number(start) + "]");
572
+ }
573
+
574
+ if (start < 0){
575
+ throw new RangeError("Start point must be non-negative [" + Number(start) + " < 0]");
576
+ }
577
+
578
+ if (start >= data_length){
579
+ throw new RangeError("Start point must be within range [" + Number(start) + " >= " + data_length + "]");
580
+ }
581
+
582
+ if (end > data_length){
583
+ end = data_length;
584
+ }
585
+
586
+ this.offset_start = start;
587
+ this.offset_end = end;
588
+ this.offset_length = end - start;
589
+ },
590
+ /**
591
+ * Creates a new segment of data.
592
+ * Pretty handy if you need to bookmark a duration and display it according to the current offset.
593
+ *
594
+ * ```javascript
595
+ * var waveform = WaveformData.create({ ... });
596
+ *
597
+ * console.log(Object.keys(waveform.segments)); // -> []
598
+ *
599
+ * waveform.set_segment(10, 120);
600
+ * waveform.set_segment(30, 90, "speakerA");
601
+ *
602
+ * console.log(Object.keys(waveform.segments)); // -> ['default', 'speakerA']
603
+ * console.log(waveform.segments.default.min.length); // -> 110
604
+ * console.log(waveform.segments.speakerA.min.length); // -> 60
605
+ * ```
606
+ *
607
+ * @param {Integer} start Beginning of the segment (inclusive)
608
+ * @param {Integer} end Ending of the segment (exclusive)
609
+ * @param {String*} identifier Unique identifier. If nothing is specified, *default* will be used as a value.
610
+ * @return {WaveformDataSegment}
611
+ */
612
+ set_segment: function setSegment(start, end, identifier){
613
+ identifier = identifier || "default";
614
+
615
+ this.segments[identifier] = new WaveformDataSegment(this, start, end);
616
+
617
+ return this.segments[identifier];
618
+ },
619
+ /**
620
+ * Creates a new point of data.
621
+ * Pretty handy if you need to bookmark a specific point and display it according to the current offset.
622
+ *
623
+ * ```javascript
624
+ * var waveform = WaveformData.create({ ... });
625
+ *
626
+ * console.log(Object.keys(waveform.points)); // -> []
627
+ *
628
+ * waveform.set_point(10);
629
+ * waveform.set_point(30, "speakerA");
630
+ *
631
+ * console.log(Object.keys(waveform.points)); // -> ['default', 'speakerA']
632
+ * ```
633
+ *
634
+ * @param {Integer} timeStamp the time to place the bookmark
635
+ * @param {String*} identifier Unique identifier. If nothing is specified, *default* will be used as a value.
636
+ * @return {WaveformDataPoint}
637
+ */
638
+ set_point: function setPoint(timeStamp, identifier){
639
+ if(identifier === undefined || identifier === null || identifier.length === 0) {
640
+ identifier = "default";
641
+ }
642
+
643
+ this.points[identifier] = new WaveformDataPoint(this, timeStamp);
644
+
645
+ return this.points[identifier];
646
+ },
647
+ /**
648
+ * Removes a point of data.
649
+ *
650
+ * ```javascript
651
+ * var waveform = WaveformData.create({ ... });
652
+ *
653
+ * console.log(Object.keys(waveform.points)); // -> []
654
+ *
655
+ * waveform.set_point(30, "speakerA");
656
+ * console.log(Object.keys(waveform.points)); // -> ['speakerA']
657
+ * waveform.remove_point("speakerA");
658
+ * console.log(Object.keys(waveform.points)); // -> []
659
+ * ```
660
+ *
661
+ * @param {String*} identifier Unique identifier. If nothing is specified, *default* will be used as a value.
662
+ * @return null
663
+ */
664
+ remove_point: function removePoint(identifier) {
665
+ if(this.points[identifier]) {
666
+ delete this.points[identifier];
667
+ }
668
+ },
669
+ /**
670
+ * Creates a new WaveformData object with resampled data.
671
+ * Returns a rescaled waveform, to either fit the waveform to a specific width, or to a specific zoom level.
672
+ *
673
+ * **Note**: You may specify either the *width* or the *scale*, but not both. The `scale` will be deduced from the `width` you want to fit the data into.
674
+ *
675
+ * Adapted from Sequence::GetWaveDisplay in Audacity, with permission.
676
+ *
677
+ * ```javascript
678
+ * // ...
679
+ * var waveform = WaveformData.create({ ... });
680
+ *
681
+ * // fitting the data in a 500px wide canvas
682
+ * var resampled_waveform = waveform.resample({ width: 500 });
683
+ *
684
+ * console.log(resampled_waveform.min.length); // -> 500
685
+ *
686
+ * // zooming out on a 3 times less precise scale
687
+ * var resampled_waveform = waveform.resample({ scale: waveform.adapter.scale * 3 });
688
+ *
689
+ * // partial resampling (to perform fast animations involving a resampling per animation frame)
690
+ * var partially_resampled_waveform = waveform.resample({ width: 500, from: 0, to: 500 });
691
+ *
692
+ * // ...
693
+ * ```
694
+ *
695
+ * @see https://code.google.com/p/audacity/source/browse/audacity-src/trunk/src/Sequence.cpp
696
+ * @param {Number|{width: Number, scale: Number}} options Either a constraint width or a constraint sample rate
697
+ * @return {WaveformData} New resampled object
698
+ */
699
+ resample: function(options){
700
+ if (typeof options === 'number'){
701
+ options = {
702
+ width: options
703
+ };
704
+ }
705
+
706
+ options.input_index = typeof options.input_index === 'number' ? options.input_index : null;
707
+ options.output_index = typeof options.output_index === 'number' ? options.output_index : null;
708
+ options.scale = typeof options.scale === 'number' ? options.scale : null;
709
+ options.width = typeof options.width === 'number' ? options.width : null;
710
+
711
+ var is_partial_resampling = Boolean(options.input_index) || Boolean(options.output_index);
712
+
713
+ if (options.input_index !== null && (options.input_index >= 0) === false){
714
+ throw new RangeError('options.input_index should be a positive integer value. ['+ options.input_index +']');
715
+ }
716
+
717
+ if (options.output_index !== null && (options.output_index >= 0) === false){
718
+ throw new RangeError('options.output_index should be a positive integer value. ['+ options.output_index +']');
719
+ }
720
+
721
+ if (options.width !== null && (options.width > 0) === false){
722
+ throw new RangeError('options.width should be a strictly positive integer value. ['+ options.width +']');
723
+ }
724
+
725
+ if (options.scale !== null && (options.scale > 0) === false){
726
+ throw new RangeError('options.scale should be a strictly positive integer value. ['+ options.scale +']');
727
+ }
728
+
729
+ if (!options.scale && !options.width){
730
+ throw new RangeError('You should provide either a resampling scale or a width in pixel the data should fit in.');
731
+ }
732
+
733
+ var definedPartialOptionsCount = ['width', 'scale', 'output_index', 'input_index'].reduce(function(count, key){
734
+ return count + (options[key] === null ? 0 : 1);
735
+ }, 0);
736
+
737
+ if (is_partial_resampling && definedPartialOptionsCount !== 4) {
738
+ throw new Error('Some partial resampling options are missing. You provided ' + definedPartialOptionsCount + ' of them over 4.');
739
+ }
740
+
741
+ var output_data = [];
742
+ var samples_per_pixel = options.scale || Math.floor(this.duration * this.adapter.sample_rate / options.width); //scale we want to reach
743
+ var scale = this.adapter.scale; //scale we are coming from
744
+ var channel_count = 2;
745
+
746
+ var input_buffer_size = this.adapter.length; //the amount of data we want to resample i.e. final zoom want to resample all data but for intermediate zoom we want to resample subset
747
+ var input_index = options.input_index || 0; //is this start point? or is this the index at current scale
748
+ var output_index = options.output_index || 0; //is this end point? or is this the index at scale we want to be?
749
+ var min = input_buffer_size ? this.min_sample(input_index) : 0; //min value for peak in waveform
750
+ var max = input_buffer_size ? this.max_sample(input_index) : 0; //max value for peak in waveform
751
+ var min_value = -128;
752
+ var max_value = 127;
753
+
754
+ if (samples_per_pixel < scale){
755
+ throw new Error("Zoom level "+samples_per_pixel+" too low, minimum: "+scale);
756
+ }
757
+
758
+ var where, prev_where, stop, value, last_input_index;
759
+
760
+ var sample_at_pixel = function sample_at_pixel(x){
761
+ return Math.floor(x * samples_per_pixel);
762
+ };
763
+
764
+ var add_sample = function add_sample(min, max){
765
+ output_data.push(min, max);
766
+ };
767
+
768
+ while (input_index < input_buffer_size) {
769
+ while (Math.floor(sample_at_pixel(output_index) / scale) <= input_index){
770
+ if (output_index){
771
+ add_sample(min, max);
772
+ }
773
+
774
+ last_input_index = input_index;
775
+
776
+ output_index++;
777
+
778
+ where = sample_at_pixel(output_index);
779
+ prev_where = sample_at_pixel(output_index - 1);
780
+
781
+ if (where !== prev_where){
782
+ min = max_value;
783
+ max = min_value;
784
+ }
785
+ }
786
+
787
+ where = sample_at_pixel(output_index);
788
+ stop = Math.floor(where / scale);
789
+
790
+ if (stop > input_buffer_size){
791
+ stop = input_buffer_size;
792
+ }
793
+
794
+ while (input_index < stop){
795
+ value = this.min_sample(input_index);
796
+
797
+ if (value < min){
798
+ min = value;
799
+ }
800
+
801
+ value = this.max_sample(input_index);
802
+
803
+ if (value > max){
804
+ max = value;
805
+ }
806
+
807
+ input_index++;
808
+ }
809
+
810
+ if (is_partial_resampling && (output_data.length / channel_count) >= options.width) {
811
+ break;
812
+ }
813
+ }
814
+
815
+ if (is_partial_resampling) {
816
+ if ((output_data.length / channel_count) > options.width && input_index !== last_input_index){
817
+ add_sample(min, max);
818
+ }
819
+ }
820
+ else if(input_index !== last_input_index){
821
+ add_sample(min, max);
822
+ }
823
+
824
+ return new WaveformData({
825
+ version: this.adapter.version,
826
+ samples_per_pixel: samples_per_pixel,
827
+ length: output_data.length / channel_count,
828
+ data: output_data,
829
+ sample_rate: this.adapter.sample_rate
830
+ }, WaveformData.adapters.object);
831
+ },
832
+ /**
833
+ * Returns all the min peaks values.
834
+ *
835
+ * ```javascript
836
+ * var waveform = WaveformData.create({ ... });
837
+ *
838
+ * console.log(waveform.min.length); // -> 150
839
+ * console.log(waveform.min[0]); // -> -12
840
+ *
841
+ * waveform.offset(20, 50);
842
+ *
843
+ * console.log(waveform.min.length); // -> 30
844
+ * console.log(waveform.min[0]); // -> -9
845
+ * ```
846
+ *
847
+ * @api
848
+ * @return {Array.<Integer>} Min values contained in the offset.
849
+ */
850
+ get min(){
851
+ return this.offsetValues(this.offset_start, this.offset_length, 0);
852
+ },
853
+ /**
854
+ * Returns all the max peaks values.
855
+ *
856
+ * ```javascript
857
+ * var waveform = WaveformData.create({ ... });
858
+ *
859
+ * console.log(waveform.max.length); // -> 150
860
+ * console.log(waveform.max[0]); // -> 12
861
+ *
862
+ * waveform.offset(20, 50);
863
+ *
864
+ * console.log(waveform.max.length); // -> 30
865
+ * console.log(waveform.max[0]); // -> 5
866
+ * ```
867
+ *
868
+ * @api
869
+ * @return {Array.<Integer>} Max values contained in the offset.
870
+ */
871
+ get max(){
872
+ return this.offsetValues(this.offset_start, this.offset_length, 1);
873
+ },
874
+ /**
875
+ * Return the unpacked values for a particular offset.
876
+ *
877
+ * @param {Integer} start
878
+ * @param {Integer} length
879
+ * @param {Integer} correction The step to skip for each iteration (as the response body is [min, max, min, max...])
880
+ * @return {Array.<Integer>}
881
+ */
882
+ offsetValues: function getOffsetValues(start, length, correction){
883
+ var adapter = this.adapter;
884
+ var values = [];
885
+
886
+ correction += (start * 2); //offsetting the positioning query
887
+
888
+ for (var i = 0; i < length; i++){
889
+ values.push(adapter.at((i * 2) + correction));
890
+ }
891
+
892
+ return values;
893
+ },
894
+ /**
895
+ * Compute the duration in seconds of the audio file.
896
+ *
897
+ * ```javascript
898
+ * var waveform = WaveformData.create({ ... });
899
+ * console.log(waveform.duration); // -> 10.33333333333
900
+ *
901
+ * waveform.offset(20, 50);
902
+ * console.log(waveform.duration); // -> 10.33333333333
903
+ * ```
904
+ *
905
+ * @api
906
+ * @return {number} Duration of the audio waveform, in seconds.
907
+ */
908
+ get duration(){
909
+ return (this.adapter.length * this.adapter.scale) / this.adapter.sample_rate;
910
+ },
911
+ /**
912
+ * Return the duration in seconds of the current offset.
913
+ *
914
+ * ```javascript
915
+ * var waveform = WaveformData.create({ ... });
916
+ *
917
+ * console.log(waveform.offset_duration); // -> 10.33333333333
918
+ *
919
+ * waveform.offset(20, 50);
920
+ *
921
+ * console.log(waveform.offset_duration); // -> 2.666666666667
922
+ * ```
923
+ *
924
+ * @api
925
+ * @return {number} Duration of the offset, in seconds.
926
+ */
927
+ get offset_duration(){
928
+ return (this.offset_length * this.adapter.scale) / this.adapter.sample_rate;
929
+ },
930
+ /**
931
+ * Return the number of pixels per second.
932
+ *
933
+ * ```javascript
934
+ * var waveform = WaveformData.create({ ... });
935
+ *
936
+ * console.log(waveform.pixels_per_second); // -> 93.75
937
+ * ```
938
+ *
939
+ * @api
940
+ * @return {number} Number of pixels per second.
941
+ */
942
+ get pixels_per_second(){
943
+ return this.adapter.sample_rate / this.adapter.scale;
944
+ },
945
+ /**
946
+ * Return the amount of time represented by a single pixel.
947
+ *
948
+ * ```javascript
949
+ * var waveform = WaveformData.create({ ... });
950
+ *
951
+ * console.log(waveform.seconds_per_pixel); // -> 0.010666666666666666
952
+ * ```
953
+ *
954
+ * @return {number} Amount of time (in seconds) contained in a pixel.
955
+ */
956
+ get seconds_per_pixel(){
957
+ return this.adapter.scale / this.adapter.sample_rate;
958
+ },
959
+ /**
960
+ * Returns a value at a specific offset.
961
+ *
962
+ * ```javascript
963
+ * var waveform = WaveformData.create({ ... });
964
+ *
965
+ * console.log(waveform.at(20)); // -> -7
966
+ * console.log(waveform.at(21)); // -> 12
967
+ * ```
968
+ *
969
+ * @proxy
970
+ * @param {Integer} index
971
+ * @return {number} Offset value
972
+ */
973
+ at: function at_sample_proxy(index){
974
+ return this.adapter.at(index);
975
+ },
976
+ /**
977
+ * Return the pixel location for a certain time.
978
+ *
979
+ * ```javascript
980
+ * var waveform = WaveformData.create({ ... });
981
+ *
982
+ * console.log(waveform.at_time(0.0000000023)); // -> 10
983
+ * ```
984
+ * @param {number} time
985
+ * @return {integer} Index location for a specific time.
986
+ */
987
+ at_time: function at_time(time){
988
+ return Math.floor((time * this.adapter.sample_rate) / this.adapter.scale);
989
+ },
990
+ /**
991
+ * Returns the time in seconds for a particular index
992
+ *
993
+ * ```javascript
994
+ * var waveform = WaveformData.create({ ... });
995
+ *
996
+ * console.log(waveform.time(10)); // -> 0.0000000023
997
+ * ```
998
+ *
999
+ * @param {Integer} index
1000
+ * @return {number}
1001
+ */
1002
+ time: function time(index){
1003
+ return index * this.seconds_per_pixel;
1004
+ },
1005
+ /**
1006
+ * Return if a pixel lies within the current offset.
1007
+ *
1008
+ * ```javascript
1009
+ * var waveform = WaveformData.create({ ... });
1010
+ *
1011
+ * console.log(waveform.in_offset(50)); // -> true
1012
+ * console.log(waveform.in_offset(120)); // -> true
1013
+ *
1014
+ * waveform.offset(100, 150);
1015
+ *
1016
+ * console.log(waveform.in_offset(50)); // -> false
1017
+ * console.log(waveform.in_offset(120)); // -> true
1018
+ * ```
1019
+ *
1020
+ * @param {number} pixel
1021
+ * @return {boolean} True if the pixel lies in the current offset, false otherwise.
1022
+ */
1023
+ in_offset: function isInOffset(pixel){
1024
+ return pixel >= this.offset_start && pixel < this.offset_end;
1025
+ },
1026
+ /**
1027
+ * Returns a min value for a specific offset.
1028
+ *
1029
+ * ```javascript
1030
+ * var waveform = WaveformData.create({ ... });
1031
+ *
1032
+ * console.log(waveform.min_sample(10)); // -> -7
1033
+ * ```
1034
+ *
1035
+ * @param {Integer} offset
1036
+ * @return {Number} Offset min value
1037
+ */
1038
+ min_sample: function getMinValue(offset){
1039
+ return this.adapter.at(offset * 2);
1040
+ },
1041
+ /**
1042
+ * Returns a max value for a specific offset.
1043
+ *
1044
+ * ```javascript
1045
+ * var waveform = WaveformData.create({ ... });
1046
+ *
1047
+ * console.log(waveform.max_sample(10)); // -> 12
1048
+ * ```
1049
+ *
1050
+ * @param {Integer} offset
1051
+ * @return {Number} Offset max value
1052
+ */
1053
+ max_sample: function getMaxValue(offset){
1054
+ return this.adapter.at((offset * 2) + 1);
1055
+ }
1056
+ };
1057
+
1058
+ /**
1059
+ * Available adapters to manage the data backends.
1060
+ *
1061
+ * @type {Object}
1062
+ */
1063
+ WaveformData.adapters = {};
1064
+
1065
+
1066
+ /**
1067
+ * WaveformData Adapter Structure
1068
+ *
1069
+ * @typedef {{from: Number, to: Number, platforms: {}}}
1070
+ */
1071
+ WaveformData.adapter = function WaveformDataAdapter(response_data){
1072
+ this.data = response_data;
1073
+ };
1074
+
1075
+ },{"./point.js":7,"./segment.js":8}],7:[function(require,module,exports){
1076
+ "use strict";
1077
+
1078
+ /**
1079
+ * Points are an easy way to keep track bookmarks of the described audio file.
1080
+ *
1081
+ * They return values based on the actual offset. Which means if you change your offset and:
1082
+ *
1083
+ * * a point becomes **out of scope**, no data will be returned;
1084
+ * * a point is **fully included in the offset**, its whole content will be returned.
1085
+ *
1086
+ * Points are created with the `WaveformData.set_point(timeStamp, name?)` method.
1087
+ *
1088
+ * @see WaveformData.prototype.set_point
1089
+ * @param {WaveformData} context WaveformData instance
1090
+ * @param {Integer} start Initial start index
1091
+ * @param {Integer} end Initial end index
1092
+ * @constructor
1093
+ */
1094
+ var WaveformDataPoint = module.exports = function WaveformDataPoint(context, timeStamp){
1095
+ this.context = context;
1096
+
1097
+ /**
1098
+ * Start index.
1099
+ *
1100
+ * ```javascript
1101
+ * var waveform = new WaveformData({ ... }, WaveformData.adapters.object);
1102
+ * waveform.set_point(10, "example");
1103
+ *
1104
+ * console.log(waveform.points.example.timeStamp); // -> 10
1105
+ *
1106
+ * waveform.offset(20, 50);
1107
+ * console.log(waveform.points.example.timeStamp); // -> 10
1108
+ *
1109
+ * waveform.offset(70, 100);
1110
+ * console.log(waveform.points.example.timeStamp); // -> 10
1111
+ * ```
1112
+ * @type {Integer} Time Stamp of the point
1113
+ */
1114
+ this.timeStamp = timeStamp;
1115
+ };
1116
+
1117
+ /**
1118
+ * @namespace WaveformDataPoint
1119
+ */
1120
+ WaveformDataPoint.prototype = {
1121
+ /**
1122
+ * Indicates if the point has some visible part in the actual WaveformData offset.
1123
+ *
1124
+ * ```javascript
1125
+ * var waveform = new WaveformData({ ... }, WaveformData.adapters.object);
1126
+ * waveform.set_point(10, "example");
1127
+ *
1128
+ * console.log(waveform.points.example.visible); // -> true
1129
+ *
1130
+ * waveform.offset(0, 50);
1131
+ * console.log(waveform.points.example.visible); // -> true
1132
+ *
1133
+ * waveform.offset(70, 100);
1134
+ * console.log(waveform.points.example.visible); // -> false
1135
+ * ```
1136
+ *
1137
+ * @return {Boolean} True if visible, false otherwise.
1138
+ */
1139
+ get visible(){
1140
+ return this.context.in_offset(this.timeStamp);
1141
+ }
1142
+ };
1143
+ },{}],8:[function(require,module,exports){
1144
+ "use strict";
1145
+
1146
+ /**
1147
+ * Segments are an easy way to keep track of portions of the described audio file.
1148
+ *
1149
+ * They return values based on the actual offset. Which means if you change your offset and:
1150
+ *
1151
+ * * a segment becomes **out of scope**, no data will be returned;
1152
+ * * a segment is only **partially included in the offset**, only the visible parts will be returned;
1153
+ * * a segment is **fully included in the offset**, its whole content will be returned.
1154
+ *
1155
+ * Segments are created with the `WaveformData.set_segment(from, to, name?)` method.
1156
+ *
1157
+ * @see WaveformData.prototype.set_segment
1158
+ * @param {WaveformData} context WaveformData instance
1159
+ * @param {Integer} start Initial start index
1160
+ * @param {Integer} end Initial end index
1161
+ * @constructor
1162
+ */
1163
+ var WaveformDataSegment = module.exports = function WaveformDataSegment(context, start, end){
1164
+ this.context = context;
1165
+
1166
+ /**
1167
+ * Start index.
1168
+ *
1169
+ * ```javascript
1170
+ * var waveform = new WaveformData({ ... }, WaveformData.adapters.object);
1171
+ * waveform.set_segment(10, 50, "example");
1172
+ *
1173
+ * console.log(waveform.segments.example.start); // -> 10
1174
+ *
1175
+ * waveform.offset(20, 50);
1176
+ * console.log(waveform.segments.example.start); // -> 10
1177
+ *
1178
+ * waveform.offset(70, 100);
1179
+ * console.log(waveform.segments.example.start); // -> 10
1180
+ * ```
1181
+ * @type {Integer} Initial starting point of the segment.
1182
+ */
1183
+ this.start = start;
1184
+
1185
+ /**
1186
+ * End index.
1187
+ *
1188
+ * ```javascript
1189
+ * var waveform = new WaveformData({ ... }, WaveformData.adapters.object);
1190
+ * waveform.set_segment(10, 50, "example");
1191
+ *
1192
+ * console.log(waveform.segments.example.end); // -> 50
1193
+ *
1194
+ * waveform.offset(20, 50);
1195
+ * console.log(waveform.segments.example.end); // -> 50
1196
+ *
1197
+ * waveform.offset(70, 100);
1198
+ * console.log(waveform.segments.example.end); // -> 50
1199
+ * ```
1200
+ * @type {Integer} Initial ending point of the segment.
1201
+ */
1202
+ this.end = end;
1203
+ };
1204
+
1205
+ /**
1206
+ * @namespace WaveformDataSegment
1207
+ */
1208
+ WaveformDataSegment.prototype = {
1209
+ /**
1210
+ * Dynamic starting point based on the WaveformData instance offset.
1211
+ *
1212
+ * ```javascript
1213
+ * var waveform = new WaveformData({ ... }, WaveformData.adapters.object);
1214
+ * waveform.set_segment(10, 50, "example");
1215
+ *
1216
+ * console.log(waveform.segments.example.offset_start); // -> 10
1217
+ *
1218
+ * waveform.offset(20, 50);
1219
+ * console.log(waveform.segments.example.offset_start); // -> 20
1220
+ *
1221
+ * waveform.offset(70, 100);
1222
+ * console.log(waveform.segments.example.offset_start); // -> null
1223
+ * ```
1224
+ *
1225
+ * @return {number} Starting point of the segment within the waveform offset. (inclusive)
1226
+ */
1227
+ get offset_start(){
1228
+ if (this.start < this.context.offset_start && this.end > this.context.offset_start){
1229
+ return this.context.offset_start;
1230
+ }
1231
+
1232
+ if (this.start >= this.context.offset_start && this.start < this.context.offset_end){
1233
+ return this.start;
1234
+ }
1235
+
1236
+ return null;
1237
+ },
1238
+ /**
1239
+ * Dynamic ending point based on the WaveformData instance offset.
1240
+ *
1241
+ * ```javascript
1242
+ * var waveform = new WaveformData({ ... }, WaveformData.adapters.object);
1243
+ * waveform.set_segment(10, 50, "example");
1244
+ *
1245
+ * console.log(waveform.segments.example.offset_end); // -> 50
1246
+ *
1247
+ * waveform.offset(20, 50);
1248
+ * console.log(waveform.segments.example.offset_end); // -> 50
1249
+ *
1250
+ * waveform.offset(70, 100);
1251
+ * console.log(waveform.segments.example.offset_end); // -> null
1252
+ * ```
1253
+ *
1254
+ * @return {number} Ending point of the segment within the waveform offset. (exclusive)
1255
+ */
1256
+ get offset_end(){
1257
+ if (this.end > this.context.offset_start && this.end <= this.context.offset_end){
1258
+ return this.end;
1259
+ }
1260
+
1261
+ if (this.end > this.context.offset_end && this.start < this.context.offset_end){
1262
+ return this.context.offset_end;
1263
+ }
1264
+
1265
+ return null;
1266
+ },
1267
+ /**
1268
+ * Dynamic segment length based on the WaveformData instance offset.
1269
+ *
1270
+ * ```javascript
1271
+ * var waveform = new WaveformData({ ... }, WaveformData.adapters.object);
1272
+ * waveform.set_segment(10, 50, "example");
1273
+ *
1274
+ * console.log(waveform.segments.example.offset_length); // -> 40
1275
+ *
1276
+ * waveform.offset(20, 50);
1277
+ * console.log(waveform.segments.example.offset_length); // -> 30
1278
+ *
1279
+ * waveform.offset(70, 100);
1280
+ * console.log(waveform.segments.example.offset_length); // -> 0
1281
+ * ```
1282
+ *
1283
+ * @return {number} Visible length of the segment within the waveform offset.
1284
+ */
1285
+ get offset_length(){
1286
+ return this.offset_end - this.offset_start;
1287
+ },
1288
+ /**
1289
+ * Initial length of the segment.
1290
+ *
1291
+ * ```javascript
1292
+ * var waveform = new WaveformData({ ... }, WaveformData.adapters.object);
1293
+ * waveform.set_segment(10, 50, "example");
1294
+ *
1295
+ * console.log(waveform.segments.example.length); // -> 40
1296
+ *
1297
+ * waveform.offset(20, 50);
1298
+ * console.log(waveform.segments.example.length); // -> 40
1299
+ *
1300
+ * waveform.offset(70, 100);
1301
+ * console.log(waveform.segments.example.length); // -> 40
1302
+ * ```
1303
+ *
1304
+ * @return {number} Initial length of the segment.
1305
+ */
1306
+ get length(){
1307
+ return this.end - this.start;
1308
+ },
1309
+ /**
1310
+ * Indicates if the segment has some visible part in the actual WaveformData offset.
1311
+ *
1312
+ * ```javascript
1313
+ * var waveform = new WaveformData({ ... }, WaveformData.adapters.object);
1314
+ * waveform.set_segment(10, 50, "example");
1315
+ *
1316
+ * console.log(waveform.segments.example.visible); // -> true
1317
+ *
1318
+ * waveform.offset(20, 50);
1319
+ * console.log(waveform.segments.example.visible); // -> true
1320
+ *
1321
+ * waveform.offset(70, 100);
1322
+ * console.log(waveform.segments.example.visible); // -> false
1323
+ * ```
1324
+ *
1325
+ * @return {Boolean} True if at least partly visible, false otherwise.
1326
+ */
1327
+ get visible(){
1328
+ return this.context.in_offset(this.start) || this.context.in_offset(this.end) || (this.context.offset_start > this.start && this.context.offset_start < this.end);
1329
+ },
1330
+ /**
1331
+ * Return the minimum values for the segment.
1332
+ *
1333
+ * ```javascript
1334
+ * var waveform = new WaveformData({ ... }, WaveformData.adapters.object);
1335
+ * waveform.set_segment(10, 50, "example");
1336
+ *
1337
+ * console.log(waveform.segments.example.min.length); // -> 40
1338
+ * console.log(waveform.segments.example.min.offset_length); // -> 40
1339
+ * console.log(waveform.segments.example.min[0]); // -> -12
1340
+ *
1341
+ * waveform.offset(20, 50);
1342
+ *
1343
+ * console.log(waveform.segments.example.min.length); // -> 40
1344
+ * console.log(waveform.segments.example.min.offset_length); // -> 30
1345
+ * console.log(waveform.segments.example.min[0]); // -> -5
1346
+ * ```
1347
+ *
1348
+ * @return {Array.<Integer>} Min values of the segment.
1349
+ */
1350
+ get min(){
1351
+ return this.visible ? this.context.offsetValues(this.offset_start, this.offset_length, 0) : [];
1352
+ },
1353
+ /**
1354
+ * Return the maximum values for the segment.
1355
+ *
1356
+ * ```javascript
1357
+ * var waveform = new WaveformData({ ... }, WaveformData.adapters.object);
1358
+ * waveform.set_segment(10, 50, "example");
1359
+ *
1360
+ * console.log(waveform.segments.example.max.length); // -> 40
1361
+ * console.log(waveform.segments.example.max.offset_length); // -> 40
1362
+ * console.log(waveform.segments.example.max[0]); // -> 5
1363
+ *
1364
+ * waveform.offset(20, 50);
1365
+ *
1366
+ * console.log(waveform.segments.example.max.length); // -> 40
1367
+ * console.log(waveform.segments.example.max.offset_length); // -> 30
1368
+ * console.log(waveform.segments.example.max[0]); // -> 11
1369
+ * ```
1370
+ *
1371
+ * @return {Array.<Integer>} Max values of the segment.
1372
+ */
1373
+ get max(){
1374
+ return this.visible ? this.context.offsetValues(this.offset_start, this.offset_length, 1) : [];
1375
+ }
1376
+ };
1377
+ },{}],9:[function(require,module,exports){
1378
+ var window = require('global/window');
1379
+
1380
+ var Context = window.AudioContext || window.webkitAudioContext;
1381
+ if (Context) module.exports = new Context;
1382
+
1383
+ },{"global/window":10}],10:[function(require,module,exports){
1384
+ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {};if (typeof window !== "undefined") {
1385
+ module.exports = window;
1386
+ } else if (typeof global !== "undefined") {
1387
+ module.exports = global;
1388
+ } else {
1389
+ module.exports = {};
1390
+ }
1391
+
1392
+ },{}],11:[function(require,module,exports){
1393
+ "use strict";
1394
+
1395
+ var WaveformData = require("./lib/core");
1396
+ WaveformData.adapters = require("./lib/adapters");
1397
+
1398
+ WaveformData.builders = {
1399
+ webaudio: require("./lib/builders/webaudio.js")
1400
+ };
1401
+
1402
+ module.exports = WaveformData;
1403
+ },{"./lib/adapters":2,"./lib/builders/webaudio.js":5,"./lib/core":6}]},{},[11])
1404
+ (11)
1405
+ });
1406
+ ;