Octave Down / Frequency Divider

bearded clumsybear's icon

Hi there!
I'm learning gen~ and I'm trying to make an octave down effect.
So as I understand it, I have to count the zero crossings and divide the frequency.

Now I'm not exactly sure how to go about it.

Found some gen~ code on reddit by user @lilTrybe but I don't have an account on there to check with him.
So I figured I ask here.


History previousInput;  
History output;   

if (in1 > 0 && previousInput <= 0) {      
if (output) output = 0;     else output = 1; }   
out1 = output; previousInput = in1;  

It basically works with a lot of filtering, but I get a lot of noise whenever there is a very low volume signal.
Can't even gate it properly.

So I guess I have two questions:
-What exactly does the code do besides changing the phase whenever the amplitude crosses zero going up?
-How can I round the signal in gen~to avoid errors and noise?

cheers

bearded clumsybear's icon

god I feel so dumb... just figured out how to use the the [round] operator in gen. just rounded the input with 0.01 - done.

still, if someone would like to chime in to explain the code, I would appreciate it :)

👽'tW∆s ∆lienz👽's icon

the inner 'if' statement alternates between outputting 1s and 0s, but only when the outer 'if' statement crosses the zero-crossing at an upward slope(basically like you said: "changing the phase whenever the amplitude crosses zero going up")... but how are you applying it in your patch(if you feel like posting it)?

bearded clumsybear's icon

I see, thanks for explaining - is there any way to let the signal alternate between 1 & -1 instead of 1 & 0? I'm aware that HP filtering should fix the signal alternating between 1 & 0, but is there a more elegant solution to this?

I'm not at my main machine right now, can't post a compressed copy of the patch at the moment. But basically, I'm using live audio input and just run it through the codebox in gen~, that's all.
Applying some filtering before & after the codebox takes care of DC offsets and nasty artefacts in the higher frequencies.

👽'tW∆s ∆lienz👽's icon

is there any way to let the signal alternate between 1 & -1 instead of 1 & 0?

yes, the inner 'if' can get changed like so: "if(output>=0) output = -1; else output = 1;"

I'm using live audio input and just run it through the codebox in gen~, that's all

if you're doing this, you're causing it to become a square wave: even if it alternates between -1 and 1, the timbre of your original sound will be replaced by a square wave because the details of the waveform's amplitude(all the fractional values between -1 and 0 and between 0 and 1) are lost.

instead, i think you want to run your signal through this, but then multiply the output of this by the original signal again(changing the 'if' as shown above to alternate between -1 and 1 will allow the multiplication to alternate the phase of the original signal properly between a 0º shift and a 180º shift).

it's described here as well: https://cycling74.com/forums/implementing-frequency-divider-in-max/
(in that thread, they talk about 'a comparator, which produces a pulse wave', and that's exactly what your codebox above is: so you would then multiply this comparator's pulse-wave output by the original signal(i think the code you found on reddit might've been referring more to oscillators which can have a phase-input fed with 0 and 1 maybe?))

hope that makes sense and helps 🍻

Roman Thilenius's icon


i was about to recommend round (i keep forgetting its name and existance but [round~] in MSP can also help in situations like that) but then i saw you already found it.

bearded clumsybear's icon

Thanks Roman, yeah I just had to learn a bit more about the basic usage of gen~ operators. Still have a lot more to learn though.

Raja, thank you for your thorough explanation, it helps a lot to wrap my head around all this.
So by applying your changes to the code I would get a square wave alternating between -1 and 1, but I would be able to multiply it by the original signal.

I'm not exactly sure what this means for the signal, would you mind elaborating on this a bit more?
What is the result of this multiplication?

👽'tW∆s ∆lienz👽's icon

no prob, happy to help :)

What is the result of this multiplication?

The multiplication switches the signal's phase by 180º every zero-crossing going upward.
First off: by tracking zero-crossings going upward, the comparator guesses as to a periodic signal's full 'period'(picture a basic sine-wave: https://en.wikipedia.org/wiki/Sine_wave...to include the full period of the sine-wave, you only want to track the zero-crossings moving in one direction, otherwise you'd end up tracking just half the cycle/period if you tracked it at every zero-crossing).
Secondly: if you then switch phase for every other full-cycle of the sine-wave, it no longer holds the shape it needs to complete a full cycle, instead, it now forms a new signal, similarly shaped(so the overall timbre will likely remain similar enough by ear depending on what kind of real-world signal you use), but achieving periodicity over two cycles where it once achieved periodicity over just one.
this effectively divides the frequency in half.

you can probably understand from just that much, but just to draw it out based on the wikipedia pic of a sine-wave, the resulting phase-switching makes the waveform look more like this with the blue-addition substituting for the red wherever blue appears in this pic(of course not as squiggly as my lack of drawing-skills cause it to be, haha):

edit - might as well add an explanation patch here, too:

Max Patch
Copy patch and select New From Clipboard in Max.

bearded clumsybear's icon

Ah, yes I see what you mean now. It totally makes sense, this seems to be the solution to get rid of the vast amount of overtones which are generated by the square wave.

There is only one thing I don't understand the theory behind. Why does the the multiplication of the square wave by the original signal result in a 180 degree shift of every other cycle?

Also would a simple [*] operator with two inputs in gen~ do the trick? Sorry for asking such basic gen/max questions. I will try it out in a couple of hours when I get back to my machine running max.

bearded clumsybear's icon

Oh wow, Raja - the explanation patch makes this even more clear to me!
Thanks so much

👽'tW∆s ∆lienz👽's icon

you're welcome, happy to help :)
(ya, the patch can show how the 180º shift happens: in the amplitude/time domain, this phase-shift is just seen as opposing polarity, where positive becomes negative and vice versa, so the square-wave shifting between -1 and 1 helps track this shift nicely according to zero-crossings)