2017-04-10 141 views
1

对于我当前的项目,我正在C++中实现本地库,我将通过JNA访问该项目,该项目是低延迟通信模拟器。为了模拟模拟器所基于的硬件,需要在传输时启用侧音。 当然,JAVA声音难以实现接近零延迟(最好我们可以得到的是〜120ms),为了保持理解,我们需要侧音上的延迟接近于零。幸运的是,在Windows中,似乎有一种方法可以听取产生完美侧音的USB麦克风的麦克风。如何以编程方式启用侧音/麦克风传递

音频属性 - >播放 - >耳机耳机 - >属性 - >色阶

An example of what I mean here

(注意,这是从“听此设备”功能,它会产生一个非常糟糕的延迟不同)

我一直在为核心音频API的MSDN示例工作,并能够查询设备,并获得他们的渠道,音量级别,静音设置等,但麦克风级静音/取消静音似乎不是甚至可以从核心音频apis访问。

我的问题是这样的:有没有办法通过编程方式与USB耳机的麦克风电平/静音设置接口?

我们的模拟器是标准化的,所以我们不必担心支持多种耳机(目前有2种)。

+1

我打算使用[IDeviceTopology](https://msdn.microsoft.com/en-us/library/windows/desktop/dd371376(v = vs.85).aspx)来查看如果该功能是可访问的 - 我会报告,如果这个工程。 – SeanIdzenga

+0

我使用MSDN中发布的一个示例,展示了通过设备拓扑树“向后走”的方法,这让我有能力搜索具有getter/setter方法的静音节点。 虽然不是我发现的原始链接[这个Stackoverflow答案](http://stackoverflow.com/a/21607996/1867465)有我引用的walkTreeBackwardsFromPart()函数。 – SeanIdzenga

回答

1

解决此问题的关键是将设备拓扑树向后走,直到找到负责设置侧音静音属性的部分。所以在我的CPP项目中,我有几种方法一起工作,以确定我在拓扑树中的位置,寻找SuperMix部分。

SuperMix似乎是sidetone的通用名称,至少可以用于我们支持的两种耳机。这两个耳机的树相同,你的里程可能会有所不同。这是输出可能如同从上述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调用它,每当我们需要激活/关闭侧音,所以在开始和任何传输的结束的问题。