audiomotion-analyzer 4.3.0 → 4.5.0-beta.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 +94 -18
- package/package.json +2 -1
- package/src/audioMotion-analyzer.js +165 -84
- package/src/index.d.ts +17 -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.
|
|
99
|
+
|
|
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.
|
|
94
101
|
|
|
95
|
-
|
|
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>
|
|
@@ -129,6 +140,7 @@ options = {<br>
|
|
|
129
140
|
  [gradient](#gradient-string): **'classic'**,<br>
|
|
130
141
|
  [gradientLeft](#gradientleft-string): *undefined*,<br>
|
|
131
142
|
  [gradientRight](#gradientright-string): *undefined*,<br>
|
|
143
|
+
  [gravity](#gravity-number): **1**,<br>
|
|
132
144
|
  [height](#height-number): *undefined*,<br>
|
|
133
145
|
  [ledBars](#ledbars-boolean): **false**,<br>
|
|
134
146
|
  [linearAmplitude](#linearamplitude-boolean): **false**,<br>
|
|
@@ -150,6 +162,8 @@ options = {<br>
|
|
|
150
162
|
  [overlay](#overlay-boolean): **false**,<br>
|
|
151
163
|
  [peakLine](#peakline-boolean): **false**,<br>
|
|
152
164
|
  [radial](#radial-boolean): **false**,<br>
|
|
165
|
+
  [radialInvert](#radialinvert-boolean): **false**,<br>
|
|
166
|
+
  [radius](#radius-number): **0.3**,<br>
|
|
153
167
|
  [reflexAlpha](#reflexalpha-number): **0.15**,<br>
|
|
154
168
|
  [reflexBright](#reflexbright-number): **1**,<br>
|
|
155
169
|
  [reflexFit](#reflexfit-boolean): **true**,<br>
|
|
@@ -164,7 +178,7 @@ options = {<br>
|
|
|
164
178
|
  [source](#source-htmlmediaelement-or-audionode-object): *undefined*, // constructor only<br>
|
|
165
179
|
  [spinSpeed](#spinspeed-number): **0**,<br>
|
|
166
180
|
  [splitGradient](#splitgradient-boolean): **false**,<br>
|
|
167
|
-
  [start](#start-boolean): **true
|
|
181
|
+
  [start](#start-boolean): **true**, // constructor only<br>
|
|
168
182
|
  [trueLeds](#trueleds-boolean): **false**,<br>
|
|
169
183
|
  [useCanvas](#usecanvas-boolean): **true**,<br>
|
|
170
184
|
  [volume](#volume-number): **1**,<br>
|
|
@@ -187,6 +201,14 @@ If neither is defined, a new audio context will be created. After instantiation,
|
|
|
187
201
|
|
|
188
202
|
See [this live code](https://codesandbox.io/s/9y6qb) and the [multi-instance demo](/demo/multi.html) for more usage examples.
|
|
189
203
|
|
|
204
|
+
#### `canvas` *HTMLCanvasElement object*
|
|
205
|
+
|
|
206
|
+
*Available since v4.4.0*
|
|
207
|
+
|
|
208
|
+
Allows you to provide an existing [*Canvas*](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement) where audioMotion should render its visualizations.
|
|
209
|
+
|
|
210
|
+
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.
|
|
211
|
+
|
|
190
212
|
#### `connectSpeakers` *boolean*
|
|
191
213
|
|
|
192
214
|
*Available since v3.2.0*
|
|
@@ -321,11 +343,13 @@ Defaults to **0.7**.
|
|
|
321
343
|
|
|
322
344
|
### `canvas` *HTMLCanvasElement object* *(Read only)*
|
|
323
345
|
|
|
324
|
-
[*Canvas*](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement) element
|
|
346
|
+
[*Canvas*](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement) element where audioMotion renders its visualizations.
|
|
347
|
+
|
|
348
|
+
See also the [`canvas`](#canvas-htmlcanvaselement-object) constructor option.
|
|
325
349
|
|
|
326
350
|
### `canvasCtx` *CanvasRenderingContext2D object* *(Read only)*
|
|
327
351
|
|
|
328
|
-
[2D rendering context](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D) used for drawing in audioMotion's
|
|
352
|
+
[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
353
|
|
|
330
354
|
### `channelLayout` *string*
|
|
331
355
|
|
|
@@ -475,18 +499,29 @@ For **_dual-combined_** channel layout or [`radial`](#radial-boolean) spectrum,
|
|
|
475
499
|
|
|
476
500
|
See also [`gradient`](#gradient-string) and [`splitGradient`](#splitgradient-boolean).
|
|
477
501
|
|
|
502
|
+
### `gravity` *number*
|
|
503
|
+
|
|
504
|
+
*Available since v4.5.0*
|
|
505
|
+
|
|
506
|
+
Controls the acceleration of [peaks](#showpeaks-boolean) falling down.
|
|
507
|
+
|
|
508
|
+
It must be a number greater than zero. Invalid values are ignored and no error is thrown.
|
|
509
|
+
|
|
510
|
+
Defaults to **1**.
|
|
511
|
+
|
|
478
512
|
### `height` *number*
|
|
479
513
|
### `width` *number*
|
|
480
514
|
|
|
481
515
|
Nominal dimensions of the analyzer.
|
|
482
516
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
This should be considered the minimum dimensions for proper visualization of all available modes and effects.
|
|
517
|
+
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.
|
|
518
|
+
In that case, it's important that you constrain the dimensions of the container via CSS to prevent the canvas from growing indefinitely.
|
|
486
519
|
|
|
487
520
|
You can set both values at once using the [`setCanvasSize()`](#setcanvassize-width-height-) method.
|
|
488
521
|
|
|
489
|
-
|
|
522
|
+
See also [`onCanvasResize`](#oncanvasresize-function).
|
|
523
|
+
|
|
524
|
+
?> 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
525
|
|
|
491
526
|
### `isAlphaBars` *boolean* *(Read only)*
|
|
492
527
|
|
|
@@ -583,7 +618,7 @@ Defaults to **false**.
|
|
|
583
618
|
|
|
584
619
|
*Available since v4.0.0*
|
|
585
620
|
|
|
586
|
-
Performs an *n*th-root to amplify low energy values when using linear scale for the amplitude.
|
|
621
|
+
Performs an *n*th-root operation to amplify low energy values when using linear scale for the amplitude.
|
|
587
622
|
|
|
588
623
|
It should be a number >= 1, while 1 means no boosting. Only effective when [`linearAmplitude`](#linearamplitude-boolean) is set to *true*.
|
|
589
624
|
|
|
@@ -771,12 +806,36 @@ In radial view, [`ledBars`](#ledbars-boolean) and [`lumiBars`](#lumibars-boolean
|
|
|
771
806
|
|
|
772
807
|
When [`channelLayout`](#channellayout-string) is set to *'dual-vertical'*, graphs for the right channel are rendered towards the center of the screen.
|
|
773
808
|
|
|
774
|
-
See also [`spinSpeed`](#spinspeed-number).
|
|
809
|
+
See also [`radialInvert`](#radialinvert-boolean), [`radius`](#radius-number) and [`spinSpeed`](#spinspeed-number).
|
|
775
810
|
|
|
776
811
|
Defaults to **false**.
|
|
777
812
|
|
|
778
813
|
!> [See related known issue](#alphabars-and-fillalpha-wont-work-with-radial-on-firefox)
|
|
779
814
|
|
|
815
|
+
### `radialInvert` *boolean*
|
|
816
|
+
|
|
817
|
+
*Available since v4.4.0*
|
|
818
|
+
|
|
819
|
+
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.
|
|
820
|
+
|
|
821
|
+
This property has no effect when [`channelLayout`](#channellayout-string) is set to *'dual-vertical'*.
|
|
822
|
+
|
|
823
|
+
See also [`radius`](#radius-number).
|
|
824
|
+
|
|
825
|
+
Defaults to **false**.
|
|
826
|
+
|
|
827
|
+
### `radius` *number*
|
|
828
|
+
|
|
829
|
+
*Available since v4.4.0*
|
|
830
|
+
|
|
831
|
+
Defines the internal radius of [`radial`](#radial-boolean) spectrum. It should be a number between **0** and **1**.
|
|
832
|
+
|
|
833
|
+
This property has no effect when [`channelLayout`](#channellayout-string) is set to *'dual-vertical'*.
|
|
834
|
+
|
|
835
|
+
When [`radialInvert`](#radialinvert-boolean) is *true*, this property controls how close to the center of the screen the bars can get.
|
|
836
|
+
|
|
837
|
+
Defaults to **0.3**.
|
|
838
|
+
|
|
780
839
|
### `reflexAlpha` *number*
|
|
781
840
|
|
|
782
841
|
*Available since v2.1.0*
|
|
@@ -858,9 +917,11 @@ and setting `showBgColor` to ***true*** will make the "unlit" LEDs visible inste
|
|
|
858
917
|
|
|
859
918
|
### `showPeaks` *boolean*
|
|
860
919
|
|
|
861
|
-
*true* to show amplitude peaks.
|
|
920
|
+
*true* to show amplitude peaks.
|
|
862
921
|
|
|
863
|
-
See also [`peakLine`](#peakline-boolean).
|
|
922
|
+
See also [`gravity`](#gravity-number) and [`peakLine`](#peakline-boolean).
|
|
923
|
+
|
|
924
|
+
Defaults to **true**.
|
|
864
925
|
|
|
865
926
|
### `showScaleX` *boolean*
|
|
866
927
|
|
|
@@ -1213,6 +1274,19 @@ Please note that preset names are case-sensitive. If the specified preset is not
|
|
|
1213
1274
|
|
|
1214
1275
|
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
1276
|
|
|
1277
|
+
### `getOptions( [ignore] )`
|
|
1278
|
+
|
|
1279
|
+
*Available since v4.4.0*
|
|
1280
|
+
|
|
1281
|
+
Returns an [**Options object**](#options-object) with all the current analyzer settings.
|
|
1282
|
+
|
|
1283
|
+
`ignore` can be a single property name or an array of property names that should not be included in the returned object.
|
|
1284
|
+
|
|
1285
|
+
Callbacks and [constructor-specific properties](#constructor-specific-options) are NOT included in the object.
|
|
1286
|
+
|
|
1287
|
+
?> 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.
|
|
1288
|
+
|
|
1289
|
+
See also [`setOptions()`](#setoptions-options-).
|
|
1216
1290
|
|
|
1217
1291
|
### `registerGradient( name, options )`
|
|
1218
1292
|
|
|
@@ -1290,10 +1364,12 @@ You can try different values in the [fluid demo](https://audiomotion.dev/demo/fl
|
|
|
1290
1364
|
|
|
1291
1365
|
Shorthand method for setting several analyzer [properties](#properties) at once.
|
|
1292
1366
|
|
|
1293
|
-
|
|
1367
|
+
`options` must be an [**Options object**](#options-object).
|
|
1294
1368
|
|
|
1295
1369
|
?> If called with no argument (or `options` is *undefined*), resets all configuration options to their default values.
|
|
1296
1370
|
|
|
1371
|
+
See also [`getOptions()`](#getoptions-ignore-).
|
|
1372
|
+
|
|
1297
1373
|
### `setSensitivity( minDecibels, maxDecibels )`
|
|
1298
1374
|
|
|
1299
1375
|
Adjust the analyzer's sensitivity. See [`minDecibels`](#mindecibels-number) and [`maxDecibels`](#maxdecibels-number) properties.
|
|
@@ -1456,7 +1532,7 @@ See [Changelog.md](Changelog.md)
|
|
|
1456
1532
|
|
|
1457
1533
|
## Contributing
|
|
1458
1534
|
|
|
1459
|
-
I kindly request that you only [open an issue](https://github.com/hvianna/audioMotion-analyzer/issues) for submitting
|
|
1535
|
+
I kindly request that you only [open an issue](https://github.com/hvianna/audioMotion-analyzer/issues) for submitting **bug reports**.
|
|
1460
1536
|
|
|
1461
1537
|
If you need help integrating *audioMotion-analyzer* with your project, have ideas for **new features** or any other questions or feedback,
|
|
1462
1538
|
please use the [**Discussions**](https://github.com/hvianna/audioMotion-analyzer/discussions) section on GitHub.
|
|
@@ -1475,5 +1551,5 @@ And if you're feeling generous, maybe:
|
|
|
1475
1551
|
|
|
1476
1552
|
## License
|
|
1477
1553
|
|
|
1478
|
-
audioMotion-analyzer copyright (c) 2018-
|
|
1554
|
+
audioMotion-analyzer copyright (c) 2018-2024 [Henrique Avila Vianna](https://henriquevianna.com)<br>
|
|
1479
1555
|
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.5.0-beta.0",
|
|
5
5
|
"main": "./src/audioMotion-analyzer.js",
|
|
6
6
|
"module": "./src/audioMotion-analyzer.js",
|
|
7
7
|
"types": "./src/index.d.ts",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
11
|
"import": "./src/audioMotion-analyzer.js",
|
|
12
|
+
"require": "./src/audioMotion-analyzer.js",
|
|
12
13
|
"types": "./src/index.d.ts"
|
|
13
14
|
}
|
|
14
15
|
},
|
|
@@ -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.5.0-beta.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.5.0-beta.0';
|
|
11
11
|
|
|
12
12
|
// internal constants
|
|
13
13
|
const PI = Math.PI,
|
|
@@ -80,6 +80,61 @@ 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
|
+
gravity : 1,
|
|
96
|
+
height : undefined,
|
|
97
|
+
ledBars : false,
|
|
98
|
+
linearAmplitude: false,
|
|
99
|
+
linearBoost : 1,
|
|
100
|
+
lineWidth : 0,
|
|
101
|
+
loRes : false,
|
|
102
|
+
lumiBars : false,
|
|
103
|
+
maxDecibels : -25,
|
|
104
|
+
maxFPS : 0,
|
|
105
|
+
maxFreq : 22000,
|
|
106
|
+
minDecibels : -85,
|
|
107
|
+
minFreq : 20,
|
|
108
|
+
mirror : 0,
|
|
109
|
+
mode : 0,
|
|
110
|
+
noteLabels : false,
|
|
111
|
+
outlineBars : false,
|
|
112
|
+
overlay : false,
|
|
113
|
+
peakLine : false,
|
|
114
|
+
radial : false,
|
|
115
|
+
radialInvert : false,
|
|
116
|
+
radius : 0.3,
|
|
117
|
+
reflexAlpha : 0.15,
|
|
118
|
+
reflexBright : 1,
|
|
119
|
+
reflexFit : true,
|
|
120
|
+
reflexRatio : 0,
|
|
121
|
+
roundBars : false,
|
|
122
|
+
showBgColor : true,
|
|
123
|
+
showFPS : false,
|
|
124
|
+
showPeaks : true,
|
|
125
|
+
showScaleX : true,
|
|
126
|
+
showScaleY : false,
|
|
127
|
+
smoothing : 0.5,
|
|
128
|
+
spinSpeed : 0,
|
|
129
|
+
splitGradient : false,
|
|
130
|
+
start : true,
|
|
131
|
+
trueLeds : false,
|
|
132
|
+
useCanvas : true,
|
|
133
|
+
volume : 1,
|
|
134
|
+
weightingFilter: FILTER_NONE,
|
|
135
|
+
width : undefined
|
|
136
|
+
};
|
|
137
|
+
|
|
83
138
|
// custom error messages
|
|
84
139
|
const ERR_AUDIO_CONTEXT_FAIL = [ 'ERR_AUDIO_CONTEXT_FAIL', 'Could not create audio context. Web Audio API not supported?' ],
|
|
85
140
|
ERR_INVALID_AUDIO_CONTEXT = [ 'ERR_INVALID_AUDIO_CONTEXT', 'Provided audio context is not valid' ],
|
|
@@ -104,6 +159,13 @@ class AudioMotionError extends Error {
|
|
|
104
159
|
// helper function - output deprecation warning message on console
|
|
105
160
|
const deprecate = ( name, alternative ) => console.warn( `${name} is deprecated. Use ${alternative} instead.` );
|
|
106
161
|
|
|
162
|
+
// helper function - check if a given object is empty (also returns `true` on null, undefined or any non-object value)
|
|
163
|
+
const isEmpty = obj => {
|
|
164
|
+
for ( const p in obj )
|
|
165
|
+
return false;
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
|
|
107
169
|
// helper function - validate a given value with an array of strings (by default, all lowercase)
|
|
108
170
|
// returns the validated value, or the first element of `list` if `value` is not found in the array
|
|
109
171
|
const validateFromList = ( value, list, modifier = 'toLowerCase' ) => list[ Math.max( 0, list.indexOf( ( '' + value )[ modifier ]() ) ) ];
|
|
@@ -125,7 +187,7 @@ if ( ! Array.prototype.findLastIndex ) {
|
|
|
125
187
|
|
|
126
188
|
// AudioMotionAnalyzer class
|
|
127
189
|
|
|
128
|
-
|
|
190
|
+
class AudioMotionAnalyzer {
|
|
129
191
|
|
|
130
192
|
/**
|
|
131
193
|
* CONSTRUCTOR
|
|
@@ -152,12 +214,26 @@ export default class AudioMotionAnalyzer {
|
|
|
152
214
|
this._selectedGrads = []; // names of the currently selected gradients for channels 0 and 1
|
|
153
215
|
this._sources = []; // input nodes
|
|
154
216
|
|
|
217
|
+
// Check if options object passed as first argument
|
|
218
|
+
if ( ! ( container instanceof Element ) ) {
|
|
219
|
+
if ( isEmpty( options ) && ! isEmpty( container ) )
|
|
220
|
+
options = container;
|
|
221
|
+
container = null;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
this._ownCanvas = ! ( options.canvas instanceof HTMLCanvasElement );
|
|
225
|
+
|
|
226
|
+
// Create a new canvas or use the one provided by the user
|
|
227
|
+
const canvas = this._ownCanvas ? document.createElement('canvas') : options.canvas;
|
|
228
|
+
canvas.style = 'max-width: 100%;';
|
|
229
|
+
this._ctx = canvas.getContext('2d');
|
|
230
|
+
|
|
155
231
|
// Register built-in gradients
|
|
156
232
|
for ( const [ name, options ] of GRADIENTS )
|
|
157
233
|
this.registerGradient( name, options );
|
|
158
234
|
|
|
159
235
|
// Set container
|
|
160
|
-
this._container = container || document.body;
|
|
236
|
+
this._container = container || ( ! this._ownCanvas && canvas.parentElement ) || document.body;
|
|
161
237
|
|
|
162
238
|
// Make sure we have minimal width and height dimensions in case of an inline container
|
|
163
239
|
this._defaultWidth = this._container.clientWidth || 640;
|
|
@@ -225,11 +301,6 @@ export default class AudioMotionAnalyzer {
|
|
|
225
301
|
if ( options.connectSpeakers !== false )
|
|
226
302
|
this.connectOutput();
|
|
227
303
|
|
|
228
|
-
// create analyzer canvas
|
|
229
|
-
const canvas = document.createElement('canvas');
|
|
230
|
-
canvas.style = 'max-width: 100%;';
|
|
231
|
-
this._ctx = canvas.getContext('2d');
|
|
232
|
-
|
|
233
304
|
// create auxiliary canvases for the X-axis and radial scale labels
|
|
234
305
|
for ( const ctx of [ '_scaleX', '_scaleR' ] )
|
|
235
306
|
this[ ctx ] = document.createElement('canvas').getContext('2d');
|
|
@@ -310,8 +381,8 @@ export default class AudioMotionAnalyzer {
|
|
|
310
381
|
// Set configuration options and use defaults for any missing properties
|
|
311
382
|
this._setProps( options, true );
|
|
312
383
|
|
|
313
|
-
//
|
|
314
|
-
if ( this.useCanvas )
|
|
384
|
+
// Add canvas to the container (only when canvas not provided by user)
|
|
385
|
+
if ( this.useCanvas && this._ownCanvas )
|
|
315
386
|
this._container.appendChild( canvas );
|
|
316
387
|
|
|
317
388
|
// Finish canvas setup
|
|
@@ -415,6 +486,13 @@ export default class AudioMotionAnalyzer {
|
|
|
415
486
|
this._setGradient( value, 1 );
|
|
416
487
|
}
|
|
417
488
|
|
|
489
|
+
get gravity() {
|
|
490
|
+
return this._gravity;
|
|
491
|
+
}
|
|
492
|
+
set gravity( value ) {
|
|
493
|
+
this._gravity = value > 0 ? +value : this._gravity || DEFAULT_SETTINGS.gravity;
|
|
494
|
+
}
|
|
495
|
+
|
|
418
496
|
get height() {
|
|
419
497
|
return this._height;
|
|
420
498
|
}
|
|
@@ -571,6 +649,24 @@ export default class AudioMotionAnalyzer {
|
|
|
571
649
|
this._makeGrad();
|
|
572
650
|
}
|
|
573
651
|
|
|
652
|
+
get radialInvert() {
|
|
653
|
+
return this._radialInvert;
|
|
654
|
+
}
|
|
655
|
+
set radialInvert( value ) {
|
|
656
|
+
this._radialInvert = !! value;
|
|
657
|
+
this._calcBars();
|
|
658
|
+
this._makeGrad();
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
get radius() {
|
|
662
|
+
return this._radius;
|
|
663
|
+
}
|
|
664
|
+
set radius( value ) {
|
|
665
|
+
this._radius = +value || 0;
|
|
666
|
+
this._calcBars();
|
|
667
|
+
this._makeGrad();
|
|
668
|
+
}
|
|
669
|
+
|
|
574
670
|
get reflexRatio() {
|
|
575
671
|
return this._reflexRatio;
|
|
576
672
|
}
|
|
@@ -777,7 +873,7 @@ export default class AudioMotionAnalyzer {
|
|
|
777
873
|
if ( ! this._ready )
|
|
778
874
|
return;
|
|
779
875
|
|
|
780
|
-
const { audioCtx, canvas, _controller, _input, _merger, _observer, _ownContext, _splitter } = this;
|
|
876
|
+
const { audioCtx, canvas, _controller, _input, _merger, _observer, _ownCanvas, _ownContext, _splitter } = this;
|
|
781
877
|
|
|
782
878
|
this._destroyed = true;
|
|
783
879
|
this._ready = false;
|
|
@@ -804,8 +900,9 @@ export default class AudioMotionAnalyzer {
|
|
|
804
900
|
if ( _ownContext )
|
|
805
901
|
audioCtx.close();
|
|
806
902
|
|
|
807
|
-
// remove canvas from the DOM
|
|
808
|
-
|
|
903
|
+
// remove canvas from the DOM (if not provided by the user)
|
|
904
|
+
if ( _ownCanvas )
|
|
905
|
+
canvas.remove();
|
|
809
906
|
|
|
810
907
|
// reset flags
|
|
811
908
|
this._calcBars();
|
|
@@ -909,6 +1006,29 @@ export default class AudioMotionAnalyzer {
|
|
|
909
1006
|
return energy / ( endBin - startBin + 1 ) / chnCount;
|
|
910
1007
|
}
|
|
911
1008
|
|
|
1009
|
+
/**
|
|
1010
|
+
* Returns current analyzer settings in object format
|
|
1011
|
+
*
|
|
1012
|
+
* @param [{string|array}] a property name or an array of property names to not include in the returned object
|
|
1013
|
+
* @returns {object} Options object
|
|
1014
|
+
*/
|
|
1015
|
+
getOptions( ignore ) {
|
|
1016
|
+
if ( ! Array.isArray( ignore ) )
|
|
1017
|
+
ignore = [ ignore ];
|
|
1018
|
+
let options = {};
|
|
1019
|
+
for ( const prop of Object.keys( DEFAULT_SETTINGS ) ) {
|
|
1020
|
+
if ( ! ignore.includes( prop ) ) {
|
|
1021
|
+
if ( prop == 'gradient' && this.gradientLeft != this.gradientRight ) {
|
|
1022
|
+
options.gradientLeft = this.gradientLeft;
|
|
1023
|
+
options.gradientRight = this.gradientRight;
|
|
1024
|
+
}
|
|
1025
|
+
else if ( prop != 'start' )
|
|
1026
|
+
options[ prop ] = this[ prop ];
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
return options;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
912
1032
|
/**
|
|
913
1033
|
* Registers a custom gradient
|
|
914
1034
|
*
|
|
@@ -1115,7 +1235,7 @@ export default class AudioMotionAnalyzer {
|
|
|
1115
1235
|
return;
|
|
1116
1236
|
}
|
|
1117
1237
|
|
|
1118
|
-
const { _ansiBands, _barSpace, canvas, _chLayout, _maxFreq, _minFreq, _mirror, _mode, _radial, _reflexRatio } = this,
|
|
1238
|
+
const { _ansiBands, _barSpace, canvas, _chLayout, _maxFreq, _minFreq, _mirror, _mode, _radial, _radialInvert, _reflexRatio } = this,
|
|
1119
1239
|
centerX = canvas.width >> 1,
|
|
1120
1240
|
centerY = canvas.height >> 1,
|
|
1121
1241
|
isDualVertical = _chLayout == CHANNEL_VERTICAL && ! _radial,
|
|
@@ -1145,9 +1265,13 @@ export default class AudioMotionAnalyzer {
|
|
|
1145
1265
|
// TODO: improve this, make it configurable?
|
|
1146
1266
|
channelGap = isDualVertical ? canvas.height - channelHeight * 2 : 0,
|
|
1147
1267
|
|
|
1148
|
-
initialX = centerX * ( _mirror == -1 && ! isDualHorizontal && ! _radial )
|
|
1149
|
-
|
|
1150
|
-
|
|
1268
|
+
initialX = centerX * ( _mirror == -1 && ! isDualHorizontal && ! _radial );
|
|
1269
|
+
|
|
1270
|
+
let innerRadius = Math.min( canvas.width, canvas.height ) * .375 * ( _chLayout == CHANNEL_VERTICAL ? 1 : this._radius ) | 0,
|
|
1271
|
+
outerRadius = Math.min( centerX, centerY );
|
|
1272
|
+
|
|
1273
|
+
if ( _radialInvert && _chLayout != CHANNEL_VERTICAL )
|
|
1274
|
+
[ innerRadius, outerRadius ] = [ outerRadius, innerRadius ];
|
|
1151
1275
|
|
|
1152
1276
|
/**
|
|
1153
1277
|
* CREATE ANALYZER BANDS
|
|
@@ -1479,8 +1603,9 @@ export default class AudioMotionAnalyzer {
|
|
|
1479
1603
|
freqLabels = [],
|
|
1480
1604
|
isDualHorizontal = this._chLayout == CHANNEL_HORIZONTAL,
|
|
1481
1605
|
isDualVertical = this._chLayout == CHANNEL_VERTICAL,
|
|
1606
|
+
minDimension = Math.min( canvas.width, canvas.height ),
|
|
1482
1607
|
scale = [ 'C',, 'D',, 'E', 'F',, 'G',, 'A',, 'B' ], // for note labels (no sharp notes)
|
|
1483
|
-
scaleHeight =
|
|
1608
|
+
scaleHeight = minDimension / 34 | 0, // circular scale height (radial mode)
|
|
1484
1609
|
fontSizeX = canvasX.height >> 1,
|
|
1485
1610
|
fontSizeR = scaleHeight >> 1,
|
|
1486
1611
|
labelWidthX = fontSizeX * ( _noteLabels ? .7 : 1.5 ),
|
|
@@ -1510,7 +1635,7 @@ export default class AudioMotionAnalyzer {
|
|
|
1510
1635
|
}
|
|
1511
1636
|
|
|
1512
1637
|
// 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 );
|
|
1638
|
+
canvasR.width = canvasR.height = Math.max( minDimension * .15, ( innerRadius << 1 ) + ( isDualVertical * scaleHeight ) );
|
|
1514
1639
|
|
|
1515
1640
|
const centerR = canvasR.width >> 1,
|
|
1516
1641
|
radialY = centerR - scaleHeight * .7; // vertical position of text labels in the circular scale
|
|
@@ -1650,6 +1775,7 @@ export default class AudioMotionAnalyzer {
|
|
|
1650
1775
|
_energy,
|
|
1651
1776
|
fillAlpha,
|
|
1652
1777
|
_fps,
|
|
1778
|
+
_gravity,
|
|
1653
1779
|
_linearAmplitude,
|
|
1654
1780
|
_lineWidth,
|
|
1655
1781
|
maxDecibels,
|
|
@@ -1678,8 +1804,8 @@ export default class AudioMotionAnalyzer {
|
|
|
1678
1804
|
dbRange = maxDecibels - minDecibels,
|
|
1679
1805
|
[ ledCount, ledSpaceH, ledSpaceV, ledHeight ] = this._leds || [];
|
|
1680
1806
|
|
|
1681
|
-
if ( _energy.val > 0 )
|
|
1682
|
-
this._spinAngle += this._spinSpeed * TAU /
|
|
1807
|
+
if ( _energy.val > 0 && _fps > 0 )
|
|
1808
|
+
this._spinAngle += this._spinSpeed * TAU / 60 / _fps; // spinSpeed * angle increment per frame for 1 RPM
|
|
1683
1809
|
|
|
1684
1810
|
/* HELPER FUNCTIONS */
|
|
1685
1811
|
|
|
@@ -1792,7 +1918,7 @@ export default class AudioMotionAnalyzer {
|
|
|
1792
1918
|
if ( _energy.peak > 0 ) {
|
|
1793
1919
|
_energy.hold--;
|
|
1794
1920
|
if ( _energy.hold < 0 )
|
|
1795
|
-
_energy.peak += _energy.hold / ( holdFrames * holdFrames /
|
|
1921
|
+
_energy.peak += _energy.hold / ( holdFrames * holdFrames / _gravity );
|
|
1796
1922
|
}
|
|
1797
1923
|
if ( newVal >= _energy.peak ) {
|
|
1798
1924
|
_energy.peak = newVal;
|
|
@@ -2024,7 +2150,7 @@ export default class AudioMotionAnalyzer {
|
|
|
2024
2150
|
bar.hold[ channel ]--;
|
|
2025
2151
|
// if hold is negative, it becomes the "acceleration" for peak drop
|
|
2026
2152
|
if ( bar.hold[ channel ] < 0 )
|
|
2027
|
-
bar.peak[ channel ] += bar.hold[ channel ] / ( holdFrames * holdFrames /
|
|
2153
|
+
bar.peak[ channel ] += bar.hold[ channel ] / ( holdFrames * holdFrames / _gravity );
|
|
2028
2154
|
}
|
|
2029
2155
|
|
|
2030
2156
|
// check if it's a new peak for this bar
|
|
@@ -2157,8 +2283,10 @@ export default class AudioMotionAnalyzer {
|
|
|
2157
2283
|
}
|
|
2158
2284
|
else if ( ! _radial )
|
|
2159
2285
|
_ctx.fillRect( posX, analyzerBottom - peak * maxBarHeight, width, 2 );
|
|
2160
|
-
else if ( _mode != MODE_GRAPH ) // radial
|
|
2161
|
-
|
|
2286
|
+
else if ( _mode != MODE_GRAPH ) { // radial (peaks for graph mode are done by the peakLine code)
|
|
2287
|
+
const y = peak * maxBarHeight;
|
|
2288
|
+
radialPoly( posX, y, width, ! this._radialInvert || isDualVertical || y + innerRadius >= 2 ? -2 : 2 );
|
|
2289
|
+
}
|
|
2162
2290
|
}
|
|
2163
2291
|
|
|
2164
2292
|
} // for ( let barIndex = 0; barIndex < nBars; barIndex++ )
|
|
@@ -2278,7 +2406,7 @@ export default class AudioMotionAnalyzer {
|
|
|
2278
2406
|
if ( this.onCanvasDraw ) {
|
|
2279
2407
|
_ctx.save();
|
|
2280
2408
|
_ctx.fillStyle = _ctx.strokeStyle = _canvasGradients[0];
|
|
2281
|
-
this.onCanvasDraw( this, { timestamp, _canvasGradients } );
|
|
2409
|
+
this.onCanvasDraw( this, { timestamp, canvasGradients: _canvasGradients } );
|
|
2282
2410
|
_ctx.restore();
|
|
2283
2411
|
}
|
|
2284
2412
|
}
|
|
@@ -2437,8 +2565,8 @@ export default class AudioMotionAnalyzer {
|
|
|
2437
2565
|
this._fsWidth = screenWidth;
|
|
2438
2566
|
this._fsHeight = screenHeight;
|
|
2439
2567
|
|
|
2440
|
-
// if canvas dimensions haven't changed, quit
|
|
2441
|
-
if ( canvas.width == newWidth && canvas.height == newHeight )
|
|
2568
|
+
// if this is not the constructor call and canvas dimensions haven't changed, quit
|
|
2569
|
+
if ( reason != REASON_CREATE && canvas.width == newWidth && canvas.height == newHeight )
|
|
2442
2570
|
return;
|
|
2443
2571
|
|
|
2444
2572
|
// apply new dimensions
|
|
@@ -2497,68 +2625,17 @@ export default class AudioMotionAnalyzer {
|
|
|
2497
2625
|
* Set object properties
|
|
2498
2626
|
*/
|
|
2499
2627
|
_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
2628
|
// callback functions properties
|
|
2552
2629
|
const callbacks = [ 'onCanvasDraw', 'onCanvasResize' ];
|
|
2553
2630
|
|
|
2554
|
-
// properties
|
|
2555
|
-
const
|
|
2631
|
+
// properties not in the defaults (`stereo` is deprecated)
|
|
2632
|
+
const extraProps = [ 'gradientLeft', 'gradientRight', 'stereo' ];
|
|
2556
2633
|
|
|
2557
2634
|
// build an array of valid properties; `start` is not an actual property and is handled after setting everything else
|
|
2558
|
-
const validProps = Object.keys(
|
|
2635
|
+
const validProps = Object.keys( DEFAULT_SETTINGS ).filter( e => e != 'start' ).concat( callbacks, extraProps );
|
|
2559
2636
|
|
|
2560
2637
|
if ( useDefaults || options === undefined )
|
|
2561
|
-
options = { ...
|
|
2638
|
+
options = { ...DEFAULT_SETTINGS, ...options }; // merge options with defaults
|
|
2562
2639
|
|
|
2563
2640
|
for ( const prop of Object.keys( options ) ) {
|
|
2564
2641
|
if ( callbacks.includes( prop ) && typeof options[ prop ] !== 'function' ) // check invalid callback
|
|
@@ -2567,8 +2644,12 @@ export default class AudioMotionAnalyzer {
|
|
|
2567
2644
|
this[ prop ] = options[ prop ];
|
|
2568
2645
|
}
|
|
2569
2646
|
|
|
2647
|
+
// deprecated - move this to the constructor in the next major release (`start` should be constructor-specific)
|
|
2570
2648
|
if ( options.start !== undefined )
|
|
2571
2649
|
this.toggleAnalyzer( options.start );
|
|
2572
2650
|
}
|
|
2573
2651
|
|
|
2574
2652
|
}
|
|
2653
|
+
|
|
2654
|
+
export { AudioMotionAnalyzer };
|
|
2655
|
+
export default AudioMotionAnalyzer;
|
package/src/index.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ export interface Options {
|
|
|
28
28
|
gradient?: string;
|
|
29
29
|
gradientLeft?: string;
|
|
30
30
|
gradientRight?: string;
|
|
31
|
+
gravity?: number;
|
|
31
32
|
height?: number;
|
|
32
33
|
ledBars?: boolean;
|
|
33
34
|
linearAmplitude?: boolean;
|
|
@@ -49,6 +50,8 @@ export interface Options {
|
|
|
49
50
|
overlay?: boolean;
|
|
50
51
|
peakLine?: boolean;
|
|
51
52
|
radial?: boolean;
|
|
53
|
+
radialInvert?: boolean;
|
|
54
|
+
radius?: number;
|
|
52
55
|
reflexAlpha?: number;
|
|
53
56
|
reflexBright?: number;
|
|
54
57
|
reflexFit?: boolean;
|
|
@@ -83,6 +86,7 @@ export interface AnalyzerBarData {
|
|
|
83
86
|
|
|
84
87
|
export interface ConstructorOptions extends Options {
|
|
85
88
|
audioCtx?: AudioContext;
|
|
89
|
+
canvas?: HTMLCanvasElement;
|
|
86
90
|
connectSpeakers?: boolean;
|
|
87
91
|
fsElement?: HTMLElement;
|
|
88
92
|
source?: HTMLMediaElement | AudioNode;
|
|
@@ -114,6 +118,7 @@ export interface LedParameters {
|
|
|
114
118
|
|
|
115
119
|
declare class AudioMotionAnalyzer {
|
|
116
120
|
constructor(container?: HTMLElement, options?: ConstructorOptions);
|
|
121
|
+
constructor(options?: ConstructorOptions);
|
|
117
122
|
|
|
118
123
|
get alphaBars(): boolean;
|
|
119
124
|
set alphaBars(value: boolean);
|
|
@@ -161,6 +166,9 @@ declare class AudioMotionAnalyzer {
|
|
|
161
166
|
get gradientRight(): string;
|
|
162
167
|
set gradientRight(value: string);
|
|
163
168
|
|
|
169
|
+
get gravity(): number;
|
|
170
|
+
set gravity(value: number);
|
|
171
|
+
|
|
164
172
|
get height(): number;
|
|
165
173
|
set height(h: number);
|
|
166
174
|
|
|
@@ -234,6 +242,12 @@ declare class AudioMotionAnalyzer {
|
|
|
234
242
|
get radial(): boolean;
|
|
235
243
|
set radial(value: boolean);
|
|
236
244
|
|
|
245
|
+
get radialInvert(): boolean;
|
|
246
|
+
set radialInvert(value: boolean);
|
|
247
|
+
|
|
248
|
+
get radius(): number;
|
|
249
|
+
set radius(value: number);
|
|
250
|
+
|
|
237
251
|
public reflexAlpha: number;
|
|
238
252
|
public reflexBright: number;
|
|
239
253
|
public reflexFit: boolean;
|
|
@@ -292,6 +306,8 @@ declare class AudioMotionAnalyzer {
|
|
|
292
306
|
public getEnergy(preset?: EnergyPreset): number;
|
|
293
307
|
public getEnergy(startFreq: number, endFreq?: number): number;
|
|
294
308
|
|
|
309
|
+
public getOptions(ignore?: string | string[]): Options;
|
|
310
|
+
|
|
295
311
|
public registerGradient(name: string, options: GradientOptions): void;
|
|
296
312
|
|
|
297
313
|
public setCanvasSize(width: number, height: number): void;
|
|
@@ -307,4 +323,5 @@ declare class AudioMotionAnalyzer {
|
|
|
307
323
|
public toggleFullscreen(): void;
|
|
308
324
|
}
|
|
309
325
|
|
|
326
|
+
export { AudioMotionAnalyzer };
|
|
310
327
|
export default AudioMotionAnalyzer;
|