Line data Source code
1 : /*
2 : Minetest
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>
8 :
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.
13 :
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.
18 :
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.
22 : */
23 :
24 : #include "sound_openal.h"
25 :
26 : #if defined(_WIN32)
27 : #include <al.h>
28 : #include <alc.h>
29 : //#include <alext.h>
30 : #elif defined(__APPLE__)
31 : #include <OpenAL/al.h>
32 : #include <OpenAL/alc.h>
33 : //#include <OpenAL/alext.h>
34 : #else
35 : #include <AL/al.h>
36 : #include <AL/alc.h>
37 : #include <AL/alext.h>
38 : #endif
39 : #include <vorbis/vorbisfile.h>
40 : #include <assert.h>
41 : #include "log.h"
42 : #include "filesys.h"
43 : #include "util/numeric.h" // myrand()
44 : #include "porting.h"
45 : #include <map>
46 : #include <vector>
47 : #include <fstream>
48 :
49 : #define BUFFER_SIZE 30000
50 :
51 0 : static const char *alcErrorString(ALCenum err)
52 : {
53 0 : switch (err) {
54 : case ALC_NO_ERROR:
55 0 : return "no error";
56 : case ALC_INVALID_DEVICE:
57 0 : return "invalid device";
58 : case ALC_INVALID_CONTEXT:
59 0 : return "invalid context";
60 : case ALC_INVALID_ENUM:
61 0 : return "invalid enum";
62 : case ALC_INVALID_VALUE:
63 0 : return "invalid value";
64 : case ALC_OUT_OF_MEMORY:
65 0 : return "out of memory";
66 : default:
67 0 : return "<unknown OpenAL error>";
68 : }
69 : }
70 :
71 0 : static const char *alErrorString(ALenum err)
72 : {
73 0 : switch (err) {
74 : case AL_NO_ERROR:
75 0 : return "no error";
76 : case AL_INVALID_NAME:
77 0 : return "invalid name";
78 : case AL_INVALID_ENUM:
79 0 : return "invalid enum";
80 : case AL_INVALID_VALUE:
81 0 : return "invalid value";
82 : case AL_INVALID_OPERATION:
83 0 : return "invalid operation";
84 : case AL_OUT_OF_MEMORY:
85 0 : return "out of memory";
86 : default:
87 0 : return "<unknown OpenAL error>";
88 : }
89 : }
90 :
91 1288 : static ALenum warn_if_error(ALenum err, const char *desc)
92 : {
93 1288 : if(err == AL_NO_ERROR)
94 1288 : return err;
95 0 : errorstream<<"WARNING: "<<desc<<": "<<alErrorString(err)<<std::endl;
96 0 : return err;
97 : }
98 :
99 2332 : void f3_set(ALfloat *f3, v3f v)
100 : {
101 2332 : f3[0] = v.X;
102 2332 : f3[1] = v.Y;
103 2332 : f3[2] = v.Z;
104 2332 : }
105 :
106 300 : struct SoundBuffer
107 : {
108 : ALenum format;
109 : ALsizei freq;
110 : ALuint buffer_id;
111 : std::vector<char> buffer;
112 : };
113 :
114 172 : SoundBuffer* loadOggFile(const std::string &filepath)
115 : {
116 172 : int endian = 0; // 0 for Little-Endian, 1 for Big-Endian
117 : int bitStream;
118 : long bytes;
119 : char array[BUFFER_SIZE]; // Local fixed size array
120 : vorbis_info *pInfo;
121 : OggVorbis_File oggFile;
122 :
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 172 : snprintf(nonconst, 10000, "%s", filepath.c_str());
127 : // Try opening the given file
128 : //if(ov_fopen(filepath.c_str(), &oggFile) != 0)
129 172 : if(ov_fopen(nonconst, &oggFile) != 0)
130 : {
131 22 : infostream<<"Audio: Error opening "<<filepath<<" for decoding"<<std::endl;
132 22 : return NULL;
133 : }
134 :
135 150 : SoundBuffer *snd = new SoundBuffer;
136 :
137 : // Get some information about the OGG file
138 150 : pInfo = ov_info(&oggFile, -1);
139 :
140 : // Check the number of channels... always use 16-bit samples
141 150 : if(pInfo->channels == 1)
142 112 : snd->format = AL_FORMAT_MONO16;
143 : else
144 38 : snd->format = AL_FORMAT_STEREO16;
145 :
146 : // The frequency of the sampling rate
147 150 : snd->freq = pInfo->rate;
148 :
149 : // Keep reading until all is read
150 16905 : do
151 : {
152 : // Read up to a buffer's worth of decoded sound data
153 17055 : bytes = ov_read(&oggFile, array, BUFFER_SIZE, endian, 2, 1, &bitStream);
154 :
155 17055 : if(bytes < 0)
156 : {
157 0 : ov_clear(&oggFile);
158 0 : infostream<<"Audio: Error decoding "<<filepath<<std::endl;
159 0 : return NULL;
160 : }
161 :
162 : // Append to end of buffer
163 17055 : snd->buffer.insert(snd->buffer.end(), array, array + bytes);
164 17055 : } while (bytes > 0);
165 :
166 150 : alGenBuffers(1, &snd->buffer_id);
167 450 : alBufferData(snd->buffer_id, snd->format,
168 300 : &(snd->buffer[0]), snd->buffer.size(),
169 150 : snd->freq);
170 :
171 150 : ALenum error = alGetError();
172 :
173 150 : if(error != AL_NO_ERROR){
174 0 : infostream<<"Audio: OpenAL error: "<<alErrorString(error)
175 0 : <<"preparing sound buffer"<<std::endl;
176 : }
177 :
178 150 : infostream<<"Audio file "<<filepath<<" loaded"<<std::endl;
179 :
180 : // Clean up!
181 150 : ov_clear(&oggFile);
182 :
183 150 : return snd;
184 : }
185 :
186 : struct PlayingSound
187 : {
188 : ALuint source_id;
189 : bool loop;
190 : };
191 :
192 : class OpenALSoundManager: public ISoundManager
193 : {
194 : private:
195 : OnDemandSoundFetcher *m_fetcher;
196 : ALCdevice *m_device;
197 : ALCcontext *m_context;
198 : bool m_can_vorbis;
199 : int m_next_id;
200 : std::map<std::string, std::vector<SoundBuffer*> > m_buffers;
201 : std::map<int, PlayingSound*> m_sounds_playing;
202 : v3f m_listener_pos;
203 : public:
204 : bool m_is_initialized;
205 2 : OpenALSoundManager(OnDemandSoundFetcher *fetcher):
206 : m_fetcher(fetcher),
207 : m_device(NULL),
208 : m_context(NULL),
209 : m_can_vorbis(false),
210 : m_next_id(1),
211 2 : m_is_initialized(false)
212 : {
213 2 : ALCenum error = ALC_NO_ERROR;
214 :
215 2 : infostream<<"Audio: Initializing..."<<std::endl;
216 :
217 2 : m_device = alcOpenDevice(NULL);
218 2 : if(!m_device){
219 0 : infostream<<"Audio: No audio device available, audio system "
220 0 : <<"not initialized"<<std::endl;
221 0 : return;
222 : }
223 :
224 2 : if(alcIsExtensionPresent(m_device, "EXT_vorbis")){
225 0 : infostream<<"Audio: Vorbis extension present"<<std::endl;
226 0 : m_can_vorbis = true;
227 : } else{
228 2 : infostream<<"Audio: Vorbis extension NOT present"<<std::endl;
229 2 : m_can_vorbis = false;
230 : }
231 :
232 2 : m_context = alcCreateContext(m_device, NULL);
233 2 : if(!m_context){
234 0 : error = alcGetError(m_device);
235 0 : infostream<<"Audio: Unable to initialize audio context, "
236 0 : <<"aborting audio initialization ("<<alcErrorString(error)
237 0 : <<")"<<std::endl;
238 0 : alcCloseDevice(m_device);
239 0 : m_device = NULL;
240 0 : return;
241 : }
242 :
243 4 : if(!alcMakeContextCurrent(m_context) ||
244 2 : (error = alcGetError(m_device) != ALC_NO_ERROR))
245 : {
246 0 : infostream<<"Audio: Error setting audio context, aborting audio "
247 0 : <<"initialization ("<<alcErrorString(error)<<")"<<std::endl;
248 0 : alcDestroyContext(m_context);
249 0 : m_context = NULL;
250 0 : alcCloseDevice(m_device);
251 0 : m_device = NULL;
252 0 : return;
253 : }
254 :
255 2 : alDistanceModel(AL_EXPONENT_DISTANCE);
256 :
257 2 : infostream<<"Audio: Initialized: OpenAL "<<alGetString(AL_VERSION)
258 4 : <<", using "<<alcGetString(m_device, ALC_DEVICE_SPECIFIER)
259 2 : <<std::endl;
260 :
261 2 : m_is_initialized = true;
262 : }
263 :
264 4 : ~OpenALSoundManager()
265 4 : {
266 2 : infostream<<"Audio: Deinitializing..."<<std::endl;
267 : // KABOOM!
268 : // TODO: Clear SoundBuffers
269 2 : alcMakeContextCurrent(NULL);
270 2 : alcDestroyContext(m_context);
271 2 : m_context = NULL;
272 2 : alcCloseDevice(m_device);
273 2 : m_device = NULL;
274 :
275 396 : for (std::map<std::string, std::vector<SoundBuffer*> >::iterator i = m_buffers.begin();
276 264 : i != m_buffers.end(); i++) {
277 840 : for (std::vector<SoundBuffer*>::iterator iter = (*i).second.begin();
278 560 : iter != (*i).second.end(); iter++) {
279 150 : delete *iter;
280 : }
281 130 : (*i).second.clear();
282 : }
283 2 : m_buffers.clear();
284 2 : infostream<<"Audio: Deinitialized."<<std::endl;
285 4 : }
286 :
287 150 : void addBuffer(const std::string &name, SoundBuffer *buf)
288 : {
289 : std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
290 150 : m_buffers.find(name);
291 150 : if(i != m_buffers.end()){
292 20 : i->second.push_back(buf);
293 20 : return;
294 : }
295 260 : std::vector<SoundBuffer*> bufs;
296 130 : bufs.push_back(buf);
297 130 : m_buffers[name] = bufs;
298 130 : return;
299 : }
300 :
301 63 : SoundBuffer* getBuffer(const std::string &name)
302 : {
303 : std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
304 63 : m_buffers.find(name);
305 63 : if(i == m_buffers.end())
306 2 : return NULL;
307 61 : std::vector<SoundBuffer*> &bufs = i->second;
308 61 : int j = myrand() % bufs.size();
309 61 : return bufs[j];
310 : }
311 :
312 38 : PlayingSound* createPlayingSound(SoundBuffer *buf, bool loop,
313 : float volume)
314 : {
315 38 : infostream<<"OpenALSoundManager: Creating playing sound"<<std::endl;
316 : assert(buf);
317 38 : PlayingSound *sound = new PlayingSound;
318 : assert(sound);
319 38 : warn_if_error(alGetError(), "before createPlayingSound");
320 38 : alGenSources(1, &sound->source_id);
321 38 : alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
322 38 : alSourcei(sound->source_id, AL_SOURCE_RELATIVE, true);
323 38 : alSource3f(sound->source_id, AL_POSITION, 0, 0, 0);
324 38 : alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
325 38 : alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
326 38 : volume = MYMAX(0.0, volume);
327 38 : alSourcef(sound->source_id, AL_GAIN, volume);
328 38 : alSourcePlay(sound->source_id);
329 38 : warn_if_error(alGetError(), "createPlayingSound");
330 38 : return sound;
331 : }
332 :
333 23 : PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop,
334 : float volume, v3f pos)
335 : {
336 23 : infostream<<"OpenALSoundManager: Creating positional playing sound"
337 23 : <<std::endl;
338 : assert(buf);
339 23 : PlayingSound *sound = new PlayingSound;
340 : assert(sound);
341 23 : warn_if_error(alGetError(), "before createPlayingSoundAt");
342 23 : alGenSources(1, &sound->source_id);
343 23 : alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
344 23 : alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
345 23 : alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
346 23 : alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
347 : //alSourcef(sound->source_id, AL_ROLLOFF_FACTOR, 0.7);
348 23 : alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
349 23 : alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
350 23 : volume = MYMAX(0.0, volume);
351 23 : alSourcef(sound->source_id, AL_GAIN, volume);
352 23 : alSourcePlay(sound->source_id);
353 23 : warn_if_error(alGetError(), "createPlayingSoundAt");
354 23 : return sound;
355 : }
356 :
357 38 : int playSoundRaw(SoundBuffer *buf, bool loop, float volume)
358 : {
359 : assert(buf);
360 38 : PlayingSound *sound = createPlayingSound(buf, loop, volume);
361 38 : if(!sound)
362 0 : return -1;
363 38 : int id = m_next_id++;
364 38 : m_sounds_playing[id] = sound;
365 38 : return id;
366 : }
367 :
368 23 : int playSoundRawAt(SoundBuffer *buf, bool loop, float volume, v3f pos)
369 : {
370 : assert(buf);
371 23 : PlayingSound *sound = createPlayingSoundAt(buf, loop, volume, pos);
372 23 : if(!sound)
373 0 : return -1;
374 23 : int id = m_next_id++;
375 23 : m_sounds_playing[id] = sound;
376 23 : return id;
377 : }
378 :
379 57 : void deleteSound(int id)
380 : {
381 : std::map<int, PlayingSound*>::iterator i =
382 57 : m_sounds_playing.find(id);
383 57 : if(i == m_sounds_playing.end())
384 0 : return;
385 57 : PlayingSound *sound = i->second;
386 :
387 57 : alDeleteSources(1, &sound->source_id);
388 :
389 57 : delete sound;
390 57 : m_sounds_playing.erase(id);
391 : }
392 :
393 : /* If buffer does not exist, consult the fetcher */
394 62 : SoundBuffer* getFetchBuffer(const std::string &name)
395 : {
396 62 : SoundBuffer *buf = getBuffer(name);
397 62 : if(buf)
398 61 : return buf;
399 1 : if(!m_fetcher)
400 0 : return NULL;
401 2 : std::set<std::string> paths;
402 2 : std::set<std::string> datas;
403 1 : m_fetcher->fetchSounds(name, paths, datas);
404 69 : for(std::set<std::string>::iterator i = paths.begin();
405 46 : i != paths.end(); i++){
406 22 : loadSoundFile(name, *i);
407 : }
408 3 : for(std::set<std::string>::iterator i = datas.begin();
409 2 : i != datas.end(); i++){
410 0 : loadSoundData(name, *i);
411 : }
412 1 : return getBuffer(name);
413 : }
414 :
415 : // Remove stopped sounds
416 68 : void maintain()
417 : {
418 68 : verbosestream<<"OpenALSoundManager::maintain(): "
419 136 : <<m_sounds_playing.size()<<" playing sounds, "
420 136 : <<m_buffers.size()<<" sound names loaded"<<std::endl;
421 136 : std::set<int> del_list;
422 550 : for(std::map<int, PlayingSound*>::iterator
423 68 : i = m_sounds_playing.begin();
424 412 : i != m_sounds_playing.end(); i++)
425 : {
426 138 : int id = i->first;
427 138 : PlayingSound *sound = i->second;
428 : // If not playing, remove it
429 : {
430 : ALint state;
431 138 : alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state);
432 138 : if(state != AL_PLAYING){
433 57 : del_list.insert(id);
434 : }
435 : }
436 : }
437 68 : if(!del_list.empty())
438 43 : verbosestream<<"OpenALSoundManager::maintain(): deleting "
439 86 : <<del_list.size()<<" playing sounds"<<std::endl;
440 375 : for(std::set<int>::iterator i = del_list.begin();
441 250 : i != del_list.end(); i++)
442 : {
443 57 : deleteSound(*i);
444 : }
445 68 : }
446 :
447 : /* Interface */
448 :
449 172 : bool loadSoundFile(const std::string &name,
450 : const std::string &filepath)
451 : {
452 172 : SoundBuffer *buf = loadOggFile(filepath);
453 172 : if(buf)
454 150 : addBuffer(name, buf);
455 172 : return false;
456 : }
457 150 : bool loadSoundData(const std::string &name,
458 : const std::string &filedata)
459 : {
460 : // The vorbis API sucks; just write it to a file and use vorbisfile
461 : // TODO: Actually load it directly from memory
462 450 : std::string basepath = porting::path_user + DIR_DELIM + "cache" +
463 450 : DIR_DELIM + "tmp";
464 300 : std::string path = basepath + DIR_DELIM + "tmp.ogg";
465 150 : verbosestream<<"OpenALSoundManager::loadSoundData(): Writing "
466 150 : <<"temporary file to ["<<path<<"]"<<std::endl;
467 150 : fs::CreateAllDirs(basepath);
468 300 : std::ofstream of(path.c_str(), std::ios::binary);
469 150 : of.write(filedata.c_str(), filedata.size());
470 150 : of.close();
471 300 : return loadSoundFile(name, path);
472 : }
473 :
474 1166 : void updateListener(v3f pos, v3f vel, v3f at, v3f up)
475 : {
476 1166 : m_listener_pos = pos;
477 1166 : alListener3f(AL_POSITION, pos.X, pos.Y, pos.Z);
478 1166 : alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z);
479 : ALfloat f[6];
480 1166 : f3_set(f, at);
481 1166 : f3_set(f+3, -up);
482 1166 : alListenerfv(AL_ORIENTATION, f);
483 1166 : warn_if_error(alGetError(), "updateListener");
484 1166 : }
485 :
486 1166 : void setListenerGain(float gain)
487 : {
488 1166 : alListenerf(AL_GAIN, gain);
489 1166 : }
490 :
491 39 : int playSound(const std::string &name, bool loop, float volume)
492 : {
493 39 : maintain();
494 39 : if(name == "")
495 0 : return 0;
496 39 : SoundBuffer *buf = getFetchBuffer(name);
497 39 : if(!buf){
498 1 : infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
499 1 : <<std::endl;
500 1 : return -1;
501 : }
502 38 : return playSoundRaw(buf, loop, volume);
503 : }
504 27 : int playSoundAt(const std::string &name, bool loop, float volume, v3f pos)
505 : {
506 27 : maintain();
507 27 : if(name == "")
508 4 : return 0;
509 23 : SoundBuffer *buf = getFetchBuffer(name);
510 23 : if(!buf){
511 0 : infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
512 0 : <<std::endl;
513 0 : return -1;
514 : }
515 23 : return playSoundRawAt(buf, loop, volume, pos);
516 : }
517 0 : void stopSound(int sound)
518 : {
519 0 : maintain();
520 0 : deleteSound(sound);
521 0 : }
522 2 : bool soundExists(int sound)
523 : {
524 2 : maintain();
525 2 : return (m_sounds_playing.count(sound) != 0);
526 : }
527 59 : void updateSoundPosition(int id, v3f pos)
528 : {
529 : std::map<int, PlayingSound*>::iterator i =
530 59 : m_sounds_playing.find(id);
531 59 : if(i == m_sounds_playing.end())
532 26 : return;
533 33 : PlayingSound *sound = i->second;
534 :
535 33 : alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
536 33 : alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
537 33 : alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
538 33 : alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
539 : }
540 : };
541 :
542 2 : ISoundManager *createOpenALSoundManager(OnDemandSoundFetcher *fetcher)
543 : {
544 2 : OpenALSoundManager *m = new OpenALSoundManager(fetcher);
545 2 : if(m->m_is_initialized)
546 2 : return m;
547 0 : delete m;
548 0 : return NULL;
549 3 : };
550 :
|