Aliasing Oscillators

Hi there,

so I am using the oscillator class, it contains raw and anti-aliased oscillators.

But the aliasing is horrible.

What is the deal here?

At 96khz with an anti-aliased oscillator, we should have pristine quality,
but in higher pitches, the aliasing is louder than the actual osc signal.

Are the daisySP oscillators just that bad?

(btw I tried the BlOsc, as well as copying the Master Osc from Reaktor, all bad…could this be a hardware issue?)

How are you observing aliasing? Scope? Can you provide a code example for me to try?

I can hear it while I perform a pitch sweep.

I managed to copy the Reaktor Osc successfully, with no aliasing in a clean, simple project.

I am now trying use this as a library oscillator in my synth project.

Now it aliases again.

I think I am doing something wrong, using the oscillator class in my project.

But what could it be?

Another weird thing is that the Reaktor Osc has stepped pitch.

Here it is:

    freq = (lfo.Process() * 18000 ) + 9000;
    inc = (1 / sample_rate) * freq;
    if (inc > 1) {inc = 1;}
    if (inc < 0.00000000002f) {inc = 0.00000000002f;}
    
    phase += inc;
    if (phase > 1) {phase = -1;}
    
    lil = inc;
    lili = 1 - lil;
    
    if (phase > lili)
    {
        saw = (lil - phase) / lil;

        saw3 = saw * fabs(saw) + phase; // Applying rectification
    }
    else if (phase < -1 * lili)
    {
        saw = (lili - phase) / lil;

        saw3 = saw * fabs(saw) + phase; // Applying rectification
    }
    else
    {
        saw3 = phase;
    } 

    
    
    
    for (size_t i = 0; i < size; i += 2)
    {

        out[i] = saw3;
        out[i + 1] = out[i];
    }

this is how I replaced the saw osc in the oscillator class with my reaktor copy:

