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