Music/MusicPlayer.cs
using Sandbox.Audio;

namespace HC3;

public class MusicPlayer : Component
{
	public MusicTrack Track
	{
		get => _track;
		set
		{
			_track = value;

			if ( value is null )
			{
				Stop();
			}
			else if ( handle.IsValid() )
			{
				Play();
			}
		}
	}
	public MusicTrack _track;

	public bool IsBrokenDown
	{
		get => _isBrokenDown;
		set
		{
			_isBrokenDown = value;

			if ( !_isBrokenDown )
				Play();
		}
	}
	bool _isBrokenDown { get; set; }

	private SoundHandle handle;

	protected override void OnDisabled()
	{
		base.OnDisabled();

		handle?.Stop();
		handle = null;
	}

	public void SetTrack( string path )
	{
		ResourceLibrary.TryGet<MusicTrack>( path, out var track );
		Track = track;
	}

	protected override void OnUpdate()
	{
		base.OnUpdate();

		if ( !Track.IsValid() )
			return;

		if ( handle.IsValid() )
		{
			handle.Paused = !(Scene.TimeScale > 0.0f);

			if ( IsBrokenDown )
			{
				// fade out thru distortion, like it's tape
				handle.Pitch = MathX.Lerp( handle.Pitch, 0.0f, Time.Delta / 2.0f );

				if ( handle.Pitch < 0.5f )
				{
					Stop( 1f );
				}

				return;
			}
		}

		// manually starting/looping so we can keep it in time
		if ( handle?.IsStopped ?? true )
		{
			Play();
		}
	}

	public void Play()
	{
		if ( !Track.IsValid() ) return;

		if ( IsBrokenDown ) return;

		var sound = Track.Sound.Sounds.FirstOrDefault();
		if ( sound == null ) return;

		sound.Preload();

		handle?.Stop();
		handle = Sound.Play( Track.Sound, WorldPosition );
		handle.TargetMixer = Mixer.FindMixerByName( "ParkMusic" );

		// keep everything in sync globally
		handle.Time = RealTime.Now % sound.Duration;
	}

	public void Stop( float fadeTime = 0.0f )
	{
		handle?.Stop( fadeTime );
		handle = null;
	}
}