Generate square wave from milliseconds or Hz


#1

I'm trying to create a BPM object similar to the lfo/square object. The idea is to set the BPM, and steps (4th notes, 8th notes or 16th notes). The object looks like the screenshot below.

I found this table online that shows how bpm relates to milliseconds and Hz. http://www.wiseguysynth.com/larry/convert/bpm_table.htm

My problem with using a square lfo to create a sequencer is that it only outputs the square wave for 4th notes, and I want to be able to use 16th notes. I'm creating this object so I don't have to look up at which position I need to set the dial of the lfo to get the tempo I need.

I've figured out the necessary formulas to calculate the necessary Hz and milliseconds values, this is what I get for 120 bpm.

120 bpm = 2 Hz = 500 msec (4ths)
120 bpm = 4 Hz = 250 msec (8ths)
120 bpm = 8 Hz = 125 msec (16ths)

And this is the point where I'm stuck. The MTOFEXTENDED() function from the square lfo uses numbers generated by the dial to calculate the frequency. I've noticed that the numbers this function needs are quite large. For instance: If I feed the number 15938355 into MTOFEXTENDED(), I get the 8 Hz wave. I also know that the range of the dial is from -134217727 to 134217727.

Any ideas on how to convert from Hz to the range the dial uses?


#2

I believe there needs to be a specific control type for BPM. I understand the demand for "detents" at round numbers. But using an integer control would not even allow smaller steps than 1 bpm, I think it should be only "detents".
An integer number control is currently not midi controller mappable, and it would limit the maximum bpm to 127. Or if taken the double of the cc value as bpm, it would not allow single bpm increments anymore.

The MTOFEXTENDED map semitones in fractional format to phase increment for oscillators. If you want whole number bpm's rather than a exponential scaling, there is no use for this function.

LFO's work internally with a phase in 32bit integer format. Every cycle, the phase gets augmented by a certain amount. This amount determines the frequency. If you add one unit to an integer above 2147483647 (2^31 - 1), it wraps to -2147483648 (- 2^31). Yes minus. So it repeats implicitly! Check two's complement number representation for the details.
If you'd augment the phase with a single unit at k-rate, the resulting frequency is 3000Hz/4294967296 = 0.69849 microhertz
To get exactly 1 Hertz, the corresponding phase increment would need to be (2^32)/3000 = 1431655.7653. We need to round that to a whole number, 1431656. The real frequency obtained then is 0.99999983608 Hz. Not good enough for CERN but good enough for musical purposes. The quartz crystal time reference is less accurate than the error caused by roundoff here.

For a square LFO, the output is true when the phase is greater than zero.

So a 1Hz square LFO can be coded like this (k-rate code)
Phase += 1431656;
outlet_wave= (Phase>0)?1:0;

In C, "+=" means add to. It is shorthand for
Phase = Phase + 1431656;
The "?" construction is shorthand for
if (phase>0)
outlet_wave=1;
else
outlet_wave=0;
If you have an integer BPM inlet or parameter, this can be modified into

Phase += 1431656 * param_bpm;
outlet_wave= (Phase>0)?1:0;

Actually the line
outlet_wave= (Phase>0)?1:0;
can be reduced to
outlet_wave= Phase>0;


#3

Yeah I am interested in the BPM object too but gave up on it for now.


#4

Thanks for the explanation @johannes. Some of the numbers I've encountered make more sense now. I started working on this object mainly to understand how objects work and how they relate to the firmware.

A floating point number control would indeed make more sense for some things. And it makes programming a lot easier. Integer math is quite hard if you aren't used to it confused.

I'll take a look at finishing the object next week.


#5

@janvantomme Did you finish your timer/bpm object? I could use it in project right now.


#6

Not yet. Too busy with other things right now.


#7

Ok. I'll give it a go. So far i kinda fake your patch by using a pll object but it's just a workaround. A specific object would be better. The time value in the pll object corresponds to the steps value in your timer/bpm.

bpm.axo (886 Bytes)
pll.axo (1.3 KB)
bpm.axp (2.4 KB)


#8

Here is my first attempt. Still needs some work.

  • PBM speed seems to be a little off. Compared to my Volca. Maybe the Volca is wrong?
  • Swing not jet implemented.

metronome.axp (4.7 KB)
metronome.axo (1.1 KB)