This commit is contained in:
Regalis
2015-07-31 21:05:55 +03:00
parent 23d847a4ac
commit 85b0cda4ca
181 changed files with 4455 additions and 4073 deletions
@@ -0,0 +1,272 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Xml.Linq;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using Microsoft.Xna.Framework;
using Subsurface.Sounds;
using System.Collections.Generic;
namespace Subsurface
{
public enum DamageSoundType {
None,
StructureBlunt, StructureSlash,
LimbBlunt, LimbSlash, LimbArmor,
Implode }
public struct DamageSound
{
//the range of inflicted damage where the sound can be played
//(10.0f, 30.0f) would be played when the inflicted damage is between 10 and 30
public readonly Vector2 damageRange;
public readonly DamageSoundType damageType;
public readonly Sound sound;
public DamageSound(Sound sound, Vector2 damageRange, DamageSoundType damageType)
{
this.sound = sound;
this.damageRange = damageRange;
this.damageType = damageType;
}
}
public class BackgroundMusic
{
public readonly string file;
public readonly string type;
public readonly Vector2 priorityRange;
public BackgroundMusic(string file, string type, Vector2 priorityRange)
{
this.file = file;
this.type = type;
this.priorityRange = priorityRange;
}
}
static class AmbientSoundManager
{
public static Sound[] flowSounds = new Sound[3];
private const float MusicLerpSpeed = 0.01f;
private static Sound waterAmbience;
private static int waterAmbienceIndex;
private static DamageSound[] damageSounds;
private static BackgroundMusic currentMusic;
private static BackgroundMusic targetMusic;
private static BackgroundMusic[] musicClips;
private static float musicVolume;
private static Sound startDrone;
public static IEnumerable<Status> Init()
{
startDrone = Sound.Load("Content/Sounds/startDrone.ogg");
startDrone.Play();
yield return Status.Running;
waterAmbience = Sound.Load("Content/Sounds/Water/WaterAmbience.ogg");
yield return Status.Running;
flowSounds[0] = Sound.Load("Content/Sounds/Water/FlowSmall.ogg");
yield return Status.Running;
flowSounds[1] = Sound.Load("Content/Sounds/Water/FlowMedium.ogg");
yield return Status.Running;
flowSounds[2] = Sound.Load("Content/Sounds/Water/FlowLarge.ogg");
yield return Status.Running;
XDocument doc = ToolBox.TryLoadXml("Content/Sounds/Sounds.xml");
if (doc == null) yield return Status.Failure;
yield return Status.Running;
var xMusic = doc.Root.Elements("music").ToList();
if (xMusic.Any())
{
musicClips = new BackgroundMusic[xMusic.Count];
int i = 0;
foreach (XElement element in xMusic)
{
string file = ToolBox.GetAttributeString(element, "file", "").ToLower();
string type = ToolBox.GetAttributeString(element, "type", "").ToLower();
Vector2 priority = ToolBox.GetAttributeVector2(element, "priorityrange", new Vector2(0.0f, 100.0f));
musicClips[i] = new BackgroundMusic(file, type, priority);
yield return Status.Running;
i++;
}
}
var xDamageSounds = doc.Root.Elements("damagesound").ToList();
if (xDamageSounds.Any())
{
damageSounds = new DamageSound[xDamageSounds.Count()];
int i = 0;
foreach (XElement element in xDamageSounds)
{
yield return Status.Running;
Sound sound = Sound.Load(ToolBox.GetAttributeString(element, "file", ""));
if (sound == null) continue;
DamageSoundType damageSoundType = DamageSoundType.None;
try
{
damageSoundType = (DamageSoundType)Enum.Parse(typeof(DamageSoundType),
ToolBox.GetAttributeString(element, "damagesoundtype", "None"));
}
catch
{
damageSoundType = DamageSoundType.None;
}
damageSounds[i] = new DamageSound(
sound, ToolBox.GetAttributeVector2(element, "damagerange", new Vector2(0.0f,100.0f)), damageSoundType);
i++;
}
}
yield return Status.Success;
}
public static void Update()
{
UpdateMusic();
if (startDrone!=null)
{
if (!SoundManager.IsPlaying(startDrone.AlBufferId))
{
startDrone.Remove();
startDrone = null;
}
}
float ambienceVolume = 0.5f;
float lowpassHFGain = 1.0f;
if (Character.Controlled != null)
{
AnimController animController = Character.Controlled.AnimController;
if (animController.HeadInWater)
{
ambienceVolume = 0.5f;
ambienceVolume += animController.limbs[0].LinearVelocity.Length();
lowpassHFGain = 0.2f;
}
}
SoundManager.LowPassHFGain = lowpassHFGain;
waterAmbienceIndex = waterAmbience.Loop(waterAmbienceIndex, ambienceVolume);
}
private static void UpdateMusic()
{
if (musicClips == null) return;
Task criticalTask = null;
if (Game1.GameSession!=null)
{
foreach (Task task in Game1.GameSession.taskManager.Tasks)
{
if (criticalTask == null || task.Priority > criticalTask.Priority)
{
criticalTask = task;
}
}
}
List<BackgroundMusic> suitableMusic = null;
if (criticalTask == null)
{
suitableMusic = musicClips.Where(x => x != null && x.type == "default").ToList();
}
else
{
suitableMusic = musicClips.Where(x =>
x != null &&
x.type == criticalTask.MusicType &&
x.priorityRange.X < criticalTask.Priority &&
x.priorityRange.Y > criticalTask.Priority).ToList();
}
if (suitableMusic.Count > 0 && !suitableMusic.Contains(currentMusic))
{
int index = Rand.Int(suitableMusic.Count());
if (currentMusic == null || suitableMusic[index].file != currentMusic.file)
{
targetMusic = suitableMusic[index];
}
}
if (targetMusic == null || currentMusic == null || targetMusic.file != currentMusic.file)
{
musicVolume = MathHelper.Lerp(musicVolume, 0.0f, MusicLerpSpeed);
if (currentMusic != null) Sound.StreamVolume(musicVolume);
if (musicVolume < 0.01f)
{
Sound.StopStream();
if (targetMusic != null) Sound.StartStream(targetMusic.file, musicVolume);
currentMusic = targetMusic;
}
}
else
{
musicVolume = MathHelper.Lerp(musicVolume, 0.3f, MusicLerpSpeed);
Sound.StreamVolume(musicVolume);
}
}
public static void PlayDamageSound(DamageSoundType damageType, float damage, Body body)
{
Vector2 bodyPosition = ConvertUnits.ToDisplayUnits(body.Position);
bodyPosition.Y = -bodyPosition.Y;
PlayDamageSound(damageType, damage, bodyPosition);
}
public static void PlayDamageSound(DamageSoundType damageType, float damage, Vector2 position)
{
damage = MathHelper.Clamp(damage, 0.0f, 100.0f);
var sounds = damageSounds.Where(x => damage >= x.damageRange.X && damage <= x.damageRange.Y && x.damageType == damageType).ToList();
if (!sounds.Any()) return;
int selectedSound = Rand.Int(sounds.Count());
int i = 0;
foreach (var s in sounds)
{
if (i == selectedSound)
{
Debug.WriteLine(s.sound.Play(1.0f, 2000.0f, position));
Debug.WriteLine("playing: " + s.sound);
return;
}
i++;
}
}
}
}
+115
View File
@@ -0,0 +1,115 @@
using System;
using NVorbis;
using OpenTK.Audio.OpenAL;
namespace Subsurface.Sounds
{
class OggSound : IDisposable
{
//internal VorbisReader Reader { get; private set; }
//const int DefaultBufferSize = 44100;
//private VorbisReader reader;
//private SoundEffect effect;
//SoundEffectInstance instance;
public const int DefaultBufferCount = 3;
private short[] castBuffer;
private int sampleRate;
private ALFormat format;
private string file;
int alBufferId;
public int AlBufferId
{
get { return alBufferId; }
}
//public bool IsLooped { get; set; }
public static OggSound Load(string oggFile, int bufferCount = DefaultBufferCount)
{
OggSound sound = new OggSound();
sound.file = oggFile;
using (VorbisReader reader = new VorbisReader(oggFile))
{
int bufferSize = (int)reader.TotalSamples;
float[] buffer = new float[bufferSize];
sound.castBuffer = new short[bufferSize];
int readSamples = reader.ReadSamples(buffer, 0, bufferSize);
CastBuffer(buffer, sound.castBuffer, readSamples);
sound.alBufferId = AL.GenBuffer();
sound.format = reader.Channels == 1 ? ALFormat.Mono16 : ALFormat.Stereo16;
sound.sampleRate = reader.SampleRate;
//alSourceId = AL.GenSource();
AL.BufferData(sound.alBufferId, reader.Channels == 1 ? ALFormat.Mono16 : ALFormat.Stereo16, sound.castBuffer,
readSamples * sizeof(short), reader.SampleRate);
}
//AL.Source(alSourceId, ALSourcei.Buffer, alBufferId);
//if (ALHelper.XRam.IsInitialized)
//{
// ALHelper.XRam.SetBufferMode(bufferCount, ref alBufferId, XRamExtension.XRamStorage.Hardware);
// ALHelper.Check();
//}
//Volume = 1;
//if (ALHelper.Efx.IsInitialized)
//{
// alFilterId = ALHelper.Efx.GenFilter();
// ALHelper.Efx.Filter(alFilterId, EfxFilteri.FilterType, (int)EfxFilterType.Lowpass);
// ALHelper.Efx.Filter(alFilterId, EfxFilterf.LowpassGain, 1);
// LowPassHFGain = 1;
//}
return sound;
}
public void SetBufferData(int alBufferId)
{
AL.BufferData(alBufferId, format, castBuffer,
castBuffer.Length * sizeof(short), sampleRate);
}
static void CastBuffer(float[] inBuffer, short[] outBuffer, int length)
{
for (int i = 0; i < length; i++)
{
int temp = (int)(32767f * inBuffer[i]);
if (temp > short.MaxValue) temp = short.MaxValue;
else if (temp < short.MinValue) temp = short.MinValue;
outBuffer[i] = (short)temp;
}
}
public void Dispose()
{
//var state = AL.GetSourceState(alSourceId);
//if (state == ALSourceState.Playing || state == ALSourceState.Paused)
// Stop();
System.Diagnostics.Debug.WriteLine(alBufferId);
//AL.DeleteSource(alSourceId);
AL.DeleteBuffer(alBufferId);
//if (ALHelper.Efx.IsInitialized)
// ALHelper.Efx.DeleteFilter(alFilterId);
ALHelper.Check();
}
}
}
+573
View File
@@ -0,0 +1,573 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using NVorbis;
using OpenTK.Audio.OpenAL;
namespace Subsurface.Sounds
{
internal static class ALHelper
{
public static readonly XRamExtension XRam = new XRamExtension();
public static readonly EffectsExtension Efx = new EffectsExtension();
static ALHelper()
{
try
{
Debug.WriteLine("OpenAL Soft [" + (AL.Get(ALGetString.Version).Contains("SOFT") ? "X" : " ") + "], ");
Debug.WriteLine("X-RAM [" + (XRam.IsInitialized ? "X" : " ") + "], ");
Debug.WriteLine("Effect Extensions [" + (Efx.IsInitialized ? "X" : " ") + "]");
}
catch (Exception e)
{
DebugConsole.ThrowError("OpenAL error!", e);
}
}
[Conditional("TRACE")]
public static void TraceMemoryUsage(Action<string, int, int> logHandler)
{
var usedHeap = (double)GC.GetTotalMemory(true);
string[] sizes = { "B", "KB", "MB", "GB" };
int order = 0;
while (usedHeap >= 1024 && order + 1 < sizes.Length)
{
order++;
usedHeap = usedHeap / 1024;
}
//logHandler(String.Format("Total memory : {0:0.###} {1} ", usedHeap, sizes[order]), 0, 6);
}
public static void Check()
{
ALError error;
if ((error = AL.GetError()) != ALError.NoError)
{
DebugConsole.ThrowError(AL.GetErrorString(error));
}
}
}
public class OggStream : IDisposable
{
public const int DefaultBufferCount = 3;
internal readonly object stopMutex = new object();
internal readonly object prepareMutex = new object();
internal readonly int alSourceId;
internal readonly int[] alBufferIds;
readonly int alFilterId;
readonly Stream underlyingStream;
internal VorbisReader Reader { get; private set; }
internal bool Ready { get; private set; }
internal bool Preparing { get; private set; }
public int BufferCount { get; private set; }
#if TRACE
public int logX, logY;
public Action<string, int, int> LogHandler;
#endif
public OggStream(string filename, int bufferCount = DefaultBufferCount) : this(File.OpenRead(filename), bufferCount) { }
public OggStream(Stream stream, int bufferCount = DefaultBufferCount)
{
BufferCount = bufferCount;
alBufferIds = AL.GenBuffers(bufferCount);
alSourceId = AL.GenSource();
if (ALHelper.XRam.IsInitialized)
{
ALHelper.XRam.SetBufferMode(BufferCount, ref alBufferIds[0], XRamExtension.XRamStorage.Hardware);
ALHelper.Check();
}
if (ALHelper.Efx.IsInitialized)
{
alFilterId = ALHelper.Efx.GenFilter();
ALHelper.Efx.Filter(alFilterId, EfxFilteri.FilterType, (int)EfxFilterType.Lowpass);
ALHelper.Efx.Filter(alFilterId, EfxFilterf.LowpassGain, 1);
LowPassHFGain = 1;
}
underlyingStream = stream;
Volume = 1;
IsLooped = true;
}
public void Prepare()
{
if (Preparing) return;
var state = AL.GetSourceState(alSourceId);
lock (stopMutex)
{
switch (state)
{
case ALSourceState.Playing:
case ALSourceState.Paused:
return;
case ALSourceState.Stopped:
lock (prepareMutex)
{
Close();
Empty();
}
break;
}
if (!Ready)
{
lock (prepareMutex)
{
Preparing = true;
//#if TRACE
// logX = 7;
// LogHandler("(*", logX, logY);
// logX += 2;
//#endif
Open(precache: true);
//#if TRACE
// LogHandler(")", logX++, logY);
//#endif
}
}
}
}
public void Play()
{
var state = AL.GetSourceState(alSourceId);
switch (state)
{
case ALSourceState.Playing: return;
case ALSourceState.Paused:
Resume();
return;
}
Prepare();
//#if TRACE
// LogHandler("{", logX++, logY);
//#endif
AL.SourcePlay(alSourceId);
try
{
ALHelper.Check();
}
catch (Exception e)
{
throw new Exception ("AIHElper", e.InnerException);
}
Preparing = false;
OggStreamer.Instance.AddStream(this);
}
public void Pause()
{
if (AL.GetSourceState(alSourceId) != ALSourceState.Playing)
return;
OggStreamer.Instance.RemoveStream(this);
//#if TRACE
// LogHandler("]", logX++, logY);
//#endif
AL.SourcePause(alSourceId);
ALHelper.Check();
}
public void Resume()
{
if (AL.GetSourceState(alSourceId) != ALSourceState.Paused)
return;
OggStreamer.Instance.AddStream(this);
#if TRACE
LogHandler("[", logX++, logY);
#endif
AL.SourcePlay(alSourceId);
ALHelper.Check();
}
public void Stop()
{
var state = AL.GetSourceState(alSourceId);
if (state == ALSourceState.Playing || state == ALSourceState.Paused)
{
//#if TRACE
// LogHandler("}", logX++, logY);
//#endif
StopPlayback();
}
lock (stopMutex)
{
OggStreamer.Instance.RemoveStream(this);
}
}
float lowPassHfGain;
public float LowPassHFGain
{
get { return lowPassHfGain; }
set
{
if (ALHelper.Efx.IsInitialized)
{
ALHelper.Efx.Filter(alFilterId, EfxFilterf.LowpassGainHF, lowPassHfGain = value);
ALHelper.Efx.BindFilterToSource(alSourceId, alFilterId);
ALHelper.Check();
}
}
}
float volume;
public float Volume
{
get { return volume; }
set
{
AL.Source(alSourceId, ALSourcef.Gain, volume = value);
ALHelper.Check();
}
}
public bool IsLooped { get; set; }
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
var state = AL.GetSourceState(alSourceId);
if (state == ALSourceState.Playing || state == ALSourceState.Paused)
StopPlayback();
lock (prepareMutex)
{
OggStreamer.Instance.RemoveStream(this);
if (state != ALSourceState.Initial)
Empty();
Close();
underlyingStream.Dispose();
}
AL.DeleteSource(alSourceId);
AL.DeleteBuffers(alBufferIds);
if (ALHelper.Efx.IsInitialized)
ALHelper.Efx.DeleteFilter(alFilterId);
ALHelper.Check();
#if TRACE
ALHelper.TraceMemoryUsage(LogHandler);
#endif
}
}
void StopPlayback()
{
AL.SourceStop(alSourceId);
ALHelper.Check();
}
void Empty()
{
int queued;
AL.GetSource(alSourceId, ALGetSourcei.BuffersQueued, out queued);
if (queued > 0)
{
try
{
AL.SourceUnqueueBuffers(alSourceId, queued);
ALHelper.Check();
}
catch (InvalidOperationException)
{
// This is a bug in the OpenAL implementation
// Salvage what we can
int processed;
AL.GetSource(alSourceId, ALGetSourcei.BuffersProcessed, out processed);
var salvaged = new int[processed];
if (processed > 0)
{
AL.SourceUnqueueBuffers(alSourceId, processed, salvaged);
ALHelper.Check();
}
// Try turning it off again?
AL.SourceStop(alSourceId);
ALHelper.Check();
Empty();
}
}
//#if TRACE
// logX = 7;
// LogHandler(new string(Enumerable.Repeat(' ', Console.BufferWidth - 6).ToArray()), logX, logY);
//#endif
}
internal void Open(bool precache = false)
{
underlyingStream.Seek(0, SeekOrigin.Begin);
Reader = new VorbisReader(underlyingStream, false);
if (precache)
{
// Fill first buffer synchronously
OggStreamer.Instance.FillBuffer(this, alBufferIds[0]);
AL.SourceQueueBuffer(alSourceId, alBufferIds[0]);
ALHelper.Check();
// Schedule the others asynchronously
OggStreamer.Instance.AddStream(this);
}
Ready = true;
}
internal void Close()
{
if (Reader != null)
{
Reader.Dispose();
Reader = null;
}
Ready = false;
}
}
public class OggStreamer : IDisposable
{
const float DefaultUpdateRate = 10;
const int DefaultBufferSize = 44100;
static readonly object singletonMutex = new object();
readonly object iterationMutex = new object();
readonly object readMutex = new object();
readonly float[] readSampleBuffer;
readonly short[] castBuffer;
readonly HashSet<OggStream> streams = new HashSet<OggStream>();
readonly List<OggStream> threadLocalStreams = new List<OggStream>();
readonly Thread underlyingThread;
volatile bool cancelled;
public float UpdateRate { get; private set; }
public int BufferSize { get; private set; }
static OggStreamer instance;
public static OggStreamer Instance
{
get
{
lock (singletonMutex)
{
if (instance == null)
throw new InvalidOperationException("No instance running");
return instance;
}
}
private set { lock (singletonMutex) instance = value; }
}
public OggStreamer(int bufferSize = DefaultBufferSize, float updateRate = DefaultUpdateRate)
{
lock (singletonMutex)
{
if (instance != null)
throw new InvalidOperationException("Already running");
Instance = this;
underlyingThread = new Thread(EnsureBuffersFilled) { Priority = ThreadPriority.Lowest };
underlyingThread.Start();
}
UpdateRate = updateRate;
BufferSize = bufferSize;
readSampleBuffer = new float[bufferSize];
castBuffer = new short[bufferSize];
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
lock (singletonMutex)
{
Debug.Assert(Instance == this, "Two instances running, somehow...?");
cancelled = true;
lock (iterationMutex)
streams.Clear();
Instance = null;
}
}
}
internal bool AddStream(OggStream stream)
{
lock (iterationMutex)
return streams.Add(stream);
}
internal bool RemoveStream(OggStream stream)
{
lock (iterationMutex)
return streams.Remove(stream);
}
public bool FillBuffer(OggStream stream, int bufferId)
{
int readSamples;
lock (readMutex)
{
readSamples = stream.Reader.ReadSamples(readSampleBuffer, 0, BufferSize);
CastBuffer(readSampleBuffer, castBuffer, readSamples);
}
AL.BufferData(bufferId, stream.Reader.Channels == 1 ? ALFormat.Mono16 : ALFormat.Stereo16, castBuffer,
readSamples * sizeof (short), stream.Reader.SampleRate);
ALHelper.Check();
//#if TRACE
// stream.LogHandler(readSamples == BufferSize ? "." : "|", stream.logX++, stream.logY);
// //ALHelper.TraceMemoryUsage(stream.LogHandler);
//#endif
return readSamples != BufferSize;
}
static void CastBuffer(float[] inBuffer, short[] outBuffer, int length)
{
for (int i = 0; i < length; i++)
{
var temp = (int)(32767f * inBuffer[i]);
if (temp > short.MaxValue) temp = short.MaxValue;
else if (temp < short.MinValue) temp = short.MinValue;
outBuffer[i] = (short)temp;
}
}
void EnsureBuffersFilled()
{
while (!cancelled)
{
Thread.Sleep((int) (1000 / UpdateRate));
if (cancelled) break;
threadLocalStreams.Clear();
lock (iterationMutex) threadLocalStreams.AddRange(streams);
foreach (var stream in threadLocalStreams)
{
lock (stream.prepareMutex)
{
lock (iterationMutex)
if (!streams.Contains(stream))
continue;
bool finished = false;
int queued;
AL.GetSource(stream.alSourceId, ALGetSourcei.BuffersQueued, out queued);
//ALHelper.Check();
int processed;
AL.GetSource(stream.alSourceId, ALGetSourcei.BuffersProcessed, out processed);
//ALHelper.Check();
if (processed == 0 && queued == stream.BufferCount) continue;
int[] tempBuffers;
if (processed > 0)
tempBuffers = AL.SourceUnqueueBuffers(stream.alSourceId, processed);
else
tempBuffers = stream.alBufferIds.Skip(queued).ToArray();
for (int i = 0; i < tempBuffers.Length; i++)
{
finished |= FillBuffer(stream, tempBuffers[i]);
if (finished)
{
if (stream.IsLooped)
{
stream.Close();
stream.Open();
}
else
{
streams.Remove(stream);
i = tempBuffers.Length;
}
}
}
AL.SourceQueueBuffers(stream.alSourceId, tempBuffers.Length, tempBuffers);
//ALHelper.Check();
if (finished && !stream.IsLooped)
continue;
}
lock (stream.stopMutex)
{
if (stream.Preparing) continue;
lock (iterationMutex)
if (!streams.Contains(stream))
continue;
var state = AL.GetSourceState(stream.alSourceId);
if (state == ALSourceState.Stopped)
{
//#if TRACE
// stream.LogHandler("!", stream.logX++, stream.logY);
//#endif
AL.SourcePlay(stream.alSourceId);
ALHelper.Check();
}
}
}
}
}
}
}
+243
View File
@@ -0,0 +1,243 @@
using System.Collections.Generic;
using System.IO;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using Microsoft.Xna.Framework;
using Subsurface.Sounds;
namespace Subsurface
{
public class Sound
{
public static Vector3 CameraPos;
private static List<Sound> loadedSounds = new List<Sound>();
private static OggStream stream;
private OggSound oggSound;
string filePath;
//public float Volume
//{
// set { SoundManager.Volume(sourceIndex, value); }
//}
public string FilePath
{
get { return filePath; }
}
public int AlBufferId
{
get { return oggSound.AlBufferId; }
}
public static void Init()
{
SoundManager.Init();
}
public static Sound Load(string file)
{
if (!File.Exists(file))
{
DebugConsole.ThrowError("File ''" + file + "'' not found!");
return null;
}
Sound s = new Sound();
s.filePath = file;
foreach (Sound loadedSound in loadedSounds)
{
if (loadedSound.filePath == file) s.oggSound = loadedSound.oggSound;
}
if (s.oggSound == null)
{
s.oggSound = OggSound.Load(file);
}
loadedSounds.Add(s);
return s;
}
public int Play(float volume = 1.0f)
{
return SoundManager.Play(this, volume);
}
public int Play(float baseVolume, float range, Vector2 position)
{
//position = new Vector2(position.X - CameraPos.X, position.Y - CameraPos.Y);
//volume = (range == 0.0f) ? 0.0f : MathHelper.Clamp(volume * (range - position.Length())/range, 0.0f, 1.0f);
Vector2 relativePos = GetRelativePosition(position);
float volume = GetVolume(relativePos, range, baseVolume);
return SoundManager.Play(this, relativePos, volume, volume);
//if (newIndex == -1) return -1;
//return UpdatePosition(newIndex, position, range, volume);
}
public int Play(float volume, float range, Body body)
{
//Vector2 bodyPosition = ConvertUnits.ToDisplayUnits(body.Position);
//bodyPosition.Y = -bodyPosition.Y;
return Play(volume, range, ConvertUnits.ToDisplayUnits(body.Position));
}
private float GetVolume(Vector2 relativePosition, float range, float baseVolume)
{
float volume = (range == 0.0f) ? 0.0f : MathHelper.Clamp(baseVolume * (range - relativePosition.Length()) / range, 0.0f, 1.0f);
return volume;
}
private Vector2 GetRelativePosition(Vector2 position)
{
return new Vector2(position.X - CameraPos.X, position.Y - CameraPos.Y);
}
//public static int UpdatePosition(int sourceIndex, Vector2 position, float range, float baseVolume = 1.0f)
//{
// position = new Vector2(position.X - CameraPos.X, position.Y - CameraPos.Y);
// float volume = (range == 0.0f) ? 0.0f : MathHelper.Clamp(baseVolume * (range - position.Length())/range, 0.0f, 1.0f);
// if (volume <= 0.0f)
// {
// if (sourceIndex > 0)
// {
// SoundManager.Stop(sourceIndex);
// sourceIndex = -1;
// }
// return sourceIndex;
// }
// SoundManager.UpdateSoundPosition(sourceIndex, position, volume, volume);
// return sourceIndex;
//}
//public int UpdatePosition(int sourceIndex, Body body, float range, float baseVolume = 1.0f)
//{
// Vector2 bodyPosition = ConvertUnits.ToDisplayUnits(body.Position);
// bodyPosition.Y = -bodyPosition.Y;
// return UpdatePosition(sourceIndex, bodyPosition, range, baseVolume);
//}
public int Loop(int sourceIndex, float volume)
{
if (volume <= 0.0f)
{
if (sourceIndex > 0)
{
SoundManager.Stop(sourceIndex);
sourceIndex = -1;
}
return sourceIndex;
}
int newIndex = SoundManager.Loop(this, sourceIndex, volume);
return newIndex;
}
public int Loop(int sourceIndex, float baseVolume, Vector2 position, float range)
{
Vector2 relativePos = GetRelativePosition(position);
float volume = GetVolume(relativePos, range, baseVolume);
if (volume <= 0.0f)
{
if (sourceIndex > 0)
{
SoundManager.Stop(sourceIndex);
sourceIndex = -1;
}
return sourceIndex;
}
return SoundManager.Loop(this, sourceIndex, position, volume, volume);
//return UpdatePosition(newIndex, position, range, volume);
}
//public int Loop(float volume = 1.0f)
//{
// return SoundManager.Loop(this, volume);
//}
//public void Pause()
//{
// SoundManager.Pause(this);
//}
//public void Resume()
//{
// SoundManager.Resume(this);
//}
//public void Stop()
//{
// SoundManager.Stop(this);
//}
public void Remove()
{
loadedSounds.Remove(this);
System.Diagnostics.Debug.WriteLine(AlBufferId);
foreach (Sound s in loadedSounds)
{
if (s.oggSound == oggSound) return;
}
SoundManager.ClearAlSource(AlBufferId);
oggSound.Dispose();
}
public static void StartStream(string file, float volume = 1.0f)
{
stream = SoundManager.StartStream(file, volume);
}
public static void StreamVolume(float volume = 1.0f)
{
stream.Volume = volume;
}
public static void StopStream()
{
if (stream!=null) SoundManager.StopStream();
}
public static void Dispose()
{
SoundManager.Dispose();
}
}
}
+366
View File
@@ -0,0 +1,366 @@
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using OpenTK.Audio;
using OpenTK.Audio.OpenAL;
using System;
namespace Subsurface.Sounds
{
static class SoundManager
{
public const int DefaultSourceCount = 16;
private static List<int> alSources = new List<int>();
private static int[] alBuffers = new int[DefaultSourceCount];
private static int lowpassFilterId;
private static float overrideLowPassGain;
public static float OverrideLowPassGain
{
get { return overrideLowPassGain; }
set { overrideLowPassGain = MathHelper.Clamp(overrideLowPassGain, 0.0f, 1.0f); }
}
static AudioContext AC;
public static OggStreamer oggStreamer;
public static OggStream oggStream;
public static void Init()
{
AC = new AudioContext();
for (int i = 0 ; i < DefaultSourceCount; i++)
{
alSources.Add(AL.GenSource());
}
if (ALHelper.Efx.IsInitialized)
{
lowpassFilterId = ALHelper.Efx.GenFilter();
//alFilters.Add(alFilterId);
ALHelper.Efx.Filter(lowpassFilterId, EfxFilteri.FilterType, (int)EfxFilterType.Lowpass);
//LowPassHFGain = 1;
}
}
//public SoundManager(int bufferCount = DefaultSourceCount)
//{
// Stopwatch sw = new Stopwatch();
// sw.Start();
// alSourceId = AL.GenSource();
// //AL.Source(alSourceId, ALSourcei.Buffer, alBufferId);
// Volume = 1;
// if (ALHelper.Efx.IsInitialized)
// {
// alFilterId = ALHelper.Efx.GenFilter();
// ALHelper.Efx.Filter(alFilterId, EfxFilteri.FilterType, (int)EfxFilterType.Lowpass);
// ALHelper.Efx.Filter(alFilterId, EfxFilterf.LowpassGain, 1);
// LowPassHFGain = 1;
// }
// sw.Stop();
// System.Diagnostics.Debug.WriteLine("oggsource: "+sw.ElapsedMilliseconds);
//}
// public static int Play(Sound sound, float volume = 1.0f)
// {
// return Play(sound, volume, new Vector2(0.0f, 0.0f));
//}
public static int Play(Sound sound, float volume = 1.0f)
{
return Play(sound, Vector2.Zero, volume, 0.0f);
//for (int i = 2; i < DefaultSourceCount; i++)
//{
// AL.SourceStop(alSources[i]);
// AL.Source(alSources[i], ALSourceb.Looping, false);
// System.Diagnostics.Debug.WriteLine(i + ": " + AL.GetSourceState(alSources[i]));
// System.Diagnostics.Debug.WriteLine(AL.GetSourceType(alSources[i]));
//}
//for (int i = 1; i < DefaultSourceCount; i++)
//{
// //find a source that's free to use (not playing or paused)
// if (AL.GetSourceState(alSources[i]) == ALSourceState.Playing
// || AL.GetSourceState(alSources[i]) == ALSourceState.Paused) continue;
// //if (position!=Vector2.Zero)
// // position /= 1000.0f;
// alBuffers[i]=sound.AlBufferId;
// AL.Source(alSources[i], ALSourceb.Looping, false);
// AL.Source(alSources[i], ALSource3f.Position, 0.0f, 0.0f, 0.0f);
// AL.Source(alSources[i], ALSourcei.Buffer, sound.AlBufferId);
// AL.Source(alSources[i], ALSourcef.Gain, volume);
// //AL.Source(alSources[i], ALSource3f.Position, position.X, position.Y, 0.0f);
// AL.SourcePlay(alSources[i]);
// //sound.sourceIndex = i;
// return i;
//}
//return -1;
}
public static int Play(Sound sound, Vector2 position, float volume = 1.0f, float lowPassGain = 0.0f)
{
//for (int i = 2; i < DefaultSourceCount; i++)
//{
// AL.SourceStop(alSources[i]);
// AL.Source(alSources[i], ALSourceb.Looping, false);
// System.Diagnostics.Debug.WriteLine(i + ": " + AL.GetSourceState(alSources[i]));
// System.Diagnostics.Debug.WriteLine(AL.GetSourceType(alSources[i]));
//}
for (int i = 1; i < DefaultSourceCount; i++)
{
//find a source that's free to use (not playing or paused)
if (AL.GetSourceState(alSources[i]) == ALSourceState.Playing
|| AL.GetSourceState(alSources[i]) == ALSourceState.Paused) continue;
//if (position!=Vector2.Zero)
// position /= 1000.0f;
alBuffers[i] = sound.AlBufferId;
AL.Source(alSources[i], ALSourceb.Looping, false);
position /= 1000.0f;
//System.Diagnostics.Debug.WriteLine("updatesoundpos: "+offset);
AL.Source(alSources[i], ALSourcef.Gain, volume);
AL.Source(alSources[i], ALSource3f.Position, position.X, position.Y, 0.0f);
AL.Source(alSources[i], ALSourcei.Buffer, sound.AlBufferId);
ALHelper.Efx.Filter(lowpassFilterId, EfxFilterf.LowpassGainHF, lowPassHfGain = Math.Min(lowPassGain, overrideLowPassGain));
ALHelper.Efx.BindFilterToSource(alSources[i], lowpassFilterId);
ALHelper.Check();
//AL.Source(alSources[i], ALSource3f.Position, position.X, position.Y, 0.0f);
AL.SourcePlay(alSources[i]);
//sound.sourceIndex = i;
return i;
}
return -1;
}
public static int Loop(Sound sound, int sourceIndex, float volume = 1.0f)
{
return Loop(sound,sourceIndex, Vector2.Zero, volume, 0.0f);
}
public static int Loop(Sound sound, int sourceIndex, Vector2 position, float volume = 1.0f, float lowPassGain = 0.0f)
{
if (sourceIndex<1)
{
sourceIndex = Play(sound, position, volume, lowPassGain);
if (sourceIndex>0)
{
AL.Source(alSources[sourceIndex], ALSourceb.Looping, true);
AL.Source(alSources[sourceIndex], ALSourcef.Gain, volume);
}
ALHelper.Check();
return sourceIndex;
}
else
{
AL.Source(alSources[sourceIndex], ALSourceb.Looping, true);
AL.Source(alSources[sourceIndex], ALSourcef.Gain, volume);
ALHelper.Check();
return sourceIndex;
}
}
//public static int Loop(int sourceIndex, float volume = 1.0f)
//{
// if (sourceIndex > 0 && alSources[sourceIndex]>0)
// {
// ALSourceState state = AL.GetSourceState(alSources[sourceIndex]);
// ALHelper.Check();
// if (state == ALSourceState.Playing) return sourceIndex;
// }
// int newSourceIndex = Play(sound, volume);
// AL.Source(alSources[sourceIndex], ALSourceb.Looping, true);
// return sourceIndex;
//}
public static void Pause(int sourceIndex)
{
if (AL.GetSourceState(alSources[sourceIndex]) != ALSourceState.Playing)
return;
AL.SourcePause(alSources[sourceIndex]);
ALHelper.Check();
}
public static void Resume(int sourceIndex)
{
if (AL.GetSourceState(alSources[sourceIndex]) != ALSourceState.Paused)
return;
Debug.WriteLine("sourceplay");
AL.SourcePlay(alSources[sourceIndex]);
ALHelper.Check();
}
public static void Stop(int sourceIndex)
{
if (sourceIndex < 1) return;
var state = AL.GetSourceState(alSources[sourceIndex]);
if (state == ALSourceState.Playing || state == ALSourceState.Paused)
{
AL.SourceStop(alSources[sourceIndex]);
AL.Source(alSources[sourceIndex], ALSourceb.Looping, false);
}
}
public static bool IsPlaying(int sourceIndex)
{
if (sourceIndex < 1 || sourceIndex>alSources.Count-1) return false;
var state = AL.GetSourceState(alSources[sourceIndex]);
return (state == ALSourceState.Playing);
}
public static void Volume(int sourceIndex, float volume)
{
AL.Source(alSources[sourceIndex], ALSourcef.Gain, volume);
ALHelper.Check();
}
//int alFilterId;
static float lowPassHfGain;
public static float LowPassHFGain
{
get { return lowPassHfGain; }
set
{
if (ALHelper.Efx.IsInitialized)
{
overrideLowPassGain = value;
for (int i = 0; i < DefaultSourceCount; i++)
{
//find a source that's free to use (not playing or paused)
if (AL.GetSourceState(alSources[i]) != ALSourceState.Playing
&& AL.GetSourceState(alSources[i])!= ALSourceState.Paused) continue;
ALHelper.Efx.Filter(lowpassFilterId, EfxFilterf.LowpassGainHF, lowPassHfGain = value);
ALHelper.Efx.BindFilterToSource(alSources[i], lowpassFilterId);
ALHelper.Check();
}
}
}
}
//float volume;
//public float Volume
//{
// get { return volume; }
// set
// {
// AL.Source(alSourceId, ALSourcef.Gain, volume = value);
// ALHelper.Check();
// }
//}
public static void UpdateSoundPosition(int sourceIndex, Vector2 position, float baseVolume = 1.0f, float lowPassGain = 0.0f)
{
if (sourceIndex < 1) return;
//Resume(sourceIndex);
position/= 1000.0f;
//System.Diagnostics.Debug.WriteLine("updatesoundpos: "+offset);
AL.Source(alSources[sourceIndex], ALSourcef.Gain, baseVolume);
AL.Source(alSources[sourceIndex], ALSource3f.Position, position.X, position.Y, 0.0f);
ALHelper.Efx.Filter(lowpassFilterId, EfxFilterf.LowpassGainHF, lowPassHfGain = Math.Min(lowPassGain, overrideLowPassGain));
ALHelper.Efx.BindFilterToSource(alSources[sourceIndex], lowpassFilterId);
ALHelper.Check();
}
public static OggStream StartStream(string file, float volume = 1.0f)
{
if (oggStreamer == null)
oggStreamer = new OggStreamer();
oggStream = new OggStream(file);
oggStreamer.AddStream(oggStream);
oggStream.Volume = volume;
oggStream.Play();
return oggStream;
}
public static void StopStream()
{
if (oggStream!=null) oggStream.Stop();
}
public static void ClearAlSource(int bufferId)
{
for (int i = 1; i < DefaultSourceCount; i++)
{
if (alBuffers[i] == bufferId)
{
AL.Source(alSources[i], ALSourcei.Buffer, 0);
}
}
}
public static void Dispose()
{
if (ALHelper.Efx.IsInitialized)
ALHelper.Efx.DeleteFilter(lowpassFilterId);
for (int i = 0; i < DefaultSourceCount; i++)
{
var state = AL.GetSourceState(alSources[i]);
if (state == ALSourceState.Playing || state == ALSourceState.Paused)
Stop(i);
AL.DeleteSource(alSources[i]);
ALHelper.Check();
}
if (oggStream!=null)
{
oggStream.Stop();
oggStream.Dispose();
}
if (oggStreamer != null)
oggStreamer.Dispose();
}
}
}