mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-11-12 14:57:20 +00:00
[idd] audio: add the mediated PCM data plane
Expose cancel-safe direct-I/O queues for reading rendered frames and writing captured frames through the private broker endpoint. Separate render and capture storage, publish format and state generations, and preserve local loopback while no broker is bound.
This commit is contained in:
@@ -112,33 +112,140 @@ NTSTATUS InstallWaveSubdevice(
|
||||
}
|
||||
|
||||
CAudioAdapter::CAudioAdapter() :
|
||||
m_refCount(1)
|
||||
m_refCount(1),
|
||||
m_brokerActive(0),
|
||||
m_renderDiscontinuity(0),
|
||||
m_renderState{},
|
||||
m_captureState{}
|
||||
{
|
||||
KeInitializeSpinLock(&m_transportLock);
|
||||
KeInitializeSpinLock(&m_streamStateLock);
|
||||
m_renderState.state = KSSTATE_STOP;
|
||||
m_captureState.state = KSSTATE_STOP;
|
||||
}
|
||||
|
||||
CAudioAdapter::~CAudioAdapter()
|
||||
{
|
||||
m_ring.Destroy();
|
||||
m_captureRing.Destroy();
|
||||
m_renderRing.Destroy();
|
||||
}
|
||||
|
||||
NTSTATUS CAudioAdapter::Initialize()
|
||||
{
|
||||
NTSTATUS status = m_clock.Initialize();
|
||||
if (NT_SUCCESS(status))
|
||||
status = m_ring.Init();
|
||||
status = m_renderRing.Init();
|
||||
if (NT_SUCCESS(status))
|
||||
status = m_captureRing.Init();
|
||||
return status;
|
||||
}
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
NTSTATUS CAudioAdapter::ConfigureRender(
|
||||
NTSTATUS CAudioAdapter::ConfigureStream(
|
||||
_In_ EndpointKind kind,
|
||||
_In_ const FormatFingerprint &format)
|
||||
{
|
||||
return m_ring.Configure(format);
|
||||
CAudioRingBuffer &ring = kind == EndpointKind::Render ?
|
||||
m_renderRing : m_captureRing;
|
||||
KIRQL transportIrql;
|
||||
KeAcquireSpinLock(&m_transportLock, &transportIrql);
|
||||
const NTSTATUS status = ring.Configure(format);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KeReleaseSpinLock(&m_transportLock, transportIrql);
|
||||
return status;
|
||||
}
|
||||
|
||||
KIRQL oldIrql;
|
||||
KeAcquireSpinLock(&m_streamStateLock, &oldIrql);
|
||||
AudioStreamSnapshot &stream = kind == EndpointKind::Render ?
|
||||
m_renderState : m_captureState;
|
||||
stream.format = format;
|
||||
stream.formatValid = true;
|
||||
++stream.generation;
|
||||
KeReleaseSpinLock(&m_streamStateLock, oldIrql);
|
||||
KeReleaseSpinLock(&m_transportLock, transportIrql);
|
||||
|
||||
BrokerDeviceNotifyStreamChanged(this, kind);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
void CAudioAdapter::SetStreamState(
|
||||
_In_ EndpointKind kind,
|
||||
_In_ KSSTATE state)
|
||||
{
|
||||
bool changed;
|
||||
bool reset;
|
||||
|
||||
KIRQL transportIrql;
|
||||
KeAcquireSpinLock(&m_transportLock, &transportIrql);
|
||||
KIRQL oldIrql;
|
||||
KeAcquireSpinLock(&m_streamStateLock, &oldIrql);
|
||||
AudioStreamSnapshot &stream = kind == EndpointKind::Render ?
|
||||
m_renderState : m_captureState;
|
||||
changed = stream.state != state;
|
||||
reset = changed && state == KSSTATE_STOP;
|
||||
stream.state = state;
|
||||
if (reset)
|
||||
++stream.generation;
|
||||
KeReleaseSpinLock(&m_streamStateLock, oldIrql);
|
||||
|
||||
if (reset)
|
||||
{
|
||||
if (kind == EndpointKind::Render)
|
||||
m_renderRing.Reset();
|
||||
else
|
||||
m_captureRing.Reset();
|
||||
}
|
||||
KeReleaseSpinLock(&m_transportLock, transportIrql);
|
||||
if (changed)
|
||||
BrokerDeviceNotifyStreamChanged(this, kind);
|
||||
}
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
void CAudioAdapter::GetStreamSnapshot(
|
||||
_In_ EndpointKind kind,
|
||||
_Out_ AudioStreamSnapshot *snapshot)
|
||||
{
|
||||
if (!snapshot)
|
||||
return;
|
||||
|
||||
KIRQL oldIrql;
|
||||
KeAcquireSpinLock(&m_streamStateLock, &oldIrql);
|
||||
*snapshot = kind == EndpointKind::Render ?
|
||||
m_renderState : m_captureState;
|
||||
KeReleaseSpinLock(&m_streamStateLock, oldIrql);
|
||||
}
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
void CAudioAdapter::SetBrokerActive(_In_ bool active)
|
||||
{
|
||||
KIRQL oldIrql;
|
||||
KeAcquireSpinLock(&m_transportLock, &oldIrql);
|
||||
const LONG value = active ? 1 : 0;
|
||||
if (InterlockedExchange(&m_brokerActive, value) == value)
|
||||
{
|
||||
KeReleaseSpinLock(&m_transportLock, oldIrql);
|
||||
return;
|
||||
}
|
||||
|
||||
m_captureRing.Reset();
|
||||
m_renderRing.Reset();
|
||||
InterlockedExchange(&m_renderDiscontinuity, active ? 1 : 0);
|
||||
KeReleaseSpinLock(&m_transportLock, oldIrql);
|
||||
}
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
void CAudioAdapter::ResetCapture()
|
||||
{
|
||||
m_ring.Reset();
|
||||
KIRQL oldIrql;
|
||||
KeAcquireSpinLock(&m_transportLock, &oldIrql);
|
||||
if (InterlockedCompareExchange(&m_brokerActive, 0, 0))
|
||||
m_captureRing.Reset();
|
||||
else
|
||||
m_renderRing.Reset();
|
||||
KeReleaseSpinLock(&m_transportLock, oldIrql);
|
||||
}
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
@@ -146,7 +253,14 @@ void CAudioAdapter::WriteRenderFrames(
|
||||
_In_reads_bytes_(bytes) const BYTE *data,
|
||||
_In_ ULONG bytes)
|
||||
{
|
||||
m_ring.Push(data, bytes);
|
||||
ULONG droppedBytes = 0;
|
||||
KIRQL oldIrql;
|
||||
KeAcquireSpinLock(&m_transportLock, &oldIrql);
|
||||
m_renderRing.Push(data, bytes, &droppedBytes);
|
||||
if (droppedBytes)
|
||||
InterlockedExchange(&m_renderDiscontinuity, 1);
|
||||
KeReleaseSpinLock(&m_transportLock, oldIrql);
|
||||
BrokerDeviceNotifyRenderAvailable(this, bytes, droppedBytes);
|
||||
}
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
@@ -155,7 +269,16 @@ void CAudioAdapter::ReadCaptureFrames(
|
||||
_Out_writes_bytes_(bytes) BYTE *data,
|
||||
_In_ ULONG bytes)
|
||||
{
|
||||
m_ring.Pop(format, data, bytes);
|
||||
KIRQL oldIrql;
|
||||
KeAcquireSpinLock(&m_transportLock, &oldIrql);
|
||||
const bool brokerActive =
|
||||
InterlockedCompareExchange(&m_brokerActive, 0, 0) != 0;
|
||||
CAudioRingBuffer &ring = brokerActive ?
|
||||
m_captureRing : m_renderRing;
|
||||
const ULONG copied = ring.Pop(format, data, bytes);
|
||||
KeReleaseSpinLock(&m_transportLock, oldIrql);
|
||||
if (brokerActive && copied < bytes)
|
||||
BrokerDeviceRecordCaptureUnderrun(this, bytes - copied);
|
||||
}
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
@@ -163,7 +286,107 @@ void CAudioAdapter::DiscardCaptureFrames(
|
||||
_In_ const FormatFingerprint &format,
|
||||
_In_ ULONGLONG bytes)
|
||||
{
|
||||
m_ring.Discard(format, bytes);
|
||||
KIRQL oldIrql;
|
||||
KeAcquireSpinLock(&m_transportLock, &oldIrql);
|
||||
const bool brokerActive =
|
||||
InterlockedCompareExchange(&m_brokerActive, 0, 0) != 0;
|
||||
CAudioRingBuffer &ring = brokerActive ?
|
||||
m_captureRing : m_renderRing;
|
||||
const ULONG discarded = ring.Discard(format, bytes);
|
||||
KeReleaseSpinLock(&m_transportLock, oldIrql);
|
||||
if (brokerActive && bytes > discarded)
|
||||
BrokerDeviceRecordCaptureUnderrun(this, bytes - discarded);
|
||||
}
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
NTSTATUS CAudioAdapter::ReadRenderFrames(
|
||||
_In_ const FormatFingerprint &format,
|
||||
_In_ ULONGLONG streamGeneration,
|
||||
_Out_writes_bytes_(capacity) BYTE *data,
|
||||
_In_ ULONG capacity,
|
||||
_Out_ PULONG bytesRead,
|
||||
_Out_ bool *discontinuity)
|
||||
{
|
||||
if (!data || !bytesRead || !discontinuity || !format.blockAlign)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
*bytesRead = 0;
|
||||
*discontinuity = false;
|
||||
KIRQL transportIrql;
|
||||
KeAcquireSpinLock(&m_transportLock, &transportIrql);
|
||||
|
||||
KIRQL stateIrql;
|
||||
KeAcquireSpinLock(&m_streamStateLock, &stateIrql);
|
||||
const bool active =
|
||||
InterlockedCompareExchange(&m_brokerActive, 0, 0) != 0 &&
|
||||
m_renderState.state == KSSTATE_RUN &&
|
||||
m_renderState.formatValid &&
|
||||
m_renderState.generation == streamGeneration &&
|
||||
EqualFormatFingerprint(m_renderState.format, format);
|
||||
KeReleaseSpinLock(&m_streamStateLock, stateIrql);
|
||||
if (!active)
|
||||
{
|
||||
KeReleaseSpinLock(&m_transportLock, transportIrql);
|
||||
return STATUS_DEVICE_NOT_READY;
|
||||
}
|
||||
|
||||
ULONG bytes = m_renderRing.Available();
|
||||
if (bytes > capacity)
|
||||
bytes = capacity;
|
||||
bytes -= bytes % format.blockAlign;
|
||||
if (!bytes)
|
||||
{
|
||||
KeReleaseSpinLock(&m_transportLock, transportIrql);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
const ULONG copied = m_renderRing.Pop(format, data, bytes);
|
||||
if (copied)
|
||||
{
|
||||
*discontinuity = InterlockedExchange(
|
||||
&m_renderDiscontinuity, 0) != 0;
|
||||
}
|
||||
*bytesRead = copied;
|
||||
KeReleaseSpinLock(&m_transportLock, transportIrql);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
NTSTATUS CAudioAdapter::WriteCaptureFrames(
|
||||
_In_ const FormatFingerprint &format,
|
||||
_In_ ULONGLONG streamGeneration,
|
||||
_In_ bool discontinuity,
|
||||
_In_reads_bytes_(bytes) const BYTE *data,
|
||||
_In_ ULONG bytes,
|
||||
_Out_ PULONG droppedBytes)
|
||||
{
|
||||
if (!data || !droppedBytes)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
*droppedBytes = 0;
|
||||
KIRQL transportIrql;
|
||||
KeAcquireSpinLock(&m_transportLock, &transportIrql);
|
||||
|
||||
KIRQL stateIrql;
|
||||
KeAcquireSpinLock(&m_streamStateLock, &stateIrql);
|
||||
const bool active =
|
||||
InterlockedCompareExchange(&m_brokerActive, 0, 0) != 0 &&
|
||||
m_captureState.state == KSSTATE_RUN &&
|
||||
m_captureState.formatValid &&
|
||||
m_captureState.generation == streamGeneration &&
|
||||
EqualFormatFingerprint(m_captureState.format, format);
|
||||
KeReleaseSpinLock(&m_streamStateLock, stateIrql);
|
||||
if (!active)
|
||||
{
|
||||
KeReleaseSpinLock(&m_transportLock, transportIrql);
|
||||
return STATUS_DEVICE_NOT_READY;
|
||||
}
|
||||
|
||||
if (discontinuity)
|
||||
m_captureRing.Reset();
|
||||
const ULONG written = m_captureRing.Push(data, bytes, droppedBytes);
|
||||
KeReleaseSpinLock(&m_transportLock, transportIrql);
|
||||
return written == bytes ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
|
||||
Reference in New Issue
Block a user