Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
    Hanns Holger Rutz
    @Sciss
    If you lose the variable holding a synth, you can stop the sound with ctrl-. (period) or in the menu Actions > Stop Synths
    If you run into any other tacit assumptions that are unclear, I shall try to collect them, and make an update to the intro video (there is one, but it's seven years old ... https://vimeo.com/26572695 ; aptly called 'demo' and not 'tutorial' anyways)
    Shubham Kamthania
    @ikamthania
    I started server again. Tried very first example. Here is the error log
    Cannot lock down 82280346 byte memory area (Cannot allocate memory)
    JackDriver: client name is 'SuperCollider'
    SC_AudioDriver: sample rate = 44100.000000, driver's block size = 1024
    Cannot use real-time scheduling (RR/5)(1: Operation not permitted)
    JackClient::AcquireSelfRealTime error
    SuperCollider 3 server ready.
    x0: de.sciss.synth.Synth = Synth(<localhost>,1000) : <temp_55>
    df1: de.sciss.synth.SynthDef = SynthDef(AnalogBubbles)
    x1: de.sciss.synth.Synth = Synth(<localhost>,1001) : <AnalogBubbles>
    FAILURE IN SERVER /n_set Node 1001 not found
    FAILURE IN SERVER /n_set Node 1001 not found
    FAILURE IN SERVER /n_set Node 1001 not found
    Hanns Holger Rutz
    @Sciss

    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?

    Shubham Kamthania
    @ikamthania

    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.

    Hanns Holger Rutz
    @Sciss
    Try the menu Actions > Show Server Meter. If you see the level meters moving, the sound is rendered by SuperCollider. If you do not hear it, then its outputs are not routed in Jack. I think that if you run Jack directly from SuperCollider (I know, I have advised against it, but...), then it automatically connects its output. If you launch Jack via QJackCtl, then SuperCollider will not route its outputs automatically to the system output. You have to do that manually in 'Connect' in QJackCtl, or better, create a default setting in 'Patchbay' (make sure you "activate" it after editing).
    (In other words, Jack acts as a patchbay for audio signals, you need to tell it how you want the output of SuperCollider to go onto your sound card, for example.)
    Here is my routing for two channels output only (you can change the number of channels in the preferences):
    Screenshot from 2019-03-04 17-06-28.png
    Shubham Kamthania
    @ikamthania
    Awesome. Now I can hear it. Thanks @Sciss I will try other examples.
    Hanns Holger Rutz
    @Sciss
    Ok cool, I'm glad it's working now. Sorry about the bumpy road, it really needs an intro for people who haven't worked with SuperCollider before.
    Shubham Kamthania
    @ikamthania
    :thumbsup:
    bmott
    @Bmottomus
    Hey - is there a 'correct' way to set overtones and their respective weights with ScalaCollider? I've done it in the below way, but it feels like a hacky way to achieve my goal
      var summation: GE = SinOsc.ar(220.0)
      (2 to 16 ).zipWithIndex.foreach{
        case (ot, idx) => {
          summation = summation + (SinOsc.ar(220.0 * ot) * weights(idx))
        }
      }
    Hanns Holger Rutz
    @Sciss

    @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
    }
    Are you familiar with multi-channel-expansion? (where you pass a sequence of values into a paramter, and that then creates a sequence of UGens)
    Mix.tabulate(n)(...) is kind of the same as Mix(Seq.tabulate(n)(...))
    bmott
    @Bmottomus
    No, wasn't aware I could do that - this is awesome / exactly what I was looking for!! Appreciate the detailed examples - this makes sense, thanks!
    Hanns Holger Rutz
    @Sciss
    :thumbsup:
    bmott
    @Bmottomus
    Hey - is it possible to create an envelope based off an 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),
        )
    Hanns Holger Rutz
    @Sciss

    @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)

    Hanns Holger Rutz
    @Sciss
    I have released new versions of ScalaCollider (1.28.5) and ScalaCollider-Swing (1.41.6) that contain the fix for Env.step among other things. So the first example should work now.
    bmott
    @Bmottomus
    Make sense - didn't realize that the envelope required static inputs. Appreciate the example code again!
    Hanns Holger Rutz
    @Sciss
    You could use DemandEnvGen for a more complex case where you don't know the number of segments in advance.
    Hanns Holger Rutz
    @Sciss
    Here's a little fun synth by Luka Princic translated:
    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
    }
    bmott
    @Bmottomus
    Hey - any chance you have examples for how to use buffer.sine1-3? For sine1 I can't figure out from the method description if the partials parameter expects overtones-as-multiples (i.e. 1-N). For sine2 I'm wondering if the partials tuples are effectively overtones.zip(weights)
    Hanns Holger Rutz
    @Sciss

    @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.

    So the argument is, as the doc says, the relative amplitudes of the partials (here fundamental 1.0, up to third overtone of 1.0/4)
    Hanns Holger Rutz
    @Sciss
    For 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
    So yes, it's pairs of freq multipliers with weights, or "overtones.zip(weights)".
    And for sine3 you have triplets with third component being the phase for each given partial.
    Hanns Holger Rutz
    @Sciss
    Here's a hackish spectral plot (no axis labels), that works if you use ScalaCollider-Swing:
    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())
      }
    }
    Showing the first four partials (linear frequency scale) with decreasing amplitudes (decibels)
    Hanns Holger Rutz
    @Sciss
    Screenshot from 2020-06-06 13-02-28.png
    Hanns Holger Rutz
    @Sciss
    ScalaCollider-Swing v1.41.7 has been published which includes a new oscilloscope. FreqScope is something that will probably be added in one of the next versions.