Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何避免使用DirectSound和C#将声音从麦克风传输到扬声器时出现无声的滴答声?_C#_.net_Directx_Noise_Directsound - Fatal编程技术网

如何避免使用DirectSound和C#将声音从麦克风传输到扬声器时出现无声的滴答声?

如何避免使用DirectSound和C#将声音从麦克风传输到扬声器时出现无声的滴答声?,c#,.net,directx,noise,directsound,C#,.net,Directx,Noise,Directsound,我尝试使用DirectSound和C将声音样本从麦克风传输到扬声器。它应该类似于“聆听麦克风”,但稍后我想将其用于其他用途。通过测试我的方法,我注意到背景中有无声的滴答声和爆裂声。我猜这与写入和播放缓冲区之间的延迟有关,这必须大于写入块的延迟 如果我将录制和播放之间的延迟设置为小于50毫秒。大多数情况下它都能工作,但有时我会听到很大的爆裂声。所以我决定延迟至少50毫秒。这对我来说还行,但系统“监听设备”的延迟似乎要短得多。我猜大概是15-30毫秒,几乎不明显。对于50毫秒,我至少得到了一点混响效

我尝试使用DirectSoundC将声音样本从麦克风传输到扬声器。它应该类似于“聆听麦克风”,但稍后我想将其用于其他用途。通过测试我的方法,我注意到背景中有无声的滴答声和爆裂声。我猜这与写入和播放缓冲区之间的延迟有关,这必须大于写入块的延迟

如果我将录制和播放之间的延迟设置为小于50毫秒。大多数情况下它都能工作,但有时我会听到很大的爆裂声。所以我决定延迟至少50毫秒。这对我来说还行,但系统“监听设备”的延迟似乎要短得多。我猜大概是15-30毫秒,几乎不明显。对于50毫秒,我至少得到了一点混响效果

在以下内容中,我将向您展示我的麦克风代码(部分): 初始化过程如下所示:

        capture = new Capture(device);

        // Creating the buffer
        // Determining the buffer size
        bufferSize = format.AverageBytesPerSecond * bufferLength / 1000;
        while (bufferSize % format.BlockAlign != 0) bufferSize += 1;
        chunkSize = Math.Max(bufferSize, 256); 
        bufferSize = chunkSize * BUFFER_CHUNKS;
        this.bufferLength = chunkSize * 1000 / format.AverageBytesPerSecond; // Redetermining the buffer Length that will be used.

        captureBufferDescription = new CaptureBufferDescription();
        captureBufferDescription.BufferBytes = bufferSize;
        captureBufferDescription.Format = format;
        captureBuffer = new CaptureBuffer(captureBufferDescription, capture);

        // Creating Buffer control           
        bufferARE = new AutoResetEvent(false);
        // Adding notifier to buffer.
        bufferNotify = new Notify(captureBuffer);
        BufferPositionNotify[] bpns = new BufferPositionNotify[BUFFER_CHUNKS];
        for(int i = 0 ; i < BUFFER_CHUNKS ; i ++) bpns[i] =    new BufferPositionNotify() { Offset = chunkSize * (i+1) - 1, EventNotifyHandle = bufferARE.SafeWaitHandle.DangerousGetHandle() };
        bufferNotify.SetNotificationPositions(bpns); 
        // Creating the dxdevice.
        dxdevice = new Device(device);
        dxdevice.SetCooperativeLevel(hWnd, CooperativeLevel.Normal);

        // Creating the buffer
        bufferDescription = new BufferDescription();
        bufferDescription.BufferBytes = bufferSize;
        bufferDescription.Format = input.Format;
        bufferDescription.ControlVolume = true;

        bufferDescription.GlobalFocus = true; // That sound doesn't stop if the hWnd looses focus.
        bufferDescription.StickyFocus = true; // - " -
        buffer = new SecondaryBuffer(bufferDescription, dxdevice);
        chunkQueue = new Queue<byte[]>();

        // Creating buffer control
        bufferARE = new AutoResetEvent(false);

        // Register at input device
        input.ChunkCaptured += new AInput.ReportBuffer(input_ChunkCaptured);
