Skip to content

Commit fccc2bf

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 a2c7937 commit fccc2bf

File tree

9 files changed

+1790
-0
lines changed

9 files changed

+1790
-0
lines changed

apps/auracast_usb/include/tusb_config.h

Lines changed: 445 additions & 0 deletions
Large diffs are not rendered by default.
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: "[email protected]"
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: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
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 <syscfg/syscfg.h>
23+
#include <os/os.h>
24+
#include <common/tusb_fifo.h>
25+
#include <class/audio/audio_device.h>
26+
#include <usb_audio.h>
27+
28+
#include <lc3.h>
29+
#include <nrf_clock.h>
30+
31+
#include "host/ble_gap.h"
32+
33+
#include "app_priv.h"
34+
35+
static uint8_t g_usb_enabled;
36+
37+
static void usb_data_func(struct os_event *ev);
38+
39+
struct chan chans[AUDIO_CHANNELS];
40+
41+
static struct os_event usb_data_ev = {
42+
.ev_cb = usb_data_func,
43+
};
44+
45+
static uint32_t frame_bytes_lc3;
46+
static uint16_t big_sdu;
47+
48+
static int16_t samples[96000 / 100 * 2 * 2];
49+
static uint8_t samples_out[96000 / 100 * 2];
50+
static int samples_idx = 0;
51+
52+
static uint32_t pkt_counter = 0;
53+
54+
static void
55+
usb_data_func(struct os_event *ev)
56+
{
57+
int ch_idx;
58+
unsigned int num_bytes;
59+
unsigned int num_samples;
60+
unsigned int num_frames;
61+
unsigned int frames_count;
62+
int read;
63+
int skip;
64+
65+
if (!g_usb_enabled) {
66+
tud_audio_clear_ep_out_ff();
67+
return;
68+
}
69+
70+
while ((num_bytes = tud_audio_available()) > 0) {
71+
num_samples = num_bytes / AUDIO_SAMPLE_SIZE;
72+
num_frames = num_samples / MYNEWT_VAL(AURACAST_CHAN_NUM);
73+
num_bytes = num_frames * AUDIO_SAMPLE_SIZE * MYNEWT_VAL(AURACAST_CHAN_NUM);
74+
75+
assert(samples_idx + num_samples < ARRAY_SIZE(samples));
76+
77+
read = tud_audio_read(&samples[samples_idx], num_bytes);
78+
79+
assert(read == num_bytes);
80+
assert(samples[ARRAY_SIZE(samples) - 1] = 0xaaaa);
81+
82+
samples_idx += num_samples;
83+
assert((samples_idx & 0x80000000) == 0);
84+
frames_count = samples_idx / MYNEWT_VAL(AURACAST_CHAN_NUM);
85+
86+
if (frames_count >= LC3_FPDT) {
87+
pkt_counter++;
88+
skip = 0;
89+
90+
for (ch_idx = 0; ch_idx < MYNEWT_VAL(AURACAST_CHAN_NUM); ch_idx++) {
91+
if (chans[ch_idx].handle == 0) {
92+
skip = 1;
93+
continue;
94+
}
95+
}
96+
97+
if (!skip) {
98+
memset(samples_out, 0, sizeof(samples_out));
99+
lc3_encode(chans[0].encoder, LC3_PCM_FORMAT_S16,
100+
samples + 0, AUDIO_CHANNELS,
101+
(int)frame_bytes_lc3, samples_out);
102+
103+
if (AUDIO_CHANNELS == 2) {
104+
ble_iso_tx(chans[0].handle, samples_out, big_sdu);
105+
106+
memset(samples_out, 0, sizeof(samples_out));
107+
lc3_encode(chans[1].encoder, LC3_PCM_FORMAT_S16,
108+
samples + 1, AUDIO_CHANNELS,
109+
(int)frame_bytes_lc3, samples_out);
110+
ble_iso_tx(chans[1].handle, samples_out, big_sdu);
111+
} else {
112+
ble_iso_tx(chans[0].handle, samples_out, big_sdu);
113+
if (BIG_NUM_BIS == 1) {
114+
memset(samples_out, 0, sizeof(samples_out));
115+
lc3_encode(chans[1].encoder, LC3_PCM_FORMAT_S16,
116+
samples + 1, AUDIO_CHANNELS,
117+
(int)frame_bytes_lc3, samples_out);
118+
}
119+
ble_iso_tx(chans[0].handle, samples_out, big_sdu);
120+
}
121+
122+
}
123+
124+
if (frames_count > LC3_FPDT) {
125+
int old_samples_idx = samples_idx;
126+
samples_idx -= LC3_FPDT * AUDIO_CHANNELS;
127+
memmove(samples, &samples[old_samples_idx],
128+
(old_samples_idx - samples_idx) * AUDIO_SAMPLE_SIZE);
129+
} else {
130+
samples_idx = 0;
131+
}
132+
}
133+
}
134+
135+
}
136+
137+
bool
138+
tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_bytes_received,
139+
uint8_t func_id, uint8_t ep_out,
140+
uint8_t cur_alt_setting)
141+
{
142+
(void)rhport;
143+
(void)n_bytes_received;
144+
(void)func_id;
145+
(void)ep_out;
146+
(void)cur_alt_setting;
147+
148+
if (!usb_data_ev.ev_queued) {
149+
os_eventq_put(os_eventq_dflt_get(), &usb_data_ev);
150+
}
151+
152+
return true;
153+
}
154+
155+
void
156+
audio_usb_init(void)
157+
{
158+
/* Need to reference those explicitly, so they are always pulled by linker
159+
* instead of weak symbols in tinyusb.
160+
*/
161+
(void)tud_audio_rx_done_post_read_cb;
162+
163+
usb_desc_sample_rate_set(LC3_SAMPLING_FREQ);
164+
165+
assert(LC3_FPDT == lc3_frame_samples(LC3_FRAME_DURATION,
166+
LC3_SAMPLING_FREQ));
167+
168+
memset(samples, 0xaa, sizeof(samples));
169+
170+
unsigned esize = lc3_encoder_size(LC3_FRAME_DURATION,
171+
LC3_SAMPLING_FREQ);
172+
for (int i = 0; i < AUDIO_CHANNELS; i++) {
173+
chans[i].encoder = calloc(1, esize);
174+
lc3_setup_encoder(LC3_FRAME_DURATION, LC3_SAMPLING_FREQ,
175+
0, chans[i].encoder);
176+
}
177+
178+
#ifdef NRF53_SERIES
179+
nrf_clock_hfclk_div_set(NRF_CLOCK, NRF_CLOCK_HFCLK_DIV_1);
180+
#endif
181+
182+
g_usb_enabled = 1;
183+
184+
frame_bytes_lc3 = lc3_frame_bytes(LC3_FRAME_DURATION, LC3_BITRATE);
185+
big_sdu = frame_bytes_lc3 *
186+
(1 + ((AUDIO_CHANNELS == 2) && (BIG_NUM_BIS == 1)));
187+
}

0 commit comments

Comments
 (0)