Tutorial: Sound

This tutorial tells you how to play audio files Quorum

An Introduction to Playing Sounds in Quorum

This tutorial will explain how to add and manipulate sounds by using the Quorum Audio library. In order to access the Audio library, we must include the use Libraries.Sound.Audio statement at the beginning of our class definition. The Quorum Audio library supports files with either .wav or .ogg extensions.In order to use sounds, we first need to place a copy of the sound file into our project. It does not matter where in our project we place it, but for this example, we make a new folder in the main project folder called "Sounds." It is placed next to the folder labeled "Source Code."This looks like so:

This is an image of the Audio Project WindowTo make things easier to get started in this tutorial, a project containing many different sounds has been provided in this zip file.

Getting Started: Playing Sounds

In order to play a sound in our program, we need to instantiate an Audio object and then load an audio file into it in the same way we loaded an image into a Drawable previously. Once we have created an Audio object, we set the audio file for the object by calling the Load action. There are two ways we can set the file for an Audio object. The easiest way is just to call Load with a file path in quotes (shown below for the object audio1 ). The other common way is to create a Quorum File object, set the path of that object to the audio file, and then pass the File object to the Audio object to the Load action (shown below for the object audio2 ). Both of these methods are correct.

// these statements tell Quorum we will be using the Audio and File libraries
use Libraries.Sound.Audio
use Libraries.System.File// these statements demonstrate how to load a file into an Audio object by
// passing the path of the audio file as text to the Load action
Audio audio1
audio1:Load("Sounds/Bing.ogg")// these statements demonstrate how to set the file of an Audio object by
// creating a File object, setting the path of that File object, and passing the
// File object to the Audio object's Load action.
Audio audio2
File file
file:SetPath("Sounds/Boom.ogg")
audio2:Load(file)

Once we have our Audio object created and loaded, we can play our Audio object by calling the Play action. It is important that the Audio object we want to play has a file loaded to it; otherwise our program will not work.

// this statement demonstrates how to play an Audio object.
audio1:Play()

You may have noticed that when we have a sound that lasts longer than a few seconds, our program ended before the sound finished playing. If we haven't prevented the program from finishing before our sound is done playing, our program will end before the sound is done and it will cut off. A simple remedy for this is to use the PlayUntilDone action to play our Audio object instead of using the regular Play action. Be aware that any statements after the call to PlayUntilDone will not execute until our sound is done playing. An example using the PlayUntilDone action is below.

// this statement demonstrates how to play an Audio object until it is done
audio2:PlayUntilDone()

We may also want to control playback of our sound during our program. Audio objects have actions that allow us to Pause, Resume, and Stop our sound. We can also enable or disable looping of the Audio object. If looping is allowed, the Audio object will begin to play again after it is finished. We can enable looping by calling the EnableLooping action, and we can disable looping by calling the DisableLooping action. The following example uses the Quorum Game Engine to implement a simple audio player using these actions.For this example, we will skip ahead a little bit and demonstrate an example that uses keyboard input, by making the game a KeyboardListener .We will go over the details of this in the next tutorial, Events Tutorial, but for now, mainly pay attention to the action calls on the Audio object song .

use Libraries.Game.Game
use Libraries.Sound.Audio
use Libraries.Interface.Events.KeyboardListener
use Libraries.Interface.Events.KeyboardEvent

class Main is Game, KeyboardListener
     Audio song

     action Main
          StartGame()
     end

     action CreateGame
          AddKeyboardListener(me)
          song:Load("Sounds/song.ogg")
          song:Play()
     end

     action Update(number seconds)
     end

   // this action sets our game to react to keyboard events from the game engine
   action PressedKey(KeyboardEvent event)
       // these statements determine which key is pressed - more in the next tutorial
       if event:keyCode = event:P
          // if the P key was pressed, pause or play the audio
           if song:IsPlaying()
               // if audio is playing, pause it
               song:Pause()
           else
               // otherwise play it
               song:Play()
           end
       elseif event:keyCode = event:S
           // if the S key was pressed, stop the audio
           song:Stop()
       elseif event:keyCode = event:R
           // if the R key was pressed, resume the audio
           song:Resume()
       elseif event:keyCode = event:E
           // if the E key was pressed, enable looping
           song:EnableLooping()
       elseif event:keyCode = event:D
           // if the D key was pressed, enable looping
           song:DisableLooping()
       elseif event:keyCode = event:D
       end
   end
