Geluid
Audio-server
Het geluid wordt bij SuperCollider gegenereerd door de server. De server is een applicatie die los van de client draait. Hierdoor zorgt SuperCollider ervoor dat dit proces bij eventuele crash van de client of IDE door kan gaan, maar ook dat het op een andere computer kan werken.
opstarten en afsluiten
De server staat bij het opstarten van SC niet standaard aan. Dit is net als in Max, waar je ook eerst de audio aan moet zetten voor er geluid mogelijk is.
De server zet je aan door de volgende regel code uit te voeren:
s.boot;
Je zult nu zien dat de server-meters groen worden. De server kiest als output standaard de output van je computer.
Wil je de server uitzetten, gebruik dan de volgende code:
s.quit;
Samplerate
Het kan gebeuren dat er een samplerate mismatch is omdat SuperCollider bijvoorbeeld op 44100Hz draait en jouw geluidskaart op 48000Hz. Om de samplerate aan te passen kan je de volgende code uitvoeren voordat je de server opstart:
Server.default.options.sampleRate = 48000
;
Output-device
Om het output-device te kiezen dat je wil gebruiken voer je de volgende code uit voor je de server opstart:
Server.default.options.outDevice_("naam_van_device");
Als je wil weten welke output devices er allemaal beschikbaar zijn voer je de volgende code uit:
ServerOptions.outDevices;
Wil je het input-apparaat aanpassen, gebruik in bovenstaande voorbeelden dan inDevice
en inDevices
Geheugengebruik
Als je gebruik maakt van verschillende reverbs en delay's kan het voorkomen dat er niet genoeg RAM-geheugen beschikbaar is voor SuperCollider om mee te werken.
Om te kijken hoeveel geheugen er op dit moment beschikbaar is voer je de volgende code uit:
Server.default.options.memSize
Waarschijnlijk komt daar 8192 uit, dat is dus 8 mb.
Als je dit wil uitbreiden naar meer geheugen kan je de volgende code uitvoeren:
Server.default.options.memSize = 512000
Daarmee maak je 500MB van je RAM-geheugen beschikbaar voor SuperCollider. Dat is in ieder geval genoeg om een grote hoeveelheid reverbs, delays en samples aan te kunnen.
Vergeet niet de server opnieuw op te starten als je aanpassingen hebt gemaakt aan audio device, samplerate of geheugengebruik
Volume
Supercollider voert de code uit zoals jij die ingeeft. Dus als er per ongeluk een komma verkeerd staat waardoor de amplitude 10 is in plaats van 1, zal het geluid 10 keer zo hard afgespeeld worden. Dit kan pijnlijk zijn aan je oren. Om te voorkomen dat dit mis gaat kan je met behulp van een extensie een limiter inbouwen op de output. Deze zal altijd aanstaan en ervoor zorgen dat je bij fouten nog steeds audio hoort binnen een redelijk volume. Je kunt de limiter, safety net geheten hier downloaden. Om te installeren voer je de volgende stappen uit:
- klik op de groene knop code en vervolgens Download Zip
- pak het zip-bestand uit en verander de naam van de map van SafetyNet-main naar SafetyNet
- voer de volgende code uit in SuperCollider:
Quarks.folder.postcs.openOS;
- verplaats de map SafetyNet naar de map die in de post-window van SuperCollider staat na uitvoeren van bovenstaande code.
- voer vervolgens de volgende code uit in SuperCollider:
Quarks.install("SafetyNet")
. - kies vervolgen in het Language-menu van SuperCollider Reboot Interpreter.
- start de server met
s.boot;
- Je zult nu in je post-window iets zien als _Safety('localhost') is running, using 'safeClip2'.
Wil je de ingebouwde limiter uitzetten voer van de volgede code uit: Quarks.uninstall("SafetyNet")
Functies
Om geluid af te kunnen spelen kun je een functie maken die je uit kan voeren. In Supercollider maak je een functie door code tussen { } te plaatsen:
~sinus = {SinOsc.ar(440);}
De functie voer je vervolgens uit met:
~sinus.play;
Als het goed is hoor je nu een sinustoon van 440Hz. Je moet hierbij wel de server aan hebben staan, anders gebeurt er niets. Gebruik cmd/ctrl + .
om het geluid weer te stoppen.
Als je de volgende code gebruikt hoor je niet alleen de sinustoon, maar zie je hem ook verschijnen op de oscilloscoop:
~sinus.scope;
Om het verloop van een golfvorm goed te zien kan je ook .plot
gebruiken. Standaard laat SuperCollider het verloop van de golfvorm van 0.1 seconde zien. Als je dit aan wil passen kan je de tijd in seconden er tussen haakjes achter zetten:
~sinus.plot(1) //laat de sinus gedurende 1 seconde zien
UGens
Supercollider wordt geleverd met een groot aantal Unit Generators, kortweg UGens. Als je in de help browser op Browse klikt en dan onderin op UGens krijg je een overzicht met welke UGens er beschikbaar zijn.
Je kunt een UGen op twee verschillende manieren gebruiken: met .ar of met .kr als achtervoegsel. .ar staat voor AudioRate, dit is te vergelijken met de audio objecten in Max, dit zijn de objecten die audio genereren / bewerken en hoorbaar moeten zijn. .kr staat voor KontrolRate, dit is te vergelijken met de objecten zonder ~ in Max, deze kun je bijvoorbeeld gebruiken als LFO.
Als je de naam van een UGen met je muis selecteert en op cmd/ctrl+D
drukt krijg je een overzicht (description) van hoe je deze kunt gebruiken.
Bij bijvoorbeeld SinOsc.ar zie je dat je vier argumenten moet invullen: de frequentie, de fase, de mul en de add. Ook kan je zien dat er standaardwaarden zijn ingevuld. Als je niets invult worden die waardes gebruikt.
mul & add
Twee argumenten die je bij bijna elke UGen tegenkomt zijn mul en add. Net als in Max zijn de digitale audiosignalen in Supercollider uitgedrukt in getallen tussen -1 en 1.
Met mul kan je deze waarden vermenigvuldigen met een getal. Als je het signaal van een standaard oscillator wilt verzwakken tot de helft moet je bij mul dus 0.5 invullen, de range van de oscillator zal dan tussen -0.5 en 0.5 zijn.
Met add kan je een offset meegeven aan een signaal. Wil je bijvoorbeeld een sinus-oscillator gebruiken als LFO-signaal om de amplitude van een oscillator aan te passen dan zul je van het signaal van -1 tot 1 een signaal van 0 tot 1 moeten maken. Je hebt net gezien dat met een mul van 0.5 het signaal van -0.5 tot 0.5 loopt. Door er dan 0.5 bij op te tellen loopt het signaal van 0 tot 1.
SinOsc.ar(100,0,0.5,0.5);
/* Let op! Voer deze code niet zomaar uit!
Het is geen bipolair audiosignaal meer,
je kunt hier je speakers mee beschadigen!
*/
Wil je nu de frequentie van een oscillator moduleren dan kan je ook de mul en add gebruiken. Wil je dat de frequentie moduleren tussen 50 en 150Hz moet je een mul gebruiken van 50 en een add van 100.
SinOsc.ar(100,0,50,100);
//LET OP! VOER DIT NOOIT ZOMAAR UIT.
//EEN SIGNAAL DAT 50X VERSTERKT IS, IS ERG HARD!
Als je wil berekenen welke mul en welke add je moet invoeren dan kun je dat als volgt aanpakken: (bij een range tussen de 50 en 150)
Mul:
bovengrens โ ondergrens / 2
150 โ 50 = 100/2 = 50
Add:
ondergrens + mul
50 + 50 = 100
De oscillator zal nu bewegen met 100 als nulpunt en tot maximaal 50 daaronder en daarboven.
Nested UGens
De kracht van Supercollider ligt erin dat je gemakkelijk UGens kunt combineren tot complexere en daarmee vaak meer interessante synthese. In het bovenstaande voorbeeld kan je bijvoorbeeld nooit de modulerende sinus zomaar uitvoeren, je zult deze moeten gebruiken om de frequentie van een andere UGen te moduleren. Dit doe je met de volgende code:
{Saw.ar(SinOsc.kr(1,0,50,100),SinOsc.kr(0.5,0,0.5,0.5),0)}.play;
Zorg bij het uitvoeren van de code dat je het volume van je laptop altijd heel zacht zet. Als je code typt die er voor zorgt dat het hard wordt afgespeeld, zal Supercollider dit ook hard afspelen! Gebruik nooit een hoofdtelefoon of oortjes als je aan het experimenteren bent.
SynthDefs
Een eenvoudige SynthDef
Een SynthDef maakt het mogelijk om een aantal UGens te combineren en daarmee Synths te maken die terwijl ze draaien bestuurd kunnen worden. Je kunt er OSC-berichten naartoe sturen die ze 'live' gebruiken om bepaalde parameters aan te passen.
Een SynthDef maken
Een voorbeeld van een SynthDef die een sinus genereert
SynthDef("mySine", { |freq=400|
Out.ar(0, SinOsc.ar(freq,0,0.2))
}).add;
Een SynthDef uitvoeren
Dit beschrijft een geluid maar maakt uit zichzelf nog geen geluid. Met deze SynthDef maken we een Synth:
~synth_a = Synth("mySine");
of zo:
~synth_a = Synth("mySine", [\freq, 440]);
Vervolgens kunnen we de frequentie aanpassen terwijl de Synth draait:
~synth_a.set(\freq, 800);
~synth_a.set(\freq, 400);
Een meer complete SynthDef, stap voor stap uitgelegd
Voor het aansturen van รฉรฉn sinustoon is een SynthDef misschien niet zo nuttig, maar voor complexere synthesizers is het heel handig om dat in een SynthDef te verpakken. Hieronder een voorbeeld van een complexere SynthDef, daarna zullen we daar een aantal onderdelen nader bekijken.
(
SynthDef("klank",{|freq=500,ffreq=500,rate=1,amp=0.5|
var env,sig,mod;
mod = SinOsc.ar(0.5,0,50,freq);
env = EnvGen.ar(Env.linen(0.01,0.5,1.0,amp),Dust.ar(rate));
sig = Ringz.ar(Dust.ar(rate*2,0.3),mod,0.6,env);
sig = BLowPass.ar(sig,ffreq.lag(3),0.5);
sig = FreeVerb.ar(sig);
Out.ar([0,1],sig);
}).add;
)
~klank= Synth("klank",[\rate,10]);
~klank.set(\ffreq,500,\freq,1000,\rate,1000);
SynthDef("klank",{|freq=500,ffreq=500,rate=1,amp=0.5|
In de eerste regel wordt de SynthDef gedefinieerd. De naam van deze SynthDef staat tussen dubbele aanhalingstekens. ""
Daarna volgt de functie waarin de synth wordt beschreven. Deze functie staat, net als een losse functie, tussen accolades {}
. Het eerste onderdeel van deze functie is een lijst met argumenten die van buitenaf aangepast kunnen worden, met daarbij ook een defaultwaarde voor elke parameter. Deze lijst met argumenten begint en eindigt met een pipe: |
.
var env,sig,mod;
In de tweede regel worden de lokale variabelen gedefinieerd. In tegenstelling tot globale variabelen die met een tilde beginnen worden lokale variabelen met het woordje var ervoor gedefinieerd. Deze variabelen zijn alleen binnen deze SynthDef bekend en kan je niet van buitenaf veranderen. In deze variabelen worden delen van de synthesizer opgeslagen. Na het aanmaken van de variabelen kan de definitie van de synthesizer opgebouwd worden.
mod = SinOsc.ar(0.5,0,50,freq);
In deze regel wordt de modulator voor de frequentie gemaakt. Het is een sinus die met een frequentie van 0.5 beweegt tussen -50 en +50 rond de frequentie die bovenin is gedefinieerd als freq.
env = EnvGen.ar(Env.linen(0.01,0.5,1.0,amp),Dust.ar(rate));
EnvGen is een envelope-generator. Om een envelope te genereren zijn er minimaal twee argumenten nodig. Het eerste argument is het type envelope. Dit kan een percussieve zijn (Env.perc)
, een lineaire (Env.linen)
of een adsr (Env.adsr)
. In de beschrijving van Env
is de complete lijst met envelopes te vinden. Elke envelope heeft verschillende argumenten nodig. Meestal een attack- en decaytijd, amplitude en curve. Om het verloop van de envelope te kunnen zien is er de plotfunctie:
Env.perc(0.1,0.1,0.5,-4).plot
Het tweede argument van EnvGen
geeft aan hoe de envelope getriggerd moet worden. In het geval hierboven gebeurt dat met een Dust-generator. Als in plaats van deze Dust-generator het woordje gate als argument wordt gebruikt kan de envelope handmatig worden gestart van buiten de SynthDef. Zie onderaan de pagina voor een voorbeeld. In datzelfde voorbeeld wordt ook een derde argument toegevoegd, een doneAction: Dit geeft aan wat er moet gebeuren als de envelope klaar is met afspelen (action when done). De meest gebruikte waarde bij een doneAction is 2, hiermee wordt de synth gestopt en verwijderd als de envelope klaar is.
sig = Ringz.ar(Dust.ar(rate*2,0.3),mod,0.6,env);
sig = BLowPass.ar(sig,ffreq.lag(3),0.5);
sig = FreeVerb.ar(sig);
In deze drie regels wordt de daadwerkelijke klank-generatie gedaan: een resonant filter met een Dust-generator als input, met als filterfrequentie de hierboven genoemde modulerende sinus. Een q van 0.6 en als mul de envelope. Daarna gaat het signaal door een lowpass filter en daarna door een reverb. Door bij elke UGen van deze code de help-file te openen krijg je te zien hoe ze precies werken.
De notatie waarbij je op elke volgende regel opnieuw sig gebruikt heeft als voordeel dat daarmee duidelijk is dat dit het signaal is dat je in een aantal stappen aan het bewerken bent. Je kunt natuurlijk voor elke stap een nieuwe variabele maken, maar dan zie je die flow niet zo mooi.
lag
Door .lag(tijd in seconden) toe te voegen achter een paramater zal de parameter bij aanpassing van buitenaf in de tijd die je aangeeft van de oude naar de nieuwe staat faden.
Out.ar([0,1],sig);
Het laatste onderdeel van de SynthDef is de output waarnaar het signaal moet worden gestuurd. Out.ar
heeft twee argumenten: de bussen waar het signaal naar moet worden gestuurd. Dit kan ook een array zijn met meerdere bussen, om het signaal naar meerdere outputs te sturen. Het tweede argument is het signaal dat wordt verstuurd, in dit geval opgeslagen in de variabele sig.
}).add
De functie en de SynthDef worden afgesloten. met .add wordt de functie naar de server gestuurd en daar toegevoegd aan het lijstje met beschikbare SynthDefs om aan te roepen.
Envelopes
EnvGen & Env
Envelopes in Supercollider maak je met behulp van de EnvGen UGen. EnvGen.ar
verwacht een aantal parameters waarvan er een aantal nodig zijn om in te voeren en een aantal optioneel zijn. Hieronder een uitleg voor alle parameters van EnvGen:
- Envelope: Hier vul je met Env.xxx het type envelope in dat je wilt gebruiken. In plaats van de xxx kan je elke beschikbare envelope binnen Supercollider kiezen. Of je maakt een envelope naar eigen wens. De envelopes waar je uit kan kiezen zijn grofweg onderverdeeld in 2 categorieรซn: envelopes die in รฉรฉn keer uitgevoerd worden na een trigger (zonder sustain) en envelopes die wel een sustain hebben en aan blijven zolang de gate op 1 staat en uit gaan zodra de gate op 0 staat. In de help-file van Env staan alle mogelijke envelope-types met daarbij voorbeelden. Door Env te typen en selecteren in SuperCollider en vervolgens cmd/ctrl-D te typen kom je bij de help-file. De waarden die je over het algemeen moet invullen bij een Env zijn attackTime, decayTime, level en curve.
- Gate: de gate moet groter dan 0 zijn om de envelope te starten. Deze kan je handmatig op 1 zetten, maar je kan ook een andere UGen gebruiken om de envelope te triggeren. Bij sustained envelopes zal de envelope starten en op sustain-level blijven zolang de gate > 0. Als de gate weer 0 is zal deze uit sustain gaan en met de release-tijd teruggaan naar 0.
- levelScale, levelBias en timeScale hoef je over het algemeen niet in te vullen. Je kunt de levels van de envelope er mee vermenigvuldigen (levelScale), er een waarde bij optellen (levelBias) of de tijdsduur vermenigvuldigen (timeScale)
- doneAction: Dit is wat SuperCollider moet doen als de envelope klaar is met afspelen. Standaard staat deze op 0 en gebeurt er niets. Als je deze op 2 zet (dat kan verkort door in je argumentenlijst
doneAction: 2
in te voeren) Zal de Synth waar de envelope in wordt afgespeeld na afloop van de envelope opgeruimd worden. Hiermee zorg je ervoor dat er niet talloze Synths actief blijven die niets meer doen.
Voorbeeld van een van buitenaf getriggerde envelope:
(
SynthDef("envelope",{|freq=300,att=0.1,dec=0.4,gate|
var env, sig;
env = EnvGen.ar(Env.perc(att,dec,0.5),gate,doneAction:2);
sig = Saw.ar(freq,env);
sig = BLowPass.ar(sig,freq*2);
Out.ar([0,1],sig);
}).add;
)
~env = Synth("envelope",[\gate,1]);
Audio opnemen en afspelen
In SuperCollider is het natuurlijk ook mogelijk om samples op te nemen, in te laden en vervolgens af te spelen.
buffers
Om audio op te slaan heb je een buffer nodig. Een buffer maak je aan door in een variabele een hoeveelheid geheugen te reserveren waar je de audio in kan opslaan.
lege buffer
Een lege buffer maak je aan met Buffer.alloc()
:
~buffer = Buffer.alloc(s, 5 * s.sampleRate, 2);
- ~buffer is de variabele waar je de buffer in wil opslaan. Hier kan je bij afspelen of opnemen aan refereren
- s geeft aan dat je de buffer wil gebruiken op je huidige localhost server.
- 5 * sampleRate geeft de hoeveelheid geheugenruimte die je voor de buffer wil vrijmaken. Dit moet je aangeven in het aantal samples dat je nodig hebt. Aangezien dat nogal lastig is kan je het best tijd in seconden keer s.sampleRate doen, waarmee je de tijd in seconden die de buffer moet zijn vermenigvuldigt met de huidige samplerate.
- 2 geeft het aantal kanalen aan dat de buffer moet bevatten.
sample inladen
Als je een sample in een buffer wil inladen kan je dat doen met Buffer.read
De buffer zal daarmee meteen de grootte van de sample krijgen.
~sample = Buffer.read(s,'/Users/gebruiker/samples/kick.wav');
- ~sample is de variabele waar je de sample in wilt opslaan.
- s is de server waar de sample in moet worden aangemaakt
- '/Users/gebruikers/samples/kick.wav' is de locatie van de sample op je computer. Dit moet het absolute Path zijn naar de sample. Als je dit niet weet kan je de sample vanuit Finder/Verkenner de SuperCollider code-editor inslepen, dan wordt het Path daar weergegeven.
audio opnemen
Als je eenmaal een lege buffer hebt gemaakt kan je daar audio in opnemen. Dit kan audio zijn vanuit een SynthDef waar je later weer bewerkingen op wil uitvoeren, of audio van de microfooningang. Hieronder een voorbeeld van hoe je geluid van je microfoon opneemt. Je kan vervolgens de UGen die gebruikt wordt om de audio op te nemen natuurlijk ook op een andere plek inzetten.
~buffer = Buffer.alloc(s, 5 * s.sampleRate, 2);
SynthDef(\record,{ | buffer|
var sig;
sig = SoundIn.ar([0,1],1);
RecordBuf(sig,buf);
}).add;
~recorder = Synth(\record,[\buffer,~buffer]);
Met SoundIn.ar()
kan je het geluid van de input van je geluidskaart binnenhalen in SuperCollider. Het eerste argument is welke input(s) je wil gebruiken. Als je alleen het linkerkanaal wilt gebruiken kan je daar 0
invoeren. Als je twee kanalen wil gebruiken dan gebruik je blokhaken: [0,1]
. Let op: als je een stereo-buffer hebt aangemaakt zal je ook een stereo-signaal moeten gebruiken om op te nemen
Met RecordBuf.ar()
kan je geluid opnemen. Het eerste argument is het signaal dat je wilt opnemen, het tweede is de buffer waarin je dit wil opnemen. Het is handig om dit in een argument van de SynthDef mee te geven, zodat je deze SynthDef ook voor andere buffers makkelijk kan gebruiken. Er zijn nog een aantal overige, optionele, opties voor RecordBuf.ar()
: Een aantal interessante zijn:
- run: als dit getal 0 is stopt ie met opnemen, anders neemt ie op.
- loop: als dit getal 0 is loopt ie niet, anders wel. Standaard staat loop aan.
- trigger: als dit getal van negatief naar positief verschuift start ie met opnemen, hierdoor kan je een opname starten op een door jou bepaald moment. Standaard staat deze op 1 en zal de opname direct starten als de Synth wordt aangeroepen.
- doneAction: Dit werkt hetzelfde als bij envelopes. Wat moet er gebeuren als de opname klaar is? blijft de Synth bestaan, of wordt ie opgeruimd?
Audio afspelen
Als er audio in een buffer is opgeslagen kan je deze vervolgens afpslen. De volgende SynthDef laat kort zien hoe je audio afspeelt:
SynthDef(\play,{ |buffer,speed=0.5,trig=1|
var sig;
sig = PlayBuf.ar(2,buffer,speed,trig,loop:1);
Out.ar(0,sig);
}).add;
~player = Synth(\play,[\buffer,~buffer]);
De argumenten van PlayBuf.ar()
zijn alsvolgt:
- 2 dit is het aantal kanalen dat afgespeeld gaan worden. Zorg dat je dit op twee hebt staan bij een stereobuffer.
- buffer dit is de verwijzing naar de buffer
- speed dit is de afspeelsnelheid, 1 is normale snelheid, 2 is 2x zo snel, 0.5 is twee keer zo langzaam. -1 is achterstevoren.
- trig hiermee trigger je het afspelen.
- loop hiermee bepaal je of het afspelen loopt of niet
Natuurlijk zijn er ook andere manieren van afspelen mogelijk. Zie hiervoor onder andere de UGens: GrainBuf.ar()
en TGrains.ar()
.
Busses
Binnen SuperCollider is het mogelijk om via busses audio van de ene plek naar de andere plek te sturen. Dus bijvoorbeeld van meerdere oscillators naar een reverb, of vanuit meerdere effect-SynthDefs naar een sample-recorder.
busnummers
Elk van de 128 audio-busses in SuperCollider heeft een nummer. Ook de in- en output van de aangesloten geluidskaart vallen onder deze 128 busses. De eerste x busses zijn voor de output van je geluidskaart, de volgende x busses zijn voor de input van je geluidskaart, de overige busses zijn vrij te gebruiken.
Stel dat je een geluidskaart hebt aangesloten met 8 in- en outputs dan ziet de bus-verdeling er als volgt uit:
- 0 - 7: output
- 8 - 15: input
- 16- 127: vrij te gebruiken Wil je voor jouw reverb-UGen bijvoorbeeld bus 20 gebruiken, dan kan dat in dit geval. Mocht je echter omschakelen naar een geluidskaart met 16 in- en outputs, dan is bus 20 gelijk aan audio input 5 en niet meer in te zetten als vrije bus.
Er zijn twee manieren om dit op te lossen:
- Zorg dat je busses gebruikt die hoger uitvallen dan wat je ooit verwacht te gebruiken voor je in- en outputs.
- Maak gebruik van het
Bus.audio()
-object. Hiermee zorgt SuperCollider dat automatisch de eerste beschikbare bus wordt gebruikt, ongeacht het aantal in- en outputs.
Bus.audio();
Met Bus.audio()
zorgt SuperCollider dat je een bus reserveert die beschikbaar is, ongeacht het aantal in- en outputs. je gebruikt dit op de volgende manier:
~reverbBus = Bus.audio(s,2);
- s geeft de server aan waar de bus aangemaakt moet worden, meestal de default-server
- 2 geeft het aantal kanalen aan dat verstuurd wordt over de bus.
Deze bus sla je op in een variabele zodat je er later mee aan de slag kan.
Bus als input
Je kunt met In.ar()
een bus inzetten als input van een SynthDef. Zie onderstaand voorbeeld:
~reverbBus = Bus.audio(s,2);
SynthDef(\reverb,{|inBus,roomSize|
var sig;
sig = In.ar(inBus,2);
sig = FreeVerb.ar(sig,1,roomSize);
Out.ar(0,sig);
}).add;
~reverb = Synth(\reverb,[\inBus,~reverbBus]);
Bus als output
Net zoals je met Out.ar()
iets naar de output van je geluidskaart kan sturen, kan je ook een signaal naar een bus sturen, zie onderstaand voorbeeld:
~reverbBus = Bus.audio(s,2);
(
SynthDef(\reverb,{|inBus,roomSize|
var sig;
sig = In.ar(inBus,2);
sig = FreeVerb.ar(sig,1,roomSize);
Out.ar(0,sig);
}).add;
SynthDef(\synth,{|outBus,freq=440,mix|
var sig;
sig = SinOsc.ar(freq,0,0.5);
Out.ar(outBus,sig*mix);
Out.ar(0,sig*(1-mix));
}).add;
)
~reverb = Synth(\reverb,[\inBus,~reverbBus]);
~synth = Synth(\synth,[\outBus,~reverbBus,\mix,0.5]);