It works[1]! I'll be developing this more; if you have ideas I'd love to
hear them.
This lets you send instructions to different types of synths in a single
string. For instance, the following creates a boop named margaret and a
sqfm named margaret (every synthdef has a separate namespace), sends them
messages, and frees them.
-- set it up
import Text.Megaparsec (parse)
import Control.Concurrent (forkIO)
import Control.Concurrent.MVar (readMVar)
import Data.Either (fromRight)
reg <- emptySynthRegister
f s = forkIO $ mapM_ act $ fromRight [] $ parse (msgs reg) "" s
-- use it
f "new boop margaret, send boop margaret freq 444.0 amp 0.1, wait 1.0,
new sqfm margaret, send sqfm margaret freq 555.0 amp 0.2 width 0.1
width-vib-freq 22.0 width-vib-amp 0.1, wait 1.0, free sqfm margaret, free
boop margaret"
Next I plan to implement the concepts of time, sequence, melody and a
scheduler, and then a bunch of combinators, and then the real language.
(This language was just a proof of concept -- I had to know whether
polymorphic parsing was possible before committing to the rest of those
plans.)
The parser has to return something of a single well-defined type, so I had
to use a GADT (currently called Action) that forgets the sdArgs type
argument of the message it is delivering. That means I can't hand an Action
to a synth, because the type checker would complain that it's not sure
they're compatible. Instead I include in the Action an MVar that points to
the Synth where the message is destined. That MVar is a map from Strings to
Synths -- so if the Action is "send the message (freq 444) to the boop
named emerson" it can look up the name "emerson" in that map.)
A downside to this approach is that I have to operate in IO space rather
than VividAction space, since you can't manipulate an MVar from within a
VividAction. On the plus side that means I'll be able to print strings as
well, which I find helpful for debugging. (Or maybe it *is* possible to
print and handle MVars from a VividAction?) Fortunately, the timing of IO
in GHC has gotten impressively solid[2].
TidalCycles implements sequences as functions from time to sound. It's a
beautiful, natural representation, but I found it hard to work with; an
idea like "delete every other note in this melody" was very hard to code. I
expect to use vectors; they're roughly as easy to work with as lists, and
faster.
[1]
https://github.com/JeffreyBenjaminBrown/vivid/tree/ce2e2891aec383a1de46ba...
[2] https://mail.haskell.org/pipermail/haskell-cafe/2018-July/129374.html
--
Jeff Brown | Jeffrey Benjamin Brown
Website <https://msu.edu/~brown202/> | Facebook
<https://www.facebook.com/mejeff.younotjeff> | LinkedIn
<https://www.linkedin.com/in/jeffreybenjaminbrown>(spammy, so I often miss
messages here) | Github <https://github.com/jeffreybenjaminbrown>

