# #+ATTRHTML: :height 80%, :width 80%
Model complex music
Formulate simple ones
But different algorithms find lots of different patterns!
Perception, convention, interaction, disagreement, not easy…
Taking a relative perspective, how do they relate to each other:
Occurrence 1 <=> Occurrence 2
Symbolic, monophonic music with human-annotated patterns
MIREX: Music Information Retrieval Evaluation eXchange
Classical-Alg: 6274
Classical-Anno: 105
Folk-Alg: 47314
Folk-Anno: 1546
type Time = Double
type MIDI = Integer
Onset(Time), Pitch(Frequency)
pattern1 occurrence1 7.00000, 45.00000 ... 11.00000, 60.00000 occurrence2 31.00000, 57.00000 ...
data Note = Note
{ ontime :: Time
, midi :: MIDI
}
type Pattern = [Note]
data PatternGroup = PatternGroup
{ prototype :: Pattern
, occurences :: [Pattern]
}
data Check a b = MkCheck (a -> b -> Bool)
(<=>) :: a -> b -> Check a b -> Bool
(x <=> y) (MkCheck p) = p x y
Example checkers:
alwaysTrue :: Check a b
alwaysTrue = MkCheck (\ _ _ -> True)
newtype Check a b = Check { unCheck :: a -> b -> Bool }
or
type Check a b = a -> b -> Bool
type HomCheck a = Check a a
Example:
equal :: Eq a => HomCheck a
equal = MkCheck (==)
instance Monoid (Check a b) where
mempty = MkCheck (\ _ _ -> True)
p <> q = MkCheck (\ x y -> (x <=> y) p
&& (x <=> y) q)
checkBasedOnTime :: (Pattern -> Time) -> HomCheck Time -> HomCheck Pattern
Contravariant functor!
In the general case: Contravariant Bifunctor!
class ContravariantBifunctor p where
contraBimap :: (c -> a) -> (d -> b) -> p a b -> p c d
contraBimap f g = contra1 f . contra2 g
contra1 :: (c -> a) -> p a b -> p c b
contra1 f = contraBimap f id
contra2 :: (d -> b) -> p a b -> p a d
contra2 g = contraBimap id g
instance ContravariantBifunctor Check where
contraBimap f g p = MkCheck (\ x y -> (f x <=> g y) p)
(>$<) :: (b -> a) -> HomCheck a -> HomCheck b
f >$< p = contraBimap f f p
(>$) :: (a -> a) -> HomCheck a -> HomCheck a
(>$) = contra1
($<) :: (a -> a) -> HomCheck a -> HomCheck a
($<) = contra2
exactRepetitionOf :: HomCheck Pattern
exactRepetitionOf = rhythm >$< equal
<> pitch >$< equal
pitch :: Pattern -> [MIDI]
rhythm :: Pattern -> [Time]
equal :: Eq a => Check a
equal = Check (==)
type ApproxCheck a = Float -> Check a
Controlling the degree of fuzziness with a percentage based score!
inversionOf :: ApproxCheck Pattern
inversionOf = basePitch >$< equal
<> rhythm >$< approxEq2
<> intervals >$< (inverse $< approxEq2)
type WindowSize = Int
type Query a = (HomCheck a, a)
type MusicPiece = Pattern
query :: Query Pattern -> MusicPiece -> [Pattern]
query (checker, base) =
filter (\p -> (base <=> p) checker) . slide (length base)
where
...
In combination with Euterpea:
data UserQuery a = ToPattern a => Check Pattern :@ a
query1 :: UserQuery (Music Pitch)
query1 = (transpositionOf ~~ 0.5) :@
(line $ map ($qn) [c 4, e 4, g 4, c 5])
query2 :: UserQuery (Time, Time)
query2 = (transpositionOf ~~ 0.5) :@ (21 `upTo` 28)
Created by ...