guitarsounds.utils

  1import struct
  2import wave
  3import numpy as np
  4import scipy.optimize
  5import scipy.integrate
  6from scipy.interpolate import interp1d
  7from scipy.optimize import least_squares
  8import guitarsounds
  9import ipywidgets as widgets
 10from guitarsounds.parameters import sound_parameters
 11
 12# instantiation of the trim time interpolator
 13sp = sound_parameters()
 14
 15freq_dict = {'E2': 82.41,
 16             'A2': 110.0,
 17             'D3': 146.83,
 18             'G3': 196.0,
 19             'B3': 246.94,
 20             'E4': 329.63}
 21
 22trim_dict = {'E2': sp.trim.E2.value,
 23             'A2': sp.trim.A2.value,
 24             'D3': sp.trim.D3.value,
 25             'G3': sp.trim.G3.value,
 26             'B3': sp.trim.B3.value,
 27             'E4': sp.trim.E4.value}
 28
 29freq2trim = interp1d(list(freq_dict.values()),
 30                     list(trim_dict.values()),
 31                     fill_value='extrapolate')
 32
 33
 34def nth_order_polynomial_residual(A, n, x, y):
 35    """
 36    Function computing the residual from a nth order
 37    polynomial, with x and y vectors
 38    :return: the evaluated residual between a polynomial and the polynomial
 39    with coefficients A
 40    """
 41    y_model = 0
 42    for i in np.arange(n):
 43        y_model += A[i] * np.array(x) ** i
 44    return y_model - y
 45
 46
 47def nth_order_polynomial_fit(n, x, y):
 48    """
 49    Function creating a function of a fitted nth order
 50    polynomial to a set of x and y data
 51    :param n: polynomial order
 52    :param x: x values
 53    :param y: y values
 54    :return: the Function such as y = F(x)
 55    """
 56    n += 1
 57    while n > len(x):
 58        n -= 1
 59    guess = np.ones(n)
 60    result = least_squares(nth_order_polynomial_residual,
 61                                          guess,
 62                                          args=(n, x, y))
 63    A = result.x
 64
 65    def polynomial_function(x_value):
 66        y_value = 0
 67        for i, a in enumerate(A):
 68            y_value += a * np.array(x_value) ** i
 69        return y_value
 70
 71    return polynomial_function
 72
 73
 74def octave_values(fraction, min_freq=10, max_freq=20200):
 75    """
 76    Compute octave fraction bin center values from min_freq to max_freq.
 77    `soundcomp.octave_values(3)` returns the bins corresponding to 1/3 octave
 78    values
 79    from 10 Hz to 20200 Hz.
 80    :param fraction: octave fraction value (1/3, 1, 1/2, ...)
 81    :param min_freq: minimum frequency in the computed octave fractions
 82    :param max_freq: maximum frequency considered in the octave fractions
 83    :return: the value in frequencies corresponding to the octave bins
 84    """
 85    # Compute the octave bins
 86    f = 1000.  # Reference Frequency
 87    f_up = f
 88    f_down = f
 89    multiple = 2 ** (1 / fraction)
 90    octave_bins = [f]
 91    while f_up < max_freq or f_down > min_freq:
 92        f_up = f_up * multiple
 93        f_down = f_down / multiple
 94        if f_down > min_freq:
 95            octave_bins.insert(0, f_down)
 96        if f_up < max_freq:
 97            octave_bins.append(f_up)
 98    return np.array(octave_bins)
 99
