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>
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.
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.
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.
24 #include "sound_openal.h"
30 #elif defined(__APPLE__)
31 #define OPENAL_DEPRECATED
32 #include <OpenAL/al.h>
33 #include <OpenAL/alc.h>
34 //#include <OpenAL/alext.h>
41 #include <vorbis/vorbisfile.h>
44 #include "util/numeric.h" // myrand()
48 #include <unordered_map>
49 #include <unordered_set>
51 #define BUFFER_SIZE 30000
53 std::shared_ptr<SoundManagerSingleton> g_sound_manager_singleton;
55 typedef std::unique_ptr<ALCdevice, void (*)(ALCdevice *p)> unique_ptr_alcdevice;
56 typedef std::unique_ptr<ALCcontext, void(*)(ALCcontext *p)> unique_ptr_alccontext;
58 static void delete_alcdevice(ALCdevice *p)
64 static void delete_alccontext(ALCcontext *p)
67 alcMakeContextCurrent(nullptr);
72 static const char *alErrorString(ALenum err)
78 return "invalid name";
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";
88 return "<unknown OpenAL error>";
92 static ALenum warn_if_error(ALenum err, const char *desc)
94 if(err == AL_NO_ERROR)
96 warningstream<<desc<<": "<<alErrorString(err)<<std::endl;
100 void f3_set(ALfloat *f3, v3f v)
112 std::vector<char> buffer;
115 SoundBuffer *load_opened_ogg_file(OggVorbis_File *oggFile,
116 const std::string &filename_for_logging)
118 int endian = 0; // 0 for Little-Endian, 1 for Big-Endian
121 char array[BUFFER_SIZE]; // Local fixed size array
124 SoundBuffer *snd = new SoundBuffer;
126 // Get some information about the OGG file
127 pInfo = ov_info(oggFile, -1);
129 // Check the number of channels... always use 16-bit samples
130 if(pInfo->channels == 1)
131 snd->format = AL_FORMAT_MONO16;
133 snd->format = AL_FORMAT_STEREO16;
135 // The frequency of the sampling rate
136 snd->freq = pInfo->rate;
138 // Keep reading until all is read
141 // Read up to a buffer's worth of decoded sound data
142 bytes = ov_read(oggFile, array, BUFFER_SIZE, endian, 2, 1, &bitStream);
147 infostream << "Audio: Error decoding "
148 << filename_for_logging << std::endl;
153 // Append to end of buffer
154 snd->buffer.insert(snd->buffer.end(), array, array + bytes);
157 alGenBuffers(1, &snd->buffer_id);
158 alBufferData(snd->buffer_id, snd->format,
159 &(snd->buffer[0]), snd->buffer.size(),
162 ALenum error = alGetError();
164 if(error != AL_NO_ERROR){
165 infostream << "Audio: OpenAL error: " << alErrorString(error)
166 << "preparing sound buffer" << std::endl;
169 //infostream << "Audio file "
170 // << filename_for_logging << " loaded" << std::endl;
178 SoundBuffer *load_ogg_from_file(const std::string &path)
180 OggVorbis_File oggFile;
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;
191 return load_opened_ogg_file(&oggFile, path);
194 struct BufferSource {
200 size_t buffer_sound_read_func(void *ptr, size_t size, size_t nmemb, void *datasource)
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;
209 int buffer_sound_seek_func(void *datasource, ogg_int64_t offset, int whence)
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
217 s->cur_offset = offset;
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
225 s->cur_offset += offset;
228 // invalid whence param (SEEK_END doesn't have to be supported)
232 long BufferSourceell_func(void *datasource)
234 BufferSource *s = (BufferSource *)datasource;
235 return s->cur_offset;
238 static ov_callbacks g_buffer_ov_callbacks = {
239 &buffer_sound_read_func,
240 &buffer_sound_seek_func,
242 &BufferSourceell_func
245 SoundBuffer *load_ogg_from_buffer(const std::string &buf, const std::string &id_for_log)
247 OggVorbis_File oggFile;
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;
260 return load_opened_ogg_file(&oggFile, id_for_log);
269 class SoundManagerSingleton
272 unique_ptr_alcdevice m_device;
273 unique_ptr_alccontext m_context;
275 SoundManagerSingleton() :
276 m_device(nullptr, delete_alcdevice),
277 m_context(nullptr, delete_alccontext)
283 if (!(m_device = unique_ptr_alcdevice(alcOpenDevice(nullptr), delete_alcdevice))) {
284 errorstream << "Audio: Global Initialization: Failed to open device" << std::endl;
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;
294 if (!alcMakeContextCurrent(m_context.get())) {
295 errorstream << "Audio: Global Initialization: Failed to make current context" << std::endl;
299 alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
301 if (alGetError() != AL_NO_ERROR) {
302 errorstream << "Audio: Global Initialization: OpenAL Error " << alGetError() << std::endl;
306 infostream << "Audio: Global Initialized: OpenAL " << alGetString(AL_VERSION)
307 << ", using " << alcGetString(m_device.get(), ALC_DEVICE_SPECIFIER)
313 ~SoundManagerSingleton()
315 infostream << "Audio: Global Deinitialized." << std::endl;
319 class OpenALSoundManager: public ISoundManager
322 OnDemandSoundFetcher *m_fetcher;
324 ALCcontext *m_context;
326 std::unordered_map<std::string, std::vector<SoundBuffer*>> m_buffers;
327 std::unordered_map<int, PlayingSound*> m_sounds_playing;
329 FadeState() = default;
331 FadeState(float step, float current_gain, float target_gain):
333 current_gain(current_gain),
334 target_gain(target_gain) {}
340 std::unordered_map<int, FadeState> m_sounds_fading;
342 OpenALSoundManager(SoundManagerSingleton *smg, OnDemandSoundFetcher *fetcher):
344 m_device(smg->m_device.get()),
345 m_context(smg->m_context.get()),
348 infostream << "Audio: Initialized: OpenAL " << std::endl;
351 ~OpenALSoundManager()
353 infostream << "Audio: Deinitializing..." << std::endl;
355 std::unordered_set<int> source_del_list;
357 for (const auto &sp : m_sounds_playing)
358 source_del_list.insert(sp.first);
360 for (const auto &id : source_del_list)
363 for (auto &buffer : m_buffers) {
364 for (SoundBuffer *sb : buffer.second) {
367 buffer.second.clear();
371 infostream << "Audio: Deinitialized." << std::endl;
374 void step(float dtime)
379 void addBuffer(const std::string &name, SoundBuffer *buf)
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);
387 std::vector<SoundBuffer*> bufs;
389 m_buffers[name] = bufs;
392 SoundBuffer* getBuffer(const std::string &name)
394 std::unordered_map<std::string, std::vector<SoundBuffer*>>::iterator i =
395 m_buffers.find(name);
396 if(i == m_buffers.end())
398 std::vector<SoundBuffer*> &bufs = i->second;
399 int j = myrand() % bufs.size();
403 PlayingSound* createPlayingSound(SoundBuffer *buf, bool loop,
404 float volume, float pitch)
406 infostream << "OpenALSoundManager: Creating playing sound" << std::endl;
408 PlayingSound *sound = new PlayingSound;
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");
425 PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop,
426 float volume, v3f pos, float pitch)
428 infostream << "OpenALSoundManager: Creating positional playing sound"
431 PlayingSound *sound = new PlayingSound;
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");
454 int playSoundRaw(SoundBuffer *buf, bool loop, float volume, float pitch)
457 PlayingSound *sound = createPlayingSound(buf, loop, volume, pitch);
460 int id = m_next_id++;
461 m_sounds_playing[id] = sound;
465 int playSoundRawAt(SoundBuffer *buf, bool loop, float volume, const v3f &pos,
469 PlayingSound *sound = createPlayingSoundAt(buf, loop, volume, pos, pitch);
472 int id = m_next_id++;
473 m_sounds_playing[id] = sound;
477 void deleteSound(int id)
479 std::unordered_map<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
480 if(i == m_sounds_playing.end())
482 PlayingSound *sound = i->second;
484 alDeleteSources(1, &sound->source_id);
487 m_sounds_playing.erase(id);
490 /* If buffer does not exist, consult the fetcher */
491 SoundBuffer* getFetchBuffer(const std::string &name)
493 SoundBuffer *buf = getBuffer(name);
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);
504 for (const std::string &data : datas) {
505 loadSoundData(name, data);
507 return getBuffer(name);
510 // Remove stopped sounds
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;
518 std::unordered_set<int> del_list;
519 for (const auto &sp : m_sounds_playing) {
521 PlayingSound *sound = sp.second;
522 // If not playing, remove it
525 alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state);
526 if(state != AL_PLAYING){
531 if(!del_list.empty())
532 verbosestream<<"OpenALSoundManager::maintain(): deleting "
533 <<del_list.size()<<" playing sounds"<<std::endl;
534 for (int i : del_list) {
541 bool loadSoundFile(const std::string &name,
542 const std::string &filepath)
544 SoundBuffer *buf = load_ogg_from_file(filepath);
546 addBuffer(name, buf);
550 bool loadSoundData(const std::string &name,
551 const std::string &filedata)
553 SoundBuffer *buf = load_ogg_from_buffer(filedata, name);
555 addBuffer(name, buf);
559 void updateListener(const v3f &pos, const v3f &vel, const v3f &at, const v3f &up)
561 alListener3f(AL_POSITION, pos.X, pos.Y, pos.Z);
562 alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z);
566 alListenerfv(AL_ORIENTATION, f);
567 warn_if_error(alGetError(), "updateListener");
570 void setListenerGain(float gain)
572 alListenerf(AL_GAIN, gain);
575 int playSound(const std::string &name, bool loop, float volume, float fade, float pitch)
580 SoundBuffer *buf = getFetchBuffer(name);
582 infostream << "OpenALSoundManager: \"" << name << "\" not found."
588 handle = playSoundRaw(buf, loop, 0.0f, pitch);
589 fadeSound(handle, fade, volume);
591 handle = playSoundRaw(buf, loop, volume, pitch);
596 int playSoundAt(const std::string &name, bool loop, float volume, v3f pos, float pitch)
601 SoundBuffer *buf = getFetchBuffer(name);
603 infostream << "OpenALSoundManager: \"" << name << "\" not found."
607 return playSoundRawAt(buf, loop, volume, pos, pitch);
610 void stopSound(int sound)
616 void fadeSound(int soundid, float step, float gain)
618 // Ignore the command if step isn't valid.
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)
628 m_sounds_fading.erase(soundid);
630 gain = rangelim(gain, 0, 1);
631 m_sounds_fading[soundid] = FadeState(step, current_gain, gain);
634 void doFades(float dtime)
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);
642 fade.current_gain = std::max(fade.current_gain, fade.target_gain);
644 fade.current_gain = std::min(fade.current_gain, fade.target_gain);
646 if (fade.current_gain <= 0.f)
649 updateSoundGain(i->first, fade.current_gain);
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++);
659 bool soundExists(int sound)
662 return (m_sounds_playing.count(sound) != 0);
665 void updateSoundPosition(int id, v3f pos)
667 auto i = m_sounds_playing.find(id);
668 if (i == m_sounds_playing.end())
670 PlayingSound *sound = i->second;
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);
678 bool updateSoundGain(int id, float gain)
680 auto i = m_sounds_playing.find(id);
681 if (i == m_sounds_playing.end())
684 PlayingSound *sound = i->second;
685 alSourcef(sound->source_id, AL_GAIN, gain);
689 float getSoundGain(int id)
691 auto i = m_sounds_playing.find(id);
692 if (i == m_sounds_playing.end())
695 PlayingSound *sound = i->second;
697 alGetSourcef(sound->source_id, AL_GAIN, &gain);
702 std::shared_ptr<SoundManagerSingleton> createSoundManagerSingleton()
704 auto smg = std::make_shared<SoundManagerSingleton>();
711 ISoundManager *createOpenALSoundManager(SoundManagerSingleton *smg, OnDemandSoundFetcher *fetcher)
713 return new OpenALSoundManager(smg, fetcher);