aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/com/jsyn/unitgen/PitchDetector.java
diff options
context:
space:
mode:
authorRubbaBoy <[email protected]>2020-07-06 02:33:28 -0400
committerPhil Burk <[email protected]>2020-10-30 11:19:34 -0700
commit46888fae6eb7b1dd386f7af7d101ead99ae61981 (patch)
tree8969bbfd68d2fb5c0d8b86da49ec2eca230a72ab /src/main/java/com/jsyn/unitgen/PitchDetector.java
parentc51e92e813dd481603de078f0778e1f75db2ab05 (diff)
Restructured project, added gradle, JUnit, logger, and more
Added Gradle (and removed ant), modernized testing via the JUnit framework, moved standalone examples from the tests directory to a separate module, removed sparsely used Java logger and replaced it with SLF4J. More work could be done, however this is a great start to greatly improving the health of the codebase.
Diffstat (limited to 'src/main/java/com/jsyn/unitgen/PitchDetector.java')
-rw-r--r--src/main/java/com/jsyn/unitgen/PitchDetector.java120
1 files changed, 120 insertions, 0 deletions
diff --git a/src/main/java/com/jsyn/unitgen/PitchDetector.java b/src/main/java/com/jsyn/unitgen/PitchDetector.java
new file mode 100644
index 0000000..ff44c93
--- /dev/null
+++ b/src/main/java/com/jsyn/unitgen/PitchDetector.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2012 Phil Burk, Mobileer Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.jsyn.unitgen;
+
+import com.jsyn.ports.UnitInputPort;
+import com.jsyn.ports.UnitOutputPort;
+import com.jsyn.util.AutoCorrelator;
+import com.jsyn.util.SignalCorrelator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Estimate the fundamental frequency of a monophonic signal. Analyzes an input signal and outputs
+ * an estimated period in frames and a frequency in Hertz. The frequency is frameRate/period. The
+ * confidence tells you how accurate the estimate is. When the confidence is low, you should ignore
+ * the period. You can use a CompareUnit and a LatchUnit to hold values that you are confident of.
+ * <P>
+ * Note that a stable monophonic signal is required for accurate pitch tracking.
+ *
+ * @author (C) 2012 Phil Burk, Mobileer Inc
+ */
+public class PitchDetector extends UnitGenerator {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(PitchDetector.class);
+
+ public UnitInputPort input;
+
+ public UnitOutputPort period;
+ public UnitOutputPort confidence;
+ public UnitOutputPort frequency;
+ public UnitOutputPort updated;
+
+ protected SignalCorrelator signalCorrelator;
+
+ private double lastFrequency = 440.0;
+ private double lastPeriod = 44100.0 / lastFrequency; // result of analysis TODO update for 48000
+ private double lastConfidence = 0.0; // Measure of confidence in the result.
+
+ private static final int LOWEST_FREQUENCY = 40;
+ private static final int HIGHEST_RATE = 48000;
+ private static final int CYCLES_NEEDED = 2;
+
+ public PitchDetector() {
+ super();
+ addPort(input = new UnitInputPort("Input"));
+
+ addPort(period = new UnitOutputPort("Period"));
+ addPort(confidence = new UnitOutputPort("Confidence"));
+ addPort(frequency = new UnitOutputPort("Frequency"));
+ addPort(updated = new UnitOutputPort("Updated"));
+ signalCorrelator = createSignalCorrelator();
+ }
+
+ public SignalCorrelator createSignalCorrelator() {
+ int framesNeeded = HIGHEST_RATE * CYCLES_NEEDED / LOWEST_FREQUENCY;
+ return new AutoCorrelator(framesNeeded);
+ }
+
+ @Override
+ public void generate(int start, int limit) {
+ double[] inputs = input.getValues();
+ double[] periods = period.getValues();
+ double[] confidences = confidence.getValues();
+ double[] frequencies = frequency.getValues();
+ double[] updateds = updated.getValues();
+
+ for (int i = start; i < limit; i++) {
+ double current = inputs[i];
+ if (signalCorrelator.addSample(current)) {
+ lastPeriod = signalCorrelator.getPeriod();
+ if (lastPeriod < 0.1) {
+ LOGGER.debug("ILLEGAL PERIOD");
+ }
+ double currentFrequency = getFrameRate() / (lastPeriod + 0);
+ double confidence = signalCorrelator.getConfidence();
+ if (confidence > 0.1) {
+ if (true) {
+ double coefficient = confidence * 0.2;
+ // Take weighted average with previous frequency.
+ lastFrequency = ((lastFrequency * (1.0 - coefficient)) + (currentFrequency * coefficient));
+ } else {
+ lastFrequency = ((lastFrequency * lastConfidence) + (currentFrequency * confidence))
+ / (lastConfidence + confidence);
+ }
+ }
+ lastConfidence = confidence;
+ updateds[i] = 1.0;
+ } else {
+ updateds[i] = 0.0;
+ }
+ periods[i] = lastPeriod;
+ confidences[i] = lastConfidence;
+ frequencies[i] = lastFrequency;
+ }
+ }
+
+ /**
+ * For debugging only.
+ *
+ * @return internal array of correlation results.
+ */
+ public float[] getDiffs() {
+ return signalCorrelator.getDiffs();
+ }
+
+}