示例#1
0
 public void CopyValues(CamdPitchCandidate other)
 {
     halftone                = other.halftone;
     error                   = other.error;
     normalizedError         = other.normalizedError;
     normalizedErrorWithBias = other.normalizedErrorWithBias;
 }
示例#2
0
 public CamdAudioSamplesAnalyzer(int sampleRateHz, int maxSampleLength)
 {
     this.maxSampleLength = maxSampleLength;
     float[] halftoneFrequencies = MidiUtils.PrecalculateHalftoneFrequencies(MinNote, NumHalftones);
     halftoneDelays = MidiUtils.PrecalculateHalftoneDelays(sampleRateHz, halftoneFrequencies);
     for (int i = 0; i < currentCandidates.Length; i++)
     {
         currentCandidates[i] = new CamdPitchCandidate();
     }
 }
示例#3
0
    private void FindBestCandidate(CamdPitchCandidate[] currentCandidates, CircularBuffer <CamdPitchCandidate> bestCandidateHistory)
    {
        bestCurrentCandidate = null;
        currentCandidates.ForEach((currentCandidate) =>
        {
            // Bias towards recently detected pitches.
            // For candidates with similar normalizedError,
            // this will prefer a candidate that has been detected before, which reduces the "jitter" of the pitch detection.
            // However, if a new pitch has been clearly identified with significantly less normalizedError,
            // then an old detected pitch from the history will not be used instead because its normalizedError will still be less - even without bias.
            currentCandidate.normalizedErrorWithBias = currentCandidate.normalizedError;
            if (HalftoneContinuationBias >= 0 && HalftoneContinuationBias <= 1)
            {
                for (int historyIndex = 0; historyIndex < bestCandidateHistory.Size; historyIndex++)
                {
                    if (bestCandidateHistory[historyIndex].halftone == currentCandidate.halftone)
                    {
                        // The further away in the history the halftone was before, the less the bias will impact the current candidate.
                        float biasConsideringHistoryDistance     = HalftoneContinuationBias - (HalftoneContinuationBias * (historyIndex / bestCandidateHistory.Size));
                        currentCandidate.normalizedErrorWithBias = currentCandidate.normalizedError * (1 - biasConsideringHistoryDistance);
                        // Do not apply bias multiple times.
                        break;
                    }
                }
            }

            if (currentCandidate.halftone >= 0 &&
                (bestCurrentCandidate == null ||
                 bestCurrentCandidate.halftone < 0 ||
                 currentCandidate.normalizedErrorWithBias < bestCurrentCandidate.normalizedErrorWithBias))
            {
                bestCurrentCandidate = currentCandidate;
            }
        });

        CamdPitchCandidate bestHistoryCandidate = null;

        for (int i = 0; i < bestCandidateHistory.Size; i++)
        {
            // Do not consider the biased error when comparing best candidate from history.
            CamdPitchCandidate historyCandidate = bestCandidateHistory[i];
            if (historyCandidate.halftone >= 0 &&
                (bestHistoryCandidate == null ||
                 bestHistoryCandidate.halftone < 0 ||
                 historyCandidate.normalizedError < bestHistoryCandidate.normalizedError))
            {
                bestHistoryCandidate = historyCandidate;
            }
        }

        // Do not consider the biased error when comparing best candidate from history.
        bestCandidate = (bestHistoryCandidate == null || bestCurrentCandidate.normalizedErrorWithBias < bestHistoryCandidate.normalizedError)
            ? bestCurrentCandidate
            : bestHistoryCandidate;
    }
示例#4
0
 public CamdPitchCandidate(CamdPitchCandidate other)
 {
     CopyValues(other);
 }