捕获
ReportChunk
时,会将数据作为可以订阅的事件发送给演讲者。扬声器部件初始化如下:

        capture = new Capture(device);

        // Creating the buffer
        // Determining the buffer size
        bufferSize = format.AverageBytesPerSecond * bufferLength / 1000;
        while (bufferSize % format.BlockAlign != 0) bufferSize += 1;
        chunkSize = Math.Max(bufferSize, 256); 
        bufferSize = chunkSize * BUFFER_CHUNKS;
        this.bufferLength = chunkSize * 1000 / format.AverageBytesPerSecond; // Redetermining the buffer Length that will be used.

        captureBufferDescription = new CaptureBufferDescription();
        captureBufferDescription.BufferBytes = bufferSize;
        captureBufferDescription.Format = format;
        captureBuffer = new CaptureBuffer(captureBufferDescription, capture);

        // Creating Buffer control           
        bufferARE = new AutoResetEvent(false);
        // Adding notifier to buffer.
        bufferNotify = new Notify(captureBuffer);
        BufferPositionNotify[] bpns = new BufferPositionNotify[BUFFER_CHUNKS];
        for(int i = 0 ; i < BUFFER_CHUNKS ; i ++) bpns[i] =    new BufferPositionNotify() { Offset = chunkSize * (i+1) - 1, EventNotifyHandle = bufferARE.SafeWaitHandle.DangerousGetHandle() };
        bufferNotify.SetNotificationPositions(bpns); 
        // Creating the dxdevice.
        dxdevice = new Device(device);
        dxdevice.SetCooperativeLevel(hWnd, CooperativeLevel.Normal);

        // Creating the buffer
        bufferDescription = new BufferDescription();
        bufferDescription.BufferBytes = bufferSize;
        bufferDescription.Format = input.Format;
        bufferDescription.ControlVolume = true;

        bufferDescription.GlobalFocus = true; // That sound doesn't stop if the hWnd looses focus.
        bufferDescription.StickyFocus = true; // - " -
        buffer = new SecondaryBuffer(bufferDescription, dxdevice);
        chunkQueue = new Queue<byte[]>();

        // Creating buffer control
        bufferARE = new AutoResetEvent(false);

        // Register at input device
        input.ChunkCaptured += new AInput.ReportBuffer(input_ChunkCaptured);
填充playbackbuffer和启动/停止播放缓冲区由另一个线程完成:

        // Initializing
        int wp = 0;
        bufferARE.WaitOne(); // wait for first chunk

        // Playing / writing data to play buffer.
        while (isPlaying)
        {
            Thread.Sleep(1);
            bufferARE.WaitOne(BufferLength * 3); // If a chunk is played and there is no new chunk we try to continue and may stop playing, else may the buffer runs out. 
            // Note that this may fails if the sender was interrupted within one chunk
            if (isPlaying)
            {
                if (chunkQueue.Count > 0)
                {
                    while (chunkQueue.Count > 0) wp = writeToBuffer(chunkQueue.Dequeue(), wp);
                    if (buffer.PlayPosition > wp - chunkSize * 3 / 2) buffer.SetCurrentPosition(((wp - chunkSize * 2 + bufferSize) % bufferSize));
                    if (!buffer.Status.Playing)
                    {
                        buffer.SetCurrentPosition(((wp - chunkSize * 2 + bufferSize) % bufferSize));    // We have 2 chunks buffered so we step back 2 chunks and play them while getting new chunks.
                        buffer.Play(0, BufferPlayFlags.Looping);
                    }
                }
                else
                {
                    buffer.Stop();
                    bufferARE.WaitOne(); // wait for a filling chunk
                }
            }
        }

        // Finalizing
        isPlaying = false;
        buffer.Stop();
        stateControlARE.Set();
