New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FM7 synth #12
Comments
Hi, I'm looking at it, is it a quark or sc3-plugin? There is a way to add ugens from an external script. |
It's in sc3-plugins |
The basic structure to make ugens available in an external module to the library is as follows: from sc3.synth.ugen import MultiOutUGen
from sc3.synth.ugens import install_ugens_module
__all__ = ['FM7']
class FM7(MultiOutUGen):
...
install_ugens_module(__name__) That will, at import time, add the classes defined in from sc3.all import *
from mymodule import FM7 The library is first imported from sc3.all so it call the initialization logic first. This ways shouldn't be problems with cyclic imports or alike and the lib needs to be initialized first because it has a few global states. I was thinking in a way to make extra packages to be installed as namespace packages but haven't solve it yet, I'm not sure if it's possible to install an extra package to the library from the extra package itself.
The way to avoid multi channel expansion if by using a import sc3.base.utils as utl
from sc3.synth.ugen import MultiOutUGen, ChannelList
from sc3.synth.ugens import install_ugens_module
# utl contains sclang list operations used by the library.
# 'utl' is the module alias used throught the library, I always use the same for each module, but you don't need to.
__all__ = ['FM7']
class FM7(MultiOutUGen):
_control_matrix = ...
_modulation_matrix = ...
@classmethod
def ar(cls, ctlmatrix=None, modmatrix=None):
ctlmatrix = utl.flatten(ctlmatrix or cls._control_matrix, 1)
modmatrix = utl.flatten(modmatrix or cls._modulation_matrix, 1)
return cls._multi_new('audio', *ctlmatrix, *modmatrix)
@classmethod
def ar_algo(cls, algo=0, ctlmatrix=None, feedback=0.0):
modmatrix, channels = cls._algo_spec(algo, feedback)
return cls._slice(cls.ar(ctlmatrix, modmatrix), channels)
@staticmethod
def _slice(clst, channels):
# slice is missing, it requires some specific logic.
# clst is a ChannelList which is a python list.
# clst = list(clst) # returns a common python list, but it needs to be recursive...
# # ChannelList shouldn't return nested ChannelList objects,
# # but I don't know why is done like that.
# ...
# return ChannelList(clist) # return the resulting python list as ChannelList.
...
@classmethod
def _algo_spec(cls, algo, feedback):
... # return a tuple with the matrix as list of lists and channels as list.
install_ugens_module(__name__) Please tell me if this doesn't make it more clear. I didn't test the last code. |
I have some typos in the last part, sorry about that. |
Tough class, code above is missing these methods: def _init_ugen(self, *inputs): # override
self._inputs = inputs
return self._init_outputs(type(self)._num_required_inputs, self.rate)
def _check_inputs(self): # override
if len(self._inputs) != type(self)._num_required_inputs:
return 'some error message' That's the way I would do it following the style in the library, it can be done in different ways. |
Short answer: |
Thanks for such verbose answer!
Or is it related to that I tried to use Splay.ar on it? |
As for
Do I need to copy the matrices from FM7.sc SuperCollider class code? |
Yes, matrices are the same but they are within lambdas, requires some boilerplate. The error is because the missing parts (multi out ugens return output proxies within channel list, that looks weird but is normal). I didn't define everything because don't have the time now, is not much what is missing but I need some time to check the rest. |
I think this should work, I didn't transcribed the algorithm data except for the first one. from copy import deepcopy
import sc3.base.utils as utl
from sc3.synth.ugen import MultiOutUGen, ChannelList
from sc3.synth.ugens import install_ugens_module
__all__ = ['FM7']
NUM_OP = 6
NUM_CTL = 3
ALGOS = [
[
[ # x, y, value
[0, 1, 1],
[2, 3, 1],
[3, 4, 1],
[4, 5, 1],
[5, 5, None]
],
[4, 2], # feedback parameter position (filled with None above)
[0, 2] # output channel numbers
],
# ... TODO: All other algorithm specs.
]
class FM7(MultiOutUGen):
_required_inputs = NUM_OP * NUM_CTL + NUM_OP * NUM_OP
_default_ctlmatrix = [[0 for _ in range(NUM_CTL)] for _ in range(NUM_OP)]
_default_modmatrix = [[0 for _ in range(NUM_OP)] for _ in range(NUM_OP)]
@classmethod
def ar(cls, ctlmatrix=None, modmatrix=None):
ctlmatrix = utl.flatten(ctlmatrix or cls._default_ctlmatrix, 1)
modmatrix = utl.flatten(modmatrix or cls._default_modmatrix, 1)
return cls._multi_new('audio', *ctlmatrix, *modmatrix)
@classmethod
def ar_algo(cls, algo=0, ctlmatrix=None, feedback=0.0):
modmatrix, channels = cls._algo_spec(algo, feedback)
output = cls.ar(ctlmatrix, modmatrix)
return ChannelList([output[c] for c in channels])
@classmethod
def _modulation_matrix(cls, *args):
m = cls._default_modmatrix
for x, y, value in args:
m[x][y] = value
return m
@classmethod
def _algo_spec(cls, num, feedback):
spec, (i, j), channels = ALGOS[num]
spec = deepcopy(spec)
spec[i][j] = feedback
return cls._modulation_matrix(*spec), channels
def _init_ugen(self, *inputs): # override
self._inputs = inputs
return self._init_outputs(type(self)._required_inputs, self.rate)
def _check_inputs(self): # override
if len(self._inputs) != type(self)._required_inputs:
return (
f'{type(self)._required_inputs} inputs required, '
f'{len(self._inputs)} received')
install_ugens_module(__name__) |
Thanks! I tried to use your code like this:
and get an error like this:
|
That is because the synthdef function argument is shadowing the global variables of the same name. PD: These questions are ok and welcome, it would be better next time if instead of creating an issue you create a discussion on the other github tab. |
Thanks! I've renamed the synthdef function and it didn't help, got the same error |
No problem. I was referring to the variable names: myvar = 123
def func(myvar=321):
print(myvar)
func() That will prints 321 because is the value of the argument, |
Oh, interesting, thanks! It works now : ) |
It should be exactly the same same as with sclang code. If the fields of the matrix are controls you can externally map values to the synth. |
Hi! Is it possible to add FM7 synth? I am especially interested in its arAlgo function. Thanks.
Also, I tried writing something like:
but it will return "ChannelList([ChannelList([ChannelList([..." which I don't know how to convert into sound.
The text was updated successfully, but these errors were encountered: