Ищите несколько слов в сценарии праат

Я пишу сценарий праата, который будет искать в нескольких файлах список слов. Это то, что у меня есть до сих пор. Он увеличивает только первое слово в процедуре и не перебирает остальные. Я думаю, что это как-то связано с тем, что выбрано. Для For i through n выбирается только текстовая сетка, но затем в аннотаторе выбираются обе. Мне нужно, чтобы скрипт продолжал поиск по каждому интервалу, чтобы можно было найти и другие слова в процедуре.

directory$ = "directory"
listfile$ = "test.txt"

Read Strings from raw text file... 'directory$'/'listfile$'
last = Get number of strings

# loop through each file
for a from 1 to last
    listfile2$ = listfile$ - ".txt"
    select Strings 'listfile2$'
    textgrid$ = Get string... 'a'
    Read from file... 'directory$'/'textgrid$'
    object_name$ = selected$("TextGrid")

    Read from file... 'directory$'/'object_name$'.wav

    # rearrange tiers 
    select TextGrid 'object_name$'
    Duplicate tier: 3, 1, "MAU"
    Remove tier: 4
    Insert interval tier: 1, "subphone"

    # find target word
    n = Get number of intervals: 3  
    for i to n

@instance: "strikes"
@instance: "raindrops"
@instance: "and"
@instance: "rainbow"
@instance: "into"
@instance: "round"
@instance: "its"
@instance: "its"

procedure instance: .target_word$

    label$ = Get label of interval: 3, i
        if label$ == .target_word$
        index = i
        i += n

# get the start and end point of the word
startpoint = Get starting point... 3 index
endpoint = Get end point... 3 index

        select TextGrid 'object_name$'
        plus Sound 'object_name$'
        View & Edit
        editor TextGrid 'object_name$'

# annotation
Select... startpoint endpoint
Zoom to selection
pause Annotate stops then continue
Close
endeditor

        endif # if the label = target word
    endfor # for number of intervals



select TextGrid 'object_name$'
Write to text file: directory$ + "/" + object_name$ + "_editedtext.TextGrid"

select all
minus Strings 'listfile2$'
Remove

endproc

#writeInfoLine: "done!"
#select Strings 'listfile2$'
endfor # for each of the files
clearinfo
print That's it!

Изменить: вот исправленный сценарий, основанный на ответе.

directory$ = "/Users/directorypath"
listfile$ = "test.txt"

Read Strings from raw text file... 'directory$'/'listfile$'
last = Get number of strings
listfile2$ = listfile$ - ".txt"

# loop through each file
for a from 1 to last
    select Strings 'listfile2$'
    textgrid$ = Get string... 'a'
    Read from file... 'directory$'/'textgrid$'
    object_name$ = selected$("TextGrid")
    Read from file... 'directory$'/'object_name$'.wav

    # rearrange tiers
    select TextGrid 'object_name$'
    Duplicate tier: 3, 1, "MAU"
    Remove tier: 4
    Insert interval tier: 1, "subphone"

    n = Get number of intervals: 3

    for i to n
        @instance: "strikes"
        @instance: "raindrops"
        @instance: "and"
        @instance: "rainbow"
        @instance: "into"
        @instance: "round"
        @instance: "its"
        @instance: "its"

    endfor
endfor

procedure instance: .target_word$

label$ = Get label of interval: 3, i
if label$ == .target_word$
    index = i
    i += n

    # get the start and end point of the word
    startpoint = Get starting point... 3 index
    endpoint = Get end point... 3 index

    select TextGrid 'object_name$'
    plus Sound 'object_name$'
    View & Edit
    editor TextGrid 'object_name$'

    # annotation
    Select... startpoint endpoint
    Zoom to selection
    pause Annotate stops then continue
    Close
    endeditor

    endif

endproc

person Lisa    schedule 22.10.2017    source источник


Ответы (3)


Сценарий, который вы написали, был не очень тщательно отступлен, поэтому я попытался отформатировать его, чтобы было легче понять, что происходит. И в каком-то смысле так оно и было. Но то, что всплыло, все же потребовало некоторых усилий, чтобы понять.

