Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gam::Saw output range: has DC offset and exceeds 1.0 #51

Open
younkhg opened this issue Jan 16, 2019 · 5 comments
Open

gam::Saw output range: has DC offset and exceeds 1.0 #51

younkhg opened this issue Jan 16, 2019 · 5 comments

Comments

@younkhg
Copy link

younkhg commented Jan 16, 2019

output of below program is:

min: -0.625428, max: 1.02889

is there a way to normalize this output to [-1.0 : 1.0] ?

#include "Gamma/Domain.h"
#include "Gamma/Oscillator.h"

#include <cfloat>
#include <iostream>

using namespace std;

int main()
{
    gam::Domain::master().spu(44100.0f);
    gam::Saw<> saw;
    saw.freq(440.0f);

    float max_s = -FLT_MAX;
    float min_s = FLT_MAX;

    for (int i = 0; i < 44100; i += 1)
    {
        float s = saw();
        if (s > max_s) max_s = s;
        if (s < min_s) min_s = s;
    }

    cout << "min: " << min_s << ", max: " << max_s << endl;
}
@younkhg
Copy link
Author

younkhg commented Jan 16, 2019

also tagging @mantaraya36 and @kybr ...

@kybr
Copy link
Contributor

kybr commented Jan 16, 2019

I think that this might be expected behaviour. For a geometrically perfect sawtooth you would expect no DC and strict limits on (-1, 1). Of course, those sound awful.

However, gam::Saw is bandlimited. It is designed to sound like a sawtooth and not introduce aliasing. (I think. I could be wrong.) Consider Synthesis of Quasi-Bandlimited Analog Waveforms Using Frequency Modulation (Schoffhauzer 2005). That implementation of Saw and Square/Pulse does not stay in (-1, 1) and it always has some DC offset. Also, when you look at the waveforms it makes... they don't really look like Saw or Square/Pulse. They sound right though.

@younkhg
Copy link
Author

younkhg commented Jan 17, 2019

gam::Saw is bandlimited, it seems like it uses BLIT integration (http://musicdsp.org/files/waveforms.txt)
I've plotted it too and everything looks right.

I was asking if there's more proper way to map the result to [-1:1] rather than just subtracting some value like 0.5...

dc offset should not matter that much but value above 1.0 seemed weird for me

@LancePutnam
Copy link
Owner

Yep, it's using BLIT integration. It might be possible to fix it to give the expected range [-1,1], but I'm not exactly sure. I suspect the odd range is because it uses a leaky integrator to kill DC and that may be distorting the wave shape due to its non-linear phase response. You might want to check out DWO::up or DWO::down. They are not exactly band limited, but have far less aliasing than a naive saw and give the expected range.

@younkhg
Copy link
Author

younkhg commented Feb 22, 2019

Thank you for suggesting DWO!
Btw I just did another experiment and I think I can more clarify what was happening.

part of the result from program attached at the bottom is:

at freq: 400
	iteration 0	min: -0.225813, max: 1.02908
	iteration 1	min: -0.425058, max: 0.889006
	iteration 2	min: -0.489829, max: 0.798894
	iteration 3	min: -0.551924, max: 0.74092
	iteration 4	min: -0.575975, max: 0.684907
	iteration 5	min: -0.591454, max: 0.664167
	iteration 6	min: -0.601419, max: 0.65422
	iteration 7	min: -0.607839, max: 0.647812
	iteration 8	min: -0.611977, max: 0.643681
	iteration 9	min: -0.616374, max: 0.641016
	iteration 10	min: -0.617493, max: 0.638173
	iteration 11	min: -0.618222, max: 0.637444
	iteration 12	min: -0.618699, max: 0.636966
	iteration 13	min: -0.619015, max: 0.636649
	iteration 14	min: -0.619227, max: 0.636436
	iteration 15	min: -0.619473, max: 0.636289

It seems like first few thousand samples are off-range before stabilization of the integration.
For practical use, it shouldn't matter for many case I guess? Since oscillators usually are not made new in the middle of audio app but predefined in the beginning.

#include "Gamma/Oscillator.h"

#include <cfloat>
#include <iostream>

using namespace std;

void test_saw (float freq)
{
    gam::Saw<> saw;
    saw.freq(freq);

    float max_s = -FLT_MAX;
    float min_s = FLT_MAX;

    cout << "at freq: " << freq << endl;

    for (int i = 0; i < 16; i += 1)
    {
        float max_s = -FLT_MAX;
        float min_s = FLT_MAX;

        for (int j = 0; j < 512; j += 1)
        {
            float s = saw();
            if (s > max_s) max_s = s;
            if (s < min_s) min_s = s;
        }

        cout << "\titeration " << i << "\tmin: " << min_s << ", max: " << max_s << endl;
    }

}

int main ()
{
    gam::Domain::master().spu(44100.0f);

    for (float t = 100.0f; t < 4000.0f; t += 100.0f)
    {
        test_saw(t);
    }

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants