{- | 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) -} {-# LANGUAGE GeneralizedNewtypeDeriving #-} module DywaPitchTrack ( PitchTrack , runPitchTrack , askSampleNum , computePitch , neededSampleNum , sampleSize ) where import DywaPitchTrack.Internal.Bindings import Control.Monad.IO.Class import Control.Monad.Trans.Reader import Data.ByteString (ByteString) import qualified Data.ByteString as B import System.IO.Unsafe newtype PitchTrack a = PitchTrack { unPitchTrack :: ReaderT (DywaPitchTrackPtr, Int) IO a } deriving (Functor, Applicative, Monad, MonadIO) -- | Run the 'PitchTrack' monad runPitchTrack :: Int -- ^ Number of samples to be used for each computation -> PitchTrack a -- ^ Computations -> IO a runPitchTrack sampleNum f = withDywaPitchTrack $ \ptr -> do dywapitchInitTracking ptr runReaderT (unPitchTrack f) (ptr, sampleNum) -- | The number of samples used for each computation askSampleNum :: PitchTrack Int askSampleNum = PitchTrack $ snd <$> ask -- | Compute the pitch. -- -- The size of the ByteString must be equal to -- the number of samples set in 'runPitchTrack' * the size of each sample ('sampleSize'). -- -- Note: this pre-condition is __not__ checked! computePitch :: ByteString -- ^ Samples -> PitchTrack Double -- ^ Computed pitch computePitch rawSample = PitchTrack $ do (ptr, sampleNum) <- ask liftIO $ B.useAsCString rawSample $ \cString -> liftIO $ realToFrac <$> dywapitchComputePitch ptr (castToPtrDouble cString) 0 (fromIntegral sampleNum) -- | Calculate the number of samples needed, based on the lowest frequency neededSampleNum :: Int -- ^ Lowest frequency, in Hz -> Int -- ^ Number of samples needed for each computation neededSampleNum n = unsafePerformIO $ fromIntegral <$> dywapitchNeededSampleCount (fromIntegral n)