我想开发一个与您的耳鸣频率相匹配的应用:播放频率,用户通过按加号或减号按钮来降低或增加频率。 (参见代码的一部分,基于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);
}
播放频率很好,但是当用户按下按钮时,您可以在频率更新时点击声音。你知道是什么让声音和我如何摆脱它吗? 谢谢。
答案 0 :(得分:1)
当您更改频率时,您会导致波形中出现点击不连续。您应该跟踪正弦计算的相位(例如,从0到2 * pi的值),而不是根据绝对时间进行信号计算,并计算出需要向相位添加多少(减去2 * pi)每次超过2 * pi时播放特定频率的下一个样本。这样,当您更改频率时,您作为Math.Sin
参数提供的阶段不会突然发生变化而导致点击。
答案 1 :(得分:0)
扩展@spender给出的答案(我需要50个代表来为他的答案添加评论),我在naudio遇到了类似的问题。我能够通过添加两个bool值来解决问题,这两个值监视正弦值的当前符号和前一个正弦值的符号。如果前一个正弦为负且当前正弦为正,我们知道我们可以安全地调整正弦波的频率。
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;
}
答案 2 :(得分:0)
以下是一个如何摆脱点击的示例。您应该跟踪当前相位并计算相位在所需频率上的变化程度,而不是使用时间。此_currentPhase
也必须是持久的,因此它将具有先前的值。 (在方法中声明它会导致点击(在大多数频率上)
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;
}
}