diff --git a/ANX.Framework/Audio/AudioCategory.cs b/ANX.Framework/Audio/AudioCategory.cs index b226e49e..3a2ad8ea 100644 --- a/ANX.Framework/Audio/AudioCategory.cs +++ b/ANX.Framework/Audio/AudioCategory.cs @@ -56,7 +56,7 @@ namespace ANX.Framework.Audio #region ToString public override string ToString() { - throw new NotImplementedException(); + return Name; } #endregion diff --git a/ANX.Framework/Audio/AudioEngine.cs b/ANX.Framework/Audio/AudioEngine.cs index 7a45a055..7a88aa3b 100644 --- a/ANX.Framework/Audio/AudioEngine.cs +++ b/ANX.Framework/Audio/AudioEngine.cs @@ -20,10 +20,8 @@ namespace ANX.Framework.Audio #region Public public bool IsDisposed { - get - { - throw new NotImplementedException(); - } + get; + private set; } public ReadOnlyCollection RendererDetails @@ -41,8 +39,7 @@ namespace ANX.Framework.Audio throw new NotImplementedException(); } - public AudioEngine(string settingsFile, TimeSpan lookAheadTime, - string rendererId) + public AudioEngine(string settingsFile, TimeSpan lookAheadTime, string rendererId) { throw new NotImplementedException(); } @@ -84,12 +81,17 @@ namespace ANX.Framework.Audio #region Dispose protected virtual void Dispose(bool disposing) { - throw new NotImplementedException(); + if (IsDisposed == false) + { + IsDisposed = true; + throw new NotImplementedException(); + } } public void Dispose() { - throw new NotImplementedException(); + Dispose(true); + GC.SuppressFinalize(this); } #endregion } diff --git a/ANX.Framework/Audio/Cue.cs b/ANX.Framework/Audio/Cue.cs index bf1a9605..3ed7fb20 100644 --- a/ANX.Framework/Audio/Cue.cs +++ b/ANX.Framework/Audio/Cue.cs @@ -23,10 +23,8 @@ namespace ANX.Framework.Audio public bool IsDisposed { - get - { - throw new NotImplementedException(); - } + get; + private set; } public bool IsPaused @@ -87,6 +85,10 @@ namespace ANX.Framework.Audio #endregion #region Constructor + internal Cue() + { + } + ~Cue() { Dispose(); @@ -145,7 +147,11 @@ namespace ANX.Framework.Audio #region Dispose public void Dispose() { - throw new NotImplementedException(); + if (IsDisposed == false) + { + IsDisposed = true; + throw new NotImplementedException(); + } } #endregion } diff --git a/ANX.Framework/Audio/InstancePlayLimitException.cs b/ANX.Framework/Audio/InstancePlayLimitException.cs index 88a49ebb..02e61ccf 100644 --- a/ANX.Framework/Audio/InstancePlayLimitException.cs +++ b/ANX.Framework/Audio/InstancePlayLimitException.cs @@ -11,7 +11,7 @@ using System.Runtime.InteropServices; namespace ANX.Framework.Audio { #if !WINDOWSMETRO //TODO: search replacement for Win8 - [SerializableAttribute] + [Serializable] #endif public sealed class InstancePlayLimitException : ExternalException { diff --git a/ANX.Framework/Audio/SoundBank.cs b/ANX.Framework/Audio/SoundBank.cs index 5ee87bd9..a9c9240d 100644 --- a/ANX.Framework/Audio/SoundBank.cs +++ b/ANX.Framework/Audio/SoundBank.cs @@ -70,9 +70,7 @@ namespace ANX.Framework.Audio protected virtual void Dispose(bool disposing) { if (IsDisposed) - { return; - } IsDisposed = true; throw new NotImplementedException(); diff --git a/ANX.Framework/Audio/SoundEffect.cs b/ANX.Framework/Audio/SoundEffect.cs index ce8e3f60..6b174aa9 100644 --- a/ANX.Framework/Audio/SoundEffect.cs +++ b/ANX.Framework/Audio/SoundEffect.cs @@ -71,11 +71,9 @@ namespace ANX.Framework.Audio #endregion #region Private - internal ISoundEffect nativeSoundEffect; - private static List fireAndForgetInstances; - - private List children = new List(); + private List children; + internal ISoundEffect nativeSoundEffect; #endregion #region Public @@ -103,13 +101,22 @@ namespace ANX.Framework.Audio #region Constructor static SoundEffect() { + fireAndForgetInstances = new List(); + MasterVolume = 1f; SpeedOfSound = 343.5f; DopplerScale = 1f; DistanceScale = 1f; } + private SoundEffect() + { + children = new List(); + IsDisposed = false; + } + private SoundEffect(Stream stream) + : this() { nativeSoundEffect = GetCreator().CreateSoundEffect(this, stream); } @@ -121,6 +128,7 @@ namespace ANX.Framework.Audio public SoundEffect(byte[] buffer, int offset, int count, int sampleRate, AudioChannels channels, int loopStart, int loopLength) + : this() { nativeSoundEffect = GetCreator().CreateSoundEffect(this, buffer, offset, count, sampleRate, channels, loopStart, loopLength); @@ -154,12 +162,10 @@ namespace ANX.Framework.Audio #endregion #region GetSampleDuration - public static TimeSpan GetSampleDuration(int sizeInBytes, int sampleRate, - AudioChannels channels) + public static TimeSpan GetSampleDuration(int sizeInBytes, int sampleRate, AudioChannels channels) { float sizeMulBlockAlign = sizeInBytes / ((int)channels * 2); - return TimeSpan.FromMilliseconds((double)(sizeMulBlockAlign * 1000f / - (float)sampleRate)); + return TimeSpan.FromMilliseconds((double)(sizeMulBlockAlign * 1000f / (float)sampleRate)); } #endregion @@ -167,10 +173,8 @@ namespace ANX.Framework.Audio public static int GetSampleSizeInBytes(TimeSpan duration, int sampleRate, AudioChannels channels) { - int timeMulSamples = (int)(duration.TotalMilliseconds * - (double)((float)sampleRate / 1000f)); - return (timeMulSamples + timeMulSamples % (int)channels) * - ((int)channels * 2); + int timeMulSamples = (int)(duration.TotalMilliseconds * (double)((float)sampleRate / 1000f)); + return (timeMulSamples + timeMulSamples % (int)channels) * ((int)channels * 2); } #endregion diff --git a/ANX.Framework/Audio/WaveBank.cs b/ANX.Framework/Audio/WaveBank.cs index 07853865..6831a930 100644 --- a/ANX.Framework/Audio/WaveBank.cs +++ b/ANX.Framework/Audio/WaveBank.cs @@ -41,8 +41,7 @@ namespace ANX.Framework.Audio { } - public WaveBank(AudioEngine audioEngine, string streamingWaveBankFilename, - int offset, short packetsize) + public WaveBank(AudioEngine audioEngine, string streamingWaveBankFilename, int offset, short packetsize) { } @@ -62,9 +61,7 @@ namespace ANX.Framework.Audio protected virtual void Dispose(bool disposing) { if (IsDisposed) - { return; - } IsDisposed = true; diff --git a/ANX.Framework/GameHost.cs b/ANX.Framework/GameHost.cs index 1757e29b..c66ac9be 100644 --- a/ANX.Framework/GameHost.cs +++ b/ANX.Framework/GameHost.cs @@ -12,14 +12,6 @@ namespace ANX.Framework { public abstract class GameHost { - //private EventHandler Activated; - //private EventHandler Deactivated; - //private EventHandler Exiting; - //private EventHandler Idle; - //private EventHandler Resume; - //private EventHandler Suspend; - - // Events internal event EventHandler Activated; internal event EventHandler Deactivated; internal event EventHandler Exiting; diff --git a/ANX.Framework/Media/Song.cs b/ANX.Framework/Media/Song.cs index 35c822d2..907d553d 100644 --- a/ANX.Framework/Media/Song.cs +++ b/ANX.Framework/Media/Song.cs @@ -16,17 +16,15 @@ namespace ANX.Framework.Media public string Name { - get - { - throw new NotImplementedException(); - } + get; + private set; } public bool IsRated { get { - throw new NotImplementedException(); + return Rating > 0; } } @@ -95,8 +93,9 @@ namespace ANX.Framework.Media } #region Constructor - private Song() + internal Song(string setName) { + Name = setName; IsDisposed = false; } diff --git a/ANX.Framework/Media/Video.cs b/ANX.Framework/Media/Video.cs index ee70fad1..828d72ca 100644 --- a/ANX.Framework/Media/Video.cs +++ b/ANX.Framework/Media/Video.cs @@ -8,44 +8,47 @@ namespace ANX.Framework.Media { public sealed class Video { + #region Public public TimeSpan Duration { - get - { - throw new NotImplementedException(); - } + get; + private set; } public int Width { - get - { - throw new NotImplementedException(); - } + get; + private set; } public int Height { - get - { - throw new NotImplementedException(); - } + get; + private set; } public float FramesPerSecond { - get - { - throw new NotImplementedException(); - } + get; + private set; } public VideoSoundtrackType VideoSoundtrackType { - get - { - throw new NotImplementedException(); - } + get; + private set; } + #endregion + + #region Constructor + internal Video(int duration, int width, int height, float framesPerSecond, VideoSoundtrackType soundtrackType) + { + Duration = new TimeSpan(0, 0, 0, 0, duration); + Width = width; + Height = height; + FramesPerSecond = framesPerSecond; + VideoSoundtrackType = soundtrackType; + } + #endregion } } diff --git a/SoundSystems/ANX.SoundSystem.OpenAL/ANX.SoundSystem.OpenAL.csproj b/SoundSystems/ANX.SoundSystem.OpenAL/ANX.SoundSystem.OpenAL.csproj index 95a3c918..30df5594 100644 --- a/SoundSystems/ANX.SoundSystem.OpenAL/ANX.SoundSystem.OpenAL.csproj +++ b/SoundSystems/ANX.SoundSystem.OpenAL/ANX.SoundSystem.OpenAL.csproj @@ -70,6 +70,12 @@ + + + + + + diff --git a/SoundSystems/ANX.SoundSystem.OpenAL/Creator.cs b/SoundSystems/ANX.SoundSystem.OpenAL/Creator.cs index 7761c6ba..bb03c82f 100644 --- a/SoundSystems/ANX.SoundSystem.OpenAL/Creator.cs +++ b/SoundSystems/ANX.SoundSystem.OpenAL/Creator.cs @@ -4,6 +4,7 @@ using System.IO; using ANX.Framework.Audio; using ANX.Framework.NonXNA; 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. @@ -14,7 +15,6 @@ namespace ANX.SoundSystem.OpenAL public class Creator : ISoundSystemCreator { #region Public - #region Name public string Name { get @@ -22,9 +22,7 @@ namespace ANX.SoundSystem.OpenAL return "Sound.OpenAL"; } } - #endregion - #region Priority public int Priority { get @@ -32,9 +30,7 @@ namespace ANX.SoundSystem.OpenAL return 100; } } - #endregion - #region IsSupported public bool IsSupported { get @@ -45,9 +41,7 @@ namespace ANX.SoundSystem.OpenAL os == PlatformName.MacOSX; } } - #endregion - #region DistanceScale public float DistanceScale { get @@ -59,23 +53,19 @@ namespace ANX.SoundSystem.OpenAL throw new NotImplementedException(); } } - #endregion - #region DopplerScale public float DopplerScale { get { - throw new NotImplementedException(); + return AL.Get(ALGetFloat.DopplerFactor); } set { - throw new NotImplementedException(); + AL.DopplerFactor(value); } } - #endregion - #region MasterVolume public float MasterVolume { get @@ -87,46 +77,41 @@ namespace ANX.SoundSystem.OpenAL throw new NotImplementedException(); } } - #endregion - #region SpeedOfSound public float SpeedOfSound { get { - throw new NotImplementedException(); + return AL.Get(ALGetFloat.SpeedOfSound); } set { - throw new NotImplementedException(); + AL.SpeedOfSound(value); } } #endregion - #endregion #region CreateSoundEffectInstance - public ISoundEffectInstance CreateSoundEffectInstance( - ISoundEffect nativeSoundEffect) + public ISoundEffectInstance CreateSoundEffectInstance(ISoundEffect nativeSoundEffect) { - AddInSystemFactory.Instance.PreventSystemChange(AddInType.SoundSystem); - return new OpenALSoundEffectInstance((OpenALSoundEffect)nativeSoundEffect); + PreventSystemChange(); + return new OpenALSoundEffectInstance(nativeSoundEffect as OpenALSoundEffect); } #endregion #region CreateSoundEffect public ISoundEffect CreateSoundEffect(SoundEffect parent, Stream stream) { - AddInSystemFactory.Instance.PreventSystemChange(AddInType.SoundSystem); + PreventSystemChange(); return new OpenALSoundEffect(parent, stream); } #endregion #region CreateSoundEffect (TODO) - public ISoundEffect CreateSoundEffect(SoundEffect parent, byte[] buffer, - int offset, int count, int sampleRate, AudioChannels channels, - int loopStart, int loopLength) + public ISoundEffect CreateSoundEffect(SoundEffect parent, byte[] buffer, int offset, int count, int sampleRate, + AudioChannels channels, int loopStart, int loopLength) { - AddInSystemFactory.Instance.PreventSystemChange(AddInType.SoundSystem); + PreventSystemChange(); throw new NotImplementedException(); } #endregion @@ -134,7 +119,7 @@ namespace ANX.SoundSystem.OpenAL #region CreateAudioListener public IAudioListener CreateAudioListener() { - AddInSystemFactory.Instance.PreventSystemChange(AddInType.SoundSystem); + PreventSystemChange(); return new OpenALAudioListener(); } #endregion @@ -142,7 +127,7 @@ namespace ANX.SoundSystem.OpenAL #region CreateAudioEmitter (TODO) public IAudioEmitter CreateAudioEmitter() { - AddInSystemFactory.Instance.PreventSystemChange(AddInType.SoundSystem); + PreventSystemChange(); throw new NotImplementedException(); } #endregion @@ -150,6 +135,7 @@ namespace ANX.SoundSystem.OpenAL #region CreateMicrophone public IMicrophone CreateMicrophone(Microphone managedMicrophone) { + PreventSystemChange(); throw new NotImplementedException(); } #endregion @@ -157,6 +143,7 @@ namespace ANX.SoundSystem.OpenAL #region GetAllMicrophones public ReadOnlyCollection GetAllMicrophones() { + PreventSystemChange(); throw new NotImplementedException(); } #endregion @@ -164,8 +151,14 @@ namespace ANX.SoundSystem.OpenAL #region GetDefaultMicrophone public int GetDefaultMicrophone(ReadOnlyCollection allMicrophones) { + PreventSystemChange(); throw new NotImplementedException(); } #endregion + + private void PreventSystemChange() + { + AddInSystemFactory.Instance.PreventSystemChange(AddInType.SoundSystem); + } } } diff --git a/SoundSystems/ANX.SoundSystem.OpenAL/OpenALSoundEffect.cs b/SoundSystems/ANX.SoundSystem.OpenAL/OpenALSoundEffect.cs index bfc70a1c..a3c1f174 100644 --- a/SoundSystems/ANX.SoundSystem.OpenAL/OpenALSoundEffect.cs +++ b/SoundSystems/ANX.SoundSystem.OpenAL/OpenALSoundEffect.cs @@ -2,6 +2,11 @@ using System.IO; using ANX.Framework.Audio; using ANX.Framework.NonXNA.SoundSystem; +using OpenTK.Audio; + +// 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 { @@ -9,14 +14,20 @@ namespace ANX.SoundSystem.OpenAL { #region Private internal SoundEffect parent; + + private WaveInfo waveInfo; + + private TimeSpan duration; + + internal int bufferHandle; #endregion - #region Public (TODO) + #region Public public TimeSpan Duration { get { - throw new NotImplementedException(); + return duration; } } #endregion @@ -25,13 +36,23 @@ namespace ANX.SoundSystem.OpenAL internal OpenALSoundEffect(SoundEffect setParent, Stream stream) { parent = setParent; + + waveInfo = WaveFile.LoadData(stream); + + float sizeMulBlockAlign = waveInfo.Data.Length / ((int)waveInfo.Channels * 2); + duration = TimeSpan.FromMilliseconds((double)(sizeMulBlockAlign * 1000f / (float)waveInfo.SampleRate)); + + bufferHandle = AL.GenBuffer(); + AL.BufferData(bufferHandle, waveInfo.OpenALFormat, waveInfo.Data, waveInfo.Data.Length, waveInfo.SampleRate); } #endregion - #region Dispose (TODO) + #region Dispose public void Dispose() { - throw new NotImplementedException(); + waveInfo = null; + + AL.DeleteBuffer(bufferHandle); } #endregion } diff --git a/SoundSystems/ANX.SoundSystem.OpenAL/OpenALSoundEffectInstance.cs b/SoundSystems/ANX.SoundSystem.OpenAL/OpenALSoundEffectInstance.cs index 9b414f6d..77b168c0 100644 --- a/SoundSystems/ANX.SoundSystem.OpenAL/OpenALSoundEffectInstance.cs +++ b/SoundSystems/ANX.SoundSystem.OpenAL/OpenALSoundEffectInstance.cs @@ -1,6 +1,11 @@ using System; using ANX.Framework.Audio; using ANX.Framework.NonXNA.SoundSystem; +using OpenTK.Audio; + +// 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 { @@ -8,18 +13,22 @@ namespace ANX.SoundSystem.OpenAL { #region Private private OpenALSoundEffect parent; + + private int handle; #endregion - #region Public (TODO) + #region Public public bool IsLooped { get { - throw new NotImplementedException(); + bool result; + AL.GetSource(handle, ALSourceb.Looping, out result); + return result; } set { - throw new NotImplementedException(); + AL.Source(handle, ALSourceb.Looping, value); } } @@ -39,31 +48,33 @@ namespace ANX.SoundSystem.OpenAL { get { - throw new NotImplementedException(); + float result; + AL.GetSource(handle, ALSourcef.Pitch, out result); + return result; } set { - throw new NotImplementedException(); + AL.Source(handle, ALSourcef.Pitch, value); } } public SoundState State { - get - { - throw new NotImplementedException(); - } + get; + private set; } public float Volume { get { - throw new NotImplementedException(); + float result; + AL.GetSource(handle, ALSourcef.Gain, out result); + return result; } set { - throw new NotImplementedException(); + AL.Source(handle, ALSourcef.Gain, value); } } #endregion @@ -72,38 +83,77 @@ namespace ANX.SoundSystem.OpenAL internal OpenALSoundEffectInstance(OpenALSoundEffect setParent) { parent = setParent; + + State = SoundState.Stopped; + + handle = AL.GenSource(); + AL.Source(handle, ALSourcei.Buffer, parent.bufferHandle); + IsLooped = false; + Pitch = 1f; + Volume = 1f; + // TODO: Pan = 0f; } #endregion + #region Play public void Play() { - throw new NotImplementedException(); + if (State != SoundState.Playing) + { + State = SoundState.Playing; + AL.SourcePlay(handle); + } } + #endregion + #region Pause public void Pause() { - throw new NotImplementedException(); + if (State != SoundState.Paused) + { + State = SoundState.Paused; + AL.SourcePause(handle); + } } + #endregion + #region Stop public void Stop(bool immediate) { - throw new NotImplementedException(); - } + if (State == SoundState.Stopped) + return; + if (immediate) + { + State = SoundState.Stopped; + AL.SourceStop(handle); + } + } + #endregion + + #region Resume public void Resume() { - throw new NotImplementedException(); + if (State != SoundState.Playing) + { + State = SoundState.Playing; + AL.SourcePlay(handle); + } } + #endregion - public void Apply3D(Framework.Audio.AudioListener[] listeners, Framework.Audio.AudioEmitter emitter) + #region Apply3D (TODO) + public void Apply3D(AudioListener[] listeners, AudioEmitter emitter) { throw new NotImplementedException(); } + #endregion - #region Dispose (TODO) + #region Dispose public void Dispose() { - throw new NotImplementedException(); + AL.DeleteSource(handle); + handle = -1; } #endregion } diff --git a/SoundSystems/ANX.SoundSystem.OpenAL/Wave/ALaw.cs b/SoundSystems/ANX.SoundSystem.OpenAL/Wave/ALaw.cs new file mode 100644 index 00000000..8c64a42e --- /dev/null +++ b/SoundSystems/ANX.SoundSystem.OpenAL/Wave/ALaw.cs @@ -0,0 +1,122 @@ +using System; +using System.IO; +using OpenTK.Audio; + +// This file is part of the ANX.Framework and originally taken from +// the AC.AL OpenAL library, released under the MIT License. +// For details see: http://acal.codeplex.com/license + +namespace ANX.SoundSystem.OpenAL +{ + /// + /// http://www.threejacks.com/?q=node/176 + /// + internal static class ALaw + { + #region Decode Table + private static readonly short[] DecodeTable = + { + -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, + -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, + -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, + -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, + -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944, + -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136, + -11008,-10496,-12032,-11520,-8960, -8448, -9984, -9472, + -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568, + -344, -328, -376, -360, -280, -264, -312, -296, + -472, -456, -504, -488, -408, -392, -440, -424, + -88, -72, -120, -104, -24, -8, -56, -40, + -216, -200, -248, -232, -152, -136, -184, -168, + -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, + -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, + -688, -656, -752, -720, -560, -528, -624, -592, + -944, -912, -1008, -976, -816, -784, -880, -848, + 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, + 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, + 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, + 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, + 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, + 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, + 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, + 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, + 344, 328, 376, 360, 280, 264, 312, 296, + 472, 456, 504, 488, 408, 392, 440, 424, + 88, 72, 120, 104, 24, 8, 56, 40, + 216, 200, 248, 232, 152, 136, 184, 168, + 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, + 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, + 688, 656, 752, 720, 560, 528, 624, 592, + 944, 912, 1008, 976, 816, 784, 880, 848 + }; + #endregion + + #region Encode Table + private static readonly byte[] EncodeTable = + { + 1, 1, 2, 2, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7 + }; + #endregion + + #region ConvertToALaw (TODO) + private static void ConvertToALaw(WaveInfo info) + { + //int sign; + //int exponent; + //int mantissa; + //unsigned char compressedByte; + + //sign = ((~sample) >> 8) & 0x80; + //if (!sign) + // sample = (short)-sample; + //if (sample > cClip) + // sample = cClip; + //if (sample >= 256) + //{ + // exponent = (int)ALawCompressTable[(sample >> 8) & 0x7F]; + // mantissa = (sample >> (exponent + 3) ) & 0x0F; + // compressedByte = ((exponent << 4) | mantissa); + //} + //else + //{ + // compressedByte = (unsigned char)(sample >> 4); + //} + //compressedByte ^= (sign ^ 0x55); + //return compressedByte; + } + #endregion + + #region ConvertToPcm + public static void ConvertToPcm(WaveInfo info) + { + info.OpenALFormat = info.Channels == 1 ? + ALFormat.Mono16 : + ALFormat.Stereo16; + MemoryStream destStream = new MemoryStream(); + BinaryWriter destWriter = new BinaryWriter(destStream); + for (int index = 0; index < info.Data.Length; index++) + { + destWriter.Write(DecodeTable[info.Data[index]]); + } + destWriter.Close(); + info.Data = destStream.ToArray(); + destStream.Dispose(); + } + #endregion + } +} diff --git a/SoundSystems/ANX.SoundSystem.OpenAL/Wave/MsAdpcm.cs b/SoundSystems/ANX.SoundSystem.OpenAL/Wave/MsAdpcm.cs new file mode 100644 index 00000000..a4409f13 --- /dev/null +++ b/SoundSystems/ANX.SoundSystem.OpenAL/Wave/MsAdpcm.cs @@ -0,0 +1,141 @@ +using System; +using System.IO; + +// This file is part of the ANX.Framework and originally taken from +// the AC.AL OpenAL library, released under the MIT License. +// For details see: http://acal.codeplex.com/license + +namespace ANX.SoundSystem.OpenAL +{ + /// + /// http://wiki.multimedia.cx/index.php?title=Microsoft_ADPCM + /// + /// http://dslinux.gits.kiev.ua/trunk/lib/audiofile/src/libaudiofile/modules/msadpcm.c + /// + /// http://netghost.narod.ru/gff/vendspec/micriff/ms_riff.txt + /// + internal static class MsAdpcm + { + #region Decoding Tables + private static readonly int[] AdaptationTable = + { + 230, 230, 230, 230, 307, 409, 512, 614, + 768, 614, 512, 409, 307, 230, 230, 230 + }; + + private static readonly int[] AdaptCoeff1 = + { + 256, 512, 0, 192, 240, 460, 392 + }; + + private static readonly int[] AdaptCoeff2 = + { + 0, -256, 0, 64, 0, -208, -232 + }; + #endregion + + #region StateObject (helper class) + private class StateObject + { + /// + /// Indices in the aCoef array to define the predictor + /// used to encode this block. + /// + public byte predicator; + /// + /// Initial Delta value to use. + /// + public short delta; + /// + /// The second sample value of the block. When decoding this will be + /// used as the previous sample to start decoding with. + /// + public short sample1; + /// + /// The first sample value of the block. When decoding this will be + /// used as the previous' previous sample to start decoding with. + /// + public short sample2; + } + #endregion + + #region ConvertToPcm + public static void ConvertToPcm(WaveInfo info) + { + BinaryReader reader = new BinaryReader(new MemoryStream(info.Data)); + MemoryStream result = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(result); + + StateObject first = new StateObject(); + StateObject second = info.Channels > 1 ? new StateObject() : first; + StateObject[] states = { first, second }; + + int numSamples = (info.ExtSamplesPerBlock - 2) * info.Channels; + int numberOfBlocks = info.Data.Length / info.BlockAlign; + for (int blockIndex = 0; blockIndex < numberOfBlocks; blockIndex++) + { + for (int index = 0; index < info.Channels; index++) + { + states[index].predicator = reader.ReadByte(); + } + for (int index = 0; index < info.Channels; index++) + { + states[index].delta = reader.ReadInt16(); + } + for (int index = 0; index < info.Channels; index++) + { + states[index].sample1 = reader.ReadInt16(); + } + for (int index = 0; index < info.Channels; index++) + { + states[index].sample2 = reader.ReadInt16(); + // Write first samples directly from preamble + writer.Write(states[index].sample2); + } + + // Write first samples directly from preamble + for (int index = 0; index < info.Channels; index++) + { + writer.Write(states[index].sample1); + } + + // We decode the samples two at a time + for (int index = 0; index < numSamples; index += 2) + { + byte code = reader.ReadByte(); + DecodeSample(first, code >> 4, writer); + DecodeSample(second, code & 0x0f, writer); + } + } + + reader.Close(); + writer.Close(); + info.Data = result.ToArray(); + result.Dispose(); + } + #endregion + + #region DecodeSample + private static void DecodeSample(StateObject state, int code, + BinaryWriter writer) + { + int linearSample = + ((state.sample1 * AdaptCoeff1[state.predicator]) + + (state.sample2 * AdaptCoeff2[state.predicator])) / 256; + + linearSample = linearSample + (state.delta * + ((code & 0x08) == 0x08 ? (code - 0x10) : code)); + + state.sample2 = state.sample1; + // clamp predictor within signed 16-bit range + state.sample1 = (short)Math.Min(short.MaxValue, + Math.Max(short.MinValue, linearSample)); + + state.delta = (short)Math.Max( + (state.delta * AdaptationTable[code]) / 256, 16); + + writer.Write(state.sample1); + } + #endregion + } +} diff --git a/SoundSystems/ANX.SoundSystem.OpenAL/Wave/MuLaw.cs b/SoundSystems/ANX.SoundSystem.OpenAL/Wave/MuLaw.cs new file mode 100644 index 00000000..a9296e64 --- /dev/null +++ b/SoundSystems/ANX.SoundSystem.OpenAL/Wave/MuLaw.cs @@ -0,0 +1,111 @@ +using System; +using System.IO; +using OpenTK.Audio; + +// This file is part of the ANX.Framework and originally taken from +// the AC.AL OpenAL library, released under the MIT License. +// For details see: http://acal.codeplex.com/license + +namespace ANX.SoundSystem.OpenAL +{ + /// + /// http://www.threejacks.com/?q=node/176 + /// + internal static class MuLaw + { + #region Decode Table + private static readonly short[] DecodeTable = + { + -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956, + -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764, + -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412, + -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316, + -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, + -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, + -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, + -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, + -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, + -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, + -876, -844, -812, -780, -748, -716, -684, -652, + -620, -588, -556, -524, -492, -460, -428, -396, + -372, -356, -340, -324, -308, -292, -276, -260, + -244, -228, -212, -196, -180, -164, -148, -132, + -120, -112, -104, -96, -88, -80, -72, -64, + -56, -48, -40, -32, -24, -16, -8, -1, + 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, + 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, + 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, + 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, + 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, + 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, + 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, + 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, + 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, + 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, + 876, 844, 812, 780, 748, 716, 684, 652, + 620, 588, 556, 524, 492, 460, 428, 396, + 372, 356, 340, 324, 308, 292, 276, 260, + 244, 228, 212, 196, 180, 164, 148, 132, + 120, 112, 104, 96, 88, 80, 72, 64, + 56, 48, 40, 32, 24, 16, 8, 0 + }; + #endregion + + #region Encode Table + private static readonly byte[] EncodeTable = + { + 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 + }; + #endregion + + #region ConvertToMuLaw (TODO) + private static void ConvertToMuLaw(WaveInfo info) + { + //int sign = (sample >> 8) & 0x80; + //if (sign) + // sample = (short)-sample; + //if (sample > cClip) + // sample = cClip; + //sample = (short)(sample + cBias); + //int exponent = (int)MuLawCompressTable[(sample>>7) & 0xFF]; + //int mantissa = (sample >> (exponent+3)) & 0x0F; + //int compressedByte = ~ (sign | (exponent << 4) | mantissa); + + //return (unsigned char)compressedByte; + } + #endregion + + #region ConvertToPcm + public static void ConvertToPcm(WaveInfo info) + { + info.OpenALFormat = info.Channels == 1 ? + ALFormat.Mono16 : + ALFormat.Stereo16; + MemoryStream destStream = new MemoryStream(); + BinaryWriter destWriter = new BinaryWriter(destStream); + for (int index = 0; index < info.Data.Length; index++) + { + destWriter.Write(DecodeTable[info.Data[index]]); + } + destWriter.Close(); + info.Data = destStream.ToArray(); + destStream.Dispose(); + } + #endregion + } +} diff --git a/SoundSystems/ANX.SoundSystem.OpenAL/Wave/WaveFile.cs b/SoundSystems/ANX.SoundSystem.OpenAL/Wave/WaveFile.cs new file mode 100644 index 00000000..c779f9e6 --- /dev/null +++ b/SoundSystems/ANX.SoundSystem.OpenAL/Wave/WaveFile.cs @@ -0,0 +1,235 @@ +using System; +using System.IO; +using ANX.Framework.NonXNA; +using OpenTK.Audio; + +// This file is part of the ANX.Framework and originally taken from +// the AC.AL OpenAL library, released under the MIT License. +// For details see: http://acal.codeplex.com/license + +namespace ANX.SoundSystem.OpenAL +{ + /// + /// This class contains all the loading process of a wave file. + /// + /// http://www-mmsp.ece.mcgill.ca/documents/audioformats/wave/wave.html + /// + /// Chunk information: http://www.sonicspot.com/guide/wavefiles.html + /// + /// Audio Codecs: http://wiki.multimedia.cx/index.php?title=Category:Audio_Codecs + /// + /// http://netghost.narod.ru/gff/vendspec/micriff/ms_riff.txt + /// + /// Most interesting file about many formats: + /// http://icculus.org/SDL_sound/downloads/external_documentation/wavecomp.htm + /// + /// http://sharkysoft.com/archive/lava/docs/javadocs/lava/riff/wave/doc-files/riffwave-content.htm + /// + public static class WaveFile + { + #region LoadData + /// + /// Load all information from a wave file. + /// + /// The stream containing the wave file data. + public static WaveInfo LoadData(Stream stream) + { + WaveInfo result = new WaveInfo(); + + using (BinaryReader reader = new BinaryReader(stream)) + { + if (CheckHeader(reader) == false) + { + throw new FormatException("The provided stream is not a valid WAVE file. Unable to load!"); + } + + #region Read Chunks + while (stream.Position < stream.Length - 8) + { + string identifier = new string(reader.ReadChars(4)); + int chunkLength = reader.ReadInt32(); + + if (stream.Position + chunkLength > stream.Length) + { + break; + } + + int startPosition = (int)reader.BaseStream.Position; + + switch (identifier.ToLower()) + { + case "fmt ": + { + #region Load fmt chunk + result.WaveFormat = (WaveFormat)reader.ReadInt16(); + result.Channels = reader.ReadInt16(); + result.SampleRate = reader.ReadInt32(); + int avgBytesPerSec = reader.ReadInt32(); + result.BlockAlign = reader.ReadInt16(); + result.BitsPerSample = reader.ReadInt16(); + + if (chunkLength > 16) + { + short extensionSize = reader.ReadInt16(); + + if (chunkLength > 18) + { + result.ExtSamplesPerBlock = reader.ReadInt16(); + int speakerPositionMask = reader.ReadInt32(); + WaveFormat extFormat = (WaveFormat)reader.ReadInt16(); + if (result.WaveFormat < 0) + { + result.WaveFormat = extFormat; + } + byte[] subFormat = reader.ReadBytes(14); + } + } + + result.OpenALFormat = (result.Channels == 1 ? + (result.BitsPerSample == 8 ? + ALFormat.Mono8 : + ALFormat.Mono16) : + (result.BitsPerSample == 8 ? + ALFormat.Stereo8 : + ALFormat.Stereo16)); + #endregion + } + break; + + case "fact": + { + #region Load fact chunk + // per channel + int numberOfSamples = reader.ReadInt32(); + // TODO: more + #endregion + } + break; + + case "data": + result.Data = reader.ReadBytes(chunkLength); + break; + } + + // If some chunks are incorrect in data length, we ensure that we + // end up in the right position. + int lengthRead = (int)reader.BaseStream.Position - startPosition; + if (lengthRead != chunkLength) + { + reader.BaseStream.Seek(chunkLength - lengthRead, + SeekOrigin.Current); + } + } + #endregion + } + + if (result.Data == null) + { + Logger.Error("There was no data chunk available. Unable to load!"); + return null; + } + + ConvertFormat(result); + + return result; + } + #endregion + + #region CheckHeader + private static bool CheckHeader(BinaryReader reader) + { + string RIFFmagic = new string(reader.ReadChars(4)); + if(RIFFmagic != "RIFF") + { + return false; + } + + // filesize + reader.ReadInt32(); + + string identifierWAVE = new string(reader.ReadChars(4)); + if(identifierWAVE != "WAVE") + { + return false; + } + + return true; + } + #endregion + + #region ConvertFormat + private static void ConvertFormat(WaveInfo info) + { + switch (info.WaveFormat) + { + case WaveFormat.PCM: + #region Convert 32 to 16 bps (TODO) + //if (info.BitsPerSample == 32) + //{ + // BinaryReader sourceReader = + // new BinaryReader(new MemoryStream(info.Data)); + // MemoryStream destStream = new MemoryStream(); + // BinaryWriter destWriter = new BinaryWriter(destStream); + + // int length = info.Data.Length / 4; + // for (int index = 0; index < length; index++) + // { + // int value = sourceReader.ReadInt32(); + // destWriter.Write((short)(value / 2)); + // } + // sourceReader.Close(); + // destWriter.Close(); + // info.Data = destStream.ToArray(); + // destStream.Dispose(); + //} + #endregion + break; + + case WaveFormat.ALAW: + ALaw.ConvertToPcm(info); + break; + + case WaveFormat.MULAW: + MuLaw.ConvertToPcm(info); + break; + + case WaveFormat.IEEE_FLOAT: + { + #region Convert float to pcm + bool is64BitFloat = info.BitsPerSample == 64; + + BinaryReader sourceReader = + new BinaryReader(new MemoryStream(info.Data)); + MemoryStream destStream = new MemoryStream(); + BinaryWriter destWriter = new BinaryWriter(destStream); + + int length = info.Data.Length / (is64BitFloat ? 8 : 4); + for (int index = 0; index < length; index++) + { + double value = is64BitFloat ? + sourceReader.ReadDouble() : + sourceReader.ReadSingle(); + + destWriter.Write((short)(value * 32767)); + } + + sourceReader.Close(); + destWriter.Close(); + info.Data = destStream.ToArray(); + destStream.Dispose(); + #endregion + } + break; + + case WaveFormat.MS_ADPCM: + MsAdpcm.ConvertToPcm(info); + break; + + default: + throw new NotSupportedException("The WAVE format " + + info.WaveFormat + " is not supported yet. Unable to load!"); + } + } + #endregion + } +} diff --git a/SoundSystems/ANX.SoundSystem.OpenAL/Wave/WaveFormat.cs b/SoundSystems/ANX.SoundSystem.OpenAL/Wave/WaveFormat.cs new file mode 100644 index 00000000..90d14449 --- /dev/null +++ b/SoundSystems/ANX.SoundSystem.OpenAL/Wave/WaveFormat.cs @@ -0,0 +1,33 @@ +using System; + +// This file is part of the ANX.Framework and originally taken from +// the AC.AL OpenAL library, released under the MIT License. +// For details see: http://acal.codeplex.com/license + +namespace ANX.SoundSystem.OpenAL +{ + public enum WaveFormat + { + PCM = 1, + /// + /// http://wiki.multimedia.cx/index.php?title=Microsoft_ADPCM + /// + MS_ADPCM = 2, + IEEE_FLOAT = 3, + /// + /// 8-bit ITU-T G.711 A-law + /// http://hazelware.luggle.com/tutorials/mulawcompression.html + /// + ALAW = 6, + /// + /// 8-bit ITU-T G.711 µ-law + /// http://hazelware.luggle.com/tutorials/mulawcompression.html + /// + MULAW = 7, + + /// + /// Determined by SubFormat + /// + WAVE_FORMAT_EXTENSIBLE = 0xFFFE, + } +} diff --git a/SoundSystems/ANX.SoundSystem.OpenAL/Wave/WaveInfo.cs b/SoundSystems/ANX.SoundSystem.OpenAL/Wave/WaveInfo.cs new file mode 100644 index 00000000..4ed67fda --- /dev/null +++ b/SoundSystems/ANX.SoundSystem.OpenAL/Wave/WaveInfo.cs @@ -0,0 +1,60 @@ +using System; +using OpenTK.Audio; + +// This file is part of the ANX.Framework and originally taken from +// the AC.AL OpenAL library, released under the MIT License. +// For details see: http://acal.codeplex.com/license + +namespace ANX.SoundSystem.OpenAL +{ + public class WaveInfo + { + public WaveFormat WaveFormat + { + get; + internal set; + } + + public byte[] Data + { + get; + internal set; + } + + public ALFormat OpenALFormat + { + get; + internal set; + } + + public int SampleRate + { + get; + internal set; + } + + public short BitsPerSample + { + get; + internal set; + } + + public short BlockAlign + { + get; + internal set; + } + + public int Channels + { + get; + internal set; + } + + public short ExtSamplesPerBlock + { + get; + internal set; + } + } +}