Oh my god. I found a PROPER square wave that doesn't click at all, AT ALL. and it allows you to variably change how sharp it is

So I found this example of a square wave algorithm, all props to them.

Lets get right to the demo. here’s me dialling in smoothly between SINE and SQUARE, and ramping up the frequency to ungodly amounts.

Note the variable square_, safely dial this from 1 (sine wave) to about 20 (full square wave)

case WAVE_NOCLIP_SQUARE:
            out = 0.0f;
            for (int n = 1; n <= square_; n++) {
                float harmonicPhase = (2 * n - 1) * phase_;
                if (harmonicPhase > TWOPI_F) {
                    harmonicPhase -= TWOPI_F * ((int)(harmonicPhase / TWOPI_F));
                }
                out += (sinf(harmonicPhase) / (2 * n - 1));
            }
            out *= (4 / PI_F);
            break;         

How would this be added to DaisySP?

I added a few methods to the tremolo class so I just made a copy of the tremolo and oscillator into my project and used a different namespace for them.

This is a horribly slow additive method - but if it works for you that’s cool.

If you want a full blown BL osc, you could look up the discrete summation formula and make a simple band limited sawtooth and then use 2 of those out of phase to create a full bandlimited Square.

Not tried the built in Blep stuff - does it not work or something?

None of those work, they all produce clicks in the signal, this is the only one that doesn’t. Sawtooth is the same. The only waveforms that aren’t unuseable on Daisy at the moment is Sine and Triangle.

Really? OMG - right something wrong - I’ll check them out later!

I’m currently writing a simple BLIT/DSF - I can post it here when finished if anyone wants one?

Here’s the paper on BLITS/DSF - super simple stuff, just create a SINC spike, integrate it and hipass to remove the DC offset.

I thought this was already known, the previous post in this category speaks about it, and @Takumi_Ogata mentions in a video I watched recently that a sudden differential between 0.f and 1.f in signal causes a click so it’s not useable.

The fourier series square wave (my example above) certainly works for the frequency ranges Id need in a tremolo, but I’d love to explore saw tooth etc and have these work. I worry it’s a hardware limitation though?

Here you go - just whipped this up quickly - reaper JSFX code!

it’s a basic BLIT - easy to understand e.t.c.
probably the HI pass is a bit “wonky” at lower freqs
edit made 2 changes to make it more stable, triangle wave and PWM!

desc:Blist Osc

slider1:midinote=40<1,112,1>Midi Note
slider4:pwma=0<0,2>PWM Amount
slider5:pwmf=1<0,16>PWM Freq
slider10:tria=0<0,1>Tri Mix
slider11:sqra=0<0,1>Square Mix

slider20:maxh=0<0,1>Max Harmonics
in_pin:left input
in_pin:right input
out_pin:left output
out_pin:right output

@init

// (C) 2024 S.D.Smith - all rights reserved
// Simple BLIT implementation
// use it as you like!

// V1.05 - LPF all the params that cause popping/cracking
// issues

// V1.04 - added max Harmonics
// pretty awful - really messes up the intergation

// V1.03 - sliders for SQR/TRI mix and PWM amount/freq
// *note* any realtime changes really need to be 
// smoothed/low pass filtered else the integration
// will mess up, it will eventually recover thx to the
// HP filters

// V1.02 - Added Triangle and better coeffs

// V1.01 - Added square

// V1.00 - basic BLIT

// T = period time 0-1
// H = Integer Harmonic Count 1 to whatever

// This aliases above 6k -  probably
// got the pi wrap wrong or the integration messy 
// and inaccurate.
// Turns out my DSF math was totally wrong - doh!
// 100% Alias free now

function BLSincSpike(t,h)
(
t=t-floor(t);
p= (amaxh)*srate / freq;
p<2? p=2;
m = 2 * floor( p * .5 ) + 1;

(m/p) * sin($pi*t*m) / (m*sin( $pi*t ))*(amaxh+1);
);

@sample
  // Midi note to actual freq
  tfreq=(440/32)*pow(2,(midinote-9)/12);
  // LPF the main freq as well
  freq+=(tfreq-freq)*0.001;
  // Update freq
  t+=freq/srate;
  t=t-floor(t);
  
  // Use the same coeff for leaking on the HP damping
  // much more stable on low freq saws
  // change 2 to 8 or more for more "Square" 
  // square waves and betterer saws
  // at the expense of DC jitter (in-audible)
  DCLeak=tan(freq/(2*$pi*srate));

  // LPF these two - cause lots of problems 
  // with fast changes and the integrator  
 amaxh+=(maxh-amaxh)*0.001;
 apwma+=(pwma-apwma)*0.001;
 atria+=(tria-atria)*0.001;
 asqra+=(sqra-asqra)*0.001; 
  
  // some sorry ass PWM for square wave
  // Saw = vibrato since it's changing phase
  // Square = PWM
  // Tri = Tri->Saw morph (this sounds like
  // the reece bass on Usual suspects - holepunch!!!)
  


  Duty=cos(pwmtime*2*$pi)*apwma+0.5;
  pwmtime+=pwmf/srate;

  // Create first sawtooth
  // Sinc Spike
  bl+=BLSincSpike(t+duty,((srate/(2*freq))&0xffff));
  // Leaky Integrate
  bl*=1-DCLeak;
  // Remove DC with hi pass
  lp+=(bl-lp)*DCLeak;
  out=bl-lp; 
  // Create Second sawtooth
  bl1+=-BLSincSpike(t,(srate/(2*freq))&0xffff);
  bl1*=1-DCLeak;
  // Remove DC with hi pass
  lp1+=(bl1-lp1)*DCLeak;
  // Add to first saw tooth for a square
  out+=asqra*(bl1-lp1);  

  // Integrate to get a triangle
  // it's a Pretty sloppy triangle tho 
  // totally unstable at low freqs
  // and fast freq changes!
  tri+=out;
  tri*=1-DCLeak;
  out+=atria*tri*0.5;
  out*=0.5;
  
spl0 = out;
spl1 = out;

But I ran it thru my scope and spectrum and it looks okish!

Have fun!

1 Like

It’s great that you found an additive solution that works well for your use case, but saying the oscillator waveforms are “not usable” is a bit exaggerated… maybe some of the waveforms aren’t usable out-of-the box for specific modulation use cases (like tremolo/AM) without causing clicks, but the bandlimited oscillator waveforms are perfectly fine for VCOs and the non-bandlimited ones are still fine for plenty of things.

The output of a lot of square/saw LFOs in modular synths are also more or less “instantaneous” in terms of rise or fall at the edges and people get on just fine with those for all sorts of types of modulation.

If you wanted to make your additive square more efficient you could render one cycle of it into a lookup table and use a phasor to do an interpolated lookup into the table and achieve virtually the same result without all the expensive sin() calls, assuming a decently sized LUT.

I’m glad you said this because I what I meant to say was that it wasn’t useful to me in this context. Thanks for the clarification and suggestions.

if the internal OSC code uses any kind of Integrator - any fast parameter changes will explode the held value and result in pops/crackles. Check out my code above - I had to wrap most of the params and LPF filter them with a arbitrary value to stop any of that kind of funky stuff. Maybe that’s solution for the Daisy OSC code - just LPF any parameters - it’s very simple to code e.t.c.

 // LPF these two - cause lots of problems 
  // with fast changes and the integrator  
 amaxh+=(maxh-amaxh)*0.001;
 apwma+=(pwma-apwma)*0.001;
 atria+=(tria-atria)*0.001;
 asqra+=(sqra-asqra)*0.001;