Bluetooth: BAP: use codec configuration parameters for LC3 processing
Replace hardcoded LC3 codec config with the selected configuration used in the GATT profiles. The selected codec configuration on the client will be used on both sides for LC3 encode/decode. This required completing the LC3 macroes for building codec cababilities and codec configuration as they were not setting the max_frames_per_sdu and frames_per_sdu values. So as a side effect this change adds the capability to pack multiple audio frames into a single SDU - tested with 2 frames every 20ms. Signed-off-by: Casper Bonde <casper_bonde@bose.com>
This commit is contained in:
parent
4d6b0ee47d
commit
52843f7f9d
|
@ -18,6 +18,8 @@
|
|||
* @{
|
||||
*/
|
||||
|
||||
#include <sys/util_macro.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
@ -137,7 +139,6 @@ enum bt_codec_capability_type {
|
|||
#define BT_CODEC_LC3_CHAN_COUNT_SUPPORT(_count) ((uint8_t)BIT((_count) - 1))
|
||||
|
||||
|
||||
|
||||
struct bt_codec_lc3_frame_len {
|
||||
uint16_t min;
|
||||
uint16_t max;
|
||||
|
@ -166,7 +167,7 @@ enum bt_codec_config_type {
|
|||
/** @brief LC3 Frame Length configuration type. */
|
||||
BT_CODEC_CONFIG_LC3_FRAME_LEN = 0x04,
|
||||
|
||||
/** @brief Codec frames = blocks, per SDU configuration type. */
|
||||
/** @brief Codec frame blocks, per SDU configuration type. */
|
||||
BT_CODEC_CONFIG_LC3_FRAME_BLKS_PER_SDU = 0x05,
|
||||
};
|
||||
|
||||
|
@ -233,17 +234,26 @@ enum bt_codec_config_type {
|
|||
#define BT_CODEC_CONFIG_LC3_DURATION_10 0x01
|
||||
|
||||
|
||||
|
||||
/** @def BT_CODEC_LC3_DATA
|
||||
* @brief Helper to declare LC3 codec capability
|
||||
*
|
||||
* _max_frames_per_sdu value is optional and will be included only if != 1
|
||||
*/
|
||||
#define BT_CODEC_LC3_DATA(_freq, _duration, _chan_count, _len_min, _len_max) \
|
||||
/* COND_CODE_1 is used to omit an LTV entry in case the _frames_per_sdu is 1.
|
||||
* COND_CODE_1 will evaluate to second argument if the flag parameter(first argument) is 1
|
||||
* - removing one layer of paranteses.
|
||||
* If the flags argument is != 1 it will evaluate to the third argument which inserts a LTV
|
||||
* entry for the max_frames_per_sdu value.
|
||||
*/
|
||||
#define BT_CODEC_LC3_DATA(_freq, _duration, _chan_count, _len_min, _len_max, _max_frames_per_sdu) \
|
||||
{ \
|
||||
BT_CODEC_DATA(BT_CODEC_LC3_FREQ, (_freq) & 0xffu, (_freq) >> 8), \
|
||||
BT_CODEC_DATA(BT_CODEC_LC3_DURATION, _duration), \
|
||||
BT_CODEC_DATA(BT_CODEC_LC3_CHAN_COUNT, _chan_count), \
|
||||
BT_CODEC_DATA(BT_CODEC_LC3_FRAME_LEN, (_len_min) & 0xffu, (_len_min) >> 8, \
|
||||
(_len_max) & 0xffu, (_len_max) >> 8) \
|
||||
COND_CODE_1(_max_frames_per_sdu, (), \
|
||||
(, BT_CODEC_DATA(BT_CODEC_LC3_FRAME_COUNT, _max_frames_per_sdu))) \
|
||||
}
|
||||
|
||||
/** @def BT_CODEC_LC3_META
|
||||
|
@ -260,22 +270,32 @@ enum bt_codec_config_type {
|
|||
* @brief Helper to declare LC3 codec
|
||||
*/
|
||||
#define BT_CODEC_LC3(_freq, _duration, _chan_count, _len_min, _len_max, \
|
||||
_frames, _prefer_context, _context) \
|
||||
_max_frames_per_sdu, _prefer_context, _context) \
|
||||
BT_CODEC(BT_CODEC_LC3_ID, 0x0000, 0x0000, \
|
||||
BT_CODEC_LC3_DATA(_freq, _duration, _chan_count, _len_min, \
|
||||
_len_max), \
|
||||
_len_max, _max_frames_per_sdu), \
|
||||
BT_CODEC_LC3_META(_prefer_context, _context))
|
||||
|
||||
/** @def BT_CODEC_LC3_CONFIG_DATA
|
||||
* @brief Helper to declare LC3 codec data configuration
|
||||
*
|
||||
* _frame_blocks_per_sdu value is optional and will be included only if != 1
|
||||
*/
|
||||
#define BT_CODEC_LC3_CONFIG_DATA(_freq, _duration, _loc, _len) \
|
||||
/* COND_CODE_1 is used to omit an LTV entry in case the _frames_per_sdu is 1.
|
||||
* COND_CODE_1 will evaluate to second argument if the flag parameter(first argument) is 1
|
||||
* - removing one layer of paranteses.
|
||||
* If the flags argument is != 1 it will evaluare to the third argument which inserts a LTV
|
||||
* entry for the frames_per_sdu value.
|
||||
*/
|
||||
#define BT_CODEC_LC3_CONFIG_DATA(_freq, _duration, _loc, _len, _frame_blocks_per_sdu) \
|
||||
{ \
|
||||
BT_CODEC_DATA(BT_CODEC_CONFIG_LC3_FREQ, _freq), \
|
||||
BT_CODEC_DATA(BT_CODEC_CONFIG_LC3_DURATION, _duration), \
|
||||
BT_CODEC_DATA(BT_CODEC_CONFIG_LC3_CHAN_ALLOC, (_loc) & 0xffu, ((_loc) >> 8) & 0xffu, \
|
||||
((_loc) >> 16) & 0xffu, (_loc) >> 24), \
|
||||
BT_CODEC_DATA(BT_CODEC_CONFIG_LC3_FRAME_LEN, (_len) & 0xffu, (_len) >> 8) \
|
||||
COND_CODE_1(_frame_blocks_per_sdu, (), \
|
||||
(, BT_CODEC_DATA(BT_CODEC_CONFIG_LC3_FRAME_BLKS_PER_SDU, _frames_per_sdu))) \
|
||||
}
|
||||
|
||||
/** @def BT_CODEC_LC3_CONFIG_DATA
|
||||
|
@ -289,16 +309,16 @@ enum bt_codec_config_type {
|
|||
/** @def BT_CODEC_LC3_CONFIG_N
|
||||
* @brief Helper to declare LC3 codec configuration for multiple channels.
|
||||
*/
|
||||
#define BT_CODEC_LC3_CONFIG_N(_freq, _duration, _loc, _len, _context) \
|
||||
#define BT_CODEC_LC3_CONFIG_N(_freq, _duration, _loc, _len, _frames_per_sdu, _context) \
|
||||
BT_CODEC(BT_CODEC_LC3_ID, 0x0000, 0x0000, \
|
||||
BT_CODEC_LC3_CONFIG_DATA(_freq, _duration, _loc, _len), \
|
||||
BT_CODEC_LC3_CONFIG_DATA(_freq, _duration, _loc, _len, _frames_per_sdu), \
|
||||
BT_CODEC_LC3_CONFIG_META(_context))
|
||||
|
||||
/** @def BT_CODEC_LC3_CONFIG
|
||||
* @brief Helper to declare LC3 codec configuration
|
||||
* @brief Helper to declare LC3 codec configuration for left location and one frame per sdu
|
||||
*/
|
||||
#define BT_CODEC_LC3_CONFIG(_freq, _duration, _len, _context) \
|
||||
BT_CODEC_LC3_CONFIG_N(_freq, _duration, 0x000000000, _len, _context)
|
||||
BT_CODEC_LC3_CONFIG_N(_freq, _duration, BT_AUDIO_LOCATION_FRONT_LEFT, _len, 1, _context)
|
||||
|
||||
/** @def BT_CODEC_LC3_CONFIG_8_1
|
||||
* @brief Helper to declare LC3 8.1 codec configuration
|
||||
|
@ -436,9 +456,6 @@ enum bt_codec_config_type {
|
|||
#define BT_CODEC_LC3_QOS_10_UNFRAMED(_sdu, _rtn, _latency, _pd) \
|
||||
BT_CODEC_QOS_UNFRAMED(10000u, _sdu, _rtn, _latency, _pd)
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -21,14 +21,17 @@ static struct bt_conn *default_conn;
|
|||
static struct k_work_delayable audio_send_work;
|
||||
static struct bt_audio_stream audio_stream;
|
||||
static struct bt_audio_unicast_group *unicast_group;
|
||||
static struct bt_codec *remote_codecs[CONFIG_BT_AUDIO_UNICAST_CLIENT_PAC_COUNT];
|
||||
static struct bt_codec *remote_codec_capabilities[CONFIG_BT_AUDIO_UNICAST_CLIENT_PAC_COUNT];
|
||||
static struct bt_audio_ep *sinks[CONFIG_BT_AUDIO_UNICAST_CLIENT_ASE_SNK_COUNT];
|
||||
NET_BUF_POOL_FIXED_DEFINE(tx_pool, 1,
|
||||
CONFIG_BT_ISO_TX_MTU + BT_ISO_CHAN_SEND_RESERVE,
|
||||
8, NULL);
|
||||
|
||||
/* Mandatory support preset by both client and server */
|
||||
static struct bt_audio_lc3_preset preset_16_2_1 = BT_AUDIO_LC3_UNICAST_PRESET_16_2_1;
|
||||
/* Select a codec configuration to apply that is mandatory to support by both client and server.
|
||||
* Allows this sample application to work without logic to parse the codec capabilities of the
|
||||
* server and selection of an appropriate codec configuration.
|
||||
*/
|
||||
static struct bt_audio_lc3_preset codec_configuration = BT_AUDIO_LC3_UNICAST_PRESET_16_2_1;
|
||||
|
||||
static K_SEM_DEFINE(sem_connected, 0, 1);
|
||||
static K_SEM_DEFINE(sem_sink_discovered, 0, 1);
|
||||
|
@ -43,20 +46,20 @@ static K_SEM_DEFINE(sem_stream_started, 0, 1);
|
|||
#include "lc3.h"
|
||||
#include "math.h"
|
||||
|
||||
/* Current sample do not use codec configuration parameters, hence below shall match the selected
|
||||
* codec configuration.
|
||||
* One sample data buffer en generated and repeated.
|
||||
*/
|
||||
#define AUDIO_SAMPLE_RATE_HZ 16000
|
||||
#define AUDIO_FREQUENCY_HZ 400
|
||||
#define AUDIO_LENGTH_US 10000 /* amount of sample data - shall match LC3 frame length */
|
||||
#define AUDIO_LENGTH_100US (AUDIO_LENGTH_US / 100)
|
||||
#define NUM_SAMPLES ((AUDIO_LENGTH_US * AUDIO_SAMPLE_RATE_HZ) / USEC_PER_SEC)
|
||||
#define MAX_SAMPLE_RATE 48000
|
||||
#define MAX_FRAME_DURATION_US 10000
|
||||
#define MAX_NUM_SAMPLES ((MAX_FRAME_DURATION_US * MAX_SAMPLE_RATE) / USEC_PER_SEC)
|
||||
#define AUDIO_VOLUME (INT16_MAX - 3000) /* codec does clipping above INT16_MAX - 3000 */
|
||||
#define AUDIO_TONE_FREQUENCY_HZ 400
|
||||
|
||||
static int16_t audio_buf[NUM_SAMPLES];
|
||||
static int16_t audio_buf[MAX_NUM_SAMPLES];
|
||||
static lc3_encoder_t lc3_encoder;
|
||||
static lc3_encoder_mem_48k_t lc3_encoder_mem;
|
||||
static int freq_hz;
|
||||
static int frame_duration_us;
|
||||
static int frame_duration_100us;
|
||||
static int frames_per_sdu;
|
||||
static int octets_per_frame;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -73,7 +76,7 @@ static void fill_audio_buf_sin(int16_t *buf, int length_us, int frequency_hz, in
|
|||
const unsigned int num_samples = (length_us * sample_rate_hz) / USEC_PER_SEC;
|
||||
const float step = 2 * 3.1415 / sine_period_samples;
|
||||
|
||||
for (int i = 0; i < num_samples; i++) {
|
||||
for (unsigned int i = 0; i < num_samples; i++) {
|
||||
const float sample = sin(i * step);
|
||||
|
||||
buf[i] = (int16_t)(AUDIO_VOLUME * sample);
|
||||
|
@ -87,11 +90,16 @@ static void lc3_audio_timer_timeout(struct k_work *work)
|
|||
*/
|
||||
const uint8_t prime_count = 2;
|
||||
static int64_t start_time;
|
||||
static int32_t pdu_cnt;
|
||||
int32_t pdu_goal_cnt;
|
||||
static int32_t sdu_cnt;
|
||||
int32_t sdu_goal_cnt;
|
||||
int64_t uptime, run_time_ms, run_time_100us;
|
||||
|
||||
k_work_schedule(&audio_send_work, K_USEC(preset_16_2_1.qos.interval));
|
||||
k_work_schedule(&audio_send_work, K_USEC(codec_configuration.qos.interval));
|
||||
|
||||
if (lc3_encoder == NULL) {
|
||||
printk("LC3 encoder not setup, cannot encode data.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (start_time == 0) {
|
||||
/* Read start time and produce the number of frames needed to catch up with any
|
||||
|
@ -106,20 +114,23 @@ static void lc3_audio_timer_timeout(struct k_work *work)
|
|||
|
||||
/* PDU count calculations done in 100us units to allow 7.5ms framelength in fixed-point */
|
||||
run_time_100us = run_time_ms * 10;
|
||||
pdu_goal_cnt = run_time_100us / AUDIO_LENGTH_100US;
|
||||
sdu_goal_cnt = run_time_100us / (frame_duration_100us * frames_per_sdu);
|
||||
|
||||
/* Add primer value to ensure the controller do not run low on data due to jitter */
|
||||
pdu_goal_cnt += prime_count;
|
||||
sdu_goal_cnt += prime_count;
|
||||
|
||||
printk("LC3 encode %d frames\n", pdu_goal_cnt - pdu_cnt);
|
||||
printk("LC3 encode %d frames in %d SDUs\n", (sdu_goal_cnt - sdu_cnt) * frames_per_sdu,
|
||||
(sdu_goal_cnt - sdu_cnt));
|
||||
|
||||
while (pdu_cnt < pdu_goal_cnt) {
|
||||
while (sdu_cnt < sdu_goal_cnt) {
|
||||
|
||||
int ret;
|
||||
struct net_buf *buf;
|
||||
uint8_t *net_buffer;
|
||||
uint16_t lc3_ret;
|
||||
const uint16_t tx_sdu_len = audio_stream.iso->qos->tx->sdu;
|
||||
const uint16_t tx_sdu_len = frames_per_sdu * octets_per_frame;
|
||||
int offset = 0;
|
||||
|
||||
|
||||
buf = net_buf_alloc(&tx_pool, K_FOREVER);
|
||||
net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE);
|
||||
|
@ -127,11 +138,17 @@ static void lc3_audio_timer_timeout(struct k_work *work)
|
|||
net_buffer = net_buf_tail(buf);
|
||||
buf->len += tx_sdu_len;
|
||||
|
||||
lc3_ret = lc3_encode(lc3_encoder, LC3_PCM_FORMAT_S16, audio_buf,
|
||||
1, tx_sdu_len, net_buffer);
|
||||
for (int i = 0; i < frames_per_sdu; i++) {
|
||||
|
||||
lc3_ret = lc3_encode(lc3_encoder, LC3_PCM_FORMAT_S16, audio_buf,
|
||||
1, octets_per_frame, net_buffer + offset);
|
||||
offset += octets_per_frame;
|
||||
}
|
||||
|
||||
if (lc3_ret == -1) {
|
||||
printk("LC3 encoder failed - wrong parameters?: %d", lc3_ret);
|
||||
net_buf_unref(buf);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = bt_audio_stream_send(&audio_stream, buf);
|
||||
|
@ -141,23 +158,52 @@ static void lc3_audio_timer_timeout(struct k_work *work)
|
|||
net_buf_unref(buf);
|
||||
} else {
|
||||
printk(" TX LC3 l: %zu\n", tx_sdu_len);
|
||||
pdu_cnt++;
|
||||
sdu_cnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void init_lc3(void)
|
||||
{
|
||||
/* Fill audio buffer with Sine wave */
|
||||
fill_audio_buf_sin(audio_buf, AUDIO_LENGTH_US, AUDIO_FREQUENCY_HZ,
|
||||
AUDIO_SAMPLE_RATE_HZ);
|
||||
unsigned int num_samples;
|
||||
|
||||
for (int i = 0; i < NUM_SAMPLES; i++) {
|
||||
freq_hz = bt_codec_cfg_get_freq(&codec_configuration.codec);
|
||||
frame_duration_us = bt_codec_cfg_get_frame_duration_us(&codec_configuration.codec);
|
||||
octets_per_frame = bt_codec_cfg_get_octets_per_frame(&codec_configuration.codec);
|
||||
frames_per_sdu = bt_codec_cfg_get_frame_blocks_per_sdu(audio_stream.codec, true);
|
||||
octets_per_frame = bt_codec_cfg_get_octets_per_frame(audio_stream.codec);
|
||||
|
||||
if (freq_hz < 0) {
|
||||
printk("Error: Codec frequency not set, cannot start codec.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (frame_duration_us < 0) {
|
||||
printk("Error: Frame duration not set, cannot start codec.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (octets_per_frame < 0) {
|
||||
printk("Error: Octets per frame not set, cannot start codec.");
|
||||
return;
|
||||
}
|
||||
|
||||
frame_duration_100us = frame_duration_us / 100;
|
||||
|
||||
|
||||
/* Fill audio buffer with Sine wave only once and repeat encoding the same tone frame */
|
||||
fill_audio_buf_sin(audio_buf, frame_duration_us, AUDIO_TONE_FREQUENCY_HZ, freq_hz);
|
||||
|
||||
num_samples = ((frame_duration_us * freq_hz) / USEC_PER_SEC);
|
||||
for (unsigned int i = 0; i < num_samples; i++) {
|
||||
printk("%3i: %6i\n", i, audio_buf[i]);
|
||||
}
|
||||
|
||||
/* Create the encoder instance. This shall complete before stream_started() is called. */
|
||||
lc3_encoder = lc3_setup_encoder(AUDIO_LENGTH_US, AUDIO_SAMPLE_RATE_HZ, 0, &lc3_encoder_mem);
|
||||
lc3_encoder = lc3_setup_encoder(frame_duration_us,
|
||||
freq_hz,
|
||||
0, /* No resampling */
|
||||
&lc3_encoder_mem);
|
||||
|
||||
if (lc3_encoder == NULL) {
|
||||
printk("ERROR: Failed to setup LC3 encoder - wrong parameters?\n");
|
||||
|
@ -229,7 +275,7 @@ static void print_hex(const uint8_t *ptr, size_t len)
|
|||
}
|
||||
}
|
||||
|
||||
static void print_codec(const struct bt_codec *codec)
|
||||
static void print_codec_capabilities(const struct bt_codec *codec)
|
||||
{
|
||||
printk("codec 0x%02x cid 0x%04x vid 0x%04x count %u\n",
|
||||
codec->id, codec->cid, codec->vid, codec->data_count);
|
||||
|
@ -418,19 +464,19 @@ static void add_remote_sink(struct bt_audio_ep *ep, uint8_t index)
|
|||
sinks[index] = ep;
|
||||
}
|
||||
|
||||
static void add_remote_codec(struct bt_codec *codec, int index,
|
||||
static void add_remote_codec(struct bt_codec *codec_capabilities, int index,
|
||||
uint8_t type)
|
||||
{
|
||||
printk("#%u: codec %p type 0x%02x\n", index, codec, type);
|
||||
printk("#%u: codec %p type 0x%02x\n", index, codec_capabilities, type);
|
||||
|
||||
print_codec(codec);
|
||||
print_codec_capabilities(codec_capabilities);
|
||||
|
||||
if (type != BT_AUDIO_SINK && type != BT_AUDIO_SOURCE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (index < CONFIG_BT_AUDIO_UNICAST_CLIENT_PAC_COUNT) {
|
||||
remote_codecs[index] = codec;
|
||||
remote_codec_capabilities[index] = codec_capabilities;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -577,7 +623,7 @@ static int configure_stream(struct bt_audio_stream *stream)
|
|||
int err;
|
||||
|
||||
err = bt_audio_stream_config(default_conn, stream, sinks[0],
|
||||
&preset_16_2_1.codec);
|
||||
&codec_configuration.codec);
|
||||
if (err != 0) {
|
||||
printk("Could not configure stream\n");
|
||||
return err;
|
||||
|
@ -610,7 +656,7 @@ static int set_stream_qos(void)
|
|||
int err;
|
||||
|
||||
err = bt_audio_stream_qos(default_conn, unicast_group,
|
||||
&preset_16_2_1.qos);
|
||||
&codec_configuration.qos);
|
||||
if (err != 0) {
|
||||
printk("Unable to setup QoS: %d", err);
|
||||
return err;
|
||||
|
@ -633,8 +679,8 @@ static int enable_stream(struct bt_audio_stream *stream)
|
|||
init_lc3();
|
||||
}
|
||||
|
||||
err = bt_audio_stream_enable(stream, preset_16_2_1.codec.meta,
|
||||
preset_16_2_1.codec.meta_count);
|
||||
err = bt_audio_stream_enable(stream, codec_configuration.codec.meta,
|
||||
codec_configuration.codec.meta_count);
|
||||
if (err != 0) {
|
||||
printk("Unable to enable stream: %d", err);
|
||||
return err;
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
#define CHANNEL_COUNT_1 BIT(0)
|
||||
|
||||
static struct bt_codec lc3_codec =
|
||||
BT_CODEC_LC3(BT_CODEC_LC3_FREQ_16KHZ, BT_CODEC_LC3_DURATION_10, CHANNEL_COUNT_1, 40u, 40u,
|
||||
BT_CODEC_LC3(BT_CODEC_LC3_FREQ_ANY, BT_CODEC_LC3_DURATION_10, CHANNEL_COUNT_1, 40u, 120u,
|
||||
1u, (BT_AUDIO_CONTEXT_TYPE_CONVERSATIONAL | BT_AUDIO_CONTEXT_TYPE_MEDIA),
|
||||
BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED);
|
||||
|
||||
|
@ -62,16 +62,14 @@ static const struct bt_data ad[] = {
|
|||
|
||||
#include "lc3.h"
|
||||
|
||||
/* Current sample do not use codec configuration parameters, hence below shall match the selected
|
||||
* codec configuration.
|
||||
*/
|
||||
#define AUDIO_SAMPLE_RATE_HZ 16000
|
||||
#define AUDIO_LENGTH_US 10000 /* amount of sample data - shall match LC3 frame length */
|
||||
#define NUM_SAMPLES ((AUDIO_LENGTH_US * AUDIO_SAMPLE_RATE_HZ) / USEC_PER_SEC)
|
||||
#define MAX_SAMPLE_RATE 48000
|
||||
#define MAX_FRAME_DURATION_US 10000
|
||||
#define MAX_NUM_SAMPLES ((MAX_FRAME_DURATION_US * MAX_SAMPLE_RATE) / USEC_PER_SEC)
|
||||
|
||||
static int16_t audio_buf[NUM_SAMPLES];
|
||||
static int16_t audio_buf[MAX_NUM_SAMPLES];
|
||||
static lc3_decoder_t lc3_decoder;
|
||||
static lc3_decoder_mem_48k_t lc3_decoder_mem;
|
||||
static int frames_per_sdu;
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -97,6 +95,23 @@ static void print_codec(const struct bt_codec *codec)
|
|||
printk("\n");
|
||||
}
|
||||
|
||||
if (codec->id == BT_CODEC_LC3_ID) {
|
||||
/* LC3 uses the generic LTV format - other codecs might do as well */
|
||||
|
||||
uint32_t chan_allocation;
|
||||
|
||||
printk(" Frequency: %d Hz\n", bt_codec_cfg_get_freq(codec));
|
||||
printk(" Frame Duration: %d us\n", bt_codec_cfg_get_frame_duration_us(codec));
|
||||
if (bt_codec_cfg_get_chan_allocation_val(codec, &chan_allocation) == 0) {
|
||||
printk(" Channel allocation: 0x%x\n", chan_allocation);
|
||||
}
|
||||
|
||||
printk(" Octets per frame: %d (negative means value not pressent)\n",
|
||||
bt_codec_cfg_get_octets_per_frame(codec));
|
||||
printk(" Frames per SDU: %d\n",
|
||||
bt_codec_cfg_get_frame_blocks_per_sdu(codec, true));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < codec->meta_count; i++) {
|
||||
printk("meta #%zu: type 0x%02x len %u\n",
|
||||
i, codec->meta[i].data.type,
|
||||
|
@ -138,6 +153,11 @@ static struct bt_audio_stream *lc3_config(struct bt_conn *conn,
|
|||
|
||||
printk("No streams available\n");
|
||||
|
||||
#if defined(CONFIG_LIBLC3CODEC)
|
||||
/* Nothing to free as static memory is used */
|
||||
lc3_decoder = NULL;
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -149,6 +169,11 @@ static int lc3_reconfig(struct bt_audio_stream *stream,
|
|||
|
||||
print_codec(codec);
|
||||
|
||||
#if defined(CONFIG_LIBLC3CODEC)
|
||||
/* Nothing to free as static memory is used */
|
||||
lc3_decoder = NULL;
|
||||
#endif
|
||||
|
||||
/* We only support one QoS at the moment, reject changes */
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
@ -169,10 +194,32 @@ static int lc3_enable(struct bt_audio_stream *stream,
|
|||
printk("Enable: stream %p meta_count %u\n", stream, meta_count);
|
||||
|
||||
#if defined(CONFIG_LIBLC3CODEC)
|
||||
/* TODO: parse codec config data and extract frame duration and sample-rate
|
||||
* Currently there is no lib for this.
|
||||
*/
|
||||
lc3_decoder = lc3_setup_decoder(AUDIO_LENGTH_US, AUDIO_SAMPLE_RATE_HZ, 0, &lc3_decoder_mem);
|
||||
{
|
||||
const int freq = bt_codec_cfg_get_freq(stream->codec);
|
||||
const int frame_duration_us = bt_codec_cfg_get_frame_duration_us(stream->codec);
|
||||
|
||||
if (freq < 0) {
|
||||
printk("Error: Codec frequency not set, cannot start codec.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (frame_duration_us < 0) {
|
||||
printk("Error: Frame duration not set, cannot start codec.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
frames_per_sdu = bt_codec_cfg_get_frame_blocks_per_sdu(stream->codec, true);
|
||||
|
||||
lc3_decoder = lc3_setup_decoder(frame_duration_us,
|
||||
freq,
|
||||
0, /* No resampling */
|
||||
&lc3_decoder_mem);
|
||||
|
||||
if (lc3_decoder == NULL) {
|
||||
printk("ERROR: Failed to setup LC3 encoder - wrong parameters?\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
@ -211,7 +258,6 @@ static int lc3_stop(struct bt_audio_stream *stream)
|
|||
static int lc3_release(struct bt_audio_stream *stream)
|
||||
{
|
||||
printk("Release: stream %p\n", stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -232,7 +278,8 @@ static struct bt_audio_capability_ops lc3_ops = {
|
|||
|
||||
static void stream_recv_lc3_codec(struct bt_audio_stream *stream, struct net_buf *buf)
|
||||
{
|
||||
uint8_t err;
|
||||
uint8_t err = -1;
|
||||
|
||||
/* TODO: If there is a way to know if the controller supports indicating errors in the
|
||||
* payload one could feed that into bad-frame-indicator. The HCI layer allows to
|
||||
* include this information, but currently there is no controller support.
|
||||
|
@ -241,19 +288,35 @@ static void stream_recv_lc3_codec(struct bt_audio_stream *stream, struct net_buf
|
|||
*/
|
||||
const uint8_t bad_frame_indicator = buf->len == 0 ? 1 : 0;
|
||||
uint8_t *in_buf = (bad_frame_indicator ? NULL : buf->data);
|
||||
const int octets_per_frame = buf->len / frames_per_sdu;
|
||||
|
||||
if (lc3_decoder == NULL) {
|
||||
printk("LC3 decoder not setup, cannot decode data.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* This code is to demonstrate the use of the LC3 codec. On an actual implementation
|
||||
* it might be required to offload the processing to another task to avoid blocking the
|
||||
* BT stack.
|
||||
*/
|
||||
for (int i = 0; i < frames_per_sdu; i++) {
|
||||
|
||||
err = lc3_decode(lc3_decoder, in_buf, buf->len, LC3_PCM_FORMAT_S16, audio_buf, 1);
|
||||
int offset = 0;
|
||||
|
||||
err = lc3_decode(lc3_decoder, in_buf + offset, octets_per_frame,
|
||||
LC3_PCM_FORMAT_S16, audio_buf, 1);
|
||||
|
||||
if (in_buf != NULL) {
|
||||
offset += octets_per_frame;
|
||||
}
|
||||
}
|
||||
|
||||
printk("RX stream %p len %u\n", stream, buf->len);
|
||||
|
||||
if (err == 1) {
|
||||
printk(" decoder performed PLC\n");
|
||||
return;
|
||||
|
||||
} else if (err < 0) {
|
||||
printk(" decoder failed - wrong parameters?\n");
|
||||
return;
|
||||
|
|
Loading…
Reference in a new issue