Peatükk 19 Praati skriptid

Siin peatükis teeme tutvust Praati skriptikeelega. Skriptiga on võimalik koguda ühte tekstifaili kokku erinevad käsud ja neid kombineerida või korrata. Nii on võimalik teha oma tööd palju lihtsamaks. Algajale skriptikirjutajale ei pruugi küll see lihtne näida ning väiksema andmestiku korral võib tunduda, et lihtsam on mõõtmisedandmed koguda toimetamisaknast käsitsi väärtuseid tabelisse kirjutades. Skripti kirjutamine on kindlasti intellektuaalselt huvitavam kui numbrite mehaaniline ümberkirjutamine ning suurema andmestiku korral tasub ettevõetud vaev end kuhjaga ära.

Praatil on täiemahuline skriptikeel. Kõik käsud, mis on Praati graafilise kasutajaliidese menüüdes, töötavad samamoodi ka skriptides. Tänu sellele on Praat hea keel päris algajale, et skriptimist õppida: saame protseduuri teha läbi graafilise kaustajaliidese kaudu ja seejärel kasutatud käsud skripti suunata. Kui skripti kirjutades ei tule mõni käsk või selle argumendid meelde, saab graafilisest menüüst järgi vaadata. Lisaks käskudele on skriptidega võimalik määrata muutujaid, teha tsükleid ja palju muud. Samas võib Praati keel kogenud programmeerijale tunduda kohmakas ning kindlasti ei olegi Praat parim vahend ressursimahukate ülesannete lahendamiseks.

Praati skriptikeel on väga süstemaatiliselt ja algaja skriptija sõbralikult dokumenteeritud Praati manuaalis. Samuti on internetis mitmeid lehekülgi või repositooriume, kust leiab valmis skripte, millega enamlevinud ülesandeid lahendada. Üks populaarsemaid Praati skriptide repositooriumeid on Mietta Lennese skriptikogu.77

19.1 Esimene skript

Ava uus skriptitoimeti aken: menüü Praat > New Praat script. Avanenud aknasse hakkamegi skripti kirjutama.

Vali menüüst Edit > Clear history. Praat peab meeles, mida sa oled parasjagu käimas oleva sessiooni jooksul teinud, aga Clear history kustutab selle mälust ära ja hakkab käske koguma uuesti sellest hetkest alates.

Nüüd mine objektiaknasse, vali mõni Sound-objekt, joonista see pildiaknasse ilma viimistlemata (st võta ära linnuke kastist Garnish) ja viimistle ise (st lisa Praati pildiakna menüüs leiduvate käskudega skaalad ja telgede pealkirjad jms).78

Kui joonis on valmis, mine avatud skriptitoimeti, vali menüüst Edit > Paste History. Nüüd ongi sul olemas skript, mis valib objektiaknast selle objekti ja teeb sellest joonise. See tekst, mis skriptitoimetis on, ongi skript: käskude kogum, mida saab tekstina toimetada ja uuesti jooksutada.

Et proovida, kas skript töötab, mine pildiaknasse ja kustuta kõik ära (Edit > Erase all) ja seejärel jooksuta skripti: mine skriptitoimetisse ja vali käsk Run > Run (või klahvikombinatsioon [Ctrl] + [r]).

Edasi võiks seda skripti kohendada nii, et see töötaks ka teiste sama tüüpi objektidega või näiteks käiks läbi kõik objektiaknas olevad objektid ja teeks igaühest joonise.

19.2 Käsud ja argumendid

Kõik käsud Praati menüüdes on samal kujul kasutatavad Praati skriptides. Praati käsud järgivad loogikat, et kui käsule järgneb dialoogiaken, kus küsitakse mingeid parameetreid, siis menüüs lõppeb käsk kolme punktiga (nt Draw...), kui dialoogiakent pole, siis pole ka kolme punkti (Erase all). Skriptis asendab kolme punkti koolon. Seega kui käsk ei eelda mingite parameetrite täpsustamist, võib selle skripti kirjutada täpselt sellisel kujul, nagu ta on, nt järgnev käsk skriptis kustutab pildiaknast ära kõik, mis seal oli:

Erase all

Kui käsk eeldab parameetrite täpsustamist, siis kõik, mida dialoogiaknas küsitakse, järgnevad skriptis koolonile ja eristatakse komadega. Skriptimisel nimetatakse käsu küsitavaid parameetreid argumentideks. Näiteks kui küsida Sound-objekti mingi sämpli helirõhu väärtust, siis menüüst Query käsk Get value at sample number... küsib dialoogiaknas kahte väärtust:

  1. mitmenda helifaili kanalis väärtust soovid (vaikimisi 0 tähendab, et kõikide kanalite peale kokku),
  2. mitmenda sämpli väärtust soovitakse (100. sämpel 44,1 kHz sämplimissageduse korral on 100 / 44 100 = 0,002267574 sekundit).

Skriptis näeks see käsurida välja selline:

Get value at sample number: 0, 100

Neis kohtades, kus dialoogiaknas on linnuke (nt Draw... käsul Garnish argumendil, mis küsib, kas joonis viimistleda või mitte), märgib linnukest "yes" (või 1) ja linnukese puudumist "no" (või 0).

