Superfm

The array functions are much nicer than setting everything individually.

Does this work?

-- Array functions
let fmparam function (x:xs) = foldr (#) (function 1 x) (zipWith function [2..] xs)
    lfma = fmparam fmamp
    lfmr = fmparam fmratio
    lfmd = fmparam fmdetune
    lfmer op = fmparam (fmegrate op)
    lfmel op = fmparam (fmeglevel op)

My haskell is rusty and was never very good to start with, so there's probably a more elegant way to do it.

3 Likes

It works very well and it's a lot more compact, thank you @paul1 !

d1
    $ stut 2 0.7 0.125
    $ slow 3 $ s "superfm" 
    |+| note (arp "pinkyup" "[0,4,7,12] [0,5,7,9]" )
    # fmfeedback 1
    # lfma [1, 1, 1, 0, 0, 0]
    # lfmr [1, (range 0 4 (slow 4 sine)), 0.26, 0.5, 0.5, 0.5]
    # lfmd [0, 1, 0, 0, 0, 0]
    # fmmod 1 1 "<0 1.25>"
    # fmmod 1 2 (range 0 4 (slow 4 sine))
    # fmmod 1 3 (range 0 4 (slow 3 saw))
    # fmmod 3 2 (range 0 3 (slow 2 sine))
    # lfmel 1 [1, 0.5, 0, 0, 0, 0]
    # lfmer 1 [10, 0.1, 0.1, 1, 0, 0]
    # lfmel 2 [1, 0, 0, 0, 0, 0]
    # lfmer 2 [1, 0.3, 0.7, 1, 0, 0]
    # lfmel 3 [1, 0.2, 0, 1, 0, 0]
    # lfmer 3 [10, 0.5, 0.4, 1, 0, 0]
    # lpf 1000
    # room 0.3
3 Likes

I just posted an anouncement for a streaming I'll be broadcasting next Thrusday to give an overview of the synth. Hoping to see you there.

3 Likes

Interesting syntax.

What does the l mean in lfma?

Isn't env more clear then eg? In SuperCollider I don't think I ever saw 'eg', but 'env' seems to be common to use in relation with envelopes. For now this seems more clear to me as a 'fm-newbie':

fmamp
fmratio
fmmod
fmenvlevel
fmenvrate

Shorter is not always better :wink:

Maybe even:

fmamps
fmratios
fmdetunes
fmmod
envlevel
envrate

In SuperCollider you would write a envelope like

([0, 1, 1, 0], [2, 1, 3])

Where the first numbers are the levels and the last two numbers are the time or rate (att, sus, rel) in this case. A syntax like that isn't possible in Tidal/Haskell?

Although this syntax is shorter, I find myself having to think what each parameter is every time I have to change something. So, for me, is more convenient to have them separated at least between levels and rates. Just a matter of taste, I guess.

I like this one. Maybe it should be envlevels, envrates, fmmods and fmratios to denote the array, shouldn't it?
And how about op instead of fm as prefix?
As for the envelopes, envr and envl might be a nice short version.

Thinking about the single-value syntax I figured a more descriptive alternative would be to make a mix of ADSR and 4-stage-envelope naming. Maybe something like:

atkl1 -- attack level for operator 1, instead of eglelvel11
atkr1 -- attack rate for operator 1, instead of egrate11
decl1 -- decay level for operator 1, instead of eglelvel12
decr1 -- decay rate for operator 1, instead of egrate12
susl1 -- sustain level for operator 1, instead of eglelvel13
susr1 -- sustain rate for operator 1, instead of egrate13
rell1 -- release level for operator 1, instead of eglelvel14
relr1 -- release rate for operator 1, instead of egrate14

or maybe

latk1
ratk1
ldec1
rdec1
...
-- or
ar1
al1
dl1
dr1
sl1
sr1
rl1
rr1
1 Like

Good thoughts.

You have x amount of levels for the envelope, including the initial and end level. Then there are the timevalues between them, which is always levels -1 (so 4 -1 = 3 in this case).

For me it feels a bit strange to tear the envelope apart in individual arguments, which you can sort the way you want

atkl1
decl3
rell1
relr6

for instance. In the Supercollider syntax, it's relation and position in the enveloppe is more clear to me at least.

Also using larger numbers for faster rates, feels a bit contractional to me. In most languages/software a smaller number would give you a faster attack. You told that you copied it from the original DX7, but I wonder if converting it to software should stick with that. That's just what my initial thoughts are, I'm not really qualified to judge this properly, just some thoughts/feedback.

Is there any relation of superfm with this DX7 implementation I found for supercollider?

I guess it's now time to start experimenting with the superfm synth. Thanks for the synth implementation and the livestream. I'm having quite some fun with it already. :sunglasses:

1 Like

I understand what you mean. For me, when live coding, I usually just tweak one or two envelope parameters, not the hole envelope. If I just want to change the decay time, for example, it makes more sense to me to just write the value for that parameter than the whole envelope thing because I have to consider the other parameters too, and remember their values.

I must confess that is still confusing for me too, because I'm used to Supercolllider's syntax with times instead of rates. The advantage of keeping the DX7 scheme is that is easier to port the loads of documented patches out there, which are all set in rates.

No, there isn't. That implementation is a serious one, really cool and sounds awesome, but it's quite complex. Superfm is just using Supercollider's FM7 Ugen, with added envelopes and mappings to the control and mod matrices. I haven't implemented fixed operator frequencies, mod sensitivity or keyboard breakpoints, band depths and curves (yet ; P).

But having you all enjoying it as you do is motivating to do it!

3 Likes

Personally I'm more fan of a bit longer but clearer names likes these, compared to the very short ones (ar1 etc.)

But it the end it's your synth, you decide :wink: @loopier

1 Like

It's short for "list" :clown_face:
I thought it would be intuitive at first, but yeah, fmamps is obviously way clearer!

1 Like

Where does op stands for?

1 Like

It stands for "operator"

2 Likes

Ok, if that's technically a more appropriate naming, it might be better indeed. It's up to you I guess.

1 Like

that's the thing though - none of this is standard library, so you can call it whatever you find more fitting :slight_smile:

2 Likes

Sure, but it might be nice too have some nice default settings :slight_smile:

1 Like

Yep it'd be nice for it to be in the standard library I think!

6 Likes

It'd be good to get an improved superfm UI in the next tidal release, did we arrive at consensus?

1 Like

For what my opinion is worth, I think @ghales's version is very sane. I'm currently using @paul1's version, but I find it to be very verbose, especially when you're only using a couple oscillators at a time but you still have to define the others at 0.

3 Likes

If there must be a prefix, I'd go for op instead of fm.

I'm not so sure about arrayed parameters, either (as @th4 suggests if I understood well). If using patterns in them, wouldn't make it a bit confusing?

d1 $ s "superfm*3"
# mod1 2 "0 1 0.5"
# mod1 3 "1 0 0.5"
# mod1 4 "0 1"
# mod1 5 "0 1 0"

might be easier to read and quicker to grab than

d1 $ s "superfm*3"
# mod1 "0 1 0.5"  "1 0 0.5" "0 1" "0 1 0" 0 0

For the envelope syntax, I myself have been playing with

atkl1 -- attack level for operator 1, instead of eglelvel11
atkr1 -- attack rate for operator 1, instead of egrate11
decl1 -- decay level for operator 1, instead of eglelvel12
decr1 -- decay rate for operator 1, instead of egrate12
susl1 -- sustain level for operator 1, instead of eglelvel13
susr1 -- sustain rate for operator 1, instead of egrate13
rell1 -- release level for operator 1, instead of eglelvel14
relr1 -- release rate for operator 1, instead of egrate14

and seems to work pretty well. Easy to remember and shorter to write.

I also have been trying a different version of superfm with envelope times instead of rates, so it's more consistent with ADSR values. It's not DX7ish anymore, but I must say it makes more sense. Then the syntax would be

-- ...
# atkt1 0.01 -- instead of  # egrate11 100
# dect1 0.3 -- instead of  # egrate12 30
# sust1 1 -- instead of  # egrate13 1
# relt1 1 -- instead of  # egrate14 1

Should I make a pull request to update it in time for next release? Or is it better to stick with what we already have not to confuse people?

I'm obviously biased here but I think the array version looks cleaner. That said, why not have both?
fmamps takes an array, while fmamp takes an extra index argument in the first position.

fmamp can be more readable while fmamps is more practical for large patches. They're good for different things - having both could be nice

4 Likes

I haven't got deep into try these out but I like the idea of adding a plural 's' for a list parameter