diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2015 Reed Rosenbluth
+
+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/djembe.cabal b/djembe.cabal
new file mode 100644
--- /dev/null
+++ b/djembe.cabal
@@ -0,0 +1,44 @@
+-- Initial djembe.cabal generated by cabal init.  For further 
+-- documentation, see http://haskell.org/cabal/users-guide/
+
+name:                djembe
+version:             0.1.0.0
+synopsis:            Hit drums with haskell           
+description:
+  A simple DSL for composing drum beats in haskell.
+  Djembe uses the system MIDI device to play these beats.
+license:             MIT
+license-file:        LICENSE
+author:              Reed Rosenbluth
+maintainer:          Reed.Rosenbluth@gmail.com
+copyright:           2015 Reed Rosenbluth
+category:            Sound
+build-type:          Simple
+-- extra-source-files:  
+cabal-version:       >=1.10
+
+library
+  exposed-modules:     Types, 
+                       Play,
+                       Interpret,
+                       Drum
+  build-depends:       base >=4.6 && <4.9
+                      ,lens >=4.7 && <4.14
+                      ,hmidi >= 0.2.1 && <3.0
+                      ,random >=1.0 && <1.2
+                      ,QuickCheck >=2.7 && <2.9
+                      ,hspec >=2.1 && <2.3
+                      ,mtl
+  hs-source-dirs:      src
+  default-language:    Haskell2010
+
+test-suite djembe-test
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      test
+  main-is:             Spec.hs
+  build-depends:       base
+                      ,hspec
+                      ,QuickCheck
+                      ,djembe
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N
+  default-language:    Haskell2010
diff --git a/src/Drum.hs b/src/Drum.hs
new file mode 100644
--- /dev/null
+++ b/src/Drum.hs
@@ -0,0 +1,81 @@
+module Drum where
+
+import Types
+import Control.Lens
+
+-- | default volume
+volume :: Rational
+volume = 127
+
+-- | set the duration for each `Hit` in a `Song`
+-- where the first argument represents the duration as a
+-- fraction of a whole note.
+note :: Rational -> Song -> Song
+note n = cmap (\h -> h & dur .~ 4 / n)
+
+-- | replicate a song a number of times
+clone :: Rational -> Song -> Song
+clone 1 s = s
+clone n s = s >> clone (n-1) s
+
+-- | modify the duration for each `Hit` in a `Song` to be
+-- 1.5 times as long
+dot :: Song -> Song
+dot = cmap (\h -> h & dur %~ (* 1.5))
+
+-- | quarter note rest
+rest :: Rational -> Song
+rest n = note n $ hsong (Hit BassDrum1 0 0)
+
+-- | notes of various durations
+n1, n2, n4, n8, n16, n32, n64 :: Song -> Song
+n1  = note 1
+n2  = note 2
+n4  = note 4
+n8  = note 8
+n16 = note 16
+n32 = note 32
+n64 = note 64
+
+-- | rests of various durations
+r1, r2, r4, r8, r16, r32, r64 :: Song
+r1  = rest 1
+r2  = rest 2
+r4  = rest 4
+r8  = rest 8
+r16 = rest 16
+r32 = rest 32
+r64 = rest 64
+
+
+-- | create an infinite loop of a `Song`
+loop :: Song -> Song
+loop s = s >> loop s
+
+-- | transform a `Beat` to a `Song`
+song :: Beat -> Song
+song b = Composition (b, ())
+
+-- | transform a `Hit` to a `Song`
+hsong :: Hit -> Song
+hsong = song . Single
+
+-- | change the velocity (volume) for each `Hit` in a `Song`
+velocity :: Rational -> Song -> Song
+velocity v = cmap (\h -> h & vol .~ max 0 (min 127 v))
+
+-- | bass drum `Hit`
+bd :: Song
+bd = n4 $ hsong (Hit BassDrum1 0 volume)
+
+-- | snare drum `Hit`
+sn :: Song
+sn = n4 $ hsong (Hit SnareDrum2 0 volume)
+
+-- | hihat drum `Hit`
+hi :: Song
+hi = n4 $ hsong (Hit ClosedHihat 0 volume)
+
+-- | rest `Hit`
+rt :: Song
+rt = n4 $ hsong (Hit BassDrum1 0 0)
diff --git a/src/Interpret.hs b/src/Interpret.hs
new file mode 100644
--- /dev/null
+++ b/src/Interpret.hs
@@ -0,0 +1,36 @@
+module Interpret where
+
+import Types
+import Control.Lens
+
+-- | calculates the total duration of a `Beat`
+totalDur :: Beat -> Rational
+totalDur (Single hit)     = hit ^. dur
+totalDur (Series c1 c2)   = totalDur c1 + totalDur c2
+totalDur (Parallel c1 c2) = max (totalDur c1) (totalDur c2)
+totalDur None             = 0
+
+-- | merge two sorted lists of `Hit`s
+mergeHits :: [Hit] -> [Hit] -> [Hit]
+mergeHits [] ys = ys
+mergeHits xs [] = xs
+mergeHits (x:xs) (y:ys)
+  | x <= y    = x : mergeHits xs (y:ys)
+  | otherwise = y : mergeHits (x:xs) ys
+
+-- | apply a tempo to a composition
+applyTempo :: Rational -> Composition a -> Composition a
+applyTempo n = cmap (\h -> h & dur *~ (60000 / n))
+
+-- | convert a `Composition` to a list of `Hit`s
+toHits :: Composition a -> [Hit]
+toHits (Composition (beat, _)) = go 0 beat
+  where
+    go d (Single hit)     = [hit & dur .~ d]
+    go d (Series b1 b2)   = go d b1 ++ go (d + totalDur b1) b2
+    go d (Parallel b1 b2) = mergeHits (go d b1) (go d b2)
+    go _ None             = []
+
+-- | interpret a `Composition` with a given tempo
+interpret :: Rational -> Composition a -> [Hit]
+interpret n c = toHits $ applyTempo n c
diff --git a/src/Play.hs b/src/Play.hs
new file mode 100644
--- /dev/null
+++ b/src/Play.hs
@@ -0,0 +1,57 @@
+module Play (play, play', loop') where
+
+import Data.Ratio
+import Control.Monad
+import Control.Monad.State
+import Control.Lens
+import Control.Concurrent
+import System.MIDI
+
+import Types
+import Drum
+import Interpret
+
+-- | convert a `Hit` to a `MidiEvent`
+hitToMidiEvent :: Hit -> MidiEvent
+hitToMidiEvent h = MidiEvent d (MidiMessage 1 (NoteOn t v))
+  where
+    t  = 35 + fromEnum (h ^. tone)
+    d  = fromR $ h ^. dur
+    v  = fromR $ h ^. vol
+    fromR r = fromIntegral $ numerator r `div` denominator r
+
+getConnection :: IO Connection
+getConnection = do
+    dstlist <- enumerateDestinations
+    case dstlist of
+      [] -> error "No MIDI Devices found."
+      (dst:_) -> openDestination dst
+
+-- | play a composition at a given tempo
+play :: Composition a -> Rational -> IO ()
+play comp n = do
+    conn <- getConnection
+    start conn
+    evalStateT runComposition (conn, interpret n comp)
+    close conn
+
+play' :: Composition a -> IO ()
+play' = flip play 120
+
+-- | loop a composition at a given tempo
+loop' :: Song -> Rational -> IO ()
+loop' s = play (loop s)
+
+runComposition :: StateT (Connection, [Hit]) IO ()
+runComposition = do
+  (conn, comp) <- get
+  t <- lift $ currentTime conn
+  case comp of
+    []     -> return ()
+    (h:hs) -> do
+      let (MidiEvent s ev) = hitToMidiEvent h
+      when (s < t) $ do
+        put (conn, hs)
+        lift $ send conn ev
+      lift $ threadDelay 250
+      runComposition
diff --git a/src/Types.hs b/src/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/Types.hs
@@ -0,0 +1,122 @@
+{-# LANGUAGE TemplateHaskell, FlexibleInstances #-}
+
+module Types where
+
+import Data.Monoid
+import Data.Ratio
+import Control.Applicative
+import Control.Monad
+import Control.Lens hiding (elements)
+import System.Random
+import Test.QuickCheck
+
+-- | Types of drum sounds
+data Sound =
+     BassDrum2     | BassDrum1     | SideStick    | SnareDrum1
+   | HandClap      | SnareDrum2    | LowTom2      | ClosedHihat
+   | LowTom1       | PedalHihat    | MidTom2      | OpenHihat
+   | MidTom1       | HighTom2      | CrashCymbal1 | HighTom1
+   | RideCymbal1   | ChineseCymbal | RideBell     | Tambourine
+   | SplashCymbal  | Cowbell       | CrashCymbal2 | VibraSlap
+   | RideCymbal2   | HighBongo     | LowBongo     | MuteHighConga
+   | OpenHighConga | LowConga      | HighTimbale  | LowTimbale
+   | HighAgogo     | LowAgogo      | Cabasa       | Maracas
+   | ShortWhistle  | LongWhistle   | ShortGuiro   | LongGuiro
+   | Claves        | HighWoodBlock | LowWoodBlock | MuteCuica
+   | OpenCuica     | MuteTriangle  | OpenTriangle
+   deriving (Show, Eq, Ord, Enum, Bounded)
+
+instance Arbitrary Sound where
+  arbitrary = toEnum <$> choose
+    (fromEnum (minBound :: Sound),
+     fromEnum (maxBound :: Sound))
+
+-- | A drum `Hit` with a tone, duration, and volume
+data Hit = Hit
+    { _tone :: Sound
+    , _dur  :: Rational
+    , _vol  :: Rational
+    } deriving (Show, Eq)
+
+instance Arbitrary Hit where
+  arbitrary = do
+    tone <- arbitrary
+    dur  <- toRational <$> choose (1 :: Int, 64)
+    vol  <- toRational <$> choose (0 :: Int, 127)
+    return $ Hit tone dur vol
+
+makeLenses ''Hit
+
+cmpToneVol :: Hit -> Hit -> Bool
+cmpToneVol x y
+  | xTone  < yTone = True
+  | xTone == yTone = x ^. vol < y ^. vol
+  | otherwise = False
+  where xTone = x ^. tone
+        yTone = y ^. tone
+
+instance Ord Hit where
+  x <= y
+    | xDur  < yDur = True
+    | xDur == yDur = cmpToneVol x y
+    | otherwise    = False
+    where xDur = x ^. dur
+          yDur = y ^. dur
+
+-- | Used for combining Hits and Beats
+data Beat =
+    None
+  | Single   Hit
+  | Series   Beat Beat
+  | Parallel Beat Beat
+  deriving (Show, Eq)
+
+instance Arbitrary Beat where
+  arbitrary = sized arbnB
+
+arbnB :: Int -> Gen Beat
+arbnB n = frequency [
+    (1, return None),
+    (3, liftM  Single arbitrary),
+    (n, liftM2 Series (arbnB (n `div` 2)) (arbnB (n `div` 2))),
+    (n, liftM2 Parallel (arbnB (n `div` 8)) (arbnB (n `div` 8))) ]
+
+-- | We wrap a `Beat` in the `Composition` data structure in order
+-- create a monad instance for it.
+data Composition a = Composition (Beat, a) deriving (Show)
+
+type Song = Composition ()
+
+instance Arbitrary Song where
+  arbitrary = do
+    b <- arbitrary :: Gen Beat
+    return $ Composition (b, ())
+
+instance Functor Composition where
+  fmap = liftM
+
+instance Applicative Composition where
+  pure  = return
+  (<*>) = ap
+
+-- | This is basically a specialized instance of the writer monad
+-- for composing compositions in series.
+instance Monad Composition where
+  return a = Composition (None, a)
+  Composition (b, a) >>= k =
+    let (Composition (b', a')) = k a
+    in Composition (Series b b', a')
+
+instance Monoid (Composition ()) where
+  mempty = Composition (None, ())
+  mappend (Composition (b1, _)) (Composition (b2, _))
+    = Composition (Parallel b1 b2, ())
+
+-- | Lift a function on `Hit`s over a `Composition`
+cmap :: (Hit -> Hit) -> Composition a -> Composition a
+cmap f (Composition (c,a)) = Composition (hmap f c, a)
+  where
+    hmap g (Single h)       = Single (g h)
+    hmap g (Series b1 b2)   = Series (hmap g b1) (hmap g b2)
+    hmap g (Parallel b1 b2) = Parallel (hmap g b1) (hmap g b2)
+    hmap _ b                = b
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 #-}
