Peatükk 20 Praati skriptid edasijõudnutele

Siin peatükis tutvume keerukamate Praati skriptielementidega, mis ei anna kokku ühte tervikuna töötavat skripti, nagu eelmises peatükis 19, aga eelmise peatüki skripti saab siin esitatavate funktsioonidega täiendada. See peatükk sobib edasijõudnud skriptijatele. Siiski ei anna see ammendavat ülevaadet Praati rikkalikust skriptikeelest, mida iga kasutaja võib Praati enda võrdlemisi hea manuaali põhjal edasi avastada.

20.1 Tingimused ja võrratused

20.1.1 Tingimuste kombineerimine

Ühte if-endif konstruktsiooni saab kokku panna mitu tingimust:

  1. peab vastama kahele tingimusele
if a=1 and b=2
  # käsud, mis tingimuste korral rakendatakse
endif
  1. peab vastama ühele kahest tingimusest
if a=1 or b=2
  # käsud, mis tingimuste korral rakendatakse
endif
  1. peab vastama ühele, aga mitte teisele tingimusele
if a=1 not b=2
  # käsud, mis tingimuste korral rakendatakse
endif

Tingimusi võib muidugi olla ka rohkem kui kaks ja neid saab sulgudega rühmitada:

  1. peab vastama ühele tingimusele või teisele kahe tingimuse kombinatsioonile
if a=1 or (a=4 and b=2)
  # kas [a on 1 ja b väärtust ei vaadata]
  # või [a on 4 ja b on 2]
endif
  1. peab vastama kahele tingimusele, millest üks on kahe alternatiiviga
if (a=1 or a=4) and b=2 
  # a võib olla 1 või 4, b on 2
endif

Kui ühel muutujal võib olla kaks võimalikku sobivat väärtust, siis peab need eraldi tingimustena välja kirjutama:

# see annab veateate:
if a = 1, 3 or 6
# õige on nii:
if a = 1 or a = 3 or a = 6
endif

20.1.2 Sõnaliste muutujate võrdlemine

Eelmises peatükis 19.8 on öeldud, et sõnaliste muutujate puhul saab ainult küsida, kas on võrdne (näiteks "a" = "a") või mittevõrdne ("a" <> "b"). Lisaks sellele on siiski mõningaid võimalusi veel: saab küsida, kas üks tekstistring on suurem või väiksem kui teine, aga suurust mõõdetakse tähemärkide tähestikulises järjekorras. Reeglid on sellised:

  1. mida kaugemal tähestikus täht asub, seda suurem ("a" < "b"),
  2. väiketähed on suuremad kui suurtähed ("A" < "a"),
  3. pikem string on suurem, kui lühemal ei ole tähestikus kaugemal paiknevat osa ("a" < "aa" ja "a" < "ab", aga "c" > "ab"),
  4. numbrimärgid on tähtedest väiksemad ("1" < "a").

20.1.3 Tingimused valemis

Üldiselt tuleb Praati skriptis kõik käsud kirjutada eraldi ridadele. Väike erand on valemid, mis võivad sisaldada ka tingimusi. Üldjuhul oleks tingimus esimesel real, siis käsuread, mis tingimuse täitmise korral rakenduvad, seejärel else eraldi real, sellele järgnevad käsuread, mis rakenduvad esimese rea tingimuse mittetäitmisel (ehk muudel juhtudel), ning lõpuks rida endif, näiteks kui muutuja x väärtus on 1, siis m väärtus on 15, kõikide muude x vääruste korral on m väärtus 30:

if x=1
  m = 3*5
else
  m = 3*10
endif

Sellise tingimuslause saab kirjutada ka üherealise valemina:

m = 3 * if x=1 then 5 else 10 fi

Sellist lühikest tingimuslauset if TINGIMUS then VÄÄRTUS else VÄÄRTUS fi saab kasutada ainult ühe tingimuse ja ühe alternatiivse väärtuse korral. Siia ei saa lisada elsif tingimust. Kui tingimusi on mitu, siis peab ikkagi pikalt eraldi ridadega kirjutama, näiteks:

if x < 1
  m = 3*2.5
elsif x = 1
  m = 3*5
elsif x > 1 and x < 10 
  m = 3*7.5
else
  m = 3*10
endif

Ent igal juhul, kui valem sisaldab käske, siis lühemat süntaksit kasutada ei saa, sest käsk peab ikkagi alati olema omaette real. Näiteks kui tahame, et sõltuvalt i väärtusest rakendatakse kas algus- või lõpuaja leidmise käsku:

for i to 2
  if i = 1 
    aeg_'i' = Get start time of interval: 1, 1 
  else 
    aeg_'i' = Get end time of interval: 1, 1
  endif
endfor

kestus = aeg_2 - aeg_1

Kuigi paljudes teistes skriptikeeltes on võimalik käske üksteise sisse istutada ja käske valemitega kombineerida, ei ole kahjuks Praati skriptikeeles see võimalik ja nt selline käsurida annab veateate:

# See annab veateate!
dur = (Get end time of interval: 1, 1) – 
... (Get start time of interval: 1, 1) * 1000

Seega tuleb see lahti kirjutada vähemalt kolme reaga nii, et iga käsk on eraldi real ning seejärel saame teha tehte käsu väljundiks olnud väärtustega.

end = Get end time of interval: 1, 1
start = Get start time of interval: 1, 1
dur = (end-start)*1000

20.2 Tehted arvuliste muutujatega

Märgid:

  • liitmine +
  • lahutamine -
  • korrutamine *
  • jagamine /
  • astendamine ^

Kui on tarvis ruutjuurt, siis selle asemel saab astendada astmele 1/2: nt 3 ruudus ehk 3 astmel 2 on 3^2; ruutjuure saame sama märgiga: ruutjuur üheksast = 9^(1/2).81

Jagatise täisarvulise poole annab div. Jagatise jäägi annab mod. Näiteks:

a = 99
b = 24
c = a div b
d = a mod b
writeInfoLine: "'b' mahub 'a' sisse 'c' korda, aga 'd' jääb üle."

Juhuslike arvude genereerimine

Käsk randomInteger annab ühe juhusliku täisarvu määratud vahemikust (miinimum, maksimum):

juhuslik_arv = randomInteger(-10,10)

Käsk randomUniform annab ühe juhusliku murdarvu määratud vahemikust (miinimum, maksimum):

juhuslik_arv = randomUniform (-1,10)

20.3 Stringi funktsioonid

Siin vaatame funktsioone, millega tekstist otsinguid teha. See, kus seda Praatis praktiliselt vaja läheb, on näiteks siis, kui TextGrid’il on ühele kihile märgitud sõnad ja tahame sealt leida sõnu, mis algavad või sisaldavad mingit kindlat tähekombinatsiooni.

20.3.1 Üksühesed vastavused

Olgu meil näiteks sõna „kellanöörike“ (et oleks piisavalt pikk ja sisaldaks korduvaid tähemärgijärgnevusi), kust hakkame mingeid märgijadasid otsima, nimetame selle muutujaks a$. Siin alapeatükis on selguse mõttes esitatud käsuridade järel valge taustaga tekstikastis ka selle väljund.

Tekstiosa stringi algusest. left$("tekst", n) leiab teksti algusest n esimest tähemärki. Nt muutuja a$ vasakult kaks esimest tähemärki (kellanöörike):

a$ = "kellanöörike"
b$ = left$ (a$, 2)
appendInfoLine: a$, " esimesed kaks tähte on ", b$
## kellanöörike esimesed kaks tähte on ke

Tekstiosa stringi lõpust. Täpselt samamoodi töötab right$("tekst", n) teksti lõpust. Leiame muutuja a$ paremalt kaks esimest tähemärki (kellanöörike):

a$ = "kellanöörike"
c$ = right$ (a$, 2)
appendInfoLine: a$, " viimased kaks tähte on ", c$
## kellanöörike viimased kaks tähte on ke

