Calculating BPM

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Calculating BPM
by on (#58365)
I've had a request to display the current song BPM in NTRQ and was wondering if anyone had a clever way of doing it.

I can't do a simple calculation based on the tick speed (number of frames per tick) because with the Speed Table it's possible to get those "in between" speeds by quickly alternating between two different tick speeds.

I was thinking of something along the lines of keeping two counters, one that's updated every frame (screen refresh rate) and another that gets updated every "tick". Then performing a calculation using the ration of one counter vs the other, which should give me something to work with.

Sounds like a bit of a nightmare in 6502 assembly though :)

Any thoughts?

by on (#58369)
What about a LUT? For the screen refresh rate you've got 2 possibilities, no? (either it's PAL or it's NTSC)
If the second factor - i.e. number of ticks per measurement interval (1 second?) has a small enough range to result in a table less than a few kB then I'd say use a LUT.

by on (#58372)
Take the sum of the last four rows' speeds, and divide 3606 (or 3000 on PAL) by that sum.

by on (#58376)
On paper I'd say the best method would be a combination of those two suggestions:

1) add up the number of frames for a count of 8 ticks/steps/beats
2) make a look up table (well, one for NTSC one for PAL) based on tepples's calculation using the number from (1) as the index

I guess the table would have to be 3 bytes per entry: 2 bytes for the whole number part and 1 byte for the fraction?

by on (#58386)
If it's something updated only once per row, a plain old division routine would work.

by on (#58400)
tepples wrote:
If it's something updated only once per row, a plain old division routine would work.


True, but if I encode the BPM table into nibbles:

e.g. 112.5 BPM would become HEX 11 25

it's then really easy to print the numbers rather than have to handle them as decimal.

by on (#58404)
If you're storing it in BCD, you'll have to convert that back to binary in order to play the file back.

It doesn't take long to convert a 16-bit binary number to decimal, especially if you don't have to do a bunch of them in a frame. There's a routine on the wiki that does the deed in under 8 scanlines of CPU time.

by on (#58408)
tepples wrote:
If you're storing it in BCD, you'll have to convert that back to binary in order to play the file back.

It doesn't take long to convert a 16-bit binary number to decimal, especially if you don't have to do a bunch of them in a frame. There's a routine on the wiki that does the deed in under 8 scanlines of CPU time.


Storing the tempo in nibbles means I can just do;

pha
lsr a
lsr a
lsr a
lsr a
sta $2007
pla
and #$0F
sta $2007

for the two bytes and there's no other calculation required. It does mean I have a fair-sized table though.

I'll check out the decimal stuff on the Wiki also, thanks tepples.

by on (#59136)
@tepples : I just spotted something in your formula. You used "3606" as the basis of the tempo calculation for NTSC whereas I assumed it would be 3600 (60 x 60) seeing as you say PAL uses 3000 (which I assume comes from 50fps x 60). Did you mean 3606 or was that a typo? If it's not a typo can you explain why 3606 is the correct figure?

Thanks

by on (#59140)
The vertical scan rate of the NTSC NES is slightly greater than 3600 frames per minute. From colorburst on Wikipedia and NTSC vs. PAL on wiki.nesdev.com:

NTSC subcarrier frequency (fsc) = 39375000/11 Hz
PPU dot frequency (fdot) = 3/2 dots per cycle * fsc
PPU frame = 341*261 + 340.5[1] = 89341.5 dots per frame
Vertical scan rate = fPPU / framelength = 60.0988 frames per second = 3605.93 frames per minute

PAL subcarrier frequency (fsc) = 4433618.75 Hz
PPU dot frequency (fdot) = 6/5 dots per cycle * fsc
PPU frame = 341*312 = 106392 dots per frame
Vertical scan rate = fPPU / framelength = 50.0070 frames per second = 3000.42 frames per minute


[1] What's the half? Every other prerender scanline is shortened by 1 dot, but only on NTSC.

by on (#59142)
Overwhelmingly thorough explanation - brilliant :)

Thanks tepples.