I'm somehow just realizing how powerful the mini-notation can be in terms of adding more voices. I'm trying to wrap my head around how to control the separate voices though.
Say I have a pattern:
d1 $ slice 8 "{0 1 2 7 0 6 8 [4 2]}%4" $ s "arp:0" # legato "1 2 1 0.5"
Now I want to add another voice an octave up:
d1 $ slice 8 "{0 1 2 7 0 6 8 [4 2]}%4" $ s "arp:0" # legato "1 2 1 0.5" # up "[0,12]"
Can I now apply functions to just that second voice? Say I want to nudge just that one, should this work?:
d1 $ slice 8 "{0 1 2 7 0 6 8 [4 2]}%4" $ s "arp:0" # legato "1 2 1 0.5" # up "[0,12]" # nudge "[0,0.5]"
Can I then continue specifying effects and functions per voice in this way, like pan "[0, 1]"
, etc?
Thanks.
1 Like
I'm not sure what final sound you were aiming for, but I think off
might work in place of nudge
if you wanted to alternate between the higher and lower segments of the sample.
d1 $ off 0.5 (# up 12) $ slice 8 "{0 1 2 7 0 6 8 [4 2]}%4" $ s "arp:0" # legato "1 2 1 0.5"
and other effects can be chained for the offset voice:
d1 $ off 0.25 ( (# up 12) . (# pan 1)) $ slice 8 "{0 1 2 7 0 6 8 [4 2]}%4" $ s "arp:0" # legato "1 2 1 0.5"
Yes, good point that off
might work better in the example I gave, but what I'm really asking is if we can specify functions per voice by indexing the same values in a list. Like will the 2nd value in a list control the second voice if there is already one that's been created in the sound? Or does adding the nudge "[0,0.5]"
create a third voice that ignores the second voice created by the up "[0,12]"
?
Or put another way, could I create a second voice, then pan it and filter it like this:
d1 $ slice 8 "{0 1 2 7 0 6 8 [4 2]}%4" $ s "arp:0" # legato "1 2 1 0.5" # up "[0,12]" # pan "[0,1] # lpf [300,900]"
I hope it makes sense what I'm asking. It seems like this would be a powerful way to control polyphony. But maybe it doesn't work like that?
can specify functions per voice by indexing the same values in a list
No.
Operators #
(etc.) work on the semantics (the contents) of patterns, not on their syntax (the shape of their source code)
The semantics of a pattern is: (function from time slice to) set of events. This loses all syntactical information.
If you want to keep/manipulate structure (e.g., for separation of voices) then you have to represent it semantically (in the types).
Example: uses pattern of pairs, and transforms first component:
let p = (,) <$> "foo" <*> "bar" :: Pattern (String,String)
p
(0>1)|("foo","bar")
p >>= (\ (x,y)-> stack[pure x,pure y])
(0>1)|"foo"
(0>1)|"bar"
(mapFst reverse <$> p) >>= (\ (x,y)-> stack[pure x,pure y])
(0>1)|"oof"
(0>1)|"bar"
You can't play these things directly (you can only play Pattern ControlMap
), so need a transformer for output
(after >>=
in the example)
Also, mini-notation might be harder to use for patterns over non-standard types, as it requires some constraints for parsing to work.
2 Likes
Thanks @jwaldmann.
Your Haskell solution is beyond me, but I think I get what you're saying about how the functions work over time chunks.
But I also still don't really understand how polyphony in mini-notation works.
Continuing on with my previous example:
d1 $ slice 8 "0 1 2 7" $ s "arp:0" # lpf "900" # up "[0,12]"
From the way it sounds to me, when I add the second voice using up "[0,12]"
I'm getting a copy of the sound an octave up that includes the lowpass filter (and therefore I assume everything before the up
). Is it right that if I then add another function after that, like a nudge "[0,0.5]"
that the 0.5 won't effect the existing (second) voice but will in fact create an entirely new voice that is a copy of everything before it? So I would end up with 3 voices? So every time mini-notation polyphony is used a new voice is created with everything before that call?
I guess that leaves the 'correct way' to specify multiple functions for individual voices as:
superimpose
layer
stack
jux
Is that correct?
Thanks!
If I then add another function after that, like a nudge "[0,0.5]"
that the 0.5 won't effect the existing (second) voice
but will in fact create an entirely new voice that is a copy of everything before it?
will apply # nudge 0
and # nudge 0.5
and stack
the results, yes.
s "arp" # up "[0,12]"
(0>1)|note: 0.0f, s: "arp"
(0>1)|note: 12.0f, s: "arp"
s "arp" # up "[0,12]" # nudge "[0, 0.5]"
(0>1)|note: 0.0f, nudge: 0.0f, s: "arp"
(0>1)|note: 0.0f, nudge: 0.5f, s: "arp"
(0>1)|note: 12.0f, nudge: 0.0f, s: "arp"
(0>1)|note: 12.0f, nudge: 0.5f, s: "arp"
It will also copy parameters for effects (filters, etc.) but there are some ("global") effects where a copy of the parameter does not mean that the effect is copied (instead the different parameters will be used by just one instance of the effect processor). There was a separate thread about this.
3 Likes
That definitely clears me up a bit, thank you!