[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

@@ -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;