Loading external .scd files

I am fairly new to SuperCollider and still trying to get my bearings.
I have found the ability to load external .scd files in the documentation like so:
“/home/pi/customlibs/launch_control.scd”.load;
This does in fact execute that scd file, but none of the variables I have assigned in that file are accessible out of that file. I tried declaring the variables used in that file prior to load like so:

 var lc, on, lc_pi, lc_uid, getLaunchControl, turnPad;
 "/home/pi/customlibs/launch_control.scd".load;
 {SinOsc.ar([400, 404])}.play;
 lc = getLaunchControl.value;
 turnPad.value(1,"green",lc);

However, the assignments I make in launch_control.scd of getLaunchControl and turnPad do not seem to effect this namespace, as my later code does not call the function. Is it possible for the loaded scd file to write to the global namespace of the scd file doing the loading?

Perhaps what I need to do is write classes and load them in as extensions. I am trying to define common functionality across many SuperCollider scripts, which I can just load in depending on the project. I’d like to avoid classes if I have to, so let me know if there is another way to do this. But if the only way to do this is through classes, let me know and I will make the dive. I have taken a few stabs at classes already, but the documentation is hard to interpret for someone new to SuperCollider (I am not new to OOP though). I will happily take recommendations for tutorials if that is the case.

1 Like

I just fixed this by adding tilde’s in my function definition of the loaded files, and function calls in my main file. So this:

 var lc, on, lc_pi, lc_uid;
 "/home/pi/customlibs/launch_control.scd".load;
 {SinOsc.ar([400, 404])}.play;
 lc = ~getLaunchControl.value;
 ~turnPad.value(1,"green",lc);

and in my launch_control.scd:

