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