2017-04-10 140 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調用它,每當我們需要激活/關閉側音,所以在開始和任何傳輸的結束的問題。