// Path: app/src/main/java/com/chrynan/android_guitar_tuner/tuner/config/AudioConfig.java
// public interface AudioConfig {
// 
//     /**
//      * The sample rate that the Audio Recorder is using, in hertz. Note that this value is device
//      * dependent.
//      *
//      * @return The sample rate of the audio recording.
//      */
//     int getSampleRate();
// 
//     /**
//      * The size of the underlying input waveform buffer array used by the Audio Recorder. This should be
//      * larger than the value returned by {@link #getReadSize()}. This value should only be used to
//      * create an instance of an Audio Recorder and when reading or performing operations on the
//      * waveform buffer array, {@link #getReadSize()} should be used. Note that an implementation of
//      * this method should return a valid value for the device.
//      *
//      * @return The size of the underlying waveform buffer array.
//      */
//     int getInputBufferSize();
// 
//     /**
//      * The size of the underlying output waveform buffer array used by the Audio Recorder. This should be
//      * larger than the value returned by {@link #getReadSize()}. This value should only be used to
//      * create an instance of an Audio Recorder and when reading or performing operations on the
//      * waveform buffer array, {@link #getReadSize()} should be used. Note that an implementation of
//      * this method should return a valid value for the device.
//      *
//      * @return The size of the underlying waveform buffer array.
//      */
//     int getOutputBufferSize();
// 
//     /**
//      * The size of the input array of data that should be read from the buffer. Use this value when
//      * processing the buffer of waveform data.
//      *
//      * @return The size of values read from the underlying waveform buffer array.
//      */
//     int getReadSize();
// 
//     /**
//      * The size of the array of data that should be written to the output buffer. Use this value when
//      * processing the buffer of waveform data.
//      *
//      * @return The size of values written to the underlying waveform buffer array.
//      */
//     int getWriteSize();
// 
//     /**
//      * The input channel to retrieve the input data.
//      *
//      * @return The input channel
//      */
//     int getInputChannel();
// 
//     /**
//      * The output channel to output the data. For example, a value representing Stereo.
//      *
//      * @return The output channel.
//      */
//     int getOutputChannel();
// 
//     /**
//      * The input audio format of the buffer array (ex: float or short).
//      *
//      * @return The format of the buffer array.
//      */
//     int getInputFormat();
// 
//     /**
//      * The output audio format of the buffer array (ex: float or short).
//      *
//      * @return The format of the buffer array.
//      */
//     int getOutputFormat();
// 
//     /**
//      * The amount of bytes to represent a single piece of output data. For instance, 16-bit PCM data
//      * can be represented by two bytes.
//      *
//      * @return The amount of bytes needed to represent the output data.
//      */
//     int getOutputFormatByteCount();
// 
//     /**
//      * The input source of the audio data.
//      *
//      * @return The input source.
//      */
//     int getInputSource();
// }


import com.chrynan.android_guitar_tuner.tuner.config.AudioConfig;
package com.chrynan.android_guitar_tuner.tuner.detection;


/**
 * A {@link PitchDetector} implementation that uses a YIN algorithm to determine the frequency of
 * the provided waveform data. The YIN algorithm is similar to the Auto-correlation Function used
 * for pitch detection but adds additional steps to better the accuracy of the results. Each step
 * lowers the error rate further. The following implementation was inspired by
 * <a href="https://github.com/JorenSix/TarsosDSP/blob/master/src/core/be/tarsos/dsp/pitch/Yin.java">TarsosDsp</a>
 * and
 * <a href="http://recherche.ircam.fr/equipes/pcm/cheveign/ps/2002_JASA_YIN_proof.pdf">this YIN paper</a>.
 * The six steps in the YIN algorithm are (according to the YIN paper):
 * <p>
 * <ol>
 * <li>Auto-correlation Method</li>
 * <li>Difference Function</li>
 * <li>Cumulative Mean Normalized Difference Function</li>
 * <li>Absolute Threshold</li>
 * <li>Parabolic Interpolation</li>
 * <li>Best Local Estimate</li>
 * </ol>
 * </p>
 * The first two steps, the Auto-correlation Method and the Difference Function, can seemingly be
 * combined into a single difference function step according to the YIN paper.
 */
public class YINPitchDetector implements PitchDetector {
    // According to the YIN Paper, the threshold should be between 0.10 and 0.15
    private static final float ABSOLUTE_THRESHOLD = 0.125f;

