WASAPI:识别环回记录中的非活动通道
WASAPI: Identify non-active channels on loopback recording
我有一个 DSP 软件,可以在共享环回模式下使用 WASAPI api 捕获播放的音频。
hr = _pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK, 0, 0, _pFormat, 0);
这部分工作正常,但现在我希望能够检测实际播放的频道数。换句话说,我如何能够检测音频播放是否为立体声、5.1、7.1?
问题是:
* 由于环回必须使用共享模式,因此可以播放多个源。
* 此分析必须实时进行。等不及播放完毕了。
* 检测任何播放源根本不使用的频道和暂时静音的频道之间的区别
我认为最好的解决方案是如果我可以检索所有回放 source/sub 混音的列表并分别查询它们的频道数。这样我就不必分析音频数据流本身了。
环回记录以端点上定义的混合格式进行,因此无论原始音频格式是什么,您都可以得到混合格式的数据,从可能的多个播放源混合并转换为这种共享格式。
WASAPI loopback contains the mix of all audio being played...
The GetMixFormat
method retrieves the stream format that the audio engine uses for its internal processing of shared-mode streams...
After an application has used GetMixFormat or IsFormatSupported to find an appropriate format for a shared-mode or exclusive-mode stream, the application can call the Initialize method to initialize a stream with that format. An application that attempts to initialize a shared-mode stream with a format that is not identical to the mix format obtained from the GetMixFormat method, but that has the same number of channels and the same sample rate as the mix format, is likely to succeed. Before calling Initialize, the application can call IsFormatSupported to verify that Initialize will accept the format.
也就是说,尽管 WASAPI 在音频格式方面提供了一定的灵活性,但在环回捕获时通道配置和采样率由共享格式定义。
在进行混音时,您无法真正识别“非活动”频道:此信息在混音为共享格式时会丢失。
此外,实际的共享格式可以通过控制面板交互配置:
好的,现在我的问题有了解决方案。据我所知,您无法检测共享混音中的子混音,因此唯一的选择是分析音频 stream/capture 缓冲区。
首先,在我的主捕获循环中,我为所有正在播放的频道设置了当前时间戳。
const time_t now = Date::getCurrentTimeMillis();
//Iterate all capture frames
for (i = 0; i < numFramesAvailable; ++i) {
for (j = 0; j < _nChannelsIn; ++j) {
//Identify which channels are playing.
if (pCaptureBuffer[j] != 0) {
_pUsedChannels[j] = now;
}
}
}
然后每一秒我都会调用这个函数来评估一个频道是否在最后一秒播放过。根据正在播放的频道,我可以进行条件路由。
void checkUsedChannels() {
const time_t now = Date::getCurrentTimeMillis();
//Compare now against last used timestamp and determine active channels
for (size_t i = 0; i < _nChannelsIn; ++i) {
if (now - _pUsedChannels[i] > 1000) {
_pUsedChannels[i] = 0;
}
}
//Update conditional routing
for (const Input *pInut : _inputs) {
pInut->evalConditions();
}
}
非常简单的解决方案,但它似乎有效。
我有一个 DSP 软件,可以在共享环回模式下使用 WASAPI api 捕获播放的音频。
hr = _pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK, 0, 0, _pFormat, 0);
这部分工作正常,但现在我希望能够检测实际播放的频道数。换句话说,我如何能够检测音频播放是否为立体声、5.1、7.1?
问题是:
* 由于环回必须使用共享模式,因此可以播放多个源。
* 此分析必须实时进行。等不及播放完毕了。
* 检测任何播放源根本不使用的频道和暂时静音的频道之间的区别
我认为最好的解决方案是如果我可以检索所有回放 source/sub 混音的列表并分别查询它们的频道数。这样我就不必分析音频数据流本身了。
环回记录以端点上定义的混合格式进行,因此无论原始音频格式是什么,您都可以得到混合格式的数据,从可能的多个播放源混合并转换为这种共享格式。
WASAPI loopback contains the mix of all audio being played...
The
GetMixFormat
method retrieves the stream format that the audio engine uses for its internal processing of shared-mode streams...After an application has used GetMixFormat or IsFormatSupported to find an appropriate format for a shared-mode or exclusive-mode stream, the application can call the Initialize method to initialize a stream with that format. An application that attempts to initialize a shared-mode stream with a format that is not identical to the mix format obtained from the GetMixFormat method, but that has the same number of channels and the same sample rate as the mix format, is likely to succeed. Before calling Initialize, the application can call IsFormatSupported to verify that Initialize will accept the format.
也就是说,尽管 WASAPI 在音频格式方面提供了一定的灵活性,但在环回捕获时通道配置和采样率由共享格式定义。
在进行混音时,您无法真正识别“非活动”频道:此信息在混音为共享格式时会丢失。
此外,实际的共享格式可以通过控制面板交互配置:
好的,现在我的问题有了解决方案。据我所知,您无法检测共享混音中的子混音,因此唯一的选择是分析音频 stream/capture 缓冲区。
首先,在我的主捕获循环中,我为所有正在播放的频道设置了当前时间戳。
const time_t now = Date::getCurrentTimeMillis();
//Iterate all capture frames
for (i = 0; i < numFramesAvailable; ++i) {
for (j = 0; j < _nChannelsIn; ++j) {
//Identify which channels are playing.
if (pCaptureBuffer[j] != 0) {
_pUsedChannels[j] = now;
}
}
}
然后每一秒我都会调用这个函数来评估一个频道是否在最后一秒播放过。根据正在播放的频道,我可以进行条件路由。
void checkUsedChannels() {
const time_t now = Date::getCurrentTimeMillis();
//Compare now against last used timestamp and determine active channels
for (size_t i = 0; i < _nChannelsIn; ++i) {
if (now - _pUsedChannels[i] > 1000) {
_pUsedChannels[i] = 0;
}
}
//Update conditional routing
for (const Input *pInut : _inputs) {
pInut->evalConditions();
}
}
非常简单的解决方案,但它似乎有效。