mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-03-30 00:36:41 +00:00
Add a function to stream sounds
This commit is contained in:
parent
9cf42f6d0f
commit
1ade01edc8
4 changed files with 198 additions and 1 deletions
|
@ -1,7 +1,11 @@
|
|||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "openal_output.hpp"
|
||||
#include "sound_decoder.hpp"
|
||||
#include "sound.hpp"
|
||||
|
||||
|
||||
namespace MWSound
|
||||
{
|
||||
|
@ -9,6 +13,176 @@ namespace MWSound
|
|||
static void fail(const std::string &msg)
|
||||
{ throw std::runtime_error("OpenAL exception: " + msg); }
|
||||
|
||||
static void throwALerror()
|
||||
{
|
||||
ALenum err = alGetError();
|
||||
if(err != AL_NO_ERROR)
|
||||
fail(alGetString(err));
|
||||
}
|
||||
|
||||
|
||||
static ALenum getALFormat(Sound_Decoder::ChannelConfig chans, Sound_Decoder::SampleType type)
|
||||
{
|
||||
if(chans == Sound_Decoder::MonoChannels)
|
||||
{
|
||||
if(type == Sound_Decoder::Int16Sample)
|
||||
return AL_FORMAT_MONO16;
|
||||
else if(type == Sound_Decoder::UInt8Sample)
|
||||
return AL_FORMAT_MONO8;
|
||||
else
|
||||
fail("Unsupported sample type");
|
||||
}
|
||||
else if(chans == Sound_Decoder::StereoChannels)
|
||||
{
|
||||
if(type == Sound_Decoder::Int16Sample)
|
||||
return AL_FORMAT_STEREO16;
|
||||
else if(type == Sound_Decoder::UInt8Sample)
|
||||
return AL_FORMAT_STEREO8;
|
||||
else
|
||||
fail("Unsupported sample type");
|
||||
}
|
||||
else
|
||||
fail("Unsupported channel config");
|
||||
return AL_NONE;
|
||||
}
|
||||
|
||||
|
||||
class OpenAL_SoundStream : public Sound
|
||||
{
|
||||
// This should be something sane, like 4, but currently cell loads tend to
|
||||
// cause the stream to underrun
|
||||
static const ALuint NumBuffers = 150;
|
||||
static const ALuint BufferSize = 32768;
|
||||
|
||||
ALuint Source;
|
||||
ALuint Buffers[NumBuffers];
|
||||
|
||||
ALenum Format;
|
||||
ALsizei SampleRate;
|
||||
|
||||
std::auto_ptr<Sound_Decoder> Decoder;
|
||||
|
||||
public:
|
||||
OpenAL_SoundStream(std::auto_ptr<Sound_Decoder> decoder) : Decoder(decoder)
|
||||
{
|
||||
throwALerror();
|
||||
|
||||
alGenSources(1, &Source);
|
||||
throwALerror();
|
||||
try
|
||||
{
|
||||
alGenBuffers(NumBuffers, Buffers);
|
||||
throwALerror();
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
alDeleteSources(1, &Source);
|
||||
alGetError();
|
||||
throw;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
int srate;
|
||||
enum Sound_Decoder::ChannelConfig chans;
|
||||
enum Sound_Decoder::SampleType type;
|
||||
|
||||
Decoder->GetInfo(&srate, &chans, &type);
|
||||
Format = getALFormat(chans, type);
|
||||
SampleRate = srate;
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
alDeleteSources(1, &Source);
|
||||
alDeleteBuffers(NumBuffers, Buffers);
|
||||
alGetError();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
virtual ~OpenAL_SoundStream()
|
||||
{
|
||||
alDeleteSources(1, &Source);
|
||||
alDeleteBuffers(NumBuffers, Buffers);
|
||||
alGetError();
|
||||
Decoder->Close();
|
||||
}
|
||||
|
||||
virtual bool Play()
|
||||
{
|
||||
std::vector<char> data(BufferSize);
|
||||
|
||||
alSourceStop(Source);
|
||||
alSourcei(Source, AL_BUFFER, 0);
|
||||
throwALerror();
|
||||
|
||||
for(ALuint i = 0;i < NumBuffers;i++)
|
||||
{
|
||||
size_t got;
|
||||
got = Decoder->Read(&data[0], data.size());
|
||||
alBufferData(Buffers[i], Format, &data[0], got, SampleRate);
|
||||
}
|
||||
throwALerror();
|
||||
|
||||
alSourceQueueBuffers(Source, NumBuffers, Buffers);
|
||||
alSourcePlay(Source);
|
||||
throwALerror();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void Stop()
|
||||
{
|
||||
alSourceStop(Source);
|
||||
alSourcei(Source, AL_BUFFER, 0);
|
||||
throwALerror();
|
||||
// FIXME: Rewind decoder
|
||||
}
|
||||
|
||||
virtual bool isPlaying()
|
||||
{
|
||||
ALint processed, state;
|
||||
|
||||
alGetSourcei(Source, AL_SOURCE_STATE, &state);
|
||||
alGetSourcei(Source, AL_BUFFERS_PROCESSED, &processed);
|
||||
throwALerror();
|
||||
|
||||
if(processed > 0)
|
||||
{
|
||||
std::vector<char> data(BufferSize);
|
||||
do {
|
||||
ALuint bufid;
|
||||
size_t got;
|
||||
|
||||
alSourceUnqueueBuffers(Source, 1, &bufid);
|
||||
processed--;
|
||||
|
||||
got = Decoder->Read(&data[0], data.size());
|
||||
if(got > 0)
|
||||
{
|
||||
alBufferData(bufid, Format, &data[0], got, SampleRate);
|
||||
alSourceQueueBuffers(Source, 1, &bufid);
|
||||
}
|
||||
} while(processed > 0);
|
||||
throwALerror();
|
||||
}
|
||||
|
||||
if(state != AL_PLAYING && state != AL_PAUSED)
|
||||
{
|
||||
ALint queued;
|
||||
|
||||
alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued);
|
||||
throwALerror();
|
||||
if(queued == 0)
|
||||
return false;
|
||||
|
||||
alSourcePlay(Source);
|
||||
throwALerror();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
bool OpenAL_Output::Initialize(const std::string &devname)
|
||||
{
|
||||
|
@ -50,6 +224,20 @@ void OpenAL_Output::Deinitialize()
|
|||
}
|
||||
|
||||
|
||||
Sound* OpenAL_Output::StreamSound(const std::string &fname, std::auto_ptr<Sound_Decoder> decoder)
|
||||
{
|
||||
std::auto_ptr<OpenAL_SoundStream> sound;
|
||||
|
||||
if(!decoder->Open(fname))
|
||||
return NULL;
|
||||
|
||||
sound.reset(new OpenAL_SoundStream(decoder));
|
||||
sound->Play();
|
||||
|
||||
return sound.release();
|
||||
}
|
||||
|
||||
|
||||
OpenAL_Output::OpenAL_Output(SoundManager &mgr)
|
||||
: Sound_Output(mgr), Device(0), Context(0)
|
||||
{
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
namespace MWSound
|
||||
{
|
||||
class SoundManager;
|
||||
class Sound_Decoder;
|
||||
class Sound;
|
||||
|
||||
class OpenAL_Output : public Sound_Output
|
||||
{
|
||||
|
@ -20,6 +22,8 @@ namespace MWSound
|
|||
virtual bool Initialize(const std::string &devname="");
|
||||
virtual void Deinitialize();
|
||||
|
||||
virtual Sound *StreamSound(const std::string &fname, std::auto_ptr<Sound_Decoder> decoder);
|
||||
|
||||
OpenAL_Output(SoundManager &mgr);
|
||||
virtual ~OpenAL_Output();
|
||||
|
||||
|
|
|
@ -2,10 +2,13 @@
|
|||
#define GAME_SOUND_SOUND_OUTPUT_H
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace MWSound
|
||||
{
|
||||
class SoundManager;
|
||||
class Sound_Decoder;
|
||||
class Sound;
|
||||
|
||||
class Sound_Output
|
||||
{
|
||||
|
@ -14,6 +17,8 @@ namespace MWSound
|
|||
virtual bool Initialize(const std::string &devname="") = 0;
|
||||
virtual void Deinitialize() = 0;
|
||||
|
||||
virtual Sound *StreamSound(const std::string &fname, std::auto_ptr<Sound_Decoder> decoder) = 0;
|
||||
|
||||
Sound_Output(SoundManager &mgr) : mgr(mgr) { }
|
||||
public:
|
||||
virtual ~Sound_Output() { }
|
||||
|
|
|
@ -155,7 +155,7 @@ namespace MWSound
|
|||
if(mMusic)
|
||||
mMusic->Stop();
|
||||
std::auto_ptr<Sound_Decoder> decoder(new DEFAULT_DECODER);
|
||||
//mMusic.reset(Output->StreamSound(filename, decoder));
|
||||
mMusic.reset(Output->StreamSound(filename, decoder));
|
||||
}
|
||||
|
||||
void SoundManager::streamMusic(const std::string& filename)
|
||||
|
|
Loading…
Reference in a new issue