Help building a frequency inverter/voice inverter?


    Jul 22 2016 | 8:27 pm
    Hi everyone!
    Beginner-intermediate max user, no experience in gen. Looking for a frequency inverter; also known as a voice inverter or voice scrambler.
    I can't seem to find one anywhere, so I decided to try to build one myself.
    This author seems to have come up with a pretty simple and clever means of getting frequency inverter. How do I go about multiplying every other sample by -1 to achieve the frequency invert?

    • Jul 23 2016 | 5:41 pm
      Hi kind of intermediate myself here too, but my thoughts anyway:
      you mention Gen, but I would guess that achieving this in the time domain would be quite tricky; have you any experience using fft~ in Max? i.e. doing it in the frequency domain. I quickly tried to phase shift an audio file using a simple pfft~, and failed - cuz my FFT chops are at best crap, so I won't even embarrass myself by posting it here (I used cartopol~/poltocar~ and added PI to the phase outlet = no change at all).
      Brendan
    • Jul 23 2016 | 7:28 pm
      How do I go about multiplying every other sample by -1 to achieve the frequency invert?
      In gen~, if that's all you want to do, and you're not worrying too much about what the author on that site wrote about "a sampling rate has to be chosen to (approximately) match the signal bandwidth", then the codebox can be as simple as this:
      History n(1); out1 = in1 * n; n *= -1; (edit: in my patch below i used 'n = n * -1' as i was just trying to be explicit to be absolutely sure of what i was doing, but it's the same thing as, and is also possible in gen~ with, 'n *= -1')
      Here's a patch to demonstrate:
      ^In that patch, if you choose(within the 'demosound' bpatcher interface), the more musical sounds like drum beats or 'sho...' or 'anton...' it works to some extent, but if you choose the native-american talking('cherokee'), you'll notice you can't hear much, because we aren't 'matching the signal bandwidth' of that particular sound very accurately here.
      I might try to come back later with a more pfft~-oriented solution if i have time, that way you wouldn't have to match the signal's spectrum-range/bandwidth, but for now, hope that makes sense and gets you started understanding how to do something similar in gen~.
    • Jul 23 2016 | 7:53 pm
      It's just a ring modulator (with modulator at Nyquist freq). No fft or gen magic needed. You can achieve a kind of downsampled effect with freqshift~ and negative frequencies (mirrored spectrum).
    • Jul 23 2016 | 9:05 pm
      with fft you´d have the option to scale, limit and shift the range until it sounds useful.
      for example you might want to dynamically assing a base frequency and mirror the spectrum around it.
    • Jul 23 2016 | 11:02 pm
      with fft you´d have the option to scale, limit and shift the range until it sounds useful.
      I don't disagree, but where is your patch? ;-) Joking aside, it's a matter of choosing convenient method. Simple RM in frequency domain is just a waste of CPU cycles.
      for example you might want to dynamically assing a base frequency and mirror the spectrum around it.
      This is what freqshift~ with negative frequency shift does. Of course it depends on what a "base frequency" is.
    • Jul 23 2016 | 11:24 pm
      Ok, raja, Roman and AK, I have to ask: wtAf is going on here:
      Is this sourcery or am I entirely stupid? I spent this afternoon trying to solve it and you guys do it with, like, 5 objects or 3 lines of code:
    • Jul 24 2016 | 2:22 am
      It's just basic DSP. If you know how to ring modulator works (side bands and stuff) it's quite obvious. Both examples (Raja's and mine) generate signal with alternating -1 and 1. Raja did this exactly like in the article linked by @OP, I did it using some stock msp objects. And this signal is how sine looks at Nyquist frequency (half the sampling rate). Multiplying a signal by sine is the 'ring modulator'. RM shifts entire spectrum up and down by the frequency of the sine. Given the sound spectrum mirrors at each multiple of Nyquist frequency, frequencies from 0 to Nyquist are moved beyond limit of hearing and frequencies from Nyquist to sample rate are moved into human hearing frequency range (see fft~ help file).
    • Jul 24 2016 | 9:46 am
      i dont find sidebands so obvious, but the default case of spectrum inversion by inverting every other sample could be called a "simple math trick" at least.
      it is superior to fft solutions whenever you dont want to deal with latency, but for everything else but 180 degrees it will introduce aliasing (ak´s second example, correct me if i am wrong)
      in many situations it might be best practice to use the ringmodulator method and apply post HP and LP filters.
      fft: sorry for not having an example ready, but in fft you can inverse the spectrum quite easily, too, by doing something like !-1024 to the bin count before rebuilding the signal. adding a fft-banded brickwall filter lies next to it wih another 3-4 objects.
    • Jul 24 2016 | 10:08 am
      spectrum stretching, by phase manipulation.
      (dc is optional, for hilbert transform use the hilbert object. sorry, cant upload binaries from my windows enclave now)
    • Jul 24 2016 | 10:10 am
      right inlet is frequency, float or signal. what the exact relationship between this value and the spectrum distortion is? no idea.
    • Jul 24 2016 | 10:47 am
      raja wrote: "and you’re not worrying too much about what the author on that site wrote about "a sampling rate has to be chosen to (approximately) match the signal bandwidth",
      which is, interesting enough, a perfect description of what you should do when "emulating" this "effect" on the digital layer.
      because on the digital layer the spectrum is known, it goes from 0 to nyquist, while an radio signals spectrum is theoretically infinite and in practice it depends on the weather, which is why analog voice inverters allows willingly set modes, such as 5, 6, and 7 khz.
      -110
    • Jul 25 2016 | 3:02 am
      (hehe... uh... the OP wrote "How do I go about multiplying every other sample by -1...?" and posted it in the 'gen' forums, so honestly, that was all i cared about answering... i'm still reading this thread, but staying out of it beyond that ;D ...but these have all been very interesting posts by very great and knowledgable people though, cheers!)
    • Jul 25 2016 | 4:08 am
      Was dragging my feet about releasing this for a while... But this thread gave me some energy to post.
      This is a project I have been working on for little while. It is not done at all. You will notice some artifacting on decryption. Open to suggestions on how to make it better, I want it to be open and available to the public at some point (I suppose it is now)
      Essentially it is fft reordering or encryption based on a generated key. It basically pulls together 2 or 3 patches from the gen examples folder. Inspired by Simon Singh's "The Code Book", it approaches encryption using a monoalphabetic cipher. Not exactly what you are talking about here but related so here you go!
    • Jul 25 2016 | 1:49 pm
      @AK, yes I understand the basic DSP *theory* behind it, I was merely marveling at the simplicity of raja's
      History n(1); out1 = in1 * n; n *= -1;
      which I have to admit I don't understand. Is the "n" in History a variable length?? I'm trying to realise his solution with gen objects, so I can understand it a bit better, as I have some Gen workshop stuff to prepare soon. I was able to RM a 22050Hz wave with various audio sources, which achieved the desired effect, what I found more challenging was inverting every other sample. Sorry to OP for the hijack.
      Brendan
    • Jul 25 2016 | 3:39 pm
      Is the "n" in History a variable length??
      No, n is initialized to 1.
    • Jul 25 2016 | 3:45 pm
      Thank you!
    • Jul 25 2016 | 3:56 pm
      @Brendan I'm so sorry, I didn't realize you were asking specifically about that code(inverting every sample with simple gen~ code was what interested me most at first about the thread). Here's some comments that might help:
      History n(1); //
      And sometimes it helps me to go through exact values, so let's take a hypothetical example where the input ramps from 0 to 1 over 4 samples(never quite reaching 1 though), so that the input values are 0., 0.25, 0.5, and 0.75: 1) 1st sample input of 'in1' = 0.; 1st sample 'n'(initial value was initialized to '1') = 1.; 1st sample output 'out1' = in1(0) * n(1) = 0*1 = 0.; 2) 2nd sample input of 'in1' = 0.25; 2nd sample 'n'(changed to -1 at the end of the previous dsp loop) = -1.; 2nd sample output 'out1' = in1(0.25) * n(-1) = 0.25 * -1 = -0.25; 3) 3rd sample input of 'in1' = 0.5; 3rd sample 'n'(changed back to 1 again at the end of the previous dsp loop) = 1.; 3rd sample output 'out1' = in1(0.5) * n(1) = 0.5 * 1 = 0.5; 4) 4th sample input of 'in1' = 0.75; 4th sample 'n' = -1.; 4th sample output 'out1' = in1(0.75) * n(-1) = 0.75 * -1 = -0.75;
      Thus 0.,0.25,0.5,and0.75 becomes 0.,-0.25,0.5,and-0.75(every other sample inverted). The key thing to remember here is that 'n' is a single sample history which changes between 1 and -1 each time through the dsp loop but only changes its state after we have already output the current input sample multiplied by the previous state of 'n'(it's important that the line "n*=-1;" comes after "out1 = in1*n;").
      Hope that helps(sorry for my wordiness, sometimes folks have mentioned it helps to be that descriptive.... but if not, ignore me).
      @Thomas John Martinez That looks amazing, I'll study up on that patch more soon.
    • Jul 25 2016 | 4:33 pm
      Hey raja I didn't explicitly say "@raja", so no apology expected! Your wordiness is very much appreciated; I had a vague idea that 'n' was updating itself every time through the loop, but your explanation has clarified this to the point that even I get it ;)
      Best regards, peace and love etc Brendan
      (OT; Gregory and I are delivering some gen stuff here in N.I. in a few months, so I need to brush up on my gen chops; been languishing in Linux/Supercollider land for the past year, so have grown a little rusty.)