A Simple Sound Example
(updated in 17/06/2002)

Previous | Index | Next

In this short lesson you will learn how to play sound in your HOpenGL applications. Please keep two things in mind here:

  • Do not expect advanced sound features in this lesson.
  • This will work only under Win32 platforms :(

Ok, let's start. First of all, let me explain how the sound stuff works: there is a Windows system routine called PlaySound, that is used to play simple sound files (such as .wav). I'll make available to you a module, written in Haskell, that treats this routine as a foreing function, and makes it possible to call it in a Haskell program, although it was written in another language. I won't explain how this module works, because this is not a HOpenGL topic, but you can get more information about the Haskell FFI (Foreign Function Interface) here.

Ok, since you download this Sound module you'll be ready to start. We'll use only one single funciton from it, playSound, which is a binding to the original PlaySound Windows function. Its signature is:

	playSound :: String -> Win32.HMODULE -> [SoundFlag] -> IO Bool

Notice that it returns a boolean, indicating if the sound file was played successfully or not. The String corresponds to the path to the .wav file. Don't worry about the second argument: it'll always be nullAddr, because we won't use any advanced sound features, as mentioned before. By the way, you can check out the PlaySound documentation for more details if you wish.

The third argument of playSound is the most interesting: it tells how the sound will be played. Its possible values are:

data SoundFlag
  = Application
  | Alias
  | AliasId
  | Async
  | Filename
  | Loop
  | Memory
  | NoDefault
  | NoStop
  | NoWait
  | Purge
  | Resource
  | Sync

Some of these values are too advanced for we to cover here, but others are very interesting. Let's check some of them:

  • Sync - Synchronous playback of a sound event: playSound returns after the sound event completes. In other words, your HOpenGL application will stop completely (freeze) during the execution of the sound.

  • Async - The sound is played asynchronously and playSound returns immediately after beginning the sound (your HOpenGL application won't freeze and its computations will occur in parallel with the sound execution).

  • Loop - The sound plays repeatedly until playSound is called again. You must also specify the Async flag to indicate an asynchronous sound event.

  • NoStop - The specified sound event will yield to another sound event that is already playing (the sound won't interrupt another sound that is being executed).

  • NoWait - If the driver is busy, return immediately without playing the sound.

Ok, let's create some code now, since you understood some sound theory. Our application will be very simple: if the user presses the 1, 2, 3 or 4 keyboard key, the sound 1.wav, 2.wav, 3.wav or 4.wav will be played respectively. Sound 1 will be synchronous, sound 2 will be asynchronous, sound 3 will be played repeatedly (loop) and sound 4 won't interrupt any other sound that is being currently played.

The first thing to do is to import the Sound module (besides the GL and GLUT modules, of course). Then create an empty window and register the function keyboard as our keyboard callback function:

import Sound
import GLUT
import GL

main :: IO ()
main = do
	GLUT.init Nothing
	createWindow "Sound Example" (return ()) [ Single, GLUT.Rgb ]
			(Just (WindowPosition 100 100))
			(Just (WindowSize     300 250))
	keyboardFunc (Just keyboard)
	mainLoop

Now there is only one thing left: define what keyboard should do. As you may have guessed, it's a very easy job: it's just necessary to set the correct flags to the sound events:

keyboard :: KeyboardAction
keyboard '1' _ = playSound "1.wav" nullAddr [Async]      >>= \soundStatus -> print soundStatus
keyboard '2' _ = playSound "2.wav" nullAddr [Sync]       >>= \soundStatus -> print soundStatus
keyboard '3' _ = playSound "3.wav" nullAddr [Loop,Async] >>= \soundStatus -> print soundStatus
keyboard '4' _ = playSound "4.wav" nullAddr [NoStop]     >>= \soundStatus -> print soundStatus
keyboard  _  _ = return ()

Notice that the prompt shows if the sound was played successfully or not (soundStatus). Notice also that we can call playSound with more that one sound flag. For example, if you wish that sound 4 to also be played asynchronously, you would use:

keyboard '4' _ = playSound "4.wav" nullAddr [Async,NoStop] >>= \soundStatus -> print soundStatus

That's it, now we are done. I'd like to thank Monique Monteiro for helping with sound issues.



Downloads:


HOpenGL Tutorial - Andre W B Furtado
Titulo
www.cin.ufpe.br/~haskell/hopengl/sound.html
Last updated in 17/06/2002
Informatics Center (CIn) - UFPE
Recife - PE - Brazil