[Test] public void RemoveAtFast() { RawList <int> list = new RawList <int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; list.RemoveAtFast(9); CollectionAssert.AreEqual(new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 }, list); list.RemoveAtFast(0); CollectionAssert.AreEqual(new int[] { 8, 1, 2, 3, 4, 5, 6, 7 }, list); list.RemoveAtFast(3); CollectionAssert.AreEqual(new int[] { 8, 1, 2, 7, 4, 5, 6 }, list); list.RemoveAtFast(6); list.RemoveAtFast(5); list.RemoveAtFast(4); list.RemoveAtFast(3); CollectionAssert.AreEqual(new int[] { 8, 1, 2 }, list); list.RemoveAtFast(0); list.RemoveAtFast(0); CollectionAssert.AreEqual(new int[] { 1 }, list); list.RemoveAtFast(0); CollectionAssert.AreEqual(new int[0], list); }
private void UpdateDebris(float timeMult) { for (int i = 0; i < debrisList.Count; i++) { ref DestructibleDebris debris = ref debrisList.Data[i]; debris.Time -= timeMult; if (debris.Scale <= 0f || debris.Alpha <= 0f) { debrisList.RemoveAtFast(i); i--; continue; } if (debris.Time <= 0f) { debris.AlphaSpeed = -MathF.Min(0.02f, debris.Alpha); } if (debris.CollisionAction != DebrisCollisionAction.None) { // Debris should collide with tilemap float nx = debris.Pos.X + debris.Speed.X * timeMult; float ny = debris.Pos.Y + debris.Speed.Y * timeMult; Hitbox hitbox = new Hitbox(nx - 1, ny - 1, nx + 1, ny + 1); if (IsTileEmpty(ref hitbox, true)) { // Nothing... } else if (debris.CollisionAction == DebrisCollisionAction.Disappear) { debris.ScaleSpeed = -0.02f; debris.AlphaSpeed = -0.006f; debris.Speed = Vector2.Zero; debris.Acceleration = Vector2.Zero; } else { // Place us to the ground only if no horizontal movement was // involved (this prevents speeds resetting if the actor // collides with a wall from the side while in the air) hitbox = new Hitbox(nx - 1, debris.Pos.Y - 1, nx + 1, debris.Pos.Y + 1); if (IsTileEmpty(ref hitbox, true)) { if (debris.Speed.Y > 0f) { debris.Speed.Y = -(0.8f /*elasticity*/ * debris.Speed.Y); //OnHitFloorHook(); } else { debris.Speed.Y = 0; //OnHitCeilingHook(); } } // If the actor didn't move all the way horizontally, // it hit a wall (or was already touching it) hitbox = new Hitbox(debris.Pos.X - 1, ny - 1, debris.Pos.X + 1, ny + 1); if (IsTileEmpty(ref hitbox, true)) { debris.Speed.X = -(0.8f /*elasticity*/ * debris.Speed.X); debris.AngleSpeed = -(0.8f /*elasticity*/ * debris.AngleSpeed); //OnHitWallHook(); } } } debris.Pos.X += debris.Speed.X * timeMult; debris.Pos.Y += debris.Speed.Y * timeMult; if (debris.Acceleration.X != 0f) { debris.Speed.X = MathF.Min(debris.Speed.X + debris.Acceleration.X * timeMult, 10f); } if (debris.Acceleration.Y != 0f) { debris.Speed.Y = MathF.Min(debris.Speed.Y + debris.Acceleration.Y * timeMult, 10f); } debris.Scale += debris.ScaleSpeed * timeMult; debris.Angle += debris.AngleSpeed * timeMult; debris.Alpha += debris.AlphaSpeed * timeMult; }
private void ThreadStreamFunc() { short[] buffer = new short[bufferSizeSamples]; Stopwatch watch = new Stopwatch(); watch.Restart(); while (!streamWorkerEnd) { // Process even number of samples (both channels of interleaved stereo) // Fill only small part of the main buffer to lower the latency // Latency is already quite high on Android int samplesPlayed = masterTrack.PlaybackHeadPosition * 2; int samplesNeeded = ((samplesPlayed + bufferSizeSamples - samplesWritten) / 12) & ~1; for (int j = 0; j < streamWorkerQueue.Count; j++) { NativeAudioSource source = streamWorkerQueue[j]; // Mix samples into the main buffer int bufferPos = 0; while (bufferPos < samplesNeeded && source.QueuedBuffers.Count > 0) { int bufferIndex = source.QueuedBuffers.Peek(); ref int playbackPos = ref source.QueuedBuffersPos[bufferIndex]; NativeAudioBuffer sourceBuffer = source.AvailableBuffers[bufferIndex]; int samplesInBuffer = MathF.Min(sourceBuffer.Length - playbackPos, samplesNeeded - bufferPos); //int samplesInBuffer = MathF.Min((int)((sourceBuffer.Length - playbackPos) / source.LastState.Pitch), samplesNeeded - bufferPos); if (!mute) { //if (MathF.Abs(1f - source.LastState.Pitch) < 0.01f) { for (int i = 0; i < samplesInBuffer; i += 2) { int sampleLeft = buffer[bufferPos] + (short)(sourceBuffer.InternalBuffer[playbackPos + i] * source.VolumeLeft); int sampleRight = buffer[bufferPos + 1] + (short)(sourceBuffer.InternalBuffer[playbackPos + i + 1] * source.VolumeRight); // Fast check to prevent clipping if (MathF.Abs(sampleLeft) < short.MaxValue && MathF.Abs(sampleRight) < short.MaxValue) { buffer[bufferPos] = (short)sampleLeft; buffer[bufferPos + 1] = (short)sampleRight; } bufferPos += 2; } //} else { // // ToDo: Check this pitch changing... // for (int i = 0; i < samplesInBuffer; i += 2) { // float io = playbackPos + (int)(i * source.LastState.Pitch); // short sample11 = sourceBuffer.InternalBuffer[(int)io]; // short sample12 = sourceBuffer.InternalBuffer[MathF.Min((int)io + 2, sourceBuffer.Length - 2)]; // short sampleLeft = (short)((sample11 + (sample12 - sample11) * (io % 1f)) * source.VolumeLeft); // short sample21 = sourceBuffer.InternalBuffer[MathF.Min((int)io + 1, sourceBuffer.Length - 1)]; // short sample22 = sourceBuffer.InternalBuffer[MathF.Min((int)io + 3, sourceBuffer.Length - 1)]; // short sampleRight = (short)((sample21 + (sample22 - sample21) * (io % 1f)) * source.VolumeRight); // // Fast check to prevent clipping // // ToDo: Do this better somehow... // if (MathF.Abs(buffer[bufferPos] + sampleLeft) < short.MaxValue && // MathF.Abs(buffer[bufferPos + 1] + sampleRight) < short.MaxValue) { // buffer[bufferPos] += sampleLeft; // buffer[bufferPos + 1] += sampleRight; // } // bufferPos += 2; // } //} } playbackPos += samplesInBuffer; //playbackPos += (int)(samplesInBuffer * source.LastState.Pitch); if (playbackPos >= sourceBuffer.Length) { playbackPos = NativeAudioSource.UnqueuedBuffer; source.QueuedBuffers.Dequeue(); } } // Perform the necessary streaming operations on the audio source, and remove it when requested if (source.IsStreamed) { // Try to stream new data if (source.IsStopped || !source.PerformStreaming()) { // End of stream, remove from queue streamWorkerQueue.RemoveAtFast(j); j--; } } else { if (source.QueuedBuffers.Count == 0) { if (source.LastState.Looped) { // Enqueue sample again if looping is turned on source.QueuedBuffers.Enqueue(0); source.QueuedBuffersPos[0] = 0; } else { // End of sample array, remove from queue streamWorkerQueue.RemoveAtFast(j); j--; } } } } // Write used part of the main buffer to Android's Audio Track masterTrack.Write(buffer, 0, samplesNeeded); samplesWritten += samplesNeeded; // Erase buffer to be ready for next batch for (int i = 0; i < samplesNeeded; i++) { buffer[i] = 0; } // After each roundtrip, sleep a little, don't keep the processor busy for no reason watch.Stop(); int roundtripTime = (int)watch.ElapsedMilliseconds; if (roundtripTime <= 1) { streamWorkerQueueEvent.WaitOne(16); } watch.Restart(); }