diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,31 @@
+Copyright (c) 2012, Henning Thielemann
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * The names of contributors may not be used to endorse or promote
+      products derived from this software without specific prior
+      written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,3 @@
+#! /usr/bin/env runhaskell
+> import Distribution.Simple
+> main = defaultMain
diff --git a/soxlib.cabal b/soxlib.cabal
new file mode 100644
--- /dev/null
+++ b/soxlib.cabal
@@ -0,0 +1,54 @@
+Name:             soxlib
+Version:          0.0
+License:          BSD3
+License-File:     LICENSE
+Author:           Henning Thielemann <haskell@henning-thielemann.de>
+Maintainer:       Henning Thielemann <haskell@henning-thielemann.de>
+Homepage:         http://www.haskell.org/haskellwiki/Sox
+Category:         Sound
+Synopsis:         Write, read, convert audio signals using libsox
+Description:
+   This is an FFI binding to @libsox@ of the Sox (Sound Exchanger) project
+   <http://sox.sourceforge.net/>.
+   It lets write, read and convert audio signals
+   in various formats, resolutions, and numbers of channels.
+   .
+   The package @sox@ has similar functionality
+   but calls the @sox@ shell command.
+Tested-With:       GHC==6.12.3, GHC==7.4.1
+Cabal-Version:     >=1.6
+Build-Type:        Simple
+Extra-Source-Files:
+  src/Sound/SoxLib/Template.h
+
+Source-Repository this
+  Tag:         0.0
+  Type:        darcs
+  Location:    http://code.haskell.org/~thielema/soxlib/
+
+Source-Repository head
+  Type:        darcs
+  Location:    http://code.haskell.org/~thielema/soxlib/
+
+Library
+  Build-Depends:
+    sample-frame >=0.0.1 && <0.1,
+    storablevector >=0.2.7 && <0.3,
+    explicit-exception >=0.1.3 && < 0.2,
+    -- that's the way to get compatibility between GHC 6.10 and 6.12
+    extensible-exceptions >=0.1.1 && <0.2,
+    transformers >=0.2 && <0.4,
+    containers >=0.1 && <0.5,
+    utility-ht >=0.0.5 && <0.1,
+    base >=4 && <5
+
+  GHC-Options: -Wall
+  Hs-Source-Dirs: src
+  Include-Dirs: src
+
+  Exposed-Modules:
+    Sound.SoxLib
+  Other-Modules:
+    Sound.SoxLib.FFI
+
+  PkgConfig-Depends: sox >=14.3 && <14.4
diff --git a/src/Sound/SoxLib.hs b/src/Sound/SoxLib.hs
new file mode 100644
--- /dev/null
+++ b/src/Sound/SoxLib.hs
@@ -0,0 +1,174 @@
+module Sound.SoxLib (
+   with, formatWith,
+   withRead,  readStorableVector,  readStorableVectorLazy,
+   withWrite, writeStorableVector, writeStorableVectorLazy,
+   seek,
+   FFI.Mode(..), FFI.ReadMode, FFI.WriteMode,
+   ReaderInfo(..), defaultReaderInfo,
+   WriterInfo(..), defaultWriterInfo,
+
+   FFI.Rate,
+   FFI.FileType(..),
+   FFI.Format(..),
+   FFI.SignalInfo(..),
+   FFI.EncodingInfo(..),
+   FFI.defaultSignalInfo,
+   ) where
+
+import qualified Sound.SoxLib.FFI as FFI
+
+import qualified Foreign.Marshal.Utils as U
+import qualified Foreign.C.String as CStr
+import Foreign.Storable (Storable, peek, )
+import Foreign.Ptr (Ptr, nullFunPtr, nullPtr, )
+
+import Control.Exception (bracket_, bracket, )
+import System.IO.Error (mkIOError, doesNotExistErrorType, )
+
+import qualified Data.StorableVector.Base as SVB
+import qualified Data.StorableVector.Lazy as SVL
+import qualified Data.StorableVector as SV
+
+import qualified Control.Monad.Trans.Cont as MC
+import Control.Monad (when, )
+
+import System.IO.Unsafe (unsafeInterleaveIO, )
+
+import Data.Int (Int32, )
+
+
+with :: IO a -> IO a
+with = bracket_ FFI.init FFI.quit
+
+formatWith :: IO a -> IO a
+formatWith = bracket_ FFI.formatInit FFI.formatQuit
+
+
+withMaybe ::
+   Storable b =>
+   (a -> (Ptr b -> IO c) -> IO c) ->
+   Maybe a -> MC.ContT c IO (Ptr b)
+withMaybe _ Nothing = return nullPtr
+withMaybe f (Just a) = MC.ContT $ f a
+
+
+data ReaderInfo =
+   ReaderInfo {
+      readerSignalInfo :: Maybe FFI.SignalInfo,
+      readerEncodingInfo :: Maybe FFI.EncodingInfo,
+      readerFileType :: Maybe FFI.FileType
+   }
+
+defaultReaderInfo :: ReaderInfo
+defaultReaderInfo = ReaderInfo Nothing Nothing Nothing
+
+withRead ::
+   ReaderInfo -> FilePath ->
+   (Ptr (FFI.Format FFI.ReadMode) -> IO a) -> IO a
+withRead info path =
+   MC.runContT $ do
+      si  <- withMaybe U.with $ readerSignalInfo info
+      enc <- withMaybe U.with $ readerEncodingInfo info
+      cft <- withMaybe CStr.withCString $
+                 fmap FFI.unFileType $ readerFileType info
+      cpath <- MC.ContT $ CStr.withCString path
+      MC.ContT $
+         bracket
+            (do fmt <- FFI.openRead cpath si enc cft
+                if fmt==nullPtr
+                  then throwDoesNotExist "SoxLib.withRead" path
+                  else return fmt)
+            FFI.close
+
+
+data WriterInfo =
+   WriterInfo {
+      writerSignalInfo :: Maybe FFI.SignalInfo,
+      writerEncodingInfo :: Maybe FFI.EncodingInfo,
+      writerFileType :: Maybe FFI.FileType
+   }
+
+defaultWriterInfo :: WriterInfo
+defaultWriterInfo = WriterInfo Nothing Nothing Nothing
+
+withWrite ::
+   WriterInfo -> FilePath ->
+   (Ptr (FFI.Format FFI.WriteMode) -> IO a) -> IO a
+withWrite info path =
+   MC.runContT $ do
+      si  <- withMaybe U.with $ writerSignalInfo info
+      enc <- withMaybe U.with $ writerEncodingInfo info
+      cft <- withMaybe CStr.withCString $
+                 fmap FFI.unFileType $ writerFileType info
+      cpath <- MC.ContT $ CStr.withCString path
+      MC.ContT $
+         bracket
+            (do fmt <- FFI.openWrite cpath si enc cft nullPtr nullFunPtr
+                if fmt==nullPtr
+                  then throwDoesNotExist "SoxLib.withWrite" path
+                  else return fmt)
+            FFI.close
+
+
+throwDoesNotExist :: String -> FilePath -> IO a
+throwDoesNotExist name path =
+   ioError $
+      mkIOError doesNotExistErrorType
+         name Nothing (Just path)
+
+{- |
+Multi-channel data is interleaved.
+@size@ must be divisible by the number of channels.
+-}
+readStorableVector ::
+   Ptr (FFI.Format FFI.ReadMode) -> Int -> IO (SV.Vector Int32)
+readStorableVector fmt size =
+   SVB.createAndTrim size $ \ptr ->
+   fmap fromIntegral $ FFI.read fmt ptr (fromIntegral size)
+
+{- |
+Multi-channel data is interleaved.
+@size@ must be divisible by the number of channels.
+-}
+writeStorableVector ::
+   Ptr (FFI.Format FFI.WriteMode) -> SV.Vector Int32 -> IO ()
+writeStorableVector fmt chunk =
+   SVB.withStartPtr chunk $ \ptr len -> do
+      written <- FFI.write fmt ptr (fromIntegral len)
+      when (written < fromIntegral len) $ do
+         f <- peek fmt
+         ioError $ userError $ FFI.soxErrStr f
+
+
+{- |
+Read complete file lazily into chunky storable vector.
+The chunkSize must be divisible by the number of channels.
+-}
+readStorableVectorLazy ::
+   Ptr (FFI.Format FFI.ReadMode) -> SVL.ChunkSize -> IO (SVL.Vector Int32)
+readStorableVectorLazy fmt (SVL.ChunkSize size) =
+   let go =
+          unsafeInterleaveIO $ do
+             chunk <- readStorableVector fmt size
+             if SV.length chunk >= size
+               then fmap (chunk:) go
+               else if SV.length chunk == 0
+                      then return []
+                      else return [chunk]
+   in  fmap SVL.fromChunks go
+
+{- |
+The chunkSize must be divisible by the number of channels.
+-}
+writeStorableVectorLazy ::
+   Ptr (FFI.Format FFI.WriteMode) -> SVL.Vector Int32 -> IO ()
+writeStorableVectorLazy fmt =
+   mapM_ (writeStorableVector fmt) . SVL.chunks
+
+
+seek :: (FFI.Mode mode) => Ptr (FFI.Format mode) -> Int -> IO ()
+seek fmt pos = do
+   res <- FFI.seek fmt (fromIntegral pos) 0
+   when (res /= 0) $ do
+      f <- peek fmt
+      ioError $ userError $ FFI.soxErrStr f
diff --git a/src/Sound/SoxLib/FFI.hsc b/src/Sound/SoxLib/FFI.hsc
new file mode 100644
--- /dev/null
+++ b/src/Sound/SoxLib/FFI.hsc
@@ -0,0 +1,342 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+module Sound.SoxLib.FFI where
+
+#include <sox.h>
+#include <Sound/SoxLib/Template.h>
+
+import qualified Foreign.C.String as CStr
+import qualified Foreign.C.Types as C
+import qualified Foreign.C.Error as E
+import Foreign.Storable (Storable, sizeOf, alignment, peek, peekByteOff, poke, pokeByteOff, )
+import Foreign.Ptr (FunPtr, Ptr, nullPtr, plusPtr, )
+
+import Control.Applicative (pure, (<*>), )
+
+import Data.Maybe.HT (toMaybe, )
+import Data.Maybe (fromMaybe, )
+
+import qualified Data.Word as Word
+-- import qualified Data.Int as Int
+-- we must import it unqualified, because Hsc calls it that way
+import Data.Word
+import Data.Int
+
+import qualified Prelude as P
+import Prelude hiding (Bool, init, length, )
+
+
+
+newtype IOType = IOType #{type lsx_io_type}
+   deriving (Storable)
+
+#{enum IOType, IOType
+   , ioFile = lsx_io_file
+   , ioPipe = lsx_io_pipe
+   , ioURL = lsx_io_url
+   }
+
+data Format mode =
+   Format {
+      filename :: FilePath,
+      signalInfo :: SignalInfo,
+      encodingInfo :: EncodingInfo,
+      filetype :: FileType,
+      -- oob :: OOB,
+      seekable :: P.Bool,
+      olength :: Int,
+      clips :: Int,
+      soxErrno :: E.Errno,
+      soxErrStr :: String,
+      fp :: Ptr C.CFile,
+      ioType :: IOType,
+      tellOff :: C.CLong,
+      dataStart :: C.CLong,
+      -- handler :: FormatHandler,
+      priv :: Ptr ()
+   }
+
+instance Mode mode => Storable (Format mode) where
+   sizeOf _    = #{size sox_format_t}
+   alignment _ = #{alignment sox_format_t}
+   peek p =
+      pure Format
+         <*> (peekEmptyCString =<< #{peek sox_format_t, filename} p)
+         <*> #{peek sox_format_t, signal} p
+         <*> #{peek sox_format_t, encoding} p
+         <*> (fmap FileType . peekEmptyCString =<<
+              #{peek sox_format_t, filetype} p)
+         -- <*> #{peek sox_format_t, oob} p
+         <*> peekBool (#{ptr sox_format_t, seekable} p)
+         <*> (#{peek_int sox_format_t, olength} p)
+         <*> (#{peek_int sox_format_t, clips} p)
+         <*> fmap E.Errno (#{peek sox_format_t, sox_errno} p)
+         <*> (CStr.peekCStringLen (#{ptr sox_format_t, sox_errstr} p, 256))
+         <*> #{peek sox_format_t, fp} p
+         <*> #{peek sox_format_t, io_type} p
+         <*> #{peek sox_format_t, tell_off} p
+         <*> #{peek sox_format_t, data_start} p
+         -- <*> #{peek sox_format_t, handler} p
+         <*> #{peek sox_format_t, priv} p
+   poke = error "SoxLib.Format.poke cannot be implemented because it requires temporary memory"
+
+
+peekEmptyCString :: CStr.CString -> IO String
+peekEmptyCString ptr =
+   if ptr == nullPtr
+     then return ""
+     else CStr.peekCString ptr
+
+
+class Mode mode where
+   getModeChar :: Format mode -> C.CChar
+
+data ReadMode  = ReadMode
+data WriteMode = WriteMode
+
+instance Mode ReadMode where
+   getModeChar _ = CStr.castCharToCChar 'r'
+
+instance Mode WriteMode where
+   getModeChar _ = CStr.castCharToCChar 'w'
+
+
+type Rate = Double
+
+ignoreLength :: Int
+ignoreLength = -1
+
+data SignalInfo =
+   SignalInfo {
+      rate :: Maybe Rate,
+      channels :: Maybe Int,
+      precision :: Maybe Int,
+      length :: Maybe Int,
+      mult :: Maybe Double
+   }
+   deriving (Show)
+
+defaultSignalInfo :: SignalInfo
+defaultSignalInfo =
+   SignalInfo Nothing Nothing Nothing Nothing Nothing
+
+readSINumber :: (Num a, Eq a) => a -> Maybe a
+readSINumber n =
+   toMaybe (n /= #{const SOX_UNSPEC}) n
+
+writeSINumber :: (Num a, Eq a) => Maybe a -> a
+writeSINumber =
+   fromMaybe (#{const SOX_UNSPEC})
+
+
+instance Storable SignalInfo where
+   sizeOf _    = #{size sox_signalinfo_t}
+   alignment _ = #{alignment sox_signalinfo_t}
+   peek p =
+      pure SignalInfo
+         <*> (fmap readSINumber $ peekDouble (#{ptr sox_signalinfo_t, rate} p))
+         <*> (fmap readSINumber $ #{peek_int sox_signalinfo_t, channels} p)
+         <*> (fmap readSINumber $ #{peek_int sox_signalinfo_t, precision} p)
+         <*> (fmap readSINumber $ #{peek_int sox_signalinfo_t, length} p)
+         <*> (do pd <- #{peek sox_signalinfo_t, mult} p
+                 if pd == nullPtr
+                   then return Nothing
+                   else fmap Just $ peekDouble pd)
+   poke p v =
+      pokeDouble (#{ptr sox_signalinfo_t, rate} p) (writeSINumber $ rate v) >>
+      #{poke sox_signalinfo_t, channels } p (writeSINumber $ channels  v) >>
+      #{poke sox_signalinfo_t, precision} p (writeSINumber $ precision v) >>
+      #{poke sox_signalinfo_t, length   } p (writeSINumber $ length    v) >>
+      case mult v of
+         Nothing -> #{poke sox_signalinfo_t, mult} p nullPtr
+         Just _ ->
+            error "SoxLib.SignalInfo.poke: Just-mult cannot simply be poked because it requires temporary memory"
+
+
+newtype Option = Option #{type sox_option_t}
+   deriving (Storable)
+
+#{enum Option, Option
+   , optionNo = SOX_OPTION_NO
+   , optionYes = SOX_OPTION_YES
+   , optionDefault = SOX_OPTION_DEFAULT
+ }
+
+instance Show Option where
+   show (Option #{const SOX_OPTION_NO     }) = "optionNo"
+   show (Option #{const SOX_OPTION_YES    }) = "optionYes"
+   show (Option #{const SOX_OPTION_DEFAULT}) = "optionDefault"
+   show (Option n) = error $ "SoxLib.Option.show :invalid number " ++ show n
+
+
+data EncodingInfo =
+   EncodingInfo {
+      encoding :: Encoding,
+      bitsPerSample :: Int,
+      compression :: Double,
+
+      reverseBytes, reverseNibbles, reverseBits :: Option,
+      oppositeEndian :: P.Bool
+   }
+   deriving (Show)
+
+instance Storable EncodingInfo where
+   sizeOf _    = #{size sox_encodinginfo_t}
+   alignment _ = #{alignment sox_encodinginfo_t}
+   peek p =
+      pure EncodingInfo
+         <*> (#{peek sox_encodinginfo_t, encoding} p)
+         <*> (#{peek_int sox_encodinginfo_t, bits_per_sample} p)
+         <*> (peekDouble $ #{ptr sox_encodinginfo_t, compression} p)
+         <*> (#{peek sox_encodinginfo_t, reverse_bytes} p)
+         <*> (#{peek sox_encodinginfo_t, reverse_nibbles} p)
+         <*> (#{peek sox_encodinginfo_t, reverse_bits} p)
+         <*> (peekBool $ #{ptr sox_encodinginfo_t, opposite_endian} p)
+   poke p v =
+      #{poke     sox_encodinginfo_t, encoding       } p (encoding      v) >>
+      #{poke_int sox_encodinginfo_t, bits_per_sample} p (bitsPerSample v) >>
+      pokeDouble (#{ptr sox_encodinginfo_t, compression} p) (compression v) >>
+      #{poke     sox_encodinginfo_t, reverse_bytes  } p (reverseBytes   v) >>
+      #{poke     sox_encodinginfo_t, reverse_nibbles} p (reverseNibbles v) >>
+      #{poke     sox_encodinginfo_t, reverse_bits   } p (reverseBits    v) >>
+      pokeBool (#{ptr sox_encodinginfo_t, opposite_endian} p) (oppositeEndian v)
+
+
+newtype Bool = Bool #{inttype sox_bool}
+   deriving (Show, Eq, Storable)
+
+#{enum Bool, Bool
+   , false = sox_false
+   , true = sox_true
+   }
+
+packBool :: P.Bool -> Bool
+packBool False = false
+packBool True = true
+
+unpackBool :: Bool -> P.Bool
+unpackBool x = x/=false
+
+
+peekBool :: Ptr Bool -> IO P.Bool
+peekBool p = fmap unpackBool $ peek p
+
+pokeBool :: Ptr Bool -> P.Bool -> IO ()
+pokeBool p b = poke p $ packBool b
+
+
+peekDouble :: Ptr C.CDouble -> IO Double
+peekDouble p = fmap realToFrac $ peek p
+
+pokeDouble :: Ptr C.CDouble -> Double -> IO ()
+pokeDouble p x = poke p $ realToFrac x
+
+
+newtype Encoding = Encoding #{inttype sox_encoding_t}
+   deriving (Storable)
+
+instance Bounded Encoding where
+   minBound = Encoding #{const SOX_ENCODING_UNKNOWN}
+   maxBound = Encoding $ pred #{const SOX_ENCODINGS}
+
+#{enum Encoding, Encoding
+   , encodingUnknown    = SOX_ENCODING_UNKNOWN
+   , encodingSign2      = SOX_ENCODING_SIGN2
+   , encodingUnsigned   = SOX_ENCODING_UNSIGNED
+   , encodingFloat      = SOX_ENCODING_FLOAT
+   , encodingFloatText  = SOX_ENCODING_FLOAT_TEXT
+   , encodingFlac       = SOX_ENCODING_FLAC
+   , encodingHcom       = SOX_ENCODING_HCOM
+   , encodingWavpack    = SOX_ENCODING_WAVPACK
+   , encodingWavpackf   = SOX_ENCODING_WAVPACKF
+   , encodingUlaw       = SOX_ENCODING_ULAW
+   , encodingAlaw       = SOX_ENCODING_ALAW
+   , encodingG721       = SOX_ENCODING_G721
+   , encodingG723       = SOX_ENCODING_G723
+   , encodingClADPCM    = SOX_ENCODING_CL_ADPCM
+   , encodingClADPCM16  = SOX_ENCODING_CL_ADPCM16
+   , encodingMsADPCM    = SOX_ENCODING_MS_ADPCM
+   , encodingImaADPCM   = SOX_ENCODING_IMA_ADPCM
+   , encodingOkiADPCM   = SOX_ENCODING_OKI_ADPCM
+   , encodingDPCM       = SOX_ENCODING_DPCM
+   , encodingDWVW       = SOX_ENCODING_DWVW
+   , encodingDWVWN      = SOX_ENCODING_DWVWN
+   , encodingGSM        = SOX_ENCODING_GSM
+   , encodingMP3        = SOX_ENCODING_MP3
+   , encodingVorbis     = SOX_ENCODING_VORBIS
+   , encodingAmrWB      = SOX_ENCODING_AMR_WB
+   , encodingAmrNB      = SOX_ENCODING_AMR_NB
+   , encodingCVSD       = SOX_ENCODING_CVSD
+   , encodingLPC10      = SOX_ENCODING_LPC10
+}
+
+instance Show Encoding where
+   show (Encoding #{const SOX_ENCODING_UNKNOWN   }) = "encodingUnknown"
+   show (Encoding #{const SOX_ENCODING_SIGN2     }) = "encodingSign2"
+   show (Encoding #{const SOX_ENCODING_UNSIGNED  }) = "encodingUnsigned"
+   show (Encoding #{const SOX_ENCODING_FLOAT     }) = "encodingFloat"
+   show (Encoding #{const SOX_ENCODING_FLOAT_TEXT}) = "encodingFloatText"
+   show (Encoding #{const SOX_ENCODING_FLAC      }) = "encodingFlac"
+   show (Encoding #{const SOX_ENCODING_HCOM      }) = "encodingHcom"
+   show (Encoding #{const SOX_ENCODING_WAVPACK   }) = "encodingWavpack"
+   show (Encoding #{const SOX_ENCODING_WAVPACKF  }) = "encodingWavpackf"
+   show (Encoding #{const SOX_ENCODING_ULAW      }) = "encodingUlaw"
+   show (Encoding #{const SOX_ENCODING_ALAW      }) = "encodingAlaw"
+   show (Encoding #{const SOX_ENCODING_G721      }) = "encodingG721"
+   show (Encoding #{const SOX_ENCODING_G723      }) = "encodingG723"
+   show (Encoding #{const SOX_ENCODING_CL_ADPCM  }) = "encodingClADPCM"
+   show (Encoding #{const SOX_ENCODING_CL_ADPCM16}) = "encodingClADPCM16"
+   show (Encoding #{const SOX_ENCODING_MS_ADPCM  }) = "encodingMsADPCM"
+   show (Encoding #{const SOX_ENCODING_IMA_ADPCM }) = "encodingImaADPCM"
+   show (Encoding #{const SOX_ENCODING_OKI_ADPCM }) = "encodingOkiADPCM"
+   show (Encoding #{const SOX_ENCODING_DPCM      }) = "encodingDPCM"
+   show (Encoding #{const SOX_ENCODING_DWVW      }) = "encodingDWVW"
+   show (Encoding #{const SOX_ENCODING_DWVWN     }) = "encodingDWVWN"
+   show (Encoding #{const SOX_ENCODING_GSM       }) = "encodingGSM"
+   show (Encoding #{const SOX_ENCODING_MP3       }) = "encodingMP3"
+   show (Encoding #{const SOX_ENCODING_VORBIS    }) = "encodingVorbis"
+   show (Encoding #{const SOX_ENCODING_AMR_WB    }) = "encodingAmrWB"
+   show (Encoding #{const SOX_ENCODING_AMR_NB    }) = "encodingAmrNB"
+   show (Encoding #{const SOX_ENCODING_CVSD      }) = "encodingCVSD"
+   show (Encoding #{const SOX_ENCODING_LPC10     }) = "encodingLPC10"
+   show (Encoding n) = "(Encoding " ++ show n ++ ")"
+
+
+
+newtype FileType = FileType {unFileType :: String}
+type CFileType = CStr.CString
+type Sample = #{type sox_sample_t}
+type OOB = ()
+-- type FormatHandler = ()
+type Whence = C.CInt
+
+
+foreign import ccall unsafe "sox.h sox_init"
+   init :: IO C.CInt
+
+foreign import ccall unsafe "sox.h sox_quit"
+   quit :: IO C.CInt
+
+foreign import ccall unsafe "sox.h sox_format_init"
+   formatInit :: IO C.CInt
+
+foreign import ccall unsafe "sox.h sox_format_quit"
+   formatQuit :: IO ()
+
+foreign import ccall unsafe "sox.h sox_open_read"
+   openRead :: CStr.CString -> Ptr SignalInfo -> Ptr EncodingInfo -> CFileType -> IO (Ptr (Format ReadMode))
+
+foreign import ccall safe "sox.h sox_open_write"
+   openWrite :: CStr.CString -> Ptr SignalInfo -> Ptr EncodingInfo -> CFileType -> Ptr OOB -> FunPtr (CStr.CString -> IO Bool) -> IO (Ptr (Format WriteMode))
+
+foreign import ccall unsafe "sox.h sox_read"
+   read :: Ptr (Format ReadMode) -> Ptr Sample -> C.CSize -> IO C.CSize
+
+foreign import ccall unsafe "sox.h sox_write"
+   write :: Ptr (Format WriteMode) -> Ptr Sample -> C.CSize -> IO C.CSize
+
+foreign import ccall unsafe "sox.h sox_close"
+   close :: Ptr (Format mode) -> IO C.CInt
+
+foreign import ccall unsafe "sox.h sox_seek"
+   seek :: Ptr (Format mode) -> C.CSize -> Whence -> IO C.CInt
diff --git a/src/Sound/SoxLib/Template.h b/src/Sound/SoxLib/Template.h
new file mode 100644
--- /dev/null
+++ b/src/Sound/SoxLib/Template.h
@@ -0,0 +1,38 @@
+#include <stdio.h>
+
+/* http://www.haskell.org/haskellwiki/FFICookBook#Working_with_structs */
+#define hsc_alignment(t) \
+    printf("(%ld)", (unsigned long)offsetof(struct {char x__; t (y__); }, y__));
+
+#define hsc_inttype(t) \
+    printf ("%s%lu",                                    \
+            (t)(-1) < (t)0 ? "Int.Int" : "Word.Word",   \
+            (unsigned long)sizeof (t) * 8);             \
+
+#define hsc_intfieldtype(t,f) \
+    { \
+        t x, y;    \
+        x.f = -1;  \
+        y.f = 0;   \
+        printf ("%s%lu",                               \
+                x.f < y.f ? "Int.Int" : "Word.Word",   \
+                (unsigned long)sizeof (x.f) * 8);      \
+    }
+
+#define hsc_peek_int(t, f) \
+    { \
+        t *x = 0; \
+        printf ("(\\hsc_ptr -> fmap fromIntegral (peekByteOff hsc_ptr %ld :: IO ", \
+            (long) &(x->f)); \
+        hsc_intfieldtype (t, f) \
+        printf ("))"); \
+    }
+
+#define hsc_poke_int(t, f) \
+    { \
+        t *x = 0; \
+        printf ("(\\hsc_ptr hsc_x -> pokeByteOff hsc_ptr %ld (fromIntegral hsc_x :: ", \
+            (long) &(x->f)); \
+        hsc_intfieldtype (t, f) \
+        printf ("))"); \
+    }
