From 289423bc96e92c49dc6bb3b5a6eb83ada0d230c4 Mon Sep 17 00:00:00 2001 From: Staijn1 Date: Mon, 19 Dec 2022 11:44:28 +0100 Subject: [PATCH 01/10] Fix boolean type for linearBoost to number --- src/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.d.ts b/src/index.d.ts index 5d82356..1cea1e8 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -17,7 +17,7 @@ export interface Options { height?: number; ledBars?: boolean; linearAmplitude?: boolean; - linearBoost?: boolean; + linearBoost?: number; lineWidth?: number; loRes?: boolean; lumiBars?: boolean; From dd9c393766dfdd6652baf92ec3f452d2a960da2a Mon Sep 17 00:00:00 2001 From: Staijn1 Date: Mon, 4 Sep 2023 23:35:09 +0200 Subject: [PATCH 02/10] Rename "value" parameter in toggleAnalyzer to improve clarity on what it does --- src/audioMotion-analyzer.js | 16 +++++++++------- src/index.d.ts | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/audioMotion-analyzer.js b/src/audioMotion-analyzer.js index 54f07a2..e29182d 100644 --- a/src/audioMotion-analyzer.js +++ b/src/audioMotion-analyzer.js @@ -942,20 +942,22 @@ export default class AudioMotionAnalyzer { /** * Start / stop canvas animation * - * @param {boolean} [value] if undefined, inverts the current status + * @param {boolean} [mustEnable] if undefined, inverts the current status * @returns {boolean} resulting status after the change */ - toggleAnalyzer( value ) { - const started = this.isOn; + toggleAnalyzer( mustEnable ) { + const hasStarted = this.isOn; - if ( value === undefined ) - value = ! started; + if ( mustEnable === undefined ) + mustEnable = ! hasStarted; - if ( started && ! value ) { + // Stop the analyzer if it was already running and must be disabled + if ( hasStarted && ! mustEnable ) { cancelAnimationFrame( this._runId ); this._runId = undefined; } - else if ( ! started && value ) { + // Start the analyzer if it was stopped and must be enabled + else if ( ! hasStarted && mustEnable ) { this._frame = this._fps = 0; this._time = performance.now(); this._runId = requestAnimationFrame( timestamp => this._draw( timestamp ) ); diff --git a/src/index.d.ts b/src/index.d.ts index d1b7018..6ae454a 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -289,7 +289,7 @@ declare class AudioMotionAnalyzer { public setOptions(options?: Options): void; public setSensitivity(minDecibels: number, maxDecibels: number): void; - public toggleAnalyzer(value?: boolean): boolean; + public toggleAnalyzer(mustEnable?: boolean): boolean; public toggleFullscreen(): void; } From 0661c75e2da4cf599c9ecd04df205a0b3fe7f2f0 Mon Sep 17 00:00:00 2001 From: Staijn1 Date: Wed, 6 Sep 2023 19:01:39 +0200 Subject: [PATCH 03/10] Rename mustEnable to force as discussed in PR #55 --- src/audioMotion-analyzer.js | 12 ++++++------ src/index.d.ts | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/audioMotion-analyzer.js b/src/audioMotion-analyzer.js index e29182d..c8d5189 100644 --- a/src/audioMotion-analyzer.js +++ b/src/audioMotion-analyzer.js @@ -942,22 +942,22 @@ export default class AudioMotionAnalyzer { /** * Start / stop canvas animation * - * @param {boolean} [mustEnable] if undefined, inverts the current status + * @param {boolean} [force] if undefined, inverts the current status * @returns {boolean} resulting status after the change */ - toggleAnalyzer( mustEnable ) { + toggleAnalyzer( force ) { const hasStarted = this.isOn; - if ( mustEnable === undefined ) - mustEnable = ! hasStarted; + if ( force === undefined ) + force = ! hasStarted; // Stop the analyzer if it was already running and must be disabled - if ( hasStarted && ! mustEnable ) { + if ( hasStarted && ! force ) { cancelAnimationFrame( this._runId ); this._runId = undefined; } // Start the analyzer if it was stopped and must be enabled - else if ( ! hasStarted && mustEnable ) { + else if ( ! hasStarted && force ) { this._frame = this._fps = 0; this._time = performance.now(); this._runId = requestAnimationFrame( timestamp => this._draw( timestamp ) ); diff --git a/src/index.d.ts b/src/index.d.ts index 6ae454a..10ff5a2 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -289,7 +289,7 @@ declare class AudioMotionAnalyzer { public setOptions(options?: Options): void; public setSensitivity(minDecibels: number, maxDecibels: number): void; - public toggleAnalyzer(mustEnable?: boolean): boolean; + public toggleAnalyzer(force?: boolean): boolean; public toggleFullscreen(): void; } From 4d6422f650cfdcf3712662f19991774026f7f036 Mon Sep 17 00:00:00 2001 From: Staijn1 Date: Mon, 22 Jan 2024 22:33:35 +0100 Subject: [PATCH 04/10] First attempt at adding particles that move bases on the bass levels --- src/audioMotion-analyzer.js | 110 +++++++++++++++++++++++++++++++++++- src/index.d.ts | 1 + 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/src/audioMotion-analyzer.js b/src/audioMotion-analyzer.js index f82eaa3..d664e82 100644 --- a/src/audioMotion-analyzer.js +++ b/src/audioMotion-analyzer.js @@ -101,6 +101,62 @@ class AudioMotionError extends Error { } } +class Particle { + constructor(spawnAtX, spawnAtY, initialVelocity, angle, size, boundaries) { + this._x = spawnAtX; + this._y = spawnAtY; + this._initialVelocity = initialVelocity; + this._angle = angle; + this._size = size; + this._initialSize = size; + this._boundaries = boundaries; + this._currentWindDirection = Math.random() * 2 - 1; // Initial wind direction + this._nextWindDirection = Math.random() * 2 - 1; // Next wind direction + this._windChangeFrames = 0; // Frame counter for wind direction change + } + + update(bassEnergy) { + let velocity = this._initialVelocity + bassEnergy * 1.5; + let velocityX = Math.cos(this._angle) * velocity; + let velocityY = Math.sin(this._angle) * velocity; + + // Check if it's time to change the wind direction + if (this._windChangeFrames <= 0) { + // Update the wind direction and reset the frame counter + this._currentWindDirection = this._nextWindDirection; + this._nextWindDirection = Math.random() * 2 - 1; + this._windChangeFrames = Math.floor(Math.random() * 60) + 60; // Change wind direction every 60 to 120 frames + } + + // Interpolate between the current and next wind direction + let windOffset = this._currentWindDirection + (this._nextWindDirection - this._currentWindDirection) * (1 - this._windChangeFrames / 60); + + // Update the particle's position + this._x += velocityX //+ windOffset; + this._y += velocityY //+ windOffset; + + // Update the particle's size based on the bass energy + this._size = this._initialSize * (1 + bassEnergy); + + // Decrease the frame counter + this._windChangeFrames--; + } + + draw(ctx) { + ctx.beginPath(); + ctx.arc(this._x, this._y, this._size, 0, Math.PI * 2, false); + ctx.fillStyle = 'white'; // Change this to the color you want + ctx.fill(); + } + + get isDead() { + return this._x + this._size < this._boundaries.left || + this._x - this._size > this._boundaries.right || + this._y + this._size < this._boundaries.top || + this._y - this._size > this._boundaries.bottom; + } +} + // helper function - output deprecation warning message on console const deprecate = ( name, alternative ) => console.warn( `${name} is deprecated. Use ${alternative} instead.` ); @@ -152,6 +208,9 @@ export default class AudioMotionAnalyzer { this._selectedGrads = []; // names of the currently selected gradients for channels 0 and 1 this._sources = []; // input nodes + this._particles = options.particles || false; + this._particleArray = []; + // Register built-in gradients for ( const [ name, options ] of GRADIENTS ) this.registerGradient( name, options ); @@ -571,6 +630,14 @@ export default class AudioMotionAnalyzer { this._makeGrad(); } + get particles() { + return this._particles; + } + + set particles( value ) { + this._particles = !! value; + } + get reflexRatio() { return this._reflexRatio; } @@ -1621,7 +1688,6 @@ export default class AudioMotionAnalyzer { } // initialize local constants - const { isAlpha, isBands, isLeds, @@ -1657,6 +1723,7 @@ export default class AudioMotionAnalyzer { _mirror, _mode, overlay, + _particles, _radial, showBgColor, showPeaks, @@ -1800,6 +1867,42 @@ export default class AudioMotionAnalyzer { } } + const drawParticles = () => { + const bassEnergy = this.getEnergy('bass'); + // todo make configurable? + if (this._frames % 5 === 0) { + // Generate a random direction for the particle + const angle = Math.random() * Math.PI * 2; // Random angle in [0, 2π] + const speed = Math.random() * 2; // Random speed + + // A particle should spawn inside an invisible circle around the exact center of the canvas + const radius = 50; + const posX = this.canvas.width / 2 + Math.cos(angle) * radius; + const posY = this.canvas.height / 2 + Math.sin(angle) * radius; + + // Create a new Particle instance and add it to the array + const particle = new Particle( + posX, + posY, + speed, + angle, + Math.random(), + {top: 0, right: this.canvas.width, bottom: this.canvas.height, left: 0}, // Boundaries for this particle to live in + ); + this._particleArray.push(particle); + } + + // Update each particle + for (let particle of this._particleArray) { + particle.update(bassEnergy); + particle.draw(this._ctx); + + // Remove the particle from the array if it's dead + if (particle.isDead) { + this._particleArray.splice(this._particleArray.indexOf(particle), 1); + } + } + } /* MAIN FUNCTION */ if ( overlay ) @@ -2250,6 +2353,10 @@ export default class AudioMotionAnalyzer { updateEnergy( currentEnergy / ( nBars << ( nChannels - 1 ) ) ); + if (this.particles && this.radial) { + drawParticles(); + } + if ( useCanvas ) { // Mirror effect if ( _mirror && ! _radial && ! isDualHorizontal ) { @@ -2527,6 +2634,7 @@ export default class AudioMotionAnalyzer { outlineBars : false, overlay : false, peakLine : false, + particles : false, radial : false, reflexAlpha : 0.15, reflexBright : 1, diff --git a/src/index.d.ts b/src/index.d.ts index d14fac4..6a1bdaf 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -48,6 +48,7 @@ export interface Options { outlineBars?: boolean; overlay?: boolean; peakLine?: boolean; + particles?: boolean; radial?: boolean; reflexAlpha?: number; reflexBright?: number; From 3f172da8efe05cf5ec6e14e43f8738d51e027aad Mon Sep 17 00:00:00 2001 From: Staijn1 Date: Tue, 23 Jan 2024 20:29:30 +0100 Subject: [PATCH 05/10] Spawn a particle every frame --- src/audioMotion-analyzer.js | 40 +++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/src/audioMotion-analyzer.js b/src/audioMotion-analyzer.js index d664e82..6d59a7f 100644 --- a/src/audioMotion-analyzer.js +++ b/src/audioMotion-analyzer.js @@ -1869,28 +1869,24 @@ export default class AudioMotionAnalyzer { const drawParticles = () => { const bassEnergy = this.getEnergy('bass'); - // todo make configurable? - if (this._frames % 5 === 0) { - // Generate a random direction for the particle - const angle = Math.random() * Math.PI * 2; // Random angle in [0, 2π] - const speed = Math.random() * 2; // Random speed - - // A particle should spawn inside an invisible circle around the exact center of the canvas - const radius = 50; - const posX = this.canvas.width / 2 + Math.cos(angle) * radius; - const posY = this.canvas.height / 2 + Math.sin(angle) * radius; - - // Create a new Particle instance and add it to the array - const particle = new Particle( - posX, - posY, - speed, - angle, - Math.random(), - {top: 0, right: this.canvas.width, bottom: this.canvas.height, left: 0}, // Boundaries for this particle to live in - ); - this._particleArray.push(particle); - } + + const angle = Math.random() * Math.PI * 2; // Random angle in [0, 2π] + const speed = Math.random() * 2; // Random speed between 0 and 2 + + // A particle should spawn inside an invisible circle around the exact center of the canvas + const radius = 50; + const posX = this.canvas.width / 2 + Math.cos(angle) * radius; + const posY = this.canvas.height / 2 + Math.sin(angle) * radius; + + const particle = new Particle( + posX, + posY, + speed, + angle, + Math.random(), + {top: 0, right: this.canvas.width, bottom: this.canvas.height, left: 0}, // Boundaries for this particle to live in + ); + this._particleArray.push(particle); // Update each particle for (let particle of this._particleArray) { From 4e5554f6dba76d1e83746afb30682fc2d9825505 Mon Sep 17 00:00:00 2001 From: Staijn1 Date: Tue, 23 Jan 2024 20:35:46 +0100 Subject: [PATCH 06/10] Rename particles to showParticles --- src/audioMotion-analyzer.js | 14 +++++++------- src/index.d.ts | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/audioMotion-analyzer.js b/src/audioMotion-analyzer.js index 6d59a7f..c417b4d 100644 --- a/src/audioMotion-analyzer.js +++ b/src/audioMotion-analyzer.js @@ -208,7 +208,7 @@ export default class AudioMotionAnalyzer { this._selectedGrads = []; // names of the currently selected gradients for channels 0 and 1 this._sources = []; // input nodes - this._particles = options.particles || false; + this._showParticles = options.showParticles || false; this._particleArray = []; // Register built-in gradients @@ -630,12 +630,12 @@ export default class AudioMotionAnalyzer { this._makeGrad(); } - get particles() { - return this._particles; + get showParticles() { + return this._showParticles; } - set particles( value ) { - this._particles = !! value; + set showParticles( value ) { + this._showParticles = !! value; } get reflexRatio() { @@ -1723,7 +1723,7 @@ export default class AudioMotionAnalyzer { _mirror, _mode, overlay, - _particles, + _showParticles, _radial, showBgColor, showPeaks, @@ -2349,7 +2349,7 @@ export default class AudioMotionAnalyzer { updateEnergy( currentEnergy / ( nBars << ( nChannels - 1 ) ) ); - if (this.particles && this.radial) { + if (this.showParticles && this.radial) { drawParticles(); } diff --git a/src/index.d.ts b/src/index.d.ts index 6a1bdaf..0e14f31 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -48,7 +48,6 @@ export interface Options { outlineBars?: boolean; overlay?: boolean; peakLine?: boolean; - particles?: boolean; radial?: boolean; reflexAlpha?: number; reflexBright?: number; @@ -58,6 +57,7 @@ export interface Options { showBgColor?: boolean; showFPS?: boolean; showPeaks?: boolean; + showParticles?: boolean; showScaleX?: boolean; showScaleY?: boolean; smoothing?: number; From 7b336142066d7b15a4c901f627251868af6a0c43 Mon Sep 17 00:00:00 2001 From: Staijn1 Date: Tue, 23 Jan 2024 20:35:57 +0100 Subject: [PATCH 07/10] Update fluid.html to add showParticles --- demo/fluid.html | 1 + 1 file changed, 1 insertion(+) diff --git a/demo/fluid.html b/demo/fluid.html index 25e52d2..774ddbf 100644 --- a/demo/fluid.html +++ b/demo/fluid.html @@ -317,6 +317,7 @@

| fl
+
From aa9e37ecfc38f09be781e4621d820a3ca29046c7 Mon Sep 17 00:00:00 2001 From: Staijn1 Date: Tue, 23 Jan 2024 20:41:20 +0100 Subject: [PATCH 08/10] Remove old wind stuff --- src/audioMotion-analyzer.js | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/src/audioMotion-analyzer.js b/src/audioMotion-analyzer.js index c417b4d..8d84057 100644 --- a/src/audioMotion-analyzer.js +++ b/src/audioMotion-analyzer.js @@ -110,45 +110,33 @@ class Particle { this._size = size; this._initialSize = size; this._boundaries = boundaries; - this._currentWindDirection = Math.random() * 2 - 1; // Initial wind direction - this._nextWindDirection = Math.random() * 2 - 1; // Next wind direction - this._windChangeFrames = 0; // Frame counter for wind direction change } - update(bassEnergy) { - let velocity = this._initialVelocity + bassEnergy * 1.5; + update(energy) { + let velocity = this._initialVelocity + energy * 1.5; let velocityX = Math.cos(this._angle) * velocity; let velocityY = Math.sin(this._angle) * velocity; - // Check if it's time to change the wind direction - if (this._windChangeFrames <= 0) { - // Update the wind direction and reset the frame counter - this._currentWindDirection = this._nextWindDirection; - this._nextWindDirection = Math.random() * 2 - 1; - this._windChangeFrames = Math.floor(Math.random() * 60) + 60; // Change wind direction every 60 to 120 frames - } - - // Interpolate between the current and next wind direction - let windOffset = this._currentWindDirection + (this._nextWindDirection - this._currentWindDirection) * (1 - this._windChangeFrames / 60); // Update the particle's position - this._x += velocityX //+ windOffset; - this._y += velocityY //+ windOffset; - - // Update the particle's size based on the bass energy - this._size = this._initialSize * (1 + bassEnergy); + this._x += velocityX; + this._y += velocityY; - // Decrease the frame counter - this._windChangeFrames--; + // Update the particle's size based on the energy + this._size = this._initialSize * (1 + energy); } draw(ctx) { ctx.beginPath(); ctx.arc(this._x, this._y, this._size, 0, Math.PI * 2, false); - ctx.fillStyle = 'white'; // Change this to the color you want + ctx.fillStyle = 'white'; ctx.fill(); } + /** + * A particle is considered dead if it, including its size, is fully outside the set boundaries + * @return {boolean} + */ get isDead() { return this._x + this._size < this._boundaries.left || this._x - this._size > this._boundaries.right || From 2725f5b3bb0f3ab60805a83c3c056fa3507ae853 Mon Sep 17 00:00:00 2001 From: Staijn1 Date: Tue, 23 Jan 2024 20:49:37 +0100 Subject: [PATCH 09/10] Rename occurrence of particles to showParticles --- src/audioMotion-analyzer.js | 5 +++-- src/index.d.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/audioMotion-analyzer.js b/src/audioMotion-analyzer.js index 8d84057..bc5be55 100644 --- a/src/audioMotion-analyzer.js +++ b/src/audioMotion-analyzer.js @@ -1861,7 +1861,8 @@ export default class AudioMotionAnalyzer { const angle = Math.random() * Math.PI * 2; // Random angle in [0, 2π] const speed = Math.random() * 2; // Random speed between 0 and 2 - // A particle should spawn inside an invisible circle around the exact center of the canvas + // A particle should spawn inside an invisible circle around the exact center of the canvas. + // It looks very unnatural when all particles spawn from the exact center of the canvas. const radius = 50; const posX = this.canvas.width / 2 + Math.cos(angle) * radius; const posY = this.canvas.height / 2 + Math.sin(angle) * radius; @@ -2618,7 +2619,6 @@ export default class AudioMotionAnalyzer { outlineBars : false, overlay : false, peakLine : false, - particles : false, radial : false, reflexAlpha : 0.15, reflexBright : 1, @@ -2627,6 +2627,7 @@ export default class AudioMotionAnalyzer { roundBars : false, showBgColor : true, showFPS : false, + showParticles : false, showPeaks : true, showScaleX : true, showScaleY : false, diff --git a/src/index.d.ts b/src/index.d.ts index 0e14f31..07ac29d 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -56,8 +56,8 @@ export interface Options { roundBars?: boolean; showBgColor?: boolean; showFPS?: boolean; - showPeaks?: boolean; showParticles?: boolean; + showPeaks?: boolean; showScaleX?: boolean; showScaleY?: boolean; smoothing?: number; From 95848ec76dae9b22327d4b848b994d3edd547042 Mon Sep 17 00:00:00 2001 From: Staijn1 Date: Tue, 23 Jan 2024 20:50:12 +0100 Subject: [PATCH 10/10] Make sure alphabetical order is correct in html --- demo/fluid.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/fluid.html b/demo/fluid.html index 774ddbf..0b89efd 100644 --- a/demo/fluid.html +++ b/demo/fluid.html @@ -316,8 +316,8 @@

| fl
- +