Issues building a pitched delay

dnealelo's icon

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.

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

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.

dnealelo's icon

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.

Roman Thilenius's icon

what are you doing with Hz there? you dont need Hz at all.

dnealelo's icon

My education in Max/MSP is full of holes. If you have an alternative approach, I'd love to hear it.

Noah's icon

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.

Roman Thilenius's icon

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.

-110

Roman Thilenius's icon

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. :)

Roman Thilenius's icon

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.

dnealelo's icon

Thanks!

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!).

Robin Parmar's icon

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.

dnealelo's icon

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!