1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-11-13 22:24:17 +01:00
openrw/rwengine/include/audio/MADStream.hpp
2015-01-26 14:40:09 +00:00

164 lines
3.2 KiB
C++

#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>
#include <iostream>
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
{
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 );
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;
}
};
#endif