]> git.lizzy.rs Git - minetest.git/blob - src/sound_openal.cpp
Fix crash on attaching player to entity
[minetest.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 "util/numeric.h" // myrand()
43 #include "porting.h"
44 #include <vector>
45 #include <fstream>
46 #include "util/cpp11_container.h"
47
48 #define BUFFER_SIZE 30000
49
50 static const char *alcErrorString(ALCenum err)
51 {
52         switch (err) {
53         case ALC_NO_ERROR:
54                 return "no error";
55         case ALC_INVALID_DEVICE:
56                 return "invalid device";
57         case ALC_INVALID_CONTEXT:
58                 return "invalid context";
59         case ALC_INVALID_ENUM:
60                 return "invalid enum";
61         case ALC_INVALID_VALUE:
62                 return "invalid value";
63         case ALC_OUT_OF_MEMORY:
64                 return "out of memory";
65         default:
66                 return "<unknown OpenAL error>";
67         }
68 }
69
70 static const char *alErrorString(ALenum err)
71 {
72         switch (err) {
73         case AL_NO_ERROR:
74                 return "no error";
75         case AL_INVALID_NAME:
76                 return "invalid name";
77         case AL_INVALID_ENUM:
78                 return "invalid enum";
79         case AL_INVALID_VALUE:
80                 return "invalid value";
81         case AL_INVALID_OPERATION:
82                 return "invalid operation";
83         case AL_OUT_OF_MEMORY:
84                 return "out of memory";
85         default:
86                 return "<unknown OpenAL error>";
87         }
88 }
89
90 static ALenum warn_if_error(ALenum err, const char *desc)
91 {
92         if(err == AL_NO_ERROR)
93                 return err;
94         warningstream<<desc<<": "<<alErrorString(err)<<std::endl;
95         return err;
96 }
97
98 void f3_set(ALfloat *f3, v3f v)
99 {
100         f3[0] = v.X;
101         f3[1] = v.Y;
102         f3[2] = v.Z;
103 }
104
105 struct SoundBuffer
106 {
107         ALenum format;
108         ALsizei freq;
109         ALuint buffer_id;
110         std::vector<char> buffer;
111 };
112
113 SoundBuffer *load_opened_ogg_file(OggVorbis_File *oggFile,
114                 const std::string &filename_for_logging)
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
122         SoundBuffer *snd = new SoundBuffer;
123
124         // Get some information about the OGG file
125         pInfo = ov_info(oggFile, -1);
126
127         // Check the number of channels... always use 16-bit samples
128         if(pInfo->channels == 1)
129                 snd->format = AL_FORMAT_MONO16;
130         else
131                 snd->format = AL_FORMAT_STEREO16;
132
133         // The frequency of the sampling rate
134         snd->freq = pInfo->rate;
135
136         // Keep reading until all is read
137         do
138         {
139                 // Read up to a buffer's worth of decoded sound data
140                 bytes = ov_read(oggFile, array, BUFFER_SIZE, endian, 2, 1, &bitStream);
141
142                 if(bytes < 0)
143                 {
144                         ov_clear(oggFile);
145                         infostream << "Audio: Error decoding "
146                                 << filename_for_logging << std::endl;
147                         delete snd;
148                         return NULL;
149                 }
150
151                 // Append to end of buffer
152                 snd->buffer.insert(snd->buffer.end(), array, array + bytes);
153         } while (bytes > 0);
154
155         alGenBuffers(1, &snd->buffer_id);
156         alBufferData(snd->buffer_id, snd->format,
157                         &(snd->buffer[0]), snd->buffer.size(),
158                         snd->freq);
159
160         ALenum error = alGetError();
161
162         if(error != AL_NO_ERROR){
163                 infostream<<"Audio: OpenAL error: "<<alErrorString(error)
164                                 <<"preparing sound buffer"<<std::endl;
165         }
166
167         infostream << "Audio file "
168                 << filename_for_logging << " loaded" << std::endl;
169
170         // Clean up!
171         ov_clear(oggFile);
172
173         return snd;
174 }
175
176 SoundBuffer *load_ogg_from_file(const std::string &path)
177 {
178         OggVorbis_File oggFile;
179
180         // Try opening the given file.
181         // This requires libvorbis >= 1.3.2, as
182         // previous versions expect a non-const char *
183         if (ov_fopen(path.c_str(), &oggFile) != 0) {
184                 infostream << "Audio: Error opening " << path
185                         << " for decoding" << std::endl;
186                 return NULL;
187         }
188
189         return load_opened_ogg_file(&oggFile, path);
190 }
191
192 struct BufferSource {
193         const char *buf;
194         size_t cur_offset;
195         size_t len;
196 };
197
198 size_t buffer_sound_read_func(void *ptr, size_t size, size_t nmemb, void *datasource)
199 {
200         BufferSource *s = (BufferSource *)datasource;
201         size_t copied_size = MYMIN(s->len - s->cur_offset, size);
202         memcpy(ptr, s->buf + s->cur_offset, copied_size);
203         s->cur_offset += copied_size;
204         return copied_size;
205 }
206
207 int buffer_sound_seek_func(void *datasource, ogg_int64_t offset, int whence)
208 {
209         BufferSource *s = (BufferSource *)datasource;
210         if (whence == SEEK_SET) {
211                 if (offset < 0 || (size_t)MYMAX(offset, 0) >= s->len) {
212                         // offset out of bounds
213                         return -1;
214                 }
215                 s->cur_offset = offset;
216                 return 0;
217         } else if (whence == SEEK_CUR) {
218                 if ((size_t)MYMIN(-offset, 0) > s->cur_offset
219                                 || s->cur_offset + offset > s->len) {
220                         // offset out of bounds
221                         return -1;
222                 }
223                 s->cur_offset += offset;
224                 return 0;
225         }
226         // invalid whence param (SEEK_END doesn't have to be supported)
227         return -1;
228 }
229
230 long BufferSourceell_func(void *datasource)
231 {
232         BufferSource *s = (BufferSource *)datasource;
233         return s->cur_offset;
234 }
235
236 static ov_callbacks g_buffer_ov_callbacks = {
237         &buffer_sound_read_func,
238         &buffer_sound_seek_func,
239         NULL,
240         &BufferSourceell_func
241 };
242
243 SoundBuffer *load_ogg_from_buffer(const std::string &buf, const std::string &id_for_log)
244 {
245         OggVorbis_File oggFile;
246
247         BufferSource s;
248         s.buf = buf.c_str();
249         s.cur_offset = 0;
250         s.len = buf.size();
251
252         if (ov_open_callbacks(&s, &oggFile, NULL, 0, g_buffer_ov_callbacks) != 0) {
253                 infostream << "Audio: Error opening " << id_for_log
254                         << " for decoding" << std::endl;
255                 return NULL;
256         }
257
258         return load_opened_ogg_file(&oggFile, id_for_log);
259 }
260
261 struct PlayingSound
262 {
263         ALuint source_id;
264         bool loop;
265 };
266
267 class OpenALSoundManager: public ISoundManager
268 {
269 private:
270         OnDemandSoundFetcher *m_fetcher;
271         ALCdevice *m_device;
272         ALCcontext *m_context;
273         int m_next_id;
274         UNORDERED_MAP<std::string, std::vector<SoundBuffer*> > m_buffers;
275         UNORDERED_MAP<int, PlayingSound*> m_sounds_playing;
276         v3f m_listener_pos;
277 public:
278         bool m_is_initialized;
279         OpenALSoundManager(OnDemandSoundFetcher *fetcher):
280                 m_fetcher(fetcher),
281                 m_device(NULL),
282                 m_context(NULL),
283                 m_next_id(1),
284                 m_is_initialized(false)
285         {
286                 ALCenum error = ALC_NO_ERROR;
287
288                 infostream<<"Audio: Initializing..."<<std::endl;
289
290                 m_device = alcOpenDevice(NULL);
291                 if(!m_device){
292                         infostream<<"Audio: No audio device available, audio system "
293                                 <<"not initialized"<<std::endl;
294                         return;
295                 }
296
297                 m_context = alcCreateContext(m_device, NULL);
298                 if(!m_context){
299                         error = alcGetError(m_device);
300                         infostream<<"Audio: Unable to initialize audio context, "
301                                         <<"aborting audio initialization ("<<alcErrorString(error)
302                                         <<")"<<std::endl;
303                         alcCloseDevice(m_device);
304                         m_device = NULL;
305                         return;
306                 }
307
308                 if(!alcMakeContextCurrent(m_context) ||
309                                 (error = alcGetError(m_device) != ALC_NO_ERROR))
310                 {
311                         infostream<<"Audio: Error setting audio context, aborting audio "
312                                         <<"initialization ("<<alcErrorString(error)<<")"<<std::endl;
313                         alcDestroyContext(m_context);
314                         m_context = NULL;
315                         alcCloseDevice(m_device);
316                         m_device = NULL;
317                         return;
318                 }
319
320                 alDistanceModel(AL_EXPONENT_DISTANCE);
321
322                 infostream<<"Audio: Initialized: OpenAL "<<alGetString(AL_VERSION)
323                                 <<", using "<<alcGetString(m_device, ALC_DEVICE_SPECIFIER)
324                                 <<std::endl;
325
326                 m_is_initialized = true;
327         }
328
329         ~OpenALSoundManager()
330         {
331                 infostream<<"Audio: Deinitializing..."<<std::endl;
332                 // KABOOM!
333                 // TODO: Clear SoundBuffers
334                 alcMakeContextCurrent(NULL);
335                 alcDestroyContext(m_context);
336                 m_context = NULL;
337                 alcCloseDevice(m_device);
338                 m_device = NULL;
339
340                 for (UNORDERED_MAP<std::string, std::vector<SoundBuffer*> >::iterator i = m_buffers.begin();
341                                 i != m_buffers.end(); ++i) {
342                         for (std::vector<SoundBuffer*>::iterator iter = (*i).second.begin();
343                                         iter != (*i).second.end(); ++iter) {
344                                 delete *iter;
345                         }
346                         (*i).second.clear();
347                 }
348                 m_buffers.clear();
349                 infostream<<"Audio: Deinitialized."<<std::endl;
350         }
351
352         void addBuffer(const std::string &name, SoundBuffer *buf)
353         {
354                 UNORDERED_MAP<std::string, std::vector<SoundBuffer*> >::iterator i =
355                                 m_buffers.find(name);
356                 if(i != m_buffers.end()){
357                         i->second.push_back(buf);
358                         return;
359                 }
360                 std::vector<SoundBuffer*> bufs;
361                 bufs.push_back(buf);
362                 m_buffers[name] = bufs;
363                 return;
364         }
365
366         SoundBuffer* getBuffer(const std::string &name)
367         {
368                 UNORDERED_MAP<std::string, std::vector<SoundBuffer*> >::iterator i =
369                                 m_buffers.find(name);
370                 if(i == m_buffers.end())
371                         return NULL;
372                 std::vector<SoundBuffer*> &bufs = i->second;
373                 int j = myrand() % bufs.size();
374                 return bufs[j];
375         }
376
377         PlayingSound* createPlayingSound(SoundBuffer *buf, bool loop,
378                         float volume)
379         {
380                 infostream<<"OpenALSoundManager: Creating playing sound"<<std::endl;
381                 assert(buf);
382                 PlayingSound *sound = new PlayingSound;
383                 assert(sound);
384                 warn_if_error(alGetError(), "before createPlayingSound");
385                 alGenSources(1, &sound->source_id);
386                 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
387                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, true);
388                 alSource3f(sound->source_id, AL_POSITION, 0, 0, 0);
389                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
390                 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
391                 volume = MYMAX(0.0, volume);
392                 alSourcef(sound->source_id, AL_GAIN, volume);
393                 alSourcePlay(sound->source_id);
394                 warn_if_error(alGetError(), "createPlayingSound");
395                 return sound;
396         }
397
398         PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop,
399                         float volume, v3f pos)
400         {
401                 infostream<<"OpenALSoundManager: Creating positional playing sound"
402                                 <<std::endl;
403                 assert(buf);
404                 PlayingSound *sound = new PlayingSound;
405                 assert(sound);
406                 warn_if_error(alGetError(), "before createPlayingSoundAt");
407                 alGenSources(1, &sound->source_id);
408                 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
409                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
410                 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
411                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
412                 //alSourcef(sound->source_id, AL_ROLLOFF_FACTOR, 0.7);
413                 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
414                 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
415                 volume = MYMAX(0.0, volume);
416                 alSourcef(sound->source_id, AL_GAIN, volume);
417                 alSourcePlay(sound->source_id);
418                 warn_if_error(alGetError(), "createPlayingSoundAt");
419                 return sound;
420         }
421
422         int playSoundRaw(SoundBuffer *buf, bool loop, float volume)
423         {
424                 assert(buf);
425                 PlayingSound *sound = createPlayingSound(buf, loop, volume);
426                 if(!sound)
427                         return -1;
428                 int id = m_next_id++;
429                 m_sounds_playing[id] = sound;
430                 return id;
431         }
432
433         int playSoundRawAt(SoundBuffer *buf, bool loop, float volume, v3f pos)
434         {
435                 assert(buf);
436                 PlayingSound *sound = createPlayingSoundAt(buf, loop, volume, pos);
437                 if(!sound)
438                         return -1;
439                 int id = m_next_id++;
440                 m_sounds_playing[id] = sound;
441                 return id;
442         }
443
444         void deleteSound(int id)
445         {
446                 UNORDERED_MAP<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
447                 if(i == m_sounds_playing.end())
448                         return;
449                 PlayingSound *sound = i->second;
450
451                 alDeleteSources(1, &sound->source_id);
452
453                 delete sound;
454                 m_sounds_playing.erase(id);
455         }
456
457         /* If buffer does not exist, consult the fetcher */
458         SoundBuffer* getFetchBuffer(const std::string &name)
459         {
460                 SoundBuffer *buf = getBuffer(name);
461                 if(buf)
462                         return buf;
463                 if(!m_fetcher)
464                         return NULL;
465                 std::set<std::string> paths;
466                 std::set<std::string> datas;
467                 m_fetcher->fetchSounds(name, paths, datas);
468                 for(std::set<std::string>::iterator i = paths.begin();
469                                 i != paths.end(); ++i){
470                         loadSoundFile(name, *i);
471                 }
472                 for(std::set<std::string>::iterator i = datas.begin();
473                                 i != datas.end(); ++i){
474                         loadSoundData(name, *i);
475                 }
476                 return getBuffer(name);
477         }
478
479         // Remove stopped sounds
480         void maintain()
481         {
482                 verbosestream<<"OpenALSoundManager::maintain(): "
483                                 <<m_sounds_playing.size()<<" playing sounds, "
484                                 <<m_buffers.size()<<" sound names loaded"<<std::endl;
485                 std::set<int> del_list;
486                 for(UNORDERED_MAP<int, PlayingSound*>::iterator i = m_sounds_playing.begin();
487                                 i != m_sounds_playing.end(); ++i) {
488                         int id = i->first;
489                         PlayingSound *sound = i->second;
490                         // If not playing, remove it
491                         {
492                                 ALint state;
493                                 alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state);
494                                 if(state != AL_PLAYING){
495                                         del_list.insert(id);
496                                 }
497                         }
498                 }
499                 if(!del_list.empty())
500                         verbosestream<<"OpenALSoundManager::maintain(): deleting "
501                                         <<del_list.size()<<" playing sounds"<<std::endl;
502                 for(std::set<int>::iterator i = del_list.begin();
503                                 i != del_list.end(); ++i)
504                 {
505                         deleteSound(*i);
506                 }
507         }
508
509         /* Interface */
510
511         bool loadSoundFile(const std::string &name,
512                         const std::string &filepath)
513         {
514                 SoundBuffer *buf = load_ogg_from_file(filepath);
515                 if (buf)
516                         addBuffer(name, buf);
517                 return false;
518         }
519         bool loadSoundData(const std::string &name,
520                         const std::string &filedata)
521         {
522                 SoundBuffer *buf = load_ogg_from_buffer(filedata, name);
523                 if (buf)
524                         addBuffer(name, buf);
525                 return false;
526         }
527
528         void updateListener(v3f pos, v3f vel, v3f at, v3f up)
529         {
530                 m_listener_pos = pos;
531                 alListener3f(AL_POSITION, pos.X, pos.Y, pos.Z);
532                 alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z);
533                 ALfloat f[6];
534                 f3_set(f, at);
535                 f3_set(f+3, -up);
536                 alListenerfv(AL_ORIENTATION, f);
537                 warn_if_error(alGetError(), "updateListener");
538         }
539
540         void setListenerGain(float gain)
541         {
542                 alListenerf(AL_GAIN, gain);
543         }
544
545         int playSound(const std::string &name, bool loop, float volume)
546         {
547                 maintain();
548                 if(name == "")
549                         return 0;
550                 SoundBuffer *buf = getFetchBuffer(name);
551                 if(!buf){
552                         infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
553                                         <<std::endl;
554                         return -1;
555                 }
556                 return playSoundRaw(buf, loop, volume);
557         }
558         int playSoundAt(const std::string &name, bool loop, float volume, v3f pos)
559         {
560                 maintain();
561                 if(name == "")
562                         return 0;
563                 SoundBuffer *buf = getFetchBuffer(name);
564                 if(!buf){
565                         infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
566                                         <<std::endl;
567                         return -1;
568                 }
569                 return playSoundRawAt(buf, loop, volume, pos);
570         }
571         void stopSound(int sound)
572         {
573                 maintain();
574                 deleteSound(sound);
575         }
576         bool soundExists(int sound)
577         {
578                 maintain();
579                 return (m_sounds_playing.count(sound) != 0);
580         }
581         void updateSoundPosition(int id, v3f pos)
582         {
583                 UNORDERED_MAP<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
584                 if (i == m_sounds_playing.end())
585                         return;
586                 PlayingSound *sound = i->second;
587
588                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
589                 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
590                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
591                 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
592         }
593 };
594
595 ISoundManager *createOpenALSoundManager(OnDemandSoundFetcher *fetcher)
596 {
597         OpenALSoundManager *m = new OpenALSoundManager(fetcher);
598         if(m->m_is_initialized)
599                 return m;
600         delete m;
601         return NULL;
602 };
603