Started reworking the sound system. Added sources/ - WIP

actorid
Nicolay Korslund 15 years ago
parent 69e8f9c9db
commit cb638cd44e

@ -0,0 +1,63 @@
#ifndef MANGLE_SOUND_SOURCE_H
#define MANGLE_SOUND_SOURCE_H
#include <string>
#include <stdint.h>
#include <assert.h>
#include "../stream/stream.h"
namespace Mangle {
namespace Sound {
/// A stream containing raw sound data and information about the format
class SampleSource : public Stream::Stream
{
protected:
bool isEof;
public:
SampleSource()
{
// These are usually not needed for sound data
isSeekable = false;
hasPosition = false;
hasSize = false;
isEof = false;
}
/// Get the sample rate, number of channels, and bits per
/// sample. NULL parameters are ignored.
virtual void getInfo(int32_t *rate, int32_t *channels, int32_t *bits) const = 0;
bool eof() const { return isEof; }
// Disabled functions
void seek(size_t pos) const { assert(0); }
size_t tell() const { assert(0); }
size_t size() const { assert(0); }
};
/// A factory interface for loading SampleSources from file or stream
class SampleSourceLoader
{
public:
/// If true, the stream version of load() works
bool canLoadStream;
/// If true, the file version of load() works
bool canLoadFile;
/// Load a sound input source from file (if canLoadFile is true)
virtual SampleSource *load(const std::string &file) = 0;
/// Load a sound input source from stream (if canLoadStream is true)
virtual SampleSource *load(Stream::Stream *input) = 0;
/// Virtual destructor
virtual ~SampleSourceLoader() {}
};
}} // namespaces
#endif

@ -0,0 +1,139 @@
#include "audiere_source.h"
#include "../../stream/clients/audiere_file.h"
// Exception handling
class Audiere_Exception : public std::exception
{
std::string msg;
public:
Audiere_Exception(const std::string &m) : msg(m) {}
~Audiere_Exception() throw() {}
virtual const char* what() const throw() { return msg.c_str(); }
};
static void fail(const std::string &msg)
{
throw Audiere_Exception("Audiere exception: " + msg);
}
using namespace audiere;
using namespace Mangle::Sound;
// --- SampleSource ---
void AudiereSource::getInfo(int32_t *rate, int32_t *channels, int32_t *bits)
{
SampleFormat fmt;
sample->getFormat(*channels, *rate, fmt);
if(fmt == SF_U8)
*bits = 8;
else if(fmt == SF_S16)
*bits = 16;
else assert(0);
}
/*
Get data. Since Audiere operates with frames, not bytes, there's a
little conversion magic going on here. We need to make sure we're
reading a whole number of frames - if not, we need to store the
remainding part of the last frame and remember it for the next read
operation.
*/
size_t AudiereSource::read(void *_data, size_t length)
{
if(isEof) return 0;
char *data = (char*)_data;
// Move the remains from the last operation first
if(pullSize)
{
// pullSize is how much was stored the last time, so skip that.
memcpy(data, pullOver+pullSize, PSIZE-pullSize);
length -= pullSize;
data += pullSize;
}
// Determine the overshoot up front
pullSize = length % frameSize;
// Number of whole frames
int frames = length / frameSize;
// Read the data
int res = sample->read(frames, data);
if(res < frames)
isEof = true;
// Are we missing data? If we're at the end of the stream, then this
// doesn't apply.
if(!isEof && pullSize)
{
// Read one more sample
if(sample->read(1, pullOver) != 0)
{
// Then, move as much of it as we can fit into the output
// data
memcpy(data+length-pullSize, pullOver, pullSize);
}
else
// Failed reading, we're out of data
isEof = true;
}
// If we're at the end of the stream, then no data remains to be
// pulled over
if(isEof)
pullSize = 0;
// Return the total number of bytes stored
return frameSize*res + pullSize;
}
// --- Constructors ---
AudiereSource::AudiereSource(const std::string &file)
{
sample = OpenSampleSource(file.c_str());
if(!sample)
fail("Couldn't load file " + file);
getFormat();
}
AudiereSource::AudiereSource(Stream::Stream *input)
{
// Use our Stream::AudiereFile implementation to convert a Mangle
// 'Stream' to an Audiere 'File'
sample = OpenSampleSource(new Stream::AudiereFile(input));
if(!sample)
fail("Couldn't load stream");
getFormat();
}
AudiereSource::AudiereSource(audiere::SampleSourcePtr src)
: sample(src)
{ assert(sample); getFormat(); }
// Common function called from all constructors
AudiereSource::getFormat()
{
assert(sample);
SampleFormat fmt;
int channels, rate;
sample->getFormat(channels, rate, fmt);
// Calculate the size of one frame
frameSize = GetSampleSize(fmt) * channels;
// Make sure that our pullover hack will work. Increase this size if
// this doesn't work in all cases.
assert(frameSize <= PSIZE);
}

