Show More
Commit Description:
Tweak calculations....
Commit Description:
Tweak calculations.
Prevent a negative number of visitors and separate visitors from donors.
References:
File last commit:
Show/Diff file:
Action:
FNA/src/Audio/SoundEffect.cs
801 lines | 17.8 KiB | text/x-csharp | CSharpLexer
801 lines | 17.8 KiB | text/x-csharp | CSharpLexer
r0 | #region License | |||
/* FNA - XNA4 Reimplementation for Desktop Platforms | ||||
r690 | * Copyright 2009-2022 Ethan Lee and the MonoGame Team | |||
r0 | * | |||
* Released under the Microsoft Public License. | ||||
* See LICENSE for details. | ||||
*/ | ||||
#endregion | ||||
#region Using Statements | ||||
using System; | ||||
using System.IO; | ||||
using System.Collections.Generic; | ||||
using System.Runtime.InteropServices; | ||||
#endregion | ||||
namespace Microsoft.Xna.Framework.Audio | ||||
{ | ||||
// http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.audio.soundeffect.aspx | ||||
public sealed class SoundEffect : IDisposable | ||||
{ | ||||
#region Public Properties | ||||
public TimeSpan Duration | ||||
{ | ||||
get | ||||
{ | ||||
return TimeSpan.FromSeconds( | ||||
(double) handle.PlayLength / | ||||
r690 | (double) sampleRate | |||
r0 | ); | |||
} | ||||
} | ||||
public bool IsDisposed | ||||
{ | ||||
get; | ||||
private set; | ||||
} | ||||
public string Name | ||||
{ | ||||
get; | ||||
set; | ||||
} | ||||
#endregion | ||||
#region Public Static Properties | ||||
public static float MasterVolume | ||||
{ | ||||
get | ||||
{ | ||||
float result; | ||||
FAudio.FAudioVoice_GetVolume( | ||||
Device().MasterVoice, | ||||
out result | ||||
); | ||||
return result; | ||||
} | ||||
set | ||||
{ | ||||
FAudio.FAudioVoice_SetVolume( | ||||
Device().MasterVoice, | ||||
value, | ||||
0 | ||||
); | ||||
} | ||||
} | ||||
public static float DistanceScale | ||||
{ | ||||
get | ||||
{ | ||||
return Device().CurveDistanceScaler; | ||||
} | ||||
set | ||||
{ | ||||
if (value <= 0.0f) | ||||
{ | ||||
throw new ArgumentOutOfRangeException("value <= 0.0f"); | ||||
} | ||||
Device().CurveDistanceScaler = value; | ||||
} | ||||
} | ||||
public static float DopplerScale | ||||
{ | ||||
get | ||||
{ | ||||
return Device().DopplerScale; | ||||
} | ||||
set | ||||
{ | ||||
if (value < 0.0f) | ||||
{ | ||||
throw new ArgumentOutOfRangeException("value < 0.0f"); | ||||
} | ||||
Device().DopplerScale = value; | ||||
} | ||||
} | ||||
public static float SpeedOfSound | ||||
{ | ||||
get | ||||
{ | ||||
return Device().SpeedOfSound; | ||||
} | ||||
set | ||||
{ | ||||
FAudioContext dev = Device(); | ||||
dev.SpeedOfSound = value; | ||||
FAudio.F3DAudioInitialize( | ||||
dev.DeviceDetails.OutputFormat.dwChannelMask, | ||||
dev.SpeedOfSound, | ||||
dev.Handle3D | ||||
); | ||||
} | ||||
} | ||||
#endregion | ||||
#region Internal Variables | ||||
internal List<WeakReference> Instances = new List<WeakReference>(); | ||||
internal FAudio.FAudioBuffer handle; | ||||
r690 | internal IntPtr formatPtr; | |||
internal ushort channels; | ||||
internal uint sampleRate; | ||||
r0 | internal uint loopStart; | |||
internal uint loopLength; | ||||
#endregion | ||||
#region Public Constructors | ||||
public SoundEffect( | ||||
byte[] buffer, | ||||
int sampleRate, | ||||
AudioChannels channels | ||||
) : this( | ||||
null, | ||||
buffer, | ||||
0, | ||||
buffer.Length, | ||||
r690 | null, | |||
r0 | 1, | |||
(ushort) channels, | ||||
(uint) sampleRate, | ||||
(uint) (sampleRate * ((ushort) channels * 2)), | ||||
(ushort) ((ushort) channels * 2), | ||||
16, | ||||
0, | ||||
0 | ||||
) { | ||||
} | ||||
public SoundEffect( | ||||
byte[] buffer, | ||||
int offset, | ||||
int count, | ||||
int sampleRate, | ||||
AudioChannels channels, | ||||
int loopStart, | ||||
int loopLength | ||||
) : this( | ||||
null, | ||||
buffer, | ||||
offset, | ||||
count, | ||||
r690 | null, | |||
r0 | 1, | |||
(ushort) channels, | ||||
(uint) sampleRate, | ||||
(uint) (sampleRate * ((ushort) channels * 2)), | ||||
(ushort) ((ushort) channels * 2), | ||||
16, | ||||
loopStart, | ||||
loopLength | ||||
) { | ||||
} | ||||
#endregion | ||||
#region Internal Constructor | ||||
r690 | internal unsafe SoundEffect( | |||
r0 | string name, | |||
byte[] buffer, | ||||
int offset, | ||||
int count, | ||||
r690 | byte[] extraData, | |||
r0 | ushort wFormatTag, | |||
ushort nChannels, | ||||
uint nSamplesPerSec, | ||||
uint nAvgBytesPerSec, | ||||
ushort nBlockAlign, | ||||
ushort wBitsPerSample, | ||||
int loopStart, | ||||
int loopLength | ||||
) { | ||||
Device(); | ||||
Name = name; | ||||
r690 | channels = nChannels; | |||
sampleRate = nSamplesPerSec; | ||||
r0 | this.loopStart = (uint) loopStart; | |||
this.loopLength = (uint) loopLength; | ||||
/* Buffer format */ | ||||
r690 | if (extraData == null) | |||
{ | ||||
formatPtr = Marshal.AllocHGlobal( | ||||
Marshal.SizeOf(typeof(FAudio.FAudioWaveFormatEx)) | ||||
); | ||||
} | ||||
else | ||||
{ | ||||
formatPtr = Marshal.AllocHGlobal( | ||||
Marshal.SizeOf(typeof(FAudio.FAudioWaveFormatEx)) + | ||||
extraData.Length | ||||
); | ||||
Marshal.Copy( | ||||
extraData, | ||||
0, | ||||
formatPtr + Marshal.SizeOf(typeof(FAudio.FAudioWaveFormatEx)), | ||||
extraData.Length | ||||
); | ||||
} | ||||
FAudio.FAudioWaveFormatEx* pcm = (FAudio.FAudioWaveFormatEx*) formatPtr; | ||||
pcm->wFormatTag = wFormatTag; | ||||
pcm->nChannels = nChannels; | ||||
pcm->nSamplesPerSec = nSamplesPerSec; | ||||
pcm->nAvgBytesPerSec = nAvgBytesPerSec; | ||||
pcm->nBlockAlign = nBlockAlign; | ||||
pcm->wBitsPerSample = wBitsPerSample; | ||||
pcm->cbSize = (ushort) ((extraData == null) ? 0 : extraData.Length); | ||||
r0 | ||||
/* Easy stuff */ | ||||
handle = new FAudio.FAudioBuffer(); | ||||
handle.Flags = FAudio.FAUDIO_END_OF_STREAM; | ||||
handle.pContext = IntPtr.Zero; | ||||
/* Buffer data */ | ||||
handle.AudioBytes = (uint) count; | ||||
handle.pAudioData = Marshal.AllocHGlobal(count); | ||||
Marshal.Copy( | ||||
buffer, | ||||
offset, | ||||
handle.pAudioData, | ||||
count | ||||
); | ||||
/* Play regions */ | ||||
handle.PlayBegin = 0; | ||||
if (wFormatTag == 1) | ||||
{ | ||||
handle.PlayLength = (uint) ( | ||||
count / | ||||
nChannels / | ||||
(wBitsPerSample / 8) | ||||
); | ||||
} | ||||
else if (wFormatTag == 2) | ||||
{ | ||||
handle.PlayLength = (uint) ( | ||||
count / | ||||
nBlockAlign * | ||||
(((nBlockAlign / nChannels) - 6) * 2) | ||||
); | ||||
} | ||||
r690 | else if (wFormatTag == 0x166) | |||
{ | ||||
FAudio.FAudioXMA2WaveFormatEx* xma2 = (FAudio.FAudioXMA2WaveFormatEx*) formatPtr; | ||||
// dwSamplesEncoded / nChannels / (wBitsPerSample / 8) doesn't always (if ever?) match up. | ||||
handle.PlayLength = xma2->dwPlayLength; | ||||
} | ||||
r0 | ||||
/* Set by Instances! */ | ||||
handle.LoopBegin = 0; | ||||
handle.LoopLength = 0; | ||||
handle.LoopCount = 0; | ||||
} | ||||
#endregion | ||||
#region Destructor | ||||
~SoundEffect() | ||||
{ | ||||
if (Instances.Count > 0) | ||||
{ | ||||
// STOP LEAKING YOUR INSTANCES, ARGH | ||||
GC.ReRegisterForFinalize(this); | ||||
return; | ||||
} | ||||
Dispose(); | ||||
} | ||||
#endregion | ||||
#region Public Methods | ||||
public void Dispose() | ||||
{ | ||||
if (!IsDisposed) | ||||
{ | ||||
/* FIXME: Is it ironic that we're generating | ||||
* garbage with ToArray while cleaning up after | ||||
* the program's leaks? | ||||
* -flibit | ||||
*/ | ||||
foreach (WeakReference instance in Instances.ToArray()) | ||||
{ | ||||
object target = instance.Target; | ||||
if (target != null) | ||||
{ | ||||
(target as IDisposable).Dispose(); | ||||
} | ||||
} | ||||
Instances.Clear(); | ||||
r690 | Marshal.FreeHGlobal(formatPtr); | |||
r0 | Marshal.FreeHGlobal(handle.pAudioData); | |||
IsDisposed = true; | ||||
} | ||||
} | ||||
public bool Play() | ||||
{ | ||||
return Play(1.0f, 0.0f, 0.0f); | ||||
} | ||||
public bool Play(float volume, float pitch, float pan) | ||||
{ | ||||
SoundEffectInstance instance = new SoundEffectInstance(this); | ||||
instance.Volume = volume; | ||||
instance.Pitch = pitch; | ||||
instance.Pan = pan; | ||||
instance.Play(); | ||||
if (instance.State != SoundState.Playing) | ||||
{ | ||||
// Ran out of AL sources, probably. | ||||
instance.Dispose(); | ||||
return false; | ||||
} | ||||
return true; | ||||
} | ||||
public SoundEffectInstance CreateInstance() | ||||
{ | ||||
return new SoundEffectInstance(this); | ||||
} | ||||
#endregion | ||||
#region Public Static Methods | ||||
public static TimeSpan GetSampleDuration( | ||||
int sizeInBytes, | ||||
int sampleRate, | ||||
AudioChannels channels | ||||
) { | ||||
sizeInBytes /= 2; // 16-bit PCM! | ||||
int ms = (int) ( | ||||
(sizeInBytes / (int) channels) / | ||||
(sampleRate / 1000.0f) | ||||
); | ||||
return new TimeSpan(0, 0, 0, 0, ms); | ||||
} | ||||
public static int GetSampleSizeInBytes( | ||||
TimeSpan duration, | ||||
int sampleRate, | ||||
AudioChannels channels | ||||
) { | ||||
return (int) ( | ||||
duration.TotalSeconds * | ||||
sampleRate * | ||||
(int) channels * | ||||
2 // 16-bit PCM! | ||||
); | ||||
} | ||||
public static SoundEffect FromStream(Stream stream) | ||||
{ | ||||
// Sample data | ||||
byte[] data; | ||||
// WaveFormatEx data | ||||
ushort wFormatTag; | ||||
ushort nChannels; | ||||
uint nSamplesPerSec; | ||||
uint nAvgBytesPerSec; | ||||
ushort nBlockAlign; | ||||
ushort wBitsPerSample; | ||||
// ushort cbSize; | ||||
r690 | int samplerLoopStart = 0; | |||
int samplerLoopEnd = 0; | ||||
r0 | using (BinaryReader reader = new BinaryReader(stream)) | |||
{ | ||||
// RIFF Signature | ||||
string signature = new string(reader.ReadChars(4)); | ||||
if (signature != "RIFF") | ||||
{ | ||||
throw new NotSupportedException("Specified stream is not a wave file."); | ||||
} | ||||
reader.ReadUInt32(); // Riff Chunk Size | ||||
string wformat = new string(reader.ReadChars(4)); | ||||
if (wformat != "WAVE") | ||||
{ | ||||
throw new NotSupportedException("Specified stream is not a wave file."); | ||||
} | ||||
// WAVE Header | ||||
string format_signature = new string(reader.ReadChars(4)); | ||||
while (format_signature != "fmt ") | ||||
{ | ||||
reader.ReadBytes(reader.ReadInt32()); | ||||
format_signature = new string(reader.ReadChars(4)); | ||||
} | ||||
int format_chunk_size = reader.ReadInt32(); | ||||
wFormatTag = reader.ReadUInt16(); | ||||
nChannels = reader.ReadUInt16(); | ||||
nSamplesPerSec = reader.ReadUInt32(); | ||||
nAvgBytesPerSec = reader.ReadUInt32(); | ||||
nBlockAlign = reader.ReadUInt16(); | ||||
wBitsPerSample = reader.ReadUInt16(); | ||||
// Reads residual bytes | ||||
if (format_chunk_size > 16) | ||||
{ | ||||
reader.ReadBytes(format_chunk_size - 16); | ||||
} | ||||
// data Signature | ||||
string data_signature = new string(reader.ReadChars(4)); | ||||
while (data_signature.ToLowerInvariant() != "data") | ||||
{ | ||||
reader.ReadBytes(reader.ReadInt32()); | ||||
data_signature = new string(reader.ReadChars(4)); | ||||
} | ||||
if (data_signature != "data") | ||||
{ | ||||
throw new NotSupportedException("Specified wave file is not supported."); | ||||
} | ||||
int waveDataLength = reader.ReadInt32(); | ||||
data = reader.ReadBytes(waveDataLength); | ||||
r690 | ||||
// Scan for other chunks | ||||
while (reader.PeekChar() != -1) | ||||
{ | ||||
char[] chunkIDChars = reader.ReadChars(4); | ||||
if (chunkIDChars.Length < 4) | ||||
{ | ||||
break; // EOL! | ||||
} | ||||
byte[] chunkSizeBytes = reader.ReadBytes(4); | ||||
if (chunkSizeBytes.Length < 4) | ||||
{ | ||||
break; // EOL! | ||||
} | ||||
string chunk_signature = new string(chunkIDChars); | ||||
int chunkDataSize = BitConverter.ToInt32(chunkSizeBytes, 0); | ||||
if (chunk_signature == "smpl") // "smpl", Sampler Chunk Found | ||||
{ | ||||
reader.ReadUInt32(); // Manufacturer | ||||
reader.ReadUInt32(); // Product | ||||
reader.ReadUInt32(); // Sample Period | ||||
reader.ReadUInt32(); // MIDI Unity Note | ||||
reader.ReadUInt32(); // MIDI Pitch Fraction | ||||
reader.ReadUInt32(); // SMPTE Format | ||||
reader.ReadUInt32(); // SMPTE Offset | ||||
uint numSampleLoops = reader.ReadUInt32(); | ||||
int samplerData = reader.ReadInt32(); | ||||
for (int i = 0; i < numSampleLoops; i += 1) | ||||
{ | ||||
reader.ReadUInt32(); // Cue Point ID | ||||
reader.ReadUInt32(); // Type | ||||
int start = reader.ReadInt32(); | ||||
int end = reader.ReadInt32(); | ||||
reader.ReadUInt32(); // Fraction | ||||
reader.ReadUInt32(); // Play Count | ||||
if (i == 0) // Grab loopStart and loopEnd from first sample loop | ||||
{ | ||||
samplerLoopStart = start; | ||||
samplerLoopEnd = end; | ||||
} | ||||
} | ||||
if (samplerData != 0) // Read Sampler Data if it exists | ||||
{ | ||||
reader.ReadBytes(samplerData); | ||||
} | ||||
} | ||||
else // Read unwanted chunk data and try again | ||||
{ | ||||
reader.ReadBytes(chunkDataSize); | ||||
} | ||||
} | ||||
// End scan | ||||
r0 | } | |||
return new SoundEffect( | ||||
null, | ||||
data, | ||||
0, | ||||
data.Length, | ||||
r690 | null, | |||
r0 | wFormatTag, | |||
nChannels, | ||||
nSamplesPerSec, | ||||
nAvgBytesPerSec, | ||||
nBlockAlign, | ||||
wBitsPerSample, | ||||
r690 | samplerLoopStart, | |||
samplerLoopEnd - samplerLoopStart | ||||
r0 | ); | |||
} | ||||
#endregion | ||||
#region FAudio Context | ||||
internal class FAudioContext | ||||
{ | ||||
public static FAudioContext Context = null; | ||||
public readonly IntPtr Handle; | ||||
public readonly byte[] Handle3D; | ||||
public readonly IntPtr MasterVoice; | ||||
public readonly FAudio.FAudioDeviceDetails DeviceDetails; | ||||
public float CurveDistanceScaler; | ||||
public float DopplerScale; | ||||
public float SpeedOfSound; | ||||
public IntPtr ReverbVoice; | ||||
private FAudio.FAudioVoiceSends reverbSends; | ||||
private FAudioContext(IntPtr ctx, uint devices) | ||||
{ | ||||
Handle = ctx; | ||||
uint i; | ||||
for (i = 0; i < devices; i += 1) | ||||
{ | ||||
FAudio.FAudio_GetDeviceDetails( | ||||
Handle, | ||||
i, | ||||
out DeviceDetails | ||||
); | ||||
if ((DeviceDetails.Role & FAudio.FAudioDeviceRole.FAudioDefaultGameDevice) == FAudio.FAudioDeviceRole.FAudioDefaultGameDevice) | ||||
{ | ||||
break; | ||||
} | ||||
} | ||||
if (i == devices) | ||||
{ | ||||
i = 0; /* Oh well. */ | ||||
FAudio.FAudio_GetDeviceDetails( | ||||
Handle, | ||||
i, | ||||
out DeviceDetails | ||||
); | ||||
} | ||||
if (FAudio.FAudio_CreateMasteringVoice( | ||||
Handle, | ||||
out MasterVoice, | ||||
FAudio.FAUDIO_DEFAULT_CHANNELS, | ||||
FAudio.FAUDIO_DEFAULT_SAMPLERATE, | ||||
0, | ||||
i, | ||||
IntPtr.Zero | ||||
) != 0) { | ||||
FAudio.FAudio_Release(ctx); | ||||
r690 | Handle = IntPtr.Zero; | |||
r0 | FNALoggerEXT.LogError( | |||
"Failed to create mastering voice!" | ||||
); | ||||
return; | ||||
} | ||||
CurveDistanceScaler = 1.0f; | ||||
DopplerScale = 1.0f; | ||||
SpeedOfSound = 343.5f; | ||||
Handle3D = new byte[FAudio.F3DAUDIO_HANDLE_BYTESIZE]; | ||||
FAudio.F3DAudioInitialize( | ||||
DeviceDetails.OutputFormat.dwChannelMask, | ||||
SpeedOfSound, | ||||
Handle3D | ||||
); | ||||
Context = this; | ||||
} | ||||
public void Dispose() | ||||
{ | ||||
if (ReverbVoice != IntPtr.Zero) | ||||
{ | ||||
FAudio.FAudioVoice_DestroyVoice(ReverbVoice); | ||||
ReverbVoice = IntPtr.Zero; | ||||
Marshal.FreeHGlobal(reverbSends.pSends); | ||||
} | ||||
r690 | if (MasterVoice != IntPtr.Zero) | |||
{ | ||||
FAudio.FAudioVoice_DestroyVoice(MasterVoice); | ||||
} | ||||
if (Handle != IntPtr.Zero) | ||||
{ | ||||
FAudio.FAudio_Release(Handle); | ||||
} | ||||
r0 | Context = null; | |||
} | ||||
public unsafe void AttachReverb(IntPtr voice) | ||||
{ | ||||
// Only create a reverb voice if they ask for it! | ||||
if (ReverbVoice == IntPtr.Zero) | ||||
{ | ||||
IntPtr reverb; | ||||
FAudio.FAudioCreateReverb(out reverb, 0); | ||||
IntPtr chainPtr; | ||||
chainPtr = Marshal.AllocHGlobal( | ||||
Marshal.SizeOf(typeof(FAudio.FAudioEffectChain)) | ||||
); | ||||
FAudio.FAudioEffectChain* reverbChain = (FAudio.FAudioEffectChain*) chainPtr; | ||||
reverbChain->EffectCount = 1; | ||||
reverbChain->pEffectDescriptors = Marshal.AllocHGlobal( | ||||
Marshal.SizeOf(typeof(FAudio.FAudioEffectDescriptor)) | ||||
); | ||||
FAudio.FAudioEffectDescriptor* reverbDesc = | ||||
(FAudio.FAudioEffectDescriptor*) reverbChain->pEffectDescriptors; | ||||
reverbDesc->InitialState = 1; | ||||
reverbDesc->OutputChannels = (uint) ( | ||||
(DeviceDetails.OutputFormat.Format.nChannels == 6) ? 6 : 1 | ||||
); | ||||
reverbDesc->pEffect = reverb; | ||||
FAudio.FAudio_CreateSubmixVoice( | ||||
Handle, | ||||
out ReverbVoice, | ||||
1, /* Reverb will be omnidirectional */ | ||||
DeviceDetails.OutputFormat.Format.nSamplesPerSec, | ||||
0, | ||||
0, | ||||
IntPtr.Zero, | ||||
chainPtr | ||||
); | ||||
FAudio.FAPOBase_Release(reverb); | ||||
Marshal.FreeHGlobal(reverbChain->pEffectDescriptors); | ||||
Marshal.FreeHGlobal(chainPtr); | ||||
// Defaults based on FAUDIOFX_I3DL2_PRESET_GENERIC | ||||
IntPtr rvbParamsPtr = Marshal.AllocHGlobal( | ||||
Marshal.SizeOf(typeof(FAudio.FAudioFXReverbParameters)) | ||||
); | ||||
FAudio.FAudioFXReverbParameters* rvbParams = (FAudio.FAudioFXReverbParameters*) rvbParamsPtr; | ||||
rvbParams->WetDryMix = 100.0f; | ||||
rvbParams->ReflectionsDelay = 7; | ||||
rvbParams->ReverbDelay = 11; | ||||
rvbParams->RearDelay = FAudio.FAUDIOFX_REVERB_DEFAULT_REAR_DELAY; | ||||
rvbParams->PositionLeft = FAudio.FAUDIOFX_REVERB_DEFAULT_POSITION; | ||||
rvbParams->PositionRight = FAudio.FAUDIOFX_REVERB_DEFAULT_POSITION; | ||||
rvbParams->PositionMatrixLeft = FAudio.FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX; | ||||
rvbParams->PositionMatrixRight = FAudio.FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX; | ||||
rvbParams->EarlyDiffusion = 15; | ||||
rvbParams->LateDiffusion = 15; | ||||
rvbParams->LowEQGain = 8; | ||||
rvbParams->LowEQCutoff = 4; | ||||
rvbParams->HighEQGain = 8; | ||||
rvbParams->HighEQCutoff = 6; | ||||
rvbParams->RoomFilterFreq = 5000f; | ||||
rvbParams->RoomFilterMain = -10f; | ||||
rvbParams->RoomFilterHF = -1f; | ||||
rvbParams->ReflectionsGain = -26.0200005f; | ||||
rvbParams->ReverbGain = 10.0f; | ||||
rvbParams->DecayTime = 1.49000001f; | ||||
rvbParams->Density = 100.0f; | ||||
rvbParams->RoomSize = FAudio.FAUDIOFX_REVERB_DEFAULT_ROOM_SIZE; | ||||
FAudio.FAudioVoice_SetEffectParameters( | ||||
ReverbVoice, | ||||
0, | ||||
rvbParamsPtr, | ||||
(uint) Marshal.SizeOf(typeof(FAudio.FAudioFXReverbParameters)), | ||||
0 | ||||
); | ||||
Marshal.FreeHGlobal(rvbParamsPtr); | ||||
reverbSends = new FAudio.FAudioVoiceSends(); | ||||
reverbSends.SendCount = 2; | ||||
reverbSends.pSends = Marshal.AllocHGlobal( | ||||
2 * Marshal.SizeOf(typeof(FAudio.FAudioSendDescriptor)) | ||||
); | ||||
FAudio.FAudioSendDescriptor* sendDesc = (FAudio.FAudioSendDescriptor*) reverbSends.pSends; | ||||
sendDesc[0].Flags = 0; | ||||
sendDesc[0].pOutputVoice = MasterVoice; | ||||
sendDesc[1].Flags = 0; | ||||
sendDesc[1].pOutputVoice = ReverbVoice; | ||||
} | ||||
// Oh hey here's where we actually attach it | ||||
FAudio.FAudioVoice_SetOutputVoices( | ||||
voice, | ||||
ref reverbSends | ||||
); | ||||
} | ||||
public static void Create() | ||||
{ | ||||
IntPtr ctx; | ||||
try | ||||
{ | ||||
FAudio.FAudioCreate( | ||||
out ctx, | ||||
0, | ||||
FAudio.FAUDIO_DEFAULT_PROCESSOR | ||||
); | ||||
} | ||||
r690 | catch (Exception e) | |||
r0 | { | |||
/* FAudio is missing, bail! */ | ||||
r690 | FNALoggerEXT.LogWarn("FAudio failed to load: " + e.ToString()); | |||
r0 | return; | |||
} | ||||
uint devices; | ||||
FAudio.FAudio_GetDeviceCount( | ||||
ctx, | ||||
out devices | ||||
); | ||||
if (devices == 0) | ||||
{ | ||||
/* No sound cards, bail! */ | ||||
FAudio.FAudio_Release(ctx); | ||||
return; | ||||
} | ||||
r690 | FAudioContext context = new FAudioContext(ctx, devices); | |||
if (context.Handle == IntPtr.Zero) | ||||
{ | ||||
/* Soundcard failed to configure, bail! */ | ||||
context.Dispose(); | ||||
return; | ||||
} | ||||
Context = context; | ||||
r0 | } | |||
} | ||||
r690 | private static readonly object createLock = new object(); | |||
r0 | internal static FAudioContext Device() | |||
{ | ||||
r690 | /* Ideally the device has been made, just return it. */ | |||
r0 | if (FAudioContext.Context != null) | |||
{ | ||||
return FAudioContext.Context; | ||||
} | ||||
r690 | ||||
/* From here on out, it gets weird... */ | ||||
lock (createLock) | ||||
r0 | { | |||
r690 | /* If this trips it's because another thread | |||
* got here first. We do the check above to | ||||
* avoid the mutex lock for the 99.99% of the | ||||
* time where it's not necessary. | ||||
*/ | ||||
if (FAudioContext.Context != null) | ||||
{ | ||||
return FAudioContext.Context; | ||||
} | ||||
/* If you're here, you were the first caller! | ||||
* that, or there genuinely is no hardware and | ||||
* you're about to get a lot more of these. | ||||
*/ | ||||
FAudioContext.Create(); | ||||
if (FAudioContext.Context == null) | ||||
{ | ||||
throw new NoAudioHardwareException(); | ||||
} | ||||
r0 | } | |||
return FAudioContext.Context; | ||||
} | ||||
#endregion | ||||
} | ||||
} | ||||