using System; #if NETFX_CORE using Windows.UI.Xaml; using TimeObject = System.Object; #else using TimeObject = System.Timers.ElapsedEventArgs; #endif namespace Photon.Voice { public static partial class AudioUtil { /// IAudioReader that provides a constant tone signal. /// Because of current resampling algorithm, the tone is distorted if SamplingRate does not equal encoder sampling rate. public class ToneAudioReader : IAudioReader { /// Create a new ToneAudioReader instance /// Function to get current time in seconds. In Unity, pass in '() => AudioSettings.dspTime' for better results. /// Frequency of the generated tone (in Hz). /// Sampling rate of the audio signal (in Hz). /// Number of channels in the audio signal. public ToneAudioReader(Func clockSec = null, double frequency = 440, int samplingRate = 48000, int channels = 2) { this.clockSec = clockSec == null ? () => DateTime.Now.Ticks / 10000000.0 : clockSec; this.samplingRate = samplingRate; this.channels = channels; k = 2 * Math.PI * frequency / SamplingRate; } /// Number of channels in the audio signal. public int Channels { get { return channels; } } /// Sampling rate of the audio signal (in Hz). public int SamplingRate { get { return samplingRate; } } /// If not null, audio object is in invalid state. public string Error { get; private set; } public void Dispose() { } double k; long timeSamples; Func clockSec; int samplingRate; int channels; public bool Read(T[] buf) { var bufSamples = buf.Length / Channels; var t = (long)(clockSec() * SamplingRate); var deltaTimeSamples = t - timeSamples; if (Math.Abs(deltaTimeSamples) > SamplingRate / 4) // when started or Read has not been called for a while { deltaTimeSamples = bufSamples; timeSamples = t - bufSamples; } if (deltaTimeSamples < bufSamples) { return false; } else { int x = 0; if (buf is float[]) { for (int i = 0; i < bufSamples; i++) { var b = buf as float[]; var v = (float)(System.Math.Sin(timeSamples++ * k) * 0.2f); for (int j = 0; j < Channels; j++) b[x++] = v; } } else if (buf is short[]) { var b = buf as short[]; for (int i = 0; i < bufSamples; i++) { var v = (short)(System.Math.Sin(timeSamples++ * k) * (0.2f * short.MaxValue)); for (int j = 0; j < Channels; j++) b[x++] = v; } } return true; } } } /// IAudioPusher that provides a constant tone signal. // Helpful for debug but does not compile for UWP because of System.Timers.Timer. public class ToneAudioPusher : IAudioPusher { /// Create a new ToneAudioReader instance /// Frequency of the generated tone (in Hz). /// Size of buffers to push (in milliseconds). /// Sampling rate of the audio signal (in Hz). /// Number of channels in the audio signal. public ToneAudioPusher(int frequency = 440, int bufSizeMs = 100, int samplingRate = 48000, int channels = 2) { this.samplingRate = samplingRate; this.channels = channels; this.bufSizeSamples = bufSizeMs * SamplingRate / 1000; k = 2 * Math.PI * frequency/ SamplingRate; } double k; #if NETFX_CORE DispatcherTimer timer; #else System.Timers.Timer timer; #endif Action callback; ObjectFactory bufferFactory; /// Set the callback function used for pushing data /// Callback function to use /// Buffer factory used to create the buffer that is pushed to the callback public void SetCallback(Action callback, ObjectFactory bufferFactory) { if (timer != null) { Dispose(); } this.callback = callback; this.bufferFactory = bufferFactory; // Hook up the Elapsed event for the timer. #if NETFX_CORE timer = new DispatcherTimer(); timer.Tick += OnTimedEvent; timer.Interval = new TimeSpan(10000000 * bufSizeSamples / SamplingRate); // ticks (10 000 000 per sec) in single buffer #else timer = new System.Timers.Timer(1000.0 * bufSizeSamples / SamplingRate); timer.Elapsed += new System.Timers.ElapsedEventHandler(OnTimedEvent); timer.Enabled = true; #endif } private void OnTimedEvent(object source, TimeObject e) { var buf = bufferFactory.New(bufSizeSamples * Channels); int x = 0; if (buf is float[]) { var b = buf as float[]; for (int i = 0; i < bufSizeSamples; i++) { var v = (float)(System.Math.Sin((posSamples + i) * k) / 2); for (int j = 0; j < Channels; j++) b[x++] = v; } } else if (buf is short[]) { var b = buf as short[]; for (int i = 0; i < bufSizeSamples; i++) { var v = (short)(System.Math.Sin((posSamples + i) * k) * short.MaxValue / 2); for (int j = 0; j < Channels; j++) b[x++] = v; } } cntFrame++; posSamples += bufSizeSamples; this.callback(buf); } int cntFrame; int posSamples; int bufSizeSamples; int samplingRate; int channels; public int Channels { get { return channels; } } public int SamplingRate { get { return samplingRate; } } public string Error { get; private set; } public void Dispose() { if (timer != null) { #if NETFX_CORE timer.Stop(); #else timer.Close(); #endif } } } } }