簡體   English   中英

通過XAudio2播放鼻竇

[英]Playing sinus through XAudio2

我正在使用XAudio2制作音頻播放器。 我們以640Hz的數據包流式傳輸數據,采樣率為8000Hz,采樣深度為16個字節。 我們正在使用SlimDX來訪問XAudio2。

但是在播放聲音時,我們注意到聲音質量很差。 例如,這是用Audacity捕獲的3KHz正弦曲線。 3KHz竇曲線

我已經將音頻播放器壓縮為基本內容,但是音頻質量仍然很差。 這是XAudio2,SlimDX或我的代碼中的錯誤,還是僅僅是當人們從8KHz變為44.1KHz時發生的偽像? 最后一個似乎不合理,因為我們還會生成PCM wav文件,這些文件可以由Windows Media Player完美播放。

以下是基本的實現,它將生成損壞的Sine。

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中或者包裝器中只是一個錯誤/限制? 我嘗試使用SharpDX代替SlimDX,但這並沒有改變結果。

看起來好像沒有適當的抗混疊(重構)濾波器就完成了升采樣。 截止頻率太高(高於原始奈奎斯特頻率),因此保留了很多混疊,導致輸出類似於在8000 Hz采樣之間的分段線性插值。

盡管您所有不同的選項都在進行從8kHz到44.1kHz的上變頻,但是它們的執行方式很重要,而且一個庫運行良好的事實不能證明上變頻不是另一種中的錯誤源。

自從我處理聲音和頻率已經有一段時間了,但是我記得這里:您的采樣率為8000Hz,想要的正弦頻率為3000Hz。 因此,在1秒鍾內,您有8000個樣本,而在這一秒內,您希望正弦振動3000次。 該頻率低於奈奎斯特頻率(一半采樣率),但幾乎沒有(請參閱奈奎斯特-香農采樣定理 )。 因此,在這里我不會指望優質的產品。

實際上:逐步執行GenerateSine方法,您會看到s[0]將包含值0、5792,-8191、5792、0,-5792、8191,-5792、0、5792 ...

但這並不能解釋您錄制的奇數正弦波,而且我不確定人耳需要多少個樣本才能聽到“良好”的正弦波。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM