// 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; } } } }