Skip to content

Commit ed1b2ed

Browse files
authored
Merge pull request #196 from scribear/mfcc_vvisualizer
mfcc visualizer UI
2 parents 1fc3be3 + 7e2174a commit ed1b2ed

2 files changed

Lines changed: 71 additions & 74 deletions

File tree

src/components/api/visualization/audioVis.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,14 @@ export const AudioVis: React.FC = (props) => {
3838
)
3939
} else if (controlStatus.showMFCC === true) {
4040
return (
41+
// <Draggable id="fullVisual">
42+
// <Resizable size="290px">
43+
// <MFCCVisual></MFCCVisual>
44+
// </Resizable>
45+
// </Draggable>
4146
<Draggable id="fullVisual">
4247
<Resizable size="290px">
43-
<MFCCVisual></MFCCVisual>
48+
<MFCCVisual visualWidth={290} visualHeight={290} />
4449
</Resizable>
4550
</Draggable>
4651
)
@@ -62,4 +67,5 @@ export const AudioVis: React.FC = (props) => {
6267
) : null}
6368
</div>
6469
);
65-
}
70+
}
71+
Lines changed: 63 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,14 @@
1-
import React, { useEffect, useRef } from 'react'
1+
import React, { useEffect, useRef } from 'react';
22
import { useSelector } from 'react-redux';
33
import { DisplayStatus } from '../../../react-redux&middleware/redux/typesImports';
44
import { RootState } from '../../../store';
55
import Meyda from 'meyda';
66
import { MeydaAnalyzer } from 'meyda/dist/esm/meyda-wa';
77

8-
9-
var audioContext: AudioContext;
10-
var analyser: MeydaAnalyzer;
11-
var source: MediaStreamAudioSourceNode;
12-
var rafId: number;
13-
var canvas: HTMLCanvasElement;
14-
var canvasCtx: CanvasRenderingContext2D;
15-
168
const LOUDNESS_THRESHOLD = 15;
179
const HISTORY_LENGTH = 80;
1810
const MFCC_COEFFICIENTS = 25;
1911
const FFT_SIZE = 512;
20-
var history_write_index = 0;
2112

2213
class Moment {
2314
mfcc: Float32Array;
@@ -29,93 +20,93 @@ class Moment {
2920
}
3021
}
3122

32-
var history: Moment[] = new Array(HISTORY_LENGTH);
33-
for (var i = 0; i < HISTORY_LENGTH; i++) {
34-
history[i] = new Moment();
35-
}
23+
let audioContext: AudioContext;
24+
let analyser: MeydaAnalyzer;
25+
let source: MediaStreamAudioSourceNode;
26+
let rafId: number;
3627

37-
var theme: DisplayStatus;
28+
const history: Moment[] = Array.from({ length: HISTORY_LENGTH }, () => new Moment());
29+
let history_write_index = 0;
3830

