Skip to content

Streaming Audio

Adamo streams audio the same way it streams cameras: you attach a named audio track on the robot, and it shows up for operators next to the video tracks. A robot can carry a microphone track and any number of camera tracks at once, and they stay loosely in sync so speech lines up with the picture without you wiring anything together.

Audio is a robot-to-operator downlink. The robot SDKs (Python, Rust, C/C++) send audio; the browser and the C/C++ SDK receive it. This mirrors video: an operator hears the robot, and a talk-back uplink is not part of the current release.

Attach an audio track before you call run(), exactly like a camera. The simplest source is test, a synthetic tone that needs no hardware — good for confirming the pipeline end to end. Swap it for a real microphone with the alsa source and a device string.

import adamo
robot = adamo.Robot(api_key="ak_...", name="my-robot")
# Synthetic tone — no hardware, verifies the path:
robot.attach_audio("mic", source_type="test")
# A real ALSA microphone (prefer a plughw: device so it resamples for you):
# robot.attach_audio("mic", source_type="alsa", device="plughw:1,0")
robot.run()

The track publishes on adamo/{org}/{robot}/audio/mic. Any operator on operate.adamohq.com viewing the robot will hear it once they interact with the page (browsers block autoplay until a click — see Operator playback).

Every SDK accepts the same set of sources, selected by source_type:

source_typeWhat it capturesNotes
testA synthetic toneNo hardware. Useful for confirming the path.
auto (default)The platform’s default inputPicks the system input automatically.
alsaA specific ALSA devicePass device (e.g. "plughw:1,0"). Prefer a plughw: device so it resamples for you.
pipelineA custom GStreamer sourcePass pipeline= — a launch string ending in raw audio. An escape hatch for unusual devices.

The Rust convenience methods map to these directly: attach_audio_test, attach_audio_alsa, attach_audio_gst (a pipeline source), and attach_audio with AudioOptions for the full surface. The C SDK mirrors them one to one.

channels is the maximum number of channels, not a demand. A mono microphone stays mono rather than being upmixed into a wasteful duplicated stereo pair — so the default channels=2 is safe to leave on a mono lapel mic.

Every knob has a working default; you rarely need more than source_type and device. The full option set (Python keywords shown; Rust AudioOptions and C adamo_audio_options_t carry the same fields):

OptionDefaultMeaning
bitrate_kbps64Opus target bitrate. 64 kbps stereo is transparent for speech.
sample_rate48000Capture/encode sample rate in Hz.
channels2Channel ceiling (see above).
frame_ms20Opus frame duration. Valid values: 2 (= 2.5 ms), 5, 10, 20, 40, 60. Smaller is lower latency, larger is more efficient.
inband_fecfalseOpus in-band forward error correction, for lossy links. Off by default.
dtxfalseDiscontinuous transmission — stop sending during silence, dropping to ~1 kbps.
allow_missingfalseKeep the robot running if this audio source is absent, instead of failing the attach.
robot.attach_audio(
"mic",
source_type="alsa",
device="plughw:1,0",
bitrate_kbps=64,
channels=2,
sample_rate=48000,
frame_ms=20,
inband_fec=False,
dtx=False,
)

The hosted operator at operate.adamohq.com plays a robot’s audio automatically once you interact with the page. Because browsers block audio autoplay, nothing is heard until the first click or keypress; in the VR view, putting the headset on (entering VR) is that gesture. A robot without an audio track is silently skipped — audio never delays or interferes with video.

See Web Interface for the mute toggle and the audio stats readout.

The TypeScript SDK plays a robot’s audio with the headless <AudioStream> component — mount one per robot. See the TypeScript SDK audio reference for <AudioStream>, the lower-level createAudioStream, and the enableAudio option on the XR players.

import { AudioStream } from "adamo-react";
// Headless — plays audio, renders nothing. Resumes on the first page gesture.
<AudioStream robot="my-robot" track="mic" />

The C SDK decodes an audio track to signed-16-bit PCM with adamo_audio_receiver_*, mirroring the video receiver. See the C SDK audio reference.

adamo_audio_receiver_t *rx = adamo_audio_receiver_open(sess, "my-robot", "mic");
for (;;) {
adamo_audio_frame_t *f = adamo_audio_receiver_recv(rx, 2000);
if (!f) continue; // timeout or error
// f->samples is f->sample_count interleaved int16 samples at f->sample_rate
adamo_audio_frame_free(f);
}
  • On a Jetson, use a USB microphone. The onboard audio path is not reliably usable for capture; a USB mic or USB audio interface is the dependable choice. Confirm the OS sees a capture device with arecord -l, and find its ALSA address (the plughw:CARD,DEVICE string) with arecord -L.
  • Verify the adapter actually has an input. Many cheap USB “audio adapters” are output-only. Check that it enumerates a microphone, not just headphones.
  • A charge-only USB cable will silently prevent the device from enumerating — use a data cable.
Terminal window
arecord -l # list capture-capable sound cards
arecord -L # list PCM device names, including plughw:CARD,DEV
arecord -d 3 -f cd test.wav # record 3 s to sanity-check the mic

Audio shipped across the stack. A few packaging details are worth knowing when you pick a build:

SDKAudio support
Pythonattach_audio in adamo 0.4.48+. The Linux aarch64 wheel (Jetson) supports every source type; the Linux x86_64 wheel supports source_type="alsa" (direct capture) only; the macOS wheel has no audio capture.
RustThe adamo crate 0.1.98+ with --features audio (implies video). Needs an audio-enabled libadamo — the published prebuilt from 0.1.98 onward carries it.
C / C++Build the library with CMake -DADAMO_BUILD_AUDIO=ON (implies ADAMO_BUILD_VIDEO). Guards the C++ types behind ADAMO_HAS_AUDIO.
TypeScriptadamo-react (<AudioStream>) and adamo/media (createAudioStream, XR players).

Robots built from the standard release binary capture audio out of the box.

An audio track uses the same key-expression convention as video, under an audio/ prefix instead of video/:

TopicDescription
adamo/{org}/{robot}/audio/{track}Opus audio frames.
adamo/{org}/{robot}/audio/{track}/capsQueryable JSON: codec, sample rate, channels, frame duration, bitrate. A late-joining consumer queries this to configure its decoder.
adamo/{org}/{robot}/audio/{track}/aliveLiveliness token — present while the track is streaming. Powers track discovery.