Remove Mangle- and OpenEngine-related sound code
Start of the sound code refactoring. Currently there is no sound, but it will be added back.actorid
parent
b2c6d52db1
commit
42b445383f
@ -1 +0,0 @@
|
||||
|
@ -1,79 +0,0 @@
|
||||
#ifndef MANGLE_SOUND_OGRELISTENERMOVER_H
|
||||
#define MANGLE_SOUND_OGRELISTENERMOVER_H
|
||||
|
||||
#include <OgreCamera.h>
|
||||
#include <assert.h>
|
||||
#include "../output.hpp"
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
/** This class lets a sound listener (ie. the SoundFactory) track a
|
||||
given camera in Ogre3D. The position and orientation of the
|
||||
listener will be updated to match the camera whenever the camera
|
||||
is moved.
|
||||
*/
|
||||
struct OgreListenerMover : Ogre::Camera::Listener
|
||||
{
|
||||
OgreListenerMover(Mangle::Sound::SoundFactoryPtr snd)
|
||||
: soundFact(snd), camera(NULL)
|
||||
{}
|
||||
|
||||
/// Follow a camera. WARNING: This will OVERRIDE any other
|
||||
/// MovableObject::Listener you may have attached to the camera.
|
||||
void followCamera(Ogre::Camera *cam)
|
||||
{
|
||||
camera = cam;
|
||||
camera->addListener(this);
|
||||
}
|
||||
|
||||
void unfollowCamera()
|
||||
{
|
||||
// If the camera is null, this object wasn't following a camera.
|
||||
// It doesn't make sense to call unfollow
|
||||
assert(camera != NULL);
|
||||
|
||||
camera->removeListener(this);
|
||||
camera = NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
Mangle::Sound::SoundFactoryPtr soundFact;
|
||||
Ogre::Camera *camera;
|
||||
Ogre::Vector3 pos, dir, up;
|
||||
|
||||
/// From Camera::Listener. This is called once per
|
||||
/// frame. Unfortunately, Ogre doesn't allow us to be notified
|
||||
/// only when the camera itself has moved, so we must poll every
|
||||
/// frame.
|
||||
void cameraPreRenderScene(Ogre::Camera *cam)
|
||||
{
|
||||
assert(cam == camera);
|
||||
|
||||
Ogre::Vector3 nPos, nDir, nUp;
|
||||
|
||||
nPos = camera->getRealPosition();
|
||||
nDir = camera->getRealDirection();
|
||||
nUp = camera->getRealUp();
|
||||
|
||||
// Don't bother the sound system needlessly
|
||||
if(nDir != dir || nPos != pos || nUp != up)
|
||||
{
|
||||
pos = nPos;
|
||||
dir = nDir;
|
||||
up = nUp;
|
||||
|
||||
soundFact->setListenerPos(pos.x, pos.y, pos.z,
|
||||
dir.x, dir.y, dir.z,
|
||||
up.x, up.y, up.z);
|
||||
}
|
||||
}
|
||||
|
||||
void cameraDestroyed(Ogre::Camera *cam)
|
||||
{
|
||||
assert(cam == camera);
|
||||
camera = NULL;
|
||||
}
|
||||
};
|
||||
}}
|
||||
#endif
|
@ -1,31 +0,0 @@
|
||||
#ifndef MANGLE_SOUND_OGREUPDATER_H
|
||||
#define MANGLE_SOUND_OGREUPDATER_H
|
||||
|
||||
/*
|
||||
This Ogre FrameListener calls update on a SoundFactory
|
||||
*/
|
||||
|
||||
#include <OgreFrameListener.h>
|
||||
#include "../output.hpp"
|
||||
#include <assert.h>
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
struct OgreOutputUpdater : Ogre::FrameListener
|
||||
{
|
||||
Mangle::Sound::SoundFactoryPtr driver;
|
||||
|
||||
OgreOutputUpdater(Mangle::Sound::SoundFactoryPtr drv)
|
||||
: driver(drv)
|
||||
{ assert(drv->needsUpdate); }
|
||||
|
||||
bool frameStarted(const Ogre::FrameEvent &evt)
|
||||
{
|
||||
driver->update();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}}
|
||||
|
||||
#endif
|
@ -1,68 +0,0 @@
|
||||
#ifndef MANGLE_INPUT_FILTER_H
|
||||
#define MANGLE_INPUT_FILTER_H
|
||||
|
||||
#include "../output.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
/**
|
||||
@brief This filter class adds file loading capabilities to a
|
||||
Sound::SoundFactory class, by associating a SampleSourceLoader with
|
||||
it.
|
||||
|
||||
The class takes an existing SoundFactory able to load streams, and
|
||||
associates a SampleSourceLoader with it. The combined class is able
|
||||
to load files directly. */
|
||||
class InputFilter : public SoundFactory
|
||||
{
|
||||
protected:
|
||||
SoundFactoryPtr snd;
|
||||
SampleSourceLoaderPtr inp;
|
||||
|
||||
public:
|
||||
/// Empty constructor
|
||||
InputFilter() {}
|
||||
|
||||
/// Assign an input manager and a sound manager to this object
|
||||
InputFilter(SoundFactoryPtr _snd, SampleSourceLoaderPtr _inp)
|
||||
{ set(_snd, _inp); }
|
||||
|
||||
/// Assign an input manager and a sound manager to this object
|
||||
void set(SoundFactoryPtr _snd, SampleSourceLoaderPtr _inp)
|
||||
{
|
||||
inp = _inp;
|
||||
snd = _snd;
|
||||
|
||||
// Set capabilities
|
||||
needsUpdate = snd->needsUpdate;
|
||||
has3D = snd->has3D;
|
||||
canLoadStream = inp->canLoadStream;
|
||||
|
||||
// Both these should be true, or the use of this class is pretty
|
||||
// pointless
|
||||
canLoadSource = snd->canLoadSource;
|
||||
canLoadFile = inp->canLoadFile;
|
||||
assert(canLoadSource && canLoadFile);
|
||||
}
|
||||
|
||||
virtual SoundPtr load(const std::string &file)
|
||||
{ return loadRaw(inp->load(file)); }
|
||||
|
||||
virtual SoundPtr load(Stream::StreamPtr input)
|
||||
{ return loadRaw(inp->load(input)); }
|
||||
|
||||
virtual SoundPtr loadRaw(SampleSourcePtr input)
|
||||
{ return snd->loadRaw(input); }
|
||||
|
||||
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,24 +0,0 @@
|
||||
#ifndef MANGLE_AUDIERE_OPENAL_H
|
||||
#define MANGLE_AUDIERE_OPENAL_H
|
||||
|
||||
#include "input_filter.hpp"
|
||||
#include "../sources/audiere_source.hpp"
|
||||
#include "../outputs/openal_out.hpp"
|
||||
|
||||
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_Factory : public InputFilter
|
||||
{
|
||||
public:
|
||||
OpenAL_Audiere_Factory()
|
||||
{
|
||||
set(SoundFactoryPtr(new OpenAL_Factory),
|
||||
SampleSourceLoaderPtr(new AudiereLoader));
|
||||
}
|
||||
};
|
||||
|
||||
}}
|
||||
#endif
|
@ -1,23 +0,0 @@
|
||||
#ifndef MANGLE_FFMPEG_OPENAL_H
|
||||
#define MANGLE_FFMPEG_OPENAL_H
|
||||
|
||||
#include "input_filter.hpp"
|
||||
#include "../sources/ffmpeg_source.hpp"
|
||||
#include "../outputs/openal_out.hpp"
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
/// A InputFilter that adds ffmpeg decoding to OpenAL.
|
||||
class OpenAL_FFMpeg_Factory : public InputFilter
|
||||
{
|
||||
public:
|
||||
OpenAL_FFMpeg_Factory()
|
||||
{
|
||||
set(SoundFactoryPtr(new OpenAL_Factory),
|
||||
SampleSourceLoaderPtr(new FFMpegLoader));
|
||||
}
|
||||
};
|
||||
|
||||
}}
|
||||
#endif
|
@ -1,24 +0,0 @@
|
||||
#ifndef MANGLE_MPG123_OPENAL_H
|
||||
#define MANGLE_MPG123_OPENAL_H
|
||||
|
||||
#include "input_filter.hpp"
|
||||
#include "../sources/mpg123_source.hpp"
|
||||
#include "../outputs/openal_out.hpp"
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
/// A InputFilter that adds mpg123 decoding to OpenAL. Only supports
|
||||
/// MP3 files.
|
||||
class OpenAL_Mpg123_Factory : public InputFilter
|
||||
{
|
||||
public:
|
||||
OpenAL_Mpg123_Factory()
|
||||
{
|
||||
set(SoundFactoryPtr(new OpenAL_Factory),
|
||||
SampleSourceLoaderPtr(new Mpg123Loader));
|
||||
}
|
||||
};
|
||||
|
||||
}}
|
||||
#endif
|
@ -1,24 +0,0 @@
|
||||
#ifndef MANGLE_SNDFILE_OPENAL_H
|
||||
#define MANGLE_SNDFILE_OPENAL_H
|
||||
|
||||
#include "input_filter.hpp"
|
||||
#include "../sources/libsndfile.hpp"
|
||||
#include "../outputs/openal_out.hpp"
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
/// A InputFilter that adds libsnd decoding to OpenAL. libsndfile
|
||||
/// supports most formats except MP3.
|
||||
class OpenAL_SndFile_Factory : public InputFilter
|
||||
{
|
||||
public:
|
||||
OpenAL_SndFile_Factory()
|
||||
{
|
||||
set(SoundFactoryPtr(new OpenAL_Factory),
|
||||
SampleSourceLoaderPtr(new SndFileLoader));
|
||||
}
|
||||
};
|
||||
|
||||
}}
|
||||
#endif
|
@ -1,33 +0,0 @@
|
||||
#ifndef MANGLE_SNDFILE_MPG123_OPENAL_H
|
||||
#define MANGLE_SNDFILE_MPG123_OPENAL_H
|
||||
|
||||
#include "input_filter.hpp"
|
||||
#include "source_splicer.hpp"
|
||||
#include "../sources/mpg123_source.hpp"
|
||||
#include "../sources/libsndfile.hpp"
|
||||
#include "../outputs/openal_out.hpp"
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
/// A InputFilter that uses OpenAL for output, and mpg123 (for MP3) +
|
||||
/// libsndfile (for everything else) to decode files. Can only load
|
||||
/// from the file system, and uses the file name to differentiate
|
||||
/// between mp3 and non-mp3 types.
|
||||
class OpenAL_SndFile_Mpg123_Factory : public InputFilter
|
||||
{
|
||||
public:
|
||||
OpenAL_SndFile_Mpg123_Factory()
|
||||
{
|
||||
SourceSplicer *splice = new SourceSplicer;
|
||||
|
||||
splice->add("mp3", SampleSourceLoaderPtr(new Mpg123Loader));
|
||||
splice->setDefault(SampleSourceLoaderPtr(new SndFileLoader));
|
||||
|
||||
set(SoundFactoryPtr(new OpenAL_Factory),
|
||||
SampleSourceLoaderPtr(splice));
|
||||
}
|
||||
};
|
||||
|
||||
}}
|
||||
#endif
|
@ -1,39 +0,0 @@
|
||||
#ifndef MANGLE_VARIOUS_OPENAL_H
|
||||
#define MANGLE_VARIOUS_OPENAL_H
|
||||
|
||||
#include "input_filter.hpp"
|
||||
#include "source_splicer.hpp"
|
||||
#include "../sources/mpg123_source.hpp"
|
||||
#include "../sources/wav_source.hpp"
|
||||
#include "../outputs/openal_out.hpp"
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
/** A InputFilter that uses OpenAL for output, and load input from
|
||||
various individual sources, depending on file extension. Currently
|
||||
supports:
|
||||
|
||||
MP3: mpg123
|
||||
WAV: custom wav loader (PCM only)
|
||||
|
||||
This could be an alternative to using eg. 3rd party decoder
|
||||
libraries like libsndfile.
|
||||
*/
|
||||
class OpenAL_Various_Factory : public InputFilter
|
||||
{
|
||||
public:
|
||||
OpenAL_Various_Factory()
|
||||
{
|
||||
SourceSplicer *splice = new SourceSplicer;
|
||||
|
||||
splice->add("mp3", SampleSourceLoaderPtr(new Mpg123Loader));
|
||||
splice->add("wav", SampleSourceLoaderPtr(new WavLoader));
|
||||
|
||||
set(SoundFactoryPtr(new OpenAL_Factory),
|
||||
SampleSourceLoaderPtr(splice));
|
||||
}
|
||||
};
|
||||
|
||||
}}
|
||||
#endif
|
@ -1,73 +0,0 @@
|
||||
#ifndef MANGLE_SOUND_OUTPUT_PUREFILTER_H
|
||||
#define MANGLE_SOUND_OUTPUT_PUREFILTER_H
|
||||
|
||||
#include "../output.hpp"
|
||||
|
||||
namespace Mangle
|
||||
{
|
||||
namespace Sound
|
||||
{
|
||||
// For use in writing other filters
|
||||
class SoundFilter : public Sound
|
||||
{
|
||||
protected:
|
||||
SoundPtr client;
|
||||
|
||||
public:
|
||||
SoundFilter(SoundPtr c) : client(c) {}
|
||||
void play() { client->play(); }
|
||||
void stop() { client->stop(); }
|
||||
void pause() { client->pause(); }
|
||||
bool isPlaying() const { return client->isPlaying(); }
|
||||
void setVolume(float f) { client->setVolume(f); }
|
||||
void setPan(float f) { client->setPan(f); }
|
||||
void setPos(float x, float y, float z)
|
||||
{ client->setPos(x,y,z); }
|
||||
void setPitch(float p) { client->setPitch(p); }
|
||||
void setRepeat(bool b) { client->setRepeat(b); }
|
||||
void setRange(float a, float b=0, float c=0)
|
||||
{ client->setRange(a,b,c); }
|
||||
void setStreaming(bool b) { client->setStreaming(b); }
|
||||
void setRelative(bool b) { client->setRelative(b); }
|
||||
|
||||
// The clone() function is not implemented here, as you will
|
||||
// almost certainly want to override it yourself
|
||||
};
|
||||
|
||||
class FactoryFilter : public SoundFactory
|
||||
{
|
||||
protected:
|
||||
SoundFactoryPtr client;
|
||||
|
||||
public:
|
||||
FactoryFilter(SoundFactoryPtr c) : client(c)
|
||||
{
|
||||
needsUpdate = client->needsUpdate;
|
||||
has3D = client->has3D;
|
||||
canLoadFile = client->canLoadFile;
|
||||
canLoadStream = client->canLoadStream;
|
||||
canLoadSource = client->canLoadSource;
|
||||
}
|
||||
|
||||
SoundPtr loadRaw(SampleSourcePtr input)
|
||||
{ return client->loadRaw(input); }
|
||||
|
||||
SoundPtr load(Stream::StreamPtr input)
|
||||
{ return client->load(input); }
|
||||
|
||||
SoundPtr load(const std::string &file)
|
||||
{ return client->load(file); }
|
||||
|
||||
void update()
|
||||
{ client->update(); }
|
||||
|
||||
void setListenerPos(float x, float y, float z,
|
||||
float fx, float fy, float fz,
|
||||
float ux, float uy, float uz)
|
||||
{
|
||||
client->setListenerPos(x,y,z,fx,fy,fz,ux,uy,uz);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,90 +0,0 @@
|
||||
#ifndef MANGLE_SOUND_SOURCE_SPLICE_H
|
||||
#define MANGLE_SOUND_SOURCE_SPLICE_H
|
||||
|
||||
#include "../source.hpp"
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <assert.h>
|
||||
|
||||
namespace Mangle
|
||||
{
|
||||
namespace Sound
|
||||
{
|
||||
class SourceSplicer : public SampleSourceLoader
|
||||
{
|
||||
struct SourceType
|
||||
{
|
||||
std::string type;
|
||||
SampleSourceLoaderPtr loader;
|
||||
};
|
||||
|
||||
typedef std::list<SourceType> TypeList;
|
||||
TypeList list;
|
||||
SampleSourceLoaderPtr catchAll;
|
||||
|
||||
static bool isMatch(char a, char b)
|
||||
{
|
||||
if(a >= 'A' && a <= 'Z')
|
||||
a += 'a' - 'A';
|
||||
if(b >= 'A' && b <= 'Z')
|
||||
b += 'a' - 'A';
|
||||
return a == b;
|
||||
}
|
||||
|
||||
public:
|
||||
SourceSplicer()
|
||||
{
|
||||
canLoadStream = false;
|
||||
canLoadFile = true;
|
||||
}
|
||||
|
||||
void add(const std::string &type, SampleSourceLoaderPtr fact)
|
||||
{
|
||||
SourceType tp;
|
||||
tp.type = type;
|
||||
tp.loader = fact;
|
||||
list.push_back(tp);
|
||||
}
|
||||
|
||||
void setDefault(SampleSourceLoaderPtr def)
|
||||
{
|
||||
catchAll = def;
|
||||
}
|
||||
|
||||
SampleSourcePtr load(const std::string &file)
|
||||
{
|
||||
// Search the list for this file type.
|
||||
for(TypeList::iterator it = list.begin();
|
||||
it != list.end(); it++)
|
||||
{
|
||||
const std::string &t = it->type;
|
||||
|
||||
int diff = file.size() - t.size();
|
||||
if(diff < 0) continue;
|
||||
|
||||
bool match = true;
|
||||
for(unsigned i=0; i<t.size(); i++)
|
||||
if(!isMatch(t[i], file[i+diff]))
|
||||
{
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Got something! We're done.
|
||||
if(match)
|
||||
return it->loader->load(file);
|
||||
}
|
||||
// If not found, use the catch-all
|
||||
if(catchAll)
|
||||
return catchAll->load(file);
|
||||
|
||||
throw std::runtime_error("No handler for sound file " + file);
|
||||
}
|
||||
|
||||
SampleSourcePtr load(Stream::StreamPtr input) { assert(0); }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,183 +0,0 @@
|
||||
#ifndef MANGLE_SOUND_OUTPUT_H
|
||||
#define MANGLE_SOUND_OUTPUT_H
|
||||
|
||||
#include <string>
|
||||
#include <assert.h>
|
||||
|
||||
#include "source.hpp"
|
||||
#include "../stream/stream.hpp"
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
/// Abstract interface for a single playable sound
|
||||
/** This class represents one sound outlet, which may be played,
|
||||
stopped, paused and so on.
|
||||
|
||||
Sound instances are created from the SoundFactory class. Sounds
|
||||
may be connected to a SampleSource or read directly from a file,
|
||||
and they may support 3d sounds, looping and other features
|
||||
depending on the capabilities of the backend system.
|
||||
|
||||
To create multiple instances of one sound, it is recommended to
|
||||
'clone' an existing instance instead of reloading it from
|
||||
file. Cloned sounds will often (depending on the back-end) use
|
||||
less memory due to shared buffers.
|
||||
*/
|
||||
class Sound;
|
||||
typedef boost::shared_ptr<Sound> SoundPtr;
|
||||
typedef boost::weak_ptr <Sound> WSoundPtr;
|
||||
|
||||
class Sound
|
||||
{
|
||||
public:
|
||||
/// Play or resume the sound
|
||||
virtual void play() = 0;
|
||||
|
||||
/// Stop the sound
|
||||
virtual void stop() = 0;
|
||||
|
||||
/// Pause the sound, may be resumed later
|
||||
virtual void pause() = 0;
|
||||
|
||||
/// Check if the sound is still playing
|
||||
virtual bool isPlaying() const = 0;
|
||||
|
||||
/// Set the volume. The parameter must be between 0.0 and 1.0.
|
||||
virtual void setVolume(float) = 0;
|
||||
|
||||
/// Set left/right pan. -1.0 is left, 0.0 is center and 1.0 is right.
|
||||
virtual void setPan(float) = 0;
|
||||
|
||||
/// Set pitch (1.0 is normal speed)
|
||||
virtual void setPitch(float) = 0;
|
||||
|
||||
/// Set range factors for 3D sounds. The meaning of the fields
|
||||
/// depend on implementation.
|
||||
virtual void setRange(float a, float b=0.0, float c=0.0) = 0;
|
||||
|
||||
/// Set the position. May not work with all backends.
|
||||
virtual void setPos(float x, float y, float z) = 0;
|
||||
|
||||
/// Set loop mode
|
||||
virtual void setRepeat(bool) = 0;
|
||||
|
||||
/// If set to true the sound will not be affected by player movement
|
||||
virtual void setRelative(bool) = 0;
|
||||
|
||||
/// Set streaming mode.
|
||||
/** This may be used by implementations to optimize for very large
|
||||
files. If streaming mode is off (default), most implementations
|
||||
will load the entire file into memory before starting playback.
|
||||
*/
|
||||
virtual void setStreaming(bool) = 0;
|
||||
|
||||
/// Create a new instance of this sound.
|
||||
/** Playback status is not cloned, only the sound data
|
||||
itself. Back-ends can use this as a means of sharing data and
|
||||
saving memory. */
|
||||
virtual SoundPtr clone() = 0;
|
||||
|
||||
/// Virtual destructor
|
||||
virtual ~Sound() {}
|
||||
};
|
||||
|
||||
/// Factory interface for creating Sound objects
|
||||
/** The SoundFactory is the main entry point to a given sound output
|
||||
system. It is used to create Sound objects, which may be connected
|
||||
to a sound file or stream, and which may be individually played,
|
||||
paused, and so on.
|
||||
|
||||
The class also contains a set of public bools which describe the
|
||||
capabilities the particular system. These should be set by
|
||||
implementations (base classes) in their respective constructors.
|
||||
*/
|
||||
class SoundFactory
|
||||
{
|
||||
public:
|
||||
/// Virtual destructor
|
||||
virtual ~SoundFactory() {}
|
||||
|
||||
/** @brief If set to true, you should call update() regularly (every frame
|
||||
or so) on this sound manager. If false, update() should not be
|
||||
called.
|
||||
*/
|
||||
bool needsUpdate;
|
||||
|
||||
/** @brief true if 3D functions are available. If false, all use of
|
||||
3D sounds and calls to setPos / setListenerPos will result in
|
||||
undefined behavior.
|
||||
*/
|
||||
bool has3D;
|
||||
|
||||
/// true if we can load sounds directly from file (containing encoded data)
|
||||
bool canLoadFile;
|
||||
|
||||
/// If true, we can lound sound files from a Stream (containing encoded data)
|
||||
bool canLoadStream;
|
||||
|
||||
/// true if we can load sounds from a SampleSource (containing raw data)
|
||||
bool canLoadSource;
|
||||
|
||||
/**
|
||||
@brief Load a sound from a sample source. Only valid if
|
||||
canLoadSource is true.
|
||||
|
||||
This function loads a sound from a given stream as defined by
|
||||
SampleSource.
|
||||
|
||||
@param input the input source
|
||||
@param stream true if the file should be streamed.
|
||||
Implementations may use this for optimizing playback of
|
||||
large files, but they are not required to.
|
||||
@return a new Sound object
|
||||
*/
|
||||
virtual SoundPtr loadRaw(SampleSourcePtr input) = 0;
|
||||
|
||||
/**
|
||||
@brief Load a sound file from stream. Only valid if canLoadStream
|
||||
is true.
|
||||
|
||||
@param input audio file stream
|
||||
@param stream true if the file should be streamed
|
||||
@see load(InputSource*,bool)
|
||||
*/
|
||||
virtual SoundPtr load(Stream::StreamPtr input) = 0;
|
||||
|
||||
/**
|
||||
@brief Load a sound directly from file. Only valid if canLoadFile
|
||||
is true.
|
||||
|
||||
@param file filename
|
||||
@param stream true if the file should be streamed
|
||||
@see load(InputSource*,bool)
|
||||
*/
|
||||
virtual SoundPtr load(const std::string &file) = 0;
|
||||
|
||||
/// Call this every frame if needsUpdate is true
|
||||
/**
|
||||
This should be called regularly (about every frame in a normal
|
||||
game setting.) Implementions may use this for filling streaming
|
||||
buffers and similar tasks. Implementations that do not need this
|
||||
should set needsUpdate to false.
|
||||
*/
|
||||
virtual void update() { assert(0); }
|
||||
|
||||
/// Set listener position (coordinates, front and up vectors)
|
||||
/**
|
||||
Only valid if has3D is true.
|
||||
|
||||
@param x,y,z listener position
|
||||
@param fx,fy,fz listener's looking direction
|
||||
@param ux,uy,uz listener's up direction
|
||||
*/
|
||||
virtual void setListenerPos(float x, float y, float z,
|
||||
float fx, float fy, float fz,
|
||||
float ux, float uy, float uz) = 0;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<SoundFactory> SoundFactoryPtr;
|
||||
|
||||
}} // Namespaces
|
||||
|
||||
#endif
|
@ -1,500 +0,0 @@
|
||||
#include "openal_out.hpp"
|
||||
#include <assert.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "../../stream/filters/buffer_stream.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <al.h>
|
||||
#include <alc.h>
|
||||
#elif defined(__APPLE__)
|
||||
#include <OpenAL/alc.h>
|
||||
#include <OpenAL/al.h>
|
||||
#else
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
#endif
|
||||
|
||||
using namespace Mangle::Sound;
|
||||
|
||||
// ---- Helper functions and classes ----
|
||||
|
||||
// Static buffer used to shuffle sound data from the input into
|
||||
// OpenAL. The data is only stored temporarily and then immediately
|
||||
// shuffled off to the library. This is not thread safe, but it works
|
||||
// fine with multiple sounds in one thread. It could be made thread
|
||||
// safe simply by using thread local storage.
|
||||
const size_t BSIZE = 32*1024;
|
||||
static char tmp_buffer[BSIZE];
|
||||
|
||||
// Number of buffers used (per sound) for streaming sounds. Each
|
||||
// buffer is of size BSIZE. Increasing this will make streaming sounds
|
||||
// more fault tolerant against temporary lapses in call to update(),
|
||||
// but will also increase memory usage.
|
||||
// This was changed from 4 to 150 for an estimated 30 seconds tolerance.
|
||||
// At some point we should replace it with a more multithreading-ish
|
||||
// solution.
|
||||
const int STREAM_BUF_NUM = 150;
|
||||
|
||||
static void fail(const std::string &msg)
|
||||
{ throw std::runtime_error("OpenAL exception: " + msg); }
|
||||
|
||||
/*
|
||||
Check for AL error. Since we're always calling this with string
|
||||
literals, and it only makes sense to optimize for the non-error
|
||||
case, the parameter is const char* rather than std::string.
|
||||
|
||||
This way we don't force the compiler to create a string object each
|
||||
time we're called (since the string is never used unless there's an
|
||||
error), although a good compiler might have optimized that away in
|
||||
any case.
|
||||
*/
|
||||
static void checkALError(const char *where)
|
||||
{
|
||||
ALenum err = alGetError();
|
||||
if(err != AL_NO_ERROR)
|
||||
{
|
||||
std::string msg = where;
|
||||
|
||||
const ALchar* errmsg = alGetString(err);
|
||||
if(errmsg)
|
||||
fail("\"" + std::string(alGetString(err)) + "\" while " + msg);
|
||||
else
|
||||
fail("non-specified error while " + msg + " (did you forget to initialize OpenAL?)");
|
||||
}
|
||||
}
|
||||
|
||||
static void getALFormat(SampleSourcePtr inp, int &fmt, int &rate)
|
||||
{
|
||||
boost::int32_t rate_, ch, bits;
|
||||
inp->getInfo(&rate_, &ch, &bits);
|
||||
rate = rate_;
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
/// OpenAL sound output
|
||||
class Mangle::Sound::OpenAL_Sound : public Sound
|
||||
{
|
||||
ALuint inst;
|
||||
|
||||
// Buffers. Only the first is used for non-streaming sounds.
|
||||
ALuint bufferID[STREAM_BUF_NUM];
|
||||
|
||||
// Number of buffers used
|
||||
int bufNum;
|
||||
|
||||
// Parameters used for filling buffers
|
||||
int fmt, rate;
|
||||
|
||||
// Poor mans reference counting. Might improve this later. When
|
||||
// NULL, the buffer has not been set up yet.
|
||||
int *refCnt;
|
||||
|
||||
bool streaming;
|
||||
|
||||
// Input stream
|
||||
SampleSourcePtr input;
|
||||
|
||||
OpenAL_Factory *owner;
|
||||
bool ownerAlive;
|
||||
|
||||
// Used for streamed sound list
|
||||
OpenAL_Sound *next, *prev;
|
||||
|
||||
void setupBuffer();
|
||||
|
||||
// Fill data into the given buffer and queue it, if there is any
|
||||
// data left to queue. Assumes the buffer is already unqueued, if
|
||||
// necessary.
|
||||
void queueBuffer(ALuint buf)
|
||||
{
|
||||
// If there is no more data, do nothing
|
||||
if(!input) return;
|
||||
if(input->eof())
|
||||
{
|
||||
input.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
// Get some new data
|
||||
size_t bytes = input->read(tmp_buffer, BSIZE);
|
||||
if(bytes == 0)
|
||||
{
|
||||
input.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
// Move data into the OpenAL buffer
|
||||
alBufferData(buf, fmt, tmp_buffer, bytes, rate);
|
||||
// Queue it
|
||||
alSourceQueueBuffers(inst, 1, &buf);
|
||||
checkALError("Queueing buffer data");
|
||||
}
|
||||
|
||||
public:
|
||||
/// Read samples from the given input buffer
|
||||
OpenAL_Sound(SampleSourcePtr input, OpenAL_Factory *fact);
|
||||
|
||||
/// Play an existing buffer, with a given ref counter. Used
|
||||
/// internally for cloning.
|
||||
OpenAL_Sound(ALuint buf, int *ref, OpenAL_Factory *fact);
|
||||
|
||||
~OpenAL_Sound();
|
||||
|
||||
// Must be called regularly on streamed sounds
|
||||
void update()
|
||||
{
|
||||
if(!streaming) return;
|
||||
if(!input) return;
|
||||
|
||||
// Get the number of processed buffers
|
||||
ALint count;
|
||||
alGetSourcei(inst, AL_BUFFERS_PROCESSED, &count);
|
||||
checkALError("getting number of unprocessed buffers");
|
||||
|
||||
for(int i=0; i<count; i++)
|
||||
{
|
||||
ALuint buf;
|
||||
// Unqueue one of the processed buffer
|
||||
alSourceUnqueueBuffers(inst, 1, &buf);
|
||||
|
||||
// Then reload it with data (if any) and queue it up
|
||||
queueBuffer(buf);
|
||||
}
|
||||
}
|
||||
|
||||
void play();
|
||||
void stop();
|
||||
void pause();
|
||||
bool isPlaying() const;
|
||||
void setVolume(float);
|
||||
void setPos(float x, float y, float z);
|
||||
void setPitch(float);
|
||||
void setRepeat(bool);
|
||||
void setRelative(bool);
|
||||
|
||||
void notifyOwnerDeath()
|
||||
{ ownerAlive = false; }
|
||||
|
||||
// We can enable streaming, but never disable it.
|
||||
void setStreaming(bool s)
|
||||
{ if(s) streaming = true; }
|
||||
|
||||
SoundPtr clone();
|
||||
|
||||
// a = AL_REFERENCE_DISTANCE
|
||||
// b = AL_MAX_DISTANCE
|
||||
// c = ignored
|
||||
void setRange(float a, float b=0.0, float c=0.0);
|
||||
|
||||
/// Not implemented
|
||||
void setPan(float) {}
|
||||
};
|
||||
|
||||
// ---- OpenAL_Factory ----
|
||||
|
||||
SoundPtr OpenAL_Factory::loadRaw(SampleSourcePtr input)
|
||||
{
|
||||
return SoundPtr(new OpenAL_Sound(input, this));
|
||||
}
|
||||
|
||||
void OpenAL_Factory::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);
|
||||
}
|
||||
|
||||
OpenAL_Factory::OpenAL_Factory(bool doSetup)
|
||||
: device(NULL), context(NULL), didSetup(doSetup)
|
||||
{
|
||||
needsUpdate = true;
|
||||
has3D = true;
|
||||
canLoadFile = false;
|
||||
canLoadStream = false;
|
||||
canLoadSource = true;
|
||||
|
||||
ALCdevice *Device;
|
||||
ALCcontext *Context;
|
||||
|
||||
if(doSetup)
|
||||
{
|
||||
// Set up sound system
|
||||
Device = alcOpenDevice(NULL);
|
||||
Context = alcCreateContext(Device, NULL);
|
||||
|
||||
if(!Device || !Context)
|
||||
fail("Failed to initialize context or device");
|
||||
|
||||
alcMakeContextCurrent(Context);
|
||||
|
||||
device = Device;
|
||||
context = Context;
|
||||
alDistanceModel(AL_LINEAR_DISTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenAL_Factory::update()
|
||||
{
|
||||
// Loop through all streaming sounds and update them
|
||||
StreamList::iterator it = streaming.begin();
|
||||
for(;it != streaming.end(); it++)
|
||||
(*it)->update();
|
||||
}
|
||||
|
||||
void OpenAL_Factory::notifyStreaming(OpenAL_Sound *snd)
|
||||
{
|
||||
// Add the sound to the streaming list
|
||||
streaming.push_back(snd);
|
||||
}
|
||||
|
||||
void OpenAL_Factory::notifyDelete(OpenAL_Sound *snd)
|
||||
{
|
||||
// Remove the sound from the stream list
|
||||
streaming.remove(snd);
|
||||
}
|
||||
|
||||
OpenAL_Factory::~OpenAL_Factory()
|
||||
{
|
||||
// Notify remaining streamed sounds that we're dying
|
||||
StreamList::iterator it = streaming.begin();
|
||||
for(;it != streaming.end(); it++)
|
||||
(*it)->notifyOwnerDeath();
|
||||
|
||||
// Deinitialize sound system
|
||||
if(didSetup)
|
||||
{
|
||||
alcMakeContextCurrent(NULL);
|
||||
if(context) alcDestroyContext((ALCcontext*)context);
|
||||
if(device) alcCloseDevice((ALCdevice*)device);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- OpenAL_Sound ----
|
||||
|
||||
void OpenAL_Sound::play()
|
||||
{
|
||||
setupBuffer();
|
||||
alSourcePlay(inst);
|
||||
checkALError("starting playback");
|
||||
}
|
||||
|
||||
void OpenAL_Sound::stop()
|
||||
{
|
||||
alSourceStop(inst);
|
||||
checkALError("stopping");
|
||||
}
|
||||
|
||||
void OpenAL_Sound::pause()
|
||||
{
|
||||
alSourcePause(inst);
|
||||
checkALError("pausing");
|
||||
}
|
||||
|
||||
bool OpenAL_Sound::isPlaying() const
|
||||
{
|
||||
ALint state;
|
||||
alGetSourcei(inst, AL_SOURCE_STATE, &state);
|
||||
|
||||
return state == AL_PLAYING;
|
||||
}
|
||||
|
||||
void OpenAL_Sound::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_Sound::setRange(float a, float b, float)
|
||||
{
|
||||
alSourcef(inst, AL_REFERENCE_DISTANCE, a);
|
||||
alSourcef(inst, AL_MAX_DISTANCE, b);
|
||||
checkALError("setting sound ranges");
|
||||
}
|
||||
|
||||
void OpenAL_Sound::setPos(float x, float y, float z)
|
||||
{
|
||||
alSource3f(inst, AL_POSITION, x, y, z);
|
||||
checkALError("setting position");
|
||||
}
|
||||
|
||||
void OpenAL_Sound::setPitch(float pitch)
|
||||
{
|
||||
alSourcef(inst, AL_PITCH, pitch);
|
||||
checkALError("setting pitch");
|
||||
}
|
||||
|
||||
void OpenAL_Sound::setRepeat(bool rep)
|
||||
{
|
||||
alSourcei(inst, AL_LOOPING, rep?AL_TRUE:AL_FALSE);
|
||||
}
|
||||
|
||||
void OpenAL_Sound::setRelative(bool rel)
|
||||
{
|
||||
alSourcei(inst, AL_SOURCE_RELATIVE, rel?AL_TRUE:AL_FALSE);
|
||||
checkALError("setting relative");
|
||||
}
|
||||
|
||||
SoundPtr OpenAL_Sound::clone()
|
||||
{
|
||||
setupBuffer();
|
||||
assert(!streaming && "cloning streamed sounds not supported");
|
||||
return SoundPtr(new OpenAL_Sound(bufferID[0], refCnt, owner));
|
||||
}
|
||||
|
||||
// Constructor used for cloned sounds
|
||||
OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref, OpenAL_Factory *fact)
|
||||
: refCnt(ref), streaming(false), owner(fact), ownerAlive(false)
|
||||
{
|
||||
// Increase the reference count
|
||||
assert(ref != NULL);
|
||||
(*refCnt)++;
|
||||
|
||||
// Set up buffer
|
||||
bufferID[0] = buf;
|
||||
bufNum = 1;
|
||||
|
||||
// Create a source
|
||||
alGenSources(1, &inst);
|
||||
checkALError("creating instance (clone)");
|
||||
alSourcei(inst, AL_BUFFER, bufferID[0]);
|
||||
checkALError("assigning buffer (clone)");
|
||||
}
|
||||
|
||||
// Constructor used for original (non-cloned) sounds
|
||||
OpenAL_Sound::OpenAL_Sound(SampleSourcePtr _input, OpenAL_Factory *fact)
|
||||
: refCnt(NULL), streaming(false), input(_input), owner(fact), ownerAlive(false)
|
||||
{
|
||||
// Create a source
|
||||
alGenSources(1, &inst);
|
||||
checkALError("creating source");
|
||||
|
||||
// By default, the sound starts out in a buffer-less mode. We don't
|
||||
// create a buffer until the sound is played. This gives the user
|
||||
// the chance to call setStreaming(true) first.
|
||||
}
|
||||
|
||||
void OpenAL_Sound::setupBuffer()
|
||||
{
|
||||
if(refCnt != NULL) return;
|
||||
|
||||
assert(input);
|
||||
|
||||
// Get the format
|
||||
getALFormat(input, fmt, rate);
|
||||
|
||||
// Create a cheap reference counter for the buffer
|
||||
refCnt = new int;
|
||||
*refCnt = 1;
|
||||
|
||||
if(streaming) bufNum = STREAM_BUF_NUM;
|
||||
else bufNum = 1;
|
||||
|
||||
// Set up the OpenAL buffer(s)
|
||||
alGenBuffers(bufNum, bufferID);
|
||||
checkALError("generating buffer(s)");
|
||||
assert(bufferID[0] != 0);
|
||||
|
||||
// STREAMING.
|
||||
if(streaming)
|
||||
{
|
||||
// Just queue all the buffers with data and exit. queueBuffer()
|
||||
// will work correctly also in the case where there is not
|
||||
// enough data to fill all the buffers.
|
||||
for(int i=0; i<bufNum; i++)
|
||||
queueBuffer(bufferID[i]);
|
||||
|
||||
// Notify the manager what we're doing
|
||||
owner->notifyStreaming(this);
|
||||
ownerAlive = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// NON-STREAMING. We have to load all the data and shove it into the
|
||||
// buffer.
|
||||
|
||||
// Does the stream support pointer operations?
|
||||
if(input->hasPtr)
|
||||
{
|
||||
// If so, we can read the data directly from the stream
|
||||
alBufferData(bufferID[0], fmt, input->getPtr(), input->size(), rate);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read the entire stream into a temporary buffer first
|
||||
Mangle::Stream::BufferStream buf(input, 128*1024);
|
||||
|
||||
// Then copy that into OpenAL
|
||||
alBufferData(bufferID[0], fmt, buf.getPtr(), buf.size(), rate);
|
||||
}
|
||||
checkALError("loading sound data");
|
||||
|
||||
// We're done with the input stream, release the pointer
|
||||
input.reset();
|
||||
|
||||
alSourcei(inst, AL_BUFFER, bufferID[0]);
|
||||
checkALError("assigning buffer");
|
||||
}
|
||||
|
||||
OpenAL_Sound::~OpenAL_Sound()
|
||||
{
|
||||
// Stop
|
||||
alSourceStop(inst);
|
||||
|
||||
// Return sound
|
||||
alDeleteSources(1, &inst);
|
||||
|
||||
// Notify the factory that we quit. You will hear from our union
|
||||
// rep. The bool check is to handle cases where the manager goes out
|
||||
// of scope before the sounds do. In that case, don't try to contact
|
||||
// the factory.
|
||||
if(ownerAlive)
|
||||
owner->notifyDelete(this);
|
||||
|
||||
// Decrease the reference counter
|
||||
if((-- (*refCnt)) == 0)
|
||||
{
|
||||
// We're the last owner. Delete the buffer(s) and the counter
|
||||
// itself.
|
||||
alDeleteBuffers(bufNum, bufferID);
|
||||
checkALError("deleting buffer");
|
||||
delete refCnt;
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
#ifndef MANGLE_SOUND_OPENAL_OUT_H
|
||||
#define MANGLE_SOUND_OPENAL_OUT_H
|
||||
|
||||
#include "../output.hpp"
|
||||
#include <list>
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
class OpenAL_Sound;
|
||||
|
||||
class OpenAL_Factory : public SoundFactory
|
||||
{
|
||||
void *device;
|
||||
void *context;
|
||||
bool didSetup;
|
||||
|
||||
// List of streaming sounds that need to be updated every frame.
|
||||
typedef std::list<OpenAL_Sound*> StreamList;
|
||||
StreamList streaming;
|
||||
|
||||
friend class OpenAL_Sound;
|
||||
void notifyStreaming(OpenAL_Sound*);
|
||||
void notifyDelete(OpenAL_Sound*);
|
||||
|
||||
public:
|
||||
/// Initialize object. Pass true (default) if you want the
|
||||
/// constructor to set up the current ALCdevice and ALCcontext for
|
||||
/// you.
|
||||
OpenAL_Factory(bool doSetup = true);
|
||||
~OpenAL_Factory();
|
||||
|
||||
SoundPtr load(const std::string &file) { assert(0); return SoundPtr(); }
|
||||
SoundPtr load(Stream::StreamPtr input) { assert(0); return SoundPtr(); }
|
||||
SoundPtr loadRaw(SampleSourcePtr input);
|
||||
|
||||
void update();
|
||||
void setListenerPos(float x, float y, float z,
|
||||
float fx, float fy, float fz,
|
||||
float ux, float uy, float uz);
|
||||
};
|
||||
|
||||
}} // namespaces
|
||||
#endif
|
@ -1,62 +0,0 @@
|
||||
#ifndef MANGLE_SOUND_SOURCE_H
|
||||
#define MANGLE_SOUND_SOURCE_H
|
||||
|
||||
#include <string>
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <assert.h>
|
||||
|
||||
#include "../stream/stream.hpp"
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
typedef boost::int32_t int32_t;
|
||||
|
||||
/// A stream containing raw sound data and information about the format
|
||||
class SampleSource : public Stream::Stream
|
||||
{
|
||||
protected:
|
||||
bool isEof;
|
||||
|
||||
public:
|
||||
SampleSource() : 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) = 0;
|
||||
|
||||
bool eof() const { return isEof; }
|
||||
|
||||
// Disabled functions by default. You can still override them in
|
||||
// subclasses.
|
||||
void seek(size_t pos) { assert(0); }
|
||||
size_t tell() const { assert(0); return 0; }
|
||||
size_t size() const { assert(0); return 0; }
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<SampleSource> SampleSourcePtr;
|
||||
|
||||
/// 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 SampleSourcePtr load(const std::string &file) = 0;
|
||||
|
||||
/// Load a sound input source from stream (if canLoadStream is true)
|
||||
virtual SampleSourcePtr load(Stream::StreamPtr input) = 0;
|
||||
|
||||
/// Virtual destructor
|
||||
virtual ~SampleSourceLoader() {}
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<SampleSourceLoader> SampleSourceLoaderPtr;
|
||||
|
||||
}} // namespaces
|
||||
#endif
|
@ -1,77 +0,0 @@
|
||||
#include "audiere_source.hpp"
|
||||
|
||||
#include "../../stream/clients/audiere_file.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace Mangle::Stream;
|
||||
|
||||
static void fail(const std::string &msg)
|
||||
{ throw std::runtime_error("Audiere exception: " + msg); }
|
||||
|
||||
using namespace audiere;
|
||||
using namespace Mangle::Sound;
|
||||
|
||||
// --- SampleSource ---
|
||||
|
||||
void AudiereSource::getInfo(Mangle::Sound::int32_t *rate,
|
||||
Mangle::Sound::int32_t *channels, Mangle::Sound::int32_t *bits)
|
||||
{
|
||||
SampleFormat fmt;
|
||||
int channels_, rate_;
|
||||
sample->getFormat(channels_, rate_, fmt);
|
||||
*channels = channels_;
|
||||
*rate = rate_;
|
||||
if(bits)
|
||||
{
|
||||
if(fmt == SF_U8)
|
||||
*bits = 8;
|
||||
else if(fmt == SF_S16)
|
||||
*bits = 16;
|
||||
else assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Constructors ---
|
||||
|
||||
AudiereSource::AudiereSource(const std::string &file)
|
||||
{
|
||||
sample = OpenSampleSource(file.c_str());
|
||||
|
||||
if(!sample)
|
||||
fail("Couldn't load file " + file);
|
||||
|
||||
doSetup();
|
||||
}
|
||||
|
||||
AudiereSource::AudiereSource(StreamPtr input)
|
||||
{
|
||||
// Use our Stream::AudiereFile implementation to convert a Mangle
|
||||
// 'Stream' to an Audiere 'File'
|
||||
sample = OpenSampleSource(new AudiereFile(input));
|
||||
if(!sample)
|
||||
fail("Couldn't load stream");
|
||||
|
||||
doSetup();
|
||||
}
|
||||
|
||||
AudiereSource::AudiereSource(audiere::SampleSourcePtr src)
|
||||
: sample(src)
|
||||
{ assert(sample); doSetup(); }
|
||||
|
||||
// Common function called from all constructors
|
||||
void AudiereSource::doSetup()
|
||||
{
|
||||
assert(sample);
|
||||
|
||||
SampleFormat fmt;
|
||||
int channels, rate;
|
||||
sample->getFormat(channels, rate, fmt);
|
||||
|
||||
// Calculate the size of one frame, and pass it to SampleReader.
|
||||
setup(GetSampleSize(fmt) * channels);
|
||||
|
||||
isSeekable = sample->isSeekable();
|
||||
hasPosition = true;
|
||||
hasSize = true;
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
#ifndef MANGLE_SOUND_AUDIERE_SOURCE_H
|
||||
#define MANGLE_SOUND_AUDIERE_SOURCE_H
|
||||
|
||||
#include "sample_reader.hpp"
|
||||
|
||||
// audiere.h from 1.9.4 (latest) release uses
|
||||
// cstring routines like strchr() and strlen() without
|
||||
// including cstring itself.
|
||||
#include <cstring>
|
||||
#include <audiere.h>
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
/// A sample source that decodes files using Audiere
|
||||
class AudiereSource : public SampleReader
|
||||
{
|
||||
audiere::SampleSourcePtr sample;
|
||||
|
||||
size_t readSamples(void *data, size_t length)
|
||||
{ return sample->read(length, data); }
|
||||
|
||||
void doSetup();
|
||||
|
||||
public:
|
||||
/// Decode the given sound file
|
||||
AudiereSource(const std::string &file);
|
||||
|
||||
/// Decode the given sound stream
|
||||
AudiereSource(Mangle::Stream::StreamPtr src);
|
||||
|
||||
/// Read directly from an existing audiere::SampleSource
|
||||
AudiereSource(audiere::SampleSourcePtr src);
|
||||
|
||||
void getInfo(int32_t *rate, int32_t *channels, int32_t *bits);
|
||||
|
||||
void seek(size_t pos) { sample->setPosition(pos/frameSize); }
|
||||
size_t tell() const { return sample->getPosition()*frameSize; }
|
||||
size_t size() const { return sample->getLength()*frameSize; }
|
||||
};
|
||||
|
||||
#include "loadertemplate.hpp"
|
||||
|
||||
/// A factory that loads AudiereSources from file and stream
|
||||
typedef SSL_Template<AudiereSource,true,true> AudiereLoader;
|
||||
|
||||
}} // Namespace
|
||||
#endif
|
@ -1,189 +0,0 @@
|
||||
#include "ffmpeg_source.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
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];
|
||||
|
||||
static void fail(const std::string &msg)
|
||||
{ throw std::runtime_error("FFMpeg exception: " + msg); }
|
||||
|
||||
// --- Loader ---
|
||||
|
||||
static bool init = false;
|
||||
|
||||
FFMpegLoader::FFMpegLoader(bool setup)
|
||||
{
|
||||
if(setup && !init)
|
||||
{
|
||||
av_register_all();
|
||||
av_log_set_level(AV_LOG_ERROR);
|
||||
init = true;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Source ---
|
||||
|
||||
FFMpegSource::FFMpegSource(const std::string &file)
|
||||
{
|
||||
std::string msg;
|
||||
AVCodec *codec;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
FFMpegSource::~FFMpegSource()
|
||||
{
|
||||
avcodec_close(CodecCtx);
|
||||
av_close_input_file(FmtCtx);
|
||||
}
|
||||
|
||||
void FFMpegSource::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;
|
||||
}
|
||||
|
||||
size_t FFMpegSource::read(void *data, size_t length)
|
||||
{
|
||||
if(isEof) return 0;
|
||||
|
||||
size_t left = length;
|
||||
uint8_t *outPtr = (uint8_t*)data;
|
||||
|
||||
// First, copy over any stored data we might be sitting on
|
||||
{
|
||||
size_t s = storage.size();
|
||||
size_t 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?
|
||||
assert(s>= copy);
|
||||
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((int)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
|
||||
size_t 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. This loop repeats as long left is > 0
|
||||
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)
|
||||
isEof = true;
|
||||
|
||||
return length - left;
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
#ifndef MANGLE_SOUND_FFMPEG_H
|
||||
#define MANGLE_SOUND_FFMPEG_H
|
||||
|
||||
#include "../source.hpp"
|
||||
#include <vector>
|
||||
#include <assert.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <avcodec.h>
|
||||
#include <avformat.h>
|
||||
}
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
class FFMpegSource : public SampleSource
|
||||
{
|
||||
AVFormatContext *FmtCtx;
|
||||
AVCodecContext *CodecCtx;
|
||||
unsigned int StreamNum;
|
||||
|
||||
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(Mangle::Stream::StreamPtr 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.hpp"
|
||||
|
||||
/// A factory that loads FFMpegSources from file
|
||||
class FFMpegLoader : public SSL_Template<FFMpegSource,false,true>
|
||||
{
|
||||
public:
|
||||
|
||||
/// Sets up the libavcodec library. If you want to do your own
|
||||
/// setup, send a setup=false parameter.
|
||||
FFMpegLoader(bool setup=true);
|
||||
};
|
||||
|
||||
}} // namespaces
|
||||
#endif
|
@ -1,48 +0,0 @@
|
||||
#include "libsndfile.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <sndfile.h>
|
||||
|
||||
using namespace Mangle::Stream;
|
||||
|
||||
static void fail(const std::string &msg)
|
||||
{ throw std::runtime_error("Mangle::libsndfile: " + msg); }
|
||||
|
||||
using namespace Mangle::Sound;
|
||||
|
||||
void SndFileSource::getInfo(int32_t *_rate, int32_t *_channels, int32_t *_bits)
|
||||
{
|
||||
*_rate = rate;
|
||||
*_channels = channels;
|
||||
*_bits = bits;
|
||||
}
|
||||
|
||||
size_t SndFileSource::readSamples(void *data, size_t length)
|
||||
{
|
||||
// readf_* reads entire frames, including channels
|
||||
return sf_readf_short((SNDFILE*)handle, (short*)data, length);
|
||||
}
|
||||
|
||||
SndFileSource::SndFileSource(const std::string &file)
|
||||
{
|
||||
SF_INFO info;
|
||||
info.format = 0;
|
||||
handle = sf_open(file.c_str(), SFM_READ, &info);
|
||||
if(handle == NULL)
|
||||
fail("Failed to open " + file);
|
||||
|
||||
// I THINK that using sf_read_short forces the library to convert to
|
||||
// 16 bits no matter what, but the libsndfile docs aren't exactly
|
||||
// very clear on this point.
|
||||
channels = info.channels;
|
||||
rate = info.samplerate;
|
||||
bits = 16;
|
||||
|
||||
// 16 bits per sample times number of channels
|
||||
setup(2*channels);
|
||||
}
|
||||
|
||||
SndFileSource::~SndFileSource()
|
||||
{
|
||||
sf_close((SNDFILE*)handle);
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
#ifndef MANGLE_SOUND_SNDFILE_SOURCE_H
|
||||
#define MANGLE_SOUND_SNDFILE_SOURCE_H
|
||||
|
||||
#include "sample_reader.hpp"
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
/// A sample source that decodes files using libsndfile. Supports most
|
||||
/// formats except mp3.
|
||||
class SndFileSource : public SampleReader
|
||||
{
|
||||
void *handle;
|
||||
int channels, rate, bits;
|
||||
|
||||
size_t readSamples(void *data, size_t length);
|
||||
|
||||
public:
|
||||
/// Decode the given sound file
|
||||
SndFileSource(const std::string &file);
|
||||
|
||||
/// Decode the given sound stream (not supported)
|
||||
SndFileSource(Mangle::Stream::StreamPtr src) { assert(0); }
|
||||
|
||||
~SndFileSource();
|
||||
|
||||
void getInfo(int32_t *rate, int32_t *channels, int32_t *bits);
|
||||
};
|
||||
|
||||
#include "loadertemplate.hpp"
|
||||
|
||||
/// A factory that loads SndFileSources from file and stream
|
||||
typedef SSL_Template<SndFileSource,false,true> SndFileLoader;
|
||||
|
||||
}} // Namespace
|
||||
#endif
|
@ -1,28 +0,0 @@
|
||||
#ifndef SSL_TEMPL_H
|
||||
#define SSL_TEMPL_H
|
||||
|
||||
template <class SourceT, bool stream, bool file>
|
||||
class SSL_Template : public SampleSourceLoader
|
||||
{
|
||||
public:
|
||||
|
||||
SSL_Template()
|
||||
{
|
||||
canLoadStream = stream;
|
||||
canLoadFile = file;
|
||||
}
|
||||
|
||||
SampleSourcePtr load(const std::string &filename)
|
||||
{
|
||||
assert(canLoadFile);
|
||||
return SampleSourcePtr(new SourceT(filename));
|
||||
}
|
||||
|
||||
SampleSourcePtr load(Stream::StreamPtr input)
|
||||
{
|
||||
assert(canLoadStream);
|
||||
return SampleSourcePtr(new SourceT(input));
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
@ -1,115 +0,0 @@
|
||||
#include "mpg123_source.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <mpg123.h>
|
||||
|
||||
using namespace Mangle::Stream;
|
||||
|
||||
/*
|
||||
TODOs:
|
||||
|
||||
- mpg123 impressively enough supports custom stream reading. Which
|
||||
means we could (and SHOULD!) support reading from Mangle::Streams
|
||||
as well. But I'll save it til I need it.
|
||||
|
||||
An alternative way to do this is through feeding (which they also
|
||||
support), but that's more messy.
|
||||
|
||||
- the library also supports output, via various other sources,
|
||||
including ALSA, OSS, PortAudio, PulseAudio and SDL. Using this
|
||||
library as a pure output library (if that is possible) would be a
|
||||
nice shortcut over using those libraries - OTOH it's another
|
||||
dependency.
|
||||
|
||||
- we could implement seek(), tell() and size(), but they aren't
|
||||
really necessary. Furthermore, since the returned size is only a
|
||||
guess, it is not safe to rely on it.
|
||||
*/
|
||||
|
||||
static void fail(const std::string &msg)
|
||||
{ throw std::runtime_error("Mangle::Mpg123 exception: " + msg); }
|
||||
|
||||
static void checkError(int err, void *mh = NULL)
|
||||
{
|
||||
if(err != MPG123_OK)
|
||||
{
|
||||
std::string msg;
|
||||
if(mh) msg = mpg123_strerror((mpg123_handle*)mh);
|
||||
else msg = mpg123_plain_strerror(err);
|
||||
fail(msg);
|
||||
}
|
||||
}
|
||||
|
||||
using namespace Mangle::Sound;
|
||||
|
||||
void Mpg123Source::getInfo(int32_t *pRate, int32_t *pChannels, int32_t *pBits)
|
||||
{
|
||||
// Use the values we found in the constructor
|
||||
*pRate = rate;
|
||||
*pChannels = channels;
|
||||
*pBits = bits;
|
||||
}
|
||||
|
||||
size_t Mpg123Source::read(void *data, size_t length)
|
||||
{
|
||||
size_t done;
|
||||
// This is extraordinarily nice. I like this library.
|
||||
int err = mpg123_read((mpg123_handle*)mh, (unsigned char*)data, length, &done);
|
||||
assert(done <= length);
|
||||
if(err == MPG123_DONE)
|
||||
isEof = true;
|
||||
else
|
||||
checkError(err, mh);
|
||||
return done;
|
||||
}
|
||||
|
||||
Mpg123Loader::Mpg123Loader(bool setup)
|
||||
{
|
||||
// Do as we're told
|
||||
if(setup)
|
||||
{
|
||||
int err = mpg123_init();
|
||||
checkError(err);
|
||||
}
|
||||
didSetup = setup;
|
||||
}
|
||||
|
||||
Mpg123Loader::~Mpg123Loader()
|
||||
{
|
||||
// Deinitialize the library on exit
|
||||
if(didSetup)
|
||||
mpg123_exit();
|
||||
}
|
||||
|
||||
Mpg123Source::Mpg123Source(const std::string &file)
|
||||
{
|
||||
int err;
|
||||
|
||||
// Create a new handle
|
||||
mh = mpg123_new(NULL, &err);
|
||||
if(mh == NULL)
|
||||
checkError(err, mh);
|
||||
|
||||
mpg123_handle *mhh = (mpg123_handle*)mh;
|
||||
|
||||
// Open the file (hack around constness)
|
||||
err = mpg123_open(mhh, (char*)file.c_str());
|
||||
checkError(err, mh);
|
||||
|
||||
// Get the format
|
||||
int encoding;
|
||||
err = mpg123_getformat(mhh, &rate, &channels, &encoding);
|
||||
checkError(err, mh);
|
||||
if(encoding != MPG123_ENC_SIGNED_16)
|
||||
fail("Unsupported encoding in " + file);
|
||||
|
||||
// This is the only bit size we support.
|
||||
bits = 16;
|
||||
}
|
||||
|
||||
Mpg123Source::~Mpg123Source()
|
||||
{
|
||||
mpg123_close((mpg123_handle*)mh);
|
||||
mpg123_delete((mpg123_handle*)mh);
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
#ifndef MANGLE_SOUND_MPG123_SOURCE_H
|
||||
#define MANGLE_SOUND_MPG123_SOURCE_H
|
||||
|
||||
#include "../source.hpp"
|
||||
#include <assert.h>
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
/// A sample source that decodes files using libmpg123. Only supports
|
||||
/// MP3 files.
|
||||
class Mpg123Source : public SampleSource
|
||||
{
|
||||
void *mh;
|
||||
long int rate;
|
||||
int channels, bits;
|
||||
|
||||
public:
|
||||
/// Decode the given sound file
|
||||
Mpg123Source(const std::string &file);
|
||||
|
||||
/// Needed by SSL_Template but not yet supported
|
||||
Mpg123Source(Mangle::Stream::StreamPtr data)
|
||||
{ assert(0); }
|
||||
|
||||
~Mpg123Source();
|
||||
|
||||
void getInfo(int32_t *rate, int32_t *channels, int32_t *bits);
|
||||
size_t read(void *data, size_t length);
|
||||
};
|
||||
|
||||
#include "loadertemplate.hpp"
|
||||
|
||||
/// A factory that loads Mpg123Sources from file and stream
|
||||
struct Mpg123Loader : SSL_Template<Mpg123Source,false,true>
|
||||
{
|
||||
/** Sets up libmpg123 for you, and closes it on destruction. If you
|
||||
want to do this yourself, send setup=false.
|
||||
*/
|
||||
Mpg123Loader(bool setup=true);
|
||||
~Mpg123Loader();
|
||||
private:
|
||||
bool didSetup;
|
||||
};
|
||||
|
||||
}} // Namespace
|
||||
#endif
|
@ -1,99 +0,0 @@
|
||||
#include "sample_reader.hpp"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
using namespace Mangle::Sound;
|
||||
|
||||
void SampleReader::setup(int size)
|
||||
{
|
||||
pullSize = 0;
|
||||
frameSize = size;
|
||||
pullOver = new char[size];
|
||||
}
|
||||
|
||||
SampleReader::~SampleReader()
|
||||
{
|
||||
if(pullOver)
|
||||
delete[] pullOver;
|
||||
}
|
||||
|
||||
size_t SampleReader::read(void *_data, size_t length)
|
||||
{
|
||||
if(isEof) return 0;
|
||||
char *data = (char*)_data;
|
||||
|
||||
// Pullsize holds the number of bytes that were copied "extra" at
|
||||
// the end of LAST round. If non-zero, it also means there is data
|
||||
// left in the pullOver buffer.
|
||||
if(pullSize)
|
||||
{
|
||||
// Amount of data left
|
||||
size_t doRead = frameSize - pullSize;
|
||||
assert(doRead > 0);
|
||||
|
||||
// Make sure we don't read more than we're supposed to
|
||||
if(doRead > length) doRead = length;
|
||||
|
||||
memcpy(data, pullOver+pullSize, doRead);
|
||||
|
||||
// Update the number of bytes now copied
|
||||
pullSize += doRead;
|
||||
assert(pullSize <= frameSize);
|
||||
|
||||
if(pullSize < frameSize)
|
||||
{
|
||||
// There is STILL data left in the pull buffer, and we've
|
||||
// done everything we were supposed to. Leave it and return.
|
||||
assert(doRead == length);
|
||||
return doRead;
|
||||
}
|
||||
|
||||
// Set up variables for further reading below. No need to update
|
||||
// pullSize, it is overwritten anyway.
|
||||
length -= doRead;
|
||||
data += doRead;
|
||||
}
|
||||
|
||||
// Number of whole frames
|
||||
size_t frames = length / frameSize;
|
||||
|
||||
// Read the data
|
||||
size_t res = readSamples(data, frames);
|
||||
assert(res <= frames);
|
||||
|
||||
// Total bytes read
|
||||
size_t num = res*frameSize;
|
||||
data += num;
|
||||
|
||||
if(res < frames)
|
||||
{
|
||||
// End of stream.
|
||||
isEof = true;
|
||||
// Determine how much we read
|
||||
return data-(char*)_data;
|
||||
}
|
||||
|
||||
// Determine the overshoot
|
||||
pullSize = length - num;
|
||||
assert(pullSize < frameSize && pullSize >= 0);
|
||||
|
||||
// Are we missing data?
|
||||
if(pullSize)
|
||||
{
|
||||
// Fill in one sample
|
||||
res = readSamples(pullOver,1);
|
||||
assert(res == 1 || res == 0);
|
||||
if(res)
|
||||
{
|
||||
// Move as much as we can into the output buffer
|
||||
memcpy(data, pullOver, pullSize);
|
||||
data += pullSize;
|
||||
}
|
||||
else
|
||||
// Failed reading, we're out of data
|
||||
isEof = true;
|
||||
}
|
||||
|
||||
// Return the total number of bytes stored
|
||||
return data-(char*)_data;
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
#ifndef MANGLE_SOUND_SAMPLE_READER_H
|
||||
#define MANGLE_SOUND_SAMPLE_READER_H
|
||||
|
||||
#include "../source.hpp"
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
/* This is a helper base class for other SampleSource
|
||||
implementations. Certain sources (like Audiere and libsndfile)
|
||||
insist on reading whole samples rather than bytes. This class
|
||||
compensates for that, and allows you to read bytes rather than
|
||||
samples.
|
||||
|
||||
There are two ways for subclasses to use this class. EITHER call
|
||||
setup() with the size of frameSize. This will allocate a buffer,
|
||||
which the destructor frees. OR set frameSize manually and
|
||||
manipulate the pullOver pointer yourself. In that case you MUST
|
||||
reset it to NULL if you don't want the destructor to call
|
||||
delete[] on it.
|
||||
*/
|
||||
class SampleReader : public SampleSource
|
||||
{
|
||||
// How much of the above buffer is in use.
|
||||
int pullSize;
|
||||
|
||||
protected:
|
||||
// Pullover buffer
|
||||
char* pullOver;
|
||||
|
||||
// Size of one frame, in bytes. This is also the size of the
|
||||
// pullOver buffer.
|
||||
int frameSize;
|
||||
|
||||
// The parameter gives the size of one sample/frame, in bytes.
|
||||
void setup(int);
|
||||
|
||||
// Read the given number of samples, in multiples of frameSize. Does
|
||||
// not have to set or respect isEof.
|
||||
virtual size_t readSamples(void *data, size_t num) = 0;
|
||||
|
||||
public:
|
||||
SampleReader() : pullSize(0), pullOver(NULL) {}
|
||||
~SampleReader();
|
||||
size_t read(void *data, size_t length);
|
||||
};
|
||||
}} // Namespace
|
||||
#endif
|
@ -1,47 +0,0 @@
|
||||
#ifndef MANGLE_SOUND_STREAMSOURCE_H
|
||||
#define MANGLE_SOUND_STREAMSOURCE_H
|
||||
|
||||
#include "../source.hpp"
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
/// A class for reading raw samples directly from a stream.
|
||||
class Stream2Samples : public SampleSource
|
||||
{
|
||||
Mangle::Stream::StreamPtr inp;
|
||||
int32_t rate, channels, bits;
|
||||
|
||||
public:
|
||||
Stream2Samples(Mangle::Stream::StreamPtr _inp, int32_t _rate, int32_t _channels, int32_t _bits)
|
||||
: inp(_inp), rate(_rate), channels(_channels), bits(_bits)
|
||||
{
|
||||
isSeekable = inp->isSeekable;
|
||||
hasPosition = inp->hasPosition;
|
||||
hasSize = inp->hasSize;
|
||||
hasPtr = inp->hasPtr;
|
||||
}
|
||||
|
||||
/// Get the sample rate, number of channels, and bits per
|
||||
/// sample. NULL parameters are ignored.
|
||||
void getInfo(int32_t *_rate, int32_t *_channels, int32_t *_bits)
|
||||
{
|
||||
if(_rate) *_rate = rate;
|
||||
if(_channels) *_channels = channels;
|
||||
if(_bits) *_bits = bits;
|
||||
}
|
||||
|
||||
size_t read(void *out, size_t count)
|
||||
{ return inp->read(out, count); }
|
||||
|
||||
void seek(size_t pos) { inp->seek(pos); }
|
||||
size_t tell() const { return inp->tell(); }
|
||||
size_t size() const { return inp->size(); }
|
||||
bool eof() const { return inp->eof(); }
|
||||
const void *getPtr() { return inp->getPtr(); }
|
||||
const void *getPtr(size_t size) { return inp->getPtr(size); }
|
||||
const void *getPtr(size_t pos, size_t size) { return inp->getPtr(pos, size); }
|
||||
};
|
||||
|
||||
}} // namespaces
|
||||
#endif
|
@ -1,99 +0,0 @@
|
||||
#include "wav_source.hpp"
|
||||
|
||||
#include "../../stream/servers/file_stream.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace Mangle::Stream;
|
||||
using namespace Mangle::Sound;
|
||||
|
||||
static void fail(const std::string &msg)
|
||||
{ throw std::runtime_error("Mangle::Wav exception: " + msg); }
|
||||
|
||||
void WavSource::getInfo(int32_t *pRate, int32_t *pChannels, int32_t *pBits)
|
||||
{
|
||||
// Use the values we found in the constructor
|
||||
*pRate = rate;
|
||||
*pChannels = channels;
|
||||
*pBits = bits;
|
||||
}
|
||||
|
||||
void WavSource::seek(size_t pos)
|
||||
{
|
||||
// Seek the stream and set 'left'
|
||||
assert(isSeekable);
|
||||
if(pos > total) pos = total;
|
||||
input->seek(dataOffset + pos);
|
||||
left = total-pos;
|
||||
}
|
||||
|
||||
size_t WavSource::read(void *data, size_t length)
|
||||
{
|
||||
if(length > left)
|
||||
length = left;
|
||||
size_t read = input->read(data, length);
|
||||
if(read < length)
|
||||
// Something went wrong
|
||||
fail("WAV read error");
|
||||
return length;
|
||||
}
|
||||
|
||||
void WavSource::open(Mangle::Stream::StreamPtr data)
|
||||
{
|
||||
input = data;
|
||||
|
||||
hasPosition = true;
|
||||
hasSize = true;
|
||||
// If we can check position and seek in the input stream, then we
|
||||
// can seek the wav data too.
|
||||
isSeekable = input->isSeekable && input->hasPosition;
|
||||
|
||||
// Read header
|
||||
unsigned int val;
|
||||
|
||||
input->read(&val,4); // header
|
||||
if(val != 0x46464952) // "RIFF"
|
||||
fail("Not a WAV file");
|
||||
|
||||
input->read(&val,4); // size (ignored)
|
||||
input->read(&val,4); // file format
|
||||
if(val != 0x45564157) // "WAVE"
|
||||
fail("Not a valid WAV file");
|
||||
|
||||
input->read(&val,4); // "fmt "
|
||||
input->read(&val,4); // chunk size (must be 16)
|
||||
if(val != 16)
|
||||
fail("Unsupported WAV format");
|
||||
|
||||
input->read(&val,2);
|
||||
if(val != 1)
|
||||
fail("Non-PCM (compressed) WAV files not supported");
|
||||
|
||||
// Sound data specification
|
||||
channels = 0;
|
||||
input->read(&channels,2);
|
||||
input->read(&rate, 4);
|
||||
|
||||
// Skip next 6 bytes
|
||||
input->read(&val, 4);
|
||||
input->read(&val, 2);
|
||||
|
||||
// Bits per sample
|
||||
bits = 0;
|
||||
input->read(&bits,2);
|
||||
|
||||
input->read(&val,4); // Data header
|
||||
if(val != 0x61746164) // "data"
|
||||
fail("Expected data block");
|
||||
|
||||
// Finally, read the data size
|
||||
input->read(&total,4);
|
||||
left = total;
|
||||
|
||||
// Store the beginning of the data block for later
|
||||
if(input->hasPosition)
|
||||
dataOffset = input->tell();
|
||||
}
|
||||
|
||||
WavSource::WavSource(const std::string &file)
|
||||
{ open(StreamPtr(new FileStream(file))); }
|
@ -1,49 +0,0 @@
|
||||
#ifndef MANGLE_SOUND_WAV_SOURCE_H
|
||||
#define MANGLE_SOUND_WAV_SOURCE_H
|
||||
|
||||
#include "../source.hpp"
|
||||
#include <assert.h>
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
/// WAV file decoder. Has no external library dependencies.
|
||||
class WavSource : public SampleSource
|
||||
{
|
||||
// Sound info
|
||||
uint32_t rate, channels, bits;
|
||||
|
||||
// Total size (of output) and bytes left
|
||||
uint32_t total, left;
|
||||
|
||||
// Offset in input of the beginning of the data block
|
||||
size_t dataOffset;
|
||||
|
||||
Mangle::Stream::StreamPtr input;
|
||||
|
||||
void open(Mangle::Stream::StreamPtr);
|
||||
|
||||
public:
|
||||
/// Decode the given sound file
|
||||
WavSource(const std::string&);
|
||||
|
||||
/// Decode from stream
|
||||
WavSource(Mangle::Stream::StreamPtr s)
|
||||
{ open(s); }
|
||||
|
||||
void getInfo(int32_t *rate, int32_t *channels, int32_t *bits);
|
||||
size_t read(void *data, size_t length);
|
||||
|
||||
void seek(size_t);
|
||||
size_t tell() const { return total-left; }
|
||||
size_t size() const { return total; }
|
||||
bool eof() const { return left > 0; }
|
||||
};
|
||||
|
||||
#include "loadertemplate.hpp"
|
||||
|
||||
/// A factory that loads WavSources from file and stream
|
||||
typedef SSL_Template<WavSource,true,true> WavLoader;
|
||||
|
||||
}} // Namespace
|
||||
#endif
|
@ -1 +0,0 @@
|
||||
*_test
|
@ -1,38 +0,0 @@
|
||||
GCC=g++ -I../ -Wall
|
||||
|
||||
all: audiere_source_test ffmpeg_source_test openal_output_test openal_audiere_test openal_ffmpeg_test openal_mpg123_test openal_sndfile_test wav_source_test openal_various_test
|
||||
|
||||
L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat)
|
||||
I_FFMPEG=-I/usr/include/libavcodec -I/usr/include/libavformat
|
||||
L_OPENAL=$(shell pkg-config --libs openal)
|
||||
L_AUDIERE=-laudiere
|
||||
|
||||
wav_source_test: wav_source_test.cpp ../sources/wav_source.cpp
|
||||
$(GCC) $^ -o $@
|
||||
|
||||
openal_various_test: openal_various_test.cpp ../sources/mpg123_source.cpp ../sources/wav_source.cpp ../outputs/openal_out.cpp
|
||||
$(GCC) $^ -o $@ -lmpg123 ${L_OPENAL}
|
||||
|
||||
openal_audiere_test: openal_audiere_test.cpp ../sources/audiere_source.cpp ../sources/sample_reader.cpp ../outputs/openal_out.cpp ../../stream/clients/audiere_file.cpp
|
||||
$(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL)
|
||||
|
||||
openal_ffmpeg_test: openal_ffmpeg_test.cpp ../sources/ffmpeg_source.cpp ../outputs/openal_out.cpp
|
||||
$(GCC) $^ -o $@ $(L_FFMPEG) $(L_OPENAL) $(I_FFMPEG)
|
||||
|
||||
openal_mpg123_test: openal_mpg123_test.cpp ../sources/mpg123_source.cpp ../outputs/openal_out.cpp
|
||||
$(GCC) $^ -o $@ -lmpg123 ${L_OPENAL}
|
||||
|
||||
openal_sndfile_test: openal_sndfile_test.cpp ../sources/libsndfile.cpp ../sources/sample_reader.cpp ../outputs/openal_out.cpp
|
||||
$(GCC) $^ -o $@ -lsndfile ${L_OPENAL}
|
||||
|
||||
openal_output_test: openal_output_test.cpp ../outputs/openal_out.cpp
|
||||
$(GCC) $^ -o $@ $(L_OPENAL)
|
||||
|
||||
audiere_source_test: audiere_source_test.cpp ../sources/audiere_source.cpp ../../stream/clients/audiere_file.cpp ../sources/sample_reader.cpp
|
||||
$(GCC) $^ -o $@ $(L_AUDIERE)
|
||||
|
||||
ffmpeg_source_test: ffmpeg_source_test.cpp ../sources/ffmpeg_source.cpp
|
||||
$(GCC) $^ -o $@ $(L_FFMPEG) $(I_FFMPEG)
|
||||
|
||||
clean:
|
||||
rm *_test
|
@ -1,68 +0,0 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "../../stream/servers/file_stream.hpp"
|
||||
#include "../sources/audiere_source.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace Mangle::Stream;
|
||||
using namespace Mangle::Sound;
|
||||
|
||||
// Contents and size of cow.raw
|
||||
void *orig;
|
||||
size_t orig_size;
|
||||
|
||||
void run(SampleSourcePtr &src)
|
||||
{
|
||||
size_t ss = src->size();
|
||||
assert(ss == orig_size);
|
||||
|
||||
cout << "Source size: " << ss << endl;
|
||||
int rate, channels, bits;
|
||||
src->getInfo(&rate, &channels, &bits);
|
||||
cout << "rate=" << rate << "\nchannels=" << channels
|
||||
<< "\nbits=" << bits << endl;
|
||||
|
||||
cout << "Reading entire buffer into memory\n";
|
||||
void *buf = malloc(ss);
|
||||
src->read(buf, ss);
|
||||
|
||||
cout << "Comparing...\n";
|
||||
if(memcmp(buf, orig, ss) != 0)
|
||||
{
|
||||
cout << "Oops!\n";
|
||||
assert(0);
|
||||
}
|
||||
|
||||
cout << "Done\n";
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
cout << "Reading cow.raw first\n";
|
||||
FileStream tmp("cow.raw");
|
||||
orig_size = tmp.size();
|
||||
cout << "Size: " << orig_size << endl;
|
||||
orig = malloc(orig_size);
|
||||
tmp.read(orig, orig_size);
|
||||
cout << "Done\n";
|
||||
}
|
||||
|
||||
{
|
||||
cout << "\nLoading cow.wav by filename:\n";
|
||||
SampleSourcePtr cow_file( new AudiereSource("cow.wav") );
|
||||
run(cow_file);
|
||||
}
|
||||
|
||||
{
|
||||
cout << "\nLoading cow.wav by stream:\n";
|
||||
StreamPtr inp( new FileStream("cow.wav") );
|
||||
SampleSourcePtr cow_stream( new AudiereSource(inp) );
|
||||
run(cow_stream);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Binary file not shown.
Binary file not shown.
@ -1,62 +0,0 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "../../stream/servers/file_stream.hpp"
|
||||
#include "../sources/ffmpeg_source.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace Mangle::Stream;
|
||||
using namespace Mangle::Sound;
|
||||
|
||||
// Contents and size of cow.raw
|
||||
void *orig;
|
||||
size_t orig_size;
|
||||
|
||||
void run(SampleSourcePtr &src)
|
||||
{
|
||||
int rate, channels, bits;
|
||||
src->getInfo(&rate, &channels, &bits);
|
||||
cout << "rate=" << rate << "\nchannels=" << channels
|
||||
<< "\nbits=" << bits << endl;
|
||||
|
||||
cout << "Reading entire buffer into memory\n";
|
||||
void *buf = malloc(orig_size);
|
||||
size_t ss = src->read(buf, orig_size);
|
||||
cout << "Actually read: " << ss << endl;
|
||||
assert(ss == orig_size);
|
||||
|
||||
cout << "Comparing...\n";
|
||||
if(memcmp(buf, orig, ss) != 0)
|
||||
{
|
||||
cout << "Oops!\n";
|
||||
assert(0);
|
||||
}
|
||||
|
||||
cout << "Done\n";
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
cout << "Reading cow.raw first\n";
|
||||
FileStream tmp("cow.raw");
|
||||
orig_size = tmp.size();
|
||||
cout << "Size: " << orig_size << endl;
|
||||
orig = malloc(orig_size);
|
||||
tmp.read(orig, orig_size);
|
||||
cout << "Done\n";
|
||||
}
|
||||
|
||||
// Initializes the library, not used for anything else.
|
||||
FFMpegLoader fm;
|
||||
|
||||
{
|
||||
cout << "\nLoading cow.wav by filename:\n";
|
||||
SampleSourcePtr cow_file( new FFMpegSource("cow.wav") );
|
||||
run(cow_file);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
|
||||
#include "../../stream/servers/file_stream.hpp"
|
||||
#include "../filters/openal_audiere.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace Mangle::Stream;
|
||||
using namespace Mangle::Sound;
|
||||
|
||||
OpenAL_Audiere_Factory mg;
|
||||
|
||||
void play(const char* name, bool stream=false)
|
||||
{
|
||||
// Only load streams if the backend supports it
|
||||
if(stream && !mg.canLoadStream)
|
||||
return;
|
||||
|
||||
cout << "Playing " << name;
|
||||
if(stream) cout << " (from stream)";
|
||||
cout << "\n";
|
||||
|
||||
SoundPtr snd;
|
||||
|
||||
try
|
||||
{
|
||||
if(stream)
|
||||
snd = mg.load(StreamPtr(new FileStream(name)));
|
||||
else
|
||||
snd = mg.load(name);
|
||||
|
||||
snd->play();
|
||||
|
||||
while(snd->isPlaying())
|
||||
{
|
||||
usleep(10000);
|
||||
if(mg.needsUpdate) mg.update();
|
||||
}
|
||||
}
|
||||
catch(exception &e)
|
||||
{
|
||||
cout << " ERROR: " << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
play("cow.wav");
|
||||
play("owl.ogg");
|
||||
play("cow.wav", true);
|
||||
return 0;
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
|
||||
#include "../../stream/servers/file_stream.hpp"
|
||||
#include "../filters/openal_ffmpeg.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace Mangle::Stream;
|
||||
using namespace Mangle::Sound;
|
||||
|
||||
OpenAL_FFMpeg_Factory mg;
|
||||
|
||||
void play(const char* name, bool stream=false)
|
||||
{
|
||||
// Only load streams if the backend supports it
|
||||
if(stream && !mg.canLoadStream)
|
||||
return;
|
||||
|
||||
cout << "Playing " << name;
|
||||
if(stream) cout << " (from stream)";
|
||||
cout << "\n";
|
||||
|
||||
SoundPtr snd;
|
||||
|
||||
try
|
||||
{
|
||||
if(stream)
|
||||
snd = mg.load(StreamPtr(new FileStream(name)));
|
||||
else
|
||||
snd = mg.load(name);
|
||||
|
||||
snd->play();
|
||||
|
||||
while(snd->isPlaying())
|
||||
{
|
||||
usleep(10000);
|
||||
if(mg.needsUpdate) mg.update();
|
||||
}
|
||||
}
|
||||
catch(exception &e)
|
||||
{
|
||||
cout << " ERROR: " << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
play("cow.wav");
|
||||
play("owl.ogg");
|
||||
play("cow.wav", true);
|
||||
return 0;
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
|
||||
#include "../../stream/servers/file_stream.hpp"
|
||||
#include "../filters/openal_mpg123.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace Mangle::Stream;
|
||||
using namespace Mangle::Sound;
|
||||
|
||||
OpenAL_Mpg123_Factory mg;
|
||||
|
||||
void play(const char* name, bool stream=false)
|
||||
{
|
||||
// Only load streams if the backend supports it
|
||||
if(stream && !mg.canLoadStream)
|
||||
return;
|
||||
|
||||
cout << "Playing " << name;
|
||||
if(stream) cout << " (from stream)";
|
||||
cout << "\n";
|
||||
|
||||
SoundPtr snd;
|
||||
|
||||
try
|
||||
{
|
||||
if(stream)
|
||||
snd = mg.load(StreamPtr(new FileStream(name)));
|
||||
else
|
||||
snd = mg.load(name);
|
||||
|
||||
snd->setStreaming(true);
|
||||
snd->play();
|
||||
|
||||
while(snd->isPlaying())
|
||||
{
|
||||
usleep(10000);
|
||||
if(mg.needsUpdate) mg.update();
|
||||
}
|
||||
}
|
||||
catch(exception &e)
|
||||
{
|
||||
cout << " ERROR: " << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char**argv)
|
||||
{
|
||||
if(argc != 2)
|
||||
cout << "Please specify an MP3 file\n";
|
||||
else
|
||||
play(argv[1]);
|
||||
return 0;
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
|
||||
#include "../../stream/servers/file_stream.hpp"
|
||||
#include "../sources/stream_source.hpp"
|
||||
#include "../outputs/openal_out.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace Mangle::Stream;
|
||||
using namespace Mangle::Sound;
|
||||
|
||||
int main()
|
||||
{
|
||||
cout << "Loading cow.raw\n";
|
||||
|
||||
int rate = 11025;
|
||||
int chan = 1;
|
||||
int bits = 16;
|
||||
|
||||
cout << " rate=" << rate << "\n channels=" << chan
|
||||
<< "\n bits=" << bits << endl;
|
||||
|
||||
StreamPtr file( new FileStream("cow.raw") );
|
||||
SampleSourcePtr source( new Stream2Samples( file, rate, chan, bits));
|
||||
|
||||
cout << "Playing\n";
|
||||
|
||||
OpenAL_Factory mg;
|
||||
|
||||
SoundPtr snd = mg.loadRaw(source);
|
||||
|
||||
try
|
||||
{
|
||||
// Try setting all kinds of stuff before playing. OpenAL_Sound
|
||||
// uses delayed buffer loading, but these should still work
|
||||
// without a buffer.
|
||||
snd->stop();
|
||||
snd->pause();
|
||||
snd->setVolume(0.8);
|
||||
snd->setPitch(0.9);
|
||||
|
||||
// Also test streaming, since all the other examples test
|
||||
// non-streaming sounds.
|
||||
snd->setStreaming(true);
|
||||
|
||||
snd->play();
|
||||
|
||||
while(snd->isPlaying())
|
||||
{
|
||||
usleep(10000);
|
||||
mg.update();
|
||||
}
|
||||
}
|
||||
catch(exception &e)
|
||||
{
|
||||
cout << " ERROR: " << e.what() << "\n";
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
|
||||
#include "../../stream/servers/file_stream.hpp"
|
||||
#include "../filters/openal_sndfile.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace Mangle::Stream;
|
||||
using namespace Mangle::Sound;
|
||||
|
||||
OpenAL_SndFile_Factory mg;
|
||||
|
||||
void play(const char* name, bool stream=false)
|
||||
{
|
||||
// Only load streams if the backend supports it
|
||||
if(stream && !mg.canLoadStream)
|
||||
return;
|
||||
|
||||
cout << "Playing " << name;
|
||||
if(stream) cout << " (from stream)";
|
||||
cout << "\n";
|
||||
|
||||
SoundPtr snd;
|
||||
|
||||
try
|
||||
{
|
||||
if(stream)
|
||||
snd = mg.load(StreamPtr(new FileStream(name)));
|
||||
else
|
||||
snd = mg.load(name);
|
||||
|
||||
snd->play();
|
||||
|
||||
while(snd->isPlaying())
|
||||
{
|
||||
usleep(10000);
|
||||
if(mg.needsUpdate) mg.update();
|
||||
}
|
||||
}
|
||||
catch(exception &e)
|
||||
{
|
||||
cout << " ERROR: " << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
play("cow.wav");
|
||||
play("owl.ogg");
|
||||
play("cow.wav", true);
|
||||
return 0;
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
|
||||
#include "../../stream/servers/file_stream.hpp"
|
||||
#include "../filters/openal_various.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace Mangle::Stream;
|
||||
using namespace Mangle::Sound;
|
||||
|
||||
OpenAL_Various_Factory mg;
|
||||
|
||||
void play(const char* name, bool stream=false)
|
||||
{
|
||||
// Only load streams if the backend supports it
|
||||
if(stream && !mg.canLoadStream)
|
||||
return;
|
||||
|
||||
cout << "Playing " << name;
|
||||
if(stream) cout << " (from stream)";
|
||||
cout << "\n";
|
||||
|
||||
SoundPtr snd;
|
||||
|
||||
try
|
||||
{
|
||||
if(stream)
|
||||
snd = mg.load(StreamPtr(new FileStream(name)));
|
||||
else
|
||||
snd = mg.load(name);
|
||||
|
||||
snd->play();
|
||||
|
||||
while(snd->isPlaying())
|
||||
{
|
||||
usleep(10000);
|
||||
if(mg.needsUpdate) mg.update();
|
||||
}
|
||||
}
|
||||
catch(exception &e)
|
||||
{
|
||||
cout << " ERROR: " << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
play("cow.wav");
|
||||
play("cow.wav", true);
|
||||
return 0;
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
Reading cow.raw first
|
||||
Size: 37502
|
||||
Done
|
||||
|
||||
Loading cow.wav by filename:
|
||||
Source size: 37502
|
||||
rate=11025
|
||||
channels=1
|
||||
bits=16
|
||||
Reading entire buffer into memory
|
||||
Comparing...
|
||||
Done
|
||||
|
||||
Loading cow.wav by stream:
|
||||
Source size: 37502
|
||||
rate=11025
|
||||
channels=1
|
||||
bits=16
|
||||
Reading entire buffer into memory
|
||||
Comparing...
|
||||
Done
|
@ -1,12 +0,0 @@
|
||||
Reading cow.raw first
|
||||
Size: 37502
|
||||
Done
|
||||
|
||||
Loading cow.wav by filename:
|
||||
rate=11025
|
||||
channels=1
|
||||
bits=16
|
||||
Reading entire buffer into memory
|
||||
Actually read: 37502
|
||||
Comparing...
|
||||
Done
|
@ -1,3 +0,0 @@
|
||||
Playing cow.wav
|
||||
Playing owl.ogg
|
||||
Playing cow.wav (from stream)
|
@ -1,2 +0,0 @@
|
||||
Playing cow.wav
|
||||
Playing owl.ogg
|
@ -1 +0,0 @@
|
||||
Please specify an MP3 file
|
@ -1,5 +0,0 @@
|
||||
Loading cow.raw
|
||||
rate=11025
|
||||
channels=1
|
||||
bits=16
|
||||
Playing
|
@ -1,2 +0,0 @@
|
||||
Playing cow.wav
|
||||
Playing owl.ogg
|
@ -1 +0,0 @@
|
||||
Playing cow.wav
|
@ -1,12 +0,0 @@
|
||||
Source size: 37502
|
||||
rate=11025
|
||||
channels=1
|
||||
bits=16
|
||||
Reading entire buffer into memory
|
||||
|
||||
Reading cow.raw
|
||||
Size: 37502
|
||||
|
||||
Comparing...
|
||||
|
||||
Done
|
Binary file not shown.
@ -1,18 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
make || exit
|
||||
|
||||
mkdir -p output
|
||||
|
||||
PROGS=*_test
|
||||
|
||||
for a in $PROGS; do
|
||||
if [ -f "output/$a.out" ]; then
|
||||
echo "Running $a:"
|
||||
./$a | diff output/$a.out -
|
||||
else
|
||||
echo "Creating $a.out"
|
||||
./$a > "output/$a.out"
|
||||
git add "output/$a.out"
|
||||
fi
|
||||
done
|
@ -1,48 +0,0 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "../sources/wav_source.hpp"
|
||||
#include "../../stream/servers/file_stream.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace Mangle::Sound;
|
||||
using namespace Mangle::Stream;
|
||||
|
||||
int main()
|
||||
{
|
||||
WavSource wav("cow.wav");
|
||||
|
||||
cout << "Source size: " << wav.size() << endl;
|
||||
int rate, channels, bits;
|
||||
wav.getInfo(&rate, &channels, &bits);
|
||||
cout << "rate=" << rate << "\nchannels=" << channels
|
||||
<< "\nbits=" << bits << endl;
|
||||
|
||||
cout << "Reading entire buffer into memory\n";
|
||||
void *buf = malloc(wav.size());
|
||||
wav.read(buf, wav.size());
|
||||
|
||||
cout << "\nReading cow.raw\n";
|
||||
FileStream tmp("cow.raw");
|
||||
cout << "Size: " << tmp.size() << endl;
|
||||
void *buf2 = malloc(tmp.size());
|
||||
tmp.read(buf2, tmp.size());
|
||||
|
||||
cout << "\nComparing...\n";
|
||||
if(tmp.size() != wav.size())
|
||||
{
|
||||
cout << "SIZE MISMATCH!\n";
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if(memcmp(buf, buf2, wav.size()) != 0)
|
||||
{
|
||||
cout << "CONTENT MISMATCH!\n";
|
||||
assert(0);
|
||||
}
|
||||
|
||||
cout << "\nDone\n";
|
||||
return 0;
|
||||
}
|
@ -1,219 +0,0 @@
|
||||
#include "sndmanager.hpp"
|
||||
|
||||
#include "../misc/list.hpp"
|
||||
#include <boost/weak_ptr.hpp>
|
||||
|
||||
using namespace OEngine::Sound;
|
||||
using namespace Mangle::Sound;
|
||||
|
||||
/** This is our own internal implementation of the
|
||||
Mangle::Sound::Sound interface. This class links a SoundPtr to
|
||||
itself and prevents itself from being deleted as long as the sound
|
||||
is playing.
|
||||
*/
|
||||
struct OEngine::Sound::ManagedSound : SoundFilter
|
||||
{
|
||||
private:
|
||||
/** Who's your daddy? This is set if and only if we are listed
|
||||
internally in the given SoundManager.
|
||||
|
||||
It may be NULL if the manager has been deleted but the user
|
||||
keeps their own SoundPtrs to the object.
|
||||
*/
|
||||
SoundManager *mgr;
|
||||
|
||||
/** Keep a weak pointer to ourselves, which we convert into a
|
||||
'strong' pointer when we are playing. When 'self' is pointing to
|
||||
ourselves, the object will never be deleted.
|
||||
|
||||
This is used to make sure the sound is not deleted while
|
||||
playing, unless it is explicitly ordered to do so by the
|
||||
manager.
|
||||
|
||||
TODO: This kind of construct is useful. If we need it elsewhere
|
||||
later, template it. It would be generally useful in any system
|
||||
where we poll to check if a resource is still needed, but where
|
||||
manual references are allowed.
|
||||
*/
|
||||
WSoundPtr weak;
|
||||
SoundPtr self;
|
||||
|
||||
// Keep this object from being deleted
|
||||
void lock()
|
||||
{
|
||||
self = SoundPtr(weak);
|
||||
}
|
||||
|
||||
// Release the lock. This may or may not delete the object. Never do
|
||||
// anything after calling unlock()!
|
||||
void unlock()
|
||||
{
|
||||
self.reset();
|
||||
}
|
||||
|
||||
public:
|
||||
// Used for putting ourselves in linked lists
|
||||
ManagedSound *next, *prev;
|
||||
|
||||
/** Detach this sound from its manager. This means that the manager
|
||||
will no longer know we exist. Typically only called when either
|
||||
the sound or the manager is about to get deleted.
|
||||
|
||||
Since this means update() will no longer be called, we also have
|
||||
to unlock the sound manually since it will no longer be able to
|
||||
do that itself. This means that the sound may be deleted, even
|
||||
if it is still playing, when the manager is deleted.
|
||||
|
||||
However, you are still allowed to keep and manage your own
|
||||
SoundPtr references, but the lock/unlock system is disabled
|
||||
after the manager is gone.
|
||||
*/
|
||||
void detach()
|
||||
{
|
||||
if(mgr)
|
||||
{
|
||||
mgr->detach(this);
|
||||
mgr = NULL;
|
||||
}
|
||||
|
||||
// Unlock must be last command. Object may get deleted at this
|
||||
// point.
|
||||
unlock();
|
||||
}
|
||||
|
||||
ManagedSound(SoundPtr snd, SoundManager *mg)
|
||||
: SoundFilter(snd), mgr(mg)
|
||||
{}
|
||||
~ManagedSound() { detach(); }
|
||||
|
||||
// Needed to set up the weak pointer
|
||||
void setup(SoundPtr self)
|
||||
{
|
||||
weak = WSoundPtr(self);
|
||||
}
|
||||
|
||||
// Override play() to mark the object as locked
|
||||
void play()
|
||||
{
|
||||
SoundFilter::play();
|
||||
|
||||
// Lock the object so that it is not deleted while playing. Only
|
||||
// do this if we have a manager, otherwise the object will never
|
||||
// get unlocked.
|
||||
if(mgr) lock();
|
||||
}
|
||||
|
||||
// Called regularly by the manager
|
||||
void update()
|
||||
{
|
||||
// If we're no longer playing, don't force object retention.
|
||||
if(!isPlaying())
|
||||
unlock();
|
||||
|
||||
// unlock() may delete the object, so don't do anything below this
|
||||
// point.
|
||||
}
|
||||
|
||||
SoundPtr clone()
|
||||
{
|
||||
// Cloning only works when we have a manager.
|
||||
assert(mgr);
|
||||
return mgr->wrap(client->clone());
|
||||
}
|
||||
};
|
||||
|
||||
struct SoundManager::SoundManagerList
|
||||
{
|
||||
private:
|
||||
// A linked list of ManagedSound objects.
|
||||
typedef Misc::List<ManagedSound> SoundList;
|
||||
SoundList list;
|
||||
|
||||
public:
|
||||
// Add a new sound to the list
|
||||
void addNew(ManagedSound* snd)
|
||||
{
|
||||
list.insert(snd);
|
||||
}
|
||||
|
||||
// Remove a sound from the list
|
||||
void remove(ManagedSound *snd)
|
||||
{
|
||||
list.remove(snd);
|
||||
}
|
||||
|
||||
// Number of sounds in the list
|
||||
int numSounds() { return list.getNum(); }
|
||||
|
||||
// Update all sounds
|
||||
void updateAll()
|
||||
{
|
||||
ManagedSound *s = list.getHead();
|
||||
while(s)
|
||||
{
|
||||
ManagedSound *cur = s;
|
||||
// Propagate first, since update() may delete object
|
||||
s = s->next;
|
||||
cur->update();
|
||||
}
|
||||
}
|
||||
|
||||
// Detach and unlock all sounds
|
||||
void detachAll()
|
||||
{
|
||||
ManagedSound *s = list.getHead();
|
||||
while(s)
|
||||
{
|
||||
ManagedSound *cur = s;
|
||||
s = s->next;
|
||||
cur->detach();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SoundManager::SoundManager(SoundFactoryPtr fact)
|
||||
: FactoryFilter(fact)
|
||||
{
|
||||
needsUpdate = true;
|
||||
list = new SoundManagerList;
|
||||
}
|
||||
|
||||
SoundManager::~SoundManager()
|
||||
{
|
||||
// Detach all sounds
|
||||
list->detachAll();
|
||||
}
|
||||
|
||||
SoundPtr SoundManager::wrap(SoundPtr client)
|
||||
{
|
||||
// Create and set up the sound wrapper
|
||||
ManagedSound *snd = new ManagedSound(client,this);
|
||||
SoundPtr ptr(snd);
|
||||
snd->setup(ptr);
|
||||
|
||||
// Add ourselves to the list of all sounds
|
||||
list->addNew(snd);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// Remove the sound from this manager.
|
||||
void SoundManager::detach(ManagedSound *sound)
|
||||
{
|
||||
list->remove(sound);
|
||||
}
|
||||
|
||||
int SoundManager::numSounds()
|
||||
{
|
||||
return list->numSounds();
|
||||
}
|
||||
|
||||
void SoundManager::update()
|
||||
{
|
||||
// Update all the sounds we own
|
||||
list->updateAll();
|
||||
|
||||
// Update the source if it needs it
|
||||
if(client->needsUpdate)
|
||||
client->update();
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
#ifndef OENGINE_SOUND_MANAGER_H
|
||||
#define OENGINE_SOUND_MANAGER_H
|
||||
|
||||
#include <mangle/sound/filters/pure_filter.hpp>
|
||||
|
||||
namespace OEngine
|
||||
{
|
||||
namespace Sound
|
||||
{
|
||||
using namespace Mangle::Sound;
|
||||
|
||||
class ManagedSound;
|
||||
|
||||
/** A manager of Mangle::Sounds.
|
||||
|
||||
The sound manager is a wrapper around the more low-level
|
||||
SoundFactory - although it is also itself an implementation of
|
||||
SoundFactory. It will:
|
||||
- keep a list of all created sounds
|
||||
- let you iterate the list
|
||||
- keep references to playing sounds so you don't have to
|
||||
- auto-release references to sounds that are finished playing
|
||||
(ie. deleting them if you're not referencing them)
|
||||
*/
|
||||
class SoundManager : public FactoryFilter
|
||||
{
|
||||
// Shove the implementation details into the cpp file.
|
||||
struct SoundManagerList;
|
||||
SoundManagerList *list;
|
||||
|
||||
// Create a new sound wrapper based on the given source sound.
|
||||
SoundPtr wrap(SoundPtr snd);
|
||||
|
||||
/** Internal function. Will completely disconnect the given
|
||||
sound from this manager. Called from ManagedSound.
|
||||
*/
|
||||
friend class ManagedSound;
|
||||
void detach(ManagedSound *sound);
|
||||
public:
|
||||
SoundManager(SoundFactoryPtr fact);
|
||||
~SoundManager();
|
||||
void update();
|
||||
|
||||
/// Get number of sounds currently managed by this manager.
|
||||
int numSounds();
|
||||
|
||||
SoundPtr loadRaw(SampleSourcePtr input)
|
||||
{ return wrap(client->loadRaw(input)); }
|
||||
|
||||
SoundPtr load(Mangle::Stream::StreamPtr input)
|
||||
{ return wrap(client->load(input)); }
|
||||
|
||||
SoundPtr load(const std::string &file)
|
||||
{ return wrap(client->load(file)); }
|
||||
|
||||
// Play a sound immediately, and release when done unless you
|
||||
// keep the returned SoundPtr.
|
||||
SoundPtr play(Mangle::Stream::StreamPtr sound)
|
||||
{
|
||||
SoundPtr snd = load(sound);
|
||||
snd->play();
|
||||
return snd;
|
||||
}
|
||||
|
||||
SoundPtr play(const std::string &sound)
|
||||
{
|
||||
SoundPtr snd = load(sound);
|
||||
snd->play();
|
||||
return snd;
|
||||
}
|
||||
|
||||
// Ditto for 3D sounds
|
||||
SoundPtr play3D(Mangle::Stream::StreamPtr sound, float x, float y, float z)
|
||||
{
|
||||
SoundPtr snd = load(sound);
|
||||
snd->setPos(x,y,z);
|
||||
snd->play();
|
||||
return snd;
|
||||
}
|
||||
|
||||
SoundPtr play3D(const std::string &sound, float x, float y, float z)
|
||||
{
|
||||
SoundPtr snd = load(sound);
|
||||
snd->setPos(x,y,z);
|
||||
snd->play();
|
||||
return snd;
|
||||
}
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<SoundManager> SoundManagerPtr;
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,16 +0,0 @@
|
||||
GCC=g++ -I../
|
||||
|
||||
all: sound_manager_test sound_3d_test
|
||||
|
||||
L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat)
|
||||
L_OPENAL=$(shell pkg-config --libs openal)
|
||||
L_AUDIERE=-laudiere
|
||||
|
||||
sound_manager_test: sound_manager_test.cpp ../../mangle/sound/sources/audiere_source.cpp ../../mangle/sound/outputs/openal_out.cpp ../../mangle/stream/clients/audiere_file.cpp ../sndmanager.cpp
|
||||
$(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) -I../..
|
||||
|
||||
sound_3d_test: sound_3d_test.cpp ../../mangle/sound/sources/audiere_source.cpp ../../mangle/sound/outputs/openal_out.cpp ../../mangle/stream/clients/audiere_file.cpp ../sndmanager.cpp
|
||||
$(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) -I../..
|
||||
|
||||
clean:
|
||||
rm *_test
|
@ -1,3 +0,0 @@
|
||||
Playing at 0,0,0
|
||||
Playing at 1,1,0
|
||||
Playing at -1,0,0
|
@ -1,5 +0,0 @@
|
||||
Playing ../../mangle/sound/tests/cow.wav
|
||||
Replaying
|
||||
pause
|
||||
restart
|
||||
Done playing.
|
@ -1,46 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
#include <assert.h>
|
||||
|
||||
#include <mangle/stream/servers/file_stream.hpp>
|
||||
#include <mangle/sound/filters/openal_audiere.hpp>
|
||||
|
||||
#include <sound/sndmanager.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace Mangle::Stream;
|
||||
using namespace Mangle::Sound;
|
||||
using namespace OEngine::Sound;
|
||||
|
||||
const std::string sound = "../../mangle/sound/tests/cow.wav";
|
||||
|
||||
SoundManagerPtr m;
|
||||
|
||||
// Play and wait for finish
|
||||
void play(float x, float y, float z)
|
||||
{
|
||||
cout << "Playing at " << x << "," << y << "," << z << endl;
|
||||
|
||||
SoundPtr snd = m->play3D(sound,x,y,z);
|
||||
|
||||
while(snd->isPlaying())
|
||||
{
|
||||
usleep(10000);
|
||||
m->update();
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
SoundFactoryPtr oaf(new OpenAL_Audiere_Factory);
|
||||
SoundManagerPtr mg(new SoundManager(oaf));
|
||||
m = mg;
|
||||
|
||||
mg->setListenerPos(0,0,0,0,1,0,0,0,1);
|
||||
|
||||
play(0,0,0);
|
||||
play(1,1,0);
|
||||
play(-1,0,0);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
#include <assert.h>
|
||||
|
||||
#include <mangle/stream/servers/file_stream.hpp>
|
||||
#include <mangle/sound/filters/openal_audiere.hpp>
|
||||
|
||||
#include <sound/sndmanager.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace Mangle::Stream;
|
||||
using namespace Mangle::Sound;
|
||||
using namespace OEngine::Sound;
|
||||
|
||||
const std::string sound = "../../mangle/sound/tests/cow.wav";
|
||||
|
||||
int main()
|
||||
{
|
||||
SoundFactoryPtr oaf(new OpenAL_Audiere_Factory);
|
||||
SoundManagerPtr mg(new SoundManager(oaf));
|
||||
|
||||
cout << "Playing " << sound << "\n";
|
||||
|
||||
assert(mg->numSounds() == 0);
|
||||
|
||||
/** Start the sound playing, and then let the pointer go out of
|
||||
scope. Lower-level players (like 'oaf' above) will immediately
|
||||
delete the sound. SoundManager OTOH will keep it until it's
|
||||
finished.
|
||||
*/
|
||||
mg->play(sound);
|
||||
|
||||
assert(mg->numSounds() == 1);
|
||||
|
||||
// Loop while there are still sounds to manage
|
||||
while(mg->numSounds() != 0)
|
||||
{
|
||||
assert(mg->numSounds() == 1);
|
||||
usleep(10000);
|
||||
if(mg->needsUpdate)
|
||||
mg->update();
|
||||
}
|
||||
|
||||
SoundPtr snd = mg->play(sound);
|
||||
cout << "Replaying\n";
|
||||
int i = 0;
|
||||
while(mg->numSounds() != 0)
|
||||
{
|
||||
assert(mg->numSounds() == 1);
|
||||
usleep(10000);
|
||||
if(mg->needsUpdate)
|
||||
mg->update();
|
||||
|
||||
if(i++ == 70)
|
||||
{
|
||||
cout << "pause\n";
|
||||
snd->pause();
|
||||
}
|
||||
if(i == 130)
|
||||
{
|
||||
cout << "restart\n";
|
||||
snd->play();
|
||||
// Let the sound go out of scope
|
||||
snd.reset();
|
||||
}
|
||||
}
|
||||
|
||||
cout << "Done playing.\n";
|
||||
|
||||
assert(mg->numSounds() == 0);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
make || exit
|
||||
|
||||
mkdir -p output
|
||||
|
||||
PROGS=*_test
|
||||
|
||||
for a in $PROGS; do
|
||||
if [ -f "output/$a.out" ]; then
|
||||
echo "Running $a:"
|
||||
./$a | diff output/$a.out -
|
||||
else
|
||||
echo "Creating $a.out"
|
||||
./$a > "output/$a.out"
|
||||
git add "output/$a.out"
|
||||
fi
|
||||
done
|
Loading…
Reference in New Issue