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 CHANGED
@@ -86,13 +86,20 @@ import AudioMotionAnalyzer from 'audiomotion-analyzer';
86
86
 
87
87
  ## Constructor
88
88
 
89
- `new AudioMotionAnalyzer( [container], [{options}] )`
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
- The analyzer canvas will be appended to the HTML element referenced by `container`, unless you set [`useCanvas: false`](#usecanvas-boolean) in the options.
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
- If `container` is undefined, the document's body will be used instead.
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 by the constructor call, the others can also be set anytime via [`setOptions()`](#setoptions-options-) method.
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
  &emsp;&emsp;[alphaBars](#alphabars-boolean): **false**,<br>
@@ -119,6 +129,7 @@ options = {<br>
119
129
  &emsp;&emsp;[audioCtx](#audioctx-audiocontext-object): *undefined*, // constructor only<br>
120
130
  &emsp;&emsp;[barSpace](#barspace-number): **0.1**,<br>
121
131
  &emsp;&emsp;[bgAlpha](#bgalpha-number): **0.7**,<br>
132
+ &emsp;&emsp;[canvas](#canvas-htmlcanvaselement-object): *undefined*, // constructor only<br>
122
133
  &emsp;&emsp;[channelLayout](#channellayout-string): **'single'**,<br>
123
134
  &emsp;&emsp;[colorMode](#colormode-string): **'gradient'**,<br>
124
135
  &emsp;&emsp;[connectSpeakers](#connectspeakers-boolean): **true**, // constructor only<br>
@@ -129,6 +140,7 @@ options = {<br>
129
140
  &emsp;&emsp;[gradient](#gradient-string): **'classic'**,<br>
130
141
  &emsp;&emsp;[gradientLeft](#gradientleft-string): *undefined*,<br>
131
142
  &emsp;&emsp;[gradientRight](#gradientright-string): *undefined*,<br>
143
+ &emsp;&emsp;[gravity](#gravity-number): **1**,<br>
132
144
  &emsp;&emsp;[height](#height-number): *undefined*,<br>
133
145
  &emsp;&emsp;[ledBars](#ledbars-boolean): **false**,<br>
134
146
  &emsp;&emsp;[linearAmplitude](#linearamplitude-boolean): **false**,<br>
@@ -150,6 +162,8 @@ options = {<br>
150
162
  &emsp;&emsp;[overlay](#overlay-boolean): **false**,<br>
151
163
  &emsp;&emsp;[peakLine](#peakline-boolean): **false**,<br>
152
164
  &emsp;&emsp;[radial](#radial-boolean): **false**,<br>
165
+ &emsp;&emsp;[radialInvert](#radialinvert-boolean): **false**,<br>
166
+ &emsp;&emsp;[radius](#radius-number): **0.3**,<br>
153
167
  &emsp;&emsp;[reflexAlpha](#reflexalpha-number): **0.15**,<br>
154
168
  &emsp;&emsp;[reflexBright](#reflexbright-number): **1**,<br>
155
169
  &emsp;&emsp;[reflexFit](#reflexfit-boolean): **true**,<br>
@@ -164,7 +178,7 @@ options = {<br>
164
178
  &emsp;&emsp;[source](#source-htmlmediaelement-or-audionode-object): *undefined*, // constructor only<br>
165
179
  &emsp;&emsp;[spinSpeed](#spinspeed-number): **0**,<br>
166
180
  &emsp;&emsp;[splitGradient](#splitgradient-boolean): **false**,<br>
167
- &emsp;&emsp;[start](#start-boolean): **true**,<br>
181
+ &emsp;&emsp;[start](#start-boolean): **true**, // constructor only<br>
168
182
  &emsp;&emsp;[trueLeds](#trueleds-boolean): **false**,<br>
169
183
  &emsp;&emsp;[useCanvas](#usecanvas-boolean): **true**,<br>
170
184
  &emsp;&emsp;[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 created by audioMotion.
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 *Canvas*.
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
- If one or both of these are `undefined`, the analyzer will try to adjust to the container's width and/or height.
484
- If the container's width and/or height are 0 (inline elements), a reference size of **640 x 270 pixels** will be used to replace the missing dimension(s).
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
- ?> You can read the actual canvas dimensions at any time directly from the [`canvas`](#canvas-htmlcanvaselement-object-read-only) object.
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. Defaults to **true**.
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
- See **[Options object](#options-object)** for object structure and default values.
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 a **bug report**.
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-2023 [Henrique Avila Vianna](https://henriquevianna.com)<br>
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.3.0",
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.3.0
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.3.0';
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
- export default class AudioMotionAnalyzer {
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
- // add canvas to the container
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
- canvas.remove();
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
- innerRadius = Math.min( canvas.width, canvas.height ) * ( _chLayout == CHANNEL_VERTICAL ? .375 : .125 ) | 0,
1150
- outerRadius = Math.min( centerX, centerY );
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 = Math.min( canvas.width, canvas.height ) / 34 | 0, // circular scale height (radial mode)
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 / ( 60 * _fps ); // spinSpeed * angle increment per frame for 1 RPM
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 / 2 );
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 / 2 );
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 - peaks for graph mode are done by the peak line code
2161
- radialPoly( posX, peak * maxBarHeight, width, -2 );
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 undefined by default
2555
- const defaultUndefined = [ 'gradientLeft', 'gradientRight', 'height', 'width', 'stereo' ];
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( defaults ).filter( e => e != 'start' ).concat( callbacks, defaultUndefined );
2635
+ const validProps = Object.keys( DEFAULT_SETTINGS ).filter( e => e != 'start' ).concat( callbacks, extraProps );
2559
2636
 
2560
2637
  if ( useDefaults || options === undefined )
2561
- options = { ...defaults, ...options }; // merge options with defaults
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;