switch(waveform_)
    {
        case WAVE_SIN: out = sinf(phase_ * TWOPI_F); break;
        case WAVE_RicoSaw:

    
   
    inc = (1 / sr_) * freq_;
    if (inc > 1) {inc = 1;}
    if (inc < 0.00000000002f) {inc = 0.00000000002f;}
    
    phase += inc;
    if (phase > 1) {phase = -1;}
    
    lil = inc;
    lili = 1 - lil;
    
    if (phase > lili)
    {
        saw = (lil - phase) / lil;

        saw3 = saw * fabsf(saw) + phase; // Applying rectification
    }
    else if (phase < -1 * lili)
    {
        saw = (lili - phase) / lil;

        saw3 = saw * fabsf(saw) + phase; // Applying rectification
    }
    else
    {
        saw3 = phase;
    }
        out = saw3;    break;

            break;
            

and here is the end of my signal chain in my synth project,
I can see here that I do stuff in the for-loop, which seems to be wrong.

for(size_t i = 0; i < size; i += 2)
    {
         envout = env.Process(trig && gate_);
        
        sig = (osc.Process() + osc2.Process())*(1-lfoAmp*lfoAmp)*envout*.5;

filt.Process(sig);

    if(Filterchoice==0)
        {del.Write(filt.Low() + dFilt.Band());}
    else {del.Write(filtM.Process(sig) + dFilt.Band());}
        
        float fbFin = (fb / 24 + fbMod + fb2Mod + fb3Mod + fb4Mod);
        if(fbFin>.99){fbFin=.99;}
        if(fbFin<0){fbFin=0;}
        dFilt.Process(del.Read() * fbFin);

        // left out
    if(Filterchoice==0){
        out[i] = (dFilt.Band() * (dlv / 24 + dlvMod) + filt.Low());}
    else {out[i] = (dFilt.Band() * (dlv / 24 + dlvMod) + filtM.Process(sig));}
        // right out
        out[i + 1] = out[i];
    }

I meant - code that I could just compile and run to hear the problem.

From the original post, it seemed you were suggesting inherent aliasing in the Daisy hardware and/or libs. I’ve been tinkering with Daisy for years, and haven’t heard complaints like that, so I expect there is user error involved.

I just made a clean compile of the daisySP antialiased saw and it was super clean.

So the mistake is on my side. But what could it be?

Maybe the Oled? I have it running with a 470uf cap which should be fine.

My sine osc is not aliasing, but it should.

I am a bit out of ideas.

Do you have any idea?

I am running the az-delivery 128x64 oled, 1 sr 595, 4 multiplexers, 2 mpr121, 1 sparkfun rgb encoder.

Might I be missing decoupling caps? I have none of these on my breadboard, they never changed anything.

taking the signal processing out of the for-loop did not help unfortunately.

I doubt it’s about coupling caps. I’d just start eliminating code until the sound changes.

Try this instead of i += 2 ?

for (size_t i = 0; i < size; i++)

That might be correct, or not, depending on if the Callback is is Interleaved or not.

patch.StartAudio(AudioCallback);

vs

patch.StartAudio(InterleavingAudioCallback);

Curious, when would InterleavingAudioCallback be used instead of AudioCallback?

my idea of this is (my project is based on the oscillator example) that we count i+=2 because the left channel is i and the right channel is i+1.

but if the culprit was that each second sample is empty, my Sine would also alias, but it doesn’t.

unfortunately, yesterday I eliminated all my peripheral code and only used out = osc.process
and an lfo sweeping the osc frequency. still aliasing.

anyway, I will dig further.

I found out that my mod-matrix is the culprit.

I already observed that there is a very high pitch in my sound.
Especially when doing SineFM with this matrix, there always goes a very high pitch along with it.

Am I making a mistake here which would throw things off?

//MODARRAY

float Mod1Sources[11]={0, env.Process(trig&&gate_), envM.Process(trig&&gate_), lfoOut, seqDec, seqPeak, seqF, osc.Process(), osc2.Process(), keyF };

float* Mod1Targets[21]={0, &CutMod, &PeakMod, &ResMod, &WavMod, &PwMod, &F1Mod, &F2Mod, &OctMod, &GlideMod, &LfoFmod, &LfoAmod, &TimeMod, &fbMod, &colMod, &dResMod, &dlvMod, &AMod, &DMod, &SMod, &RMod};

for(int i=0; i<21; i++)

{*Mod1Targets[i] = 0;}

*Mod1Targets[Mod1Targ] = Mod1Sources[Mod1Source]*Mod1Amt/24;

float Mod2Sources[11]={0, env.Process(trig&&gate_), envM.Process(trig&&gate_), lfoOut, seqDec, seqPeak, seqF, osc.Process(), osc2.Process(), keyF };

float* Mod2Targets[21]={0, &Cut2Mod, &Peak2Mod, &Res2Mod, &Wav2Mod, &Pw2Mod, &F12Mod, &F22Mod, &Oct2Mod, &Glide2Mod, &Lfo2Fmod, &Lfo2Amod, &Time2Mod, &fb2Mod, &col2Mod, &dRes2Mod, &dlv2Mod, &A2Mod, &D2Mod, &S2Mod, &R2Mod};

for(int i=0; i<21; i++)

{*Mod2Targets[i] = 0;}

*Mod2Targets[Mod2Targ] = Mod2Sources[Mod2Source]*Mod2Amt/24;

float Mod3Sources[11]={0, env.Process(trig&&gate_), envM.Process(trig&&gate_), lfoOut, seqDec, seqPeak, seqF, osc.Process(), osc2.Process(), keyF };

float* Mod3Targets[21]={0, &Cut3Mod, &Peak3Mod, &Res3Mod, &Wav3Mod, &Pw3Mod, &F13Mod, &F23Mod, &Oct3Mod, &Glide3Mod, &Lfo3Fmod, &Lfo3Amod, &Time3Mod, &fb3Mod, &col3Mod, &dRes3Mod, &dlv3Mod, &A3Mod, &D3Mod, &S3Mod, &R3Mod};

for(int i=0; i<21; i++)

{*Mod3Targets[i] = 0;}

*Mod3Targets[Mod3Targ] = Mod3Sources[Mod3Source]*Mod3Amt/24;

float Mod4Sources[11]={0, env.Process(trig&&gate_), envM.Process(trig&&gate_), lfoOut, seqDec, seqPeak, seqF, osc.Process(), osc2.Process(), keyF };

float* Mod4Targets[21]={0, &Cut4Mod, &Peak4Mod, &Res4Mod, &Wav4Mod, &Pw4Mod, &F14Mod, &F24Mod, &Oct4Mod, &Glide4Mod, &Lfo4Fmod, &Lfo4Amod, &Time4Mod, &fb4Mod, &col4Mod, &dRes4Mod, &dlv4Mod, &A4Mod, &D4Mod, &S4Mod, &R4Mod};

for(int i=0; i<21; i++)

{*Mod4Targets[i] = 0;}

*Mod4Targets[Mod4Targ] = Mod4Sources[Mod4Source]*Mod4Amt/24;

Out of context, and without tags to preserve text formatting, it’s difficult to even consider what might be wrong.

Perhaps the sine example isn’t the best to follow, and instead do something like this?

void AudioCallback(AudioHandle::InputBuffer in, AudioHandle::OutputBuffer out, size_t size)
{

for (size_t i = 0; i < size; i++)
{

out[0][i] = audio_output;
out[1][i] = audio_output;

}

}

Might be good to watch the first few Bela videos, the platforms are similar and the videos are great.

thanks for your help, corvin!
I will watch the bela videos for sure.

I tried your suggestion, but it did not change anything.
still, I kept is your way and found out that only my mod matrix is the problem.

the one I posted above introduces aliasing.

then I tried the one below, and still funny behavior:
as soon as I select osc.Process as a source with no target and no mod amount, the sound changes and starts to alias.
As if selecting osc.Process would destabilize the osc.

another problem I encountered is that the sound starts aliasing as soon as the oscillators are detuned with each other.
I must be missing something…

float* Mod1Targets[21]={&none, &CutMod, &PeakMod, &ResMod, &WavMod, &PwMod, &F1Mod, &F2Mod, &OctMod, &GlideMod, &LfoFmod, &LfoAmod, &TimeMod, &fbMod, &colMod, &dResMod, &dlvMod, &AMod, &DMod, &SMod, &RMod};
switch(Mod1Source)
{
case 0: *Mod1Targets[Mod1Targ] = 0;   break; 
case 1: *Mod1Targets[Mod1Targ] = envout * Mod1Amt/24;break; 
case 2: *Mod1Targets[Mod1Targ] = Menv * Mod1Amt/24;break; 
case 3: *Mod1Targets[Mod1Targ] = lfoOut * Mod1Amt/24;break; 
case 4: *Mod1Targets[Mod1Targ] = seqDec * Mod1Amt/24;break; 
case 5: *Mod1Targets[Mod1Targ] = seqPeak * Mod1Amt/24;break; 
case 6: *Mod1Targets[Mod1Targ] = seqF * Mod1Amt/24;break; 
case 7: *Mod1Targets[Mod1Targ] = osc.Process() * Mod1Amt/24;break; 
case 8: *Mod1Targets[Mod1Targ] = osc2.Process() * Mod1Amt/24;break; 
case 9: *Mod1Targets[Mod1Targ] = keyF * Mod1Amt/24;break; 
break;}

this is my osc output (simply adding->filter->delay):

        sig = (osc.Process() + osc2.Process())*envout;
 
        filt.Process(sig);

    if(Filterchoice==0)
        {del.Write(filt.Low() + dFilt.Band());}
    else {del.Write(filtM.Process(sig) + dFilt.Band());}
        
        fbFin = (fb / 24 + fbMod + fb2Mod + fb3Mod + fb4Mod);
        if(fbFin>.99){fbFin=.99;}
        if(fbFin<0){fbFin=0;}
        dFilt.Process(del.Read() * fbFin);

    if(Filterchoice==0){
     finsig    = (dFilt.Band() * (dlv / 24 + dlvMod) + filt.Low());}
    else {finsig  = (dFilt.Band() * (dlv / 24 + dlvMod) + filtM.Process(sig));}

    for(size_t i = 0; i < size; i++)
    {
        out[0][i]=finsig*.7f;
        out[1][i]=finsig*.7f;
    }

The choice of how to index the input and output buffers isn’t a ‘try it see and what works’ thing. One way is correct for Interleaved buffers, the other way is correct for non-Interleaved buffers. Which callback form is being used still hasn’t been shown.

Is ‘aliasing’ the correct term? In audio, this term would refer to artifacts caused by a signal whose frequency is above the Nyquist frequncy, which is samplerate/2. I don’t think that’s what’s happening.

It could be that the code is just doing too much stuff, and is causing an underrun to the codec. Without code to actually run, nobody else can debug it.

The only other thing I can think of is that size = 96 by default I think, so each time through AudioCallback, you have a delay perhaps because nothing is being done in the for loop?

for(size_t i = 0; i < size; i++)
{
    out[0][i]=finsig*.7f;
    out[1][i]=finsig*.7f;
}

Good luck, hope to hear you’ve solved this at some point. tele_player is much more knowledgable and may have better insight into how to solve any issues.

That is a complete non issue. Simply put, if blocksize is too small, you might get underruns, but the only possible downside of large blocksize is increased latency.

Thanks corvin, I hope for good news as well! But right now I am out of ideas.

I am running a block size of 4 samples at 96khz.

I tried blocksize 32 and 48khz to no avail, but changing blocksize
changed the pitch for some reason, I think that should not be the case?

Since the sine osc produces no aliasing, I can rule out buffer underruns.

Many thanks for trying to help.

Here is my hw setup:

    hw.Configure();
    hw.Init(true);
    hw.SetAudioBlockSize(4);
    hw.SetAudioSampleRate(SaiHandle::Config::SampleRate::SAI_96KHZ);
    float sample_rate = hw.AudioSampleRate();

audiocallback:

void AudioCallback(AudioHandle::InputBuffer  in,
                          AudioHandle::OutputBuffer out,
                          size_t                    size)

{ 

and my audio code which I am sure you dont want to suffer:

        lfo.SetFreq((lfoF * lfoF + (LfoFmod + Lfo2Fmod + Lfo3Fmod + Lfo4Fmod) )*499.99f + .01f);

        lfoOut= lfo.Process() * (1- (LfoAmod + Lfo2Amod + Lfo3Amod + Lfo4Amod));
        
        Menv = envM.Process(trig || gate_);

        peak = myP3[5];

        Cut = myP3[4] * 128;

        fPeak = ((peak * 256 - 96) + seqPeak * 2 + (PeakMod + Peak2Mod + Peak3Mod + Peak4Mod)*96) * Menv;

        Clfo = myP2[1];
        lfoamt = lfoOut * Clfo * 60;
        cutFin= Cut + fPeak + lfoamt + 17 + (CutMod + Cut2Mod + Cut3Mod + Cut4Mod)*96;
        if(cutFin>135){cutFin=135;}
        if(cutFin<0){cutFin=0;}
        filt.SetFreq(mtof(cutFin));
        filtM.SetFreq(mtof(cutFin));
      
        Res = myP3[6];
        resFin=Res + ResMod + Res2Mod + Res3Mod + Res4Mod;
        if(resFin>1){resFin=1;}
        if(resFin<0){resFin=0;}
        filt.SetRes(resFin);
        filtM.SetRes(resFin);


        octi = myP[2];
        int rawct=0;
        rawct= (octi+ OctMod + Oct2Mod + Oct3Mod + Oct4Mod)  * 4 - 2 + .5;
        oct = rawct * 12;


        wav = myP3[7];
        int owav = (static_cast<int>((wav + WavMod + Wav2Mod + Wav3Mod + Wav4Mod) * 3 + .7));
        if (owav>3){owav = 3;}
        if (owav<0){owav = 0;}
        switch (owav)
        {
        case 0: osc.SetWaveform(0); osc2.SetWaveform(0); break;
        case 1: osc.SetWaveform(5); osc2.SetWaveform(5); break;
        case 2: osc.SetWaveform(6); osc2.SetWaveform(6); break;
        case 3: osc.SetWaveform(7); osc2.SetWaveform(7); break;
        }

        oscFk = myP[3];
        oscF2k = myP[4];
        oscF = oscFk * 48;
        osc2F = oscF2k * 48;

        lfoP = myP2[2];

        lfoAmpp = myP2[7];
        lfopamt = lfoOut * lfoP * lfoP * 48;
        lfoAmp = lfoOut * lfoAmpp * 2;
        lfopwamt = lfoOut * pwmk * .5;
        
//Scale

        finalF1=0;
        finalF2=0;

        switch(scale)
        {
        case 0: keysc=keyF+seqF;  break;
        case 1: keyFsc= static_cast<int>(keyF + seqF) % 7;
        scoct = static_cast<int>((keyF + seqF)/7)*12;
        keysc = Major[keyFsc] + scoct; break;
        case 2: keyFsc= static_cast<int>(keyF + seqF) % 7;
        scoct = static_cast<int>((keyF + seqF)/7)*12;
        keysc= Minor[keyFsc] + scoct; break;
        case 3: keyFsc= static_cast<int>(keyF + seqF) % 5;
        scoct = static_cast<int>((keyF + seqF)/5)*12;
        keysc = Penta[keyFsc] + scoct; break;
        }

        finalF1 = keysc + oscF + oct + 24 + lfopamt + (F1Mod + F12Mod + F13Mod + F14Mod)*24;
        if (finalF1>127){finalF1=127;}
        if (finalF1<0){finalF1=0;}


        glide = myP[1];
        finF1=mtof(finalF1);
        fonepole(finF11, finF1, glide*glide*.05+.000001); 
        osc.SetFreq(finF11);
        
        pwmk = myP[6];
        
        pwk = myP[7];
        pwm = pwk * .9 + lfopwamt;
        if (pwm > .99){pwm = .99;}
        else if (pwm < .01){pwm = .01;}
        osc.SetPw(pwm + PwMod + Pw2Mod + Pw3Mod + Pw4Mod);

        if(osc2pv==0){
        finalF2 = keysc + osc2F + oct;
        }
        else if(osc2pv==1){
            switch(scale)
        {
        case 0: keysc=keyF;  break;
        case 1: keyFsc= static_cast<int>(keyF) % 7;
        scoct = static_cast<int>(keyF/7)*12;
        keysc = Major[keyFsc] + scoct; break;
        case 2: keyFsc= static_cast<int>(keyF) % 7;
        scoct = static_cast<int>(keyF/7)*12;
        keysc= Minor[keyFsc] + scoct; break;
        case 3: keyFsc= static_cast<int>(keyF) % 5;
        scoct = static_cast<int>(keyF/5)*12;
        keysc = Penta[keyFsc] + scoct; break;
        }
        finalF2 = keysc + osc2F + oct;
        }
        else if(osc2pv==2){
        finalF2 = osc2F + oct;
        }
        finalF2 += 24 + lfopamt + (F2Mod + F22Mod + F23Mod + F24Mod)*24;
        if (finalF2>127){finalF2=127;}
        if (finalF2<0){finalF2=0;}

        
        finF2=mtof(finalF2);
        fonepole(finF12, finF2, glide*glide*.02+.000001); 
        osc2.SetFreq(finF12);

        osc2.SetPw(pwm + PwMod + Pw2Mod + Pw3Mod + Pw4Mod);


        Att = myP3[0];

        dec = myP3[1];

        sus = myP3[2];
      
        rel = myP3[3];

        env.SetTime(ADSR_SEG_ATTACK, Att + AMod + A2Mod + A3Mod + A4Mod);
        env.SetTime(ADSR_SEG_DECAY, (dec * dec) + (seqDec/196) + DMod + D2Mod + D3Mod + D4Mod);
        env.SetTime(ADSR_SEG_RELEASE, rel + RMod + R2Mod + R3Mod + R4Mod);
        env.SetSustainLevel(sus + SMod + S2Mod + S3Mod + S4Mod);


        AttM = myP2[3];

        decM1 = myP2[4];

        susM = myP2[5];

        relM = myP2[6];

        envM.SetTime(ADSR_SEG_ATTACK, AttM + AMod);
        envM.SetTime(ADSR_SEG_DECAY, (decM1 * decM1) + (seqDec/196) + DMod);
        envM.SetTime(ADSR_SEG_RELEASE, relM + RMod);
        envM.SetSustainLevel(susM + SMod);

        envout = env.Process(trig || gate_);

        del.SetDelay((delT + TimeMod + Time2Mod + Time3Mod + Time4Mod) * 94000 * 0.99f);
        fonepole(delT, delTfone/48, 0.0001f);

        dCut = mtof(dCtf * 2 + 90 + ((colMod + col2Mod + col3Mod + col4Mod) * 60));
        dFilt.SetFreq(dCut);
       
        sig = (osc.Process() + osc2.Process())*envout;
 
        filt.Process(sig);

    if(Filterchoice==0)
        {del.Write(filt.Low() + dFilt.Band());}
    else {del.Write(filtM.Process(sig) + dFilt.Band());}
        
        fbFin = (fb / 24 + fbMod + fb2Mod + fb3Mod + fb4Mod);
        if(fbFin>.99){fbFin=.99;}
        if(fbFin<0){fbFin=0;}
        dFilt.Process(del.Read() * fbFin);

    if(Filterchoice==0){
     finsig    = (dFilt.Band() * (dlv / 24 + dlvMod) + filt.Low());}
    else {finsig  = (dFilt.Band() * (dlv / 24 + dlvMod) + filtM.Process(sig));}

    for(size_t i = 0; i < size; i++)
    {
        out[0][i]=finsig*.7f;
        out[1][i]=finsig*.7f;
    }
}
``