crucialfelix / supercolliderjs

The JavaScript client library for SuperCollider
https://crucialfelix.github.io/supercolliderjs/
MIT License
475 stars 41 forks source link

update synth instance instead of spawning a new one? #71

Closed hems closed 4 years ago

hems commented 4 years ago

First of all, absolutely great work! Really impressive ! 🍺

On the example i'm trying to create i would like to have my SineOSC always playing and instead of spawning a new instance i would like to update the "freq" parameter..

For instance:

  sc.server.boot().then( async (server) => {
    const sine = server.synthDef(
      "sine",
      `
        SynthDef("sine", { arg out=0, freq=400;
          var zout;

          zout = SinOsc.ar([freq, freq], 0, 0.2);

          // zout = zout * EnvGen.kr( 
          //   Env.adsr(0, 0.1, 0, 0),
          //   doneAction: 2
          // );

          Out.ar(out, zout);
        });
      `,
    );

    const instance = await server.synth(sine, {
      freq: 440 * Math.random(),
      releaseTime: 0.001
    });

    setInterval(() => {
      instance.set("\freq", Math.random() * 100)
    }, 500);

  } )

Unfortunately, I'm getting an error: "Error: Unmatched type: req".

I'm sure i'm missing something.. What would be the right way to go about it?

Thank You!

crucialfelix commented 4 years ago

Hi!

Just set "freq" not \freq. It's not an SC symbol ;-) You escaped the f or something.

I will post an example with streams too.

hems.io notifications@github.com schrieb am Sa., 30. Nov. 2019, 06:53:

First of all, absolutely great work! Really impressive ! 🍺

On the example i'm trying to create i would like to have my SineOSC always playing and instead of spawning a new instance i would like to update the "freq" parameter..

For instance:

sc.server.boot().then( async (server) => {

const sine = server.synthDef(

  "sine",

  `
    SynthDef("sine", { arg out=0, freq=400;
      var zout;

      zout = SinOsc.ar([freq, freq], 0, 0.2);

      // zout = zout * EnvGen.kr(
      //   Env.adsr(0, 0.1, 0, 0),
      //   doneAction: 2
      // );

      Out.ar(out, zout);
    });
  `,

);

const instance = await server.synth(sine, {

  freq: 440 * Math.random(),

  releaseTime: 0.001

});

setInterval(() => {

  instance.set("\freq", Math.random() * 100)

}, 500);

} )

Unfortunately, I'm getting an error: "Error: Unmatched type: req".

I'm sure i'm missing something.. What would be the right way to go about it?

Thank You!

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/crucialfelix/supercolliderjs/issues/71?email_source=notifications&email_token=AABVM4VW7PFPHLM7BPBW5C3QWH5WJA5CNFSM4JTFD4I2YY3PNVWWK3TUL52HS4DFUVEXG43VMWVGG33NNVSW45C7NFSM4H47LL5Q, or unsubscribe https://github.com/notifications/unsubscribe-auth/AABVM4Q6KRGUM2HXOEHFB5DQWH5WJANCNFSM4JTFD4IQ .

crucialfelix commented 4 years ago

