forked from teamnwah/openmw-tes3coop
Added custom WAV loader
This commit is contained in:
parent
27bef84091
commit
8f154ac622
8 changed files with 303 additions and 1 deletions
40
sound/filters/openal_various.hpp
Normal file
40
sound/filters/openal_various.hpp
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#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. libsndfile or other 3rd
|
||||||
|
party decoder libraries. (We implemented this for OpenMW because
|
||||||
|
we were experiencing crashes when using 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
|
95
sound/sources/wav_source.cpp
Normal file
95
sound/sources/wav_source.cpp
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
#include "wav_source.hpp"
|
||||||
|
|
||||||
|
#include "../../tools/str_exception.hpp"
|
||||||
|
#include "../../stream/servers/file_stream.hpp"
|
||||||
|
|
||||||
|
using namespace Mangle::Stream;
|
||||||
|
using namespace Mangle::Sound;
|
||||||
|
|
||||||
|
static void fail(const std::string &msg)
|
||||||
|
{ throw str_exception("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;
|
||||||
|
input->read(data, length);
|
||||||
|
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))); }
|
49
sound/sources/wav_source.hpp
Normal file
49
sound/sources/wav_source.hpp
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
#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,12 +1,18 @@
|
||||||
GCC=g++ -I../ -Wall
|
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
|
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)
|
L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat)
|
||||||
I_FFMPEG=-I/usr/include/libavcodec -I/usr/include/libavformat
|
I_FFMPEG=-I/usr/include/libavcodec -I/usr/include/libavformat
|
||||||
L_OPENAL=$(shell pkg-config --libs openal)
|
L_OPENAL=$(shell pkg-config --libs openal)
|
||||||
L_AUDIERE=-laudiere
|
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
|
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)
|
$(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL)
|
||||||
|
|
||||||
|
|
51
sound/tests/openal_various_test.cpp
Normal file
51
sound/tests/openal_various_test.cpp
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
#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
sound/tests/output/openal_various_test.out
Normal file
1
sound/tests/output/openal_various_test.out
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Playing cow.wav
|
12
sound/tests/output/wav_source_test.out
Normal file
12
sound/tests/output/wav_source_test.out
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
Source size: 37502
|
||||||
|
rate=11025
|
||||||
|
channels=1
|
||||||
|
bits=16
|
||||||
|
Reading entire buffer into memory
|
||||||
|
|
||||||
|
Reading cow.raw
|
||||||
|
Size: 37502
|
||||||
|
|
||||||
|
Comparing...
|
||||||
|
|
||||||
|
Done
|
48
sound/tests/wav_source_test.cpp
Normal file
48
sound/tests/wav_source_test.cpp
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
#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;
|
||||||
|
}
|
Loading…
Reference in a new issue