Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🚧Improve pathing based on OS #359

Merged
merged 10 commits into from
Aug 26, 2024
3 changes: 3 additions & 0 deletions CASL.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_BEFORE_NEW_PARENTHESES/@EntryValue">True</s:Boolean>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LIMIT/@EntryValue">145</s:Int64>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AL/@EntryIndexedValue">AL</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ALC/@EntryIndexedValue">ALC</s:String>
Expand All @@ -8,6 +9,8 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RTLD/@EntryIndexedValue">RTLD</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=f9fce829_002De6f4_002D4cb2_002D80f1_002D5497c44f51df/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:Boolean x:Key="/Default/Environment/Filtering/ExcludeCoverageFilters/=CASLTesting_003B_002A_003B_002A_003B_002A/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/Filtering/ExcludeCoverageFilters/=CASLTests_003B_002A_003B_002A_003B_002A/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
Expand Down
80 changes: 45 additions & 35 deletions CASL/Audio.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace CASL;
using Devices;
using Exceptions;
using Factories;
using NativeInterop;
using OpenAL;
using ReactableData;

Expand All @@ -23,7 +24,6 @@ namespace CASL;
[SuppressMessage("ReSharper", "ClassWithVirtualMembersNeverInherited.Global", Justification = "Users need to inherit.")]
public class Audio : IAudio
{
private const char CrossPlatDirSeparatorChar = '/';
private const string IsDisposedExceptionMessage = "The audio is disposed. You must create another audio instance.";
private readonly IAudioDeviceManager audioManager;
private readonly IPushReactable<AudioCommandData> audioCommandReactable;
Expand All @@ -32,6 +32,8 @@ public class Audio : IAudio
private readonly IOpenALInvoker alInvoker;
private readonly IAudioBuffer audioBuffer;
private readonly IPath path;
private readonly IFile file;
private readonly IPlatform platform;
private uint srcId;
private bool isDisposed;
private bool audioDeviceChanging;
Expand All @@ -47,10 +49,10 @@ public class Audio : IAudio
/// <param name="filePath">The path to the audio file.</param>
/// <param name="bufferType">The type of audio buffer used.</param>
/// <remarks>
/// Using <see cref="CASL.BufferType"/>.<see cref="CASL.BufferType.Full"/> means all of the audio data will be loaded into memory.
/// Using <see cref="CASL.BufferType"/>.<see cref="CASL.BufferType.Full"/> means all the audio data will be loaded into memory.
/// This is fine for small audio files like audio effects. Large audio files will consume more memory and take longer to load.
/// <para/>
/// Using <see cref="CASL.BufferType"/>.<see cref="CASL.BufferType.Stream"/> means all of the audio data will be loaded/streamed into
/// Using <see cref="CASL.BufferType"/>.<see cref="CASL.BufferType.Stream"/> means all the audio data will be loaded/streamed into
/// This is better for larger audio files like music. It will consume less memory and much better for performances from a loading perspective.
/// in chunks as needed memory.
/// </remarks>
Expand All @@ -60,16 +62,11 @@ public class Audio : IAudio
[ExcludeFromCodeCoverage(Justification = "Directly interacts with the IoC container.")]
public Audio(string filePath, BufferType bufferType)
{
var file = IoC.Container.GetInstance<IFile>();
this.file = IoC.Container.GetInstance<IFile>();

if (!file.Exists(filePath))
{
throw new FileNotFoundException($"The audio file could not be found.", filePath);
}

FilePath = filePath.ToCrossPlatPath().TrimAllFromEnd(CrossPlatDirSeparatorChar);
BufferType = bufferType;

this.platform = IoC.Container.GetInstance<IPlatform>();
this.alInvoker = IoC.Container.GetInstance<IOpenALInvoker>();
this.alInvoker.ErrorCallback += ErrorCallback;

Expand All @@ -79,8 +76,8 @@ public Audio(string filePath, BufferType bufferType)
this.path = IoC.Container.GetInstance<IPath>();

var reactableFactory = IoC.Container.GetInstance<IReactableFactory>();
this.audioCommandReactable = reactableFactory.CreateAudioCmndReactable();
this.posCommandReactable = reactableFactory.CreatePositionCmndReactable();
this.audioCommandReactable = reactableFactory.CreateAudioCmdReactable();
this.posCommandReactable = reactableFactory.CreatePositionCmdReactable();
this.loopingReactable = reactableFactory.CreateIsLoopingReactable();

var bufferFactory = IoC.Container.GetInstance<IAudioBufferFactory>();
Expand All @@ -92,13 +89,14 @@ public Audio(string filePath, BufferType bufferType)
_ => throw new InvalidEnumArgumentException(nameof(bufferType), (int)bufferType, typeof(BufferType))
};

Init();
Init(filePath);
}