    private final double sampleRate;
    private final float[] resultBuffer;

// Path: app/src/main/java/com/chrynan/android_guitar_tuner/tuner/config/AudioConfig.java
// public interface AudioConfig {
// 
//     /**
//      * The sample rate that the Audio Recorder is using, in hertz. Note that this value is device
//      * dependent.
//      *
//      * @return The sample rate of the audio recording.
//      */
//     int getSampleRate();
// 
//     /**
//      * The size of the underlying input waveform buffer array used by the Audio Recorder. This should be
//      * larger than the value returned by {@link #getReadSize()}. This value should only be used to
//      * create an instance of an Audio Recorder and when reading or performing operations on the
//      * waveform buffer array, {@link #getReadSize()} should be used. Note that an implementation of
//      * this method should return a valid value for the device.
//      *
//      * @return The size of the underlying waveform buffer array.
//      */
//     int getInputBufferSize();
// 
//     /**
//      * The size of the underlying output waveform buffer array used by the Audio Recorder. This should be
//      * larger than the value returned by {@link #getReadSize()}. This value should only be used to
//      * create an instance of an Audio Recorder and when reading or performing operations on the
//      * waveform buffer array, {@link #getReadSize()} should be used. Note that an implementation of
//      * this method should return a valid value for the device.
//      *
//      * @return The size of the underlying waveform buffer array.
//      */
//     int getOutputBufferSize();
// 
//     /**
//      * The size of the input array of data that should be read from the buffer. Use this value when
//      * processing the buffer of waveform data.
//      *
//      * @return The size of values read from the underlying waveform buffer array.
//      */
//     int getReadSize();
// 
//     /**
//      * The size of the array of data that should be written to the output buffer. Use this value when
//      * processing the buffer of waveform data.
//      *
//      * @return The size of values written to the underlying waveform buffer array.
//      */
//     int getWriteSize();
// 
//     /**
//      * The input channel to retrieve the input data.
//      *
//      * @return The input channel
//      */
//     int getInputChannel();
// 
//     /**
//      * The output channel to output the data. For example, a value representing Stereo.
//      *
//      * @return The output channel.
//      */
//     int getOutputChannel();
// 
//     /**
//      * The input audio format of the buffer array (ex: float or short).
//      *
//      * @return The format of the buffer array.
//      */
//     int getInputFormat();
// 
//     /**
//      * The output audio format of the buffer array (ex: float or short).
//      *
//      * @return The format of the buffer array.
//      */
//     int getOutputFormat();
// 
//     /**
//      * The amount of bytes to represent a single piece of output data. For instance, 16-bit PCM data
//      * can be represented by two bytes.
//      *
//      * @return The amount of bytes needed to represent the output data.
//      */
//     int getOutputFormatByteCount();
// 
//     /**
//      * The input source of the audio data.
//      *
//      * @return The input source.
//      */
//     int getInputSource();
// }


// Path: app/src/main/java/com/chrynan/android_guitar_tuner/tuner/detection/YINPitchDetector.java
import com.chrynan.android_guitar_tuner.tuner.config.AudioConfig;

package com.chrynan.android_guitar_tuner.tuner.detection;


/**
 * A {@link PitchDetector} implementation that uses a YIN algorithm to determine the frequency of
 * the provided waveform data. The YIN algorithm is similar to the Auto-correlation Function used
 * for pitch detection but adds additional steps to better the accuracy of the results. Each step
 * lowers the error rate further. The following implementation was inspired by
 * <a href="https://github.com/JorenSix/TarsosDSP/blob/master/src/core/be/tarsos/dsp/pitch/Yin.java">TarsosDsp</a>
 * and
 * <a href="http://recherche.ircam.fr/equipes/pcm/cheveign/ps/2002_JASA_YIN_proof.pdf">this YIN paper</a>.
 * The six steps in the YIN algorithm are (according to the YIN paper):
 * <p>
 * <ol>
 * <li>Auto-correlation Method</li>
 * <li>Difference Function</li>
 * <li>Cumulative Mean Normalized Difference Function</li>
 * <li>Absolute Threshold</li>
 * <li>Parabolic Interpolation</li>
 * <li>Best Local Estimate</li>
 * </ol>
 * </p>
 * The first two steps, the Auto-correlation Method and the Difference Function, can seemingly be
 * combined into a single difference function step according to the YIN paper.
 */
public class YINPitchDetector implements PitchDetector {
    // According to the YIN Paper, the threshold should be between 0.10 and 0.15
    private static final float ABSOLUTE_THRESHOLD = 0.125f;

    private final double sampleRate;
    private final float[] resultBuffer;

    public YINPitchDetector(final AudioConfig audioConfig) {