Tekstiosa stringi keskelt. Alates a$ kuuendast märgist kolm märki (kellanöörike; esimene number tähistab märki alates millest, teine number mitu märki):

a$ = "kellanöörike"
d$ = mid$ (a$, 6, 3)
appendInfoLine: a$, " kuues kuni kaheksas täht on ", d$
## kellanöörike kuues kuni kaheksas täht on nöö

Mitmendast tähemärgist algab? Funktsioon index("abcd", "ab") otsib esimesest stringist teist stringi algust, ehk mitmendast tähemärgist see algab esimest korda. Kui teise stringiga kattuvat osa esimeses ei ole, siis on vastus 0 (aga vastus siin näites on 6):

a$ = "kellanöörike"
d$ = mid$ (a$, 6,3)
f = index (a$,d$)
appendInfoLine: "sõnas ", a$, " algab ", d$, " ", f, ". tähest"
## sõnas kellanöörike algab nöö 6. tähest

Funktsioon rindex() otsib tekstis tekstiosa viimase (ehk paremalt esimese) esinemise algust (vastus on 11):

"a$ = "kellanöörike"
c$ = right$ (a$, 2)
g = rindex (a$,c$)
appendInfoLine: "sõnas ", a$, " viimane ", c$, " ", g, ". tähest"
## sõnas kellanöörike viimane ke 11. tähest

Järgmised käsud startsWith ja endsWith otsivad, kas string a$ algab stringiga c$ ja lõppeb stringiga b$ (ehk siin siis kontrollib, kas algus ja lõpp on samasugused). Vastus esitatakse arvulisena, kuid on tõlgendatav tõeväärtusena ehk 1 = TÕENE, 0 = VÄÄR.

a$ = "kellanöörike"
b$ = left$ (a$, 2)
c$ = right$ (a$, 2)
h = startsWith (a$,c$)
i = endsWith (a$, b$)
if h = 1 and i = 1
  appendInfoLine: a$, " algus ja lõpp on mõlemad ", c$
elsif h = 0 and i = 0
  appendInfoLine: a$, " algus ja lõpp on erinevad"
endif
## kellanöörike algus ja lõpp on mõlemad ke

Asendused. Funktsioon replace$(algstring$, osaMidaVahetame$, millegaVahetame$, mitmelKorral) asendab tekstis ühe või mitu tähemärki mingi muu tekstistringiga. Kui asenduste arvu tähistav viimane argument on 0, siis toimub vahetus kõigil kordadel. Kui asenduste arvuks seada näiteks 2, siis asendatakse (kuni) kaks esimest esinemisjuhtumit ja ülejäänud otsitava esinemised jäävad asendamata. Vahetame sõnas kellanöörike ühe (esimese) ke-järjendi -ga:

a$ = "kellanöörike"
b$ = left$ (a$, 2)
j$ = replace$ (a$, b$, "võ", 1)
appendInfoLine: j$, " on, kui 1. silp ", b$, " vahetada ära võ-ga"
## võllanöörike on, kui 1. silp ke vahetada ära võ-ga

Tekstistringi (muutuja) pikkus tähemärkides. legth("tekst") annab stringi pikkuse tähemärkides:

a$ = "kellanöörike"
o = length (a$)
printline 'o' tähte on 'a$' kokku
## 12 tähte on kellanöörike kokku

20.3.2 Otsingud ja asendused regulaaravaldistega

Regulaaravaldistega saab otsingut üldistada ja painlikumaks teha. Kui sa pole varem regulaaravaldistega otseselt kokku puutunud, siis tõenäoliselt oled näiteks Windowsi failisüsteemi otsingus näinud võimalust otsida kõiki mingit tüüpi faile, nt Wordi dokumente nii, et paned otsingusse „*.docx“. See tärn tähistab seal otsingus „ükskõik milliseid tähemärke“.

