Deleted a bunch of files, started on output
parent
4ee198d66c
commit
fb88d9ef0e
@ -1,83 +0,0 @@
|
||||
#ifndef MANGLE_SOUND_INPUT_H
|
||||
#define MANGLE_SOUND_INPUT_H
|
||||
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "../stream/stream.h"
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
/// An abstract interface for a read-once stream of audio data.
|
||||
/** All instances of this is created through InputSource. Objects
|
||||
should be manually deleted through a call to drop() when they are
|
||||
no longer needed.
|
||||
*/
|
||||
class InputStream
|
||||
{
|
||||
public:
|
||||
/// 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) = 0;
|
||||
|
||||
/// Get decoded sound data from the stream.
|
||||
/** Stores 'length' bytes (or less) in the buffer pointed to by
|
||||
'output'. Returns the number of bytes written. The function will
|
||||
only return less than 'length' at the end of the stream. When
|
||||
the stream is empty, all subsequent calls will return zero.
|
||||
|
||||
@param output where to store data
|
||||
@param length number of bytes to get
|
||||
@return number of bytes actually written
|
||||
*/
|
||||
virtual uint32_t getData(void *output, uint32_t length) = 0;
|
||||
|
||||
/// Kill this object
|
||||
virtual void drop() = 0;
|
||||
|
||||
/// Virtual destructor
|
||||
virtual ~InputStream() {}
|
||||
};
|
||||
|
||||
/// Abstract interface representing one sound source.
|
||||
/** A sound source may represent one sound file or buffer, and is a
|
||||
factory for producing InputStream objects from that
|
||||
sound. Instances of this class are created by an InputManager. All
|
||||
instances should be deleted through drop() when they are no longer
|
||||
needed.
|
||||
*/
|
||||
class InputSource
|
||||
{
|
||||
public:
|
||||
/// Create a stream from this sound
|
||||
virtual InputStream *getStream() = 0;
|
||||
|
||||
/// Kill this object
|
||||
virtual void drop() = 0;
|
||||
|
||||
/// Virtual destructor
|
||||
virtual ~InputSource() {}
|
||||
};
|
||||
|
||||
/// Main interface to a sound decoder backend.
|
||||
/** An input manager is a factory of InputSource objects.
|
||||
*/
|
||||
class InputManager
|
||||
{
|
||||
public:
|
||||
/// If true, the stream version of load() works
|
||||
bool canLoadStream;
|
||||
|
||||
/// Load a sound input source from file
|
||||
virtual InputSource *load(const std::string &file) = 0;
|
||||
|
||||
/// Load a sound input source from stream (if canLoadStream is true)
|
||||
virtual InputSource *load(Stream::Stream *input) = 0;
|
||||
|
||||
/// Virtual destructor
|
||||
virtual ~InputManager() {}
|
||||
};
|
||||
|
||||
}} // namespaces
|
||||
#endif
|
@ -1 +0,0 @@
|
||||
|
@ -1,95 +0,0 @@
|
||||
#include "audiere_imp.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;
|
||||
|
||||
AudiereManager::AudiereManager()
|
||||
{
|
||||
needsUpdate = false;
|
||||
has3D = false;
|
||||
canRepeatStream = true;
|
||||
canLoadFile = true;
|
||||
canLoadSource = false;
|
||||
canLoadStream = false;
|
||||
|
||||
device = OpenDevice("");
|
||||
|
||||
if(device == NULL)
|
||||
fail("Failed to open device");
|
||||
}
|
||||
|
||||
// --- Manager ---
|
||||
|
||||
Sound *AudiereManager::load(const std::string &file, bool stream)
|
||||
{ return new AudiereSound(file, device, stream); }
|
||||
|
||||
|
||||
// --- Sound ---
|
||||
|
||||
AudiereSound::AudiereSound(const std::string &file,
|
||||
AudioDevicePtr _device,
|
||||
bool _stream)
|
||||
: device(_device), stream(_stream)
|
||||
{
|
||||
sample = OpenSampleSource(file.c_str());
|
||||
if(!sample)
|
||||
fail("Couldn't load file " + file);
|
||||
|
||||
buf = CreateSampleBuffer(sample);
|
||||
}
|
||||
|
||||
Instance *AudiereSound::getInstance(bool is3d, bool repeat)
|
||||
{
|
||||
// Ignore is3d. Audiere doesn't implement 3d sound. We could make a
|
||||
// hack software 3D implementation later, but it's not that
|
||||
// important.
|
||||
|
||||
SampleSourcePtr sample = buf->openStream();
|
||||
if(!sample)
|
||||
fail("Failed to open sample stream");
|
||||
|
||||
OutputStreamPtr sound = OpenSound(device, sample, stream);
|
||||
|
||||
if(repeat)
|
||||
sound->setRepeat(true);
|
||||
|
||||
return new AudiereInstance(sound);
|
||||
}
|
||||
|
||||
|
||||
// --- Instance ---
|
||||
|
||||
AudiereInstance::AudiereInstance(OutputStreamPtr _sound)
|
||||
: sound(_sound) {}
|
||||
|
||||
void AudiereInstance::play()
|
||||
{ sound->play(); }
|
||||
|
||||
void AudiereInstance::stop()
|
||||
{ sound->stop(); }
|
||||
|
||||
void AudiereInstance::pause()
|
||||
{ stop(); }
|
||||
|
||||
bool AudiereInstance::isPlaying()
|
||||
{ return sound->isPlaying(); }
|
||||
|
||||
void AudiereInstance::setVolume(float vol)
|
||||
{ sound->setVolume(vol); }
|
@ -1,77 +0,0 @@
|
||||
#ifndef MANGLE_SOUND_AUDIERE_H
|
||||
#define MANGLE_SOUND_AUDIERE_H
|
||||
|
||||
#include "../sound.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <audiere.h>
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
/// Implementation of Sound::Manager for Audiere
|
||||
class AudiereManager : public Manager
|
||||
{
|
||||
audiere::AudioDevicePtr device;
|
||||
|
||||
public:
|
||||
AudiereManager();
|
||||
|
||||
virtual Sound *load(const std::string &file, bool stream=false);
|
||||
|
||||
/// not implemented yet
|
||||
virtual Sound *load(Stream::Stream *input, bool stream=false)
|
||||
{ assert(0); }
|
||||
|
||||
/// disabled
|
||||
virtual Sound *load(InputSource *input, bool stream=false)
|
||||
{ assert(0); }
|
||||
/// disabled
|
||||
virtual void update() { assert(0); }
|
||||
/// disabled
|
||||
virtual void setListenerPos(float x, float y, float z,
|
||||
float fx, float fy, float fz,
|
||||
float ux, float uy, float uz)
|
||||
{ assert(0); };
|
||||
};
|
||||
|
||||
/// Audiere Sound implementation
|
||||
class AudiereSound : public Sound
|
||||
{
|
||||
audiere::AudioDevicePtr device;
|
||||
audiere::SampleSourcePtr sample;
|
||||
audiere::SampleBufferPtr buf;
|
||||
|
||||
bool stream;
|
||||
|
||||
public:
|
||||
virtual Instance *getInstance(bool is3d, bool repeat);
|
||||
virtual void drop()
|
||||
{ delete this; }
|
||||
|
||||
AudiereSound(const std::string &file, audiere::AudioDevicePtr device,
|
||||
bool stream);
|
||||
};
|
||||
|
||||
/// Audiere Instance implementation
|
||||
class AudiereInstance : public Instance
|
||||
{
|
||||
audiere::OutputStreamPtr sound;
|
||||
|
||||
public:
|
||||
virtual void play();
|
||||
virtual void stop();
|
||||
virtual void pause();
|
||||
virtual bool isPlaying();
|
||||
virtual void setVolume(float);
|
||||
/// disabled
|
||||
virtual void setPos(float x, float y, float z)
|
||||
{ assert(0); }
|
||||
virtual void drop()
|
||||
{ delete this; }
|
||||
|
||||
AudiereInstance(audiere::OutputStreamPtr);
|
||||
};
|
||||
|
||||
}} // Namespace
|
||||
#endif
|
@ -1,139 +0,0 @@
|
||||
#include "input_audiere.h"
|
||||
#include <assert.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;
|
||||
|
||||
// --- InputManager ---
|
||||
|
||||
AudiereInput::AudiereInput()
|
||||
{
|
||||
canLoadStream = true;
|
||||
}
|
||||
|
||||
InputSource *AudiereInput::load(const std::string &file)
|
||||
{ return new AudiereSource(file); }
|
||||
|
||||
InputSource *AudiereInput::load(Stream::Stream *input)
|
||||
{ return new AudiereSource(input); }
|
||||
|
||||
// --- InputSource ---
|
||||
|
||||
AudiereSource::AudiereSource(const std::string &file)
|
||||
{
|
||||
SampleSourcePtr sample = OpenSampleSource(file.c_str());
|
||||
if(!sample)
|
||||
fail("Couldn't load file " + file);
|
||||
|
||||
buf = CreateSampleBuffer(sample);
|
||||
}
|
||||
|
||||
AudiereSource::AudiereSource(Stream::Stream *input)
|
||||
{
|
||||
SampleSourcePtr sample = OpenSampleSource
|
||||
(new Stream::AudiereFile(input));
|
||||
if(!sample)
|
||||
fail("Couldn't load stream");
|
||||
|
||||
buf = CreateSampleBuffer(sample);
|
||||
}
|
||||
|
||||
InputStream *AudiereSource::getStream()
|
||||
{
|
||||
return new AudiereStream(buf->openStream());
|
||||
}
|
||||
|
||||
// --- InputStream ---
|
||||
|
||||
AudiereStream::AudiereStream(SampleSourcePtr _sample)
|
||||
: sample(_sample), pullSize(0)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
void AudiereStream::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.
|
||||
|
||||
*/
|
||||
uint32_t AudiereStream::getData(void *_data, uint32_t length)
|
||||
{
|
||||
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);
|
||||
|
||||
// Are we missing data? If res<length and we're at the end of the
|
||||
// stream, then this doesn't apply.
|
||||
if(res == frames && pullSize &&
|
||||
// Read one more sample
|
||||
(sample->read(1, pullOver) != 0))
|
||||
{
|
||||
// Now, move as much of it as we can fit into the output
|
||||
// data
|
||||
memcpy(data+length-pullSize, pullOver, pullSize);
|
||||
}
|
||||
else pullSize = 0;
|
||||
|
||||
// Return the total number of bytes stored
|
||||
return frameSize*res + pullSize;
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
#ifndef MANGLE_SOUND_AUDIERE_INPUT_H
|
||||
#define MANGLE_SOUND_AUDIERE_INPUT_H
|
||||
|
||||
#include "../input.h"
|
||||
|
||||
#include <audiere.h>
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
/// Implementation of Sound::InputManager for Audiere
|
||||
class AudiereInput : public InputManager
|
||||
{
|
||||
public:
|
||||
AudiereInput();
|
||||
|
||||
/// Load a source from a file
|
||||
InputSource *load(const std::string &file);
|
||||
|
||||
/// Load a source from a stream
|
||||
virtual InputSource *load(Stream::Stream *input);
|
||||
};
|
||||
|
||||
/// Audiere InputSource implementation
|
||||
class AudiereSource : public InputSource
|
||||
{
|
||||
audiere::SampleBufferPtr buf;
|
||||
|
||||
public:
|
||||
AudiereSource(const std::string &file);
|
||||
AudiereSource(Stream::Stream *input);
|
||||
InputStream *getStream();
|
||||
void drop() { delete this; }
|
||||
};
|
||||
|
||||
/// Audiere InputStream implementation
|
||||
class AudiereStream : public InputStream
|
||||
{
|
||||
audiere::SampleSourcePtr sample;
|
||||
int frameSize; // Size of one frame, in bytes
|
||||
|
||||
static const int PSIZE = 10;
|
||||
|
||||
// Temporary storage for unevenly read samples. See the comment for
|
||||
// getData() in the .cpp file.
|
||||
char pullOver[PSIZE];
|
||||
// How much of the above buffer is in use
|
||||
int pullSize;
|
||||
|
||||
public:
|
||||
AudiereStream(audiere::SampleSourcePtr _sample);
|
||||
|
||||
void getInfo(int32_t *rate, int32_t *channels, int32_t *bits);
|
||||
uint32_t getData(void *data, uint32_t length);
|
||||
void drop() { delete this; }
|
||||
};
|
||||
|
||||
}} // Namespace
|
||||
#endif
|
@ -1,224 +0,0 @@
|
||||
#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;
|
||||
|
||||
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; }
|
@ -1,75 +0,0 @@
|
||||
#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 {
|
||||
|
||||
/// 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();
|
||||
};
|
||||
|
||||
/// FFMpeg implementation of InputManager
|
||||
class FFM_InputManager : public InputManager
|
||||
{
|
||||
static bool init;
|
||||
|
||||
public:
|
||||
FFM_InputManager();
|
||||
virtual InputSource *load(const std::string &file);
|
||||
|
||||
/// not supported
|
||||
virtual InputSource *load(Stream::Stream *input) { assert(0); }
|
||||
};
|
||||
|
||||
/// FFMpeg implementation of InputSource
|
||||
class FFM_InputSource : public InputSource
|
||||
{
|
||||
std::string name;
|
||||
|
||||
public:
|
||||
FFM_InputSource(const std::string &file);
|
||||
|
||||
virtual InputStream *getStream();
|
||||
virtual void drop();
|
||||
};
|
||||
|
||||
/// FFMpeg implementation of InputStream
|
||||
class FFM_InputStream : public InputStream
|
||||
{
|
||||
AVFormatContext *FmtCtx;
|
||||
AVCodecContext *CodecCtx;
|
||||
int StreamNum;
|
||||
bool empty;
|
||||
|
||||
std::vector<uint8_t> storage;
|
||||
|
||||
public:
|
||||
FFM_InputStream(const std::string &file);
|
||||
~FFM_InputStream();
|
||||
|
||||
virtual void getInfo(int32_t *rate, int32_t *channels, int32_t *bits);
|
||||
virtual uint32_t getData(void *data, uint32_t length);
|
||||
virtual void drop();
|
||||
};
|
||||
|
||||
}} // namespaces
|
||||
#endif
|
@ -1,80 +0,0 @@
|
||||
#ifndef MANGLE_INPUT_FILTER_H
|
||||
#define MANGLE_INPUT_FILTER_H
|
||||
|
||||
#include "../sound.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
/**
|
||||
@brief This filter class adds file loading capabilities to a
|
||||
Sound::Manager class, by associating an InputManager with it.
|
||||
|
||||
The class takes an existing Manager able to load streams, and
|
||||
associates an InputManager with it. The combined class is able to
|
||||
load files directly.
|
||||
|
||||
Example:
|
||||
\code
|
||||
|
||||
// Add FFmpeg input to an OpenAL soud output manager. OpenAL cannot
|
||||
// decode sound files on its own.
|
||||
InputFilter mg(new OpenAL_Manager, new FFM_InputManager);
|
||||
|
||||
// We can now load filenames directly.
|
||||
mg.load("file1.mp3");
|
||||
\endcode
|
||||
*/
|
||||
class InputFilter : public Manager
|
||||
{
|
||||
protected:
|
||||
Manager *snd;
|
||||
InputManager *inp;
|
||||
|
||||
public:
|
||||
/// Empty constructor
|
||||
InputFilter() {}
|
||||
|
||||
/// Assign an input manager and a sound manager to this object
|
||||
InputFilter(Manager *_snd, InputManager *_inp)
|
||||
{ set(_snd, _inp); }
|
||||
|
||||
/// Assign an input manager and a sound manager to this object
|
||||
void set(Manager *_snd, InputManager *_inp)
|
||||
{
|
||||
inp = _inp;
|
||||
snd = _snd;
|
||||
|
||||
// Set capabilities
|
||||
needsUpdate = snd->needsUpdate;
|
||||
has3D = snd->has3D;
|
||||
canRepeatStream = snd->canRepeatStream;
|
||||
canLoadStream = inp->canLoadStream;
|
||||
|
||||
// Both these should be true, or the use of this class is pretty
|
||||
// pointless
|
||||
canLoadSource = snd->canLoadSource;
|
||||
canLoadFile = canLoadSource;
|
||||
assert(canLoadSource && canLoadFile);
|
||||
}
|
||||
|
||||
virtual Sound *load(const std::string &file, bool stream=false)
|
||||
{ return load(inp->load(file), stream); }
|
||||
|
||||
virtual Sound *load(Stream::Stream *input, bool stream=false)
|
||||
{ return load(inp->load(input), stream); }
|
||||
|
||||
virtual Sound *load(InputSource *input, bool stream=false)
|
||||
{ return snd->load(input, stream); }
|
||||
|
||||
virtual void update() { snd->update(); }
|
||||
virtual void setListenerPos(float x, float y, float z,
|
||||
float fx, float fy, float fz,
|
||||
float ux, float uy, float uz)
|
||||
{ snd->setListenerPos(x,y,z,fx,fy,fz,ux,uy,uz); }
|
||||
};
|
||||
|
||||
}}
|
||||
#endif
|
@ -1,29 +0,0 @@
|
||||
#ifndef MANGLE_FFMPEG_OPENAL_H
|
||||
#define MANGLE_FFMPEG_OPENAL_H
|
||||
|
||||
#include "input_filter.h"
|
||||
#include "input_audiere.h"
|
||||
#include "output_openal.h"
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
/// A InputFilter that adds audiere decoding to OpenAL. Audiere has
|
||||
/// it's own output, but OpenAL sports 3D and other advanced features.
|
||||
class OpenAL_Audiere_Manager : public InputFilter
|
||||
{
|
||||
public:
|
||||
OpenAL_Audiere_Manager()
|
||||
{
|
||||
set(new OpenAL_Manager,
|
||||
new AudiereInput);
|
||||
}
|
||||
~OpenAL_Audiere_Manager()
|
||||
{
|
||||
delete snd;
|
||||
delete inp;
|
||||
}
|
||||
};
|
||||
|
||||
}}
|
||||
#endif
|
@ -1,28 +0,0 @@
|
||||
#ifndef MANGLE_FFMPEG_OPENAL_H
|
||||
#define MANGLE_FFMPEG_OPENAL_H
|
||||
|
||||
#include "input_filter.h"
|
||||
#include "input_ffmpeg.h"
|
||||
#include "output_openal.h"
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
/// An InputFilter that adds FFmpeg decoding to OpenAL
|
||||
class OpenAL_FFM_Manager : public InputFilter
|
||||
{
|
||||
public:
|
||||
OpenAL_FFM_Manager()
|
||||
{
|
||||
set(new OpenAL_Manager,
|
||||
new FFM_InputManager);
|
||||
}
|
||||
~OpenAL_FFM_Manager()
|
||||
{
|
||||
delete snd;
|
||||
delete inp;
|
||||
}
|
||||
};
|
||||
|
||||
}}
|
||||
#endif
|
@ -1,353 +0,0 @@
|
||||
#include "output_openal.h"
|
||||
#include <assert.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
using namespace Mangle::Sound;
|
||||
|
||||
|
||||
// ---- Helper functions and classes ----
|
||||
|
||||
class OpenAL_Exception : public std::exception
|
||||
{
|
||||
std::string msg;
|
||||
|
||||
public:
|
||||
|
||||
OpenAL_Exception(const std::string &m) : msg(m) {}
|
||||
~OpenAL_Exception() throw() {}
|
||||
virtual const char* what() const throw() { return msg.c_str(); }
|
||||
};
|
||||
|
||||
static void fail(const std::string &msg)
|
||||
{
|
||||
throw OpenAL_Exception("OpenAL exception: " + msg);
|
||||
}
|
||||
|
||||
static void checkALError(const std::string &msg)
|
||||
{
|
||||
ALenum err = alGetError();
|
||||
if(err != AL_NO_ERROR)
|
||||
fail("\"" + std::string(alGetString(err)) + "\" while " + msg);
|
||||
}
|
||||
|
||||
static void getALFormat(InputStream *inp, int &fmt, int &rate)
|
||||
{
|
||||
int ch, bits;
|
||||
inp->getInfo(&rate, &ch, &bits);
|
||||
|
||||
fmt = 0;
|
||||
|
||||
if(bits == 8)
|
||||
{
|
||||
if(ch == 1) fmt = AL_FORMAT_MONO8;
|
||||
if(ch == 2) fmt = AL_FORMAT_STEREO8;
|
||||
if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
|
||||
{
|
||||
if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD8");
|
||||
if(ch == 6) fmt = alGetEnumValue("AL_FORMAT_51CHN8");
|
||||
}
|
||||
}
|
||||
if(bits == 16)
|
||||
{
|
||||
if(ch == 1) fmt = AL_FORMAT_MONO16;
|
||||
if(ch == 2) fmt = AL_FORMAT_STEREO16;
|
||||
if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD16");
|
||||
if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
|
||||
{
|
||||
if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD16");
|
||||
if(ch == 6) fmt = alGetEnumValue("AL_FORMAT_51CHN16");
|
||||
}
|
||||
}
|
||||
|
||||
if(fmt == 0)
|
||||
fail("Unsupported input format");
|
||||
}
|
||||
|
||||
|
||||
// ---- Manager ----
|
||||
|
||||
OpenAL_Manager::OpenAL_Manager()
|
||||
: Context(NULL), Device(NULL)
|
||||
{
|
||||
needsUpdate = true;
|
||||
has3D = true;
|
||||
canRepeatStream = false;
|
||||
canLoadFile = false;
|
||||
canLoadSource = true;
|
||||
canLoadStream = false;
|
||||
|
||||
// Set up sound system
|
||||
Device = alcOpenDevice(NULL);
|
||||
Context = alcCreateContext(Device, NULL);
|
||||
|
||||
if(!Device || !Context)
|
||||
fail("Failed to initialize context or device");
|
||||
|
||||
alcMakeContextCurrent(Context);
|
||||
}
|
||||
|
||||
OpenAL_Manager::~OpenAL_Manager()
|
||||
{
|
||||
// Deinitialize sound system
|
||||
alcMakeContextCurrent(NULL);
|
||||
if(Context) alcDestroyContext(Context);
|
||||
if(Device) alcCloseDevice(Device);
|
||||
}
|
||||
|
||||
Sound *OpenAL_Manager::load(const std::string &file, bool stream)
|
||||
{ assert(0 && "OpenAL cannot decode files"); }
|
||||
|
||||
Sound *OpenAL_Manager::load(Stream::Stream*,bool)
|
||||
{ assert(0 && "OpenAL cannot decode streams"); }
|
||||
|
||||
Sound *OpenAL_Manager::load(InputSource *source, bool stream)
|
||||
{ return new OpenAL_Sound(source, this, stream); }
|
||||
|
||||
void OpenAL_Manager::update()
|
||||
{
|
||||
// Loop through all the streaming sounds and update them
|
||||
LST::iterator it, next;
|
||||
for(it = streaming.begin();
|
||||
it != streaming.end();
|
||||
it++)
|
||||
{
|
||||
(*it)->update();
|
||||
}
|
||||
}
|
||||
|
||||
void OpenAL_Manager::setListenerPos(float x, float y, float z,
|
||||
float fx, float fy, float fz,
|
||||
float ux, float uy, float uz)
|
||||
{
|
||||
ALfloat orient[6];
|
||||
orient[0] = fx;
|
||||
orient[1] = fy;
|
||||
orient[2] = fz;
|
||||
orient[3] = ux;
|
||||
orient[4] = uy;
|
||||
orient[5] = uz;
|
||||
alListener3f(AL_POSITION, x, y, z);
|
||||
alListenerfv(AL_ORIENTATION, orient);
|
||||
checkALError("setting listener position");
|
||||
}
|
||||
|
||||
OpenAL_Manager::LST::iterator OpenAL_Manager::add_stream(OpenAL_Stream_Instance* inst)
|
||||
{
|
||||
streaming.push_front(inst);
|
||||
return streaming.begin();
|
||||
}
|
||||
|
||||
void OpenAL_Manager::remove_stream(LST::iterator it)
|
||||
{
|
||||
streaming.erase(it);
|
||||
}
|
||||
|
||||
|
||||
// ---- Sound ----
|
||||
|
||||
OpenAL_Sound::~OpenAL_Sound()
|
||||
{
|
||||
// Kill the input source
|
||||
if(source) source->drop();
|
||||
|
||||
// And any allocated buffers
|
||||
if(bufferID)
|
||||
alDeleteBuffers(1, &bufferID);
|
||||
}
|
||||
|
||||
Instance *OpenAL_Sound::getInstance(bool is3d, bool repeat)
|
||||
{
|
||||
assert((!repeat || !stream) && "OpenAL implementation does not support looping streams");
|
||||
|
||||
if(stream)
|
||||
return new OpenAL_Stream_Instance(source->getStream(), owner);
|
||||
|
||||
// Load the buffer if it hasn't been done already
|
||||
if(bufferID == 0)
|
||||
{
|
||||
// Get an input stream and load the file from it
|
||||
InputStream *inp = source->getStream();
|
||||
|
||||
std::vector<unsigned char> buffer;
|
||||
|
||||
// Add 32 kb at each increment
|
||||
const int ADD = 32*1024;
|
||||
|
||||
// Fill the buffer. We increase the buffer until it's large
|
||||
// enough to fit all the data.
|
||||
while(true)
|
||||
{
|
||||
// Increase the buffer
|
||||
int oldlen = buffer.size();
|
||||
buffer.resize(oldlen+ADD);
|
||||
|
||||
// Read the data
|
||||
size_t len = inp->getData(&buffer[oldlen], ADD);
|
||||
|
||||
// If we read less than requested, we're done.
|
||||
if(len < ADD)
|
||||
{
|
||||
// Downsize the buffer to the right size
|
||||
buffer.resize(oldlen+len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the format
|
||||
int fmt, rate;
|
||||
getALFormat(inp, fmt, rate);
|
||||
|
||||
// We don't need the file anymore
|
||||
inp->drop();
|
||||
source->drop();
|
||||
source = NULL;
|
||||
|
||||
// Move the data into OpenAL
|
||||
alGenBuffers(1, &bufferID);
|
||||
alBufferData(bufferID, fmt, &buffer[0], buffer.size(), rate);
|
||||
checkALError("loading sound buffer");
|
||||
} // End of buffer loading
|
||||
|
||||
// At this point, the file data has been loaded into the buffer
|
||||
// in 'bufferID', and we should be ready to go.
|
||||
assert(bufferID != 0);
|
||||
|
||||
return new OpenAL_Simple_Instance(bufferID);
|
||||
}
|
||||
|
||||
|
||||
// ---- OpenAL_Instance_Base ----
|
||||
|
||||
void OpenAL_Instance_Base::play()
|
||||
{
|
||||
alSourcePlay(inst);
|
||||
checkALError("starting playback");
|
||||
}
|
||||
|
||||
void OpenAL_Instance_Base::stop()
|
||||
{
|
||||
alSourceStop(inst);
|
||||
checkALError("stopping");
|
||||
}
|
||||
|
||||
void OpenAL_Instance_Base::pause()
|
||||
{
|
||||
alSourcePause(inst);
|
||||
checkALError("pausing");
|
||||
}
|
||||
|
||||
bool OpenAL_Instance_Base::isPlaying()
|
||||
{
|
||||
ALint state;
|
||||
alGetSourcei(inst, AL_SOURCE_STATE, &state);
|
||||
|
||||
return state == AL_PLAYING;
|
||||
}
|
||||
|
||||
void OpenAL_Instance_Base::setVolume(float volume)
|
||||
{
|
||||
if(volume > 1.0) volume = 1.0;
|
||||
if(volume < 0.0) volume = 0.0;
|
||||
alSourcef(inst, AL_GAIN, volume);
|
||||
checkALError("setting volume");
|
||||
}
|
||||
|
||||
void OpenAL_Instance_Base::setPos(float x, float y, float z)
|
||||
{
|
||||
alSource3f(inst, AL_POSITION, x, y, z);
|
||||
checkALError("setting position");
|
||||
}
|
||||
|
||||
|
||||
// ---- OpenAL_Simple_Instance ----
|
||||
|
||||
OpenAL_Simple_Instance::OpenAL_Simple_Instance(ALuint buf)
|
||||
{
|
||||
// Create instance and associate buffer
|
||||
alGenSources(1, &inst);
|
||||
alSourcei(inst, AL_BUFFER, buf);
|
||||
}
|
||||
|
||||
OpenAL_Simple_Instance::~OpenAL_Simple_Instance()
|
||||
{
|
||||
// Stop
|
||||
alSourceStop(inst);
|
||||
|
||||
// Return sound
|
||||
alDeleteSources(1, &inst);
|
||||
}
|
||||
|
||||
|
||||
// ---- OpenAL_Stream_Instance ----
|
||||
|
||||
OpenAL_Stream_Instance::OpenAL_Stream_Instance(InputStream *_stream,
|
||||
OpenAL_Manager *_owner)
|
||||
: stream(_stream), owner(_owner)
|
||||
{
|
||||
// Deduce the file format from the stream info
|
||||
getALFormat(stream, fmt, rate);
|
||||
|
||||
// Create the buffers and the sound instance
|
||||
alGenBuffers(BUFS, bufs);
|
||||
alGenSources(1, &inst);
|
||||
|
||||
checkALError("initializing");
|
||||
|
||||
// Fill the buffers and que them
|
||||
for(int i=0; i<BUFS; i++)
|
||||
queueBuffer(bufs[i]);
|
||||
|
||||
checkALError("buffering initial data");
|
||||
|
||||
// Add ourselves to the stream list
|
||||
lit = owner->add_stream(this);
|
||||
}
|
||||
|
||||
void OpenAL_Stream_Instance::queueBuffer(ALuint bId)
|
||||
{
|
||||
char buf[SIZE];
|
||||
|
||||
// Get the data
|
||||
int len = stream->getData(buf, SIZE);
|
||||
if(len == 0)
|
||||
return;
|
||||
|
||||
// .. and stash it
|
||||
alBufferData(bId, fmt, buf, len, rate);
|
||||
alSourceQueueBuffers(inst, 1, &bId);
|
||||
}
|
||||
|
||||
OpenAL_Stream_Instance::~OpenAL_Stream_Instance()
|
||||
{
|
||||
// Remove ourselves from streaming list
|
||||
owner->remove_stream(lit);
|
||||
|
||||
// Stop
|
||||
alSourceStop(inst);
|
||||
|
||||
// Kill the input stream
|
||||
stream->drop();
|
||||
|
||||
// Return sound
|
||||
alDeleteSources(1, &inst);
|
||||
|
||||
// Delete buffers
|
||||
alDeleteBuffers(BUFS, bufs);
|
||||
}
|
||||
|
||||
void OpenAL_Stream_Instance::update()
|
||||
{
|
||||
ALint count;
|
||||
alGetSourcei(inst, AL_BUFFERS_PROCESSED, &count);
|
||||
|
||||
for(int i = 0;i < count;i++)
|
||||
{
|
||||
// Unque a finished buffer
|
||||
ALuint bId;
|
||||
alSourceUnqueueBuffers(inst, 1, &bId);
|
||||
|
||||
// Queue a new buffer
|
||||
queueBuffer(bId);
|
||||
}
|
||||
}
|
@ -1,125 +0,0 @@
|
||||
#ifndef MANGLE_SOUND_OPENAL_H
|
||||
#define MANGLE_SOUND_OPENAL_H
|
||||
|
||||
#include "../sound.h"
|
||||
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
#include <list>
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
class OpenAL_Stream_Instance;
|
||||
|
||||
/// OpenAL implementation of Manager
|
||||
class OpenAL_Manager : public Manager
|
||||
{
|
||||
public:
|
||||
// List of all streaming sounds - these need to be updated regularly
|
||||
typedef std::list<OpenAL_Stream_Instance*> LST;
|
||||
|
||||
OpenAL_Manager();
|
||||
virtual ~OpenAL_Manager();
|
||||
|
||||
LST::iterator add_stream(OpenAL_Stream_Instance*);
|
||||
void remove_stream(LST::iterator);
|
||||
|
||||
virtual Sound *load(const std::string &file, bool stream=false);
|
||||
virtual Sound *load(Stream::Stream *input, bool stream=false);
|
||||
virtual Sound *load(InputSource* input, bool stream=false);
|
||||
virtual void update();
|
||||
virtual void setListenerPos(float x, float y, float z,
|
||||
float fx, float fy, float fz,
|
||||
float ux, float uy, float uz);
|
||||
|
||||
private:
|
||||
ALCdevice *Device;
|
||||
ALCcontext *Context;
|
||||
|
||||
LST streaming;
|
||||
};
|
||||
|
||||
/// OpenAL implementation of Sound
|
||||
class OpenAL_Sound : public Sound
|
||||
{
|
||||
InputSource *source;
|
||||
OpenAL_Manager *owner;
|
||||
bool stream;
|
||||
|
||||
// Used for non-streaming files, contains the entire sound buffer if
|
||||
// non-zero
|
||||
ALuint bufferID;
|
||||
|
||||
public:
|
||||
OpenAL_Sound(InputSource *src, OpenAL_Manager *own, bool str)
|
||||
: source(src), owner(own), stream(str), bufferID(0) {}
|
||||
~OpenAL_Sound();
|
||||
|
||||
virtual Instance *getInstance(bool is3d, bool repeat);
|
||||
void drop() { delete this; }
|
||||
};
|
||||
|
||||
/// Shared parent class that holds an OpenAL sound instance. Just used
|
||||
/// for shared functionality, has no setup or cleanup code.
|
||||
class OpenAL_Instance_Base : public Instance
|
||||
{
|
||||
protected:
|
||||
ALuint inst;
|
||||
|
||||
public:
|
||||
void drop() { delete this; }
|
||||
virtual void play();
|
||||
virtual void stop();
|
||||
virtual void pause();
|
||||
virtual bool isPlaying();
|
||||
virtual void setVolume(float);
|
||||
virtual void setPos(float x, float y, float z);
|
||||
};
|
||||
|
||||
/// Non-streaming OpenAL-implementation of Instance. Uses a shared
|
||||
/// sound buffer in OpenAL_Sound.
|
||||
class OpenAL_Simple_Instance : public OpenAL_Instance_Base
|
||||
{
|
||||
public:
|
||||
OpenAL_Simple_Instance(ALuint buf);
|
||||
~OpenAL_Simple_Instance();
|
||||
};
|
||||
|
||||
/// Streaming OpenAL-implementation of Instance.
|
||||
class OpenAL_Stream_Instance : public OpenAL_Instance_Base
|
||||
{
|
||||
// Since OpenAL streams have to be updated manually each frame, we
|
||||
// need to have a sufficiently large buffer so that we don't run out
|
||||
// of data in the mean time. Each instance will take around 512 Kb
|
||||
// of memory, independent of how large the file is.
|
||||
static const int BUFS = 4;
|
||||
static const int SIZE = 128*1024;
|
||||
|
||||
// Buffers
|
||||
ALuint bufs[BUFS];
|
||||
|
||||
// Sound format settings
|
||||
int rate, fmt;
|
||||
|
||||
// Source of data
|
||||
InputStream *stream;
|
||||
|
||||
OpenAL_Manager *owner;
|
||||
|
||||
// List iterator, used for removing ourselves from the streaming
|
||||
// list when we're deleted.
|
||||
OpenAL_Manager::LST::iterator lit;
|
||||
|
||||
// Load and queue a new buffer
|
||||
void queueBuffer(ALuint buffer);
|
||||
|
||||
public:
|
||||
OpenAL_Stream_Instance(InputStream*, OpenAL_Manager*);
|
||||
~OpenAL_Stream_Instance();
|
||||
|
||||
void update();
|
||||
};
|
||||
|
||||
}} // namespaces
|
||||
#endif
|
Loading…
Reference in New Issue