audiomotion-analyzer 4.0.0-beta.4 → 4.0.0-beta.5

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
@@ -161,7 +161,6 @@ options = {<br>
161
161
  &emsp;&emsp;[spinSpeed](#spinspeed-number): **0**,<br>
162
162
  &emsp;&emsp;[splitGradient](#splitgradient-boolean): **false**,<br>
163
163
  &emsp;&emsp;[start](#start-boolean): **true**,<br>
164
- &emsp;&emsp;[stereo](#stereo-deprecated-boolean): **false**, // DEPRECATED - use channelLayout instead<br>
165
164
  &emsp;&emsp;[useCanvas](#usecanvas-boolean): **true**,<br>
166
165
  &emsp;&emsp;[volume](#volume-number): **1**,<br>
167
166
  &emsp;&emsp;[weightingFilter](#weightingFilter-string): **''**<br>
@@ -236,7 +235,7 @@ Defaults to **true**, so the analyzer will start running right after initializat
236
235
 
237
236
  When set to *true* each bar's amplitude affects its opacity, i.e., higher bars are rendered more opaque while shorter bars are more transparent.
238
237
 
239
- This is similar to the [`lumiBars`](#lumibars-boolean) effect, but bars' amplitudes are preserved and it also works on **Discrete** [mode](#mode-number) and [radial](#radial-boolean) visualization.
238
+ This is similar to the [`lumiBars`](#lumibars-boolean) effect, but bars' amplitudes are preserved and it also works on **Discrete** [mode](#mode-number) and [radial](#radial-boolean) spectrum.
240
239
 
241
240
  For effect priority when combined with other settings, see [`isAlphaBars`](#isalphabars-boolean-read-only).
242
241
 
@@ -324,11 +323,11 @@ Defaults to **0.7**.
324
323
 
325
324
  Defines the number and layout of analyzer channels.
326
325
 
327
- channelLayout | description
328
- -------------- |------------
329
- 'single' | Single channel analyzer, representing the combined output of both left and right channels.
330
- 'dualVertical' | Dual channel analyzer, with left channel shown at the top and right channel at the bottom.
331
- 'dualCombined' | Left and right channel graphs are shown overlaid. Works best with semi-transparent **Graph** [`mode`](#mode-number) or [`outlineBars`](#outlinebars-boolean).
326
+ channelLayout | description
327
+ ----------------|------------
328
+ 'single' | Single channel analyzer, representing the combined output of both left and right channels.
329
+ 'dual-vertical' | Dual channel analyzer, with left channel shown at the top and right channel at the bottom.
330
+ 'dual-combined' | Left and right channel graphs are shown overlaid. Works best with semi-transparent **Graph** [`mode`](#mode-number) or [`outlineBars`](#outlinebars-boolean).
332
331
 
333
332
  !> When a *dual* layout is selected, any mono (single channel) audio source connected to the analyzer will output sound only from the left speaker,
334
333
  unless a stereo source is simultaneously connected to the analyzer, which will force the mono input to be upmixed to stereo.
@@ -416,9 +415,23 @@ Canvas dimensions used during fullscreen mode. These take the current pixel rati
416
415
 
417
416
  Name of the color gradient used for analyzer graphs.
418
417
 
419
- It must be a built-in or [registered](#registergradient-name-options-) gradient name. Built-in gradients are *'classic'*, *'prism'* and *'rainbow'*.
418
+ It must be a built-in or registered gradient name (see [`registerGradient()`](#registergradient-name-options-)).
420
419
 
421
- See also [`gradientLeft`](#gradientleft-string) and [`gradientRight`](#gradientright-string).
420
+ `gradient` sets the gradient for both analyzer channels, but its read value represents only the gradient on the left (or single) channel.
421
+
422
+ When using a dual [`channelLayout`](#channellayout-string), use [`gradientLeft`](#gradientleft-string) and [`gradientRight`](#gradientright-string) if you want to individually set/read the gradient for each channel.
423
+
424
+ Built-in gradients are shown below:
425
+
426
+ gradient | preview
427
+ ------------|---------
428
+ 'classic' | ![classic](img/gradient-classic.png)
429
+ 'orangered' | ![orangered](img/gradient-orangered.png)
430
+ 'prism' | ![prism](img/gradient-prism.png)
431
+ 'rainbow' | ![rainbow](img/gradient-rainbow.png)
432
+ 'steelblue' | ![steelblue](img/gradient-steelblue.png)
433
+
434
+ See also [`splitGradient`](#splitgradient-boolean).
422
435
 
423
436
  Defaults to **'classic'**.
424
437
 
@@ -427,11 +440,13 @@ Defaults to **'classic'**.
427
440
 
428
441
  *Available since v4.0.0*
429
442
 
430
- When using a dual [`channelLayout`](#channellayout-string), different gradients can be selected for the left and right channels.
443
+ Select gradients for the left and right analyzer channels independently, for use with a dual [`channelLayout`](#channellayout-string).
431
444
 
432
- When [`channelLayout`](#channellayout-string) is set to *'single'*, the gradient selected by `gradientLeft` is used.
445
+ **_Single_** channel layout will use the gradient selected by `gradientLeft`.
433
446
 
434
- The [`gradient`](#gradient-string) property can be used as a shorthand to set the same gradient for both channels. Its read value returns only the left (or single) channel gradient though.
447
+ For **_dual-combined_** channel layout or [`radial`](#radial-boolean) spectrum, only the background color defined by `gradientLeft` will be applied when [`showBgColor`](#showbgcolor-boolean) is *true*.
448
+
449
+ See also [`gradient`](#gradient-string) and [`splitGradient`](#splitgradient-boolean).
435
450
 
436
451
  ### `height` *number*
437
452
  ### `width` *number*
@@ -567,7 +582,7 @@ This is only effective for [bands modes](#mode-number).
567
582
 
568
583
  When set to *true* all analyzer bars will be displayed at full height with varying luminance (opacity, actually) instead.
569
584
 
570
- `lumiBars` takes precedence over [`alphaBars`](#alphabars-boolean) and [`outlineBars`](#outlinebars-boolean), except in [`radial`](#radial-boolean) visualization.
585
+ `lumiBars` takes precedence over [`alphaBars`](#alphabars-boolean) and [`outlineBars`](#outlinebars-boolean), except on [`radial`](#radial-boolean) spectrum.
571
586
 
572
587
  For effect priority when combined with other settings, see [`isLumiBars`](#islumibars-boolean-read-only).
573
588
 
@@ -685,10 +700,10 @@ You can refer to this value to adjust any additional drawings done in the canvas
685
700
 
686
701
  When *true*, the spectrum analyzer is rendered in a circular shape, with radial frequency bars spreading from its center.
687
702
 
688
- In radial visualization, [`ledBars`](#ledbars-boolean) and [`lumiBars`](#lumibars-boolean) effects are disabled, and
703
+ On radial spectrum, [`ledBars`](#ledbars-boolean) and [`lumiBars`](#lumibars-boolean) effects are disabled, and
689
704
  [`showPeaks`](#showpeaks-boolean) has no effect for [**Graph** mode](#mode-number).
690
705
 
691
- When [`channelLayout`](#channellayout-string) is set to *'dualVertical'*, a larger diameter is used and the right channel bars are rendered towards the center of the analyzer.
706
+ When [`channelLayout`](#channellayout-string) is set to *'dual-vertical'*, a larger diameter is used and the right channel bars are rendered towards the center of the analyzer.
692
707
 
693
708
  See also [`spinSpeed`](#spinspeed-number).
694
709
 
@@ -749,6 +764,8 @@ Opacity can be adjusted via [`bgAlpha`](#bgalpha-number) property, when [`overla
749
764
  If ***false***, the canvas background will be painted black when [`overlay`](#overlay-boolean) is ***false***,
750
765
  or transparent when [`overlay`](#overlay-boolean) is ***true***.
751
766
 
767
+ See also [`registerGradient()`](#registergradient-name-options-).
768
+
752
769
  Defaults to **true**.
753
770
 
754
771
  ?> Please note that when [`overlay`](#overlay-boolean) is ***false*** and [`ledBars`](#ledbars-boolean) is ***true***, the background color will always be black,
@@ -809,13 +826,15 @@ Defaults to **0**.
809
826
 
810
827
  *Available since v3.0.0*
811
828
 
812
- When *true*, the gradient will be split between both channels, so each channel will have different colors. If *false*, both channels will use the full gradient.
829
+ When set to *true* and [`channelLayout`](#channellayout-string) is **_dual-vertical_**, the gradient will be split between channels.
813
830
 
814
- | splitGradient: *true* | splitGradient: *false* |
831
+ When *false*, both channels will use the full gradient.
832
+
833
+ | gradient: *'classic'* - splitGradient: *false* | gradient: *'classic'* - splitGradient: *true* |
815
834
  |:--:|:--:|
816
- | ![split-on](img/splitGradient_on.png) | ![split-off](img/splitGradient_off.png) |
835
+ | ![split-off](img/splitGradient_off.png) | ![split-on](img/splitGradient_on.png) |
817
836
 
818
- This option has no effect on horizontal gradients (except in [`radial`](#radial-boolean) visualization - see note in [`registerGradient()`](#registergradient-name-options-)), or when [`channelLayout`](#channellayout-string) is set to *'single'* or *'dualCombined'*.
837
+ This option has no effect on horizontal gradients, except on [`radial`](#radial-boolean) spectrum - see note in [`registerGradient()`](#registergradient-name-options-).
819
838
 
820
839
  Defaults to **false**.
821
840
 
@@ -1083,9 +1102,11 @@ const options = {
1083
1102
  audioMotion.registerGradient( 'myGradient', options );
1084
1103
  ```
1085
1104
 
1086
- ?> During [`radial`](#radial-boolean) visualization all gradients are rendered in radial direction, so the horizontal flag (`dir: 'h'`) has no effect.
1105
+ Check the [built-in **_'rainbow'_** gradient](#gradient-string) for an example of horizontal gradient.
1106
+
1107
+ **Note:** the horizontal flag (`dir: 'h'`) has no effect on [`radial`](#radial-boolean) spectrum, because in that mode all gradients are rendered in radial direction.
1087
1108
 
1088
- Additional information about [gradient color-stops](https://developer.mozilla.org/en-US/docs/Web/API/CanvasGradient/addColorStop).
1109
+ ?> Any gradient, including the built-in ones, may be modified by (re-)registering the same gradient name (names are case sensitive).
1089
1110
 
1090
1111
  ### `setCanvasSize( width, height )`
1091
1112
 
@@ -1190,7 +1211,7 @@ which is [currently not supported in some browsers](https://caniuse.com/#feat=md
1190
1211
 
1191
1212
  ### alphaBars and fillAlpha won't work with Radial on Firefox <!-- {docsify-ignore} -->
1192
1213
 
1193
- On Firefox, [`alphaBars`](#alphaBars-boolean) and [`fillAlpha`](#fillalpha-number) won't work with [`radial`](#radial-boolean) visualization when using hardware acceleration, due to [this bug](https://bugzilla.mozilla.org/show_bug.cgi?id=1164912).
1214
+ On Firefox, [`alphaBars`](#alphaBars-boolean) and [`fillAlpha`](#fillalpha-number) won't work with [`radial`](#radial-boolean) spectrum when using hardware acceleration, due to [this bug](https://bugzilla.mozilla.org/show_bug.cgi?id=1164912).
1194
1215
 
1195
1216
  ### Visualization of live streams won't work on Safari {docsify-ignore}
1196
1217
 
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.0.0-beta.4",
4
+ "version": "4.0.0-beta.5",
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.0.0-beta.4
5
+ * @version 4.0.0-beta.5
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.0.0-beta.4';
10
+ const VERSION = '4.0.0-beta.5';
11
11
 
12
12
  // internal constants
13
13
  const TAU = 2 * Math.PI,
@@ -16,9 +16,9 @@ const TAU = 2 * Math.PI,
16
16
  C_1 = 8.17579892; // frequency for C -1
17
17
 
18
18
  const CANVAS_BACKGROUND_COLOR = '#000',
19
- CHANNEL_COMBINED = 'dualCombined',
19
+ CHANNEL_COMBINED = 'dual-combined',
20
20
  CHANNEL_SINGLE = 'single',
21
- CHANNEL_VERTICAL = 'dualVertical',
21
+ CHANNEL_VERTICAL = 'dual-vertical',
22
22
  GRADIENT_DEFAULT_BGCOLOR = '#111',
23
23
  FILTER_NONE = '',
24
24
  FILTER_A = 'A',
@@ -28,9 +28,6 @@ const CANVAS_BACKGROUND_COLOR = '#000',
28
28
  FILTER_468 = '468',
29
29
  FONT_FAMILY = 'sans-serif',
30
30
  FPS_COLOR = '#0f0',
31
- GRADIENT_CLASSIC = 'classic',
32
- GRADIENT_PRISM = 'prism',
33
- GRADIENT_RAINBOW = 'rainbow',
34
31
  LEDS_UNLIT_COLOR = '#7f7f7f22',
35
32
  REASON_CREATE = 'create',
36
33
  REASON_FSCHANGE = 'fschange',
@@ -47,6 +44,46 @@ const CANVAS_BACKGROUND_COLOR = '#000',
47
44
  SCALE_LOG = 'log',
48
45
  SCALE_MEL = 'mel';
49
46
 
47
+ // built-in gradients
48
+ const GRADIENTS = [
49
+ [ 'classic', {
50
+ colorStops: [
51
+ 'hsl( 0, 100%, 50% )',
52
+ { pos: .6, color: 'hsl( 60, 100%, 50% )' },
53
+ 'hsl( 120, 100%, 50% )'
54
+ ]
55
+ }],
56
+ [ 'prism', {
57
+ colorStops: [
58
+ 'hsl( 0, 100%, 50% )',
59
+ 'hsl( 60, 100%, 50% )',
60
+ 'hsl( 120, 100%, 50% )',
61
+ 'hsl( 180, 100%, 50% )',
62
+ 'hsl( 240, 100%, 50% )'
63
+ ]
64
+ }],
65
+ [ 'rainbow', {
66
+ dir: 'h',
67
+ colorStops: [
68
+ 'hsl( 0, 100%, 50% )',
69
+ 'hsl( 60, 100%, 50% )',
70
+ 'hsl( 120, 100%, 50% )',
71
+ 'hsl( 180, 100%, 47% )',
72
+ 'hsl( 240, 100%, 58% )',
73
+ 'hsl( 300, 100%, 50% )',
74
+ 'hsl( 360, 100%, 50% )'
75
+ ]
76
+ }],
77
+ [ 'orangered', {
78
+ bgColor: '#3e2f29',
79
+ colorStops: [ 'OrangeRed' ]
80
+ }],
81
+ [ 'steelblue', {
82
+ bgColor: '#222c35',
83
+ colorStops: [ 'SteelBlue' ]
84
+ }]
85
+ ];
86
+
50
87
  // custom error messages
51
88
  const ERR_AUDIO_CONTEXT_FAIL = [ 'ERR_AUDIO_CONTEXT_FAIL', 'Could not create audio context. Web Audio API not supported?' ],
52
89
  ERR_INVALID_AUDIO_CONTEXT = [ 'ERR_INVALID_AUDIO_CONTEXT', 'Provided audio context is not valid' ],
@@ -88,38 +125,12 @@ export default class AudioMotionAnalyzer {
88
125
 
89
126
  // Initialize internal gradient objects
90
127
  this._gradients = {}; // registered gradients
91
- this._gradientNames = []; // names of the currently selected gradients for channels 0 and 1
128
+ this._selectedGrads = []; // names of the currently selected gradients for channels 0 and 1
92
129
  this._canvasGradients = []; // actual CanvasGradient objects for channels 0 and 1
93
130
 
94
131
  // Register built-in gradients
95
- this.registerGradient( GRADIENT_CLASSIC, {
96
- colorStops: [
97
- 'hsl( 0, 100%, 50% )',
98
- { pos: .6, color: 'hsl( 60, 100%, 50% )' },
99
- 'hsl( 120, 100%, 50% )'
100
- ]
101
- });
102
- this.registerGradient( GRADIENT_PRISM, {
103
- colorStops: [
104
- 'hsl( 0, 100%, 50% )',
105
- 'hsl( 60, 100%, 50% )',
106
- 'hsl( 120, 100%, 50% )',
107
- 'hsl( 180, 100%, 50% )',
108
- 'hsl( 240, 100%, 50% )'
109
- ]
110
- });
111
- this.registerGradient( GRADIENT_RAINBOW, {
112
- dir: 'h',
113
- colorStops: [
114
- 'hsl( 0, 100%, 50% )',
115
- 'hsl( 60, 100%, 50% )',
116
- 'hsl( 120, 100%, 50% )',
117
- 'hsl( 180, 100%, 47% )',
118
- 'hsl( 240, 100%, 58% )',
119
- 'hsl( 300, 100%, 50% )',
120
- 'hsl( 360, 100%, 50% )'
121
- ]
122
- });
132
+ for ( const [ name, options ] of GRADIENTS )
133
+ this.registerGradient( name, options );
123
134
 
124
135
  // Set container
125
136
  this._container = container || document.body;
@@ -315,8 +326,8 @@ export default class AudioMotionAnalyzer {
315
326
  return this._chLayout;
316
327
  }
317
328
  set channelLayout( value ) {
318
- const MODES = [ CHANNEL_SINGLE, CHANNEL_VERTICAL, CHANNEL_COMBINED ];
319
- this._chLayout = MODES[ Math.max( 0, MODES.findIndex( el => el.toLowerCase() == ( '' + value ).toLowerCase() ) ) ];
329
+ const LAYOUTS = [ CHANNEL_SINGLE, CHANNEL_VERTICAL, CHANNEL_COMBINED ];
330
+ this._chLayout = LAYOUTS[ Math.max( 0, LAYOUTS.indexOf( ( '' + value ).toLowerCase() ) ) ];
320
331
 
321
332
  // update node connections
322
333
  this._input.disconnect();
@@ -354,21 +365,21 @@ export default class AudioMotionAnalyzer {
354
365
  }
355
366
 
356
367
  get gradient() {
357
- return this._gradientNames[0];
368
+ return this._selectedGrads[0];
358
369
  }
359
370
  set gradient( value ) {
360
371
  this._setGradient( value );
361
372
  }
362
373
 
363
374
  get gradientLeft() {
364
- return this._gradientNames[0];
375
+ return this._selectedGrads[0];
365
376
  }
366
377
  set gradientLeft( value ) {
367
378
  this._setGradient( value, 0 );
368
379
  }
369
380
 
370
381
  get gradientRight() {
371
- return this._gradientNames[1];
382
+ return this._selectedGrads[1];
372
383
  }
373
384
  set gradientRight( value ) {
374
385
  this._setGradient( value, 1 );
@@ -808,7 +819,7 @@ export default class AudioMotionAnalyzer {
808
819
  };
809
820
 
810
821
  // if the registered gradient is one of the currently selected gradients, regenerate them
811
- if ( this._gradientNames.includes( name ) )
822
+ if ( this._selectedGrads.includes( name ) )
812
823
  this._makeGrad();
813
824
  }
814
825
 
@@ -1353,11 +1364,14 @@ export default class AudioMotionAnalyzer {
1353
1364
  * this is called 60 times per second by requestAnimationFrame()
1354
1365
  */
1355
1366
  _draw( timestamp ) {
1356
- const ctx = this._canvasCtx,
1367
+ const barSpace = this._barSpace,
1368
+ barSpacePx = this._barSpacePx,
1369
+ ctx = this._canvasCtx,
1357
1370
  canvas = ctx.canvas,
1358
1371
  canvasX = this._scaleX.canvas,
1359
1372
  canvasR = this._scaleR.canvas,
1360
1373
  energy = this._energy,
1374
+ fillAlpha = this.fillAlpha,
1361
1375
  mode = this._mode,
1362
1376
  isAlphaBars = this._isAlphaBars,
1363
1377
  isLedDisplay = this._isLedDisplay,
@@ -1365,6 +1379,7 @@ export default class AudioMotionAnalyzer {
1365
1379
  isLumiBars = this._isLumiBars,
1366
1380
  isBandsMode = this._isBandsMode,
1367
1381
  isOutline = this._isOutline,
1382
+ isOverlay = this.overlay,
1368
1383
  isRadial = this._radial,
1369
1384
  channelLayout = this._chLayout,
1370
1385
  lineWidth = +this.lineWidth, // make sure the damn thing is a number!
@@ -1378,6 +1393,7 @@ export default class AudioMotionAnalyzer {
1378
1393
  centerX = canvas.width >> 1,
1379
1394
  centerY = canvas.height >> 1,
1380
1395
  radius = this._radius,
1396
+ showBgColor = this.showBgColor,
1381
1397
  maxBarHeight = isRadial ? Math.min( centerX, centerY ) - radius : analyzerHeight,
1382
1398
  maxdB = this.maxDecibels,
1383
1399
  mindB = this.minDecibels,
@@ -1461,15 +1477,12 @@ export default class AudioMotionAnalyzer {
1461
1477
  const [ ledCount, ledSpaceH, ledSpaceV, ledHeight ] = this._leds || [];
1462
1478
  const ledPosY = height => Math.max( 0, ( height * ledCount | 0 ) * ( ledHeight + ledSpaceV ) - ledSpaceV );
1463
1479
 
1464
- // select background color
1465
- const bgColor = ( ! this.showBgColor || isLedDisplay && ! this.overlay ) ? '#000' : this._gradients[ this._gradientNames[0] ].bgColor;
1466
-
1467
1480
  // compute the effective bar width, considering the selected bar spacing
1468
1481
  // if led effect is active, ensure at least the spacing from led definitions
1469
- let width = this._barWidth - ( ! isBandsMode ? 0 : Math.max( isLedDisplay ? ledSpaceH : 0, this._barSpacePx ) );
1482
+ let width = this._barWidth - ( ! isBandsMode ? 0 : Math.max( isLedDisplay ? ledSpaceH : 0, barSpacePx ) );
1470
1483
 
1471
1484
  // make sure width is integer for pixel accurate calculation, when no bar spacing is required
1472
- if ( this._barSpace == 0 && ! isLedDisplay )
1485
+ if ( barSpace == 0 && ! isLedDisplay )
1473
1486
  width |= 0;
1474
1487
 
1475
1488
  let currentEnergy = 0;
@@ -1481,24 +1494,26 @@ export default class AudioMotionAnalyzer {
1481
1494
 
1482
1495
  const channelTop = channelLayout == CHANNEL_VERTICAL ? channelHeight * channel + channelGap * channel : 0,
1483
1496
  channelBottom = channelTop + channelHeight,
1484
- analyzerBottom = channelTop + analyzerHeight - ( isLedDisplay && ! this._maximizeLeds ? ledSpaceV : 0 );
1497
+ analyzerBottom = channelTop + analyzerHeight - ( isLedDisplay && ! this._maximizeLeds ? ledSpaceV : 0 ),
1498
+ bgColor = ( ! showBgColor || isLedDisplay && ! isOverlay ) ? '#000' : this._gradients[ this._selectedGrads[ channel ] ].bgColor,
1499
+ mustClear = channel == 0 || ! isRadial && channelLayout != CHANNEL_COMBINED;
1485
1500
 
1486
1501
  if ( useCanvas ) {
1487
1502
  // clear the channel area, if in overlay mode
1488
1503
  // this is done per channel to clear any residue below 0 off the top channel (especially in line graph mode with lineWidth > 1)
1489
- if ( this.overlay && ( ! isRadial || channel == 0 ) )
1504
+ if ( isOverlay && mustClear )
1490
1505
  ctx.clearRect( 0, channelTop - channelGap, canvas.width, channelHeight + channelGap );
1491
1506
 
1492
1507
  // fill the analyzer background if needed (not overlay or overlay + showBgColor)
1493
- if ( ! this.overlay || this.showBgColor ) {
1494
- if ( this.overlay )
1508
+ if ( ! isOverlay || showBgColor ) {
1509
+ if ( isOverlay )
1495
1510
  ctx.globalAlpha = this.bgAlpha;
1496
1511
 
1497
1512
  ctx.fillStyle = bgColor;
1498
1513
 
1499
1514
  // exclude the reflection area when overlay is true and reflexAlpha == 1 (avoids alpha over alpha difference, in case bgAlpha < 1)
1500
- if ( ! isRadial && channelLayout != CHANNEL_COMBINED || channel == 0 )
1501
- ctx.fillRect( initialX, channelTop - channelGap, analyzerWidth, ( this.overlay && this.reflexAlpha == 1 ? analyzerHeight : channelHeight ) + channelGap );
1515
+ if ( mustClear )
1516
+ ctx.fillRect( initialX, channelTop - channelGap, analyzerWidth, ( isOverlay && this.reflexAlpha == 1 ? analyzerHeight : channelHeight ) + channelGap );
1502
1517
 
1503
1518
  ctx.globalAlpha = 1;
1504
1519
  }
@@ -1622,7 +1637,7 @@ export default class AudioMotionAnalyzer {
1622
1637
  if ( isLumiBars || isAlphaBars )
1623
1638
  ctx.globalAlpha = barHeight;
1624
1639
  else if ( isOutline )
1625
- ctx.globalAlpha = this.fillAlpha;
1640
+ ctx.globalAlpha = fillAlpha;
1626
1641
 
1627
1642
  // compute actual bar height on screen
1628
1643
  barHeight = isLedDisplay ? ledPosY( barHeight ) : barHeight * maxBarHeight | 0;
@@ -1671,9 +1686,9 @@ export default class AudioMotionAnalyzer {
1671
1686
  else {
1672
1687
  if ( mode > 0 ) {
1673
1688
  if ( isLedDisplay )
1674
- posX += Math.max( ledSpaceH / 2, this._barSpacePx / 2 );
1689
+ posX += Math.max( ledSpaceH / 2, barSpacePx / 2 );
1675
1690
  else {
1676
- if ( this._barSpace == 0 ) {
1691
+ if ( barSpace == 0 ) {
1677
1692
  posX |= 0;
1678
1693
  if ( i > 0 && posX > this._bars[ i - 1 ].posX + width ) {
1679
1694
  posX--;
@@ -1681,14 +1696,14 @@ export default class AudioMotionAnalyzer {
1681
1696
  }
1682
1697
  }
1683
1698
  else
1684
- posX += this._barSpacePx / 2;
1699
+ posX += barSpacePx / 2;
1685
1700
  }
1686
1701
  }
1687
1702
 
1688
1703
  if ( isLedDisplay ) {
1689
1704
  const x = posX + width / 2;
1690
1705
  // draw "unlit" leds
1691
- if ( this.showBgColor && ! this.overlay ) {
1706
+ if ( showBgColor && ! isOverlay ) {
1692
1707
  const alpha = ctx.globalAlpha;
1693
1708
  ctx.beginPath();
1694
1709
  ctx.moveTo( x, channelTop );
@@ -1770,7 +1785,7 @@ export default class AudioMotionAnalyzer {
1770
1785
  if ( lineWidth > 0 )
1771
1786
  ctx.stroke();
1772
1787
 
1773
- if ( this.fillAlpha > 0 ) {
1788
+ if ( fillAlpha > 0 ) {
1774
1789
  if ( isRadial ) {
1775
1790
  // exclude the center circle from the fill area
1776
1791
  ctx.moveTo( centerX + radius, centerY );
@@ -1781,7 +1796,7 @@ export default class AudioMotionAnalyzer {
1781
1796
  ctx.lineTo( initialX, analyzerBottom );
1782
1797
  }
1783
1798
 
1784
- ctx.globalAlpha = this.fillAlpha;
1799
+ ctx.globalAlpha = fillAlpha;
1785
1800
  ctx.fill();
1786
1801
  ctx.globalAlpha = 1;
1787
1802
  }
@@ -1921,9 +1936,10 @@ export default class AudioMotionAnalyzer {
1921
1936
 
1922
1937
  const ctx = this._canvasCtx,
1923
1938
  canvas = ctx.canvas,
1939
+ channelLayout = this._chLayout,
1924
1940
  isLumiBars = this._isLumiBars,
1925
1941
  isRadial = this._radial,
1926
- gradientHeight = isLumiBars ? canvas.height : canvas.height * ( 1 - this._reflexRatio * ( this._chLayout != CHANNEL_VERTICAL ) ) | 0,
1942
+ gradientHeight = isLumiBars ? canvas.height : canvas.height * ( 1 - this._reflexRatio * ( channelLayout != CHANNEL_VERTICAL ) ) | 0,
1927
1943
  // for vertical stereo we keep the full canvas height and handle the reflex areas while generating the color stops
1928
1944
  analyzerRatio = 1 - this._reflexRatio,
1929
1945
  initialX = this._initialX;
@@ -1934,29 +1950,28 @@ export default class AudioMotionAnalyzer {
1934
1950
  maxRadius = Math.min( centerX, centerY ),
1935
1951
  radius = this._radius;
1936
1952
 
1937
- for ( let chn = 0; chn < 2; chn++ ) {
1938
- const currGradient = this._gradients[ this._gradientNames[ chn ] ],
1953
+ for ( const channel of [0,1] ) {
1954
+ const currGradient = this._gradients[ this._selectedGrads[ channel ] ],
1939
1955
  colorStops = currGradient.colorStops,
1940
1956
  isHorizontal = currGradient.dir == 'h';
1941
1957
 
1942
1958
  let grad;
1943
1959
 
1944
1960
  if ( isRadial )
1945
- grad = ctx.createRadialGradient( centerX, centerY, maxRadius, centerX, centerY, radius - ( maxRadius - radius ) * ( this._chLayout == CHANNEL_VERTICAL ) );
1961
+ grad = ctx.createRadialGradient( centerX, centerY, maxRadius, centerX, centerY, radius - ( maxRadius - radius ) * ( channelLayout == CHANNEL_VERTICAL ) );
1946
1962
  else
1947
1963
  grad = ctx.createLinearGradient( ...( isHorizontal ? [ initialX, 0, initialX + this._analyzerWidth, 0 ] : [ 0, 0, 0, gradientHeight ] ) );
1948
1964
 
1949
1965
  if ( colorStops ) {
1950
- const dual = this._chLayout == CHANNEL_VERTICAL && ! this._splitGradient && ( ! isHorizontal || isRadial );
1966
+ const dual = channelLayout == CHANNEL_VERTICAL && ! this._splitGradient && ( ! isHorizontal || isRadial );
1951
1967
 
1952
1968
  // helper function
1953
1969
  const addColorStop = ( offset, colorInfo ) => grad.addColorStop( offset, colorInfo.color || colorInfo );
1954
1970
 
1955
- for ( let channel = 0; channel < 1 + dual; channel++ ) {
1956
- colorStops.forEach( ( colorInfo, index ) => {
1971
+ for ( let channelArea = 0; channelArea < 1 + dual; channelArea++ ) {
1957
1972
 
1973
+ colorStops.forEach( ( colorInfo, index ) => {
1958
1974
  const maxIndex = colorStops.length - 1;
1959
-
1960
1975
  let offset = colorInfo.pos !== undefined ? colorInfo.pos : index / Math.max( 1, maxIndex );
1961
1976
 
1962
1977
  // in dual mode (not split), use half the original offset for each channel
@@ -1964,15 +1979,15 @@ export default class AudioMotionAnalyzer {
1964
1979
  offset /= 2;
1965
1980
 
1966
1981
  // constrain the offset within the useful analyzer areas (avoid reflex areas)
1967
- if ( this._chLayout == CHANNEL_VERTICAL && ! isLumiBars && ! isRadial && ! isHorizontal ) {
1982
+ if ( channelLayout == CHANNEL_VERTICAL && ! isLumiBars && ! isRadial && ! isHorizontal ) {
1968
1983
  offset *= analyzerRatio;
1969
1984
  // skip the first reflex area in split mode
1970
1985
  if ( ! dual && offset > .5 * analyzerRatio )
1971
1986
  offset += .5 * this._reflexRatio;
1972
1987
  }
1973
1988
 
1974
- // only for non-split gradient
1975
- if ( channel == 1 ) {
1989
+ // only for dual-vertical non-split gradient (creates full gradient on both halves of the canvas)
1990
+ if ( channelArea == 1 ) {
1976
1991
  // add colors in reverse order if radial or lumi are active
1977
1992
  if ( isRadial || isLumiBars ) {
1978
1993
  const revIndex = maxIndex - index;
@@ -1992,14 +2007,14 @@ export default class AudioMotionAnalyzer {
1992
2007
  addColorStop( offset, colorInfo );
1993
2008
 
1994
2009
  // create additional color stop at the end of first channel to prevent bleeding
1995
- if ( this._chLayout == CHANNEL_VERTICAL && index == maxIndex && offset < .5 )
2010
+ if ( channelLayout == CHANNEL_VERTICAL && index == maxIndex && offset < .5 )
1996
2011
  addColorStop( .5, colorInfo );
1997
2012
  });
1998
- }
2013
+ } // for ( let channelArea = 0; channelArea < 1 + dual; channelArea++ )
1999
2014
  }
2000
2015
 
2001
- this._canvasGradients[ chn ] = grad;
2002
- } // for (chn)
2016
+ this._canvasGradients[ channel ] = grad;
2017
+ } // for ( const channel of [0,1] )
2003
2018
  }
2004
2019
 
2005
2020
  /**
@@ -2104,11 +2119,11 @@ export default class AudioMotionAnalyzer {
2104
2119
  throw new AudioMotionError( ERR_UNKNOWN_GRADIENT, name );
2105
2120
 
2106
2121
  if ( ! [0,1].includes( channel ) ) {
2107
- this._gradientNames[1] = name;
2122
+ this._selectedGrads[1] = name;
2108
2123
  channel = 0;
2109
2124
  }
2110
2125
 
2111
- this._gradientNames[ channel ] = name;
2126
+ this._selectedGrads[ channel ] = name;
2112
2127
  this._makeGrad();
2113
2128
  }
2114
2129
 
@@ -2127,7 +2142,7 @@ export default class AudioMotionAnalyzer {
2127
2142
  fftSize : 8192,
2128
2143
  fillAlpha : 1,
2129
2144
  frequencyScale : SCALE_LOG,
2130
- gradient : GRADIENT_CLASSIC,
2145
+ gradient : GRADIENTS[0][0],
2131
2146
  ledBars : false,
2132
2147
  linearAmplitude: false,
2133
2148
  linearBoost : 1,
package/src/index.d.ts CHANGED
@@ -74,7 +74,7 @@ export interface ConstructorOptions extends Options {
74
74
  source?: HTMLMediaElement | AudioNode;
75
75
  }
76
76
 
77
- export type ChannelLayout = "single" | "dualVertical" | "dualCombined";
77
+ export type ChannelLayout = "single" | "dual-vertical" | "dual-combined";
78
78
 
79
79
  export type EnergyPreset = "peak" | "bass" | "lowMid" | "mid" | "highMid" | "treble";
80
80