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>
49 #define BUFFER_SIZE 30000
51 static const char *alcErrorString(ALCenum err)
56 case ALC_INVALID_DEVICE:
57 return "invalid device";
58 case ALC_INVALID_CONTEXT:
59 return "invalid context";
60 case ALC_INVALID_ENUM:
61 return "invalid enum";
62 case ALC_INVALID_VALUE:
63 return "invalid value";
64 case ALC_OUT_OF_MEMORY:
65 return "out of memory";
67 return "<unknown OpenAL error>";
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, NULL, 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 OpenALSoundManager: public ISoundManager
271 OnDemandSoundFetcher *m_fetcher;
273 ALCcontext *m_context;
275 std::unordered_map<std::string, std::vector<SoundBuffer*>> m_buffers;
276 std::unordered_map<int, PlayingSound*> m_sounds_playing;
279 FadeState() = default;
281 FadeState(float step, float current_gain, float target_gain):
283 current_gain(current_gain),
284 target_gain(target_gain) {}
290 std::unordered_map<int, FadeState> m_sounds_fading;
293 bool m_is_initialized;
294 OpenALSoundManager(OnDemandSoundFetcher *fetcher):
300 m_is_initialized(false)
302 ALCenum error = ALC_NO_ERROR;
304 infostream<<"Audio: Initializing..."<<std::endl;
306 m_device = alcOpenDevice(NULL);
308 infostream<<"Audio: No audio device available, audio system "
309 <<"not initialized"<<std::endl;
313 m_context = alcCreateContext(m_device, NULL);
315 error = alcGetError(m_device);
316 infostream<<"Audio: Unable to initialize audio context, "
317 <<"aborting audio initialization ("<<alcErrorString(error)
319 alcCloseDevice(m_device);
324 if(!alcMakeContextCurrent(m_context) ||
325 (error = alcGetError(m_device) != ALC_NO_ERROR))
327 infostream<<"Audio: Error setting audio context, aborting audio "
328 <<"initialization ("<<alcErrorString(error)<<")"<<std::endl;
329 alcDestroyContext(m_context);
331 alcCloseDevice(m_device);
336 alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
338 infostream<<"Audio: Initialized: OpenAL "<<alGetString(AL_VERSION)
339 <<", using "<<alcGetString(m_device, ALC_DEVICE_SPECIFIER)
342 m_is_initialized = true;
345 ~OpenALSoundManager()
347 infostream<<"Audio: Deinitializing..."<<std::endl;
349 // TODO: Clear SoundBuffers
350 alcMakeContextCurrent(NULL);
351 alcDestroyContext(m_context);
353 alcCloseDevice(m_device);
356 for (auto &buffer : m_buffers) {
357 for (SoundBuffer *sb : buffer.second) {
360 buffer.second.clear();
363 infostream<<"Audio: Deinitialized."<<std::endl;
366 void step(float dtime)
371 void addBuffer(const std::string &name, SoundBuffer *buf)
373 std::unordered_map<std::string, std::vector<SoundBuffer*>>::iterator i =
374 m_buffers.find(name);
375 if(i != m_buffers.end()){
376 i->second.push_back(buf);
379 std::vector<SoundBuffer*> bufs;
381 m_buffers[name] = bufs;
384 SoundBuffer* getBuffer(const std::string &name)
386 std::unordered_map<std::string, std::vector<SoundBuffer*>>::iterator i =
387 m_buffers.find(name);
388 if(i == m_buffers.end())
390 std::vector<SoundBuffer*> &bufs = i->second;
391 int j = myrand() % bufs.size();
395 PlayingSound* createPlayingSound(SoundBuffer *buf, bool loop,
396 float volume, float pitch)
398 infostream<<"OpenALSoundManager: Creating playing sound"<<std::endl;
400 PlayingSound *sound = new PlayingSound;
402 warn_if_error(alGetError(), "before createPlayingSound");
403 alGenSources(1, &sound->source_id);
404 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
405 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, true);
406 alSource3f(sound->source_id, AL_POSITION, 0, 0, 0);
407 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
408 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
409 volume = std::fmax(0.0f, volume);
410 alSourcef(sound->source_id, AL_GAIN, volume);
411 alSourcef(sound->source_id, AL_PITCH, pitch);
412 alSourcePlay(sound->source_id);
413 warn_if_error(alGetError(), "createPlayingSound");
417 PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop,
418 float volume, v3f pos, float pitch)
420 infostream<<"OpenALSoundManager: Creating positional playing sound"
423 PlayingSound *sound = new PlayingSound;
425 warn_if_error(alGetError(), "before createPlayingSoundAt");
426 alGenSources(1, &sound->source_id);
427 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
428 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
429 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
430 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
431 // Use alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED) and set reference
432 // distance to clamp gain at <1 node distance, to avoid excessive
433 // volume when closer
434 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 10.0f);
435 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
436 // Multiply by 3 to compensate for reducing AL_REFERENCE_DISTANCE from
437 // the previous value of 30 to the new value of 10
438 volume = std::fmax(0.0f, volume * 3.0f);
439 alSourcef(sound->source_id, AL_GAIN, volume);
440 alSourcef(sound->source_id, AL_PITCH, pitch);
441 alSourcePlay(sound->source_id);
442 warn_if_error(alGetError(), "createPlayingSoundAt");
446 int playSoundRaw(SoundBuffer *buf, bool loop, float volume, float pitch)
449 PlayingSound *sound = createPlayingSound(buf, loop, volume, pitch);
452 int id = m_next_id++;
453 m_sounds_playing[id] = sound;
457 int playSoundRawAt(SoundBuffer *buf, bool loop, float volume, const v3f &pos,
461 PlayingSound *sound = createPlayingSoundAt(buf, loop, volume, pos, pitch);
464 int id = m_next_id++;
465 m_sounds_playing[id] = sound;
469 void deleteSound(int id)
471 std::unordered_map<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
472 if(i == m_sounds_playing.end())
474 PlayingSound *sound = i->second;
476 alDeleteSources(1, &sound->source_id);
479 m_sounds_playing.erase(id);
482 /* If buffer does not exist, consult the fetcher */
483 SoundBuffer* getFetchBuffer(const std::string &name)
485 SoundBuffer *buf = getBuffer(name);
490 std::set<std::string> paths;
491 std::set<std::string> datas;
492 m_fetcher->fetchSounds(name, paths, datas);
493 for (const std::string &path : paths) {
494 loadSoundFile(name, path);
496 for (const std::string &data : datas) {
497 loadSoundData(name, data);
499 return getBuffer(name);
502 // Remove stopped sounds
505 verbosestream<<"OpenALSoundManager::maintain(): "
506 <<m_sounds_playing.size()<<" playing sounds, "
507 <<m_buffers.size()<<" sound names loaded"<<std::endl;
508 std::set<int> del_list;
509 for (auto &sp : m_sounds_playing) {
511 PlayingSound *sound = sp.second;
512 // If not playing, remove it
515 alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state);
516 if(state != AL_PLAYING){
521 if(!del_list.empty())
522 verbosestream<<"OpenALSoundManager::maintain(): deleting "
523 <<del_list.size()<<" playing sounds"<<std::endl;
524 for (int i : del_list) {
531 bool loadSoundFile(const std::string &name,
532 const std::string &filepath)
534 SoundBuffer *buf = load_ogg_from_file(filepath);
536 addBuffer(name, buf);
540 bool loadSoundData(const std::string &name,
541 const std::string &filedata)
543 SoundBuffer *buf = load_ogg_from_buffer(filedata, name);
545 addBuffer(name, buf);
549 void updateListener(v3f pos, v3f vel, v3f at, v3f up)
551 m_listener_pos = pos;
552 alListener3f(AL_POSITION, pos.X, pos.Y, pos.Z);
553 alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z);
557 alListenerfv(AL_ORIENTATION, f);
558 warn_if_error(alGetError(), "updateListener");
561 void setListenerGain(float gain)
563 alListenerf(AL_GAIN, gain);
566 int playSound(const std::string &name, bool loop, float volume, float fade, float pitch)
571 SoundBuffer *buf = getFetchBuffer(name);
573 infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
579 handle = playSoundRaw(buf, loop, 0.0f, pitch);
580 fadeSound(handle, fade, volume);
582 handle = playSoundRaw(buf, loop, volume, pitch);
587 int playSoundAt(const std::string &name, bool loop, float volume, v3f pos, float pitch)
592 SoundBuffer *buf = getFetchBuffer(name);
594 infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
598 return playSoundRawAt(buf, loop, volume, pos, pitch);
601 void stopSound(int sound)
607 void fadeSound(int soundid, float step, float gain)
609 m_sounds_fading[soundid] = FadeState(step, getSoundGain(soundid), gain);
612 void doFades(float dtime)
614 m_fade_delay += dtime;
616 if (m_fade_delay < 0.1f)
620 for (std::unordered_map<int, FadeState>::iterator i = m_sounds_fading.begin();
621 i != m_sounds_fading.end();) {
622 if (i->second.step < 0.f)
623 chkGain = -(i->second.current_gain);
625 chkGain = i->second.current_gain;
627 if (chkGain < i->second.target_gain) {
628 i->second.current_gain += (i->second.step * m_fade_delay);
629 i->second.current_gain = rangelim(i->second.current_gain, 0, 1);
631 updateSoundGain(i->first, i->second.current_gain);
634 if (i->second.target_gain <= 0.f)
637 m_sounds_fading.erase(i++);
643 bool soundExists(int sound)
646 return (m_sounds_playing.count(sound) != 0);
649 void updateSoundPosition(int id, v3f pos)
651 std::unordered_map<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
652 if (i == m_sounds_playing.end())
654 PlayingSound *sound = i->second;
656 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
657 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
658 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
659 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
662 bool updateSoundGain(int id, float gain)
664 std::unordered_map<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
665 if (i == m_sounds_playing.end())
668 PlayingSound *sound = i->second;
669 alSourcef(sound->source_id, AL_GAIN, gain);
673 float getSoundGain(int id)
675 std::unordered_map<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
676 if (i == m_sounds_playing.end())
679 PlayingSound *sound = i->second;
681 alGetSourcef(sound->source_id, AL_GAIN, &gain);
686 ISoundManager *createOpenALSoundManager(OnDemandSoundFetcher *fetcher)
688 OpenALSoundManager *m = new OpenALSoundManager(fetcher);
689 if(m->m_is_initialized)