Skip to main content
Version: QTrobot V3

Audio

The /qtrobot/media/audio/... services and topics play audio on two independent lanes — foreground (FG) and background (BG) — from files, online URLs, or as a raw streamed PCM topic. These examples assume the gateway from Connection is already running.

Each lane has its own volume and accepts both a file and a stream input, with file playback always taking priority over the stream — see the Python Audio tutorial for the full FG/BG architecture diagram and the reasoning behind it (it applies identically here, just over services/topics instead of an SDK object).

Volume

ros2 service call /qtrobot/media/audio/fg/volume/set qtrobot_interfaces/srv/MediaAudioFgVolumeSet "{value: 1.0}"
ros2 service call /qtrobot/media/audio/fg/volume/get qtrobot_interfaces/srv/MediaAudioFgVolumeGet "{}"

ros2 service call /qtrobot/media/audio/bg/volume/set qtrobot_interfaces/srv/MediaAudioBgVolumeSet "{value: 1.0}"

Play a file

A file path must exist on the robot itself (not on your laptop) for these examples to work.

ros2 service call /qtrobot/media/audio/fg/file/play qtrobot_interfaces/srv/MediaAudioFgFilePlay \
"{uri: '/home/qtrobot/robot/data/audios/QT/5LittleBunnies.wav'}"

uri accepts a local path or an HTTP(S) URL, the same as the Python and TypeScript SDKs.

Pause and resume

ros2 service call /qtrobot/media/audio/fg/file/pause qtrobot_interfaces/srv/MediaAudioFgFilePause "{}"
ros2 service call /qtrobot/media/audio/fg/file/resume qtrobot_interfaces/srv/MediaAudioFgFileResume "{}"
ros2 service call /qtrobot/media/audio/fg/file/cancel qtrobot_interfaces/srv/MediaAudioFgFileCancel "{}"

Play FG and BG simultaneously

ros2 service call /qtrobot/media/audio/bg/volume/set qtrobot_interfaces/srv/MediaAudioBgVolumeSet "{value: 0.7}"
ros2 service call /qtrobot/media/audio/bg/file/play qtrobot_interfaces/srv/MediaAudioBgFilePlay \
"{uri: '/home/qtrobot/robot/data/audios/QT/5LittleBunnies.wav'}"

ros2 service call /qtrobot/media/audio/fg/file/play qtrobot_interfaces/srv/MediaAudioFgFilePlay \
"{uri: '/home/qtrobot/robot/data/audios/QT/John_Wesley_Tequila.mp3'}"

Both calls return as soon as the service accepts the request — playback runs independently on each lane, so issuing the FG call doesn't wait for the BG one to finish.

Stream raw audio

Publish AudioFrameRaw messages directly to a lane's stream topic instead of playing a file:

#!/usr/bin/env python3
import math
import struct
import rclpy
from rclpy.node import Node
from qtrobot_interfaces.msg import AudioFrameRaw


def _make_sine_chunk(freq_hz, sample_rate, chunk_frames, phase):
amplitude = 32767 # max for signed 16-bit
samples = [
int(amplitude * math.sin(2 * math.pi * freq_hz * (phase + i) / sample_rate))
for i in range(chunk_frames)
]
return struct.pack(f'<{chunk_frames}h', *samples), phase + chunk_frames


class SineStreamer(Node):
def __init__(self):
super().__init__('sine_streamer')
self.pub = self.create_publisher(AudioFrameRaw, '/qtrobot/media/audio/fg/stream', 10)

def stream(self, freq_hz=440.0, sample_rate=16000, duration_s=5.0, chunk_frames=1024):
total_frames = int(sample_rate * duration_s)
phase = 0
timer_period = chunk_frames / sample_rate
while phase < total_frames:
frames_this_chunk = min(chunk_frames, total_frames - phase)
raw, phase = _make_sine_chunk(freq_hz, sample_rate, frames_this_chunk, phase)
msg = AudioFrameRaw(channels=1, sample_rate=sample_rate, bit_depth=16, data=raw)
self.pub.publish(msg)
rclpy.spin_once(self, timeout_sec=timer_period)


def main():
rclpy.init()
node = SineStreamer()
node.stream()
node.destroy_node()
rclpy.shutdown()


if __name__ == '__main__':
main()

Stream frames are dropped while a file is playing on the same lane — pause or cancel the file first if you want the stream to take over.

Next steps

Continue with the Video tutorial, or see the full media namespace in the ROS2 API Reference.