Chorale
This composition is an infinite minimalist chorale in four parts written in Haskell using the Euterpea library. It is useful to compute its cycle length: the smallest number of beats after which the piece repeats itself. This turns out to be 156780 beats. At a metronome marking of 120 beats per minute, that means 21 hours 46 minutes and 30 seconds of music. Whew!
With the cycle length in hand, one
can construct a one-cycle finite version of the piece.
It can then be exported to MIDI
using the Haskell code writeMidi "chorale.midi" chorale'
.
The result is a 1.1 megabyte MIDI file. Yikes! Importing the file into an
program such as Logic Pro, one can produce
a playable audio file of reasonable size. To do this, I first deleted everything
after measure 200, then exported the resulting data to a .WAV file. This file
was converted to mp3 and uploaded here:
I plan to work further on this composition. At the very least, the phrases need to be shaped so that they start off softly, then crescendo, and finally decrescendo near the end of the phrase. A decrescendo to pianissimo trailing off into silence at the end would also be good. More generally, the ending of the 3 minute, 34 second clip from the “full” 21+ hour cycle of the infinite piece is abrupt and unsatisfactory.
An interesting technical point is whether there is a way of streaming midi output from Haskell running on a computer to a MIDI device that would perform the piece in some esthetically satisfactory way, e.g, with decent sounding instruments.
Whether using an audio program for post-processing or using a streaming MIDI solution, I think it would be good also to have a touch of reverb so that the music would blend a bit and possibly sound more mysterious.
Code (Haskell)
module Chorale where
import Euterpea
--- THE COMPOSITION ---
-- Infinite version:
chorale :: InstrumentName -> Music Pitch
chorale player = instrument player $ bass :=: tenor :=: tenor2 :=: solo
-- Finite version with instrument = clarinet
chorale' :: Music Pitch
chorale' = bassLine' :=: tenorLine' :=: tenorLine2' :=: soloLine'
-- Examples:
-- > playDev 2 $ chorale Clarinet -- play the infinite version
-- > playDev 2 chorale' -- play the finite version
-- > writeMidi "chorale.midi" 'chorale' -- save to a MIDI file
--- CONSTRUCTION OF THE INFINITE VERSION ---
solo :: Music Pitch
solo = forever $ soloLine
tenor :: Music Pitch
tenor = forever $ tenorLine
tenor2 :: Music Pitch
tenor2 = forever $ tenorLine2
bass :: Music Pitch
bass = forever $ bassLine
--- CONSTRUCTION OF THE PARTS ---
-- The bass line
bassNote = d 1
bassNote' = ds 1
bassNote'' = e 1
bassNote''' = f 1
bassLine = line $ [bassNote 6, rest 3, bassNote' 3, rest 3, bassNote'' 3
, rest 2, bassNote''' 4, rest 1 , bassNote 7, rest 4]
-- The tenor
tenorNote = a 1
tenorNote' = c 2
tenorNote'' = d 2
tenorLine = line $ map (rescale (1/2)) [rest 2, tenorNote 8, rest 3
, tenorNote' 4, tenorNote'' 5, rest 4]
-- The second tenor
tenorLine2 = rest 7 :+: f 1 4 :+: c 2 4
-- The solo
soloMotif = line $ [a 2 1, d 3 1, c 3 2, f 3 2, e 3 4, rest 2]
soloMotif' = rest 9 :+: soloMotif :+: transpose 3 soloMotif
soloLine = soloMotif' :+: transpose 7 soloMotif' :+: rest 1
--- CONSTRUCTION OF THE PARTS FOR THE FINITE VERSION ---
-- Compute the length of each line
lSolo = ilength soloLine
lTenor = ilength tenorLine
lTenor2 = ilength tenorLine2
lBass = ilength bassLine
-- Compute the cycle length as the
-- least common multiple of the lengths
-- of the individual lines.
cycleLength = lcm_ [lSolo, lTenor, lTenor2, lBass]
-- Compute the number of times each line of
-- music must be repeated so as to have
-- the given cycle length.
nSolo = cycleLength `div` lSolo
nTenor = cycleLength `div` lTenor
nTenor2 = cycleLength `div` lTenor2
nBass = cycleLength `div` lBass
-- Construct one cycle of the chorale
soloLine', tenorLine', tenorLine2', bassLine' :: Music Pitch
soloLine' = mrepeat nSolo soloLine
tenorLine' = mrepeat nTenor tenorLine
tenorLine2' = mrepeat nTenor2 tenorLine2
bassLine' = mrepeat nBass bassLine
-- Verify:
-- > map ilength [bassLine', tenorLine', tenorLine2', soloLine']
-- [156780,156780,156780,156780]
--- HELPER FUNCTIONS ---
-- Compute the length of a melody in beats
mlength :: Music Pitch -> Dur
mlength mp =
case mp of
(Prim (Note d p)) -> d
Prim (Rest d) -> d
m1 :+: m2 -> mlength m1 + mlength m2
m1 :=: m2 -> maximum [mlength m1, mlength m2]
Modify control m -> mlength m
-- Compute the length of a melody in beats, but convert
-- to an integer. Oops, we loose fractional beats,
-- if there are any.
ilength :: Music Pitch -> Int
ilength mp = round $ mlength mp
-- Repeat the music m n times
mrepeat :: Int -> Music a -> Music a
mrepeat 0 m = rest 0
mrepeat n m = m :+: mrepeat (n - 1) m
-- Least common multiple of a list of integers.
lcm_ :: [Int] -> Int
lcm_ [] = 1
lcm_ (x:[]) = x
lcm_ (x:xs) = lcm x (lcm_ xs)
-- Change the duration of each note by a factor
rescale :: Dur -> Music Pitch -> Music Pitch
rescale factor mp =
case mp of
(Prim (Note d p)) -> Prim (Note (factor * d) p)
Prim (Rest d) -> Prim (Rest (factor * d))
m1 :+: m2 -> rescale factor m1 :+: rescale factor m2
m1 :=: m2 -> rescale factor m1 :=: rescale factor m2
Modify control m -> Modify control (rescale factor m)