2f107db...5202af9
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using OpenTK.Audio.OpenAL;
|
||||
using NVorbis;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Barotrauma.Sounds
|
||||
{
|
||||
@@ -8,7 +9,10 @@ namespace Barotrauma.Sounds
|
||||
{
|
||||
private VorbisReader reader;
|
||||
|
||||
public OggSound(SoundManager owner,string filename,bool stream) : base(owner,filename,stream)
|
||||
//key = sample rate, value = filter
|
||||
private static Dictionary<int, BiQuad> muffleFilters = new Dictionary<int, BiQuad>();
|
||||
|
||||
public OggSound(SoundManager owner, string filename, bool stream) : base(owner, filename, stream, true)
|
||||
{
|
||||
if (!ToolBox.IsProperFilenameCase(filename))
|
||||
{
|
||||
@@ -40,7 +44,7 @@ namespace Barotrauma.Sounds
|
||||
throw new Exception("Failed to set buffer data for non-streamed audio! "+AL.GetErrorString(alError));
|
||||
}
|
||||
|
||||
MuffleBuffer(floatBuffer, reader.Channels);
|
||||
MuffleBuffer(floatBuffer, SampleRate, reader.Channels);
|
||||
|
||||
CastBuffer(floatBuffer, shortBuffer, readSamples);
|
||||
|
||||
@@ -60,53 +64,28 @@ namespace Barotrauma.Sounds
|
||||
public override int FillStreamBuffer(int samplePos, short[] buffer)
|
||||
{
|
||||
if (!Stream) throw new Exception("Called FillStreamBuffer on a non-streamed sound!");
|
||||
|
||||
|
||||
if (samplePos >= reader.TotalSamples * reader.Channels * 2) return 0;
|
||||
|
||||
samplePos /= reader.Channels*2;
|
||||
samplePos /= reader.Channels * 2;
|
||||
reader.DecodedPosition = samplePos;
|
||||
|
||||
float[] floatBuffer = new float[buffer.Length];
|
||||
int readSamples = reader.ReadSamples(floatBuffer, 0, buffer.Length/2);
|
||||
int readSamples = reader.ReadSamples(floatBuffer, 0, buffer.Length / 2);
|
||||
//MuffleBuffer(floatBuffer, reader.Channels);
|
||||
CastBuffer(floatBuffer, buffer, readSamples);
|
||||
|
||||
return readSamples*2;
|
||||
|
||||
return readSamples * 2;
|
||||
}
|
||||
|
||||
static void MuffleBuffer(float[] buffer,int channelCount)
|
||||
static void MuffleBuffer(float[] buffer, int sampleRate, int channelCount)
|
||||
{
|
||||
//this function will probably have to replace EFX on OSX
|
||||
float[] avgvals = new float[channelCount];
|
||||
for (int j = 0; j < channelCount; j++)
|
||||
if (!muffleFilters.TryGetValue(sampleRate, out BiQuad filter))
|
||||
{
|
||||
avgvals[j] = buffer[j];
|
||||
}
|
||||
for (int i = 0; i < buffer.Length; i+=channelCount)
|
||||
{
|
||||
for (int j = 0; j < channelCount; j++)
|
||||
{
|
||||
float fval = buffer[i + j];
|
||||
float weight = 0.7f;
|
||||
weight = 1.0f - weight;
|
||||
weight *= weight * weight;
|
||||
avgvals[j] = (avgvals[j] * (1.0f - weight) + fval * weight);
|
||||
fval = avgvals[j]*1.7f;
|
||||
buffer[i + j] = fval;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void CastBuffer(float[] inBuffer, short[] outBuffer, int length)
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
float fval = Math.Max(Math.Min(inBuffer[i], 1.0f), -1.0f);
|
||||
int temp = (int)(32767f * fval);
|
||||
if (temp > short.MaxValue) temp = short.MaxValue;
|
||||
else if (temp < short.MinValue) temp = short.MinValue;
|
||||
outBuffer[i] = (short)temp;
|
||||
filter = new LowpassFilter(sampleRate, 400);
|
||||
muffleFilters.Add(sampleRate, filter);
|
||||
}
|
||||
filter.Process(buffer);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
|
||||
@@ -7,6 +7,12 @@ namespace Barotrauma.Sounds
|
||||
{
|
||||
public abstract class Sound : IDisposable
|
||||
{
|
||||
protected bool disposed;
|
||||
public bool Disposed
|
||||
{
|
||||
get { return disposed; }
|
||||
}
|
||||
|
||||
public SoundManager Owner
|
||||
{
|
||||
get;
|
||||
@@ -25,6 +31,20 @@ namespace Barotrauma.Sounds
|
||||
protected set;
|
||||
}
|
||||
|
||||
public bool StreamsReliably
|
||||
{
|
||||
get;
|
||||
protected set;
|
||||
}
|
||||
|
||||
public virtual SoundManager.SourcePoolIndex SourcePoolIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
return SoundManager.SourcePoolIndex.Default;
|
||||
}
|
||||
}
|
||||
|
||||
private uint alBuffer;
|
||||
public uint ALBuffer
|
||||
{
|
||||
@@ -49,15 +69,21 @@ namespace Barotrauma.Sounds
|
||||
protected set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// How many instances of the same sound clip can be playing at the same time
|
||||
/// </summary>
|
||||
public int MaxSimultaneousInstances = 5;
|
||||
|
||||
public float BaseGain;
|
||||
public float BaseNear;
|
||||
public float BaseFar;
|
||||
|
||||
public Sound(SoundManager owner,string filename,bool stream)
|
||||
|
||||
public Sound(SoundManager owner, string filename, bool stream, bool streamsReliably)
|
||||
{
|
||||
Owner = owner;
|
||||
Filename = Path.GetFullPath(filename);
|
||||
Stream = stream;
|
||||
StreamsReliably = streamsReliably;
|
||||
|
||||
BaseGain = 1.0f;
|
||||
BaseNear = 100.0f;
|
||||
@@ -100,40 +126,63 @@ namespace Barotrauma.Sounds
|
||||
return GetType().ToString() + " (" + Filename + ")";
|
||||
}
|
||||
|
||||
public bool IsPlaying()
|
||||
public virtual bool IsPlaying()
|
||||
{
|
||||
return Owner.IsPlaying(this);
|
||||
}
|
||||
|
||||
public SoundChannel Play(float gain, float range, Vector2 position, bool muffle = false)
|
||||
public virtual SoundChannel Play(float gain, float range, Vector2 position, bool muffle = false)
|
||||
{
|
||||
return new SoundChannel(this, gain, new Vector3(position.X, position.Y, 0.0f), range * 0.4f, range, "default", muffle);
|
||||
}
|
||||
|
||||
public SoundChannel Play(Vector3? position, float gain, bool muffle = false)
|
||||
|
||||
public virtual SoundChannel Play(Vector3? position, float gain, bool muffle = false)
|
||||
{
|
||||
return new SoundChannel(this, gain, position, BaseNear, BaseFar, "default", muffle);
|
||||
}
|
||||
|
||||
public SoundChannel Play(float gain)
|
||||
public virtual SoundChannel Play(float gain)
|
||||
{
|
||||
return Play(null, gain);
|
||||
}
|
||||
|
||||
public SoundChannel Play()
|
||||
public virtual SoundChannel Play()
|
||||
{
|
||||
return Play(BaseGain);
|
||||
}
|
||||
|
||||
public SoundChannel Play(float? gain, string category)
|
||||
public virtual SoundChannel Play(float? gain, string category)
|
||||
{
|
||||
if (Owner.CountPlayingInstances(this) >= MaxSimultaneousInstances) { return null; }
|
||||
return new SoundChannel(this, gain ?? BaseGain, null, BaseNear, BaseFar, category);
|
||||
}
|
||||
|
||||
static protected void CastBuffer(float[] inBuffer, short[] outBuffer, int length)
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
outBuffer[i] = FloatToShort(inBuffer[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static protected short FloatToShort(float fVal)
|
||||
{
|
||||
int temp = (int)(32767 * fVal);
|
||||
if (temp > short.MaxValue) temp = short.MaxValue;
|
||||
else if (temp < short.MinValue) temp = short.MinValue;
|
||||
return (short)temp;
|
||||
}
|
||||
static protected float ShortToFloat(short shortVal)
|
||||
{
|
||||
return shortVal / 32767f;
|
||||
}
|
||||
|
||||
public abstract int FillStreamBuffer(int samplePos, short[] buffer);
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
if (disposed) { return; }
|
||||
|
||||
Owner.KillChannels(this);
|
||||
if (alBuffer != 0)
|
||||
{
|
||||
@@ -150,7 +199,24 @@ namespace Barotrauma.Sounds
|
||||
throw new Exception("Failed to delete OpenAL buffer for non-streamed sound: " + AL.GetErrorString(alError));
|
||||
}
|
||||
}
|
||||
if (alMuffledBuffer != 0)
|
||||
{
|
||||
if (!AL.IsBuffer(alMuffledBuffer))
|
||||
{
|
||||
throw new Exception("Buffer to delete is invalid!");
|
||||
}
|
||||
|
||||
AL.DeleteBuffer(ref alMuffledBuffer); alMuffledBuffer = 0;
|
||||
|
||||
ALError alError = AL.GetError();
|
||||
if (alError != ALError.NoError)
|
||||
{
|
||||
throw new Exception("Failed to delete OpenAL buffer for non-streamed sound: " + AL.GetErrorString(alError));
|
||||
}
|
||||
}
|
||||
|
||||
Owner.RemoveSound(this);
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -0,0 +1,492 @@
|
||||
using System;
|
||||
|
||||
namespace Barotrauma.Sounds
|
||||
{
|
||||
/* This code is adapted from CSCore (.NET Audio Library) which is under a Microsoft Public License (Ms-PL) license.*/
|
||||
|
||||
/*
|
||||
Microsoft Public License (Ms-PL)
|
||||
|
||||
This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
|
||||
|
||||
1. Definitions
|
||||
The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
|
||||
|
||||
A "contribution" is the original software, or any additions or changes to the software.
|
||||
|
||||
A "contributor" is any person that distributes its contribution under this license.
|
||||
|
||||
"Licensed patents" are a contributor's patent claims that read directly on its contribution.
|
||||
|
||||
2. Grant of Rights
|
||||
(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
|
||||
|
||||
(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
|
||||
|
||||
3. Conditions and Limitations
|
||||
(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
|
||||
|
||||
(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
|
||||
|
||||
(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
|
||||
|
||||
(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
|
||||
|
||||
(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
|
||||
*/
|
||||
|
||||
/*
|
||||
* These implementations are based on http://www.earlevel.com/main/2011/01/02/biquad-formulas/
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Represents a biquad-filter.
|
||||
/// </summary>
|
||||
public abstract class BiQuad
|
||||
{
|
||||
/// <summary>
|
||||
/// The a0 value.
|
||||
/// </summary>
|
||||
protected double A0;
|
||||
/// <summary>
|
||||
/// The a1 value.
|
||||
/// </summary>
|
||||
protected double A1;
|
||||
/// <summary>
|
||||
/// The a2 value.
|
||||
/// </summary>
|
||||
protected double A2;
|
||||
/// <summary>
|
||||
/// The b1 value.
|
||||
/// </summary>
|
||||
protected double B1;
|
||||
/// <summary>
|
||||
/// The b2 value.
|
||||
/// </summary>
|
||||
protected double B2;
|
||||
/// <summary>
|
||||
/// The q value.
|
||||
/// </summary>
|
||||
private double _q;
|
||||
/// <summary>
|
||||
/// The gain value in dB.
|
||||
/// </summary>
|
||||
private double _gainDB;
|
||||
/// <summary>
|
||||
/// The z1 value.
|
||||
/// </summary>
|
||||
protected double Z1;
|
||||
/// <summary>
|
||||
/// The z2 value.
|
||||
/// </summary>
|
||||
protected double Z2;
|
||||
|
||||
private double _frequency;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the frequency.
|
||||
/// </summary>
|
||||
/// <exception cref="System.ArgumentOutOfRangeException">value;The samplerate has to be bigger than 2 * frequency.</exception>
|
||||
public double Frequency
|
||||
{
|
||||
get { return _frequency; }
|
||||
set
|
||||
{
|
||||
if (SampleRate < value * 2)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("value", "The samplerate has to be bigger than 2 * frequency.");
|
||||
}
|
||||
_frequency = value;
|
||||
CalculateBiQuadCoefficients();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sample rate.
|
||||
/// </summary>
|
||||
public int SampleRate { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The q value.
|
||||
/// </summary>
|
||||
public double Q
|
||||
{
|
||||
get { return _q; }
|
||||
set
|
||||
{
|
||||
if (value <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("value");
|
||||
}
|
||||
_q = value;
|
||||
CalculateBiQuadCoefficients();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the gain value in dB.
|
||||
/// </summary>
|
||||
public double GainDB
|
||||
{
|
||||
get { return _gainDB; }
|
||||
set
|
||||
{
|
||||
_gainDB = value;
|
||||
CalculateBiQuadCoefficients();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BiQuad"/> class.
|
||||
/// </summary>
|
||||
/// <param name="sampleRate">The sample rate.</param>
|
||||
/// <param name="frequency">The frequency.</param>
|
||||
/// <exception cref="System.ArgumentOutOfRangeException">
|
||||
/// sampleRate
|
||||
/// or
|
||||
/// frequency
|
||||
/// or
|
||||
/// q
|
||||
/// </exception>
|
||||
protected BiQuad(int sampleRate, double frequency)
|
||||
: this(sampleRate, frequency, 1.0 / Math.Sqrt(2))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BiQuad"/> class.
|
||||
/// </summary>
|
||||
/// <param name="sampleRate">The sample rate.</param>
|
||||
/// <param name="frequency">The frequency.</param>
|
||||
/// <param name="q">The q.</param>
|
||||
/// <exception cref="System.ArgumentOutOfRangeException">
|
||||
/// sampleRate
|
||||
/// or
|
||||
/// frequency
|
||||
/// or
|
||||
/// q
|
||||
/// </exception>
|
||||
protected BiQuad(int sampleRate, double frequency, double q)
|
||||
{
|
||||
if (sampleRate <= 0)
|
||||
throw new ArgumentOutOfRangeException("sampleRate");
|
||||
if (frequency <= 0)
|
||||
throw new ArgumentOutOfRangeException("frequency");
|
||||
if (q <= 0)
|
||||
throw new ArgumentOutOfRangeException("q");
|
||||
SampleRate = sampleRate;
|
||||
Frequency = frequency;
|
||||
Q = q;
|
||||
GainDB = 6;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes a single <paramref name="input"/> sample and returns the result.
|
||||
/// </summary>
|
||||
/// <param name="input">The input sample to process.</param>
|
||||
/// <returns>The result of the processed <paramref name="input"/> sample.</returns>
|
||||
public float Process(float input)
|
||||
{
|
||||
double o = input * A0 + Z1;
|
||||
Z1 = input * A1 + Z2 - B1 * o;
|
||||
Z2 = input * A2 - B2 * o;
|
||||
return (float)o;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes multiple <paramref name="input"/> samples.
|
||||
/// </summary>
|
||||
/// <param name="input">The input samples to process.</param>
|
||||
/// <remarks>The result of the calculation gets stored within the <paramref name="input"/> array.</remarks>
|
||||
public void Process(float[] input)
|
||||
{
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
input[i] = Process(input[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates all coefficients.
|
||||
/// </summary>
|
||||
protected abstract void CalculateBiQuadCoefficients();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to apply a lowpass-filter to a signal.
|
||||
/// </summary>
|
||||
public class LowpassFilter : BiQuad
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LowpassFilter"/> class.
|
||||
/// </summary>
|
||||
/// <param name="sampleRate">The sample rate.</param>
|
||||
/// <param name="frequency">The filter's corner frequency.</param>
|
||||
public LowpassFilter(int sampleRate, double frequency)
|
||||
: base(sampleRate, frequency)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates all coefficients.
|
||||
/// </summary>
|
||||
protected override void CalculateBiQuadCoefficients()
|
||||
{
|
||||
double k = Math.Tan(Math.PI * Frequency / SampleRate);
|
||||
var norm = 1 / (1 + k / Q + k * k);
|
||||
A0 = k * k * norm;
|
||||
A1 = 2 * A0;
|
||||
A2 = A0;
|
||||
B1 = 2 * (k * k - 1) * norm;
|
||||
B2 = (1 - k / Q + k * k) * norm;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to apply a highpass-filter to a signal.
|
||||
/// </summary>
|
||||
public class HighpassFilter : BiQuad
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HighpassFilter"/> class.
|
||||
/// </summary>
|
||||
/// <param name="sampleRate">The sample rate.</param>
|
||||
/// <param name="frequency">The filter's corner frequency.</param>
|
||||
public HighpassFilter(int sampleRate, double frequency)
|
||||
: base(sampleRate, frequency)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates all coefficients.
|
||||
/// </summary>
|
||||
protected override void CalculateBiQuadCoefficients()
|
||||
{
|
||||
double k = Math.Tan(Math.PI * Frequency / SampleRate);
|
||||
var norm = 1 / (1 + k / Q + k * k);
|
||||
A0 = 1 * norm;
|
||||
A1 = -2 * A0;
|
||||
A2 = A0;
|
||||
B1 = 2 * (k * k - 1) * norm;
|
||||
B2 = (1 - k / Q + k * k) * norm;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to apply a bandpass-filter to a signal.
|
||||
/// </summary>
|
||||
public class BandpassFilter : BiQuad
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BandpassFilter"/> class.
|
||||
/// </summary>
|
||||
/// <param name="sampleRate">The sample rate.</param>
|
||||
/// <param name="frequency">The filter's corner frequency.</param>
|
||||
public BandpassFilter(int sampleRate, double frequency)
|
||||
: base(sampleRate, frequency)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates all coefficients.
|
||||
/// </summary>
|
||||
protected override void CalculateBiQuadCoefficients()
|
||||
{
|
||||
double k = Math.Tan(Math.PI * Frequency / SampleRate);
|
||||
double norm = 1 / (1 + k / Q + k * k);
|
||||
A0 = k / Q * norm;
|
||||
A1 = 0;
|
||||
A2 = -A0;
|
||||
B1 = 2 * (k * k - 1) * norm;
|
||||
B2 = (1 - k / Q + k * k) * norm;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to apply a notch-filter to a signal.
|
||||
/// </summary>
|
||||
public class NotchFilter : BiQuad
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NotchFilter"/> class.
|
||||
/// </summary>
|
||||
/// <param name="sampleRate">The sample rate.</param>
|
||||
/// <param name="frequency">The filter's corner frequency.</param>
|
||||
public NotchFilter(int sampleRate, double frequency)
|
||||
: base(sampleRate, frequency)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates all coefficients.
|
||||
/// </summary>
|
||||
protected override void CalculateBiQuadCoefficients()
|
||||
{
|
||||
double k = Math.Tan(Math.PI * Frequency / SampleRate);
|
||||
double norm = 1 / (1 + k / Q + k * k);
|
||||
A0 = (1 + k * k) * norm;
|
||||
A1 = 2 * (k * k - 1) * norm;
|
||||
A2 = A0;
|
||||
B1 = A1;
|
||||
B2 = (1 - k / Q + k * k) * norm;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to apply a lowshelf-filter to a signal.
|
||||
/// </summary>
|
||||
public class LowShelfFilter : BiQuad
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LowShelfFilter"/> class.
|
||||
/// </summary>
|
||||
/// <param name="sampleRate">The sample rate.</param>
|
||||
/// <param name="frequency">The filter's corner frequency.</param>
|
||||
/// <param name="gainDB">Gain value in dB.</param>
|
||||
public LowShelfFilter(int sampleRate, double frequency, double gainDB)
|
||||
: base(sampleRate, frequency)
|
||||
{
|
||||
GainDB = gainDB;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates all coefficients.
|
||||
/// </summary>
|
||||
protected override void CalculateBiQuadCoefficients()
|
||||
{
|
||||
const double sqrt2 = 1.4142135623730951;
|
||||
double k = Math.Tan(Math.PI * Frequency / SampleRate);
|
||||
double v = Math.Pow(10, Math.Abs(GainDB) / 20.0);
|
||||
double norm;
|
||||
if (GainDB >= 0)
|
||||
{ // boost
|
||||
norm = 1 / (1 + sqrt2 * k + k * k);
|
||||
A0 = (1 + Math.Sqrt(2 * v) * k + v * k * k) * norm;
|
||||
A1 = 2 * (v * k * k - 1) * norm;
|
||||
A2 = (1 - Math.Sqrt(2 * v) * k + v * k * k) * norm;
|
||||
B1 = 2 * (k * k - 1) * norm;
|
||||
B2 = (1 - sqrt2 * k + k * k) * norm;
|
||||
}
|
||||
else
|
||||
{ // cut
|
||||
norm = 1 / (1 + Math.Sqrt(2 * v) * k + v * k * k);
|
||||
A0 = (1 + sqrt2 * k + k * k) * norm;
|
||||
A1 = 2 * (k * k - 1) * norm;
|
||||
A2 = (1 - sqrt2 * k + k * k) * norm;
|
||||
B1 = 2 * (v * k * k - 1) * norm;
|
||||
B2 = (1 - Math.Sqrt(2 * v) * k + v * k * k) * norm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to apply a highshelf-filter to a signal.
|
||||
/// </summary>
|
||||
public class HighShelfFilter : BiQuad
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HighShelfFilter"/> class.
|
||||
/// </summary>
|
||||
/// <param name="sampleRate">The sample rate.</param>
|
||||
/// <param name="frequency">The filter's corner frequency.</param>
|
||||
/// <param name="gainDB">Gain value in dB.</param>
|
||||
public HighShelfFilter(int sampleRate, double frequency, double gainDB)
|
||||
: base(sampleRate, frequency)
|
||||
{
|
||||
GainDB = gainDB;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates all coefficients.
|
||||
/// </summary>
|
||||
protected override void CalculateBiQuadCoefficients()
|
||||
{
|
||||
const double sqrt2 = 1.4142135623730951;
|
||||
double k = Math.Tan(Math.PI * Frequency / SampleRate);
|
||||
double v = Math.Pow(10, Math.Abs(GainDB) / 20.0);
|
||||
double norm;
|
||||
if (GainDB >= 0)
|
||||
{ // boost
|
||||
norm = 1 / (1 + sqrt2 * k + k * k);
|
||||
A0 = (v + Math.Sqrt(2 * v) * k + k * k) * norm;
|
||||
A1 = 2 * (k * k - v) * norm;
|
||||
A2 = (v - Math.Sqrt(2 * v) * k + k * k) * norm;
|
||||
B1 = 2 * (k * k - 1) * norm;
|
||||
B2 = (1 - sqrt2 * k + k * k) * norm;
|
||||
}
|
||||
else
|
||||
{ // cut
|
||||
norm = 1 / (v + Math.Sqrt(2 * v) * k + k * k);
|
||||
A0 = (1 + sqrt2 * k + k * k) * norm;
|
||||
A1 = 2 * (k * k - 1) * norm;
|
||||
A2 = (1 - sqrt2 * k + k * k) * norm;
|
||||
B1 = 2 * (k * k - v) * norm;
|
||||
B2 = (v - Math.Sqrt(2 * v) * k + k * k) * norm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to apply an peak-filter to a signal.
|
||||
/// </summary>
|
||||
public class PeakFilter : BiQuad
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the bandwidth.
|
||||
/// </summary>
|
||||
public double BandWidth
|
||||
{
|
||||
get { return Q; }
|
||||
set
|
||||
{
|
||||
if (value <= 0)
|
||||
throw new ArgumentOutOfRangeException("value");
|
||||
Q = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PeakFilter"/> class.
|
||||
/// </summary>
|
||||
/// <param name="sampleRate">The sampleRate of the audio data to process.</param>
|
||||
/// <param name="frequency">The center frequency to adjust.</param>
|
||||
/// <param name="bandWidth">The bandWidth.</param>
|
||||
/// <param name="peakGainDB">The gain value in dB.</param>
|
||||
public PeakFilter(int sampleRate, double frequency, double bandWidth, double peakGainDB)
|
||||
: base(sampleRate, frequency, bandWidth)
|
||||
{
|
||||
GainDB = peakGainDB;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates all coefficients.
|
||||
/// </summary>
|
||||
protected override void CalculateBiQuadCoefficients()
|
||||
{
|
||||
double norm;
|
||||
double v = Math.Pow(10, Math.Abs(GainDB) / 20.0);
|
||||
double k = Math.Tan(Math.PI * Frequency / SampleRate);
|
||||
double q = Q;
|
||||
|
||||
if (GainDB >= 0) //boost
|
||||
{
|
||||
norm = 1 / (1 + 1 / q * k + k * k);
|
||||
A0 = (1 + v / q * k + k * k) * norm;
|
||||
A1 = 2 * (k * k - 1) * norm;
|
||||
A2 = (1 - v / q * k + k * k) * norm;
|
||||
B1 = A1;
|
||||
B2 = (1 - 1 / q * k + k * k) * norm;
|
||||
}
|
||||
else //cut
|
||||
{
|
||||
norm = 1 / (1 + v / q * k + k * k);
|
||||
A0 = (1 + 1 / q * k + k * k) * norm;
|
||||
A1 = 2 * (k * k - 1) * norm;
|
||||
A2 = (1 - 1 / q * k + k * k) * norm;
|
||||
B1 = A1;
|
||||
B2 = (1 - v / q * k + k * k) * norm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,11 +15,16 @@ namespace Barotrauma.Sounds
|
||||
|
||||
private IntPtr alcDevice;
|
||||
private OpenTK.ContextHandle alcContext;
|
||||
private List<string> alcCaptureDeviceNames;
|
||||
private uint[] alSources;
|
||||
|
||||
|
||||
public enum SourcePoolIndex
|
||||
{
|
||||
Default = 0,
|
||||
Voice = 1
|
||||
}
|
||||
private SoundSourcePool[] sourcePools;
|
||||
|
||||
private List<Sound> loadedSounds;
|
||||
private SoundChannel[] playingChannels;
|
||||
private readonly SoundChannel[][] playingChannels = new SoundChannel[2][];
|
||||
|
||||
private Thread streamingThread;
|
||||
|
||||
@@ -75,6 +80,7 @@ namespace Barotrauma.Sounds
|
||||
get { return listenerGain; }
|
||||
set
|
||||
{
|
||||
if (Math.Abs(ListenerGain - value) < 0.001f) { return; }
|
||||
listenerGain = value;
|
||||
AL.Listener(ALListenerf.Gain, listenerGain);
|
||||
ALError alError = AL.GetError();
|
||||
@@ -99,7 +105,6 @@ namespace Barotrauma.Sounds
|
||||
public SoundManager()
|
||||
{
|
||||
loadedSounds = new List<Sound>();
|
||||
playingChannels = new SoundChannel[SOURCE_COUNT];
|
||||
|
||||
streamingThread = null;
|
||||
|
||||
@@ -147,50 +152,13 @@ namespace Barotrauma.Sounds
|
||||
|
||||
ALError alError = ALError.NoError;
|
||||
|
||||
alSources = new uint[SOURCE_COUNT];
|
||||
for (int i=0;i<SOURCE_COUNT;i++)
|
||||
{
|
||||
AL.GenSource(out alSources[i]);
|
||||
alError = AL.GetError();
|
||||
if (alError!=ALError.NoError)
|
||||
{
|
||||
throw new Exception("Error generating alSource["+i.ToString()+"]: " + AL.GetErrorString(alError));
|
||||
}
|
||||
sourcePools = new SoundSourcePool[2];
|
||||
sourcePools[(int)SourcePoolIndex.Default] = new SoundSourcePool(SOURCE_COUNT);
|
||||
playingChannels[(int)SourcePoolIndex.Default] = new SoundChannel[SOURCE_COUNT];
|
||||
|
||||
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));
|
||||
}
|
||||
sourcePools[(int)SourcePoolIndex.Voice] = new SoundSourcePool(8);
|
||||
playingChannels[(int)SourcePoolIndex.Voice] = new SoundChannel[8];
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
AL.DistanceModel(ALDistanceModel.LinearDistanceClamped);
|
||||
|
||||
alError = AL.GetError();
|
||||
@@ -198,21 +166,11 @@ namespace Barotrauma.Sounds
|
||||
{
|
||||
throw new Exception("Error setting distance model: " + AL.GetErrorString(alError));
|
||||
}
|
||||
|
||||
if (Alc.IsExtensionPresent(IntPtr.Zero, "ALC_EXT_CAPTURE"))
|
||||
{
|
||||
alcCaptureDeviceNames = new List<string>(Alc.GetString(IntPtr.Zero, AlcGetStringList.CaptureDeviceSpecifier));
|
||||
}
|
||||
else
|
||||
{
|
||||
alcCaptureDeviceNames = null;
|
||||
}
|
||||
|
||||
|
||||
listenerOrientation = new float[6];
|
||||
ListenerPosition = Vector3.Zero;
|
||||
ListenerTargetVector = new Vector3(0.0f, 0.0f, 1.0f);
|
||||
ListenerUpVector = new Vector3(0.0f, -1.0f, 0.0f);
|
||||
|
||||
}
|
||||
|
||||
public Sound LoadSound(string filename, bool stream = false)
|
||||
@@ -248,22 +206,22 @@ namespace Barotrauma.Sounds
|
||||
return newSound;
|
||||
}
|
||||
|
||||
public SoundChannel GetSoundChannelFromIndex(int ind)
|
||||
public SoundChannel GetSoundChannelFromIndex(SourcePoolIndex poolIndex, int ind)
|
||||
{
|
||||
if (ind < 0 || ind >= SOURCE_COUNT) return null;
|
||||
return playingChannels[ind];
|
||||
if (ind < 0 || ind >= playingChannels[(int)poolIndex].Length) return null;
|
||||
return playingChannels[(int)poolIndex][ind];
|
||||
}
|
||||
|
||||
public uint GetSourceFromIndex(int ind)
|
||||
public uint GetSourceFromIndex(SourcePoolIndex poolIndex, int srcInd)
|
||||
{
|
||||
if (ind < 0 || ind >= SOURCE_COUNT) return 0;
|
||||
if (srcInd < 0 || srcInd >= sourcePools[(int)poolIndex].ALSources.Length) return 0;
|
||||
|
||||
if (!AL.IsSource(alSources[ind]))
|
||||
if (!AL.IsSource(sourcePools[(int)poolIndex].ALSources[srcInd]))
|
||||
{
|
||||
throw new Exception("alSources[" + ind.ToString() + "] is invalid!");
|
||||
throw new Exception("alSources[" + srcInd.ToString() + "] is invalid!");
|
||||
}
|
||||
|
||||
return alSources[ind];
|
||||
return sourcePools[(int)poolIndex].ALSources[srcInd];
|
||||
}
|
||||
|
||||
public int AssignFreeSourceToChannel(SoundChannel newChannel)
|
||||
@@ -272,21 +230,17 @@ namespace Barotrauma.Sounds
|
||||
{
|
||||
//remove a channel that has stopped
|
||||
//or hasn't even been assigned
|
||||
for (int i = 0; i < SOURCE_COUNT; i++)
|
||||
int poolIndex = (int)newChannel.Sound.SourcePoolIndex;
|
||||
for (int i = 0; i < playingChannels[poolIndex].Length; i++)
|
||||
{
|
||||
if (playingChannels[i]==null || !playingChannels[i].IsPlaying)
|
||||
if (playingChannels[poolIndex][i] == null || !playingChannels[poolIndex][i].IsPlaying)
|
||||
{
|
||||
if (playingChannels[i]!=null) playingChannels[i].Dispose();
|
||||
playingChannels[i] = newChannel;
|
||||
|
||||
if (!AL.IsSource(alSources[i]))
|
||||
{
|
||||
throw new Exception("alSources[" + i.ToString() + "] is invalid!");
|
||||
}
|
||||
|
||||
if (playingChannels[poolIndex][i] != null) { playingChannels[poolIndex][i].Dispose(); }
|
||||
playingChannels[poolIndex][i] = newChannel;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
//we couldn't get a free source to assign to this channel!
|
||||
return -1;
|
||||
}
|
||||
@@ -295,10 +249,10 @@ namespace Barotrauma.Sounds
|
||||
#if DEBUG
|
||||
public void DebugSource(int ind)
|
||||
{
|
||||
for (int i=0;i<SOURCE_COUNT;i++)
|
||||
for (int i = 0; i < sourcePools[0].ALSources.Length; i++)
|
||||
{
|
||||
AL.Source(alSources[i], ALSourcef.MaxGain, i == ind ? 1.0f : 0.0f);
|
||||
AL.Source(alSources[i], ALSourcef.MinGain, 0.0f);
|
||||
AL.Source(sourcePools[0].ALSources[i], ALSourcef.MaxGain, i == ind ? 1.0f : 0.0f);
|
||||
AL.Source(sourcePools[0].ALSources[i], ALSourcef.MinGain, 0.0f);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -307,26 +261,45 @@ namespace Barotrauma.Sounds
|
||||
{
|
||||
lock (playingChannels)
|
||||
{
|
||||
for (int i = 0; i < SOURCE_COUNT; i++)
|
||||
for (int i = 0; i < playingChannels[(int)sound.SourcePoolIndex].Length; i++)
|
||||
{
|
||||
if (playingChannels[i] != null && playingChannels[i].Sound == sound)
|
||||
if (playingChannels[(int)sound.SourcePoolIndex][i] != null &&
|
||||
playingChannels[(int)sound.SourcePoolIndex][i].Sound == sound)
|
||||
{
|
||||
if (playingChannels[i].IsPlaying) return true;
|
||||
if (playingChannels[(int)sound.SourcePoolIndex][i].IsPlaying) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int CountPlayingInstances(Sound sound)
|
||||
{
|
||||
int count = 0;
|
||||
lock (playingChannels)
|
||||
{
|
||||
for (int i = 0; i < playingChannels[(int)sound.SourcePoolIndex].Length; i++)
|
||||
{
|
||||
if (playingChannels[(int)sound.SourcePoolIndex][i] != null &&
|
||||
playingChannels[(int)sound.SourcePoolIndex][i].Sound.Filename == sound.Filename)
|
||||
{
|
||||
if (playingChannels[(int)sound.SourcePoolIndex][i].IsPlaying) { count++; };
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public SoundChannel GetChannelFromSound(Sound sound)
|
||||
{
|
||||
lock (playingChannels)
|
||||
{
|
||||
for (int i = 0; i < SOURCE_COUNT; i++)
|
||||
for (int i = 0; i < playingChannels[(int)sound.SourcePoolIndex].Length; i++)
|
||||
{
|
||||
if (playingChannels[i] != null && playingChannels[i].Sound == sound)
|
||||
if (playingChannels[(int)sound.SourcePoolIndex][i] != null &&
|
||||
playingChannels[(int)sound.SourcePoolIndex][i].Sound == sound)
|
||||
{
|
||||
if (playingChannels[i].IsPlaying) return playingChannels[i];
|
||||
if (playingChannels[(int)sound.SourcePoolIndex][i].IsPlaying) return playingChannels[(int)sound.SourcePoolIndex][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -337,12 +310,12 @@ namespace Barotrauma.Sounds
|
||||
{
|
||||
lock (playingChannels)
|
||||
{
|
||||
for (int i = 0; i < SOURCE_COUNT; i++)
|
||||
for (int i = 0; i < playingChannels[(int)sound.SourcePoolIndex].Length; i++)
|
||||
{
|
||||
if (playingChannels[i]!=null && playingChannels[i].Sound == sound)
|
||||
if (playingChannels[(int)sound.SourcePoolIndex][i]!=null && playingChannels[(int)sound.SourcePoolIndex][i].Sound == sound)
|
||||
{
|
||||
playingChannels[i].Dispose();
|
||||
playingChannels[i] = null;
|
||||
playingChannels[(int)sound.SourcePoolIndex][i]?.Dispose();
|
||||
playingChannels[(int)sound.SourcePoolIndex][i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -350,9 +323,9 @@ namespace Barotrauma.Sounds
|
||||
|
||||
public void RemoveSound(Sound sound)
|
||||
{
|
||||
for (int i=0;i<loadedSounds.Count;i++)
|
||||
for (int i = 0; i < loadedSounds.Count; i++)
|
||||
{
|
||||
if (loadedSounds[i]==sound)
|
||||
if (loadedSounds[i] == sound)
|
||||
{
|
||||
loadedSounds.RemoveAt(i);
|
||||
return;
|
||||
@@ -372,11 +345,17 @@ namespace Barotrauma.Sounds
|
||||
{
|
||||
categoryModifiers[category].First = gain;
|
||||
}
|
||||
for (int i = 0; i < SOURCE_COUNT; i++)
|
||||
lock (playingChannels)
|
||||
{
|
||||
if (playingChannels[i] != null && playingChannels[i].IsPlaying)
|
||||
for (int i = 0; i < playingChannels.Length; i++)
|
||||
{
|
||||
playingChannels[i].Gain = playingChannels[i].Gain; //force all channels to recalculate their gain
|
||||
for (int j = 0; j < playingChannels[i].Length; j++)
|
||||
{
|
||||
if (playingChannels[i][j] != null && playingChannels[i][j].IsPlaying)
|
||||
{
|
||||
playingChannels[i][j].Gain = playingChannels[i][j].Gain; //force all channels to recalculate their gain
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -402,11 +381,14 @@ namespace Barotrauma.Sounds
|
||||
categoryModifiers[category].Second = muffle;
|
||||
}
|
||||
|
||||
for (int i = 0; i < SOURCE_COUNT; i++)
|
||||
for (int i = 0; i < playingChannels.Length; i++)
|
||||
{
|
||||
if (playingChannels[i] != null && playingChannels[i].IsPlaying)
|
||||
for (int j = 0; j < playingChannels[i].Length; j++)
|
||||
{
|
||||
if (playingChannels[i].Category.ToLower() == category) playingChannels[i].Muffled = muffle;
|
||||
if (playingChannels[i][j] != null && playingChannels[i][j].IsPlaying)
|
||||
{
|
||||
if (playingChannels[i][j].Category.ToLower() == category) playingChannels[i][j].Muffled = muffle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -424,6 +406,7 @@ namespace Barotrauma.Sounds
|
||||
{
|
||||
streamingThread = new Thread(UpdateStreaming)
|
||||
{
|
||||
Name = "SoundManager Streaming Thread",
|
||||
IsBackground = true //this should kill the thread if the game crashes
|
||||
};
|
||||
streamingThread.Start();
|
||||
@@ -438,36 +421,42 @@ namespace Barotrauma.Sounds
|
||||
areStreamsPlaying = false;
|
||||
lock (playingChannels)
|
||||
{
|
||||
for (int i=0;i<SOURCE_COUNT;i++)
|
||||
for (int i = 0; i < playingChannels.Length; i++)
|
||||
{
|
||||
if (playingChannels[i]!=null && playingChannels[i].IsStream)
|
||||
for (int j = 0; j < playingChannels[i].Length; j++)
|
||||
{
|
||||
if (playingChannels[i].IsPlaying)
|
||||
if (playingChannels[i][j] != null && playingChannels[i][j].IsStream)
|
||||
{
|
||||
areStreamsPlaying = true;
|
||||
playingChannels[i].UpdateStream();
|
||||
}
|
||||
else
|
||||
{
|
||||
playingChannels[i].Dispose();
|
||||
if (playingChannels[i][j].IsPlaying)
|
||||
{
|
||||
areStreamsPlaying = true;
|
||||
playingChannels[i][j].UpdateStream();
|
||||
}
|
||||
else
|
||||
{
|
||||
playingChannels[i][j].Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Thread.Sleep(300);
|
||||
Thread.Sleep(50); //TODO: use a separate thread for network audio?
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (playingChannels)
|
||||
{
|
||||
for (int i=0;i<SOURCE_COUNT;i++)
|
||||
for (int i = 0; i < playingChannels.Length; i++)
|
||||
{
|
||||
if (playingChannels[i]!=null) playingChannels[i].Dispose();
|
||||
for (int j = 0; j < playingChannels[i].Length; j++)
|
||||
{
|
||||
if (playingChannels[i][j] != null) playingChannels[i][j].Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (streamingThread != null && streamingThread.ThreadState == ThreadState.Running)
|
||||
if (streamingThread != null && !streamingThread.ThreadState.HasFlag(ThreadState.Stopped))
|
||||
{
|
||||
streamingThread.Join();
|
||||
}
|
||||
@@ -475,16 +464,9 @@ namespace Barotrauma.Sounds
|
||||
{
|
||||
loadedSounds[i].Dispose();
|
||||
}
|
||||
for (int i = 0; i < SOURCE_COUNT; 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));
|
||||
}
|
||||
}
|
||||
|
||||
sourcePools[(int)SourcePoolIndex.Default].Dispose();
|
||||
sourcePools[(int)SourcePoolIndex.Voice].Dispose();
|
||||
|
||||
if (!Alc.MakeContextCurrent(OpenTK.ContextHandle.Zero))
|
||||
{
|
||||
throw new Exception("Failed to detach the current ALC context! (error code: " + Alc.GetError(alcDevice).ToString() + ")");
|
||||
|
||||
@@ -87,6 +87,7 @@ namespace Barotrauma
|
||||
const float FireSoundRange = 1000.0f;
|
||||
const float FireSoundLargeLimit = 200.0f; //switch to large fire sound when the size of a firesource is above this
|
||||
|
||||
// TODO: could use a dictionary to split up the list into smaller lists of same type?
|
||||
private static List<DamageSound> damageSounds;
|
||||
|
||||
private static Sound startUpSound;
|
||||
@@ -162,7 +163,7 @@ namespace Barotrauma
|
||||
|
||||
damageSounds.Add(new DamageSound(
|
||||
damageSound,
|
||||
soundElement.GetAttributeVector2("damagerange", new Vector2(0.0f, 100.0f)),
|
||||
soundElement.GetAttributeVector2("damagerange", Vector2.Zero),
|
||||
damageSoundType,
|
||||
soundElement.GetAttributeString("requiredtag", "")));
|
||||
|
||||
@@ -513,7 +514,9 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
//get the appropriate intensity layers for current situation
|
||||
IEnumerable<BackgroundMusic> suitableIntensityMusic = GetSuitableMusicClips("intensity", currentIntensity);
|
||||
IEnumerable<BackgroundMusic> suitableIntensityMusic = Screen.Selected == GameMain.GameScreen ?
|
||||
GetSuitableMusicClips("intensity", currentIntensity) :
|
||||
Enumerable.Empty<BackgroundMusic>();
|
||||
|
||||
for (int i = 1; i < MaxMusicChannels; i++)
|
||||
{
|
||||
@@ -604,7 +607,12 @@ namespace Barotrauma
|
||||
private static string GetCurrentMusicType()
|
||||
{
|
||||
if (OverrideMusicType != null) return OverrideMusicType;
|
||||
|
||||
|
||||
if (Screen.Selected != GameMain.GameScreen)
|
||||
{
|
||||
return "menu";
|
||||
}
|
||||
|
||||
if (Character.Controlled != null &&
|
||||
Level.Loaded != null && Level.Loaded.Ruins != null &&
|
||||
Level.Loaded.Ruins.Any(r => r.Area.Contains(Character.Controlled.WorldPosition)))
|
||||
@@ -667,7 +675,7 @@ namespace Barotrauma
|
||||
{
|
||||
return "start";
|
||||
}
|
||||
|
||||
|
||||
return "default";
|
||||
}
|
||||
|
||||
@@ -684,13 +692,17 @@ namespace Barotrauma
|
||||
lowpassHFGain *= Character.Controlled.LowPassMultiplier;
|
||||
if (lowpassHFGain < 0.5f) return true;
|
||||
|
||||
|
||||
Hull targetHull = Hull.FindHull(soundWorldPos, hullGuess, true);
|
||||
if (listener.CurrentHull == null || targetHull == null)
|
||||
{
|
||||
return listener.CurrentHull != targetHull;
|
||||
}
|
||||
return listener.CurrentHull.GetApproximateDistance(targetHull, range) > range;
|
||||
Vector2 soundPos = soundWorldPos;
|
||||
if (targetHull.Submarine != null)
|
||||
{
|
||||
soundPos += -targetHull.Submarine.WorldPosition + targetHull.Submarine.HiddenSubPosition;
|
||||
}
|
||||
return listener.CurrentHull.GetApproximateDistance(listener.Position, soundPos, targetHull, range) > range;
|
||||
}
|
||||
|
||||
public static void PlaySplashSound(Vector2 worldPosition, float strength)
|
||||
@@ -711,9 +723,8 @@ namespace Barotrauma
|
||||
{
|
||||
damage = MathHelper.Clamp(damage + Rand.Range(-10.0f, 10.0f), 0.0f, 100.0f);
|
||||
var sounds = damageSounds.FindAll(s =>
|
||||
s.damageRange == null ||
|
||||
(damage >= s.damageRange.X &&
|
||||
damage <= s.damageRange.Y) &&
|
||||
(s.damageRange == Vector2.Zero ||
|
||||
(damage >= s.damageRange.X && damage <= s.damageRange.Y)) &&
|
||||
s.damageType == damageType &&
|
||||
(tags == null ? string.IsNullOrEmpty(s.requiredTag) : tags.Contains(s.requiredTag)));
|
||||
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using OpenTK.Audio.OpenAL;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using Barotrauma.Media;
|
||||
|
||||
namespace Barotrauma.Sounds
|
||||
{
|
||||
public class VideoSound : Sound
|
||||
{
|
||||
private readonly object mutex;
|
||||
private Queue<short[]> sampleQueue;
|
||||
|
||||
private SoundChannel soundChannel;
|
||||
private Video video;
|
||||
|
||||
public VideoSound(SoundManager owner, string filename, int sampleRate, Video vid) : base(owner, filename, true, false)
|
||||
{
|
||||
ALFormat = ALFormat.Stereo16;
|
||||
SampleRate = sampleRate;
|
||||
|
||||
sampleQueue = new Queue<short[]>();
|
||||
mutex = new object();
|
||||
|
||||
soundChannel = null;
|
||||
|
||||
video = vid;
|
||||
}
|
||||
|
||||
public override bool IsPlaying()
|
||||
{
|
||||
bool retVal = false;
|
||||
lock (mutex)
|
||||
{
|
||||
retVal = soundChannel != null && soundChannel.IsPlaying;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public void Enqueue(short[] buf)
|
||||
{
|
||||
lock (mutex)
|
||||
{
|
||||
sampleQueue.Enqueue(buf);
|
||||
}
|
||||
}
|
||||
|
||||
public override SoundChannel Play(float gain, float range, Vector2 position, bool muffle = false)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
public override SoundChannel Play(Vector3? position, float gain, bool muffle = false)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
public override SoundChannel Play(float gain)
|
||||
{
|
||||
SoundChannel chn = null;
|
||||
lock (mutex)
|
||||
{
|
||||
if (soundChannel != null) soundChannel.Dispose();
|
||||
chn = new SoundChannel(this, gain, null, 1.0f, 3.0f, "video", false);
|
||||
soundChannel = chn;
|
||||
}
|
||||
return chn;
|
||||
}
|
||||
|
||||
public override SoundChannel Play()
|
||||
{
|
||||
return Play(1.0f);
|
||||
}
|
||||
|
||||
public override int FillStreamBuffer(int samplePos, short[] buffer)
|
||||
{
|
||||
if (!video.IsPlaying) return -1;
|
||||
|
||||
short[] buf;
|
||||
int readAmount = 0;
|
||||
lock (mutex)
|
||||
{
|
||||
while (readAmount<buffer.Length)
|
||||
{
|
||||
if (sampleQueue.Count == 0) break;
|
||||
buf = sampleQueue.Peek();
|
||||
if (readAmount + buf.Length >= buffer.Length) break;
|
||||
buf = sampleQueue.Dequeue();
|
||||
buf.CopyTo(buffer, readAmount);
|
||||
readAmount += buf.Length;
|
||||
}
|
||||
}
|
||||
return readAmount*2;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
lock (mutex)
|
||||
{
|
||||
if (soundChannel != null)
|
||||
{
|
||||
soundChannel.Dispose();
|
||||
}
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using OpenTK.Audio.OpenAL;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Barotrauma.Sounds
|
||||
{
|
||||
public class VoipSound : Sound
|
||||
{
|
||||
public override SoundManager.SourcePoolIndex SourcePoolIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
return SoundManager.SourcePoolIndex.Voice;
|
||||
}
|
||||
}
|
||||
|
||||
public new bool IsPlaying
|
||||
{
|
||||
get
|
||||
{
|
||||
return soundChannel != null && soundChannel.IsPlaying;
|
||||
}
|
||||
}
|
||||
|
||||
private VoipQueue queue;
|
||||
public int bufferID = 0;
|
||||
|
||||
private SoundChannel soundChannel;
|
||||
|
||||
public bool UseRadioFilter;
|
||||
public bool UseMuffleFilter;
|
||||
|
||||
private static BiQuad[] muffleFilters = new BiQuad[]
|
||||
{
|
||||
new LowpassFilter(VoipConfig.FREQUENCY, 400)
|
||||
};
|
||||
private static BiQuad[] radioFilters = new BiQuad[]
|
||||
{
|
||||
new BandpassFilter(VoipConfig.FREQUENCY, 2000)
|
||||
};
|
||||
|
||||
public VoipSound(SoundManager owner, VoipQueue q) : base(owner, "voip", true, true)
|
||||
{
|
||||
VoipConfig.SetupEncoding();
|
||||
|
||||
ALFormat = ALFormat.Mono16;
|
||||
SampleRate = VoipConfig.FREQUENCY;
|
||||
|
||||
queue = q;
|
||||
bufferID = queue.LatestBufferID;
|
||||
|
||||
soundChannel = null;
|
||||
|
||||
SoundChannel chn = new SoundChannel(this, 1.0f, null, 0.4f, 1.0f, "voip", false);
|
||||
soundChannel = chn;
|
||||
}
|
||||
|
||||
public void SetPosition(Vector3? pos)
|
||||
{
|
||||
soundChannel.Position = pos;
|
||||
}
|
||||
|
||||
public void SetRange(float near, float far)
|
||||
{
|
||||
soundChannel.Near = near;
|
||||
soundChannel.Far = far;
|
||||
}
|
||||
|
||||
public void ApplyFilters(short[] buffer, int readSamples)
|
||||
{
|
||||
if (UseMuffleFilter)
|
||||
{
|
||||
ApplyFilters(radioFilters, buffer, readSamples);
|
||||
}
|
||||
|
||||
if (UseRadioFilter)
|
||||
{
|
||||
ApplyFilters(radioFilters, buffer, readSamples);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyFilters(IEnumerable<BiQuad> filters, short[] buffer, int readSamples)
|
||||
{
|
||||
for (int i = 0; i < readSamples; i++)
|
||||
{
|
||||
float fVal = ShortToFloat(buffer[i]);
|
||||
foreach (var filter in filters)
|
||||
{
|
||||
fVal = filter.Process(fVal);
|
||||
}
|
||||
buffer[i] = FloatToShort(fVal);
|
||||
}
|
||||
}
|
||||
|
||||
public override SoundChannel Play(float gain, float range, Vector2 position, bool muffle = false)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
public override SoundChannel Play(Vector3? position, float gain, bool muffle = false)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
public override SoundChannel Play(float gain)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
public override SoundChannel Play()
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
public override int FillStreamBuffer(int samplePos, short[] buffer)
|
||||
{
|
||||
queue.RetrieveBuffer(bufferID, out int compressedSize, out byte[] compressedBuffer);
|
||||
if (compressedSize > 0)
|
||||
{
|
||||
VoipConfig.Decoder.Decode(compressedBuffer, 0, compressedSize, buffer, 0, VoipConfig.BUFFER_SIZE);
|
||||
bufferID++;
|
||||
return VoipConfig.BUFFER_SIZE * 2;
|
||||
}
|
||||
if (bufferID < queue.LatestBufferID - (VoipQueue.BUFFER_COUNT - 1)) bufferID = queue.LatestBufferID - (VoipQueue.BUFFER_COUNT - 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (soundChannel != null)
|
||||
{
|
||||
soundChannel.Dispose();
|
||||
soundChannel = null;
|
||||
}
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user