100
101def octave_histogram(fraction, **kwargs):
102    """
103    Compute the octave histogram bins limits corresponding an octave fraction.
104    min_freq and max_freq can be provided in the **kwargs.
105    :param fraction: octave fraction used to compute the
106    octave histogram (1/2, 1/3, ...)
107    :**kwargs: key word arguments to pass to the octave_values function
108    :return: amplitude of the octave bin histogram
109    """
110    # get the octave bins
111    octave_bins = octave_values(fraction, **kwargs)
112
113    # Compute the histogram bins
114    hist_bins = []
115    for f_o in octave_bins:
116        # Intersecting lower bounds
117        hist_bins.append(f_o / 2 ** (1 / (2 * fraction)))
118    # Last upper bound
119    f_o_end = octave_bins[-1]
120    hist_bins.append(f_o_end * 2 ** (1 / (2 * fraction)))
121    return np.array(hist_bins)
122
123
124def trim_sounds(*sounds, length=None):
125    """
126    Trim sounds to have the same length
127    :param sounds: guitarsounds.Sound instances
128    :param length: trim length in seconds, if None the sounds a trimmed to the
129    length of the shortest one
130    :return: the trimmed sounds
131    """
132    if length is None:
133        pack = guitarsounds.SoundPack(*sounds)
134        return pack.sounds
135
136    new_sounds = []
137    for sound in sounds:
138        if length < sound.signal.time()[-1]:
139            sound.signal = sound.signal.trim_time(length)
140        else:
141            raise ValueError("Specify a shorter length")
142        sound.bin_divide()
143        new_sounds.append(sound)
144    return new_sounds
145
146
147def load_wav(filename):
148    """
149    load a wave file and return the signal data with the sample rate
150    :param filename: string, name of the file to load
151    :return: signal_data, sample_rate
152
153    Example :
154    y, sr = load_wav('sound.wav')
155    """
156    audio = wave.open(filename)
157    sample_rate = audio.getframerate()
158    samples = []
159    for _ in range(audio.getnframes()):
160        frame = audio.readframes(1)
161        samples.append(struct.unpack("h", frame)[0])
162    signal = np.array(samples) / 32768
163    return signal, sample_rate
164
165
166def resample(y, sr_orig, sr_target=22050):
167    """
168    resample a signal using scipy.signal
169    :param y: signal data to be resampled
170    :param sr_orig: original sample rate
171    :param sr_target: target sample rate
172
173    Sample rate = n.o. samples in a second
174
175    Example :
176    signal, sr = load_wav('sound.wav')
177    # resample to sr=22050
178    signal = resample(signal, sr, 22050)
179    """
180    y_len = int(sr_target * len(y) / sr_orig)
181    y_new = scipy.signal.resample(y, num=y_len)
182    return y_new
183
184
185def generate_error_widget(text):
186    """
187    Creates a ipywidget error message from a string
188    :param text: python str of the error message
189    :return: widgets.HTLM object corresponding to the error message
190    """
191    return widgets.HTML('<p style="color:#CC4123;">' + text + '</p>')
def nth_order_polynomial_residual(A, n, x, y):
35def nth_order_polynomial_residual(A, n, x, y):
36    """
37    Function computing the residual from a nth order
38    polynomial, with x and y vectors
39    :return: the evaluated residual between a polynomial and the polynomial
40    with coefficients A
41    """
42    y_model = 0
43    for i in np.arange(n):
44        y_model += A[i] * np.array(x) ** i
45    return y_model - y

Function computing the residual from a nth order polynomial, with x and y vectors

Returns

the evaluated residual between a polynomial and the polynomial with coefficients A

def nth_order_polynomial_fit(n, x, y):
48def nth_order_polynomial_fit(n, x, y):
49    """
50    Function creating a function of a fitted nth order
51    polynomial to a set of x and y data
52    :param n: polynomial order
53    :param x: x values
54    :param y: y values
55    :return: the Function such as y = F(x)
56    """
57    n += 1
58    while n > len(x):
59        n -= 1
60    guess = np.ones(n)
61    result = least_squares(nth_order_polynomial_residual,
62                                          guess,
63                                          args=(n, x, y))
64    A = result.x
65
66    def polynomial_function(x_value):
67        y_value = 0
68        for i, a in enumerate(A):
69            y_value += a * np.array(x_value) ** i
70        return y_value
71
72    return polynomial_function

Function creating a function of a fitted nth order polynomial to a set of x and y data

Parameters
  • n: polynomial order
  • x: x values
  • y: y values
Returns

the Function such as y = F(x)

def octave_values(fraction, min_freq=10, max_freq=20200):
75def octave_values(fraction, min_freq=10, max_freq=20200):
76    """
77    Compute octave fraction bin center values from min_freq to max_freq.
78    `soundcomp.octave_values(3)` returns the bins corresponding to 1/3 octave
79    values
80    from 10 Hz to 20200 Hz.
81    :param fraction: octave fraction value (1/3, 1, 1/2, ...)
82    :param min_freq: minimum frequency in the computed octave fractions
83    :param max_freq: maximum frequency considered in the octave fractions
84    :return: the value in frequencies corresponding to the octave bins
85    """
86    # Compute the octave bins
87    f = 1000.  # Reference Frequency
88    f_up = f
89    f_down = f
90    multiple = 2 ** (1 / fraction)
91    octave_bins = [f]
92    while f_up < max_freq or f_down > min_freq:
93        f_up = f_up * multiple
94        f_down = f_down / multiple
95        if f_down > min_freq:
96            octave_bins.insert(0, f_down)
97        if f_up < max_freq:
98            octave_bins.append(f_up)
99    return np.array(octave_bins)

Compute octave fraction bin center values from min_freq to max_freq. soundcomp.octave_values(3) returns the bins corresponding to 1/3 octave values from 10 Hz to 20200 Hz.