writeToBuffer
通过
this.buffer.Write(wp,data,LockFlag.None)将排队的块写入缓冲区
和关心
bufferSize
chunkSize
wp
,它们代表最后的写入位置。我认为这是我的代码中最重要的部分。可能缺少定义,至少还有另一个方法可以启动/停止=控制线程


我已经发布了此代码,以防我在填充缓冲区时出错或我的初始化错误。但是我猜出现这个问题是因为C字节码的执行太慢或者类似的东西。但最后我的问题仍然悬而未决:我的问题是如何减少延迟以及如何避免不应有的噪音?我知道问题的原因和解决方法,但我无法在C#和.Net中实现它,因此我将解释它,希望您能找到自己的方法

音频将由麦克风录制。使用指定的频率(例如44100),然后以相同的采样率在声卡上播放(同样是44100),问题在于输入设备(例如麦克风)中计算时间的晶体与声卡中播放声音的晶体不同。 此外,差异是如此之小,它们是不一样的(在整个世界上没有2个完全相同的晶体),所以在一段时间后,将有一个间隙,在您的播放例程


现在的解决方案是重新采样数据以匹配输出的采样率,但我不知道如何在C#和.Net中做到这一点。很久以前,我发现这个问题是由
线程.Sleep(1)引起的与高CPU使用率相结合。由于windows timerresolution默认为15,6ms,此睡眠并不意味着睡眠1ms,而是直到达到下一个时钟中断为止。(用于更多读取)结合高CPU使用率,它的堆栈长度可能达到一个块的长度,甚至更多

例如:如果我的chunksize是40ms,那么这个值可能是46,8ms(3*15,6ms),这会导致tickling。一种解决方案是将分辨率设置为1ms。这可以通过以下方式实现:

[DllImport("winmm.dll", EntryPoint="timeBeginPeriod", SetLastError=true)]
private static extern uint timeBeginPeriod(uint uiPeriod);

[DllImport("winmm.dll", EntryPoint="timeEndPeriod", SetLastError=true)]
private static extern uint timeEndPeriod(uint uiPeriod);

void routine()
{
   Thead.Sleep(1); // May takes about 15,6ms or even longer.
   timeBeginPeriod(1); // Should be set at the startup of the application.
   Thead.Sleep(1); // May takes about 1, 2 or 3 ms depending on the CPU usage.

   // ... time depending routines goes here ...

   timeEndPeriod(1); // Should end at application shutdown.
}
据我所知,这应该已经由directx完成。但由于此设置是全局设置,应用程序的其他部分或其他应用程序可能会更改它。如果应用程序设置并撤销设置一次,则不应发生这种情况。但不知何故,这似乎是由任何肮脏的编程部分或其他运行的应用程序引起的


另外一件需要注意的事情是,如果出于任何原因跳过了一个数据块,您是否仍在使用directx缓冲区的正确位置。在这种情况下,需要重新同步。

电脑输入往往有噪音,尤其是车载音频硬件。音频芯片通常离实际插孔有一段距离,主板上的痕迹会拾取各种噪音。这种噪音是不可避免的,因为它是一个物理问题,而不是由软件引起的。你所说的简单的白噪音不是我的问题。我的问题是有时会发生无声的破裂。当我在系统设置中使用“收听设备”时,不会出现这种情况。你如何定义“静音”?有一点“明显”:我会将它与距离麦克风几米远的人进行比较,然后慢慢地合上拉链,这样你有时会听到它的咔嗒声。这种噪音不是主要的,但如果你试着去听,你会听到的。尤其是当我大声说话时,几乎是过度调制。我听说过,但据我所知,这应该由声卡驱动程序或至少由directx解决。如果样品中填充了零,没有人会注意到一个丢失的样品。顺便说一句,自从一年前有人问这个问题以来,我已经找到了答案,我会补充一下。