hosc-json (empty) → 0.14
raw patch · 5 files changed
+321/−0 lines, 5 filesdep +aesondep +attoparsecdep +basesetup-changed
Dependencies added: aeson, attoparsec, base, bifunctors, bytestring, hosc, json, text, unordered-containers, utf8-string, vector
Files
- README +23/−0
- Setup.hs +3/−0
- Sound/OSC/Type/JSON.hs +157/−0
- Sound/OSC/Type/JSON/Aeson.hs +103/−0
- hosc-json.cabal +35/−0
+ README view
@@ -0,0 +1,23 @@+hosc-json+---------++[hosc][hosc] <-> [json][json]++HOSC JSON+--------------------------- -----------------------------------+Int32 0 0+Float 0.0 0.0+Double 0.1 0.1+ASCII_String (pack "s") "s"+Blob (pack [0,1]) {"blob":[0,1]}+TimeStamp 0.0 {"timestamp":0.0}+Midi (MIDI 0 1 2 3) {"midi":[0,1,2,3]}+Message "/m" [Int32 0] ["/m",0]+Bundle 0 [Message "/m" []] ["#bundle",{"timestamp":0.0},["/m"]]++[hosc]: http://rd.slavepianos.org/?t=hosc+[json]: http://www.json.org/++© rd, 2013, [gpl]++[gpl]: http://gnu.org/copyleft/
+ Setup.hs view
@@ -0,0 +1,3 @@+#!/usr/bin/env runhaskell+import Distribution.Simple+main = defaultMain
+ Sound/OSC/Type/JSON.hs view
@@ -0,0 +1,157 @@+-- | Encoding and decoding of OSC types as JSON values.+module Sound.OSC.Type.JSON where++import qualified Data.Aeson as A {- aeson -}+import qualified Data.ByteString.Char8 as C {- bytestring -}+import qualified Data.ByteString.Lazy as B.L {- bytestring -}+import qualified Data.ByteString.Lazy.UTF8 as U {- utf8-string -}+import Sound.OSC as O {- hosc -}+import Sound.OSC.Type.JSON.Aeson {- hosc -}++-- * Library variant++-- | The JSON value type.+type Value = A.Value++-- * String translation++-- | 'String' variant of 'encode_json'.+encode_json_str :: Value -> String+encode_json_str = U.toString . encode_json++-- | 'String' variant of 'decode_json'.+decode_json_str :: String -> Maybe Value+decode_json_str = decode_json . U.fromString++-- * Encoding++-- | JSON numbers are 'Either' 'Integer' or 'Double'.+type Number = Either Integer Double++-- | Encode 'Number'.+encode_number :: Number -> Value+encode_number = either encode_integral encode_floating++-- | Encode 'O.TimeStamp' data ('O.Time'), ie. the @hosc@ real-valued+-- @NRT@ representation.+encode_timestamp :: Time -> Value+encode_timestamp n = encode_assoc ("timestamp",encode_floating n)++-- | Encode 'O.Blob' data ('B.L.ByteString').+encode_blob :: B.L.ByteString -> Value+encode_blob b =+ let a = encode_list (map encode_integral (B.L.unpack b))+ in encode_assoc ("blob",a)++-- | Encode 'O.Midi' data ('Word8' tuple).+encode_midi :: MIDI -> A.Value+encode_midi (MIDI p q r s) =+ let a = encode_list (map encode_integral [p,q,r,s])+ in encode_assoc ("midi",a)++-- | 'Datum' encoder. The encoding is shallow, 'O.Int', 'O.Float' and+-- 'O.Double' are all sent to 'A.Number'. 'O.Blob', 'O.TimeStamp' and+-- 'O.Midi' are tagged.+--+-- > let {t = [(int32 0,"0")+-- > ,(int64 0,"0")+-- > ,(float 0.0,"0.0")+-- > ,(double 0.1,"0.1")+-- > ,(string "s","\"s\"")+-- > ,(Blob (Data.ByteString.Lazy.pack [0,1]),"{\"blob\":[0,1]}")+-- > ,(TimeStamp 0.0,"{\"timestamp\":0.0}")+-- > ,(midi (0,1,2,3),"{\"midi\":[0,1,2,3]}")]+-- > ;r = map (\(d,s) -> encode_json_str (encode_datum d) == s) t}+-- > in all id r == True+encode_datum :: Datum -> Value+encode_datum d =+ case d of+ Int32 n -> encode_integral n+ Int64 n -> encode_integral n+ Float n -> encode_floating n+ Double n -> encode_floating n+ ASCII_String s -> encode_string (C.unpack s)+ Blob b -> encode_blob b+ TimeStamp n -> encode_timestamp n+ Midi m -> encode_midi m++-- | 'Message' encoder, the representation is a flat array of+-- @address@ and then arguments.+--+-- > let m = message "/m" [Int32 0,Float 1,string "s"]+-- > in encode_json_str (encode_message m)+--+-- > import Sound.SC3+-- > encode_json_str (encode_message (n_free [0])) == "[\"/n_free\",0]"+encode_message :: Message -> Value+encode_message (Message a d) =+ let a' = encode_string a+ d' = map encode_datum d+ in encode_list (a' : d')++-- | 'O.Bundle' encoder, the representation is a flat array of @#bundle@+-- tag, 'O.TimeStamp' and then message arrays.+--+-- > let b = bundle 0 [message "/m" []]+-- > in encode_json_str (encode_bundle b)+--+-- > let {b = bundle 0 [c_set1 3 4,n_free [0]]+-- > ;r = "[\"#bundle\",{\"timestamp\":0.0}" +++-- > ",[\"/c_set\",3,4.0],[\"/n_free\",0]]"}+-- > in encode_json_str (encode_bundle b) == r+encode_bundle :: Bundle -> Value+encode_bundle (Bundle t m) =+ let b = encode_string "#bundle"+ t' = encode_timestamp t+ m' = map encode_message m+ in encode_list (b : t' : m')++-- | 'Packet' encoder.+encode_packet :: Packet -> Value+encode_packet p =+ case p of+ Packet_Message m -> encode_message m+ Packet_Bundle b -> encode_bundle b++-- * Decoder++-- | Decode 'Message'.+--+-- > let m = message "/m" [Int32 1,Float 1]+-- > in decode_message (encode_message m) == Just m+decode_message :: Value -> Maybe Message+decode_message j =+ case decode_list j of+ Just (m : d) ->+ case decode_datum m of+ Just (ASCII_String m') -> mapM decode_datum d >>=+ Just . message (C.unpack m')+ _ -> Nothing+ _ -> Nothing++-- | Decode 'Bundle'.+--+-- > let b = bundle 0.0 [message "/m" [Int32 1,Float 1]]+-- > in decode_bundle (encode_bundle b) == Just b+--+-- > let {b = bundle 0 [c_set1 3 4,n_free [0]]+-- > ;j = encode_bundle b}+-- > in (b,decode_bundle j)+decode_bundle :: Value -> Maybe Bundle+decode_bundle j =+ case decode_list j of+ Just (b : t : m) ->+ case (datum_string =<< decode_datum b,decode_datum t) of+ (Just "#bundle",Just (TimeStamp t')) ->+ mapM decode_message m >>= Just . Bundle t'+ _ -> Nothing+ _ -> Nothing++-- | Decode 'Packet'.+decode_packet :: Value -> Maybe Packet+decode_packet v =+ case decode_bundle v of+ Just b -> Just (Packet_Bundle b)+ Nothing -> case decode_message v of+ Just m -> Just (Packet_Message m)+ Nothing -> Nothing
+ Sound/OSC/Type/JSON/Aeson.hs view
@@ -0,0 +1,103 @@+-- | Encoding and decoding of OSC types as JSON values.+module Sound.OSC.Type.JSON.Aeson where++import qualified Data.Aeson as A {- aeson -}+import qualified Data.Attoparsec.Number as N {- attoparsec -}+import Data.Bifunctor {- bifunctors -}+import qualified Data.ByteString.Lazy as B.L {- bytestring -}+import qualified Data.HashMap.Strict as M {- unordered-containers -}+import qualified Data.Vector as V {- vector -}+import Data.Word {- base -}+import qualified Data.Text as T {- text -}+import Sound.OSC.Type {- hosc -}++-- * Library variant++-- | Encode 'A.Value' to 'B.L.ByteString'.+encode_json :: A.Value -> B.L.ByteString+encode_json = A.encode++-- | Decode 'A.Value' from 'B.L.ByteString'.+decode_json :: B.L.ByteString -> Maybe A.Value+decode_json = A.decode++-- * Encoders++-- | All 'Integral' values are packed to 'Integer'.+encode_integral :: Integral n => n -> A.Value+encode_integral = A.Number . N.I . fromIntegral++-- | All 'Floating' values are packed to 'Double'.+encode_floating :: (Real n,Floating n) => n -> A.Value+encode_floating = A.Number . N.D . realToFrac++-- | Pack 'String'.+encode_string :: String -> A.Value+encode_string = A.String . T.pack++-- | Pack @(key,value)@ pair to 'JSObject'.+--+-- > encode_assoc ("a",encode_int 0)+encode_assoc :: (String,A.Value) -> A.Value+encode_assoc = A.Object . M.fromList . return . first T.pack++encode_list :: [A.Value] -> A.Value+encode_list = A.Array . V.fromList++-- * Decoders++decode_str :: A.Value -> Maybe String+decode_str j =+ case j of+ A.String t -> Just (T.unpack t)+ _ -> Nothing++decode_list :: A.Value -> Maybe [A.Value]+decode_list j =+ case j of+ A.Array a -> Just (V.toList a)+ _ -> Nothing++-- > decode_assoc (encode_assoc ("a",encode_int 0))+decode_assoc :: A.Value -> Maybe (String,A.Value)+decode_assoc j =+ case j of+ A.Object o ->+ case map (first T.unpack) (M.toList o) of+ [(k,v)] -> Just (k,v)+ _ -> Nothing+ _ -> Nothing++-- > map decode_number [encode_integral 0,encode_floating 1]+decode_number :: A.Value -> Maybe (Either Integer Double)+decode_number j =+ case j of+ A.Number n -> case n of+ N.I i -> Just (Left i)+ N.D d -> Just (Right d)+ _ -> Nothing++decode_word8 :: A.Value -> Maybe Word8+decode_word8 j =+ case decode_number j of+ Just (Left i) -> if i < 256+ then Just (fromIntegral i)+ else Nothing+ _ -> Nothing++decode_datum :: A.Value -> Maybe Datum+decode_datum j =+ case j of+ A.Number (N.I n) -> Just (Int64 (fromIntegral n))+ A.Number (N.D n) -> Just (Double n)+ A.String t -> Just (string (T.unpack t))+ _ -> case decode_assoc j of+ Just ("blob",A.Array v) ->+ mapM decode_word8 (V.toList v) >>= Just . Blob . B.L.pack+ Just ("midi",A.Array v) ->+ case mapM decode_word8 (V.toList v) of+ Just [p,q,r,s] -> Just (midi (p,q,r,s))+ _ -> Nothing+ Just ("timestamp",A.Number (N.D n)) -> Just (TimeStamp n)+ _ -> Nothing+
+ hosc-json.cabal view
@@ -0,0 +1,35 @@+Name: hosc-json+Version: 0.14+Synopsis: Haskell Open Sound Control JSON Serialisation+Description: hosc-json+License: GPL+Category: Sound+Copyright: (c) Rohan Drape, 2013+Author: Rohan Drape+Maintainer: rd@slavepianos.org+Stability: Experimental+Homepage: http://rd.slavepianos.org/?t=hosc-json+Tested-With: GHC == 7.6.1+Build-Type: Simple+Cabal-Version: >= 1.8+Data-Files: README++Library+ Build-Depends: aeson,+ attoparsec,+ base == 4.*,+ bifunctors,+ hosc == 0.14,+ bytestring,+ json,+ text,+ unordered-containers,+ utf8-string,+ vector+ GHC-Options: -Wall -fwarn-tabs+ Exposed-modules: Sound.OSC.Type.JSON+ Sound.OSC.Type.JSON.Aeson++Source-Repository head+ Type: darcs+ Location: http://rd.slavepianos.org/sw/hosc-json