Open younkhg opened 5 years ago
also tagging @mantaraya36 and @kybr ...
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.
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
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.
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);
}
}
output of below program is:
min: -0.625428, max: 1.02889
is there a way to normalize this output to [-1.0 : 1.0] ?