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.
- checksums.yaml +7 -0
- data/COPYING +674 -0
- data/COPYING.LESSER +165 -0
- data/README.md +32 -0
- data/lib/waveform_data_js_rails.rb +5 -0
- data/lib/waveform_data_js_rails/version.rb +3 -0
- data/vendor/assets/javascripts/waveform-data.js +1406 -0
- metadata +94 -0
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,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
|
+
;
|