Parameters
  • fraction: octave fraction value (1/3, 1, 1/2, ...)
  • min_freq: minimum frequency in the computed octave fractions
  • max_freq: maximum frequency considered in the octave fractions
Returns

the value in frequencies corresponding to the octave bins

def octave_histogram(fraction, **kwargs):
102def octave_histogram(fraction, **kwargs):
103    """
104    Compute the octave histogram bins limits corresponding an octave fraction.
105    min_freq and max_freq can be provided in the **kwargs.
106    :param fraction: octave fraction used to compute the
107    octave histogram (1/2, 1/3, ...)
108    :**kwargs: key word arguments to pass to the octave_values function
109    :return: amplitude of the octave bin histogram
110    """
111    # get the octave bins
112    octave_bins = octave_values(fraction, **kwargs)
113
114    # Compute the histogram bins
115    hist_bins = []
116    for f_o in octave_bins:
117        # Intersecting lower bounds
118        hist_bins.append(f_o / 2 ** (1 / (2 * fraction)))
119    # Last upper bound
120    f_o_end = octave_bins[-1]
121    hist_bins.append(f_o_end * 2 ** (1 / (2 * fraction)))
122    return np.array(hist_bins)

Compute the octave histogram bins limits corresponding an octave fraction. min_freq and max_freq can be provided in the **kwargs.

Parameters
  • fraction: octave fraction used to compute the octave histogram (1/2, 1/3, ...) :**kwargs: key word arguments to pass to the octave_values function
Returns

amplitude of the octave bin histogram

def trim_sounds(*sounds, length=None):
125def trim_sounds(*sounds, length=None):
126    """
127    Trim sounds to have the same length
128    :param sounds: guitarsounds.Sound instances
129    :param length: trim length in seconds, if None the sounds a trimmed to the
130    length of the shortest one
131    :return: the trimmed sounds
132    """
133    if length is None:
134        pack = guitarsounds.SoundPack(*sounds)
135        return pack.sounds
136
137    new_sounds = []
138    for sound in sounds:
139        if length < sound.signal.time()[-1]:
140            sound.signal = sound.signal.trim_time(length)
141        else:
142            raise ValueError("Specify a shorter length")
143        sound.bin_divide()
144        new_sounds.append(sound)
145    return new_sounds

Trim sounds to have the same length

Parameters
  • sounds: guitarsounds.Sound instances
  • length: trim length in seconds, if None the sounds a trimmed to the length of the shortest one
Returns

the trimmed sounds

def load_wav(filename):
148def load_wav(filename):
149    """
150    load a wave file and return the signal data with the sample rate
151    :param filename: string, name of the file to load
152    :return: signal_data, sample_rate
153
154    Example :
155    y, sr = load_wav('sound.wav')
156    """
157    audio = wave.open(filename)
158    sample_rate = audio.getframerate()
159    samples = []
160    for _ in range(audio.getnframes()):
161        frame = audio.readframes(1)
162        samples.append(struct.unpack("h", frame)[0])
163    signal = np.array(samples) / 32768
164    return signal, sample_rate

load a wave file and return the signal data with the sample rate

Parameters
  • filename: string, name of the file to load
Returns

signal_data, sample_rate

Example : y, sr = load_wav('sound.wav')

def resample(y, sr_orig, sr_target=22050):
167def resample(y, sr_orig, sr_target=22050):
168    """
169    resample a signal using scipy.signal
170    :param y: signal data to be resampled
171    :param sr_orig: original sample rate
172    :param sr_target: target sample rate
173
174    Sample rate = n.o. samples in a second
175
176    Example :
177    signal, sr = load_wav('sound.wav')
178    # resample to sr=22050
179    signal = resample(signal, sr, 22050)
180    """
181    y_len = int(sr_target * len(y) / sr_orig)
182    y_new = scipy.signal.resample(y, num=y_len)
183    return y_new

resample a signal using scipy.signal

Parameters
  • y: signal data to be resampled
  • sr_orig: original sample rate
  • sr_target: target sample rate

Sample rate = n.o. samples in a second

Example : signal, sr = load_wav('sound.wav')

resample to sr=22050

signal = resample(signal, sr, 22050)

def generate_error_widget(text):
186def generate_error_widget(text):
187    """
188    Creates a ipywidget error message from a string
189    :param text: python str of the error message
190    :return: widgets.HTLM object corresponding to the error message
191    """
192    return widgets.HTML('<p style="color:#CC4123;">' + text + '</p>')

Creates a ipywidget error message from a string

Parameters
  • text: python str of the error message
Returns

widgets.HTLM object corresponding to the error message