Mathematically Ramp down a sinewave to 0 or switch frequencies

AI Thread Summary
To avoid clicking sounds when ramping down a sine wave or switching frequencies, it's crucial to manage amplitude transitions carefully. Gradually reducing amplitude by halving it can help, as can switching at zero crossings to prevent abrupt changes. Using the NAudio library, implementing FadeInOutSampleProviders can facilitate smoother transitions between tones. Additionally, employing a Direct Digital Synthesis (DDS) approach allows for real-time frequency and amplitude adjustments without introducing phase disturbances. Properly managing these transitions is essential for achieving clean audio outputs in sound synthesis.
btb4198
Messages
570
Reaction score
10
How do Mathematically Ramp down a sinewave to 0 or switch frequencies without causing a clicking sound?
I wrote code to play a sinewave but when I stop the sound I can hear a clicking sound. I tried to slowly ramp down the Amplitude, but that seem to only work for some Combination of frequencies and amplitudes.

here is what I have:

[CODE title="Naudio play code"]
public override int Read(byte[] buffer, int offset, int sampleCount)
{

if (position == 0 && onetimeflag == false )
{
n2 = 0;
onetimeflag = true;
}

if(stopflag == true && Amplitude > 0 )
{
Amplitude = Math.Round(Amplitude - 0.01,2);
}

if ((n2 >= BufferLength || stopflag == true) && lastvalue == 0 )
{


return -1;
}
for (int i = 0; i < (sampleCount / 4); i++)
{

temp1 = (float)(Amplitude * Math.Sin(2D * Math.PI * RightFrequencyOutPut * n2 / 44100D));
Frequency_switch = true;
n2++;
}
lastvalue = temp1;
byte[] bytes = BitConverter.GetBytes(temp1);
buffer[i * 4 + 0] = bytes[0];
buffer[i * 4 + 1] = bytes[1];
buffer[i * 4 + 2] = bytes[2];
buffer[i * 4 + 3] = bytes[3];
tempSample++;

}
return sampleCount;
}[/CODE]
 
Technology news on Phys.org
Also, I am using the C# library NAudio. Anyhow I know there has to be a Mathematically way of not producing a clicking sound and a Mathematically way of smoothly switching between frequencies.
is there an Equation to do this?
 
I’m sure you can create a function to do it. However, there is no one function that does this ramping down.

You might consider halving the amplitude until it’s reallly small like 1/256 of the original value picking 1/256 as a convenient value for basically eight steps to silence.

Db wise this will subtract 6db from the signal at each step.
 
You will get a click if there is a step at the transition.
Switch the sinewave on or off at a zero crossing.
To change between two sinewaves, switch when they have equal values and the derivatives have the same sign.
 
  • Like
Likes berkeman, Filip Larsen, jedishrfu and 1 other person
Baluncore said:
You will get a click if there is a step at the transition.
Switch the sinewave on or off at a zero crossing.
To change between two sinewaves, switch when they have equal values and the derivatives have the same sign.
is there a mathematically way to know when numbers I will cross from 0 on ?
 
btb4198 said:
is there a mathematically way to know when numbers I will cross from 0 on ?
That will depend on how you generate your sinewave, and how it is stored.
The sign ± will change at each zero crossing.

You could generate the data in a separate array, search that array backwards for the last change of sign, and then copy the part you need, up to the final zero, into the Naudio buffer.

You might be able to read the data in, and change the length of, a Naudio buffer.
 
btb4198 said:
Is there a way to make it fade out when you click a button ?
Yes, you call the BeginFadeOut method in the button's click event handler. How much C# programming have you done? What is your end goal? Dealing with button clicks and sound events is something that is handled well by most game engines and you may be better off using one of these appropriate for your platform than trying to create something from scratch.
 
  • #10
