Calculate saw and triangle wave from specific data

2020-06-28 16:22发布

问题:

I need to calculate a triangle and saw wave but it is a little complicate because of my model and the data I'm able to work with (but maybe I'm just confused).

I'm able to calculate my sine wave but I'm not really using a frame counter. What I do is, calculate a theta_increment variable which I can use the next time I need to calculate a sample. This works like this:

float x = note.frequency / AppSettings::sampleRate;
float theta_increment = 2.0f * M_PI * x;
float value = 0;

if(waveType == SINE){
    value = sin(note.theta) * fixedAmplitude;
}

Now that I have the value of the currend frame/sample I store theta_increment inside my note.theta member so I can use it for the next sample:

note.theta += theta_increment;

I've looked at tons of examples on how I should calculate a saw or a triangle but I can't figure it out. (I only have the data mentioned above at my disposal) This is my last attempt but it's not working and giving me tons of glitches:

value = 1.0f - (2.0f * ((float)note.theta / (float)44100));

回答1:

If you have a loop generating your values like this:

for (size_t frame=0; frame!=n_frames; ++frame) {
  float pos = fmod(frequency*frame/sample_rate,1.0);
  value[frame] = xFunc(pos)*fixedAmplitude;
}

Then you can use these functions for different types of waves:

float sinFunc(float pos)
{
  return sin(pos*2*M_PI);
}

float sawFunc(float pos)
{
  return pos*2-1;
}

float triangleFunc(float pos)
{
  return 1-fabs(pos-0.5)*4;
}

The basic idea is that you want a value (pos) that goes from 0.0 to 1.0 over each cycle. You can then shape this however you want.

For a sine wave, the sin() function does the job, you just need to multiply by 2*PI to convert the 0.0 to 1.0 range into a 0.0 to 2*PI range.

For a sawtooth wave, you just need to convert the 0.0 to 1.0 range into a -1.0 to 1.0 range. Multiplying by two and subtracting one does that.

For a triangle wave, you can use the absolute value function to cause the sudden change in direction. First we map the 0.0 to 1.0 range into a -0.5 to 0.5 range by subtracting -0.5. Then we make this into a 0.5 to 0.0 to 0.5 shape by taking the absolute value. By multiplying by 4, we convert this into a 2.0 to 0.0 to 2.0 shape. And finally by subtracting it from one, we get a -1.0 to 1.0 to -1.0 shape.



回答2:

A sawtooth wave could be calculated like this:

value = x - floor(x);

A triangle could be calculated like this:

value = 1.0 - fabs(fmod(x,2.0) - 1.0);

where x is note.theta.