diff --git a/Help/crotale.sdif b/Help/crotale.sdif
new file mode 100644
Binary files /dev/null and b/Help/crotale.sdif differ
diff --git a/Help/hsdif.hs b/Help/hsdif.hs
new file mode 100644
--- /dev/null
+++ b/Help/hsdif.hs
@@ -0,0 +1,54 @@
+import qualified Data.ByteString.Lazy as B
+import Sound.SDIF.Byte.Frame
+import Sound.SDIF.Byte.Matrix
+import Sound.SDIF.Byte.SDIF
+import Sound.SDIF.Frame
+import Sound.SDIF.Matrix
+import Sound.SDIF
+
+main :: IO ()
+main = do
+  b <- B.readFile "crotale.sdif"
+  -- (856,True,2)
+  print (show (B.length b,is_sdif_b b,sdif_b_frames b))
+  s <- sdif_read_file "crotale.sdif"
+  -- (2,[(0,16),(16,856)])
+  print (sdif_frames s,sdif_frame_i s)
+  let fb = sdif_frame_b s 1
+  -- ("1RES",832,0.0,2,1,[(24,840)])
+  print (frame_b_type fb
+        ,frame_b_size fb
+        ,frame_b_time fb
+        ,frame_b_id fb
+        ,frame_b_matrices fb
+        ,frame_b_matrix_i fb)
+  let mb = frame_b_data fb
+  -- ("1RES",4,50,4,200,800,816)
+  print (matrix_b_type mb
+        ,matrix_b_data_type mb
+        ,matrix_b_rows mb
+        ,matrix_b_columns mb
+        ,matrix_b_elements mb
+        ,matrix_b_data_size mb
+        ,matrix_b_storage_size mb)
+  let f = sdif_frame s 1
+  -- ("1RES",832,0.0,2,1,[(24,840)])
+  print (frame_type f
+        ,frame_size f
+        ,frame_time f
+        ,frame_id f
+        ,frame_matrices f
+        ,frame_matrix_i f)
+  let m = frame_matrix f 0
+  -- ("1RES",4,50,4,200,800,816)
+  print (matrix_type m
+        ,matrix_data_type m
+        ,matrix_rows m
+        ,matrix_columns m
+        ,matrix_elements m
+        ,matrix_data_size m
+        ,matrix_storage_size m)
+  -- ~ [F32 35.45,F32 1.28e-3,F32 5.20,F32 0.0]
+  print (matrix_row m 0)
+  -- ~ [F32 35.45,F32 128.59,F32 346.97]
+  print (take 3 (matrix_column m 0))
diff --git a/README b/README
new file mode 100644
--- /dev/null
+++ b/README
@@ -0,0 +1,8 @@
+hsdif - haskell sound description interchange format
+
+  http://slavepianos.org/rd/?t=hsdif
+  http://haskell.org/
+  http://sdif.sourceforge.net/
+
+(c) rohan drape, 2010-2011
+    gpl, http://gnu.org/copyleft/
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,3 @@
+#!/usr/bin/env runhaskell
+import Distribution.Simple
+main = defaultMain
diff --git a/Sound/SDIF.hs b/Sound/SDIF.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SDIF.hs
@@ -0,0 +1,53 @@
+-- | SDIF (Sound Description Interchange Format)
+module Sound.SDIF where
+
+import qualified Data.ByteString.Lazy as B
+import Sound.SDIF.Byte.SDIF
+import Sound.SDIF.Frame
+import Sound.SDIF.Matrix
+import Sound.SDIF.Type
+
+-- | SDIF data store.
+data SDIF = SDIF { sdif_b :: B.ByteString
+                 , sdif_frames :: Int
+                 , sdif_frame_i :: [(Int, Int)]
+                 , sdif_frame_c :: [Frame] }
+            deriving (Eq, Show)
+
+-- | Decode 'SDIF' data stream.
+decode_sdif :: B.ByteString -> SDIF
+decode_sdif sdf =
+    let n = sdif_b_frames sdf
+        ix = sdif_b_frame_i sdf n
+        frm (i,j) = decode_frame (section' sdf i j)
+        s = SDIF { sdif_b = sdf
+                 , sdif_frames = n
+                 , sdif_frame_i = ix
+                 , sdif_frame_c = map frm ix }
+    in if is_sdif_b sdf
+       then s
+       else error "decode_sdif" "illegal data"
+
+-- | Read and decode 'SDIF' from named file.
+sdif_read_file :: FilePath -> IO SDIF
+sdif_read_file file_name = do
+  b <- B.readFile file_name
+  return (decode_sdif b)
+
+-- | Extract /n/th frame data from 'SDIF'.
+sdif_frame_b :: SDIF -> Int -> B.ByteString
+sdif_frame_b sdf n =
+    let (i,j) = sdif_frame_i sdf !! n
+    in (section' (sdif_b sdf) i j)
+
+-- | Extract and decode /n/th frame from 'SDIF'.
+sdif_frame :: SDIF -> Int -> Frame
+sdif_frame sdf n = sdif_frame_c sdf !! n
+
+-- | Extract and decode /j/th matrix from /i/th frame from 'SDIF'.
+sdif_matrix :: SDIF -> Int -> Int -> Matrix
+sdif_matrix sdf i = frame_matrix (sdif_frame sdf i)
+
+-- | Run 'matrix_v' on result of 'sdif_matrix'.
+sdif_matrix_v :: SDIF -> Int -> Int -> [Datum]
+sdif_matrix_v sdf i = matrix_v . sdif_matrix sdf i
diff --git a/Sound/SDIF/Byte/Frame.hs b/Sound/SDIF/Byte/Frame.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SDIF/Byte/Frame.hs
@@ -0,0 +1,49 @@
+-- | Byte level SDIF frame data structure.
+module Sound.SDIF.Byte.Frame where
+
+import qualified Data.ByteString.Lazy as B
+import Sound.OpenSoundControl.Coding.Byte
+import Sound.SDIF.Byte.Matrix
+import Sound.SDIF.Type
+
+-- | Data integrity check for SDIF frame data stream.
+is_frame_b :: B.ByteString -> Bool
+is_frame_b frm =
+    let n = fromIntegral (B.length frm) - 8
+    in frame_b_size frm == n
+
+-- | Extract type string from SDIF frame byte stream.
+frame_b_type :: B.ByteString -> String
+frame_b_type frm = map (toEnum . fromIntegral) (B.unpack (section frm 0 4))
+
+-- | Extract size from SDIF frame byte stream.
+frame_b_size :: B.ByteString -> Int
+frame_b_size frm = decode_i32 (section frm 4 8)
+
+-- | Extract time stamp from SDIF frame byte stream.
+frame_b_time :: B.ByteString -> Double
+frame_b_time frm = decode_f32 (section frm 8 16)
+
+-- | Extract identifier from SDIF frame byte stream.
+frame_b_id :: B.ByteString -> Int
+frame_b_id frm = decode_i32 (section frm 16 20)
+
+-- | Extract matrix count from SDIF frame byte stream.
+frame_b_matrices :: B.ByteString -> Int
+frame_b_matrices frm = decode_i32 (section frm 20 24)
+
+-- | Extract frame data segment from SDIF frame byte stream.
+frame_b_data :: B.ByteString -> B.ByteString
+frame_b_data frm = section frm 24 (B.length frm)
+
+-- | Extract frame matrix /(start,end)/ indices from SDIF frame byte stream.
+frame_b_matrix_i :: B.ByteString -> [(Int, Int)]
+frame_b_matrix_i frm =
+    let go i j xs =
+            if j == 0
+            then reverse xs
+            else let h = section' frm i (i + matrix_b_header_size)
+                     sz = fromIntegral (matrix_b_storage_size h)
+                     xs' = (i, i + sz) : xs
+                 in go (i + sz) (j - sz) xs'
+    in go 24 (fromIntegral (B.length frm) - 24) []
diff --git a/Sound/SDIF/Byte/Matrix.hs b/Sound/SDIF/Byte/Matrix.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SDIF/Byte/Matrix.hs
@@ -0,0 +1,69 @@
+-- | Byte level SDIF frame data structure.
+module Sound.SDIF.Byte.Matrix where
+
+import qualified Data.ByteString.Lazy as B
+import Sound.OpenSoundControl.Coding.Byte
+import Sound.SDIF.Type
+
+-- | Data integrity check for SDIF matix byte stream.
+is_matrix_b :: B.ByteString -> Bool
+is_matrix_b mtx =
+    let n = fromIntegral (B.length mtx)
+    in matrix_b_storage_size mtx == n
+
+-- | Matrix header size (constant).
+matrix_b_header_size :: Int
+matrix_b_header_size = 16
+
+-- | Extract matrix header byte stream.
+matrix_b_header :: B.ByteString -> B.ByteString
+matrix_b_header mtx = section mtx 0 (fromIntegral matrix_b_header_size)
+
+-- | Extract matrix type string.
+matrix_b_type :: B.ByteString -> String
+matrix_b_type mtx = map (toEnum . fromIntegral) (B.unpack (section mtx 0 4))
+
+-- | Extract matrix element data 'Type'.
+matrix_b_data_type :: B.ByteString -> Type
+matrix_b_data_type mtx = decode_i32 (section mtx 4 8)
+
+-- | Extract matrix row count.
+matrix_b_rows :: B.ByteString -> Int
+matrix_b_rows mtx = decode_i32 (section mtx 8 12)
+
+-- | Extract matrix column count.
+matrix_b_columns :: B.ByteString -> Int
+matrix_b_columns mtx = decode_i32 (section mtx 12 16)
+
+-- | Extract matrix element count (ie. rows by columns).
+matrix_b_elements :: B.ByteString -> Int
+matrix_b_elements mtx = matrix_b_rows mtx * matrix_b_columns mtx
+
+-- | Calculate size of matrix data store (ie. elements by 'Type' size).
+matrix_b_data_size :: B.ByteString -> Int
+matrix_b_data_size mtx =
+    let r = matrix_b_rows mtx
+        c = matrix_b_columns mtx
+    in r * c * data_type_size (matrix_b_data_type mtx)
+
+-- | Variant of 'matrix_b_data_size' taking into account required padding.
+matrix_b_storage_size :: B.ByteString -> Int
+matrix_b_storage_size mtx =
+    let pad_bytes sz = let m = sz `mod` 8
+                       in if m > 0 then 8 - m else 0
+        n = matrix_b_data_size mtx
+    in n + pad_bytes n + 16
+
+-- | Extract matrix data from byte stream.
+matrix_b_to_matrix_v :: B.ByteString -> [Datum]
+matrix_b_to_matrix_v mtx =
+    let ty = matrix_b_data_type mtx
+        sz = data_type_size ty
+        dc = data_type_decoder ty
+        go i j xs =
+            if j == 0
+            then reverse xs
+            else let i' = i + sz
+                     xs' = dc (section' mtx i i') : xs
+                 in go i' (j - 1) xs'
+    in go 16 (matrix_b_elements mtx) []
diff --git a/Sound/SDIF/Byte/SDIF.hs b/Sound/SDIF/Byte/SDIF.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SDIF/Byte/SDIF.hs
@@ -0,0 +1,33 @@
+-- | Byte level SDIF data structure.
+module Sound.SDIF.Byte.SDIF where
+
+import qualified Data.ByteString.Lazy as B
+import Sound.OpenSoundControl.Coding.Byte
+import Sound.SDIF.Type
+
+-- | Check signature of SDIF byte stream.
+is_sdif_b :: B.ByteString -> Bool
+is_sdif_b sdf =
+    let sig = map (fromIntegral . fromEnum) "SDIF"
+    in B.unpack (section sdf 0 4) == sig
+
+-- | Count number of frames at SDIF byte stream.
+sdif_b_frames :: B.ByteString -> Int
+sdif_b_frames sdf =
+    let go i j n =
+            if j == 0
+            then n
+            else let sz = decode_i32 (section' sdf (i + 4) (i + 8)) + 8
+                 in go (i + sz) (j - sz) (n + 1)
+    in go 0 (fromIntegral (B.length sdf)) 0
+
+-- | Extract start and end indices for /n/ frames at SDIF byte stream.
+sdif_b_frame_i :: B.ByteString -> Int -> [(Int, Int)]
+sdif_b_frame_i sdf frame_count =
+    let go i j xs n =
+            if n == frame_count
+            then reverse xs
+            else let sz = decode_i32 (section' sdf (i + 4) (i + 8)) + 8
+                     xs' = (i, i + sz) : xs
+                 in go (i + sz) (j - sz) xs' (n + 1)
+    in go 0 (fromIntegral (B.length sdf)) [] 0
diff --git a/Sound/SDIF/Frame.hs b/Sound/SDIF/Frame.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SDIF/Frame.hs
@@ -0,0 +1,49 @@
+-- | SDIF frame functions
+module Sound.SDIF.Frame where
+
+import qualified Data.ByteString.Lazy as B
+import Sound.SDIF.Byte.Frame
+import Sound.SDIF.Matrix
+import Sound.SDIF.Type
+
+-- | SDIF frame data store
+data Frame = Frame { frame_b :: B.ByteString
+                   , frame_type :: String
+                   , frame_size :: Int
+                   , frame_time :: Double
+                   , frame_id :: Int
+                   , frame_matrices :: Int
+                   , frame_matrix_i :: [(Int, Int)]
+                   , frame_matrix_c :: [Matrix] }
+             deriving (Eq, Show)
+
+-- | Decode SDIF 'Frame'.
+decode_frame :: B.ByteString -> Frame
+decode_frame frm =
+    let ty = frame_b_type frm
+        sz = frame_b_size frm
+        mtx (i,j) = decode_matrix (section' frm i j)
+        ix = frame_b_matrix_i frm
+        f = if ty == "SDIF"
+            then Frame frm ty sz 0 0 0 [] []
+            else Frame { frame_b = frm
+                       , frame_type = ty
+                       , frame_size = sz
+                       , frame_time = frame_b_time frm
+                       , frame_id = frame_b_id frm
+                       , frame_matrices = frame_b_matrices frm
+                       , frame_matrix_i = ix
+                       , frame_matrix_c = map mtx ix }
+    in if is_frame_b frm
+       then f
+       else error "decode_frame" "illegal data"
+
+-- | Extract /n/th matrix of 'Frame'.
+frame_matrix_b :: Frame -> Int -> B.ByteString
+frame_matrix_b frm n =
+    let (i,j) = frame_matrix_i frm !! n
+    in section' (frame_b frm) i j
+
+-- | Extract and decode /n/th matrix of 'Frame'.
+frame_matrix :: Frame -> Int -> Matrix
+frame_matrix frm n = frame_matrix_c frm !! n
diff --git a/Sound/SDIF/Matrix.hs b/Sound/SDIF/Matrix.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SDIF/Matrix.hs
@@ -0,0 +1,64 @@
+-- | SDIF matrix functions.
+module Sound.SDIF.Matrix where
+
+import qualified Data.ByteString.Lazy as B
+import Sound.SDIF.Byte.Matrix
+import Sound.SDIF.Type
+
+-- | SDIF matrix data store.
+data Matrix = Matrix { matrix_b :: B.ByteString
+                     , matrix_type :: String
+                     , matrix_data_type :: Int
+                     , matrix_rows :: Int
+                     , matrix_columns :: Int
+                     , matrix_elements :: Int
+                     , matrix_data_size :: Int
+                     , matrix_storage_size :: Int
+                     , matrix_v :: [Datum] }
+              deriving (Eq, Show)
+
+-- | Decode 'Matrix'.
+decode_matrix :: B.ByteString -> Matrix
+decode_matrix mtx =
+    let m = Matrix { matrix_b = mtx
+                   , matrix_type = matrix_b_type mtx
+                   , matrix_data_type = matrix_b_data_type mtx
+                   , matrix_rows = matrix_b_rows mtx
+                   , matrix_columns = matrix_b_columns mtx
+                   , matrix_elements = matrix_b_elements mtx
+                   , matrix_data_size = matrix_b_data_size mtx
+                   , matrix_storage_size = matrix_b_storage_size mtx
+                   , matrix_v = matrix_b_to_matrix_v mtx }
+    in if is_matrix_b mtx
+       then m
+       else error "decode_matrix: illegal data"
+
+-- | Section of list from /i/th to /j/th indices.
+--
+-- > list_section [1..9] 4 6 == [5,6]
+list_section :: [a] -> Int -> Int -> [a]
+list_section xs i j = take (j - i) (drop i xs)
+
+-- | Extract /n/th row of 'Matrix'.
+matrix_row :: Matrix -> Int -> [Datum]
+matrix_row m n =
+    let r = matrix_rows m
+        c = matrix_columns m
+        i = n * c
+    in if n >= r
+       then error "matrix_row: domain error"
+       else list_section (matrix_v m) i (i + c)
+
+-- | Extract /n/th column of 'Matrix'.
+matrix_column :: Matrix -> Int -> [Datum]
+matrix_column m n =
+    let nr = matrix_rows m
+        nc = matrix_columns m
+        v = matrix_v m
+        build i xs =
+            if i == nr
+            then reverse xs
+            else build (i + 1) ((v !! (n + (i * nc))) : xs)
+    in if n >= nc
+       then error "matrix_column: domain error"
+       else build 0 []
diff --git a/Sound/SDIF/Type.hs b/Sound/SDIF/Type.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SDIF/Type.hs
@@ -0,0 +1,89 @@
+-- | SDIF related data types
+module Sound.SDIF.Type where
+
+import Data.Bits
+import qualified Data.ByteString.Lazy as B
+import Data.Int
+import Data.Word
+import Sound.OpenSoundControl.Coding.Byte
+
+-- | Section of 'B.ByteString' from /i/th to /j/th indices.
+section :: B.ByteString -> Int64 -> Int64 -> B.ByteString
+section xs i j = B.take (j - i) (B.drop i xs)
+
+-- | 'Int' based variant of 'section'.
+section' :: B.ByteString -> Int -> Int -> B.ByteString
+section' xs i j = section xs (fromIntegral i) (fromIntegral j)
+
+-- | Data element type.
+type Type = Int
+
+-- | Is data element type standard.
+data_type_standard_p :: Type -> Bool
+data_type_standard_p d =
+    let xs = [0x004, 0x008
+             ,0x101, 0x102, 0x104, 0x108
+             ,0x201, 0x202, 0x204, 0x208
+             ,0x301
+             ,0x401]
+    in d `elem` xs
+
+-- | String describing indicated data element type.
+--
+-- > data_type_string 0x008 == "real number"
+data_type_string :: Type -> String
+data_type_string d
+    | d `elem` [0x004,0x008] = "real number"
+    | d `elem` [0x101,0x102,0x104,0x108] = "signed integer"
+    | d `elem` [0x201,0x202,0x204,0x208] = "unsigned integer"
+    | d `elem` [0x301] = "utf_8 byte"
+    | d `elem` [0x401] = "byte"
+    | otherwise = "unknown"
+
+-- | Size (in bytes) of data element type.
+--
+-- > data_type_size 0x008 == 8
+data_type_size :: Type -> Int
+data_type_size d = d .&. 0xff
+
+-- | Universal type for element data.
+data Datum = I8 Int
+           | I16 Int
+           | I32 Int
+           | I64 Int64
+           | U32 Int
+           | U64 Word64
+           | F32 Double
+           | F64 Double
+             deriving (Eq, Show)
+
+-- | Decoder for indicated data element type to 'Datum'.
+data_type_decoder :: Type -> B.ByteString -> Datum
+data_type_decoder d x =
+    case d of
+      0x004 -> F32 (decode_f32 x)
+      0x008 -> F64 (decode_f64 x)
+      0x101 -> I8 (decode_i8 x)
+      0x102 -> I16 (decode_i16 x)
+      0x104 -> I32 (decode_i32 x)
+      0x108 -> I64 (decode_i64 x)
+      0x201 -> undefined -- decode_u8
+      0x202 -> undefined -- decode_u16
+      0x204 -> U32 (decode_u32 x)
+      0x208 -> U64 (decode_u64 x)
+      0x301 -> undefined
+      0x401 -> undefined
+      _ -> undefined
+
+-- | SDIF encoder for 'Datum'.
+data_type_encoder :: Datum -> B.ByteString
+data_type_encoder x =
+    case x of
+      I8 n -> encode_i8 n
+      I16 n -> encode_i16 n
+      I32 n -> encode_i32 n
+      I64 n -> encode_i64 n
+      U32 n -> encode_u32 n
+      U64 n -> encode_u64 n
+      F32 n -> encode_f32 n
+      F64 n -> encode_f64 n
diff --git a/hsdif.cabal b/hsdif.cabal
new file mode 100644
--- /dev/null
+++ b/hsdif.cabal
@@ -0,0 +1,37 @@
+Name:              hsdif
+Version:           0.11
+Synopsis:          Haskell SDIF
+Description:       hsdif provides Sound.SDIF, a haskell
+                   module implementing a subset of Sound Description
+                   Interchange Format.
+License:           GPL
+Category:          Sound
+Copyright:         (c) Rohan Drape, 2011
+Author:            Rohan Drape
+Maintainer:        rd@slavepianos.org
+Stability:         Experimental
+Homepage:          http://slavepianos.org/rd/?t=hsdif
+Tested-With:       GHC == 7.2.2
+Build-Type:        Simple
+Cabal-Version:     >= 1.8
+
+Data-Files:        README
+                   Help/hsdif.hs
+                   Help/crotale.sdif
+
+Library
+  Build-Depends:   base == 4.*,
+                   bytestring,
+                   hosc == 0.11.*
+  GHC-Options:     -Wall -fwarn-tabs
+  Exposed-modules: Sound.SDIF
+                   Sound.SDIF.Frame
+                   Sound.SDIF.Matrix
+                   Sound.SDIF.Type
+                   Sound.SDIF.Byte.Frame
+                   Sound.SDIF.Byte.Matrix
+                   Sound.SDIF.Byte.SDIF
+
+Source-Repository  head
+  Type:            darcs
+  Location:        http://slavepianos.org/rd/sw/hsdif/
