Client LuaCsForBarotrauma
OggSound.cs
1 using NVorbis;
2 using OpenAL;
3 using System;
4 using System.Collections.Generic;
5 using System.Threading.Tasks;
6 
8 {
9  sealed class OggSound : Sound
10  {
11  private readonly VorbisReader streamReader;
12 
13  public long MaxStreamSamplePos => streamReader == null ? 0 : streamReader.TotalSamples * streamReader.Channels * 2;
14 
15  private List<float> playbackAmplitude;
16  private const int AMPLITUDE_SAMPLE_COUNT = 4410; //100ms in a 44100hz file
17 
18  private short[] sampleBuffer = Array.Empty<short>();
19  private short[] muffleBuffer = Array.Empty<short>();
20  public OggSound(SoundManager owner, string filename, bool stream, ContentXElement xElement) : base(owner, filename,
21  stream, true, xElement)
22  {
23  var reader = new VorbisReader(Filename);
24 
25  ALFormat = reader.Channels == 1 ? Al.FormatMono16 : Al.FormatStereo16;
26  SampleRate = reader.SampleRate;
27 
28  if (stream)
29  {
30  streamReader = reader;
31  return;
32  }
33 
34  Loading = true;
35  TaskPool.Add(
36  $"LoadSamples {filename}",
37  LoadSamples(reader),
38  t =>
39  {
40  reader.Dispose();
41  if (!t.TryGetResult(out TaskResult result))
42  {
43  return;
44  }
45  sampleBuffer = result.SampleBuffer;
46  muffleBuffer = result.MuffleBuffer;
47  playbackAmplitude = result.PlaybackAmplitude;
48  Owner.KillChannels(this); // prevents INVALID_OPERATION error
49  buffers?.Dispose(); buffers = null;
50  Loading = false;
51  });
52  }
53 
54  private readonly record struct TaskResult(
55  short[] SampleBuffer,
56  short[] MuffleBuffer,
57  List<float> PlaybackAmplitude);
58 
59  private static async Task<TaskResult> LoadSamples(VorbisReader reader)
60  {
61  reader.DecodedPosition = 0;
62 
63  int bufferSize = (int)reader.TotalSamples * reader.Channels;
64 
65  float[] floatBuffer = new float[bufferSize];
66  var sampleBuffer = new short[bufferSize];
67  var muffledBuffer = new short[bufferSize];
68 
69  int readSamples = await Task.Run(() => reader.ReadSamples(floatBuffer, 0, bufferSize));
70 
71  var playbackAmplitude = new List<float>();
72  for (int i = 0; i < bufferSize; i += reader.Channels * AMPLITUDE_SAMPLE_COUNT)
73  {
74  float maxAmplitude = 0.0f;
75  for (int j = i; j < i + reader.Channels * AMPLITUDE_SAMPLE_COUNT; j++)
76  {
77  if (j >= bufferSize) { break; }
78  maxAmplitude = Math.Max(maxAmplitude, Math.Abs(floatBuffer[j]));
79  }
80  playbackAmplitude.Add(maxAmplitude);
81  }
82 
83  CastBuffer(floatBuffer, sampleBuffer, readSamples);
84 
85  MuffleBuffer(floatBuffer, reader.SampleRate);
86 
87  CastBuffer(floatBuffer, muffledBuffer, readSamples);
88 
89  return new TaskResult(sampleBuffer, muffledBuffer, playbackAmplitude);
90  }
91 
92  public override float GetAmplitudeAtPlaybackPos(int playbackPos)
93  {
94  if (playbackAmplitude == null || playbackAmplitude.Count == 0) { return 0.0f; }
95  int index = playbackPos / AMPLITUDE_SAMPLE_COUNT;
96  if (index < 0) { return 0.0f; }
97  if (index >= playbackAmplitude.Count) { index = playbackAmplitude.Count - 1; }
98  return playbackAmplitude[index];
99  }
100 
101  private float[] streamFloatBuffer = null;
102  public override int FillStreamBuffer(int samplePos, short[] buffer)
103  {
104  if (!Stream) { throw new Exception("Called FillStreamBuffer on a non-streamed sound!"); }
105  if (streamReader == null) { throw new Exception("Called FillStreamBuffer when the reader is null!"); }
106 
107  if (samplePos >= MaxStreamSamplePos) { return 0; }
108 
109  samplePos /= streamReader.Channels * 2;
110  streamReader.DecodedPosition = samplePos;
111 
112  if (streamFloatBuffer is null || streamFloatBuffer.Length < buffer.Length)
113  {
114  streamFloatBuffer = new float[buffer.Length];
115  }
116  int readSamples = streamReader.ReadSamples(streamFloatBuffer, 0, buffer.Length);
117  //MuffleBuffer(floatBuffer, reader.Channels);
118  CastBuffer(streamFloatBuffer, buffer, readSamples);
119 
120  return readSamples;
121  }
122 
123  static void MuffleBuffer(float[] buffer, int sampleRate)
124  {
125  var filter = new LowpassFilter(sampleRate, 1600);
126  filter.Process(buffer);
127  }
128 
129  public override void InitializeAlBuffers()
130  {
132  {
133  FillAlBuffers();
134  }
135  }
136 
137  public override void FillAlBuffers()
138  {
139  if (Stream) { return; }
140  if (sampleBuffer.Length == 0 || muffleBuffer.Length == 0) { return; }
141  buffers ??= new SoundBuffers(this);
142  if (!buffers.RequestAlBuffers()) { return; }
143 
144  Al.BufferData(buffers.AlBuffer, ALFormat, sampleBuffer,
145  sampleBuffer.Length * sizeof(short), SampleRate);
146 
147  int alError = Al.GetError();
148  if (alError != Al.NoError)
149  {
150  throw new Exception("Failed to set regular buffer data for non-streamed audio! " + Al.GetErrorString(alError));
151  }
152 
154  muffleBuffer.Length * sizeof(short), SampleRate);
155 
156  alError = Al.GetError();
157  if (alError != Al.NoError)
158  {
159  throw new Exception("Failed to set muffled buffer data for non-streamed audio! " + Al.GetErrorString(alError));
160  }
161  }
162 
163  public override void Dispose()
164  {
165  if (Stream)
166  {
167  streamReader?.Dispose();
168  }
169 
170  base.Dispose();
171  }
172  }
173 }
Used to apply a lowpass-filter to a signal.
override int FillStreamBuffer(int samplePos, short[] buffer)
Definition: OggSound.cs:102
override float GetAmplitudeAtPlaybackPos(int playbackPos)
Definition: OggSound.cs:92
OggSound(SoundManager owner, string filename, bool stream, ContentXElement xElement)
Definition: OggSound.cs:20
override void InitializeAlBuffers()
Definition: OggSound.cs:129
override void FillAlBuffers()
Definition: OggSound.cs:137
override void Dispose()
Definition: OggSound.cs:163
static void CastBuffer(float[] inBuffer, short[] outBuffer, int length)
Definition: Sound.cs:136
SoundBuffers buffers
Definition: Sound.cs:38
readonly bool Stream
Definition: Sound.cs:23
readonly string Filename
Definition: Sound.cs:19
readonly SoundManager Owner
Definition: Sound.cs:17
void KillChannels(Sound sound)
Definition: Al.cs:38
const int FormatStereo16
Definition: Al.cs:86
static string GetErrorString(int error)
Definition: Al.cs:164
static int GetError()
const int FormatMono16
Definition: Al.cs:84
static void BufferData(uint bid, int format, IntPtr data, int size, int freq)
const int NoError
Definition: Al.cs:98
Definition: Al.cs:36