mirror of
https://github.com/janishutz/MusicPlayerV2.git
synced 2025-11-25 21:14:22 +00:00
162 lines
4.8 KiB
TypeScript
162 lines
4.8 KiB
TypeScript
import ColorThief from 'colorthief';
|
|
|
|
const colorThief = new ColorThief();
|
|
|
|
const getImageData = (): Promise<number[][]> => {
|
|
return new Promise( resolve => {
|
|
const img = document.getElementById( 'current-image' ) as HTMLImageElement;
|
|
|
|
if ( img.complete ) {
|
|
resolve( colorThief.getPalette( img ) );
|
|
} else {
|
|
img.addEventListener( 'load', () => {
|
|
resolve( colorThief.getPalette( img ) );
|
|
} );
|
|
}
|
|
} );
|
|
};
|
|
|
|
const createBackground = () => {
|
|
return new Promise( resolve => {
|
|
getImageData().then( palette => {
|
|
const colourDetails: number[][] = [];
|
|
const colours: string[] = [];
|
|
|
|
let differentEnough = true;
|
|
|
|
if ( palette[ 0 ] ) {
|
|
for ( const i in palette ) {
|
|
for ( const colour in colourDetails ) {
|
|
const colourDiff = ( Math.abs( colourDetails[ colour ][ 0 ] - palette[ i ][ 0 ] ) / 255
|
|
+ Math.abs( colourDetails[ colour ][ 1 ] - palette[ i ][ 1 ] ) / 255
|
|
+ Math.abs( colourDetails[ colour ][ 2 ] - palette[ i ][ 2 ] ) / 255 ) / 3 * 100;
|
|
|
|
if ( colourDiff > 15 ) {
|
|
differentEnough = true;
|
|
}
|
|
}
|
|
|
|
if ( differentEnough ) {
|
|
colourDetails.push( palette[ i ] );
|
|
colours.push( 'rgb(' + palette[ i ][ 0 ] + ',' + palette[ i ][ 1 ] + ',' + palette[ i ][ 2 ] + ')' );
|
|
}
|
|
|
|
differentEnough = false;
|
|
}
|
|
}
|
|
|
|
let outColours = 'conic-gradient(';
|
|
|
|
if ( colours.length < 3 ) {
|
|
for ( let i = 0; i < 3; i++ ) {
|
|
if ( colours[ i ] ) {
|
|
outColours += colours[ i ] + ',';
|
|
} else {
|
|
if ( i === 0 ) {
|
|
outColours += 'blue,';
|
|
} else if ( i === 1 ) {
|
|
outColours += 'green,';
|
|
} else if ( i === 2 ) {
|
|
outColours += 'red,';
|
|
}
|
|
}
|
|
}
|
|
} else if ( colours.length < 11 ) {
|
|
for ( const i in colours ) {
|
|
outColours += colours[ i ] + ',';
|
|
}
|
|
} else {
|
|
for ( let i = 0; i < 10; i++ ) {
|
|
outColours += colours[ i ] + ',';
|
|
}
|
|
}
|
|
|
|
outColours += colours[ 0 ] ?? 'blue' + ')';
|
|
resolve( outColours );
|
|
} );
|
|
} );
|
|
};
|
|
|
|
let callbackFun = () => {};
|
|
|
|
const subscribeToBeatUpdate = ( cb: () => void ) => {
|
|
callbackFun = cb;
|
|
micAudioHandler();
|
|
};
|
|
|
|
const unsubscribeFromBeatUpdate = () => {
|
|
callbackFun = () => {};
|
|
|
|
try {
|
|
clearInterval( micAnalyzer );
|
|
} catch ( e ) { /* empty */ }
|
|
};
|
|
|
|
const coolDown = () => {
|
|
beatDetected = false;
|
|
};
|
|
|
|
let micAnalyzer = 0;
|
|
let beatDetected = false;
|
|
|
|
const micAudioHandler = () => {
|
|
const audioContext = new ( window.AudioContext || window.webkitAudioContext )();
|
|
const analyser = audioContext.createAnalyser();
|
|
|
|
analyser.fftSize = 256;
|
|
const bufferLength = analyser.frequencyBinCount;
|
|
const dataArray = new Uint8Array( bufferLength );
|
|
|
|
beatDetected = false;
|
|
|
|
navigator.mediaDevices.getUserMedia( {
|
|
'audio': true
|
|
} ).then( stream => {
|
|
const mic = audioContext.createMediaStreamSource( stream );
|
|
|
|
mic.connect( analyser );
|
|
analyser.getByteFrequencyData( dataArray );
|
|
let prevSpectrum: number[] = [];
|
|
|
|
const threshold = 10; // Adjust as needed
|
|
|
|
micAnalyzer = setInterval( () => {
|
|
analyser.getByteFrequencyData( dataArray );
|
|
// Convert the frequency data to a numeric array
|
|
const currentSpectrum = Array.from( dataArray );
|
|
|
|
if ( prevSpectrum ) {
|
|
// Calculate the spectral flux
|
|
const flux = calculateSpectralFlux( prevSpectrum, currentSpectrum );
|
|
|
|
if ( flux > threshold && !beatDetected ) {
|
|
// Beat detected
|
|
beatDetected = true;
|
|
callbackFun();
|
|
}
|
|
}
|
|
|
|
prevSpectrum = currentSpectrum;
|
|
}, 60 / 180 * 250 );
|
|
} );
|
|
};
|
|
|
|
const calculateSpectralFlux = ( prevSpectrum: number[], currentSpectrum: number[] ) => {
|
|
let flux = 0;
|
|
|
|
for ( let i = 0; i < prevSpectrum.length; i++ ) {
|
|
const diff = currentSpectrum[ i ] - prevSpectrum[ i ];
|
|
|
|
flux += Math.max( 0, diff );
|
|
}
|
|
|
|
return flux;
|
|
};
|
|
|
|
export default {
|
|
createBackground,
|
|
subscribeToBeatUpdate,
|
|
unsubscribeFromBeatUpdate,
|
|
coolDown,
|
|
};
|