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))
adc() only listens: the input is measured and drawn, not played. To hear it
-- processed, through a voice's fuzz, grit, filter and effects -- use it as a
synth's sound: synth(uid:them, wave:in, fuzz:0.8, crush:4) (see
wave:in).
Syntax
adc()
adc(uid:NAME)
adc(ch:l)
adc(ch:l, uid:left)
adc(uid:mic, waveform:none)
adc(gain:4)
adc(floor:-40)
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 (its box goes with it); on its own, releases the microphone entirely. |
gain |
✖ | How much louder the input is taken, before anything is measured or drawn: 1 as it comes, 4 four times, 0.5 half. A number, or follow(...) (a fader, a MIDI knob). Run again with another, it changes in place. A quiet input wants it for amp, peak and onset, which have fixed thresholds; level and hit do not need it. |
floor |
✖ | The noise gate, in dB: below it the input counts as silence, for everything adc measures and draws. -50 by default -- above a quiet room's hiss, below speech or playing -- so the level, the bands and the scope do not blow the hiss up to full size and jitter. Higher (-40, -30) for a noisier room, floor:off for none. It opens on anything above it and closes once the input has been 6 dB under it for 0.2 s, so it does not chatter at the edge. |
waveform |
✖ | Its scope. From the live console the input draws one in a box of its own, as a synth does; waveform:none for none. |
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 |
band(k, n) |
0–1 | the spectrum cut in n bands on a log scale (as pitch is heard), 30 Hz to 16 kHz, low to high: band k of them, each against its own loudest lately -- every band reaches 1, the treble as the bass. Made only while followed: adc.band(1, 4) the lowest of four. See the Bands example |
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 |
level |
0–1 | amp against its own loudest of the last ~10 s, as a synth's level is: reaches 1 however hot or quiet the input |
hit |
0 / 1 | 1 for a moment on each attack -- a clap, a pluck, a drum -- as a synth's is on each step; against the input's own recent level, so at any gain |
open |
0 / 1 | 1 while the input is above its floor (the noise gate is open) |
n |
count | how many hits so far; every(4, adc.hit) is 1 on every 4th hit only (any number), as for a synth |
onset and hit both mark attacks: onset is an envelope (1, then
falling away) that needs a loud jump; hit is an event, one per attack,
for trig: -- text(pick:["NOW"], trig:adc.hit), grid(shuffle, trig:adc.hit).
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.
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.
No prompt at all usually means there is nothing to ask for: the system
offers the browser no audio input. The browser then fails at once, without a
dialog, and the console says so ([adc] no audio input to open). Plug an
interface or a microphone in, or turn an input on in the system's sound
settings. On Linux a sound card can be on an output-only profile: choose its
duplex profile (input and output), in the sound settings or with
pactl set-card-profile <card> output:analog-stereo+input:analog-stereo
(pactl list cards names the card and its profiles). Over plain http from
another machine the browser offers no input either; use https, or
localhost.
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.
Its scope -- drawing the external input
Run from the live console, adc() draws what it hears as a synth draws
what it plays: a box of its own, named by its uid, that
gui() reaches like any other -- every view, trail:,
cell:, the effects.
adc() the input, drawn in a box: gui(adc, ...)
gui(adc, view:ridge, rows:40, span:8, size:[0.9, 0.5])
adc(ch:l, uid:left)
adc(ch:r, uid:right)
gui(left, view:xy, y:right, size:0.8, trail:1) left against right: a stereo goniometer
synth(uid:s1, freq:220, amp:0.03)
gui(s1, view:xy, y:adc) a synth across, the input up
adc(uid:left, off:1) that one stopped, its box gone
So the visuals can be for an external input -- a player's microphone, a
mixer's send, another laptop -- with the same views, layouts and effects as
your own synths, and its measurements (adc.low, adc.onset, adc.pitch
...) to follow() as above. Each input draws from an analyser of its own,
so a view's settings never change what adc measures. waveform:none
leaves the box out; the input can still be drawn against
(gui(s1, view:xy, y:mic)). Run again, an input keeps its box where gui()
put it.
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