Advertisement

[COMPLETELY SOLVED] Generating Sound Effect

Started by December 23, 2007 10:26 AM
4 comments, last by Endurion 17 years, 1 month ago
SOLVED - Stupid bug of mine SOLVED - New question related to noise effect Hi, i've recently found a very nifty sound creation tool and decided to try to do something like that just for kicks. I'm having a weird problem with a simple sine wave. It sounds distorted. I've compared it to another app and it seems the sample values are similar but not exactly the same (saved both to a .wav file). Might this be the cause already? Here's the snippet that creates the sound buffer: m_FXFrequency is set to 440 m_SampleFrequency is set to 44100 SoundData is sized to 2048 to carry 1024 samples. The generated sound is supposed to be 16bit mono. The GR::somethings are typecasts, GR::f32 is float, GR::f64 double, GR::i16 signed short.


  GR::f32     SineFrequency = (GR::f32)m_FXFrequency;

  for ( int i = 0; i < (int)SoundData.Size() / 2; ++i )
  {
    GR::f64     TimePos = (GR::f64)i * 1.0 / (GR::f64)m_SampleFrequency;

    GR::f64     SampleValue = sin( TimePos * SineFrequency * 2.0 * 3.1415926 );

    if ( SampleValue < -1.0 )
    {
      SampleValue = -1.0;
    }
    if ( SampleValue > 1.0 )
    {
      SampleValue = 1.0;
    }
    SoundData.SetU16At( i * 2, (GR::i16)( 32767.0f * SampleValue ) );
  }
Is my approach too naive? Is the usual floating point error the cause and makes people hear the sound distorted? Remember kids, if you do sound effect synthesizing in parts, don't forget to actually pass the offset if you render the next part. In other words, i was basically stitching the first part of the sine together over and over again. [Edited by - Endurion on December 24, 2007 8:11:54 AM]

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

Quote:
Original post by Endurion
m_FXFrequency is set to 440
m_SampleFrequency is set to 44100
SoundData is sized to 2048 to carry 1024 samples.


Are you just repeating SoundData over and over? If so...

A 440Hz signal sampled at 44100Hz is going to oscillate just over 100 times a second - more precisely, each oscillation requires 100.2 samples to represent it. A 1024 sample buffer therefore will contain 10 full oscillations and then about a quarter of the next one, which is quite unfortunate because a quarter of a full sine wave is going to cause a large drop in value between 2 adjacent samples, ie. a very high frequency click. This will occur about 44 times a second, meaning those clicks may form a low frequency component too. These 2 factors could explain your distortion.

The fix, if I've understood this correctly, is to remember where you were at the end of filling one buffer so that when you start the next, you continue from that point. ie. Move timePos outside of the loop - all you need is to increment it by '1.0 / (GR::f64)m_SampleFrequency' each iteration. When it equals or exceeds m_SampleFrequency (ie. after you've generated a second's worth of samples), subtract m_SampleFrequency from it to reset it.

That assumes you're continually regenerating the buffer. If you really need a one-shot buffer that you loop, you need to think about making the buffer of such a size that you can get a whole number of complete oscillations in there. You can still get quantisation errors at the edges though (because you can't have half a sample, or anything), so then you'd have to use a trick to fix it, such as crossfading.

Quote:
    if ( SampleValue < -1.0 )    {      SampleValue = -1.0;    }    if ( SampleValue > 1.0 )    {      SampleValue = 1.0;    }


Also, precede those with assertions. The result should never come outside the -1.0 to +1.0 range at this point. If it does, the wave amplitude is too big for the sample resolution and you end up squaring off your wave, introducing aliasing effects - ie. distortion.

Quote:
Is the usual floating point error the cause and makes people hear the sound distorted?


No, it should be fine at this resolution - 64 bit floats have far more resolution than 16 bit int can handle. You probably introduce much more error when converting to 16 bit than you get in the original 64 bit floats.
Advertisement
Just saw your edit: I think that is fundamentally what I said, so at least we're on the same page. :)
Thanks for the information, good to know.

The clamping code is in for the other waveforms i'm implementing, just to be on the safe side. I want to add things like enveloping and volume control, so i guess i need this.
The code doesn't have to run realtime, i calculate a big buffer in segments. If i need this in realtime i can use the buffer segments for streaming.


I do have a new question though:

Noise waveform. As far as i understand it i just use randomized values. This however isn't affected by frequency; which is not what i want.

What's the common way for frequency dependant noise generation?

I was thinking along the line of filling a noise buffer and then depending on the wanted frequency iterate slower or faster to the next noise buffer value. I'm not sure if this is the "correct" way to go about this though.

Edit:

The answer is: There is no frequency in the common sense with noise. You basically just adjust the length of a random sample to make the effect sound higher or lower.

[Edited by - Endurion on December 24, 2007 8:05:56 AM]

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

Quote:
Original post by Endurion
The clamping code is in for the other waveforms i'm implementing, just to be on the safe side. I want to add things like enveloping and volume control, so i guess i need this.


Still, if you ever clamp your signal, then you will get clipping, which means audible distortion. As long as you're aware of this, that's fine. Typically 'volume control' means the degree to which you attenuate the input, from 0.0 (full volume) to 1.0 (no volume), and no clamping is used or required.

Quote:
The answer is: There is no frequency in the common sense with noise. You basically just adjust the length of a random sample to make the effect sound higher or lower.


Your terminology is a bit unusual, as a sample has a fixed length in this context. Perhaps what you're essentially saying is to generate random values (ie. white noise - there are other types of noise) which theoretically cover the whole frequency range of your sampling rate (ie. up to 22.1KHz) and then stretch it out so that the highest value, and thus also the average value, becomes lower.

Another way would be to run a low-pass filter on it to remove the high frequency components.
Exactamundo :)

I'm obviously not used to the correct terminologies with sound/music, but you summed it up nicely.

You're right about the volume enveloping, there shouldn't be any clamping needed.

I'll try experimenting with ADSR now...

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

This topic is closed to new replies.

Advertisement