end

Activity: Playing Sounds

Use the Quorum Game Engine to implement a simple audio player using these actions.

Playing Longer Audio: Streaming

If we have a longer audio file, like one that contains a full-length song, we may want to load our file via the LoadToStream action. The LoadToStream action allows our audio file to be loaded in "chunks,"as they are needed instead of all at once. This is more efficient and allows the file to start playing more quickly than if it must wait for the entire audio file to be loaded, especially for longer sounds, like songs. In order to stream our Audio object, we will need a loop that keeps the Audio object streaming as long as it is playing. An example of this follows:

use Libraries.Sound.Audio

// this line creates our Audio object we will be using to stream
Audio streamingAudio

// this line loads a "chunk"of the data from the file "LongSong.ogg"to our Audio object
streamingAudio:LoadToStream("Sounds/longSong.ogg")

// this line plays the first "chunk"of the data that was loaded from the file
streamingAudio:Play()

// this loop will continuously check if the whole song has been played
repeat while streamingAudio:IsPlaying()
   // this gets the next "chunk"of data to play
   streamingAudio:Stream()
end

A Quorum File object can also be used instead of a text file path when calling the LoadToStream action.

If we are playing sound as part of a game using the Quorum Game Engine, the proper place to put the Stream() call is inside the Update action, which is called every frame in the Main Game Loop. This will ensure that the next "chunk"of sound will be loaded at every opportunity. An example of streaming in the game engine follows:

use Libraries.Game.Game
use Libraries.Sound.Audio

class Main is Game
   Audio audio1
   Audio song

   action Main
       StartGame()
   end

   action CreateGame
       audio1:Load("Sounds/Fwip.ogg")
       audio1:Play()
       song:LoadToStream("Sounds/song.ogg")
       song:Play()
   end

   action Update(number seconds)
       //since this action is in the main game loop and called every frame it should not be inside another loop.
       song:Stream()
   end
end

Activity: Playing Longer Audio

The same thing but with a longer audio file

Changing the Audio: Controlling Volume and Pitch

We are able to set the volume and pitch of our Audio objects as they are playing by calling the SetVolume and SetPitch actions. These actions both take number parameters that represent the percent change in the pitch. For example, a number of 0.5 passed to SetVolume will set the new volume at 50% of the maximum volume. A number of 1.2 passed to SetPitch will raise the pitch 20% above the normal pitch. Examples of these actions follow:

use Libraries.Sound.Audio
Audio audio
audio:Load("Sounds/Clang.ogg")

// this line sets the volume of our Audio object at 50% its maximum volume.
audio:SetVolume(0.5)

// this line sets the pitch of our Audio object at 120% the normal pitch
audio:SetPitch(1.2)

audio:PlayUntilDone()

If we want to know what the current settings of the volume and pitch of our Audio objects are, we can use the GetVolume and GetPitch actions, respectively. These actions return a number that represents the value of the volume or pitch and can be used with their corresponding Set actions to change the values of the volume and/or pitch over time. An example of this follows:

use Libraries.Sound.Audio
Audio audio
audio:Load("Sounds/song.ogg")
audio:Play()

// this loop keeps our sound playing until it is done
repeat while audio:IsPlaying()
   // this conditional checks if the current volume is greater than 0. If it is, it decreases the volume
   if audio:GetVolume() > 0.0
   // this line calculates the new volume by getting the current volume and subtracting 0.00001% (one hundred-thousandth of a percent)
   number newVolume = audio:GetVolume() - 0.0000001

   // this line sets the volumen to newVolume, which will have the effect of fading the sound out quickly.
   audio:SetVolume(newVolume)
   end
end

Activity: Changing the Audio

Call the SetVolume and SetPitch actions to set the volume and pitch of our Audio objects.

Changing where the sound is played:

Balance, Fade and Rotation

When using these actions, we should be aware of a few things.

  • First, the default volume of an Audio object is its maximum volume. This means that the volume can never be set higher than that default volume. Passing a number parameter that is greater than 1 to the SetVolume action will have no effect.
  • Second, changing the pitch of the Audio object also affects the speed of playback. Making the pitch lower causes the sound to play slower; likewise, making the pitch higher causes the sound to play faster.

