{- |
MIDI messages in MIDI files.
They are not a superset of the messages,
that are used for real-time communication between MIDI devices.
For these refer to "Sound.MIDI.Message".
Namely System Common and System Real Time messages are missing.
If you need both real-time and file messages (say for ALSA sequencer),
you need a custom datatype.
-}
module Sound.MIDI.File.Event (
   T(..), get, put,
   TrackEvent, getTrackEvent,
   ElapsedTime, fromElapsedTime, toElapsedTime,
   mapBody, maybeMIDIEvent, maybeMetaEvent, maybeVoice, mapVoice,
   ) where

import qualified Sound.MIDI.Message.Channel       as ChannelMsg
import qualified Sound.MIDI.Message.Channel.Voice as Voice
import qualified Sound.MIDI.File.Event.SystemExclusive as SysEx
import qualified Sound.MIDI.File.Event.Meta as MetaEvent

import Sound.MIDI.Message.Channel (Channel)

import Sound.MIDI.File.Event.Meta (
   ElapsedTime, fromElapsedTime, toElapsedTime,
   )

import           Sound.MIDI.Parser.Primitive
import qualified Sound.MIDI.Parser.Status as StatusParser
import qualified Sound.MIDI.Parser.Class  as Parser

import Control.Monad (liftM, liftM2, )

import qualified Sound.MIDI.Writer.Status as StatusWriter
import qualified Sound.MIDI.Writer.Basic  as Writer

