Upsample to reduce aliasing using only Gen, not poly~ object.


    Oct 27 2014 | 12:38 pm
    Hi,
    I have a triangle wave with variable slope. It is currently not bandlimited. I know I can use poly~ like in the attached example but I want a simple explanation, or better still please edit this patch, of how I can upsample, filter to Nyquist and downsample all in Gen.
    The link below describes a Pure Data patch which does 16x upsampling. How can I achieve this in Gen?
    Any help is appreciated.
    Dave

    • Oct 27 2014 | 4:57 pm
      hi.
      i have bad news for you.
      there is no simple resampling in gen~. you have to roll your own. simple resampling is a known feature request for gen~ and hopefully on their agenda.
      i made a sinc resampling system for arbitrary up/down in gen~ but it was quite useless as it took way too much cpu.
      the simplest is the cheap trick interpolation method - repeat the processing x # of times, then high-quality interpolate between them. this is quite easy in gen~ for 4x because of the [history] and [interp] operators, and is the same method used often throughout reaktor-core.
      however, a couple of points: - usually one would resample based on the code at hand - i.e., not brute force but various methods whether filters or oscillators or waveshaping, etc - the well known pd examples you posted are not really resampling. they are even worse than pre-max-6 days. in fact they are so terrible it is sort of embarrassing they form a tutorial. an x-pole butterworth lowpass at the end of your x-times chain for a naive oscillator is never really going to do much good. - a lot of those pd people and those finish guys and the likes of lazarini et al often work with 'alias suppression' for oscillators, but you got to ask yourself, why work with shit which needs 'suppression' in the first place? that is why people used to do 'blit' and now do 'blep' and 'blamp'.
      so, none of that is supposed to be disheartening (or rude!). just my opinions, please ignore if you wish.
      however, you'd be better off using the gen~ BLIT examples in the examples folder to make triangles etc, which is actually relatively simple. there was a post about it a while back.
      for the record, yes it would be fantastic to have arbitrary resampling system inside gen~, and i too hope for it one day.
      .
    • Oct 28 2014 | 1:42 pm
      Hi STKR,
      Thanks for replying.
      however, you’d be better off using the gen~ BLIT examples in the examples folder to make triangles etc, which is actually relatively simple. there was a post about it a while back.
      I wasn't able to find a way to adapt the BLIT saw example into a triangle. In Digital Sound Generation: http://courses.cs.washington.edu/courses/cse490s/11au/Readings/Digital_Sound_Generation_1.pdf they have a diagram for DSF-BLIT Sawtooth Oscillator but no Triangle. I also couldn't find an example online or work out how to adapt the Gen codebox code for the saw. There are, however many other Triangle algorithms in the same document but I was unable to implement any of them in Gen. I'll make a separate forum question tonight explaining this.
      EDIT: I made a triangle wave by using a leaky integrator on the end of my PTR Square wave. Happy days
      the simplest is the cheap trick interpolation method – repeat the processing x # of times, then high-quality interpolate between them. this is quite easy in gen~ for 4x because of the [history] and [interp] operators, and is the same method used often throughout reaktor-core.
      I tried to use the History object and Interp to produce less aliasing but it doesn't work. Unless I have completely misunderstood what you meant, it should be possible to reduce aliasing using your suggestion? I realise that a better solution is to use another method for my triangle oscillator but I'd like to know how (if possible) to reduce aliasing by Interpolation. Perhaps I've completely got the wrong idea!! Anyway, now I made my bandlimited triangle I don't care but by all means examine my patch if you wish.
      Cheers
      Dave
    • Oct 29 2014 | 11:51 am
      hi. no time to look at your stuff now, sorry. hopefully these help.
      attached is a dsf-blit multiwave example. pasted below is a resampling example.
      hth.
    • Oct 29 2014 | 11:53 am
      That's awesome. Thanks very much
    • Oct 30 2014 | 7:53 am
      Hi Raja,
      Yes, I settled for a triangle derived from a PTR square I made. Very low cpu usage. I was still interested in how the upsampling would work however.
    • Oct 30 2014 | 12:56 pm
      I'd be interested in looking at this triangle and square wave you made, as the saw of the other post is really great...
    • Oct 30 2014 | 1:18 pm
      @BERTRANDFRAYSSE Sure. No problem. Here you go. I'll also share this as a tool.
      The duty cycle messes with the amplitude of the triangle (partially solved in my patch) and moves the square but these problems should be easily solved.
      What would be really fantastic would be if somebody could take the saw and square here and adapt them from PTR to EPTR. That would be great. SADGUITARIUS provided some C code but I haven't tried to implement it yet. Then the oscillators would be even more efficient. Would be a nice Christmas present perhaps from somebody in the know to the rest of us:
      Enjoy :-)
    • Oct 31 2014 | 12:56 pm
      That's some nice coding work, thank you. I did implement 3x upsampling in gen~ objects for an SVF filter, if you're interested, the link to it is here
    • Oct 31 2014 | 1:00 pm
      Hi Ernest,
      No problem!
      I'll take a look at your link. Thanks very much
      Dave
    • Nov 08 2014 | 10:14 pm
      The ICST library has a triangle oscillator that is simpler and just replaces the transition with a polynomial. It's more efficient than most methods, IIRC.
    • Nov 12 2014 | 8:22 am
      Thank you Peter, very helpful. I think I have a better idea for upsampling a ramp signal source for oscillators. Spline interpolation tries to guess the signal shape but introduces a discontinuity for a ramp causing jaggies in sines and other nasties. So I built this, which simply interpolates values linearly while the ramp is rising, and, iff the ramp ended in the last cycle, interpolates the slope from the prior sample and applies wrapping on each upsample phase so the ramp downslope occurs in the optimal place. Maybe there is a better way to do things, I am still learning.
    • Nov 12 2014 | 12:58 pm
      Hi Ernest, this looks somewhat like the PTR algorithm; you might check out the paper for that if you haven't already. It's similar: the wave is unaffected through most of the ramp, and the interpolation switches on at the discontinuity. HTH
    • Nov 12 2014 | 1:00 pm
      Hi Peter,
      Thanks for the info.
      Dave
    • Nov 12 2014 | 2:05 pm
      Well, after fixing it, I was still unimpressed by the quality of the output compared to your oscillators. After downsampling with both spline and sinc+blackman window, even with 16x oversampling, there was only a slight reduction in noise at higher frequencies. I think both Davey and Peter have a better existing approach than with upsampling, unless Im missing something, but I had read before that was about as much as one can expect unless one does no downsampling.
    • Nov 12 2014 | 2:45 pm
      Sawtooth wave harmonics fall off at 1/f in terms of amplitude, so an oversampling+downsampling approach usually doesn't work well because there are just so many partials.
      I definitely recommend the ICST "Digital Sound Generation" pdfs because they explain a lot of these issues and also provide suggestions as to which algorithms are appropriate for particular conditions. For example, the triangle wave I mentioned is good if you need an efficient triangle, but it can't do things like the variable slope present in tri~'s middle inlet.
    • Nov 13 2014 | 3:29 am
      Well I was still expecting better results, but after sleeping on it, I realized this is not really upsamplikng per se. It is running four oscillators at a slight offset from each other at the same frequency, and then mixing the results to emulate upsampling. The oscillators are still running at the same frequency, not higher, so the Nyquist is exactly the same, repeated four times then mixed down. Thus a little noise reduction is exactly what should be expected. Upsampling by this method doesn't really change the aliasing at all.
    • Nov 13 2014 | 3:37 am
      Maybe the approach for varying triangle/saw is to run two ramp oscillators. Suppose the 'triangle' has a 75% rising and 25% falling slope. then could two antialiased saws make it by running one at 4x frequency and one at 1/.75 frequency with inverted phase, and switching between them at ramp boundaries?
    • Nov 15 2014 | 1:37 am
      Well I have to agree, on investigation, blep fidlling seems far more computationally intensive and difficult to get right than PTR and EPTR. There is a paper on an EPTR algorithm here which does describe an EPTR method for a varying triangle waveform. The description is clear enough that a low-level mathematician like me can still understand it...so I will try it out.
    • Nov 15 2014 | 2:40 pm
      @ERNEST If you manage to implement EPTR in Gen, please share your results. I tried but failed, see:
      Cheers
      Dave
    • Nov 15 2014 | 8:31 pm
      But of course. It is worth considering that maybe EPTR doesn't work as well as touted, after all, oversampling by duplication doesn't help with Nyquist and a lot of people do it anyway. Thinking about it, it seems like it is only selective application of a low-pass filter to the problematic portion of the wave, so I don't expect as good results as for BLEPs, but it is certainly, by first appearance, a lot easier to do.
    • Nov 15 2014 | 11:21 pm
      First I made the antialiased saw with PTR, attached, and right away I can tell you there is a mistake at the beginning of this paper which may explain your problems. The equation for samples before the discontinuity
      y[n] = p0 - p0/T + 1/T -1
      should be
      y[n] = p0 - p0*T + T -1
      As obviously p0/T when p0>0 is much greater than 1. However, please let me add, I am not really a mathematician, I only have a lot of experience with audio. As a corolarry, I do make the observation, when p0 < 1 (that is the half way point between two samples when the next sample is the saw discontinuity is before the discontinuity), an approximation of 0 is still pretty good, which halves the calculations. Anyway I do see the PTR saw seems to work very well, here is the first maxpat.
      I made the same mistake with calculating the intermediate sample as the first time with downsampling, AND it is simpler to subtract delta from the ramp than add half to history +delta to the multiplied value, AND I removed another subtraction and multiplication from the genexpr by making use of the phasor already providing values in the range 0-1 which are half those of the output. New maxpat attached.
    • Nov 16 2014 | 1:03 am
      Now for the EPTR algorithm described in this paper, I don't see it providing significant enhancement over my second maxpat. This is because it requires no wrapping on the phasor signal, but to let the accumulator free run. I think This is impractical because the increment is small. Hence after time the accumulator value will be very large and even with 64-bit floating-point, the increment value will be too small to appear in the output.
      One possibility would be to accumulate freq rather than freq/samplerate, then divide by samplerate afterwards. Even with that, after a time the increment wouldn't show up in they floating point value output by the accumulator, but maybe the time before that error would be so long it doesn't matter, I'm not sure.
    • Nov 16 2014 | 6:12 am
      Finally I did a test with the saw PTR quotients in this article against simply substituting the top and bottom transition points with 1 and -1. It's several times more efficient, and the I can't see any discriminable difference in the results. Maybe I set the quotients wrong. Anyway here it is, as per request.
    • Nov 19 2014 | 6:39 pm
      Hi Ernest.
      Thanks very much. On holiday without my machine right now but will download when I get back in about a week.
      All the best
      Dave
    • Nov 20 2014 | 6:36 pm
      Hope you have a great vacation. I tidied it up quite a bit, added FM and sync, and a Yamaha-style multisine oscillator. I think it wouldn't be a big step to add full polynomials and antialias higher octaves too, as the transition points are all isolated. I'll try to make an EPTR version of PM's PTR pulse.
    • Nov 22 2014 | 4:50 pm
      And I am very pleased with the results! it yields an astonishingly pure tone, as you can see from the attached illustration, and provides effective antialiasing up to C7. It sounds much more like an analog oscillator than anything I have heard in a while, and at first I thought it a little thin, and then realized how accustomed I had become to alias distortion. So first, here is the code.
      History z0; History z1; History z2; History z3; History z4;
      fc   = in1; 		// freq (Hz)
      inc  = in2;			// phase increment(including FM), Fc/2*sr
      sync = in3; 		// resets phase on positive transitions
      w1   = .5 * in4; 	// Duty cycle. 0.025 = 5%, and 0.5 = 95%.
      w2   = (1-in4 *.5);	// inverted duty cycle 
      sr2 = in5;			// 2/SR constant (restart required to change sr).
      
      // The phase accumulator works in the range -1~+1,
      // to prevent phase inversion by negative wraps from FM signals
      
      ramp = (sync)? 0: wrap(inc + z0,-1,1); 
      z0 = ramp; 
      
      // In case FM present, a new incr is interpolated from phase history
      
      z1=inc; z2=z1; z3=z2; z4=z3;			
      inc2 = interp(inc,z1,z2,z3,z4,mode="spline");
      
      /* --- Pulse with EPTR antialiasing --- */
      
      ramp2 = ramp *.5 + .5;	// ramp rescaled to 0-1 for EPTR calcs
      d =  2 * inc2; 		// width of phase transition region, 4*fc/sr
      d2 = 0.213332 / d;	// increment factor in transtiion region
      
      // the Pulse is divided into five stages. Antialiasing calucations
      //only occur during the fisign and falling transionts, resulting in
      // extremely low CPU usage. 
      
      if (ramp2 
      The only thing I am really not sure about is how interpolation works in the spline object. So I will do some checking on it, and upload the demo patch to Yofiel.
    • Nov 22 2014 | 5:10 pm
      Great work Ernest. Looking forward to checking this out.
    • Nov 24 2014 | 12:01 pm
    • Nov 27 2014 | 4:37 pm
      I think I finished the documentation today, and on discussion with some people in musicdsp.org I think I have an additional enhancement for FM sync. Currently there is still a bit of a problem if sync falls during the transition phase.
      Over there, they call this a 'polyblep' and some people have done similar things, but not as far as I can tell using the sin/tanh shaping like Peter shared, which appears to be much better for FM. Hackles rose a bit when I said there was good precedent for this to be called 'EPTR,' so you are warned.
      Regardless the sync issue, I am totally delighted with this design, I never thought I would be able to do it. Thank you Davey and Peter for your help, and happy thanksgiving.
    • Nov 27 2014 | 7:44 pm
      No worries Ernest. I downloaded the patches from your site. Looks good.
      Happy thanksgiving to you too.
    • Nov 27 2014 | 7:57 pm
      oh, well I added a lot of explanation. Also I didn't see this before....there is a help patch for plot~, on the spectral tab, that gives much better frequency analysis )
    • Nov 27 2014 | 11:29 pm
      Hi Ernest.
      Yes, I see that you've been busy. It looks like a good read but it's near 2am here and I'm currently struggling with an unrelated issue. --> just in case you know the answer ;-) .......... Playing back a wav in Gen without using Buffer (using Data only - https://cycling74.com/forums/buffer-and-data-dont-sound-the-same/)
      Anyway, well done again and I'll be sure to have a closer look at your website asap.
      Cheers
      Dave
    • Jan 21 2015 | 6:18 pm
      Looks like you are having fun with alot of other bits. I think you also said you wanted a wavetable oscillator, well I got a waveset oscillator working in gen~ which you may find helpful:
      I cant remember whether it was wabetable or waveset, but the same method pretty much works for both
    • Jan 21 2015 | 6:47 pm
      Hello Ernest! Nice to hear from you.
      I'll check that out. I'm very interested.
      All the best, Dave