With mono sounds, i.e., sounds that have only one audio channel, we can change the way the sound is played from our speakers through the use of the SetBalance, SetFade, and Rotation actions. The SetBalance action is used to change which speaker (the right speaker or left speaker) the sound will play out of. The SetFade action sets the sound to play from the forward or backward audio channels. The Rotate action allows the sound to be set at a rotation between 0 and 360 degrees around the user.

The SetBalance action is used to change how the audio plays out of the left and right speakers. SetBalance has one parameter, a number between -1 and 1 that represents which speaker to play out of. A number between -1 and 0 causes the audio to play from the left speaker, with a value of -1 causing it to play completely from the left speaker. A value of 0 causes the audio to play from both speakers equally. A value between 0 and 1 causes the audio to play from the right speaker, with a value of 1 causing it to play completely from the right speaker. The default balance of Audio objects is 0. Example uses of the SetBalance action are below.

use Libraries.Sound.Audio

Audio left
Audio right
Audio center

left:Load("Sounds/Firework.ogg")
right:Load("Sounds/Robot.ogg")
center:Load("Sounds/Modem.ogg")

// set the balances of the objects (there is no need to set the balance of the center object because its default balance is 0)
left:SetBalance(-1)
right:SetBalance(1)

left:PlayUntilDone()
right:PlayUntilDone()
center:PlayUntilDone()

The SetFade action is used to change how the audio plays out of the forward and back channels. SetFade has one parameter, a number, which represents which channel, forward or back, the audio should play out of. This number behaves in much the same way that the parameter of the SetBalance action works. A number between -1 and 0 causes the audio to play out of the backwards channel. A number between 0 and 1 causes the audio to play out of the forwards channel. Examples of the SetFade actions follow:

use Libraries.Sound.Audio

Audio front
Audio back
Audio center

front:Load("Sounds/Firework.ogg")
back:Load("Sounds/Robot.ogg")
center:Load("Sounds/Modem.ogg")

// set the balances of the objects (there is no need to set the fade of the center object because its default fade is 0)
front:SetFade(1)
back:SetFade(-1)

front:PlayUntilDone()
back:PlayUntilDone()
center:PlayUntilDone()

The Rotate action is used to rotate the sound about the listener. Rotate takes one parameter, a number that represents the amount of degrees to rotate the sound around the listener. 0 degrees represents a rotation in front of the listener. 180 degrees represents a rotation behind the listener. Examples of the Rotate action follow:

use Libraries.Sound.Audio

Audio audio

audio:Load("Sounds/Fwip.ogg")

// set the audio to be rotated 90 degrees with respect to the listener
audio:Rotate(90)

audio:PlayUntilDone()

SetBalance, SetFade, and Rotate have corresponding GetBalance, GetFade, and GetRotation actions that return the current value of each setting.

It is important to note that SetBalance, SetFade, and Rotate cannot be used at the same time. For example, if the balance is set on an Audio object, and then a call is made to set the fade of that same Audio object, the balance will be reset to the default value. We will not be able to use these functions to create the illusion of 3D sound. To do that, use the positional audio functions described in the next section.

Activity: Changing where the sound is played: Balance, Fade and Rotation

Use the SetBalance, SetFade and Rotate action

3D Sound: Positional Audio

Quorum audio objects have the ability to be set in a position in a virtual 3D space. 3D space is often represented in math and other applications with 3D (x, y, z) coordinate system. An example of a 3D coordinate system is below.

Quorum uses a virtual 3D coordinate system very much like this one to set sounds in 3D space. Audio objects have x, y, and z-coordinates that can be set through the SetX, SetY, and SetZ actions. These three actions each take a single number parameter that represents the value of that coordinate. Audio objects also have corresponding GetX, GetY, and GetZ actions that return the requested coordinate.

Using positional audio can allow us to do some cool things in our programs. For example, we can use an audio object in a game, set its initial position, and then update its position in the game update loop. An example is below.

use Libraries.Game.Game
use Libraries.Sound.Audio

