]> git.lizzy.rs Git - minetest.git/blob - src/sound_openal.cpp
Add minetest.register_lbm() to run code on block load only
[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 <map>
45 #include <vector>
46 #include <fstream>
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                         return NULL;
148                 }
149
150                 // Append to end of buffer
151                 snd->buffer.insert(snd->buffer.end(), array, array + bytes);
152         } while (bytes > 0);
153
154         alGenBuffers(1, &snd->buffer_id);
155         alBufferData(snd->buffer_id, snd->format,
156                         &(snd->buffer[0]), snd->buffer.size(),
157                         snd->freq);
158
159         ALenum error = alGetError();
160
161         if(error != AL_NO_ERROR){
162                 infostream<<"Audio: OpenAL error: "<<alErrorString(error)
163                                 <<"preparing sound buffer"<<std::endl;
164         }
165
166         infostream << "Audio file "
167                 << filename_for_logging << " loaded" << std::endl;
168
169         // Clean up!
170         ov_clear(oggFile);
171
172         return snd;
173 }
174
175 SoundBuffer *load_ogg_from_file(const std::string &path)
176 {
177         OggVorbis_File oggFile;
178
179         // Try opening the given file.
180         // This requires libvorbis >= 1.3.2, as
181         // previous versions expect a non-const char *
182         if (ov_fopen(path.c_str(), &oggFile) != 0) {
183                 infostream << "Audio: Error opening " << path
184                         << " for decoding" << std::endl;
185                 return NULL;
186         }
187
188         return load_opened_ogg_file(&oggFile, path);
189 }
190
191 struct BufferSource {
192         const char *buf;
193         size_t cur_offset;
194         size_t len;
195 };
196
197 size_t buffer_sound_read_func(void *ptr, size_t size, size_t nmemb, void *datasource)
198 {
199         BufferSource *s = (BufferSource *)datasource;
200         size_t copied_size = MYMIN(s->len - s->cur_offset, size);
201         memcpy(ptr, s->buf + s->cur_offset, copied_size);
202         s->cur_offset += copied_size;
203         return copied_size;
204 }
205
206 int buffer_sound_seek_func(void *datasource, ogg_int64_t offset, int whence)
207 {
208         BufferSource *s = (BufferSource *)datasource;
209         if (whence == SEEK_SET) {
210                 if (offset < 0 || (size_t)MYMAX(offset, 0) >= s->len) {
211                         // offset out of bounds
212                         return -1;
213                 }
214                 s->cur_offset = offset;
215                 return 0;
216         } else if (whence == SEEK_CUR) {
217                 if ((size_t)MYMIN(-offset, 0) > s->cur_offset
218                                 || s->cur_offset + offset > s->len) {
219                         // offset out of bounds
220                         return -1;
221                 }
222                 s->cur_offset += offset;
223                 return 0;
224         }
225         // invalid whence param (SEEK_END doesn't have to be supported)
226         return -1;
227 }
228
229 long BufferSourceell_func(void *datasource)
230 {
231         BufferSource *s = (BufferSource *)datasource;
232         return s->cur_offset;
233 }
234
235 static ov_callbacks g_buffer_ov_callbacks = {
236         &buffer_sound_read_func,
237         &buffer_sound_seek_func,
238         NULL,
239         &BufferSourceell_func
240 };
241
242 SoundBuffer *load_ogg_from_buffer(const std::string &buf, const std::string &id_for_log)
243 {
244         OggVorbis_File oggFile;
245
246         BufferSource s;
247         s.buf = buf.c_str();
248         s.cur_offset = 0;
249         s.len = buf.size();
250
251         if (ov_open_callbacks(&s, &oggFile, NULL, 0, g_buffer_ov_callbacks) != 0) {
252                 infostream << "Audio: Error opening " << id_for_log
253                         << " for decoding" << std::endl;
254                 return NULL;
255         }
256
257         return load_opened_ogg_file(&oggFile, id_for_log);
258 }
259
260 struct PlayingSound
261 {
262         ALuint source_id;
263         bool loop;
264 };
265
266 class OpenALSoundManager: public ISoundManager
267 {
268 private:
269         OnDemandSoundFetcher *m_fetcher;
270         ALCdevice *m_device;
271         ALCcontext *m_context;
272         int m_next_id;
273         std::map<std::string, std::vector<SoundBuffer*> > m_buffers;
274         std::map<int, PlayingSound*> m_sounds_playing;
275         v3f m_listener_pos;
276 public:
277         bool m_is_initialized;
278         OpenALSoundManager(OnDemandSoundFetcher *fetcher):
279                 m_fetcher(fetcher),
280                 m_device(NULL),
281                 m_context(NULL),
282                 m_next_id(1),
283                 m_is_initialized(false)
284         {
285                 ALCenum error = ALC_NO_ERROR;
286
287                 infostream<<"Audio: Initializing..."<<std::endl;
288
289                 m_device = alcOpenDevice(NULL);
290                 if(!m_device){
291                         infostream<<"Audio: No audio device available, audio system "
292                                 <<"not initialized"<<std::endl;
293                         return;
294                 }
295
296                 m_context = alcCreateContext(m_device, NULL);
297                 if(!m_context){
298                         error = alcGetError(m_device);
299                         infostream<<"Audio: Unable to initialize audio context, "
300                                         <<"aborting audio initialization ("<<alcErrorString(error)
301                                         <<")"<<std::endl;
302                         alcCloseDevice(m_device);
303                         m_device = NULL;
304                         return;
305                 }
306
307                 if(!alcMakeContextCurrent(m_context) ||
308                                 (error = alcGetError(m_device) != ALC_NO_ERROR))
309                 {
310                         infostream<<"Audio: Error setting audio context, aborting audio "
311                                         <<"initialization ("<<alcErrorString(error)<<")"<<std::endl;
312                         alcDestroyContext(m_context);
313                         m_context = NULL;
314                         alcCloseDevice(m_device);
315                         m_device = NULL;
316                         return;
317                 }
318
319                 alDistanceModel(AL_EXPONENT_DISTANCE);
320
321                 infostream<<"Audio: Initialized: OpenAL "<<alGetString(AL_VERSION)
322                                 <<", using "<<alcGetString(m_device, ALC_DEVICE_SPECIFIER)
323                                 <<std::endl;
324
325                 m_is_initialized = true;
326         }
327
328         ~OpenALSoundManager()
329         {
330                 infostream<<"Audio: Deinitializing..."<<std::endl;
331                 // KABOOM!
332                 // TODO: Clear SoundBuffers
333                 alcMakeContextCurrent(NULL);
334                 alcDestroyContext(m_context);
335                 m_context = NULL;
336                 alcCloseDevice(m_device);
337                 m_device = NULL;
338
339                 for (std::map<std::string, std::vector<SoundBuffer*> >::iterator i = m_buffers.begin();
340                                 i != m_buffers.end(); ++i) {
341                         for (std::vector<SoundBuffer*>::iterator iter = (*i).second.begin();
342                                         iter != (*i).second.end(); ++iter) {
343                                 delete *iter;
344                         }
345                         (*i).second.clear();
346                 }
347                 m_buffers.clear();
348                 infostream<<"Audio: Deinitialized."<<std::endl;
349         }
350
351         void addBuffer(const std::string &name, SoundBuffer *buf)
352         {
353                 std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
354                                 m_buffers.find(name);
355                 if(i != m_buffers.end()){
356                         i->second.push_back(buf);
357                         return;
358                 }
359                 std::vector<SoundBuffer*> bufs;
360                 bufs.push_back(buf);
361                 m_buffers[name] = bufs;
362                 return;
363         }
364
365         SoundBuffer* getBuffer(const std::string &name)
366         {
367                 std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
368                                 m_buffers.find(name);
369                 if(i == m_buffers.end())
370                         return NULL;
371                 std::vector<SoundBuffer*> &bufs = i->second;
372                 int j = myrand() % bufs.size();
373                 return bufs[j];
374         }
375
376         PlayingSound* createPlayingSound(SoundBuffer *buf, bool loop,
377                         float volume)
378         {
379                 infostream<<"OpenALSoundManager: Creating playing sound"<<std::endl;
380                 assert(buf);
381                 PlayingSound *sound = new PlayingSound;
382                 assert(sound);
383                 warn_if_error(alGetError(), "before createPlayingSound");
384                 alGenSources(1, &sound->source_id);
385                 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
386                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, true);
387                 alSource3f(sound->source_id, AL_POSITION, 0, 0, 0);
388                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
389                 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
390                 volume = MYMAX(0.0, volume);
391                 alSourcef(sound->source_id, AL_GAIN, volume);
392                 alSourcePlay(sound->source_id);
393                 warn_if_error(alGetError(), "createPlayingSound");
394                 return sound;
395         }
396
397         PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop,
398                         float volume, v3f pos)
399         {
400                 infostream<<"OpenALSoundManager: Creating positional playing sound"
401                                 <<std::endl;
402                 assert(buf);
403                 PlayingSound *sound = new PlayingSound;
404                 assert(sound);
405                 warn_if_error(alGetError(), "before createPlayingSoundAt");
406                 alGenSources(1, &sound->source_id);
407                 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
408                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
409                 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
410                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
411                 //alSourcef(sound->source_id, AL_ROLLOFF_FACTOR, 0.7);
412                 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
413                 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
414                 volume = MYMAX(0.0, volume);
415                 alSourcef(sound->source_id, AL_GAIN, volume);
416                 alSourcePlay(sound->source_id);
417                 warn_if_error(alGetError(), "createPlayingSoundAt");
418                 return sound;
419         }
420
421         int playSoundRaw(SoundBuffer *buf, bool loop, float volume)
422         {
423                 assert(buf);
424                 PlayingSound *sound = createPlayingSound(buf, loop, volume);
425                 if(!sound)
426                         return -1;
427                 int id = m_next_id++;
428                 m_sounds_playing[id] = sound;
429                 return id;
430         }
431
432         int playSoundRawAt(SoundBuffer *buf, bool loop, float volume, v3f pos)
433         {
434                 assert(buf);
435                 PlayingSound *sound = createPlayingSoundAt(buf, loop, volume, pos);
436                 if(!sound)
437                         return -1;
438                 int id = m_next_id++;
439                 m_sounds_playing[id] = sound;
440                 return id;
441         }
442
443         void deleteSound(int id)
444         {
445                 std::map<int, PlayingSound*>::iterator i =
446                                 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(std::map<int, PlayingSound*>::iterator
487                                 i = m_sounds_playing.begin();
488                                 i != m_sounds_playing.end(); ++i)
489                 {
490                         int id = i->first;
491                         PlayingSound *sound = i->second;
492                         // If not playing, remove it
493                         {
494                                 ALint state;
495                                 alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state);
496                                 if(state != AL_PLAYING){
497                                         del_list.insert(id);
498                                 }
499                         }
500                 }
501                 if(!del_list.empty())
502                         verbosestream<<"OpenALSoundManager::maintain(): deleting "
503                                         <<del_list.size()<<" playing sounds"<<std::endl;
504                 for(std::set<int>::iterator i = del_list.begin();
505                                 i != del_list.end(); ++i)
506                 {
507                         deleteSound(*i);
508                 }
509         }
510
511         /* Interface */
512
513         bool loadSoundFile(const std::string &name,
514                         const std::string &filepath)
515         {
516                 SoundBuffer *buf = load_ogg_from_file(filepath);
517                 if (buf)
518                         addBuffer(name, buf);
519                 return false;
520         }
521         bool loadSoundData(const std::string &name,
522                         const std::string &filedata)
523         {
524                 SoundBuffer *buf = load_ogg_from_buffer(filedata, name);
525                 if (buf)
526                         addBuffer(name, buf);
527                 return false;
528         }
529
530         void updateListener(v3f pos, v3f vel, v3f at, v3f up)
531         {
532                 m_listener_pos = pos;
533                 alListener3f(AL_POSITION, pos.X, pos.Y, pos.Z);
534                 alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z);
535                 ALfloat f[6];
536                 f3_set(f, at);
537                 f3_set(f+3, -up);
538                 alListenerfv(AL_ORIENTATION, f);
539                 warn_if_error(alGetError(), "updateListener");
540         }
541
542         void setListenerGain(float gain)
543         {
544                 alListenerf(AL_GAIN, gain);
545         }
546
547         int playSound(const std::string &name, bool loop, float volume)
548         {
549                 maintain();
550                 if(name == "")
551                         return 0;
552                 SoundBuffer *buf = getFetchBuffer(name);
553                 if(!buf){
554                         infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
555                                         <<std::endl;
556                         return -1;
557                 }
558                 return playSoundRaw(buf, loop, volume);
559         }
560         int playSoundAt(const std::string &name, bool loop, float volume, v3f pos)
561         {
562                 maintain();
563                 if(name == "")
564                         return 0;
565                 SoundBuffer *buf = getFetchBuffer(name);
566                 if(!buf){
567                         infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
568                                         <<std::endl;
569                         return -1;
570                 }
571                 return playSoundRawAt(buf, loop, volume, pos);
572         }
573         void stopSound(int sound)
574         {
575                 maintain();
576                 deleteSound(sound);
577         }
578         bool soundExists(int sound)
579         {
580                 maintain();
581                 return (m_sounds_playing.count(sound) != 0);
582         }
583         void updateSoundPosition(int id, v3f pos)
584         {
585                 std::map<int, PlayingSound*>::iterator i =
586                                 m_sounds_playing.find(id);
587                 if(i == m_sounds_playing.end())
588                         return;
589                 PlayingSound *sound = i->second;
590
591                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
592                 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
593                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
594                 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
595         }
596 };
597
598 ISoundManager *createOpenALSoundManager(OnDemandSoundFetcher *fetcher)
599 {
600         OpenALSoundManager *m = new OpenALSoundManager(fetcher);
601         if(m->m_is_initialized)
602                 return m;
603         delete m;
604         return NULL;
605 };
606