module Sound.MIDI.MachineControl (
splitCommandList,
getCommand,
getCommands,
Command (
Stop,
Play,
DeferredPlay,
FastForward,
Rewind,
RecordStrobe,
RecordExit,
RecordPause,
Pause,
Eject,
Chase,
CommandErrorReset,
Reset,
Wait,
Resume
),
runParser,
) where
import Sound.MIDI.Parser.Primitive
import qualified Sound.MIDI.Parser.Class as Parser
import qualified Sound.MIDI.Parser.Stream as SP
import Sound.MIDI.IO (ByteList, )
import qualified Numeric.NonNegative.Wrapper as NonNeg
import qualified Control.Monad.Trans.Writer as MW
import qualified Control.Monad.Trans.State as MS
import Control.Monad (liftM, liftM2, liftM3, )
import Data.List (unfoldr, )
import Data.Tuple.HT (mapFst, )
import Data.Bool.HT (if', )
import Data.Word (Word8, )
import Data.Maybe (isNothing, catMaybes, )
splitCommandList :: [Word8] -> [(Word8, [Word8])]
splitCommandList =
unfoldr $ \xt ->
case xt of
[] -> Nothing
x:xs ->
Just $ (mapFst ((,) x)) $
if' (x==0 || x==0xF7) (xs, []) $
if' (0x40 <= x && x < 0x78)
(case xs of
[] -> ([], [])
n:ys -> splitAt (fromIntegral n) ys) $
([], xs)
data Command =
Stop
| Play
| DeferredPlay
| FastForward
| Rewind
| RecordStrobe
| RecordExit
| RecordPause
| Pause
| Eject
| Chase
| CommandErrorReset
| Reset
| Write ByteList
| MaskedWrite ByteList
| Read ByteList
| Update ByteList
| Locate ByteList
| VariablePlay Word8 Word8 Word8
| Search Word8 Word8 Word8
| Shuttle Word8 Word8 Word8
| Step Word8
| AssignSystemMaster Word8
| GeneratorCommand Word8
| MIDITimeCodeCommand Word8
| Move Word8 Word8
| Add Word8 Word8 Word8
| Subtract Word8 Word8 Word8
| DropFrameAdjust Word8
| Procedure ByteList
| Event ByteList
| Group ByteList
| CommandSegment ByteList
| DeferredVariablePlay ByteList
| RecordStrobeVariable ByteList
| Wait
| Resume
| GenericNoData Word8
| GenericVariableLength Word8 ByteList
deriving (Show)
getCommands :: Parser.C parser => Parser.Partial parser [Command]
getCommands =
liftM (fmap catMaybes) $
Parser.until isNothing $ do
code <- getByte
if code == 0xF7
then return Nothing
else liftM Just $ getCommand code
getCommand :: Parser.C parser => Word8 -> Parser.Fragile parser Command
getCommand code =
let fetchMany f = liftM f $ getN . NonNeg.fromNumberMsg "Midi.get1" =<< get1
fetchN reqLen act = do
len <- get1
if len==reqLen
then act
else Parser.giveUp $
"expect " ++ show reqLen ++
" argument(s) for command " ++ show code ++
", but got " ++ show len
fetch1 f = fetchN 1 (liftM f getByte)
fetch2 f = fetchN 2 (liftM2 f getByte getByte)
fetch3 f = fetchN 3 (liftM3 f getByte getByte getByte)
in case code of
0x01 -> return Stop
0x02 -> return Play
0x03 -> return DeferredPlay
0x04 -> return FastForward
0x05 -> return Rewind
0x06 -> return RecordStrobe
0x07 -> return RecordExit
0x08 -> return RecordPause
0x09 -> return Pause
0x0A -> return Eject
0x0B -> return Chase
0x0C -> return CommandErrorReset
0x0D -> return Reset
0x40 -> fetchMany Write
0x41 -> fetchMany MaskedWrite
0x42 -> fetchMany Read
0x43 -> fetchMany Update
0x44 -> fetchMany Locate
0x45 -> fetch3 VariablePlay
0x46 -> fetch3 Search
0x47 -> fetch3 Shuttle
0x48 -> fetch1 Step
0x49 -> fetch1 AssignSystemMaster
0x4A -> fetch1 GeneratorCommand
0x4B -> fetch1 MIDITimeCodeCommand
0x4C -> fetch2 Move
0x4D -> fetch3 Add
0x4E -> fetch3 Subtract
0x4F -> fetch1 DropFrameAdjust
0x50 -> fetchMany Procedure
0x51 -> fetchMany Event
0x52 -> fetchMany Group
0x53 -> fetchMany CommandSegment
0x54 -> fetchMany DeferredVariablePlay
0x55 -> fetchMany RecordStrobeVariable
0x7C -> return Wait
0x7F -> return Resume
0x00 -> Parser.giveUp "encountered command zero"
0xF7 -> Parser.giveUp "end of SysEx"
_ ->
if' (0x40 <= code && code < 0x78)
(fetchMany $ GenericVariableLength code)
(return $ GenericNoData code)
runParser ::
Parser.Partial (SP.T SP.ByteList) a ->
ByteList ->
(SP.PossiblyIncomplete a, [SP.UserMessage])
runParser p =
MS.evalState (MW.runWriterT $ SP.decons p) .
SP.ByteList