diff --git a/applications/nrf5340_audio/prj.conf b/applications/nrf5340_audio/prj.conf index 162cc1ffdb96..91d8461da51b 100644 --- a/applications/nrf5340_audio/prj.conf +++ b/applications/nrf5340_audio/prj.conf @@ -58,3 +58,7 @@ CONFIG_DEVICE_SHELL=n # Suppress err msg from sd_check_card_type. Because SPI_SDHC has no card presence method, # assume card is in slot. Thus error message is always shown if card is not inserted CONFIG_SD_LOG_LEVEL_OFF=y +CONFIG_LC3_DEC_CHAN_MAX=2 + +CONFIG_MAIN_STACK_SIZE=1800 +CONFIG_LC3_PLAYBACK=y diff --git a/applications/nrf5340_audio/src/audio/audio_datapath.c b/applications/nrf5340_audio/src/audio/audio_datapath.c index e487f5c85583..253d100ca02e 100644 --- a/applications/nrf5340_audio/src/audio/audio_datapath.c +++ b/applications/nrf5340_audio/src/audio/audio_datapath.c @@ -24,6 +24,7 @@ #include "contin_array.h" #include "pcm_mix.h" #include "streamctrl.h" +#include "lc3_playback.h" #include LOG_MODULE_REGISTER(audio_datapath, CONFIG_AUDIO_DATAPATH_LOG_LEVEL); @@ -42,23 +43,23 @@ LOG_MODULE_REGISTER(audio_datapath, CONFIG_AUDIO_DATAPATH_LOG_LEVEL); /* Total sample FIFO period in microseconds */ #define FIFO_SMPL_PERIOD_US (CONFIG_AUDIO_MAX_PRES_DLY_US * 2) -#define FIFO_NUM_BLKS NUM_BLKS(FIFO_SMPL_PERIOD_US) -#define MAX_FIFO_SIZE (FIFO_NUM_BLKS * BLK_SIZE_SAMPLES(CONFIG_AUDIO_SAMPLE_RATE_HZ) * 2) +#define FIFO_NUM_BLKS NUM_BLKS(FIFO_SMPL_PERIOD_US) +#define MAX_FIFO_SIZE (FIFO_NUM_BLKS * BLK_SIZE_SAMPLES(CONFIG_AUDIO_SAMPLE_RATE_HZ) * 2) /* Number of audio blocks given a duration */ -#define NUM_BLKS(d) ((d) / BLK_PERIOD_US) +#define NUM_BLKS(d) ((d) / BLK_PERIOD_US) /* Single audio block size in number of samples (stereo) */ #define BLK_SIZE_SAMPLES(r) (((r)*BLK_PERIOD_US) / 1000000) /* Increment sample FIFO index by one block */ -#define NEXT_IDX(i) (((i) < (FIFO_NUM_BLKS - 1)) ? ((i) + 1) : 0) +#define NEXT_IDX(i) (((i) < (FIFO_NUM_BLKS - 1)) ? ((i) + 1) : 0) /* Decrement sample FIFO index by one block */ -#define PREV_IDX(i) (((i) > 0) ? ((i)-1) : (FIFO_NUM_BLKS - 1)) +#define PREV_IDX(i) (((i) > 0) ? ((i)-1) : (FIFO_NUM_BLKS - 1)) -#define NUM_BLKS_IN_FRAME NUM_BLKS(CONFIG_AUDIO_FRAME_DURATION_US) -#define BLK_MONO_NUM_SAMPS BLK_SIZE_SAMPLES(CONFIG_AUDIO_SAMPLE_RATE_HZ) -#define BLK_STEREO_NUM_SAMPS (BLK_MONO_NUM_SAMPS * 2) +#define NUM_BLKS_IN_FRAME NUM_BLKS(CONFIG_AUDIO_FRAME_DURATION_US) +#define BLK_MONO_NUM_SAMPS BLK_SIZE_SAMPLES(CONFIG_AUDIO_SAMPLE_RATE_HZ) +#define BLK_STEREO_NUM_SAMPS (BLK_MONO_NUM_SAMPS * 2) /* Number of octets in a single audio block */ -#define BLK_MONO_SIZE_OCTETS (BLK_MONO_NUM_SAMPS * CONFIG_AUDIO_BIT_DEPTH_OCTETS) +#define BLK_MONO_SIZE_OCTETS (BLK_MONO_NUM_SAMPS * CONFIG_AUDIO_BIT_DEPTH_OCTETS) #define BLK_STEREO_SIZE_OCTETS (BLK_MONO_SIZE_OCTETS * 2) /* How many function calls before moving on with drift compensation */ #define DRIFT_COMP_WAITING_CNT (DRIFT_MEAS_PERIOD_US / BLK_PERIOD_US) @@ -67,27 +68,27 @@ LOG_MODULE_REGISTER(audio_datapath, CONFIG_AUDIO_DATAPATH_LOG_LEVEL); /* Audio clock - nRF5340 Analog Phase-Locked Loop (APLL) */ #define APLL_FREQ_CENTER 39854 -#define APLL_FREQ_MIN 36834 -#define APLL_FREQ_MAX 42874 +#define APLL_FREQ_MIN 36834 +#define APLL_FREQ_MAX 42874 /* Use nanoseconds to reduce rounding errors */ #define APLL_FREQ_ADJ(t) (-((t)*1000) / 331) -#define DRIFT_MEAS_PERIOD_US 100000 -#define DRIFT_ERR_THRESH_LOCK 16 +#define DRIFT_MEAS_PERIOD_US 100000 +#define DRIFT_ERR_THRESH_LOCK 16 #define DRIFT_ERR_THRESH_UNLOCK 32 /* 3000 us to allow BLE transmission and (host -> HCI -> controller) */ -#define JUST_IN_TIME_US (CONFIG_AUDIO_FRAME_DURATION_US - 3000) +#define JUST_IN_TIME_US (CONFIG_AUDIO_FRAME_DURATION_US - 3000) #define JUST_IN_TIME_THRESHOLD_US 1500 /* How often to print underrun warning */ #define UNDERRUN_LOG_INTERVAL_BLKS 5000 enum drift_comp_state { - DRIFT_STATE_INIT, /* Waiting for data to be received */ - DRIFT_STATE_CALIB, /* Calibrate and zero out local delay */ + DRIFT_STATE_INIT, /* Waiting for data to be received */ + DRIFT_STATE_CALIB, /* Calibrate and zero out local delay */ DRIFT_STATE_OFFSET, /* Adjust I2S offset relative to SDU Reference */ - DRIFT_STATE_LOCKED /* Drift compensation locked - Minor corrections */ + DRIFT_STATE_LOCKED /* Drift compensation locked - Minor corrections */ }; static const char *const drift_comp_state_names[] = { @@ -98,9 +99,9 @@ static const char *const drift_comp_state_names[] = { }; enum pres_comp_state { - PRES_STATE_INIT, /* Initialize presentation compensation */ - PRES_STATE_MEAS, /* Measure presentation delay */ - PRES_STATE_WAIT, /* Wait for some time */ + PRES_STATE_INIT, /* Initialize presentation compensation */ + PRES_STATE_MEAS, /* Measure presentation delay */ + PRES_STATE_WAIT, /* Wait for some time */ PRES_STATE_LOCKED /* Presentation compensation locked */ }; @@ -137,7 +138,7 @@ static struct { uint32_t current_pres_dly_us; struct { - enum drift_comp_state state : 8; + enum drift_comp_state state: 8; uint16_t ctr; /* Count func calls. Used for waiting */ uint32_t meas_start_time_us; uint32_t center_freq; @@ -145,7 +146,7 @@ static struct { } drift_comp; struct { - enum pres_comp_state state : 8; + enum pres_comp_state state: 8; uint16_t ctr; /* Count func calls. Used for collecting data points and waiting */ int32_t sum_err_dly_us; uint32_t pres_delay_us; @@ -554,6 +555,8 @@ static void alt_buffer_free_both(void) * New I2S RX data is located in rx_buf_released, and is locked into * the in.fifo message queue. */ + +uint8_t sound_mix_buf[BLK_MONO_SIZE_OCTETS]; static void audio_datapath_i2s_blk_complete(uint32_t frame_start_ts, uint32_t *rx_buf_released, uint32_t const *tx_buf_released) { @@ -850,18 +853,21 @@ void audio_datapath_stream_out(const uint8_t *buf, size_t size, uint32_t sdu_ref int ret; size_t pcm_size; - ret = sw_codec_decode(buf, size, bad_frame, &ctrl_blk.decoded_data, &pcm_size); + ret = sw_codec_decode(buf, size, bad_frame, &ctrl_blk.decoded_data, &pcm_size, true); if (ret) { LOG_WRN("SW codec decode error: %d", ret); } + if (IS_ENABLED(CONFIG_LC3_PLAYBACK) && lc3_playback_is_active()) { + lc3_playback_mix_with_stream(ctrl_blk.decoded_data, pcm_size); + } + if (pcm_size != (BLK_STEREO_SIZE_OCTETS * NUM_BLKS_IN_FRAME)) { LOG_WRN("Decoded audio has wrong size"); /* Discard frame */ return; } - /*** Add audio data to FIFO buffer ***/ int32_t num_blks_in_fifo = ctrl_blk.out.prod_blk_idx - ctrl_blk.out.cons_blk_idx; @@ -1071,24 +1077,23 @@ static int cmd_audio_pres_comp_disable(const struct shell *shell, size_t argc, c return 0; } -SHELL_STATIC_SUBCMD_SET_CREATE( - test_cmd, - SHELL_COND_CMD(CONFIG_SHELL, nrf_tone_start, NULL, "Start local tone from nRF5340", - cmd_i2s_tone_play), - SHELL_COND_CMD(CONFIG_SHELL, nrf_tone_stop, NULL, "Stop local tone from nRF5340", - cmd_i2s_tone_stop), - SHELL_COND_CMD(CONFIG_SHELL, pll_drift_comp_enable, NULL, - "Enable audio PLL auto drift compensation (default)", - cmd_hfclkaudio_drift_comp_enable), - SHELL_COND_CMD(CONFIG_SHELL, pll_drift_comp_disable, NULL, - "Disable audio PLL auto drift compensation", - cmd_hfclkaudio_drift_comp_disable), - SHELL_COND_CMD(CONFIG_SHELL, pll_pres_comp_enable, NULL, - "Enable audio presentation compensation (default)", - cmd_audio_pres_comp_enable), - SHELL_COND_CMD(CONFIG_SHELL, pll_pres_comp_disable, NULL, - "Disable audio presentation compensation", - cmd_audio_pres_comp_disable), - SHELL_SUBCMD_SET_END); +SHELL_STATIC_SUBCMD_SET_CREATE(test_cmd, + SHELL_COND_CMD(CONFIG_SHELL, nrf_tone_start, NULL, + "Start local tone from nRF5340", cmd_i2s_tone_play), + SHELL_COND_CMD(CONFIG_SHELL, nrf_tone_stop, NULL, + "Stop local tone from nRF5340", cmd_i2s_tone_stop), + SHELL_COND_CMD(CONFIG_SHELL, pll_drift_comp_enable, NULL, + "Enable audio PLL auto drift compensation (default)", + cmd_hfclkaudio_drift_comp_enable), + SHELL_COND_CMD(CONFIG_SHELL, pll_drift_comp_disable, NULL, + "Disable audio PLL auto drift compensation", + cmd_hfclkaudio_drift_comp_disable), + SHELL_COND_CMD(CONFIG_SHELL, pll_pres_comp_enable, NULL, + "Enable audio presentation compensation (default)", + cmd_audio_pres_comp_enable), + SHELL_COND_CMD(CONFIG_SHELL, pll_pres_comp_disable, NULL, + "Disable audio presentation compensation", + cmd_audio_pres_comp_disable), + SHELL_SUBCMD_SET_END); SHELL_CMD_REGISTER(test, &test_cmd, "Test mode commands", NULL); diff --git a/applications/nrf5340_audio/src/audio/audio_system.c b/applications/nrf5340_audio/src/audio/audio_system.c index b2a763de2b82..b06d32349cdf 100644 --- a/applications/nrf5340_audio/src/audio/audio_system.c +++ b/applications/nrf5340_audio/src/audio/audio_system.c @@ -21,6 +21,8 @@ #include "pcm_stream_channel_modifier.h" #include "audio_usb.h" #include "streamctrl.h" +#include "pcm_mix.h" +#include #include LOG_MODULE_REGISTER(audio_system, CONFIG_AUDIO_SYSTEM_LOG_LEVEL); @@ -90,7 +92,7 @@ static void audio_headset_configure(void) } #endif /* (CONFIG_STREAM_BIDIRECTIONAL) */ - sw_codec_cfg.decoder.num_ch = SW_CODEC_MONO; + sw_codec_cfg.decoder.num_ch = IS_ENABLED(CONFIG_LC3_PLAYBACK) ? 2 : SW_CODEC_MONO; sw_codec_cfg.decoder.enabled = true; } @@ -240,12 +242,11 @@ int audio_decode(void const *const encoded_data, size_t encoded_data_size, bool } ret = sw_codec_decode(encoded_data, encoded_data_size, bad_frame, &pcm_raw_data, - &pcm_block_size); + &pcm_block_size, true); if (ret) { LOG_ERR("Failed to decode"); return ret; } - /* Split decoded frame into CONFIG_FIFO_FRAME_SPLIT_NUM blocks */ for (int i = 0; i < CONFIG_FIFO_FRAME_SPLIT_NUM; i++) { memcpy(tmp_pcm_raw_data[i], (char *)pcm_raw_data + (i * (BLOCK_SIZE_BYTES)), @@ -303,11 +304,10 @@ void audio_system_start(void) sw_codec_cfg.initialized = true; if (sw_codec_cfg.encoder.enabled && encoder_thread_id == NULL) { - encoder_thread_id = - k_thread_create(&encoder_thread_data, encoder_thread_stack, - CONFIG_ENCODER_STACK_SIZE, (k_thread_entry_t)encoder_thread, - NULL, NULL, NULL, - K_PRIO_PREEMPT(CONFIG_ENCODER_THREAD_PRIO), 0, K_NO_WAIT); + encoder_thread_id = k_thread_create( + &encoder_thread_data, encoder_thread_stack, CONFIG_ENCODER_STACK_SIZE, + (k_thread_entry_t)encoder_thread, NULL, NULL, NULL, + K_PRIO_PREEMPT(CONFIG_ENCODER_THREAD_PRIO), 0, K_NO_WAIT); ret = k_thread_name_set(encoder_thread_id, "ENCODER"); ERR_CHK(ret); } diff --git a/applications/nrf5340_audio/src/audio/sw_codec_select.c b/applications/nrf5340_audio/src/audio/sw_codec_select.c index d1aff99ca0e7..1eeef9bfd8ac 100644 --- a/applications/nrf5340_audio/src/audio/sw_codec_select.c +++ b/applications/nrf5340_audio/src/audio/sw_codec_select.c @@ -19,11 +19,17 @@ LOG_MODULE_REGISTER(sw_codec_select, CONFIG_SW_CODEC_SELECT_LOG_LEVEL); static struct sw_codec_config m_config; +static bool initialized; + +bool sw_codec_is_initialized(void) +{ + return initialized; +} int sw_codec_encode(void *pcm_data, size_t pcm_size, uint8_t **encoded_data, size_t *encoded_size) { /* Temp storage for split stereo PCM signal */ - char pcm_data_mono[AUDIO_CH_NUM][PCM_NUM_BYTES_MONO] = { 0 }; + char pcm_data_mono[AUDIO_CH_NUM][PCM_NUM_BYTES_MONO] = {0}; /* Make sure we have enough space for two frames (stereo) */ static uint8_t m_encoded_data[ENC_MAX_FRAME_SIZE * AUDIO_CH_NUM]; @@ -101,7 +107,7 @@ int sw_codec_encode(void *pcm_data, size_t pcm_size, uint8_t **encoded_data, siz } int sw_codec_decode(uint8_t const *const encoded_data, size_t encoded_size, bool bad_frame, - void **decoded_data, size_t *decoded_size) + void **decoded_data, size_t *decoded_size, bool lc3_playback_is_active) { if (!m_config.decoder.enabled) { LOG_ERR("Decoder has not been initialized"); @@ -109,7 +115,7 @@ int sw_codec_decode(uint8_t const *const encoded_data, size_t encoded_size, bool } int ret; - char pcm_data_mono[PCM_NUM_BYTES_MONO] = { 0 }; + char pcm_data_mono[PCM_NUM_BYTES_MONO] = {0}; static char pcm_data_stereo[PCM_NUM_BYTES_STEREO]; size_t pcm_size_stereo = 0; @@ -119,18 +125,16 @@ int sw_codec_decode(uint8_t const *const encoded_data, size_t encoded_size, bool case SW_CODEC_LC3: { #if (CONFIG_SW_CODEC_LC3) /* Typically used for right channel if stereo signal */ - char pcm_data_mono_right[PCM_NUM_BYTES_MONO] = { 0 }; + char pcm_data_mono_right[PCM_NUM_BYTES_MONO] = {0}; - switch (m_config.decoder.num_ch) { - case SW_CODEC_MONO: { + if (m_config.decoder.num_ch == SW_CODEC_MONO) { if (bad_frame && IS_ENABLED(CONFIG_SW_CODEC_OVERRIDE_PLC)) { memset(pcm_data_mono, 0, PCM_NUM_BYTES_MONO); pcm_size_session = PCM_NUM_BYTES_MONO; } else { - ret = sw_codec_lc3_dec_run(encoded_data, encoded_size, - LC3_PCM_NUM_BYTES_MONO, 0, pcm_data_mono, - (uint16_t *)&pcm_size_session, - bad_frame); + ret = sw_codec_lc3_dec_run( + encoded_data, encoded_size, LC3_PCM_NUM_BYTES_MONO, 0, + pcm_data_mono, (uint16_t *)&pcm_size_session, bad_frame); if (ret) { return ret; } @@ -146,29 +150,25 @@ int sw_codec_decode(uint8_t const *const encoded_data, size_t encoded_size, bool if (ret) { return ret; } - break; - } - case SW_CODEC_STEREO: { + } else if (m_config.decoder.num_ch == SW_CODEC_STEREO && !lc3_playback_is_active) { if (bad_frame && IS_ENABLED(CONFIG_SW_CODEC_OVERRIDE_PLC)) { memset(pcm_data_mono, 0, PCM_NUM_BYTES_MONO); memset(pcm_data_mono_right, 0, PCM_NUM_BYTES_MONO); pcm_size_session = PCM_NUM_BYTES_MONO; } else { /* Decode left channel */ - ret = sw_codec_lc3_dec_run(encoded_data, encoded_size / 2, - LC3_PCM_NUM_BYTES_MONO, AUDIO_CH_L, - pcm_data_mono, - (uint16_t *)&pcm_size_session, - bad_frame); + ret = sw_codec_lc3_dec_run( + encoded_data, encoded_size / 2, LC3_PCM_NUM_BYTES_MONO, + AUDIO_CH_L, pcm_data_mono, (uint16_t *)&pcm_size_session, + bad_frame); if (ret) { return ret; } /* Decode right channel */ - ret = sw_codec_lc3_dec_run((encoded_data + (encoded_size / 2)), - encoded_size / 2, LC3_PCM_NUM_BYTES_MONO, - AUDIO_CH_R, pcm_data_mono_right, - (uint16_t *)&pcm_size_session, - bad_frame); + ret = sw_codec_lc3_dec_run( + (encoded_data + (encoded_size / 2)), encoded_size / 2, + LC3_PCM_NUM_BYTES_MONO, AUDIO_CH_R, pcm_data_mono_right, + (uint16_t *)&pcm_size_session, bad_frame); if (ret) { return ret; } @@ -179,13 +179,33 @@ int sw_codec_decode(uint8_t const *const encoded_data, size_t encoded_size, bool if (ret) { return ret; } - break; - } - default: + } else if (m_config.decoder.num_ch == SW_CODEC_STEREO && lc3_playback_is_active) { + if (bad_frame && IS_ENABLED(CONFIG_SW_CODEC_OVERRIDE_PLC)) { + memset(pcm_data_mono, 0, PCM_NUM_BYTES_MONO); + pcm_size_session = PCM_NUM_BYTES_MONO; + } else { + ret = sw_codec_lc3_dec_run( + encoded_data, encoded_size, LC3_PCM_NUM_BYTES_MONO, 0, + pcm_data_mono, (uint16_t *)&pcm_size_session, bad_frame); + if (ret) { + return ret; + } + } + + /* For now, i2s is only stereo, so in order to send + * just one channel, we need to insert 0 for the + * other channel + */ + ret = pscm_zero_pad(pcm_data_mono, pcm_size_session, + m_config.decoder.audio_ch, CONFIG_AUDIO_BIT_DEPTH_BITS, + pcm_data_stereo, &pcm_size_stereo); + if (ret) { + return ret; + } + } else { LOG_ERR("Unsupported number of channels: %d", m_config.encoder.num_ch); return -ENODEV; } - *decoded_size = pcm_size_stereo; *decoded_data = pcm_data_stereo; #endif /* (CONFIG_SW_CODEC_LC3) */ @@ -288,10 +308,9 @@ int sw_codec_init(struct sw_codec_config sw_codec_cfg) CONFIG_AUDIO_SAMPLE_RATE_HZ, CONFIG_AUDIO_BIT_DEPTH_BITS, CONFIG_AUDIO_FRAME_DURATION_US, sw_codec_cfg.decoder.num_ch); - ret = sw_codec_lc3_dec_init(CONFIG_AUDIO_SAMPLE_RATE_HZ, - CONFIG_AUDIO_BIT_DEPTH_BITS, - CONFIG_AUDIO_FRAME_DURATION_US, - sw_codec_cfg.decoder.num_ch); + ret = sw_codec_lc3_dec_init( + CONFIG_AUDIO_SAMPLE_RATE_HZ, CONFIG_AUDIO_BIT_DEPTH_BITS, + CONFIG_AUDIO_FRAME_DURATION_US, sw_codec_cfg.decoder.num_ch); if (ret) { return ret; @@ -308,5 +327,6 @@ int sw_codec_init(struct sw_codec_config sw_codec_cfg) } m_config = sw_codec_cfg; + initialized = true; return 0; } diff --git a/applications/nrf5340_audio/src/audio/sw_codec_select.h b/applications/nrf5340_audio/src/audio/sw_codec_select.h index bc46b8991752..e76fc8f923a2 100644 --- a/applications/nrf5340_audio/src/audio/sw_codec_select.h +++ b/applications/nrf5340_audio/src/audio/sw_codec_select.h @@ -11,7 +11,7 @@ #include "channel_assignment.h" #if (CONFIG_SW_CODEC_LC3) -#define LC3_MAX_FRAME_SIZE_MS 10 +#define LC3_MAX_FRAME_SIZE_MS 10 #define LC3_ENC_MONO_FRAME_SIZE (CONFIG_LC3_BITRATE * LC3_MAX_FRAME_SIZE_MS / (8 * 1000)) #define LC3_PCM_NUM_BYTES_MONO \ @@ -20,16 +20,16 @@ #define LC3_DEC_TIME_US 1500 #else #define LC3_ENC_MONO_FRAME_SIZE 0 -#define LC3_PCM_NUM_BYTES_MONO 0 -#define LC3_ENC_TIME_US 0 -#define LC3_DEC_TIME_US 0 +#define LC3_PCM_NUM_BYTES_MONO 0 +#define LC3_ENC_TIME_US 0 +#define LC3_DEC_TIME_US 0 #endif /* CONFIG_SW_CODEC_LC3 */ /* Max will be used when multiple codecs are supported */ -#define ENC_MAX_FRAME_SIZE MAX(LC3_ENC_MONO_FRAME_SIZE, 0) -#define ENC_TIME_US MAX(LC3_ENC_TIME_US, 0) -#define DEC_TIME_US MAX(LC3_DEC_TIME_US, 0) -#define PCM_NUM_BYTES_MONO MAX(LC3_PCM_NUM_BYTES_MONO, 0) +#define ENC_MAX_FRAME_SIZE MAX(LC3_ENC_MONO_FRAME_SIZE, 0) +#define ENC_TIME_US MAX(LC3_ENC_TIME_US, 0) +#define DEC_TIME_US MAX(LC3_DEC_TIME_US, 0) +#define PCM_NUM_BYTES_MONO MAX(LC3_PCM_NUM_BYTES_MONO, 0) #define PCM_NUM_BYTES_STEREO (PCM_NUM_BYTES_MONO * 2) enum sw_codec_select { @@ -39,7 +39,7 @@ enum sw_codec_select { enum sw_codec_num_ch { SW_CODEC_ZERO_CHANNELS, - SW_CODEC_MONO, /* Only use one channel */ + SW_CODEC_MONO, /* Only use one channel */ SW_CODEC_STEREO, /* Use both channels */ }; @@ -59,12 +59,17 @@ struct sw_codec_decoder { /** @brief Sw_codec configuration structure */ struct sw_codec_config { - enum sw_codec_select sw_codec; /* sw_codec to be used, e.g. LC3, etc */ + enum sw_codec_select sw_codec; /* sw_codec to be used, e.g. LC3, etc */ struct sw_codec_decoder decoder; /* Struct containing settings for decoder */ struct sw_codec_encoder encoder; /* Struct containing settings for encoder */ - bool initialized; /* Status of codec */ + bool initialized; /* Status of codec */ }; +/** @brief Check if sw_codec is initialized + * @return true, if initialized. false, otherwise + */ +bool sw_codec_is_initialized(void); + /**@brief Encode PCM data and output encoded data * * @note Takes in stereo PCM stream, will encode either one or two @@ -90,7 +95,7 @@ int sw_codec_encode(void *pcm_data, size_t pcm_size, uint8_t **encoded_data, siz * @return 0 if success, error codes depends on sw_codec selected */ int sw_codec_decode(uint8_t const *const encoded_data, size_t encoded_size, bool bad_frame, - void **pcm_data, size_t *pcm_size); + void **decoded_data, size_t *decoded_size, bool lc3_playback_is_active); /**@brief Uninitialize sw_codec and free allocated space * diff --git a/applications/nrf5340_audio/src/main.c b/applications/nrf5340_audio/src/main.c index 864ac4750f48..3679f504ebcd 100644 --- a/applications/nrf5340_audio/src/main.c +++ b/applications/nrf5340_audio/src/main.c @@ -25,6 +25,7 @@ #include "audio_system.h" #include "channel_assignment.h" #include "streamctrl.h" +#include "lc3_playback.h" #if defined(CONFIG_AUDIO_DFU_ENABLE) #include "dfu_entry.h" @@ -227,4 +228,8 @@ int main(void) ret = streamctrl_start(); ERR_CHK(ret); + + if (IS_ENABLED(CONFIG_LC3_PLAYBACK)) { + lc3_playback_init(); + } } diff --git a/applications/nrf5340_audio/src/modules/CMakeLists.txt b/applications/nrf5340_audio/src/modules/CMakeLists.txt index d1865c09e84e..a299c18253b5 100644 --- a/applications/nrf5340_audio/src/modules/CMakeLists.txt +++ b/applications/nrf5340_audio/src/modules/CMakeLists.txt @@ -13,6 +13,7 @@ target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/led.c ${CMAKE_CURRENT_SOURCE_DIR}/power_meas.c ${CMAKE_CURRENT_SOURCE_DIR}/sd_card.c + ${CMAKE_CURRENT_SOURCE_DIR}/lc3_playback.c ) if (CONFIG_AUDIO_DFU_ENABLE) diff --git a/applications/nrf5340_audio/src/modules/Kconfig b/applications/nrf5340_audio/src/modules/Kconfig index 4afdafbaa9ab..5816ed1c6c17 100644 --- a/applications/nrf5340_audio/src/modules/Kconfig +++ b/applications/nrf5340_audio/src/modules/Kconfig @@ -116,4 +116,9 @@ config BUTTON_PUBLISH_STACK_SIZE default 300 endmenu # Stack sizes +menu "Lc3 playback" +config LC3_PLAYBACK + bool "If true, lc3 playback is enabled" + default n +endmenu # Lc3 playback endmenu # Modules diff --git a/applications/nrf5340_audio/src/modules/lc3_playback.c b/applications/nrf5340_audio/src/modules/lc3_playback.c new file mode 100644 index 000000000000..a8a35acaa9f7 --- /dev/null +++ b/applications/nrf5340_audio/src/modules/lc3_playback.c @@ -0,0 +1,229 @@ +#include "lc3_playback.h" +#include +#include +#include "sd_card.h" +#include "hw_codec.h" +#include "sw_codec_lc3.h" +#include "sw_codec_select.h" +#include "pcm_stream_channel_modifier.h" +#include "pcm_mix.h" +#include "audio_i2s.h" +#include "sw_codec_lc3.h" + +#include +LOG_MODULE_REGISTER(lc3_playback, 4); /* For draft PR feedback: going to change this line */ + +#define RING_BUF_SIZE 960 /* This can be modified */ +#define lc3_playback_STACK_SIZE 4096 + +/*File structure of the LC3 encoded files*/ +struct lc3BinaryHdr_t { + uint16_t fileId; /* Constant value, 0xCC1C */ + uint16_t hdrSize; /* Header size, 0x0012 */ + uint16_t sampleRate_divided100; /* Sample frequency / 100 */ + uint16_t bitRate_divided100; /* Bit rate / 100 (total for all channels) */ + uint16_t channels; /* Number of channels */ + uint16_t frameMs_times100; /* Frame duration in ms * 100 */ + uint16_t rfu; /* RFU value */ + uint16_t signalLen; /* Number of samples in signal, 16 LSB */ + uint16_t signalLenRed; /* Number of samples in signal, 16 MSB (>> 16) */ +}; + +static struct lc3BinaryHdr_t header; +static size_t header_size = sizeof(header); +static uint16_t lc3_frame_length; +static uint16_t lc3_frames_num; +static uint16_t pcm_mono_frame_size; +static bool lc3_playback_active; + +RING_BUF_DECLARE(m_ringbuf_sound_data_lc3, RING_BUF_SIZE); +K_SEM_DEFINE(m_sem_load_from_buf_lc3, 1, 1); +K_MUTEX_DEFINE(mtx_ringbuf); + +static struct k_thread lc3_playback_thread_data; +static k_tid_t lc3_playback_thread_id; + +K_THREAD_STACK_DEFINE(lc3_playback_thread_stack, lc3_playback_STACK_SIZE); + +bool lc3_playback_is_active(void) +{ + return lc3_playback_active; +} + +int lc3_playback_buffer_set(uint8_t *buf, size_t size) +{ + uint16_t size_gotten; + + k_mutex_lock(&mtx_ringbuf, K_FOREVER); + size_gotten = ring_buf_get(&m_ringbuf_sound_data_lc3, buf, size); + k_mutex_unlock(&mtx_ringbuf); + if (ring_buf_space_get(&m_ringbuf_sound_data_lc3) >= pcm_mono_frame_size) { + k_sem_give(&m_sem_load_from_buf_lc3); + } + return 0; +} + +int lc3_playback_mix_with_stream(void *const pcm_a, size_t pcm_a_size) +{ + int ret; + uint8_t pcm_b[pcm_mono_frame_size]; + + ret = lc3_playback_buffer_set(pcm_b, pcm_mono_frame_size); + if (ret < 0) { + LOG_ERR("Error when loading pcm data into buffer. Ret: %d", ret); + return ret; + } + pcm_mix(pcm_a, pcm_a_size, pcm_b, pcm_mono_frame_size, B_MONO_INTO_A_STEREO_L); + return 0; +} + +static int lc3_playback_buffer_to_ringbuffer(uint8_t *buffer, size_t numbytes) +{ + /* Burde kanskje ha en sjekk her for å sjekke at numbytes gir mening */ + static uint8_t *buf_ptr; + + k_mutex_lock(&mtx_ringbuf, K_FOREVER); + numbytes = ring_buf_put_claim(&m_ringbuf_sound_data_lc3, &buf_ptr, numbytes); + + for (int i = 0; i < numbytes; i++) { + buf_ptr[i] = buffer[i]; + } + ring_buf_put_finish(&m_ringbuf_sound_data_lc3, numbytes); + k_mutex_unlock(&mtx_ringbuf); + return numbytes; +} + +static int lc3_playback_header_read(const char *filename, const char *path_to_file) +{ + LOG_DBG("In header read"); + int ret; + size_t lc3_frame_length_size = sizeof(lc3_frame_length); + uint32_t num_samples; + + ret = sd_card_segment_open(filename, path_to_file); + if (ret < 0) { + LOG_ERR("Error when trying to open file on SD card. Return value: %d", ret); + return ret; + } + + /* Read the header */ + ret = sd_card_segment_read((char *)&header, &header_size); + if (ret < 0) { + LOG_ERR("Error when trying to peek at file segment on SD card. Return value: %d", + ret); + return ret; + } + + pcm_mono_frame_size = 2 * header.sampleRate_divided100 * header.frameMs_times100 / 1000; + num_samples = (header.signalLenRed << 16) + header.signalLen; + lc3_frames_num = 2 * num_samples / pcm_mono_frame_size; + + /* Read the frame length */ + ret = sd_card_segment_peek((char *)&lc3_frame_length, &lc3_frame_length_size); + if (ret < 0) { + LOG_ERR("Error when trying to peek at file segment on SD card. Return value: %d", + ret); + return ret; + } + + ret = sd_card_segment_close(); + if (ret < 0) { + LOG_ERR("Error when trying to close file on SD card. Return value: %d", ret); + return ret; + } + + return 0; +} + +static int lc3_playback_play(const char *filename, const char *path_to_file) +{ + int ret; + + ret = lc3_playback_header_read(filename, path_to_file); + if (ret < 0) { + LOG_ERR("Audio Lc3 header read failed. Return value: %d", ret); + return ret; + } + + uint16_t pcm_mono_frame[pcm_mono_frame_size / 2]; + uint16_t pcm_mono_write_size; + uint16_t lc3_frame[lc3_frame_length]; + size_t lc3_fr_len = lc3_frame_length; /* Converting to size_t */ + size_t lc3_fr_len_size = sizeof(lc3_frame_length); + + /* First, open file on SD card */ + ret = sd_card_segment_open(filename, path_to_file); + if (ret < 0) { + LOG_ERR("Error when trying to open file on SD card. Return value: %d", ret); + return ret; + } + + /* Then, skip the header */ + ret = sd_card_segment_skip(&header_size); + if (ret < 0) { + LOG_ERR("Error when trying to open file on SD card. Return value: %d", ret); + return ret; + } + lc3_playback_active = true; + for (uint32_t i = 0; i < lc3_frames_num; i++) { + + /* Skip the frame length info to get to the audio data */ + ret = sd_card_segment_skip(&lc3_fr_len_size); + if (ret < 0) { + LOG_ERR("Error when trying to skip file on SD card. Return value: %d", ret); + return ret; + } + + /* Read the audio data frame to be encoded */ + ret = sd_card_segment_read((char *)lc3_frame, &lc3_fr_len); + if (ret < 0) { + LOG_ERR("Something went wrong when reading from SD card. Return value: %d", + ret); + return ret; + } + + /* Decode audio data frame*/ + + ret = sw_codec_lc3_dec_run((char *)lc3_frame, lc3_frame_length, pcm_mono_frame_size, + 1, pcm_mono_frame, &pcm_mono_write_size, false); + if (ret < 0) { + LOG_ERR("Error when running decoder. Return value: %d\n", ret); + return ret; + } + + /* Wait until there is enough space in the ringbuffer */ + k_sem_take(&m_sem_load_from_buf_lc3, K_FOREVER); + lc3_playback_buffer_to_ringbuffer((char *)pcm_mono_frame, pcm_mono_write_size); + } + ret = sd_card_segment_close(); + if (ret < 0) { + LOG_ERR("Error when closing file. Return value: %d", ret); + return ret; + } + lc3_playback_active = false; + return 0; +} + +static void lc3_playback_thread(void *arg1, void *arg2, void *arg3) +{ + while (!sw_codec_is_initialized()) { + k_msleep(100); + } + lc3_playback_play("enc_21.bin", ""); +} + +int lc3_playback_init(void) +{ + int ret; + + lc3_playback_thread_id = + k_thread_create(&lc3_playback_thread_data, lc3_playback_thread_stack, + lc3_playback_STACK_SIZE, (k_thread_entry_t)lc3_playback_thread, + NULL, NULL, NULL, K_PRIO_PREEMPT(5), 0, K_NO_WAIT); + ret = k_thread_name_set(lc3_playback_thread_id, "lc3_playback"); + if (ret < 0) { + LOG_ERR("Failed"); + return ret; + } + return 0; +} diff --git a/applications/nrf5340_audio/src/modules/lc3_playback.h b/applications/nrf5340_audio/src/modules/lc3_playback.h new file mode 100644 index 000000000000..be0d4b534251 --- /dev/null +++ b/applications/nrf5340_audio/src/modules/lc3_playback.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef _LC3_PLAYBACK_H_ +#define _LC3_PLAYBACK_H_ + +#include + +/**@brief Figure out whether or not the lc3_playback module is active + * @retval true, if active. false, otherwise + */ +bool lc3_playback_is_active(void); + +/**@brief Mix pcm data from lc3_playback module with audio stream out + * param pcm_a Buffer into which to mix pcm data from lc3_module + * param size Size of input buffer + * @retval 0 on success + */ +int lc3_playback_mix_with_stream(void *const pcm_a, size_t pcm_a_size); + +/**@brief Initialize lc3_playback module. Create lc3_playback thread + * @retval 0 on success + * Otherwise, error from underlying drivers + */ +int lc3_playback_init(void); + +#endif diff --git a/applications/nrf5340_audio/tools/buildprog/nrf5340_audio_dk_devices.json b/applications/nrf5340_audio/tools/buildprog/nrf5340_audio_dk_devices.json index 0a42fe365d5b..0acda4753625 100644 --- a/applications/nrf5340_audio/tools/buildprog/nrf5340_audio_dk_devices.json +++ b/applications/nrf5340_audio/tools/buildprog/nrf5340_audio_dk_devices.json @@ -1,11 +1,11 @@ [ { - "nrf5340_audio_dk_snr": 1000, + "nrf5340_audio_dk_snr": 1050183438, "nrf5340_audio_dk_dev": "headset", "channel": "left" }, { - "nrf5340_audio_dk_snr": 1000, + "nrf5340_audio_dk_snr": 1050139103, "nrf5340_audio_dk_dev": "gateway", "channel": "NA" },