~getLaunchControl = { // some code here };
~turnPad = { | pad_no, state, lc_object | // some code here };

But if someone wants to advise me on the appropriate context to use classes, I am happy to hear it

You could also have a look at the Require quark.
You can install it with Quarks.gui

You can use Dictionaries to keep things in the same namespace without having to use global variables. The trick here is to write your external file as a Function, read the file as a string, and pass in an existing Dictionary (or subclass). It’s a bit of a pain but it works.

I made a class extension for File (*.include) that does this, with or without a parent Dictionary, always returning the result. If the parent Dictionary is nil, it just evaluates the contents of the file as is, otherwise it evaluates the file in the context of the Dictionary.

Usage:

var myDict;
myDict = (); // an Event for simpler syntax
File.include("path/to/my/file.scd", myDict); // takes the path and an optional dictionary, returning the result
myDict.someFunction.value(); // evalulate a function that's now in myDict
myDict.someValue; // get some value that was set

In this case, file.scd would be a function which takes a single argument:

{|dict|
  // make some function
  dict.someFunction = {|x|
    x.postln;
  };
  // set some value
  dict.someValue = 10;
}

Coming from nodejs and not being super happy with sc code reutilization patterns, I’m bummed 'cause I can’t install the require Quark. It fails installing the UnitTesting Quark, which is a dependency :frowning: (I’m under linux, using 3.10 btw)

Ah… UnitTesting was included in core, hence the error.
Maybe you can fill an issue on the Quark’s github page ?

I think you still can use the Require quark, even though UnitTesting fails installing on versions >=3.9. The Require quark should work anyway. And since it needs UnitTesting for versions < 3.9, it correctly lists it as a dependency.

If you have installed the Require quark on 3.10, and UnitTesting for some reason got installed, you will get duplicate class errors from UnitTesting. In that case you can go to Preferences > Interpreter and remove UnitTesting from the include paths list. After a recompile everything should (hopefully) be ok.

FYI I had fixed several other of my Quarks with issues related to UnitTesting, but I forgot Require. Just fixed it now, so if you update and install again it should PROBABLY fix things. Also, as previous replies suggested, you can always manually remove UnitTesting from your includes in the IDE.

The issue with UnitTesting now is that it will (rightfully) not get installed on 3:10, but that also blocks installs of third party Quarks that list it as dependency. So for 3.10 the only option is to install classes that still list UnitTesting as a dependency manually I guess (by downloading a repository).

I wouldn’t really suggest this - installing by downloading will just mean you miss the other dependencies. It’s much easier to just remove the UnitTesting include once you’ve done installs (if it happens to creep in). Apart from Require (just fixed) I think there are only one or two other quarks that have UnitTesting as a dep left.

So, just installed Require using the latest version from github, I’m having an error

Message 'isFile' not understood.
RECEIVER: /home/devnull/Code/supercollider/playground/12_12.scd

when evaluating the following line

Require("/home/devnull/Code/supercollider/Setup.scd")

and also when importing a file from a relative file. I can’t file an issue in the repo since I don’t have a github account anymore (migrated to gitlab). Seeing the source code, the error is at line 104 (I think, since SC stack traces are pretty uninformative, sadly…)

I wouldn’t really suggest this - installing by downloading will just mean you miss the other dependencies. It’s much easier to just remove the UnitTesting include once you’ve done installs (if it happens to creep in).

@scztt my intention was not to suggest this (suboptimal, if the only) solution, but to highlight another problem: on 3.10 you are unable to install a quark depending on UnitTesting. An attempt results in this (here installing MachineListetningUnitTests):

Installing MachineListeningUnitTests
Installing UnitTesting
UnitTesting reports an incompatibility with this SuperCollider version or with other already installed quarks.
ERROR: Failed to install MachineListeningUnitTests

Correct me if I’m wrong, but if you absolutely need the quark isn’t manual installation the only solution?
As you said, we hopefully have most of the UnitTesting dependencies removed by now…

Ah, I did not know that UnitTesting expressly reported an incompatibility. Not really sure I agree with that, as it breaks more than it fixes.

I had a fix for this that I had not pushed yet - I just pushed it, so the problem should be resolved if you update the quark.

1 Like

Can this also be done using Interpreter.compileFile? I would prefer to avoid using quarks to solve this, if possible. I’m trying this with a very simple target file and I keep getting an error that my parameters are nil.

sine.scd:

{
	arg freq=440, amp=1;
	("freq " ++ 440 ++ " amp " ++ amp).postln;
	SinOsc.ar(freq, mul: amp);
}

Neighboring script:

thisProcess.interpreter
.compileFile(
    PathName.new(thisProcess.nowExecutingPath).pathOnly ++ "sine.scd"
)
.play(freq: 440, amp: 1);

The arguments are posted as expected:

freq 440 amp 1

But then, immediately following, an error is logged:

SinOsc arg: 'freq' has bad input: nil

It seems like the OutputProxy isn’t being properly constructed somehow.

Another interesting thing: I took the function implementation from sine.scd, inlined it, and assigned it to another variable for comparison. That one works, and the other one doesn’t, but when I .postString them, their implementations indeed appear to be identical. Is there some environment property at work here?

EDIT: The original .load implementation works great, though. I’ll roll with that for now.

The Require quark will not solve the namespace problem, because it’s an architectural issue, not a specific problem of load. So, you don’t need it.

Running an external file goes through these steps:

  1. Get the code string from disk.
  2. Compile it into a function. It’s exactly like { ... code string from file ... }.
  3. .value this function.

Step 2 means that all variables inside the compiled function are local to the function. Local variables cannot be accessed outside that scope. The Require quark doesn’t – can’t – have any magic to get around that.

I favor woolgathering’s approach – use a dictionary as a conduit to pass information out of the inner scope.

compileFile is step 2 above – wrapping the code in the file within a function. So really you’re getting { { arg freq... } }. Then play tries to wrap the (outer) function’s result (the inner function) in an Out.ar. This requires evaluating the inner function, but no arguments are passed at this time – so freq is not promoted to be a synth control.

Better here to load the file – then you get the function you defined, not a function returning the function you defined.

Also note that this isn’t the right way to use synth arguments with Function:play. Should be play(args: [freq: 440... ]).

hjh

2 Likes

This makes perfect sense, thank you.

I apologize for neglecting to mention that meanwhile I arrived at just using .load.value and the correct args format from reading the thread more closely and through experimentation.

I didn’t realize the compileFile worked just like wrapping the contents in curly braces, but this is also consistent with what I observed. This is much clearer now.