Re: [haskell-art] Haskell audio I/O packages

2008-12-10 Thread John Lato
On Tue, Dec 9, 2008 at 9:15 PM, Henning Thielemann
<[EMAIL PROTECTED]> wrote:
>
>
> I think the problem is that readFile style routines are not modular
> enough. We need open/getContent/close style routines and sadly,
> composition of them is error-prone. You can easily close a file and try to
> read data from it afterwards. Maybe continuation style functions (like
> withCString) are better, but it is still possible to access closed files.
> It would be very nice if these problems could be solved, since the interim
> lazy processing is very elegant, and also useful for feedback.

This is one of the things I really like about Iteratees.  They are
composable.  I originally tried to use a withFile function, but
composing those, so that one process will read from multiple files, is
not really a modular process.
___
haskell-art mailing list
haskell-art@lurk.org
http://lists.lurk.org/mailman/listinfo/haskell-art


Re: [haskell-art] Haskell audio I/O packages

2008-12-09 Thread Henning Thielemann

On Tue, 9 Dec 2008, John Lato wrote:

> On Tue, Dec 9, 2008 at 4:58 PM, Henning Thielemann
> <[EMAIL PROTECTED]> wrote:
>>
>> What are the reasons, you do not like lazy IO? Yes, currently it's a hack
>> using unsafeInterleaveIO. But I hope someday one can hide this safely in a
>> nice monad. But in general I find lazy stream processing a very elegant
>> way of programming. Why else should we use Haskell and not, say OCaml?
>
> The main reason I don't like lazy IO is that you need to force
> evaluation while the handle is open, or leave handles open.

I think the problem is that readFile style routines are not modular 
enough. We need open/getContent/close style routines and sadly, 
composition of them is error-prone. You can easily close a file and try to 
read data from it afterwards. Maybe continuation style functions (like 
withCString) are better, but it is still possible to access closed files. 
It would be very nice if these problems could be solved, since the interim 
lazy processing is very elegant, and also useful for feedback.
___
haskell-art mailing list
haskell-art@lurk.org
http://lists.lurk.org/mailman/listinfo/haskell-art


Re: [haskell-art] Haskell audio I/O packages

2008-12-09 Thread stefan kersten
Henning Thielemann wrote:
> What are the reasons, you do not like lazy IO?

i guess the biggest issue is when it is safe to release resources. i
think a safe paradigm would abstract timely allocation and release of
resources; at which point you need to "consume" your whole lazy stream
of data and arrive at an abstraction similar to streams + foldl' or
iteratees. oleg's paper lists more benefits like improved performance
and robustness wrt deadlocks, but i haven't really checked it out yet.

> But in general I find lazy stream processing a very elegant 
> way of programming.

recently i've been working with a framework closer to the notion of
stream processors rather than working on streams themselves, with the
main motivation of being able to use the same algorithms in a non
realtime setting (soundfile IO) and in a realtime, callback-based
framework (e.g. coreaudio or jack). i'm not sure how lazy streams would
be used in such an environment without additional buffering, are you
doing this in your synthesizer package?

> Why else should we use Haskell and not, say OCaml?

because there are far more good uses for lazyness than "just" stream
processing ... plus, haskell is way cooler than ocaml ;)


___
haskell-art mailing list
haskell-art@lurk.org
http://lists.lurk.org/mailman/listinfo/haskell-art


Re: [haskell-art] Haskell audio I/O packages

2008-12-09 Thread John Lato
On Tue, Dec 9, 2008 at 4:58 PM, Henning Thielemann
<[EMAIL PROTECTED]> wrote:
>
> What are the reasons, you do not like lazy IO? Yes, currently it's a hack
> using unsafeInterleaveIO. But I hope someday one can hide this safely in a
> nice monad. But in general I find lazy stream processing a very elegant
> way of programming. Why else should we use Haskell and not, say OCaml?

