Start Euterpea Music in main function together with gameworld? (in Haskell)

226 views Asked by At

Making a game and looking for a way to start the music (made in haskell with Euterpea) when calling the main function to start the game.

The problem with this code is that it will play the music but then it won't start the game. If I put the music after Pure.Game.play the game starts but no music will be played.

main :: IO ()
main = do
backgroundImage <- background
 let backgrounds = [backgroundImage]
**Euterpea.play $ Euterpea.line [af 4 dqn :=: cf 4 dqn :=: ef 4 dqn]**
Graphics.Gloss.Interface.Pure.Game.play (InWindow "game" (windowwidth, 
windowheight) (0,0)) cyan 300 (drawGame background) inputHandler step

Also tried to have the music in its own function melody :: Music Pitch melody = Euterpea.line [af 4 dqn :=: cf 4 dqn]

and bind it (like with the background): music <- melody and call it in main, but can't get that to work either.

Any tip how to do this?

2

There are 2 answers

0
Daniel Wagner On

I don't have gloss and euterpea installed, so I can't test, but I'm willing to bet that forking one or the other into its own thread will be sufficient. I'd recommend forking euterpea off; graphics libraries sometimes use thread-local state which complicates the matters in sort of boring ways. So:

import Control.Concurrent
main = do
    backgroundImage <- background
    forkIO $ Euterpea.play (Euterpea.line [af 4 dqn :=: cf 4 dqn :=: ef 4 dqn])
    Gloss.play (InWindow "game" (windowwidth, windowheight) (0,0)) cyan 300 (drawGame [background]) inputHandler step

It's possible that one or the other uses FFI calls in an interesting way, so if the above code snippet isn't enough to get things off the ground, consider compiling with -threaded to ensure that FFI calls don't block each other. (N.B. -threaded is not required for using Haskell threads; a full discussion of its effects is longer than would be reasonable to go into here, but there is an excellent paper with more details.)

0
Niko On

I have neither installed gloss nor euterpea but I guess the IO actions described by both of the play functions run infinitely, so whichever is called first will execute and the other one will wait forever without beeing executed.

In order to fix this, you can start the music in a seperate thread using forkIO from Control.Concurrent:

musicThreadId <- forkIO $ Euterpea.play $ Euterpea.line [af 4 dqn :=: cf 4 dqn :=: ef 4 dqn]

and kill the thread after your game is closed:

killThread musicThreadId

This works as long as your music is independet of the game. If you want to include sound that is dependent on whats happening in the game (for example a sound effects), you will need to have your game communicate thread-safe with a thread which plays the sounds. Communication between threads is a side effect which has to be handled using IO. In this case you should take a look at playIO from Graphics.Gloss.Interface.IO.Game, as play only supports game logic without side effects.

You can read more about concurrent programming in Haskell on hackage: http://hackage.haskell.org/package/base-4.12.0.0/docs/Control-Concurrent.html

or in the book Parallel and Concurrent Programming in Haskell by Simon Marlow which is available online: https://www.oreilly.com/library/view/parallel-and-concurrent/9781449335939/