-- | 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) import qualified Data.ByteString.Lazy as B import Data.Int import Data.Maybe import Sound.OpenSoundControl.Coding.Byte 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 2 = Linear8 toEncoding 3 = Linear16 toEncoding 5 = Linear32 toEncoding 6 = Float toEncoding 7 = Double toEncoding _ = undefined fromEncoding :: Encoding -> Int fromEncoding Linear8 = 2 fromEncoding Linear16 = 3 fromEncoding Linear32 = 5 fromEncoding Float = 6 fromEncoding 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 :: FilePath -> Header -> [[Double]] -> 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 = do (h, _) <- read fn return h -- | Read sound file, data is interleaved. read :: FilePath -> IO (Header, [[Double]]) read fn = do (Header nf enc sr nc, b) <- readB fn let d = decode enc nc b return (Header nf enc sr nc, d)