Leviathan  0.8.0.0
Leviathan game engine
Leviathan::SoundDevice Class Reference

Manages loading the audio library and provides some helpers. More...

#include <SoundDevice.h>

Classes

struct  Implementation
 

Public Member Functions

DLLEXPORT SoundDevice ()
 
DLLEXPORT ~SoundDevice ()
 
DLLEXPORT bool Init (bool simulatesound=false)
 
DLLEXPORT void Release ()
 
DLLEXPORT void Tick (int PassedMs)
 
DLLEXPORT void SetSoundListenerPosition (const Float3 &pos, const Float4 &orientation)
 Loads the file and plays the sound. More...
 
DLLEXPORT void SetGlobalVolume (float vol)
 
DLLEXPORT void Play2DSoundEffect (const std::string &filename)
 Plays a 2d sound without possibility of interrupting. More...
 
DLLEXPORT AudioSource::pointer Play2DSound (const std::string &filename, bool looping)
 Plays a 2d sound with options. More...
 
DLLEXPORT AudioSource::pointer CreateProceduralSound (const Sound::ProceduralSoundData::pointer &data, size_t chunksize=56000, size_t chunkstoqueue=4)
 Opens an audio source from a procedural data stream. More...
 
DLLEXPORT Sound::AudioBuffer::pointer GetBufferFromFile (const std::string &filename, bool cache=true)
 Creates a sound buffer from a file. More...
 
DLLEXPORT AudioSource::pointer GetAudioSource ()
 Creates an audio source with no settings applied. More...
 
DLLEXPORT void BabysitAudio (AudioSource::pointer audio)
 This class holds the audio source until it has finished playing and then releases the reference. More...
 
DLLEXPORT void ReportDestroyedBuffer (Sound::AudioBuffer &buffer)
 

Detailed Description

Manages loading the audio library and provides some helpers.

Definition at line 15 of file SoundDevice.h.

Constructor & Destructor Documentation

◆ SoundDevice()

SoundDevice::SoundDevice ( )

Definition at line 57 of file SoundDevice.cpp.

57 : Pimpl(std::make_unique<Implementation>()) {}

◆ ~SoundDevice()

SoundDevice::~SoundDevice ( )

Definition at line 58 of file SoundDevice.cpp.

59 {
60  Release();
61  Pimpl.reset();
62 }
DLLEXPORT void Release()

Member Function Documentation

◆ BabysitAudio()

DLLEXPORT void SoundDevice::BabysitAudio ( AudioSource::pointer  audio)

This class holds the audio source until it has finished playing and then releases the reference.

Definition at line 397 of file SoundDevice.cpp.

398 {
400 
401  Pimpl->HandledAudioSources.push_back(audio);
402 }
static DLLEXPORT Engine * Get()
Definition: Engine.cpp:85
DLLEXPORT void AssertIfNotMainThread() const
Asserts if not called on the main thread.
Definition: Engine.h:105

◆ CreateProceduralSound()

DLLEXPORT AudioSource::pointer SoundDevice::CreateProceduralSound ( const Sound::ProceduralSoundData::pointer data,
size_t  chunksize = 56000,
size_t  chunkstoqueue = 4 
)

Opens an audio source from a procedural data stream.

Definition at line 332 of file SoundDevice.cpp.

335 {
337 
338  if(!Pimpl || !data)
339  return nullptr;
340 
341  const auto source = GetAudioSource();
342 
343  if(!source)
344  return nullptr;
345 
346  source->PlayWithDecoder(data, chunksize, chunkstoqueue);
347  return source;
348 }
DLLEXPORT AudioSource::pointer GetAudioSource()
Creates an audio source with no settings applied.
static DLLEXPORT Engine * Get()
Definition: Engine.cpp:85
DLLEXPORT void AssertIfNotMainThread() const
Asserts if not called on the main thread.
Definition: Engine.h:105

◆ GetAudioSource()

DLLEXPORT AudioSource::pointer SoundDevice::GetAudioSource ( )

Creates an audio source with no settings applied.

Definition at line 385 of file SoundDevice.cpp.

386 {
387  auto alureSource = Pimpl->Context.createSource();
388 
389  if(!alureSource) {
390  LOG_ERROR("SoundDevice: GetAudioSource: couldn't create alure source");
391  return nullptr;
392  }
393 
394  return AudioSource::MakeShared<AudioSource>(alureSource);
395 }
#define LOG_ERROR(x)
Definition: Define.h:90

◆ GetBufferFromFile()

DLLEXPORT Sound::AudioBuffer::pointer SoundDevice::GetBufferFromFile ( const std::string &  filename,
bool  cache = true 
)

