summaryrefslogtreecommitdiff
path: root/audio/Acx/Samples/Common/CircuitHelper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'audio/Acx/Samples/Common/CircuitHelper.cpp')
-rw-r--r--audio/Acx/Samples/Common/CircuitHelper.cpp620
1 files changed, 620 insertions, 0 deletions
diff --git a/audio/Acx/Samples/Common/CircuitHelper.cpp b/audio/Acx/Samples/Common/CircuitHelper.cpp
new file mode 100644
index 00000000..bfff74b2
--- /dev/null
+++ b/audio/Acx/Samples/Common/CircuitHelper.cpp
@@ -0,0 +1,620 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+ THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+ KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+ PURPOSE.
+
+Module Name:
+
+ CircuitHelper.cpp
+
+Abstract:
+
+ This module contains helper functions for circuits.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "private.h"
+#include "public.h"
+#include "CircuitHelper.h"
+
+#ifndef __INTELLISENSE__
+#include "CircuitHelper.tmh"
+#endif
+
+const ULONG _DSP_STREAM_PROPERTY_UI4_VALUE = 1;
+
+PAGED_CODE_SEG
+NTSTATUS AllocateFormat(
+ _In_ KSDATAFORMAT_WAVEFORMATEXTENSIBLE WaveFormat,
+ _In_ ACXCIRCUIT Circuit,
+ _In_ WDFDEVICE Device,
+ _Out_ ACXDATAFORMAT* Format
+)
+{
+ PAGED_CODE();
+
+ NTSTATUS status = STATUS_SUCCESS;
+
+ WDF_OBJECT_ATTRIBUTES attributes;
+ WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+
+ ACX_DATAFORMAT_CONFIG formatCfg;
+ ACX_DATAFORMAT_CONFIG_INIT_KS(&formatCfg, &WaveFormat);
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, FORMAT_CONTEXT);
+ attributes.ParentObject = Circuit;
+
+ //
+ // Creates an ACXDATAFORMAT handle for the given wave format.
+ //
+ RETURN_NTSTATUS_IF_FAILED(AcxDataFormatCreate(Device, &attributes, &formatCfg, Format));
+
+ ASSERT((*Format) != NULL);
+ FORMAT_CONTEXT* formatCtx;
+ formatCtx = GetFormatContext(*Format);
+ ASSERT(formatCtx);
+ UNREFERENCED_PARAMETER(formatCtx);
+
+ return status;
+}
+
+PAGED_CODE_SEG
+NTSTATUS CreateStreamBridge(
+ _In_ ACX_STREAM_BRIDGE_CONFIG StreamCfg,
+ _In_ ACXCIRCUIT Circuit,
+ _In_ ACXPIN Pin,
+ _In_ DSP_PIN_CONTEXT* PinCtx,
+ _In_ BOOL Render)
+{
+ PAGED_CODE();
+
+ NTSTATUS status = STATUS_SUCCESS;
+
+ WDF_OBJECT_ATTRIBUTES attributes;
+ WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+
+ attributes.ParentObject = Pin;
+
+ ACX_OBJECTBAG_CONFIG objBagCfg;
+ ACXOBJECTBAG objBag = NULL;
+ ACX_OBJECTBAG_CONFIG_INIT(&objBagCfg);
+ WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+ attributes.ParentObject = Circuit;
+ RETURN_NTSTATUS_IF_FAILED(AcxObjectBagCreate(&attributes, &objBagCfg, &objBag));
+
+ DECLARE_CONST_ACXOBJECTBAG_DRIVER_PROPERTY_NAME(msft, TestUI4);
+ RETURN_NTSTATUS_IF_FAILED(AcxObjectBagAddUI4(objBag, &TestUI4, _DSP_STREAM_PROPERTY_UI4_VALUE));
+
+ //
+ // Add a stream BRIDGE.
+ //
+ PCGUID inModes[] = {
+ &AUDIO_SIGNALPROCESSINGMODE_RAW,
+ &AUDIO_SIGNALPROCESSINGMODE_DEFAULT,
+ };
+
+ if (Render)
+ {
+ StreamCfg.InModesCount = SIZEOF_ARRAY(inModes);
+ StreamCfg.InModes = inModes;
+ }
+
+ //
+ // Do not specify InModes for capture - this will prevent the ACX framework from adding created streams to this stream
+ // bridge automatically. We want to add the stream bridges manually since we don't want KWS streams added.
+ //
+ StreamCfg.OutMode = &AUDIO_SIGNALPROCESSINGMODE_RAW;
+ StreamCfg.OutStreamVarArguments = objBag;
+
+ //
+ // Uncomment this line to reverse the change-state sequence notifications.
+ //
+ // streamCfg.Flags |= AcxStreamBridgeInvertChangeStateSequence;
+
+ ACXSTREAMBRIDGE streamBridge = NULL;
+ RETURN_NTSTATUS_IF_FAILED(AcxStreamBridgeCreate(Circuit, &attributes, &StreamCfg, &streamBridge));
+
+ if (!Render)
+ {
+ PinCtx->HostStreamBridge = streamBridge;
+ }
+
+ RETURN_NTSTATUS_IF_FAILED(AcxPinAddStreamBridges(Pin, &streamBridge, 1));
+
+ return status;
+}
+
+PAGED_CODE_SEG
+NTSTATUS ConnectRenderCircuitElements(
+ _In_ ACXAUDIOENGINE AudioEngineElement,
+ _In_ ACXCIRCUIT Circuit
+)
+{
+ PAGED_CODE();
+
+ NTSTATUS status = STATUS_SUCCESS;
+
+ //
+ // Explicitly connect the circuit/elements. Note that driver doesn't
+ // need to perform this step when circuit/elements are connected in the
+ // same order as they were added to the circuit. By default ACX connects
+ // the elements starting from the sink circuit pin and ending with the
+ // source circuit pin for both render and capture devices.
+ //
+ // Circuit layout
+ // -----------------------------------------
+ // | |
+ // | -------------------- |
+ // Host -0->|-----1->| |-0-------->|-3-> Bridge Pin
+ // | | Audio Engine | |
+ // Offload -1->|-----2->| Node |-3--| |
+ // | |------------------| | |
+ // | | |
+ // Loopback <-2-|<------------------------------ | |
+ // | |
+ // | |
+ // |---------------------------------------|
+ //
+
+ ACX_CONNECTION connections[4];
+
+ ACX_CONNECTION_INIT(&connections[0], Circuit, AudioEngineElement);
+ connections[0].FromPin.Id = RenderHostPin;
+ connections[0].ToPin.Id = 1;
+
+ ACX_CONNECTION_INIT(&connections[1], Circuit, AudioEngineElement);
+ connections[1].FromPin.Id = RenderOffloadPin;
+ connections[1].ToPin.Id = 2;
+
+ ACX_CONNECTION_INIT(&connections[2], AudioEngineElement, Circuit);
+ connections[2].ToPin.Id = RenderLoopbackPin;
+ connections[2].FromPin.Id = 3;
+
+ ACX_CONNECTION_INIT(&connections[3], AudioEngineElement, Circuit);
+ connections[3].ToPin.Id = RenderBridgePin;
+ connections[3].FromPin.Id = 0;
+
+ //
+ // Add the connections linking circuit to elements.
+ //
+ RETURN_NTSTATUS_IF_FAILED(AcxCircuitAddConnections(Circuit, connections, SIZEOF_ARRAY(connections)));
+
+ return status;
+
+}
+
+PAGED_CODE_SEG
+NTSTATUS
+Codec_GetModeFromAttributeList(
+ _In_ const PKSMULTIPLE_ITEM Attributes,
+ _In_ ULONG AttributesSize,
+ _Out_ GUID * SignalProcessingMode
+ )
+{
+ NTSTATUS status = STATUS_NO_MORE_ENTRIES;
+ PKSATTRIBUTE attributeHeader = NULL;
+ PAGED_CODE();
+ *SignalProcessingMode = AUDIO_SIGNALPROCESSINGMODE_DEFAULT;
+
+ status = FindKsAttributeById(Attributes,
+ AttributesSize,
+ &KSATTRIBUTEID_AUDIOSIGNALPROCESSING_MODE,
+ sizeof(KSATTRIBUTE_AUDIOSIGNALPROCESSING_MODE),
+ &attributeHeader);
+ if (!NT_SUCCESS(status))
+ {
+ goto exit;
+ }
+
+ ASSERT(attributeHeader->Attribute == KSATTRIBUTEID_AUDIOSIGNALPROCESSING_MODE);
+ ASSERT(attributeHeader->Size == sizeof(KSATTRIBUTE_AUDIOSIGNALPROCESSING_MODE));
+
+ KSATTRIBUTE_AUDIOSIGNALPROCESSING_MODE* signalProcessingModeAttribute;
+ signalProcessingModeAttribute = (KSATTRIBUTE_AUDIOSIGNALPROCESSING_MODE*)attributeHeader;
+ *SignalProcessingMode = signalProcessingModeAttribute->SignalProcessingMode;
+ status = STATUS_SUCCESS;
+exit:
+ return status;
+}
+
+struct AFX_FIND_KSATTRIBUTE_BY_ID
+{
+ const _GUID * Id;
+ ULONG Size;
+ PKSATTRIBUTE Attribute;
+};
+
+PAGED_CODE_SEG
+NTSTATUS
+FindKsAttributeByIdVisitor(
+ _In_ PKSATTRIBUTE AttributeHeader,
+ _In_ PVOID Context,
+ _Out_ BOOLEAN * bContinue
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ AFX_FIND_KSATTRIBUTE_BY_ID * ctx = (AFX_FIND_KSATTRIBUTE_BY_ID *)Context;
+ PAGED_CODE();
+ // Default: continue searching.
+ *bContinue = TRUE;
+
+ if (IsEqualGUIDAligned(AttributeHeader->Attribute, *ctx->Id))
+ {
+ // Validate its size.
+ if (AttributeHeader->Size < ctx->Size)
+ {
+ status = STATUS_INVALID_PARAMETER;
+ goto exit;
+ }
+
+ ctx->Attribute = AttributeHeader;
+ *bContinue = FALSE;
+ }
+exit:
+ return status;
+}
+
+PAGED_CODE_SEG
+NTSTATUS
+FindKsAttributeById(
+ _In_ const PKSMULTIPLE_ITEM Attributes,
+ _In_ ULONG AttributesSize,
+ _In_ const _GUID * AttributeId,
+ _In_ ULONG AttributeSize,
+ _Out_ PKSATTRIBUTE * AttributeHeader
+ )
+{
+ PAGED_CODE();
+ NTSTATUS status;
+ AFX_FIND_KSATTRIBUTE_BY_ID ctx = {0};
+ *AttributeHeader = NULL;
+ ctx.Id = AttributeId;
+ ctx.Size = AttributeSize;
+ status = TraverseKsAttributeList(Attributes, AttributesSize, FindKsAttributeByIdVisitor, &ctx);
+ if (!NT_SUCCESS(status))
+ {
+ goto exit;
+ }
+ if (ctx.Attribute == NULL)
+ {
+ status = STATUS_NO_MORE_ENTRIES;
+ goto exit;
+ }
+ *AttributeHeader = ctx.Attribute;
+exit:
+ return status;
+}
+
+PAGED_CODE_SEG
+NTSTATUS
+TraverseKsAttributeList(
+ _In_ const PKSMULTIPLE_ITEM Attributes,
+ _In_ ULONG AttributesSize,
+ _In_ PFN_KSATTRIBUTES_VISITOR Visitor,
+ _In_ PVOID Context
+ )
+{
+ PAGED_CODE();
+ NTSTATUS status = STATUS_SUCCESS;
+ ULONG cbRemaining = 0;
+ PKSATTRIBUTE attributeHeader = NULL;
+ ASSERT(Visitor);
+ //
+ // Note: multiple-item ptr must have a FILE_QUAD_ALIGNMENT only in relation to the Pin's format size.
+ // I.e., it is not guaranteed that the attributes are FILE_QUAD_ALIGNMENT in memory, unless the caller reallocates
+ // them before calling this function (which is currently not done by anyone in any driver/sample).
+ //
+ //ASSERT((ULONG_PTR)Attributes == (((ULONG_PTR)Attributes + FILE_QUAD_ALIGNMENT) & ~FILE_QUAD_ALIGNMENT));
+ if (AttributesSize < sizeof(KSMULTIPLE_ITEM))
+ {
+ status = STATUS_INVALID_PARAMETER;
+ goto exit;
+ }
+ if (Attributes->Size < sizeof(KSMULTIPLE_ITEM))
+ {
+ status = STATUS_INVALID_PARAMETER;
+ goto exit;
+ }
+ if (AttributesSize < Attributes->Size)
+ {
+ status = STATUS_INVALID_PARAMETER;
+ goto exit;
+ }
+
+ cbRemaining = Attributes->Size;
+ //
+ // Init ptr / size of attributes of list.
+ //
+ cbRemaining -= sizeof(KSMULTIPLE_ITEM);
+ attributeHeader = (PKSATTRIBUTE)(Attributes + 1);
+ for (ULONG i = 0; i < Attributes->Count; i++)
+ {
+ BOOLEAN bContinue = TRUE;
+ size_t cbAttribute = 0;
+
+ if (cbRemaining < sizeof(KSATTRIBUTE))
+ {
+ status = STATUS_INVALID_PARAMETER;
+ goto exit;
+ }
+ if (attributeHeader->Size < sizeof(KSATTRIBUTE))
+ {
+ status = STATUS_INVALID_PARAMETER;
+ goto exit;
+ }
+ if (cbRemaining < attributeHeader->Size)
+ {
+ status = STATUS_INVALID_PARAMETER;
+ goto exit;
+ }
+ //
+ // Invoke the descriptor enumeration routine.
+ //
+ status = Visitor(attributeHeader, Context, &bContinue);
+ if (!NT_SUCCESS(status))
+ {
+ goto exit;
+ }
+
+ if (!bContinue)
+ {
+ break;
+ }
+ //
+ // Adjust pointer and buffer size to next attribute (QWORD aligned)
+ //
+ cbAttribute = CODEC_ALIGN_SIZE_UP_CONSTANT(attributeHeader->Size, FILE_QUAD_ALIGNMENT);
+ if (cbRemaining < cbAttribute)
+ {
+ //
+ // Out of buffer, check if this was the last attribute.
+ //
+ if (i + 1 != Attributes->Count)
+ {
+ //
+ // This was not the last attribute, nevertheless there is no more buffer, error out.
+ //
+ status = STATUS_INVALID_PARAMETER;
+ goto exit;
+ }
+ // This was the final attribute. Exit loop with success status.
+ break;
+ }
+ // Check next attribute.
+ attributeHeader = (PKSATTRIBUTE)(((PBYTE)attributeHeader) + cbAttribute);
+ cbRemaining -= (ULONG)cbAttribute;
+ }
+ // Normalize success code.
+ status = STATUS_SUCCESS;
+exit:
+ return status;
+}
+
+PAGED_CODE_SEG
+NTSTATUS
+EvtJackRetrievePresence(
+ _In_ ACXJACK Jack,
+ _In_ PBOOLEAN IsConnected
+)
+{
+ PAGED_CODE();
+
+ UNREFERENCED_PARAMETER(Jack);
+ UNREFERENCED_PARAMETER(IsConnected);
+
+ NTSTATUS status = STATUS_SUCCESS;
+
+ //
+ // Because this is a sample we always return true (jack is present). A real driver should check
+ // if the device is actually present before returning true.
+ //
+ *IsConnected = true;
+
+ return status;
+}
+
+PAGED_CODE_SEG
+NTSTATUS
+CreateAudioJack(
+ _In_ ULONG ChannelMapping,
+ _In_ ULONG Color,
+ _In_ ACX_JACK_CONNECTION_TYPE ConnectionType,
+ _In_ ACX_JACK_GEO_LOCATION GeoLocation,
+ _In_ ACX_JACK_GEN_LOCATION GenLocation,
+ _In_ ACX_JACK_PORT_CONNECTION PortConnection,
+ _In_ ULONG Flags,
+ _In_ ACXPIN BridgePin
+ )
+{
+ PAGED_CODE();
+
+ NTSTATUS status = STATUS_SUCCESS;
+ ACX_JACK_CONFIG jackCfg;
+ ACXJACK jack;
+ PJACK_CONTEXT jackCtx;
+ ACX_JACK_CALLBACKS jackCallbacks;
+
+ WDF_OBJECT_ATTRIBUTES attributes;
+ WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+
+ ACX_JACK_CONFIG_INIT(&jackCfg);
+ jackCfg.Description.ChannelMapping = ChannelMapping;
+ jackCfg.Description.Color = Color;
+ jackCfg.Description.ConnectionType = ConnectionType;
+ jackCfg.Description.GeoLocation = GeoLocation;
+ jackCfg.Description.GenLocation = GenLocation;
+ jackCfg.Description.PortConnection = PortConnection;
+ jackCfg.Flags = Flags;
+
+ ACX_JACK_CALLBACKS_INIT(&jackCallbacks);
+ jackCallbacks.EvtAcxJackRetrievePresenceState = EvtJackRetrievePresence;
+ jackCfg.Callbacks = &jackCallbacks;
+
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, JACK_CONTEXT);
+ attributes.ParentObject = BridgePin;
+
+ status = AcxJackCreate(BridgePin, &attributes, &jackCfg, &jack);
+ if (!NT_SUCCESS(status))
+ {
+ goto exit;
+ }
+
+ ASSERT(jack != nullptr);
+
+ jackCtx = GetJackContext(jack);
+ ASSERT(jackCtx);
+ jackCtx->Dummy = 0;
+
+ status = AcxPinAddJacks(BridgePin, &jack, 1);
+
+exit:
+ return status;
+}
+
+PAGED_CODE_SEG
+VOID
+CpuResourcesCallbackHelper
+(
+ _In_ WDFOBJECT Object,
+ _In_ WDFREQUEST Request,
+ _In_ ACXELEMENT Element
+)
+{
+ NTSTATUS ntStatus = STATUS_NOT_SUPPORTED;
+ ULONG_PTR outDataCb = 0;
+ ACX_REQUEST_PARAMETERS params;
+ ULONG minSize = sizeof(ULONG);
+
+ PAGED_CODE();
+
+ ACX_REQUEST_PARAMETERS_INIT(&params);
+ AcxRequestGetParameters(Request, &params);
+
+ if ((params.Type != AcxRequestTypeProperty) ||
+ (params.Parameters.Property.ItemType != AcxItemTypeElement))
+ {
+ // Return to acx
+ (VOID) AcxCircuitDispatchAcxRequest((ACXCIRCUIT)Object, Request);
+ Request = NULL;
+ goto exit;
+ }
+
+ if (Element == NULL)
+ {
+ ntStatus = STATUS_NOT_SUPPORTED;
+ goto exit;
+ }
+
+ ULONG elementId = params.Parameters.Property.ItemId;
+ ULONG currentElementId = AcxElementGetId(Element);
+ ULONG valueCb = params.Parameters.Property.ValueCb;
+
+ if (valueCb != 0)
+ {
+ if (params.Parameters.Property.Value == NULL)
+ {
+ ntStatus = STATUS_BUFFER_TOO_SMALL;
+ goto exit;
+ }
+ }
+
+ //
+ // Check to see if the current node is the peakmeter node, if not then return the call to ACX
+ //
+ if (elementId != currentElementId)
+ {
+ (VOID) AcxCircuitDispatchAcxRequest((ACXCIRCUIT)Object, Request);
+ Request = NULL;
+ goto exit;
+ }
+
+ if (params.Parameters.Property.Verb == AcxPropertyVerbGet)
+ {
+
+ if (valueCb == 0)
+ {
+ outDataCb = minSize;
+ ntStatus = STATUS_BUFFER_OVERFLOW;
+ goto exit;
+ }
+ else if (valueCb < minSize)
+ {
+ outDataCb = 0;
+ ntStatus = STATUS_BUFFER_TOO_SMALL;
+ goto exit;
+ }
+ else
+ {
+ *((PULONG)params.Parameters.Property.Value) = KSAUDIO_CPU_RESOURCES_NOT_HOST_CPU;
+ params.Parameters.Property.ValueCb = sizeof(ULONG);
+ outDataCb = params.Parameters.Property.ValueCb;
+ ntStatus = STATUS_SUCCESS;
+ }
+ }
+ else if (params.Parameters.Property.Verb == AcxPropertyVerbBasicSupport)
+ {
+ if ((valueCb != sizeof(ULONG)) && (valueCb != sizeof(KSPROPERTY_DESCRIPTION)))
+ {
+ outDataCb = minSize;
+ ntStatus = STATUS_BUFFER_OVERFLOW;
+ goto exit;
+ }
+
+ if (valueCb >= sizeof(KSPROPERTY_DESCRIPTION))
+ {
+ // if return buffer can hold a KSPROPERTY_DESCRIPTION, return it
+ //
+ PKSPROPERTY_DESCRIPTION PropDesc = (PKSPROPERTY_DESCRIPTION)params.Parameters.Property.Value;
+
+ PropDesc->AccessFlags = KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET;
+ PropDesc->DescriptionSize = sizeof(KSPROPERTY_DESCRIPTION);
+ PropDesc->PropTypeSet.Set = KSPROPTYPESETID_General;
+ PropDesc->PropTypeSet.Id = VT_UI4;
+ PropDesc->PropTypeSet.Flags = 0;
+ PropDesc->MembersListCount = 0;
+ PropDesc->Reserved = 0;
+ outDataCb = sizeof(KSPROPERTY_DESCRIPTION);
+ ntStatus = STATUS_SUCCESS;
+ }
+ else if (valueCb >= sizeof(ULONG))
+ {
+ // if return buffer can hold a ULONG, return the access flags
+ //
+ *((PULONG)params.Parameters.Property.Value) = KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET;
+ outDataCb = minSize;
+ ntStatus = STATUS_SUCCESS;
+ }
+ else if (valueCb > 0)
+ {
+ outDataCb = 0;
+ ntStatus = STATUS_BUFFER_TOO_SMALL;
+ }
+ else
+ {
+ outDataCb = minSize;
+ ntStatus = STATUS_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ {
+ //
+ // Just give it back to ACX. After this call the request is gone.
+ //
+ (VOID) AcxCircuitDispatchAcxRequest((ACXCIRCUIT)Object, Request);
+ Request = NULL;
+ goto exit;
+ }
+
+exit:
+ if (Request != NULL)
+ {
+ WdfRequestCompleteWithInformation(Request, ntStatus, outDataCb);
+ }
+} // EvtAudioCpuResourcesCallback