File size: 3,046 Bytes
06af375 779c244 06af375 53792d8 06af375 53792d8 06af375 2defee0 53792d8 2defee0 53792d8 06af375 53792d8 06af375 2defee0 06af375 53792d8 06af375 53792d8 06af375 53792d8 06af375 53792d8 06af375 53792d8 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
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
# returns f0 data as list of Time, F0 if exists, voicing indicator
def get_reaper(wav_path, reaper_path, maxf0='700', minf0='50'):
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), float(v)] for t,v,f in f0_data]
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=False):
with open(save_path,'w') as handle:
if hed:
handle.write('TIME\tF0\tVOICED\n')
handle.write(''.join([f'{t}\t{f}\t{v}\n' for t,f,v in f0_data]))
# 2 pass pitch estimation
def estimate_pitch(sound_path,reaper_path = "REAPER/build/reaper"):
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,reaper_path)
first_pass = [f for t,f,v in first_pass if float(v) ==1]
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,reaper_path, maxf0 = str(round(pceil)), minf0 = str(round(pfloor)))
#if orig_ftype != '.wav':
# subprocess.run(["rm", tmp_path])
# don't remove it yet, need it for clustering too
# therefore, actually change so reaper2pass is called from inside clusterprosody
# before it wants to read the f0 file.
# TODO
return second_pass
|