Files
LuaCsForBarotraumaEP/Barotrauma/BarotraumaClient/ClientSource/Sounds/VoipSound.cs
2025-06-17 16:38:11 +03:00

190 lines
5.7 KiB
C#

using Barotrauma.Networking;
using Concentus.Structs;
using Microsoft.Xna.Framework;
using OpenAL;
using System;
namespace Barotrauma.Sounds
{
class VoipSound : Sound
{
public override double? DurationSeconds => null;
public override SoundManager.SourcePoolIndex SourcePoolIndex
{
get
{
return SoundManager.SourcePoolIndex.Voice;
}
}
public new bool IsPlaying
{
get
{
return soundChannel != null && soundChannel.IsPlaying;
}
}
private readonly VoipQueue queue;
private int bufferID = 0;
private SoundChannel soundChannel;
private readonly OpusDecoder decoder;
public bool UseRadioFilter;
public bool UseMuffleFilter;
public bool UsingRadio;
public float Near { get; private set; }
public float Far { get; private set; }
private readonly BiQuad[] muffleFilters = new BiQuad[]
{
new LowpassFilter(VoipConfig.FREQUENCY, 800)
};
private readonly BiQuad[] radioFilters = new BiQuad[]
{
new BandpassFilter(VoipConfig.FREQUENCY, 2000)
};
private const float PostRadioFilterBoost = 1.2f;
private float gain;
public float Gain
{
get { return soundChannel == null ? 0.0f : gain; }
set
{
if (soundChannel == null) { return; }
gain = value;
soundChannel.Gain = value * GameSettings.CurrentConfig.Audio.VoiceChatVolume * client.VoiceVolume;
}
}
public float CurrentAmplitude
{
get { return soundChannel?.CurrentAmplitude ?? 0.0f; }
}
private Client client;
public VoipSound(Client targetClient, SoundManager owner, VoipQueue q) : base(owner, $"VoIP ({targetClient.Name})", true, true, getFullPath: false)
{
client = targetClient;
decoder = VoipConfig.CreateDecoder();
ALFormat = Al.FormatMono16;
SampleRate = VoipConfig.FREQUENCY;
queue = q;
bufferID = queue.LatestBufferID;
soundChannel = null;
SoundChannel chn = new SoundChannel(this, 1.0f, null, 1.0f, 0.4f, 1.0f, SoundManager.SoundCategoryVoip, false);
soundChannel = chn;
Gain = 1.0f;
}
public override float GetAmplitudeAtPlaybackPos(int playbackPos)
{
throw new NotImplementedException(); //TODO: implement?
}
public void SetPosition(Vector3? pos)
{
soundChannel.Position = pos;
}
public void SetRange(float near, float far)
{
soundChannel.Near = Near = near;
soundChannel.Far = Far = far;
}
public void ApplyFilters(short[] buffer, int readSamples)
{
float finalGain = gain * GameSettings.CurrentConfig.Audio.VoiceChatVolume * client.VoiceVolume;
for (int i = 0; i < readSamples; i++)
{
float fVal = ToolBox.ShortAudioSampleToFloat(buffer[i]);
if (finalGain > 1.0f) //TODO: take distance into account?
{
fVal = Math.Clamp(fVal * finalGain, -1f, 1f);
}
if (UseMuffleFilter)
{
foreach (var filter in muffleFilters)
{
fVal = filter.Process(fVal);
}
}
if (UseRadioFilter)
{
foreach (var filter in radioFilters)
{
fVal = Math.Clamp(filter.Process(fVal) * PostRadioFilterBoost, -1f, 1f);
}
}
buffer[i] = ToolBox.FloatToShortAudioSample(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, float freqMult = 1.0f, 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);
try
{
if (compressedSize > 0)
{
decoder.Decode(compressedBuffer, 0, compressedSize, buffer, 0, VoipConfig.BUFFER_SIZE);
bufferID++;
return VoipConfig.BUFFER_SIZE;
}
if (bufferID < queue.LatestBufferID - (VoipQueue.BUFFER_COUNT - 1)) bufferID = queue.LatestBufferID - (VoipQueue.BUFFER_COUNT - 1);
}
catch (Exception e)
{
DebugConsole.ThrowError($"Failed to decode Opus buffer (buffer size {compressedBuffer.Length}, packet size {compressedSize})", e);
bufferID = queue.LatestBufferID - (VoipQueue.BUFFER_COUNT - 1);
}
return 0;
}
public override void Dispose()
{
if (soundChannel != null)
{
soundChannel.Dispose();
soundChannel = null;
}
base.Dispose();
}
}
}