class Main is Game
   Audio audio

   action Main
       StartGame()
   end

   action CreateGame
       audio:Load("Sounds/Firework.ogg")

       // give the audio some initial coordinates
       audio:SetX(-1)
       audio:SetY(-1)
       audio:SetZ(-1)

       audio:Play()
   end

   action Update(number seconds)
       // calculate the new x, y, and z coordinates based off the current coordinates.
      //In this example, the x, y, and z coordinates are increased by 0.25 every second.
       number newX = audio:GetX() + 0.30 * seconds
       number newY = audio:GetY() + 0.30 * seconds
       number newZ = audio:GetZ() + 0.30 * seconds

       // using the newly calculated coordinates, update the position of the object
       audio:SetX(newX)
       audio:SetY(newY)
       audio:SetZ(newZ)
   end
end

Activity: 3D Sound: Positional Audio

Using positional audio can allow us to do some cool things in our programs

Audio3D

The Quorum game engine also supports Audio3D objects, which inherit from the Item3D class and the Audio class. An Audio3D object can be moved, positioned, and rotated just like a Model in 3-space, and it can use all of the Audio actions. We can add Audio3D objects directly to our models so that the positional audio will be calculated for us. Our Audio3D objects share the same coordinate system as all other Item3D objects and can exist on the same Layer .

Doppler Shift

Audio3D objects have their Doppler shift (the change in pitch based on listener position, for more information, see here) calculated and enabled by default. At any time we can disable Doppler by calling the Audio3D DisableDoppler() action. Once it has been disabled, the Audio3D action EnableDoppler() will turn it back on. We should remember that these actions can only be called on an instantiated Audio3D object. For example:
audio:DisableDoppler()
audio:EnableDoppler()

Listener Position and Direction

Recall this from our Camera tutorial: A camera object has both a position and a direction that it uses to display a scene. Our Audio3D objects have a similar feature called a listener. That listener has a position and a direction that it uses to represent a scene. The default listener position is at (0, 0, 0), with the direction (0, 0, 1). Both of these can be changed to track the movement of a player in the game. The actions that we use to change the position are Audio3D actions that should be called on our audio object. Below is an example of how we can call these actions on our Audio3D object:
audio:SetListenerPosition(0, 1, -1)
audio:SetListenerDirection(0, -1, 1)

We can also represent our position and direction as a Vector3.

Below is an example of a program that adds a sound to a moving box. The default settings for Doppler (on) and Listener are used, but we can try any of the code fragments from the two previous sections to understand how Doppler and Listener position/direction affect our game.

use Libraries.Game.Game
use Libraries.Game.Graphics.Model
use Libraries.Game.Graphics.Color
use Libraries.Game.Audio3D

class Main is Game
   Audio3D audio
   Model model

   action Main
       StartGame()
   end

   action CreateGame
       Color color
       model:LoadBox(0.5, 0.5, 0.5, color:Green())
       Add(model)
       audio:Load("Sounds/Robot.ogg")
       audio:EnableLooping()
       //adds the audio to the model so position is automatically calculated
       model:Add(audio)

       model:SetX(-5)

       audio:Play()
   end

   action Update(number seconds)
       //Moves the model a small amount in each frame
       model:Move(0.025, 0, 0)

   end
end

Activity: Audio3D

Using the Doppler Shift and also Listener Position and Direction

Common Problems

When I run my program, I get an error telling me that the file extension is unsupported.

Quorum can only play audio files in the ".wav" or ".ogg" formats. Ensure that the audio file is one of these formats. If it is not, there are many free programs available that will convert the file to one of these formats.

When I run my program, I get an error telling me that I can't play audio before it is loaded.

In order to play an audio object, a file must be loaded to it. Ensure that a file is loaded to the audio object before calling its Play action.

I have called SetBalance/SetPitch/Rotate on my audio object, but it did not have any effect.

The SetBalance, SetPitch, and Rotate actions only work on mono sounds. Ensure that the sound is not stereo. If it is, there are many free programs available that will convert sound files from stereo to mono.

I have called SetX/SetY/SetZ on my audio object, but it did not have any effect.

The SetX, SetY, and SetZ actions only work on mono sounds. Ensure that the sound is not stereo. If it is, there are many free programs available that will convert sound files from stereo to mono.