Peaks Extension#

biotuner.peaks_extension — extend a peak set with harmonics / consonant fits.

Module type: Functions

EEG_harmonics_mult(peaks, n_harmonics, n_oct_up=0)[source]#

Computes the harmonics of a list of frequency peaks. Given a list of frequency peaks, this function computes the desired number of harmonics for each peak. The harmonics are calculated using the formula x, 2x, 3x …, nx, where x is the frequency of the peak and n_harmonics is the number of harmonics to compute.

Parameters:
  • peaks (list of float) – The frequency peaks, represented as local maxima in a spectrum.

  • n_harmonics (int) – The number of harmonics to compute for each peak.

  • n_oct_up (int, default=0) – The number of octaves by which to shift the peaks before computing the harmonics.

Returns:

multi_harmonics (numpy.ndarray) – An array of shape (n_peaks, n_harmonics+1), where n_peaks is the number of frequency peaks and each row represents the computed harmonics for the corresponding peak.

Examples

>>> peaks = [10.0, 20.0, 30.0]
>>> n_harmonics = 3
>>> n_oct_up = 1
>>> EEG_harmonics_mult(peaks, n_harmonics, n_oct_up)
array([[ 20.,  40.,  60.,  80.],
    [ 40.,  80., 120., 160.],
    [ 60., 120., 180., 240.]])
EEG_harmonics_div(peaks, n_harmonics, n_oct_up=0, mode='div')[source]#

Computes the sub-harmonics of a list of frequency peaks using division. Given a list of frequency peaks, this function computes the desired number of sub-harmonics for each peak using division. The sub-harmonics are calculated using the formulas x, x/2, x/3 …, x/n or x, (x+x/2), (x+x/3), … (x+x/n), depending on the specified mode, where x is the frequency of the peak and n_harmonics is the number of sub-harmonics to compute.

Parameters:
  • peaks (list of float) – The frequency peaks, represented as local maxima in a spectrum.

  • n_harmonics (int) – The number of sub-harmonics to compute for each peak.

  • n_oct_up (int, default=0) – The number of octaves by which to shift the peaks before computing the sub-harmonics.

  • mode (str, default=’div’) – The mode to use for computing the sub-harmonics. Possible values are ‘div’ for x, x/2, x/3 …, x/n and ‘div_add’ for x, (x+x/2), (x+x/3), … (x+x/n).

Returns:

  • div_harmonics (numpy.ndarray) – An array of shape (n_peaks, n_harmonics+1), where n_peaks is the number of frequency peaks and each row represents the computed sub-harmonics for the corresponding peak, in Hz.

  • div_harmonics_bounded (numpy.ndarray) – An array of shape (n_peaks, n_harmonics+1), where n_peaks is the number of frequency peaks and each row represents the computed sub-harmonics for the corresponding peak, bounded between unison (1) and octave (2), in Hz.

Examples

>>> peaks = [10.0, 20.0, 30.0]
>>> n_harmonics = 3
>>> n_oct_up = 1
>>> mode = 'div'
>>> EEG_harmonics_div(peaks, n_harmonics, n_oct_up, mode)
(array([[10.        ,  5.        ,  3.33333333,  2.5       ],
        [20.        , 10.        ,  6.66666667,  5.        ],
        [30.        , 15.        , 10.        ,  7.5       ]]),
array([[1.        , 1.        , 1.        , 1.        ],
        [2.        , 2.        , 1.5       , 1.25      ],
        [2.        , 2.        , 1.5       , 1.25      ]]))
harmonic_fit(peaks, n_harm=10, bounds=1, function='mult', div_mode='div', n_common_harms=5)[source]#

Compute harmonics of a list of peaks and compare the lists of harmonics pairwise to find fitting between the harmonic series.

Parameters:
  • peaks (list of float) – Spectral peaks representing local maximum in a spectrum

  • n_harm (int, default=10) – Number of harmonics to compute.

  • bounds (int, default=1) – Minimum distance (in Hz) between two frequencies to consider a fit.

  • function (str, default=’mult’) – Type of harmonic function to use. Possible values are:

    • ‘mult’ will use natural harmonics.

    • ‘div’ will use natural sub-harmonics.

    • ‘exp’ will use exponentials.

  • div_mode (str, default=’div’) – Mode of the natural sub-harmonic function when function=’div’. See EEG_harmonics_div function.

  • n_common_harms (int, default=5) – Minimum number of times a harmonic position must appear across different peak pairs to be included in most_common_harmonics output. Acts as a threshold filter (not a limit on number of results).

Returns:

  • harm_fit (list) – Frequencies of the harmonics that match.

  • harmonics_pos (list) – Positions of the harmonics that match.

  • most_common_harmonics (list) – Harmonic positions that appear at least n_common_harms times across peak pairs, sorted by frequency of occurrence (most common first).

  • matching_positions (list of lists) – Each sublist corresponds to an harmonic fit, the first number is the frequency and the two others are harmonic positions.

