Lazy Sound File IO

Hannah Schroeter
Sun, 2 Dec 2001 13:17:46 +0100


On Tue, Oct 23, 2001 at 11:01:00AM -0400, Carl McTague wrote:
> Please let me refine my question.

> Is there a general way to coerce an [Int] or [Double] etc. into a String,
> so that it may be written out to a binary file with hPutStr?  There is
> toEnum, but this doesn't seem like a real solution, since it only works for
> Int values <256.

I'd write some format at least readable by programs like sox. E.g.
raw headerless 16-bit signed integers w/ a fixed sample rate. Maybe
even stereo if you are generating stereo sounds.

Now, let's do simple 16-bit integer lists, assume we have a stream
of Ints which additionally are between -2^15 and 2^15 - 1. And assume
we want network byte order (high byte first).

writeInts :: [Int] -> IO ()
writeInts = putStr . ints2Str

ints2Str :: [Int] -> String
ints2Str = concatMap int2str
	int2str i | i >= -32768 && i <= 32767 =
	    map Char.chr [(i `mod` 65536) `div` 256, i `mod` 256]

That's it.

If you want to write stereo, you should interleave the Ints,
such as

nullElement :: Int -- the sample value for silence

mixStereo :: [Int] -> [Int] -> ([Int], [Int])
mixStereo [] [] = []
mixStereo l [] = zip l (repeat nullElement)
mixStereo [] l = zip (repeat nullElement) l
mixStereo (x:xs) (y:ys) = (x,y) : mixStereo xs ys

Hope that helps, even if late.

Kind regards,