首页 > 解决方案 > 连续调用 Console.Beep() 之间的延迟

问题描述

我正在尝试制作一个简单的程序来生成可以根据输入值变化的音调。我想起诉 Console.Beep() 函数,因为它似乎是最简单的解决方案,但遇到了问题。我注意到当我连续调用 Console.beep() 函数时,如下所示:

System.Console.Beep(440, 500);
System.Console.Beep(460, 500);
System.Console.Beep(480, 500);

在当前音调停止和下一个音调开始之间有一个小的延迟(可能是 100-200 毫秒左右)。我想知道是否有任何方法可以减少这种延迟,以便在更新频率时音调不会出现暂时暂停?

标签: c#.net

解决方案


在尝试了解 .NET 为何会出现不良行为时,查看 GitHub 上的 .NET Core 源代码通常会有所帮助。查看Console.Beep 的源代码

    public static void Beep(int frequency, int duration)
    {
        ConsolePal.Beep(frequency, duration);
    }

有两种实现ConsolePal:一种用于 Windows,另一种用于类 Unix 系统。假设你在 Windows 上,让我们看看下面Beep实现ConsolePal.Windows.cs

    public static void Beep(int frequency, int duration)
    {
        const int MinBeepFrequency = 37;
        const int MaxBeepFrequency = 32767;

        if (frequency < MinBeepFrequency || frequency > MaxBeepFrequency)
            throw new ArgumentOutOfRangeException(nameof(frequency), frequency, SR.Format(SR.ArgumentOutOfRange_BeepFrequency, MinBeepFrequency, MaxBeepFrequency));
        if (duration <= 0)
            throw new ArgumentOutOfRangeException(nameof(duration), duration, SR.ArgumentOutOfRange_NeedPosNum);

        Interop.Kernel32.Beep(frequency, duration);
    }

这里的关键是调用Interop.Kernel32.Beep. 这是我们即将调用本机 Windows API 的一个强有力的指标,但让我们验证一下。 被分割成一堆文件Interop.Kernel32——通常这可能会给我们带来一些困难,但碰巧列表中的第一个是——方便!Interop.Beep.cs

打开文件,我们对本地调用的怀疑得到了证实

    [DllImport(Libraries.Kernel32, SetLastError = true)]
    internal static extern bool Beep(int frequency, int duration);

这是使用一种称为平台调用的技术,通常缩写为 P/Invoke。有一个很棒的站点 pinvoke.net,它提供了常见的 P/Invoke 片段以及一些附带的实现。让我们看看pinvoke.net 对 kernel32.dll 中的 Beep 有什么看法

与 MessageBeep 不同,此函数是同步的。(在声音结束之前,它不会将控制权返回给它的调用者。)

由于您的代码中没有其他内容或Console.Beep会导致延迟,这意味着 Windows API 可能是罪魁祸首。幸运的是,pinvoke.net 提供了一种可行的解决方法。为了确保这一点,让我们查看MSDN 在 Windows 上的文档Beep,pinvoke.net 可以方便地链接到该文档。它带有一些非常迷人的历史Beep,以及切换到的建议MessageBeep

不幸的是,MSDN 文档MessageBeep令人失望:我们不能使用它来生成任意音调。

这使我们得出了最终结论:kernel32Beep并不是真正用于生成花哨的声音。它既依赖于硬件,也依赖于平台,如果您试图在各种设备上生成一致的声音,这两者都不是理想的。它本质上是过去的遗物。它甚至在 Windows XP 中被删除,但后来在 Windows 7 中又被添加回来。这意味着 .NETConsole.Beep(int, int)具有所有相同的问题,因为它只是 kernel32 的包装器Beep

一个更强大的解决方案是研究各种音频框架,但这些可能会复杂得多。目前,如果您刚开始使用 C# 并希望保持简单,您可能会遇到不一致的情况,例如您观察到的延迟。


推荐阅读