diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Lorenzo Tabacchini
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/pitchtrack.cabal b/pitchtrack.cabal
new file mode 100644
--- /dev/null
+++ b/pitchtrack.cabal
@@ -0,0 +1,75 @@
+name: pitchtrack
+version: 0.1.0.0
+cabal-version: >=1.10
+build-type: Simple
+license: MIT
+license-file: LICENSE
+copyright: (c) 2015 Lorenzo Tabacchini
+maintainer: lortabac@gmx.com
+bug-reports: https://github.com/lortabac/pitchtrack/issues
+synopsis: Pitch tracking library
+description:
+    Pitch tracking library, based on <http://www.schmittmachine.com/dywapitchtrack.html dywapitchtrack>.
+    .
+    See the "PitchTrack" module for an easy, "ready to use" interface,
+    or "PitchTrack.Pipes" for Pipes-based components, if you need more flexibility.
+    .
+    Note that because all parameters are hard-coded into the C library,
+    you are limited to the following audio configuration:
+    .
+    * raw (headerless) format
+    .
+    * a sampling rate of 44100Hz,
+    .
+    * a sample size of @sizeof(double)@
+    .
+    * floating-point encoding
+    .
+    * one channel (mono)
+category: Sound
+author: Lorenzo Tabacchini
+tested-with: GHC >=7.10
+extra-source-files:
+    test/files/a4-5sec.raw
+
+source-repository head
+    type: git
+    location: https://github.com/lortabac/pitchtrack.git
+
+library
+    exposed-modules:
+        PitchTrack
+        PitchTrack.Track
+        PitchTrack.Pipes
+    build-depends:
+        base ==4.8.*,
+        bytestring >=0.10.6.0 && <0.11,
+        dywapitchtrack >=0.1.0.0 && <0.2,
+        pipes >=4.1.6 && <4.2,
+        pipes-bytestring >=2.1.1 && <2.2,
+        process >=1.2.3.0 && <1.3,
+        transformers >=0.4.2.0 && <0.5
+    default-language: Haskell2010
+    hs-source-dirs: src
+    ghc-options: -Wall
+
+test-suite spec
+    type: exitcode-stdio-1.0
+    main-is: Spec.hs
+    build-depends:
+        base ==4.8.*,
+        bytestring >=0.10.6.0 && <0.11,
+        dywapitchtrack >=0.1.0.0 && <0.2,
+        hspec >=2.2.0 && <2.3,
+        pipes >=4.1.6 && <4.2,
+        pipes-bytestring >=2.1.1 && <2.2,
+        process >=1.2.3.0 && <1.3,
+        transformers >=0.4.2.0 && <0.5
+    default-language: Haskell2010
+    hs-source-dirs: test src
+    other-modules:
+        PitchTrack
+        PitchTrack.Track
+        PitchTrack.Pipes
+        PitchTrack.TrackSpec
+    ghc-options: -Wall
diff --git a/src/PitchTrack.hs b/src/PitchTrack.hs
new file mode 100644
--- /dev/null
+++ b/src/PitchTrack.hs
@@ -0,0 +1,9 @@
+module PitchTrack (
+-- * Re-exported modules
+    module PitchTrack.Track
+-- * Utilities
+  , neededSampleNum
+  ) where
+
+import           DywaPitchTrack   (neededSampleNum)
+import           PitchTrack.Track
diff --git a/src/PitchTrack/Pipes.hs b/src/PitchTrack/Pipes.hs
new file mode 100644
--- /dev/null
+++ b/src/PitchTrack/Pipes.hs
@@ -0,0 +1,41 @@
+module PitchTrack.Pipes (
+    samplesFromHandle
+  , samplesFromLBS
+  , forPitch
+  , forPitch_
+  , getPitch
+  , printPitch
+  ) where
+
+import           DywaPitchTrack
+
+import           Data.ByteString      (ByteString)
+import qualified Data.ByteString.Lazy as LBS
+import           Pipes
+import qualified Pipes.ByteString     as PB
+import qualified Pipes.Prelude        as P
+import           System.IO
+
+-- | Stream chunks of a fixed number of samples from a handle
+samplesFromHandle :: Int -> Handle -> Producer ByteString PitchTrack ()
+samplesFromHandle sampleNum = PB.hGet (sampleNum * sampleSize)
+
+-- | Stream chunks of a fixed number of samples from a lazy 'LBS.ByteString'
+samplesFromLBS :: Int -> LBS.ByteString -> Producer ByteString PitchTrack ()
+samplesFromLBS sampleNum lbs = PB.fromLazy lbs >-> PB.take sampleNum
+
+-- | Apply a function to each pitch
+forPitch :: Producer ByteString PitchTrack () -> (Double -> PitchTrack a) -> Producer a PitchTrack ()
+forPitch samplesProducer f = getPitch samplesProducer >-> P.mapM f
+
+-- | Consume all pitches, applying a function to each one
+forPitch_ :: Producer ByteString PitchTrack () -> (Double -> PitchTrack ()) -> Effect PitchTrack ()
+forPitch_ samplesProducer f = getPitch samplesProducer >-> P.mapM_ f
+
+-- | Stream computed pitches
+getPitch :: Producer ByteString PitchTrack () -> Producer Double PitchTrack ()
+getPitch samplesProducer = samplesProducer >-> P.mapM computePitch
+
+-- | Print all pitches
+printPitch :: Producer ByteString PitchTrack () -> Effect PitchTrack ()
+printPitch samplesProducer = getPitch samplesProducer >-> P.print
diff --git a/src/PitchTrack/Track.hs b/src/PitchTrack/Track.hs
new file mode 100644
--- /dev/null
+++ b/src/PitchTrack/Track.hs
@@ -0,0 +1,88 @@
+{- |
+This module provides a high-level interface for the most common cases.
+
+The default number of samples used for each computation is 2048,
+which should be enough if you never go below 65Hz.
+If you need a different number, you should use the functions whose name ends with @N@,
+which take the number of samples as a first parameter.
+-}
+module PitchTrack.Track (
+-- * Reading from a file
+    trackFile
+  , trackFileN
+  , trackFileToList
+-- * Reading from stdin
+  , trackStdin
+  , trackStdinN
+-- * Reading from a handle
+  , trackHandle
+  , trackHandleN
+-- * Reading from a lazy 'LBS.ByteString'
+  , trackLBS
+  , trackLBSN
+-- * Other definitions
+  , defaultSampleNum
+  ) where
+
+import           DywaPitchTrack
+import           PitchTrack.Pipes
+
+import qualified Data.ByteString.Lazy as LBS
+import           Pipes
+import qualified Pipes.Prelude        as P
+import           System.IO
+
+-- | Track a file and apply a function to each computed pitch
+--
+-- >>> trackFile "a440.raw" $ \pitch -> liftIO $ putStr (show pitch ++ ",")
+-- 440.0,440.0,440.0,440.0,440.0,440.0,440.0,440.0,440.0,440.0,440.0,440.0,
+trackFile :: FilePath -> (Double -> PitchTrack ()) -> IO ()
+trackFile = trackFileN defaultSampleNum
+
+-- | Same as 'trackFile', but takes the number of samples as a first parameter
+trackFileN :: Int -> FilePath -> (Double -> PitchTrack ()) -> IO ()
+trackFileN sampleNum file f = withFileR file $ \ h->
+    trackHandleN sampleNum h f
+
+-- | Same as 'trackFile' but reads from 'System.IO.stdin' instead of a file
+trackStdin :: (Double -> PitchTrack ()) -> IO ()
+trackStdin = trackStdinN defaultSampleNum
+
+-- | Same as 'trackStdin', but takes the number of samples as a first parameter
+trackStdinN :: Int -> (Double -> PitchTrack ()) -> IO ()
+trackStdinN sampleNum = trackHandleN sampleNum stdin
+
+-- | Same as 'trackFile' but reads from a handle instead of a file
+trackHandle :: Handle -> (Double -> PitchTrack ()) -> IO ()
+trackHandle = trackHandleN defaultSampleNum
+
+-- | Same as 'trackHandle', but takes the number of samples as a first parameter
+trackHandleN :: Int -> Handle -> (Double -> PitchTrack ()) -> IO ()
+trackHandleN sampleNum h f = runPitchTrack sampleNum $
+    runEffect $ forPitch_ (samplesFromHandle sampleNum h) f
+
+-- | Track a lazy 'LBS.ByteString' and apply a function to each computed pitch
+trackLBS :: LBS.ByteString -> (Double -> PitchTrack ()) -> IO ()
+trackLBS = trackLBSN defaultSampleNum
+
+-- | Same as 'trackLBS', but takes the number of samples as a first parameter
+trackLBSN :: Int -> LBS.ByteString -> (Double -> PitchTrack ()) -> IO ()
+trackLBSN sampleNum lbs f = runPitchTrack sampleNum $
+    runEffect $ forPitch_ (samplesFromLBS sampleNum lbs) f
+
+-- | Track a file and return a list of all the computed pitches
+--
+-- Note: the whole list is loaded into memory
+trackFileToList :: FilePath -> IO [Double]
+trackFileToList = trackFileToListN defaultSampleNum
+
+trackFileToListN  :: Int -> FilePath -> IO [Double]
+trackFileToListN sampleNum file = withFileR file $ \h ->
+    runPitchTrack sampleNum $ P.toListM $ forPitch (samplesFromHandle sampleNum h) return
+
+-- | The default number of samples used for each computation (2048)
+defaultSampleNum :: Int
+defaultSampleNum = 2048
+
+withFileR :: FilePath -> (Handle -> IO r) -> IO r
+withFileR file = withFile file ReadMode
diff --git a/test/PitchTrack/TrackSpec.hs b/test/PitchTrack/TrackSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/PitchTrack/TrackSpec.hs
@@ -0,0 +1,22 @@
+module PitchTrack.TrackSpec (spec) where
+
+import           PitchTrack.Track
+
+import           Control.Monad.IO.Class
+import           Test.Hspec
+
+spec :: Spec
+spec = describe "trackFile" $ do
+    it "should find the right frequency (±1Hz)" $ do
+        pitches <- liftIO $ trackFileToList a4File
+        all (\p -> abs (440.0 - p) < 1) (init pitches) `shouldBe` True
+
+    it "should compute the right number of pitches" $ do
+        pitches <- liftIO $ trackFileToList a4File
+        length pitches `shouldBe` ceiling (sampleRate * 5 / fromIntegral defaultSampleNum)
+
+a4File :: FilePath
+a4File = "test/files/a4-5sec.raw"
+
+sampleRate :: Double
+sampleRate = 44100
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
diff --git a/test/files/a4-5sec.raw b/test/files/a4-5sec.raw
new file mode 100644
Binary files /dev/null and b/test/files/a4-5sec.raw differ