Rather than jumping into Coding with both feet, why not consider what's needed first. You have FSK here (frequency shift keying). That can be achieved as smoothly as you want. If you are synthesising sine wave samples using
A(t) = A0(sin(ωt))
and slowly change the value of ω from one frequency to the next then you will have samples of a continuous curve. The varying values of ω (call them ω(t')) can be varied over as long a range of t' as you want. That ω(t') function need not be linear but it can follow, say, a raised cos shape (easy to code for). That will result in very narrow sidebands with frequency spacing of around 1/(transition time). That's about as good as you could get.
 
  • #11
sophiecentaur said:
If you are synthesising sine wave samples using A(t) = A0(sin(ωt)) and slowly change the value of ω from one frequency to the next then you will have samples of a continuous curve.
Careful, if you are varying ## \omega ## I think you need ## A(t) = A_0 \sin \left ( \omega \left ( t - 2 \pi \left \lfloor \frac{t}{2 \pi} \right \rfloor \right ) \right) ##
 
  • #12
You vary omega values with time in a straight sine wave formula. I haven’t time to search for a ref but look up a definition of fm.
The steps in t will be equal. Omega is varied over a period covering many carrier cycles if you want to avoid a click.
The coding would be easy enough.
PS
@pbuk that formula is wrong / incomplete. The bit in the brackets is zero)
 
Last edited:
  • #13
sophiecentaur said:
The coding would be easy enough.
Yes, it's easier to code than to write it in ## \LaTeX ## but if you don't make the correction I indicated then you will get ## \sin ( (\omega + \delta \omega) (t + \delta t)) ## instead of ## \sin ( \omega (t + \delta t)+ \delta t \delta \omega)) ##: consider the impact of the ## t \delta \omega ## term when t is, say, 10 seconds.
 
  • #14
pbuk said:
Yes, it's easier to code than to write it in ## \LaTeX ## but if you don't make the correction I indicated then you will get ## \sin ( (\omega + \delta \omega) (t + \delta t)) ## instead of ## \sin ( \omega (t + \delta t)+ \delta t \delta \omega)) ##: consider the impact of the ## t \delta \omega ## term when t is, say, 10 seconds.
I have used t for the carrier and t' for the modulation waveform. That's sloppy and I should have pointed out that ω is a function of time like:
ω(t) = ω0(1+rt)

Also sloppy but r is the desired rate of change of ω and would be Δω/ transition time for a linear slide in frequency and it applied just during the transition period. r can be modified to reduce the bandwidth ('clicks')

(I usually fail with Latex so I tend to use the lazy verbal alternative for anything complicated. Mea culpa)
 
  • #15
The math could be avoided by programming a state machine as a DDS, to generate the buffer content step by step. That way the frequency and amplitude could be changed in real time, at rates specified in the numerical control registers.
https://en.wikipedia.org/wiki/Direct_digital_synthesis
 
  • Like
Likes sophiecentaur and pbuk
  • #16
Baluncore said:
The math could be avoided by programming a state machine as a DDS, to generate the buffer content step by step. That way the frequency and amplitude could be changed in real time, at rates specified in the numerical control registers.
https://en.wikipedia.org/wiki/Direct_digital_synthesis
Thanks for shaking us out of the hole we have dropped into - in real time digital signal processing there is no time to be calculating the sine of anything, we use lookup tables. And we don't need to worry about the detail of this anyway when we write an application because we use a library, in this case NAudio.

Note that in order to play a tone with a different frequency in NAudio, like most simple sound libraries, you stop playing one tone (with a call to BeginFadeOut) and start playing another one (with a call to BeginFadeIn).
 
  • #17
pbuk said:
Thanks for shaking us out of the hole we have dropped into - in real time digital signal processing there is no time to be calculating the sine of anything, we use lookup tables. And we don't need to worry about the detail of this anyway when we write an application because we use a library, in this case NAudio.

Note that in order to play a tone with a different frequency in NAudio, like most simple sound libraries, you stop playing one tone (with a call to BeginFadeOut) and start playing another one (with a call to BeginFadeIn).
Last nights I was discussing just this approach but in a totally different context. Machines exist that will perform all sorts of functions and the risk is going too far down the “just get it done” route.
Using a look up table is not essentially different from calculating sin(c) except you need to have some grasp of what a sine function means.
That is unless you want Mr Google to do all your thinking for you and then we end up in the tyrany of the machine. Democracy will die. (Over dramatic? Are you sure?)
 
  • #18
pbuk said:
(with a call to BeginFadeOut) and start playing another one (with a call to BeginFadeIn).
That could involve a variation in level and not necessarily the minimal phase disturbance. Doing it as quickly as possible would not be best (and cleanest) achieved this way because the phase progression between tones is not defined.
Proper synthesis can specify the precise transition. A library of functions may or may not have the required best function built in.
 
  • Like
Likes Baluncore
  • #19
pbuk said:
Note that in order to play a tone with a different frequency in NAudio, like most simple sound libraries, you stop playing one tone (with a call to BeginFadeOut) and start playing another one (with a call to BeginFadeIn).
That is a significant limitation of Naudio.
Fade_in and fade_out prevent changing the generated frequency on the fly without an amplitude step. The transition between the two sine waves will have an unknown phase difference during the transition.

A DDS employs a phase accumulator. Frequency is the rate of change of phase. Loading the frequency control register will immediately change the frequency, without any transient step in amplitude or phase.

At audio sample rates it matters little whether you use a lookup table or evaluate sine( phase ).
 
  • #20
sophiecentaur said:
That could involve a variation in level and not necessarily the minimal phase disturbance.
Baluncore said:
That is a significant limitation of Naudio.
Hey, don't shoot the messenger - NAudio is the OP's choice, not mine. But given the limitations exposed in this post I think they have more significant issues to address than the choice of audio library.
 
  • #21
  • #22
By necessity, shifting from a sinewave of a specific amplitude to anything else will involved the generation of other frequency components during the transition.

My strategy would be to minimize those other components.

So, in a loop that varies t, you calculate
. amplitude = Asin(Bt).
At the start of the transition (t0), you split this into two constructively interfering sine waves of slightly different pitch - with the difference represented by D:
. amplitude = (A/2)( sin(Bt+D(t-t0)) + sin(Bt-D(t-t0)) )
which can also be written:
. amplitude = Asin(Bt)cos(D(t-t0))
Once the D(t-t0) term reaches pi/2 use:
. amplitude = 0

If you are transitioning from one tone (A,B) to another (E,F), I would recommend treating them independently:
. amplitude = Asin(Bt)cos(D(t-t0)) + Esin(Ft)sin(D(t-t0)) . . . [for D(t-t0) from 0 to pi/2]
 
Back
Top