Creates a sound buffer from a file.

Definition at line 350 of file SoundDevice.cpp.

352 {
353  const auto now = Time::GetTimeMs64();
354 
355  AudioBuffer::pointer buffer;
356 
357  // Find buffer from cache
358  if(cache) {
359  const auto found = Pimpl->BufferCache.find(filename);
360 
361  if(found != Pimpl->BufferCache.end()) {
362  buffer = std::get<0>(found->second);
363  std::get<1>(found->second) = now;
364  }
365  }
366 
367  if(!buffer) {
368  // Not cached
369  auto alureBuffer = Pimpl->Context.getBuffer(filename);
370 
371  if(!alureBuffer) {
372  LOG_ERROR("SoundDevice: failed to create buffer from file: " + filename);
373  return nullptr;
374  }
375 
376  buffer = AudioBuffer::MakeShared<AudioBuffer>(std::move(alureBuffer), this);
377 
378  if(cache)
379  Pimpl->BufferCache[filename] = {buffer, now};
380  }
381 
382  return buffer;
383 }
#define LOG_ERROR(x)
Definition: Define.h:90
static DLLEXPORT int64_t GetTimeMs64()

◆ Init()

bool SoundDevice::Init ( bool  simulatesound = false)
Parameters
simulatenosoundIf true the sound device isn't initialized to simulate not having a valid audio device (or if the user just doesn't want sound)

Definition at line 64 of file SoundDevice.cpp.

65 {
66  if(simulatesound) {
67  LOG_WARNING("SoundDevice: simulating not having a playing device");
68  return true;
69  }
70 
71  Pimpl->DeviceManager = alure::DeviceManager::getInstance();
72 
73  const auto defaultChoice =
74  Pimpl->DeviceManager.defaultDeviceName(alure::DefaultDeviceType::Full);
75 
76  auto devices = Pimpl->DeviceManager.enumerate(alure::DeviceEnumeration::Full);
77 
78  if(defaultChoice.empty()) {
79 
80  LOG_ERROR("SoundDevice: no default sound device detected");
81  return false;
82  }
83 
84  if(devices.empty()) {
85 
86  LOG_ERROR("SoundDevice: no audio devices detected");
87  return false;
88  }
89 
90  std::string selectedDevice;
91  // There's no print error here if missing to make tests run
92  ObjectFileProcessor::LoadValueFromNamedVars<std::string>(
93  Engine::Get()->GetDefinition()->GetValues(), "AudioDevice", selectedDevice, "default");
94 
95  if(selectedDevice == "default") {
96 
97  selectedDevice = defaultChoice;
98 
99  } else if(std::find(devices.begin(), devices.end(), selectedDevice) == devices.end()) {
100  LOG_ERROR("SoundDevice: selected audio device \"" + selectedDevice +
101  "\" doesn't exists. Using default");
102  selectedDevice = defaultChoice;
103  }
104 
105  LOG_INFO("SoundDevice: Initializing sound with device: " + selectedDevice);
106 
107  std::stringstream sstream;
108 
109  sstream << "Start of audio system information:\n"
110  << "// ------------------------------------ //\n";
111 
112  auto defaultDeviceName =
113  Pimpl->DeviceManager.defaultDeviceName(alure::DefaultDeviceType::Basic);
114  devices = Pimpl->DeviceManager.enumerate(alure::DeviceEnumeration::Basic);
115 
116  sstream << "Available basic devices:\n";
117  for(const auto& name : devices) {
118  sstream << "\t> " << name;
119  if(name == defaultDeviceName)
120  sstream << " [DEFAULT]";
121  sstream << "\n";
122  }
123 
124  sstream << "\n";
125 
126  devices = Pimpl->DeviceManager.enumerate(alure::DeviceEnumeration::Full);
127  defaultDeviceName = Pimpl->DeviceManager.defaultDeviceName(alure::DefaultDeviceType::Full);
128 
129  sstream << "Available devices:\n";
130  for(const auto& name : devices) {
131  sstream << "\t> " << name;
132  if(name == defaultDeviceName)
133  sstream << " [DEFAULT]";
134  sstream << "\n";
135  }
136  sstream << "\n";
137 
138  devices = Pimpl->DeviceManager.enumerate(alure::DeviceEnumeration::Capture);
139  defaultDeviceName =
140  Pimpl->DeviceManager.defaultDeviceName(alure::DefaultDeviceType::Capture);
141  sstream << "Available capture devices:\n";
142  for(const auto& name : devices) {
143  sstream << "\t> " << name;
144  if(name == defaultDeviceName)
145  sstream << " [DEFAULT]";
146  sstream << "\n";
147  }
148  sstream << "\n";
149 
150  try {
151  Pimpl->Device = Pimpl->DeviceManager.openPlayback(selectedDevice);
152  } catch(const std::exception& e) {
153  LOG_INFO(sstream.str());
154  LOG_ERROR("SoundDevice: opening playback failed: " + std::string(e.what()));
155  return false;
156  }
157 
158  sstream << "Info for device \"" << Pimpl->Device.getName(alure::PlaybackName::Full)
159  << "\":" << std::endl;
160  auto version = Pimpl->Device.getALCVersion();
161  sstream << "ALC version: " << version.getMajor() << "." << version.getMinor() << "\n";
162  version = Pimpl->Device.getEFXVersion();
163  if(!version.isZero()) {
164  sstream << "EFX version: " << version.getMajor() << "." << version.getMinor() << "\n";
165  sstream << "Max auxiliary sends: " << Pimpl->Device.getMaxAuxiliarySends() << "\n";
166  } else
167  sstream << "EFX not supported"
168  << "\n";
169 
170  // TODO: extensions
171  // Pimpl->Device.queryExtension(const String &name)
172 
173  sstream << "// ------------------------------------ //";
174 
175  LOG_INFO(sstream.str());
176 
177  try {
178  Pimpl->Context = Pimpl->Device.createContext();
179 
180  if(!Pimpl->Context)
181  throw Exception("returned context is null");
182 
183  } catch(const std::exception& e) {
184  LOG_ERROR("SoundDevice: opening context failed: " + std::string(e.what()));
185  return false;
186  }
187 
188  alure::Context::MakeCurrent(Pimpl->Context);
189  Pimpl->Listener = Pimpl->Context.getListener();
190 
191  Pimpl->Context.setMessageHandler(alure::MakeShared<SoundMessageHandler>());
192 
193  // Setup global volume //
194  SetGlobalVolume(1.f);
195 
196  Pimpl->Listener.setPosition({0, 0, 0});
197  Pimpl->Listener.set3DParameters(
198  // Pos
199  {0, 0, 0},
200  // Velocity
201  {0, 0, 0},
202  // Orientation, at and up
203  {{0, 0, 0}, {0, 1, 0}});
204 
205  return true;
206 }
#define LOG_INFO(x)
Definition: Define.h:88
#define LOG_ERROR(x)
Definition: Define.h:90
DLLEXPORT void SetGlobalVolume(float vol)
Base class for all exceptions thrown by Leviathan.
Definition: Exceptions.h:10
#define LOG_WARNING(x)
Definition: Define.h:89
static DLLEXPORT Engine * Get()
Definition: Engine.cpp:85

