Skip to content

Commit

Permalink
Add AudioAnalyser
Browse files Browse the repository at this point in the history
  • Loading branch information
Emroni committed Aug 9, 2024
1 parent 4a0468f commit 243a8a7
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 104 deletions.
66 changes: 14 additions & 52 deletions src/app/drops/1/page.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
'use client';
import { AudioAnalyser } from '@/components';
import { PI_M2 } from '@/setup';
import { Box, Typography } from '@mui/material';
import React, { createRef } from 'react';
import './styles.css';

export default class Drops1 extends React.Component {

average = 0;
center = { x: 0, y: 0 };
containerRef = createRef<HTMLDivElement>();
frameRequest = 0;
radius = 0;
rotation = 0;

analyser: AnalyserNode;
frequencies: Float32Array;
frequenciesData: Uint8Array;

componentDidMount() {
window.addEventListener('resize', this.resize);
this.resize();
Expand All @@ -38,59 +34,27 @@ export default class Drops1 extends React.Component {
}
}

handlePlay = (e: any) => {
if (!this.analyser ) {
// Get analyser
const audioContext = new AudioContext();
const source = audioContext.createMediaElementSource(e.target);
this.analyser = audioContext.createAnalyser();
source.connect(this.analyser);
this.analyser.connect(audioContext.destination);
this.analyser.fftSize = 32;
this.analyser.minDecibels = -90;
this.analyser.maxDecibels = 0;

// Get frequency data
this.frequenciesData = new Uint8Array(this.analyser.frequencyBinCount);
this.frequencies = new Float32Array(this.frequenciesData.length);

// Initialize tick
this.tick();
}
}

tick = () => {
this.frameRequest = requestAnimationFrame(this.tick);

this.analyser.getByteFrequencyData(this.frequenciesData);
this.average = 0;
for (let i = 0; i < this.frequenciesData.length; i++) {
const n = this.frequenciesData[i] / 256;
this.frequencies[i] = n;
this.average += n;
}
this.average /= this.frequenciesData.length;

let a = 0;
for (let i = Math.floor(this.frequencies.length * 0.5); i < this.frequencies.length; i++) {
a += this.frequencies[i];
handleTick = (frequencies: Float32Array, average: number) => {
let offset = 0;
for (let i = Math.floor(frequencies.length * 0.5); i < frequencies.length; i++) {
offset += frequencies[i];
}
a /= 2;
offset /= 2;

for (let i = 0; i < this.frequencies.length; i++) {
const f = this.frequencies[i];
if (f > 0.025) {
this.add(i / this.frequencies.length, f, a);
for (let i = 0; i < frequencies.length; i++) {
const frequency = frequencies[i];
if (frequency > 0.025) {
this.add(i / frequencies.length, frequency, offset, average);
}
}

if (this.containerRef.current) {
this.rotation += this.average * this.average * this.average * 10;
this.rotation += average * average * average * 10;
this.containerRef.current.style.transform = `rotate(${this.rotation}deg)`;
}
}

add = (distance: number, force: number, offset: number) => {
add = (distance: number, force: number, offset: number, average: number) => {
if (this.containerRef.current) {
const drop = document.createElement('span');
drop.className = 'drop';
Expand All @@ -101,7 +65,7 @@ export default class Drops1 extends React.Component {
drop.style.left = this.center.x + Math.sin(n) * r + 'px';
drop.style.top = this.center.y + Math.cos(n) * r + 'px';
drop.style.border = `${force * 10}px solid hsl(${(force + offset) * 256}, 100%, 50%)`;
drop.style.width = drop.style.height = (this.average * force * 100) + 'px';
drop.style.width = drop.style.height = (average * force * 100) + 'px';
drop.style.animationDuration = force * 3 + 's';

setTimeout(() => {
Expand All @@ -114,9 +78,7 @@ export default class Drops1 extends React.Component {
return <>
<Box flex={1} position="relative" ref={this.containerRef} />
<Box alignItems="flex-end" display="flex" justifyContent="space-between" padding={3}>
<audio controls controlsList="nodownload noplaybackrate" onPlay={this.handlePlay}>
<source src="/assets/sounds/max_cooper-order_from_chaos.mp3" type="audio/mp3" />
</audio>
<AudioAnalyser onTick={this.handleTick} />
<Typography>
Music by <a href="https://maxcooper.net/order-from-chaos" target="_blank">Max Cooper</a>
</Typography>
Expand Down
61 changes: 9 additions & 52 deletions src/app/drops/2/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'use client';
import { ExperimentControls, PixiPlayer } from '@/components';
import { AudioAnalyser, ExperimentControls, PixiPlayer } from '@/components';
import { hslToHex } from '@/helpers';
import { PI_M2 } from '@/setup';
import { Box, Typography } from '@mui/material';
Expand All @@ -9,17 +9,12 @@ import React from 'react';
export default class Drops2 extends React.Component<any, ExperimentControlItems> {

container = new PIXI.Container();
fftSize = 32;
frameRequest = 0;
layers: PIXI.Container[] = [];
particleHole = 50;
particles: PIXI.Sprite[] = [];
particleSize = 100;
size = 640;

analyser: AnalyserNode;
frequencies: Float32Array;
frequenciesData: Uint8Array;
particleTexture: PIXI.Texture;

state = {
Expand All @@ -35,7 +30,7 @@ export default class Drops2 extends React.Component<any, ExperimentControlItems>
app.stage.addChild(this.container);

// Create layers
for (let i = 0; i < this.fftSize; i++) {
for (let i = 0; i < 32; i++) {
const layer = new PIXI.Container();
this.layers.push(layer);
this.container.addChild(layer);
Expand Down Expand Up @@ -86,49 +81,13 @@ export default class Drops2 extends React.Component<any, ExperimentControlItems>
}
}

componentWillUnmount() {
cancelAnimationFrame(this.frameRequest);
}

handlePlay = (e: any) => {
if (!this.analyser) {
// Get analyser
const audioContext = new AudioContext();
const source = audioContext.createMediaElementSource(e.target);
this.analyser = audioContext.createAnalyser();
source.connect(this.analyser);
this.analyser.connect(audioContext.destination);
this.analyser.fftSize = this.fftSize;
this.analyser.minDecibels = -90;
this.analyser.maxDecibels = 0;

// Get frequency data
this.frequenciesData = new Uint8Array(this.analyser.frequencyBinCount);
this.frequencies = new Float32Array(this.frequenciesData.length);

// Initialize tick
this.tick();
}
}

tick = () => {
handleTick = (frequencies: Float32Array, average: number) => {
const { force, shrink, size, speed } = this.state;
this.frameRequest = requestAnimationFrame(this.tick);

// Analyze frequencies
this.analyser.getByteFrequencyData(this.frequenciesData);
let average = 0;
for (let i = 0; i < this.frequenciesData.length; i++) {
const n = this.frequenciesData[i] / 256;
this.frequencies[i] = n;
average += n;
}
average /= this.frequenciesData.length;

// Get offset
let offset = 0;
for (let i = Math.floor(this.frequencies.length * 0.5); i < this.frequencies.length; i++) {
offset += this.frequencies[i];
for (let i = Math.floor(frequencies.length * 0.5); i < frequencies.length; i++) {
offset += frequencies[i];
}
offset /= 2;

Expand All @@ -145,10 +104,10 @@ export default class Drops2 extends React.Component<any, ExperimentControlItems>
}

// Ping particles
for (let i = 0; i < this.frequencies.length; i++) {
const frequency = this.frequencies[i];
for (let i = 0; i < frequencies.length; i++) {
const frequency = frequencies[i];
if (frequency > 0.025) {
const layerIndex = Math.floor(i / this.frequencies.length * this.layers.length);
const layerIndex = Math.floor(i / frequencies.length * this.layers.length);
const layer = this.layers[layerIndex];

const particleIndex = Math.floor(Math.random() * layer.children.length);
Expand All @@ -170,9 +129,7 @@ export default class Drops2 extends React.Component<any, ExperimentControlItems>
onInit={this.handleInit}
/>
<Box alignItems="flex-end" display="flex" justifyContent="space-between" padding={3}>
<audio controls controlsList="nodownload noplaybackrate" onPlay={this.handlePlay}>
<source src="/assets/sounds/max_cooper-order_from_chaos.mp3" type="audio/mp3" />
</audio>
<AudioAnalyser onTick={this.handleTick} />
<Typography>
Music by <a href="https://maxcooper.net/order-from-chaos" target="_blank">Max Cooper</a>
</Typography>
Expand Down
82 changes: 82 additions & 0 deletions src/components/AudioAnalyser/AudioAnalyser.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
'use client';
import { Typography } from '@mui/material';
import { Component } from 'react';

export default class AudioAnalyser extends Component<AudioAnalyserProps, AudioAnalyserState> {

animationFrame = 0;

analyser: AnalyserNode;
frequencies: Float32Array;
frequenciesData: Uint8Array;

state = {
initialized: false,
};

componentWillUnmount() {
cancelAnimationFrame(this.animationFrame);
}

handlePlay = (e: any) => {
if (!this.analyser) {
// Get analyser
const audioContext = new AudioContext();
const source = audioContext.createMediaElementSource(e.target);
this.analyser = audioContext.createAnalyser();
source.connect(this.analyser);
this.analyser.connect(audioContext.destination);
this.analyser.fftSize = 32;
this.analyser.minDecibels = -90;
this.analyser.maxDecibels = 0;

// Get frequency data
this.frequenciesData = new Uint8Array(this.analyser.frequencyBinCount);
this.frequencies = new Float32Array(this.frequenciesData.length);

// Initialize
this.setState({
initialized: true,
});
this.tick();
}
}

tick = () => {
this.animationFrame = requestAnimationFrame(this.tick);

// Analyze frequencies
this.analyser.getByteFrequencyData(this.frequenciesData);
let average = 0;
for (let i = 0; i < this.frequenciesData.length; i++) {
const n = this.frequenciesData[i] / 256;
this.frequencies[i] = n;
average += n;
}
average /= this.frequenciesData.length;

// Call tick
this.props.onTick(this.frequencies, average);
}

render() {
return <>
<audio controls controlsList="nodownload noplaybackrate" onPlay={this.handlePlay}>
<source src="/assets/sounds/max_cooper-order_from_chaos.mp3" type="audio/mp3" />
</audio>
{!this.state.initialized && (
<Typography
align="center"
left={0}
position="absolute"
textTransform="uppercase"
top="calc(50% - 50px)"
width="100%"
>
Press Play
</Typography>
)}
</>;
}

}
7 changes: 7 additions & 0 deletions src/components/AudioAnalyser/AudioAnalyser.types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
interface AudioAnalyserProps {
onTick(frequencies: Float32Array, average: number): void;
}

interface AudioAnalyserState {
initialized: boolean;
}
1 change: 1 addition & 0 deletions src/components/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { default as Analytics } from './Analytics/Analytics';
export { default as AudioAnalyser } from './AudioAnalyser/AudioAnalyser';
export { default as ExperimentControls } from './ExperimentControls/ExperimentControls';
export { default as ExperimentSource } from './ExperimentSource/ExperimentSource';
export { default as PixiPlayer } from './PixiPlayer/PixiPlayer';
Expand Down

0 comments on commit 243a8a7

Please sign in to comment.