using NAudio.CoreAudioApi.Interfaces; using System; using System.Globalization; using System.Runtime.InteropServices; namespace NAudio.CoreAudioApi { /// /// Manages the AudioStreamVolume for the . /// public class AudioStreamVolume : IDisposable { IAudioStreamVolume audioStreamVolumeInterface; internal AudioStreamVolume(IAudioStreamVolume audioStreamVolumeInterface) { this.audioStreamVolumeInterface = audioStreamVolumeInterface; } /// /// Verify that the channel index is valid. /// /// /// private void CheckChannelIndex(int channelIndex, string parameter) { int channelCount = ChannelCount; if (channelIndex >= channelCount) { throw new ArgumentOutOfRangeException(parameter, "You must supply a valid channel index < current count of channels: " + channelCount.ToString()); } } /// /// Return the current stream volumes for all channels /// /// An array of volume levels between 0.0 and 1.0 for each channel in the audio stream. public float[] GetAllVolumes() { Marshal.ThrowExceptionForHR(audioStreamVolumeInterface.GetChannelCount(out var channels)); var levels = new float[channels]; Marshal.ThrowExceptionForHR(audioStreamVolumeInterface.GetAllVolumes(channels, levels)); return levels; } /// /// Returns the current number of channels in this audio stream. /// public int ChannelCount { get { Marshal.ThrowExceptionForHR(audioStreamVolumeInterface.GetChannelCount(out var channels)); unchecked { return (int)channels; } } } /// /// Return the current volume for the requested channel. /// /// The 0 based index into the channels. /// The volume level for the channel between 0.0 and 1.0. public float GetChannelVolume(int channelIndex) { CheckChannelIndex(channelIndex, "channelIndex"); uint index; unchecked { index = (uint)channelIndex; } Marshal.ThrowExceptionForHR(audioStreamVolumeInterface.GetChannelVolume(index, out var level)); return level; } /// /// Set the volume level for each channel of the audio stream. /// /// An array of volume levels (between 0.0 and 1.0) one for each channel. /// /// A volume level MUST be supplied for reach channel in the audio stream. /// /// /// Thrown when does not contain elements. /// public void SetAllVolumes(float[] levels) { // Make friendly Net exceptions for common problems: int channelCount = ChannelCount; if (levels == null) { throw new ArgumentNullException(nameof(levels)); } if (levels.Length != channelCount) { throw new ArgumentOutOfRangeException( nameof(levels), String.Format(CultureInfo.InvariantCulture, "SetAllVolumes MUST be supplied with a volume level for ALL channels. The AudioStream has {0} channels and you supplied {1} channels.", channelCount, levels.Length)); } for (int i = 0; i < levels.Length; i++) { float level = levels[i]; if (level < 0.0f) throw new ArgumentOutOfRangeException(nameof(levels), "All volumes must be between 0.0 and 1.0. Invalid volume at index: " + i.ToString()); if (level > 1.0f) throw new ArgumentOutOfRangeException(nameof(levels), "All volumes must be between 0.0 and 1.0. Invalid volume at index: " + i.ToString()); } unchecked { Marshal.ThrowExceptionForHR(audioStreamVolumeInterface.SetAllVoumes((uint)channelCount, levels)); } } /// /// Sets the volume level for one channel in the audio stream. /// /// The 0-based index into the channels to adjust the volume of. /// The volume level between 0.0 and 1.0 for this channel of the audio stream. public void SetChannelVolume(int index, float level) { CheckChannelIndex(index, "index"); if (level < 0.0f) throw new ArgumentOutOfRangeException(nameof(level), "Volume must be between 0.0 and 1.0"); if (level > 1.0f) throw new ArgumentOutOfRangeException(nameof(level), "Volume must be between 0.0 and 1.0"); unchecked { Marshal.ThrowExceptionForHR(audioStreamVolumeInterface.SetChannelVolume((uint)index, level)); } } #region IDisposable Members /// /// Dispose /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Release/cleanup objects during Dispose/finalization. /// /// True if disposing and false if being finalized. protected virtual void Dispose(bool disposing) { if (disposing) { if (audioStreamVolumeInterface != null) { // although GC would do this for us, we want it done now Marshal.ReleaseComObject(audioStreamVolumeInterface); audioStreamVolumeInterface = null; } } } #endregion } }