1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include <errno.h>
7 #include <errno.h>
8 #include <stdbool.h>
9 #include <stdint.h>
10 #include <string.h>
11 
12 #include <zephyr/bluetooth/audio/audio.h>
13 #include <zephyr/kernel.h>
14 #include <zephyr/logging/log.h>
15 #include <zephyr/logging/log_core.h>
16 #include <zephyr/net_buf.h>
17 #include <zephyr/sys/printk.h>
18 #include <zephyr/sys_clock.h>
19 
20 #include <lc3.h>
21 #include <sys/errno.h>
22 #include <math.h>
23 
24 #include "stream_lc3.h"
25 #include "stream_tx.h"
26 
27 LOG_MODULE_REGISTER(lc3, LOG_LEVEL_INF);
28 
29 #define LC3_MAX_SAMPLE_RATE       48000U
30 #define LC3_MAX_FRAME_DURATION_US 10000U
31 #define LC3_MAX_NUM_SAMPLES       ((LC3_MAX_FRAME_DURATION_US * LC3_MAX_SAMPLE_RATE) / USEC_PER_SEC)
32 /* codec does clipping above INT16_MAX - 3000 */
33 #define AUDIO_VOLUME              (INT16_MAX - 3000)
34 #define AUDIO_TONE_FREQUENCY_HZ   400
35 
36 static int16_t audio_buf[LC3_MAX_NUM_SAMPLES];
37 /**
38  * Use the math lib to generate a sine-wave using 16 bit samples into a buffer.
39  *
40  * @param stream The TX stream to generate and fill the sine wave for
41  */
fill_audio_buf_sin(struct tx_stream * stream)42 static void fill_audio_buf_sin(struct tx_stream *stream)
43 {
44 	const unsigned int num_samples =
45 		(stream->lc3_tx.frame_duration_us * stream->lc3_tx.freq_hz) / USEC_PER_SEC;
46 	const int sine_period_samples = stream->lc3_tx.freq_hz / AUDIO_TONE_FREQUENCY_HZ;
47 	const float step = 2 * 3.1415f / sine_period_samples;
48 
49 	for (unsigned int i = 0; i < num_samples; i++) {
50 		const float sample = sinf(i * step);
51 
52 		audio_buf[i] = (int16_t)(AUDIO_VOLUME * sample);
53 	}
54 }
55 
extract_lc3_config(struct tx_stream * stream)56 static int extract_lc3_config(struct tx_stream *stream)
57 {
58 	const struct bt_audio_codec_cfg *codec_cfg = stream->bap_stream->codec_cfg;
59 	struct stream_lc3_tx *lc3_tx = &stream->lc3_tx;
60 	int ret;
61 
62 	LOG_INF("Extracting LC3 configuration values");
63 
64 	ret = bt_audio_codec_cfg_get_freq(codec_cfg);
65 	if (ret >= 0) {
66 		ret = bt_audio_codec_cfg_freq_to_freq_hz(ret);
67 		if (ret > 0) {
68 			if (LC3_CHECK_SR_HZ(ret)) {
69 				lc3_tx->freq_hz = (uint32_t)ret;
70 			} else {
71 				LOG_ERR("Unsupported sampling frequency for LC3: %d", ret);
72 
73 				return ret;
74 			}
75 		} else {
76 			LOG_ERR("Invalid frequency: %d", ret);
77 
78 			return ret;
79 		}
80 	} else {
81 		LOG_ERR("Could not get frequency: %d", ret);
82 
83 		return ret;
84 	}
85 
86 	ret = bt_audio_codec_cfg_get_frame_dur(codec_cfg);
87 	if (ret >= 0) {
88 		ret = bt_audio_codec_cfg_frame_dur_to_frame_dur_us(ret);
89 		if (ret > 0) {
90 			if (LC3_CHECK_DT_US(ret)) {
91 				lc3_tx->frame_duration_us = (uint32_t)ret;
92 			} else {
93 				LOG_ERR("Unsupported frame duration for LC3: %d", ret);
94 
95 				return ret;
96 			}
97 		} else {
98 			LOG_ERR("Invalid frame duration: %d", ret);
99 
100 			return ret;
101 		}
102 	} else {
103 		LOG_ERR("Could not get frame duration: %d", ret);
104 
105 		return ret;
106 	}
107 
108 	ret = bt_audio_codec_cfg_get_chan_allocation(codec_cfg, &lc3_tx->chan_allocation, false);
109 	if (ret != 0) {
110 		LOG_DBG("Could not get channel allocation: %d", ret);
111 		lc3_tx->chan_allocation = BT_AUDIO_LOCATION_MONO_AUDIO;
112 	}
113 
114 	lc3_tx->chan_cnt = bt_audio_get_chan_count(lc3_tx->chan_allocation);
115 
116 	ret = bt_audio_codec_cfg_get_frame_blocks_per_sdu(codec_cfg, true);
117 	if (ret >= 0) {
118 		lc3_tx->frame_blocks_per_sdu = (uint8_t)ret;
119 	}
120 
121 	ret = bt_audio_codec_cfg_get_octets_per_frame(codec_cfg);
122 	if (ret >= 0) {
123 		lc3_tx->octets_per_frame = (uint16_t)ret;
124 	} else {
125 		LOG_ERR("Could not get octets per frame: %d", ret);
126 
127 		return ret;
128 	}
129 
130 	return 0;
131 }
132 
encode_frame(struct tx_stream * stream,uint8_t index,struct net_buf * out_buf)133 static bool encode_frame(struct tx_stream *stream, uint8_t index, struct net_buf *out_buf)
134 {
135 	const uint16_t octets_per_frame = stream->lc3_tx.octets_per_frame;
136 	int lc3_ret;
137 
138 	/* Generate sine wave */
139 	fill_audio_buf_sin(stream);
140 
141 	lc3_ret = lc3_encode(stream->lc3_tx.encoder, LC3_PCM_FORMAT_S16, audio_buf, 1,
142 			     octets_per_frame, net_buf_tail(out_buf));
143 	if (lc3_ret < 0) {
144 		LOG_ERR("LC3 encoder failed - wrong parameters?: %d", lc3_ret);
145 
146 		return false;
147 	}
148 
149 	out_buf->len += octets_per_frame;
150 
151 	return true;
152 }
153 
encode_frame_block(struct tx_stream * stream,struct net_buf * out_buf)154 static bool encode_frame_block(struct tx_stream *stream, struct net_buf *out_buf)
155 {
156 	for (uint8_t i = 0U; i < stream->lc3_tx.chan_cnt; i++) {
157 		/* We provide the total number of decoded frames to `decode_frame` for logging
158 		 * purposes
159 		 */
160 		if (!encode_frame(stream, i, out_buf)) {
161 			return false;
162 		}
163 	}
164 
165 	return true;
166 }
167 
stream_lc3_add_data(struct tx_stream * stream,struct net_buf * buf)168 void stream_lc3_add_data(struct tx_stream *stream, struct net_buf *buf)
169 {
170 	for (uint8_t i = 0U; i < stream->lc3_tx.frame_blocks_per_sdu; i++) {
171 		if (!encode_frame_block(stream, buf)) {
172 			break;
173 		}
174 	}
175 }
176 
stream_lc3_init(struct tx_stream * stream)177 int stream_lc3_init(struct tx_stream *stream)
178 {
179 	int err;
180 
181 	err = extract_lc3_config(stream);
182 	if (err != 0) {
183 		memset(&stream->lc3_tx, 0, sizeof(stream->lc3_tx));
184 
185 		return err;
186 	}
187 
188 	/* Fill audio buffer with Sine wave only once and repeat encoding the same tone frame */
189 	LOG_INF("Initializing sine wave data");
190 	fill_audio_buf_sin(stream);
191 
192 	LOG_INF("Setting up LC3 encoder");
193 	stream->lc3_tx.encoder =
194 		lc3_setup_encoder(stream->lc3_tx.frame_duration_us, stream->lc3_tx.freq_hz, 0,
195 				  &stream->lc3_tx.encoder_mem);
196 
197 	if (stream->lc3_tx.encoder == NULL) {
198 		LOG_ERR("Failed to setup LC3 encoder");
199 
200 		memset(&stream->lc3_tx, 0, sizeof(stream->lc3_tx));
201 
202 		return -ENOEXEC;
203 	}
204 
205 	return 0;
206 }
207