import copy import numpy as np import scipy.io.wavfile import scipy.signal from . import utils as ut import pdb def load_sound(wav_fname): rate, samples = scipy.io.wavfile.read(wav_fname) times = (1./rate) * np.arange(len(samples)) return Sound(times, rate, samples) class Sound: def __init__(self, times, rate, samples=None): # Allow Sound(samples, sr) if samples is None: samples = times times = None if samples.dtype == np.float32: samples = samples.astype('float64') self.rate = rate # self.samples = ut.atleast_2d_col(samples) self.samples = samples self.length = samples.shape[0] if times is None: self.times = np.arange(len(self.samples)) / float(self.rate) else: self.times = times def copy(self): return copy.deepcopy(self) def parts(self): return (self.times, self.rate, self.samples) def __getslice__(self, *args): return Sound(self.times.__getslice__(*args), self.rate, self.samples.__getslice__(*args)) def duration(self): return self.samples.shape[0] / float(self.rate) def normalized(self, check=True): if self.samples.dtype == np.double: assert (not check) or np.max(np.abs(self.samples)) <= 4. x = copy.deepcopy(self) x.samples = np.clip(x.samples, -1., 1.) return x else: s = copy.deepcopy(self) s.samples = np.array(s.samples, 'double') / np.iinfo(s.samples.dtype).max s.samples[s.samples < -1] = -1 s.samples[s.samples > 1] = 1 return s def unnormalized(self, dtype_name='int32'): s = self.normalized() inf = np.iinfo(np.dtype(dtype_name)) samples = np.clip(s.samples, -1., 1.) samples = inf.max * samples samples = np.array(np.clip(samples, inf.min, inf.max), dtype_name) s.samples = samples return s def sample_from_time(self, t, bound=False): if bound: return min(max(0, int(np.round(t * self.rate))), self.samples.shape[0]-1) else: return int(np.round(t * self.rate)) # st = sample_from_time def shift_zero(self): s = copy.deepcopy(self) s.times -= s.times[0] return s def select_channel(self, c): s = copy.deepcopy(self) s.samples = s.samples[:, c] return s def left_pad_silence(self, n): if n == 0: return self.shift_zero() else: if np.ndim(self.samples) == 1: samples = np.concatenate([[0] * n, self.samples]) else: samples = np.vstack( [np.zeros((n, self.samples.shape[1]), self.samples.dtype), self.samples]) return Sound(None, self.rate, samples) def right_pad_silence(self, n): if n == 0: return self.shift_zero() else: if np.ndim(self.samples) == 1: samples = np.concatenate([self.samples, [0] * n]) else: samples = np.vstack([self.samples, np.zeros( (n, self.samples.shape[1]), self.samples.dtype)]) return Sound(None, self.rate, samples) def pad_slice(self, s1, s2): assert s1 < self.samples.shape[0] and s2 >= 0 s = self[max(0, s1): min(s2, self.samples.shape[0])] s = s.left_pad_silence(max(0, -s1)) s = s.right_pad_silence(max(0, s2 - self.samples.shape[0])) return s def to_mono(self, force_copy= True): s = copy.deepcopy(self) s.samples = make_mono(s.samples) return s def slice_time(self, t1, t2): return self[self.st(t1): self.st(t2)] @property def nchannels(self): return 1 if np.ndim(self.samples) == 1 else self.samples.shape[1] def save(self, fname): s = self.unnormalized('int16') scipy.io.wavfile.write(fname, s.rate, s.samples.transpose()) def resampled(self, new_rate, clip= True): if new_rate == self.rate: return copy.deepcopy(self) else: #assert self.samples.shape[1] == 1 return Sound(None, new_rate, self.resample(self.samples, float(new_rate)/self.rate, clip= clip)) def trim_to_size(self, n): return Sound(None, self.rate, self.samples[:n]) def resample(self, signal, sc, clip = True, num_samples = None): n = int(round(signal.shape[0] * sc)) if num_samples is None else num_samples r = scipy.signal.resample(signal, n) if clip: r = np.clip(r, -1, 1) else: r = r.astype(np.int16) return r