diff --git a/sound/filters/openal_various.hpp b/sound/filters/openal_various.hpp new file mode 100644 index 000000000..87030f4b6 --- /dev/null +++ b/sound/filters/openal_various.hpp @@ -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 diff --git a/sound/sources/wav_source.cpp b/sound/sources/wav_source.cpp new file mode 100644 index 000000000..5ee87d492 --- /dev/null +++ b/sound/sources/wav_source.cpp @@ -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))); } diff --git a/sound/sources/wav_source.hpp b/sound/sources/wav_source.hpp new file mode 100644 index 000000000..227f4da73 --- /dev/null +++ b/sound/sources/wav_source.hpp @@ -0,0 +1,49 @@ +#ifndef MANGLE_SOUND_WAV_SOURCE_H +#define MANGLE_SOUND_WAV_SOURCE_H + +#include "../source.hpp" +#include + +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 WavLoader; + +}} // Namespace +#endif diff --git a/sound/tests/Makefile b/sound/tests/Makefile index 365e4a05c..6fcac72da 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -1,12 +1,18 @@ 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) 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) diff --git a/sound/tests/openal_various_test.cpp b/sound/tests/openal_various_test.cpp new file mode 100644 index 000000000..9426a672e --- /dev/null +++ b/sound/tests/openal_various_test.cpp @@ -0,0 +1,51 @@ +#include +#include + +#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; +} diff --git a/sound/tests/output/openal_various_test.out b/sound/tests/output/openal_various_test.out new file mode 100644 index 000000000..f25a55513 --- /dev/null +++ b/sound/tests/output/openal_various_test.out @@ -0,0 +1 @@ +Playing cow.wav diff --git a/sound/tests/output/wav_source_test.out b/sound/tests/output/wav_source_test.out new file mode 100644 index 000000000..b6fc8e6fc --- /dev/null +++ b/sound/tests/output/wav_source_test.out @@ -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 diff --git a/sound/tests/wav_source_test.cpp b/sound/tests/wav_source_test.cpp new file mode 100644 index 000000000..749af1849 --- /dev/null +++ b/sound/tests/wav_source_test.cpp @@ -0,0 +1,48 @@ +#include + +#include "../sources/wav_source.hpp" +#include "../../stream/servers/file_stream.hpp" + +#include +#include + +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; +}