Chat and questions about the Scala based SuperCollider client. Newbies welcome. @ me if you need fast replies, otherwise I might not see new posts in a few days. Also checkout https://gitter.im/Sciss/Mellite for the computer music environment that embeds ScalaCollider.
hello!
got a little issue when running synth.Server
i'm running on the v1.18.1 you just published but i got the same issue on v1.18.0
it's not really an issue since it was working fine yesterday but now i get the "Exception in World_OpenUDP: unable to bind udp socket"
good to know thank you!
anyway thank you for the feedbacks it's very nice to be able to talk to you so easily.
i'll ask you for further questions about UGens on the gitter but just to be clear. if we want to add extra UGens to your ScalaColliderUGens project we have to create a .xml configuration file right? i'm not sure that I understood everything on the readme..
We try to add the VBAPUGens to sc3plugin to work with scalacollider but as i said the processus still looks blurry for me..
So we have the .scx generated by supercollider but it doesn't work (as expected) :)
.xml
input and generates .scala
source code output. I'd say that is the preferred method to introduce new UGens. The "output" in this case is the .scala
files; they are not written by hand but by a code generator. So if at some point I change the API, I don't have to rewrite hundreds of UGen .scala
files, but just adapt the generator, perhaps make a few adjustments in the XML. However, for quick testing, you can take those generated .scala
output files as a clue on how they look, and you could just copy + paste + edit them for new UGens, if you don't want to go through the process of defining the .xml
files. If you come up with xml files for the VBAPUGen plugin, I am happy to include it with the project, so future updates will make the classes available. If you have specific questions about the meaning of the XML fields (they are explained a bit in the readme), I can help with that. Basically one xml file = one plugin (.scx file; collection of related ugens), although that organisation is not mandatory. TJUGens is an example for third-party plugins: https://github.com/Sciss/ScalaColliderUGens/blob/master/spec/src/main/resources/de/sciss/synth/ugen/TJUGens.xml
The generated output is not part of the repository. But if you open the published jars, e.g. you open the sources.jar from http://search.maven.org/#artifactdetails|de.sciss|scalacolliderugens-core_2.11|1.14.0|jar - you will see all the .scala
files thus produced. If you are using an IDE such as IntelliJ or Eclipse, you can probably also just jump from a symbol, say SinOsc
to its source code (if you selected to include library source code in your project). So you get from this:
<ugen name="SinOsc">
<rate name="audio"/>
<rate name="control"/>
<arg name="freq" default="440.0">
<doc>
frequency in Hertz
</doc>
</arg>
<arg name="phase" default="0.0">
<doc>
phase offset or modulator in radians
</doc>
</arg>
<doc>
<text>
A sinusoidal (sine tone) oscillator UGen.
This is the same as `Osc` except that it uses a built-in interpolating sine table of 8192 entries.
</text>
<see>ugen.Osc</see>
<see>ugen.FSinOsc</see>
</doc>
</ugen>
to this:
/** A sinusoidal (sine tone) oscillator UGen. This is the same as `Osc` except that
* it uses a built-in interpolating sine table of 8192 entries.
*
* @see [[de.sciss.synth.ugen.Osc$ Osc]]
* @see [[de.sciss.synth.ugen.FSinOsc$ FSinOsc]]
*/
object SinOsc {
def kr: SinOsc = kr()
/** @param freq frequency in Hertz
* @param phase phase offset or modulator in radians
*/
def kr(freq: GE = 440.0f, phase: GE = 0.0f): SinOsc = new SinOsc(control, freq, phase)
def ar: SinOsc = ar()
/** @param freq frequency in Hertz
* @param phase phase offset or modulator in radians
*/
def ar(freq: GE = 440.0f, phase: GE = 0.0f): SinOsc = new SinOsc(audio, freq, phase)
}
/** A sinusoidal (sine tone) oscillator UGen. This is the same as `Osc` except that
* it uses a built-in interpolating sine table of 8192 entries.
*
* @param freq frequency in Hertz
* @param phase phase offset or modulator in radians
*
* @see [[de.sciss.synth.ugen.Osc$ Osc]]
* @see [[de.sciss.synth.ugen.FSinOsc$ FSinOsc]]
*/
final case class SinOsc(rate: Rate, freq: GE = 440.0f, phase: GE = 0.0f) extends UGenSource.SingleOut {
protected def makeUGens: UGenInLike = unwrap(Vector(freq.expand, phase.expand))
protected def makeUGen(_args: Vec[UGenIn]): UGenInLike = UGen.SingleOut(name, rate, _args)
}
ok so i came up with a partial xml file for VBAPUgen.
The VBAPUgen contains 3 classes : VBAP, VBAPSpeaker and VBAPSpeakerArray
VBAPSpeaker and VBAPSpeakerArray only have new() method. what would be the equivalent for the generator to understand on the xml.
<ugens revision="1">
<ugen name="VBAP">
<rate name="audio"/>
<rate name="control"/>
<arg name="numChans">
<doc>
the number of output channels
</doc>
</arg>
<arg name="in">
<doc>
the input to be panne
</doc>
</arg>
<arg name="bufnum">
<doc>
a buffer or it's bufnum containing data calculated by an instance of VBAPSpeakerArray
it's number of channels must correspond to numChans above
</doc>
</arg>
<arg name="azimuth" default="0">
<doc>
+/- 180° from the medium plane
</doc>
</arg>
<arg name="elevation" default="1">
<doc>
+/- 90° from the azimuth plane
</doc>
</arg>
<arg name="spread" default="0">
<doc>
A value from 0-100. When 0, if the signal is panned exactly to a speaker location the signal is only on that speaker.
At values higher than 0, the signal will always be on more than one speaker.
This can smooth the panning effect by making localisation blur more constant.
</doc>
</arg>
</ugen>
</ugens>
And also i have issue trying to generate from sbt this way :
$ sbt
$ project scalacolliderugens-gen
my terminal says :
project scalacolliderugens-gen
[error] Not a valid project ID: scalacolliderugens-gen
Klang
being another one, so I have a hand written KlangSpec
. The XML really just has the UGens. Their arguments are either scalar values, e.g. Int
for determining the number of channels (In.ar
) or they are graph elements GE
. So if your custom aux types can be converted to GE
then this approach works. It's not optimal in terms of type-safety, but good enough IMO. Let me have a look at the original VBAP source to refresh my memory
VBAP
, like you pasted above. The other classes it appears are only auxiliary classes, for example to calculate speaker angles and such, and are never directly used by the UGen class - if I'm not mistaken. So in order to use the UGen, you just need VBAP
. If you want to use the functions of say VBAPSpeakerSet
available from Scala, you'll have to translate that class. It's independent of ScalaCollider. Let me know if you need help with this.
// 8 channel ring
val a = VBAPSetup(2, Seq(0, 45, 90, 135, 180, -135, -90, -45))
val b = Buffer.alloc(s, a.bufferData.size)
b.setn(a.bufferData)
val x = play {
val azi = "azi".kr(0)
val ele = "ele".kr(0)
val spr = "spr".kr(0)
VBAP.ar(8, PinkNoise.ar(0.2), b.id, azi, ele, spr)
}
// test them out
x.set("azi" -> a.directions(1).azi)
x.set("azi" -> a.directions(2).azi)
x.set("azi" -> a.directions(3).azi)
// ...
x.set("azi" -> a.directions(7).azi)
x.set("azi" -> a.directions(0).azi)
// try the spread
x.set("spr" -> 20)
x.set("spr" -> 100) // all speakers
x.free(); b.free();
if (value == 1) Synth.play(ssa.name)
if (value == 2) {
ssa release(2)
println("recu 2")
}
After I created 'transactional' systems based on ScalaCollider, I wanted to remove the plain side-effecting methods from the basic API. The basic API just supports generating the OSC message; when you call a method such as synth.set(...)
or synth.free(...)
, this is facilitated by an additional import de.sciss.synth.Ops._
. So when you look at the README, you have this preamble:
import de.sciss.synth._
import ugen._
import Ops._
The last import makes the side-effects available. The docs are here: https://sciss.github.io/ScalaCollider/latest/api/#de.sciss.synth.Ops$ - so for release
, that is NodeOps
: https://sciss.github.io/ScalaCollider/latest/api/#de.sciss.synth.Ops$$NodeOps
server.run(cfg) { serv =>
var synth = Synth()
ssa.recv(serv) //previous SynthDef
receiver.action = {
case (m@osc.Message("/test", value: Int), s) =>
if (value == 1) synth = Synth.play(ssa.name)
if (value == 2) println("recu 2"); synth.release(2.0) //synth.free()
}
}
compilation is ok, free() works fine, but release() has no action still :/
val synth = play {}
That's the same as in SuperCollider - using play { }
will add an envelope that has a control gate
that is used by release
. release
is just a convention for synth.set("gate" -> -1 - releaseTime)
(or similar). So if you want to add a similar kind of envelope to a SynthDef
you either have to create an EnvGen
with a gate
argument, or you use the pseudo-UGen WrapOut
instead (that's the thing that play {}
uses):
SynthDef.recv("test") {
val sig = WhiteNoise.ar(Seq(0.2, 0.2))
WrapOut(sig) // !
}
val x = Synth.play("test")
x.release(10)
WrapOut
creates controls "out"
(for bus) and "gate"
(for release)
WrapOut
which when expanding adds the envelope. https://github.com/Sciss/ScalaCollider/blob/master/src/main/scala/de/sciss/synth/ugen/HelperElements.scala#L196
exception in GraphDef_Recv: UGen 'VBAP' not installed.
val a = VBAPSetup(2, Seq(Polar(-60,0), Polar(60,0), Polar(110,0), Polar(-110,0), 3.35))
val b = Buffer.alloc(serv, a.bufferData.size)
b.setn(a.bufferData)
SynthDef.write("synthTest.txt", MySynths.load(b), 1)
synthDefs = SynthDef.read("synthTest.txt")
val ssa = synthDefs.find(_.name == "soudscape-1").get
ssa.recv(serv)
MySynths.load(Buffer)
loads a Seq[SynthDef]
to store
write
will always write in the standard binary format of SuperCollider. Therefore, this is not a "text file".val sd = SynthDef("test") { ... }
sd.write(dir = "my-dir")
SynthDef.load(path = "my-dir/test.scsyndef")
// ... synchronise! then
Synth.play("test")
It is stored - the synth definition - but not the contents of buffer
. If you want to store this, you should also never hard-code the buffer id. This is just an integer number associated with a live buffer on the server. If you load a synth def with a hard-coded buffer id (here it will be just the constant that you happened to have received by the allocator at the time you created the synth-def), it will be pure luck that you get to have the same buffer again. And it will not be allocated unless you do so.
So in SuperCollider, for buffers it's always advisable - except for quick live coding - to use a control instead:
SynthDef("test") {
val bufId = "buf".kr
VBAP.ar(4, sig, bufId, ...)
...
}
val b = Buffer.alloc(...)
b.setn(...)
val x = Synth.play("test", args = Seq("buf" -> b.id))
That way the buffer-identifier becomes a parameter of the synth def.
Is there a particular reason you want to store the synth-def, anyway? I have stopped doing this at all, it's quick enough to generate it ad hoc.