module Data.Riff.Assemble
( assembleRiffFile
, assembleRiffFileStream
, putRiffFile
) where
import Data.Riff.RiffData
import Data.Riff.Operations
import qualified Data.ByteString.Lazy as BL
import System.IO (withBinaryFile, IOMode(..))
import Control.Monad (when)
import Data.Binary.Put
import Data.Char (ord)
assembleRiffFile
:: FilePath
-> RiffFile
-> IO ()
assembleRiffFile filePath riffFile = withBinaryFile filePath WriteMode $ \h ->
BL.hPut h (assembleRiffFileStream riffFile)
assembleRiffFileStream
:: RiffFile
-> BL.ByteString
assembleRiffFileStream = runPut . putRiffFile
putRiffFile
:: RiffFile
-> Put
putRiffFile riffFile = do
printHeader . riffFileType $ riffFile
putSize context . calculateFileLength $ riffFile
putString . safeId . riffFileFormatType $ riffFile
sequence_ $ fmap (putRiffChunk context) (riffFileChildren riffFile)
where
context = getContext . riffFileType $ riffFile
getContext :: RiffFileType -> AssemblyContext
getContext RIFF = AssemblyContext putWord32le
getContext RIFX = AssemblyContext putWord32be
data AssemblyContext = AssemblyContext
{ putSize :: RiffChunkSize -> Put
}
putRiffChunk :: AssemblyContext -> RiffChunk -> Put
putRiffChunk context chunk@(RiffChunkChild _ _) = do
putString . safeId . riffChunkId $ chunk
let chunkSize = calculateChunkLength chunk
putSize context chunkSize
putLazyByteString . riffData $ chunk
maybeFillBlank chunkSize
putRiffChunk context chunk@(RiffChunkParent _ _) = do
putString "LIST"
let chunkSize = calculateChunkLength chunk
putSize context chunkSize
putString . safeId . riffFormTypeInfo $ chunk
sequence_ $ fmap (putRiffChunk context) (riffChunkChildren chunk)
maybeFillBlank chunkSize
maybeFillBlank :: RiffChunkSize -> Put
maybeFillBlank chunkSize = when (chunkSize `mod` 2 == 1) putBlankByte
putBlankByte = putWord8 0
printHeader :: RiffFileType -> Put
printHeader RIFF = putString "RIFF"
printHeader RIFX = putString "RIFX"
putString :: String -> Put
putString = sequence_ . fmap (putWord8 . fromIntegral . ord)
safeId :: RiffId -> RiffId
safeId input = take 4 $ input ++ repeat ' '