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