Regulaaravaldisi saab kasutada paljudes erinevates programmeerimiskeeltes ja üldjoontes toimivad nad Praatis ka samamoodi nagu muudes keeltes. Mõned regulaaravaldise süntaksi elemendid:

  • . (punkt) tähistab ükskõik mis sümbolit. Nt ".a" vasteteks on „ma“, „sa“, „ta“, „ka“ jne;
  • ? (küsimärk) tähistab eelneva sümboli kordumist null korda või üks kord. Nt "tal?" vasteteks on „ta“ ja „tal“, aga mitte „tall“;
  • + (pluss) tähistab eelneva sümboli kordumist üks või enam korda. Nt "ma+" vasteteks on „ma“ ja „maa“, aga mitte „m“;
  • * (tärn) tähistab eelneva sümboli kordumist null või enam korda. Nt "sam*" vasteteks on „sa“ ja „samm“, aga ka „sammmmmmmm“, kui selline sõna peaks esinema;
  • {x} (loogelised sulud) tähistab eelneva sümboli kordumist x korda. Näiteks "a{2}" vasteks on „aa“;
  • {x,y} tähistab eelneva sümboli kordumist x kuni y korda. Näiteks "a{1,2}" vasteteks on „a“ ja „aa“;
  • [] (nurksulud) – esineb üks nurksulgudes olevatest sümbolitest, nt "[vk]ana" vasteteks on „vana“ ja „kana“;
  • \ (längkriips) tõlgendab järgnevat sümbolit sümbolina, kui sama sümbol võiks muidu tähistada mingit regulaaravaldise süntaksi elementi. Seda peab kasutama siis, kui otsida näiteks foneetikakorpusest liitsõnu, sest korpuse sõnatasandi märgenduses on liitsõnapiir tähitatud plussmärgiga, nt "keele\+teadus"82 või SAMPA transkriptsiooni /æ/ vastet {, nt sõna täna "t\{nA".83 Längkriipsu sümbol on eesti klaviatuuril klahvikombinatsioonis [AltGr] ja [+].

Tekstiosa otsing regulaaravaldisega. Käsk index_regex otsib ühest stringist teise stringi algust (samamoodi kui lihtsalt index, aga otsitavat stringi tõlgendatakse regulaaravaldise, mitte üksühese tähemärgijärjendina). Näiteks:

a$ = "kellanöörike"
k = index_regex (a$, "ö..k")
appendInfoLine: k, ". tähemärgist algab ö..k"
## 8. tähemärgist algab ö..k

Regulaaravaldisele "ö..k" vastab sõnas kellanöörike järjend alates teisest ö-st, sest punkt tähistab ühte täpsustamata tähemärki ja seega kaks punkti on kaks täpsustamata tähemärki, mille vasteks siin sõnas on siis tähemärgid r ja i.

Ja sama asi viimase kattumise kohta (mis annab sama vastuse, sest otsingule sobivat tähemärgijada esineb sõnas ainult ühe korra).

a$ = "kellanöörike"
l = rindex_regex (a$, "ö.*k")
appendInfoLine: l, ". tähest algab mingi ö.*k viimast korda"
## 8. tähest algab mingi ö.*k viimast korda

Tekstiasendused regulaaravaldistega. Asendame stringis a$ alates a-st kuni i-ni, kui nende vahel on ükskõik mitu tähte, u-ga. Viimane arvuline argument tähistab seda, et kui stringis a$ oleks seda kombinatsiooni rohkem kui korra, siis mitmel juhul (vasakult lugedes) asendus tehakse, ning väärtus 0 tähendab, et kõigil kordadel.

a$ = "kellanöörike"
m$ = replace_regex$ (a$, "a.*i", "u", 0)
appendInfoLine: "'m$' on, kui sõnas 'a$' a.*i vahetada u vastu"
## kelluke on, kui sõnas kellanöörike a.*i vahetada u vastu

Paneme esimesed viis tähte topelt:

a$ = "kellanöörike"
n$ = replace_regex$ (a$, ".", "&&", 5)
appendInfoLine: n$, " on esimesed 5 tähte topelt"
## kkeellllaanöörike on esimesed 5 tähte topelt

20.4 Peatused ja väljumine

20.4.1 Skripti peatamine

Kui mingitel ebasobivatel tingimustel ei saa skripti edasi jooksutada (nt objektiloendis peaksid olema valitud mingid failid, mida ei ole Praatis avatud, või TextGrid’il on mingi tasandi märgendus puudu, mille olemasolu skript eeldab), saab skripti seisma panna käsuga exitScript. Nt:

# küsime 2. märgenduskihi nime
kiht2$ = Get tier name: 2
# kui see ei ole häälikud, peatame skripti
if not kiht2$ = "häälikud"
  exitScript: "Teisel kihil pole häälikud!"
endif
# Kui skript ei ole katkestatud, oli kihi nimi "häälikud"
writeInfoLine: "Teise kihi nimi on häälikud."

Juhul kui teise märgenduskihi nimi ei ole „häälikud“, lõppeb skript ja ette visatakse dialoogiaken sõnumiga „Teisel kihil pole häälikud!“. Kui kihte on vähemalt 2 ja 2. kihi nimi on häälikud, jookseb skript lõpuni ja infoaknasse trükitakse tekst, et 2. kihi nimi oli „häälikud“. Juhul kui aga TextGrid’il polegi 2. kihti, ei jõua skript esimesest reast kaugemale ja lõpetab veateatega.

Käsuga pauseScript saab skriptis teha pausi, et seda kas jätkata või katkestada. Ekraanile ilmub dialoogiaken, kus on nupud Continue ja Stop. Kui skriptis käsule pauseScript järgneb mingi tekst, siis see kuvatakse ka dialoogiaknasse, nt:

kiht2$ = Get tier name: 2
if not kiht2$ = "häälikud"
  exitScript: "Teisel kihil pole häälikud!"
endif
pauseScript: "Kiht on olemas. Kas soovid jätkata?"
### ... muud käsud

Pausi on kindlasti mõistlik kasutada skripti kirjutades tsüklite puhul, mille käitumises päris kindel ei ole. Nii saab peatada skripti, kuhu on näiteks kogemata sattunud igikestev tsükkel. Saab aga ka näiteks teha pausi, et midagi vahepeal salvestada, teha objektile mõni muudatus käsitsi vms.

20.4.2 Paus dialoogiaknaga

Vahel on vaja skriptis mõnda muutujat määrata skripti keskel, aga selleks oleks vaja dialoogiakent, mille kaudu uusi väärtuseid sisestada. Peatükis 19.10 lisasime skriptile dialoogiakna form-endform konstruktsiooniga, kuid sellega saab dialoogiakent esile kutsuda ainult skripti alguses. Kui dialoogiaken peab ilmuma rohkem kui ühe korra või mitte kohe skripti alguses, saab kasutada beginPause-endPause konstruktsiooni, mis üldiselt töötab samadel põhimõtetel nagu form-endform. Näiteks saab niimoodi käsitsi kontrollida skriptiga saadud põhitooniväärtusi: skript käib tsükliga läbi TextGrid-objektil märgitud intervallid, leiab intervalli keskmise põhitooniväärtuse ning teeb pausi, visates ette dialoogiakna, kus on see põhitooniväärtus kirjas. Nüüd saab kasutaja teha lahti SoundEditor’i, kontrollida, kas põhitooniväärtus on õige, vajadusel seda korrigeerida ja skripti jooksutamist jätkata. Jätkates kirjutatakse väärtus infoaknasse.

fail$ = "minufaili"
for i to nSegm
  selectObject: "TextGrid " + fail$
  segm$ = Get label of interval: 1, i
  alg = Get start point: 1, i
  lop = Get end point: 1, i
  selectObject: "Pitch " + fail$
  f0_vaartus = Get mean: alg, lop, "Hertz"
  beginPause: "kontrolli F0 väärtust"
    comment: "kontrolli põhitooni siin segmendis"
    word: "segm", segm$
    positive: "f0_vaartus", f0vaartus
    comment: "Jätkamiseks vajuta nuppu Edasi"
  endPause: "Edasi", 1
  appendInfoLine: segm$, tab$, f0_vaartus
endfor

Pausi lõpu jaoks saab ka mitu erinevat nuppu lisada ja eri nupuvajutustele erinevad jätkamistingimused lisada. Näiteks nupud „OK“ ja „Eemalda“, nii et esimesele vajutades läheb tulemus tabelisse kirja, aga teise puhul mitte:

# muud käsud
  beginPause: "kontrolli F0 väärtust"
    comment: "kontrolli põhitooni siin segmendis"
    word: "segm", segm$
    positive: "f0_vaartus", f0vaartus
    comment: "Jätkamiseks vajuta Edasi"
  clicked = endPause: "OK", "Eemalda", 1
  if cliked = 1
    appendInfoLine: segm$, tab$, f0_vaartus
  endif
endfor

20.5 Tsüklid, protseduurid, hüpped

Siia alajaotusesse on pandud kokku Praati skriptielemendid, mis rikuvad skripti lineaarset kulgu.

Peatükis 19.4 oli juttu for-tsüklitest, mis kordavad tegevust kindlas ette antud vahemikus ja liiguvad edasi üheväärtuseliste sammudega. Siin tutvume tsüklitega, mis on kombineeritud tingimustega: repeat- ja while-tsüklid on pisut erineva loogikaga, aga mõlemad kordavad tsüklit seni, kuni mingid tingimused kehtivad.

Protseduurid võimaldavad samuti mingit skripti osa korrata, aga natuke lõdvemalt määratletud struktuuriga. Kui oled R-i süntaksiga tuttav, siis protseduur on nagu R-i funktsioon. Lõpuks vaatame hüppeid, mis võimaldavad vabalt skriptis edasi või tagasi liikuda.

20.5.1 repeat-tsüklid

Lisaks tsüklitele, millega saab midagi teha mingi arv kordi, on tsüklid, millega saab korrata midagi seni, kuni tulemus vastab mingitele tingimustele. Näiteks järgmine skriptike simuleerib täringuviskeid kahe täringuga. Vaatame, mitme viskega tuleb 12 silma:

visked = 0
repeat
   silmad = randomInteger (1,6) + randomInteger (1,6)
   visked +=  1
until silmad = 12
appendInfoLine: "Mul läks ", visked, " viset, et saada 12 silma."
## Mul läks 25 viset, et saada 12 silma.

Käsk randomInteger (1,6) genereerib ühe juhusliku täisarvu vahemikus 1–6. Käske korratakse seni, kuni saadakse sobiv tulemus. Muutuja visked loeb kokku, mitu korda tsüklit korratakse (enne alustamist on väärtus 0, iga kord liidetakse 1).

Praktilisem näide sellise tsükli kasutamisest oleks näiteks siis, kui TextGrid’ile on märgitud häälikute piirid ja on vaja mõõta näiteks f0 väärtust vokaali algusest, aga helilisus algab mõnel segmendil natuke hiljem või lõppeb natuke hiljem, kui on segmendi piir. Näiteks:

repeat
  pohitoon = Get value at time: haalikuAlgus, "Hertz", "Linear"
  haalikuAlgus += 0.001
until pohitoon <> undefined

Siin liigutakse hääliku alguspunktist ühe millisekundi võrra edasi seni, kuni põhitooni väärtus on midagi muud kui undefined (teadmata väärtus, mis näiteks R-is on NA; on arvuline väärtus). Sellega peab aga ettevaatlik olema, sest kui häälik on helitu, siis liigub skript kuni järgmise helilise hääliku alguseni. Selle pärast võiks ka täpsustada tsükli lõputingimust, et ei liigutaks hääliku piiridest kaugemale, nt

until pohitoon <> undefined or haalikuAlgus >= haalikuLopp

Ettevaatust! Enne repeat-tsükli käivitamist peab olema kindel, et tingimustele sobiv väärtus ka leidub, muidu ei välju Praat sellest tsüklist kunagi ja kui tsükli sees on ka näiteks käsk kirjutada midagi infoaknasse, võib juhtuda, et seda ei saa peatada muidu, kui pead Praati jõuga sulgema või lausa arvutile restardi tegema. Mõistlik oleks until rea ette skripti testimise ajaks panna üks paus, mis peatub igal tsükli jooksmise korral, ja kui oled juba kindel, et kõik töötab, nagu peab, siis paus ära koristada:

repeat
  pohitoon = Get value at time: haalikuAlgus, "Hertz", "Linear"
  pauseScript: "Punktis ",haalikuAlgus, " f0 = ", pohitoon
  haalikuAlgus += 0.001
until pohitoon <> undefined

Siis saab skripti valutult katkestada, kui tundub, et kuskil on mingi viga.

20.5.2 while-tsüklid

Peale for- ja repeat-tsüklite on veel while-tsüklid. Need kordavad mingit toimingut seni, kuni saadakse tingimustele mittesobiv vastus. Näiteks:

clearinfo
x = 0
while x < 10
  appendInfoLine: "x = ", x
  x += 1
endwhile

Sellest näites töötab see tsükkel küll täpselt sama moodi kui for-tsükkel, olles pealegi kohmakam. See, mis siin on viiel real, saaks for-tsüklis hakkama kolme reaga:

for x to 9
  appendInfoLine: "x = ", x
endfor

Seda on aga mõtet kasutada siis, kui muutuja väärtus on lähteseisus teadmata ja pole ka teada, mismoodi ta väärtused järgnevatel juhtudel muutuvad, näiteks juhuslike arvudega:

clearinfo
x = randomInteger (0,10)
while x > 0
  appendInfoLine: "x = ", x
  x = randomInteger (0,10)
endwhile

ETTEVAATUST! Nagu repeat-tsükli puhul, peab tingimustele sobiv väärtus muutuma nii, et millalgi tuleb tingimustele mittevastav väärtus, muidu ei välju Praat sellest tsüklist mitte kunagi. St ridade while ja endwhile vahel peab muutuja kunagi saama sellise väärtuse, mis tingimustele ei vastaks. Järgnev tsükkel kestab igavesti, lõpetab ainult restart. Seepärast on soovitatav nii skript ise kui objektiloendis olevad salvestamata asjad enne skripti käivitamist salvestada.

x = 1
while x = 1
  appendInfoLine: x
endwhile

20.5.3 Protseduurid

Protseduurid on skripti terviklikud osad, millele skriptis saab viidata. See on nagu eraldi skript ühe skripti sees. Näiteks kui meil on skript, mis otsib TextGrid’ilt midagi ja erinevate tingimuste korral teeb vastete esinemisel mingi hulga samu protseduure, siis selle asemel, et iga tingimuse juurde kõik käsud välja kirjutada, võime panna viite protseduurile ja protseduuri enda kirjutada lahti skripti lõppu.

Järgnevast näitest skripti põhiosa sisaldab tsüklit, mis käib läbi TextGrid’i 1. märgenduskihi ja otsib sealt sõnu, mis vastaksid mingitele tingimustele. Siin näites on tingimuseks, et sõna esimesed neli tähemärki on „mant“ (millega saame „Põhjatuule ja päikese“ tekstist kätte sõna mantel erinevad käändevormid) või sõna on „päike“. Iga vaste leidmisel kutsutakse esile protseduur @pohitoon.

Protseduur ise, mis sisaldab mitut käsku (siin sõna alguse-lõpu leidmist, Sound-objektist põhitooni leidmist ja tulemuste trükkimist infoaknasse), asub skripti lõpus. Oluline on, et see oleks lineaarselt jooksva skriptiosa järel peale kõikide skripti sees olevate tsüklite ja tingimuste lõppu.

clearinfo
# üks põhjatuule-päikese Sound ja TextGrid on objektiloendis 
# ja TextGrid-objekt on valitud
fail$ = selected$("TextGrid")
intd = Get number of intervals: 1

for i to intd
  selectObject: "TextGrid " + fail$
  i$ = Get label of interval: 1, i
  if left$(i$, 4)= "mant"
    @pohitoon
  elsif i$ = "päike"
    @pohitoon
  endif
endfor
# Siia maani jookseb skript lineaarselt
# SIIN ON SKRIPTI LÕPP.


# SIIT ALGAB PROTSEDUUR. 
# Seda juppi procedure-endproc ridade vahel
# jooksutatakse iga kord, kui põhiosas esineb @pohitoon
procedure pohitoon
  alg = Get start point: 1, i
  lop = Get end point: 1, i
  selectObject: "Sound " + fail$
  temp1 = Extract part: alg, lop, "rectangular", 1, "yes"
  temp2 = noprogress To Pitch: 0, 75, 600
  f0 = Get mean: alg, lop, "Hertz"
  appendInfoLine: i$, tab$, round(f0)
  plusObject: temp1
  Remove
endproc

20.5.4 goto-label-hüpped

Kui üldiselt skriptid jooksevad lineaarselt algusest lõpuni, siis seda lineaarsust saab Praati skriptikeeles lõhkuda hüpetega mingite kindlate märgisega (label) tähistatud kohtade juurde. Kui oled tuttav html-keelega, siis märgised on nagu ankrud ja hüpped on nagu hüperlingid neile akrupunktidele.

Ankrupunkti tähistab skriptis label ja linki sellele ankrule goto.

Teeme nüüd hoopis hüpetega läbi eelmise näite, kus ptk 20.5.3 kasutasime protseduuri. Tegemist pole küll selle Praati süntaksielemendi kasutamise kõige elegantsema näitega, aga ehk aitab paralleel eelnevaga funktsiooni paremini mõista.

Hüppeid Praatis kasutatakse kõige rohkem Demoakent juhtivates skriptides, mille kohta vaata näidet peatükist 20.12.

clearinfo
# üks põhjatuule-päikese Sound ja TG on objektiloendis valitud
fail$ = selected$("TextGrid")
intd = Get number of intervals: 1
for i to intd
  selectObject: "TextGrid " + fail$
  i$ = Get label of interval: 1, i
  if left$(i$, 4)= "mant"
    goto POHITOON
  elsif i$ = "päike"
    goto POHITOON
  endif
  label TAGASITSYKLISSE
endfor

# Kuna nüüd leibeli POHITOON taga pole protseduur, 
# jookseks skript edasi, aga paneme siia hüppe päris lõppu
goto END


label POHITOON
  alg = Get start point: 1, i
  lop = Get end point: 1, i
  selectObject: "Sound " + fail$
  temp1 = Extract part: alg, lop, "rectangular", 1, "yes"
  temp2 = noprogress To Pitch: 0, 75, 600
  f0 = Get mean: alg, lop, "Hertz"
  appendInfoLine: i$, tab$, round(f0)
  plusObject: temp1
  Remove
  goto TAGASITSYKLISSE

label END
# siin on skripti lõpp

20.6 Vektorid

Tavalisel muutujal võib Praati skriptikeeles olla ainult üks väärtus, aga ka Praatis saab kasutada vektoreid. Vektori muutujanime lõppu tuleb lisada sümbol # ning väärtused loogelistes sulgudes komadega eraldada.

arvud# = {3, 5, 14, -10}
writeInfo: "Vektor sisaldab arve: ", arvud#

Üksikute väärtuste pärimiseks tuleb lisada muutuja nime lõppu vektorielemendi indeks nurksulgudes:

arvud# = {3, 5, 14, -10}
writeInfo: "Teine arv: ", arvud#[2]

Indeksi väärtus võib olla sisestatud ka läbi teise muutuja. Nii saab näiteks teha tsükli, mis ükshaaval vektori elemendid läbi käib:

clearinfo
arvud# = {3, 5, 14, -10}
for i to size(arvud#)
  appendInfoLine: i, ". arv: ", arvud#[i]
endfor

Sarnaselt tavalistele muutujatele tuleb sõnaliste väärtustega vektor tähistada ka dollarimärgiga:

sonad$# = {"üks", "väärtus", "on", "43"}
writeInfo: sonad$#

Sõnalise vektori väärtuseks saavad olla tekstistringid ja vektoris sonad$# on viimane väärtus 43 sisestatud jutumärkides, ilma jutumärkideta saaks veateate.

Arvuliste vektoritega saab teha ka mõningaid tehteid (leida summat, standardhälvet jms), samuti on Praati skriptikeeles maatriksid ehk kahemõõtmelised mitmest väärtusest koosnevad muutujad, kuid nendest võimalustest võib põhjalikumalt lugeda Praati manuaalist.

20.7 Eeldefineeritud muutujad

Praati skriptides saab kasutada mõningaid eeldefineeritud sõnalisi muutujaid, mille sisu on kas alati sama või tuleb operatsioonisüsteemist. Kahte neist oleme juba kasutanud. Need on tab$, mis tekitab tabulatsioonimärgi, ja newline$, mis tekitab reavahetuse. Lisaks neile on mõned muutujad, millega saab määrata katalooge:

  • homeDirectory$ – kasutaja kodukataloog operatsioonisüsteemis, nt Windowsis võiks see olla C:\Users\partel ja Macis /Users/partel;
  • preferencesDirectory$ – kasutaja Praati eelistuste kataloog (sinna salvestuvad nt SoundEditor’i seaded, pluginad jms):
    • Windowsis C:\Users\partel\Praat;
    • Macis /Users/partel/Library/Preferences/Praat Prefs. Macis võib olla kataloog Library kasutaja eest peidetud;
  • temporaryDirectory$ – ajutine kataloog;
  • shellDirectory$ – kataloog, kuhu Praat on installitud, Windowsis näiteks C:\Program Files\Praat.

Tõenäoselt on vaja kahte esimest ja pigem harvem kahte viimast. Eeldefineeritud muutujaga on hea kodukataloogi märkida näiteks siis, kui skript peab salvestama tulemused kuhugi faili, aga skripti kasutavad eri kasutajad või kui töötad mitmes arvutis, kus on erinevad operatsioonisüsteemid või erinev kasutajatunnus. Siis saad lihtsalt oma kodukausta teha sama nimega kataloogi (või lasta skriptil kataloog teha) ega pea seda skriptis muutma.

Erinevates operatsioonisüsteemides on failiaadressides kasutatav kausta eraldav märk erinev. Kui Windowsis on see tagurpidi kaldkriips (\, Eesti klaviatuuril [AltGr]+[+]), siis Unix/Linux/Mac kasutavad selleks tavalist kaldkriipsu (/, [Shift]+[7]) ja vanemad Mac-id (vanemad kui OS X) hoopis koolonit. Praat õnneks oskab kaldkriipsu asendada vastava süsteemi märgiga. Seetõttu on mõistlik kasutada määratud kodukataloogi tähistavat muutujat koos tavalise kaldkriipsuga, nt

tulemused$ = homeDirectory$/tulem.txt

Sellisel juhul on üsna kindel, et selline kaust on olemas ja kasutajale ligipääsetav.

Arvuti kellaaega saab pärida muutujaga date$(). Seda on vahel hea näiteks tulemuste faili algusesse ja/või lõppu logida – näiteks kui andmete faili lisamine on seatud nii, et skript ei kirjuta olemasolevat faili üle ning oled skripti mitu korda jooksutanud, leiab ajatempli järgi failist üles kõige uuemad andmed.

aeg$ = date$()
writeInfo: aeg$
## Mon Mar  2 22:43:44 2026

Kui on pikem skript ja tahame teada, kui palju aega kulub selle jooksutamiseks, siis selleks on käsk stopwatch ehk stopper. Iga käsurida stopwatch annab eelmisest stopwatch mainimisest kulunud aja, nullib stopperi ja paneb selle uuesti käima. Seda saab näiteks kasutada siis, kui on ressursimahukam skript, mis kogub andmeid mitmest failist, ja tahame logida infoaknasse iga tsükli ringi lõpus, kui palju aega kulus (et selle põhjal hinnata, kui kaua veel aega võiks minna). Järgmises näites on stopper lisatud varasemale täringuviske näitele:

clearinfo
stopwatch
for i to 10
  visked = 0
  repeat
    silmad = randomInteger (1,6) + randomInteger (1,6)
    visked +=  1
  until silmad = 12
  aeg = stopwatch
  appendInfoLine: i, ".korral ", visked, " viset, ", aeg, " sek."
endfor
## 1.korral 33 viset, 7.38329254090786e-05 sek.
## 2.korral 24 viset, 4.8459041863679886e-05 sek.
## 3.korral 24 viset, 3.9499951526522636e-05 sek.
## 4.korral 28 viset, 4.2833155021071434e-05 sek.
## 5.korral 70 viset, 7.566693238914013e-05 sek.
## 6.korral 13 viset, 2.38749198615551e-05 sek.
## 7.korral 12 viset, 2.1625077351927757e-05 sek.
## 8.korral 30 viset, 4.3165870010852814e-05 sek.
## 9.korral 67 viset, 7.183407433331013e-05 sek.
## 10.korral 120 viset, 0.00011287513189017773 sek.

Arvulistest muutujatest on eeldefineeritud veel mõned konstandid: pi = 3,141592653589793, e = 2,718281828459045. Kuna need on konstandid, siis nende nimedega muid muutujaid ei saa kasutada.

20.8 Hoiatuste ja sõnumite ignoreerimine

Kui skriptis on mingi selline käsk, mille arvutamine on üldiselt mahukas ja Praat näitab progressiakent, mida on võimalik katkestada (nt To Pitch käsud), siis vajaduse korral saab selle progressiakna ära peita. Kui skriptis on palju kordusi käsul, mis progressiakna esile kutsub, on mõistlik see ära peita kahel põhjusel: esiteks, ilma selleta on skript oluliselt kiirem, ja teiseks, kui skript taustal jookseb ja teed samal ajal midagi muud, siis on tüütu, kui midagi kogu aeg vilgub. Selleks, et progressiakent ei ilmuks, tuleb käsu ette kirjutada noprogress:

noprogress To Pitch: 0, 75, 600

Aeg-ajalt on mõned käsud, mis annavad mingeid hoiatus- või veateatesõnumeid. Hoiatussõnumid on skripti puhul üsna tülikad, sest otseselt mingit viga nad ei põhjusta, aga skript jääb selle taha seisma, kuni kasutaja vajutab OK-nuppu. Hoiatussõnum hüppab dialoogiaknana ette näiteks siis, kui tahame Sound- või TextGrid-objekti salvestada, aga selle nimega fail on juba olemas. On muidugi ohtlik olemasolevaid faile üle kirjutada, aga kui näiteks tahame skriptiga lisada TextGrid’ile mingeid märgendeid ja iga muudatuse järel faili salvestada, on tülikas, kui skript iga kord selle koha peal seisma jääb. nowarn vastava käsu ees ignoreerib hoiatusi:

nowarn Save as WAV file: fail$ + ".wav"

Kui üritame rakendada dünaamilise menüü käsku valele objektile, annab Praat veateate ja skript lõpetab jooksmise. Veateate saab ka näiteks sellisel juhul, kui tahame eemaldada objekte, aga ühtegi objekti ei ole valitud või ei ole ühtegi objekti objektiloendis. Siis aitab nocheck:

nocheck Remove

20.9 Failid ja kaustad

20.9.1 Failide lugemine ja kirjutamine

Kui skripti paljude eri failide peal jooksutada, on hea, kui skript kontrollib kõigepealt, et fail oleks olemas või loetav. Kui faili ei ole, saab kasutajale tehnilise veateate asemel anda selge sõnumi, et faili pole. Järgnev näide küsib, kas arvuti töölaual on kataloogis nimega „pohjatuul“ fail nimega „POHJA_006-006_M.wav“.

kaust$ = homeDirectory$ + "/Desktop/pohjatuul/"
fail$ = "POHJA_006-006_M.wav"
loetav = fileReadable: kaust$ + fail$

Funktsiooni fileReadable vastus on jällegi arvuliselt esitatud tõeväärtus, ehk siis 1 = TÕENE (fail on olemas ja loetav), 0 = VÄÄR (faili pole või see ei ole loetav). Viimase rea võib ka asendada if-lausega:

if fileReadable: kaust$ + fail$
  Read from file: kaust$ + fail$
  Play
else
  writeInfoLine: "Seda faili pole!"
endif

Tekstifaili võib ka kohe muutuja väärtuseks lugeda. Seda teeb käsk readFile (juhul kui faili sisu on üks arv), või readFile$ (sõnalise muutuja puhul). Näiteks kui mul kaustas „pohjatuul“ oleks tekstifail „POHJA_006-006_M.txt“, mis sisaldab helifaili „POHJA_006-006_M.wav“ transkriptsiooni, saaks selle teksti lugeda muutuja x$ väärtuseks.

kaust$ = homeDirectory$ + "/Desktop/pohjatuul/"
fail$ = "POHJA_006-006_M.txt"
x = fileReadable: kaust$ + fail$
if x=1
  x$ = readFile$: kaust$ + fail$
writeInfo: x$
endif

Muutuja sisu saab saata faili käskudega writeFile, writeFileLine ja appendFile, appendFileLine. write ja append vahe on selles, et kui sama nimega fail on juba olemas, siis write kirjutab selle üle, append lisab olemasoleva faili lõppu. Käsk tahab vähemalt kahte argumenti: peale koolonit faili aadress ja seejärel tekst, mis faili kirjutatakse. Teksti (muutujaid) võib ka mitu olla nagu järgmises näites:

for i to 100
  if i = 1
   writeFile: homeDirectory$+"/Desktop/test.txt", "See on ", i,
   ... ". kord.", newline$
  else
   appendFile: homeDirectory$+"/Desktop/test.txt", "See on ", i,
   ... ". kord.", newline$
  endif
endfor

Lõpuks saab faili vajaduse korral ka ära kustutada (kui selline fail on, sest kui faili pole, siis ei juhtu midagi, veateadet ei saa):

deleteFile: homeDirectory$+"/Desktop/test.txt"

Eelmise näite võib ka natuke teisiti kirja panna nii, et kõigepealt küsime, kas selline fail on olemas, ja kui on, kustutame ära, ning seejärel tekitame teksti lisamisega uue faili:

x = fileReadable: homeDirectory$ + "/Desktop/test.txt"
if x=1
  deleteFile: homeDirectory$ + "/Desktop/test.txt"
endif
for i to 100
  appendFileLine: homeDirectory$ + "/Desktop/test.txt", "See on ",
  ... i, ". kord."
endfor

Faili saab salvestada ka infoakna sisu:

appendFile: homeDirectory$ + "/Desktop/test.txt", info$()

20.9.2 Kaustade loomine ja kustutamine ning süsteemikäsud

Vahel on tarvis skriptiga tekitada terve hulk uusi faile, mis võiksid minna eraldi uude kataloogi. createFolder: "uus_kataloog" on Praati skripti käsk kausta loomiseks. Kui selline kaust on juba olemas, siis seda üle ei kirjutata. Kui katalooginimi ei ole antud täisaadressina, siis tehakse vastava nimega kataloog sinna kataloogi, kuhu skript on salvestatud. Proovime uut kataloogi luua arvuti töölauale, määrates kodukataloogi asukoha eeldefineeritud muutujaga homeDirectory$.

createFolder:  homeDirectory$ + "/Desktop/uus_kataloog"

Sageli Windowsi süsteemis see käsk aga millegipärast ei tööta. Siis on võimalik kasutada süsteemikäske, mis töötavad operatsioonisüsteemi käsureal (Windowsis CommandLine, Macis Terminal). Käsu runSystem kaudu saab rakendada kõiki süsteemikäske. Nii Windowsis kui Macis on käsurea kataloogi loomise käsk mkdir katalooginimi. Praati skriptis oleks see siis:

runSystem: "mkdir uus_kataloog"

Nagu Praati käsk createFolder, teeb mkdir alamkausta nimega uus_kataloog sinna kataloogi, kuhu skript on salvestatud. Kui anda loodavale kaustale täisaadress, tuleb nüüd arvestada seda, et kui kasutad Windowsi, siis siin peavad kaustu eraldavad kaldkriipsud olema längkriipsud, nagu Windowsi failisüsteem neid kasutab, sest runSystem saadab järgneva käsurea sisu otse Windowsi süsteemile.

runSystem: "mkdir C:\Users\partel\Desktop\uus_kataloog"

Kausta kustutamiseks on Windowsi ja Maci süsteemis erinevad käsud. Windowsis

runSystem: "del C:\Users\partel\Desktop\uus_kataloog"

ja Macis

runSystem: "rm /Users/partel/Desktop/uus_kataloog"

Käsurea võib ka mitmest muutujast kokku panna. Käsu runSystem järel võib olla mitu komadega eristatud muutujat või väärtust, need pannakse üheks tekstistringiks kokku. Tähele tuleks panna seda, et tekst kokku peab andma käsurea nii, nagu see süsteemi käsureale peaks minema, nii näiteks peab käsu rm ja kataloogiaadressi vahel olema tühik.

ajutine_kataloog$ = "/Users/partel/Desktop/uus_kataloog"
runSystem: "rm ", ajutine_kataloog$

20.9.3 Failinimekirjad

Ühte skripti saab terve kataloogitäie failide peal jooksutada. Selleks on vaja luua nimekiri kataloogis olevate failide nimedest, mida saab teha käsuga Create Strings as file list..., mille leiab objektiakna menüüst New > Generics > Strings. See käsk teeb Strings-objekti, mille sisuks on määratud kataloogis asuvate failinimede loend. Skriptis saab teha tsükli, mis Strings-objektist ükshaaval failinimesid pärib, et neid faile siis avada ja nendega edasi töötada. Järgnevas näites avame kataloogist WAV-faile, mängime neid ja paneme uuesti kinni. Kui skript asub akustiliste meetodite repositooriumi kaustas, siis seal on alamkaust taju/stiimulid/, kus on ptk 17.3 juhiste järgi loodud stiimulfailid, mida proovime järgnevas näites lugeda. Muutuja dir$ väärtuseks võid panna ka vastava kataloogi täisaadressi oma arvutis.

dir$ = "taju/stiimulid/"
nimekiri = Create Strings as file list: "fileList", dir$ + "*.wav"
faile = Get number of strings
for fail to faile
  selectObject: nimekiri
  fnimi$ = Get string: fail
  Read from file: dir$ + fnimi$
  Play
  Remove
endfor
selectObject: nimekiri
Remove

Strings on objekt, mis sisaldab mingisugust nimekirja, antud juhul siis nimekirja failinimedest. Objekti saab avada käsuga View & Edit (vt joonis 20.1). Käsud, millega skriptis failinimesid pärida, leiab menüüst Query: esmalt peab küsima, mitu nime nimekirjas on käsuga Get number of strings ning tsükli sees iga üksiku failinime pärimiseks Get string.... Menüüst Modify leiab käsu Randomize, millega saab nimekirja juhuslikku järjekorda ajada, kui failinimekirja kasutada näiteks Demoakna skriptiga (vt ptk 20.12) tajutestis stiimulite esitamiseks.

Strings-objekt failinimekirjaga Praati graafilises kasutajaliideses.

Joonis 20.1. Strings-objekt failinimekirjaga Praati graafilises kasutajaliideses.

Võrdlemisi hiljuti (aastal 2021 Praati versiooniga 6.1.38) on lisandunud ka skriptikeele funktsioon, millega ilma String-objekti vahenduseta saab lugeda failinimekirja vektorina. Vektorist saab seejärel tsüklis ükshaaval failinimesid pärida (vt vektorite kohta 20.6).

dir$ = "taju/stiimulid"
failid$# = fileNames$#(dir$)
for i to size(failid$#)
  fail$ = failid$# [i]
  appendInfoLine: fail$
endfor
## stim_F1_200.wav
## stim_F1_300.wav
## stim_F1_400.wav
## stim_F1_500.wav
## stim_F1_600.wav
## stim_F1_700.wav
## stim_F1_800.wav

20.9.4 Praati objektide valimine

Kui muidu oleme nimetanud selectObjects: järel objektitüüpi ja failinime (nagu on kirjeldatud ptk 19.7), siis lisaks nimele on objektidel ka numbrid. Iga objekt saab objektiloendis sessiooni piires unikaalse järjekorranumbri. Näiteks kui Praat lahti teha ja avada näiteks üks helifail ja üks TextGrid-fail, siis see esimesena avatud Sound-objekt on järjekorras esimene ja TextGrid on teine. Neid numbreid saame kasutada selleks, et objekti valida, nt valime objektiloendis esimese objekti:

selectObject: 1

Objekti nime järgi objekti valimise puhul on piirang, et kui meil on objektiloendis mitu sama nimega objekti, siis nimega pöördumine viib meid objektiloendis viimase vastavanimelise objekti juurde. Olukord, et sama nimega objekte on mitu, võib tekkida näiteks siis, kui oleme lõiganud pikemast Sound-objektist välja tükke ega ole neid pidevalt ümber nimetanud – siis on loendis mitu objekti nimega „Sound untitled“. Järjekorranumbritega valimise puhul on aga piirang see, et numeratsioon kehtib ainult Praati sessiooni piires ning kui panna Praat kinni ja uuesti avada, ei pruugi sama objekt sama numbrit saada.

Kui kasutame skriptis käsku, mis faili avab või loob, siis me saame ühe skripti jooksutamise piires selle järjekorranumbri salvestada muutujaks ja skripti piires selle muutuja kaudu objekti valida. Nii on võimalik väga selgelt jälgida, et ühe skripti piires loetud või loodud objektidele oleksid suunatud õiged käsud. Kui on aga tarvis mitut objekti valida, siis lisaks valitule teise objekti valimiseks on käsk plusObject. Järgmises näites loeme Praati Sound-objekti ja salvestame selle numbri muutujasse heli. Seejärel teeme sellest Pitch-objekti ja järjekorranumbri muutujaks pohitoon. Siis joonistame põhitoonikontuuri ja lõpetuseks valime Sound- ja TextGrid-objektid ja eemaldame objektiloendist.

heli = Read from file: "isoleeritud_vokaalid.wav"
pohitoon = To Pitch: 0, 75, 600
Draw: 0, 0, 0, 200, "yes"
Save as 300-dpi PNG file: "isol_vok_pohitoon.png"
selectObject: heli
plusObject: pohitoon
Remove

20.10 Toimetamisakna skriptid

Siiamaani oleme vaadanud ainult üldiseid skripte, kus töötavad objekti- ja pildiakna käsud. Üldised skriptid toimetamisakende käske kasutada ei saa. Põhjus on erinevas loogikas: toimetamisaknas arvestab käsk kursori paiknemist x-y teljel, st näiteks kui SoundEditor’is küsime käsuga Get Pitch põhitooni väärtust, siis sõltub vastus sellest, kus sel hetkel kursor asub; objektiaknas Pitch-objektilt sama asja küsides aga peame ise käsu argumentidega ütlema, mis ajateljepunktist väärtust tahame. Kuid ka toimetamisakende käsud on täiel määral skriptitavad.

Ava mõni toimetamisaken (vali mõni objekt objektiaknast ja vajuta nuppu View & Edit). Toimetamisakna skriptid töötavad ainult ühe Editor’i sees vastava objekti Editor’i käskudega. Näiteks on siin TextGridEditor’i skript, mis poolitab teisel segmenteerimiskihil valitud segmendi (vt ka ptk 14.1.2).

segmalgus = Get start of selection
segmkestus = Get selection length
pool = segmkestus / 2
piir = segmalgus + pool
Move cursor to: piir
Add interval on tier 2
Move to nearest zero crossing

Selle skripti jaoks peab olema avatud toimetamisaken Sound- ja TextGrid-objektiga, samuti peab olema TextGrid’il vähemalt 2 märgenduskihti ja mingi ajaintervall peab olema kursoriga valitud. Skript küsib kursoriga valitud lõigu algusaega ja kestust, jagab kestuse kahega ning liigutab kursori selle intervalli võrra edasi. Seejärel lisab skript piiri kursori asukohale ning nihutab piiri kohakuti helilaine lähima nullpunktiga.

20.11 Toimetamisakna käsud üldise skripti sees

Toimetamisakende käske on siiski võimalik kasutada ka üldises skriptis. Siis tuleb skriptil kõigepealt toimetamisaken avada (või peab see olema avatud enne skripti jooksutamist) ja suunata käsud Editor’ile. Näiteks saab neid kasutada koos pauseScript käsuga selleks, et kasutaja saaks skripti jooksutamise käigus välja otsitud kohas käsitsi märgendust kontrollida või muuta ning seejärel saab skript näiteks faili salvestada või mingid analüüsid võtta.

# ...mingid eelnevad käsud, mis otsivad TextGridilt mingisugust
# märgendust ja kui nad selle leiavad, siis
# valime TextGridi ja küsime segmendi algus- ja lõpuaega
selectObject: "TextGrid " + fail$
sAlg = Get starting point: k, j
sLop = Get end point: k, j
# lisame valikusse Sound objekti ja avame Editori
plusObject: "Sound " + fail$
View & Edit
# Nüüd suunatakse käsud editorile
editor: "TextGrid " + fail$
    # valime kursoriga segmendi algusest lõpuni
    Select: sAlg, sLop
    # zoomime selle segmendi ekraanile
    Zoom: sAlg, sLop
    # teeme skriptis pausi, et kasutaja saaks 
    # editoris toimetada ja edasi minna
    beginPause: "Kontrolli märgendust"
      comment: "segm " + string$(j) + " oleks vaja kontrollida"
    tegevus = endPause: "Loobu", "Salvesta", 2, 1
    # kui kasutaja on pausi lõpetanud, paneme editori kinni
    # ja sulgeme editorile käskude suunamise
    Close
endeditor

# siit jätkub tavaskript
# kui kasutaja vajutas Salvesta nuppu, siis salvestame TextGridi
if segmKontrolliTegevus = 2
  selectObject: "TextGrid " + fail$
  Save as text file: fail$ + ".TextGrid"
endif
        

20.12 Demoaken

Praati demoakent saab kasutada katsete läbiviimiseks, aga ka kasvõi PowerPointi asemel slaidiesitluste tegemiseks. Demoaken on lihtsalt üks täiendav menüüdeta aken, kuhu saab kuvada sama sisu, mis muidu läheks pildiaknasse. Selleks tuleb lihtsalt käsurea algusesse muidu pildiaknas toimivate käskude ette lisada demo. Näiteks:

Create Sound as pure tone: "tone", 1, 0, 0.1, 44100, 100,
... 0.2, 0.01, 0.01
demo Draw: 0, 0, 0, 0, "yes", "curve"
demo Text top: "yes", "See on siinusheli sagedusega 100 hertsi."
Remove

Kui pilte või slaide on rohkem kui üks, siis tuleb vahepeal eelmine sisu ära kustutada ja seejärel uus joonistada. Ning kuna skript muidu jookseb algusest lõpuni järjest nii kiiresti kui arvuti protsessor seda võimaldab, on vaja lisada slaidivahetuse koha peale paus. Demoaknas saab ka salvestada koordinaadid, kuhu hiirega klikiti, nii on võimalik programmeerida edasi või tagasi liikumiseks nupud. Skriptis edasi-tagasi liikumiseks on hea kasutada goto-hüppeid (vt ptk 20.5.4).

Create Sound as pure tone: "tone", 1, 0, 0.1, 44100, 100,
... 0.2, 0.01, 0.01
# kustutab eelneva, kui midagi demoakanas juba oli
demo Erase all
# määrame joonisele ruudukese akna keskel
demo Select outer viewport: 20, 80, 20, 80
demo Draw: 0, 0, 0, 0, "yes", "curve"
demo Text top: "yes", "See on siinusheli sagedusega 100 hertsi."
Remove
# aktiveerime jälle kogu akna pinna
demo Select outer viewport: 0, 100, 0, 100
# määrame akna telgede skaala, 
# mis helilaine joonistamisega sai muud väärtused
demo Axes: 0, 1, 0, 1
demo Paint rounded rectangle: "pink", 0.8, 1, 0, 0.1, 4
demo Text special: 0.9, "Centre", 0.05, "Half", "Helvetica",
... 14, "0", "Järgmine"

# siit edasi minemiseks oodatakse tegevust, mis on kas klikk 
# roosa nupu peal või ükskõik mis klahvi vajutamine,
# mõlemal juhul hüpatakse leibeli NEXT juurde
while demoWaitForInput ( )
  if demoClickedIn (0.8, 1, 0, 0.1) or demoKeyPressed()
    goto NEXT
  endif
endwhile

# see on teine slaid
label NEXT
Create Sound as pure tone: "tone", 1, 0, 0.1, 44100, 500, 
... 0.2, 0.01, 0.01
demo Erase all
demo Select outer viewport: 20, 80, 20, 80
demo Draw: 0, 0, 0, 0, "yes", "curve"
demo Text top: "yes", "See on siinusheli sagedusega 500 hertsi."
Remove

Demoakna skriptiga on võimalik teha keerukamaid tajukatseid kui ExperimentMFC võimaldab (vt ptk 18.1), näiteks katseid, kus katseisik peab stiimulile reageerima kõnega, mida salvestatakse. Hetkel on sellise katse disaini puhul Praati kõige suurem puudus see, et skriptiga saab juhtida ainult etteantud kestusega salvestust käsuga Record Sound (fixed time)....84 Salvestusfunktsiooni ei saa nupuvajutusega kinni panna siis, kui katseisik on lõpetanud, mis tähendab, et kui katseisikul kulub prognoositust kauem aega, jääb lõpp salvestamata, aga kui tal kulub vähem aega, siis peab ootama, kuni salvestus lõppeb.

Siin üks veidi lihtsam näide ilma heli salvestamiseta. Teeme ühe katse, kus katseisik peab hindama kuuldud heli kõrgust. Seda katset saaks edukalt ka ExperimentMFC’ga teha.

skoor = 0
demo Erase all
demo Text special: 0, "Left", 0.5, "Half", "Helvetica", 18, "0",
... "Tere tulemast katsesse. Alustamiseks vajuta tühikut."

demo Paint rounded rectangle: "pink", 0.8, 1, 0, 0.1, 4
demo Text special: 0.9, "Centre", 0.05, "Half", "Helvetica", 14,
... "0", "Alustame"
  while demoWaitForInput ( )
    if demoClickedIn (0.8, 1, 0, 0.1) or demoKeyPressed()
      goto KATSE
    endif
  endwhile

label KATSE
# genereerime vektori 10 numbriga vahemikus 1-5
x# = randomInteger#(10, 1, 5)

for i to 10
  tmp = Create Sound as pure tone: "tone", 1, 0, 0.4, 44100, 
  ... 100 * x# [i], 0.2, 0.01, 0.01
  demo Erase all
  demo Text special: 0.5, "Center", 0.8, "Half", "Helvetica", 
  ... 36, "0", "Mis sagedusega on heli, mida kuuled?"

  # nupud vastuste jaoks
  demo Paint rounded rectangle: "pink", 0, 0.1, 0.4, 0.5, 4
  demo Paint rounded rectangle: "pink", 0.2, 0.3, 0.4, 0.5, 4
  demo Paint rounded rectangle: "pink", 0.4, 0.5, 0.4, 0.5, 4
  demo Paint rounded rectangle: "pink", 0.6, 0.7, 0.4, 0.5, 4
  demo Paint rounded rectangle: "pink", 0.8, 0.9, 0.4, 0.5, 4
  # kiri nupu peal
  demo Text special: 0.05, "Center", 0.45, "Half", "Helvetica", 
  ... 14, "0", "100"
  demo Text special: 0.25, "Center", 0.45, "Half", "Helvetica", 
  ... 14, "0", "200"
  demo Text special: 0.45, "Center", 0.45, "Half", "Helvetica", 
  ... 14, "0", "300"
  demo Text special: 0.65, "Center", 0.45, "Half", "Helvetica",
  ... 14, "0", "400"
  demo Text special: 0.85, "Center", 0.45, "Half", "Helvetica", 
  ... 14, "0", "500"
  # mängime stiimulit ja eemaldame selle peale mängimist
  Play
  Remove

  while demoWaitForInput ( )
    if demoClickedIn (0, 0.1, 0.4, 0.5)
      vastus = 100
    elsif demoClickedIn (0.2, 0.3, 0.4, 0.5)
      vastus = 200
    elsif demoClickedIn (0.4, 0.5, 0.4, 0.5)
      vastus = 300
    elsif demoClickedIn (0.6, 0.7, 0.4, 0.5)
      vastus = 400
    elsif demoClickedIn (0.8, 0.9, 0.4, 0.5)
      vastus = 500
    endif
    goto VASTUS
  endwhile

  label VASTUS
  demo Erase all
  if vastus = 100*x# [i]
    skoor += 1
    demo Text special: 0.5, "Center", 0.8, "Half", "Helvetica", 
    ... 36, "0", "Õige, see heli on " + string$(vastus) + " Hz."
  else
    demo Text special: 0.5, "Center", 0.8, "Half", "Helvetica", 
    ... 36, "0", "Vale vastus, see heli on " +
    ... string$(100*x# [i]) + " Hz."
  endif
  demo Paint rounded rectangle: "pink", 0.8, 1, 0, 0.1, 4
  demo Text special: 0.9, "Centre", 0.05, "Half", "Helvetica",
  ... 14, "0", "Edasi"
  while demoWaitForInput ( )
    if demoClickedIn (0.8, 1, 0, 0.1) or demoKeyPressed()
      goto EDASI
    endif
  endwhile

  label EDASI
endfor

label LOPP
demo Erase all
demo Text special: 0, "Left", 0.5, "Half", "Helvetica",
... 18, "0", "Katse sai läbi. Vastasid " + 
... string$(skoor) + " korda õigesti."

Selle näitega lõpetame katsetused Praati skriptidega siin õpikus. Me ei ole käinud läbi kõiki Praati skripti võimalusi, kuid neid võib omal käel edasi avastada Praati manuaali toel. Kui aga sulle tundub Praati skriptikeel kohmakas ja ebamugav, siis peatükis 21 vaatame põgusalt üle, kuidas foneetilisi andmeid koguda ja analüüsida programmiga R.


  1. Astendamist tähistava sümboli leiab eesti klaviatuuril [AltGr]+[ä] kombinatsiooni alt.↩︎

  2. Sümbol „+“ tähistab muidu eelneva sümboli kordumist ja otsing "keele+teadus" võiks anda vasteks „keeleteadus“ ilma plussmärgita ning „keeleeteadus“ või „keeleeeeeeteadus“ jne, aga mitte „keele+teadus“.↩︎

  3. Kui kasutada lihtsalt algava loogelise sulu sümbolit regulaaravaldises, nt "t{nA", saaks vastuseks veateate.↩︎

  4. See käsk on peidetud, aga Praat > Settings > Buttons saab selle üles otsida ja ka menüüs nähtavaks teha. Skriptis töötab käsk aga ka peidus olevana.↩︎