You might consider using the pipes library:
http://hackage.haskell.org/package/pipes-4.1.8/docs/Pipes-Tutorial.html
The pipes library will allow you to generate audio and write it to
disk with out having to worry if you are going to suck up all the RAM
accidentally.
It should also help you decompose your pipeline into smaller pieces.
For example, you would like to be able to decompose your code into
things like:
1. the code that generates the audio
2. the code that converts the audio into a format like wav/aiff/etc
3. the code that writes binary data to disk
And then simply glue those pieces together, while feeling secure that
you don't create a space leak in the process.
It will also allow you to use StateT or IO (or anything other type
with a Monad instance) if you need to.
The pipes library is not trivial to learn. But it is well designed.
Without using the pipes library you have two options:
1. understand how laziness works and then be very careful to make
sure you never accidentally hold onto data too long and cause a space
leak.
2. use strict IO functions and write code that generates the output
in little chunks at a time. The concept is, perhaps, easy to
understand. But as your code base grows, the code will become harder
to understand and to modify.