Absolutely right, both serve their purpose. Having the two of them would be great. I also like the plural s
for list parameters.
I've learnt the old hard way. Nothing is going to change my mind about it being the best way.
Hi, I'm using @ghales definitions (with some minor renaming) with superfm and it's great, but when I try to add them to BootTidal.hs they don't work. This is my BootTidal.hs file:
:set -XOverloadedStrings
:set prompt ""
import Sound.Tidal.Context
import System.IO (hSetEncoding, stdout, utf8)
hSetEncoding stdout utf8
-- total latency = oLatency + cFrameTimespan
tidal <- startTidal (superdirtTarget {oLatency = 0.1, oAddress = "127.0.0.1", oPort = 57120}) (defaultConfig {cFrameTimespan = 1/20})
:{
let p = streamReplace tidal
hush = streamHush tidal
list = streamList tidal
mute = streamMute tidal
unmute = streamUnmute tidal
solo = streamSolo tidal
unsolo = streamUnsolo tidal
once = streamOnce tidal
first = streamFirst tidal
asap = once
nudgeAll = streamNudgeAll tidal
all = streamAll tidal
resetCycles = streamResetCycles tidal
setcps = asap . cps
xfade i = transition tidal True (Sound.Tidal.Transition.xfadeIn 4) i
xfadeIn i t = transition tidal True (Sound.Tidal.Transition.xfadeIn t) i
histpan i t = transition tidal True (Sound.Tidal.Transition.histpan t) i
wait i t = transition tidal True (Sound.Tidal.Transition.wait t) i
waitT i f t = transition tidal True (Sound.Tidal.Transition.waitT f t) i
jump i = transition tidal True (Sound.Tidal.Transition.jump) i
jumpIn i t = transition tidal True (Sound.Tidal.Transition.jumpIn t) i
jumpIn' i t = transition tidal True (Sound.Tidal.Transition.jumpIn' t) i
jumpMod i t = transition tidal True (Sound.Tidal.Transition.jumpMod t) i
mortal i lifespan release = transition tidal True (Sound.Tidal.Transition.mortal lifespan release) i
interpolate i = transition tidal True (Sound.Tidal.Transition.interpolate) i
interpolateIn i t = transition tidal True (Sound.Tidal.Transition.interpolateIn t) i
clutch i = transition tidal True (Sound.Tidal.Transition.clutch) i
clutchIn i t = transition tidal True (Sound.Tidal.Transition.clutchIn t) i
anticipate i = transition tidal True (Sound.Tidal.Transition.anticipate) i
anticipateIn i t = transition tidal True (Sound.Tidal.Transition.anticipateIn t) i
forId i t = transition tidal False (Sound.Tidal.Transition.mortalOverlay t) i
d1 = p 1 . (|< orbit 0)
d2 = p 2 . (|< orbit 1)
d3 = p 3 . (|< orbit 2)
d4 = p 4 . (|< orbit 3)
d5 = p 5 . (|< orbit 4)
d6 = p 6 . (|< orbit 5)
d7 = p 7 . (|< orbit 6)
d8 = p 8 . (|< orbit 7)
d9 = p 9 . (|< orbit 8)
d10 = p 10 . (|< orbit 9)
d11 = p 11 . (|< orbit 10)
d12 = p 12 . (|< orbit 11)
d13 = p 13
d14 = p 14
d15 = p 15
d16 = p 16
:}
:{
let fmamp op = pF ("amp" ++ show op)
fmratio op = pF ("ratio" ++ show op)
fmdt op = pF ("detune" ++ show op)
fmmod opa opb = pF ("mod" ++ show opa ++ show opb)
fmegrate op step = pF ("egrate" ++ show op ++ show step)
fmeglevel op step = pF ("eglevel" ++ show op ++ show step)
fmfb = pF "feedback"
lfof = pF "lfofreq"
lfod = pF "lfodepth" --amplitude 0 - 1
fmparam function = foldr (#) (gain 1) . zipWith function [1..]
fma = fmparam fmamp
fmr = fmparam fmratio
fmd = fmparam fmdt
fmer op = fmparam (fmegrate op) -- higher nr = faster
fmel op = fmparam (fmeglevel op)
fmm opa = fmparam (fmmod opa)
:}
:{
let setI = streamSetI tidal
setF = streamSetF tidal
setS = streamSetS tidal
setR = streamSetR tidal
setB = streamSetB tidal
:}
:set prompt "tidal> "
:set prompt-cont ""
Any idea of what could it be? After booting Tidal when I use any of them I get "Variable not in scope:(...)". But when I run them on my tidal file in atom they work fine
That looks fine, are you editing the right copy of BootTidal.hs?
I think so, I'm going to Atom Preferences/Packages /tidalcycles /View Code and then editing BootTidal.hs inside tidalcycles/lib
When you start tidal, does it give the same path?
Ohhh you are right, my mistake. The path it shows when booting is different:
Load BootTidal.hs from /Users/elefou/.cabal/share/x86_64-osx-ghc-8.8.4/tidal-1.7.1/BootTidal.hs
Just added the definitions in that boot file and now it's working perfectly. Thanks a lot
Hey folks, in case anyone is interested, i spent some time over the weekend working out how to make the Control Buses in Tidal 1.7 work with the superfm
parameters - this allows you to sweep / modify parameters continuously, rather than having the changes quantised to note onsets - it's fantastic for super subtle variations, slow/drone/noisy music, and doubly so if you have a MIDI controller handy to map to some of the parameters. You just need to add the following to your BootTidal.hs
then use eg mod11bus
instead of mod11
in your code. The makeBus
and makeRecv
functions defined here will also work if you want to create control bus versions of parameters of other synths too, where they don't already exist:
Thanks!
Tim
-- SuperFM control buses
makeBus name busId pat = pF name pat # pI ("^" ++ name) busId
makeRecv name busId = pI ("^ ++ name") busId
mod11bus = makeBus "mod11"
mod11recv = makeRecv "mod11"
mod12bus = makeBus "mod12"
mod12recv = makeRecv "mod12"
mod13bus = makeBus "mod13"
mod13recv = makeRecv "mod13"
mod14bus = makeBus "mod14"
mod14recv = makeRecv "mod14"
mod15bus = makeBus "mod15"
mod15recv = makeRecv "mod15"
mod16bus = makeBus "mod16"
mod16recv = makeRecv "mod16"
mod21bus = makeBus "mod21"
mod21recv = makeRecv "mod21"
mod22bus = makeBus "mod22"
mod22recv = makeRecv "mod22"
mod23bus = makeBus "mod23"
mod23recv = makeRecv "mod23"
mod24bus = makeBus "mod24"
mod24recv = makeRecv "mod24"
mod25bus = makeBus "mod25"
mod25recv = makeRecv "mod25"
mod26bus = makeBus "mod26"
mod26recv = makeRecv "mod26"
mod31bus = makeBus "mod31"
mod31recv = makeRecv "mod31"
mod32bus = makeBus "mod32"
mod32recv = makeRecv "mod32"
mod33bus = makeBus "mod33"
mod33recv = makeRecv "mod33"
mod34bus = makeBus "mod34"
mod34recv = makeRecv "mod34"
mod35bus = makeBus "mod35"
mod35recv = makeRecv "mod35"
mod36bus = makeBus "mod36"
mod36recv = makeRecv "mod36"
mod41bus = makeBus "mod41"
mod41recv = makeRecv "mod41"
mod42bus = makeBus "mod42"
mod42recv = makeRecv "mod42"
mod43bus = makeBus "mod43"
mod43recv = makeRecv "mod43"
mod44bus = makeBus "mod44"
mod44recv = makeRecv "mod44"
mod45bus = makeBus "mod45"
mod45recv = makeRecv "mod45"
mod46bus = makeBus "mod46"
mod46recv = makeRecv "mod46"
mod51bus = makeBus "mod51"
mod51recv = makeRecv "mod51"
mod52bus = makeBus "mod52"
mod52recv = makeRecv "mod52"
mod53bus = makeBus "mod53"
mod53recv = makeRecv "mod53"
mod54bus = makeBus "mod54"
mod54recv = makeRecv "mod54"
mod55bus = makeBus "mod55"
mod55recv = makeRecv "mod55"
mod56bus = makeBus "mod56"
mod56recv = makeRecv "mod56"
mod61bus = makeBus "mod61"
mod61recv = makeRecv "mod61"
mod62bus = makeBus "mod62"
mod62recv = makeRecv "mod62"
mod63bus = makeBus "mod63"
mod63recv = makeRecv "mod63"
mod64bus = makeBus "mod64"
mod64recv = makeRecv "mod64"
mod65bus = makeBus "mod65"
mod65recv = makeRecv "mod65"
mod66bus = makeBus "mod66"
mod66recv = makeRecv "mod66"
eglevel11bus = makeBus "eglevel11"
eglevel11recv = makeRecv "eglevel11"
eglevel12bus = makeBus "eglevel12"
eglevel12recv = makeRecv "eglevel12"
eglevel13bus = makeBus "eglevel13"
eglevel13recv = makeRecv "eglevel13"
eglevel14bus = makeBus "eglevel14"
eglevel14recv = makeRecv "eglevel14"
eglevel21bus = makeBus "eglevel21"
eglevel21recv = makeRecv "eglevel21"
eglevel22bus = makeBus "eglevel22"
eglevel22recv = makeRecv "eglevel22"
eglevel23bus = makeBus "eglevel23"
eglevel23recv = makeRecv "eglevel23"
eglevel24bus = makeBus "eglevel24"
eglevel24recv = makeRecv "eglevel24"
eglevel31bus = makeBus "eglevel31"
eglevel31recv = makeRecv "eglevel31"
eglevel32bus = makeBus "eglevel32"
eglevel32recv = makeRecv "eglevel32"
eglevel33bus = makeBus "eglevel33"
eglevel33recv = makeRecv "eglevel33"
eglevel34bus = makeBus "eglevel34"
eglevel34recv = makeRecv "eglevel34"
eglevel41bus = makeBus "eglevel41"
eglevel41recv = makeRecv "eglevel41"
eglevel42bus = makeBus "eglevel42"
eglevel42recv = makeRecv "eglevel42"
eglevel43bus = makeBus "eglevel43"
eglevel43recv = makeRecv "eglevel43"
eglevel44bus = makeBus "eglevel44"
eglevel44recv = makeRecv "eglevel44"
eglevel51bus = makeBus "eglevel51"
eglevel51recv = makeRecv "eglevel51"
eglevel52bus = makeBus "eglevel52"
eglevel52recv = makeRecv "eglevel52"
eglevel53bus = makeBus "eglevel53"
eglevel53recv = makeRecv "eglevel53"
eglevel54bus = makeBus "eglevel54"
eglevel54recv = makeRecv "eglevel54"
eglevel61bus = makeBus "eglevel61"
eglevel61recv = makeRecv "eglevel61"
eglevel62bus = makeBus "eglevel62"
eglevel62recv = makeRecv "eglevel62"
eglevel63bus = makeBus "eglevel63"
eglevel63recv = makeRecv "eglevel63"
eglevel64bus = makeBus "eglevel64"
eglevel64recv = makeRecv "eglevel64"
egrate11bus = makeBus "egrate11"
egrate11recv = makeRecv "egrate11"
egrate12bus = makeBus "egrate12"
egrate12recv = makeRecv "egrate12"
egrate13bus = makeBus "egrate13"
egrate13recv = makeRecv "egrate13"
egrate14bus = makeBus "egrate14"
egrate14recv = makeRecv "egrate14"
egrate21bus = makeBus "egrate21"
egrate21recv = makeRecv "egrate21"
egrate22bus = makeBus "egrate22"
egrate22recv = makeRecv "egrate22"
egrate23bus = makeBus "egrate23"
egrate23recv = makeRecv "egrate23"
egrate24bus = makeBus "egrate24"
egrate24recv = makeRecv "egrate24"
egrate31bus = makeBus "egrate31"
egrate31recv = makeRecv "egrate31"
egrate32bus = makeBus "egrate32"
egrate32recv = makeRecv "egrate32"
egrate33bus = makeBus "egrate33"
egrate33recv = makeRecv "egrate33"
egrate34bus = makeBus "egrate34"
egrate34recv = makeRecv "egrate34"
egrate41bus = makeBus "egrate41"
egrate41recv = makeRecv "egrate41"
egrate42bus = makeBus "egrate42"
egrate42recv = makeRecv "egrate42"
egrate43bus = makeBus "egrate43"
egrate43recv = makeRecv "egrate43"
egrate44bus = makeBus "egrate44"
egrate44recv = makeRecv "egrate44"
egrate51bus = makeBus "egrate51"
egrate51recv = makeRecv "egrate51"
egrate52bus = makeBus "egrate52"
egrate52recv = makeRecv "egrate52"
egrate53bus = makeBus "egrate53"
egrate53recv = makeRecv "egrate53"
egrate54bus = makeBus "egrate54"
egrate54recv = makeRecv "egrate54"
egrate61bus = makeBus "egrate61"
egrate61recv = makeRecv "egrate61"
egrate62bus = makeBus "egrate62"
egrate62recv = makeRecv "egrate62"
egrate63bus = makeBus "egrate63"
egrate63recv = makeRecv "egrate63"
egrate64bus = makeBus "egrate64"
egrate64recv = makeRecv "egrate64"
amp1bus = makeBus "amp1"
amp1recv = makeRecv "amp1"
amp2bus = makeBus "amp2"
amp2recv = makeRecv "amp2"
amp3bus = makeBus "amp3"
amp3recv = makeRecv "amp3"
amp4bus = makeBus "amp4"
amp4recv = makeRecv "amp4"
amp5bus = makeBus "amp5"
amp5recv = makeRecv "amp5"
amp6bus = makeBus "amp6"
amp6recv = makeRecv "amp6"
ratio1bus = makeBus "ratio1"
ratio1recv = makeRecv "ratio1"
ratio2bus = makeBus "ratio2"
ratio2recv = makeRecv "ratio2"
ratio3bus = makeBus "ratio3"
ratio3recv = makeRecv "ratio3"
ratio4bus = makeBus "ratio4"
ratio4recv = makeRecv "ratio4"
ratio5bus = makeBus "ratio5"
ratio5recv = makeRecv "ratio5"
ratio6bus = makeBus "ratio6"
ratio6recv = makeRecv "ratio6"
detune1bus = makeBus "detune1"
detune1recv = makeRecv "detune1"
detune2bus = makeBus "detune2"
detune2recv = makeRecv "detune2"
detune3bus = makeBus "detune3"
detune3recv = makeRecv "detune3"
detune4bus = makeBus "detune4"
detune4recv = makeRecv "detune4"
detune5bus = makeBus "detune5"
detune5recv = makeRecv "detune5"
detune6bus = makeBus "detune6"
detune6recv = makeRecv "detune6"
lfofreqbus = makeBus "lfofreq"
lfofreqrecv = makeRecv "lfofreq"
lfodepthbus = makeBus "lfodepth"
lfodepthrecv = makeRecv "lfodepth"
feedbackbus = makeBus "feedback"
feedbackrecv = makeRecv "feedback"
Oh cool- do you have any examples of how you're patterning these with Tidal? Would be interested to hear !
Hey Lizzie!
Thank you! Aha, sure thing - these are both super rough, but here's a brief explanation and a 'from scratch' style demo (it's a bit chaotic, and gets suddenly LOUD at about 14m in, when i start messing with the FM feedback and adding effects, so be careful with levels, I always forget that room
seems to add quite a bit of gain too):
https://www.youtube.com/watch?v=z1mUpVDko40
Here's a brief demo of what I'm actually attempting to do with it (approximately, it still needs work) - there's not much to look at as most of the actual 'playing' happens on a MIDI controller hooked into various parameters rather than in code, plus messing around with some guitar pedals fed from an aux bus on the mixer:
(and the code for the above: https://gist.github.com/timcowlishaw/8734950003a4f67f0822c86f94495140)
Heloo
This is really useful and super cool, thanks for making the video some very haunting fm sounds. Always wanted to have a bit more control over superfm so this is ace.
Excited to hear if you do anything more with it
Hey @mistertim this is awsome! Thanks for the video. This is how I like to play with FM, too, and gave me lots of ideas.
I'd love to see more videos on this. Your explanation is also great. Thanks!
I have created fm algorithm selection function for superfm, based on GHEIKA’s footwork selection function. Maybe it will be of use to someone.
I have only used four operators and it is based on @ghales superfm syntax (earlier in this thread). All connections between operators are set to 1.
Here is the algorithm chart I have used:
And here is the code:
let
algs = [
lfma [1,0,0,0,0,0] # fmmod 1 2 1 # fmmod 2 3 1 # fmmod 3 4 1,
lfma [1,0,0,0,0,0] # fmmod 1 2 1 # fmmod 2 3 1 # fmmod 3 4 1 # fmmod 4 4 1,
lfma [1,0,0,0,0,0] # fmmod 1 2 1 # fmmod 2 3 1 # fmmod 2 4 1,
lfma [1,0,0,0,0,0] # fmmod 1 2 1 # fmmod 2 3 1 # fmmod 2 4 1 # fmmod 4 4 1,
lfma [1,0,0,0,0,0] # fmmod 1 2 1 # fmmod 2 3 1 # fmmod 1 4 1,
lfma [1,0,0,0,0,0] # fmmod 1 2 1 # fmmod 2 3 1 # fmmod 1 4 1 # fmmod 4 4 1,
lfma [1,0,0,0,0,0] # fmmod 1 2 1 # fmmod 1 3 1 # fmmod 3 4 1,
lfma [1,0,0,0,0,0] # fmmod 1 2 1 # fmmod 1 3 1 # fmmod 3 4 1 # fmmod 4 4 1,
lfma [1,1,0,0,0,0] # fmmod 2 3 1 # fmmod 3 4 1,
lfma [1,1,0,0,0,0] # fmmod 2 3 1 # fmmod 3 4 1 # fmmod 4 4 1,
lfma [1,1,0,0,0,0] # fmmod 1 3 1 # fmmod 2 3 1 # fmmod 3 4 1,
lfma [1,1,0,0,0,0] # fmmod 1 3 1 # fmmod 2 3 1 # fmmod 3 4 1 # fmmod 4 4 1,
lfma [1,0,1,0,0,0] # fmmod 1 2 1 # fmmod 3 4 1,
lfma [1,0,1,0,0,0] # fmmod 1 2 1 # fmmod 3 4 1 # fmmod 4 4 1,
lfma [1,1,0,0,0,0] # fmmod 2 3 1 # fmmod 2 4 1,
lfma [1,1,0,0,0,0] # fmmod 2 3 1 # fmmod 2 4 1 # fmmod 4 4 1,
lfma [1,0,0,0,0,0] # fmmod 1 2 1 # fmmod 1 3 1 # fmmod 1 4 1,
lfma [1,0,0,0,0,0] # fmmod 1 2 1 # fmmod 1 3 1 # fmmod 1 4 1 # fmmod 4 4 1,
lfma [1,1,1,0,0,0] # fmmod 3 4 1,
lfma [1,1,1,0,0,0] # fmmod 3 4 1 # fmmod 4 4 1,
lfma [1,1,1,0,0,0] # fmmod 1 4 1 # fmmod 2 4 1 # fmmod 3 4 1,
lfma [1,1,1,0,0,0] # fmmod 1 4 1 # fmmod 2 4 1 # fmmod 3 4 1 # fmmod 4 4 1,
lfma [1,0,0,0,0,0] # fmmod 1 2 1 # fmmod 1 3 1 # fmmod 2 4 1 # fmmod 3 4 1,
lfma [1,0,0,0,0,0] # fmmod 1 2 1 # fmmod 1 3 1 # fmmod 2 4 1 # fmmod 3 4 1 # fmmod 4 4 1
]
alg p = select (p|/23) algs
Because of zero-indexing algorithm 1 is 0, and so on. Here is an example of how to use the function:
d1
$ slow 2 $ note ("c5(9,16)" + "0..7")
# s "superfm"
# alg (irand 23)
# lfmr [1, 2.3, 1.9, 0.6]
# fmfeedback 0.5
Wow! This is brilliant!
Hi! While trying to recreate the DX7 E. Piano patch, I noticed a problem with the envelopes: they seem to be scaled by the total cycles length. That means that even a very fast egrate
for an operator is being overwritten when you use slow 8
on your melody pattern. However, to simulate a keypress, the attack/decay phases of some of the modulator operators have to stay really short and snappy, regardless of the length of the melody.
Now that I think about it, musically and physically, it doesn't make much sense that cycle length and instrument envelopes are connected. Is this intentional, or a limitation of either Tidal, superfm
, or SuperDirt?
Yes, I agree. It makes sense that the attack is set.
You probably already know that there is an envelope in superdirt. According to the description it is set in seconds (not relative to the cycle). I don't use it because I often get clicks.
I've created a minimal example that illustrates the problem:
do
let melo_cycle_length = 1 -- try 10, changes sound completely
let pluck_patch =
s "superfm"
# amp1 1
# ratio2 2
# mod12 1.75
-- amp envelope for op1 (snappy)
# egrate11 100
# eglevel11 1
# egrate12 0.3
# eglevel12 0.0
# egrate14 1
# eglevel14 0
-- amp envelope for op2 (also snappy)
# egrate21 100
# eglevel21 1
# egrate22 0.2
# eglevel22 0
# egrate23 1
# eglevel23 0
# egrate24 1
# eglevel24 0
let melody = slow melo_cycle_length $ note "[a4(3, 8), c(3, 8, 1), d(3, 8, 2)]"
d1
$ melody
# pluck_patch
If you make the melody pattern "longer" by increasing melo_cycle_length
, you'll notice that the pluck_patch
doesnt sound plucky at all anymore, because it's internal envelopes get stretched.
It could be that I'm alone with finding this irritating - coming from other traditional DAW/hardware tools, you'd expect your synth patch to be independent from the melody it plays. But maybe here that's intentional, and superfm users have come to like that? I would be happy to discuss this here.
So you would like your synth durations to be synced with cps
?
Looking at the code, you can see that the envelope is defined as follows:
var envs = Array.fill(6, { |i|
EnvGen.kr(
Env.new(
// EG levels
[0]++Array.fill(4, { |n| presets[3][i][n] }),
// EG rates
Array.fill(4, { |n| presets[4][i][n] })
),
timeScale:sustain,
);
});
The timeScale:sustain
argument means that the whole envelope is scale with the length of the note. If you want to fix this length, you can explicitly set the sustain in tidal. I am not sure if this is what you want.
All kinds of behaviours can be done by editing the synthdef, even on the fly …