6

XAudio2 を使用してオーディオ プレーヤーを作成しています。サンプル レート 8000Hz、サンプル深度 16 バイトで、データを 640 バイトのパケットでストリーミングしています。SlimDX を使用して XAudio2 にアクセスしています。

しかし、サウンドを再生すると、音質が悪いことに気づきます。たとえば、これは Audacity でキャプチャされた 3KHz の正弦曲線です。 3KHz正弦曲線

オーディオプレーヤーを最低限の基本に凝縮しましたが、音質はまだ悪いです。これは XAudio2、SlimDX、または私のコードのバグですか、それとも単に 8KHz から 44.1KHz に移行したときに発生するアーティファクトですか? Windows Media Player で完全に再生される PCM wav ファイルも生成するため、最後のものは不合理に思えます。

以下は、壊れたサインを生成する基本的な実装です。

public partial class MainWindow : Window
{
    private XAudio2 device = new XAudio2();
    private WaveFormatExtensible format = new WaveFormatExtensible();
    private SourceVoice sourceVoice = null;
    private MasteringVoice masteringVoice = null;
    private Guid KSDATAFORMAT_SUBTYPE_PCM = new Guid("00000001-0000-0010-8000-00aa00389b71");
    private AutoResetEvent BufferReady = new AutoResetEvent(false);

    private PlayBufferPool PlayBuffers = new PlayBufferPool();

    public MainWindow()
    {
        InitializeComponent();

        Closing += OnClosing;

        format.Channels = 1;
        format.BitsPerSample = 16;
        format.FormatTag = WaveFormatTag.Extensible;
        format.BlockAlignment = (short)(format.Channels * (format.BitsPerSample / 8));
        format.SamplesPerSecond = 8000;
        format.AverageBytesPerSecond = format.SamplesPerSecond * format.BlockAlignment;
        format.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
    }

    private void OnClosing(object sender, CancelEventArgs cancelEventArgs)
    {
        sourceVoice.Stop();
        sourceVoice.Dispose();
        masteringVoice.Dispose();

        PlayBuffers.Dispose();
    }

    private void button_Click(object sender, RoutedEventArgs e)
    {
        masteringVoice = new MasteringVoice(device);

        PlayBuffer buffer = PlayBuffers.NextBuffer();

        GenerateSine(buffer.Buffer);
        buffer.AudioBuffer.AudioBytes = 640;

        sourceVoice = new SourceVoice(device, format, VoiceFlags.None, 8);
        sourceVoice.BufferStart += new EventHandler<ContextEventArgs>(sourceVoice_BufferStart);
        sourceVoice.BufferEnd += new EventHandler<ContextEventArgs>(sourceVoice_BufferEnd);

        sourceVoice.SubmitSourceBuffer(buffer.AudioBuffer);

        sourceVoice.Start();
    }

    private void sourceVoice_BufferEnd(object sender, ContextEventArgs e)
    {
        BufferReady.Set();
    }

    private void sourceVoice_BufferStart(object sender, ContextEventArgs e)
    {
        BufferReady.WaitOne(1000);

        PlayBuffer nextBuffer = PlayBuffers.NextBuffer();
        nextBuffer.DataStream.Position = 0;
        nextBuffer.AudioBuffer.AudioBytes = 640;
        GenerateSine(nextBuffer.Buffer);

        Result r = sourceVoice.SubmitSourceBuffer(nextBuffer.AudioBuffer);
    }

    private void GenerateSine(byte[] buffer)
    {
        double sampleRate = 8000.0;
        double amplitude = 0.25 * short.MaxValue;
        double frequency = 3000.0;
        for (int n = 0; n < buffer.Length / 2; n++)
        {
            short[] s = { (short)(amplitude * Math.Sin((2 * Math.PI * n * frequency) / sampleRate)) };
            Buffer.BlockCopy(s, 0, buffer, n * 2, 2);
        }
    }
}

public class PlayBuffer : IDisposable
{
    #region Private variables
    private IntPtr BufferPtr;
    private GCHandle BufferHandle;
    #endregion

    #region Constructors
    public PlayBuffer()
    {
        Index = 0;
        Buffer = new byte[640 * 4]; // 640 = 30ms
        BufferHandle = GCHandle.Alloc(this.Buffer, GCHandleType.Pinned);
        BufferPtr = new IntPtr(BufferHandle.AddrOfPinnedObject().ToInt32());

        DataStream = new DataStream(BufferPtr, 640 * 4, true, false);
        AudioBuffer = new AudioBuffer();
        AudioBuffer.AudioData = DataStream;
    }