The main reason I don't like lazy IO is that you need to force
evaluation while the handle is open, or leave handles open.  Both are
non-optimal, although using Control.Parallel.Strategies makes the
former at least palatable to me.  Unfortunately I don't like most
strict IO solutions I've seen either, although for different reasons.

I do agree that lazy stream processing is very elegant.  It just
sometimes intersects with the real world badly (e.g. half-closed
handles, handles which are still open because they were never read to
EOF, etc.).

At the moment I'm trying to find the ideal of good performance, stream
processing, and real-world concerns removed.  It seems to me that the
Iteratee idea is worth exploring, as it may turn out to be fruitful.
And, it just feels right.  So I'm willing to spend some time on it.

John
___
haskell-art mailing list
haskell-art@lurk.org
http://lists.lurk.org/mailman/listinfo/haskell-art


Re: [haskell-art] Haskell audio I/O packages

2008-12-09 Thread John Lato
On Tue, Dec 9, 2008 at 4:45 PM, stefan kersten <[EMAIL PROTECTED]> wrote:
> hi john,
>
> John Lato wrote:
>> Using unsafeFreezeIOCArray and my stream implementation provides the
>> fastest version yet, with an average of about 1.9s per run.  This is
>> in the hsndfile.hs test code as function "test1".
>>
>> For the record, the stream implementation and fold I'm using are
>> copied from Data.ByteString.Lazy.  I changed the types to suit this
>> code, but that's the source.
>
> thanks for posting the code. i'm not very convinced of lazy IO, but i'd
> be very interested in incorporating an iteratee based approach into
> hsndfile. i'm currently finalizing various api changes and extensions
> (mostly to do with abstracting both mutable and immutable buffers) and
> when i'm done i'll have a look at what you did in hsoundfile-3.
> obviously oleg's iteratee code is not hackaged yet, and i couldn't find
> it anywhere else, do you have any pointers?
>
> thanks,
> 
>

Hi Stefan,

It's available at
http://okmij.org/ftp/Haskell/Iteratee/

His DEFUN slides and notes, found at
http://okmij.org/ftp/Streams.html#iteratee, are also helpful.

I think it's pretty obvious how to apply this to hsndfile.  However, I
just noticed that Oleg has posted a TIFF library using an Iteratee
approach.  It supports seeking/random access and various other things
that directly apply to the matter at hand.  Looks like I'll have to do
some studying again...
http://okmij.org/ftp/Streams.html#random-bin-IO
___
haskell-art mailing list
haskell-art@lurk.org
http://lists.lurk.org/mailman/listinfo/haskell-art


Re: [haskell-art] Haskell audio I/O packages

2008-12-09 Thread Henning Thielemann

On Tue, 9 Dec 2008, stefan kersten wrote:

> thanks for posting the code. i'm not very convinced of lazy IO, but i'd
> be very interested in incorporating an iteratee based approach into
> hsndfile.

What are the reasons, you do not like lazy IO? Yes, currently it's a hack 
using unsafeInterleaveIO. But I hope someday one can hide this safely in a 
nice monad. But in general I find lazy stream processing a very elegant 
way of programming. Why else should we use Haskell and not, say OCaml?
___
haskell-art mailing list
haskell-art@lurk.org
http://lists.lurk.org/mailman/listinfo/haskell-art


Re: [haskell-art] Haskell audio I/O packages

2008-12-09 Thread stefan kersten
hi john,

John Lato wrote:
> Using unsafeFreezeIOCArray and my stream implementation provides the
> fastest version yet, with an average of about 1.9s per run.  This is
> in the hsndfile.hs test code as function "test1".
> 
> For the record, the stream implementation and fold I'm using are
> copied from Data.ByteString.Lazy.  I changed the types to suit this
> code, but that's the source.

thanks for posting the code. i'm not very convinced of lazy IO, but i'd
be very interested in incorporating an iteratee based approach into
hsndfile. i'm currently finalizing various api changes and extensions
(mostly to do with abstracting both mutable and immutable buffers) and
when i'm done i'll have a look at what you did in hsoundfile-3.
obviously oleg's iteratee code is not hackaged yet, and i couldn't find
it anywhere else, do you have any pointers?

