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