РЕДАКТИРОВАТЬ 2 + Ответ
Оказывается, мне нужно было заключить в скобки между 0
и Frequency*2*Math.pi
.
Все, кто писал, участвовали в выяснении этой проблемы.Поскольку у гостя была самая низкая репутация, я просто пометил его пост как ответ.Огромное спасибо всем, это сводило меня с ума!
РЕДАКТИРОВАТЬ 1
Вот мой метод WrapValue, я должен был подумать опубликовать это раньше.Это не так сложно, как у Криса Тейлора, но оно оказывает тот же эффект на мой конец.
public static double WrapValue(double value, double min, double max)
{
if (value > max)
return (value - max) + min;
if (value < min)
return max - (min - value);
return value;
}
Это может быть уместно для Gamedev, но это меньше game-y и больше кода и математики-я так я положил здесь.
Я пытаюсь превратить свой Xbox в цифровой инструмент, используя новый класс XNA 4.0 DynamicSoundEffectInstance, и я получаю щелчок каждую секунду.Я определил, что это вызвано любой попыткой обернуть значение моего домена между 0 и 2 * pi ..
. Я написал небольшой класс под названием SineGenerator, который просто поддерживает DynamicSoundEffectInstance и передает ему образцы буферов, сгенерированные с * 1023.*.
Поскольку я хочу быть точным и использовать частоту дискретизации 44,1 или 48 тыс., Я сохраняю double x
(угол, на который я кормлю Math.Sin()
) и double step
, где step
это 2 * Math.PI / SAMPLING_FREQUENCY
.Каждый раз, когда я генерирую данные для DynamicSoundEffectInstance.SubmitBuffer (), я увеличиваю x
на step
и добавляю sin(frequency * x)
в свой буфер выборок (усекается до short
, поскольку XNA поддерживает только 16-битную глубину выборки).
Полагаю, мне лучше обернуть угол между 0 и 2 * пи, чтобы я не терял точность для x
, так как он становится большим.Однако при этом вводится щелчок.Я написал свой собственный метод double WrapValue(double val, double min, double max)
на случай, если MathHelper.WrapAngle () пошло не так.Ни переход между Math.PI и -Math.PI, ни 0 и 2 * Math.PI не избавят от щелчка.Однако, если я не буду пытаться обернуть значение и просто позволить ему расти, щелчок исчезнет.
Я думаю, что это как-то связано с точностью функций триггера .NET, как грех (0)! = Sin (2 * pi), но я не знаю достаточно, чтобы судить.
Мой вопрос: Почему это происходит, и стоит ли мне беспокоиться об этом?
Код:
using System;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework;
namespace TestDynAudio
{
class SineGenerator
{
// Sample rate and sample depth variables
private const int BUFFER_SAMPLE_CAPACITY = 1024;
private const int BIT_RATE = 16;
private const int SAMPLING_FREQUENCY = 48000;
private readonly int BYTES_PER_SAMPLE;
private readonly int SAMPLE_BUFFER_SIZE;
private DynamicSoundEffectInstance dynSound = new DynamicSoundEffectInstance(SAMPLING_FREQUENCY, AudioChannels.Mono);
private double x = 0; // The domain or angle value
private double step; // 48k / 2pi, increment x by this for each sample generated
private byte[] sampleData; // The sample buffer
private double volume = 1.0f; // Volume scale value
// Property for volume
public double Volume
{
get { return volume; }
set { if (value <= 1.0 && value >= 0.0) volume = value; }
}
// Property for frequency
public double Frequency { get; set; }
public SineGenerator()
{
Frequency = 440; // Default pitch set to A above middle C
step = Math.PI * 2 / SAMPLING_FREQUENCY;
BYTES_PER_SAMPLE = BIT_RATE / 8;
SAMPLE_BUFFER_SIZE = BUFFER_SAMPLE_CAPACITY * BYTES_PER_SAMPLE;
sampleData = new byte[SAMPLE_BUFFER_SIZE];
// Use the pull-method, DynamicSoundEffectInstance will
// raise an event when more samples are needed
dynSound.BufferNeeded += GenerateAudioData;
}
private void buildSampleData()
{
// Generate a sample with sin(frequency * domain),
// Convert the sample from a double to a short,
// Then write the bytes to the sample buffer
for (int i = 0; i < BUFFER_SAMPLE_CAPACITY; i++)
{
BitConverter.GetBytes((short)((Math.Sin(Frequency * x) * (double)short.MaxValue) * volume)).CopyTo(sampleData, i * 2);
// Simple value wrapper method that takes into account the
// different between the min/max and the passed value
x = MichaelMath.WrapValue(x + step, 0, 2f * (Single)Math.PI);
}
}
// Delegate for DynamicSoundInstance, generates samples then submits them
public void GenerateAudioData(Object sender, EventArgs args)
{
buildSampleData();
dynSound.SubmitBuffer(sampleData);
}
// Preloads a 3 sample buffers then plays the DynamicSoundInstance
public void play()
{
for (int i = 0; i < 3; i++)
{
buildSampleData();
dynSound.SubmitBuffer(sampleData);
}
dynSound.Play();
}
public void stop()
{
dynSound.Stop();
}
}
}