module Data.Conduit.Audio.Sndfile
( sourceSnd, sourceSndFrom, sourceSndWithHandle
, sinkSnd, sinkSndWithHandle
) where
import Data.Conduit.Audio
import qualified Data.Conduit as C
import qualified Data.Conduit.List as CL
import qualified Sound.File.Sndfile as Snd
import qualified Sound.File.Sndfile.Buffer.Vector as SndBuf
import Control.Monad.IO.Class
import Control.Monad.Trans.Class (lift)
import Control.Monad (void)
import Control.Monad.Fix (fix)
import Control.Monad.Trans.Resource (MonadResource)
sourceSnd
:: (MonadResource m, Snd.Sample a)
=> FilePath
-> IO (AudioSource m a)
sourceSnd = sourceSndFrom $ Frames 0
sourceSndFrom
:: (MonadResource m, Snd.Sample a)
=> Duration
-> FilePath
-> IO (AudioSource m a)
sourceSndFrom (Seconds secs) fp = do
info <- Snd.getFileInfo fp
sourceSndFrom (Frames $ secondsToFrames secs $ fromIntegral $ Snd.samplerate info) fp
sourceSndFrom (Frames fms) fp = do
src <- sourceSndWithHandle fp $ \h -> liftIO $ void $ Snd.hSeek h Snd.AbsoluteSeek fms
return src{ frames = frames src fms }
sourceSndWithHandle
:: (MonadResource m, Snd.Sample a)
=> FilePath
-> (Snd.Handle -> m ())
-> IO (AudioSource m a)
sourceSndWithHandle fp setup = do
info <- Snd.getFileInfo fp
let r = fromIntegral $ Snd.samplerate info
c = Snd.channels info
src = C.bracketP
(Snd.openFile fp Snd.ReadMode Snd.defaultInfo)
Snd.hClose
$ \h -> do
lift $ setup h
fix $ \loop -> liftIO (Snd.hGetBuffer h chunkSize) >>= \mx -> case mx of
Nothing -> return ()
Just buf -> do
C.yield $ SndBuf.fromBuffer buf
loop
return $ AudioSource src r c $ Snd.frames info
sinkSnd :: (MonadResource m, Snd.Sample a) => FilePath -> Snd.Format -> AudioSource m a -> m ()
sinkSnd fp fmt = sinkSndWithHandle fp fmt $ \_ -> return ()
sinkSndWithHandle
:: (MonadResource m, Snd.Sample a)
=> FilePath
-> Snd.Format
-> (Snd.Handle -> m ())
-> AudioSource m a
-> m ()
sinkSndWithHandle fp fmt setup (AudioSource s r c _) = s C.$$ do
C.bracketP
(Snd.openFile fp Snd.WriteMode $ Snd.defaultInfo
{ Snd.format = fmt
, Snd.samplerate = round r
, Snd.channels = c
})
Snd.hClose
$ \h -> do
lift $ setup h
CL.mapM_ $ liftIO . void . Snd.hPutBuffer h . SndBuf.toBuffer