module Sound.MIDI.Message.Class.Query where import Sound.MIDI.Message.Channel (Channel, ) import Sound.MIDI.Message.Channel.Voice (Pitch, Velocity, Program, Controller, ) import qualified Sound.MIDI.Message as MidiMsg import qualified Sound.MIDI.Message.Channel as ChannelMsg import qualified Sound.MIDI.Message.Channel.Voice as VoiceMsg import qualified Sound.MIDI.Message.Channel.Mode as Mode {- | All methods have default implementations that return 'Nothing'. This helps implementing event data types that support only a subset of types of events. Maybe a better approach is to provide type classes for every type of event and make 'C' a subclass of all of them. -} class C event where note :: event -> Maybe (Channel, (Velocity, Pitch, Bool)) program :: event -> Maybe (Channel, Program) anyController :: event -> Maybe (Channel, (Controller, Int)) pitchBend :: event -> Maybe (Channel, Int) channelPressure :: event -> Maybe (Channel, Int) mode :: event -> Maybe (Channel, Mode.T) note _ev = Nothing program _ev = Nothing anyController _ev = Nothing pitchBend _ev = Nothing channelPressure _ev = Nothing mode _ev = Nothing instance C ChannelMsg.T where note msg = do ChannelMsg.Voice voice <- Just $ ChannelMsg.messageBody msg fmap ((,) (ChannelMsg.messageChannel msg)) $ case voice of VoiceMsg.NoteOn pitch velocity -> Just (velocity, pitch, True) VoiceMsg.NoteOff pitch velocity -> Just (velocity, pitch, False) _ -> Nothing program msg = do ChannelMsg.Voice (VoiceMsg.ProgramChange pgm) <- Just $ ChannelMsg.messageBody msg return (ChannelMsg.messageChannel msg, pgm) anyController msg = do ChannelMsg.Voice (VoiceMsg.Control ctrl val) <- Just $ ChannelMsg.messageBody msg return (ChannelMsg.messageChannel msg, (ctrl, val)) pitchBend msg = do ChannelMsg.Voice (VoiceMsg.PitchBend bend) <- Just $ ChannelMsg.messageBody msg return (ChannelMsg.messageChannel msg, bend) channelPressure msg = do ChannelMsg.Voice (VoiceMsg.MonoAftertouch pressure) <- Just $ ChannelMsg.messageBody msg return (ChannelMsg.messageChannel msg, pressure) mode msg = do ChannelMsg.Mode m <- Just $ ChannelMsg.messageBody msg return (ChannelMsg.messageChannel msg, m) liftMidi :: (ChannelMsg.T -> Maybe (Channel, a)) -> (MidiMsg.T -> Maybe (Channel, a)) liftMidi checkMsg msg = case msg of MidiMsg.Channel chanMsg -> checkMsg chanMsg _ -> Nothing instance C MidiMsg.T where note = liftMidi note program = liftMidi program anyController = liftMidi anyController pitchBend = liftMidi pitchBend channelPressure = liftMidi channelPressure mode = liftMidi mode