BLE audio course – Audio Location & Channel Allocation

BLE audio course – Audio Location & Channel Allocation
Spatial audio routing · LC3 multiplexing · Media Packet structure
4-byte
Location Bitfield
LC3
Single Channel Codec
5.1+
Surround Channels

Previous Bluetooth audio sent mono or stereo to a single sink and left it to figure out rendering. BLE Audio optimises this by telling each sink which spatial channel it should render, using Audio Location. Because LC3 is a single-channel codec, multiple channels are multiplexed into one packet via Channel Allocation — enabling true surround sound over Bluetooth.

1. Audio Location — Spatial Routing

4-Octet Bitfield — Based on CTA-861-G Speaker Placement

Each Acceptor declares which spatial channel(s) it renders. This allows the Initiator to send only the relevant audio stream to each sink — the left earbud only receives the left channel, saving battery on both devices.

Common Audio Location Values (PACS — Bluetooth Assigned Numbers)
Audio Location Bitmap Value Bit Position Typical Device
Front Left 0x00000001 Bit 1 Left earbud / left hearing aid
Front Right 0x00000002 Bit 2 Right earbud / right hearing aid
Front Centre 0x00000004 Bit 3 Centre soundbar / headphone (stereo folded)
LFE / Front Woofer 0x00000008 Bit 4 Subwoofer
Back Left 0x00000010 Bit 5 Rear-left surround speaker
Back Right 0x00000010 Bit 6 Rear-right surround speaker
Prohibited 0x00000000 All-zero is NOT allowed — every Acceptor must declare at least one location

Optimised Stereo Routing to Two Earbuds
📱
Phone (Initiator)
Stereo audio source
CIS 0 → Front Left only (0x01)
↗↘
CIS 1 → Front Right only (0x02)
🎧
Left Earbud
Audio_Location = 0x01
Receives LEFT only
Receiver ON less = battery saved
🎧
Right Earbud
Audio_Location = 0x02
Receives RIGHT only
No wasted decoding
Mono ≠ a Location: Mono is a property of the audio stream, not the physical device. A single-channel speaker sets both Front Left (0x01) AND Front Right (0x02) in its Audio Location, signalling it can downmix stereo to mono.

2. Why Channel Allocation Is Needed (LC3 Is Single-Channel)

LC3 vs SBC — Different Stereo Handling
SBC (Classic) vs LC3 (BLE Audio) Stereo Strategy
SBC (Bluetooth Classic)
Supports Joint Stereo — encodes difference between L and R channels. Efficient because L and R are often highly correlated.

But: max 2 channels. No surround. Codec locked to stereo or mono concepts.

LC3 (BLE Audio) — Single-Channel Codec
Encodes each channel independently. Frames concatenated in one PDU.

LC3 is so efficient that joint stereo offers minimal advantage over two separate streams. Benefit: supports any number of channels — 2.0, 5.1, 7.1+ — all multiplexed into one isochronous PDU using Channel Allocation.

3. Media Packet — Multiplexing Multiple LC3 Frames

5.1 Surround Sound Example — One PDU Carries All Channels

LC3 frames for all channels are arranged in ascending order of Audio Location bit value and packed into a single Media Packet, which rides inside one isochronous PDU.

5-Channel Surround: LC3 Encoding → Channel Allocation → Single PDU

Front Left
Front Right
Front Centre
Back Left
Back Right
LC3
Encoder
(×5)

MEDIA PACKET (single Isochronous PDU)
CF_1
Front
Left
0x01
CF_2
Front
Right
0x02
CF_3
Front
Centre
0x04
CF_4
Back
Left
0x10
CF_5
Back
Right
0x20
Frames sorted in ascending Audio Location bit order — no header needed

Air
Audio
Stream
⚠️ Multiple Blocks Warning: A Media Packet can hold multiple blocks of frames (e.g., CF_N1 + CF_N2 for two sample periods). But this makes packets bigger → more vulnerable to interference, and adds latency since the controller must wait for all samples before transmitting. Avoid multiple blocks in general audio use cases.
No header in the Media Packet: Channel count is configured via Audio_Channel_Location LTV in PAC records. Block count is set via Codec_Frame_Blocks_Per_SDU LTV. Both are negotiated at stream setup — the receiver knows the layout before the first packet arrives.

