Praat脚本编程中文教程:子程序procedures的应用

分类:Praat
 标签:Praat脚本,Praat中文教程,Praat编程
   修改 | 阅读(1474)| 评论(1)


在Praat脚本编程过程中,你可能要重复地做一些工作,在某种情况下,你可以用循环来解决(参考:循环语句for/while/repeat的用法)。但有时候循环并不能满足我们需求,如:我们要调用一系列语句,但每次调用的时候都要根据不同的情况来进行调用,或者说我们要传参数来执行这一系列脚本语句。这里我们可以用子程序来解决。在Praat中,用procedures来表示,基本语法:

procedures 子程序名
    程序体
endproc

我们来看如下脚本:

Create Sound as pure tone: "note", 1, 0, 0.3, 44100, 440, 0.2, 0.01, 0.01
Play
Remove
Create Sound as pure tone: "note", 1, 0, 0.3, 44100, 880, 0.2, 0.01, 0.01
Play
Remove
Create Sound as pure tone: "note", 1, 0, 0.3, 44100, 400, 0.2, 0.01, 0.01
Play
Remove
Create Sound as pure tone: "note", 1, 0, 0.3, 44100, 800, 0.2, 0.01, 0.01
Play
Remove
Create Sound as pure tone: "note", 1, 0, 0.3, 44100, 500, 0.2, 0.01, 0.01
Play
Remove
Create Sound as pure tone: "note", 1, 0, 0.3, 44100, 1000, 0.2, 0.01, 0.01
Play
Remove

这段脚本是创建一个正弦波的音频,频率分别为440、880、400、800、500、1000。每创建一个音频,我们就来播放它,播放完后将它从Objects窗口移除,再创建下一个音频、播放、移除……

可能有人会想到,这一系列重复的动作,可以用一个循环来写,但我们看到其中的440、880、400……这些是不按规律来的,用循环时你很难设定这个值。

我们来看看440、880;400、800;500、1000这些数后面的都是前面的两倍,那我们可以用两个变量来表示:frequency,octaveHigher=frequency*2 。

下面我们就用子程序来改写上面的脚本:

procedure playOctave
    Create Sound as pure tone: "note", 1, 0, 0.3, 44100, frequency, 0.2, 0.01, 0.01
    Play
    Remove
    octaveHigher = 2 * frequency
    Create Sound as pure tone: "note", 1, 0, 0.3, 44100, octaveHigher, 0.2, 0.01, 0.01
    Play
    Remove
endproc
frequency = 440
@playOctave
frequency = 400
@playOctave
frequency = 500
@playOctave

上面定义了一个playOctave的子程序,在下面就用@playOctave来调用此子程序,在调用之前先给frequency赋上不同的值。

这脚本看起来还是有点冗长,每调用一次就要进行一次赋值,那么有没有办法不用对变量进行赋值就直接调用呢,答案是有的,看下面的结构:

procedures 子程序名:参数1,参数2,参数3……
    程序体
endproc

看了上面这个结构,相信有些同学已经看明白了,我们在调用的时候直接传参数就可以了,不用每次对变量进行赋值再调用,这样麻烦。

procedure playOctave: frequency
    Create Sound as pure tone: "note", 1, 0, 0.3, 44100, frequency, 0.2, 0.01, 0.01
    Play
    Remove
    octaveHigher = 2 * frequency
    Create Sound as pure tone: "note", 1, 0, 0.3, 44100, octaveHigher, 0.2, 0.01, 0.01
    Play
    Remove
endproc

@playOctave: 440
@playOctave: 400
@playOctave: 500

我们在定义playOctave子程序的时候,同时给定了一个参数:frequency,在调用的时候,我们就直接在后面跟上参数就行了,脚本就会把后面跟上的参数赋值给frequency。

如果有多个参数,就用英文逗号“,”隔开。

procedure playOctave: frequency,octaveHigher
    Create Sound as pure tone: "note", 1, 0, 0.3, 44100, frequency, 0.2, 0.01, 0.01
    Play
    Remove
    Create Sound as pure tone: "note", 1, 0, 0.3, 44100, octaveHigher, 0.2, 0.01, 0.01
    Play
    Remove
endproc

