Skip to content

Commit 2dd7acb

Browse files
committed
Use essentia bpm
1 parent ce500a9 commit 2dd7acb

File tree

5 files changed

+12
-16
lines changed

5 files changed

+12
-16
lines changed

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@
5353
"dotenv": "^16.4.5",
5454
"express": "^4.18.2",
5555
"fft-js": "^0.0.12",
56-
"music-tempo": "^1.0.3",
5756
"mic": "^2.1.1",
5857
"essentia.js": "^0.1.3",
5958
"react": "^19.1.0",

pnpm-lock.yaml

Lines changed: 0 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/backend/wsAudio.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { WebSocketServer } from 'ws'
22
import fftjs from 'fft-js'
3-
import MusicTempo from 'music-tempo'
3+
import { Essentia, EssentiaWASM } from 'essentia.js'
44
// eslint-disable-next-line import/default
55
import RingBufferTs from 'ring-buffer-ts'
66
const RingBuffer = RingBufferTs.RingBuffer
@@ -29,8 +29,9 @@ const sampleRateDefault = 44100
2929
const bpmWindow = sampleRateDefault * 8
3030
const sampleBuffer = new RingBuffer<number>(bpmWindow)
3131
let lastBpmUpdate = 0
32+
const essentiaPromise = Promise.resolve(new Essentia(EssentiaWASM))
3233

33-
export function processAudio(buffer: Buffer, sampleRate = sampleRateDefault) {
34+
export async function processAudio(buffer: Buffer, sampleRate = sampleRateDefault) {
3435
const samples = new Int16Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 2)
3536
const input = Array.from(samples, s => s / 32768)
3637
for (const s of input) sampleBuffer.add(s)
@@ -56,8 +57,10 @@ export function processAudio(buffer: Buffer, sampleRate = sampleRateDefault) {
5657
if (now - lastBpmUpdate > 2000 && sampleBuffer.getBufferLength() >= sampleRate * 4) {
5758
lastBpmUpdate = now
5859
try {
59-
const mt = new MusicTempo(Float32Array.from(sampleBuffer.toArray()))
60-
const tempo = parseFloat(String(mt.tempo))
60+
const essentia = await essentiaPromise
61+
const signal = essentia.arrayToVector(Float32Array.from(sampleBuffer.toArray()))
62+
const res = essentia.RhythmExtractor2013(signal, 208, 'multifeature', 40)
63+
const tempo = res.bpm
6164
if (!Number.isNaN(tempo)) audioState.bpm = tempo
6265
} catch {
6366
// ignore errors

src/types/essentia.js.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
declare module 'essentia.js'

tests/wsAudio.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,16 @@ describe('processAudio', () => {
2424
beforeEach(() => {
2525
resetAudioState()
2626
})
27-
test('detects frequency', () => {
27+
test('detects frequency', async () => {
2828
const buf = genSine(440, 1234, 44100)
29-
processAudio(buf)
29+
await processAudio(buf)
3030
expect(audioState.freq).toBeGreaterThan(430)
3131
expect(audioState.freq).toBeLessThan(450)
3232
})
3333

34-
test('detects bpm', () => {
34+
test('detects bpm', async () => {
3535
const buf = genBeat(120, 4, 44100)
36-
processAudio(buf)
36+
await processAudio(buf)
3737
expect(audioState.bpm).toBeGreaterThan(110)
3838
expect(audioState.bpm).toBeLessThan(130)
3939
})

0 commit comments

Comments
 (0)