diff --git a/client/transports/LGMP/audio.c b/client/transports/LGMP/audio.c index 391bc58e..c7c41a5d 100644 --- a/client/transports/LGMP/audio.c +++ b/client/transports/LGMP/audio.c @@ -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; } diff --git a/common/include/common/KVMFRAudio.h b/common/include/common/KVMFRAudio.h index 48f872d9..994e03e0 100644 --- a/common/include/common/KVMFRAudio.h +++ b/common/include/common/KVMFRAudio.h @@ -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; diff --git a/idd/LGAudio/BrokerDevice.cpp b/idd/LGAudio/BrokerDevice.cpp index b874855d..f9717efa 100644 --- a/idd/LGAudio/BrokerDevice.cpp +++ b/idd/LGAudio/BrokerDevice.cpp @@ -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) diff --git a/idd/LGAudio/Formats.cpp b/idd/LGAudio/Formats.cpp index 6ae4af8c..8c1bf868 100644 --- a/idd/LGAudio/Formats.cpp +++ b/idd/LGAudio/Formats.cpp @@ -27,13 +27,19 @@ namespace LGAudio constexpr USHORT kExtensibleExtraSize = static_cast( 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(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(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 && diff --git a/idd/LGAudio/Formats.h b/idd/LGAudio/Formats.h index ca734fc3..67d28785 100644 --- a/idd/LGAudio/Formats.h +++ b/idd/LGAudio/Formats.h @@ -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; diff --git a/idd/LGAudio/MiniportWaveRT.cpp b/idd/LGAudio/MiniportWaveRT.cpp index 96fd8a3a..f235ef91 100644 --- a/idd/LGAudio/MiniportWaveRT.cpp +++ b/idd/LGAudio/MiniportWaveRT.cpp @@ -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) { diff --git a/idd/LGAudio/MiniportWaveRTStream.cpp b/idd/LGAudio/MiniportWaveRTStream.cpp index f3108607..52fd5db3 100644 --- a/idd/LGAudio/MiniportWaveRTStream.cpp +++ b/idd/LGAudio/MiniportWaveRTStream.cpp @@ -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(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(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(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(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(context); + auto *stream = static_cast(context); if (!stream) return; diff --git a/idd/LGAudio/MiniportWaveRTStream.h b/idd/LGAudio/MiniportWaveRTStream.h index 0cca94ac..b424a119 100644 --- a/idd/LGAudio/MiniportWaveRTStream.h +++ b/idd/LGAudio/MiniportWaveRTStream.h @@ -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); diff --git a/idd/LGAudio/RingBuffer.cpp b/idd/LGAudio/RingBuffer.cpp index 5abfa5ad..55e8875b 100644 --- a/idd/LGAudio/RingBuffer.cpp +++ b/idd/LGAudio/RingBuffer.cpp @@ -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 || diff --git a/idd/LGCommon/LGAudioBroker.h b/idd/LGCommon/LGAudioBroker.h index bf89bf31..e30fa9ff 100644 --- a/idd/LGCommon/LGAudioBroker.h +++ b/idd/LGCommon/LGAudioBroker.h @@ -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"); diff --git a/idd/LGIdd/audio/CLGAudioDevice.cpp b/idd/LGIdd/audio/CLGAudioDevice.cpp index 293ebb9d..7270accf 100644 --- a/idd/LGIdd/audio/CLGAudioDevice.cpp +++ b/idd/LGIdd/audio/CLGAudioDevice.cpp @@ -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; diff --git a/idd/LGIdd/transport/IAudioSource.h b/idd/LGIdd/transport/IAudioSource.h index 2c4931bd..47cbf5c2 100644 --- a/idd/LGIdd/transport/IAudioSource.h +++ b/idd/LGIdd/transport/IAudioSource.h @@ -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 diff --git a/idd/LGIdd/transport/lgmp/CLGMPAudioTransport.cpp b/idd/LGIdd/transport/lgmp/CLGMPAudioTransport.cpp index 1a001b50..3f306047 100644 --- a/idd/LGIdd/transport/lgmp/CLGMPAudioTransport.cpp +++ b/idd/LGIdd/transport/lgmp/CLGMPAudioTransport.cpp @@ -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(AudioSampleFormat::S16_LE) == + KVMFR_AUDIO_SAMPLE_S16_LE && + static_cast(AudioSampleFormat::S24_LE) == + KVMFR_AUDIO_SAMPLE_S24_LE && + static_cast(AudioSampleFormat::S32_LE) == + KVMFR_AUDIO_SAMPLE_S32_LE && + static_cast(AudioSampleFormat::F32_LE) == + KVMFR_AUDIO_SAMPLE_F32_LE, + "audio sample formats differ"); static_assert(static_cast(AUDIO_CAP_PLAYBACK) == static_cast(KVMFR_AUDIO_CAP_PLAYBACK) && static_cast(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( 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(format.sampleFormat); result.sampleRate = format.sampleRate; result.channelMask = format.channelMask; result.channelCount = format.channels;