SDRSharper/SDRSharper.Radio/SDRSharp.Radio/StreamControl.cs
SDRSharpR c07e6e6034 SDRSharper (SDRSharp Remake) Full Source (VS2017)
SDRSharper (SDRSharp Remake) Full Source (VS2017)
2018-03-26 14:02:05 -07:00

642 lines
18 KiB
C#

using SDRSharp.Radio.PortAudio;
using System;
using System.IO;
using System.Threading;
namespace SDRSharp.Radio
{
public sealed class StreamControl : IDisposable
{
private const int WaveBufferSize = 65536;
private const int MaxDecimationFactor = 1024;
private static readonly int _processorCount = Utils.ProcessorCount;
private int _minOutputSampleRate = Utils.GetIntSetting("minOutputSampleRate", 18000);
private unsafe float* _dspOutPtr;
private UnsafeBuffer _dspOutBuffer;
private unsafe Complex* _iqInPtr;
private UnsafeBuffer _iqInBuffer;
private unsafe Complex* _dspInPtr;
private UnsafeBuffer _dspInBuffer;
private WavePlayer _wavePlayer;
private WaveRecorder _waveRecorder;
private WaveDuplex _waveDuplex;
private WaveFile _waveFile;
private string _fileName;
private ComplexFifoStream _iqStream;
private FloatFifoStream _audioStream;
private Thread _waveReadThread;
private Thread _dspThread;
private DateTime _waveStart;
private float _audioGain;
private float _outputGain;
private int _inputDevice;
private double _inputSampleRate;
private int _inputBufferSize;
private int _bufferSizeInMs;
private int _outputDevice;
private double _outputSampleRate;
private int _outputBufferSize;
private double _soundCardRatio = 1.0;
private int _decimationStageCount;
private bool _swapIQ;
private bool _useASIO;
private InputType _inputType;
private IFrontendController _frontend;
private bool _isBuffering;
private float _clipLevel;
public DateTime WaveStart
{
get
{
return this._waveStart;
}
set
{
this._waveStart = value;
}
}
public string FileName => this._fileName;
public WaveFile WaveFile => this._waveFile;
public int MinOutputSampleRate
{
get
{
return this._minOutputSampleRate;
}
set
{
this._minOutputSampleRate = value;
}
}
public double SoundCardRatio => this._soundCardRatio;
public int AudioStreamSize
{
get
{
if (this._audioStream == null)
{
return 0;
}
return this._audioStream.Length;
}
}
public float AudioGain
{
get
{
return this._audioGain;
}
set
{
this._audioGain = value;
this._outputGain = (float)Math.Pow((double)value / 10.0, 10.0);
this._outputGain = (float)Math.Pow(10.0, (double)value / 10.0);
}
}
public bool SwapIQ
{
get
{
return this._swapIQ;
}
set
{
this._swapIQ = value;
}
}
public bool UseASIO
{
get
{
return this._useASIO;
}
set
{
this._useASIO = value;
}
}
public double SampleRate => this._inputSampleRate;
public bool IsPlaying => this._inputSampleRate != 0.0;
public bool IsBuffering
{
get
{
return this._isBuffering;
}
set
{
this._isBuffering = false;
}
}
public int BufferSize => this._inputBufferSize;
public int BufferSizeInMs => this._bufferSizeInMs;
public int DecimationStageCount => this._decimationStageCount;
public InputType InputType => this._inputType;
public float ClipLevel => this._clipLevel;
public event ProcessBufferDelegate ProcessBufferPtr;
public StreamControl()
{
this.AudioGain = 0f;
}
~StreamControl()
{
this.Dispose();
}
public void Dispose()
{
this.Stop();
GC.SuppressFinalize(this);
}
private unsafe void DuplexFiller(float* buffer, int frameCount)
{
if (this._dspInBuffer == null || this._dspInBuffer.Length != frameCount)
{
this._dspInBuffer = UnsafeBuffer.Create(frameCount, sizeof(Complex));
this._dspInPtr = (Complex*)(void*)this._dspInBuffer;
}
if (this._dspOutBuffer == null || this._dspOutBuffer.Length != this._dspInBuffer.Length * 2)
{
this._dspOutBuffer = UnsafeBuffer.Create(this._dspInBuffer.Length * 2, 4);
this._dspOutPtr = (float*)(void*)this._dspOutBuffer;
}
Utils.Memcpy(this._dspInPtr, buffer, frameCount * sizeof(Complex));
this.ProcessBuffer();
this.ScaleBuffer(this._dspOutPtr, this._dspOutBuffer.Length);
Utils.Memcpy(buffer, this._dspOutPtr, this._dspOutBuffer.Length * 4);
}
private unsafe void PlayerFiller(float* buffer, int frameCount)
{
int num = frameCount * 2;
int num2 = this._audioStream.Read(buffer, num);
this.ScaleBuffer(buffer, num2);
this._clipLevel = 0f;
for (int i = 0; i < num2; i++)
{
this._clipLevel = Math.Max(this._clipLevel, Math.Abs(buffer[i]));
}
for (int j = num2; j < num; j++)
{
buffer[j] = 0f;
}
}
private unsafe void RecorderFiller(float* buffer, int frameCount)
{
if (this._iqStream.Length <= this._inputBufferSize * 2)
{
if (this._iqInBuffer == null || this._iqInBuffer.Length != frameCount)
{
this._iqInBuffer = UnsafeBuffer.Create(frameCount, sizeof(Complex));
this._iqInPtr = (Complex*)(void*)this._iqInBuffer;
}
Utils.Memcpy(this._iqInPtr, buffer, frameCount * sizeof(Complex));
this._iqStream.Write(this._iqInPtr, frameCount);
}
}
private unsafe void FrontendFiller(IFrontendController sender, Complex* samples, int len)
{
if (this._iqStream.Length < this._inputBufferSize * 4)
{
this._iqStream.Write(samples, len);
}
}
private unsafe void WaveFileFiller()
{
Complex[] array = new Complex[65536];
int num = 0;
Complex[] array2 = array;
fixed (Complex* ptr = array2)
{
Console.WriteLine("WaveFiller started");
while (this.IsPlaying)
{
if (this._iqStream.Length < this._inputBufferSize * 4)
{
int num2 = this._waveFile.Read(ptr, array.Length, ref num);
this._iqStream.Write(ptr, num2);
if (num2 < array.Length)
{
string fileName = this._waveFile.FileName;
int num3 = fileName.ToLower().LastIndexOf(".wav");
if (int.TryParse(fileName.Substring(num3 - 2, 2), out int num4))
{
num3 -= 2;
}
string str = fileName.Substring(0, num3);
int num5 = ++num4;
string text = str + num5.ToString("00") + ".wav";
if (!File.Exists(text))
{
text = this._fileName;
this._waveStart = DateTime.Now;
}
this._waveFile.Close();
this._waveFile.Dispose();
this._waveFile = new WaveFile(text);
}
}
else
{
Thread.Sleep(2);
}
}
Console.WriteLine("WaveFiller stopped");
}
}
private unsafe void ScaleBuffer(float* buffer, int length)
{
for (int i = 0; i < length; i++)
{
buffer[i] *= this._outputGain;
}
}
private unsafe void DSPProc()
{
if (this._dspInBuffer == null || this._dspInBuffer.Length != this._inputBufferSize)
{
this._dspInBuffer = UnsafeBuffer.Create(this._inputBufferSize, sizeof(Complex));
this._dspInPtr = (Complex*)(void*)this._dspInBuffer;
}
if (this._dspOutBuffer == null || this._dspOutBuffer.Length != this._outputBufferSize)
{
this._dspOutBuffer = UnsafeBuffer.Create(this._outputBufferSize, 4);
this._dspOutPtr = (float*)(void*)this._dspOutBuffer;
}
Console.WriteLine("DSPPROC started");
while (this.IsPlaying)
{
int num = 0;
while (this.IsPlaying && num < this._dspInBuffer.Length)
{
int count = this._dspInBuffer.Length - num;
num += this._iqStream.Read(this._dspInPtr, num, count);
}
int count2 = this.ProcessBuffer();
this._audioStream.Write(this._dspOutPtr, count2);
}
this._isBuffering = false;
Console.WriteLine("DSPPROC stopped");
}
private unsafe int ProcessBuffer()
{
if (this.ProcessBufferPtr == null)
{
return 0;
}
if (this._swapIQ)
{
this.swapIQBuffer();
}
this._isBuffering = true;
var x = this.ProcessBufferPtr(this._dspInPtr, this._dspOutPtr, this._dspInBuffer.Length, this._dspOutBuffer.Length);
return x;
}
private unsafe void swapIQBuffer()
{
for (int i = 0; i < this._dspInBuffer.Length; i++)
{
float real = this._dspInPtr[i].Real;
this._dspInPtr[i].Real = this._dspInPtr[i].Imag;
this._dspInPtr[i].Imag = real;
}
}
public void Stop()
{
if (this._inputType == InputType.Plugin && this._frontend != null)
{
this._frontend.Stop();
this._frontend = null;
}
if (this._wavePlayer != null)
{
this._wavePlayer.Dispose();
this._wavePlayer = null;
}
if (this._waveRecorder != null)
{
this._waveRecorder.Dispose();
this._waveRecorder = null;
}
if (this._waveDuplex != null)
{
this._waveDuplex.Dispose();
this._waveDuplex = null;
}
this._inputSampleRate = 0.0;
if (this._waveReadThread != null)
{
this._waveReadThread.Join();
this._waveReadThread = null;
}
if (this._iqStream != null)
{
this._iqStream.Close();
}
if (this._audioStream != null)
{
this._audioStream.Close();
}
if (this._dspThread != null)
{
this._dspThread.Join();
this._dspThread = null;
}
if (this._waveFile != null)
{
this._waveFile.Dispose();
this._waveFile = null;
}
if (this._iqStream != null)
{
this._iqStream.Dispose();
this._iqStream = null;
}
this._audioStream = null;
this._dspOutBuffer = null;
this._iqInBuffer = null;
}
public unsafe void Play()
{
if (this._wavePlayer == null && this._waveDuplex == null)
{
if (this._inputType != InputType.WaveFile)
{
this._waveStart = DateTime.Now.AddYears(1);
}
double sampleRate = this._outputSampleRate;
int framesPerBuffer = this._outputBufferSize / 2;
if (this._soundCardRatio > 1.0)
{
sampleRate = (double)this._minOutputSampleRate;
framesPerBuffer = (int)((double)this._outputBufferSize / (2.0 * this._soundCardRatio));
}
switch (this._inputType)
{
case InputType.SoundCard:
if (this._inputDevice == this._outputDevice)
{
this._waveDuplex = new WaveDuplex(this._inputDevice, this._inputSampleRate, this._inputBufferSize, this.DuplexFiller);
}
else
{
this._iqStream = new ComplexFifoStream(BlockMode.BlockingRead);
ComplexFifoStream iqStream2 = this._iqStream;
bool read2 = false;
iqStream2.SetLog("_iqStream", 20000, read2, true);
this._audioStream = new FloatFifoStream(BlockMode.BlockingWrite, this._outputBufferSize);
Console.WriteLine("_audioStream created with size " + this._outputBufferSize.ToString());
FloatFifoStream audioStream3 = this._audioStream;
bool write3 = false;
audioStream3.SetLog("_audioStream", this._outputBufferSize, true, write3);
this._waveRecorder = new WaveRecorder(this._inputDevice, this._inputSampleRate, this._inputBufferSize, this.RecorderFiller);
this._wavePlayer = new WavePlayer(this._outputDevice, sampleRate, framesPerBuffer, this.PlayerFiller);
this._dspThread = new Thread(this.DSPProc);
this._dspThread.Start();
}
break;
case InputType.WaveFile:
{
this._iqStream = new ComplexFifoStream(BlockMode.BlockingRead);
this._iqStream.SetLog("_iqStream", 20000, true, true);
this._audioStream = new FloatFifoStream(BlockMode.BlockingWrite, this._outputBufferSize * 2);
Console.WriteLine("_audioStream created with size " + (this._outputBufferSize * 2).ToString());
FloatFifoStream audioStream2 = this._audioStream;
bool write2 = false;
audioStream2.SetLog("_audioStream", this._outputBufferSize, true, write2);
this._wavePlayer = new WavePlayer(this._outputDevice, sampleRate, framesPerBuffer, this.PlayerFiller);
this._waveReadThread = new Thread(this.WaveFileFiller);
this._waveReadThread.Start();
this._dspThread = new Thread(this.DSPProc);
this._dspThread.Start();
break;
}
case InputType.Plugin:
{
this._iqStream = new ComplexFifoStream(BlockMode.BlockingRead);
ComplexFifoStream iqStream = this._iqStream;
bool read = false;
iqStream.SetLog("_iqStream", 20000, read, true);
this._audioStream = new FloatFifoStream(BlockMode.BlockingWrite, this._outputBufferSize * 2);
Console.WriteLine("_audioStream created with size " + (this._outputBufferSize * 2).ToString());
FloatFifoStream audioStream = this._audioStream;
bool write = false;
audioStream.SetLog("_audioStream", 0, true, write);
this._wavePlayer = new WavePlayer(this._outputDevice, sampleRate, framesPerBuffer, this.PlayerFiller);
this._frontend.Start(this.FrontendFiller);
this._dspThread = new Thread(this.DSPProc);
this._dspThread.Start();
break;
}
}
}
}
public void OpenSoundDevice(int inputDevice, int outputDevice, double inputSampleRate, int latency)
{
this.Stop();
this._inputType = InputType.SoundCard;
this._inputDevice = inputDevice;
this._outputDevice = outputDevice;
this._inputSampleRate = inputSampleRate;
this._bufferSizeInMs = latency;
this._inputBufferSize = (int)((double)this._bufferSizeInMs * this._inputSampleRate / 1000.0);
if (this._inputDevice == this._outputDevice)
{
this._decimationStageCount = 0;
this._inputBufferSize = this._inputBufferSize / StreamControl._processorCount * StreamControl._processorCount;
int num = (int)Math.Log((double)this._inputBufferSize, 2.0);
this._inputBufferSize = (int)Math.Pow(2.0, (double)num);
this._bufferSizeInMs = (int)Math.Round((double)this._inputBufferSize / this._inputSampleRate * 1000.0);
this._outputSampleRate = this._inputSampleRate;
this._outputBufferSize = this._inputBufferSize * 2;
this._soundCardRatio = 1.0;
}
else
{
this._decimationStageCount = this.GetDecimationStageCount();
int num2 = (int)Math.Pow(2.0, (double)this._decimationStageCount);
this._inputBufferSize = this._inputBufferSize / num2 * num2;
this._inputBufferSize = this._inputBufferSize / StreamControl._processorCount * StreamControl._processorCount;
int num3 = (int)Math.Log((double)this._inputBufferSize, 2.0);
this._inputBufferSize = (int)Math.Pow(2.0, (double)num3);
this._bufferSizeInMs = (int)Math.Round((double)this._inputBufferSize / this._inputSampleRate * 1000.0);
this._outputSampleRate = this._inputSampleRate / (double)num2;
this._outputBufferSize = this._inputBufferSize / num2 * 2;
if (this._outputSampleRate > 192000.0)
{
this._soundCardRatio = this._outputSampleRate / 192000.0;
}
else
{
this._soundCardRatio = (this._useASIO ? (this._outputSampleRate / (double)this._minOutputSampleRate) : 1.0);
}
}
Console.WriteLine("_inputBuffersize (complex) set to " + (Utils.FastConvolve ? "power of 2 : " : ": ") + this._inputBufferSize.ToString());
Console.WriteLine("_outputBuffersize (real) set to : " + this._outputBufferSize.ToString());
Console.WriteLine("_soundCardRatio set to : " + this._soundCardRatio.ToString());
}
public int OpenFile(string filename, int outputDevice, int latency)
{
this.Stop();
try
{
this._inputType = InputType.WaveFile;
this._fileName = filename;
this._waveFile = new WaveFile(filename);
this._waveStart = DateTime.Now;
this._outputDevice = outputDevice;
this._bufferSizeInMs = latency;
this._inputSampleRate = (double)this._waveFile.SampleRate;
this._decimationStageCount = this.GetDecimationStageCount();
this._inputBufferSize = (int)((double)this._bufferSizeInMs * this._inputSampleRate / 1000.0);
int num = (int)Math.Pow(2.0, (double)this._decimationStageCount);
this._inputBufferSize = this._inputBufferSize / num * num;
this._inputBufferSize = this._inputBufferSize / StreamControl._processorCount * StreamControl._processorCount;
int num2 = (int)Math.Log((double)this._inputBufferSize, 2.0);
this._inputBufferSize = (int)Math.Pow(2.0, (double)num2);
this._bufferSizeInMs = (int)Math.Round((double)this._inputBufferSize / this._inputSampleRate * 1000.0);
this._outputSampleRate = this._inputSampleRate / (double)num;
this._outputBufferSize = this._inputBufferSize / num * 2;
if (this._outputSampleRate > 192000.0)
{
this._soundCardRatio = this._outputSampleRate / 192000.0;
}
else
{
this._soundCardRatio = (this._useASIO ? (this._outputSampleRate / (double)this._minOutputSampleRate) : 1.0);
}
Console.WriteLine("_inputBuffersize (complex) set to " + (Utils.FastConvolve ? "power of 2 : " : ": ") + this._inputBufferSize.ToString());
Console.WriteLine("_outputBuffersize (real) set to : " + this._outputBufferSize.ToString());
Console.WriteLine("_soundCardRatio set to : " + this._soundCardRatio.ToString());
return this._waveFile.Duration;
}
catch
{
this.Stop();
throw;
}
}
public void OpenPlugin(IFrontendController frontend, int outputDevice, int latency)
{
this.Stop();
try
{
this._inputType = InputType.Plugin;
this._frontend = frontend;
this._inputSampleRate = this._frontend.Samplerate;
this._outputDevice = outputDevice;
this._bufferSizeInMs = latency;
this._inputBufferSize = (int)((double)this._bufferSizeInMs * this._inputSampleRate / 1000.0);
this._decimationStageCount = this.GetDecimationStageCount();
int num = (int)Math.Pow(2.0, (double)this._decimationStageCount);
this._inputBufferSize = this._inputBufferSize / num * num;
this._inputBufferSize = this._inputBufferSize / StreamControl._processorCount * StreamControl._processorCount;
int num2 = (int)Math.Log((double)this._inputBufferSize, 2.0);
this._inputBufferSize = (int)Math.Pow(2.0, (double)num2);
this._bufferSizeInMs = (int)Math.Round((double)this._inputBufferSize / this._inputSampleRate * 1000.0);
this._outputSampleRate = this._inputSampleRate / (double)num;
this._outputBufferSize = this._inputBufferSize / num * 2;
if (this._outputSampleRate > 192000.0)
{
this._soundCardRatio = this._outputSampleRate / 192000.0;
}
else
{
this._soundCardRatio = (this._useASIO ? (this._outputSampleRate / (double)this._minOutputSampleRate) : 1.0);
}
Console.WriteLine("_inputBuffersize (complex) set to " + (Utils.FastConvolve ? "power of 2 : " : ": ") + this._inputBufferSize.ToString());
Console.WriteLine("_outputBuffersize (real) set to : " + this._outputBufferSize.ToString());
Console.WriteLine("_soundCardRatio set to : " + this._soundCardRatio.ToString());
}
catch
{
this.Stop();
throw;
}
}
private int GetDecimationStageCount()
{
if (this._inputSampleRate <= (double)this._minOutputSampleRate)
{
return 0;
}
int num = 1024;
while (this._inputSampleRate < (double)(this._minOutputSampleRate * num) && num > 0)
{
num /= 2;
}
return (int)Math.Log((double)num, 2.0);
}
}
}