从麦克风无损读取

Posted

技术标签:

【中文标题】从麦克风无损读取【英文标题】:Lossless reading from mic 【发布时间】:2011-07-13 14:07:26 【问题描述】:

我正在使用 NAudio(但它适用于直接读取)来捕获麦克风波数据。似乎如果我的应用程序很忙,它会丢弃/跳过麦克风中的一些输入数据。

我已将读取线程设置为最高优先级,但我同时在其他几个线程中进行大量计算。

有没有办法无损读取数据? (或者它是无损的,而我的错误在别处?)

【问题讨论】:

【参考方案1】:

当我在制作一个类似的应用程序并遇到类似的问题时,结果证明我需要一个可以容纳至少 3 秒数据的缓冲区。尝试将缓冲区增加到 10 秒的数据,如果它不能解决您的问题,那么还有更多问题。如果它有效,请尝试减小缓冲区大小,直到它正常工作

编辑:这里有一个快速而肮脏的托管 dx 记录供您尝试。

    public class BMSRecordingEventArgs : EventArgs

    byte[] data;
    bool endRec;

    public BMSRecordingEventArgs(byte[] data, bool endRec)
    
        this.data = data;
        this.endRec = endRec;
    
    public byte[] Data
    
        get  return data; 
    
    public bool EndRec
    
        get  return endRec; 
    

public class AudioRecorder

    public delegate void DataReceivedHandler(object sender, BMSRecordingEventArgs e);
    public event DataReceivedHandler DataReceivedHandle;

    public const int CAPTURE_BUFFER_SIZE = 32000;
    DXS.Capture dxsCapDev;
    DXS.CaptureBuffer dxsCapBuffer;
    DXS.CaptureBufferDescription dxsCapBufferDesc;
    System.Threading.Thread thrdCapturingThread;
    DXS.BufferPositionNotify[] dxsBpna;
    private volatile bool StopRec;

    System.Threading.ManualResetEvent mreStillRunning = new System.Threading.ManualResetEvent(false);
    DXS.BufferPositionNotify dxsBPNHalf;
    DXS.BufferPositionNotify dxsBPNFull;
    DXS.Notify Notify;
    System.Threading.AutoResetEvent ARE;

    public AudioRecorder(Guid DeviceGuid,DXS.WaveFormat wfWaveFormat,DXS.CaptureEffectDescription[] dxsCapEffectDesc)
    

        dxsCapDev = new Microsoft.DirectX.DirectSound.Capture(DeviceGuid);
        dxsCapBufferDesc = new Microsoft.DirectX.DirectSound.CaptureBufferDescription();
        dxsCapBufferDesc.BufferBytes = CAPTURE_BUFFER_SIZE;
        dxsCapBufferDesc.Format = wfWaveFormat;
        dxsCapBufferDesc.WaveMapped = true;
        dxsCapBufferDesc.CaptureEffectDescription = dxsCapEffectDesc;
        dxsCapBufferDesc.ControlEffects = true;


        dxsCapBuffer = new Microsoft.DirectX.DirectSound.CaptureBuffer(dxsCapBufferDesc, dxsCapDev);

        ARE = new System.Threading.AutoResetEvent(false);
        dxsBPNHalf = new Microsoft.DirectX.DirectSound.BufferPositionNotify();
        dxsBPNFull = new Microsoft.DirectX.DirectSound.BufferPositionNotify();
        dxsBPNHalf.Offset = CAPTURE_BUFFER_SIZE / 2 - 1;
        dxsBPNFull.Offset = CAPTURE_BUFFER_SIZE-1;
        dxsBPNFull.EventNotifyHandle = ARE.SafeWaitHandle.DangerousGetHandle();
        dxsBPNHalf.EventNotifyHandle = ARE.SafeWaitHandle.DangerousGetHandle();


        dxsBpna = new Microsoft.DirectX.DirectSound.BufferPositionNotify[2];
        dxsBpna[0] = dxsBPNHalf;
        dxsBpna[1] = dxsBPNFull;

        Notify = new Microsoft.DirectX.DirectSound.Notify(dxsCapBuffer);
        Notify.SetNotificationPositions(dxsBpna);

    

    public void StartRecording()
    
        if (thrdCapturingThread != null)
            throw new Exception("Already Recording !");
        StopRec = false;
        thrdCapturingThread = new System.Threading.Thread(Record);
        thrdCapturingThread.Start();


    
    private void Record()
    
        DataReceivedHandler drh2 = DataReceivedHandle;


        dxsCapBuffer.Start(true);
        byte[] TempBaf = new byte[CAPTURE_BUFFER_SIZE / 2];
        int StartingOffset = 0;
        while (dxsCapBuffer.Capturing && !StopRec)
        
            ARE.WaitOne(-1,false);

                StartingOffset %= CAPTURE_BUFFER_SIZE;
                TempBaf = (byte[])dxsCapBuffer.Read(StartingOffset, typeof(byte), Microsoft.DirectX.DirectSound.LockFlag.FromWriteCursor, CAPTURE_BUFFER_SIZE / 2);
                StartingOffset += TempBaf.Length;
                if (drh2 != null)
                    drh2(this, new BMSRecordingEventArgs(TempBaf, false));

        
        dxsCapBuffer.Stop();
        if (drh2 != null)
            drh2(this, new BMSRecordingEventArgs(TempBaf, true));

        mreStillRunning.Set();


    
    public void StopRecording()
    
        StopRec = true;
        mreStillRunning.WaitOne(-1,false);
        thrdCapturingThread = null;
    

【讨论】:

传递数据的线程只对它进行缓冲,然后一个单独的线程使用优化的缓冲系统进行快速拾取以减少使用时间。它与具有轻量内存配置文件的 StringBuilder 类似的概念(删除部分块时不复制)。所以我认为麦克风读取线程正在有效地缓冲数据。我正在缓冲高达 32MB 的内存,并且不会溢出。 尝试使用 Managed DirectX(或者甚至更好——常规的)进行录制,这相当容易。没有代码就帮不了你了:) 好的,谢谢。当我有时间时,我会尝试实施您的示例(因为它似乎不是一个明确的解决方案)。我想问题出在操作系统架构上。我发现我在缓冲数据之前将数据转换为 Double,所以我可以在那里进行一些迭代。我先试试看。 好像我忘记了,但我前段时间在我的博客上发布了我的缓冲类。 blog.tedd.no/2011/06/02/arraybuilderqueue 这不是一个坏主意,但是以它的编写方式,您不应该在记录完成之前执行读取,因为锁定可能会导致数据丢失。尝试使用 System.Collections.Generic.Queue<T> 不需要锁,因为它是 MT 安全的,除非您正在迭代它。

以上是关于从麦克风无损读取的主要内容,如果未能解决你的问题,请参考以下文章

如何从麦克风快速(超声波)读取音频?

音频流 - 捕获和读取麦克风数据

python ANSI艺术谱图查看器,用于从麦克风读取音频

PortAudio:如何从麦克风获取记录(获取数据)

从 C 中的麦克风获取原始数据块

在 Java 中从麦克风播放音频时减少延迟