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;
}
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?) –
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