音频位置属性

音频驱动程序的客户端使用 KSPROPERTY_AUDIO_POSITION 属性获取和设置音频流中的当前位置。 该属性使用 KSAUDIO_POSITION 结构来描述当前位置。 该结构包含两个成员:PlayOffsetWriteOffset

PlayOffsetWriteOffset 成员定义客户端缓冲区区域边界,该区域目前保留专用于音频设备。 客户端必须假定设备目前可能正在访问该区域中包含的任何数据。 因此,客户端必须仅访问位于此区域之外的缓冲区部分。 区域边界会在流向前移动时移动。

如果客户端缓冲区是循环缓冲区(即流类型为 KSINTERFACE_STANDARD_LOOPED_STREAMING),则 PlayOffsetWriteOffset 是缓冲区相对偏移量。 也就是说,它们被指定为从循环客户端缓冲区开头的字节偏移量。 当任一偏移量递增到缓冲区末尾时,它将回绕到缓冲区的开头。 (缓冲区开头的偏移量为零。)因此,两个偏移量都不会超过缓冲区大小。

如果客户端缓冲区是非循环缓冲区(即流类型为 KSINTERFACE_STANDARD_STREAMING),则 PlayOffsetWriteOffset 是流相对偏移量。 也就是说,它们被指定为流开头的字节偏移量。 可以将这些偏移量视为包含整个流的理想化缓冲区的偏移量,并且从头到尾是连续的。

对于呈现流,PlayOffset 成员会指定该流的播放位置,WriteOffset 成员会指定该流的写入位置。 下图显示了客户端缓冲区中的播放和写入位置。

Diagram illustrating play and write positions in a render stream.

播放位置是当前正在播放的样本的字节偏移量,即,在数模转换器 (DAC) 的输入处闩锁的样本。 写入位置是指客户端可以在该位置之外安全地写入缓冲区。 当流播放时,播放和写入位置在上图中从左向右移动。 客户端的写入必须领先于写入位置。 此外,如果缓冲区是循环缓冲区,客户端的写入绝不能超出播放位置。

尽管 WaveCyclic 或 WavePci 端口驱动程序依赖于微型端口驱动程序来跟踪播放位置,但端口驱动程序会跟踪写入位置。 WaveCyclic 和 WavePci 端口驱动程序按如下所示更新写入位置:

  • WaveCyclic

    每当 WaveCyclic 端口驱动程序调用 IDmaChannel::CopyTo 以将新数据块复制到循环缓冲区(从客户端缓冲区),写入位置将推进到数据块中最后一个字节的位置(在客户端缓冲区中)。

  • WavePci

    默认情况下,每当 WavePci 微型端口驱动程序调用 IPortWavePciStream::GetMapping 以获取新映射(客户端缓冲区的一部分)并且调用成功时,写入位置就会推进到新映射中最后一个字节的位置(在客户端缓冲区中)。

    如果 WavePci 微型端口驱动程序通过指定端口驱动程序的预提取偏移量来替代默认行为,则当前写入位置始终等于当前播放位置和预提取偏移量的总和。 有关详细信息,请参阅预提取偏移量

对于捕获流,PlayOffset 成员会指定该流的记录位置,WriteOffset 成员会指定该流的读取位置。 下图显示了客户端缓冲区中的记录和读取位置。

Diagram illustrating record and read positions in a capture stream.

记录位置是将在模数转换器(即 ADC)的输出处闩锁的最新样本的字节偏移量。 (此位置指定音频设备的 DMA 引擎最终将样本写入的缓冲区位置。)读取位置是指客户端在该位置之外无法从缓冲区安全读取。 随着流记录的进行,读取和记录位置在上图中从左到右推进。 客户端的读取必须跟踪读取位置。 此外,如果缓冲区是循环缓冲区,客户端的读取必须领先于记录位置。

尽管 WaveCyclic 或 WavePci 端口驱动程序依赖于微型端口驱动程序来跟踪记录位置,但端口驱动程序会跟踪读取位置。 WaveCyclic 和 WavePci 端口驱动程序按如下所示更新读取位置:

  • WaveCyclic

    每当 WaveCyclic 端口驱动程序调用 IDmaChannel::CopyFrom 以将新数据块从循环缓冲区复制到客户端缓冲区时,读取位置将推进到数据块中最后一个字节的位置(在客户端缓冲区中)。

  • WavePci

    每次 WavePci 微型端口驱动程序调用 IPortWavePciStream::ReleaseMapping 以释放以前获取的映射(客户端缓冲区的一部分)时,读取位置将推进到已释放映射中最后一个字节的位置(在客户端缓冲区中)。