@ -0,0 +1,51 @@
#ifndef MANGLE_SOUND_AUDIERE_SOURCE_H
#define MANGLE_SOUND_AUDIERE_SOURCE_H
#include "../source.h"
#include <audiere.h>
namespace Mangle {
namespace Sound {
/// A sample source that decodes files using Audiere
class AudiereSource : public SampleSource
{
audiere::SampleSourcePtr sample;
// Number of bytes we cache between reads. This should correspond to
// the maximum possible value of frameSize.
static const int PSIZE = 10;
// Size of one frame, in bytes
int frameSize;
// Temporary storage for unevenly read samples. See the comment for
// read() in the .cpp file.
char pullOver[PSIZE];
// How much of the above buffer is in use
int pullSize;
void getFormat();
public:
/// Decode the given sound file
AudiereSource(const std::string &file);
/// Decode the given sound stream
AudiereSource(Stream::Stream *src);
/// Read directly from an existing audiere::SampleSource
AudiereSource(audiere::SampleSourcePtr src);
void getInfo(int32_t *rate, int32_t *channels, int32_t *bits);
size_t read(void *data, size_t length);
};
#include "loadertemplate.h"
/// A factory that loads AudiereSources from file and stream
typedef SSL_Template<AudiereSource,true,true> AudiereLoader;
}} // Namespace
#endif

