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>
41 #include "utility.h" // myrand()
47 #define BUFFER_SIZE 30000
49 static const char *alcErrorString(ALCenum err)
54 case ALC_INVALID_DEVICE:
55 return "invalid device";
56 case ALC_INVALID_CONTEXT:
57 return "invalid context";
58 case ALC_INVALID_ENUM:
59 return "invalid enum";
60 case ALC_INVALID_VALUE:
61 return "invalid value";
62 case ALC_OUT_OF_MEMORY:
63 return "out of memory";
65 return "<unknown OpenAL error>";
69 static const char *alErrorString(ALenum err)
75 return "invalid name";
77 return "invalid enum";
78 case AL_INVALID_VALUE:
79 return "invalid value";
80 case AL_INVALID_OPERATION:
81 return "invalid operation";
82 case AL_OUT_OF_MEMORY:
83 return "out of memory";
85 return "<unknown OpenAL error>";
89 static ALenum warn_if_error(ALenum err, const char *desc)
91 if(err == AL_NO_ERROR)
93 errorstream<<"WARNING: "<<desc<<": "<<alErrorString(err)<<std::endl;
97 void f3_set(ALfloat *f3, v3f v)
109 std::vector<char> buffer;
112 SoundBuffer* loadOggFile(const std::string &filepath)
114 int endian = 0; // 0 for Little-Endian, 1 for Big-Endian
117 char array[BUFFER_SIZE]; // Local fixed size array
119 OggVorbis_File oggFile;
121 // Do a dumb-ass static string copy for old versions of ov_fopen
122 // because they expect a non-const char*
123 char nonconst[10000];
124 snprintf(nonconst, 10000, "%s", filepath.c_str());
125 // Try opening the given file
126 //if(ov_fopen(filepath.c_str(), &oggFile) != 0)
127 if(ov_fopen(nonconst, &oggFile) != 0)
129 infostream<<"Audio: Error opening "<<filepath<<" for decoding"<<std::endl;
133 SoundBuffer *snd = new SoundBuffer;
135 // Get some information about the OGG file
136 pInfo = ov_info(&oggFile, -1);
138 // Check the number of channels... always use 16-bit samples
139 if(pInfo->channels == 1)
140 snd->format = AL_FORMAT_MONO16;
142 snd->format = AL_FORMAT_STEREO16;
144 // The frequency of the sampling rate
145 snd->freq = pInfo->rate;
147 // Keep reading until all is read
150 // Read up to a buffer's worth of decoded sound data
151 bytes = ov_read(&oggFile, array, BUFFER_SIZE, endian, 2, 1, &bitStream);
156 infostream<<"Audio: Error decoding "<<filepath<<std::endl;
160 // Append to end of buffer
161 snd->buffer.insert(snd->buffer.end(), array, array + bytes);
164 alGenBuffers(1, &snd->buffer_id);
165 alBufferData(snd->buffer_id, snd->format,
166 &(snd->buffer[0]), snd->buffer.size(),
169 ALenum error = alGetError();
171 if(error != AL_NO_ERROR){
172 infostream<<"Audio: OpenAL error: "<<alErrorString(error)
173 <<"preparing sound buffer"<<std::endl;
176 infostream<<"Audio file "<<filepath<<" loaded"<<std::endl;
190 class OpenALSoundManager: public ISoundManager
193 OnDemandSoundFetcher *m_fetcher;
195 ALCcontext *m_context;
198 std::map<std::string, std::vector<SoundBuffer*> > m_buffers;
199 std::map<int, PlayingSound*> m_sounds_playing;
202 bool m_is_initialized;
203 OpenALSoundManager(OnDemandSoundFetcher *fetcher):
209 m_is_initialized(false)
211 ALCenum error = ALC_NO_ERROR;
213 infostream<<"Audio: Initializing..."<<std::endl;
215 m_device = alcOpenDevice(NULL);
217 infostream<<"Audio: No audio device available, audio system "
218 <<"not initialized"<<std::endl;
222 if(alcIsExtensionPresent(m_device, "EXT_vorbis")){
223 infostream<<"Audio: Vorbis extension present"<<std::endl;
226 infostream<<"Audio: Vorbis extension NOT present"<<std::endl;
227 m_can_vorbis = false;
230 m_context = alcCreateContext(m_device, NULL);
232 error = alcGetError(m_device);
233 infostream<<"Audio: Unable to initialize audio context, "
234 <<"aborting audio initialization ("<<alcErrorString(error)
236 alcCloseDevice(m_device);
241 if(!alcMakeContextCurrent(m_context) ||
242 (error = alcGetError(m_device) != ALC_NO_ERROR))
244 infostream<<"Audio: Error setting audio context, aborting audio "
245 <<"initialization ("<<alcErrorString(error)<<")"<<std::endl;
246 alcDestroyContext(m_context);
248 alcCloseDevice(m_device);
253 alDistanceModel(AL_EXPONENT_DISTANCE);
255 infostream<<"Audio: Initialized: OpenAL "<<alGetString(AL_VERSION)
256 <<", using "<<alcGetString(m_device, ALC_DEVICE_SPECIFIER)
259 m_is_initialized = true;
262 ~OpenALSoundManager()
264 infostream<<"Audio: Deinitializing..."<<std::endl;
266 // TODO: Clear SoundBuffers
267 alcMakeContextCurrent(NULL);
268 alcDestroyContext(m_context);
270 alcCloseDevice(m_device);
272 infostream<<"Audio: Deinitialized."<<std::endl;
275 void addBuffer(const std::string &name, SoundBuffer *buf)
277 std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
278 m_buffers.find(name);
279 if(i != m_buffers.end()){
280 i->second.push_back(buf);
283 std::vector<SoundBuffer*> bufs;
285 m_buffers[name] = bufs;
289 SoundBuffer* getBuffer(const std::string &name)
291 std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
292 m_buffers.find(name);
293 if(i == m_buffers.end())
295 std::vector<SoundBuffer*> &bufs = i->second;
296 int j = myrand() % bufs.size();
300 PlayingSound* createPlayingSound(SoundBuffer *buf, bool loop,
303 infostream<<"OpenALSoundManager: Creating playing sound"<<std::endl;
305 PlayingSound *sound = new PlayingSound;
307 warn_if_error(alGetError(), "before createPlayingSound");
308 alGenSources(1, &sound->source_id);
309 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
310 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, true);
311 alSource3f(sound->source_id, AL_POSITION, 0, 0, 0);
312 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
313 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
314 volume = MYMAX(0.0, volume);
315 alSourcef(sound->source_id, AL_GAIN, volume);
316 alSourcePlay(sound->source_id);
317 warn_if_error(alGetError(), "createPlayingSound");
321 PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop,
322 float volume, v3f pos)
324 infostream<<"OpenALSoundManager: Creating positional playing sound"
327 PlayingSound *sound = new PlayingSound;
329 warn_if_error(alGetError(), "before createPlayingSoundAt");
330 alGenSources(1, &sound->source_id);
331 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
332 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
333 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
334 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
335 //alSourcef(sound->source_id, AL_ROLLOFF_FACTOR, 0.7);
336 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
337 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
338 volume = MYMAX(0.0, volume);
339 alSourcef(sound->source_id, AL_GAIN, volume);
340 alSourcePlay(sound->source_id);
341 warn_if_error(alGetError(), "createPlayingSoundAt");
345 int playSoundRaw(SoundBuffer *buf, bool loop, float volume)
348 PlayingSound *sound = createPlayingSound(buf, loop, volume);
351 int id = m_next_id++;
352 m_sounds_playing[id] = sound;
356 int playSoundRawAt(SoundBuffer *buf, bool loop, float volume, v3f pos)
359 PlayingSound *sound = createPlayingSoundAt(buf, loop, volume, pos);
362 int id = m_next_id++;
363 m_sounds_playing[id] = sound;
367 void deleteSound(int id)
369 std::map<int, PlayingSound*>::iterator i =
370 m_sounds_playing.find(id);
371 if(i == m_sounds_playing.end())
373 PlayingSound *sound = i->second;
375 alDeleteSources(1, &sound->source_id);
378 m_sounds_playing.erase(id);
381 /* If buffer does not exist, consult the fetcher */
382 SoundBuffer* getFetchBuffer(const std::string name)
384 SoundBuffer *buf = getBuffer(name);
389 std::set<std::string> paths;
390 std::set<std::string> datas;
391 m_fetcher->fetchSounds(name, paths, datas);
392 for(std::set<std::string>::iterator i = paths.begin();
393 i != paths.end(); i++){
394 loadSoundFile(name, *i);
396 for(std::set<std::string>::iterator i = datas.begin();
397 i != datas.end(); i++){
398 loadSoundData(name, *i);
400 return getBuffer(name);
403 // Remove stopped sounds
406 verbosestream<<"OpenALSoundManager::maintain(): "
407 <<m_sounds_playing.size()<<" playing sounds, "
408 <<m_buffers.size()<<" sound names loaded"<<std::endl;
409 std::set<int> del_list;
410 for(std::map<int, PlayingSound*>::iterator
411 i = m_sounds_playing.begin();
412 i != m_sounds_playing.end(); i++)
415 PlayingSound *sound = i->second;
416 // If not playing, remove it
419 alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state);
420 if(state != AL_PLAYING){
425 if(del_list.size() != 0)
426 verbosestream<<"OpenALSoundManager::maintain(): deleting "
427 <<del_list.size()<<" playing sounds"<<std::endl;
428 for(std::set<int>::iterator i = del_list.begin();
429 i != del_list.end(); i++)
437 bool loadSoundFile(const std::string &name,
438 const std::string &filepath)
440 SoundBuffer *buf = loadOggFile(filepath);
442 addBuffer(name, buf);
445 bool loadSoundData(const std::string &name,
446 const std::string &filedata)
448 // The vorbis API sucks; just write it to a file and use vorbisfile
449 // TODO: Actually load it directly from memory
450 std::string basepath = porting::path_user + DIR_DELIM + "cache" +
452 std::string path = basepath + DIR_DELIM + "tmp.ogg";
453 verbosestream<<"OpenALSoundManager::loadSoundData(): Writing "
454 <<"temporary file to ["<<path<<"]"<<std::endl;
455 fs::CreateAllDirs(basepath);
456 std::ofstream of(path.c_str(), std::ios::binary);
457 of.write(filedata.c_str(), filedata.size());
459 return loadSoundFile(name, path);
462 void updateListener(v3f pos, v3f vel, v3f at, v3f up)
464 m_listener_pos = pos;
465 alListener3f(AL_POSITION, pos.X, pos.Y, pos.Z);
466 alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z);
470 alListenerfv(AL_ORIENTATION, f);
471 warn_if_error(alGetError(), "updateListener");
474 void setListenerGain(float gain)
476 alListenerf(AL_GAIN, gain);
479 int playSound(const std::string &name, bool loop, float volume)
484 SoundBuffer *buf = getFetchBuffer(name);
486 infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
490 return playSoundRaw(buf, loop, volume);
492 int playSoundAt(const std::string &name, bool loop, float volume, v3f pos)
497 SoundBuffer *buf = getFetchBuffer(name);
499 infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
503 return playSoundRawAt(buf, loop, volume, pos);
505 void stopSound(int sound)
510 bool soundExists(int sound)
513 return (m_sounds_playing.count(sound) != 0);
515 void updateSoundPosition(int id, v3f pos)
517 std::map<int, PlayingSound*>::iterator i =
518 m_sounds_playing.find(id);
519 if(i == m_sounds_playing.end())
521 PlayingSound *sound = i->second;
523 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
524 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
525 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
526 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
530 ISoundManager *createOpenALSoundManager(OnDemandSoundFetcher *fetcher)
532 OpenALSoundManager *m = new OpenALSoundManager(fetcher);
533 if(m->m_is_initialized)