]> git.lizzy.rs Git - dragonfireclient.git/blob - src/sound_openal.cpp
Fixed u64 ambiguous symbol error
[dragonfireclient.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 "filesys.h"
42 #include "util/numeric.h" // myrand()
43 #include "debug.h" // assert()
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                 infostream<<"Audio: Deinitialized."<<std::endl;
275         }
276         
277         void addBuffer(const std::string &name, SoundBuffer *buf)
278         {
279                 std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
280                                 m_buffers.find(name);
281                 if(i != m_buffers.end()){
282                         i->second.push_back(buf);
283                         return;
284                 }
285                 std::vector<SoundBuffer*> bufs;
286                 bufs.push_back(buf);
287                 m_buffers[name] = bufs;
288                 return;
289         }
290
291         SoundBuffer* getBuffer(const std::string &name)
292         {
293                 std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
294                                 m_buffers.find(name);
295                 if(i == m_buffers.end())
296                         return NULL;
297                 std::vector<SoundBuffer*> &bufs = i->second;
298                 int j = myrand() % bufs.size();
299                 return bufs[j];
300         }
301
302         PlayingSound* createPlayingSound(SoundBuffer *buf, bool loop,
303                         float volume)
304         {
305                 infostream<<"OpenALSoundManager: Creating playing sound"<<std::endl;
306                 assert(buf);
307                 PlayingSound *sound = new PlayingSound;
308                 assert(sound);
309                 warn_if_error(alGetError(), "before createPlayingSound");
310                 alGenSources(1, &sound->source_id);
311                 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
312                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, true);
313                 alSource3f(sound->source_id, AL_POSITION, 0, 0, 0);
314                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
315                 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
316                 volume = MYMAX(0.0, volume);
317                 alSourcef(sound->source_id, AL_GAIN, volume);
318                 alSourcePlay(sound->source_id);
319                 warn_if_error(alGetError(), "createPlayingSound");
320                 return sound;
321         }
322
323         PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop,
324                         float volume, v3f pos)
325         {
326                 infostream<<"OpenALSoundManager: Creating positional playing sound"
327                                 <<std::endl;
328                 assert(buf);
329                 PlayingSound *sound = new PlayingSound;
330                 assert(sound);
331                 warn_if_error(alGetError(), "before createPlayingSoundAt");
332                 alGenSources(1, &sound->source_id);
333                 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
334                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
335                 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
336                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
337                 //alSourcef(sound->source_id, AL_ROLLOFF_FACTOR, 0.7);
338                 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
339                 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
340                 volume = MYMAX(0.0, volume);
341                 alSourcef(sound->source_id, AL_GAIN, volume);
342                 alSourcePlay(sound->source_id);
343                 warn_if_error(alGetError(), "createPlayingSoundAt");
344                 return sound;
345         }
346
347         int playSoundRaw(SoundBuffer *buf, bool loop, float volume)
348         {
349                 assert(buf);
350                 PlayingSound *sound = createPlayingSound(buf, loop, volume);
351                 if(!sound)
352                         return -1;
353                 int id = m_next_id++;
354                 m_sounds_playing[id] = sound;
355                 return id;
356         }
357
358         int playSoundRawAt(SoundBuffer *buf, bool loop, float volume, v3f pos)
359         {
360                 assert(buf);
361                 PlayingSound *sound = createPlayingSoundAt(buf, loop, volume, pos);
362                 if(!sound)
363                         return -1;
364                 int id = m_next_id++;
365                 m_sounds_playing[id] = sound;
366                 return id;
367         }
368         
369         void deleteSound(int id)
370         {
371                 std::map<int, PlayingSound*>::iterator i =
372                                 m_sounds_playing.find(id);
373                 if(i == m_sounds_playing.end())
374                         return;
375                 PlayingSound *sound = i->second;
376                 
377                 alDeleteSources(1, &sound->source_id);
378
379                 delete sound;
380                 m_sounds_playing.erase(id);
381         }
382
383         /* If buffer does not exist, consult the fetcher */
384         SoundBuffer* getFetchBuffer(const std::string name)
385         {
386                 SoundBuffer *buf = getBuffer(name);
387                 if(buf)
388                         return buf;
389                 if(!m_fetcher)
390                         return NULL;
391                 std::set<std::string> paths;
392                 std::set<std::string> datas;
393                 m_fetcher->fetchSounds(name, paths, datas);
394                 for(std::set<std::string>::iterator i = paths.begin();
395                                 i != paths.end(); i++){
396                         loadSoundFile(name, *i);
397                 }
398                 for(std::set<std::string>::iterator i = datas.begin();
399                                 i != datas.end(); i++){
400                         loadSoundData(name, *i);
401                 }
402                 return getBuffer(name);
403         }
404         
405         // Remove stopped sounds
406         void maintain()
407         {
408                 verbosestream<<"OpenALSoundManager::maintain(): "
409                                 <<m_sounds_playing.size()<<" playing sounds, "
410                                 <<m_buffers.size()<<" sound names loaded"<<std::endl;
411                 std::set<int> del_list;
412                 for(std::map<int, PlayingSound*>::iterator
413                                 i = m_sounds_playing.begin();
414                                 i != m_sounds_playing.end(); i++)
415                 {
416                         int id = i->first;
417                         PlayingSound *sound = i->second;
418                         // If not playing, remove it
419                         {
420                                 ALint state;
421                                 alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state);
422                                 if(state != AL_PLAYING){
423                                         del_list.insert(id);
424                                 }
425                         }
426                 }
427                 if(del_list.size() != 0)
428                         verbosestream<<"OpenALSoundManager::maintain(): deleting "
429                                         <<del_list.size()<<" playing sounds"<<std::endl;
430                 for(std::set<int>::iterator i = del_list.begin();
431                                 i != del_list.end(); i++)
432                 {
433                         deleteSound(*i);
434                 }
435         }
436
437         /* Interface */
438
439         bool loadSoundFile(const std::string &name,
440                         const std::string &filepath)
441         {
442                 SoundBuffer *buf = loadOggFile(filepath);
443                 if(buf)
444                         addBuffer(name, buf);
445                 return false;
446         }
447         bool loadSoundData(const std::string &name,
448                         const std::string &filedata)
449         {
450                 // The vorbis API sucks; just write it to a file and use vorbisfile
451                 // TODO: Actually load it directly from memory
452                 std::string basepath = porting::path_user + DIR_DELIM + "cache" +
453                                 DIR_DELIM + "tmp";
454                 std::string path = basepath + DIR_DELIM + "tmp.ogg";
455                 verbosestream<<"OpenALSoundManager::loadSoundData(): Writing "
456                                 <<"temporary file to ["<<path<<"]"<<std::endl;
457                 fs::CreateAllDirs(basepath);
458                 std::ofstream of(path.c_str(), std::ios::binary);
459                 of.write(filedata.c_str(), filedata.size());
460                 of.close();
461                 return loadSoundFile(name, path);
462         }
463
464         void updateListener(v3f pos, v3f vel, v3f at, v3f up)
465         {
466                 m_listener_pos = pos;
467                 alListener3f(AL_POSITION, pos.X, pos.Y, pos.Z);
468                 alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z);
469                 ALfloat f[6];
470                 f3_set(f, at);
471                 f3_set(f+3, -up);
472                 alListenerfv(AL_ORIENTATION, f);
473                 warn_if_error(alGetError(), "updateListener");
474         }
475         
476         void setListenerGain(float gain)
477         {
478                 alListenerf(AL_GAIN, gain);
479         }
480
481         int playSound(const std::string &name, bool loop, float volume)
482         {
483                 maintain();
484                 if(name == "")
485                         return 0;
486                 SoundBuffer *buf = getFetchBuffer(name);
487                 if(!buf){
488                         infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
489                                         <<std::endl;
490                         return -1;
491                 }
492                 return playSoundRaw(buf, loop, volume);
493         }
494         int playSoundAt(const std::string &name, bool loop, float volume, v3f pos)
495         {
496                 maintain();
497                 if(name == "")
498                         return 0;
499                 SoundBuffer *buf = getFetchBuffer(name);
500                 if(!buf){
501                         infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
502                                         <<std::endl;
503                         return -1;
504                 }
505                 return playSoundRawAt(buf, loop, volume, pos);
506         }
507         void stopSound(int sound)
508         {
509                 maintain();
510                 deleteSound(sound);
511         }
512         bool soundExists(int sound)
513         {
514                 maintain();
515                 return (m_sounds_playing.count(sound) != 0);
516         }
517         void updateSoundPosition(int id, v3f pos)
518         {
519                 std::map<int, PlayingSound*>::iterator i =
520                                 m_sounds_playing.find(id);
521                 if(i == m_sounds_playing.end())
522                         return;
523                 PlayingSound *sound = i->second;
524
525                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
526                 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
527                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
528                 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
529         }
530 };
531
532 ISoundManager *createOpenALSoundManager(OnDemandSoundFetcher *fetcher)
533 {
534         OpenALSoundManager *m = new OpenALSoundManager(fetcher);
535         if(m->m_is_initialized)
536                 return m;
537         delete m;
538         return NULL;
539 };
540