def _stable_pitches_to_notes(self, stable_pitches_hz, theoretical_intervals, tonic_hz): stable_pitches_cent = Converter.hz_to_cent(stable_pitches_hz, tonic_hz) # Finding nearest theoretical values of each stable pitch, identify the # name of this value and write to output stable_notes = {} # Defining output (return) object for stable_pitch_cent, stable_pitch_hz in zip(stable_pitches_cent, stable_pitches_hz): note_cent = TonicLastNote.find_nearest( theoretical_intervals.values(), stable_pitch_cent) if abs(stable_pitch_cent - note_cent) < self.pitch_threshold: for key, val in theoretical_intervals.iteritems(): if val == note_cent: theoretical_pitch = Converter.cent_to_hz( note_cent, tonic_hz) stable_notes[key] = { "performed_interval": {"value": stable_pitch_cent, "unit": "cent"}, "theoretical_interval": {"value": note_cent, "unit": "cent"}, "theoretical_pitch": {"value": theoretical_pitch, "unit": "cent"}, "stable_pitch": {"value": stable_pitch_hz, "unit": "Hz"}} break return stable_notes
def check_tonic_with_octave_correction(self, tonic, distribution): # shift the distribution to tonic distribution.bins -= Converter.hz_to_cent(tonic, distribution.ref_freq) distribution.ref_freq = tonic # get the stable pitches peaks = distribution.detect_peaks() peak_idx = peaks[0] stable_pitches = distribution.bins[peak_idx] # find all the frequencies in the tonic candidate's pitch class pitches_in_tonic_pitch_class = [ sp for sp in stable_pitches if min([sp % 1200, 1200 - (sp % 1200)]) < self.stable_pitch_dev] # sum all the pitch occurrences in the pitch distribution starting from # these pitches till their octave pitch_weights = [] for pp in pitches_in_tonic_pitch_class: vals_in_octave = distribution.vals[(pp <= distribution.bins) * (distribution.bins < pp + 1200)] pitch_weights.append(np.sum(vals_in_octave)) # the candidate which accumulates the highest weight is the tonic try: tonic_corr_cent = pitches_in_tonic_pitch_class[ pitch_weights.index(max(pitch_weights))] return Converter.cent_to_hz(tonic_corr_cent, tonic) except ValueError: return None # no stable pitch class found for the given frequency
def _slice_pitch(self, pp, ti, tt): p_sliced = [p for t, p in zip(tt, pp) if ti[1] > t >= ti[0]] p_cent = Converter.hz_to_cent(p_sliced, self._dummy_ref_freq, min_freq=20.0) # pop nan and inf p_cent = p_cent[~np.isnan(p_cent)] p_cent = p_cent[~np.isinf(p_cent)] # shouldn't exist, but anyways... return p_cent, p_sliced
def _get_tunings(newtonic, note_models): for nm in note_models.values(): interval = Converter.hz_to_cent(nm['stable_pitch']['Value'], newtonic['alignment']['Value']) nm['performed_interval'] = {'Value': interval, 'Unit': 'cent'} theo_pitch = Converter.cent_to_hz( nm['theoretical_interval']['Value'], newtonic['alignment']['Value']) nm['theoretical_pitch'] = {'Value': theo_pitch, 'Unit': 'Hz'}
def _parse_pitch_input(pitch_in, tonic_freq): """ Parses the pitch input from list, numpy array or file. If the input (or the file content) is a matrix, the method assumes the columns represent timestamps, pitch and "other columns". respectively. It only returns the second column in this case. :param pitch_in: pitch input, which is a list, numpy array or filename :param tonic_freq: the tonic frequency in Hz :return: parsed pitch track (numpy array) """ # parse the pitch track from txt file, list or numpy array try: p = np.loadtxt(pitch_in) except ValueError: logger.debug('pitch_in is not a filename') p = np.array(pitch_in) p = p[:, 1] if p.ndim > 1 else p # get the pitch stream # normalize wrt tonic return Converter.hz_to_cent(p, tonic_freq)