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 #include <OpenAL/al.h>
32 #include <OpenAL/alc.h>
33 //#include <OpenAL/alext.h>
40 #include <vorbis/vorbisfile.h>
43 #include "util/numeric.h" // myrand()
47 #include <unordered_map>
48 #include <unordered_set>
50 #define BUFFER_SIZE 30000
52 std::shared_ptr<SoundManagerSingleton> g_sound_manager_singleton;
54 typedef std::unique_ptr<ALCdevice, void (*)(ALCdevice *p)> unique_ptr_alcdevice;
55 typedef std::unique_ptr<ALCcontext, void(*)(ALCcontext *p)> unique_ptr_alccontext;
57 static void delete_alcdevice(ALCdevice *p)
63 static void delete_alccontext(ALCcontext *p)
66 alcMakeContextCurrent(nullptr);
71 static const char *alErrorString(ALenum err)
77 return "invalid name";
79 return "invalid enum";
80 case AL_INVALID_VALUE:
81 return "invalid value";
82 case AL_INVALID_OPERATION:
83 return "invalid operation";
84 case AL_OUT_OF_MEMORY:
85 return "out of memory";
87 return "<unknown OpenAL error>";
91 static ALenum warn_if_error(ALenum err, const char *desc)
93 if(err == AL_NO_ERROR)
95 warningstream<<desc<<": "<<alErrorString(err)<<std::endl;
99 void f3_set(ALfloat *f3, v3f v)
111 std::vector<char> buffer;
114 SoundBuffer *load_opened_ogg_file(OggVorbis_File *oggFile,
115 const std::string &filename_for_logging)
117 int endian = 0; // 0 for Little-Endian, 1 for Big-Endian
120 char array[BUFFER_SIZE]; // Local fixed size array
123 SoundBuffer *snd = new SoundBuffer;
125 // Get some information about the OGG file
126 pInfo = ov_info(oggFile, -1);
128 // Check the number of channels... always use 16-bit samples
129 if(pInfo->channels == 1)
130 snd->format = AL_FORMAT_MONO16;
132 snd->format = AL_FORMAT_STEREO16;
134 // The frequency of the sampling rate
135 snd->freq = pInfo->rate;
137 // Keep reading until all is read
140 // Read up to a buffer's worth of decoded sound data
141 bytes = ov_read(oggFile, array, BUFFER_SIZE, endian, 2, 1, &bitStream);
146 infostream << "Audio: Error decoding "
147 << filename_for_logging << std::endl;
152 // Append to end of buffer
153 snd->buffer.insert(snd->buffer.end(), array, array + bytes);
156 alGenBuffers(1, &snd->buffer_id);
157 alBufferData(snd->buffer_id, snd->format,
158 &(snd->buffer[0]), snd->buffer.size(),
161 ALenum error = alGetError();
163 if(error != AL_NO_ERROR){
164 infostream << "Audio: OpenAL error: " << alErrorString(error)
165 << "preparing sound buffer" << std::endl;
168 //infostream << "Audio file "
169 // << filename_for_logging << " loaded" << std::endl;
177 SoundBuffer *load_ogg_from_file(const std::string &path)
179 OggVorbis_File oggFile;
181 // Try opening the given file.
182 // This requires libvorbis >= 1.3.2, as
183 // previous versions expect a non-const char *
184 if (ov_fopen(path.c_str(), &oggFile) != 0) {
185 infostream << "Audio: Error opening " << path
186 << " for decoding" << std::endl;
190 return load_opened_ogg_file(&oggFile, path);
193 struct BufferSource {
199 size_t buffer_sound_read_func(void *ptr, size_t size, size_t nmemb, void *datasource)
201 BufferSource *s = (BufferSource *)datasource;
202 size_t copied_size = MYMIN(s->len - s->cur_offset, size);
203 memcpy(ptr, s->buf + s->cur_offset, copied_size);
204 s->cur_offset += copied_size;
208 int buffer_sound_seek_func(void *datasource, ogg_int64_t offset, int whence)
210 BufferSource *s = (BufferSource *)datasource;
211 if (whence == SEEK_SET) {
212 if (offset < 0 || (size_t)MYMAX(offset, 0) >= s->len) {
213 // offset out of bounds
216 s->cur_offset = offset;
218 } else if (whence == SEEK_CUR) {
219 if ((size_t)MYMIN(-offset, 0) > s->cur_offset
220 || s->cur_offset + offset > s->len) {
221 // offset out of bounds
224 s->cur_offset += offset;
227 // invalid whence param (SEEK_END doesn't have to be supported)
231 long BufferSourceell_func(void *datasource)
233 BufferSource *s = (BufferSource *)datasource;
234 return s->cur_offset;
237 static ov_callbacks g_buffer_ov_callbacks = {
238 &buffer_sound_read_func,
239 &buffer_sound_seek_func,
241 &BufferSourceell_func
244 SoundBuffer *load_ogg_from_buffer(const std::string &buf, const std::string &id_for_log)
246 OggVorbis_File oggFile;
253 if (ov_open_callbacks(&s, &oggFile, nullptr, 0, g_buffer_ov_callbacks) != 0) {
254 infostream << "Audio: Error opening " << id_for_log
255 << " for decoding" << std::endl;
259 return load_opened_ogg_file(&oggFile, id_for_log);
268 class SoundManagerSingleton
271 unique_ptr_alcdevice m_device;
272 unique_ptr_alccontext m_context;
274 SoundManagerSingleton() :
275 m_device(nullptr, delete_alcdevice),
276 m_context(nullptr, delete_alccontext)
282 if (!(m_device = unique_ptr_alcdevice(alcOpenDevice(nullptr), delete_alcdevice))) {
283 errorstream << "Audio: Global Initialization: Failed to open device" << std::endl;
287 if (!(m_context = unique_ptr_alccontext(
288 alcCreateContext(m_device.get(), nullptr), delete_alccontext))) {
289 errorstream << "Audio: Global Initialization: Failed to create context" << std::endl;
293 if (!alcMakeContextCurrent(m_context.get())) {
294 errorstream << "Audio: Global Initialization: Failed to make current context" << std::endl;
298 alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
300 if (alGetError() != AL_NO_ERROR) {
301 errorstream << "Audio: Global Initialization: OpenAL Error " << alGetError() << std::endl;
305 infostream << "Audio: Global Initialized: OpenAL " << alGetString(AL_VERSION)
306 << ", using " << alcGetString(m_device.get(), ALC_DEVICE_SPECIFIER)
312 ~SoundManagerSingleton()
314 infostream << "Audio: Global Deinitialized." << std::endl;
318 class OpenALSoundManager: public ISoundManager
321 OnDemandSoundFetcher *m_fetcher;
323 ALCcontext *m_context;
325 std::unordered_map<std::string, std::vector<SoundBuffer*>> m_buffers;
326 std::unordered_map<int, PlayingSound*> m_sounds_playing;
328 FadeState() = default;
330 FadeState(float step, float current_gain, float target_gain):
332 current_gain(current_gain),
333 target_gain(target_gain) {}
339 std::unordered_map<int, FadeState> m_sounds_fading;
341 OpenALSoundManager(SoundManagerSingleton *smg, OnDemandSoundFetcher *fetcher):
343 m_device(smg->m_device.get()),
344 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) {
366 buffer.second.clear();
370 infostream << "Audio: Deinitialized." << std::endl;
373 void step(float dtime)
378 void addBuffer(const std::string &name, SoundBuffer *buf)
380 std::unordered_map<std::string, std::vector<SoundBuffer*>>::iterator i =
381 m_buffers.find(name);
382 if(i != m_buffers.end()){
383 i->second.push_back(buf);
386 std::vector<SoundBuffer*> bufs;
388 m_buffers[name] = bufs;
391 SoundBuffer* getBuffer(const std::string &name)
393 std::unordered_map<std::string, std::vector<SoundBuffer*>>::iterator i =
394 m_buffers.find(name);
395 if(i == m_buffers.end())
397 std::vector<SoundBuffer*> &bufs = i->second;
398 int j = myrand() % bufs.size();
402 PlayingSound* createPlayingSound(SoundBuffer *buf, bool loop,
403 float volume, float pitch)
405 infostream << "OpenALSoundManager: Creating playing sound" << std::endl;
407 PlayingSound *sound = new PlayingSound;
409 warn_if_error(alGetError(), "before createPlayingSound");
410 alGenSources(1, &sound->source_id);
411 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
412 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, true);
413 alSource3f(sound->source_id, AL_POSITION, 0, 0, 0);
414 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
415 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
416 volume = std::fmax(0.0f, volume);
417 alSourcef(sound->source_id, AL_GAIN, volume);
418 alSourcef(sound->source_id, AL_PITCH, pitch);
419 alSourcePlay(sound->source_id);
420 warn_if_error(alGetError(), "createPlayingSound");
424 PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop,
425 float volume, v3f pos, float pitch)
427 infostream << "OpenALSoundManager: Creating positional playing sound"
430 PlayingSound *sound = new PlayingSound;
432 warn_if_error(alGetError(), "before createPlayingSoundAt");
433 alGenSources(1, &sound->source_id);
434 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
435 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
436 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
437 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
438 // Use alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED) and set reference
439 // distance to clamp gain at <1 node distance, to avoid excessive
440 // volume when closer
441 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 10.0f);
442 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
443 // Multiply by 3 to compensate for reducing AL_REFERENCE_DISTANCE from
444 // the previous value of 30 to the new value of 10
445 volume = std::fmax(0.0f, volume * 3.0f);
446 alSourcef(sound->source_id, AL_GAIN, volume);
447 alSourcef(sound->source_id, AL_PITCH, pitch);
448 alSourcePlay(sound->source_id);
449 warn_if_error(alGetError(), "createPlayingSoundAt");
453 int playSoundRaw(SoundBuffer *buf, bool loop, float volume, float pitch)
456 PlayingSound *sound = createPlayingSound(buf, loop, volume, pitch);
459 int id = m_next_id++;
460 m_sounds_playing[id] = sound;
464 int playSoundRawAt(SoundBuffer *buf, bool loop, float volume, const v3f &pos,
468 PlayingSound *sound = createPlayingSoundAt(buf, loop, volume, pos, pitch);
471 int id = m_next_id++;
472 m_sounds_playing[id] = sound;
476 void deleteSound(int id)
478 std::unordered_map<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
479 if(i == m_sounds_playing.end())
481 PlayingSound *sound = i->second;
483 alDeleteSources(1, &sound->source_id);
486 m_sounds_playing.erase(id);
489 /* If buffer does not exist, consult the fetcher */
490 SoundBuffer* getFetchBuffer(const std::string &name)
492 SoundBuffer *buf = getBuffer(name);
497 std::set<std::string> paths;
498 std::set<std::string> datas;
499 m_fetcher->fetchSounds(name, paths, datas);
500 for (const std::string &path : paths) {
501 loadSoundFile(name, path);
503 for (const std::string &data : datas) {
504 loadSoundData(name, data);
506 return getBuffer(name);
509 // Remove stopped sounds
512 if (!m_sounds_playing.empty()) {
513 verbosestream << "OpenALSoundManager::maintain(): "
514 << m_sounds_playing.size() <<" playing sounds, "
515 << m_buffers.size() <<" sound names loaded"<<std::endl;
517 std::unordered_set<int> del_list;
518 for (const auto &sp : m_sounds_playing) {
520 PlayingSound *sound = sp.second;
521 // If not playing, remove it
524 alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state);
525 if(state != AL_PLAYING){
530 if(!del_list.empty())
531 verbosestream<<"OpenALSoundManager::maintain(): deleting "
532 <<del_list.size()<<" playing sounds"<<std::endl;
533 for (int i : del_list) {
540 bool loadSoundFile(const std::string &name,
541 const std::string &filepath)
543 SoundBuffer *buf = load_ogg_from_file(filepath);
545 addBuffer(name, buf);
549 bool loadSoundData(const std::string &name,
550 const std::string &filedata)
552 SoundBuffer *buf = load_ogg_from_buffer(filedata, name);
554 addBuffer(name, buf);
558 void updateListener(const v3f &pos, const v3f &vel, const v3f &at, const v3f &up)
560 alListener3f(AL_POSITION, pos.X, pos.Y, pos.Z);
561 alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z);
565 alListenerfv(AL_ORIENTATION, f);
566 warn_if_error(alGetError(), "updateListener");
569 void setListenerGain(float gain)
571 alListenerf(AL_GAIN, gain);
574 int playSound(const std::string &name, bool loop, float volume, float fade, float pitch)
579 SoundBuffer *buf = getFetchBuffer(name);
581 infostream << "OpenALSoundManager: \"" << name << "\" not found."
587 handle = playSoundRaw(buf, loop, 0.0f, pitch);
588 fadeSound(handle, fade, volume);
590 handle = playSoundRaw(buf, loop, volume, pitch);
595 int playSoundAt(const std::string &name, bool loop, float volume, v3f pos, float pitch)
600 SoundBuffer *buf = getFetchBuffer(name);
602 infostream << "OpenALSoundManager: \"" << name << "\" not found."
606 return playSoundRawAt(buf, loop, volume, pos, pitch);
609 void stopSound(int sound)
615 void fadeSound(int soundid, float step, float gain)
617 // Ignore the command if step isn't valid.
620 float current_gain = getSoundGain(soundid);
621 step = gain - current_gain > 0 ? abs(step) : -abs(step);
622 if (m_sounds_fading.find(soundid) != m_sounds_fading.end()) {
623 auto current_fade = m_sounds_fading[soundid];
624 // Do not replace the fade if it's equivalent.
625 if (current_fade.target_gain == gain && current_fade.step == step)
627 m_sounds_fading.erase(soundid);
629 gain = rangelim(gain, 0, 1);
630 m_sounds_fading[soundid] = FadeState(step, current_gain, gain);
633 void doFades(float dtime)
635 for (auto i = m_sounds_fading.begin(); i != m_sounds_fading.end();) {
636 FadeState& fade = i->second;
637 assert(fade.step != 0);
638 fade.current_gain += (fade.step * dtime);
641 fade.current_gain = std::max(fade.current_gain, fade.target_gain);
643 fade.current_gain = std::min(fade.current_gain, fade.target_gain);
645 if (fade.current_gain <= 0.f)
648 updateSoundGain(i->first, fade.current_gain);
650 // The increment must happen during the erase call, or else it'll segfault.
651 if (fade.current_gain == fade.target_gain)
652 m_sounds_fading.erase(i++);
658 bool soundExists(int sound)
661 return (m_sounds_playing.count(sound) != 0);
664 void updateSoundPosition(int id, v3f pos)
666 auto i = m_sounds_playing.find(id);
667 if (i == m_sounds_playing.end())
669 PlayingSound *sound = i->second;
671 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
672 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
673 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
674 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
677 bool updateSoundGain(int id, float gain)
679 auto i = m_sounds_playing.find(id);
680 if (i == m_sounds_playing.end())
683 PlayingSound *sound = i->second;
684 alSourcef(sound->source_id, AL_GAIN, gain);
688 float getSoundGain(int id)
690 auto i = m_sounds_playing.find(id);
691 if (i == m_sounds_playing.end())
694 PlayingSound *sound = i->second;
696 alGetSourcef(sound->source_id, AL_GAIN, &gain);
701 std::shared_ptr<SoundManagerSingleton> createSoundManagerSingleton()
703 auto smg = std::make_shared<SoundManagerSingleton>();
710 ISoundManager *createOpenALSoundManager(SoundManagerSingleton *smg, OnDemandSoundFetcher *fetcher)
712 return new OpenALSoundManager(smg, fetcher);