微型端口驱动程序不需要为 KSPROPERTY_AUDIO_POSITION 属性请求实现处理程序例程。 相反,WaveCyclic 和 WavePci 端口驱动程序代表微型端口驱动程序处理这些请求。 处理 get-property 请求时,WaveCyclic 或 WavePci 端口驱动程序已具有计算 WriteOffset 值所需的所有信息,但仍需要微型端口驱动程序提供信息才能计算 PlayOffset 值。 若要获取此信息,端口驱动程序会调用微型端口驱动程序的 IMiniportWaveCyclicStream::GetPositionIMiniportWavePciStream::GetPosition 方法。

对于呈现流,GetPosition 方法会检索播放位置 - 当前通过 DAC 播放的样本的字节偏移量。 对于捕获流,GetPosition 方法会检索记录位置 - ADC 要捕获的最新样本的字节偏移量。

请注意,GetPosition 调用检索的偏移量值是与当前正通过扬声器插孔传输的信号对应的播放位置,或者是与当前正通过麦克风插孔接收的信号对应的记录位置。 它不是 DMA 位置。 (DMA 位置是音频设备中 DMA 引擎当前正在传输到 DMA 缓冲区或从 DMA 缓冲区传输的样本的字节偏移量。)

某种音频硬件包含一个位置寄存器,用于跟踪当前位于每个 DAC 或 ADC 中的样本的字节偏移量,在这种情况下,GetPosition 方法只会检索相应流的位置寄存器的内容。 其他音频硬件只能向驱动程序提供 DMA 位置,在这种情况下,GetPosition 方法必须考虑到当前 DMA 位置以及设备内部的缓冲延迟,从而提供对 DAC 或 ADC 中样本的字节偏移量的最佳估计。

尽管 WaveCyclic 或 WavePci 端口驱动程序中的属性处理程序必须区分循环缓冲区和非循环缓冲区,以确定是提供流相对还是缓冲区相对字节偏移量,但此详细信息(即缓冲区是循环还是非循环)对微型端口驱动程序是透明的。

无论客户端缓冲区是循环还是非循环,IMiniportWaveCyclicStream::GetPosition 方法都始终报告缓冲区相对播放或记录位置。 如果客户端缓冲区是循环缓冲区,则属性处理程序会将微型端口驱动程序所报告的缓冲区相对位置(表示为循环缓冲区的偏移量)转换为客户端缓冲区的偏移量,然后该处理程序会写入 PlayOffset 成员。 如果客户端缓冲区是非循环缓冲区,则属性处理程序会将缓冲区相对播放位置转换为流相对播放位置,然后再将其写入 PlayOffset 成员。

无论客户端缓冲区是循环还是非循环,IMiniportWavePciStream::GetPosition 方法都始终报告流相对播放或记录位置。 如果客户端缓冲区是循环缓冲区,则属性处理程序会将流相对播放位置转换为缓冲区相对播放位置(表示为客户端缓冲区的偏移量),然后再将其写入属性请求中 KSAUDIO_POSITION 结构中的 PlayOffset 成员。 如果客户端缓冲区是非循环缓冲区,则属性处理程序会将流相对位置写入 PlayOffset 成员。

播放或记录位置在流初始化后立即为零。 转换至 KSSTATE_STOP 状态(请参阅 KSSTATE)会将位置重置为零。 当流因从 KSSTATE_RUN 转换至 KSSTATE_PAUSE 或 KSSTATE_ACQUIRE 而停止时,位置将冻结。 当流从 KSSTATE_PAUSE 或 KSSTATE_ACQUIRE 转换回 KSSTATE_RUN 时,它会解除冻结。

有关 WaveCyclic 和 WavePci 微型端口驱动程序的 GetPosition 方法的示例实现,请参阅 Windows 驱动程序工具包 (WDK) 中的示例音频驱动程序。