module Data.Riff.Assemble
( assembleRiffFile
, assembleRiffFileStream
) 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 . writeRiffFile
writeRiffFile :: RiffFile -> Put
writeRiffFile riffFile = do
printHeader . riffFileType $ riffFile
putSize context . calculateFileLength $ riffFile
putString . safeId . riffFileFormatType $ riffFile
sequence_ $ fmap (writeRiffChunk 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
}
writeRiffChunk :: AssemblyContext -> RiffChunk -> Put
writeRiffChunk context chunk@(RiffChunkChild _ _) = do
putString . safeId . riffChunkId $ chunk
let chunkSize = calculateChunkLength chunk
putSize context chunkSize
putLazyByteString . riffData $ chunk
maybeFillBlank chunkSize
writeRiffChunk context chunk@(RiffChunkParent _ _) = do
putString "LIST"
let chunkSize = calculateChunkLength chunk
putSize context chunkSize
putString . safeId . riffFormTypeInfo $ chunk
sequence_ $ fmap (writeRiffChunk 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 ' '