{-# LANGUAGE MultiParamTypeClasses, ScopedTypeVariables #-} -- | This module provides the 'Buffer' type class that abstracts the array type that is being used for I\/O. For concrete instances see for example the /hsndfile-vector/ package . module Sound.File.Sndfile.Buffer ( module Sound.File.Sndfile.Buffer.Sample , Buffer(..) , hGetBuffer , hGetContents , readFile -- , hGetContentChunks -- , readFileChunks , hPutBuffer , writeFile ) where import Control.Exception (bracket) import Foreign import Prelude hiding (readFile, writeFile) import Sound.File.Sndfile.Exception (throw) import Sound.File.Sndfile.Interface import Sound.File.Sndfile.Buffer.Sample (Sample(..)) import System.IO.Unsafe (unsafeInterleaveIO) -- | Buffer class for I\/O on soundfile handles. class Buffer a e where -- | Construct a buffer from a 'ForeignPtr', a start index and the element count. fromForeignPtr :: ForeignPtr e -> Int -> Int -> IO (a e) -- | Retrieve from a buffer a 'ForeignPtr' pointing to its data, a start index and an element count. toForeignPtr :: a e -> IO (ForeignPtr e, Int, Int) -- | Return an buffer with the requested number of frames of data. -- The resulting buffer size is equal to the product of the number of frames `n' and the number of channels in the soundfile. hGetBuffer :: forall a e . (Sample e, Storable e, Buffer a e) => Handle -> Count -> IO (Maybe (a e)) hGetBuffer h n = do p <- mallocBytes (sizeOf (undefined :: e) * numChannels * n) n' <- hGetBuf h p n if n' == 0 then return Nothing else do fp <- newForeignPtr finalizerFree p Just `fmap` fromForeignPtr fp 0 (n * numChannels) where numChannels = (channels.hInfo) h -- | Return the contents of a handle open for reading in a single buffer. hGetContents :: (Sample e, Buffer a e) => Handle -> IO (Info, Maybe (a e)) hGetContents h = (,) info `fmap` hGetBuffer h (frames info) where info = hInfo h -- | Return the contents of a handle open for reading as a lazy list of buffers. hGetContentChunks :: (Sample e, Buffer a e) => Count -> Handle -> IO (Info, [a e]) hGetContentChunks n h = (,) (hInfo h) `fmap` lazyread where {-# NOINLINE lazyread #-} lazyread = unsafeInterleaveIO loop loop = do r <- hGetBuffer h n case r of Just b -> do bs <- lazyread return (b:bs) Nothing -> return [] -- | Return the contents of a file in a single buffer. readFile :: (Sample e, Buffer a e) => FilePath -> IO (Info, Maybe (a e)) readFile path = do bracket (openFile path ReadMode defaultInfo) (hClose) (hGetContents) -- | Return the contents of a file as a lazy list of buffers. readFileChunks :: (Sample e, Buffer a e) => Count -> FilePath -> IO (Info, [a e]) readFileChunks n path = do bracket (openFile path ReadMode defaultInfo) (hClose) (hGetContentChunks n) -- | Write the contents of a buffer to a handle open for writing. -- Return the number of frames written. hPutBuffer :: forall a e . (Sample e, Storable e, Buffer a e) => Handle -> a e -> IO Count hPutBuffer h buffer = do (fp, i, n) <- toForeignPtr buffer if n `mod` numChannels /= 0 then throw 0 "hPutBuffer: invalid buffer size (not a multiple of channel count)" else do withForeignPtr fp $ \ptr -> do let p = plusPtr ptr (sizeOf (undefined :: e) * i) :: Ptr e hPutBuf h p (n `div` numChannels) where numChannels = channels $ hInfo h -- | Write the contents of a buffer to a file. -- Return the number of frames written. writeFile :: (Sample e, Buffer a e) => Info -> FilePath -> a e -> IO Count writeFile info path buffer = do bracket (openFile path WriteMode info) (hClose) (flip hPutBuffer buffer)