Randomness at the beginnings of time (function randrun)

I was using randrun, and wanted to apply the usual trick of time-shifting, to get different (pseudo) random sequences.

I am surprised that near time 0, sequences are not permuted at all. Also, for small shifts, there are long consecutive subsequences:

do
  let p = 
       ( cat $ replicate 100
             $ n (fmap fromIntegral
                  $ 0.1 -- <== change this (increase slowly)
                        -- up to 0.5 : contains long increasing runs
                        -- above 1.0 : fully random
                  ~> randrun 12)
       )
  resetCycles
  d1 $ stack [ p
             , slow 1.01 p -- to get the "Piano Phase" effect, just for show
             ]
     # s "superpiano"
     # room 0.8 

Focusing on randrun:

ghci> flip queryArc (Arc 0 1) $ randrun 10
[(0>⅒)|0,(⅒>⅕)|1,(⅕>3/10)|2,(3/10>⅖)|3,(⅖>½)|4,(½>⅗)|5,(⅗>7/10)|6,(7/10>⅘)|7,(⅘>9/10)|8,(9/10>1)|9]

ghci> flip queryArc (Arc 0.5 1.5) $ randrun 10
[(½>⅗)|5,(⅗>7/10)|6,(7/10>⅘)|7,(⅘>9/10)|8,(9/10>1)|9,(1>1⅒)|3,(1⅒>1⅕)|1,(1⅕>13/10)|2,(13/10>1⅖)|5,(1⅖>1½)|4]

a- ha, it is run 10 from time 0 to 1, and random only after that? Intentionally?

[the following is a bit technical ... in short, there is a problem in my code (time shift is useless), and there's a problem in randrun implementation (ignores Arc endpoint), and I propose an alternative. I can make a PR if someone confirms that the idea works, and would like to use it.]

  • time-shifting: this only makes sense for the continuous pattern (rand), not fot the discretized one (randrun is discrete). E.g., two different sequences, events (for both) at each fourth:
segment 4 (0.1 ~> irand 10) 
-- [(0>⅒)-¼|9,0-(⅒>¼)|5,(¼>½)|7,(½>¾)|5,(¾>1)|0]
segment 4 (0.2 ~> irand 10)
--  [(0>⅕)-¼|8,0-(⅕>¼)|4,(¼>½)|1,(½>¾)|8,(¾>1)|6]

something quite different:

 0.1 ~> (segment 4 $ irand 10)
-- [-117/20-(0>⅒)|3,(⅒>7/20)|3,(7/20>⅗)|6,(⅗>17/20)|1,(17/20>1)-1⅒|3]
0.2 ~> (segment 4 $ irand 10)
-- [-119/20-(0>⅕)|3,(⅕>9/20)|3,(9/20>7/10)|6,(7/10>19/20)|1,(19/20>1)-1⅕|3]
  • non-randomness for randrun at time zero: I think the reason is
randrun n' =
  splitQueries $ Pattern (\(State a@(Arc s _) _) -> events a $ sam s)
.....................................^^^^^^^^

uses just s (start of arc) and ignores the arc's end.

NB - Each time I see (in Tidal sources) an implementation that works on queries directly, I wonder - is it really necessary to break the abstraction barrier? I am dreaming of a world where data Pattern a is opaque (constructor is not exported), and the module that contains data Pattern a = ... is as small as possible)

  • I tried to find a "more abstract" implementation. Maybe this:
rands :: Fractional a => Int -> Pattern [a]
rands k = Pattern $ \(State a@(Arc s e) _)
  -> [Event (Context []) Nothing a (map realToFrac $ (timeToRands ((e + s)/2) k :: [Double]))]

randrun' :: Int -> Pattern Int
randrun' n = segment 1 (rands n) >>= \ rs ->
  let go [] [] = []
      go (r:rs) xs =
        let i = floor (r * fromIntegral (length xs))
            (pre, p:ost) = splitAt i xs
        in  p : go rs (pre <> ost)
  in  fastcat $ map pure $ go rs [0 .. n-1]

perhaps rands can be built in some better way (I just copied rand and replaced timeToRand with timeToRands).

  • If we want a different "random" realization (for events at the same time), then time-shifting does not help, see above. To be able to do this, we can
randrun'' :: Int -> Pattern (Pattern Int)
randrun'' n = flip fmap (rands n) $ \ rs ->
  let go [] [] = []
      go (r:rs) xs =
        let i = floor (r * fromIntegral (length xs))
            (pre, p:ost) = splitAt i xs
        in  p : go rs (pre <> ost)
  in  fastcat $ map pure $ go rs [0 .. n-1]

note on the result type: the outer pattern is continuous (it refers to rand), and the inner pattern is discrete. In the application, we can then

 unwrap  $ segment 1  $ 0.4  ~> randrun'' 12

and by changing the shift, we get different values while keeping the timing.