Argumendid, mille sisuks on tekst, peavad olema jutumärkides. NB! Praatis peavad need olema sirged topeltjutumärgid ", mitte stiliseeritud „algavad-lõppevad“ jutumärgid. Seetõttu on soovitatav mitte kasutada Wordi vms tekstiredaktorit skripti kirjutamiseks, sest need kipuvad jutumärke automaatselt stiliseerima. Samuti ei tööta Praatis tekstistringide tähistamiseks (erinevalt näiteks Pythonist või R-ist) ühekordsed jutumärgid ' (neil on Praatis natuke teine funktsioon, vt ptk 19.12).

Näiteks kui tahame Sound-objektist joonistada esimesest kolmest sekundist helilaine, mille amplituudiulatus oleks valitud automaatselt, aga joonist ei viimistleta, siis käsu Draw... peale graafilises kasutajaliideses avaneb selline dialoogiaken nagu joonisel 19.1. Skriptis oleks sama käsurida kirjas nii:

Draw: 0, 3, 0, 0, "no", "Curve"
Sound-objekti käsu Draw... dialoogiaken.

Joonis 19.1. Sound-objekti käsu Draw... dialoogiaken.

Praati skriptis peab iga käsk olema eraldi real. Kui tunned mõnd muud levinumat skripikeelt (R, Python, Unix), siis see Praati eripära võib olla piirav, aga Praati käske üksteise sisse kombineerida ei saa.

Samas üks käsk peab olema ilma reavahetusteta ühel real. Juhul, kui käsurida läheb väga pikaks, saab seda ka mitme rea peale jagada. Kui ridu murda, peab iga järgnev rida algama kolme punktiga, nt:

To Pitch (filtered autocorrelation): 
... 0, 50, 800, 15, "no", 0.03, 0.09,
... 0.5, 0.055, 0.35, 0.14

Järgnevates peatükkides on seda võimalust kasutatud, et tekst veerule ära mahuks. Kui neid näiteid Praati skriptiaknas katsetad, võid käsud ühes reas kirjutada.

19.3 Muutujad

Skriptis saab määrata muutujaid ja anda neile väärtusi. Muutujaid oled kindlasti näinud ja kasutanud matemaatilistes valemites, kus on a ja b või x ja y või ka pi. Muutuja tähistab mingit kindlat üksust, aga selle väärtus võib muutuda. Näiteks saame muutujaga a tähistada ruudu külje pikkust ruudu ümbermõõdu valemis P = 4a.

a = 5
tulemus = 4*a
writeInfo: "Ruudu ümbermõõt on ", tulemus, "."

Muutujatel peavad olema nimed, mis ei algaks suure algustähe või numbriga (muidu võivad suuri tähti ja numbreid sisaldada küll), ei sisaldaks täpitähti (või üldisemalt mitte-ASCII sümboleid) ega tühikuid. Suure algustähega algavad skriptis ainult käsud ja muutujad ei tohi nendega sassi minna.

Muutujaid on kahte sorti: sõnalised ja arvulised. Erinevalt R-i skriptikeelest ja sarnaselt Pythonile peab Praatis sõnalistel ja numbrilistel väärtustel selget vahet tegema. Praatis peavad sõnaliste muutujate nimed lõppema dollarimärgiga $.

a = 5
tulemus = 4*a
vastus$ = "Ruudu ümbermõõt on "
yhik$ = "cm"
writeInfo: vastus$, tulemus, yhik$

19.3.1 Arvuline muutuja

Arvuliste muutujatega saab teha matemaatilisi tehteid: liitmine, lahutamine, korrutamine, jagamine, astendamine jne.

Proovi, mis juhtub, kui jooksutad sellist skripti:

muutuja1 = 5
muutuja2 = 7
muutuja3 = muutuja2 - muutuja1
muutuja4 = muutuja3 * muutuja1
writeInfoLine: "(", muutuja2, " - ", 
... muutuja1, ") * ", muutuja1, " = ", muutuja4

Proovi, mis juhtub, kui mõne muutuja väärtust muudad?

Viimasel real on käsk writeInfoLine, millele järgnev tekst kuvatakse infoaknasse tekstina. Kui teksti sees on mõni muutuja, siis see asendatakse selle muutuja väärtusega.

Juba olemasolevale muutujale saab omistada väärtuseks tehet iseendaga:

muutuja = 3
muutuja = muutuja + 1

Tulemuseks on muutuja väärtus 4. Seda muutuja väärtusele millegi liitmist või sellest millegi lahutamist saab ka natuke lühemalt kirja panna nii:

muutuja = 3
writeInfoLine: muutuja
muutuja += 3
appendInfoLine: muutuja
muutuja -= 1
appendInfoLine: muutuja
muutuja *= 2
appendInfoLine: muutuja
muutuja /= 5
appendInfoLine: muutuja

Käsk appendInfoLine on sarnane käsule writeInfoLine, ainult et kui viimane kustutab varem infoaknas olnu ära, siis appendInfoLine lisab teksti lihtsalt uuele reale. Seega kui skripti jooksutada, siis esimene writeInfoLine tühjendab varasemast infoaknas olnud teksti ja kirjutab esimese rea, järgmised appendInfoLine read lisavad uue rea, aga jätavad selle skripti piires muu teksti ka alles.

