// -------------------------------------------------------------------------------------------------------------------- // // 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 } }