module Rhs.SndFile (Header(..), readHeader, readSndFile, writeSndFile, auFloat) where import Data.Word import Rhs.U8v (i32_u8v, u8v_i32, u8vWrite, u8vRead, f32_u8v, u8v_f32) data Encoding = Unspecified | MuLaw8 | Linear8 | Linear16 | Linear24 | Linear32 | Float | Double deriving (Eq, Show, Enum) type Offset = Int type SampleRate = Int type FrameCount = Int type ChannelCount = Int data Header = Header FrameCount Encoding SampleRate ChannelCount deriving (Eq, Show) data Transcoder a = Transcoder Encoding (a -> [Word8]) ([Word8] -> a) -- | Bytes per sample at specified encoding. sizeOf :: Encoding -> Int sizeOf Unspecified = error "sizeOf: unspecified format" sizeOf MuLaw8 = 1 sizeOf Linear8 = 1 sizeOf Linear16 = 2 sizeOf Linear24 = 3 sizeOf Linear32 = 4 sizeOf Float = 4 sizeOf Double = 8 -- | The AU header magic number. auMagic :: Int auMagic = 0x2e736e64 -- | Byte-encode an AU header. encodeHeader :: Header -> [Word8] encodeHeader (Header nf enc sr nc) = f [auMagic, 28, nb, (fromEnum enc), sr, nc, 0] where f = concatMap i32_u8v nb = nf * nc * (sizeOf enc) -- | Byte-decode an AU header. decodeHeader :: [Word8] -> (Offset, Header) decodeHeader u = (off, Header nf enc sr nc) where f n = u8v_i32 (take 4 (drop n u)) off = f 4 nb = f 8 enc = toEnum (f 12) sr = f 16 nc = f 20 nf = nb `div` (nc * sizeOf enc) -- | Write sound file, data is interleaved. writeSndFile :: (Transcoder a) -> SampleRate -> ChannelCount -> FilePath -> [a] -> IO () writeSndFile (Transcoder enc encdr _) sr nc fn d = u8vWrite fn b where nf = length d `div` nc h = encodeHeader (Header nf enc sr nc) d' = concatMap encdr d b = h ++ d' -- | Read sound file meta data. readHeader :: FilePath -> IO Header readHeader fn = do b <- u8vRead fn let (_, h) = decodeHeader b return h -- | Read sound file, data is interleaved. readSndFile :: (Transcoder a) -> FilePath -> IO [a] readSndFile (Transcoder e _ d) f = do b <- u8vRead f let (off, _) = decodeHeader b n = sizeOf e return (map d (setsOf n (drop off b))) where setsOf _ [] = [] setsOf n l = a : setsOf n b where (a, b) = splitAt n l -- | Transcoder for 32-bit floating point data. auFloat :: Transcoder Double auFloat = Transcoder Float f32_u8v u8v_f32