@ -0,0 +1,238 @@
// NOT UPDATED - WIP
#include "input_ffmpeg.h"
#include <assert.h>
using namespace Mangle::Sound;
// Static output buffer. Not thread safe, but supports multiple
// streams operated from the same thread.
static uint8_t outBuf[AVCODEC_MAX_AUDIO_FRAME_SIZE];
bool FFM_InputManager::init = false;
/// FFmpeg exception.
class FFM_Exception : public std::exception
{
std::string msg;
public:
FFM_Exception(const std::string &m);
~FFM_Exception() throw();
virtual const char* what() const throw();
};
FFM_Exception::FFM_Exception(const std::string &m)
: msg(m) {}
const char* FFM_Exception::what() const throw()
{ return msg.c_str(); }
FFM_Exception::~FFM_Exception() throw() {}
static void fail(const std::string &msg)
{
throw FFM_Exception("FFMpeg exception: " + msg);
}
// --- Manager ---
FFM_InputManager::FFM_InputManager()
{
if(!init)
{
av_register_all();
av_log_set_level(AV_LOG_ERROR);
init = true;
}
canLoadStream = false;
}
InputSource *FFM_InputManager::load(const std::string &file)
{ return new FFM_InputSource(file); }
// --- Source ---
FFM_InputSource::FFM_InputSource(const std::string &file)
{
// FFmpeg doesn't handle several instances from one source. So we
// just store the filename.
name = file;
}
InputStream *FFM_InputSource::getStream()
{ return new FFM_InputStream(name); }
void FFM_InputSource::drop()
{ delete this; }
// --- Stream ---
FFM_InputStream::FFM_InputStream(const std::string &file)
{
std::string msg;
AVCodec *codec;
empty = false;
if(av_open_input_file(&FmtCtx, file.c_str(), NULL, 0, NULL) != 0)
fail("Error loading audio file " + file);
if(av_find_stream_info(FmtCtx) < 0)
{
msg = "Error in file stream " + file;
goto err;
}
// Pick the first audio stream, if any
for(StreamNum = 0; StreamNum < FmtCtx->nb_streams; StreamNum++)
{
// Pick the first audio stream
if(FmtCtx->streams[StreamNum]->codec->codec_type == CODEC_TYPE_AUDIO)
break;
}
if(StreamNum == FmtCtx->nb_streams)
fail("File " + file + " didn't contain any audio streams");
// Open the decoder
CodecCtx = FmtCtx->streams[StreamNum]->codec;
codec = avcodec_find_decoder(CodecCtx->codec_id);
if(!codec || avcodec_open(CodecCtx, codec) < 0)
{
msg = "Error loading " + file + ": ";
if(codec)
msg += "coded error";
else
msg += "no codec found";
goto err;
}
// No errors, we're done
return;
// Handle errors
err:
av_close_input_file(FmtCtx);
fail(msg);
}
FFM_InputStream::~FFM_InputStream()
{
avcodec_close(CodecCtx);
av_close_input_file(FmtCtx);
}
void FFM_InputStream::getInfo(int32_t *rate, int32_t *channels, int32_t *bits)
{
if(rate) *rate = CodecCtx->sample_rate;
if(channels) *channels = CodecCtx->channels;
if(bits) *bits = 16;
}
uint32_t FFM_InputStream::getData(void *data, uint32_t length)
{
if(empty) return 0;
uint32_t left = length;
uint8_t *outPtr = (uint8_t*)data;
// First, copy over any stored data we might be sitting on
{
int s = storage.size();
int copy = s;
if(s)
{
// Make sure there's room
if(copy > left)
copy = left;
// Copy
memcpy(outPtr, &storage[0], copy);
outPtr += copy;
left -= copy;
// Is there anything left in the storage?
s -= copy;
if(s)
{
assert(left == 0);
// Move it to the start and resize
memmove(&storage[0], &storage[copy], s);
storage.resize(s);
}
}
}
// Next, get more input data from stream, and decode it
while(left)
{
AVPacket packet;
// Get the next packet, if any
if(av_read_frame(FmtCtx, &packet) < 0)
break;
// We only allow one stream per file at the moment
assert(StreamNum == packet.stream_index);
// Decode the packet
int len = AVCODEC_MAX_AUDIO_FRAME_SIZE;
int tmp = avcodec_decode_audio2(CodecCtx, (int16_t*)outBuf,
&len, packet.data, packet.size);
assert(tmp < 0 || tmp == packet.size);
// We don't need the input packet any longer
av_free_packet(&packet);
if(tmp < 0)
fail("Error decoding audio stream");
// Copy whatever data we got, and advance the pointer
if(len > 0)
{
// copy = how many bytes do we copy now
int copy = len;
if(copy > left)
copy = left;
// len = how many bytes are left uncopied
len -= copy;
// copy data
memcpy(outPtr, outBuf, copy);
// left = how much space is left in the caller output
// buffer
left -= copy;
outPtr += copy;
assert(left >= 0);
if(len > 0)
{
// There were uncopied bytes. Store them for later.
assert(left == 0);
storage.resize(len);
memcpy(&storage[0], outBuf, len);
}
}
}
// End of loop. Return the number of bytes copied.
assert(left <= length);
// If we're returning less than asked for, then we're done
if(left > 0)
empty = true;
return length - left;
}
void FFM_InputStream::drop()
{ delete this; }

@ -0,0 +1,47 @@
#ifndef MANGLE_SOUND_FFMPEG_H
#define MANGLE_SOUND_FFMPEG_H
#include "../input.h"
#include <exception>
#include <vector>
#include <assert.h>
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
}
namespace Mangle {
namespace Sound {
class FFMpegSource : public SampleSource
{
AVFormatContext *FmtCtx;
AVCodecContext *CodecCtx;
int StreamNum;
bool empty;
std::vector<uint8_t> storage;
public:
/// Decode the given sound file
FFMpegSource(const std::string &file);
/// Decode the given sound stream (not supported by FFmpeg)
FFMpegSource(Stream::Stream *src) { assert(0); }
~FFMpegSource();
// Overrides
void getInfo(int32_t *rate, int32_t *channels, int32_t *bits);
size_t read(void *data, size_t length);
};
#include "loadertemplate.h"
/// A factory that loads FFMpegSources from file
typedef SSL_Template<AudiereSource,false,true> AudiereLoader;
}} // namespaces
#endif

@ -0,0 +1,26 @@
#ifndef SSL_TEMPL_H
#define SSL_TEMPL_H
template <class X, bool stream, bool file>
class SSL_Template : public SampleSourceLoader
{
SSL_Template()
{
canLoadStream = stream;
canLoadFile = file;
}
SampleSource *load(const std::string &file)
{
assert(canLoadFile);
return new X(file);
}
SampleSource *load(Stream::Stream *input)
{
assert(canLoadStream);
return new X(input);
}
};
#endif
Loading…
Cancel
Save