using Barotrauma.Networking; using Concentus.Structs; using Microsoft.Xna.Framework; using OpenAL; using System; namespace Barotrauma.Sounds { class VoipSound : Sound { 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, "voip", 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(); } } }