midi-0.0.4: src/Sound/MIDI/Event.hs
{- |
Datatype for MIDI events as they can be sent to a synthesizer.
That is, no timing is handled here.
Taken from Haskore.
-}
module Sound.MIDI.Event(
T(..),
Pitch, Program,
Channel, Controller(..), ControllerValue, PitchBendRange, Pressure, Velocity,
isNote, isNoteOn, isNoteOff, zeroKey,
maximumVelocity, normalVelocity, toFloatVelocity,
toFloatController,
bankSelect, modulation, breathControl, footControl, portamentoTime,
dataEntry, mainVolume, balance, panorama, expression,
generalPurpose1, generalPurpose2, generalPurpose3, generalPurpose4,
vectorX, vectorY,
fromPitch, toPitch,
fromVelocity, toVelocity,
fromProgram, toProgram,
fromChannel, toChannel,
increasePitch, subtractPitch,
) where
{- * MIDI Events -}
newtype Pitch = Pitch {fromPitch :: Int} deriving (Show, Eq, Ord)
newtype Velocity = Velocity {fromVelocity :: Int} deriving (Show, Eq, Ord)
newtype Program = Program {fromProgram :: Int} deriving (Show, Eq, Ord)
newtype Channel = Channel {fromChannel :: Int} deriving (Show, Eq, Ord)
toPitch :: Int -> Pitch
toPitch = checkRange "Pitch" Pitch
toVelocity :: Int -> Velocity
toVelocity = checkRange "Velocity" Velocity
toProgram :: Int -> Program
toProgram = checkRange "Program" Program
toChannel :: Int -> Channel
toChannel = checkRange "Channel" Channel
checkRange :: (Bounded a, Ord a, Show a) =>
String -> (Int -> a) -> Int -> a
checkRange typ f x =
let y = f x
in if minBound <= y && y <= maxBound
then y
else error (typ ++ ": value " ++ show x ++ " outside range " ++
show ((minBound, maxBound) `asTypeOf` (y,y)))
instance Enum Program where
toEnum = toProgram
fromEnum = fromProgram
instance Enum Channel where
toEnum = toChannel
fromEnum = fromChannel
instance Enum Pitch where
toEnum = toPitch
fromEnum = fromPitch
-- typical methods of a type class for affine spaces
increasePitch :: Int -> Pitch -> Pitch
increasePitch d = toPitch . (d+) . fromPitch
subtractPitch :: Pitch -> Pitch -> Int
subtractPitch (Pitch p0) (Pitch p1) = p1-p0
instance Bounded Pitch where
minBound = Pitch 0
maxBound = Pitch 127
instance Bounded Velocity where
minBound = Velocity 0
maxBound = Velocity 127
instance Bounded Program where
minBound = Program 0
maxBound = Program 127
instance Bounded Channel where
minBound = Channel 0
maxBound = Channel 15
{- |
A MIDI problem is that one cannot uniquely map
a MIDI key to a frequency.
The frequency depends on the instrument.
I don't know if the deviations are defined for General MIDI.
If 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.
-}
zeroKey :: Pitch
zeroKey = toPitch 48
{- |
The velocity of an ordinary key stroke and
the maximum possible velocity.
-}
normalVelocity, maximumVelocity :: Num quant => quant
-- Velocity
normalVelocity = 64
maximumVelocity = 127
{- |
64 is given as default value by the MIDI specification
and thus we map it to 1.
-}
toFloatVelocity :: (Integral a, Fractional b) => a -> b
toFloatVelocity x = fromIntegral x / normalVelocity
maximumControllerValue :: Num quant => quant
maximumControllerValue = 127
toFloatController :: (Integral a, Fractional b) => a -> b
toFloatController x = fromIntegral x / maximumControllerValue
type PitchBendRange = Int
type Pressure = Int
type ControllerValue = Int
data T =
NoteOff Pitch Velocity
| NoteOn Pitch Velocity
| PolyAfter Pitch Pressure
| ProgramChange Program
| Control Controller ControllerValue
| PitchBend PitchBendRange
| MonoAfter Pressure
deriving (Show, Eq, Ord)
isNote :: T -> Bool
isNote (NoteOn _ _) = True
isNote (NoteOff _ _) = True
isNote _ = False
isNoteOn :: T -> Bool
isNoteOn (NoteOn _ _) = True
isNoteOn _ = False
isNoteOff :: T -> Bool
isNoteOff (NoteOff _ _) = True
isNoteOff _ = False
{- |
Types of predefined MIDI controllers.
-}
data Controller =
{- 00 00 -} BankSelectMSB
| {- 01 01 -} ModulationMSB
| {- 02 02 -} BreathControlMSB
| {- 03 03 -} Controller03
| {- 04 04 -} FootControlMSB
| {- 05 05 -} PortamentoTimeMSB
| {- 06 06 -} DataEntryMSB
| {- 07 07 -} MainVolumeMSB
| {- 08 08 -} BalanceMSB
| {- 09 09 -} Controller09
| {- 10 0A -} PanoramaMSB
| {- 11 0B -} ExpressionMSB
| {- 12 0C -} Controller0C
| {- 13 0D -} Controller0D
| {- 14 0E -} Controller0E
| {- 15 0F -} Controller0F
| {- 16 10 -} GeneralPurpose1MSB
| {- 17 11 -} GeneralPurpose2MSB
| {- 18 12 -} GeneralPurpose3MSB
| {- 19 13 -} GeneralPurpose4MSB
| {- 20 14 -} Controller14
| {- 21 15 -} Controller15
| {- 22 16 -} Controller16
| {- 23 17 -} Controller17
| {- 24 18 -} Controller18
| {- 25 19 -} Controller19
| {- 26 1A -} Controller1A
| {- 27 1B -} Controller1B
| {- 28 1C -} Controller1C
| {- 29 1D -} Controller1D
| {- 30 1E -} Controller1E
| {- 31 1F -} Controller1F
| {- 32 20 -} BankSelectLSB
| {- 33 21 -} ModulationLSB
| {- 34 22 -} BreathControlLSB
| {- 35 23 -} Controller23
| {- 36 24 -} FootControlLSB
| {- 37 25 -} PortamentoTimeLSB
| {- 38 26 -} DataEntryLSB
| {- 39 27 -} MainVolumeLSB
| {- 40 28 -} BalanceLSB
| {- 41 29 -} Controller29
| {- 42 2A -} PanoramaLSB
| {- 43 2B -} ExpressionLSB
| {- 44 2C -} Controller2C
| {- 45 2D -} Controller2D
| {- 46 2E -} Controller2E
| {- 47 2F -} Controller2F
| {- 48 30 -} GeneralPurpose1LSB
| {- 49 31 -} GeneralPurpose2LSB
| {- 50 32 -} GeneralPurpose3LSB
| {- 51 33 -} GeneralPurpose4LSB
| {- 52 34 -} Controller34
| {- 53 35 -} Controller35
| {- 54 36 -} Controller36
| {- 55 37 -} Controller37
| {- 56 38 -} Controller38
| {- 57 39 -} Controller39
| {- 58 3A -} Controller3A
| {- 59 3B -} Controller3B
| {- 60 3C -} Controller3C
| {- 61 3D -} Controller3D
| {- 62 3E -} Controller3E
| {- 63 3F -} Controller3F
{- Continuous 7 bit (switches: 0-3f=off, 40-7f=on) -}
| {- 64 40 -} Sustain
| {- 65 41 -} Porta
| {- 66 42 -} Sustenuto
| {- 67 43 -} SoftPedal
| {- 68 44 -} Controller44
| {- 69 45 -} Hold2
| {- 70 46 -} Controller46
| {- 71 47 -} Controller47
| {- 72 48 -} Controller48
| {- 73 49 -} Controller49
| {- 74 4A -} Controller4A
| {- 75 4B -} Controller4B
| {- 76 4C -} Controller4C
| {- 77 4D -} Controller4D
| {- 78 4E -} Controller4E
| {- 79 4F -} Controller4F
| {- 80 50 -} GeneralPurpose5
| {- 81 51 -} GeneralPurpose6
| {- 82 52 -} GeneralPurpose7
| {- 83 53 -} GeneralPurpose8
| {- 84 54 -} Controller54
| {- 85 55 -} Controller55
| {- 86 56 -} Controller56
| {- 87 57 -} Controller57
| {- 88 58 -} Controller58
| {- 89 59 -} Controller59
| {- 90 5A -} Controller5A
| {- 91 5B -} ExtDepth
| {- 92 5C -} TremoloDepth
| {- 93 5D -} ChorusDepth
| {- 94 5E -} CelesteDepth
| {- 95 5F -} PhaserDepth
{- Parameters -}
| {- 96 60 -} DataIncrement
| {- 97 61 -} DataDecrement
| {- 98 62 -} NonRegisteredParameterLSB
| {- 99 63 -} NonRegisteredParameterMSB
| {- 100 64 -} RegisteredParameterLSB
| {- 101 65 -} RegisteredParameterMSB
| {- 102 66 -} Controller66
| {- 103 67 -} Controller67
| {- 104 68 -} Controller68
| {- 105 69 -} Controller69
| {- 106 6A -} Controller6A
| {- 107 6B -} Controller6B
| {- 108 6C -} Controller6C
| {- 109 6D -} Controller6D
| {- 110 6E -} Controller6E
| {- 111 6F -} Controller6F
| {- 112 70 -} Controller70
| {- 113 71 -} Controller71
| {- 114 72 -} Controller72
| {- 115 73 -} Controller73
| {- 116 74 -} Controller74
| {- 117 75 -} Controller75
| {- 118 76 -} Controller76
| {- 119 77 -} Controller77
| {- 120 78 -} Controller78
| {- 121 79 -} Controller79
| {- 122 7A -} Controller7A
| {- 123 7B -} Controller7B
| {- 124 7C -} Controller7C
| {- 125 7D -} Controller7D
| {- 126 7E -} Controller7E
| {- 127 7F -} Controller7F
deriving (Show, Eq, Ord, Enum)
bankSelect, modulation, breathControl, footControl, portamentoTime,
dataEntry, mainVolume, balance, panorama, expression,
generalPurpose1, generalPurpose2, generalPurpose3, generalPurpose4,
vectorX, vectorY :: Controller
bankSelect = BankSelectMSB
modulation = ModulationMSB
breathControl = BreathControlMSB
footControl = FootControlMSB
portamentoTime = PortamentoTimeMSB
dataEntry = DataEntryMSB
mainVolume = MainVolumeMSB
balance = BalanceMSB
panorama = PanoramaMSB
expression = ExpressionMSB
generalPurpose1 = GeneralPurpose1MSB
generalPurpose2 = GeneralPurpose2MSB
generalPurpose3 = GeneralPurpose3MSB
generalPurpose4 = GeneralPurpose4MSB
vectorX = generalPurpose1
vectorY = generalPurpose2