def block_mix_wavs(wavpath_a, wavpath_b, out_wavpath, a_gain=1., b_gain=1., block_size=4096, mute_left=False): ''' Mix two wav files, applying gains to each ''' wav_a = PySndfile(wavpath_a, 'r') wav_b = PySndfile(wavpath_b, 'r') out_wav = PySndfile(out_wavpath, 'w', construct_format('wav', 'pcm16'), wav_a.channels(), wav_a.samplerate()) i = 0 while i < wav_a.frames(): if i + block_size > wav_a.frames(): block_size = wav_a.frames() - i x1 = wav_a.read_frames(block_size) x2 = wav_b.read_frames(block_size) x1[:, :2] *= a_gain x2 *= b_gain if x1.shape[1] == 3: y = np.zeros(x1.shape) y[:, 0] = x1[:, 0] + x2 y[:, 1] = x1[:, 1] + x2 y[:, 2] = x1[:, 2] if mute_left: y[:, 0] = 0.0 else: y = x1 + x2 out_wav.write_frames(y) i += block_size
def block_process_wav(wavpath, out_wavpath, func, block_size=4096, **args): ''' Mix two wav files, applying gains to each ''' wav = PySndfile(wavpath, 'r') out_wav = PySndfile(out_wavpath, 'w', construct_format('wav', 'pcm16'), wav.channels(), wav.samplerate()) i = 0 while i < wav.frames(): if i + block_size > wav.frames(): block_size = wav.frames() - i x = wav.read_frames(block_size) y = func(x, **args) out_wav.write_frames(y) i += block_size del out_wav
def __init__(self, fn, sr=None, chns=None, blksz=2**16, dtype=np.float32): fnd = False if not fnd and (PySndfile is not None): try: sf = PySndfile(fn, mode='r') except IOError: pass else: if (sr is None or sr == sf.samplerate()) and ( chns is None or chns == sf.channels()): # no resampling required self.channels = sf.channels() self.samplerate = sf.samplerate() self.frames = sf.frames() self.rdr = sndreader(sf, blksz, dtype=dtype) fnd = True if not fnd: ffmpeg = findfile('ffmpeg') or findfile('avconv') if ffmpeg is not None: pipe = sp.Popen([ffmpeg, '-i', fn, '-'], stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE) fmtout = pipe.stderr.read() if (sys.version_info > (3, 0)): fmtout = fmtout.decode() m = re.match( r"^(ffmpeg|avconv) version.*Duration: (\d\d:\d\d:\d\d.\d\d),.*Audio: (.+), (\d+) Hz, (.+), (.+), (\d+) kb/s", " ".join(fmtout.split('\n'))) if m is not None: self.samplerate = int(m.group(4)) if not sr else int(sr) chdef = m.group(5) if chdef.endswith(" channels") and len(chdef.split()) == 2: self.channels = int(chdef.split()[0]) else: try: self.channels = { 'mono': 1, '1 channels (FL+FR)': 1, 'stereo': 2, 'hexadecagonal': 16 }[chdef] if not chns else chns except: print(f"Channel definition '{chdef}' unknown") raise dur = reduce(lambda x, y: x * 60 + y, list(map(float, m.group(2).split(':')))) self.frames = int( dur * self.samplerate ) # that's actually an estimation, because of potential resampling with round-off errors pipe = sp.Popen( [ ffmpeg, '-i', fn, '-f', 'f32le', '-acodec', 'pcm_f32le', '-ar', str(self.samplerate), '-ac', str(self.channels), '-' ], # bufsize=self.samplerate*self.channels*4*50, stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE) def rdr(): bufsz = (blksz // self.channels) * self.channels * 4 while True: data = pipe.stdout.read(bufsz) if len(data) == 0: break data = np.fromstring(data, dtype=dtype) yield data.reshape((-1, self.channels)).T self.rdr = rdr() fnd = True if not fnd: raise IOError("Format not usable")