diff --git a/ANX.Framework/Audio/SoundEffect.cs b/ANX.Framework/Audio/SoundEffect.cs index 95aa8829..f0f131e9 100644 --- a/ANX.Framework/Audio/SoundEffect.cs +++ b/ANX.Framework/Audio/SoundEffect.cs @@ -75,9 +75,9 @@ namespace ANX.Framework.Audio #endregion #region Private - private static List fireAndForgetInstances; - private List children; - internal ISoundEffect nativeSoundEffect; + private static readonly List fireAndForgetInstances; + private readonly List children; + internal ISoundEffect NativeSoundEffect; #endregion #region Public @@ -85,7 +85,7 @@ namespace ANX.Framework.Audio { get { - return nativeSoundEffect.Duration; + return NativeSoundEffect.Duration; } } @@ -123,7 +123,7 @@ namespace ANX.Framework.Audio : this() { var creator = GetCreator(); - nativeSoundEffect = creator.CreateSoundEffect(this, stream); + NativeSoundEffect = creator.CreateSoundEffect(this, stream); } public SoundEffect(byte[] buffer, int sampleRate, AudioChannels channels) @@ -135,7 +135,7 @@ namespace ANX.Framework.Audio : this() { var creator = GetCreator(); - nativeSoundEffect = creator.CreateSoundEffect(this, buffer, offset, count, sampleRate, channels, loopStart, loopLength); + NativeSoundEffect = creator.CreateSoundEffect(this, buffer, offset, count, sampleRate, channels, loopStart, loopLength); } ~SoundEffect() @@ -168,16 +168,15 @@ namespace ANX.Framework.Audio #region GetSampleDuration public static TimeSpan GetSampleDuration(int sizeInBytes, int sampleRate, AudioChannels channels) { - float sizeMulBlockAlign = sizeInBytes / ((int)channels * 2); - return TimeSpan.FromMilliseconds((double)(sizeMulBlockAlign * 1000f / (float)sampleRate)); + float sizeMulBlockAlign = (float)sizeInBytes / ((int)channels * 2); + return TimeSpan.FromMilliseconds(sizeMulBlockAlign * 1000f / sampleRate); } #endregion #region GetSampleSizeInBytes - public static int GetSampleSizeInBytes(TimeSpan duration, int sampleRate, - AudioChannels channels) + public static int GetSampleSizeInBytes(TimeSpan duration, int sampleRate, AudioChannels channels) { - int timeMulSamples = (int)(duration.TotalMilliseconds * (double)((float)sampleRate / 1000f)); + int timeMulSamples = (int)(duration.TotalMilliseconds * (sampleRate / 1000f)); return (timeMulSamples + timeMulSamples % (int)channels) * ((int)channels * 2); } #endregion @@ -195,12 +194,12 @@ namespace ANX.Framework.Audio try { - SoundEffectInstance newInstance = new SoundEffectInstance(this, true) - { - Volume = volume, - Pitch = pitch, - Pan = pan, - }; + var newInstance = new SoundEffectInstance(this, true) + { + Volume = volume, + Pitch = pitch, + Pan = pan, + }; children.Add(new WeakReference(newInstance)); @@ -228,26 +227,22 @@ namespace ANX.Framework.Audio return; IsDisposed = true; - nativeSoundEffect.Dispose(); - nativeSoundEffect = null; + NativeSoundEffect.Dispose(); + NativeSoundEffect = null; - List weakRefs = new List(children); + var weakRefs = new List(children); lock (fireAndForgetInstances) { foreach (WeakReference current in weakRefs) { - SoundEffectInstance soundInstance = - current.Target as SoundEffectInstance; + var soundInstance = current.Target as SoundEffectInstance; + if (soundInstance == null) + continue; - if (soundInstance != null) - { - if (soundInstance.IsFireAndForget) - { - fireAndForgetInstances.Remove(soundInstance); - } - soundInstance.Dispose(); - } + if (soundInstance.IsFireAndForget) + fireAndForgetInstances.Remove(soundInstance); + soundInstance.Dispose(); } } diff --git a/ANX.Framework/Audio/SoundEffectInstance.cs b/ANX.Framework/Audio/SoundEffectInstance.cs index 59406b4c..9afce232 100644 --- a/ANX.Framework/Audio/SoundEffectInstance.cs +++ b/ANX.Framework/Audio/SoundEffectInstance.cs @@ -15,8 +15,6 @@ namespace ANX.Framework.Audio public class SoundEffectInstance : IDisposable { #region Private - private SoundEffect parent; - private ISoundEffectInstance nativeInstance; internal bool IsFireAndForget @@ -27,93 +25,47 @@ namespace ANX.Framework.Audio #endregion #region Public - #region IsDisposed - public bool IsDisposed - { - get; - private set; - } - #endregion + public bool IsDisposed { get; private set; } - #region IsLooped - public virtual bool IsLooped - { - get - { - return nativeInstance.IsLooped; - } - set - { - nativeInstance.IsLooped = value; - } - } - #endregion + public virtual bool IsLooped + { + get { return nativeInstance.IsLooped; } + set { nativeInstance.IsLooped = value; } + } - #region Pan - public float Pan - { - get - { - return nativeInstance.Pan; - } - set - { - nativeInstance.Pan = value; - } - } - #endregion + public float Pan + { + get { return nativeInstance.Pan; } + set { nativeInstance.Pan = value; } + } - #region Pitch - public float Pitch - { - get - { - return nativeInstance.Pitch; - } - set - { - nativeInstance.Pitch = value; - } - } - #endregion + public float Pitch + { + get { return nativeInstance.Pitch; } + set { nativeInstance.Pitch = value; } + } - #region State - public SoundState State - { - get - { - return nativeInstance.State; - } - } - #endregion + public SoundState State + { + get { return nativeInstance.State; } + } - #region Volume - public float Volume - { - get - { - return nativeInstance.Volume; - } - set - { - nativeInstance.Volume = value; - } - } - #endregion + public float Volume + { + get { return nativeInstance.Volume; } + set { nativeInstance.Volume = value; } + } #endregion #region Constructor - protected SoundEffectInstance() - { - } + protected SoundEffectInstance() + { + } - internal SoundEffectInstance(SoundEffect setParent, bool setIsFireAndForget) + internal SoundEffectInstance(SoundEffect setParent, bool setIsFireAndForget) { - parent = setParent; IsFireAndForget = setIsFireAndForget; - - nativeInstance = GetCreator().CreateSoundEffectInstance( - setParent.nativeSoundEffect); + nativeInstance = GetCreator().CreateSoundEffectInstance(setParent.NativeSoundEffect); } ~SoundEffectInstance() @@ -132,7 +84,7 @@ namespace ANX.Framework.Audio #region Apply3D public void Apply3D(AudioListener listener, AudioEmitter emitter) { - Apply3D(new AudioListener[] { listener }, emitter); + Apply3D(new[] { listener }, emitter); } public void Apply3D(AudioListener[] listeners, AudioEmitter emitter) diff --git a/Samples/AudioSample/AudioSample.csproj b/Samples/AudioSample/AudioSample.csproj index 7b20dfe9..4c785e9a 100644 --- a/Samples/AudioSample/AudioSample.csproj +++ b/Samples/AudioSample/AudioSample.csproj @@ -91,6 +91,10 @@ {068EB2E9-963C-4E1B-8831-E25011F11FFE} ANX.PlatformSystem.Windows + + {5BE49183-2F6F-4527-AC90-D816911FCF90} + ANX.RenderSystem.Windows.DX10 + {14EF49AB-6D3F-458D-9D5C-D120B86EDD7A} ANX.SoundSystem.OpenAL diff --git a/Samples/AudioSample/Game1.cs b/Samples/AudioSample/Game1.cs index 302b7fdd..2d35a5b8 100644 --- a/Samples/AudioSample/Game1.cs +++ b/Samples/AudioSample/Game1.cs @@ -1,4 +1,3 @@ -using System; using ANX.Framework; using ANX.Framework.Audio; @@ -8,51 +7,45 @@ using ANX.Framework.Audio; namespace AudioSample { - public class Game1 : Game - { - GraphicsDeviceManager graphics; - SoundEffect sound; + public class Game1 : Game + { + private GraphicsDeviceManager graphics; + private SoundEffect sound; + private float timer; + private float duration; - float timer; - float duration; + public Game1() + { + graphics = new GraphicsDeviceManager(this); + Content.RootDirectory = "SampleContent"; + } - public Game1() - { - graphics = new GraphicsDeviceManager(this); - Content.RootDirectory = "SampleContent"; - } + protected override void LoadContent() + { + sound = Content.Load("Sounds\\testsound"); + timer = duration = (float)sound.Duration.TotalSeconds; + } - protected override void Initialize() - { - base.Initialize(); - } + protected override void UnloadContent() + { + } - protected override void LoadContent() - { - sound = Content.Load("Sounds\\testsound"); - timer = duration = (float)sound.Duration.TotalSeconds; - } + protected override void Update(GameTime gameTime) + { + timer += (float)gameTime.ElapsedGameTime.TotalSeconds; + if (timer >= duration) + { + timer -= duration; + sound.Play(1f, 1f, 0f); + } - protected override void UnloadContent() - { - } + base.Update(gameTime); + } - protected override void Update(GameTime gameTime) - { - timer += (float)gameTime.ElapsedGameTime.TotalSeconds; - if (timer >= duration) - { - timer -= duration; - sound.Play(); - } - - base.Update(gameTime); - } - - protected override void Draw(GameTime gameTime) - { - GraphicsDevice.Clear(Color.CornflowerBlue); - base.Draw(gameTime); - } - } + protected override void Draw(GameTime gameTime) + { + GraphicsDevice.Clear(Color.CornflowerBlue); + base.Draw(gameTime); + } + } } diff --git a/Samples/AudioSample/Program.cs b/Samples/AudioSample/Program.cs index a6a3021f..d9a1090b 100644 --- a/Samples/AudioSample/Program.cs +++ b/Samples/AudioSample/Program.cs @@ -11,8 +11,8 @@ namespace AudioSample { static void Main(string[] args) { - AddInSystemFactory.Instance.SetPreferredSystem(AddInType.SoundSystem, "OpenAL"); - //AddInSystemFactory.Instance.SetPreferredSystem(AddInType.SoundSystem, "XAudio"); + //AddInSystemFactory.Instance.SetPreferredSystem(AddInType.SoundSystem, "OpenAL"); + AddInSystemFactory.Instance.SetPreferredSystem(AddInType.SoundSystem, "XAudio"); using (Game1 game = new Game1()) { diff --git a/SoundSystems/ANX.SoundSystem.Windows.XAudio/Creator.cs b/SoundSystems/ANX.SoundSystem.Windows.XAudio/Creator.cs index f11c0de5..9622562a 100644 --- a/SoundSystems/ANX.SoundSystem.Windows.XAudio/Creator.cs +++ b/SoundSystems/ANX.SoundSystem.Windows.XAudio/Creator.cs @@ -13,120 +13,98 @@ using SharpDX.XAudio2; namespace ANX.SoundSystem.Windows.XAudio { public class Creator : ISoundSystemCreator - { + { private XAudio2 device; - private MasteringVoice masteringVoice; + private float distanceScale; + private float dopplerScale; + private float speedOfSound; + internal static MasteringVoice MasteringVoice { get; private set; } #region Public - #region Name public string Name { get { return "XAudio"; } } - #endregion - #region Priority public int Priority { get { return 10; } } - #endregion - #region IsSupported - public bool IsSupported - { - get - { - //TODO: this is just a very basic version of test for support - return OSInformation.IsWindows; - } - } - #endregion + public bool IsSupported + { + get { return OSInformation.IsWindows; } + } - public float DistanceScale - { - get - { - return 1f; - //throw new NotImplementedException(); - } - set - { - //throw new NotImplementedException(); - } + public float DistanceScale + { + get { return distanceScale; } + set + { + distanceScale = value; + // TODO: actually set the parameter to XAudio + } } public float DopplerScale - { - get - { - return 1f; - //throw new NotImplementedException(); - } - set - { - //throw new NotImplementedException(); - } + { + get { return dopplerScale; } + set + { + dopplerScale = value; + // TODO: actually set the parameter to XAudio + } } - public float MasterVolume - { - get - { - return masteringVoice.Volume; - //throw new NotImplementedException(); - } - set - { - masteringVoice.SetVolume(value, 0); - //throw new NotImplementedException(); - } - } + public float MasterVolume + { + get { return MasteringVoice.Volume; } + set { MasteringVoice.SetVolume(value, 0); } + } - public float SpeedOfSound + public float SpeedOfSound { - get - { - return 1f; - //throw new NotImplementedException(); - } - set - { - //throw new NotImplementedException(); - } + get { return speedOfSound; } + set + { + speedOfSound = value; + // TODO: actually set the parameter to XAudio + } } #endregion #region Constructor public Creator() - { + { + distanceScale = 1f; + dopplerScale = 1f; + speedOfSound = 343.5f; device = new XAudio2(); - masteringVoice = new MasteringVoice(device); - } + MasteringVoice = new MasteringVoice(device, XAudio2.DefaultChannels, XAudio2.DefaultSampleRate); + } - ~Creator() - { - if (masteringVoice != null) - { - masteringVoice.Dispose(); - masteringVoice = null; - } + ~Creator() + { + if (MasteringVoice != null) + MasteringVoice.Dispose(); - if (device != null) - { - device.Dispose(); - device = null; - } - } - #endregion + if (device != null) + device.Dispose(); + + MasteringVoice = null; + device = null; + } + #endregion public IAudioListener CreateAudioListener() - { + { + PreventSystemChange(); throw new NotImplementedException(); } public IAudioEmitter CreateAudioEmitter() - { + { + PreventSystemChange(); throw new NotImplementedException(); } @@ -154,21 +132,24 @@ namespace ANX.SoundSystem.Windows.XAudio #endregion public IMicrophone CreateMicrophone(Microphone managedMicrophone) - { + { + PreventSystemChange(); throw new NotImplementedException(); } public ReadOnlyCollection GetAllMicrophones() - { + { + PreventSystemChange(); throw new NotImplementedException(); } public int GetDefaultMicrophone(ReadOnlyCollection allMicrophones) - { + { + PreventSystemChange(); throw new NotImplementedException(); } - private void PreventSystemChange() + private static void PreventSystemChange() { AddInSystemFactory.Instance.PreventSystemChange(AddInType.SoundSystem); } diff --git a/SoundSystems/ANX.SoundSystem.Windows.XAudio/XAudioSoundEffect.cs b/SoundSystems/ANX.SoundSystem.Windows.XAudio/XAudioSoundEffect.cs index ceb20be3..44d49218 100644 --- a/SoundSystems/ANX.SoundSystem.Windows.XAudio/XAudioSoundEffect.cs +++ b/SoundSystems/ANX.SoundSystem.Windows.XAudio/XAudioSoundEffect.cs @@ -2,8 +2,8 @@ using System.IO; using ANX.Framework.Audio; using ANX.Framework.NonXNA.SoundSystem; -using SharpDX.XAudio2; using SharpDX.Multimedia; +using SharpDX.XAudio2; // This file is part of the ANX.Framework created by the // "ANX.Framework developer group" and released under the Ms-PL license. @@ -14,38 +14,35 @@ namespace ANX.SoundSystem.Windows.XAudio public class XAudioSoundEffect : ISoundEffect { #region Private - internal SoundEffect parent; + internal SoundEffect Parent; private TimeSpan duration; - internal WaveFormat waveFormat; - internal AudioBuffer audioBuffer; + internal WaveFormat WaveFormat; + internal AudioBuffer AudioBuffer; internal uint[] DecodedPacketsInfo; #endregion #region Public - public TimeSpan Duration - { - get - { - return duration; - } - } - #endregion + public TimeSpan Duration + { + get { return duration; } + } + #endregion #region Constructor internal XAudioSoundEffect(SoundEffect setParent, Stream stream) { - parent = setParent; + Parent = setParent; CreateFromStream(stream); } internal XAudioSoundEffect(SoundEffect setParent, byte[] buffer, int offset, int count, int sampleRate, AudioChannels channels, int loopStart, int loopLength) { - parent = setParent; + Parent = setParent; - using (MemoryStream stream = new MemoryStream()) + using (var stream = new MemoryStream()) { - BinaryWriter writer = new BinaryWriter(stream); + var writer = new BinaryWriter(stream); writer.Write(buffer, offset, count); stream.Position = 0; CreateFromStream(stream); @@ -62,16 +59,16 @@ namespace ANX.SoundSystem.Windows.XAudio private void CreateFromStream(Stream stream) { var soundStream = new SoundStream(stream); - waveFormat = soundStream.Format; - audioBuffer = new AudioBuffer + WaveFormat = soundStream.Format; + AudioBuffer = new AudioBuffer { Stream = soundStream.ToDataStream(), AudioBytes = (int)stream.Length, Flags = BufferFlags.EndOfStream }; - float sizeMulBlockAlign = soundStream.Length / (waveFormat.Channels * 2); - duration = TimeSpan.FromMilliseconds((double)(sizeMulBlockAlign * 1000f / (float)waveFormat.SampleRate)); + float sizeMulBlockAlign = (float)soundStream.Length / (WaveFormat.Channels * 2); + duration = TimeSpan.FromMilliseconds(sizeMulBlockAlign * 1000f / WaveFormat.SampleRate); DecodedPacketsInfo = soundStream.DecodedPacketsInfo; @@ -82,8 +79,8 @@ namespace ANX.SoundSystem.Windows.XAudio #region Dispose public void Dispose() { - waveFormat = null; - audioBuffer = null; + WaveFormat = null; + AudioBuffer = null; } #endregion } diff --git a/SoundSystems/ANX.SoundSystem.Windows.XAudio/XAudioSoundEffectInstance.cs b/SoundSystems/ANX.SoundSystem.Windows.XAudio/XAudioSoundEffectInstance.cs index e74b5523..ba2ccb0b 100644 --- a/SoundSystems/ANX.SoundSystem.Windows.XAudio/XAudioSoundEffectInstance.cs +++ b/SoundSystems/ANX.SoundSystem.Windows.XAudio/XAudioSoundEffectInstance.cs @@ -1,4 +1,5 @@ using System; +using ANX.Framework; using ANX.Framework.Audio; using ANX.Framework.NonXNA.SoundSystem; using SharpDX.XAudio2; @@ -9,147 +10,181 @@ using SharpDX.XAudio2; namespace ANX.SoundSystem.Windows.XAudio { - public class XAudioSoundEffectInstance : ISoundEffectInstance - { - #region Private - private XAudioSoundEffect parent; + public class XAudioSoundEffectInstance : ISoundEffectInstance + { + #region Private + private SourceVoice source; + private float currentPitch; + private float currentPan; + private bool currentIsLooped; + private readonly XAudioSoundEffect parent; + private float[] panMatrix; + #endregion - private SourceVoice source; - #endregion + #region Public + public bool IsLooped + { + get { return currentIsLooped; } + set + { + currentIsLooped = value; + // TODO: set real parameter + if(value) + throw new NotImplementedException("IsLooped is currently not implemented for XAudio!"); + } + } - #region Public - public bool IsLooped - { - get - { - return false; - //throw new NotImplementedException(); - } - set - { - //throw new NotImplementedException(); - } - } + public float Pan + { + get { return currentPan; } + set + { + currentPan = MathHelper.Clamp(value, -1f, 1f); + UpdateSourcePan(); + } + } - public float Pan - { - get - { - return 0f; - //throw new NotImplementedException(); - } - set - { - //throw new NotImplementedException(); - } - } + public float Pitch + { + get { return currentPitch; } + set + { + currentPitch = value; + source.SetFrequencyRatio(value); + // TODO: pitch <= 1 is working, but greater isn't + if (value > 1f) + throw new NotImplementedException("Pitch greater than 1f is currently not implemented for XAudio!"); + } + } - public float Pitch - { - get - { - return 1f; - //throw new NotImplementedException(); - } - set - { - //throw new NotImplementedException(); - } - } + public SoundState State { get; private set; } - public SoundState State - { - get; - private set; - } + public float Volume + { + get { return source.Volume; } + set { source.SetVolume(value, 0); } + } + #endregion - public float Volume - { - get - { - return source.Volume; - } - set - { - source.SetVolume(value, 0); - } - } - #endregion + #region Constructor + internal XAudioSoundEffectInstance(XAudio2 device, XAudioSoundEffect setParent) + { + parent = setParent; + currentIsLooped = false; + currentPan = 0f; + currentPitch = 1f; + State = SoundState.Stopped; + source = new SourceVoice(device, setParent.WaveFormat); + source.SubmitSourceBuffer(setParent.AudioBuffer, setParent.DecodedPacketsInfo); + } + #endregion - #region Constructor - internal XAudioSoundEffectInstance(XAudio2 device, XAudioSoundEffect setParent) - { - parent = setParent; + #region Play + public void Play() + { + if (State == SoundState.Playing) + return; - State = SoundState.Stopped; + State = SoundState.Playing; + source.Start(); + } + #endregion - var sourceVoice = new SourceVoice(device, setParent.waveFormat); - sourceVoice.SubmitSourceBuffer(setParent.audioBuffer, setParent.DecodedPacketsInfo); - sourceVoice.Start(); - } - #endregion + #region Pause + public void Pause() + { + State = SoundState.Paused; + } + #endregion - #region Play - public void Play() - { - if (State != SoundState.Playing) - { - State = SoundState.Playing; - source.Start(); - } - } - #endregion + #region Stop + public void Stop(bool immediate) + { + if (State == SoundState.Stopped) + return; - #region Pause - public void Pause() - { - if (State != SoundState.Paused) - { - State = SoundState.Paused; - } - } - #endregion + if (immediate == false) + return; - #region Stop - public void Stop(bool immediate) - { - if (State == SoundState.Stopped) - return; + State = SoundState.Stopped; + source.Stop(); + } + #endregion - if (immediate) - { - State = SoundState.Stopped; - source.Stop(); - } - } - #endregion + #region Resume + public void Resume() + { + State = SoundState.Playing; + } + #endregion - #region Resume - public void Resume() - { - if (State != SoundState.Playing) - { - State = SoundState.Playing; - } - } - #endregion + #region UpdateSourcePan (TODO) + private void UpdateSourcePan() + { + var sourceChannelCount = parent.WaveFormat.Channels; + var destinationChannelCount = Creator.MasteringVoice.VoiceDetails.InputChannelCount; + InitializePanMatrix(destinationChannelCount); - #region Apply3D (TODO) - public void Apply3D(AudioListener[] listeners, AudioEmitter emitter) - { - throw new NotImplementedException(); - } - #endregion + var leftPanValue = 1f - currentPan; + var rightPanValue = 1f + currentPan; + panMatrix[0] = leftPanValue; + panMatrix[1] = rightPanValue; + + // TODO: get the channel mask which is strangely only available on Windows8 + //switch (Creator.MasteringVoice.ChannelMask) + //{ + // case (int)Speakers.Quad: + // panMatrix[2] = leftPanValue; + // panMatrix[3] = rightPanValue; + // break; - #region Dispose - public void Dispose() - { - if (source != null) - { - source.Dispose(); - source = null; - } - } - #endregion - } + // case (int)Speakers.FourPointOne: + // panMatrix[3] = leftPanValue; + // panMatrix[4] = rightPanValue; + // break; + + // case (int)Speakers.FivePointOne: + // case (int)Speakers.SevenPointOne: + // case (int)Speakers.FivePointOneSurround: + // panMatrix[4] = leftPanValue; + // panMatrix[5] = rightPanValue; + // break; + + // case (int)Speakers.SevenPointOneSurround: + // panMatrix[4] = panMatrix[6] = leftPanValue; + // panMatrix[5] = panMatrix[7] = rightPanValue; + // break; + //} + + source.SetOutputMatrix(sourceChannelCount, destinationChannelCount, panMatrix); + } + #endregion + + #region InitializePanMatrix + private void InitializePanMatrix(int size) + { + if (panMatrix == null || panMatrix.Length < size) + panMatrix = new float[Math.Max(size, 8)]; + + for (var index = 0; index < panMatrix.Length; index++) + panMatrix[index] = 1f; + } + #endregion + + #region Apply3D (TODO) + public void Apply3D(AudioListener[] listeners, AudioEmitter emitter) + { + throw new NotImplementedException(); + } + #endregion + + #region Dispose + public void Dispose() + { + if (source != null) + source.Dispose(); + source = null; + } + #endregion + } }