audiomotion-analyzer 4.0.0-beta.0 → 4.0.0-beta.1
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 +62 -10
- package/package.json +1 -1
- package/src/audioMotion-analyzer.js +157 -63
- package/src/index.d.ts +15 -3
package/README.md
CHANGED
|
@@ -158,6 +158,7 @@ options = {<br>
|
|
|
158
158
|
  [stereo](#stereo-boolean): **false**,<br>
|
|
159
159
|
  [useCanvas](#usecanvas-boolean): **true**,<br>
|
|
160
160
|
  [volume](#volume-number): **1**,<br>
|
|
161
|
+
  [weightingFilter](#weightingFilter-string): **''**<br>
|
|
161
162
|
  [width](#width-number): *undefined*<br>
|
|
162
163
|
}
|
|
163
164
|
|
|
@@ -450,11 +451,24 @@ Defaults to **false**.
|
|
|
450
451
|
|
|
451
452
|
*Available since v4.0.0*
|
|
452
453
|
|
|
453
|
-
When set to *true
|
|
454
|
+
When set to *true*, spectrum amplitudes are represented in linear scale instead of decibels (logarithmic).
|
|
455
|
+
|
|
454
456
|
This may improve the visualization of predominant tones, especially at higher frequencies, but it will make the entire spectrum look much quieter.
|
|
455
457
|
|
|
458
|
+
See also [`linearBoost`](#linearboost-number).
|
|
459
|
+
|
|
456
460
|
Defaults to **false**.
|
|
457
461
|
|
|
462
|
+
### `linearBoost` *number*
|
|
463
|
+
|
|
464
|
+
*Available since v4.0.0*
|
|
465
|
+
|
|
466
|
+
Performs an *n*th-root to amplify low energy values when using linear scale for the amplitude.
|
|
467
|
+
|
|
468
|
+
It should be a number >= 1, while 1 means no boosting. Only effective when [`linearAmplitude`](#linearamplitude-boolean) is set to *true*.
|
|
469
|
+
|
|
470
|
+
Defaults to **1**.
|
|
471
|
+
|
|
458
472
|
### `lineWidth` *number*
|
|
459
473
|
|
|
460
474
|
*Available since v2.0.0*
|
|
@@ -514,7 +528,8 @@ Highest and lowest frequencies represented in the X-axis of the analyzer. Values
|
|
|
514
528
|
|
|
515
529
|
The minimum allowed value is **1**. Trying to set a lower value will throw an `ERR_FREQUENCY_TOO_LOW` [error](#custom-errors).
|
|
516
530
|
|
|
517
|
-
The maximum
|
|
531
|
+
The maximum allowed value is half the sampling rate ([`audioCtx.sampleRate`](#audioctx-audiocontext-object-read-only)), known as the [Nyquist frequency](https://en.wikipedia.org/wiki/Nyquist_frequency).
|
|
532
|
+
Values higher than that will be capped.
|
|
518
533
|
|
|
519
534
|
It is preferable to use the [`setFreqRange()`](#setfreqrange-minfreq-maxfreq-) method and set both values at once, to prevent `minFreq` being higher than the current `maxFreq` or vice-versa at a given moment.
|
|
520
535
|
|
|
@@ -783,6 +798,30 @@ Please note that changing the audio element volume directly will affect the ampl
|
|
|
783
798
|
|
|
784
799
|
Defaults to **1**.
|
|
785
800
|
|
|
801
|
+
### `weightingFilter` *string*
|
|
802
|
+
|
|
803
|
+
*Available since v4.0.0*
|
|
804
|
+
|
|
805
|
+
Select a [weighting filter](https://en.wikipedia.org/wiki/Weighting_filter) for spectrum visualization.
|
|
806
|
+
|
|
807
|
+
Each filter applies a different curve of gain/attenuation to specific frequency ranges, but the general idea is to adjust the
|
|
808
|
+
visualization of frequencies to which the human ear is more or less sensitive.
|
|
809
|
+
Refer to the [weighting filters viewer tool](/tools/weighting-filters.html) for curves and response tables.
|
|
810
|
+
|
|
811
|
+
?> Selecting a weighting filter **does NOT** affect the audio output, only the visualization.
|
|
812
|
+
|
|
813
|
+
weightingFilter | description
|
|
814
|
+
------|------------------------------
|
|
815
|
+
'' (empty string) | No weighting applied (default)
|
|
816
|
+
'A' | A-weighting
|
|
817
|
+
'B' | B-weighting
|
|
818
|
+
'C' | C-weighting
|
|
819
|
+
'D' | D-weighting
|
|
820
|
+
'468' | ITU-R 468 weighting
|
|
821
|
+
|
|
822
|
+
Defaults to **''**.
|
|
823
|
+
|
|
824
|
+
|
|
786
825
|
## Static properties
|
|
787
826
|
|
|
788
827
|
### `AudioMotionAnalyzer.version` *string* *(Read only)*
|
|
@@ -925,8 +964,9 @@ Returns an array with current data for each analyzer bar. Each array element is
|
|
|
925
964
|
```js
|
|
926
965
|
{
|
|
927
966
|
posX: <number>, // horizontal position of this bar on the canvas
|
|
928
|
-
|
|
929
|
-
|
|
967
|
+
freq: <number>, // center frequency for this bar (added in v4.0.0)
|
|
968
|
+
freqLo: <number>, // lower edge frequency
|
|
969
|
+
freqHi: <number>, // upper edge frequency
|
|
930
970
|
peak: <array>, // peak values for left and right channels
|
|
931
971
|
hold: <array>, // peak hold frames for left and right channels - values < 0 mean the peak is falling down
|
|
932
972
|
value: <array> // current amplitude on left and right channels
|
|
@@ -970,7 +1010,8 @@ Use this method inside your callback function to create additional visual effect
|
|
|
970
1010
|
|
|
971
1011
|
Registers a custom color gradient.
|
|
972
1012
|
|
|
973
|
-
`name` must be a non-empty *string* that will be used
|
|
1013
|
+
`name` must be a non-empty *string* that will be used to select this gradient, via the [`gradient`](#gradient-string) property.
|
|
1014
|
+
`options` must be an object as shown below:
|
|
974
1015
|
|
|
975
1016
|
```js
|
|
976
1017
|
const options = {
|
|
@@ -978,12 +1019,21 @@ const options = {
|
|
|
978
1019
|
dir: 'h', // add this property to create a horizontal gradient (optional)
|
|
979
1020
|
colorStops: [ // list your gradient colors in this array (at least 2 entries are required)
|
|
980
1021
|
'red', // colors may be defined in any valid CSS format
|
|
981
|
-
{ pos: .6, color: '#ff0' }, // use an object to adjust the
|
|
1022
|
+
{ pos: .6, color: '#ff0' }, // use an object to adjust the offset (0 to 1) of a colorStop
|
|
982
1023
|
'hsl( 120, 100%, 50% )' // colors may be defined in any valid CSS format
|
|
983
1024
|
]
|
|
984
1025
|
}
|
|
985
1026
|
|
|
986
|
-
audioMotion.registerGradient( '
|
|
1027
|
+
audioMotion.registerGradient( 'myGradient', options );
|
|
1028
|
+
```
|
|
1029
|
+
|
|
1030
|
+
!> In TypeScript projects make sure to import the `GradientOptions` definition and use it as the type of your object, like so:
|
|
1031
|
+
|
|
1032
|
+
```js
|
|
1033
|
+
import AudioMotionAnalyzer, { GradientOptions } from 'audiomotion-analyzer'
|
|
1034
|
+
|
|
1035
|
+
const options: GradientOptions = {
|
|
1036
|
+
⋮
|
|
987
1037
|
```
|
|
988
1038
|
|
|
989
1039
|
Additional information about [gradient color-stops](https://developer.mozilla.org/en-US/docs/Web/API/CanvasGradient/addColorStop).
|
|
@@ -996,11 +1046,13 @@ Sets the analyzer nominal dimensions in pixels. See [`height`](#height-number) a
|
|
|
996
1046
|
|
|
997
1047
|
Sets the desired frequency range. Values are expressed in Hz (Hertz).
|
|
998
1048
|
|
|
1049
|
+
See [`minFreq` and `maxFreq`](#minfreq-number) for lower and upper limit values.
|
|
1050
|
+
|
|
999
1051
|
### `setLedParams( [params] )`
|
|
1000
1052
|
|
|
1001
1053
|
*Available since v3.2.0*
|
|
1002
1054
|
|
|
1003
|
-
Customize parameters used to create the [
|
|
1055
|
+
Customize parameters used to create the [`ledBars`](#ledbars-boolean) effect.
|
|
1004
1056
|
|
|
1005
1057
|
`params` should be an object with the following structure:
|
|
1006
1058
|
|
|
@@ -1014,9 +1066,9 @@ const params = {
|
|
|
1014
1066
|
|
|
1015
1067
|
property | description
|
|
1016
1068
|
----------|-------------
|
|
1017
|
-
`maxLeds` | maximum desired number of LED elements per analyzer bar
|
|
1069
|
+
`maxLeds` | **maximum** desired number of LED elements per analyzer bar
|
|
1018
1070
|
`spaceV` | vertical spacing ratio, relative to the LED height (**1** means spacing is the same as the LED height)
|
|
1019
|
-
`spaceH` | **minimum** horizontal spacing ratio, relative to the available width
|
|
1071
|
+
`spaceH` | **minimum** horizontal spacing ratio, relative to the available width for each band, or a literal pixel value if **>= 1**;<br>this behaves exactly like [`barSpace`](#barspace-number) and the largest spacing (resulting from either `barSpace` or `spaceH`) will prevail.
|
|
1020
1072
|
|
|
1021
1073
|
The available canvas height is initially divided by `maxLeds` and vertical spacing is calculated observing the `spaceV` ratio;
|
|
1022
1074
|
if necessary, the led count is decreased until both the led segment and the vertical spacing are at least 2px tall.
|
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.1",
|
|
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.1
|
|
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.1';
|
|
11
11
|
|
|
12
12
|
// internal constants
|
|
13
13
|
const TAU = 2 * Math.PI,
|
|
@@ -24,7 +24,8 @@ const CANVAS_BACKGROUND_COLOR = '#000',
|
|
|
24
24
|
SCALEX_LABEL_COLOR = '#fff',
|
|
25
25
|
SCALEX_HIGHLIGHT_COLOR = '#4f4',
|
|
26
26
|
SCALEY_LABEL_COLOR = '#888',
|
|
27
|
-
SCALEY_MIDLINE_COLOR = '#555'
|
|
27
|
+
SCALEY_MIDLINE_COLOR = '#555',
|
|
28
|
+
WEIGHTING_FILTERS = [ '', 'A', 'B', 'C', 'D', '468' ];
|
|
28
29
|
|
|
29
30
|
// custom error messages
|
|
30
31
|
const ERR_AUDIO_CONTEXT_FAIL = [ 'ERR_AUDIO_CONTEXT_FAIL', 'Could not create audio context. Web Audio API not supported?' ],
|
|
@@ -295,7 +296,7 @@ export default class AudioMotionAnalyzer {
|
|
|
295
296
|
for ( const i of [0,1] )
|
|
296
297
|
this._analyzer[ i ].fftSize = value;
|
|
297
298
|
const binCount = this._analyzer[0].frequencyBinCount;
|
|
298
|
-
this._fftData = [ new
|
|
299
|
+
this._fftData = [ new Float32Array( binCount ), new Float32Array( binCount ) ];
|
|
299
300
|
this._calcBars();
|
|
300
301
|
}
|
|
301
302
|
|
|
@@ -326,6 +327,20 @@ export default class AudioMotionAnalyzer {
|
|
|
326
327
|
this._calcAux();
|
|
327
328
|
}
|
|
328
329
|
|
|
330
|
+
get linearAmplitude() {
|
|
331
|
+
return this._linearAmplitude;
|
|
332
|
+
}
|
|
333
|
+
set linearAmplitude( value ) {
|
|
334
|
+
this._linearAmplitude = !! value;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
get linearBoost() {
|
|
338
|
+
return this._linearBoost;
|
|
339
|
+
}
|
|
340
|
+
set linearBoost( value ) {
|
|
341
|
+
this._linearBoost = value >= 1 ? +value : 1;
|
|
342
|
+
}
|
|
343
|
+
|
|
329
344
|
get loRes() {
|
|
330
345
|
return this._loRes;
|
|
331
346
|
}
|
|
@@ -503,6 +518,13 @@ export default class AudioMotionAnalyzer {
|
|
|
503
518
|
this._output.gain.value = value;
|
|
504
519
|
}
|
|
505
520
|
|
|
521
|
+
get weightingFilter() {
|
|
522
|
+
return this._weightingFilter;
|
|
523
|
+
}
|
|
524
|
+
set weightingFilter( value ) {
|
|
525
|
+
this._weightingFilter = WEIGHTING_FILTERS[ Math.max( 0, WEIGHTING_FILTERS.indexOf( ( '' + value ).toUpperCase() ) ) ];
|
|
526
|
+
}
|
|
527
|
+
|
|
506
528
|
get width() {
|
|
507
529
|
return this._width;
|
|
508
530
|
}
|
|
@@ -701,10 +723,10 @@ export default class AudioMotionAnalyzer {
|
|
|
701
723
|
let energy = 0;
|
|
702
724
|
for ( let channel = 0; channel < chnCount; channel++ ) {
|
|
703
725
|
for ( let i = startBin; i <= endBin; i++ )
|
|
704
|
-
energy += this._fftData[ channel ][ i ];
|
|
726
|
+
energy += this._normalizedB( this._fftData[ channel ][ i ] );
|
|
705
727
|
}
|
|
706
728
|
|
|
707
|
-
return energy / ( endBin - startBin + 1 ) / chnCount
|
|
729
|
+
return energy / ( endBin - startBin + 1 ) / chnCount;
|
|
708
730
|
}
|
|
709
731
|
|
|
710
732
|
/**
|
|
@@ -855,6 +877,13 @@ export default class AudioMotionAnalyzer {
|
|
|
855
877
|
* ==========================================================================
|
|
856
878
|
*/
|
|
857
879
|
|
|
880
|
+
/**
|
|
881
|
+
* Return the frequency (in Hz) for a given FFT bin
|
|
882
|
+
*/
|
|
883
|
+
_binToFreq( bin ) {
|
|
884
|
+
return bin * this.audioCtx.sampleRate / this.fftSize || 1; // returns 1 for bin 0
|
|
885
|
+
}
|
|
886
|
+
|
|
858
887
|
/**
|
|
859
888
|
* Calculate auxiliary values and flags
|
|
860
889
|
*/
|
|
@@ -911,10 +940,9 @@ export default class AudioMotionAnalyzer {
|
|
|
911
940
|
if ( ! this._ready )
|
|
912
941
|
return;
|
|
913
942
|
|
|
914
|
-
// helper
|
|
915
|
-
const binToFreq = bin => bin * this.audioCtx.sampleRate / this.fftSize || 1; // returns 1 for bin 0
|
|
916
|
-
const barsPush = args => bars.push( { ...args, peak: [0,0], hold: [0], value: [0] } );
|
|
943
|
+
// helper function
|
|
917
944
|
// bar object: { posX, freq, freqLo, freqHi, binLo, binHi, ratioLo, ratioHi, peak, hold, value }
|
|
945
|
+
const barsPush = args => bars.push( { ...args, peak: [0,0], hold: [0], value: [0] } );
|
|
918
946
|
|
|
919
947
|
const analyzerWidth = this._analyzerWidth,
|
|
920
948
|
initialX = this._initialX,
|
|
@@ -949,8 +977,8 @@ export default class AudioMotionAnalyzer {
|
|
|
949
977
|
// helper function to calculate FFT bin and interpolation ratio for a given frequency
|
|
950
978
|
const calcRatio = freq => {
|
|
951
979
|
const bin = this._freqToBin( freq, 'floor' ), // find closest FFT bin
|
|
952
|
-
lower =
|
|
953
|
-
upper =
|
|
980
|
+
lower = this._binToFreq( bin ),
|
|
981
|
+
upper = this._binToFreq( bin + 1 ),
|
|
954
982
|
ratio = Math.log2( freq / lower ) / Math.log2( upper / lower );
|
|
955
983
|
|
|
956
984
|
return [ bin, ratio ];
|
|
@@ -1047,7 +1075,7 @@ export default class AudioMotionAnalyzer {
|
|
|
1047
1075
|
let lastPos = -999;
|
|
1048
1076
|
|
|
1049
1077
|
for ( let i = minIndex; i <= maxIndex; i++ ) {
|
|
1050
|
-
const freq =
|
|
1078
|
+
const freq = this._binToFreq( i ), // frequency represented by this index
|
|
1051
1079
|
posX = initialX + Math.round( logWidth * ( Math.log10( freq ) - minLog ) ); // avoid fractionary pixel values
|
|
1052
1080
|
|
|
1053
1081
|
// if it's on a different X-coordinate, create a new bar for this frequency
|
|
@@ -1251,7 +1279,7 @@ export default class AudioMotionAnalyzer {
|
|
|
1251
1279
|
mode = this._mode,
|
|
1252
1280
|
isAlphaBars = this._isAlphaBars,
|
|
1253
1281
|
isLedDisplay = this._isLedDisplay,
|
|
1254
|
-
|
|
1282
|
+
isLinear = this._linearAmplitude,
|
|
1255
1283
|
isLumiBars = this._isLumiBars,
|
|
1256
1284
|
isOctaveBands = this._isOctaveBands,
|
|
1257
1285
|
isOutline = this._isOutline,
|
|
@@ -1271,12 +1299,51 @@ export default class AudioMotionAnalyzer {
|
|
|
1271
1299
|
maxBarHeight = isRadial ? Math.min( centerX, centerY ) - radius : analyzerHeight,
|
|
1272
1300
|
maxdB = this.maxDecibels,
|
|
1273
1301
|
mindB = this.minDecibels,
|
|
1274
|
-
|
|
1275
|
-
useCanvas = this.useCanvas
|
|
1302
|
+
dbRange = maxdB - mindB,
|
|
1303
|
+
useCanvas = this.useCanvas,
|
|
1304
|
+
weightingFilter= this._weightingFilter;
|
|
1276
1305
|
|
|
1277
1306
|
if ( energy.val > 0 )
|
|
1278
1307
|
this._spinAngle += this._spinSpeed * RPM;
|
|
1279
1308
|
|
|
1309
|
+
// helper function - apply the selected weighting filter and return dB gain for a given frequency
|
|
1310
|
+
const weightingdB = freq => {
|
|
1311
|
+
const f2 = freq ** 2,
|
|
1312
|
+
SQ20_6 = 424.36,
|
|
1313
|
+
SQ107_7 = 11599.29,
|
|
1314
|
+
SQ158_5 = 25122.25,
|
|
1315
|
+
SQ737_9 = 544496.41,
|
|
1316
|
+
SQ12194 = 148693636,
|
|
1317
|
+
linearTodB = value => 20 * Math.log10( value );
|
|
1318
|
+
|
|
1319
|
+
switch ( weightingFilter ) {
|
|
1320
|
+
case 'A' : // A-weighting https://en.wikipedia.org/wiki/A-weighting
|
|
1321
|
+
const rA = ( SQ12194 * f2 ** 2 ) / ( ( f2 + SQ20_6 ) * Math.sqrt( ( f2 + SQ107_7 ) * ( f2 + SQ737_9 ) ) * ( f2 + SQ12194 ) );
|
|
1322
|
+
return 2 + linearTodB( rA );
|
|
1323
|
+
|
|
1324
|
+
case 'B' :
|
|
1325
|
+
const rB = ( SQ12194 * f2 * freq ) / ( ( f2 + SQ20_6 ) * Math.sqrt( f2 + SQ158_5 ) * ( f2 + SQ12194 ) );
|
|
1326
|
+
return .17 + linearTodB( rB );
|
|
1327
|
+
|
|
1328
|
+
case 'C' :
|
|
1329
|
+
const rC = ( SQ12194 * f2 ) / ( ( f2 + SQ20_6 ) * ( f2 + SQ12194 ) );
|
|
1330
|
+
return .06 + linearTodB( rC );
|
|
1331
|
+
|
|
1332
|
+
case 'D' :
|
|
1333
|
+
const h = ( ( 1037918.48 - f2 ) ** 2 + 1080768.16 * f2 ) / ( ( 9837328 - f2 ) ** 2 + 11723776 * f2 ),
|
|
1334
|
+
rD = ( freq / 6.8966888496476e-5 ) * Math.sqrt( h / ( ( f2 + 79919.29 ) * ( f2 + 1345600 ) ) );
|
|
1335
|
+
return linearTodB( rD );
|
|
1336
|
+
|
|
1337
|
+
case '468' : // ITU-R 468 https://en.wikipedia.org/wiki/ITU-R_468_noise_weighting
|
|
1338
|
+
const h1 = -4.737338981378384e-24 * freq ** 6 + 2.043828333606125e-15 * freq ** 4 - 1.363894795463638e-7 * f2 + 1,
|
|
1339
|
+
h2 = 1.306612257412824e-19 * freq ** 5 - 2.118150887518656e-11 * freq ** 3 + 5.559488023498642e-4 * freq,
|
|
1340
|
+
rI = 1.246332637532143e-4 * freq / Math.hypot( h1, h2 );
|
|
1341
|
+
return 18.2 + linearTodB( rI );
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
return 0; // unknown filter
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1280
1347
|
const strokeIf = flag => {
|
|
1281
1348
|
if ( flag && lineWidth ) {
|
|
1282
1349
|
const alpha = ctx.globalAlpha;
|
|
@@ -1357,9 +1424,9 @@ export default class AudioMotionAnalyzer {
|
|
|
1357
1424
|
if ( this.showScaleY && ! isLumiBars && ! isRadial ) {
|
|
1358
1425
|
const scaleWidth = canvasX.height,
|
|
1359
1426
|
fontSize = scaleWidth >> 1,
|
|
1360
|
-
max =
|
|
1361
|
-
min =
|
|
1362
|
-
incr =
|
|
1427
|
+
max = isLinear ? 100 : maxdB,
|
|
1428
|
+
min = isLinear ? 0 : mindB,
|
|
1429
|
+
incr = isLinear ? 20 : 5,
|
|
1363
1430
|
interval = analyzerHeight / ( max - min );
|
|
1364
1431
|
|
|
1365
1432
|
ctx.fillStyle = SCALEY_LABEL_COLOR;
|
|
@@ -1410,12 +1477,16 @@ export default class AudioMotionAnalyzer {
|
|
|
1410
1477
|
} // if ( useCanvas )
|
|
1411
1478
|
|
|
1412
1479
|
// get a new array of data from the FFT
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1480
|
+
let fftData = this._fftData[ channel ];
|
|
1481
|
+
this._analyzer[ channel ].getFloatFrequencyData( fftData );
|
|
1482
|
+
|
|
1483
|
+
// apply weighting
|
|
1484
|
+
if ( weightingFilter )
|
|
1485
|
+
fftData = fftData.map( ( val, idx ) => val + weightingdB( this._binToFreq( idx ) ) );
|
|
1416
1486
|
|
|
1417
1487
|
// helper function for FFT data interpolation
|
|
1418
|
-
const
|
|
1488
|
+
const lastBin = fftData.length - 1,
|
|
1489
|
+
interpolate = ( bin, ratio ) => fftData[ bin ] + ( bin < lastBin ? ( fftData[ bin + 1 ] - fftData[ bin ] ) * ratio : 0 );
|
|
1419
1490
|
|
|
1420
1491
|
// start drawing path (for mode 10)
|
|
1421
1492
|
ctx.beginPath();
|
|
@@ -1428,7 +1499,7 @@ export default class AudioMotionAnalyzer {
|
|
|
1428
1499
|
for ( let i = 0; i < nBars; i++ ) {
|
|
1429
1500
|
|
|
1430
1501
|
const bar = this._bars[ i ],
|
|
1431
|
-
{ binLo, binHi, ratioLo, ratioHi } = bar;
|
|
1502
|
+
{ freq, binLo, binHi, ratioLo, ratioHi } = bar;
|
|
1432
1503
|
|
|
1433
1504
|
let barHeight = Math.max( interpolate( binLo, ratioLo ), interpolate( binHi, ratioHi ) );
|
|
1434
1505
|
|
|
@@ -1438,9 +1509,9 @@ export default class AudioMotionAnalyzer {
|
|
|
1438
1509
|
barHeight = fftData[ j ];
|
|
1439
1510
|
}
|
|
1440
1511
|
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1512
|
+
// normalize bar amplitude in [0;1] range
|
|
1513
|
+
barHeight = this._normalizedB( barHeight );
|
|
1514
|
+
|
|
1444
1515
|
bar.value[ channel ] = barHeight;
|
|
1445
1516
|
currentEnergy += barHeight;
|
|
1446
1517
|
|
|
@@ -1830,6 +1901,27 @@ export default class AudioMotionAnalyzer {
|
|
|
1830
1901
|
this._canvasGradient = grad;
|
|
1831
1902
|
}
|
|
1832
1903
|
|
|
1904
|
+
/**
|
|
1905
|
+
* Normalize a dB value in the [0;1] range
|
|
1906
|
+
*/
|
|
1907
|
+
_normalizedB( value ) {
|
|
1908
|
+
const isLinear = this._linearAmplitude,
|
|
1909
|
+
boost = isLinear ? 1 / this._linearBoost : 1,
|
|
1910
|
+
clamp = ( val, min, max ) => val <= min ? min : val >= max ? max : val,
|
|
1911
|
+
dBToLinear = val => 10 ** ( val / 20 );
|
|
1912
|
+
|
|
1913
|
+
let maxValue = this.maxDecibels,
|
|
1914
|
+
minValue = this.minDecibels;
|
|
1915
|
+
|
|
1916
|
+
if ( isLinear ) {
|
|
1917
|
+
maxValue = dBToLinear( maxValue );
|
|
1918
|
+
minValue = dBToLinear( minValue );
|
|
1919
|
+
value = dBToLinear( value ) ** boost;
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
return clamp( ( value - minValue ) / ( maxValue - minValue ) ** boost, 0, 1 );
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1833
1925
|
/**
|
|
1834
1926
|
* Internal function to change canvas dimensions on demand
|
|
1835
1927
|
*/
|
|
@@ -1907,44 +1999,46 @@ export default class AudioMotionAnalyzer {
|
|
|
1907
1999
|
|
|
1908
2000
|
// settings defaults
|
|
1909
2001
|
const defaults = {
|
|
1910
|
-
alphaBars
|
|
1911
|
-
ansiBands
|
|
1912
|
-
barSpace
|
|
1913
|
-
bgAlpha
|
|
1914
|
-
fftSize
|
|
1915
|
-
fillAlpha
|
|
1916
|
-
gradient
|
|
1917
|
-
ledBars
|
|
2002
|
+
alphaBars : false,
|
|
2003
|
+
ansiBands : false,
|
|
2004
|
+
barSpace : 0.1,
|
|
2005
|
+
bgAlpha : 0.7,
|
|
2006
|
+
fftSize : 8192,
|
|
2007
|
+
fillAlpha : 1,
|
|
2008
|
+
gradient : 'classic',
|
|
2009
|
+
ledBars : false,
|
|
1918
2010
|
linearAmplitude: false,
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
2011
|
+
linearBoost : 1,
|
|
2012
|
+
lineWidth : 0,
|
|
2013
|
+
loRes : false,
|
|
2014
|
+
lumiBars : false,
|
|
2015
|
+
maxDecibels : -25,
|
|
2016
|
+
maxFreq : 22000,
|
|
2017
|
+
minDecibels : -85,
|
|
2018
|
+
minFreq : 20,
|
|
2019
|
+
mirror : 0,
|
|
2020
|
+
mode : 0,
|
|
2021
|
+
noteLabels : false,
|
|
2022
|
+
outlineBars : false,
|
|
2023
|
+
overlay : false,
|
|
2024
|
+
radial : false,
|
|
2025
|
+
reflexAlpha : 0.15,
|
|
2026
|
+
reflexBright : 1,
|
|
2027
|
+
reflexFit : true,
|
|
2028
|
+
reflexRatio : 0,
|
|
2029
|
+
showBgColor : true,
|
|
2030
|
+
showFPS : false,
|
|
2031
|
+
showPeaks : true,
|
|
2032
|
+
showScaleX : true,
|
|
2033
|
+
showScaleY : false,
|
|
2034
|
+
smoothing : 0.5,
|
|
2035
|
+
spinSpeed : 0,
|
|
2036
|
+
splitGradient : false,
|
|
2037
|
+
start : true,
|
|
2038
|
+
stereo : false,
|
|
2039
|
+
useCanvas : true,
|
|
2040
|
+
volume : 1,
|
|
2041
|
+
weightingFilter: ''
|
|
1948
2042
|
};
|
|
1949
2043
|
|
|
1950
2044
|
// callback functions properties
|
package/src/index.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export interface Options {
|
|
|
17
17
|
height?: number;
|
|
18
18
|
ledBars?: boolean;
|
|
19
19
|
linearAmplitude?: boolean;
|
|
20
|
+
linearBoost?: boolean;
|
|
20
21
|
lineWidth?: number;
|
|
21
22
|
loRes?: boolean;
|
|
22
23
|
lumiBars?: boolean;
|
|
@@ -48,6 +49,7 @@ export interface Options {
|
|
|
48
49
|
stereo?: boolean;
|
|
49
50
|
useCanvas?: boolean;
|
|
50
51
|
volume?: number;
|
|
52
|
+
weightingFilter?: WeightingFilter;
|
|
51
53
|
width?: number;
|
|
52
54
|
}
|
|
53
55
|
|
|
@@ -72,6 +74,8 @@ type EnergyPreset = "peak" | "bass" | "lowMid" | "mid" | "highMid" | "treble";
|
|
|
72
74
|
|
|
73
75
|
type GradientColorStop = string | { pos: number; color: string };
|
|
74
76
|
|
|
77
|
+
type WeightingFilter = "" | "A" | "B" | "C" | "D" | "468";
|
|
78
|
+
|
|
75
79
|
type ArrayTwoOrMore<T> = {
|
|
76
80
|
0: T
|
|
77
81
|
1: T
|
|
@@ -85,8 +89,8 @@ export interface GradientOptions {
|
|
|
85
89
|
|
|
86
90
|
export interface LedParameters {
|
|
87
91
|
maxLeds: number;
|
|
88
|
-
|
|
89
|
-
|
|
92
|
+
spaceV: number;
|
|
93
|
+
spaceH: number;
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
declare class AudioMotionAnalyzer {
|
|
@@ -141,7 +145,12 @@ declare class AudioMotionAnalyzer {
|
|
|
141
145
|
get ledBars(): boolean;
|
|
142
146
|
set ledBars(value: boolean);
|
|
143
147
|
|
|
144
|
-
|
|
148
|
+
get linearAmplitude(): boolean;
|
|
149
|
+
set linearAmplitude(value: boolean);
|
|
150
|
+
|
|
151
|
+
get linearBoost(): number;
|
|
152
|
+
set linearBoost(value: number);
|
|
153
|
+
|
|
145
154
|
public lineWidth: number;
|
|
146
155
|
|
|
147
156
|
get loRes(): boolean;
|
|
@@ -211,6 +220,9 @@ declare class AudioMotionAnalyzer {
|
|
|
211
220
|
get volume(): number;
|
|
212
221
|
set volume(value: number);
|
|
213
222
|
|
|
223
|
+
get weightingFilter(): WeightingFilter;
|
|
224
|
+
set weightingFilter(value: WeightingFilter);
|
|
225
|
+
|
|
214
226
|
static get version(): string;
|
|
215
227
|
|
|
216
228
|
public onCanvasDraw: OnCanvasDrawFunction | undefined;
|