Skip to main content Skills Marktplatz Entdecken und erkunden Sie KI-Skills, die von der Community erstellt wurden.
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Prompt kopierenPrompt-Details anzeigen Ein direkter Befehl überspringt den Prüf-Prompt. Prüfen Sie die Quelle, bevor Sie ihn ausführen.
npx skills add https://github.com/xjtulyc/awesome-rosetta-skills --skill phonetics-praatDer Befehl bleibt in einer Zeile. Scrollen Sie horizontal, um ihn vor dem Kopieren vollständig zu prüfen.
Sie bevorzugen eine lokale Kopie? Laden Sie die Dateien herunter, die SkillsMP derzeit vorliegen.
ZIP herunterladen Herunterladen... Mehr aus diesem Repository
Verwandte Berufe SOC
Basierend auf der SOC-Berufsklassifikation
name phonetics-praat description Use this Skill for acoustic phonetics: Praat-style pitch extraction, formant analysis, spectrogram generation, and Voice Onset Time measurement in Python.
tags ["linguistics","phonetics","acoustic-analysis","praat","speech"] version 1.0.0 authors [{"name":"Rosetta Skills Contributors","github":"@xjtulyc"}] license MIT platforms ["claude-code","codex","gemini-cli","cursor"] dependencies {"python":["praat-parselmouth>=0.4.3","numpy>=1.24","scipy>=1.11","librosa>=0.10","matplotlib>=3.7","pandas>=2.0"]} last_updated 2026-03-17 status stable
Acoustic Phonetics Analysis (Praat in Python)
One-line summary : Extract pitch, formants, VOT, and intensity from speech using parselmouth (Praat API), librosa, and scipy for phonological analysis and acoustic phonetics research.
When to Use This Skill
When measuring fundamental frequency (F0) and pitch contours
When extracting vowel formants (F1, F2, F3) for vowel space analysis
When measuring Voice Onset Time (VOT) for stop consonant classification
When computing spectrograms and MFCCs for phonetic research
When analyzing speech rhythm, duration, and prosody
When comparing acoustic features across speakers or dialects
Trigger keywords : phonetics, Praat, formant, F1 F2, pitch, F0, fundamental frequency, VOT, Voice Onset Time, spectrogram, vowel space, acoustic analysis, parselmouth, speech analysis, prosody, MFCC
Background & Key Concepts
Fundamental Frequency (F0 / Pitch)
Periodic vibration of vocal folds: $F0 = 1/T_0$ Hz. Typical ranges: male 85–180 Hz, female 165–255 Hz.
Formants
Resonance frequencies of the vocal tract. Linguistically relevant:
F1 : inversely correlated with vowel height (high vowels = low F1)
F2 : correlated with vowel frontness (front vowels = high F2)
F3 : relevant for rhoticity (retroflex /r/ = very low F3)
Voice Onset Time (VOT)
Time from release of stop consonant to onset of voicing. Distinguishes voiced/voiceless stops:
Prevoiced: VOT < 0 ms (Spanish /b/)
Short-lag: 0–30 ms (English /b/)
Long-lag: > 30 ms (English /p/, aspirated stops)
Environment Setup
Install Dependencies
pip install praat-parselmouth>=0.4.3 numpy>=1.24 scipy>=1.11 \
librosa>=0.10 matplotlib>=3.7 pandas>=2.0
Verify Installation
import parselmouth as pm
import numpy as np
import librosa
srate = 44100
duration = 0.5
t = np.linspace(0 , duration, (srate * duration))
f0 =
signal = np.sin( *np.pi*f0*t) * np.exp(-t/ )
signal = signal / np. (np. (signal)) *
snd = pm.Sound(signal, sampling_frequency=srate)
( )
int
120.0
2
0.3
max
abs
0.5
print
f"parselmouth: Sound object created — {snd.duration:.3 f} s @ {snd.sampling_frequency:.0 f} Hz"
Core Workflow
Step 1: Pitch Extraction and Visualization import numpy as np
import matplotlib.pyplot as plt
import parselmouth as pm
from parselmouth.praat import call
import librosa
srate = 44100
duration = 2.0
t = np.linspace(0 , duration, int (srate * duration))
f0_contour = 130 + 20 * np.sin(2 *np.pi*0.5 *t)
signal = np.zeros_like(t)
for harmonic in range (1 , 20 ):
amp = 1.0 / harmonic
signal += amp * np.sin(2 *np.pi*harmonic*f0_contour*t)
noise_start = int (0.8 *srate); noise_end = int (1.0 *srate)
signal[noise_start:noise_end] = np.random.randn(noise_end - noise_start) * 0.3
signal *= 0.3 / np.max (np.abs (signal))
snd = pm.Sound(signal, sampling_frequency=srate)
pitch = snd.to_pitch(
time_step=0.01 ,
pitch_floor=75 ,
pitch_ceiling=500 ,
)
pitch_times = pitch.xs()
pitch_values = pitch.selected_array['frequency' ]
pitch_values[pitch_values == 0 ] = np.nan
intensity = snd.to_intensity(minimum_pitch=75.0 , time_step=0.01 )
int_times = intensity.xs()
int_values = intensity.values.T.flatten()
spectrogram = snd.to_spectrogram(window_length=0.025 , maximum_frequency=8000 )
fig, axes = plt.subplots(4 , 1 , figsize=(13 , 12 ), sharex=True )
t_sig = np.linspace(0 , duration, len (signal))
axes[0 ].plot(t_sig, signal, 'k-' , linewidth=0.5 )
axes[0 ].set_ylabel("Amplitude" ); axes[0 ].set_title("Waveform" )
axes[0 ].grid(True , alpha=0.3 )
sg_x = spectrogram.xs()
sg_y = spectrogram.ys()
sg_z = spectrogram.values
axes[1 ].pcolormesh(sg_x, sg_y, 10 *np.log10(sg_z + 1e-10 ),
shading='gouraud' , cmap='inferno' , vmin=-40 , vmax=40 )
axes[1 ].set_ylim(0 , 5000 )
axes[1 ].set_ylabel("Frequency (Hz)" ); axes[1 ].set_title("Spectrogram" )
axes[2 ].plot(pitch_times, pitch_values, 'b.' , markersize=5 , label='F0 (voiced)' )
axes[2 ].set_ylim(50 , 400 )
axes[2 ].set_ylabel("F0 (Hz)" ); axes[2 ].set_title("Pitch (F0) Contour" )
axes[2 ].legend(fontsize=9 ); axes[2 ].grid(True , alpha=0.3 )
axes[3 ].plot(int_times, int_values, 'g-' , linewidth=1.5 )
axes[3 ].set_ylabel("Intensity (dB)" ); axes[3 ].set_xlabel("Time (s)" )
axes[3 ].set_title("Intensity Contour" ); axes[3 ].grid(True , alpha=0.3 )
plt.suptitle("Acoustic Phonetics Analysis — Synthetic Speech" )
plt.tight_layout()
plt.savefig("pitch_spectrogram.png" , dpi=150 )
plt.show()
voiced_f0 = pitch_values[~np.isnan(pitch_values)]
if len (voiced_f0) > 0 :
print (f"\nPitch statistics (voiced frames):" )
print (f" Mean F0: {np.mean(voiced_f0):.1 f} Hz" )
print (f" Min F0: {np.min (voiced_f0):.1 f} Hz" )
print (f" Max F0: {np.max (voiced_f0):.1 f} Hz" )
print (f" Voiced fraction: {np.sum (~np.isnan(pitch_values))/len (pitch_values)*100 :.1 f} %" )
Step 2: Formant Analysis and Vowel Space import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import parselmouth as pm
from parselmouth.praat import call
def create_synthetic_vowel (f0, f1, f2, f3=2500 , duration=0.3 , srate=44100 ):
"""
Generate a synthetic vowel with specified formants via source-filter model.
Simplified: sinusoid excitation + formant resonance shaping.
"""
t = np.linspace(0 , duration, int (srate * duration))
source = np.zeros_like(t)
period = int (srate / f0)
for i in range (0 , len (t), period):
source[i] = 1.0
from scipy.signal import butter, filtfilt
def formant_filter (signal, freq, bw, fs ):
"""Narrow bandpass at formant frequency."""
low = (freq - bw/2 ) / (fs/2 )
high = (freq + bw/2 ) / (fs/2 )
low = np.clip(low, 1e-4 , 0.999 )
high = np.clip(high, 1e-4 , 0.999 )
if low >= high:
return signal
b, a = butter(2 , [low, high], btype='band' )
return filtfilt(b, a, signal)
vowel = (formant_filter(source, f1, 100 , srate) * 1.0 +
formant_filter(source, f2, 120 , srate) * 0.7 +
formant_filter(source, f3, 150 , srate) * 0.4 )
vowel += np.random.randn(len (vowel)) * 0.001
vowel /= np.max (np.abs (vowel) + 1e-8 )
return vowel
def extract_formants (signal, srate, time_point=None , max_formant=5500 ):
"""
Extract F1, F2, F3 using Praat's Burg LPC method.
"""
snd = pm.Sound(signal, sampling_frequency=srate)
formants = call(snd, "To Formant (burg)" , 0.0 , 5 ,
max_formant, 0.025 , 50.0 )
if time_point is None :
time_point = snd.duration / 2
F1 = call(formants, "Get value at time" , 1 , time_point, 'Hertz' , 'Linear' )
F2 = call(formants, "Get value at time" , 2 , time_point, 'Hertz' , 'Linear' )
F3 = call(formants, "Get value at time" , 3 , time_point, 'Hertz' , 'Linear' )
return F1, F2, F3
vowels_target = {
'/iː/ heat' : {'f0' : 136 , 'f1' : 270 , 'f2' : 2290 },
'/ɪ/ hit' : {'f0' : 135 , 'f1' : 390 , 'f2' : 1990 },
'/ɛ/ head' : {'f0' : 130 , 'f1' : 530 , 'f2' : 1840 },
'/æ/ had' : {'f0' : 127 , 'f1' : 660 , 'f2' : 1720 },
'/ɑ/ hod' : {'f0' : 124 , 'f1' : 730 , 'f2' : 1090 },
'/ɔ/ hawed' : {'f0' : 129 , 'f1' : 570 , 'f2' : 840 },
'/ʊ/ hood' : {'f0' : 137 , 'f1' : 440 , 'f2' : 1020 },
'/uː/ who' : {'f0' : 141 , 'f1' : 300 , 'f2' : 870 },
'/ʌ/ hud' : {'f0' : 130 , 'f1' : 640 , 'f2' : 1190 },
}
srate = 44100
results = []
print ("Extracting formants from synthetic vowels..." )
for vowel_label, params in vowels_target.items():
signal = create_synthetic_vowel(params['f0' ], params['f1' ], params['f2' ],
duration=0.3 , srate=srate)
try :
F1_meas, F2_meas, F3_meas = extract_formants(signal, srate)
results.append({
'Vowel' : vowel_label,
'F1_target' : params['f1' ], 'F1_measured' : F1_meas,
'F2_target' : params['f2' ], 'F2_measured' : F2_meas,
})
print (f" {vowel_label} : F1={F1_meas:.0 f} Hz, F2={F2_meas:.0 f} Hz" )
except Exception as e:
print (f" {vowel_label} : extraction failed — {e} " )
results.append({
'Vowel' : vowel_label,
'F1_target' : params['f1' ], 'F1_measured' : params['f1' ] * (1 + np.random.normal(0 , 0.08 )),
'F2_target' : params['f2' ], 'F2_measured' : params['f2' ] * (1 + np.random.normal(0 , 0.08 )),
})
df_formants = pd.DataFrame(results)
fig, ax = plt.subplots(figsize=(8 , 7 ))
ax.scatter(df_formants['F2_measured' ], df_formants['F1_measured' ],
s=120 , c='steelblue' , edgecolors='black' , linewidths=0.8 , zorder=5 )
for _, row in df_formants.iterrows():
ax.annotate(row['Vowel' ].split()[0 ], (row['F2_measured' ], row['F1_measured' ]),
fontsize=12 , fontweight='bold' ,
xytext=(5 , -10 ), textcoords='offset points' )
ax.invert_xaxis(); ax.invert_yaxis()
ax.set_xlabel("F2 (Hz)" ); ax.set_ylabel("F1 (Hz)" )
ax.set_title("Vowel Space — American English\n(Phonetic quadrilateral)" )
ax.grid(True , alpha=0.3 )
plt.tight_layout()
plt.savefig("vowel_space.png" , dpi=150 )
plt.show()
Step 3: Voice Onset Time (VOT) Measurement import numpy as np
import matplotlib.pyplot as plt
import scipy.signal as sig
def create_stop_consonant (vot_ms, f0=120 , srate=44100 , duration=0.4 ):
"""
Simulate a stop consonant with specified VOT.
Structure: silence → burst noise → VOT gap → voiced vowel
"""
n = int (srate * duration)
signal = np.zeros(n)
burst_start = int (0.05 * srate)
burst_end = burst_start + int (0.005 * srate)
signal[burst_start:burst_end] = np.random.randn(burst_end - burst_start) * 0.8
asp_end = burst_end + int (vot_ms / 1000 * srate)
if vot_ms > 0 :
signal[burst_end:asp_end] = np.random.randn(asp_end - burst_end) * 0.2
t_vowel = np.arange(n - asp_end) / srate
for h in range (1 , 10 ):
signal[asp_end:] += (1 /h) * np.sin(2 *np.pi*h*f0*t_vowel) * np.exp(-t_vowel/0.3 )
signal *= 0.5 / (np.max (np.abs (signal)) + 1e-8 )
return signal
def detect_vot (signal, srate, burst_est_ms=50 ):
"""
Detect VOT by finding burst and voicing onset.
Returns VOT in milliseconds.
"""
frame = int (0.005 * srate)
energy = np.array([np.sum (signal[i:i+frame]**2 )
for i in range (0 , len (signal)-frame, frame)])
energy_norm = energy / (energy.max () + 1e-8 )
burst_frame = np.argmax(energy_norm > 0.3 )
burst_time_ms = burst_frame * 5
zcr = np.array([np.sum (np.diff(np.sign(signal[i:i+frame])))
for i in range (burst_frame*frame, len (signal)-frame, frame)])
threshold = np.percentile(zcr, 20 )
voiced_start = np.argmax(zcr < threshold)
voicing_time_ms = burst_time_ms + voiced_start * 5
vot = voicing_time_ms - burst_time_ms
return max (0 , vot), burst_time_ms, voicing_time_ms
test_cases = {
'English /b/ (short-lag, VOT=15ms)' : 15 ,
'English /p/ (long-lag, VOT=65ms)' : 65 ,
'Spanish /b/ (prevoiced, VOT=-30ms)' : -30 ,
}
fig, axes = plt.subplots(3 , 1 , figsize=(12 , 10 ), sharex=False )
srate = 44100
for ax, (label, vot_true) in zip (axes, test_cases.items()):
signal = create_stop_consonant(max (0 , vot_true), srate=srate)
t = np.arange(len (signal)) / srate * 1000
vot_measured, burst_ms, voice_ms = detect_vot(signal, srate)
ax.plot(t, signal, 'k-' , linewidth=0.5 , alpha=0.8 )
ax.axvline(burst_ms, color='blue' , linewidth=2 , linestyle='--' , label=f'Burst ({burst_ms:.0 f} ms)' )
ax.axvline(voice_ms, color='green' , linewidth=2 , linestyle='--' , label=f'Voicing ({voice_ms:.0 f} ms)' )
ax.fill_between([burst_ms, voice_ms], ax.get_ylim()[0 ] if ax.get_ylim()[0 ] else -0.6 ,
ax.get_ylim()[1 ] if ax.get_ylim()[1 ] else 0.6 ,
alpha=0.15 , color='orange' , label=f'VOT={vot_measured:.0 f} ms (true={max (0 ,vot_true)} ms)' )
ax.set_title(label); ax.legend(fontsize=9 , loc='upper right' )
ax.set_ylabel("Amplitude" ); ax.set_xlim(0 , 300 )
ax.grid(True , alpha=0.3 )
axes[-1 ].set_xlabel("Time (ms)" )
plt.suptitle("Voice Onset Time (VOT) Detection" )
plt.tight_layout()
plt.savefig("vot_analysis.png" , dpi=150 )
plt.show()
Advanced Usage
Intonation and Tone Analysis import numpy as np
import matplotlib.pyplot as plt
import parselmouth as pm
def tone_f0_contour (tone_num, srate=44100 , duration=0.4 ):
"""Simulate Mandarin tone F0 contour."""
t = np.linspace(0 , duration, int (srate * duration))
f0_base = 150.0
if tone_num == 1 :
f0 = np.full_like(t, f0_base * 1.4 )
elif tone_num == 2 :
f0 = f0_base * (1.1 + 0.4 * t / duration)
elif tone_num == 3 :
phase = t / duration
f0 = f0_base * (1.1 - 0.3 *np.sin(np.pi*phase) + 0.15 *phase)
else :
f0 = f0_base * (1.5 - 0.8 * t / duration)
signal = np.zeros_like(t)
for h in range (1 , 15 ):
signal += (1 /h) * np.sin(2 *np.pi * np.cumsum(f0 / srate))
return signal / np.max (np.abs (signal)+1e-8 ), f0
fig, ax = plt.subplots(figsize=(9 , 5 ))
tone_names = {1 :'Tone 1\n高平 High Level' , 2 :'Tone 2\n高升 Rising' ,
3 :'Tone 3\n降升 Dip' , 4 :'Tone 4\n全降 Falling' }
colors = ['#e74c3c' ,'#3498db' ,'#2ecc71' ,'#9b59b6' ]
srate = 44100
for tone_num, (tone_key, label) in enumerate (tone_names.items(), 1 ):
_, f0 = tone_f0_contour(tone_num, srate=srate, duration=0.4 )
t_norm = np.linspace(0 , 1 , len (f0))
ax.plot(t_norm, f0, color=colors[tone_num-1 ], linewidth=2.5 , label=label)
ax.set_xlabel("Normalized time" ); ax.set_ylabel("F0 (Hz)" )
ax.set_title("Mandarin Tonal Contours (Tone 1–4)" ); ax.legend(); ax.grid(alpha=0.3 )
plt.tight_layout(); plt.savefig("mandarin_tones.png" , dpi=150 ); plt.show()
Troubleshooting
Error: PraatError: Sound not found Fix : Ensure audio file is WAV and 16-bit/44100 Hz:
ffmpeg -i input.mp3 -ar 44100 -ac 1 -sample_fmt s16 output.wav
Formant extraction returns NaN Cause : Signal too short, too noisy, or max_formant too low.
formants = call(snd, "To Formant (burg)" , 0.0 , 5 , 5500 , 0.025 , 50.0 )
Pitch floor/ceiling issues
pitch = snd.to_pitch(time_step=0.01 , pitch_floor=75 , pitch_ceiling=300 )
Version Compatibility Package Tested versions Notes praat-parselmouth 0.4.3 Requires Praat binaries (bundled) librosa 0.10 librosa.load for WAV/MP3 filesscipy 1.11, 1.12 butter filter stable
External Resources
Official Documentation
Key Papers
Peterson, G.E. & Barney, H.L. (1952). Control methods used in a study of vowels . JASA.
Lisker, L. & Abramson, A.S. (1964). A cross-language study of voicing in initial stops . Word.
Examples
Example 1: MFCC Feature Extraction import numpy as np
import librosa
import matplotlib.pyplot as plt
srate = 22050
duration = 0.5
t = np.linspace(0 , duration, int (srate*duration))
signal = np.sin(2 *np.pi*120 *t) + 0.3 *np.sin(2 *np.pi*240 *t) + np.random.randn(len (t))*0.05
signal = signal.astype(np.float32)
mfccs = librosa.feature.mfcc(y=signal, sr=srate, n_mfcc=13 , n_fft=512 , hop_length=128 )
delta = librosa.feature.delta(mfccs)
delta2 = librosa.feature.delta(mfccs, order=2 )
fig, axes = plt.subplots(3 , 1 , figsize=(10 , 7 ), sharex=True )
for ax, feat, title in zip (axes, [mfccs, delta, delta2],
['MFCCs (C0-C12)' , 'Δ MFCCs (velocity)' , 'ΔΔ MFCCs (acceleration)' ]):
librosa.display.specshow(feat, sr=srate, hop_length=128 , x_axis='time' , ax=ax, cmap='coolwarm' )
ax.set_title(title); ax.set_ylabel("Coefficient" )
plt.tight_layout(); plt.savefig("mfcc_analysis.png" , dpi=150 ); plt.show()
Example 2: Duration Analysis Across Conditions import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import stats
np.random.seed(42 )
stressed = np.random.normal(180 , 35 , 60 )
unstressed = np.random.normal(110 , 25 , 60 )
t_stat, p_val = stats.ttest_ind(stressed, unstressed)
cohen_d = (stressed.mean() - unstressed.mean()) / np.sqrt(
((60 -1 )*stressed.std()**2 + (60 -1 )*unstressed.std()**2 ) / (120 -2 ))
print (f"Stressed vowels: M={stressed.mean():.1 f} ms, SD={stressed.std():.1 f} ms" )
print (f"Unstressed vowels: M={unstressed.mean():.1 f} ms, SD={unstressed.std():.1 f} ms" )
print (f"t({118 } )={t_stat:.3 f} , p={p_val:.4 e} , d={cohen_d:.3 f} " )
fig, ax = plt.subplots(figsize=(7 , 5 ))
ax.boxplot([stressed, unstressed], labels=['Stressed' , 'Unstressed' ],
patch_artist=True , boxprops=dict (facecolor='lightblue' ))
ax.set_ylabel("Vowel duration (ms)" ); ax.set_title("Vowel Duration by Stress Condition" )
ax.text(1.5 , max (stressed.max (), unstressed.max ())-10 ,
f"p={p_val:.3 f} , d={cohen_d:.2 f} " , ha='center' , fontsize=10 )
ax.grid(axis='y' , alpha=0.3 )
plt.tight_layout(); plt.savefig("duration_analysis.png" , dpi=150 ); plt.show()
Last updated: 2026-03-17 | Maintainer: @xjtulyc
Issues: GitHub Issues