I find myself still fighting Vivid's type safety for synth arguments ...
Suppose you had a synth `tuba` with two parameters, `freq` and `amp`, and
another synth called `snare` with one parameter, `whack`. The tuba is
playing a melody, and you'd like the snare to duplicate what the tuba is
doing.
Specifically, the tuba should ignore every `freq` message, but it should
convert every `amp` message into a `whack` message.
The filter would need, I believe, the following type signature:
{-# LANGUAGE ScopedTypeVariables #-}
f :: forall inParams outParams.
( VarList inParams, VarList outParams,
, Subset (InnerVars inParams) '["freq","amp"]
, Subset (InnerVars outParams) '["whack"] )
=> inParams -> outParams
But even the most trivial definition imaginable for `f` does not compile:
f _ = ()
Is this possible?
--
Jeff Brown | Jeffrey Benjamin Brown
Website <https://msu.edu/~brown202/> | Facebook
<https://www.facebook.com/mejeff.younotjeff> | LinkedIn
<https://www.linkedin.com/in/jeffreybenjaminbrown>(spammy, so I often miss
messages here) | Github <https://github.com/jeffreybenjaminbrown>

Thanks, Tom! I have one exclamation and two questions about that.
(1) OMG localIn and localOut are already defined in Vivid! Somehow the
first time I searched for them I didn't find them. Suddenly this looks way
more hopeful!
(2) Can you get that localBuf example to compile? Here's what I'm trying:
{-# LANGUAGE DataKinds, ExtendedDefaultRules #-}
import Vivid
foo :: SynthDef '[]
foo = sd () $ do
l <- localBuf (numChans_ 1, numFrames_ 48000)
fb <- playBuf (buf_ l, loop_ 1)
fb <- delayL (in_ fb, maxDelaySecs_ 0.1, delaySecs_ 0.001)
-- delay of 0.001 ~ frequency of 1000 Hz
s <- 0.1 ~* whiteNoise
s <- s ~+ (0.99 ~* fb)
-- 0.99 multiplier => gradual decay
recordBuf (buf_ l, in_ s, loop_ 1)
out 0 [s,s]
main = do
s <- synth foo ()
wait 1
free s
(I changed the delaySecs argument of the delayL UGen to 0.001, to give it a
frequency of 1000 Hz. I also changed the feedback scale factor to 0.99, so
that it will decay gradually.)
When I run 'main' GHC hangs until interrupted, and SC reports nothing.
(3) We've got a delay of 1 ms, but we're writing onto a buffer that's an
entire second long. So it looks like the scale factor (0.99 above) will
only kick in after a full second. Is that correct? If I'm understanding it
correctly, it seems like this idiom would require me to tune the length of
the buffer to the length of the delay. For instance if it's delaying for 1
ms and the sample rate is 48K, then the buffer should be 48 samples long.
Right?

hi
finally got
cabal install csound-expression
complete its job on pi3(raspbian-lite stretch jackless)
but
dac $ osc 440
results into csound's error complaining that
device 'default' could not be found
i thought jack wasn't a must
but maybe it is?
can i twist this to use alsa instead?
anyone running this on pi?

Inicio del mensaje reenviado:
> De: amindfv(a)gmail.com
> Fecha: 10 de julio de 2018, 2:13:32 EDT
> Para: haskell-art(a)group.lurk.org
> Asunto: Re: [livecode] Re: Vivid: Brief, triggered sounds
>
> Yep, you've got exactly the right idea with adsrGen! Specifically, the difference you notice with 0.01 seconds vs 0.0001 is that "gate" triggers when the value goes from zero to non-zero. Assuming sample rate 48,000 and 64 samples per control period, 0.0001 isn't enough.
>
> I only have a minute now so can only point you in the general direction. I'll get back with more specifics soon.
>
> "percGen" is (basically) a special case of "envGen"+"env". The args to "env" are pairs of (target value, time to get there). Try:
>
> env 0 [(1,1),(0,1)] (Curve_Curve (-4))
>
> Then, check out "_envLiterally_loopNode", to specify where the envelope should loop back, then use "envGen_wGate" to tie it all together.
>
> Hope this points you in the right direction!
>
> Tom
>
>> El 9 jul 2018, a las 21:43, Jeffrey Brown <jeffbrown.the(a)gmail.com> escribió:
>>
>> I suspect I should provide an example. Here's a working SynthDef that Tom wrote most of:
>>
>> foo :: SynthDef '["gate"]
>> foo = sd (1 :: I "gate") $ do
>> e <- adsrGen 1 1 0.5 1
>> (Curve_Curve $ 0)
>> (gate_ (V::V "gate"))
>> s <- e ~* sinOsc (freq_ (500 ~+ (100 ~* e)))
>> out 0 [s,s]
>>
>> And here's a use of it, which also works:
>>
>> test pauseLength = doScheduledIn 0.1 $ test' pauseLength
>> test' pauseLength = do
>> s <- synth foo () -- Start the ADSR envelope. By default gate=1.
>> wait 2
>>
>> set s (0 :: I "gate") -- Trigger the R phase.
>> wait pauseLength
>> -- The next "gate" signal will only retrigger the AD phase if
>> -- the R phase is allowed to progress for something like a millisecond.
>> -- (To see this, try running `test 0.01` vs. `test 0.0001`.)
>>
>> set s (1 :: I "gate") -- Maybe retrigger the AD phase.
>> wait 2
>>
>> free s
>>
>> Thus by dancing the right dance with its "gate" input, we can get an adsrGen to retrigger. I'm hoping there's a similar way to get the percGen to retrigger, but with only one message instead of two.
>>
>>> On Sun, Jul 8, 2018 at 2:20 PM Jeffrey Brown <jeffbrown.the(a)gmail.com> wrote:
>>> I spoke a little too soon. I am able to retrigger a "sustainning" envelope like ADSR or ASR, but only awkwardly.
>>>
>>> Those envelopes take a "gate" argument. (I'm taking these terms from the SuperCollider built-in documentation.) When "gate" is positive, the envelope is "open", which means that it will not progress past the sustain portion of the curve. When the gate signal becomes zero, the gate is "closed", and will move from sustain to release.
>>>
>>> I can't retrigger an open envelope. I've got to close it by sending a gate=0 signal, and then wait about a milisecond, before sending a gate=1 signal to open it again. This has the desired effect: The envelope value rises again as in the attack phase, before the release phase reaches zero.
>>>
>>> So I was hoping to be able to do something similar with the "fixed-length" envelopes like Env.perc (Vivid's percGen) or Env.linen (Vivid's linen). In the SC documentation for Env, it describes the gate parameter in the following way: "If the Env is fixed-length (e.g. Env.linen, Env.perc), the gate argument is used as a simple trigger. If it is an sustaining envelope (e.g. Env.adsr, Env.asr), the envelope is held open until the gate becomes 0 ..." That led me to think maybe I can send gate=1 repeatedly to a percGen in order to retrigger it before it has gone to zero.
>>>
>>> But percGen has no gate argument!
>>>
>>> Am I trying to do something that's possible?
>>>
>>>
>>> On Sat, Jul 7, 2018 at 11:15 PM Jeffrey Brown <jeffbrown.the(a)gmail.com> wrote:
>>>> Solved!
>>>>
>>>>> On Sat, Jul 7, 2018 at 6:43 PM Jeffrey Brown <jeffbrown.the(a)gmail.com> wrote:
>>>>> This is awesome!
>>>>>
>>>>> What if the voice persisted and you needed to retrigger the envelope later? I'm imagining a resonating guitar string: It's continuously making sound, but every now and then the player strikes it again. Or someone playing monophonic slide violin: They might want to play an F# for a while, then slide into an A. Or a didgeridoo: The sound is in a steady state, then the player makes a "pew" noise, and then the didge settles back into another (usually the same) steady state.
>>>>>
>>>>> In the first case there's an amplitude envelope that's (probably) in its decay portion when it is retriggered. In the second there's a frequency envelope that's (probably) reached its sustain portion when it is retriggered to go to a new frequency.
>>>>>
>>>>> This might dovetail with an earlier question about sending strings to synths. The synth needs to react not to a parameter change that persists, but rather to a momentary instruction -- something like what the Max/MSP community calls a "bang".
>>>>>
>>>>> Actually in the slide violin case the synth would also receive a new frequency value. And I can imagine writing a synth that has a rule like "when the frequency changes, slide into it logarithmically over the course of 100 ms". So maybe you wouldn't need bang-like messages in some cases. But in the case of re-striking a resonating guitar string, or making a "pew" sound into a didge, it might be that none of the steady-state parameters needs changing; the envelope just needs to be re-triggered.
>>>>>
>>>>> Expressible?
>>>>>
>>>>>> On Sat, Jul 7, 2018 at 12:36 AM <amindfv(a)gmail.com> wrote:
>>>>>> I unfortunately need to log out but here's something to get you started:
>>>>>>
>>>>>> foo = sd (1 :: I "gate") $ do
>>>>>> e <- adsrGen 0.2 0.1 0.6 0.7 (Curve_Curve (-4)) (gate_ (V::V "gate"))
>>>>>> s <- e ~* sinOsc (freq_ $ midiCPS 50)
>>>>>> out 0 [s,s]
>>>>>>
>>>>>> main = do
>>>>>> s <- synth foo ()
>>>>>> wait 1
>>>>>> release s
>>>>>>
>>>>>>> El 6 jul 2018, a las 23:03, Jeffrey Brown <jeffbrown.the(a)gmail.com> escribió:
>>>>>>>
>>>>>>> So far I'm able from Vivid to create continuously-running signal architectures and change their parameters over time. Next I want to create a "snare" (some noise with a brief amplitude envelope) and a "kick" (a sine wave with a quickly descending frequency).
>>>>>>>
>>>>>>> Is there an idiom for that?
>>>>>>>
>>>>>>>
>>>>>>> --
>>>>>>> Jeff Brown | Jeffrey Benjamin Brown
>>>>>>> Website | Facebook | LinkedIn(spammy, so I often miss messages here) | Github
>>>>>>> _______________________________________________
>>>>>>> Livecode mailing list -- livecode(a)we.lurk.org
>>>>>>> To unsubscribe send an email to livecode-leave(a)we.lurk.org
>>>>>> _______________________________________________
>>>>>> Livecode mailing list -- livecode(a)we.lurk.org
>>>>>> To unsubscribe send an email to livecode-leave(a)we.lurk.org
>>>>>
>>>>>
>>>>> --
>>>>> Jeff Brown | Jeffrey Benjamin Brown
>>>>> Website | Facebook | LinkedIn(spammy, so I often miss messages here) | Github
>>>>
>>>>
>>>> --
>>>> Jeff Brown | Jeffrey Benjamin Brown
>>>> Website | Facebook | LinkedIn(spammy, so I often miss messages here) | Github
>>>
>>>
>>> --
>>> Jeff Brown | Jeffrey Benjamin Brown
>>> Website | Facebook | LinkedIn(spammy, so I often miss messages here) | Github
>>
>>
>> --
>> Jeff Brown | Jeffrey Benjamin Brown
>> Website | Facebook | LinkedIn(spammy, so I often miss messages here) | Github
>> _______________________________________________
>> Livecode mailing list -- livecode(a)we.lurk.org
>> To unsubscribe send an email to livecode-leave(a)we.lurk.org