diff --git a/prj.conf b/prj.conf index 8389b667..85995a01 100644 --- a/prj.conf +++ b/prj.conf @@ -127,6 +127,7 @@ CONFIG_BT_USER_PHY_UPDATE=y CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 CONFIG_BT_BUF_ACL_RX_SIZE=502 +CONFIG_BT_BUF_ACL_RX_COUNT_EXTRA=8 CONFIG_BT_BUF_ACL_TX_SIZE=502 CONFIG_BT_L2CAP_TX_MTU=498 CONFIG_BT_CTLR_PHY_2M=y diff --git a/prj_fota.conf b/prj_fota.conf index 1bbfb9a9..36cb645f 100644 --- a/prj_fota.conf +++ b/prj_fota.conf @@ -136,6 +136,7 @@ CONFIG_BT_USER_PHY_UPDATE=y CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 CONFIG_BT_BUF_ACL_RX_SIZE=502 +CONFIG_BT_BUF_ACL_RX_COUNT_EXTRA=8 CONFIG_BT_BUF_ACL_TX_SIZE=502 CONFIG_BT_L2CAP_TX_MTU=498 CONFIG_BT_CTLR_PHY_2M=y diff --git a/src/audio/audio_datapath.c b/src/audio/audio_datapath.c index fddae9ee..25b63b2f 100644 --- a/src/audio/audio_datapath.c +++ b/src/audio/audio_datapath.c @@ -222,8 +222,14 @@ static void (*buffer_play_callback)(void) = NULL; extern struct k_poll_signal encoder_sig; extern struct k_poll_event logger_sig; -/* Decimation buffer for SD card logging */ -static int16_t decimated_audio[BLOCK_SIZE_BYTES / sizeof(int16_t) / 4]; /* /4 for decimation factor 4 */ +/* + * Decimation output for one interleaved stereo audio block. + * + * The decimator supports factors down to 1, so its worst-case output contains + * every input sample. Seal check currently uses factor 3 (48 kHz -> 16 kHz); + * sizing this buffer for the old fixed factor 4 overflowed it on every block. + */ +static int16_t decimated_audio[BLOCK_SIZE_BYTES / sizeof(int16_t)]; // Funktion für den neuen Thread static void data_thread(void *arg1, void *arg2, void *arg3) diff --git a/src/audio/decimation_filter.cpp b/src/audio/decimation_filter.cpp index e7cc4b2d..36af29fa 100644 --- a/src/audio/decimation_filter.cpp +++ b/src/audio/decimation_filter.cpp @@ -211,7 +211,9 @@ int CascadedDecimator::process(const int16_t* input, int16_t* output, uint32_t n } if (num_stages_ == 0) { - memcpy(output, input, num_frames * 2 * sizeof(int16_t)); + if (output != input) { + memcpy(output, input, num_frames * 2U * sizeof(int16_t)); + } return num_frames; } diff --git a/src/audio/decimation_filter.h b/src/audio/decimation_filter.h index 2fd57353..a3f0d5d9 100644 --- a/src/audio/decimation_filter.h +++ b/src/audio/decimation_filter.h @@ -142,8 +142,8 @@ class CascadedDecimator { uint8_t num_stages_; bool configured_; Decimator stages_[MAX_STAGES]; - /* Stage output alternates between this buffer and the caller's output. */ - int16_t intermediate_buffer_[MAX_FRAMES]; + /* Stage output is interleaved stereo, so each frame needs two samples. */ + int16_t intermediate_buffer_[MAX_FRAMES * 2U]; float32_t processing_buffer_[MAX_FRAMES * 2]; }; #endif diff --git a/src/bluetooth/gatt_services/audio_response_service.c b/src/bluetooth/gatt_services/audio_response_service.c index f63634d8..a8b71ed6 100644 --- a/src/bluetooth/gatt_services/audio_response_service.c +++ b/src/bluetooth/gatt_services/audio_response_service.c @@ -14,13 +14,14 @@ #include "audio_system.h" #include "hw_codec.h" -LOG_MODULE_REGISTER(audio_response_service, LOG_LEVEL_DBG); +LOG_MODULE_REGISTER(audio_response_service, CONFIG_LOG_DEFAULT_LEVEL); #define AUDIO_RESPONSE_CAPTURE_SAMPLES 2048 #define AUDIO_RESPONSE_CAPTURE_SAMPLE_RATE 4000U #define AUDIO_RESPONSE_INITIAL_DROP 128 #define AUDIO_RESPONSE_DEFAULT_POINTS 9 #define AUDIO_RESPONSE_TRANSFER_CREDITS 1 +#define AUDIO_RESPONSE_TRANSFER_CREDIT_DELAY K_MSEC(1) #define AUDIO_RESPONSE_TRANSFER_TIMEOUT K_SECONDS(30) #define AUDIO_RESPONSE_STATUS_ENCODED_SIZE 9 #define AUDIO_RESPONSE_RESULT_ENCODED_SIZE (2 + (CONFIG_AUDIO_RESPONSE_MAX_POINTS * 4)) @@ -88,6 +89,7 @@ static bool result_notifications_enabled; static struct k_work measurement_work; static struct k_work measurement_complete_work; +static struct k_work_delayable transfer_ready_work; static struct k_work_delayable transfer_timeout_work; K_MUTEX_DEFINE(service_mutex); @@ -249,6 +251,7 @@ static void reset_transfer(void) { LOG_DBG("Reset transfer state: id=%u active=%d committed=%d received=%u/%u", transfer.id, transfer.active, transfer.committed, transfer.received_samples, transfer.total_samples); + k_work_cancel_delayable(&transfer_ready_work); memset(&transfer, 0, sizeof(transfer)); } @@ -312,11 +315,12 @@ static ssize_t start_transfer(const audio_response_transfer_start_t *start, uint LOG_INF("Transfer start requested: id=%u samples=%u rate=%u checksum=0x%08x", start->transfer_id, start->total_samples, start->sampling_rate, start->checksum); - if (measurement_active || transfer.active || start->total_samples == 0 || + if (measurement_active || (transfer.active && !transfer.committed) || + start->total_samples == 0 || start->sampling_rate != CONFIG_AUDIO_SAMPLE_RATE_HZ) { - LOG_WRN("Transfer start rejected: measurement_active=%d active=%d samples=%u rate=%u expected_rate=%u", - measurement_active, transfer.active, start->total_samples, start->sampling_rate, - CONFIG_AUDIO_SAMPLE_RATE_HZ); + LOG_WRN("Transfer start rejected: measurement_active=%d active=%d committed=%d samples=%u rate=%u expected_rate=%u", + measurement_active, transfer.active, transfer.committed, start->total_samples, + start->sampling_rate, CONFIG_AUDIO_SAMPLE_RATE_HZ); return reject_transfer(AUDIO_RESPONSE_TRANSFER_INVALID_STATE, BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED)); } @@ -541,7 +545,7 @@ static ssize_t write_transfer_data(struct bt_conn *conn, const struct bt_gatt_at } transfer.received_samples += sample_count; k_work_reschedule(&transfer_timeout_work, AUDIO_RESPONSE_TRANSFER_TIMEOUT); - (void)notify_transfer_status(AUDIO_RESPONSE_TRANSFER_READY, AUDIO_RESPONSE_TRANSFER_CREDITS); + k_work_reschedule(&transfer_ready_work, AUDIO_RESPONSE_TRANSFER_CREDIT_DELAY); if (transfer.received_samples == transfer.total_samples) { LOG_INF("Transfer upload complete: id=%u samples=%u", transfer.id, transfer.total_samples); @@ -759,13 +763,18 @@ static void measurement_work_handler(struct k_work *work) } LOG_DBG("Audio datapath acquired: id=%u", pending_config.id); audio_session.datapath_acquired = true; - audio_session.measurement_codec_enabled = true; ret = hw_codec_default_conf_enable(); if (ret != 0) { LOG_ERR("Failed to enable codec for audio response measurement: %d", ret); goto fail; } + audio_session.measurement_codec_enabled = true; LOG_DBG("Codec enabled for audio response measurement: id=%u", pending_config.id); + ret = hw_codec_volume_unmute(); + if (ret != 0) { + LOG_ERR("Failed to unmute codec for audio response measurement: %d", ret); + goto fail; + } ret = audio_datapath_buffer_play(transfer.samples, transfer.total_samples, false, pending_config.volume, NULL); if (ret != 0) { @@ -849,6 +858,20 @@ static void measurement_complete_work_handler(struct k_work *work) LOG_DBG("Audio response measurement complete: id=%u", pending_config.id); } +/** + * Grant the next upload credit after the current GATT write has released its + * incoming ACL buffer. + */ +static void transfer_ready_work_handler(struct k_work *work) +{ + k_mutex_lock(&service_mutex, K_FOREVER); + if (transfer.active && !transfer.committed) { + (void)notify_transfer_status(AUDIO_RESPONSE_TRANSFER_READY, + AUDIO_RESPONSE_TRANSFER_CREDITS); + } + k_mutex_unlock(&service_mutex); +} + /** * Release an incomplete transfer after the protocol timeout. */ @@ -875,6 +898,7 @@ int init_audio_response_service(void) LOG_INF("Initializing audio response service"); k_work_init(&measurement_work, measurement_work_handler); k_work_init(&measurement_complete_work, measurement_complete_work_handler); + k_work_init_delayable(&transfer_ready_work, transfer_ready_work_handler); k_work_init_delayable(&transfer_timeout_work, transfer_timeout_work_handler); return 0; }