Playing random note durations with Euterpea / Haskell

128 views Asked by At

I'm just getting started with Euterpea / Haskell, and I am trying to write a simple script that randomizes the note durations.

I wrote this that works:

import Euterpea
playMyNote = play $ line [c 4 qn, c 4 qn, d 4 qn, e 4 qn, a 4 qn, a 4 qn, g 4 hn]

Then I read this: https://www.schoolofhaskell.com/school/starting-with-haskell/libraries-and-frameworks/randoms

Which shows a way of generating random numbers like this:

import System.Random

main = do
  g <- getStdGen
  print $ take 10 (randomRs ('a', 'z') g)

I am trying to combine them like below, but it's not complete (I don't know where to put z for example.)

Can anyone advise me on the next step to replace the numbers representing duration with randomly defined numbers?

This is where I'm at currently:

import Euterpea
import System.Random

playRandomly = do
    z <- newStdGen
    play $ line [c 4 qn, c 4 qn, d 4 qn, e 4 qn, a 4 qn, a 4 qn, g 4 hn]


playMyNote = play $ line [c 4 qn, c 4 qn, d 4 qn, e 4 qn, a 4 qn, a 4 qn, g 4 hn]
1

There are 1 answers

0
leftaroundabout On

First (in general, not only for Euterpea) you want to look up the type of the quantity you want. This can be seen e.g. by checking the type signature of the note-functions like c, d etc.. Turns out they all have type

c :: Octave -> Dur -> Music Pitch

In the documentation you can follow the links to the types, where you'll find that Dur is really just a synonym for Rational. So all you really need is generate random rational numbers. There are a couple of options you have, depending on how you want the randomness:

  • Generate random integers for numerator and denominator, and combine them to a rational.

    playRandomly = do
       z <- newStdGen
       let (zn, zd) = split z
           numrs = randomRs (1,5) zn
           denoms = randomRs (1,4) zd
       play $ line
         [ note 4 $ fromInteger numr / fromInteger denom
         | (note, (numr,denom)) <- zip [c, c, d, e, a, a, g]
                           $ zip numrs denums
         ]
    

    Obtaining those separate random-generators and then the numbers from them is really awkward, note that this is much easier with a suitable random monad like from random-fu.

  • Generate continuously-variable (floating) durations, and only pseudo-quantize them to rationals.

    playRandomly = do
       z <- newStdGen
       let durations = toRational <$> (randomRs (0.1,2) z :: [Float])
       play $ line
         [ note 4 duration
         | (note, duration) <- zip [c, c, d, e, a, a, g] durations
         ]
    
  • Just choose randomly from a list of possible durations. That's also something you can easily do with random-fu.