Examples

>>> from biotuner.peaks_extension import harmonic_fit
>>> peaks = [3, 9, 12]
>>> harm_fit, harmonics_pos, _, _ = harmonic_fit(peaks, n_harm=5, bounds=0.1, function="mult")
>>> print(harm_fit)
>>> harm_fit, harmonics_pos, _, _ = harmonic_fit(peaks, n_harm=10, bounds=0.1, function="div")
>>> print(harm_fit)
[9.0, 18.0, 12.0, 36.0]
[0.784, 1.5, 1.045, 3.0, 1.0, 1.757, 1.31, 1.243, 1.162, 1.108]
intermodulation_spectrum(peaks, amplitudes=None, max_order=2, sum_diff=('sum', 'diff'), drop_negative=True, min_freq=0.0, drop_originals=False, dedupe=True, dedupe_tol=1e-06)[source]#

Compute intermodulation distortion (IMD) products of a peak set.

For each ordered pair (i, j) of distinct peaks and integer coefficients (m, n) with m + n = order, m >= 1, n >= 1, generate the IMD products:

m * f_i + n * f_j   (sum-type product)
m * f_i - n * f_j   (difference-type product)

with amplitudes (a_i ** m) * (a_j ** n) (the standard power-series nonlinearity model: a memoryless polynomial of order order produces order-order IMD products with amplitudes proportional to the product of input amplitudes raised to the matching coefficients).

These products are the Tartini (combination) tones perceived in a chord: the brain’s auditory system generates them via cochlear nonlinearity, and many chord-resolution effects are explained by where the IMD products fall on the consonance lattice. They are also the spectral primitive for n-limit tuning theory: the n-limit of a chord is, equivalently, the largest prime in any of its IMD products.

Parameters:
  • peaks (sequence of float) – Input peak frequencies in Hz.

  • amplitudes (sequence of float, optional) – Per-peak amplitudes. Defaults to uniform 1 / N.

  • max_order (int, default=2) – Maximum IMD order m + n to compute. 2 gives the textbook f_i ± f_j set; 3 adds 2 f_i ± f_j and f_i ± 2 f_j; higher orders proliferate quickly.

  • sum_diff (tuple of {‘sum’, ‘diff’}, default=(‘sum’, ‘diff’)) – Which IMD families to include.

  • drop_negative (bool, default=True) – Discard products with f < 0 (sign-flipped difference terms).

  • min_freq (float, default=0.0) – Discard products below this frequency (Hz).

  • drop_originals (bool, default=False) – Exclude the original input peaks from the output. False (the default) prepends the originals so the output is originals + IMD products.

  • dedupe (bool, default=True) – If True, products that land on the same frequency (within dedupe_tol) are merged: their amplitudes are summed.

  • dedupe_tol (float, default=1e-6) – Frequency tolerance for the dedupe pass.

Returns:

  • peaks_out (numpy.ndarray) – Output peak frequencies sorted ascending.

  • amps_out (numpy.ndarray) – Corresponding amplitudes.

  • sources (list) – Records of how each output peak was generated. Each record is a list of (m, n, i, j, sign) tuples (one tuple per contribution; multiple after dedupe). Originals (when included) carry the sentinel record (0, 0, i, i, '0').

Examples

>>> import numpy as np
>>> peaks, amps, _ = intermodulation_spectrum(
...     [100.0, 150.0], amplitudes=[1.0, 1.0], max_order=2,
... )
>>> sorted(peaks.tolist())
[50.0, 100.0, 150.0, 250.0]

Notes

Used by biotuner.harmonic_geometry to enrich a chord with its IMD-derived peaks before rendering. Composition pattern:

from biotuner.peaks_extension import intermodulation_spectrum
from biotuner.harmonic_geometry import (
    HarmonicInput, quasicrystal_field_2d,
)
chord = HarmonicInput(peaks=[100.0, 125.0, 150.0])
imd_peaks, imd_amps, _ = intermodulation_spectrum(
    chord.peaks, chord.amplitudes,
)
rich = HarmonicInput(peaks=imd_peaks, amplitudes=imd_amps)
field = quasicrystal_field_2d(rich)
multi_consonance(cons_pairs, n_freqs=5)[source]#

Function that keeps the frequencies that are the most consonant with others Takes pairs of frequencies that are consonant as input (output of the ‘compute consonance’ function).

Parameters:
  • cons_pairs (List of lists (float)) – list of lists of each pairs of consonant peaks

  • n_freqs (int) – maximum number of consonant freqs to keep

Returns:

freqs_related (List (float)) – peaks that are consonant with at least two other peaks, starting with the peak that is consonant with the maximum number of other peaks