gogins / csound-extended

Extensions for Csound including algorithmic composition, Android app, and WebAssembly.
GNU Lesser General Public License v2.1
40 stars 1 forks source link

Evaluate the use of Haskell with Csound for algorithmic composition #105

Closed gogins closed 4 years ago

gogins commented 4 years ago

Note, csound-expression is about synthesis, not composition.

Euterpea/Haskell School of Music/Kulitta is the main thing here, I think. Everything presupposes MIDI and that a MIDI synthesizer is running to accept MIDI messages from Haskell. So, is there a level of abstraction in Euterpea or elsewhere where notes, as opposed to MIDI messages, can be intercepted, translated to i statements, and sent to Csound or saved in a file?

Who else besides the impressive Donya Quick is using Haskell for real algorithmic composition?

gogins commented 4 years ago

There is a Music structure in Euterpea that gets translated to MIDI. Can these be translated to Csound scores?

Yes. The Music type has some rather severe limitations. Pitch is just MIDI note numbers as integers. Notes don't have loudnesses. These limitations are overcome to some considerable extent by layering NoteAttribute over Music and defining more sophisticated Player types. This seems to get pretty good.

So there should be a Csound-specific Player that runs either in real time, or generates strings to be sent to an instance of Csound, or writes a .csd file. See Haskell School of Music page 130.

See also https://github.com/donya/VividEuterpea/blob/master/VividEuterpea.lhs.

gogins commented 4 years ago

Failing to run GUI examples from HSoM with:

<interactive>: user error (unknown GLUT entry glutInit)
*** Exception: thread blocked indefinitely in an MVar operation

This in spite of the fact that glut works in a standalone simple GUI program.

gogins commented 4 years ago

Tried again:

Input devices: InputDeviceID 1 Midi Through Port-0

Output devices: OutputDeviceID 0 Midi Through Port-0 OutputDeviceID 2 TiMidity port 0 OutputDeviceID 3 TiMidity port 1 OutputDeviceID 4 TiMidity port 2mkg@bodhimandala:~$ ghci GHCi, version 8.6.5: http://www.haskell.org/ghc/ :? for help Prelude> import HSoM.Examples.MUIExamples2 Prelude HSoM.Examples.MUIExamples2> bifurcate Requested buffer size 32768, fragment size 8192 ALSA pcm 'default' set buffer size 32768, period size 8192 bytes Prelude HSoM.Examples.MUIExamples2> Playing time: ~34 seconds Notes cut: 0 Notes lost totally: 0

OutputDeviceID 5 TiMidity port 3

Prelude Euterpea> playDev 2 $ c 4 qn Requested buffer size 32768, fragment size 8192 ALSA pcm 'default' set buffer size 32768, period size 8192 bytes Prelude Euterpea>

Playing a note works.
- Tried running MUI examples again:

mkg@bodhimandala:~$ ghci GHCi, version 8.6.5: http://www.haskell.org/ghc/ :? for help Prelude> import HSoM.Examples.MUIExamples2 Prelude HSoM.Examples.MUIExamples2> bifurcate Requested buffer size 32768, fragment size 8192 ALSA pcm 'default' set buffer size 32768, period size 8192 bytes Prelude HSoM.Examples.MUIExamples2> Playing time: ~34 seconds Notes cut: 0 Notes lost totally: 0


That works!
gogins commented 4 years ago

To do:

gogins commented 4 years ago

The Kulitta GUI did not run out of the box.

I cloned https://github.com/donya/Jazzkell and executed:

mkg@bodhimandala:~/Jazzkell$ cabal v1-install
Resolving dependencies...
In order, the following will be installed:
Jazzkell-0.0.2 (reinstall)
Warning: Note that reinstalls are always dangerous. Continuing anyway...
Starting     Jazzkell-0.0.2
Building     Jazzkell-0.0.2
Completed    Jazzkell-0.0.2
mkg@bodhimandala:~/Jazzkell$ cd examples/
mkg@bodhimandala:~/Jazzkell/examples$ ghci
GHCi, version 8.6.5: http://www.haskell.org/ghc/  :? for help
Prelude> :load Hypnotize
[1 of 1] Compiling Hypnotize        ( Hypnotize.lhs, interpreted )
Ok, one module loaded.
*Hypnotize> playDev 2 hypnotize
Requested buffer size 32768, fragment size 8192
ALSA pcm 'default' set buffer size 32768, period size 8192 bytes

That worked.

gogins commented 4 years ago

This is going much better than prior attempts to deal with Haskell, probably because I am always using the latest versions of things.

gogins commented 4 years ago

Sheesh, 10 days now of might and main but I finally got a rudimentary Csound performance working from a single .hs file including the .csd text for Xanadu. Haskell is really different.

I need to marshal String to CString transparently, apparently they use lambdas for that to keep the CString in scope and then destroy it.

Then threading.

After that, sending a Music to a Csound performance running in another thread.

gogins commented 4 years ago

The way it currently works is to compile a CSD String, then map a toCsound lambda that encloses the Csound pointer over a performance ([MEvent]]).

Haskell is nearly as fast as C or C++, so I get these messages from csoundInputMessageInternal:

realloc 61
realloc 60
realloc 60
realloc 61
realloc 60
realloc 61
realloc 48
realloc 47
realloc 62
realloc 61

So, I wonder if my code is sending input messages to Csound faster than Csound can consume them.

If so, then either I can only do finite performances, or I must find some sort of blocking queue in Haskell to slow things down.

In addition, it's not clear that Haskell is waiting for the CSD to compile and start before sending events.

gogins commented 4 years ago

The problem seems to have been sending events to Csound before csoundStart had been called. I fixed that, and it now works, even for an infinite performance. Remaining tasks:

gogins commented 4 years ago

This is working well enough as a "beta" implementation. More refinement will come through use.