/// <summary> /// Setups a forced fading out. /// </summary> /// <param name="action">The action to execute once its over.</param> private void SetupForceFadeOut(Action action) { // Check if there is anything currently playing to fade out at all. if (CurrentlyPlayingFile == null || Status == SoundStatus.Stopped) { ALThread.ExecuteALThread(action); return; } // Check if a force fade out is already running. if (!_forceFadeOut) { float timeLeft = CurrentlyPlayingFile.Duration - PlaybackLocation; _forceFadeOut = true; _forceFadeOutStartDuration = PlaybackLocation; _forceFadeOutLength = MathHelper.Clamp(FadeOutLength, FadeOutLength, timeLeft); _forceFadeOutEndEvent = action; } else { // Chain action if a new one is added. Action oldAction = _forceFadeOutEndEvent; _forceFadeOutEndEvent = () => { oldAction(); action(); }; } Debugger.Log(MessageType.Info, MessageSource.SoundManager, $"Performing smooth fade out on {ToString()}."); }
/// <summary> /// Play a file on the layer. If any previous file is playing it will be stopped. /// </summary> /// <param name="file">The file to play.</param> public ContinuousAction Play(SoundFile file) { ContinuousAction thisAction = new ContinuousAction(); void PlayInternal() { // Stop whatever was playing before. StopPlayingAll(true); // Queue the file. AL.SourceQueueBuffer(_pointer, file.Pointer); _playList.Add(file); Helpers.CheckErrorAL($"queuing single in source {_pointer}"); // Play it. AL.SourcePlay(_pointer); Status = SoundStatus.Playing; Helpers.CheckErrorAL($"playing single in source {_pointer}"); Debugger.Log(MessageType.Info, MessageSource.SoundManager, $"Started playing [{file.Name}] on {ToString()}."); thisAction.Done(); } // Check if forcing a fade out. if (FadeOutOnChange) { SetupForceFadeOut(PlayInternal); } else { ALThread.ExecuteALThread(PlayInternal); } return(thisAction); }
/// <summary> /// Stop playing any files. /// </summary> /// <param name="now">Whether to stop instantly or perform FadeOutOnChange if enabled.</param> public ContinuousAction StopPlayingAll(bool now = false) { ContinuousAction thisAction = new ContinuousAction(); void StopPlayingAllInternal() { Debugger.Log(MessageType.Info, MessageSource.SoundManager, $"Stopped {ToString()}."); // Stop playback, clear played buffer. AL.Source(_pointer, ALSourceb.Looping, false); AL.SourceStop(_pointer); // Remove played buffers. RemovePlayed(); Status = SoundStatus.Stopped; Helpers.CheckErrorAL("stopping"); // Reset tracker variables. PerformReset(); thisAction.Done(); } if (FadeOutOnChange && !now) { SetupForceFadeOut(StopPlayingAllInternal); } else { ALThread.ExecuteALThread(StopPlayingAllInternal); } return(thisAction); }
/// <summary> /// Wait for the sound manager to loop. /// </summary> /// <param name="loopCount">How many times it should loop.</param> private void WaitForSoundLoops(int loopCount) { for (int i = 0; i < loopCount + 1; i++) { ALThread.ExecuteALThread(() => { }).Wait(); ALThread.ExecuteALThread(() => { }).Wait(); } }
/// <summary> /// Creates a new sound layer. This is usually done and managed by the SoundManager object in the Context. /// </summary> /// <param name="name">The name of the layer. Used by the SoundManager to refer to the layer.</param> public SoundLayer(string name) { Name = name; _playList = new List <SoundFile>(); ALThread.ExecuteALThread(() => { // Initiate source. _pointer = AL.GenSource(); Helpers.CheckErrorAL("creating source"); Debugger.Log(MessageType.Info, MessageSource.SoundManager, $"Created {ToString()}."); }); }
/// <summary> /// Pause if playing. /// </summary> public void Pause() { if (Status != SoundStatus.Playing) { return; } Debugger.Log(MessageType.Trace, MessageSource.SoundManager, $"Paused {ToString()}."); ALThread.ExecuteALThread(() => { AL.SourcePause(_pointer); Status = SoundStatus.Paused; Helpers.CheckErrorAL("pausing"); }); }
/// <summary> /// Queue a file to be played on the layer. /// </summary> /// <param name="file"></param> public ContinuousAction QueuePlay(SoundFile file) { ContinuousAction thisAction = new ContinuousAction(); void QueuePlayInternal() { Debugger.Log(MessageType.Info, MessageSource.SoundManager, $"Queued [{file.Name}] on {ToString()}."); // If playback is over but stop wasn't called then cleanup needs to be performed. if (Status == SoundStatus.Stopped) { PerformReset(); } AL.SourceQueueBuffer(_pointer, file.Pointer); _playList.Add(file); Helpers.CheckErrorAL($"queuing in source {_pointer}"); // Play if not playing. if (Status != SoundStatus.Stopped) { return; } AL.SourcePlay(_pointer); Status = SoundStatus.Playing; Helpers.CheckErrorAL($"playing source {_pointer}"); Debugger.Log(MessageType.Info, MessageSource.SoundManager, $"Started playing [{file.Name}] on {ToString()}."); thisAction.Done(); } if (FadeOutOnChange) { SetupForceFadeOut(QueuePlayInternal); } else { ALThread.ExecuteALThread(QueuePlayInternal); } return(thisAction); }
/// <summary> /// Destroy the layer freeing resources. /// </summary> public ContinuousAction Dispose() { ContinuousAction thisAction = new ContinuousAction(); ALThread.ExecuteALThread(() => { Debugger.Log(MessageType.Info, MessageSource.SoundManager, $"Destroyed {ToString()}."); StopPlayingAll(true); AL.DeleteSource(_pointer); Helpers.CheckErrorAL($"cleanup of source {_pointer}"); _pointer = -1; _playList.Clear(); thisAction.Done(); }); return(thisAction); }
/// <summary> /// Destroy the audio context. /// </summary> public void Dispose() { ALThread.ExecuteALThread(() => { _audioContext.Dispose(); }); }
internal override void Create(byte[] data) { using (MemoryStream stream = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(stream)) { // Read RIFF header. string signature = new string(reader.ReadChars(4)); if (signature != "RIFF") { throw new Exception("Unsupported sound signature."); } // Chunk size. reader.ReadInt32(); string format = new string(reader.ReadChars(4)); if (format != "WAVE") { throw new Exception("Unsupported sound format."); } // Read WAVE header. string formatSignature = new string(reader.ReadChars(4)); // Skip junk. while (formatSignature != "fmt ") { int junkSize = reader.ReadInt32(); reader.ReadBytes(junkSize); formatSignature = new string(reader.ReadChars(4)); } // Format chunk size. int chunkSize = reader.ReadInt32(); // Audio format. reader.ReadInt16(); int channels = reader.ReadInt16(); // Frequency. int sampleRate = reader.ReadInt32(); // Byte rate. reader.ReadInt32(); // Block align. reader.ReadInt16(); int bitsPerSample = reader.ReadInt16(); // Finish the rest of the chunk. reader.ReadBytes(chunkSize - 16); // Read the signature. formatSignature = new string(reader.ReadChars(4)); // Find the data chunk. while (formatSignature != "data") { int junkSize = reader.ReadInt32(); reader.ReadBytes(junkSize); formatSignature = new string(reader.ReadChars(4)); } // Read the data chunk length. int dataLength = reader.ReadInt32(); // Read the data. byte[] soundData = reader.ReadBytes(dataLength); // Create a sound buffer and load it. ALThread.ExecuteALThread(() => { Pointer = AL.GenBuffer(); AL.BufferData(Pointer, GetSoundFormat(channels, bitsPerSample), soundData, soundData.Length, sampleRate); }); Duration = soundData.Length / (sampleRate * channels * bitsPerSample / 8f); } } }
internal override void Destroy() { ALThread.ExecuteALThread(() => { AL.DeleteBuffer(Pointer); }); }