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;
342 OpenALSoundManager(SoundManagerSingleton *smg, OnDemandSoundFetcher *fetcher):
344 m_device(smg->m_device.get()),
345 m_context(smg->m_context.get()),
349 infostream << "Audio: Initialized: OpenAL " << std::endl;
352 ~OpenALSoundManager()
354 infostream << "Audio: Deinitializing..." << std::endl;
356 std::unordered_set<int> source_del_list;
358 for (const auto &sp : m_sounds_playing)
359 source_del_list.insert(sp.first);
361 for (const auto &id : source_del_list)
364 for (auto &buffer : m_buffers) {
365 for (SoundBuffer *sb : buffer.second) {
368 buffer.second.clear();
372 infostream << "Audio: Deinitialized." << std::endl;
375 void step(float dtime)
380 void addBuffer(const std::string &name, SoundBuffer *buf)
382 std::unordered_map<std::string, std::vector<SoundBuffer*>>::iterator i =
383 m_buffers.find(name);
384 if(i != m_buffers.end()){
385 i->second.push_back(buf);
388 std::vector<SoundBuffer*> bufs;
390 m_buffers[name] = bufs;
393 SoundBuffer* getBuffer(const std::string &name)
395 std::unordered_map<std::string, std::vector<SoundBuffer*>>::iterator i =
396 m_buffers.find(name);
397 if(i == m_buffers.end())
399 std::vector<SoundBuffer*> &bufs = i->second;
400 int j = myrand() % bufs.size();
404 PlayingSound* createPlayingSound(SoundBuffer *buf, bool loop,
405 float volume, float pitch)
407 infostream << "OpenALSoundManager: Creating playing sound" << std::endl;
409 PlayingSound *sound = new PlayingSound;
411 warn_if_error(alGetError(), "before createPlayingSound");
412 alGenSources(1, &sound->source_id);
413 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
414 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, true);
415 alSource3f(sound->source_id, AL_POSITION, 0, 0, 0);
416 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
417 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
418 volume = std::fmax(0.0f, volume);
419 alSourcef(sound->source_id, AL_GAIN, volume);
420 alSourcef(sound->source_id, AL_PITCH, pitch);
421 alSourcePlay(sound->source_id);
422 warn_if_error(alGetError(), "createPlayingSound");
426 PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop,
427 float volume, v3f pos, float pitch)
429 infostream << "OpenALSoundManager: Creating positional playing sound"
432 PlayingSound *sound = new PlayingSound;
434 warn_if_error(alGetError(), "before createPlayingSoundAt");
435 alGenSources(1, &sound->source_id);
436 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
437 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
438 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
439 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
440 // Use alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED) and set reference
441 // distance to clamp gain at <1 node distance, to avoid excessive
442 // volume when closer
443 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 10.0f);
444 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
445 // Multiply by 3 to compensate for reducing AL_REFERENCE_DISTANCE from
446 // the previous value of 30 to the new value of 10
447 volume = std::fmax(0.0f, volume * 3.0f);
448 alSourcef(sound->source_id, AL_GAIN, volume);
449 alSourcef(sound->source_id, AL_PITCH, pitch);
450 alSourcePlay(sound->source_id);
451 warn_if_error(alGetError(), "createPlayingSoundAt");
455 int playSoundRaw(SoundBuffer *buf, bool loop, float volume, float pitch)
458 PlayingSound *sound = createPlayingSound(buf, loop, volume, pitch);
461 int id = m_next_id++;
462 m_sounds_playing[id] = sound;
466 int playSoundRawAt(SoundBuffer *buf, bool loop, float volume, const v3f &pos,
470 PlayingSound *sound = createPlayingSoundAt(buf, loop, volume, pos, pitch);
473 int id = m_next_id++;
474 m_sounds_playing[id] = sound;
478 void deleteSound(int id)
480 std::unordered_map<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
481 if(i == m_sounds_playing.end())
483 PlayingSound *sound = i->second;
485 alDeleteSources(1, &sound->source_id);
488 m_sounds_playing.erase(id);
491 /* If buffer does not exist, consult the fetcher */
492 SoundBuffer* getFetchBuffer(const std::string &name)
494 SoundBuffer *buf = getBuffer(name);
499 std::set<std::string> paths;
500 std::set<std::string> datas;
501 m_fetcher->fetchSounds(name, paths, datas);
502 for (const std::string &path : paths) {
503 loadSoundFile(name, path);
505 for (const std::string &data : datas) {
506 loadSoundData(name, data);
508 return getBuffer(name);
511 // Remove stopped sounds
514 if (!m_sounds_playing.empty()) {
515 verbosestream << "OpenALSoundManager::maintain(): "
516 << m_sounds_playing.size() <<" playing sounds, "
517 << m_buffers.size() <<" sound names loaded"<<std::endl;
519 std::unordered_set<int> del_list;
520 for (const auto &sp : m_sounds_playing) {
522 PlayingSound *sound = sp.second;
523 // If not playing, remove it
526 alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state);
527 if(state != AL_PLAYING){
532 if(!del_list.empty())
533 verbosestream<<"OpenALSoundManager::maintain(): deleting "
534 <<del_list.size()<<" playing sounds"<<std::endl;
535 for (int i : del_list) {
542 bool loadSoundFile(const std::string &name,
543 const std::string &filepath)
545 SoundBuffer *buf = load_ogg_from_file(filepath);
547 addBuffer(name, buf);
551 bool loadSoundData(const std::string &name,
552 const std::string &filedata)
554 SoundBuffer *buf = load_ogg_from_buffer(filedata, name);
556 addBuffer(name, buf);
560 void updateListener(const v3f &pos, const v3f &vel, const v3f &at, const v3f &up)
562 alListener3f(AL_POSITION, pos.X, pos.Y, pos.Z);
563 alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z);
567 alListenerfv(AL_ORIENTATION, f);
568 warn_if_error(alGetError(), "updateListener");
571 void setListenerGain(float gain)
573 alListenerf(AL_GAIN, gain);
576 int playSound(const std::string &name, bool loop, float volume, float fade, float pitch)
581 SoundBuffer *buf = getFetchBuffer(name);
583 infostream << "OpenALSoundManager: \"" << name << "\" not found."
589 handle = playSoundRaw(buf, loop, 0.0f, pitch);
590 fadeSound(handle, fade, volume);
592 handle = playSoundRaw(buf, loop, volume, pitch);
597 int playSoundAt(const std::string &name, bool loop, float volume, v3f pos, float pitch)
602 SoundBuffer *buf = getFetchBuffer(name);
604 infostream << "OpenALSoundManager: \"" << name << "\" not found."
608 return playSoundRawAt(buf, loop, volume, pos, pitch);
611 void stopSound(int sound)
617 void fadeSound(int soundid, float step, float gain)
619 m_sounds_fading[soundid] = FadeState(step, getSoundGain(soundid), gain);
622 void doFades(float dtime)
624 m_fade_delay += dtime;
626 if (m_fade_delay < 0.1f)
630 for (auto i = m_sounds_fading.begin();
631 i != m_sounds_fading.end();) {
632 if (i->second.step < 0.f)
633 chkGain = -(i->second.current_gain);
635 chkGain = i->second.current_gain;
637 if (chkGain < i->second.target_gain) {
638 i->second.current_gain += (i->second.step * m_fade_delay);
639 i->second.current_gain = rangelim(i->second.current_gain, 0, 1);
641 updateSoundGain(i->first, i->second.current_gain);
644 if (i->second.target_gain <= 0.f)
647 m_sounds_fading.erase(i++);
653 bool soundExists(int sound)
656 return (m_sounds_playing.count(sound) != 0);
659 void updateSoundPosition(int id, v3f pos)
661 auto i = m_sounds_playing.find(id);
662 if (i == m_sounds_playing.end())
664 PlayingSound *sound = i->second;
666 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
667 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
668 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
669 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
672 bool updateSoundGain(int id, float gain)
674 auto i = m_sounds_playing.find(id);
675 if (i == m_sounds_playing.end())
678 PlayingSound *sound = i->second;
679 alSourcef(sound->source_id, AL_GAIN, gain);
683 float getSoundGain(int id)
685 auto i = m_sounds_playing.find(id);
686 if (i == m_sounds_playing.end())
689 PlayingSound *sound = i->second;
691 alGetSourcef(sound->source_id, AL_GAIN, &gain);
696 std::shared_ptr<SoundManagerSingleton> createSoundManagerSingleton()
698 auto smg = std::make_shared<SoundManagerSingleton>();
705 ISoundManager *createOpenALSoundManager(SoundManagerSingleton *smg, OnDemandSoundFetcher *fetcher)
707 return new OpenALSoundManager(smg, fetcher);