Skip to content

Commit

Permalink
Merge pull request #25 from Tornado-Technology/audio-management
Browse files Browse the repository at this point in the history
Audio management
  • Loading branch information
Tornado-Technology authored Jul 14, 2024
2 parents dfbf30f + 8834e4b commit dec412f
Show file tree
Hide file tree
Showing 23 changed files with 753 additions and 2 deletions.
9 changes: 9 additions & 0 deletions Hypercube.Client/Audio/AudioFormat.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Hypercube.Client.Audio;

public enum AudioFormat
{
Mono8 = 0x1100,
Mono16 = 0x1101,
Stereo8 = 0x1102,
Stereo16 = 0x1103
}
44 changes: 44 additions & 0 deletions Hypercube.Client/Audio/AudioId.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
namespace Hypercube.Client.Audio;

public readonly struct AudioId(int value) : IEquatable<AudioId>
{
public static readonly AudioId Invalid = new(-1);
public static readonly AudioId Zero = new(0);

public readonly int Value = value;

public bool Equals(AudioId other)
{
return Value == other.Value;
}

public override bool Equals(object? obj)
{
return obj is AudioId id && Equals(id);
}

public static bool operator ==(AudioId a, AudioId b)
{
return a.Equals(b);
}

public static bool operator !=(AudioId a, AudioId b)
{
return !a.Equals(b);
}

public static implicit operator int(AudioId id)
{
return id.Value;
}

public override int GetHashCode()
{
return Value;
}

public override string ToString()
{
return $"audio({Value})";
}
}
3 changes: 3 additions & 0 deletions Hypercube.Client/Audio/AudioSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace Hypercube.Client.Audio;

public readonly struct AudioSettings;
13 changes: 13 additions & 0 deletions Hypercube.Client/Audio/AudioStream.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Hypercube.Client.Audio;

public sealed class AudioStream
{
public readonly AudioId Id;
public readonly TimeSpan Length;

public AudioStream(AudioId id, TimeSpan length)
{
Id = id;
Length = length;
}
}
52 changes: 52 additions & 0 deletions Hypercube.Client/Audio/AudioType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
namespace Hypercube.Client.Audio;

/// <remarks>
/// Taken from <a href="https://en.wikipedia.org/wiki/Audio_file_format">Wikipedia</a>.
/// </remarks>
public enum AudioType
{
_3gp,
Aa,
Aac,
Aax,
Act,
Aiff,
Alac,
Amr,
Ape,
Au,
Awb,
Dss,
Dvf,
Flac,
Gsm,
Iklax,
Ivs,
M4A,
M4B,
M4P,
Mmf,
Movpkg,
Mp3,
Mpc,
Msv,
Nmf,
Ogg,
Oga,
Mogg,
Opus,
Ra,
Rm,
Raw,
Rf64,
Sln,
Tta,
Voc,
Vox,
Wav,
Wma,
Wv,
Webm,
_8svx,
Cda,
}
9 changes: 9 additions & 0 deletions Hypercube.Client/Audio/IAudioData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Hypercube.Client.Audio;

public interface IAudioData
{
AudioFormat Format { get; }
ReadOnlyMemory<byte> Data { get; }
int SampleRate { get; }
TimeSpan Length { get; }
}
11 changes: 11 additions & 0 deletions Hypercube.Client/Audio/IAudioGroup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Hypercube.Client.Audio;

public interface IAudioGroup
{
float Gain { get; }

void SetGain(float gain);

void PauseAll();
void StopAll();
}
22 changes: 22 additions & 0 deletions Hypercube.Client/Audio/IAudioManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Hypercube.Shared.Resources;

namespace Hypercube.Client.Audio;

public interface IAudioManager
{
IAudioSource CreateSource(ResourcePath path, AudioSettings settings);
IAudioSource CreateSource(AudioStream stream);
AudioStream GetAudio(ResourcePath path, AudioSettings settings);

void Initialize();

/// <summary>
/// Pause all audio sources.
/// </summary>
void PauseAll();

/// <summary>
/// Stops all audio sources.
/// </summary>
void StopAll();
}
16 changes: 16 additions & 0 deletions Hypercube.Client/Audio/IAudioSource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace Hypercube.Client.Audio;

