Skip to content

Commit 64cbce1

Browse files
committed
Separate helpers
1 parent 03d5edf commit 64cbce1

File tree

1 file changed

+61
-43
lines changed

1 file changed

+61
-43
lines changed

src/Audiovis.tsx

Lines changed: 61 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -71,65 +71,83 @@ const Waveform: FC<{ audio: AudioBuffer }> = ({ audio }) => {
7171
const Sonogram: FC<{ audio: AudioBuffer }> = ({ audio }) => {
7272
const canvas = useRef<HTMLCanvasElement>(null);
7373

74-
const fttData = useMemo(() => {
75-
const fftSize = 512; // Ensure this is a power of two
76-
const fft = new FFT(fftSize);
77-
const data = audio.getChannelData(0);
74+
const imageData = useMemo(() => {
75+
const fftSize = 512;
76+
const data = spectrum(audio, fftSize);
77+
const image = spectrumToImage(data, fftSize);
7878

79-
const chunks = Math.floor(audio.length / fftSize);
80-
const target = new Float32Array(fftSize * chunks);
81-
82-
const sample = new Array(fftSize);
83-
const out = fft.createComplexArray();
84-
85-
let max = -Infinity;
79+
return image;
80+
}, [audio]);
8681

87-
for (let i = 0; i < chunks; i++) {
88-
const offset = i * fftSize;
89-
for (let i = 0; i < sample.length; i++) sample[i] = data[i + offset];
90-
fft.realTransform(out, sample);
82+
useEffect(() => {
83+
const ctx = canvas.current?.getContext("2d");
84+
if (ctx) {
85+
ctx.canvas.width = imageData.width;
86+
ctx.canvas.height = imageData.height;
9187

92-
for (let j = 0; j < fftSize; j++) {
93-
target[i * fftSize + j] = Math.abs(out[j]);
94-
max = Math.max(max, target[i * fftSize + j]);
95-
}
88+
ctx.putImageData(imageData, 0, 0);
9689
}
90+
}, [imageData]);
9791

98-
max = max / 4;
92+
return <canvas ref={canvas} className={styles.waveform} />;
93+
};
9994

100-
const image = new ImageData(chunks, fftSize);
95+
// load the spectrum for an entire audio buffer
96+
function spectrum(
97+
audio: AudioBuffer,
98+
fftSize: 2 | 4 | 8 | 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 4096
99+
): Float32Array {
100+
const fft = new FFT(fftSize);
101+
const data = audio.getChannelData(0);
101102

102-
for (let i = 0; i < chunks; i++) {
103-
for (let j = 0; j < fftSize; j++) {
104-
const idx = j * chunks + i;
105-
const value = target[i * fftSize + j];
103+
const chunks = Math.floor(audio.length / fftSize);
104+
const target = new Float32Array(fftSize * chunks);
106105

107-
const scaledValue = Math.pow(value / max, 0.3); // Apply a power scale
106+
const sample = new Array(fftSize);
107+
const out = fft.createComplexArray();
108108

109-
const [r, g, b, a] = valueToColor(scaledValue);
109+
for (let i = 0; i < chunks; i++) {
110+
const offset = i * fftSize;
111+
for (let i = 0; i < sample.length; i++) sample[i] = data[i + offset];
112+
fft.realTransform(out, sample);
110113

111-
image.data[idx * 4] = r;
112-
image.data[idx * 4 + 1] = g;
113-
image.data[idx * 4 + 2] = b;
114-
image.data[idx * 4 + 3] = a;
115-
}
114+
for (let j = 0; j < fftSize; j++) {
115+
target[i * fftSize + j] = Math.abs(out[j]);
116116
}
117+
}
117118

118-
return image;
119-
}, [audio]);
119+
return target;
120+
}
120121

121-
useEffect(() => {
122-
const ctx = canvas.current?.getContext("2d");
123-
if (ctx) {
124-
ctx.canvas.width = fttData.width;
125-
ctx.canvas.height = fttData.height;
122+
// Render a spectrum as a visible image to be written to canvas
123+
function spectrumToImage(
124+
spectrum: Float32Array,
125+
fftSize: 2 | 4 | 8 | 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 4096
126+
) {
127+
const chunks = spectrum.length / fftSize;
128+
129+
const image = new ImageData(chunks, fftSize);
130+
131+
const max = spectrum.reduce((a, b) => Math.max(a, b), -Infinity);
126132

127-
ctx.putImageData(fttData, 0, 0);
133+
for (let i = 0; i < chunks; i++) {
134+
for (let j = 0; j < fftSize; j++) {
135+
const idx = j * chunks + i;
136+
const value = spectrum[i * fftSize + j];
137+
138+
const scaledValue = Math.pow(value / max, 0.3); // Apply a power scale
139+
140+
const [r, g, b, a] = valueToColor(scaledValue);
141+
142+
image.data[idx * 4] = r;
143+
image.data[idx * 4 + 1] = g;
144+
image.data[idx * 4 + 2] = b;
145+
image.data[idx * 4 + 3] = a;
128146
}
129-
}, [fttData]);
147+
}
130148

131-
return <canvas ref={canvas} className={styles.waveform} />;
132-
};
149+
return image;
150+
}
133151

134152
function valueToColor(value: number) {
135153
const r = value < 0.5 ? 0 : 255 * (value - 0.5) * 2;

0 commit comments

Comments
 (0)