packages feed

Euterpea 2.0.2 → 2.0.3

raw patch · 145 files changed

+439/−211 lines, 145 filesdep ~heapbinary-addedPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: heap

API changes (from Hackage documentation)

+ Euterpea.IO.MIDI.FromMidi2: chunkEvents :: [MEvent] -> Chunk
+ Euterpea.IO.MIDI.FromMidi2: chunkToMusic :: Chunk -> Music (Pitch, Volume)
+ Euterpea.IO.MIDI.FromMidi2: data Chunk
+ Euterpea.IO.MIDI.FromMidi2: fromMidi2 :: Midi -> Music (Pitch, Volume)
+ Euterpea.IO.MIDI.FromMidi2: instance GHC.Classes.Eq Euterpea.IO.MIDI.FromMidi2.Chunk
+ Euterpea.IO.MIDI.FromMidi2: instance GHC.Classes.Ord Euterpea.IO.MIDI.FromMidi2.Chunk
+ Euterpea.IO.MIDI.FromMidi2: instance GHC.Show.Show Euterpea.IO.MIDI.FromMidi2.Chunk
+ Euterpea.IO.MIDI.FromMidi2: restructure :: (ToMusic1 a) => Music a -> Music (Pitch, Volume)
- Euterpea.IO.Audio.CSound: oscil :: (Clock p, ArrowCircuit a) => Table -> Double -> ArrowP a p Double Double
+ Euterpea.IO.Audio.CSound: oscil :: (ArrowCircuit a, Clock p) => Table -> Double -> ArrowP a p Double Double
- Euterpea.IO.Audio.CSound: oscil1 :: (Clock p, ArrowCircuit a, ArrowChoice a) => Table -> Double -> Double -> ArrowP a p Double Double
+ Euterpea.IO.Audio.CSound: oscil1 :: (ArrowCircuit a, ArrowChoice a, Clock p) => Table -> Double -> Double -> ArrowP a p Double Double
- Euterpea.IO.Audio.CSound: oscil1i :: (Clock p, ArrowCircuit a, ArrowChoice a) => Table -> Double -> Double -> ArrowP a p Double Double
+ Euterpea.IO.Audio.CSound: oscil1i :: (ArrowCircuit a, ArrowChoice a, Clock p) => Table -> Double -> Double -> ArrowP a p Double Double
- Euterpea.IO.Audio.CSound: oscili :: (Clock p, ArrowCircuit a) => Table -> Double -> ArrowP a p Double Double
+ Euterpea.IO.Audio.CSound: oscili :: (ArrowCircuit a, Clock p) => Table -> Double -> ArrowP a p Double Double
- Euterpea.IO.Audio.CSound: oscils :: (Clock p, ArrowCircuit a) => Double -> ArrowP a p Double Double
+ Euterpea.IO.Audio.CSound: oscils :: (ArrowCircuit a, Clock p) => Double -> ArrowP a p Double Double
- Euterpea.IO.MIDI.ToMidi: toDelta :: (RealFrac a, Integral b) => a -> b
+ Euterpea.IO.MIDI.ToMidi: toDelta :: (Integral b, RealFrac a) => a -> b

Files

− .git/COMMIT_EDITMSG
@@ -1,1 +0,0 @@-Dependency versions narrowed again
.git/FETCH_HEAD view
@@ -1,1 +1,1 @@-e1a838d747ab8dcfe9e50965ef0ca4bf7c7a3779		branch 'master' of https://github.com/Euterpea/Euterpea2+ef06402d714aa53dae0ba9c74b68d8b4ddea1329		branch 'master' of https://github.com/Euterpea/Euterpea2
.git/ORIG_HEAD view
@@ -1,1 +1,1 @@-fd7be762cd1a7bbca6b0eeec10ab260858db340d+23b0fe619f7f91be4123ef976dd9284c11c1093a
.git/config view
@@ -5,7 +5,6 @@ 	logallrefupdates = true 	symlinks = false 	ignorecase = true-	hideDotFiles = dotGitOnly [remote "origin"] 	url = https://github.com/Euterpea/Euterpea2.git 	fetch = +refs/heads/*:refs/remotes/origin/*
+ .git/hooks/pre-receive.sample view
@@ -0,0 +1,24 @@+#!/bin/sh+#+# An example hook script to make use of push options.+# The example simply echoes all push options that start with 'echoback='+# and rejects all pushes when the "reject" push option is used.+#+# To enable this hook, rename this file to "pre-receive".++if test -n "$GIT_PUSH_OPTION_COUNT"+then+	i=0+	while test "$i" -lt "$GIT_PUSH_OPTION_COUNT"+	do+		eval "value=\$GIT_PUSH_OPTION_$i"+		case "$value" in+		echoback=*)+			echo "echo from the pre-receive-hook: ${value#*=}" >&2+			;;+		reject)+			exit 1+		esac+		i=$((i + 1))+	done+fi
.git/hooks/update.sample view
@@ -1,6 +1,6 @@ #!/bin/sh #-# An example hook script to blocks unannotated tags from entering.+# An example hook script to block unannotated tags from entering. # Called by "git receive-pack" with arguments: refname sha1-old sha1-new # # To enable this hook, rename this file to "update".
.git/index view

binary file changed (2340 → 2436 bytes)

.git/logs/HEAD view
@@ -1,12 +1,2 @@-0000000000000000000000000000000000000000 61d09a71180e5a1a4ea1d893f82a4709d8e79c27 donya <donyavq@netscape.net> 1453085786 -0500	clone: from https://github.com/Euterpea/Euterpea2.git-61d09a71180e5a1a4ea1d893f82a4709d8e79c27 b1eb4e6bd0c330b7cbe720d59df11bf3105b2b85 donya <donyavq@netscape.net> 1455231002 -0500	pull --progress --rebase --prune origin master: checkout b1eb4e6bd0c330b7cbe720d59df11bf3105b2b85-b1eb4e6bd0c330b7cbe720d59df11bf3105b2b85 b1eb4e6bd0c330b7cbe720d59df11bf3105b2b85 donya <donyavq@netscape.net> 1455231002 -0500	rebase finished: returning to refs/heads/master-b1eb4e6bd0c330b7cbe720d59df11bf3105b2b85 086557985556cc77f5a559d74d0502a2d761babd donya <donyavq@netscape.net> 1456848084 -0500	commit: ToMusic1 instance for AbsPitch added-086557985556cc77f5a559d74d0502a2d761babd e461c0ab2477605902ab7a9c42969603f38bd586 donya <donyavq@netscape.net> 1465925605 -0500	pull --progress --rebase --prune origin master: checkout e461c0ab2477605902ab7a9c42969603f38bd586-e461c0ab2477605902ab7a9c42969603f38bd586 e461c0ab2477605902ab7a9c42969603f38bd586 donya <donyavq@netscape.net> 1465925606 -0500	rebase finished: returning to refs/heads/master-e461c0ab2477605902ab7a9c42969603f38bd586 aa72de41851f42d3f1023c01ae0edc5832c47ebd donya <donyavq@netscape.net> 1465930431 -0500	commit: Dependency versions temporarily relaxed-aa72de41851f42d3f1023c01ae0edc5832c47ebd 40d6be12def8fc357df4248d9943bd3cc1f75f89 donya <donyavq@netscape.net> 1466564399 -0500	pull --progress --rebase --prune origin master: checkout 40d6be12def8fc357df4248d9943bd3cc1f75f89-40d6be12def8fc357df4248d9943bd3cc1f75f89 40d6be12def8fc357df4248d9943bd3cc1f75f89 donya <donyavq@netscape.net> 1466564400 -0500	rebase finished: returning to refs/heads/master-40d6be12def8fc357df4248d9943bd3cc1f75f89 fd7be762cd1a7bbca6b0eeec10ab260858db340d donya <donyavq@netscape.net> 1467320139 -0500	commit: Dependency versions narrowed again-fd7be762cd1a7bbca6b0eeec10ab260858db340d e1a838d747ab8dcfe9e50965ef0ca4bf7c7a3779 donya <donyavq@netscape.net> 1468034494 -0500	pull --progress --rebase --prune origin master: checkout e1a838d747ab8dcfe9e50965ef0ca4bf7c7a3779-e1a838d747ab8dcfe9e50965ef0ca4bf7c7a3779 e1a838d747ab8dcfe9e50965ef0ca4bf7c7a3779 donya <donyavq@netscape.net> 1468034494 -0500	rebase finished: returning to refs/heads/master+0000000000000000000000000000000000000000 23b0fe619f7f91be4123ef976dd9284c11c1093a Donya Quick <donyaquick@gmail.com> 1495035254 -0700	clone: from https://github.com/Euterpea/Euterpea2.git+23b0fe619f7f91be4123ef976dd9284c11c1093a ef06402d714aa53dae0ba9c74b68d8b4ddea1329 Donya Quick <donyaquick@gmail.com> 1496256684 -0500	pull --no-rebase --progress origin: Fast-forward
.git/logs/refs/heads/master view
@@ -1,8 +1,2 @@-0000000000000000000000000000000000000000 61d09a71180e5a1a4ea1d893f82a4709d8e79c27 donya <donyavq@netscape.net> 1453085786 -0500	clone: from https://github.com/Euterpea/Euterpea2.git-61d09a71180e5a1a4ea1d893f82a4709d8e79c27 b1eb4e6bd0c330b7cbe720d59df11bf3105b2b85 donya <donyavq@netscape.net> 1455231002 -0500	rebase finished: refs/heads/master onto b1eb4e6bd0c330b7cbe720d59df11bf3105b2b85-b1eb4e6bd0c330b7cbe720d59df11bf3105b2b85 086557985556cc77f5a559d74d0502a2d761babd donya <donyavq@netscape.net> 1456848084 -0500	commit: ToMusic1 instance for AbsPitch added-086557985556cc77f5a559d74d0502a2d761babd e461c0ab2477605902ab7a9c42969603f38bd586 donya <donyavq@netscape.net> 1465925606 -0500	rebase finished: refs/heads/master onto e461c0ab2477605902ab7a9c42969603f38bd586-e461c0ab2477605902ab7a9c42969603f38bd586 aa72de41851f42d3f1023c01ae0edc5832c47ebd donya <donyavq@netscape.net> 1465930431 -0500	commit: Dependency versions temporarily relaxed-aa72de41851f42d3f1023c01ae0edc5832c47ebd 40d6be12def8fc357df4248d9943bd3cc1f75f89 donya <donyavq@netscape.net> 1466564400 -0500	rebase finished: refs/heads/master onto 40d6be12def8fc357df4248d9943bd3cc1f75f89-40d6be12def8fc357df4248d9943bd3cc1f75f89 fd7be762cd1a7bbca6b0eeec10ab260858db340d donya <donyavq@netscape.net> 1467320139 -0500	commit: Dependency versions narrowed again-fd7be762cd1a7bbca6b0eeec10ab260858db340d e1a838d747ab8dcfe9e50965ef0ca4bf7c7a3779 donya <donyavq@netscape.net> 1468034494 -0500	rebase finished: refs/heads/master onto e1a838d747ab8dcfe9e50965ef0ca4bf7c7a3779+0000000000000000000000000000000000000000 23b0fe619f7f91be4123ef976dd9284c11c1093a Donya Quick <donyaquick@gmail.com> 1495035254 -0700	clone: from https://github.com/Euterpea/Euterpea2.git+23b0fe619f7f91be4123ef976dd9284c11c1093a ef06402d714aa53dae0ba9c74b68d8b4ddea1329 Donya Quick <donyaquick@gmail.com> 1496256684 -0500	pull --no-rebase --progress origin: Fast-forward
.git/logs/refs/remotes/origin/HEAD view
@@ -1,1 +1,1 @@-0000000000000000000000000000000000000000 61d09a71180e5a1a4ea1d893f82a4709d8e79c27 donya <donyavq@netscape.net> 1453085786 -0500	clone: from https://github.com/Euterpea/Euterpea2.git+0000000000000000000000000000000000000000 23b0fe619f7f91be4123ef976dd9284c11c1093a Donya Quick <donyaquick@gmail.com> 1495035254 -0700	clone: from https://github.com/Euterpea/Euterpea2.git
.git/logs/refs/remotes/origin/master view
@@ -1,7 +1,1 @@-61d09a71180e5a1a4ea1d893f82a4709d8e79c27 b1eb4e6bd0c330b7cbe720d59df11bf3105b2b85 donya <donyavq@netscape.net> 1455231001 -0500	pull --progress --rebase --prune origin master: fast-forward-b1eb4e6bd0c330b7cbe720d59df11bf3105b2b85 086557985556cc77f5a559d74d0502a2d761babd donya <donyavq@netscape.net> 1456848102 -0500	update by push-086557985556cc77f5a559d74d0502a2d761babd e461c0ab2477605902ab7a9c42969603f38bd586 donya <donyavq@netscape.net> 1465925602 -0500	pull --progress --rebase --prune origin master: fast-forward-e461c0ab2477605902ab7a9c42969603f38bd586 aa72de41851f42d3f1023c01ae0edc5832c47ebd donya <donyavq@netscape.net> 1465930440 -0500	update by push-aa72de41851f42d3f1023c01ae0edc5832c47ebd 40d6be12def8fc357df4248d9943bd3cc1f75f89 donya <donyavq@netscape.net> 1466564397 -0500	pull --progress --rebase --prune origin master: fast-forward-40d6be12def8fc357df4248d9943bd3cc1f75f89 fd7be762cd1a7bbca6b0eeec10ab260858db340d donya <donyavq@netscape.net> 1468028809 -0500	update by push-fd7be762cd1a7bbca6b0eeec10ab260858db340d e1a838d747ab8dcfe9e50965ef0ca4bf7c7a3779 donya <donyavq@netscape.net> 1468029425 -0500	fetch --prune --recurse-submodules=on-demand origin: fast-forward+23b0fe619f7f91be4123ef976dd9284c11c1093a ef06402d714aa53dae0ba9c74b68d8b4ddea1329 Donya Quick <donyaquick@gmail.com> 1496256683 -0500	fetch --progress --prune origin: fast-forward
− .git/objects/08/1fa266eebe94ad9d1401de24163f05d6d378b3

