1 //
2 //  SuperTuxKart - a fun racing game with go-kart
3 //  Copyright (C) 2010-2015 Marianne Gagnon
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 #include "audio/sfx_buffer.hpp"
20 #include "audio/sfx_manager.hpp"
21 #include "config/user_config.hpp"
22 #include "io/file_manager.hpp"
23 #include "utils/constants.hpp"
24 #include "utils/file_utils.hpp"
25 #include "utils/log.hpp"
26 
27 #ifdef ENABLE_SOUND
28 #  include <vorbis/codec.h>
29 #  include <vorbis/vorbisfile.h>
30 #endif
31 
32 //----------------------------------------------------------------------------
33 /** Creates a sfx. The parameter are taken from the parameters:
34  *  \param file File name of the buffer.
35  *  \param positional If the sfx is positional.
36  *  \param rolloff Rolloff value of this sfx.
37  *  \param max_dist Maximum distance the sfx can be heard.
38  *  \param gain Gain value of this sfx.
39  */
SFXBuffer(const std::string & file,bool positional,float rolloff,float max_dist,float gain)40 SFXBuffer::SFXBuffer(const std::string& file,
41                      bool  positional,
42                      float rolloff,
43                      float max_dist,
44                      float gain)
45 {
46     m_buffer      = 0;
47     m_gain        = 1.0f;
48     m_rolloff     = 0.1f;
49     m_loaded      = false;
50     m_max_dist    = max_dist;
51     m_duration    = -1.0f;
52     m_file        = file;
53 
54     m_rolloff     = rolloff;
55     m_positional  = positional;
56     m_gain        = gain;
57 }   // SFXBuffer
58 
59 //----------------------------------------------------------------------------
60 /** Constructor getting the sfx parameters from an XML node.
61  *  \param file File name of the data.
62  *  \param node XML Node with the data for this sfx.
63  */
SFXBuffer(const std::string & file,const XMLNode * node)64 SFXBuffer::SFXBuffer(const std::string& file,
65                      const XMLNode* node)
66 {
67     m_buffer      = 0;
68     m_gain        = 1.0f;
69     m_rolloff     = 0.1f;
70     m_max_dist    = 300.0f;
71     m_duration    = -1.0f;
72     m_positional  = false;
73     m_loaded      = false;
74     m_file        = file;
75 
76     node->get("rolloff",     &m_rolloff    );
77     node->get("positional",  &m_positional );
78     node->get("volume",      &m_gain       );
79     node->get("max_dist",    &m_max_dist   );
80     node->get("duration",    &m_duration   );
81 }   // SFXBuffer(XMLNode)
82 
83 //----------------------------------------------------------------------------
84 /** \brief load the buffer from file into OpenAL.
85  *  \note If this buffer is already loaded, this call does nothing and
86   *       returns false.
87  *  \return Whether loading was successful.
88  */
load()89 bool SFXBuffer::load()
90 {
91     if (UserConfigParams::m_sfx == false) return false;
92 
93 #ifdef ENABLE_SOUND
94     if (UserConfigParams::m_enable_sound)
95     {
96         if (m_loaded) return false;
97 
98         alGetError(); // clear errors from previously
99 
100         alGenBuffers(1, &m_buffer);
101         if (!SFXManager::checkError("generating a buffer"))
102         {
103             return false;
104         }
105 
106         assert(alIsBuffer(m_buffer));
107 
108         if (!loadVorbisBuffer(m_file, m_buffer))
109         {
110             Log::error("SFXBuffer", "Could not load sound effect %s",
111                        m_file.c_str());
112             // TODO: free al buffer here?
113             return false;
114         }
115     }
116 #endif
117 
118     m_loaded = true;
119     return true;
120 }   // load
121 
122 //----------------------------------------------------------------------------
123 /** \brief Frees the loaded buffer.
124  *  Cannot appear in destructor because copy-constructors may be used,
125  *  and the OpenAL source must not be deleted on a copy
126  */
127 
unload()128 void SFXBuffer::unload()
129 {
130 #ifdef ENABLE_SOUND
131     if (UserConfigParams::m_enable_sound)
132     {
133         if (m_loaded)
134         {
135             alDeleteBuffers(1, &m_buffer);
136             m_buffer = 0;
137         }
138     }
139 #endif
140     m_loaded = false;
141 }   // unload
142 
143 //----------------------------------------------------------------------------
144 /** Load a vorbis file into an OpenAL buffer
145  *  based on a routine by Peter Mulholland, used with permission (quote :
146  *  "Feel free to use")
147  */
loadVorbisBuffer(const std::string & name,ALuint buffer)148 bool SFXBuffer::loadVorbisBuffer(const std::string &name, ALuint buffer)
149 {
150 #ifdef ENABLE_SOUND
151     if (!UserConfigParams::m_enable_sound)
152         return false;
153 
154     const int ogg_endianness = (IS_LITTLE_ENDIAN ? 0 : 1);
155 
156 
157     bool success = false;
158     FILE *file;
159     vorbis_info *info;
160     OggVorbis_File oggFile;
161 
162     if (alIsBuffer(buffer) == AL_FALSE)
163     {
164         Log::error("SFXBuffer", "Error, bad OpenAL buffer");
165         return false;
166     }
167 
168     file = FileUtils::fopenU8Path(name, "rb");
169 
170     if(!file)
171     {
172         Log::error("SFXBuffer", "LoadVorbisBuffer() - couldn't open file!");
173         return false;
174     }
175 
176     if (ov_open_callbacks(file, &oggFile, NULL, 0,  OV_CALLBACKS_NOCLOSE) != 0)
177     {
178         fclose(file);
179         Log::error("SFXBuffer", "LoadVorbisBuffer() - ov_open_callbacks() failed, "
180                                 "file isn't vorbis?");
181         return false;
182     }
183 
184     info = ov_info(&oggFile, -1);
185 
186     // always 16 bit data
187     long len = (long)ov_pcm_total(&oggFile, -1) * info->channels * 2;
188 
189     std::unique_ptr<char []> data = std::unique_ptr<char []>(new char[len]);
190 
191     int bs = -1;
192     long todo = len;
193     char *bufpt = data.get();
194 
195     while (todo)
196     {
197         int read = ov_read(&oggFile, bufpt, todo, ogg_endianness, 2, 1, &bs);
198         todo -= read;
199         bufpt += read;
200     }
201 
202     alBufferData(buffer, (info->channels == 1) ? AL_FORMAT_MONO16
203                  : AL_FORMAT_STEREO16,
204                  data.get(), len, info->rate);
205     success = true;
206 
207     ov_clear(&oggFile);
208     fclose(file);
209 
210     // Allow the xml data to overwrite the duration, but if there is no
211     // duration (which is the norm), compute it:
212     if(m_duration < 0)
213     {
214         ALint buffer_size, frequency, bits_per_sample, channels;
215         alGetBufferi(buffer, AL_SIZE,      &buffer_size    );
216         alGetBufferi(buffer, AL_FREQUENCY, &frequency      );
217         alGetBufferi(buffer, AL_CHANNELS,  &channels       );
218         alGetBufferi(buffer, AL_BITS,      &bits_per_sample);
219         m_duration = float(buffer_size)
220                    / (frequency*channels*(bits_per_sample / 8));
221     }
222     return success;
223 #else
224     return false;
225 #endif
226 }   // loadVorbisBuffer
227 
228