0
votes

You can start an instance of Praat and then use sendpraat.exe to send GUI commands to it. So, if you have Praat running in the background, the following command will cause it to read in a local mysound.wav file as a Sound object:

sendpraat praat "Read from file... mysound.wav"

And there are a bunch of ways to send this command via Python, e.g.:

import subprocess
subprocess.call('sendpraat praat "Read from file... mysound.wav"')

But if mysound.wav doesn't exist, the Praat instance will pop up a message box with the error. How can I capture the contents of this error message in Python and avoid the popup?

These solutions don't work for the problem:

  1. For this particular example, I could just check in Python if the file exists. But I also need to deal with all of the other errors that Praat will throw, such as when it has trouble with encodings, or when samples are clipped.

  2. I can use nocheck before the command (sendpraat praat nocheck "Read from file... mysound.wav" to avoid the popup, but then Python has no way of knowing that the command failed.

1

1 Answers

1
votes

nocheck is the only way in Praat to do error handling, limited though it may be. If you want to use the GUI programmatically from Python (or any other such), the best way to do error handling is to use nocheck and then catch errors by looking for the side effects of those commands.

If you are opening a Sound, you can do assert numberOfSelected("Sound") or something like that (with more or less elegant tests). If you are writing something to disk, you can use fileReadable() to see if the file was created.

Alternatively, if you are not actually using the GUI, you can bypass sendpraat entirely and use Praat through the console (with versions before 6.0 a different binary called praatcon was needed for Windows, but more recent versions use the same program with the --run option).

You won't be able to pass commands directly to it, but you can encapsulate those commands into scripts and then just do subprocess.call('praat --run path/to/my/script.praat arguments') or something like that. Then you might be able to catch errors in that script (= your commands) using Python, or implement the same manual error checking as above.

Update: an example

Here's an example (in Perl, and in Linux, but you get the idea):

#!/usr/bin/env perl

use Capture::Tiny ':all';
use Try::Tiny;

try {
  ($stdout, $stderr, $exit) = capture {
    system( 'praat', '--run', '~/stdout.praat' );
  }
}
catch {
  chomp;
  warn "It died: $_";
};

print "STDOUT:\n$stdout\n";
print "STDERR:\n$stderr";

And the contents of stdout.praat:

abc$ = "abcde"
num$ = "0123456789"
writeInfoLine: abc$
assert selected("Sound")  ; Fail
writeInfoLine: abc$, num$ ; Won't run

The output:

user@linux:~$ perl stdout.pl
STDOUT:
abcde

STDERR:
Error: No Sound selected.
Formula not run.
Script line 4 not performed or completed:
« assert selected("Sound") ; Fail »
Script “/home/user/stdout.praat” not completed.
Praat: command file “/home/user/stdout.praat” not completed.

Update: try / catch in Praat

Since I wrote this answer, I've managed to implement a very rudimentary version of a try/catch procedure for Praat. With it, a script like

include path/to/try.proc

writeInfoLine: "Before fail"

call try
  ... abc$ = "abcde"                                \n
  ... num$ = "0123456789"                           \n
  ... Create Sound as pure tone: "tone",            \n
  ...   ... 1, 0, 0.4, 44100, 440, 0.2, 0.01, 0.01  \n
  ... assert selected("TextGrid")  ; Fail           \n
  ... Remove                       ; Won't run      \n

if try.catch
  appendInfoLine: "Failed!"
endif

will execute without crashing, leaving behind the Sound that was created because the line where it is removed did not run.

The procedure is available in the utils plugin distributed through CPrAN, and is implemented here.