【发布时间】:2010-12-29 11:34:39
【问题描述】:
编辑 2 + 答案
原来我需要在0 和Frequency*2*Math.pi 之间进行换行。
所有发帖的人都为解决这个问题做出了贡献。由于客人的声誉最低,我只是将他的帖子标记为答案。非常感谢大家,这让我发疯了!
编辑 1
这是我的 WrapValue 方法,之前应该考虑过发布。它不像 Chris Taylor 那样复杂,但它对我的影响是一样的。
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,但它不是游戏性的,而是更多的代码和数学,所以我把它放在这里。
我正在尝试使用新的 XNA 4.0 DynamicSoundEffectInstance 类将我的 Xbox 变成一个数字乐器,并且我每秒都会收到一次点击。我已经确定这是由于任何尝试将我的域值包装在 0 和 2*pi 之间造成的。
我编写了一个名为 SineGenerator 的小类,它只维护一个 DynamicSoundEffectInstance 并将使用Math.Sin() 生成的样本缓冲区提供给它。
因为我想要精确并使用 44.1 或 48k 采样率,所以我保留了 double x(我输入的角度 Math.Sin())和 double step,其中 step 是 2 * Math.PI / SAMPLING_FREQUENCY .每次我为 DynamicSoundEffectInstance.SubmitBuffer() 生成数据时,我都会将 x 增加 step 并将 sin(frequency * x) 添加到我的样本缓冲区(截断为 short,因为 XNA 仅支持 16 位样本深度)。
我认为我最好将角度包裹在 0 和 2*pi 之间,这样我就不会因为 x 变大而失去精度。但是,这样做会引入点击。我编写了自己的 double WrapValue(double val, double min, double max) 方法,以防 MathHelper.WrapAngle() 搞砸了。 Math.PI 和 -Math.PI 或 0 和 2*Math.PI 之间的环绕都不会消除点击。但是,如果我不费心包装该值并让它增长,那么点击就会消失。
我认为这与 .NET trig 函数的准确性有关,如何 sin(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();
}
}
}
【问题讨论】:
-
你说如果你让它生长它就会消失;这是否意味着如果您从 0 开始并从那里出去,则根本没有点击,或者它在某个时候消失?您是否尝试过其他 2*pi 间隔,如 1*pi - 3*pi,或 2C*pi,如 100*pi 至 200*pi?
-
我尝试了 2*pi 的倍数,但不幸的是它只会增加点击之间的时间。不过谢谢你的建议。
标签: c# audio xna trigonometry sampling