// These work by shifting the signal until it seems to correlate with itself. // In other words if the signal looks very similar to (signal shifted 200 samples) than the fundamental period is probably 200 samples // Note that the algorithm only works well when there's only one prominent fundamental. // This could be optimized by looking at the rate of change to determine a maximum without testing all periods. private static double[] detectPitchCalculation(WaveAudio w, double minHz, double maxHz, int nCandidates, int nResolution, PitchDetectAlgorithm algorithm) { // note that higher frequency means lower period int nLowPeriodInSamples = hzToPeriodInSamples(maxHz, w.getSampleRate()); int nHiPeriodInSamples = hzToPeriodInSamples(minHz, w.getSampleRate()); if (nHiPeriodInSamples <= nLowPeriodInSamples) throw new Exception("Bad range for pitch detection."); if (w.getNumChannels() != 1) throw new Exception("Only mono supported."); double[] samples = w.data[0]; if (samples.Length < nHiPeriodInSamples) throw new Exception("Not enough samples."); // both algorithms work in a similar way // they yield an array of data, and then we find the index at which the value is highest. double[] results = new double[nHiPeriodInSamples - nLowPeriodInSamples]; if (algorithm == PitchDetectAlgorithm.Amdf) { for (int period = nLowPeriodInSamples; period < nHiPeriodInSamples; period += nResolution) { double sum = 0; // for each sample, see how close it is to a sample n away. Then sum these. for (int i = 0; i < samples.Length - period; i++) sum += Math.Abs(samples[i] - samples[i + period]); double mean = sum / (double)samples.Length; mean *= -1; //somewhat of a hack. We are trying to find the minimum value, but our findBestCandidates finds the max. value. results[period - nLowPeriodInSamples] = mean; } } else if (algorithm == PitchDetectAlgorithm.Autocorrelation) { for (int period = nLowPeriodInSamples; period < nHiPeriodInSamples; period += nResolution) { double sum = 0; // for each sample, find correlation. (If they are far apart, small) for (int i = 0; i < samples.Length - period; i++) sum += samples[i] * samples[i + period]; double mean = sum / (double)samples.Length; results[period - nLowPeriodInSamples] = mean; } } // find the best indices int[] bestIndices = findBestCandidates(nCandidates, ref results); //note findBestCandidates modifies parameter // convert back to Hz double[] res = new double[nCandidates]; for (int i=0; i<nCandidates;i++) res[i] = periodInSamplesToHz(bestIndices[i]+nLowPeriodInSamples, w.getSampleRate()); return res; }
/// <param name="nCandidates">Number of results to return</param> public static double[] DetectPitchCandidates(WaveAudio w, double minHz, double maxHz, int nCandidates, PitchDetectAlgorithm algorithm) { return detectPitchCalculation(w, minHz, maxHz, nCandidates, 1, algorithm); }
/// <param name="nCandidates">Number of results to return</param> /// <param name="nResolution">Resolution. A value of 1, default, is slowest but most precise.</param> public static double[] DetectPitchCandidates(WaveAudio w, double minHz, double maxHz, int nCandidates, PitchDetectAlgorithm algorithm, int nResolution) { if (nResolution < 1) throw new Exception("Resultion must be >= 1"); return detectPitchCalculation(w, minHz, maxHz, nCandidates, nResolution, algorithm); }
public static double DetectPitch(WaveAudio w, double minHz, double maxHz, PitchDetectAlgorithm algorithm) { return detectPitchCalculation(w, minHz, maxHz, 1, 1, algorithm)[0]; }
// These work by shifting the signal until it seems to correlate with itself. // In other words if the signal looks very similar to (signal shifted 200 samples) than the fundamental period is probably 200 samples // Note that the algorithm only works well when there's only one prominent fundamental. // This could be optimized by looking at the rate of change to determine a maximum without testing all periods. private static double[] detectPitchCalculation(WaveAudio w, double minHz, double maxHz, int nCandidates, int nResolution, PitchDetectAlgorithm algorithm) { // note that higher frequency means lower period int nLowPeriodInSamples = hzToPeriodInSamples(maxHz, w.getSampleRate()); int nHiPeriodInSamples = hzToPeriodInSamples(minHz, w.getSampleRate()); if (nHiPeriodInSamples <= nLowPeriodInSamples) { throw new Exception("Bad range for pitch detection."); } if (w.getNumChannels() != 1) { throw new Exception("Only mono supported."); } double[] samples = w.data[0]; if (samples.Length < nHiPeriodInSamples) { throw new Exception("Not enough samples."); } // both algorithms work in a similar way // they yield an array of data, and then we find the index at which the value is highest. double[] results = new double[nHiPeriodInSamples - nLowPeriodInSamples]; if (algorithm == PitchDetectAlgorithm.Amdf) { for (int period = nLowPeriodInSamples; period < nHiPeriodInSamples; period += nResolution) { double sum = 0; // for each sample, see how close it is to a sample n away. Then sum these. for (int i = 0; i < samples.Length - period; i++) { sum += Math.Abs(samples[i] - samples[i + period]); } double mean = sum / (double)samples.Length; mean *= -1; //somewhat of a hack. We are trying to find the minimum value, but our findBestCandidates finds the max. value. results[period - nLowPeriodInSamples] = mean; } } else if (algorithm == PitchDetectAlgorithm.Autocorrelation) { for (int period = nLowPeriodInSamples; period < nHiPeriodInSamples; period += nResolution) { double sum = 0; // for each sample, find correlation. (If they are far apart, small) for (int i = 0; i < samples.Length - period; i++) { sum += samples[i] * samples[i + period]; } double mean = sum / (double)samples.Length; results[period - nLowPeriodInSamples] = mean; } } // find the best indices int[] bestIndices = findBestCandidates(nCandidates, ref results); //note findBestCandidates modifies parameter // convert back to Hz double[] res = new double[nCandidates]; for (int i = 0; i < nCandidates; i++) { res[i] = periodInSamplesToHz(bestIndices[i] + nLowPeriodInSamples, w.getSampleRate()); } return(res); }
/// <param name="nCandidates">Number of results to return</param> /// <param name="nResolution">Resolution. A value of 1, default, is slowest but most precise.</param> public static double[] DetectPitchCandidates(WaveAudio w, double minHz, double maxHz, int nCandidates, PitchDetectAlgorithm algorithm, int nResolution) { if (nResolution < 1) { throw new Exception("Resultion must be >= 1"); } return(detectPitchCalculation(w, minHz, maxHz, nCandidates, nResolution, algorithm)); }
/// <param name="nCandidates">Number of results to return</param> public static double[] DetectPitchCandidates(WaveAudio w, double minHz, double maxHz, int nCandidates, PitchDetectAlgorithm algorithm) { return(detectPitchCalculation(w, minHz, maxHz, nCandidates, 1, algorithm)); }
public static double DetectPitch(WaveAudio w, double minHz, double maxHz, PitchDetectAlgorithm algorithm) { return(detectPitchCalculation(w, minHz, maxHz, 1, 1, algorithm)[0]); }