binary file changed (168 → absent bytes)

− .git/objects/08/6557985556cc77f5a559d74d0502a2d761babd
@@ -1,1 +0,0 @@-x•ŽKŽÂ0Yû}P·q$4š9ÀH,¸€»ÝV²Àaƒ4·å¬^mJõd¹ÝæÖÓ¡¯ª0&‹Hc°Ö9‰å‡Z|µ‰Ô¹Š•$š{^µu`Rö¹ 8‡<ë`±„±T"®Ž0°åL~ôiY¡,í?ÃyŸçßwÓ¾I¾ëé
_@>Ää&GˆFöw]?õÌuù}l³Ìm빉B}Çx»Ì]&È¥h1/v(M£
− .git/objects/08/cf131880f52ce6a6d1e8dd2f0afb8ee0f30bcf

binary file changed (1048 → absent bytes)

− .git/objects/09/53e4337fcc90c2a5c2ec957f9d46b4aa32ca99

binary file changed (1548 → absent bytes)

− .git/objects/0c/262c7a72c9e17768fa826a90472e376c09767a

binary file changed (233 → absent bytes)

− .git/objects/0e/40a7618f3033863399b53fd837057d05c90d33

binary file changed (155285 → absent bytes)

− .git/objects/10/aedf5be4cd4cba55b64d5f6995b2848da5156b

binary file changed (15321 → absent bytes)

− .git/objects/10/f805b915ea4190789c13856fc1c8e0003fd2aa

binary file changed (244 → absent bytes)

− .git/objects/13/ff9346b625728e873af12be8d41ba411d16d3a

binary file changed (10765 → absent bytes)

− .git/objects/16/053d551af0017fc14257241ace886edd2afc02

binary file changed (188 → absent bytes)

− .git/objects/18/2a0398ba6a070c251b036c128fb0d923b1b04a

binary file changed (108 → absent bytes)

− .git/objects/1b/963cb16e69a4719c07d01041419fc0016888e9

binary file changed (4844 → absent bytes)

− .git/objects/1d/865ab036c3dafe78300b3876f46e7fc91b1836

binary file changed (579 → absent bytes)

− .git/objects/1e/e46d0b56ddaa82b9a081a21369c1509b84402e

binary file changed (158 → absent bytes)

− .git/objects/21/e4b87b26d4717ebc40733493992d3025b1d90e

binary file changed (244 → absent bytes)

− .git/objects/26/d1e77943b1fe7cb446dd7ac0fef7cc1c495845
@@ -1,6 +0,0 @@-xTQoÚ@Þóý-+O‰J"J7:¡1i+tªZvÓ¦I½-]’+œš\Xr)l¿¾vr˜PÄç³?۟틳"†Ó·çïÞäEZgn…YVóimd¹’|ø{–e¥-Ýki>+=QeP¼Q±'Â'£ZndBGçDח*“_E.×瘝Џhƒ&¬—²”Œ©|U”~×"SJ¦pQhSY4Ý$re0
tgOèÑÏ6Oð­àGQ8gdMõ³*KmÀÇðܙܖ2«SL"&Y^Í`4ü~¿]Í:™nÈ"pBç:Þæ5pŒY	Õ&Ø©ÆN÷ƒ^¿×€û+Í™Š›oŠ5:±ÛÈUË"ʄKS™u¦îÁ¼‹ç?*¤‡óOi®4~V+âŒóo…@łóDÄ"ã£z¬
ùÞêìý0\câź-Ë$<NûÑçn¤ÂAԏúá¥þõ·Nn×_òé©®îfÃåÆc¶ÆãÓ~eà¦4Kޑﲂ†G¼4‰ÇرëEK¸Ý$;±ÝthX«j;õโæí yø|Nƒ\JS—Zu@)µk~¤™=V݀мMpN¥bû¿cõ.û¸„ÂȶÑ{hÛ«p‡¿C¹m;»¸½-šR©î±ÛÝKûLìöçS#•ø$ÒJ©¿d‚Ä¡ÎvƧ»“ð8÷èKn{é+ñ@
− .git/objects/27/f49fd3429321b1ee2c1546eb63256dc0730c1f

binary file changed (3699 → absent bytes)

− .git/objects/29/3bd1d6053e165fb63a1bc860ce65d28160759f

file too large to diff

− .git/objects/2c/230b496eb8d7ee15595fc8bb6e0893af19008b

binary file changed (7054 → absent bytes)

− .git/objects/32/eee8d97247283b82f847ae18b0739f135577c6
@@ -1,1 +0,0 @@-x+)JMU°0g040031QH,É(Žw--I-*HMÔË(fP»ø¼Òy㿚-nw«üû~FÆ3ª891)1'>71¹(¿X/ƒáö¦7334â¦òO<÷õ¨Cᵇ‡ÛÌJ(s
− .git/objects/35/05de91ee4ca4ab2c00ab4d0e05daae68dde53b

binary file changed (12169 → absent bytes)

− .git/objects/36/e8447f23fe4c9fc02644fae5965b7b9fe966fd

binary file changed (28935 → absent bytes)

− .git/objects/37/3df86f01c69ae7dcc599f9c11e786415e4d1e6

binary file changed (27398 → absent bytes)

− .git/objects/38/b9dc35dfc14d5f4d529bdbdffe7a3ea8b12458

binary file changed (245 → absent bytes)

− .git/objects/39/c0cba701ff610264b64dcb238120dc18c08501

binary file changed (244 → absent bytes)

− .git/objects/3d/2457f12c9429a1f6df0d9005b8ee39c2b8515a

binary file changed (6844 → absent bytes)

− .git/objects/3f/fdf51a212612f37f71d8456e980e3a9445dea9

binary file changed (1174 → absent bytes)

− .git/objects/40/0528d8d6e72ec2edc4fee57e208bc972fdc90f
@@ -1,2 +0,0 @@-x+)JMU03e040031QvÓËÈdØQä֑Ì*Ûûêä‘;âþA+-ò>i§>Lßú*d‡3«h¯¡Y÷Ël
− .git/objects/40/d6be12def8fc357df4248d9943bd3cc1f75f89
@@ -1,1 +0,0 @@-xŽMNÃ0FYûs*ÿ%R…@pŽ0™«‰œ	RoOÚ]·ì¾·xŸÕe™‚sOÚD ë„ìØ#eáHŒ)ö#Æá…ÆŒh1
è|V̊MŠbò,Ñ
ËÑsÈÎú@Ö¡Xaê†à)&Ù஗Úೖ+Â×>Ñ7œù¿?oEt#\åtŒWp±ïC\Lðl;k
ÝUþg\×ùúQ‹¶:o°Ô&Ðê¸ojÌ;³0 ó¤S-8á&ä#4†VЋÀãMÞݜ“ùß[hÀ
− .git/objects/41/2224f723a4330a7004dc7c1eb1f62ddef2c206