thanks,


___
haskell-art mailing list
haskell-art@lurk.org
http://lists.lurk.org/mailman/listinfo/haskell-art


Re: [haskell-art] Haskell audio I/O packages

2008-12-08 Thread John Lato
> On Fri, Dec 5, 2008 at 7:04 PM, stefan kersten <[EMAIL PROTECTED]> wrote:
>
 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.
>>
>> for CArray i've been using unsafeFreezeIOCArray, which does an O(1)
>> conversion (simply keeping the pointer to the mutable array).
>

Using unsafeFreezeIOCArray and my stream implementation provides the
fastest version yet, with an average of about 1.9s per run.  This is
in the hsndfile.hs test code as function "test1".

For the record, the stream implementation and fold I'm using are
copied from Data.ByteString.Lazy.  I changed the types to suit this
code, but that's the source.

I've posted all test code I was using to
https://webspace.utexas.edu/latojw/haskell-audio/

All test files were compiled with ghc 6.10.1, with -O2 --make

HSoundFile-3 isn't entirely published yet, but for the "fromList"
function I'm using StorableVector.pack.  I'm open to suggestions.
I've thrown a tarball into the same directory, but it is currently
incomplete.

I was using:
HCodecs-0.0.3
WAVE-0.1.1
binary-0.4.4
bytestring-0.9.1.4
carray-0.1.2
hsndfile-0.3.3
storablevector-0.2.1
uvector-0.1.0.3

Cheers,
John
___
haskell-art mailing list
haskell-art@lurk.org
http://lists.lurk.org/mailman/listinfo/haskell-art


Re: [haskell-art] Haskell audio I/O packages

2008-12-08 Thread Henning Thielemann

On Mon, 8 Dec 2008, John Lato wrote:

> On Fri, Dec 5, 2008 at 4:36 PM, Henning Thielemann
> <[EMAIL PROTECTED]> wrote:
>>
>>> Data Types:
>>> HSoundFile-3 -  custom AudioBuffer class.  Implementations are
>>> provided for UArr Double, List Double, and StorableVector Double
>>
>> StorableVector or StorableVector.Lazy? The latter seems appropriate here.
>
> I think that StorableVector is the correct choice.  AudioBuffers are
> meant to be read/written strictly, in small chunks.  I could certainly
> try StorableVector.Lazy.  It may in fact be more performance.

StorableVector.Lazy uses chunks. It won't be faster but will require less 
memory for stream operations.
___
haskell-art mailing list
haskell-art@lurk.org
http://lists.lurk.org/mailman/listinfo/haskell-art


Re: [haskell-art] Haskell audio I/O packages

2008-12-08 Thread Henning Thielemann

On Mon, 8 Dec 2008, John Lato wrote:

> My suspicion is that signal processing should be done with a Stream
> type, something like the following:
>
> data Stream a = Stream a | EOF | Err String
>
> using some sort of buffer (Arrays, StorableVector, etc.).  The real
> problem is that this leads to notably lower-level code than one would
> like to write.  I've been toying with ways around this problem
> recently, but so far have nothing concrete.

You do not need to bake the (Err String) into the Stream type. You can 
just use (Maybe ErrorString, StorableVector.Lazy), where the ErrorString 
is computed only after completely evaluating StorableVector.Lazy. In case 
of Nothing, the stream could be computed without errors, and in case of 
Just e the stream is truncated for the reason of 'e'. The pair (Maybe 
ErrorString, a) can also be turned into a specific type and this is what I 
have implemented as asynchronous exceptions:
   
http://hackage.haskell.org/packages/archive/explicit-exception/0.0.1/doc/html/Control-Monad-Exception-Asynchronous.html

