packages feed

Euterpea 2.0.0 → 2.0.1

raw patch · 227 files changed

+1802/−1853 lines, 227 filessetup-changedbinary-addedPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

+ Euterpea.IO.MIDI.MEvent: applyControls :: Music1 -> Music1
- Euterpea.IO.Audio: writeWav :: (AudioSample a1, Clock p, ToMusic1 a) => String -> InstrMap (Signal p () a1) -> Music a -> IO ()
+ Euterpea.IO.Audio: writeWav :: (ToMusic1 a1, Clock p, AudioSample a) => String -> InstrMap (Signal p () a) -> Music a1 -> IO ()
- Euterpea.IO.Audio: writeWavNorm :: (AudioSample a1, Clock p, ToMusic1 a) => String -> InstrMap (Signal p () a1) -> Music a -> IO ()
+ Euterpea.IO.Audio: writeWavNorm :: (ToMusic1 a1, Clock p, AudioSample a) => String -> InstrMap (Signal p () a) -> Music a1 -> IO ()
- Euterpea.IO.Audio.BasicSigFuns: balance :: Clock p => Int -> Signal p (Double, Double) Double
+ Euterpea.IO.Audio.BasicSigFuns: balance :: forall p. Clock p => Int -> Signal p (Double, Double) Double
- Euterpea.IO.Audio.BasicSigFuns: delayLine :: Clock p => Double -> Signal p Double Double
+ Euterpea.IO.Audio.BasicSigFuns: delayLine :: forall p. Clock p => Double -> Signal p Double Double
- Euterpea.IO.Audio.BasicSigFuns: delayLine1 :: Clock p => Double -> Signal p (Double, Double) Double
+ Euterpea.IO.Audio.BasicSigFuns: delayLine1 :: forall p. Clock p => Double -> Signal p (Double, Double) Double
- Euterpea.IO.Audio.BasicSigFuns: delayLineT :: Clock p => Int -> Table -> Signal p Double Double
+ Euterpea.IO.Audio.BasicSigFuns: delayLineT :: forall p. Clock p => Int -> Table -> Signal p Double Double
- Euterpea.IO.Audio.BasicSigFuns: envCSEnvlpx :: Clock p => Double -> Double -> Double -> Table -> Double -> Double -> Signal p () Double
+ Euterpea.IO.Audio.BasicSigFuns: envCSEnvlpx :: forall p. Clock p => Double -> Double -> Double -> Table -> Double -> Double -> Signal p () Double
- Euterpea.IO.Audio.BasicSigFuns: envExpon :: Clock p => Double -> Double -> Double -> Signal p () Double
+ Euterpea.IO.Audio.BasicSigFuns: envExpon :: forall p. Clock p => Double -> Double -> Double -> Signal p () Double
- Euterpea.IO.Audio.BasicSigFuns: envLine :: Clock p => Double -> Double -> Double -> Signal p () Double
+ Euterpea.IO.Audio.BasicSigFuns: envLine :: forall p. Clock p => Double -> Double -> Double -> Signal p () Double
- Euterpea.IO.Audio.BasicSigFuns: filterBandPass :: Clock p => Int -> Signal p (Double, Double, Double) Double
+ Euterpea.IO.Audio.BasicSigFuns: filterBandPass :: forall p. Clock p => Int -> Signal p (Double, Double, Double) Double
- Euterpea.IO.Audio.BasicSigFuns: filterBandPassBW :: Clock p => Signal p (Double, Double, Double) Double
+ Euterpea.IO.Audio.BasicSigFuns: filterBandPassBW :: forall p. Clock p => Signal p (Double, Double, Double) Double
- Euterpea.IO.Audio.BasicSigFuns: filterBandStop :: Clock p => Int -> Signal p (Double, Double, Double) Double
+ Euterpea.IO.Audio.BasicSigFuns: filterBandStop :: forall p. Clock p => Int -> Signal p (Double, Double, Double) Double
- Euterpea.IO.Audio.BasicSigFuns: filterBandStopBW :: Clock p => Signal p (Double, Double, Double) Double
+ Euterpea.IO.Audio.BasicSigFuns: filterBandStopBW :: forall p. Clock p => Signal p (Double, Double, Double) Double
- Euterpea.IO.Audio.BasicSigFuns: filterHighPassBW :: Clock p => Signal p (Double, Double) Double
+ Euterpea.IO.Audio.BasicSigFuns: filterHighPassBW :: forall p. Clock p => Signal p (Double, Double) Double
- Euterpea.IO.Audio.BasicSigFuns: filterLowPass :: Clock p => Signal p (Double, Double) Double
+ Euterpea.IO.Audio.BasicSigFuns: filterLowPass :: forall p. Clock p => Signal p (Double, Double) Double
- Euterpea.IO.Audio.BasicSigFuns: filterLowPassBW :: Clock p => Signal p (Double, Double) Double
+ Euterpea.IO.Audio.BasicSigFuns: filterLowPassBW :: forall p. Clock p => Signal p (Double, Double) Double
- Euterpea.IO.Audio.BasicSigFuns: noiseBLH :: Clock p => Int -> Signal p Double Double
+ Euterpea.IO.Audio.BasicSigFuns: noiseBLH :: forall p. Clock p => Int -> Signal p Double Double
- Euterpea.IO.Audio.BasicSigFuns: noiseBLI :: Clock p => Int -> Signal p Double Double
+ Euterpea.IO.Audio.BasicSigFuns: noiseBLI :: forall p. Clock p => Int -> Signal p Double Double
- Euterpea.IO.Audio.BasicSigFuns: oscFixed :: (Clock p, ArrowCircuit a) => Double -> ArrowP a p () Double
+ Euterpea.IO.Audio.BasicSigFuns: oscFixed :: forall p a. (Clock p, ArrowCircuit a) => Double -> ArrowP a p () Double
- Euterpea.IO.Audio.BasicSigFuns: oscPartials :: Clock p => Table -> Double -> Signal p (Double, Int) Double
+ Euterpea.IO.Audio.BasicSigFuns: oscPartials :: forall p. Clock p => Table -> Double -> Signal p (Double, Int) Double
- Euterpea.IO.Audio.BasicSigFuns: pluck :: Clock p => Table -> Double -> PluckDecayMethod -> Signal p Double Double
+ Euterpea.IO.Audio.BasicSigFuns: pluck :: forall p. Clock p => Table -> Double -> PluckDecayMethod -> Signal p Double Double
- Euterpea.IO.Audio.BasicSigFuns: samples :: Clock p => Signal p () (SEvent ())
+ Euterpea.IO.Audio.BasicSigFuns: samples :: forall p. Clock p => Signal p () (SEvent ())
- Euterpea.IO.Audio.Basics: integral :: (ArrowCircuit a, Clock p) => ArrowP a p Double Double
+ Euterpea.IO.Audio.Basics: integral :: forall a p. (ArrowCircuit a, Clock p) => ArrowP a p Double Double
- Euterpea.IO.Audio.Basics: upsample :: (ArrowChoice a, ArrowCircuit a, Clock p1, Clock p2, AudioSample c) => ArrowP a p1 b c -> ArrowP a p2 b c
+ Euterpea.IO.Audio.Basics: upsample :: forall a b c p1 p2. (ArrowChoice a, ArrowCircuit a, Clock p1, Clock p2, AudioSample c) => ArrowP a p1 b c -> ArrowP a p2 b c
- Euterpea.IO.Audio.CSound: oscil :: (ArrowCircuit a, Clock p) => Table -> Double -> ArrowP a p Double Double
+ Euterpea.IO.Audio.CSound: oscil :: (Clock p, ArrowCircuit a) => Table -> Double -> ArrowP a p Double Double
- Euterpea.IO.Audio.CSound: oscil1 :: (ArrowChoice a, ArrowCircuit a, Clock p) => Table -> Double -> 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: oscil1i :: (ArrowChoice a, ArrowCircuit 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: oscili :: (ArrowCircuit a, Clock p) => Table -> 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: oscils :: (ArrowCircuit a, Clock p) => Double -> ArrowP a p Double Double
+ Euterpea.IO.Audio.CSound: oscils :: (Clock p, ArrowCircuit a) => Double -> ArrowP a p Double Double
- Euterpea.IO.Audio.IO: maxSample :: (AudioSample a, Clock p) => Double -> Signal p () a -> Double
+ Euterpea.IO.Audio.IO: maxSample :: forall a p. (AudioSample a, Clock p) => Double -> Signal p () a -> Double
- Euterpea.IO.Audio.IO: outFile :: (AudioSample a, Clock p) => String -> Double -> Signal p () a -> IO ()
+ Euterpea.IO.Audio.IO: outFile :: forall a p. (AudioSample a, Clock p) => String -> Double -> Signal p () a -> IO ()
- Euterpea.IO.Audio.IO: outFileNorm :: (AudioSample a, Clock p) => String -> Double -> Signal p () a -> IO ()
+ Euterpea.IO.Audio.IO: outFileNorm :: forall a p. (AudioSample a, Clock p) => String -> Double -> Signal p () a -> IO ()
- Euterpea.IO.MIDI: ChannelPrefix :: SrictNotUnpackedChannel -> Message
+ Euterpea.IO.MIDI: ChannelPrefix :: ~Channel -> Message
- Euterpea.IO.MIDI: ChannelPressure :: SrictNotUnpackedChannel -> SrictNotUnpackedPressure -> Message
+ Euterpea.IO.MIDI: ChannelPressure :: ~Channel -> ~Pressure -> Message
- Euterpea.IO.MIDI: ControlChange :: SrictNotUnpackedChannel -> SrictNotUnpackedInt -> SrictNotUnpackedInt -> Message
+ Euterpea.IO.MIDI: ControlChange :: ~Channel -> ~Int -> ~Int -> Message
- Euterpea.IO.MIDI: Copyright :: SrictNotUnpackedString -> Message
+ Euterpea.IO.MIDI: Copyright :: ~String -> Message
- Euterpea.IO.MIDI: CuePoint :: SrictNotUnpackedString -> Message
+ Euterpea.IO.MIDI: CuePoint :: ~String -> Message
- Euterpea.IO.MIDI: DeviceName :: SrictNotUnpackedString -> Message
+ Euterpea.IO.MIDI: DeviceName :: ~String -> Message
- Euterpea.IO.MIDI: InstrumentName :: SrictNotUnpackedString -> Message
+ Euterpea.IO.MIDI: InstrumentName :: ~String -> Message
- Euterpea.IO.MIDI: KeyPressure :: SrictNotUnpackedChannel -> SrictNotUnpackedKey -> SrictNotUnpackedPressure -> Message
+ Euterpea.IO.MIDI: KeyPressure :: ~Channel -> ~Key -> ~Pressure -> Message
- Euterpea.IO.MIDI: KeySignature :: SrictNotUnpackedInt -> SrictNotUnpackedInt -> Message
+ Euterpea.IO.MIDI: KeySignature :: ~Int -> ~Int -> Message
- Euterpea.IO.MIDI: Lyrics :: SrictNotUnpackedString -> Message
+ Euterpea.IO.MIDI: Lyrics :: ~String -> Message
- Euterpea.IO.MIDI: Marker :: SrictNotUnpackedString -> Message
+ Euterpea.IO.MIDI: Marker :: ~String -> Message
- Euterpea.IO.MIDI: NoteOff :: SrictNotUnpackedChannel -> SrictNotUnpackedKey -> SrictNotUnpackedVelocity -> Message
+ Euterpea.IO.MIDI: NoteOff :: ~Channel -> ~Key -> ~Velocity -> Message
- Euterpea.IO.MIDI: NoteOn :: SrictNotUnpackedChannel -> SrictNotUnpackedKey -> SrictNotUnpackedVelocity -> Message
+ Euterpea.IO.MIDI: NoteOn :: ~Channel -> ~Key -> ~Velocity -> Message
- Euterpea.IO.MIDI: PitchWheel :: SrictNotUnpackedChannel -> SrictNotUnpackedPitchWheel -> Message
+ Euterpea.IO.MIDI: PitchWheel :: ~Channel -> ~PitchWheel -> Message
- Euterpea.IO.MIDI: ProgramChange :: SrictNotUnpackedChannel -> SrictNotUnpackedPreset -> Message
+ Euterpea.IO.MIDI: ProgramChange :: ~Channel -> ~Preset -> Message
- Euterpea.IO.MIDI: ProgramName :: SrictNotUnpackedString -> Message
+ Euterpea.IO.MIDI: ProgramName :: ~String -> Message
- Euterpea.IO.MIDI: Reserved :: SrictNotUnpackedInt -> SrictNotUnpackedByteString -> Message
+ Euterpea.IO.MIDI: Reserved :: ~Int -> ~ByteString -> Message
- Euterpea.IO.MIDI: SMPTEOffset :: SrictNotUnpackedInt -> SrictNotUnpackedInt -> SrictNotUnpackedInt -> SrictNotUnpackedInt -> SrictNotUnpackedInt -> Message
+ Euterpea.IO.MIDI: SMPTEOffset :: ~Int -> ~Int -> ~Int -> ~Int -> ~Int -> Message
- Euterpea.IO.MIDI: SequenceNumber :: SrictNotUnpackedInt -> Message
+ Euterpea.IO.MIDI: SequenceNumber :: ~Int -> Message
- Euterpea.IO.MIDI: Sysex :: SrictNotUnpackedInt -> SrictNotUnpackedByteString -> Message
+ Euterpea.IO.MIDI: Sysex :: ~Int -> ~ByteString -> Message
- Euterpea.IO.MIDI: TempoChange :: SrictNotUnpackedTempo -> Message
+ Euterpea.IO.MIDI: TempoChange :: ~Tempo -> Message
- Euterpea.IO.MIDI: Text :: SrictNotUnpackedString -> Message
+ Euterpea.IO.MIDI: Text :: ~String -> Message
- Euterpea.IO.MIDI: TimeSignature :: SrictNotUnpackedInt -> SrictNotUnpackedInt -> SrictNotUnpackedInt -> SrictNotUnpackedInt -> Message
+ Euterpea.IO.MIDI: TimeSignature :: ~Int -> ~Int -> ~Int -> ~Int -> Message
- Euterpea.IO.MIDI: TrackName :: SrictNotUnpackedString -> Message
+ Euterpea.IO.MIDI: TrackName :: ~String -> Message
- Euterpea.IO.MIDI: [channel] :: Message -> SrictNotUnpackedChannel
+ Euterpea.IO.MIDI: [channel] :: Message -> ~Channel
- Euterpea.IO.MIDI: [controllerNumber] :: Message -> SrictNotUnpackedInt
+ Euterpea.IO.MIDI: [controllerNumber] :: Message -> ~Int
- Euterpea.IO.MIDI: [controllerValue] :: Message -> SrictNotUnpackedInt
+ Euterpea.IO.MIDI: [controllerValue] :: Message -> ~Int
- Euterpea.IO.MIDI: [key] :: Message -> SrictNotUnpackedKey
+ Euterpea.IO.MIDI: [key] :: Message -> ~Key
- Euterpea.IO.MIDI: [pitchWheel] :: Message -> SrictNotUnpackedPitchWheel
+ Euterpea.IO.MIDI: [pitchWheel] :: Message -> ~PitchWheel
- Euterpea.IO.MIDI: [preset] :: Message -> SrictNotUnpackedPreset
+ Euterpea.IO.MIDI: [preset] :: Message -> ~Preset
- Euterpea.IO.MIDI: [pressure] :: Message -> SrictNotUnpackedPressure
+ Euterpea.IO.MIDI: [pressure] :: Message -> ~Pressure
- Euterpea.IO.MIDI: [velocity] :: Message -> SrictNotUnpackedVelocity
+ Euterpea.IO.MIDI: [velocity] :: Message -> ~Velocity
- Euterpea.IO.MIDI.MidiIO: ChannelPrefix :: SrictNotUnpackedChannel -> Message
+ Euterpea.IO.MIDI.MidiIO: ChannelPrefix :: ~Channel -> Message
- Euterpea.IO.MIDI.MidiIO: ChannelPressure :: SrictNotUnpackedChannel -> SrictNotUnpackedPressure -> Message
+ Euterpea.IO.MIDI.MidiIO: ChannelPressure :: ~Channel -> ~Pressure -> Message
- Euterpea.IO.MIDI.MidiIO: ControlChange :: SrictNotUnpackedChannel -> SrictNotUnpackedInt -> SrictNotUnpackedInt -> Message
+ Euterpea.IO.MIDI.MidiIO: ControlChange :: ~Channel -> ~Int -> ~Int -> Message
- Euterpea.IO.MIDI.MidiIO: Copyright :: SrictNotUnpackedString -> Message
+ Euterpea.IO.MIDI.MidiIO: Copyright :: ~String -> Message
- Euterpea.IO.MIDI.MidiIO: CuePoint :: SrictNotUnpackedString -> Message
+ Euterpea.IO.MIDI.MidiIO: CuePoint :: ~String -> Message
- Euterpea.IO.MIDI.MidiIO: DeviceName :: SrictNotUnpackedString -> Message
+ Euterpea.IO.MIDI.MidiIO: DeviceName :: ~String -> Message
- Euterpea.IO.MIDI.MidiIO: InstrumentName :: SrictNotUnpackedString -> Message
+ Euterpea.IO.MIDI.MidiIO: InstrumentName :: ~String -> Message
- Euterpea.IO.MIDI.MidiIO: KeyPressure :: SrictNotUnpackedChannel -> SrictNotUnpackedKey -> SrictNotUnpackedPressure -> Message
+ Euterpea.IO.MIDI.MidiIO: KeyPressure :: ~Channel -> ~Key -> ~Pressure -> Message
- Euterpea.IO.MIDI.MidiIO: KeySignature :: SrictNotUnpackedInt -> SrictNotUnpackedInt -> Message
+ Euterpea.IO.MIDI.MidiIO: KeySignature :: ~Int -> ~Int -> Message
- Euterpea.IO.MIDI.MidiIO: Lyrics :: SrictNotUnpackedString -> Message
+ Euterpea.IO.MIDI.MidiIO: Lyrics :: ~String -> Message
- Euterpea.IO.MIDI.MidiIO: Marker :: SrictNotUnpackedString -> Message
+ Euterpea.IO.MIDI.MidiIO: Marker :: ~String -> Message
- Euterpea.IO.MIDI.MidiIO: NoteOff :: SrictNotUnpackedChannel -> SrictNotUnpackedKey -> SrictNotUnpackedVelocity -> Message
+ Euterpea.IO.MIDI.MidiIO: NoteOff :: ~Channel -> ~Key -> ~Velocity -> Message
- Euterpea.IO.MIDI.MidiIO: NoteOn :: SrictNotUnpackedChannel -> SrictNotUnpackedKey -> SrictNotUnpackedVelocity -> Message
+ Euterpea.IO.MIDI.MidiIO: NoteOn :: ~Channel -> ~Key -> ~Velocity -> Message
- Euterpea.IO.MIDI.MidiIO: PitchWheel :: SrictNotUnpackedChannel -> SrictNotUnpackedPitchWheel -> Message
+ Euterpea.IO.MIDI.MidiIO: PitchWheel :: ~Channel -> ~PitchWheel -> Message
- Euterpea.IO.MIDI.MidiIO: ProgramChange :: SrictNotUnpackedChannel -> SrictNotUnpackedPreset -> Message
+ Euterpea.IO.MIDI.MidiIO: ProgramChange :: ~Channel -> ~Preset -> Message
- Euterpea.IO.MIDI.MidiIO: ProgramName :: SrictNotUnpackedString -> Message
+ Euterpea.IO.MIDI.MidiIO: ProgramName :: ~String -> Message
- Euterpea.IO.MIDI.MidiIO: Reserved :: SrictNotUnpackedInt -> SrictNotUnpackedByteString -> Message
+ Euterpea.IO.MIDI.MidiIO: Reserved :: ~Int -> ~ByteString -> Message
- Euterpea.IO.MIDI.MidiIO: SMPTEOffset :: SrictNotUnpackedInt -> SrictNotUnpackedInt -> SrictNotUnpackedInt -> SrictNotUnpackedInt -> SrictNotUnpackedInt -> Message
+ Euterpea.IO.MIDI.MidiIO: SMPTEOffset :: ~Int -> ~Int -> ~Int -> ~Int -> ~Int -> Message
- Euterpea.IO.MIDI.MidiIO: SequenceNumber :: SrictNotUnpackedInt -> Message
+ Euterpea.IO.MIDI.MidiIO: SequenceNumber :: ~Int -> Message
- Euterpea.IO.MIDI.MidiIO: Sysex :: SrictNotUnpackedInt -> SrictNotUnpackedByteString -> Message
+ Euterpea.IO.MIDI.MidiIO: Sysex :: ~Int -> ~ByteString -> Message
- Euterpea.IO.MIDI.MidiIO: TempoChange :: SrictNotUnpackedTempo -> Message
+ Euterpea.IO.MIDI.MidiIO: TempoChange :: ~Tempo -> Message
- Euterpea.IO.MIDI.MidiIO: Text :: SrictNotUnpackedString -> Message
+ Euterpea.IO.MIDI.MidiIO: Text :: ~String -> Message
- Euterpea.IO.MIDI.MidiIO: TimeSignature :: SrictNotUnpackedInt -> SrictNotUnpackedInt -> SrictNotUnpackedInt -> SrictNotUnpackedInt -> Message
+ Euterpea.IO.MIDI.MidiIO: TimeSignature :: ~Int -> ~Int -> ~Int -> ~Int -> Message
- Euterpea.IO.MIDI.MidiIO: TrackName :: SrictNotUnpackedString -> Message
+ Euterpea.IO.MIDI.MidiIO: TrackName :: ~String -> Message
- Euterpea.IO.MIDI.MidiIO: [channel] :: Message -> SrictNotUnpackedChannel
+ Euterpea.IO.MIDI.MidiIO: [channel] :: Message -> ~Channel
- Euterpea.IO.MIDI.MidiIO: [controllerNumber] :: Message -> SrictNotUnpackedInt
+ Euterpea.IO.MIDI.MidiIO: [controllerNumber] :: Message -> ~Int
- Euterpea.IO.MIDI.MidiIO: [controllerValue] :: Message -> SrictNotUnpackedInt
+ Euterpea.IO.MIDI.MidiIO: [controllerValue] :: Message -> ~Int
- Euterpea.IO.MIDI.MidiIO: [key] :: Message -> SrictNotUnpackedKey
+ Euterpea.IO.MIDI.MidiIO: [key] :: Message -> ~Key
- Euterpea.IO.MIDI.MidiIO: [pitchWheel] :: Message -> SrictNotUnpackedPitchWheel
+ Euterpea.IO.MIDI.MidiIO: [pitchWheel] :: Message -> ~PitchWheel
- Euterpea.IO.MIDI.MidiIO: [preset] :: Message -> SrictNotUnpackedPreset
+ Euterpea.IO.MIDI.MidiIO: [preset] :: Message -> ~Preset
- Euterpea.IO.MIDI.MidiIO: [pressure] :: Message -> SrictNotUnpackedPressure
+ Euterpea.IO.MIDI.MidiIO: [pressure] :: Message -> ~Pressure
- Euterpea.IO.MIDI.MidiIO: [velocity] :: Message -> SrictNotUnpackedVelocity
+ Euterpea.IO.MIDI.MidiIO: [velocity] :: Message -> ~Velocity
- Euterpea.IO.MIDI.ToMidi: toDelta :: (Integral b, RealFrac a) => a -> b
+ Euterpea.IO.MIDI.ToMidi: toDelta :: (RealFrac a, Integral b) => a -> b

Files

.git/COMMIT_EDITMSG view
@@ -1,4 +1,1 @@-dependency caps and audio render update--Dependencies are capped more strictly and audio rendering will now clip-by default (rather than overflowing) when values exceed +/-1.+Dependency versions narrowed again
.git/FETCH_HEAD view
@@ -1,1 +1,1 @@-896621f361866ce787d0bad1dfd5434d371b5679		branch 'master' of https://github.com/Euterpea/Euterpea2+40d6be12def8fc357df4248d9943bd3cc1f75f89		branch 'master' of https://github.com/Euterpea/Euterpea2
.git/ORIG_HEAD view
@@ -1,1 +1,1 @@-b1eb4e6bd0c330b7cbe720d59df11bf3105b2b85+aa72de41851f42d3f1023c01ae0edc5832c47ebd
.git/index view

binary file changed (2340 → 2340 bytes)

.git/logs/HEAD view
@@ -1,21 +1,10 @@-0000000000000000000000000000000000000000 f8154e00ed87f700e71790ab9966fc8414807b8a donya <donyavq@netscape.net> 1450296215 -0500	commit (initial): Initial commit-f8154e00ed87f700e71790ab9966fc8414807b8a 4f1c6161cd6b76930d6ab63910f661e4c9280c64 donya <donyavq@netscape.net> 1450311885 -0500	commit: Renaming & restructuring-4f1c6161cd6b76930d6ab63910f661e4c9280c64 85af43da1948ffbdf3b1fff153d88119842852b9 donya <donyavq@netscape.net> 1450315255 -0500	commit: Field renaming for MContext-85af43da1948ffbdf3b1fff153d88119842852b9 5ea6aeb96d1894c8e38cc8f865fadb85bcacaad2 donya <donyavq@netscape.net> 1451596992 -0500	pull --progress --rebase --prune origin master: checkout 5ea6aeb96d1894c8e38cc8f865fadb85bcacaad2-5ea6aeb96d1894c8e38cc8f865fadb85bcacaad2 5ea6aeb96d1894c8e38cc8f865fadb85bcacaad2 donya <donyavq@netscape.net> 1451596993 -0500	rebase finished: returning to refs/heads/master-5ea6aeb96d1894c8e38cc8f865fadb85bcacaad2 8ca262164429aa9765dfd2fa47331f3349869329 donya <donyavq@netscape.net> 1451597251 -0500	commit: Dependency on UISF removed-8ca262164429aa9765dfd2fa47331f3349869329 9bc107a80e863822b328bf64a795c3ef54e001b1 donya <donyavq@netscape.net> 1451597503 -0500	commit: Reame updated-9bc107a80e863822b328bf64a795c3ef54e001b1 31ba3d0f73a9b2159cfa27d8e11e275daee8c481 donya <donyavq@netscape.net> 1451954740 -0500	pull --progress --rebase --prune origin master: checkout 31ba3d0f73a9b2159cfa27d8e11e275daee8c481-31ba3d0f73a9b2159cfa27d8e11e275daee8c481 31ba3d0f73a9b2159cfa27d8e11e275daee8c481 donya <donyavq@netscape.net> 1451954740 -0500	rebase finished: returning to refs/heads/master-31ba3d0f73a9b2159cfa27d8e11e275daee8c481 877cdbe88a44c2962bc9bc66e021ad8a9c185268 Donya Quick <donyavq@netscape.net> 1452131260 -0500	commit: Added writeWav-877cdbe88a44c2962bc9bc66e021ad8a9c185268 323835bed7c98ee14e0445126cfc2df5cbed9bae Donya Quick <donyavq@netscape.net> 1452451734 -0500	pull --progress --rebase --prune origin master: checkout 323835bed7c98ee14e0445126cfc2df5cbed9bae-323835bed7c98ee14e0445126cfc2df5cbed9bae 323835bed7c98ee14e0445126cfc2df5cbed9bae Donya Quick <donyavq@netscape.net> 1452451735 -0500	rebase finished: returning to refs/heads/master-323835bed7c98ee14e0445126cfc2df5cbed9bae 73bc3b4c2d2ca70c8a8844a118e3d3db3aeb4d0c Donya Quick <donyavq@netscape.net> 1452537818 -0500	pull --progress --rebase --prune origin master: checkout 73bc3b4c2d2ca70c8a8844a118e3d3db3aeb4d0c-73bc3b4c2d2ca70c8a8844a118e3d3db3aeb4d0c 73bc3b4c2d2ca70c8a8844a118e3d3db3aeb4d0c Donya Quick <donyavq@netscape.net> 1452537818 -0500	rebase finished: returning to refs/heads/master-73bc3b4c2d2ca70c8a8844a118e3d3db3aeb4d0c 61d09a71180e5a1a4ea1d893f82a4709d8e79c27 Donya Quick <donyavq@netscape.net> 1453084077 -0500	commit: removeInstruments fixed-61d09a71180e5a1a4ea1d893f82a4709d8e79c27 b1eb4e6bd0c330b7cbe720d59df11bf3105b2b85 Donya Quick <donyavq@netscape.net> 1455221540 -0500	commit: invert fix-b1eb4e6bd0c330b7cbe720d59df11bf3105b2b85 086557985556cc77f5a559d74d0502a2d761babd Donya Quick <donyavq@netscape.net> 1456862557 -0500	pull --progress --rebase --prune origin master: checkout 086557985556cc77f5a559d74d0502a2d761babd-086557985556cc77f5a559d74d0502a2d761babd 086557985556cc77f5a559d74d0502a2d761babd Donya Quick <donyavq@netscape.net> 1456862558 -0500	rebase finished: returning to refs/heads/master-086557985556cc77f5a559d74d0502a2d761babd 7a5e865406dc8dc8d2bd730b7625656fc4c80036 Donya Quick <donyavq@netscape.net> 1459959169 -0400	commit: Dependency version changes-7a5e865406dc8dc8d2bd730b7625656fc4c80036 896621f361866ce787d0bad1dfd5434d371b5679 Donya Quick <donyavq@netscape.net> 1461339370 -0400	commit: Exports added-896621f361866ce787d0bad1dfd5434d371b5679 4afd6ed7d81c474bf5c88c395a584e4f68b039ce Donya Quick <donyavq@netscape.net> 1462819240 -0400	commit: dependency caps and audio render update+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
.git/logs/refs/heads/master view
@@ -1,16 +1,7 @@-0000000000000000000000000000000000000000 f8154e00ed87f700e71790ab9966fc8414807b8a donya <donyavq@netscape.net> 1450296215 -0500	commit (initial): Initial commit-f8154e00ed87f700e71790ab9966fc8414807b8a 4f1c6161cd6b76930d6ab63910f661e4c9280c64 donya <donyavq@netscape.net> 1450311885 -0500	commit: Renaming & restructuring-4f1c6161cd6b76930d6ab63910f661e4c9280c64 85af43da1948ffbdf3b1fff153d88119842852b9 donya <donyavq@netscape.net> 1450315255 -0500	commit: Field renaming for MContext-85af43da1948ffbdf3b1fff153d88119842852b9 5ea6aeb96d1894c8e38cc8f865fadb85bcacaad2 donya <donyavq@netscape.net> 1451596992 -0500	rebase finished: refs/heads/master onto 5ea6aeb96d1894c8e38cc8f865fadb85bcacaad2-5ea6aeb96d1894c8e38cc8f865fadb85bcacaad2 8ca262164429aa9765dfd2fa47331f3349869329 donya <donyavq@netscape.net> 1451597251 -0500	commit: Dependency on UISF removed-8ca262164429aa9765dfd2fa47331f3349869329 9bc107a80e863822b328bf64a795c3ef54e001b1 donya <donyavq@netscape.net> 1451597503 -0500	commit: Reame updated-9bc107a80e863822b328bf64a795c3ef54e001b1 31ba3d0f73a9b2159cfa27d8e11e275daee8c481 donya <donyavq@netscape.net> 1451954740 -0500	rebase finished: refs/heads/master onto 31ba3d0f73a9b2159cfa27d8e11e275daee8c481-31ba3d0f73a9b2159cfa27d8e11e275daee8c481 877cdbe88a44c2962bc9bc66e021ad8a9c185268 Donya Quick <donyavq@netscape.net> 1452131260 -0500	commit: Added writeWav-877cdbe88a44c2962bc9bc66e021ad8a9c185268 323835bed7c98ee14e0445126cfc2df5cbed9bae Donya Quick <donyavq@netscape.net> 1452451735 -0500	rebase finished: refs/heads/master onto 323835bed7c98ee14e0445126cfc2df5cbed9bae-323835bed7c98ee14e0445126cfc2df5cbed9bae 73bc3b4c2d2ca70c8a8844a118e3d3db3aeb4d0c Donya Quick <donyavq@netscape.net> 1452537818 -0500	rebase finished: refs/heads/master onto 73bc3b4c2d2ca70c8a8844a118e3d3db3aeb4d0c-73bc3b4c2d2ca70c8a8844a118e3d3db3aeb4d0c 61d09a71180e5a1a4ea1d893f82a4709d8e79c27 Donya Quick <donyavq@netscape.net> 1453084077 -0500	commit: removeInstruments fixed-61d09a71180e5a1a4ea1d893f82a4709d8e79c27 b1eb4e6bd0c330b7cbe720d59df11bf3105b2b85 Donya Quick <donyavq@netscape.net> 1455221540 -0500	commit: invert fix-b1eb4e6bd0c330b7cbe720d59df11bf3105b2b85 086557985556cc77f5a559d74d0502a2d761babd Donya Quick <donyavq@netscape.net> 1456862558 -0500	rebase finished: refs/heads/master onto 086557985556cc77f5a559d74d0502a2d761babd-086557985556cc77f5a559d74d0502a2d761babd 7a5e865406dc8dc8d2bd730b7625656fc4c80036 Donya Quick <donyavq@netscape.net> 1459959169 -0400	commit: Dependency version changes-7a5e865406dc8dc8d2bd730b7625656fc4c80036 896621f361866ce787d0bad1dfd5434d371b5679 Donya Quick <donyavq@netscape.net> 1461339370 -0400	commit: Exports added-896621f361866ce787d0bad1dfd5434d371b5679 4afd6ed7d81c474bf5c88c395a584e4f68b039ce Donya Quick <donyavq@netscape.net> 1462819240 -0400	commit: dependency caps and audio render update+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
.git/logs/refs/remotes/origin/HEAD view
@@ -1,1 +1,1 @@-0000000000000000000000000000000000000000 9bc107a80e863822b328bf64a795c3ef54e001b1 Donya Quick <donyavq@netscape.net> 1451691193 -0800	clone: from https://github.com/Euterpea/Euterpea2.git+0000000000000000000000000000000000000000 61d09a71180e5a1a4ea1d893f82a4709d8e79c27 donya <donyavq@netscape.net> 1453085786 -0500	clone: from https://github.com/Euterpea/Euterpea2.git
.git/logs/refs/remotes/origin/master view
@@ -1,16 +1,5 @@-0000000000000000000000000000000000000000 f8154e00ed87f700e71790ab9966fc8414807b8a donya <donyavq@netscape.net> 1450296225 -0500	update by push-f8154e00ed87f700e71790ab9966fc8414807b8a 4f1c6161cd6b76930d6ab63910f661e4c9280c64 donya <donyavq@netscape.net> 1450311899 -0500	update by push-4f1c6161cd6b76930d6ab63910f661e4c9280c64 85af43da1948ffbdf3b1fff153d88119842852b9 donya <donyavq@netscape.net> 1450315267 -0500	update by push-85af43da1948ffbdf3b1fff153d88119842852b9 5ea6aeb96d1894c8e38cc8f865fadb85bcacaad2 donya <donyavq@netscape.net> 1451596991 -0500	pull --progress --rebase --prune origin master: fast-forward-5ea6aeb96d1894c8e38cc8f865fadb85bcacaad2 8ca262164429aa9765dfd2fa47331f3349869329 donya <donyavq@netscape.net> 1451597262 -0500	update by push-8ca262164429aa9765dfd2fa47331f3349869329 9bc107a80e863822b328bf64a795c3ef54e001b1 donya <donyavq@netscape.net> 1451597514 -0500	update by push-9bc107a80e863822b328bf64a795c3ef54e001b1 31ba3d0f73a9b2159cfa27d8e11e275daee8c481 donya <donyavq@netscape.net> 1451954738 -0500	pull --progress --rebase --prune origin master: fast-forward-31ba3d0f73a9b2159cfa27d8e11e275daee8c481 877cdbe88a44c2962bc9bc66e021ad8a9c185268 Donya Quick <donyavq@netscape.net> 1452131278 -0500	update by push-877cdbe88a44c2962bc9bc66e021ad8a9c185268 323835bed7c98ee14e0445126cfc2df5cbed9bae Donya Quick <donyavq@netscape.net> 1452451733 -0500	pull --progress --rebase --prune origin master: fast-forward-323835bed7c98ee14e0445126cfc2df5cbed9bae 73bc3b4c2d2ca70c8a8844a118e3d3db3aeb4d0c Donya Quick <donyavq@netscape.net> 1452537816 -0500	pull --progress --rebase --prune origin master: fast-forward-73bc3b4c2d2ca70c8a8844a118e3d3db3aeb4d0c 61d09a71180e5a1a4ea1d893f82a4709d8e79c27 Donya Quick <donyavq@netscape.net> 1453084100 -0500	update by push-61d09a71180e5a1a4ea1d893f82a4709d8e79c27 b1eb4e6bd0c330b7cbe720d59df11bf3105b2b85 Donya Quick <donyavq@netscape.net> 1455221559 -0500	update by push-b1eb4e6bd0c330b7cbe720d59df11bf3105b2b85 086557985556cc77f5a559d74d0502a2d761babd Donya Quick <donyavq@netscape.net> 1456862556 -0500	pull --progress --rebase --prune origin master: fast-forward-086557985556cc77f5a559d74d0502a2d761babd 7a5e865406dc8dc8d2bd730b7625656fc4c80036 Donya Quick <donyavq@netscape.net> 1459959184 -0400	update by push-7a5e865406dc8dc8d2bd730b7625656fc4c80036 896621f361866ce787d0bad1dfd5434d371b5679 Donya Quick <donyavq@netscape.net> 1461339414 -0400	update by push-896621f361866ce787d0bad1dfd5434d371b5679 4afd6ed7d81c474bf5c88c395a584e4f68b039ce Donya Quick <donyavq@netscape.net> 1462819260 -0400	update by push+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
− .git/objects/07/34c378fd990e9ab7a7f9fb2fe361a84d41e95e

binary file changed (245 → absent bytes)

+ .git/objects/08/1fa266eebe94ad9d1401de24163f05d6d378b3 view

binary file changed (absent → 168 bytes)

+ .git/objects/08/cf131880f52ce6a6d1e8dd2f0afb8ee0f30bcf view

binary file changed (absent → 1048 bytes)

+ .git/objects/09/53e4337fcc90c2a5c2ec957f9d46b4aa32ca99 view

binary file changed (absent → 1548 bytes)

+ .git/objects/0c/262c7a72c9e17768fa826a90472e376c09767a view

binary file changed (absent → 233 bytes)

+ .git/objects/0e/40a7618f3033863399b53fd837057d05c90d33 view

binary file changed (absent → 155285 bytes)

− .git/objects/0e/668119469c51796e6d599be3210067a29b2519

binary file changed (430 → absent bytes)

− .git/objects/0f/40bd8185ca44ac857d80e28f1571f7dc4c2f0b

binary file changed (215 → absent bytes)

+ .git/objects/10/aedf5be4cd4cba55b64d5f6995b2848da5156b view

binary file changed (absent → 15321 bytes)

− .git/objects/12/422ea17098ee8ef49c69ec4f706610531ff7a5

binary file changed (4571 → absent bytes)

+ .git/objects/13/ff9346b625728e873af12be8d41ba411d16d3a view

binary file changed (absent → 10765 bytes)

− .git/objects/15/3be0645392d0fcd358365100287c8153667803

binary file changed (137 → absent bytes)

+ .git/objects/16/053d551af0017fc14257241ace886edd2afc02 view

binary file changed (absent → 188 bytes)

− .git/objects/18/120c7891df6d6b7a4edb6505c550b9a48dcfc6

binary file changed (82 → absent bytes)

+ .git/objects/18/2a0398ba6a070c251b036c128fb0d923b1b04a view

binary file changed (absent → 108 bytes)

− .git/objects/19/0569154271005cf561a49b93e0dacf5d237670

binary file changed (245 → absent bytes)

− .git/objects/19/a4d9c645f7e4b9f5414e5bb1b45de9a1c3ea40

binary file changed (828 → absent bytes)

+ .git/objects/1b/963cb16e69a4719c07d01041419fc0016888e9 view

binary file changed (absent → 4844 bytes)

− .git/objects/1d/4d0774da205784863ae2d06988d4c525dd5b22

binary file changed (245 → absent bytes)

+ .git/objects/1d/865ab036c3dafe78300b3876f46e7fc91b1836 view

binary file changed (absent → 579 bytes)

+ .git/objects/1e/e46d0b56ddaa82b9a081a21369c1509b84402e view

binary file changed (absent → 158 bytes)

− .git/objects/1f/0b51995096b04b9c0bb36b633333ee53cf6ede

binary file changed (2129 → absent bytes)

− .git/objects/22/9b691fbf867126dc4e40ed8b5bf16facc90e7a

binary file changed (4712 → absent bytes)

− .git/objects/23/e8522114db4fb8526a9948e7c91250c63a9928

binary file changed (136 → absent bytes)

− .git/objects/26/252acd178a04d8b98a75016a754d8b7bc4dfdd

binary file changed (2136 → absent bytes)

+ .git/objects/26/d1e77943b1fe7cb446dd7ac0fef7cc1c495845 view
@@ -0,0 +1,6 @@+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 view

binary file changed (absent → 3699 bytes)

− .git/objects/28/0bbfa003c4249f5fc4a59ad5fa724272bc2d02

binary file changed (417 → absent bytes)

− .git/objects/28/fb2df2b4bf2f8be62602e7b3b6ad99289fefac

binary file changed (245 → absent bytes)

− .git/objects/29/09458fe91e193322f78abdafd7c8b9c2480b9f

binary file changed (136 → absent bytes)

+ .git/objects/29/3bd1d6053e165fb63a1bc860ce65d28160759f view

file too large to diff

− .git/objects/2c/1360591bec55fca7893b298b53197be3c867ba

binary file changed (2125 → absent bytes)

+ .git/objects/2c/230b496eb8d7ee15595fc8bb6e0893af19008b view

binary file changed (absent → 7054 bytes)

− .git/objects/2e/0b0c295239ecdf1e63e6a7536505ac813f47f9

binary file changed (137 → absent bytes)

− .git/objects/31/5c172ba4c6d45564249a225f0e075f671c5fbb
@@ -1,3 +0,0 @@-x+)JMU022e040031QpJ,ÎLÎLw+Í+ÖËÉ(f	8¹…ŸyQð’BÛ-E®Gfä"«-Ö*Ê©XÇ|?Gú°é§Ã7¥6‹O«½ÈÕ
Uäœ_š—6já'ŸY¬=6”œÝshë¤éÚ÷í¡ª<ýAÆ·‰aæ¨v˜.`î rãòç"¡-‚RóRR‹@ŠÔTµÎŠw±ÜØÙUʘUêÛ]}äþ]¨¢Ê‚T°{ÎnÌ­ò­yxwK~WºêL†µ™SÕ_X2
− .git/objects/31/ba3d0f73a9b2159cfa27d8e11e275daee8c481

binary file changed (172 → absent bytes)

− .git/objects/32/3835bed7c98ee14e0445126cfc2df5cbed9bae

binary file changed (159 → absent bytes)

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

binary file changed (absent → 12169 bytes)

− .git/objects/35/8436995aff5176e643cebf3c5d070536ac57cb

binary file changed (244 → absent bytes)

+ .git/objects/36/e8447f23fe4c9fc02644fae5965b7b9fe966fd view

binary file changed (absent → 28935 bytes)

+ .git/objects/37/3df86f01c69ae7dcc599f9c11e786415e4d1e6 view

binary file changed (absent → 27398 bytes)

− .git/objects/38/79732a59fd96782ac7fec01e882bc419a650f6

binary file changed (137 → absent bytes)

− .git/objects/39/1156a543887fc398238ebd6b61194807e1c8df

binary file changed (6732 → absent bytes)

− .git/objects/3a/77dd1cc37ae544654eafe0d6106d0322fcd6d6

binary file changed (83 → absent bytes)

− .git/objects/3a/f2137fd14e17ec39cd86c35eb817be41236fa3

binary file changed (246 → absent bytes)

− .git/objects/3e/ee7432174ba6920cdc77a546f40abea4363bb0

binary file changed (1294 → absent bytes)

+ .git/objects/40/0528d8d6e72ec2edc4fee57e208bc972fdc90f view
@@ -0,0 +1,2 @@+x+)JMU03e040031QvÓËÈdØQä֑Ì*Ûûêä‘;âþA++ò>i§>Lßú*d‡3«h¯¡Y÷Ël
+ .git/objects/40/d6be12def8fc357df4248d9943bd3cc1f75f89 view
@@ -0,0 +1,1 @@+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 view

