Otclient  14/8/2020
soundmanager.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2010-2020 OTClient <https://github.com/edubart/otclient>
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20  * THE SOFTWARE.
21  */
22 
23 #include "soundmanager.h"
24 #include "soundsource.h"
25 #include "soundbuffer.h"
26 #include "soundfile.h"
27 #include "streamsoundsource.h"
28 #include "combinedsoundsource.h"
29 
30 #include <framework/core/clock.h>
34 #include <thread>
35 
37 
39 {
40  m_device = alcOpenDevice(nullptr);
41  if(!m_device) {
42  g_logger.error("unable to open audio device");
43  return;
44  }
45 
46  m_context = alcCreateContext(m_device, nullptr);
47  if(!m_context) {
48  g_logger.error(stdext::format("unable to create audio context: %s", alcGetString(m_device, alcGetError(m_device))));
49  return;
50  }
51 
52  if(alcMakeContextCurrent(m_context) != ALC_TRUE) {
53  g_logger.error(stdext::format("unable to make context current: %s", alcGetString(m_device, alcGetError(m_device))));
54  return;
55  }
56 }
57 
59 {
60  ensureContext();
61 
62  for(auto &streamFile: m_streamFiles) {
63  auto& future = streamFile.second;
64  future.wait();
65  }
66  m_streamFiles.clear();
67 
68  m_sources.clear();
69  m_buffers.clear();
70  m_channels.clear();
71 
72  m_audioEnabled = false;
73 
74  alcMakeContextCurrent(nullptr);
75 
76  if(m_context) {
77  alcDestroyContext(m_context);
78  m_context = nullptr;
79  }
80 
81  if(m_device) {
82  alcCloseDevice(m_device);
83  m_device = nullptr;
84  }
85 }
86 
88 {
89  static ticks_t lastUpdate = 0;
90  ticks_t now = g_clock.millis();
91 
92  if(now - lastUpdate < POLL_DELAY)
93  return;
94 
95  lastUpdate = now;
96 
97  ensureContext();
98 
99  for(auto it = m_streamFiles.begin(); it != m_streamFiles.end();) {
100  StreamSoundSourcePtr source = it->first;
101  auto& future = it->second;
102 
103  if(future.is_ready()) {
104  SoundFilePtr sound = future.get();
105  if(sound)
106  source->setSoundFile(sound);
107  else
108  source->stop();
109  it = m_streamFiles.erase(it);
110  } else {
111  ++it;
112  }
113  }
114 
115  for(auto it = m_sources.begin(); it != m_sources.end();) {
116  SoundSourcePtr source = *it;
117 
118  source->update();
119 
120  if(!source->isPlaying())
121  it = m_sources.erase(it);
122  else
123  ++it;
124  }
125 
126  for(auto it : m_channels) {
127  it.second->update();
128  }
129 
130  if(m_context) {
131  alcProcessContext(m_context);
132  }
133 }
134 
136 {
137  if(m_audioEnabled == enable)
138  return;
139 
140  m_audioEnabled = enable;
141  if(!enable) {
142  ensureContext();
143  for(const SoundSourcePtr& source : m_sources) {
144  source->stop();
145  }
146  }
147 }
148 
149 void SoundManager::preload(std::string filename)
150 {
151  filename = resolveSoundFile(filename);
152 
153  auto it = m_buffers.find(filename);
154  if(it != m_buffers.end())
155  return;
156 
157  ensureContext();
158  SoundFilePtr soundFile = SoundFile::loadSoundFile(filename);
159 
160  // only keep small files
161  if(!soundFile || soundFile->getSize() > MAX_CACHE_SIZE)
162  return;
163 
165  if(buffer->fillBuffer(soundFile))
166  m_buffers[filename] = buffer;
167 }
168 
169 SoundSourcePtr SoundManager::play(std::string filename, float fadetime, float gain)
170 {
171  if(!m_audioEnabled)
172  return nullptr;
173 
174  ensureContext();
175 
176  if(gain == 0)
177  gain = 1.0f;
178 
179  filename = resolveSoundFile(filename);
180  SoundSourcePtr soundSource = createSoundSource(filename);
181  if(!soundSource) {
182  g_logger.error(stdext::format("unable to play '%s'", filename));
183  return nullptr;
184  }
185 
186  soundSource->setName(filename);
187  soundSource->setRelative(true);
188  soundSource->setGain(gain);
189 
190  if(fadetime > 0)
191  soundSource->setFading(StreamSoundSource::FadingOn, fadetime);
192 
193  soundSource->play();
194 
195  m_sources.push_back(soundSource);
196 
197  return soundSource;
198 }
199 
201 {
202  ensureContext();
203  if(!m_channels[channel])
204  m_channels[channel] = SoundChannelPtr(new SoundChannel(channel));
205  return m_channels[channel];
206 }
207 
209 {
210  ensureContext();
211  for(const SoundSourcePtr& source : m_sources) {
212  source->stop();
213  }
214 
215  for(auto it : m_channels) {
216  it.second->stop();
217  }
218 }
219 
220 SoundSourcePtr SoundManager::createSoundSource(const std::string& filename)
221 {
222  SoundSourcePtr source;
223 
224  try {
225  auto it = m_buffers.find(filename);
226  if(it != m_buffers.end()) {
227  source = SoundSourcePtr(new SoundSource);
228  source->setBuffer(it->second);
229  } else {
230 #if defined __linux && !defined OPENGL_ES
231  // due to OpenAL implementation bug, stereo buffers are always downmixed to mono on linux systems
232  // this is hack to work around the issue
233  // solution taken from http://opensource.creative.com/pipermail/openal/2007-April/010355.html
234  CombinedSoundSourcePtr combinedSource(new CombinedSoundSource);
235  StreamSoundSourcePtr streamSource;
236 
237  streamSource = StreamSoundSourcePtr(new StreamSoundSource);
238  streamSource->downMix(StreamSoundSource::DownMixLeft);
239  streamSource->setRelative(true);
240  streamSource->setPosition(Point(-128, 0));
241  combinedSource->addSource(streamSource);
242  m_streamFiles[streamSource] = g_asyncDispatcher.schedule([=]() -> SoundFilePtr {
243  stdext::timer a;
244  try {
245  return SoundFile::loadSoundFile(filename);
246  } catch(std::exception& e) {
247  g_logger.error(e.what());
248  return nullptr;
249  }
250  });
251 
252  streamSource = StreamSoundSourcePtr(new StreamSoundSource);
253  streamSource->downMix(StreamSoundSource::DownMixRight);
254  streamSource->setRelative(true);
255  streamSource->setPosition(Point(128,0));
256  combinedSource->addSource(streamSource);
257  m_streamFiles[streamSource] = g_asyncDispatcher.schedule([=]() -> SoundFilePtr {
258  try {
259  return SoundFile::loadSoundFile(filename);
260  } catch(std::exception& e) {
261  g_logger.error(e.what());
262  return nullptr;
263  }
264  });
265 
266  source = combinedSource;
267 #else
268  StreamSoundSourcePtr streamSource(new StreamSoundSource);
269  m_streamFiles[streamSource] = g_asyncDispatcher.schedule([=]() -> SoundFilePtr {
270  try {
271  return SoundFile::loadSoundFile(filename);
272  } catch(std::exception& e) {
273  g_logger.error(e.what());
274  return nullptr;
275  }
276  });
277  source = streamSource;
278 #endif
279  }
280  } catch(std::exception& e) {
281  g_logger.error(stdext::format("failed to load sound source: '%s'", e.what()));
282  return nullptr;
283  }
284 
285  return source;
286 }
287 
288 std::string SoundManager::resolveSoundFile(std::string file)
289 {
290  file = g_resources.guessFilePath(file, "ogg");
291  file = g_resources.resolvePath(file);
292  return file;
293 }
294 
296 {
297  if(m_context)
298  alcMakeContextCurrent(m_context);
299 }
SoundSourcePtr
stdext::shared_object_ptr< SoundSource > SoundSourcePtr
Definition: declarations.h:40
stdext::timer
Definition: time.h:36
eventdispatcher.h
SoundManager::stopAll
void stopAll()
Definition: soundmanager.cpp:208
SoundManager::preload
void preload(std::string filename)
Definition: soundmanager.cpp:149
SoundSource
Definition: soundsource.h:30
StreamSoundSourcePtr
stdext::shared_object_ptr< StreamSoundSource > StreamSoundSourcePtr
Definition: declarations.h:46
combinedsoundsource.h
AsyncDispatcher::schedule
boost::shared_future< typename std::result_of< F()>::type > schedule(const F &task)
Definition: asyncdispatcher.h:38
SoundManager::resolveSoundFile
std::string resolveSoundFile(std::string file)
Definition: soundmanager.cpp:288
resourcemanager.h
ResourceManager::resolvePath
std::string resolvePath(const std::string &path)
Definition: resourcemanager.cpp:311
SoundManager::getChannel
SoundChannelPtr getChannel(int channel)
Definition: soundmanager.cpp:200
Logger::error
void error(const std::string &what)
Definition: logger.h:54
ticks_t
int64 ticks_t
Definition: types.h:43
SoundSource::isPlaying
virtual bool isPlaying()
Definition: soundsource.h:45
soundbuffer.h
Point
TPoint< int > Point
Definition: point.h:86
SoundSource::FadingOn
@ FadingOn
Definition: soundsource.h:36
stdext::format
std::string format()
Definition: format.h:82
SoundFile::getSize
int getSize()
Definition: soundfile.h:45
clock.h
g_sounds
SoundManager g_sounds
Definition: soundmanager.cpp:36
asyncdispatcher.h
SoundSource::setFading
virtual void setFading(FadeState state, float fadetime)
Definition: soundsource.cpp:119
g_resources
ResourceManager g_resources
Definition: resourcemanager.cpp:32
SoundChannelPtr
stdext::shared_object_ptr< SoundChannel > SoundChannelPtr
Definition: declarations.h:45
soundfile.h
StreamSoundSource::DownMixLeft
@ DownMixLeft
Definition: streamsoundsource.h:37
SoundManager::ensureContext
void ensureContext()
Definition: soundmanager.cpp:295
g_logger
Logger g_logger
Definition: logger.cpp:35
SoundManager
Definition: soundmanager.h:30
SoundSource::play
virtual void play()
Definition: soundsource.cpp:52
StreamSoundSource
Definition: streamsoundsource.h:28
SoundManager::play
SoundSourcePtr play(std::string filename, float fadetime=0, float gain=0)
Definition: soundmanager.cpp:169
SoundManager::init
void init()
Definition: soundmanager.cpp:38
SoundSource::setName
void setName(const std::string &name)
Definition: soundsource.h:47
soundmanager.h
SoundBuffer
Definition: soundbuffer.h:30
SoundSource::setGain
virtual void setGain(float gain)
Definition: soundsource.cpp:98
SoundManager::poll
void poll()
Definition: soundmanager.cpp:87
SoundBuffer::fillBuffer
bool fillBuffer(const SoundFilePtr &soundFile)
Definition: soundbuffer.cpp:39
stdext::shared_object_ptr
Definition: shared_object.h:39
StreamSoundSource::DownMixRight
@ DownMixRight
Definition: streamsoundsource.h:37
SoundSource::update
virtual void update()
Definition: soundsource.cpp:141
streamsoundsource.h
soundsource.h
SoundManager::terminate
void terminate()
Definition: soundmanager.cpp:58
Clock::millis
ticks_t millis()
Definition: clock.h:37
SoundFile::loadSoundFile
static SoundFilePtr loadSoundFile(const std::string &filename)
Definition: soundfile.cpp:32
SoundSource::setRelative
virtual void setRelative(bool relative)
Definition: soundsource.cpp:88
SoundManager::setAudioEnabled
void setAudioEnabled(bool enable)
Definition: soundmanager.cpp:135
CombinedSoundSource
Definition: combinedsoundsource.h:28
SoundBufferPtr
stdext::shared_object_ptr< SoundBuffer > SoundBufferPtr
Definition: declarations.h:44
g_clock
Clock g_clock
Definition: clock.cpp:25
g_asyncDispatcher
AsyncDispatcher g_asyncDispatcher
Definition: asyncdispatcher.cpp:25
stdext::shared_object_ptr::get
T * get() const
Definition: shared_object.h:82
ResourceManager::guessFilePath
std::string guessFilePath(const std::string &filename, const std::string &type)
Definition: resourcemanager.cpp:352
SoundChannel
Definition: soundchannel.h:29