// ----------------------------------------------------------------------- // // Photon Voice API Framework for Photon - Copyright (C) 2017 Exit Games GmbH // // // Photon data streaming support. // // developer@photonengine.com // ---------------------------------------------------------------------------- using System; using System.Collections.Generic; namespace Photon.Voice { /// Utility class to re-frame packets. public class Framer { protected T[] frame; /// Create new Framer instance. public Framer(int frameSize) { this.frame = new T[frameSize]; var x = new T[1]; if (x[0] is byte) this.sizeofT = sizeof(byte); else if (x[0] is short) this.sizeofT = sizeof(short); else if (x[0] is float) this.sizeofT = sizeof(float); else throw new Exception("Input data type is not supported: " + x[0].GetType()); } protected int sizeofT; protected int framePos = 0; /// Append arbitrary-sized buffer and return available full frames. /// Array of samples to add. /// Enumerator of full frames (might be none). public virtual IEnumerable Frame(T[] buf) { // quick return in trivial case if (frame.Length == buf.Length && framePos == 0) { yield return buf; } else { var bufPos = 0; while (frame.Length - framePos <= buf.Length - bufPos) { var l = frame.Length - framePos; Buffer.BlockCopy(buf, bufPos * sizeofT, frame, framePos * sizeofT, l * sizeofT); //Console.WriteLine("=== Y {0} {1} -> {2} {3} ", bufPos, bufPos + l, sourceFramePos, sourceFramePos + l); bufPos += l; framePos = 0; yield return this.frame; } if (bufPos != buf.Length) { var l = buf.Length - bufPos; Buffer.BlockCopy(buf, bufPos * sizeofT, frame, framePos * sizeofT, l * sizeofT); //Console.WriteLine("=== L {0} {1} -> {2} {3} ", bufPos, bufPos + l, sourceFramePos, sourceFramePos + l); framePos += l; } } } } public class FramerResampler : Framer { protected bool TisFloat; protected bool interpolate; protected int channels; protected int resampleNum; protected int resampleDen; protected float resampleRatioInv; protected int delta; T inSampleCh1; T inSampleCh2; float inSampleCh1Interp; float inSampleCh2Interp; float inSampleCh1InterpChange; // duff between current and previous samples values float inSampleCh2InterpChange; public FramerResampler(int frameSize, int channels, int resampleNum, int resampleDen, bool interpolate) : base(frameSize / channels * channels) { this.TisFloat = default(T) is float; this.channels = channels; this.resampleNum = resampleNum; this.resampleDen = resampleDen; this.interpolate = interpolate; this.resampleRatioInv = (float)resampleDen / resampleNum; } public override IEnumerable Frame(T[] bufT) { int bufPos = 0; int bufLen = bufT.Length / channels * channels; if (!interpolate) { switch (channels) { case 1: while (bufPos < bufLen) { if (delta <= 0) { inSampleCh1 = bufT[bufPos++]; delta += resampleNum; } while (delta > 0) { this.frame[framePos++] = inSampleCh1; if (framePos == this.frame.Length) { yield return this.frame; framePos = 0; } delta -= resampleDen; } } break; case 2: while (bufPos < bufLen) { if (delta <= 0) { inSampleCh1 = bufT[bufPos++]; inSampleCh2 = bufT[bufPos++]; delta += resampleNum; } while (delta > 0) { this.frame[framePos++] = inSampleCh1; this.frame[framePos++] = inSampleCh2; if (framePos == this.frame.Length) { yield return this.frame; framePos = 0; } delta -= resampleDen; } } break; } } else { // float and short code differ only in type casts for arrays and values stored to frame if (TisFloat) { var buf = bufT as float[]; var frame = this.frame as float[]; // interpolation factor float deltaK = (float)delta / resampleNum; // update regularly from delta to avoid float precision loss switch (channels) { case 1: while (bufPos < bufLen) { if (delta <= 0) { var x = buf[bufPos++]; inSampleCh1InterpChange = inSampleCh1Interp - x; inSampleCh1Interp = x; delta += resampleNum; deltaK += 1.0f; } while (delta > 0) { frame[framePos++] = inSampleCh1Interp + inSampleCh1InterpChange * deltaK; if (framePos == frame.Length) { yield return this.frame; framePos = 0; } delta -= resampleDen; deltaK -= resampleRatioInv; } } break; case 2: while (bufPos < bufLen) { if (delta <= 0) { var x1 = buf[bufPos++]; var x2 = buf[bufPos++]; inSampleCh1InterpChange = inSampleCh1Interp - x1; inSampleCh2InterpChange = inSampleCh2Interp - x2; inSampleCh1Interp = x1; inSampleCh2Interp = x2; delta += resampleNum; deltaK += 1.0f; } while (delta > 0) { frame[framePos++] = inSampleCh1Interp + inSampleCh1InterpChange * deltaK; frame[framePos++] = inSampleCh2Interp + inSampleCh2InterpChange * deltaK; if (framePos == frame.Length) { yield return this.frame; framePos = 0; } delta -= resampleDen; deltaK -= resampleRatioInv; } } break; } } else { var buf = bufT as short[]; var frame = this.frame as short[]; // interpolation factor float deltaK = (float)delta / resampleNum; // update regularly from delta to avoid float precision loss switch (channels) { case 1: while (bufPos < bufLen) { if (delta <= 0) { var x = buf[bufPos++]; inSampleCh1InterpChange = inSampleCh1Interp - x; inSampleCh1Interp = x; delta += resampleNum; deltaK += 1.0f; } while (delta > 0) { frame[framePos++] = (short)(inSampleCh1Interp + inSampleCh1InterpChange * deltaK); if (framePos == frame.Length) { yield return this.frame; framePos = 0; } delta -= resampleDen; deltaK -= resampleRatioInv; } } break; case 2: while (bufPos < bufLen) { if (delta <= 0) { var x1 = buf[bufPos++]; var x2 = buf[bufPos++]; inSampleCh1InterpChange = inSampleCh1Interp - x1; inSampleCh2InterpChange = inSampleCh2Interp - x2; inSampleCh1Interp = x1; inSampleCh2Interp = x2; delta += resampleNum; deltaK += 1.0f; } while (delta > 0) { frame[framePos++] = (short)(inSampleCh1Interp + inSampleCh1InterpChange * deltaK); frame[framePos++] = (short)(inSampleCh2Interp + inSampleCh2InterpChange * deltaK); if (framePos == frame.Length) { yield return this.frame; framePos = 0; } delta -= resampleDen; deltaK -= resampleRatioInv; } } break; } } } } } }