3 Copyright (C) 2012 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>
43 #include "utility.h" // myrand()
46 #define BUFFER_SIZE 30000
48 static const char *alcErrorString(ALCenum err)
53 case ALC_INVALID_DEVICE:
54 return "invalid device";
55 case ALC_INVALID_CONTEXT:
56 return "invalid context";
57 case ALC_INVALID_ENUM:
58 return "invalid enum";
59 case ALC_INVALID_VALUE:
60 return "invalid value";
61 case ALC_OUT_OF_MEMORY:
62 return "out of memory";
64 return "<unknown OpenAL error>";
68 static const char *alErrorString(ALenum err)
74 return "invalid name";
76 return "invalid enum";
77 case AL_INVALID_VALUE:
78 return "invalid value";
79 case AL_INVALID_OPERATION:
80 return "invalid operation";
81 case AL_OUT_OF_MEMORY:
82 return "out of memory";
84 return "<unknown OpenAL error>";
88 static ALenum warn_if_error(ALenum err, const char *desc)
90 if(err == AL_NO_ERROR)
92 errorstream<<"WARNING: "<<desc<<": "<<alErrorString(err)<<std::endl;
96 void f3_set(ALfloat *f3, v3f v)
108 std::vector<char> buffer;
111 SoundBuffer* loadOggFile(const std::string &filepath)
113 int endian = 0; // 0 for Little-Endian, 1 for Big-Endian
116 char array[BUFFER_SIZE]; // Local fixed size array
118 OggVorbis_File oggFile;
120 // Do a dumb-ass static string copy for old versions of ov_fopen
121 // because they expect a non-const char*
122 char nonconst[10000];
123 snprintf(nonconst, 10000, "%s", filepath.c_str());
124 // Try opening the given file
125 //if(ov_fopen(filepath.c_str(), &oggFile) != 0)
126 if(ov_fopen(nonconst, &oggFile) != 0)
128 infostream<<"Audio: Error opening "<<filepath<<" for decoding"<<std::endl;
132 SoundBuffer *snd = new SoundBuffer;
134 // Get some information about the OGG file
135 pInfo = ov_info(&oggFile, -1);
137 // Check the number of channels... always use 16-bit samples
138 if(pInfo->channels == 1)
139 snd->format = AL_FORMAT_MONO16;
141 snd->format = AL_FORMAT_STEREO16;
143 // The frequency of the sampling rate
144 snd->freq = pInfo->rate;
146 // Keep reading until all is read
149 // Read up to a buffer's worth of decoded sound data
150 bytes = ov_read(&oggFile, array, BUFFER_SIZE, endian, 2, 1, &bitStream);
155 infostream<<"Audio: Error decoding "<<filepath<<std::endl;
159 // Append to end of buffer
160 snd->buffer.insert(snd->buffer.end(), array, array + bytes);
163 alGenBuffers(1, &snd->buffer_id);
164 alBufferData(snd->buffer_id, snd->format,
165 &(snd->buffer[0]), snd->buffer.size(),
168 ALenum error = alGetError();
170 if(error != AL_NO_ERROR){
171 infostream<<"Audio: OpenAL error: "<<alErrorString(error)
172 <<"preparing sound buffer"<<std::endl;
175 infostream<<"Audio file "<<filepath<<" loaded"<<std::endl;
189 class OpenALSoundManager: public ISoundManager
192 OnDemandSoundFetcher *m_fetcher;
194 ALCcontext *m_context;
197 std::map<std::string, std::vector<SoundBuffer*> > m_buffers;
198 std::map<int, PlayingSound*> m_sounds_playing;
201 bool m_is_initialized;
202 OpenALSoundManager(OnDemandSoundFetcher *fetcher):
208 m_is_initialized(false)
210 ALCenum error = ALC_NO_ERROR;
212 infostream<<"Audio: Initializing..."<<std::endl;
214 m_device = alcOpenDevice(NULL);
216 infostream<<"Audio: No audio device available, audio system "
217 <<"not initialized"<<std::endl;
221 if(alcIsExtensionPresent(m_device, "EXT_vorbis")){
222 infostream<<"Audio: Vorbis extension present"<<std::endl;
225 infostream<<"Audio: Vorbis extension NOT present"<<std::endl;
226 m_can_vorbis = false;
229 m_context = alcCreateContext(m_device, NULL);
231 error = alcGetError(m_device);
232 infostream<<"Audio: Unable to initialize audio context, "
233 <<"aborting audio initialization ("<<alcErrorString(error)
235 alcCloseDevice(m_device);
240 if(!alcMakeContextCurrent(m_context) ||
241 (error = alcGetError(m_device) != ALC_NO_ERROR))
243 infostream<<"Audio: Error setting audio context, aborting audio "
244 <<"initialization ("<<alcErrorString(error)<<")"<<std::endl;
245 alcDestroyContext(m_context);
247 alcCloseDevice(m_device);
252 alDistanceModel(AL_EXPONENT_DISTANCE);
254 infostream<<"Audio: Initialized: OpenAL "<<alGetString(AL_VERSION)
255 <<", using "<<alcGetString(m_device, ALC_DEVICE_SPECIFIER)
258 m_is_initialized = true;
261 ~OpenALSoundManager()
263 infostream<<"Audio: Deinitializing..."<<std::endl;
265 // TODO: Clear SoundBuffers
266 alcMakeContextCurrent(NULL);
267 alcDestroyContext(m_context);
269 alcCloseDevice(m_device);
271 infostream<<"Audio: Deinitialized."<<std::endl;
274 void addBuffer(const std::string &name, SoundBuffer *buf)
276 std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
277 m_buffers.find(name);
278 if(i != m_buffers.end()){
279 i->second.push_back(buf);
282 std::vector<SoundBuffer*> bufs;
284 m_buffers[name] = bufs;
288 SoundBuffer* getBuffer(const std::string &name)
290 std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
291 m_buffers.find(name);
292 if(i == m_buffers.end())
294 std::vector<SoundBuffer*> &bufs = i->second;
295 int j = myrand() % bufs.size();
299 PlayingSound* createPlayingSound(SoundBuffer *buf, bool loop,
302 infostream<<"OpenALSoundManager: Creating playing sound"<<std::endl;
304 PlayingSound *sound = new PlayingSound;
306 warn_if_error(alGetError(), "before createPlayingSound");
307 alGenSources(1, &sound->source_id);
308 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
309 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, true);
310 alSource3f(sound->source_id, AL_POSITION, 0, 0, 0);
311 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
312 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
313 volume = MYMAX(0.0, volume);
314 alSourcef(sound->source_id, AL_GAIN, volume);
315 alSourcePlay(sound->source_id);
316 warn_if_error(alGetError(), "createPlayingSound");
320 PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop,
321 float volume, v3f pos)
323 infostream<<"OpenALSoundManager: Creating positional playing sound"
326 PlayingSound *sound = new PlayingSound;
328 warn_if_error(alGetError(), "before createPlayingSoundAt");
329 alGenSources(1, &sound->source_id);
330 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
331 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
332 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
333 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
334 //alSourcef(sound->source_id, AL_ROLLOFF_FACTOR, 0.7);
335 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
336 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
337 volume = MYMAX(0.0, volume);
338 alSourcef(sound->source_id, AL_GAIN, volume);
339 alSourcePlay(sound->source_id);
340 warn_if_error(alGetError(), "createPlayingSoundAt");
344 int playSoundRaw(SoundBuffer *buf, bool loop, float volume)
347 PlayingSound *sound = createPlayingSound(buf, loop, volume);
350 int id = m_next_id++;
351 m_sounds_playing[id] = sound;
355 int playSoundRawAt(SoundBuffer *buf, bool loop, float volume, v3f pos)
358 PlayingSound *sound = createPlayingSoundAt(buf, loop, volume, pos);
361 int id = m_next_id++;
362 m_sounds_playing[id] = sound;
366 void deleteSound(int id)
368 std::map<int, PlayingSound*>::iterator i =
369 m_sounds_playing.find(id);
370 if(i == m_sounds_playing.end())
372 PlayingSound *sound = i->second;
374 alDeleteSources(1, &sound->source_id);
377 m_sounds_playing.erase(id);
380 /* If buffer does not exist, consult the fetcher */
381 SoundBuffer* getFetchBuffer(const std::string name)
383 SoundBuffer *buf = getBuffer(name);
388 std::set<std::string> paths;
389 std::set<std::string> datas;
390 m_fetcher->fetchSounds(name, paths, datas);
391 for(std::set<std::string>::iterator i = paths.begin();
392 i != paths.end(); i++){
393 loadSoundFile(name, *i);
395 for(std::set<std::string>::iterator i = datas.begin();
396 i != datas.end(); i++){
397 loadSoundData(name, *i);
399 return getBuffer(name);
402 // Remove stopped sounds
405 verbosestream<<"OpenALSoundManager::maintain(): "
406 <<m_sounds_playing.size()<<" playing sounds, "
407 <<m_buffers.size()<<" sound names loaded"<<std::endl;
408 std::set<int> del_list;
409 for(std::map<int, PlayingSound*>::iterator
410 i = m_sounds_playing.begin();
411 i != m_sounds_playing.end(); i++)
414 PlayingSound *sound = i->second;
415 // If not playing, remove it
418 alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state);
419 if(state != AL_PLAYING){
424 if(del_list.size() != 0)
425 verbosestream<<"OpenALSoundManager::maintain(): deleting "
426 <<del_list.size()<<" playing sounds"<<std::endl;
427 for(std::set<int>::iterator i = del_list.begin();
428 i != del_list.end(); i++)
436 bool loadSoundFile(const std::string &name,
437 const std::string &filepath)
439 SoundBuffer *buf = loadOggFile(filepath);
441 addBuffer(name, buf);
444 bool loadSoundData(const std::string &name,
445 const std::string &filedata)
447 // The vorbis API sucks; just write it to a file and use vorbisfile
448 // TODO: Actually load it directly from memory
449 std::string basepath = porting::path_user + DIR_DELIM + "cache" +
451 std::string path = basepath + DIR_DELIM + "tmp.ogg";
452 verbosestream<<"OpenALSoundManager::loadSoundData(): Writing "
453 <<"temporary file to ["<<path<<"]"<<std::endl;
454 fs::CreateAllDirs(basepath);
455 std::ofstream of(path.c_str(), std::ios::binary);
456 of.write(filedata.c_str(), filedata.size());
458 return loadSoundFile(name, path);
461 void updateListener(v3f pos, v3f vel, v3f at, v3f up)
463 m_listener_pos = pos;
464 alListener3f(AL_POSITION, pos.X, pos.Y, pos.Z);
465 alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z);
469 alListenerfv(AL_ORIENTATION, f);
470 warn_if_error(alGetError(), "updateListener");
473 void setListenerGain(float gain)
475 alListenerf(AL_GAIN, gain);
478 int playSound(const std::string &name, bool loop, float volume)
483 SoundBuffer *buf = getFetchBuffer(name);
485 infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
489 return playSoundRaw(buf, loop, volume);
491 int playSoundAt(const std::string &name, bool loop, float volume, v3f pos)
496 SoundBuffer *buf = getFetchBuffer(name);
498 infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
502 return playSoundRawAt(buf, loop, volume, pos);
504 void stopSound(int sound)
509 bool soundExists(int sound)
512 return (m_sounds_playing.count(sound) != 0);
514 void updateSoundPosition(int id, v3f pos)
516 std::map<int, PlayingSound*>::iterator i =
517 m_sounds_playing.find(id);
518 if(i == m_sounds_playing.end())
520 PlayingSound *sound = i->second;
522 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
523 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
524 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
525 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
529 ISoundManager *createOpenALSoundManager(OnDemandSoundFetcher *fetcher)
531 OpenALSoundManager *m = new OpenALSoundManager(fetcher);
532 if(m->m_is_initialized)