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;
325 u16 m_last_used_id = 0; // only access within getFreeId() !
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())
347 infostream << "Audio: Initialized: OpenAL " << std::endl;
350 ~OpenALSoundManager()
352 infostream << "Audio: Deinitializing..." << std::endl;
354 std::unordered_set<int> source_del_list;
356 for (const auto &sp : m_sounds_playing)
357 source_del_list.insert(sp.first);
359 for (const auto &id : source_del_list)
362 for (auto &buffer : m_buffers) {
363 for (SoundBuffer *sb : buffer.second) {
364 alDeleteBuffers(1, &sb->buffer_id);
366 ALenum error = alGetError();
367 if (error != AL_NO_ERROR) {
368 warningstream << "Audio: Failed to free stream for "
369 << buffer.first << ": " << alErrorString(error) << std::endl;
374 buffer.second.clear();
378 infostream << "Audio: Deinitialized." << std::endl;
383 u16 startid = m_last_used_id;
384 while (!isFreeId(++m_last_used_id)) {
385 if (m_last_used_id == startid)
389 return m_last_used_id;
392 inline bool isFreeId(int id) const
394 return id > 0 && m_sounds_playing.find(id) == m_sounds_playing.end();
397 void step(float dtime)
402 void addBuffer(const std::string &name, SoundBuffer *buf)
404 std::unordered_map<std::string, std::vector<SoundBuffer*>>::iterator i =
405 m_buffers.find(name);
406 if(i != m_buffers.end()){
407 i->second.push_back(buf);
410 std::vector<SoundBuffer*> bufs;
412 m_buffers[name] = bufs;
415 SoundBuffer* getBuffer(const std::string &name)
417 std::unordered_map<std::string, std::vector<SoundBuffer*>>::iterator i =
418 m_buffers.find(name);
419 if(i == m_buffers.end())
421 std::vector<SoundBuffer*> &bufs = i->second;
422 int j = myrand() % bufs.size();
426 PlayingSound* createPlayingSound(SoundBuffer *buf, bool loop,
427 float volume, float pitch)
429 infostream << "OpenALSoundManager: Creating playing sound" << std::endl;
431 PlayingSound *sound = new PlayingSound;
433 warn_if_error(alGetError(), "before createPlayingSound");
434 alGenSources(1, &sound->source_id);
435 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
436 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, true);
437 alSource3f(sound->source_id, AL_POSITION, 0, 0, 0);
438 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
439 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
440 volume = std::fmax(0.0f, volume);
441 alSourcef(sound->source_id, AL_GAIN, volume);
442 alSourcef(sound->source_id, AL_PITCH, pitch);
443 alSourcePlay(sound->source_id);
444 warn_if_error(alGetError(), "createPlayingSound");
448 PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop,
449 float volume, v3f pos, float pitch)
451 infostream << "OpenALSoundManager: Creating positional playing sound"
454 PlayingSound *sound = new PlayingSound;
456 warn_if_error(alGetError(), "before createPlayingSoundAt");
457 alGenSources(1, &sound->source_id);
458 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
459 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
460 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
461 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
462 // Use alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED) and set reference
463 // distance to clamp gain at <1 node distance, to avoid excessive
464 // volume when closer
465 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 10.0f);
466 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
467 // Multiply by 3 to compensate for reducing AL_REFERENCE_DISTANCE from
468 // the previous value of 30 to the new value of 10
469 volume = std::fmax(0.0f, volume * 3.0f);
470 alSourcef(sound->source_id, AL_GAIN, volume);
471 alSourcef(sound->source_id, AL_PITCH, pitch);
472 alSourcePlay(sound->source_id);
473 warn_if_error(alGetError(), "createPlayingSoundAt");
477 int playSoundRaw(SoundBuffer *buf, bool loop, float volume, float pitch)
480 PlayingSound *sound = createPlayingSound(buf, loop, volume, pitch);
484 int handle = getFreeId();
485 m_sounds_playing[handle] = sound;
489 void deleteSound(int id)
491 auto i = m_sounds_playing.find(id);
492 if(i == m_sounds_playing.end())
494 PlayingSound *sound = i->second;
496 alDeleteSources(1, &sound->source_id);
499 m_sounds_playing.erase(id);
502 /* If buffer does not exist, consult the fetcher */
503 SoundBuffer* getFetchBuffer(const std::string &name)
505 SoundBuffer *buf = getBuffer(name);
510 std::set<std::string> paths;
511 std::set<std::string> datas;
512 m_fetcher->fetchSounds(name, paths, datas);
513 for (const std::string &path : paths) {
514 loadSoundFile(name, path);
516 for (const std::string &data : datas) {
517 loadSoundData(name, data);
519 return getBuffer(name);
522 // Remove stopped sounds
525 if (!m_sounds_playing.empty()) {
526 verbosestream << "OpenALSoundManager::maintain(): "
527 << m_sounds_playing.size() <<" playing sounds, "
528 << m_buffers.size() <<" sound names loaded"<<std::endl;
530 std::unordered_set<int> del_list;
531 for (const auto &sp : m_sounds_playing) {
533 PlayingSound *sound = sp.second;
534 // If not playing, remove it
537 alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state);
538 if(state != AL_PLAYING){
543 if(!del_list.empty())
544 verbosestream<<"OpenALSoundManager::maintain(): deleting "
545 <<del_list.size()<<" playing sounds"<<std::endl;
546 for (int i : del_list) {
553 bool loadSoundFile(const std::string &name,
554 const std::string &filepath)
556 SoundBuffer *buf = load_ogg_from_file(filepath);
558 addBuffer(name, buf);
562 bool loadSoundData(const std::string &name,
563 const std::string &filedata)
565 SoundBuffer *buf = load_ogg_from_buffer(filedata, name);
567 addBuffer(name, buf);
571 void updateListener(const v3f &pos, const v3f &vel, const v3f &at, const v3f &up)
573 alListener3f(AL_POSITION, pos.X, pos.Y, pos.Z);
574 alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z);
578 alListenerfv(AL_ORIENTATION, f);
579 warn_if_error(alGetError(), "updateListener");
582 void setListenerGain(float gain)
584 alListenerf(AL_GAIN, gain);
587 int playSound(const SimpleSoundSpec &spec)
590 if (spec.name.empty())
592 SoundBuffer *buf = getFetchBuffer(spec.name);
594 infostream << "OpenALSoundManager: \"" << spec.name << "\" not found."
601 handle = playSoundRaw(buf, spec.loop, 0.0f, spec.pitch);
602 fadeSound(handle, spec.fade, spec.gain);
604 handle = playSoundRaw(buf, spec.loop, spec.gain, spec.pitch);
609 int playSoundAt(const SimpleSoundSpec &spec, const v3f &pos)
612 if (spec.name.empty())
614 SoundBuffer *buf = getFetchBuffer(spec.name);
616 infostream << "OpenALSoundManager: \"" << spec.name << "\" not found."
621 PlayingSound *sound = createPlayingSoundAt(buf, spec.loop, spec.gain, pos, spec.pitch);
624 int handle = getFreeId();
625 m_sounds_playing[handle] = sound;
629 void stopSound(int sound)
635 void fadeSound(int soundid, float step, float gain)
637 // Ignore the command if step isn't valid.
638 if (step == 0 || soundid < 0)
641 float current_gain = getSoundGain(soundid);
642 step = gain - current_gain > 0 ? abs(step) : -abs(step);
643 if (m_sounds_fading.find(soundid) != m_sounds_fading.end()) {
644 auto current_fade = m_sounds_fading[soundid];
645 // Do not replace the fade if it's equivalent.
646 if (current_fade.target_gain == gain && current_fade.step == step)
648 m_sounds_fading.erase(soundid);
650 gain = rangelim(gain, 0, 1);
651 m_sounds_fading[soundid] = FadeState(step, current_gain, gain);
654 void doFades(float dtime)
656 for (auto i = m_sounds_fading.begin(); i != m_sounds_fading.end();) {
657 FadeState& fade = i->second;
658 assert(fade.step != 0);
659 fade.current_gain += (fade.step * dtime);
662 fade.current_gain = std::max(fade.current_gain, fade.target_gain);
664 fade.current_gain = std::min(fade.current_gain, fade.target_gain);
666 if (fade.current_gain <= 0.f)
669 updateSoundGain(i->first, fade.current_gain);
671 // The increment must happen during the erase call, or else it'll segfault.
672 if (fade.current_gain == fade.target_gain)
673 m_sounds_fading.erase(i++);
679 bool soundExists(int sound)
682 return (m_sounds_playing.count(sound) != 0);
685 void updateSoundPosition(int id, v3f pos)
687 auto i = m_sounds_playing.find(id);
688 if (i == m_sounds_playing.end())
690 PlayingSound *sound = i->second;
692 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
693 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
694 alSource3f(sound->source_id, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
695 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 10.0f);
698 bool updateSoundGain(int id, float gain)
700 auto i = m_sounds_playing.find(id);
701 if (i == m_sounds_playing.end())
704 PlayingSound *sound = i->second;
705 alSourcef(sound->source_id, AL_GAIN, gain);
709 float getSoundGain(int id)
711 auto i = m_sounds_playing.find(id);
712 if (i == m_sounds_playing.end())
715 PlayingSound *sound = i->second;
717 alGetSourcef(sound->source_id, AL_GAIN, &gain);
722 std::shared_ptr<SoundManagerSingleton> createSoundManagerSingleton()
724 auto smg = std::make_shared<SoundManagerSingleton>();
731 ISoundManager *createOpenALSoundManager(SoundManagerSingleton *smg, OnDemandSoundFetcher *fetcher)
733 return new OpenALSoundManager(smg, fetcher);