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, aga eelmise peatüki skripti saab siin esitatavate funktsioonidega täiendada. See peatükk sobib edasijõudnud skriptijatele. Siiski ei anna see peatükk ammendavat ülevaadet Praati rikkalikust skriptikeelest, mida iga kasutaja võib Praati enda võrdlemisi hea manuaali põhjal edasi avastada.
20.1 Tingimuste kombineerimine
Ühte if-endif konstruktsiooni saab kokku panna mitu tingimust:
- peab vastama kahele tingimusele
if a=1 and b=2
- peab vastama ühele kahest tingimusest
if a=1 or b=2
- peab vastama ühele, aga mitte teisele tingimusele
if a=1 not b=2
Tingimusi võib muidugi olla ka rohkem kui kaks ja neid saab sulgudega rühmitada
- peab vastama ühele tingimusele või kahele alternatiivsele
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]
- peab vastama kahele tingimusele…
if (a=1 or a=4) and b=2
# a võib olla 1 või 4, b on 2
Kui ühel muutujal võib olla kaks võimalikku sobivat väärtust, siis peab need eraldi tingimustena välja kirjutama, ei saa ühele anda mitut:
# see annab veateate:
if a = 1, 3 or 6
# õige on nii:
if a = 1 or a = 3 or a = 6
20.2 Sõnaliste muutujate võrdlemine
Sõnalisi muutujaid saab mõnevõrra võrrelda ka, peale selle, et kas on = (sama) või <> (mitte). Saab võrrelda, kas on suurem või väiksem, aga siin on tähestikujärjekorras. Reeglid on sellised:
- mida kaugemal tähestikus täht asub, seda suurem (a<b),
- väiksed tähed on suuremad kui suured (A<a),
- pikem string on suurem, kui lühemal ei ole tähestikus kaugemal paiknevat osa (a<aa ja a<ab, aga c>ab),
- numbrid on tähtedest väiksemad (1<a).
20.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 esimsel real, siis käsuread, mis tingimuse täitmise korral rakenduvad, siis else eraldi real, sellele järgnevad käsuread, mis rakenduvad tingimuse mittetäitmisel ning lõpuks rida endif:
if x=1
m = 3*5
else
m = 3*10
endif
Sellise tingimuslause saab kirjutada ka ühe reaga:
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:
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:
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
end = Get end time of interval: 1, 1
start = (Get start time of interval: 1, 1
dur = (end-start)*1000
20.4 Tehted arvuliste muutujatega
Märgid:
- liitmine +
- lahutamine -
- korrutamine *
- jagamine /
- astendamine ^ (Märgi leiab eesti klaviatuuril [AltGr]+[ä] alt)
Kolm ruudus ehk 3 astmel 2 = 3^2; ruutjuure saame sama märgiga: ruutjuur üheksast = 9^(1/2)
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 siis ikka üle."
20.5 Skripti peatamine
Kui mingitel ebasobivatel tingimustel ei saa skripti edasi jooksutada (nt objektiloendis peaksid olema valitud mingid failid mis ei ole, või TextGridil on mingi taseme märgendus puudu), saab skripti seisma panna käsuga exitScript
. Nt:
kiht2$ = Get tier name: 2
if not kiht2$ = "häälikud"
exitScript: "Teisel kihil pole häälikud!"
endif
writeInfoLine: "Teise kihi nimi on häälikud"
Juhul kui 2 kihi nimi ei ole “häälikud”, skript lõppeb ja ette visatakse dialoogiaken sõnumiga “Teisel kihil pole häälikud!”. Juhul kui aga TextGridil polegi 2. kihti, tuleb ei jõua skript esimsest reast kaugemale ja lõpetab veateatega.
Käsuga pause
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. Aga saab ka näiteks teha pausi, et midagi vahepeal salvestada, teha objektile mõni muudatus käsitsi vms.
20.6 Paus dialoogiaknaga
Vahel on vaja skriptis mõnda muutujat määrata skripti keskel. form-endform konstruktsiooniga saab dialoogiakna 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
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.7 Juhuslike arvude genereerimine
Käsk randomInteger
annab ühe juhusliku täisarvu määratud vahemikust (minimum, maksimum):
juhuslik_arv = randomInteger(-10,10)
Käsk randomUniform
annab ühe juhusliku murdarvu määratud vahemikust (minimum, maksimum):
juhuslik_arv = randomUniform (-1,10)
20.8 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äringuviseid 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 14 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 TextGridile on märgitud häälikute piirid ja on vaja mõõta nt 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 käivitamist peab olema kindel, et tingimustele sobiv väärtus ka leidub, muidu ei välju Praat sellest silmusest kunagi ja kui silmuse 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 testimiseks 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.9 while-tsüklid
Peale for- ja repeat-tsüklite on veel while-silmused. 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
Aga mõtet on seda 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! Tingimustele sobituv väärtus peab 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.10 Stringi funktsioonid e kuidas otsida sõnalisi muutujaid
Siin vaatame funktsioone, millega tekstist otsinguid teha. Kus seda Praatis praktiliselt vaja läheb, on näiteks kui TextGridil on ühele kihile märgitud sõnad ja tahame leida sõnu, mis algavad või sisaldavad mingit kindlat tähekombinatsiooni.
20.10.1 Üks-ühesed vastavused
Olgu meil näiteks sõna “kellanöörike” (et oleks piisavalt pikk ja sisaldaks korduvaid järgnevusi), kust hakkame mingeid märgijadasid otsima, nimetame selle muutujaks a$
.
Tekstiosa stringi algusest. left$("tekst", n)
leiab teksti algusest n esimest tähemärki. Nt muutuja a$
vasakult kaks esimest tähemärki (__ke__llanöö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 sama moodi töötab right$("tekst", n)
teksti lõpust. Leiame muutuja a$
paremalt kaks esimest tähemärki (kellanööri__ke__):
a$ = "kellanöörike"
c$ = right$ (a$, 2)
appendInfoLine: a$, " viimased kaks on ", c$
## kellanöörike viimased kaks on ke
Tekstiosa stringi keskelt. Alates a$
kuuendast märgist kolm märki (kella__nöö__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 on ", d$
## kellanöörike kuues kuni kaheksas 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, ".-st tähest"
## sõnas kellanöörike algab nöö 6.-st 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$, " algab viimane ", c$, " ", g, ".-st tähest"
## sõnas kellanöörike algab viimane ke 11.-st 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 on tõeväärtus, e TÕENE=1, VÄÄR=0.
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 viimane number = 0, siis toimub vahetus kõigil kordadel, kui 2, siis kahel esimesel jne. Vahetame esimesed kaks tähte sõnas kellanöörike:
a$ = "kellanöörike"
b$ = left$ (a$, 2)
j$ = replace$ (a$, b$, "võ", 1)
appendInfoLine: j$, " on siis, kui 1. silp ", b$, " vahetada ära võ-ga"
## võllanöörike on siis, 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.10.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 sama moodi 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 või üks korda. 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) üks nurksulgudes olevatest sümbolitest võib esineda. 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 liitsõna (Sümbol + tähistab muidu eelneva sümboli kordumist), nt keele+teadus või SAMPA transkriptsiooni ä vastet {, nt t{nt_vAp. Sümbol on eesti klaviatuuril klahvikombinatsioonis AltGr+.
Tekstiosa otsing regulaaravaldisega. Käsk index_regex
otsib ühest stringist teise stringi algust (sama moodi kui lihtsalt index
, aga otsitavat stringi tõlgendatakse regulaaravaldise mitte üks-ühese tähemärgi järjendina). Näiteks:
a$ = "kellanöörike"
k = index_regex (a$, "ö..k")
appendInfoLine: k, ". tähest algab 4-täheline jada, mis algab ö-ga ja lõppeb k-ga"
## 8. tähest algab 4-täheline jada, mis algab ö-ga ja lõppeb k-ga
Ja sama asi viimse kattumise kohta:
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 juhuhul (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 siis, kui sõnas 'a$' a.*i vahetada u vastu"
## kelluke on siis, 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.11 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, minu kunagises WinXP-sC:\Documents and Settings\partel
ja Macis/Users/partel
preferencesDirectory$
– kasutaja Praati eelistuste kataloog (sinna salvestuvad nt SoundEditori seaded, pluginad jms); WindowsisC:\Documents and Settings\partel\Praat
ja macis/Users/partel/Library/Preferences/Praat Prefs
. Macis võib olla kataloog Library kasutaja eest peidetudtemporaryDirectory$
– ajutine kataloogshellDirectory$
– kataloog, kuhu Praat on installitud, Windowsis näiteksC:\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) ja ei 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 kella aega saab pärida muutujaga date$(). Seda on vahel on hea näiteks tulemuste faili logida.
aeg$ = date$()
writeInfo: aeg$
## Tue May 6 09:40:30 2025
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
nullib ja paneb stopperi 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, 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 läks ", visked, " viset ja ", aeg, " sekundit."
endfor
## 1.korral läks 4 viset ja 4.5042019337415695e-05 sekundit.
## 2.korral läks 15 viset ja 3.2250070944428444e-05 sekundit.
## 3.korral läks 39 viset ja 4.0207989513874054e-05 sekundit.
## 4.korral läks 36 viset ja 4.370789974927902e-05 sekundit.
## 5.korral läks 30 viset ja 3.729201853275299e-05 sekundit.
## 6.korral läks 52 viset ja 5.550007335841656e-05 sekundit.
## 7.korral läks 29 viset ja 3.875000402331352e-05 sekundit.
## 8.korral läks 18 viset ja 2.574990503489971e-05 sekundit.
## 9.korral läks 109 viset ja 8.90830997377634e-05 sekundit.
## 10.korral läks 42 viset ja 4.720897413790226e-05 sekundit.
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.12 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 koguaeg vilgub. Selleks, et progressiakent ei ilmuks, tuleb käsu ette kirjutada noprogress
:
noprogress To Pitch: 0, 75, 600
Aegajalt 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 skrpitiga lisada TextGridile mingeid märgendeid ja iga muudatuse järel faili salvestada, on tülikas, kui skript iga kord selle kohapeal 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 objektiloendis. Siis aitab nocheck
:
nocheck Remove
20.13 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 skript ilusti öelda, et faili pole, selle asemel et anda mingi veateade.
kaust$ = homeDirectory$ + "/Desktop/praat_edasij6udnud/
...pohjatuul/"
fail$ = "POHJA_006-006_M.wav"
loetav = fileReadable: kaust$ + fail$
Funktsiooni fileReadable
vasutus on tõeväärtus, ehk siis tõene = 1 (fail on olemas ja loetav), väär = 0 (faili pole või on vigane). 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 märk käsk readFile
(juhul kui faili sisu on üks arv), või readFile$ (sõnalise muutuja puhul):
dir$ = homeDirectory$ + "/foneetika_programmiga_praat/"
fail$ = "POHJA_005-006_M.txt"
x = fileReadable: dir$ + fail$
if x=1
x$ = readFile$: dir$ + 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äks 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
Ja 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: kõigepealt küsime, kas selline fail on olemas; kui on, kustutame ära; kirjutame teksti 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.14 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: kasutaAadressJaNimi$
on Praati skripti käsk kausta loomiseks. Kui selline kaust on juba olemas, siis seda üle ei kirjutata:
createFolder: homeDirectory$+"/Desktop/uus_kataloog"
Sageli Windowsi süsteemis see käsk aga millegi pärast ei tööta. Siis on võimalik kasutada süsteemikäsku, 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"
See teeb uus_kataloog nimega alamkausta 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 keeratud:
runSystem: "mkdir C:\Documents and Settings\partel\Desktop\uus_kataloog"
Kausta kustutamiseks on Windowsi ja Maci süsteemis erinevad käsud. Windowsis:
runSystem: "del C:\Documents and Settings\partel\Desktop\uus_kataloog"
ja Macis
runSystem: "rm /Users/partel/Desktop/uus_kataloog"
Ja muidugi võib käsurea ka mitmest muutujast kokku panna:
ajutine_kataloog$ = "/Users/partel/Desktop/uus_kataloog"
runSystem: "rm ", ajutine_kataloog$
20.15 Editori skriptid
Siiamaani oleme vaadanud ainult üldiseid skripte, kus töötavad objekti- ja pildiakna käsud. Üldised skriptid editoride käske kasutada ei saa, põhjus on erinevas loogikas: editorides arvestab käsk kursori paiknemist x-y teljel, st näiteks kui SoundEditoris 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 ütlema ise, mis ajatelje punktist väärtust tahame. Kuid ka editorides leiduvad käsud on täiel määral skriptitavad.
Ava mõni editor (vali mõni objekt objektiaknast ja vajuta nuppu View & Edit
). Editori skriptid töötavad ainult ühe editori sees selle editori käskudega. Näiteks üks TextGridEditori skript, mis poolitab teisel segmenteerimiskihil valitud segmendi:
segmkestus = Get selection length
pool = segmkestus / 2
segmalgus = Get start of selection
piir = segmalgus + pool
Move cursor to: piir
Add interval on tier 2
Move to nearest zero crossing
Selle skripti jaoks peab olema avatud Sound (mitte LongSound) + TextGrid editor, samuti peab olema TextGridil vähemalt 2 märgenduskihti ja mingi ajaintervall peab olema kursoriga valitud. (Sound ja mitte LongSound selle pärast, et viimane käsk, mis nihutab piiri lähimasse nullpunkti, on ainult Sound objektil kasutatav).
20.16 Editori skriptid üldise skripti sees
Editoride käske on siiski võimalik kasutada ka üldises skriptis. Siis tuleb skriptil kõigepealt editor avada (või peab olema editor enne avatud) ja suunata käsud editori. 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 kasuta saaks editoris toimetada ja edasi minna
beginPause: "Kontrolli märgendust"
comment: "segm " + string$(j) + " sisaldab oleks vaja kontrollida"
tegevus = endPause: "Loobu", "Salvesta", 2, 1
# kui kasutaja on pausi lõpetanud, paneme editori kinni ja sulgeme editorile käskude suunamise, st lähme edasi tavaskripti käskudega
Close
endeditor
# kui kasutaja vajutas Salvesta nuppu, siis salvestame TextGridi
if segmKontrolliTegevus = 2
selectObject: "TextGrid " + fail$
Save as text file: fail$ + ".TextGrid"
endif
20.17 Mitme faili lugemine
Ühte skripti saab terve kataloogitäie failide peal jooksutada. Kõige parem on seda teha käsuga Create Strings as file list...
, mille leiab objektiakna menüüst New
. Selle käsuga saab tekitada uue objekti Strings, kus on määratud kausta määratud laiendiga failide nimekiri, kust skript saab edasi failinimesid pärida. Näiteks avame üks haaval kõik WAV failid, mis kaustas on, mängime faili ja paneme uuesti kinni:
dir$ = homeDirectory$ +
..."/foneetika_programmiga_praat/pohjatuul/"
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. Sealt nimekirjast saab ükshaaval failinimesid pärida, aga nimekirja saab ka randomiseerida, kui nimekirja on vaja kasutada näiteks tajutesti stiimulite mängimiseks.
Siin näites kasutasime ka Strings objekti valimiseks objektiloendi järjekorra numbrit (mis oli salvestatud muutujasse nimekiri). Kui muidu oleme nimetanud selectObjects:
järel objektitüüpi ja failinime, siis sama moodi saame ka numbri järgi valida. Iga objekt saab objektiloendis sessiooni piires unikaalse järejekorranumbri. Nt kui Praat lahti teah ja avada näiteks üks helifail ja üks TextGrid fail, siis see esimesena avatud helifail on 1. ja TextGrid on 2.
selectObject: 1
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. Ja kui 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 + 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.18 Protseduurid
Protseduurid on skripti terviklikud osad, millele skriptis saab viidata. Näiteks kui meil on skript, mis otsib TextGridilt midagi ja erinevate tingimuste korral teeb leidude korral 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 TextGridi #1 kihi ja otsib sealt sõnu, mis vastaksid mingitele tingimustele (siin näites mant[el/li..], päike). Iga leiu puhul kutsutakse esile protseduur @pohitoon
. Protseduur, 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:
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"
@pohitoon
elsif i$ = "päike"
@pohitoon
endif
endfor
# SIIN ON SKRIPTI LÕPP. Siia maani jookseb skript käivitamisel lineaarselt
# SIIN ON 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.19 goto-label hüpped
Kui üldiselt skriptid jooksevad lineaarselt algusest lõpuni, siis seda lineaarsust saab lõhkuda hüpetega mingite kindlate leibelite juurde. Kui oled tuttav html-keelega, siis leibelid on nagu ankrud. Teeme eelmise protseduuri näite läbi hoopis hüpetega (pole küll kõige elegantsem näide leibelite kasutamisest, aga vaata ka 20.20 näidet.
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,
# siis skript jookseks 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.20 Demo aken
Lisaks pildiaknale on võimalik graafikat suunata ka demo aknasse. Selleks tuleb lihtsalt käsurea algusesse lisada demo
. Demoaken on lihtsalt üks täiendav menüüdeta aken, kuhu saab kuvada sama sisu, mis muidu läheks pildiaknasse. 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.
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 vahjutamine, 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 ette antud kestusega salvestust käsuga Record Sound (fixed time)...
.34 Salvestusfunktsiooni ei saa nupuvajutusega kinni panna siis kui katseisik on lõpetanud ja see tähendab, et kui katseisikul kulub prognoositust kauem aega, jääb lõpp salvestamata ning kui tal kulub vähem, 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", "Teretulemast katsesse. Alustamiseks vajuta tühikut või alustamise nuppu."
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 vasutste 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, heli kõrgus 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."
Vaata lisaks
Praati manuaal, skriptid: http://www.fon.hum.uva.nl/praat/manual/Scripting.html
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.↩︎