Has anyone got a formula for BPM ??

Started by wadmixfm, June 14, 2024, 18:38:54

Previous topic - Next topic

wadmixfm

typical example :

bpm = 120

in theory this should be 2 beats per second

but how could we calculate it

image a bar with

16 1/4 notes like this

1 . . . 1 . . . 1 . . . 1 . . .

in a loop this would play smooth but setting the tempo in that loop so the bpm would be spot on or very close

btw this is pattern based so no 96 quarter notes etc..... its just on or off

lee

Yes its really me :)

dawlane


Jayenkai

120bpm = 1 beat every 0.5 seconds.
Your issue (mine was) is likely trying to keep that when your frame rate is 60fps.  It feels like it oughta be divisible. But if the frame rate drifts even a millisecond, you'll end up drifting out of sync.  And when the floating precision gets involved, it descends in perfect precision.
Instead use a NextBeatAt=... variable.
When the first beat hits, NextBeatAt#=Float(Mills())+(60000.0/bpm#)
Do your loop, and as soon as Mills()>=NextBeatAt#, then do whatever, and NextBeatAt#=NextBeatAt#+(60000.0/bpm#)
Do not do what you're expecting and do NextBeatAt=Mills()+... because that's when things go out of sync.

(You might also want to divide bpm by 4 to account for 4 beats in a bar.  .. Depends on your usage.  I know I did this very very recently, but can't remember where it was!!!)
"Load, Next List"

wadmixfm

ahhhhh i see i will have a play thanks for the info :)

lee
Yes its really me :)

Hardcoal

Every Beat is 1000ms and there is 60,000ms in a minute. so
you do 60,000/beat

For example 60,000/120beat will come 500ms

Some of the things I've done:   https://itch.io/profile/hardcoal  [I keep improving them btw]

wadmixfm

Thanks Hardcoal
in my loop i just did this
Import BRL.TimerDefault
Import sdl.sdlfreeaudio
Graphics 1024,768
SetAudioDriver("FreeAudio")
Global b1:TChannel = AllocChannel()
Global s1:TSound =LoadSound("\Users\Wad\Desktop\01 Bd.ogg") ' put a ogg/wav sample here
Global grid$[5,1,20] ' in my program this is 5= pn , 1 how many tracks ,20 notes per track
Global tem
tem=125
Global Pn
Pn=1
For Local t=1 To 17 Step 4 ' this just creates a default pattern 1 note on beat 1,5,9,13 These are the beats
Grid$[pn,0,t]="1" ' grid$ - variable - array pn Pattern - 0 track T= note loop
Next
DrawText "Press ' S ' To Run Sequence " ,0,20
Flip
Repeat
Until KeyHit(KEY_S)
playme()

Function playme()
Repeat
For Local i = 0 To 15 ' this is 16 notes
Cls

DrawText "Tempo : "+tem,0,20
DrawText "press arrow left or right to change tempo",0,40
DrawText "Press Space to Stop",0,60
Flip
Delay 14700/tem
If grid$[1,0,i]="1" Then PlaySound s1,b1
If KeyHit(KEY_SPACE) Then End
If KeyHit(KEY_LEFT) Then If tem<=40 Then tem=40 ; playme() ;  Else tem=tem-1
If KeyHit(KEY_RIGHT) Then If tem>=240 Then tem=240 ; playme() ; Else tem=tem+1
Next
Until KeyHit(KEY_ESCAPE)
End Function

 it works well :)

but if anyone out there can modify it to run better please have a look :)

i wrote this on the mac but it should be ok on any platform all you need to do is load a ogg file from your location :)

cheers

I know it looks old school but thats what i am :))

Lee

Yes its really me :)

Midimaster

#6
This is not the best way to do it.

You should call a PlayMe() function that checks if the waiting time is over or not

SuperStrict
Graphics 1024,768
Global s1:TSound = LoadSound("\Users\Wad\Desktop\01 Bd.ogg")
Global Grid:String[5,1,20]
Global tem:Int=125,  Pn:Int=1
 
Global Note:Int      ' runs from 0 to 15
Global NextBeat:Int  ' timestamp of the next note
Global Stop:Int=True ' sequence is running or not
 
For Local t:Int=1 To 17 Step 4
    Grid[pn,0,t] = "1"
Next

Repeat
    Cls
    PlayCheck()
    DoAllOtherThings()
    Flip 1
Until AppTerminate() Or  KeyHit(KEY_ESCAPE)