Вот пошаговое описание того, что происходит, как это видит Праат:

  1. В строке 8 вы запускаете цикл for: for a from 1 to last

  2. Внутри этого цикла, в строке 25, вы запускаете второй: for i to n

  3. Внутри этого второго цикла в строке 27 вы вызываете процедуру с именем instance.

    В этот момент Praat переходит к последней строке, определяющей эту процедуру (поэтому, если вы определите ее несколько раз, вы получите только последнюю). Так как он только один, Праат переходит к строке 36: procedure instance: .target_word$

  4. Внутри этой процедуры (которая, кстати, определена внутри цикла for, что... необычно) у вас есть блок if: if label$ == .target_word$

  5. В конце этого блока endfor увеличивает управляющую переменную (в данном случае i) и закрывает цикл for. Но какое?

    Вы можете ожидать, что он закроет последний введенный нами цикл for (это то, что я сделал). Но на самом деле Praat, похоже, отслеживает открывающие for и закрывающие endfor операторы и отображает их по вертикали.

    Я не изучил интерпретатор достаточно подробно, чтобы выяснить, точно что происходит, но в таком случае результаты будут такими же, как при отображении самого нижнего endfor (= того, что ближе всего к нижнему вашего скрипта) до наивысшего for и так далее.

    (Вероятно, это не то, что происходит на самом деле (иначе несколько неперекрывающихся циклов не сработают), но это не очень важно: важно то, что endfor только закрывает один for, независимо от того, где он находится в сценарии или когда Праат его видит. Между прочим, это не то, что происходит с endproc.)

    Независимо от точных правил, это endfor сопоставляется со вторым for, которое мы ввели в пункте 2 (это была строка 25). Итак, мы возвращаемся к первой строке того цикла (строка 26).

  6. Теперь мы добираемся до строки 27 во второй раз (на этот раз для второго интервала) и снова вызываем @instance: "strikes". Мы еще не добрались до @instance: "raindrops"!

  7. Это повторяется для всех интервалов, каждый раз увеличивая i на единицу (всякий раз, когда мы нажимаем endfor), пока i не станет n. На этот раз, когда мы вызываем @instance, мы проходим блок if и снова попадаем в endfor из точки 5.

    Праат послушно увеличивает управляющую переменную (теперь i = n + 1) и проверяет конечное условие, установленное в начале цикла for. В этом случае Праат знает, что цикл for заканчивается, когда i == n, и начиная с i = n + 1, вместо того, чтобы вернуться наверх, он продолжает работать.

    Только сейчас, пройдя все интервалы первого файла, мы действительно подходим к концу процедуры!

  8. Процедура наконец заканчивается. Праат помнит, что мы вошли в эту процедуру еще в пункте 3, и тогда мы читали строку 27. Так что, послушно, она идет и читает строку 28, которая является еще одним вызовом той же процедуры: @instance: "raindrops".

  9. И вот где (наконец-то!) он умирает.

    Он умирает, потому что управляющая переменная i теперь n + 1 (она стала таковой в пункте 7). Обычно этого не происходит, поскольку вы обычно не нажимаете оператор endfor для цикла, который уже завершился. Но в этом случае мы делаем. Поэтому, когда Praat пытается прочитать метку интервала i в TextGrid с интервалами i-1... он жалуется, говоря, что номер интервала слишком велик. Потому что это так.

    Вашу первоначальную проблему (она выполняет только часть работы, а не умирает) я не могу воспроизвести, потому что в блоке if вы фактически вручную меняете значение i (что рискованно), и этот блок if выполняется только в том случае, если метка соответствует достаточно рано в вашем TextGrid (достаточно рано, чтобы это произошло до того, как скрипт взорвется).

Вы можете увидеть весь этот беспорядок в действии с помощью этой упрощенной версии структуры вашего скрипта:

last = 10
for a from 1 to last
  appendInfoLine: "First line of main loop; a = ", a

  n = 5 
  for i to n
    appendInfoLine: "First line of second loop; i = ", i

    @instance: "strikes"
    @instance: "raindrops"
    @instance: "and"

    procedure instance: .target_word$
      appendInfoLine: "Called @instance: " + .target_word$
      appendInfoLine: "a=", a, " i=", i

  endfor # for number of intervals

      appendInfoLine: "End of @instance"
    endproc

  appendInfoLine: "Script claims we are done!"
endfor # for each of the files

Чтобы исправить это, вам, вероятно, следует изменить структуру кода, чтобы он более или менее следовал этому шаблону:

