2016-07-11 3 views
1

J'utilise wasapi pour la capture en boucle. Toutes les données après cela sont enregistrées dans le fichier wav. Quand il commence à capturer, il délivre des données tout le temps jusqu'à ce que j'arrête de capturer le processus, même si aucune application n'utilise de périphérique audio et donc personne ne produit de musique. En conséquence, j'écris pour ne pas déposer des données précieuses - juste le silence.wasapi: détecter que rien n'est joué (le périphérique n'est pas utilisé)

Il est donc possible d'obtenir un silence distinct dans la musique actuellement diffusée et une situation dans laquelle l'appareil n'est pas utilisé du tout. Dans une situation ultérieure, je veux interrompre le processus d'enregistrement des données dans un fichier et en créer un nouveau lorsque vous jouez à nouveau via le périphérique audio.

méthode PS AudioCaptureClinet GetBuffer est en vedette avec le paramètre de sortie flag qui peut apparemment avoir une valeur AUDCLNT_BUFFERFLAGS_SILENT == 0x1 dans certaines conditions, mais dans mon cas, il retourne flag==0 chaque fois

+0

Vous venez de vérifier le silence? Par exemple. arrêter le flux après X secondes de zéros d'enregistrement (ou quelque chose en dessous d'un certain seuil?) –

+0

Bien sûr, c'est possible. Mais pas élégant et ne peut pas être une méthode fiable. Parfois, dans certaines compositions (en particulier compositeur moderne), nous avons une assez longue pause (voir John Cage 4'33 ").))) – DotNetter

Répondre

1

Ok, il se trouve qu'il n'y a aucun moyen fiable pour déduire un flux de réalimentation lui-même que rien n'est joué sur le périphérique de sortie correspondant. Cependant, il est possible d'interroger des sessions audio sur le périphérique de sortie lui-même et pour chaque session, vérifiez que son état est actif. Il est également possible d'être averti lorsque des sessions sont ajoutées/retirées d'un périphérique et d'être averti lorsqu'une session (de) est activée. De cette façon, vous pouvez démarrer le flux de bouclage lorsque la première session est activée sur un périphérique et l'arrêter lorsque le dernier est désactivé.

Voici comment faire:

  • Mettre en oeuvre IAudioSessionNotification et IAudioSessionEvents
  • Mettre en place un thread MTA, STA ne fonctionnera pas
  • Extrayez interface IMMDevice de la sortie que vous souhaitez surveiller
  • À partir de IMMDevice, procurez-vous IAudioSessionManager2
  • À partir de IAudioSessionManager2, procurez-vous IAudioSessionEnumerator
  • De IAudioSessionEnumerator, énumérer IAudioSessionControls
    • Ceci est la liste des sessions initiales. Il restera constant pour la durée de vie de l'énumérateur.
    • De IAudioSessionControl, utilisez GetState pour voir ce qui est initialement actif
    • Utilisation IAudioSessionControl :: RegisterAudioSessionNotification pour être averti de (de) l'activation des premières sessions
  • utilisation IAudioSessionManager2 :: RegisterSessionNotification pour enregistrer votre notification de session personnalisé
    • Ce sera appelé pour toutes les sessions supplémentaires créées après l'énumération initiale
    • de IAudioSessionNotification :: OnSessionCreated, utilisation IAudioSessionControl :: RegisterAudioSessionNotification pour être averti de la (de) l'activation des sessions supplémentaires
  • Si une session est d'abord active, démarrez le flux de réalimentation
  • Lors de la dernière session (initiale ou supplémentaire) obtient deactivaed ou déconnecté, arrêtez le flux de réalimentation
  • Et redémarrage Bien sûr, lorsque la session active à nouveau

