-- | Read and write NeXT/Sun format sound files. module Sound.File.NeXT ( FrameCount,SampleRate,ChannelCount,Header(..) , header , read , write , module Sound.File.Encoding ) where import Prelude hiding (read) {- base -} import qualified Data.ByteString.Lazy as B {- bytestring -} import Data.Int {- base -} import Data.Maybe {- base -} import Sound.OSC.Coding.Byte {- hosc -} import Sound.File.Decode import Sound.File.Encode import Sound.File.Encoding type Offset = Int64 -- | Sample rate at 'Header'. type SampleRate = Int -- | Number of frames at 'Header'. type FrameCount = Int -- | Number of channels at 'Header'. type ChannelCount = Int -- | Data type encapsulating sound file meta data. data Header = Header { frameCount :: FrameCount , encoding :: Encoding , sampleRate :: SampleRate , channelCount :: ChannelCount } deriving (Eq, Show) toEncoding :: Int -> Encoding toEncoding n = case n of 2 -> Linear8 3 -> Linear16 5 -> Linear32 6 -> Float 7 -> Double _ -> undefined fromEncoding :: Encoding -> Int fromEncoding c = case c of Linear8 -> 2 Linear16 -> 3 Linear32 -> 5 Float -> 6 Double -> 7 -- | The NeXT header magic number. magic :: Int magic = 0x2e736e64 -- | Byte-encode a NeXT header. encodeHeader :: Header -> B.ByteString encodeHeader (Header nf enc sr nc) = let nb = nf * nc * sizeOf enc h = [magic, 28, nb, fromEncoding enc, sr, nc, 0] in B.concat (map encode_i32 h) -- | Byte-decode a NeXT header. decodeHeader :: B.ByteString -> Maybe (Offset, Header) decodeHeader u = let f n = decode_i32 (B.drop n u) m = f 0 off = fromIntegral (f 4) nb = f 8 enc = toEncoding (f 12) sr = f 16 nc = f 20 nf = nb `div` (nc * sizeOf enc) in if m == magic then Just (off, Header nf enc sr nc) else Nothing writeB :: FilePath -> Header -> B.ByteString -> IO () writeB fn (Header nf enc sr nc) d = let h = encodeHeader (Header nf enc sr nc) b = B.append h d in B.writeFile fn b -- | Write sound file, data is non-interleaved. write :: (Real n,Floating n) => FilePath -> Header -> [[n]] -> IO () write fn (Header nf enc sr nc) d = let b = encode enc d in writeB fn (Header nf enc sr nc) b readB :: FilePath -> IO (Header, B.ByteString) readB fn = do b <- B.readFile fn let (off, h) = fromMaybe undefined (decodeHeader b) return (h, B.drop off b) -- | Read sound file meta data. header :: FilePath -> IO Header header fn = fmap fst (readB fn) -- | Read sound file, data is interleaved. read :: (Real n,Floating n) => FilePath -> IO (Header, [[n]]) read fn = do (Header nf enc sr nc, b) <- readB fn let d = decode enc nc b return (Header nf enc sr nc, d)