module Sound.OSC.Type where
import qualified Data.ByteString.Lazy as B
import qualified Data.ByteString.Char8 as C
import Data.Int
import Data.List
import Data.Word
import Numeric
type Time = Double
immediately :: Time
immediately = 1 / 2^(32::Int)
type Datum_Type = Char
type ASCII = C.ByteString
ascii :: String -> ASCII
ascii = C.pack
ascii_to_string :: ASCII -> String
ascii_to_string = C.unpack
data MIDI = MIDI Word8 Word8 Word8 Word8
deriving (Eq,Show,Read)
data Datum = Int32 {d_int32 :: Int32}
| Int64 {d_int64 :: Int64}
| Float {d_float :: Float}
| Double {d_double :: Double}
| ASCII_String {d_ascii_string :: ASCII}
| Blob {d_blob :: B.ByteString}
| TimeStamp {d_timestamp :: Time}
| Midi {d_midi :: MIDI}
deriving (Eq,Read,Show)
datum_tag :: Datum -> Datum_Type
datum_tag dt =
case dt of
Int32 _ -> 'i'
Int64 _ -> 'h'
Float _ -> 'f'
Double _ -> 'd'
ASCII_String _ -> 's'
Blob _ -> 'b'
TimeStamp _ -> 't'
Midi _ -> 'm'
datum_integral :: Integral i => Datum -> Maybe i
datum_integral d =
case d of
Int32 x -> Just (fromIntegral x)
Int64 x -> Just (fromIntegral x)
_ -> Nothing
datum_floating :: Floating n => Datum -> Maybe n
datum_floating d =
case d of
Int32 n -> Just (fromIntegral n)
Int64 n -> Just (fromIntegral n)
Float n -> Just (realToFrac n)
Double n -> Just (realToFrac n)
TimeStamp n -> Just (realToFrac n)
_ -> Nothing
class Datem a where
d_put :: a -> Datum
d_get :: Datum -> Maybe a
instance Datem Int32 where
d_put = Int32
d_get d = case d of {Int32 x -> Just x;_ -> Nothing}
instance Datem Int64 where
d_put = Int64
d_get d = case d of {Int64 x -> Just x;_ -> Nothing}
instance Datem Int where
d_put = Int64 . fromIntegral
d_get = datum_integral
instance Datem Integer where
d_put = Int64 . fromIntegral
d_get = datum_integral
instance Datem Float where
d_put = Float
d_get d = case d of {Float x -> Just x;_ -> Nothing}
instance Datem Double where
d_put = Double
d_get d = case d of {Double x -> Just x;_ -> Nothing}
instance Datem C.ByteString where
d_put = ASCII_String
d_get d = case d of {ASCII_String x -> Just x;_ -> Nothing}
instance Datem B.ByteString where
d_put = Blob
d_get d = case d of {Blob x -> Just x;_ -> Nothing}
instance Datem MIDI where
d_put = Midi
d_get d = case d of {Midi x -> Just x;_ -> Nothing}
int32 :: Integral n => n -> Datum
int32 = Int32 . fromIntegral
int64 :: Integral n => n -> Datum
int64 = Int64 . fromIntegral
float :: Real n => n -> Datum
float = Float . realToFrac
double :: Real n => n -> Datum
double = Double . realToFrac
string :: String -> Datum
string = ASCII_String . C.pack
midi :: (Word8,Word8,Word8,Word8) -> Datum
midi (p,q,r,s) = Midi (MIDI p q r s)
type Address_Pattern = String
data Message = Message {messageAddress :: Address_Pattern
,messageDatum :: [Datum]}
deriving (Eq,Read,Show)
message :: Address_Pattern -> [Datum] -> Message
message a xs =
case a of
'/':_ -> Message a xs
_ -> error "message: ill-formed address pattern"
descriptor :: [Datum] -> ASCII
descriptor l = C.pack (',' : map datum_tag l)
descriptor_tags :: ASCII -> ASCII
descriptor_tags = C.drop 1
data Bundle = Bundle {bundleTime :: Time
,bundleMessages :: [Message]}
deriving (Eq,Read,Show)
instance Ord Bundle where
compare (Bundle a _) (Bundle b _) = compare a b
bundle :: Time -> [Message] -> Bundle
bundle t xs =
case xs of
[] -> error "bundle: empty?"
_ -> Bundle t xs
data Packet = Packet_Message {packetMessage :: Message}
| Packet_Bundle {packetBundle :: Bundle}
deriving (Eq,Read,Show)
p_bundle :: Time -> [Message] -> Packet
p_bundle t = Packet_Bundle . bundle t
p_message :: Address_Pattern -> [Datum] -> Packet
p_message a = Packet_Message . message a
packetTime :: Packet -> Time
packetTime = at_packet (const immediately) bundleTime
packetMessages :: Packet -> [Message]
packetMessages = at_packet return bundleMessages
packet_to_bundle :: Packet -> Bundle
packet_to_bundle = at_packet (\m -> Bundle immediately [m]) id
packet_to_message :: Packet -> Maybe Message
packet_to_message p =
case p of
Packet_Bundle b ->
case b of
Bundle t [m] -> if t == immediately then Just m else Nothing
_ -> Nothing
Packet_Message m -> Just m
packet_is_immediate :: Packet -> Bool
packet_is_immediate = (== immediately) . packetTime
at_packet :: (Message -> a) -> (Bundle -> a) -> Packet -> a
at_packet f g p =
case p of
Packet_Message m -> f m
Packet_Bundle b -> g b
message_has_address :: Address_Pattern -> Message -> Bool
message_has_address x = (== x) . messageAddress
bundle_has_address :: Address_Pattern -> Bundle -> Bool
bundle_has_address x = any (message_has_address x) . bundleMessages
packet_has_address :: Address_Pattern -> Packet -> Bool
packet_has_address x =
at_packet (message_has_address x)
(bundle_has_address x)
type FP_Precision = Maybe Int
floatPP :: RealFloat n => Maybe Int -> n -> String
floatPP p n =
let s = showFFloat p n ""
s' = dropWhile (== '0') (reverse s)
in case s' of
'.':_ -> reverse ('0' : s')
_ -> reverse s'
timePP :: FP_Precision -> Time -> String
timePP = floatPP
vecPP :: Show a => [a] -> String
vecPP v = '<' : intercalate "," (map show v) ++ ">"
datumPP :: FP_Precision -> Datum -> String
datumPP p d =
case d of
Int32 n -> show n
Int64 n -> show n
Float n -> floatPP p n
Double n -> floatPP p n
ASCII_String s -> show (C.unpack s)
Blob s -> show s
TimeStamp t -> timePP p t
Midi (MIDI b1 b2 b3 b4) -> vecPP [b1,b2,b3,b4]
messagePP :: FP_Precision -> Message -> String
messagePP p (Message a d) =
let d' = map (datumPP p) d
in unwords ("#message" : a : d')
bundlePP :: FP_Precision -> Bundle -> String
bundlePP p (Bundle t m) =
let m' = intersperse ";" (map (messagePP p) m)
in unwords ("#bundle" : timePP p t : m')
packetPP :: FP_Precision -> Packet -> String
packetPP p pkt =
case pkt of
Packet_Message m -> messagePP p m
Packet_Bundle b -> bundlePP p b
readMaybe :: (Read a) => String -> Maybe a
readMaybe s =
case reads s of
[(x, "")] -> Just x
_ -> Nothing
parse_datum :: Datum_Type -> String -> Maybe Datum
parse_datum ty =
case ty of
'i' -> fmap Int32 . readMaybe
'h' -> fmap Int64 . readMaybe
'f' -> fmap Float . readMaybe
'd' -> fmap Double . readMaybe
's' -> fmap (ASCII_String . C.pack) . readMaybe
'b' -> fmap (Blob . B.pack) . readMaybe
't' -> error "parse_datum: timestamp"
'm' -> fmap midi . readMaybe
_ -> error "parse_datum: type"