To clarify a bit more (now that I'm not on mobile):

The Supercollider language has Symbols that are written as: \freq or 'freq'

In JavaScript you just want a simple string, and by writing \freq you got a 4 character string with an unprintable first character:

Screen Shot 2019-12-05 at 9 34 10 AM

Not quite ready, but this would be the dryadic version:

const { Synth } = require("supercolliderjs").dryads;

import { interval } from "baconjs";

const sy = Synth.fromSource(`|freq=400| SinOsc.ar([freq, freq], 0, 0.2)`);

const i = interval(500).map(() => {
  return { freq: Math.random() * 100 };
});

sy.setStream(i);

export default sy;

With all the Bacon stream functions: https://baconjs.github.io/api3/classes/eventstream.html

hems commented 4 years ago

thanks for the example @crucialfelix !

Unfortunately, I'm having a hard time with the "set" function, here is a standalone version i baked together to illustrate.

const sc = require("supercolliderjs");

startLang = async () => {
  sc.lang.boot().then( async (lang) => {

    const pyr8 = await lang.interpret(`

      Server.default.options.outDevice_("Midas FW");
      Server.default.options.numOutputBusChannels(32);

    `);

  } )
}

startServer = async() => {

  sc.server.boot().then( async (server) => {
    const sine = server.synthDef(
      "sine",
      `
        SynthDef("sine", { arg out=0, freq=400;
          var zout;

          zout = SinOsc.ar([freq, freq], 0, 0.2);

          Out.ar(out, zout);
        });
      `,
    );

    const instance = await server.synth(sine, {
      //freq: 440 * Math.random(),
      //releaseTime: 0.001
    });

    setInterval(() => {
      instance.set("freq", Math.random() * 100)
    }, 500);

  } )
}

work = async () => {
  await startLang()

  await startServer()
}

work()

this will yield the error: "Unmatched type: freq", as you can see on this logs:

/Users/h/git/hz/supercolliderjs-tests/node_modules/@supercollider/server/lib/osc/msg.js:24
    throw new Error(`Unmatched type: ${pairs}`);
    ^

Error: Unmatched type: freq
    at flattenPairs (/Users/h/git/hz/supercolliderjs-tests/node_modules/@supercollider/server/lib/osc/msg.js:24:11)
    at Object.nodeSet (/Users/h/git/hz/supercolliderjs-tests/node_modules/@supercollider/server/lib/osc/msg.js:250:34)
    at Synth.set (/Users/h/git/hz/supercolliderjs-tests/node_modules/@supercollider/server-plus/lib/ServerPlus.js:44:43)
    at Timeout._onTimeout (/Users/h/git/hz/supercolliderjs-tests/src/test.js:43:16)
    at listOnTimeout (internal/timers.js:531:17)
    at processTimers (internal/timers.js:475:7)
crucialfelix commented 4 years ago

Ah, I should have looked more carefully.

   * synth.set({freq: 441, amp: 0.9});

It should take a {freq: 440}

Note that you can send many param changes at once.

I will make that throw a TypeError with a proper message.

  throw new TypeError(
    `Received ${typeof pairs} ${JSON.stringify(pairs)}.` + "Expected {key: value} or [[key, value],...]",
  );

that would post:

TypeError: Received string "freq". Expected {key: value} or [[key, value], ...]
crucialfelix commented 4 years ago

I still need to fix the documentation generator. This is pretty useless: https://crucialfelix.github.io/supercolliderjs/packages/server-plus/docs/classes/_serverplus_.serverplus.html#synth

Because the Synth class that it returns is not exported, it's not in the docs.

And the formatting of that those TypeDocs is kind of ugly.

Most of the time I use VSCode so I can just option hover / option click on anything and I see docs. But it needs to be published too.

hems commented 4 years ago

Most of the time I use VSCode so I can just option hover / option click on anything and I see docs. But it needs to be published too.

great.. it takes an object, i should have tried that!

Also, I was using vim so i didn't have those code navigation features, but considering you used typ script it makes sense to use VSCode to benefit from all the hints!

thank you once again, great work.

hems commented 4 years ago

It worked!! 🍻

Another question I have - which perhaps should be in another issue - is there an easy way to sample the amplitude of that Sine so i could use to LFO some variables on my JS code?

Effectively I would then be modulating my JS code ( for instance a 3D object on WebGL ) with LFOs from Supercollider... So exciting!!

hems commented 4 years ago

You triggering so many questions in my mind with this library : D

I have like 100 of them now

crucialfelix commented 4 years ago

I would expect VIM has a TypeScript / Language Server package. But VSCode has been killing it lately. I use the VIM bindings (not perfect).

With classic supercollider and my older crucial library I used to do a lot of polling and triggering. Inside a Patch you could do lfo.onTrig(scfunction)

There is Poll http://danielnouri.org/docs/SuperColliderHelp/UGens/Triggers/Poll.html which can send a trig back to the client.

The dryadic components will be able to support some really interesting cross communication like that. It would just come back as a stream and then you can filter or map that and plug it into other things.

Another use of Dryadic I've dreamed up is that the event/stream system can be connected to from any remote client via a websocket. So you can have a separate browser based app with webgl etc. and it can easily communicate back and forth with the node app. The web app can use Dryad classes and it messages back to the node app to update while playing.

I have like 100 of them now

Please let me know! At least I can figure out how to make the docs and development open enough that people can contribute and build things.

hems commented 4 years ago

I would expect VIM has a TypeScript / Language Server package. But VSCode has been killing it lately. I use the VIM bindings (not perfect).

it does but indeed VSCode with a few tweaks is working pretty well..

With classic supercollider and my older crucial library I used to do a lot of polling and triggering. Inside a Patch you could do lfo.onTrig(scfunction)

There is Poll http://danielnouri.org/docs/SuperColliderHelp/UGens/Triggers/Poll.html which can send a trig back to the client.

yes that is super sweet! so basically we would set the "LFO" to be the "in" and a Pulse wave to be the "sampling rate" and wait for the messages on the client side with something similar to the OSCresponderNode...

The dryadic components will be able to support some really interesting cross communication like that. It would just come back as a stream and then you can filter or map that and plug it into other things.

that sound super amazing, i can't wait!

Another use of Dryadic I've dreamed up is that the event/stream system can be connected to from any remote client via a websocket. So you can have a separate browser based app with webgl etc. and it can easily communicate back and forth with the node app. The web app can use Dryad classes and it messages back to the node app to update while playing.

Yes, that's super sweet. Actually the code i'm writing right now have some WebGL elements moving on the screen and i wanted to make their rotation to be a function of SuperCollider's LFO's amplitude....

I have like 100 of them now

really looking forward to check this out..

Please let me know! At least I can figure out how to make the docs and development open enough that people can contribute and build things.

Great, ping me anytime you would like me to have a look or try something out. I don't have such a deep understanding of the supercollider server as you do, but hopefully i can be helpful by reviewing/trying out examples, giving feedback and hopefully writing some code.

once again thanks a lot for such awesome work!

☮️