Files
sbox-public/engine/Sandbox.Engine/Systems/Render/Multimedia/MusicPlayer.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

253 lines
5.0 KiB
C#

using ExCSS;
using NativeEngine;
using Sandbox.Audio;
namespace Sandbox;
/// <summary>
/// Enables music playback. Use this for music, not for playing game sounds.
/// </summary>
public sealed class MusicPlayer : IDisposable
{
/// <summary>
/// We only use the audio component of the video player.
/// </summary>
VideoPlayer native;
/// <summary>
/// Sample rate of the audio being played.
/// </summary>
public int SampleRate { get; private set; }
/// <summary>
/// Number of channels of the audio being played.
/// </summary>
public int Channels { get; private set; }
/// <summary>
/// Gets the total duration of the video in seconds.
/// </summary>
public float Duration => native.Duration;
/// <summary>
/// Gets the current playback time in seconds.
/// </summary>
public float PlaybackTime => native.PlaybackTime;
/// <summary>
/// Invoked when the audio has finished playing.
/// </summary>
public Action OnFinished { get; set; }
/// <summary>
/// Invoked when the audio has repeated.
/// </summary>
public Action OnRepeated { get; set; }
/// <summary>
/// Place the listener at 0,0,0 facing 1,0,0.
/// </summary>
public bool ListenLocal
{
get => native.Audio.ListenLocal;
set => native.Audio.ListenLocal = value;
}
/// <summary>
/// Position of the sound.
/// </summary>
public Vector3 Position
{
get => native.Audio.Position;
set => native.Audio.Position = value;
}
/// <summary>
/// Pause playback of audio.
/// </summary>
public bool Paused
{
get => native.IsPaused;
set
{
if ( value )
{
native.Pause();
}
else
{
native.Resume();
}
}
}
/// <summary>
/// Audio will repeat when reaching the end.
/// </summary>
public bool Repeat
{
get => native.Repeat;
set => native.Repeat = value;
}
/// <summary>
/// Change the volume of this music.
/// </summary>
public float Volume
{
get => native.Audio.Volume;
set => native.Audio.Volume = value;
}
/// <summary>
/// Enables lipsync processing.
/// </summary>
public bool LipSync
{
get => native.Audio.LipSync;
set => native.Audio.LipSync = value;
}
/// <summary>
/// Which mixer do we want to write to
/// </summary>
public Mixer TargetMixer
{
get => native.Audio.TargetMixer;
set => native.Audio.TargetMixer = value;
}
/// <inheritdoc cref="SoundHandle.Distance"/>
public float Distance
{
get => native.Audio.Distance;
set => native.Audio.Distance = value;
}
/// <inheritdoc cref="SoundHandle.Falloff"/>
public Curve Falloff
{
get => native.Audio.Falloff;
set => native.Audio.Falloff = value;
}
/// <summary>
/// A list of 15 lipsync viseme weights. Requires <see cref="LipSync"/> to be enabled.
/// </summary>
public IReadOnlyList<float> Visemes => native.Audio.Visemes;
/// <summary>
/// Get title of the track.
/// </summary>
public string Title
{
get
{
var title = GetMeta( "title" );
return string.IsNullOrWhiteSpace( title ) ? GetMeta( "StreamTitle" ) : title;
}
}
/// <summary>
/// 512 FFT magnitudes used for audio visualization.
/// </summary>
public ReadOnlySpan<float> Spectrum => GetSpectrum();
/// <summary>
/// Approximate measure of audio loudness.
/// </summary>
public float Amplitude => native.Audio.GetAmplitude();
private unsafe ReadOnlySpan<float> GetSpectrum()
{
return native.Audio.GetSpectrum();
}
internal MusicPlayer()
{
native = new VideoPlayer();
native.OnRepeated += OnRepeatInternal;
native.OnFinished += OnFinishedInternal;
// MusicPlayer used to have different defaults than VideoPlayer, need to make sure they are still set
native.Audio.ListenLocal = false;
native.Audio.Position = Vector3.Zero;
native.Audio.Volume = 1.0f;
native.Audio.LipSync = false;
}
~MusicPlayer()
{
MainThread.QueueDispose( this );
}
public void Dispose()
{
native?.Dispose();
native.OnRepeated -= OnRepeatInternal;
native.OnFinished -= OnFinishedInternal;
GC.SuppressFinalize( this );
}
/// <summary>
/// Plays a music stream from a URL.
/// </summary>
public static MusicPlayer PlayUrl( string url )
{
var player = new MusicPlayer();
player.native.Play( url );
return player;
}
/// <summary>
/// Plays a music file from a relative path.
/// </summary>
public static MusicPlayer Play( BaseFileSystem filesystem, string path )
{
var player = new MusicPlayer();
player.native.Play( filesystem, path );
return player;
}
/// <summary>
/// Stops audio playback.
/// </summary>
public void Stop()
{
native.Stop();
}
/// <summary>
/// Sets the playback position to a specified time in the audio, given in seconds.
/// </summary>
public void Seek( float time )
{
native.Seek( time );
}
/// <summary>
/// Get meta data string.
/// </summary>
internal string GetMeta( string key )
{
return native.GetMeta( key );
}
internal void OnFinishedInternal()
{
MainThread.Queue( () =>
{
OnFinished?.Invoke();
} );
}
internal void OnRepeatInternal()
{
MainThread.Queue( () =>
{
OnRepeated?.Invoke();
} );
}
}