/// <summary>
/// Initializes a new instance of the <see cref="Audio"/> class.
/// </summary>
/// <param name="filePath">The path to the audio file.</param>
/// <param name="platform">Provides platform specific information.</param>
/// <param name="bufferType">The type of audio buffer used.</param>
/// <param name="alInvoker">Provides access to OpenAL.</param>
/// <param name="audioManager">Manages audio device related operations.</param>
Expand All @@ -121,6 +119,7 @@ public Audio(string filePath, BufferType bufferType)
[SuppressMessage("csharpsquid", "S107", Justification = "Not part of the public API.")]
internal Audio(
string filePath,
IPlatform platform,
BufferType bufferType,
IOpenALInvoker alInvoker,
IAudioDeviceManager audioManager,
Expand All @@ -130,31 +129,17 @@ internal Audio(
IFile file)
{
ArgumentException.ThrowIfNullOrEmpty(filePath);
ArgumentNullException.ThrowIfNull(platform);
ArgumentNullException.ThrowIfNull(alInvoker);
ArgumentNullException.ThrowIfNull(audioManager);
ArgumentNullException.ThrowIfNull(bufferFactory);
ArgumentNullException.ThrowIfNull(reactableFactory);
ArgumentNullException.ThrowIfNull(path);
ArgumentNullException.ThrowIfNull(file);

if (!file.Exists(filePath))
{
throw new FileNotFoundException($"The audio file could not be found.", filePath);
}

var extension = path.GetExtension(filePath).ToLower();

var exMsg = $"The file extension '{extension}' is not supported.";
exMsg += " Supported extensions are '.ogg' and '.mp3'.";

if (extension != ".ogg" && extension != ".mp3")
{
throw new AudioException(exMsg);
}

FilePath = filePath.ToCrossPlatPath().TrimAllFromEnd(CrossPlatDirSeparatorChar);
BufferType = bufferType;

this.platform = platform;
this.alInvoker = alInvoker;
this.alInvoker.ErrorCallback += ErrorCallback;

Expand All @@ -165,17 +150,18 @@ internal Audio(
_ => throw new InvalidEnumArgumentException(nameof(bufferType), (int)bufferType, typeof(BufferType))
};

this.audioCommandReactable = reactableFactory.CreateAudioCmndReactable();
this.posCommandReactable = reactableFactory.CreatePositionCmndReactable();
this.audioCommandReactable = reactableFactory.CreateAudioCmdReactable();
this.posCommandReactable = reactableFactory.CreatePositionCmdReactable();
this.loopingReactable = reactableFactory.CreateIsLoopingReactable();

this.audioManager = audioManager;
this.path = path;
this.file = file;

this.audioManager.DeviceChanging += AudioManager_DeviceChanging;
this.audioManager.DeviceChanged += AudioManager_DeviceChanged;

Init();
Init(filePath);
}

/// <summary>
Expand All @@ -188,7 +174,7 @@ internal Audio(
public string Name => this.path.GetFileNameWithoutExtension(FilePath);

/// <inheritdoc/>
public string FilePath { get; }
public string FilePath { get; private set; } = string.Empty;

/// <inheritdoc/>
/// <exception cref="InvalidOperationException">Thrown if the <see cref="Audio"/> has been disposed.</exception>
Expand Down Expand Up @@ -481,9 +467,33 @@ protected virtual void Dispose(bool disposing)

/// <summary>
/// Initializes the audio.
/// <param name="filePath">The path to the audio file.</param>
/// </summary>
private void Init()
private void Init(string filePath)
{
var extension = this.path.GetExtension(filePath).ToLower();

var exMsg = $"The file extension '{extension}' is not supported.";
exMsg += " Supported extensions are '.ogg' and '.mp3'.";

if (extension != ".ogg" && extension != ".mp3")
{
throw new AudioException(exMsg);
}

FilePath = filePath.TrimAllFromEnd(this.path.DirectorySeparatorChar)
.TrimAllFromEnd(this.path.AltDirectorySeparatorChar);

if (this.platform.IsWinPlatform())
{
FilePath = filePath.Replace(this.path.AltDirectorySeparatorChar, this.path.DirectorySeparatorChar);
}

if (!this.file.Exists(filePath))
{
throw new FileNotFoundException("The audio file could not be found.", filePath);
}

this.srcId = this.audioBuffer.Init(FilePath);
this.audioBuffer.Upload();
}
Expand Down Expand Up @@ -517,7 +527,7 @@ private void AudioManager_DeviceChanging(object? sender, EventArgs e)
/// </summary>
private void AudioManager_DeviceChanged(object? sender, EventArgs e)
{
Init();
Init(FilePath);

this.audioDeviceChanging = false;
this.alInvoker.Source(this.srcId, ALSourceb.Looping, this.loopStateBeforeDeviceChange);
Expand Down
2 changes: 1 addition & 1 deletion CASL/AudioTime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace CASL;
using System.Diagnostics.CodeAnalysis;

/// <summary>
/// Represents a time value of a audio.
/// Represents a time value for some audio.
/// </summary>
/// <remarks>
/// This could represent the current position or the length of a audio.
Expand Down
8 changes: 4 additions & 4 deletions CASL/Data/Decoders/IAudioDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ internal interface IAudioDecoder : IDisposable
/// Gets the total number of sample frames.
/// </summary>
/// <remarks>
/// A sample frame is all of the samples that consist of all the channels combined.
/// A sample frame is all the samples that consist of all the channels combined.
/// </remarks>
long TotalSampleFrames { get; }

Expand All @@ -66,7 +66,7 @@ internal interface IAudioDecoder : IDisposable
int ReadUpTo(uint upTo);

/// <summary>
/// Reads the from the audio.
/// Reads the audio.
/// </summary>
/// <returns>The total number of samples read.</returns>
/// <remarks>
Expand All @@ -75,7 +75,7 @@ internal interface IAudioDecoder : IDisposable
int ReadSamples();

/// <summary>
/// Reads all of the samples from the audio.
/// Reads all the samples from the audio.
/// </summary>
/// <remarks>
/// The data can be retrieved using the <see cref="GetSampleData{T}"/> method.
Expand All @@ -88,7 +88,7 @@ internal interface IAudioDecoder : IDisposable
/// <typeparam name="T">The type of each data item.</typeparam>
/// <returns>The audio sample data.</returns>
/// <remarks>
/// For MP3 data this would be bytes and for OGG data this would be float.
/// For MP3 data this would be bytes and for OGG data this would be floats.
/// </remarks>
T[] GetSampleData<T>();

Expand Down
2 changes: 1 addition & 1 deletion CASL/Data/Decoders/IAudioFileDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace CASL.Data.Decoders;
/// Streams mp3 audio data from a mp3 file.
/// </summary>
/// <typeparam name="T">The type of data from the audio stream.</typeparam>
internal interface IAudioFileDecoder<T> : IDisposable
internal interface IAudioFileDecoder<in T> : IDisposable
{
/// <summary>
/// Gets the number of audio channels.
Expand Down
8 changes: 4 additions & 4 deletions CASL/Data/FullBuffer.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// <copyright file="FullBuffer.cs" company="KinsonDigital">
// <copyright file="FullBuffer.cs" company="KinsonDigital">
// Copyright (c) KinsonDigital. All rights reserved.
// </copyright>

Expand Down Expand Up @@ -67,8 +67,8 @@ public FullBuffer(
this.path = path;
this.file = file;

var audioCmdReactable = reactableFactory.CreateAudioCmndReactable();
var posCmdReactable = reactableFactory.CreatePositionCmndReactable();
var audioCmdReactable = reactableFactory.CreateAudioCmdReactable();
var posCmdReactable = reactableFactory.CreatePositionCmdReactable();
var loopingReactable = reactableFactory.CreateIsLoopingReactable();

this.audioCmdUnsubscriber = audioCmdReactable.CreateOneWayReceive(
Expand Down Expand Up @@ -187,7 +187,7 @@ public void Upload()
throw new InvalidOperationException("The buffer has not been initialized.");
}

// Buffer all of the data
// Buffer all the data
this.audioDecoder.ReadAllSamples();

switch (this.audioFormatType)
Expand Down
8 changes: 4 additions & 4 deletions CASL/Data/StreamBuffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ public StreamBuffer(
this.path = path;
this.file = file;

var audioCmdReactable = reactableFactory.CreateAudioCmndReactable();
var posCmdReactable = reactableFactory.CreatePositionCmndReactable();
var audioCmdReactable = reactableFactory.CreateAudioCmdReactable();
var posCmdReactable = reactableFactory.CreatePositionCmdReactable();
var loopingReactable = reactableFactory.CreateIsLoopingReactable();

this.audioCmdUnsubscriber = audioCmdReactable.CreateOneWayReceive(
Expand Down Expand Up @@ -452,7 +452,7 @@ private void StreamData()
}

/// <summary>
/// Returns a value indicating whether or not the end of the audio has been reached.
/// Returns a value indicating whether the end of the audio has been reached.
/// </summary>
/// <returns>True if the end has been reached, otherwise false.</returns>
private bool HasReachedEnd()
Expand Down Expand Up @@ -504,7 +504,7 @@ private T[] ReadSampleData<T>()
/// <exception cref="InvalidEnumArgumentException">
/// Thrown if the <see cref="AudioFormat"/> is not a valid enum value.
/// </exception>
[SuppressMessage("ReSharper", "ForCanBeConvertedToForeach", Justification = "Need as for loop for reference to buffer id.")]
[SuppressMessage("ReSharper", "ForCanBeConvertedToForeach", Justification = "Need for loop for reference to buffer id.")]
private void FillBuffersFromStart()
{
var bufferStats = new BufferStats
Expand Down
2 changes: 1 addition & 1 deletion CASL/Devices/AudioDeviceManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public ImmutableArray<string> GetDeviceNames()
var result = this.alInvoker.GetDeviceList()
.Select(n => n.Replace(DeviceNamePrefix, string.Empty, StringComparison.Ordinal)).ToArray();

return result.ToImmutableArray();
return [..result];
}

/// <inheritdoc/>
Expand Down
2 changes: 1 addition & 1 deletion CASL/DotnetWrappers/ITaskService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace CASL.DotnetWrappers;
internal interface ITaskService : IDisposable
{
/// <summary>
/// Gets a value indicating whether or not the task has ran to completion.
/// Gets a value indicating whether the task has run to completion.
/// </summary>
bool IsCompletedSuccessfully { get; }

Expand Down
Loading
Loading