[idd] audio: support 32-bit and common channel layouts

Advertise signed and floating-point 32-bit formats across mono,
stereo, quad, surround, 5.1, and 7.1 at every supported rate.

Carry sample encoding through the broker and LGMP protocol so integer
and floating-point streams remain distinct.

Expand packet, shared-memory, and ring capacity for eight-channel
32-bit audio.
This commit is contained in:
Geoffrey McRae
2026-09-05 07:39:32 +10:00
parent 5681dcdbe7
commit efc9952e74
13 changed files with 444 additions and 170 deletions

View File

@@ -420,6 +420,20 @@ static bool validFormat(const KVMFRAudioFormat * wire,
return false;
break;
case KVMFR_AUDIO_SAMPLE_S32_LE:
format = LG_AUDIO_FMT_S32_LE;
bytesPerSample = 4;
if (wire->validBits != 32 || wire->containerBits != 32)
return false;
break;
case KVMFR_AUDIO_SAMPLE_F32_LE:
format = LG_AUDIO_FMT_F32_LE;
bytesPerSample = 4;
if (wire->validBits != 32 || wire->containerBits != 32)
return false;
break;
default:
return false;
}

View File

@@ -51,7 +51,7 @@ enum
#define KVMFR_AUDIO_MAX_CHANNELS 18U
#define KVMFR_AUDIO_MAX_SAMPLE_RATE 192000U
#define KVMFR_AUDIO_MAX_FRAMES_PER_PACKET 2048U
#define KVMFR_AUDIO_MAX_BYTES_PER_SAMPLE 3U
#define KVMFR_AUDIO_MAX_BYTES_PER_SAMPLE 4U
#define KVMFR_AUDIO_STREAM_MAX_CHANNELS 8U
#define KVMFR_AUDIO_STREAM_SLOT_COUNT 16U
#define KVMFR_AUDIO_OWNER_LEASE_MS 1000U
@@ -75,6 +75,9 @@ enum
KVMFR_AUDIO_SAMPLE_S16_LE = 1,
/* Signed little-endian samples packed into exactly three bytes. */
KVMFR_AUDIO_SAMPLE_S24_LE = 2,
KVMFR_AUDIO_SAMPLE_S32_LE = 3,
/* IEEE 754 little-endian floating-point samples. */
KVMFR_AUDIO_SAMPLE_F32_LE = 4,
};
typedef uint32_t KVMFRAudioSampleFormat;
@@ -83,6 +86,8 @@ enum
{
KVMFR_AUDIO_SAMPLE_MASK_S16_LE = 1U << 0,
KVMFR_AUDIO_SAMPLE_MASK_S24_LE = 1U << 1,
KVMFR_AUDIO_SAMPLE_MASK_S32_LE = 1U << 2,
KVMFR_AUDIO_SAMPLE_MASK_F32_LE = 1U << 3,
};
typedef uint32_t KVMFRAudioSampleFormatFlags;

View File

