From 167202e8ff907871df71629d71435c9f6808c79b Mon Sep 17 00:00:00 2001 From: Flamefire Date: Tue, 2 Dec 2014 21:08:14 +0100 Subject: [PATCH] Remove PitchTracker# (not good enough) --- .../Lib/Sound/Record/PitchTracker/CPtSharp.cs | 71 -- .../PitchTracker/Pitch/CircularBuffer.cs | 173 ----- .../Record/PitchTracker/Pitch/Extensions.cs | 105 --- .../Record/PitchTracker/Pitch/IIRFilter.cs | 613 ------------------ .../Record/PitchTracker/Pitch/PitchDsp.cs | 561 ---------------- .../Record/PitchTracker/Pitch/PitchTracker.cs | 431 ------------ Vocaluxe/Vocaluxe.csproj | 6 - 7 files changed, 1960 deletions(-) delete mode 100644 Vocaluxe/Lib/Sound/Record/PitchTracker/CPtSharp.cs delete mode 100644 Vocaluxe/Lib/Sound/Record/PitchTracker/Pitch/CircularBuffer.cs delete mode 100644 Vocaluxe/Lib/Sound/Record/PitchTracker/Pitch/Extensions.cs delete mode 100644 Vocaluxe/Lib/Sound/Record/PitchTracker/Pitch/IIRFilter.cs delete mode 100644 Vocaluxe/Lib/Sound/Record/PitchTracker/Pitch/PitchDsp.cs delete mode 100644 Vocaluxe/Lib/Sound/Record/PitchTracker/Pitch/PitchTracker.cs diff --git a/Vocaluxe/Lib/Sound/Record/PitchTracker/CPtSharp.cs b/Vocaluxe/Lib/Sound/Record/PitchTracker/CPtSharp.cs deleted file mode 100644 index 53e7a26c..00000000 --- a/Vocaluxe/Lib/Sound/Record/PitchTracker/CPtSharp.cs +++ /dev/null @@ -1,71 +0,0 @@ -#region license -// This file is part of Vocaluxe. -// -// Vocaluxe is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Vocaluxe is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Vocaluxe. If not, see . -#endregion - -using System; - -namespace Vocaluxe.Lib.Sound.Record.PitchTracker -{ - /// - /// Pitchtracker (Pt) in C# that uses IIR filters for smoothing and an intelligent detection algorithm to reduce calculations - /// DO NOT USE in 32 Bit! Due to floating point errors the filter will raise all values to NAN (TODO: FIX it!) - /// - class CPtSharp : CPitchTracker - { - private Pitch.PitchTracker _Instance; - - public CPtSharp() - { - _Instance = new Pitch.PitchTracker {SampleRate = 44100, DetectLevelThreshold = 0.01f, PitchRecordHistorySize = 1}; - } - - public override void Input(byte[] data) - { - short[] samplesShort = new short[data.Length / 2]; - Buffer.BlockCopy(data, 0, samplesShort, 0, data.Length); - float[] samplesFloat = new float[samplesShort.Length]; - for (int i = 0; i < samplesShort.Length; i++) - samplesFloat[i] = (float)samplesShort[i] / short.MaxValue; - _Instance.ProcessBuffer(samplesFloat); - } - - public override int GetNote(out float maxVolume, float[] weights) - { - int note = _Instance.CurrentPitchRecord.MidiNote - 15 - 21; - maxVolume = _Instance.LastMaxVol; - _SetWeights(note, weights); - return note; - } - - public override int GetNumHalfTones() - { - return NumHalfTonesDef; - } - - public override float VolumeTreshold - { - get { return _Instance.DetectLevelThreshold; } - set { _Instance.DetectLevelThreshold = value; } - } - - protected override void _Dispose(bool disposing) - { - if (_Instance == null) - throw new ObjectDisposedException(GetType().Name); - _Instance = null; - } - } -} \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/Record/PitchTracker/Pitch/CircularBuffer.cs b/Vocaluxe/Lib/Sound/Record/PitchTracker/Pitch/CircularBuffer.cs deleted file mode 100644 index 07b6836b..00000000 --- a/Vocaluxe/Lib/Sound/Record/PitchTracker/Pitch/CircularBuffer.cs +++ /dev/null @@ -1,173 +0,0 @@ -using System; - -namespace Vocaluxe.Lib.Sound.Record.PitchTracker.Pitch -{ - public class CircularBuffer : IDisposable - { - int m_bufSize; - int m_begBufOffset; - int m_availBuf; - long m_startPos; // total circular buffer position - T[] m_buffer; - - /// - /// Constructor - /// - public CircularBuffer() - { - } - - /// - /// Constructor - /// - /// - public CircularBuffer(int bufCount) - { - SetSize(bufCount); - } - - /// - /// Dispose - /// - public void Dispose() - { - SetSize(0); - } - - /// - /// Reset to the beginning of the buffer - /// - public void Reset() - { - m_begBufOffset = 0; - m_availBuf = 0; - m_startPos = 0; - } - - /// - /// Set the buffer to the specified size - /// - /// - public void SetSize(int newSize) - { - Reset(); - - if (m_bufSize == newSize) - return; - - if (m_buffer != null) - m_buffer = null; - - m_bufSize = newSize; - - if (m_bufSize > 0) - m_buffer = new T[m_bufSize]; - } - - /// - /// Clear the buffer - /// - public void Clear() - { - Array.Clear(m_buffer, 0, m_buffer.Length); - } - - /// - /// Get or set the start position - /// - public long StartPosition - { - get { return m_startPos; } - set { m_startPos = value; } - } - - /// - /// Get the end position - /// - public long EndPosition - { - get { return m_startPos + m_availBuf; } - } - - /// - /// Get or set the amount of avaliable space - /// - public int Available - { - get { return m_availBuf; } - set { m_availBuf = Math.Min(value, m_bufSize); } - } - - /// - /// Write data into the buffer - /// - /// - /// - /// - public int WriteBuffer(T[] m_pInBuffer, int count) - { - count = Math.Min(count, m_bufSize); - - var startPos = m_availBuf != m_bufSize ? m_availBuf : m_begBufOffset; - var pass1Count = Math.Min(count, m_bufSize - startPos); - var pass2Count = count - pass1Count; - - PitchDsp.CopyBuffer(m_pInBuffer, 0, m_buffer, startPos, pass1Count); - - if (pass2Count > 0) - PitchDsp.CopyBuffer(m_pInBuffer, pass1Count, m_buffer, 0, pass2Count); - - if (pass2Count == 0) - { - // did not wrap around - if (m_availBuf != m_bufSize) - m_availBuf += count; // have never wrapped around - else - { - m_begBufOffset += count; - m_startPos += count; - } - } - else - { - // wrapped around - if (m_availBuf != m_bufSize) - m_startPos += pass2Count; // first time wrap-around - else - m_startPos += count; - - m_begBufOffset = pass2Count; - m_availBuf = m_bufSize; - } - - return count; - } - - /// - /// Read from the buffer - /// - /// - /// - /// - /// - public bool ReadBuffer(T[] outBuffer, long startRead, int readCount) - { - var endRead = (int)(startRead + readCount); - var endAvail = (int)(m_startPos + m_availBuf); - - if (startRead < m_startPos || endRead > endAvail) - return false; - - var startReadPos = (int)(((startRead - m_startPos) + m_begBufOffset) % m_bufSize); - var block1Samples = Math.Min(readCount, m_bufSize - startReadPos); - var block2Samples = readCount - block1Samples; - - PitchDsp.CopyBuffer(m_buffer, startReadPos, outBuffer, 0, block1Samples); - - if (block2Samples > 0) - PitchDsp.CopyBuffer(m_buffer, 0, outBuffer, block1Samples, block2Samples); - - return true; - } - } -} diff --git a/Vocaluxe/Lib/Sound/Record/PitchTracker/Pitch/Extensions.cs b/Vocaluxe/Lib/Sound/Record/PitchTracker/Pitch/Extensions.cs deleted file mode 100644 index 4cbd7c81..00000000 --- a/Vocaluxe/Lib/Sound/Record/PitchTracker/Pitch/Extensions.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; - -namespace Vocaluxe.Lib.Sound.Record.PitchTracker.Pitch -{ - public static class Extensions - { - /// - /// Clear the buffer - /// - /// - /// - public static void Clear(this float[] buffer) - { - Array.Clear(buffer, 0, buffer.Length); - } - - /// - /// Clear the buffer - /// - /// - /// - public static void Clear(this double[] buffer) - { - Array.Clear(buffer, 0, buffer.Length); - } - - /// - /// Copy the values from one buffer to a different or the same buffer. - /// It is safe to copy to the same buffer, even if the areas overlap - /// - /// - /// - /// - /// - public static void Copy(this float[] fromBuffer, float[] toBuffer, int fromStart, int toStart, int length) - { - if (toBuffer == null || fromBuffer.Length == 0 || toBuffer.Length == 0) - return; - - int fromBegIdx = fromStart; - int fromEndIdx = fromStart + length; - int toBegIdx = toStart; - int toEndIdx = toStart + length; - - if (fromBegIdx < 0) - { - toBegIdx -= fromBegIdx; - fromBegIdx = 0; - } - - if (toBegIdx < 0) - { - fromBegIdx -= toBegIdx; - toBegIdx = 0; - } - - if (fromEndIdx >= fromBuffer.Length) - { - toEndIdx -= fromEndIdx - fromBuffer.Length + 1; - fromEndIdx = fromBuffer.Length - 1; - } - - if (toEndIdx >= toBuffer.Length) - { - fromEndIdx -= toEndIdx - toBuffer.Length + 1; - toEndIdx = fromBuffer.Length - 1; - } - - if (fromBegIdx < toBegIdx) - { - // Shift right, so start at the right - for (int fromIdx = fromEndIdx, toIdx = toEndIdx; fromIdx >= fromBegIdx; fromIdx--, toIdx--) - toBuffer[toIdx] = fromBuffer[fromIdx]; - } - else - { - // Shift left, so start at the left - for (int fromIdx = fromBegIdx, toIdx = toBegIdx; fromIdx <= fromEndIdx; fromIdx++, toIdx++) - toBuffer[toIdx] = fromBuffer[fromIdx]; - } - } - - /// - /// - /// - /// - /// - public static void Clear(this float[] buffer, int startIdx, int endIdx) - { - Array.Clear(buffer, startIdx, endIdx - startIdx + 1); - } - - /// - /// Fill the buffer with the specified value - /// - /// - /// - /// - public static void Fill(this double[] buffer, double value) - { - for (int idx = 0; idx < buffer.Length; idx++) - buffer[idx] = value; - } - } -} \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/Record/PitchTracker/Pitch/IIRFilter.cs b/Vocaluxe/Lib/Sound/Record/PitchTracker/Pitch/IIRFilter.cs deleted file mode 100644 index e180ff72..00000000 --- a/Vocaluxe/Lib/Sound/Record/PitchTracker/Pitch/IIRFilter.cs +++ /dev/null @@ -1,613 +0,0 @@ -using System; - -namespace Vocaluxe.Lib.Sound.Record.PitchTracker.Pitch -{ - /// - /// Infinite impulse response filter (old style analog filters) - /// - class IIRFilter - { - /// - /// The type of filter - /// - public enum FilterType - { - None = 0, - LP, - HP, - BP - } - - /// - /// The filter prototype - /// - public enum ProtoType - { - None = 0, - Butterworth, - Chebyshev, - } - - const int kHistMask = 31; - const int kHistSize = 32; - - private int m_order; - private ProtoType m_protoType; - private FilterType m_filterType; - - private float m_fp1; - private float m_fp2; - private float m_fN; - private float m_ripple; - private float m_sampleRate; - private double[] m_real; - private double[] m_imag; - private double[] m_z; - private double[] m_aCoeff; - private double[] m_bCoeff; - private double[] m_inHistory; - private double[] m_outHistory; - private int m_histIdx; - private bool m_invertDenormal; - - public IIRFilter() - { - } - - /// - /// Returns true if all the filter parameters are valid - /// - public bool FilterValid - { - get - { - if (m_order < 1 || m_order > 16 || - m_protoType == ProtoType.None || - m_filterType == FilterType.None || - m_sampleRate <= 0.0f || - m_fN <= 0.0f) - return false; - - switch (m_filterType) - { - case FilterType.LP: - if (m_fp2 <= 0.0f) - return false; - break; - - case FilterType.BP: - if (m_fp1 <= 0.0f || m_fp2 <= 0.0f || m_fp1 >= m_fp2) - return false; - break; - - case FilterType.HP: - if (m_fp1 <= 0.0f) - return false; - break; - } - - // For bandpass, the order must be even - if (m_filterType == FilterType.BP && (m_order & 1) != 0) - return false; - - return true; - } - } - - /// - /// Set the filter prototype - /// - public ProtoType Proto - { - get { return m_protoType; } - - set - { - m_protoType = value; - Design(); - } - } - - /// - /// Set the filter type - /// - public FilterType Type - { - get { return m_filterType; } - - set - { - m_filterType = value; - Design(); - } - } - - public int Order - { - get { return m_order; } - - set - { - m_order = Math.Min(16, Math.Max(1, Math.Abs(value))); - - if (m_filterType == FilterType.BP && Odd(m_order)) - m_order++; - - Design(); - } - } - - public float SampleRate - { - get { return m_sampleRate; } - - set - { - m_sampleRate = value; - m_fN = 0.5f * m_sampleRate; - Design(); - } - } - - public float FreqLow - { - get { return m_fp1; } - - set - { - m_fp1 = value; - Design(); - } - } - - public float FreqHigh - { - get { return m_fp2; } - - set - { - m_fp2 = value; - Design(); - } - } - - public float Ripple - { - get { return m_ripple; } - - set - { - m_ripple = value; - Design(); - } - } - - /// - /// Returns true if n is odd - /// - /// - /// - private bool Odd(int n) - { - return (n & 1) == 1; - } - - /// - /// Square - /// - /// - /// - private float Sqr(float value) - { - return value * value; - } - - /// - /// Square - /// - /// - /// - private double Sqr(double value) - { - return value * value; - } - - /// - /// Determines poles and zeros of IIR filter - /// based on bilinear transform method - /// - private void LocatePolesAndZeros() - { - m_real = new double[m_order + 1]; - m_imag = new double[m_order + 1]; - m_z = new double[m_order + 1]; - double ln10 = Math.Log(10.0); - - // Butterworth, Chebyshev parameters - int n = m_order; - - if (m_filterType == FilterType.BP) - n = n / 2; - - int ir = n % 2; - int n1 = n + ir; - int n2 = (3 * n + ir) / 2 - 1; - double f1; - - switch (m_filterType) - { - case FilterType.LP: - f1 = m_fp2; - break; - - case FilterType.HP: - f1 = m_fN - m_fp1; - break; - - case FilterType.BP: - f1 = m_fp2 - m_fp1; - break; - - default: - f1 = 0.0; - break; - } - - double tanw1 = Math.Tan(0.5 * Math.PI * f1 / m_fN); - double tansqw1 = Sqr(tanw1); - - // Real and Imaginary parts of low-pass poles - double t, a = 1.0, r = 1.0, i = 1.0; - - for (int k = n1; k <= n2; k++) - { - t = 0.5 * (2 * k + 1 - ir) * Math.PI / (double)n; - - switch (m_protoType) - { - case ProtoType.Butterworth: - double b3 = 1.0 - 2.0 * tanw1 * Math.Cos(t) + tansqw1; - r = (1.0 - tansqw1) / b3; - i = 2.0 * tanw1 * Math.Sin(t) / b3; - break; - - case ProtoType.Chebyshev: - double d = 1.0 - Math.Exp(-0.05 * m_ripple * ln10); - double e = 1.0 / Math.Sqrt(1.0 / Sqr(1.0 - d) - 1.0); - double x = Math.Pow(Math.Sqrt(e * e + 1.0) + e, 1.0 / (double)n); - a = 0.5 * (x - 1.0 / x); - double b = 0.5 * (x + 1.0 / x); - double c3 = a * tanw1 * Math.Cos(t); - double c4 = b * tanw1 * Math.Sin(t); - double c5 = Sqr(1.0 - c3) + Sqr(c4); - r = 2.0 * (1.0 - c3) / c5 - 1.0; - i = 2.0 * c4 / c5; - break; - } - - int m = 2 * (n2 - k) + 1; - m_real[m + ir] = r; - m_imag[m + ir] = Math.Abs(i); - m_real[m + ir + 1] = r; - m_imag[m + ir + 1] = -Math.Abs(i); - } - - if (Odd(n)) - { - if (m_protoType == ProtoType.Butterworth) - r = (1.0 - tansqw1) / (1.0 + 2.0 * tanw1 + tansqw1); - - if (m_protoType == ProtoType.Chebyshev) - r = 2.0 / (1.0 + a * tanw1) - 1.0; - - m_real[1] = r; - m_imag[1] = 0.0; - } - - switch (m_filterType) - { - case FilterType.LP: - for (int m = 1; m <= n; m++) - m_z[m] = -1.0; - break; - - case FilterType.HP: - // Low-pass to high-pass transformation - for (int m = 1; m <= n; m++) - { - m_real[m] = -m_real[m]; - m_z[m] = 1.0; - } - break; - - case FilterType.BP: - // Low-pass to bandpass transformation - for (int m = 1; m <= n; m++) - { - m_z[m] = 1.0; - m_z[m + n] = -1.0; - } - - double f4 = 0.5 * Math.PI * m_fp1 / m_fN; - double f5 = 0.5 * Math.PI * m_fp2 / m_fN; - double aa = Math.Cos(f4 + f5) / Math.Cos(f5 - f4); - double aR, aI, h1, h2, p1R, p2R, p1I, p2I; - - for (int m1 = 0; m1 <= (m_order - 1) / 2; m1++) - { - int m = 1 + 2 * m1; - aR = m_real[m]; - aI = m_imag[m]; - - if (Math.Abs(aI) < 0.0001) - { - h1 = 0.5 * aa * (1.0 + aR); - h2 = Sqr(h1) - aR; - if (h2 > 0.0) - { - p1R = h1 + Math.Sqrt(h2); - p2R = h1 - Math.Sqrt(h2); - p1I = 0.0; - p2I = 0.0; - } - else - { - p1R = h1; - p2R = h1; - p1I = Math.Sqrt(Math.Abs(h2)); - p2I = -p1I; - } - } - else - { - double fR = aa * 0.5 * (1.0 + aR); - double fI = aa * 0.5 * aI; - double gR = Sqr(fR) - Sqr(fI) - aR; - double gI = 2 * fR * fI - aI; - double sR = Math.Sqrt(0.5 * Math.Abs(gR + Math.Sqrt(Sqr(gR) + Sqr(gI)))); - double sI = gI / (2.0 * sR); - p1R = fR + sR; - p1I = fI + sI; - p2R = fR - sR; - p2I = fI - sI; - } - - m_real[m] = p1R; - m_real[m + 1] = p2R; - m_imag[m] = p1I; - m_imag[m + 1] = p2I; - } - - if (Odd(n)) - { - m_real[2] = m_real[n + 1]; - m_imag[2] = m_imag[n + 1]; - } - - for (int k = n; k >= 1; k--) - { - int m = 2 * k - 1; - m_real[m] = m_real[k]; - m_real[m + 1] = m_real[k]; - m_imag[m] = Math.Abs(m_imag[k]); - m_imag[m + 1] = -Math.Abs(m_imag[k]); - } - - break; - } - } - - /// - /// Calculate all the values - /// - public void Design() - { - if (!this.FilterValid) - return; - - m_aCoeff = new double[m_order + 1]; - m_bCoeff = new double[m_order + 1]; - m_inHistory = new double[kHistSize]; - m_outHistory = new double[kHistSize]; - - double[] newA = new double[m_order + 1]; - double[] newB = new double[m_order + 1]; - - // Find filter poles and zeros - LocatePolesAndZeros(); - - // Compute filter coefficients from pole/zero values - m_aCoeff[0] = 1.0; - m_bCoeff[0] = 1.0; - - for (int i = 1; i <= m_order; i++) - { - m_aCoeff[i] = 0.0; - m_bCoeff[i] = 0.0; - } - - int k = 0; - int n = m_order; - int pairs = n / 2; - - if (Odd(m_order)) - { - // First subfilter is first order - m_aCoeff[1] = -m_z[1]; - m_bCoeff[1] = -m_real[1]; - k = 1; - } - - for (int p = 1; p <= pairs; p++) - { - int m = 2 * p - 1 + k; - double alpha1 = -(m_z[m] + m_z[m + 1]); - double alpha2 = m_z[m] * m_z[m + 1]; - double beta1 = -2.0 * m_real[m]; - double beta2 = Sqr(m_real[m]) + Sqr(m_imag[m]); - - newA[1] = m_aCoeff[1] + alpha1 * m_aCoeff[0]; - newB[1] = m_bCoeff[1] + beta1 * m_bCoeff[0]; - - for (int i = 2; i <= n; i++) - { - newA[i] = m_aCoeff[i] + alpha1 * m_aCoeff[i - 1] + alpha2 * m_aCoeff[i - 2]; - newB[i] = m_bCoeff[i] + beta1 * m_bCoeff[i - 1] + beta2 * m_bCoeff[i - 2]; - } - - for (int i = 1; i <= n; i++) - { - m_aCoeff[i] = newA[i]; - m_bCoeff[i] = newB[i]; - } - } - - // Ensure the filter is normalized - FilterGain(1000); - } - - /// - /// Reset the history buffers - /// - public void Reset() - { - if (m_inHistory != null) - m_inHistory.Clear(); - - if (m_outHistory != null) - m_outHistory.Clear(); - - m_histIdx = 0; - } - - /// - /// Reset the filter, and fill the appropriate history buffers with the specified value - /// - /// - public void Reset(double startValue) - { - m_histIdx = 0; - - if (m_inHistory == null || m_outHistory == null) - return; - - m_inHistory.Fill(startValue); - - if (m_inHistory != null) - { - switch (m_filterType) - { - case FilterType.LP: - m_outHistory.Fill(startValue); - break; - - default: - m_outHistory.Clear(); - break; - } - } - } - - /// - /// Apply the filter to the buffer - /// - /// - public void FilterBuffer(float[] srcBuf, long srcPos, float[] dstBuf, long dstPos, long nLen) - { - const double kDenormal = 0.000000000000001; - double denormal = m_invertDenormal ? -kDenormal : kDenormal; - m_invertDenormal = !m_invertDenormal; - - for (int sampleIdx = 0; sampleIdx < nLen; sampleIdx++) - { - double sum = 0.0f; - - m_inHistory[m_histIdx] = srcBuf[srcPos + sampleIdx] + denormal; - - for (int idx = 0; idx < m_aCoeff.Length; idx++) - sum += m_aCoeff[idx] * m_inHistory[(m_histIdx - idx) & kHistMask]; - - for (int idx = 1; idx < m_bCoeff.Length; idx++) - sum -= m_bCoeff[idx] * m_outHistory[(m_histIdx - idx) & kHistMask]; - - m_outHistory[m_histIdx] = sum; - m_histIdx = (m_histIdx + 1) & kHistMask; - dstBuf[dstPos + sampleIdx] = (float)sum; - } - } - - public float FilterSample(float inVal) - { - double sum = 0.0f; - - m_inHistory[m_histIdx] = inVal; - - for (int idx = 0; idx < m_aCoeff.Length; idx++) - sum += m_aCoeff[idx] * m_inHistory[(m_histIdx - idx) & kHistMask]; - - for (int idx = 1; idx < m_bCoeff.Length; idx++) - sum -= m_bCoeff[idx] * m_outHistory[(m_histIdx - idx) & kHistMask]; - - m_outHistory[m_histIdx] = sum; - m_histIdx = (m_histIdx + 1) & kHistMask; - - return (float)sum; - } - - /// - /// Get the gain at the specified number of frequency points - /// - /// - /// - public float[] FilterGain(int freqPoints) - { - // Filter gain at uniform frequency intervals - float[] g = new float[freqPoints]; - double theta, s, c, sac, sas, sbc, sbs; - float gMax = -100.0f; - float sc = 10.0f / (float)Math.Log(10.0f); - double t = Math.PI / (freqPoints - 1); - - for (int i = 0; i < freqPoints; i++) - { - theta = i * t; - - if (i == 0) - theta = Math.PI * 0.0001; - - if (i == freqPoints - 1) - theta = Math.PI * 0.9999; - - sac = 0.0f; - sas = 0.0f; - sbc = 0.0f; - sbs = 0.0f; - - for (int k = 0; k <= m_order; k++) - { - c = Math.Cos(k * theta); - s = Math.Sin(k * theta); - sac += c * m_aCoeff[k]; - sas += s * m_aCoeff[k]; - sbc += c * m_bCoeff[k]; - sbs += s * m_bCoeff[k]; - } - - g[i] = sc * (float)Math.Log((Sqr(sac) + Sqr(sas)) / (Sqr(sbc) + Sqr(sbs))); - gMax = Math.Max(gMax, g[i]); - } - - // Normalize to 0 dB maximum gain - for (int i = 0; i < freqPoints; i++) - g[i] -= gMax; - - // Normalize numerator (a) coefficients - float normFactor = (float)Math.Pow(10.0, -0.05 * gMax); - - for (int i = 0; i <= m_order; i++) - m_aCoeff[i] *= normFactor; - - return g; - } - } -} diff --git a/Vocaluxe/Lib/Sound/Record/PitchTracker/Pitch/PitchDsp.cs b/Vocaluxe/Lib/Sound/Record/PitchTracker/Pitch/PitchDsp.cs deleted file mode 100644 index 3fc22ee6..00000000 --- a/Vocaluxe/Lib/Sound/Record/PitchTracker/Pitch/PitchDsp.cs +++ /dev/null @@ -1,561 +0,0 @@ -using System; - -namespace Vocaluxe.Lib.Sound.Record.PitchTracker.Pitch -{ - /// - /// Pitch related DSP - /// - public class PitchDsp - { - public static readonly double InverseLog2 = 1.0 / Math.Log10(2.0); - - private const int kCourseOctaveSteps = 96; - private const int kScanHiSize = 31; - private const float kScanHiFreqStep = 1.005f; - private const int kMinMidiNote = 21; // A0 - private const int kMaxMidiNote = 108; // C8 - - private float m_minPitch; - private float m_maxPitch; - private int m_minNote; - private int m_maxNote; - private int m_blockLen14; // 1/4 block len - private int m_blockLen24; // 2/4 block len - private int m_blockLen34; // 3/4 block len - private int m_blockLen44; // 4/4 block len - private double m_sampleRate; - - private int m_numCourseSteps; - private float[] m_pCourseFreqOffset; - private float[] m_pCourseFreq; - private float[] m_scanHiOffset = new float[kScanHiSize]; - private float[] m_peakBuf = new float[kScanHiSize]; - private int m_prevPitchIdx; - private float[] m_detectCurve; - - /// - /// Constructor - /// - /// - /// - /// - /// - public PitchDsp(double sampleRate, float minPitch, float maxPitch) - { - m_sampleRate = sampleRate; - m_minPitch = minPitch; - m_maxPitch = maxPitch; - - m_minNote = (int)(PitchToMidiNote(m_minPitch) + 0.5f) + 2; - m_maxNote = (int)(PitchToMidiNote(m_maxPitch) + 0.5f) - 2; - - m_blockLen44 = (int)(m_sampleRate / m_minPitch + 0.5f); - m_blockLen34 = (m_blockLen44 * 3) / 4; - m_blockLen24 = m_blockLen44 / 2; - m_blockLen14 = m_blockLen44 / 4; - - m_numCourseSteps = (int)((Math.Log((double)m_maxPitch / (double)m_minPitch) / Math.Log(2.0)) * kCourseOctaveSteps + 0.5) + 3; - - m_pCourseFreqOffset = new float[m_numCourseSteps + 10000]; - m_pCourseFreq = new float[m_numCourseSteps + 10000]; - - m_detectCurve = new float[m_numCourseSteps]; - - double freqStep = 1.0 / Math.Pow(2.0, 1.0 / kCourseOctaveSteps); - double curFreq = m_maxPitch / freqStep; - - // frequency is stored from high to low - for (int idx = 0; idx < m_numCourseSteps; idx++) - { - m_pCourseFreq[idx] = (float)curFreq; - m_pCourseFreqOffset[idx] = (float)(m_sampleRate / curFreq); - curFreq *= freqStep; - } - - for (int idx = 0; idx < kScanHiSize; idx++) - m_scanHiOffset[idx] = (float)Math.Pow(kScanHiFreqStep, (kScanHiSize / 2) - idx); - } - - /// - /// Get the max detected pitch - /// - public float MaxPitch - { - get { return m_maxPitch; } - } - - /// - /// Get the min detected pitch - /// - public float MinPitch - { - get { return m_minPitch; } - } - - /// - /// Get the max note - /// - public int MaxNote - { - get { return m_maxNote; } - } - - /// - /// Get the min note - /// - public int MinNote - { - get { return m_minNote; } - } - - /// - /// Detect the pitch - /// - public float DetectPitch(float[] samplesLo, float[] samplesHi) - { - return DetectPitchLo(samplesLo, samplesHi); - } - - /// - /// Low resolution pitch detection - /// - /// - /// - /// - /// - /// - /// - private float DetectPitchLo(float[] samplesLo, float[] samplesHi) - { - m_detectCurve.Clear(); - - const int skipSize = 8; - const int peakScanSize = 23; - const int peakScanSizeHalf = peakScanSize / 2; - - float peakThresh1 = 200.0f; - float peakThresh2 = 600.0f; - bool bufferSwitched = false; - - for (int idx = 0; idx < m_numCourseSteps; idx += skipSize) - { - int blockLen = Math.Min(m_blockLen44, (int)m_pCourseFreqOffset[idx] * 2); - float[] curSamples = null; - - // 258 is at 250 Hz, which is the switchover frequency for the two filters - bool loBuffer = idx >= 258; - - if (loBuffer) - { - if (!bufferSwitched) - { - m_detectCurve.Clear(258 - peakScanSizeHalf, 258 + peakScanSizeHalf); - bufferSwitched = true; - } - - curSamples = samplesLo; - } - else - curSamples = samplesHi; - - int stepSizeLoRes = blockLen / 10; - int stepSizeHiRes = Math.Max(1, Math.Min(5, idx * 5 / m_numCourseSteps)); - - float fValue = RatioAbsDiffLinear(curSamples, idx, blockLen, stepSizeLoRes, false); - - if (fValue > peakThresh1) - { - // Do a closer search for the peak - int peakIdx = -1; - float peakVal = 0.0f; - float prevVal = 0.0f; - int dir = 4; // start going forward - int curPos = idx; // start at center of the scan range - int begSearch = Math.Max(idx - peakScanSizeHalf, 0); - int endSearch = Math.Min(idx + peakScanSizeHalf, m_numCourseSteps - 1); - - while (curPos >= begSearch && curPos < endSearch) - { - float curVal = RatioAbsDiffLinear(curSamples, curPos, blockLen, stepSizeHiRes, true); - - if (peakVal < curVal) - { - peakVal = curVal; - peakIdx = curPos; - } - - if (prevVal > curVal) - { - dir = -dir >> 1; - - if (dir == 0) - { - if (peakVal > peakThresh2 && peakIdx >= 6 && peakIdx <= m_numCourseSteps - 7) - { - float fValL = RatioAbsDiffLinear(curSamples, peakIdx - 5, blockLen, stepSizeHiRes, true); - float fValR = RatioAbsDiffLinear(curSamples, peakIdx + 5, blockLen, stepSizeHiRes, true); - float fPointy = peakVal / (fValL + fValR) * 2.0f; - - float minPointy = (m_prevPitchIdx > 0 && Math.Abs(m_prevPitchIdx - peakIdx) < 10) ? 1.2f : 1.5f; - - if (fPointy > minPointy) - { - float pitchHi = DetectPitchHi(curSamples, peakIdx); - - if (pitchHi > 1.0f) - { - m_prevPitchIdx = peakIdx; - return pitchHi; - } - } - } - - break; - } - } - - prevVal = curVal; - curPos += dir; - } - } - } - - m_prevPitchIdx = 0; - return 0.0f; - } - - /// - /// High resolution pitch detection - /// - /// - /// - /// - private float DetectPitchHi(float[] samples, int lowFreqIdx) - { - int peakIdx = -1; - float prevVal = 0.0f; - int dir = 4; // start going forward - int curPos = kScanHiSize >> 1; // start at center of the scan range - - m_peakBuf.Clear(); - - float offset = m_pCourseFreqOffset[lowFreqIdx]; - - while (curPos >= 0 && curPos < kScanHiSize) - { - if (m_peakBuf[curPos] == 0.0f) - m_peakBuf[curPos] = SumAbsDiffHermite(samples, offset * m_scanHiOffset[curPos], m_blockLen44, 1); - - if (peakIdx < 0 || m_peakBuf[peakIdx] < m_peakBuf[curPos]) - peakIdx = curPos; - - if (prevVal > m_peakBuf[curPos]) - { - dir = -dir >> 1; - - if (dir == 0) - { - // found the peak - float minVal = Math.Min(m_peakBuf[peakIdx - 1], m_peakBuf[peakIdx + 1]); - - minVal -= minVal * (1.0f / 32.0f); - - var y1 = (float)Math.Log10(m_peakBuf[peakIdx - 1] - minVal); - var y2 = (float)Math.Log10(m_peakBuf[peakIdx] - minVal); - var y3 = (float)Math.Log10(m_peakBuf[peakIdx + 1] - minVal); - - float fIdx = (float)peakIdx + (y3 - y1) / (2.0f * (2.0f * y2 - y1 - y3)); - - return (float)Math.Pow(kScanHiFreqStep, fIdx - (kScanHiSize / 2)) * m_pCourseFreq[lowFreqIdx]; - } - } - - prevVal = m_peakBuf[curPos]; - curPos += dir; - } - - return 0.0f; - } - - /// - /// Create a sine wave with the specified frequency, amplitude and starting angle. - /// Returns the updated angle. - /// - /// - /// - /// - /// - /// - /// - public static double CreateSineWave(float[] buffer, int numSamples, float sampleRate, - float freq, float amplitude, double startAngle) - { - double angleStep = freq / sampleRate * Math.PI * 2.0; - double curAngle = startAngle; - - for (int idx = 0; idx < numSamples; idx++) - { - buffer[idx] = (float)Math.Sin(curAngle) * amplitude; - - curAngle += angleStep; - - while (curAngle > Math.PI) - curAngle -= Math.PI * 2.0; - } - - return curAngle; - } - - /// - /// Returns true if the level is above the specified value - /// - /// - /// - /// - /// - /// - public bool LevelIsAbove(float[] buffer, int len, float level) - { - if (buffer == null || buffer.Length == 0) - return false; - - int endIdx = Math.Min(buffer.Length, len); - - for (int idx = 0; idx < endIdx; idx++) - { - if (Math.Abs(buffer[idx]) >= level) - return true; - } - - return false; - } - - /// - /// Copy the data from the source to the destination buffer - /// - /// - /// - /// - /// - public static void CopyBuffer(T[] source, int srcStart, T[] destination, int dstStart, int length) - { - if (length < 0) - throw new ArgumentOutOfRangeException("length"); - - if (source == null || source.Length < srcStart + length) - throw new Exception("Source buffer is null or not large enough"); - - if (destination == null || destination.Length < dstStart + length) - throw new Exception("Destination buffer is null or not large enough"); - - int srcIdx = srcStart; - int dstIdx = dstStart; - - for (int idx = 0; idx < length; idx++) - destination[dstIdx++] = source[srcIdx++]; - } - - /// - /// // 4-point, 3rd-order Hermite (x-form) - /// - private float InterpolateHermite(float fY0, float fY1, float fY2, float fY3, float frac) - { - float c1 = 0.5f * (fY2 - fY0); - float c3 = 1.5f * (fY1 - fY2) + 0.5f * (fY3 - fY0); - float c2 = fY0 - fY1 + c1 - c3; - - return ((c3 * frac + c2) * frac + c1) * frac + fY1; - } - - /// - /// Linear interpolation - /// nFrac is based on 1.0 = 256 - /// - private float InterpolateLinear(float y0, float y1, float frac) - { - return y0 * (1.0f - frac) + y1 * frac; - } - - /// - /// Medium Low res SumAbsDiff - /// - private float RatioAbsDiffLinear(float[] samples, int freqIdx, int blockLen, int stepSize, bool hiRes) - { - if (hiRes && m_detectCurve[freqIdx] > 0.0f) - return m_detectCurve[freqIdx]; - - var offsetInt = (int)m_pCourseFreqOffset[freqIdx]; - float offsetFrac = m_pCourseFreqOffset[freqIdx] - offsetInt; - float rect = 0.0f; - float absDiff = 0.01f; // prevent divide by zero - int count = 0; - float interp; - float sample; - - // Do a scan using linear interpolation and the specified step size - for (int idx = 0; idx < blockLen; idx += stepSize, count++) - { - sample = samples[idx]; - interp = InterpolateLinear(samples[offsetInt + idx], samples[offsetInt + idx + 1], offsetFrac); - absDiff += Math.Abs(sample - interp); - rect += Math.Abs(sample) + Math.Abs(interp); - } - - float finalVal = rect / absDiff * 100.0f; - - if (hiRes) - m_detectCurve[freqIdx] = finalVal; - - return finalVal; - } - - /// - /// Medium High res SumAbsDiff - /// - private float SumAbsDiffHermite(float[] samples, float fOffset, int blockLen, int stepSize) - { - var offsetInt = (int)fOffset; - float offsetFrac = fOffset - offsetInt; - float value = 0.001f; // prevent divide by zero - int count = 0; - - // do a scan using linear interpolation and the specified step size - for (int idx = 0; idx < blockLen; idx += stepSize, count++) - { - int offsetIdx = offsetInt + idx; - - value += Math.Abs(samples[idx] - InterpolateHermite(samples[offsetIdx - 1], - samples[offsetIdx], - samples[offsetIdx + 1], - samples[offsetIdx + 2], - offsetFrac)); - } - - return count / value; - } - - /// - /// Get the MIDI note and cents of the pitch - /// - /// - /// - /// - /// - public static bool PitchToMidiNote(float pitch, out int note, out int cents) - { - if (pitch < 20.0f) - { - note = 0; - cents = 0; - return false; - } - - float fNote = (float)((12.0 * Math.Log10(pitch / 55.0) * InverseLog2)) + 33.0f; - note = (int)(fNote + 0.5f); - cents = (int)((note - fNote) * 100); - return true; - } - - /// - /// Get the pitch from the MIDI note - /// - /// - /// - public static float PitchToMidiNote(float pitch) - { - if (pitch < 20.0f) - return 0.0f; - - return (float)(12.0 * Math.Log10(pitch / 55.0) * InverseLog2) + 33.0f; - } - - /// - /// Get the pitch from the MIDI note - /// - /// - /// - public float MidiNoteToPitch(float note) - { - if (note < 33.0f) - return 0.0f; - - float pitch = (float)Math.Pow(10.0, (note - 33.0f) / InverseLog2 / 12.0f) * 55.0f; - return pitch <= m_maxPitch ? pitch : 0.0f; - } - - /// - /// Format a midi note to text - /// - /// - /// - /// - /// - public static string GetNoteName(int note, bool sharps, bool showOctave) - { - if (note < kMinMidiNote || note > kMaxMidiNote) - return null; - - note -= kMinMidiNote; - - int octave = (note + 9) / 12; - note = note % 12; - string noteText = null; - - switch (note) - { - case 0: - noteText = "A"; - break; - - case 1: - noteText = sharps ? "A#" : "Bb"; - break; - - case 2: - noteText = "B"; - break; - - case 3: - noteText = "C"; - break; - - case 4: - noteText = sharps ? "C#" : "Db"; - break; - - case 5: - noteText = "D"; - break; - - case 6: - noteText = sharps ? "D#" : "Eb"; - break; - - case 7: - noteText = "E"; - break; - - case 8: - noteText = "F"; - break; - - case 9: - noteText = sharps ? "F#" : "Gb"; - break; - - case 10: - noteText = "G"; - break; - - case 11: - noteText = sharps ? "G#" : "Ab"; - break; - } - - if (showOctave) - noteText += " " + octave.ToString(); - - return noteText; - } - } -} \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/Record/PitchTracker/Pitch/PitchTracker.cs b/Vocaluxe/Lib/Sound/Record/PitchTracker/Pitch/PitchTracker.cs deleted file mode 100644 index 59522197..00000000 --- a/Vocaluxe/Lib/Sound/Record/PitchTracker/Pitch/PitchTracker.cs +++ /dev/null @@ -1,431 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections; - -namespace Vocaluxe.Lib.Sound.Record.PitchTracker.Pitch -{ - /// - /// Tracks pitch - /// - // ReSharper disable InconsistentNaming - public class PitchTracker - { - private const int kOctaveSteps = 96; - private const int kStepOverlap = 4; - private const float kMinFreq = 50.0f; // A1, Midi note 33, 55.0Hz - private const float kMaxFreq = 1800.0f; // A#6. Midi note 92 - private const int kStartCircular = 40; // how far into the sample buffer do we start checking (allow for filter settling) - private const float kDetectOverlapSec = 0.005f; - private const float kMaxOctaveSecRate = 10.0f; - - private const float kAvgOffset = 0.005f; // time offset between pitch averaging values - private const int kAvgCount = 1; // number of average pitch samples to take - private const float kCircularBufSaveTime = 1.0f; // Amount of samples to store in the history buffer - - private PitchDsp m_dsp; - private CircularBuffer m_circularBufferLo; - private CircularBuffer m_circularBufferHi; - private double m_sampleRate; - private float m_detectLevelThreshold = 0.01f; // -40dB - private int m_pitchRecordsPerSecond = 50; // default is 50, or one record every 20ms - - private float[] m_pitchBufLo; - private float[] m_pitchBufHi; - private int m_pitchBufSize; - private int m_samplesPerPitchBlock; - private int m_curPitchIndex; - private long m_curPitchSamplePos; - - private int m_detectOverlapSamples; - private float m_maxOverlapDiff; - - private bool m_recordPitchRecords; - private int m_pitchRecordHistorySize; - private List m_pitchRecords = new List(); - private PitchRecord m_curPitchRecord = new PitchRecord(); - - private IIRFilter m_iirFilterLoLo; - private IIRFilter m_iirFilterLoHi; - private IIRFilter m_iirFilterHiLo; - private IIRFilter m_iirFilterHiHi; - - public float LastMaxVol = 0; - - public delegate void PitchDetectedHandler(PitchTracker sender, PitchRecord pitchRecord); - - public event PitchDetectedHandler PitchDetected; - - /// - /// Constructor - /// - public PitchTracker() {} - - /// - /// Set the sample rate - /// - public double SampleRate - { - set - { - if (m_sampleRate == value) - return; - - m_sampleRate = value; - Setup(); - } - } - - /// - /// Set the detect level threshold, The value must be between 0.0001f and 1.0f (-80 dB to 0 dB) - /// - public float DetectLevelThreshold - { - set - { - float newValue = Math.Max(0.0001f, Math.Min(1.0f, value)); - - if (m_detectLevelThreshold == newValue) - return; - - m_detectLevelThreshold = newValue; - //Setup(); - } - get { return m_detectLevelThreshold; } - } - - /// - /// Return the samples per pitch block - /// - public int SamplesPerPitchBlock - { - get { return m_samplesPerPitchBlock; } - } - - /// - /// Get or set the number of pitch records per second (default is 50, or one record every 20ms) - /// - public int PitchRecordsPerSecond - { - get { return m_pitchRecordsPerSecond; } - set - { - m_pitchRecordsPerSecond = Math.Max(1, Math.Min(100, value)); - Setup(); - } - } - - /// - /// Get or set whether pitch records should be recorded into a history buffer - /// - public bool RecordPitchRecords - { - get { return m_recordPitchRecords; } - set - { - if (m_recordPitchRecords == value) - return; - - m_recordPitchRecords = value; - - if (!m_recordPitchRecords) - m_pitchRecords = new List(); - } - } - - /// - /// Get or set the max number of pitch records to keep. A value of 0 means no limit. - /// Don't leave this at 0 when RecordPitchRecords is true and this is used in a realtime - /// application since the buffer will grow indefinately! - /// - public int PitchRecordHistorySize - { - get { return m_pitchRecordHistorySize; } - set - { - m_pitchRecordHistorySize = value; - - m_pitchRecords.Capacity = m_pitchRecordHistorySize; - } - } - - /// - /// Get the current pitch records - /// - public IList PitchRecords - { - get { return m_pitchRecords.AsReadOnly(); } - } - - /// - /// Get the latest pitch record - /// - public PitchRecord CurrentPitchRecord - { - get { return m_curPitchRecord; } - } - - /// - /// Get the current pitch position - /// - public long CurrentPitchSamplePosition - { - get { return m_curPitchSamplePos; } - } - - /// - /// Get the minimum frequency that can be detected - /// - public static float MinDetectFrequency - { - get { return kMinFreq; } - } - - /// - /// Get the maximum frequency that can be detected - /// - public static float MaxDetectFrequency - { - get { return kMaxFreq; } - } - - /// - /// Get the frequency step - /// - public static double FrequencyStep - { - get { return Math.Pow(2.0, 1.0 / kOctaveSteps); } - } - - /// - /// Get the number of samples that the detected pitch is offset from the input buffer. - /// This is just an estimate to sync up the samples and detected pitch - /// - public int DetectSampleOffset - { - get { return (m_pitchBufSize + m_detectOverlapSamples) / 2; } - } - - /// - /// Reset the pitch tracker. Call this when the sample position is - /// not consecutive from the previous position - /// - public void Reset() - { - m_curPitchIndex = 0; - m_curPitchSamplePos = 0; - m_pitchRecords.Clear(); - m_iirFilterLoLo.Reset(); - m_iirFilterLoHi.Reset(); - m_iirFilterHiLo.Reset(); - m_iirFilterHiHi.Reset(); - m_circularBufferLo.Reset(); - m_circularBufferLo.Clear(); - m_circularBufferHi.Reset(); - m_circularBufferHi.Clear(); - m_pitchBufLo.Clear(); - m_pitchBufHi.Clear(); - - m_circularBufferLo.StartPosition = -m_detectOverlapSamples; - m_circularBufferLo.Available = m_detectOverlapSamples; - m_circularBufferHi.StartPosition = -m_detectOverlapSamples; - m_circularBufferHi.Available = m_detectOverlapSamples; - } - - /// - /// Process the passed in buffer of data. During this call, the PitchDetected event will - /// be fired zero or more times, depending how many pitch records will fit in the new - /// and previously cached buffer. - /// - /// This means that there is no size restriction on the buffer that is passed into ProcessBuffer. - /// For instance, ProcessBuffer can be called with one very large buffer that contains all of the - /// audio to be processed (many PitchDetected events will be fired), or just a small buffer at - /// a time which is more typical for realtime applications. In the latter case, the PitchDetected - /// event might not be fired at all since additional calls must first be made to accumulate enough - /// data do another pitch detect operation. - /// - /// Input buffer. Samples must be in the range -1.0 to 1.0 - /// - /// Number of samples to process. Zero means all samples in the buffer - public void ProcessBuffer(float[] inBuffer, int sampleCount = 0) - { - if (inBuffer == null) - throw new ArgumentNullException("inBuffer", "Input buffer cannot be null"); - - int samplesProcessed = 0; - int srcLength = sampleCount == 0 ? inBuffer.Length : Math.Min(sampleCount, inBuffer.Length); - - float maxVolume = LastMaxVol * 0.85f; - - while (samplesProcessed < srcLength) - { - int frameCount = Math.Min(srcLength - samplesProcessed, m_pitchBufSize + m_detectOverlapSamples); - - m_iirFilterLoLo.FilterBuffer(inBuffer, samplesProcessed, m_pitchBufLo, 0, frameCount); - m_iirFilterLoHi.FilterBuffer(m_pitchBufLo, 0, m_pitchBufLo, 0, frameCount); - - m_iirFilterHiLo.FilterBuffer(inBuffer, samplesProcessed, m_pitchBufHi, 0, frameCount); - m_iirFilterHiHi.FilterBuffer(m_pitchBufHi, 0, m_pitchBufHi, 0, frameCount); - - m_circularBufferLo.WriteBuffer(m_pitchBufLo, frameCount); - m_circularBufferHi.WriteBuffer(m_pitchBufHi, frameCount); - - // Loop while there is enough samples in the circular buffer - while (m_circularBufferLo.ReadBuffer(m_pitchBufLo, m_curPitchSamplePos, m_pitchBufSize + m_detectOverlapSamples)) - { - float pitch1; - float pitch2; - float detectedPitch = 0.0f; - - m_circularBufferHi.ReadBuffer(m_pitchBufHi, m_curPitchSamplePos, m_pitchBufSize + m_detectOverlapSamples); - - maxVolume = 0f; - for (int i = 0; i < (m_pitchBufSize + m_detectOverlapSamples) / 2; i++) - { - if (maxVolume < m_pitchBufHi[i]) - maxVolume = m_pitchBufHi[i]; - if (maxVolume < m_pitchBufLo[i]) - maxVolume = m_pitchBufLo[i]; - } - - pitch1 = (maxVolume >= m_detectLevelThreshold) ? m_dsp.DetectPitch(m_pitchBufLo, m_pitchBufHi) : 0f; - - if (pitch1 > 0.0f) - { - // Shift the buffers left by the overlapping amount - m_pitchBufLo.Copy(m_pitchBufLo, m_detectOverlapSamples - 1, 0, m_pitchBufSize); - m_pitchBufHi.Copy(m_pitchBufHi, m_detectOverlapSamples - 1, 0, m_pitchBufSize); - - pitch2 = m_dsp.DetectPitch(m_pitchBufLo, m_pitchBufHi); - - if (pitch2 > 0.0f) - { - float fDiff = Math.Max(pitch1, pitch2) / Math.Min(pitch1, pitch2) - 1.0f; - - if (fDiff < m_maxOverlapDiff) - detectedPitch = (pitch1 + pitch2) * 0.5f; - } - } - - // Log the pitch record - AddPitchRecord(detectedPitch); - - m_curPitchSamplePos += m_samplesPerPitchBlock; - m_curPitchIndex++; - } - - samplesProcessed += frameCount; - } - LastMaxVol = maxVolume; - } - - /// - /// Setup - /// - private void Setup() - { - if (m_sampleRate < 1.0f) - return; - - m_dsp = new PitchDsp(m_sampleRate, kMinFreq, kMaxFreq); - - m_iirFilterLoLo = new IIRFilter(); - m_iirFilterLoLo.Proto = IIRFilter.ProtoType.Butterworth; - m_iirFilterLoLo.Type = IIRFilter.FilterType.HP; - m_iirFilterLoLo.Order = 5; - m_iirFilterLoLo.FreqLow = 45.0f; - m_iirFilterLoLo.SampleRate = (float)m_sampleRate; - - m_iirFilterLoHi = new IIRFilter(); - m_iirFilterLoHi.Proto = IIRFilter.ProtoType.Butterworth; - m_iirFilterLoHi.Type = IIRFilter.FilterType.LP; - m_iirFilterLoHi.Order = 5; - m_iirFilterLoHi.FreqHigh = 280.0f; - m_iirFilterLoHi.SampleRate = (float)m_sampleRate; - - m_iirFilterHiLo = new IIRFilter(); - m_iirFilterHiLo.Proto = IIRFilter.ProtoType.Butterworth; - m_iirFilterHiLo.Type = IIRFilter.FilterType.HP; - m_iirFilterHiLo.Order = 5; - m_iirFilterHiLo.FreqLow = 45.0f; - m_iirFilterHiLo.SampleRate = (float)m_sampleRate; - - m_iirFilterHiHi = new IIRFilter(); - m_iirFilterHiHi.Proto = IIRFilter.ProtoType.Butterworth; - m_iirFilterHiHi.Type = IIRFilter.FilterType.LP; - m_iirFilterHiHi.Order = 5; - m_iirFilterHiHi.FreqHigh = 2000.0f; - m_iirFilterHiHi.SampleRate = (float)m_sampleRate; - - m_detectOverlapSamples = (int)(kDetectOverlapSec * m_sampleRate); - m_maxOverlapDiff = kMaxOctaveSecRate * kDetectOverlapSec; - - m_pitchBufSize = (int)(((1.0f / (float)kMinFreq) * 2.0f + ((kAvgCount - 1) * kAvgOffset)) * m_sampleRate) + 16; - m_pitchBufLo = new float[m_pitchBufSize + m_detectOverlapSamples]; - m_pitchBufHi = new float[m_pitchBufSize + m_detectOverlapSamples]; - m_samplesPerPitchBlock = (int)Math.Round(m_sampleRate / m_pitchRecordsPerSecond); - - m_circularBufferLo = new CircularBuffer((int)(kCircularBufSaveTime * m_sampleRate + 0.5f) + 10000); - m_circularBufferHi = new CircularBuffer((int)(kCircularBufSaveTime * m_sampleRate + 0.5f) + 10000); - } - - /// - /// The pitch was detected - add the record - /// - /// - private void AddPitchRecord(float pitch) - { - int midiNote = 0; - int midiCents = 0; - - PitchDsp.PitchToMidiNote(pitch, out midiNote, out midiCents); - - var record = new PitchRecord(); - - record.RecordIndex = m_curPitchIndex; - record.Pitch = pitch; - record.MidiNote = midiNote; - record.MidiCents = midiCents; - - m_curPitchRecord = record; - - if (m_recordPitchRecords) - { - if (m_pitchRecordHistorySize > 0 && m_pitchRecords.Count >= m_pitchRecordHistorySize) - m_pitchRecords.RemoveAt(0); - - m_pitchRecords.Add(record); - } - - if (this.PitchDetected != null) - this.PitchDetected(this, record); - } - - /// - /// Stores one record - /// - public struct PitchRecord - { - /// - /// The index of the pitch record since the last Reset call - /// - public int RecordIndex { get; set; } - - /// - /// The detected pitch - /// - public float Pitch { get; set; } - - /// - /// The detected MIDI note, or 0 for no pitch - /// - public int MidiNote { get; set; } - - /// - /// The offset from the detected MIDI note in cents, from -50 to +50. - /// - public int MidiCents { get; set; } - } - } - - // ReSharper restore InconsistentNaming -} \ No newline at end of file diff --git a/Vocaluxe/Vocaluxe.csproj b/Vocaluxe/Vocaluxe.csproj index 02af3467..0c0d6aac 100644 --- a/Vocaluxe/Vocaluxe.csproj +++ b/Vocaluxe/Vocaluxe.csproj @@ -292,13 +292,7 @@ - - - - - -