> I ended up splitting out all the Vivid.Jbb stuff from the stuff
> provided by the 'vivid' package, so I don't have confusion with the
> "real" vivid. This did mean I had to re-import the >26 arg instances.
> Overall I'm glad I did, since I feel like it'll get harder to
> disentangle later.
I agree, it's not arranged optimally yet. Rather than Vivid.Jbb.Dispatch I
intend to move Dipsatch to Vivid.Dispatch. Vivid.Jbb.Synths ought to be
somewhere also; maybe Dispatch.Synths.Examples? I thought once it's good
I'd post it to Hackage as a separate, Vivid-dependent package.
> You mention that you silence synths before freeing them, to
> prevent popping sounds. Does this definitely prevent that? If I'm
> looking in the right place it looks like "amp" is just getting set to
> zero, which can still cause pops, right? (I.e. sudden drops from 1.0
> to 0.0)
You're right, it's no sure thing. In fact the Boop synth (so far the only
one I'm really using, even though Zot is really dope -- it's based on
Reaktor Spark[1], a crazy feedback synth by Stephen Schmidt of Native
Instruments, the manual of which illustrates the network) pops when it's
silenced. I'll need to use envelopes ... which still baffle me ...
The better solution will be for the user to specify on a per-synth basis
how to turn it off poplessly. dispatchLoop would then send that
message|those messages, half a frameDuration before freeing the synth.
> The Vivid.Jbb.ReadHsAsGhci stuff is a clever solution to the
> reload problem, but if you haven't seen it before I might recommend
> the foreign-store package (hackage.haskell.org/package/foreign-store).
> This should let you do a true ghci ":r", while keeping the bindings
> for your current "disp", "tid", "hush", etc. I think this could work
> well with your ":s" and ":." workflow
Brilliant!
> # Install things (note I'm using ghc+cabal, not stack):
> ...
I've added a note to the todo doc that I should include such Cabal guidance
when publishing it. I don't even have Cabal installed; your guidance might
be the entirety of it.
When I get around to splitting Vivid.Dispatch from Vivid I'll ask you
whether you're able to run it in Cabal.
> Here, have a (very) rudimentary pattern!:
> stop disp "meta" >> stop disp "a slow pattern"
> replace disp "a faster one" $ cat [fast 4 $ p 330 220, fast 4 $ p
> 660 440, rev $ p 440 220, rev $ fast 4 $ p 330 440, fast 4 $ p 330
> 440]
Woah -- rhythm! Added to demo.
> I also needed to comment out the "Vivid.Jbb.Random.Signal" line
Neat -- I didn't realize stack forgives exposing a nonexistent module until
you pointed out that Cabal doesn't.
[1]
https://www.native-instruments.com/en/products/komplete/synths/reaktor-sp...

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>