C#XNAダイナミックオーディオのラジアン角度をラッピングします
-
13-10-2019 - |
質問
編集2 +回答
私は間に包む必要があることがわかりました 0
と Frequency*2*Math.pi
.
投稿したすべての人は、この問題の把握に貢献しました。ゲストの評判は最も低かったので、私は彼の投稿を答えとしてマークしました。みなさん、どうもありがとうございました、これは私を夢中にさせていました!
編集1
これが私のラップバリュー方法です。これを前に投稿することを考えていたはずです。クリス・テイラーほど洗練されていませんが、私の終わりにも同じ効果があります。
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()
)そしてa double step
どこ step
は 2 * Math.PI / SAMPLING_FREQUENCY
. 。 DynamicSoundEffectInstance.submitbuffer()のデータを生成するたびにI増分 x
に step
そして追加します sin(frequency * x)
私のサンプルバッファーに(aに切り捨てられます short
XNAは16ビットのサンプル深さのみをサポートするためです)。
私は0〜2*piの間で角度を包む方が良いので、私は精度を失いません x
大きくなるにつれて。ただし、これを行うとクリックが紹介されます。私は自分のものを書きました double WrapValue(double val, double min, double max)
Mathhelper.wrapangle()がscigkyであった場合の方法。 Math.piと-math.piの間のラッピングも0と2*Math.piもクリックを取り除きません。ただし、値をラップして成長させることを気にしないと、クリックが消えます。
.NETトリガー関数の正確性、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();
}
}
}
解決
ラップ機能は正常に機能すると推測していますが、サイン(x)を服用していません(x)sine(頻度*x) - 周波数*xが2*piのラウンド倍数を生成しない場合、ポップを取得します。周波数*xをラップして、ポップを取り除いてください。
他のヒント
あなたはあなたのラップ機能を表示しませんでした、私は次のように聞こえるクリックを与えなかったというクイックテストをしました。
私のクイックラップ機能
public static float Wrap(float value, float lower, float upper)
{
float distance = upper - lower;
float times = (float)System.Math.Floor((value - lower) / distance);
return value - (times * distance);
}
このように呼ばれます
x = Wrap((float)(x + step), 0, 2 * (float)Math.PI);
ラップ機能のためにダブルをフロートに変換することにより、精密エラーを引き起こす可能性がありますか?とにかくこれの目的は何ですか?あなたはもともとダブルを使用していて、ダブルバックを取得しているからです。結局のところ、1秒あたり48K変換を行っており、エラーが蓄積されます。 XSTEPが2piを超える場合、ラップ機能も機能しませんが、それがどのように起こるかわかりません...
フロートとしてキャストを停止し、問題が解決しない場合は、X2変数を作成し、値が異なる理由を確認するためにブレークポイントを設定することをお勧めします。
// declarations
private double x2 = 0;
private byte[] sampleData2 = new byte[BUFFER_SAMPLE_CAPACITY * BYTES_PER_SAMPLE];
// replace your for loop with this and set a breakpoint on
// the line with Console.WriteLine()
for (int i = 0; i < BUFFER_SAMPLE_CAPACITY; i++)
{
BitConverter.GetBytes((short)((Math.Sin(Frequency * x) * (double)short.MaxValue) * volume)).CopyTo(sampleData, i * 2);
BitConverter.GetBytes((short)((Math.Sin(Frequency * x2) * (double)short.MaxValue) * volume)).CopyTo(sampleData2, i * 2);
if (sampleData[i * 2] != sampleData2[i * 2] || sampleData[i * 2 + 1] != sampleData2[i * 2 + 1]) {
Console.WriteLine("DIFFERENT VALUES!");
}
// 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);
x2 = x2 + step;
}