Skip to content

Commit 9c91f10

Browse files
committed
apps: add Auracast USB sample with LC3 codec
This adds sample that acts as USB sound device. Audio signal is coded using LC3 and broadacasted using Auracast package.
1 parent bbba284 commit 9c91f10

File tree

9 files changed

+1848
-0
lines changed

9 files changed

+1848
-0
lines changed

apps/auracast_usb/include/tusb_config.h

Lines changed: 445 additions & 0 deletions
Large diffs are not rendered by default.

apps/auracast_usb/include/usb_audio.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
#ifndef H_USB_AUDIO_
21+
#define H_USB_AUDIO_
22+
23+
#include <stdint.h>
24+
25+
typedef void (* usb_audio_sample_rate_cb_t)(uint32_t);
26+
27+
/* Set default sample rate, should only be used before USB is initialized */
28+
void usb_desc_sample_rate_set(uint32_t sample_rate);
29+
30+
/* Set callback to receive sample rate set by USB host */
31+
void usb_audio_sample_rate_cb_set(usb_audio_sample_rate_cb_t cb);
32+
33+
/* Get current sample rate */
34+
uint32_t usb_audio_sample_rate_get(void);
35+
36+
#endif /* H_USB_AUDIO_ */

apps/auracast_usb/pkg.yml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
#
18+
19+
pkg.name: apps/auracast_usb
20+
pkg.type: app
21+
pkg.description: Auracast sample application.
22+
23+
pkg.author: "Krzysztof Kopyściński"
24+
pkg.email: "krzysztof.kopyscinski@codecoup.pl"
25+
pkg.homepage: "http://mynewt.apache.org/"
26+
pkg.keywords:
27+
28+
pkg.deps:
29+
- "@apache-mynewt-core/sys/config"
30+
- nimble/host
31+
- nimble/host/util
32+
- nimble/host/services/gap
33+
- nimble/host/store/config
34+
- "@apache-mynewt-core/kernel/os"
35+
- "@apache-mynewt-core/sys/console"
36+
- "@apache-mynewt-core/sys/log"
37+
- "@apache-mynewt-core/sys/stats"
38+
- "@apache-mynewt-core/sys/sysinit"
39+
- "@apache-mynewt-core/sys/id"
40+
- "@apache-mynewt-core/hw/usb/tinyusb"
41+
- "@apache-mynewt-nimble/nimble/host/services/auracast"
42+
- "@apache-mynewt-nimble/ext/liblc3"
43+
44+
pkg.init:
45+
audio_usb_init: 402

apps/auracast_usb/src/app_priv.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
#ifndef H_APP_PRIV_
21+
#define H_APP_PRIV_
22+
23+
#include <syscfg/syscfg.h>
24+
25+
#ifndef MIN
26+
#define MIN(a,b) ((a) < (b) ? (a) : (b))
27+
#endif
28+
29+
#define AUDIO_CHANNELS MYNEWT_VAL(AURACAST_CHAN_NUM)
30+
#define AUDIO_SAMPLE_SIZE sizeof(int16_t)
31+
32+
#define LC3_FRAME_DURATION (MYNEWT_VAL(LC3_FRAME_DURATION))
33+
#define LC3_SAMPLING_FREQ (MYNEWT_VAL(LC3_SAMPLING_FREQ))
34+
#define LC3_BITRATE (MYNEWT_VAL(LC3_BITRATE))
35+
#define LC3_FPDT (LC3_SAMPLING_FREQ * LC3_FRAME_DURATION / 1000000)
36+
#define BIG_NUM_BIS (MIN(AUDIO_CHANNELS, MYNEWT_VAL(BIG_NUM_BIS)))
37+
38+
struct chan {
39+
void *encoder;
40+
uint16_t handle;
41+
};
42+
43+
extern struct chan chans[AUDIO_CHANNELS];
44+
#endif /* H_APP_PRIV_ */