binary file changed (12653 → absent bytes)

− .git/objects/42/4bc74689367dde97ffde93f82558e7dc6c0bf2

binary file changed (18249 → absent bytes)

+ .git/objects/44/4f7257c52f2671cbe413f4bdcf8ca94ca23263 view
@@ -0,0 +1,5 @@+x¥‘½+Â0FówlA++:hÁ+Ω¹j IJ~,¾½iŠh‘’Á19'_òÝԍªa¹Z̄b®AØ9‹ºEše¶qŒ+À̶ÔðKÅo¹“fîUˆ¨1éôl1æeä¦#J†z:Í-žéc¼:(-ükSè.Z¥íoùQ¿ˆe¦yh5‹rš
]y÷€«¤ïiÖfÞ …„ÍMê7t(_åžöRà\(gsî?x8ÏÀ|2ûiü›û•²_=L“
− .git/objects/46/6a9d4591dd2b033e07fa515b559afe6166f0a3

binary file changed (245 → absent bytes)

− .git/objects/4a/fd6ed7d81c474bf5c88c395a584e4f68b039ce

binary file changed (263 → absent bytes)

− .git/objects/52/c3e648f28c37c75cbad302221d9d7e63b8b83e

binary file changed (82 → absent bytes)

− .git/objects/54/7dcd3ab2847acaaa664560053063485e9799af
@@ -1,2 +0,0 @@-xŽA-Â0E]çs%iÒv".¼€G˜ÌL°H›ZSÁÛ[=‚»÷á}x\Æq¨ÐxÜÕE˜mŸZ$Ìì]Ö¦“ì5%›bK>'/”„ÌL‹NeéTzAÇ¡)·ŒÈ~S[r‡ÉúÈjh­·²À¥Lo‚ë:ðŽò¯ÇyÒúdšõ°Á	\èt1Æö6Xkø×Xõ¿·¹^Ç­•êP&Xg¡ªb>	ÿN©
− .git/objects/55/ec0b6acfed4cda746ba489cbfaa0a78a12330e

binary file changed (298 → absent bytes)

− .git/objects/57/c5213c0484db428f6c4cf6e6214e89a8ae1495

binary file changed (1896 → absent bytes)

− .git/objects/5a/0a4abb675bb7edeeb1581ef36c3107d9ddabc0

binary file changed (8803 → absent bytes)

− .git/objects/5a/db5e8db2e5525db664ff2e17de8b87ff0f8670

binary file changed (356980 → absent bytes)

− .git/objects/60/492b4c521313c25cb4c46b57d0b72cc74d7cbd

binary file changed (136 → absent bytes)

− .git/objects/65/0d6db099811f2892c4dd0d24c34de9a8d07435

binary file changed (244 → absent bytes)

− .git/objects/67/eab6e41d9b7d3b8cce4cfc4b5ad44649fba760

binary file changed (242 → absent bytes)

− .git/objects/6b/cae40f355478a4d15eee9b66d161e7a0a12dd2

binary file changed (1133 → absent bytes)

− .git/objects/6e/b5f25f3191008705cf5d25da4253992c579f87

binary file changed (136 → absent bytes)

− .git/objects/70/c3238c627e9fc1187b9828d5583a470e9ad056

binary file changed (6697 → absent bytes)

− .git/objects/79/3937ccb5e6531cc7ad87b1d34f4ef1a23a6e72

binary file changed (2445 → absent bytes)

− .git/objects/7a/5e865406dc8dc8d2bd730b7625656fc4c80036

binary file changed (200 → absent bytes)

− .git/objects/7a/e36bd46d77905af4da2001aedd031cc773f6c2

binary file changed (82 → absent bytes)

− .git/objects/80/c8d821ef0e1310670d9383a0164a161ef6d896

binary file changed (6112 → absent bytes)

− .git/objects/81/64db06d50157acee8946c245af5c82e93298f7

binary file changed (2953 → absent bytes)

− .git/objects/87/c4b67904dfc03dcabd7f4c0ec98fc4031f8531
@@ -1,2 +0,0 @@-x+)JMU022e040031QpJ,ÎLÎLw+Í+ÖËÉ(fh|W°Æ}Ê7ÕÏ=SÄ÷‰püŸpYm±PQP_΅ÇsçîžTk½eÏ
»ó½¿®A9ç—æ¥€Ú0Aè‰Ãgk
%g÷Ú:iºö}{¨*O1•–æg¶>–9¾¶}ãe¿‹¬òŠ -‚RóRR‹@ŠÔTµÎŠw±ÜØÙUʘUêÛ]}äþ]¨¢Ê‚T°{ÎnÌ­ò­yxwK~WºêL†µ™Sƒ²^Î
− .git/objects/89/6621f361866ce787d0bad1dfd5434d371b5679
@@ -1,1 +0,0 @@-x;NAD‰ç}¬Þùd!$ÀGèín˖í™e=‹áöØNȑ*¨<©ŠÛé´ï`S~è³*ĀeÄRò0lm.–½Šõì¼h¡,˜¼f¢Yk‡DAs£p¾ÅŽ’Ž)ÚCܲçŒè¢¡¥ïÚ¯­þ|,{>ÀZnðõù\µŸ™&]]Ë>ΗÑ#¾oìú?Û¼}Omîg c^vT«ßiª¸Y*\OAmл£²2¿Y¨
+ .git/objects/8b/b417a4b89ec3ecea4151d2ac8e0de8499e1eda view

binary file changed (absent → 7569 bytes)

− .git/objects/8b/c8ec9664745b51066a29467fdcc9ce217a69b9

binary file changed (1130 → absent bytes)

− .git/objects/8f/926490ec9977aaa657c03492a2092cd96ed879

binary file changed (36212 → absent bytes)

− .git/objects/90/f10f0ae98fa550ed4f3ba96b2726b2fa0f5948

binary file changed (79 → absent bytes)

+ .git/objects/92/93d3861772b24047a535829d74e8eeb2b46735 view
@@ -0,0 +1,1 @@+x+)JMU06´d040031Qp­(È/*ñÍLÉtËÌIÕËÉ(f˜[ñ«Ùä¶|>y˜âw}éÖKæPÕnEù¹ µ`uJ³3å÷·ªÝñsxÛý1ÍI¾*4uF`…ÓýÕ?³E¯Üüywñž¬#IÛVï‚*tOÍK-J̛	´ú„¾ —qYjcY©Õ²9;ô>vPªôu-KÍ+›~Tц¥å¶SŽÏ·gŠ~+։L…©:ÎÓ¬ª{‹ø’ó¿yåxiMïÏyr· ªr+Áj–Ër+]n·—¼ýÞþbKí¦¤ +/¨š|¸GsgØý1þu>–qþû;‚‡‚Yôó¥Âvà€¨
+ .git/objects/95/0a22f91621d654a3fc02109d04e693efd601dd view

binary file changed (absent → 82 bytes)

− .git/objects/95/530bd9f8b33aa4403e449541aca8782fcf58d7

binary file changed (107426 → absent bytes)

− .git/objects/97/d0dbaf178d03770a923bdd241b477208b116bf

binary file changed (1290 → absent bytes)

− .git/objects/98/2001952233e68b31aa67fd4fce281e33f0f1c6

binary file changed (244 → absent bytes)

− .git/objects/98/70f262ba46f8c63923b4c9d373b5502d534bff

binary file changed (73 → absent bytes)

− .git/objects/9d/73156b57d5c16c87c54244219f4be0df7a4d9c

binary file changed (106 → absent bytes)

− .git/objects/9e/ad253d2b2018bf4a7fb68a0470bb225793668e

binary file changed (41122 → absent bytes)

− .git/objects/a2/3d8e779deb983f7f4d0b32ada14d2249281d48

binary file changed (1131 → absent bytes)

− .git/objects/a3/69e39dd54db4cf0df66937ea62949fc00175e5

binary file changed (82 → absent bytes)

− .git/objects/a3/6b03bd6d8dc290929d43a93ba9ff0609ffd6de

binary file changed (240 → absent bytes)

− .git/objects/a7/1c310b22d3873f19dbef3fd1847db262523a4a

binary file changed (4678 → absent bytes)

− .git/objects/a9/232c0b57605a3ad8a06faad4de4c75fde6ba1a

binary file changed (4984 → absent bytes)

− .git/objects/a9/54bb4608151754bc3ef6b10f354257fc23247e

binary file changed (137 → absent bytes)

− .git/objects/aa/6d02eafa38dd2e10940244f0951551753f3060

binary file changed (2597 → absent bytes)

− .git/objects/aa/72de41851f42d3f1023c01ae0edc5832c47ebd

binary file changed (255 → absent bytes)

+ .git/objects/aa/81ebb1eb76e5ad15763b0d1d4706c5e4e9b390 view

binary file changed (absent → 1137 bytes)

− .git/objects/aa/9e74daedbcb4fbae1920ed99ce78a3b122d07f

binary file changed (244 → absent bytes)

− .git/objects/ab/dbdc6ad3b69bcd244b80ee836215e015658a56

binary file changed (8294 → absent bytes)

− .git/objects/ad/f2673657fa321a214a529e015ee10b062af2f4

binary file changed (82 → absent bytes)

− .git/objects/af/80f1983c7a72bae981b15def1d8cc05c9ec872

binary file changed (82 → absent bytes)

− .git/objects/af/a69c96947b53fb4d8c3f13d2597f42f166b954

binary file changed (1971 → absent bytes)

− .git/objects/b1/127851608df2ad2af6966e3558157bf768a183

binary file changed (21353 → absent bytes)

− .git/objects/b1/3d346f7417c0e401862c9a2473787ff583afd1

binary file changed (482058 → absent bytes)

− .git/objects/b1/eb4e6bd0c330b7cbe720d59df11bf3105b2b85

binary file changed (223 → absent bytes)

− .git/objects/b3/dac8fd881bc1fd8dfe20ab121cfa7344ace238

binary file changed (11887 → absent bytes)

− .git/objects/b6/af0dcb1cc69563bda463208ca520057e2e11b3

binary file changed (245 → absent bytes)

− .git/objects/b7/d28caad2a4b8583bee99a026cabed13ed818d6

binary file changed (244 → absent bytes)

− .git/objects/b8/724b568863051d8deac9e814dc00174f5281a9

binary file changed (6690 → absent bytes)

− .git/objects/bc/7e70f191403e2d3c97a09b11bcf2476f038a60

binary file changed (664 → absent bytes)

− .git/objects/be/ff554d699578a58c434cccfd0b09b8be1b872d

binary file changed (270 → absent bytes)

− .git/objects/c4/2b297f598a10a933a3e8a3856946c737e25a90

binary file changed (1309 → absent bytes)

− .git/objects/c4/3ce9302dcd23cedd4cf553e09c685e528e587d

binary file changed (1244 → absent bytes)

− .git/objects/c5/fc3c5cebdf7ad00414095d73e7f628cd20d4b4

binary file changed (5132 → absent bytes)

− .git/objects/c6/5ac01da8f5dfafd421c9c0deadc2dc429057e3

binary file changed (168 → absent bytes)

− .git/objects/c8/5e4a4f82193fd374d9808476f8439ff61d79bc
@@ -1,3 +0,0 @@-x¥‘½-Â0F]›§¸c…ÒMÐÁAÁB-XÁ95W
4Iɏŷ7m©(R2¸…œ“/÷KªZU°X®fB1W#ìœEÝ Mó"Ý8ƈa-¦[jø¥ä·ÌI“xjH:=9y¸éˆ’¡¤Vs‹gúHH4.J?êÚ;j$„‹FiûÛü«\À2Ó¼¯4óbš
E'‡«¤ïiÖÐ?xb–˜¹ßÐ}ó2ó´“zÎ%€r6ãþw‡ó‰Þ¡Ýsüü‘Ñ…“j$Â,
− .git/objects/c8/66ed10fcb19f4a2fb6382721b1d617638ba958

binary file changed (9292 → absent bytes)

− .git/objects/cc/07b21458a8fc31fe26df3ebb0b95a3fb3dabda

binary file changed (244 → absent bytes)

− .git/objects/cd/023c5127ad0de2f22259fb58b303ab534b75b4

binary file changed (4551 → absent bytes)

+ .git/objects/cd/bcf48274b5ddfec1701e1002ccc9a53cfb5757 view

binary file changed (absent → 244 bytes)

− .git/objects/ce/7bb09495716bdbad1408f0d4969fdc39100142

binary file changed (244 → absent bytes)

− .git/objects/cf/2cd659a50d600cb0315d2897a67e8b19635ac1

binary file changed (6864 → absent bytes)

− .git/objects/cf/df673ee21dc78ee19e2f2c4403ff9c81ffa1f4

binary file changed (262 → absent bytes)

− .git/objects/d2/cf53ff238e7f97c925f41e114e7100ed259734

binary file changed (1055 → absent bytes)

− .git/objects/d3/9da1469f1f05215ea3284392a585730464e8de

binary file changed (1129 → absent bytes)

− .git/objects/db/b2c0d96968285e950f91cef5c54071d6e1c387

binary file changed (958 → absent bytes)

− .git/objects/df/9b55b1972552b0142c70dcd1ab0d1b80a6127d

binary file changed (244 → absent bytes)

− .git/objects/df/b22ee42cf26aa1d33211add3c6fdc1a8d117be

binary file changed (13625 → absent bytes)

− .git/objects/e1/a838d747ab8dcfe9e50965ef0ca4bf7c7a3779
@@ -1,2 +0,0 @@-xŽQ-Â0DýÎ)öÊv7¦]ñÃx„$»bÓÚ¦‚··z†y&G_<oêdԘO]›(¨o›ÖRöØ2{aRFÚ§F͍q²R!rHÈIƒvšIPHÔsNQ®W¸æ:š‹K½
œ‡òŽpYú|‡ƒ~áõ<«sŽ£íÖr„ƇIlqèòïcµÿlw¶ÑŠZÉoxÙ4÷CeÔXmvìÙN~
− .git/objects/e2/bdf760d4a28556c3c8bdcec92c5cb51c376e8a

binary file changed (3615 → absent bytes)

− .git/objects/e4/61c0ab2477605902ab7a9c42969603f38bd586
@@ -1,2 +0,0 @@-xÎM-Â0†a×9Å\@Ió;½€G˜dF,Ò¤ÖTðöVàîû¼¹NÓØÀXܵERdƒ™ˆ
¹„mé{Ò&dJÆ9¨™)
¼‹œÙR2è"mBp>hí­Ö¡—>nþªhm·ºÀP˛ಎùGþž×ã\¤=3ÍrØÆ	:§­‡½vZ«üklòŸVCÍë´µRkufjÂêDM\
− .git/objects/e5/c124001e0bd84a9c3f6f42ee520c39fcba32cc

binary file changed (23186 → absent bytes)

− .git/objects/eb/2a0d24372b9029567ea502f851b922edc3f462

binary file changed (4005 → absent bytes)

− .git/objects/eb/4dfc2eece871c74eeab502a3943f85884afdad

binary file changed (28085 → absent bytes)

− .git/objects/ed/8ca5964cb76d44755cdcb3ed22fd070676ebc7

binary file changed (356 → absent bytes)

+ .git/objects/ef/06402d714aa53dae0ba9c74b68d8b4ddea1329 view
@@ -0,0 +1,3 @@+x•ޱNÄ0D©ý[BAd'qKAʼn‚/X¯×$RbۗÓý=ÉÁPÍJ3³ó(.ËT éä]IÌ@Βo‡ºo­vÎ3©^*VRÖDdP7ä­îu/VL+ԍ•ž;e|ܪºaoúÎ9S-)EJšžË¸®O7Ù¾_—L¸rµÏ Z£»Fk9À£ÔR+ºÑþoO¼1®°qÊS@#†/v•§˜ÓAþçfˆÆ#âpžã…Üÿv2”§ÉMïÕ<fÀà¡Å¯š8?TðY^¡ =ÆÒŽœ÷¡%nû3Ÿâ¯g7Åj̕øKÊxR
− .git/objects/f0/716627a25c33e08df74920306b4fe6f446ac00

binary file changed (1278 → absent bytes)

− .git/objects/f2/2b65e167b5ea54b84305158d31368b9c10f0a7

binary file changed (7384 → absent bytes)

− .git/objects/f2/bba23a1c3bbf6ff9964d90731485399a69b8a9

binary file changed (15403 → absent bytes)

+ .git/objects/f5/06729c57606b06dadd36d037346bbaf4cd92bf view

binary file changed (absent → 136 bytes)

− .git/objects/f5/602af44f15d195f373445ff4ea9525fe659cdc

binary file changed (423 → absent bytes)

− .git/objects/fd/70ab6de3c28ef42e6450fabade4e4bbdd5869e

binary file changed (21394 → absent bytes)

− .git/objects/fd/7be762cd1a7bbca6b0eeec10ab260858db340d

binary file changed (172 → absent bytes)

− .git/objects/fd/f2171184f9e6cf6d1dad23f970d6fb284ede26
@@ -1,1 +0,0 @@-xŽ[J1EýÎ*j•G§™
¸„¤ª262é˜NöîýpþÝÃáÀ¥õz]˜9<Œ.6RN3êR¼Fã]öŽ)´A&ÄZµÔ¥pÈ>‹6,%²ÓÌÅ8Fg3["]橄¨Ò>Þ×çµ	Þö…>à‰àöùRel”šœîã´ó1ºhà'DE¿‡ü¯VgiRY*p“¾-k…½q¢Ôë1d}©—?ÕS½ÈWK÷†OêÒÙ[f
− .git/objects/fe/3a486f7a94b12d196d8e17a1fdb728e26dbff3

binary file changed (59 → absent bytes)

− .git/objects/pack/pack-070189755adac644d6a1c3fd2d044e268ceae11e.idx

binary file changed (4488 → absent bytes)

− .git/objects/pack/pack-070189755adac644d6a1c3fd2d044e268ceae11e.pack

binary file changed (76560 → absent bytes)

+ .git/objects/pack/pack-9b722e175e2c2aebda7ff55a097590198546bb01.idx view

binary file changed (absent → 8212 bytes)

+ .git/objects/pack/pack-9b722e175e2c2aebda7ff55a097590198546bb01.pack view

file too large to diff

.git/packed-refs view
@@ -1,2 +1,2 @@ # pack-refs with: peeled fully-peeled -61d09a71180e5a1a4ea1d893f82a4709d8e79c27 refs/remotes/origin/master+23b0fe619f7f91be4123ef976dd9284c11c1093a refs/remotes/origin/master
.git/refs/heads/master view
@@ -1,1 +1,1 @@-e1a838d747ab8dcfe9e50965ef0ca4bf7c7a3779+ef06402d714aa53dae0ba9c74b68d8b4ddea1329
.git/refs/remotes/origin/master view
@@ -1,1 +1,1 @@-e1a838d747ab8dcfe9e50965ef0ca4bf7c7a3779+ef06402d714aa53dae0ba9c74b68d8b4ddea1329
Euterpea.cabal view
@@ -1,5 +1,5 @@ name:           Euterpea
-version:        2.0.2
+version:        2.0.3
 Cabal-Version:  >= 1.8
 license:        BSD3
 license-file:	License
@@ -49,6 +49,7 @@         Euterpea.IO.MIDI.MEvent,
         Euterpea.IO.MIDI.MidiIO, 
         Euterpea.IO.MIDI.FromMidi,
+        Euterpea.IO.MIDI.FromMidi2,
         Euterpea.IO.MIDI.GeneralMidi,
         Euterpea.IO.MIDI.ToMidi,
         Euterpea.IO.MIDI.Play,
@@ -66,5 +67,5 @@         stm==2.4.2, 
         containers>=0.5.5.1 && <=0.5.7.1, 
         bytestring>=0.10.4.0 && <= 0.10.9,
-        heap == 0.6.0, 
+        heap >= 1.0 && < 2.0, 
         ghc-prim
Euterpea/IO/Audio.hs view
@@ -5,7 +5,7 @@     module Euterpea.IO.Audio.IO,
     module Euterpea.IO.Audio.Render,
     writeWav,
-	writeWavNorm
+    writeWavNorm
   ) where
 
 import Euterpea.IO.Audio.BasicSigFuns
@@ -17,7 +17,7 @@ writeWav fname iMap m = 
     let (d,s) = renderSF m iMap
     in  outFile fname d s
-	
+
 writeWavNorm fname iMap m = 
     let (d,s) = renderSF m iMap
     in  outFileNorm fname d s
+ Euterpea/IO/MIDI/FromMidi2.lhs view
@@ -0,0 +1,249 @@+FromMidi2: an alternative Midi-to-Music conversion algorithm.
+Author: Donya Quick
+Last modified: 28-Dec-2016
+
+The goal of this module is to provide a more intelligent
+parse from MIDI files to Music structures. The fromMidi
+function will convert Midi into Music, but the resulting
+structure is one big parallel composition with no other 
+relationships between the notes. The fromMidi2 function
+here is an attempt to provide a parse of musical features
+that is more in line with how a human might write them
+in Euterpea or perceive them by ear. It works best on 
+MIDI files that are very close to paper score in terms 
+of how the events are structured. The functions here are
+not intended for use with "messy" MIDI files that have 
+been recorded from a live performance without quantization.
+
+You can use fromMidi2 as an alternative to fromMidi to 
+parse a Midi value into a Music value with a better 
+method of grouping events together. The same algorithm 
+can be applied directly to a Music value with the 
+restructure function. 
+
+Examples of how to use fromMidi2 and restructure:
+
+testMidi file = do
+    x <- importFile file
+    case x of Left err -> error err
+              Right m -> do
+                  let v = fromMidi2 m
+                  putStrLn $ show v
+                  play v
+                  
+myMusic :: Music (Pitch, Volume)                  
+myMusic = ...
+newMusic :: Music (Pitch, Volume)
+newMusic = restructure myMusic
+
+Restructuring is done from the MEvent level. Importantly, 
+this means that there are no tempo changes or other Modify 
+nodes in the resulting Music value! A global tempo of 
+120BPM is assumed. If your MIDI file has a different BPM,
+you can use fromMidi in combination with restructure and
+then apply a tempo modifier afterwards.
+
+The method for organizing events is:
+(1) Identify and group chords where every note 
+    has the same start time and duration.
+(2) Identify and group sequential patterns where items
+    are back-to-back. Note that this may include a mix of
+    single notes and chords from step 1.
+(3) Greedily group any patterns with gaps between 
+    them into a sequence with rests.
+
+
+> module Euterpea.IO.MIDI.FromMidi2 (fromMidi2, restructure, Chunk, chunkEvents, chunkToMusic)where
+> import Euterpea.Music hiding (E)
+> import Euterpea.IO.MIDI.ToMidi
+> import Euterpea.IO.MIDI.GeneralMidi
+> import Euterpea.IO.MIDI.MEvent
+> import Euterpea.IO.MIDI.FromMidi
+> import Data.List
+> import Codec.Midi
+
+The primary exported functions for this module are:
+
+> fromMidi2 :: Midi -> Music (Pitch, Volume)
+> fromMidi2 = restructure . fromMidi
+
+> restructure :: (ToMusic1 a) => Music a -> Music (Pitch, Volume)
+> restructure = parseFeaturesI
+
+Other exported features are related to the Chunk datatype.
+
+A Chunk is the data structure used to group events by the algorithm 
+described at the top of this file. Par and Chord correspond to features
+that will be composed in parallel (:=:) at different levels, and Seq 
+corresponds to features that will be composed in sequence (:+:). E is 
+a wrapper for single events and R is a rest place-holder.
+
+> type Onset = Dur -- to clarify some type signatures
+
+> data Chunk = Par [Chunk] | Seq [Chunk] | Chord [Chunk] | E MEvent | R Onset Dur
+>     deriving Eq
+
+Initially, each MEvent is placed in its own chunk.
+
+> initChunk :: [MEvent] -> [Chunk]
+> initChunk mevs = 
+>     let mevs' = sortBy sortFun mevs
+>     in  map E mevs'
+
+The chunkChord function looks for chunks that share the same
+onset and duration and places them together in Chord chunks.
+
+> chunkChord :: [Chunk] -> [Chunk]
+> chunkChord [] = []
+> chunkChord (c:cs) = 
+>     let cChord = filter (chordWith c) cs
+>         notInChord = filter (\v -> not $ elem v cChord) cs
+>     in  if null cChord then c : chunkChord cs
+>         else Chord (c:cChord) : chunkChord notInChord
+
+> chordWith :: Chunk -> Chunk -> Bool
+> chordWith c0 c = chunkOnset c == chunkOnset c0 && chunkDur c == chunkDur c0
+
+The chunkMel function looks for sequences of chunks (which need
+not be adjacent in the input list) where the end time of one chunk
+is equal to the start time of the next chunk. There are no gaps 
+permitted, so notes separated by rests will not be grouped here.
+
+> chunkMel :: [Chunk] -> [Chunk]
+> chunkMel [] = []
+> chunkMel x@(c:cs) = 
+>     let cMel = buildMelFrom (chunkOnset c) x -- get ALL possible melody elements
+>         notInMel = filter (\v -> not $ elem v cMel) x
+>     in  if null cMel then c : chunkMel cs
+>         else Seq cMel : chunkMel notInMel
+
+> buildMelFrom :: Onset -> [Chunk] -> [Chunk]
+> buildMelFrom t [] = []
+> buildMelFrom t (c:cs) = 
+>     if chunkOnset c == t then c : buildMelFrom (t + chunkDur c) cs
+>     else buildMelFrom t cs
+
+The chunkSeqs function is more general and will look for anything 
+that can be grouped together linearly in time, even if it requires
+inserting a rest. This will group together all non-overlapping 
+chunks in a greedy fashion.
+
+> chunkSeqs :: [Chunk] -> [Chunk]
+> chunkSeqs [] = []
+> chunkSeqs x@(c:cs) = 
+>     let s = seqWithRests (chunkOnset c) x
+>         notInS = filter (\v -> not $ elem v s) x
+>     in  if s == [c] then c : chunkSeqs cs 
+>         else Seq s : chunkSeqs notInS
+
+> seqWithRests :: Onset -> [Chunk] -> [Chunk]
+> seqWithRests t [] = []
+> seqWithRests t x@(c:cs) = 
+>     let tc = chunkOnset c
+>         dt = tc - t
+>     in  if dt == 0 then c : seqWithRests (tc + chunkDur c) cs
+>         else if dt > 0 then R t dt : c : seqWithRests (tc + chunkDur c) cs
+>         else seqWithRests t cs
+
+Finally, chunkEvents combines all of these methods in a particular
+order that establishes preference for chords first, then melodies
+(which may include chords), and then sequences including rests. 
+Anything left over will be handled by an outer Par.
+
+> chunkEvents :: [MEvent] -> Chunk
+> chunkEvents = Par . chunkSeqs . chunkMel . chunkChord. initChunk
+
+Chunks can be converted directly to Music. Durations have to be 
+divided in half because MEvents deal with seconds, while Music 
+deals with duration as whole notes (1 whole note = 2 seconds).
+
+> chunkToMusic :: Chunk -> Music (Pitch, Volume)
+> chunkToMusic (E e) = note (eDur e / 2) (pitch $ ePitch e, eVol e)
+> chunkToMusic (R o d) = rest (d/2)
+> chunkToMusic (Seq x) = line(map chunkToMusic x)
+> chunkToMusic (Chord x) = chord(map chunkToMusic x)
+> chunkToMusic (Par x) = chord $ map (\v -> rest (chunkOnset v / 2) :+: chunkToMusic v) x
+
+The parseFeatures function will take an existing Music value, such
+as one returned by fromMidi, and use the algorithms above to identify
+musical features (chords and melodies) and construct a new Music 
+tree that is performance-equivalent to the original.
+
+> parseFeatures :: (ToMusic1 a) => Music a -> Music (Pitch, Volume)
+> parseFeatures = removeZeros . chunkToMusic . chunkEvents . perform
+
+> parseFeaturesI :: (ToMusic1 a) => Music a -> Music (Pitch, Volume)
+> parseFeaturesI m = 
+>     let mevs = perform m
+>         (iList, mevsI) = unzip $ splitByInst mevs
+>         parsesI = map (removeZeros . chunkToMusic . chunkEvents) mevsI
+>     in  chord $ zipWith instrument iList parsesI
+
+================
+
+Utility Functions and Type Class Instances
+
+First, some functions to pretty-up printing of things for debugging purposes
+
+> doubleShow :: Rational -> String
+> doubleShow x = show (fromRational x :: Double)
+
+> pcShow :: AbsPitch -> String
+> pcShow = show . fst . pitch
+
+> listShow, listShowN :: (Show a) => [a] -> String
+> listShow x = "["++(concat $ intersperse ", " $ map show x)++"]"
+> listShowN x = "[\n    "++(concat $ intersperse ",\n    " $ map show x)++"\n]"
+
+> listShowX :: (Show a) => Int -> [a] -> String
+> listShowX i x = let v = concat (take i (repeat " ")) in
+>     "[\n"++v++(concat $ intersperse (",\n"++v) $ map show x)++"\n"++v++"]"
+
+> instance Show Chunk where
+>     show (E e) = "E "++doubleShow (eTime e)++" "++pcShow (ePitch e)++" "++doubleShow (eDur e)
+>     show s@(Seq x) = "S "++doubleShow (chunkOnset s)++" "++listShowX 4 x
+>     show c@(Chord x) = "C "++doubleShow (chunkOnset c)++" "++listShowX 6 x
+>     show p@(Par x) = "P "++doubleShow (chunkOnset p)++" "++listShowX 2 x
+>     show (R o d) = "R "++doubleShow o++" "++doubleShow d
+
+An Ord instance for Chunk that enforces sorting based on onset time. No
+other features are considered.
+
+> instance Ord Chunk where
+>     compare x1 x2 = compare (chunkOnset x1) (chunkOnset x2)
+
+Functions to determine the start time (onset) and duration of a Chunk.
+
+> chunkOnset :: Chunk -> Onset
+> chunkOnset (Seq x) = if null x then error "Empty Seq!" else chunkOnset (head x)
+> chunkOnset (Chord x) = if null x then error "Empty Chord!" else chunkOnset (head x)
+> chunkOnset (Par x) = if null x then 0 else minimum $ map chunkOnset x
+> chunkOnset (E e) = eTime e
+> chunkOnset (R o d) = o
+
+> chunkEnd :: Chunk -> Onset
+> chunkEnd (Seq x) = if null x then error "Empty Seq!" else chunkEnd (last x)
+> chunkEnd (Chord x) = if null x then error "Empty Chord!" else chunkEnd (head x)
+> chunkEnd (Par x) = if null x then 0 else maximum $ map chunkEnd x
+> chunkEnd (E e) = eTime e + eDur e
+> chunkEnd (R o d) = o + d
+
+> chunkDur :: Chunk -> Dur
+> chunkDur (Seq x) = if null x then error "Empty Seq!" else sum $ map chunkDur x
+> chunkDur (Chord x) = if null x then error "Empty Chord!" else chunkDur (head x)
+> chunkDur c@(Par x) = if null x then 0 else 
+>     let o = chunkOnset c
+>         e = chunkEnd c
+>     in  e-o
+> chunkDur (E e) = eDur e
+> chunkDur (R o d) = d
+
+Special sorting function for MEvents.
+
+> sortFun :: MEvent -> MEvent -> Ordering
+> sortFun e1 e2 = 
+>     if eTime e1 == eTime e2 then compare (ePitch e1) (ePitch e2)
+>     else compare (eTime e1) (eTime e2)
+
+
+
Euterpea/IO/MIDI/MidiIO.lhs view
@@ -1,4 +1,3 @@-
 > {-# LANGUAGE GeneralizedNewtypeDeriving #-}
 > module Euterpea.IO.MIDI.MidiIO (
 >   getAllDevices, --isValidInputDevice, isValidOutputDevice, -- Used only by Euterpea.IO.MUI.MidiWidgets
@@ -6,22 +5,22 @@ >   outputMidi, deliverMidiEvent, -- Used only by Euterpea.IO.MUI.MidiWidgets (particularly by midiOut)
 >   pollMidi, -- Used only by Euterpea.IO.MUI.MidiWidgets (particularly by midiIn)
 >   defaultOutput, defaultInput,
->   playMidi, 
->   MidiMessage (ANote, Std), 
+>   playMidi,
+>   MidiMessage (ANote, Std),
 >   getTimeNow,
 >   DeviceInfo(..), InputDeviceID, OutputDeviceID, Message(..), Time,
 >   unsafeInputID, unsafeOutputID,
 > ) where
 
-> import Codec.Midi (Time, Channel, Key, Velocity, 
->                    Message (..), Midi (..), Track, 
+> import Codec.Midi (Time, Channel, Key, Velocity,
+>                    Message (..), Midi (..), Track,
 >                    toRealTime, toAbsTime, toSingleTrack, isTrackEnd)
-> import Sound.PortMidi (DeviceInfo (..), getDeviceInfo, 
->                        DeviceID, countDevices, time, 
->                        getDefaultOutputDeviceID, getDefaultInputDeviceID, 
->                        openInput, openOutput, readEvents, 
->                        close, writeShort, getErrorText, terminate, initialize, 
->                        PMError (NoError, BufferOverflow), PMStream, 
+> import Sound.PortMidi (DeviceInfo (..), getDeviceInfo,
+>                        DeviceID, countDevices, time,
+>                        getDefaultOutputDeviceID, getDefaultInputDeviceID,
+>                        openInput, openOutput, readEvents,
+>                        close, writeShort, getErrorText, terminate, initialize,
+>                        PMError (NoError, BufferOverflow), PMStream,
 >                        PMEvent (..), PMMsg (PMMsg))
 > import Control.Exception (finally)
 > import Control.Concurrent
@@ -40,7 +39,7 @@ 
 
 ----------------------------
- | Midi Type declarations | 
+ | Midi Type declarations |
 ----------------------------
 
 > type MidiEvent = (Time, MidiMessage)
@@ -62,20 +61,20 @@ > unsafeOutputID = OutputDeviceID
 
 ----------
- | Time | 
+ | Time |
 ----------
 
-Is this the time we want?  This comes from PortMidi, but there's also the 
+Is this the time we want?  This comes from PortMidi, but there's also the
 function FRP.UISF.SOE.timeGetTime which uses time data from GLFW.
 
-> getTimeNow :: IO Time 
+> getTimeNow :: IO Time
 > getTimeNow = do
 >   t <- time
 >   return (fromIntegral t / 1000)
 
 
 ----------------------
- | Device Functions | 
+ | Device Functions |
 ----------------------
 
 getAllDevices returns a list of all of the DeviceInfos found.
@@ -86,11 +85,11 @@ >   n <- countDevices
 >   deviceInfos <- mapM getDeviceInfo [0..n-1]
 >   let devs = zip [0..n-1] deviceInfos
->   return ([ (InputDeviceID  d, i) | (d,i) <- devs, input  i], 
+>   return ([ (InputDeviceID  d, i) | (d,i) <- devs, input  i],
 >           [ (OutputDeviceID d, i) | (d,i) <- devs, output i])
 
 
-isValidInputDevice and isValideOutputDevice check whether the given 
+isValidInputDevice and isValideOutputDevice check whether the given
 devices are respectively valid for input or output.
 
 isValidInputDevice, isValidOutputDevice :: DeviceID -> IO Bool
@@ -98,21 +97,21 @@ isValidOutputDevice = isValidDevice output
 isValidDevice :: (DeviceInfo -> Bool) -> DeviceID -> IO Bool
 isValidDevice pred i = do
-  n <- countDevices   
+  n <- countDevices
   info <- getAllDevices
-  return $ 
+  return $
     i >= 0 && i < n && pred (snd $ info !! i)
 
 
 ---------------------
- | Default devices | 
+ | Default devices |
 ---------------------
 
-Rather than export the deviceIDs directly, these two functions allow 
+Rather than export the deviceIDs directly, these two functions allow
 the caller to use the DeviceID without directly controlling it.
 
-They take a function (such as playMidi) and an auxiary argument and 
-apply them together with the default device.  If no default device 
+They take a function (such as playMidi) and an auxiary argument and
+apply them together with the default device.  If no default device
 exists, an error is thrown.
 
 > defaultOutput :: (OutputDeviceID -> a -> IO b) -> a -> IO b
@@ -121,7 +120,7 @@ >   case i of
 >     Nothing -> error "No MIDI output device found"
 >     Just i  -> f (OutputDeviceID i) a
-> 
+>
 > defaultInput :: (InputDeviceID -> a -> IO b) -> a -> IO b
 > defaultInput f a = do
 >   i <- getDefaultInputDeviceID
@@ -131,10 +130,10 @@ 
 
 -----------------------
- | Priority Channels | 
+ | Priority Channels |
 -----------------------
 
-The priority channel data type and a constructor for it will be used 
+The priority channel data type and a constructor for it will be used
 by devices.  We define them here.
 
 > data PrioChannel a b = PrioChannel
@@ -150,41 +149,39 @@ >       push a b = modifyIORef heapRef (Heap.insert (a,b))
 >       pop = do
 >         h <- get
->         let (a, h') = Heap.extractHead h
+>         let Just (a, h') = Heap.view h
 >         modifyIORef heapRef (\_ -> h')
 >         return a
 >       peek = do
 >         h <- get
->         if Heap.isEmpty h 
->           then return Nothing 
->           else return $ Just $ Heap.head h
->         
+>         return $ Heap.viewHead h
+>
 >   return $ PrioChannel get push pop peek
 
 
 ------------------------
- | Global Device Data | 
+ | Global Device Data |
 ------------------------
 
 We keep a mapping from DeviceID to the priority channel for keeping
-track of future MIDI messages, an output function to produce sound, 
-and a stop function.  This mapping is stored in the global ref 
-outDevMap, and it is accessed by getOutDev (which looks up info 
-and adds associations if necessary) and terminateMidi (which calls 
+track of future MIDI messages, an output function to produce sound,
+and a stop function.  This mapping is stored in the global ref
+outDevMap, and it is accessed by getOutDev (which looks up info
+and adds associations if necessary) and terminateMidi (which calls
 the stop function on all elements and clears the mapping).
 
 outDevMap is the global mapping.
 
-> outDevMap :: IORef [(OutputDeviceID, 
+> outDevMap :: IORef [(OutputDeviceID,
 >                      (PrioChannel Time Message, -- priority channel
 >                       (Time, Message) -> IO (), -- sound output function
 >                       IO ()))]                  -- stop/terminate function
 > outDevMap = unsafePerformIO $ newIORef []
 
 
-outPort and inPort are global memory refs that contain a mapping of 
-DeviceID to Port Midi Streams.  They are modified with addPort (which 
-adds a new mapping to the list) and lookupPort (which, given a DeviceID, 
+outPort and inPort are global memory refs that contain a mapping of
+DeviceID to Port Midi Streams.  They are modified with addPort (which
+adds a new mapping to the list) and lookupPort (which, given a DeviceID,
 returns the Port Midi Stream associated with it).
 
 > outPort :: IORef [(OutputDeviceID, PMStream)]
@@ -200,7 +197,7 @@ 
 
 --------------------------------------------------
- | Global Device Initialization and Termination | 
+ | Global Device Initialization and Termination |
 --------------------------------------------------
 
 initializeMidi just initializes PortMidi
@@ -208,11 +205,11 @@ > initializeMidi :: IO ()
 > initializeMidi = do
 >   e <- initialize
->   if e == NoError 
->       then return () 
+>   if e == NoError
+>       then return ()
 >       else reportError "initializeMidi" e
 
-terminateMidi calls the stop function on all elements of outDevMap 
+terminateMidi calls the stop function on all elements of outDevMap
 and clears the mapping entirely.  It also clears outPort and inPort.
 
 > terminateMidi :: IO ()
@@ -226,10 +223,10 @@ 
 
 -------------------
- | Device Lookup | 
+ | Device Lookup |
 -------------------
 
-getOutDev looks up info in outDevMap and adds associations if necessary.  
+getOutDev looks up info in outDevMap and adds associations if necessary.
 It is accessed as a helper function for outputMidi and deliverMidiEvent.
 
 > getOutDev :: OutputDeviceID -> IO (PrioChannel Time Message, (Time, Message) -> IO (), IO ())
@@ -247,31 +244,31 @@ 
 
 ----------------
- | Midi Input | 
+ | Midi Input |
 ----------------
 
-pollMidi take an input device and a callback function and polls the device 
-for midi events.  Any events are sent, along with the current time, to 
+pollMidi take an input device and a callback function and polls the device
+for midi events.  Any events are sent, along with the current time, to
 the callback function.
 DWC NOTE: Why is the time even used?  All messages get the same time?
 
 > pollMidiCB :: InputDeviceID -> ((Time, [Message]) -> IO ()) -> IO ()
 > pollMidiCB idid@(InputDeviceID devId) callback = do
->   s <- lookupPort inPort idid 
+>   s <- lookupPort inPort idid
 >   case s of
 >     Nothing -> do
->       r <- openInput devId 
+>       r <- openInput devId
 >       case r of
 >         Right e -> reportError "pollMidiCB" e
 >         Left s -> addPort inPort (idid, s) >> input s
->     Just s -> input s 
+>     Just s -> input s
 >   where
 >     input :: PMStream -> IO ()
 >     input s = do
 >       e <- readEvents s
 >       case e of
->         Right e -> if e == NoError 
->           then return () 
+>         Right e -> if e == NoError
+>           then return ()
 >           else reportError "pollMidiCB" e
 >         Left l -> do
 >           now <- getTimeNow
@@ -281,20 +278,20 @@ 
 > pollMidi :: InputDeviceID -> IO (Maybe (Time, [Message]))
 > pollMidi idid@(InputDeviceID devId) = do
->   s <- lookupPort inPort idid 
+>   s <- lookupPort inPort idid
 >   case s of
 >     Nothing -> do
->       r <- openInput devId 
+>       r <- openInput devId
 >       case r of
 >         Right e -> reportError "pollMIDI" e >> return Nothing
 >         Left s -> addPort inPort (idid, s) >> input s
->     Just s -> input s 
+>     Just s -> input s
 >   where
 >     input :: PMStream -> IO (Maybe (Time, [Message]))
 >     input s = do
 >       e <- readEvents s
 >       case e of
->         Right e -> if e == NoError 
+>         Right e -> if e == NoError
 >           then return Nothing
 >           else reportError "pollMIDI" e >> return Nothing
 >         Left l -> do
@@ -305,19 +302,19 @@ 
 
 ---------------------------------------------
- | Midi Output for inidividual Midi events | 
+ | Midi Output for inidividual Midi events |
 ---------------------------------------------
 
-The following two functions are for sending and playing individual 
-Midi events to devices.  Typically, usage will be to call outputMidi 
-to play anything that's ready to play and then send in the latest 
-messages with deliverMidiEvent.  Of course, if no new messages are 
-ready to be delivered, that step can be omitted.  Either way, 
-outputMidi should be called many times per second to assure that 
+The following two functions are for sending and playing individual
+Midi events to devices.  Typically, usage will be to call outputMidi
+to play anything that's ready to play and then send in the latest
+messages with deliverMidiEvent.  Of course, if no new messages are
+ready to be delivered, that step can be omitted.  Either way,
+outputMidi should be called many times per second to assure that
 all Midi messages are played approximately when scheduled.
 
-deliverMidiEvent sends the given MidiEvent to the given device.  If 
-the event is scheduled to happen ``now'', then it is immediately 
+deliverMidiEvent sends the given MidiEvent to the given device.  If
+the event is scheduled to happen ``now'', then it is immediately
 played.  Otherwise, it is queued for later.
 
 > deliverMidiEvent :: OutputDeviceID -> MidiEvent -> IO ()
@@ -326,9 +323,9 @@ >   now <- getTimeNow
 >   let deliver t m = do
 >       if t == 0
->         then out (now,m) 
+>         then out (now,m)
 >         else push pChan (now+t) m
->              
+>
 >   case m of
 >     Std m -> deliver t m
 >     ANote c k v d -> do
@@ -336,7 +333,7 @@ >         deliver (t+d) (NoteOff c k v)
 
 
-outputMidi plays all midi events that are waiting in this device's 
+outputMidi plays all midi events that are waiting in this device's
 priority queue whose time to play has come.
 
 > outputMidi :: OutputDeviceID -> IO ()
@@ -348,7 +345,7 @@ >           Nothing     -> return ()
 >           Just (t,m)  -> do
 >             now <- getTimeNow
->             if t <= now 
+>             if t <= now
 >               then out (now, m) >> pop pChan >> loop
 >               else return ()
 >   loop
@@ -356,13 +353,13 @@ 
 
 -------------------------------------------
- | Midi Output for a complete Midi track | 
+ | Midi Output for a complete Midi track |
 -------------------------------------------
 
-When an entire Midi track is ready to be played, the playMidi function 
+When an entire Midi track is ready to be played, the playMidi function
 may be more appropriate than deliverMidiEvent and outputMidi.
 
-playMidi will queue up the entire Midi track given to it and then close 
+playMidi will queue up the entire Midi track given to it and then close
 the output device.
 
 > playMidi :: OutputDeviceID -> Midi -> IO ()
@@ -372,44 +369,44 @@ >   case out of
 >     Nothing -> return ()
 >     Just (out, stop) -> do
->       t0 <- getTimeNow 
+>       t0 <- getTimeNow
 >       finally (playTrack t0 0 out track) stop
 >   where
 >     playTrack t0 t' out [] = out (t0 + t', TrackEnd)
 >     playTrack t0 t' out (e@(t, m) : s) = do
->       out (t0 + t, m) 
->       if isTrackEnd m 
+>       out (t0 + t, m)
+>       if isTrackEnd m
 >         then return ()
 >         else playTrack t0 t out s
 
 
 ---------------------
- | midiOutRealTime | 
+ | midiOutRealTime |
 ---------------------
 
-The following two functions are used to open a device for Midi output.  
-They should only be called when the device hasn't yet been opened, and 
+The following two functions are used to open a device for Midi output.
+They should only be called when the device hasn't yet been opened, and
 they both return a ``play'' function and a ``stop'' function.
 
-Currently, midiOutRealTime' is used for Midi output for inidividual 
-Midi events, and midiOutRealTime is used for Midi output for a complete 
+Currently, midiOutRealTime' is used for Midi output for inidividual
+Midi events, and midiOutRealTime is used for Midi output for a complete
 Midi track.
 
 DWC Notes:
-I'm not entirely sure how they both work yet.  midiOutRealTime' 
-actually looks pretty straightforward in that it just creates the process 
-and stop functions and adds this device to the outPort device list.  The 
-process function will look up the device in the outPort device list, and 
-if it finds it, it writes the message to it.  The stop function removes 
+I'm not entirely sure how they both work yet.  midiOutRealTime'
+actually looks pretty straightforward in that it just creates the process
+and stop functions and adds this device to the outPort device list.  The
+process function will look up the device in the outPort device list, and
+if it finds it, it writes the message to it.  The stop function removes
 the device from the outPort list and closes it.
 
-On the other hand, midiOutRealTime spawns a new thread and does some 
-concurrent stuff.  Really, it looks similar, but I don't know when to 
+On the other hand, midiOutRealTime spawns a new thread and does some
+concurrent stuff.  Really, it looks similar, but I don't know when to
 use one and when to use the other.
 
 > midiOutRealTime' :: OutputDeviceID -> IO (Maybe ((Time, Message) -> IO (), IO ()))
 > midiOutRealTime' odid@(OutputDeviceID devId) = do
->   s <- openOutput devId 1  
+>   s <- openOutput devId 1
 >   case s of
 >     Right e -> reportError "Unable to open output device in midiOutRealTime'" e >> return Nothing
 >     Left s -> do
@@ -421,7 +418,7 @@ >       case s of
 >         Nothing -> error ("midiOutRealTime': port " ++ show odid ++ " is not open for output")
 >         Just s -> do
->           if isTrackEnd msg 
+>           if isTrackEnd msg
 >               then return ()
 >               else case midiEvent msg of
 >                 Just m  -> writeMsg s t m
@@ -429,23 +426,23 @@ >     writeMsg s t m = do
 >               e <- writeShort s (PMEvent m (round (t * 1e3)))
 >               case e of
->                 NoError -> return () 
+>                 NoError -> return ()
 >                 _ -> reportError "midiOutRealTime'" e
 >     finalize odid = do
 >       s <- lookupPort outPort odid
 >       e <- maybe (return NoError) close s
 >       case e of
->         NoError -> return () 
+>         NoError -> return ()
 >         _ -> reportError "midiOutRealTime'" e
 
 
 > midiOutRealTime :: OutputDeviceID -> IO (Maybe ((Time, Message) -> IO (), IO ()))
 > midiOutRealTime odid@(OutputDeviceID devId) = do
->   s <- openOutput devId 1  
+>   s <- openOutput devId 1
 >   case s of
 >     Right e -> reportError "outputMidi" e >> return Nothing
 >     Left s -> do
->       ch <- atomically newTChan 
+>       ch <- atomically newTChan
 >       wait <- newEmptyMVar
 >       fin <- newEmptyMVar
 >       forkIO (pump s ch wait fin)
@@ -457,50 +454,50 @@ >       if isTrackEnd m then takeMVar wait else return ()
 >     pump s ch wait fin = loop
 >       where
->         loop = do 
+>         loop = do
 >           e <- atomically $ readTChan ch
 >           case e of
 >             Nothing -> close s >> putMVar fin ()
 >             Just (t, msg) -> do
 >               now <- getTimeNow
->               if (t > now + 5) 
+>               if (t > now + 5)
 >                 then atomically (unGetTChan ch e) >> threadDelay 10000 >> loop
->                 else do 
+>                 else do
 >                   done <- process t msg
->                   if done 
+>                   if done
 >                     then waitUntil (t + 1)
->                     else loop 
+>                     else loop
 >           where
 >             waitUntil t = do
 >               now <- getTimeNow
->               if t > now 
+>               if t > now
 >                 then do
->                   threadDelay $ min 10000 (round((t - now) * 1E6)) 
+>                   threadDelay $ min 10000 (round((t - now) * 1E6))
 >                   empty <- atomically $ isEmptyTChan ch
->                   if empty 
+>                   if empty
 >                     then waitUntil t
 >                     else do
 >                       e <- atomically $ readTChan ch
 >                       case e of
->                         Nothing -> finishup 
+>                         Nothing -> finishup
 >                         _ -> waitUntil t
 >                 else finishup
 >             finishup = putMVar wait () >> close s >> putMVar fin ()
->             process t msg = if isTrackEnd msg 
->               then return True 
+>             process t msg = if isTrackEnd msg
+>               then return True
 >               else case midiEvent msg of
 >                 Just m  -> writeMsg t m
->                 Nothing -> return False 
+>                 Nothing -> return False
 >             writeMsg t m = do
 >               e <- writeShort s (PMEvent m (round (t * 1e3)))
 >               case e of
->                 NoError -> return False 
+>                 NoError -> return False
 >                 BufferOverflow -> putStrLn "overflow" >> threadDelay 10000 >> writeMsg t m
->                 _ -> reportError "outputMidi" e >> return True 
+>                 _ -> reportError "outputMidi" e >> return True
 
 
 ---------------------
- | MIDI Conversion | 
+ | MIDI Conversion |
 ---------------------
 
 A conversion function from Codec.Midi Messages to PortMidi PMMsgs.
@@ -514,13 +511,13 @@ > midiEvent (ChannelPressure c pr)  = Just $ PMMsg (208 .|. (fromIntegral c .&. 0xF)) (fromIntegral pr) 0
 > midiEvent (PitchWheel c pb)       = Just $ PMMsg (224 .|. (fromIntegral c .&. 0xF)) (fromIntegral lo) (fromIntegral hi)
 >  where (hi,lo) = (pb `shiftR` 8, pb .&. 0xFF)
-> midiEvent _ = Nothing 
+> midiEvent _ = Nothing
 
 
 A conversion function from PortMidi PMMsgs to Codec.Midi Messages.
 
 > msgToMidi :: PMMsg -> Maybe Message
-> msgToMidi (PMMsg m d1 d2) = 
+> msgToMidi (PMMsg m d1 d2) =
 >   let k = (m .&. 0xF0) `shiftR` 4
 >       c = fromIntegral (m .&. 0x0F)
 >   in case k of
@@ -536,12 +533,12 @@ 
 
 ---------------------
- | Error Reporting | 
+ | Error Reporting |
 ---------------------
 
 > reportError :: String -> PMError -> IO ()
 > reportError prompt e = do
->   err <- getErrorText e 
+>   err <- getErrorText e
 >   hPutStrLn stderr $ prompt ++ ": " ++  err
 
 
@@ -549,7 +546,7 @@ 
 
 ----------------------
- | Unused Functions | 
+ | Unused Functions |
 ----------------------
 
 > -- Prints all DeviceInfo found by getAllDevices.
@@ -559,12 +556,12 @@ >   mapM_ (print . snd) indevs
 >   mapM_ (print . snd) outdevs
 
--- Given whether the device is an input device and the device name, 
+-- Given whether the device is an input device and the device name,
 -- returns the DeviceID.
-getDeviceId :: Bool -> String -> IO (Maybe DeviceID)
-getDeviceId isInput n = do
-  devs <- getAllDevices
-  return $ findIndex (\(_,d) -> name d == n && input d == isInput) devs
+ getDeviceId :: Bool -> String -> IO (Maybe DeviceID)
+ getDeviceId isInput n = do
+   devs <- getAllDevices
+   return $ findIndex (\(_,d) -> name d == n && input d == isInput) devs
 
 > playTrackRealTime :: OutputDeviceID -> [(t, Message)] -> IO ()
 > playTrackRealTime device track = do
@@ -578,59 +575,55 @@ >       out (t, TrackEnd)
 >     playTrack out (e@(_, m) : s) = do
 >       t <- getTimeNow
->       out (t, m) 
->       if isTrackEnd m 
+>       out (t, m)
+>       if isTrackEnd m
 >         then return ()
 >         else playTrack out s
-
 > {-
 >     ticksPerBeat = case division of
 >       TicksPerBeat n -> n
->       TicksPerSecond mode nticks -> (256 - mode - 128) * nticks `div` 2 
+>       TicksPerSecond mode nticks -> (256 - mode - 128) * nticks `div` 2
 > -}
-
 > {-
 > runTrack tpb = runTrack' 0 0 120                 -- 120 beat/s is the default tempo
 >   where
->     runTrack' t t0 bps ((_, TempoChange tempo) : l) = 
->       let bps' = 1000000 `div` fromIntegral tempo  
+>     runTrack' t t0 bps ((_, TempoChange tempo) : l) =
+>       let bps' = 1000000 `div` fromIntegral tempo
 >       in runTrack' t t0 bps' l
->     runTrack' t t0 bps ((t1, m) : l) = 
+>     runTrack' t t0 bps ((t1, m) : l) =
 >       let t' = t + 1000 * fromIntegral (t1 - t0) `div` (tpb * bps)
 >       in (t', m) : runTrack' t' t1 bps l
->     runTrack' _ _ _ [] = [] 
+>     runTrack' _ _ _ [] = []
 >
 > playTrack s ch t0 = playTrack' 0
 >   where
 >     playTrack' t [] = putStrLn "done" >> putMVar ch Nothing >> return (round (t * 1.0E3))
->     playTrack' _ ((t, e):es) = putMVar ch (Just io) >> playTrack' t es 
->       where 
+>     playTrack' _ ((t, e):es) = putMVar ch (Just io) >> playTrack' t es
+>       where
 >         io = case midiEvent e of
 >           Just m  -> writeShort s (PMEvent m (t0 + round (t * 1.0E3)))
->           Nothing -> return NoError 
+>           Nothing -> return NoError
 > -}
-
 > recordMidi :: DeviceID -> (Track Time -> IO ()) -> IO ()
 > recordMidi device f = do
 >   ch <- newChan
 >   final <- midiInRealTime device (\e -> writeChan ch e >> return False)
->   case final of 
->     Nothing  -> return () 
+>   case final of
+>     Nothing  -> return ()
 >     Just fin -> do
 >       track <- getChanContents ch
->       done <- newEmptyMVar 
->       forkIO (f track >> putMVar done ())  
+>       done <- newEmptyMVar
+>       forkIO (f track >> putMVar done ())
 >       putStrLn "Start recording, hit ENTER when you are done."
 >       getLine
->       fin 
+>       fin
 >       takeMVar done
 >       return ()
-
 > midiInRealTime :: DeviceID -> ((Time, Message) -> IO Bool) -> IO (Maybe (IO ()))
 > midiInRealTime device callback = do
->   r <- openInput device 
+>   r <- openInput device
 >   case r of
->     Right e -> reportError "midiInRealTime" e >> return Nothing 
+>     Right e -> reportError "midiInRealTime" e >> return Nothing
 >     Left s -> do
 >       fin <- newEmptyMVar
 >       forkIO (loop Nothing s fin)
@@ -644,16 +637,16 @@ >         Nothing -> do
 >           e <- readEvents s
 >           case e of
->             Right e -> if e == NoError 
+>             Right e -> if e == NoError
 >               then threadDelay 1000 >> loop start s fin
 >               else do
->                 reportError "midiInRealTime" e 
+>                 reportError "midiInRealTime" e
 >                 callback (t, TrackEnd)
 >                 return ()
 >             Left l -> do
 >               t <- getTimeNow
 >               sendEvts start t l
->       where 
+>       where
 >         sendEvts start now [] = loop start s fin
 >         sendEvts start now (e@(PMEvent m t):l) = do
 >           let t0 = maybe t id start
ReadMe.txt view
@@ -1,5 +1,5 @@-Version 2.0.2
-Last modified: 09-July-2016
+Version 2.0.3
+Last modified: 27-Dec-2016
  _____      _                             
 |  ___|    | |                            
 | |__ _   _| |_ ___ _ __ _ __   ___  __ _