module Sound.MIDI.Message.Channel (
T(..), Body(..), get, getWithStatus, put, putWithStatus,
Channel, fromChannel, toChannel,
Voice.Pitch, Voice.fromPitch, Voice.toPitch,
Voice.Velocity, Voice.fromVelocity, Voice.toVelocity,
Voice.Program, Voice.fromProgram, Voice.toProgram,
Voice.Controller, Voice.fromController, Voice.toController,
decodeStatus,
) where
import qualified Sound.MIDI.Message.Channel.Voice as Voice
import qualified Sound.MIDI.Message.Channel.Mode as Mode
import qualified Sound.MIDI.Parser.Status as StatusParser
import Sound.MIDI.Parser.Primitive
import qualified Sound.MIDI.Parser.Class as Parser
import Sound.MIDI.Parser.Status (Channel, fromChannel, toChannel, )
import Control.Monad (liftM, liftM2, when, )
import qualified Sound.MIDI.Writer.Status as StatusWriter
import qualified Sound.MIDI.Writer.Basic as Writer
import qualified Sound.MIDI.Bit as Bit
import Sound.MIDI.Monoid ((+#+))
import Data.Tuple.HT (mapSnd, )
import Test.QuickCheck (Arbitrary(arbitrary, shrink), )
import qualified Test.QuickCheck as QC
data T = Cons {
messageChannel :: Channel,
messageBody :: Body
}
deriving (Show, Eq, Ord)
data Body =
Voice Voice.T
| Mode Mode.T
deriving (Show, Eq, Ord)
instance Arbitrary T where
arbitrary =
liftM2 Cons
(liftM toChannel $ QC.frequency $
(20, return 3) :
( 1, QC.choose (0,15)) :
[])
(QC.frequency $
(20, liftM Voice arbitrary) :
( 1, liftM Mode arbitrary) :
[])
shrink (Cons chan body) =
map (uncurry Cons) $
case body of
Voice v -> map (mapSnd Voice) $ shrink (chan, v)
Mode m -> map (mapSnd Mode) $ shrink (chan, m)
getWithStatus :: Parser.C parser => Int -> Parser.Fragile (StatusParser.T parser) T
getWithStatus tag =
do (status@(code, channel), firstData) <-
if tag < 0x80
then maybe
(Parser.giveUp "messages wants to repeat status byte, but there was no status yet")
(\cc -> return (cc,tag))
=<< StatusParser.get
else liftM ((,) $ decodeStatus tag) $ StatusParser.lift get1
StatusParser.set (Just status)
StatusParser.lift $ get code channel firstData
decodeStatus :: Int -> (Int, Channel)
decodeStatus = mapSnd toChannel . Bit.splitAt 4
get :: Parser.C parser => Int -> Channel -> Int -> Parser.Fragile parser T
get code channel firstData =
liftM (Cons channel) $
if code == 11 && firstData >= 0x78
then
when (firstData >= 0x80)
(Parser.giveUp ("mode value out of range: " ++ show firstData)) >>
liftM Mode (Mode.get firstData)
else liftM Voice (Voice.get code firstData)
put :: Writer.C writer => T -> writer
put = StatusWriter.toWriterWithoutStatus . putWithStatus
putWithStatus ::
(StatusWriter.Compression compress, Writer.C writer) =>
T -> StatusWriter.T compress writer
putWithStatus (Cons c e) =
case e of
Voice v -> Voice.putWithStatus (putChannel c) v
Mode m -> putChannel c 11 +#+ StatusWriter.lift (Mode.put m)
putChannel ::
(StatusWriter.Compression compress, Writer.C writer) =>
Channel -> Int -> StatusWriter.T compress writer
putChannel chan code =
StatusWriter.change (code, chan) $
Writer.putIntAsByte (16*code + fromChannel chan)