/// <summary> /// Stop the <see cref="Sound"/>. /// </summary> public void Stop() { // This may cause OpenAL error if the Source is not valid. ALChecker.Check(() => AL.SourceStop(Source)); Reader.DecodeTime = 0; if (!Deferred) { SoundSystem.Instance.RemoveUndeferredSource(Source); Deferred = true; } if (SoundStopped != null) { SoundStopped(this, EventArgs.Empty); } // In this case, we really need to check this first // Whether the Source is valid bool isValidSource = AL.IsSource(Source); ALChecker.CheckError(); if (isValidSource) { // TODO: Should we really need to dispose the source here? ALChecker.Check(() => AL.DeleteSource(Source)); Source = -1; // Set to invalid Source to make the next cycle source available } }
/// <summary> /// Initialize the Native Handle, De-queue existing buffer and (re)Queue the buffer of the <see cref="SoundBuffer"/>. /// </summary> protected void Initialize() { // Check whether the buffer is already initializing state if (IsPreparing) { return; } // Check whether the current Source is valid bool isValidSource = AL.IsSource(Source); ALChecker.CheckError(); if (!isValidSource || Source == -1) { // Get unused Source from SoundSystem Source = SoundSystem.Instance.GetAvailableSource(); } // We don't have clue which buffer id that being attached to the source right now // So we don't attach (queue) the buffer to the source now // We will attach (queue) it when we are (really) going to queue the buffer (read: Play()) // Initialize() call will dequeue current buffer and queue the buffer when its required // Check the sound state switch (State) { // Sound is already playing, no need preparation(?) case SoundState.Paused: case SoundState.Playing: return; // The sounds is already stopped (which mean its been played once or more before) case SoundState.Stopped: // Reset to 0 Position Reader.DecodeTime = 0; // Dequeue remaining buffer before we (re)queue the buffer DequeueBuffer(); // We are not ready to start yet, we haven't queue the buffer IsReady = false; break; } // Check whether our buffer is ready if (!IsReady) { // If its not ready, lets queue them IsPreparing = true; // set the flag to prevent requeue during process QueueBuffer(precache: true); IsPreparing = false; // done } }
internal int GetAvailableSource() { for (int i = 0; i < _sources.Length; i++) { bool isValidSource = AL.IsSource(_sources[i]); ALChecker.CheckError(); if (isValidSource) { // Some sources are probably used by another sounds // Return the one that not in use // Identify source state ALSourceState state = AL.GetSourceState(_sources[i]); ALChecker.CheckError(); // Do not use un-deferred source if (_unDeferredSources.Contains(_sources[i]) && state == ALSourceState.Initial) { continue; } else if (_unDeferredSources.Contains(_sources[i]) && state == ALSourceState.Stopped) { _unDeferredSources.Remove(_sources[i]); } // No sounds using it or no longer in use, use it if (state == ALSourceState.Initial || (state == ALSourceState.Stopped)) { return(_sources[i]); } // Source is in use by a sound, find another one else if (state == ALSourceState.Paused || state == ALSourceState.Playing) { continue; } } else { // Not a source (anymore..?) // Generate and use it _sources[i] = AL.GenSource(); // Since it's newly generated, it must be unused source return(_sources[i]); } } // All sources are used at the moment... // Return invalid source return(-1); }
/// <summary> /// Initialize the <see cref="SoundSystem"/>. /// </summary> /// <param name="bufferSize"></param> public void Initialize(int bufferSize = DEFAULT_BUFFER_SIZE) { if (_context == null) { _context = new ALContext(); } BufferSize = bufferSize; XRam = new XRamExtension(); Efx = new EffectsExtension(); // Init empty sources _sources = AL.GenSources(MAXIMUM_NUMBER_OF_SOURCES); ALChecker.CheckError(); }
/// <summary> /// Update the <see cref="SoundSystem"/>. /// </summary> /// <param name="delta"></param> public void Update(double delta) { // Copy to a new list List <SoundBuffer> soundStreams = new List <SoundBuffer>(_sounds); // Loop it foreach (var sound in soundStreams) { // The sounds is no longer exist, skip it if (!_sounds.Contains(sound)) { continue; } bool finished = false; // Get how many queued buffers int queued = -1; ALChecker.Check(() => AL.GetSource(sound.Source, ALGetSourcei.BuffersQueued, out queued)); // Get how many processed buffers int processed = -1; ALChecker.Check(() => AL.GetSource(sound.Source, ALGetSourcei.BuffersProcessed, out processed)); // Every queued buffers are processed, skip it if (processed == 0 && queued == sound.Buffers.Length) { continue; } // Unqueue the processed buffers int[] tempBuffers; if (processed > 0) { tempBuffers = AL.SourceUnqueueBuffers(sound.Source, processed); ALChecker.CheckError(); } else { // Get remaining queued buffers List <int> buffers = new List <int>(); for (int i = queued; i < sound.Buffers.Length; i++) { buffers.Add(sound.Buffers[i]); } //tempBuffers = sound.alBufferIds.Skip(queued).ToArray(); tempBuffers = buffers.ToArray(); } // Loop for each queued buffers for (int i = 0; i < tempBuffers.Length; i++) { // Upload buffer data of samples and check whether its already processed finished |= sound.Reader.BufferData(BufferSize, tempBuffers[i]); // Buffer data has been processed if (finished) { // Check whether the sounds is looping // Reset position of DecodeTime to zero // Since Looping via AL command is not supported on stream sounds. if (sound.IsLooping) { sound.Reader.DecodeTime = 0; } else { // No repeat, then safely remove the sounds from the queue list // Triger the event first before remove sound.OnFinish(); _sounds.Remove(sound); i = tempBuffers.Length; } } } ALChecker.Check(() => AL.SourceQueueBuffers(sound.Source, tempBuffers.Length, tempBuffers)); if (finished && !sound.IsLooping) { continue; } if (sound.IsPreparing) { continue; } if (!_sounds.Contains(sound)) { continue; } var state = AL.GetSourceState(sound.Source); ALChecker.CheckError(); if (state == ALSourceState.Stopped) { ALChecker.Check(() => AL.SourcePlay(sound.Source)); } } }