简体   繁体   中英

C++ mathematical function generation

In working on a project I came across the need to generate various waves, accurately. I thought that a simple sine wave would be the easiest to begin with, but it appears that I am mistaken. I made a simple program that generates a vector of samples and then plays those samples back so that the user hears the wave, as a test. Here is the relevant code:

vector<short> genSineWaveSample(int nsamples, float freq, float amp) {
  vector<short> samples;
  for(float i = 0; i <= nsamples; i++) {
    samples.push_back(amp * sinx15(freq*i));
  }
  return samples;
}

I'm not sure what the issue with this is. I understand that there could be some issue with the vector being made of short s, but that's what my audio framework wants, and I am inexperienced with that kind of library and so do not know what to expect.

The symptoms are as follows:

  • frequency not correct
    • ie: given freq=440, A4 is not the note played back
  • strange distortion
    • Most frequencies do not generate a clean wave. 220, 440, 880 are all clean, most others are distorted
  • Most frequencies are shifted upwards considerably

Can anyone give advice as to what I may be doing wrong?

Here's what I've tried so far:

  • Making my own sine function, for greater accuracy.
    • I used a 15th degree Taylor Series expansion for sin(x)
  • Changed the sample rate, anything from 256 to 44100, no change can be heard given the above errors, the waves are simply more distorted.

Thank you. If there is any information that can help you, I'd be obliged to provide it.

I suspect that you are passing incorrect values to your sin15x function. If you are familiar with the basics of signal processing the Nyquist frequency is the minimum frequency at which you can faithful reconstruct (or in your case construct) a sampled signal. The is defined as 2x the highest frequency component present in the signal.

What this means for your program is that you need at last 2 values per cycle of the highest frequency you want to reproduce. At 20Khz you'd need 40,000 samples per second. It looks like you are just packing a vector with values and letting the playback program sort out the timing.

We will assume you use 44.1Khz as your playback sampling frequency. This means that a snipet of code producing one second of a 1kHz wave would look like

DataStructure wave = new DataStructure(44100) // creates some data structure of 44100 in length

for(int i = 0; i < 44100; i++)
{
  wave[i] = sin(2*pi * i * (frequency / 44100) + pi / 2) // sin is in radians, frequency in Hz
}

You need to divide by the frequency, not multiply. To see this, take the case of a 22,050 Hz frequency value is passed. For i = 0, you get sin(0) = 1. For i = 1, sin(3pi/2) = -1 and so on are so forth. This gives you a repeating sequence of 1, -1, 1, -1... which is the correct representation of a 22,050Hz wave sampled at 44.1Khz. This works as you go down in frequency but you get more and more samples per cycle. Interestingly though this does not make a difference. A sinewave sampled at 2 samples per cycle is just as accurately recreated as one that is sampled 1000 times per second. This doesn't take into account noise but for most purposes works well enough.

I would suggest looking into the basics of digital signal processing as it a very interesting field and very useful to understand.

Edit: This assumes all of those parameters are evaluated as floating point numbers.

Fundamentally, you're missing a piece of information. You don't specify the amount of time over which you want your samples taken. This could also be thought of as the rate at which the samples will be played by your system. Something roughly in this direction will get you closer, for now, though.

samples.push_back(amp * std::sin(M_PI / freq *i));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM