module Haskore.Example.Fractal where import Prelude hiding (init) import System.Random (randomRs, mkStdGen) import Data.Array (Array, (!), listArray, bounds) import qualified Haskore.Basic.Pitch as Pitch import qualified Haskore.Music as Music import qualified Haskore.Melody as Melody import Haskore.Music((+:+)) import qualified Haskore.Basic.Duration as Dur type Vector a = [a] type Matrix a = [Vector a] type AT a = Vector a -> Vector a type IFS a = Array Int (AT a) -- First define some general matrix operations. -- These will facilitate moving to higher dimensions later. vadd :: Num a => Vector a -> Vector a -> Vector a vadd = zipWith (+) vvmult :: Num a => Vector a -> Vector a -> a vvmult v1 v2 = sum (zipWith (*) v1 v2) mvmult :: Num a => Matrix a -> Vector a -> Vector a mvmult m v = map (vvmult v) m cvmult :: Num a => a -> Vector a -> Vector a cvmult z = map (z*) --------------------------------------------------------------------- {- The following simulates the Iterated Function System for the Sierpinski Triangle as described in Barnsley's "Desktop Fractal Design Handbook". -} -- First the affine transformations: w0, w1, w2 :: Fractional a => AT a w0 v = (cvmult 0.01 ([[50,0],[0,50],[50,0]] `mvmult` v)) `vadd` [8,8,8] w1 v = (cvmult 0.01 ([[50,0],[0,50],[50,0]] `mvmult` v)) `vadd` [30,16,2] w2 v = (cvmult 0.01 ([[50,0],[0,50],[50,0]] `mvmult` v)) `vadd` [20,40,30] init0 :: Num a => Vector a init0 = [0,0,0] -- Now we have an Iterated Function System: ws :: Fractional a => IFS a ws = let wl = [w0,w1,w2] in listArray (0, length wl - 1) wl -- And here is the result: result :: [Vector Rational] result = let ws' = ws -- make it monomorph f init r = (ws'!r) init in scanl f init0 (randomRs (bounds ws') (mkStdGen 215)) -- (read "42" :: StdGen) -- where "randomRs" computes a list of random indices in the range 0-2, -- which simulates flipping the coin in Barnsley. -------- mkNote :: [Rational] -> Melody.T () mkNote [a,b,c] = Music.rest (Dur.fromRatio (b/20)) +:+ Melody.note (Pitch.fromInt (round a)) (Dur.fromRatio (c/20)) () mkNote _ = error "mkNote: Need three components." {- Of course, a triple would be the better type but that would complicate the vector computation. -} sourceToMusic :: [[Rational]] -> Melody.T () sourceToMusic s = Music.chord (map mkNote s) song :: Melody.T () song = Music.transpose (-12) (sourceToMusic (take 128 result))