@@ -778,11 +778,39 @@ namespace
_In_ const LGAudioBrokerFormat &format,
_Out_opt_ FormatFingerprint *fingerprint)
{
if (format.reserved)
return false;
SampleFormat sampleFormat = SampleFormat::Unknown;
switch (format.sampleFormat)
{
case LGAUDIO_BROKER_SAMPLE_S16_LE:
if (format.containerBits != 16 || format.validBits != 16)
return false;
sampleFormat = SampleFormat::S16LE;
break;
case LGAUDIO_BROKER_SAMPLE_S24_LE:
if (format.containerBits != 24 || format.validBits != 24)
return false;
sampleFormat = SampleFormat::S24LE;
break;
case LGAUDIO_BROKER_SAMPLE_S32_LE:
if (format.containerBits != 32 || format.validBits != 32)
return false;
sampleFormat = SampleFormat::S32LE;
break;
case LGAUDIO_BROKER_SAMPLE_F32_LE:
if (format.containerBits != 32 || format.validBits != 32)
return false;
sampleFormat = SampleFormat::F32LE;
break;
default:
return false;
}
if (!format.sampleRate ||
format.sampleRate > kMaximumSampleRate ||
!format.channels || format.channels > 8 ||
(format.containerBits != 16 && format.containerBits != 24) ||
format.validBits != format.containerBits ||
format.blockAlign !=
format.channels * (format.containerBits / 8))
{
@@ -791,6 +819,7 @@ namespace
if (fingerprint)
{
fingerprint->sampleFormat = sampleFormat;
fingerprint->sampleRate = format.sampleRate;
fingerprint->channelMask = format.channelMask;
fingerprint->channels = format.channels;
@@ -805,12 +834,31 @@ namespace
_In_ const FormatFingerprint &source,
_Out_ LGAudioBrokerFormat *destination)
{
switch (source.sampleFormat)
{
case SampleFormat::S16LE:
destination->sampleFormat = LGAUDIO_BROKER_SAMPLE_S16_LE;
break;
case SampleFormat::S24LE:
destination->sampleFormat = LGAUDIO_BROKER_SAMPLE_S24_LE;
break;
case SampleFormat::S32LE:
destination->sampleFormat = LGAUDIO_BROKER_SAMPLE_S32_LE;
break;
case SampleFormat::F32LE:
destination->sampleFormat = LGAUDIO_BROKER_SAMPLE_F32_LE;
break;
default:
destination->sampleFormat = 0;
break;
}
destination->sampleRate = source.sampleRate;
destination->channelMask = source.channelMask;
destination->channels = source.channels;
destination->containerBits = source.containerBits;
destination->validBits = source.validBits;
destination->blockAlign = source.blockAlign;
destination->reserved = 0;
}
UINT32 GetBrokerStreamState(_In_ KSSTATE state)

View File

@@ -27,13 +27,19 @@ namespace LGAudio
constexpr USHORT kExtensibleExtraSize = static_cast<USHORT>(
sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX));
bool IsAudioPcmWaveFormat(_In_ const KSDATAFORMAT * format)
bool IsSupportedSubFormat(_In_ const GUID & subFormat)
{
return
IsEqualGUIDAligned(subFormat, KSDATAFORMAT_SUBTYPE_PCM) ||
IsEqualGUIDAligned(subFormat, KSDATAFORMAT_SUBTYPE_IEEE_FLOAT);
}
bool IsAudioWaveFormat(_In_ const KSDATAFORMAT * format)
{
return
IsEqualGUIDAligned(format->MajorFormat,
KSDATAFORMAT_TYPE_AUDIO) &&
IsEqualGUIDAligned(format->SubFormat,
KSDATAFORMAT_SUBTYPE_PCM) &&
IsSupportedSubFormat(format->SubFormat) &&
IsEqualGUIDAligned(format->Specifier,
KSDATAFORMAT_SPECIFIER_WAVEFORMATEX);
}
@@ -80,23 +86,6 @@ namespace LGAudio
}
}
void GetDataRangeList(
_In_ EndpointKind endpoint,
_Outptr_result_buffer_(*count) KSDATARANGE_AUDIO ** ranges,
_Out_ PULONG count)
{
if (endpoint == EndpointKind::Render)
{
*ranges = g_renderDataRanges;
*count = kRenderFormatCount;
}
else
{
*ranges = g_captureDataRanges;
*count = kCaptureFormatCount;
}
}
bool AudioRangeContainsFormat(
_In_ const KSDATARANGE_AUDIO & range,
_In_ const KSDATAFORMAT_WAVEFORMATEXTENSIBLE & format)
@@ -104,6 +93,8 @@ namespace LGAudio
const WAVEFORMATEX & wave = format.WaveFormatExt.Format;
return
IsEqualGUIDAligned(range.DataRange.SubFormat,
format.DataFormat.SubFormat) &&
range.MaximumChannels == wave.nChannels &&
range.MinimumBitsPerSample <= wave.wBitsPerSample &&
range.MaximumBitsPerSample >= wave.wBitsPerSample &&
@@ -118,16 +109,48 @@ namespace LGAudio
const WAVEFORMATEX & wave = format.WaveFormatExt.Format;
return
IsEqualGUIDAligned(range.DataRange.SubFormat,
format.DataFormat.SubFormat) &&
range.MaximumChannels == wave.nChannels &&
range.MinimumBitsPerSample == wave.wBitsPerSample &&
range.MaximumBitsPerSample == wave.wBitsPerSample &&
range.MinimumSampleFrequency == wave.nSamplesPerSec &&
range.MaximumSampleFrequency == wave.nSamplesPerSec;
}
SampleFormat GetSampleFormat(
_In_ const WAVEFORMATEXTENSIBLE & wave)
{
if (IsEqualGUIDAligned(wave.SubFormat,
KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
{
return wave.Format.wBitsPerSample == 32 &&
wave.Samples.wValidBitsPerSample == 32 ?
SampleFormat::F32LE : SampleFormat::Unknown;
}
if (!IsEqualGUIDAligned(wave.SubFormat,
KSDATAFORMAT_SUBTYPE_PCM) ||
wave.Format.wBitsPerSample !=
wave.Samples.wValidBitsPerSample)
return SampleFormat::Unknown;
switch (wave.Format.wBitsPerSample)
{
case 16:
return SampleFormat::S16LE;
case 24:
return SampleFormat::S24LE;
case 32:
return SampleFormat::S32LE;
default:
return SampleFormat::Unknown;
}
}
}
#define LG_AUDIO_FORMAT( \
sampleRate_, channels_, containerBits_, validBits_, mask_) \
sampleRate_, channels_, bits_, mask_, subType_) \
{ \
{ \
sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), \
@@ -135,7 +158,7 @@ namespace LGAudio
0, \
0, \
STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), \
STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), \
STATICGUIDOF(KSDATAFORMAT_SUBTYPE_##subType_), \
STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) \
}, \
{ \
@@ -143,28 +166,56 @@ namespace LGAudio
WAVE_FORMAT_EXTENSIBLE, \
channels_, \
sampleRate_, \
sampleRate_ * channels_ * (containerBits_ / 8), \
channels_ * (containerBits_ / 8), \
containerBits_, \
sampleRate_ * channels_ * (bits_ / 8), \
channels_ * (bits_ / 8), \
bits_, \
kExtensibleExtraSize \
}, \
validBits_, \
bits_, \
mask_, \
STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) \
STATICGUIDOF(KSDATAFORMAT_SUBTYPE_##subType_) \
} \
}
#define LG_RENDER_LAYOUTS(sampleRate_, bits_, subType_) \
LG_AUDIO_FORMAT(sampleRate_, 2, bits_, KSAUDIO_SPEAKER_STEREO, \
subType_), \
LG_AUDIO_FORMAT(sampleRate_, 1, bits_, KSAUDIO_SPEAKER_MONO, \
subType_), \
LG_AUDIO_FORMAT(sampleRate_, 4, bits_, KSAUDIO_SPEAKER_QUAD, \
subType_), \
LG_AUDIO_FORMAT(sampleRate_, 4, bits_, KSAUDIO_SPEAKER_SURROUND, \
subType_), \
LG_AUDIO_FORMAT(sampleRate_, 6, bits_, \
KSAUDIO_SPEAKER_5POINT1_SURROUND, subType_), \
LG_AUDIO_FORMAT(sampleRate_, 8, bits_, \
KSAUDIO_SPEAKER_7POINT1_SURROUND, subType_)
#define LG_CAPTURE_LAYOUTS(sampleRate_, bits_, subType_) \
LG_AUDIO_FORMAT(sampleRate_, 1, bits_, KSAUDIO_SPEAKER_MONO, \
subType_), \
LG_AUDIO_FORMAT(sampleRate_, 2, bits_, KSAUDIO_SPEAKER_STEREO, \
subType_), \
LG_AUDIO_FORMAT(sampleRate_, 4, bits_, KSAUDIO_SPEAKER_QUAD, \
subType_), \
LG_AUDIO_FORMAT(sampleRate_, 4, bits_, KSAUDIO_SPEAKER_SURROUND, \
subType_), \
LG_AUDIO_FORMAT(sampleRate_, 6, bits_, \
KSAUDIO_SPEAKER_5POINT1_SURROUND, subType_), \
LG_AUDIO_FORMAT(sampleRate_, 8, bits_, \
KSAUDIO_SPEAKER_7POINT1_SURROUND, subType_)
#define LG_RENDER_FORMATS(sampleRate_) \
LG_AUDIO_FORMAT(sampleRate_, 2, 16, 16, KSAUDIO_SPEAKER_STEREO), \
LG_AUDIO_FORMAT(sampleRate_, 2, 24, 24, KSAUDIO_SPEAKER_STEREO), \
LG_AUDIO_FORMAT(sampleRate_, 6, 24, 24, \
KSAUDIO_SPEAKER_5POINT1_SURROUND), \
LG_AUDIO_FORMAT(sampleRate_, 8, 24, 24, \
KSAUDIO_SPEAKER_7POINT1_SURROUND)
LG_RENDER_LAYOUTS(sampleRate_, 16, PCM), \
LG_RENDER_LAYOUTS(sampleRate_, 24, PCM), \
LG_RENDER_LAYOUTS(sampleRate_, 32, PCM), \
LG_RENDER_LAYOUTS(sampleRate_, 32, IEEE_FLOAT)
#define LG_CAPTURE_FORMATS(sampleRate_) \
LG_AUDIO_FORMAT(sampleRate_, 1, 16, 16, KSAUDIO_SPEAKER_MONO), \
LG_RENDER_FORMATS(sampleRate_)
LG_CAPTURE_LAYOUTS(sampleRate_, 16, PCM), \
LG_CAPTURE_LAYOUTS(sampleRate_, 24, PCM), \
LG_CAPTURE_LAYOUTS(sampleRate_, 32, PCM), \
LG_CAPTURE_LAYOUTS(sampleRate_, 32, IEEE_FLOAT)
KSDATAFORMAT_WAVEFORMATEXTENSIBLE
g_renderFormats[kRenderFormatCount] =
@@ -190,9 +241,11 @@ namespace LGAudio
#undef LG_CAPTURE_FORMATS
#undef LG_RENDER_FORMATS
#undef LG_CAPTURE_LAYOUTS
#undef LG_RENDER_LAYOUTS
#undef LG_AUDIO_FORMAT
#define LG_AUDIO_RANGE(sampleRate_, channels_, containerBits_) \
#define LG_AUDIO_RANGE(sampleRate_, channels_, bits_, subType_) \
{ \
{ \
sizeof(KSDATARANGE_AUDIO), \
@@ -200,27 +253,43 @@ namespace LGAudio
0, \
0, \
STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), \
STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), \
STATICGUIDOF(KSDATAFORMAT_SUBTYPE_##subType_), \
STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) \
}, \
channels_, \
containerBits_, \
containerBits_, \
bits_, \
bits_, \
sampleRate_, \
sampleRate_ \
}
#define LG_RENDER_LAYOUT_RANGES(sampleRate_, bits_, subType_) \
LG_AUDIO_RANGE(sampleRate_, 2, bits_, subType_), \
LG_AUDIO_RANGE(sampleRate_, 1, bits_, subType_), \
LG_AUDIO_RANGE(sampleRate_, 4, bits_, subType_), \
LG_AUDIO_RANGE(sampleRate_, 6, bits_, subType_), \
LG_AUDIO_RANGE(sampleRate_, 8, bits_, subType_)
#define LG_CAPTURE_LAYOUT_RANGES(sampleRate_, bits_, subType_) \
LG_AUDIO_RANGE(sampleRate_, 1, bits_, subType_), \
LG_AUDIO_RANGE(sampleRate_, 2, bits_, subType_), \
LG_AUDIO_RANGE(sampleRate_, 4, bits_, subType_), \
LG_AUDIO_RANGE(sampleRate_, 6, bits_, subType_), \
LG_AUDIO_RANGE(sampleRate_, 8, bits_, subType_)
#define LG_RENDER_RANGES(sampleRate_) \
LG_AUDIO_RANGE(sampleRate_, 2, 16), \
LG_AUDIO_RANGE(sampleRate_, 2, 24), \
LG_AUDIO_RANGE(sampleRate_, 6, 24), \
LG_AUDIO_RANGE(sampleRate_, 8, 24)
LG_RENDER_LAYOUT_RANGES(sampleRate_, 16, PCM), \
LG_RENDER_LAYOUT_RANGES(sampleRate_, 24, PCM), \
LG_RENDER_LAYOUT_RANGES(sampleRate_, 32, PCM), \
LG_RENDER_LAYOUT_RANGES(sampleRate_, 32, IEEE_FLOAT)
#define LG_CAPTURE_RANGES(sampleRate_) \
LG_AUDIO_RANGE(sampleRate_, 1, 16), \
LG_RENDER_RANGES(sampleRate_)
LG_CAPTURE_LAYOUT_RANGES(sampleRate_, 16, PCM), \
LG_CAPTURE_LAYOUT_RANGES(sampleRate_, 24, PCM), \
LG_CAPTURE_LAYOUT_RANGES(sampleRate_, 32, PCM), \
LG_CAPTURE_LAYOUT_RANGES(sampleRate_, 32, IEEE_FLOAT)
KSDATARANGE_AUDIO g_renderDataRanges[kRenderFormatCount] =
KSDATARANGE_AUDIO g_renderDataRanges[kRenderDataRangeCount] =
{
LG_RENDER_RANGES(48000),
LG_RENDER_RANGES(44100),
@@ -230,7 +299,7 @@ namespace LGAudio
LG_RENDER_RANGES(192000)
};
KSDATARANGE_AUDIO g_captureDataRanges[kCaptureFormatCount] =
KSDATARANGE_AUDIO g_captureDataRanges[kCaptureDataRangeCount] =
{
LG_CAPTURE_RANGES(48000),
LG_CAPTURE_RANGES(44100),
@@ -242,43 +311,45 @@ namespace LGAudio
#undef LG_CAPTURE_RANGES
#undef LG_RENDER_RANGES
#undef LG_CAPTURE_LAYOUT_RANGES
#undef LG_RENDER_LAYOUT_RANGES
#undef LG_AUDIO_RANGE
#define LG_RENDER_RANGE_POINTERS(base_) \
(PKSDATARANGE)&g_renderDataRanges[(base_)], \
(PKSDATARANGE)&g_renderDataRanges[(base_) + 1], \
(PKSDATARANGE)&g_renderDataRanges[(base_) + 2], \
(PKSDATARANGE)&g_renderDataRanges[(base_) + 3]
#define LG_RANGE_POINTERS_5(ranges_, base_) \
(PKSDATARANGE)&ranges_[(base_)], \
(PKSDATARANGE)&ranges_[(base_) + 1], \
(PKSDATARANGE)&ranges_[(base_) + 2], \
(PKSDATARANGE)&ranges_[(base_) + 3], \
(PKSDATARANGE)&ranges_[(base_) + 4]
#define LG_CAPTURE_RANGE_POINTERS(base_) \
(PKSDATARANGE)&g_captureDataRanges[(base_)], \
(PKSDATARANGE)&g_captureDataRanges[(base_) + 1], \
(PKSDATARANGE)&g_captureDataRanges[(base_) + 2], \
(PKSDATARANGE)&g_captureDataRanges[(base_) + 3], \
(PKSDATARANGE)&g_captureDataRanges[(base_) + 4]
#define LG_RANGE_POINTERS(ranges_, base_) \
LG_RANGE_POINTERS_5(ranges_, base_), \
LG_RANGE_POINTERS_5(ranges_, (base_) + 5), \
LG_RANGE_POINTERS_5(ranges_, (base_) + 10), \
LG_RANGE_POINTERS_5(ranges_, (base_) + 15)
PKSDATARANGE g_renderDataRangePointers[kRenderFormatCount] =
PKSDATARANGE g_renderDataRangePointers[kRenderDataRangeCount] =
{
LG_RENDER_RANGE_POINTERS(0),
LG_RENDER_RANGE_POINTERS(4),
LG_RENDER_RANGE_POINTERS(8),
LG_RENDER_RANGE_POINTERS(12),
LG_RENDER_RANGE_POINTERS(16),
LG_RENDER_RANGE_POINTERS(20)
LG_RANGE_POINTERS(g_renderDataRanges, 0),
LG_RANGE_POINTERS(g_renderDataRanges, 20),
LG_RANGE_POINTERS(g_renderDataRanges, 40),
LG_RANGE_POINTERS(g_renderDataRanges, 60),
LG_RANGE_POINTERS(g_renderDataRanges, 80),
LG_RANGE_POINTERS(g_renderDataRanges, 100)
};
PKSDATARANGE g_captureDataRangePointers[kCaptureFormatCount] =
PKSDATARANGE g_captureDataRangePointers[kCaptureDataRangeCount] =
{
LG_CAPTURE_RANGE_POINTERS(0),
LG_CAPTURE_RANGE_POINTERS(5),
LG_CAPTURE_RANGE_POINTERS(10),
LG_CAPTURE_RANGE_POINTERS(15),
LG_CAPTURE_RANGE_POINTERS(20),
LG_CAPTURE_RANGE_POINTERS(25)
LG_RANGE_POINTERS(g_captureDataRanges, 0),
LG_RANGE_POINTERS(g_captureDataRanges, 20),
LG_RANGE_POINTERS(g_captureDataRanges, 40),
LG_RANGE_POINTERS(g_captureDataRanges, 60),
LG_RANGE_POINTERS(g_captureDataRanges, 80),
LG_RANGE_POINTERS(g_captureDataRanges, 100)
};
#undef LG_CAPTURE_RANGE_POINTERS
#undef LG_RENDER_RANGE_POINTERS
#undef LG_RANGE_POINTERS
#undef LG_RANGE_POINTERS_5
_IRQL_requires_max_(DISPATCH_LEVEL)
NTSTATUS ValidateFormat(
@@ -292,7 +363,7 @@ namespace LGAudio
if (!dataFormat ||
dataFormat->FormatSize < sizeof(KSDATAFORMAT_WAVEFORMATEX) ||
!IsAudioPcmWaveFormat(dataFormat))
!IsAudioWaveFormat(dataFormat))
return STATUS_NO_MATCH;
const WAVEFORMATEX * requested =
@@ -307,18 +378,29 @@ namespace LGAudio
const KSDATAFORMAT_WAVEFORMATEXTENSIBLE & candidate = formats[i];
const WAVEFORMATEXTENSIBLE & candidateWave = candidate.WaveFormatExt;
if (!WaveFormatCoreMatches(candidateWave.Format, *requested))
if (!IsEqualGUIDAligned(candidate.DataFormat.SubFormat,
dataFormat->SubFormat) ||
!WaveFormatCoreMatches(candidateWave.Format, *requested))
continue;
if (requested->wFormatTag == WAVE_FORMAT_PCM)
{
// A stand-alone WAVEFORMATEX is unambiguous only for these legacy
// mono/stereo, 16-bit formats.
if (candidateWave.Format.wBitsPerSample != 16 ||
if (!IsEqualGUIDAligned(candidateWave.SubFormat,
KSDATAFORMAT_SUBTYPE_PCM) ||
candidateWave.Format.wBitsPerSample != 16 ||
candidateWave.Samples.wValidBitsPerSample != 16 ||
candidateWave.Format.nChannels > 2)
continue;
}
else if (requested->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
{
if (!IsEqualGUIDAligned(candidateWave.SubFormat,
KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) ||
candidateWave.Format.wBitsPerSample != 32 ||
candidateWave.Samples.wValidBitsPerSample != 32 ||
candidateWave.Format.nChannels > 2)
continue;
}
else if (requested->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
{
if (dataFormat->FormatSize <
@@ -328,7 +410,9 @@ namespace LGAudio
const WAVEFORMATEXTENSIBLE * requestedExtensible =
reinterpret_cast<const WAVEFORMATEXTENSIBLE *>(requested);
if (!ExtensibleFormatMatches(candidateWave, *requestedExtensible))
if (!IsEqualGUIDAligned(dataFormat->SubFormat,
requestedExtensible->SubFormat) ||
!ExtensibleFormatMatches(candidateWave, *requestedExtensible))
continue;
}
else
@@ -359,8 +443,8 @@ namespace LGAudio
if (clientDataRange->FormatSize < sizeof(KSDATARANGE_AUDIO) ||
driverDataRange->FormatSize < sizeof(KSDATARANGE_AUDIO) ||
!IsAudioPcmWaveFormat(clientDataRange) ||
!IsAudioPcmWaveFormat(driverDataRange))
!IsAudioWaveFormat(clientDataRange) ||
!IsAudioWaveFormat(driverDataRange))
return STATUS_NO_MATCH;
const KSDATARANGE_AUDIO & client =
@@ -369,20 +453,13 @@ namespace LGAudio
*reinterpret_cast<const KSDATARANGE_AUDIO *>(driverDataRange);
KSDATAFORMAT_WAVEFORMATEXTENSIBLE * formats;
KSDATARANGE_AUDIO * ranges;
ULONG formatCount;
ULONG rangeCount;
GetFormatList(endpoint, &formats, &formatCount);
GetDataRangeList(endpoint, &ranges, &rangeCount);
if (formatCount != rangeCount)
return STATUS_INTERNAL_ERROR;
const KSDATAFORMAT_WAVEFORMATEXTENSIBLE * selected = nullptr;
for (ULONG i = 0; i < formatCount; ++i)
{
if (!DataRangeIdentifiesFormat(driver, formats[i]) ||
!DataRangeIdentifiesFormat(ranges[i], formats[i]) ||
!AudioRangeContainsFormat(client, formats[i]))
continue;
@@ -417,6 +494,7 @@ namespace LGAudio
return STATUS_INVALID_PARAMETER;
const WAVEFORMATEXTENSIBLE & wave = dataFormat->WaveFormatExt;
const SampleFormat sampleFormat = GetSampleFormat(wave);
if (wave.Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE ||
wave.Format.cbSize < kExtensibleExtraSize ||
wave.Format.nChannels == 0 ||
@@ -425,11 +503,13 @@ namespace LGAudio
wave.Samples.wValidBitsPerSample == 0 ||
wave.Samples.wValidBitsPerSample >
wave.Format.wBitsPerSample ||
!IsAudioPcmWaveFormat(&dataFormat->DataFormat) ||
!IsEqualGUIDAligned(wave.SubFormat,
KSDATAFORMAT_SUBTYPE_PCM))
!IsAudioWaveFormat(&dataFormat->DataFormat) ||
!IsEqualGUIDAligned(dataFormat->DataFormat.SubFormat,
wave.SubFormat) ||
sampleFormat == SampleFormat::Unknown)
return STATUS_INVALID_PARAMETER;
fingerprint->sampleFormat = sampleFormat;
fingerprint->sampleRate = wave.Format.nSamplesPerSec;
fingerprint->channelMask = wave.dwChannelMask;
fingerprint->channels = wave.Format.nChannels;
@@ -445,6 +525,7 @@ namespace LGAudio
_In_ const FormatFingerprint & right)
{
return
left.sampleFormat == right.sampleFormat &&
left.sampleRate == right.sampleRate &&
left.channelMask == right.channelMask &&
left.channels == right.channels &&

View File

@@ -26,6 +26,15 @@
namespace LGAudio
{
enum class SampleFormat : ULONG
{
Unknown = 0,
S16LE = 1,
S24LE = 2,
S32LE = 3,
F32LE = 4
};
enum class EndpointKind : UCHAR
{
Render,
@@ -34,6 +43,7 @@ namespace LGAudio
struct FormatFingerprint
{
SampleFormat sampleFormat;
ULONG sampleRate;
ULONG channelMask;
USHORT channels;
@@ -44,15 +54,21 @@ namespace LGAudio
constexpr ULONG kSampleRate = 48000;
constexpr ULONG kSampleRateCount = 6;
constexpr ULONG kRenderFormatsPerRate = 4;
constexpr ULONG kCaptureFormatsPerRate = 5;
constexpr ULONG kRenderFormatsPerRate = 24;
constexpr ULONG kCaptureFormatsPerRate = 24;
constexpr ULONG kRenderDataRangesPerRate = 20;
constexpr ULONG kCaptureDataRangesPerRate = 20;
constexpr ULONG kRenderFormatCount =
kSampleRateCount * kRenderFormatsPerRate;
constexpr ULONG kCaptureFormatCount =
kSampleRateCount * kCaptureFormatsPerRate;
constexpr ULONG kRenderDataRangeCount =
kSampleRateCount * kRenderDataRangesPerRate;
constexpr ULONG kCaptureDataRangeCount =
kSampleRateCount * kCaptureDataRangesPerRate;
constexpr ULONG kMaximumSampleRate = 192000;
constexpr ULONG kMaximumBlockAlign =
8 * 3;
8 * 4;
constexpr ULONG kMaximumBytesPerSecond =
kMaximumSampleRate * kMaximumBlockAlign;
@@ -61,11 +77,11 @@ namespace LGAudio
extern KSDATAFORMAT_WAVEFORMATEXTENSIBLE
g_captureFormats[kCaptureFormatCount];
extern KSDATARANGE_AUDIO g_renderDataRanges[kRenderFormatCount];
extern KSDATARANGE_AUDIO g_captureDataRanges[kCaptureFormatCount];
extern KSDATARANGE_AUDIO g_renderDataRanges[kRenderDataRangeCount];
extern KSDATARANGE_AUDIO g_captureDataRanges[kCaptureDataRangeCount];
extern PKSDATARANGE g_renderDataRangePointers[kRenderFormatCount];
extern PKSDATARANGE g_captureDataRangePointers[kCaptureFormatCount];
extern PKSDATARANGE g_renderDataRangePointers[kRenderDataRangeCount];
extern PKSDATARANGE g_captureDataRangePointers[kCaptureDataRangeCount];
/**
* Validate an instantiated WaveRT format against the endpoint's exact
@@ -106,6 +122,7 @@ namespace LGAudio
// The rest of the driver uses these as its shared, unqualified audio types.
using LGAudio::EndpointKind;
using LGAudio::FormatFingerprint;
using LGAudio::SampleFormat;
using LGAudio::EqualFormatFingerprint;
using LGAudio::GetFormatFingerprint;
using LGAudio::SelectFormatIntersection;
@@ -117,9 +134,11 @@ using LGAudio::g_renderDataRangePointers;
using LGAudio::g_renderDataRanges;
using LGAudio::g_renderFormats;
using LGAudio::kCaptureFormatCount;
using LGAudio::kCaptureDataRangeCount;
using LGAudio::kMaximumBlockAlign;
using LGAudio::kMaximumBytesPerSecond;
using LGAudio::kMaximumSampleRate;
using LGAudio::kRenderFormatCount;
using LGAudio::kRenderDataRangeCount;
using LGAudio::kSampleRate;
using LGAudio::kSampleRateCount;

View File

@@ -46,7 +46,7 @@ PCPIN_DESCRIPTOR g_RenderWavePins[] =
{
0, nullptr,
0, nullptr,
kRenderFormatCount, g_renderDataRangePointers,
kRenderDataRangeCount, g_renderDataRangePointers,
KSPIN_DATAFLOW_IN,
KSPIN_COMMUNICATION_SINK,
&KSCATEGORY_AUDIO,
@@ -115,7 +115,7 @@ PCPIN_DESCRIPTOR g_CaptureWavePins[] =
{
0, nullptr,
0, nullptr,
kCaptureFormatCount, g_captureDataRangePointers,
kCaptureDataRangeCount, g_captureDataRangePointers,
KSPIN_DATAFLOW_OUT,
KSPIN_COMMUNICATION_SINK,
&KSCATEGORY_AUDIO,
@@ -284,8 +284,8 @@ NTSTATUS CMiniportWaveRT::NewStream(
if (InterlockedCompareExchange(&m_streamOpen, 1, 0) != 0)
return STATUS_DEVICE_BUSY;
CMiniportWaveRTStream *newStream =
new(NonPagedPoolNx, LGAUDIO_POOL_TAG) CMiniportWaveRTStream(
CWS *newStream =
new(NonPagedPoolNx, LGAUDIO_POOL_TAG) CWS(
this, m_adapter, portStream, m_kind);
if (!newStream)
{

View File

@@ -129,7 +129,7 @@ ULONGLONG FramesToQpcTicks(
}
}
CMiniportWaveRTStream::CMiniportWaveRTStream(
CWS::CWS(
_In_ CMiniportWaveRT *miniport,
_In_ CAudioAdapter *adapter,
_In_ PPORTWAVERTSTREAM portStream,
@@ -177,7 +177,7 @@ CMiniportWaveRTStream::CMiniportWaveRTStream(
KeInitializeSpinLock(&m_positionLock);
}
CMiniportWaveRTStream::~CMiniportWaveRTStream()
CWS::~CWS()
{
SetState(KSSTATE_STOP);
@@ -195,7 +195,7 @@ CMiniportWaveRTStream::~CMiniportWaveRTStream()
m_miniport->Release();
}
NTSTATUS CMiniportWaveRTStream::Initialize(
NTSTATUS CWS::Initialize(
_In_ const KSDATAFORMAT_WAVEFORMATEXTENSIBLE *format)
{
if (!format)
@@ -221,7 +221,7 @@ NTSTATUS CMiniportWaveRTStream::Initialize(
return status;
}
NTSTATUS CMiniportWaveRTStream::QueryInterface(
NTSTATUS CWS::QueryInterface(
_In_ REFIID interfaceId,
_COM_Outptr_ void **object)
{
@@ -254,12 +254,12 @@ NTSTATUS CMiniportWaveRTStream::QueryInterface(
return STATUS_SUCCESS;
}
ULONG CMiniportWaveRTStream::AddRef()
ULONG CWS::AddRef()
{
return static_cast<ULONG>(InterlockedIncrement(&m_refCount));
}
ULONG CMiniportWaveRTStream::Release()
ULONG CWS::Release()
{
const LONG count = InterlockedDecrement(&m_refCount);
if (!count)
@@ -267,7 +267,7 @@ ULONG CMiniportWaveRTStream::Release()
return static_cast<ULONG>(count);
}
NTSTATUS CMiniportWaveRTStream::SetFormat(
NTSTATUS CWS::SetFormat(
_In_ PKSDATAFORMAT dataFormat)
{
if (!dataFormat)
@@ -301,7 +301,7 @@ NTSTATUS CMiniportWaveRTStream::SetFormat(
return status;
}
NTSTATUS CMiniportWaveRTStream::SetState(_In_ KSSTATE state)
NTSTATUS CWS::SetState(_In_ KSSTATE state)
{
if (state < KSSTATE_STOP || state > KSSTATE_RUN)
return STATUS_INVALID_PARAMETER;
@@ -394,7 +394,7 @@ NTSTATUS CMiniportWaveRTStream::SetState(_In_ KSSTATE state)
return STATUS_SUCCESS;
}
NTSTATUS CMiniportWaveRTStream::GetPosition(
NTSTATUS CWS::GetPosition(
_Out_ PKSAUDIO_POSITION position)
{
if (!position)
@@ -418,7 +418,7 @@ NTSTATUS CMiniportWaveRTStream::GetPosition(
return STATUS_SUCCESS;
}
NTSTATUS CMiniportWaveRTStream::AllocateAudioBuffer(
NTSTATUS CWS::AllocateAudioBuffer(
_In_ ULONG requestedSize,
_Out_ PMDL *audioBufferMdl,
_Out_ ULONG *actualSize,
@@ -429,7 +429,7 @@ NTSTATUS CMiniportWaveRTStream::AllocateAudioBuffer(
offsetFromFirstPage, cacheType);
}
void CMiniportWaveRTStream::FreeAudioBuffer(
void CWS::FreeAudioBuffer(
_In_opt_ PMDL audioBufferMdl,
_In_ ULONG bufferSize)
{
@@ -437,14 +437,14 @@ void CMiniportWaveRTStream::FreeAudioBuffer(
FreeBuffer(audioBufferMdl);
}
void CMiniportWaveRTStream::GetHWLatency(
void CWS::GetHWLatency(
_Out_ PKSRTAUDIO_HWLATENCY latency)
{
if (latency)
RtlZeroMemory(latency, sizeof(*latency));
}
NTSTATUS CMiniportWaveRTStream::GetPositionRegister(
NTSTATUS CWS::GetPositionRegister(
_Out_ PKSRTAUDIO_HWREGISTER positionRegister)
{
if (positionRegister)
@@ -452,7 +452,7 @@ NTSTATUS CMiniportWaveRTStream::GetPositionRegister(
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS CMiniportWaveRTStream::GetClockRegister(
NTSTATUS CWS::GetClockRegister(
_Out_ PKSRTAUDIO_HWREGISTER clockRegister)
{
if (clockRegister)
@@ -460,7 +460,7 @@ NTSTATUS CMiniportWaveRTStream::GetClockRegister(
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS CMiniportWaveRTStream::AllocateBufferWithNotification(
NTSTATUS CWS::AllocateBufferWithNotification(
_In_ ULONG notificationCount,
_In_ ULONG requestedSize,
_Out_ PMDL *audioBufferMdl,
@@ -475,7 +475,7 @@ NTSTATUS CMiniportWaveRTStream::AllocateBufferWithNotification(
actualSize, offsetFromFirstPage, cacheType);
}
void CMiniportWaveRTStream::FreeBufferWithNotification(
void CWS::FreeBufferWithNotification(
_In_ PMDL audioBufferMdl,
_In_ ULONG bufferSize)
{
@@ -483,7 +483,7 @@ void CMiniportWaveRTStream::FreeBufferWithNotification(
FreeBuffer(audioBufferMdl);
}
NTSTATUS CMiniportWaveRTStream::RegisterNotificationEvent(
NTSTATUS CWS::RegisterNotificationEvent(
_In_ PKEVENT notificationEvent)
{
if (!notificationEvent)
@@ -511,7 +511,7 @@ NTSTATUS CMiniportWaveRTStream::RegisterNotificationEvent(
return STATUS_SUCCESS;
}
NTSTATUS CMiniportWaveRTStream::UnregisterNotificationEvent(
NTSTATUS CWS::UnregisterNotificationEvent(
_In_ PKEVENT notificationEvent)
{
if (!notificationEvent)
@@ -535,7 +535,7 @@ NTSTATUS CMiniportWaveRTStream::UnregisterNotificationEvent(
return STATUS_NOT_FOUND;
}
NTSTATUS CMiniportWaveRTStream::GetReadPacket(
NTSTATUS CWS::GetReadPacket(
_Out_ ULONG *packetNumber,
_Out_ DWORD *flags,
_Out_ ULONG64 *performanceCounterValue,
@@ -597,7 +597,7 @@ NTSTATUS CMiniportWaveRTStream::GetReadPacket(
return STATUS_SUCCESS;
}
NTSTATUS CMiniportWaveRTStream::SetWritePacket(
NTSTATUS CWS::SetWritePacket(
_In_ ULONG packetNumber,
_In_ DWORD flags,
_In_ ULONG eosPacketLength)
@@ -669,7 +669,7 @@ NTSTATUS CMiniportWaveRTStream::SetWritePacket(
return STATUS_SUCCESS;
}
NTSTATUS CMiniportWaveRTStream::GetOutputStreamPresentationPosition(
NTSTATUS CWS::GetOutputStreamPresentationPosition(
_Out_ KSAUDIO_PRESENTATION_POSITION *position)
{
if (m_kind != EndpointKind::Render || !position)
@@ -710,7 +710,7 @@ NTSTATUS CMiniportWaveRTStream::GetOutputStreamPresentationPosition(
return STATUS_SUCCESS;
}
NTSTATUS CMiniportWaveRTStream::GetPacketCount(
NTSTATUS CWS::GetPacketCount(
_Out_ ULONG *packetCount)
{
if (m_kind != EndpointKind::Render || !packetCount)
@@ -732,7 +732,7 @@ NTSTATUS CMiniportWaveRTStream::GetPacketCount(
return STATUS_SUCCESS;
}
NTSTATUS CMiniportWaveRTStream::AllocateBuffer(
NTSTATUS CWS::AllocateBuffer(
_In_ ULONG notificationCount,
_In_ ULONG requestedSize,
_Out_ PMDL *audioBufferMdl,
@@ -824,7 +824,7 @@ NTSTATUS CMiniportWaveRTStream::AllocateBuffer(
return STATUS_SUCCESS;
}
void CMiniportWaveRTStream::FreeBuffer(_In_opt_ PMDL audioBufferMdl)
void CWS::FreeBuffer(_In_opt_ PMDL audioBufferMdl)
{
if (!audioBufferMdl)
return;
@@ -856,13 +856,13 @@ void CMiniportWaveRTStream::FreeBuffer(_In_opt_ PMDL audioBufferMdl)
m_portStream->FreePagesFromMdl(mdl);
}
void CMiniportWaveRTStream::StopTimer()
void CWS::StopTimer()
{
if (m_timer)
ExCancelTimer(m_timer, nullptr);
}
void CMiniportWaveRTStream::ArmTimerLocked()
void CWS::ArmTimerLocked()
{
if (!m_timer || m_state != KSSTATE_RUN || !m_notificationCount ||
!m_packetSize || !m_clockFrequency || !m_qpcFrequency ||
@@ -927,7 +927,7 @@ void CMiniportWaveRTStream::ArmTimerLocked()
ExSetTimer(m_timer, -static_cast<LONGLONG>(dueTime), 0, nullptr);
}
void CMiniportWaveRTStream::AdvanceLocked(
void CWS::AdvanceLocked(
_In_ const AudioClockSnapshot &clock)
{
if (m_state != KSSTATE_RUN || !m_buffer || !m_bufferSize ||
@@ -986,7 +986,7 @@ void CMiniportWaveRTStream::AdvanceLocked(
AdvanceEventPacketsLocked(clock);
}
void CMiniportWaveRTStream::AdvanceEventPacketsLocked(
void CWS::AdvanceEventPacketsLocked(
_In_ const AudioClockSnapshot &clock)
{
if (!m_packetSize)
@@ -1080,7 +1080,7 @@ void CMiniportWaveRTStream::AdvanceEventPacketsLocked(
}
}
bool CMiniportWaveRTStream::RenderPacketCommittedLocked(
bool CWS::RenderPacketCommittedLocked(
_In_ ULONGLONG position) const
{
if (m_kind != EndpointKind::Render || !m_notificationCount ||
@@ -1091,7 +1091,7 @@ bool CMiniportWaveRTStream::RenderPacketCommittedLocked(
return static_cast<LONG>(m_lastWritePacket - packet) >= 0;
}
void CMiniportWaveRTStream::PublishRenderFramesLocked()
void CWS::PublishRenderFramesLocked()
{
if (m_kind != EndpointKind::Render || !m_notificationCount ||
!m_packetSize || !m_renderPublicationSliceBytes || !m_bufferSize)
@@ -1133,7 +1133,7 @@ void CMiniportWaveRTStream::PublishRenderFramesLocked()
}
}
void CMiniportWaveRTStream::UpdatePresentationQpcLocked(
void CWS::UpdatePresentationQpcLocked(
_In_ const AudioClockSnapshot &clock)
{
const ULONG blockAlign =
@@ -1161,7 +1161,7 @@ void CMiniportWaveRTStream::UpdatePresentationQpcLocked(
clock.QpcPosition - ticksSinceBoundary : 0;
}
void CMiniportWaveRTStream::TransferFramesLocked(
void CWS::TransferFramesLocked(
_In_ ULONGLONG bytes,
_In_ bool transferRender)
{
@@ -1233,19 +1233,19 @@ void CMiniportWaveRTStream::TransferFramesLocked(
}
}
void CMiniportWaveRTStream::SignalNotificationsLocked()
void CWS::SignalNotificationsLocked()
{
for (ULONG i = 0; i < m_notificationEventCount; ++i)
KeSetEvent(m_notificationEvents[i], IO_NO_INCREMENT, FALSE);
}
void CMiniportWaveRTStream::TimerCallback(
void CWS::TimerCallback(
_In_ PEX_TIMER timer,
_In_opt_ void *context)
{
UNREFERENCED_PARAMETER(timer);
auto *stream = static_cast<CMiniportWaveRTStream *>(context);
auto *stream = static_cast<CWS *>(context);
if (!stream)
return;

View File

@@ -26,18 +26,18 @@
class CMiniportWaveRT;
class CMiniportWaveRTStream final :
class CWS final :
public IMiniportWaveRTStreamNotification,
public IMiniportWaveRTInputStream,
public IMiniportWaveRTOutputStream
{
public:
CMiniportWaveRTStream(
CWS(
_In_ CMiniportWaveRT *miniport,
_In_ CAudioAdapter *adapter,
_In_ PPORTWAVERTSTREAM portStream,
_In_ EndpointKind kind);
~CMiniportWaveRTStream();
~CWS();
NTSTATUS Initialize(
_In_ const KSDATAFORMAT_WAVEFORMATEXTENSIBLE *format);

View File

@@ -25,6 +25,8 @@ namespace LGAudio
namespace
{
constexpr ULONG kRingPoolTag = 'rAGL';
// LCM of every supported channel-count and sample-width block alignment.
constexpr ULONG kRingBufferAlignment = 288;
ULONG MinUlong(_In_ ULONG left, _In_ ULONG right)
{
@@ -56,8 +58,8 @@ namespace LGAudio
return STATUS_SUCCESS;
constexpr ULONG capacity =
(kMaximumBytesPerSecond + kMaximumBlockAlign - 1) /
kMaximumBlockAlign * kMaximumBlockAlign;
(kMaximumBytesPerSecond + kRingBufferAlignment - 1) /
kRingBufferAlignment * kRingBufferAlignment;
#pragma warning(push)
#pragma warning(disable:4996) // Required for pre-2004 Windows 10 releases.
@@ -110,7 +112,8 @@ namespace LGAudio
NTSTATUS CAudioRingBuffer::Configure(
_In_ const FormatFingerprint & format)
{
if (format.sampleRate == 0 ||
if (format.sampleFormat == SampleFormat::Unknown ||
format.sampleRate == 0 ||
format.channels == 0 ||
format.containerBits == 0 ||
(format.containerBits % 8) != 0 ||

View File

@@ -43,7 +43,7 @@ static const GUID LGAUDIO_BROKER_DEVICE_CLASS_GUID =
{ 0x98f877f1, 0x73d6, 0x4429,
{ 0xb4, 0x90, 0x28, 0xc5, 0x30, 0xd2, 0x38, 0xcd } };
static constexpr UINT32 LGAUDIO_BROKER_ABI_VERSION = 1U;
static constexpr UINT32 LGAUDIO_BROKER_ABI_VERSION = 2U;
// The transfer limits in LGAudioBrokerCaps exclude the packet header.
static constexpr UINT32 LGAUDIO_BROKER_MAX_PAYLOAD_BYTES = 64U * 1024U;
@@ -87,6 +87,14 @@ enum LGAudioBrokerPacketFlag : UINT32
LGAUDIO_BROKER_PACKET_DISCONTINUITY = 1U << 0
};
enum LGAudioBrokerSampleFormat : UINT32
{
LGAUDIO_BROKER_SAMPLE_S16_LE = 1,
LGAUDIO_BROKER_SAMPLE_S24_LE = 2,
LGAUDIO_BROKER_SAMPLE_S32_LE = 3,
LGAUDIO_BROKER_SAMPLE_F32_LE = 4
};
static constexpr UINT32 IOCTL_LGAUDIO_BROKER_QUERY_CAPS =
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x900, METHOD_BUFFERED,
FILE_READ_ACCESS | FILE_WRITE_ACCESS);
@@ -116,12 +124,14 @@ struct LGAudioBrokerHeader
struct LGAudioBrokerFormat
{
UINT32 sampleFormat;
UINT32 sampleRate;
UINT32 channelMask;
UINT16 channels;
UINT16 containerBits;
UINT16 validBits;
UINT16 blockAlign;
UINT32 reserved;
};
struct LGAudioBrokerStreamState
@@ -230,15 +240,15 @@ struct LGAudioBrokerClockControl
static_assert(sizeof(LGAudioBrokerHeader) == 8,
"LGAudio broker header layout changed");
static_assert(sizeof(LGAudioBrokerFormat) == 16,
static_assert(sizeof(LGAudioBrokerFormat) == 24,
"LGAudio broker format layout changed");
static_assert(sizeof(LGAudioBrokerStreamState) == 32,
static_assert(sizeof(LGAudioBrokerStreamState) == 40,
"LGAudio broker stream-state layout changed");
static_assert(sizeof(LGAudioBrokerPacketHeader) == 80,
static_assert(sizeof(LGAudioBrokerPacketHeader) == 88,
"LGAudio broker packet layout changed");
static_assert(sizeof(LGAudioBrokerCaps) == 64,
"LGAudio broker capabilities layout changed");
static_assert(sizeof(LGAudioBrokerState) == 104,
static_assert(sizeof(LGAudioBrokerState) == 120,
"LGAudio broker state layout changed");
static_assert(sizeof(LGAudioBrokerBind) == 48,
"LGAudio broker binding layout changed");
@@ -247,5 +257,5 @@ static_assert(sizeof(LGAudioBrokerStats) == 160,
static_assert(sizeof(LGAudioBrokerClockControl) == 64,
"LGAudio broker clock-control layout changed");
static_assert(LGAUDIO_BROKER_MAX_PAYLOAD_BYTES >=
(192000U / 100U) * 8U * 3U,
(192000U / 100U) * 8U * 4U,
"LGAudio broker payload cannot hold 10 ms at the maximum format");

View File

@@ -70,7 +70,8 @@ namespace
bool SameFormat(const AudioFormat& left, const AudioFormat& right)
{
return left.sampleRate == right.sampleRate &&
return left.sampleFormat == right.sampleFormat &&
left.sampleRate == right.sampleRate &&
left.channelMask == right.channelMask &&
left.channels == right.channels &&
left.containerBits == right.containerBits &&
@@ -78,6 +79,22 @@ namespace
left.blockAlign == right.blockAlign;
}
bool ValidSampleFormat(const AudioFormat& format)
{
switch (format.sampleFormat)
{
case AudioSampleFormat::S16_LE:
return format.containerBits == 16 && format.validBits == 16;
case AudioSampleFormat::S24_LE:
return format.containerBits == 24 && format.validBits == 24;
case AudioSampleFormat::S32_LE:
case AudioSampleFormat::F32_LE:
return format.containerBits == 32 && format.validBits == 32;
default:
return false;
}
}
bool ValidFormat(const AudioFormat& format)
{
return format.sampleRate && format.sampleRate <= 192000 &&
@@ -85,15 +102,52 @@ namespace
format.channelMask &&
!(format.channelMask & ~AUDIO_CHANNEL_POSITION_MASK) &&
PopCount(format.channelMask) == format.channels &&
(format.containerBits == 16 || format.containerBits == 24) &&
format.validBits == format.containerBits &&
ValidSampleFormat(format) &&
format.blockAlign ==
format.channels * (format.containerBits / 8);
}
AudioSampleFormat BrokerSampleFormat(uint32_t source)
{
switch (source)
{
case LGAUDIO_BROKER_SAMPLE_S16_LE:
return AudioSampleFormat::S16_LE;
case LGAUDIO_BROKER_SAMPLE_S24_LE:
return AudioSampleFormat::S24_LE;
case LGAUDIO_BROKER_SAMPLE_S32_LE:
return AudioSampleFormat::S32_LE;
case LGAUDIO_BROKER_SAMPLE_F32_LE:
return AudioSampleFormat::F32_LE;
default:
return AudioSampleFormat::UNKNOWN;
}
}
uint32_t DeviceSampleFormat(AudioSampleFormat source)
{
switch (source)
{
case AudioSampleFormat::S16_LE:
return LGAUDIO_BROKER_SAMPLE_S16_LE;
case AudioSampleFormat::S24_LE:
return LGAUDIO_BROKER_SAMPLE_S24_LE;
case AudioSampleFormat::S32_LE:
return LGAUDIO_BROKER_SAMPLE_S32_LE;
case AudioSampleFormat::F32_LE:
return LGAUDIO_BROKER_SAMPLE_F32_LE;
default:
return 0;
}
}
AudioFormat BrokerFormat(const LGAudioBrokerFormat& source)
{
AudioFormat result;
if (source.reserved)
return result;
result.sampleFormat = BrokerSampleFormat(source.sampleFormat);
result.sampleRate = source.sampleRate;
result.channelMask = source.channelMask;
result.channels = source.channels;
@@ -106,6 +160,7 @@ namespace
LGAudioBrokerFormat DeviceFormat(const AudioFormat& source)
{
LGAudioBrokerFormat result = {};
result.sampleFormat = DeviceSampleFormat(source.sampleFormat);
result.sampleRate = source.sampleRate;
result.channelMask = source.channelMask;
result.channels = source.channels;

View File

@@ -26,7 +26,7 @@
static constexpr uint32_t AUDIO_MAX_CHANNELS = 18;
static constexpr uint32_t AUDIO_STREAM_MAX_CHANNELS = 8;
static constexpr uint32_t AUDIO_MAX_FRAMES_PER_PACKET = 2048;
static constexpr uint32_t AUDIO_MAX_BYTES_PER_SAMPLE = 3;
static constexpr uint32_t AUDIO_MAX_BYTES_PER_SAMPLE = 4;
static constexpr uint32_t AUDIO_MAX_PCM_BYTES =
AUDIO_MAX_FRAMES_PER_PACKET * AUDIO_STREAM_MAX_CHANNELS *
AUDIO_MAX_BYTES_PER_SAMPLE;
@@ -54,6 +54,15 @@ enum AudioClockFlag : uint32_t
AUDIO_CLOCK_DISCONTINUITY = 1U << 2,
};
enum class AudioSampleFormat : uint32_t
{
UNKNOWN = 0,
S16_LE = 1,
S24_LE = 2,
S32_LE = 3,
F32_LE = 4,
};
enum class AudioStreamState : uint32_t
{
UNAVAILABLE,
@@ -75,12 +84,13 @@ enum class AudioResult
struct AudioFormat
{
uint32_t sampleRate = 0;
uint32_t channelMask = 0;
uint16_t channels = 0;
uint16_t containerBits = 0;
uint16_t validBits = 0;
uint16_t blockAlign = 0;
AudioSampleFormat sampleFormat = AudioSampleFormat::UNKNOWN;
uint32_t sampleRate = 0;
uint32_t channelMask = 0;
uint16_t channels = 0;
uint16_t containerBits = 0;
uint16_t validBits = 0;
uint16_t blockAlign = 0;
};
struct AudioStreamStatus

View File

@@ -50,6 +50,16 @@ namespace
"audio packet limits differ");
static_assert(AUDIO_MAX_PCM_BYTES == KVMFR_AUDIO_PCM_BYTES,
"audio PCM limits differ");
static_assert(
static_cast<uint32_t>(AudioSampleFormat::S16_LE) ==
KVMFR_AUDIO_SAMPLE_S16_LE &&
static_cast<uint32_t>(AudioSampleFormat::S24_LE) ==
KVMFR_AUDIO_SAMPLE_S24_LE &&
static_cast<uint32_t>(AudioSampleFormat::S32_LE) ==
KVMFR_AUDIO_SAMPLE_S32_LE &&
static_cast<uint32_t>(AudioSampleFormat::F32_LE) ==
KVMFR_AUDIO_SAMPLE_F32_LE,
"audio sample formats differ");
static_assert(static_cast<uint32_t>(AUDIO_CAP_PLAYBACK) ==
static_cast<uint32_t>(KVMFR_AUDIO_CAP_PLAYBACK) &&
static_cast<uint32_t>(AUDIO_CAP_CAPTURE) ==
@@ -75,7 +85,7 @@ namespace
KVMFR_AUDIO_MESSAGE_CLOCK_VALID |
KVMFR_AUDIO_MESSAGE_CLOCK_STABLE;
constexpr size_t AUDIO_SHARED_MEMORY_PREFLIGHT =
2U * 1024U * 1024U;
3U * 1024U * 1024U;
bool ArmAudioPollTimer(HANDLE timer, uint32_t waitUs)
{
@@ -122,7 +132,8 @@ CLGMPAudioTransport::~CLGMPAudioTransport()
bool CLGMPAudioTransport::SameFormat(
const AudioFormat& left, const AudioFormat& right)
{
return left.sampleRate == right.sampleRate &&
return left.sampleFormat == right.sampleFormat &&
left.sampleRate == right.sampleRate &&
left.channelMask == right.channelMask &&
left.channels == right.channels &&
left.containerBits == right.containerBits &&
@@ -152,11 +163,29 @@ bool CLGMPAudioTransport::ValidFormat(const AudioFormat& format)
ChannelCount(format.channelMask) != format.channels)
return false;
const bool s16 = format.containerBits == 16 && format.validBits == 16;
const bool s24 = format.containerBits == 24 && format.validBits == 24;
bool validSampleFormat = false;
switch (format.sampleFormat)
{
case AudioSampleFormat::S16_LE:
validSampleFormat =
format.containerBits == 16 && format.validBits == 16;
break;
case AudioSampleFormat::S24_LE:
validSampleFormat =
format.containerBits == 24 && format.validBits == 24;
break;
case AudioSampleFormat::S32_LE:
case AudioSampleFormat::F32_LE:
validSampleFormat =
format.containerBits == 32 && format.validBits == 32;
break;
default:
return false;
}
const uint16_t bytes = static_cast<uint16_t>(
format.channels * (format.containerBits / 8U));
return (s16 || s24) && format.blockAlign == bytes;
return validSampleFormat && format.blockAlign == bytes;
}
KVMFRAudioFormat CLGMPAudioTransport::WireFormat(
@@ -165,8 +194,8 @@ KVMFRAudioFormat CLGMPAudioTransport::WireFormat(
KVMFRAudioFormat result = {};
result.version = KVMFR_AUDIO_FORMAT_VERSION;
result.size = sizeof(result);
result.sampleFormat = format.containerBits == 16 ?
KVMFR_AUDIO_SAMPLE_S16_LE : KVMFR_AUDIO_SAMPLE_S24_LE;
result.sampleFormat =
static_cast<uint32_t>(format.sampleFormat);
result.sampleRate = format.sampleRate;
result.channelMask = format.channelMask;
result.channelCount = format.channels;