import soundfile as sf import numpy as np from scipy import signal from pydub import AudioSegment import subprocess import os # ref. Hirst The analysis by synthesis of speech melody: from data to models # reaper requires wav file path input, # not audio data itself. # reaper does NOT require 16khz mono audio. def reaper_soundfile(sound_path, orig_filetype): aud_data = AudioSegment.from_file(sound_path, orig_filetype) curdir = subprocess.run(["pwd"], capture_output=True, text=True) curdir = curdir.stdout.splitlines()[0] fname = sound_path.split('/')[-1].replace(orig_filetype,'') tmp_path = f'{curdir}/REAPER_TMP/{fname}_tmp.wav' if not os.path.exists(f'{curdir}/REAPER_TMP'): os.mkdir(f'{curdir}/REAPER_TMP') aud_data.export(tmp_path, format="wav") wav_path = tmp_path return wav_path def get_reaper_data(wav_path, maxf0='700', minf0='50', reaper_path = "REAPER/build/reaper"): f0_data = subprocess.run([reaper_path, "-i", wav_path, '-f', '/dev/stdout', '-x', maxf0, '-m', minf0, '-a'],capture_output=True).stdout #print('PLAIN:',f0_data) f0_data = f0_data.decode() #print('DECODE-PITCH:',f0_data) f0_data = f0_data.split('EST_Header_End\n')[1].splitlines() #print(f0_data) f0_data = [l.split(' ') for l in f0_data] f0_data = [l for l in f0_data if len(l) == 3] # the last line or 2 lines are other info, different format f0_data = [ [float(t), float(f)] for t,v,f in f0_data if v=='1'] return f0_data # currently, # take the simplified list data from get_reaper_data, # with format Time F0Val only at times with existing F0Val, # and write that to a text file. # alternate would be letting reaper write its own files # instead of capturing the stdout... def save_pitch(f0_data, save_path,hed=True): with open(save_path,'w') as handle: if hed: handle.write('TIME\tF0\n') handle.write(''.join(['\t'.join(l) + '\n' for l in f0_data])) # 2 pass pitch estimation def estimate_pitch(sound_path): orig_ftype = sound_path.split('.')[-1] if orig_ftype == '.wav': wav_path = sound_path else: tmp_path = reaper_soundfile(sound_path, orig_ftype) wav_path = tmp_path print('REAPER FILE PATH:', wav_path) first_pass = get_reaper(wav_path) first_pass = [f for t,f in first_pass] q1 = np.quantile(first_pass,0.25) q3 = np.quantile(first_pass,0.75) pfloor = 0.75 * q1 pceil = 1.5 * q3 second_pass = get_reaper(wav_path,maxf0 = str(round(pceil)), minf0 = str(round(pfloor))) if orig_ftype != '.wav': subprocess.run(["rm", tmp_path]) return second_pass