est Ci-dessous un exemple de travail. Notez que je ne:

  • Est-ce que toute erreur de manipulation complète en plus d'imprimer ce qui est erroné
  • En fait, mis en place le flux de réalimentation. L'exemple ne s'imprime que lorsque tout (de) est activé.
  • Faire de la comptabilité des interfaces COM! L'échantillon fuit des poignées d'interface partout. Pour obtenir ces informations correctement, vous souhaitez probablement stocker vos objets SessionNotifications/SessionEvents dans une collection thread-safe et les désenregistrer/les libérer lorsque les sessions sont détruites.


    #include <windows.h> 
    #include <atlbase.h> 
    #include <atlcomcli.h> 
    #include <mmdeviceapi.h> 
    #include <audioclient.h> 
    #include <audiopolicy.h> 
    #include <iostream> 
    #include <vector> 
    #include <string> 

    #define VERIFY(expr) do {         \ 
     if(FAILED(hr = (expr))) {        \ 
     std::cout << "Error: " << #expr << ": " << hr << "\n"; \ 
     goto error;           \ 
     }              \ 
    } while(0) 

    static volatile ULONG sessionId = 0; 

    class SessionEvents : public IAudioSessionEvents { 

    private: 
     LONG rc; 
     ULONG id; 
     ~SessionEvents() {} 

    public: 
     SessionEvents(ULONG id) : 
     id(id), rc(1) {} 

     ULONG STDMETHODCALLTYPE AddRef() { 
     return InterlockedIncrement(&rc); 
     } 

     ULONG STDMETHODCALLTYPE Release() { 
     ULONG rc = InterlockedDecrement(&this->rc); 
     if(rc == 0) { 
      delete this; 
     } 
     return rc; 
     } 

     HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv) { 
     if(IID_IUnknown == riid) { 
      AddRef(); 
      *ppv = static_cast<IUnknown*>(this); 
      return S_OK; 
     } 
     else if(__uuidof(IAudioSessionEvents) == riid) { 
      AddRef(); 
      *ppv = static_cast<IAudioSessionEvents*>(this); 
      return S_OK; 
     } 
     else { 
      *ppv = nullptr; 
      return E_NOINTERFACE; 
     } 
     } 

     HRESULT STDMETHODCALLTYPE OnDisplayNameChanged(LPCWSTR NewDisplayName, LPCGUID EventContext) { 
     return S_OK; 
     } 

     HRESULT STDMETHODCALLTYPE OnIconPathChanged(LPCWSTR NewIconPath, LPCGUID EventContext) { 
     return S_OK; 
     } 

     HRESULT STDMETHODCALLTYPE OnSimpleVolumeChanged(float NewVolume, BOOL NewMute, LPCGUID EventContext) { 
     return S_OK; 
     } 

     HRESULT STDMETHODCALLTYPE OnChannelVolumeChanged(DWORD ChannelCount, float NewChannelVolumeArray[], DWORD ChangedChannel, LPCGUID EventContext) { 
     return S_OK; 
     } 

     HRESULT STDMETHODCALLTYPE OnGroupingParamChanged(LPCGUID NewGroupingParam, LPCGUID EventContext) { 
     return S_OK; 
     } 

     HRESULT STDMETHODCALLTYPE OnStateChanged(AudioSessionState NewState) { 
     switch(NewState) { 
     case AudioSessionStateInactive: std::wcout << L"AudioSessionStateInactive session # " << id << L"\n"; break; 
     case AudioSessionStateActive: std::wcout << L"AudioSessionStateActive session # " << id << L"\n"; break; 
     case AudioSessionStateExpired: std::wcout << L"AudioSessionStateExpired session # " << id << L"\n"; break; 
     } 
     return S_OK; 
     } 

     HRESULT STDMETHODCALLTYPE OnSessionDisconnected(AudioSessionDisconnectReason DisconnectReason) { 
     std::wcout << L"OnSessionDisconnected session # " << id << L"\n"; 
     return S_OK; 
     } 
    }; 

    class SessionNotification : public IAudioSessionNotification { 

    private: 
     LONG rc; 
     ~SessionNotification() {} 

    public: 
     SessionNotification() : rc(1) {} 

     ULONG STDMETHODCALLTYPE AddRef() { 
     return InterlockedIncrement(&rc); 
     } 

     ULONG STDMETHODCALLTYPE Release() { 
     ULONG rc = InterlockedDecrement(&this->rc); 
     if(rc == 0) 
      delete this; 
     return rc; 
     } 

     HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv) { 
     if(IID_IUnknown == riid) { 
      AddRef(); 
      *ppv = static_cast<IUnknown*>(this); 
      return S_OK; 
     } 
     else if(__uuidof(IAudioSessionNotification) == riid) { 
      AddRef(); 
      *ppv = static_cast<IAudioSessionNotification*>(this); 
      return S_OK; 
     } 
     else { 
      *ppv = nullptr; 
      return E_NOINTERFACE; 
     } 
     } 

     HRESULT OnSessionCreated(IAudioSessionControl *newSession) { 
     HRESULT hr = S_OK; 
     if(newSession) { 
      newSession->AddRef(); 
      CComHeapPtr<WCHAR> name; 
      ULONG id = InterlockedIncrement(&sessionId); 
      VERIFY(newSession->GetDisplayName(&name)); 
      std::wcout << L"created session # " << id << L": " << name.m_pData << L"\n"; 
      VERIFY(newSession->RegisterAudioSessionNotification(new SessionEvents(id))); 
     } 
     error: 
     return hr; 
     } 
    }; 

    int main(int argc, char** argv) { 

     HRESULT hr = S_OK; 
     VERIFY(CoInitializeEx(nullptr, COINIT_MULTITHREADED)); 

     { 
     int count; 
     std::wstring line; 
     CComPtr<IMMDevice> defaultOutput; 
     CComPtr<IMMDeviceEnumerator> devices; 
     CComPtr<IAudioSessionManager2> manager; 
     CComPtr<IAudioSessionEnumerator> sessions; 
     SessionNotification* notification = new SessionNotification; 

     VERIFY(devices.CoCreateInstance(__uuidof(MMDeviceEnumerator))); 
     VERIFY(devices->GetDefaultAudioEndpoint(eRender, eMultimedia, &defaultOutput)); 
     VERIFY(defaultOutput->Activate(__uuidof(IAudioSessionManager2), CLSCTX_ALL, nullptr, reinterpret_cast<void**>(&manager))); 
     VERIFY(manager->RegisterSessionNotification(notification)); 
     VERIFY(manager->GetSessionEnumerator(&sessions)); 
     VERIFY(sessions->GetCount(&count)); 
     std::wcout << L"Initial sessions: " << count << L"\n"; 
     for(int s = 0; s < count; s++) { 
      AudioSessionState state; 
      CComHeapPtr<WCHAR> name; 
      CComPtr<IAudioSessionControl> control; 
      VERIFY(sessions->GetSession(s, &control)); 
      VERIFY(control->GetDisplayName(&name)); 
      VERIFY(control->GetState(&state)); 
      std::wcout << L"Initial session name: " << name.m_pData << L", active = " << (state == AudioSessionStateActive) << L"\n"; 
      VERIFY(control->RegisterAudioSessionNotification(new SessionEvents(InterlockedIncrement(&sessionId)))); 
     } 

     std::wcout << L"Press return to exit...\n"; 
     std::getline(std::wcin, line); 
     VERIFY(manager->UnregisterSessionNotification(notification)); 
     } 

    error: 
     CoUninitialize(); 
     return 0; 
    }