mirror of
https://github.com/rwengine/openrw.git
synced 2024-11-09 12:22:34 +01:00
Merge pull request #111 from tsjost/fix/audio
Replace SFML Audio with libsndfile + OpenAL
This commit is contained in:
commit
42c847cfab
@ -65,10 +65,12 @@ IF(APPLE)
|
||||
ENDIF()
|
||||
|
||||
find_package(OpenGL REQUIRED)
|
||||
find_package(OpenAL REQUIRED)
|
||||
find_package(Bullet REQUIRED)
|
||||
find_package(SFML 2 COMPONENTS system window audio graphics network REQUIRED)
|
||||
find_package(SFML 2 COMPONENTS system window graphics network REQUIRED)
|
||||
find_package(MAD REQUIRED)
|
||||
find_package(GLM REQUIRED)
|
||||
find_package(LibSndFile REQUIRED)
|
||||
|
||||
include_directories("${GLM_INCLUDE_DIRS}")
|
||||
|
||||
|
34
cmake/modules/FindLibSndFile.cmake
Normal file
34
cmake/modules/FindLibSndFile.cmake
Normal file
@ -0,0 +1,34 @@
|
||||
# - Try to find libsndfile
|
||||
# Once done, this will define
|
||||
#
|
||||
# LIBSNDFILE_FOUND - system has libsndfile
|
||||
# LIBSNDFILE_INCLUDE_DIRS - the libsndfile include directories
|
||||
# LIBSNDFILE_LIBRARIES - link these to use libsndfile
|
||||
|
||||
# Use pkg-config to get hints about paths
|
||||
find_package(PkgConfig QUIET)
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(LIBSNDFILE_PKGCONF sndfile)
|
||||
endif(PKG_CONFIG_FOUND)
|
||||
|
||||
# Include dir
|
||||
find_path(LIBSNDFILE_INCLUDE_DIR
|
||||
NAMES sndfile.h
|
||||
PATHS ${LIBSNDFILE_PKGCONF_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
# Library
|
||||
find_library(LIBSNDFILE_LIBRARY
|
||||
NAMES sndfile libsndfile-1
|
||||
PATHS ${LIBSNDFILE_PKGCONF_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
find_package(PackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(LibSndFile DEFAULT_MSG LIBSNDFILE_LIBRARY LIBSNDFILE_INCLUDE_DIR)
|
||||
|
||||
if(LIBSNDFILE_FOUND)
|
||||
set(LIBSNDFILE_LIBRARIES ${LIBSNDFILE_LIBRARY})
|
||||
set(LIBSNDFILE_INCLUDE_DIRS ${LIBSNDFILE_INCLUDE_DIR})
|
||||
endif(LIBSNDFILE_FOUND)
|
||||
|
||||
mark_as_advanced(LIBSNDFILE_LIBRARY LIBSNDFILE_LIBRARIES LIBSNDFILE_INCLUDE_DIR LIBSNDFILE_INCLUDE_DIRS)
|
@ -13,12 +13,16 @@ target_link_libraries(rwengine
|
||||
rwlib
|
||||
${MAD_LIBRARY}
|
||||
${SFML_LIBRARIES}
|
||||
${LIBSNDFILE_LIBRARY}
|
||||
${OPENAL_LIBRARY}
|
||||
${OPENRW_PLATFORM_LIBS})
|
||||
|
||||
include_directories(SYSTEM
|
||||
${BULLET_INCLUDE_DIR}
|
||||
${MAD_INCLUDE_DIR}
|
||||
${SFML_INCLUDE_DIR}
|
||||
${LIBSNDFILE_INCLUDE_DIR}
|
||||
${OPENAL_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
include_directories(
|
||||
|
@ -7,30 +7,17 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <SFML/Audio.hpp>
|
||||
#include <stdint.h>
|
||||
#include <iostream>
|
||||
#include <rw/defines.hpp>
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
#include "audio/alCheck.hpp"
|
||||
|
||||
static inline
|
||||
signed int scale(mad_fixed_t sample)
|
||||
{
|
||||
/* round */
|
||||
sample += (1L << (MAD_F_FRACBITS - 16));
|
||||
|
||||
/* clip */
|
||||
if (sample >= MAD_F_ONE)
|
||||
sample = MAD_F_ONE - 1;
|
||||
else if (sample < -MAD_F_ONE)
|
||||
sample = -MAD_F_ONE;
|
||||
|
||||
/* quantize */
|
||||
return sample >> (MAD_F_FRACBITS + 1 - 16);
|
||||
}
|
||||
|
||||
#include <vector>
|
||||
|
||||
class MADStream : public sf::SoundStream
|
||||
class MADStream
|
||||
{
|
||||
mad_decoder mDecoder;
|
||||
unsigned int mMadSampleRate;
|
||||
@ -40,131 +27,34 @@ class MADStream : public sf::SoundStream
|
||||
unsigned int mReadProgress;
|
||||
std::vector<int16_t> mCurrentSamples;
|
||||
|
||||
static mad_flow ms_header(void* user, mad_header const* header)
|
||||
{
|
||||
MADStream* stream = static_cast<MADStream*>(user);
|
||||
/**
|
||||
* The number of OpenAL buffers is arbitrary, but due to the kind of small
|
||||
* buffer/audio sample size, we need a bunch of them so the computer can
|
||||
* keep up with filling them.
|
||||
*/
|
||||
constexpr static size_t numALbuffers = 8;
|
||||
ALuint buffers[numALbuffers];
|
||||
ALuint unqueuedBuffers[numALbuffers];
|
||||
size_t numFreeBuffers = numALbuffers;
|
||||
size_t currentBuffer = 0;
|
||||
ALuint alSource;
|
||||
|
||||
stream->mMadSampleRate = header->samplerate;
|
||||
// see enum mad_mode
|
||||
stream->mMadChannels = header->mode + 1;
|
||||
bool stopped = false;
|
||||
|
||||
return MAD_FLOW_CONTINUE;
|
||||
}
|
||||
|
||||
static mad_flow ms_input(void* user, mad_stream* stream)
|
||||
{
|
||||
MADStream* self = static_cast<MADStream*>(user);
|
||||
|
||||
if(! self->mReadProgress ) {
|
||||
return MAD_FLOW_STOP;
|
||||
}
|
||||
|
||||
auto rd = self->mReadProgress;
|
||||
self->mReadProgress = 0;
|
||||
|
||||
mad_stream_buffer(stream, self->mFdm, rd);
|
||||
|
||||
return MAD_FLOW_CONTINUE;
|
||||
}
|
||||
|
||||
static mad_flow ms_output(void* user, mad_header const* header,
|
||||
mad_pcm* pcm)
|
||||
{
|
||||
RW_UNUSED(header);
|
||||
|
||||
MADStream* self = static_cast<MADStream*>(user);
|
||||
|
||||
int nsamples = pcm->length;
|
||||
mad_fixed_t const *left, *right;
|
||||
|
||||
left = pcm->samples[0];
|
||||
right = pcm->samples[1];
|
||||
|
||||
int s = 0;
|
||||
while( (nsamples) -- ) {
|
||||
signed int sample = *left++;
|
||||
self->mCurrentSamples.push_back(scale(sample));
|
||||
|
||||
sample = *right++;
|
||||
self->mCurrentSamples.push_back(scale(sample));
|
||||
s++;
|
||||
}
|
||||
|
||||
return MAD_FLOW_CONTINUE;
|
||||
}
|
||||
|
||||
static mad_flow ms_error(void* user, mad_stream* stream, mad_frame* frame)
|
||||
{
|
||||
RW_UNUSED(user);
|
||||
RW_UNUSED(frame);
|
||||
|
||||
sf::err() << "libmad error: " << mad_stream_errorstr(stream);
|
||||
return MAD_FLOW_BREAK;
|
||||
}
|
||||
|
||||
virtual bool onGetData(sf::SoundStream::Chunk& data)
|
||||
{
|
||||
data.samples = mCurrentSamples.data();
|
||||
data.sampleCount = mCurrentSamples.size();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void onSeek(sf::Time timeOffset)
|
||||
{
|
||||
RW_UNUSED(timeOffset);
|
||||
/// @todo support seeking.
|
||||
}
|
||||
static inline signed int scale(mad_fixed_t sample);
|
||||
static mad_flow ms_header(void* user, mad_header const* header);
|
||||
static mad_flow ms_input(void* user, mad_stream* stream);
|
||||
static mad_flow ms_output(void* user, mad_header const* header, mad_pcm* pcm);
|
||||
static mad_flow ms_error(void* user, mad_stream* stream, mad_frame* frame);
|
||||
|
||||
public:
|
||||
|
||||
MADStream()
|
||||
: mFdm(nullptr)
|
||||
{
|
||||
MADStream();
|
||||
~MADStream();
|
||||
|
||||
}
|
||||
|
||||
~MADStream()
|
||||
{
|
||||
if( mFdm )
|
||||
{
|
||||
munmap( mFdm, mStat.st_size );
|
||||
mad_decoder_finish(&mDecoder);
|
||||
}
|
||||
}
|
||||
|
||||
bool openFromFile(const std::string& loc)
|
||||
{
|
||||
if( mFdm ) {
|
||||
munmap( mFdm, mStat.st_size );
|
||||
mCurrentSamples.clear();
|
||||
mad_decoder_finish(&mDecoder);
|
||||
}
|
||||
|
||||
int fd = ::open(loc.c_str(), O_RDONLY);
|
||||
|
||||
if( fstat(fd, &mStat) == -1 || mStat.st_size == 0) {
|
||||
std::cerr << "Fstat failed (" << loc << ")" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
void* m = mmap(0, mStat.st_size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
if( m == MAP_FAILED ) {
|
||||
std::cerr << "mmap failed (" << loc << ")" << std::endl;
|
||||
return false;
|
||||
}
|
||||
mFdm = (unsigned char*)m;
|
||||
mReadProgress = mStat.st_size;
|
||||
|
||||
mad_decoder_init(&mDecoder, this,
|
||||
ms_input, ms_header, 0, ms_output, ms_error, 0);
|
||||
|
||||
mad_decoder_run(&mDecoder, MAD_DECODER_MODE_SYNC);
|
||||
|
||||
this->initialize(2, mMadSampleRate);
|
||||
|
||||
return true;
|
||||
}
|
||||
bool openFromFile(const std::string& loc);
|
||||
void play();
|
||||
void stop();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1,28 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include <sndfile.h>
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <SFML/Audio.hpp>
|
||||
|
||||
class MADStream;
|
||||
|
||||
class SoundManager
|
||||
{
|
||||
public:
|
||||
SoundManager();
|
||||
|
||||
void playSound(const std::string& fileName);
|
||||
bool loadSound(const std::string& name, const std::string& fileName);
|
||||
bool isLoaded(const std::string& name);
|
||||
void playSound(const std::string& name);
|
||||
void pauseSound(const std::string& name);
|
||||
bool isPlaying(const std::string& name);
|
||||
|
||||
bool playBackground(const std::string& fileName);
|
||||
|
||||
bool loadMusic(const std::string& name, const std::string& fileName);
|
||||
void playMusic(const std::string& name);
|
||||
void stopMusic(const std::string& name);
|
||||
|
||||
void pause(bool p);
|
||||
|
||||
private:
|
||||
|
||||
struct PlayingSound
|
||||
class SoundSource
|
||||
{
|
||||
sf::Sound sound;
|
||||
sf::SoundBuffer buffer;
|
||||
friend class SoundManager;
|
||||
friend class SoundBuffer;
|
||||
public:
|
||||
void loadFromFile(const std::string& filename);
|
||||
private:
|
||||
SF_INFO fileInfo;
|
||||
SNDFILE* file;
|
||||
std::vector<uint16_t> data;
|
||||
};
|
||||
|
||||
std::vector<PlayingSound> sounds;
|
||||
|
||||
sf::SoundStream* backgroundNoise;
|
||||
class SoundBuffer
|
||||
{
|
||||
friend class SoundManager;
|
||||
public:
|
||||
SoundBuffer();
|
||||
bool bufferData(SoundSource& soundSource);
|
||||
private:
|
||||
ALuint source;
|
||||
ALuint buffer;
|
||||
};
|
||||
|
||||
};
|
||||
struct Sound
|
||||
{
|
||||
SoundSource source;
|
||||
SoundBuffer buffer;
|
||||
bool isLoaded = false;
|
||||
};
|
||||
|
||||
bool initializeOpenAL();
|
||||
|
||||
ALCcontext* alContext;
|
||||
ALCdevice* alDevice;
|
||||
|
||||
std::map<std::string, Sound> sounds;
|
||||
std::map<std::string, MADStream> musics;
|
||||
std::string backgroundNoise;
|
||||
};
|
||||
|
11
rwengine/include/audio/alCheck.hpp
Normal file
11
rwengine/include/audio/alCheck.hpp
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
void checkALerror(const std::string& file, unsigned int line);
|
||||
|
||||
#if RW_DEBUG
|
||||
#define alCheck(stmt) do { stmt; checkALerror(__FILE__, __LINE__); } while(0)
|
||||
#else
|
||||
#define alCheck(stmt) stmt
|
||||
#endif
|
@ -148,7 +148,7 @@ public:
|
||||
void loadWeaponDAT(const std::string& name);
|
||||
|
||||
bool loadAudioStream(const std::string& name);
|
||||
bool loadAudioClip(const std::string& name);
|
||||
bool loadAudioClip(const std::string& name, const std::string& fileName);
|
||||
|
||||
void loadSplash(const std::string& name);
|
||||
|
||||
|
@ -306,10 +306,9 @@ public:
|
||||
void clearCutscene();
|
||||
bool isCutsceneDone();
|
||||
|
||||
sf::SoundStream* cutsceneAudio;
|
||||
std::string cutsceneAudio;
|
||||
bool cutsceneAudioLoaded;
|
||||
sf::SoundBuffer* missionAudio;
|
||||
sf::Sound missionSound;
|
||||
std::string missionAudio;
|
||||
|
||||
/**
|
||||
* @brief loads a model into a special character slot.
|
||||
|
174
rwengine/src/audio/MADStream.cpp
Normal file
174
rwengine/src/audio/MADStream.cpp
Normal file
@ -0,0 +1,174 @@
|
||||
#include "audio/MADStream.hpp"
|
||||
|
||||
#include <thread>
|
||||
|
||||
inline signed int MADStream::scale(mad_fixed_t sample)
|
||||
{
|
||||
/* round */
|
||||
sample += (1L << (MAD_F_FRACBITS - 16));
|
||||
|
||||
/* clip */
|
||||
if (sample >= MAD_F_ONE) {
|
||||
sample = MAD_F_ONE - 1;
|
||||
} else if (sample < -MAD_F_ONE) {
|
||||
sample = -MAD_F_ONE;
|
||||
}
|
||||
|
||||
/* quantize */
|
||||
return sample >> (MAD_F_FRACBITS + 1 - 16);
|
||||
}
|
||||
|
||||
mad_flow MADStream::ms_header(void* user, mad_header const* header)
|
||||
{
|
||||
MADStream* stream = static_cast<MADStream*>(user);
|
||||
|
||||
stream->mMadSampleRate = header->samplerate;
|
||||
// see enum mad_mode
|
||||
stream->mMadChannels = header->mode + 1;
|
||||
|
||||
return MAD_FLOW_CONTINUE;
|
||||
}
|
||||
|
||||
mad_flow MADStream::ms_input(void* user, mad_stream* stream)
|
||||
{
|
||||
MADStream* self = static_cast<MADStream*>(user);
|
||||
|
||||
if ( ! self->mReadProgress ) {
|
||||
return MAD_FLOW_STOP;
|
||||
}
|
||||
|
||||
auto rd = self->mReadProgress;
|
||||
self->mReadProgress = 0;
|
||||
|
||||
mad_stream_buffer(stream, self->mFdm, rd);
|
||||
|
||||
return MAD_FLOW_CONTINUE;
|
||||
}
|
||||
|
||||
mad_flow MADStream::ms_output(void* user, mad_header const* header, mad_pcm* pcm)
|
||||
{
|
||||
RW_UNUSED(header);
|
||||
|
||||
MADStream* self = static_cast<MADStream*>(user);
|
||||
|
||||
if (self->stopped) {
|
||||
return MAD_FLOW_STOP;
|
||||
}
|
||||
|
||||
if ( ! self->numFreeBuffers) {
|
||||
ALint buffersProcessed;
|
||||
do {
|
||||
/**
|
||||
* Sleep a bit while waiting for OpenAL buffers to become available.
|
||||
* The number is arbitrary and depends on the size of the buffer/audio samples,
|
||||
* as well as how quickly the computer can feed more buffers into OpenAL.
|
||||
*/
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
alGetSourcei(self->alSource, AL_BUFFERS_PROCESSED, &buffersProcessed);
|
||||
} while (buffersProcessed <= 0);
|
||||
|
||||
alCheck(alSourceUnqueueBuffers(self->alSource, buffersProcessed, self->unqueuedBuffers));
|
||||
self->numFreeBuffers += buffersProcessed;
|
||||
}
|
||||
|
||||
int nsamples = pcm->length;
|
||||
mad_fixed_t const *left, *right;
|
||||
|
||||
left = pcm->samples[0];
|
||||
right = pcm->samples[1];
|
||||
|
||||
int s = 0;
|
||||
while (nsamples--) {
|
||||
signed int sample = *left++;
|
||||
self->mCurrentSamples.push_back(scale(sample));
|
||||
|
||||
sample = *right++;
|
||||
self->mCurrentSamples.push_back(scale(sample));
|
||||
s++;
|
||||
}
|
||||
|
||||
alCheck(alBufferData(self->buffers[self->currentBuffer], AL_FORMAT_STEREO16, self->mCurrentSamples.data(), self->mCurrentSamples.size() * sizeof(uint16_t), pcm->samplerate));
|
||||
alCheck(alSourceQueueBuffers(self->alSource, 1, self->buffers + self->currentBuffer));
|
||||
|
||||
self->mCurrentSamples.clear();
|
||||
self->currentBuffer++;
|
||||
self->currentBuffer %= numALbuffers;
|
||||
self->numFreeBuffers--;
|
||||
|
||||
return MAD_FLOW_CONTINUE;
|
||||
}
|
||||
|
||||
mad_flow MADStream::ms_error(void* user, mad_stream* stream, mad_frame* frame)
|
||||
{
|
||||
RW_UNUSED(user);
|
||||
RW_UNUSED(frame);
|
||||
|
||||
std::cerr << "libmad error: " << mad_stream_errorstr(stream) << std::endl;
|
||||
return MAD_FLOW_BREAK;
|
||||
}
|
||||
|
||||
MADStream::MADStream()
|
||||
: mFdm(nullptr)
|
||||
{
|
||||
alCheck(alGenBuffers(numALbuffers, buffers));
|
||||
alCheck(alGenSources(1, &alSource));
|
||||
}
|
||||
|
||||
MADStream::~MADStream()
|
||||
{
|
||||
if (mFdm) {
|
||||
munmap(mFdm, mStat.st_size);
|
||||
mad_decoder_finish(&mDecoder);
|
||||
}
|
||||
}
|
||||
|
||||
bool MADStream::openFromFile(const std::string& loc)
|
||||
{
|
||||
if (mFdm) {
|
||||
munmap(mFdm, mStat.st_size);
|
||||
mCurrentSamples.clear();
|
||||
mad_decoder_finish(&mDecoder);
|
||||
}
|
||||
|
||||
int fd = ::open(loc.c_str(), O_RDONLY);
|
||||
|
||||
if (fstat(fd, &mStat) == -1 || mStat.st_size == 0) {
|
||||
std::cerr << "Fstat failed (" << loc << ")" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
void* m = mmap(0, mStat.st_size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
if (m == MAP_FAILED) {
|
||||
std::cerr << "mmap failed (" << loc << ")" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
mFdm = (unsigned char*) m;
|
||||
mReadProgress = mStat.st_size;
|
||||
|
||||
mad_decoder_init(&mDecoder, this,
|
||||
ms_input, ms_header, 0, ms_output, ms_error, 0);
|
||||
|
||||
new std::thread([&] () {
|
||||
mad_decoder_run(&mDecoder, MAD_DECODER_MODE_SYNC);
|
||||
});
|
||||
|
||||
alCheck(alSourcef(alSource, AL_PITCH, 1));
|
||||
alCheck(alSourcef(alSource, AL_GAIN, 1));
|
||||
alCheck(alSource3f(alSource, AL_POSITION, 0, 0, 0));
|
||||
alCheck(alSource3f(alSource, AL_VELOCITY, 0, 0, 0));
|
||||
alCheck(alSourcei(alSource, AL_LOOPING, AL_FALSE));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MADStream::play()
|
||||
{
|
||||
alCheck(alSourcePlay(alSource));
|
||||
}
|
||||
|
||||
void MADStream::stop()
|
||||
{
|
||||
stopped = true;
|
||||
alCheck(alSourcePlay(alSource));
|
||||
}
|
@ -1,44 +1,177 @@
|
||||
#include <audio/SoundManager.hpp>
|
||||
#include <audio/MADStream.hpp>
|
||||
|
||||
SoundManager::SoundManager()
|
||||
: backgroundNoise(nullptr)
|
||||
{
|
||||
#include "audio/alCheck.hpp"
|
||||
#include "audio/MADStream.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
|
||||
void SoundManager::SoundSource::loadFromFile(const std::string& filename)
|
||||
{
|
||||
fileInfo.format = 0;
|
||||
file = sf_open(filename.c_str(), SFM_READ, &fileInfo);
|
||||
|
||||
if (file) {
|
||||
size_t numRead = 0;
|
||||
std::array<int16_t, 4096> readBuffer;
|
||||
|
||||
while ((numRead = sf_read_short(file, readBuffer.data(), readBuffer.size())) != 0) {
|
||||
data.insert(data.end(), readBuffer.begin(), readBuffer.begin() + numRead);
|
||||
}
|
||||
} else {
|
||||
std::cerr << "Error opening sound file \"" << filename << "\": " << sf_strerror(file) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
SoundManager::SoundBuffer::SoundBuffer()
|
||||
{
|
||||
alCheck(alGenSources(1, &source));
|
||||
alCheck(alGenBuffers(1, &buffer));
|
||||
|
||||
alCheck(alSourcef(source, AL_PITCH, 1));
|
||||
alCheck(alSourcef(source, AL_GAIN, 1));
|
||||
alCheck(alSource3f(source, AL_POSITION, 0, 0, 0));
|
||||
alCheck(alSource3f(source, AL_VELOCITY, 0, 0, 0));
|
||||
alCheck(alSourcei(source, AL_LOOPING, AL_FALSE));
|
||||
}
|
||||
|
||||
bool SoundManager::SoundBuffer::bufferData(SoundSource& soundSource)
|
||||
{
|
||||
alCheck(alBufferData(
|
||||
buffer,
|
||||
soundSource.fileInfo.channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16,
|
||||
&soundSource.data.front(),
|
||||
soundSource.data.size() * sizeof(uint16_t),
|
||||
soundSource.fileInfo.samplerate
|
||||
));
|
||||
alCheck(alSourcei(source, AL_BUFFER, buffer));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SoundManager::SoundManager()
|
||||
{
|
||||
initializeOpenAL();
|
||||
}
|
||||
|
||||
bool SoundManager::initializeOpenAL()
|
||||
{
|
||||
alDevice = alcOpenDevice(NULL);
|
||||
if ( ! alDevice) {
|
||||
std::cerr << "Could not find OpenAL device!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
alContext = alcCreateContext(alDevice, NULL);
|
||||
if ( ! alContext) {
|
||||
std::cerr << "Could not create OpenAL context!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! alcMakeContextCurrent(alContext)) {
|
||||
std::cerr << "Unable to make OpenAL context current!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SoundManager::loadSound(const std::string& name, const std::string& fileName)
|
||||
{
|
||||
Sound* sound = nullptr;
|
||||
auto sound_iter = sounds.find(name);
|
||||
|
||||
if (sound_iter != sounds.end()) {
|
||||
sound = &sound_iter->second;
|
||||
} else {
|
||||
auto emplaced = sounds.emplace(std::piecewise_construct, std::forward_as_tuple(name), std::forward_as_tuple());
|
||||
sound = &emplaced.first->second;
|
||||
|
||||
sound->source.loadFromFile(fileName);
|
||||
sound->isLoaded = sound->buffer.bufferData(sound->source);
|
||||
}
|
||||
|
||||
return sound->isLoaded;
|
||||
}
|
||||
bool SoundManager::isLoaded(const std::string& name)
|
||||
{
|
||||
if (sounds.find(name) != sounds.end()) {
|
||||
return sounds[name].isLoaded;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
void SoundManager::playSound(const std::string& name)
|
||||
{
|
||||
if (sounds.find(name) != sounds.end()) {
|
||||
alCheck(alSourcePlay(sounds[name].buffer.source));
|
||||
}
|
||||
}
|
||||
void SoundManager::pauseSound(const std::string& name)
|
||||
{
|
||||
if (sounds.find(name) != sounds.end()) {
|
||||
alCheck(alSourcePause(sounds[name].buffer.source));
|
||||
}
|
||||
}
|
||||
bool SoundManager::isPlaying(const std::string& name)
|
||||
{
|
||||
if (sounds.find(name) != sounds.end()) {
|
||||
ALint sourceState;
|
||||
alCheck(alGetSourcei(sounds[name].buffer.source, AL_SOURCE_STATE, &sourceState));
|
||||
return AL_PLAYING == sourceState;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SoundManager::playBackground(const std::string& fileName)
|
||||
{
|
||||
if( backgroundNoise )
|
||||
{
|
||||
delete backgroundNoise;
|
||||
}
|
||||
|
||||
sf::Music* bg = new sf::Music;
|
||||
|
||||
if( bg->openFromFile( fileName ) )
|
||||
{
|
||||
backgroundNoise = bg;
|
||||
backgroundNoise->setLoop(true);
|
||||
bg->play();
|
||||
if (this->loadSound(fileName, fileName)) {
|
||||
backgroundNoise = fileName;
|
||||
this->playSound(fileName);
|
||||
return true;
|
||||
}
|
||||
|
||||
delete bg;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SoundManager::loadMusic(const std::string& name, const std::string& fileName)
|
||||
{
|
||||
MADStream* music = nullptr;
|
||||
auto music_iter = musics.find(name);
|
||||
|
||||
if (music_iter != musics.end()) {
|
||||
music = &music_iter->second;
|
||||
} else {
|
||||
auto emplaced = musics.emplace(std::piecewise_construct, std::forward_as_tuple(name), std::forward_as_tuple());
|
||||
music = &emplaced.first->second;
|
||||
}
|
||||
|
||||
return music->openFromFile(fileName);
|
||||
}
|
||||
void SoundManager::playMusic(const std::string& name)
|
||||
{
|
||||
auto music = musics.find(name);
|
||||
if (music != musics.end()) {
|
||||
music->second.play();
|
||||
}
|
||||
}
|
||||
void SoundManager::stopMusic(const std::string& name)
|
||||
{
|
||||
auto music = musics.find(name);
|
||||
if (music != musics.end()) {
|
||||
music->second.stop();
|
||||
}
|
||||
}
|
||||
|
||||
void SoundManager::pause(bool p)
|
||||
{
|
||||
if( backgroundNoise )
|
||||
{
|
||||
if( p )
|
||||
{
|
||||
backgroundNoise->pause();
|
||||
}
|
||||
else
|
||||
{
|
||||
backgroundNoise->play();
|
||||
if (backgroundNoise.length() > 0) {
|
||||
if (p) {
|
||||
pauseSound(backgroundNoise);
|
||||
} else {
|
||||
playSound(backgroundNoise);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
30
rwengine/src/audio/alCheck.cpp
Normal file
30
rwengine/src/audio/alCheck.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
#include "audio/alCheck.hpp"
|
||||
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
void checkALerror(const std::string& file, unsigned int line)
|
||||
{
|
||||
ALenum err = alGetError();
|
||||
if (err != AL_NO_ERROR) {
|
||||
std::cerr << "OpenAL error at " << file << ":" << line << ": ";
|
||||
|
||||
switch (err) {
|
||||
case AL_INVALID_NAME:
|
||||
std::cerr << "Invalid name!";
|
||||
break;
|
||||
case AL_INVALID_VALUE:
|
||||
std::cerr << "Invalid value!";
|
||||
break;
|
||||
case AL_INVALID_OPERATION:
|
||||
std::cerr << "Invalid operation!";
|
||||
break;
|
||||
default:
|
||||
std::cerr << err;
|
||||
}
|
||||
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
}
|
@ -492,59 +492,40 @@ void GameData::loadWeaponDAT(const std::string &name)
|
||||
|
||||
bool GameData::loadAudioStream(const std::string &name)
|
||||
{
|
||||
auto fname = findPathRealCase(datpath + "/audio/", name);
|
||||
auto filePath = findPathRealCase(datpath + "/audio/", name);
|
||||
|
||||
if ( engine->cutsceneAudio )
|
||||
{
|
||||
delete engine->cutsceneAudio;
|
||||
engine->cutsceneAudio = nullptr;
|
||||
if (engine->cutsceneAudio.length() > 0) {
|
||||
engine->sound.stopMusic(engine->cutsceneAudio);
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
if ( name.find(".mp3") != name.npos )
|
||||
{
|
||||
auto stream = new MADStream;
|
||||
engine->cutsceneAudio = stream;
|
||||
result = stream->openFromFile(fname);
|
||||
|
||||
if (engine->sound.loadMusic(name, filePath)) {
|
||||
engine->cutsceneAudio = name;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto stream = new sf::Music;
|
||||
engine->cutsceneAudio = stream;
|
||||
result = stream->openFromFile(fname);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GameData::loadAudioClip(const std::string& name)
|
||||
bool GameData::loadAudioClip(const std::string& name, const std::string& fileName)
|
||||
{
|
||||
auto fname = findPathRealCase(datpath + "/audio/", name);
|
||||
auto filePath = findPathRealCase(datpath + "/audio/", fileName);
|
||||
|
||||
if ( engine->missionAudio )
|
||||
{
|
||||
delete engine->missionAudio;
|
||||
engine->missionAudio = nullptr;
|
||||
}
|
||||
|
||||
if ( name.find(".mp3") != name.npos )
|
||||
if (fileName.find(".mp3") != fileName.npos)
|
||||
{
|
||||
logger->error("Data", "MP3 Audio unsupported outside cutscenes");
|
||||
return false;
|
||||
}
|
||||
|
||||
engine->missionAudio = new sf::SoundBuffer;
|
||||
|
||||
bool r = engine->missionAudio->loadFromFile(fname);
|
||||
|
||||
if (! r )
|
||||
{
|
||||
logger->error("Data", "Error loading audio clip " + fname);
|
||||
delete engine->missionAudio;
|
||||
engine->missionAudio = nullptr;
|
||||
|
||||
bool loaded = engine->sound.loadSound(name, filePath);
|
||||
|
||||
if ( ! loaded) {
|
||||
logger->error("Data", "Error loading audio clip "+ filePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
return r;
|
||||
|
||||
engine->missionAudio = name;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameData::loadSplash(const std::string &name)
|
||||
|
@ -78,7 +78,7 @@ public:
|
||||
|
||||
GameWorld::GameWorld(Logger* log, WorkContext* work, GameData* dat)
|
||||
: logger(log), data(dat), randomEngine(rand()),
|
||||
_work( work ), cutsceneAudio(nullptr), missionAudio(nullptr),
|
||||
_work( work ),
|
||||
paused(false)
|
||||
{
|
||||
data->engine = this;
|
||||
@ -833,8 +833,9 @@ void GameWorld::startCutscene()
|
||||
{
|
||||
state->cutsceneStartTime = getGameTime();
|
||||
state->skipCutscene = false;
|
||||
if( cutsceneAudio ) {
|
||||
cutsceneAudio->play();
|
||||
|
||||
if (cutsceneAudio.length() > 0) {
|
||||
sound.playMusic(cutsceneAudio);
|
||||
}
|
||||
}
|
||||
|
||||
@ -844,11 +845,9 @@ void GameWorld::clearCutscene()
|
||||
destroyObjectQueued(p.second);
|
||||
}
|
||||
|
||||
if( cutsceneAudio )
|
||||
{
|
||||
cutsceneAudio->stop();
|
||||
delete cutsceneAudio;
|
||||
cutsceneAudio = nullptr;
|
||||
if (cutsceneAudio.length() > 0) {
|
||||
sound.stopMusic(cutsceneAudio);
|
||||
cutsceneAudio = "";
|
||||
}
|
||||
|
||||
delete state->currentCutscene;
|
||||
|
@ -2,6 +2,8 @@
|
||||
#include <render/GameRenderer.hpp>
|
||||
#include <engine/GameWorld.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
/// @todo This is very rough
|
||||
int charToIndex(char g)
|
||||
{
|
||||
|
@ -789,10 +789,9 @@ void game_load_audio(const ScriptArguments& args)
|
||||
{
|
||||
std::string name = args[0].string;
|
||||
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
|
||||
if(! args.getWorld()->data->loadAudioClip(name + ".wav") )
|
||||
{
|
||||
if(! args.getWorld()->data->loadAudioClip(name + ".mp3") )
|
||||
{
|
||||
|
||||
if ( ! args.getWorld()->data->loadAudioClip(name, name + ".wav")) {
|
||||
if ( ! args.getWorld()->data->loadAudioClip(name, name + ".mp3")) {
|
||||
args.getWorld()->logger->error("SCM", "Failed to load audio: " + name);
|
||||
}
|
||||
}
|
||||
@ -801,22 +800,26 @@ void game_load_audio(const ScriptArguments& args)
|
||||
bool game_is_audio_loaded(const ScriptArguments& args)
|
||||
{
|
||||
auto world = args.getWorld();
|
||||
return world->missionAudio != nullptr;
|
||||
return world->sound.isLoaded(world->missionAudio);
|
||||
}
|
||||
|
||||
void game_play_mission_audio(const ScriptArguments& args)
|
||||
{
|
||||
auto world = args.getWorld();
|
||||
if ( world->missionAudio )
|
||||
{
|
||||
world->missionSound.setBuffer(*args.getWorld()->missionAudio);
|
||||
world->missionSound.play();
|
||||
world->missionSound.setLoop(false);
|
||||
if (world->missionAudio.length() > 0) {
|
||||
world->sound.playSound(world->missionAudio);
|
||||
}
|
||||
}
|
||||
bool game_is_audio_finished(const ScriptArguments& args)
|
||||
{
|
||||
return args.getWorld()->missionSound.getStatus() == sf::SoundSource::Stopped;
|
||||
auto world = args.getWorld();
|
||||
bool isFinished = ! world->sound.isPlaying(world->missionAudio);
|
||||
|
||||
if (isFinished) {
|
||||
world->missionAudio = "";
|
||||
}
|
||||
|
||||
return isFinished;
|
||||
}
|
||||
|
||||
void game_play_music_id(const ScriptArguments& args)
|
||||
@ -828,16 +831,14 @@ void game_play_music_id(const ScriptArguments& args)
|
||||
std::string name = "Miscom";
|
||||
|
||||
// TODO play anything other than Miscom.wav
|
||||
if(! gw->data->loadAudioClip( name + ".wav" ) )
|
||||
if(! gw->data->loadAudioClip( name, name + ".wav" ) )
|
||||
{
|
||||
args.getWorld()->logger->error("SCM", "Error loading audio " + name);
|
||||
return;
|
||||
}
|
||||
else if ( args.getWorld()->missionAudio )
|
||||
else if (args.getWorld()->missionAudio.length() > 0)
|
||||
{
|
||||
gw->missionSound.setBuffer(* args.getWorld()->missionAudio);
|
||||
gw->missionSound.play();
|
||||
gw->missionSound.setLoop(false);
|
||||
args.getWorld()->sound.playSound(args.getWorld()->missionAudio);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user