]> git.lizzy.rs Git - dragonfireclient.git/blob - src/client/sound_openal.cpp
Merge branch 'master' of https://github.com/minetest/minetest
[dragonfireclient.git] / src / client / sound_openal.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 OpenAL support based on work by:
5 Copyright (C) 2011 Sebastian 'Bahamada' Rühl
6 Copyright (C) 2011 Cyriaque 'Cisoun' Skrapits <cysoun@gmail.com>
7 Copyright (C) 2011 Giuseppe Bilotta <giuseppe.bilotta@gmail.com>
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public License along
20 with this program; ifnot, write to the Free Software Foundation, Inc.,
21 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 */
23
24 #include "sound_openal.h"
25
26 #if defined(_WIN32)
27         #include <al.h>
28         #include <alc.h>
29         //#include <alext.h>
30 #elif defined(__APPLE__)
31         #define OPENAL_DEPRECATED
32         #include <OpenAL/al.h>
33         #include <OpenAL/alc.h>
34         //#include <OpenAL/alext.h>
35 #else
36         #include <AL/al.h>
37         #include <AL/alc.h>
38         #include <AL/alext.h>
39 #endif
40 #include <cmath>
41 #include <vorbis/vorbisfile.h>
42 #include <cassert>
43 #include "log.h"
44 #include "util/numeric.h" // myrand()
45 #include "porting.h"
46 #include <vector>
47 #include <fstream>
48 #include <unordered_map>
49 #include <unordered_set>
50
51 #define BUFFER_SIZE 30000
52
53 std::shared_ptr<SoundManagerSingleton> g_sound_manager_singleton;
54
55 typedef std::unique_ptr<ALCdevice, void (*)(ALCdevice *p)> unique_ptr_alcdevice;
56 typedef std::unique_ptr<ALCcontext, void(*)(ALCcontext *p)> unique_ptr_alccontext;
57
58 static void delete_alcdevice(ALCdevice *p)
59 {
60         if (p)
61                 alcCloseDevice(p);
62 }
63
64 static void delete_alccontext(ALCcontext *p)
65 {
66         if (p) {
67                 alcMakeContextCurrent(nullptr);
68                 alcDestroyContext(p);
69         }
70 }
71
72 static const char *alErrorString(ALenum err)
73 {
74         switch (err) {
75         case AL_NO_ERROR:
76                 return "no error";
77         case AL_INVALID_NAME:
78                 return "invalid name";
79         case AL_INVALID_ENUM:
80                 return "invalid enum";
81         case AL_INVALID_VALUE:
82                 return "invalid value";
83         case AL_INVALID_OPERATION:
84                 return "invalid operation";
85         case AL_OUT_OF_MEMORY:
86                 return "out of memory";
87         default:
88                 return "<unknown OpenAL error>";
89         }
90 }
91
92 static ALenum warn_if_error(ALenum err, const char *desc)
93 {
94         if(err == AL_NO_ERROR)
95                 return err;
96         warningstream<<desc<<": "<<alErrorString(err)<<std::endl;
97         return err;
98 }
99
100 void f3_set(ALfloat *f3, v3f v)
101 {
102         f3[0] = v.X;
103         f3[1] = v.Y;
104         f3[2] = v.Z;
105 }
106
107 struct SoundBuffer
108 {
109         ALenum format;
110         ALsizei freq;
111         ALuint buffer_id;
112         std::vector<char> buffer;
113 };
114
115 SoundBuffer *load_opened_ogg_file(OggVorbis_File *oggFile,
116                 const std::string &filename_for_logging)
117 {
118         int endian = 0; // 0 for Little-Endian, 1 for Big-Endian
119         int bitStream;
120         long bytes;
121         char array[BUFFER_SIZE]; // Local fixed size array
122         vorbis_info *pInfo;
123
124         SoundBuffer *snd = new SoundBuffer;
125
126         // Get some information about the OGG file
127         pInfo = ov_info(oggFile, -1);
128
129         // Check the number of channels... always use 16-bit samples
130         if(pInfo->channels == 1)
131                 snd->format = AL_FORMAT_MONO16;
132         else
133                 snd->format = AL_FORMAT_STEREO16;
134
135         // The frequency of the sampling rate
136         snd->freq = pInfo->rate;
137
138         // Keep reading until all is read
139         do
140         {
141                 // Read up to a buffer's worth of decoded sound data
142                 bytes = ov_read(oggFile, array, BUFFER_SIZE, endian, 2, 1, &bitStream);
143
144                 if(bytes < 0)
145                 {
146                         ov_clear(oggFile);
147                         infostream << "Audio: Error decoding "
148                                 << filename_for_logging << std::endl;
149                         delete snd;
150                         return nullptr;
151                 }
152
153                 // Append to end of buffer
154                 snd->buffer.insert(snd->buffer.end(), array, array + bytes);
155         } while (bytes > 0);
156
157         alGenBuffers(1, &snd->buffer_id);
158         alBufferData(snd->buffer_id, snd->format,
159                         &(snd->buffer[0]), snd->buffer.size(),
160                         snd->freq);
161
162         ALenum error = alGetError();
163
164         if(error != AL_NO_ERROR){
165                 infostream << "Audio: OpenAL error: " << alErrorString(error)
166                                 << "preparing sound buffer" << std::endl;
167         }
168
169         //infostream << "Audio file "
170         //      << filename_for_logging << " loaded" << std::endl;
171
172         // Clean up!
173         ov_clear(oggFile);
174
175         return snd;
176 }
177
178 SoundBuffer *load_ogg_from_file(const std::string &path)
179 {
180         OggVorbis_File oggFile;
181
182         // Try opening the given file.
183         // This requires libvorbis >= 1.3.2, as
184         // previous versions expect a non-const char *
185         if (ov_fopen(path.c_str(), &oggFile) != 0) {
186                 infostream << "Audio: Error opening " << path
187                         << " for decoding" << std::endl;
188                 return nullptr;
189         }
190
191         return load_opened_ogg_file(&oggFile, path);
192 }
193
194 struct BufferSource {
195         const char *buf;
196         size_t cur_offset;
197         size_t len;
198 };
199
200 size_t buffer_sound_read_func(void *ptr, size_t size, size_t nmemb, void *datasource)
201 {
202         BufferSource *s = (BufferSource *)datasource;
203         size_t copied_size = MYMIN(s->len - s->cur_offset, size);
204         memcpy(ptr, s->buf + s->cur_offset, copied_size);
205         s->cur_offset += copied_size;
206         return copied_size;
207 }
208
209 int buffer_sound_seek_func(void *datasource, ogg_int64_t offset, int whence)
210 {
211         BufferSource *s = (BufferSource *)datasource;
212         if (whence == SEEK_SET) {
213                 if (offset < 0 || (size_t)MYMAX(offset, 0) >= s->len) {
214                         // offset out of bounds
215                         return -1;
216                 }
217                 s->cur_offset = offset;
218                 return 0;
219         } else if (whence == SEEK_CUR) {
220                 if ((size_t)MYMIN(-offset, 0) > s->cur_offset
221                                 || s->cur_offset + offset > s->len) {
222                         // offset out of bounds
223                         return -1;
224                 }
225                 s->cur_offset += offset;
226                 return 0;
227         }
228         // invalid whence param (SEEK_END doesn't have to be supported)
229         return -1;
230 }
231
232 long BufferSourceell_func(void *datasource)
233 {
234         BufferSource *s = (BufferSource *)datasource;
235         return s->cur_offset;
236 }
237
238 static ov_callbacks g_buffer_ov_callbacks = {
239         &buffer_sound_read_func,
240         &buffer_sound_seek_func,
241         nullptr,
242         &BufferSourceell_func
243 };
244
245 SoundBuffer *load_ogg_from_buffer(const std::string &buf, const std::string &id_for_log)
246 {
247         OggVorbis_File oggFile;
248
249         BufferSource s;
250         s.buf = buf.c_str();
251         s.cur_offset = 0;
252         s.len = buf.size();
253
254         if (ov_open_callbacks(&s, &oggFile, nullptr, 0, g_buffer_ov_callbacks) != 0) {
255                 infostream << "Audio: Error opening " << id_for_log
256                         << " for decoding" << std::endl;
257                 return nullptr;
258         }
259
260         return load_opened_ogg_file(&oggFile, id_for_log);
261 }
262
263 struct PlayingSound
264 {
265         ALuint source_id;
266         bool loop;
267 };
268
269 class SoundManagerSingleton
270 {
271 public:
272         unique_ptr_alcdevice  m_device;
273         unique_ptr_alccontext m_context;
274 public:
275         SoundManagerSingleton() :
276                 m_device(nullptr, delete_alcdevice),
277                 m_context(nullptr, delete_alccontext)
278         {
279         }
280
281         bool init()
282         {
283                 if (!(m_device = unique_ptr_alcdevice(alcOpenDevice(nullptr), delete_alcdevice))) {
284                         errorstream << "Audio: Global Initialization: Failed to open device" << std::endl;
285                         return false;
286                 }
287
288                 if (!(m_context = unique_ptr_alccontext(
289                                 alcCreateContext(m_device.get(), nullptr), delete_alccontext))) {
290                         errorstream << "Audio: Global Initialization: Failed to create context" << std::endl;
291                         return false;
292                 }
293
294                 if (!alcMakeContextCurrent(m_context.get())) {
295                         errorstream << "Audio: Global Initialization: Failed to make current context" << std::endl;
296                         return false;
297                 }
298
299                 alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
300
301                 if (alGetError() != AL_NO_ERROR) {
302                         errorstream << "Audio: Global Initialization: OpenAL Error " << alGetError() << std::endl;
303                         return false;
304                 }
305
306                 infostream << "Audio: Global Initialized: OpenAL " << alGetString(AL_VERSION)
307                         << ", using " << alcGetString(m_device.get(), ALC_DEVICE_SPECIFIER)
308                         << std::endl;
309
310                 return true;
311         }
312
313         ~SoundManagerSingleton()
314         {
315                 infostream << "Audio: Global Deinitialized." << std::endl;
316         }
317 };
318
319 class OpenALSoundManager: public ISoundManager
320 {
321 private:
322         OnDemandSoundFetcher *m_fetcher;
323         ALCdevice *m_device;
324         ALCcontext *m_context;
325         int m_next_id;
326         std::unordered_map<std::string, std::vector<SoundBuffer*>> m_buffers;
327         std::unordered_map<int, PlayingSound*> m_sounds_playing;
328         struct FadeState {
329                 FadeState() = default;
330
331                 FadeState(float step, float current_gain, float target_gain):
332                         step(step),
333                         current_gain(current_gain),
334                         target_gain(target_gain) {}
335                 float step;
336                 float current_gain;
337                 float target_gain;
338         };
339
340         std::unordered_map<int, FadeState> m_sounds_fading;
341 public:
342         OpenALSoundManager(SoundManagerSingleton *smg, OnDemandSoundFetcher *fetcher):
343                 m_fetcher(fetcher),
344                 m_device(smg->m_device.get()),
345                 m_context(smg->m_context.get()),
346                 m_next_id(1)
347         {
348                 infostream << "Audio: Initialized: OpenAL " << std::endl;
349         }
350
351         ~OpenALSoundManager()
352         {
353                 infostream << "Audio: Deinitializing..." << std::endl;
354
355                 std::unordered_set<int> source_del_list;
356
357                 for (const auto &sp : m_sounds_playing)
358                         source_del_list.insert(sp.first);
359
360                 for (const auto &id : source_del_list)
361                         deleteSound(id);
362
363                 for (auto &buffer : m_buffers) {
364                         for (SoundBuffer *sb : buffer.second) {
365                                 delete sb;
366                         }
367                         buffer.second.clear();
368                 }
369                 m_buffers.clear();
370
371                 infostream << "Audio: Deinitialized." << std::endl;
372         }
373
374         void step(float dtime)
375         {
376                 doFades(dtime);
377         }
378
379         void addBuffer(const std::string &name, SoundBuffer *buf)
380         {
381                 std::unordered_map<std::string, std::vector<SoundBuffer*>>::iterator i =
382                                 m_buffers.find(name);
383                 if(i != m_buffers.end()){
384                         i->second.push_back(buf);
385                         return;
386                 }
387                 std::vector<SoundBuffer*> bufs;
388                 bufs.push_back(buf);
389                 m_buffers[name] = bufs;
390         }
391
392         SoundBuffer* getBuffer(const std::string &name)
393         {
394                 std::unordered_map<std::string, std::vector<SoundBuffer*>>::iterator i =
395                                 m_buffers.find(name);
396                 if(i == m_buffers.end())
397                         return nullptr;
398                 std::vector<SoundBuffer*> &bufs = i->second;
399                 int j = myrand() % bufs.size();
400                 return bufs[j];
401         }
402
403         PlayingSound* createPlayingSound(SoundBuffer *buf, bool loop,
404                         float volume, float pitch)
405         {
406                 infostream << "OpenALSoundManager: Creating playing sound" << std::endl;
407                 assert(buf);
408                 PlayingSound *sound = new PlayingSound;
409                 assert(sound);
410                 warn_if_error(alGetError(), "before createPlayingSound");
411                 alGenSources(1, &sound->source_id);
412                 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
413                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, true);
414                 alSource3f(sound->source_id, AL_POSITION, 0, 0, 0);
415                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
416                 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
417                 volume = std::fmax(0.0f, volume);
418                 alSourcef(sound->source_id, AL_GAIN, volume);
419                 alSourcef(sound->source_id, AL_PITCH, pitch);
420                 alSourcePlay(sound->source_id);
421                 warn_if_error(alGetError(), "createPlayingSound");
422                 return sound;
423         }
424
425         PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop,
426                         float volume, v3f pos, float pitch)
427         {
428                 infostream << "OpenALSoundManager: Creating positional playing sound"
429                                 << std::endl;
430                 assert(buf);
431                 PlayingSound *sound = new PlayingSound;
432                 assert(sound);
433                 warn_if_error(alGetError(), "before createPlayingSoundAt");
434                 alGenSources(1, &sound->source_id);
435                 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
436                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
437                 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
438                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
439                 // Use alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED) and set reference
440                 // distance to clamp gain at <1 node distance, to avoid excessive
441                 // volume when closer
442                 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 10.0f);
443                 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
444                 // Multiply by 3 to compensate for reducing AL_REFERENCE_DISTANCE from
445                 // the previous value of 30 to the new value of 10
446                 volume = std::fmax(0.0f, volume * 3.0f);
447                 alSourcef(sound->source_id, AL_GAIN, volume);
448                 alSourcef(sound->source_id, AL_PITCH, pitch);
449                 alSourcePlay(sound->source_id);
450                 warn_if_error(alGetError(), "createPlayingSoundAt");
451                 return sound;
452         }
453
454         int playSoundRaw(SoundBuffer *buf, bool loop, float volume, float pitch)
455         {
456                 assert(buf);
457                 PlayingSound *sound = createPlayingSound(buf, loop, volume, pitch);
458                 if(!sound)
459                         return -1;
460                 int id = m_next_id++;
461                 m_sounds_playing[id] = sound;
462                 return id;
463         }
464
465         int playSoundRawAt(SoundBuffer *buf, bool loop, float volume, const v3f &pos,
466                         float pitch)
467         {
468                 assert(buf);
469                 PlayingSound *sound = createPlayingSoundAt(buf, loop, volume, pos, pitch);
470                 if(!sound)
471                         return -1;
472                 int id = m_next_id++;
473                 m_sounds_playing[id] = sound;
474                 return id;
475         }
476
477         void deleteSound(int id)
478         {
479                 std::unordered_map<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
480                 if(i == m_sounds_playing.end())
481                         return;
482                 PlayingSound *sound = i->second;
483
484                 alDeleteSources(1, &sound->source_id);
485
486                 delete sound;
487                 m_sounds_playing.erase(id);
488         }
489
490         /* If buffer does not exist, consult the fetcher */
491         SoundBuffer* getFetchBuffer(const std::string &name)
492         {
493                 SoundBuffer *buf = getBuffer(name);
494                 if(buf)
495                         return buf;
496                 if(!m_fetcher)
497                         return nullptr;
498                 std::set<std::string> paths;
499                 std::set<std::string> datas;
500                 m_fetcher->fetchSounds(name, paths, datas);
501                 for (const std::string &path : paths) {
502                         loadSoundFile(name, path);
503                 }
504                 for (const std::string &data : datas) {
505                         loadSoundData(name, data);
506                 }
507                 return getBuffer(name);
508         }
509
510         // Remove stopped sounds
511         void maintain()
512         {
513                 if (!m_sounds_playing.empty()) {
514                         verbosestream << "OpenALSoundManager::maintain(): "
515                                         << m_sounds_playing.size() <<" playing sounds, "
516                                         << m_buffers.size() <<" sound names loaded"<<std::endl;
517                 }
518                 std::unordered_set<int> del_list;
519                 for (const auto &sp : m_sounds_playing) {
520                         int id = sp.first;
521                         PlayingSound *sound = sp.second;
522                         // If not playing, remove it
523                         {
524                                 ALint state;
525                                 alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state);
526                                 if(state != AL_PLAYING){
527                                         del_list.insert(id);
528                                 }
529                         }
530                 }
531                 if(!del_list.empty())
532                         verbosestream<<"OpenALSoundManager::maintain(): deleting "
533                                         <<del_list.size()<<" playing sounds"<<std::endl;
534                 for (int i : del_list) {
535                         deleteSound(i);
536                 }
537         }
538
539         /* Interface */
540
541         bool loadSoundFile(const std::string &name,
542                         const std::string &filepath)
543         {
544                 SoundBuffer *buf = load_ogg_from_file(filepath);
545                 if (buf)
546                         addBuffer(name, buf);
547                 return !!buf;
548         }
549
550         bool loadSoundData(const std::string &name,
551                         const std::string &filedata)
552         {
553                 SoundBuffer *buf = load_ogg_from_buffer(filedata, name);
554                 if (buf)
555                         addBuffer(name, buf);
556                 return !!buf;
557         }
558
559         void updateListener(const v3f &pos, const v3f &vel, const v3f &at, const v3f &up)
560         {
561                 alListener3f(AL_POSITION, pos.X, pos.Y, pos.Z);
562                 alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z);
563                 ALfloat f[6];
564                 f3_set(f, at);
565                 f3_set(f+3, -up);
566                 alListenerfv(AL_ORIENTATION, f);
567                 warn_if_error(alGetError(), "updateListener");
568         }
569
570         void setListenerGain(float gain)
571         {
572                 alListenerf(AL_GAIN, gain);
573         }
574
575         int playSound(const std::string &name, bool loop, float volume, float fade, float pitch)
576         {
577                 maintain();
578                 if (name.empty())
579                         return 0;
580                 SoundBuffer *buf = getFetchBuffer(name);
581                 if(!buf){
582                         infostream << "OpenALSoundManager: \"" << name << "\" not found."
583                                         << std::endl;
584                         return -1;
585                 }
586                 int handle = -1;
587                 if (fade > 0) {
588                         handle = playSoundRaw(buf, loop, 0.0f, pitch);
589                         fadeSound(handle, fade, volume);
590                 } else {
591                         handle = playSoundRaw(buf, loop, volume, pitch);
592                 }
593                 return handle;
594         }
595
596         int playSoundAt(const std::string &name, bool loop, float volume, v3f pos, float pitch)
597         {
598                 maintain();
599                 if (name.empty())
600                         return 0;
601                 SoundBuffer *buf = getFetchBuffer(name);
602                 if(!buf){
603                         infostream << "OpenALSoundManager: \"" << name << "\" not found."
604                                         << std::endl;
605                         return -1;
606                 }
607                 return playSoundRawAt(buf, loop, volume, pos, pitch);
608         }
609
610         void stopSound(int sound)
611         {
612                 maintain();
613                 deleteSound(sound);
614         }
615
616         void fadeSound(int soundid, float step, float gain)
617         {
618                 // Ignore the command if step isn't valid.
619                 if (step == 0)
620                         return;
621                 float current_gain = getSoundGain(soundid);
622                 step = gain - current_gain > 0 ? abs(step) : -abs(step);
623                 if (m_sounds_fading.find(soundid) != m_sounds_fading.end()) {
624                         auto current_fade = m_sounds_fading[soundid];
625                         // Do not replace the fade if it's equivalent.
626                         if (current_fade.target_gain == gain && current_fade.step == step)
627                                 return;
628                         m_sounds_fading.erase(soundid);
629                 }
630                 gain = rangelim(gain, 0, 1);
631                 m_sounds_fading[soundid] = FadeState(step, current_gain, gain);
632         }
633
634         void doFades(float dtime)
635         {
636                 for (auto i = m_sounds_fading.begin(); i != m_sounds_fading.end();) {
637                         FadeState& fade = i->second;
638                         assert(fade.step != 0);
639                         fade.current_gain += (fade.step * dtime);
640
641                         if (fade.step < 0.f)
642                                 fade.current_gain = std::max(fade.current_gain, fade.target_gain);
643                         else
644                                 fade.current_gain = std::min(fade.current_gain, fade.target_gain);
645
646                         if (fade.current_gain <= 0.f)
647                                 stopSound(i->first);
648                         else
649                                 updateSoundGain(i->first, fade.current_gain);
650
651                         // The increment must happen during the erase call, or else it'll segfault.
652                         if (fade.current_gain == fade.target_gain)
653                                 m_sounds_fading.erase(i++);
654                         else
655                                 i++;
656                 }
657         }
658
659         bool soundExists(int sound)
660         {
661                 maintain();
662                 return (m_sounds_playing.count(sound) != 0);
663         }
664
665         void updateSoundPosition(int id, v3f pos)
666         {
667                 auto i = m_sounds_playing.find(id);
668                 if (i == m_sounds_playing.end())
669                         return;
670                 PlayingSound *sound = i->second;
671
672                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
673                 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
674                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
675                 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
676         }
677
678         bool updateSoundGain(int id, float gain)
679         {
680                 auto i = m_sounds_playing.find(id);
681                 if (i == m_sounds_playing.end())
682                         return false;
683
684                 PlayingSound *sound = i->second;
685                 alSourcef(sound->source_id, AL_GAIN, gain);
686                 return true;
687         }
688
689         float getSoundGain(int id)
690         {
691                 auto i = m_sounds_playing.find(id);
692                 if (i == m_sounds_playing.end())
693                         return 0;
694
695                 PlayingSound *sound = i->second;
696                 ALfloat gain;
697                 alGetSourcef(sound->source_id, AL_GAIN, &gain);
698                 return gain;
699         }
700 };
701
702 std::shared_ptr<SoundManagerSingleton> createSoundManagerSingleton()
703 {
704         auto smg = std::make_shared<SoundManagerSingleton>();
705         if (!smg->init()) {
706                 smg.reset();
707         }
708         return smg;
709 }
710
711 ISoundManager *createOpenALSoundManager(SoundManagerSingleton *smg, OnDemandSoundFetcher *fetcher)
712 {
713         return new OpenALSoundManager(smg, fetcher);
714 };