Working on native DynamicSoundEffectInstance implementation in XAudio and OpenAL

This commit is contained in:
SND\AstrorEnales_cp 2012-10-05 20:41:18 +00:00 committed by Konstantin Koch
parent 0417857fd9
commit 3e65589f23
19 changed files with 344 additions and 46 deletions

View File

@ -33,7 +33,7 @@ namespace ANX.Framework.Audio
this.sampleRate = sampleRate;
this.channels = channels;
var creator = AddInSystemFactory.Instance.GetDefaultCreator<ISoundSystemCreator>();
nativeDynamicInstance = creator.CreateDynamicSoundEffectInstance();
nativeDynamicInstance = creator.CreateDynamicSoundEffectInstance(sampleRate, channels);
nativeDynamicInstance.BufferNeeded += OnBufferNeeded;
SetNativeInstance(nativeDynamicInstance);
}

View File

@ -38,6 +38,6 @@ namespace ANX.Framework.NonXNA.SoundSystem
ISong CreateSong(Song parentSong, Uri uri);
ISong CreateSong(Song parentSong, string filepath, int duration);
IDynamicSoundEffectInstance CreateDynamicSoundEffectInstance();
IDynamicSoundEffectInstance CreateDynamicSoundEffectInstance(int sampleRate, AudioChannels channels);
}
}

View File

@ -50,6 +50,6 @@ using System.Runtime.InteropServices;
[assembly: InternalsVisibleTo("ANX.PlatformSystem.Metro")]
[assembly: InternalsVisibleTo("ANX.PlatformSystem.PsVita")]
[assembly: InternalsVisibleTo("ANX.SoundSystem.Windows.XAudio")]
[assembly: InternalsVisibleTo("ANX.SoundSystem.Windows.OpenAL")]
[assembly: InternalsVisibleTo("ANX.SoundSystem.OpenAL")]
[assembly: InternalsVisibleTo("ANX.Tools.XNBInspector")]
[assembly: InternalsVisibleTo("ANX.Framework.Content.Pipeline")]

View File

