7#include <condition_variable>
58 void initialize(
const rclcpp::Node::SharedPtr& node)
override
61 node->declare_parameter(
"driver.audio.SpeakerAudioDriver.name",
"SpeakerAudioDriver");
62 node->declare_parameter(
"driver.audio.SpeakerAudioDriver.device_id", -1);
63 node->declare_parameter(
"driver.audio.SpeakerAudioDriver.device_name",
"");
64 node->declare_parameter(
"driver.audio.SpeakerAudioDriver.sample_rate", 44100);
65 node->declare_parameter(
"driver.audio.SpeakerAudioDriver.channels", 1);
66 node->declare_parameter(
"driver.audio.SpeakerAudioDriver.test_file_path",
70 name_ = node->get_parameter(
"driver.audio.SpeakerAudioDriver.name").as_string();
71 device_id_ = node->get_parameter(
"driver.audio.SpeakerAudioDriver.device_id").as_int();
72 device_name_ = node->get_parameter(
"driver.audio.SpeakerAudioDriver.device_name").as_string();
73 sample_rate_ = node->get_parameter(
"driver.audio.SpeakerAudioDriver.sample_rate").as_int();
74 channels_ = node->get_parameter(
"driver.audio.SpeakerAudioDriver.channels").as_int();
75 test_file_path_ = node->get_parameter(
"driver.audio.SpeakerAudioDriver.test_file_path").as_string();
81 err = Pa_Initialize();
84 RCLCPP_ERROR(
node_->get_logger(),
"PortAudio initialization failed: %s", Pa_GetErrorText(
err));
88 const int device_count = Pa_GetDeviceCount();
91 RCLCPP_ERROR(
node_->get_logger(),
"Pa_GetDeviceCount failed: %s", Pa_GetErrorText(device_count));
103 RCLCPP_INFO(
node_->get_logger(),
"Resolved speaker device_name '%s' to device_id %d.",
device_name_.c_str(),
108 RCLCPP_WARN(
node_->get_logger(),
109 "Failed to map speaker device_name '%s' to an ID: %s. Falling back to default output device.",
111 resolved_device_id = Pa_GetDefaultOutputDevice();
114 else if (resolved_device_id < 0 || resolved_device_id >= device_count)
116 resolved_device_id = Pa_GetDefaultOutputDevice();
119 if (resolved_device_id < 0 || resolved_device_id >= device_count)
121 for (
int i = 0; i < device_count; ++i)
123 const PaDeviceInfo* info = Pa_GetDeviceInfo(i);
124 if (info && info->maxOutputChannels > 0)
126 resolved_device_id = i;
127 RCLCPP_WARN(
node_->get_logger(),
"Falling back to first available speaker device_id %d ('%s').",
128 resolved_device_id, info->name);
134 const PaDeviceInfo* device_info = Pa_GetDeviceInfo(resolved_device_id);
138 std::to_string(resolved_device_id));
142 RCLCPP_WARN(
node_->get_logger(),
"Speaker device id %d was replaced by %d (%s).",
device_id_, resolved_device_id,
146 if (device_info->maxOutputChannels <= 0)
149 "') has no output channels");
153 RCLCPP_INFO(
node_->get_logger(),
"Assigned driver name: %s",
name_.c_str());
154 RCLCPP_INFO(
node_->get_logger(),
"Assigned driver device_id: %d",
device_id_);
156 RCLCPP_INFO(
node_->get_logger(),
"Assigned driver sample_rate: %d",
sample_rate_);
157 RCLCPP_INFO(
node_->get_logger(),
"Assigned driver channels: %d",
channels_);
162 RCLCPP_INFO(
node_->get_logger(),
"Initialized");
178 err = Pa_StopStream(pair.second);
179 if (
err != paNoError)
181 RCLCPP_ERROR(
node_->get_logger(),
"Failed to stop speaker stream: %s", Pa_GetErrorText(
err));
184 err = Pa_CloseStream(pair.second);
185 if (
err != paNoError)
187 RCLCPP_ERROR(
node_->get_logger(),
"Failed to close speaker stream: %s", Pa_GetErrorText(
err));
189 pair.second =
nullptr;
194 RCLCPP_INFO(
node_->get_logger(),
"stopped.");
222 auto data = input_data;
224 if (data.samples.empty())
226 RCLCPP_ERROR(
node_->get_logger(),
"Received empty audio data, nothing to set.");
235 RCLCPP_WARN(
node_->get_logger(),
236 "Incoming audio sample_rate (%d) differs from configured output sample_rate (%d). Resampling.",
241 const auto frames =
static_cast<int>(data.samples.size() / std::max(1, data.channels));
242 data.chunk_size = std::max(1, frames);
243 data.chunk_count = 1;
251 PaStreamParameters outputParameters;
253 outputParameters.channelCount =
channels_;
254 outputParameters.sampleFormat = paInt16;
255 outputParameters.suggestedLatency = Pa_GetDeviceInfo(
device_id_)->defaultLowOutputLatency;
256 outputParameters.hostApiSpecificStreamInfo =
nullptr;
261 PaStream* stream =
nullptr;
262 err = Pa_OpenStream(&stream,
nullptr, &outputParameters,
sample_rate_, paFramesPerBufferUnspecified, paClipOff,
265 if (
err != paNoError)
267 RCLCPP_ERROR(
node_->get_logger(),
"Failed to open speaker stream: %s", Pa_GetErrorText(
err));
271 err = Pa_StartStream(stream);
272 if (
err != paNoError)
274 RCLCPP_ERROR(
node_->get_logger(),
"Failed to start speaker stream: %s", Pa_GetErrorText(
err));
278 if (!Pa_IsStreamActive(stream))
280 RCLCPP_ERROR(
node_->get_logger(),
"Stream is not active after starting.");
286 RCLCPP_INFO(
node_->get_logger(),
"Opened new speaker stream: %s", stream_key.c_str());
287 const PaStreamInfo* stream_info = Pa_GetStreamInfo(stream);
290 RCLCPP_INFO(
node_->get_logger(),
"Speaker stream info: sample_rate=%.0f output_latency=%.4f",
291 stream_info->sampleRate, stream_info->outputLatency);
300 RCLCPP_INFO(
node_->get_logger(),
"Queueing audio to speaker stream %s and waiting for playback drain.",
305 RCLCPP_INFO(
node_->get_logger(),
"Queueing audio to speaker stream %s without waiting for playback drain.",
313 RCLCPP_INFO(
node_->get_logger(),
"Speaker playback drained for stream: %s", stream_key.c_str());
317 RCLCPP_INFO(
node_->get_logger(),
"Audio data queued to stream asynchronously: %s", stream_key.c_str());
322 RCLCPP_ERROR(
node_->get_logger(),
"Error writing audio data to stream: %s", e.
what());
339 RCLCPP_INFO(
node_->get_logger(),
"Read audio data from %s with %zu samples, %d Hz, %d channels.",
343 long double sum_squares = 0.0;
346 max_abs = std::max(max_abs, std::abs(
static_cast<int>(sample)));
347 sum_squares +=
static_cast<long double>(sample) *
static_cast<long double>(sample);
351 const double duration =
354 RCLCPP_INFO(
node_->get_logger(),
"Speaker test input stats: max_abs=%d rms=%.2f estimated_duration=%.2fs",
355 max_abs, rms, duration);
358 RCLCPP_WARN(
node_->get_logger(),
"Speaker test input appears nearly silent; use a known-good WAV to test "
366 RCLCPP_ERROR(
node_->get_logger(),
"Error during test: %s", e.
what());
369 RCLCPP_INFO(
node_->get_logger(),
"Test completed.");
373 static int pa_output_callback(
const void* input_buffer,
void* output_buffer,
unsigned long frames_per_buffer,
374 const PaStreamCallbackTimeInfo* time_info, PaStreamCallbackFlags status_flags,
388 int fill_output_buffer(
void* output_buffer,
unsigned long frames_per_buffer, PaStreamCallbackFlags status_flags)
390 auto* output =
static_cast<int16_t*
>(output_buffer);
391 const size_t samples_needed =
static_cast<size_t>(frames_per_buffer) *
static_cast<size_t>(std::max(1,
channels_));
394 if (status_flags & paOutputUnderflow)
398 if (lock.owns_lock())
414 if (copied < samples_needed)
416 std::fill(output + copied, output + samples_needed,
static_cast<int16_t
>(0));
424 int input_rate,
int output_rate)
426 if (input_rate <= 0 || output_rate <= 0)
429 channels = std::max(1, channels);
430 const size_t input_frames = input.size() /
static_cast<size_t>(channels);
432 if (input_frames == 0 || input_rate == output_rate)
435 const double ratio =
static_cast<double>(output_rate) /
static_cast<double>(input_rate);
436 const size_t output_frames = std::max<size_t>(1,
static_cast<size_t>(std::llround(input_frames * ratio)));
437 std::vector<int16_t> output(output_frames *
static_cast<size_t>(channels));
439 for (
int ch = 0; ch < channels; ++ch)
441 for (
size_t out_i = 0; out_i < output_frames; ++out_i)
443 const double src_pos =
static_cast<double>(out_i) / ratio;
444 const size_t i0 =
static_cast<size_t>(std::floor(src_pos));
445 const size_t i1 = std::min(i0 + 1, input_frames - 1);
446 const double frac = src_pos -
static_cast<double>(i0);
448 const int16_t s0 = input[i0 *
static_cast<size_t>(channels) +
static_cast<size_t>(ch)];
449 const int16_t s1 = input[i1 *
static_cast<size_t>(channels) +
static_cast<size_t>(ch)];
451 const double mixed = (1.0 - frac) *
static_cast<double>(s0) + frac *
static_cast<double>(s1);
452 const long rounded = std::lround(mixed);
453 const long clamped = std::clamp<long>(rounded, -32768, 32767);
455 output[out_i *
static_cast<size_t>(channels) +
static_cast<size_t>(ch)] =
static_cast<int16_t
>(clamped);
466 std::vector<int16_t> data;
474 data.resize(input_data.
samples.size() * 2);
475 for (
size_t i = 0; i < input_data.
samples.size(); ++i)
477 data[2 * i] = input_data.
samples[i];
478 data[2 * i + 1] = input_data.
samples[i];
484 data.resize(input_data.
samples.size() / 2);
485 for (
size_t i = 0; i < data.size(); ++i)
487 data[i] =
static_cast<int16_t
>((input_data.
samples[2 * i] + input_data.
samples[2 * i + 1]) / 2);
503 const size_t required_samples =
504 static_cast<size_t>(std::max(1, input_data.
chunk_size)) *
static_cast<size_t>(std::max(1,
channels_));
506 if (data.size() < required_samples)
508 throw fp_perception_exception(
"Insufficient data" + std::to_string(data.size()) +
" for requested chunk size " +
509 std::to_string(required_samples));
512 const unsigned long frames_to_queue =
static_cast<unsigned long>(data.size() /
static_cast<size_t>(
channels_));
513 RCLCPP_INFO(
node_->get_logger(),
"Queueing %lu frames (%zu samples) to speaker stream.", frames_to_queue,
536 [
this](diagnostic_updater::DiagnosticStatusWrapper& status) {
produce_diagnostics(status); });
544 size_t queued_samples = 0;
550 const size_t queued_frames = queued_samples /
static_cast<size_t>(std::max(1,
channels_));
551 bool stream_active =
false;
554 if (pair.second && Pa_IsStreamActive(pair.second) == 1)
556 stream_active =
true;
561 if (!stream_active && queued_samples > 0)
562 status.summary(diagnostic_msgs::msg::DiagnosticStatus::ERROR,
"Speaker queue has data but no active stream");
563 else if (underrun_count > 0 || lock_miss_count > 0)
564 status.summary(diagnostic_msgs::msg::DiagnosticStatus::WARN,
"Speaker callback underruns observed");
565 else if (stream_active)
566 status.summary(diagnostic_msgs::msg::DiagnosticStatus::OK,
"Speaker playback healthy");
568 status.summary(diagnostic_msgs::msg::DiagnosticStatus::OK,
"Speaker idle");
574 status.add(
"queued_samples", queued_samples);
575 status.add(
"queued_frames", queued_frames);
576 status.add(
"queue_drained", queued_samples == 0 ?
"true" :
"false");
577 status.add(
"callback_underrun_count", underrun_count);
578 status.add(
"callback_lock_miss_count", lock_miss_count);
Definition audio_sink_driver.hpp:10
std::string name_
Name of the driver.
Definition driver_base.hpp:143
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
const std::filesystem::path check_file(const std::string &file_name)
Definition driver_base.hpp:78
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
SpeakerAudioDriver class for handling audio output to a speaker.
Definition speaker_audio_driver.hpp:29
std::mutex playback_mutex_
Definition speaker_audio_driver.hpp:590
void wait_for_playback_drain()
Definition speaker_audio_driver.hpp:527
std::atomic< uint64_t > callback_lock_miss_count_
Definition speaker_audio_driver.hpp:593
void deinitialize() override
Stop driver streaming. This function stops the audio stream and closes it. It also terminates the Por...
Definition speaker_audio_driver.hpp:169
static std::vector< int16_t > resample_linear_interleaved(const std::vector< int16_t > &input, int channels, int input_rate, int output_rate)
Definition speaker_audio_driver.hpp:423
std::deque< int16_t > playback_queue_
Definition speaker_audio_driver.hpp:589
std::map< std::string, PaStream * > stream_dict_
Definition speaker_audio_driver.hpp:588
void test() override
Read test/mic_test.wav and play it through the speaker.
Definition speaker_audio_driver.hpp:329
SpeakerAudioDriver()
Constructor for SpeakerAudioDriver.
Definition speaker_audio_driver.hpp:36
void stopPlayback() override
Definition speaker_audio_driver.hpp:213
void produce_diagnostics(diagnostic_updater::DiagnosticStatusWrapper &status)
Definition speaker_audio_driver.hpp:539
int channels_
Definition speaker_audio_driver.hpp:585
int sample_rate_
Definition speaker_audio_driver.hpp:584
std::string test_file_path_
Definition speaker_audio_driver.hpp:586
~SpeakerAudioDriver() override
Destructor for SpeakerAudioDriver.
Definition speaker_audio_driver.hpp:45
int device_id_
Definition speaker_audio_driver.hpp:581
void play_internal(const audio_data &input_data, bool wait_for_drain)
Definition speaker_audio_driver.hpp:220
PaError err
Definition speaker_audio_driver.hpp:583
void enqueuePlayback(const audio_data &input_data) override
Definition speaker_audio_driver.hpp:208
std::atomic< uint64_t > underrun_count_
Definition speaker_audio_driver.hpp:592
std::string device_name_
Definition speaker_audio_driver.hpp:582
void queue_data(audio_data input_data, const std::string &stream_key, bool wait_for_drain)
Definition speaker_audio_driver.hpp:462
int fill_output_buffer(void *output_buffer, unsigned long frames_per_buffer, PaStreamCallbackFlags status_flags)
Definition speaker_audio_driver.hpp:388
void setup_diagnostics()
Definition speaker_audio_driver.hpp:533
std::condition_variable playback_cv_
Definition speaker_audio_driver.hpp:591
void play(const audio_data &input_data) override
Set the latest audio data to the driver. This function sends the latest audio data to the speaker dri...
Definition speaker_audio_driver.hpp:203
void initialize(const rclcpp::Node::SharedPtr &node) override
Initialize the driver.
Definition speaker_audio_driver.hpp:58
static int pa_output_callback(const void *input_buffer, void *output_buffer, unsigned long frames_per_buffer, const PaStreamCallbackTimeInfo *time_info, PaStreamCallbackFlags status_flags, void *user_data)
Definition speaker_audio_driver.hpp:373
Definition audio_buffer.hpp:16
int getDeviceIdByName(const std::string &target_name)
Definition utils.hpp:63
std::string describePortAudioDevice(int device_id)
Definition utils.hpp:28
void logPortAudioDevices(const LoggerT &logger)
Definition utils.hpp:46
audio_data readWavFile(const std::string &filepath)
Read audio data from a WAV file.
Definition wav.hpp:77
Struct to hold audio data.
Definition structs.hpp:16
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