// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Runtime.InteropServices;
namespace SharpDX.XAudio2
{
public partial class XAudio2
{
private EngineCallbackImpl engineCallbackImpl;
private IntPtr engineShadowPtr;
///// Constant None.
private static Guid CLSID_XAudio27 = new Guid("5a508685-a254-4fba-9b82-9a24b00306af");
///// Constant None.
private static Guid CLSID_XAudio27_Debug = new Guid("db05ea35-0329-4d4b-a53a-6dead03d3852");
///// Constant None.
private static Guid IID_IXAudio2 = new Guid("8bcf1f58-9fe7-4583-8ac6-e2adc465c8bb");
///
/// Called by XAudio2 just before an audio processing pass begins.
///
public event EventHandler ProcessingPassStart;
///
/// Called by XAudio2 just after an audio processing pass ends.
///
public event EventHandler ProcessingPassEnd;
///
/// Called if a critical system error occurs that requires XAudio2 to be closed down and restarted.
///
public event EventHandler CriticalError;
#if !WINDOWS_UWP
private const uint RPC_E_CHANGED_MODE = 0x80010106;
private const uint COINIT_MULTITHREADED = 0x0;
private const uint COINIT_APARTMENTTHREADED = 0x2;
static XAudio2()
{
if (CoInitializeEx(IntPtr.Zero, COINIT_APARTMENTTHREADED) == RPC_E_CHANGED_MODE)
{
CoInitializeEx(IntPtr.Zero, COINIT_MULTITHREADED);
}
}
[DllImport("ole32.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
private static extern uint CoInitializeEx([In, Optional] IntPtr pvReserved, [In]uint dwCoInit);
#endif
///
/// Initializes a new instance of the class.
///
public XAudio2()
: this(XAudio2Version.Default)
{
}
///
/// Initializes a new instance of the class.
///
/// The requested version.
public XAudio2(XAudio2Version requestedVersion)
: this(XAudio2Flags.None, ProcessorSpecifier.DefaultProcessor, requestedVersion)
{
}
///
/// Initializes a new instance of the class.
///
/// Specify a Debug or Normal XAudio2 instance.
/// The processor specifier.
/// The requestedVersion to use (auto, 2.7 or 2.8).
/// XAudio2 requestedVersion [ + requestedVersion + ] is not installed
public XAudio2(XAudio2Flags flags, ProcessorSpecifier processorSpecifier, XAudio2Version requestedVersion = XAudio2Version.Default)
: base(IntPtr.Zero)
{
var tryVersions = requestedVersion == XAudio2Version.Default
? new[] { XAudio2Version.Version29, XAudio2Version.Version28, XAudio2Version.Version27 }
: new[] { requestedVersion };
foreach (var tryVersion in tryVersions)
{
switch (tryVersion)
{
#if !WINDOWS_UWP
case XAudio2Version.Version27:
Guid clsid = ((int)flags == 1) ? CLSID_XAudio27_Debug : CLSID_XAudio27;
if ((requestedVersion == XAudio2Version.Default || requestedVersion == XAudio2Version.Version27) && Utilities.TryCreateComInstance(clsid, Utilities.CLSCTX.ClsctxInprocServer, IID_IXAudio2, this))
{
SetupVtblFor27();
// Initialize XAudio2
Initialize(0, processorSpecifier);
Version = XAudio2Version.Version27;
}
break;
#endif
case XAudio2Version.Version28:
try
{
XAudio28Functions.XAudio2Create(this, 0, (int)processorSpecifier);
Version = XAudio2Version.Version28;
}
catch (DllNotFoundException) { }
break;
case XAudio2Version.Version29:
try
{
XAudio29Functions.XAudio2Create(this, 0, (int)processorSpecifier);
Version = XAudio2Version.Version29;
}
catch (DllNotFoundException) { }
break;
}
// Early exit if we found a requestedVersion
if (Version != XAudio2Version.Default)
{
break;
}
}
if (Version == XAudio2Version.Default)
{
throw new DllNotFoundException(string.Format("Unable to find XAudio2 dlls for requested versions [{0}], not installed on this machine", requestedVersion == XAudio2Version.Default ? "2.7, 2.8 or 2.9" : requestedVersion.ToString()));
}
// Register engine callback
engineCallbackImpl = new EngineCallbackImpl(this);
engineShadowPtr = EngineShadow.ToIntPtr(engineCallbackImpl);
RegisterForCallbacks(engineCallbackImpl);
}
///
/// Gets the requestedVersion of this XAudio2 instance, once a device has been instanciated.
///
/// The requestedVersion.
public XAudio2Version Version { get; private set; }
// ---------------------------------------------------------------------------------
// Start handling 2.7 requestedVersion here
// ---------------------------------------------------------------------------------
///
/// Setups the VTBL for XAudio 2.7. The 2.7 verions had 3 methods starting at VTBL[3]:
/// - GetDeviceCount
/// - GetDeviceDetails
/// - Initialize
///
private void SetupVtblFor27()
{
RegisterForCallbacks__vtbl_index += 3;
UnregisterForCallbacks__vtbl_index += 3;
CreateSourceVoice__vtbl_index += 3;
CreateSubmixVoice__vtbl_index += 3;
CreateMasteringVoice__vtbl_index += 3;
StartEngine__vtbl_index += 3;
StopEngine__vtbl_index += 3;
CommitChanges__vtbl_index += 3;
GetPerformanceData__vtbl_index += 3;
SetDebugConfiguration__vtbl_index += 3;
}
internal void CheckVersion27()
{
if (Version != XAudio2Version.Version27)
{
throw new InvalidOperationException("This method is only valid on the XAudio 2.7 requestedVersion [Current is: " + Version + "]");
}
}
///
/// No documentation.
///
///
/// GetDeviceCount
/// GetDeviceCount
/// HRESULT IXAudio2::GetDeviceCount([Out] unsigned int* pCount)
public int DeviceCount
{
get
{
CheckVersion27();
int result;
this.GetDeviceCount(out result);
return result;
}
}
///
/// No documentation.
///
/// No documentation.
/// No documentation.
///
/// HRESULT IXAudio2::GetDeviceCount([Out] unsigned int* pCount)
/// IXAudio2::GetDeviceCount
private unsafe void GetDeviceCount(out int countRef)
{
Result result;
fixed (void* ptr = &countRef)
{
result = LocalInterop.CalliGetDeviceCount(this._nativePointer, ptr, *(*(void***)this._nativePointer + 3));
}
result.CheckError();
}
internal unsafe void CreateMasteringVoice27(MasteringVoice masteringVoiceOut, int inputChannels, int inputSampleRate, int flags, int deviceIndex, EffectChain? effectChainRef)
{
IntPtr zero = IntPtr.Zero;
EffectChain value;
if (effectChainRef.HasValue)
{
value = effectChainRef.Value;
}
Result result = LocalInterop.CalliCreateMasteringVoice(this._nativePointer, (void*)&zero, inputChannels, inputSampleRate, flags, deviceIndex, effectChainRef.HasValue ? ((void*)(&value)) : ((void*)IntPtr.Zero), *(*(void***)this._nativePointer + 10));
masteringVoiceOut.NativePointer = zero;
result.CheckError();
}
///
/// Returns information about an audio output device.
///
/// [in] Index of the device to be queried. This value must be less than the count returned by .
/// On success, pointer to an structure that is returned.
/// HRESULT IXAudio2::GetDeviceDetails([None] UINT32 Index,[Out] XAUDIO2_DEVICE_DETAILS* pDeviceDetails)
public SharpDX.XAudio2.DeviceDetails GetDeviceDetails(int index)
{
CheckVersion27();
DeviceDetails details;
GetDeviceDetails(index, out details);
return details;
}
private unsafe void GetDeviceDetails(int index, out DeviceDetails deviceDetailsRef)
{
DeviceDetails.__Native _Native = default(DeviceDetails.__Native);
Result result = LocalInterop.CalliGetDeviceDetails(this._nativePointer, index, &_Native, *(*(void***)this._nativePointer + 4));
deviceDetailsRef = default(DeviceDetails);
deviceDetailsRef.__MarshalFrom(ref _Native);
result.CheckError();
}
private unsafe void Initialize(int flags, ProcessorSpecifier xAudio2Processor)
{
var result = (Result)LocalInterop.CalliInitialize(this._nativePointer, (int)flags, (int)xAudio2Processor, *(*(void***)this._nativePointer + 5));
result.CheckError();
}
// ---------------------------------------------------------------------------------
// End handling 2.7 requestedVersion here
// ---------------------------------------------------------------------------------
///
/// Calculate a decibel from a volume.
///
/// The volume.
/// a dB value
public static float AmplitudeRatioToDecibels(float volume)
{
if (volume == 0f)
return float.MinValue;
return (float)(Math.Log10(volume) * 20);
}
///
/// Calculate radians from a cutoffs frequency.
///
/// The cutoff frequency.
/// The sample rate.
/// radian
public static float CutoffFrequencyToRadians(float cutoffFrequency, int sampleRate)
{
if (((int)cutoffFrequency * 6.0) >= sampleRate)
return 1f;
return (float)(Math.Sin(cutoffFrequency*Math.PI/sampleRate)*2);
}
///
/// Calculate a cutoff frequency from a radian.
///
/// The radians.
/// The sample rate.
/// cutoff frequency
public static float RadiansToCutoffFrequency(float radians, float sampleRate)
{
return (float)((Math.Asin(radians * 0.5) * sampleRate) / Math.PI);
}
///
/// Calculate a volume from a decibel
///
/// a dB value
/// an amplitude value
public static float DecibelsToAmplitudeRatio(float decibels)
{
return (float)Math.Pow(10, decibels / 20);
}
///
/// Calculate semitones from a Frequency ratio
///
/// The frequency ratio.
/// semitones
public static float FrequencyRatioToSemitones(float frequencyRatio)
{
return (float)(Math.Log10(frequencyRatio) * 12 * Math.PI);
}
///
/// Calculate frequency from semitones.
///
/// The semitones.
/// the frequency
public static float SemitonesToFrequencyRatio(float semitones)
{
return (float)Math.Pow(2, semitones / 12);
}
///
/// Atomically applies a set of operations for all pending operations.
///
/// HRESULT IXAudio2::CommitChanges([None] UINT32 OperationSet)
public void CommitChanges()
{
this.CommitChanges(0);
}
protected override void Dispose(bool disposing)
{
if (engineCallbackImpl != null)
UnregisterForCallbacks(engineCallbackImpl);
if (disposing)
{
if (engineCallbackImpl != null)
engineCallbackImpl.Dispose();
}
Version = XAudio2Version.Default;
base.Dispose(disposing);
}
private class EngineCallbackImpl : CallbackBase, EngineCallback
{
XAudio2 XAudio2 { get; set; }
public EngineCallbackImpl(XAudio2 xAudio2)
{
XAudio2 = xAudio2;
}
public void OnProcessingPassStart()
{
EventHandler handler = XAudio2.ProcessingPassStart;
if (handler != null) handler(this, EventArgs.Empty);
}
public void OnProcessingPassEnd()
{
EventHandler handler = XAudio2.ProcessingPassEnd;
if (handler != null) handler(this, EventArgs.Empty);
}
public void OnCriticalError(Result error)
{
EventHandler handler = XAudio2.CriticalError;
if (handler != null) handler(this, new ErrorEventArgs(error));
}
IDisposable ICallbackable.Shadow { get; set; }
}
}
}