diff --git a/COPYING b/COPYING
new file mode 100644
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,34 @@
+Copyright (C) 2007 Bart Massey
+ALL RIGHTS RESERVED
+
+[This program is licensed under the "3-clause ('new') BSD License"]
+
+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/README b/README
new file mode 100644
--- /dev/null
+++ b/README
@@ -0,0 +1,20 @@
+truelevel -- audio file compresor / limiter
+version 0.1
+Bart Massey <bart@cs.pdx.edu>
+8 March 2008
+
+This program is a simple command-line compressor / limiter
+for WAVE format audio files.  There is precious little
+documentation at this point; that needs to be fixed soon.
+
+I have used this code with ghc 6.6 and ghc 6.8 on Linux,
+although the current cabal setup has only been tested with
+cabal 1.2 and ghc 6.8.  Other than the lack of
+documentation, it is a fairly standard Hackage-ready
+package, to the extent I know how to construct such.
+
+I'm not really working on this Haskell code any longer; I
+rewrote it in Java for a project I'm working on.  However, I
+think it's a nice demo app for my WAVE library.
+
+Have fun with it, and let me know if there are problems.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,7 @@
+--- Copyright (C) 2007 Bart Massey
+--- ALL RIGHTS RESERVED
+--- Please see the file COPYING in this software
+--- distribution for license information.
+
+import Distribution.Simple
+main = defaultMain
diff --git a/truelevel.cabal b/truelevel.cabal
new file mode 100644
--- /dev/null
+++ b/truelevel.cabal
@@ -0,0 +1,19 @@
+Name: truelevel
+Build-Type: Simple
+Description: An audio file compressor / limiter.  This
+             program will adjust the dynamic range of a WAVE file.
+Version: 0.1
+License: BSD3
+License-File: COPYING
+Copyright: Copyright (C) 2006-2008 Bart Massey
+Author: Bart Massey <bart@cs.pdx.edu>
+Maintainer: Bart Massey <bart@cs.pdx.edu>
+Homepage: http://wiki.cs.pdx.edu/bartforge/truelevel
+Stability: Alpha
+Synopsis: Audio file compressor-limiter
+Category: Audio
+Build-Depends: base, WAVE, parseargs
+Extra-Source-Files: README
+
+Executable: truelevel
+Main-Is: truelevel.hs
diff --git a/truelevel.hs b/truelevel.hs
new file mode 100644
--- /dev/null
+++ b/truelevel.hs
@@ -0,0 +1,160 @@
+--- Copyright (C) 2007 Bart Massey
+--- ALL RIGHTS RESERVED
+--- Please see the end of this file for license information.
+
+module Main
+where
+
+import Data.List
+import Data.Maybe
+import System.Environment
+import System.Console.ParseArgs
+import Data.WAVE
+import TrueLevelArgs
+
+-- Create overlapping tiles of n samples.  Each tile is a
+-- list of n samples together with a list of n + 2m samples
+-- (in general) representing the surrounding window.
+offset_tile :: Int -> Int -> [a] -> [([a], [a])]
+offset_tile n m s =
+    one_tile (0, n, m) s
+    where
+      one_tile (l, c, r) s =
+          let target = take c (drop l s)
+              (window, l', s') =
+                  if l < r
+                  then (take (l + c + r) s,  (l + c) `min` (r + c), s)
+                  else (take (r + c + r) (drop (l - r) s), l, drop c s)
+          in
+            if null target
+            then []
+            else (target, window) : one_tile (l', c, r) s'
+
+avg :: (Fractional a) => [a] -> a
+avg l = sum l / fromIntegral (length l)
+
+--- Estimate peak amplitude using rms power
+rms :: [Double] -> Double
+rms s = 2 * sqrt (2 * sum (map (\x -> x * x) s) / fromIntegral (length s))
+
+--- Estimate peak amplitude as twice highest absolute peak
+--- of low-pass
+--- XXX fixme: low-pass is moving average for now
+peak :: [Double] -> Double
+peak s = 2 * (maximum . map abs . map avg . tile 3 2) s
+
+make_window :: Int -> Int -> [Double]
+make_window n w =
+    let tri = [ fromIntegral i / fromIntegral (w + 1) |
+                i <- [ 1 .. w ] ]
+        flat = replicate (n - 2 * w) 1
+    in tri ++ flat ++ reverse tri
+
+vprod :: [Double] -> [Double] -> [Double]
+vprod l1 l2 = map (\(v1, v2) -> v1 * v2) (zip l1 l2)
+
+compress :: Double -> Double -> Double -> Double
+          -> (Double, [[Double]]) -> [[Double]]
+compress l h l' h' (s, a)  =
+    if s <= 0.0001 then
+        map (map (\v -> 0)) a
+    else if s >= h then
+        scale (h' / s)
+    else if s <= l then
+        scale (l' / s)
+    else let s' = l' + (h' - l') * (s - l) / (h - l) in
+        scale (s' / s)
+    where
+      scale v = map (map (v *)) a
+
+ac :: [Double] -> [Double]
+ac tick = map (\v -> v - avg tick) tick
+
+score_tick :: ([Double] -> [Double])
+           -> ([Double] -> Double)
+           -> [Double]
+           -> ([[Double]], [[Double]])
+           -> (Double, [[Double]])
+score_tick bias power window (target, surround) = 
+    let powerf = map (power . bias . (vprod window))
+        score = maximum (powerf (transpose surround)) in
+    (score, target)
+
+main :: IO ()
+main = do
+    args <- parseArgsIO ArgsComplete argd
+    --- get the input
+    h <- getArgStdio args OptionInputFile ReadMode
+    wav <- hGetWAVE h
+    let samples =  map (map sampleToDouble)
+                       (waveSamples wav)
+    --- group into ticks
+    let hd = waveHeader wav
+    let rate = waveFrameRate hd
+    let tick_ms = fromJust (getArgInt args OptionTickMS)
+    let tick_samples = rate * tick_ms `div` 1000
+    let window_pct =
+            fromJust (getArgDouble args OptionWindowPercent) / 100
+    let window_edge = floor ((window_pct / 2) * fromIntegral tick_samples)
+    let overlap = case getArgDouble args OptionWindowOverlap of
+                    Nothing -> 2 * window_edge
+                    Just p -> floor (p * fromIntegral tick_samples / 100)
+    let offset_ticks = offset_tile tick_samples overlap samples
+    --- score ticks
+    let window = make_window tick_samples overlap
+    let bias = if gotArg args OptionDCBias
+               then id
+               else ac
+    let power = if gotArg args OptionUseRMS
+                then rms
+                else peak
+    let scored_ticks = map (score_tick bias power window) offset_ticks
+    --- scale the input
+    let low_limit =
+            fromJust (getArgDouble args OptionLowLimit)  / 100
+    let high_limit =
+            fromJust (getArgDouble args OptionHighLimit)  / 100
+    let low_output =
+            fromJust (getArgDouble args OptionLowOutput) / 100
+    let high_output =
+            fromJust (getArgDouble args OptionHighOutput) / 100
+    let lev = compress low_limit high_limit low_output high_output
+    let scaled = concatMap lev scored_ticks
+    --- write the output
+    let samples' = map (map doubleToSample) scaled
+    let wav' = WAVE { waveHeader = hd,
+                      waveSamples = 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.
