using System; using System.Threading; using NAudio.CoreAudioApi.Interfaces; using System.Runtime.InteropServices; using NAudio.Wave; namespace NAudio.CoreAudioApi { /// /// Windows Vista CoreAudio AudioClient /// public class AudioClient : IDisposable { IAudioClient audioClientInterface; WaveFormat mixFormat; AudioRenderClient audioRenderClient; AudioCaptureClient audioCaptureClient; internal AudioClient(IAudioClient audioClientInterface) { this.audioClientInterface = audioClientInterface; } /// /// Mix Format, /// Can be called before initialize /// public WaveFormat MixFormat { get { if(mixFormat == null) { IntPtr waveFormatPointer; audioClientInterface.GetMixFormat(out waveFormatPointer); //WaveFormatExtensible waveFormat = new WaveFormatExtensible(44100,32,2); //Marshal.PtrToStructure(waveFormatPointer, waveFormat); WaveFormat waveFormat = WaveFormat.MarshalFromPtr(waveFormatPointer); Marshal.FreeCoTaskMem(waveFormatPointer); mixFormat = waveFormat; return waveFormat; } else { return mixFormat; } } } /// /// Initialize the Audio Client /// /// Share Mode /// Stream Flags /// Buffer Duration /// Periodicity /// Wave Format /// Audio Session GUID (can be null) public void Initialize(AudioClientShareMode shareMode, AudioClientStreamFlags streamFlags, long bufferDuration, long periodicity, WaveFormat waveFormat, Guid audioSessionGuid) { audioClientInterface.Initialize(shareMode, streamFlags, bufferDuration, periodicity, waveFormat, ref audioSessionGuid); // may have changed the mix format so reset it mixFormat = null; } /// /// Gets the buffer size (must initialize first) /// public int BufferSize { get { uint bufferSize; audioClientInterface.GetBufferSize(out bufferSize); return (int) bufferSize; } } /// /// Gets the stream latency (must initialize first) /// public long StreamLatency { get { return audioClientInterface.GetStreamLatency(); } } /// /// Gets the current padding (must initialize first) /// public int CurrentPadding { get { int currentPadding; audioClientInterface.GetCurrentPadding(out currentPadding); return currentPadding; } } /// /// Gets the default device period (can be called before initialize) /// public long DefaultDevicePeriod { get { long defaultDevicePeriod; long minimumDevicePeriod; audioClientInterface.GetDevicePeriod(out defaultDevicePeriod, out minimumDevicePeriod); return defaultDevicePeriod; } } /// /// Gets the minimum device period (can be called before initialize) /// public long MinimumDevicePeriod { get { long defaultDevicePeriod; long minimumDevicePeriod; audioClientInterface.GetDevicePeriod(out defaultDevicePeriod, out minimumDevicePeriod); return minimumDevicePeriod; } } // TODO: GetService: // IID_IAudioCaptureClient // IID_IAudioClock // IID_IAudioSessionControl // IID_IAudioStreamVolume // IID_IChannelAudioVolume // IID_ISimpleAudioVolume /// /// Gets the AudioRenderClient service /// public AudioRenderClient AudioRenderClient { get { if (audioRenderClient == null) { object audioRenderClientInterface; Guid audioRenderClientGuid = new Guid("F294ACFC-3146-4483-A7BF-ADDCA7C260E2"); audioClientInterface.GetService(ref audioRenderClientGuid, out audioRenderClientInterface); audioRenderClient = new AudioRenderClient((IAudioRenderClient)audioRenderClientInterface); } return audioRenderClient; } } /// /// Gets the AudioCaptureClient service /// public AudioCaptureClient AudioCaptureClient { get { if (audioCaptureClient == null) { object audioCaptureClientInterface; Guid audioCaptureClientGuid = new Guid("c8adbd64-e71e-48a0-a4de-185c395cd317"); audioClientInterface.GetService(ref audioCaptureClientGuid, out audioCaptureClientInterface); audioCaptureClient = new AudioCaptureClient((IAudioCaptureClient)audioCaptureClientInterface); } return audioCaptureClient; } } /// /// Determines whether if the specified output format is supported /// /// The share mode. /// The desired format. /// /// true if [is format supported] [the specified share mode]; otherwise, false. /// public bool IsFormatSupported(AudioClientShareMode shareMode, WaveFormat desiredFormat) { WaveFormatExtensible closestMatchFormat; return IsFormatSupported(shareMode, desiredFormat, out closestMatchFormat); } /// /// Determines if the specified output format is supported in shared mode /// /// Share Mode /// Desired Format /// Output The closest match format. /// /// true if [is format supported] [the specified share mode]; otherwise, false. /// public bool IsFormatSupported(AudioClientShareMode shareMode, WaveFormat desiredFormat, out WaveFormatExtensible closestMatchFormat) { int hresult = audioClientInterface.IsFormatSupported(shareMode, desiredFormat, out closestMatchFormat); // S_OK is 0, S_FALSE = 1 if (hresult == 0) { // directly supported return true; } if (hresult == 1) { return false; } else if (hresult == (int)AudioClientErrors.UnsupportedFormat) { return false; } else { Marshal.ThrowExceptionForHR(hresult); } // shouldn't get here throw new NotSupportedException("Unknown hresult " + hresult.ToString()); } /// /// Starts the audio stream /// public void Start() { audioClientInterface.Start(); } /// /// Stops the audio stream. /// public void Stop() { audioClientInterface.Stop(); } /// /// Set the Event Handle for buffer synchro. /// /// The Wait Handle to setup public void SetEventHandle(EventWaitHandle eventWaitHandle) { audioClientInterface.SetEventHandle(eventWaitHandle.SafeWaitHandle.DangerousGetHandle()); } /// /// Resets the audio stream /// Reset is a control method that the client calls to reset a stopped audio stream. /// Resetting the stream flushes all pending data and resets the audio clock stream /// position to 0. This method fails if it is called on a stream that is not stopped /// public void Reset() { audioClientInterface.Reset(); } #region IDisposable Members /// /// Dispose /// public void Dispose() { if (audioClientInterface != null) { if (audioRenderClient != null) { audioRenderClient.Dispose(); audioRenderClient = null; } if (audioCaptureClient != null) { audioCaptureClient.Dispose(); audioCaptureClient = null; } Marshal.ReleaseComObject(audioClientInterface); audioClientInterface = null; GC.SuppressFinalize(this); } } #endregion } }