using System; using OpenAL; using NVorbis; using System.Collections.Generic; using System.Xml.Linq; namespace Barotrauma.Sounds { class OggSound : Sound { private VorbisReader reader; //key = sample rate, value = filter private static readonly Dictionary muffleFilters = new Dictionary(); private static List 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) { } public override float GetAmplitudeAtPlaybackPos(int playbackPos) { if (playbackAmplitude == null || playbackAmplitude.Count == 0) { return 0.0f; } int index = playbackPos / AMPLITUDE_SAMPLE_COUNT; if (index < 0) { return 0.0f; } if (index >= playbackAmplitude.Count) { index = playbackAmplitude.Count - 1; } return playbackAmplitude[index]; } public override int FillStreamBuffer(int samplePos, short[] buffer) { if (!Stream) { throw new Exception("Called FillStreamBuffer on a non-streamed sound!"); } if (reader == null) { throw new Exception("Called FillStreamBuffer when the reader is null!"); } if (samplePos >= reader.TotalSamples * reader.Channels * 2) return 0; samplePos /= reader.Channels * 2; reader.DecodedPosition = samplePos; float[] floatBuffer = new float[buffer.Length]; int readSamples = reader.ReadSamples(floatBuffer, 0, buffer.Length / 2); //MuffleBuffer(floatBuffer, reader.Channels); CastBuffer(floatBuffer, buffer, readSamples); return readSamples; } static void MuffleBuffer(float[] buffer, int sampleRate, int channelCount) { if (!muffleFilters.TryGetValue(sampleRate, out BiQuad filter)) { filter = new LowpassFilter(sampleRate, 1600); muffleFilters.Add(sampleRate, filter); } 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 (Buffers != null && SoundBuffers.BuffersGenerated < SoundBuffers.MaxBuffers) { Buffers.RequestAlBuffers(); FillBuffers(); } } public override void FillBuffers() { if (!Stream) { reader ??= new VorbisReader(Filename); reader.DecodedPosition = 0; 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(); 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(Buffers.AlBuffer, ALFormat, shortBuffer, readSamples * sizeof(short), SampleRate); int alError = Al.GetError(); if (alError != Al.NoError) { throw new Exception("Failed to set regular buffer data for non-streamed audio! " + Al.GetErrorString(alError)); } MuffleBuffer(floatBuffer, SampleRate, reader.Channels); CastBuffer(floatBuffer, shortBuffer, readSamples); Al.BufferData(Buffers.AlMuffledBuffer, ALFormat, shortBuffer, readSamples * sizeof(short), SampleRate); alError = Al.GetError(); if (alError != Al.NoError) { throw new Exception("Failed to set muffled buffer data for non-streamed audio! " + Al.GetErrorString(alError)); } reader.Dispose(); reader = null; } } public override void Dispose() { if (Stream) { reader?.Dispose(); } base.Dispose(); } } }