From 5270d570d5b02bfd0cae240654c27739a401900d Mon Sep 17 00:00:00 2001 From: Flamefire Date: Fri, 14 Mar 2014 00:36:42 +0100 Subject: [PATCH] Split classes from CSound.cs to separate files (1 class/file) and move to Lib/Sound (where they belong) --- Vocaluxe/Base/CSound.cs | 361 ------------------------------- Vocaluxe/Lib/Sound/CBuffer.cs | 209 ++++++++++++++++++ Vocaluxe/Lib/Sound/CPt1.cs | 83 +++++++ Vocaluxe/Lib/Sound/CSyncTimer.cs | 81 +++++++ Vocaluxe/Vocaluxe.csproj | 3 + 5 files changed, 376 insertions(+), 361 deletions(-) create mode 100644 Vocaluxe/Lib/Sound/CBuffer.cs create mode 100644 Vocaluxe/Lib/Sound/CPt1.cs create mode 100644 Vocaluxe/Lib/Sound/CSyncTimer.cs diff --git a/Vocaluxe/Base/CSound.cs b/Vocaluxe/Base/CSound.cs index 7dfe5840..d6b55cbe 100644 --- a/Vocaluxe/Base/CSound.cs +++ b/Vocaluxe/Base/CSound.cs @@ -17,7 +17,6 @@ using System; using System.Collections.ObjectModel; -using System.Diagnostics; using System.IO; using Vocaluxe.Lib.Sound; using VocaluxeLib; @@ -311,364 +310,4 @@ namespace Vocaluxe.Base } #endregion Record } - - class CBuffer : IDisposable - { - private const double _BaseToneFreq = 65.4064; - public const int NumHalfTones = 17; - - private const int _AnalysisBufLen = 4096; - private const int _AnalysisByteBufLen = _AnalysisBufLen * 2; - - private readonly Int16[] _AnalysisBuffer = new Int16[_AnalysisBufLen]; - private readonly byte[] _AnalysisByteBuffer = new byte[_AnalysisByteBufLen]; //tmp buffer (stream->tmpBuffer->(Int16)AnalysisBuffer) - - private static readonly int[] _SamplesPerPeriodPerTone = new int[NumHalfTones]; //Precalculated table holding sample# per period for each tone - private readonly float[] _TmpWeight = new float[NumHalfTones]; //tmp buffer for weights (gets copied to ToneWeight when checked) - - private double _MaxVolume; - private bool _NewSamples; - - private MemoryStream _Stream = new MemoryStream(); // full buffer - - public CBuffer() - { - ToneWeigth = new float[NumHalfTones]; - for (int i = 0; i < ToneWeigth.Length; i++) - ToneWeigth[i] = 0.99f; - } - - static CBuffer() - { - const double halftoneBase = 1.05946309436; // 2^(1/12) -> HalftoneBase^12 = 2 (one octave) - //Init Array to avoid costly calculations - for (int toneIndex = 0; toneIndex < NumHalfTones; toneIndex++) - { - double freq = _BaseToneFreq * Math.Pow(halftoneBase, toneIndex); - _SamplesPerPeriodPerTone[toneIndex] = (int)Math.Round(44100.0 / freq); // samples in one period - } - } - - public int ToneAbs { get; private set; } - - public int Tone { get; set; } - - public float MaxVolume - { - get { return (float)_MaxVolume; } - } - - public bool ToneValid { get; private set; } - - public float[] ToneWeigth { get; private set; } - - public void Reset() - { - lock (_Stream) - { - _Stream.SetLength(0L); - _NewSamples = false; - } - ToneValid = false; - ToneAbs = 0; - Tone = 0; - } - - private void _Add(byte[] bytes) - { - _Stream.Write(bytes, 0, bytes.Length); - } - - public void ProcessNewBuffer(byte[] buffer) - { - // apply software boost - //BoostBuffer(Buffer, BufferSize); - - // voice passthrough (send data to playback-device) - //if (assigned(fVoiceStream)) then - //fVoiceStream.WriteData(Buffer, BufferSize); - lock (_Stream) - { - _Add(buffer); - _NewSamples = true; - } - } - - public void AnalyzeBuffer() - { - if (!_NewSamples) - return; - - lock (_Stream) - { - if (_Stream.Length >= _AnalysisByteBufLen) - { - _Stream.Position -= _AnalysisByteBufLen; - _Stream.Read(_AnalysisByteBuffer, 0, _AnalysisByteBufLen); - } - _NewSamples = false; - } - - for (int i = 0; i < _AnalysisBufLen; i++) - _AnalysisBuffer[i] = BitConverter.ToInt16(_AnalysisByteBuffer, i * 2); - - try - { - // find maximum volume - _FindMaxVolume(); - - if (MaxVolume >= 0.02) - _AnalyzeByAutocorrelation(); - else - ToneValid = false; - } - catch (Exception) {} - } - - private void _FindMaxVolume() - { - short maxVolume = 0; - for (int i = 0; i < _AnalysisBuffer.Length / 4; i++) - { - short volume = _AnalysisBuffer[i]; - if (volume < 0) - { - if (volume == Int16.MinValue) - { - maxVolume = Int16.MaxValue; - break; - } - volume = (short)-volume; - } - if (volume > maxVolume) - maxVolume = volume; - } - _MaxVolume = (double)maxVolume / Int16.MaxValue; - } - - private void _AnalyzeByAutocorrelation() - { - // prepare to analyze - double maxWeight = -1.0; - double minWeight = 1.0; - int maxTone = -1; - - // analyze halftones - // Note: at the lowest tone (~65Hz) and a buffer-size of 4096 - // at 44.1 (or 48kHz) only 6 (or 5) samples are compared, this might be - // too few samples -> use a bigger buffer-size - for (int toneIndex = 0; toneIndex < NumHalfTones; toneIndex++) - { - double curWeight = _AnalyzeAutocorrelationTone(toneIndex); - - if (curWeight > maxWeight) - { - maxWeight = curWeight; - maxTone = toneIndex; - } - - if (curWeight < minWeight) - minWeight = curWeight; - - _TmpWeight[toneIndex] = (float)curWeight; - } - - if (maxWeight - minWeight > 0.01) - { - Array.Copy(_TmpWeight, ToneWeigth, NumHalfTones); - - ToneAbs = maxTone; - Tone = maxTone % 12; - ToneValid = true; - } - else - ToneValid = false; - } - - private double _AnalyzeAutocorrelationTone(int tone) - { - int samplesPerPeriod = _SamplesPerPeriodPerTone[tone]; // samples in one period - - int accumDist = 0; // accumulated distances - - // compare correlating samples - int sampleIndex = 0; // index of sample to analyze - // Start value= index of sample one period ahead - for (int correlatingSampleIndex = sampleIndex + samplesPerPeriod; correlatingSampleIndex < _AnalysisBufLen; correlatingSampleIndex++, sampleIndex++) - { - // calc distance (correlation: 1-dist/IntMax) to corresponding sample in next period - // distance (0=equal .. IntMax=totally different) between correlated samples - int dist = Math.Abs(_AnalysisBuffer[sampleIndex] - _AnalysisBuffer[correlatingSampleIndex]); - accumDist += dist; - } - - return 1.0 - (double)accumDist / Int16.MaxValue / _AnalysisBufLen; - } - - public void Dispose() - { - if (_Stream != null) - { - _Stream.Dispose(); - _Stream = null; - } - GC.SuppressFinalize(this); - } - } - - class CSyncTimer - { - private readonly CPt1 _ExternTime; - private readonly Stopwatch _Timer; - private float _SetValue; - - public float Time - { - get - { - double nanosecPerTick = (1000.0 * 1000.0 * 1000.0) / Stopwatch.Frequency; - long ticks = _Timer.ElapsedTicks; - float dt = _Timer.ElapsedMilliseconds / 1000f; - - if (Stopwatch.IsHighResolution && ticks != 0) - dt = (float)(ticks * nanosecPerTick / 1000000000.0); - - return _SetValue + dt; - } - - set - { - _ExternTime.Time = value; - _SetValue = value; - _Timer.Reset(); - _Timer.Start(); - } - } - - public CSyncTimer(float currentTime, float k, float t) - { - _ExternTime = new CPt1(currentTime, k, t); - _Timer = new Stopwatch(); - _SetValue = currentTime; - } - - public float Update(float newTime) - { - float et = _ExternTime.Update(newTime); - - float dt = Time; - - float diff = et - dt; - if (Math.Abs(diff) > 0.05f) - { - _Timer.Reset(); - _Timer.Start(); - _SetValue = dt + diff; - dt = _SetValue; - //Console.WriteLine("DRIFTED!!! " + diff.ToString()); - } - else - { - if (diff > 0.01f) - _SetValue += 0.000025f; - - if (diff < -0.01f) - _SetValue -= 0.000025f; - } - //Console.WriteLine(diff.ToString()); - return dt; - } - - public void Pause() - { - _ExternTime.Pause(); - _Timer.Stop(); - } - - public void Resume() - { - _ExternTime.Resume(); - _Timer.Start(); - } - } - - class CPt1 - { - private float _CurrentTime; - private float _OldTime; - - private readonly Stopwatch _Timer; - private readonly float _K; - private readonly float _T; - - public float Time - { - get - { - double nanosecPerTick = (1000.0 * 1000.0 * 1000.0) / Stopwatch.Frequency; - long ticks = _Timer.ElapsedTicks; - float dt = _Timer.ElapsedMilliseconds / 1000f; - - if (Stopwatch.IsHighResolution && ticks != 0) - dt = (float)(ticks * nanosecPerTick / 1000000000.0); - - return _CurrentTime + dt; - } - - set - { - if (value < 0f) - return; - - _CurrentTime = value; - _OldTime = value; - _Timer.Reset(); - _Timer.Start(); - } - } - - public CPt1(float currentTime, float k, float t) - { - _CurrentTime = currentTime; - _OldTime = currentTime; - - _Timer = new Stopwatch(); - _K = k; - _T = t; - } - - public float Update(float newTime) - { - _Timer.Stop(); - - double nanosecPerTick = (1000.0 * 1000.0 * 1000.0) / Stopwatch.Frequency; - long ticks = _Timer.ElapsedTicks; - float dt = _Timer.ElapsedMilliseconds / 1000f; - - if (Stopwatch.IsHighResolution && ticks != 0) - dt = (float)(ticks * nanosecPerTick / 1000000000.0); - - float ts = 0f; - if (dt > 0) - ts = 1 / (_T / dt + 1); - - _CurrentTime = ts * (_K * newTime - _OldTime) + _OldTime; - _OldTime = _CurrentTime; - - _Timer.Reset(); - _Timer.Start(); - - return _CurrentTime; - } - - public void Pause() - { - _Timer.Stop(); - } - - public void Resume() - { - _Timer.Start(); - } - } } \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/CBuffer.cs b/Vocaluxe/Lib/Sound/CBuffer.cs new file mode 100644 index 00000000..34831ccd --- /dev/null +++ b/Vocaluxe/Lib/Sound/CBuffer.cs @@ -0,0 +1,209 @@ +using System; +using System.IO; + +namespace Vocaluxe.Lib.Sound +{ + class CBuffer : IDisposable + { + private const double _BaseToneFreq = 65.4064; + public const int NumHalfTones = 17; + + private const int _AnalysisBufLen = 4096; + private const int _AnalysisByteBufLen = _AnalysisBufLen * 2; + + private readonly Int16[] _AnalysisBuffer = new Int16[_AnalysisBufLen]; + private readonly byte[] _AnalysisByteBuffer = new byte[_AnalysisByteBufLen]; //tmp buffer (stream->tmpBuffer->(Int16)AnalysisBuffer) + + private static readonly int[] _SamplesPerPeriodPerTone = new int[NumHalfTones]; //Precalculated table holding sample# per period for each tone + private readonly float[] _TmpWeight = new float[NumHalfTones]; //tmp buffer for weights (gets copied to ToneWeight when checked) + + private double _MaxVolume; + private bool _NewSamples; + + private MemoryStream _Stream = new MemoryStream(); // full buffer + + public CBuffer() + { + ToneWeigth = new float[NumHalfTones]; + for (int i = 0; i < ToneWeigth.Length; i++) + ToneWeigth[i] = 0.99f; + } + + static CBuffer() + { + const double halftoneBase = 1.05946309436; // 2^(1/12) -> HalftoneBase^12 = 2 (one octave) + //Init Array to avoid costly calculations + for (int toneIndex = 0; toneIndex < NumHalfTones; toneIndex++) + { + double freq = _BaseToneFreq * Math.Pow(halftoneBase, toneIndex); + _SamplesPerPeriodPerTone[toneIndex] = (int)Math.Round(44100.0 / freq); // samples in one period + } + } + + public int ToneAbs { get; private set; } + + public int Tone { get; set; } + + public float MaxVolume + { + get { return (float)_MaxVolume; } + } + + public bool ToneValid { get; private set; } + + public float[] ToneWeigth { get; private set; } + + public void Reset() + { + lock (_Stream) + { + _Stream.SetLength(0L); + _NewSamples = false; + } + ToneValid = false; + ToneAbs = 0; + Tone = 0; + } + + private void _Add(byte[] bytes) + { + _Stream.Write(bytes, 0, bytes.Length); + } + + public void ProcessNewBuffer(byte[] buffer) + { + // apply software boost + //BoostBuffer(Buffer, BufferSize); + + // voice passthrough (send data to playback-device) + //if (assigned(fVoiceStream)) then + //fVoiceStream.WriteData(Buffer, BufferSize); + lock (_Stream) + { + _Add(buffer); + _NewSamples = true; + } + } + + public void AnalyzeBuffer() + { + if (!_NewSamples) + return; + + lock (_Stream) + { + if (_Stream.Length >= _AnalysisByteBufLen) + { + _Stream.Position -= _AnalysisByteBufLen; + _Stream.Read(_AnalysisByteBuffer, 0, _AnalysisByteBufLen); + } + _NewSamples = false; + } + + for (int i = 0; i < _AnalysisBufLen; i++) + _AnalysisBuffer[i] = BitConverter.ToInt16(_AnalysisByteBuffer, i * 2); + + try + { + // find maximum volume + _FindMaxVolume(); + + if (MaxVolume >= 0.02) + _AnalyzeByAutocorrelation(); + else + ToneValid = false; + } + catch (Exception) {} + } + + private void _FindMaxVolume() + { + short maxVolume = 0; + for (int i = 0; i < _AnalysisBuffer.Length / 4; i++) + { + short volume = _AnalysisBuffer[i]; + if (volume < 0) + { + if (volume == Int16.MinValue) + { + maxVolume = Int16.MaxValue; + break; + } + volume = (short)-volume; + } + if (volume > maxVolume) + maxVolume = volume; + } + _MaxVolume = (double)maxVolume / Int16.MaxValue; + } + + private void _AnalyzeByAutocorrelation() + { + // prepare to analyze + double maxWeight = -1.0; + double minWeight = 1.0; + int maxTone = -1; + + // analyze halftones + // Note: at the lowest tone (~65Hz) and a buffer-size of 4096 + // at 44.1 (or 48kHz) only 6 (or 5) samples are compared, this might be + // too few samples -> use a bigger buffer-size + for (int toneIndex = 0; toneIndex < NumHalfTones; toneIndex++) + { + double curWeight = _AnalyzeAutocorrelationTone(toneIndex); + + if (curWeight > maxWeight) + { + maxWeight = curWeight; + maxTone = toneIndex; + } + + if (curWeight < minWeight) + minWeight = curWeight; + + _TmpWeight[toneIndex] = (float)curWeight; + } + + if (maxWeight - minWeight > 0.01) + { + Array.Copy(_TmpWeight, ToneWeigth, NumHalfTones); + + ToneAbs = maxTone; + Tone = maxTone % 12; + ToneValid = true; + } + else + ToneValid = false; + } + + private double _AnalyzeAutocorrelationTone(int tone) + { + int samplesPerPeriod = _SamplesPerPeriodPerTone[tone]; // samples in one period + + int accumDist = 0; // accumulated distances + + // compare correlating samples + int sampleIndex = 0; // index of sample to analyze + // Start value= index of sample one period ahead + for (int correlatingSampleIndex = sampleIndex + samplesPerPeriod; correlatingSampleIndex < _AnalysisBufLen; correlatingSampleIndex++, sampleIndex++) + { + // calc distance (correlation: 1-dist/IntMax) to corresponding sample in next period + // distance (0=equal .. IntMax=totally different) between correlated samples + int dist = Math.Abs(_AnalysisBuffer[sampleIndex] - _AnalysisBuffer[correlatingSampleIndex]); + accumDist += dist; + } + + return 1.0 - (double)accumDist / Int16.MaxValue / _AnalysisBufLen; + } + + public void Dispose() + { + if (_Stream != null) + { + _Stream.Dispose(); + _Stream = null; + } + GC.SuppressFinalize(this); + } + } +} \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/CPt1.cs b/Vocaluxe/Lib/Sound/CPt1.cs new file mode 100644 index 00000000..4ca4b951 --- /dev/null +++ b/Vocaluxe/Lib/Sound/CPt1.cs @@ -0,0 +1,83 @@ +using System.Diagnostics; + +namespace Vocaluxe.Lib.Sound +{ + class CPt1 + { + private float _CurrentTime; + private float _OldTime; + + private readonly Stopwatch _Timer; + private readonly float _K; + private readonly float _T; + + public float Time + { + get + { + double nanosecPerTick = (1000.0 * 1000.0 * 1000.0) / Stopwatch.Frequency; + long ticks = _Timer.ElapsedTicks; + float dt = _Timer.ElapsedMilliseconds / 1000f; + + if (Stopwatch.IsHighResolution && ticks != 0) + dt = (float)(ticks * nanosecPerTick / 1000000000.0); + + return _CurrentTime + dt; + } + + set + { + if (value < 0f) + return; + + _CurrentTime = value; + _OldTime = value; + _Timer.Reset(); + _Timer.Start(); + } + } + + public CPt1(float currentTime, float k, float t) + { + _CurrentTime = currentTime; + _OldTime = currentTime; + + _Timer = new Stopwatch(); + _K = k; + _T = t; + } + + public float Update(float newTime) + { + _Timer.Stop(); + + double nanosecPerTick = (1000.0 * 1000.0 * 1000.0) / Stopwatch.Frequency; + long ticks = _Timer.ElapsedTicks; + float dt = _Timer.ElapsedMilliseconds / 1000f; + + if (Stopwatch.IsHighResolution && ticks != 0) + dt = (float)(ticks * nanosecPerTick / 1000000000.0); + + float ts = 0f; + if (dt > 0) + ts = 1 / (_T / dt + 1); + + _CurrentTime = ts * (_K * newTime - _OldTime) + _OldTime; + _OldTime = _CurrentTime; + + _Timer.Restart(); + + return _CurrentTime; + } + + public void Pause() + { + _Timer.Stop(); + } + + public void Resume() + { + _Timer.Start(); + } + } +} \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/CSyncTimer.cs b/Vocaluxe/Lib/Sound/CSyncTimer.cs new file mode 100644 index 00000000..6ccd380c --- /dev/null +++ b/Vocaluxe/Lib/Sound/CSyncTimer.cs @@ -0,0 +1,81 @@ +using System; +using System.Diagnostics; + +namespace Vocaluxe.Lib.Sound +{ + class CSyncTimer + { + private readonly CPt1 _ExternTime; + private readonly Stopwatch _Timer; + private float _SetValue; + + public float Time + { + get + { + double nanosecPerTick = (1000.0 * 1000.0 * 1000.0) / Stopwatch.Frequency; + long ticks = _Timer.ElapsedTicks; + float dt = _Timer.ElapsedMilliseconds / 1000f; + + if (Stopwatch.IsHighResolution && ticks != 0) + dt = (float)(ticks * nanosecPerTick / 1000000000.0); + + return _SetValue + dt; + } + + set + { + _ExternTime.Time = value; + _SetValue = value; + _Timer.Reset(); + _Timer.Start(); + } + } + + public CSyncTimer(float currentTime, float k, float t) + { + _ExternTime = new CPt1(currentTime, k, t); + _Timer = new Stopwatch(); + _SetValue = currentTime; + } + + public float Update(float newTime) + { + float et = _ExternTime.Update(newTime); + + float dt = Time; + + float diff = et - dt; + if (Math.Abs(diff) > 0.05f) + { + _Timer.Reset(); + _Timer.Start(); + _SetValue = dt + diff; + dt = _SetValue; + //Console.WriteLine("DRIFTED!!! " + diff.ToString()); + } + else + { + if (diff > 0.01f) + _SetValue += 0.000025f; + + if (diff < -0.01f) + _SetValue -= 0.000025f; + } + //Console.WriteLine(diff.ToString()); + return dt; + } + + public void Pause() + { + _ExternTime.Pause(); + _Timer.Stop(); + } + + public void Resume() + { + _ExternTime.Resume(); + _Timer.Start(); + } + } +} \ No newline at end of file diff --git a/Vocaluxe/Vocaluxe.csproj b/Vocaluxe/Vocaluxe.csproj index 380eb362..c08199fc 100644 --- a/Vocaluxe/Vocaluxe.csproj +++ b/Vocaluxe/Vocaluxe.csproj @@ -212,11 +212,14 @@ + + +