1 //
2 //  SuperTuxKart - a fun racing game with go-kart
3 //  Copyright (C) 2007-2015 Damien Morel <divdams@free.fr>
4 //
5 //  This program is free software; you can redistribute it and/or
6 //  modify it under the terms of the GNU General Public License
7 //  as published by the Free Software Foundation; either version 3
8 //  of the License, or (at your option) any later version.
9 //
10 //  This program is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU General Public License for more details.
14 //
15 //  You should have received a copy of the GNU General Public License
16 //  along with this program; if not, write to the Free Software
17 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 
19 #ifdef ENABLE_SOUND
20 
21 #include "audio/music_ogg.hpp"
22 
23 #include <stdexcept>
24 
25 #include "audio/music_manager.hpp"
26 #include "audio/sfx_manager.hpp"
27 #include "utils/constants.hpp"
28 #include "utils/file_utils.hpp"
29 #include "utils/log.hpp"
30 
MusicOggStream(float loop_start)31 MusicOggStream::MusicOggStream(float loop_start)
32 {
33     //m_oggStream= NULL;
34     m_soundBuffers[0] = m_soundBuffers[1]= 0;
35     m_soundSource     = -1;
36     m_pausedMusic     = true;
37     m_playing.store(false);
38     m_loop_start      = loop_start;
39 }   // MusicOggStream
40 
41 //-----------------------------------------------------------------------------
~MusicOggStream()42 MusicOggStream::~MusicOggStream()
43 {
44     if(stopMusic() == false)
45         Log::warn("MusicOgg", "problems while stopping music.");
46 }   // ~MusicOggStream
47 
48 //-----------------------------------------------------------------------------
load(const std::string & filename)49 bool MusicOggStream::load(const std::string& filename)
50 {
51     if (isPlaying()) stopMusic();
52 
53     m_error = true;
54     m_fileName = filename;
55     if(m_fileName=="") return false;
56 
57     m_oggFile = FileUtils::fopenU8Path(m_fileName, "rb");
58 
59     if(!m_oggFile)
60     {
61         Log::error("MusicOgg", "Loading Music: %s failed (fopen returned NULL)",
62                    m_fileName.c_str());
63         return false;
64     }
65 
66 #if defined( WIN32 ) || defined( WIN64 )
67     const int result = ov_open_callbacks((void *)m_oggFile, &m_oggStream, NULL,
68                                          0, OV_CALLBACKS_DEFAULT             );
69 #else
70     const int result = ov_open(m_oggFile, &m_oggStream, NULL, 0);
71 #endif
72 
73     if (result < 0)
74     {
75         fclose(m_oggFile);
76 
77 
78         const char* errorMessage;
79         switch (result)
80         {
81             case OV_EREAD:
82                 errorMessage = "OV_EREAD";
83                 break;
84             case OV_ENOTVORBIS:
85                 errorMessage = "OV_ENOTVORBIS";
86                 break;
87             case OV_EVERSION:
88                 errorMessage = "OV_EVERSION";
89                 break;
90             case OV_EBADHEADER:
91                 errorMessage = "OV_EBADHEADER";
92                 break;
93             case OV_EFAULT:
94                 errorMessage = "OV_EFAULT";
95                 break;
96             default:
97                 errorMessage = "Unknown Error";
98         }
99 
100         Log::error("MusicOgg", "Loading Music: %s failed : "
101                                "ov_open returned error code %i (%s)",
102                m_fileName.c_str(), result, errorMessage);
103         return false;
104     }
105 
106     m_vorbisInfo = ov_info(&m_oggStream, -1);
107 
108     if (m_vorbisInfo->channels == 1) nb_channels = AL_FORMAT_MONO16;
109     else                             nb_channels = AL_FORMAT_STEREO16;
110 
111     alGenBuffers(2, m_soundBuffers);
112     if (check("alGenBuffers") == false) return false;
113 
114     alGenSources(1, &m_soundSource);
115     if (check("alGenSources") == false) return false;
116 
117     alSource3f(m_soundSource, AL_POSITION,        0.0, 0.0, 0.0);
118     alSource3f(m_soundSource, AL_VELOCITY,        0.0, 0.0, 0.0);
119     alSource3f(m_soundSource, AL_DIRECTION,       0.0, 0.0, 0.0);
120     alSourcef (m_soundSource, AL_ROLLOFF_FACTOR,  0.0          );
121     alSourcef (m_soundSource, AL_GAIN,            1.0          );
122     alSourcei (m_soundSource, AL_SOURCE_RELATIVE, AL_TRUE      );
123 
124     m_error=false;
125     return true;
126 }   // load
127 
128 //-----------------------------------------------------------------------------
empty()129 bool MusicOggStream::empty()
130 {
131     int queued= 0;
132     alGetSourcei(m_soundSource, AL_BUFFERS_QUEUED, &queued);
133 
134     while(queued--)
135     {
136         ALuint buffer = 0;
137         alSourceUnqueueBuffers(m_soundSource, 1, &buffer);
138 
139         if (!check("alSourceUnqueueBuffers")) return false;
140     }
141 
142     return true;
143 }   // empty
144 
145 //-----------------------------------------------------------------------------
release()146 bool MusicOggStream::release()
147 {
148     if (m_fileName == "")
149     {
150         // nothing is loaded
151         return true;
152     }
153 
154     pauseMusic();
155     m_fileName= "";
156 
157     empty();
158     alDeleteSources(1, &m_soundSource);
159     check("alDeleteSources");
160     alDeleteBuffers(2, m_soundBuffers);
161     check("alDeleteBuffers");
162 
163     // Handle error correctly
164     if(!m_error) ov_clear(&m_oggStream);
165 
166     m_soundSource = -1;
167     m_playing.store(false);
168 
169     return true;
170 }   // release
171 
172 //-----------------------------------------------------------------------------
playMusic()173 bool MusicOggStream::playMusic()
174 {
175     if(isPlaying())
176         return true;
177 
178     if(!streamIntoBuffer(m_soundBuffers[0]))
179         return false;
180 
181     if(!streamIntoBuffer(m_soundBuffers[1]))
182         return false;
183 
184     alSourceQueueBuffers(m_soundSource, 2, m_soundBuffers);
185 
186     alSourcePlay(m_soundSource);
187     m_pausedMusic = false;
188     m_playing.store(true);
189     check("playMusic");
190     return true;
191 }   // playMusic
192 
193 //-----------------------------------------------------------------------------
isPlaying()194 bool MusicOggStream::isPlaying()
195 {
196     return m_playing.load();
197 
198     /*
199     if (m_soundSource == -1) return false;
200 
201     ALenum state;
202     alGetSourcei(m_soundSource, AL_SOURCE_STATE, &state);
203 
204     return (state == AL_PLAYING);
205      */
206 }   // isPlaying
207 
208 //-----------------------------------------------------------------------------
stopMusic()209 bool MusicOggStream::stopMusic()
210 {
211     m_playing.store(false);
212     return (release());
213 }   // stopMusic
214 
215 //-----------------------------------------------------------------------------
pauseMusic()216 bool MusicOggStream::pauseMusic()
217 {
218     m_playing.store(false);
219     if (m_fileName == "")
220     {
221         // nothing is loaded
222         return true;
223     }
224 
225     alSourceStop(m_soundSource);
226     m_pausedMusic= true;
227     return true;
228 }   // pauseMusic
229 
230 //-----------------------------------------------------------------------------
resumeMusic()231 bool MusicOggStream::resumeMusic()
232 {
233     if (m_fileName == "")
234     {
235         // nothing is loaded
236         return true;
237     }
238 
239     m_playing.store(true);
240 
241     alSourcePlay(m_soundSource);
242     m_pausedMusic= false;
243     return true;
244 }   // resumeMusic
245 
246 //-----------------------------------------------------------------------------
setVolume(float volume)247 void MusicOggStream::setVolume(float volume)
248 {
249     volume *= music_manager->getMasterMusicVolume();
250     if (volume > 1.0f) volume = 1.0f;
251     if (volume < 0.0f) volume = 0.0f;
252 
253     alSourcef(m_soundSource, AL_GAIN, volume);
254     check("volume music");   // clear errors
255 }   // setVolume
256 
257 //-----------------------------------------------------------------------------
updateFaster(float percent,float max_pitch)258 void MusicOggStream::updateFaster(float percent, float max_pitch)
259 {
260     alSourcef(m_soundSource,AL_PITCH,1+max_pitch*percent);
261     update();
262 }   // updateFaster
263 
264 //-----------------------------------------------------------------------------
update()265 void MusicOggStream::update()
266 {
267 
268     if (m_pausedMusic || m_soundSource == ALuint(-1))
269     {
270         // nothing todo
271         return;
272     }
273 
274     int processed= 0;
275     bool active= true;
276 
277     alGetSourcei(m_soundSource, AL_BUFFERS_PROCESSED, &processed);
278 
279     while(processed--)
280     {
281         ALuint buffer;
282 
283         alSourceUnqueueBuffers(m_soundSource, 1, &buffer);
284         if(!check("alSourceUnqueueBuffers")) return;
285 
286         active = streamIntoBuffer(buffer);
287         if(!active)
288         {
289             // no more data. Seek to loop start (causes the sound to loop)
290             ov_time_seek(&m_oggStream, m_loop_start);
291             active = streamIntoBuffer(buffer);//now there really should be data
292         }
293 
294         alSourceQueueBuffers(m_soundSource, 1, &buffer);
295         if (!check("alSourceQueueBuffers")) return;
296     }
297 
298     if (active)
299     {
300         // For debugging
301         SFXManager::checkError("before source state");
302         // we have data, so we should be playing...
303         ALenum state;
304         alGetSourcei(m_soundSource, AL_SOURCE_STATE, &state);
305         if (state != AL_PLAYING)
306         {
307             // Prevent flooding
308             static int count = 0;
309             count++;
310             if (count<10)
311                 Log::warn("MusicOgg", "Music not playing when it should be. "
312                           "Source state: %d", state);
313             alGetSourcei(m_soundSource, AL_BUFFERS_PROCESSED, &processed);
314             alSourcePlay(m_soundSource);
315         }
316     }
317     else
318     {
319         Log::warn("MusicOgg", "Attempt to stream music into buffer failed "
320                               "twice in a row.");
321     }
322 }   // update
323 
324 //-----------------------------------------------------------------------------
streamIntoBuffer(ALuint buffer)325 bool MusicOggStream::streamIntoBuffer(ALuint buffer)
326 {
327     char pcm[m_buffer_size];
328     const int isBigEndian = (IS_LITTLE_ENDIAN ? 0 : 1);
329 
330     int  size = 0;
331     int  portion;
332 
333     while(size < m_buffer_size)
334     {
335         int result = ov_read(&m_oggStream, pcm + size, m_buffer_size - size,
336                          isBigEndian, 2, 1, &portion);
337 
338         if(result > 0)
339             size += result;
340         else
341             if(result < 0)
342                 throw errorString(result);
343             else
344                 break;
345     }
346 
347     if(size == 0) return false;
348 
349     alBufferData(buffer, nb_channels, pcm, size, m_vorbisInfo->rate);
350     check("alBufferData");
351 
352     return true;
353 }   // streamIntoBuffer
354 
355 //-----------------------------------------------------------------------------
check(const char * what)356 bool MusicOggStream::check(const char* what)
357 {
358     int error = alGetError();
359 
360     if (error != AL_NO_ERROR)
361     {
362         Log::error("MusicOgg", "OpenAL error at %s : %s (%i)", what,
363                   SFXManager::getErrorString(error).c_str(), error);
364         return false;
365     }
366 
367     return true;
368 }   // check
369 
370 //-----------------------------------------------------------------------------
errorString(int code)371 std::string MusicOggStream::errorString(int code)
372 {
373     switch(code)
374     {
375         case OV_EREAD:
376             return std::string("Read error from media.");
377         case OV_ENOTVORBIS:
378             return std::string("It is not Vorbis data.");
379         case OV_EVERSION:
380             return std::string("Vorbis version mismatch.");
381         case OV_EBADHEADER:
382             return std::string("Invalid Vorbis bitstream header.");
383         case OV_EFAULT:
384             return std::string("Internal logic fault (bug or heap/stack corruption).");
385         default:
386             return std::string("Unknown Vorbis error.");
387     }
388 }   // errorString
389 
390 #endif // ENABLE_SOUND
391