// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) VRMADA, All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- using System; using System.IO; using UltimateXR.Extensions.System; using UnityEngine; namespace UltimateXR.Extensions.Unity.Audio { public static partial class AudioClipExt { #region Public Types & Data /// /// Describes a PCM audio clip. /// public sealed class StreamedPcmClip : IDisposable { #region Public Types & Data /// /// Gets the described by the object. /// public AudioClip InnerClip { get; } #endregion #region Constructors & Finalizer /// /// Constructor. /// /// PCM data /// Name assigned to the audio clip /// PCM data header private StreamedPcmClip(Stream pcmStream, string clipName, in PcmHeader header) { _pcmHeader = header; _pcmStream = pcmStream; _pcmReader = new BinaryReader(pcmStream); InnerClip = AudioClip.Create(clipName, header.AudioSampleCount, header.Channels, header.SampleRate, true, OnPcmRead, OnPcmSetPosition); } #endregion #region Implicit IDisposable /// public void Dispose() { _pcmReader.Dispose(); } #endregion #region Public Methods /// /// Creates a object from a data stream. /// /// Source data stream /// Name that will be assigned to the clip /// describing the PCM audio clip /// The bit depth is not supported public static StreamedPcmClip Create(Stream pcmStream, string clipName = "pcm") { pcmStream.ThrowIfNull(nameof(pcmStream)); clipName.ThrowIfNullOrWhitespace(nameof(clipName)); var pcmHeader = PcmHeader.FromStream(pcmStream); if (pcmHeader.BitDepth != 16 && pcmHeader.BitDepth != 32 && pcmHeader.BitDepth != 8) { throw new ArgumentOutOfRangeException(nameof(pcmHeader.BitDepth), pcmHeader.BitDepth, "Supported values are: 8, 16, 32"); } return new StreamedPcmClip(pcmStream, clipName, in pcmHeader); } #endregion #region Event Trigger Methods /// /// PCM reader callback. /// /// Source data /// Unsupported audio bit depth private void OnPcmRead(float[] data) { for (int i = 0; i < data.Length && _pcmStream.Position < _pcmStream.Length; ++i) { float rawSample; switch (_pcmHeader.AudioSampleSize) { case 1: rawSample = _pcmReader.ReadByte(); break; case 2: rawSample = _pcmReader.ReadInt16(); break; case 3: rawSample = _pcmReader.ReadInt32(); break; default: throw new ArgumentOutOfRangeException(nameof(_pcmHeader.BitDepth), _pcmHeader.BitDepth, "Supported values are: 8, 16, 32"); } data[i] = _pcmHeader.NormalizeSample(rawSample); // needs to be scaled to be within the range of - 1.0f to 1.0f. } } /// /// PCM reader positioning callback. /// /// New index where to position the read cursor private void OnPcmSetPosition(int newPosition) { _pcmStream.Position = _pcmHeader.AudioStartIndex + newPosition * _pcmHeader.AudioSampleSize; } #endregion #region Private Types & Data private readonly Stream _pcmStream; private readonly BinaryReader _pcmReader; private readonly PcmHeader _pcmHeader; #endregion } #endregion } }