19.3.2 Sõnaline muutuja

Sõnalise muutuja puhul on matemaatilised tehted piiratud – neid saab omavahel ainult liita. Proovi:

muutuja1$ = "te"
muutuja2$ = "re"
muutuja3 = 2
muutuja4$ = muutuja1$ + muutuja2$
writeInfoLine: "Sõnas ", muutuja4$,  " on ", 
... muutuja3, " silpi: ", muutuja1$, " ja ",
... muutuja2$, "."

Mingil määral saab neid siiski ka lahutada, st ühe stringi lõpust saab lahutada sellega täpselt kattuva stringi, aga algusest või keskelt lahutada ei saa:

m1$ = "tere"
m2$ = "te"
m3$ = "re"
m4$ = m1$ - m2$
m5$ = m1$ - m3$
writeInfoLine: m1$, " - ", m2$, " = ", m4$,
... ", aga ", m1$, " - ", m3$, " = ", m5$, "."

Tekstistringi sisse saab sõnalisi muutujaid lisada ka ühekordsete jutumärkidega tähistatult. Nii saab kahe tekstistringi liitmistehte asemel neid omavahel kokku panna ka nii:

muutuja4$ = "'muutuja1$''muutuja2$'"

Sel viisil võib kombineerida ka uut teksti ja numbrilisi muutujaid sõnalistega, tulemus on sõnaline muutuja:

muutuja4$ = "'muutuja3'-silbiline 'muutuja1$''muutuja2$'"

Plussmärkidega arvulisi ja sõnalisi muutujaid kokku panna ei saa, siis tuleb enne arv tekstiks teisendada käsuga string$():

a = 2
tekst$ = "Väärtus on "
vastus$  = tekst$ + string$(a)

Muutujate väärtust saab määrata ka mõne käsu abil. Näiteks muutuja1 väärtus on Sound-objekti kestus (Sound-objekt peab objektiaknas olema valitud):

muutuja1 = Get total duration
writeInfoLine "Kestus on 'muutuja1' sekundit."

Muutujatega saab määrata dialoogiaknas küsitavaid väärtusi (jällegi peab Sound-objekt olema valitud):79

f0alumine = 75
f0ylemine = 300
To Pitch: 0, f0alumine, f0ylemine

Niisiis muutujatega saame vahetada ja kombineerida skriptis erinevaid arvulisi ja sõnalisi väärtusi. Nii saame näiteks määrata põhitooni ülemise ja alumise piiri skripti alguses ühe korra ning edasi kasutada neid väärtusi mitmete käskude parameetritena ilma väärtusi uuesti sisestamata. Kui siis on vaja sama analüüs teha mitme failiga uuesti natuke teiste parameetritega, saame neid väärtusi muuta ühes kohas ja säästame kõvasti aega. Veelgi enam, me saame ühe käsuga küsida mingit väärtust ja lisada selle väärtuse hiljem skriptis mingi teise käsu parameetriks.

19.4 Tsüklid

Tsüklite abil on võimalik ühte käsku mitu korda jooksutada. Selleks tuleb korratava(te)le käs(k)u(de)le panna ümber for-endfor konstruktsioon. Tsükli alguses for käsu järel defineeritakse muutuja, nt i ja kordade arv.

for i to 8
  appendInfoLine: "See on 'i'. kord."
endfor

Siin näites kirjutatakse infoaknasse lause 8 korda, iga kord on muutuja i väärtus ühe võrra suurem. Käsud, mida korratakse, peab jääma ridade for ja endfor vahele.

Lisaks juba kasutatud käskudele appendInfoLine on writeInfoLine on veel infoakna juhtimise käske: appendInfo, mis ei lisa trükitava teksti lõppu reavahetust, ja clearinfo, mis kustutab midagi uut trükkimata kõik infoaknast ära.

Erinevalt paljudest teistest skriptikeeltest (vähemalt R) on Praati for-tsüklites muutuja alati arvuline ja tsükli samm alati +1. Tsükkel ei pea algama ühest, misjuhul tuleb määrata lisaks algus, mis võib olla ka negatiivne, aga Praatis ei saa tsüklit teha kahanevate väärtuste suunas. Näiteks tsükkel 6 kuni 9 oleks selline:

for i from 6 to 9
 appendInfoLine: "See on 'i'. kord."
endfor

Kuna skriptis võib rea alguses olla ükskõik kui palju tühikuid, on kombeks märkida taandega (nt kaks tühikut) see, mis jääb tsükli ja muude mitut järgnevat rida hõlmavate süntaksielementide sisse. Nii on algust ja lõppu lihtsam üles leida. Ühe tsükli sees võib ka teine tsükkel olla, nii et siis võiks selle sisu olla täiendava taandega. Nii on lihtsam arvet pidada, kust üks tsükkel algab ja lõppeb. Need tühikud on ainult visuaalse selguse pärast ega mõjuta skripti tööd, nii et need võib ka panemata jätta. Näiteks siin on üks tsükkel teise sees:

clearinfo
for i to 3
  for j from 10 to 14
    appendInfoLine: "välimise tsükli i = ", i,
    ... " ja sisemise tsükli j = ", j
  endfor
