Documents LC3 Plus feature

This commit is contained in:
Antoine Soulier
2023-12-28 10:14:57 -08:00
parent 6c99421bac
commit 125c682696
5 changed files with 136 additions and 69 deletions

View File

@@ -1,8 +1,15 @@
# Low Complexity Communication Codec (LC3)
The LC3 is an efficient low latency audio codec.
LC3 and LC3 Plus are audio codecs designed for low-latency audio transport.
[_Low Complexity Communication Codec_](https://www.bluetooth.org/DocMan/handlers/DownloadDoc.ashx?doc_id=502107&vId=542963)
- LC3 is specified by [_the Bluetooth Special Interset Group for the LE Audio
protocol_](https://www.bluetooth.org/DocMan/handlers/DownloadDoc.ashx?doc_id=502107&vId=542963)
- LC3 Plus is defined by [_ETSI TS 103 634_](https://www.etsi.org/deliver/etsi_ts/103600_103699/103634/01.04.01_60/ts_103634v010401p.pdf)
In addition to LC3, following features of LC3 Plus are proposed:
- Frame duration of 2.5 and 5ms.
- High-Resolution mode, 48 KHz, and 96 kHz sampling rates.
## Overview
@@ -10,7 +17,7 @@ The directory layout is as follows :
- include: Library interface
- src: Source files
- tools: Standalone encoder/decoder tools
- test: Python implentation, used as reference for unit testing
- test: Unit testing framework
- fuzz: Roundtrip fuzz testing harness
- build: Building outputs
- bin: Compilation output
@@ -25,6 +32,16 @@ $ make -j
Compiled library `liblc3.a` will be found in `bin` directory.
LC3 Plus features can be selectively disabled :
- `LC3_PLUS=0` disable the support of 2.5ms and 5ms frame durations.
- `LC3_PLUS_HR=0` turns off the support of the High-Resolution mode.
Only Bluetooth LC3 features will be included using the following command:
```sh
$ make LC3_PLUS=0 LC3_PLUS_HR=0 -j
```
#### Cross compilation
The cc, as, ld and ar can be selected with respective Makefile variables `CC`,
@@ -77,7 +94,7 @@ $ ./elc3 <in.wav> -b <bitrate> | ./dlc3 | aplay
A python implementation of the encoder is provided in `test` diretory.
The C implementation is unitary validated against this implementation and
intermediate values given in Appendix C of the specification.
intermediate values given in Appendix C of the LC3 specification.
#### Prerequisite

View File

@@ -20,11 +20,15 @@
* Low Complexity Communication Codec (LC3)
*
* This implementation conforms to :
* Low Complexity Communication Codec (LC3)
* Bluetooth Specification v1.0
*
* - Low Complexity Communication Codec (LC3)
* Bluetooth Specification v1.0
*
* The LC3 is an efficient low latency audio codec.
* - ETSI TS 103 634 v1.4.1
* Digital Enhanced Cordless Telecommunications (DECT)
* Low Complexity Communication Codec plus (LC3plus)
*
* LC3 and LC3 Plus are audio codecs designed for low-latency audio transport.
*
* - Unlike most other codecs, the LC3 codec is focused on audio streaming
* in constrained (on packet sizes and interval) tranport layer.
@@ -40,19 +44,65 @@
*
* - Unlike classic codecs, the LC3 codecs does not run on fixed amount
* of samples as input. It operates only on fixed frame duration, for
* any supported samplerates (8 to 48 KHz). Two frames duration are
* any supported sample rates (8 to 48 KHz). Two frames duration are
* available 7.5ms and 10ms.
*
*
* --- About 44.1 KHz samplerate ---
* --- LC3 Plus features ---
*
* The Bluetooth specification reference the 44.1 KHz samplerate, although
* there is no support in the core algorithm of the codec of 44.1 KHz.
* We can summarize the 44.1 KHz support by "you can put any samplerate
* around the defined base samplerates". Please mind the following items :
* In addition to LC3, following features of LC3 Plus are proposed:
* - Frame duration of 2.5 and 5ms.
* - High-Resolution mode, 48 KHz, and 96 kHz sampling rates.
*
* 1. The frame size will not be 7.5 ms or 10 ms, but is scaled
* by 'supported samplerate' / 'input samplerate'
* The distinction between LC3 and LC3 plus is made according to :
*
* Frame Duration | 2.5ms | 5ms | 7.5ms | 10 ms |
* ---------------- | ----- | ----- | ----- | ----- |
* LC3 | | | X | X |
* LC3 Plus | X | X | | X |
*
* The 10 ms frame duration is available in LC3 and LC3 plus standard.
* In this mode, the produced bitstream can be referenced either
* as LC3 or LC3 plus.
*
* The LC3 Plus high-resolution mode should be preferred at high bitrates
* and larger audio bandwidth. In this mode, the audio bandwidth is always
* up to the Nyquist frequency, compared to LC3 at 48 KHz, which limits
* the bandwidth to 20 KHz.
*
*
* --- Bit rate ---
*
* The proposed implementation accepts any frame sizes between 20 and 400 Bytes
* for non-high-resolution mode. Mind that the LC3 Plus standard defines
* smaller sizes for frame durations shorter than 10 ms and/or sampling rates
* less than 48 kHz.
*
* In High-Resolution mode, the frame sizes (and bitrates) are restricted
* as follows:
*
* HR Configuration | Frame sizes | Bitrate (kbps) |
* ------------------ | ------------- | -------------- |
* 10 ms - 48 KHz | 156 to 625 | 124.8 - 500 |
* 10 ms - 96 KHz | 187 to 625 | 149.6 - 500 |
* ------------------ | ------------- | -------------- |
* 5 ms - 48 KHz | 93 to 375 | 148.8 - 600 |
* 5 ms - 96 KHz | 109 to 375 | 174.4 - 600 |
* ------------------ | ------------- | -------------- |
* 2.5 ms - 48 KHz | 54 to 210 | 172.8 - 672 |
* 2.5 ms - 96 KHz | 62 to 210 | 198.4 - 672 |
*
*
* --- About 44.1 KHz sample rate ---
*
* The Bluetooth specification and the ETSI TS 103 634 standard references
* the 44.1 KHz sample rate, although there is no support in the core algorithm
* of the codec.
* We can summarize the 44.1 KHz support by "You can put any sample rate around
* the defined base sample rates." Please mind the following items :
*
* 1. The frame size will not be 2.5ms, 5ms, 7.5 ms or 10 ms, but is scaled
* by 'supported sample rate' / 'input sample rate'
*
* 2. The bandwidth will be hard limited (to 20 KHz) if you select 48 KHz.
* The encoded bandwidth will also be affected by the above inverse
@@ -84,8 +134,8 @@
* space must be aligned to a pointer size. As an example, you can setup
* encoder like this :
*
* | enc = lc3_setup_encoder(frame_us, samplerate,
* | malloc(lc3_encoder_size(frame_us, samplerate)));
* | enc = lc3_setup_encoder(frame_us, sample rate,
* | malloc(lc3_encoder_size(frame_us, sample rate)));
* | ...
* | free(enc);
*
@@ -124,31 +174,31 @@ extern "C" {
/**
* Limitations
* - On the bitrate, in bps (48 KHz, 10ms frame size)
* - On the bitrate, in bps
* - On the size of the frames in bytes
* - On the number of samples by frames
*/
#define LC3_MIN_BITRATE 16000
#define LC3_MAX_BITRATE 320000
#define LC3_MAX_HR_BITRATE 500000
#define LC3_HR_MAX_BITRATE 672000
#define LC3_MIN_FRAME_BYTES 20
#define LC3_MAX_FRAME_BYTES 400
#define LC3_MAX_HR_FRAME_BYTES 625
#define LC3_HR_MAX_FRAME_BYTES 625
#define LC3_MIN_FRAME_SAMPLES LC3_NS( 2500, 8000)
#define LC3_MAX_FRAME_SAMPLES LC3_NS(10000, 48000)
#define LC3_MAX_HR_FRAME_SAMPLES LC3_NS(10000, 96000)
#define LC3_HR_MAX_FRAME_SAMPLES LC3_NS(10000, 96000)
/**
* Parameters check
* LC3_CHECK_DT_US(us) True when frame duration in us is suitable
* LC3_CHECK_SR_HZ(sr) True when samplerate in Hz is suitable
* LC3_CHECK_SR_HZ(sr) True when sample rate in Hz is suitable
*
* LC3_CHECK_HR_SR_HZ(hrmode, sr)
* True when samplerate in Hz is suitable, according to the
* LC3_HR_CHECK_SR_HZ(hrmode, sr)
* True when sample rate in Hz is suitable, according to the
* selection of the high-resolution mode `hrmode`.
*/
@@ -160,7 +210,7 @@ extern "C" {
( ((sr) == 8000) || ((sr) == 16000) || ((sr) == 24000) || \
((sr) == 32000) || ((sr) == 48000) )
#define LC3_CHECK_HR_SR_HZ(hrmode, sr) \
#define LC3_HR_CHECK_SR_HZ(hrmode, sr) \
( (hrmode) ? ((sr) == 48000) || ((sr) == 96000) : LC3_CHECK_SR_HZ(sr) )
@@ -190,10 +240,10 @@ typedef struct lc3_decoder *lc3_decoder_t;
/**
* Static memory of encoder context
* Static memory of encoder/decoder contexts
*
* Propose types suitable for static memory allocation, supporting
* any frame duration, and maximum samplerates 16k and 48k respectively
* any frame duration, and maximum sample rates 16k and 48k respectively
* You can customize your type using the `LC3_ENCODER_MEM_T` or
* `LC3_DECODER_MEM_T` macro.
*/
@@ -207,9 +257,9 @@ typedef LC3_DECODER_MEM_T(10000, 48000) lc3_decoder_mem_48k_t;
/**
* Return the number of PCM samples in a frame
* hrmode Enable High-Resolution mode (48000 and 96000 samplerates)
* hrmode Enable High-Resolution mode (48000 and 96000 sample rates)
* dt_us Frame duration in us, 2500, 5000, 7500 or 10000
* sr_hz Samplerate in Hz, 8000, 16000, 24000, 32000, 48000 or 96000
* sr_hz Sample rate in Hz, 8000, 16000, 24000, 32000, 48000 or 96000
* return Number of PCM samples, -1 on bad parameters
*/
int lc3_hr_frame_samples(bool hrmode, int dt_us, int sr_hz);
@@ -218,9 +268,9 @@ int lc3_frame_samples(int dt_us, int sr_hz);
/**
* Return the size of frames, from bitrate
* hrmode Enable High-Resolution mode (48000 and 96000 samplerates)
* hrmode Enable High-Resolution mode (48000 and 96000 sample rates)
* dt_us Frame duration in us, 2500, 5000, 7500 or 10000
* sr_hz Samplerate in Hz, 8000, 16000, 24000, 32000, 48000 or 96000
* sr_hz Sample rate in Hz, 8000, 16000, 24000, 32000, 48000 or 96000
* bitrate Target bitrate in bit per second, 0 or `INT_MAX` returns
* respectively the minimum and maximum allowed size.
* return The floor size in bytes of the frames, -1 on bad parameters
@@ -231,9 +281,9 @@ int lc3_frame_bytes(int dt_us, int bitrate);
/**
* Resolve the bitrate, from the size of frames
* hrmode Enable High-Resolution mode (48000 and 96000 samplerates)
* hrmode Enable High-Resolution mode (48000 and 96000 sample rates)
* dt_us Frame duration in us, 2500, 5000, 7500 or 10000
* sr_hz Samplerate in Hz, 8000, 16000, 24000, 32000, 48000 or 96000
* sr_hz Sample rate in Hz, 8000, 16000, 24000, 32000, 48000 or 96000
* nbytes Size in bytes of the frames, 0 or `INT_MAX` returns
* respectively the minimum and maximum allowed bitrate.
* return The according bitrate in bps, -1 on bad parameters
@@ -244,9 +294,9 @@ int lc3_resolve_bitrate(int dt_us, int nbytes);
/**
* Return algorithmic delay, as a number of samples
* hrmode Enable High-Resolution mode (48000 and 96000 samplerates)
* hrmode Enable High-Resolution mode (48000 and 96000 sample rates)
* dt_us Frame duration in us, 2500, 5000, 7500 or 10000
* sr_hz Samplerate in Hz, 8000, 16000, 24000, 32000, 48000 or 96000
* sr_hz Sample rate in Hz, 8000, 16000, 24000, 32000, 48000 or 96000
* return Number of algorithmic delay samples, -1 on bad parameters
*/
int lc3_hr_delay_samples(bool hrmode, int dt_us, int sr_hz);
@@ -255,12 +305,12 @@ int lc3_delay_samples(int dt_us, int sr_hz);
/**
* Return size needed for an encoder
* hrmode Enable High-Resolution mode (48000 and 96000 samplerates)
* hrmode Enable High-Resolution mode (48000 and 96000 sample rates)
* dt_us Frame duration in us, 2500, 5000, 7500 or 10000
* sr_hz Samplerate in Hz, 8000, 16000, 24000, 32000, 48000 or 96000
* sr_hz Sample rate in Hz, 8000, 16000, 24000, 32000, 48000 or 96000
* return Size of then encoder in bytes, 0 on bad parameters
*
* The `sr_hz` parameter is the samplerate of the PCM input stream,
* The `sr_hz` parameter is the sample rate of the PCM input stream,
* and will match `sr_pcm_hz` of `lc3_hr_setup_encoder()`.
*/
unsigned lc3_hr_encoder_size(bool hrmode, int dt_us, int sr_hz);
@@ -269,18 +319,18 @@ unsigned lc3_encoder_size(int dt_us, int sr_hz);
/**
* Setup encoder
* hrmode Enable High-Resolution mode (48000 and 96000 samplerates)
* hrmode Enable High-Resolution mode (48000 and 96000 sample rates)
* dt_us Frame duration in us, 2500, 5000, 7500 or 10000
* sr_hz Samplerate in Hz, 8000, 16000, 24000, 32000, 48000 or 96000
* sr_pcm_hz Input samplerate, downsampling option of input, or 0
* sr_hz Sample rate in Hz, 8000, 16000, 24000, 32000, 48000 or 96000
* sr_pcm_hz Input sample rate, downsampling option of input, or 0
* mem Encoder memory space, aligned to pointer type
* return Encoder as an handle, NULL on bad parameters
*
* The `sr_pcm_hz` parameter is a downsampling option of PCM input,
* the value `0` fallback to the samplerate of the encoded stream `sr_hz`.
* the value `0` fallback to the sample rate of the encoded stream `sr_hz`.
* When used, `sr_pcm_hz` is intended to be higher or equal to the encoder
* samplerate `sr_hz`. The size of the context needed, given by
* `lc3_encoder_size()` will be set accordingly to `sr_pcm_hz`.
* sample rate `sr_hz`. The size of the context needed, given by
* `lc3_hr_encoder_size()` will be set accordingly to `sr_pcm_hz`.
*/
lc3_encoder_t lc3_hr_setup_encoder(bool hrmode,
int dt_us, int sr_hz, int sr_pcm_hz, void *mem);
@@ -293,7 +343,7 @@ lc3_encoder_t lc3_setup_encoder(
* encoder Handle of the encoder
* fmt PCM input format
* pcm, stride Input PCM samples, and count between two consecutives
* nbytes Target size, in bytes, of the frame (20 to 400)
* nbytes Target size, in bytes, of the frame
* out Output buffer of `nbytes` size
* return 0: On success -1: Wrong parameters
*/
@@ -302,13 +352,13 @@ int lc3_encode(lc3_encoder_t encoder, enum lc3_pcm_format fmt,
/**
* Return size needed for an decoder
* hrmode Enable High-Resolution mode (48000 and 96000 samplerates)
* hrmode Enable High-Resolution mode (48000 and 96000 sample rates)
* dt_us Frame duration in us, 2500, 5000, 7500 or 10000
* sr_hz Samplerate in Hz, 8000, 16000, 24000, 32000, 48000 or 96000
* sr_hz Sample rate in Hz, 8000, 16000, 24000, 32000, 48000 or 96000
* return Size of then decoder in bytes, 0 on bad parameters
*
* The `sr_hz` parameter is the samplerate of the PCM output stream,
* and will match `sr_pcm_hz` of `lc3_setup_decoder()`.
* The `sr_hz` parameter is the sample rate of the PCM output stream,
* and will match `sr_pcm_hz` of `lc3_hr_setup_decoder()`.
*/
unsigned lc3_hr_decoder_size(bool hrmode, int dt_us, int sr_hz);
@@ -316,18 +366,18 @@ unsigned lc3_decoder_size(int dt_us, int sr_hz);
/**
* Setup decoder
* hrmode Enable High-Resolution mode (48000 and 96000 samplerates)
* hrmode Enable High-Resolution mode (48000 and 96000 sample rates)
* dt_us Frame duration in us, 2500, 5000, 7500 or 10000
* sr_hz Samplerate in Hz, 8000, 16000, 24000, 32000, 48000 or 96000
* sr_pcm_hz Output samplerate, upsampling option of output (or 0)
* sr_hz Sample rate in Hz, 8000, 16000, 24000, 32000, 48000 or 96000
* sr_pcm_hz Output sample rate, upsampling option of output (or 0)
* mem Decoder memory space, aligned to pointer type
* return Decoder as an handle, NULL on bad parameters
*
* The `sr_pcm_hz` parameter is an upsampling option of PCM output,
* the value `0` fallback to the samplerate of the decoded stream `sr_hz`.
* the value `0` fallback to the sample rate of the decoded stream `sr_hz`.
* When used, `sr_pcm_hz` is intended to be higher or equal to the decoder
* samplerate `sr_hz`. The size of the context needed, given by
* `lc3_decoder_size()` will be set accordingly to `sr_pcm_hz`.
* sample rate `sr_hz`. The size of the context needed, given by
* `lc3_hr_decoder_size()` will be set accordingly to `sr_pcm_hz`.
*/
lc3_decoder_t lc3_hr_setup_decoder(bool hrmode,
int dt_us, int sr_hz, int sr_pcm_hz, void *mem);

View File

@@ -108,14 +108,14 @@ class Encoder : public Base<struct lc3_encoder> {
// Encoder construction / destruction
//
// The frame duration `dt_us` is 2500, 5000, 7500 or 10000 us.
// The samplerate `sr_hz` is 8000, 16000, 24000, 32000 or 48000 Hz.
// The sample rate `sr_hz` is 8000, 16000, 24000, 32000 or 48000 Hz.
// The `hrmode` flag enables the high-resolution mode, in which case
// the sample rate is 48000 or 96000 Hz.
//
// The `sr_pcm_hz` parameter is a downsampling option of PCM input,
// the value 0 fallback to the samplerate of the encoded stream `sr_hz`.
// the value 0 fallback to the sample rate of the encoded stream `sr_hz`.
// When used, `sr_pcm_hz` is intended to be higher or equal to the encoder
// samplerate `sr_hz`.
// sample rate `sr_hz`.
Encoder(int dt_us, int sr_hz, int sr_pcm_hz = 0,
size_t nchannels = 1, bool hrmode = false)
@@ -209,14 +209,14 @@ class Decoder : public Base<struct lc3_decoder> {
// Decoder construction / destruction
//
// The frame duration `dt_us` is 2500, 5000, 7500 or 10000 us.
// The samplerate `sr_hz` is 8000, 16000, 24000, 32000 or 48000 Hz.
// The sample rate `sr_hz` is 8000, 16000, 24000, 32000 or 48000 Hz.
// The `hrmode` flag enables the high-resolution mode, in which case
// the sample rate is 48000 or 96000 Hz.
//
// The `sr_pcm_hz` parameter is an downsampling option of PCM output,
// the value 0 fallback to the samplerate of the decoded stream `sr_hz`.
// the value 0 fallback to the sample rate of the decoded stream `sr_hz`.
// When used, `sr_pcm_hz` is intended to be higher or equal to the decoder
// samplerate `sr_hz`.
// sample rate `sr_hz`.
Decoder(int dt_us, int sr_hz, int sr_pcm_hz = 0,
size_t nchannels = 1, bool hrmode = false)

View File

@@ -171,10 +171,10 @@ int main(int argc, char *argv[])
if (!LC3_CHECK_DT_US(frame_us))
error(EINVAL, "Frame duration");
if (!LC3_CHECK_HR_SR_HZ(hrmode, srate_hz))
if (!LC3_HR_CHECK_SR_HZ(hrmode, srate_hz))
error(EINVAL, "Samplerate %d Hz", srate_hz);
if (p.srate_hz && (!LC3_CHECK_HR_SR_HZ(hrmode, p.srate_hz) ||
if (p.srate_hz && (!LC3_HR_CHECK_SR_HZ(hrmode, p.srate_hz) ||
p.srate_hz < srate_hz ))
error(EINVAL, "Output samplerate %d Hz", p.srate_hz);
@@ -190,8 +190,8 @@ int main(int argc, char *argv[])
/* --- Setup decoding --- */
uint8_t in[2 * LC3_MAX_HR_FRAME_BYTES];
int8_t alignas(int32_t) pcm[2 * LC3_MAX_HR_FRAME_SAMPLES*4];
uint8_t in[2 * LC3_HR_MAX_FRAME_BYTES];
int8_t alignas(int32_t) pcm[2 * LC3_HR_MAX_FRAME_SAMPLES*4];
lc3_decoder_t dec[2];
int frame_samples = lc3_hr_frame_samples(hrmode, frame_us, pcm_srate_hz);

View File

@@ -171,7 +171,7 @@ int main(int argc, char *argv[])
if (!LC3_CHECK_DT_US(frame_us))
error(EINVAL, "Frame duration");
if (!LC3_CHECK_HR_SR_HZ(p.hrmode, srate_hz))
if (!LC3_HR_CHECK_SR_HZ(p.hrmode, srate_hz))
error(EINVAL, "Samplerate %d Hz", srate_hz);
if (pcm_sbits != 16 && pcm_sbits != 24)
@@ -184,7 +184,7 @@ int main(int argc, char *argv[])
if (nch < 1 || nch > 2)
error(EINVAL, "Number of channels %d", nch);
if (p.srate_hz && (!LC3_CHECK_HR_SR_HZ(p.hrmode, p.srate_hz) ||
if (p.srate_hz && (!LC3_HR_CHECK_SR_HZ(p.hrmode, p.srate_hz) ||
p.srate_hz > srate_hz ))
error(EINVAL, "Encoder samplerate %d Hz", p.srate_hz);
@@ -198,8 +198,8 @@ int main(int argc, char *argv[])
/* --- Setup encoding --- */
int8_t alignas(int32_t) pcm[2 * LC3_MAX_HR_FRAME_SAMPLES*4];
uint8_t out[2 * LC3_MAX_HR_FRAME_BYTES];
int8_t alignas(int32_t) pcm[2 * LC3_HR_MAX_FRAME_SAMPLES*4];
uint8_t out[2 * LC3_HR_MAX_FRAME_BYTES];
lc3_encoder_t enc[2];
int frame_bytes = lc3_hr_frame_bytes(