解决这个问题的关键是向后遍历设备拓扑树,直到找到负责设置侧音静音属性的部分。因此,在我的 CPP 项目中,我使用了几种方法共同确定我在拓扑树中的位置,以寻找 SuperMix 部分。
SuperMix 似乎是侧音的通用名称,并且至少被我们支持的两款耳机使用。两个耳机的树是相同的,您的里程可能会有所不同。这就是前面提到的 WalkTreeBackwardsFromPart 示例的输出结果(参见 this answer)
Part Name: SuperMix
Part Name: Volume
Part Name: Mute
这是我修改后的 WalkTreeBackwardsFromPart 版本,它出于所有意图和目的仅检查我们当前正在查看的部分是否是 SuperMix 并且该部分的直接子节点是否是卷节点,这是为了防止不正确分配,因为我发现对于我们的耳机,通常会有两个称为 SuperMix 的节点,唯一的区别是我们想要的那个有一个音量节点子节点。
HRESULT Sidetone::WalkTreeBackwardsFromPart(IPart *part) {
HRESULT hr;
if (wcscmp(this->getPartName(part), L"SuperMix") == 0 && this->treePeek(part, L"Volume")){
this->superMix = part;
IPart** superMixChildren = this->getChildParts(part);
int nSuperMixChildren = sizeof(superMixChildren) / sizeof(superMixChildren[0]);
if (nSuperMixChildren > 0){
for (int i = 0; i < nSuperMixChildren; i++){
if (wcscmp(this->getPartName(superMixChildren[i]), L"Volume") == 0){
this->volumeNode = this->getIPartAsIAudioVolumeLevel(superMixChildren[i]);
if (this->volumeNode != NULL){
IPart** volumeNodeChildren = this->getChildParts(superMixChildren[i]);
int nVolumeNodeChildren = sizeof(volumeNodeChildren) / sizeof(volumeNodeChildren[0]);
if (nVolumeNodeChildren > 0){
for (int j = 0; j < nVolumeNodeChildren; j++){
if (wcscmp(this->getPartName(volumeNodeChildren[j]), L"Mute") == 0){
this->muteNode = this->getIPartAsIAudioMute(volumeNodeChildren[j]);
break;
}
}
}
}
break;
}
}
}
delete[] superMixChildren;
this->muteNode; // = someotherfunc();
this->superMixFound = true;
return S_OK;
} else if(superMixFound == false){
IPartsList *pIncomingParts = NULL;
hr = part->EnumPartsIncoming(&pIncomingParts);
if (E_NOTFOUND == hr) {
// not an error... we've just reached the end of the path
//printf("%S - No incoming parts at this part: 0x%08x\n", this->MSGIDENTIFIER, hr);
return S_OK;
}
if (FAILED(hr)) {
printf("%S - Couldn't enum incoming parts: hr = 0x%08x\n", this->MSGIDENTIFIER, hr);
return hr;
}
UINT nParts = 0;
hr = pIncomingParts->GetCount(&nParts);
if (FAILED(hr)) {
printf("%S - Couldn't get count of incoming parts: hr = 0x%08x\n", this->MSGIDENTIFIER, hr);
pIncomingParts->Release();
return hr;
}
// walk the tree on each incoming part recursively
for (UINT n = 0; n < nParts; n++) {
IPart *pIncomingPart = NULL;
hr = pIncomingParts->GetPart(n, &pIncomingPart);
if (FAILED(hr)) {
printf("%S - Couldn't get part #%u (0-based) of %u (1-basedSmile hr = 0x%08x\n", this->MSGIDENTIFIER, n, nParts, hr);
pIncomingParts->Release();
return hr;
}
hr = WalkTreeBackwardsFromPart(pIncomingPart);
if (FAILED(hr)) {
printf("%S - Couldn't walk tree on part #%u (0-based) of %u (1-basedSmile hr = 0x%08x\n", this->MSGIDENTIFIER, n, nParts, hr);
pIncomingPart->Release();
pIncomingParts->Release();
return hr;
}
pIncomingPart->Release();
}
pIncomingParts->Release();
}
return S_OK;
}
Sidetone::superMixFound 是一个布尔成员,用于快速中断我们的递归循环并阻止我们进一步遍历设备拓扑树(浪费时间)。
Sidetone::getPartName() 是一个简单的可重用方法,用于返回零件名称的宽字符串数组。
Sidetone::treePeek() 如果指定部件的子项包含名称作为第二个参数指定的部件,则返回 true。
Sidetone::getChildParts() 为给定部件的每个子元素返回一个指针数组。
在弄清楚这一点之后,只需将 setMute 方法暴露给 dllmain.cpp 并在我们需要激活/停用侧音时通过 JNA 调用它,因此在任何传输的开始和结束时。