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)
278 if (!(m_device = unique_ptr_alcdevice(alcOpenDevice(nullptr), delete_alcdevice)))
279 throw std::runtime_error("Audio: Global Initialization: Device Open");
281 if (!(m_context = unique_ptr_alccontext(
282 alcCreateContext(m_device.get(), nullptr), delete_alccontext))) {
283 throw std::runtime_error("Audio: Global Initialization: Context Create");
286 if (!alcMakeContextCurrent(m_context.get()))
287 throw std::runtime_error("Audio: Global Initialization: Context Current");
289 alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
291 if (alGetError() != AL_NO_ERROR)
292 throw std::runtime_error("Audio: Global Initialization: OpenAL Error");
294 infostream << "Audio: Global Initialized: OpenAL " << alGetString(AL_VERSION)
295 << ", using " << alcGetString(m_device.get(), ALC_DEVICE_SPECIFIER)
299 ~SoundManagerSingleton()
301 infostream << "Audio: Global Deinitialized." << std::endl;
305 class OpenALSoundManager: public ISoundManager
308 OnDemandSoundFetcher *m_fetcher;
310 ALCcontext *m_context;
312 std::unordered_map<std::string, std::vector<SoundBuffer*>> m_buffers;
313 std::unordered_map<int, PlayingSound*> m_sounds_playing;
315 FadeState() = default;
317 FadeState(float step, float current_gain, float target_gain):
319 current_gain(current_gain),
320 target_gain(target_gain) {}
326 std::unordered_map<int, FadeState> m_sounds_fading;
329 OpenALSoundManager(SoundManagerSingleton *smg, OnDemandSoundFetcher *fetcher):
331 m_device(smg->m_device.get()),
332 m_context(smg->m_context.get()),
336 infostream << "Audio: Initialized: OpenAL " << std::endl;
339 ~OpenALSoundManager()
341 infostream << "Audio: Deinitializing..." << std::endl;
343 std::unordered_set<int> source_del_list;
345 for (const auto &sp : m_sounds_playing)
346 source_del_list.insert(sp.first);
348 for (const auto &id : source_del_list)
351 for (auto &buffer : m_buffers) {
352 for (SoundBuffer *sb : buffer.second) {
355 buffer.second.clear();
359 infostream << "Audio: Deinitialized." << std::endl;
362 void step(float dtime)
367 void addBuffer(const std::string &name, SoundBuffer *buf)
369 std::unordered_map<std::string, std::vector<SoundBuffer*>>::iterator i =
370 m_buffers.find(name);
371 if(i != m_buffers.end()){
372 i->second.push_back(buf);
375 std::vector<SoundBuffer*> bufs;
377 m_buffers[name] = bufs;
380 SoundBuffer* getBuffer(const std::string &name)
382 std::unordered_map<std::string, std::vector<SoundBuffer*>>::iterator i =
383 m_buffers.find(name);
384 if(i == m_buffers.end())
386 std::vector<SoundBuffer*> &bufs = i->second;
387 int j = myrand() % bufs.size();
391 PlayingSound* createPlayingSound(SoundBuffer *buf, bool loop,
392 float volume, float pitch)
394 infostream << "OpenALSoundManager: Creating playing sound" << std::endl;
396 PlayingSound *sound = new PlayingSound;
398 warn_if_error(alGetError(), "before createPlayingSound");
399 alGenSources(1, &sound->source_id);
400 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
401 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, true);
402 alSource3f(sound->source_id, AL_POSITION, 0, 0, 0);
403 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
404 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
405 volume = std::fmax(0.0f, volume);
406 alSourcef(sound->source_id, AL_GAIN, volume);
407 alSourcef(sound->source_id, AL_PITCH, pitch);
408 alSourcePlay(sound->source_id);
409 warn_if_error(alGetError(), "createPlayingSound");
413 PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop,
414 float volume, v3f pos, float pitch)
416 infostream << "OpenALSoundManager: Creating positional playing sound"
419 PlayingSound *sound = new PlayingSound;
421 warn_if_error(alGetError(), "before createPlayingSoundAt");
422 alGenSources(1, &sound->source_id);
423 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
424 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
425 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
426 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
427 // Use alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED) and set reference
428 // distance to clamp gain at <1 node distance, to avoid excessive
429 // volume when closer
430 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 10.0f);
431 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
432 // Multiply by 3 to compensate for reducing AL_REFERENCE_DISTANCE from
433 // the previous value of 30 to the new value of 10
434 volume = std::fmax(0.0f, volume * 3.0f);
435 alSourcef(sound->source_id, AL_GAIN, volume);
436 alSourcef(sound->source_id, AL_PITCH, pitch);
437 alSourcePlay(sound->source_id);
438 warn_if_error(alGetError(), "createPlayingSoundAt");
442 int playSoundRaw(SoundBuffer *buf, bool loop, float volume, float pitch)
445 PlayingSound *sound = createPlayingSound(buf, loop, volume, pitch);
448 int id = m_next_id++;
449 m_sounds_playing[id] = sound;
453 int playSoundRawAt(SoundBuffer *buf, bool loop, float volume, const v3f &pos,
457 PlayingSound *sound = createPlayingSoundAt(buf, loop, volume, pos, pitch);
460 int id = m_next_id++;
461 m_sounds_playing[id] = sound;
465 void deleteSound(int id)
467 std::unordered_map<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
468 if(i == m_sounds_playing.end())
470 PlayingSound *sound = i->second;
472 alDeleteSources(1, &sound->source_id);
475 m_sounds_playing.erase(id);
478 /* If buffer does not exist, consult the fetcher */
479 SoundBuffer* getFetchBuffer(const std::string &name)
481 SoundBuffer *buf = getBuffer(name);
486 std::set<std::string> paths;
487 std::set<std::string> datas;
488 m_fetcher->fetchSounds(name, paths, datas);
489 for (const std::string &path : paths) {
490 loadSoundFile(name, path);
492 for (const std::string &data : datas) {
493 loadSoundData(name, data);
495 return getBuffer(name);
498 // Remove stopped sounds
501 verbosestream<<"OpenALSoundManager::maintain(): "
502 <<m_sounds_playing.size()<<" playing sounds, "
503 <<m_buffers.size()<<" sound names loaded"<<std::endl;
504 std::unordered_set<int> del_list;
505 for (const auto &sp : m_sounds_playing) {
507 PlayingSound *sound = sp.second;
508 // If not playing, remove it
511 alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state);
512 if(state != AL_PLAYING){
517 if(!del_list.empty())
518 verbosestream<<"OpenALSoundManager::maintain(): deleting "
519 <<del_list.size()<<" playing sounds"<<std::endl;
520 for (int i : del_list) {
527 bool loadSoundFile(const std::string &name,
528 const std::string &filepath)
530 SoundBuffer *buf = load_ogg_from_file(filepath);
532 addBuffer(name, buf);
536 bool loadSoundData(const std::string &name,
537 const std::string &filedata)
539 SoundBuffer *buf = load_ogg_from_buffer(filedata, name);
541 addBuffer(name, buf);
545 void updateListener(const v3f &pos, const v3f &vel, const v3f &at, const v3f &up)
547 alListener3f(AL_POSITION, pos.X, pos.Y, pos.Z);
548 alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z);
552 alListenerfv(AL_ORIENTATION, f);
553 warn_if_error(alGetError(), "updateListener");
556 void setListenerGain(float gain)
558 alListenerf(AL_GAIN, gain);
561 int playSound(const std::string &name, bool loop, float volume, float fade, float pitch)
566 SoundBuffer *buf = getFetchBuffer(name);
568 infostream << "OpenALSoundManager: \"" << name << "\" not found."
574 handle = playSoundRaw(buf, loop, 0.0f, pitch);
575 fadeSound(handle, fade, volume);
577 handle = playSoundRaw(buf, loop, volume, pitch);
582 int playSoundAt(const std::string &name, bool loop, float volume, v3f pos, float pitch)
587 SoundBuffer *buf = getFetchBuffer(name);
589 infostream << "OpenALSoundManager: \"" << name << "\" not found."
593 return playSoundRawAt(buf, loop, volume, pos, pitch);
596 void stopSound(int sound)
602 void fadeSound(int soundid, float step, float gain)
604 m_sounds_fading[soundid] = FadeState(step, getSoundGain(soundid), gain);
607 void doFades(float dtime)
609 m_fade_delay += dtime;
611 if (m_fade_delay < 0.1f)
615 for (auto i = m_sounds_fading.begin();
616 i != m_sounds_fading.end();) {
617 if (i->second.step < 0.f)
618 chkGain = -(i->second.current_gain);
620 chkGain = i->second.current_gain;
622 if (chkGain < i->second.target_gain) {
623 i->second.current_gain += (i->second.step * m_fade_delay);
624 i->second.current_gain = rangelim(i->second.current_gain, 0, 1);
626 updateSoundGain(i->first, i->second.current_gain);
629 if (i->second.target_gain <= 0.f)
632 m_sounds_fading.erase(i++);
638 bool soundExists(int sound)
641 return (m_sounds_playing.count(sound) != 0);
644 void updateSoundPosition(int id, v3f pos)
646 auto i = m_sounds_playing.find(id);
647 if (i == m_sounds_playing.end())
649 PlayingSound *sound = i->second;
651 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
652 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
653 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
654 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
657 bool updateSoundGain(int id, float gain)
659 auto i = m_sounds_playing.find(id);
660 if (i == m_sounds_playing.end())
663 PlayingSound *sound = i->second;
664 alSourcef(sound->source_id, AL_GAIN, gain);
668 float getSoundGain(int id)
670 auto i = m_sounds_playing.find(id);
671 if (i == m_sounds_playing.end())
674 PlayingSound *sound = i->second;
676 alGetSourcef(sound->source_id, AL_GAIN, &gain);
681 std::shared_ptr<SoundManagerSingleton> createSoundManagerSingleton()
683 return std::shared_ptr<SoundManagerSingleton>(new SoundManagerSingleton());
686 ISoundManager *createOpenALSoundManager(SoundManagerSingleton *smg, OnDemandSoundFetcher *fetcher)
688 return new OpenALSoundManager(smg, fetcher);