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>
39 #include <vorbis/vorbisfile.h>
42 #include "util/numeric.h" // myrand()
48 #define BUFFER_SIZE 30000
50 static const char *alcErrorString(ALCenum err)
55 case ALC_INVALID_DEVICE:
56 return "invalid device";
57 case ALC_INVALID_CONTEXT:
58 return "invalid context";
59 case ALC_INVALID_ENUM:
60 return "invalid enum";
61 case ALC_INVALID_VALUE:
62 return "invalid value";
63 case ALC_OUT_OF_MEMORY:
64 return "out of memory";
66 return "<unknown OpenAL error>";
70 static const char *alErrorString(ALenum err)
76 return "invalid name";
78 return "invalid enum";
79 case AL_INVALID_VALUE:
80 return "invalid value";
81 case AL_INVALID_OPERATION:
82 return "invalid operation";
83 case AL_OUT_OF_MEMORY:
84 return "out of memory";
86 return "<unknown OpenAL error>";
90 static ALenum warn_if_error(ALenum err, const char *desc)
92 if(err == AL_NO_ERROR)
94 warningstream<<desc<<": "<<alErrorString(err)<<std::endl;
98 void f3_set(ALfloat *f3, v3f v)
110 std::vector<char> buffer;
113 SoundBuffer *load_opened_ogg_file(OggVorbis_File *oggFile,
114 const std::string &filename_for_logging)
116 int endian = 0; // 0 for Little-Endian, 1 for Big-Endian
119 char array[BUFFER_SIZE]; // Local fixed size array
122 SoundBuffer *snd = new SoundBuffer;
124 // Get some information about the OGG file
125 pInfo = ov_info(oggFile, -1);
127 // Check the number of channels... always use 16-bit samples
128 if(pInfo->channels == 1)
129 snd->format = AL_FORMAT_MONO16;
131 snd->format = AL_FORMAT_STEREO16;
133 // The frequency of the sampling rate
134 snd->freq = pInfo->rate;
136 // Keep reading until all is read
139 // Read up to a buffer's worth of decoded sound data
140 bytes = ov_read(oggFile, array, BUFFER_SIZE, endian, 2, 1, &bitStream);
145 infostream << "Audio: Error decoding "
146 << filename_for_logging << std::endl;
150 // Append to end of buffer
151 snd->buffer.insert(snd->buffer.end(), array, array + bytes);
154 alGenBuffers(1, &snd->buffer_id);
155 alBufferData(snd->buffer_id, snd->format,
156 &(snd->buffer[0]), snd->buffer.size(),
159 ALenum error = alGetError();
161 if(error != AL_NO_ERROR){
162 infostream<<"Audio: OpenAL error: "<<alErrorString(error)
163 <<"preparing sound buffer"<<std::endl;
166 infostream << "Audio file "
167 << filename_for_logging << " loaded" << std::endl;
175 SoundBuffer *load_ogg_from_file(const std::string &path)
177 OggVorbis_File oggFile;
179 // Try opening the given file.
180 // This requires libvorbis >= 1.3.2, as
181 // previous versions expect a non-const char *
182 if (ov_fopen(path.c_str(), &oggFile) != 0) {
183 infostream << "Audio: Error opening " << path
184 << " for decoding" << std::endl;
188 return load_opened_ogg_file(&oggFile, path);
191 struct BufferSource {
197 size_t buffer_sound_read_func(void *ptr, size_t size, size_t nmemb, void *datasource)
199 BufferSource *s = (BufferSource *)datasource;
200 size_t copied_size = MYMIN(s->len - s->cur_offset, size);
201 memcpy(ptr, s->buf + s->cur_offset, copied_size);
202 s->cur_offset += copied_size;
206 int buffer_sound_seek_func(void *datasource, ogg_int64_t offset, int whence)
208 BufferSource *s = (BufferSource *)datasource;
209 if (whence == SEEK_SET) {
210 if (offset < 0 || (size_t)MYMAX(offset, 0) >= s->len) {
211 // offset out of bounds
214 s->cur_offset = offset;
216 } else if (whence == SEEK_CUR) {
217 if ((size_t)MYMIN(-offset, 0) > s->cur_offset
218 || s->cur_offset + offset > s->len) {
219 // offset out of bounds
222 s->cur_offset += offset;
225 // invalid whence param (SEEK_END doesn't have to be supported)
229 long BufferSourceell_func(void *datasource)
231 BufferSource *s = (BufferSource *)datasource;
232 return s->cur_offset;
235 static ov_callbacks g_buffer_ov_callbacks = {
236 &buffer_sound_read_func,
237 &buffer_sound_seek_func,
239 &BufferSourceell_func
242 SoundBuffer *load_ogg_from_buffer(const std::string &buf, const std::string &id_for_log)
244 OggVorbis_File oggFile;
251 if (ov_open_callbacks(&s, &oggFile, NULL, 0, g_buffer_ov_callbacks) != 0) {
252 infostream << "Audio: Error opening " << id_for_log
253 << " for decoding" << std::endl;
257 return load_opened_ogg_file(&oggFile, id_for_log);
266 class OpenALSoundManager: public ISoundManager
269 OnDemandSoundFetcher *m_fetcher;
271 ALCcontext *m_context;
273 std::map<std::string, std::vector<SoundBuffer*> > m_buffers;
274 std::map<int, PlayingSound*> m_sounds_playing;
277 bool m_is_initialized;
278 OpenALSoundManager(OnDemandSoundFetcher *fetcher):
283 m_is_initialized(false)
285 ALCenum error = ALC_NO_ERROR;
287 infostream<<"Audio: Initializing..."<<std::endl;
289 m_device = alcOpenDevice(NULL);
291 infostream<<"Audio: No audio device available, audio system "
292 <<"not initialized"<<std::endl;
296 m_context = alcCreateContext(m_device, NULL);
298 error = alcGetError(m_device);
299 infostream<<"Audio: Unable to initialize audio context, "
300 <<"aborting audio initialization ("<<alcErrorString(error)
302 alcCloseDevice(m_device);
307 if(!alcMakeContextCurrent(m_context) ||
308 (error = alcGetError(m_device) != ALC_NO_ERROR))
310 infostream<<"Audio: Error setting audio context, aborting audio "
311 <<"initialization ("<<alcErrorString(error)<<")"<<std::endl;
312 alcDestroyContext(m_context);
314 alcCloseDevice(m_device);
319 alDistanceModel(AL_EXPONENT_DISTANCE);
321 infostream<<"Audio: Initialized: OpenAL "<<alGetString(AL_VERSION)
322 <<", using "<<alcGetString(m_device, ALC_DEVICE_SPECIFIER)
325 m_is_initialized = true;
328 ~OpenALSoundManager()
330 infostream<<"Audio: Deinitializing..."<<std::endl;
332 // TODO: Clear SoundBuffers
333 alcMakeContextCurrent(NULL);
334 alcDestroyContext(m_context);
336 alcCloseDevice(m_device);
339 for (std::map<std::string, std::vector<SoundBuffer*> >::iterator i = m_buffers.begin();
340 i != m_buffers.end(); ++i) {
341 for (std::vector<SoundBuffer*>::iterator iter = (*i).second.begin();
342 iter != (*i).second.end(); ++iter) {
348 infostream<<"Audio: Deinitialized."<<std::endl;
351 void addBuffer(const std::string &name, SoundBuffer *buf)
353 std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
354 m_buffers.find(name);
355 if(i != m_buffers.end()){
356 i->second.push_back(buf);
359 std::vector<SoundBuffer*> bufs;
361 m_buffers[name] = bufs;
365 SoundBuffer* getBuffer(const std::string &name)
367 std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
368 m_buffers.find(name);
369 if(i == m_buffers.end())
371 std::vector<SoundBuffer*> &bufs = i->second;
372 int j = myrand() % bufs.size();
376 PlayingSound* createPlayingSound(SoundBuffer *buf, bool loop,
379 infostream<<"OpenALSoundManager: Creating playing sound"<<std::endl;
381 PlayingSound *sound = new PlayingSound;
383 warn_if_error(alGetError(), "before createPlayingSound");
384 alGenSources(1, &sound->source_id);
385 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
386 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, true);
387 alSource3f(sound->source_id, AL_POSITION, 0, 0, 0);
388 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
389 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
390 volume = MYMAX(0.0, volume);
391 alSourcef(sound->source_id, AL_GAIN, volume);
392 alSourcePlay(sound->source_id);
393 warn_if_error(alGetError(), "createPlayingSound");
397 PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop,
398 float volume, v3f pos)
400 infostream<<"OpenALSoundManager: Creating positional playing sound"
403 PlayingSound *sound = new PlayingSound;
405 warn_if_error(alGetError(), "before createPlayingSoundAt");
406 alGenSources(1, &sound->source_id);
407 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
408 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
409 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
410 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
411 //alSourcef(sound->source_id, AL_ROLLOFF_FACTOR, 0.7);
412 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
413 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
414 volume = MYMAX(0.0, volume);
415 alSourcef(sound->source_id, AL_GAIN, volume);
416 alSourcePlay(sound->source_id);
417 warn_if_error(alGetError(), "createPlayingSoundAt");
421 int playSoundRaw(SoundBuffer *buf, bool loop, float volume)
424 PlayingSound *sound = createPlayingSound(buf, loop, volume);
427 int id = m_next_id++;
428 m_sounds_playing[id] = sound;
432 int playSoundRawAt(SoundBuffer *buf, bool loop, float volume, v3f pos)
435 PlayingSound *sound = createPlayingSoundAt(buf, loop, volume, pos);
438 int id = m_next_id++;
439 m_sounds_playing[id] = sound;
443 void deleteSound(int id)
445 std::map<int, PlayingSound*>::iterator i =
446 m_sounds_playing.find(id);
447 if(i == m_sounds_playing.end())
449 PlayingSound *sound = i->second;
451 alDeleteSources(1, &sound->source_id);
454 m_sounds_playing.erase(id);
457 /* If buffer does not exist, consult the fetcher */
458 SoundBuffer* getFetchBuffer(const std::string &name)
460 SoundBuffer *buf = getBuffer(name);
465 std::set<std::string> paths;
466 std::set<std::string> datas;
467 m_fetcher->fetchSounds(name, paths, datas);
468 for(std::set<std::string>::iterator i = paths.begin();
469 i != paths.end(); ++i){
470 loadSoundFile(name, *i);
472 for(std::set<std::string>::iterator i = datas.begin();
473 i != datas.end(); ++i){
474 loadSoundData(name, *i);
476 return getBuffer(name);
479 // Remove stopped sounds
482 verbosestream<<"OpenALSoundManager::maintain(): "
483 <<m_sounds_playing.size()<<" playing sounds, "
484 <<m_buffers.size()<<" sound names loaded"<<std::endl;
485 std::set<int> del_list;
486 for(std::map<int, PlayingSound*>::iterator
487 i = m_sounds_playing.begin();
488 i != m_sounds_playing.end(); ++i)
491 PlayingSound *sound = i->second;
492 // If not playing, remove it
495 alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state);
496 if(state != AL_PLAYING){
501 if(!del_list.empty())
502 verbosestream<<"OpenALSoundManager::maintain(): deleting "
503 <<del_list.size()<<" playing sounds"<<std::endl;
504 for(std::set<int>::iterator i = del_list.begin();
505 i != del_list.end(); ++i)
513 bool loadSoundFile(const std::string &name,
514 const std::string &filepath)
516 SoundBuffer *buf = load_ogg_from_file(filepath);
518 addBuffer(name, buf);
521 bool loadSoundData(const std::string &name,
522 const std::string &filedata)
524 SoundBuffer *buf = load_ogg_from_buffer(filedata, name);
526 addBuffer(name, buf);
530 void updateListener(v3f pos, v3f vel, v3f at, v3f up)
532 m_listener_pos = pos;
533 alListener3f(AL_POSITION, pos.X, pos.Y, pos.Z);
534 alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z);
538 alListenerfv(AL_ORIENTATION, f);
539 warn_if_error(alGetError(), "updateListener");
542 void setListenerGain(float gain)
544 alListenerf(AL_GAIN, gain);
547 int playSound(const std::string &name, bool loop, float volume)
552 SoundBuffer *buf = getFetchBuffer(name);
554 infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
558 return playSoundRaw(buf, loop, volume);
560 int playSoundAt(const std::string &name, bool loop, float volume, v3f pos)
565 SoundBuffer *buf = getFetchBuffer(name);
567 infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
571 return playSoundRawAt(buf, loop, volume, pos);
573 void stopSound(int sound)
578 bool soundExists(int sound)
581 return (m_sounds_playing.count(sound) != 0);
583 void updateSoundPosition(int id, v3f pos)
585 std::map<int, PlayingSound*>::iterator i =
586 m_sounds_playing.find(id);
587 if(i == m_sounds_playing.end())
589 PlayingSound *sound = i->second;
591 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
592 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
593 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
594 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
598 ISoundManager *createOpenALSoundManager(OnDemandSoundFetcher *fetcher)
600 OpenALSoundManager *m = new OpenALSoundManager(fetcher);
601 if(m->m_is_initialized)