]> git.lizzy.rs Git - minetest.git/blob - src/sound_openal.cpp
OpenAL sound: Use a simpler distance model
[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_INVERSE_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_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                 UNORDERED_MAP<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
446                 if(i == m_sounds_playing.end())
447                         return;
448                 PlayingSound *sound = i->second;
449
450                 alDeleteSources(1, &sound->source_id);
451
452                 delete sound;
453                 m_sounds_playing.erase(id);
454         }
455
456         /* If buffer does not exist, consult the fetcher */
457         SoundBuffer* getFetchBuffer(const std::string &name)
458         {
459                 SoundBuffer *buf = getBuffer(name);
460                 if(buf)
461                         return buf;
462                 if(!m_fetcher)
463                         return NULL;
464                 std::set<std::string> paths;
465                 std::set<std::string> datas;
466                 m_fetcher->fetchSounds(name, paths, datas);
467                 for(std::set<std::string>::iterator i = paths.begin();
468                                 i != paths.end(); ++i){
469                         loadSoundFile(name, *i);
470                 }
471                 for(std::set<std::string>::iterator i = datas.begin();
472                                 i != datas.end(); ++i){
473                         loadSoundData(name, *i);
474                 }
475                 return getBuffer(name);
476         }
477
478         // Remove stopped sounds
479         void maintain()
480         {
481                 verbosestream<<"OpenALSoundManager::maintain(): "
482                                 <<m_sounds_playing.size()<<" playing sounds, "
483                                 <<m_buffers.size()<<" sound names loaded"<<std::endl;
484                 std::set<int> del_list;
485                 for(UNORDERED_MAP<int, PlayingSound*>::iterator i = m_sounds_playing.begin();
486                                 i != m_sounds_playing.end(); ++i) {
487                         int id = i->first;
488                         PlayingSound *sound = i->second;
489                         // If not playing, remove it
490                         {
491                                 ALint state;
492                                 alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state);
493                                 if(state != AL_PLAYING){
494                                         del_list.insert(id);
495                                 }
496                         }
497                 }
498                 if(!del_list.empty())
499                         verbosestream<<"OpenALSoundManager::maintain(): deleting "
500                                         <<del_list.size()<<" playing sounds"<<std::endl;
501                 for(std::set<int>::iterator i = del_list.begin();
502                                 i != del_list.end(); ++i)
503                 {
504                         deleteSound(*i);
505                 }
506         }
507
508         /* Interface */
509
510         bool loadSoundFile(const std::string &name,
511                         const std::string &filepath)
512         {
513                 SoundBuffer *buf = load_ogg_from_file(filepath);
514                 if (buf)
515                         addBuffer(name, buf);
516                 return false;
517         }
518         bool loadSoundData(const std::string &name,
519                         const std::string &filedata)
520         {
521                 SoundBuffer *buf = load_ogg_from_buffer(filedata, name);
522                 if (buf)
523                         addBuffer(name, buf);
524                 return false;
525         }
526
527         void updateListener(v3f pos, v3f vel, v3f at, v3f up)
528         {
529                 m_listener_pos = pos;
530                 alListener3f(AL_POSITION, pos.X, pos.Y, pos.Z);
531                 alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z);
532                 ALfloat f[6];
533                 f3_set(f, at);
534                 f3_set(f+3, -up);
535                 alListenerfv(AL_ORIENTATION, f);
536                 warn_if_error(alGetError(), "updateListener");
537         }
538
539         void setListenerGain(float gain)
540         {
541                 alListenerf(AL_GAIN, gain);
542         }
543
544         int playSound(const std::string &name, bool loop, float volume)
545         {
546                 maintain();
547                 if(name == "")
548                         return 0;
549                 SoundBuffer *buf = getFetchBuffer(name);
550                 if(!buf){
551                         infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
552                                         <<std::endl;
553                         return -1;
554                 }
555                 return playSoundRaw(buf, loop, volume);
556         }
557         int playSoundAt(const std::string &name, bool loop, float volume, v3f pos)
558         {
559                 maintain();
560                 if(name == "")
561                         return 0;
562                 SoundBuffer *buf = getFetchBuffer(name);
563                 if(!buf){
564                         infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
565                                         <<std::endl;
566                         return -1;
567                 }
568                 return playSoundRawAt(buf, loop, volume, pos);
569         }
570         void stopSound(int sound)
571         {
572                 maintain();
573                 deleteSound(sound);
574         }
575         bool soundExists(int sound)
576         {
577                 maintain();
578                 return (m_sounds_playing.count(sound) != 0);
579         }
580         void updateSoundPosition(int id, v3f pos)
581         {
582                 UNORDERED_MAP<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
583                 if (i == m_sounds_playing.end())
584                         return;
585                 PlayingSound *sound = i->second;
586
587                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
588                 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
589                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
590                 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
591         }
592 };
593
594 ISoundManager *createOpenALSoundManager(OnDemandSoundFetcher *fetcher)
595 {
596         OpenALSoundManager *m = new OpenALSoundManager(fetcher);
597         if(m->m_is_initialized)
598                 return m;
599         delete m;
600         return NULL;
601 };
602