On Wed, 2 Feb 2011, alex wrote:
So, why not hit reply and introduce yourself (even if you've posted
already), and reveal your interest in haskell and/or art, whatever
that may be. I'll do it too, but someone else go first :)
Ok, where to start - at the very beginning? I first tried to make sound
with the ZX Spectrum from 1986 to 1990 with assembly programming, then
with Amiga, various BASICs and assembly programming, then with a Modula-II
derivative called Cluster until around 2001:
http://www.assampler.com/
Then I tried Modula-3 on Linux. When I later got to know Haskell, I found
that I had reinvented lazy evaluation for Assampler. Consequently I moved
to the original. I wanted to integrate music composition and signal
processing. I wanted programming features for music arrangement, since the
many trackers known from Amiga did not offer much structuring and thus
required a lot of copy and paste. I wanted programming features also for
signal processing, since the interactive graph editing became cumbersome
for repetitive signal algorithms like vocoders, although I already added
some support for them to Assampler.
At a long weekend in 2004 I put together a song using Haskore and custom
signal processing functions working on lists:
http://users.informatik.uni-halle.de/~thielema/Music/winterade/winterade.mp3
Composing this piece was really crazy, since due to a bug in a SuSE
upgrade, I could not play sounds. Thus I transfered every rendered stream
to an Amiga via FTP and played it there.
Starting from this example I wrote more signal processes, tried to make
them safer, cleaner, more flexible, faster, aware of physical units,
adaptive to sample rates. I extended Haskore by Paul Hudak, wrote packages
haskore-realtime, haskore-supercollider, supercollider-midi, event-list,
midi, midi-alsa, streamed (realtime MIDI event manipulation in ALSA),
sample-frame, sox, synthesizer-core, synthesizer-dimensional,
synthesizer-alsa, synthesizer-llvm, extended and maintained storablevector
(based on Spencer Janssen's work), numeric-prelude (based on Dylan
Thurston's work), alsa-seq, alsa-pcm (based on Iavor Diatchki's work),
jack (based on Sönke Hahn's work).
Using plain lists for signal processing I got perfect integration of
signal processing and music and hours to wait for rendering results. In
contrast to that, the hsc3 package by Rohan Drape allowed me to get
realtime sound:
http://www.youtube.com/watch?v=d2JvOwS26Zg
(YouTube is great in making the audio track sound awful.
Better follow the links in the video description to the original files.)
Using storablevector, stream-fusion:Stream like signal generators, and
arrows for causal processes I got eventually realtime sound with plain
Haskell:
http://www.youtube.com/watch?v=KA6DE9jlpSY
Unfortunately this turned out to be really fragile. You can never predict
what the optimizer will do and you easily get code that is no longer
realtime. In many times the problem is as follows: If you use a function
more than one time, then GHC may no longer inline it, and with the
overhead of a real function call and lost opportunities for unboxing the
performance drops dramatically. Maybe GHC-7.0 is better in this respect, I
still have to check that.
2010 I started to use a DSL targeting LLVM JIT.
http://www.youtube.com/watch?v=GNiAqBTVa6U
http://www.youtube.com/watch?v=cuzYgJGfMfY
Performance is excellent, also due to vector unit support (SSE or
AltiVec), but every release of LLVM surprises me with things that worked
before and do not work any longer. Integration with Haskell is quite good,
but it has the usual problems, that every DSL has. In contrast to a
connection to SuperCollider or CSound, I can feed signal data from Haskell
to LLVM code and pull it back. I would be very happy to use the same code
for list based processing, storablevector based processing and LLVM based
processing by some type class framework. That's not very easy, but I
already managed to employ some computations both for pure Haskell and LLVM
processing.
I also spent a lot of time of hunting memory leaks. It is very easy to
get them when relying on lazy evaluation. It is even hard or impossible to
avoid them, even if you know, where they are. I am uncertain whether this
problem is specific to GHC or whether this is hard and unsolved in
general. I came to the suspicion that memory leaks are _the_ reason, why
the garbage collector is considered to be not ready for real-time
applications: The more memory is orphaned the more has the garbage
collector to check for liveliness. The typical memory leak works as
follows:
let (prefix, suffix) = splitAt largeNumber xs
in processA prefix ++ processB suffix
Although this can be perfectly processed in a streaming manner, sometimes
GHC does not manage to release the pointer to the beginning of prefix and
thus prefix is kept until the processing of suffix starts. I wonder
whether the problem could be solved if the garbage collector would be able
to trigger evaluations if somewhere the memory consumption grows.
My current plan is to move completely to causal arrows. I have already
written a lot of code the Arrow way, but the remaining lazy processing
still has hard to track leaks. I have to get rid of lazy evaluation
completely, which is sad, since it allows for a very elegant style of
programming.
_______________________________________________
haskell-art mailing list
haskell-art@lurk.org
http://lists.lurk.org/mailman/listinfo/haskell-art