adc(...) — Audio Input as a Signal Source
adc() opens the live audio input and publishes what it hears on the control
plane, so any cue in the score can be driven by the sound in the room. It is
named after adc~ in Pd and Max, and behaves the way that name implies: it is
where sound comes in, and its measurements are outlets you patch from.
adc()
synth(uid:s1, freq: follow(adc.pitch, 90, 2000))
scale(uid:box, sx: map(adc.amp, 0, 0.4, 0.5, 3))
Syntax
adc()
adc(uid:NAME)
adc(ch:l)
adc(ch:l, uid:left)
adc(off:1)
Parameters
| param | required | description |
|---|---|---|
uid |
✖ | Publish under this name instead of adc. Two mics with different uids are two independent sources. |
ch |
✖ | Which channel to listen to: l/left/0, r/right/1, or mix (the default). Higher indices work on multi-input interfaces. |
off |
✖ | off:1 stops. With a uid, stops only that one; on its own, releases the microphone entirely. |
off:, notstop:.stopis a cue name in the grammar, soadc(stop:1)is read as thestop()cue and fails to parse.
Outlets
Ten measurements, published as <uid>.<channel>:
| channel | range | what it carries |
|---|---|---|
amp |
0–1 | RMS amplitude, smoothed — overall loudness |
peak |
0–1 | Peak amplitude, unsmoothed |
centroid |
0–1 | Spectral centroid — brightness |
onset |
0–1 | Spikes to 1 on an attack, then decays |
low |
0–1 | Low band, ~0–300 Hz |
mid |
0–1 | Mid band, ~300–2000 Hz |
high |
0–1 | High band, ~2000 Hz up |
pitch |
Hz | Detected pitch, 0 when there is no clear one |
note |
0–127 | The same pitch as a MIDI note number (60 = middle C) |
pitchConf |
0–1 | How much to trust pitch |
Eight of these are already 0–1, so follow() is enough. pitch and note are
not, which is what map() is for:
fade(uid:glow, opacity: follow(adc.centroid))
scale(uid:b, sx: map(adc.pitch, 80, 800, 0.4, 3))
It needs a gesture
Browsers refuse getUserMedia unless something the performer did led to it, so
an adc() that fires from the playhead at load will usually be blocked. The
reliable way in is a button:
button(label:"START LISTENING", trigger:adc())
button(label:"STOP", trigger:adc(off:1))
The failure is logged rather than thrown, so a score without permission still
runs — anything bound to adc.* simply never moves. Once the input is open,
firing adc() again does nothing, so it is safe to place one on every page of
a multi-page score.
Stereo
ch: picks one channel. A pair is two cues with two uids:
adc(ch:l, uid:left)
adc(ch:r, uid:right)
scale(uid:lBox, sy: map(left.amp, 0, 0.35, 0.05, 1))
scale(uid:rBox, sy: map(right.amp, 0, 0.35, 0.05, 1))
Both taps share one input, so there is only ever one permission prompt however many channels a score listens to — and a second tap on an already-open stream costs nothing, needing no gesture of its own. Each keeps its own onset envelope and its own smoothed amplitude and pitch; they are independent sources, not two views of one.
The channel count is requested as ideal, never required, because a hard
constraint would be refused outright by the mono microphone most people
actually have. On a mono input, ch:r logs a warning and reads channel 0
rather than silently reporting nothing.
The mic object
adc() does not need a drawing, but it is usually better with one. The +
button in the top bar creates a mic element — an adc() cue with a small
microphone drawn on the score, with fields for uid, channel, size, colour and
show/hide. See the DSL inspector.
The reason to place one is the connections view: a
source with no element has nothing to anchor a cord to, so follow(adc.amp, …)
draws as a dangling, unresolved connection. Give it a body and it becomes an
object with ten outlets like any other. A hidden mic still listens and still
has a position, so the cords still reach it.
Demo
See demo-adc — level and peak, the three bands, pitch and brightness, and a stereo pair as two independent sources. Press START LISTENING at B first; nothing moves until the input is open.
http://localhost:8001/?project=demo-adc
See Also
- Audio Analysis — the measurements in detail, and using them with
if: - Control & Modulation —
follow(),map(), and what every cue publishes - Conditional Triggering — gating cues on a signal
random()—trig:"adc.onset>0.5"to fire a cue on an attack
Tip: use ← → or ↑ ↓ to navigate the docs