5#include <condition_variable>
10#include <rclcpp/rclcpp.hpp>
22 "interactions, increase the audio retention window parameter.";
26 append(data, max_duration_seconds, rclcpp::Clock().now());
29 void append(
const audio_data& data,
int max_duration_seconds,
const rclcpp::Time& end_time)
34 std::unique_lock<std::mutex> lock(
mutex_);
36 const int channels = std::max(1, data.
channels);
37 const int sample_rate = std::max(1, data.
sample_rate);
39 const size_t incoming_frames = data.
samples.size() /
static_cast<size_t>(channels);
40 if (incoming_frames == 0)
47 RCLCPP_WARN(rclcpp::get_logger(
"AudioBuffer"),
48 "Resetting public audio buffer due to format change: sample_rate %d -> %d, channels %d -> %d, "
49 "buffered_samples=%zu",
59 const rclcpp::Duration incoming_duration =
framesToDuration(incoming_frames, sample_rate);
70 if (observed_gap > incoming_duration)
72 const auto missing_duration = observed_gap - incoming_duration;
73 const size_t missing_frames =
durationToFrames(missing_duration, sample_rate);
74 const size_t missing_samples = missing_frames *
static_cast<size_t>(channels);
75 if (missing_samples > 0)
94 const size_t max_buffer_size =
static_cast<size_t>(std::max(1, data.
sample_rate)) *
95 static_cast<size_t>(std::max(1, data.
channels)) *
96 static_cast<size_t>(std::max(1, max_duration_seconds));
100 const size_t excess_samples =
buffer_.
samples.size() - max_buffer_size;
101 const size_t excess_frames = excess_samples /
static_cast<size_t>(channels);
102 const size_t samples_to_remove = excess_frames *
static_cast<size_t>(channels);
103 if (samples_to_remove > 0)
106 buffer_.
samples.begin() +
static_cast<std::ptrdiff_t
>(samples_to_remove));
112 const size_t buffered_frames =
buffer_.
samples.size() /
static_cast<size_t>(channels);
116 static_cast<size_t>(std::max(1, data.
chunk_size)) *
static_cast<size_t>(std::max(1, data.
channels));
119 const double buffered_seconds =
static_cast<double>(buffered_frames) /
static_cast<double>(sample_rate);
120 if (buffered_seconds < 0.5)
122 RCLCPP_DEBUG(rclcpp::get_logger(
"AudioBuffer"),
123 "Public audio buffer remains small after append: added_frames=%zu buffered_frames=%zu "
124 "buffered_seconds=%.3f retention=%d start=%.9f end=%.9f",
125 incoming_frames, buffered_frames, buffered_seconds, max_duration_seconds,
133 void waitForAudio(std::chrono::seconds timeout = std::chrono::seconds(5))
135 std::unique_lock<std::mutex> lock(
mutex_);
148 duration_seconds = std::max(1, duration_seconds);
151 std::lock_guard<std::mutex> lock(
mutex_);
155 const size_t requested_samples =
156 static_cast<size_t>(sample_rate) *
static_cast<size_t>(channels) *
static_cast<size_t>(duration_seconds);
159 if (available_samples == 0)
167 const size_t samples_to_copy = std::min(requested_samples, available_samples);
168 const size_t start_index = available_samples - samples_to_copy;
171 out.
chunk_size =
static_cast<int>(samples_to_copy /
static_cast<size_t>(channels));
178 duration_seconds = std::max(1, duration_seconds);
181 std::lock_guard<std::mutex> lock(
mutex_);
189 const rclcpp::Time requested_end_time = start_time + rclcpp::Duration::from_seconds(duration_seconds);
192 const size_t requested_frames =
static_cast<size_t>(sample_rate) *
static_cast<size_t>(duration_seconds);
200 const rclcpp::Time overlap_end = requested_end_time > buffer_end_time ? buffer_end_time : requested_end_time;
201 if (overlap_end <= overlap_start)
205 const size_t output_start_frame =
durationToFrames(overlap_start - start_time, sample_rate);
206 size_t frames_to_copy =
durationToFrames(overlap_end - overlap_start, sample_rate);
208 const size_t available_frames =
buffer_.
samples.size() /
static_cast<size_t>(channels);
209 if (source_start_frame >= available_frames || output_start_frame >= requested_frames)
212 frames_to_copy = std::min(frames_to_copy, available_frames - source_start_frame);
213 frames_to_copy = std::min(frames_to_copy, requested_frames - output_start_frame);
214 if (frames_to_copy == 0)
217 const size_t source_start_sample = source_start_frame *
static_cast<size_t>(channels);
218 const size_t samples_to_copy = frames_to_copy *
static_cast<size_t>(channels);
220 buffer_.
samples.begin() +
static_cast<std::ptrdiff_t
>(source_start_sample + samples_to_copy));
221 out.
chunk_size =
static_cast<int>(frames_to_copy);
227 std::chrono::seconds timeout = std::chrono::seconds(30))
229 duration_seconds = std::max(1, duration_seconds);
232 const rclcpp::Time requested_end_time = start_time + rclcpp::Duration::from_seconds(duration_seconds);
233 std::unique_lock<std::mutex> lock(
mutex_);
253 std::to_string(requested_end_time.seconds()) +
"] buffered=[" + std::to_string(
buffer_start_time_.seconds()) +
254 ", " + std::to_string(current_end_time.seconds()) +
"]");
261 "Timeout waiting for requested audio slice. requested=[" + std::to_string(start_time.seconds()) +
", " +
262 std::to_string(requested_end_time.seconds()) +
"] buffered=[" + std::to_string(
buffer_start_time_.seconds()) +
263 ", " + std::to_string(current_end_time.seconds()) +
"]");
269 std::lock_guard<std::mutex> lock(
mutex_);
275 std::lock_guard<std::mutex> lock(
mutex_);
282 const int64_t nanoseconds =
static_cast<int64_t
>((
static_cast<long double>(frames) * 1000000000.0L) /
283 static_cast<long double>(std::max(1, sample_rate)));
284 return rclcpp::Duration::from_nanoseconds(nanoseconds);
289 if (duration.nanoseconds() <= 0)
291 return static_cast<size_t>(
292 (
static_cast<long double>(duration.nanoseconds()) *
static_cast<long double>(std::max(1, sample_rate))) /
312 return retention_start > start_time;
Definition audio_buffer.hpp:19
uint64_t total_samples_
Definition audio_buffer.hpp:318
rclcpp::Time endTime() const
Definition audio_buffer.hpp:273
rclcpp::Time startTime() const
Definition audio_buffer.hpp:267
bool isInitializedLocked() const
Definition audio_buffer.hpp:296
void append(const audio_data &data, int max_duration_seconds, const rclcpp::Time &end_time)
Definition audio_buffer.hpp:29
static size_t durationToFrames(const rclcpp::Duration &duration, int sample_rate)
Definition audio_buffer.hpp:287
uint64_t total_frames_
Definition audio_buffer.hpp:319
static rclcpp::Duration framesToDuration(size_t frames, int sample_rate)
Definition audio_buffer.hpp:280
rclcpp::Time buffer_start_time_
Definition audio_buffer.hpp:322
rclcpp::Time buffer_end_time_
Definition audio_buffer.hpp:323
std::condition_variable condition_variable_
Definition audio_buffer.hpp:317
bool isExpiredLocked(const rclcpp::Time &start_time) const
Definition audio_buffer.hpp:306
static constexpr const char * kExpiredAudioSliceError
Definition audio_buffer.hpp:21
audio_data readWindow(const rclcpp::Time &start_time, int duration_seconds)
Definition audio_buffer.hpp:176
void waitForWindow(const rclcpp::Time &start_time, int duration_seconds, std::chrono::seconds timeout=std::chrono::seconds(30))
Definition audio_buffer.hpp:226
bool initialized_time_
Definition audio_buffer.hpp:321
void waitForAudio(std::chrono::seconds timeout=std::chrono::seconds(5))
Definition audio_buffer.hpp:133
int max_duration_seconds_
Definition audio_buffer.hpp:320
std::mutex mutex_
Definition audio_buffer.hpp:316
audio_data readLatest(int duration_seconds)
Definition audio_buffer.hpp:146
audio_data buffer_
Definition audio_buffer.hpp:315
rclcpp::Time currentEndTimeLocked() const
Definition audio_buffer.hpp:301
void append(const audio_data &data, int max_duration_seconds)
Definition audio_buffer.hpp:24
Definition audio_buffer.hpp:16
Struct to hold audio data.
Definition structs.hpp:16
int chunk_count
Number of chunks in the audio data.
Definition structs.hpp:21
std::vector< int16_t > samples
Audio samples.
Definition structs.hpp:17
int sample_rate
Sample rate in Hz.
Definition structs.hpp:18
int channels
Number of audio channels.
Definition structs.hpp:19
bool override
if the system sample_rate, channels, and chunk_size should be overridden by message data
Definition structs.hpp:22
int chunk_size
Size of each audio chunk in samples.
Definition structs.hpp:20
Base class for driver exceptions.
Definition exceptions.hpp:14