public interface IAudioSource : IDisposable
{
IAudioGroup? Group { get; }
bool Playing { get; }

bool Looping { get; set; }
float Pitch { get; set; }
float Gain { get; set; }

void Start();
void Stop();
void Restart();
void Pause();
}
18 changes: 18 additions & 0 deletions Hypercube.Client/Audio/Loading/AudioLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Collections.Frozen;
using Hypercube.Client.Audio.Loading.Type;

namespace Hypercube.Client.Audio.Loading;

public sealed class AudioLoader : IAudioLoader
{
private readonly FrozenDictionary<AudioType, IAudioTypeLoader> _loaders = new Dictionary<AudioType, IAudioTypeLoader>
{
{ AudioType.Wav, new AudioWavLoader() }
}.ToFrozenDictionary();


public IAudioData LoadAudioData(Stream stream, AudioType type)
{
return _loaders[type].LoadAudioData(stream);
}
}
6 changes: 6 additions & 0 deletions Hypercube.Client/Audio/Loading/IAudioLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Hypercube.Client.Audio.Loading;

public interface IAudioLoader
{
IAudioData LoadAudioData(Stream stream, AudioType type);
}
12 changes: 12 additions & 0 deletions Hypercube.Client/Audio/Loading/Type/AudioWavLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Hypercube.Client.Audio.Reader.Wav;

namespace Hypercube.Client.Audio.Loading.Type;

public sealed class AudioWavLoader : IAudioTypeLoader
{
public IAudioData LoadAudioData(Stream stream)
{
var reader = new AudioWavReader(stream);
return reader.Read();
}
}
6 changes: 6 additions & 0 deletions Hypercube.Client/Audio/Loading/Type/IAudioTypeLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Hypercube.Client.Audio.Loading.Type;

public interface IAudioTypeLoader
{
IAudioData LoadAudioData(Stream stream);
}
61 changes: 61 additions & 0 deletions Hypercube.Client/Audio/Reader/Wav/AudioWavData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
namespace Hypercube.Client.Audio.Reader.Wav;

/// <summary>
/// Read the <a href="http://soundfile.sapp.org/doc/WaveFormat/">specification</a> for more information
/// </summary>
/// <remarks>
/// 8-bit samples are stored as unsigned bytes, ranging from 0 to 255.
/// 16-bit samples are stored as 2's-complement signed integers, ranging from -32768 to 32767.
/// </remarks>
public readonly struct AudioWavData : IAudioData
{
public AudioFormat Format { get; }
public ReadOnlyMemory<byte> Data { get; }
public int SampleRate { get; }
public TimeSpan Length { get; }

public readonly short FormatType;
public readonly short Channels;
public readonly int ByteRate;
public readonly short BlockAlign;
public readonly short BitsPerSample;

public AudioWavData(short formatType, short channels, int sampleRate, int byteRate, short blockAlign, short bitsPerSample, ReadOnlyMemory<byte> data)
{
Format = GetFormat(channels, bitsPerSample);
Data = data;
SampleRate = sampleRate;
Length = TimeSpan.FromSeconds(data.Length / (double) blockAlign / sampleRate);

FormatType = formatType;
Channels = channels;
ByteRate = byteRate;
BlockAlign = blockAlign;
BitsPerSample = bitsPerSample;
}

public override string ToString()
{
return $"format type {FormatType}, channels {Channels}, sample rate {SampleRate}, byte rate {ByteRate}, block align {BlockAlign}, bits per sample {BitsPerSample}, data length {Data.Length}";
}

private static AudioFormat GetFormat(int channels, int bits)
{
return bits switch
{
8 => channels switch
{
1 => AudioFormat.Mono8,
2 => AudioFormat.Stereo8,
_ => throw new InvalidOperationException()
},
16 => channels switch
{
1 => AudioFormat.Mono16,
2 => AudioFormat.Stereo16,
_ => throw new InvalidOperationException()
},
_ => throw new InvalidOperationException()
};
}
}
Loading

0 comments on commit dec412f

Please sign in to comment.