audiomotion-analyzer 4.3.0 → 4.4.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.
- package/README.md +78 -15
- package/package.json +1 -1
- package/src/audioMotion-analyzer.js +150 -81
- package/src/index.d.ts +12 -0
package/README.md
CHANGED
|
@@ -86,13 +86,20 @@ import AudioMotionAnalyzer from 'audiomotion-analyzer';
|
|
|
86
86
|
|
|
87
87
|
## Constructor
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
```js
|
|
90
|
+
new AudioMotionAnalyzer()
|
|
91
|
+
new AudioMotionAnalyzer( container )
|
|
92
|
+
new AudioMotionAnalyzer( container, {options} )
|
|
93
|
+
new AudioMotionAnalyzer( {options} )
|
|
94
|
+
```
|
|
90
95
|
|
|
91
96
|
Creates a new instance of **audioMotion-analyzer**.
|
|
92
97
|
|
|
93
|
-
|
|
98
|
+
`container` is the DOM element into which the canvas created for the analyzer should be inserted.
|
|
94
99
|
|
|
95
|
-
If `
|
|
100
|
+
If not defined, defaults to `document.body`, unless [`canvas`](#canvas-htmlcanvaselement-object) is defined in the options, in which case its parent element will be considered the container.
|
|
101
|
+
|
|
102
|
+
`options` must be an [Options object](#options-object).
|
|
96
103
|
|
|
97
104
|
Usage example:
|
|
98
105
|
|
|
@@ -107,11 +114,14 @@ const audioMotion = new AudioMotionAnalyzer(
|
|
|
107
114
|
|
|
108
115
|
This will insert the analyzer canvas inside the *#container* element and start the visualization of audio coming from the *#audio* element.
|
|
109
116
|
|
|
117
|
+
?> By default, audioMotion will try to use all available container space for the canvas. To prevent it from growing indefinitely, you must either constrain the dimensions of the container via CSS or explicitly define [`height`](#height-number) and/or [`width`](#width-number) properties in the constructor [options](#options-object).
|
|
118
|
+
|
|
110
119
|
### Options object
|
|
111
120
|
|
|
112
121
|
Valid properties and default values are shown below.
|
|
113
122
|
|
|
114
|
-
Properties marked as *constructor only* can only be set
|
|
123
|
+
Properties marked as *constructor only* can only be set in the constructor call, the others can also be set anytime via [`setOptions()`](#setoptions-options-) method or
|
|
124
|
+
directly as [properties](#properties) of the audioMotion instance.
|
|
115
125
|
|
|
116
126
|
options = {<br>
|
|
117
127
|
  [alphaBars](#alphabars-boolean): **false**,<br>
|
|
@@ -119,6 +129,7 @@ options = {<br>
|
|
|
119
129
|
  [audioCtx](#audioctx-audiocontext-object): *undefined*, // constructor only<br>
|
|
120
130
|
  [barSpace](#barspace-number): **0.1**,<br>
|
|
121
131
|
  [bgAlpha](#bgalpha-number): **0.7**,<br>
|
|
132
|
+
  [canvas](#canvas-htmlcanvaselement-object): *undefined*, // constructor only<br>
|
|
122
133
|
  [channelLayout](#channellayout-string): **'single'**,<br>
|
|
123
134
|
  [colorMode](#colormode-string): **'gradient'**,<br>
|
|
124
135
|
  [connectSpeakers](#connectspeakers-boolean): **true**, // constructor only<br>
|
|
@@ -150,6 +161,8 @@ options = {<br>
|
|
|
150
161
|
  [overlay](#overlay-boolean): **false**,<br>
|
|
151
162
|
  [peakLine](#peakline-boolean): **false**,<br>
|
|
152
163
|
  [radial](#radial-boolean): **false**,<br>
|
|
164
|
+
  [radialInvert](#radialinvert-boolean): **false**,<br>
|
|
165
|
+
  [radius](#radius-number): **0.3**,<br>
|
|
153
166
|
  [reflexAlpha](#reflexalpha-number): **0.15**,<br>
|
|
154
167
|
  [reflexBright](#reflexbright-number): **1**,<br>
|
|
155
168
|
  [reflexFit](#reflexfit-boolean): **true**,<br>
|
|
@@ -164,7 +177,7 @@ options = {<br>
|
|
|
164
177
|
  [source](#source-htmlmediaelement-or-audionode-object): *undefined*, // constructor only<br>
|
|
165
178
|
  [spinSpeed](#spinspeed-number): **0**,<br>
|
|
166
179
|
  [splitGradient](#splitgradient-boolean): **false**,<br>
|
|
167
|
-
  [start](#start-boolean): **true
|
|
180
|
+
  [start](#start-boolean): **true**, // constructor only<br>
|
|
168
181
|
  [trueLeds](#trueleds-boolean): **false**,<br>
|
|
169
182
|
  [useCanvas](#usecanvas-boolean): **true**,<br>
|
|
170
183
|
  [volume](#volume-number): **1**,<br>
|
|
@@ -187,6 +200,14 @@ If neither is defined, a new audio context will be created. After instantiation,
|
|
|
187
200
|
|
|
188
201
|
See [this live code](https://codesandbox.io/s/9y6qb) and the [multi-instance demo](/demo/multi.html) for more usage examples.
|
|
189
202
|
|
|
203
|
+
#### `canvas` *HTMLCanvasElement object*
|
|
204
|
+
|
|
205
|
+
*Available since v4.4.0*
|
|
206
|
+
|
|
207
|
+
Allows you to provide an existing [*Canvas*](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement) where audioMotion should render its visualizations.
|
|
208
|
+
|
|
209
|
+
If not defined, a new canvas will be created. After instantiation, you can obtain its reference from the [`canvas`](#canvas-htmlcanvaselement-object-read-only) read-only property.
|
|
210
|
+
|
|
190
211
|
#### `connectSpeakers` *boolean*
|
|
191
212
|
|
|
192
213
|
*Available since v3.2.0*
|
|
@@ -321,11 +342,13 @@ Defaults to **0.7**.
|
|
|
321
342
|
|
|
322
343
|
### `canvas` *HTMLCanvasElement object* *(Read only)*
|
|
323
344
|
|
|
324
|
-
[*Canvas*](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement) element
|
|
345
|
+
[*Canvas*](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement) element where audioMotion renders its visualizations.
|
|
346
|
+
|
|
347
|
+
See also the [`canvas`](#canvas-htmlcanvaselement-object) constructor option.
|
|
325
348
|
|
|
326
349
|
### `canvasCtx` *CanvasRenderingContext2D object* *(Read only)*
|
|
327
350
|
|
|
328
|
-
[2D rendering context](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D) used for drawing in audioMotion's
|
|
351
|
+
[2D rendering context](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D) used for drawing in audioMotion's [`canvas`](#canvas-htmlcanvaselement-object-read-only).
|
|
329
352
|
|
|
330
353
|
### `channelLayout` *string*
|
|
331
354
|
|
|
@@ -480,13 +503,14 @@ See also [`gradient`](#gradient-string) and [`splitGradient`](#splitgradient-boo
|
|
|
480
503
|
|
|
481
504
|
Nominal dimensions of the analyzer.
|
|
482
505
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
This should be considered the minimum dimensions for proper visualization of all available modes and effects.
|
|
506
|
+
Setting one or both properties to **_undefined_** (default) will trigger the fluid/responsive behavior and the analyzer will try to adjust to the container's height and/or width.
|
|
507
|
+
In that case, it's important that you constrain the dimensions of the container via CSS to prevent the canvas from growing indefinitely.
|
|
486
508
|
|
|
487
509
|
You can set both values at once using the [`setCanvasSize()`](#setcanvassize-width-height-) method.
|
|
488
510
|
|
|
489
|
-
|
|
511
|
+
See also [`onCanvasResize`](#oncanvasresize-function).
|
|
512
|
+
|
|
513
|
+
?> The actual dimensions of the canvas may differ from these values, depending on the device's [pixelRatio](#pixelratio-number-read-only), the [`loRes`](#lores-boolean) setting and while in fullscreen. For the actual pixel values, read `height` and `width` directly from the [`canvas`](#canvas-htmlcanvaselement-object-read-only) object.
|
|
490
514
|
|
|
491
515
|
### `isAlphaBars` *boolean* *(Read only)*
|
|
492
516
|
|
|
@@ -771,12 +795,36 @@ In radial view, [`ledBars`](#ledbars-boolean) and [`lumiBars`](#lumibars-boolean
|
|
|
771
795
|
|
|
772
796
|
When [`channelLayout`](#channellayout-string) is set to *'dual-vertical'*, graphs for the right channel are rendered towards the center of the screen.
|
|
773
797
|
|
|
774
|
-
See also [`spinSpeed`](#spinspeed-number).
|
|
798
|
+
See also [`radialInvert`](#radialinvert-boolean), [`radius`](#radius-number) and [`spinSpeed`](#spinspeed-number).
|
|
775
799
|
|
|
776
800
|
Defaults to **false**.
|
|
777
801
|
|
|
778
802
|
!> [See related known issue](#alphabars-and-fillalpha-wont-work-with-radial-on-firefox)
|
|
779
803
|
|
|
804
|
+
### `radialInvert` *boolean*
|
|
805
|
+
|
|
806
|
+
*Available since v4.4.0*
|
|
807
|
+
|
|
808
|
+
When set to *true* (and [`radial`](#radial-boolean) is also *true*) creates a radial spectrum with maximum size and bars growing towards the center of the screen.
|
|
809
|
+
|
|
810
|
+
This property has no effect when [`channelLayout`](#channellayout-string) is set to *'dual-vertical'*.
|
|
811
|
+
|
|
812
|
+
See also [`radius`](#radius-number).
|
|
813
|
+
|
|
814
|
+
Defaults to **false**.
|
|
815
|
+
|
|
816
|
+
### `radius` *number*
|
|
817
|
+
|
|
818
|
+
*Available since v4.4.0*
|
|
819
|
+
|
|
820
|
+
Defines the internal radius of [`radial`](#radial-boolean) spectrum. It should be a number between **0** and **1**.
|
|
821
|
+
|
|
822
|
+
This property has no effect when [`channelLayout`](#channellayout-string) is set to *'dual-vertical'*.
|
|
823
|
+
|
|
824
|
+
When [`radialInvert`](#radialinvert-boolean) is *true*, this property controls how close to the center of the screen the bars can get.
|
|
825
|
+
|
|
826
|
+
Defaults to **0.3**.
|
|
827
|
+
|
|
780
828
|
### `reflexAlpha` *number*
|
|
781
829
|
|
|
782
830
|
*Available since v2.1.0*
|
|
@@ -1213,6 +1261,19 @@ Please note that preset names are case-sensitive. If the specified preset is not
|
|
|
1213
1261
|
|
|
1214
1262
|
Use this method inside your callback function to create additional visual effects. See the [fluid demo](/demo/fluid.html) or [this pen](https://codepen.io/hvianna/pen/poNmVYo) for examples.
|
|
1215
1263
|
|
|
1264
|
+
### `getOptions( [ignore] )`
|
|
1265
|
+
|
|
1266
|
+
*Available since v4.4.0*
|
|
1267
|
+
|
|
1268
|
+
Returns an [**Options object**](#options-object) with all the current analyzer settings.
|
|
1269
|
+
|
|
1270
|
+
`ignore` can be a single property name or an array of property names that should not be included in the returned object.
|
|
1271
|
+
|
|
1272
|
+
Callbacks and [constructor-specific properties](#constructor-specific-options) are NOT included in the object.
|
|
1273
|
+
|
|
1274
|
+
?> If the same [gradient](#gradient-string) is selected for both channels, only the `gradient` property is included in the object; otherwise, only `gradientLeft` and `gradientRight` are included (not `gradient`). If 'gradient' is added to `ignore`, none of the gradient properties will be included.
|
|
1275
|
+
|
|
1276
|
+
See also [`setOptions()`](#setoptions-options-).
|
|
1216
1277
|
|
|
1217
1278
|
### `registerGradient( name, options )`
|
|
1218
1279
|
|
|
@@ -1290,10 +1351,12 @@ You can try different values in the [fluid demo](https://audiomotion.dev/demo/fl
|
|
|
1290
1351
|
|
|
1291
1352
|
Shorthand method for setting several analyzer [properties](#properties) at once.
|
|
1292
1353
|
|
|
1293
|
-
|
|
1354
|
+
`options` must be an [**Options object**](#options-object).
|
|
1294
1355
|
|
|
1295
1356
|
?> If called with no argument (or `options` is *undefined*), resets all configuration options to their default values.
|
|
1296
1357
|
|
|
1358
|
+
See also [`getOptions()`](#getoptions-ignore-).
|
|
1359
|
+
|
|
1297
1360
|
### `setSensitivity( minDecibels, maxDecibels )`
|
|
1298
1361
|
|
|
1299
1362
|
Adjust the analyzer's sensitivity. See [`minDecibels`](#mindecibels-number) and [`maxDecibels`](#maxdecibels-number) properties.
|
|
@@ -1456,7 +1519,7 @@ See [Changelog.md](Changelog.md)
|
|
|
1456
1519
|
|
|
1457
1520
|
## Contributing
|
|
1458
1521
|
|
|
1459
|
-
I kindly request that you only [open an issue](https://github.com/hvianna/audioMotion-analyzer/issues) for submitting
|
|
1522
|
+
I kindly request that you only [open an issue](https://github.com/hvianna/audioMotion-analyzer/issues) for submitting **bug reports**.
|
|
1460
1523
|
|
|
1461
1524
|
If you need help integrating *audioMotion-analyzer* with your project, have ideas for **new features** or any other questions or feedback,
|
|
1462
1525
|
please use the [**Discussions**](https://github.com/hvianna/audioMotion-analyzer/discussions) section on GitHub.
|
|
@@ -1475,5 +1538,5 @@ And if you're feeling generous, maybe:
|
|
|
1475
1538
|
|
|
1476
1539
|
## License
|
|
1477
1540
|
|
|
1478
|
-
audioMotion-analyzer copyright (c) 2018-
|
|
1541
|
+
audioMotion-analyzer copyright (c) 2018-2024 [Henrique Avila Vianna](https://henriquevianna.com)<br>
|
|
1479
1542
|
Licensed under the [GNU Affero General Public License, version 3 or later](https://www.gnu.org/licenses/agpl.html).
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "audiomotion-analyzer",
|
|
3
3
|
"description": "High-resolution real-time graphic audio spectrum analyzer JavaScript module with no dependencies.",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.4.0",
|
|
5
5
|
"main": "./src/audioMotion-analyzer.js",
|
|
6
6
|
"module": "./src/audioMotion-analyzer.js",
|
|
7
7
|
"types": "./src/index.d.ts",
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
* audioMotion-analyzer
|
|
3
3
|
* High-resolution real-time graphic audio spectrum analyzer JS module
|
|
4
4
|
*
|
|
5
|
-
* @version 4.
|
|
5
|
+
* @version 4.4.0
|
|
6
6
|
* @author Henrique Avila Vianna <hvianna@gmail.com> <https://henriquevianna.com>
|
|
7
7
|
* @license AGPL-3.0-or-later
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
const VERSION = '4.
|
|
10
|
+
const VERSION = '4.4.0';
|
|
11
11
|
|
|
12
12
|
// internal constants
|
|
13
13
|
const PI = Math.PI,
|
|
@@ -80,6 +80,60 @@ const PRISM = [ '#a35', '#c66', '#e94', '#ed0', '#9d5', '#4d8', '#2cb', '#0bc',
|
|
|
80
80
|
}]
|
|
81
81
|
];
|
|
82
82
|
|
|
83
|
+
// settings defaults
|
|
84
|
+
const DEFAULT_SETTINGS = {
|
|
85
|
+
alphaBars : false,
|
|
86
|
+
ansiBands : false,
|
|
87
|
+
barSpace : 0.1,
|
|
88
|
+
bgAlpha : 0.7,
|
|
89
|
+
channelLayout : CHANNEL_SINGLE,
|
|
90
|
+
colorMode : COLOR_GRADIENT,
|
|
91
|
+
fftSize : 8192,
|
|
92
|
+
fillAlpha : 1,
|
|
93
|
+
frequencyScale : SCALE_LOG,
|
|
94
|
+
gradient : GRADIENTS[0][0],
|
|
95
|
+
height : undefined,
|
|
96
|
+
ledBars : false,
|
|
97
|
+
linearAmplitude: false,
|
|
98
|
+
linearBoost : 1,
|
|
99
|
+
lineWidth : 0,
|
|
100
|
+
loRes : false,
|
|
101
|
+
lumiBars : false,
|
|
102
|
+
maxDecibels : -25,
|
|
103
|
+
maxFPS : 0,
|
|
104
|
+
maxFreq : 22000,
|
|
105
|
+
minDecibels : -85,
|
|
106
|
+
minFreq : 20,
|
|
107
|
+
mirror : 0,
|
|
108
|
+
mode : 0,
|
|
109
|
+
noteLabels : false,
|
|
110
|
+
outlineBars : false,
|
|
111
|
+
overlay : false,
|
|
112
|
+
peakLine : false,
|
|
113
|
+
radial : false,
|
|
114
|
+
radialInvert : false,
|
|
115
|
+
radius : 0.3,
|
|
116
|
+
reflexAlpha : 0.15,
|
|
117
|
+
reflexBright : 1,
|
|
118
|
+
reflexFit : true,
|
|
119
|
+
reflexRatio : 0,
|
|
120
|
+
roundBars : false,
|
|
121
|
+
showBgColor : true,
|
|
122
|
+
showFPS : false,
|
|
123
|
+
showPeaks : true,
|
|
124
|
+
showScaleX : true,
|
|
125
|
+
showScaleY : false,
|
|
126
|
+
smoothing : 0.5,
|
|
127
|
+
spinSpeed : 0,
|
|
128
|
+
splitGradient : false,
|
|
129
|
+
start : true,
|
|
130
|
+
trueLeds : false,
|
|
131
|
+
useCanvas : true,
|
|
132
|
+
volume : 1,
|
|
133
|
+
weightingFilter: FILTER_NONE,
|
|
134
|
+
width : undefined
|
|
135
|
+
};
|
|
136
|
+
|
|
83
137
|
// custom error messages
|
|
84
138
|
const ERR_AUDIO_CONTEXT_FAIL = [ 'ERR_AUDIO_CONTEXT_FAIL', 'Could not create audio context. Web Audio API not supported?' ],
|
|
85
139
|
ERR_INVALID_AUDIO_CONTEXT = [ 'ERR_INVALID_AUDIO_CONTEXT', 'Provided audio context is not valid' ],
|
|
@@ -104,6 +158,13 @@ class AudioMotionError extends Error {
|
|
|
104
158
|
// helper function - output deprecation warning message on console
|
|
105
159
|
const deprecate = ( name, alternative ) => console.warn( `${name} is deprecated. Use ${alternative} instead.` );
|
|
106
160
|
|
|
161
|
+
// helper function - check if a given object is empty (also returns `true` on null, undefined or any non-object value)
|
|
162
|
+
const isEmpty = obj => {
|
|
163
|
+
for ( const p in obj )
|
|
164
|
+
return false;
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
|
|
107
168
|
// helper function - validate a given value with an array of strings (by default, all lowercase)
|
|
108
169
|
// returns the validated value, or the first element of `list` if `value` is not found in the array
|
|
109
170
|
const validateFromList = ( value, list, modifier = 'toLowerCase' ) => list[ Math.max( 0, list.indexOf( ( '' + value )[ modifier ]() ) ) ];
|
|
@@ -152,12 +213,26 @@ export default class AudioMotionAnalyzer {
|
|
|
152
213
|
this._selectedGrads = []; // names of the currently selected gradients for channels 0 and 1
|
|
153
214
|
this._sources = []; // input nodes
|
|
154
215
|
|
|
216
|
+
// Check if options object passed as first argument
|
|
217
|
+
if ( ! ( container instanceof Element ) ) {
|
|
218
|
+
if ( isEmpty( options ) && ! isEmpty( container ) )
|
|
219
|
+
options = container;
|
|
220
|
+
container = null;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
this._ownCanvas = ! ( options.canvas instanceof HTMLCanvasElement );
|
|
224
|
+
|
|
225
|
+
// Create a new canvas or use the one provided by the user
|
|
226
|
+
const canvas = this._ownCanvas ? document.createElement('canvas') : options.canvas;
|
|
227
|
+
canvas.style = 'max-width: 100%;';
|
|
228
|
+
this._ctx = canvas.getContext('2d');
|
|
229
|
+
|
|
155
230
|
// Register built-in gradients
|
|
156
231
|
for ( const [ name, options ] of GRADIENTS )
|
|
157
232
|
this.registerGradient( name, options );
|
|
158
233
|
|
|
159
234
|
// Set container
|
|
160
|
-
this._container = container || document.body;
|
|
235
|
+
this._container = container || ( ! this._ownCanvas && canvas.parentElement ) || document.body;
|
|
161
236
|
|
|
162
237
|
// Make sure we have minimal width and height dimensions in case of an inline container
|
|
163
238
|
this._defaultWidth = this._container.clientWidth || 640;
|
|
@@ -225,11 +300,6 @@ export default class AudioMotionAnalyzer {
|
|
|
225
300
|
if ( options.connectSpeakers !== false )
|
|
226
301
|
this.connectOutput();
|
|
227
302
|
|
|
228
|
-
// create analyzer canvas
|
|
229
|
-
const canvas = document.createElement('canvas');
|
|
230
|
-
canvas.style = 'max-width: 100%;';
|
|
231
|
-
this._ctx = canvas.getContext('2d');
|
|
232
|
-
|
|
233
303
|
// create auxiliary canvases for the X-axis and radial scale labels
|
|
234
304
|
for ( const ctx of [ '_scaleX', '_scaleR' ] )
|
|
235
305
|
this[ ctx ] = document.createElement('canvas').getContext('2d');
|
|
@@ -310,8 +380,8 @@ export default class AudioMotionAnalyzer {
|
|
|
310
380
|
// Set configuration options and use defaults for any missing properties
|
|
311
381
|
this._setProps( options, true );
|
|
312
382
|
|
|
313
|
-
//
|
|
314
|
-
if ( this.useCanvas )
|
|
383
|
+
// Add canvas to the container (only when canvas not provided by user)
|
|
384
|
+
if ( this.useCanvas && this._ownCanvas )
|
|
315
385
|
this._container.appendChild( canvas );
|
|
316
386
|
|
|
317
387
|
// Finish canvas setup
|
|
@@ -571,6 +641,24 @@ export default class AudioMotionAnalyzer {
|
|
|
571
641
|
this._makeGrad();
|
|
572
642
|
}
|
|
573
643
|
|
|
644
|
+
get radialInvert() {
|
|
645
|
+
return this._radialInvert;
|
|
646
|
+
}
|
|
647
|
+
set radialInvert( value ) {
|
|
648
|
+
this._radialInvert = !! value;
|
|
649
|
+
this._calcBars();
|
|
650
|
+
this._makeGrad();
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
get radius() {
|
|
654
|
+
return this._radius;
|
|
655
|
+
}
|
|
656
|
+
set radius( value ) {
|
|
657
|
+
this._radius = +value || 0;
|
|
658
|
+
this._calcBars();
|
|
659
|
+
this._makeGrad();
|
|
660
|
+
}
|
|
661
|
+
|
|
574
662
|
get reflexRatio() {
|
|
575
663
|
return this._reflexRatio;
|
|
576
664
|
}
|
|
@@ -777,7 +865,7 @@ export default class AudioMotionAnalyzer {
|
|
|
777
865
|
if ( ! this._ready )
|
|
778
866
|
return;
|
|
779
867
|
|
|
780
|
-
const { audioCtx, canvas, _controller, _input, _merger, _observer, _ownContext, _splitter } = this;
|
|
868
|
+
const { audioCtx, canvas, _controller, _input, _merger, _observer, _ownCanvas, _ownContext, _splitter } = this;
|
|
781
869
|
|
|
782
870
|
this._destroyed = true;
|
|
783
871
|
this._ready = false;
|
|
@@ -804,8 +892,9 @@ export default class AudioMotionAnalyzer {
|
|
|
804
892
|
if ( _ownContext )
|
|
805
893
|
audioCtx.close();
|
|
806
894
|
|
|
807
|
-
// remove canvas from the DOM
|
|
808
|
-
|
|
895
|
+
// remove canvas from the DOM (if not provided by the user)
|
|
896
|
+
if ( _ownCanvas )
|
|
897
|
+
canvas.remove();
|
|
809
898
|
|
|
810
899
|
// reset flags
|
|
811
900
|
this._calcBars();
|
|
@@ -909,6 +998,29 @@ export default class AudioMotionAnalyzer {
|
|
|
909
998
|
return energy / ( endBin - startBin + 1 ) / chnCount;
|
|
910
999
|
}
|
|
911
1000
|
|
|
1001
|
+
/**
|
|
1002
|
+
* Returns current analyzer settings in object format
|
|
1003
|
+
*
|
|
1004
|
+
* @param [{string|array}] a property name or an array of property names to not include in the returned object
|
|
1005
|
+
* @returns {object} Options object
|
|
1006
|
+
*/
|
|
1007
|
+
getOptions( ignore ) {
|
|
1008
|
+
if ( ! Array.isArray( ignore ) )
|
|
1009
|
+
ignore = [ ignore ];
|
|
1010
|
+
let options = {};
|
|
1011
|
+
for ( const prop of Object.keys( DEFAULT_SETTINGS ) ) {
|
|
1012
|
+
if ( ! ignore.includes( prop ) ) {
|
|
1013
|
+
if ( prop == 'gradient' && this.gradientLeft != this.gradientRight ) {
|
|
1014
|
+
options.gradientLeft = this.gradientLeft;
|
|
1015
|
+
options.gradientRight = this.gradientRight;
|
|
1016
|
+
}
|
|
1017
|
+
else if ( prop != 'start' )
|
|
1018
|
+
options[ prop ] = this[ prop ];
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
return options;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
912
1024
|
/**
|
|
913
1025
|
* Registers a custom gradient
|
|
914
1026
|
*
|
|
@@ -1115,7 +1227,7 @@ export default class AudioMotionAnalyzer {
|
|
|
1115
1227
|
return;
|
|
1116
1228
|
}
|
|
1117
1229
|
|
|
1118
|
-
const { _ansiBands, _barSpace, canvas, _chLayout, _maxFreq, _minFreq, _mirror, _mode, _radial, _reflexRatio } = this,
|
|
1230
|
+
const { _ansiBands, _barSpace, canvas, _chLayout, _maxFreq, _minFreq, _mirror, _mode, _radial, _radialInvert, _reflexRatio } = this,
|
|
1119
1231
|
centerX = canvas.width >> 1,
|
|
1120
1232
|
centerY = canvas.height >> 1,
|
|
1121
1233
|
isDualVertical = _chLayout == CHANNEL_VERTICAL && ! _radial,
|
|
@@ -1145,9 +1257,13 @@ export default class AudioMotionAnalyzer {
|
|
|
1145
1257
|
// TODO: improve this, make it configurable?
|
|
1146
1258
|
channelGap = isDualVertical ? canvas.height - channelHeight * 2 : 0,
|
|
1147
1259
|
|
|
1148
|
-
initialX = centerX * ( _mirror == -1 && ! isDualHorizontal && ! _radial )
|
|
1149
|
-
|
|
1150
|
-
|
|
1260
|
+
initialX = centerX * ( _mirror == -1 && ! isDualHorizontal && ! _radial );
|
|
1261
|
+
|
|
1262
|
+
let innerRadius = Math.min( canvas.width, canvas.height ) * .375 * ( _chLayout == CHANNEL_VERTICAL ? 1 : this._radius ) | 0,
|
|
1263
|
+
outerRadius = Math.min( centerX, centerY );
|
|
1264
|
+
|
|
1265
|
+
if ( _radialInvert && _chLayout != CHANNEL_VERTICAL )
|
|
1266
|
+
[ innerRadius, outerRadius ] = [ outerRadius, innerRadius ];
|
|
1151
1267
|
|
|
1152
1268
|
/**
|
|
1153
1269
|
* CREATE ANALYZER BANDS
|
|
@@ -1479,8 +1595,9 @@ export default class AudioMotionAnalyzer {
|
|
|
1479
1595
|
freqLabels = [],
|
|
1480
1596
|
isDualHorizontal = this._chLayout == CHANNEL_HORIZONTAL,
|
|
1481
1597
|
isDualVertical = this._chLayout == CHANNEL_VERTICAL,
|
|
1598
|
+
minDimension = Math.min( canvas.width, canvas.height ),
|
|
1482
1599
|
scale = [ 'C',, 'D',, 'E', 'F',, 'G',, 'A',, 'B' ], // for note labels (no sharp notes)
|
|
1483
|
-
scaleHeight =
|
|
1600
|
+
scaleHeight = minDimension / 34 | 0, // circular scale height (radial mode)
|
|
1484
1601
|
fontSizeX = canvasX.height >> 1,
|
|
1485
1602
|
fontSizeR = scaleHeight >> 1,
|
|
1486
1603
|
labelWidthX = fontSizeX * ( _noteLabels ? .7 : 1.5 ),
|
|
@@ -1510,7 +1627,7 @@ export default class AudioMotionAnalyzer {
|
|
|
1510
1627
|
}
|
|
1511
1628
|
|
|
1512
1629
|
// in radial dual-vertical layout, the scale is positioned exactly between both channels, by making the canvas a bit larger than the inner diameter
|
|
1513
|
-
canvasR.width = canvasR.height = ( innerRadius << 1 ) + ( isDualVertical * scaleHeight );
|
|
1630
|
+
canvasR.width = canvasR.height = Math.max( minDimension * .15, ( innerRadius << 1 ) + ( isDualVertical * scaleHeight ) );
|
|
1514
1631
|
|
|
1515
1632
|
const centerR = canvasR.width >> 1,
|
|
1516
1633
|
radialY = centerR - scaleHeight * .7; // vertical position of text labels in the circular scale
|
|
@@ -1678,8 +1795,8 @@ export default class AudioMotionAnalyzer {
|
|
|
1678
1795
|
dbRange = maxDecibels - minDecibels,
|
|
1679
1796
|
[ ledCount, ledSpaceH, ledSpaceV, ledHeight ] = this._leds || [];
|
|
1680
1797
|
|
|
1681
|
-
if ( _energy.val > 0 )
|
|
1682
|
-
this._spinAngle += this._spinSpeed * TAU /
|
|
1798
|
+
if ( _energy.val > 0 && _fps > 0 )
|
|
1799
|
+
this._spinAngle += this._spinSpeed * TAU / 60 / _fps; // spinSpeed * angle increment per frame for 1 RPM
|
|
1683
1800
|
|
|
1684
1801
|
/* HELPER FUNCTIONS */
|
|
1685
1802
|
|
|
@@ -2157,8 +2274,10 @@ export default class AudioMotionAnalyzer {
|
|
|
2157
2274
|
}
|
|
2158
2275
|
else if ( ! _radial )
|
|
2159
2276
|
_ctx.fillRect( posX, analyzerBottom - peak * maxBarHeight, width, 2 );
|
|
2160
|
-
else if ( _mode != MODE_GRAPH ) // radial
|
|
2161
|
-
|
|
2277
|
+
else if ( _mode != MODE_GRAPH ) { // radial (peaks for graph mode are done by the peakLine code)
|
|
2278
|
+
const y = peak * maxBarHeight;
|
|
2279
|
+
radialPoly( posX, y, width, ! this._radialInvert || isDualVertical || y + innerRadius >= 2 ? -2 : 2 );
|
|
2280
|
+
}
|
|
2162
2281
|
}
|
|
2163
2282
|
|
|
2164
2283
|
} // for ( let barIndex = 0; barIndex < nBars; barIndex++ )
|
|
@@ -2278,7 +2397,7 @@ export default class AudioMotionAnalyzer {
|
|
|
2278
2397
|
if ( this.onCanvasDraw ) {
|
|
2279
2398
|
_ctx.save();
|
|
2280
2399
|
_ctx.fillStyle = _ctx.strokeStyle = _canvasGradients[0];
|
|
2281
|
-
this.onCanvasDraw( this, { timestamp, _canvasGradients } );
|
|
2400
|
+
this.onCanvasDraw( this, { timestamp, canvasGradients: _canvasGradients } );
|
|
2282
2401
|
_ctx.restore();
|
|
2283
2402
|
}
|
|
2284
2403
|
}
|
|
@@ -2437,8 +2556,8 @@ export default class AudioMotionAnalyzer {
|
|
|
2437
2556
|
this._fsWidth = screenWidth;
|
|
2438
2557
|
this._fsHeight = screenHeight;
|
|
2439
2558
|
|
|
2440
|
-
// if canvas dimensions haven't changed, quit
|
|
2441
|
-
if ( canvas.width == newWidth && canvas.height == newHeight )
|
|
2559
|
+
// if this is not the constructor call and canvas dimensions haven't changed, quit
|
|
2560
|
+
if ( reason != REASON_CREATE && canvas.width == newWidth && canvas.height == newHeight )
|
|
2442
2561
|
return;
|
|
2443
2562
|
|
|
2444
2563
|
// apply new dimensions
|
|
@@ -2497,68 +2616,17 @@ export default class AudioMotionAnalyzer {
|
|
|
2497
2616
|
* Set object properties
|
|
2498
2617
|
*/
|
|
2499
2618
|
_setProps( options, useDefaults ) {
|
|
2500
|
-
|
|
2501
|
-
// settings defaults
|
|
2502
|
-
const defaults = {
|
|
2503
|
-
alphaBars : false,
|
|
2504
|
-
ansiBands : false,
|
|
2505
|
-
barSpace : 0.1,
|
|
2506
|
-
bgAlpha : 0.7,
|
|
2507
|
-
channelLayout : CHANNEL_SINGLE,
|
|
2508
|
-
colorMode : COLOR_GRADIENT,
|
|
2509
|
-
fftSize : 8192,
|
|
2510
|
-
fillAlpha : 1,
|
|
2511
|
-
frequencyScale : SCALE_LOG,
|
|
2512
|
-
gradient : GRADIENTS[0][0],
|
|
2513
|
-
ledBars : false,
|
|
2514
|
-
linearAmplitude: false,
|
|
2515
|
-
linearBoost : 1,
|
|
2516
|
-
lineWidth : 0,
|
|
2517
|
-
loRes : false,
|
|
2518
|
-
lumiBars : false,
|
|
2519
|
-
maxDecibels : -25,
|
|
2520
|
-
maxFPS : 0,
|
|
2521
|
-
maxFreq : 22000,
|
|
2522
|
-
minDecibels : -85,
|
|
2523
|
-
minFreq : 20,
|
|
2524
|
-
mirror : 0,
|
|
2525
|
-
mode : 0,
|
|
2526
|
-
noteLabels : false,
|
|
2527
|
-
outlineBars : false,
|
|
2528
|
-
overlay : false,
|
|
2529
|
-
peakLine : false,
|
|
2530
|
-
radial : false,
|
|
2531
|
-
reflexAlpha : 0.15,
|
|
2532
|
-
reflexBright : 1,
|
|
2533
|
-
reflexFit : true,
|
|
2534
|
-
reflexRatio : 0,
|
|
2535
|
-
roundBars : false,
|
|
2536
|
-
showBgColor : true,
|
|
2537
|
-
showFPS : false,
|
|
2538
|
-
showPeaks : true,
|
|
2539
|
-
showScaleX : true,
|
|
2540
|
-
showScaleY : false,
|
|
2541
|
-
smoothing : 0.5,
|
|
2542
|
-
spinSpeed : 0,
|
|
2543
|
-
splitGradient : false,
|
|
2544
|
-
start : true,
|
|
2545
|
-
trueLeds : false,
|
|
2546
|
-
useCanvas : true,
|
|
2547
|
-
volume : 1,
|
|
2548
|
-
weightingFilter: FILTER_NONE
|
|
2549
|
-
};
|
|
2550
|
-
|
|
2551
2619
|
// callback functions properties
|
|
2552
2620
|
const callbacks = [ 'onCanvasDraw', 'onCanvasResize' ];
|
|
2553
2621
|
|
|
2554
|
-
// properties
|
|
2555
|
-
const
|
|
2622
|
+
// properties not in the defaults (`stereo` is deprecated)
|
|
2623
|
+
const extraProps = [ 'gradientLeft', 'gradientRight', 'stereo' ];
|
|
2556
2624
|
|
|
2557
2625
|
// build an array of valid properties; `start` is not an actual property and is handled after setting everything else
|
|
2558
|
-
const validProps = Object.keys(
|
|
2626
|
+
const validProps = Object.keys( DEFAULT_SETTINGS ).filter( e => e != 'start' ).concat( callbacks, extraProps );
|
|
2559
2627
|
|
|
2560
2628
|
if ( useDefaults || options === undefined )
|
|
2561
|
-
options = { ...
|
|
2629
|
+
options = { ...DEFAULT_SETTINGS, ...options }; // merge options with defaults
|
|
2562
2630
|
|
|
2563
2631
|
for ( const prop of Object.keys( options ) ) {
|
|
2564
2632
|
if ( callbacks.includes( prop ) && typeof options[ prop ] !== 'function' ) // check invalid callback
|
|
@@ -2567,6 +2635,7 @@ export default class AudioMotionAnalyzer {
|
|
|
2567
2635
|
this[ prop ] = options[ prop ];
|
|
2568
2636
|
}
|
|
2569
2637
|
|
|
2638
|
+
// deprecated - move this to the constructor in the next major release (`start` should be constructor-specific)
|
|
2570
2639
|
if ( options.start !== undefined )
|
|
2571
2640
|
this.toggleAnalyzer( options.start );
|
|
2572
2641
|
}
|
package/src/index.d.ts
CHANGED
|
@@ -49,6 +49,8 @@ export interface Options {
|
|
|
49
49
|
overlay?: boolean;
|
|
50
50
|
peakLine?: boolean;
|
|
51
51
|
radial?: boolean;
|
|
52
|
+
radialInvert?: boolean;
|
|
53
|
+
radius?: number;
|
|
52
54
|
reflexAlpha?: number;
|
|
53
55
|
reflexBright?: number;
|
|
54
56
|
reflexFit?: boolean;
|
|
@@ -83,6 +85,7 @@ export interface AnalyzerBarData {
|
|
|
83
85
|
|
|
84
86
|
export interface ConstructorOptions extends Options {
|
|
85
87
|
audioCtx?: AudioContext;
|
|
88
|
+
canvas?: HTMLCanvasElement;
|
|
86
89
|
connectSpeakers?: boolean;
|
|
87
90
|
fsElement?: HTMLElement;
|
|
88
91
|
source?: HTMLMediaElement | AudioNode;
|
|
@@ -114,6 +117,7 @@ export interface LedParameters {
|
|
|
114
117
|
|
|
115
118
|
declare class AudioMotionAnalyzer {
|
|
116
119
|
constructor(container?: HTMLElement, options?: ConstructorOptions);
|
|
120
|
+
constructor(options?: ConstructorOptions);
|
|
117
121
|
|
|
118
122
|
get alphaBars(): boolean;
|
|
119
123
|
set alphaBars(value: boolean);
|
|
@@ -234,6 +238,12 @@ declare class AudioMotionAnalyzer {
|
|
|
234
238
|
get radial(): boolean;
|
|
235
239
|
set radial(value: boolean);
|
|
236
240
|
|
|
241
|
+
get radialInvert(): boolean;
|
|
242
|
+
set radialInvert(value: boolean);
|
|
243
|
+
|
|
244
|
+
get radius(): number;
|
|
245
|
+
set radius(value: number);
|
|
246
|
+
|
|
237
247
|
public reflexAlpha: number;
|
|
238
248
|
public reflexBright: number;
|
|
239
249
|
public reflexFit: boolean;
|
|
@@ -292,6 +302,8 @@ declare class AudioMotionAnalyzer {
|
|
|
292
302
|
public getEnergy(preset?: EnergyPreset): number;
|
|
293
303
|
public getEnergy(startFreq: number, endFreq?: number): number;
|
|
294
304
|
|
|
305
|
+
public getOptions(ignore?: string | string[]): Options;
|
|
306
|
+
|
|
295
307
|
public registerGradient(name: string, options: GradientOptions): void;
|
|
296
308
|
|
|
297
309
|
public setCanvasSize(width: number, height: number): void;
|