DiamondSlideAudio.cs
using System;
using Sandbox;

namespace Diamonds;

/// <summary>Overlapping excerpts of the recorded gem scrape, without a fixed loop seam.</summary>
public sealed class DiamondSlideAudio
{
	const float Crossfade = 0.07f;
	readonly Random random = new();
	SoundHandle current;
	SoundHandle outgoing;
	float currentGain;
	float outgoingGain;
	float currentPitch;
	float outgoingPitch;
	float blendTime;
	float changeAt;

	public void Update( float speed, float delta )
	{
		// Read the source duration so replacing the recording does not leave stale timings.
		var source = SoundFile.Load( "sfx/slide_1.vsnd" );
		if ( source is null || !source.IsValid ) { Stop(); return; }
		// Load creates the resource wrapper; native audio data can still be pending
		// on first use or after an asset reload. Duration requires that data.
		source.Preload();
		if ( !source.IsLoaded ) { Stop(); return; }
		float duration = source.Duration;
		if ( duration <= 0 ) { Stop(); return; }
		if ( current is null || !current.IsValid || current.Time >= changeAt )
			StartExcerpt( duration );
		if ( current is null ) return;

		blendTime = MathF.Min( Crossfade, blendTime + delta );
		float blend = blendTime / Crossfade;
		float volume = (0.20f + speed * 0.40f) * DiamondSettings.EffectsGain;
		float pitch = 0.95f + speed * 0.10f;
		current.Volume = currentGain * volume * MathF.Sin( blend * MathF.PI * 0.5f );
		current.Pitch = currentPitch * pitch;
		if ( outgoing is not null )
		{
			outgoing.Volume = outgoingGain * volume * MathF.Cos( blend * MathF.PI * 0.5f );
			outgoing.Pitch = outgoingPitch * pitch;
			if ( blend >= 1 ) { outgoing.Stop(); outgoing = null; }
		}
	}

	void StartExcerpt( float duration )
	{
		outgoing?.Stop();
		outgoing = current;
		outgoingGain = currentGain;
		outgoingPitch = currentPitch;
		current = Sound.Play( "sfx/slide_1.sound" );
		if ( current is null ) { Stop(); return; }
		currentGain = current.Volume;
		currentPitch = current.Pitch * (0.98f + (float)random.NextDouble() * 0.04f);
		current.Volume = 0;
		current.SpacialBlend = 0;
		// Avoid replaying the same attack and tail. Keep enough source after the
		// handoff for the outgoing voice to finish its crossfade at maximum pitch.
		float margin = MathF.Min( 0.04f, duration * 0.05f );
		float fadeRoom = Crossfade * 1.2f;
		float length = MathF.Min( 0.22f + (float)random.NextDouble() * 0.12f, duration * 0.55f );
		float startRange = MathF.Max( 0, duration - margin * 2 - length - fadeRoom );
		float start = margin + (float)random.NextDouble() * startRange;
		current.Time = start;
		changeAt = start + length;
		blendTime = 0;
	}

	public void Stop()
	{
		current?.Stop();
		outgoing?.Stop();
		current = null;
		outgoing = null;
		blendTime = 0;
	}
}