apps/auracast_usb/src/audio_usb.c

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
#include <assert.h>
21+
#include <string.h>
22+
#include <stdint.h>
23+
#include <syscfg/syscfg.h>
24+
#include <hal/hal_gpio.h>
25+
#include <bsp/bsp.h>
26+
27+
#include <os/os.h>
28+
#include <common/tusb_fifo.h>
29+
#include <class/audio/audio_device.h>
30+
#include <usb_audio.h>
31+
32+
#include <lc3.h>
33+
#include <nrf_clock.h>
34+
35+
#include <nimble/hci_common.h>
36+
#include <nimble/transport.h>
37+
#include "host/ble_gap.h"
38+
39+
#include "app_priv.h"
40+
41+
#if 1
42+
#define TP_PIN_ENCODE LED_1
43+
#define TP_PIN_DATA_CB LED_2
44+
#define TP_PIN_DATA_IN LED_3
45+
#define TP_PIN_DATA_ISR 0
46+
#else
47+
#define TP_PIN_ENCODE 0
48+
#define TP_PIN_DATA_CB 0
49+
#define TP_PIN_DATA_IN 0
50+
#define TP_PIN_DATA_ISR 0
51+
#endif
52+
53+
#define TP_INIT(_pin) (TP_PIN_ ## _pin ? \
54+
hal_gpio_init_out(TP_PIN_ ## _pin, 0) : (void)0)
55+
#define TP_1(_pin) (TP_PIN_ ## _pin ? \
56+
hal_gpio_write(TP_PIN_ ## _pin, 1) : (void)0)
57+
#define TP_0(_pin) (TP_PIN_ ## _pin ? \
58+
hal_gpio_write(TP_PIN_ ## _pin, 0) : (void)0)
59+
60+
static uint8_t g_usb_enabled;
61+
62+
static void usb_data_func(struct os_event *ev);
63+
64+
struct chan chans[AUDIO_CHANNELS];
65+
66+
static struct os_event usb_data_ev = {
67+
.ev_cb = usb_data_func,
68+
};
69+
70+
static uint32_t frame_bytes_lc3;
71+
static uint16_t big_sdu;
72+
73+
static int16_t samples[96000 / 100 * 2 * 2];
74+
static uint8_t samples_out[96000 / 100 * 2];
75+
static int samples_idx = 0;
76+
77+
static uint32_t pkt_counter = 0;
78+
79+
static void
80+
usb_data_func(struct os_event *ev)
81+
{
82+
int ch_idx;
83+
unsigned int num_bytes;
84+
unsigned int num_samples;
85+
unsigned int num_frames;
86+
unsigned int frames_count;
87+
int read;
88+
int skip;
89+
90+
if (!g_usb_enabled) {
91+
tud_audio_clear_ep_out_ff();
92+
return;
93+
}
94+
95+
TP_1(DATA_CB);
96+
97+
while ((num_bytes = tud_audio_available()) > 0) {
98+
num_samples = num_bytes / AUDIO_SAMPLE_SIZE;
99+
num_frames = num_samples / MYNEWT_VAL(AURACAST_CHAN_NUM);
100+
num_bytes = num_frames * AUDIO_SAMPLE_SIZE * MYNEWT_VAL(AURACAST_CHAN_NUM);
101+
102+
assert(samples_idx + num_samples < ARRAY_SIZE(samples));
103+
104+
TP_1(DATA_IN);
105+
read = tud_audio_read(&samples[samples_idx], num_bytes);
106+
TP_0(DATA_IN);
107+
108+
assert(read == num_bytes);
109+
assert(samples[ARRAY_SIZE(samples) - 1] = 0xaaaa);
110+
111+
samples_idx += num_samples;
112+
assert((samples_idx & 0x80000000) == 0);
113+
frames_count = samples_idx / MYNEWT_VAL(AURACAST_CHAN_NUM);
114+
115+
if (frames_count >= LC3_FPDT) {
116+
pkt_counter++;
117+
skip = 0;
118+
119+
for (ch_idx = 0; ch_idx < MYNEWT_VAL(AURACAST_CHAN_NUM); ch_idx++) {
120+
if (chans[ch_idx].handle == 0) {
121+
skip = 1;
122+
continue;
123+
}
124+
}
125+
126+
if (!skip) {
127+
memset(samples_out, 0, sizeof(samples_out));
128+
TP_1(ENCODE);
129+
lc3_encode(chans[0].encoder, LC3_PCM_FORMAT_S16,
130+
samples + 0, AUDIO_CHANNELS,
131+
(int)frame_bytes_lc3, samples_out);
132+
TP_0(ENCODE);
133+
134+
if (AUDIO_CHANNELS == 2) {
135+
ble_iso_tx(chans[0].handle, samples_out, big_sdu);
136+
137+
TP_1(ENCODE);
138+
memset(samples_out, 0, sizeof(samples_out));
139+
lc3_encode(chans[1].encoder, LC3_PCM_FORMAT_S16,
140+
samples + 1, AUDIO_CHANNELS,
141+
(int)frame_bytes_lc3, samples_out);
142+
TP_0(ENCODE);
143+
ble_iso_tx(chans[1].handle, samples_out, big_sdu);
144+
} else {
145+
ble_iso_tx(chans[0].handle, samples_out, big_sdu);
146+
if (BIG_NUM_BIS == 1) {
147+
memset(samples_out, 0, sizeof(samples_out));
148+
lc3_encode(chans[1].encoder, LC3_PCM_FORMAT_S16,
149+
samples + 1, AUDIO_CHANNELS,
150+
(int)frame_bytes_lc3, samples_out);
151+
}
152+
ble_iso_tx(chans[0].handle, samples_out, big_sdu);
153+
}
154+
155+
}
156+
157+
if (frames_count > LC3_FPDT) {
158+
int old_samples_idx = samples_idx;
159+
samples_idx -= LC3_FPDT * AUDIO_CHANNELS;
160+
memmove(samples, &samples[old_samples_idx],
161+
(old_samples_idx - samples_idx) * AUDIO_SAMPLE_SIZE);
162+
} else {
163+
samples_idx = 0;
164+
}
165+
}
166+
}
167+
168+
TP_0(DATA_CB);
169+
}
170+
171+
bool
172+
tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_bytes_received,
173+
uint8_t func_id, uint8_t ep_out,
174+
uint8_t cur_alt_setting)
175+
{
176+
(void)rhport;
177+
(void)n_bytes_received;
178+
(void)func_id;
179+
(void)ep_out;
180+
(void)cur_alt_setting;
181+
182+
TP_1(DATA_ISR);
183+
184+
if (!usb_data_ev.ev_queued) {
185+
os_eventq_put(os_eventq_dflt_get(), &usb_data_ev);
186+
}
187+
188+
TP_0(DATA_ISR);
189+
190+
return true;
191+
}
192+
193+
void
194+
audio_usb_init(void)
195+
{
196+
/* Need to reference those explicitly, so they are always pulled by linker
197+
* instead of weak symbols in tinyusb.
198+
*/
199+
(void)tud_audio_rx_done_post_read_cb;
200+
201+
TP_INIT(ENCODE);
202+
TP_INIT(DATA_CB);
203+
TP_INIT(DATA_IN);
204+
TP_INIT(DATA_ISR);
205+
206+
usb_desc_sample_rate_set(LC3_SAMPLING_FREQ);
207+
208+
assert(LC3_FPDT == lc3_frame_samples(LC3_FRAME_DURATION,
209+
LC3_SAMPLING_FREQ));
210+
211+
memset(samples, 0xaa, sizeof(samples));
212+
213+
unsigned esize = lc3_encoder_size(LC3_FRAME_DURATION,
214+
LC3_SAMPLING_FREQ);
215+
for (int i = 0; i < AUDIO_CHANNELS; i++) {
216+
chans[i].encoder = calloc(1, esize);
217+
lc3_setup_encoder(LC3_FRAME_DURATION, LC3_SAMPLING_FREQ,
218+
0, chans[i].encoder);
219+
}
220+
221+
#ifdef NRF53_SERIES
222+
nrf_clock_hfclk_div_set(NRF_CLOCK, NRF_CLOCK_HFCLK_DIV_1);
223+
#endif
224+
225+
g_usb_enabled = 1;
226+
227+
frame_bytes_lc3 = lc3_frame_bytes(LC3_FRAME_DURATION, LC3_BITRATE);
228+
big_sdu = frame_bytes_lc3 *
229+
(1 + ((AUDIO_CHANNELS == 2) && (BIG_NUM_BIS == 1)));
230+
}
231+
232+
void
233+
audio_usb_enable(uint8_t enabled)
234+
{
235+
g_usb_enabled = enabled;
236+
}

0 commit comments

Comments
 (0)