@ -1,6 +1,6 @@
using System;
using System.IO;
using ANX.Framework.NonXNA;
using ANX.Framework.NonXNA.Development;
using ANX.Framework.NonXNA.PlatformSystem;
// This file is part of the ANX.Framework created by the
@ -9,38 +9,27 @@ using ANX.Framework.NonXNA.PlatformSystem;
namespace ANX.Framework.Storage
{
[Developer("AstrorEnales")]
[TestState(TestStateAttribute.TestState.Untested)]
public class StorageContainer : IDisposable
{
private INativeStorageContainer nativeImplementation;
private readonly INativeStorageContainer nativeImplementation;
#region Public
public event EventHandler<EventArgs> Disposing;
public string DisplayName
{
get;
protected set;
}
public string DisplayName { get; protected set; }
public StorageDevice StorageDevice { get; protected set; }
public bool IsDisposed { get; protected set; }
#endregion
public StorageDevice StorageDevice
{
get;
protected set;
}
public bool IsDisposed
{
get;
protected set;
}
#endregion
internal StorageContainer(StorageDevice device, PlayerIndex player,
string displayName)
internal StorageContainer(StorageDevice device, PlayerIndex player, string displayName)
{
StorageDevice = device;
DisplayName = displayName;
// TODO: player!
nativeImplementation = PlatformSystem.Instance.CreateStorageContainer(this);
}
@ -117,7 +106,8 @@ namespace ANX.Framework.Storage
public void Dispose()
{
Disposing(this, EventArgs.Empty);
if (Disposing != null)
Disposing(this, EventArgs.Empty);
IsDisposed = true;
}
}

View File

@ -1,6 +1,6 @@
using System;
using System.IO;
using ANX.Framework.NonXNA;
using ANX.Framework.NonXNA.Development;
using ANX.Framework.NonXNA.PlatformSystem;
// This file is part of the ANX.Framework created by the
@ -9,6 +9,8 @@ using ANX.Framework.NonXNA.PlatformSystem;
namespace ANX.Framework.Storage
{
[Developer("AstrorEnales")]
[TestState(TestStateAttribute.TestState.Untested)]
public sealed class StorageDevice
{
#region Private

View File

@ -2,6 +2,7 @@
using System;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using ANX.Framework.NonXNA.Development;
#endregion // Using Statements
@ -11,6 +12,8 @@ using System.Runtime.Serialization;
namespace ANX.Framework.Storage
{
[Developer("AstrorEnales")]
[TestState(TestStateAttribute.TestState.Untested)]
public class StorageDeviceNotConnectedException : ExternalException
{
public StorageDeviceNotConnectedException()

View File

@ -42,6 +42,7 @@
<ItemGroup>
<Compile Include="Creator.cs" />
<Compile Include="OpenALAudioListener.cs" />
<Compile Include="OpenALDynamicSoundEffectInstance.cs" />
<Compile Include="OpenALSong.cs" />
<Compile Include="OpenALSoundEffect.cs" />
<Compile Include="OpenALSoundEffectInstance.cs" />

View File

@ -42,6 +42,7 @@
<ItemGroup>
<Compile Include="Creator.cs" />
<Compile Include="OpenALAudioListener.cs" />
<Compile Include="OpenALDynamicSoundEffectInstance.cs" />
<Compile Include="OpenALSong.cs" />
<Compile Include="OpenALSoundEffect.cs" />
<Compile Include="OpenALSoundEffectInstance.cs" />

View File

@ -43,6 +43,7 @@
<ItemGroup>
<Compile Include="Creator.cs" />
<Compile Include="OpenALAudioListener.cs" />
<Compile Include="OpenALDynamicSoundEffectInstance.cs" />
<Compile Include="OpenALSong.cs" />
<Compile Include="OpenALSoundEffect.cs" />
<Compile Include="OpenALSoundEffectInstance.cs" />

View File

@ -44,6 +44,7 @@
<ItemGroup>
<Compile Include="Creator.cs" />
<Compile Include="OpenALAudioListener.cs" />
<Compile Include="OpenALDynamicSoundEffectInstance.cs" />
<Compile Include="OpenALSong.cs" />
<Compile Include="OpenALSoundEffect.cs" />
<Compile Include="OpenALSoundEffectInstance.cs" />

View File

@ -115,7 +115,15 @@ namespace ANX.SoundSystem.OpenAL
PreventSystemChange();
return new OpenALSoundEffect(buffer, offset, count, sampleRate, channels, loopStart, loopLength);
}
#endregion
#endregion
#region CreateDynamicSoundEffectInstance
public IDynamicSoundEffectInstance CreateDynamicSoundEffectInstance(int sampleRate, AudioChannels channels)
{
PreventSystemChange();
return new OpenALDynamicSoundEffectInstance(sampleRate, channels);
}
#endregion
#region CreateAudioListener
public IAudioListener CreateAudioListener()
@ -133,7 +141,7 @@ namespace ANX.SoundSystem.OpenAL
}
#endregion
#region CreateMicrophone
#region CreateMicrophone (TODO)
public IMicrophone CreateMicrophone(Microphone managedMicrophone)
{
PreventSystemChange();
@ -141,22 +149,23 @@ namespace ANX.SoundSystem.OpenAL
}
#endregion
#region GetAllMicrophones
public ReadOnlyCollection<Microphone> GetAllMicrophones()
#region GetAllMicrophones (TODO)
public ReadOnlyCollection<Microphone> GetAllMicrophones()
{
PreventSystemChange();
throw new NotImplementedException();
}
#endregion
#region GetDefaultMicrophone
public int GetDefaultMicrophone(ReadOnlyCollection<Microphone> allMicrophones)
#region GetDefaultMicrophone (TODO)
public int GetDefaultMicrophone(ReadOnlyCollection<Microphone> allMicrophones)
{
PreventSystemChange();
throw new NotImplementedException();
}
#endregion
#region CreateSong
public ISong CreateSong(Song parentSong, Uri uri)
{
PreventSystemChange();
@ -168,12 +177,7 @@ namespace ANX.SoundSystem.OpenAL
PreventSystemChange();
return new OpenALSong(parentSong, filepath, duration);
}
public IDynamicSoundEffectInstance CreateDynamicSoundEffectInstance()
{
PreventSystemChange();
throw new NotImplementedException();
}
#endregion
private static void PreventSystemChange()
{

View File

@ -0,0 +1,154 @@
using System;
using ANX.Framework;
using ANX.Framework.Audio;
using ANX.Framework.NonXNA.Development;
using ANX.Framework.NonXNA.SoundSystem;
using OpenTK.Audio.OpenAL;
// This file is part of the ANX.Framework created by the
// "ANX.Framework developer group" and released under the Ms-PL license.
// For details see: http://anxframework.codeplex.com/license
namespace ANX.SoundSystem.OpenAL
{
[Developer("AstrorEnales")]
public class OpenALDynamicSoundEffectInstance : IDynamicSoundEffectInstance
{
private int source;
private readonly int sampleRate;
private readonly int channels;
private readonly ALFormat format;
public event EventHandler<EventArgs> BufferNeeded;
public int PendingBufferCount
{
get
{
int queueSize;
AL.GetSource(source, ALGetSourcei.BuffersQueued, out queueSize);
return queueSize;
}
}
public bool IsLooped
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
public float Pan
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
public float Pitch
{
get
{
float value;
AL.GetSource(source, ALSourcef.Pitch, out value);
return value;
}
set { AL.Source(source, ALSourcef.Pitch, value); }
}
public SoundState State { get; private set; }
public float Volume
{
get
{
float value;
AL.GetSource(source, ALSourcef.Gain, out value);
return value;
}
set { AL.Source(source, ALSourcef.Gain, value); }
}
public OpenALDynamicSoundEffectInstance(int setSampleRate, AudioChannels setChannels)
{
State = SoundState.Stopped;
sampleRate = setSampleRate;
channels = (int)setChannels;
format = channels == 1 ? ALFormat.Mono16 : ALFormat.Stereo16;
source = AL.GenSource();
FrameworkDispatcher.OnUpdate += Tick;
}
public void SubmitBuffer(byte[] buffer)
{
SubmitBuffer(buffer, 0, buffer.Length);
}
public unsafe void SubmitBuffer(byte[] buffer, int offset, int count)
{
int bufferHandle = AL.GenBuffer();
IntPtr dataHandle;
fixed (byte* ptr = &buffer[0])
dataHandle = (IntPtr)(ptr + offset);
AL.BufferData(bufferHandle, format, dataHandle, count, sampleRate);
AL.SourceQueueBuffer(source, bufferHandle);
}
public void Play()
{
if (State != SoundState.Playing)
AL.SourcePlay(source);
}
public void Pause()
{
if (State != SoundState.Paused)
AL.SourcePause(source);
}
public void Stop(bool immediate)
{
// TODO: handle immediate!
if (State != SoundState.Stopped)
AL.SourceStop(source);
}
public void Resume()
{
Play();
}
private void Tick()
{
if (State != SoundState.Playing)
return;
int buffersProcessed;
AL.GetSource(source, ALGetSourcei.BuffersProcessed, out buffersProcessed);
for (int index = 0; index < buffersProcessed; index++)
{
int buffer = AL.SourceUnqueueBuffer(source);
AL.DeleteBuffer(buffer);
}
if (PendingBufferCount == 0 && BufferNeeded != null)
BufferNeeded(null, EventArgs.Empty);
}
public void Apply3D(AudioListener[] listeners, AudioEmitter emitter)
{
throw new NotImplementedException();
}
public void Dispose()
{
if (source != -1)
{
FrameworkDispatcher.OnUpdate -= Tick;
AL.DeleteSource(source);
}
source = -1;
}
}
}

View File

@ -103,8 +103,7 @@ namespace ANX.SoundSystem.PsVita
#endregion
#region CreateSoundEffectInstance
public ISoundEffectInstance CreateSoundEffectInstance(
ISoundEffect nativeSoundEffect)
public ISoundEffectInstance CreateSoundEffectInstance(ISoundEffect nativeSoundEffect)
{
AddInSystemFactory.Instance.PreventSystemChange(AddInType.SoundSystem);
throw new NotImplementedException();
@ -178,10 +177,10 @@ namespace ANX.SoundSystem.PsVita
throw new NotImplementedException();
}
public IDynamicSoundEffectInstance CreateDynamicSoundEffectInstance()
public IDynamicSoundEffectInstance CreateDynamicSoundEffectInstance(int sampleRate, AudioChannels channels)
{
AddInSystemFactory.Instance.PreventSystemChange(AddInType.SoundSystem);
throw new NotImplementedException();
}
}
}
}

View File

@ -44,6 +44,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Creator.cs" />
<Compile Include="XAudioDynamicSoundEffectInstance.cs" />
<Compile Include="XAudioOggInputStream.cs" />
<Compile Include="XAudioSong.cs" />
<Compile Include="XAudioSoundEffectInstance.cs" />

View File

@ -44,6 +44,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Creator.cs" />
<Compile Include="XAudioDynamicSoundEffectInstance.cs" />
<Compile Include="XAudioOggInputStream.cs" />
<Compile Include="XAudioSong.cs" />
<Compile Include="XAudioSoundEffectInstance.cs" />

View File

@ -45,6 +45,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Creator.cs" />
<Compile Include="XAudioDynamicSoundEffectInstance.cs" />
<Compile Include="XAudioOggInputStream.cs" />
<Compile Include="XAudioSong.cs" />
<Compile Include="XAudioSoundEffectInstance.cs" />

View File

@ -45,6 +45,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Creator.cs" />
<Compile Include="XAudioDynamicSoundEffectInstance.cs" />
<Compile Include="XAudioOggInputStream.cs" />
<Compile Include="XAudioSong.cs" />
<Compile Include="XAudioSoundEffectInstance.cs" />

View File

@ -148,13 +148,15 @@ namespace ANX.SoundSystem.Windows.XAudio
PreventSystemChange();
return new XAudioSoundEffectInstance(device, nativeSoundEffect as XAudioSoundEffect);
}
#endregion
#endregion
public IDynamicSoundEffectInstance CreateDynamicSoundEffectInstance()
#region CreateDynamicSoundEffectInstance
public IDynamicSoundEffectInstance CreateDynamicSoundEffectInstance(int sampleRate, AudioChannels channels)
{
PreventSystemChange();
throw new NotImplementedException();
return new XAudioDynamicSoundEffectInstance(device, sampleRate, channels);
}
#endregion
public IMicrophone CreateMicrophone(Microphone managedMicrophone)
{

View File

@ -0,0 +1,136 @@
using System;
using ANX.Framework.Audio;
using ANX.Framework.NonXNA.Development;
using ANX.Framework.NonXNA.SoundSystem;
using SharpDX;
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.
// For details see: http://anxframework.codeplex.com/license
namespace ANX.SoundSystem.Windows.XAudio
{
/// <summary>
/// DynamicSoundEffectInstance supports only PCM 16-bit mono or stereo data.
/// </summary>
[Developer("AstrorEnales")]
public class XAudioDynamicSoundEffectInstance : IDynamicSoundEffectInstance
{
private SourceVoice source;
private float currentPitch;
public event EventHandler<EventArgs> BufferNeeded;
public int PendingBufferCount
{
get { return source.State.BuffersQueued; }
}
public bool IsLooped
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
public float Pan
{
get { 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 SoundState State { get; private set; }
public float Volume
{
get { return source.Volume; }
set { source.SetVolume(value, 0); }
}
public XAudioDynamicSoundEffectInstance(XAudio2 device, int sampleRate, AudioChannels channels)
{
State = SoundState.Stopped;
currentPitch = 1f;
var format = new WaveFormat(sampleRate, 16, (int)channels);
source = new SourceVoice(device, format, true);
source.BufferEnd += OnBufferEnd;
}
private void OnBufferEnd(IntPtr handle)
{
if (source.State.BuffersQueued == 0 && BufferNeeded != null)
BufferNeeded(null, EventArgs.Empty);
}
public void SubmitBuffer(byte[] buffer)
{
SubmitBuffer(buffer, 0, buffer.Length);
}
public unsafe void SubmitBuffer(byte[] buffer, int offset, int count)
{
var audioBuffer = new AudioBuffer();
IntPtr dataHandle;
fixed (byte* ptr = &buffer[0])
dataHandle = (IntPtr)(ptr + offset);
audioBuffer.Stream = new DataStream(dataHandle, count, false, false);
source.SubmitSourceBuffer(audioBuffer, null);
}
public void Play()
{
if (State != SoundState.Playing)
source.Start();
}
public void Pause()
{
if (State != SoundState.Paused)
source.Stop();
}
public void Stop(bool immediate)
{
// TODO: handle immediate
if (State != SoundState.Stopped)
source.Stop();
}
public void Resume()
{
Play();
}
public void Apply3D(AudioListener[] listeners, AudioEmitter emitter)
{
throw new NotImplementedException();
}
public void Dispose()
{
if (source != null)
{
source.BufferEnd -= OnBufferEnd;
source.DestroyVoice();
source.Dispose();
}
source = null;
}
}
}