v0.1
This commit is contained in:
@@ -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++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user