hosc 0.10.1 → 0.11
raw patch · 10 files changed
+94/−70 lines, 10 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Sound.OpenSoundControl.Coding: type Coder = (OSC -> ByteString, ByteString -> OSC)
Files
- Sound/OpenSoundControl.hs +25/−11
- Sound/OpenSoundControl/Coding.hs +4/−1
- Sound/OpenSoundControl/Coding/Decode/Base.hs +3/−0
- Sound/OpenSoundControl/Coding/Decode/Binary.hs +12/−9
- Sound/OpenSoundControl/Coding/Encode/Builder.hs +3/−0
- Sound/OpenSoundControl/Time.hs +28/−24
- Sound/OpenSoundControl/Transport/TCP.hs +1/−2
- Sound/OpenSoundControl/Transport/UDP.hs +1/−2
- Sound/OpenSoundControl/Type.hs +12/−16
- hosc.cabal +5/−5
Sound/OpenSoundControl.hs view
@@ -1,21 +1,35 @@--- | hosc implements a subset of the Open Sound Control byte protocol.--- The protocol is documented at <http://opensoundcontrol.org/>.-module Sound.OpenSoundControl (module O+-- | An implementation of a subset of the /Open Sound Control/ byte+-- protocol, documented at <http://opensoundcontrol.org/>.+--+-- For the most part this top-level module is the only import+-- required. It provides the 'Datum' and 'OSC' types, 'encodeOSC' and+-- 'decodeOSC' functions, basic 'UDP' and 'TCP' 'Transport' layers,+-- and basic temporal operations 'utcr' to access the current time and+-- 'pauseThread' to delay the current thread.+--+-- > let o = Bundle immediately [Message "/g_free" [Int 0]]+-- > in decodeOSC (encodeOSC o) == o+module Sound.OpenSoundControl (module Sound.OpenSoundControl.Type+ ,module Sound.OpenSoundControl.Time+ ,module Sound.OpenSoundControl.Transport+ ,module Sound.OpenSoundControl.Transport.UDP+ ,module Sound.OpenSoundControl.Transport.TCP ,C.encodeOSC,C.decodeOSC ,openUDP,udpServer ,openTCP,tcpServer) where -import qualified Sound.OpenSoundControl.Coding.Decode.Binary as C-import qualified Sound.OpenSoundControl.Coding.Encode.Builder as C-import Sound.OpenSoundControl.Type as O-import Sound.OpenSoundControl.Time as O-import Sound.OpenSoundControl.Transport as O-import Sound.OpenSoundControl.Transport.UDP as O-import Sound.OpenSoundControl.Transport.TCP as O+import Sound.OpenSoundControl.Coding.Decode.Binary as C+import Sound.OpenSoundControl.Coding.Encode.Builder as C+import Sound.OpenSoundControl.Type+import Sound.OpenSoundControl.Time+import Sound.OpenSoundControl.Transport+import Sound.OpenSoundControl.Transport.UDP+import Sound.OpenSoundControl.Transport.TCP -- | Make a UDP connection. ----- > withTransport (openUDP "127.0.0.1" 57110) (\fd -> recvT 0.5 fd >>= print)+-- > let t = openUDP "127.0.0.1" 57110+-- > in withTransport t (\fd -> recvT 0.5 fd >>= print) openUDP :: String -> Int -> IO UDP openUDP = openUDP' (C.encodeOSC,C.decodeOSC)
Sound/OpenSoundControl/Coding.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE FlexibleInstances,TypeSynonymInstances #-} -- | A type-class to provide coding operations to different data types -- using the same function names.-module Sound.OpenSoundControl.Coding (Coding(..) ) where+module Sound.OpenSoundControl.Coding (Coding(..),Coder) where import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as B@@ -28,3 +28,6 @@ instance Coding String where encodeOSC = C.unpack . encodeOSC decodeOSC = decodeOSC . C.pack++-- | An 'encodeOSC' and 'decodeOSC' pair over 'B.ByteString'.+type Coder = (OSC -> B.ByteString,B.ByteString -> OSC)
Sound/OpenSoundControl/Coding/Decode/Base.hs view
@@ -79,6 +79,9 @@ in Bundle timeStamp ms -- | Decode an OSC packet.+--+-- > let b = B.pack [47,103,95,102,114,101,101,0,44,105,0,0,0,0,0,0]+-- > in decodeOSC b == Message "/g_free" [Int 0] decodeOSC :: B.ByteString -> OSC decodeOSC b = if bundleHeader `B.isPrefixOf` b
Sound/OpenSoundControl/Coding/Decode/Binary.hs view
@@ -7,7 +7,7 @@ import Data.Binary.Get import qualified Data.Binary.IEEE754 as I import qualified Data.ByteString as S-import qualified Data.ByteString.Lazy as B+import qualified Data.ByteString.Lazy as L import qualified Data.ByteString.Lazy.Char8 as C import Data.Int (Int32) import Data.Word (Word32)@@ -21,7 +21,7 @@ isolate n m = do s <- get_bytes n let (a, s', _) = runGetState m s 0- if B.null s'+ if L.null s' then return a else fail "isolate: not all bytes consumed" @@ -33,14 +33,14 @@ get_string :: Get String get_string = do s <- getLazyByteStringNul- skip (fromIntegral (align (B.length s + 1)))+ skip (fromIntegral (align (L.length s + 1))) return $ C.unpack s -- | Get binary data prefixed by byte count.-get_bytes :: Word32 -> Get B.ByteString+get_bytes :: Word32 -> Get L.ByteString get_bytes n = do b <- getLazyByteString (fromIntegral n)- if n /= fromIntegral (B.length b)+ if n /= fromIntegral (L.length b) then fail "get_bytes: end of stream" else skip (fromIntegral (align n)) return b@@ -75,7 +75,7 @@ -- | Get an OSC packet. get_packet :: Get OSC get_packet = do- h <- uncheckedLookAhead (B.length bundleHeader)+ h <- uncheckedLookAhead (L.length bundleHeader) if h == bundleHeader then get_bundle else get_message@@ -93,7 +93,7 @@ get_bundle :: Get OSC get_bundle = do- skip (fromIntegral (B.length bundleHeader))+ skip (fromIntegral (L.length bundleHeader)) t <- NTPi <$> getWord64be ps <- get_packet_seq return $ Bundle t ps@@ -103,11 +103,14 @@ getOSC = get_packet -- | Decode an OSC packet from a lazy ByteString.-decodeOSC :: B.ByteString -> OSC+--+-- > let b = L.pack [47,103,95,102,114,101,101,0,44,105,0,0,0,0,0,0]+-- > in decodeOSC b == Message "/g_free" [Int 0]+decodeOSC :: L.ByteString -> OSC {-# INLINE decodeOSC #-} decodeOSC = runGet getOSC -- | Decode an OSC packet from a strict ByteString. decodeOSC' :: S.ByteString -> OSC {-# INLINE decodeOSC' #-}-decodeOSC' = runGet getOSC . B.fromChunks . (:[])+decodeOSC' = runGet getOSC . L.fromChunks . (:[])
Sound/OpenSoundControl/Coding/Encode/Builder.hs view
@@ -64,6 +64,9 @@ buildOSC (Bundle (UTCr t) l) = build_bundle_ntpi (utcr_ntpi t) l -- | Encode an OSC packet to a lazy ByteString.+--+-- > let b = L.pack [47,103,95,102,114,101,101,0,44,105,0,0,0,0,0,0]+-- > in encodeOSC (Message "/g_free" [Int 0]) == b encodeOSC :: OSC -> L.ByteString {-# INLINE encodeOSC #-} encodeOSC = B.toLazyByteString . buildOSC
Sound/OpenSoundControl/Time.hs view
@@ -7,10 +7,12 @@ import Data.Word import qualified Data.Time as T +-- * Temporal types+ -- | Type for integer representation of NTP time. type NTPi = Word64 --- | Time is represented in either UTC or NTP form. The NTP form may+-- | Time is represented in either @UTC@ or @NTP@ form. The @NTP@ form may -- be either integral or real. data Time = UTCr Double | NTPr Double | NTPi NTPi deriving (Read, Show)@@ -19,7 +21,7 @@ a == b = as_ntpi a == as_ntpi b a /= b = as_ntpi a /= as_ntpi b --- | Coerce to NTPi form.+-- | Coerce 'Time' to integral @NTP@ form. as_ntpi :: Time -> NTPi as_ntpi x = case x of@@ -27,7 +29,7 @@ NTPr t -> ntpr_ntpi t NTPi t -> t --- | Coerce to UTCr form.+-- | Coerce 'Time' to real-valued @UTC@ form. as_utcr :: Time -> Double as_utcr x = case x of@@ -44,27 +46,27 @@ (NTPi p',NTPi q') -> compare p' q' _ -> compare (as_ntpi p) (as_ntpi q) --- | Convert a real-valued NTP timestamp to an NTP timestamp.+-- | Convert a real-valued NTP timestamp to an 'NTPi' timestamp. ntpr_ntpi :: Double -> NTPi ntpr_ntpi t = round (t * 2^(32::Int)) --- | Convert an NTP timestamp to a real-valued NTP timestamp.+-- | Convert an 'NTPi' timestamp to a real-valued NTP timestamp. ntpi_ntpr :: NTPi -> Double ntpi_ntpr t = fromIntegral t / 2^(32::Int) --- | Convert UTC timestamp to NTP timestamp.+-- | Convert a real-valued UTC timestamp to an 'NTPi' timestamp. utcr_ntpi :: Double -> NTPi utcr_ntpi t = let secdif = (70 * 365 + 17) * 24 * 60 * 60 in ntpr_ntpi (t + secdif) --- | Convert NTP timestamp to UTC timestamp.+-- | Convert a real-valued NTP timestamp to a real-valued UTC timestamp. ntpr_utcr :: Double -> Double ntpr_utcr t = let secdif = (70 * 365 + 17) * 24 * 60 * 60 in t - secdif --- | Convert NTP timestamp to UTC timestamp.+-- | Convert an 'NTPi' timestamp to a real-valued UTC timestamp. ntpi_utcr :: NTPi -> Double ntpi_utcr = ntpr_utcr . ntpi_ntpr @@ -75,35 +77,41 @@ s = T.secondsToDiffTime 0 in T.UTCTime d s --- | Read current UTCr timestamp.+-- | Constant indicating the bundle is to be executed immediately.+immediately :: Time+immediately = NTPi 1++-- * Clock operations++-- | Read current real-valued @UTC@ timestamp. utcr :: IO Double utcr = do t <- T.getCurrentTime return (realToFrac (T.diffUTCTime t utc_base)) --- | Read current NTP timestamp.+-- | Read current 'NTPi' timestamp. ntpi :: IO NTPi ntpi = liftM utcr_ntpi utcr --- | The pauseThread limit (in seconds). Values larger than this--- require a different thread delay mechanism, see sleepThread. The--- value is the number of microseconds in maxBound::Int.+-- | The 'pauseThread' limit (in seconds). Values larger than this+-- require a different thread delay mechanism, see 'sleepThread'. The+-- value is the number of microseconds in @maxBound::Int@. pauseThreadLimit :: Double pauseThreadLimit = fromIntegral (maxBound::Int) / 1e6 -- | Pause current thread for the indicated duration (in seconds), see--- pauseThreadLimit. Note also that this function does not attempt--- pauses less than 1e-4.+-- 'pauseThreadLimit'. Note also that this function does not+-- attempt pauses less than @1e-4@. pauseThread :: Double -> IO () pauseThread n = when (n > 1e-4) (threadDelay (floor (n * 1e6))) --- | Pause current thread until the given utcr time, see--- pauseThreadLimit.+-- | Pause current thread until the given real-valued @UTC@ time, see+-- 'pauseThreadLimit'. pauseThreadUntil :: Double -> IO () pauseThreadUntil t = pauseThread . (t -) =<< utcr -- | Sleep current thread for the indicated duration (in seconds).--- Divides long sleeps into parts smaller than pauseThreadLimit.+-- Divides long sleeps into parts smaller than 'pauseThreadLimit'. sleepThread :: Double -> IO () sleepThread n = if n >= pauseThreadLimit@@ -111,11 +119,7 @@ in pauseThread n >> sleepThread (n - n') else pauseThread n --- | Sleep current thread until the given utcr time. Divides long--- sleeps into parts smaller than pauseThreadLimit.+-- | Sleep current thread until the given real-valued @UTC@ time.+-- Divides long sleeps into parts smaller than 'pauseThreadLimit'. sleepThreadUntil :: Double -> IO () sleepThreadUntil t = sleepThread . (t -) =<< utcr---- | Execute the bundle immediately.-immediately :: Time-immediately = NTPi 1
Sound/OpenSoundControl/Transport/TCP.hs view
@@ -6,6 +6,7 @@ import qualified Data.ByteString.Lazy as B import Control.Monad import Network+import Sound.OpenSoundControl.Coding import Sound.OpenSoundControl.Coding.Byte import Sound.OpenSoundControl.Transport import Sound.OpenSoundControl.Type@@ -27,8 +28,6 @@ b1 <- B.hGet fd (fromIntegral (decode_u32 b0)) return (dec b1) close (TCP _ _ fd) = hClose fd--type Coder = (OSC -> B.ByteString,B.ByteString -> OSC) -- | Make a TCP connection using specified coder. openTCP' :: Coder -> String -> Int -> IO TCP
Sound/OpenSoundControl/Transport/UDP.hs view
@@ -10,6 +10,7 @@ import qualified Network.Socket as N import qualified Network.Socket.ByteString as C (sendTo,recvFrom) import qualified Network.Socket.ByteString.Lazy as C (send,recv)+import Sound.OpenSoundControl.Coding import Sound.OpenSoundControl.Transport import Sound.OpenSoundControl.Type @@ -26,8 +27,6 @@ send (UDP enc _ fd) msg = C.send fd (enc msg) >> return () recv (UDP _ dec fd) = liftM dec (C.recv fd 8192) close (UDP _ _ fd) = N.sClose fd--type Coder = (OSC -> B.ByteString,B.ByteString -> OSC) -- | Make a UDP connection with specified coder. openUDP' :: Coder -> String -> Int -> IO UDP
Sound/OpenSoundControl/Type.hs view
@@ -1,9 +1,9 @@ -- | Alegbraic data types for OSC datum and packets.-module Sound.OpenSoundControl.Type ( OSC(..)- , Datum(..)- , message- , bundle- , tag ) where+module Sound.OpenSoundControl.Type (OSC(..)+ ,Datum(..)+ ,message+ ,bundle+ ,tag) where import qualified Data.ByteString.Lazy as B import Data.Word@@ -17,15 +17,15 @@ | Blob B.ByteString | TimeStamp Time | Midi (Word8,Word8,Word8,Word8)- deriving (Eq, Read, Show)+ deriving (Eq,Read,Show) -- | An OSC packet. data OSC = Message String [Datum] | Bundle Time [OSC]- deriving (Eq, Read, Show)+ deriving (Eq,Read,Show) -- | OSC bundles can be ordered (time ascending). Bundles and--- messages compare EQ.+-- messages compare 'EQ'. instance Ord OSC where compare (Bundle a _) (Bundle b _) = compare a b compare _ _ = EQ@@ -42,21 +42,17 @@ TimeStamp _ -> 't' Midi _ -> 'm' --- | Bundle constructor.------ Signals an error when @xs@ is empty.+-- | Bundle constructor. It is an 'error' if the 'OSC' list is empty. bundle :: Time -> [OSC] -> OSC bundle t xs = case xs of [] -> error "bundle: empty?" _ -> Bundle t xs --- | Message constructor------ Signals an error when the address @a@ doesn't conform to the OSC--- specification.+-- | Message constructor. It is an 'error' if the address doesn't+-- conform to the OSC specification. message :: String -> [Datum] -> OSC message a xs = case a of- ('/':_) -> Message a xs+ '/':_ -> Message a xs _ -> error "message: ill-formed address"
hosc.cabal view
@@ -1,9 +1,9 @@ Name: hosc-Version: 0.10.1+Version: 0.11 Synopsis: Haskell Open Sound Control-Description: hosc provides Sound.OpenSoundControl, a haskell- module implementing a subset of the Open Sound- Control byte protocol.+Description: @hosc@ provides "Sound.OpenSoundControl", an+ implementation of a subset of the /Open Sound Control/+ byte protocol documented at <http://opensoundcontrol.org/>. License: GPL Category: Sound Copyright: (c) Rohan Drape, Stefan Kersten and others, 2006-2011@@ -11,7 +11,7 @@ Maintainer: rd@slavepianos.org Stability: Experimental Homepage: http://slavepianos.org/rd/?t=hosc-Tested-With: GHC == 7.0.4+Tested-With: GHC == 7.2.2 Build-Type: Simple Cabal-Version: >= 1.8 Data-Files: README