Skip to content

Commit 4ec3563

Browse files
committed
fix bpm fft size
1 parent ac3b3fc commit 4ec3563

File tree

2 files changed

+30
-9
lines changed

2 files changed

+30
-9
lines changed

src/backend/wsAudio.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ export function processAudio(buffer: Buffer, sampleRate = sampleRateDefault) {
3434
const samples = new Int16Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 2)
3535
const input = Array.from(samples, s => s / 32768)
3636
for (const s of input) sampleBuffer.add(s)
37-
const spectrum = fft(input)
37+
38+
const size = 1 << Math.floor(Math.log2(input.length || 1))
39+
const spectrum = fft(input.slice(0, size))
3840
const mags = util.fftMag(spectrum)
3941
audioState.bins = mags.slice(0, mags.length / 2)
4042
let max = 0
@@ -45,7 +47,7 @@ export function processAudio(buffer: Buffer, sampleRate = sampleRateDefault) {
4547
idx = i
4648
}
4749
}
48-
const freq = (idx * sampleRate) / input.length
50+
const freq = (idx * sampleRate) / size
4951
audioState.freq = freq
5052
audioState.hue = (idx / (mags.length / 2)) * 360
5153
audioState.level = max / (mags.length / 2)
@@ -55,7 +57,8 @@ export function processAudio(buffer: Buffer, sampleRate = sampleRateDefault) {
5557
lastBpmUpdate = now
5658
try {
5759
const mt = new MusicTempo(Float32Array.from(sampleBuffer.toArray()))
58-
if (mt.tempo) audioState.bpm = mt.tempo
60+
const tempo = parseFloat(String(mt.tempo))
61+
if (!Number.isNaN(tempo)) audioState.bpm = tempo
5962
} catch {
6063
// ignore errors
6164
}

tests/wsAudio.test.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,33 @@ function genSine(freq: number, samples: number, sampleRate: number) {
99
return buf
1010
}
1111

12+
function genBeat(bpm: number, seconds: number, sampleRate: number) {
13+
const total = Math.round(seconds * sampleRate)
14+
const buf = Buffer.alloc(total * 2)
15+
const period = Math.round((60 / bpm) * sampleRate)
16+
for (let i = 0; i < total; i++) {
17+
const v = i % period === 0 ? 1 : 0
18+
buf.writeInt16LE(Math.round(v * 32767), i * 2)
19+
}
20+
return buf
21+
}
22+
1223
describe('processAudio', () => {
1324
beforeEach(() => {
1425
resetAudioState()
1526
})
16-
test('detects frequency', () => {
17-
const buf = genSine(440, 1024, 44100)
18-
processAudio(buf)
19-
expect(audioState.freq).toBeGreaterThan(430)
20-
expect(audioState.freq).toBeLessThan(450)
21-
})
27+
test('detects frequency', () => {
28+
const buf = genSine(440, 1234, 44100)
29+
processAudio(buf)
30+
expect(audioState.freq).toBeGreaterThan(430)
31+
expect(audioState.freq).toBeLessThan(450)
32+
})
33+
34+
test('detects bpm', () => {
35+
const buf = genBeat(120, 4, 44100)
36+
processAudio(buf)
37+
expect(audioState.bpm).toBeGreaterThan(110)
38+
expect(audioState.bpm).toBeLessThan(130)
39+
})
2240

2341
})

0 commit comments

Comments
 (0)