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.