BPM Számláló algoritmus

A BPM azaz “beat per minute” a zene sebességét mutatja meg.  Sok idő volt mire eljutottam odáig, hogy egy jól működő ütem számlálót sikerüljön elkészítenem, de mondanom sem kell mennyi mindent tanultam vele. Többféle algoritmust kipróbáltam, itt most azt szeretném bemutatni ami nekem a legjobban bevált.

Működési elmélet

A mai elektronikus zenék ritmusát a dob adja, tehát ebből a legegyszerűbb kiindulni. Első lépésben érdemes a mintavételezést csökkenteni, ezzel számolási kapacitást spórolhatunk meg. Mintavételezés után abszolút értékét vesszük a jelnek, majd egy nagyon alacsony vágású aluláteresztő szűrőt alkalmazunk, ekkor megkapjuk az “envelope”-ot. Ezután autokorellációval elemezzük a jelet és kiszámoljuk a BPM-et. Ismét excel segítségével lássuk lépésenként a folyamatot:

  • Down sampling

Az eredény pontosságát, illetve a felbontást befolyásolja a mintavételezési frekvencia, kompromisszumot kell kötni az algoritmus gyorsasága és a kívánt felbontás között. 8kHz mintavételezéssel a felbontás 0.0075Bpm, ami már bőven elég lehet. Mivel a hangminőség nem kritikus, ezért a legegyszerűbb megoldás is elegendő(elhagyjuk minden x-edik mintát) a  down sampling-hez. A könnyebbség kedvéért az excelbe importálás előt már 8000Hz-re csökkentettem a mintavételezést, az így kapott waveform:

  • A jel abszolút értéke
Sub Abs1()
 Dim n As Long
 For n = 2 To 100000 Step 1
   Cells(n, 2).Value = Abs(Cells(n, 1).Value)
 Next n
End Sub

  • Aluláteresztő szűrő 10Hz-es vágási frekvenciával

Sub FilterData()
Dim n As Long
 Call LowPassFilter(8000, 10, 1)
 For n = 2 To 100000 Step 1
   Cells(n, 3).Value = Transform(Cells(n, 2).Value)
 Next n
 End Sub

  • Autokorelláció

Mire jó az autokorelláció? Segítségével elemezhetjük hogy egy jel mennyire hasonlít “saját magára”,  megkereshetjük a jelben az ismétlődő mintákat. Autokorelláció során a jelet időbeni eltolt változatával hasonlítjuk össze. Az eltolás mértéke a vizsgálni kívánt frekvenciával egyenlő. Nekünk a frekvenciát jelen esetben a BPM jelenti. Meg kell határoznunk hogy milyen tartományban szeretnénk mérni, ez határozza meg az eltolás kezdeti és végső értékét. A mérési tartományt megfelelő választását két dolog is meghatározza. Az egyik a számolási kapacitást, ugyanis minnél nagyobb tartományt vizsgálunk, annál több számolás szükséges, és mikrokontroller esetén igen csak végesek az erőforrásaink. A másik tényező, hogy gyakran a BPM al- és fel-harmonikusai gyakran erősebben jelentkeznek, azaz egy 125BPM sebességű zenénél lehet hogy 62,5BPM-et fogunk kapni (az excelben csatolt zenére is pont igaz).


Sub AutoCorellation()

Dim WinStart As Long, WinStop As Long, SampleRate As Long, MinBPM As Long, MaxBPM As Long, i As Long, n As Long
Dim Sum As Double
Dim Samples(100000) As Double
Dim Result(100000) As Double

MinBPM = 110
MaxBPM = 150
SampleRate = 8000
WinStart = 60 / MaxBPM * SampleRate
WinStop = 60 / MinBPM * SampleRate

For i = 0 To 100000 Step 1
  Samples(i) = Cells(i + 2, 3)
Next i

For i = WinStart To WinStop Step 1
  Sum = 0
  For n = 1 To 100000 - WinStop Step 1
    Sum = Sum + Samples(n) * Samples(i + n)
  Next n
  Result(i) = Sum
  DoEvents
Next i
For i = 0 To 100000 Step 1
  Cells(i + 2, 4) = Result(i)
Next i

End Sub

  • BPM kiszámolása

Ezek után nincs más hátra csak megtalálni a legmagasabb értéket az autokorelláció eredményei között. A pozíciójából és a mintavételezési sebességből adódik a másodpercenkénti ütemszám, amit felszorozva 60-al megkapjuk a BPM-et.

Sub FindBpm()
 Dim i As Long
 Dim Max As Double
 Dim Position As Integer
 Dim Coeff As Long
 Dim BPM As Double
 Dim SampleRate As Long
 SampleRate = 8000
 Max = 0

 For i = 0 To 10000 Step 1
   If Cells(i + 2, 4).Value > Max Then
     Max = Cells(i + 2, 4).Value
     Position = i
   End If
 Next i
 BPM = SampleRate / Position * 60
 Debug.Print BPM
End Sub

 

A kódot elkészítettem számítógépre is C#-ban, és androidra JAVA-ban. Az androidos verzió githubon elérhető. A fenti VBA kód pedig teljes egészében megtalálható ebben az excelben.

Facebook Comments