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 +43 -22
- package/package.json +1 -1
- package/src/audioMotion-analyzer.js +94 -79
- package/src/index.d.ts +1 -1
package/README.md
CHANGED
|
@@ -161,7 +161,6 @@ options = {<br>
|
|
|
161
161
|
  [spinSpeed](#spinspeed-number): **0**,<br>
|
|
162
162
|
  [splitGradient](#splitgradient-boolean): **false**,<br>
|
|
163
163
|
  [start](#start-boolean): **true**,<br>
|
|
164
|
-
  [stereo](#stereo-deprecated-boolean): **false**, // DEPRECATED - use channelLayout instead<br>
|
|
165
164
|
  [useCanvas](#usecanvas-boolean): **true**,<br>
|
|
166
165
|
  [volume](#volume-number): **1**,<br>
|
|
167
166
|
  [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)
|
|
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
|
|
328
|
-
|
|
329
|
-
'single'
|
|
330
|
-
'
|
|
331
|
-
'
|
|
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 [
|
|
418
|
+
It must be a built-in or registered gradient name (see [`registerGradient()`](#registergradient-name-options-)).
|
|
420
419
|
|
|
421
|
-
|
|
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' | 
|
|
429
|
+
'orangered' | 
|
|
430
|
+
'prism' | 
|
|
431
|
+
'rainbow' | 
|
|
432
|
+
'steelblue' | 
|
|
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
|
-
|
|
443
|
+
Select gradients for the left and right analyzer channels independently, for use with a dual [`channelLayout`](#channellayout-string).
|
|
431
444
|
|
|
432
|
-
|
|
445
|
+
**_Single_** channel layout will use the gradient selected by `gradientLeft`.
|
|
433
446
|
|
|
434
|
-
|
|
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
|
|
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
|
-
|
|
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 *'
|
|
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
|
|
829
|
+
When set to *true* and [`channelLayout`](#channellayout-string) is **_dual-vertical_**, the gradient will be split between channels.
|
|
813
830
|
|
|
814
|
-
|
|
831
|
+
When *false*, both channels will use the full gradient.
|
|
832
|
+
|
|
833
|
+
| gradient: *'classic'* - splitGradient: *false* | gradient: *'classic'* - splitGradient: *true* |
|
|
815
834
|
|:--:|:--:|
|
|
816
|
-
|  |  |
|
|
817
836
|
|
|
818
|
-
This option has no effect on horizontal gradients
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
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
|
+
"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.
|
|
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.
|
|
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 = '
|
|
19
|
+
CHANNEL_COMBINED = 'dual-combined',
|
|
20
20
|
CHANNEL_SINGLE = 'single',
|
|
21
|
-
CHANNEL_VERTICAL = '
|
|
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.
|
|
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
|
-
|
|
96
|
-
|
|
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
|
|
319
|
-
this._chLayout =
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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,
|
|
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 (
|
|
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 (
|
|
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 ( !
|
|
1494
|
-
if (
|
|
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 (
|
|
1501
|
-
ctx.fillRect( initialX, channelTop - channelGap, analyzerWidth, (
|
|
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 =
|
|
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,
|
|
1689
|
+
posX += Math.max( ledSpaceH / 2, barSpacePx / 2 );
|
|
1675
1690
|
else {
|
|
1676
|
-
if (
|
|
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 +=
|
|
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 (
|
|
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 (
|
|
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 =
|
|
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 * (
|
|
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 (
|
|
1938
|
-
const currGradient = this._gradients[ this.
|
|
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 ) * (
|
|
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 =
|
|
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
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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[
|
|
2002
|
-
} // for (
|
|
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.
|
|
2122
|
+
this._selectedGrads[1] = name;
|
|
2108
2123
|
channel = 0;
|
|
2109
2124
|
}
|
|
2110
2125
|
|
|
2111
|
-
this.
|
|
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 :
|
|
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" | "
|
|
77
|
+
export type ChannelLayout = "single" | "dual-vertical" | "dual-combined";
|
|
78
78
|
|
|
79
79
|
export type EnergyPreset = "peak" | "bass" | "lowMid" | "mid" | "highMid" | "treble";
|
|
80
80
|
|