2using Microsoft.Extensions.Logging;
11[JsonObject(IsReference =
true)]
12public class AudioResource : IDisposable
26 private bool _disposed;
29 private float _volume = 1.0f;
35 private float _playbackSpeed = 1.0f;
38 private bool _isLooping;
56 private AudioResource()
66 float playbackSpeed = 1.0f,
67 string? filePathOrExt =
null,
68 byte[]? rawBytes =
null,
72 _playback = playback ??
throw new ArgumentNullException(nameof(playback));
75 SourceFilePath = assetIdentifier is
null && !
string.IsNullOrWhiteSpace(filePathOrExt) && File.Exists(filePathOrExt)
79 var ext = Path.GetExtension(filePathOrExt ??
string.Empty);
80 SourceExtension =
string.IsNullOrEmpty(ext) ? null : NormalizeExtension(ext);
88 _playback.PlaybackCompleted += OnPlaybackCompleted;
93 public string Key {
get;
private set; }
148 get => _playback?.CurrentTime ?? TimeSpan.Zero;
156 public TimeSpan
Duration => _playback?.Duration ?? TimeSpan.Zero;
165 if (_playback is not
null)
166 _playback.IsLooping = value;
176 if (
float.IsNaN(value))
throw new ArgumentOutOfRangeException(nameof(value));
177 _volume = Math.Clamp(value, 0f, 1f);
178 if (_playback is not
null)
179 _playback.Volume = _volume;
189 if (
float.IsNaN(value))
throw new ArgumentOutOfRangeException(nameof(value));
190 _pan = Math.Clamp(value, -1f, 1f);
191 if (_playback is not
null)
192 _playback.Pan = _pan;
201 get => _playbackSpeed;
204 if (
float.IsNaN(value))
throw new ArgumentOutOfRangeException(nameof(value));
206 if (_playback is not
null)
207 _playback.PlaybackSpeed = _playbackSpeed;
215 public void Play(
bool fromStart =
true) => Playback.Play(fromStart);
220 public void Pause() => Playback.Pause();
225 public void Resume() => Playback.Resume();
231 public void Seek(TimeSpan position) => Playback.Seek(position);
236 public void Stop() => Playback.Stop();
238 internal void SetSourceUri(
string uri)
242 SourceExtension = NormalizeExtension(Path.GetExtension(
new Uri(uri, UriKind.RelativeOrAbsolute).IsAbsoluteUri
243 ?
new Uri(uri).AbsolutePath
259 internal void ReloadIntoManager(
bool forceReload =
false)
261 if (
string.IsNullOrWhiteSpace(
Key))
262 throw new InvalidOperationException(
"AudioResource has no Key and cannot be reloaded.");
264 var manager = AudioResourceManager.Instance;
266 if (!forceReload && manager.TryGet(
Key, out var existing) && existing is not
null)
275 if (forceReload && manager.Contains(
Key))
278 AudioResource loaded;
283 throw new InvalidOperationException($
"Missing asset data for {Key}.");
292 else if (!
string.IsNullOrWhiteSpace(
SourceUri))
298 throw new InvalidOperationException($
"AudioResource '{Key}' has no persisted source.");
304 private IAudioPlaybackHandle Playback
308 ObjectDisposedException.ThrowIf(_disposed,
this);
309 return _playback ??
throw new InvalidOperationException(
310 $
"AudioResource '{Key}' is not attached to an audio backend. Configure an audio backend before loading or restoring audio.");
314 private async
void OnPlaybackCompleted(
object? sender, EventArgs e)
323 Engine.Logger.LogError(ex,
"PlaybackCompletedAsync threw an exception for audio resource: {Key}",
Key);
331 private static string? NormalizeExtension(
string? extension)
333 if (
string.IsNullOrWhiteSpace(extension))
336 return extension.StartsWith(
'.')
337 ? extension.ToLowerInvariant()
338 :
"." + extension.ToLowerInvariant();
350 if (_playback is not
null)
352 _playback.PlaybackCompleted -= OnPlaybackCompleted;
358 Disposed?.Invoke(
this, EventArgs.Empty);
359 GC.SuppressFinalize(
this);
Represents an identifier for a specific asset within a file, including its type and name.
Stream? Data
Gets the stream containing the asset data.
Represents an audio resource that can be played, paused, resumed, sought, and disposed....
float Pan
Gets or sets stereo pan from -1.0 (left) through 1.0 (right).
string Key
Gets the unique key associated with this audio resource.
EventHandler? Disposed
Event raised when the resource is disposed.
float Volume
Gets or sets output volume in the range 0.0 through 1.0.
bool IsLooping
Gets or sets whether playback loops continuously.
TimeSpan CurrentTime
Gets or sets the current playback position within the resource. Setting this property seeks to the sp...
string? SourceExtension
Normalized file extension used by the configured backend.
float PlaybackSpeed
Gets or sets playback speed. 1.0 is normal speed; supported values are clamped to 0....
bool IsPlaying
Gets a value indicating whether playback is currently active.
string? TempFilePath
Gets the temporary file path used by a backend that requires file-based decoding, if any.
const float MaximumPlaybackSpeed
Maximum allowed playback speed multiplier. Values above this are clamped.
void Dispose()
Disposes the audio resource and releases any underlying playback handle and temporary files....
TimeSpan Duration
Gets the total duration of the resource, or TimeSpan.Zero when unknown.
byte?[] OriginalBytes
Gets the original byte array of the audio data, if available.
bool IsPaused
Gets a value indicating whether playback is currently paused.
void Stop()
Stops playback immediately.
void Pause()
Pauses playback if currently playing.
EventHandler? PlaybackCompleted
Event raised when non-looping playback reaches the end of the resource.
const float MinimumPlaybackSpeed
Minimum allowed playback speed multiplier. Values below this are clamped.
Func< Task >? PlaybackCompletedAsync
Optional asynchronous callback invoked when non-looping playback completes.
string? SourceFilePath
Original file path when the sound was loaded from disk. Null when loaded from an AssetsFile or URI.
AudioPlaybackState State
Gets the current high-level playback state of the resource.
void Play(bool fromStart=true)
Starts playback of the resource.
AssetsFileIdentifier? AssetIdentifier
Asset identifier when the sound was loaded from an AssetsFile.
string? SourceUri
Source URI when the resource was loaded by URI, primarily for browser playback.
void Seek(TimeSpan position)
Seeks to the specified playback position.
void Resume()
Resumes playback if currently paused.
Backend-provided playback handle used by AudioResource. Implements IDisposable to allow releasing any...
AudioPlaybackState
Describes the current playback state of an audio resource without exposing backend-specific playback ...