4. BlueZ: PAC Record — Audio Location & Channel Allocation Config

Codec Specific Capabilities LTV Structure in BlueZ (C)
/* Audio Location values from BT Generic Audio Assigned Numbers */ #define AUDIO_LOC_FRONT_LEFT 0x00000001 #define AUDIO_LOC_FRONT_RIGHT 0x00000002 #define AUDIO_LOC_FRONT_CENTRE 0x00000004 #define AUDIO_LOC_BACK_LEFT 0x00000010 #define AUDIO_LOC_BACK_RIGHT 0x00000020 #define AUDIO_LOC_MONO (AUDIO_LOC_FRONT_LEFT | AUDIO_LOC_FRONT_RIGHT) /* * PAC Record: Codec Specific Capabilities LTV * Sent via PACS characteristic to advertise what the sink supports. * * LTV format: [Length][Type][Value…] * * Type 0x01 = Supported Sampling Frequencies (bitmask) * Type 0x02 = Supported Frame Durations (bitmask) * Type 0x03 = Supported Audio Channel Counts * Type 0x04 = Supported Octets per Codec Frame (min + max) * Type 0x05 = Supported Max Codec Frames per SDU */ struct bt_pac_ltv { uint8_t len; uint8_t type; uint8_t value[0]; } __attribute__((packed)); /* Example: Left earbud PAC record capabilities */ static const uint8_t left_earbud_caps[] = { /* Type 0x01: Sampling Freq — supports 16kHz (0x0008) and 48kHz (0x0800) */ 0x03, 0x01, 0x08, 0x08, /* Type 0x02: Frame Duration — 7.5ms (bit0) and 10ms (bit1) */ 0x02, 0x02, 0x03, /* Type 0x03: Audio Channel Counts — 1 channel (bit0 set) */ 0x02, 0x03, 0x01, /* Type 0x04: Octets per Codec Frame — min=40 (0x0028), max=120 (0x0078) */ 0x05, 0x04, 0x28, 0x00, 0x78, 0x00, /* Type 0x05: Max Codec Frames per SDU = 1 */ 0x02, 0x05, 0x01, }; /* PAC Record Metadata LTV */ /* Type 0x03 = Audio Location (4 bytes) */ static const uint8_t left_earbud_metadata[] = { /* Audio_Channel_Location = Front Left (0x00000001) */ 0x05, 0x03, (AUDIO_LOC_FRONT_LEFT >> 0) & 0xFF, (AUDIO_LOC_FRONT_LEFT >> 8) & 0xFF, (AUDIO_LOC_FRONT_LEFT >> 16) & 0xFF, (AUDIO_LOC_FRONT_LEFT >> 24) & 0xFF, /* Preferred Audio Contexts = Media | Conversational */ 0x03, 0x02, 0x06, 0x00, }; /* Codec Config LTV set by Initiator during CIS setup (BAP ASE Configure Codec) */ /* Type 0x03 = Audio Channel Allocation — tells sink which channel(s) in stream */ static const uint8_t ase_codec_config[] = { /* Sampling frequency: 48kHz = 0x08 */ 0x02, 0x01, 0x08, /* Frame Duration: 10ms = 0x01 */ 0x02, 0x02, 0x01, /* Audio Channel Allocation: Front Left = 0x00000001 */ 0x05, 0x03, 0x01, 0x00, 0x00, 0x00, /* Octets per Codec Frame: 120 = 0x0078 */ 0x03, 0x04, 0x78, 0x00, /* Codec Frames per SDU: 1 */ 0x02, 0x05, 0x01, };
How the receiver knows the channel layout without a header: The Initiator writes the Audio_Channel_Allocation LTV (type 0x03) into the ASE Codec Configuration during stream setup. The receiver parses this once and knows exactly which LC3 frame in every subsequent PDU corresponds to Front Left, Right, etc. — no per-packet parsing needed.
Key Takeaways
Audio Location = 4-byte bitfield — each Acceptor declares its spatial channel
LC3 is single-channel → Channel Allocation multiplexes multiple frames per PDU
Frames ordered by ascending Audio Location bit — no header needed
Mono = Front Left | Front Right in the bitfield (not a separate location)
Avoid multi-block packets in general audio — adds latency and packet fragility

Leave a Reply

Your email address will not be published. Required fields are marked *