import Sound.MIDI.Monoid ((+#+))
import Data.Tuple.HT (mapSnd)


import Test.QuickCheck (Arbitrary(arbitrary), )
import qualified Test.QuickCheck as QC



type TrackEvent = (ElapsedTime, T)

mapBody :: (T -> T) -> (TrackEvent -> TrackEvent)
mapBody :: (T -> T) -> TrackEvent -> TrackEvent
mapBody = (T -> T) -> TrackEvent -> TrackEvent
forall b c a. (b -> c) -> (a, b) -> (a, c)
mapSnd


data T =
     MIDIEvent       ChannelMsg.T
   | MetaEvent       MetaEvent.T
   | SystemExclusive SysEx.T
     deriving (Int -> T -> ShowS
[T] -> ShowS
T -> String
(Int -> T -> ShowS) -> (T -> String) -> ([T] -> ShowS) -> Show T
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [T] -> ShowS
$cshowList :: [T] -> ShowS
show :: T -> String
$cshow :: T -> String
showsPrec :: Int -> T -> ShowS
$cshowsPrec :: Int -> T -> ShowS
Show,T -> T -> Bool
(T -> T -> Bool) -> (T -> T -> Bool) -> Eq T
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: T -> T -> Bool
$c/= :: T -> T -> Bool
== :: T -> T -> Bool
$c== :: T -> T -> Bool
Eq,Eq T
Eq T
-> (T -> T -> Ordering)
-> (T -> T -> Bool)
-> (T -> T -> Bool)
-> (T -> T -> Bool)
-> (T -> T -> Bool)
-> (T -> T -> T)
-> (T -> T -> T)
-> Ord T
T -> T -> Bool
T -> T -> Ordering
T -> T -> T
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: T -> T -> T
$cmin :: T -> T -> T
max :: T -> T -> T
$cmax :: T -> T -> T
>= :: T -> T -> Bool
$c>= :: T -> T -> Bool
> :: T -> T -> Bool
$c> :: T -> T -> Bool
<= :: T -> T -> Bool
$c<= :: T -> T -> Bool
< :: T -> T -> Bool
$c< :: T -> T -> Bool
compare :: T -> T -> Ordering
$ccompare :: T -> T -> Ordering
$cp1Ord :: Eq T
Ord)

instance Arbitrary T where
   arbitrary :: Gen T
arbitrary =
      [(Int, Gen T)] -> Gen T
forall a. [(Int, Gen a)] -> Gen a
QC.frequency ([(Int, Gen T)] -> Gen T) -> [(Int, Gen T)] -> Gen T
forall a b. (a -> b) -> a -> b
$
         (Int
100, (T -> T) -> Gen T -> Gen T
forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
liftM T -> T
MIDIEvent Gen T
forall a. Arbitrary a => Gen a
arbitrary) (Int, Gen T) -> [(Int, Gen T)] -> [(Int, Gen T)]
forall a. a -> [a] -> [a]
:
         (  Int
1, (T -> T) -> Gen T -> Gen T
forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
liftM T -> T
MetaEvent Gen T
forall a. Arbitrary a => Gen a
arbitrary) (Int, Gen T) -> [(Int, Gen T)] -> [(Int, Gen T)]
forall a. a -> [a] -> [a]
:
         []


maybeMIDIEvent :: T -> Maybe ChannelMsg.T
maybeMIDIEvent :: T -> Maybe T
maybeMIDIEvent (MIDIEvent T
msg) = T -> Maybe T
forall a. a -> Maybe a
Just T
msg
maybeMIDIEvent T
_ = Maybe T
forall a. Maybe a
Nothing

maybeMetaEvent :: T -> Maybe MetaEvent.T
maybeMetaEvent :: T -> Maybe T
maybeMetaEvent (MetaEvent T
mev) = T -> Maybe T
forall a. a -> Maybe a
Just T
mev
maybeMetaEvent T
_ = Maybe T
forall a. Maybe a
Nothing

maybeVoice :: T -> Maybe (Channel, Voice.T)
maybeVoice :: T -> Maybe (Channel, T)
maybeVoice (MIDIEvent (ChannelMsg.Cons Channel
ch (ChannelMsg.Voice T
ev))) = (Channel, T) -> Maybe (Channel, T)
forall a. a -> Maybe a
Just (Channel
ch,T
ev)
maybeVoice T
_ = Maybe (Channel, T)
forall a. Maybe a
Nothing

mapVoice :: (Voice.T -> Voice.T) -> T -> T
mapVoice :: (T -> T) -> T -> T
mapVoice T -> T
f (MIDIEvent (ChannelMsg.Cons Channel
ch (ChannelMsg.Voice T
ev))) =
   T -> T
MIDIEvent (Channel -> Body -> T
ChannelMsg.Cons Channel
ch (T -> Body
ChannelMsg.Voice (T -> T
f T
ev)))
mapVoice T -> T
_ T
msg = T
msg


-- * serialization

get :: Parser.C parser => Parser.Fragile (StatusParser.T parser) T
get :: Fragile (T parser) T
get =
   Fragile parser Int -> Fragile (T parser) Int
forall (parser :: * -> *) a.
Monad parser =>
Fragile parser a -> Fragile (T parser) a
StatusParser.lift Fragile parser Int
forall (parser :: * -> *). C parser => Fragile parser Int
get1 Fragile (T parser) Int
-> (Int -> Fragile (T parser) T) -> Fragile (T parser) T
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \Int
tag ->
   if Int
tag Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0xF0
     then (T -> T)
-> ExceptionalT String (T parser) T -> Fragile (T parser) T
forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
liftM T -> T
MIDIEvent (ExceptionalT String (T parser) T -> Fragile (T parser) T)
-> ExceptionalT String (T parser) T -> Fragile (T parser) T
forall a b. (a -> b) -> a -> b
$ Int -> ExceptionalT String (T parser) T
forall (parser :: * -> *). C parser => Int -> Fragile (T parser) T
ChannelMsg.getWithStatus Int
tag
     else
       Status -> Fragile (T parser) ()
forall (parser :: * -> *).
Monad parser =>
Status -> Fragile (T parser) ()
StatusParser.set Status
forall a. Maybe a
Nothing Fragile (T parser) ()
-> Fragile (T parser) T -> Fragile (T parser) T
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>>
       (Fragile parser T -> Fragile (T parser) T
forall (parser :: * -> *) a.
Monad parser =>
Fragile parser a -> Fragile (T parser) a
StatusParser.lift (Fragile parser T -> Fragile (T parser) T)
-> Fragile parser T -> Fragile (T parser) T
forall a b. (a -> b) -> a -> b
$
        if Int
tag Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0xFF
          then (T -> T) -> ExceptionalT String parser T -> Fragile parser T
forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
liftM T -> T
MetaEvent (ExceptionalT String parser T -> Fragile parser T)
-> ExceptionalT String parser T -> Fragile parser T
forall a b. (a -> b) -> a -> b
$ ExceptionalT String parser T
forall (parser :: * -> *). C parser => Fragile parser T
MetaEvent.get
          else (T -> T) -> ExceptionalT String parser T -> Fragile parser T
forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
liftM T -> T
SystemExclusive (ExceptionalT String parser T -> Fragile parser T)
-> ExceptionalT String parser T -> Fragile parser T
forall a b. (a -> b) -> a -> b
$ Int -> ExceptionalT String parser T
forall (parser :: * -> *). C parser => Int -> Fragile parser T
SysEx.get Int
tag)

{- |
Each event is preceded by the delta time: the time in ticks between the
last event and the current event.  Parse a time and an event, ignoring
System Exclusive messages.
-}
getTrackEvent :: Parser.C parser => Parser.Fragile (StatusParser.T parser) TrackEvent
getTrackEvent :: Fragile (T parser) TrackEvent
getTrackEvent  =  (Integer -> T -> TrackEvent)
-> ExceptionalT String (T parser) Integer
-> ExceptionalT String (T parser) T
-> Fragile (T parser) TrackEvent
forall (m :: * -> *) a1 a2 r.
Monad m =>
(a1 -> a2 -> r) -> m a1 -> m a2 -> m r
liftM2 (,) (Fragile parser Integer -> ExceptionalT String (T parser) Integer
forall (parser :: * -> *) a.
Monad parser =>
Fragile parser a -> Fragile (T parser) a
StatusParser.lift Fragile parser Integer
forall (parser :: * -> *). C parser => Fragile parser Integer
getVar) ExceptionalT String (T parser) T
forall (parser :: * -> *). C parser => Fragile (T parser) T
get


{- |
The following functions encode various 'MIDIFile.T' elements
into the raw data of a standard MIDI file.
-}

put ::
   (StatusWriter.Compression compress, Writer.C writer) =>
   T -> StatusWriter.T compress writer
put :: T -> T compress writer
put T
e =
   case T
e of
      MIDIEvent       T
m -> T -> T compress writer
forall compress writer.
(Compression compress, C writer) =>
T -> T compress writer
ChannelMsg.putWithStatus T
m
      MetaEvent       T
m -> T compress writer
forall compress writer.
(Compression compress, Monoid writer) =>
T compress writer
StatusWriter.clear T compress writer -> T compress writer -> T compress writer
forall m. Monoid m => m -> m -> m
+#+ writer -> T compress writer
forall (t :: * -> *) m. (C t, Monoid m) => m -> t m
StatusWriter.lift (T -> writer
forall writer. C writer => T -> writer
MetaEvent.put  T
m)
      SystemExclusive T
m -> T compress writer
forall compress writer.
(Compression compress, Monoid writer) =>
T compress writer
StatusWriter.clear T compress writer -> T compress writer -> T compress writer
forall m. Monoid m => m -> m -> m
+#+ writer -> T compress writer
forall (t :: * -> *) m. (C t, Monoid m) => m -> t m
StatusWriter.lift (T -> writer
forall writer. C writer => T -> writer
SysEx.put      T
m)