Straight into Subsample
By the end of this chapter, every transmission Substation records goes straight into Subsample, ready to play, and is there again each time Subsample starts.
This is the short way from a receiver to sounds to play, for a musician using Subsample: Installing Substation, A first scan, then this chapter. The chapters after it explain what Substation does at each step, and how to set it up when it does not do what you hoped.
The folder so far
The radio folder as A first scan left it: Substation installed in an environment inside it, switched on, its settings written, and the test signal made.
mkdir radio
cd radio
python3 -m venv .venv
source .venv/bin/activate
pip install "substation==0.8.2"
substation --init
# Makes test-signal.wav: eight seconds of the 2m band as a receiver hands it over,
# hiss with a transmission in the middle, for Substation to scan in place of a receiver.
import wave
import numpy
# The frequency the transmission is sent on, in hertz, and how many seconds it lasts.
frequency = 145_500_000
length = 3
# The frequency the recording is centred on, and how many IQ samples it holds
# a second, as a receiver set for the 2m band hands them over.
centre = 145_000_000
rate = 2_400_000
# Two seconds of hiss first, while Substation measures it, and three after.
start = 2
seconds = start + length + 3
# The hiss is random, but the same every time the script runs.
noise = numpy.random.default_rng(1)
with wave.open("test-signal.wav", "wb") as file:
# Each IQ sample as two 16-bit numbers, I and Q, held as two audio channels.
file.setnchannels(2)
file.setsampwidth(2)
file.setframerate(rate)
# A second at a time, so the script needs little memory.
for second in range(seconds):
# The moment of each IQ sample, in seconds.
t = second + numpy.arange(rate) / rate
# The receiver's own hiss.
hiss = (noise.standard_normal(rate) + 1j * noise.standard_normal(rate)) * 0.01
# A buzz like a voice: overtones of a low note, each softer than the one below.
voice = sum(5.3 / n ** 2 * numpy.sin(2 * numpy.pi * 150 * n * t) for n in range(2, 7))
# Louder and softer six times a second, like syllables.
loudness = numpy.abs(numpy.sin(2 * numpy.pi * 3 * t))
# FM: the sound swings the transmission's frequency to either side of its own.
phase = 2 * numpy.pi * (frequency - centre) * t + loudness * voice
# The transmission, only while it is on, added to the hiss.
on = (t >= start) & (t < start + length)
iq = hiss + numpy.where(on, 0.3 * numpy.exp(1j * phase), 0)
# Written as pairs of 16-bit numbers, I then Q.
pairs = numpy.stack([iq.real, iq.imag], axis=1)
file.writeframes((pairs * 32767).astype("<i2").tobytes())
python make_signal.py
What you need
Subsample installed with its OSC support, and a project of it set up to play, as Subsample's own guide shows. OSC, Open Sound Control, is how one program tells another what has happened. Subsample listens for it once osc.enabled and osc.receive_enabled are on in its project's config.yaml, as Subsample's guide shows under Messages over OSC. This chapter takes the project that guide makes, kitchen-kit, to be beside your radio folder.
Two settings
Open config.yaml, the file --init wrote in A first scan, in any text editor, and change two settings in it: under recording, set audio_output_dir to your Subsample project's samples/captures folder, and under osc, set sampler_enabled to true. Every other setting in that file is one of Substation's defaults, so with those two changed, this is all that differs:
recording:
audio_output_dir: ../kitchen-kit/samples/captures
osc:
sampler_enabled: true
sampler_enabledsends each recording to Subsample as Substation saves it, and Subsample loads it within seconds.audio_output_dirsaves the recordings in Subsample'ssamples/capturesfolder, in a folder for each day and band, where Subsample finds every one of them again each time it starts. A recording that only arrived by OSC would stay in Subsample until it stopped.
Scan
Start Subsample first, in a terminal of its own: a recording saved while Subsample is not running is not sent again, though Subsample finds it in its folder the next time it starts. Then, in another terminal, scan from the radio folder as before:
Example not checked: it is not run when this site is built.
substation --band amateur_2m
Without a receiver, scan the test signal from A first scan instead. Two lines of the log say what changed: where the recordings go, and that each is sent to Subsample, on this computer at port 9002, where Subsample listens:
substation --band amateur_2m --iq-file test-signal.wav --center-freq 145000000 --start-time "2026-10-05 21:00:00"
Subsample's log says when a recording arrives: OSC /sample/import:, then the recording's path, and a few seconds later its instrument library one sound larger. Beside the recording, Subsample writes two files of its own: what it measured in the sound, and a picture of it.
Playing them
Each recording joins Subsample's library like a sound Subsample recorded itself. Its ready-made drum kit plays, for each drum, whichever sound in the library is most like it, so your recordings take their place among the others, and a map of your own can choose them by what they hold, as Subsample's guide shows from Your first MIDI map.
Which bands record
Out of the box, only some of Substation's bands record: the amateur bands and CB radio, six of the 48. The others only detect, logging each transmission without recording it. The 2m band can be quiet, so the first sound may take a while to arrive.
The next chapter shows which bands record by default, and how to make a band of your own.