diff --git a/sox.cabal b/sox.cabal
--- a/sox.cabal
+++ b/sox.cabal
@@ -1,11 +1,11 @@
+Cabal-Version:    2.2
 Name:             sox
-Version:          0.0.1
-License:          GPL
+Version:          0.2.3.2
+License:          GPL-3.0-only
 License-File:     LICENSE
 Author:           Henning Thielemann <haskell@henning-thielemann.de>
 Maintainer:       Henning Thielemann <haskell@henning-thielemann.de>
 Homepage:         http://www.haskell.org/haskellwiki/Sox
-Package-URL:      http://code.haskell.org/~thielema/sox/
 Category:         Sound
 Synopsis:         Play, write, read, convert audio signals using Sox
 Description:
@@ -19,44 +19,44 @@
    and in the path to the executables must be set.
    .
    In the past this was part of the synthesizer package.
-   .
-   There is some ancient code,
-   which allows interactive playback using the shell-pipe package on GHC-6.2,
-   where no runInteractiveProcess was available.
-Tested-With:       GHC==6.8.2
-Cabal-Version:     >=1.2
+Tested-With:       GHC==6.8.2, GHC==6.10.4, GHC==6.12.1
+Tested-With:       GHC==7.0.4, GHC==7.2.1, GHC==7.4.2, GHC==7.6.1
 Build-Type:        Simple
 Extra-Source-Files:
   Makefile
   unix/src/Sound/Sox/System.hs
   windows/src/Sound/Sox/System.hs
 
-Flag splitBase
-  description: Choose the new smaller, split-up base package.
+Source-Repository this
+  Tag:         0.2.3.2
+  Type:        darcs
+  Location:    https://hub.darcs.net/thielema/sox/
 
-Flag executeShell
-  description: use ShellPipe package instead of process, preserved for historical reasons
-  default:     False
+Source-Repository head
+  Type:        darcs
+  Location:    https://hub.darcs.net/thielema/sox/
 
 Library
   Build-Depends:
-    explicit-exception >= 0.1.3 && < 0.2,
-    transformers >= 0.0.1 && <0.2
-  If flag(splitBase)
-    Build-Depends:
-      base >= 2 && <4,
-      process >= 1.0 && < 1.1,
-      containers >= 0.1 && <0.3
-  Else
-    Build-Depends: base >= 1.0 && < 2
+    sample-frame >= 0.0.1 && <0.1,
+    explicit-exception >= 0.1.3 && < 0.3,
+    -- 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.7,
+    semigroups >=0.1 && <1.0,
+    utility-ht >=0.0.5 && <0.1,
+    process >=1.0 && <1.7,
+    containers >=0.1 && <0.7,
+    base >=2 && <5
 
   Hs-Source-Dirs: src
   If os(windows)
     Hs-Source-Dirs: windows/src
   Else
     Hs-Source-Dirs: unix/src
-    Build-Depends: unix >=2.3 && <2.4
+    Build-Depends: unix >=2.3 && <2.9
 
+  Default-Language: Haskell98
   GHC-Options: -Wall
 
   Exposed-Modules:
@@ -69,10 +69,10 @@
     Sound.Sox.Frame
     Sound.Sox.Frame.Stereo
     Sound.Sox.Signal.List
+    Sound.Sox.Information
   Other-Modules:
     Sound.Sox.Private.Option
     Sound.Sox.Private.Arguments
     Sound.Sox.Private.Format
---    Sound.Sox.Private.Information
-    Sound.Sox.StorableUtility
+    Sound.Sox.Private.Information
     Sound.Sox.System
diff --git a/src/Sound/Sox/Convert.hs b/src/Sound/Sox/Convert.hs
--- a/src/Sound/Sox/Convert.hs
+++ b/src/Sound/Sox/Convert.hs
@@ -4,7 +4,7 @@
 import qualified Sound.Sox.Private.Arguments as Args
 import Data.Monoid (mconcat, )
 
-import qualified System.Cmd as Cmd
+import qualified System.Process as Cmd
 -- import qualified System.IO as IO
 import System.Exit (ExitCode, )
 
