stk-code_catmod/src/audio/sfx_buffer.cpp
Benau 8e885d15ac Calculate duration from the original buffer size
MojoAL use SDL_AudioCVT internally which doubles the size
2021-05-14 23:58:59 +08:00

230 lines
6.5 KiB
C++

//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2010-2015 Marianne Gagnon
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "audio/sfx_buffer.hpp"
#include "audio/sfx_manager.hpp"
#include "config/user_config.hpp"
#include "io/file_manager.hpp"
#include "utils/constants.hpp"
#include "utils/file_utils.hpp"
#include "utils/log.hpp"
#ifdef ENABLE_SOUND
# include <vorbis/codec.h>
# include <vorbis/vorbisfile.h>
#endif
//----------------------------------------------------------------------------
/** Creates a sfx. The parameter are taken from the parameters:
* \param file File name of the buffer.
* \param positional If the sfx is positional.
* \param rolloff Rolloff value of this sfx.
* \param max_dist Maximum distance the sfx can be heard.
* \param gain Gain value of this sfx.
*/
SFXBuffer::SFXBuffer(const std::string& file,
bool positional,
float rolloff,
float max_dist,
float gain)
{
m_buffer = 0;
m_gain = 1.0f;
m_rolloff = 0.1f;
m_loaded = false;
m_max_dist = max_dist;
m_duration = -1.0f;
m_file = file;
m_rolloff = rolloff;
m_positional = positional;
m_gain = gain;
} // SFXBuffer
//----------------------------------------------------------------------------
/** Constructor getting the sfx parameters from an XML node.
* \param file File name of the data.
* \param node XML Node with the data for this sfx.
*/
SFXBuffer::SFXBuffer(const std::string& file,
const XMLNode* node)
{
m_buffer = 0;
m_gain = 1.0f;
m_rolloff = 0.1f;
m_max_dist = 300.0f;
m_duration = -1.0f;
m_positional = false;
m_loaded = false;
m_file = file;
node->get("rolloff", &m_rolloff );
node->get("positional", &m_positional );
node->get("volume", &m_gain );
node->get("max_dist", &m_max_dist );
node->get("duration", &m_duration );
} // SFXBuffer(XMLNode)
//----------------------------------------------------------------------------
/** \brief load the buffer from file into OpenAL.
* \note If this buffer is already loaded, this call does nothing and
* returns false.
* \return Whether loading was successful.
*/
bool SFXBuffer::load()
{
if (UserConfigParams::m_sfx == false) return false;
#ifdef ENABLE_SOUND
if (UserConfigParams::m_enable_sound)
{
if (m_loaded) return false;
alGetError(); // clear errors from previously
alGenBuffers(1, &m_buffer);
if (!SFXManager::checkError("generating a buffer"))
{
return false;
}
assert(alIsBuffer(m_buffer));
if (!loadVorbisBuffer(m_file, m_buffer))
{
Log::error("SFXBuffer", "Could not load sound effect %s",
m_file.c_str());
// TODO: free al buffer here?
return false;
}
}
#endif
m_loaded = true;
return true;
} // load
//----------------------------------------------------------------------------
/** \brief Frees the loaded buffer.
* Cannot appear in destructor because copy-constructors may be used,
* and the OpenAL source must not be deleted on a copy
*/
void SFXBuffer::unload()
{
#ifdef ENABLE_SOUND
if (UserConfigParams::m_enable_sound)
{
if (m_loaded)
{
alDeleteBuffers(1, &m_buffer);
m_buffer = 0;
}
}
#endif
m_loaded = false;
} // unload
//----------------------------------------------------------------------------
/** Load a vorbis file into an OpenAL buffer
* based on a routine by Peter Mulholland, used with permission (quote :
* "Feel free to use")
*/
bool SFXBuffer::loadVorbisBuffer(const std::string &name, ALuint buffer)
{
#ifdef ENABLE_SOUND
if (!UserConfigParams::m_enable_sound)
return false;
const int ogg_endianness = (IS_LITTLE_ENDIAN ? 0 : 1);
bool success = false;
FILE *file;
vorbis_info *info;
OggVorbis_File oggFile;
if (alIsBuffer(buffer) == AL_FALSE)
{
Log::error("SFXBuffer", "Error, bad OpenAL buffer");
return false;
}
file = FileUtils::fopenU8Path(name, "rb");
if(!file)
{
Log::error("SFXBuffer", "LoadVorbisBuffer() - couldn't open file!");
return false;
}
if (ov_open_callbacks(file, &oggFile, NULL, 0, OV_CALLBACKS_NOCLOSE) != 0)
{
fclose(file);
Log::error("SFXBuffer", "LoadVorbisBuffer() - ov_open_callbacks() failed, "
"file isn't vorbis?");
return false;
}
info = ov_info(&oggFile, -1);
// always 16 bit data
long len = (long)ov_pcm_total(&oggFile, -1) * info->channels * 2;
std::unique_ptr<char []> data = std::unique_ptr<char []>(new char[len]);
int bs = -1;
long todo = len;
char *bufpt = data.get();
while (todo)
{
int read = ov_read(&oggFile, bufpt, todo, ogg_endianness, 2, 1, &bs);
todo -= read;
bufpt += read;
}
alBufferData(buffer, (info->channels == 1) ? AL_FORMAT_MONO16
: AL_FORMAT_STEREO16,
data.get(), len, info->rate);
success = true;
int buffer_size, frequency, bits_per_sample, channels;
buffer_size = len;
frequency = info->rate;
// We use AL_FORMAT_MONO16 or AL_FORMAT_STEREO16 so it's always 16
bits_per_sample = 16;
channels = info->channels;
ov_clear(&oggFile);
fclose(file);
// Allow the xml data to overwrite the duration, but if there is no
// duration (which is the norm), compute it:
if(m_duration < 0)
{
m_duration = float(buffer_size)
/ (frequency*channels*(bits_per_sample / 8));
}
return success;
#else
return false;
#endif
} // loadVorbisBuffer