۬~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ Wadler's force function + guarantees that the parser does not fail. !Thus it makes parsing more lazy. -However if the original parser fails though, then we get an unrecoverable irrefutable pattern error on .  Wadler's force function + guarantees that the parser does not fail. !Thus it makes parsing more lazy. -However if the original parser fails though, then we get an unrecoverable irrefutable pattern error on .  %Shift bitwise to the left and right.  The call  toBase n x takes a given number x and  chops it up, @returning its digits in base b. Its output is in the form of a Fbig-endian list of ints. divMod is used because it gives the correct Grounding for negative numbers. Ex. toBytes 1000 -> toBase 256 1000 -> (256*3) + 232 -> [ 3 , 232 ] GGet only n of the least significant bytes of x. If it takes less than >n digits to express x, then fill the extra digits with zeros. GThe fromBase function converts a list of digits in another base into a single base-10 number. fromBase b [x,y,z] = x*b^2 + y*b^1 + z*b^0 Like  but for big numbers. 7It chops the list into blocks of tractable sizes (e.g.  maxBound::Int).  trunc b n* takes the b least significant bits of n.  splitAt b n< splits a number into a tuple: (before bit b, after bit b).    ?Hugs makes trouble here because it performs UTF-8 conversions. E.g. [255] is output as [195,191] UIt would be easy to replace these routines by FastPackedString(fps).ByteString.Lazy, 2however this introduces a new package dependency. &Types of predefined MIDI controllers.   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~/A MIDI problem is that one cannot uniquely map a MIDI key to a frequency. )The frequency depends on the instrument. I don'7t know if the deviations are defined for General MIDI. 8If this applies one could add transposition information to the use patch map. @For now I have chosen a value that leads to the right frequency "for some piano sound in my setup. +The velocity of an ordinary key stroke and the maximum possible velocity. 764 is given as default value by the MIDI specification and thus we map it to 1.   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!  ~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!    !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~H;The Key Signature specifies a mode, either major or minor. GThe following enumerated type lists all the keys in order of their key !signatures from flats to sharps. (Cf = 7 flats, Gf = 6 flats ... F = 1 flat, C = 0 flats/sharps, G = 1 sharp, ... Cs = 7 sharps.) Useful for transposition. -The datatypes for MIDI Files and MIDI Events GDefault duration of a whole note, in seconds; and the default SetTempo Fvalue, in microseconds per quarter note. Both express the default of 120 beats per minute. An empty MIDI file.  Show the  with one event per line, $suited for comparing MIDIFiles with diff. Can this be replaced by Sound.MIDI.Load.showFile? 9A hack that changes the velocities by a rational factor. Change the time base. )Sort MIDI note events lexicographically. "This is to make MIDI files unique /and robust against changes in the computation. 2In principle Performance.merge should handle this $but due to rounding errors in Float @the order of note events still depends on some internal issues. 7The sample rate of MIDI events should be coarse enough to assert unique results. Old versions of Haskore.Interface.MIDI.Write wrote  and  /once at the beginning of a file in that order. &The current version supports multiple s in a track and thus a # is set immediately before a note. Because of this a  is now always after a . FFor checking equivalence with old MIDI files we can switch this back. HHH1The & monad parses a track of a MIDI File. EIn MIDI, a shortcut is used for long strings of similar MIDI events: FIf a stream of consecutive events all have the same type and channel, Athe type and channel can be omitted for all but the first event. To implement this feature, Rthe parser must keep track of the type and channel of the most recent MIDI Event. This is done by setting a  parser, Jwhich parses the data bytes according to the currently active event type. The main load function. A MIDI file is made of chunks, each of which is either a  header chunk or a  track chunk6. To be correct, it must consist of one header chunk :followed by any number of track chunks, but for robustness's sake we ignore Lany non-header chunks that come before a header chunk. The header tells us 1the number of tracks to come, which is passed to . #Check if a chunk contains a track. Like 7, if a chunk is not a track chunk, it is just ignored. 1There are two ways to mark the end of the track: -The end of the event list and the meta event . 1Thus the end marker is redundant and we remove a  at the end of the track and complain about all s within the event list.   DParse a chunk, whether a header chunk, a track chunk, or otherwise. *A chunk consists of a four-byte type code  (a header is MThd ; a track is MTrk), ,four bytes for the size of the coming data, and the data itself.  CParse a Header Chunk. A header consists of a format (0, 1, or 2), Cthe number of track chunks to come, and the smallest time division ,to be used in reading the rest of the file.  EThe division is implemented thus: the most significant bit is 0 if it's "in ticks per quarter note; 1 if it's an SMPTE value.  FA track is a series of events. Parse a track, stopping when the size  is zero. HEach event is preceded by the delta time: the time in ticks between the Glast event and the current event. Parse a time and an event, ignoring System Exclusive messages. KParse an event. Note that in the case of a regular MIDI Event, the tag is >the status, and we read the first byte of data before we call . LIn the case of a MIDIEvent with running status, we find out the status from the parser (it'<s been nice enough to keep track of it for us), and the tag that we'-ve already gotten is the first byte of data. Simpler version of  , used in the Show functions. GFind out the status (MIDIEvent type and channel) given a byte of data. Parse a MIDI Event. ;Note that since getting the first byte is a little complex ((there are issues with running status), &it has already been handled for us by . Parse a MetaEvent. $ gets a single byte from the input. getN n. returns n characters (bytes) from the input. , , , and  take 1-, 2-, 3-, or H4-byte numbers from the input (respectively), convert the base-256 data "into a single number, and return.  Variable-length quantities" are used often in MIDI notation. +They are represented in the following way: VEach byte (containing 8 bits) uses the 7 least significant bits to store information. VThe most significant bit is used to signal whether or not more information is coming. If it's 1, another byte is coming. If it's 0, that byte is the last one.  1 gets a variable-length quantity from the input. !MThe returned list contains only bytes with the most significant bit cleared. &These are digits of a 128-ary number. QFunctions to show the decoded contents of a MIDI file in an easy-to-read format. "#$These two functions, the % and $ parsers, 5do not combine directly into a single master parser. 9Rather, they should be used to chop parts of a midi file ;up into chunks of bytes which can be outputted separately. (Chop a MIDI file into chunks returning:  list of  chunk-type-contents pairs; and >* leftover slop (should be empty in correctly formatted file) %&'()*The * accumulates a String. ?For all the usual reasons, the String is represented by ShowS.  The function  is the main function  for writing  values to an actual file; $its first argument is the filename: ?MIDI files are first converted to a monadic string computation using the function +,  and then "executed" using ,. +-./'The following functions encode various  elements +into the raw data of a standard MIDI file. 0123456789:FNumbers of variable size are represented by sequences of 7-bit blocks /tagged (in the top bit) with a bit indicating: (1) that more data follows; or !(0) that this is the last block. ;<=      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~7~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:986543210/.-,+*)('&%$#"!      /6543210/.-,+*)('&%$#"!            !"#$%&'()*+,-./01234567~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9889:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~>  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~                                      !"#$%&'()*+,-./0123456789:;<=>?@ABC midi-0.0.5 Sound.MIDI.IOSound.MIDI.EventSound.MIDI.FileSound.MIDI.File.LoadSound.MIDI.File.SaveSound.MIDI.GeneralSound.MIDI.UtilitySound.MIDI.ParserSound.MIDI.ParserStateSound.MIDI.BitSound.MIDI.StringbaseGHC.IO.Handle.FDopenBinaryFile ByteStringwriteBinaryFilestringCharFromBytereadBinaryFilestringByteFromChar Controller Controller7F Controller7E Controller7D Controller7C Controller7B Controller7A Controller79 Controller78 Controller77 Controller76 Controller75 Controller74 Controller73 Controller72 Controller71 Controller70 Controller6F Controller6E Controller6D Controller6C Controller6B Controller6A Controller69 Controller68 Controller67 Controller66RegisteredParameterMSBRegisteredParameterLSBNonRegisteredParameterMSBNonRegisteredParameterLSB DataDecrement DataIncrement PhaserDepth CelesteDepth ChorusDepth TremoloDepthExtDepth Controller5A Controller59 Controller58 Controller57 Controller56 Controller55 Controller54GeneralPurpose8GeneralPurpose7GeneralPurpose6GeneralPurpose5 Controller4F Controller4E Controller4D Controller4C Controller4B Controller4A Controller49 Controller48 Controller47 Controller46Hold2 Controller44 SoftPedal SustenutoPortaSustain Controller3F Controller3E Controller3D Controller3C Controller3B Controller3A Controller39 Controller38 Controller37 Controller36 Controller35 Controller34GeneralPurpose4LSBGeneralPurpose3LSBGeneralPurpose2LSBGeneralPurpose1LSB Controller2F Controller2E Controller2D Controller2C ExpressionLSB PanoramaLSB Controller29 BalanceLSB MainVolumeLSB DataEntryLSBPortamentoTimeLSBFootControlLSB Controller23BreathControlLSB ModulationLSB BankSelectLSB Controller1F Controller1E Controller1D Controller1C Controller1B Controller1A Controller19 Controller18 Controller17 Controller16 Controller15 Controller14GeneralPurpose4MSBGeneralPurpose3MSBGeneralPurpose2MSBGeneralPurpose1MSB Controller0F Controller0E Controller0D Controller0C ExpressionMSB PanoramaMSB Controller09 BalanceMSB MainVolumeMSB DataEntryMSBPortamentoTimeMSBFootControlMSB Controller03BreathControlMSB ModulationMSB BankSelectMSBT MonoAfter PitchBendControl ProgramChange PolyAfterNoteOnNoteOffControllerValuePressurePitchBendRangeChannel fromChannelProgram fromProgramVelocity fromVelocityPitch fromPitchtoPitch toVelocity toProgram toChannel increasePitch subtractPitchzeroKeynormalVelocitymaximumVelocitytoFloatVelocitytoFloatControllerisNoteisNoteOn isNoteOff bankSelect modulation breathControl footControlportamentoTime dataEntry mainVolumebalancepanorama expressiongeneralPurpose1generalPurpose2generalPurpose3generalPurpose4vectorXvectorYModeMinorMajorKeyKeyCsKeyFsKeyBKeyEKeyAKeyDKeyGKeyCKeyFKeyBfKeyEfKeyAfKeyDfKeyGfKeyCf MetaEventUnknownSequencerSpecificKeySigTimeSig SMPTEOffsetSetTempo EndOfTrack MIDIPrefixCuePointMarkerLyric InstrName TrackName Copyright TextEvent SequenceNum SMPTEBits SMPTEFrames SMPTESecs SMPTEMins SMPTEHoursTempoEvent SysExCont SysExStart MIDIEvent ElapsedTime SchedEventTrackDivisionSMPTETicksTypeSerialParallelMixedConsmaybeMIDIEventmaybeMetaEvent defltDurTdefltSTempty showLinesshowTime showEventchangeVelocity resampleTime getTracks sortEventsprogChangeBeforeSetTempofromFile fromStreammaybeFromStreamshowFiletoFiletoStream toOpenStreamDrum OpenTriangle MuteTriangle OpenCuica MuteCuica LowWoodBlock HiWoodBlockClaves LongGuiro ShortGuiro LongWhistle ShortWhistleMaracasCabasaLowAgogo HighAgogo LowTimbale HighTimbaleLowConga OpenHiConga MuteHiCongaLowBongoHiBongo RideCymbal2 Vibraslap CrashCymbal2Cowbell SplashCymbal TambourineRideBell ChineseCymbal RideCymbal1HighTom CrashCymbal1HiMidTom LowMidTom OpenHiHatLowTom PedalHiHat HighFloorTom ClosedHiHat LowFloorTom ElectricSnareHandClap AcousticSnare SideStick BassDrum1AcousticBassDrum InstrumentGunshotApplause Helicopter TelephoneRing BirdTweetSeashore BreathNoiseGuitarFretNoise ReverseCymbal SynthDrum MelodicTom TaikoDrum Woodblock SteelDrumsAgogo TinkleBellShanaiFiddleBagpipeKalimbaKotoShamisenBanjoSitarFX8SciFi FX7Echoes FX6Goblins FX5Brightness FX4Atmosphere FX3Crystal FX2SoundtrackFX1Rain Pad8SweepPad7Halo Pad6Metallic Pad5Bowed Pad4Choir Pad3PolysynthPad2Warm Pad1NewAge Lead8BassLead Lead7Fifths Lead6Voice Lead5Charang Lead4Chiff Lead3Calliope Lead2Sawtooth Lead1SquareOcarinaWhistle Skakuhachi BlownBottlePanFluteRecorderFlutePiccoloClarinetBassoon EnglishHornOboe BaritoneSaxTenorSaxAltoSax SopranoSax SynthBrass2 SynthBrass1 BrassSection FrenchHorn MutedTrumpetTubaTromboneTrumpet OrchestraHit SynthVoice VoiceOohs ChoirAahs SynthStrings2 SynthStrings1StringEnsemble2StringEnsemble1TimpaniOrchestralHarpPizzicatoStringsTremoloStrings ContrabassCelloViolaViolin SynthBass2 SynthBass1 SlapBass2 SlapBass1 FretlessBassElectricBassPickElectricBassFinger AcousticBassGuitarHarmonicsDistortionGuitarOverdrivenGuitarElectricGuitarMutedElectricGuitarCleanElectricGuitarJazzAcousticGuitarSteelAcousticGuitarNylonTangoAccordian Harmonica Accordion ReedOrgan ChurchOrgan RockOrganPercussiveOrgan DrawbarOrganDulcimer TubularBells XylophoneMarimba VibraphoneMusicBox GlockenspielCelestaClavinet HarpsichordElectricPiano2ElectricPiano1 HonkyTonkElectricGrandPianoBrightAcousticPianoAcousticGrandPianoinstrumentNameToPrograminstrumentNamesinstrumentProgramsinstrumentFromPrograminstrumentToPrograminstrumentChannels instruments drumChannel drumProgram drumMinKey drumKeyTable drumFromKey drumToKeydrums enumRandomRboundedEnumRandommapFstmapSndfst3snd3thd3toMaybeswapforce Data.MaybeJust zeroOrMore oneOrMore mtl-1.1.1.0Control.Monad.State.Lazy runStateTStateTshiftLshiftRtoBasetoBytestoHextoOctaltoBits someBytesfromBase fromBytesfromHex fromOctalfromBits replicateBigGHC.List replicatetruncsplitAtunlinesSconcatSrightSleftScentreSrightleftcentrespaces stateToReadS checkRangemaximumControllerValue TrackParser EventParser ByteParser parseEventChunk AlienChunkHeader evalParserparsetrackFromChunkremoveEndOfTrack isEndOfTrackgetChunk getHeader getDivisiongetTrack getSchedEventgetEvent getMIDIEvent getPlainTrack decodeStatus getMetaEventgetText toKeyNamegetByteviewLgetN getStringgetBigNget1get2get3get4 getNByteIntgetVar getVarBytes showChunksshowMR getChunks getPlainChunkputEventParsergetEventParser initReadEventOutChunk MIDIWriteroutMFControl.Monad.Writer.Lazy execWriteroutputDivision outputTrack outputEventoutputMIDIEventoutChanoutMetaByteStr outMetaStr outMetaList outMetaMWoutputMetaEventoutIntoutStr outByteStroutVaroutTagoutChunk outOpenChunk