From 476dd43a6298c57a8f2cbce89ad0b642187b37cf Mon Sep 17 00:00:00 2001 From: shastry Date: Wed, 21 Feb 2024 21:56:36 +0530 Subject: [PATCH] Audio: aec: optimize acoustic echo cancellation processing This check-in proposal enhances the audio Echo Cancellation AEC) implementation. The enhancements seek to enhance loop designs and memory copy operations, reducing cycle consumption. When pointer arithmetic is substituted for array indexing, it expedites performance and enhances data processing. To maximize efficiency, data copy verification is done outside of the loop, and performance is increased by using pointer arithmetic instead of inner loops. Error management is included when the size of the destination buffer is exceeded by the source data. Before converting or copying data, the pointers "ref," "src," and "dst" are verified to be inside the valid range of "ref_buf," "src_buf," and "dst_buf." These changes improve the code's robustness and efficiency by adding the necessary error checks. Signed-off-by: shastry --- .../google/google_rtc_audio_processing.c | 330 +++++++++++++----- .../google/google_rtc_audio_processing_mock.c | 86 ++++- src/include/sof/audio/audio_stream.h | 43 ++- 3 files changed, 361 insertions(+), 98 deletions(-) diff --git a/src/audio/google/google_rtc_audio_processing.c b/src/audio/google/google_rtc_audio_processing.c index e1121c253e10..502fb2cd7a5c 100644 --- a/src/audio/google/google_rtc_audio_processing.c +++ b/src/audio/google/google_rtc_audio_processing.c @@ -63,6 +63,7 @@ struct google_rtc_audio_processing_comp_data { float *process_buffer_ptrs[SOF_IPC_MAX_CHANNELS]; #else /* CONFIG_COMP_GOOGLE_RTC_USE_32_BIT_FLOAT_API */ int16_t *aec_reference_buffer; + size_t aec_reference_buffer_size; /* Add this field to store the buffer size */ int16_t *process_buffer; #endif /* CONFIG_COMP_GOOGLE_RTC_USE_32_BIT_FLOAT_API */ @@ -87,6 +88,39 @@ struct google_rtc_audio_processing_comp_data { int raw_microphone_source; }; +/* Function prototype for google_rtc_safe_memcpy */ +int google_rtc_safe_memcpy(struct google_rtc_audio_processing_comp_data *cd, int16_t *dst, + int16_t *src, size_t num_bytes, size_t buffer_size, + size_t frame_index); + +/* Helper function to perform error checking and memcpy operation */ +int google_rtc_safe_memcpy(struct google_rtc_audio_processing_comp_data *cd, int16_t *dst, + int16_t *src, size_t num_bytes, size_t buffer_size, + size_t frame_index) +{ + /* Check if the source or destination pointers are null */ + if (!dst || !src) + return -EINVAL; + + /* Calculate the used and remaining space in the buffer */ + size_t buffer_used = frame_index * sizeof(int16_t); + size_t buffer_remaining = buffer_size - buffer_used; + + /* Check if the source data can fit in the destination buffer */ + if (num_bytes <= buffer_remaining) { + int16_t *buffer_start = dst; + size_t offset = frame_index * cd->num_capture_channels; + + /* Copy the source data to the destination buffer */ + buffer_start += offset; + memcpy(buffer_start, src, num_bytes); + return 0; + } + + /* The source data is too big to fit in the destination buffer */ + return -EINVAL; +} + void *GoogleRtcMalloc(size_t size) { return rballoc(0, SOF_MEM_CAPS_RAM, size); @@ -790,15 +824,34 @@ static int google_rtc_audio_processing_process(struct processing_module *mod, int8_t *dst_buf_end; size_t dst_buf_size; + struct google_rtc_audio_processing_comp_data *cd = module_get_private_data(mod); + + /* Determine the number of elements needed */ + size_t number_of_elements = cd->num_frames * cd->num_aec_reference_channels; + + /* Free the old buffer if it exists */ + if (cd->aec_reference_buffer) { + free(cd->aec_reference_buffer); + cd->aec_reference_buffer = NULL; + } + + /* Allocate the new buffer */ + cd->aec_reference_buffer = malloc(number_of_elements * sizeof(int16_t)); + + /* Check if the allocation was successful */ + if (!cd->aec_reference_buffer) { + /* Handle the error */ + return -ENOMEM; + } + + /* Update the buffer size */ + cd->aec_reference_buffer_size = number_of_elements * sizeof(int16_t); + size_t num_of_bytes_to_process; - size_t channel; - size_t buffer_offset; struct sof_source *ref_stream, *src_stream; struct sof_sink *dst_stream; - struct google_rtc_audio_processing_comp_data *cd = module_get_private_data(mod); - if (cd->reconfigure) { ret = google_rtc_audio_processing_reconfigure(mod); if (ret) @@ -822,23 +875,57 @@ static int google_rtc_audio_processing_process(struct processing_module *mod, /* 32float: de-interlace ref buffer, convert it to float, skip channels if > Max * 16int: linearize buffer, skip channels if > Max */ - buffer_offset = 0; - for (int i = 0; i < cd->num_frames; i++) { - for (channel = 0; channel < cd->num_aec_reference_channels; ++channel) { + /* Reduce cycle waste by streamlining the inner loop, + * converting from array indexing to pointer arithmetic, + * and putting data copy verification outside the loop. + */ + const int16_t *ref_data_end = ref + cd->num_frames * cd->num_aec_reference_channels; + + /* Check that ref is within the valid range of the ref_buf buffer */ + if (!ref || ref < (int16_t *)ref_buf_start || ref >= (int16_t *)ref_buf_end) { + /* ref does not point to valid int16_t data, + * return -EINVAL immediately to indicate an invalid argument was passed + */ + return -EINVAL; + } + #if CONFIG_COMP_GOOGLE_RTC_USE_32_BIT_FLOAT_API - cd->aec_reference_buffer_ptrs[channel][i] = - convert_int16_to_float(ref[channel]); + float **ref_ptr = cd->aec_reference_buffer_ptrs; + int s_chan; + int i; + + /* Loop over frames and channels, converting data from int16 to float */ + for (i = 0; i < cd->num_frames; ++i) { + for (s_chan = 0; s_chan < cd->num_aec_reference_channels; ++s_chan) { + (*ref_ptr)[s_chan] = convert_int16_to_float(*ref++); + ref_ptr++; + } + } + #else /* CONFIG_COMP_GOOGLE_RTC_USE_32_BIT_FLOAT_API */ - cd->aec_reference_buffer[buffer_offset++] = ref[channel]; -#endif /* CONFIG_COMP_GOOGLE_RTC_USE_32_BIT_FLOAT_API */ + int16_t *ref_buf = cd->aec_reference_buffer; - } + /* Use memcpy to copy the data from ref buffer to ref_buf buffer until it reaches + * ref_data_end + * This assumes that the data in the ref buffer is contiguous + */ + size_t num_bytes = (ref_data_end - ref) * sizeof(*ref); - ref += cd->num_aec_reference_channels; - if ((void *)ref >= (void *)ref_buf_end) - ref = (void *)ref_buf_start; + if (num_bytes > cd->aec_reference_buffer_size) { + /* Handle the error: the source data is too large to fit in the + * destination buffer + */ + return -EINVAL; } + memcpy(cd->aec_reference_buffer, ref, num_bytes); + + /* Update the ref and ref_buf pointers */ + ref = ref_data_end; + ref_buf += (ref_data_end - ref); + +#endif /* CONFIG_COMP_GOOGLE_RTC_USE_32_BIT_FLOAT_API */ + #if CONFIG_COMP_GOOGLE_RTC_USE_32_BIT_FLOAT_API GoogleRtcAudioProcessingAnalyzeRender_float32(cd->state, (const float **) @@ -856,23 +943,62 @@ static int google_rtc_audio_processing_process(struct processing_module *mod, assert(!ret); src_buf_end = src_buf_start + src_buf_size; - buffer_offset = 0; - for (int i = 0; i < cd->num_frames; i++) { - for (channel = 0; channel < cd->num_capture_channels; channel++) + /* The second optimization eliminates the inner loop + * and replaces it with pointer arithmetic for speedier access. + * To reduce cycle waste, the data copy check is moved outside of the loop. + */ + const int16_t *src_end = src + cd->num_frames * cd->config.output_fmt.channels_count; + /* Check if the calculated end of the source buffer exceeds the actual end of the buffer */ + src_end = cir_buf_wrap_const(src_end, src_buf_start, src_buf_end); + #if CONFIG_COMP_GOOGLE_RTC_USE_32_BIT_FLOAT_API - cd->process_buffer_ptrs[channel][i] = convert_int16_to_float(src[channel]); + /* Declare a pointer to the process buffer */ + float **proc_ptr = cd->process_buffer_ptrs; + + /* Check for null pointers and buffer overflows */ + if (!src || !proc_ptr || src >= (const int16_t *)src_end) { + /* If there's an error, return -EINVAL immediately to indicate an + * invalid argument was passed + */ + return -EINVAL; + } + + /* If there's no error, continue processing */ + while (src != (const int16_t *)src_end) { + /* If the source pointer has reached or exceeded the end of the source + * buffer, wrap it back to the start + */ + src = cir_buf_wrap_const(src, src_buf_start, src_buf_end); + /* Convert the source data from int16_t to float and store it in the + * process buffer + */ + *proc_ptr++ = convert_int16_to_float(src++); + } + #else /* CONFIG_COMP_GOOGLE_RTC_USE_32_BIT_FLOAT_API */ - cd->process_buffer[buffer_offset++] = src[channel]; -#endif /* CONFIG_COMP_GOOGLE_RTC_USE_32_BIT_FLOAT_API */ + /* Declare a pointer to the process buffer */ + int16_t *proc_buf = cd->process_buffer; + + /* Check for null pointers and buffer overflows */ + if (!src || !proc_buf || src >= (int16_t *)src_end) { + /* If there's an error, return -EINVAL immediately to indicate an + * invalid argument was passed + */ + return -EINVAL; + } - /* move pointer to next frame - * number of incoming channels may be < cd->num_capture_channels + /* If there's no error, continue processing */ + while (src != (int16_t *)src_end) { + /* If the source pointer has reached or exceeded the end of the source + * buffer, wrap it back to the start */ - src += cd->config.output_fmt.channels_count; - if ((void *)src >= (void *)src_buf_end) - src = (void *)src_buf_start; + src = cir_buf_wrap_const(src, src_buf_start, src_buf_end); + /* Copy the source data to the process buffer */ + *proc_buf++ = *src++; } +#endif /* CONFIG_COMP_GOOGLE_RTC_USE_32_BIT_FLOAT_API */ + source_release_data(src_stream, num_of_bytes_to_process); /* call the library, use same in/out buffers */ @@ -893,41 +1019,64 @@ static int google_rtc_audio_processing_process(struct processing_module *mod, dst_buf_end = dst_buf_start + dst_buf_size; /* process all channels in output stream */ - buffer_offset = 0; - for (int i = 0; i < cd->num_frames; i++) { - for (channel = 0; channel < cd->config.output_fmt.channels_count; channel++) { - /* set data in processed channels, zeroize not processed */ - if (channel < cd->num_capture_channels) + /* Calculate the end of the destination buffer based on the number of frames and + * channels + */ + int16_t *dst_end = dst + cd->num_frames * cd->config.output_fmt.channels_count; + /* Check if the calculated end of the destination buffer exceeds the actual end + * of the buffer + */ + dst_end = cir_buf_wrap_const(dst_end, dst_buf_start, dst_buf_end); + #if CONFIG_COMP_GOOGLE_RTC_USE_32_BIT_FLOAT_API - dst[channel] = convert_float_to_int16( - cd->process_buffer_ptrs[channel][i]); + float **proc_ptr = cd->process_buffer_ptrs; + + /* Check for null pointers and buffer overflows */ + if (!dst || !proc_ptr || dst >= dst_end || *proc_ptr >= *proc_ptr + cd->num_frames) + /*if there's an error, return -EINVAL immediately to indicate an + * invalid argument was passed + */ + return -EINVAL; + + /* Convert data from float to int16_t and store it in the destination buffer */ + for (; dst != dst_end; ++dst, ++proc_ptr) + *dst = convert_float_to_int16(*proc_ptr); + #else /* CONFIG_COMP_GOOGLE_RTC_USE_32_BIT_FLOAT_API */ - dst[channel] = cd->process_buffer[buffer_offset++]; -#endif /* CONFIG_COMP_GOOGLE_RTC_USE_32_BIT_FLOAT_API */ - else - dst[channel] = 0; - } + int16_t *process_buffer = cd->process_buffer; - dst += cd->config.output_fmt.channels_count; - if ((void *)dst >= (void *)dst_buf_end) - dst = (void *)dst_buf_start; - } + /* Check for null pointers and buffer overflows */ + if (!dst || !process_buffer || dst >= dst_end || + process_buffer >= process_buffer + cd->num_frames) + /* if there's an error, return -EINVAL immediately to indicate an + * invalid argument was passed + */ + return -EINVAL; + + /* Copy the data from the process buffer to the destination buffer */ + for (; dst != dst_end; ++dst, ++process_buffer) + *dst = *process_buffer; +#endif /* CONFIG_COMP_GOOGLE_RTC_USE_32_BIT_FLOAT_API */ sink_commit_buffer(dst_stream, num_of_bytes_to_process); return 0; } #else /* CONFIG_IPC_MAJOR_4 */ -static int google_rtc_audio_processing_process(struct processing_module *mod, - struct input_stream_buffer *input_buffers, - int num_input_buffers, - struct output_stream_buffer *output_buffers, - int num_output_buffers) +static int google_rtc_audio_processing_process( + struct processing_module *mod, + struct input_stream_buffer *input_buffers, + int num_input_buffers, + struct output_stream_buffer *output_buffers, + int num_output_buffers) { - struct google_rtc_audio_processing_comp_data *cd = module_get_private_data(mod); + struct google_rtc_audio_processing_comp_data *cd = + module_get_private_data(mod); int16_t *src, *dst, *ref; uint32_t num_aec_reference_frames; uint32_t num_aec_reference_bytes; + int ref_channels; + int aec_ref_product; int num_samples_remaining; int num_frames_remaining; int channel; @@ -936,9 +1085,9 @@ static int google_rtc_audio_processing_process(struct processing_module *mod, int ret; int i, j, n; + struct audio_stream *ref_stream, *mic_stream = NULL, *out_stream; struct input_stream_buffer *ref_streamb, *mic_streamb; struct output_stream_buffer *out_streamb; - struct audio_stream *ref_stream, *mic_stream, *out_stream; if (cd->reconfigure) { ret = google_rtc_audio_processing_reconfigure(mod); @@ -950,25 +1099,43 @@ static int google_rtc_audio_processing_process(struct processing_module *mod, ref_stream = ref_streamb->data; ref = audio_stream_get_rptr(ref_stream); + /* Pre-calculate the number of channels in the reference stream for efficiency */ + ref_channels = audio_stream_get_channels(ref_stream); + + /* Pre-calculate the product of the number of AEC reference channels and the AEC + * reference frame index + */ + aec_ref_product = cd->num_aec_reference_channels * cd->aec_reference_frame_index; num_aec_reference_frames = input_buffers[cd->aec_reference_source].size; num_aec_reference_bytes = audio_stream_frame_bytes(ref_stream) * num_aec_reference_frames; + num_samples_remaining = num_aec_reference_frames * ref_channels; + + /* Move out of loop */ + int mic_stream_channels = audio_stream_get_channels(mic_stream); + size_t num_bytes = sizeof(int16_t) * cd->num_capture_channels; + size_t num_frames = cd->num_frames; + size_t num_capture_channels = cd->num_capture_channels; + size_t size_of_buffer_element = sizeof(int16_t); + size_t buffer_size = num_frames * num_capture_channels; + + buffer_size *= size_of_buffer_element; - num_samples_remaining = num_aec_reference_frames * audio_stream_get_channels(ref_stream); while (num_samples_remaining) { nmax = audio_stream_samples_without_wrap_s16(ref_stream, ref); n = MIN(num_samples_remaining, nmax); for (i = 0; i < n; i += cd->num_aec_reference_channels) { - j = cd->num_aec_reference_channels * cd->aec_reference_frame_index; + j = aec_ref_product; for (channel = 0; channel < cd->num_aec_reference_channels; ++channel) cd->aec_reference_buffer[j++] = ref[channel]; - - ref += audio_stream_get_channels(ref_stream); + ref += ref_channels; ++cd->aec_reference_frame_index; - if (cd->aec_reference_frame_index == cd->num_frames) { - GoogleRtcAudioProcessingAnalyzeRender_int16(cd->state, - cd->aec_reference_buffer); + GoogleRtcAudioProcessingAnalyzeRender_int16( + cd->state, + cd->aec_reference_buffer); cd->aec_reference_frame_index = 0; + /* Reset the product as the frame index is reset */ + aec_ref_product = 0; } } num_samples_remaining -= n; @@ -993,34 +1160,40 @@ static int google_rtc_audio_processing_process(struct processing_module *mod, nmax = audio_stream_frames_without_wrap(out_stream, dst); n = MIN(n, nmax); for (i = 0; i < n; i++) { - memcpy_s(&(cd->raw_mic_buffer[cd->raw_mic_buffer_frame_index * - cd->num_capture_channels]), - cd->num_frames * cd->num_capture_channels * - sizeof(cd->raw_mic_buffer[0]), src, - sizeof(int16_t) * cd->num_capture_channels); - ++cd->raw_mic_buffer_frame_index; - - memcpy_s(dst, cd->num_frames * cd->num_capture_channels * - sizeof(cd->output_buffer[0]), - &(cd->output_buffer[cd->output_buffer_frame_index * - cd->num_capture_channels]), - sizeof(int16_t) * cd->num_capture_channels); - ++cd->output_buffer_frame_index; - - if (cd->raw_mic_buffer_frame_index == cd->num_frames) { - GoogleRtcAudioProcessingProcessCapture_int16(cd->state, - cd->raw_mic_buffer, - cd->output_buffer); - cd->output_buffer_frame_index = 0; - cd->raw_mic_buffer_frame_index = 0; + /* If we haven't filled the buffer yet, copy the data */ + if (cd->raw_mic_buffer_frame_index < cd->num_frames) { + int ret = google_rtc_safe_memcpy(cd, cd->raw_mic_buffer, src, + num_bytes, buffer_size, + cd->raw_mic_buffer_frame_index); + if (ret < 0) + return ret; + ++cd->raw_mic_buffer_frame_index; + } + if (cd->output_buffer_frame_index < cd->num_frames) { + int ret = google_rtc_safe_memcpy(cd, dst, cd->output_buffer, + num_bytes, buffer_size, + cd->output_buffer_frame_index); + if (ret < 0) + return ret; + ++cd->output_buffer_frame_index; } - src += audio_stream_get_channels(mic_stream); - dst += audio_stream_get_channels(out_stream); + /* Move to the next set of channels in the microphone and output streams */ + src += mic_stream_channels; + dst += mic_stream_channels; } num_frames_remaining -= n; src = audio_stream_wrap(mic_stream, src); dst = audio_stream_wrap(out_stream, dst); + + /* If we've filled the buffer, process the data */ + if (cd->raw_mic_buffer_frame_index == cd->num_frames) { + GoogleRtcAudioProcessingProcessCapture_int16(cd->state, + cd->raw_mic_buffer, + cd->output_buffer); + cd->output_buffer_frame_index = 0; + cd->raw_mic_buffer_frame_index = 0; + } } module_update_buffer_position(&input_buffers[cd->raw_microphone_source], @@ -1028,6 +1201,7 @@ static int google_rtc_audio_processing_process(struct processing_module *mod, return 0; } + #endif /* CONFIG_IPC_MAJOR_4 */ static struct module_interface google_rtc_audio_processing_interface = { diff --git a/src/audio/google/google_rtc_audio_processing_mock.c b/src/audio/google/google_rtc_audio_processing_mock.c index 9c654f919fc5..1d01e9187278 100644 --- a/src/audio/google/google_rtc_audio_processing_mock.c +++ b/src/audio/google/google_rtc_audio_processing_mock.c @@ -150,34 +150,61 @@ int GoogleRtcAudioProcessingProcessCapture_float32(GoogleRtcAudioProcessingState const float *const *src, float * const *dest) { + // Check if the input pointers are NULL + if (!state || !src || !dest) + return -1; // Return an error code + + // Check if the num_output_channels, num_aec_reference_channels, + // and num_frames values are positive + if (state->num_output_channels <= 0 || + state->num_aec_reference_channels <= 0 || + state->num_frames <= 0) { + return -2; // Return an error code + } + float *ref = state->aec_reference; float **mic = (float **)src; int n, chan; + int num_frames = state->num_frames; + int num_output_channels = state->num_output_channels; + int num_aec_reference_channels = state->num_aec_reference_channels; - for (chan = 0; chan < state->num_output_channels; chan++) { - for (n = 0; n < state->num_frames; ++n) { - float mic_save = mic[chan][n]; /* allow same in/out buffer */ + for (chan = 0; chan < num_output_channels; chan++) { + float *mic_chan = mic[chan]; + float *dest_chan = dest[chan]; + float *ref_chan = ref + chan * num_frames; - if (chan < state->num_aec_reference_channels) - dest[chan][n] = mic_save + ref[n + (chan * state->num_frames)]; + if (chan < num_aec_reference_channels) + for (n = 0; n < num_frames; ++n) + dest_chan[n] = mic_chan[n] + ref_chan[n]; + else + if (mic_chan != dest_chan) + memcpy_s(dest_chan, + num_frames * sizeof(float), + mic_chan, + num_frames * sizeof(float)); else - dest[chan][n] = mic_save; - } + memmove(dest_chan, mic_chan, num_frames * sizeof(float)); } return 0; } -int GoogleRtcAudioProcessingAnalyzeRender_float32(GoogleRtcAudioProcessingState *const state, - const float *const *data) +inline int GoogleRtcAudioProcessingAnalyzeRender_float32 + (GoogleRtcAudioProcessingState * const restrict state, + const float * const *restrict data) { const size_t buffer_size = sizeof(state->aec_reference[0]) * state->num_frames; int channel; + int num_aec_reference_channels = state->num_aec_reference_channels; + int num_frames = state->num_frames; - for (channel = 0; channel < state->num_aec_reference_channels; channel++) { - memcpy_s(&state->aec_reference[channel * state->num_frames], buffer_size, - data[channel], buffer_size); + if (buffer_size > 0) { + for (channel = 0; channel < num_aec_reference_channels; channel++) { + memcpy_s(&state->aec_reference[channel * num_frames], buffer_size, + data[channel], buffer_size); + } } return 0; @@ -187,18 +214,37 @@ int GoogleRtcAudioProcessingProcessCapture_int16(GoogleRtcAudioProcessingState * const int16_t *const src, int16_t *const dest) { + if (!state || !src || !dest || !state->aec_reference) + return -1; // Return an error code if any of the pointers are null + int16_t *ref = state->aec_reference; int n, chan; + int num_capture_channels = state->num_capture_channels; + int num_aec_reference_channels = state->num_aec_reference_channels; + for (chan = 0; chan < state->num_output_channels; chan++) { + int capture_index = chan; + int reference_index = chan; + for (n = 0; n < state->num_frames; ++n) { - int16_t mic_save = src[(n * state->num_capture_channels) + chan]; + if (capture_index >= num_capture_channels || + reference_index >= num_aec_reference_channels) { + // Return an error code if the indices are out of bounds + return -1; + } + + int16_t mic_save = src[capture_index]; - if (chan < state->num_aec_reference_channels) - dest[(n * state->num_capture_channels) + chan] = - mic_save + ref[(n * state->num_aec_reference_channels) + chan]; + // Use the local variables instead of fetching the values from memory + // each time + if (chan < num_aec_reference_channels) + dest[capture_index] = mic_save + ref[reference_index]; else - dest[(n * state->num_capture_channels) + chan] = mic_save; + dest[capture_index] = mic_save; + + capture_index += num_capture_channels; + reference_index += num_aec_reference_channels; } } @@ -212,8 +258,10 @@ int GoogleRtcAudioProcessingAnalyzeRender_int16(GoogleRtcAudioProcessingState *c sizeof(state->aec_reference[0]) * state->num_frames * state->num_aec_reference_channels; - memcpy_s(state->aec_reference, buffer_size, - data, buffer_size); + + if (memcmp(state->aec_reference, data, buffer_size) != 0) + memcpy_s(state->aec_reference, buffer_size, data, buffer_size); + return 0; } diff --git a/src/include/sof/audio/audio_stream.h b/src/include/sof/audio/audio_stream.h index 650d2caf9854..34165747b56c 100644 --- a/src/include/sof/audio/audio_stream.h +++ b/src/include/sof/audio/audio_stream.h @@ -28,6 +28,8 @@ #include #include +#include +#include /** \addtogroup audio_stream_api Audio Stream API * @{ @@ -402,7 +404,6 @@ static inline void *audio_stream_wrap(const struct audio_stream *buffer, void *p return ptr; } - /** * Verifies the pointer and performs rollover when reached the end of * the circular buffer. @@ -422,6 +423,46 @@ static inline void *cir_buf_wrap(void *ptr, void *buf_addr, void *buf_end) return ptr; } +/** + * @brief Wraps a circular buffer with const pointers. + * + * This function is a wrapper for the cir_buf_wrap function that accepts const pointers. + * It makes a copy of the data pointed to by the input pointers, then calls cir_buf_wrap + * with the copies. This ensures that the original data is not modified, which would be + * undefined behavior if the input pointers are const. + * + * @param ptr A const pointer to the current position in the buffer. + * @param buf_addr A const pointer to the start of the buffer. + * @param buf_end A const pointer to the end of the buffer. + * @return A pointer to the new position in the buffer after wrapping, or NULL if memory + * allocation failed. + */ +static inline void *cir_buf_wrap_const(const void *ptr, const void *buf_addr, const void *buf_end) +{ + void *ptr_copy = malloc(sizeof(int16_t)); + void *buf_addr_copy = malloc(sizeof(int8_t)); + void *buf_end_copy = malloc(sizeof(int8_t)); + + if (!ptr_copy || !buf_addr_copy || !buf_end_copy) { + fprintf(stderr, "Memory allocation failed!\n"); + free(ptr_copy); + free(buf_addr_copy); + free(buf_end_copy); + return NULL; + } + + memcpy(ptr_copy, ptr, sizeof(int16_t)); + memcpy(buf_addr_copy, buf_addr, sizeof(int8_t)); + memcpy(buf_end_copy, buf_end, sizeof(int8_t)); + + void *result = cir_buf_wrap(ptr_copy, buf_addr_copy, buf_end_copy); + + free(ptr_copy); + free(buf_addr_copy); + free(buf_end_copy); + + return result; +} /** * Verifies the pointer and performs rollover when reached the end of * the buffer.