Source code for vitalDSP.advanced_computation.pitch_shift

"""
Advanced Computation Module for Physiological Signal Processing

This module provides comprehensive capabilities for physiological
signal processing including ECG, PPG, EEG, and other vital signs.

Author: vitalDSP Team
Date: 2025-01-27
Version: 1.0.0

Key Features:
- Object-oriented design with comprehensive classes
- Multiple processing methods and functions
- NumPy integration for numerical computations

Examples:
--------
Basic usage:
    >>> import numpy as np
    >>> from vitalDSP.advanced_computation.pitch_shift import PitchShift
    >>> signal = np.random.randn(1000)
    >>> processor = PitchShift(signal)
    >>> result = processor.process()
    >>> print(f'Processing result: {result}')
"""

import numpy as np


[docs] class PitchShift: """ A class for pitch shifting and pitch detection, primarily used in the analysis of vocal pitch. This is particularly useful in diagnosing speech disorders and other vocal characteristics. Methods ------- shift_pitch : method Shifts the pitch of the input signal by a specified number of semitones. detect_pitch : method Detects the pitch of the input signal using the autocorrelation method. Example Usage ------------- signal = np.sin(2 * np.pi * 440 * np.linspace(0, 1, 1000)) # A simple sine wave at 440 Hz (A4) pitch_shift = PitchShift(signal, sampling_rate=1000) shifted_signal = pitch_shift.shift_pitch(semitones=2) print("Shifted Signal:", shifted_signal) detected_pitch = pitch_shift.detect_pitch() print("Detected Pitch:", detected_pitch, "Hz") """ def __init__(self, signal, sampling_rate=1000): """ Initialize the PitchShift class with the input signal and its sampling rate. Parameters ---------- signal : numpy.ndarray The input signal (e.g., a vocal recording) to be processed. sampling_rate : int, optional The sampling rate of the input signal in Hz. Default is 1000 Hz. """ self.signal = signal self.sampling_rate = sampling_rate
[docs] def shift_pitch(self, semitones=1): """ Shift the pitch of the input signal by a given number of semitones. This method shifts the pitch of the signal without altering its duration, which is useful for modifying vocal pitch in a controlled manner. Parameters ---------- semitones : int, optional The number of semitones to shift the pitch. A positive value raises the pitch, while a negative value lowers it. Default is 1 semitone. Returns ------- shifted_signal : numpy.ndarray The pitch-shifted signal. Examples -------- >>> signal = np.sin(2 * np.pi * 440 * np.linspace(0, 1, 1000)) # 440 Hz signal >>> pitch_shift = PitchShift(signal, sampling_rate=1000) >>> shifted_signal = pitch_shift.shift_pitch(semitones=3) >>> print("Shifted Signal:", shifted_signal) """ factor = 2 ** (semitones / 12.0) indices = np.round(np.arange(0, len(self.signal), factor)) indices = indices[indices < len(self.signal)].astype(int) shifted_signal = self.signal[indices] return shifted_signal
[docs] def detect_pitch(self): """ Detect the pitch of the input signal using the autocorrelation method. The autocorrelation method is commonly used to estimate the fundamental frequency (pitch) of a signal. This method is particularly effective for monophonic signals like speech or musical notes. Returns ------- pitch : float The detected pitch of the signal in Hertz (Hz). Examples -------- >>> signal = np.sin(2 * np.pi * 440 * np.linspace(0, 1, 1000)) # 440 Hz signal >>> pitch_shift = PitchShift(signal, sampling_rate=1000) >>> detected_pitch = pitch_shift.detect_pitch() >>> print("Detected Pitch:", detected_pitch, "Hz") """ autocorr = np.correlate(self.signal, self.signal, mode="full") autocorr = autocorr[len(autocorr) // 2 :] # Handle silence (if the signal is flat) if np.all(autocorr == 0): return float("inf") d = np.diff(autocorr) # Find the first positive difference to ignore the zero-lag peak try: start = np.where(d > 0)[0][0] except IndexError: # In case no positive difference is found (e.g., for silence) return float("inf") # Find the first peak after the zero-lag peak peak = np.argmax(autocorr[start:]) + start pitch = self.sampling_rate / peak if peak > 0 else float("inf") return pitch