(965c31410a) Unstable v0.10.4.0

This commit is contained in:
Juan Pablo Arce
2020-07-21 08:57:50 -03:00
parent 4f8bd39789
commit 33d3a41104
546 changed files with 45952 additions and 25762 deletions
@@ -16,67 +16,7 @@ namespace Barotrauma.Sounds
private static List<float> playbackAmplitude;
private const int AMPLITUDE_SAMPLE_COUNT = 4410; //100ms in a 44100hz file
public OggSound(SoundManager owner, string filename, bool stream, XElement xElement) : base(owner, filename, stream, true, xElement)
{
filename = filename.CleanUpPath();
if (!ToolBox.IsProperFilenameCase(filename))
{
DebugConsole.ThrowError("Sound file \"" + filename + "\" has incorrect case!");
}
reader = new VorbisReader(filename);
ALFormat = reader.Channels == 1 ? Al.FormatMono16 : Al.FormatStereo16;
SampleRate = reader.SampleRate;
if (!stream)
{
int bufferSize = (int)reader.TotalSamples * reader.Channels;
float[] floatBuffer = new float[bufferSize];
short[] shortBuffer = new short[bufferSize];
int readSamples = reader.ReadSamples(floatBuffer, 0, bufferSize);
playbackAmplitude = new List<float>();
for (int i=0;i<bufferSize;i+=reader.Channels*AMPLITUDE_SAMPLE_COUNT)
{
float maxAmplitude = 0.0f;
for (int j=i;j<i+reader.Channels*AMPLITUDE_SAMPLE_COUNT;j++)
{
if (j >= bufferSize) { break; }
maxAmplitude = Math.Max(maxAmplitude, Math.Abs(floatBuffer[j]));
}
playbackAmplitude.Add(maxAmplitude);
}
CastBuffer(floatBuffer, shortBuffer, readSamples);
Al.BufferData(ALBuffer, ALFormat, shortBuffer,
readSamples * sizeof(short), SampleRate);
int alError = Al.GetError();
if (alError != Al.NoError)
{
throw new Exception("Failed to set buffer data for non-streamed audio! "+Al.GetErrorString(alError));
}
MuffleBuffer(floatBuffer, SampleRate, reader.Channels);
CastBuffer(floatBuffer, shortBuffer, readSamples);
Al.BufferData(ALMuffledBuffer, ALFormat, shortBuffer,
readSamples * sizeof(short), SampleRate);
alError = Al.GetError();
if (alError != Al.NoError)
{
throw new Exception("Failed to set buffer data for non-streamed audio! " + Al.GetErrorString(alError));
}
reader.Dispose();
}
}
public OggSound(SoundManager owner, string filename, bool stream, XElement xElement) : base(owner, filename, stream, true, xElement) { }
public override float GetAmplitudeAtPlaybackPos(int playbackPos)
{
@@ -114,6 +54,64 @@ namespace Barotrauma.Sounds
filter.Process(buffer);
}
public override void InitializeALBuffers()
{
base.InitializeALBuffers();
reader ??= new VorbisReader(Filename);
ALFormat = reader.Channels == 1 ? Al.FormatMono16 : Al.FormatStereo16;
SampleRate = reader.SampleRate;
if (!Stream)
{
int bufferSize = (int)reader.TotalSamples * reader.Channels;
float[] floatBuffer = new float[bufferSize];
short[] shortBuffer = new short[bufferSize];
int readSamples = reader.ReadSamples(floatBuffer, 0, bufferSize);
playbackAmplitude = new List<float>();
for (int i = 0; i < bufferSize; i += reader.Channels * AMPLITUDE_SAMPLE_COUNT)
{
float maxAmplitude = 0.0f;
for (int j = i; j < i + reader.Channels * AMPLITUDE_SAMPLE_COUNT; j++)
{
if (j >= bufferSize) { break; }
maxAmplitude = Math.Max(maxAmplitude, Math.Abs(floatBuffer[j]));
}
playbackAmplitude.Add(maxAmplitude);
}
CastBuffer(floatBuffer, shortBuffer, readSamples);
Al.BufferData(ALBuffer, ALFormat, shortBuffer,
readSamples * sizeof(short), SampleRate);
int alError = Al.GetError();
if (alError != Al.NoError)
{
throw new Exception("Failed to set buffer data for non-streamed audio! " + Al.GetErrorString(alError));
}
MuffleBuffer(floatBuffer, SampleRate, reader.Channels);
CastBuffer(floatBuffer, shortBuffer, readSamples);
Al.BufferData(ALMuffledBuffer, ALFormat, shortBuffer,
readSamples * sizeof(short), SampleRate);
alError = Al.GetError();
if (alError != Al.NoError)
{
throw new Exception("Failed to set buffer data for non-streamed audio! " + Al.GetErrorString(alError));
}
reader.Dispose(); reader = null;
}
}
public override void Dispose()
{
if (Stream)
@@ -43,11 +43,42 @@ namespace OpenAL
#elif LINUX
public const string OpenAlDll = "libopenal.so.1";
#elif WINDOWS
#if X86
public const string OpenAlDll = "soft_oal_x86.dll";
#elif X64
public const string OpenAlDll = "soft_oal_x64.dll";
#endif
public delegate void ErrorReasonCallback(string str);
#if WINDOWS
[DllImport(OpenAlDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alcSetErrorReasonCallback")]
private static extern void SetErrorReasonCallback(IntPtr callback);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void ErrorReasonCallbackInternal(IntPtr cstr);
private static ErrorReasonCallbackInternal CurrentErrorReasonCallback;
private static IntPtr CurrentErrorReasonCallbackPtr;
public static void SetErrorReasonCallback(ErrorReasonCallback callback)
{
CurrentErrorReasonCallback = (IntPtr cstr) =>
{
int strLen = 0;
while (Marshal.ReadByte(cstr, strLen) != '\0') { strLen++; }
byte[] bytes = new byte[strLen];
Marshal.Copy(cstr, bytes, 0, strLen);
string csStr = Encoding.UTF8.GetString(bytes);
callback?.Invoke(csStr);
};
CurrentErrorReasonCallbackPtr = Marshal.GetFunctionPointerForDelegate(CurrentErrorReasonCallback);
SetErrorReasonCallback(CurrentErrorReasonCallbackPtr);
}
#else
public static void SetErrorReasonCallback(ErrorReasonCallback callback)
{
//FIXME: not implemented on macOS and Linux
}
#endif
#region Enum
@@ -77,10 +108,11 @@ namespace OpenAL
public const int CaptureDeviceSpecifier = 0x310;
public const int CaptureDefaultDeviceSpecifier = 0x311;
public const int EnumCaptureSamples = 0x312;
public const int EnumConnected = 0x313;
#endregion
#endregion
#region Context Management Functions
#region Context Management Functions
[DllImport(OpenAlDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alcCreateContext")]
private static extern IntPtr _CreateContext(IntPtr device, IntPtr attrlist);
@@ -112,7 +144,22 @@ namespace OpenAL
public static extern IntPtr GetContextsDevice(IntPtr context);
[DllImport(OpenAlDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alcOpenDevice")]
public static extern IntPtr OpenDevice(string deviceName);
private static extern IntPtr OpenDevice(IntPtr deviceName);
public static IntPtr OpenDevice(string deviceName)
{
if (deviceName == null)
{
return OpenDevice(IntPtr.Zero);
}
byte[] devicenameBytes = Encoding.UTF8.GetBytes(deviceName + "\0");
GCHandle devicenameHandle = GCHandle.Alloc(devicenameBytes, GCHandleType.Pinned);
IntPtr retVal = OpenDevice(devicenameHandle.AddrOfPinnedObject());
devicenameHandle.Free();
return retVal;
}
[DllImport(OpenAlDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alcCloseDevice")]
public static extern bool CloseDevice(IntPtr device);
@@ -150,9 +197,9 @@ namespace OpenAL
[DllImport(OpenAlDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alcGetEnumValue")]
public static extern int GetEnumValue(IntPtr device, string enumname);
#endregion
#endregion
#region Query Functions
#region Query Functions
[DllImport(OpenAlDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alcGetString")]
private static extern IntPtr _GetString(IntPtr device, int param);
@@ -171,6 +218,7 @@ namespace OpenAL
{
List<string> retVal = new List<string>();
IntPtr strPtr = _GetString(device, param);
if (strPtr == IntPtr.Zero) { return retVal; }
int strStart = 0;
int strEnd = 0;
byte currChar = Marshal.ReadByte(strPtr, strEnd);
@@ -208,9 +256,9 @@ namespace OpenAL
data = dataArr[0];
}
#endregion
#endregion
#region Capture Functions
#region Capture Functions
[DllImport(OpenAlDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alcCaptureOpenDevice")]
private static extern IntPtr CaptureOpenDevice(IntPtr devicename, uint frequency, int format, int buffersize);
@@ -236,6 +284,6 @@ namespace OpenAL
[DllImport(OpenAlDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alcCaptureSamples")]
public static extern void CaptureSamples(IntPtr device, IntPtr buffer, int samples);
#endregion
#endregion
}
}
@@ -98,37 +98,8 @@ namespace Barotrauma.Sounds
BaseGain = 1.0f;
BaseNear = 100.0f;
BaseFar = 200.0f;
if (!stream)
{
Al.GenBuffer(out alBuffer);
int alError = Al.GetError();
if (alError != Al.NoError)
{
throw new Exception("Failed to create OpenAL buffer for non-streamed sound: " + Al.GetErrorString(alError));
}
if (!Al.IsBuffer(alBuffer))
{
throw new Exception("Generated OpenAL buffer is invalid!");
}
Al.GenBuffer(out alMuffledBuffer);
alError = Al.GetError();
if (alError != Al.NoError)
{
throw new Exception("Failed to create OpenAL buffer for non-streamed sound: " + Al.GetErrorString(alError));
}
if (!Al.IsBuffer(alMuffledBuffer))
{
throw new Exception("Generated OpenAL buffer is invalid!");
}
}
else
{
alBuffer = 0;
}
InitializeALBuffers();
}
public override string ToString()
@@ -191,10 +162,42 @@ namespace Barotrauma.Sounds
public abstract float GetAmplitudeAtPlaybackPos(int playbackPos);
public virtual void Dispose()
public virtual void InitializeALBuffers()
{
if (disposed) { return; }
if (!Stream)
{
Al.GenBuffer(out alBuffer);
int alError = Al.GetError();
if (alError != Al.NoError)
{
throw new Exception("Failed to create OpenAL buffer for non-streamed sound: " + Al.GetErrorString(alError));
}
if (!Al.IsBuffer(alBuffer))
{
throw new Exception("Generated OpenAL buffer is invalid!");
}
Al.GenBuffer(out alMuffledBuffer);
alError = Al.GetError();
if (alError != Al.NoError)
{
throw new Exception("Failed to create OpenAL buffer for non-streamed sound: " + Al.GetErrorString(alError));
}
if (!Al.IsBuffer(alMuffledBuffer))
{
throw new Exception("Generated OpenAL buffer is invalid!");
}
}
else
{
alBuffer = 0;
}
}
public virtual void DeleteALBuffers()
{
Owner.KillChannels(this);
if (alBuffer != 0)
{
@@ -202,7 +205,7 @@ namespace Barotrauma.Sounds
{
throw new Exception("Buffer to delete is invalid!");
}
Al.DeleteBuffer(alBuffer); alBuffer = 0;
int alError = Al.GetError();
@@ -226,6 +229,13 @@ namespace Barotrauma.Sounds
throw new Exception("Failed to delete OpenAL buffer for non-streamed sound: " + Al.GetErrorString(alError));
}
}
}
public virtual void Dispose()
{
if (disposed) { return; }
DeleteALBuffers();
Owner.RemoveSound(this);
disposed = true;
@@ -65,6 +65,7 @@ namespace Barotrauma.Sounds
public void Dispose()
{
if (ALSources == null) { return; }
for (int i = 0; i < ALSources.Length; i++)
{
Al.DeleteSource(ALSources[i]);
@@ -19,8 +19,8 @@ namespace Barotrauma.Sounds
private set;
}
private readonly IntPtr alcDevice;
private readonly IntPtr alcContext;
private IntPtr alcDevice;
private IntPtr alcContext;
public enum SourcePoolIndex
{
@@ -33,6 +33,10 @@ namespace Barotrauma.Sounds
private readonly SoundChannel[][] playingChannels = new SoundChannel[2][];
private readonly object threadDeathMutex = new object();
public bool CanDetectDisconnect { get; private set; }
public bool Disconnected { get; private set; }
private Thread streamingThread;
private Vector3 listenerPosition;
@@ -197,18 +201,55 @@ namespace Barotrauma.Sounds
streamingThread = null;
categoryModifiers = null;
int alcError = Alc.NoError;
sourcePools = new SoundSourcePool[2];
playingChannels[(int)SourcePoolIndex.Default] = new SoundChannel[SOURCE_COUNT];
playingChannels[(int)SourcePoolIndex.Voice] = new SoundChannel[16];
string deviceName = GameMain.Config.AudioOutputDevice;
if (string.IsNullOrEmpty(deviceName))
{
deviceName = Alc.GetString((IntPtr)null, Alc.DefaultDeviceSpecifier);
}
#if (!OSX)
var audioDeviceNames = Alc.GetStringList((IntPtr)null, Alc.AllDevicesSpecifier);
if (audioDeviceNames.Any() && !audioDeviceNames.Any(n => n.Equals(deviceName, StringComparison.OrdinalIgnoreCase)))
{
deviceName = audioDeviceNames[0];
}
#endif
GameMain.Config.AudioOutputDevice = deviceName;
InitializeAlcDevice(deviceName);
ListenerPosition = Vector3.Zero;
ListenerTargetVector = new Vector3(0.0f, 0.0f, 1.0f);
ListenerUpVector = new Vector3(0.0f, -1.0f, 0.0f);
CompressionDynamicRangeGain = 1.0f;
}
public bool InitializeAlcDevice(string deviceName)
{
ReleaseResources(true);
string deviceName = Alc.GetString(IntPtr.Zero, Alc.DefaultDeviceSpecifier);
DebugConsole.NewMessage($"Attempting to open ALC device \"{deviceName}\"");
alcDevice = IntPtr.Zero;
int alcError = Al.NoError;
for (int i = 0; i < 3; i++)
{
alcDevice = Alc.OpenDevice(deviceName);
if (alcDevice == IntPtr.Zero)
{
DebugConsole.NewMessage($"ALC device initialization attempt #{i + 1} failed: device is null");
alcError = Alc.GetError(IntPtr.Zero);
DebugConsole.NewMessage($"ALC device initialization attempt #{i + 1} failed: device is null (error code {Alc.GetErrorString(alcError)})");
if (!string.IsNullOrEmpty(deviceName))
{
deviceName = null;
DebugConsole.NewMessage($"Switching to default device...");
}
}
else
{
@@ -223,29 +264,44 @@ namespace Barotrauma.Sounds
}
alcDevice = IntPtr.Zero;
}
else
{
break;
}
}
}
if (alcDevice == IntPtr.Zero)
{
DebugConsole.ThrowError("ALC device creation failed too many times!");
Disabled = true;
return;
return false;
}
CanDetectDisconnect = Alc.IsExtensionPresent(alcDevice, "ALC_EXT_disconnect");
alcError = Alc.GetError(alcDevice);
if (alcError != Alc.NoError)
{
DebugConsole.ThrowError("Error determining if disconnect can be detected: " + alcError.ToString() + ". Disabling audio playback...");
Disabled = true;
return false;
}
Disconnected = false;
int[] alcContextAttrs = new int[] { };
alcContext = Alc.CreateContext(alcDevice, alcContextAttrs);
if (alcContext == null)
{
DebugConsole.ThrowError("Failed to create an ALC context! (error code: " + Alc.GetError(alcDevice).ToString() + "). Disabling audio playback...");
Disabled = true;
return;
return false;
}
if (!Alc.MakeContextCurrent(alcContext))
{
DebugConsole.ThrowError("Failed to assign the current ALC context! (error code: " + Alc.GetError(alcDevice).ToString() + "). Disabling audio playback...");
Disabled = true;
return;
return false;
}
alcError = Alc.GetError(alcDevice);
@@ -253,31 +309,27 @@ namespace Barotrauma.Sounds
{
DebugConsole.ThrowError("Error after assigning ALC context: " + Alc.GetErrorString(alcError) + ". Disabling audio playback...");
Disabled = true;
return;
return false;
}
sourcePools = new SoundSourcePool[2];
sourcePools[(int)SourcePoolIndex.Default] = new SoundSourcePool(SOURCE_COUNT);
playingChannels[(int)SourcePoolIndex.Default] = new SoundChannel[SOURCE_COUNT];
sourcePools[(int)SourcePoolIndex.Voice] = new SoundSourcePool(16);
playingChannels[(int)SourcePoolIndex.Voice] = new SoundChannel[16];
Al.DistanceModel(Al.LinearDistanceClamped);
int alError = Al.GetError();
if (alError != Al.NoError)
{
DebugConsole.ThrowError("Error setting distance model: " + Al.GetErrorString(alError) + ". Disabling audio playback...");
Disabled = true;
return;
return false;
}
ListenerPosition = Vector3.Zero;
ListenerTargetVector = new Vector3(0.0f, 0.0f, 1.0f);
ListenerUpVector = new Vector3(0.0f, -1.0f, 0.0f);
sourcePools[(int)SourcePoolIndex.Default] = new SoundSourcePool(SOURCE_COUNT);
sourcePools[(int)SourcePoolIndex.Voice] = new SoundSourcePool(16);
CompressionDynamicRangeGain = 1.0f;
ReloadSounds();
Disabled = false;
return true;
}
public Sound LoadSound(string filename, bool stream = false)
@@ -551,7 +603,25 @@ namespace Barotrauma.Sounds
public void Update()
{
if (Disabled) { return; }
if (Disconnected || Disabled) { return; }
if (CanDetectDisconnect)
{
Alc.GetInteger(alcDevice, Alc.EnumConnected, out int isConnected);
int alcError = Alc.GetError(alcDevice);
if (alcError != Alc.NoError)
{
throw new Exception("Failed to determine if device is connected: " + alcError.ToString());
}
if (isConnected == 0)
{
DebugConsole.ThrowError("Playback device has been disconnected. You can select another available device in the settings.");
GameMain.Config.AudioOutputDevice = "<disconnected>";
Disconnected = true;
return;
}
}
if (GameMain.Client != null && GameMain.Config.VoipAttenuationEnabled)
{
@@ -681,10 +751,16 @@ namespace Barotrauma.Sounds
}
}
public void Dispose()
private void ReloadSounds()
{
if (Disabled) { return; }
for (int i = loadedSounds.Count - 1; i >= 0; i--)
{
loadedSounds[i].InitializeALBuffers();
}
}
private void ReleaseResources(bool keepSounds)
{
for (int i = 0; i < playingChannels.Length; i++)
{
lock (playingChannels[i])
@@ -699,10 +775,24 @@ namespace Barotrauma.Sounds
streamingThread?.Join();
for (int i = loadedSounds.Count - 1; i >= 0; i--)
{
loadedSounds[i].Dispose();
if (keepSounds)
{
loadedSounds[i].DeleteALBuffers();
}
else
{
loadedSounds[i].Dispose();
}
}
sourcePools[(int)SourcePoolIndex.Default]?.Dispose();
sourcePools[(int)SourcePoolIndex.Voice]?.Dispose();
}
public void Dispose()
{
if (Disabled) { return; }
ReleaseResources(false);
if (!Alc.MakeContextCurrent(IntPtr.Zero))
{
@@ -809,7 +809,8 @@ namespace Barotrauma
Screen.Selected == GameMain.ParticleEditorScreen ||
Screen.Selected == GameMain.SpriteEditorScreen ||
Screen.Selected == GameMain.SubEditorScreen ||
(Screen.Selected == GameMain.GameScreen && GameMain.GameSession?.GameMode is SubTestMode))
Screen.Selected == GameMain.EventEditorScreen ||
(Screen.Selected == GameMain.GameScreen && GameMain.GameSession?.GameMode is TestGameMode))
{
return "editor";
}
@@ -882,7 +883,7 @@ namespace Barotrauma
if (GameMain.GameSession != null)
{
if (Submarine.Loaded != null && Level.Loaded != null && Submarine.MainSub.AtEndPosition)
if (Submarine.Loaded != null && Level.Loaded != null && Submarine.MainSub != null && Submarine.MainSub.AtEndPosition)
{
return "levelend";
}