endfor

19.5 Praktiline näide tsükli kasutamisest

Kuidas tsükleid Praatis praktiliselt kasutada? Näiteks on meil vaja tsüklit, kui me tahame skriptiga TextGrid-objektist infot välja saada: Praatis on käsud selleks, et ühest TextGrid’i intervallist märgendi teksti või algus- ja lõpuaegu pärida ja me saame teha tsükli, mis käib sedasi läbi kõik ühe märgenduskihi intervallid ning trükib infoaknasse teksti ja segmendi kestuse. Kõik kasutatud käsud on TextGrid-objekti dünaamilises menüüs Query.

  1. Kõigepealt küsime, mitu segmenti on TextGridi 1. kihil (menüüst Query > Query interval tier > Get number of intervals...).
  2. Siis teeme tsükli, mis kordab järgnevat tegevust nii mitu korda, kui TextGrid’il on segmente.
  3. Küsime esimese kihi i-nda segmendi nime (Query > Query interval tier > Get label of interval...).
  4. Küsime selle intervalli algusaega (segmendi alguspunkt faili alguspunkti suhtes ajateljel, Query > Query interval tier > Get start time of interval...).
  5. Küsime ka intervalli lõpuaega (Query > Query interval tier > Get end time of interval...).
  6. Kestuse millisekundites saame nii, et lahutame segmendi lõpuajast algusaja ja korrutame tuhandega – millisekund on tuhandik sekundit.

Selle skripti jooksutamiseks peab olema objektiaknas valitud TextGrid-objekt, millel on esimeseks kihiks intervallikiht.

segmendid = Get number of intervals: 1

for i to segmendid
  nimi$ = Get label of interval: 1, i
  algus = Get start time of interval: 1, i
  ots = Get end time of interval: 1, i
  kestus = (ots - algus) * 1000
  appendInfoLine: nimi$, " kestus ", round(kestus), " ms."
endfor

Käsk round() ümardab arvu lähimaks täisarvuks. Kui soovid aga täpsustada komakohtade arvu, peab kasutama käsku fixed$(), mis teeb väärtuse ühtlasi sõnaliseks:

m = 0.12345
writeInfoLine: m, " lähim täisarv on ", round(m),
... ", kolme komakohaga ", fixed$(m, 3)

19.6 Kommentaarid skriptis

Oma skripte on alati mõistlik põhjalikult kommenteerida nii enda kui ka teiste pärast: kui oled kirjutanud pika skripti, siis mõne aja pärast ei pruugi sa enam mäletada, mida ja miks see täpselt teeb. Samuti siis kui oma skripte teistega jagada, on teistel neist lihtsam aru saada, kui skript on korralikult kommenteeritud.

