module Data.Conduit.Audio.Sndfile ( sourceSnd, sourceSndFrom , sinkSnd ) 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 (void) import Control.Monad.Fix (fix) import Control.Monad.Trans.Resource (MonadResource) -- | Uses @libsndfile@ to load an audio file as a stream. sourceSnd :: (MonadResource m, Snd.Sample a) => FilePath -> IO (AudioSource m a) sourceSnd = sourceSndFrom $ Frames 0 sourceSndFrom :: (MonadResource m, Snd.Sample a) => Duration -- ^ Initial position to seek to in the file (more efficient than using 'dropStart') -> 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 -- TODO: allow user to supply Snd.Format for raw files? 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 liftIO $ void $ Snd.hSeek h Snd.AbsoluteSeek fms 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 - fms -- | Uses @libsndfile@ to write an audio stream to a file. sinkSnd :: (MonadResource m, Snd.Sample a) => FilePath -> Snd.Format -> AudioSource m a -> m () sinkSnd fp fmt (AudioSource s r c _) = s C.$$ C.bracketP (Snd.openFile fp Snd.WriteMode $ Snd.defaultInfo { Snd.format = fmt , Snd.samplerate = round r , Snd.channels = c }) Snd.hClose (\h -> CL.mapM_ $ liftIO . void . Snd.hPutBuffer h . SndBuf.toBuffer)