问题
I want to develop an app to match your tinnitus frequency : A frequency is played and the user decrease or increase the freqency by pressing a plus or minus button. (see part of the codes, based on some coding from stackoverflow thx :-))
public static short[] BufferSamples = new short[44100 * 1 * 2];
private SourceVoice sourceVoice;
private AudioBuffer buffer;
private int Tfreq;
public MatchTinn()
{
InitializeComponent();
Loaded += MatchTinn_Loaded;
TFreq = 5000;
}
private void MatchTinn_Loaded(object sender, RoutedEventArgs e)
{
var dataStream = DataStream.Create(BufferSamples, true, true);
buffer = new AudioBuffer
{
LoopCount = AudioBuffer.LoopInfinite,
Stream = dataStream,
AudioBytes = (int)dataStream.Length,
Flags = BufferFlags.EndOfStream
};
FillBuffer(BufferSamples, 44100, Tfreq);
var waveFormat = new WaveFormat();
XAudio2 xaudio = new XAudio2();
MasteringVoice masteringVoice = new MasteringVoice(xaudio);
sourceVoice = new SourceVoice(xaudio, waveFormat, true);
// Submit the buffer
sourceVoice.SubmitSourceBuffer(buffer, null);
}
private void FillBuffer(short[] buffer, int sampleRate, int frequency)
{
if (sourceVoice != null)
{
sourceVoice.FlushSourceBuffers();
}
double totalTime = 0;
for (int i = 0; i < buffer.Length - 1; i += 2)
{
double time = (double)totalTime / (double)sampleRate;
short currentSample = (short)(Math.Sin(2 * Math.PI * frequency * time) * (double)short.MaxValue);
buffer[i] = currentSample;
buffer[i + 1] = currentSample;
totalTime++;
}
private void m1_OnTap(object sender, GestureEventArgs e)
{
Tfreq = Tfreq - 1;
if (Tfreq < 0)
{
Tfreq = 0;
}
FillBuffer(BufferSamples, 44100, Tfreq);
}
private void p1_OnTap(object sender, GestureEventArgs e)
{
Tfreq = Tfreq + 1;
if (Tfreq > 16000)
{
Tfreq = 16000;
}
FillBuffer(BufferSamples, 44100, Tfreq);
}
Playing the frequency is fine, but when the user presses a button you here a clicking sound when the frequency is updated. Do you have any idea what makes the sound and how i can get rid of it? Thanks.
回答1:
When you change the frequency, you're causing a discontinuity in the waveform that manifests as a click. Instead of making your signal calculations against absolute time, you should keep track of the phase of your sine calculation (e.g. a value from 0 to 2*pi), and figure out how much you need to add to your phase (subtracting 2*pi every time you exceed 2*pi) for the next sample when playing a specific frequency. This way, when you change frequency, the phase that you supply as a parameter to Math.Sin
doesn't change abruptly causing a click.
回答2:
Expanding on the answer @spender gave (I need 50 rep to add comment to his answer), I had a similar problem with naudio. I was able to solve the issue by adding two bool values that monitored the current sign of the sine value and the previous sign of the sine value. If the previous sine was negative and the current sine is positive, we know we can safely adjust the frequency of the sine wave.
double sine = amplitude * Math.Sin(Math.PI * 2 * frequency * time);
isPreviousSineWaveValPositive = isSineWaveValPositive;
if (sine < 0)
{
isSineWaveValPositive = false;
}
else
{
isSineWaveValPositive = true;
}
// When the time is right, change the frequency
if ( false == isPreviousSineWaveValPositive && true == isSineWaveValPositive )
{
time = 0.0;
frequency = newFrequency;
}
回答3:
Here's an example how you can get rid of the clicking. Instead of using a time, you should keep track of the current phase and calculate how much the phase is changed on the required frequency. Also this _currentPhase
must be persistent so it will have the previous value. (declaring it within the method would result in a click aswell (on most frequencies)
private double _currentPhase = 0;
private void FillBuffer(short[] buffer, int sampleRate, int frequency)
{
if (sourceVoice != null)
{
sourceVoice.FlushSourceBuffers();
}
var phaseStep = ((Math.PI * 2) / (double)sampleRate) * frequency;
for (int i = 0; i < buffer.Length - 1; i += 2)
{
_currentPhase += phaseStep;
short currentSample = (short)(Math.Sin(_currentPhase) * (double)short.MaxValue);
buffer[i] = currentSample;
buffer[i + 1] = currentSample;
}
}
来源:https://stackoverflow.com/questions/28013797/xaudio2-play-generated-sine-when-changing-frequency-clicking-sound