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>')
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
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)
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
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
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
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')
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)
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