◆ Play2DSound()

DLLEXPORT AudioSource::pointer SoundDevice::Play2DSound ( const std::string &  filename,
bool  looping 
)

Plays a 2d sound with options.

Returns
The audio source that is playing the sound (this must be held onto until it is done playing, can be passed to BabysitAudio if not manually wanted to be managed or use the SoundEffect variant of this method) may be null on error

Definition at line 311 of file SoundDevice.cpp.

313 {
315 
316  const auto buffer = GetBufferFromFile(filename);
317  if(!buffer)
318  return nullptr;
319 
320  const auto source = GetAudioSource();
321 
322  if(!source)
323  return nullptr;
324 
325  source->Play2D(buffer);
326 
327  if(looping)
328  source->SetLooping(true);
329  return source;
330 }
DLLEXPORT Sound::AudioBuffer::pointer GetBufferFromFile(const std::string &filename, bool cache=true)
Creates a sound buffer from a file.
DLLEXPORT AudioSource::pointer GetAudioSource()
Creates an audio source with no settings applied.
static DLLEXPORT Engine * Get()
Definition: Engine.cpp:85
DLLEXPORT void AssertIfNotMainThread() const
Asserts if not called on the main thread.
Definition: Engine.h:105

◆ Play2DSoundEffect()

DLLEXPORT void SoundDevice::Play2DSoundEffect ( const std::string &  filename)

Plays a 2d sound without possibility of interrupting.

Definition at line 293 of file SoundDevice.cpp.

