XAudio2 - Воспроизведение сгенерированного синуса, при изменении частоты нажатия звука
Я хочу разработать приложение, соответствующее вашей частоте звон в ушах: частота воспроизводится, и пользователь уменьшает или увеличивает частоту, нажимая кнопку плюс или минус. (см. часть кодов, основанную на некотором кодировании из stackru 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);
}
Воспроизведение частоты - это хорошо, но когда пользователь нажимает кнопку, вы слышите щелчок при обновлении частоты. Ты хоть представляешь, что делает звук и как я могу от него избавиться? Благодарю.
3 ответа
Когда вы меняете частоту, вы вызываете разрыв в форме волны, которая проявляется в виде щелчка. Вместо того, чтобы рассчитывать сигналы по абсолютному времени, вы должны отслеживать фазу расчета синуса (например, значение от 0 до 2* пи) и выяснить, сколько вам нужно добавить к вашей фазе (вычитая 2* пи). каждый раз, когда вы превышаете 2* пи) для следующего семпла при воспроизведении определенной частоты. Таким образом, когда вы изменяете частоту, фаза, которую вы предоставляете в качестве параметра для Math.Sin
не меняется внезапно, вызывая щелчок.
Расширяя ответ, который дал @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;
}
Вот пример, как вы можете избавиться от нажатия. Вместо использования времени вы должны отслеживать текущую фазу и вычислять, насколько фаза изменяется на требуемой частоте. Также это _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;
}
}