@@ -13,7 +13,7 @@
 {- |
 > :load Sound.Sox.Convert
 >
-> generic Option.none "test.aiff" Option.none "test.wav"
+> simple Option.none "test.aiff" Option.none "test.wav"
 -}
 simple ::
    Option.T {- ^ source options -} ->
diff --git a/src/Sound/Sox/Format.hs b/src/Sound/Sox/Format.hs
--- a/src/Sound/Sox/Format.hs
+++ b/src/Sound/Sox/Format.hs
@@ -24,15 +24,22 @@
 iff8svx :: T
 iff8svx = custom "8svx"
 
+muLaw :: T
+muLaw = custom "ul"
 
+
 signedByte, unsignedByte :: T
-signedByte   = custom "sb"
-unsignedByte = custom "ub"
+signedByte   = custom "s8"
+unsignedByte = custom "u8"
 
 signedWord, unsignedWord :: T
-signedWord   = custom "sw"
-unsignedWord = custom "uw"
+signedWord   = custom "s16"
+unsignedWord = custom "u16"
 
-signedLong {-, unsignedLong-} :: T
-signedLong   = custom "sl"
--- unsignedLong = custom "ul"
+signedLong, unsignedLong :: T
+signedLong   = custom "s32"
+unsignedLong = custom "u32"
+
+ieeeSinglePrecision, ieeeDoublePrecision :: T
+ieeeSinglePrecision = custom "f32"
+ieeeDoublePrecision = custom "f64"
diff --git a/src/Sound/Sox/Frame.hs b/src/Sound/Sox/Frame.hs
--- a/src/Sound/Sox/Frame.hs
+++ b/src/Sound/Sox/Frame.hs
@@ -1,42 +1,58 @@
-module Sound.Sox.Frame where
+module Sound.Sox.Frame (C(..), Frame.withSignal, Frame.numberOfChannels, ) where
 
+import qualified Sound.Frame as Frame
+import qualified Sound.Frame.Stereo as Stereo
+import qualified Sound.Frame.MuLaw  as MuLaw
+
 import qualified Sound.Sox.Format as Format
 
-import Data.Word (Word8, Word16, )
+import Data.Word (Word8, Word16, Word32, )
 import Data.Int (Int8, Int16, Int32, )
 
 
-class C y where
-   {- | The argument is not touched and can be undefined -}
-   numberOfChannels :: y -> Int
+class Frame.C y => C y where
    format :: y -> Format.T
 
+
 instance C Word8 where
-   numberOfChannels _ = 1
    format _ = Format.unsignedByte
 
 instance C Int8 where
-   numberOfChannels _ = 1
    format _ = Format.signedByte
 
 instance C Word16 where
-   numberOfChannels _ = 1
    format _ = Format.unsignedWord
 
 instance C Int16 where
-   numberOfChannels _ = 1
    format _ = Format.signedWord
 
-{-
 instance C Word32 where
-   numberOfChannels _ = 1
    format _ = Format.unsignedLong
--}
 
 instance C Int32 where
-   numberOfChannels _ = 1
    format _ = Format.signedLong
 
+{- |
+The floating point instances are dangerous,
+because Storable Float may not use IEEE format
+that sox uses according to its man page.
+This is strange since sox uses the host's endianess for multi-byte values.
+So, why does it not use the machine's floating point format?
+-}
+instance C Float where
+   format _ = Format.ieeeSinglePrecision
 
