FP Perception 0.1.2
Loading...
Searching...
No Matches
microphone_audio_driver.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <portaudio.h>
4#include <vector>
5#include <mutex>
6#include <condition_variable>
7#include <algorithm>
8#include <cmath>
9#include <atomic>
10#include <chrono>
11#include <rclcpp/rclcpp.hpp>
12#include <std_msgs/msg/int16_multi_array.hpp>
17
18namespace fp_perception
19{
20
28{
29public:
36 {
37 }
38
45 {
47 Pa_Terminate();
48 }
49
57 void initialize(const rclcpp::Node::SharedPtr& node) override
58 {
59 // Configure parameters for the nodevision
60 node->declare_parameter("driver.audio.MicrophoneAudioDriver.name", "MicrophoneAudioDriver");
61 node->declare_parameter("driver.audio.MicrophoneAudioDriver.device_id", -1);
62 node->declare_parameter("driver.audio.MicrophoneAudioDriver.device_name", "");
63 node->declare_parameter("driver.audio.MicrophoneAudioDriver.publish", false);
64 node->declare_parameter("driver.audio.MicrophoneAudioDriver.chunk_size", 256); // default chunk size
65 node->declare_parameter("driver.audio.MicrophoneAudioDriver.sample_rate", 44100); // default sample rate
66 node->declare_parameter("driver.audio.MicrophoneAudioDriver.channels", 1); // default number of channels
67 node->declare_parameter("driver.audio.MicrophoneAudioDriver.capture_buffer_window", 10);
68 node->declare_parameter("driver.audio.MicrophoneAudioDriver.timeout_sec", -1); // -1 waits indefinitely
69 // Load parameters from the node
70 name_ = node->get_parameter("driver.audio.MicrophoneAudioDriver.name").as_string();
71 device_id_ = node->get_parameter("driver.audio.MicrophoneAudioDriver.device_id").as_int();
72 device_name_ = node->get_parameter("driver.audio.MicrophoneAudioDriver.device_name").as_string();
73 chunk_size_ = node->get_parameter("driver.audio.MicrophoneAudioDriver.chunk_size").as_int();
74 sample_rate_ = node->get_parameter("driver.audio.MicrophoneAudioDriver.sample_rate").as_int();
75 channels_ = node->get_parameter("driver.audio.MicrophoneAudioDriver.channels").as_int();
76 capture_buffer_window_ = node->get_parameter("driver.audio.MicrophoneAudioDriver.capture_buffer_window").as_int();
77 timeout_sec_ = node->get_parameter("driver.audio.MicrophoneAudioDriver.timeout_sec").as_int();
78
79 // Initialize the base driver
80 initialize_base(node);
81
82 // Initialize PortAudio
83 err = Pa_Initialize();
84 if (err != paNoError)
85 {
86 RCLCPP_ERROR(node_->get_logger(), "PortAudio initialization failed: %s", Pa_GetErrorText(err));
87 throw fp_perception_exception("PortAudio initialization failed: " + std::string(Pa_GetErrorText(err)));
88 }
89
90 const int device_count = Pa_GetDeviceCount();
91 if (device_count < 0)
92 {
93 RCLCPP_ERROR(node_->get_logger(), "Pa_GetDeviceCount failed: %s", Pa_GetErrorText(device_count));
94 throw fp_perception_exception("PortAudio failed to enumerate devices");
95 }
96
97 logPortAudioDevices(node_->get_logger());
98
99 int resolved_device_id = device_id_;
100 if (!device_name_.empty())
101 {
102 try
103 {
104 resolved_device_id = getDeviceIdByName(device_name_);
105 RCLCPP_INFO(node_->get_logger(), "Resolved microphone device_name '%s' to device_id %d.", device_name_.c_str(),
106 resolved_device_id);
107 }
108 catch (const fp_perception_exception& e)
109 {
110 RCLCPP_WARN(node_->get_logger(),
111 "Failed to map microphone device_name '%s' to an ID: %s. Falling back to default input device.",
112 device_name_.c_str(), e.what());
113 resolved_device_id = Pa_GetDefaultInputDevice();
114 }
115 }
116 else if (resolved_device_id < 0 || resolved_device_id >= device_count)
117 {
118 resolved_device_id = Pa_GetDefaultInputDevice();
119 }
120
121 if (resolved_device_id < 0 || resolved_device_id >= device_count)
122 {
123 for (int i = 0; i < device_count; ++i)
124 {
125 const PaDeviceInfo* info = Pa_GetDeviceInfo(i);
126 if (info && info->maxInputChannels > 0)
127 {
128 resolved_device_id = i;
129 RCLCPP_WARN(node_->get_logger(), "Falling back to first available microphone device_id %d ('%s').",
130 resolved_device_id, info->name);
131 break;
132 }
133 }
134 }
135
136 const PaDeviceInfo* device_info = Pa_GetDeviceInfo(resolved_device_id);
137 if (!device_info)
138 {
139 throw fp_perception_exception("PortAudio returned null device info for device_id " +
140 std::to_string(resolved_device_id));
141 }
142 if (resolved_device_id != device_id_)
143 {
144 RCLCPP_WARN(node_->get_logger(), "Microphone device id %d was replaced by %d (%s).", device_id_,
145 resolved_device_id, device_info->name);
146 device_id_ = resolved_device_id;
147 }
148 if (device_info->maxInputChannels <= 0)
149 {
150 throw fp_perception_exception("Selected device_id " + std::to_string(device_id_) + " ('" + device_info->name +
151 "') has no input channels");
152 }
153
154 // Initial expected buffer size. It is recomputed after stream open if capture_sample_rate_ changes.
156
157 // Publish about the assigned driver parameters
158 RCLCPP_INFO(node_->get_logger(), "Assigned driver name: %s", name_.c_str());
159 RCLCPP_INFO(node_->get_logger(), "Assigned driver device_id: %d", device_id_);
160 RCLCPP_INFO(node_->get_logger(), "Assigned driver device: %s", describePortAudioDevice(device_id_).c_str());
161 RCLCPP_INFO(node_->get_logger(), "Assigned driver chunk_size: %lu", chunk_size_);
162 RCLCPP_INFO(node_->get_logger(), "Assigned driver sample_rate: %d", sample_rate_);
163 RCLCPP_INFO(node_->get_logger(), "Assigned driver channels: %d", channels_);
164 RCLCPP_INFO(node_->get_logger(), "Assigned driver capture_buffer_window: %d", capture_buffer_window_);
165 RCLCPP_INFO(node_->get_logger(), "Assigned driver timeout_sec: %d", timeout_sec_);
166 RCLCPP_INFO(node_->get_logger(), "Assigned driver buffer size: %d", buffer_size_);
167
168 RCLCPP_INFO(node_->get_logger(), "starting on device %d", device_id_);
169
170 PaStreamParameters inputParams;
171 inputParams.device = device_id_;
172 inputParams.channelCount = channels_;
173 inputParams.sampleFormat = paInt16;
174 inputParams.suggestedLatency = device_info->defaultLowInputLatency;
175 inputParams.hostApiSpecificStreamInfo = nullptr;
176
177 // Try to open with the configured sample rate first; if the device rejects it,
178 // fall back to the device default sample rate and resample to the configured rate
179 // when returning data.
181 err = Pa_OpenStream(&stream_, &inputParams, nullptr, sample_rate_, chunk_size_, paNoFlag,
183 if (err == paInvalidSampleRate)
184 {
185 const PaDeviceInfo* info = Pa_GetDeviceInfo(device_id_);
186 const int fallback_rate = info ? static_cast<int>(std::lround(info->defaultSampleRate)) : sample_rate_;
187 RCLCPP_WARN(node_->get_logger(),
188 "Requested mic sample_rate (%d) not supported by device '%s'. Falling back to %d and resampling.",
189 sample_rate_, (info && info->name) ? info->name : "unknown", fallback_rate);
190
191 capture_sample_rate_ = fallback_rate;
192 err = Pa_OpenStream(&stream_, &inputParams, nullptr, capture_sample_rate_, chunk_size_, paNoFlag,
194 }
195
196 if (err != paNoError)
197 {
198 RCLCPP_ERROR(node_->get_logger(), "Failed to open microphone stream: %s", Pa_GetErrorText(err));
199 throw fp_perception_exception("Failed to open microphone stream: " + std::string(Pa_GetErrorText(err)));
200 }
201
202 // Buffer stores captured samples at the capture sample rate.
204 audio_buffer_.assign(static_cast<size_t>(std::max(1, buffer_size_)), 0);
205 read_index_ = 0;
206 write_index_ = 0;
208 RCLCPP_INFO(node_->get_logger(), "Assigned driver capture_sample_rate: %d", capture_sample_rate_);
209 RCLCPP_INFO(node_->get_logger(), "Assigned driver final buffer size: %d samples", buffer_size_);
210
211 // The callback can be invoked as soon as Pa_StartStream succeeds.
212 is_running_ = true;
213 err = Pa_StartStream(stream_);
214 if (err != paNoError)
215 {
216 is_running_ = false;
217 RCLCPP_ERROR(node_->get_logger(), "Failed to start microphone stream: %s", Pa_GetErrorText(err));
218 throw fp_perception_exception("Failed to start microphone stream: " + std::string(Pa_GetErrorText(err)));
219 }
220
221 if (!Pa_IsStreamActive(stream_))
222 {
223 is_running_ = false;
224 RCLCPP_ERROR(node_->get_logger(), "Stream is not active after starting.");
225 throw fp_perception_exception("PortAudio stream failed to activate.");
226 }
227
228 // Start callback-driven audio capture.
229 RCLCPP_INFO(node_->get_logger(), "starting callback-driven audio capture...");
230
233
234 // Log that the driver has been initialized
235 RCLCPP_INFO(node_->get_logger(), "Initialized");
236 }
237
242 void deinitialize() override
243 {
244 is_running_ = false;
245 buffer_cv_.notify_all();
247
248 if (stream_)
249 {
250 Pa_StopStream(stream_);
251 Pa_CloseStream(stream_);
252 stream_ = nullptr;
253 RCLCPP_INFO(node_->get_logger(), "stopped.");
254 }
255
256 std::lock_guard<std::mutex> lock(buffer_mutex_);
257 read_index_ = 0;
258 write_index_ = 0;
260 }
261
272 {
273 // Check if the audio buffer is empty and proceed only if it has enough data
274 std::unique_lock<std::mutex> lock(buffer_mutex_);
275
276 const size_t chunk_samples = static_cast<size_t>(chunk_size_) * static_cast<size_t>(std::max(1, channels_));
277
278 const auto has_enough_audio = [this, chunk_samples] {
279 return buffered_sample_count_ >= chunk_samples || !is_running_;
280 };
281 if (timeout_sec_ < 0)
282 {
283 buffer_cv_.wait(lock, has_enough_audio);
284 }
285 else
286 {
287 if (!buffer_cv_.wait_for(lock, std::chrono::seconds(timeout_sec_), has_enough_audio))
288 {
289 throw fp_perception_exception("Timeout waiting for audio data.");
290 }
291 }
292
293 if (buffered_sample_count_ < chunk_samples && !is_running_)
294 throw fp_perception_exception("Microphone stream is not running.");
295
296 audio_data data;
298 data.channels = channels_;
299 data.chunk_size = chunk_size_;
300 data.chunk_count = 0;
301
302 const size_t samples_to_copy = (buffered_sample_count_ / chunk_samples) * chunk_samples;
303 std::vector<int16_t> captured(samples_to_copy);
304 if (samples_to_copy > 0)
305 {
306 read_samples_locked(captured.data(), samples_to_copy);
307 data.chunk_count = static_cast<int>(samples_to_copy / chunk_samples);
308 }
309
310 // If the capture sample rate differs from the configured sample rate, resample.
312 {
315 data.chunk_size =
316 std::max<int>(1, static_cast<int>(data.samples.size() / static_cast<size_t>(std::max(1, channels_))));
317 data.chunk_count = 1;
318 }
319 else
320 {
321 data.samples = std::move(captured);
323 data.chunk_size =
324 std::max<int>(1, static_cast<int>(data.samples.size() / static_cast<size_t>(std::max(1, channels_))));
325 data.chunk_count = 1;
326 }
327
328 return data;
329 }
330
336 void test() override
337 {
338 RCLCPP_INFO(node_->get_logger(), "Testing started. Please speak into the microphone. waiting 5 seconds...");
339
340 // Amount of frames required for 5 seconds of audio. Frames already include all channels.
341 unsigned long frames_to_capture = sample_rate_ * 5; // 5 seconds of audio
342 unsigned long frames_captured = 0;
343
344 audio_data accumilated_samples;
345
346 // Retrieve and log the audio data
347 while (frames_captured < frames_to_capture)
348 {
349 try
350 {
351 auto data = readChunk();
352
353 frames_captured += (data.chunk_size * data.chunk_count);
354
355 accumilated_samples.samples.insert(accumilated_samples.samples.end(), data.samples.begin(), data.samples.end());
356 accumilated_samples.sample_rate = data.sample_rate;
357 accumilated_samples.channels = data.channels;
358 accumilated_samples.chunk_size = data.chunk_size;
359 accumilated_samples.chunk_count += data.chunk_count;
360
361 RCLCPP_INFO(node_->get_logger(), "Captured %lu/%lu frames of audio data.", frames_captured, frames_to_capture);
362 }
363 catch (const fp_perception_exception& e)
364 {
365 RCLCPP_ERROR(node_->get_logger(), "Error during test: %s", e.what());
366 std::this_thread::sleep_for(std::chrono::milliseconds(100));
367 continue; // Retry getting data if an error occurs
368 }
369 }
370
371 // Create the "test" directory if it doesn't exist
372 check_directory("test");
373
374 int max_abs = 0;
375 long double sum_squares = 0.0;
376 for (const auto sample : accumilated_samples.samples)
377 {
378 max_abs = std::max(max_abs, std::abs(static_cast<int>(sample)));
379 sum_squares += static_cast<long double>(sample) * static_cast<long double>(sample);
380 }
381 const double rms = accumilated_samples.samples.empty() ?
382 0.0 :
383 std::sqrt(static_cast<double>(sum_squares / accumilated_samples.samples.size()));
384 RCLCPP_INFO(node_->get_logger(), "Microphone test signal stats: samples=%zu max_abs=%d rms=%.2f",
385 accumilated_samples.samples.size(), max_abs, rms);
386 if (max_abs < 128)
387 {
388 RCLCPP_WARN(node_->get_logger(), "Microphone test appears nearly silent. Check capture device_id, input source, "
389 "gain, mute state, and channel count.");
390 }
391
392 // Write the data to a file for further analysis
393 writeWavFile("test/mic_test.wav", accumilated_samples);
394
395 RCLCPP_INFO(node_->get_logger(), "Audio data written to file: test/mic_test.wav");
396
397 RCLCPP_INFO(node_->get_logger(), "Test completed.");
398 }
399
400protected:
401 static int pa_input_callback(const void* input_buffer, void* output_buffer, unsigned long frames_per_buffer,
402 const PaStreamCallbackTimeInfo* time_info, PaStreamCallbackFlags status_flags,
403 void* user_data)
404 {
405 (void)output_buffer;
406 (void)time_info;
407
408 auto* driver = static_cast<MicrophoneAudioDriver*>(user_data);
409 if (!driver)
410 return paAbort;
411
412 return driver->capture_input_buffer(input_buffer, frames_per_buffer, status_flags);
413 }
414
415 int capture_input_buffer(const void* input_buffer, unsigned long frames_per_buffer,
416 PaStreamCallbackFlags status_flags)
417 {
418 if (!is_running_)
419 return paComplete;
420
421 if (status_flags & paInputOverflow)
423
424 if (!input_buffer || frames_per_buffer == 0)
425 return paContinue;
426
427 const auto* samples = static_cast<const int16_t*>(input_buffer);
428 const size_t sample_count = static_cast<size_t>(frames_per_buffer) * static_cast<size_t>(std::max(1, channels_));
429
430 std::lock_guard<std::mutex> lock(buffer_mutex_);
431 write_samples_locked(samples, sample_count);
432 buffer_cv_.notify_one();
433
434 return paContinue;
435 }
436
437 static std::vector<int16_t> resample_linear_interleaved(const std::vector<int16_t>& input, int channels,
438 int input_rate, int output_rate)
439 {
440 if (input_rate <= 0 || output_rate <= 0)
441 throw fp_perception_exception("Invalid sample rate for resampling");
442
443 channels = std::max(1, channels);
444 const size_t input_frames = input.size() / static_cast<size_t>(channels);
445
446 if (input_frames == 0 || input_rate == output_rate)
447 return input;
448
449 const double ratio = static_cast<double>(output_rate) / static_cast<double>(input_rate);
450 const size_t output_frames = std::max<size_t>(1, static_cast<size_t>(std::llround(input_frames * ratio)));
451 std::vector<int16_t> output(output_frames * static_cast<size_t>(channels));
452
453 for (int ch = 0; ch < channels; ++ch)
454 {
455 for (size_t out_i = 0; out_i < output_frames; ++out_i)
456 {
457 const double src_pos = static_cast<double>(out_i) / ratio;
458 const size_t i0 = static_cast<size_t>(std::floor(src_pos));
459 const size_t i1 = std::min(i0 + 1, input_frames - 1);
460 const double frac = src_pos - static_cast<double>(i0);
461
462 const int16_t s0 = input[i0 * static_cast<size_t>(channels) + static_cast<size_t>(ch)];
463 const int16_t s1 = input[i1 * static_cast<size_t>(channels) + static_cast<size_t>(ch)];
464
465 const double mixed = (1.0 - frac) * static_cast<double>(s0) + frac * static_cast<double>(s1);
466 const long rounded = std::lround(mixed);
467 const long clamped = std::clamp<long>(rounded, -32768, 32767);
468
469 output[out_i * static_cast<size_t>(channels) + static_cast<size_t>(ch)] = static_cast<int16_t>(clamped);
470 }
471 }
472
473 return output;
474 }
475
477 {
478 enable_diagnostics("portaudio-microphone-" + std::to_string(device_id_), name_ + " capture",
479 [this](diagnostic_updater::DiagnosticStatusWrapper& status) { produce_diagnostics(status); });
480 }
481
482 void produce_diagnostics(diagnostic_updater::DiagnosticStatusWrapper& status)
483 {
484 const auto overflow_count = input_overflow_count_.load();
485 const auto drop_count = dropped_callback_chunks_.load();
486 const bool stream_active = is_running_.load() && stream_ && Pa_IsStreamActive(stream_) == 1;
487
488 size_t buffered_samples = 0;
489 {
490 std::lock_guard<std::mutex> lock(buffer_mutex_);
491 buffered_samples = buffered_sample_count_;
492 }
493
494 const size_t buffered_frames = buffered_samples / static_cast<size_t>(std::max(1, channels_));
495
496 if (!stream_active)
497 status.summary(diagnostic_msgs::msg::DiagnosticStatus::ERROR, "Microphone stream inactive");
498 else if (overflow_count > 0 || drop_count > 0)
499 status.summary(diagnostic_msgs::msg::DiagnosticStatus::WARN, "Microphone callback overruns observed");
500 else
501 status.summary(diagnostic_msgs::msg::DiagnosticStatus::OK, "Microphone capture healthy");
502
503 status.add("device_id", device_id_);
504 status.add("device_name", device_name_);
505 status.add("configured_sample_rate", sample_rate_);
506 status.add("capture_sample_rate", capture_sample_rate_);
507 status.add("channels", channels_);
508 status.add("chunk_size", static_cast<int>(chunk_size_));
509 status.add("capture_buffer_window_seconds", capture_buffer_window_);
510 status.add("buffer_size_samples", buffer_size_);
511 status.add("buffered_samples", buffered_samples);
512 status.add("buffered_frames", buffered_frames);
513 status.add("input_overflow_count", overflow_count);
514 status.add("dropped_callback_chunks", drop_count);
515 }
516
517 void write_samples_locked(const int16_t* samples, size_t sample_count)
518 {
519 if (audio_buffer_.empty() || !samples || sample_count == 0)
520 return;
521
522 if (sample_count >= audio_buffer_.size())
523 {
524 const size_t keep = audio_buffer_.size();
525 std::copy(samples + (sample_count - keep), samples + sample_count, audio_buffer_.begin());
526 read_index_ = 0;
527 write_index_ = 0;
529 return;
530 }
531
532 const size_t free_space = audio_buffer_.size() - buffered_sample_count_;
533 if (sample_count > free_space)
534 {
535 const size_t overflow = sample_count - free_space;
536 read_index_ = (read_index_ + overflow) % audio_buffer_.size();
537 buffered_sample_count_ -= overflow;
538 }
539
540 const size_t first_copy = std::min(sample_count, audio_buffer_.size() - write_index_);
541 std::copy(samples, samples + first_copy, audio_buffer_.begin() + static_cast<std::ptrdiff_t>(write_index_));
542
543 const size_t remaining = sample_count - first_copy;
544 if (remaining > 0)
545 std::copy(samples + first_copy, samples + sample_count, audio_buffer_.begin());
546
547 write_index_ = (write_index_ + sample_count) % audio_buffer_.size();
548 buffered_sample_count_ += sample_count;
549 }
550
551 void read_samples_locked(int16_t* destination, size_t sample_count)
552 {
553 if (!destination || sample_count == 0)
554 return;
555
556 const size_t first_copy = std::min(sample_count, audio_buffer_.size() - read_index_);
557 std::copy(audio_buffer_.begin() + static_cast<std::ptrdiff_t>(read_index_),
558 audio_buffer_.begin() + static_cast<std::ptrdiff_t>(read_index_ + first_copy), destination);
559
560 const size_t remaining = sample_count - first_copy;
561 if (remaining > 0)
562 std::copy(audio_buffer_.begin(), audio_buffer_.begin() + static_cast<std::ptrdiff_t>(remaining),
563 destination + first_copy);
564
565 read_index_ = (read_index_ + sample_count) % audio_buffer_.size();
566 buffered_sample_count_ -= sample_count;
567 }
568
569 PaStream* stream_;
570 PaError err = paNoError;
571 std::vector<int16_t> audio_buffer_;
572 int device_id_; // Device ID for the microphone
573 std::string device_name_;
574 unsigned long chunk_size_; // Default chunk size
575 int sample_rate_; // Default sample rate
576 int capture_sample_rate_{ 0 }; // Actual device capture rate (may differ from sample_rate_)
577 int channels_; // Default number of channels
578 int capture_buffer_window_; // Capture buffer window in seconds
579 int timeout_sec_{ -1 }; // Wait timeout for audio data (-1 waits indefinitely)
580 int buffer_size_{ 0 }; // Buffer size in samples
581 size_t read_index_{ 0 };
582 size_t write_index_{ 0 };
584 std::atomic<uint64_t> input_overflow_count_{ 0 };
585 std::atomic<uint64_t> dropped_callback_chunks_{ 0 };
586};
587
588} // namespace fp_perception
Definition audio_source_driver.hpp:10
void check_directory(std::string folder_name)
Check if the test directory exists, create it if not.
Definition driver_base.hpp:68
std::mutex buffer_mutex_
Mutex to protect access to the driver data.
Definition driver_base.hpp:149
std::atomic< bool > is_running_
Flag to indicate if the driver thread is running.
Definition driver_base.hpp:161
std::string name_
Name of the driver.
Definition driver_base.hpp:143
std::condition_variable buffer_cv_
Condition variable to notify when new data is available.
Definition driver_base.hpp:155
rclcpp::Node::SharedPtr node_
ROS node for the driver.
Definition driver_base.hpp:138
void initialize_base(const rclcpp::Node::SharedPtr &node)
Initializer base driver in place of constructor due to plugin semantics.
Definition driver_base.hpp:95
void disable_diagnostics()
Definition driver_base.hpp:129
bool diagnostics_enabled() const
Definition driver_base.hpp:105
void enable_diagnostics(const std::string &hardware_id, const std::string &task_name, std::function< void(diagnostic_updater::DiagnosticStatusWrapper &)> task, std::chrono::milliseconds period=std::chrono::seconds(1))
Definition driver_base.hpp:110
MicrophoneAudioDriver class for handling audio input from a microphone.
Definition microphone_audio_driver.hpp:28
int capture_buffer_window_
Definition microphone_audio_driver.hpp:578
int capture_sample_rate_
Definition microphone_audio_driver.hpp:576
PaStream * stream_
Definition microphone_audio_driver.hpp:569
void write_samples_locked(const int16_t *samples, size_t sample_count)
Definition microphone_audio_driver.hpp:517
static std::vector< int16_t > resample_linear_interleaved(const std::vector< int16_t > &input, int channels, int input_rate, int output_rate)
Definition microphone_audio_driver.hpp:437
void deinitialize() override
Stop driver streaming. This function stops the audio stream and closes it. It also terminates the Por...
Definition microphone_audio_driver.hpp:242
void setup_diagnostics()
Definition microphone_audio_driver.hpp:476
std::string device_name_
Definition microphone_audio_driver.hpp:573
void produce_diagnostics(diagnostic_updater::DiagnosticStatusWrapper &status)
Definition microphone_audio_driver.hpp:482
size_t read_index_
Definition microphone_audio_driver.hpp:581
std::atomic< uint64_t > dropped_callback_chunks_
Definition microphone_audio_driver.hpp:585
unsigned long chunk_size_
Definition microphone_audio_driver.hpp:574
size_t buffered_sample_count_
Definition microphone_audio_driver.hpp:583
audio_data readChunk() override
Get latest audio data from the driver as a stream. This function reads audio data from the microphone...
Definition microphone_audio_driver.hpp:271
int buffer_size_
Definition microphone_audio_driver.hpp:580
std::vector< int16_t > audio_buffer_
Definition microphone_audio_driver.hpp:571
size_t write_index_
Definition microphone_audio_driver.hpp:582
~MicrophoneAudioDriver() override
Start the audio stream.
Definition microphone_audio_driver.hpp:44
void test() override
Test the driver.
Definition microphone_audio_driver.hpp:336
int capture_input_buffer(const void *input_buffer, unsigned long frames_per_buffer, PaStreamCallbackFlags status_flags)
Definition microphone_audio_driver.hpp:415
PaError err
Definition microphone_audio_driver.hpp:570
void read_samples_locked(int16_t *destination, size_t sample_count)
Definition microphone_audio_driver.hpp:551
void initialize(const rclcpp::Node::SharedPtr &node) override
Initialize the driver.
Definition microphone_audio_driver.hpp:57
MicrophoneAudioDriver()
Constructor for MicrophoneAudioDriver.
Definition microphone_audio_driver.hpp:35
static int pa_input_callback(const void *input_buffer, void *output_buffer, unsigned long frames_per_buffer, const PaStreamCallbackTimeInfo *time_info, PaStreamCallbackFlags status_flags, void *user_data)
Definition microphone_audio_driver.hpp:401
int channels_
Definition microphone_audio_driver.hpp:577
int sample_rate_
Definition microphone_audio_driver.hpp:575
int device_id_
Definition microphone_audio_driver.hpp:572
int timeout_sec_
Definition microphone_audio_driver.hpp:579
std::atomic< uint64_t > input_overflow_count_
Definition microphone_audio_driver.hpp:584
Definition audio_buffer.hpp:16
int getDeviceIdByName(const std::string &target_name)
Definition utils.hpp:63
void writeWavFile(const std::string &filename, const audio_data &data)
Write audio data to a WAV file.
Definition wav.hpp:63
std::string describePortAudioDevice(int device_id)
Definition utils.hpp:28
void logPortAudioDevices(const LoggerT &logger)
Definition utils.hpp:46
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
int chunk_size
Size of each audio chunk in samples.
Definition structs.hpp:20
Base class for driver exceptions.
Definition exceptions.hpp:14
virtual const char * what() const noexcept override
Definition exceptions.hpp:21