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.
So the nodes on the sound server have ids, the count beginning at 1000. The first synth x0 you created, is 1000; the second is x1 with 1001; the error log shows that "n_set" does not find node x1 or 1001. This has again todo with the execution order; I assume you executed this en-bloc:
val df1 = SynthDef("AnalogBubbles") {
val f1 = "freq1".kr(0.4)
val f2 = "freq2".kr(8.0)
val d = "detune".kr(0.90375)
val f = LFSaw.ar(f1).mulAdd(24, LFSaw.ar(Seq(f2, f2 * d)).mulAdd(3, 80)).midiCps // glissando function
val x = CombN.ar(SinOsc.ar(f) * 0.04, 0.2, 0.2, 4) // echoing sine wave
Out.ar(0, x)
}
val x1 = df1.play()
x1.set("freq1" -> 0.1)
x1.set("freq2" -> 222.2)
x1.set("detune" -> 0.44)
However, there are some procedures in SuperCollider which are asynchronous, so they do not complete instantaneously. One of them is SynthDef#play
. The server tries to run x1.set
before the synth x1
is actually initalised. Sorry, this is not obvious from the document, only if you were familiar with SuperCollider. So you need to execute that in two steps:
val df1 = SynthDef("AnalogBubbles") {
val f1 = "freq1".kr(0.4)
val f2 = "freq2".kr(8.0)
val d = "detune".kr(0.90375)
val f = LFSaw.ar(f1).mulAdd(24, LFSaw.ar(Seq(f2, f2 * d)).mulAdd(3, 80)).midiCps // glissando function
val x = CombN.ar(SinOsc.ar(f) * 0.04, 0.2, 0.2, 4) // echoing sine wave
Out.ar(0, x)
}
val x1 = df1.play()
And then, separately:
x1.set("freq1" -> 0.1)
x1.set("freq2" -> 222.2)
x1.set("detune" -> 0.44)
Are you hearing the sounds?
Tried that. Not hearing anything. Also tried
val x0 = play {
val f = LFSaw.kr(0.4).mulAdd(24, LFSaw.kr(Seq(8, 7.23)).mulAdd(3, 80)).midiCps // glissando function
CombN.ar(SinOsc.ar(f)*0.04, 0.2, 0.2, 4) // echoing sine wave
}
Not working. However super collider client works like a charm without QJackCtl.
var summation: GE = SinOsc.ar(220.0)
(2 to 16 ).zipWithIndex.foreach{
case (ot, idx) => {
summation = summation + (SinOsc.ar(220.0 * ot) * weights(idx))
}
}
@Bmottomus Yes sure; so let's say your complete example is
play {
val f0 = 220.0
var summation: GE = SinOsc.ar(f0)
val weights = Seq.tabulate(15)(i => 1.0 / (i + 1))
(2 to 16 ).zipWithIndex.foreach{
case (ot, idx) => {
summation = summation + (SinOsc.ar(f0 * ot) * weights(idx))
}
}
summation * 0.2
}
That is, weights are 1.0, 0.5, 0.333, .... You could write that more concisely as
play {
val f0 = 220.0 // fudamental
val n = 16 // number of partials
val sum = Mix.tabulate(n) { i =>
val j = i + 1
SinOsc.ar(f0 * j) * 1.0 / j
}
sum * 0.2
}
Or use multi-channel expansion followed by a Mix
:
play {
val f0 = 220.0 // fudamental
val n = 16 // number of partials
val f = (1 to n).map(_ * f0)
val w = (1 to n).map(_.reciprocal)
val sum = Mix(SinOsc.ar(f) * w)
sum * 0.2
}
Mix.tabulate(n)(...)
is kind of the same as Mix(Seq.tabulate(n)(...))
AudioControlProxy
? Something like Env.Step(...)
but instead of a Seq[GE] a Proxy which expands to a Seq[Float] like below? SynthDef.recv("envl") {
Out.ar(0,
PanAz.ar(2,
SinOsc.ar(220) *
EnvGen.ar(Env.step("levels".ar(Seq(1f,0f,2f,0f)), "durs".ar(Seq(1f,1f,1f,1f)))),
orient = 0.5 )
)
}
Synth.play("envl", Seq(
"levels" -> Seq(1f, 0f, 1f, 0f),
"durs" -> Seq(1f, 1f, 1f, 1f),
)
@Bmottomus yes, there is a bit of mismatch between unexpanded multi-channel UGen and explicit requirement for Seq[GE]
, which comes from the fact that the envelope generator must have a static number of elements. This would be the workaround if there wasn't another bug:
SynthDef.recv("envl") {
val lvl = "levels".ar(Seq(1f,0f,2f,0f))
val dur = "durs" .ar(Seq(1f,1f,1f,1f))
val lvlSq = Seq.tabulate(4)(lvl.out)
val durSq = Seq.tabulate(4)(dur.out)
val eg = EnvGen.ar(Env.step(lvlSq, durSq))
Out.ar(0, Pan2.ar(SinOsc.ar(441) * eg))
}
So you still need to statically know that the number of controls is four.
Unfortunately, it seems that Env.step
is buggy, it swaps levels and durations. So you have to create the envelope by hand:
SynthDef.recv("envl") {
val lvl = "levels".ar(Seq(1f,0f,2f,0f))
val dur = "durs" .ar(Seq(1f,1f,1f,1f))
val lvlSq = Seq.tabulate(4)(lvl.out)
val durSq = Seq.tabulate(4)(dur.out)
val env = new Env(lvlSq.head, (durSq zip lvlSq).map { case (d, l) =>
new Env.Segment(d, l, Curve.step)
})
val eg = EnvGen.ar(env)
Out.ar(0, Pan2.ar(SinOsc.ar(441) * eg))
}
Synth.play("envl", Seq(
"levels" -> Seq(1f, 0f, 1f, 0f),
"durs" -> Seq(1f, 1f, 1f, 1f),
))
(The bug is noted)
play {
val h = HenonN.ar(5000,
LFNoise2.kr(1).mulAdd(0.20, 1.20),
LFNoise2.kr(1).mulAdd(0.15, 0.15),
)
val lo = 40
val hi = LFNoise2.kr(0.1).linLin(-1, 1, 1000, 10000)
val f = (h * 0.6).linLin(-1, 1, lo, hi)
val p = (Pulse.ar(f) * 0.2).ring3(0.5).clip2(0.8)
val dry = LeakDC.ar(p)
val vrb = Greyhole.ar(dry, dry, feedback = 0.2, diff = 1, delayTime = 0.6) // needs sc3plugins installed
dry + vrb * 0.7
}
@Bmottomus yes, they are integer multiples, because it's filling a wavetable, so naturally there are only integer overtones.
val b = Buffer.alloc(s, 512, 1)
b.sine1((1 to 4).map(_.reciprocal), normalize = true, wavetable = true, clear = true)
val x = play { Osc.ar(b.id, 200) * 0.2 }
Sadly, we currently don't have freq scope, but you could record and look at a spectrogram.
sine2
you indicate which partials to use; again wavetable, so only integer numbers make sense (indeed I'm not sure why it accepts float at all).b.sine2(Seq((1, 1.0f), (3, 0.5f)), true, true, true) // fundamental at 1.0 amplitude, second overtone (third partial) at 0.5 or -6 dB
sine3
you have triplets with third component being the phase for each given partial.
val bRec = Buffer.alloc(s, 16384, 1)
val b = Buffer.alloc(s, 512, 1)
b.sine1((1 to 4).map(_.reciprocal), normalize = true, wavetable = true, clear = true)
(play {
val sig = Osc.ar(b.id, 200)
RecordBuf.ar(sig, bRec.id, loop = 0, doneAction = freeSelf)
Pan2.ar(sig * 0.2)
}).onEnd {
bRec.getData().foreach { sig =>
val f = de.sciss.dsp.Fourier(bRec.numFrames)
val sigA = (sig :+ 0f :+ 0f).toArray
f.realForward(sigA)
val spect = sigA.iterator.grouped(2).map { case Seq(re, im) => (re.squared + im.squared).sqrt.ampDb } .toVector
scala.swing.Swing.onEDT(spect.take(1024).plot())
}
}
thanks! I forgot to announce it here, so here's the demo link: https://www.sciss.de/temp/scalacollider.js/
Working now on the possibility to export Mellite workspaces so they can be directly read in the browser (this still needs a few things, but I expect that to work in the next few weeks)