Re: [Haskell-cafe] ANN: Hemkay, the 100% Haskell MOD player
However, there seems to be a conflict between the nature of mixing and stream processing when it comes to efficiency. As it turns out, it's more efficient to process channels one by one within a chunk instead of producing samples one by one. It takes a lot less context switching to first generate the output of channel 1, then generate channel 2 (and simultaneously add it to the mix) and so on, than to mix sample 1 of all channels, then sample 2 etc., since we can write much tighter loops when we only deal with one channel at a time. On the other hand, stream fusion is naturally fit to generate samples one by one. It looks like the general solution requires a fusable transpose operation, otherwise we're back to hand-coding the mixer. Have you found a satisfying solution to this problem? I wonder if data-parallel haskell won't be able to help here, mod rendering is a scatter-gather style of processing, the problem is that the different channels trigger different processing. ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] ANN: Hemkay, the 100% Haskell MOD player
I see no problem. I would generate a frequency and a volume control curve for each channel and apply this to the played instrument, then I would mix it. Yes, that's basically what I do now: I flatten the song into a series of play states where for each active channel I store the pointer to the current sample, its frequency, volume and panning (none of these three parameters change within a chunk), and use that information to perform mixing. The mixing step is quite ad hoc, but at least it's simple enough, so it doesn't get out of hand. It is the strength of Haskell to separate everything into logical steps and let laziness do things simultaneously. Stream fusion can eliminate interim lists, and final conversion to storable vector using http://hackage.haskell.org/package/storablevector-streamfusion/ can eliminate lists at all. But in my understanding that elimination is only possible if lists are not used as persistent containers, only to mimic control structures. Now I rely on samples being stored as lists, so I can represent looping samples with infinite lists and not worry about the wrap-around at all. So in order to have any chance for fusion I'd have to store samples as vectors and wrap them in some kind of unfold mechanism to turn them into lists that can be potentially fused away. In other words, besides a 'good consumer', I need a 'good producer' too. However, there seems to be a conflict between the nature of mixing and stream processing when it comes to efficiency. As it turns out, it's more efficient to process channels one by one within a chunk instead of producing samples one by one. It takes a lot less context switching to first generate the output of channel 1, then generate channel 2 (and simultaneously add it to the mix) and so on, than to mix sample 1 of all channels, then sample 2 etc., since we can write much tighter loops when we only deal with one channel at a time. On the other hand, stream fusion is naturally fit to generate samples one by one. It looks like the general solution requires a fusable transpose operation, otherwise we're back to hand-coding the mixer. Have you found a satisfying solution to this problem? Gergely -- http://www.fastmail.fm - A no graphics, no pop-ups email service ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] ANN: Hemkay, the 100% Haskell MOD player
Patai Gergely schrieb: It is the strength of Haskell to separate everything into logical steps and let laziness do things simultaneously. Stream fusion can eliminate interim lists, and final conversion to storable vector using http://hackage.haskell.org/package/storablevector-streamfusion/ can eliminate lists at all. But in my understanding that elimination is only possible if lists are not used as persistent containers, only to mimic control structures. Now I rely on samples being stored as lists, so I can represent looping samples with infinite lists and not worry about the wrap-around at all. So in order to have any chance for fusion I'd have to store samples as vectors and wrap them in some kind of unfold mechanism to turn them into lists that can be potentially fused away. In other words, besides a 'good consumer', I need a 'good producer' too. Right. The conversion from storablevector to stream-fusion:Stream is such a good producer. However, there seems to be a conflict between the nature of mixing and stream processing when it comes to efficiency. As it turns out, it's more efficient to process channels one by one within a chunk instead of producing samples one by one. It takes a lot less context switching to first generate the output of channel 1, then generate channel 2 (and simultaneously add it to the mix) and so on, than to mix sample 1 of all channels, then sample 2 etc., since we can write much tighter loops when we only deal with one channel at a time. Yes, I would also do it this way. So in the end you will have some storablevectors as intermediate data structures. ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] ANN: Hemkay, the 100% Haskell MOD player
Patai Gergely schrieb: I would do resampling (with some of the Interpolation routines) and mixing in two steps, that is I would prepare (lazy) storable vectors with the resampled sounds and mix them. And is that straightforward considering the peculiarities of tracked music? After all, frequency can change between ticks thanks to portamento effects, and samples can loop or end in the middle of a tick. Do I have to trim and pad the samples manually to be able to describe these transformations? I see no problem. I would generate a frequency and a volume control curve for each channel and apply this to the played instrument, then I would mix it. It is the strength of Haskell to separate everything into logical steps and let laziness do things simultaneously. Stream fusion can eliminate interim lists, and final conversion to storable vector using http://hackage.haskell.org/package/storablevector-streamfusion/ can eliminate lists at all. ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] ANN: Hemkay, the 100% Haskell MOD player
Hello all, I did some refactoring, and separated the device independent part of Hemkay into a package of its own, hemkay-core [1]. This is a library that provides facilities to load MOD music and render it in various ways, including a direct-to-buffer option that's considerably more efficient than creating a list of samples. You can use it to create a MOD player with any sound-making API or even write the mixer output into a wav file. The hemkay package [2] is now reduced to an example that shows how to use the core library with PortAudio. It is also considerably faster than the previous version, because now I push as many samples as possible at a time without blocking. I had no luck with Mietek's callback interface so far, because it keeps randomly segfaulting on me, but when it works, it's about four times as fast as the current player. You are encouraged to try adapting it (using mixToBuffer) to other systems. Gergely [1] http://hackage.haskell.org/package/hemkay-core [2] http://hackage.haskell.org/package/hemkay -- http://www.fastmail.fm - Choose from over 50 domains or use your own ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] ANN: Hemkay, the 100% Haskell MOD player
Patai Gergely schrieb: I have a function for mixing sounds at different (relative) start times. I feel that it does not get maximum speed in GHC, but is still ready for realtime application. http://hackage.haskell.org/packages/archive/synthesizer-core/0.2.1/doc/html/Synthesizer-Storable-Cut.html#v%3Aarrange And how can you mix that with changing frequencies (effectively resampling on the fly)? I would do resampling (with some of the Interpolation routines) and mixing in two steps, that is I would prepare (lazy) storable vectors with the resampled sounds and mix them. Since Haskell is lazy, this is still somehow on the fly, although one could still wish to eliminate the interim storable vectors. ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] ANN: Hemkay, the 100% Haskell MOD player
I would do resampling (with some of the Interpolation routines) and mixing in two steps, that is I would prepare (lazy) storable vectors with the resampled sounds and mix them. Since Haskell is lazy, this is still somehow on the fly, although one could still wish to eliminate the interim storable vectors. You could use stream fusion, although you will need to adapt that for the interpolation, but it should work. ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] ANN: Hemkay, the 100% Haskell MOD player
I would do resampling (with some of the Interpolation routines) and mixing in two steps, that is I would prepare (lazy) storable vectors with the resampled sounds and mix them. And is that straightforward considering the peculiarities of tracked music? After all, frequency can change between ticks thanks to portamento effects, and samples can loop or end in the middle of a tick. Do I have to trim and pad the samples manually to be able to describe these transformations? -- http://www.fastmail.fm - The way an email service should be ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] ANN: Hemkay, the 100% Haskell MOD player
Hello again, Your message has motivated me to publish my own PortAudio binding, which provides a simpler, more efficient callback-based interface: http://github.com/mietek/portaudio I tried this, and after rewriting the code a bit, I managed to decrease CPU load by 70-80%, which is not bad for starters. However, I'm getting random segfaults, and I've no idea why. Here's the modified player module (hpaste kindly rearranged the empty lines for some reason): http://hpaste.org/fastcgi/hpaste.fcgi/view?id=14347 Instead of building a list of samples right away, I use an unfoldr-style generator to fill the buffer, whose initial state is created by mixGenerator, and its stepper function is nextSample. In order to get this working, I renamed your module to avoid conflict with the other PortAudio binding, and I had to change the dependencies in the cabal file to base = 4 5 because of the exception handling code. There's also some broken MVar-based code to handle the end of the song, you can ignore that for the time being. Gergely -- http://www.fastmail.fm - The professional email service ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] ANN: Hemkay, the 100% Haskell MOD player
On Mon, 14 Dec 2009, Patai Gergely wrote: Hello all, I just uploaded the fruit of a little side project. Hemkay [1] is an oldschool module music [2] player that performs all the hard work in Haskell. Cool. The most complicated I tried was to import OctaMED printout to Haskore: http://darcs.haskell.org/haskore/src/Haskore/Interface/MED/Text.hs http://hackage.haskell.org/packages/archive/haskore/0.1/doc/html/Haskore-Interface-MED-Text.html Still, I'd be curious to see how the overall quality of the code could be improved. In particular, retrieving and updating record fields is somewhat inconvenient. Also, the actual mixing (limited to the mixChunk function) is embarrassingly slow, and I wonder how much it could be improved without leaving the pure world. I have a function for mixing sounds at different (relative) start times. I feel that it does not get maximum speed in GHC, but is still ready for realtime application. http://hackage.haskell.org/packages/archive/synthesizer-core/0.2.1/doc/html/Synthesizer-Storable-Cut.html#v%3Aarrange ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] ANN: Hemkay, the 100% Haskell MOD player
The most complicated I tried was to import OctaMED printout to Haskore: http://darcs.haskell.org/haskore/src/Haskore/Interface/MED/Text.hs http://hackage.haskell.org/packages/archive/haskore/0.1/doc/html/Haskore-Interface-MED-Text.html Oh, I'll have to try that too! I have a function for mixing sounds at different (relative) start times. I feel that it does not get maximum speed in GHC, but is still ready for realtime application. http://hackage.haskell.org/packages/archive/synthesizer-core/0.2.1/doc/html/Synthesizer-Storable-Cut.html#v%3Aarrange And how can you mix that with changing frequencies (effectively resampling on the fly)? By the way, the latest code I have in my hands typically uses 2-3% CPU time on my machine (Core 2 Duo at 2 GHz), which is okay for now, but I think going at least ten times as fast is a realistic goal. Gergely -- http://www.fastmail.fm - Accessible with your email software or over the web ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] ANN: Hemkay, the 100% Haskell MOD player
Patai, I asked a similar question about a week ago, inquiring about efficient buffers for audio. I got a good response: http://thread.gmane.org/gmane.comp.lang.haskell.cafe/67258/focus=67293 Well, the general lesson seems to be that using stream fusion or something analogous is the way to go. In this particular case I don't need any fancy processing, and I have no feedback loops. All I do in my mixing routine is to calculate a weighted (volume+panning) sum of samples and stretch them in time as dictated by their frequency. The song is flattened into a series of play states first, so all the information required for mixing is readily available. I could try rewriting the mixer to return an iterative generator function instead, which could be passed to an appropriate sound interface. The latter would have to be written once and for all. Gergely -- http://www.fastmail.fm - Does exactly what it says on the tin ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] ANN: Hemkay, the 100% Haskell MOD player
Hi Patai, Your message has motivated me to publish my own PortAudio binding, which provides a simpler, more efficient callback-based interface: http://github.com/mietek/portaudio The documentation is incomplete, but along with the example program, it should be enough to get you going: http://github.com/mietek/portaudio/blob/master/examples/Play.hs Best regards, -- Mietek Bąk ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] ANN: Hemkay, the 100% Haskell MOD player
The documentation is incomplete, but along with the example program, it should be enough to get you going: http://github.com/mietek/portaudio/blob/master/examples/Play.hs Yes, it looks like something worth giving a go. I'll try it soon, thanks. Gergely -- http://www.fastmail.fm - The way an email service should be ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] ANN: Hemkay, the 100% Haskell MOD player
Hello all, I just uploaded the fruit of a little side project. Hemkay [1] is an oldschool module music [2] player that performs all the hard work in Haskell. If there was any goal, it was to express the transformation from the song structure to the output of the mixer as a series of function compositions, maintaining a style that one might call idiomatic Haskell. Considering the dirtiness of the format in question, I'm quite pleased with the initial version. Still, I'd be curious to see how the overall quality of the code could be improved. In particular, retrieving and updating record fields is somewhat inconvenient. Also, the actual mixing (limited to the mixChunk function) is embarrassingly slow, and I wonder how much it could be improved without leaving the pure world. The program uses Portaudio for playback, but that might easily change in the future. The problem is that I couldn't get sound to work smoothly when producing samples in batches, so I'm sending them off one by one (!) at the moment, which doesn't help with performance either. I'm open to suggestions as to what library to use to push data to the sound card. Gergely [1] http://hackage.haskell.org/package/hemkay [2] http://en.wikipedia.org/wiki/MOD_(file_format) -- http://www.fastmail.fm - Or how I learned to stop worrying and love email again ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] ANN: Hemkay, the 100% Haskell MOD player
Nice work! Did you try the OpenAL binding? Not sure if that works. 2009/12/14 Patai Gergely patai_gerg...@fastmail.fm: Hello all, I just uploaded the fruit of a little side project. Hemkay [1] is an oldschool module music [2] player that performs all the hard work in Haskell. If there was any goal, it was to express the transformation from the song structure to the output of the mixer as a series of function compositions, maintaining a style that one might call idiomatic Haskell. Considering the dirtiness of the format in question, I'm quite pleased with the initial version. Still, I'd be curious to see how the overall quality of the code could be improved. In particular, retrieving and updating record fields is somewhat inconvenient. Also, the actual mixing (limited to the mixChunk function) is embarrassingly slow, and I wonder how much it could be improved without leaving the pure world. The program uses Portaudio for playback, but that might easily change in the future. The problem is that I couldn't get sound to work smoothly when producing samples in batches, so I'm sending them off one by one (!) at the moment, which doesn't help with performance either. I'm open to suggestions as to what library to use to push data to the sound card. Gergely [1] http://hackage.haskell.org/package/hemkay [2] http://en.wikipedia.org/wiki/MOD_(file_format) -- http://www.fastmail.fm - Or how I learned to stop worrying and love email again ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] ANN: Hemkay, the 100% Haskell MOD player
Nice work! Thanks! :) Did you try the OpenAL binding? Not sure if that works. No, I haven't really looked hard, to be honest. Portaudio looked simple enough, so I picked it. But it could be anything, since the mixing is done on my side anyway, and all I need is a way to push (preferably Float) samples to the sound unit. I'm sure there are several viable options, so the deciding factor is rather portability, and secondly ease of use. -- http://www.fastmail.fm - The professional email service ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] ANN: Hemkay, the 100% Haskell MOD player
The more of these I see, the more guilt I feel over the condition of the portaudio module. There's a good chance that the performance issue you're seeing is a bad implementation of the portaudio library. Do you have any specific problems you ran into with portaudio? I'd love to revisit at some point... Well, when I tried to push whole blocks instead of individual samples, it seemed to miss some of these writes. It might do the same even now, but it wouldn't be noticeable with a sample missing here and there anyway. The interface for writeStream is not very fortunate. It forces me to pack samples into lists no matter what, even if the interleaved list is actually easier to produce. So it should probably provide alternative interfaces for interleaved lists (which would actually be possible right away if writeStream didn't ignore its third argument altogether), and maybe an array interface as well. I don't know if the callback interface works better, maybe that's also worth a shot. Ultimately, it would be probably best if it gave the programmer a higher abstraction, where it is passed a potentially infinite list of samples, and takes care of all the buffering duties. Also, this could be made to play nice with stream fusion in order to get the maximum performance out of it. Gergely -- http://www.fastmail.fm - One of many happy users: http://www.fastmail.fm/docs/quotes.html ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] ANN: Hemkay, the 100% Haskell MOD player
It's been well over a year. I'll see what I can do when I get some free(er) time. 2009/12/14 Patai Gergely patai_gerg...@fastmail.fm The more of these I see, the more guilt I feel over the condition of the portaudio module. There's a good chance that the performance issue you're seeing is a bad implementation of the portaudio library. Do you have any specific problems you ran into with portaudio? I'd love to revisit at some point... Well, when I tried to push whole blocks instead of individual samples, it seemed to miss some of these writes. It might do the same even now, but it wouldn't be noticeable with a sample missing here and there anyway. The interface for writeStream is not very fortunate. It forces me to pack samples into lists no matter what, even if the interleaved list is actually easier to produce. So it should probably provide alternative interfaces for interleaved lists (which would actually be possible right away if writeStream didn't ignore its third argument altogether), and maybe an array interface as well. I don't know if the callback interface works better, maybe that's also worth a shot. Ultimately, it would be probably best if it gave the programmer a higher abstraction, where it is passed a potentially infinite list of samples, and takes care of all the buffering duties. Also, this could be made to play nice with stream fusion in order to get the maximum performance out of it. Gergely -- http://www.fastmail.fm - One of many happy users: http://www.fastmail.fm/docs/quotes.html ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] ANN: Hemkay, the 100% Haskell MOD player
--- On Mon, 12/14/09, M Xyz functionallyharmoni...@yahoo.com wrote: From: M Xyz functionallyharmoni...@yahoo.com Subject: Re: [Haskell-cafe] ANN: Hemkay, the 100% Haskell MOD player To: Patai Gergely patai_gerg...@fastmail.fm Date: Monday, December 14, 2009, 5:50 PM --- On Mon, 12/14/09, Patai Gergely patai_gerg...@fastmail.fm wrote: Also, the actual mixing (limited to the mixChunk function) is embarrassingly slow, and I wonder how much it could be improved without leaving the pure world. The program uses Portaudio for playback... Patai, I asked a similar question about a week ago, inquiring about efficient buffers for audio. I got a good response: http://thread.gmane.org/gmane.comp.lang.haskell.cafe/67258/focus=67293 Working with immutable trees instead of arrays still freaks me out, but page 289 of RWH actually made me feel a little better about it. :) ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe