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 if (!m_sounds_playing.empty()) {
502 verbosestream << "OpenALSoundManager::maintain(): "
503 << m_sounds_playing.size() <<" playing sounds, "
504 << m_buffers.size() <<" sound names loaded"<<std::endl;
506 std::unordered_set<int> del_list;
507 for (const auto &sp : m_sounds_playing) {
509 PlayingSound *sound = sp.second;
510 // If not playing, remove it
513 alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state);
514 if(state != AL_PLAYING){
519 if(!del_list.empty())
520 verbosestream<<"OpenALSoundManager::maintain(): deleting "
521 <<del_list.size()<<" playing sounds"<<std::endl;
522 for (int i : del_list) {
529 bool loadSoundFile(const std::string &name,
530 const std::string &filepath)
532 SoundBuffer *buf = load_ogg_from_file(filepath);
534 addBuffer(name, buf);
538 bool loadSoundData(const std::string &name,
539 const std::string &filedata)
541 SoundBuffer *buf = load_ogg_from_buffer(filedata, name);
543 addBuffer(name, buf);
547 void updateListener(const v3f &pos, const v3f &vel, const v3f &at, const v3f &up)
549 alListener3f(AL_POSITION, pos.X, pos.Y, pos.Z);
550 alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z);
554 alListenerfv(AL_ORIENTATION, f);
555 warn_if_error(alGetError(), "updateListener");
558 void setListenerGain(float gain)
560 alListenerf(AL_GAIN, gain);
563 int playSound(const std::string &name, bool loop, float volume, float fade, float pitch)
568 SoundBuffer *buf = getFetchBuffer(name);
570 infostream << "OpenALSoundManager: \"" << name << "\" not found."
576 handle = playSoundRaw(buf, loop, 0.0f, pitch);
577 fadeSound(handle, fade, volume);
579 handle = playSoundRaw(buf, loop, volume, pitch);
584 int playSoundAt(const std::string &name, bool loop, float volume, v3f pos, float pitch)
589 SoundBuffer *buf = getFetchBuffer(name);
591 infostream << "OpenALSoundManager: \"" << name << "\" not found."
595 return playSoundRawAt(buf, loop, volume, pos, pitch);
598 void stopSound(int sound)
604 void fadeSound(int soundid, float step, float gain)
606 m_sounds_fading[soundid] = FadeState(step, getSoundGain(soundid), gain);
609 void doFades(float dtime)
611 m_fade_delay += dtime;
613 if (m_fade_delay < 0.1f)
617 for (auto i = m_sounds_fading.begin();
618 i != m_sounds_fading.end();) {
619 if (i->second.step < 0.f)
620 chkGain = -(i->second.current_gain);
622 chkGain = i->second.current_gain;
624 if (chkGain < i->second.target_gain) {
625 i->second.current_gain += (i->second.step * m_fade_delay);
626 i->second.current_gain = rangelim(i->second.current_gain, 0, 1);
628 updateSoundGain(i->first, i->second.current_gain);
631 if (i->second.target_gain <= 0.f)
634 m_sounds_fading.erase(i++);
640 bool soundExists(int sound)
643 return (m_sounds_playing.count(sound) != 0);
646 void updateSoundPosition(int id, v3f pos)
648 auto i = m_sounds_playing.find(id);
649 if (i == m_sounds_playing.end())
651 PlayingSound *sound = i->second;
653 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
654 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
655 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
656 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
659 bool updateSoundGain(int id, float gain)
661 auto i = m_sounds_playing.find(id);
662 if (i == m_sounds_playing.end())
665 PlayingSound *sound = i->second;
666 alSourcef(sound->source_id, AL_GAIN, gain);
670 float getSoundGain(int id)
672 auto i = m_sounds_playing.find(id);
673 if (i == m_sounds_playing.end())
676 PlayingSound *sound = i->second;
678 alGetSourcef(sound->source_id, AL_GAIN, &gain);
683 std::shared_ptr<SoundManagerSingleton> createSoundManagerSingleton()
685 return std::shared_ptr<SoundManagerSingleton>(new SoundManagerSingleton());
688 ISoundManager *createOpenALSoundManager(SoundManagerSingleton *smg, OnDemandSoundFetcher *fetcher)
690 return new OpenALSoundManager(smg, fetcher);