Baby Language Lab Scripts
A collection of data processing tools.
 All Classes Namespaces Files Functions Variables Pages
praat_interop.py
Go to the documentation of this file.
1 ## @package utils.praat_interop
2 
3 import subprocess
4 import time
5 import os
6 import platform
7 import socket
8 import select
9 
10 from db.bll_database import DBConstants
11 
12 ## This class contains static methods that can be used to perform some basic operations in Praat.
13 # To do this, it uses the sendpraat program, available <a href="http://www.fon.hum.uva.nl/praat/sendpraat.html" target="_blank">here</a>. The location of this program on the current machine should be available in the constant DBConstants.SETTINGS.SENDPRAAT_PATH (which is defined in the DB settings table).
14 class PraatInterop(object):
15 
16  ## Opens a Praat instance.
17  # Note: Things won't work if you open more than one at a time.
18  @staticmethod
19  def open_praat():
20  praat = subprocess.Popen([DBConstants.SETTINGS.PRAAT_PATH])
21 
22  #wait for praat to start up before sending it commands
23  time.sleep(0.5)
24 
25  ## Closes an open Praat instance.
26  @staticmethod
27  def close_praat():
28  PraatInterop.send_commands(['Quit'])
29 
30  ## Sends a list of commands to Praat.
31  # These commands are just Strings that manipulate the Praat UI. You can access anything clickable in the UI by its name.
32  # If you need to use Praat variables to store stuff, or other Praat scripting language functions, you'll need to open an editor window.
33  # See get_sel_bounds_script() for an example of how to do that.
34  # This idea is to generate a Praat scripts for a specific task using the get_..._script() methods below. These methods return a list of strings that can be then be passed to this method for execution.
35  # This allows us to dynamically generate Praat scripts, using values or other information from our Python scripts.
36  # @param cmds (list) list of Praat commands (String). Eg. ['Open long sound file... "mysound.wav"', 'Extract part... 0.0 10.0']
37  # @returns (int) 0 on success, nonzero on issue (see subprocess module in Python API for details)
38  @staticmethod
39  def send_commands(cmds):
40  sendpraat_args = [
41  DBConstants.SETTINGS.SENDPRAAT_PATH,
42  #'0', #the unix version of sendpraat requires an extra 'timeout' argument. This is auto-added below if this is a non-windows system.
43  'praat',
44  ] + cmds
45  if platform.system().lower() != 'windows':
46  sendpraat_args.insert(1, '0')
47 
48  return subprocess.call(sendpraat_args,
49  shell=False,
50  ) #returns 0 on success
51 
52  ## This method returns a script that opens praat and displays a spectrograph of a given section of a given wave file.
53  # The intention is that the user can select a portion with the mouse, and then the selection boundaries
54  # can be retreived with the close_praat() method (below).
55  # @param start_time (float) absolute start time of the clip to display
56  # @param end_time (float) absolute end time of the clip to display
57  # @param wav_filename (string) full path to the wave file to open
58  # @param open_spec_win (boolean=True) set to False if you don't want to open a spectrogram window (this will just extract the sound clip)
59  @staticmethod
60  def get_open_clip_script(start_time, end_time, wav_filename, open_spec_win=True):
61  #open the sound in praat, extract the correct portion, and open a soundEditor window (spectrogram) so the user can select a chunk.
62  script = [
63  'Open long sound file... %s' % (wav_filename),
64  'Extract part... %f %f yes' % (start_time, end_time),
65  ]
66 
67  if open_spec_win:
68  script += ['View & Edit']
69 
70  return script
71 
72  ## Used in custom_scripts
73  @staticmethod
74  def get_sel_bounds_script(wav_filename):
75  port = int(DBConstants.SETTINGS.PRAAT_IPC_PORT)
76  sound_ed_name = os.path.basename(wav_filename)[:-4]
77 
78  script = [
79  'editor Sound %s' % (sound_ed_name),
80  'start = Get start of selection',
81  'end = Get end of selection',
82  # this causes praat to send a message on a socket created on the specified port
83  "sendsocket localhost:%d 'start' 'end'" % (port), #msg format is "<start_time> <end_time>"
84  'Close',
85  ]
86 
87  return script
88 
89  ## Used in Melissa's thesis project (see custom_scripts directory)
90  @staticmethod
92  script = [
93  'snd = selected("Sound")',
94  'To Pitch... 0.01 60 700',
95  'q1 = Get quantile... 0 0 0.25 Hertz',
96  'q3 = Get quantile... 0 0 0.75 Hertz',
97  'Remove',
98  'select snd',
99  'floor = 60',
100  'ceiling = 700',
101  'if q1 != undefined',
102  ' floor = q1 * 0.75',
103  'endif',
104  'if q3 != undefined',
105  ' ceiling = q3 * 2.0',
106  'endif',
107  'pitch = To Pitch... 0.001 floor ceiling',
108  ]
109 
110  return script
111 
112  ## This low-pass filters a clip from a wav file, and saves the clip as a new wav file.
113  # Used in relaibility project project (see custom_scripts directory)
114  @staticmethod
115  def get_low_pass_filter_script(wav_filename, clip_filename, file_code, start_time, stop_time, filter_from, filter_to, filter_smoothing):
116  script = [
117  "Extract part... %f %f yes" % (start_time, stop_time),
118  "Filter (pass Hann band)... %d %d %d" % (filter_from, filter_to, filter_smoothing),
119  "max = Get maximum... 0 0 Sinc70",
120  "min = Get minimum... 0 0 Sinc70",
121  "max_intensity = max(abs(min), abs(max))",
122  "scale_factor = 0.999 / max_intensity",
123  "Multiply... scale_factor",
124  'Save as WAV file... %s' % (clip_filename),
125  'select Sound %s' % (file_code + '_band'),
126  'Remove',
127  'select Sound %s' % (file_code),
128  'Remove',
129  'select LongSound %s' % (file_code),
130  ]
131 
132  return script
133 
134  ## Used in Melissa's thesis project (see custom_scripts directory)
135  @staticmethod
136  def get_pitch_sample_vals_script(clip_start, clip_end, wav_filename, step=0.01):
137  port = int(DBConstants.SETTINGS.PRAAT_IPC_PORT)
138  pitch_ed_name = os.path.basename(wav_filename)[:-4]
139 
140  script = [
141  "View & Edit",
142  "editor Pitch %s" % (pitch_ed_name),
143  "start = %f" % (clip_start),
144  "end = %f" % (clip_end),
145  "start_pitch = undefined",
146  "start_time = undefined",
147  "end_pitch = undefined",
148  "end_time = undefined",
149  "sample_time = start",
150  "while start_pitch == undefined and sample_time <= end",
151  " Move cursor to... 'sample_time'",
152  " pitch = Get pitch",
153  " if pitch != undefined",
154  " start_pitch = pitch",
155  " start_time = sample_time",
156  " endif",
157  " sample_time = sample_time + %f" % (step),
158  "endwhile",
159  "sample_time = end",
160  "while end_pitch == undefined and sample_time >= start",
161  " Move cursor to... 'sample_time'",
162  " pitch = Get pitch",
163  " if pitch != undefined",
164  " end_pitch = pitch",
165  " end_time = sample_time",
166  " endif",
167  " sample_time = sample_time - %f" % (step),
168  "endwhile",
169 
170  "sendsocket localhost:%d 'start_time' 'start_pitch' 'end_time' 'end_pitch'" % (port),
171  "Close",
172  ]
173 
174  return script
175 
176  ## Used in Melissa's thesis project (see custom_scripts directory)
177  @staticmethod
179  port = int(DBConstants.SETTINGS.PRAAT_IPC_PORT)
180 
181  script = [
182  'min_pitch = Get minimum... 0.0 0.0 Hertz Parabolic',
183  'max_pitch = Get maximum... 0.0 0.0 Hertz Parabolic',
184  'mean_pitch = Get mean... 0.0 0.0 Hertz',
185  "sendsocket localhost:%d 'min_pitch' 'max_pitch' 'mean_pitch'" % (port),
186  ]
187 
188  return script
189 
190  ## Creates a socket to listen for info that Praat is instructed to send.
191  # To see how praat can be intructed to send info, see the get_sel_bounds_script() method.
192  # @param port (int) the port to listen on
193  # @returns (socket) a socket object from the Python socket library. This socket is set up and listening on the specified port.
194  @staticmethod
196  port = int(DBConstants.SETTINGS.PRAAT_IPC_PORT)
197 
198  #create an INET, STREAMing socket
199  serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
200  serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
201  serversocket.setblocking(0)
202  #bind the socket to a public host and a well-known port
203  serversocket.bind(('localhost', port))
204  #fire up
205  serversocket.listen(1)
206 
207  return serversocket
208 
209  ## Receives a message containing the selected boundary information from Praat.
210  # @param serversocket (socket) Python socket library socket object to receive the message on
211  # @returns (list) returns a list of values sent back by Praat. Values should be separated by a space character.
212  @staticmethod
213  def socket_receive(serversocket, delim=' '):
214  #block until we get a connection on the specified port (20 second timeout)
215  select.select([serversocket], [], [], 20)
216  #now grab the message
217  (clientsocket, address) = serversocket.accept()
218  c = clientsocket.recv(1)
219  msg = ''
220  while c != '\x00': #end byte is marked with a zero
221  msg += c
222  c = clientsocket.recv(1)
223 
224  #message is in format '<start_time> <end_time>'
225  return msg.split(delim)