1 using Microsoft.Xna.Framework;
4 using System.Threading;
21 for (
int i = 0; i < sourceCount; i++)
27 throw new Exception(
"Error generating alSource[" + i.ToString() +
"]: " +
Al.
GetErrorString(alError));
32 throw new Exception(
"Generated alSource[" + i.ToString() +
"] is invalid!");
39 throw new Exception(
"Error stopping newly generated alSource[" + i.ToString() +
"]: " +
Al.
GetErrorString(alError));
46 throw new Exception(
"Error setting min gain: " +
Al.
GetErrorString(alError));
53 throw new Exception(
"Error setting max gain: " +
Al.
GetErrorString(alError));
60 throw new Exception(
"Error setting rolloff factor: " +
Al.
GetErrorString(alError));
68 for (
int i = 0; i <
ALSources.Length; i++)
74 throw new Exception(
"Failed to delete ALSources[" + i.ToString() +
"]: " +
Al.
GetErrorString(alError));
83 private const int STREAM_BUFFER_SIZE = 8820;
84 private readonly
short[] streamShortBuffer;
86 private string debugName =
"SoundChannel";
88 private Vector3? position;
91 get {
return position; }
100 if (
float.IsNaN(position.Value.X))
102 DebugConsole.ThrowError(
"Failed to set source's position: " + debugName +
", position.X is NaN", appendStackTrace:
true);
105 if (
float.IsNaN(position.Value.Y))
107 DebugConsole.ThrowError(
"Failed to set source's position: " + debugName +
", position.Y is NaN", appendStackTrace:
true);
110 if (
float.IsNaN(position.Value.Z))
112 DebugConsole.ThrowError(
"Failed to set source's position: " + debugName +
", position.Z is NaN", appendStackTrace:
true);
116 if (
float.IsInfinity(position.Value.X))
118 DebugConsole.ThrowError(
"Failed to set source's position: " + debugName +
", position.X is Infinity", appendStackTrace:
true);
121 if (
float.IsInfinity(position.Value.Y))
123 DebugConsole.ThrowError(
"Failed to set source's position: " + debugName +
", position.Y is Infinity", appendStackTrace:
true);
126 if (
float.IsInfinity(position.Value.Z))
128 DebugConsole.ThrowError(
"Failed to set source's position: " + debugName +
", position.Z is Infinity", appendStackTrace:
true);
137 DebugConsole.ThrowError(
"Failed to enable source's relative flag: " + debugName +
", " +
Al.
GetErrorString(alError), appendStackTrace:
true);
145 DebugConsole.ThrowError(
"Failed to set source's position: " + debugName +
", " +
Al.
GetErrorString(alError), appendStackTrace:
true);
156 DebugConsole.ThrowError(
"Failed to disable source's relative flag: " + debugName +
", " +
Al.
GetErrorString(alError), appendStackTrace:
true);
164 DebugConsole.ThrowError(
"Failed to reset source's position: " + debugName +
", " +
Al.
GetErrorString(alError), appendStackTrace:
true);
187 DebugConsole.ThrowError(
"Failed to set source's reference distance: " + debugName +
", " +
Al.
GetErrorString(alError), appendStackTrace:
true);
208 DebugConsole.ThrowError(
"Failed to set source's max distance: " + debugName +
", " +
Al.
GetErrorString(alError), appendStackTrace:
true);
220 if (!MathUtils.IsValid(value)) {
return; }
222 gain = Math.Max(value, 0.0f);
228 float effectiveGain = gain;
235 DebugConsole.ThrowError($
"Failed to set source's gain to {gain} (effective gain {effectiveGain}): {debugName}, {Al.GetErrorString(alError)}", appendStackTrace:
true);
241 private bool looping;
244 get {
return looping; }
258 DebugConsole.ThrowError(
"Failed to set source's looping state: " + debugName +
", " +
Al.
GetErrorString(alError), appendStackTrace:
true);
279 DebugConsole.ThrowError($
"Frequency multiplier out of range: {value}" + Environment.StackTrace.CleanupStackTrace());
291 throw new Exception(
"Failed to set source's frequency multiplier: " + debugName +
", " +
Al.
GetErrorString(alError));
302 private int decayTimer;
304 private bool muffled;
307 get {
return muffled; }
310 if (muffled == value) {
return; }
325 DebugConsole.ThrowError(
"Failed to get source's playback position: " + debugName +
", " +
Al.
GetErrorString(alError), appendStackTrace:
true);
334 DebugConsole.ThrowError(
"Failed to stop source: " + debugName +
", " +
Al.
GetErrorString(alError), appendStackTrace:
true);
339 if (
Sound.
Buffers is not { AlBuffer: not 0, AlMuffledBuffer: not 0 }) {
return; }
346 DebugConsole.ThrowError(
"Failed to bind buffer to source: " + debugName +
", " +
Al.
GetErrorString(alError), appendStackTrace:
true);
354 DebugConsole.ThrowError(
"Failed to replay source: " + debugName +
", " +
Al.
GetErrorString(alError), appendStackTrace:
true);
362 DebugConsole.ThrowError(
"Failed to reset playback position: " + debugName +
", " +
Al.
GetErrorString(alError), appendStackTrace:
true);
368 private float streamAmplitude;
377 if (alSource == 0) {
return 0.0f; }
385 DebugConsole.ThrowError(
"Failed to get source's playback position: " + debugName +
", " +
Al.
GetErrorString(alError), appendStackTrace:
true);
393 Monitor.Enter(mutex);
394 retVal = streamAmplitude;
401 private string category;
404 get {
return category; }
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;
439 get {
return streamSeekPos; }
444 throw new InvalidOperationException(
"Cannot set StreamSeekPos on a non-streaming sound channel.");
446 streamSeekPos = Math.Max(value, 0);
458 return oggSound.MaxStreamSamplePos;
462 private readonly
object mutex;
469 if (
IsStream && !reachedEndSample) {
return true; }
476 DebugConsole.ThrowError(
"Failed to determine playing state from source: " + debugName +
", " +
Al.
GetErrorString(alError), appendStackTrace:
true);
484 public SoundChannel(
Sound sound,
float gain, Vector3? position,
float freqMult,
float near,
float far,
string category,
bool muffle =
false)
488 debugName = sound ==
null ?
489 "SoundChannel (null)" :
490 $
"SoundChannel ({(string.IsNullOrEmpty(sound.Filename) ? "filename empty
" : sound.Filename) })";
495 streamSeekPos = 0; reachedEndSample =
false;
496 buffersToRequeue = 4;
501 mutex =
new object();
508 if (mutex !=
null) { Monitor.Enter(mutex); }
522 throw new Exception(
"Failed to reset source buffer: " + debugName +
", " +
Al.
GetErrorString(alError));
526 if (
Sound.
Buffers is not { AlBuffer: not 0, AlMuffledBuffer: not 0 }) {
return; }
542 throw new Exception(
"Failed to play source: " + debugName +
", " +
Al.
GetErrorString(alError));
552 throw new Exception(
"Failed to reset source buffer: " + debugName +
", " +
Al.
GetErrorString(alError));
559 throw new Exception(
"Failed to set stream looping state: " + debugName +
", " +
Al.
GetErrorString(alError));
562 streamShortBuffer =
new short[STREAM_BUFFER_SIZE];
564 streamBuffers =
new uint[4];
565 unqueuedBuffers =
new uint[4];
566 streamBufferAmplitudes =
new float[4];
567 for (
int i = 0; i < 4; i++)
574 throw new Exception(
"Failed to generate stream buffers: " + debugName +
", " +
Al.
GetErrorString(alError));
579 throw new Exception(
"Generated streamBuffer[" + i.ToString() +
"] is invalid! " + debugName);
595 if (mutex !=
null) { Monitor.Exit(mutex); }
634 if (mutex !=
null) { Monitor.Enter(mutex); }
641 throw new Exception(
"Failed to stop source: " + debugName +
", " +
Al.
GetErrorString(alError));
652 throw new Exception(
"Failed to stop streamed source: " + debugName +
", " +
Al.
GetErrorString(alError));
655 int buffersToRequeue = 0;
657 buffersToRequeue = 0;
662 throw new Exception(
"Failed to determine processed buffers from streamed source: " + debugName +
", " +
Al.
GetErrorString(alError));
669 throw new Exception(
"Failed to unqueue buffers from streamed source: " + debugName +
", " +
Al.
GetErrorString(alError));
676 throw new Exception(
"Failed to reset buffer for streamed source: " + debugName +
", " +
Al.
GetErrorString(alError));
679 for (
int i = 0; i < 4; i++)
685 throw new Exception(
"Failed to delete streamBuffers[" + i.ToString() +
"] (" + streamBuffers[i].ToString() +
"): " + debugName +
", " +
Al.
GetErrorString(alError));
689 reachedEndSample =
true;
697 throw new Exception(
"Failed to unbind buffer to non-streamed source: " + debugName +
", " +
Al.
GetErrorString(alError));
702 debugName +=
" [DISPOSED]";
707 if (mutex !=
null) { Monitor.Exit(mutex); }
715 if (!
IsStream) {
throw new Exception(
"Called UpdateStream on a non-streamed sound channel!"); }
717 Monitor.Enter(mutex);
718 if (!reachedEndSample)
727 throw new Exception(
"Failed to determine playing state from streamed source: " + debugName +
", " +
Al.
GetErrorString(alError));
734 throw new Exception(
"Failed to determine processed buffers from streamed source: " + debugName +
", " +
Al.
GetErrorString(alError));
741 throw new Exception(
"Failed to unqueue buffers from streamed source: " + debugName +
", " +
Al.
GetErrorString(alError));
744 buffersToRequeue += unqueuedBufferCount;
746 int iterCount = buffersToRequeue;
747 for (
int k = 0; k < iterCount; k++)
749 int index = queueStartIndex;
750 short[] buffer = streamShortBuffer;
752 float readAmplitude = 0.0f;
754 for (
int i = 0; i < Math.Min(readSamples, buffer.Length); i++)
756 float sampleF = ((float)buffer[i]) / ((float)
short.MaxValue);
757 readAmplitude = Math.Max(readAmplitude, Math.Abs(sampleF));
762 if (readSamples <= 0)
764 streamAmplitude *= 0.5f;
766 if (decayTimer > 120)
768 reachedEndSample =
true;
775 voipSound.ApplyFilters(buffer, readSamples);
783 streamSeekPos += readSamples * 2;
784 if (readSamples * 2 < STREAM_BUFFER_SIZE)
792 reachedEndSample =
true;
799 streamBufferAmplitudes[index] = readAmplitude;
806 throw new Exception(
"Failed to assign data to stream buffer: " +
807 Al.
GetErrorString(alError) +
": " + streamBuffers[index].ToString() +
"/" + streamBuffers.Length +
", readSamples: " + readSamples +
", " + debugName);
811 queueStartIndex = (queueStartIndex + 1) % 4;
816 throw new Exception(
"Failed to queue streamBuffer[" + index.ToString() +
"] to stream: " + debugName +
", " +
Al.
GetErrorString(alError));
823 reachedEndSample =
true;
830 streamAmplitude = streamBufferAmplitudes[queueStartIndex];
836 throw new Exception(
"Failed to retrieve stream source state: " + debugName +
", " +
Al.
GetErrorString(alError));
845 throw new Exception(
"Failed to start stream playback: " + debugName +
", " +
Al.
GetErrorString(alError));
850 if (reachedEndSample)
852 streamAmplitude = 0.0f;
857 DebugConsole.ThrowError($
"An exception was thrown when updating a sound stream ({debugName})", e);
const float MaxFrequencyMultiplier
SoundChannel(Sound sound, float gain, Vector3? position, float freqMult, float near, float far, string category, bool muffle=false)
float FrequencyMultiplier
const float MinFrequencyMultiplier
float frequencyMultiplier
bool FadingOutAndDisposing
override string ToString()
float???? CurrentAmplitude
virtual SoundManager.SourcePoolIndex SourcePoolIndex
int MaxSimultaneousInstances
How many instances of the same sound clip can be playing at the same time
virtual void FillAlBuffers()
abstract int FillStreamBuffer(int samplePos, short[] buffer)
readonly bool StreamsReliably
abstract float GetAmplitudeAtPlaybackPos(int playbackPos)
readonly SoundManager Owner
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)
static void DeleteBuffer(uint buffer)
static void GetSourcei(uint sid, int param, out int value)
static bool IsSource(uint sid)
static bool IsBuffer(uint bid)
static void SourceQueueBuffer(uint sid, uint bid)
static void Sourcef(uint sid, int param, float value)
static void DeleteSource(uint source)
static void GenBuffer(out uint buffer)
static void SourcePlay(uint sid)
const int ReferenceDistance
const int BuffersProcessed
static void SourceStop(uint sid)
static string GetErrorString(int error)
static void GenSource(out uint source)
static void Sourcei(uint sid, int param, int value)
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)
static void SourceUnqueueBuffers(uint sid, int numEntries, uint[] bids)