mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 06:26:39 +00:00 
			
		
		
		
	Implemented OpenAL streaming. Fixed bugs in SampleReader and elsewhere.
This commit is contained in:
		
							parent
							
								
									932465442b
								
							
						
					
					
						commit
						cd4ed4e6bf
					
				
					 6 changed files with 217 additions and 46 deletions
				
			
		| 
						 | 
					@ -11,6 +11,20 @@ using namespace Mangle::Sound;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ---- Helper functions and classes ----
 | 
					// ---- 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. 4 should be ok.
 | 
				
			||||||
 | 
					const int STREAM_BUF_NUM = 4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void fail(const std::string &msg)
 | 
					static void fail(const std::string &msg)
 | 
				
			||||||
{ throw str_exception("OpenAL exception: " + msg); }
 | 
					{ throw str_exception("OpenAL exception: " + msg); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -73,10 +87,18 @@ static void getALFormat(SampleSourcePtr inp, int &fmt, int &rate)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// OpenAL sound output
 | 
					/// OpenAL sound output
 | 
				
			||||||
class OpenAL_Sound : public Sound
 | 
					class Mangle::Sound::OpenAL_Sound : public Sound
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  ALuint inst;
 | 
					  ALuint inst;
 | 
				
			||||||
  ALuint bufferID;
 | 
					
 | 
				
			||||||
 | 
					  // Buffers. Only the first is used for non-streaming sounds.
 | 
				
			||||||
 | 
					  ALuint bufferID[4];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Number of buffers used
 | 
				
			||||||
 | 
					  int bufNum;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Parameters used for filling buffers
 | 
				
			||||||
 | 
					  int fmt, rate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Poor mans reference counting. Might improve this later. When
 | 
					  // Poor mans reference counting. Might improve this later. When
 | 
				
			||||||
  // NULL, the buffer has not been set up yet.
 | 
					  // NULL, the buffer has not been set up yet.
 | 
				
			||||||
| 
						 | 
					@ -87,18 +109,74 @@ class OpenAL_Sound : public Sound
 | 
				
			||||||
  // Input stream
 | 
					  // Input stream
 | 
				
			||||||
  SampleSourcePtr input;
 | 
					  SampleSourcePtr input;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  OpenAL_Factory *owner;
 | 
				
			||||||
 | 
					  bool ownerAlive;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Used for streamed sound list
 | 
				
			||||||
 | 
					  OpenAL_Sound *next, *prev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void setupBuffer();
 | 
					  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:
 | 
					 public:
 | 
				
			||||||
  /// Read samples from the given input buffer
 | 
					  /// Read samples from the given input buffer
 | 
				
			||||||
  OpenAL_Sound(SampleSourcePtr input);
 | 
					  OpenAL_Sound(SampleSourcePtr input, OpenAL_Factory *fact);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Play an existing buffer, with a given ref counter. Used
 | 
					  /// Play an existing buffer, with a given ref counter. Used
 | 
				
			||||||
  /// internally for cloning.
 | 
					  /// internally for cloning.
 | 
				
			||||||
  OpenAL_Sound(ALuint buf, int *ref);
 | 
					  OpenAL_Sound(ALuint buf, int *ref, OpenAL_Factory *fact);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ~OpenAL_Sound();
 | 
					  ~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 play();
 | 
				
			||||||
  void stop();
 | 
					  void stop();
 | 
				
			||||||
  void pause();
 | 
					  void pause();
 | 
				
			||||||
| 
						 | 
					@ -107,7 +185,14 @@ class OpenAL_Sound : public Sound
 | 
				
			||||||
  void setPos(float x, float y, float z);
 | 
					  void setPos(float x, float y, float z);
 | 
				
			||||||
  void setPitch(float);
 | 
					  void setPitch(float);
 | 
				
			||||||
  void setRepeat(bool);
 | 
					  void setRepeat(bool);
 | 
				
			||||||
  void setStreaming(bool s) { streaming = s; }
 | 
					
 | 
				
			||||||
 | 
					  void notifyOwnerDeath()
 | 
				
			||||||
 | 
					  { ownerAlive = false; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // We can enable streaming, but never disable it.
 | 
				
			||||||
 | 
					  void setStreaming(bool s)
 | 
				
			||||||
 | 
					  { if(s) streaming = true; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  SoundPtr clone();
 | 
					  SoundPtr clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // a = AL_REFERENCE_DISTANCE
 | 
					  // a = AL_REFERENCE_DISTANCE
 | 
				
			||||||
| 
						 | 
					@ -123,11 +208,7 @@ class OpenAL_Sound : public Sound
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SoundPtr OpenAL_Factory::loadRaw(SampleSourcePtr input)
 | 
					SoundPtr OpenAL_Factory::loadRaw(SampleSourcePtr input)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  return SoundPtr(new OpenAL_Sound(input));
 | 
					  return SoundPtr(new OpenAL_Sound(input, this));
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void OpenAL_Factory::update()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void OpenAL_Factory::setListenerPos(float x, float y, float z,
 | 
					void OpenAL_Factory::setListenerPos(float x, float y, float z,
 | 
				
			||||||
| 
						 | 
					@ -173,8 +254,33 @@ OpenAL_Factory::OpenAL_Factory(bool doSetup)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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()
 | 
					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
 | 
					  // Deinitialize sound system
 | 
				
			||||||
  if(didSetup)
 | 
					  if(didSetup)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
| 
						 | 
					@ -249,27 +355,31 @@ SoundPtr OpenAL_Sound::clone()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  setupBuffer();
 | 
					  setupBuffer();
 | 
				
			||||||
  assert(!streaming && "cloning streamed sounds not supported");
 | 
					  assert(!streaming && "cloning streamed sounds not supported");
 | 
				
			||||||
  return SoundPtr(new OpenAL_Sound(bufferID, refCnt));
 | 
					  return SoundPtr(new OpenAL_Sound(bufferID[0], refCnt, owner));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Constructor used for cloned sounds
 | 
					// Constructor used for cloned sounds
 | 
				
			||||||
OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref)
 | 
					OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref, OpenAL_Factory *fact)
 | 
				
			||||||
  : bufferID(buf), refCnt(ref), streaming(false)
 | 
					  : refCnt(ref), streaming(false), owner(fact), ownerAlive(false)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  // Increase the reference count
 | 
					  // Increase the reference count
 | 
				
			||||||
  assert(ref != NULL);
 | 
					  assert(ref != NULL);
 | 
				
			||||||
  *refCnt++;
 | 
					  *refCnt++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Set up buffer
 | 
				
			||||||
 | 
					  bufferID[0] = buf;
 | 
				
			||||||
 | 
					  bufNum = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Create a source
 | 
					  // Create a source
 | 
				
			||||||
  alGenSources(1, &inst);
 | 
					  alGenSources(1, &inst);
 | 
				
			||||||
  checkALError("creating instance (clone)");
 | 
					  checkALError("creating instance (clone)");
 | 
				
			||||||
  alSourcei(inst, AL_BUFFER, bufferID);
 | 
					  alSourcei(inst, AL_BUFFER, bufferID[0]);
 | 
				
			||||||
  checkALError("assigning buffer (clone)");
 | 
					  checkALError("assigning buffer (clone)");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Constructor used for original (non-cloned) sounds
 | 
					// Constructor used for original (non-cloned) sounds
 | 
				
			||||||
OpenAL_Sound::OpenAL_Sound(SampleSourcePtr _input)
 | 
					OpenAL_Sound::OpenAL_Sound(SampleSourcePtr _input, OpenAL_Factory *fact)
 | 
				
			||||||
  : bufferID(0), refCnt(NULL), streaming(false), input(_input)
 | 
					  : refCnt(NULL), streaming(false), input(_input), owner(fact), ownerAlive(false)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  // Create a source
 | 
					  // Create a source
 | 
				
			||||||
  alGenSources(1, &inst);
 | 
					  alGenSources(1, &inst);
 | 
				
			||||||
| 
						 | 
					@ -287,46 +397,60 @@ void OpenAL_Sound::setupBuffer()
 | 
				
			||||||
  assert(input);
 | 
					  assert(input);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Get the format
 | 
					  // Get the format
 | 
				
			||||||
  int fmt, rate;
 | 
					 | 
				
			||||||
  getALFormat(input, fmt, rate);
 | 
					  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)
 | 
					  if(streaming)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      // To be done
 | 
					      // 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
 | 
					  // NON-STREAMING. We have to load all the data and shove it into the
 | 
				
			||||||
 | 
					  // buffer.
 | 
				
			||||||
  // Set up the OpenAL buffer
 | 
					 | 
				
			||||||
  alGenBuffers(1, &bufferID);
 | 
					 | 
				
			||||||
  checkALError("generating buffer");
 | 
					 | 
				
			||||||
  assert(bufferID != 0);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Does the stream support pointer operations?
 | 
					  // Does the stream support pointer operations?
 | 
				
			||||||
  if(input->hasPtr)
 | 
					  if(input->hasPtr)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      // If so, we can read the data directly from the stream
 | 
					      // If so, we can read the data directly from the stream
 | 
				
			||||||
      alBufferData(bufferID, fmt, input->getPtr(), input->size(), rate);
 | 
					      alBufferData(bufferID[0], fmt, input->getPtr(), input->size(), rate);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  else
 | 
					  else
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      // Read the entire stream into a temporary buffer first
 | 
					      // Read the entire stream into a temporary buffer first
 | 
				
			||||||
      Mangle::Stream::BufferStream buf(input, streaming?1024*1024:32*1024);
 | 
					      Mangle::Stream::BufferStream buf(input, 128*1024);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // Then copy that into OpenAL
 | 
					      // Then copy that into OpenAL
 | 
				
			||||||
      alBufferData(bufferID, fmt, buf.getPtr(), buf.size(), rate);
 | 
					      alBufferData(bufferID[0], fmt, buf.getPtr(), buf.size(), rate);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  checkALError("loading sound buffer");
 | 
					  checkALError("loading sound data");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // We're done with the input stream, release the pointer
 | 
					  // We're done with the input stream, release the pointer
 | 
				
			||||||
  input.reset();
 | 
					  input.reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  alSourcei(inst, AL_BUFFER, bufferID);
 | 
					  alSourcei(inst, AL_BUFFER, bufferID[0]);
 | 
				
			||||||
  checkALError("assigning buffer");
 | 
					  checkALError("assigning buffer");
 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Create a cheap reference counter for the buffer
 | 
					 | 
				
			||||||
  refCnt = new int;
 | 
					 | 
				
			||||||
  *refCnt = 1;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OpenAL_Sound::~OpenAL_Sound()
 | 
					OpenAL_Sound::~OpenAL_Sound()
 | 
				
			||||||
| 
						 | 
					@ -337,12 +461,19 @@ OpenAL_Sound::~OpenAL_Sound()
 | 
				
			||||||
  // Return sound
 | 
					  // Return sound
 | 
				
			||||||
  alDeleteSources(1, &inst);
 | 
					  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
 | 
					  // Decrease the reference counter
 | 
				
			||||||
  if((-- *refCnt) == 0)
 | 
					  if((-- *refCnt) == 0)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      // We're the last owner. Delete the buffer and the counter
 | 
					      // We're the last owner. Delete the buffer(s) and the counter
 | 
				
			||||||
      // itself.
 | 
					      // itself.
 | 
				
			||||||
      alDeleteBuffers(1, &bufferID);
 | 
					      alDeleteBuffers(bufNum, bufferID);
 | 
				
			||||||
      checkALError("deleting buffer");
 | 
					      checkALError("deleting buffer");
 | 
				
			||||||
      delete refCnt;
 | 
					      delete refCnt;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,16 +2,27 @@
 | 
				
			||||||
#define MANGLE_SOUND_OPENAL_OUT_H
 | 
					#define MANGLE_SOUND_OPENAL_OUT_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "../output.hpp"
 | 
					#include "../output.hpp"
 | 
				
			||||||
 | 
					#include <list>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Mangle {
 | 
					namespace Mangle {
 | 
				
			||||||
namespace Sound {
 | 
					namespace Sound {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OpenAL_Sound;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class OpenAL_Factory : public SoundFactory
 | 
					class OpenAL_Factory : public SoundFactory
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  void *device;
 | 
					  void *device;
 | 
				
			||||||
  void *context;
 | 
					  void *context;
 | 
				
			||||||
  bool didSetup;
 | 
					  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:
 | 
					 public:
 | 
				
			||||||
  /// Initialize object. Pass true (default) if you want the
 | 
					  /// Initialize object. Pass true (default) if you want the
 | 
				
			||||||
  /// constructor to set up the current ALCdevice and ALCcontext for
 | 
					  /// constructor to set up the current ALCdevice and ALCcontext for
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,9 +19,9 @@ void SndFileSource::getInfo(int32_t *_rate, int32_t *_channels, int32_t *_bits)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
size_t SndFileSource::readSamples(void *data, size_t length)
 | 
					size_t SndFileSource::readSamples(void *data, size_t length)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  // Read frames. We count channels as part of the frame. Even though
 | 
					  // Read frames. We count channels as part of the frame, even though
 | 
				
			||||||
  // libsndfile does not, since it still requires the number of frames
 | 
					  // libsndfile does not. This is because the library still requires
 | 
				
			||||||
  // read to be a multiple of channels.
 | 
					  // the number of frames read to be a multiple of channels.
 | 
				
			||||||
  return channels*sf_read_short((SNDFILE*)handle, (short*)data, length*channels);
 | 
					  return channels*sf_read_short((SNDFILE*)handle, (short*)data, length*channels);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,15 +22,36 @@ size_t SampleReader::read(void *_data, size_t length)
 | 
				
			||||||
  if(isEof) return 0;
 | 
					  if(isEof) return 0;
 | 
				
			||||||
  char *data = (char*)_data;
 | 
					  char *data = (char*)_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Move the remains from the last operation first
 | 
					  // 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)
 | 
					  if(pullSize)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      // pullSize is how much was stored the last time. The data is
 | 
					      // Amount of data left
 | 
				
			||||||
      // stored at the end of the buffer.
 | 
					      size_t doRead = frameSize - pullSize;
 | 
				
			||||||
      memcpy(data, pullOver+(frameSize-pullSize), pullSize);
 | 
					      assert(doRead > 0);
 | 
				
			||||||
      length -= pullSize;
 | 
					
 | 
				
			||||||
      data += pullSize;
 | 
					      // Make sure we don't read more than we're supposed to
 | 
				
			||||||
      pullSize = 0;
 | 
					      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
 | 
					  // Number of whole frames
 | 
				
			||||||
| 
						 | 
					@ -38,6 +59,7 @@ size_t SampleReader::read(void *_data, size_t length)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Read the data
 | 
					  // Read the data
 | 
				
			||||||
  size_t res = readSamples(data, frames);
 | 
					  size_t res = readSamples(data, frames);
 | 
				
			||||||
 | 
					  assert(res <= frames);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Total bytes read
 | 
					  // Total bytes read
 | 
				
			||||||
  size_t num = res*frameSize;
 | 
					  size_t num = res*frameSize;
 | 
				
			||||||
| 
						 | 
					@ -53,12 +75,14 @@ size_t SampleReader::read(void *_data, size_t length)
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  // Determine the overshoot
 | 
					  // Determine the overshoot
 | 
				
			||||||
  pullSize = length - num;
 | 
					  pullSize = length - num;
 | 
				
			||||||
 | 
					  assert(pullSize < frameSize && pullSize >= 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Are we missing data?
 | 
					  // Are we missing data?
 | 
				
			||||||
  if(pullSize)
 | 
					  if(pullSize)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      // Fill in one sample
 | 
					      // Fill in one sample
 | 
				
			||||||
      res = readSamples(pullOver,1);
 | 
					      res = readSamples(pullOver,1);
 | 
				
			||||||
 | 
					      assert(res == 1 || res == 0);
 | 
				
			||||||
      if(res)
 | 
					      if(res)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
          // Move as much as we can into the output buffer
 | 
					          // Move as much as we can into the output buffer
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,11 +39,16 @@ int main()
 | 
				
			||||||
      snd->setVolume(0.8);
 | 
					      snd->setVolume(0.8);
 | 
				
			||||||
      snd->setPitch(0.9);
 | 
					      snd->setPitch(0.9);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Also test streaming, since all the other examples test
 | 
				
			||||||
 | 
					      // non-streaming sounds.
 | 
				
			||||||
 | 
					      snd->setStreaming(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      snd->play();
 | 
					      snd->play();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      while(snd->isPlaying())
 | 
					      while(snd->isPlaying())
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
          usleep(10000);
 | 
					          usleep(10000);
 | 
				
			||||||
 | 
					          mg.update();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  catch(exception &e)
 | 
					  catch(exception &e)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,7 +29,7 @@ class StdStream : public Stream
 | 
				
			||||||
  size_t read(void* buf, size_t len)
 | 
					  size_t read(void* buf, size_t len)
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    inf->read((char*)buf, len);
 | 
					    inf->read((char*)buf, len);
 | 
				
			||||||
    if(inf->fail())
 | 
					    if(inf->bad())
 | 
				
			||||||
      fail("error reading from stream");
 | 
					      fail("error reading from stream");
 | 
				
			||||||
    return inf->gcount();
 | 
					    return inf->gcount();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue