// --------------------------------------------------------------------------------------------------------------------
//
// Copyright (c) VRMADA, All rights reserved.
//
// --------------------------------------------------------------------------------------------------------------------
using System;
using UltimateXR.Extensions.System;
namespace UltimateXR.Extensions.Unity.Audio
{
public static partial class AudioClipExt
{
#region Private Types & Data
///
/// Container of PCM audio data.
///
private readonly struct PcmData
{
#region Public Types & Data
///
/// Gets the sample data.
///
public float[] Value { get; }
///
/// Gets the sample count.
///
public int Length { get; }
///
/// Gets the number of audio channels.
///
public int Channels { get; }
///
/// Gets the sample rate in Hz.
///
public int SampleRate { get; }
#endregion
#region Constructors & Finalizer
///
/// Constructor.
///
/// Sample data
/// Audio channel count
/// Sample rate in Hz
private PcmData(float[] value, int channels, int sampleRate)
{
Value = value;
Length = value.Length;
Channels = channels;
SampleRate = sampleRate;
}
#endregion
#region Public Methods
///
/// Creates a object from a byte data array.
///
/// Byte data array with the PCM header and sample data
/// object with the audio data
/// The PCM header contains invalid data
public static PcmData FromBytes(byte[] bytes)
{
bytes.ThrowIfNull(nameof(bytes));
PcmHeader pcmHeader = PcmHeader.FromBytes(bytes);
if (pcmHeader.BitDepth != 16 && pcmHeader.BitDepth != 32 && pcmHeader.BitDepth != 8)
{
throw new ArgumentOutOfRangeException(nameof(pcmHeader.BitDepth), pcmHeader.BitDepth, "Supported values are: 8, 16, 32");
}
float[] samples = new float[pcmHeader.AudioSampleCount];
for (int i = 0; i < samples.Length; ++i)
{
int byteIndex = pcmHeader.AudioStartIndex + i * pcmHeader.AudioSampleSize;
float rawSample;
switch (pcmHeader.BitDepth)
{
case 8:
rawSample = bytes[byteIndex];
break;
case 16:
rawSample = BitConverter.ToInt16(bytes, byteIndex);
break;
case 32:
rawSample = BitConverter.ToInt32(bytes, byteIndex);
break;
default: throw new ArgumentOutOfRangeException(nameof(pcmHeader.BitDepth), pcmHeader.BitDepth, "Supported values are: 8, 16, 32");
}
samples[i] = pcmHeader.NormalizeSample(rawSample); // normalize sample between [-1f, 1f]
}
return new PcmData(samples, pcmHeader.Channels, pcmHeader.SampleRate);
}
#endregion
}
#endregion
}
}