Load sound from memory
[oweals/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 "filesys.h"
43 #include "util/numeric.h" // myrand()
44 #include "porting.h"
45 #include <map>
46 #include <vector>
47 #include <fstream>
48
49 #define BUFFER_SIZE 30000
50
51 static const char *alcErrorString(ALCenum err)
52 {
53         switch (err) {
54         case ALC_NO_ERROR:
55                 return "no error";
56         case ALC_INVALID_DEVICE:
57                 return "invalid device";
58         case ALC_INVALID_CONTEXT:
59                 return "invalid context";
60         case ALC_INVALID_ENUM:
61                 return "invalid enum";
62         case ALC_INVALID_VALUE:
63                 return "invalid value";
64         case ALC_OUT_OF_MEMORY:
65                 return "out of memory";
66         default:
67                 return "<unknown OpenAL error>";
68         }
69 }
70
71 static const char *alErrorString(ALenum err)
72 {
73         switch (err) {
74         case AL_NO_ERROR:
75                 return "no error";
76         case AL_INVALID_NAME:
77                 return "invalid name";
78         case AL_INVALID_ENUM:
79                 return "invalid enum";
80         case AL_INVALID_VALUE:
81                 return "invalid value";
82         case AL_INVALID_OPERATION:
83                 return "invalid operation";
84         case AL_OUT_OF_MEMORY:
85                 return "out of memory";
86         default:
87                 return "<unknown OpenAL error>";
88         }
89 }
90
91 static ALenum warn_if_error(ALenum err, const char *desc)
92 {
93         if(err == AL_NO_ERROR)
94                 return err;
95         warningstream<<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 *load_opened_ogg_file(OggVorbis_File *oggFile,
115                 const std::string &filename_for_logging)
116 {
117         int endian = 0; // 0 for Little-Endian, 1 for Big-Endian
118         int bitStream;
119         long bytes;
120         char array[BUFFER_SIZE]; // Local fixed size array
121         vorbis_info *pInfo;
122
123         SoundBuffer *snd = new SoundBuffer;
124
125         // Get some information about the OGG file
126         pInfo = ov_info(oggFile, -1);
127
128         // Check the number of channels... always use 16-bit samples
129         if(pInfo->channels == 1)
130                 snd->format = AL_FORMAT_MONO16;
131         else
132                 snd->format = AL_FORMAT_STEREO16;
133
134         // The frequency of the sampling rate
135         snd->freq = pInfo->rate;
136
137         // Keep reading until all is read
138         do
139         {
140                 // Read up to a buffer's worth of decoded sound data
141                 bytes = ov_read(oggFile, array, BUFFER_SIZE, endian, 2, 1, &bitStream);
142
143                 if(bytes < 0)
144                 {
145                         ov_clear(oggFile);
146                         infostream << "Audio: Error decoding "
147                                 << filename_for_logging << std::endl;
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         bool m_can_vorbis;
274         int m_next_id;
275         std::map<std::string, std::vector<SoundBuffer*> > m_buffers;
276         std::map<int, PlayingSound*> m_sounds_playing;
277         v3f m_listener_pos;
278 public:
279         bool m_is_initialized;
280         OpenALSoundManager(OnDemandSoundFetcher *fetcher):
281                 m_fetcher(fetcher),
282                 m_device(NULL),
283                 m_context(NULL),
284                 m_can_vorbis(false),
285                 m_next_id(1),
286                 m_is_initialized(false)
287         {
288                 ALCenum error = ALC_NO_ERROR;
289
290                 infostream<<"Audio: Initializing..."<<std::endl;
291
292                 m_device = alcOpenDevice(NULL);
293                 if(!m_device){
294                         infostream<<"Audio: No audio device available, audio system "
295                                 <<"not initialized"<<std::endl;
296                         return;
297                 }
298
299                 if(alcIsExtensionPresent(m_device, "EXT_vorbis")){
300                         infostream<<"Audio: Vorbis extension present"<<std::endl;
301                         m_can_vorbis = true;
302                 } else{
303                         infostream<<"Audio: Vorbis extension NOT present"<<std::endl;
304                         m_can_vorbis = false;
305                 }
306
307                 m_context = alcCreateContext(m_device, NULL);
308                 if(!m_context){
309                         error = alcGetError(m_device);
310                         infostream<<"Audio: Unable to initialize audio context, "
311                                         <<"aborting audio initialization ("<<alcErrorString(error)
312                                         <<")"<<std::endl;
313                         alcCloseDevice(m_device);
314                         m_device = NULL;
315                         return;
316                 }
317
318                 if(!alcMakeContextCurrent(m_context) ||
319                                 (error = alcGetError(m_device) != ALC_NO_ERROR))
320                 {
321                         infostream<<"Audio: Error setting audio context, aborting audio "
322                                         <<"initialization ("<<alcErrorString(error)<<")"<<std::endl;
323                         alcDestroyContext(m_context);
324                         m_context = NULL;
325                         alcCloseDevice(m_device);
326                         m_device = NULL;
327                         return;
328                 }
329
330                 alDistanceModel(AL_EXPONENT_DISTANCE);
331
332                 infostream<<"Audio: Initialized: OpenAL "<<alGetString(AL_VERSION)
333                                 <<", using "<<alcGetString(m_device, ALC_DEVICE_SPECIFIER)
334                                 <<std::endl;
335
336                 m_is_initialized = true;
337         }
338
339         ~OpenALSoundManager()
340         {
341                 infostream<<"Audio: Deinitializing..."<<std::endl;
342                 // KABOOM!
343                 // TODO: Clear SoundBuffers
344                 alcMakeContextCurrent(NULL);
345                 alcDestroyContext(m_context);
346                 m_context = NULL;
347                 alcCloseDevice(m_device);
348                 m_device = NULL;
349
350                 for (std::map<std::string, std::vector<SoundBuffer*> >::iterator i = m_buffers.begin();
351                                 i != m_buffers.end(); ++i) {
352                         for (std::vector<SoundBuffer*>::iterator iter = (*i).second.begin();
353                                         iter != (*i).second.end(); ++iter) {
354                                 delete *iter;
355                         }
356                         (*i).second.clear();
357                 }
358                 m_buffers.clear();
359                 infostream<<"Audio: Deinitialized."<<std::endl;
360         }
361
362         void addBuffer(const std::string &name, SoundBuffer *buf)
363         {
364                 std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
365                                 m_buffers.find(name);
366                 if(i != m_buffers.end()){
367                         i->second.push_back(buf);
368                         return;
369                 }
370                 std::vector<SoundBuffer*> bufs;
371                 bufs.push_back(buf);
372                 m_buffers[name] = bufs;
373                 return;
374         }
375
376         SoundBuffer* getBuffer(const std::string &name)
377         {
378                 std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
379                                 m_buffers.find(name);
380                 if(i == m_buffers.end())
381                         return NULL;
382                 std::vector<SoundBuffer*> &bufs = i->second;
383                 int j = myrand() % bufs.size();
384                 return bufs[j];
385         }
386
387         PlayingSound* createPlayingSound(SoundBuffer *buf, bool loop,
388                         float volume)
389         {
390                 infostream<<"OpenALSoundManager: Creating playing sound"<<std::endl;
391                 assert(buf);
392                 PlayingSound *sound = new PlayingSound;
393                 assert(sound);
394                 warn_if_error(alGetError(), "before createPlayingSound");
395                 alGenSources(1, &sound->source_id);
396                 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
397                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, true);
398                 alSource3f(sound->source_id, AL_POSITION, 0, 0, 0);
399                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
400                 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
401                 volume = MYMAX(0.0, volume);
402                 alSourcef(sound->source_id, AL_GAIN, volume);
403                 alSourcePlay(sound->source_id);
404                 warn_if_error(alGetError(), "createPlayingSound");
405                 return sound;
406         }
407
408         PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop,
409                         float volume, v3f pos)
410         {
411                 infostream<<"OpenALSoundManager: Creating positional playing sound"
412                                 <<std::endl;
413                 assert(buf);
414                 PlayingSound *sound = new PlayingSound;
415                 assert(sound);
416                 warn_if_error(alGetError(), "before createPlayingSoundAt");
417                 alGenSources(1, &sound->source_id);
418                 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
419                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
420                 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
421                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
422                 //alSourcef(sound->source_id, AL_ROLLOFF_FACTOR, 0.7);
423                 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
424                 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
425                 volume = MYMAX(0.0, volume);
426                 alSourcef(sound->source_id, AL_GAIN, volume);
427                 alSourcePlay(sound->source_id);
428                 warn_if_error(alGetError(), "createPlayingSoundAt");
429                 return sound;
430         }
431
432         int playSoundRaw(SoundBuffer *buf, bool loop, float volume)
433         {
434                 assert(buf);
435                 PlayingSound *sound = createPlayingSound(buf, loop, volume);
436                 if(!sound)
437                         return -1;
438                 int id = m_next_id++;
439                 m_sounds_playing[id] = sound;
440                 return id;
441         }
442
443         int playSoundRawAt(SoundBuffer *buf, bool loop, float volume, v3f pos)
444         {
445                 assert(buf);
446                 PlayingSound *sound = createPlayingSoundAt(buf, loop, volume, pos);
447                 if(!sound)
448                         return -1;
449                 int id = m_next_id++;
450                 m_sounds_playing[id] = sound;
451                 return id;
452         }
453
454         void deleteSound(int id)
455         {
456                 std::map<int, PlayingSound*>::iterator i =
457                                 m_sounds_playing.find(id);
458                 if(i == m_sounds_playing.end())
459                         return;
460                 PlayingSound *sound = i->second;
461
462                 alDeleteSources(1, &sound->source_id);
463
464                 delete sound;
465                 m_sounds_playing.erase(id);
466         }
467
468         /* If buffer does not exist, consult the fetcher */
469         SoundBuffer* getFetchBuffer(const std::string &name)
470         {
471                 SoundBuffer *buf = getBuffer(name);
472                 if(buf)
473                         return buf;
474                 if(!m_fetcher)
475                         return NULL;
476                 std::set<std::string> paths;
477                 std::set<std::string> datas;
478                 m_fetcher->fetchSounds(name, paths, datas);
479                 for(std::set<std::string>::iterator i = paths.begin();
480                                 i != paths.end(); ++i){
481                         loadSoundFile(name, *i);
482                 }
483                 for(std::set<std::string>::iterator i = datas.begin();
484                                 i != datas.end(); ++i){
485                         loadSoundData(name, *i);
486                 }
487                 return getBuffer(name);
488         }
489
490         // Remove stopped sounds
491         void maintain()
492         {
493                 verbosestream<<"OpenALSoundManager::maintain(): "
494                                 <<m_sounds_playing.size()<<" playing sounds, "
495                                 <<m_buffers.size()<<" sound names loaded"<<std::endl;
496                 std::set<int> del_list;
497                 for(std::map<int, PlayingSound*>::iterator
498                                 i = m_sounds_playing.begin();
499                                 i != m_sounds_playing.end(); ++i)
500                 {
501                         int id = i->first;
502                         PlayingSound *sound = i->second;
503                         // If not playing, remove it
504                         {
505                                 ALint state;
506                                 alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state);
507                                 if(state != AL_PLAYING){
508                                         del_list.insert(id);
509                                 }
510                         }
511                 }
512                 if(!del_list.empty())
513                         verbosestream<<"OpenALSoundManager::maintain(): deleting "
514                                         <<del_list.size()<<" playing sounds"<<std::endl;
515                 for(std::set<int>::iterator i = del_list.begin();
516                                 i != del_list.end(); ++i)
517                 {
518                         deleteSound(*i);
519                 }
520         }
521
522         /* Interface */
523
524         bool loadSoundFile(const std::string &name,
525                         const std::string &filepath)
526         {
527                 SoundBuffer *buf = load_ogg_from_file(filepath);
528                 if (buf)
529                         addBuffer(name, buf);
530                 return false;
531         }
532         bool loadSoundData(const std::string &name,
533                         const std::string &filedata)
534         {
535                 SoundBuffer *buf = load_ogg_from_buffer(filedata, name);
536                 if (buf)
537                         addBuffer(name, buf);
538                 return false;
539         }
540
541         void updateListener(v3f pos, v3f vel, v3f at, v3f up)
542         {
543                 m_listener_pos = pos;
544                 alListener3f(AL_POSITION, pos.X, pos.Y, pos.Z);
545                 alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z);
546                 ALfloat f[6];
547                 f3_set(f, at);
548                 f3_set(f+3, -up);
549                 alListenerfv(AL_ORIENTATION, f);
550                 warn_if_error(alGetError(), "updateListener");
551         }
552
553         void setListenerGain(float gain)
554         {
555                 alListenerf(AL_GAIN, gain);
556         }
557
558         int playSound(const std::string &name, bool loop, float volume)
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 playSoundRaw(buf, loop, volume);
570         }
571         int playSoundAt(const std::string &name, bool loop, float volume, v3f pos)
572         {
573                 maintain();
574                 if(name == "")
575                         return 0;
576                 SoundBuffer *buf = getFetchBuffer(name);
577                 if(!buf){
578                         infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
579                                         <<std::endl;
580                         return -1;
581                 }
582                 return playSoundRawAt(buf, loop, volume, pos);
583         }
584         void stopSound(int sound)
585         {
586                 maintain();
587                 deleteSound(sound);
588         }
589         bool soundExists(int sound)
590         {
591                 maintain();
592                 return (m_sounds_playing.count(sound) != 0);
593         }
594         void updateSoundPosition(int id, v3f pos)
595         {
596                 std::map<int, PlayingSound*>::iterator i =
597                                 m_sounds_playing.find(id);
598                 if(i == m_sounds_playing.end())
599                         return;
600                 PlayingSound *sound = i->second;
601
602                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
603                 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
604                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
605                 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
606         }
607 };
608
609 ISoundManager *createOpenALSoundManager(OnDemandSoundFetcher *fetcher)
610 {
611         OpenALSoundManager *m = new OpenALSoundManager(fetcher);
612         if(m->m_is_initialized)
613                 return m;
614         delete m;
615         return NULL;
616 };
617