3931
export function MFCCVisual(props: any) {
4032
const canvasRef = useRef<HTMLCanvasElement | null>(null);
41-
42-
theme = useSelector((state: RootState) => {
43-
return state.DisplayReducer as DisplayStatus;
44-
});
33+
const theme = useSelector((state: RootState) => state.DisplayReducer as DisplayStatus);
4534

4635
useEffect(() => {
4736
audioContext = new (window.AudioContext || window.webkitAudioContext)();
4837

49-
navigator.mediaDevices.getUserMedia({
50-
audio: true,
51-
video: false
52-
}).then(newMediaStream => {
53-
source = audioContext.createMediaStreamSource(newMediaStream);
54-
55-
analyser = Meyda.createMeydaAnalyzer({
56-
audioContext: audioContext,
57-
source: source,
58-
bufferSize: FFT_SIZE,
59-
numberOfMFCCCoefficients: MFCC_COEFFICIENTS,
60-
featureExtractors: [
61-
'loudness',
62-
'spectralCentroid',
63-
'mfcc',
64-
],
65-
callback: (features: {
66-
loudness: { specific: Float32Array, total: number },
67-
spectralCentroid: number,
68-
mfcc: Float32Array,
69-
}) => {
70-
if (features.loudness.total >= LOUDNESS_THRESHOLD) {
71-
history[history_write_index].mfcc.set(features.mfcc);
72-
history[history_write_index].centroid = features.spectralCentroid;
73-
history_write_index = (history_write_index + 1) % HISTORY_LENGTH;
74-
}
75-
}
38+
navigator.mediaDevices
39+
.getUserMedia({ audio: true, video: false })
40+
.then((mediaStream) => {
41+
source = audioContext.createMediaStreamSource(mediaStream);
42+
43+
analyser = Meyda.createMeydaAnalyzer({
44+
audioContext,
45+
source,
46+
bufferSize: FFT_SIZE,
47+
numberOfMFCCCoefficients: MFCC_COEFFICIENTS,
48+
featureExtractors: ['loudness', 'spectralCentroid', 'mfcc'],
49+
callback: (features) => {
50+
if (features.loudness.total >= LOUDNESS_THRESHOLD) {
51+
history[history_write_index].mfcc.set(features.mfcc);
52+
history[history_write_index].centroid = features.spectralCentroid;
53+
history_write_index = (history_write_index + 1) % HISTORY_LENGTH;
54+
}
55+
},
56+
});
57+
58+
analyser.start();
7659
});
7760

78-
analyser.start();
79-
});
80-
8161
rafId = requestAnimationFrame(draw);
8262

83-
// setup canvas
84-
canvas = canvasRef.current!;
85-
canvasCtx = canvas.getContext('2d')!;
86-
8763
return () => {
8864
cancelAnimationFrame(rafId);
8965
analyser.stop();
9066
source.disconnect();
91-
}
67+
};
9268
}, []);
9369

94-
const draw = () => { // the draw function
70+
const draw = () => {
9571
requestAnimationFrame(draw);
96-
97-
canvasCtx.clearRect(0, 0, canvas.width, canvas.height);
98-
99-
const sliceWidth = canvas.width / MFCC_COEFFICIENTS;
100-
const sliceHeight = canvas.height / HISTORY_LENGTH;
101-
history.forEach((moment, index) => {
102-
const row = (HISTORY_LENGTH - history_write_index + index) % HISTORY_LENGTH;
103-
console.log(moment.centroid);
104-
const centroid = Math.round(720 * moment.centroid / FFT_SIZE);
105-
moment.mfcc.forEach((data, col) => {
72+
73+
const canvas = canvasRef.current!;
74+
const canvasCtx = canvas.getContext('2d')!;
75+
76+
// Set dull white background
77+
canvasCtx.fillStyle = '#D3D3D3';
78+
canvasCtx.fillRect(0, 0, canvas.width, canvas.height);
79+
80+
const sliceWidth = canvas.width / HISTORY_LENGTH;
81+
const sliceHeight = canvas.height / MFCC_COEFFICIENTS;
82+
83+
for (let i = 0; i < HISTORY_LENGTH; i++) {
84+
const historyIndex = (history_write_index - i + HISTORY_LENGTH) % HISTORY_LENGTH; // Map newest to right
85+
const x = canvas.width - sliceWidth * (i + 1); // Calculate position from right to left
86+
const moment = history[historyIndex];
87+
88+
moment.mfcc.forEach((data, row) => {
89+
//const intensity = Math.min(data / 20, 1); // Normalize MFCC value
90+
//const brightness = intensity * 255; // Map intensity to brightness
91+
92+
// Grayscale color mapping
93+
///canvasCtx.fillStyle = `rgb(${brightness}, ${brightness}, ${brightness})`;
10694
const intensity = data / 255;
107-
canvasCtx.fillStyle = `hsl(${centroid}deg 100% 50% / ${intensity})`;
95+
const centroid = Math.round((720 * moment.centroid) / FFT_SIZE);
96+
canvasCtx.fillStyle = `hsl(${centroid}deg 100% 60% / ${Math.min(intensity * 10, 1)})`;
97+
98+
// Draw rectangle for each coefficient
10899
canvasCtx.fillRect(
109-
col * sliceWidth,
100+
x,
110101
row * sliceHeight,
111102
sliceWidth,
112103
sliceHeight
113104
);
114105
});
115-
});
116-
}
106+
}
107+
};
108+
109+
117110

118-
return <canvas width={props.visualWidth}
119-
height={props.visualHeight}
120-
ref={canvasRef} />
111+
return <canvas width={props.visualWidth} height={props.visualHeight} ref={canvasRef} />;
121112
}

0 commit comments

Comments
 (0)