@playOctave: 440,880
@playOctave: 400,800
@playOctave: 500,1000

这样我们就把两个参数就都传进去了,我们没有子程序中计算octaveHigher。


封装和局部变量

我们来看看,试着输出一个变量frequency

procedure playOctave: frequency
    Create Sound as pure tone: "note", 1, 0, 0.3, 44100, frequency, 0.2, 0.01, 0.01
    Play
    Remove
    octaveHigher = 2 * frequency
    Create Sound as pure tone: "note", 1, 0, 0.3, 44100, octaveHigher, 0.2, 0.01, 0.01
    Play
    Remove
endproc
frequency = 300
@playOctave: 440
@playOctave: 400
@playOctave: 500
writeInfoLine: frequency

可能很多人看到从上到下,要第10行定义了一个frequency的变量并赋值为300,在接下来的第11、12、13行都没有再进行赋值,第14行进行输出变量时,应该会输出300。不!你错了,它输出的是500。我们仔细看看,子程序中也定义了一个名为“frequency”的变量,在执行第13行时,传入了参数“500”,那子程序中的“frequency”就被赋值为500了。但我们的原意是输出我们定义的“frequency”,而不是子程序中的“frequency”。这样就很容易造成混乱了。那如果我想自己定义一个变量,在子程序中又用同一个变量名,但却要它们表示不同的意义,这样行不行呢?当然是可以的!

在Praat中,我们可以这样命名一个子程序中的变量

子程序名.变量名

上面的例子就写成:

procedure playOctave: playOctave.frequency
    Create Sound as pure tone: "note", 1, 0, 0.3, 44100, playOctave.frequency, 0.2, 0.01, 0.01
    Play
    Remove
    playOctave.octaveHigher = 2 * playOctave.frequency
    Create Sound as pure tone: "note", 1, 0, 0.3, 44100, playOctave.octaveHigher, 0.2, 0.01, 0.01
    Play
    Remove
endproc
frequency = 300
@playOctave: 440
@playOctave: 400
@playOctave: 500
writeInfoLine: frequency

这时我们看到输出的结果是:300。

可能有人会说,本来这个变量名就够长了,你再加上一个子程序名,这样更长,不方便我们写啊。

Praat中还提供了另一种写法,就是把点“.”前的子程序名去掉。那上面的脚本就写成:

procedure playOctave: .frequency
    Create Sound as pure tone: "note", 1, 0, 0.3, 44100, .frequency, 0.2, 0.01, 0.01
    Play
    Remove
    .octaveHigher = 2 * .frequency
    Create Sound as pure tone: "note", 1, 0, 0.3, 44100, .octaveHigher, 0.2, 0.01, 0.01
    Play
    Remove
endproc
frequency = 300
@playOctave: 440
@playOctave: 400
@playOctave: 500
writeInfoLine: frequency

很多时候,我们调用子程序不仅仅是想执行一些脚本,也有可能要进行一些计算,并把计算结果返回来给我们的主程序,那我们该如何写呢?看下面的例子:

procedure doSomeCalculate: .num
    .result=.num^2+.num*2
endproc
@doSomeCalculate:3
writeInfoLine: doSomeCalculate.result
@doSomeCalculate:4
appendInfoLine: doSomeCalculate.result

例子解释:

我们定义了一个“doSomeCalculate”的子程序,其功能是传入一个数num,计算num的平方加上num乘于2的值。在子程序中,我们用变量.result来保存计算结果。脚本中第4行传入3调用子程序,然后第5行我们输出“doSomeCalculate.result”,也就是“子程序名.变量名”来输出子程序中的“.result”变量。我们可以看到得出的结果是:15、24。


这一节对于没有编程经验或者理解能力相对较差的同学来说可能有点难度,如果有疑问的可以直接问我。本节教程就到这里。


原创作品,未经授权不可进行转载。


如您觉得本文对您有帮助,可以赞助博主一杯奶茶吗

    


【苍洱】评论于:2019-9-11 20:35:56    [回复本评论]

请问,我想让一个文件夹里的100个trail重复一样的操作,怎么做呢?

您的昵称:*
QQ登录(无需注册直接登录可进行回复)
您的邮箱:(填写邮箱,如有回复可进行邮件通知)
验证码:
点击刷新