MIDI 和 DirectMusic 组件

应用程序依赖于用户和内核模式组件的组合来捕获和播放 MIDI 和 DirectMusic 流。

应用程序可以使用以下任一软件接口进行 MIDI 播放和捕获:

  • Microsoft Windows 多媒体 midiOutXxxmidiInXxx 函数

  • DirectMusic API

midiOutXxxmidiInXxx 函数的行为基于旧版 MIDI 驱动程序和设备的功能。 从 Windows 98 开始,WDMAud 系统驱动程序会将对这些函数的调用转换为 WDM 音频驱动程序的命令。 但是,通过模拟旧软件和硬件的行为,midiOutXxxmidiInXxx 函数会牺牲现在通过 DirectMusic API 提供的精度计时和增强功能。 有关 DirectMusic 和 Windows 多媒体 MIDI 函数的详细信息,请参阅 Microsoft Windows SDK 文档。

DirectMusic 和 Windows 多媒体 MIDI 函数是 SysAudio 系统驱动程序的客户端,可生成处理 MIDI 和 DirectMusic 流的音频筛选器图。 图形生成对于使用这些软件接口的应用程序是透明的。

MIDI 组件

下图显示了 MIDI 应用程序用于播放 MIDI 数据的用户模式和内核模式组件。 此应用程序通过 midiOutXxx 函数连接到 WDM 音频驱动程序,这些函数在 WinMM 系统组件 Winmm.dll 中实现。

Diagram illustrating MIDI playback user-mode and kernel-mode components.

上图中的 MIDI 应用程序从 MIDI 文件读取带时间戳的 MIDI 事件并播放这些事件。 MIDI 和 DMus 微型端口驱动程序显示为深色框,以指示它们可以是供应商提供的组件。 如果合适,供应商可能会选择使用系统提供的微型端口驱动程序之一,即 FMSynth、UART 或 DMusUART,而不是编写自定义微型端口驱动程序。 图中的所有其他组件都由系统提供。

典型的 MIDI 播放应用程序的主循环会调用 timeSetEvent 来计划下一个注释打开或注释关闭事件。 此调用采用其参数之一,即指向应用程序回调例程的函数指针。 事件发生时,操作系统会调用回调例程,此例程会调用 midiOutShortMsg,以打开或关闭一个或多个计划注释。 midiOutShortMsg 函数将 MIDI 消息存储在页锁定的数据缓冲区中,这样就无需在调用期间换入此内存。 有关 timeSetEventmidiOutShortMsg 调用的详细信息,请参阅 Microsoft Windows SDK 文档。

WDMAud 由用户和内核模式组件(Wdmaud.drv 和 Wdmaud.sys)组成,记录来自 midiOutShortMsg 调用的原始 MIDI 消息到达的时间。 WDMAud 将这些时间戳与 MIDI 消息相组合,以生成它发送到该图中显示在 WDMAud 下方的其中一个内核模式组件的 MIDI 流。

为 MIDI 应用程序生成音频筛选器图时,SysAudio 只选择三个可能的连接之一,即与 SWMidi、MIDI 端口或 DMus 端口驱动程序的链接,如上图所示。 如果应用程序选择默认 MIDI 设备,则 SysAudio 首先查找 MIDI 或 DMus 微型端口驱动程序具有 MIDI 引脚的合成器设备。 如果它在注册表中找不到此类设备,SysAudio 将改用 SWMidi 系统驱动程序 (Swmidi.sys)。 SWMidi 是一个 KS 筛选器,用于在软件中实现可波式合成器,它只需要一个可以呈现波次音频流的设备。

SWMidi 将所有语音混合在一起,以生成单波次 PCM 流,该流将输出到 KMixer 系统驱动程序。 KMixer 反过来又将 PCM 格式的波次流传递给 WaveCyclic 或 WavePci 设备,其端口和微型端口驱动程序显示在该图左下角。 或者,KMixer 可以将其输出流传递给 USBAudio 类系统驱动程序控制的 USB 音频设备(如图所示)。

在上图中,MIDI 端口驱动程序采用 WDMAud 中带时间戳的 MIDI 流,并将其转换为原始 MIDI 消息,MIDI 微型端口驱动程序会通过合成器设备播放这些消息。 MIDI 端口驱动程序包含一个排序器,该排序器将在软件中实现,并且能够计划计时器分辨率为 1 毫秒的原始 MIDI 消息。

如果合成器设备包含硬件排序器,DMus 端口驱动程序可以实现比 MIDI 端口驱动程序更高的计时精度。 在这种情况下,DMus 微型端口驱动程序应指定一个足够大的硬件缓冲区,以使用 ISR(中断服务例程)和其他高优先级操作来吸收 CPU 时间竞争产生的抖动。 DMus 端口驱动程序输出到微型端口驱动程序的 MIDI 流中的时间戳是 64 位值,分辨率为 100 纳秒。

如果 DMusic 合成器没有硬件排序器,它必须依赖于 DMus 端口驱动程序的软件排序器,如 MIDI 端口驱动程序的软件排序器,其计时器分辨率为 1 毫秒。

适配器驱动程序会分别调用 GUID 值为 CLSID_PortMidiCLSID_PortDMusPcNewPort 来创建 MIDI 或 DMus 端口驱动程序。 在 Windows XP 及更高版本中,MIDI 和 DMus 端口驱动程序共享相同的软件实现。

