Проигрывание звука
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 предоставляет систему многоканального звука с поддержкой стереозвука на основе функции HRTF (функции моделирования восприятия звука), различных 3D эффектов, преграждения и множественной реверберации звука. Можно проигрывать звук в формате MP3, WAV или OGA при контакте объектов для имитации их физических свойств на уровне звука. Для движущихся объектов моделируется эффект Допплера.
UNIGINE has two types of sound sources: В UNIGINE есть два типа источников звука:
- Sound Source — used to create directional sound sources.Sound Source – используется для создания направленных источников звука.
- Ambient Source — used for playing background music and sounds that can be heard throughout the scene.Ambient Source – для воспроизведения фоновых музыки и звуков, которые слышны повсюду в сцене.
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.Направленный источник звука можно добавить в сцену как в Редакторе, так и через код. В последнем случае, достаточно создать экземпляр класса SoundSource и указать все необходимые настройки. Воспроизведение звука можно включать и выключать при помощи методов Play() и Stop().
// создаем новый источник звука, используя указанный аудиофайл
SoundSource sound = new SoundSource("sound.mp3");
// отключаем заглушение звука преградами
sound.Occlusion = 0;
// устанавливаем дистанцию, начиная с которой звук становится чистым
sound.MinDistance = 10.0f;
// устанавливаем дистанцию, начиная с которой звук перестает быть слышимым
sound.MaxDistance = 100.0f;
// устанавливаем коэффициент усиления звука
sound.Gain = 0.5f;
// включить циклическое воспроизведение звука
sound.Loop = 1;
// включаем воспроизведение звука
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.Проигрываемый звук зависит от взаимного расположения источников звука и слушателя. Звук линейно затухает в указанном диапазоне (MinDistance и MaxDistance). Если определены внутренний и внешний звуковые конусы, они также будут вносить свою лепту в коэффициент затухания (ConeInnerAngle и ConeOuterAngle). Кроме того, различные объекты в сцене также могут преграждать распространение звука от источников SoundSource (можно даже задавать коэффициент поглощения звука для различных поверхностей). Количество таких источников звука неограничено, поскольку проигрываются только те, что находятся в пределах слышимости.
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.Для воспроизведения фоновой музыки и звуков, которые должны быть слышны везде нужно создать экземпляр класса AmbientSource (такие источники можно создавать только через API). При создании также указываются необходимые параметры.
// создаем персонаж (player), чтобы звук Ambient Source был слышен
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;
// создаем источник звука AmbientSource
AmbientSource sound = new AmbientSource("sound.mp3");
// устанавливаем необходимые параметры звука
sound.Gain = 0.5f;
sound.Pitch = 1.0f;
sound.Loop = 1;
// включаем воспроизведение звука
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.Звук обрабатывается в отдельном потоке с частотой 30 кадров в секунду, поэтому изменения не применяются мгновенно. В ряде случаев, как например после остановки воспроизведения перед сменой трека, нужно форсированно обновить звуковой поток, чтобы избежать ошибок в работе приложения.
PracticeПрактика#
To create an atmosphere in our application, let's add the StereoSystem component to play music tracks using AmbientSource.В нашем приложении для создания атмосферы добавим компонент StereoSystem для проигрывания музыкальных треков при помощи источника 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")] // <-- идентификатор генерируется автоматически для нового компонента
public class StereoSystem : Interactable
{
public List<AssetLink> sound_tracks = null;
private int current_track = 0;
private AmbientSource track_player;
private void Init()
{
// задаем текст подсказки, которая будет отображаться при наведении курсора на объект
tooltip = "По правому щелчку мыши циклически переключает звуковые дорожки по списку.";
// инициализируем первый трек, если список не пуст
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)
{
// индексы действий отличные от нуля для данного компонента невалидны, поэтому игнорируются
if (num != 0)
return;
// если список воспроизведения пуст, ничего не делаем
if(sound_tracks == null || sound_tracks.Capacity < 1)
return;
// при необходимости отключаем воспроизведение предыдущего трека перед переключением
if (!track_player.IsStopped){
track_player.Stop();
}
// после остановки воспроизведения перед сменой трека нужно форсированно обновить звуковой поток
Sound.RenderWorld(1);
// меняем трек на элемент списка аудиофайлов с индексом
track_player.SampleName = sound_tracks[current_track].AbsolutePath;
// отображаем информацию о текущем треке
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(" > Воспроизведение дорожки №{0}: {1}", current_track + 1, track_player.SampleName),
vec4.YELLOW,1,18,1);
track_player.Time = 0.0f;
track_player.Play();
// увеличиваем индекс текущего трека (если он превышает число треков, устанавливаем его в 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:Назначьте компонент StereoSystem на ноду tv1 в сцене (interior -> tv1) и добавьте треки из папки archviz/sounds в список Sound Tracks:
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: Также давайте добавим несколько строк в компонент Fan.cs для проигрывания звука вентилятора. Важный момент здесь в том, что компонент Fan назначен на ноду fan_rotator, управляемую выключателем (Toggle), при отключении компонентом ноды fan_rotator, перестанет выполняться логика назначенного на нее компонента Fan и звук просто не выключится. Нам нужно, чтобы часть кода компонента Fan (а именно метод UpdateSound) работала и тогда, когда нода отключена и выполнялась каждый кадр, поэтому перед методом UpdateSound добавляем:
[MethodUpdate(InvokeDisabled = true)][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")] // <-- идентификатор генерируется автоматически для нового компонента
public class Fan : Component
{
public Node fan_node = null;
public float speed = 720.0f;
private String soundFile = "archviz/sounds/fan_sound.mp3"; // аудио-ассет
private SoundSource sound= null; // источник звука вращения лопастей вентилятора
private void Init()
{
// создаем источник звука с использованием указанного аудио-ассета
sound = new SoundSource(soundFile);
// отключаем заглушение звука преградами
sound.Occlusion = 0;
// устанавливаем дистанцию, начиная с которой звук становится чистым
sound.MinDistance = 1.0f;
// устанавливаем дистанцию, начиная с которой звук перестает быть слышимым
sound.MaxDistance = 4.0f;
// устанавливаем интервал плавного изменения громкости звука
sound.Adaptation = 2.0f;
// устанавливаем коэффициент усиления звука
sound.Gain = 0.5f;
// включаем циклическое воспроизведение звука
sound.Loop = 1;
// устанавливаем положение источника по позиции вентилятора
sound.WorldTransform = fan_node.WorldTransform;
}
private void Update()
{
// если не назначена нода лопастей, ничего не делаем
if(!fan_node)
return;
// поворачиваем ноду с заданной скоростью
fan_node.Rotate(0, speed * Game.IFps, 0);
}
// обеспечиваем выполнение этого метода даже если компонент отключен вместе с нодой
[MethodUpdate(InvokeDisabled = true)]
private void UpdateSound()
{
// если компонент выключен и звук воспроизводится - выключаем воспроизведение
if (!Enabled && sound.IsPlaying){
sound.Stop();
return;
}
// если компонент включен и звук не воспроизводится - включаем воспроизведение
if (Enabled && !sound.IsPlaying)
sound.Play();
}
}
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.В итоге, в ходе знакомства с компонентной системой UNIGINE и основными аспектами работы с различными составляющими сцены из кода, у нас получилась интерактивная комната, в которой можно расставлять и удалять объекты, взаимодействовать с ними, изменять их внешний вид, воспроизводить звуки.