___
haskell-art mailing list
haskell-art@lurk.org
http://lists.lurk.org/mailman/listinfo/haskell-art


Re: [haskell-art] Haskell audio I/O packages

2008-12-08 Thread John Lato
On Fri, Dec 5, 2008 at 4:36 PM, Henning Thielemann
<[EMAIL PROTECTED]> wrote:
>
>>
>> Data Types:
>> HSoundFile-3 -  custom AudioBuffer class.  Implementations are
>> provided for UArr Double, List Double, and StorableVector Double
>
> StorableVector or StorableVector.Lazy? The latter seems appropriate here.

I think that StorableVector is the correct choice.  AudioBuffers are
meant to be read/written strictly, in small chunks.  I could certainly
try StorableVector.Lazy.  It may in fact be more performance.

>
>> Codecs - real 1m20s
>> Enumerator  - real 19s
>> WAVE- real 29.1s
>> hsndfile - real 2.0s
>
> You said, hsndfile can run with IOCArray and StorableVector. Which one did
> you use here?

IOCArray.  I didn't test hsndfile with StorableVector.  I would expect
performance to be comparable here.

>
> I'm also still not satisfied with the signal processing speed I achieve
> currently, although it increased dramatically using StorableVector instead
> of [Double]. Your simple task is certainly a good testcase to track down
> the problems.

My suspicion is that signal processing should be done with a Stream
type, something like the following:

data Stream a = Stream a | EOF | Err String

using some sort of buffer (Arrays, StorableVector, etc.).  The real
problem is that this leads to notably lower-level code than one would
like to write.  I've been toying with ways around this problem
recently, but so far have nothing concrete.
___
haskell-art mailing list
haskell-art@lurk.org
http://lists.lurk.org/mailman/listinfo/haskell-art


Re: [haskell-art] Haskell audio I/O packages

2008-12-08 Thread John Lato
On Fri, Dec 5, 2008 at 7:04 PM, stefan kersten <[EMAIL PROTECTED]> wrote:
> Henning Thielemann wrote:
>> Thank you for this benchmark! I'm particularly interested in
>> StorableVector because I hacked it quite a bit and use it for my own
>> signal processing.
>>
>> I would also like to know how Fusion+Inlining improves the picture, but I
>> do not know if there is anything to fuse at all in this simple example.
>> Can you show us the actual test you run? I would then compare with my
>> fusing signal data type from the synthesizer package.
>
> yes, thanks john, very interesting ... i'd also be interested in the
> benchmarking code ;)

I'll try to post the code tonight after work.  I'll also post the
library versions I tested, which in all cases were the latest
available on Hackage at the time I ran my tests.  One thing to keep in
mind is that, in general, the benchmarking code is pretty simple.  I
was trying to approach this from the point of view of an average user
(e.g. me), who may not be familiar with all the Haskell optimization
strategies.

I doubt there's anything to fuse in this example, but I don't know
much about fusion implementations.  I have tried inlining certain
functions, but either there was no change (which was my expectation),
or a slight slowdown.

>
>>> 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.
>
> for CArray i've been using unsafeFreezeIOCArray, which does an O(1)
> conversion (simply keeping the pointer to the mutable array).

Thanks, I'll try that.

>
>>> 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.
>>>
>>> Timing results:
>>> HSoundFile-3, StorableVector - real 16.5s
>>> HSoundFile-3, UArr- real 15.7s
>>> HSoundFile-3, List  - real 17.6s
>>
>> Is this the plain Prelude [] type? Why are List and StorableVector similar
>> in speed?

Yes, it's the plain Prelude [] type.  I expect they're similar in
speed because of the particular implementation of AudioBuffer in
HSoundFile-3.  Specifically, the AudioBuffer class has a fromList
function which is used to create the buffer from a Data.Binary decode
operation.  The UArr API is somewhat optimized for this use case,
however I believe it's relatively inefficient for StorableVector.  If
someone more familiar with StorableVector were to write the
AudioBuffer instance, I think it would perform better.