显示在上图底部是系统提供的微型端口驱动程序 FMSynth、UART 和 DMusUART 的名称,这些名称包含在 Portcls.sys 中。 适配器驱动程序会调用 PcNewMiniport 来创建其中一个微型端口驱动程序。 FMSynth 和 UART 提供 IMiniportMidi 接口,DMusUART 提供 IMiniportDMus 接口。 请注意,UART 现已过时(在 Windows 98 Gold 之后),仅现有驱动程序支持。 新适配器驱动程序应改用 DMusUART(在 Windows 98 SE 及更高版本中,以及 Windows 2000 及更高版本中),后者实现 UART 功能的超集。 DMusUART 是不支持 DLS 下载和硬件排序的 DMus 微型端口驱动程序的示例。 FMSynth 和 DMusUART 微型端口驱动程序的源代码在 Windows 驱动程序工具包 (WDK) 的示例音频驱动程序中提供。

下图显示了 MIDI 应用程序用于捕获 MIDI 数据的用户模式和内核模式组件。 此应用程序通过 midiInXxx 函数连接到 WDM 音频驱动程序。

Diagram showing MIDI capture user-mode and kernel-mode components.

在上图中,MIDI 和 DMus 微型端口驱动程序显示为深色框,以指示它们可以是供应商提供的组件。 如果合适,供应商可能会选择使用系统提供的微型端口驱动程序之一,即 UART 或 DMusUARTCapture。 图中的所有其他组件都由系统提供。

MIDI 数据的源通常是 MPU-401 设备。 通过调用 PcNewMiniport,适配器驱动程序可以创建系统提供的微型端口驱动程序之一,即 UART 或 DMusUARTCapture,以便从 MPU-401 设备捕获 MIDI 数据。 同样,UART 已过时,新的驱动程序应改用 DMusUARTCapture(在 Windows 98 SE 及更高版本中,以及 Windows 2000 及更高版本中)。

每次发生 MIDI 注释打开或注释关闭事件时,MIDI 或 DMusic 捕获微型端口驱动程序(上图底部)都会向 MIDI 消息添加时间戳,然后再将其添加到流向 MIDI 或 DMus 端口驱动程序的 MIDI 流。

MIDI 或 DMusic 捕获端口驱动程序将带时间戳的 MIDI 流输出到 Wdmaud.sys,这是 WDMAud 系统驱动程序的内核模式一半。 用户模式的一半 Wdmaud.drv 通过 midiInXxx 函数将带时间戳的 MIDI 流输出到应用程序,这些函数是在 Winmm.dll 中实现的。

图顶部的 MIDI 应用程序将嗲时间戳的 MIDI 事件写入 MIDI 文件。 应用程序调用 midiInOpen 以打开 MIDI 输入流时,它会在函数指针中传递到其回调例程。 发生注释打开或注释关闭事件时,操作系统将使用包含一个或多个带时间戳的 MIDI 消息的数据块来调用回调例程。 这些消息上的时间戳实际上与 MIDI 或 DMus 微型端口驱动程序最初生成的时间戳相同。

DirectMusic 组件

下图显示了 DirectMusic 应用程序用于播放捕获 MIDI 数据的用户和内核模式组件。

Diagram depicting DirectMusic playback and capture user-mode and kernel-mode components.

播放组件显示在上图的左半部分,捕获组件显示在右侧。 DMus 微型端口驱动程序显示为深色框,以指示它们可以是供应商提供的组件。 如果合适,供应商可以改用系统提供的微型端口驱动程序之一,即 DMusUART 或 DMusUARTCapture。 图中的其他组件由系统提供。

在图的左上角,DirectMusic 应用程序将带时间戳的 MIDI 流从文件定向到用户模式 DirectMusic 系统组件 (DMusic.dll),此组件反过来又将流定向到 DMus 端口驱动程序。 可以将此驱动程序绑定到 DirectMusic 合成器或 MPU-401 设备的微型端口驱动程序(如果可用)。 或者,可以将端口驱动程序绑定到 DMusic 系统驱动程序> (Dmusic.sys),这是一个系统提供的 DMus 微型端口驱动程序,该驱动程序在软件中实现支持 DLS 的可波式合成器,并且只需要一个可以呈现波次音频流的设备。

与 SWMidi 一样,DMusic 驱动程序 Dmusic.sys 将其所有语音混合在一起,以生成单个 PCM 格式的波次流,该流将输出到 KMixer。 反过来,KMixer 可以将波次流传递给波次设备,其端口和微型端口驱动程序显示在图的左下角,或者传递给由 USBAudio 系统驱动程序控制的 USB 音频设备,该驱动程序未显示在该图中。

DirectMusic 捕获组件显示在上图的右半部分。 该图右下角的 DMusic 捕获微型端口驱动程序控制捕获硬件,并为其记录的每个 MIDI 消息标记时间戳。 DMus 端口驱动程序将带时间戳的 MIDI 流定向到用户模式 DirectMusic 组件 DMusic.dll。 应用程序通过 DirectMusic API 访问此流,并将带时间戳的 MIDI 数据写入文件。

适配器驱动程序可以使用系统提供的 DMusUARTCapture 微型端口驱动程序来控制 MPU-401 捕获设备。 适配器驱动程序通过调用 GUID 值为 CLSID_DMusUARTCapturePcNewMiniport 来创建此微型端口驱动程序。 生成的微型端口驱动程序对象支持 IMiniportDMus 接口。 DMusUARTCapture 微型端口驱动程序的源代码在 Windows 驱动程序工具包 (WDK) 的示例音频驱动程序中提供。

DirectMusic 应用程序还可以通过 midiOutXxx 设备(如 SWMidi (Swmidi.sys))运行(选择时)。 为简单起见,上图中省略了此路径。 DMusic 驱动程序 (Dmusic.sys) 需要初始 DLS 下载,才能正常运行;使用 SWMidi 可规避此要求。