«audrey II» by gosub

on 07 Feb'26 11:45 in feedbackdronehardwareaudreyiiselfoscillating

To start hearing something, raise the FbGain level above 0dB

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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// patch name: Audrey II
// patch date: 2026-02-06
// patch desc: software Audrey II drone synthesizer (Synthux Academy / Fede Repic)
//             feedback-driven self-oscillating drone machine
//
// signal flow:
//   noise seed (-90dBFS) + fb_return -> KS resonator -> overdrive -> LPF -> HPF -> reverb
//       ^                                                                            |
//       +--- feedback delay ("body", 1-100ms, stereo decorrelated) <--- fb_gain <----+
//                                                                                    |
//                                                                              echo send
//                                                                                    v
//                                                                     echo delay (50ms-5s)
//                                                                     BPF 800Hz -> tanh
//                                                                        (tape degradation)
//
// no oscillator generates sound -- the feedback loop self-excites when gain > ~0 dB

(
SynthDef(\audrey2, {
	|out=0, freq=48, fbGain= -20, body=0.01, lpf=12000, hpf=60,
	 verbMix=0.3, verbDecay=0.5,
	 echoSend=0, echoTime=0.3, echoFb=0.5,
	 vol=0.3|
	var seed, sigL, sigR, verbL, verbR;
	var fbRetL, fbRetR, echoRetL, echoRetR;
	var echoTapeL, echoTapeR, echoOutL, echoOutR;
	var kFreq = Lag.kr(freq, 0.2).midicps;
	var kBody = Lag.kr(body, 1.0);
	var kFbAmp = Lag.kr(fbGain, 0.05).dbamp;
	var kLpf = Lag.kr(lpf, 0.05);
	var kHpf = Lag.kr(hpf, 0.05);
	var kEchoTime = Lag.kr(echoTime, 0.5);
	var kEchoSend = Lag.kr(echoSend, 0.05);
	var kEchoFb = Lag.kr(echoFb, 0.05);

	// feedback return: 2 main fb + 2 echo fb
	#fbRetL, fbRetR, echoRetL, echoRetR = LocalIn.ar(4);

	// === MAIN FEEDBACK LOOP ===

	// white noise seed at -90 dBFS + feedback (independent L/R seeds)
	sigL = WhiteNoise.ar(-90.dbamp) + (fbRetL * kFbAmp);
	sigR = WhiteNoise.ar(-90.dbamp) + (fbRetR * kFbAmp);

	// karplus-strong resonator (tuned comb filter + brightness damping)
	// decay matches original's 0.8 damping factor: ~31 periods to -60dB
	sigL = OnePole.ar(CombL.ar(sigL, 0.2, kFreq.reciprocal, kFreq.reciprocal * 31), 0.98);
	sigR = OnePole.ar(CombL.ar(sigR, 0.2, kFreq.reciprocal, kFreq.reciprocal * 31), 0.98);

	// overdrive (soft clipping)
	sigL = (sigL * 1.5).tanh;
	sigR = (sigR * 1.5).tanh;

	// feedback loop filters + DC blocker
	sigL = LeakDC.ar(HPF.ar(LPF.ar(sigL, kLpf), kHpf));
	sigR = LeakDC.ar(HPF.ar(LPF.ar(sigR, kLpf), kHpf));

	// reverb (inside the feedback loop, as in original)
	#verbL, verbR = FreeVerb2.ar(sigL, sigR,
		Lag.kr(verbMix, 0.05), Lag.kr(verbDecay, 0.05), 0.5);

	// === ECHO DELAY (outside the feedback loop) ===
	// tape-style: each repetition passes through BPF + soft clip,
	// progressively losing highs and lows (telephone-like degradation)

	// tape degradation on echo feedback return
	echoTapeL = BPF.ar(echoRetL, 800, 1.0).tanh;
	echoTapeR = BPF.ar(echoRetR, 800, 1.0).tanh;

	// mix echo input (dry send) with degraded feedback, then delay
	echoOutL = DelayC.ar((verbL * kEchoSend) + (echoTapeL * kEchoFb), 5, kEchoTime);
	echoOutR = DelayC.ar((verbR * kEchoSend) + (echoTapeR * kEchoFb), 5, kEchoTime);

	// write all feedback paths
	LocalOut.ar([
		// main feedback delay ("body") -- right offset by 4 samples for stereo
		DelayC.ar(verbL, 0.25, kBody),
		DelayC.ar(verbR, 0.25, max(SampleDur.ir, kBody - (4 * SampleDur.ir))),
		// echo delay feedback
		echoOutL,
		echoOutR
	]);

	// final mix: dry + echo wet, then limiter
	Out.ar(out, Limiter.ar([
		verbL + echoOutL,
		verbR + echoOutR
	] * 0.5 * Lag.kr(vol, 0.05), 0.7));
}).add;
)

