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 // Try opening the given file
121 if(ov_fopen(filepath.c_str(), &oggFile) != 0)
123 infostream<<"Audio: Error opening "<<filepath<<" for decoding"<<std::endl;
127 SoundBuffer *snd = new SoundBuffer;
129 // Get some information about the OGG file
130 pInfo = ov_info(&oggFile, -1);
132 // Check the number of channels... always use 16-bit samples
133 if(pInfo->channels == 1)
134 snd->format = AL_FORMAT_MONO16;
136 snd->format = AL_FORMAT_STEREO16;
138 // The frequency of the sampling rate
139 snd->freq = pInfo->rate;
141 // Keep reading until all is read
144 // Read up to a buffer's worth of decoded sound data
145 bytes = ov_read(&oggFile, array, BUFFER_SIZE, endian, 2, 1, &bitStream);
150 infostream<<"Audio: Error decoding "<<filepath<<std::endl;
154 // Append to end of buffer
155 snd->buffer.insert(snd->buffer.end(), array, array + bytes);
158 alGenBuffers(1, &snd->buffer_id);
159 alBufferData(snd->buffer_id, snd->format,
160 &(snd->buffer[0]), snd->buffer.size(),
163 ALenum error = alGetError();
165 if(error != AL_NO_ERROR){
166 infostream<<"Audio: OpenAL error: "<<alErrorString(error)
167 <<"preparing sound buffer"<<std::endl;
170 infostream<<"Audio file "<<filepath<<" loaded"<<std::endl;
184 class OpenALSoundManager: public ISoundManager
187 OnDemandSoundFetcher *m_fetcher;
189 ALCcontext *m_context;
192 std::map<std::string, std::vector<SoundBuffer*> > m_buffers;
193 std::map<int, PlayingSound*> m_sounds_playing;
196 OpenALSoundManager(OnDemandSoundFetcher *fetcher):
203 ALCenum error = ALC_NO_ERROR;
205 infostream<<"Audio: Initializing..."<<std::endl;
207 m_device = alcOpenDevice(NULL);
209 infostream<<"Audio: No audio device available, audio system "
210 <<"not initialized"<<std::endl;
214 if(alcIsExtensionPresent(m_device, "EXT_vorbis")){
215 infostream<<"Audio: Vorbis extension present"<<std::endl;
218 infostream<<"Audio: Vorbis extension NOT present"<<std::endl;
219 m_can_vorbis = false;
222 m_context = alcCreateContext(m_device, NULL);
224 error = alcGetError(m_device);
225 infostream<<"Audio: Unable to initialize audio context, "
226 <<"aborting audio initialization ("<<alcErrorString(error)
228 alcCloseDevice(m_device);
233 if(!alcMakeContextCurrent(m_context) ||
234 (error = alcGetError(m_device) != ALC_NO_ERROR))
236 infostream<<"Audio: Error setting audio context, aborting audio "
237 <<"initialization ("<<alcErrorString(error)<<")"<<std::endl;
238 alcDestroyContext(m_context);
240 alcCloseDevice(m_device);
245 alDistanceModel(AL_EXPONENT_DISTANCE);
247 infostream<<"Audio: Initialized: OpenAL "<<alGetString(AL_VERSION)
248 <<", using "<<alcGetString(m_device, ALC_DEVICE_SPECIFIER)
252 ~OpenALSoundManager()
254 infostream<<"Audio: Deinitializing..."<<std::endl;
256 // TODO: Clear SoundBuffers
257 alcMakeContextCurrent(NULL);
258 alcDestroyContext(m_context);
260 alcCloseDevice(m_device);
262 infostream<<"Audio: Deinitialized."<<std::endl;
265 void addBuffer(const std::string &name, SoundBuffer *buf)
267 std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
268 m_buffers.find(name);
269 if(i != m_buffers.end()){
270 i->second.push_back(buf);
273 std::vector<SoundBuffer*> bufs;
275 m_buffers[name] = bufs;
279 SoundBuffer* getBuffer(const std::string &name)
281 std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
282 m_buffers.find(name);
283 if(i == m_buffers.end())
285 std::vector<SoundBuffer*> &bufs = i->second;
286 int j = myrand() % bufs.size();
290 PlayingSound* createPlayingSound(SoundBuffer *buf, bool loop,
293 infostream<<"OpenALSoundManager: Creating playing sound"<<std::endl;
295 PlayingSound *sound = new PlayingSound;
297 warn_if_error(alGetError(), "before createPlayingSound");
298 alGenSources(1, &sound->source_id);
299 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
300 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, true);
301 alSource3f(sound->source_id, AL_POSITION, 0, 0, 0);
302 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
303 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
304 volume = MYMAX(0.0, volume);
305 alSourcef(sound->source_id, AL_GAIN, volume);
306 alSourcePlay(sound->source_id);
307 warn_if_error(alGetError(), "createPlayingSound");
311 PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop,
312 float volume, v3f pos)
314 infostream<<"OpenALSoundManager: Creating positional playing sound"
317 PlayingSound *sound = new PlayingSound;
319 warn_if_error(alGetError(), "before createPlayingSoundAt");
320 alGenSources(1, &sound->source_id);
321 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
322 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
323 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
324 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
325 //alSourcef(sound->source_id, AL_ROLLOFF_FACTOR, 0.7);
326 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
327 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
328 volume = MYMAX(0.0, volume);
329 alSourcef(sound->source_id, AL_GAIN, volume);
330 alSourcePlay(sound->source_id);
331 warn_if_error(alGetError(), "createPlayingSoundAt");
335 int playSoundRaw(SoundBuffer *buf, bool loop, float volume)
338 PlayingSound *sound = createPlayingSound(buf, loop, volume);
341 int id = m_next_id++;
342 m_sounds_playing[id] = sound;
346 int playSoundRawAt(SoundBuffer *buf, bool loop, float volume, v3f pos)
349 PlayingSound *sound = createPlayingSoundAt(buf, loop, volume, pos);
352 int id = m_next_id++;
353 m_sounds_playing[id] = sound;
357 void deleteSound(int id)
359 std::map<int, PlayingSound*>::iterator i =
360 m_sounds_playing.find(id);
361 if(i == m_sounds_playing.end())
363 PlayingSound *sound = i->second;
365 alDeleteSources(1, &sound->source_id);
368 m_sounds_playing.erase(id);
371 /* If buffer does not exist, consult the fetcher */
372 SoundBuffer* getFetchBuffer(const std::string name)
374 SoundBuffer *buf = getBuffer(name);
379 std::set<std::string> paths;
380 std::set<std::string> datas;
381 m_fetcher->fetchSounds(name, paths, datas);
382 for(std::set<std::string>::iterator i = paths.begin();
383 i != paths.end(); i++){
384 loadSoundFile(name, *i);
386 for(std::set<std::string>::iterator i = datas.begin();
387 i != datas.end(); i++){
388 loadSoundData(name, *i);
390 return getBuffer(name);
393 // Remove stopped sounds
396 verbosestream<<"OpenALSoundManager::maintain(): "
397 <<m_sounds_playing.size()<<" playing sounds, "
398 <<m_buffers.size()<<" sound names loaded"<<std::endl;
399 std::set<int> del_list;
400 for(std::map<int, PlayingSound*>::iterator
401 i = m_sounds_playing.begin();
402 i != m_sounds_playing.end(); i++)
405 PlayingSound *sound = i->second;
406 // If not playing, remove it
409 alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state);
410 if(state != AL_PLAYING){
415 if(del_list.size() != 0)
416 verbosestream<<"OpenALSoundManager::maintain(): deleting "
417 <<del_list.size()<<" playing sounds"<<std::endl;
418 for(std::set<int>::iterator i = del_list.begin();
419 i != del_list.end(); i++)
427 bool loadSoundFile(const std::string &name,
428 const std::string &filepath)
430 SoundBuffer *buf = loadOggFile(filepath);
432 addBuffer(name, buf);
435 bool loadSoundData(const std::string &name,
436 const std::string &filedata)
438 // The vorbis API sucks; just write it to a file and use vorbisfile
439 // TODO: Actually load it directly from memory
440 std::string basepath = porting::path_user + DIR_DELIM + "cache" +
442 std::string path = basepath + DIR_DELIM + "tmp.ogg";
443 verbosestream<<"OpenALSoundManager::loadSoundData(): Writing "
444 <<"temporary file to ["<<path<<"]"<<std::endl;
445 fs::CreateAllDirs(basepath);
446 std::ofstream of(path.c_str(), std::ios::binary);
447 of.write(filedata.c_str(), filedata.size());
449 return loadSoundFile(name, path);
452 void updateListener(v3f pos, v3f vel, v3f at, v3f up)
454 m_listener_pos = pos;
455 alListener3f(AL_POSITION, pos.X, pos.Y, pos.Z);
456 alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z);
460 alListenerfv(AL_ORIENTATION, f);
461 warn_if_error(alGetError(), "updateListener");
464 int playSound(const std::string &name, bool loop, float volume)
469 SoundBuffer *buf = getFetchBuffer(name);
471 infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
475 return playSoundRaw(buf, loop, volume);
477 int playSoundAt(const std::string &name, bool loop, float volume, v3f pos)
482 SoundBuffer *buf = getFetchBuffer(name);
484 infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
488 return playSoundRawAt(buf, loop, volume, pos);
490 void stopSound(int sound)
495 bool soundExists(int sound)
498 return (m_sounds_playing.count(sound) != 0);
500 void updateSoundPosition(int id, v3f pos)
502 std::map<int, PlayingSound*>::iterator i =
503 m_sounds_playing.find(id);
504 if(i == m_sounds_playing.end())
506 PlayingSound *sound = i->second;
508 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
509 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
510 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
511 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
515 ISoundManager *createOpenALSoundManager(OnDemandSoundFetcher *fetcher)
517 return new OpenALSoundManager(fetcher);