File: DetectSilence.schelp

package info (click to toggle)
supercollider 1%3A3.13.0%2Brepack-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 80,292 kB
  • sloc: cpp: 476,363; lisp: 84,680; ansic: 77,685; sh: 25,509; python: 7,909; makefile: 3,440; perl: 1,964; javascript: 974; xml: 826; java: 677; yacc: 314; lex: 175; objc: 152; ruby: 136
file content (94 lines) | stat: -rw-r--r-- 3,284 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
class:: DetectSilence
summary:: Detect when input falls below an amplitude threshold
categories::  UGens>Synth control, UGens>Analysis>Amplitude


Description::

When the absolute value of the input signal remains below the threshold for a given window of time, output 1. Otherwise, output 0. If the output transitions from 0 to 1, doneAction is also evaluated.

code::
// this frees after the Decay has become quiet enough for a long enough time
{ var signal = Decay.ar(Impulse.ar(0), 2, PinkNoise.ar(0.2)); DetectSilence.ar(signal, doneAction: Done.freeSelf); signal }.play;
::

If the input signal starts with silence at the beginning of the synth's duration, then DetectSilence will wait indefinitely until the first sound before starting to monitor for silence. To avoid a hanging silent sound where the input may remain zero, you can add an code::Impulse.ar(0):: to its input.

DetectSilence does not distinguish a DC-biased signal from a loud signal. If your signal has DC bias, you should wrap it in link::Classes/LeakDC::.

warning:: DetectSilence can be tricky with multi-channel input! See below. ::

classmethods::

method::ar, kr

argument::in

The input signal.


argument::amp

When input falls below this for long enough, evaluate

code::doneAction:: .


argument::time

The minimum duration for which input must fall below

code::amp::  before this triggers.


argument::doneAction

An integer representing the doneAction. See

link::Classes/Done::  for more detail.

returns::
This UGen outputs 1 if silence is detected, otherwise 0.

Examples::

code::

(
SynthDef("detectSilence-help", { arg out;
	var z;
	z = SinOsc.ar(Rand(400, 700), 0, LFDNoise3.kr(8).max(0)).softclip * 0.3;
	DetectSilence.ar(z, doneAction: Done.freeSelf);
	Out.ar(out, z);
}).add;
)

Synth("detectSilence-help");
Synth("detectSilence-help");
Synth("detectSilence-help");


(
Task({
	loop({
		Synth("detectSilence-help");
		[0.5, 1].choose.wait;
	})
}).play;
)

::

section::DetectSilence and multiple inputs

Be careful feeding multiple inputs into DetectSilence, since multichannel expansion can lead to confusing behavior. This code

code::DetectSilence.ar([left, right] + Impulse.ar(0), doneAction: 2)::

spawns strong::two:: DetectSilences, and the enclosing Synth frees itself when left OR right becomes silent. (The code::Impulse.ar(0):: is there to ensure that DetectSilence starts detecting silence right away rather than waiting for non-silence.)

Usually you want to detect silence for both inputs, so what's a good way to change that OR into an AND? The solution is strong::not:: to sum code::(left + right) / 2.sqrt::, because there may be phase cancellation in that sum. Instead, use DetectSilence's output instead of its doneAction, and use FreeSelf to do the freeing. Recall that DetectSilence outputs 1 if silence is detected, and 0 otherwise. Taking the product of multiple DetectSilences will return 1 only if all the DetectSilences are 1. So here is the correct solution to freeing a Synth when both code::left:: and code::right:: first fall silent:

code::FreeSelf.kr(DetectSilence.ar([left, right] + Impulse.ar(0)).product)::

It's also possible to mix the signals with full-wave rectification to avert phase cancellation issues (code::[left, right].abs.sum / 2.sqrt::).