(
a = Synth(\audrey2);

w = Window.new("Audrey II", Rect(200, 200, 460, 480)).layout_(
	VLayout(
		StaticText().string_("Audrey II -- feedback drone synthesizer").align_(\center),
		HLayout(
			VLayout(StaticText().string_("Freq"),
				Slider().value_([16,72].asSpec.unmap(48))
				.action_({|x| a.set(\freq, [16,72].asSpec.map(x.value))})),
			VLayout(StaticText().string_("Fb Gain"),
				Slider().value_([-60,12].asSpec.unmap(-20))
				.action_({|x| a.set(\fbGain, [-60,12].asSpec.map(x.value))})),
			VLayout(StaticText().string_("Body"),
				Slider().value_([0.001,0.1,\exp].asSpec.unmap(0.01))
				.action_({|x| a.set(\body, [0.001,0.1,\exp].asSpec.map(x.value))})),
		),
		HLayout(
			VLayout(StaticText().string_("LPF"),
				Slider().value_([100,18000,\exp].asSpec.unmap(12000))
				.action_({|x| a.set(\lpf, [100,18000,\exp].asSpec.map(x.value))})),
			VLayout(StaticText().string_("HPF"),
				Slider().value_([10,4000,\exp].asSpec.unmap(60))
				.action_({|x| a.set(\hpf, [10,4000,\exp].asSpec.map(x.value))})),
		),
		HLayout(
			VLayout(StaticText().string_("Verb Mix"),
				Slider().value_(0.3)
				.action_({|x| a.set(\verbMix, x.value)})),
			VLayout(StaticText().string_("Verb Decay"),
				Slider().value_([0.2,1.0].asSpec.unmap(0.5))
				.action_({|x| a.set(\verbDecay, [0.2,1.0].asSpec.map(x.value))})),
		),
		HLayout(
			VLayout(StaticText().string_("Echo Send"),
				Slider().value_([0.001,1.0,\exp].asSpec.unmap(0.001))
				.action_({|x| a.set(\echoSend, [0.001,1.0,\exp].asSpec.map(x.value))})),
			VLayout(StaticText().string_("Echo Time"),
				Slider().value_([0.05,5.0,\exp].asSpec.unmap(0.3))
				.action_({|x| a.set(\echoTime, [0.05,5.0,\exp].asSpec.map(x.value))})),
			VLayout(StaticText().string_("Echo Fb"),
				Slider().value_([0.0,1.5].asSpec.unmap(0.5))
				.action_({|x| a.set(\echoFb, [0.0,1.5].asSpec.map(x.value))})),
		),
		HLayout(
			VLayout(StaticText().string_("Volume"),
				Slider().value_([0.001,1.0,\exp].asSpec.unmap(0.3))
				.action_({|x| a.set(\vol, [0.001,1.0,\exp].asSpec.map(x.value))})),
		),
	)
).front;
)
raw 5896 chars (focus & ctrl+a+c to copy)
reception
comments