Copyright | (c) Robert Massaioli, 2014 |
---|---|
License | MIT |
Maintainer | robertmassaioli@gmail.com |
Stability | experimental |
Safe Haskell | Safe-Inferred |
Language | Haskell2010 |
The RIFF module allows the parsing of RIFF files in pure Haskell. It was written to be as efficient and as simple as possible.
You can parse a RIFF file using the methods provided in this module. For example, if you wanted to parse a RIFF file and print it out then you could:
main = withRiffFile "path/to/file.riff" print
And that will print the RIFF file, in gory details, to the screen. You can also use this module to create a RIFF file in pure Haskell starting with a RiffFile and building it up until you are ready to write it out to a ByteString or Disk. For example, this is how you might construct a RIFF file to be written out:
import Data.Riff import qualified Data.ByteString.Lazy as BL riffFile = RiffFile RIFX "EXPL" children children = [ RiffChunkChild "fst " $ BL.pack [1..11] , RiffChunkChild "snd " $ BL.pack [11..100] ] main = assembleRiffFile "example.riff" riffFile
As you can see it is a very simple API that lets you write out data into Riff Files. Have a play around with with the examples until you can see how it works and fits together.
- data RiffFile = RiffFile {}
- type RiffChunkSize = Word32
- data RiffFileType
- data RiffChunk
- = RiffChunkChild { }
- | RiffChunkParent { }
- type RiffId = String
- type RiffData = ByteString
- type ParseError = (ByteOffset, String)
- withRiffFile :: FilePath -> (Either ParseError RiffFile -> IO ()) -> IO ()
- parseRiffData :: ByteString -> Either ParseError RiffFile
- assembleRiffFile :: FilePath -> RiffFile -> IO ()
- assembleRiffFileStream :: RiffFile -> ByteString
RIFF File Data Representaion
This is our representation of a RIFF file. These files all have a format type and are composed by one or more nestable data Chunks which we represent with a RiffChunk.
RiffFile | |
|
type RiffChunkSize = Word32 Source
A Riff file is made up exclusively of Riff Chunks and each chunk, as the second piece of data in the chunk, contains it's size. The size never includes the first 8 bytes of the chunk, which are the Chunk Id and the Chunk Size but, in the case of a nested chunk, it does include the four bytes of the Chunk FormType Id.
According to the specification a chunk size must be represented by four bytes of data. This means that the maximum number of bytes that can be present in a RIFF file is:
2 ^ 32 + 8 = 4294967304 bytes or ~4GB
If your raw data is larger than that then this file format cannot support it.
data RiffFileType Source
There are only two different types of RIFF file: RIFF and RIFX and the difference is in the way that data is encoded inside them.
A RiffFile is just an alias for a RiffChunk. A RiffFile is merely a nested collection of RiffChunks where the first element must be a list of chunks with the ID RIFF or RIFX.
A RiffId is just a four character string (FourCC). It is usually (but by no means always) chosen to be something that is human readable when converted to ASCII. Please note that attempting to assemble a riff file with a RIFF ID that is not exactly four characters long will result in the RiffId being modified in the output stream to be four characters long. If the ID is too short then it will be padded with space (' ') characters and if it is too long then it will be truncated at four characters.
type RiffData = ByteString Source
The data in a riff file is just a stream of bytes.
type ParseError = (ByteOffset, String) Source
Represents an error in the parsing of a Riff File. It contains the location in the file that we read up to and a message of what went wrong.
Reading (parsing) RIFF Files
:: FilePath | The file that will be read. |
-> (Either ParseError RiffFile -> IO ()) | An action to perform on the potentialy parsed file |
-> IO () | The resultant IO action. |
Given a FilePath you can provide a function that will be given either a ParseError or an actual RiffFile to process. It is important to note that you should do all of the processing of the RiffFile in the provided action because the file will be closed at the end of this function.
parseRiffData :: ByteString -> Either ParseError RiffFile Source
You can parse a raw ByteString and try and convert it into a RiffFile. This will give a best attempt at parsing the data and, if success is not possible, will give you a ParseError.
:: FilePath | The location on the filesystem to save the RiffFile. |
-> RiffFile | The in-memory representation of a RiffFile to be saved. |
-> IO () | Writing to disk is an IO operatin and we return no results. |
Given a file path and a RiffFile representation this will allow you to safely write out a RiffFile to disk. This allows you to save anything that you like in a RiffFile. Just remember that the maximum file size of a RiffFile is bounded by the maximum size of a 32bit integer. The behaviour of this function, should you give it too much data, is undefined.
:: RiffFile | The RIFF file to be written out. |
-> ByteString | The resultant stream of bytes representing the file. |
Assembles a RiffFile into it's representation in a Lazy ByteString.