-withSignal :: (y -> a) -> (sig y -> a)
-withSignal f _ = f undefined
+instance C Double where
+   format _ = Format.ieeeDoublePrecision
+
+instance C MuLaw.T where
+   format _ = Format.muLaw
+
+{-
+Shall we add instances for Float and Double?
+Sox requires floating point numbers in IEEE formats,
+but we cannot warrant that the Storable instances uses those formats.
+-}
+
+instance C a => C (Stereo.T a) where
+   format y = format (Stereo.left y)
diff --git a/src/Sound/Sox/Frame/Stereo.hs b/src/Sound/Sox/Frame/Stereo.hs
--- a/src/Sound/Sox/Frame/Stereo.hs
+++ b/src/Sound/Sox/Frame/Stereo.hs
@@ -1,42 +1,6 @@
-{-
-This data type can be used as sample type for stereo signals.
+{- |
+We just re-export the type from sample-frame:Sound.Frame.Stereo.
 -}
 module Sound.Sox.Frame.Stereo (T, left, right, cons, ) where
 
-import qualified Sound.Sox.Frame as Frame
-
-import Control.Monad (liftM2, )
-
-import Foreign.Storable (Storable (..), )
-import qualified Sound.Sox.StorableUtility as Store
-
-
-data T a = Cons {left, right :: !a}
-
-
-{-# INLINE cons #-}
-cons :: a -> a -> T a
-cons = Cons
-
-instance Functor T where
-   {-# INLINE fmap #-}
-   fmap f (Cons l r) = Cons (f l) (f r)
-
-
--- cf. StorableInstances
-instance (Storable a) => Storable (T a) where
-   sizeOf y = Store.sizeOfArray 2 $ left y
-   alignment y = alignment $ left y
-   peek ptr =
-      Store.run ptr $
-         liftM2 Cons
-            Store.peekNext
-            Store.peekNext
-   poke ptr (Cons l r) =
-      Store.run ptr $
-         do Store.pokeNext l
-            Store.pokeNext r
-
-instance Frame.C a => Frame.C (T a) where
-   numberOfChannels y = 2 * Frame.numberOfChannels (left y)
-   format y = Frame.format (left y)
+import Sound.Frame.Stereo
diff --git a/src/Sound/Sox/Information.hs b/src/Sound/Sox/Information.hs
new file mode 100644
--- /dev/null
+++ b/src/Sound/Sox/Information.hs
@@ -0,0 +1,97 @@
+{- |
+This module calls the 'soxi' command
+which is available since 'sox' version 14.
+
+We have to call 'soxi' for every option.
+However we hide this in our interface,
+such that we could do more efficiently,
+if 'soxi' supports multiple outputs in future.
+-}
+module Sound.Sox.Information (
+   T(Cons),
+   simple,
+   format,
+   sampleRate,
+   numberOfChannels,
+   length,
+   bitsPerSample,
+   get,
+   exampleMulti,
+   exampleSingle,
+   ) where
+
+import qualified Control.Monad.Trans.Reader as MR
+import Control.Applicative (Applicative, pure, liftA3, (<*>), )
+import Data.String.HT (trim, )
+import Text.Read.HT (maybeRead, )
+
+import qualified System.Process as Proc
+import qualified System.IO as IO
+import Control.Exception (bracket, )
+-- import System.IO.Error (ioError, userError, )
+-- import System.Exit (ExitCode, )
+
+import Prelude hiding (length, )
+
+
+newtype T a =
+   Cons (MR.ReaderT FilePath IO a)
+
+instance Functor T where
+   {-# INLINE fmap #-}
+   fmap f (Cons m) = Cons (fmap f m)
+
+instance Applicative T where
+   {-# INLINE pure #-}
+   {-# INLINE (<*>) #-}
+   pure = Cons . pure
+   (Cons f) <*> (Cons x) = Cons (f <*> x)
+
+
+simple :: Read a => (String -> Maybe a) -> String -> T a
+simple rd option =
+   Cons $ MR.ReaderT $ \fileName ->
+      bracket
+         (Proc.runInteractiveProcess "soxi"
+             (option : fileName : [])
+             Nothing Nothing)
+         (\(input,output,err,proc) ->
+             mapM_ IO.hClose [input, output, err] >>
+             Proc.terminateProcess proc)
+         (\(_,output,_,_) ->
+            maybe
+               (ioError (userError "soxi returned rubbish"))
+               return .
+            rd =<<
+            IO.hGetContents output)
+
+format :: T String
+format = simple Just "-t"
+
+simpleRead :: String -> T Int
+simpleRead = simple (maybeRead . trim)
+
+sampleRate :: T Int
+sampleRate = simpleRead "-r"
+
+numberOfChannels :: T Int
+numberOfChannels = simpleRead "-c"
+
+length :: T Int
+length = simpleRead "-s"
+
+bitsPerSample :: T Int
+bitsPerSample = simpleRead "-b"
+
+
+
+get :: T a -> FilePath -> IO a
+get (Cons act) = MR.runReaderT act
+
+exampleMulti :: IO (String, Int, Int)
+exampleMulti =
+   get (liftA3 (,,) format sampleRate bitsPerSample) "test.aiff"
+
+exampleSingle :: IO Int
+exampleSingle =
+   get sampleRate "test.aiff"
diff --git a/src/Sound/Sox/Option/Format.hs b/src/Sound/Sox/Option/Format.hs
--- a/src/Sound/Sox/Option/Format.hs
+++ b/src/Sound/Sox/Option/Format.hs
@@ -26,6 +26,10 @@
 sampleRate r =
    single "-r" [show r]
 
+bitsPerSample :: Int -> T
+bitsPerSample b =
+   single "-b" [show b]
+
 format :: Format.T -> T
 format fmt =
    single "-t" [Format.decons fmt]
diff --git a/src/Sound/Sox/Play.hs b/src/Sound/Sox/Play.hs
--- a/src/Sound/Sox/Play.hs
+++ b/src/Sound/Sox/Play.hs
@@ -18,7 +18,7 @@
 {- |
 > :load Sound.Sox.Play Sound.Sox.Signal.List
 >
-> generic Sound.Sox.Signal.List.put Option.none 11025 (iterate (1000+) (0::Data.Int.Int16))
+> simple Sound.Sox.Signal.List.put Option.none 11025 (iterate (1000+) (0::Data.Int.Int16))
 -}
 simple ::
    (Frame.C y) =>
@@ -50,7 +50,12 @@
    IO ExitCode
 extended write srcOpts dstOpts sampleRate stream =
    bracket
-      (Proc.runInteractiveProcess "play"
+      {-
+      Formerly we called 'play' here.
+      On Windows the SoX package does not install a 'play' command.
+      However using the '-d' argument for the destination always works.
+      -}
+      (Proc.runInteractiveProcess "sox"
           (Args.decons $ mconcat $
            OptPriv.toArguments
              (mconcat $
@@ -62,6 +67,7 @@
               []) :
            Args.pipe :
            OptPriv.toArguments dstOpts :
+           Args.single "-d" :
            [])
           Nothing Nothing)
       (\(input,output,err,_proc) ->
diff --git a/src/Sound/Sox/Private/Arguments.hs b/src/Sound/Sox/Private/Arguments.hs
--- a/src/Sound/Sox/Private/Arguments.hs
+++ b/src/Sound/Sox/Private/Arguments.hs
@@ -1,12 +1,16 @@
 module Sound.Sox.Private.Arguments where
 
-import Data.Monoid (Monoid(..), )
+import Data.Monoid (Monoid(mempty, mappend), )
+import Data.Semigroup (Semigroup((<>)), )
 
 newtype T = Cons {decons :: [String]}
 
+instance Semigroup T where
+   Cons x <> Cons y = Cons (x++y)
+
 instance Monoid T where
    mempty = Cons mempty
-   mappend (Cons x) (Cons y) = Cons (mappend x y)
+   mappend = (<>)
 
 single :: String -> T
 single x = Cons [x]
diff --git a/src/Sound/Sox/Private/Information.hs b/src/Sound/Sox/Private/Information.hs
new file mode 100644
--- /dev/null
+++ b/src/Sound/Sox/Private/Information.hs
@@ -0,0 +1,99 @@
+{- |
+This module calls the 'soxi' command
+which is available since 'sox' version 14.
+
+This module is very clever as it calls 'soxi' once
+with one option per requested piece of information
+and parses the results in a correctly typed tuple.
+Unfortunately, 'soxi' does not work this way.
+It accepts only one option per call.
+-}
+module Sound.Sox.Private.Information where
+
+import Control.Monad.Trans.Writer (Writer, writer, runWriter, )
+import Control.Monad.Trans.State (StateT(StateT), runStateT, )
+import Control.Monad.Trans.Class (lift, )
+import Control.Applicative (Applicative, pure, liftA3, (<*>), )
+import Data.Functor.Compose (Compose(Compose), )
+import Data.List.HT (viewL, )
+import Data.String.HT (trim, )
+import Text.Read.HT (maybeRead, )
+
+import qualified System.Process as Proc
+import qualified System.IO as IO
+import Control.Exception (bracket, )
+-- import System.IO.Error (ioError, userError, )
+-- import System.Exit (ExitCode, )
+
+import Prelude hiding (length, )
+
+
+{-
+Cf. Synthesizer.Basic.Interpolation.PrefixReader.
+-}
+newtype T a =
+   Cons (Compose (Writer [String]) (StateT [String] Maybe) a)
+
+instance Functor T where
+   {-# INLINE fmap #-}
+   fmap f (Cons m) = Cons (fmap f m)
+
+instance Applicative T where
+   {-# INLINE pure #-}
+   {-# INLINE (<*>) #-}
+   pure = Cons . pure
+   (Cons f) <*> (Cons x) = Cons (f <*> x)
+
+
+simple :: Read a => (String -> Maybe a) -> String -> T a
+simple rd option =
+   Cons $ Compose $ writer
+      (lift . rd =<< StateT viewL, [option])
+
+format :: T String
+format = simple Just "-t"
+
+simpleRead :: String -> T Int
+simpleRead = simple (maybeRead . trim)
+
+sampleRate :: T Int
+sampleRate = simpleRead "-r"
+
+numberOfChannels :: T Int
+numberOfChannels = simpleRead "-c"
+
+length :: T Int
+length = simpleRead "-s"
+
+bitsPerSample :: T Int
+bitsPerSample = simpleRead "-b"
+
+
+
+get :: T a -> FilePath -> IO a
+get (Cons (Compose w)) fileName =
+   let (parser, opts) = runWriter w
+   in  bracket
+          (Proc.runInteractiveProcess "soxi"
+              (opts ++ fileName : [])
+              Nothing Nothing)
+          (\(input,output,err,proc) ->
+              mapM_ IO.hClose [input, output, err] >>
+              Proc.terminateProcess proc)
+          (\(_,output,_,_) ->
+             maybe
+                (ioError (userError "soxi returned rubbish"))
+                (\(x,str) ->
+                    if null str
+                      then return x
+                      else ioError (userError "soxi returned more lines than expected")) .
+             runStateT parser . lines =<<
+             IO.hGetContents output)
+
+exampleMulti :: IO (String, Int, Int)
+exampleMulti =
+   get (liftA3 (,,) format sampleRate bitsPerSample) "test.aiff"
+
+exampleSingle :: IO Int
+exampleSingle =
+   get sampleRate "test.aiff"
diff --git a/src/Sound/Sox/Private/Option.hs b/src/Sound/Sox/Private/Option.hs
--- a/src/Sound/Sox/Private/Option.hs
+++ b/src/Sound/Sox/Private/Option.hs
@@ -2,7 +2,8 @@
 
 import qualified Sound.Sox.Private.Arguments as Args
 import qualified Data.Map as Map
-import Data.Monoid (Monoid(..), )
+import Data.Monoid (Monoid(mappend, mempty), )
+import Data.Semigroup (Semigroup((<>)), )
 
 {- |
 You can combine options using the 'Monoid' functions 'mappend' and 'mconcat'.
@@ -11,9 +12,12 @@
 -}
 newtype T = Cons {decons :: Map.Map String [String]}
 
+instance Semigroup T where
+   Cons x <> Cons y = Cons (Map.union x y)
+
 instance Monoid T where
    mempty = Cons mempty
-   mappend (Cons x) (Cons y) = Cons (Map.union x y)
+   mappend = (<>)
 
 toArguments :: T -> Args.T
 toArguments =
diff --git a/src/Sound/Sox/Read.hs b/src/Sound/Sox/Read.hs
--- a/src/Sound/Sox/Read.hs
+++ b/src/Sound/Sox/Read.hs
@@ -44,8 +44,8 @@
 However there is @soxi@ for this purpose, which we may support in future.
 
 > :load Sound.Sox.Read Sound.Sox.Signal.List
->
-> open Option.none "test.aiff" >>= withHandle2 Sound.Sox.Signal.List.getContents >>= (\x -> print (Control.Monad.Exception.Asynchronous.result x :: [Data.Int.Int16]))
+> :module + Control.Exception
+> bracket (open Option.none "test.aiff") close $ \h -> withHandle2 Sound.Sox.Signal.List.getContents h >>= \x -> print (Control.Monad.Exception.Asynchronous.result x :: [Data.Int.Int16])
 -}
 open ::
    (Frame.C y) =>
diff --git a/src/Sound/Sox/Signal/List.hs b/src/Sound/Sox/Signal/List.hs
--- a/src/Sound/Sox/Signal/List.hs
+++ b/src/Sound/Sox/Signal/List.hs
@@ -17,13 +17,13 @@
 import qualified Control.Monad.Exception.Synchronous as Sync
 import qualified Control.Monad.Exception.Asynchronous as Async
 
-import Control.Monad.Trans (lift, )
+import Control.Monad.Trans.Class (lift, )
 
 import Foreign.Storable (Storable (..), )
 
-import Foreign (Ptr, alloca, sizeOf, poke, peek, )
+import Foreign (Ptr, alloca, )
 import System.IO (withBinaryFile, IOMode(WriteMode,ReadMode), Handle, hPutBuf, hGetBuf, )
-import Control.Exception (Exception, try, )
+import Control.Exception.Extensible (SomeException, try, )
 import Control.Monad (liftM)
 
 import System.IO.Unsafe (unsafeInterleaveIO, )
@@ -51,7 +51,7 @@
   deriving (Show, Eq, Enum)
 
 type IOReadException =
-   Either ReadException Exception
+   Either ReadException SomeException
 
 withReadFile :: Storable a =>
    FilePath ->
@@ -66,6 +66,7 @@
 getContents h =
    alloca $
       \p ->
+--         Async.eatNothingT $
          liftM (\(Async.Exceptional (Just e) a) -> Async.Exceptional e a) $
          Async.manySynchronousT
             unsafeInterleaveIO
diff --git a/src/Sound/Sox/StorableUtility.hs b/src/Sound/Sox/StorableUtility.hs
deleted file mode 100644
--- a/src/Sound/Sox/StorableUtility.hs
+++ /dev/null
@@ -1,43 +0,0 @@
-{- |
-support for Storable instances
-
-Cf. synthesizer package
--}
-module Sound.Sox.StorableUtility where
-
-import Control.Monad.Trans.State (StateT, evalStateT, get, put, )
-import Control.Monad.Trans (lift, )
-
-import Foreign.Ptr (Ptr, castPtr, )
-import Foreign.Storable (Storable(..))
-import Foreign.Marshal.Array (advancePtr, )
-
-
-{-# INLINE roundUp #-}
-roundUp :: Int -> Int -> Int
-roundUp m x = x + mod (-x) m
-
-{-# INLINE sizeOfArray #-}
-sizeOfArray :: Storable a => Int -> a -> Int
-sizeOfArray n x =
-   n * roundUp (alignment x) (sizeOf x)
-
-{-# INLINE pokeNext #-}
-pokeNext :: (Storable a) => a -> StateT (Ptr a) IO ()
-pokeNext x =
-   do ptr <- get
-      lift $ poke ptr x
-      put (ptr `advancePtr` 1)
---      put (ptr `plusPtr` size x + div (- size x) (alignment x))
-
-{-# INLINE peekNext #-}
-peekNext :: (Storable a) => StateT (Ptr a) IO a
-peekNext =
-   do ptr <- get
-      a <- lift $ peek ptr
-      put (ptr `advancePtr` 1)
-      return a
-
-run :: Ptr (t a) -> StateT (Ptr a) IO c -> IO c
-run ptr act =
-   evalStateT act (castPtr ptr)
diff --git a/src/Sound/Sox/Write.hs b/src/Sound/Sox/Write.hs
--- a/src/Sound/Sox/Write.hs
+++ b/src/Sound/Sox/Write.hs
@@ -1,4 +1,4 @@
-module Sound.Sox.Write where
+module Sound.Sox.Write (simple, extended, manyExtended, ) where
 
 import qualified Sound.Sox.Frame as Frame
 import Sound.Sox.System (catchCtrlC, )
@@ -8,6 +8,9 @@
 import qualified Sound.Sox.Private.Arguments as Args
 import Data.Monoid (mconcat, )
 
+import qualified Data.Traversable as Trav
+import qualified Data.Foldable as Fold
+
 import qualified System.Process as Proc
 import qualified System.IO as IO
 import Control.Exception (bracket, )
@@ -21,7 +24,7 @@
 
 > :load Sound.Sox.Write Sound.Sox.Signal.List
 >
-> generic Sound.Sox.Signal.List.put Option.none "test.aiff" 11025 (take 100 $ iterate (1000+) (0::Data.Int.Int16))
+> simple Sound.Sox.Signal.List.put Option.none "test.aiff" 11025 (take 100 $ iterate (1000+) (0::Data.Int.Int16))
 -}
 simple ::
    (Frame.C y) =>
@@ -53,28 +56,81 @@
       {- ^ sample rate -} ->
    sig y ->
    IO ExitCode
-extended write srcOpts dstOpts fileName sampleRate stream =
+extended write srcOpts dstOpts fileName sampleRate signal =
    bracket
-      (Proc.runInteractiveProcess "sox"
-          (Args.decons $ mconcat $
-           OptPriv.toArguments
-             (mconcat $
-              srcOpts :
-              Option.numberOfChannels
-                 (Frame.withSignal Frame.numberOfChannels stream) :
-              Option.sampleRate sampleRate :
-              Option.format (Frame.withSignal Frame.format stream) :
-              []) :
-           Args.pipe :
-           OptPriv.toArguments dstOpts :
-           Args.fileName fileName :
-           [])
-          Nothing Nothing)
-      (\(input,output,err,_proc) ->
-          mapM_ IO.hClose [input, output, err])
+      (open srcOpts dstOpts fileName sampleRate signal)
+      close
       (\(input,_,_,proc) ->
          catchCtrlC >>
-         write input stream >>
+         write input signal >>
          return proc)
    -- get exit code, e.g. when options were wrong
    >>= Proc.waitForProcess
+
+{- |
+The traversable functor @f@ might be 'Maybe' or '[]'.
+It allows you to write to many files simultaneously
+and returns the exit codes of all writing processes.
+-}
+manyExtended ::
+   (Frame.C y, Trav.Traversable f) =>
+   (f IO.Handle -> sig y -> IO ())
+      {- ^ Writer routine -
+           e.g. 'Sound.Sox.Signal.List.put'
+           or 'Data.StorableVector.hPut' -} ->
+   Option.T
+      {- ^ source options, usually none -} ->
+   Option.T
+      {- ^ target options -} ->
+   f FilePath ->
+   Int
+      {- ^ sample rate -} ->
+   sig y ->
+   IO (f ExitCode)
+manyExtended write srcOpts dstOpts fileNames sampleRate signal =
+   bracket
+      (Trav.traverse
+         (\fileName -> open srcOpts dstOpts fileName sampleRate signal)
+         fileNames)
+      (Fold.traverse_ close)
+      (\handles ->
+         catchCtrlC >>
+         write (fmap (\(input,_,_,_) -> input) handles) signal >>
+         return (fmap (\(_,_,_,proc) -> proc) handles))
+   -- get exit code, e.g. when options were wrong
+   >>= Trav.traverse Proc.waitForProcess
+
+
+type Handle = (IO.Handle, IO.Handle, IO.Handle, Proc.ProcessHandle)
+
+open ::
+   (Frame.C y) =>
+   Option.T
+      {- ^ source options, usually none -} ->
+   Option.T
+      {- ^ target options -} ->
+   FilePath ->
+   Int
+      {- ^ sample rate -} ->
+   sig y ->
+   IO Handle
+open srcOpts dstOpts fileName sampleRate signal =
+   Proc.runInteractiveProcess "sox"
+      (Args.decons $ mconcat $
+       OptPriv.toArguments
+         (mconcat $
+          srcOpts :
+          Option.numberOfChannels
+             (Frame.withSignal Frame.numberOfChannels signal) :
+          Option.sampleRate sampleRate :
+          Option.format (Frame.withSignal Frame.format signal) :
+          []) :
+       Args.pipe :
+       OptPriv.toArguments dstOpts :
+       Args.fileName fileName :
+       [])
+      Nothing Nothing
+
+close :: Handle -> IO ()
+close (input,output,err,_proc) =
+   mapM_ IO.hClose [input, output, err]
