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