This is the sort of thing that most people would put in a blog post,
but I don't have a blog so I'm sending it to this list, since I think
most readers would be interested.
I've recently been doing some tests of different sound file I/O
packages in Haskell, and I would like to share the results. My test
script involves reading a 66MB Wave file, a stereo recording at 44,100
/ 16. The entire file is processed to determine the peak sample
value. This is of particular instance to me because it involves
scanning a large amount of data in its entirety, the file format is
extremely common, and the process seems representative of the sort of
operations often encountered doing audio work.
I tested the following packages:
in addition, I tested two experimental interfaces to my library,
HSoundFile. I did not test the current published interface,
HSoundFile v. 2. The first interface I am experimenting with uses a
"smart handle" approach, and in some ways is very similar to a Haskell
implementation of libsndfile. I refer to this as HSoundFile-3. The
second interface uses an enumerator streams overlaid on a Word8
enumerator, derived from Oleg's Iteratee package. I refer to this as
All packages are haskell only, except hsndfile, which is a binding to
the C libsndfile library.
HSoundFile-3 - a stream type is created to hold AudioBuffers. A
recursive reader function uses unsafeInterleaveIO to read the entire
file on demand. A strict foldl processes the Stream chunks to achieve
the final value.
Enumerator - An Iteratee is processes all values as received
Codecs - the data list is processed by a strict foldl
WAVE - the data list is processed by a strict foldl
hsndfile - a recursive I/O function reads a chunk from the file (using
IOCArray type) and accumulates the maximum values from each chunk. I
attempted to create a framework like used for HSoundFile-3, however
performance dropped dramatically; I suspect the slowdown is mostly
from the process of freezing mutable arrays to immutable arrays.
HSoundFile-3 - strict reads/writes of buffers
Enumerator - enumerator-based strict I/O
Codecs - read/write entire file into data structure
WAVE - lazily read file into list
hsndfile - strict reads/writes of buffers
HSoundFile-3 - custom AudioBuffer class. Implementations are
provided for UArr Double, List Double, and StorableVector Double
Enumerator - enumerates Doubles
Codecs - array of Doubles
WAVE - [[Int32]]
hsndfile - custom Buffer class with Array constraint. Implementations
are provided for IOCArray and StorableVector.
For chunked data types, all data is with chunk size 1000.
All timing/memory data is the median value from multiple runs. In
general different runs had very similar performance.
HSoundFile-3, StorableVector - real 16.5s
HSoundFile-3, UArr - real 15.7s
HSoundFile-3, List - real 17.6s
Codecs - real 1m20s
Enumerator - real 19s
WAVE - real 29.1s
hsndfile - real 2.0s
Memory results (running test program with -hT)
HSoundFile-3, StorableVector - 457,674,398 bytes x seconds
HSoundFile-3, UArr - 531,452,404 bytes x seconds
HSoundFile-3, List - 532,806,405 bytes x seconds
Codecs - 5,869,214,258 bytes x seconds
Enumerator - 957,845 bytes x seconds
WAVE - 986,310,412 bytes x seconds
hsndfile - 23,160 bytes x seconds
A few notes:
I did make an HSoundFile-3 implementation that used only UArrs, not
the AudioBuffer class. This implementation was about 2 seconds faster
for this test than the shown implementation, however I didn't think
the extra efficiency was worth the more restrictive API.
Anyway, that's what I've found so far. I was very hopeful that at
least one of the pure haskell packages would be similar to hsndfile,
but unfortunately I haven't found anything at this time. It's
certainly beyond my ability.
haskell-art mailing list