Leviathan  0.8.0.0
Leviathan game engine
SoundDevice.cpp
Go to the documentation of this file.
1 // ------------------------------------ //
2 #include "SoundDevice.h"
3 
4 #include "SoundInternalTypes.h"
5 
8 #include "Engine.h"
10 
11 #include "cAudio/cAudio.h"
12 
13 #include <boost/filesystem.hpp>
14 
15 #include <algorithm>
16 using namespace Leviathan;
17 using namespace Leviathan::Sound;
18 // ------------------------------------ //
19 
22 {
23  Release();
24 }
25 // ------------------------------------ //
26 bool SoundDevice::Init(bool simulatesound /*= false*/, bool noconsolelog /*= false*/)
27 {
28  AudioLogPath = StringOperations::RemoveExtension(Logger::Get()->GetLogFile(), false) +
29  "cAudioLog.html";
30 
31  AudioManager = cAudio::createAudioManager(
32  // No default init, we want to select the device
33  false,
34  // And write to a program specific log file
35  AudioLogPath.c_str(), noconsolelog);
36 
37  LEVIATHAN_ASSERT(AudioManager, "Failed to create cAudio manager");
38 
39  size_t defaultDevice = 0;
40  const auto devices = GetAudioDevices(&defaultDevice);
41 
42  if(simulatesound == true) {
43 
44  LOG_WARNING("SoundDevice: simulating not having a playing device");
45  return true;
46  }
47 
48  if(devices.empty() || defaultDevice >= devices.size()) {
49 
50  LOG_ERROR("SoundDevice: no sound devices detected");
51  return false;
52  }
53 
54  if(std::find(devices.begin(), devices.end(), devices[defaultDevice]) == devices.end()) {
55  // I have no clue how it would ever get here, but you never know
56  LOG_ERROR("SoundDevice: Default device doesn't exist. The code should not have "
57  "been able to get here.");
58  }
59 
60  LOG_INFO("Detected audio devices: ");
61 
62  for(const auto& dev : devices)
63  LOG_INFO("> " + dev);
64 
65  LOG_INFO("End of devices");
66 
67  std::string selectedDevice;
68  // There's no print error here if missing to make tests run
69  ObjectFileProcessor::LoadValueFromNamedVars<std::string>(
70  Engine::Get()->GetDefinition()->GetValues(), "AudioDevice", selectedDevice, "default");
71 
72  if(selectedDevice == "default") {
73 
74  selectedDevice = devices[defaultDevice];
75 
76  } else if(std::find(devices.begin(), devices.end(), selectedDevice) == devices.end()) {
77  LOG_ERROR("SoundDevice: selected audio device \"" + selectedDevice +
78  "\" doesn't exists. Using default");
79  selectedDevice = devices[defaultDevice];
80  }
81 
82  LOG_INFO("SoundDevice: Initializing sound with device: " + selectedDevice);
83 
84  if(!AudioManager->initialize(selectedDevice.c_str())) {
85 
86  LOG_ERROR("SoundDevice: initializing failed");
87  return false;
88  }
89 
90  ListeningPosition = AudioManager->getListener();
91 
92  // setup global volume //
93  SetGlobalVolume(1.f);
94 
95  ListeningPosition->setUpVector(cAudio::cVector3(0, 1, 0));
96 
97  return true;
98 }
100 {
101  HandledAudioSources.clear();
102 
103  if(AudioManager) {
104 
105  cAudio::destroyAudioManager(AudioManager);
106  AudioManager = nullptr;
107  }
108 }
109 // ------------------------------------ //
110 void SoundDevice::Tick(int PassedMs)
111 {
112  for(auto iter = HandledAudioSources.begin(); iter != HandledAudioSources.end(); ++iter) {
113 
114  if(!(*iter)->Get()->isPlaying()) {
115 
116  iter = HandledAudioSources.erase(iter);
117  }
118  }
119 }
120 
122  const Float3& pos, const Float4& orientation)
123 {
124  // we need to create a vector from the angles //
125  // Float3 vec = Float3(-sin(pitchyawroll.X*DEGREES_TO_RADIANS),
126  // sin(pitchyawroll.Y*DEGREES_TO_RADIANS), -cos(pitchyawroll.X*DEGREES_TO_RADIANS));
127 
128  if(!ListeningPosition)
129  return;
130 
131  ListeningPosition->move(cAudio::cVector3(pos.X, pos.Y, pos.Z));
132 
133  bs::Quaternion quaternion(orientation);
134 
135  bs::Radian angle;
136  bs::Vector3 direction;
137 
138  // TODO: does this do the right thing?
139  quaternion.toAxisAngle(direction, angle); // toAngleAxis(angle, direction);
140 
141  ListeningPosition->setDirection(cAudio::cVector3(direction.x, direction.y, direction.z));
142 }
143 
145 {
146  if(!AudioManager)
147  return;
148 
149  vol = std::clamp(vol, 0.f, 1.f);
150 
151  AudioManager->setMasterVolume(vol);
152 }
153 // ------------------------------------ //
154 DLLEXPORT void SoundDevice::Play2DSoundEffect(const std::string& filename)
155 {
156  if(!AudioManager)
157  return;
158 
159  cAudio::IAudioSource* source = AudioManager->play2D(filename.c_str(), false, false);
160 
161  if(source) {
162 
163  LOG_ERROR("SoundDevice: Play2DSoundEffect: shouldn't return a source but it did. "
164  "Babysitting it");
165 
167  [=]() { this->BabysitAudio(AudioSource::MakeShared<AudioSource>(source, this)); });
168  }
169 }
170 
172  const std::string& filename, bool looping, bool startpaused)
173 {
174  if(!AudioManager)
175  return nullptr;
176 
177  if(!looping && !startpaused)
178  LOG_WARNING("SoundDevice: Play2DSound: called with same settings that "
179  "Play2DSoundEffect uses. looping or startpaused must be true to return an "
180  "AudioSource.");
181 
182  cAudio::IAudioSource* source =
183  AudioManager->play2D(filename.c_str(), looping, startpaused);
184 
185  if(!source) {
186  LOG_ERROR(
187  "SoundDevice: Play2DSound: failed to create IAudioSource from file: " + filename);
188 
189  if(!boost::filesystem::is_regular(filename))
190  LOG_INFO("SoundDevice: file '" + filename + "' doesn't exist");
191 
192  return nullptr;
193  }
194 
195  return AudioSource::MakeShared<AudioSource>(source, this);
196 }
197 
199  ProceduralSoundData::pointer data, const char* soundname)
200 {
201  if(!AudioManager || !data)
202  return nullptr;
203 
204  return AudioSource::MakeShared<AudioSource>(
205  AudioManager->createFromAudioDecoder(soundname, data->Properties.SourceName.c_str(),
206  CAUDIO_NEW ProceduralSoundStream(data)),
207  this);
208 }
209 // ------------------------------------ //
211 {
213 
214  HandledAudioSources.push_back(audio);
215 }
216 // ------------------------------------ //
217 DLLEXPORT std::vector<std::string> SoundDevice::GetAudioDevices(
218  size_t* indexofdefault /*= nullptr*/)
219 {
220  std::vector<std::string> result;
221 
222  cAudio::IAudioDeviceList* devices = cAudio::createAudioDeviceList();
223 
224  if(!devices) {
225  LOG_ERROR("SoundDevice: GetAudioDevices: failed to get audio device list");
226  return {};
227  }
228 
229  const auto deviceCount = devices->getDeviceCount();
230  result.reserve(deviceCount);
231 
232  const auto defaultDeviceName = devices->getDefaultDeviceName();
233 
234  for(unsigned int i = 0; i < deviceCount; ++i) {
235 
236  const auto deviceName = devices->getDeviceName(i);
237 
238  if(deviceName.compare(defaultDeviceName) == 0 && indexofdefault) {
239 
240  *indexofdefault = i;
241  }
242 
243  result.push_back(deviceName);
244  }
245 
246  CAUDIO_DELETE devices;
247 
248  return result;
249 }
Used to feed data retrieved from a callback to cAudio::IAudioSource.
DLLEXPORT bool Init(bool simulatesound=false, bool noconsolelog=false)
Definition: SoundDevice.cpp:26
#define LOG_INFO(x)
Definition: Define.h:88
#define LOG_ERROR(x)
Definition: Define.h:90
DLLEXPORT void Tick(int PassedMs)
DLLEXPORT void SetGlobalVolume(float vol)
static const StringTypeN RemoveExtension(const StringTypeN &filepath, bool delpath=true)
#define LOG_WARNING(x)
Definition: Define.h:89
DLLEXPORT NamedVars * GetValues()
Definition: AppDefine.cpp:40
DLLEXPORT void RunOnMainThread(const std::function< void()> &function)
Runs the function now if on the main thread otherwise calls Invoke.
Definition: Engine.cpp:1185
DLLEXPORT AudioSource::pointer Play2DSound(const std::string &filename, bool looping, bool startpaused)
Plays a 2d sound with options.
#define LEVIATHAN_ASSERT(x, msg)
Definition: Define.h:98
DLLEXPORT void BabysitAudio(AudioSource::pointer audio)
This class holds the audio source until it has finished playing and then releases the reference...
static DLLEXPORT std::vector< std::string > GetAudioDevices(size_t *indexofdefault=nullptr)
Returns a list of audio playback devices.
DLLEXPORT AudioSource::pointer CreateProceduralSound(ProceduralSoundData::pointer data, const char *soundname)
Opens an audio source from a procedural data stream.
DLLEXPORT void Play2DSoundEffect(const std::string &filename)
Plays a 2d sound without possibility of interrupting.
static DLLEXPORT Logger * Get()
Definition: Logger.cpp:106
DLLEXPORT void SetSoundListenerPosition(const Float3 &pos, const Float4 &orientation)
Loads the file and plays the sound.
DLLEXPORT ~SoundDevice()
Definition: SoundDevice.cpp:21
AppDef * GetDefinition()
Definition: Engine.h:205
boost::intrusive_ptr< JSProxyable > pointer
Definition: JSProxyable.h:15
#define DLLEXPORT
Definition: Include.h:84
DLLEXPORT void Release()
Definition: SoundDevice.cpp:99
static DLLEXPORT Engine * Get()
Definition: Engine.cpp:85
The access mask controls which registered functions and classes a script sees.
Definition: GameModule.h:12
DLLEXPORT void AssertIfNotMainThread() const
Asserts if not called on the main thread.
Definition: Engine.h:105