Client LuaCsForBarotrauma
SoundChannel.cs
1 using Microsoft.Xna.Framework;
2 using OpenAL;
3 using System;
4 using System.Threading;
5 
6 namespace Barotrauma.Sounds
7 {
8  class SoundSourcePool : IDisposable
9  {
10  public uint[] ALSources
11  {
12  get;
13  private set;
14  }
15 
16  public SoundSourcePool(int sourceCount = SoundManager.SourceCount)
17  {
18  int alError;
19 
20  ALSources = new uint[sourceCount];
21  for (int i = 0; i < sourceCount; i++)
22  {
23  Al.GenSource(out ALSources[i]);
24  alError = Al.GetError();
25  if (alError != Al.NoError)
26  {
27  throw new Exception("Error generating alSource[" + i.ToString() + "]: " + Al.GetErrorString(alError));
28  }
29 
30  if (!Al.IsSource(ALSources[i]))
31  {
32  throw new Exception("Generated alSource[" + i.ToString() + "] is invalid!");
33  }
34 
36  alError = Al.GetError();
37  if (alError != Al.NoError)
38  {
39  throw new Exception("Error stopping newly generated alSource[" + i.ToString() + "]: " + Al.GetErrorString(alError));
40  }
41 
42  Al.Sourcef(ALSources[i], Al.MinGain, 0.0f);
43  alError = Al.GetError();
44  if (alError != Al.NoError)
45  {
46  throw new Exception("Error setting min gain: " + Al.GetErrorString(alError));
47  }
48 
49  Al.Sourcef(ALSources[i], Al.MaxGain, 1.0f);
50  alError = Al.GetError();
51  if (alError != Al.NoError)
52  {
53  throw new Exception("Error setting max gain: " + Al.GetErrorString(alError));
54  }
55 
56  Al.Sourcef(ALSources[i], Al.RolloffFactor, 1.0f);
57  alError = Al.GetError();
58  if (alError != Al.NoError)
59  {
60  throw new Exception("Error setting rolloff factor: " + Al.GetErrorString(alError));
61  }
62  }
63  }
64 
65  public void Dispose()
66  {
67  if (ALSources == null) { return; }
68  for (int i = 0; i < ALSources.Length; i++)
69  {
71  int alError = Al.GetError();
72  if (alError != Al.NoError)
73  {
74  throw new Exception("Failed to delete ALSources[" + i.ToString() + "]: " + Al.GetErrorString(alError));
75  }
76  }
77  ALSources = null;
78  }
79  }
80 
81  class SoundChannel : IDisposable
82  {
83  private const int STREAM_BUFFER_SIZE = 8820;
84  private readonly short[] streamShortBuffer;
85 
86  private string debugName = "SoundChannel";
87 
88  private Vector3? position;
89  public Vector3? Position
90  {
91  get { return position; }
92  set
93  {
94  position = value;
95 
96  if (ALSourceIndex < 0) { return; }
97 
98  if (position != null)
99  {
100  if (float.IsNaN(position.Value.X))
101  {
102  DebugConsole.ThrowError("Failed to set source's position: " + debugName + ", position.X is NaN", appendStackTrace: true);
103  return;
104  }
105  if (float.IsNaN(position.Value.Y))
106  {
107  DebugConsole.ThrowError("Failed to set source's position: " + debugName + ", position.Y is NaN", appendStackTrace: true);
108  return;
109  }
110  if (float.IsNaN(position.Value.Z))
111  {
112  DebugConsole.ThrowError("Failed to set source's position: " + debugName + ", position.Z is NaN", appendStackTrace: true);
113  return;
114  }
115 
116  if (float.IsInfinity(position.Value.X))
117  {
118  DebugConsole.ThrowError("Failed to set source's position: " + debugName + ", position.X is Infinity", appendStackTrace: true);
119  return;
120  }
121  if (float.IsInfinity(position.Value.Y))
122  {
123  DebugConsole.ThrowError("Failed to set source's position: " + debugName + ", position.Y is Infinity", appendStackTrace: true);
124  return;
125  }
126  if (float.IsInfinity(position.Value.Z))
127  {
128  DebugConsole.ThrowError("Failed to set source's position: " + debugName + ", position.Z is Infinity", appendStackTrace: true);
129  return;
130  }
131 
133  Al.Sourcei(alSource, Al.SourceRelative, Al.False);
134  int alError = Al.GetError();
135  if (alError != Al.NoError)
136  {
137  DebugConsole.ThrowError("Failed to enable source's relative flag: " + debugName + ", " + Al.GetErrorString(alError), appendStackTrace: true);
138  return;
139  }
140 
141  Al.Source3f(alSource, Al.Position, position.Value.X, position.Value.Y, position.Value.Z);
142  alError = Al.GetError();
143  if (alError != Al.NoError)
144  {
145  DebugConsole.ThrowError("Failed to set source's position: " + debugName + ", " + Al.GetErrorString(alError), appendStackTrace: true);
146  return;
147  }
148  }
149  else
150  {
152  Al.Sourcei(alSource, Al.SourceRelative, Al.True);
153  int alError = Al.GetError();
154  if (alError != Al.NoError)
155  {
156  DebugConsole.ThrowError("Failed to disable source's relative flag: " + debugName + ", " + Al.GetErrorString(alError), appendStackTrace: true);
157  return;
158  }
159 
160  Al.Source3f(alSource, Al.Position, 0.0f, 0.0f, 0.0f);
161  alError = Al.GetError();
162  if (alError != Al.NoError)
163  {
164  DebugConsole.ThrowError("Failed to reset source's position: " + debugName + ", " + Al.GetErrorString(alError), appendStackTrace: true);
165  return;
166  }
167  }
168  }
169  }
170 
171  private float near;
172  public float Near
173  {
174  get { return near; }
175  set
176  {
177  near = value;
178 
179  if (ALSourceIndex < 0) { return; }
180 
182  Al.Sourcef(alSource, Al.ReferenceDistance, near);
183 
184  int alError = Al.GetError();
185  if (alError != Al.NoError)
186  {
187  DebugConsole.ThrowError("Failed to set source's reference distance: " + debugName + ", " + Al.GetErrorString(alError), appendStackTrace: true);
188  return;
189  }
190  }
191  }
192 
193  private float far;
194  public float Far
195  {
196  get { return far; }
197  set
198  {
199  far = value;
200 
201  if (ALSourceIndex < 0) { return; }
202 
204  Al.Sourcef(alSource, Al.MaxDistance, far);
205  int alError = Al.GetError();
206  if (alError != Al.NoError)
207  {
208  DebugConsole.ThrowError("Failed to set source's max distance: " + debugName + ", " + Al.GetErrorString(alError), appendStackTrace: true);
209  return;
210  }
211  }
212  }
213 
214  private float gain;
215  public float Gain
216  {
217  get { return gain; }
218  set
219  {
220  if (!MathUtils.IsValid(value)) { return; }
221 
222  gain = Math.Max(value, 0.0f);
223 
224  if (ALSourceIndex < 0) { return; }
225 
227 
228  float effectiveGain = gain;
229  if (category != null) { effectiveGain *= Sound.Owner.GetCategoryGainMultiplier(category); }
230 
231  Al.Sourcef(alSource, Al.Gain, effectiveGain);
232  int alError = Al.GetError();
233  if (alError != Al.NoError)
234  {
235  DebugConsole.ThrowError($"Failed to set source's gain to {gain} (effective gain {effectiveGain}): {debugName}, {Al.GetErrorString(alError)}", appendStackTrace: true);
236  return;
237  }
238  }
239  }
240 
241  private bool looping;
242  public bool Looping
243  {
244  get { return looping; }
245  set
246  {
247  looping = value;
248 
249  if (ALSourceIndex < 0) { return; }
250 
251  if (!IsStream)
252  {
254  Al.Sourcei(alSource, Al.Looping, looping ? Al.True : Al.False);
255  int alError = Al.GetError();
256  if (alError != Al.NoError)
257  {
258  DebugConsole.ThrowError("Failed to set source's looping state: " + debugName + ", " + Al.GetErrorString(alError), appendStackTrace: true);
259  return;
260  }
261  }
262  }
263  }
264 
265  public const float MinFrequencyMultiplier = 0.25f;
266  public const float MaxFrequencyMultiplier = 4.0f;
267 
268  public float frequencyMultiplier;
269  public float FrequencyMultiplier
270  {
271  get
272  {
273  return frequencyMultiplier;
274  }
275  set
276  {
277  if (value is < MinFrequencyMultiplier or > MaxFrequencyMultiplier)
278  {
279  DebugConsole.ThrowError($"Frequency multiplier out of range: {value}" + Environment.StackTrace.CleanupStackTrace());
280  }
282 
283  if (ALSourceIndex < 0) { return; }
284 
286 
287  Al.Sourcef(alSource, Al.Pitch, frequencyMultiplier);
288  int alError = Al.GetError();
289  if (alError != Al.NoError)
290  {
291  throw new Exception("Failed to set source's frequency multiplier: " + debugName + ", " + Al.GetErrorString(alError));
292  }
293  }
294  }
295 
296  public bool FilledByNetwork
297  {
298  get;
299  private set;
300  }
301 
302  private int decayTimer;
303 
304  private bool muffled;
305  public bool Muffled
306  {
307  get { return muffled; }
308  set
309  {
310  if (muffled == value) { return; }
311 
312  muffled = value;
313 
314  if (ALSourceIndex < 0) { return; }
315 
316  if (!IsPlaying) { return; }
317 
318  if (IsStream) { return; }
319 
321  Al.GetSourcei(alSource, Al.SampleOffset, out int playbackPos);
322  int alError = Al.GetError();
323  if (alError != Al.NoError)
324  {
325  DebugConsole.ThrowError("Failed to get source's playback position: " + debugName + ", " + Al.GetErrorString(alError), appendStackTrace: true);
326  return;
327  }
328 
329  Al.SourceStop(alSource);
330 
331  alError = Al.GetError();
332  if (alError != Al.NoError)
333  {
334  DebugConsole.ThrowError("Failed to stop source: " + debugName + ", " + Al.GetErrorString(alError), appendStackTrace: true);
335  return;
336  }
337 
339  if (Sound.Buffers is not { AlBuffer: not 0, AlMuffledBuffer: not 0 }) { return; }
340 
341  Al.Sourcei(alSource, Al.Buffer, muffled ? (int)Sound.Buffers.AlMuffledBuffer : (int)Sound.Buffers.AlBuffer);
342 
343  alError = Al.GetError();
344  if (alError != Al.NoError)
345  {
346  DebugConsole.ThrowError("Failed to bind buffer to source: " + debugName + ", " + Al.GetErrorString(alError), appendStackTrace: true);
347  return;
348  }
349 
350  Al.SourcePlay(alSource);
351  alError = Al.GetError();
352  if (alError != Al.NoError)
353  {
354  DebugConsole.ThrowError("Failed to replay source: " + debugName + ", " + Al.GetErrorString(alError), appendStackTrace: true);
355  return;
356  }
357 
358  Al.Sourcei(alSource, Al.SampleOffset, playbackPos);
359  alError = Al.GetError();
360  if (alError != Al.NoError)
361  {
362  DebugConsole.ThrowError("Failed to reset playback position: " + debugName + ", " + Al.GetErrorString(alError), appendStackTrace: true);
363  return;
364  }
365  }
366  }
367 
368  private float streamAmplitude;
369  public float CurrentAmplitude
370  {
371  get
372  {
373  if (!IsPlaying) { return 0.0f; }
374 
376 
377  if (alSource == 0) { return 0.0f; }
378 
379  if (!IsStream)
380  {
381  Al.GetSourcei(alSource, Al.SampleOffset, out int playbackPos);
382  int alError = Al.GetError();
383  if (alError != Al.NoError)
384  {
385  DebugConsole.ThrowError("Failed to get source's playback position: " + debugName + ", " + Al.GetErrorString(alError), appendStackTrace: true);
386  return 0.0f;
387  }
388  return Sound.GetAmplitudeAtPlaybackPos(playbackPos);
389  }
390  else
391  {
392  float retVal;
393  Monitor.Enter(mutex);
394  retVal = streamAmplitude;
395  Monitor.Exit(mutex);
396  return retVal;
397  }
398  }
399  }
400 
401  private string category;
402  public string Category
403  {
404  get { return category; }
405  set
406  {
407  category = value;
408  Gain = gain;
409  }
410  }
411 
412  public Sound Sound
413  {
414  get;
415  private set;
416  }
417 
418  public int ALSourceIndex
419  {
420  get;
421  private set;
422  } = -1;
423 
424  public bool IsStream
425  {
426  get;
427  private set;
428  }
429  private int streamSeekPos;
430  private int buffersToRequeue;
431  private bool reachedEndSample;
432  private int queueStartIndex;
433  private readonly uint[] streamBuffers;
434  private readonly uint[] unqueuedBuffers;
435  private readonly float[] streamBufferAmplitudes;
436 
437  public int StreamSeekPos
438  {
439  get { return streamSeekPos; }
440  set
441  {
442  if (!IsStream)
443  {
444  throw new InvalidOperationException("Cannot set StreamSeekPos on a non-streaming sound channel.");
445  }
446  streamSeekPos = Math.Max(value, 0);
447  }
448  }
449 
450  public long MaxStreamSeekPos
451  {
452  get
453  {
454  if (!IsStream || Sound is not OggSound oggSound)
455  {
456  return 0;
457  }
458  return oggSound.MaxStreamSamplePos;
459  }
460  }
461 
462  private readonly object mutex;
463 
464  public bool IsPlaying
465  {
466  get
467  {
468  if (ALSourceIndex < 0) { return false; }
469  if (IsStream && !reachedEndSample) { return true; }
471  if (!Al.IsSource(alSource)) { return false; }
472  Al.GetSourcei(alSource, Al.SourceState, out int state);
473  int alError = Al.GetError();
474  if (alError != Al.NoError)
475  {
476  DebugConsole.ThrowError("Failed to determine playing state from source: " + debugName + ", " + Al.GetErrorString(alError), appendStackTrace: true);
477  return false;
478  }
479  bool playing = state == Al.Playing;
480  return playing;
481  }
482  }
483 
484  public SoundChannel(Sound sound, float gain, Vector3? position, float freqMult, float near, float far, string category, bool muffle = false)
485  {
486  Sound = sound;
487 
488  debugName = sound == null ?
489  "SoundChannel (null)" :
490  $"SoundChannel ({(string.IsNullOrEmpty(sound.Filename) ? "filename empty" : sound.Filename) })";
491 
492  IsStream = sound.Stream;
493  FilledByNetwork = sound is VoipSound;
494  decayTimer = 0;
495  streamSeekPos = 0; reachedEndSample = false;
496  buffersToRequeue = 4;
497  muffled = muffle;
498 
499  if (IsStream)
500  {
501  mutex = new object();
502  }
503 
504 #if !DEBUG
505  try
506  {
507 #endif
508  if (mutex != null) { Monitor.Enter(mutex); }
509  if (sound.Owner.CountPlayingInstances(sound) < sound.MaxSimultaneousInstances)
510  {
512  }
513 
514  if (ALSourceIndex >= 0)
515  {
516  if (!IsStream)
517  {
519  int alError = Al.GetError();
520  if (alError != Al.NoError)
521  {
522  throw new Exception("Failed to reset source buffer: " + debugName + ", " + Al.GetErrorString(alError));
523  }
524 
526  if (Sound.Buffers is not { AlBuffer: not 0, AlMuffledBuffer: not 0 }) { return; }
527 
528  uint alBuffer = sound.Owner.GetCategoryMuffle(category) || muffled ? Sound.Buffers.AlMuffledBuffer : Sound.Buffers.AlBuffer;
530  alError = Al.GetError();
531  if (alError != Al.NoError)
532  {
533  throw new Exception("Failed to bind buffer to source (" + ALSourceIndex.ToString() + ":" + sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex) + "," + alBuffer.ToString() + "): " + debugName + ", " + Al.GetErrorString(alError));
534  }
535 
536  SetProperties();
537 
539  alError = Al.GetError();
540  if (alError != Al.NoError)
541  {
542  throw new Exception("Failed to play source: " + debugName + ", " + Al.GetErrorString(alError));
543  }
544  }
545  else
546  {
547  uint alBuffer = 0;
549  int alError = Al.GetError();
550  if (alError != Al.NoError)
551  {
552  throw new Exception("Failed to reset source buffer: " + debugName + ", " + Al.GetErrorString(alError));
553  }
554 
556  alError = Al.GetError();
557  if (alError != Al.NoError)
558  {
559  throw new Exception("Failed to set stream looping state: " + debugName + ", " + Al.GetErrorString(alError));
560  }
561 
562  streamShortBuffer = new short[STREAM_BUFFER_SIZE];
563 
564  streamBuffers = new uint[4];
565  unqueuedBuffers = new uint[4];
566  streamBufferAmplitudes = new float[4];
567  for (int i = 0; i < 4; i++)
568  {
569  Al.GenBuffer(out streamBuffers[i]);
570 
571  alError = Al.GetError();
572  if (alError != Al.NoError)
573  {
574  throw new Exception("Failed to generate stream buffers: " + debugName + ", " + Al.GetErrorString(alError));
575  }
576 
577  if (!Al.IsBuffer(streamBuffers[i]))
578  {
579  throw new Exception("Generated streamBuffer[" + i.ToString() + "] is invalid! " + debugName);
580  }
581  }
583  SetProperties();
584  }
585  }
586 #if !DEBUG
587  }
588  catch
589  {
590  throw;
591  }
592  finally
593  {
594 #endif
595  if (mutex != null) { Monitor.Exit(mutex); }
596 #if !DEBUG
597  }
598 #endif
599 
600  void SetProperties()
601  {
602  this.Position = position;
603  this.Gain = gain;
604  this.FrequencyMultiplier = freqMult;
605  this.Looping = false;
606  this.Near = near;
607  this.Far = far;
608  this.Category = category;
609  }
610 
611  Sound.Owner.Update();
612  }
613 
614  public override string ToString()
615  {
616  return debugName;
617  }
618 
620  {
621  get;
622  private set;
623  }
624  public void FadeOutAndDispose()
625  {
626  FadingOutAndDisposing = true;
628  }
629 
630  public void Dispose()
631  {
632  try
633  {
634  if (mutex != null) { Monitor.Enter(mutex); }
635  if (ALSourceIndex >= 0)
636  {
638  int alError = Al.GetError();
639  if (alError != Al.NoError)
640  {
641  throw new Exception("Failed to stop source: " + debugName + ", " + Al.GetErrorString(alError));
642  }
643 
644  if (IsStream)
645  {
647 
648  Al.SourceStop(alSource);
649  alError = Al.GetError();
650  if (alError != Al.NoError)
651  {
652  throw new Exception("Failed to stop streamed source: " + debugName + ", " + Al.GetErrorString(alError));
653  }
654 
655  int buffersToRequeue = 0;
656 
657  buffersToRequeue = 0;
658  Al.GetSourcei(alSource, Al.BuffersProcessed, out buffersToRequeue);
659  alError = Al.GetError();
660  if (alError != Al.NoError)
661  {
662  throw new Exception("Failed to determine processed buffers from streamed source: " + debugName + ", " + Al.GetErrorString(alError));
663  }
664 
665  Al.SourceUnqueueBuffers(alSource, buffersToRequeue, unqueuedBuffers);
666  alError = Al.GetError();
667  if (alError != Al.NoError)
668  {
669  throw new Exception("Failed to unqueue buffers from streamed source: " + debugName + ", " + Al.GetErrorString(alError));
670  }
671 
672  Al.Sourcei(alSource, Al.Buffer, 0);
673  alError = Al.GetError();
674  if (alError != Al.NoError)
675  {
676  throw new Exception("Failed to reset buffer for streamed source: " + debugName + ", " + Al.GetErrorString(alError));
677  }
678 
679  for (int i = 0; i < 4; i++)
680  {
681  Al.DeleteBuffer(streamBuffers[i]);
682  alError = Al.GetError();
683  if (alError != Al.NoError)
684  {
685  throw new Exception("Failed to delete streamBuffers[" + i.ToString() + "] (" + streamBuffers[i].ToString() + "): " + debugName + ", " + Al.GetErrorString(alError));
686  }
687  }
688 
689  reachedEndSample = true;
690  }
691  else
692  {
694  alError = Al.GetError();
695  if (alError != Al.NoError)
696  {
697  throw new Exception("Failed to unbind buffer to non-streamed source: " + debugName + ", " + Al.GetErrorString(alError));
698  }
699  }
700 
701  ALSourceIndex = -1;
702  debugName += " [DISPOSED]";
703  }
704  }
705  finally
706  {
707  if (mutex != null) { Monitor.Exit(mutex); }
708  }
709  }
710 
711  public void UpdateStream()
712  {
713  try
714  {
715  if (!IsStream) { throw new Exception("Called UpdateStream on a non-streamed sound channel!"); }
716 
717  Monitor.Enter(mutex);
718  if (!reachedEndSample)
719  {
721 
722  Al.GetSourcei(alSource, Al.SourceState, out int state);
723  bool playing = state == Al.Playing;
724  int alError = Al.GetError();
725  if (alError != Al.NoError)
726  {
727  throw new Exception("Failed to determine playing state from streamed source: " + debugName + ", " + Al.GetErrorString(alError));
728  }
729 
730  Al.GetSourcei(alSource, Al.BuffersProcessed, out int unqueuedBufferCount);
731  alError = Al.GetError();
732  if (alError != Al.NoError)
733  {
734  throw new Exception("Failed to determine processed buffers from streamed source: " + debugName + ", " + Al.GetErrorString(alError));
735  }
736 
737  Al.SourceUnqueueBuffers(alSource, unqueuedBufferCount, unqueuedBuffers);
738  alError = Al.GetError();
739  if (alError != Al.NoError)
740  {
741  throw new Exception("Failed to unqueue buffers from streamed source: " + debugName + ", " + Al.GetErrorString(alError));
742  }
743 
744  buffersToRequeue += unqueuedBufferCount;
745 
746  int iterCount = buffersToRequeue;
747  for (int k = 0; k < iterCount; k++)
748  {
749  int index = queueStartIndex;
750  short[] buffer = streamShortBuffer;
751  int readSamples = Sound.FillStreamBuffer(streamSeekPos, buffer);
752  float readAmplitude = 0.0f;
753 
754  for (int i = 0; i < Math.Min(readSamples, buffer.Length); i++)
755  {
756  float sampleF = ((float)buffer[i]) / ((float)short.MaxValue);
757  readAmplitude = Math.Max(readAmplitude, Math.Abs(sampleF));
758  }
759 
760  if (FilledByNetwork)
761  {
762  if (readSamples <= 0)
763  {
764  streamAmplitude *= 0.5f;
765  decayTimer++;
766  if (decayTimer > 120) //TODO: replace magic number
767  {
768  reachedEndSample = true;
769  }
770  }
771  else
772  {
773  if (Sound is VoipSound voipSound)
774  {
775  voipSound.ApplyFilters(buffer, readSamples);
776  }
777 
778  decayTimer = 0;
779  }
780  }
781  else if (Sound.StreamsReliably)
782  {
783  streamSeekPos += readSamples * 2;
784  if (readSamples * 2 < STREAM_BUFFER_SIZE)
785  {
786  if (looping)
787  {
788  streamSeekPos = 0;
789  }
790  else
791  {
792  reachedEndSample = true;
793  }
794  }
795  }
796 
797  if (readSamples > 0)
798  {
799  streamBufferAmplitudes[index] = readAmplitude;
800 
801  Al.BufferData<short>(streamBuffers[index], Sound.ALFormat, buffer, readSamples * 2, Sound.SampleRate);
802 
803  alError = Al.GetError();
804  if (alError != Al.NoError)
805  {
806  throw new Exception("Failed to assign data to stream buffer: " +
807  Al.GetErrorString(alError) + ": " + streamBuffers[index].ToString() + "/" + streamBuffers.Length + ", readSamples: " + readSamples + ", " + debugName);
808  }
809 
810  Al.SourceQueueBuffer(alSource, streamBuffers[index]);
811  queueStartIndex = (queueStartIndex + 1) % 4;
812 
813  alError = Al.GetError();
814  if (alError != Al.NoError)
815  {
816  throw new Exception("Failed to queue streamBuffer[" + index.ToString() + "] to stream: " + debugName + ", " + Al.GetErrorString(alError));
817  }
818  }
819  else
820  {
821  if (readSamples < 0)
822  {
823  reachedEndSample = true;
824  }
825  break;
826  }
827  buffersToRequeue--;
828  }
829 
830  streamAmplitude = streamBufferAmplitudes[queueStartIndex];
831 
832  Al.GetSourcei(alSource, Al.SourceState, out state);
833  alError = Al.GetError();
834  if (alError != Al.NoError)
835  {
836  throw new Exception("Failed to retrieve stream source state: " + debugName + ", " + Al.GetErrorString(alError));
837  }
838 
839  if (state != Al.Playing)
840  {
841  Al.SourcePlay(alSource);
842  alError = Al.GetError();
843  if (alError != Al.NoError)
844  {
845  throw new Exception("Failed to start stream playback: " + debugName + ", " + Al.GetErrorString(alError));
846  }
847  }
848  }
849 
850  if (reachedEndSample)
851  {
852  streamAmplitude = 0.0f;
853  }
854  }
855  catch (Exception e)
856  {
857  DebugConsole.ThrowError($"An exception was thrown when updating a sound stream ({debugName})", e);
858  }
859  finally
860  {
861  Monitor.Exit(mutex);
862  }
863  }
864  }
865 }
SoundChannel(Sound sound, float gain, Vector3? position, float freqMult, float near, float far, string category, bool muffle=false)
virtual SoundManager.SourcePoolIndex SourcePoolIndex
Definition: Sound.cs:31
int MaxSimultaneousInstances
How many instances of the same sound clip can be playing at the same time
Definition: Sound.cs:59
virtual void FillAlBuffers()
Definition: Sound.cs:150
abstract int FillStreamBuffer(int samplePos, short[] buffer)
readonly bool StreamsReliably
Definition: Sound.cs:25
readonly bool Stream
Definition: Sound.cs:23
SoundBuffers? Buffers
Definition: Sound.cs:40
abstract float GetAmplitudeAtPlaybackPos(int playbackPos)
readonly SoundManager Owner
Definition: Sound.cs:17
int CountPlayingInstances(Sound sound)
float GetCategoryGainMultiplier(string category, int index=-1)
bool GetCategoryMuffle(string category)
uint GetSourceFromIndex(SourcePoolIndex poolIndex, int srcInd)
void InitUpdateChannelThread()
Initializes the thread that handles streaming audio and fading out and disposing channels that are no...
int AssignFreeSourceToChannel(SoundChannel newChannel)
SoundSourcePool(int sourceCount=SoundManager.SourceCount)
Definition: SoundChannel.cs:16
Definition: Al.cs:38
static void DeleteBuffer(uint buffer)
static void GetSourcei(uint sid, int param, out int value)
const int SourceRelative
Definition: Al.cs:56
const int SourceState
Definition: Al.cs:69
static bool IsSource(uint sid)
static bool IsBuffer(uint bid)
static void SourceQueueBuffer(uint sid, uint bid)
Definition: Al.cs:402
static void Sourcef(uint sid, int param, float value)
static void DeleteSource(uint source)
static void GenBuffer(out uint buffer)
Definition: Al.cs:423
const int False
Definition: Al.cs:54
static void SourcePlay(uint sid)
const int ReferenceDistance
Definition: Al.cs:87
const int MaxGain
Definition: Al.cs:67
const int BuffersProcessed
Definition: Al.cs:75
const int MaxDistance
Definition: Al.cs:90
static void SourceStop(uint sid)
static string GetErrorString(int error)
Definition: Al.cs:164
static int GetError()
const int RolloffFactor
Definition: Al.cs:88
const int True
Definition: Al.cs:55
static void GenSource(out uint source)
Definition: Al.cs:251
const int Pitch
Definition: Al.cs:59
static void Sourcei(uint sid, int param, int value)
const int Gain
Definition: Al.cs:65
static void BufferData(uint bid, int format, IntPtr data, int size, int freq)
static void Source3f(uint sid, int param, float value1, float value2, float value3)
const int MinGain
Definition: Al.cs:66
const int Position
Definition: Al.cs:60
const int SampleOffset
Definition: Al.cs:77
const int Playing
Definition: Al.cs:71
const int Looping
Definition: Al.cs:63
const int Buffer
Definition: Al.cs:64
const int NoError
Definition: Al.cs:98
static void SourceUnqueueBuffers(uint sid, int numEntries, uint[] bids)
Definition: Al.cs:36