Syncing Tri~ Saw~ Rect~
I’m building a subtractive synth. Each ‘oscillator’ has an adjustable waveform and I’m doing this by cross-fading between saw~ tri~ rect~ signals, with these objects all being driven by the same frequency signal. I have some questions about doing this:
(1) Is it possible that saw~ tri~ and rect~ could drift out of phase, even if they are driven by the same frequency signal, as in the left-hand arrangement in the attached patch?
(2) If this is possible that they could drift out of phase, they can be kept in phase using the arrangement in the right hand part of the attached patch. As far as I can see, this has no adverse affect on the output waveform but am I correct? Is this a good way to keep them in phase?
for best results I think you’d need to use cycle~ with pre-buffered waveforms, and then use the phasor to drive the phase of the cycle~ directly, otherwise you can risk getting discontinuities with the signal (if you have the hard sync setup as in version 2 of your patch)
hope that makes sense
Your reply does make sense to me, and the discontinuities to which you refer are my worry. Having said that, I’ve recorded the output and zoomed in a lot on the waveform and it looks fine and indistinguishable from the non phasor~-synced version (sounds fine too). I would think that there’d have to be quite a problem for the phasor~ and saw~ (or tri~ or rect~) to go that far out over the course of just one cycle that a discontinuity would arise, no?
Using your cycle~ solution, I’d lose the nice anti-aliasing, right?
yep, the potential for aliasing is an issue– but if you are using the waveforms directly for audio (not as control signals), I can’t really see how any phase drift would matter. So I wouldn’t worry about the syncing between waveforms, and build the patch you want. I suspect any phase drift (if it is possible) would only occur over a very long time span… but someone else with more experience might have more salient advice…
Phase drift does matter when you’re mixing waveforms for audio (rather than control). Try the attached example, using the -1 and +1 messages to flip the phase of the saw~ signal. Of course it doesn’t matter if the saw~ signal is on its own, but when mixed with the signal from rect~, it makes a huge difference. This means that if the different waveform generators drift out of phase, then the synth could sound different after you’ve left it on for a while! Of course 180 degrees is the most extreme phase shift, but any shift definitely matters.
I keep thinking there must be a "standard" Max way to keep the various anti-aliased waveform generator objects sync’d in phase, unless it’s absolutely impossible (or would take a very long time), for the waveform generators to drift out when they’re being controlled by the same frequency signal.
Might a Cycling 74 person comment on this?
OK, of course! A stark example– if it’s simple ‘pure’ contrasting (like square and sawtooth, which are the most extreme) waveforms of the _same_ frequency, then wow, very different.
I think the only way to guarantee no phase drift _and_ no risk of discontinuity, is to drive all oscillators from the same phasor– and to do that you need the cycle~ referencing a buffer (which then brings aliasing issues). Someone correct me if I’m wrong.
You may be right. It would be good to have a definitive answer though. The anti-aliased objects are much more convenient than using a wavetable.
I have arrived to give you your definitive answer >:D
"I keep thinking there must be a "standard" Max way to keep the various anti-aliased waveform generator objects sync’d in phase"
You have achieved the ‘standard’ Max way to keep these three particular anti-aliased waveform generator objects sync’d in phase in your first original patch on this thread, under solution #2(traditionally, a rectangular waveform is not thought of in terms of phase, but rather duty cycle anyways… but i digress). In that solution, they do not drift out of phase over time, they simply reset to phase 0 at the midpoint of phasor~’s ramp and then traverse the independent phase trajectories of their particular waveforms(if you have the same frequency going into them via signal at the same time, they will traverse their independent waveforms at the same rate, and so it can be said that they are all still synced in phase). The only problem is what Terry wrote originally about discontinuities. These discontinuities can happen when you change frequency because the phasor~ will tell those objects to reset their oscillators somewhere in the middle of their previous waveform-cycles(whose traversal began before the frequency change). The phase input on cycle~ allows you to drive the phase directly(rather than simply resetting it), thus allowing you to be more exact and transpose the timbre of any waveform more specifically/naturally(if such words can even be used without subjectivity in a digital environment with limited floating-point accuracy :p).
If you’re using Max6 the new cycle~ allows for as long a wavetable as you’d like(use the ‘setall’ message to set it to the entire length of any buffer~ you choose). Using a lengthy wavetable for cycle~ can drastically reduce the effects of anti-aliasing(hence, the increase of cycle~’s default internal buffer from just 512 samps before Max6 to a good-sized 16K samps now).
These three anti-aliased generator objects you’re using allow for a different kind of hard-synced timbral control(one which revolves more around the onset of a duty cycle rather than finite points of phase). Even the points of discontinuity introduced by frequency changes will happen in all three at once(proving they are hard-synced in phase) with your solution #2(you certainly don’t need to worry about drift over long periods of time in that solution).
For aliased oscillators, I tried this. The idea was, s&h causes the source phasor frequency to change during the ramp transition to zero. there was some improvement, but the history module is necessary because there is a feedback loop, and it delays the transition one sample after the end of the ramp. So there was still a bit of click. But not as much.
I thought maybe there could be two phasors the same frequency offset by one sample period, one to trigger the s&h and one for output, which could elimintate the discontinuity entirely, but it adds CPU overhead.
And my last thought on the topic is, the other way to do that in gen would be to count samples. However, if the wavelength is not an integer multiple of the sample period, the down transition of the phasor shifts between two cycles. I haven’t inspected how gen handles that. there’s two ways. One is to keep the down transition exactly 1. and shift it to the nearest sample, which causes single-sample frequency variation and some audible distortion. So more commonly for audio, the down transition starts before the ramp reaches its peak value of 1. and there is a tiny amplitude variation over time, which is less audible.
the signal path leading from phasor to ‘out 2′ is how i’d create a rectangular waveform(i don’t need my square-waves anti-aliased, i love the irregularities introduced at higher frequencies :p).
The phase-control signals in this clever gen~ object by Ernest remap to a range between -1 and 1 (rather than phasor~’s original 0 and 1), so just a heads up to aengus, if he uses this, to use the ‘synctrig’ message to rect~ and saw~ to change where the reset happens(see helpfiles and reference docs).
Unfortunately, i notice the tri~ object does not have a ‘synctrig’ message… someone should probably send a feature request to Cycling74 ;D
Glad it’s helpful. A triangle is possible much the same way, by multiplying the phasor output by 2, then subtracting it from 1 when the phasor output > .5. That yields a triangle in the range 0.~1, and of course it syncs with the other waveforms as they’re all from the same ramp generator.
I mostly agree about the aliasing irregularities, but I still do want antialiasing with Nyquist. Have people tried oversampling by putting the gen~ object inside po;y~ with the built-in Hamming window on the output? Or sinc? I think that could be a real improvement for basic waveforms over C7, and alot eaiser than building it from scratch:)
Using a lengthy wavetable for cycle~ can drastically reduce the effects of anti-aliasing(hence, the increase of cycle~'s default internal buffer from just 512 samps before Max6 to a good-sized 16K samps now).
A longer wavetable will reduce interpolation noise, but it will not reduce aliasing. Essentially, aliasing arises when you skip through the wavetable too fast, so increasing the length doesn't help.
The only problem is what Terry wrote originally about discontinuities. These discontinuities can happen when you change frequency because the phasor~ will tell those objects to reset their oscillators somewhere in the middle of their previous waveform-cycles(whose traversal began before the frequency change).
I am not sure the phasor~ arrangement I had in the first patch I posted behaves as you suggest when the frequency changes (unless I misunderstand what you're saying). I've attached a patch that randomly changes the frequency of a phasor~ and the saw~ that is acting as its sync-trigger. If you record a bit of this and examine the points the frequency changes (I just zoomed in using Audacity), there are no discontinuities in the waveform. You get discontinuities in the slope of the waveform (as in the attached image), but you never get the phase of the waveform being abruptly reset to 0.
Unless, I'm missing something…?
That looks right:) A waveform patch as described above is also now in freeze, here: