1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-09-18 16:32:32 +02:00

Add Cutscene Audio, via libmad

+ MADStream probably needs a good look over, for saftey's sake
This commit is contained in:
Daniel Evans 2014-08-01 21:04:58 +01:00
parent 51fa23a04c
commit 97ad0414f7
9 changed files with 196 additions and 3 deletions

View File

@ -9,6 +9,6 @@ add_library(rwengine
${RENDERWARE_HEADERS}
)
target_link_libraries(rwengine sfml-window GLEW)
target_link_libraries(rwengine sfml-window sfml-audio GLEW mad)
include_directories(include /usr/include/bullet)

View File

@ -0,0 +1,161 @@
#pragma once
#ifndef _MADSTREAM_HPP_
#define _MADSTREAM_HPP_
#include <mad.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <SFML/Audio.hpp>
#include <stdint.h>
static inline
signed int scale(mad_fixed_t sample)
{
double s = mad_f_todouble(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
{
mad_decoder mDecoder;
unsigned int mMadSampleRate;
unsigned int mMadChannels;
unsigned char* mFdm;
struct stat mStat;
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);
stream->mMadSampleRate = header->samplerate;
// see enum mad_mode
stream->mMadChannels = header->mode + 1;
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)
{
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)
{
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)
{
/// @todo support seeking.
}
public:
MADStream()
: mFdm(nullptr)
{
}
~MADStream()
{
if( mFdm )
{
munmap( mFdm, mStat.st_size );
}
}
bool open(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) {
sf::err() << "Fstat failed\n";
}
void* m = mmap(0, mStat.st_size, PROT_READ, MAP_SHARED, fd, 0);
if( m == MAP_FAILED ) {
sf::err() << "mmap failed\n";
}
mFdm = (unsigned char*)m;
mReadProgress = mStat.st_size;
mad_decoder_init(&mDecoder, this,
ms_input, ms_header, 0, ms_output, ms_error, 0);
int res = mad_decoder_run(&mDecoder, MAD_DECODER_MODE_SYNC);
this->initialize(2, mMadSampleRate);
return true;
}
};
#endif

View File

@ -19,6 +19,8 @@ struct CutsceneMetadata
std::string gxt;
};
std::string name;
/// The origin for coordinates in the cutscene
glm::vec3 sceneOffset;

View File

@ -13,6 +13,8 @@
#include <data/CollisionModel.hpp>
#include <data/GameTexts.hpp>
#include <audio/MADStream.hpp>
#include <memory>
struct DynamicObjectData;
@ -148,6 +150,8 @@ public:
*/
void loadWeaponDAT(const std::string& name);
bool loadAudio(MADStream &music, const std::string& name);
/**
* Returns a pointer to the named file if it is available,
* the memory must be freed by the caller.

View File

@ -256,9 +256,12 @@ public:
* @param name
*/
void loadCutscene(const std::string& name);
void startCutscene();
void clearCutscene();
MADStream fgAudio;
void playForegroundAudio(const std::string& name);
/**
* @brief loads a model into a special character slot.
*/

View File

@ -617,6 +617,11 @@ void GameData::loadWeaponDAT(const std::string &name)
}
}
bool GameData::loadAudio(MADStream& music, const std::string &name)
{
return music.open(datpath + "/audio/" + name);
}
char* GameData::openFile(const std::string& name)
{
auto i = _knownFiles.find(name);

View File

@ -695,9 +695,16 @@ void GameWorld::loadCutscene(const std::string &name)
delete state.currentCutscene;
}
state.currentCutscene = cutscene;
state.currentCutscene->meta.name = name;
std::cout << "Loaded cutscene: " << name << std::endl;
}
void GameWorld::startCutscene()
{
state.cutsceneStartTime = gameTime;
playForegroundAudio(state.currentCutscene->meta.name);
}
void GameWorld::clearCutscene()
{
/// @todo replace with the queued deletion from the projectile branch
@ -707,11 +714,20 @@ void GameWorld::clearCutscene()
}
}
fgAudio.stop();
delete state.currentCutscene;
state.currentCutscene = nullptr;
state.cutsceneStartTime = -1.f;
}
void GameWorld::playForegroundAudio(const std::string &name)
{
if( gameData.loadAudio(fgAudio, name+".mp3") ) {
fgAudio.play();
}
}
void GameWorld::loadSpecialCharacter(const unsigned short index, const std::string &name)
{
std::string lowerName(name);

View File

@ -425,7 +425,7 @@ VM_OPCODE_DEF( 0x02E6 )
}
VM_OPCODE_DEF( 0x02E7 )
{
m->getWorld()->state.cutsceneStartTime = m->getWorld()->gameTime;
m->getWorld()->startCutscene();
}
VM_OPCODE_DEF( 0x02E8 )
{

View File

@ -506,5 +506,7 @@ int main(int argc, char *argv[])
}
delete gta;
return 0;
}