Reputation: 547
I have a praat script that extracts formant information from a folder of wavefiles:
clearinfo
min_f0 = 75
max_f0 = 350
directory$ = "./soundfiles/"
outputDir$ = "./test/"
strings = Create Strings as file list: "list", directory$ + "/*.WAV"
numberOfFiles = Get number of strings
for ifile to numberOfFiles
select Strings list
filename$ = Get string... ifile
Read from file... 'directory$''filename$'
soundname$ = selected$ ("Sound", 1)
outputFileName$ = outputDir$ + soundname$ + ".f0123"
appendInfoLine: outputFileName$
select Sound 'soundname$'
formant = To Formant (burg): 0, 4, 5000, 0.025, 50
formantStep = Get time step
selectObject: formant
table = Down to Table: "no", "yes", 6, "yes", 3, "yes", 3, "yes"
numberOfRows = Get number of rows
select Sound 'soundname$'
pitch = To Pitch: 0, min_f0, max_f0
selectObject: table
Append column: "Pitch"
for step to numberOfRows
selectObject: table
t = Get value: step, "time(s)"
selectObject: pitch
pitchValue = Get value at time: t, "Hertz", "Nearest"
selectObject: table
Set numeric value: step, "Pitch", pitchValue
endfor
#export to csv
selectObject: table
Save as comma-separated file: outputFileName$
removeObject(table)
select all
minus Strings list
Remove
endfor
select all
Remove
exit
And it generates the following output:
time(s),intensity,nformants,F1(Hz),B1(Hz),F2(Hz),B2(Hz),F3(Hz),B3(Hz),F4(Hz),B4(Hz),Pitch
0.025370,0.000007,3,213.115,14.053,2385.911,791.475,3622.099,677.605,--undefined--,--undefined--,--undefined--
0.031620,0.000007,3,208.843,15.034,2487.710,687.736,3818.027,645.184,--undefined--,--undefined--,197.5315925472943
...
This works great for what I need, but is there a way to get the intensity of each formant as well? Right now I only have the one intensity estimate.
Upvotes: 2
Views: 1935
Reputation: 2098
I'm not sure if this is what you need, but based on the comment from @nikolay-shmyrev, this is how you'd insert the measurement of formant intensity from Spectrogram objects into your script.
I seem to be inoculated against the pain of scripting using Praat...
I simplified the script below so that it works only on the currently selected Sound object (for testing), and simply kept the generated Table (so you can check it out), but it should point you in the right direction.
form Script...
positive Minimum_F0 75
positive Maximum_F0 350
positive Formants 4
endform
sound = selected("Sound")
pitch = To Pitch: 0, minimum_F0, maximum_F0
# You need this for the intensity
selectObject: sound
spectrogram = To Spectrogram: 0.005, 5000, 0.002, 20, "Gaussian"
selectObject: sound
formant = To Formant (burg): 0, formants, 5000, 0.025, 50
table = Down to Table: "no", "yes", 6, "yes", 3, "yes", 3, "yes"
Append column: "Pitch"
# Insert columns for each formant intensity
# (labeled here as "I#", where # is the formant index)
for f to formants
index = Get column index: "F" + string$(f) + "(Hz)"
Insert column: index + 1, "I" + string$(f)
endfor
for row to Object_'table'.nrow
selectObject: table
time = Object_'table'[row, "time(s)"]
# Get the intensity of each formant
for f to formants
frequency = Object_'table'[row, "F" + string$(f) + "(Hz)"]
selectObject: spectrogram
if frequency != undefined
intensity = Get power at: time, frequency
else
intensity = undefined
endif
selectObject: table
Set string value: row, "I" + string$(f), fixed$(intensity, 3)
endfor
selectObject: pitch
pitchValue = Get value at time: time, "Hertz", "Nearest"
selectObject: table
Set string value: row, "Pitch", fixed$(pitchValue, 3)
endfor
removeObject: spectrogram, formant, pitch
Upvotes: 0
Reputation: 24096
It's an old question, but I'll still respond.
I've ran into this too in 2002, when I was creating an editor for a hardware format synthesizer (FS1R). I used praat to do the wav->format tracks calculation, and the synthesizer expects formant frequencies and intensities as an input.
I've implemented several algorithms for it, but the one that had the most realistic results evaluated the intensity for each formant at each frame in the spectogram.
Here's the code that I've used for that. Keep in mind that it was my goal to get a list of 512 frames with up to 8 freq/intensity pairs, and a fundamental pitch.
# Add to dynamic menu... Sound 1 "" 0 "" 0 "Sine-wave speech" Resample... 1 yourdisk:Praat:scripts:SWS
form Add Sounds
word wavePath e:\samples\wav\root\
word waveFile DOUG.wav
word OutPath e:\samples\wav\root\
integer minFP 75
integer maxFP 500
integer maxFF 5000
integer Amp_low_pass_freq 50
integer Formant_low_pass_freq 20
endform
echo Wave to FSeq - FORMANT EXTRACTION
echo -------------------------------------------------------
# LOAD WAVEFILE
echo loading 'wavePath$''waveFile$'
Read from file... 'wavePath$''waveFile$'
if numberOfSelected ("Sound") <> 1
pause Select one Sound then Continue
endif
snd$ = selected$("Sound", 1)
snd = selected("Sound", 1)
sampleRate = Get sample rate
numSamples = Get number of samples
dur = Get duration
zzz = 512/509*512
timeStep = dur/zzz
echo samplerate : 'sampleRate' herz
echo number of samples : 'numSamples'
echo duration : 'dur' seconds
echo timestep : 'timeStep' seconds
echo
# GET FUNDAMENTAL PITCH
echo getting fundamental pitch
# this was the old method, used until FSeqEdit 1.21:
# To Pitch... 'timeStep' 'minFP' 'maxFP'
# Interpolate
# this algorithm seems to work better
To Pitch (ac)... 'timeStep' 'minFP' 15 no 1e-06 0.1 0.01 1 1 'maxFP'
Kill octave jumps
Interpolate
select Pitch 'snd$'
Write to short text file... 'outPath$'pitch.txt
select Pitch 'snd$'
Remove
# GET VOICED/UNVOICED INFORMATION
echo getting voiced/unvoiced information
select Pitch 'snd$'
To PointProcess
select PointProcess 'snd$'
To TextGrid (vuv)... 0.02 'timeStep'
select TextGrid 'snd$'
Write to short text file... 'outPath$'vuv.txt
#create wide-band spectrogram for finding formant amplitudes
# to spectorgam analwidth maxfreq timestep freqstep windowshape
echo to spectogram
select 'snd'
To Spectrogram... 0.003 'maxFF' 0.001 40 Gaussian
select 'snd'
echo finding formants
To Formant (burg)... 'timeStep' 8 'maxFF' 0.025 50
Rename... untrack
Track... 6 'maxFP' 'maxFP'*3 'maxFP'*5 'maxFP'*7 'maxFP'*9 1 0.1 1
Rename... 'snd$'
select Formant untrack
Remove
select 'snd'
#start of main formant loop
#===========================
#for each chosen formant turn formant tracks into
#a Matrix then a Sound object for optional low-pass filtering
#NB this Sound object is the formant TRACK
#then back into a Matrix object for sound synthesis
for i from 1 to 6
# make a matrix from Fi
select Formant 'snd$'
echo extracting formant 'i'
To Matrix... 'i'
Rename... f'i'
#low-pass filter the formant track and tidy-up the names
#filtering needs a Sound object, so cast as Sound, filter and then back to Matrix
if Formant_low_pass_freq <> 0
To Sound (slice)... 1
Filter (pass Hann band)... 0 'formant_low_pass_freq' 'formant_low_pass_freq'
Down to Matrix
select Matrix f'i'
Remove
select Matrix f'i'_band
Rename... f'i'
select Sound f'i'
plus Sound f'i'_band
Remove
endif
#set up amplitude contour array (sample only at 1kHz) for i'th formant
#make it a Sound object so that it can be smoothed by filtering
Create Sound... amp'i' 0 'dur' 1000 sqrt(Spectrogram_'snd$'(x,Matrix_f'i'(x)))
#smooth out pitch amplitude modulation by low-pass filtering
if Amp_low_pass_freq <> 0
Filter (pass Hann band)... 0 'amp_low_pass_freq' 'amp_low_pass_freq'
select Sound amp'i'
Remove
select Sound amp'i'_band
Rename... amp'i'
endif
Extract part... 0 'dur' Rectangular 1 yes
To Intensity... 'minFP' 0
Write to short text file... 'outPath$'amp'i'.txt
select Matrix f'i'
Remove
endfor
#===========================
#end of the main formant loop
select Formant 'snd$'
Write to short text file... 'outPath$'formant.txt
#tidy-up
select Spectrogram 'snd$'
plus Formant 'snd$'
plus Pitch 'snd$'
plus PointProcess 'snd$'
plus TextGrid 'snd$'
Remove
echo
echo -------------------------------------------------------
echo done.
Upvotes: 0