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 OpenALSoundManager(OnDemandSoundFetcher *fetcher):
208 ALCenum error = ALC_NO_ERROR;
210 infostream<<"Audio: Initializing..."<<std::endl;
212 m_device = alcOpenDevice(NULL);
214 infostream<<"Audio: No audio device available, audio system "
215 <<"not initialized"<<std::endl;
219 if(alcIsExtensionPresent(m_device, "EXT_vorbis")){
220 infostream<<"Audio: Vorbis extension present"<<std::endl;
223 infostream<<"Audio: Vorbis extension NOT present"<<std::endl;
224 m_can_vorbis = false;
227 m_context = alcCreateContext(m_device, NULL);
229 error = alcGetError(m_device);
230 infostream<<"Audio: Unable to initialize audio context, "
231 <<"aborting audio initialization ("<<alcErrorString(error)
233 alcCloseDevice(m_device);
238 if(!alcMakeContextCurrent(m_context) ||
239 (error = alcGetError(m_device) != ALC_NO_ERROR))
241 infostream<<"Audio: Error setting audio context, aborting audio "
242 <<"initialization ("<<alcErrorString(error)<<")"<<std::endl;
243 alcDestroyContext(m_context);
245 alcCloseDevice(m_device);
250 alDistanceModel(AL_EXPONENT_DISTANCE);
252 infostream<<"Audio: Initialized: OpenAL "<<alGetString(AL_VERSION)
253 <<", using "<<alcGetString(m_device, ALC_DEVICE_SPECIFIER)
257 ~OpenALSoundManager()
259 infostream<<"Audio: Deinitializing..."<<std::endl;
261 // TODO: Clear SoundBuffers
262 alcMakeContextCurrent(NULL);
263 alcDestroyContext(m_context);
265 alcCloseDevice(m_device);
267 infostream<<"Audio: Deinitialized."<<std::endl;
270 void addBuffer(const std::string &name, SoundBuffer *buf)
272 std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
273 m_buffers.find(name);
274 if(i != m_buffers.end()){
275 i->second.push_back(buf);
278 std::vector<SoundBuffer*> bufs;
280 m_buffers[name] = bufs;
284 SoundBuffer* getBuffer(const std::string &name)
286 std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
287 m_buffers.find(name);
288 if(i == m_buffers.end())
290 std::vector<SoundBuffer*> &bufs = i->second;
291 int j = myrand() % bufs.size();
295 PlayingSound* createPlayingSound(SoundBuffer *buf, bool loop,
298 infostream<<"OpenALSoundManager: Creating playing sound"<<std::endl;
300 PlayingSound *sound = new PlayingSound;
302 warn_if_error(alGetError(), "before createPlayingSound");
303 alGenSources(1, &sound->source_id);
304 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
305 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, true);
306 alSource3f(sound->source_id, AL_POSITION, 0, 0, 0);
307 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
308 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
309 volume = MYMAX(0.0, volume);
310 alSourcef(sound->source_id, AL_GAIN, volume);
311 alSourcePlay(sound->source_id);
312 warn_if_error(alGetError(), "createPlayingSound");
316 PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop,
317 float volume, v3f pos)
319 infostream<<"OpenALSoundManager: Creating positional playing sound"
322 PlayingSound *sound = new PlayingSound;
324 warn_if_error(alGetError(), "before createPlayingSoundAt");
325 alGenSources(1, &sound->source_id);
326 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
327 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
328 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
329 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
330 //alSourcef(sound->source_id, AL_ROLLOFF_FACTOR, 0.7);
331 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
332 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
333 volume = MYMAX(0.0, volume);
334 alSourcef(sound->source_id, AL_GAIN, volume);
335 alSourcePlay(sound->source_id);
336 warn_if_error(alGetError(), "createPlayingSoundAt");
340 int playSoundRaw(SoundBuffer *buf, bool loop, float volume)
343 PlayingSound *sound = createPlayingSound(buf, loop, volume);
346 int id = m_next_id++;
347 m_sounds_playing[id] = sound;
351 int playSoundRawAt(SoundBuffer *buf, bool loop, float volume, v3f pos)
354 PlayingSound *sound = createPlayingSoundAt(buf, loop, volume, pos);
357 int id = m_next_id++;
358 m_sounds_playing[id] = sound;
362 void deleteSound(int id)
364 std::map<int, PlayingSound*>::iterator i =
365 m_sounds_playing.find(id);
366 if(i == m_sounds_playing.end())
368 PlayingSound *sound = i->second;
370 alDeleteSources(1, &sound->source_id);
373 m_sounds_playing.erase(id);
376 /* If buffer does not exist, consult the fetcher */
377 SoundBuffer* getFetchBuffer(const std::string name)
379 SoundBuffer *buf = getBuffer(name);
384 std::set<std::string> paths;
385 std::set<std::string> datas;
386 m_fetcher->fetchSounds(name, paths, datas);
387 for(std::set<std::string>::iterator i = paths.begin();
388 i != paths.end(); i++){
389 loadSoundFile(name, *i);
391 for(std::set<std::string>::iterator i = datas.begin();
392 i != datas.end(); i++){
393 loadSoundData(name, *i);
395 return getBuffer(name);
398 // Remove stopped sounds
401 verbosestream<<"OpenALSoundManager::maintain(): "
402 <<m_sounds_playing.size()<<" playing sounds, "
403 <<m_buffers.size()<<" sound names loaded"<<std::endl;
404 std::set<int> del_list;
405 for(std::map<int, PlayingSound*>::iterator
406 i = m_sounds_playing.begin();
407 i != m_sounds_playing.end(); i++)
410 PlayingSound *sound = i->second;
411 // If not playing, remove it
414 alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state);
415 if(state != AL_PLAYING){
420 if(del_list.size() != 0)
421 verbosestream<<"OpenALSoundManager::maintain(): deleting "
422 <<del_list.size()<<" playing sounds"<<std::endl;
423 for(std::set<int>::iterator i = del_list.begin();
424 i != del_list.end(); i++)
432 bool loadSoundFile(const std::string &name,
433 const std::string &filepath)
435 SoundBuffer *buf = loadOggFile(filepath);
437 addBuffer(name, buf);
440 bool loadSoundData(const std::string &name,
441 const std::string &filedata)
443 // The vorbis API sucks; just write it to a file and use vorbisfile
444 // TODO: Actually load it directly from memory
445 std::string basepath = porting::path_user + DIR_DELIM + "cache" +
447 std::string path = basepath + DIR_DELIM + "tmp.ogg";
448 verbosestream<<"OpenALSoundManager::loadSoundData(): Writing "
449 <<"temporary file to ["<<path<<"]"<<std::endl;
450 fs::CreateAllDirs(basepath);
451 std::ofstream of(path.c_str(), std::ios::binary);
452 of.write(filedata.c_str(), filedata.size());
454 return loadSoundFile(name, path);
457 void updateListener(v3f pos, v3f vel, v3f at, v3f up)
459 m_listener_pos = pos;
460 alListener3f(AL_POSITION, pos.X, pos.Y, pos.Z);
461 alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z);
465 alListenerfv(AL_ORIENTATION, f);
466 warn_if_error(alGetError(), "updateListener");
469 int playSound(const std::string &name, bool loop, float volume)
474 SoundBuffer *buf = getFetchBuffer(name);
476 infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
480 return playSoundRaw(buf, loop, volume);
482 int playSoundAt(const std::string &name, bool loop, float volume, v3f pos)
487 SoundBuffer *buf = getFetchBuffer(name);
489 infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
493 return playSoundRawAt(buf, loop, volume, pos);
495 void stopSound(int sound)
500 bool soundExists(int sound)
503 return (m_sounds_playing.count(sound) != 0);
505 void updateSoundPosition(int id, v3f pos)
507 std::map<int, PlayingSound*>::iterator i =
508 m_sounds_playing.find(id);
509 if(i == m_sounds_playing.end())
511 PlayingSound *sound = i->second;
513 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
514 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
515 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
516 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
520 ISoundManager *createOpenALSoundManager(OnDemandSoundFetcher *fetcher)
522 return new OpenALSoundManager(fetcher);