Issues building a pitched delay

    May 31 2018 | 3:07 am
    I’m trying to build a delay that raises the frequency of each delayed line by a specific (chromatic) interval. In other words, if the initial note is C60 and the pitch raise is a whole step, then the first delay will play at D62, the next at E64, then F#66, G#68, etc. The below patch works so long as the initial note is C60, but if it is higher than that, the intervals sound stretched; below C60, the intervals sound compressed.
    The [plugin~] to [plugout~] audio chain should be self-explanatory. The “delay” [live.dial] sets the delay time in ms and the “interval” [live.dial] sets the interval in half steps. The [r note1] object receives the MIDI note number from an external patch (this is a workaround so I don’t have to include a pitch recognition patch).
    Calculating the interval frequency proceeds as follows using the above example: C60 comes in as a starting pitch and has “2” added to it (representing the whole step) to generate MIDI note D62. Both note numbers are converted to Hz and the difference is calculated. That number (32.039203 in this case) is sent to the appropriate [freqshift~] object in the delay chain. D62 then has the same operation performed on it, yielding the frequency difference between it and E64 (35.962789 Hz); E64 has the same operation performed on it, etc., through five total iterations.
    As far as I’ve been able to check, the math on this is correct, e.g., the difference between D62 and E64 is in fact 35.962789 Hz and the difference between D86 and E88 really is 143.851156 Hz. However, when the patch is played with a starting note higher than C60 the perceived difference between, for example, D86 and E88 is not a whole step. Setting the “interval” [live.dial] for fifths (“7”), this is very clear, e.g., C60 to G67 is 130.369871 Hz and sounds like a perfect fifth, but in C84 to G91 -- a difference of 521.479482 Hz -- the G sounds very sharp.
    I’m assuming there’s something I’m not accounting for, but I can’t figure what. Is raising a pitch by the frequency difference between it and the desired note not the right method for this? Is [freqshift~] not the right object (i.e., is it not made for what I want it to do)? I’d be grateful for any feedback.

    • May 31 2018 | 3:33 am
      Oy. Of course, having posted this, I have now figured out the issue: formants. It's not that the [freqshift~] isn't working or that the math is wrong, it's that the chain of [freqshift~] objects is mucking with the formants. The stretching/compressing isn't really what's happening, it's that the sound is seriously altered. When I use a sine wave (I've been using viola samples), the pitch shift is clear as a bell. Seems like the solution is to sum the frequencies before sending them to the [freqshift~] objects, rather than chaining them. Any comments are still welcome.
    • May 31 2018 | 7:48 am
      what are you doing with Hz there? you dont need Hz at all.
    • Jun 01 2018 | 1:02 am
      My education in Max/MSP is full of holes. If you have an alternative approach, I'd love to hear it.
    • Jun 01 2018 | 1:44 am
      Frequency shifting is terrible at maintaining the harmonic relationships present in whatever you put into it. You'll have much better luck using pitch shifting (Yeah, it's actually different from frequency shifting). I don't really know much about calculating shifts in precise semitones, but I do know it's possible since Tom Erbe did it in one of his VSTs.
    • Jun 01 2018 | 2:14 am
      1. i havent looked at your patch and 2. pitch shifting is indeed not possible with the freqshift object.
      besides that i would recommend to run parallel delays here - and not a feedback loop, which makes things extremly complicated, and reduces the quality with every loop.
      you can mimic an echo with a feedback of 50% gain using [tapout~ 100. 200. 300. 400.] ... [*~ 1.] [*~ 0.5] [*~ 0.25] [*~ 0.125] ...
      and then you apply a pitch shifting, spectrum shifting, or filter effect of your choice to each of the outputs. actually really simple!
      alternatively you can combine a tapping buffer delay contruction with a granular pitch shifter, by reading out the tapouts using continiously raising signals, but that involves some weird math for the readout signal and expensive per-output windowing.
    • Jun 01 2018 | 2:22 am
      btw, in your original approach to get frequency (in Hz, which is right for spectrum shifting) number 3 from frequency number 2, it would probably be best to just "undo" the calculation for frequency number 2 and so always start from zero/default/unpitched.
      but hitting chromatic positions doesnt make any sense for spectrum shifting anyway. :)
    • Jun 01 2018 | 2:27 am
      I don't really know much about calculating shifts in precise semitones
      you can at least do something similar in cases where the associated base pitch of the incoming musical event is known, say like you make an effect plug-in with a midi input, so that it can follow the melody.
      to do so you combine spectrum shifting with spectrum rotation.
    • Jun 01 2018 | 1:43 pm
      Noah, you hit it exactly. I think the artifacts introduced by the [freqshift~] object processing timbrally complex sounds are interesting in their own right, but not at all what I'm looking for in this patch.
      Roman, it sounds like we're thinking similarly, even as your understanding is more nuanced than mine. If I'm following you, both in the original patch and in my "fix" of it (following my second post) I ended up applying several of the principles you suggest.
      At this point, having demonstrated that the patch works as designed -- and coming to realize that getting it to do what I want may require much more complex processes than I want to invest the time in right now in order to compensate for the mucked up formants -- I'm going to relegate it to the "good ideas requiring more education and time" bin. In the meantime, I'll take a look at pitch shifting (oh, look! there's a [pitchshift~] object!).
    • Jun 01 2018 | 2:36 pm
      Anything as sophisticated as preserving formants when shifting will no doubt require a trip into [gen~] territory. You might also need to be processing in the frequency domain, so check out [fft~] etc.
    • Jun 03 2018 | 1:41 am
      Omigosh. Clearly I was just using the wrong object. Rookie mistake. Rebuilding with [pitchshift~] produces a much more interesting and desirable result. Thanks all for your help!