294 {
296 
297  const auto buffer = GetBufferFromFile(filename);
298  if(!buffer)
299  return;
300 
301  const auto source = GetAudioSource();
302 
303  if(!source)
304  return;
305 
306  source->Play2D(buffer);
307 
308  Engine::Get()->RunOnMainThread([=]() { this->BabysitAudio(source); });
309 }
DLLEXPORT Sound::AudioBuffer::pointer GetBufferFromFile(const std::string &filename, bool cache=true)
Creates a sound buffer from a file.
DLLEXPORT AudioSource::pointer GetAudioSource()
Creates an audio source with no settings applied.
DLLEXPORT void RunOnMainThread(const std::function< void()> &function)
Runs the function now if on the main thread otherwise calls Invoke.
Definition: Engine.cpp:1183
DLLEXPORT void BabysitAudio(AudioSource::pointer audio)
This class holds the audio source until it has finished playing and then releases the reference.
static DLLEXPORT Engine * Get()
Definition: Engine.cpp:85
DLLEXPORT void AssertIfNotMainThread() const
Asserts if not called on the main thread.
Definition: Engine.h:105

◆ Release()

void SoundDevice::Release ( )

Definition at line 208 of file SoundDevice.cpp.

209 {
210  if(!Pimpl->Device)
211  return;
212 
213  Pimpl->HandledAudioSources.clear();
214  Pimpl->BufferCache.clear();
215 
216  alure::Context::MakeCurrent(nullptr);
217  Pimpl->Listener = nullptr;
218  Pimpl->Context.destroy();
219  Pimpl->Device.close();
220 
221  Pimpl->Device = nullptr;
222 }

◆ ReportDestroyedBuffer()

DLLEXPORT void SoundDevice::ReportDestroyedBuffer ( Sound::AudioBuffer buffer)

Definition at line 404 of file SoundDevice.cpp.

405 {
406  Pimpl->Context.removeBuffer(buffer.GetBuffer());
407 }
alure::Buffer & GetBuffer()
Definition: AudioBuffer.h:40

◆ SetGlobalVolume()

DLLEXPORT void SoundDevice::SetGlobalVolume ( float  vol)
Parameters
volThe volume [0.f, 1.f]

Definition at line 286 of file SoundDevice.cpp.

287 {
288  vol = std::clamp(vol, 0.f, 1.f);
289 
290  Pimpl->Listener.setGain(vol);
291 }

◆ SetSoundListenerPosition()

DLLEXPORT void SoundDevice::SetSoundListenerPosition ( const Float3 pos,
const Float4 orientation 
)

Loads the file and plays the sound.

This creates a temporary SoundEffect on a background thread that is loaded from the file and destroyed once it finishes

Returns
False if the file doesn't exist or the sound couldn't be played for some other reason

Definition at line 260 of file SoundDevice.cpp.

262 {
263  if(!Pimpl->Listener)
264  return;
265 
266  bs::Quaternion quaternion(orientation);
267 
268  bs::Radian angle;
269  bs::Vector3 direction;
270 
271  // TODO: does this do the right thing?
272  quaternion.toAxisAngle(direction, angle); // toAngleAxis(angle, direction);
273 
274  alure::Vector3 at(0, 0, 0);
275 
276  // TODO: better velocity calculation
277  const auto temp = pos - Pimpl->PreviousPosition;
278  alure::Vector3 velocity(temp.X, temp.Y, temp.Z);
279 
280  Pimpl->Listener.set3DParameters(
281  {pos.X, pos.Y, pos.Z}, velocity, {at, {direction.x, direction.y, direction.z}});
282 
283  Pimpl->PreviousPosition = pos;
284 }

◆ Tick()

void SoundDevice::Tick ( int  PassedMs)

Definition at line 224 of file SoundDevice.cpp.

225 {
226  ElapsedSinceLastClean += PassedMs;
227 
228  Pimpl->Context.update();
229 
230  if(ElapsedSinceLastClean > 200) {
231  ElapsedSinceLastClean = 0;
232 
233  for(auto iter = Pimpl->HandledAudioSources.begin();
234  iter != Pimpl->HandledAudioSources.end();) {
235 
236  if(!(*iter)->IsPlaying()) {
237 
238  iter = Pimpl->HandledAudioSources.erase(iter);
239  } else {
240  ++iter;
241  }
242  }
243 
244  const auto now = Time::GetTimeMs64();
245 
246  // Clear cache entries
247  for(auto iter = Pimpl->BufferCache.begin(); iter != Pimpl->BufferCache.end();) {
248 
249  if(std::get<1>(iter->second) - now > CacheSoundEffectMilliseconds) {
250  // Remove from cache
251 
252  iter = Pimpl->BufferCache.erase(iter);
253  } else {
254  ++iter;
255  }
256  }
257  }
258 }
static DLLEXPORT int64_t GetTimeMs64()

The documentation for this class was generated from the following files: