Playing Sounds
In addition to the visuals, sound is an important component of real-time solutions. It is sound that creates the feeling of immersion in the virtual world. A rumbling echo that makes you think the action is taking place in a spacious building, the soft tapping of footsteps on a stone floor or a car speeding past — all this can be simulated. UNIGINE provides a multi-channel sound system with stereo sound support based on HRTF (sound perception modeling function), various 3D effects, obstruction, and multiple sound reverberation. You can play sound in MP3, WAV, or OGA format when objects are in contact to simulate their physical properties at the sound level. For moving objects, the Doppler effect is simulated.
UNIGINE has two types of sound sources:
- Sound Source — used to create directional sound sources.
- Ambient Source — used for playing background music and sounds that can be heard throughout the scene.
A directional sound source can be added to a scene either in the Editor or via code. To add it via code, all you need to do is create an instance of the SoundSource class and specify all the necessary settings. Sound playback can be turned on and off using the Play() and Stop() methods.
// create a new sound source using the given sound sample file
SoundSource sound = new SoundSource("sound.mp3");
// disable sound muffling when being occluded
sound.Occlusion = 0;
// set the distance at which the sound gets clear
sound.MinDistance = 10.0f;
// set the distance at which the sound becomes out of audible range
sound.MaxDistance = 100.0f;
// set the sound amplification factor
sound.Gain = 0.5f;
// loop the sound
sound.Loop = 1;
// start playing the sound sample
sound.Play();
The sound played depends on the relative position of the sound sources and the listener. The sound is linearly attenuated within the specified range (MinDistance and MaxDistance). If inner and outer sound cones are set, they will also contribute to the attenuation factor (ConeInnerAngle and ConeOuterAngle). In addition, various objects in the scene can also block the propagation of sound from SoundSource (you can even set different sound absorption coefficients for different surfaces). The number of such sound sources is unlimited, as only those within hearing range are played.
To play background music and sounds that should be heard everywhere, you need to create an instance of the AmbientSource class (such sources can only be created via API). When creating it, the necessary parameters are also specified.
// create Player so that the sound produced by Ambient Source is played
PlayerSpectator player = new PlayerSpectator();
player.Position = new Vec3(0.0f, -3.401f, 1.5f);
player.ViewDirection = new vec3(0.0f, 1.0f, -0.4f);
Game.Player = player;
// create AmbientSource
AmbientSource sound = new AmbientSource("sound.mp3");
// set necessary sound settings
sound.Gain = 0.5f;
sound.Pitch = 1.0f;
sound.Loop = 1;
// play the sound
sound.Play();
Sound is processed in a separate thread at 30 frames per second, so changes are not applied instantly. In some cases, such as after stopping playback before changing track, it is necessary to force update the audio stream to avoid application errors.
Practice#
To create an atmosphere in our application, let's add the StereoSystem component to play music tracks using AmbientSource.
The component controls the stereo system in the room (switches it on and off, changes tracks).
using System;
using System.Collections;
using System.Collections.Generic;
using Unigine;
[Component(PropertyGuid = "AUTOGENERATED_GUID")] // <-- this line is generated automatically for a new component
public class StereoSystem : Interactable
{
public List<AssetLink> sound_tracks = null;
private int current_track = 0;
private AmbientSource track_player;
private void Init()
{
// set the tooltip text that will be displayed when the cursor hovers over the object
tooltip = "Right-click switches the sound tracks in a cyclic order.";
// initialize the first track if the list is not empty
if(sound_tracks != null && sound_tracks.Capacity > 0)
track_player = new AmbientSource(sound_tracks[current_track].AbsolutePath);
track_player.Stop();
}
public override void Action(int num = 0)
{
// action indices other than zero are invalid for this component, so they are ignored
if (num != 0)
return;
// if the playlist is empty, do nothing
if(sound_tracks == null || sound_tracks.Capacity < 1)
return;
// if necessary, disable playback of the previous track before switching to the next one
if (!track_player.IsStopped){
track_player.Stop();
}
// after stopping playback before changing tracks, we need to force refresh the sound stream
Sound.RenderWorld(1);
// change the track to an element of the indexed audio list with index
track_player.SampleName = sound_tracks[current_track].AbsolutePath;
// display information about the current track
Visualizer.RenderMessage2D( new vec3(0.0f, (float)(WindowManager.MainWindow.ClientSize.y - 40)/WindowManager.MainWindow.ClientSize.y, 0.0f),
new vec3(1.0f, 0.1f, 0.0f),
String.Format(" > Current track no.{0}: {1}", current_track + 1, track_player.SampleName),
vec4.YELLOW,1,18,1);
track_player.Time = 0.0f;
track_player.Play();
// increase the index of the current track (if it exceeds the number of tracks, set it to 0)
current_track++;
if (current_track >= sound_tracks.Capacity)
current_track = 0;
}
}
Assign the StereoSystem component to the tv1 node in the scene (interior -> tv1) and add tracks from the archviz/sounds folder to the Sound Tracks list:
Let's also add a few lines to the Fan.cs component to play the fan sound. The important point here is that the Fan component is assigned to the fan_rotator node controlled by Toggle, so if the component disables the fan_rotator node, the logic of the Fan component assigned to it will not be executed and the sound will not be turned off. We need to make that part of the Fan component code (namely the UpdateSound method) work even if the node is disabled and be executed every frame, so we add the following line before the UpdateSound method:
[MethodUpdate(InvokeDisabled = true)]
As a result, we have the following code and everything works out well:
using System;
using System.Collections;
using System.Collections.Generic;
using Unigine;
[Component(PropertyGuid = "AUTOGENERATED_GUID")] // <-- this line is generated automatically for a new component
public class Fan : Component
{
public Node fan_node = null;
public float speed = 720.0f;
${#HL}$ private String soundFile = "archviz/sounds/fan_sound.mp3"; // audio asset
private SoundSource sound= null; // source of fan blades rotation sound
private void Init()
{
// create a sound source using the specified audio asset
sound = new SoundSource(soundFile);
// disable sound muffling when being occluded
sound.Occlusion = 0;
// set the distance at which the sound gets clear
sound.MinDistance = 1.0f;
// set the distance at which the sound becomes out of audible range
sound.MaxDistance = 4.0f;
// set the interval for a smooth volume change
sound.Adaptation = 2.0f;
// set the sound amplification factor
sound.Gain = 0.5f;
// loop the sound
sound.Loop = 1;
// set the sound source in the fan position
sound.WorldTransform = fan_node.WorldTransform;
} ${HL#}$
private void Update()
{
// if the fan node is not assigned, do nothing
if(!fan_node)
return;
// rotate the node with the specified speed
fan_node.Rotate(0, speed * Game.IFps, 0);
}
${#HL}$ // ensure that this method is executed even if the component is disabled along with the node
[MethodUpdate(InvokeDisabled = true)]
private void UpdateSound()
{
// if the component is disabled and the sound is being played, turn off the playback
if (!Enabled && sound.IsPlaying){
sound.Stop();
return;
}
// if the component is enabled and sound is not playing, enable playback
if (Enabled && !sound.IsPlaying)
sound.Play();
} ${HL#}$
}
As a result, we have created an interactive room while getting acquainted with the UNIGINE component system and the essential aspects of working with various scene components from the code. This room allows us to arrange and remove objects, interact with them, modify their appearance, and play sounds.