]> git.lizzy.rs Git - minetest.git/blob - src/sound_openal.cpp
9566f95c2c5b2167cc5bed71974ce772a51fd8bb
[minetest.git] / src / sound_openal.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2012 celeron55, Perttu Ahola <celeron55@gmail.com>
4 OpenAL support based on work by:
5 Copyright (C) 2011 Sebastian 'Bahamada' Rühl
6 Copyright (C) 2011 Cyriaque 'Cisoun' Skrapits <cysoun@gmail.com>
7 Copyright (C) 2011 Giuseppe Bilotta <giuseppe.bilotta@gmail.com>
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 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 General Public License for more details.
18
19 You should have received a copy of the GNU 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(_MSC_VER)
27         #include <al.h>
28         #include <alc.h>
29         #include <alext.h>
30 #elif defined(__APPLE__)
31         #include <OpenAL/al.h>
32         #include <OpenAL/alc.h>
33         #include <OpenAL/alext.h>
34 #else
35         #include <AL/al.h>
36         #include <AL/alc.h>
37         #include <AL/alext.h>
38 #endif
39 #include <vorbis/vorbisfile.h>
40 #include "log.h"
41 #include <map>
42 #include <vector>
43 #include "utility.h" // myrand()
44
45 #define BUFFER_SIZE 30000
46
47 static const char *alcErrorString(ALCenum err)
48 {
49         switch (err) {
50         case ALC_NO_ERROR:
51                 return "no error";
52         case ALC_INVALID_DEVICE:
53                 return "invalid device";
54         case ALC_INVALID_CONTEXT:
55                 return "invalid context";
56         case ALC_INVALID_ENUM:
57                 return "invalid enum";
58         case ALC_INVALID_VALUE:
59                 return "invalid value";
60         case ALC_OUT_OF_MEMORY:
61                 return "out of memory";
62         default:
63                 return "<unknown OpenAL error>";
64         }
65 }
66
67 static const char *alErrorString(ALenum err)
68 {
69         switch (err) {
70         case AL_NO_ERROR:
71                 return "no error";
72         case AL_INVALID_NAME:
73                 return "invalid name";
74         case AL_INVALID_ENUM:
75                 return "invalid enum";
76         case AL_INVALID_VALUE:
77                 return "invalid value";
78         case AL_INVALID_OPERATION:
79                 return "invalid operation";
80         case AL_OUT_OF_MEMORY:
81                 return "out of memory";
82         default:
83                 return "<unknown OpenAL error>";
84         }
85 }
86
87 void f3_set(ALfloat *f3, v3f v)
88 {
89         f3[0] = v.X;
90         f3[1] = v.Y;
91         f3[2] = v.Z;
92 }
93
94 struct SoundBuffer
95 {
96         ALenum  format;
97         ALsizei freq;
98         ALuint  bufferID;
99         std::vector<char> buffer;
100 };
101
102 SoundBuffer* loadOggFile(const std::string &filepath)
103 {
104         int endian = 0; // 0 for Little-Endian, 1 for Big-Endian
105         int bitStream;
106         long bytes;
107         char array[BUFFER_SIZE]; // Local fixed size array
108         vorbis_info *pInfo;
109         OggVorbis_File oggFile;
110
111         // Try opening the given file
112         if(ov_fopen(filepath.c_str(), &oggFile) != 0)
113         {
114                 infostream<<"Audio: Error opening "<<filepath<<" for decoding"<<std::endl;
115                 return NULL;
116         }
117
118         SoundBuffer *snd = new SoundBuffer;
119
120         // Get some information about the OGG file
121         pInfo = ov_info(&oggFile, -1);
122
123         // Check the number of channels... always use 16-bit samples
124         if(pInfo->channels == 1)
125                 snd->format = AL_FORMAT_MONO16;
126         else
127                 snd->format = AL_FORMAT_STEREO16;
128
129         // The frequency of the sampling rate
130         snd->freq = pInfo->rate;
131
132         // Keep reading until all is read
133         do
134         {
135                 // Read up to a buffer's worth of decoded sound data
136                 bytes = ov_read(&oggFile, array, BUFFER_SIZE, endian, 2, 1, &bitStream);
137
138                 if(bytes < 0)
139                 {
140                         ov_clear(&oggFile);
141                         infostream<<"Audio: Error decoding "<<filepath<<std::endl;
142                         return NULL;
143                 }
144
145                 // Append to end of buffer
146                 snd->buffer.insert(snd->buffer.end(), array, array + bytes);
147         } while (bytes > 0);
148
149         alGenBuffers(1, &snd->bufferID);
150         alBufferData(snd->bufferID, snd->format,
151                         &(snd->buffer[0]), snd->buffer.size(),
152                         snd->freq);
153
154         ALenum error = alGetError();
155
156         if(error != AL_NO_ERROR){
157                 infostream<<"Audio: OpenAL error: "<<alErrorString(error)
158                                 <<"preparing sound buffer"<<std::endl;
159         }
160
161         infostream<<"Audio file "<<filepath<<" loaded"<<std::endl;
162
163         // Clean up!
164         ov_clear(&oggFile);
165
166         return snd;
167 }
168
169 struct PlayingSound
170 {
171 };
172
173 class OpenALSoundManager: public ISoundManager
174 {
175 private:
176         ALCdevice *m_device;
177         ALCcontext *m_context;
178         bool m_can_vorbis;
179         int m_next_id;
180         std::map<std::string, std::vector<SoundBuffer*> > m_buffers;
181         std::map<int, PlayingSound*> m_sounds_playing;
182 public:
183         OpenALSoundManager():
184                 m_device(NULL),
185                 m_context(NULL),
186                 m_can_vorbis(false),
187                 m_next_id(1)
188         {
189                 ALCenum error = ALC_NO_ERROR;
190                 
191                 infostream<<"Audio: Initializing..."<<std::endl;
192
193                 m_device = alcOpenDevice(NULL);
194                 if(!m_device){
195                         infostream<<"Audio: No audio device available, audio system "
196                                 <<"not initialized"<<std::endl;
197                         return;
198                 }
199
200                 if(alcIsExtensionPresent(m_device, "EXT_vorbis")){
201                         infostream<<"Audio: Vorbis extension present"<<std::endl;
202                         m_can_vorbis = true;
203                 } else{
204                         infostream<<"Audio: Vorbis extension NOT present"<<std::endl;
205                         m_can_vorbis = false;
206                 }
207
208                 m_context = alcCreateContext(m_device, NULL);
209                 if(!m_context){
210                         error = alcGetError(m_device);
211                         infostream<<"Audio: Unable to initialize audio context, "
212                                         <<"aborting audio initialization ("<<alcErrorString(error)
213                                         <<")"<<std::endl;
214                         alcCloseDevice(m_device);
215                         m_device = NULL;
216                         return;
217                 }
218
219                 if(!alcMakeContextCurrent(m_context) ||
220                                 (error = alcGetError(m_device) != ALC_NO_ERROR))
221                 {
222                         infostream<<"Audio: Error setting audio context, aborting audio "
223                                         <<"initialization ("<<alcErrorString(error)<<")"<<std::endl;
224                         alcDestroyContext(m_context);
225                         m_context = NULL;
226                         alcCloseDevice(m_device);
227                         m_device = NULL;
228                         return;
229                 }
230
231                 alDistanceModel(AL_EXPONENT_DISTANCE);
232
233                 infostream<<"Audio: Initialized: OpenAL "<<alGetString(AL_VERSION)
234                                 <<", using "<<alcGetString(m_device, ALC_DEVICE_SPECIFIER)
235                                 <<std::endl;
236         }
237
238         ~OpenALSoundManager()
239         {
240                 infostream<<"Audio: Deinitializing..."<<std::endl;
241                 // KABOOM!
242                 // TODO: Clear SoundBuffers
243                 alcMakeContextCurrent(NULL);
244                 alcDestroyContext(m_context);
245                 m_context = NULL;
246                 alcCloseDevice(m_device);
247                 m_device = NULL;
248                 infostream<<"Audio: Deinitialized."<<std::endl;
249         }
250         
251         void addBuffer(const std::string &name, SoundBuffer *buf)
252         {
253                 std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
254                                 m_buffers.find(name);
255                 if(i != m_buffers.end()){
256                         i->second.push_back(buf);
257                         return;
258                 }
259                 std::vector<SoundBuffer*> bufs;
260                 bufs.push_back(buf);
261                 return;
262         }
263
264         SoundBuffer* getBuffer(const std::string &name)
265         {
266                 std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
267                                 m_buffers.find(name);
268                 if(i == m_buffers.end())
269                         return NULL;
270                 std::vector<SoundBuffer*> &bufs = i->second;
271                 int j = myrand() % bufs.size();
272                 return bufs[j];
273         }
274
275         void updateListener(v3f pos, v3f vel, v3f at, v3f up)
276         {
277                 ALfloat f[6];
278                 f3_set(f, pos);
279                 alListenerfv(AL_POSITION, f);
280                 f3_set(f, vel);
281                 alListenerfv(AL_VELOCITY, f);
282                 f3_set(f, at);
283                 f3_set(f+3, up);
284                 alListenerfv(AL_ORIENTATION, f);
285         }
286         
287         bool loadSound(const std::string &name,
288                         const std::string &filepath)
289         {
290                 SoundBuffer *buf = loadOggFile(filepath);
291                 if(buf)
292                         addBuffer(name, buf);
293                 return false;
294         }
295         bool loadSound(const std::string &name,
296                         const std::vector<char> &filedata)
297         {
298                 errorstream<<"OpenALSoundManager: Loading from filedata not"
299                                 " implemented"<<std::endl;
300                 return false;
301         }
302
303         int playSound(const std::string &name, int loopcount,
304                         float volume)
305         {
306                 return -1;
307         }
308         int playSoundAt(const std::string &name, int loopcount,
309                         v3f pos, float volume)
310         {
311                 return -1;
312         }
313         void stopSound(int sound)
314         {
315         }
316 };
317
318 ISoundManager *createSoundManager()
319 {
320         return new OpenALSoundManager();
321 };
322