2022-06-29 14:45:17 +03:00

193 lines
7.7 KiB
C#

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
{
/// <summary>IAudioReader that provides a constant tone signal.</summary>
/// Because of current resampling algorithm, the tone is distorted if SamplingRate does not equal encoder sampling rate.
public class ToneAudioReader<T> : IAudioReader<T>
{
/// <summary>Create a new ToneAudioReader instance</summary>
/// <param name="clockSec">Function to get current time in seconds. In Unity, pass in '() => AudioSettings.dspTime' for better results.</param>
/// <param name="frequency">Frequency of the generated tone (in Hz).</param>
/// <param name="samplingRate">Sampling rate of the audio signal (in Hz).</param>
/// <param name="channels">Number of channels in the audio signal.</param>
public ToneAudioReader(Func<double> 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;
}
/// <summary>Number of channels in the audio signal.</summary>
public int Channels { get { return channels; } }
/// <summary>Sampling rate of the audio signal (in Hz).</summary>
public int SamplingRate { get { return samplingRate; } }
/// <summary>If not null, audio object is in invalid state.</summary>
public string Error { get; private set; }
public void Dispose()
{
}
double k;
long timeSamples;
Func<double> 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;
}
}
}
/// <summary>IAudioPusher that provides a constant tone signal.</summary>
// Helpful for debug but does not compile for UWP because of System.Timers.Timer.
public class ToneAudioPusher<T> : IAudioPusher<T>
{
/// <summary>Create a new ToneAudioReader instance</summary>
/// <param name="frequency">Frequency of the generated tone (in Hz).</param>
/// <param name="bufSizeMs">Size of buffers to push (in milliseconds).</param>
/// <param name="samplingRate">Sampling rate of the audio signal (in Hz).</param>
/// <param name="channels">Number of channels in the audio signal.</param>
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<T[]> callback;
ObjectFactory<T[], int> bufferFactory;
/// <summary>Set the callback function used for pushing data</summary>
/// <param name="callback">Callback function to use</param>
/// <param name="bufferFactory">Buffer factory used to create the buffer that is pushed to the callback</param>
public void SetCallback(Action<T[]> callback, ObjectFactory<T[], int> 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
}
}
}
}
}