Function PlayCheck()
    If Stop=True Then Return
    If MilliSecs() < NextBeat Then Return

    Note = Note +1
    If Note>15
        Stop=True
    EndIf
      
    NextBeat = NextBeat + 60000/tem
    If Grid[Pn,0,Note] = "1" Then PlaySound s1
End Function  


Function DoAllOtherThings()  
    If Stop=True
        DrawText "Press ' S ' To Run Sequence " ,0,20
    Else
        DrawText "Press Space to Stop",0,20
    EndIf   
  
    DrawText "Tempo : "+tem,0,60
    DrawText "press arrow left or right to change tempo",0,80
    If KeyHit(KEY_SPACE) Then
        Stop = True
    ElseIf    KeyHit(KEY_S) Then
        Stop = False
        Note = 0
        NextBeat = MilliSecs()
    EndIf
    If KeyHit(KEY_LEFT)
        Tem = Max(Tem-1, 60)
    ElseIf KeyHit(KEY_RIGHT)
        Tem = Min(Tem+1, 240)      
    EndIf   
End Function
...crossing the alps with my bike at the moment

wadmixfm

i did say i was old school he he

cheers

midimaster

Yes its really me :)

wadmixfm

in your code i get this error

Expecting expression but encountered 'next'
Yes its really me :)

wadmixfm

don't you need a for local loop for a next command ??

when i take out the next command it just says

found newline ??

this is sooooo not old school lol

lee
Yes its really me :)

Midimaster

#10
removed some bugs in post #6 ... sorry I'm not in front of my computer....

...crossing the alps with my bike at the moment

wadmixfm

#11
ahhhhh ok it works now BUT.......

when the bar is created its way to slow

its not 125bpm at 60000/tem

the grid$ string is 16 notes long not 4

i just created that so i could simulate 4 beats to the bar

so in theory


                1111111  <--- note info
1234567890123456  <--- note info

1     1     1     1        <--- Grid$ data

in between the 1's there are spaces these spaces are notes also for example

1234567890123456

the 1 in the list is bass drum
2 snare drum
and so on

so when i run your code which now works it runs at have speed

now when i put in my value of 14700/tem it runs perfectly

i have timed it on a stop watch and its bang on 125 beats a minute

so my old school code is no different to yours only yours is more compact mine does exactly the same

because in my own code if i change the 14700/tem back to 60000 it runs at the same speed which is not what i want

bpm calculations are soooo mucked up

but i can iterate  that when i change the tempo to say 100 (with my 14700/tem) its bang on 100 beats a minute

very weird , its maybe something to do with the 60hz timing or something like that but it works.

cheers

lee


Yes its really me :)

wadmixfm

I am working on a classic commodore 64 drum machine called MicroRhythm

here are some screen shots

lee.





Yes its really me :)

Hardcoal



here is a small metronome ive made..

it works well..

its part of my big projects but it can also be stand alone
Some of the things I've done:   https://itch.io/profile/hardcoal  [I keep improving them btw]

Midimaster

Ah I understand now!

As I understand your explanations in post #11 your time base is not the QUARTER but the SIXTEENTH.

Your 16 position represent the sixteen 1/16-notes of one bar. So the calculation base is not 60000/tim but 15000/tim

Test my code with 15000/tim and you will get exact 125.0 bpm or any other bpm with an accuracy of 100%

Your (by experiments) discovered 14700 are because of the inaccuracy of a DELAY based model. Reason is, that the moment, when the DELAY returns it is often already 1msec too late. If you now add the note distance 15000/tim you will not compensate this error. So the only chance you have is to add the 15000/tim to the old timestamp you calculated!

Try this experiment:

This test runs 10 seconds and should count 1000 frames:

SuperStrict
Graphics 800,600
Global Start:Int, Frames:Int, NextBeat:Int

Start  = MilliSecs()+10000
Frames = 0

Repeat
Cls
DrawText "DELAY Test running...", 100,100
Frames = Frames +1
Delay 10
Flip 0
Until MilliSecs()>Start

Print "DELAY based couting:   " + Frames

Start     = MilliSecs()+10000
Frames    = 0
NextBeat  = MilliSecs()
Repeat
Cls
DrawText "NEXTBEAT Test running...",100,100
If MilliSecs()>NextBeat
Frames   = Frames +1
NextBeat = Nextbeat +10
EndIf
Flip 0
Until MilliSecs()>Start

Print "NextBeat based couting: " + Frames

But the DELAY based test only counts ca. 920 beats
...crossing the alps with my bike at the moment