diff --git a/WAVE.cabal b/WAVE.cabal
--- a/WAVE.cabal
+++ b/WAVE.cabal
@@ -1,7 +1,7 @@
 Name: WAVE
 Description: Module for reading and writing audio files in WAVE format.
 Build-Type: Simple
-Version: 0.1.2
+Version: 0.1.3
 License: BSD3
 License-File: COPYING
 Author: Bart Massey <bart@cs.pdx.edu>
@@ -11,18 +11,26 @@
 Category: Data, Sound
 Synopsis: WAVE audio file IO library
 Stability: Alpha
-Extra-Source-Files: README.md
-Cabal-Version: >=1.6
+Extra-Source-Files: README.md readtest.hs
+Cabal-Version: >=1.10
 
 Library
+  Default-Language: Haskell2010
   Exposed-Modules: Data.WAVE
-  Build-Depends: base >=4&&<5, bytestring>=0.10&&<1
+  Build-Depends: base >=3&&<5, bytestring>=0.10&&<1
 
-Source-repository head
+Executable sinewave
+  Default-Language: Haskell2010
+  Main-is: sinewave.hs
+  Build-depends: base >=3&&<5, containers >=0.3&&<1,
+                 bytestring>=0.10&&<1,
+                 WAVE >=0.1&&<1, parseargs >=0.1&&<1
+
+Source-Repository head
   Type: git
   Location: https://github.com/BartMassey/WAVE.git
 
-Source-repository this
+Source-Repository this
   Type: git
   Location: https://github.com/BartMassey/WAVE.git
-  Tag: v0.1.2
+  Tag: v0.1.3
diff --git a/readtest.hs b/readtest.hs
new file mode 100644
--- /dev/null
+++ b/readtest.hs
@@ -0,0 +1,60 @@
+--- Copyright (C) 2007 Bart Massey
+--- ALL RIGHTS RESERVED
+--- Please see the end of this file for license information.
+
+import System.Environment
+import ParseArgs
+import WAVE
+
+argd :: [ Arg Int ]
+argd = [ Arg { argIndex = 0,
+               argName = Nothing,
+               argAbbr = Nothing,
+               argData = argDataOptional "input" ArgtypeString,
+               argDesc = "Input file" }]
+
+main = do
+  args <- parseArgsIO ArgsComplete argd
+  h <- getArgStdio args 0 ReadMode
+  wav <- hGetWAVE h
+  let samples = waveSamples wav
+  case (waveFrames (waveHeader wav)) of
+    Just n -> let n' = length samples in
+              if (n /= n') then do
+                    putStrLn "WAVE size mismatch"
+                    putStrLn ("Header: " ++ (show n))
+                    putStrLn ("Samples: " ++ (show n'))
+              else do
+                  mapM_ (putStrLn . show) samples
+
+
+
+--- 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.
+---     * Neither the name of Bart Massey, nor the names
+---       of other affiliated organizations, nor the names
+---       of other contributors may 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/sinewave.hs b/sinewave.hs
new file mode 100644
--- /dev/null
+++ b/sinewave.hs
@@ -0,0 +1,106 @@
+--- Copyright (C) 2007 Bart Massey
+--- ALL RIGHTS RESERVED
+--- Please see the end of this file for license information.
+
+import System.Environment
+import Data.Maybe
+import Data.List
+import System.Console.ParseArgs
+import Data.WAVE
+
+data Options =
+    OptionHz |
+    OptionAmplitude |
+    OptionFrameRate |
+    OptionBits |
+    OptionDuration |
+    OptionOutputFile
+    deriving (Ord, Eq, Show)
+
+argd :: [ Arg Options ]
+argd = [ Arg { argIndex = OptionHz,
+               argName = Just "frequency",
+               argAbbr = Just 'f',
+               argData = argDataDefaulted "hz" ArgtypeDouble 440,
+               argDesc = "Output frequency (default 440)" },
+         Arg { argIndex = OptionAmplitude,
+               argName = Just "amplitude",
+               argAbbr = Just 'a',
+               argData = argDataDefaulted "fraction" ArgtypeDouble 0.5,
+               argDesc = "Output amplitude (0.0..1.0, default 0.5)" },
+         Arg { argIndex = OptionFrameRate,
+               argName = Just "rate",
+               argAbbr = Just 'r',
+               argData = argDataDefaulted "fps" ArgtypeInt 44100,
+               argDesc = "Output frame rate (default 44100)" },
+         Arg { argIndex = OptionBits,
+               argName = Just "bits",
+               argAbbr = Just 'b',
+               argData = argDataDefaulted "nbits" ArgtypeInt 16,
+               argDesc = "Output bit width (default 16)" },
+         Arg { argIndex = OptionDuration,
+               argName = Just "time",
+               argAbbr = Just 't',
+               argData = argDataDefaulted "secs" ArgtypeDouble 1.0,
+               argDesc = "Output duration  (default 1.0)" },
+         Arg { argIndex = OptionOutputFile,
+               argName = Nothing,
+               argAbbr = Nothing,
+               argData = argDataOptional "output" ArgtypeString,
+               argDesc = "Output file" }]
+
+gen_sin :: Double -> Double -> Double -> Int -> [Double]
+gen_sin a hz secs frame_rate =
+    let sr = fromIntegral frame_rate in
+    [ a * sin (2 * pi * hz * fromIntegral t / sr) |
+      t <- [0 .. floor (sr * secs)] ]
+
+main = do
+  args <- parseArgsIO ArgsComplete argd
+  let hz = fromJust (getArgDouble args OptionHz)
+  let frame_rate = fromJust (getArgInt args OptionFrameRate)
+  let bits_per_sample = fromJust (getArgInt args OptionBits)
+  let seconds = fromJust (getArgDouble args OptionDuration)
+  let frames = floor (fromIntegral frame_rate * seconds)
+  let amplitude = fromJust (getArgDouble args OptionAmplitude)
+  let sin_samples = map doubleToSample
+                        (gen_sin amplitude hz seconds frame_rate)
+  let wav = WAVE {
+    waveHeader = WAVEHeader {
+      waveNumChannels = 1,
+      waveFrameRate = frame_rate,
+      waveBitsPerSample = bits_per_sample,
+      waveFrames = Just frames },
+    waveSamples = transpose [sin_samples] }
+  h <- getArgStdio args OptionOutputFile WriteMode
+  hPutWAVE h wav
+
+--- 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.
+---     * Neither the name of Bart Massey, nor the names
+---       of other affiliated organizations, nor the names
+---       of other contributors may 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.