    public PlayBuffer(int index)
        : this()
    {
        Index = index;
    }
    #endregion

    #region Destructor
    ~PlayBuffer()
    {
        Dispose();
    }
    #endregion

    #region Properties
    protected int Index { get; private set; }
    public byte[] Buffer { get; private set; }
    public DataStream DataStream { get; private set; }
    public AudioBuffer AudioBuffer { get; private set; }
    #endregion

    #region Public functions
    public void Dispose()
    {
        if (AudioBuffer != null)
        {
            AudioBuffer.Dispose();
            AudioBuffer = null;
        }

        if (DataStream != null)
        {
            DataStream.Dispose();
            DataStream = null;
        }
    }
    #endregion
}

public class PlayBufferPool : IDisposable
{
    #region Private variables
    private int _currentIndex = -1;
    private PlayBuffer[] _buffers = new PlayBuffer[2];
    #endregion

    #region Constructors
    public PlayBufferPool()
    {
        for (int i = 0; i < 2; i++)
            Buffers[i] = new PlayBuffer(i);
    }
    #endregion

    #region Desctructor
    ~PlayBufferPool()
    {
        Dispose();
    }
    #endregion

    #region Properties
    protected int CurrentIndex
    {
        get { return _currentIndex; }
        set { _currentIndex = value; }
    }

    protected PlayBuffer[] Buffers
    {
        get { return _buffers; }
        set { _buffers = value; }
    }
    #endregion

    #region Public functions
    public void Dispose()
    {
        for (int i = 0; i < Buffers.Length; i++)
        {
            if (Buffers[i] == null)
                continue;

            Buffers[i].Dispose();
            Buffers[i] = null;
        }
    }

    public PlayBuffer NextBuffer()
    {
        CurrentIndex = (CurrentIndex + 1) % Buffers.Length;
        return Buffers[CurrentIndex];
    }
    #endregion
}

追加の詳細:

これは、ALAW、μLAW、TrueSpeech などのさまざまな圧縮で録音された音声を再生するために使用されます。データは小さなパケットで送信され、デコードされてこのプレーヤーに送信されます。これが、サンプリング レートが非常に低く、バッファが非常に小さい理由です。ただし、データを使用して WAV ファイルを生成すると、WMP または VLC によって完全に再生されるため、データに問題はありません。

編集: NAudio でプレーヤーを書き直すことで、これを「解決」しました。ここで何が起こっているのかについての情報にはまだ興味があります。PlayBuffers での私たちのアプローチですか、それとも DirectX またはラッパーの単なるバグ/制限ですか? SlimDX の代わりに SharpDX を使用してみましたが、結果は何も変わりませんでした。

4

2 に答える 2

2

適切なアンチエイリアシング (再構築) フィルターなしでアップサンプリングが行われたように見えます。カットオフ周波数が高すぎる (元のナイキスト周波数より高い) ため、多くのエイリアスが保持され、8000 Hz で取得されたサンプル間の区分的線形補間に似た出力が得られます。

さまざまなオプションがすべて 8kHz から 44.1kHz へのアップコンバージョンを行っていますが、それを行う方法は重要であり、1 つのライブラリがそれをうまく行っているという事実は、アップコンバージョンが他のライブラリのエラーの原因ではないという証拠にはなりません。

于 2012-09-26T13:29:22.127 に答える
0

サウンドと周波数を扱ってからしばらく経ちましたが、覚えていることは次のとおりです。8000Hz のサンプル レートがあり、3000Hz の正弦波周波数が必要です。したがって、1 秒間に 8000 個のサンプルがあり、その 1 秒間に正弦波を 3000 回振動させる必要があります。これはナイキスト周波数 (サンプルレートの半分) を下回っていますが、かろうじて (ナイキスト・シャノンのサンプリング定理を参照)。だから私はここで良い品質を期待していません。

実際: -メソッドをステップGenerateSine実行するs[0]と、値 0、5792、-8191、5792、0、-5792、8191、-5792、0、5792... が含まれていることがわかります。

それでもなお、これはあなたが録音した奇妙な正弦波を説明するものではなく、人間の耳が「良い」正弦波を聞くのにどれだけのサンプルが必要かはわかりません.

于 2012-09-26T10:05:01.160 に答える