binary file changed (absent → 12653 bytes)

− .git/objects/41/d19a4642f6660a4179f3c71e91735989598daf

binary file changed (6426 → absent bytes)

+ .git/objects/42/4bc74689367dde97ffde93f82558e7dc6c0bf2 view

binary file changed (absent → 18249 bytes)

− .git/objects/42/e3ec9572ed4138542efdf91a07554c835f4882

binary file changed (1285 → absent bytes)

− .git/objects/46/4199cbe596b6d8e366c3f5900c87740c819acf

binary file changed (4566 → absent bytes)

− .git/objects/46/d74297ecb00ced0498fca2d37ef1123967d685

binary file changed (2447 → absent bytes)

− .git/objects/4d/011c28a383821201d0c2da48db909df774df62

binary file changed (481 → absent bytes)

− .git/objects/4f/1c6161cd6b76930d6ab63910f661e4c9280c64
@@ -1,1 +0,0 @@-x•N»NÄ0¤öWlu‘÷Ç	¡k(Añ~¬HĎ
âï±BEI53šÍø¼®K…ò¦fÐg6ΑÓJ#ir“Grj¢ æÑg…VÇ9ŠÍN"¡YJd¢éhÐÌÒºyž¦èiđ¤qd…mõ-9}[¸;àó㒸îÞn<tr8j©‰4ÜJ-¥ðÇ»Êÿí‰Nv]ÒNPx¯¥ùÚJ×B<s‰¹¬Ö½s·zŠÔ¯ù±í‹GñÐúÜÆv8ôð”+ÿRXvHùþÄ7eÈ
− .git/objects/50/3f8ef4dd7e6196ceb6045ca57275d8e337e43b
@@ -1,3 +0,0 @@-x…‘=Â0†]ͯ¸±…ÒIÐÅAÁB)XÁ95§šòañßSDBÖ{ž÷½F5Àj½YÅüˆpðFZ·]½óŒ+P@-Ö{jùµç÷ÆK[2jN:¿4朶ËütBÉÐTd9îðBŸ!X	Ó
…VÆý÷ü©’±lšÇiÜvi6Ç&ä›n’-~¤l!®wD«l&öì›@?Rä\(ïn9¿g`	y­I 4
− .git/objects/52/8e6cd0e3e16d9dbb927d3bb4bcd83ecf8dfad6
@@ -1,6 +0,0 @@-x…UÙnÛ8g}ÅAúP)µÜÊ0˜ à¦étÉL’¾4EÙÄÐ"AQ™þ{ï¥$/‰>‰¾Û9wõÜØ9Š?ÿ:ûã¿ü>;¼¿›½¿ÂÌ{{ߌpµÖMPuÐÂüÝ-úVZŠ m=Â;£ÖznÔ¥­ƒZ2¾‘Ö©òvãÔ7áµ ]ƒgùÏ$YÙ²5-WmPÞ)1þøu<kKmÇoD£eƒ4lf#ÐCS¸…fDoiÛ:¼µ÷ŸwŽ¥­kÄÊÅo'—·öÃÃÂñ7Ép¿T^%‰^9ëÃósKPO¤[&̺Ԝ’·f«pT8þꔏ…8îÔ¹^'IÂyáüi‘az9æj-á=tIŒûÄÙº²TCvnÜ{^j/[ F¸4Vþ#Ũ×lˆ·¶¥’÷Ÿ]´)0* ˜¢ÀKu…´­KUéZ•Œè²h¦k8o%ÖÈ/PÚ(¼’1€~N4^ú”¢õZ¯ñ:G©ŒØàò×ÐÏ{e̟I²í&ãEâÛ¤¸(ëÀ iÆï=ó5¡FVß÷X1§=T"L ÈQDàǰwî8è!Yý©ËŠð)&ó …sH¸n²måÒj©¸“‡EØv¶Ø¾&dÛr2ëšÉç’íº_ÐDI®ÎN4aюRE©-¢{Üz÷©ö­Þ‹ÃsÓ÷ÜÉ åãFW,»˜ÂçÂRÕÔeEÒ]zÏî³aw}¦S2=ç´	^;T·rý„ÏAœø#âtC÷ ¼í
Èyó˜ø0{-OùÒüÃÛ@ÃSÇÇ¡kä§èzœ\Šº¶a{~hR½¨…­°Ôº;Ýbcïû_'òˆœÐ—=è³'A܎íhÑ-iLtˆqÜr’%IžãÆ®ÚFU-“v­~ƒª­%ñfþ¼Ê]/ jÌæÍµr	J„’-HåFò5§£‡FI[—ÙyÒ]\õwÆÒ-dç8”ƒ;/ó`%x³ÎÎ^Ñݘàôiå튶<žz¤¤Î!Çt6:Ë2ºSEŸÆCö$¤ŽâaZÅD?<œ'ýŸÁSJmä3ØÐݍÿoq×Ó
+ .git/objects/52/c3e648f28c37c75cbad302221d9d7e63b8b83e view

binary file changed (absent → 82 bytes)

− .git/objects/52/e7bb8b5351858b6c18b368d3947dc1231a5f9f

binary file changed (244 → absent bytes)

− .git/objects/53/d8a923285953866fc263789d67e4c38525c4fa

binary file changed (875 → absent bytes)

− .git/objects/54/5758c9b40f03a253a47131d9207245c4986c51

binary file changed (14473 → absent bytes)

+ .git/objects/54/7dcd3ab2847acaaa664560053063485e9799af view
@@ -0,0 +1,2 @@+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 view

binary file changed (absent → 298 bytes)

+ .git/objects/57/c5213c0484db428f6c4cf6e6214e89a8ae1495 view

binary file changed (absent → 1896 bytes)

− .git/objects/59/22faa1f075f43bf5ff192ef560a4c1ee5ac7d8

binary file changed (136 → absent bytes)

+ .git/objects/5a/0a4abb675bb7edeeb1581ef36c3107d9ddabc0 view

binary file changed (absent → 8803 bytes)

− .git/objects/5a/7588690dec40ed5e79d4831b388b095febcb2f

binary file changed (4720 → absent bytes)

+ .git/objects/5a/db5e8db2e5525db664ff2e17de8b87ff0f8670 view

binary file changed (absent → 356980 bytes)

− .git/objects/5b/f300951510805ff5a3fe166487788a031778a5

binary file changed (4573 → absent bytes)

− .git/objects/5e/a6aeb96d1894c8e38cc8f865fadb85bcacaad2

binary file changed (175 → absent bytes)

+ .git/objects/60/492b4c521313c25cb4c46b57d0b72cc74d7cbd view

binary file changed (absent → 136 bytes)

− .git/objects/61/d09a71180e5a1a4ea1d893f82a4709d8e79c27

binary file changed (172 → absent bytes)

− .git/objects/64/0e75d56b3758363d58a86d728c5403aa92c840

binary file changed (244 → absent bytes)

− .git/objects/64/5249b0b24a3bd4e03e6fc30606002c2e91696d
@@ -1,1 +0,0 @@-xuËjÃ0E»öWd“@¤U(ty€Žñ"[?&d@ÖI&6¡ÿ^7Æ©º»™{h®RÍ)¬>ÞßÖp3ˆ>cO ΍5¥•q>1:˜‰¯àՊ›OaÓc±5Î+°¯<ژ·¼¦2¬eòÈ»q9i©Hnªœø_ªÁ-oYˍµ|ëˆ_ɁÁڃ&ƒÐ.Ël9ÇL†”Óãu¬K¶þ@—@E??ÐnW´¬Û­CςªÀ„Ó™F?-~ɨÂ0Oººƒ_Q‰6ñÄÆÁ7U¦,
− .git/objects/66/08033e85b06e8aa5ef9fe4a115b47500a073f0

binary file changed (73 → absent bytes)

− .git/objects/66/a5ca12ace8806a4001b7082db3bdb8b35267eb

binary file changed (1738 → absent bytes)

+ .git/objects/67/eab6e41d9b7d3b8cce4cfc4b5ad44649fba760 view

binary file changed (absent → 242 bytes)

− .git/objects/68/440e1520af9fb733724b7b81e907a4dab956b8

binary file changed (244 → absent bytes)

− .git/objects/68/afa542d4797b1b25cc5a255fb4ae50014fcc65

binary file changed (7687 → absent bytes)

− .git/objects/69/680bdab02d8e951979477178f796b43bd5df01

binary file changed (54 → absent bytes)

− .git/objects/69/fdad39ac0a3e2e0c9b5347e7e6f666d8f769a4

binary file changed (208 → absent bytes)

+ .git/objects/6b/cae40f355478a4d15eee9b66d161e7a0a12dd2 view

binary file changed (absent → 1133 bytes)

− .git/objects/6c/080e1fa918201ddbbdf7faa8bf8426e590092d

binary file changed (4665 → absent bytes)

− .git/objects/6c/78ae03df6c1bc335f2c3d91ab317967dd10a8b

binary file changed (820 → absent bytes)

− .git/objects/6d/983efc33facf5d019fefdc11c253042f6f1a57

binary file changed (1669 → absent bytes)

+ .git/objects/70/c3238c627e9fc1187b9828d5583a470e9ad056 view

binary file changed (absent → 6697 bytes)

− .git/objects/72/00e3a6391ba70ec0e19bf5e6a7effdbcdd0cf6

binary file changed (83 → absent bytes)

− .git/objects/72/420aa90b49de8264d659eee043ad94c94f08f0

binary file changed (6552 → absent bytes)

− .git/objects/72/44f2a719afed28d42fdd235e91ba80a76bf8c8
@@ -1,2 +0,0 @@-xu=‹Â@†­ó+lÜm®Ä;a‹˜S\›6;awƒ	‡ÿÝhÈ]"±›yŸæ™Ts-›õl¿bÑw¬¢ã	ÄÏAcM©FeœOL†æâ¼¶â¦ÄScØ4ŸØ6Î+ðUy´%&°h3xMeX9ÊV“HErWåÄoi¨>ÕîÙxËZî¬åkG„€øBÖ4„v9[.`Ï9f2¤œž×±.Ùúi\ýüDK¸^Ðb°m·ý}ÕùOä½ü4z˜ÿ“‘ö0îÓrËawð¹ŒJ´‰'6.îYž!
− .git/objects/73/bc3b4c2d2ca70c8a8844a118e3d3db3aeb4d0c
@@ -1,1 +0,0 @@-xŽMNÃ0…Yû³	ÊØñO\q®àŒ'M¤Æn··ÇjaÏjÞÓ÷>i(/Ë\¡Ãþ®fõÒhå$¢¡ÑX´|ÇC«QuÎ:‡P85Qu}gŽŽ|Ï,5£ÖF*K#©8jȁE8Õ)ˆ9}x¹žóñ=q])xÓÂHm”¶Ê9„'4ˆ‚®ßUþ§×÷¿žøš+MNËÐ\Ê¥0Õ9'qÏÃz[PNg.kCò¦°BÆ¼ßç˜v°Öb(qáϹÿ|Ôâ,nÄ»Þd
− .git/objects/76/62906e1fb8860b48907234e40addbfdc4bd604

binary file changed (1292 → absent bytes)

− .git/objects/79/e1abc7ad5a3aa84e9c723999da1a2977b07839

binary file changed (209 → absent bytes)

− .git/objects/7a/7cacc4ba4fa2ca82e2d85abf6b44e7f93625ce

binary file changed (8053 → absent bytes)

− .git/objects/7b/f4417449debcc857e7444b200faf64d6cb58a2

binary file changed (82 → absent bytes)

− .git/objects/80/1751ee70ac4794f625f38c9417be1408ff90df

binary file changed (14473 → absent bytes)

+ .git/objects/80/c8d821ef0e1310670d9383a0164a161ef6d896 view

binary file changed (absent → 6112 bytes)

+ .git/objects/81/64db06d50157acee8946c245af5c82e93298f7 view

binary file changed (absent → 2953 bytes)

− .git/objects/85/af43da1948ffbdf3b1fff153d88119842852b9

binary file changed (172 → absent bytes)

− .git/objects/87/7cdbe88a44c2962bc9bc66e021ad8a9c185268
@@ -1,4 +0,0 @@-xŽM-Â0F]çs%3i’D¼€+×Ó̋´ÕoïÏÜ}ރ/OÃÐW ‡‹:«B-Ž|b£K¹a*1P鬴šJEG–(’˜Ï:Vpر[¢ãÔú”SüèˆJÑ«¶¹iÑ𽞦öÓøb8Üû|†µ|áqݎZo™/ºúŒ
`ã	R°°´ÞZ“«þW›ˆ-<ç¾ê‘æ
vúI—
− .git/objects/88/37a8a5670a0e0375d119c9d20d4353b5913a03

binary file changed (1666 → absent bytes)

− .git/objects/88/3ee63aca3703b5c269e0fa4bf0880ade5a9984

binary file changed (8062 → absent bytes)

− .git/objects/89/367b0736b710c6e9c383ef2e019fb07f6cf51c

binary file changed (136 → absent bytes)

− .git/objects/8c/a262164429aa9765dfd2fa47331f3349869329

binary file changed (167 → absent bytes)

+ .git/objects/8f/926490ec9977aaa657c03492a2092cd96ed879 view

binary file changed (absent → 36212 bytes)

+ .git/objects/90/f10f0ae98fa550ed4f3ba96b2726b2fa0f5948 view

binary file changed (absent → 79 bytes)

− .git/objects/95/4c9cf3acd7bc7d436c9548c6d50a7936b3055c

binary file changed (82 → absent bytes)

+ .git/objects/95/530bd9f8b33aa4403e449541aca8782fcf58d7 view

binary file changed (absent → 107426 bytes)

− .git/objects/96/325c6d0739c4a2f762fb0d8e9f6de13202272d

binary file changed (245 → absent bytes)

+ .git/objects/97/d0dbaf178d03770a923bdd241b477208b116bf view

binary file changed (absent → 1290 bytes)

+ .git/objects/98/70f262ba46f8c63923b4c9d373b5502d534bff view

binary file changed (absent → 73 bytes)

− .git/objects/9b/c107a80e863822b328bf64a795c3ef54e001b1
@@ -1,1 +0,0 @@-x•ŽË
!@=S
hf` 1Æì`†èa?®¬‰Ýk¶Oï]^òÊ<Žn=¤C_U­Š2{,[¡V9QB ˆšK<hõì¸
bYuê6ñÑC$òY$sµU߄"å3úldë÷yµuž>bÏ;ÞÏë¤ýUdÑÓO.(@ÈڣΙ²ßuý·37•Qí¶TéZ͔CDÅ
+ .git/objects/9d/73156b57d5c16c87c54244219f4be0df7a4d9c view

binary file changed (absent → 106 bytes)

− .git/objects/9d/78fa8fa693d03da04df9e4e1644ed7a5b5d237

binary file changed (4343 → absent bytes)

+ .git/objects/9e/ad253d2b2018bf4a7fb68a0470bb225793668e view

binary file changed (absent → 41122 bytes)

− .git/objects/a1/b5f177c89e03bb1f90afbc3ee699d9c9b063de

binary file changed (6730 → absent bytes)

− .git/objects/a1/bd5bad273b384e804013a76ba56070c418244b

binary file changed (76 → absent bytes)

− .git/objects/a2/e4ba96b68cfb636a59754dc68261df30cc6530

binary file changed (419 → absent bytes)

− .git/objects/a2/f609f631d66e93917a5ace7ad8e9d3e1ff7dcd

binary file changed (1673 → absent bytes)

− .git/objects/a6/92c6ff1f43e7f0e6048501efab53476b451697

binary file changed (83 → absent bytes)

+ .git/objects/a9/232c0b57605a3ad8a06faad4de4c75fde6ba1a view

binary file changed (absent → 4984 bytes)

− .git/objects/a9/2acc695c112526737719cae217919dd26cc8b0

binary file changed (4345 → absent bytes)

− .git/objects/a9/e9e0080f3f5357b79e1034457f57c4c50a61c1

binary file changed (243 → absent bytes)

+ .git/objects/aa/6d02eafa38dd2e10940244f0951551753f3060 view

binary file changed (absent → 2597 bytes)

+ .git/objects/aa/72de41851f42d3f1023c01ae0edc5832c47ebd view

binary file changed (absent → 255 bytes)

+ .git/objects/ab/dbdc6ad3b69bcd244b80ee836215e015658a56 view

binary file changed (absent → 8294 bytes)

− .git/objects/ad/3a905fb36fe44c5f0f7d1bf0e6fab3d36b4d61

binary file changed (914 → absent bytes)

− .git/objects/ad/790ff8bd9f52c7ddfb2b28093a8ff21940e3b6

binary file changed (14671 → absent bytes)

− .git/objects/ad/934e32017310e7df1888d183754ae30fa6c67e

binary file changed (6468 → absent bytes)

+ .git/objects/af/a69c96947b53fb4d8c3f13d2597f42f166b954 view

binary file changed (absent → 1971 bytes)

− .git/objects/b0/9012e440f3043b48b074cdbcc2b592972bdf3f

binary file changed (822 → absent bytes)

− .git/objects/b1/030551f45c9a9ab195759fb1b77e5b346421fb

binary file changed (244 → absent bytes)

+ .git/objects/b1/127851608df2ad2af6966e3558157bf768a183 view

binary file changed (absent → 21353 bytes)

+ .git/objects/b1/3d346f7417c0e401862c9a2473787ff583afd1 view

binary file changed (absent → 482058 bytes)

− .git/objects/b2/4b2515b2d6b41f2101bf7c8ea8e825495d0168

binary file changed (64 → absent bytes)

+ .git/objects/b3/dac8fd881bc1fd8dfe20ab121cfa7344ace238 view

binary file changed (absent → 11887 bytes)

+ .git/objects/b6/af0dcb1cc69563bda463208ca520057e2e11b3 view

binary file changed (absent → 245 bytes)

+ .git/objects/b7/d28caad2a4b8583bee99a026cabed13ed818d6 view

binary file changed (absent → 244 bytes)

− .git/objects/b8/4d39c532ad044ad8b60cee3bc5292061de39a6

binary file changed (988 → absent bytes)

+ .git/objects/b8/724b568863051d8deac9e814dc00174f5281a9 view

binary file changed (absent → 6690 bytes)

− .git/objects/b9/499a0096c52a092b1f7dd5d9ebabfcaeff6f96

binary file changed (215 → absent bytes)

− .git/objects/bc/3466a48bc1ea669750415e2741d93b7f244611

binary file changed (55 → absent bytes)

+ .git/objects/bc/7e70f191403e2d3c97a09b11bcf2476f038a60 view

binary file changed (absent → 664 bytes)

− .git/objects/be/dc2220c923720d40fbf6b318d4b16245c73a55
@@ -1,3 +0,0 @@-xŽK-1]繀ÒI:“ˆ¸ðzƒîØ£ƒLFÇ(x{?Gp÷ª à•i‡f½‹6«ÚŽA]ôÀ}î%…<Jrš!1Yrì„Ì•g­Íf)îã	”º@ÞKð$}‡œr,Aûˆ-àÄ~´ó4ÛÝT_l÷¡\ìúø…çm[µÝ_uõë0ºŒäíÀ”ßǦÿÕæ •Ç¡žÌX>Fê
+ .git/objects/be/ff554d699578a58c434cccfd0b09b8be1b872d view

binary file changed (absent → 270 bytes)

− .git/objects/c1/1295cd418389633d038186b951bd6617e7f6e4

binary file changed (82 → absent bytes)

− .git/objects/c1/7ffce0743beb137344a74f6ae5fb5046c955b1

binary file changed (836 → absent bytes)

− .git/objects/c1/ba0d1945a06d1610f61128dbe0355002d90c2f

binary file changed (185 → absent bytes)

+ .git/objects/c4/3ce9302dcd23cedd4cf553e09c685e528e587d view

binary file changed (absent → 1244 bytes)

+ .git/objects/c5/fc3c5cebdf7ad00414095d73e7f628cd20d4b4 view

binary file changed (absent → 5132 bytes)

− .git/objects/c7/3c5f5403087b409710374014d8d3f3b91489d1

binary file changed (2151 → absent bytes)

− .git/objects/c8/2f114a3376658176753aa69cb1882ff9e34024

binary file changed (2439 → absent bytes)

+ .git/objects/c8/66ed10fcb19f4a2fb6382721b1d617638ba958 view

binary file changed (absent → 9292 bytes)

+ .git/objects/cc/07b21458a8fc31fe26df3ebb0b95a3fb3dabda view

binary file changed (absent → 244 bytes)

− .git/objects/cc/2e92fccfecefca2ba40533b089ace71f5bba65

binary file changed (246 → absent bytes)

+ .git/objects/cd/023c5127ad0de2f22259fb58b303ab534b75b4 view

binary file changed (absent → 4551 bytes)

− .git/objects/cd/b16d7a4d7ce1ddb46f600a67259900ad69e165

binary file changed (560 → absent bytes)

+ .git/objects/ce/7bb09495716bdbad1408f0d4969fdc39100142 view

binary file changed (absent → 244 bytes)

− .git/objects/cf/2b919bc619dd9c7d058d324954872788d208e9

binary file changed (73 → absent bytes)

− .git/objects/cf/aafd13c43ed1207a8a323d7532ac25ce643e36

binary file changed (125 → absent bytes)

+ .git/objects/cf/df673ee21dc78ee19e2f2c4403ff9c81ffa1f4 view

binary file changed (absent → 262 bytes)

− .git/objects/d0/ded1ec1f5916aa8e3df243a7a2e953e49abfb7

binary file changed (8052 → absent bytes)

− .git/objects/d2/ba530f2b786897d2b6b681d5f438efa154b349

binary file changed (83 → absent bytes)

+ .git/objects/d2/cf53ff238e7f97c925f41e114e7100ed259734 view

binary file changed (absent → 1055 bytes)

+ .git/objects/d3/9da1469f1f05215ea3284392a585730464e8de view

binary file changed (absent → 1129 bytes)

− .git/objects/d6/32a8bbb2a5162fba6f031bf3a3a8c58167c034

binary file changed (1267 → absent bytes)

− .git/objects/d7/b27434f174e0dbe05f8bef916760dfee482141

binary file changed (665 → absent bytes)

− .git/objects/d8/a75f45fc2a9f796ef5af34dd139f243f0e1df6

binary file changed (1827 → absent bytes)

− .git/objects/d9/712de374ecf26cff922a1fa3eaf739bfa71471

binary file changed (208 → absent bytes)

+ .git/objects/db/b2c0d96968285e950f91cef5c54071d6e1c387 view

binary file changed (absent → 958 bytes)

− .git/objects/dc/277dc4fd142e9bdf214b89824f0ecd5c5910ec

binary file changed (208 → absent bytes)

− .git/objects/de/0e0984670170b610524acf8c55af1102969233

binary file changed (208 → absent bytes)

− .git/objects/de/184f0f48f080cd952b4906bd6d10ce95d657aa

binary file changed (1817 → absent bytes)

− .git/objects/de/1cc98e65c7c80ceea57473bdadce3f65ea49a7

binary file changed (245 → absent bytes)

+ .git/objects/df/b22ee42cf26aa1d33211add3c6fdc1a8d117be view

binary file changed (absent → 13625 bytes)

− .git/objects/e0/947bc61ee49f66c63fe1c930c899cbd6b1a368

binary file changed (136 → absent bytes)

− .git/objects/e1/a32ab024ca47185d08c172e66b5ae83b6d8ea5

binary file changed (245 → absent bytes)

+ .git/objects/e2/bdf760d4a28556c3c8bdcec92c5cb51c376e8a view

binary file changed (absent → 3615 bytes)

+ .git/objects/e4/61c0ab2477605902ab7a9c42969603f38bd586 view
@@ -0,0 +1,2 @@+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 view

binary file changed (absent → 23186 bytes)

− .git/objects/e6/e7d977a11bc0f0fd2d821ba0faff4eab5e22bd

binary file changed (6458 → absent bytes)

− .git/objects/e7/a64b8b7d8600b944a2d7a5ffff8f590b4199b7

binary file changed (51 → absent bytes)

− .git/objects/ea/e7723c93fc4fd784831416d1f087bed2707fba

binary file changed (244 → absent bytes)

− .git/objects/eb/02119459572b6b0cf79a2f9b4895b3b9764380

binary file changed (6496 → absent bytes)

+ .git/objects/eb/2a0d24372b9029567ea502f851b922edc3f462 view

binary file changed (absent → 4005 bytes)

+ .git/objects/eb/4dfc2eece871c74eeab502a3943f85884afdad view

binary file changed (absent → 28085 bytes)

+ .git/objects/ed/8ca5964cb76d44755cdcb3ed22fd070676ebc7 view

binary file changed (absent → 356 bytes)

− .git/objects/ed/fb8f1378c1c0021ed0a0f847618b4b310c14f1
@@ -1,3 +0,0 @@-x½RÑjÂ0Ýs¿âB_,8Qƒ	JÙÜX7ÛØc‰ö¶bS’ˆŠîߗ¦†ÆMÙÛ^JrÎÍ=çžÛ9ãs
ïn®BØ_ûD¯ÓhzqšöáeÍM‰ «÷]1#R¢ìÃÃ-3|ª¤"Õ…b^)Ü*	þõ—ç…°âùš!4°àl	Á7í7…Í6EtUs¡N‹.ÀƒY‚(Ê+éù´€,›&ÑÛtö™=FoÏ÷I’eNàv4üÝ &-K.v“-dëaIsZ•Ðë
‚>Ð<ð|¬rZ4Þ*Ü(=;ç)¨a˜X`R	ZÃx¬¹†ÑcÿmìXKúí$ìn½N-8Æhg.͵9°.JØcÑÕô¸LÄVÑtnåÚã-"¸­Ì=0šR}WŽ`KiI™Ä&èÖu2) C›Jéš4DénùÔfÂyíDd®v\ý¸+aM©åN¢0̹ðc*kª‹Ø>?4rddÔ™àˆœÙl¼ätnï¸Ðšaqa“†ÑöO=çm¨÷
•uYŒ
− .git/objects/ef/337b7200de9c55fdc48320e19a26e19564d79b

binary file changed (82 → absent bytes)

+ .git/objects/f2/2b65e167b5ea54b84305158d31368b9c10f0a7 view

binary file changed (absent → 7384 bytes)

− .git/objects/f2/9871f63fcf1a8c2cc7ea8d7e2d3505f641c08e

binary file changed (2440 → absent bytes)

+ .git/objects/f2/bba23a1c3bbf6ff9964d90731485399a69b8a9 view

binary file changed (absent → 15403 bytes)

− .git/objects/f4/077bc12e204876fb485cac9bb75ac11a651c0c

binary file changed (82 → absent bytes)

+ .git/objects/f5/602af44f15d195f373445ff4ea9525fe659cdc view

binary file changed (absent → 423 bytes)

− .git/objects/f8/154e00ed87f700e71790ab9966fc8414807b8a
@@ -1,2 +0,0 @@-x•A-1E]÷¹€’L›©·#ÖN«c¼½2žÀ՛÷~îÓT
(ÒÊfU¤I·X|aÏñ“ú8Ž9dF)““§]úçÞÞ»e^÷CS{d¹éæ{ À8¤q †52¢Ë˛鿞;¶jU®ð¸Ä17«
+ .git/objects/fd/70ab6de3c28ef42e6450fabade4e4bbdd5869e view

binary file changed (absent → 21394 bytes)

+ .git/objects/fd/7be762cd1a7bbca6b0eeec10ab260858db340d view

binary file changed (absent → 172 bytes)

− .git/objects/fe/09b1a7385815667ec69a1b68be676d0eb7cf46

binary file changed (4574 → absent bytes)

+ .git/objects/fe/3a486f7a94b12d196d8e17a1fdb728e26dbff3 view

binary file changed (absent → 59 bytes)

− .git/objects/fe/977d416f9d343f9e97002d5664550eeed16ee0

binary file changed (246 → absent bytes)

+ .git/objects/pack/pack-070189755adac644d6a1c3fd2d044e268ceae11e.idx view

binary file changed (absent → 4488 bytes)

+ .git/objects/pack/pack-070189755adac644d6a1c3fd2d044e268ceae11e.pack view

binary file changed (absent → 76560 bytes)

.git/packed-refs view
@@ -1,2 +1,2 @@ # pack-refs with: peeled fully-peeled -9bc107a80e863822b328bf64a795c3ef54e001b1 refs/remotes/origin/master+61d09a71180e5a1a4ea1d893f82a4709d8e79c27 refs/remotes/origin/master
.git/refs/heads/master view
@@ -1,1 +1,1 @@-4afd6ed7d81c474bf5c88c395a584e4f68b039ce+fd7be762cd1a7bbca6b0eeec10ab260858db340d
.git/refs/remotes/origin/master view
@@ -1,1 +1,1 @@-4afd6ed7d81c474bf5c88c395a584e4f68b039ce+40d6be12def8fc357df4248d9943bd3cc1f75f89
Control/Arrow/ArrowP.lhs view
@@ -1,38 +1,38 @@-> {-# LANGUAGE CPP, MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-}--> module Control.Arrow.ArrowP where--> import Control.Arrow-> import Control.Arrow.Operations-#if __GLASGOW_HASKELL__ >= 610-> import Control.Category-> import Prelude hiding ((.), id)-#endif--> newtype ArrowP a p b c = ArrowP { strip :: a b c }--#if __GLASGOW_HASKELL__ >= 610-> instance Category a => Category (ArrowP a p) where->   id = ArrowP id->   ArrowP g . ArrowP f = ArrowP (g . f)--> instance Arrow a => Arrow (ArrowP a p) where->   arr f = ArrowP (arr f)->   first (ArrowP f) = ArrowP (first f)-#else-> instance Arrow a => Arrow (ArrowP a p) where->   arr f = ArrowP (arr f)->   first (ArrowP f) = ArrowP (first f)->   ArrowP f >>> ArrowP g = ArrowP (f >>> g)-#endif--> instance ArrowLoop a => ArrowLoop (ArrowP a p) where->   loop (ArrowP f) = ArrowP (loop f)--> instance ArrowCircuit a => ArrowCircuit (ArrowP a p) where->   delay i = ArrowP (delay i)--> instance ArrowChoice a => ArrowChoice (ArrowP a p) where->   left (ArrowP f) = ArrowP (left f)->   ArrowP f ||| ArrowP g = ArrowP (f ||| g)-+> {-# LANGUAGE CPP, MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-}
+
+> module Control.Arrow.ArrowP where
+
+> import Control.Arrow
+> import Control.Arrow.Operations
+#if __GLASGOW_HASKELL__ >= 610
+> import Control.Category
+> import Prelude hiding ((.), id)
+#endif
+
+> newtype ArrowP a p b c = ArrowP { strip :: a b c }
+
+#if __GLASGOW_HASKELL__ >= 610
+> instance Category a => Category (ArrowP a p) where
+>   id = ArrowP id
+>   ArrowP g . ArrowP f = ArrowP (g . f)
+
+> instance Arrow a => Arrow (ArrowP a p) where
+>   arr f = ArrowP (arr f)
+>   first (ArrowP f) = ArrowP (first f)
+#else
+> instance Arrow a => Arrow (ArrowP a p) where
+>   arr f = ArrowP (arr f)
+>   first (ArrowP f) = ArrowP (first f)
+>   ArrowP f >>> ArrowP g = ArrowP (f >>> g)
+#endif
+
+> instance ArrowLoop a => ArrowLoop (ArrowP a p) where
+>   loop (ArrowP f) = ArrowP (loop f)
+
+> instance ArrowCircuit a => ArrowCircuit (ArrowP a p) where
+>   delay i = ArrowP (delay i)
+
+> instance ArrowChoice a => ArrowChoice (ArrowP a p) where
+>   left (ArrowP f) = ArrowP (left f)
+>   ArrowP f ||| ArrowP g = ArrowP (f ||| g)
+
Control/SF/SF.lhs view
@@ -1,113 +1,113 @@-> {-# LANGUAGE CPP, BangPatterns #-}--> module Control.SF.SF where--#if __GLASGOW_HASKELL__ >= 610-> import Control.Category-> import Prelude hiding ((.), id)-#endif--> import Control.Arrow-> import Control.Arrow.ArrowP-> import Control.Arrow.Operations---> newtype SF a b = SF { runSF :: (a -> (b, SF a b)) }--#if __GLASGOW_HASKELL__ >= 610-> instance Category SF where->   id = SF h where h x = (x, SF h)->   g . f = SF (h f g)->     where->       h f g x =->         let (y, f') = runSF f x->             (z, g') = runSF g y->         in f' `seq` g' `seq` (z, SF (h f' g'))--> instance Arrow SF where->   arr f = g->     where g = SF (\x -> (f x, g))->   first f = SF (g f)->     where->       g f (x, z) = f' `seq` ((y, z), SF (g f'))->         where (y, f') = runSF f x->   f &&& g = SF (h f g)->     where->       h f g x =->         let (y, f') = runSF f x->             (z, g') = runSF g x ->         in ((y, z), SF (h f' g'))->   f *** g = SF (h f g)->     where->       h f g x =->         let (y, f') = runSF f (fst x)->             (z, g') = runSF g (snd x) ->         in ((y, z), SF (h f' g'))-#else-> instance Arrow SF where->   arr f = g->     where g = SF (\x -> (f x, g))->   f >>> g = SF (h f g)->     where->       h f g x =->         let (y, f') = runSF f x->             (z, g') = runSF g y->         in (z, SF (h f' g'))->   first f = SF (g f)->     where->       g f (x, z) = ((y, z), SF (g f'))->         where (y, f') = runSF f x->   f &&& g = SF (h f g)->     where->       h f g x =->         let (y, f') = runSF f x->             (z, g') = runSF g x ->         in ((y, z), SF (h f' g'))->   f *** g = SF (h f g)->     where->       h f g x =->         let (y, f') = runSF f (fst x)->             (z, g') = runSF g (snd x) ->         in ((y, z), SF (h f' g'))-#endif--> instance ArrowLoop SF where->   loop sf = SF (g sf)->     where->       g f x = f' `seq` (y, SF (g f'))->         where ((y, z), f') = runSF f (x, z)--> instance ArrowChoice SF where->    left sf = SF (g sf)->        where ->          g f x = case x of->                    Left a -> let (y, f') = runSF f a in f' `seq` (Left y, SF (g f'))->                    Right b -> (Right b, SF (g f))-> -> instance ArrowCircuit SF where->   delay i = SF (f i)->     where f i x = (i, SF (f x))--> run :: SF a b -> [a] -> [b]-> run _ [] = []-> run (SF f) (x:xs) =->   let (y, f') = f x ->   in y `seq` f' `seq` (y : run f' xs)-> -> unfold :: SF () a -> [a]-> unfold = flip run inp->   where inp = () : inp->-> -> nth :: Int -> SF () a -> a-> nth n (SF f) = x `seq` if n == 0 then x else nth (n - 1) f'->   where (x, f') = f ()-> -> nth' :: Int -> (b, ((), b) -> (a, b)) -> a-> nth' !n (i, f) = n `seq` i `seq` f `seq` aux n i->   where->     aux !n !i = x `seq` i' `seq` if n == 0 then x else aux (n-1) i'->       where (x, i') = f ((), i)-> --+> {-# LANGUAGE CPP, BangPatterns #-}
+
+> module Control.SF.SF where
+
+#if __GLASGOW_HASKELL__ >= 610
+> import Control.Category
+> import Prelude hiding ((.), id)
+#endif
+
+> import Control.Arrow
+> import Control.Arrow.ArrowP
+> import Control.Arrow.Operations
+
+
+> newtype SF a b = SF { runSF :: (a -> (b, SF a b)) }
+
+#if __GLASGOW_HASKELL__ >= 610
+> instance Category SF where
+>   id = SF h where h x = (x, SF h)
+>   g . f = SF (h f g)
+>     where
+>       h f g x =
+>         let (y, f') = runSF f x
+>             (z, g') = runSF g y
+>         in f' `seq` g' `seq` (z, SF (h f' g'))
+
+> instance Arrow SF where
+>   arr f = g
+>     where g = SF (\x -> (f x, g))
+>   first f = SF (g f)
+>     where
+>       g f (x, z) = f' `seq` ((y, z), SF (g f'))
+>         where (y, f') = runSF f x
+>   f &&& g = SF (h f g)
+>     where
+>       h f g x =
+>         let (y, f') = runSF f x
+>             (z, g') = runSF g x 
+>         in ((y, z), SF (h f' g'))
+>   f *** g = SF (h f g)
+>     where
+>       h f g x =
+>         let (y, f') = runSF f (fst x)
+>             (z, g') = runSF g (snd x) 
+>         in ((y, z), SF (h f' g'))
+#else
+> instance Arrow SF where
+>   arr f = g
+>     where g = SF (\x -> (f x, g))
+>   f >>> g = SF (h f g)
+>     where
+>       h f g x =
+>         let (y, f') = runSF f x
+>             (z, g') = runSF g y
+>         in (z, SF (h f' g'))
+>   first f = SF (g f)
+>     where
+>       g f (x, z) = ((y, z), SF (g f'))
+>         where (y, f') = runSF f x
+>   f &&& g = SF (h f g)
+>     where
+>       h f g x =
+>         let (y, f') = runSF f x
+>             (z, g') = runSF g x 
+>         in ((y, z), SF (h f' g'))
+>   f *** g = SF (h f g)
+>     where
+>       h f g x =
+>         let (y, f') = runSF f (fst x)
+>             (z, g') = runSF g (snd x) 
+>         in ((y, z), SF (h f' g'))
+#endif
+
+> instance ArrowLoop SF where
+>   loop sf = SF (g sf)
+>     where
+>       g f x = f' `seq` (y, SF (g f'))
+>         where ((y, z), f') = runSF f (x, z)
+
+> instance ArrowChoice SF where
+>    left sf = SF (g sf)
+>        where 
+>          g f x = case x of
+>                    Left a -> let (y, f') = runSF f a in f' `seq` (Left y, SF (g f'))
+>                    Right b -> (Right b, SF (g f))
+> 
+> instance ArrowCircuit SF where
+>   delay i = SF (f i)
+>     where f i x = (i, SF (f x))
+
+> run :: SF a b -> [a] -> [b]
+> run _ [] = []
+> run (SF f) (x:xs) =
+>   let (y, f') = f x 
+>   in y `seq` f' `seq` (y : run f' xs)
+> 
+> unfold :: SF () a -> [a]
+> unfold = flip run inp
+>   where inp = () : inp
+>
+> 
+> nth :: Int -> SF () a -> a
+> nth n (SF f) = x `seq` if n == 0 then x else nth (n - 1) f'
+>   where (x, f') = f ()
+> 
+> nth' :: Int -> (b, ((), b) -> (a, b)) -> a
+> nth' !n (i, f) = n `seq` i `seq` f `seq` aux n i
+>   where
+>     aux !n !i = x `seq` i' `seq` if n == 0 then x else aux (n-1) i'
+>       where (x, i') = f ((), i)
+> 
+
+
Euterpea.cabal view
@@ -1,5 +1,5 @@ name:           Euterpea
-version:        2.0.0
+version:        2.0.1
 Cabal-Version:  >= 1.8
 license:        BSD3
 license-file:	License
@@ -25,12 +25,7 @@         wide-spectrum DSL, suitable for high-level music representation, 
         algorithmic composition, and analysis; mid-level concepts such as 
         MIDI; and low-level audio processing, sound synthesis, and instrument
-        design.  It also includes a "musical user interface," a set of 
-        computer-music specific GUI widgets such as keyboards, pushbuttons, 
-        sliders, and so on.  The performance of Euterpea is intended to be 
-        as good as any other computer music language, with the goal of being 
-        able to develop real-time applications, using both MIDI and a 
-        high-performance back-end for real-time audio.  
+        design.  
 extra-source-files:
         ReadMe.txt,
         Euterpea/Examples/EnableGUI.hs
@@ -70,6 +65,6 @@         HCodecs == 0.5, 
         stm==2.4.2, 
         containers>=0.5.5.1 && <=0.5.7.1, 
-        bytestring>=0.10.4.0 && <= 0.10.8.0, 
+        bytestring>=0.10.4.0 && <= 0.10.8.0,
         heap == 0.6.0, 
         ghc-prim
Euterpea.lhs view
@@ -1,19 +1,19 @@-> {-# OPTIONS -XFlexibleInstances #-}-> {-# OPTIONS -XTypeSynonymInstances #-}--> module Euterpea (->   module Euterpea.Music,->   module Euterpea.IO.Audio,->   module Euterpea.IO.MIDI,->   module Control.Arrow,->   -- This next line is from Codec.Midi->   exportFile, importFile->   ) where->-> import Euterpea.Music-> import Euterpea.IO.Audio-> import Euterpea.IO.MIDI-> import Control.Arrow-> import Codec.Midi(exportFile, importFile)-> import Control.Arrow.Operations-+> {-# OPTIONS -XFlexibleInstances #-}
+> {-# OPTIONS -XTypeSynonymInstances #-}
+
+> module Euterpea (
+>   module Euterpea.Music,
+>   module Euterpea.IO.Audio,
+>   module Euterpea.IO.MIDI,
+>   module Control.Arrow,
+>   -- This next line is from Codec.Midi
+>   exportFile, importFile
+>   ) where
+>
+> import Euterpea.Music
+> import Euterpea.IO.Audio
+> import Euterpea.IO.MIDI
+> import Control.Arrow
+> import Codec.Midi(exportFile, importFile)
+> import Control.Arrow.Operations
+
Euterpea/IO/Audio/IO.hs view
@@ -1,204 +1,204 @@-{-# LANGUAGE BangPatterns, ExistentialQuantification, -    ScopedTypeVariables, FlexibleContexts #-}--module Euterpea.IO.Audio.IO (-    outFile,  outFileNorm, ---    outFileA, outFileNormA, RecordStatus, -    maxSample) where--import Control.Arrow.ArrowP-import Control.SF.SF-import Euterpea.IO.Audio.Types hiding (Signal)--import Codec.Wav-import Data.Audio-import Data.Array.Unboxed-import Data.Int----import Data.IORef---import Foreign.C---import Foreign.Marshal.Array---import Foreign.Marshal.Utils---import Foreign.Ptr---import Foreign.Storable---import Control.CCA.Types---import Control.Arrow---import Control.Concurrent.MonadIO---import Sound.RtAudio--type Signal clk a b = ArrowP SF clk a b---- | Writes sound to a wave file (.wav)-outFile :: forall a p. (AudioSample a, Clock p) => -           String              -- ^ Filename to write to.-        -> Double              -- ^ Duration of the wav in seconds.-        -> Signal p () a       -- ^ Signal representing the sound.-        -> IO ()-outFile = outFileHelp' id--normList :: [Double] -> [Double]-normList xs = map (/ mx) xs -    where mx = max 1.0 (maximum (map abs xs))---- | Like outFile, but normalizes the output if the amplitude of --- the signal goes above 1.  If the maximum sample is less than--- or equal to 1, the output is not normalized.--- Currently this requires storing the entire output stream in memory--- before writing to the file.-outFileNorm :: forall a p. (AudioSample a, Clock p) => -            String              -- ^ Filename to write to.-         -> Double              -- ^ Duration of the wav in seconds.-         -> Signal p () a       -- ^ Signal representing the sound.-         -> IO ()-outFileNorm = outFileHelp' normList--outFileHelp :: forall a p. (AudioSample a, Clock p) => -            ([Double] -> [Double]) -- ^ Post-processing function.-         -> String              -- ^ Filename to write to.-         -> Double              -- ^ Duration of the wav in seconds.-         -> Signal p () a       -- ^ Signal representing the sound.-         -> IO ()-outFileHelp f filepath dur sf = -  let sr          = rate (undefined :: p)-      numChannels = numChans (undefined :: a)-      numSamples  = truncate (dur * sr) * numChannels-      dat         = map (fromSample . (*0.999)) -                        (f (toSamples dur sf)) :: [Int32]-                    -- multiply by 0.999 to avoid wraparound at 1.0-      array       = listArray (0, numSamples-1) dat-      aud = Audio { sampleRate    = truncate sr,-                    channelNumber = numChannels,-                    sampleData    = array }-  in exportFile filepath aud-  -{--Alternative definition of the above that enforces a clipping behavior when -the value exceeds the [-1.0, 1.0] range. The overflow behavior makes it -very hard to debug sound modeling problems that involve certain waveforms, -such as saw waves. Clipping is also a more common behavior in other audio -software rather than overflowing or wrap-around.--}-  -outFileHelp' :: forall a p. (AudioSample a, Clock p) => -            ([Double] -> [Double]) -- ^ Post-processing function.-         -> String              -- ^ Filename to write to.-         -> Double              -- ^ Duration of the wav in seconds.-         -> Signal p () a       -- ^ Signal representing the sound.-         -> IO ()-outFileHelp' f filepath dur sf = -  let sr          = rate (undefined :: p)-      numChannels = numChans (undefined :: a)-      numSamples  = truncate (dur * sr) * numChannels-      dat         = map (fromSample . (*0.999) . clipFix) -                        (f (toSamples dur sf)) :: [Int32]-      array       = listArray (0, numSamples-1) dat-      aud = Audio { sampleRate    = truncate sr,-                    channelNumber = numChannels,-                    sampleData    = array }-  in exportFile filepath aud where-      clipFix x = if x > 1.0 then 1.0 else if x < -1.0 then -1.0 else x---{--data RecordStatus = Pause | Record | Clear | Write--outFileA :: forall a. AudioSample a => -            String               -- ^ Filename to write to.-         -> Double               -- ^ Sample rate of the incoming signal.-         -> UISF (a, RecordStatus) ()-outFileA = outFileHelpA id--outFileNormA :: forall a. AudioSample a => -                String               -- ^ Filename to write to.-             -> Double               -- ^ Sample rate of the incoming signal.-             -> UISF (a, RecordStatus) ()-outFileNormA = outFileHelpA normList--outFileHelpA :: forall a. AudioSample a => -             ([Double] -> [Double]) -- ^ Post-processing function.-          -> String                 -- ^ Filename to write to.-          -> Double                 -- ^ Sample rate of the incoming signal.-          -> UISF (a, RecordStatus) ()-outFileHelpA f filepath sr = -  let numChannels = numChans (undefined :: a)-      writeWavSink = sink (writeWav f filepath sr numChannels)-  in proc (a, rs) -> do-        rec dat <- delay [] -< dat'-            dat' <- case rs of-                        Pause  -> returnA -< dat-                        Record -> returnA -< a:dat-                        Clear  -> returnA -< []-                        Write  -> do writeWavSink -< dat-                                     returnA -< a:dat-        returnA -< ()--}-{--writeWav :: AudioSample a => ([Double] -> [Double]) -> String -> Double -> Int -> [a] -> UI ()-writeWav f filepath sr numChannels adat = -  let dat         = map (fromSample . (*0.999)) -                        (f (concatMap collapse adat)) :: [Int32]-                    -- multiply by 0.999 to avoid wraparound at 1.0-      array       = listArray (0, (length dat)-1) dat-      aud = Audio { sampleRate    = truncate sr,-                    channelNumber = numChannels,-                    sampleData    = array }-  in liftIO $ exportFile filepath aud--}---  --toSamples :: forall a p. (AudioSample a, Clock p) =>-             Double -> Signal p () a -> [Double]-toSamples dur sf = -  let sr          = rate     (undefined :: p)-      numChannels = numChans (undefined :: a)-      numSamples  = truncate (dur * sr) * numChannels-  in take numSamples $ concatMap collapse $ unfold $ strip sf---- | Compute the maximum sample of an SF in the first 'dur' seconds.-maxSample :: forall a p. (AudioSample a, Clock p) =>-             Double -> Signal p () a -> Double-maxSample dur sf = maximum (map abs (toSamples dur sf))---{--chunk !nFrames !(i, f) ref buf = nFrames `seq` i `seq` f `seq` aux nFrames i -    where aux !n !i = x `seq` i `seq` i' `seq`-                       if n == 0 then do-                                  writeIORef ref i-                                  return ()-                       else do-                        pokeElemOff buf (fromIntegral nFrames-n) (realToFrac x)-                        aux (n-1) i'-              where (x, i') = f ((), i)-{-# INLINE [0] chunk #-}--chunkify !i !f !secs = do-  --userData <- new i-  ref <- newIORef i-  let cb :: RtAudioCallback -      cb oBuf iBuf nFrames nSecs status userData = do-                      -                      lastState <- readIORef ref-                      -- Fill output buffer with nFrames of samples-                      chunk (fromIntegral nFrames) (lastState,f) ref oBuf-                      if secs < (realToFrac nSecs) then return 2 else return 0-                              -                                                          -  mkAudioCallback cb                                 ----playPure :: Show b => Double -> (b, ((), b) -> (Double, b)) -> IO ()-playPure !secs !(i, f) = do-  rtaCloseStream-  rtaInitialize-  dev <- rtaGetDefaultOutputDevice-  callback <- chunkify i f secs-  with (StreamParameters dev 1 0) (\params -> do-         rtaOpenStream params nullPtr float64 44100 4096 callback nullPtr nullPtr)-  rtaStartStream-  return ()-  --}+{-# LANGUAGE BangPatterns, ExistentialQuantification, 
+    ScopedTypeVariables, FlexibleContexts #-}
+
+module Euterpea.IO.Audio.IO (
+    outFile,  outFileNorm, 
+--    outFileA, outFileNormA, RecordStatus, 
+    maxSample) where
+
+import Control.Arrow.ArrowP
+import Control.SF.SF
+import Euterpea.IO.Audio.Types hiding (Signal)
+
+import Codec.Wav
+import Data.Audio
+import Data.Array.Unboxed
+import Data.Int
+
+--import Data.IORef
+--import Foreign.C
+--import Foreign.Marshal.Array
+--import Foreign.Marshal.Utils
+--import Foreign.Ptr
+--import Foreign.Storable
+--import Control.CCA.Types
+--import Control.Arrow
+--import Control.Concurrent.MonadIO
+--import Sound.RtAudio
+
+type Signal clk a b = ArrowP SF clk a b
+
+-- | Writes sound to a wave file (.wav)
+outFile :: forall a p. (AudioSample a, Clock p) => 
+           String              -- ^ Filename to write to.
+        -> Double              -- ^ Duration of the wav in seconds.
+        -> Signal p () a       -- ^ Signal representing the sound.
+        -> IO ()
+outFile = outFileHelp' id
+
+normList :: [Double] -> [Double]
+normList xs = map (/ mx) xs 
+    where mx = max 1.0 (maximum (map abs xs))
+
+-- | Like outFile, but normalizes the output if the amplitude of 
+-- the signal goes above 1.  If the maximum sample is less than
+-- or equal to 1, the output is not normalized.
+-- Currently this requires storing the entire output stream in memory
+-- before writing to the file.
+outFileNorm :: forall a p. (AudioSample a, Clock p) => 
+            String              -- ^ Filename to write to.
+         -> Double              -- ^ Duration of the wav in seconds.
+         -> Signal p () a       -- ^ Signal representing the sound.
+         -> IO ()
+outFileNorm = outFileHelp' normList
+
+outFileHelp :: forall a p. (AudioSample a, Clock p) => 
+            ([Double] -> [Double]) -- ^ Post-processing function.
+         -> String              -- ^ Filename to write to.
+         -> Double              -- ^ Duration of the wav in seconds.
+         -> Signal p () a       -- ^ Signal representing the sound.
+         -> IO ()
+outFileHelp f filepath dur sf = 
+  let sr          = rate (undefined :: p)
+      numChannels = numChans (undefined :: a)
+      numSamples  = truncate (dur * sr) * numChannels
+      dat         = map (fromSample . (*0.999)) 
+                        (f (toSamples dur sf)) :: [Int32]
+                    -- multiply by 0.999 to avoid wraparound at 1.0
+      array       = listArray (0, numSamples-1) dat
+      aud = Audio { sampleRate    = truncate sr,
+                    channelNumber = numChannels,
+                    sampleData    = array }
+  in exportFile filepath aud
+  
+{-
+Alternative definition of the above that enforces a clipping behavior when 
+the value exceeds the [-1.0, 1.0] range. The overflow behavior makes it 
+very hard to debug sound modeling problems that involve certain waveforms, 
+such as saw waves. Clipping is also a more common behavior in other audio 
+software rather than overflowing or wrap-around.
+-}
+  
+outFileHelp' :: forall a p. (AudioSample a, Clock p) => 
+            ([Double] -> [Double]) -- ^ Post-processing function.
+         -> String              -- ^ Filename to write to.
+         -> Double              -- ^ Duration of the wav in seconds.
+         -> Signal p () a       -- ^ Signal representing the sound.
+         -> IO ()
+outFileHelp' f filepath dur sf = 
+  let sr          = rate (undefined :: p)
+      numChannels = numChans (undefined :: a)
+      numSamples  = truncate (dur * sr) * numChannels
+      dat         = map (fromSample . (*0.999) . clipFix) 
+                        (f (toSamples dur sf)) :: [Int32]
+      array       = listArray (0, numSamples-1) dat
+      aud = Audio { sampleRate    = truncate sr,
+                    channelNumber = numChannels,
+                    sampleData    = array }
+  in exportFile filepath aud where
+      clipFix x = if x > 1.0 then 1.0 else if x < -1.0 then -1.0 else x
+
+
+{-
+data RecordStatus = Pause | Record | Clear | Write
+
+outFileA :: forall a. AudioSample a => 
+            String               -- ^ Filename to write to.
+         -> Double               -- ^ Sample rate of the incoming signal.
+         -> UISF (a, RecordStatus) ()
+outFileA = outFileHelpA id
+
+outFileNormA :: forall a. AudioSample a => 
+                String               -- ^ Filename to write to.
+             -> Double               -- ^ Sample rate of the incoming signal.
+             -> UISF (a, RecordStatus) ()
+outFileNormA = outFileHelpA normList
+
+outFileHelpA :: forall a. AudioSample a => 
+             ([Double] -> [Double]) -- ^ Post-processing function.
+          -> String                 -- ^ Filename to write to.
+          -> Double                 -- ^ Sample rate of the incoming signal.
+          -> UISF (a, RecordStatus) ()
+outFileHelpA f filepath sr = 
+  let numChannels = numChans (undefined :: a)
+      writeWavSink = sink (writeWav f filepath sr numChannels)
+  in proc (a, rs) -> do
+        rec dat <- delay [] -< dat'
+            dat' <- case rs of
+                        Pause  -> returnA -< dat
+                        Record -> returnA -< a:dat
+                        Clear  -> returnA -< []
+                        Write  -> do writeWavSink -< dat
+                                     returnA -< a:dat
+        returnA -< ()
+-}
+{-
+writeWav :: AudioSample a => ([Double] -> [Double]) -> String -> Double -> Int -> [a] -> UI ()
+writeWav f filepath sr numChannels adat = 
+  let dat         = map (fromSample . (*0.999)) 
+                        (f (concatMap collapse adat)) :: [Int32]
+                    -- multiply by 0.999 to avoid wraparound at 1.0
+      array       = listArray (0, (length dat)-1) dat
+      aud = Audio { sampleRate    = truncate sr,
+                    channelNumber = numChannels,
+                    sampleData    = array }
+  in liftIO $ exportFile filepath aud
+-}
+
+
+  
+
+toSamples :: forall a p. (AudioSample a, Clock p) =>
+             Double -> Signal p () a -> [Double]
+toSamples dur sf = 
+  let sr          = rate     (undefined :: p)
+      numChannels = numChans (undefined :: a)
+      numSamples  = truncate (dur * sr) * numChannels
+  in take numSamples $ concatMap collapse $ unfold $ strip sf
+
+-- | Compute the maximum sample of an SF in the first 'dur' seconds.
+maxSample :: forall a p. (AudioSample a, Clock p) =>
+             Double -> Signal p () a -> Double
+maxSample dur sf = maximum (map abs (toSamples dur sf))
+
+
+{-
+chunk !nFrames !(i, f) ref buf = nFrames `seq` i `seq` f `seq` aux nFrames i 
+    where aux !n !i = x `seq` i `seq` i' `seq`
+                       if n == 0 then do
+                                  writeIORef ref i
+                                  return ()
+                       else do
+                        pokeElemOff buf (fromIntegral nFrames-n) (realToFrac x)
+                        aux (n-1) i'
+              where (x, i') = f ((), i)
+{-# INLINE [0] chunk #-}
+
+chunkify !i !f !secs = do
+  --userData <- new i
+  ref <- newIORef i
+  let cb :: RtAudioCallback 
+      cb oBuf iBuf nFrames nSecs status userData = do
+                      
+                      lastState <- readIORef ref
+                      -- Fill output buffer with nFrames of samples
+                      chunk (fromIntegral nFrames) (lastState,f) ref oBuf
+                      if secs < (realToFrac nSecs) then return 2 else return 0
+                              
+                                                          
+  mkAudioCallback cb                                 
+
+
+
+playPure :: Show b => Double -> (b, ((), b) -> (Double, b)) -> IO ()
+playPure !secs !(i, f) = do
+  rtaCloseStream
+  rtaInitialize
+  dev <- rtaGetDefaultOutputDevice
+  callback <- chunkify i f secs
+  with (StreamParameters dev 1 0) (\params -> do
+         rtaOpenStream params nullPtr float64 44100 4096 callback nullPtr nullPtr)
+  rtaStartStream
+  return ()
+  
+-}
Euterpea/IO/Audio/Types.hs view
@@ -1,51 +1,51 @@-{-# LANGUAGE EmptyDataDecls, FlexibleInstances #-}--module Euterpea.IO.Audio.Types where--import Control.Arrow.ArrowP-import Control.SF.SF---class Clock p where-    rate :: p -> Double  -- sampling rate--data AudRate-data CtrRate--instance Clock AudRate where-    rate _ = 44100--instance Clock CtrRate where-    rate _ = 4410--type AudSF a b  = SigFun AudRate a b-type CtrSF a b  = SigFun CtrRate a b--type Signal clk a b    = ArrowP SF clk a b-type SigFun clk a b    = ArrowP SF clk a b---- Arbitrary number of channels (say, 5.1) can be supported by just adding more--- instances of the AudioSample type class.--class AudioSample a where-    zero :: a-    mix :: a -> a -> a-    collapse :: a -> [Double]-    numChans :: a -> Int   -      -- allows us to reify the number of channels from the type.--instance AudioSample Double where-    zero = 0-    mix = (+)-    collapse a = [a]-    numChans _ = 1--instance AudioSample (Double,Double) where-    zero = (0,0)-    mix (a,b) (c,d) = (a+c,b+d)-    collapse (a,b) = [a,b]-    numChans _ = 2---- Some useful type synonyms:-type Mono p = Signal p () Double-type Stereo p = Signal p () (Double,Double)+{-# LANGUAGE EmptyDataDecls, FlexibleInstances #-}
+
+module Euterpea.IO.Audio.Types where
+
+import Control.Arrow.ArrowP
+import Control.SF.SF
+
+
+class Clock p where
+    rate :: p -> Double  -- sampling rate
+
+data AudRate
+data CtrRate
+
+instance Clock AudRate where
+    rate _ = 44100
+
+instance Clock CtrRate where
+    rate _ = 4410
+
+type AudSF a b  = SigFun AudRate a b
+type CtrSF a b  = SigFun CtrRate a b
+
+type Signal clk a b    = ArrowP SF clk a b
+type SigFun clk a b    = ArrowP SF clk a b
+
+-- Arbitrary number of channels (say, 5.1) can be supported by just adding more
+-- instances of the AudioSample type class.
+
+class AudioSample a where
+    zero :: a
+    mix :: a -> a -> a
+    collapse :: a -> [Double]
+    numChans :: a -> Int   
+      -- allows us to reify the number of channels from the type.
+
+instance AudioSample Double where
+    zero = 0
+    mix = (+)
+    collapse a = [a]
+    numChans _ = 1
+
+instance AudioSample (Double,Double) where
+    zero = (0,0)
+    mix (a,b) (c,d) = (a+c,b+d)
+    collapse (a,b) = [a,b]
+    numChans _ = 2
+
+-- Some useful type synonyms:
+type Mono p = Signal p () Double
+type Stereo p = Signal p () (Double,Double)
Euterpea/IO/MIDI/ExportMidiFile.lhs view
@@ -1,282 +1,282 @@-MIDI File-writing module for use with Euterpea-Donya Quick-Last modified: 19-June-2013--This file fixes some file-writing bugs in Codec.Midi that -prevent some multi-instrument output from showing up correctly. -It defines the function exportMidiFile, which can be used like-Codec.Midi's exportFile function. Additionally, it defines two-functions for writing MIDI files, writeMidi and writeMidiA that-are like test and testA respectively but with an additional file-path argument.--NOTE #1: some of the binary handling should be redone at some -point. Currently, parts of it are using conversion to a String -type, and although it works, it should not be necessary (or at -least a cleaner way should be found).--NOTE #2: many MIDI messages are currently unsupported. The set -of supported messages is limited to those that can be produced by -Euterpea.--> module Euterpea.IO.MIDI.ExportMidiFile->     (exportMidiFile)  where-> import Codec.Midi-> import Numeric-> import Data.Char-> import qualified Data.ByteString as Byte --A standard MIDI file has two main sections: a header and a -series of track chunks. Track chunks each have a track header-section and end with an end-of-track marker. Detailed infomation-on the file format can be found here:--http://faydoc.tripod.com/formats/mid.htm---> makeFile :: Midi -> Byte.ByteString-> makeFile (Midi ft td trs) = ->     let ticksPerQn = ->             case td of TicksPerBeat x -> x->                        TicksPerSecond x y -> ->                            error ("(makeFile) Don't know how "++->                            "to handle TicksPerSecond yet.")->         header = makeHeader ft (length trs) ticksPerQn->         body = map makeTrack trs->     in  Byte.concat (header:body)--============--BUILD FILE HEADER--The standard MIDI file header starts with the following value:-4D 54 68 00 00 00 06 ff ff nn nn dd dd--ff ff is the format of the file: single-track, multi-track, or -multi-track/multi-pattern. Only the first two cases are addressed -here.--nn nn is the number of tracks in the file.--dd dd is the delta-time in ticks for a quarternote or beat.--> midiHeaderConst :: Byte.ByteString-> midiHeaderConst = ->     Byte.pack [0x4D, 0x54, 0x68, 0x64, 0x00, 0x00, 0x00, 0x06] --> type TrackCount = Int-> type TicksPerQN = Int---The MIDI file header is built as described above. --> makeHeader :: FileType -> TrackCount -> TicksPerQN -> Byte.ByteString-> makeHeader ft numTracks ticksPerQn = ->     let ->         ft' = case ft of SingleTrack -> [0x00, 0x00]->                          MultiTrack -> [0x00, 0x01]->                          MultiPattern -> error ("(makeHeader) Don't know "++->                                          "how to handle multi-pattern yet.")->         numTracks' = padByte 2 numTracks->         ticksPerQn' = padByte 2 ticksPerQn->     in  if numTracks > 16 then error ("(makeHeader) Don't know how to "++->                                "handle >16 tracks!")->         else Byte.concat [midiHeaderConst, Byte.pack ft', numTracks', ticksPerQn']--> padByte :: Integral a => Int -> a -> Byte.ByteString-> padByte byteCount i = ->   let b = Byte.pack [fromIntegral i] ->       n = Byte.length b->       padding = Byte.pack $ take (byteCount - n) $ repeat 0x00->   in  if n < byteCount then Byte.concat [padding, b] else b--================--BUILDING TRACKS--A track consists of a track header, event information, and an -end-of-track marker. The track header has the format:--4D 54 72 6B xx xx xx xx--xx xx xx xx is the total number of BYTES in the track that -follows the header. This includes the end marker! This value-is obtained by generating the track first and then generating-its header.--> makeTrack :: Track Ticks -> Byte.ByteString-> makeTrack t = ->     let body = makeTrackBody t->         header = makeTrackHeader body->     in  Byte.concat [header, body]--> trackHeaderConst :: Byte.ByteString-> trackHeaderConst = Byte.pack [0x4D, 0x54, 0x72, 0x6B] --> makeTrackHeader :: Byte.ByteString -> Byte.ByteString-> makeTrackHeader tbody = ->     let len = Byte.length tbody->         f = Byte.pack . map (fromIntegral . binStrToNum . reverse) . ->             breakBinStrs 8 . pad (8*4) '0' . numToBinStr->     in  Byte.concat [trackHeaderConst, f len]--Track events have two components: a variable-length delta-time and-a message. The delta-time is the number of ticks between the last -message and the next one. The format will be: time message time message ...--However, delta-times are tricky things. The fact that they can be -any length requires that they be encoded in a special way. The binary-value of the number is split into 7-bit sections. This splitting -goes from RIGHT TO LEFT (this is not in any documentation I have read,-but was the only way that worked). For n sections, the first start -with a 1 and the last starts with a 0 - thereby indicating the last -byte of the number. The following is an example of the conversion:--192 track ticks = C0 (hex) = 1100 0000 (bin) -==> converts to 8140 (hex)--Split into 7-bit groups:        [1]  [100 0000]-Apply padding:           [000 0001]  [100 0000]-Add flags:              [1000 0001] [0100 0000]-Result as hex               8    1      4    0--> makeTrackBody :: Track Ticks -> Byte.ByteString -> makeTrackBody [] = endOfTrack -- end marker, very important!-> makeTrackBody ((ticks, msg):rest) = ->     let b = msgToBytes msg->         b' = [to7Bits ticks, msgToBytes msg, makeTrackBody rest]->     in  if Byte.length b > 0 then Byte.concat b'             ->         else makeTrackBody rest--The end of track marker is set 96 ticks after the last event in the -track. This offset is arbitrary, but it helps avoid clipping the notes-at the end of a file during playback in a program like Winamp or-Quicktime.--> endOfTrack = Byte.concat [to7Bits 96, Byte.pack [0xFF, 0x2F, 0x00]]--Splitting numbers into 7-bit sections and applying flags is done-by the following process:-- convert to a binary string representation-- pad the number to be full bytes-- split from right to left into groups of 7 and apply flags-- convert each 8-bit chunk back to a byte representation--> to7Bits :: (Integral a, Show a) => a -> Byte.ByteString-> to7Bits =  Byte.pack . map (fromIntegral . binStrToNum . reverse) .->            fixBinStrs . map (padTo 7 . reverse). reverse . ->            breakBinStrs 7 . reverse . padTo 8 . numToBinStr--Pad a binary string to be a multiple of 8 bits:--> padTo :: Int -> String -> String-> padTo i xs = if length xs `mod` i == 0 then xs else padTo i ('0':xs)--Break a string into chunks of length i:--> breakBinStrs :: Int -> String -> [String]-> breakBinStrs i s = ->     if length s <= i then [s] else take i s : breakBinStrs i (drop i s)--Convert a number to a binary string:--> numToBinStr :: (Integral a, Show a) => a -> String-> numToBinStr i = showIntAtBase 2 intToDigit i ""--Convert a binary string to an integer:--> binStrToNum :: String -> Int-> binStrToNum [] = 0-> binStrToNum ('0':xs) = 2* binStrToNum xs-> binStrToNum ('1':xs) = 1 + 2*binStrToNum xs-> binStrToNum _ = error "bad data."--Append flags to a string (note, the string must be BACKWARDS):--> fixBinStrs :: [String] -> [String]-> fixBinStrs xs = ->     let n = length xs->         bits = take (n-1) (repeat '1') ++ "0"->     in  Prelude.zipWith (:) bits xs--Pad a list from the left until it is a fixed length:--> pad :: Int -> a -> [a] -> [a]-> pad b x xs = if length xs >= b then xs else pad b x (x:xs)--Messages have the following encodings:--8x nn vv	Note Off for pitch nn at velocity vv, channel x-9x nn vv	Note On for pitch nn at velocity vv, channel x-Ax nn vv	Key aftertouch for pitch nn at velocity vv, channel x-Bx cc vv	Control Change for controller cc with value vv, channel x-Cx pp		Program Change to patch pp for channel x-Dx cc 		Channel after-touch to cc on channel x-Ex bb tt 	Pitch wheel to value ttbb, channel x (2000 hex is "normal") -            (note: bb are least significant bits, tt are most significant)--Currently, only note on/off, control change, and program change are supported.--There are also META -EVENTS. This are events that have no channel number.-All meta-events have the format--FF xx nn nn dd dd ...--where xx is the command code, and nnnn is the number of bytes in the data (dd).--FF 00 nn ssss		Set track sequence number-FF 01 nn tt...		Text event-FF 02 nn tt...		Copyright info-FF 03 nn tt...		Track name-FF 04 nn tt...		Track instrument name-FF 05 nn tt...		Lyric-FF 06 nn tt...		Marker-FF 07 nn tt...		Cue point-FF 2F 00			END OF TRACK MARKER-FF 51 03 tttttt		Tempo change marker, where tttttt is the microseconds per qn-FF 48 04 nnddccbb	Time signature nn/dd with cc ticks per beat and bb 32nds/qn-FF 59 02 sfmi		Key signature with sf sharps/flats and mi mode in {0,1}--Of these, only the end of track and tempo marker are implemented.--> msgToBytes :: Message -> Byte.ByteString-> msgToBytes (NoteOn c k v) = ->     Byte.concat [Byte.pack [0x90 + fromIntegral c], padByte 1 k, padByte 1 v]-> msgToBytes (NoteOff c k v) = ->     Byte.concat [Byte.pack [0x80 + fromIntegral c], padByte 1 k, padByte 1 v]-> msgToBytes (ProgramChange c p) =  ->     Byte.concat [Byte.pack [0xC0 + fromIntegral c], padByte 1 p]-> msgToBytes (ControlChange c n v) =  ->     Byte.concat [Byte.pack [0xB0 + fromIntegral c], padByte 1 n, padByte 1 v]-> msgToBytes (TempoChange t) = -- META EVENT, HAS NO CHANNEL NUMBER->     Byte.concat [Byte.pack [0xFF, 0x51, 0x03], fixTempo t]-> msgToBytes x = error ("(msgToBytes) Message type not currently "++ ->                "supported: "++show x)--Fix a tempo value to be exactly 3 bytes:--> fixTempo = Byte.pack . map (fromIntegral . binStrToNum . reverse) . ->            breakBinStrs 8 . pad (4*6) '0' . numToBinStr--> exportMidiFile :: FilePath -> Midi -> IO ()-> exportMidiFile fn = Byte.writeFile fn . makeFile---=================--USAGE--The exportMidiFile can now be used as follows in place of Codec.Midi's exportFile:-- writeMidi :: (ToMusic1 a) => FilePath -> Music a -> IO ()- writeMidi fn = exportMidiFile fn . testMidi-- writeMidiA :: (ToMusic1 a) => FilePath -> PMap Note1 -> Context Note1 -> Music a -> IO ()- writeMidiA fn pm con m = exportMidiFile fn $ testMidiA pm con m-- test :: (ToMusic1 a) => Music a -> IO ()- test = exportMidiFile "test.mid" . testMidi- - testA :: ToMusic1 a => PMap Note1 -> Context Note1 -> Music a -> IO ()- testA pm con m = exportMidiFile "test.mid" (testMidiA pm con m)+MIDI File-writing module for use with Euterpea
+Donya Quick
+Last modified: 19-June-2013
+
+This file fixes some file-writing bugs in Codec.Midi that 
+prevent some multi-instrument output from showing up correctly. 
+It defines the function exportMidiFile, which can be used like
+Codec.Midi's exportFile function. Additionally, it defines two
+functions for writing MIDI files, writeMidi and writeMidiA that
+are like test and testA respectively but with an additional file
+path argument.
+
+NOTE #1: some of the binary handling should be redone at some 
+point. Currently, parts of it are using conversion to a String 
+type, and although it works, it should not be necessary (or at 
+least a cleaner way should be found).
+
+NOTE #2: many MIDI messages are currently unsupported. The set 
+of supported messages is limited to those that can be produced by 
+Euterpea.
+
+> module Euterpea.IO.MIDI.ExportMidiFile
+>     (exportMidiFile)  where
+> import Codec.Midi
+> import Numeric
+> import Data.Char
+> import qualified Data.ByteString as Byte 
+
+A standard MIDI file has two main sections: a header and a 
+series of track chunks. Track chunks each have a track header
+section and end with an end-of-track marker. Detailed infomation
+on the file format can be found here:
+
+http://faydoc.tripod.com/formats/mid.htm
+
+
+> makeFile :: Midi -> Byte.ByteString
+> makeFile (Midi ft td trs) = 
+>     let ticksPerQn = 
+>             case td of TicksPerBeat x -> x
+>                        TicksPerSecond x y -> 
+>                            error ("(makeFile) Don't know how "++
+>                            "to handle TicksPerSecond yet.")
+>         header = makeHeader ft (length trs) ticksPerQn
+>         body = map makeTrack trs
+>     in  Byte.concat (header:body)
+
+============
+
+BUILD FILE HEADER
+
+The standard MIDI file header starts with the following value:
+4D 54 68 00 00 00 06 ff ff nn nn dd dd
+
+ff ff is the format of the file: single-track, multi-track, or 
+multi-track/multi-pattern. Only the first two cases are addressed 
+here.
+
+nn nn is the number of tracks in the file.
+
+dd dd is the delta-time in ticks for a quarternote or beat.
+
+> midiHeaderConst :: Byte.ByteString
+> midiHeaderConst = 
+>     Byte.pack [0x4D, 0x54, 0x68, 0x64, 0x00, 0x00, 0x00, 0x06] 
+
+> type TrackCount = Int
+> type TicksPerQN = Int
+
+
+The MIDI file header is built as described above. 
+
+> makeHeader :: FileType -> TrackCount -> TicksPerQN -> Byte.ByteString
+> makeHeader ft numTracks ticksPerQn = 
+>     let 
+>         ft' = case ft of SingleTrack -> [0x00, 0x00]
+>                          MultiTrack -> [0x00, 0x01]
+>                          MultiPattern -> error ("(makeHeader) Don't know "++
+>                                          "how to handle multi-pattern yet.")
+>         numTracks' = padByte 2 numTracks
+>         ticksPerQn' = padByte 2 ticksPerQn
+>     in  if numTracks > 16 then error ("(makeHeader) Don't know how to "++
+>                                "handle >16 tracks!")
+>         else Byte.concat [midiHeaderConst, Byte.pack ft', numTracks', ticksPerQn']
+
+> padByte :: Integral a => Int -> a -> Byte.ByteString
+> padByte byteCount i = 
+>   let b = Byte.pack [fromIntegral i] 
+>       n = Byte.length b
+>       padding = Byte.pack $ take (byteCount - n) $ repeat 0x00
+>   in  if n < byteCount then Byte.concat [padding, b] else b
+
+================
+
+BUILDING TRACKS
+
+A track consists of a track header, event information, and an 
+end-of-track marker. The track header has the format:
+
+4D 54 72 6B xx xx xx xx
+
+xx xx xx xx is the total number of BYTES in the track that 
+follows the header. This includes the end marker! This value
+is obtained by generating the track first and then generating
+its header.
+
+> makeTrack :: Track Ticks -> Byte.ByteString
+> makeTrack t = 
+>     let body = makeTrackBody t
+>         header = makeTrackHeader body
+>     in  Byte.concat [header, body]
+
+> trackHeaderConst :: Byte.ByteString
+> trackHeaderConst = Byte.pack [0x4D, 0x54, 0x72, 0x6B] 
+
+> makeTrackHeader :: Byte.ByteString -> Byte.ByteString
+> makeTrackHeader tbody = 
+>     let len = Byte.length tbody
+>         f = Byte.pack . map (fromIntegral . binStrToNum . reverse) . 
+>             breakBinStrs 8 . pad (8*4) '0' . numToBinStr
+>     in  Byte.concat [trackHeaderConst, f len]
+
+Track events have two components: a variable-length delta-time and
+a message. The delta-time is the number of ticks between the last 
+message and the next one. The format will be: time message time message ...
+
+However, delta-times are tricky things. The fact that they can be 
+any length requires that they be encoded in a special way. The binary
+value of the number is split into 7-bit sections. This splitting 
+goes from RIGHT TO LEFT (this is not in any documentation I have read,
+but was the only way that worked). For n sections, the first start 
+with a 1 and the last starts with a 0 - thereby indicating the last 
+byte of the number. The following is an example of the conversion:
+
+192 track ticks = C0 (hex) = 1100 0000 (bin) 
+==> converts to 8140 (hex)
+
+Split into 7-bit groups:        [1]  [100 0000]
+Apply padding:           [000 0001]  [100 0000]
+Add flags:              [1000 0001] [0100 0000]
+Result as hex               8    1      4    0
+
+> makeTrackBody :: Track Ticks -> Byte.ByteString 
+> makeTrackBody [] = endOfTrack -- end marker, very important!
+> makeTrackBody ((ticks, msg):rest) = 
+>     let b = msgToBytes msg
+>         b' = [to7Bits ticks, msgToBytes msg, makeTrackBody rest]
+>     in  if Byte.length b > 0 then Byte.concat b'             
+>         else makeTrackBody rest
+
+The end of track marker is set 96 ticks after the last event in the 
+track. This offset is arbitrary, but it helps avoid clipping the notes
+at the end of a file during playback in a program like Winamp or
+Quicktime.
+
+> endOfTrack = Byte.concat [to7Bits 96, Byte.pack [0xFF, 0x2F, 0x00]]
+
+Splitting numbers into 7-bit sections and applying flags is done
+by the following process:
+- convert to a binary string representation
+- pad the number to be full bytes
+- split from right to left into groups of 7 and apply flags
+- convert each 8-bit chunk back to a byte representation
+
+> to7Bits :: (Integral a, Show a) => a -> Byte.ByteString
+> to7Bits =  Byte.pack . map (fromIntegral . binStrToNum . reverse) .
+>            fixBinStrs . map (padTo 7 . reverse). reverse . 
+>            breakBinStrs 7 . reverse . padTo 8 . numToBinStr
+
+Pad a binary string to be a multiple of 8 bits:
+
+> padTo :: Int -> String -> String
+> padTo i xs = if length xs `mod` i == 0 then xs else padTo i ('0':xs)
+
+Break a string into chunks of length i:
+
+> breakBinStrs :: Int -> String -> [String]
+> breakBinStrs i s = 
+>     if length s <= i then [s] else take i s : breakBinStrs i (drop i s)
+
+Convert a number to a binary string:
+
+> numToBinStr :: (Integral a, Show a) => a -> String
+> numToBinStr i = showIntAtBase 2 intToDigit i ""
+
+Convert a binary string to an integer:
+
+> binStrToNum :: String -> Int
+> binStrToNum [] = 0
+> binStrToNum ('0':xs) = 2* binStrToNum xs
+> binStrToNum ('1':xs) = 1 + 2*binStrToNum xs
+> binStrToNum _ = error "bad data."
+
+Append flags to a string (note, the string must be BACKWARDS):
+
+> fixBinStrs :: [String] -> [String]
+> fixBinStrs xs = 
+>     let n = length xs
+>         bits = take (n-1) (repeat '1') ++ "0"
+>     in  Prelude.zipWith (:) bits xs
+
+Pad a list from the left until it is a fixed length:
+
+> pad :: Int -> a -> [a] -> [a]
+> pad b x xs = if length xs >= b then xs else pad b x (x:xs)
+
+Messages have the following encodings:
+
+8x nn vv	Note Off for pitch nn at velocity vv, channel x
+9x nn vv	Note On for pitch nn at velocity vv, channel x
+Ax nn vv	Key aftertouch for pitch nn at velocity vv, channel x
+Bx cc vv	Control Change for controller cc with value vv, channel x
+Cx pp		Program Change to patch pp for channel x
+Dx cc 		Channel after-touch to cc on channel x
+Ex bb tt 	Pitch wheel to value ttbb, channel x (2000 hex is "normal") 
+            (note: bb are least significant bits, tt are most significant)
+
+Currently, only note on/off, control change, and program change are supported.
+
+There are also META -EVENTS. This are events that have no channel number.
+All meta-events have the format
+
+FF xx nn nn dd dd ...
+
+where xx is the command code, and nnnn is the number of bytes in the data (dd).
+
+FF 00 nn ssss		Set track sequence number
+FF 01 nn tt...		Text event
+FF 02 nn tt...		Copyright info
+FF 03 nn tt...		Track name
+FF 04 nn tt...		Track instrument name
+FF 05 nn tt...		Lyric
+FF 06 nn tt...		Marker
+FF 07 nn tt...		Cue point
+FF 2F 00			END OF TRACK MARKER
+FF 51 03 tttttt		Tempo change marker, where tttttt is the microseconds per qn
+FF 48 04 nnddccbb	Time signature nn/dd with cc ticks per beat and bb 32nds/qn
+FF 59 02 sfmi		Key signature with sf sharps/flats and mi mode in {0,1}
+
+Of these, only the end of track and tempo marker are implemented.
+
+> msgToBytes :: Message -> Byte.ByteString
+> msgToBytes (NoteOn c k v) = 
+>     Byte.concat [Byte.pack [0x90 + fromIntegral c], padByte 1 k, padByte 1 v]
+> msgToBytes (NoteOff c k v) = 
+>     Byte.concat [Byte.pack [0x80 + fromIntegral c], padByte 1 k, padByte 1 v]
+> msgToBytes (ProgramChange c p) =  
+>     Byte.concat [Byte.pack [0xC0 + fromIntegral c], padByte 1 p]
+> msgToBytes (ControlChange c n v) =  
+>     Byte.concat [Byte.pack [0xB0 + fromIntegral c], padByte 1 n, padByte 1 v]
+> msgToBytes (TempoChange t) = -- META EVENT, HAS NO CHANNEL NUMBER
+>     Byte.concat [Byte.pack [0xFF, 0x51, 0x03], fixTempo t]
+> msgToBytes x = error ("(msgToBytes) Message type not currently "++ 
+>                "supported: "++show x)
+
+Fix a tempo value to be exactly 3 bytes:
+
+> fixTempo = Byte.pack . map (fromIntegral . binStrToNum . reverse) . 
+>            breakBinStrs 8 . pad (4*6) '0' . numToBinStr
+
+> exportMidiFile :: FilePath -> Midi -> IO ()
+> exportMidiFile fn = Byte.writeFile fn . makeFile
+
+
+=================
+
+USAGE
+
+The exportMidiFile can now be used as follows in place of Codec.Midi's exportFile:
+
+ writeMidi :: (ToMusic1 a) => FilePath -> Music a -> IO ()
+ writeMidi fn = exportMidiFile fn . testMidi
+
+ writeMidiA :: (ToMusic1 a) => FilePath -> PMap Note1 -> Context Note1 -> Music a -> IO ()
+ writeMidiA fn pm con m = exportMidiFile fn $ testMidiA pm con m
+
+ test :: (ToMusic1 a) => Music a -> IO ()
+ test = exportMidiFile "test.mid" . testMidi
+ 
+ testA :: ToMusic1 a => PMap Note1 -> Context Note1 -> Music a -> IO ()
+ testA pm con m = exportMidiFile "test.mid" (testMidiA pm con m)
  
Euterpea/IO/MIDI/FromMidi.lhs view
@@ -1,267 +1,267 @@-> module Euterpea.IO.MIDI.FromMidi (fromMidi) where-> import Euterpea.Music-> import Euterpea.IO.MIDI.ToMidi-> import Euterpea.IO.MIDI.GeneralMidi-> import Data.List-> import Codec.Midi---Donya Quick-Last updated 15-Oct-2013.--Changes since last major version (15-Jan-2013):-- makeUPM: (is !! i, 10) changed to (is !! i, 9) for Percussion.-- Instrument numbers <0 are interpreted as Percussion.-- ProgChange 10 x is now assigned (-1) as an instrument number.--KNOWN ISSUES:-- Tempo changes occuring between matching note on/off events may not be -  interpreted optimally. A performance-correct representation rather -  than a score-correct representation could be accomplished by looking -  for these sorts of between-on-off tempo changes when calculating a -  note's duration. -  -This code was originally developed for research purposes and then -adapted for CPSC 431/531 to overcome some problems exhibited by the-original implementation of fromMidi. --This code has functions to read Midi values into an intermediate type,-SimpleMsg, before conversion to Music (Pitch, Volume) to make processing -instrument changes easier. The following features will be retained from -the input file:-- Placement of notes relative to the beat (assumed to be quarternotes).-- The pitch, volume, and instrument of each note.-- Tempo changes indicated by TempoChange MIDI events--Other MIDI controller information is currently not supported. This includes -events such as pitch bends and modulations. For these controllers, there is -no simple way to capture the information in a Music data structure.--The following datatype is for a simplification of MIDI events into simple -On/off events for pitches occurring at different times. There are two -types of events considered: tempo changes and note events. The note events-are represented by tuples of:-- exact onset time, Rational-- absolute pitch, AbsPitch-- volume from 0-127, Volume-- instrument number, Int. The value (-1) is used for Percussion.-- on/off type, NEvent--> data NEvent = On | Off->   deriving (Eq, Show, Ord)--> data SimpleMsg = SE (Rational, AbsPitch, Volume, Int, NEvent) |->               T (Rational, Rational)->   deriving (Eq, Show)-> instance Ord (SimpleMsg) where->     compare (SE(t,p,v,i,e)) (SE(t',p',v',i',e')) = ->         if t<t' then LT else if t>t' then GT else EQ->     compare (T(t,x)) (SE(t',p',v',i',e')) = ->         if t<t' then LT else if t>t' then GT else EQ->     compare (SE(t,p,v,i,e)) (T(t',x)) = ->         if t<t' then LT else if t>t' then GT else EQ->     compare (T(t,x)) (T(t',x')) =->         if t<t' then LT else if t>t' then GT else EQ--The importFile function places track ticks (Ticks) in a format where -each value attached to a message represents the number of ticks that -have passed SINCE THE LAST MESSAGE. The following function will convert -input in that format into a list of pairs where the ticks are absolute. -In otherwords, ticks in the output will represent the exact point in -time of an event. This means that unsupported events (e.g. pitch bend) -can later be filtered out without affecting the timing of support events.--> addTrackTicks :: Int -> [(Ticks, a)] -> [(Ticks, a)]-> addTrackTicks sum [] = []-> addTrackTicks sum ((t,x):ts) = (t+sum,x) : addTrackTicks (t+sum) ts--The following function addresses a ticks to Music duration conversion.--> applyTD :: TimeDiv -> SimpleMsg -> SimpleMsg-> applyTD tdw x = ->     case x of T(t,i) -> T(fixT tdw t, i) ->               SE(t,p,v,i,e) -> SE(fixT tdw t, p, v, i, e) where--> fixT tdw t = ->     case tdw of TicksPerBeat td -> t / (fromIntegral td * 4)->                 TicksPerSecond fps tpf -> t / fromIntegral (fps * tpf)---The midiToEvents function will take a Midi structure (from importFile, -for example) and convert it to a list of lists of SimpleMsgs. Each outer -list represents a track in the original Midi. --> midiToEvents :: Midi -> [[SimpleMsg]]-> midiToEvents m = ->     let ts = map (simplifyTrack 0) $ map (addTrackTicks 0) (tracks m) ->     in  distributeTempos $ map (map (applyTD $ timeDiv m)) ts where ->   simplifyTrack :: Int -> [(Ticks, Message)] -> [SimpleMsg]->   simplifyTrack icur [] = []->   simplifyTrack icur ((t,m):ts) = ->     case m of (NoteOn c p v) -> ->                   SE (fromIntegral t, p, v, icur, On) : simplifyTrack icur ts->               (NoteOff c p v) -> ->                   SE (fromIntegral t, p, v, icur, Off) : simplifyTrack icur ts->               (ProgramChange c p) -> simplifyTrack (if c==9 then (-1) else p) ts ->               (TempoChange x) -> T (fromIntegral t, fromIntegral x) : simplifyTrack icur ts->               _ -> simplifyTrack icur ts ---The first track is the tempo track. It's events need to be distributed-across the other tracks. This function below is called for that purpose-in midiToEvents above.--> distributeTempos :: [[SimpleMsg]] -> [[SimpleMsg]]-> distributeTempos tracks = ->     if length tracks > 1 then map (sort . (head tracks ++)) (tail tracks)->     else tracks -- must be a single-track file with embedded tempo changes.---The eventsToMusic function will convert a list of lists of SimpleMsgs -(output from midiToEvents) to a Music(Pitch,Volume) structure. All -notes will be connected together using the (:=:) constructor. For -example, the first line of "Frere Jaque", which would normally be-written as:--c 5 qn :+: d 5 qn :+: e 5 qn :+: c 5 qn--would actually get represented like this when read in from a MIDI:--	(rest 0 :+: c 5 qn) :=:-      (rest qn :+: d 5 qn) :=:-      (rest hn :+: e 5 qn) :=:-      (rest dhn :+: c 5 qn)--This structure is clearly more complicated than it needs to be.-However, identifying melodic lines and phrases inorder to group the-events in a more musically appropriate manor is non-trivial, since-it requires both phrase and voice identification within an instrument -To see why this is the case, consider a Piano, which may have right -and lef thand lines that might be best separated by :=: at the -outermost level. In a MIDI, however, we are likely to get all of the-events for both hands lumped into the same track. --The parallelized structure is also required for keeping tempo changes-syced between instruments. While MIDI files allow tempo changes to -occur in the middle of a note, Euterpea's Music values do not.-      -Instruments will be grouped at the outermost level. For example, if -there are 2 instruments with music values m1 and m2 repsectively, the-structure would be:--    (instrument i1 m1) :=: (instrument i2 m1)-	-Tempo changes are processed within each instrument.--> eventsToMusic :: [[SimpleMsg]] -> Music (Pitch, Volume)-> eventsToMusic tracks = ->     let tracks' = splitByInstruments tracks -- handle any mid-track program changes->         is = map toInstr $ map getInstrument $ filter (not.null) tracks' -- instruments->         tDef = 500000 -- current tempo, 120bpm as microseconds per qn->     in  chord $ zipWith instrument is $ map (seToMusic tDef) tracks' where->   ->   toInstr :: Int -> InstrumentName->   toInstr i = if i<0 then Percussion else toEnum i ->->   seToMusic :: Rational -> [SimpleMsg] -> Music (Pitch, Volume)->   seToMusic tCurr [] = rest 0->   seToMusic tCurr (e1@(SE(t,p,v,ins,On)):es) = ->     let piMatch (SE(t1,p1,v1,ins1,e1)) = (p1==p && ins1==ins) && e1==Off->         piMatch (T(t1,x)) = False->         is = findIndices piMatch es -- find mactching note-offs->         SE(t1,p1,v1,ins1, e) = es !! (is !! 0) -- pick the first matching note-off->         n = (rest t :+: note (t1-t) (pitch p,v)) -- create a Music note->     in  if v > 0 then -- a zero volume note is silence->              if length is > 0 then n :=: seToMusic tCurr es -- found an off->              else seToMusic tCurr ((e1:es)++[correctOff e1 es]) -- missing off case->         else seToMusic tCurr es->   seToMusic tCurr (e1@(T (t,newTempo)):es) = ->     let t2 = getTime $ head es -- find time of next event after tempo change->         tfact = tCurr / newTempo -- calculate tempo change factor->         es' = map (changeTime (subtract t)) es -- adjust start times->         m = rest t :+: tempo tfact (seToMusic newTempo es')  ->     in  if null es then rest 0 else m where->         changeTime f (SE (t,p,v,i,e)) = SE (f t,p,v,i,e)->         changeTime f (T (t,x)) = T (f t, x)->   seToMusic tCurr (_:es) = seToMusic tCurr es -- ignore note-offs (already handled)---Finding the time of an event.--> getTime (SE(t,p,v,i,e)) = t-> getTime (T (t,x)) = t---Finding the instrument associated with a track. Only the first-instrument label to appear is chosen. If a program change happens-mid-track, it will not be counted.--> getInstrument ((SE(t,p,v,i,e)):xs) = i-> getInstrument ((T x) : xs) = getInstrument xs-> getInstrument [] = -1 -- No instrument assigned---The following function ensure that only one instrument appears in -each list of SimpleMsgs. This is necessary in order to ensure that -instrument assignments occur at the outermost level of the Music.--> splitByInstruments :: [[SimpleMsg]] -> [[SimpleMsg]] -> splitByInstruments [] = []-> splitByInstruments (t:ts) = ->     let i = getInstrument t->         (t',t'') = splitByI i t->         ts' = if or $ map isSE t'' then splitByInstruments (t'':ts) ->               else splitByInstruments ts->     in  if or $ map isSE t' then t' : ts' else ts'--> isSE :: SimpleMsg -> Bool-> isSE (SE xs) = True-> isSE (T i) = False---The splitByI function partitions a stream to select a specific instrument's events.--> splitByI :: Int -> [SimpleMsg] -> ([SimpleMsg],[SimpleMsg])-> splitByI i0 [] = ([],[])-> splitByI i0 (x:xs) = ->     let (ts,fs) = splitByI i0 xs->         f (SE(_,_,_,i1,_)) = i0 == i1->         f _ = False->     in  case x of SE x' -> if f x then (x:ts,fs) else (ts,x:fs)->                   T i -> (x:ts, x:fs) -- add tempos to both streams---This function is an error-handling method for MIDI files which have -mismatched note on/off events. This seems to be common in output from -some software. The solution used here is to assume that the note lasts -until the the time of the last event in the list. --> correctOff (SE(t,p,v,ins,e)) [] = SE(t,p,v,ins,Off)-> correctOff (SE(t,p,v,ins,e)) es = ->     let SE(t1,p1,v1,ins1,e1) = last $ filter isSE es->     in  SE(t1,p,v,ins,Off) ---The fromMidi function wraps the combination of midiToEvents and -eventsToMusic and performs the final conversion to Music1.--> fromMidi :: Midi -> Music1-> fromMidi m = ->     let seList = midiToEvents m->         iNums = filter (>0) $ map getInstrument seList->         upm = makeUPM $ map toEnum iNums->     in  mMap (\(p,v) -> (p, [Volume v])) $ eventsToMusic seList---This function is to correct for the fact that channel 10 is-traditionally reserved for percussion. If there is no percussion,-then channel 10 must remain empty. Channels are indexed from zero -in this representation, so channel 1 is 0, channel 10 is 9, etc.--> makeUPM :: [InstrumentName] -> UserPatchMap-> makeUPM is = ->     case findIndex (==Percussion) is of ->         Nothing -> zip is ([0..8]++[10..]) -- no percussion->         Just i -> (is !! i, 9) : ->                   zip (take i is ++ drop (i+1) is) ([0..8]++[10..])-+> module Euterpea.IO.MIDI.FromMidi (fromMidi) where
+> import Euterpea.Music
+> import Euterpea.IO.MIDI.ToMidi
+> import Euterpea.IO.MIDI.GeneralMidi
+> import Data.List
+> import Codec.Midi
+
+
+Donya Quick
+Last updated 15-Oct-2013.
+
+Changes since last major version (15-Jan-2013):
+- makeUPM: (is !! i, 10) changed to (is !! i, 9) for Percussion.
+- Instrument numbers <0 are interpreted as Percussion.
+- ProgChange 10 x is now assigned (-1) as an instrument number.
+
+KNOWN ISSUES:
+- Tempo changes occuring between matching note on/off events may not be 
+  interpreted optimally. A performance-correct representation rather 
+  than a score-correct representation could be accomplished by looking 
+  for these sorts of between-on-off tempo changes when calculating a 
+  note's duration. 
+  
+This code was originally developed for research purposes and then 
+adapted for CPSC 431/531 to overcome some problems exhibited by the
+original implementation of fromMidi. 
+
+This code has functions to read Midi values into an intermediate type,
+SimpleMsg, before conversion to Music (Pitch, Volume) to make processing 
+instrument changes easier. The following features will be retained from 
+the input file:
+- Placement of notes relative to the beat (assumed to be quarternotes).
+- The pitch, volume, and instrument of each note.
+- Tempo changes indicated by TempoChange MIDI events
+
+Other MIDI controller information is currently not supported. This includes 
+events such as pitch bends and modulations. For these controllers, there is 
+no simple way to capture the information in a Music data structure.
+
+The following datatype is for a simplification of MIDI events into simple 
+On/off events for pitches occurring at different times. There are two 
+types of events considered: tempo changes and note events. The note events
+are represented by tuples of:
+- exact onset time, Rational
+- absolute pitch, AbsPitch
+- volume from 0-127, Volume
+- instrument number, Int. The value (-1) is used for Percussion.
+- on/off type, NEvent
+
+> data NEvent = On | Off
+>   deriving (Eq, Show, Ord)
+
+> data SimpleMsg = SE (Rational, AbsPitch, Volume, Int, NEvent) |
+>               T (Rational, Rational)
+>   deriving (Eq, Show)
+> instance Ord (SimpleMsg) where
+>     compare (SE(t,p,v,i,e)) (SE(t',p',v',i',e')) = 
+>         if t<t' then LT else if t>t' then GT else EQ
+>     compare (T(t,x)) (SE(t',p',v',i',e')) = 
+>         if t<t' then LT else if t>t' then GT else EQ
+>     compare (SE(t,p,v,i,e)) (T(t',x)) = 
+>         if t<t' then LT else if t>t' then GT else EQ
+>     compare (T(t,x)) (T(t',x')) =
+>         if t<t' then LT else if t>t' then GT else EQ
+
+The importFile function places track ticks (Ticks) in a format where 
+each value attached to a message represents the number of ticks that 
+have passed SINCE THE LAST MESSAGE. The following function will convert 
+input in that format into a list of pairs where the ticks are absolute. 
+In otherwords, ticks in the output will represent the exact point in 
+time of an event. This means that unsupported events (e.g. pitch bend) 
+can later be filtered out without affecting the timing of support events.
+
+> addTrackTicks :: Int -> [(Ticks, a)] -> [(Ticks, a)]
+> addTrackTicks sum [] = []
+> addTrackTicks sum ((t,x):ts) = (t+sum,x) : addTrackTicks (t+sum) ts
+
+The following function addresses a ticks to Music duration conversion.
+
+> applyTD :: TimeDiv -> SimpleMsg -> SimpleMsg
+> applyTD tdw x = 
+>     case x of T(t,i) -> T(fixT tdw t, i) 
+>               SE(t,p,v,i,e) -> SE(fixT tdw t, p, v, i, e) where
+
+> fixT tdw t = 
+>     case tdw of TicksPerBeat td -> t / (fromIntegral td * 4)
+>                 TicksPerSecond fps tpf -> t / fromIntegral (fps * tpf)
+
+
+The midiToEvents function will take a Midi structure (from importFile, 
+for example) and convert it to a list of lists of SimpleMsgs. Each outer 
+list represents a track in the original Midi. 
+
+> midiToEvents :: Midi -> [[SimpleMsg]]
+> midiToEvents m = 
+>     let ts = map (simplifyTrack 0) $ map (addTrackTicks 0) (tracks m) 
+>     in  distributeTempos $ map (map (applyTD $ timeDiv m)) ts where 
+>   simplifyTrack :: Int -> [(Ticks, Message)] -> [SimpleMsg]
+>   simplifyTrack icur [] = []
+>   simplifyTrack icur ((t,m):ts) = 
+>     case m of (NoteOn c p v) -> 
+>                   SE (fromIntegral t, p, v, icur, On) : simplifyTrack icur ts
+>               (NoteOff c p v) -> 
+>                   SE (fromIntegral t, p, v, icur, Off) : simplifyTrack icur ts
+>               (ProgramChange c p) -> simplifyTrack (if c==9 then (-1) else p) ts 
+>               (TempoChange x) -> T (fromIntegral t, fromIntegral x) : simplifyTrack icur ts
+>               _ -> simplifyTrack icur ts 
+
+
+The first track is the tempo track. It's events need to be distributed
+across the other tracks. This function below is called for that purpose
+in midiToEvents above.
+
+> distributeTempos :: [[SimpleMsg]] -> [[SimpleMsg]]
+> distributeTempos tracks = 
+>     if length tracks > 1 then map (sort . (head tracks ++)) (tail tracks)
+>     else tracks -- must be a single-track file with embedded tempo changes.
+
+
+The eventsToMusic function will convert a list of lists of SimpleMsgs 
+(output from midiToEvents) to a Music(Pitch,Volume) structure. All 
+notes will be connected together using the (:=:) constructor. For 
+example, the first line of "Frere Jaque", which would normally be
+written as:
+
+c 5 qn :+: d 5 qn :+: e 5 qn :+: c 5 qn
+
+would actually get represented like this when read in from a MIDI:
+
+	(rest 0 :+: c 5 qn) :=:
+      (rest qn :+: d 5 qn) :=:
+      (rest hn :+: e 5 qn) :=:
+      (rest dhn :+: c 5 qn)
+
+This structure is clearly more complicated than it needs to be.
+However, identifying melodic lines and phrases inorder to group the
+events in a more musically appropriate manor is non-trivial, since
+it requires both phrase and voice identification within an instrument 
+To see why this is the case, consider a Piano, which may have right 
+and lef thand lines that might be best separated by :=: at the 
+outermost level. In a MIDI, however, we are likely to get all of the
+events for both hands lumped into the same track. 
+
+The parallelized structure is also required for keeping tempo changes
+syced between instruments. While MIDI files allow tempo changes to 
+occur in the middle of a note, Euterpea's Music values do not.
+      
+Instruments will be grouped at the outermost level. For example, if 
+there are 2 instruments with music values m1 and m2 repsectively, the
+structure would be:
+
+    (instrument i1 m1) :=: (instrument i2 m1)
+	
+Tempo changes are processed within each instrument.
+
+> eventsToMusic :: [[SimpleMsg]] -> Music (Pitch, Volume)
+> eventsToMusic tracks = 
+>     let tracks' = splitByInstruments tracks -- handle any mid-track program changes
+>         is = map toInstr $ map getInstrument $ filter (not.null) tracks' -- instruments
+>         tDef = 500000 -- current tempo, 120bpm as microseconds per qn
+>     in  chord $ zipWith instrument is $ map (seToMusic tDef) tracks' where
+>   
+>   toInstr :: Int -> InstrumentName
+>   toInstr i = if i<0 then Percussion else toEnum i 
+>
+>   seToMusic :: Rational -> [SimpleMsg] -> Music (Pitch, Volume)
+>   seToMusic tCurr [] = rest 0
+>   seToMusic tCurr (e1@(SE(t,p,v,ins,On)):es) = 
+>     let piMatch (SE(t1,p1,v1,ins1,e1)) = (p1==p && ins1==ins) && e1==Off
+>         piMatch (T(t1,x)) = False
+>         is = findIndices piMatch es -- find mactching note-offs
+>         SE(t1,p1,v1,ins1, e) = es !! (is !! 0) -- pick the first matching note-off
+>         n = (rest t :+: note (t1-t) (pitch p,v)) -- create a Music note
+>     in  if v > 0 then -- a zero volume note is silence
+>              if length is > 0 then n :=: seToMusic tCurr es -- found an off
+>              else seToMusic tCurr ((e1:es)++[correctOff e1 es]) -- missing off case
+>         else seToMusic tCurr es
+>   seToMusic tCurr (e1@(T (t,newTempo)):es) = 
+>     let t2 = getTime $ head es -- find time of next event after tempo change
+>         tfact = tCurr / newTempo -- calculate tempo change factor
+>         es' = map (changeTime (subtract t)) es -- adjust start times
+>         m = rest t :+: tempo tfact (seToMusic newTempo es')  
+>     in  if null es then rest 0 else m where
+>         changeTime f (SE (t,p,v,i,e)) = SE (f t,p,v,i,e)
+>         changeTime f (T (t,x)) = T (f t, x)
+>   seToMusic tCurr (_:es) = seToMusic tCurr es -- ignore note-offs (already handled)
+
+
+Finding the time of an event.
+
+> getTime (SE(t,p,v,i,e)) = t
+> getTime (T (t,x)) = t
+
+
+Finding the instrument associated with a track. Only the first
+instrument label to appear is chosen. If a program change happens
+mid-track, it will not be counted.
+
+> getInstrument ((SE(t,p,v,i,e)):xs) = i
+> getInstrument ((T x) : xs) = getInstrument xs
+> getInstrument [] = -1 -- No instrument assigned
+
+
+The following function ensure that only one instrument appears in 
+each list of SimpleMsgs. This is necessary in order to ensure that 
+instrument assignments occur at the outermost level of the Music.
+
+> splitByInstruments :: [[SimpleMsg]] -> [[SimpleMsg]] 
+> splitByInstruments [] = []
+> splitByInstruments (t:ts) = 
+>     let i = getInstrument t
+>         (t',t'') = splitByI i t
+>         ts' = if or $ map isSE t'' then splitByInstruments (t'':ts) 
+>               else splitByInstruments ts
+>     in  if or $ map isSE t' then t' : ts' else ts'
+
+> isSE :: SimpleMsg -> Bool
+> isSE (SE xs) = True
+> isSE (T i) = False
+
+
+The splitByI function partitions a stream to select a specific instrument's events.
+
+> splitByI :: Int -> [SimpleMsg] -> ([SimpleMsg],[SimpleMsg])
+> splitByI i0 [] = ([],[])
+> splitByI i0 (x:xs) = 
+>     let (ts,fs) = splitByI i0 xs
+>         f (SE(_,_,_,i1,_)) = i0 == i1
+>         f _ = False
+>     in  case x of SE x' -> if f x then (x:ts,fs) else (ts,x:fs)
+>                   T i -> (x:ts, x:fs) -- add tempos to both streams
+
+
+This function is an error-handling method for MIDI files which have 
+mismatched note on/off events. This seems to be common in output from 
+some software. The solution used here is to assume that the note lasts 
+until the the time of the last event in the list. 
+
+> correctOff (SE(t,p,v,ins,e)) [] = SE(t,p,v,ins,Off)
+> correctOff (SE(t,p,v,ins,e)) es = 
+>     let SE(t1,p1,v1,ins1,e1) = last $ filter isSE es
+>     in  SE(t1,p,v,ins,Off) 
+
+
+The fromMidi function wraps the combination of midiToEvents and 
+eventsToMusic and performs the final conversion to Music1.
+
+> fromMidi :: Midi -> Music1
+> fromMidi m = 
+>     let seList = midiToEvents m
+>         iNums = filter (>0) $ map getInstrument seList
+>         upm = makeUPM $ map toEnum iNums
+>     in  mMap (\(p,v) -> (p, [Volume v])) $ eventsToMusic seList
+
+
+This function is to correct for the fact that channel 10 is
+traditionally reserved for percussion. If there is no percussion,
+then channel 10 must remain empty. Channels are indexed from zero 
+in this representation, so channel 1 is 0, channel 10 is 9, etc.
+
+> makeUPM :: [InstrumentName] -> UserPatchMap
+> makeUPM is = 
+>     case findIndex (==Percussion) is of 
+>         Nothing -> zip is ([0..8]++[10..]) -- no percussion
+>         Just i -> (is !! i, 9) : 
+>                   zip (take i is ++ drop (i+1) is) ([0..8]++[10..])
+
Euterpea/IO/MIDI/MEvent.lhs view
@@ -43,13 +43,15 @@ >     -- timing musicToMEventss
 >     metro :: Int -> Dur -> DurT
 >     metro setting dur  = 60 / (fromIntegral setting * dur)
->     applyControls :: Music1 -> Music1
->     applyControls (Modify (Tempo r) m) = scaleDurations r $ applyControls m
->     applyControls (Modify (Transpose k) m) = shiftPitches1 k $ applyControls m
->     applyControls (m1 :+: m2) = applyControls m1 :+: applyControls m2
->     applyControls (m1 :=: m2) = applyControls m1 :=: applyControls m2
->     applyControls x = x
 
+> applyControls :: Music1 -> Music1
+> applyControls (Modify (Tempo r) m) = scaleDurations r $ applyControls m
+> applyControls (Modify (Transpose k) m) = shiftPitches1 k $ applyControls m
+> applyControls (Modify x m) = Modify x $ applyControls m
+> applyControls (m1 :+: m2) = applyControls m1 :+: applyControls m2
+> applyControls (m1 :=: m2) = applyControls m1 :=: applyControls m2
+> applyControls x = x
+
 > musicToMEvents :: MContext -> Music1 -> (Performance, DurT)
 > musicToMEvents c@MContext{mcTime=t, mcDur=dt} (Prim (Note d p)) = ([noteToMEvent c d p], d*dt)
 > musicToMEvents c@MContext{mcTime=t, mcDur=dt}  (Prim (Rest d)) = ([], d*dt)
@@ -63,7 +65,9 @@ >     in  (merge evs1 evs2, max d1 d2)
 > musicToMEvents c (Modify (Instrument i) m) = musicToMEvents c{mcInst=i} m
 > musicToMEvents c (Modify (Phrase pas) m) = phraseToMEvents c pas m
-> musicToMEvents c (Modify x m) = musicToMEvents c m -- Transpose and Tempo addressed by applyControls
+> musicToMEvents c (Modify (KeySig x y) m) = musicToMEvents c m -- KeySig causes no change
+> musicToMEvents c (Modify (Custom x) m) = musicToMEvents c m -- Custom cuases no change
+> musicToMEvents c m@(Modify x m') = musicToMEvents c $ applyControls m -- Transpose and Tempo addressed by applyControls
 
 > noteToMEvent :: MContext -> Dur -> (Pitch, [NoteAttribute]) -> MEvent
 > noteToMEvent c@(MContext ct ci cdur cvol) d (p, nas) = 
Euterpea/IO/MIDI/MidiIO.lhs view
@@ -1,665 +1,665 @@--> {-# LANGUAGE GeneralizedNewtypeDeriving #-}-> module Euterpea.IO.MIDI.MidiIO (->   getAllDevices, --isValidInputDevice, isValidOutputDevice, -- Used only by Euterpea.IO.MUI.MidiWidgets->   terminateMidi, initializeMidi, -- Used only by Euterpea.IO.MUI->   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), ->   getTimeNow,->   DeviceInfo(..), InputDeviceID, OutputDeviceID, Message(..), Time,->   unsafeInputID, unsafeOutputID,-> ) where--> 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, ->                        PMEvent (..), PMMsg (PMMsg))-> import Control.Exception (finally)-> import Control.Concurrent-> import Control.Concurrent.STM.TChan-> import Control.Monad.STM (atomically)-> import Data.IORef--> import Data.Bits (shiftR, shiftL, (.|.), (.&.))-> import Data.List (findIndex)-> import Data.Maybe (mapMaybe)-> import qualified Data.Heap as Heap--> import System.IO (hPutStrLn, stderr)-> import System.IO.Unsafe (unsafePerformIO)-> import Control.DeepSeq (NFData)-------------------------------- | Midi Type declarations | -------------------------------> type MidiEvent = (Time, MidiMessage)--> data MidiMessage = ANote { channel :: !Channel, key :: !Key,->                           velocity :: !Velocity, duration :: !Time }->                  | Std Message->   deriving Show--> newtype InputDeviceID  = InputDeviceID  DeviceID->   deriving (Eq, Show, NFData)-> newtype OutputDeviceID = OutputDeviceID DeviceID->   deriving (Eq, Show, NFData)--> unsafeInputID :: Int -> InputDeviceID-> unsafeInputID = InputDeviceID--> unsafeOutputID :: Int -> OutputDeviceID-> unsafeOutputID = OutputDeviceID------------- | Time | -------------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 = do->   t <- time->   return (fromIntegral t / 1000)-------------------------- | Device Functions | -------------------------getAllDevices returns a list of all of the DeviceInfos found.-It calls Port.Midi.getDeviceInfo over all device numbers--> getAllDevices :: IO ([(InputDeviceID, DeviceInfo)], [(OutputDeviceID, DeviceInfo)])-> getAllDevices = do->   n <- countDevices->   deviceInfos <- mapM getDeviceInfo [0..n-1]->   let devs = zip [0..n-1] deviceInfos->   return ([ (InputDeviceID  d, i) | (d,i) <- devs, input  i], ->           [ (OutputDeviceID d, i) | (d,i) <- devs, output i])---isValidInputDevice and isValideOutputDevice check whether the given -devices are respectively valid for input or output.--isValidInputDevice, isValidOutputDevice :: DeviceID -> IO Bool-isValidInputDevice = isValidDevice input-isValidOutputDevice = isValidDevice output-isValidDevice :: (DeviceInfo -> Bool) -> DeviceID -> IO Bool-isValidDevice pred i = do-  n <- countDevices   -  info <- getAllDevices-  return $ -    i >= 0 && i < n && pred (snd $ info !! i)------------------------- | Default devices | ------------------------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 -exists, an error is thrown.--> defaultOutput :: (OutputDeviceID -> a -> IO b) -> a -> IO b-> defaultOutput f a = do->   i <- getDefaultOutputDeviceID->   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->   case i of->     Nothing -> error "No MIDI input device found"->     Just i  -> f (InputDeviceID i) a--------------------------- | Priority Channels | --------------------------The priority channel data type and a constructor for it will be used -by devices.  We define them here.--> data PrioChannel a b = PrioChannel->     { get           :: IO (Heap.MinPrioHeap a b),->       push          :: a -> b -> IO (),->       pop           :: IO (a,b),->       peek          :: IO (Maybe (a,b)) }--> makePriorityChannel :: IO (PrioChannel Time Message)-> makePriorityChannel = do->   heapRef <- newIORef (Heap.empty :: Heap.MinPrioHeap Time Message)->   let get = readIORef heapRef->       push a b = modifyIORef heapRef (Heap.insert (a,b))->       pop = do->         h <- get->         let (a, h') = Heap.extractHead h->         modifyIORef heapRef (\_ -> h')->         return a->       peek = do->         h <- get->         if Heap.isEmpty h ->           then return Nothing ->           else return $ Just $ Heap.head h->         ->   return $ PrioChannel get push pop peek---------------------------- | 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 -the stop function on all elements and clears the mapping).--outDevMap is the global mapping.--> 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, -returns the Port Midi Stream associated with it).--> outPort :: IORef [(OutputDeviceID, PMStream)]-> inPort  :: IORef [(InputDeviceID,  PMStream)]-> outPort = unsafePerformIO (newIORef [])-> inPort  = unsafePerformIO (newIORef [])--> lookupPort :: (Eq deviceid) => IORef [(deviceid, PMStream)] -> deviceid -> IO (Maybe PMStream)-> lookupPort p i = readIORef p >>= (return . lookup i)--> addPort :: IORef [(deviceid, PMStream)] -> (deviceid, PMStream) -> IO ()-> addPort p is = modifyIORef p (is:)------------------------------------------------------ | Global Device Initialization and Termination | -----------------------------------------------------initializeMidi just initializes PortMidi--> initializeMidi :: IO ()-> initializeMidi = do->   e <- initialize->   if e == NoError ->       then return () ->       else reportError "initializeMidi" e--terminateMidi calls the stop function on all elements of outDevMap -and clears the mapping entirely.  It also clears outPort and inPort.--> terminateMidi :: IO ()-> terminateMidi = do->   inits <- readIORef outDevMap->   mapM_ (\(_, (_,_out,stop)) -> stop) inits->   terminate->   modifyIORef outDevMap (const [])->   writeIORef outPort []->   writeIORef inPort []----------------------- | Device Lookup | ----------------------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 ())-> getOutDev devId = do->   inits <- readIORef outDevMap->   case lookup devId inits of->     Just f -> return f->     Nothing -> do->         x <- midiOutRealTime' devId -- Changes made by Donya Quick: this line used to pattern match against Just.->         pChan <- makePriorityChannel->         case x of Just (mout,stop) -> do -- Case statement added.->         				modifyIORef outDevMap ((devId,(pChan,mout,stop)):)->         				return (pChan,mout,stop)->                   Nothing -> return (pChan, const (return ()), return ()) -- Nothing case added-------------------- | 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 -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 ->   case s of->     Nothing -> do->       r <- openInput devId ->       case r of->         Right e -> reportError "pollMidiCB" e->         Left s -> addPort inPort (idid, 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 () ->           else reportError "pollMidiCB" e->         Left l -> do->           now <- getTimeNow->           case mapMaybe (msgToMidi . message) l of->             [] -> return ()->             ms -> callback (now, ms)--> pollMidi :: InputDeviceID -> IO (Maybe (Time, [Message]))-> pollMidi idid@(InputDeviceID devId) = do->   s <- lookupPort inPort idid ->   case s of->     Nothing -> do->       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 ->   where->     input :: PMStream -> IO (Maybe (Time, [Message]))->     input s = do->       e <- readEvents s->       case e of->         Right e -> if e == NoError ->           then return Nothing->           else reportError "pollMIDI" e >> return Nothing->         Left l -> do->           now <- getTimeNow->           case mapMaybe (msgToMidi . message) l of->             [] -> return Nothing->             ms -> return $ Just (now, ms)------------------------------------------------- | 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 -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 -played.  Otherwise, it is queued for later.--> deliverMidiEvent :: OutputDeviceID -> MidiEvent -> IO ()-> deliverMidiEvent devId (t,m) = do->   (pChan, out, _stop) <- getOutDev devId->   now <- getTimeNow->   let deliver t m = do->       if t == 0->         then out (now,m) ->         else push pChan (now+t) m->              ->   case m of->     Std m -> deliver t m->     ANote c k v d -> do->         deliver t     (NoteOn c k v)->         deliver (t+d) (NoteOff c k v)---outputMidi plays all midi events that are waiting in this device's -priority queue whose time to play has come.--> outputMidi :: OutputDeviceID -> IO ()-> outputMidi devId = do->   (pChan, out, _stop) <- getOutDev devId->   let loop = do->         r <- peek pChan->         case r of->           Nothing     -> return ()->           Just (t,m)  -> do->             now <- getTimeNow->             if t <= now ->               then out (now, m) >> pop pChan >> loop->               else return ()->   loop->   return ()----------------------------------------------- | Midi Output for a complete Midi track | ----------------------------------------------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 -the output device.--> playMidi :: OutputDeviceID -> Midi -> IO ()-> playMidi device midi@(Midi _ division _) = do->   let track = toRealTime division (toAbsTime (head (tracks (toSingleTrack midi))))->   out <- midiOutRealTime device->   case out of->     Nothing -> return ()->     Just (out, stop) -> do->       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 ->         then return ()->         else playTrack t0 t out s------------------------- | 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 -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 -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 -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 -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  ->   case s of->     Right e -> reportError "Unable to open output device in midiOutRealTime'" e >> return Nothing->     Left s -> do->       addPort outPort (odid, s)->       return $ Just (process odid, finalize odid)->   where->     process odid (t, msg) = do->       s <- lookupPort outPort odid->       case s of->         Nothing -> error ("midiOutRealTime': port " ++ show odid ++ " is not open for output")->         Just s -> do->           if isTrackEnd msg ->               then return ()->               else case midiEvent msg of->                 Just m  -> writeMsg s t m->                 Nothing -> return ()->     writeMsg s t m = do->               e <- writeShort s (PMEvent m (round (t * 1e3)))->               case e of->                 NoError -> return () ->                 _ -> reportError "midiOutRealTime'" e->     finalize odid = do->       s <- lookupPort outPort odid->       e <- maybe (return NoError) close s->       case e of->         NoError -> return () ->         _ -> reportError "midiOutRealTime'" e---> midiOutRealTime :: OutputDeviceID -> IO (Maybe ((Time, Message) -> IO (), IO ()))-> midiOutRealTime odid@(OutputDeviceID devId) = do->   s <- openOutput devId 1  ->   case s of->     Right e -> reportError "outputMidi" e >> return Nothing->     Left s -> do->       ch <- atomically newTChan ->       wait <- newEmptyMVar->       fin <- newEmptyMVar->       forkIO (pump s ch wait fin)->       return $ Just (output s ch wait, stop ch fin)->   where->     stop ch fin = atomically (unGetTChan ch Nothing) >> takeMVar fin->     output s ch wait evt@(_, m) = do->       atomically $ writeTChan ch (Just evt)->       if isTrackEnd m then takeMVar wait else return ()->     pump s ch wait fin = loop->       where->         loop = do ->           e <- atomically $ readTChan ch->           case e of->             Nothing -> close s >> putMVar fin ()->             Just (t, msg) -> do->               now <- getTimeNow->               if (t > now + 5) ->                 then atomically (unGetTChan ch e) >> threadDelay 10000 >> loop->                 else do ->                   done <- process t msg->                   if done ->                     then waitUntil (t + 1)->                     else loop ->           where->             waitUntil t = do->               now <- getTimeNow->               if t > now ->                 then do->                   threadDelay $ min 10000 (round((t - now) * 1E6)) ->                   empty <- atomically $ isEmptyTChan ch->                   if empty ->                     then waitUntil t->                     else do->                       e <- atomically $ readTChan ch->                       case e of->                         Nothing -> finishup ->                         _ -> waitUntil t->                 else finishup->             finishup = putMVar wait () >> close s >> putMVar fin ()->             process t msg = if isTrackEnd msg ->               then return True ->               else case midiEvent msg of->                 Just m  -> writeMsg t m->                 Nothing -> return False ->             writeMsg t m = do->               e <- writeShort s (PMEvent m (round (t * 1e3)))->               case e of->                 NoError -> return False ->                 BufferOverflow -> putStrLn "overflow" >> threadDelay 10000 >> writeMsg t m->                 _ -> reportError "outputMidi" e >> return True ------------------------- | MIDI Conversion | ------------------------A conversion function from Codec.Midi Messages to PortMidi PMMsgs.--> midiEvent :: Message -> Maybe PMMsg-> midiEvent (NoteOff c p v)         = Just $ PMMsg (128 .|. (fromIntegral c .&. 0xF)) (fromIntegral p) (fromIntegral v)-> midiEvent (NoteOn c p v)          = Just $ PMMsg (144 .|. (fromIntegral c .&. 0xF)) (fromIntegral p) (fromIntegral v)-> midiEvent (KeyPressure c p pr)    = Just $ PMMsg (160 .|. (fromIntegral c .&. 0xF)) (fromIntegral p) (fromIntegral pr)-> midiEvent (ControlChange c cn cv) = Just $ PMMsg (176 .|. (fromIntegral c .&. 0xF)) (fromIntegral cn) (fromIntegral cv)-> midiEvent (ProgramChange c pn)    = Just $ PMMsg (192 .|. (fromIntegral c .&. 0xF)) (fromIntegral pn) 0-> 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 ---A conversion function from PortMidi PMMsgs to Codec.Midi Messages.--> msgToMidi :: PMMsg -> Maybe Message-> msgToMidi (PMMsg m d1 d2) = ->   let k = (m .&. 0xF0) `shiftR` 4->       c = fromIntegral (m .&. 0x0F)->   in case k of->     0x8 -> Just $ NoteOff c (fromIntegral d1) (fromIntegral d2)->     0x9 -> Just $ NoteOn  c (fromIntegral d1) (fromIntegral d2)->     0xA -> Just $ KeyPressure c (fromIntegral d1) (fromIntegral d2)->     0xB -> Just $ ControlChange c (fromIntegral d1) (fromIntegral d2)->     0xC -> Just $ ProgramChange c (fromIntegral d1)->     0xD -> Just $ ChannelPressure c (fromIntegral d1)->     0xE -> Just $ PitchWheel c (fromIntegral (d1 + d2 `shiftL` 8))->     0xF -> Nothing -- SysEx event not handled->     _   -> Nothing------------------------- | Error Reporting | ------------------------> reportError :: String -> PMError -> IO ()-> reportError prompt e = do->   err <- getErrorText e ->   hPutStrLn stderr $ prompt ++ ": " ++  err----------------------------- | Unused Functions | -------------------------> -- Prints all DeviceInfo found by getAllDevices.-> printAllDeviceInfo :: IO ()-> printAllDeviceInfo = do->   (indevs, outdevs) <- getAllDevices->   mapM_ (print . snd) indevs->   mapM_ (print . snd) outdevs---- 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--> playTrackRealTime :: OutputDeviceID -> [(t, Message)] -> IO ()-> playTrackRealTime device track = do->   out <- midiOutRealTime device->   case out of->     Nothing -> return ()->     Just (out, stop) -> finally (playTrack out track) stop->   where->     playTrack out [] = do->       t <- getTimeNow->       out (t, TrackEnd)->     playTrack out (e@(_, m) : s) = do->       t <- getTimeNow->       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 -> -}--> {--> 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  ->       in runTrack' t t0 bps' 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' _ _ _ [] = [] ->-> 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 ->         io = case midiEvent e of->           Just m  -> writeShort s (PMEvent m (t0 + round (t * 1.0E3)))->           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 () ->     Just fin -> do->       track <- getChanContents ch->       done <- newEmptyMVar ->       forkIO (f track >> putMVar done ())  ->       putStrLn "Start recording, hit ENTER when you are done."->       getLine->       fin ->       takeMVar done->       return ()--> midiInRealTime :: DeviceID -> ((Time, Message) -> IO Bool) -> IO (Maybe (IO ()))-> midiInRealTime device callback = do->   r <- openInput device ->   case r of->     Right e -> reportError "midiInRealTime" e >> return Nothing ->     Left s -> do->       fin <- newEmptyMVar->       forkIO (loop Nothing s fin)->       return (Just (putMVar fin () >> putMVar fin ()))->   where->     loop start s fin = do->       done <- tryTakeMVar fin->       t <- getTimeNow->       case done of->         Just _ -> close s >> callback (t, TrackEnd) >> takeMVar fin >> return ()->         Nothing -> do->           e <- readEvents s->           case e of->             Right e -> if e == NoError ->               then threadDelay 1000 >> loop start s fin->               else do->                 reportError "midiInRealTime" e ->                 callback (t, TrackEnd)->                 return ()->             Left l -> do->               t <- getTimeNow->               sendEvts start t l->       where ->         sendEvts start now [] = loop start s fin->         sendEvts start now (e@(PMEvent m t):l) = do->           let t0 = maybe t id start->           case msgToMidi m of->             Just m' -> do->               done <- callback (now + fromIntegral (t - t0) / 1E3, m')->               if done then close s >> return () else sendEvts (Just t0) now l->             Nothing -> sendEvts (Just t0) now l-+
+> {-# LANGUAGE GeneralizedNewtypeDeriving #-}
+> module Euterpea.IO.MIDI.MidiIO (
+>   getAllDevices, --isValidInputDevice, isValidOutputDevice, -- Used only by Euterpea.IO.MUI.MidiWidgets
+>   terminateMidi, initializeMidi, -- Used only by Euterpea.IO.MUI
+>   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), 
+>   getTimeNow,
+>   DeviceInfo(..), InputDeviceID, OutputDeviceID, Message(..), Time,
+>   unsafeInputID, unsafeOutputID,
+> ) where
+
+> 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, 
+>                        PMEvent (..), PMMsg (PMMsg))
+> import Control.Exception (finally)
+> import Control.Concurrent
+> import Control.Concurrent.STM.TChan
+> import Control.Monad.STM (atomically)
+> import Data.IORef
+
+> import Data.Bits (shiftR, shiftL, (.|.), (.&.))
+> import Data.List (findIndex)
+> import Data.Maybe (mapMaybe)
+> import qualified Data.Heap as Heap
+
+> import System.IO (hPutStrLn, stderr)
+> import System.IO.Unsafe (unsafePerformIO)
+> import Control.DeepSeq (NFData)
+
+
+----------------------------
+ | Midi Type declarations | 
+----------------------------
+
+> type MidiEvent = (Time, MidiMessage)
+
+> data MidiMessage = ANote { channel :: !Channel, key :: !Key,
+>                           velocity :: !Velocity, duration :: !Time }
+>                  | Std Message
+>   deriving Show
+
+> newtype InputDeviceID  = InputDeviceID  DeviceID
+>   deriving (Eq, Show, NFData)
+> newtype OutputDeviceID = OutputDeviceID DeviceID
+>   deriving (Eq, Show, NFData)
+
+> unsafeInputID :: Int -> InputDeviceID
+> unsafeInputID = InputDeviceID
+
+> unsafeOutputID :: Int -> OutputDeviceID
+> unsafeOutputID = OutputDeviceID
+
+----------
+ | Time | 
+----------
+
+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 = do
+>   t <- time
+>   return (fromIntegral t / 1000)
+
+
+----------------------
+ | Device Functions | 
+----------------------
+
+getAllDevices returns a list of all of the DeviceInfos found.
+It calls Port.Midi.getDeviceInfo over all device numbers
+
+> getAllDevices :: IO ([(InputDeviceID, DeviceInfo)], [(OutputDeviceID, DeviceInfo)])
+> getAllDevices = do
+>   n <- countDevices
+>   deviceInfos <- mapM getDeviceInfo [0..n-1]
+>   let devs = zip [0..n-1] deviceInfos
+>   return ([ (InputDeviceID  d, i) | (d,i) <- devs, input  i], 
+>           [ (OutputDeviceID d, i) | (d,i) <- devs, output i])
+
+
+isValidInputDevice and isValideOutputDevice check whether the given 
+devices are respectively valid for input or output.
+
+isValidInputDevice, isValidOutputDevice :: DeviceID -> IO Bool
+isValidInputDevice = isValidDevice input
+isValidOutputDevice = isValidDevice output
+isValidDevice :: (DeviceInfo -> Bool) -> DeviceID -> IO Bool
+isValidDevice pred i = do
+  n <- countDevices   
+  info <- getAllDevices
+  return $ 
+    i >= 0 && i < n && pred (snd $ info !! i)
+
+
+---------------------
+ | Default devices | 
+---------------------
+
+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 
+exists, an error is thrown.
+
+> defaultOutput :: (OutputDeviceID -> a -> IO b) -> a -> IO b
+> defaultOutput f a = do
+>   i <- getDefaultOutputDeviceID
+>   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
+>   case i of
+>     Nothing -> error "No MIDI input device found"
+>     Just i  -> f (InputDeviceID i) a
+
+
+-----------------------
+ | Priority Channels | 
+-----------------------
+
+The priority channel data type and a constructor for it will be used 
+by devices.  We define them here.
+
+> data PrioChannel a b = PrioChannel
+>     { get           :: IO (Heap.MinPrioHeap a b),
+>       push          :: a -> b -> IO (),
+>       pop           :: IO (a,b),
+>       peek          :: IO (Maybe (a,b)) }
+
+> makePriorityChannel :: IO (PrioChannel Time Message)
+> makePriorityChannel = do
+>   heapRef <- newIORef (Heap.empty :: Heap.MinPrioHeap Time Message)
+>   let get = readIORef heapRef
+>       push a b = modifyIORef heapRef (Heap.insert (a,b))
+>       pop = do
+>         h <- get
+>         let (a, h') = Heap.extractHead h
+>         modifyIORef heapRef (\_ -> h')
+>         return a
+>       peek = do
+>         h <- get
+>         if Heap.isEmpty h 
+>           then return Nothing 
+>           else return $ Just $ Heap.head h
+>         
+>   return $ PrioChannel get push pop peek
+
+
+------------------------
+ | 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 
+the stop function on all elements and clears the mapping).
+
+outDevMap is the global mapping.
+
+> 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, 
+returns the Port Midi Stream associated with it).
+
+> outPort :: IORef [(OutputDeviceID, PMStream)]
+> inPort  :: IORef [(InputDeviceID,  PMStream)]
+> outPort = unsafePerformIO (newIORef [])
+> inPort  = unsafePerformIO (newIORef [])
+
+> lookupPort :: (Eq deviceid) => IORef [(deviceid, PMStream)] -> deviceid -> IO (Maybe PMStream)
+> lookupPort p i = readIORef p >>= (return . lookup i)
+
+> addPort :: IORef [(deviceid, PMStream)] -> (deviceid, PMStream) -> IO ()
+> addPort p is = modifyIORef p (is:)
+
+
+--------------------------------------------------
+ | Global Device Initialization and Termination | 
+--------------------------------------------------
+
+initializeMidi just initializes PortMidi
+
+> initializeMidi :: IO ()
+> initializeMidi = do
+>   e <- initialize
+>   if e == NoError 
+>       then return () 
+>       else reportError "initializeMidi" e
+
+terminateMidi calls the stop function on all elements of outDevMap 
+and clears the mapping entirely.  It also clears outPort and inPort.
+
+> terminateMidi :: IO ()
+> terminateMidi = do
+>   inits <- readIORef outDevMap
+>   mapM_ (\(_, (_,_out,stop)) -> stop) inits
+>   terminate
+>   modifyIORef outDevMap (const [])
+>   writeIORef outPort []
+>   writeIORef inPort []
+
+
+-------------------
+ | Device Lookup | 
+-------------------
+
+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 ())
+> getOutDev devId = do
+>   inits <- readIORef outDevMap
+>   case lookup devId inits of
+>     Just f -> return f
+>     Nothing -> do
+>         x <- midiOutRealTime' devId -- Changes made by Donya Quick: this line used to pattern match against Just.
+>         pChan <- makePriorityChannel
+>         case x of Just (mout,stop) -> do -- Case statement added.
+>         				modifyIORef outDevMap ((devId,(pChan,mout,stop)):)
+>         				return (pChan,mout,stop)
+>                   Nothing -> return (pChan, const (return ()), return ()) -- Nothing case added
+
+
+----------------
+ | 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 
+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 
+>   case s of
+>     Nothing -> do
+>       r <- openInput devId 
+>       case r of
+>         Right e -> reportError "pollMidiCB" e
+>         Left s -> addPort inPort (idid, 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 () 
+>           else reportError "pollMidiCB" e
+>         Left l -> do
+>           now <- getTimeNow
+>           case mapMaybe (msgToMidi . message) l of
+>             [] -> return ()
+>             ms -> callback (now, ms)
+
+> pollMidi :: InputDeviceID -> IO (Maybe (Time, [Message]))
+> pollMidi idid@(InputDeviceID devId) = do
+>   s <- lookupPort inPort idid 
+>   case s of
+>     Nothing -> do
+>       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 
+>   where
+>     input :: PMStream -> IO (Maybe (Time, [Message]))
+>     input s = do
+>       e <- readEvents s
+>       case e of
+>         Right e -> if e == NoError 
+>           then return Nothing
+>           else reportError "pollMIDI" e >> return Nothing
+>         Left l -> do
+>           now <- getTimeNow
+>           case mapMaybe (msgToMidi . message) l of
+>             [] -> return Nothing
+>             ms -> return $ Just (now, ms)
+
+
+---------------------------------------------
+ | 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 
+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 
+played.  Otherwise, it is queued for later.
+
+> deliverMidiEvent :: OutputDeviceID -> MidiEvent -> IO ()
+> deliverMidiEvent devId (t,m) = do
+>   (pChan, out, _stop) <- getOutDev devId
+>   now <- getTimeNow
+>   let deliver t m = do
+>       if t == 0
+>         then out (now,m) 
+>         else push pChan (now+t) m
+>              
+>   case m of
+>     Std m -> deliver t m
+>     ANote c k v d -> do
+>         deliver t     (NoteOn c k v)
+>         deliver (t+d) (NoteOff c k v)
+
+
+outputMidi plays all midi events that are waiting in this device's 
+priority queue whose time to play has come.
+
+> outputMidi :: OutputDeviceID -> IO ()
+> outputMidi devId = do
+>   (pChan, out, _stop) <- getOutDev devId
+>   let loop = do
+>         r <- peek pChan
+>         case r of
+>           Nothing     -> return ()
+>           Just (t,m)  -> do
+>             now <- getTimeNow
+>             if t <= now 
+>               then out (now, m) >> pop pChan >> loop
+>               else return ()
+>   loop
+>   return ()
+
+
+-------------------------------------------
+ | Midi Output for a complete Midi track | 
+-------------------------------------------
+
+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 
+the output device.
+
+> playMidi :: OutputDeviceID -> Midi -> IO ()
+> playMidi device midi@(Midi _ division _) = do
+>   let track = toRealTime division (toAbsTime (head (tracks (toSingleTrack midi))))
+>   out <- midiOutRealTime device
+>   case out of
+>     Nothing -> return ()
+>     Just (out, stop) -> do
+>       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 
+>         then return ()
+>         else playTrack t0 t out s
+
+
+---------------------
+ | 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 
+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 
+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 
+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 
+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  
+>   case s of
+>     Right e -> reportError "Unable to open output device in midiOutRealTime'" e >> return Nothing
+>     Left s -> do
+>       addPort outPort (odid, s)
+>       return $ Just (process odid, finalize odid)
+>   where
+>     process odid (t, msg) = do
+>       s <- lookupPort outPort odid
+>       case s of
+>         Nothing -> error ("midiOutRealTime': port " ++ show odid ++ " is not open for output")
+>         Just s -> do
+>           if isTrackEnd msg 
+>               then return ()
+>               else case midiEvent msg of
+>                 Just m  -> writeMsg s t m
+>                 Nothing -> return ()
+>     writeMsg s t m = do
+>               e <- writeShort s (PMEvent m (round (t * 1e3)))
+>               case e of
+>                 NoError -> return () 
+>                 _ -> reportError "midiOutRealTime'" e
+>     finalize odid = do
+>       s <- lookupPort outPort odid
+>       e <- maybe (return NoError) close s
+>       case e of
+>         NoError -> return () 
+>         _ -> reportError "midiOutRealTime'" e
+
+
+> midiOutRealTime :: OutputDeviceID -> IO (Maybe ((Time, Message) -> IO (), IO ()))
+> midiOutRealTime odid@(OutputDeviceID devId) = do
+>   s <- openOutput devId 1  
+>   case s of
+>     Right e -> reportError "outputMidi" e >> return Nothing
+>     Left s -> do
+>       ch <- atomically newTChan 
+>       wait <- newEmptyMVar
+>       fin <- newEmptyMVar
+>       forkIO (pump s ch wait fin)
+>       return $ Just (output s ch wait, stop ch fin)
+>   where
+>     stop ch fin = atomically (unGetTChan ch Nothing) >> takeMVar fin
+>     output s ch wait evt@(_, m) = do
+>       atomically $ writeTChan ch (Just evt)
+>       if isTrackEnd m then takeMVar wait else return ()
+>     pump s ch wait fin = loop
+>       where
+>         loop = do 
+>           e <- atomically $ readTChan ch
+>           case e of
+>             Nothing -> close s >> putMVar fin ()
+>             Just (t, msg) -> do
+>               now <- getTimeNow
+>               if (t > now + 5) 
+>                 then atomically (unGetTChan ch e) >> threadDelay 10000 >> loop
+>                 else do 
+>                   done <- process t msg
+>                   if done 
+>                     then waitUntil (t + 1)
+>                     else loop 
+>           where
+>             waitUntil t = do
+>               now <- getTimeNow
+>               if t > now 
+>                 then do
+>                   threadDelay $ min 10000 (round((t - now) * 1E6)) 
+>                   empty <- atomically $ isEmptyTChan ch
+>                   if empty 
+>                     then waitUntil t
+>                     else do
+>                       e <- atomically $ readTChan ch
+>                       case e of
+>                         Nothing -> finishup 
+>                         _ -> waitUntil t
+>                 else finishup
+>             finishup = putMVar wait () >> close s >> putMVar fin ()
+>             process t msg = if isTrackEnd msg 
+>               then return True 
+>               else case midiEvent msg of
+>                 Just m  -> writeMsg t m
+>                 Nothing -> return False 
+>             writeMsg t m = do
+>               e <- writeShort s (PMEvent m (round (t * 1e3)))
+>               case e of
+>                 NoError -> return False 
+>                 BufferOverflow -> putStrLn "overflow" >> threadDelay 10000 >> writeMsg t m
+>                 _ -> reportError "outputMidi" e >> return True 
+
+
+---------------------
+ | MIDI Conversion | 
+---------------------
+
+A conversion function from Codec.Midi Messages to PortMidi PMMsgs.
+
+> midiEvent :: Message -> Maybe PMMsg
+> midiEvent (NoteOff c p v)         = Just $ PMMsg (128 .|. (fromIntegral c .&. 0xF)) (fromIntegral p) (fromIntegral v)
+> midiEvent (NoteOn c p v)          = Just $ PMMsg (144 .|. (fromIntegral c .&. 0xF)) (fromIntegral p) (fromIntegral v)
+> midiEvent (KeyPressure c p pr)    = Just $ PMMsg (160 .|. (fromIntegral c .&. 0xF)) (fromIntegral p) (fromIntegral pr)
+> midiEvent (ControlChange c cn cv) = Just $ PMMsg (176 .|. (fromIntegral c .&. 0xF)) (fromIntegral cn) (fromIntegral cv)
+> midiEvent (ProgramChange c pn)    = Just $ PMMsg (192 .|. (fromIntegral c .&. 0xF)) (fromIntegral pn) 0
+> 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 
+
+
+A conversion function from PortMidi PMMsgs to Codec.Midi Messages.
+
+> msgToMidi :: PMMsg -> Maybe Message
+> msgToMidi (PMMsg m d1 d2) = 
+>   let k = (m .&. 0xF0) `shiftR` 4
+>       c = fromIntegral (m .&. 0x0F)
+>   in case k of
+>     0x8 -> Just $ NoteOff c (fromIntegral d1) (fromIntegral d2)
+>     0x9 -> Just $ NoteOn  c (fromIntegral d1) (fromIntegral d2)
+>     0xA -> Just $ KeyPressure c (fromIntegral d1) (fromIntegral d2)
+>     0xB -> Just $ ControlChange c (fromIntegral d1) (fromIntegral d2)
+>     0xC -> Just $ ProgramChange c (fromIntegral d1)
+>     0xD -> Just $ ChannelPressure c (fromIntegral d1)
+>     0xE -> Just $ PitchWheel c (fromIntegral (d1 + d2 `shiftL` 8))
+>     0xF -> Nothing -- SysEx event not handled
+>     _   -> Nothing
+
+
+---------------------
+ | Error Reporting | 
+---------------------
+
+> reportError :: String -> PMError -> IO ()
+> reportError prompt e = do
+>   err <- getErrorText e 
+>   hPutStrLn stderr $ prompt ++ ": " ++  err
+
+
+
+
+
+----------------------
+ | Unused Functions | 
+----------------------
+
+> -- Prints all DeviceInfo found by getAllDevices.
+> printAllDeviceInfo :: IO ()
+> printAllDeviceInfo = do
+>   (indevs, outdevs) <- getAllDevices
+>   mapM_ (print . snd) indevs
+>   mapM_ (print . snd) outdevs
+
+-- 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
+
+> playTrackRealTime :: OutputDeviceID -> [(t, Message)] -> IO ()
+> playTrackRealTime device track = do
+>   out <- midiOutRealTime device
+>   case out of
+>     Nothing -> return ()
+>     Just (out, stop) -> finally (playTrack out track) stop
+>   where
+>     playTrack out [] = do
+>       t <- getTimeNow
+>       out (t, TrackEnd)
+>     playTrack out (e@(_, m) : s) = do
+>       t <- getTimeNow
+>       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 
+> -}
+
+> {-
+> 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  
+>       in runTrack' t t0 bps' 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' _ _ _ [] = [] 
+>
+> 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 
+>         io = case midiEvent e of
+>           Just m  -> writeShort s (PMEvent m (t0 + round (t * 1.0E3)))
+>           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 () 
+>     Just fin -> do
+>       track <- getChanContents ch
+>       done <- newEmptyMVar 
+>       forkIO (f track >> putMVar done ())  
+>       putStrLn "Start recording, hit ENTER when you are done."
+>       getLine
+>       fin 
+>       takeMVar done
+>       return ()
+
+> midiInRealTime :: DeviceID -> ((Time, Message) -> IO Bool) -> IO (Maybe (IO ()))
+> midiInRealTime device callback = do
+>   r <- openInput device 
+>   case r of
+>     Right e -> reportError "midiInRealTime" e >> return Nothing 
+>     Left s -> do
+>       fin <- newEmptyMVar
+>       forkIO (loop Nothing s fin)
+>       return (Just (putMVar fin () >> putMVar fin ()))
+>   where
+>     loop start s fin = do
+>       done <- tryTakeMVar fin
+>       t <- getTimeNow
+>       case done of
+>         Just _ -> close s >> callback (t, TrackEnd) >> takeMVar fin >> return ()
+>         Nothing -> do
+>           e <- readEvents s
+>           case e of
+>             Right e -> if e == NoError 
+>               then threadDelay 1000 >> loop start s fin
+>               else do
+>                 reportError "midiInRealTime" e 
+>                 callback (t, TrackEnd)
+>                 return ()
+>             Left l -> do
+>               t <- getTimeNow
+>               sendEvts start t l
+>       where 
+>         sendEvts start now [] = loop start s fin
+>         sendEvts start now (e@(PMEvent m t):l) = do
+>           let t0 = maybe t id start
+>           case msgToMidi m of
+>             Just m' -> do
+>               done <- callback (now + fromIntegral (t - t0) / 1E3, m')
+>               if done then close s >> return () else sendEvts (Just t0) now l
+>             Nothing -> sendEvts (Just t0) now l
+
License view
@@ -1,20 +1,20 @@-Copyright (c) 2008-2015 Euterpea authors--This software is provided 'as-is', without any express or implied-warranty.  In no event will the authors be held liable for any damages-arising from the use of this software.--Permission is granted to anyone to use this software for any purpose,-including commercial applications, and to alter it and redistribute it-freely, subject to the following restrictions:--1. The origin of this software must not be misrepresented; you must not-   claim that you wrote the original software.  If you use this software-   in a product, an acknowledgment in the product documentation would-   be appreciated but is not required.--2. Altered source versions must be plainly marked as such, and must not-   be misrepresented as being the original software.--3. This notice may not be removed or altered from any source-   distribution.+Copyright (c) 2008-2015 Euterpea authors
+
+This software is provided 'as-is', without any express or implied
+warranty.  In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+   claim that you wrote the original software.  If you use this software
+   in a product, an acknowledgment in the product documentation would
+   be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and must not
+   be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+   distribution.
ReadMe.txt view
@@ -1,38 +1,38 @@-Version 2.0.0-Last modified: 31-Dec-2015- _____      _                             -|  ___|    | |                            -| |__ _   _| |_ ___ _ __ _ __   ___  __ _ -|  __| | | | __/ _ \ '__| '_ \ / _ \/ _` |-| |__| |_| | ||  __/ |  | |_) |  __/ (_| |-\____/\__,_|\__\___|_|  | .__/ \___|\__,_|-                        | |               -                        |_|               --Authors:-  Paul Hudak-  Eric Cheng-  Hai (Paul) Liu-  Donya Quick -  Dan Winograd-Cort --Maintainers:-  Donya Quick <donyaquick@gmail.com>--Euterpea is a domain-specific language embedded in Haskell for -computer music research, education, and development, providing -both note-level and signal-level abstractions.  It is a descendant -of Haskore and HasSound, and is intended for both educational purposes -as well as serious computer music applications.  Euterpea is a -wide-spectrum DSL, suitable for high-level music representation, -algorithmic composition, and analysis; mid-level concepts such as -MIDI; and low-level audio processing, sound synthesis, and instrument -design.  --See License for licensing information.--Please send questions, comments, and bug reports to Donya Quick:-donyaquick@gmail.com--Installation instructions are available at:+Version 2.0.0
+Last modified: 31-Dec-2015
+ _____      _                             
+|  ___|    | |                            
+| |__ _   _| |_ ___ _ __ _ __   ___  __ _ 
+|  __| | | | __/ _ \ '__| '_ \ / _ \/ _` |
+| |__| |_| | ||  __/ |  | |_) |  __/ (_| |
+\____/\__,_|\__\___|_|  | .__/ \___|\__,_|
+                        | |               
+                        |_|               
+
+Authors:
+  Paul Hudak
+  Eric Cheng
+  Hai (Paul) Liu
+  Donya Quick 
+  Dan Winograd-Cort 
+
+Maintainers:
+  Donya Quick <donyaquick@gmail.com>
+
+Euterpea is a domain-specific language embedded in Haskell for 
+computer music research, education, and development, providing 
+both note-level and signal-level abstractions.  It is a descendant 
+of Haskore and HasSound, and is intended for both educational purposes 
+as well as serious computer music applications.  Euterpea is a 
+wide-spectrum DSL, suitable for high-level music representation, 
+algorithmic composition, and analysis; mid-level concepts such as 
+MIDI; and low-level audio processing, sound synthesis, and instrument 
+design.  
+
+See License for licensing information.
+
+Please send questions, comments, and bug reports to Donya Quick:
+donyaquick@gmail.com
+
+Installation instructions are available at:
 http://www.euterpea.com
Setup.hs view
@@ -1,51 +1,51 @@-import Distribution.Simple-main = defaultMain---- January 18, 2014--- The following setup script uses the CCA preprocessor (ccap) to preprocess --- certain *.as files in the Euterpea code base.  As of January 18, 2014, only --- one file is being preprocessed in this way (Euterpea.IO.Audio.Basics), and --- as some users have had difficulty with installations due to this --- preprocessing step, we are removing it from the installation procedure.--- --- Now, to process *.as files, one can directly use the ArrowWrap module in --- Euterpea.  In ArrowWrap, all files to be preprocessed must be declared in --- the list called fileList.  Then, simply run main.--- --- If this preprocessor is going to be reenabled, or if ArrowWrap is going --- to be used, one must either add haskell-src-exts >= 1.14.0 to the cabal --- build-depends or just install it directly.-{--module Main (main) where--import Distribution.Simple-import Distribution.Simple.PreProcess-import Distribution.Simple.Program-import Distribution.Simple.Utils-import System.Exit--import ArrowWrap--findArrowP verbosity = do-  a <- findProgramLocation verbosity "ccap"-  case a of -    Nothing -> error "Preprocessor ccap not found. Please make sure the \-                     \CCA library is already installed, and ccap is in \-                     \your PATH environment."-    Just p  -> return p--ppArrow bi lbi = PreProcessor {-    platformIndependent = True,-    runPreProcessor = -      mkSimplePreProcessor $ \inFile outFile verbosity ->-        do info verbosity (inFile ++ " has been preprocessed to " ++ outFile)-           arrowp <- findArrowP verbosity-           runArrowP arrowp inFile outFile-  }--myHooks = simpleUserHooks -            { hookedPreProcessors = ("as", ppArrow) : knownSuffixHandlers }--main :: IO ()-main = defaultMainWithHooks myHooks+import Distribution.Simple
+main = defaultMain
+
+-- January 18, 2014
+-- The following setup script uses the CCA preprocessor (ccap) to preprocess 
+-- certain *.as files in the Euterpea code base.  As of January 18, 2014, only 
+-- one file is being preprocessed in this way (Euterpea.IO.Audio.Basics), and 
+-- as some users have had difficulty with installations due to this 
+-- preprocessing step, we are removing it from the installation procedure.
+-- 
+-- Now, to process *.as files, one can directly use the ArrowWrap module in 
+-- Euterpea.  In ArrowWrap, all files to be preprocessed must be declared in 
+-- the list called fileList.  Then, simply run main.
+-- 
+-- If this preprocessor is going to be reenabled, or if ArrowWrap is going 
+-- to be used, one must either add haskell-src-exts >= 1.14.0 to the cabal 
+-- build-depends or just install it directly.
+{-
+module Main (main) where
+
+import Distribution.Simple
+import Distribution.Simple.PreProcess
+import Distribution.Simple.Program
+import Distribution.Simple.Utils
+import System.Exit
+
+import ArrowWrap
+
+findArrowP verbosity = do
+  a <- findProgramLocation verbosity "ccap"
+  case a of 
+    Nothing -> error "Preprocessor ccap not found. Please make sure the \
+                     \CCA library is already installed, and ccap is in \
+                     \your PATH environment."
+    Just p  -> return p
+
+ppArrow bi lbi = PreProcessor {
+    platformIndependent = True,
+    runPreProcessor = 
+      mkSimplePreProcessor $ \inFile outFile verbosity ->
+        do info verbosity (inFile ++ " has been preprocessed to " ++ outFile)
+           arrowp <- findArrowP verbosity
+           runArrowP arrowp inFile outFile
+  }
+
+myHooks = simpleUserHooks 
+            { hookedPreProcessors = ("as", ppArrow) : knownSuffixHandlers }
+
+main :: IO ()
+main = defaultMainWithHooks myHooks
 -}