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