Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
W tym temacie przedstawiono zestaw nowych interfejsów API systemu Windows 11 dla obiektów przetwarzania audio (APOs), które są dostarczane ze sterownikiem audio.
System Windows umożliwia producentom sprzętu audio innych firm uwzględnienie niestandardowych efektów przetwarzania sygnałów cyfrowych opartych na hoście. Te efekty są pakowane jako obiekty przetwarzania audio w trybie użytkownika (APOs). Aby uzyskać więcej informacji, zobacz Obiekty przetwarzania dźwięku systemu Windows.
Niektóre z opisanych tutaj interfejsów API umożliwiają korzystanie z nowych scenariuszy dla niezależnych dostawców sprzętu (IHV) i niezależnych dostawców oprogramowania (ISV), podczas gdy inne interfejsy API mają zapewnić alternatywy, które zwiększają ogólną niezawodność dźwięku i możliwości debugowania.
- Struktura anulowania echa akustycznego (AEC) umożliwia APO identyfikację się jako obiekt AEC APO , udzielając dostępu do strumienia referencyjnego i dodatkowych kontrolek.
- Platforma Ustawienia umożliwi obiektom APO udostępnianie metod do wykonywania zapytań i modyfikowania repozytorium właściwości dla efektów dźwiękowych ("repozytorium właściwości FX") na punkcie końcowym audio. Gdy te metody są implementowane przez obiekt APO, mogą być wywoływane przez aplikacje do obsługi sprzętu (HSA), które są skojarzone z tym obiektem APO.
- Framework Powiadomień umożliwia efektom dźwiękowym (APOs) żądać powiadomień dotyczących obsługi zmian w magazynie właściwości woluminu, punktu końcowego i efektów dźwiękowych.
- Struktura rejestrowania ułatwia opracowywanie i debugowanie obiektów API.
- Struktura Wątkowość pozwala na wielowątkowość przez użycie puli wątków zarządzanej przez system operacyjny, zarejestrowanej w MMCSS.
- Interfejsy API odnajdywania i sterowania efektów dźwiękowych umożliwiają systemowi operacyjnemu wykrywanie, włączanie i wyłączanie efektów dostępnych do przetwarzania w strumieniu.
Aby korzystać z tych nowych API, oczekuje się, że APO będą używać nowego interfejsu IAudioSystemEffects3. Gdy obiekt APO implementuje ten interfejs, system operacyjny interpretuje to jako niejawny sygnał, że obiekt APO obsługuje framework ustawień APO i umożliwia obiektowi APO subskrybowanie typowych powiadomień związanych z dźwiękiem z silnika audio.
Wymagania programistyczne APO CAPX dla systemu Windows 11
Wszystkie nowe interfejsy API dostarczane na urządzeniu dla systemu Windows 11 muszą być zgodne z interfejsami API wymienionymi w tym temacie, zweryfikowane za pośrednictwem HLK. Ponadto oczekuje się, że wszystkie jednostki APO korzystające z AEC będą zgodne z implementacją opisaną w tym temacie, zweryfikowaną przez HLK. Implementacje niestandardowe dla tych podstawowych rozszerzeń do przetwarzania dźwięku (Ustawienia, Logowanie, Powiadomienia, Wątkowanie, AEC) powinny wykorzystywać interfejsy API CAPX. Zostanie to zweryfikowane za pośrednictwem testów HLK systemu Windows 11. Jeśli na przykład obiekt APO używa danych rejestru do zapisywania ustawień zamiast używania struktury Ustawień, skojarzony test HLK zakończy się niepowodzeniem.
Wymagania dotyczące wersji systemu Windows
Interfejsy API opisane w tym temacie są dostępne od kompilacji 22000 systemu operacyjnego Windows 11, a także w WDK i SDK. System Windows 10 nie będzie obsługiwał tych interfejsów API. Jeśli obiekt APO zamierza działać zarówno w systemach Windows 10, jak i Windows 11, może sprawdzić, czy jest inicjowany za pomocą interfejsu APOInitSystemEffects2 , czy struktury APOInitSystemEffects3 , aby określić, czy jest on uruchomiony w systemie operacyjnym obsługującym interfejsy API CAPX.
Najnowsze wersje systemu Windows, zestawu WDK i zestawu SDK można pobrać poniżej, za pośrednictwem programu Windows Insider. Partnerzy, którzy są zaangażowani w firmę Microsoft za pośrednictwem Centrum partnerskiego, mogą również uzyskiwać dostęp do tej zawartości za pośrednictwem współpracy. Aby uzyskać więcej informacji na temat współpracy, zobacz Wprowadzenie do współpracy firmy Microsoft.
- Windows Insider Preview — pobieranie iso
- Windows Insider Preview — pobieranie zestawu WDK
- Windows Insider Preview — pobieranie zestawu SDK
Zawartość WHCP systemu Windows 11 została zaktualizowana, aby zapewnić partnerom środki sprawdzania poprawności tych interfejsów API.
Przykładowy kod zawartości opisanej w tym temacie można znaleźć tutaj: Audio/SYSVAD/APO — github
Anulowanie echa akustycznego (AEC)
Anulowanie echa akustycznego (AEC) jest powszechnie stosowanym efektem audio, wdrażanym przez niezależnych dostawców sprzętu (IHV) i oprogramowania (ISV) jako obiekt przetwarzania dźwięku (APO) w ścieżce przechwytywania mikrofonu. Ten efekt różni się od innych efektów zwykle implementowanych przez niezależnych dostawców sprzętu i niezależnych dostawców oprogramowania, ponieważ wymaga dwóch wejść: strumienia audio z mikrofonu oraz strumienia audio z urządzenia renderującego, które działa jako sygnał referencyjny.
Ten nowy zestaw interfejsów umożliwia AEC APO zidentyfikowanie się jako taki dla silnika audio. W ten sposób silnik audio odpowiednio konfiguruje APO z wieloma wejściami i jednym wyjściem.
Gdy nowe interfejsy AEC zostaną zaimplementowane przez APO, aparat audio będzie:
- Skonfiguruj obiekt APO z dodatkowym wejściem, zapewniając obiektowi APO strumień referencyjny z odpowiedniego punktu renderowania.
- Przełącz strumienie odwołań w miarę zmiany urządzenia renderowania.
- Umożliwienie mechanizmowi APO kontrolowania formatu mikrofonu wejściowego i strumienia odniesienia.
- Zezwalaj obiektowi APO na uzyskiwanie znaczników czasu z mikrofonu i strumieni referencyjnych.
Poprzednie podejście — Windows 10
Obiekty APOs to pojedyncze dane wejściowe — pojedyncze obiekty wyjściowe. Silnik audio dostarcza AEC APO dźwięk z punktu końcowego mikrofonu na wejściu. Aby uzyskać strumień odwołania, obiekt APO może wchodzić w interakcje ze sterownikiem przy użyciu zastrzeżonych interfejsów w celu pobrania dźwięku referencyjnego z punktu końcowego renderowania lub użyć interfejsu WASAPI, aby otworzyć strumień sprzężenia zwrotnego w punkcie końcowym renderowania.
Oba powyższe podejścia mają wady:
Obiekt AEC APO, który używa kanałów prywatnych do uzyskania strumienia referencyjnego ze sterownika, zazwyczaj może to zrobić tylko poprzez zintegrowane urządzenie renderujące dźwięk. W związku z tym anulowanie echa nie będzie działać, jeśli użytkownik odtwarza dźwięk poza zintegrowanym urządzeniem, takim jak usb lub urządzenie audio Bluetooth. Tylko system operacyjny zdaje sobie sprawę z odpowiednich punktów końcowych renderowania, które mogą służyć jako punkty końcowe odniesienia.
Obiekt APO może użyć interfejsu WASAPI, aby wybrać domyślny punkt końcowy renderowania w celu przeprowadzenia anulowania echa. Istnieją jednak pewne pułapki, o których należy pamiętać podczas otwierania strumienia sprzężenia zwrotnego z procesu audiodg.exe (w którym jest hostowany obiekt APO).
- Nie można otworzyć/usunąć strumienia sprzężenia zwrotnego, gdy aparat audio wywołuje główne metody APO, ponieważ może to spowodować deadlock.
- Obiekt APO przechwytywania nie zna stanu strumieni danych swoich klientów. To znaczy aplikacja przechwytywania może mieć strumień przechwytywania w stanie "STOP", jednakże APO nie jest świadome tego stanu, i dlatego utrzymuje strumień sprzężenia zwrotnego otwarty w stanie "RUN", co jest nieefektywne pod względem zużycia energii.
Definicja interfejsu API — AEC
Struktura AEC udostępnia nowe struktury i interfejsy, których mogą używać APOs. Te nowe struktury i interfejsy zostały opisane poniżej.
struktura APO_CONNECTION_PROPERTY_V2
Obiekty APOs implementujące interfejs IApoAcousticEchoCancellation zostanie przekazana struktura APO_CONNECTION_PROPERTY_V2 w wywołaniu do IAudioProcessingObjectRT::APOProcess. Oprócz wszystkich pól w strukturze APO_CONNECTION_PROPERTY, wersja 2 struktury zapewnia również informacje o sygnaturach czasowych dla buforów audio.
Obiekt APO może przeanalizować pole APO_CONNECTION_PROPERTY.u32Signature, aby określić, czy struktura otrzymana z silnika audio jest typu APO_CONNECTION_PROPERTY, czy APO_CONNECTION_PROPERTY_V2. APO_CONNECTION_PROPERTY struktury mają podpis APO_CONNECTION_PROPERTY_SIGNATURE, podczas gdy struktury APO_CONNECTION_PROPERTY_V2 mają podpis równy APO_CONNECTION_PROPERTY_V2_SIGNATURE. Jeśli podpis ma wartość równą APO_CONNECTION_PROPERTY_V2_SIGNATURE, wskaźnik do struktury APO_CONNECTION_PROPERTY może być bezpiecznie rzutowany do wskaźnika APO_CONNECTION_PROPERTY_V2.
Poniższy kod pochodzi z przykładu Aec APO MFX - AecApoMfx.cpp i pokazuje przekształcenie.
if (ppInputConnections[0]->u32Signature == APO_CONNECTION_PROPERTY_V2_SIGNATURE)
{
const APO_CONNECTION_PROPERTY_V2* connectionV2 = reinterpret_cast<const APO_CONNECTION_PROPERTY_V2*>(ppInputConnections[0]);
}
IApoAcousticEchoCancellation
Interfejs IApoAcousticEchoCancellation nie ma na nim jawnych metod. Jego celem jest zidentyfikowanie obiektu AEC APO dla silnika dźwiękowego. Ten interfejs może być implementowany tylko przez efekty trybu (MFX) w punktach końcowych przechwytywania. Zaimplementowanie tego interfejsu na jakimkolwiek innym APO doprowadzi do błędu przy wczytywaniu tego APO. Aby uzyskać ogólne informacje na temat MFX, zobacz Architektura obiektów przetwarzania audio.
Jeśli wpływ trybu na punkt końcowy przechwytywania jest implementowany jako seria połączonych obiektów APO, tylko APO znajdujący się najbliżej urządzenia może zaimplementować ten interfejs. Obiekty APO implementujące ten interfejs będą otrzymywały strukturę APO_CONNECTION_PROPERTY_V2 w wywołaniu metody IAudioProcessingobjectRT::APOProcess. Obiekt APO może sprawdzić sygnaturę APO_CONNECTION_PROPERTY_V2_SIGNATURE właściwości połączenia i wpisać strukturę przychodzących APO_CONNECTION_PROPERTY do struktury APO_CONNECTION_PROPERTY_V2.
Silnik audio zapewnia obsługę ponownego próbkowania dla APO, które implementują interfejs IApoAcousticEchoCancellation, uznając fakt, że APO AEC zwykle uruchamiają swoje algorytmy na określonej częstotliwości próbkowania oraz liczbie kanałów.
Gdy obiekt APO AEC zwraca APOERR_FORMAT_NOT_SUPPORTED w wywołaniu IAudioProcessingObject::OutInputFormatSupported, aparat audio ponownie wywoła IAudioProcessingObject::IsInputFormatSupported na obiekcie APO z formatem wyjściowym NULL i formatem wejściowym innym niż NULL, aby uzyskać sugerowany format. Silnik audio będzie następnie próbował ponownie dźwięk z mikrofonu do sugerowanego formatu przed wysłaniem go do AEC APO. Eliminuje to konieczność zaimplementowania konwersji częstotliwości próbkowania i liczby kanałów przez obiekt AEC APO.
IApoAuxiliaryInputConfiguration
Interfejs IApoAuxiliaryInputConfiguration udostępnia metody, które mogą implementować obiekty APO, aby silnik audio mógł dodawać i usuwać pomocnicze strumienie wejściowe.
Ten interfejs jest implementowany przez moduł AEC APO i używany przez silnik audio do inicjalizacji wejścia referencyjnego. W systemie Windows 11 obiekt AEC APO zostanie zainicjowany tylko przy użyciu jednego pomocniczego wejścia — takiego, który ma strumień dźwięku odniesienia do anulowania echa. Metoda AddAuxiliaryInput zostanie użyta do dodania wejściowego odniesienia w obiekcie APO. Parametry inicjowania będą zawierać odwołanie do punktu końcowego renderowania uzyskanego ze strumienia sprzężenia zwrotnego.
Metoda IsInputFormatSupported jest wywoływana przez silnik audio w celu negocjowania formatów danych na wejściu pomocniczym. Jeśli obiekt AEC APO preferuje określony format, może zwrócić S_FALSE w wywołaniu metody IsInputFormatSupported i określić sugerowany format. Silnik audio przepróbkuje dźwięk referencyjny do sugerowanego formatu, a następnie dostarczy go w pomocniczym wejściu obiektu AEC APO.
IApoAuxiliaryInputRT
Interfejs IApoAuxiliaryInputRT to interfejs bezpieczny do pracy w czasie rzeczywistym, używany do obsługi wejść pomocniczych APO.
Ten interfejs służy do dostarczania danych dźwiękowych na wejściu pomocniczym do modułu APO. Należy pamiętać, że pomocnicze dane wejściowe audio nie są synchronizowane z wywołaniami interfejsu IAudioProcessingObjectRT::APOProcess. Kiedy nie jest odtwarzany dźwięk przez punkt końcowy renderowania, dane pętli zwrotnej nie będą dostępne na wejściu pomocniczym. tj. nie będzie żadnych wywołań do IApoAuxiliaryInputRT::AcceptInput
Podsumowanie interfejsów API AEC CAPX
Aby uzyskać więcej informacji, znajdź dodatkowe informacje na następujących stronach.
- struktura APO_CONNECTION_PROPERTY_V2 (audioapotypes.h)
- Interfejs IApoAcousticEchoCancellation
- IApoAuxiliaryInputConfiguration
- IApoAuxiliaryInputRT
Przykładowy kod — AEC
Zapoznaj się z następującymi przykładami kodu Sysvad Audio AecApo.
Poniższy kod z przykładowego nagłówka AEC APO — AecAPO.h przedstawia trzy dodawane nowe metody publiczne.
public IApoAcousticEchoCancellation,
public IApoAuxiliaryInputConfiguration,
public IApoAuxiliaryInputRT
...
COM_INTERFACE_ENTRY(IApoAcousticEchoCancellation)
COM_INTERFACE_ENTRY(IApoAuxiliaryInputConfiguration)
COM_INTERFACE_ENTRY(IApoAuxiliaryInputRT)
...
// IAPOAuxiliaryInputConfiguration
STDMETHOD(AddAuxiliaryInput)(
DWORD dwInputId,
UINT32 cbDataSize,
BYTE *pbyData,
APO_CONNECTION_DESCRIPTOR *pInputConnection
) override;
STDMETHOD(RemoveAuxiliaryInput)(
DWORD dwInputId
) override;
STDMETHOD(IsInputFormatSupported)(
IAudioMediaType* pRequestedInputFormat,
IAudioMediaType** ppSupportedInputFormat
) override;
...
// IAPOAuxiliaryInputRT
STDMETHOD_(void, AcceptInput)(
DWORD dwInputId,
const APO_CONNECTION_PROPERTY *pInputConnection
) override;
// IAudioSystemEffects3
STDMETHODIMP GetControllableSystemEffectsList(_Outptr_result_buffer_maybenull_(*numEffects) AUDIO_SYSTEMEFFECT** effects, _Out_ UINT* numEffects, _In_opt_ HANDLE event) override
{
UNREFERENCED_PARAMETER(effects);
UNREFERENCED_PARAMETER(numEffects);
UNREFERENCED_PARAMETER(event);
return S_OK;
}
Poniższy kod pochodzi z przykładu AEC APO MFX — AecApoMfx.cpp i pokazuje implementację addAuxiliaryInput, gdy obiekt APO może obsługiwać tylko jedno pomocnicze dane wejściowe.
STDMETHODIMP
CAecApoMFX::AddAuxiliaryInput(
DWORD dwInputId,
UINT32 cbDataSize,
BYTE *pbyData,
APO_CONNECTION_DESCRIPTOR * pInputConnection
)
{
HRESULT hResult = S_OK;
CComPtr<IAudioMediaType> spSupportedType;
ASSERT_NONREALTIME();
IF_TRUE_ACTION_JUMP(m_bIsLocked, hResult = APOERR_APO_LOCKED, Exit);
IF_TRUE_ACTION_JUMP(!m_bIsInitialized, hResult = APOERR_NOT_INITIALIZED, Exit);
BOOL bSupported = FALSE;
hResult = IsInputFormatSupportedForAec(pInputConnection->pFormat, &bSupported);
IF_FAILED_JUMP(hResult, Exit);
IF_TRUE_ACTION_JUMP(!bSupported, hResult = APOERR_FORMAT_NOT_SUPPORTED, Exit);
// This APO can only handle 1 auxiliary input
IF_TRUE_ACTION_JUMP(m_auxiliaryInputId != 0, hResult = APOERR_NUM_CONNECTIONS_INVALID, Exit);
m_auxiliaryInputId = dwInputId;
Zapoznaj się również z przykładowym kodem, który pokazuje zarówno implementację CAecApoMFX::IsInputFormatSupported i CAecApoMFX::AcceptInput, jak i obsługę APO_CONNECTION_PROPERTY_V2.
Sekwencja operacji — AEC
Podczas inicjowania:
- IAudioProcessingObject::Initialize
- IApoAuxiliaryInputConfiguration::AddAuxiliaryInput
- IAudioProcessingObjectConfiguration:: LockForProcess
- IAudioProcessingObjectConfiguration ::UnlockForProcess
- IApoAuxiliaryInputConfiguration::RemoveAuxiliaryInput
Po zmianie urządzenia renderowania:
- IAudioProcessingObject::Initialize
- IApoAuxiliaryInputConfiguration::AddAuxiliaryInput
- IAudioProcessingObjectConfiguration::LockForProcess
- Zmiany dotyczące domyślnego urządzenia
- IAudioProcessingObjectConfiguration::UnlockForProcess
- IApoAuxiliaryInputConfiguration::RemoveAuxiliaryInput
- IApoAuxiliaryInputConfiguration::AddAuxiliaryInput
- IAudioProcessingObjectConfiguration::LockForProcess
Zalecane zachowanie buforowania — AEC
Jest to zalecane zachowanie bufora dla AEC.
- Bufory uzyskane w wywołaniu metody IApoAuxiliaryInputRT::AcceptInput należy zapisać w buforze cyklicznym, nie blokując głównego wątku.
- Podczas wywołania metody IAudioProcessingObjectRT::APOProcess bufor okrągły powinien być odczytany dla najświeższego pakietu audio ze strumienia referencyjnego, a ten pakiet powinien być używany do przetwarzania w algorytmie usuwania echa.
- Sygnatury czasowe na danych referencyjnych i mikrofonowych mogą służyć do synchronizowania danych głośnika i mikrofonu.
Strumień pętli zwrotnej odwołań
Domyślnie strumień sprzężenia zwrotnego nasłuchuje strumienia audio przed zastosowaniem dowolnej głośności lub wyciszenia. Strumień sprzężenia zwrotnego przechwycony przed zastosowaniem głośności jest znany jako strumień sprzężenia zwrotnego przed-głośnością. Zaletą korzystania ze strumienia sprzężenia zwrotnego przedwzmacniacza jest jasny i jednolity strumień audio, niezależnie od bieżącego ustawienia głośności.
Niektóre algorytmy AEC mogą preferować uzyskanie strumienia pętli zwrotnej, który został połączony po wszelkiej obróbce dźwięku (w tym wyciszeniu). Ta konfiguracja jest znana jako pętla zwrotna po woluminie.
W następnej wersji głównej obiektów API systemu Windows AEC można zażądać sprzężenia zwrotnego po woluminie w obsługiwanych punktach końcowych.
Ograniczenia
W przeciwieństwie do strumieni sprzężenia zwrotnego przed woluminem, które są dostępne dla wszystkich punktów końcowych renderowania, strumienie sprzężenia zwrotnego po woluminie mogą nie być dostępne we wszystkich punktach końcowych.
Żądanie sprzężenia zwrotnego po woluminie
AEC APOs, które chcą używać sprzężenia zwrotnego po woluminie, powinny zaimplementować interfejs IApoAcousticEchoCancellation2 .
Obiekt APO AEC może zażądać sprzężenia zwrotnego po przetworzeniu głośności, zwracając flagę APO_REFERENCE_STREAM_PROPERTIES_POST_VOLUME_LOOPBACK za pośrednictwem parametru Właściwości w implementacji IApoAcousticEchoCancellation2::GetDesiredReferenceStreamProperties.
W zależności od aktualnie używanego punktu końcowego renderowania pętla zwrotna po woluminie może być niedostępna. Obiekt APO AEC jest powiadamiany, jeśli jest używana pętla zwrotna po woluminie, gdy jest wywoływana metoda IApoAuxiliaryInputConfiguration::AddAuxiliaryInput . Jeśli pole AcousticEchoCanceller_Reference_Input streamProperties zawiera APO_REFERENCE_STREAM_PROPERTIES_POST_VOLUME_LOOPBACK, funkcja sprzężenia zwrotnego po woluminie jest używana.
Poniższy kod z przykładowego nagłówka AEC APO — AecAPO.h przedstawia trzy dodawane nowe metody publiczne.
public:
// IApoAcousticEchoCancellation2
STDMETHOD(GetDesiredReferenceStreamProperties)(
_Out_ APO_REFERENCE_STREAM_PROPERTIES * properties) override;
// IApoAuxiliaryInputConfiguration
STDMETHOD(AddAuxiliaryInput)(
DWORD dwInputId,
UINT32 cbDataSize,
_In_ BYTE* pbyData,
_In_ APO_CONNECTION_DESCRIPTOR *pInputConnection
) override;
Poniższy fragment kodu pochodzi z przykładu AEC APO MFX: AecApoMfx.cpp i pokazuje implementację GetDesiredReferenceStreamProperties oraz odpowiednią część AddAuxiliaryInput.
STDMETHODIMP SampleApo::GetDesiredReferenceStreamProperties(
_Out_ APO_REFERENCE_STREAM_PROPERTIES * properties)
{
RETURN_HR_IF_NULL(E_INVALIDARG, properties);
// Always request that a post-volume loopback stream be used, if
// available. We will find out which type of stream was actually
// created when AddAuxiliaryInput is invoked.
*properties = APO_REFERENCE_STREAM_PROPERTIES_POST_VOLUME_LOOPBACK;
return S_OK;
}
STDMETHODIMP
CAecApoMFX::AddAuxiliaryInput(
DWORD dwInputId,
UINT32 cbDataSize,
BYTE *pbyData,
APO_CONNECTION_DESCRIPTOR * pInputConnection
)
{
// Parameter checking skipped for brevity, please see sample for
// full implementation.
AcousticEchoCanceller_Reference_Input* referenceInput = nullptr;
APOInitSystemEffects3* papoSysFxInit3 = nullptr;
if (cbDataSize == sizeof(AcousticEchoCanceller_Reference_Input))
{
referenceInput =
reinterpret_cast<AcousticEchoCanceller_Reference_Input*>(pbyData);
if (WI_IsFlagSet(
referenceInput->streamProperties,
APO_REFERENCE_STREAM_PROPERTIES_POST_VOLUME_LOOPBACK))
{
// Post-volume loopback is being used.
m_bUsingPostVolumeLoopback = TRUE;
// Note that we can get to the APOInitSystemEffects3 from
// AcousticEchoCanceller_Reference_Input.
papoSysFxInit3 = (APOInitSystemEffects3*)pbyData;
}
else if (cbDataSize == sizeof(APOInitSystemEffects3))
{
// Post-volume loopback is not supported.
papoSysFxInit3 = (APOInitSystemEffects3*)pbyData;
}
// Remainder of method skipped for brevity.
Struktura ustawień
Framework ustawień umożliwia APO udostępnianie metod do wykonywania zapytań i modyfikowania magazynu właściwości dla efektów dźwiękowych ("FX Property Store") na końcówce audio. Ta struktura może być używana przez APO i przez aplikacje wsparcia sprzętowego (HSA), które chcą przekazywać ustawienia do tego APO. Moduły HSA mogą być aplikacjami platformy uniwersalnej systemu Windows (UWP) i wymagają specjalnej możliwości wywoływania interfejsów API w strukturze ustawień. Aby uzyskać więcej informacji na temat aplikacji HSA, zobacz Aplikacje urządzeń platformy uniwersalnej systemu Windows.
Struktura FxProperty Store
Nowy sklep FxProperty ma trzy podstores: Default, User i Volatile.
Podklucz "Domyślny" zawiera właściwości efektów niestandardowych i jest wypełniany z pliku INF. Te właściwości nie są utrwalane w ramach uaktualnień systemu operacyjnego. Na przykład właściwości, które są zwykle zdefiniowane w INF, znajdują tutaj zastosowanie. Następnie zostaną one ponownie uzupełnione z INF.
Podklucz "Użytkownik" zawiera ustawienia użytkownika dotyczące właściwości efektów. Te ustawienia są utrwalane przez system operacyjny w ramach uaktualnień i migracji. Na przykład wszelkie ustawienia wstępne, które użytkownik może skonfigurować, które mają być utrwalane podczas uaktualniania.
Podklucz "Volatile" zawiera właściwości efektów nietrwałych. Te właściwości są tracone po ponownym uruchomieniu urządzenia i są czyszczone za każdym razem, gdy punkt końcowy stanie się aktywny. Powinny one zawierać właściwości zależne od czasu (np. na podstawie aktualnie uruchomionych aplikacji, stanu urządzenia itp.). Na przykład wszelkie ustawienia zależne od obecnego środowiska.
Sposób myślenia o użytkowniku a domyślnym polega na tym, czy właściwości mają być utrzymywane w trakcie aktualizacji systemów operacyjnych i sterowników. Właściwości użytkownika zostaną utrwalone. Właściwości domyślne zostaną ponownie wypełnione z pliku INF.
Konteksty APO
Struktura ustawień CAPX umożliwia autorowi obiektu APO grupowanie właściwości obiektu APO według kontekstów. Każdy obiekt APO może definiować własne właściwości kontekstu i aktualizować je względem własnego kontekstu. Magazyn właściwości efektów dla punktu końcowego audio może mieć zero lub więcej kontekstów. Dostawcy mogą tworzyć konteksty według własnego wyboru, czy to poprzez SFX/MFX/EFX, czy tryb. Dostawca może również wybrać pojedynczy kontekst dla wszystkich APOs dostarczanych przez niego.
Ograniczona zdolność ustawień
Interfejs API ustawień jest przeznaczony do obsługi wszystkich producentów OEM i HSA zainteresowanych wykonywaniem zapytań i modyfikowaniem ustawień efektów dźwiękowych skojarzonych z urządzeniem audio. Ten Interfejs API jest udostępniany aplikacjom HSA i Win32 w celu umożliwienia dostępu do magazynu właściwości za pośrednictwem ograniczonej funkcjonalności "audioDeviceConfiguration", która musi zostać zadeklarowana w manifeście. Ponadto należy zadeklarować odpowiednią przestrzeń nazw w następujący sposób:
<Package
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap mp rescap">
...
<Capabilities>
<rescap:Capability Name="audioDeviceConfiguration" />
</Capabilities>
</Package>
IAudioSystemEffectsPropertyStore jest czytelny i zapisywalny przez usługę ISV/IHV, aplikację sklepu platformy UWP, aplikacje pulpitu, które nie wymagają uprawnień administratora, oraz obiekty APO. Ponadto może to działać jako mechanizm umożliwiający APO przekazywanie komunikatów z powrotem do aplikacji usługi lub aplikacji sklepu UWP.
Uwaga / Notatka
Jest to ograniczone uprawnienie: jeśli aplikacja zostanie przesłana z tym uprawnieniem do Sklepu Microsoft, zostanie poddana dokładnej kontroli. Aplikacja musi być aplikacją do obsługi sprzętu (HSA) i będzie podlegała ocenie, aby upewnić się, że jest ona rzeczywiście HSA przed zatwierdzeniem przesłania.
Definicja interfejsu API — Struktura ustawień
Nowy interfejs IAudioSystemEffectsPropertyStore umożliwia modułowi HSA uzyskiwanie dostępu do magazynów właściwości efektów systemu audio i rejestrowanie w celu otrzymywania powiadomień o zmianie właściwości.
Funkcja ActiveAudioInterfaceAsync udostępnia metodę uzyskiwania asynchronicznego interfejsu IAudioSystemEffectsPropertyStore .
Aplikacja może otrzymywać powiadomienia, gdy zmieni się magazyn właściwości efektów systemowych, przy użyciu nowego interfejsu zwrotnego IAudioSystemEffectsPropertyChangeNotificationClient.
Aplikacja próbująca pobrać IAudioSystemEffectsPropertyStore przy użyciu funkcji IMMDevice::Activate
W przykładzie pokazano, jak aplikacja pomocy technicznej sprzętu może używać funkcji IMMDevice::Activate w celu aktywowania IAudioSystemEffectsPropertyStore. Przykład pokazuje, jak za pomocą interfejsu IAudioSystemEffectsPropertyStore otworzyć IPropertyStore z ustawieniami użytkownika.
#include <mmdeviceapi.h>
// This function opens an IPropertyStore with user settings on the specified IMMDevice.
// Input parameters:
// device - IMMDevice object that identifies the audio endpoint.
// propertyStoreContext - GUID that identifies the property store. Each APO can have its own GUID. These
// GUIDs are chosen by the audio driver at installation time.
HRESULT GetPropertyStoreFromMMDevice(_In_ IMMDevice* device,
REFGUID propertyStoreContext,
_COM_Outptr_ IPropertyStore** userPropertyStore)
{
*userPropertyStore = nullptr;
wil::unique_prop_variant activationParam;
RETURN_IF_FAILED(InitPropVariantFromCLSID(propertyStoreContext, &activationParam));
wil::com_ptr_nothrow<IAudioSystemEffectsPropertyStore> effectsPropertyStore;
RETURN_IF_FAILED(device->Activate(__uuidof(effectsPropertyStore), CLSCTX_INPROC_SERVER, activationParam.addressof(), effectsPropertyStore.put_void()));
RETURN_IF_FAILED(effectsPropertyStore->OpenUserPropertyStore(STGM_READWRITE, userPropertyStore));
return S_OK;
}
Przykład użycia ActivateAudioInterfaceAsync
Ten przykład wykonuje tę samą czynność co w poprzednim przykładzie, ale zamiast używać IMMDevice, używa API ActivateAudioInterfaceAsync, aby asynchronicznie uzyskać interfejs IAudioSystemEffectsPropertyStore.
include <mmdeviceapi.h>
class PropertyStoreHelper :
public winrt::implements<PropertyStoreHelper, IActivateAudioInterfaceCompletionHandler>
{
public:
wil::unique_event_nothrow m_asyncOpCompletedEvent;
HRESULT GetPropertyStoreAsync(
_In_ PCWSTR deviceInterfacePath,
REFGUID propertyStoreContext,
_COM_Outptr_ IActivateAudioInterfaceAsyncOperation** operation);
HRESULT GetPropertyStoreResult(_COM_Outptr_ IPropertyStore** userPropertyStore);
// IActivateAudioInterfaceCompletionHandler
STDMETHOD(ActivateCompleted)(_In_ IActivateAudioInterfaceAsyncOperation *activateOperation);
private:
wil::com_ptr_nothrow<IPropertyStore> m_userPropertyStore;
HRESULT m_hrAsyncOperationResult = E_FAIL;
HRESULT GetUserPropertyStore(
_In_ IActivateAudioInterfaceAsyncOperation* operation,
_COM_Outptr_ IPropertyStore** userPropertyStore);
};
// This function opens an IPropertyStore with user settings asynchronously on the specified audio endpoint.
// Input parameters:
// deviceInterfacePath - the Device Interface Path string that identifies the audio endpoint. Can be
// obtained from Windows.Devices.Enumeration.DeviceInformation.
// propertyStoreContext - GUID that identifies the property store. Each APO can have its own GUID. These
// GUIDs are chosen by the audio driver at installation time.
//
// The function returns an IActivateAudioInterfaceAsyncOperation, which can be used to check the result of
// the asynchronous operation.
HRESULT PropertyStoreHelper::GetPropertyStoreAsync(
_In_ PCWSTR deviceInterfacePath,
REFGUID propertyStoreContext,
_COM_Outptr_ IActivateAudioInterfaceAsyncOperation** operation)
{
*operation = nullptr;
wil::unique_prop_variant activationParam;
RETURN_IF_FAILED(InitPropVariantFromCLSID(propertyStoreContext, &activationParam));
RETURN_IF_FAILED(ActivateAudioInterfaceAsync(deviceInterfacePath,
__uuidof(IAudioSystemEffectsPropertyStore),
activationParam.addressof(),
this,
operation));
return S_OK;
}
// Once the IPropertyStore is available, the app can call this function to retrieve it.
// (The m_asyncOpCompletedEvent event is signaled when the asynchronous operation to retrieve
// the IPropertyStore has completed.)
HRESULT PropertyStoreHelper::GetPropertyStoreResult(_COM_Outptr_ IPropertyStore** userPropertyStore)
{
*userPropertyStore = nullptr;
// First check if the asynchronous operation completed. If it failed, the error code
// is stored in the m_hrAsyncOperationResult variable.
RETURN_IF_FAILED(m_hrAsyncOperationResult);
RETURN_IF_FAILED(m_userPropertyStore.copy_to(userPropertyStore));
return S_OK;
}
// Implementation of IActivateAudioInterfaceCompletionHandler::ActivateCompleted.
STDMETHODIMP PropertyStoreHelper::ActivateCompleted(_In_ IActivateAudioInterfaceAsyncOperation* operation)
{
m_hrAsyncOperationResult = GetUserPropertyStore(operation, m_userPropertyStore.put());
// Always signal the event that our caller might be waiting on before we exit,
// even in case of failure.
m_asyncOpCompletedEvent.SetEvent();
return S_OK;
}
HRESULT PropertyStoreHelper::GetUserPropertyStore(
_In_ IActivateAudioInterfaceAsyncOperation* operation,
_COM_Outptr_ IPropertyStore** userPropertyStore)
{
*userPropertyStore = nullptr;
// Check if the asynchronous operation completed successfully, and retrieve an
// IUnknown pointer to the result.
HRESULT hrActivateResult;
wil::com_ptr_nothrow<IUnknown> audioInterfaceUnknown;
RETURN_IF_FAILED(operation->GetActivateResult(&hrActivateResult, audioInterfaceUnknown.put()));
RETURN_IF_FAILED(hrActivateResult);
// Convert the result to IAudioSystemEffectsPropertyStore
wil::com_ptr_nothrow<IAudioSystemEffectsPropertyStore> effctsPropertyStore;
RETURN_IF_FAILED(audioInterfaceUnknown.query_to(&effectsPropertyStore));
// Open an IPropertyStore with the user settings.
RETURN_IF_FAILED(effectsPropertyStore->OpenUserPropertyStore(STGM_READWRITE, userPropertyStore));
return S_OK;
}
Kodu inicjalizacji IAudioProcessingObject za pomocą IAudioSystemEffectsPropertyStore
Przykład demonstruje, w jaki sposób implementacja APO może korzystać ze struktury APOInitSystemEffects3, aby pobrać interfejsy IPropertyStore użytkownika, domyślne oraz tymczasowe dla APO podczas jego inicjacji.
#include <audioenginebaseapo.h>
// Partial implementation of APO to show how an APO that implements IAudioSystemEffects3 can handle
// being initialized with the APOInitSystemEffects3 structure.
class SampleApo : public winrt::implements<SampleApo, IAudioProcessingObject,
IAudioSystemEffects, IAudioSystemEffects2, IAudioSystemEffects3>
{
public:
// IAudioProcessingObject
STDMETHOD(Initialize)(UINT32 cbDataSize, BYTE* pbyData);
// Implementation of IAudioSystemEffects2, IAudioSystemEffects3 has been omitted from this sample for brevity.
private:
wil::com_ptr_nothrow<IPropertyStore> m_defaultStore;
wil::com_ptr_nothrow<IPropertyStore> m_userStore;
wil::com_ptr_nothrow<IPropertyStore> m_volatileStore;
// Each APO has its own private collection of properties. The collection is identified through a
// a property store context GUID, which is defined below and in the audio driver INF file.
const GUID m_propertyStoreContext = ...;
};
// Implementation of IAudioProcessingObject::Initialize
STDMETHODIMP SampleApo::Initialize(UINT32 cbDataSize, BYTE* pbyData)
{
if (cbDataSize == sizeof(APOInitSystemEffects3))
{
// SampleApo supports the new IAudioSystemEffects3 interface so it will receive APOInitSystemEffects3
// in pbyData if the audio driver has declared support for this.
// Use IMMDevice to activate IAudioSystemEffectsPropertyStore that contains the default, user and
// volatile settings.
IMMDeviceCollection* deviceCollection =
reinterpret_cast<APOInitSystemEffects3*>(pbyData)->pDeviceCollection;
if (deviceCollection != nullptr)
{
UINT32 numDevices;
wil::com_ptr_nothrow<IMMDevice> endpoint;
// Get the endpoint on which this APO has been created
// (It is the last device in the device collection)
if (SUCCEEDED(deviceCollection->GetCount(&numDevices)) &&
numDevices > 0 &&
SUCCEEDED(deviceCollection->Item(numDevices - 1, &endpoint)))
{
wil::unique_prop_variant activationParam;
RETURN_IF_FAILED(InitPropVariantFromCLSID(m_propertyStoreContext, &activationParam));
wil::com_ptr_nothrow<IAudioSystemEffectsPropertyStore> effectsPropertyStore;
RETURN_IF_FAILED(endpoint->Activate(__uuidof(effectsPropertyStore), CLSCTX_ALL, &activationParam, effectsPropertyStore.put_void()));
// Read default, user and volatile property values to set up initial operation of the APO
RETURN_IF_FAILED(effectsPropertyStore->OpenDefaultPropertyStore(STGM_READWRITE, m_defaultStore.put()));
RETURN_IF_FAILED(effectsPropertyStore->OpenUserPropertyStore(STGM_READWRITE, m_userStore.put()));
RETURN_IF_FAILED(effectsPropertyStore->OpenVolatilePropertyStore(STGM_READWRITE, m_volatileStore.put()));
// At this point the APO can read and write settings in the various property stores,
// as appropriate. (Not shown.)
// Note that APOInitSystemEffects3 contains all the members of APOInitSystemEffects2,
// so an APO that knows how to initialize from APOInitSystemEffects2 can use the same
// code to continue its initialization here.
}
}
}
else if (cbDataSize == sizeof(APOInitSystemEffects2))
{
// Use APOInitSystemEffects2 for the initialization of the APO.
// If we get here, the audio driver did not declare support for IAudioSystemEffects3.
}
else if (cbDataSize == sizeof(APOInitSystemEffects))
{
// Use APOInitSystemEffects for the initialization of the APO.
}
return S_OK;
}
Rejestrowanie aplikacji w celu otrzymywania powiadomień o zmianie właściwości
W przykładzie pokazano użycie rejestracji dla powiadomień o zmianie właściwości. Nie należy tego używać z obiektu APO i należy to używać w aplikacjach Win32.
class PropertyChangeNotificationClient : public
winrt::implements<PropertyChangeNotificationClient, IAudioSystemEffectsPropertyChangeNotificationClient>
{
private:
wil::com_ptr_nothrow<IAudioSystemEffectsPropertyStore> m_propertyStore;
bool m_isListening = false;
public:
HRESULT OpenPropertyStoreOnDefaultRenderEndpoint(REFGUID propertyStoreContext);
HRESULT StartListeningForPropertyStoreChanges();
HRESULT StopListeningForPropertyStoreChanges();
// IAudioSystemEffectsPropertyChangeNotificationClient
STDMETHOD(OnPropertyChanged)(AUDIO_SYSTEMEFFECTS_PROPERTYSTORE_TYPE type, const PROPERTYKEY key);
};
// Open the IAudioSystemEffectsPropertyStore. This should be the first method invoked on this class.
HRESULT PropertyChangeNotificationClient::OpenPropertyStoreOnDefaultRenderEndpoint(
REFGUID propertyStoreContext)
{
wil::com_ptr_nothrow<IMMDeviceEnumerator> deviceEnumerator;
RETURN_IF_FAILED(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&deviceEnumerator)));
wil::com_ptr_nothrow<IMMDevice> device;
RETURN_IF_FAILED(deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, device.put()));
wil::unique_prop_variant activationParam;
RETURN_IF_FAILED(InitPropVariantFromCLSID(propertyStoreContext, &activationParam));
RETURN_IF_FAILED(device->Activate(__uuidof(m_propertyStore), CLSCTX_INPROC_SERVER,
&activationParam, m_propertyStore.put_void()));
return S_OK;
}
// Start subscribing to callbacks that are invoked when there are changes to any of the IPropertyStores
// that are managed by IAudioSystemEffectsPropertyStore.
// The OpenPropertyStoreOnDefaultRenderEndpoint should have been invoked prior to invoking this function.
HRESULT PropertyChangeNotificationClient::StartListeningForPropertyStoreChanges()
{
RETURN_HR_IF(E_FAIL, !m_propertyStore);
RETURN_IF_FAILED(m_propertyStore->RegisterPropertyChangeNotification(this));
m_isListening = true;
return S_OK;
}
// Unsubscribe to event callbacks. Since IAudioSystemEffectsPropertyStore takes a reference on our
// PropertyChangeNotificationClient class, it is important that this method is invoked prior to cleanup,
// to break the circular reference.
HRESULT PropertyChangeNotificationClient::StopListeningForPropertyStoreChanges()
{
if (m_propertyStore != nullptr && m_isListening)
{
RETURN_IF_FAILED(m_propertyStore->UnregisterPropertyChangeNotification(this));
m_isListening = false;
}
return S_OK;
}
// Callback method that gets invoked when there have been changes to any of the IPropertyStores
// that are managed by IAudioSystemEffectsPropertyStore. Note that calls to
// IAudioSystemEffectsPropertyChangeNotificationClient are not marshalled across COM apartments.
// Therefore, the OnPropertyChanged is most likely invoked on a different thread than the one used when
// invoking RegisterPropertyChangeNotification. If necessary, concurrent access to shared state should be
// protected with a critical section.
STDMETHODIMP PropertyChangeNotificationClient::OnPropertyChanged(AUDIO_SYSTEMEFFECTS_PROPERTYSTORE_TYPE type, const PROPERTYKEY key)
{
if (type == AUDIO_SYSTEMEFFECTS_PROPERTYSTORE_TYPE_USER)
{
// Handle changes to the User property store.
wil::com_ptr_nothrow<IPropertyStore> userPropertyStore;
RETURN_IF_FAILED(m_propertyStore->OpenUserPropertyStore(STGM_READ, userPropertyStore.put()));
// Here we can call IPropertyStore::GetValue to read the current value of PROPERTYKEYs that we are
// interested in.
}
else if (type == AUDIO_SYSTEMEFFECTS_PROPERTYSTORE_TYPE_VOLATILE)
{
// Handle changes to the Volatile property store, if desired
}
return S_OK;
}
Przykładowy kod — struktura ustawień
Ten przykładowy kod pochodzi z przykładu sysvad SFX Swap APO — SwapAPOSFX.cpp.
// SampleApo supports the new IAudioSystemEffects3 interface so it will receive APOInitSystemEffects3
// in pbyData if the audio driver has declared support for this.
// Use IMMDevice to activate IAudioSystemEffectsPropertyStore that contains the default, user and
// volatile settings.
IMMDeviceCollection* deviceCollection = reinterpret_cast<APOInitSystemEffects3*>(pbyData)->pDeviceCollection;
if (deviceCollection != nullptr)
{
UINT32 numDevices;
wil::com_ptr_nothrow<IMMDevice> endpoint;
// Get the endpoint on which this APO has been created
// (It is the last device in the device collection)
if (SUCCEEDED(deviceCollection->GetCount(&numDevices)) && numDevices > 0 &&
SUCCEEDED(deviceCollection->Item(numDevices - 1, &endpoint)))
{
wil::unique_prop_variant activationParam;
hr = InitPropVariantFromCLSID(SWAP_APO_SFX_CONTEXT, &activationParam);
IF_FAILED_JUMP(hr, Exit);
wil::com_ptr_nothrow<IAudioSystemEffectsPropertyStore> effectsPropertyStore;
hr = endpoint->Activate(__uuidof(effectsPropertyStore), CLSCTX_ALL, &activationParam, effectsPropertyStore.put_void());
IF_FAILED_JUMP(hr, Exit);
// This is where an APO might want to open the volatile or default property stores as well
// Use STGM_READWRITE if IPropertyStore::SetValue is needed.
hr = effectsPropertyStore->OpenUserPropertyStore(STGM_READ, m_userStore.put());
IF_FAILED_JUMP(hr, Exit);
}
}
Sekcja INF — Struktura ustawień
Składnia pliku INF do deklarowania właściwości efektu przy użyciu nowej struktury ustawień CAPX jest następująca:
HKR, FX\0\{ApoContext}\{Default|User}, %CUSTOM_PROPERTY_KEY%,,,
Zastępuje to starszą składnię deklarowania właściwości efektu w następujący sposób:
# Old way of declaring FX properties
HKR, FX\0, %CUSTOM_PROPERTY_KEY_1%,,,
INF nie może mieć zarówno wpisu IAudioSystemEffectsPropertyStore, jak i wpisu IPropertyStore dla tego samego punktu końcowego audio. To nie jest obsługiwane.
Przykład przedstawiający użycie nowego magazynu właściwości:
HKR,FX\0\%SWAP_APO_CONTEXT%,%PKEY_FX_Association%,,%KSNODETYPE_ANY%
; Enable the channel swap in the APO
HKR,FX\0\%SWAP_APO_CONTEXT%\User,%PKEY_Endpoint_Enable_Channel_Swap_SFX%,REG_DWORD,0x1
PKEY_Endpoint_Enable_Channel_Swap_SFX = "{A44531EF-5377-4944-AE15-53789A9629C7},2"
REG_DWORD = 0x00010001 ; FLG_ADDREG_TYPE_DWORD
SWAP_APO_CONTEXT = "{24E7F619-5B33-4084-9607-878DA8722417}"
PKEY_FX_Association = "{D04E05A6-594B-4FB6-A80D-01AF5EED7D1D},0"
KSNODETYPE_ANY = "{00000000-0000-0000-0000-000000000000}"
Struktura powiadomień
Platforma Powiadomień umożliwia efektom dźwiękowym (APO) wysyłanie żądań oraz obsługę powiadomień o zmianach woluminu, punktu końcowego i właściwości efektów dźwiękowych. Ta struktura ma zastąpić istniejące interfejsy API, które są używane przez APOs do rejestrowania i wyrejestrowywania się z powiadomień.
Nowe API wprowadza interfejs, którego APO mogą używać do określania typów powiadomień, którymi są zainteresowane. System Windows zapyta obiekt APO o powiadomienia, które go interesują, i przekaże te powiadomienia do obiektów APO. APOs nie muszą już jawnie wywoływać interfejsów API rejestracji ani wyrejestrowania.
Powiadomienia są dostarczane do APO przy użyciu kolejki szeregowej. Jeśli ma to zastosowanie, pierwsze powiadomienie emituje początkowy stan żądanej wartości (na przykład wolumin punktu końcowego audio). Powiadomienia przestają działać, gdy audiodg.exe przestaje zamierzać używać obiektu APO do przesyłania strumieniowego. APOs przestaną otrzymywać powiadomienia po UnlockForProcess. Nadal konieczne jest zsynchronizowanie funkcji UnlockForProcess i wszystkich powiadomień w locie.
Implementacja — struktura powiadomień
Aby korzystać z platformy powiadomień, obiekt APO deklaruje, jakimi powiadomieniami jest zainteresowany. Nie ma jawnych wywołań rejestracji/wyrejestrowania. Wszystkie powiadomienia do obiektu APO są serializowane i ważne jest, aby nie blokować wątku wywołania zwrotnego powiadomień zbyt długo.
Definicja interfejsu API — Struktura powiadomień
Struktura powiadomień implementuje nowy interfejs IAudioProcessingObjectNotifications, który klienci mogą zaimplementować w celu rejestracji i odbierania typowych powiadomień związanych z dźwiękiem, dotyczących punktów końcowych APO i powiadomień o efektach systemowych.
Aby uzyskać więcej informacji, znajdź dodatkową zawartość na następujących stronach:
Przykładowy kod — struktura powiadomień
W przykładzie pokazano, jak obiekt APO może zaimplementować interfejs IAudioProcessingObjectNotifications. W metodzie GetApoNotificationRegistrationInfo przykładowy obiekt APO rejestruje powiadomienia o zmianach w magazynach właściwości efektów systemowych.
Metoda HandleNotification jest wywoływana przez system operacyjny w celu powiadomienia obiektu APO o zmianach, które są zgodne z tym, co obiekt APO zarejestrował.
class SampleApo : public winrt::implements<SampleApo, IAudioProcessingObject,
IAudioSystemEffects, IAudioSystemEffects2, IAudioSystemEffects3,
IAudioProcessingObjectNotifications>
{
public:
// IAudioProcessingObjectNotifications
STDMETHOD(GetApoNotificationRegistrationInfo)(
_Out_writes_(count) APO_NOTIFICATION_DESCRIPTOR** apoNotifications, _Out_ DWORD* count);
STDMETHOD_(void, HandleNotification)(_In_ APO_NOTIFICATION *apoNotification);
// Implementation of IAudioSystemEffects2, IAudioSystemEffects3 has been omitted from this sample for brevity.
private:
wil::com_ptr_nothrow<IMMDevice> m_device;
// Each APO has its own private collection of properties. The collection is identified through a
// a property store context GUID, which is defined below and in the audio driver INF file.
const GUID m_propertyStoreContext = ...;
float m_masterVolume = 1.0f;
BOOL m_isMuted = FALSE;
BOOL m_allowOffloading = FALSE;
// The rest of the implementation of IAudioProcessingObject is omitted for brevity
};
// The OS invokes this method on the APO to find out what notifications the APO is interested in.
STDMETHODIMP SampleApo::GetApoNotificationRegistrationInfo(
_Out_writes_(count) APO_NOTIFICATION_DESCRIPTOR** apoNotificationDescriptorsReturned,
_Out_ DWORD* count)
{
*apoNotificationDescriptorsReturned = nullptr;
*count = 0;
// Before this function can be called, our m_device member variable should already have been initialized.
// This would typically be done in our implementation of IAudioProcessingObject::Initialize, by using
// APOInitSystemEffects3::pDeviceCollection to obtain the last IMMDevice in the collection.
RETURN_HR_IF_NULL(E_FAIL, m_device);
// Let the OS know what notifications we are interested in by returning an array of
// APO_NOTIFICATION_DESCRIPTORs.
constexpr DWORD numDescriptors = 3;
wil::unique_cotaskmem_ptr<APO_NOTIFICATION_DESCRIPTOR[]> apoNotificationDescriptors;
apoNotificationDescriptors.reset(static_cast<APO_NOTIFICATION_DESCRIPTOR*>(
CoTaskMemAlloc(sizeof(APO_NOTIFICATION_DESCRIPTOR) * numDescriptors)));
RETURN_IF_NULL_ALLOC(apoNotificationDescriptors);
// Our APO wants to get notified when any change occurs on the user property store on the audio endpoint
// identified by m_device.
// The user property store is different for each APO. Ours is identified by m_propertyStoreContext.
apoNotificationDescriptors[0].type = APO_NOTIFICATION_TYPE_AUDIO_SYSTEM_EFFECTS_PROPERTY_CHANGE;
(void)m_device.query_to(&apoNotificationDescriptors[0].audioSystemEffectsPropertyChange.device);
apoNotificationDescriptors[0].audioSystemEffectsPropertyChange.propertyStoreContext = m_propertyStoreContext;
// Our APO wants to get notified when an endpoint property changes on the audio endpoint.
apoNotificationDescriptors[1].type = APO_NOTIFICATION_TYPE_ENDPOINT_PROPERTY_CHANGE;
(void)m_device.query_to(&apoNotificationDescriptors[1].audioEndpointPropertyChange.device);
// Our APO also wants to get notified when the volume level changes on the audio endpoint.
apoNotificationDescriptors [2].type = APO_NOTIFICATION_TYPE_ENDPOINT_VOLUME;
(void)m_device.query_to(&apoNotificationDescriptors[2].audioEndpointVolume.device);
*apoNotificationDescriptorsReturned = apoNotificationDescriptors.release();
*count = numDescriptors;
return S_OK;
}
static bool IsSameEndpointId(IMMDevice* device1, IMMDevice* device2)
{
bool isSameEndpointId = false;
wil::unique_cotaskmem_string deviceId1;
if (SUCCEEDED(device1->GetId(&deviceId1)))
{
wil::unique_cotaskmem_string deviceId2;
if (SUCCEEDED(device2->GetId(&deviceId2)))
{
isSameEndpointId = (CompareStringOrdinal(deviceId1.get(), -1, deviceId2.get(), -1, TRUE) == CSTR_EQUAL);
}
}
return isSameEndpointId;
}
// HandleNotification is called whenever there is a change that matches any of the
// APO_NOTIFICATION_DESCRIPTOR elements in the array that was returned by GetApoNotificationRegistrationInfo.
// Note that the APO will have to query each property once to get its initial value because this method is
// only invoked when any of the properties have changed.
STDMETHODIMP_(void) SampleApo::HandleNotification(_In_ APO_NOTIFICATION* apoNotification)
{
// Check if a property in the user property store has changed.
if (apoNotification->type == APO_NOTIFICATION_TYPE_AUDIO_SYSTEM_EFFECTS_PROPERTY_CHANGE
&& IsSameEndpointId(apoNotification->audioSystemEffectsPropertyChange.endpoint, m_device.get())
&& apoNotification->audioSystemEffectsPropertyChange.propertyStoreContext == m_propertyStoreContext
&& apoNotification->audioSystemEffectsPropertyChange.propertyStoreType == AUDIO_SYSTEMEFFECTS_PROPERTYSTORE_TYPE_USER)
{
// Check if one of the properties that we are interested in has changed.
// As an example, we check for "PKEY_Endpoint_Enable_Channel_Swap_SFX" which is a fictitious
// PROPERTYKEY that could be set on our user property store.
if (apoNotification->audioSystemEffectsPropertyChange.propertyKey ==
PKEY_Endpoint_Enable_Channel_Swap_SFX)
{
wil::unique_prop_variant var;
if (SUCCEEDED(apoNotification->audioSystemEffectsPropertyChange.propertyStore->GetValue(
PKEY_Endpoint_Enable_Channel_Swap_SFX, &var)) &&
var.vt != VT_EMPTY)
{
// We have retrieved the property value. Now we can do something interesting with it.
}
}
}
else if (apoNotification->type == APO_NOTIFICATION_TYPE_ENDPOINT_PROPERTY_CHANGE
&& IsSameEndpointId(apoNotification->audioEndpointPropertyChange.endpoint, m_device.get())
{
// Handle changes to PROPERTYKEYs in the audio endpoint's own property store.
// In this example, we are interested in a property called "PKEY_Endpoint_AllowOffloading" that the
// user might change in the audio control panel, and we update our member variable if this
// property changes.
if (apoNotification->audioEndpointPropertyChange.propertyKey == PKEY_Endpoint_AllowOffloading)
{
wil::unique_prop_variant var;
if (SUCCEEDED(propertyStore->GetValue(PKEY_Endpoint_AllowOffloading, &var)) && var.vt == VT_BOOL)
{
m_allowOffloading = var.boolVal;
}
}
}
else if (apoNotification->type == APO_NOTIFICATION_TYPE_ENDPOINT_VOLUME
&& IsSameEndpointId(apoNotification->audioEndpointVolumeChange.endpoint, m_device.get())
{
// Handle endpoint volume change
m_masterVolume = apoNotification->audioEndpointVolumeChange.volume->fMasterVolume;
m_isMuted = apoNotification->audioEndpointVolumeChange.volume->bMuted;
}
}
Poniższy kod pochodzi z przykładu swap APO MFX — swapapomfx.cpp i pokazuje rejestrowanie dla zdarzeń, zwracając tablicę APO_NOTIFICATION_DESCRIPTORs.
HRESULT CSwapAPOMFX::GetApoNotificationRegistrationInfo(_Out_writes_(*count) APO_NOTIFICATION_DESCRIPTOR **apoNotifications, _Out_ DWORD *count)
{
*apoNotifications = nullptr;
*count = 0;
RETURN_HR_IF_NULL(E_FAIL, m_device);
// Let the OS know what notifications we are interested in by returning an array of
// APO_NOTIFICATION_DESCRIPTORs.
constexpr DWORD numDescriptors = 1;
wil::unique_cotaskmem_ptr<APO_NOTIFICATION_DESCRIPTOR[]> apoNotificationDescriptors;
apoNotificationDescriptors.reset(static_cast<APO_NOTIFICATION_DESCRIPTOR*>(
CoTaskMemAlloc(sizeof(APO_NOTIFICATION_DESCRIPTOR) * numDescriptors)));
RETURN_IF_NULL_ALLOC(apoNotificationDescriptors);
// Our APO wants to get notified when an endpoint property changes on the audio endpoint.
apoNotificationDescriptors[0].type = APO_NOTIFICATION_TYPE_ENDPOINT_PROPERTY_CHANGE;
(void)m_device.query_to(&apoNotificationDescriptors[0].audioEndpointPropertyChange.device);
*apoNotifications = apoNotificationDescriptors.release();
*count = numDescriptors;
return S_OK;
}
Poniższy kod pochodzi z przykładu SwapAPO MFX HandleNotifications — swapapomfx.cpp i pokazuje, jak obsługiwać powiadomienia.
void CSwapAPOMFX::HandleNotification(APO_NOTIFICATION *apoNotification)
{
if (apoNotification->type == APO_NOTIFICATION_TYPE_ENDPOINT_PROPERTY_CHANGE)
{
// If either the master disable or our APO's enable properties changed...
if (PK_EQUAL(apoNotification->audioEndpointPropertyChange.propertyKey, PKEY_Endpoint_Enable_Channel_Swap_MFX) ||
PK_EQUAL(apoNotification->audioEndpointPropertyChange.propertyKey, PKEY_AudioEndpoint_Disable_SysFx))
{
struct KeyControl
{
PROPERTYKEY key;
LONG* value;
};
KeyControl controls[] = {
{PKEY_Endpoint_Enable_Channel_Swap_MFX, &m_fEnableSwapMFX},
};
m_apoLoggingService->ApoLog(APO_LOG_LEVEL_INFO, L"HandleNotification - pkey: " GUID_FORMAT_STRING L" %d", GUID_FORMAT_ARGS(apoNotification->audioEndpointPropertyChange.propertyKey.fmtid), apoNotification->audioEndpointPropertyChange.propertyKey.pid);
for (int i = 0; i < ARRAYSIZE(controls); i++)
{
LONG fNewValue = true;
// Get the state of whether channel swap MFX is enabled or not
fNewValue = GetCurrentEffectsSetting(m_userStore.get(), controls[i].key, m_AudioProcessingMode);
SetAudioSystemEffectState(m_effectInfos[i].id, fNewValue ? AUDIO_SYSTEMEFFECT_STATE_ON : AUDIO_SYSTEMEFFECT_STATE_OFF);
}
}
}
}
Struktura rejestrowania
Struktura rejestrowania zapewnia deweloperom obiektu APO dodatkowe środki zbierania danych w celu ulepszenia programowania i debugowania. Ta struktura unifikuje różne metody rejestrowania używane przez różnych dostawców i wiąże je z dostawcami logowania śladów audio w celu stworzenia bardziej znaczącego logowania. Nowa struktura udostępnia interfejs API rejestrowania, pozostawiając pozostałą część pracy, którą należy wykonać przez system operacyjny.
Dostawca jest zdefiniowany jako:
IMPLEMENT_TRACELOGGING_CLASS(ApoTelemetryProvider, "Microsoft.Windows.Audio.ApoTrace",
// {8b4a0b51-5dcf-5a9c-2817-95d0ec876a87}
(0x8b4a0b51, 0x5dcf, 0x5a9c, 0x28, 0x17, 0x95, 0xd0, 0xec, 0x87, 0x6a, 0x87));
Każdy obiekt APO ma własny identyfikator aktywności. Ponieważ to korzysta z istniejącego mechanizmu śledzenia logów, istniejące narzędzia konsoli mogą służyć do filtrowania tych zdarzeń i wyświetlania ich w czasie rzeczywistym. Istniejące narzędzia, takie jak tracelog i tracefmt, można użyć zgodnie z opisem w temacie Narzędzia do śledzenia oprogramowania — sterowniki systemu Windows. Aby uzyskać więcej informacji na temat sesji śledzenia, zobacz Tworzenie sesji śledzenia za pomocą kontrolnego GUID-u.
Zdarzenia rejestrowania śledzenia nie są oznaczone jako dane telemetryczne i nie będą wyświetlane jako dostawca danych telemetrycznych w narzędziach, takich jak xperf.
Implementacja — struktura rejestrowania
Framework rejestrowania jest oparty na mechanizmach rejestrowania udostępnianych przez śledzenie danych ETW. Aby uzyskać więcej informacji na temat funkcji ETW, zobacz Śledzenie zdarzeń. Nie jest to przeznaczone do rejestrowania danych audio, ale do rejestrowania zdarzeń, które są zwykle rejestrowane w środowisku produkcyjnym. API rejestrowania nie powinny być używane z wątku przesyłania strumieniowego w czasie rzeczywistym, ponieważ mogą spowodować, że wątek obsługi zostanie wstępnie zastąpiony przez harmonogram procesora systemu operacyjnego. Rejestrowanie powinno być używane głównie w przypadku zdarzeń, które ułatwiają debugowanie problemów, które często znajdują się w polu.
Definicja interfejsu API — system logowania
Platforma rejestrowania wprowadza interfejs IAudioProcessingObjectLoggingService , który udostępnia nową usługę rejestrowania dla obiektów API.
Aby uzyskać więcej informacji, zobacz IAudioProcessingObjectLoggingService.
Przykładowy kod — struktura rejestrowania
W przykładzie pokazano użycie metody IAudioProcessingObjectLoggingService::ApoLog oraz sposobu uzyskiwania tego wskaźnika interfejsu w obiekcie IAudioProcessingObject::Initialize.
Przykład rejestrowania AecApoMfx.
class SampleApo : public winrt::implements<SampleApo, IAudioProcessingObject,
IAudioSystemEffects, IAudioSystemEffects2, IAudioSystemEffects3>
{
private:
wil::com_ptr_nothrow<IAudioProcessingObjectLoggingService> m_apoLoggingService;
public:
// IAudioProcessingObject
STDMETHOD(Initialize)(UINT32 cbDataSize, BYTE* pbyData);
// Implementation of IAudioProcessingObject, IAudioSystemEffects2 andIAudioSystemEffects3 has been omitted for brevity.
};
// Implementation of IAudioProcessingObject::Initialize
STDMETHODIMP SampleApo::Initialize(UINT32 cbDataSize, BYTE* pbyData)
{
if (cbDataSize == sizeof(APOInitSystemEffects3))
{
APOInitSystemEffects3* apoInitSystemEffects3 = reinterpret_cast<APOInitSystemEffects3*>(pbyData);
// Try to get the logging service, but ignore errors as failure to do logging it is not fatal.
(void)apoInitSystemEffects3->pServiceProvider->QueryService(SID_AudioProcessingObjectLoggingService,
__uuidof(IAudioProcessingObjectLoggingService), IID_PPV_ARGS(&m_apoLoggingService));
}
// Do other APO initialization work
if (m_apoLoggingService != nullptr)
{
m_apoLoggingService->ApoLog(APO_LOG_LEVEL_INFO, L"APO Initialization completed");
}
return S_OK;
}
Struktura wątków
Struktura wątkowość umożliwiająca wielowątkowanie efektów przy użyciu kolejek roboczych z odpowiedniego zadania usługi Harmonogramu klas multimedialnych (MMCSS) za pomocą prostego interfejsu API. Tworzenie kolejek pracy szeregowych w czasie rzeczywistym i ich skojarzenie z głównym wątkiem pompy są obsługiwane przez system operacyjny. Ta struktura umożliwia APOs ustawianie w kolejce zadań krótkotrwałego działania. Synchronizacja między zadaniami nadal jest odpowiedzialnością obiektu APO. Aby uzyskać więcej informacji na temat wątkowania MMCSS, zobacz Usługa harmonogramu klas multimedialnych i interfejs API kolejki pracyReal-Time Work Queue API.
Definicje interfejsu API — Struktura wątków
Struktura wątkowości wprowadza interfejs IAudioProcessingObjectQueueService , który zapewnia dostęp do kolejki pracy w czasie rzeczywistym dla obiektów APO.
Aby uzyskać więcej informacji, znajdź dodatkową zawartość na następujących stronach:
Przykładowy kod — struktura wątków
W tym przykładzie pokazano użycie metody IAudioProcessingObjectRTQueueService::GetRealTimeWorkQueue oraz sposób uzyskiwania wskaźnika interfejsu IAudioProcessingObjectRTQueueService w obiekcie IAudioProcessingObject::Initialize.
#include <rtworkq.h>
class SampleApo3 :
public winrt::implements<SampleApo3, IAudioProcessingObject, IAudioProcessingObjectConfiguration,
IAudioSystemEffects, IAudioSystemEffects2, IAudioSystemEffects3>
{
private:
DWORD m_queueId = 0;
wil::com_ptr_nothrow<SampleApo3AsyncCallback> m_asyncCallback;
public:
// IAudioProcessingObject
STDMETHOD(Initialize)(UINT32 cbDataSize, BYTE* pbyData);
// IAudioProcessingObjectConfiguration
STDMETHOD(LockForProcess)(
_In_ UINT32 u32NumInputConnections,
_In_reads_(u32NumInputConnections) APO_CONNECTION_DESCRIPTOR** ppInputConnections,
_In_ UINT32 u32NumOutputConnections,
_In_reads_(u32NumOutputConnections) APO_CONNECTION_DESCRIPTOR** ppOutputConnections);
// Non-interface methods called by the SampleApo3AsyncCallback helper class.
HRESULT DoWorkOnRealTimeThread()
{
// Do the actual work here
return S_OK;
}
void HandleWorkItemCompleted(_In_ IRtwqAsyncResult* asyncResult);
// Implementation of IAudioProcessingObject, IAudioSystemEffects2, IAudioSystemEffects3 and IAudioProcessingObjectConfiguration is omitted
// for brevity.
};
// Implementation of IAudioProcessingObject::Initialize
STDMETHODIMP SampleApo3::Initialize(UINT32 cbDataSize, BYTE* pbyData)
{
if (cbDataSize == sizeof(APOInitSystemEffects3))
{
APOInitSystemEffects3* apoInitSystemEffects3 = reinterpret_cast<APOInitSystemEffects3*>(pbyData);
wil::com_ptr_nothrow<IAudioProcessingObjectRTQueueService> apoRtQueueService;
RETURN_IF_FAILED(apoInitSystemEffects3->pServiceProvider->QueryService(
SID_AudioProcessingObjectRTQueue, IID_PPV_ARGS(&apoRtQueueService)));
// Call the GetRealTimeWorkQueue to get the ID of a work queue that can be used for scheduling tasks
// that need to run at a real-time priority. The work queue ID is used with the Rtwq APIs.
RETURN_IF_FAILED(apoRtQueueService->GetRealTimeWorkQueue(&m_queueId));
}
// Do other initialization here
return S_OK;
}
STDMETHODIMP SampleApo3::LockForProcess(
_In_ UINT32 u32NumInputConnections,
_In_reads_(u32NumInputConnections) APO_CONNECTION_DESCRIPTOR** ppInputConnections,
_In_ UINT32 u32NumOutputConnections,
_In_reads_(u32NumOutputConnections) APO_CONNECTION_DESCRIPTOR** ppOutputConnections)
{
// Implementation details of LockForProcess omitted for brevity
m_asyncCallback = winrt::make<SampleApo3AsyncCallback>(m_queueId).get();
RETURN_IF_NULL_ALLOC(m_asyncCallback);
wil::com_ptr_nothrow<IRtwqAsyncResult> asyncResult;
RETURN_IF_FAILED(RtwqCreateAsyncResult(this, m_asyncCallback.get(), nullptr, &asyncResult));
RETURN_IF_FAILED(RtwqPutWorkItem(m_queueId, 0, asyncResult.get()));
return S_OK;
}
void SampleApo3::HandleWorkItemCompleted(_In_ IRtwqAsyncResult* asyncResult)
{
// check the status of the result
if (FAILED(asyncResult->GetStatus()))
{
// Handle failure
}
// Here the app could call RtwqPutWorkItem again with m_queueId if it has more work that needs to
// execute on a real-time thread.
}
class SampleApo3AsyncCallback :
public winrt::implements<SampleApo3AsyncCallback, IRtwqAsyncCallback>
{
private:
DWORD m_queueId;
public:
SampleApo3AsyncCallback(DWORD queueId) : m_queueId(queueId) {}
// IRtwqAsyncCallback
STDMETHOD(GetParameters)(_Out_ DWORD* pdwFlags, _Out_ DWORD* pdwQueue)
{
*pdwFlags = 0;
*pdwQueue = m_queueId;
return S_OK;
}
STDMETHOD(Invoke)(_In_ IRtwqAsyncResult* asyncResult);
};
STDMETHODIMP SampleApo3AsyncCallback::Invoke(_In_ IRtwqAsyncResult* asyncResult)
{
// We are now executing on the real-time thread. Invoke the APO and let it execute the work.
wil::com_ptr_nothrow<IUnknown> objectUnknown;
RETURN_IF_FAILED(asyncResult->GetObject(objectUnknown.put_unknown()));
wil::com_ptr_nothrow<SampleApo3> sampleApo3 = static_cast<SampleApo3*>(objectUnknown.get());
HRESULT hr = sampleApo3->DoWorkOnRealTimeThread();
RETURN_IF_FAILED(asyncResult->SetStatus(hr));
sampleApo3->HandleWorkItemCompleted(asyncResult);
return S_OK;
}
Aby uzyskać więcej przykładów sposobu korzystania z tego interfejsu, zobacz następujący przykładowy kod:
- SwapAPO SwapMFXApoAsyncCallback, definicja klasy — przykład
- Funkcja wywołania SwapAPO — przykład
- SwapAPO Create Async Callback — przykład
Odkrywanie i zarządzanie efektami dźwiękowymi
Struktura odnajdywania umożliwia systemowi operacyjnemu kontrolowanie wpływu dźwięku na ich strumień. Te interfejsy API zapewniają obsługę scenariuszy, w których użytkownik aplikacji musi kontrolować określony wpływ na strumienie (np. pomijanie głębokiego szumu). Aby to osiągnąć, ta struktura dodaje następujące elementy:
- Nowy interfejs API do wykonywania zapytań z obiektu APO w celu określenia, czy można włączyć lub wyłączyć efekt dźwiękowy.
- Nowy interfejs API umożliwiający ustawienie stanu efektu dźwięku na włączony/wyłączony.
- Powiadomienie o zmianie listy efektów dźwiękowych lub dostępności zasobów w celu włączenia/wyłączenia efektu dźwiękowego.
Implementacja — odnajdywanie efektów dźwiękowych
Obiekt APO musi zaimplementować interfejs IAudioSystemEffects3, jeśli zamierza udostępnić efekty, które mogą być dynamicznie włączane i wyłączane. APO uwidacznia efekty dźwiękowe poprzez funkcję IAudioSystemEffects3::GetControllableSystemEffectsList oraz włącza i wyłącza te efekty dzięki funkcji IAudioSystemEffects3::SetAudioSystemEffectState.
Przykładowy kod — odnajdywanie efektów dźwięku
Przykładowy kod rozwiązania Audio Effect Discovery można znaleźć w przykładzie SwapAPOSFX — swapaposfx.cpp.
Poniższy przykładowy kod ilustruje sposób pobierania listy konfigurowalnych efektów. Przykład GetControllableSystemEffectsList — swapaposfx.cpp
HRESULT CSwapAPOSFX::GetControllableSystemEffectsList(_Outptr_result_buffer_maybenull_(*numEffects) AUDIO_SYSTEMEFFECT** effects, _Out_ UINT* numEffects, _In_opt_ HANDLE event)
{
RETURN_HR_IF_NULL(E_POINTER, effects);
RETURN_HR_IF_NULL(E_POINTER, numEffects);
*effects = nullptr;
*numEffects = 0;
// Always close existing effects change event handle
if (m_hEffectsChangedEvent != NULL)
{
CloseHandle(m_hEffectsChangedEvent);
m_hEffectsChangedEvent = NULL;
}
// If an event handle was specified, save it here (duplicated to control lifetime)
if (event != NULL)
{
if (!DuplicateHandle(GetCurrentProcess(), event, GetCurrentProcess(), &m_hEffectsChangedEvent, EVENT_MODIFY_STATE, FALSE, 0))
{
RETURN_IF_FAILED(HRESULT_FROM_WIN32(GetLastError()));
}
}
if (!IsEqualGUID(m_AudioProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW))
{
wil::unique_cotaskmem_array_ptr<AUDIO_SYSTEMEFFECT> audioEffects(
static_cast<AUDIO_SYSTEMEFFECT*>(CoTaskMemAlloc(NUM_OF_EFFECTS * sizeof(AUDIO_SYSTEMEFFECT))), NUM_OF_EFFECTS);
RETURN_IF_NULL_ALLOC(audioEffects.get());
for (UINT i = 0; i < NUM_OF_EFFECTS; i++)
{
audioEffects[i].id = m_effectInfos[i].id;
audioEffects[i].state = m_effectInfos[i].state;
audioEffects[i].canSetState = m_effectInfos[i].canSetState;
}
*numEffects = (UINT)audioEffects.size();
*effects = audioEffects.release();
}
return S_OK;
}
Poniższy przykładowy kod ilustruje sposób włączania i wyłączania efektów. SetAudioSystemEffectState sample — swapaposfx.cpp
HRESULT CSwapAPOSFX::SetAudioSystemEffectState(GUID effectId, AUDIO_SYSTEMEFFECT_STATE state)
{
for (auto effectInfo : m_effectInfos)
{
if (effectId == effectInfo.id)
{
AUDIO_SYSTEMEFFECT_STATE oldState = effectInfo.state;
effectInfo.state = state;
// Synchronize access to the effects list and effects changed event
m_EffectsLock.Enter();
// If anything changed and a change event handle exists
if (oldState != effectInfo.state)
{
SetEvent(m_hEffectsChangedEvent);
m_apoLoggingService->ApoLog(APO_LOG_LEVEL_INFO, L"SetAudioSystemEffectState - effect: " GUID_FORMAT_STRING L", state: %i", effectInfo.id, effectInfo.state);
}
m_EffectsLock.Leave();
return S_OK;
}
}
return E_NOTFOUND;
}
Ponowne wykorzystanie WM SFX i MFX APO w wersji 22H2 systemu Windows 11
Począwszy od systemu Windows 11, wersja 22H2, pliki konfiguracji INF, które ponownie używa skrzynki odbiorczej WM SFX i MFX APOs, mogą teraz ponownie używać capX SFX i MFX APOs. W tej sekcji opisano trzy sposoby, aby to zrobić.
Istnieją trzy punkty wstawiania dla APO: renderowanie przed miksowaniem, renderowanie po miksowaniu i przechwytywanie. Silnik audio każdego urządzenia logicznego obsługuje jedno wystąpienie obiektu APO przed-miksowania na strumień (renderowanie SFX) i jedno wystąpienie obiektu APO po-miksowania (MFX). Silnik audio obsługuje również jedną instancję przechwytywania APO (capture SFX), która jest wstawiana do każdego strumienia przechwytywania. Aby uzyskać więcej informacji na temat ponownego użycia lub zawijania wbudowanych APOs, zobacz Łączenie niestandardowych i Windows APOs.
Interfejsy API CAPX SFX i MFX można użyć ponownie na jeden z następujących trzech sposobów.
Korzystanie z sekcji INF DDInstall
Użyj mssysfx.CopyFilesAndRegisterCapX z pliku wdmaudio.inf, dodając następujące wpisy.
Include=wdmaudio.inf
Needs=mssysfx.CopyFilesAndRegisterCapX
Używanie pliku INF dla rozszerzenia funkcji
Plik wdmaudioapo.inf to rozszerzenie klasy AudioProcessingObject inf. Zawiera rejestrację obiektów APO SFX i MFX specyficzną dla urządzenia.
Bezpośrednie odwoływanie się do WM SFX i MFX APO w celu uzyskania efektów strumienia i trybu
Aby bezpośrednio odwoływać się do tych obiektów API dla efektów strumienia i trybu, użyj następujących wartości identyfikatora GUID.
- Użyj
{C9453E73-8C5C-4463-9984-AF8BAB2F5447}jako WM SFX APO - Użyj
{13AB3EBD-137E-4903-9D89-60BE8277FD17}jako obiektu APO MFX WM.
Systemy SFX (Stream) i MFX (tryb) w systemie Windows 8.1 były określane jako LFX (lokalny) oraz GFX (globalny). Te wpisy rejestru nadal używają poprzednich nazw.
Rejestracja specyficzna dla urządzenia używa HKR zamiast HKCR.
Plik INF musi mieć dodane następujące wpisy.
HKR,"FX\\0\\%WMALFXGFXAPO_Context%",%PKEY_FX_Association%,,%KSNODETYPE_ANY%
HKR,"FX\\0\\%WMALFXGFXAPO_Context%\\User",,,
WMALFXGFXAPO_Context = "{B13412EE-07AF-4C57-B08B-E327F8DB085B}"
Te wpisy pliku INF spowodują utworzenie magazynu właściwości, który będzie używany przez interfejsy API systemu Windows 11 dla nowych APOs.
PKEY_FX_Association w pliku INF, przykład.
HKR,"FX\\0",%PKEY_FX_Association%,,%KSNODETYPE_ANY% należy zastąpić HKR,"FX\\0\\%WMALFXGFXAPO_Context%",%PKEY_FX_Association%,,%KSNODETYPE_ANY%.