Skip to content

Commit

Permalink
Add settings for the pitch tracker
Browse files Browse the repository at this point in the history
  • Loading branch information
celeste-sinead authored and tlecomte committed May 6, 2024
1 parent 3b87258 commit c5ac102
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 14 deletions.
36 changes: 22 additions & 14 deletions friture/pitch_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@
from friture.pitch_tracker_settings import (
DEFAULT_MIN_FREQ,
DEFAULT_MAX_FREQ,
DEFAULT_DURATION
DEFAULT_DURATION,
DEFAULT_FFT_SIZE,
DEFAULT_MIN_SNR,
PitchTrackerSettingsDialog
)
from friture.plotting.coordinateTransform import CoordinateTransform
import friture.plotting.frequency_scales as fscales
Expand Down Expand Up @@ -111,9 +114,10 @@ def __init__(self, parent, engine):
self.duration = DEFAULT_DURATION
self._pitch_tracker_data.horizontal_axis.setRange(-self.duration, 0.)

self.settings_dialog = PitchTrackerSettingsDialog(self)

self.audiobuffer = None
self.tracker = PitchTracker(RingBuffer())

self.update_curve()


Expand Down Expand Up @@ -143,45 +147,49 @@ def canvasUpdate(self):
# nothing to do here
return

def setmin(self, value):
def set_min_freq(self, value):
self.min_freq = value
self._pitch_tracker_data.vertical_axis.setRange(self.min_freq, self.max_freq)
self.vertical_transform.setRange(self.min_freq, self.max_freq)

def setmax(self, value):
def set_max_freq(self, value):
self.max_freq = value
self._pitch_tracker_data.vertical_axis.setRange(self.min_freq, self.max_freq)
self.vertical_transform.setRange(self.min_freq, self.max_freq)

def setduration(self, value):
def set_duration(self, value):
self.duration = value
self._pitch_tracker_data.horizontal_axis.setRange(-self.duration, 0.)

def set_min_snr(self, value: float):
self.tracker.min_snr = value

# slot
def settings_called(self, checked):
# self.settings_dialog.show()
pass
self.settings_dialog.show()

# method
def saveState(self, settings):
# self.settings_dialog.saveState(settings)
pass
self.settings_dialog.save_state(settings)

# method
def restoreState(self, settings):
# self.settings_dialog.restoreState(settings)
pass
self.settings_dialog.restore_state(settings)


class PitchTracker:
def __init__(
self,
input_buf: RingBuffer,
fft_size: int = 8192,
fft_size: int = DEFAULT_FFT_SIZE,
overlap: float = 0.75,
sample_rate: int = SAMPLING_RATE
sample_rate: int = SAMPLING_RATE,
min_snr: float = DEFAULT_MIN_SNR,
):
self.fft_size = fft_size
self.overlap = overlap
self.sample_rate = sample_rate
self.min_snr = min_snr

self.input_buf = input_buf
self.input_buf.grow_if_needed(fft_size)
Expand Down Expand Up @@ -240,7 +248,7 @@ def estimate_pitch(self, frame: np.ndarray) -> Optional[float]:
# a false detection and return no result.
variance = np.mean(spectrum ** 2)
snr = 10 * np.log10((spectrum[pitch_idx] ** 2) / variance)
if snr < 3:
if snr < self.min_snr:
return None
else:
return self.proc.freq[pitch_idx]
70 changes: 70 additions & 0 deletions friture/pitch_tracker_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,77 @@
# You should have received a copy of the GNU General Public License
# along with Friture. If not, see <http://www.gnu.org/licenses/>.

from PyQt5 import QtWidgets

from friture.audiobackend import SAMPLING_RATE

# Roughly the maximum singing range:
DEFAULT_MIN_FREQ = 80
DEFAULT_MAX_FREQ = 1000
DEFAULT_DURATION = 30
DEFAULT_MIN_SNR = 3.0
DEFAULT_FFT_SIZE = 16384

class PitchTrackerSettingsDialog(QtWidgets.QDialog):
def __init__(self, parent):
super().__init__(parent)
self.setWindowTitle("Pitch Tracker Settings")
self.form_layout = QtWidgets.QFormLayout(self)

self.min_freq = QtWidgets.QSpinBox(self)
self.min_freq.setMinimum(10)
self.min_freq.setMaximum(SAMPLING_RATE // 2)
self.min_freq.setSingleStep(10)
self.min_freq.setValue(DEFAULT_MIN_FREQ)
self.min_freq.setSuffix(" Hz")
self.min_freq.setObjectName("min_freq")
self.min_freq.valueChanged.connect(self.parent().set_min_freq)
self.form_layout.addRow("Min:", self.min_freq)

self.max_freq = QtWidgets.QSpinBox(self)
self.max_freq.setMinimum(10)
self.max_freq.setMaximum(SAMPLING_RATE // 2)
self.max_freq.setSingleStep(10)
self.max_freq.setValue(DEFAULT_MAX_FREQ)
self.max_freq.setSuffix(" Hz")
self.max_freq.setObjectName("max_freq")
self.max_freq.valueChanged.connect(self.parent().set_max_freq)
self.form_layout.addRow("Max:", self.max_freq)

self.duration = QtWidgets.QSpinBox(self)
self.duration.setMinimum(5)
self.duration.setMaximum(600)
self.duration.setSingleStep(1)
self.duration.setValue(DEFAULT_DURATION)
self.duration.setSuffix(" s")
self.duration.setObjectName("duration")
self.duration.valueChanged.connect(self.parent().set_duration)
self.form_layout.addRow("Duration:", self.duration)

self.min_snr = QtWidgets.QDoubleSpinBox(self)
self.min_snr.setMinimum(0)
self.min_snr.setMaximum(50)
self.min_snr.setSingleStep(1)
self.min_snr.setValue(DEFAULT_MIN_SNR)
self.min_snr.setSuffix(" dB")
self.min_snr.setObjectName("min_snr")
self.min_snr.valueChanged.connect(self.parent().set_min_snr)
self.form_layout.addRow("Min SNR:", self.min_snr)

self.setLayout(self.form_layout)

def save_state(self, settings):
settings.setValue("min_freq", self.min_freq.value())
settings.setValue("max_freq", self.max_freq.value())
settings.setValue("duration", self.duration.value())
settings.setValue("min_snr", self.min_snr.value())

def restore_state(self, settings):
self.min_freq.setValue(
settings.value("min_freq", DEFAULT_MIN_FREQ, type=int))
self.max_freq.setValue(
settings.value("max_freq", DEFAULT_MAX_FREQ, type=int))
self.duration.setValue(
settings.value("duration", DEFAULT_DURATION, type=int))
self.min_snr.setValue(
settings.value("min_snr", DEFAULT_MIN_SNR, type=float))

0 comments on commit c5ac102

Please sign in to comment.