Live

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:, not stop:. stop is a cue name in the grammar, so adc(stop:1) is read as the stop() 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

Tip: use ← → or ↑ ↓ to navigate the docs