Adding specialized instances for AudioBuffer UArr and AudioBuffer
StorableVector would likely produce a significant gain; about 10% for
UArr and probably less for StorableVector.

Changing the AudioBuffer class to avoid the intermediate List is
trickier.  Last time I tried, I don't believe I found a successful
approach.

Given my results, my next approach will be an Enumerator API that uses
hsndfile to actually read from the file.  I think that would have
nearly all the performance of hsndfile with a functional, composable
interface.
___
haskell-art mailing list
haskell-art@lurk.org
http://lists.lurk.org/mailman/listinfo/haskell-art


Re: [haskell-art] Haskell audio I/O packages

2008-12-05 Thread stefan kersten
Henning Thielemann wrote:
> Thank you for this benchmark! I'm particularly interested in 
> StorableVector because I hacked it quite a bit and use it for my own 
> signal processing.
> 
> I would also like to know how Fusion+Inlining improves the picture, but I 
> do not know if there is anything to fuse at all in this simple example. 
> Can you show us the actual test you run? I would then compare with my 
> fusing signal data type from the synthesizer package.

yes, thanks john, very interesting ... i'd also be interested in the
benchmarking code ;)

>> 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.

for CArray i've been using unsafeFreezeIOCArray, which does an O(1)
conversion (simply keeping the pointer to the mutable array).

>> 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.
>>
>> Timing results:
>> HSoundFile-3, StorableVector - real 16.5s
>> HSoundFile-3, UArr- real 15.7s
>> HSoundFile-3, List  - real 17.6s
> 
> Is this the plain Prelude [] type? Why are List and StorableVector similar 
> in speed?

i'm curious about that one too ...


___
haskell-art mailing list
haskell-art@lurk.org
http://lists.lurk.org/mailman/listinfo/haskell-art


Re: [haskell-art] Haskell audio I/O packages

2008-12-05 Thread Henning Thielemann

On Thu, 4 Dec 2008, John Lato wrote:

> 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.

Thank you for this benchmark! I'm particularly interested in 
StorableVector because I hacked it quite a bit and use it for my own 
signal processing.

I would also like to know how Fusion+Inlining improves the picture, but I 
do not know if there is anything to fuse at all in this simple example. 
Can you show us the actual test you run? I would then compare with my 
fusing signal data type from the synthesizer package.


> Test harnesses:
> 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.
>
> API type:
> 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
>
> Data Types:
> HSoundFile-3 -  custom AudioBuffer class.  Implementations are
> provided for UArr Double, List Double, and StorableVector Double

StorableVector or StorableVector.Lazy? The latter seems appropriate here.

> 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.
>
> Timing results:
> HSoundFile-3, StorableVector - real 16.5s
> HSoundFile-3, UArr- real 15.7s
> HSoundFile-3, List  - real 17.6s

Is this the plain Prelude [] type? Why are List and StorableVector similar 
in speed?

> Codecs - real 1m20s
> Enumerator  - real 19s
> WAVE- real 29.1s
> hsndfile - real 2.0s

You said, hsndfile can run with IOCArray and StorableVector. Which one did 
you use here?


> 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.

I'm also still not satisfied with the signal processing speed I achieve 
currently, although it increased dramatically using StorableVector instead 
of [Double]. Your simple task is certainly a good testcase to track down 
the problems.
___
haskell-art mailing list
haskell-art@lurk.org
http://lists.lurk.org/mailman/listinfo/haskell-art


[haskell-art] Haskell audio I/O packages

2008-12-03 Thread John Lato
Hello,

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:

Codecs
hsndfile
WAVE

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
Enumerator.

All packages are haskell only, except hsndfile, which is a binding to
the C libsndfile library.

Test harnesses:
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.

API type:
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

Data Types:
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.

Timing results:
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.

Cheers,
John
___
haskell-art mailing list
haskell-art@lurk.org
http://lists.lurk.org/mailman/listinfo/haskell-art