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()
43 #include "debug.h" // assert()
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 errorstream<<"WARNING: "<<desc<<": "<<alErrorString(err)<<std::endl;
99 void f3_set(ALfloat *f3, v3f v)
111 std::vector<char> buffer;
114 SoundBuffer* loadOggFile(const std::string &filepath)
116 int endian = 0; // 0 for Little-Endian, 1 for Big-Endian
119 char array[BUFFER_SIZE]; // Local fixed size array
121 OggVorbis_File oggFile;
123 // Do a dumb-ass static string copy for old versions of ov_fopen
124 // because they expect a non-const char*
125 char nonconst[10000];
126 snprintf(nonconst, 10000, "%s", filepath.c_str());
127 // Try opening the given file
128 //if(ov_fopen(filepath.c_str(), &oggFile) != 0)
129 if(ov_fopen(nonconst, &oggFile) != 0)
131 infostream<<"Audio: Error opening "<<filepath<<" for decoding"<<std::endl;
135 SoundBuffer *snd = new SoundBuffer;
137 // Get some information about the OGG file
138 pInfo = ov_info(&oggFile, -1);
140 // Check the number of channels... always use 16-bit samples
141 if(pInfo->channels == 1)
142 snd->format = AL_FORMAT_MONO16;
144 snd->format = AL_FORMAT_STEREO16;
146 // The frequency of the sampling rate
147 snd->freq = pInfo->rate;
149 // Keep reading until all is read
152 // Read up to a buffer's worth of decoded sound data
153 bytes = ov_read(&oggFile, array, BUFFER_SIZE, endian, 2, 1, &bitStream);
158 infostream<<"Audio: Error decoding "<<filepath<<std::endl;
162 // Append to end of buffer
163 snd->buffer.insert(snd->buffer.end(), array, array + bytes);
166 alGenBuffers(1, &snd->buffer_id);
167 alBufferData(snd->buffer_id, snd->format,
168 &(snd->buffer[0]), snd->buffer.size(),
171 ALenum error = alGetError();
173 if(error != AL_NO_ERROR){
174 infostream<<"Audio: OpenAL error: "<<alErrorString(error)
175 <<"preparing sound buffer"<<std::endl;
178 infostream<<"Audio file "<<filepath<<" loaded"<<std::endl;
192 class OpenALSoundManager: public ISoundManager
195 OnDemandSoundFetcher *m_fetcher;
197 ALCcontext *m_context;
200 std::map<std::string, std::vector<SoundBuffer*> > m_buffers;
201 std::map<int, PlayingSound*> m_sounds_playing;
204 bool m_is_initialized;
205 OpenALSoundManager(OnDemandSoundFetcher *fetcher):
211 m_is_initialized(false)
213 ALCenum error = ALC_NO_ERROR;
215 infostream<<"Audio: Initializing..."<<std::endl;
217 m_device = alcOpenDevice(NULL);
219 infostream<<"Audio: No audio device available, audio system "
220 <<"not initialized"<<std::endl;
224 if(alcIsExtensionPresent(m_device, "EXT_vorbis")){
225 infostream<<"Audio: Vorbis extension present"<<std::endl;
228 infostream<<"Audio: Vorbis extension NOT present"<<std::endl;
229 m_can_vorbis = false;
232 m_context = alcCreateContext(m_device, NULL);
234 error = alcGetError(m_device);
235 infostream<<"Audio: Unable to initialize audio context, "
236 <<"aborting audio initialization ("<<alcErrorString(error)
238 alcCloseDevice(m_device);
243 if(!alcMakeContextCurrent(m_context) ||
244 (error = alcGetError(m_device) != ALC_NO_ERROR))
246 infostream<<"Audio: Error setting audio context, aborting audio "
247 <<"initialization ("<<alcErrorString(error)<<")"<<std::endl;
248 alcDestroyContext(m_context);
250 alcCloseDevice(m_device);
255 alDistanceModel(AL_EXPONENT_DISTANCE);
257 infostream<<"Audio: Initialized: OpenAL "<<alGetString(AL_VERSION)
258 <<", using "<<alcGetString(m_device, ALC_DEVICE_SPECIFIER)
261 m_is_initialized = true;
264 ~OpenALSoundManager()
266 infostream<<"Audio: Deinitializing..."<<std::endl;
268 // TODO: Clear SoundBuffers
269 alcMakeContextCurrent(NULL);
270 alcDestroyContext(m_context);
272 alcCloseDevice(m_device);
274 infostream<<"Audio: Deinitialized."<<std::endl;
277 void addBuffer(const std::string &name, SoundBuffer *buf)
279 std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
280 m_buffers.find(name);
281 if(i != m_buffers.end()){
282 i->second.push_back(buf);
285 std::vector<SoundBuffer*> bufs;
287 m_buffers[name] = bufs;
291 SoundBuffer* getBuffer(const std::string &name)
293 std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
294 m_buffers.find(name);
295 if(i == m_buffers.end())
297 std::vector<SoundBuffer*> &bufs = i->second;
298 int j = myrand() % bufs.size();
302 PlayingSound* createPlayingSound(SoundBuffer *buf, bool loop,
305 infostream<<"OpenALSoundManager: Creating playing sound"<<std::endl;
307 PlayingSound *sound = new PlayingSound;
309 warn_if_error(alGetError(), "before createPlayingSound");
310 alGenSources(1, &sound->source_id);
311 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
312 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, true);
313 alSource3f(sound->source_id, AL_POSITION, 0, 0, 0);
314 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
315 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
316 volume = MYMAX(0.0, volume);
317 alSourcef(sound->source_id, AL_GAIN, volume);
318 alSourcePlay(sound->source_id);
319 warn_if_error(alGetError(), "createPlayingSound");
323 PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop,
324 float volume, v3f pos)
326 infostream<<"OpenALSoundManager: Creating positional playing sound"
329 PlayingSound *sound = new PlayingSound;
331 warn_if_error(alGetError(), "before createPlayingSoundAt");
332 alGenSources(1, &sound->source_id);
333 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
334 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
335 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
336 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
337 //alSourcef(sound->source_id, AL_ROLLOFF_FACTOR, 0.7);
338 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
339 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
340 volume = MYMAX(0.0, volume);
341 alSourcef(sound->source_id, AL_GAIN, volume);
342 alSourcePlay(sound->source_id);
343 warn_if_error(alGetError(), "createPlayingSoundAt");
347 int playSoundRaw(SoundBuffer *buf, bool loop, float volume)
350 PlayingSound *sound = createPlayingSound(buf, loop, volume);
353 int id = m_next_id++;
354 m_sounds_playing[id] = sound;
358 int playSoundRawAt(SoundBuffer *buf, bool loop, float volume, v3f pos)
361 PlayingSound *sound = createPlayingSoundAt(buf, loop, volume, pos);
364 int id = m_next_id++;
365 m_sounds_playing[id] = sound;
369 void deleteSound(int id)
371 std::map<int, PlayingSound*>::iterator i =
372 m_sounds_playing.find(id);
373 if(i == m_sounds_playing.end())
375 PlayingSound *sound = i->second;
377 alDeleteSources(1, &sound->source_id);
380 m_sounds_playing.erase(id);
383 /* If buffer does not exist, consult the fetcher */
384 SoundBuffer* getFetchBuffer(const std::string name)
386 SoundBuffer *buf = getBuffer(name);
391 std::set<std::string> paths;
392 std::set<std::string> datas;
393 m_fetcher->fetchSounds(name, paths, datas);
394 for(std::set<std::string>::iterator i = paths.begin();
395 i != paths.end(); i++){
396 loadSoundFile(name, *i);
398 for(std::set<std::string>::iterator i = datas.begin();
399 i != datas.end(); i++){
400 loadSoundData(name, *i);
402 return getBuffer(name);
405 // Remove stopped sounds
408 verbosestream<<"OpenALSoundManager::maintain(): "
409 <<m_sounds_playing.size()<<" playing sounds, "
410 <<m_buffers.size()<<" sound names loaded"<<std::endl;
411 std::set<int> del_list;
412 for(std::map<int, PlayingSound*>::iterator
413 i = m_sounds_playing.begin();
414 i != m_sounds_playing.end(); i++)
417 PlayingSound *sound = i->second;
418 // If not playing, remove it
421 alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state);
422 if(state != AL_PLAYING){
427 if(del_list.size() != 0)
428 verbosestream<<"OpenALSoundManager::maintain(): deleting "
429 <<del_list.size()<<" playing sounds"<<std::endl;
430 for(std::set<int>::iterator i = del_list.begin();
431 i != del_list.end(); i++)
439 bool loadSoundFile(const std::string &name,
440 const std::string &filepath)
442 SoundBuffer *buf = loadOggFile(filepath);
444 addBuffer(name, buf);
447 bool loadSoundData(const std::string &name,
448 const std::string &filedata)
450 // The vorbis API sucks; just write it to a file and use vorbisfile
451 // TODO: Actually load it directly from memory
452 std::string basepath = porting::path_user + DIR_DELIM + "cache" +
454 std::string path = basepath + DIR_DELIM + "tmp.ogg";
455 verbosestream<<"OpenALSoundManager::loadSoundData(): Writing "
456 <<"temporary file to ["<<path<<"]"<<std::endl;
457 fs::CreateAllDirs(basepath);
458 std::ofstream of(path.c_str(), std::ios::binary);
459 of.write(filedata.c_str(), filedata.size());
461 return loadSoundFile(name, path);
464 void updateListener(v3f pos, v3f vel, v3f at, v3f up)
466 m_listener_pos = pos;
467 alListener3f(AL_POSITION, pos.X, pos.Y, pos.Z);
468 alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z);
472 alListenerfv(AL_ORIENTATION, f);
473 warn_if_error(alGetError(), "updateListener");
476 void setListenerGain(float gain)
478 alListenerf(AL_GAIN, gain);
481 int playSound(const std::string &name, bool loop, float volume)
486 SoundBuffer *buf = getFetchBuffer(name);
488 infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
492 return playSoundRaw(buf, loop, volume);
494 int playSoundAt(const std::string &name, bool loop, float volume, v3f pos)
499 SoundBuffer *buf = getFetchBuffer(name);
501 infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
505 return playSoundRawAt(buf, loop, volume, pos);
507 void stopSound(int sound)
512 bool soundExists(int sound)
515 return (m_sounds_playing.count(sound) != 0);
517 void updateSoundPosition(int id, v3f pos)
519 std::map<int, PlayingSound*>::iterator i =
520 m_sounds_playing.find(id);
521 if(i == m_sounds_playing.end())
523 PlayingSound *sound = i->second;
525 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
526 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
527 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
528 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
532 ISoundManager *createOpenALSoundManager(OnDemandSoundFetcher *fetcher)
534 OpenALSoundManager *m = new OpenALSoundManager(fetcher);
535 if(m->m_is_initialized)