A "rounded" square waveform?

Hi folks. I’ve been working on a tremolo effect and I’d like to use a square wave without the nasty ticks and clicks that Oscillator::WAVE_SQUARE and Oscillator::WAVE_POLYBLEP_SQUARE produces at extreme “depths”. I want to just slightly round off the sharp corners of the square wave, (i.e., temporal filtering, Bessel or Gaussian filters, etc.)

Here’s a bit of MATLAB code that demonstrates, where A=amplitude, f=freq, and delta sets the amount of “rounding”:

t = linspace(0, 2*pi, 500);
A = 1;
f = 1/(2*pi);
deltas = [0.1, 0.01, 0.001];
colors = {'r-', 'g-', 'b-'};
figure; hold on;

for i = 1:length(deltas)
    delta = deltas(i);
    y = (A/atan(1/delta))*atan(sin(2*pi*t*f)/delta);
    plot(t, y, colors{i}, 'LineWidth', 2);

legend('Delta 0.1', 'Delta 0.01', 'Delta 0.001', 'Location', 'SouthOutSide');
axis tight; axis square;
set(gca, 'FontSize', 15); grid on; box on;

Here it is plotted with various delta values:

So, my questions:

  1. Am I over-thinking this and there’s an easy way to solve this problem in the DaisySP library? (This would be classic me.)
  2. I suppose it’s easy enough to write my own oscillator to achieve this; is that the best/easiest option?
  3. Would such a waveform be handy enough to add to the DaisySP Oscillator implementation?

Thanks in advance!

Well, if anyone is playing along at home, I simply extended the Oscillator class with my own ExtendedOscillator. I added WAVE_SQUARE_ROUNDED to the enum and added this case to port my MATLAB code:

            delta = 0.07;  // Seems to handle discontinuities nicely @ 48kHz
            out = (amp_ / atanf(1.0f / delta)) *
                  atanf(sinf(2.0f * PI_F * phase_) / delta);

This works a treat for a tremolo effect. No clicking or ticking and it still sounds like a square wave to my ears. It also sounds fantastic in harmonic tremolo mode with the cutoff frequencies of the high and low bands somewhat wide (e.g., 1500Hz and 600Hz, respectively.)

Here’s the waveform in MATLAB (or Octave if you’re so inclined):

t = linspace(0, 2*pi, 500);
A = 1;
f = 1/(2*pi);
delta = 0.07;
color = 'r-';
y = (A/atan(1/delta))*atan(sin(2*pi*t*f)/delta);
plot(t, y, color, 'LineWidth', 2);
axis tight; axis square;
set(gca, 'FontSize', 15); grid on; box on;

Which produces:

If you want to experiment with different levels of “rounding”, change the value of delta. Lower values = less rounding. 0.07 works well for my application.


Edit: fixed a typo in the MATLAB code.


Seems to me this should be added to DaisySP, why not submit a PR? Maybe even use pw_ to set delta?

1 Like

Do you have PWM on this waveform?

I don’t know much about DSP, but isn’t that the same as square wave followed by a low-pass filter?

1 Like

@tele_player Possibly so; I’ll submit and see what happens. On the other hand, it’s fairly easy for any hacker to implement and seems sorta niche, maybe? Let’s see.

@dreamer I ‘evolved’ the brainpower here and went no further: smoothing - Generating smoothed versions of square wave, triangular, etc - Signal Processing Stack Exchange

@KnightHill I’m not so sure. As far as I know (see above - I’m about a month in to this DSP thing), there are many types of low pass filter. For example, while doinking with this, I found that a single-pole LPF basically results in the familiar “shark fin headed west” wave form. No other LPFs that I tried yielded what I was looking for. This basic trigonometry gave the desired results, but there may very well be other / better ways to make the magic happen.

Happy to be here and learn from all y’all!

1 Like

It’s probably more accurate to call it a distorted sine wave than filtered square wave — although, since it’s all odd harmonics, there is some filter you could apply to a square wave the yields this shape.

DaisySP implements a “bandlimited” square oscillators — an approximation of a square wave by finitely many harmonics — whose waveform might not look quite as similar in shape to a classic square, but sounds very similar.

1 Like

That is my understanding as well. It’s a sine wave shaped by the arctangent function.

The WAVE_POLYBLEP_SQUARE waveform in DaisySP does not work for my application: it ticks and clicks like mad and I found this solution before I found a filter that yielded anything close to this shape.

Just a quick follow-up re: filters after a square wave. Clearly not an exhaustive list of options, but after spelunking in a few source texts, I just resorted to the distorted sine wave approach.

1 Like

Have you tested these for aliasing? usually above 1khz you’ll start to see any issues. If they do alias - it’s probably ok to use them as LFO’s but not raw OSC’s as long as they don’t push ur src OSC’s> -60db harmonics into reflection et…c.

1 Like

Good point. The waveform suggested early in the thread — and then what was implemented — is intended for tremolo applications below 18Hz or so. Definitely LFO territory.

yeah I had a quick bash with it in reaper - it aliases around 1200hz, you could oversample to get rid of that if you wanted - but it’s fine for LFO. Why didn’t you use tanh instead of atan - tanh is perfectly suited since it’s range is -1 1?

Here’s the reaper jsfx code - just fire up before a EQ or FFT after it - both have a spectrum display.

desc:Signal Generator


in_pin:left input
in_pin:right input
out_pin:left output
out_pin:right output

t>1? t-=1;

delta = 0.07; // Seems to handle discontinuities nicely @ 48kHz
out = (1.0 / atan(1.0 / delta)) * atan(sin(2.0 * $pi * t) / delta);

spl0 = out;
spl1 = out;

1 Like

Ah, that’s a cool tip. I’ve used Reaper for years, but haven’t delved much into JSFX. Thanks for adding that tool to the box!

Re: atan v. tanh:

Honestly, no reason other than the delta value felt more intuitive to me at the time. One could use either, but I got anchored on the atan approach by some interwebs fodder I found (linked above.)

If you’re doing R&D or just tinkering in DSP - reaper JSFX is the only thing to use, it will save you a ton of time - it’s soo fast in execution and instant compilation.

1 Like

Amazing thanks! Square wave was the first thing I tried on the Daisy seed

1 Like