for a to last
  for i to n
    @instance: "word"
  endfor
endfor

procedure instance: .word$
  # do things
endproc

Это было очень поучительно. :)

person jja    schedule 25.10.2017
comment
Большое спасибо, это было очень поучительно. Я сильно почистил свой сценарий. Тем не менее, у меня все еще есть та же проблема в цикле if. Я получаю сообщение об ошибке получить метку интервала, недоступную для текущего выбора (я думаю, потому что в этой точке выбраны и текстовая сетка, и звук). Я не уверен, как это исправить, чтобы это был цикл и только текст выбрана сетка - или, может быть, это даже не проблема. Я вставляю свой новый сценарий в свой исходный пост. - person Lisa; 26.10.2017
comment
Похоже, вы уже нашли ответ. Слава! Но на вашем месте я бы сохранил исходную версию сценария в вашем вопросе. Тем более, что я часто ссылаюсь на него по номеру строки, и теперь это будет иметь очень мало смысла для людей, впервые задающихся этим вопросом. Однако вы все равно сможете увидеть исходный сценарий, просмотрев историю редактирования вашего вопроса. Но это дополнительный шаг. - person jja; 30.10.2017

Попробуйте добавить select TextGrid 'object_name$' сразу после инициализации процедуры.

procedure instance: .target_word$

    select TextGrid 'object_name$'

    label$ = Get label of interval: 3, i
    if label$ == .target_word$
         index = i
         i += n
person Stefano    schedule 23.10.2017
comment
Я думаю, что это сработало, но теперь я получаю сообщение об ошибке: Слишком большой интервал: строка сценария 48 не выполнена или завершена ‹‹ метка$ = Получить метку интервала: 3, i ›› Команда меню Выполнить не завершена. - person Lisa; 24.10.2017

Я заставил его работать! страница руководства по циклам for оказалась полезной. Вот код, если кому пригодится.

#############################################################
#
# This script requires a file listing the .Textgrid files 
# in the directory containing the .wav files and .Textgrid files.
# Using the command line, you can make this by navigating to the directory,
# and typing ls *.TextGrid > contents.txt
#
# This script reads in files and textgrids from a directory,
# rearranges the tiers that were output by MAUS, - http://www.bas.uni-muenchen.de/Bas/BasMAUS.html
# provides a subphone tier, searches for words specified in the procedure
# so that they can be annotated.
#
# Written using code from Bert Remijsen that was rewritten by Peggy Renwick
#
#############################################################

# enter your directory here
directory$ = "/Users/lisalipani/Documents/School/Graduate/Research/NSP/CorpusFiles/Test"

# enter your list file here
listfile$ = "test.txt"

Read Strings from raw text file... 'directory$'/'listfile$'
last = Get number of strings
listfile2$ = listfile$ - ".txt"

# loop through each file
for a from 1 to last
    select Strings 'listfile2$'
    textgrid$ = Get string... 'a'
    Read from file... 'directory$'/'textgrid$'
    object_name$ = selected$("TextGrid")
    Read from file... 'directory$'/'object_name$'.wav

    # rearrange tiers
    select TextGrid 'object_name$'
    Duplicate tier: 3, 1, "MAU"
    Remove tier: 4
    Insert interval tier: 1, "subphone"

    # input target words here
    @instance: "strikes"
    @instance: "friends"

    # start the procedure
    procedure instance: .target_word$

    select TextGrid 'object_name$'

    numberOfIntervals = Get number of intervals: 3

    for intervalNumber from 1 to numberOfIntervals
        label$ = Get label of interval: 3, intervalNumber
        if label$ == .target_word$

            # get the start and end point of the word
            startpoint = Get starting point... 3 intervalNumber
            endpoint = Get end point... 3 intervalNumber

            select TextGrid 'object_name$'
            plus Sound 'object_name$'
            View & Edit
            editor TextGrid 'object_name$'

            # annotation
            Select... startpoint endpoint
            Zoom to selection
            pause Annotate stops then continue
            Close
            endeditor

            select TextGrid 'object_name$'

        endif
    endfor
    endproc

    select TextGrid 'object_name$'
    Save as text file... 'directory$'/'object_name$'_annotated.TextGrid

endfor
appendInfoLine: "all done!"
person Lisa    schedule 27.10.2017