-
Notifications
You must be signed in to change notification settings - Fork 399
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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.
- Loading branch information
1 parent
a2c7937
commit fccc2bf
Showing
9 changed files
with
1,790 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one | ||
* or more contributor license agreements. See the NOTICE file | ||
* distributed with this work for additional information | ||
* regarding copyright ownership. The ASF licenses this file | ||
* to you under the Apache License, Version 2.0 (the | ||
* "License"); you may not use this file except in compliance | ||
* with the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
#ifndef H_USB_AUDIO_ | ||
#define H_USB_AUDIO_ | ||
|
||
#include <stdint.h> | ||
|
||
typedef void (* usb_audio_sample_rate_cb_t)(uint32_t); | ||
|
||
/* Set default sample rate, should only be used before USB is initialized */ | ||
void usb_desc_sample_rate_set(uint32_t sample_rate); | ||
|
||
/* Set callback to receive sample rate set by USB host */ | ||
void usb_audio_sample_rate_cb_set(usb_audio_sample_rate_cb_t cb); | ||
|
||
/* Get current sample rate */ | ||
uint32_t usb_audio_sample_rate_get(void); | ||
|
||
#endif /* H_USB_AUDIO_ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# Licensed to the Apache Software Foundation (ASF) under one | ||
# or more contributor license agreements. See the NOTICE file | ||
# distributed with this work for additional information | ||
# regarding copyright ownership. The ASF licenses this file | ||
# to you under the Apache License, Version 2.0 (the | ||
# "License"); you may not use this file except in compliance | ||
# with the License. You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, | ||
# software distributed under the License is distributed on an | ||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
# KIND, either express or implied. See the License for the | ||
# specific language governing permissions and limitations | ||
# under the License. | ||
# | ||
|
||
pkg.name: apps/auracast_usb | ||
pkg.type: app | ||
pkg.description: Auracast sample application. | ||
|
||
pkg.author: "Krzysztof Kopyściński" | ||
pkg.email: "krzysztof.kopyscinski@codecoup.pl" | ||
pkg.homepage: "http://mynewt.apache.org/" | ||
pkg.keywords: | ||
|
||
pkg.deps: | ||
- "@apache-mynewt-core/sys/config" | ||
- nimble/host | ||
- nimble/host/util | ||
- nimble/host/services/gap | ||
- nimble/host/store/config | ||
- "@apache-mynewt-core/kernel/os" | ||
- "@apache-mynewt-core/sys/console" | ||
- "@apache-mynewt-core/sys/log" | ||
- "@apache-mynewt-core/sys/stats" | ||
- "@apache-mynewt-core/sys/sysinit" | ||
- "@apache-mynewt-core/sys/id" | ||
- "@apache-mynewt-core/hw/usb/tinyusb" | ||
- "@apache-mynewt-nimble/nimble/host/services/auracast" | ||
- "@apache-mynewt-nimble/ext/liblc3" | ||
|
||
pkg.init: | ||
audio_usb_init: 402 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one | ||
* or more contributor license agreements. See the NOTICE file | ||
* distributed with this work for additional information | ||
* regarding copyright ownership. The ASF licenses this file | ||
* to you under the Apache License, Version 2.0 (the | ||
* "License"); you may not use this file except in compliance | ||
* with the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
#ifndef H_APP_PRIV_ | ||
#define H_APP_PRIV_ | ||
|
||
#include <syscfg/syscfg.h> | ||
|
||
#ifndef MIN | ||
#define MIN(a,b) ((a) < (b) ? (a) : (b)) | ||
#endif | ||
|
||
#define AUDIO_CHANNELS MYNEWT_VAL(AURACAST_CHAN_NUM) | ||
#define AUDIO_SAMPLE_SIZE sizeof(int16_t) | ||
|
||
#define LC3_FRAME_DURATION (MYNEWT_VAL(LC3_FRAME_DURATION)) | ||
#define LC3_SAMPLING_FREQ (MYNEWT_VAL(LC3_SAMPLING_FREQ)) | ||
#define LC3_BITRATE (MYNEWT_VAL(LC3_BITRATE)) | ||
#define LC3_FPDT (LC3_SAMPLING_FREQ * LC3_FRAME_DURATION / 1000000) | ||
#define BIG_NUM_BIS (MIN(AUDIO_CHANNELS, MYNEWT_VAL(BIG_NUM_BIS))) | ||
|
||
struct chan { | ||
void *encoder; | ||
uint16_t handle; | ||
}; | ||
|
||
extern struct chan chans[AUDIO_CHANNELS]; | ||
#endif /* H_APP_PRIV_ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one | ||
* or more contributor license agreements. See the NOTICE file | ||
* distributed with this work for additional information | ||
* regarding copyright ownership. The ASF licenses this file | ||
* to you under the Apache License, Version 2.0 (the | ||
* "License"); you may not use this file except in compliance | ||
* with the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
#include <assert.h> | ||
#include <string.h> | ||
#include <syscfg/syscfg.h> | ||
#include <os/os.h> | ||
#include <common/tusb_fifo.h> | ||
#include <class/audio/audio_device.h> | ||
#include <usb_audio.h> | ||
|
||
#include <lc3.h> | ||
#include <nrf_clock.h> | ||
|
||
#include "host/ble_gap.h" | ||
|
||
#include "app_priv.h" | ||
|
||
static uint8_t g_usb_enabled; | ||
|
||
static void usb_data_func(struct os_event *ev); | ||
|
||
struct chan chans[AUDIO_CHANNELS]; | ||
|
||
static struct os_event usb_data_ev = { | ||
.ev_cb = usb_data_func, | ||
}; | ||
|
||
static uint32_t frame_bytes_lc3; | ||
static uint16_t big_sdu; | ||
|
||
static int16_t samples[96000 / 100 * 2 * 2]; | ||
static uint8_t samples_out[96000 / 100 * 2]; | ||
static int samples_idx = 0; | ||
|
||
static uint32_t pkt_counter = 0; | ||
|
||
static void | ||
usb_data_func(struct os_event *ev) | ||
{ | ||
int ch_idx; | ||
unsigned int num_bytes; | ||
unsigned int num_samples; | ||
unsigned int num_frames; | ||
unsigned int frames_count; | ||
int read; | ||
int skip; | ||
|
||
if (!g_usb_enabled) { | ||
tud_audio_clear_ep_out_ff(); | ||
return; | ||
} | ||
|
||
while ((num_bytes = tud_audio_available()) > 0) { | ||
num_samples = num_bytes / AUDIO_SAMPLE_SIZE; | ||
num_frames = num_samples / MYNEWT_VAL(AURACAST_CHAN_NUM); | ||
num_bytes = num_frames * AUDIO_SAMPLE_SIZE * MYNEWT_VAL(AURACAST_CHAN_NUM); | ||
|
||
assert(samples_idx + num_samples < ARRAY_SIZE(samples)); | ||
|
||
read = tud_audio_read(&samples[samples_idx], num_bytes); | ||
|
||
assert(read == num_bytes); | ||
assert(samples[ARRAY_SIZE(samples) - 1] = 0xaaaa); | ||
|
||
samples_idx += num_samples; | ||
assert((samples_idx & 0x80000000) == 0); | ||
frames_count = samples_idx / MYNEWT_VAL(AURACAST_CHAN_NUM); | ||
|
||
if (frames_count >= LC3_FPDT) { | ||
pkt_counter++; | ||
skip = 0; | ||
|
||
for (ch_idx = 0; ch_idx < MYNEWT_VAL(AURACAST_CHAN_NUM); ch_idx++) { | ||
if (chans[ch_idx].handle == 0) { | ||
skip = 1; | ||
continue; | ||
} | ||
} | ||
|
||
if (!skip) { | ||
memset(samples_out, 0, sizeof(samples_out)); | ||
lc3_encode(chans[0].encoder, LC3_PCM_FORMAT_S16, | ||
samples + 0, AUDIO_CHANNELS, | ||
(int)frame_bytes_lc3, samples_out); | ||
|
||
if (AUDIO_CHANNELS == 2) { | ||
ble_iso_tx(chans[0].handle, samples_out, big_sdu); | ||
|
||
memset(samples_out, 0, sizeof(samples_out)); | ||
lc3_encode(chans[1].encoder, LC3_PCM_FORMAT_S16, | ||
samples + 1, AUDIO_CHANNELS, | ||
(int)frame_bytes_lc3, samples_out); | ||
ble_iso_tx(chans[1].handle, samples_out, big_sdu); | ||
} else { | ||
ble_iso_tx(chans[0].handle, samples_out, big_sdu); | ||
if (BIG_NUM_BIS == 1) { | ||
memset(samples_out, 0, sizeof(samples_out)); | ||
lc3_encode(chans[1].encoder, LC3_PCM_FORMAT_S16, | ||
samples + 1, AUDIO_CHANNELS, | ||
(int)frame_bytes_lc3, samples_out); | ||
} | ||
ble_iso_tx(chans[0].handle, samples_out, big_sdu); | ||
} | ||
|
||
} | ||
|
||
if (frames_count > LC3_FPDT) { | ||
int old_samples_idx = samples_idx; | ||
samples_idx -= LC3_FPDT * AUDIO_CHANNELS; | ||
memmove(samples, &samples[old_samples_idx], | ||
(old_samples_idx - samples_idx) * AUDIO_SAMPLE_SIZE); | ||
} else { | ||
samples_idx = 0; | ||
} | ||
} | ||
} | ||
|
||
} | ||
|
||
bool | ||
tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_bytes_received, | ||
uint8_t func_id, uint8_t ep_out, | ||
uint8_t cur_alt_setting) | ||
{ | ||
(void)rhport; | ||
(void)n_bytes_received; | ||
(void)func_id; | ||
(void)ep_out; | ||
(void)cur_alt_setting; | ||
|
||
if (!usb_data_ev.ev_queued) { | ||
os_eventq_put(os_eventq_dflt_get(), &usb_data_ev); | ||
} | ||
|
||
return true; | ||
} | ||
|
||
void | ||
audio_usb_init(void) | ||
{ | ||
/* Need to reference those explicitly, so they are always pulled by linker | ||
* instead of weak symbols in tinyusb. | ||
*/ | ||
(void)tud_audio_rx_done_post_read_cb; | ||
|
||
usb_desc_sample_rate_set(LC3_SAMPLING_FREQ); | ||
|
||
assert(LC3_FPDT == lc3_frame_samples(LC3_FRAME_DURATION, | ||
LC3_SAMPLING_FREQ)); | ||
|
||
memset(samples, 0xaa, sizeof(samples)); | ||
|
||
unsigned esize = lc3_encoder_size(LC3_FRAME_DURATION, | ||
LC3_SAMPLING_FREQ); | ||
for (int i = 0; i < AUDIO_CHANNELS; i++) { | ||
chans[i].encoder = calloc(1, esize); | ||
lc3_setup_encoder(LC3_FRAME_DURATION, LC3_SAMPLING_FREQ, | ||
0, chans[i].encoder); | ||
} | ||
|
||
#ifdef NRF53_SERIES | ||
nrf_clock_hfclk_div_set(NRF_CLOCK, NRF_CLOCK_HFCLK_DIV_1); | ||
#endif | ||
|
||
g_usb_enabled = 1; | ||
|
||
frame_bytes_lc3 = lc3_frame_bytes(LC3_FRAME_DURATION, LC3_BITRATE); | ||
big_sdu = frame_bytes_lc3 * | ||
(1 + ((AUDIO_CHANNELS == 2) && (BIG_NUM_BIS == 1))); | ||
} |
Oops, something went wrong.