Kommentaarid tuleb kirjutada eraldi reale, mis algab trellide, hüüumärgi või semikooloniga (#, ! või ;), nt:

# muutuja a väärtus on ruudu külje pikkus
a = 4

Käsurea lõppu, käsuga samale reale saab Praatis ka kommentaare lisada, aga siis algab kommentaar kindlasti ainult semikooloniga (trellid või hüüumärk siin ei tööta):

a = 4; ruudu külje pikkus

19.7 Mis objektile käsk rakendub?

Praati käsud enamasti teevad midagi mingi konkreetse objektiga, näiteks küsivad TextGrid’i intervallide algus- ja lõpuaegu, teevad Sound-objektile põhitoonianalüüsi, küsivad Pitch-objekti põhitooni väärtust mingil ajahetkel. Skriptis olevad käsud rakenduvad sellele objektile, mis on objektiaknas valitud. Skriptiga saab leida andmeid mitmest objektist. Kui me muidu objektiaknas valime objekti selle peal hiirega klõpsates, siis skriptis on objekti valimiseks käsk selectObject.

Järgmises näites rakendame vaheldumisi käske mitmele objektile.

  1. Teeme objektiaknas avatud Sound-objektist isoleeritud_vokaalid Pitch-objekti.
  2. Otsime objektiaknas avatud TextGrid-objektis isoleeritud_vokaalid märgitud intervallide keskmise põhitooni.
  3. Tulemused saadame infoaknasse tabuleeritud tekstina, mille saame salvestada tabelina, kus esimeses lahtris on segmendi nimi, teises f0 väärtus. Tabulatsioonimärgi sisestamiseks kasutame Praati tabulatsioonisümboli muutujat tab$.
  4. Teeme tabelile ka päise, kus on tulpade pealkirjad. Selleks kasutame käsku writeInfo, et ühtlasi kustutada infoaknast kõik, mis seal enne oli. Edasi kasutame infoaknasse kirjutamiseks käsku appendInfo.
  5. Kuna Pitch-objekti pärast enam vaja pole, kustutame selle skripti lõpus objektiloendist ära (käsk Remove graafiliselt asub objektiakna alumises osas).
# valime objektiloendist Sound-objekti
# ja rakendame põhitoonianalüüsi käsku
selectObject: "Sound isoleeritud_vokaalid"
To Pitch (filtered autocorrelation): 0, 50, 800, 
... 15, "no", 0.03, 0.09, 0.5, 0.055, 0.35, 0.14

# Selle koha peal jääb objektiloendis valituks
# loodud Pitch-objekt.

# Valime TextGrid objekti ja küsime,
# mitu segmenti on 1. kihil
selectObject: "TextGrid isoleeritud_vokaalid"
segmendid = Get number of intervals: 1

# kirjutame infoaknasse tulemustetabeli päiserea
writeInfoLine: "Segment", tab$, "Põhitoon (Hz)"

# tsükkel, mis käib läbi TextGridi 1. kihi segmendid
for segment to segmendid
  # iga tsükliringi alguses valime TextGrid objekti, 
  # sest tsükli lõpus võib jääda valituks mingi muu objekt
  # (siin skriptis Pitch) ja siis saame veateate,
  # sest järgmist käsku, mis on TextGridile mõeldud
  # ei saa Pitch-objektile rakendada
  selectObject: "TextGrid isoleeritud_vokaalid"

  # Küsime selle tsükliringi segmendi teksti
  # ning algus- ja lõpuaega.
  nimi$ = Get label of interval: 1, segment
  algus = Get start time of interval: 1, segment
  lopp = Get end time of interval: 1, segment
  
  # valime Pitch-objekti ja küsime, 
  # mis on selle segmendi keskmine põhitoon
  selectObject: "Pitch isoleeritud_vokaalid"
  pohitoon = Get mean: algus, lopp, "Hertz"
  
  # kirjutame tulemused infoaknasse
  appendInfoLine: nimi$, tab$, pohitoon

  #tsükkel lõppeb ja valituks jääb Pitch objekt
endfor

# Kui tsükkel on läbi ja kõik segmendid läbi käidud, 
# valime Pitch-objekti ja eemaldame selle objektiloendist
selectObject: "Pitch isoleeritud_vokaalid"
Remove

Muutuja tab$ väärtus on tabulatsioonisümbol, seda on mugav kasutada väljade eristajana, niimoodi pärast põhitooniväärtused nt Excelisse kopeerides on andmed kahes tulbas: esimeses segmendi nimi, teises põhitooni väärtus.80

Käsule appendInfoLine võib järgneda üks või mitu komadega eraldatud teksti, mis infoakna real kõik üksteise otsa laotakse.

Kui on vaja, et valitud oleks korraga mitu objekti, siis selleks on käsk plusObject. Näiteks saame valida Sound- ja TextGrid-objektid ja mõlemad korraga objektiloendist eemaldada.

selectObject: "Sound isoleeritud_vokaalid"
plusObject: "TextGrid isoleeritud_vokaalid"
Remove

19.8 Tingimused

Vahel on vaja, et sõltuvalt mõne muutuja väärtusest teeks skript erinevaid asju. Selleks on hea if-endif konstruktsioon. Järgnev näide trükib infoaknasse teksti Muutuja väärtus on… ainult juhul, kui muutuja väärtus on 5.

clearinfo
muutuja = 5
if muutuja = 5
  writeInfoLine: "Muutuja väärtus on ", muutuja
endif

Proovi skriptis nüüd muutujale mingi muu väärtus anda. Kui seda jooksutada, ei juhtu mitte midagi, sest tingimus infoaknasse kirjutamiseks ei ole täidetud. Võime ka tahta, et ühel juhul teeks skript üht ja kõigil teistel juhtudel teist:

muutuja = 5
if muutuja = 5
  writeInfoLine: "Muutuja väärtus on ", muutuja
else
  writeInfoLine: "Muutuja väärtus ei vasta tingimusele."
endif

Võime veel tahta, et ühel juhul teeks skript üht, teisel juhul teist ja muudel juhtudel mitte midagi:

muutuja = 5
if muutuja = 5
  writeInfoLine: "Muutuja väärtus on ", muutuja
elsif muutuja = 6
  writeInfoLine: "Muutuja väärtus on nüüd ", muutuja
endif

If-konstruktsiooni lõpus peab alati olema rida endif.

Arvulise muutuja puhul saame tingimustena kasutada kõiki võrratuse märke:

  • a = b – muutuja a on võrdne b-ga;
  • a < b – muutuja a on väiksem kui b;
  • a > b – muutuja a on suurem kui b;
  • a <= b – a on väiksem või võrdne b-ga;
  • a >= b – a on suurem või võrdne b-ga;
  • a <> b – a ei ole võrdne b-ga (st on suurem või väiksem).

Sõnalistele muutujatele saab rakendada ainult = (on võrdne) ja <> (kõike muud kui).

Täiendame põhitooni otsimise skripti nii, et see otsiks ainult neid segmente, millel on mingi nimi (st mille nimi oleks „kõike muud kui mitte midagi“), teistest (ehk tühjadest segmentidest) hüppaks üle.

selectObject: "Sound isoleeritud_vokaalid"
To Pitch (filtered autocorrelation): 0, 50, 800, 
... 15, "no", 0.03, 0.09, 0.5, 0.055, 0.35, 0.14

selectObject: "TextGrid isoleeritud_vokaalid"
segmendid = Get number of intervals: 1

writeInfoLine: "Segment", tab$, "Põhitoon"

for segment to segmendid
  selectObject: "TextGrid isoleeritud_vokaalid"
  nimi$ = Get label of interval: 1, segment
  # lisame siia tingimuse: if-endif vahele jäävad käsud
  # rakenduvad ainult nende segmentide korral,
  # millel on märgitud mingi tekst
  if nimi$ <> ""
    algus = Get start time of interval: 1, segment
    lopp = Get end time of interval: 1, segment
    selectObject: "Pitch isoleeritud_vokaalid"
    pohitoon = Get mean: algus, lopp, "Hertz"
    appendInfoLine: nimi$, tab$, pohitoon
  endif
endfor

selectObject: "Pitch isoleeritud_vokaalid"
Remove

19.9 Tulemuste faili kirjutamine

Käsud writeInfo, writeInfoLine, appendInfo ja appendInfoLine kirjutavad teksti infoaknasse. Sealt tuleb see eraldi salvestada või kuhugi (nt Excelisse) kopeerida, kui ei taha, et see läheks kaotsi, kui Praati sulgeme.

Käsuga writeFile (ja writeFileLine, appendInfo, appendInfoLine) saab tulemuse kirjutada faili. Selleks on vaja asendada rida appendInfo: Minu tekst reaga appendFile: kataloog/failinimi, Minu tekst.

Nii infoaknasse kui ka faili kirjutamise käskude puhul on write ja append vahe selles, et write kirjutab varasema sisu üle (kustutab varasema) ja append lisab olemasolevale juurde. Näiteks siin esimene rida kirjutab üle varasema faili f0tulemused.txt sisu ja lisab teksti lõppu reavahetuse, aga teine rida lisab varasemale faili sisule teksti ilma reavahetuseta (mis on eraldi lisatud teksti lõppu muutujaga newline$).

writeFileLine: "f0tulemused.txt", "Segment", tab$, "Põhitoon"
appendFile: "f0tulemused.txt", nimi$, tab$, pohitoon, newline$

Muutuja newline$ teeb reavahetuse. Veel sarnaseid määratud muutujaid on tab$, mis trükib tabulatsiooni. Selle asemel võib muidugi tabulatsiooniklahviga sisestatavat sümbolit kasutada, aga skriptis on tab$ selgem, sest tabluatsioonimärk on „nähtamatu“ nagu tühik.

Kui käsu esimeseks argumendiks kirjutada ainult failinimi, siis kirjutatakse fail sellesse kataloogi, kus skript ise asub.

Selle asemel võime skripti asukoha määrata täisaadressiga, nt kui tahame faili saada oma kodukausta,

  • Windowsis "C:\Users\partel\Documents\f0tulemused.txt",
  • või Macis "/Users/partel/Documents/f0tulemused.txt".

Failiaadressi võib asendada ka muutujaga. Lisame kuhugi skripti algusesse rea, kus defineerime muutuja tulemusfail$:

tulemusfail$ = "C:\Users\partel\Documents\F0tulemused.txt"

Ja nüüd võib appendFile-real muuta failinime muutujaga:

appendFile: tulemusfail$, nimi$, tab$, pohitoon, newline$

19.10 Dialoogiakna lisamine skriptile

Et skripti universaalsemaks teha, saab lisada algusesse dialoogiakna, kus skriptis olulisi muutujaid defineerida. Siis avaneb skripti jooksutamise peale esimese asjana dialoogiaken, kus vastavaid parameetreid küsitakse, ega pea skripti ennast muutma. Näiteks võiks lisada eelmistes näidetes kasutatud põhitoonileidmise skriptile dialoogiakna, kus küsitakse failinime ja põhitooni piire.

Dialoogiakna algust märgib form Akna pealkiri ja lõppu endform. Nende vahel on dialoogiakna sisu. Iga rida koosneb kolmest elemendist: välja tüüp (tekst, arv, valik), muutuja nime ja vaikimisi väärtust. Näiteks kui küsime failinime (õieti objekti nime, mis on failinimi ilma laiendita), mis on sõnaline muutuja, oleks dialoogiakna vorm skriptis selline:

form Keskmine põhitoon
  sentence Failinimi Vokaalid_isol
endform

NB! Siin on muutuja nimi ilma $-märgita ja võib olla suure algustähega. Hiljem skriptis tuleb kasutada sama muutujat kujul failinimi$. Kui sõnavahedesse panna alakriips, näeb dialoogiaknas tühikut, aga muutujanimes on hiljem alakriips. Kui liikuda erinevate objektide vahel, millele enne andsime argumendiks lihtsalt tekstina objekti täisnime, siis nüüd tuleb see panna kokku failinimest ja objektitüüp jääb sinna ette endiselt lihtsalt tekstina: selectObject: "TextGrid " + failinimi$.

Põhitooni väärtused on positiivsed reaalarvud, nii peaks rida olema positive F0_alumine_piir_(Hz) 75.

Dialoogiaknas saab lisada muutujanimele sulgudes kommentaare, mis on dialoogiaknas nähtavad, aga hiljem muutujanimme ei jää. Siin jääb edaspidi muutujanimest _(Hz) ära ja muutuja on kujul f0_alumine_piir.

form Keskmine põhitoon
  sentence Failinimi Vokaalid_isol
  positive F0_alumine_piir_(Hz) 75
  positive F0_ylemine_piir_(Hz) 500
endform

# kuna nüüd on failinimi$, f0_alumine_piir ja
# f0_ylemine_piir väärtused dialoogiaknas,
# tuleks varasemal kujul read kustutada, muidu
# dialoogiaknas  määratud väärtused kirjutatakse
# üle ja dialoogiaken kaotab mõtte.

selectObject: "Sound " + failinimi$
To Pitch (filtered autocorrelation): 0,
... f0_alumine_piir, f0_ylemine_piir,
... 15, "no", 0.03, 0.09, 0.5, 0.055, 0.35, 0.14

selectObject: "TextGrid " + failinimi$
segmendid = Get number of intervals: 1
writeInfoLine: "Segment", tab$, "Põhitoon"
for segment to segmendid
  selectObject: "TextGrid " + failinimi$
  nimi$ = Get label of interval: 1, segment
  if nimi$ <> ""
    algus = Get start time of interval: 1, segment
    lopp = Get end time of interval: 1, segment
    selectObject: "Pitch " + failinimi$
    pohitoon = Get mean: algus, lopp, "Hertz"
    appendInfoLine: nimi$, tab$, round(pohitoon)
  endif
endfor
selectObject: "Pitch " + failinimi$
Remove

Dialoogiaknas saab määrata, missuguseid väärtusi muutujatele saab anda. Arvuliste muutujate välja tüübid on:

  • real (reaalarvud),
  • positive (ainult positiivsed reaalarvud; negatiivne väärtus annab veateate),
  • integer (ainult täisarvud; komakohad ümardatakse),
  • natural (ainult positiivsed täisarvud).

Sõnalised muutujad võivad olla:

  • word (üks sõna),
  • sentence (tekst),
  • text (pikem tekst, muutuja nime ei kuvata ja väli on selle võrra suurem).

Etteantud väärtustega muutujad:

  • boolean (linnukesega kast – kui on tühi, on väärtus 0, kui linnuke märgitud, siis 1),
  • choice ja button (valik, mille nuppudel on väärtused alates 1-st).

Kui dialoogiaknas tahta mingit selgitavat teksti esitada, siis comment reale kuvatakse ainult järgnev kommentaari tekst. Väärtused, mis on skriptis, on vaikimisi väärtused.

Siin on väike näide erinevate väljatüüpidega dialoogiaknast:

form test
  comment Palun sisesta parameetrid
  real Reaalarv -0.5
  positive Positiivne_reaalarv 1.5
  integer Taisarv -10
  natural Positiivne_taisarv 2
  word Sona_(ilma_tühikuteta_string) kana
  sentence Lause Põhjatuul ja päike.
  text Tekst Ükskord vaidlesid põhjatuul ja päike selle üle...
  boolean Linnuke_(kas_on_või_pole) 0
  choice Valik 2
  button a
  button b
  button c
endform
writeInfoLine: reaalarv, tab$, positiivne_reaalarv, tab$,
# taisarv, tab$, positiivne_taisarv
appendInfoLine: sona$, newline$, lause$, newline$, tekst$
appendInfoLine: linnuke, tab$, valik

19.11 Valmis skript

Siin on nüüd kokku pandud skript, mille alguses me ütleme, millist faili (objektiaknas olevatest) kasutame, mis on põhitoonianalüüsi piirid ja kuhu faili tulemused kirjutame. Tulemuste faili kirjutame segmendi nime, kestuse (millisekundites) ja põhitooni (hertsides).

# Form peab olema skripti päris alguses, sellele võib
# eelneda kommentaare, aga ei saa olla mingeid käsuridu.
# Skripti algusesse on hea lisada paar kommentaaririda
# selle kohta, mida skript üldiselt teeb:
# See skript leiab textgridil märgendatud segmentide
# kestuse ja põhitooni. Sound ja TextGrid peavad olema
# avatud, tulemused kirjutatakse faili. Kui fail on juba
# olemas, kirjutatakse see üle ja varasem sisu läheb kaotsi.
# Skripti kirjutas Pärtel Lippus, viimati muudetud 15. juuli 2024.
# Skripti võib kasutada CC BY litsentsi alusel.

form Keskmine põhitoon
  comment Sound ja TextGrid peavad olema objektiloendis
  sentence Failinimi Vokaalid_isol
  comment Määra põhitoonianalüüsi piirid
  positive F0_alumine_piir_(Hz) 75
  positive F0_ylemine_piir_(Hz) 500
  comment Täisaadress, kuhu tulemused salvestatakse:
  sentence Tulemusfail C:\Users\partel\Documents\F0tulemused.txt
endform

selectObject: "Sound " + failinimi$
  To Pitch (filtered autocorrelation): 0, 
... f0_alumine_piir, f0_ylemine_piir, 
... 15, "no", 0.03, 0.09, 0.5, 0.055, 0.35, 0.14

selectObject: "TextGrid " + failinimi$
segmendid = Get number of intervals: 1

# tulemuste faili päis
writeFile: tulemusfail$, "Segment", tab$, "Kestus", 
... tab$, "Põhitoon", newline$

# tsükkel, mis käib läbi kõik TextGridi 1. kihi segmendid
for segment to segmendid
  selectObject: "TextGrid " + failinimi$
  nimi$ = Get label of interval: 1, segment

  # kui segmendi tekst ei ole tühi
  if nimi$ <> ""
    algus = Get start time of interval: 1, segment
    lopp = Get end time of interval: 1, segment
    # arvutame kestuse: lõpuaeg - algusaeg annab kestuse
    # sekundites, korrutame tulemuse 1000-ga, 
    # et teisendada millisekunditesse
    kestus = (lopp - algus) * 1000
    selectObject: "Pitch " + failinimi$
    pohitoon = Get mean: algus, lopp, "Hertz"
    appendFile: tulemusfail$, nimi$, tab$, kestus,
    ... tab$, pohitoon, newline$
  endif
endfor

# tsükkel on läbi, eemaldame Pitch-objekti
selectObject: "Pitch " + failinimi$
Remove

19.12 Muudatused Praati skriptikeele süntaksis

Praati skriptikeele süntaks tegi aastatel 2013–2014 läbi mõningase muutuse. Siin õpikus on läbivalt üritatud kasutada uuemat süntaksit, aga kuna repositooriumites leidub skripte eri aegadest, siis on hea natuke teada, kuidas vanem süntaks välja näeb, et vanu skripte vajaduse korral kasutada ja modifitseerida. Õnneks uuemates Praati versioonides töötavad nii vanad käsud kui ka vana skriptisüntaks endiselt. Ka ühe skripti piires võib läbisegi kasutada nii uut kui ka vana süntaksit.

Varasemas Praati skriptikeeles lõppesid käsud kolme punktiga samamoodi nagu menüüdes. Kolmele punktile järgnesid argumendid ilma komadeta ja sõnalised väärtused ilma jutumärkideta. Näiteks kui soovime TextGridi esimese kihi viienda segmendi tekstiks märkida sõna maja, siis uuema süntaksiga käiks see nii:

Set interval text: 1, 5, "maja"

Vanema süntaksiga oleks sama käsurida selline:

Set interval text... 1 5 maja

Et muutuja eristuks tavalisest tekstist, tuli see vanemas süntaksis tähistada ülakomadega:

segm = 5
Set interval text... 1 'segm' maja

Vanema süntaksi puuduseks on see, et tekst võib ka sisaldada tühikuid ja kui rea lõpus on käsureal tühikuid, on neid raske märgata (nt "maja" vs. "maja ") ja kergesti tulevad sisse näpuvead, mida on raske üles leida. Samuti on tülikas ja kohati ebaloogiline, et tavaline tekst on markeerimata (ilma jutumärkideta) ja muutujad tuleb jutumärkidega tähistada.

Üürikest aega 2013/2014 (alates 7. aprillist 2013 versioonist 5.3.44) oli kasutusel ka üleminekusüntaks, kus käsurida alustas do või do$ sõltuvalt sellest, kas käsu tulemiks oli arvuline või sõnaline väärtus, käsk ise ja argumendid järgnesid sellele sulgudes ja olid eristatud komadega:

segm = do("Get interval at time...", 3, 0.5)
sona$ = do$("Get label of interval...", 1, segm)

Praegune süntaks, kus käsk lõppeb koolongiga ja sellele järgnevad argumentide väärtused, mis on eristatud komadega, on Praatis kasutusel alates versioonist 5.3.63 (24. jaanuar 2014). Kõik kolm varianti Praati skriptisüntaksist töötavad siiski edasi, nii et vanu skripte uuemas Praati versioonis jooksutamiseks ümber kirjutama ei pea.

Samuti on aja jooksul toimunud muudatusi Praati käskudes. Mõningaid käske on lihtsalt korrastamise ja parema selguse eesmärgil ümber nimetatud (nt TextGrid-objekti intervalli algusaja pärimise käsk oli varem Get start point... ja on praegu Get start time of interval...), teisi on asendatud sisuliste muudatuste tõttu (nt põhitoonianalüüsi käsu To Pitch... on alates versioonist 6.4 (15. november 2023) asendanud To Pitch (filtered autocorrelation)...). Et vanad skriptid oleksid endiselt kasutatavad, on ka kõik vanad käsud Praatis alles, nad on lihtsalt graafilise kasutajaliidese menüüdes peidetud. Täielikku nimekirja kõigist käskudest saab vaadata ning peidetud käske menüüdes nähtavaks teha Praat > Settings > Buttons....


  1. https://lennes.github.io/spect/↩︎

  2. Et see skriptis pärast esimese korraga ilma täiendava skripti toimetamiseta töötaks, siis sama käsuga mitme joone või skaala lisamiseks ära kasuta Apply nuppu, vaid vali iga teksti või joone lisamiseks uuesti menüüst käsk ning vajuta OK.↩︎

  3. Siin on kasutatud vanemat põhitoonianalüüsi käsku To Pitch..., mis menüüs on peidetud ja mis 2023. aastast on asendatud käsuga To Pitch (filtered autocorrelation)..., millel on aga palju rohkem argumente.↩︎

  4. Andmeid Excelisse viies pane tähele, et Eesti-lokatsiooniga Excelis on komakoha eristaja koma, aga Praatis on selleks punkt. Punkti sisaldavaid arve võib Excel aga tõlgendada kuupäevana (nt väärtus 1.02 teisendatakse automaatselt väärtuseks 01.Feb).↩︎