with OS 4.0 C&G just reorganised the folder structure slightly, you can find it here:
thanks so much for the quick answer !
Hi @thetechnobear , I have some questions about the mother.scd patch, if you like to exchange about this topicā¦
As there is a lot of good sounding SC patches out there, I was planning to adapt some for my organelle! (and share it with the community)
But Iām not sure to understand what is missing, in order to use all the functionalities of the organelle (like aux button, expression pedal, midi messages, output volume ?)
Also, I couldnāt play a sound file using some buffer manipulation examples (that I can adapted, following the example of simple synth) and I didnāt understand what was the reason (I tried to put the sound file in the same folder as the main.scd patch or using a sound that should be installed with supercollider). I wanted to check error messages in the dialog window of supercollider, using VNC viewer on a laptop but apparently the patch window is not opening with the sc app on organelle desktop (unlike PD which is possible to program directly in the organelle, with this method). Is it something doable by any way ?
I donāt have a long experience with sc but I have a friend who is kind of sc guru. He may help if needed, but for that I must know a bit more about how the mother patch works.
If you can give some help to start, it would be really nice !
have you taken a look at my example patch?
Ive also explained this patch in this post
you should make sure you understand how this works and implement a few patches before you considering āextending itā
how to extend past the functionality Iāve implemented this should be fairly straightforward using what I have already implemented as a basis.
bare in mind that all communication with the screen and hardware is done via OSC message to/from the mother host - you can see how these messages are handled in PureData, and simply implement these in the mother.scd in the same way I did the current set of messages.
note: re vumeter
so \OrganelleAudioLevel in mother.scd is now wrong, the reason is because OS 4.0 changed the call to /oled/vumeter to include a peak level - you can see this in mother.pd.
so if you wish this to work, you will need to override this OSCdef to correct it.
note:
its important to recognise, you do not to have use what I have done in mother.scd at all! ,
really these are just āhelperā functions, that I thought would be be useful to have - but your supercollider patch, could ignore them entirely, and simply use things like OSCdef (as i do in mother.scd) directly⦠which you may find simpler?!
This is standard supercollider stuff, try using a fixed path.
What I would recommend is getting the patches working on your desktop first, and then learn to adapt them to the Organelle environment (i.e. adding the āUIā) - really there is not much different.
obviously, the Organelle has not where near the cpu resources or memory that a desktop/laptop has, so keep this in mind during the development phase.
on a more general note, Id highly recommend Eli Fieldsteelās Supercollider videos, they are absolutely amazing - a gem of youtube, and will really help you get started in Supercollider.
Hi @thetechnobear !
Thanks again, for all your help So nice !
I finally succeeded in doing a first Supercollider patch for the Organelle.
Itās a simple metal percussion rhythm generator based on a physical model, that I took from SC examples.
Simple but it already sounds quite good. Still, there is some mysteries about some Organelle functions Iād like to solve before sharing it in Patchstorage website.
As Iām a new forum user, I canāt upload a zip file, so I send you the code directly, at the end of this message.
There is 3 things Iād like to do with it:
- make the output vumeters work. (I took the code from the mother patch that I thought could do the job, but I donāt know how to do, as there is no documentation about it)
- use the AUX button as a sustain control. (same pb as above)
- in a later version I would like to synchronise to a midi clock, to generate rhythms accordingly. (any explanations in how the midi is handled by this version of Organelle/Supercollider would be really great)
If I can do that, Iāll start soon to share some patches, cause there is a lot of interesting things out there, just waiting to be shrunk into this cute little box !
Here is my code (sorry itās many not very convenient, and tell me if there a way to send directly the zip file):
//////
// initialise
~voices = Dictionary.new;
~bandwith=8000.0;
~partials=8;
~impulse=0.01;
~ringtime=2.0;
~oled.screen(1,format(āBandwith % Hzā,~bandwith.asInteger));
~oled.screen(2,format(āPartials %ā,~partials.asInteger));
~oled.screen(3,format(āImpulse %ā,~impulse.asStringPrec(3)));
~oled.screen(4,format(āRingTime % secā,~ringtime.asStringPrec(3)));
~oled.screen(5,āSustain OFFā);
// output for vumeter and volume control
~mainVolume = 0.8;
~mainOut = nil;
~outL = Bus.audio(s,1);
~outR = Bus.audio(s,1);
~mainOutDef = SynthDef(\mainOutput,
{
arg amp = 0.8;
var insig,sig, peaksig;
insig = SoundIn.ar([0,1]);
sig = In.ar(~outL,2);
sig = sig * amp;
Out.ar(0,sig);
peaksig=[insig[0],insig[1],sig[0],sig[1]];
SendPeakRMS.kr(peaksig, 5, 3, ā/audioLevelā);
}
);
// create knob callback
~knobfunc = {
arg func, msg, knob, value;
if(knob==0, {
~bandwith = value.linexp(0,1,20,8000);
~oled.screen(1,format(āBandwith % Hzā,~bandwith.asInteger));
});
if(knob==1, {
~partials = value.linlin(0,1,2,16).asInteger;
~oled.screen(2,format("Partials %",~partials));
});
if(knob==2, {
~impulse = value.linexp(0,1,0.003,3.0);
~oled.screen(3,format("Impulse %",~impulse.asStringPrec(3)));
});
if(knob==3, {
~ringtime = value.linexp(0,1,0.1,10.0);
~oled.screen(4,format("RingTime % s",~ringtime.asStringPrec(3)));
});
};
// register knob callback
~knobs.addDependant(~knobfunc);
// aux callback
~aux[\key_hit] = {
arg self,key,vel;
self.changed(\aux,vel>0);
if(~aux>0,
{~oled.screen(5,āSustain ONā);
~led.value(7);
},
{~oled.screen(5,āSustain OFFā);
~led.value(0);
});
};
// create notes callback
~notesfunc = {
arg func, msg, note, vel;
if(vel>0 , {
if(~voices[note]!=nil,{~voices[note].set(\gate,0);});
~voices[note] = Synth.new(āKlankSynth_ā++~partials.asSymbol,[
\freq, (note).linexp(60,83,20,8000),
\bandwith, ~bandwith,
\impulse, ~impulse,
\ringtime, ~ringtime,
\pan, 1.0.rand2,
\rhythm, #[0.125,0.25,0.375,0.5,0.75,1,1.5,2].choose
// \amp, (vel / 3.0) .clip(0,0.3)
]
);
~led.value(1);
} , {
if(~voices[note]!=nil,{~voices[note].set(\gate,0);});
~voices[note] = nil;
~led.value(0);
}; )
};
// register key callback
~notes.addDependant(~notesfunc);
//other callbacks
//~encoder_turn
//~encoder_button
//~footswitch
// metallic synths
SynthDef(\KlankSynth_2, {
arg freq=20, bandwith=8000.0, impulse=0.5, ringtime=2.0, gate=1, pan=0.0, rhythm=1;
var trig, p, exc, x, sig, env, partials=2;
exc = BrownNoise.ar(Decay2.kr(Impulse.kr(rhythm,0,0.04), 0.1, impulse));
env = EnvGen.ar(Env.adsr(),gate,doneAction:2);
sig = (Klank.ar(`[Array.fill(partials, {linrand(bandwith)+freq}),nil,Array.fill(partials, {rrand(0.1,ringtime)})], exc) * 0.35).softclip;
sig = sig * env;
sig = Pan2.ar(sig,pan);
Out.ar(~outL,sig);
}
).add;
SynthDef(\KlankSynth_3, {
arg freq=20, bandwith=8000.0, impulse=0.5, ringtime=2.0, gate=1, pan=0.0, rhythm=1;
var trig, p, exc, x, sig, env, partials=3;
exc = BrownNoise.ar(Decay2.kr(Impulse.kr(rhythm,0,0.04), 0.1, impulse));
env = EnvGen.ar(Env.adsr(),gate,doneAction:2);
sig = (Klank.ar(`[Array.fill(partials, {linrand(bandwith)+freq}),nil,Array.fill(partials, {rrand(0.1,ringtime)})], exc) * 0.35).softclip;
sig = sig * env;
sig = Pan2.ar(sig,pan);
Out.ar(~outL,sig);
}
).add;
SynthDef(\KlankSynth_4, {
arg freq=20, bandwith=8000.0, impulse=0.5, ringtime=2.0, gate=1, pan=0.0, rhythm=1;
var trig, p, exc, x, sig, env, partials=4;
exc = BrownNoise.ar(Decay2.kr(Impulse.kr(rhythm,0,0.04), 0.1, impulse));
env = EnvGen.ar(Env.adsr(),gate,doneAction:2);
sig = (Klank.ar(`[Array.fill(partials, {linrand(bandwith)+freq}),nil,Array.fill(partials, {rrand(0.1,ringtime)})], exc) * 0.35).softclip;
sig = sig * env;
sig = Pan2.ar(sig,pan);
Out.ar(~outL,sig);
}
).add;
SynthDef(\KlankSynth_5, {
arg freq=20, bandwith=8000.0, impulse=0.5, ringtime=2.0, gate=1, pan=0.0, rhythm=1;
var trig, p, exc, x, sig, env, partials=5;
exc = BrownNoise.ar(Decay2.kr(Impulse.kr(rhythm,0,0.04), 0.1, impulse));
env = EnvGen.ar(Env.adsr(),gate,doneAction:2);
sig = (Klank.ar(`[Array.fill(partials, {linrand(bandwith)+freq}),nil,Array.fill(partials, {rrand(0.1,ringtime)})], exc) * 0.35).softclip;
sig = sig * env;
sig = Pan2.ar(sig,pan);
Out.ar(~outL,sig);
}
).add;
SynthDef(\KlankSynth_6, {
arg freq=20, bandwith=8000.0, impulse=0.5, ringtime=2.0, gate=1, pan=0.0, rhythm=1;
var trig, p, exc, x, sig, env, partials=6;
exc = BrownNoise.ar(Decay2.kr(Impulse.kr(rhythm,0,0.04), 0.1, impulse));
env = EnvGen.ar(Env.adsr(),gate,doneAction:2);
sig = (Klank.ar(`[Array.fill(partials, {linrand(bandwith)+freq}),nil,Array.fill(partials, {rrand(0.1,ringtime)})], exc) * 0.35).softclip;
sig = sig * env;
sig = Pan2.ar(sig,pan);
Out.ar(~outL,sig);
}
).add;
SynthDef(\KlankSynth_7, {
arg freq=20, bandwith=8000.0, impulse=0.5, ringtime=2.0, gate=1, pan=0.0, rhythm=1;
var trig, p, exc, x, sig, env, partials=7;
exc = BrownNoise.ar(Decay2.kr(Impulse.kr(rhythm,0,0.04), 0.1, impulse));
env = EnvGen.ar(Env.adsr(),gate,doneAction:2);
sig = (Klank.ar(`[Array.fill(partials, {linrand(bandwith)+freq}),nil,Array.fill(partials, {rrand(0.1,ringtime)})], exc) * 0.35).softclip;
sig = sig * env;
sig = Pan2.ar(sig,pan);
Out.ar(~outL,sig);
}
).add;
SynthDef(\KlankSynth_8, {
arg freq=20, bandwith=8000.0, impulse=0.5, ringtime=2.0, gate=1, pan=0.0, rhythm=1;
var trig, p, exc, x, sig, env, partials=8;
exc = BrownNoise.ar(Decay2.kr(Impulse.kr(rhythm,0,0.04), 0.1, impulse));
env = EnvGen.ar(Env.adsr(),gate,doneAction:2);
sig = (Klank.ar(`[Array.fill(partials, {linrand(bandwith)+freq}),nil,Array.fill(partials, {rrand(0.1,ringtime)})], exc) * 0.35).softclip;
sig = sig * env;
sig = Pan2.ar(sig,pan);
Out.ar(~outL,sig);
}
).add;
SynthDef(\KlankSynth_9, {
arg freq=20, bandwith=8000.0, impulse=0.5, ringtime=2.0, gate=1, pan=0.0, rhythm=1;
var trig, p, exc, x, sig, env, partials=9;
exc = BrownNoise.ar(Decay2.kr(Impulse.kr(rhythm,0,0.04), 0.1, impulse));
env = EnvGen.ar(Env.adsr(),gate,doneAction:2);
sig = (Klank.ar(`[Array.fill(partials, {linrand(bandwith)+freq}),nil,Array.fill(partials, {rrand(0.1,ringtime)})], exc) * 0.35).softclip;
sig = sig * env;
sig = Pan2.ar(sig,pan);
Out.ar(~outL,sig);
}
).add;
SynthDef(\KlankSynth_10, {
arg freq=20, bandwith=8000.0, impulse=0.5, ringtime=2.0, gate=1, pan=0.0, rhythm=1;
var trig, p, exc, x, sig, env, partials=10;
exc = BrownNoise.ar(Decay2.kr(Impulse.kr(rhythm,0,0.04), 0.1, impulse));
env = EnvGen.ar(Env.adsr(),gate,doneAction:2);
sig = (Klank.ar(`[Array.fill(partials, {linrand(bandwith)+freq}),nil,Array.fill(partials, {rrand(0.1,ringtime)})], exc) * 0.35).softclip;
sig = sig * env;
sig = Pan2.ar(sig,pan);
Out.ar(~outL,sig);
}
).add;
SynthDef(\KlankSynth_11, {
arg freq=20, bandwith=8000.0, impulse=0.5, ringtime=2.0, gate=1, pan=0.0, rhythm=1;
var trig, p, exc, x, sig, env, partials=11;
exc = BrownNoise.ar(Decay2.kr(Impulse.kr(rhythm,0,0.04), 0.1, impulse));
env = EnvGen.ar(Env.adsr(),gate,doneAction:2);
sig = (Klank.ar(`[Array.fill(partials, {linrand(bandwith)+freq}),nil,Array.fill(partials, {rrand(0.1,ringtime)})], exc) * 0.35).softclip;
sig = sig * env;
sig = Pan2.ar(sig,pan);
Out.ar(~outL,sig);
}
).add;
SynthDef(\KlankSynth_12, {
arg freq=20, bandwith=8000.0, impulse=0.5, ringtime=2.0, gate=1, pan=0.0, rhythm=1;
var trig, p, exc, x, sig, env, partials=12;
exc = BrownNoise.ar(Decay2.kr(Impulse.kr(rhythm,0,0.04), 0.1, impulse));
env = EnvGen.ar(Env.adsr(),gate,doneAction:2);
sig = (Klank.ar(`[Array.fill(partials, {linrand(bandwith)+freq}),nil,Array.fill(partials, {rrand(0.1,ringtime)})], exc) * 0.35).softclip;
sig = sig * env;
sig = Pan2.ar(sig,pan);
Out.ar(~outL,sig);
}
).add;
SynthDef(\KlankSynth_13, {
arg freq=20, bandwith=8000.0, impulse=0.5, ringtime=2.0, gate=1, pan=0.0, rhythm=1;
var trig, p, exc, x, sig, env, partials=13;
exc = BrownNoise.ar(Decay2.kr(Impulse.kr(rhythm,0,0.04), 0.1, impulse));
env = EnvGen.ar(Env.adsr(),gate,doneAction:2);
sig = (Klank.ar(`[Array.fill(partials, {linrand(bandwith)+freq}),nil,Array.fill(partials, {rrand(0.1,ringtime)})], exc) * 0.35).softclip;
sig = sig * env;
sig = Pan2.ar(sig,pan);
Out.ar(~outL,sig);
}
).add;
SynthDef(\KlankSynth_14, {
arg freq=20, bandwith=8000.0, impulse=0.5, ringtime=2.0, gate=1, pan=0.0, rhythm=1;
var trig, p, exc, x, sig, env, partials=14;
exc = BrownNoise.ar(Decay2.kr(Impulse.kr(rhythm,0,0.04), 0.1, impulse));
env = EnvGen.ar(Env.adsr(),gate,doneAction:2);
sig = (Klank.ar(`[Array.fill(partials, {linrand(bandwith)+freq}),nil,Array.fill(partials, {rrand(0.1,ringtime)})], exc) * 0.35).softclip;
sig = sig * env;
sig = Pan2.ar(sig,pan);
Out.ar(~outL,sig);
}
).add;
SynthDef(\KlankSynth_15, {
arg freq=20, bandwith=8000.0, impulse=0.5, ringtime=2.0, gate=1, pan=0.0, rhythm=1;
var trig, p, exc, x, sig, env, partials=15;
exc = BrownNoise.ar(Decay2.kr(Impulse.kr(rhythm,0,0.04), 0.1, impulse));
env = EnvGen.ar(Env.adsr(),gate,doneAction:2);
sig = (Klank.ar(`[Array.fill(partials, {linrand(bandwith)+freq}),nil,Array.fill(partials, {rrand(0.1,ringtime)})], exc) * 0.35).softclip;
sig = sig * env;
sig = Pan2.ar(sig,pan);
Out.ar(~outL,sig);
}
).add;
SynthDef(\KlankSynth_16, {
arg freq=20, bandwith=8000.0, impulse=0.5, ringtime=2.0, gate=1, pan=0.0, rhythm=1;
var trig, p, exc, x, sig, env, partials=16;
exc = BrownNoise.ar(Decay2.kr(Impulse.kr(rhythm,0,0.04), 0.1, impulse));
env = EnvGen.ar(Env.adsr(),gate,doneAction:2);
sig = (Klank.ar(`[Array.fill(partials, {linrand(bandwith)+freq}),nil,Array.fill(partials, {rrand(0.1,ringtime)})], exc) * 0.35).softclip;
sig = sig * env;
sig = Pan2.ar(sig,pan);
Out.ar(~outL,sig);
}
).add;
OSCdef( \OrganelleAudioLevel,
{
arg msg, time, addr, recvPort;
var peakInL,peakInR,peakOutL,peakOutR;
peakInL = (msg[3] * 11).asInteger;
peakInR = (msg[5] * 11).asInteger;
peakOutL = (msg[7] * 11).asInteger;
peakOutR = (msg[9] * 11).asInteger;
~motherHost.sendMsg("/oled/vumeter", peakInL,peakInR,peakOutL,peakOutR);
},
"/audioLevel"
);
tl;dr use this snippet. Read on for details.
OSCdef(\OrganelleAudioLevel, { arg msg, time, addr, recvPort;
var peakInL, peakInR, peakOutL, peakOutR;
peakInL = (msg[3] * 11).asInteger;
peakInR = (msg[5] * 11).asInteger;
peakOutL = (msg[7] * 11).asInteger;
peakOutR = (msg[9] * 11).asInteger;
~motherHost.sendMsg("/oled/vumeter", peakInL, peakInR, peakOutL, peakOutR, 1);
},
"/audioLevel"
);
Iām a total n00b when it comes to Supercollider, but the snippet above adjusts the OSCdef(\OrganelleAudioLevel
with the tip @thetechnobear mentioned farther above.
The key is the 5th argument required by /oled/vumeter
. From what I can tell this argument allows the meter to track what the last peak was, Iām cheating and just statically assigning 1
so that the meter at least works. Perhaps in the future Iāll not be so lazy and adjust so that it works as intended and of course post back here.
In general just crack open mother.pd
to see the full functionality available for OSC messages.
Not sure if I am alone here but on my vanilla Organelle M I would get a black screen running @thetechnobearās test SC patch⦠running OS 4.2
I think I may have found the culprit? It looks like MainMenu.cpp is looking for sclang in a different place than the default install that started to be built in with newer Organelle_OS versions.
I submitted a PR to correct this and there is more detail there as well as a workaround if folks are interested. Fix SC on Organelle M by dessertplanet Ā· Pull Request #47 Ā· critterandguitari/Organelle_OS Ā· GitHub
I am super interested in messing around with SC capabilities on Organelle so looking forward to getting into this!
Also caught one more bit of weirdness. SuperCollider needs X to be running. This means on Organelle you have to do one of the following before any SC patches will work:
- connect a monitor and run startx from cmdline (you can then unplug your monitor!?)
- ssh into organelle and then add the environment variable DISPLAY=:0.0 before launching any SC patches
- Just enable VNC in settings! This appears to do what is necessary. You donāt even need to connect Wi-if or AP.
I found details here: https://github.com/supercollider/supercollider/blob/develop/README_RASPBERRY_PI.md
I was only not hitting this before because I was always hooked up to VNC and therefore X was running. Good to know!
Back to this finally
Iāve successfully built SuperCollider 3.11 from source on my Organelle M, with Supernova enabled (for multi-threaded shenanigans) and QT disabled, following the āgui-lessā build approach from the SC GitHub. After installing jackd supercollider patches load seamlessly and without any of the display-related errors that are observed on the in-built version.
The new build put sclang in /usr/local/bin (where the MainMenu.cpp pointed to before my recent PR) so unpatched Organelle OS 4.2 automatically points to the new installation without incorporating my change.
Scide is still installed and runs on the old version which is handy (No_QT version doesnāt include the IDE).
Here are the steps I took to get this far:
remount as rw!
Install dependencies for non-GUI Build from the instructions here
Upgrade cmake to 12.0 using bootstrap/make/make install from source tar.gz available in āolder releasesā here
Build and install SC 3.11 from source following instructions linked above (need to switch to the 3.11 branch, I think they broke something on RPi in later releases, I had compile issues)
When it came time to pass cmake parameters these are the incantations that finally worked:
cmake -DCMAKE_BUILD_TYPE=Release -DSUPERNOVA=ON -DSC_ED=OFF -DSC_EL=OFF DSC_VIM=ON -DNATIVE=ON -DSC_QT=OFF ..
After install I ran the ldconfig command as described and didnāt touch jack since I know MainMenu.cpp runs start-jack.sh which appears to have similar functionality.
Tested with the SimplePoly.sc patch and created a blank SC patch as well for starting mother.scd and then sending messages remotely. My ultimate goal is to get that darn DX7 patch working (I know Faustās DX7 is out there but this one is different right?).
Iāve grabbed an .img file that you can put on your own sdcard if you are familiar with etcher and understand the risk of data loss etc. but it is too big to upload here. If anybody is interested in kicking the tires, let me know.
Hi dessertplanet, I got the DX7 patch working in a simple way quite a while ago. (see this thread Status of SuperCollider? - #21 by DaveBowman)
If i remember rightly the UI was a bit rough but it worked! also looking at your instructions, I pretty sure my firmware I uploaded in that thread is pretty much the same method you used to get supercollider working
Hi @teapot !
Cool I will have to give your image a try. I got a version of it working as well that I never shared because I couldnāt quite get the presets stable. Might revisit it soon. I wanted to set it up so you could do ācar radioā style preset favourites. Polyphony was also a challenge because the original DX7 patch leverages multi-threading via supernova. I could get it going for most presets up to three note polyphony. After that things got weird.
If you are still interested in SC on Organelle you should check out my Icarus patch Icarus for Organelle
I wrote a custom mother.scd for that patch that handles VU meter, MIDI, Audio etc. and also the install script for that patch allows for āliving off the landā on an existing install- it installs jackd and sets things up so that only the VNC/display hack is necessary for things to work. The mother.scd and install script should in theory be useful for any SC patches on Organelle.
for mine I just did the easiest thing which was to multiply the value of knob1 by knob2 to set the preset⦠so maybe yourās was a bit further along anyway!
Here is what I had- though I havenāt touched it in ages and probably didnāt really understand supercollider at that time
Following up here- after a couple months of work and considerably more than that scheming about this, I released my take on the supercollider DX7 this week! @teapot