音频位置属性

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

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

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

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

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

diagram illustrating the play position and write position in a render stream.

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

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

  • WaveCyclic

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

  • WavePci

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

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

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

diagram illustrating the record position and read position in a capture stream.

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

尽管 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) 。