2f107db...5202af9
This commit is contained in:
@@ -1,12 +1,86 @@
|
||||
using System;
|
||||
using OpenTK.Audio.OpenAL;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Barotrauma.Sounds
|
||||
{
|
||||
public class SoundSourcePool : IDisposable
|
||||
{
|
||||
public uint[] ALSources
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public SoundSourcePool(int sourceCount = SoundManager.SOURCE_COUNT)
|
||||
{
|
||||
ALError alError = ALError.NoError;
|
||||
|
||||
ALSources = new uint[sourceCount];
|
||||
for (int i = 0; i < sourceCount; i++)
|
||||
{
|
||||
AL.GenSource(out ALSources[i]);
|
||||
alError = AL.GetError();
|
||||
if (alError != ALError.NoError)
|
||||
{
|
||||
throw new Exception("Error generating alSource[" + i.ToString() + "]: " + AL.GetErrorString(alError));
|
||||
}
|
||||
|
||||
if (!AL.IsSource(ALSources[i]))
|
||||
{
|
||||
throw new Exception("Generated alSource[" + i.ToString() + "] is invalid!");
|
||||
}
|
||||
|
||||
AL.SourceStop(ALSources[i]);
|
||||
alError = AL.GetError();
|
||||
if (alError != ALError.NoError)
|
||||
{
|
||||
throw new Exception("Error stopping newly generated alSource[" + i.ToString() + "]: " + AL.GetErrorString(alError));
|
||||
}
|
||||
|
||||
AL.Source(ALSources[i], ALSourcef.MinGain, 0.0f);
|
||||
alError = AL.GetError();
|
||||
if (alError != ALError.NoError)
|
||||
{
|
||||
throw new Exception("Error setting min gain: " + AL.GetErrorString(alError));
|
||||
}
|
||||
|
||||
AL.Source(ALSources[i], ALSourcef.MaxGain, 1.0f);
|
||||
alError = AL.GetError();
|
||||
if (alError != ALError.NoError)
|
||||
{
|
||||
throw new Exception("Error setting max gain: " + AL.GetErrorString(alError));
|
||||
}
|
||||
|
||||
AL.Source(ALSources[i], ALSourcef.RolloffFactor, 1.0f);
|
||||
alError = AL.GetError();
|
||||
if (alError != ALError.NoError)
|
||||
{
|
||||
throw new Exception("Error setting rolloff factor: " + AL.GetErrorString(alError));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
for (int i = 0; i < ALSources.Length; i++)
|
||||
{
|
||||
AL.DeleteSource(ref ALSources[i]);
|
||||
ALError alError = AL.GetError();
|
||||
if (alError != ALError.NoError)
|
||||
{
|
||||
throw new Exception("Failed to delete ALSources[" + i.ToString() + "]: " + AL.GetErrorString(alError));
|
||||
}
|
||||
}
|
||||
ALSources = null;
|
||||
}
|
||||
}
|
||||
|
||||
public class SoundChannel : IDisposable
|
||||
{
|
||||
private const int STREAM_BUFFER_SIZE = 65536;
|
||||
private short[] streamShortBuffer;
|
||||
|
||||
private Vector3? position;
|
||||
public Vector3? Position
|
||||
@@ -20,7 +94,7 @@ namespace Barotrauma.Sounds
|
||||
|
||||
if (position != null)
|
||||
{
|
||||
uint alSource = Sound.Owner.GetSourceFromIndex(ALSourceIndex);
|
||||
uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex);
|
||||
AL.Source(alSource, ALSourceb.SourceRelative, false);
|
||||
ALError alError = AL.GetError();
|
||||
if (alError != ALError.NoError)
|
||||
@@ -37,7 +111,7 @@ namespace Barotrauma.Sounds
|
||||
}
|
||||
else
|
||||
{
|
||||
uint alSource = Sound.Owner.GetSourceFromIndex(ALSourceIndex);
|
||||
uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex);
|
||||
AL.Source(alSource, ALSourceb.SourceRelative, true);
|
||||
ALError alError = AL.GetError();
|
||||
if (alError != ALError.NoError)
|
||||
@@ -65,7 +139,7 @@ namespace Barotrauma.Sounds
|
||||
|
||||
if (ALSourceIndex < 0) return;
|
||||
|
||||
uint alSource = Sound.Owner.GetSourceFromIndex(ALSourceIndex);
|
||||
uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex);
|
||||
AL.Source(alSource, ALSourcef.ReferenceDistance, near);
|
||||
|
||||
ALError alError = AL.GetError();
|
||||
@@ -86,7 +160,7 @@ namespace Barotrauma.Sounds
|
||||
|
||||
if (ALSourceIndex < 0) return;
|
||||
|
||||
uint alSource = Sound.Owner.GetSourceFromIndex(ALSourceIndex);
|
||||
uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex);
|
||||
AL.Source(alSource, ALSourcef.MaxDistance, far);
|
||||
ALError alError = AL.GetError();
|
||||
if (alError != ALError.NoError)
|
||||
@@ -106,7 +180,7 @@ namespace Barotrauma.Sounds
|
||||
|
||||
if (ALSourceIndex < 0) return;
|
||||
|
||||
uint alSource = Sound.Owner.GetSourceFromIndex(ALSourceIndex);
|
||||
uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex);
|
||||
|
||||
float effectiveGain = gain;
|
||||
if (category != null) effectiveGain *= Sound.Owner.GetCategoryGainMultiplier(category);
|
||||
@@ -132,7 +206,7 @@ namespace Barotrauma.Sounds
|
||||
|
||||
if (!IsStream)
|
||||
{
|
||||
uint alSource = Sound.Owner.GetSourceFromIndex(ALSourceIndex);
|
||||
uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex);
|
||||
AL.Source(alSource, ALSourceb.Looping, looping);
|
||||
ALError alError = AL.GetError();
|
||||
if (alError != ALError.NoError)
|
||||
@@ -143,6 +217,14 @@ namespace Barotrauma.Sounds
|
||||
}
|
||||
}
|
||||
|
||||
public bool FilledByNetwork
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
private int decayTimer;
|
||||
|
||||
private bool muffled;
|
||||
public bool Muffled
|
||||
{
|
||||
@@ -159,7 +241,7 @@ namespace Barotrauma.Sounds
|
||||
|
||||
if (!IsStream)
|
||||
{
|
||||
uint alSource = Sound.Owner.GetSourceFromIndex(ALSourceIndex);
|
||||
uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex);
|
||||
int playbackPos; AL.GetSource(alSource, ALGetSourcei.SampleOffset, out playbackPos);
|
||||
ALError alError = AL.GetError();
|
||||
if (alError != ALError.NoError)
|
||||
@@ -221,7 +303,7 @@ namespace Barotrauma.Sounds
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
} = -1;
|
||||
|
||||
public bool IsStream
|
||||
{
|
||||
@@ -231,7 +313,8 @@ namespace Barotrauma.Sounds
|
||||
private int streamSeekPos;
|
||||
private bool startedPlaying;
|
||||
private bool reachedEndSample;
|
||||
private uint[] streamBuffers;
|
||||
private readonly uint[] streamBuffers;
|
||||
private readonly List<uint> emptyBuffers;
|
||||
|
||||
private object mutex;
|
||||
|
||||
@@ -241,11 +324,11 @@ namespace Barotrauma.Sounds
|
||||
{
|
||||
if (ALSourceIndex < 0) return false;
|
||||
if (IsStream && !reachedEndSample) return true;
|
||||
bool playing = AL.GetSourceState(Sound.Owner.GetSourceFromIndex(ALSourceIndex)) == ALSourceState.Playing;
|
||||
bool playing = AL.GetSourceState(Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex)) == ALSourceState.Playing;
|
||||
ALError alError = AL.GetError();
|
||||
if (alError != ALError.NoError)
|
||||
{
|
||||
throw new Exception("Failed to determine playing state from source: "+AL.GetErrorString(alError));
|
||||
throw new Exception("Failed to determine playing state from source: " + AL.GetErrorString(alError));
|
||||
}
|
||||
return playing;
|
||||
}
|
||||
@@ -256,87 +339,98 @@ namespace Barotrauma.Sounds
|
||||
Sound = sound;
|
||||
|
||||
IsStream = sound.Stream;
|
||||
FilledByNetwork = sound is VoipSound;
|
||||
decayTimer = 0;
|
||||
streamSeekPos = 0; reachedEndSample = false;
|
||||
startedPlaying = true;
|
||||
|
||||
|
||||
mutex = new object();
|
||||
|
||||
ALSourceIndex = sound.Owner.AssignFreeSourceToChannel(this);
|
||||
|
||||
if (ALSourceIndex>=0)
|
||||
lock (mutex)
|
||||
{
|
||||
if (!IsStream)
|
||||
if (sound.Owner.CountPlayingInstances(sound) < sound.MaxSimultaneousInstances)
|
||||
{
|
||||
AL.BindBufferToSource(sound.Owner.GetSourceFromIndex(ALSourceIndex), 0);
|
||||
ALError alError = AL.GetError();
|
||||
if (alError != ALError.NoError)
|
||||
{
|
||||
throw new Exception("Failed to reset source buffer: " + AL.GetErrorString(alError));
|
||||
}
|
||||
|
||||
if (!AL.IsBuffer(sound.ALBuffer))
|
||||
{
|
||||
throw new Exception(sound.Filename + " has an invalid buffer!");
|
||||
}
|
||||
|
||||
uint alBuffer = sound.Owner.GetCategoryMuffle(category) || muffle ? sound.ALMuffledBuffer : sound.ALBuffer;
|
||||
AL.BindBufferToSource(sound.Owner.GetSourceFromIndex(ALSourceIndex), alBuffer);
|
||||
alError = AL.GetError();
|
||||
if (alError != ALError.NoError)
|
||||
{
|
||||
throw new Exception("Failed to bind buffer to source (" +ALSourceIndex.ToString()+":"+sound.Owner.GetSourceFromIndex(ALSourceIndex)+"," +sound.ALBuffer.ToString()+"): " + AL.GetErrorString(alError));
|
||||
}
|
||||
|
||||
AL.SourcePlay(sound.Owner.GetSourceFromIndex(ALSourceIndex));
|
||||
alError = AL.GetError();
|
||||
if (alError != ALError.NoError)
|
||||
{
|
||||
throw new Exception("Failed to play source: " + AL.GetErrorString(alError));
|
||||
}
|
||||
ALSourceIndex = sound.Owner.AssignFreeSourceToChannel(this);
|
||||
}
|
||||
else
|
||||
|
||||
if (ALSourceIndex >= 0)
|
||||
{
|
||||
AL.BindBufferToSource(sound.Owner.GetSourceFromIndex(ALSourceIndex), (uint)sound.ALBuffer);
|
||||
ALError alError = AL.GetError();
|
||||
if (alError != ALError.NoError)
|
||||
if (!IsStream)
|
||||
{
|
||||
throw new Exception("Failed to reset source buffer: " + AL.GetErrorString(alError));
|
||||
}
|
||||
|
||||
AL.Source(sound.Owner.GetSourceFromIndex(ALSourceIndex), ALSourceb.Looping, false);
|
||||
alError = AL.GetError();
|
||||
if (alError != ALError.NoError)
|
||||
{
|
||||
throw new Exception("Failed to set stream looping state: " + AL.GetErrorString(alError));
|
||||
}
|
||||
AL.BindBufferToSource(sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex), 0);
|
||||
ALError alError = AL.GetError();
|
||||
if (alError != ALError.NoError)
|
||||
{
|
||||
throw new Exception("Failed to reset source buffer: " + AL.GetErrorString(alError));
|
||||
}
|
||||
|
||||
streamBuffers = new uint[4];
|
||||
for (int i=0;i<4;i++)
|
||||
{
|
||||
AL.GenBuffer(out streamBuffers[i]);
|
||||
if (!AL.IsBuffer(sound.ALBuffer))
|
||||
{
|
||||
throw new Exception(sound.Filename + " has an invalid buffer!");
|
||||
}
|
||||
|
||||
uint alBuffer = sound.Owner.GetCategoryMuffle(category) || muffle ? sound.ALMuffledBuffer : sound.ALBuffer;
|
||||
AL.BindBufferToSource(sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex), alBuffer);
|
||||
alError = AL.GetError();
|
||||
if (alError != ALError.NoError)
|
||||
{
|
||||
throw new Exception("Failed to generate stream buffers: " + AL.GetErrorString(alError));
|
||||
throw new Exception("Failed to bind buffer to source (" + ALSourceIndex.ToString() + ":" + sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex) + "," + sound.ALBuffer.ToString() + "): " + AL.GetErrorString(alError));
|
||||
}
|
||||
|
||||
if (!AL.IsBuffer(streamBuffers[i]))
|
||||
AL.SourcePlay(sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex));
|
||||
alError = AL.GetError();
|
||||
if (alError != ALError.NoError)
|
||||
{
|
||||
throw new Exception("Generated streamBuffer[" + i.ToString() + "] is invalid!");
|
||||
throw new Exception("Failed to play source: " + AL.GetErrorString(alError));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AL.BindBufferToSource(sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex), (uint)sound.ALBuffer);
|
||||
ALError alError = AL.GetError();
|
||||
if (alError != ALError.NoError)
|
||||
{
|
||||
throw new Exception("Failed to reset source buffer: " + AL.GetErrorString(alError));
|
||||
}
|
||||
|
||||
Sound.Owner.InitStreamThread();
|
||||
AL.Source(sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex), ALSourceb.Looping, false);
|
||||
alError = AL.GetError();
|
||||
if (alError != ALError.NoError)
|
||||
{
|
||||
throw new Exception("Failed to set stream looping state: " + AL.GetErrorString(alError));
|
||||
}
|
||||
|
||||
streamShortBuffer = new short[STREAM_BUFFER_SIZE];
|
||||
|
||||
streamBuffers = new uint[4];
|
||||
emptyBuffers = new List<uint>();
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
AL.GenBuffer(out streamBuffers[i]);
|
||||
|
||||
alError = AL.GetError();
|
||||
if (alError != ALError.NoError)
|
||||
{
|
||||
throw new Exception("Failed to generate stream buffers: " + AL.GetErrorString(alError));
|
||||
}
|
||||
|
||||
if (!AL.IsBuffer(streamBuffers[i]))
|
||||
{
|
||||
throw new Exception("Generated streamBuffer[" + i.ToString() + "] is invalid!");
|
||||
}
|
||||
}
|
||||
|
||||
Sound.Owner.InitStreamThread();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.Position = position;
|
||||
this.Gain = gain;
|
||||
this.Looping = false;
|
||||
this.Near = near;
|
||||
this.Far = far;
|
||||
this.Category = category;
|
||||
|
||||
this.Position = position;
|
||||
this.Gain = gain;
|
||||
this.Looping = false;
|
||||
this.Near = near;
|
||||
this.Far = far;
|
||||
this.Category = category;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -345,7 +439,7 @@ namespace Barotrauma.Sounds
|
||||
{
|
||||
if (ALSourceIndex >= 0)
|
||||
{
|
||||
AL.SourceStop(Sound.Owner.GetSourceFromIndex(ALSourceIndex));
|
||||
AL.SourceStop(Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex));
|
||||
ALError alError = AL.GetError();
|
||||
if (alError != ALError.NoError)
|
||||
{
|
||||
@@ -354,7 +448,7 @@ namespace Barotrauma.Sounds
|
||||
|
||||
if (IsStream)
|
||||
{
|
||||
uint alSource = Sound.Owner.GetSourceFromIndex(ALSourceIndex);
|
||||
uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex);
|
||||
|
||||
AL.SourceStop(alSource);
|
||||
alError = AL.GetError();
|
||||
@@ -403,7 +497,7 @@ namespace Barotrauma.Sounds
|
||||
}
|
||||
else
|
||||
{
|
||||
AL.BindBufferToSource(Sound.Owner.GetSourceFromIndex(ALSourceIndex), 0);
|
||||
AL.BindBufferToSource(Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex), 0);
|
||||
alError = AL.GetError();
|
||||
if (alError != ALError.NoError)
|
||||
{
|
||||
@@ -424,7 +518,7 @@ namespace Barotrauma.Sounds
|
||||
{
|
||||
if (!reachedEndSample)
|
||||
{
|
||||
uint alSource = Sound.Owner.GetSourceFromIndex(ALSourceIndex);
|
||||
uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex);
|
||||
|
||||
bool playing = AL.GetSourceState(alSource) == ALSourceState.Playing;
|
||||
ALError alError = AL.GetError();
|
||||
@@ -432,7 +526,7 @@ namespace Barotrauma.Sounds
|
||||
{
|
||||
throw new Exception("Failed to determine playing state from streamed source: " + AL.GetErrorString(alError));
|
||||
}
|
||||
|
||||
|
||||
int buffersToUnqueue = 0;
|
||||
int[] unqueuedBuffers = null;
|
||||
if (!startedPlaying)
|
||||
@@ -445,8 +539,14 @@ namespace Barotrauma.Sounds
|
||||
throw new Exception("Failed to determine processed buffers from streamed source: " + AL.GetErrorString(alError));
|
||||
}
|
||||
|
||||
unqueuedBuffers = new int[buffersToUnqueue];
|
||||
unqueuedBuffers = new int[buffersToUnqueue+emptyBuffers.Count];
|
||||
AL.SourceUnqueueBuffers((int)alSource, buffersToUnqueue, unqueuedBuffers);
|
||||
for (int i = 0; i < emptyBuffers.Count; i++)
|
||||
{
|
||||
unqueuedBuffers[buffersToUnqueue + i] = (int)emptyBuffers[i];
|
||||
}
|
||||
buffersToUnqueue += emptyBuffers.Count;
|
||||
emptyBuffers.Clear();
|
||||
alError = AL.GetError();
|
||||
if (alError != ALError.NoError)
|
||||
{
|
||||
@@ -463,23 +563,47 @@ namespace Barotrauma.Sounds
|
||||
unqueuedBuffers[i] = (int)streamBuffers[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < buffersToUnqueue; i++)
|
||||
{
|
||||
short[] buffer = new short[STREAM_BUFFER_SIZE];
|
||||
short[] buffer = streamShortBuffer;
|
||||
int readSamples = Sound.FillStreamBuffer(streamSeekPos, buffer);
|
||||
streamSeekPos += readSamples;
|
||||
if (readSamples < STREAM_BUFFER_SIZE)
|
||||
if (FilledByNetwork)
|
||||
{
|
||||
if (looping)
|
||||
if (Sound is VoipSound voipSound)
|
||||
{
|
||||
streamSeekPos = 0;
|
||||
voipSound.ApplyFilters(buffer, readSamples);
|
||||
}
|
||||
|
||||
if (readSamples <= 0)
|
||||
{
|
||||
decayTimer++;
|
||||
if (decayTimer > 120) //TODO: replace magic number
|
||||
{
|
||||
reachedEndSample = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reachedEndSample = true;
|
||||
decayTimer = 0;
|
||||
}
|
||||
}
|
||||
else if (Sound.StreamsReliably)
|
||||
{
|
||||
streamSeekPos += readSamples;
|
||||
if (readSamples < STREAM_BUFFER_SIZE)
|
||||
{
|
||||
if (looping)
|
||||
{
|
||||
streamSeekPos = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
reachedEndSample = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (readSamples > 0)
|
||||
{
|
||||
AL.BufferData<short>(unqueuedBuffers[i], Sound.ALFormat, buffer, readSamples, Sound.SampleRate);
|
||||
@@ -498,6 +622,14 @@ namespace Barotrauma.Sounds
|
||||
throw new Exception("Failed to queue buffer[" + i.ToString() + "] to stream: " + AL.GetErrorString(alError));
|
||||
}
|
||||
}
|
||||
else if (readSamples < 0)
|
||||
{
|
||||
reachedEndSample = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
emptyBuffers.Add((uint)unqueuedBuffers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (AL.GetSourceState(alSource) != ALSourceState.Playing)
|
||||
|
||||
Reference in New Issue
Block a user