What constitutes a musical pattern?

Orestis Melkonian, Iris Ren, Wouter Swierstra, Anja Volk

Introduction

♫ Pattern 𝄞

hanon1roll.png

Diverse range of examples

# #+ATTRHTML: :height 80%, :width 80%

examples.png

Different terminologies and theories

  • lick
  • riff
  • leitmotif
  • sequence

Described in different ways

  • the smallest independent particle in a musical idea (Webern)
  • structural unit possessing thematic identity (White)
  • a unit which contains one or more features of interval and rhythm [whose] presence is maintained in constant use throughout a piece (Schoenberg)

Computation can help

Model complex music

Formulate simple ones

complextrans.png

Musical pattern discovery algorithms

  • Geometric
  • String-based
  • Machine learning

But different algorithms find lots of different patterns!

Patterns discovered at very different locations

alg.png

Challenges

  • How to model, extract, compare patterns, given the diversity of music?
  • How to interpret and use the results from algorithmic pattern discovery?

Perception, convention, interaction, disagreement, not easy…

A starting point: repetition and variations

  • Functional programming
  • Music Information Retrieval (MIR)
  • Music theory and musicology

What constitutes a musical pattern?

Taking a relative perspective, how do they relate to each other:

Occurrence 1 <=> Occurrence 2

Transformations

transformations.png

Music Data

  • MTC-ANN (Folk): Dutch folk songs
  • JKU-PDD (Classical): Bach, Mozart, Beethoven, Chopin, Gibbons

Symbolic, monophonic music with human-annotated patterns

MIREX: Music Information Retrieval Evaluation eXchange

Comparison

annoalg.png

JKU-PDD Human Annotation: 105 pattern occurrences

ce.png

JKU-PDD + 7 Algorithms: 6274 pattern occurrences

ca.png

Comparison: all

ca.png

Classical-Alg: 6274

ce.png

Classical-Anno: 105

fa.png

Folk-Alg: 47314

fe.png

Folk-Anno: 1546

Implementation

Type Synonyms

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 Type: Pattern

data Note = Note
  { ontime  :: Time
  , midi    :: MIDI
  }
type Pattern = [Note]

Data Type: Pattern Group

data PatternGroup = PatternGroup
  { prototype   :: Pattern
  , occurences  :: [Pattern]
  }

Check

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)

Equivalently: Use a newtype or type synonym

newtype Check a b = Check { unCheck :: a -> b -> Bool }

or

type Check a b = a -> b -> Bool

HomCheck

type HomCheck a = Check a a

Example:

equal :: Eq a => HomCheck a
equal = MkCheck (==)

Combining checks

instance Monoid (Check a b) where
  mempty = MkCheck (\ _  _  -> True)
  p <> q = MkCheck (\ x  y  ->   (x <=> y) p
                             &&  (x <=> y) q)

Check one feature

checkBasedOnTime :: (Pattern -> Time) -> HomCheck Time -> HomCheck Pattern

Contravariant functor!

Borrowing from category theory

In the general case: Contravariant Bifunctor!

contra.png

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)

For HomCheck

(>$<) :: (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

Exact repetition

exactRepetitionOf :: HomCheck Pattern
exactRepetitionOf  =   rhythm  >$< equal
                   <>  pitch   >$< equal

pitch :: Pattern -> [MIDI]

rhythm :: Pattern -> [Time]

equal :: Eq a => Check a
equal = Check (==)

Approximation

  • ([A,C,F,A,B] <=> [A,C,G,A,B])
  • ([A,B,C,D,E] <=> [A,Bb,B,D,E,F,F\#])
type ApproxCheck a = Float -> Check a

Controlling the degree of fuzziness with a percentage based score!

Inversion

inversionOf :: ApproxCheck Pattern
inversionOf = basePitch >$< equal
           <> rhythm    >$< approxEq2
           <> intervals >$< (inverse $< approxEq2)

inversion.png

List of transformations

  • Exact repetition
  • Real/Chromatic transposition
  • Tonal/Diatonic transposition
  • Tonal/Diatonic Inversion
  • Retrograde
  • Augmentation
  • Diminution

Querying and Discovery

Querying and discovery come for free

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
  ...

Examples

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)

Conclusion

  • Category theory and Haskell in modelling and implementing higher order comparison: compare occurrence relations using musical transformations.
  • Implications for music:
    • Differences between musical pattern discovery algorithms and experts annotations.
    • Differences between different corpora.
  • Useful pattern query/discovery tool: https://github.com/omelkonian/hs-pattrans

Thank you!

Created by ...