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 General Public License as published by
11 the Free Software Foundation; either version 2 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 General Public License for more details.
19 You should have received a copy of the GNU 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)