├── .gitignore ├── LICENCE ├── LICENCE.pyDAWG ├── README.md ├── align.py ├── alignannotation.praat ├── aligntier.praat ├── book ├── .chktexrc ├── .gitignore ├── Makefile ├── book.bib ├── book.tex └── preamble.tex ├── cleaninterval.praat ├── generatedict.praat ├── generatedict.py ├── install.sh ├── install_win.bat ├── mkrelease.sh ├── par.dutch ├── DICT ├── HVITECONF ├── MMF ├── MONOPHONES └── PRECONFIGNIST ├── par.spanish ├── DICT ├── HVITECONF ├── MMF ├── MONOPHONES └── PRECONFIGNIST ├── phonetizer.py ├── procs.praat ├── pyDAWG.py ├── settings.praat ├── settings_ni.praat └── setup.praat /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.tar.xz 3 | *.zip 4 | settings 5 | isettings 6 | phon.txt 7 | test 8 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENCE.pyDAWG: -------------------------------------------------------------------------------- 1 | # encoded in utf-8. 2 | 3 | Copyright (c) 2011, Wojciech Muła 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or 7 | without modification, are permitted provided that the following 8 | conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | * Redistributions in binary form must reproduce the above 13 | copyright notice, this list of conditions and the following 14 | disclaimer in the documentation and/or other materials 15 | provided with the distribution. 16 | * Neither the name of the Wojciech Muła nor the names of its 17 | contributors may be used to endorse or promote products 18 | derived from this software without specific prior written 19 | permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 22 | CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 23 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 24 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 26 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 28 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 29 | USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 30 | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 32 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 33 | THE POSSIBILITY OF SUCH DAMAGE. 34 | # eof 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Praatalign: an interactive Praat plug-in for performing phonetic forced alignment 2 | 3 | #### Due to licencing the english and sampa models have been removed. 4 | 5 | ### Version 2.0a 6 | 7 | Praatalign is a plug-in for Praat that can be used to do forced phonetic 8 | alignment on speech signals and in particular free speech. Praatalign combines 9 | the powerful HTK toolkit with the interactivity and modularity of Praat to 10 | create an interactive, intuitive and extendable application. Currently 11 | Praatalign uses the acoustic models trained by Schiel et al. used in 12 | MAUS\[1,2\]. 13 | 14 | ### Releases 15 | 16 | [Latest manual](https://github.com/dopefishh/praatalign/releases/download/v2.0/book_2.0.pdf) 17 | 18 | Latest plugin: 19 | [.tar.xz](https://github.com/dopefishh/praatalign/releases/download/v2.0a/praatalign_2.0a.tar.xz) 20 | [.zip](https://github.com/dopefishh/praatalign/releases/download/v2.0a/praatalign_2.0a.zip) 21 | 22 | [Older releases](https://github.com/dopefishh/praatalign/releases) 23 | 24 | ### How to cite 25 | 26 | ```tex 27 | @misc{praatalign2.0a, 28 | author={Lubbers, Mart and Torreira, Francisco}, 29 | title={Praatalign: an interactive Praat plug-in for performing phonetic forced alignment}, 30 | howpublished={\url{https://github.com/dopefishh/praatalign}}, 31 | year={2013-2018}, 32 | note={Version 2.0} 33 | } 34 | ``` 35 | 36 | - IEEE: `M. Lubbers and F. Torreira, Praatalign: an interactive Praat plug-in for performing phonetic forced alignment. 2016.` 37 | - APA: `Lubbers, M., & Torreira, F. (2016). Praatalign: an interactive Praat plug-in for performing phonetic forced alignment. Retrieved from https://github.com/dopefishh/praatalign` 38 | 39 | ### Authors 40 | 41 | Mart Lubbers (mart@martlubbers.net) and Francisco Torreira 42 | 43 | ### References 44 | 45 | 1. http://www.bas.uni-muenchen.de/Bas/BasMAUS.html 46 | 2. Schiel F. (1999): Automatic Phonetic Transcription of Non-Prompted Speech, Proc. of the ICPhS 1999. San Francisco, August 1999. pp. 607-610. 47 | -------------------------------------------------------------------------------- /align.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import codecs 5 | import itertools 6 | import logging 7 | import os 8 | import phonetizer as ph 9 | import subprocess 10 | import sys 11 | 12 | 13 | def force(phonetizer, utterance, starttime, endtime, wavefile, 14 | soxbinary, hvitebinary, hcopybinary, parameterdir, 15 | basename, hdr=True, code='w'): 16 | """Wrapper for the _force function that writes the status to a file for 17 | feedback via praat module. 18 | 19 | statusses can be: 20 | done - Alignment was successfull. 21 | missox - Sox binary not found. 22 | mishcopy - HCopy binary not found. 23 | mishvite - HVite binary not found. 24 | """ 25 | status = _force( 26 | phonetizer, utterance, starttime, endtime, wavefile, 27 | soxbinary, hvitebinary, hcopybinary, parameterdir, 28 | basename, hdr, code) 29 | with open('{}.status'.format(basename), 'w') as f: 30 | f.write(status) 31 | return not status 32 | 33 | 34 | def _force(phonetizer, utterance, starttime, duration, wavefile, 35 | soxbinary, hvitebinary, hcopybinary, parameterdir, 36 | basename, hdr, code): 37 | """ 38 | Force aligns the given utterance, all parameters are passed by kwarg 39 | """ 40 | logging.info('Starting to align: {}'.format(code)) 41 | logging.info('Removing old files') 42 | for suffix in ['dot', 'htk', 'nis', 'rec', 'slf', 'status']: 43 | try: 44 | os.remove('{}.{}'.format(basename, suffix)) 45 | except: 46 | pass 47 | 48 | # Open the preconfig and extract the sourcerate 49 | sourcerate = str(1e7/625) 50 | 51 | # Load the phonetizer 52 | pron = phonetizer.phonetize(utterance) or [[['']]] 53 | 54 | canonical = [''.join(p[0]) for p in pron] 55 | logging.info('Utterance phonetized spawned') 56 | 57 | # Create the graph file 58 | dawg = phonetizer.todawg(pron) 59 | with open('{}.slf'.format(basename), 'w') as f: 60 | f.write(phonetizer.toslf(*dawg)) 61 | with open('{}.dot'.format(basename), 'w') as f: 62 | f.write(phonetizer.todot(*dawg)) 63 | logging.info('SLF file created') 64 | 65 | # Run the sound processing 66 | soxcommand = [ 67 | soxbinary, wavefile, 68 | '-t', 'sph', 69 | '-e', 'signed-integer', 70 | '-b', '16', '-c', '1', 71 | '{}.nis'.format(basename), 72 | 'trim', starttime, duration, 73 | 'rate', '-s', '-a', sourcerate] 74 | logging.info(' '.join(soxcommand)) 75 | proc = subprocess.Popen(soxcommand, 76 | stderr=subprocess.PIPE, stdout=subprocess.PIPE) 77 | out, err = proc.communicate() 78 | if proc.returncode == 127: 79 | return 'Sox binary couldn\'t be found...\n'\ 80 | 'It is searched for in {}'.format(hcopybinary) 81 | logging.info('Sox ran({}):\n\tout: {}\n\terr: {}'.format( 82 | proc.returncode, out, err)) 83 | 84 | # Run the HCopy process 85 | hcopycommand = [ 86 | hcopybinary, 87 | '-T', '0', 88 | '-C', os.path.join(parameterdir, 'PRECONFIGNIST'), 89 | '{}.nis'.format(basename), '{}.htk'.format(basename)] 90 | 91 | logging.info(' '.join(hcopycommand)) 92 | proc = subprocess.Popen(hcopycommand, 93 | stderr=subprocess.PIPE, stdout=subprocess.PIPE) 94 | out, err = proc.communicate() 95 | if proc.returncode == 127: 96 | return 'HCopy binary couldn\'t be found...\n'\ 97 | 'It is searched for in {}'.format(hcopybinary) 98 | logging.info('HCopy ran({}):\n\tout: {}\n\terr: {}'.format( 99 | proc.returncode, out, err)) 100 | 101 | # Run the HVite actual alignment 102 | hvitecommand = [ 103 | hvitebinary, 104 | '-C', os.path.join(parameterdir, 'HVITECONF'), 105 | '-w', '-X', 'slf', 106 | '-H', os.path.join(parameterdir, 'MMF'), 107 | '-s', '7.0', 108 | '-p', '6.0', 109 | os.path.join(parameterdir, 'DICT'), 110 | os.path.join(parameterdir, 'MONOPHONES'), 111 | '{}.htk'.format(basename)] 112 | logging.info(' '.join(hvitecommand)) 113 | proc = subprocess.Popen(hvitecommand, 114 | stderr=subprocess.PIPE, stdout=subprocess.PIPE) 115 | out, err = proc.communicate() 116 | if proc.returncode == 127: 117 | return 'HVite binary couldn\'t be found...\n'\ 118 | 'It is searched for in {}'.format(hvitebinary) 119 | logging.info('HVite ran({}):\n\tout: {}\n\terr: {}'.format( 120 | proc.returncode, out, err)) 121 | if proc.returncode != 0: 122 | return 'HVite failed with the following error: {}'.format(err) 123 | 124 | # Open the output file 125 | out = 'praat_temp_out' 126 | ortwords = utterance.replace(",", "").split(' ') 127 | with open(out, code) as fileio: 128 | logging.info('Output file selected') 129 | with open('{}.rec'.format(basename), 'r') as f: 130 | # Write if necessary the header 131 | if hdr != 'False': 132 | fileio.write('start,end,label,type\n') 133 | logging.info('Header written') 134 | word = None 135 | for d in itertools.imap(lambda x: x.split(), f): 136 | # Parse the time parameters and convert them to seconds 137 | start = float(starttime) + int(d[0]) / 1e7 138 | end = float(starttime) + int(d[1]) / 1e7 139 | 140 | # Detect word boundaries 141 | if d[2] == '<': 142 | word = (end, '') 143 | # If the end of a word is reached write the word to file 144 | elif d[2] == '#': 145 | fileio.write('{:f},{:f},{},w\n'.format( 146 | word[0], start, word[1])) 147 | fileio.write('{:f},{:f},{},c\n'.format( 148 | word[0], start, canonical.pop(0))) 149 | fileio.write('{:f},{:f},{},o\n'.format( 150 | word[0], start, ortwords.pop(0).encode('utf-8'))) 151 | word = (end, '') 152 | # Else add the current phone to the current word 153 | else: 154 | word = (word[0], word[1] + d[2]) 155 | # If the length is non zero write the phone to the file 156 | if end - start > 0: 157 | fileio.write('{:f},{:f},{},p\n'.format( 158 | start, end, d[2])) 159 | fileio.write('{:f},{:f},{},l\n'.format( 160 | start, end, float(d[3]))) 161 | logging.info('Datafile written') 162 | logging.info('Finished') 163 | return '' 164 | 165 | 166 | def parsesettings(filepath): 167 | with open(filepath, 'r') as f: 168 | settings = {k: v.strip() for k, v in (x.split(': ') for x in f)} 169 | return settings 170 | 171 | 172 | if __name__ == '__main__': 173 | # Load the sett 174 | sett = parsesettings('isettings') 175 | sett.update(parsesettings('settings')) 176 | 177 | # Initialize the logger 178 | logging.basicConfig(filename=sett['LOG'], level=20, 179 | format='%(created)f: %(message)s') 180 | 181 | # Load the phonetizer 182 | try: 183 | phone = ph.getphonetizer( 184 | sett['LAN'], sett['PHO'], sett['DCT'], sett['RUL']) 185 | except UnicodeError: 186 | with open('temp.status', 'w') as f: 187 | f.write('Unicode error. Check if the files are utf-8') 188 | exit() 189 | except IOError as e: 190 | if e.filename == sett['DCT']: 191 | error = 'Dictionary file couldn\'t be found\n'\ 192 | 'It is searched for in {}'.format(e.filename) 193 | elif e.filename == sett['RUL']: 194 | error = 'Ruleset file couldn\'t be found\n'\ 195 | 'It is searched for in {}'.format(e.filename) 196 | elif e.filename == sett['PHO']: 197 | error = 'Universal phonetizer file couldn\'t be found\n'\ 198 | 'It is searched for in {}'.format(e.filename) 199 | else: 200 | error = 'Some io error: ' + str(e) 201 | with open('temp.status', 'w') as f: 202 | f.write(error) 203 | exit() 204 | 205 | p = 'par.{}'.format(sett['MOD']) 206 | 207 | if sys.argv[1] == 'tier': 208 | # Read the data 209 | with codecs.open(sett['OUT'], 'r', 'utf-8') as f: 210 | data = f.readlines() 211 | 212 | # Setup the code for writing and header sett 213 | first = 0 214 | code = 'w' 215 | hdr = 'True' 216 | 217 | # Parse the data 218 | data = map(lambda x: x.strip().split('\t'), data[1:]) 219 | 220 | # Setup and align all data 221 | for i, (start, _, utt, end) in enumerate(data): 222 | if first == 0: 223 | first += 1 224 | 225 | # If neccesary unset the header and change write mode to append 226 | elif first == 1: 227 | hdr = 'False' 228 | code = 'a' 229 | 230 | # Check for empty pre and post annotations for extended bounds option 231 | if i > 0: 232 | start = max(float(start)-float(sett['THR']), 233 | float(data[i-1][3])) 234 | if i < len(data) - 1: 235 | end = min(float(end) + float(sett['THR']), 236 | float(data[i+1][0])) 237 | 238 | dur = str(float(end)-float(start)) 239 | # Do the actual alignment 240 | if not force( 241 | phone, utt, str(start), dur, sett['WAV'], 242 | sett['SOX'], sett['HVB'], sett['HCB'], p, 'temp', 243 | hdr=hdr, code=code): 244 | break 245 | elif sys.argv[1] == 'annotation': 246 | force(phone, sett['UTT'], sett['STA'], sett['DUR'], sett['WAV'], 247 | sett['SOX'], sett['HVB'], sett['HCB'], p, 'temp') 248 | -------------------------------------------------------------------------------- /alignannotation.praat: -------------------------------------------------------------------------------- 1 | include procs.praat 2 | # Get current selection 3 | selection_start = Get starting point of interval 4 | selection_end = Get end point of interval 5 | utterance$ = Get label of interval 6 | 7 | # Settings loads: phonetier_name$, wordtier_name$, cantier_name$, tmpfile$, 8 | # pythonex$, boundary_margin, llhtier_name$ 9 | @loadSettings: 10 | 11 | # Load editor and longsound info: sound_file$, sound_object$, 12 | # textgrid_object$, selected_tier, sound_duration, pitch_on, 13 | # intensity_on, spectrum_on, formant_on, pulses_on 14 | @loadFileInfo: 15 | 16 | statusfile$ = "temp.status" 17 | 18 | # Unshow pitch, intensity and spectrum if they are enabled 19 | @toggleGUIValues: 20 | 21 | # restore the selection as it might be crappy due to sound selection 22 | Select: selection_start, selection_end 23 | 24 | # Check if the previous or next interval are empty 25 | Select previous interval 26 | interval_label$ = Get label of interval 27 | if interval_label$ = "" 28 | margin_before = boundary_margin 29 | else 30 | margin_before = 0 31 | endif 32 | Select next interval 33 | Select next interval 34 | interval_label$ = Get label of interval 35 | if interval_label$ = "" 36 | margin_after = boundary_margin 37 | else 38 | margin_after = 0 39 | endif 40 | Select previous interval 41 | Zoom to selection 42 | Zoom out 43 | endeditor 44 | 45 | # Calculate the true start and end times with respect to the extended bounds 46 | selection_start = max(selection_start - margin_before, 0) 47 | selection_end = min(selection_end + margin_after, sound_duration) 48 | selection_duration = selection_end - selection_start 49 | 50 | # Get the index the tiers 51 | selectObject: textgrid_object$ 52 | 53 | phonetier_number = -1 54 | llhtier_number = -1 55 | wordtier_number = -1 56 | cantier_number = -1 57 | orttier_number = -1 58 | 59 | if phonetier_name$ <> "" 60 | @indexOfTier: phonetier_name$ 61 | phonetier_number = indexOfTier.number 62 | endif 63 | 64 | if orttier_name$ <> "" 65 | @indexOfTier: orttier_name$ 66 | orttier_number = indexOfTier.number 67 | if indexOfTier.inserted == 1 68 | if phonetier_name$ <> "" 69 | phonetier_number = phonetier_number + 1 70 | endif 71 | endif 72 | endif 73 | 74 | if llhtier_name$ <> "" 75 | @indexOfTier: llhtier_name$ 76 | llhtier_number = indexOfTier.number 77 | if indexOfTier.inserted == 1 78 | if orttier_name$ <> "" 79 | orttier_number = orttier_number + 1 80 | endif 81 | if phonetier_name$ <> "" 82 | phonetier_number = phonetier_number + 1 83 | endif 84 | endif 85 | endif 86 | 87 | if wordtier_name$ <> "" 88 | @indexOfTier: wordtier_name$ 89 | wordtier_number = indexOfTier.number 90 | if indexOfTier.inserted == 1 91 | if orttier_name$ <> "" 92 | orttier_number = orttier_number + 1 93 | endif 94 | if phonetier_name$ <> "" 95 | phonetier_number = phonetier_number + 1 96 | endif 97 | if llhtier_name$ <> "" 98 | llhtier_number = llhtier_number + 1 99 | endif 100 | endif 101 | endif 102 | 103 | if cantier_name$ <> "" 104 | @indexOfTier: cantier_name$ 105 | cantier_number = indexOfTier.number 106 | if indexOfTier.inserted == 1 107 | if orttier_name$ <> "" 108 | orttier_number = orttier_number + 1 109 | endif 110 | if phonetier_name$ <> "" 111 | phonetier_number = phonetier_number + 1 112 | endif 113 | if llhtier_name$ <> "" 114 | llhtier_number = llhtier_number + 1 115 | endif 116 | if wordtier_name$ <> "" 117 | wordtier_number = wordtier_number + 1 118 | endif 119 | endif 120 | endif 121 | 122 | # Clean up the phone tier 123 | if orttier_name$ <> "" 124 | @cleanAnnotation: textgrid_object$, orttier_number, selection_start, selection_end 125 | endif 126 | if phonetier_name$ <> "" 127 | @cleanAnnotation: textgrid_object$, phonetier_number, selection_start, selection_end 128 | endif 129 | if wordtier_name$ <> "" 130 | @cleanAnnotation: textgrid_object$, wordtier_number, selection_start, selection_end 131 | endif 132 | if cantier_name$ <> "" 133 | @cleanAnnotation: textgrid_object$, cantier_number, selection_start, selection_end 134 | endif 135 | if llhtier_name$ <> "" 136 | @cleanAnnotation: textgrid_object$, llhtier_number, selection_start, selection_end 137 | endif 138 | 139 | # Write the interval specific settings to the settings file 140 | writeFileLine("isettings", 141 | ..."STA: ", selection_start, newline$, 142 | ..."DUR: ", selection_duration, newline$, 143 | ..."UTT: ", utterance$, newline$, 144 | ..."WAV: ", sound_file$) 145 | 146 | # Do the actual alignment 147 | system 'pythonex$' align.py annotation 148 | 149 | returnstatus$ = readFile$: statusfile$ 150 | if returnstatus$ == "" 151 | @insertTableTextGrid: tmpfile$, textgrid_object$, phonetier_name$, 152 | ... orttier_name$, wordtier_name$, cantier_name$, llhtier_name$, 153 | ... phonetier_number, orttier_number, wordtier_number, cantier_number, 154 | ... llhtier_number 155 | else 156 | pauseScript: "Error! check the info window for details..." 157 | appendInfoLine: returnstatus$ 158 | endif 159 | 160 | # Reset pitch, intensity and spectrum if they were unset before 161 | editor 162 | @toggleGUIValues: 163 | -------------------------------------------------------------------------------- /aligntier.praat: -------------------------------------------------------------------------------- 1 | include procs.praat 2 | # Settings loads: phonetier_name$, wordtier_name$, cantier_name$, tmpfile$, 3 | # orttier_name$, pythonex$, boundary_margin 4 | @loadSettings: 5 | 6 | # Load editor and longsound info: sound_file$, sound_object$, 7 | # textgrid_object$, selected_tier, sound_duration, pitch_on, 8 | # intensity_on, spectrum_on, formant_on, pulses_on 9 | @loadFileInfo: 10 | 11 | # Extract the tier 12 | Extract entire selected tier 13 | endeditor 14 | 15 | # Select the tier and convert it to a table and write it to a file 16 | extracted_tier$ = selected$("TextGrid", 1) 17 | Down to Table: "no", 6, "yes", "no" 18 | Save as tab-separated file: tmpfile$ 19 | 20 | # Remove all the created temporary objects 21 | Remove 22 | select TextGrid 'extracted_tier$' 23 | Remove 24 | 25 | # Write the tier specific settings 26 | writeFileLine("isettings", 27 | ..."WAV: ", sound_file$) 28 | 29 | # Remove the tiers if they already exist 30 | selectObject: textgrid_object$ 31 | number_of_tiers = Get number of tiers 32 | tiernumber = 1 33 | while tiernumber < number_of_tiers 34 | nametier$ = Get tier name: tiernumber 35 | if nametier$ = phonetier_name$ or nametier$ = wordtier_name$ or nametier$ = cantier_name$ or nametier$ = llhtier_name$ or nametier$ = orttier_name$ 36 | Remove tier: tiernumber 37 | else 38 | tiernumber = tiernumber + 1 39 | endif 40 | number_of_tiers = Get number of tiers 41 | endwhile 42 | 43 | # Get the index the tiers 44 | selectObject: textgrid_object$ 45 | 46 | phonetier_number = -1 47 | llhtier_number = -1 48 | wordtier_number = -1 49 | cantier_number = -1 50 | orttier_number = -1 51 | 52 | if phonetier_name$ <> "" 53 | @indexOfTier: phonetier_name$ 54 | phonetier_number = indexOfTier.number 55 | endif 56 | 57 | if orttier_name$ <> "" 58 | @indexOfTier: orttier_name$ 59 | orttier_number = indexOfTier.number 60 | if indexOfTier.inserted == 1 61 | if phonetier_name$ <> "" 62 | phonetier_number = phonetier_number + 1 63 | endif 64 | endif 65 | endif 66 | 67 | if llhtier_name$ <> "" 68 | @indexOfTier: llhtier_name$ 69 | llhtier_number = indexOfTier.number 70 | if indexOfTier.inserted == 1 71 | if phonetier_name$ <> "" 72 | phonetier_number = phonetier_number + 1 73 | endif 74 | if orttier_name$ <> "" 75 | orttier_number = orttier_number + 1 76 | endif 77 | endif 78 | endif 79 | 80 | if wordtier_name$ <> "" 81 | @indexOfTier: wordtier_name$ 82 | wordtier_number = indexOfTier.number 83 | if indexOfTier.inserted == 1 84 | if phonetier_name$ <> "" 85 | phonetier_number = phonetier_number + 1 86 | endif 87 | if orttier_name$ <> "" 88 | orttier_number = orttier_number + 1 89 | endif 90 | if llhtier_name$ <> "" 91 | llhtier_number = llhtier_number + 1 92 | endif 93 | endif 94 | endif 95 | 96 | if cantier_name$ <> "" 97 | @indexOfTier: cantier_name$ 98 | cantier_number = indexOfTier.number 99 | if indexOfTier.inserted == 1 100 | if phonetier_name$ <> "" 101 | phonetier_number = phonetier_number + 1 102 | endif 103 | if orttier_name$ <> "" 104 | orttier_number = orttier_number + 1 105 | endif 106 | if llhtier_name$ <> "" 107 | llhtier_number = llhtier_number + 1 108 | endif 109 | if wordtier_name$ <> "" 110 | wordtier_number = wordtier_number + 1 111 | endif 112 | endif 113 | endif 114 | 115 | # Do the actual alignment 116 | system 'pythonex$' align.py tier 117 | 118 | # Close the editor for more speed 119 | editor: textgrid_object$ 120 | Close 121 | endeditor 122 | 123 | @insertTableTextGrid: tmpfile$, textgrid_object$, phonetier_name$, 124 | ... orttier_name$, wordtier_name$, cantier_name$, llhtier_name$, 125 | ... phonetier_number, orttier_number, wordtier_number, cantier_number, 126 | ... llhtier_number 127 | 128 | # Reselect the TextGrid and re-open editor 129 | selectObject: textgrid_object$ 130 | plusObject: sound_object$ 131 | Edit 132 | -------------------------------------------------------------------------------- /book/.chktexrc: -------------------------------------------------------------------------------- 1 | VerbEnvir { texttt } 2 | WipeArg { 3 | \texttt:{} 4 | } 5 | -------------------------------------------------------------------------------- /book/.gitignore: -------------------------------------------------------------------------------- 1 | *.eps 2 | *.aux 3 | *.fmt 4 | *.log 5 | *.out 6 | *.pdf 7 | *.toc 8 | *.bbl 9 | *.blg 10 | *.dvi 11 | *.mlog 12 | -------------------------------------------------------------------------------- /book/Makefile: -------------------------------------------------------------------------------- 1 | DOC:=book 2 | LATEX:=latex 3 | BIBTEX:=bibtex 4 | LATEXFLAGS:=-file-line-error -halt-on-error -no-shell-escape 5 | 6 | TEXS:=$(wildcard *.tex) 7 | 8 | .PHONY: all 9 | .SECONDARY: $(DOC).fmt 10 | 11 | all: $(DOC).pdf 12 | 13 | %.fmt: preamble.tex 14 | $(LATEX) $(LATEXFLAGS) -ini -jobname="$(basename $@)" "&$(LATEX) $<\dump" 15 | 16 | %.pdf: %.dvi 17 | dvipdfm $< 18 | 19 | %.dvi: %.tex %.fmt %.bib $(TEXS) 20 | $(LATEX) $(LATEXFLAGS) $< 21 | grep -q '^\\bibdata{' $(basename $<).aux && $(BIBTEX) $(basename $<) || true 22 | $(LATEX) $(LATEXFLAGS) $< | tee $(basename $<).mlog 23 | grep -iqF 'rerun' $(basename $<).mlog &&\ 24 | $(LATEX) $(LATEXFLAGS) $< || true 25 | 26 | clean: 27 | $(RM) $(addprefix $(DOC).,aux log fmt toc bbl blg mlog run.xml out pdf) 28 | -------------------------------------------------------------------------------- /book/book.bib: -------------------------------------------------------------------------------- 1 | @inproceedings{schiel1999, 2 | address = {San Francisco}, 3 | title = {Automatic phonetic transcription of non-prompted speech}, 4 | booktitle = {Proc. of the {ICPhS} 1999}, 5 | author = {Schiel, Florian}, 6 | year = {1999}, 7 | pages = {607--610}, 8 | } 9 | -------------------------------------------------------------------------------- /book/book.tex: -------------------------------------------------------------------------------- 1 | %&book 2 | \begin{document} 3 | \cleardoublepage% 4 | \maketitle 5 | \setcounter{page}{1} 6 | \tableofcontents 7 | \chapter{Introduction} 8 | \section{Introduction} 9 | Praatalign is a plug-in for Praat that can be used to do forced phonetic 10 | alignment on speech signals and in particular free speech. Praatalign combines 11 | the powerful HTK toolkit and the well trained MAUS models with the 12 | interactivity and modularity of Praat to create an interactive, intuitive and 13 | extendible application. Text in \texttt{monospace} means that the word is a 14 | command, variable or value. Praatalign has the capability to work with 15 | python-programmed phonetizers, practical orthography, dictionaries and 16 | pronunciation rules. 17 | 18 | \chapter{Installation} 19 | \section{Preparation} 20 | The installation of the program is very straightforward, however installing the 21 | dependencies might not be on some systems. Some programs are not included in 22 | the package due to licencing and environment compatibility but they do need to 23 | be installed in order for the plug-in to work properly. All programs Praatalign 24 | depends on are free and open source. The following list of programs need to be 25 | installed with their installation instructions. 26 | \begin{itemize} 27 | \item \textbf{Praat}\\ 28 | Praat is a program that allows you to do phonetic analysis and annotations 29 | with a computer and Praatalign uses Praat to provide an interactive user 30 | interface to the annotated sound files. 31 | \begin{itemize} 32 | \item \textbf{Windows}\\ 33 | You can download and install Praat with the instructions on 34 | \url{http://www.fon.hum.uva.nl/praat/download_win.html}. 35 | \item \textbf{Mac}\\ 36 | You can download and install Praat with the instructions on 37 | \url{http://www.fon.hum.uva.nl/praat/download_mac.html}. 38 | \item \textbf{Linux and other *NIX}\\ 39 | In most of the cases the standard package manager coming with the 40 | Linux distribution also Praat. However this is usually an old version 41 | so use with care. You can download and install the latest Praat with 42 | the instructions on 43 | \url{http://www.fon.hum.uva.nl/praat/download_linux.html}. 44 | \end{itemize} 45 | \item \textbf{Python}\\ 46 | Python is used to interpret the scripts that run the core of the aligner. 47 | The script uses specifically Python 2. Python 3 is not supported. All 48 | scripts are tested with Python $2.7.x$, older version might work. 49 | \begin{itemize} 50 | \item \textbf{Windows}\\ 51 | You can download and install from the Windows MSI installer found on 52 | this page under the Latest Python 2 Release link 53 | \url{https://www.python.org/downloads/windows/}. 54 | \item \textbf{Mac}\\ 55 | Mac OS X 10.8 comes with Python $2.7$ preinstalled. If you run an 56 | obscure version without Python you can download it on 57 | \url{https://www.python.org/downloads/mac-osx/}. 58 | \item \textbf{Linux and other *NIX}\\ 59 | In almost all cases the standard package manager comes with a 60 | sufficiently up to date Python version and most of the distributions 61 | have Python preinstalled. If you work with an obscure system that does 62 | not have a package manager or the Python version in it is obselete you 63 | can download Python on \url{https://www.python.org/downloads/source/}. 64 | \end{itemize} 65 | \item \textbf{SoX}\\ 66 | For processing the sound files in a very detailed and controlled way we 67 | use SoX. Although Praat also has sound processing capabilities SoX works 68 | better is some situations, this is because Praat does not allow you to 69 | specify certain options like sampling rate for all formats. 70 | \begin{itemize} 71 | \item \textbf{Windows}\\ 72 | You can download SoX on 73 | \url{http://sourceforge.net/projects/sox/files/sox/}. Either download 74 | the executable and run it or unzip the zipfile. 75 | \item \textbf{Mac}\\ 76 | You can download SoX on 77 | \url{http://sourceforge.net/projects/sox/files/sox/}. Download the 78 | zipfile and drag the contects to you \texttt{Applications}. Another 79 | option is to use homebrew and type \texttt{brew install sox} in a 80 | terminal. 81 | \item \textbf{Linux and other *NIX}\\ 82 | In almost all cases the standard package manager comes with a 83 | sufficiently up to date SoX version. If you work with an obscure system 84 | that does not have a package manager or the SoX version in it is 85 | obselete it on 86 | \url{http://sourceforge.net/projects/sox/files/sox/14.4.2/} 87 | \end{itemize} 88 | \item \textbf{HCopy \& HVite}\\ 89 | HCopy and HVite are programs from the HTK toolkit and due to licencing 90 | issues we can not provide the binaries in a direct way. The program is for 91 | free but you are not allowed to distribute it. 92 | \begin{itemize} 93 | \item \textbf{Windows}\\ 94 | You can download a zip file containing precompiled Windows executables 95 | on \url{http://htk.eng.cam.ac.uk/ftp/software/htk-3.3-windows-binary.zip}. 96 | Just unzip them and make sure to add them to \texttt{\%PATH\%} or to 97 | point Praatalign to it in the setup screen. 98 | \item \textbf{Mac, Linux and other *NIX}\\ 99 | Installing HCopy and HVite is probably the hardest on Linux and Mac 100 | since you need to compile the binaries yourself. You can find the 101 | latest version on \url{http://htk.eng.cam.ac.uk/ftp/software/}. 102 | Download the zipfile, extract the zipfile and go to the folder with 103 | your terminal. While you can compile the entire toolkit, Praatalign is 104 | only interested in HCopy and HVite. Thus the following commands 105 | suffice: 106 | \begin{lstlisting}[language=bash] 107 | $ ./configure --disable-hlmtools --disable-hslab 108 | $ make -j4 htktools 109 | \end{lstlisting} 110 | When the compilation has succeeded you can either add the binary 111 | directory \texttt{HTKTools} to \texttt{\$PATH} or point Praatalign to 112 | the binaries in the setup screen. 113 | \end{itemize} 114 | \end{itemize} 115 | 116 | \section{Installation}\label{sec:installation} 117 | The installation of the plug-in is very easy but the method differs for 118 | different systems.\footnote{The plugin is tested on Windows 7, Windows Server 119 | 2008 via Windows Terminal Services, Windows 10, Linux and Mac. Other versions 120 | or other operating systems might also work but are untested.} 121 | 122 | \subsection{Automated installation} 123 | Run the installation script for your system. 124 | \begin{itemize} 125 | \item \textbf{Mac, Linux or *NIX}: \texttt{install.sh}\\ 126 | Depending on the operating system you either have to run the script from 127 | the terminal or double click it from some explorer like program. Running 128 | the script from the terminal is very easy and preferred. Just start a 129 | terminal program and type the full and exact path in the terminal and press 130 | enter. For example on a Mac this will something like be:\\ 131 | \texttt{/Users/frobnicator/Downloads/praatalign/install.sh}. 132 | \item \textbf{Windows}: \texttt{install\_win.bat}\\ 133 | This installation script can be double clicked from the explorer and it 134 | will install Praatalign. When the installation is finished you can press 135 | Enter to make the windows disappear. 136 | \end{itemize} 137 | 138 | \subsection{Manual installation} 139 | Copy the contents of the root directory to (you have to create the directory if 140 | it does not already exist): 141 | \begin{itemize} 142 | \item \textbf{Linux and *NIX}\\ 143 | \texttt{\$\{HOME\}/.praat-dir/plugin\_pralign/}. 144 | \item \textbf{Windows}\\ 145 | \texttt{\%USERPROFILE\%\textbackslash{} Praat\textbackslash{} plugin\_pralign/} 146 | \item \textbf{Mac}\\ 147 | \texttt{\$\{HOME\}/Library/Preferences/Praat Prefs/plugin\_pralign/} 148 | \end{itemize} 149 | 150 | \chapter{Documentation} 151 | \section{General information} 152 | With the Praatalign plugin you can currently align out of the box the data 153 | using Spanish and Dutch acoustic models from 154 | MAUS~\footnote{\url{http://www.bas.uni-muenchen.de/Bas/BasMAUS.html}} created 155 | by Schiel et al.~\cite{schiel:999}. These models are already included in the 156 | package. %Other models from MAUS can be easily added and will be in the future. 157 | %If you like to have a language added please contact us. 158 | %Presets for Australian English, Estonian, German, Hungarian, Italian, New 159 | %Zealand English Polish and Portuguese are available at minimum. 160 | 161 | Dictionary, ruleset, universal phonetizer all other files are, and should be, 162 | encoded in \texttt{UTF-8}. To enforce this the plug-in changes the default 163 | behaviour of Praat every time Praat loads to make sure Praats reading and 164 | writing preferences are set to \texttt{UTF-8}. 165 | 166 | When the plug-in is successfully installed several menu items are added in the 167 | \texttt{TextGrid} editor under the \texttt{Interval} menu. The added 168 | functionality only works when you are editing a \texttt{TextGrid} and a 169 | \texttt{LongSound} or a \texttt{Sound}\footnote{Sound files are written to disk 170 | prior to alignment, thus \texttt{Longsound} is preferred}. Currently the plugin 171 | is only tested on \texttt{WAVE} files. It should however work on all sound 172 | filetypes SoX can detect from the extension. 173 | 174 | \section{Menu items} 175 | Almost all menu items will fail when there is no settings file present. The 176 | settings file has to be created by running \texttt{Set up forced 177 | alignment\ldots} interactively or by running \texttt{settings\_ni.praat} in a 178 | script. 179 | 180 | \subsection{\texttt{Generate dictionary from tier}} 181 | This functions allows the user to generate a dictionary containing all the 182 | missing or unphonetizable words from the currently selected tier using the 183 | current settings the plugin is initialized with. The plugin will prompt you 184 | after pressing the button for a location for the dictionary file. When this 185 | process is done the user can add the pronunciations after every entry that 186 | is found in the skeleton dictionary. Note that there is no sanitation 187 | applied on the words. This means that if the phonetizer removes punctuation 188 | it can still be present in the dictionary. 189 | 190 | \subsection{\texttt{Clean selection}} 191 | This function is a helper function to clean up old or wrong alignment. When the 192 | function runs all annotation data within the selection within the selected tier 193 | will be removed. 194 | 195 | \emph{Note that this is not necessary to do before an alignment because this 196 | function runs by default before any alignment.} 197 | 198 | \subsection{\texttt{Align current interval}} 199 | This function aligns the current selected interval on the current selected 200 | tier. When selecting a small interval it should not take much time at all. When 201 | you select an interval from an output tier (phone, ort, word, canonical or log) 202 | the function will prompt you to make sure this is what you want to do. This 203 | functions first clears out the annotations on the output tiers. 204 | 205 | \subsection{\texttt{Align current tier}} 206 | This function aligns the entire selected tier. Aligning an entire tier can take 207 | some time, especially when you have a lot of pronunciation variants. This 208 | functions first clears out the annotations on the output tiers. 209 | 210 | \subsection{\texttt{Set up forced alignment\ldots}} 211 | This functions spawns two option menus that will create the necessary settings 212 | file. When you finish the forms a settings file is written to disk. 213 | 214 | \subsubsection{Basic options} 215 | The first form contains all the basic settings needed for alignment. It also 216 | shows the version of the plugin on the first line. The following options can be 217 | entered. All settings are analogous to the names of the settings in the 218 | \texttt{settings\_ni.praat} script. 219 | \begin{itemize} 220 | \item \texttt{new}: % 221 | Name of the output tier storing the phone level alignment 222 | 223 | In this option you specify the name of the tier where the phone level 224 | alignment is stored, this can be either an existing tier or a non existing 225 | tier. If the tier does not exist it will be created upon doing the first 226 | alignment. When you leave this field empty no phone level tier will be 227 | created. 228 | \item \texttt{ort}: % 229 | Name of the output tier storing the word level orthographic alignment 230 | 231 | In this option you specify the name of the tier where the word level 232 | orthographic alignment is stored, this can be either an existing tier 233 | or a non existing tier. If the tier does not exist it will be created 234 | upon doing the first alignment. When you leave this field empty no 235 | word level orthographic level tier will be created. 236 | \item \texttt{wrd}: % 237 | Name of the output tier storing the word level alignment 238 | 239 | In this option you specify the name of the tier where the word level 240 | alignment is stored, this can be either an existing tier or a non existing 241 | tier. If the tier does not exist it will be created upon doing the first 242 | alignment. When you leave this field empty no word level tier will be 243 | created. 244 | \item \texttt{can}: % 245 | Name of the output tier storing the canonical pronunciation 246 | 247 | In this option you specify the name of the tier where the canonical 248 | pronunciation of every word is stored, this can be either an existing tier 249 | or a non existing tier. If the tier does not exist it will be created upon 250 | doing the first alignment. When you leave this field empty no canonical 251 | pronunciation tier will be created. 252 | \item \texttt{llh}: % 253 | Name of the output tier storing the log likelihood 254 | 255 | In this option you specify the name of the tier where the log likelihood of 256 | every phone is stored, this can be either an existing tier or a non 257 | existing tier. If the tier does not exist it will be created upon doing the 258 | first alignment. When you leave this field empty no log likelihood tier 259 | will be created. 260 | \item \texttt{model}: % 261 | Select model 262 | 263 | In this option you specify which acoustic model to use. More info about the 264 | models can be found in Section~\ref{sec:models}. 265 | \item \texttt{lan}: % 266 | Select model 267 | 268 | In this option you specify which phonetizer to use. More info about the 269 | models can be found in Section~\ref{sec:phonetizers}. 270 | 271 | \emph{Note that if you select the universal phonetizer you will be prompted 272 | to select the universal phonetizer file. More about the universal 273 | phonetizer in Section~\ref{sec:univphonetizer}} 274 | \item \texttt{dic}, \texttt{dictionary}: % 275 | Select a dictionary file 276 | 277 | In this option you can specify a dictionary file. When you tick the box you 278 | will be prompted to select a dictionary file. When a dictionary is already 279 | selected the \texttt{dictionary} option is added which contains the path to 280 | the file. When you want to switch to using no dictionary you can clear that 281 | box and leave the \texttt{dic} box unticked. 282 | \item \texttt{rul}, \texttt{ruleset}: % 283 | Select a ruleset file 284 | 285 | In this option you can specify a ruleset file. When you tick the box you 286 | will be prompted to select a ruleset file. When a ruleset is already 287 | selected the \texttt{ruleset} option is added which contains the path to 288 | the file. When you want to switch to using no ruleset you can clear that 289 | box and leave the \texttt{rul} box unticked. 290 | \item \texttt{pho}, \texttt{phonetizer}: % 291 | Select a universal phonetizer file 292 | 293 | This option will only appear if a phonetizer file has been set 294 | previously. When no phonetizer file has ever been set and the universal 295 | phonetizer is used you will be prompted for it anyways since it is 296 | mandatory. When you tick the box you 297 | will be prompted to select a phonetizer file. The current phonetizer 298 | file is shown in \texttt{phonetize} 299 | \item \texttt{thr}: % 300 | Set the size to add to the annotations 301 | 302 | In this option you can specify an extra margin used for every annotation. 303 | When the annotations are placed to close to the real sound the initial 304 | pause can clobber up the beginning of speech and that can reduce the 305 | performance. Setting the \texttt{thr} value to $0.1$ will for example increase 306 | all boundaries from annotations with $100$ms. Note that this does not 307 | change the original annotation and it will only increase the widen the 308 | annotation when there is room to do so, meaning that it will not create 309 | overlap with other annotations. 310 | \end{itemize} 311 | 312 | \subsubsection{Advanced options} 313 | The second form contains all the more advanced settings needed for alignment. 314 | It shouldn't be necessary to change these options regularly. All settings are 315 | analogous to the names of the settings in the \texttt{settings\_ni.praat} 316 | script. 317 | 318 | \begin{itemize} 319 | \item \texttt{log}: % 320 | Set a location for the logfile 321 | 322 | In this option you can specify a location to write a debug log to. When you 323 | want to switch to not using a logfile you can redirect the log to either 324 | \texttt{/dev/null} on Linux, Mac and other *NIX systems and \texttt{nul} on 325 | Windows. 326 | \item \texttt{sox}: % 327 | Set a SoX executable 328 | 329 | In this option you can specify a SoX executable. When you tick the box you 330 | will be prompted to select a SoX executable. When a SoX executable is 331 | already selected the \texttt{soxex} option is added which contains the path 332 | to the executable. When you want to switch to using the SoX executable in 333 | \texttt{PATH} you can clear that box leave the \texttt{sox} box unticked. 334 | 335 | \begin{itemize} 336 | \item \textbf{Windows}\\ 337 | If you have installed sox using the MSI you can find \texttt{sox.exe} 338 | in \texttt{C:\textbackslash% 339 | Program Files (x86)\textbackslash{} sox{-}14{-}4{-}1} or 340 | \texttt{C:\textbackslash{} Program Files\textbackslash{} 341 | sox{-}14{-}4{-}1}. 342 | If you just downloaded the zip file you can just point to the location 343 | you extracted the archive and select \texttt{sox.exe}. 344 | \item \textbf{Mac}\\ 345 | If you dragged sox to the \texttt{Applications} you can find it there 346 | and you can just point to the sox executable. If you installed sox via 347 | homebrew it is probably already in \texttt{\$PATH}. If this is not the 348 | case you can find the location by typing in a terminal \texttt{which 349 | sox} and pointing Praatalign to that location. 350 | \item \textbf{Linux and other *NIX}\\ 351 | If you have installed sox using a package manager it probably already 352 | is in your \texttt{\$PATH}. If this is not the case you can find the 353 | location by typing in a terminal: \texttt{which sox} and pointing 354 | Praatalign to location. 355 | \end{itemize} 356 | \item \texttt{hvite}: % 357 | Set a HVite executable 358 | 359 | In this option you can specify a HVite executable. When you tick the box you 360 | will be prompted to select a HVite executable. When a HVite executable is 361 | already selected the \texttt{hviteex} option is added which contains the 362 | path to the executable. When you want to switch to using the HVite 363 | executable in \texttt{PATH} you can clear that box leave the \texttt{hvite} 364 | box unticked. 365 | 366 | \begin{itemize} 367 | \item \textbf{Windows}\\ 368 | Point to the directory where you unzipped the file 369 | from HTK and select \texttt{HVite.exe}. 370 | \item \textbf{Mac, Linux, and other *NIX}\\ 371 | Point to the directory where you compiled the tools from HTK and 372 | select \texttt{HVite}. It resides in the \texttt{HTKTools} 373 | directory. 374 | \end{itemize} 375 | \item \texttt{hcopy}: % 376 | Set a HCopy executable 377 | 378 | In this option you can specify a HCopy executable. When you tick the box you 379 | will be prompted to select a HCopy executable. When a HCopy executable is 380 | already selected the \texttt{hcopyex} option is added which contains the 381 | path to the executable. When you want to switch to using the HCopy 382 | executable in \texttt{PATH} you can clear that box leave the \texttt{hcopy} 383 | box unticked. 384 | 385 | \begin{itemize} 386 | \item \textbf{Windows}\\ 387 | Point to the directory where you unzipped the file 388 | from HTK and select \texttt{HCopy.exe}. 389 | \item \textbf{Mac, Linux, and other *NIX}\\ 390 | Point to the directory where you compiled the tools from HTK and 391 | select \texttt{HCopy}. It resides in the \texttt{HTKTools} 392 | directory. 393 | \end{itemize} 394 | \item \texttt{python}: % 395 | Set a Python executable 396 | 397 | In this option you can specify a Python executable. When you tick the box 398 | you will be prompted to select a Python executable. When a Python 399 | executable is already selected the \texttt{pythonex} option is added which 400 | contains the path to the executable. When you want to switch to using the 401 | Python executable in \texttt{PATH} you can clear that box leave the 402 | \texttt{python} box unticked. 403 | 404 | \begin{itemize} 405 | \item \textbf{Windows}\\ 406 | Python can usually be found in \texttt{C:\textbackslash{} Python27}. 407 | From there you can select \texttt{python.exe} 408 | \item \textbf{Mac, Linux, and other *NIX}\\ 409 | If you have installed sox using a package manager it probably 410 | already is in your \texttt{\$PATH}. If this is not the case you can 411 | find the location by typing in a terminal: \texttt{which python} 412 | and pointing Praatalign to that location. Note that in some systems 413 | \texttt{python} symlinks to \texttt{python3}, in that case point 414 | Praatalign to \texttt{python2}. If you still can not find the 415 | executable you can try using the search function in the file 416 | manager. 417 | \end{itemize} 418 | \end{itemize} 419 | 420 | \section{Dictionary} 421 | To phonetize words Praatalign either uses the provided phonetizer or a 422 | dictionary. Dictionaries are plain text files that contain words and one or 423 | more pronunciations. A dictionary file is a \texttt{UTF-8} encoded file 424 | containing non-empty lines separated by a newline character\footnote{On Mac, 425 | Linux, and *NIX this is default, on Windows this can cause problems. When using 426 | Praatalign on windows please refrain to a text editor that has newline 427 | capabilities like Notepad++}. Lines starting with a \texttt{\#} will be ignored 428 | and can thus be used as comments. The format of a dictionary entry is a word 429 | followed by a tab followed by tab separated pronunciations. An example 430 | dictionary can be found in Listing~\ref{lst:exampledictionary} 431 | 432 | \begin{lstlisting}[caption={Example dictionary},label={lst:exampledictionary}] 433 | # This is comment 434 | # This is a word with two possible pronunciations 435 | adoa d oa o 436 | # These are words with one possible pronunciation 437 | empatare m p a t a r 438 | empatarane m p a t a r a n 439 | \end{lstlisting} 440 | 441 | \section{Ruleset} 442 | Besides generating pronunciation by using the dictionary and phonetization you 443 | can also use rulesets to define pronunciation variants. Ruleset make you able 444 | to define general rules applied over all words (phonetized words and dictionary 445 | words). In this way you can easily define for example deletion rules. 446 | A ruleset file is a \texttt{UTF-8} encoded file containing non-empty lines 447 | separated by a newline character. Lines starting with a \texttt{\#} will be 448 | ignored and can thus be used as comments. There are two ways of defining rules 449 | for a ruleset. 450 | \begin{itemize} 451 | \item \textbf{Simple}\\ 452 | Simple rules are just find and replace queries. The first column is the 453 | target and the second column is the replace value. For example the deletion 454 | rule \texttt{a d o -> a o} can be written as \texttt{a d oa o}. 455 | \item \textbf{Regular}\\ 456 | Regular rules are like regular expressions and are therefore much more 457 | expressive but also more complicated to write. Regular rules start with a 458 | single tab character to denote that they are in fact regular expressions. 459 | For example a deletion rule that deletes a \texttt{d} between two vowels 460 | and potentially also across word boundaries can be written like 461 | \verb|([aouie] #?) d ([aouie])\1\2| 462 | Internally we use the \texttt{re.sub} function from the Python \texttt{re} 463 | library\footnote{\url{https://docs.python.org/2/library/re.html}}. Besides 464 | that there are some extra shortcuts and options: 465 | \begin{itemize} 466 | \item \verb|\v| for vowels (\verb|[aoeiu]|) 467 | \item \verb|\c| for consonants (\verb|[~aoeiu]|) 468 | \item \verb|#| for a inter word silence 469 | \end{itemize} 470 | \end{itemize} 471 | 472 | 473 | \section{Phonetizers}\label{sec:phonetizers} 474 | \subsection{Spanish} 475 | The Spanish phonetizer is designed only to work with the spanish models. It 476 | removes a lot of non speech annotated symbols and does some tricks to get 477 | exceptions well phonetized. It can be seen as an example of writing an advanced 478 | phonetizer in Python. 479 | 480 | %\subsection{Tzeltal} 481 | %The Tzeltal phonetizer is an example of how to use the SAMPA models to align a 482 | %new language. Thus it only works for the SAMPA models. It removes some non 483 | %speech annotated symbols and does an almost literal character to character 484 | %translation. 485 | 486 | \subsection{Universal}\label{sec:univphonetizer} 487 | When you select the universal phonetizer you will be prompted to point the 488 | plugin to an universal phonetizer file. 489 | A universal phonetizer file is a \texttt{UTF-8} encoded file containing 490 | non-empty lines separated by a newline character. Every line contains a 491 | translation from practical orthography to phonetic transcription and the order 492 | of appearance in the file is the order of importance in the phonetizer. When a 493 | word gets phonetized the phonetizer tries to match the first rule in the 494 | phonetizer file. For example see the start of an example file listed in 495 | Listing~\ref{lst:exampleuniversalphonetizer} representing a translation from 496 | spanish orthography to phonetic transcription. The safest order is always the 497 | order in which the biggest sections are topmost in the file. 498 | 499 | \begin{lstlisting}[caption={Example universal phonetizer file}, 500 | label={lst:exampleuniversalphonetizer}, 501 | literate={ü}{{\"u}}1 {ñ}{{\~n}}1 {ç}{{\c{c}}}1 {í}{{\'e}}1] 502 | gueg 503 | guig 504 | cht S 505 | ceT 506 | ciT 507 | cíT 508 | güg u 509 | lljj 510 | quk 511 | ñJ 512 | çT 513 | jx 514 | ck 515 | vb 516 | wb 517 | zT 518 | yj 519 | qk 520 | ... 521 | \end{lstlisting} 522 | 523 | \subsection{None} 524 | The None phonetizer is a dummy phonetizer that does nothing. This means that 525 | every word should be available in the dictionary. 526 | 527 | \section{Scriptability and batch processing} 528 | Note that this section is not updated as often as it should. Always check the 529 | exact format in \texttt{settings\_ni.praat}. 530 | 531 | \subsection{Non interactive settings file creation} 532 | Although the Praatalign script is inherently interactive it is still possible 533 | to batch process corpora using simple praat scripts. To facilitate this 534 | function a file called \texttt{\$DIR/settings\_ni.praat} can be run where 535 | \texttt{\$DIR} is the location of the plugin files. The location of the plugin 536 | files for your operating system can be found in Section~\ref{sec:installation} 537 | in the manual installation section. The \texttt{settings\_ni.praat} is a 538 | stripped down version of the settings dialog present in the aligner. Since it 539 | is using a praat form to ask for the user input, in contrary to the pause 540 | dialog in the normal settings scirpt, it can be run non interactively by 541 | running the script from a praat script. 542 | 543 | For example if you want to setup the aligner to align a spanish file with all 544 | custom values on linux under the user frobnicator you can put this in your 545 | script to setup the aligner: 546 | 547 | \begin{lstlisting} 548 | runScript: "/home/frobnicator/.praat-dir/plugin_pralign/settings_ni.praat", 549 | ..."phon", "wrd", "can", "llh", "spa", "spanish", "None", "/path/to/dict", 550 | ..."/path/to/ruleset", 0, "/some/path/to/logfile", 551 | ..."/usr/bin/sox", "/usr/bin/HVite", "/usr/bin/HCopy", "python" 552 | \end{lstlisting} 553 | 554 | Note that due to the lack of interactivity the format is a little bit 555 | different. The differences are: 556 | \begin{itemize} 557 | \item \texttt{lan} must be the language code as in the interactive settings. 558 | \item \texttt{mod} must be the model code as in the interactive settings. 559 | \item \texttt{pho} must be the path to the universal phonetizer. When you do 560 | not want to use a universal phonetizer you must use \texttt{None}. 561 | \item \texttt{dic} and \texttt{rul} must be the full path, when you do not 562 | want to use a dictionary or ruleset you must use \texttt{None}. 563 | \item \texttt{sox}, \texttt{hvb}, \texttt{hcb}, and \texttt{py} must be the 564 | full path, when you do not want a custom location you must use respectively 565 | \texttt{sox}, \texttt{HVite}, \texttt{HCopy} and \texttt{python} 566 | \end{itemize} 567 | 568 | \subsection{Example} 569 | When you then open a \texttt{TextGrid} and a \texttt{LongSound} file and do 570 | \texttt{View \& Edit} to open the editor you can run the alignment from the 571 | script using the button text as function. For example the script could look 572 | like the script in Listing~\ref{lis:scriptab}. 573 | 574 | \begin{lstlisting}[caption={Example scriptability},label={lis:scriptab}] 575 | # We assume the LongSound and TextGrid are selected previously 576 | 577 | # Generate the settings file 578 | runScript: "/home/frobnicator/.praat-dir/plugin_pralign/settings_ni.praat", 579 | ..."phon", "wrd", "can", "llh", "spa", "spanish", "None", "/path/to/dict", 580 | ..."/path/to/ruleset", 0, "/some/path/to/logfile", 581 | ..."/usr/bin/sox", "/usr/bin/HVite", "/usr/bin/HCopy", "python" 582 | 583 | # Spawn the editor 584 | View & Edit 585 | 586 | # Open the editor 587 | editor: "TextGrid " + objectname$ 588 | # This bit of code is a small snippet to select a specific tier with the 589 | # index: tiernum, tiernum is obtained by querying all tiers outside the 590 | # editor and finding the tier that matches the name 591 | currenttiernum = -1 592 | while currenttiernum <> tiernum 593 | Select next tier 594 | inf$ = Editor info 595 | currenttiernum = extractNumber(inf$, "Selected tier: ") 596 | endwhile 597 | 598 | # Do the actual alignment 599 | Align current tier 600 | # When this is done aligned data can be found in: custom_phone_tier and 601 | # custom_word_tier. 602 | endeditor 603 | \end{lstlisting} 604 | 605 | \section{Troubleshooting} 606 | \begin{itemize} 607 | \item \textbf{Some words are ignored and thus not aligned}\\ 608 | Some phonetizers phonetize unphonetizable words into an empty word to avoid 609 | throwing exceptions. When words are not phonetized it means that it is not 610 | in the dictionary nor phonetizable. To fix this you can edit the phonetizer 611 | or add the word to the dictionary. 612 | \item Pop-up stating: \textbf{Error! check the text window for details\ldots}\\ 613 | This means something went wrong in the python script. Check the info 614 | window. Usually it is a missing binary, ruleset etc. It could also be 615 | that you used a phone that does not exist in the model 616 | \item Pop-up stating: \textbf{Unknown IO error}\\ 617 | Some IO error occurred of an unknown type. Please check the logfile. 618 | \item Log stating: \texttt{sox FAIL trim: Position 1 is behind the 619 | following position.} 620 | You are aligning an annotation that lies outside the wave file. 621 | \item \textbf{Other errors}\\ 622 | When the plugin crashes without any reason you should enable logging in the 623 | settings menu to see where it crashes. If the problem is not solvable 624 | please file a bugreport via 625 | github~\footnote{\url{https://github.com/dopefishh/praatalign/issues}} or 626 | contact us directly via e-mail. 627 | \end{itemize} 628 | 629 | \chapter{Extending Praatalign} 630 | \section{Introduction} 631 | %Extending the aligner with new languages should be very easy for languages that 632 | %can be mapped on the current SAMPA model or on any other existing model (maus 633 | %model). 634 | Adding a language with a new model could be possible but no support will be 635 | given, however you can always try, you can even try getting help. Adding a 636 | language requires a couple of components that need to be written or adapted. 637 | 638 | \section{Phonetizer} 639 | Phonetization of your language is the most elegant solution of translating the 640 | graphemes to phonemes. Implementing a phonetizer is as easy as implementing one 641 | function called \texttt{phonetizeword}. A skeleton class can be found in 642 | \texttt{phonetizer.py}. The function in the skeleton class is accompanied by 643 | comments. A phonetized utterance is always of the following form:\\ 644 | \texttt{utt=[word1, word2, \ldots, wordn]}, 645 | \texttt{word=[pron1, pron2, \ldots, pronn]} and 646 | \texttt{pron=[phone1, phone2, \ldots, phonen]} and every phone is a string. 647 | So if you want to use the skeleton class with the \texttt{phonetizeword} 648 | function you need to return a list of lists of strings where every 649 | string is a phone from the model. If you also want to do utterance based 650 | translation you need to return a list of lists of lists of strings. 651 | 652 | \section{Dictionary} 653 | If you do not want to use a phonetizer you can also suffice with only using a 654 | dictionary based translation. Dictionary based translation still needs to be 655 | loaded as a phonetizer though. All phonetizers include also a dictionary based 656 | lookup. In the \texttt{phonetizer.py} a dictionary phonetizer is already 657 | present called \texttt{PhonetizerDictionary}. There is also a loopback 658 | phonetizer that takes the literal annotation as transcription. This phonetizer 659 | is currently not used but could be used when an exact phonetic translation is 660 | already available. 661 | 662 | \section{Adding the language to the aligner} 663 | When you have the translation from grapheme to phoneme the only thing that 664 | needs to be done is adding it to the script files. 665 | \begin{itemize} 666 | \item \texttt{phonetizer.py}\\ 667 | On the bottom of this file there is a dictionary containing all the 668 | translations from language code to phonetizer and parameters directory. You 669 | need to add your language to that dictionary. 670 | \item \texttt{settings.praat}\\ 671 | In this file you need to add stuff on multiple locations, namely within the 672 | second \texttt{if} that relies in the first outer \texttt{if} block you 673 | need to add your language with its appropriate position. When you want your 674 | language on top you need to adapt the other numbers too. 675 | 676 | Finally within the \texttt{pause} block you need to add your language code 677 | in the \texttt{optionMenu:} block on the same position as specified 678 | earlier. 679 | \end{itemize} 680 | When you have changed these files properly your language should be available in 681 | the menus and work out of the box. 682 | 683 | \chapter{Appendices} 684 | \section{How to cite} 685 | \begin{lstlisting}[caption={Bibtex snippet}] 686 | @misc{praatalign2.0, 687 | author={Lubbers, Mart and Torreira, Francisco}, 688 | title={Praatalign: an interactive Praat plug-in for performing phonetic forced alignment}, 689 | howpublished={\url{https://github.com/dopefishh/praatalign}}, 690 | year={2013-2016}, 691 | note={Version 2.0} 692 | } 693 | \end{lstlisting} 694 | 695 | \section{Licence} 696 | \lstinputlisting{../LICENCE} 697 | 698 | \section{Acoustic model phone specifications}\label{sec:models} 699 | \subsection{Spanish}\label{sec:phspanish} 700 | The Spanish mapping is an exact mapping with the spanish SAMPA 701 | phoneset\footnote{\url{http://www.phon.ucl.ac.uk/home/sampa/spanish.htm}}. 702 | 703 | {\small 704 | \begin{longtable}{l|l|l|l} 705 | \toprule 706 | & Symbol & Word & Transcription\\ 707 | \midrule 708 | \multirow{6}{*}{Plosives} & 709 | p & padre & p a D r e\\ 710 | & b & vino & b i n o\\ 711 | & t & tomo & t o m o\\ 712 | & d & donde & d o n d e\\ 713 | & k & casa & k a s a\\ 714 | & g & gata & g a t a\\ 715 | \midrule 716 | \multirow{2}{*}{Affricatives} & 717 | tS & mucho & m u tS o\\ 718 | & jj & hielo & jj e l o\\ 719 | \midrule 720 | \multirow{7}{*}{Fricatives} & 721 | f & fácil & f a T i l\\ 722 | & B & cabra & k a B r a\\ 723 | & T & cinco & T i n k o\\ 724 | & D & nada & n a D a\\ 725 | & s & sala & s a l a \\ 726 | & x & mujer & m u x e r\\ 727 | & G & luego & l w e G o\\ 728 | \midrule 729 | \multirow{3}{*}{Nasals} & 730 | m & mismo & m i s m o\\ 731 | & n & nunca & n u n k a\\ 732 | & J & año & a J o\\ 733 | \midrule 734 | \multirow{4}{*}{Liquids} & 735 | l & lejos & l e x o s\\ 736 | & L & caballo & k a b a L o\\ 737 | & r & puro & p u r o\\ 738 | & rr & torre & t o rr e\\ 739 | \midrule 740 | \multirow{4}{*}{Semivowels} & 741 | j & rei & rr e j\\ 742 | & & pie & p j e\\ 743 | & w & deuda & d e w D a\\ 744 | & & muy & m w i\\ 745 | \midrule 746 | \multirow{5}{*}{Vowels} & 747 | i & pico & p i k o\\ 748 | & e & pero & p e r o\\ 749 | & a & valle & b a L e\\ 750 | & o & toro & t o r o\\ 751 | & u & duro & d u r o\\ 752 | \midrule 753 | \multirow{4}{*}{Special} & 754 | \textless{} & word initial silence & \\ 755 | & \textgreater{} & word final silence & \\ 756 | & \# & inter word silence & \\ 757 | & \textless{}nib\textgreater{} & non speech sound & \\ 758 | \bottomrule 759 | \caption{Spanish phone specification} 760 | \end{longtable} 761 | } 762 | 763 | \subsection{Dutch} 764 | The Dutch mapping is an almost\footnote{Derivations are marked in bold} exact 765 | mapping with the dutch SAMPA phoneset\footnote{\url{ 766 | http://www.phon.ucl.ac.uk/home/sampa/dutch.htm}} 767 | 768 | {\small 769 | \begin{longtable}{l|l|l|l} 770 | & Praatalign & Word & Transcription\\ 771 | \toprule 772 | \multirow{6}{*}{Plosives} & 773 | p & pak & p A k\\ 774 | & b & bak & b A k\\ 775 | & t & tak & t A k\\ 776 | & d & dak & d A k\\ 777 | & k & kap & k A p\\ 778 | & g & goal & g o: l\\ 779 | \midrule 780 | \multirow{9}{*}{Fricatives} & 781 | f & fel & f E l\\ 782 | & v & vel & v E l\\ 783 | & s & sein & s E i n\\ 784 | & z & zijn & z E i n\\ 785 | & x & toch & t o x\\ 786 | & G & goed & G u t\\ 787 | & h & hand & h A n t\\ 788 | & Z & bagage & b A g a: Z @\\ 789 | & S & show & S o: u\\ 790 | \midrule 791 | \multirow{7}{*}{Sonorants} & 792 | m & met & m E t\\ 793 | & n & net & n E t\\ 794 | & N & bang & b A N\\ 795 | & l & land & l A n t\\ 796 | & r & rand & r A n t\\ 797 | & w & wit & w I t\\ 798 | & j & ja & j a:\\ 799 | \midrule 800 | \multirow{6}{*}{Checked vowels} & 801 | I & pit & p I t\\ 802 | & E & pet & p E t\\ 803 | & A & pat & p A t\\ 804 | & O & pot & p O t\\ 805 | & Y & put & p Y t\\ 806 | & @ & gemakkelijk & G @ m A k @ l @ k\\ 807 | \midrule 808 | \multirow{10}{*}{Free vowels} & 809 | i & vier & v i r\\ 810 | & y & vuur & v y r\\ 811 | & u & voer & v u r\\ 812 | & a: & naam & n a: m\\ 813 | & e: & veer & v e: r\\ 814 | & \textbf{P2:} & deur & d P2: r\\ 815 | & o: & voor & v o: r\\ 816 | & EI & fijn & f EI n\\ 817 | & \textbf{P9y} &huis & h P9y s\\ 818 | & Au & goud & x Au t\\ 819 | \midrule 820 | \multirow{6}{*}{Dipthongs} & 821 | a:i & draai & d r a: i\\ 822 | & o:i &mooi & m o: i\\ 823 | & ui & roeiboot & r ui b o: t\\ 824 | & iu & nieuw & n iu\\ 825 | & yu & duw & d yu\\ 826 | & e:u & sneeuw & s n e: u\\ 827 | \midrule 828 | \multirow{3}{*}{Marginal vowels} & 829 | E:\ & cr\'eme & k r E:\ m\\ 830 | & \textbf{P9:} & freule & f r P9: l @\\ 831 | & O:\ & roze & r O:\ z @\\ 832 | \midrule 833 | \multirow{4}{*}{Special} & 834 | \textless{} & word initial silence & \\ 835 | & \textgreater{} & word final silence & \\ 836 | & \# & inter word silence & \\ 837 | & \textless{} nib\textgreater{} & non speech sound & \\ 838 | \bottomrule 839 | \caption{Dutch phone specification} 840 | \end{longtable} 841 | } 842 | 843 | %\subsection{English} 844 | %The English mapping is an exact\footnote{Derivations are marked in bold} exact 845 | %mapping with the english SAMPA phoneset\footnote{\url{ 846 | %https://www.phon.ucl.ac.uk/home/sampa/english.htm}} with some borrowed phones. 847 | % 848 | %Also there is conversion script for the English CMU 849 | %dictionary\footnote{\url{http://www.speech.cs.cmu.edu/cgi-bin/cmudict}} located 850 | %in \texttt{./par.eng/cmu2praatalign.py} that converts the CMU dictionary to 851 | %Praatalign format. The scripts is a Python script and should download the 852 | %dictionary if you haven't done it yourself and will write it to a 853 | %\texttt{dict.eng} file by default. The usage is: \texttt{python 854 | %cmu2praatalign.py [inputfile [outputfile]]}. 855 | % 856 | %{\small 857 | %\begin{longtable}{l|l|p{.3\linewidth}|l} 858 | % & Symbol & Word & Transcription\\ 859 | % \toprule 860 | % \multirow{6}{*}{Plosives} & 861 | % p & pen &\\ 862 | % & b & but &\\ 863 | % & t & two &\\ 864 | % & d & do &\\ 865 | % & k & skill &\\ 866 | % & g & go &\\ 867 | % \midrule 868 | % \multirow{2}{*}{Affricatives} & 869 | % tS & chair &\\ 870 | % & dZ & gin &\\ 871 | % \midrule 872 | % \multirow{9}{*}{Fricatives} & 873 | % f & fool &\\ 874 | % & v & voice &\\ 875 | % & T & thing &\\ 876 | % & D & this &\\ 877 | % & s & sin &\\ 878 | % & z & zoo &\\ 879 | % & S & she &\\ 880 | % & Z & pleasure &\\ 881 | % & h & ham &\\ 882 | % \midrule 883 | % \multirow{3}{*}{Nasals} & 884 | % m & man &\\ 885 | % & n & no &\\ 886 | % & N & ring &\\ 887 | % \midrule 888 | % \multirow{2}{*}{Liquids} & 889 | % r & perro &\\ 890 | % & l & left &\\ 891 | % \midrule 892 | % \multirow{2}{*}{Sonorant glides} & 893 | % w & we &\\ 894 | % & j & yes &\\ 895 | % \midrule 896 | % \multirow{6}{*}{Checked vowels} & 897 | % I & English city &\\ 898 | % & e & bear &\\ 899 | % & \{ & cat &\\ 900 | % & Q & cough &\\ 901 | % & V & run &\\ 902 | % & U & put &\\ 903 | % \midrule 904 | % \multirow{1}{*}{Short vowels} & 905 | % @ & about &\\ 906 | % \midrule 907 | % \multirow{13}{*}{Free vowels} & 908 | % i: & ease &\\ 909 | % & eI & raise &\\ 910 | % & aI & rise &\\ 911 | % & OI & noise &\\ 912 | % & u: & lose &\\ 913 | % & @U & nose &\\ 914 | % & aU & rouse &\\ 915 | % & 3: & furs &\\ 916 | % & A:\ & stars &\\ 917 | % & O:\ & cause &\\ 918 | % & I@ & dears &\\ 919 | % & e@ & stairs &\\ 920 | % & U@ & cures &\\ 921 | % \midrule 922 | % \multirow{3}{*}{Special} & 923 | % \textless{} & word initial silence & \\ 924 | % & \textgreater{} & word final silence & \\ 925 | % & \# & inter word silence & \\ 926 | % & \textless{} nib\textgreater{} & non speech sound & \\ 927 | % \midrule\midrule 928 | % \multirow{22}{*}{Borrowed phones} & Symbol & description & 929 | % example (language)\\ 930 | % \midrule 931 | % & a\textasciitilde{} & 932 | % nasalized central open vowel & vent (fra)\\ 933 | % & E\textasciitilde{} & 934 | % nasalized lengthened front half open unrounded vowel & (deu)\\ 935 | % & o\textasciitilde{} & 936 | % nasalized back half closed rounded vowel & bon (fra)\\ 937 | % & 6:\ & lengthened central neutral unrounded vowel & (aus)\\ 938 | % & \}:\ & lengthened central closed rounded vowel & pool (aus)\\ 939 | % & e:\ & lengthened front half closed unrounded vowel & mehr (deu)\\ 940 | % & o:\ & lengthened back half closed rounded vowel & Sohle (deu)\\ 941 | % & @\} & diphthong &\\ 942 | % & Ae & diphthong & (aus)\\ 943 | % & \{I & diphthong & (aus)\\ 944 | % & \{O & diphthong & (aus)\\ 945 | % & oI & diphthong & (aus)\\ 946 | % & O & back half open rounded vowel & law (brit)\\ 947 | % & i & front closed unrounded vowel & see\\ 948 | % & u & back closed rounded vowel & soon\\ 949 | % & o & back half closed rounded vowel & sore (us)\\ 950 | % & E & front half open unrounded vowel & bed\\ 951 | % & 6 & central neutral unrounded vowel & besser (deu)\\ 952 | % &? & glottal stop & Verein (ger)\\ 953 | % & x & voiceless velar fricative & loch (scot)\\ 954 | % & C & voiceless palatal fricative & Ich (ger)\\ 955 | % & W & voiceless labial-velar fricative &\\ 956 | % \bottomrule 957 | % \caption{English phone specification} 958 | %\end{longtable} 959 | %} 960 | % 961 | %\subsection{SAMPA} 962 | %{\small 963 | %\begin{longtable}{l|p{.3\linewidth}|p{.15\linewidth}|l|p{.15\linewidth}|l} 964 | % Sym & description & examples & lang & type & location\\ 965 | % \toprule 966 | % i & front closed unrounded vowel & English see, Spanish sí, French vite, German mi.e.ten, Italian visto & xxx & vowel & front \\ 967 | % I & front closed unrounded vowel, but somewhat more centralised and relaxed, in Polish: mid closed unrounded & English city, German mit & xxx & vowel & front \\ 968 | % 1 & close central unrounded vowel & Russian mys & xxx & vowel & central \\ 969 | % e & front half closed unrounded vowel & US English bear, Spanish él, French année, German mehr, Italian rete, Catalan més & xxx & vowel & front \\ 970 | % E & front half open unrounded vowel & English bed, French même, German Herr, Männer, Italian ferro, Catalan mes, Spanish perro & xxx & vowel & front \\ 971 | % \{ & front open unrounded vowel & English cat & xxx & vowel & front \\ 972 | % y & front closed rounded vowel & French du, German Tür & xxx & vowel & front \\ 973 | % 2 & front half closed rounded vowel & French deux (hence '2'), German Höhle & xxx & vowel & front \\ 974 | % 9 & front half open rounded vowel & French neuf (hence '9'), German Hölle & xxx & vowel & front \\ 975 | % @ & central neutral unrounded vowel & English about, winner,German bitte & xxx & vowel & central \\ 976 | % P6 & central neutral unrounded vowel & German besser & xxx & vowel & central\\ 977 | % 3 & front half open unrounded vowel, but somewhat more centralised and relaxed & English bird,nurse & xxx & vowel & central \\ 978 | % a & central open vowel & Spanish da, barra, French bateau,lac, German Haar, Italian pazzo & xxx & vowel & front \\ 979 | % \} & central closed rounded vowel & Scottish English pool, Swedish sju & xxx & vowel & central \\ 980 | % 8 & central neutral rounded vowel & Swedish kust & xxx & vowel & central \\ 981 | % \& & front open rounded vowel & American English that & xxx & vowel & front \\ 982 | % M & back closed unrounded vowel & Japanese fuji, Korean eu & xxx & vowel & back \\ 983 | % 7 & back half closed unrounded vowel & Korean eo & xxx & vowel & back \\ 984 | % V & back half open unrounded vowel & RP and US English run,enough,strut & xxx & vowel & back \\ 985 | % A & back open unrounded vowel & English arm, US English law, standard French âme & xxx & vowel & back \\ 986 | % u & back closed rounded vowel & English soon, Spanish tú, French goût, German Hut, Mutter, Italian azzurro,tutto & xxx & vowel & back \\ 987 | % U & back closed rounded vowel somewhat more centralised and relaxed & English put, Buddhist & xxx & vowel & back \\ 988 | % o & back half closed rounded vowel & US English sore, Scottish English boat, Spanish yo, French beau, German Sohle, Italian dove, Catalan ona & xxx & vowel & back \\ 989 | % O & back half open rounded vowel & British English law, caught, Italian cosa, Catalan dona, Spanish ojo, German Wort & xxx & vowel & back \\ 990 | % Q & back open rounded vowel & British English not, cough & xxx & vowel & back \\ 991 | % Y & lax [y] & German hübsch & xxx & vowel & front \\ 992 | % p & voiceless bilabial plosive & English pen & xxx & plosive & bilabial \\ 993 | % b & voiced bilabial plosive & English but & xxx & plosive & bilabial \\ 994 | % t & voiceless alveolar plosive & English two, Spanish toma & xxx & plosive & alveolar \\ 995 | % d & voiced alveolar plosive & English do, Italian cade & xxx & plosive & alveolar \\ 996 | % ts & voiceless alveolar affricate & Italian calza, German zeit & xxx & affricate & alveolar \\ 997 | % dz & voiced alveolar affricate & Italian zona & xxx & affricate & alveolar \\ 998 | % tS & voiceless postalveolar affricate & English chair, Spanish mucho & xxx & affricate & post-alveolar \\ 999 | % dZ & voiced postalveolar affricate & English gin, Italian giorno & xxx & affricate & post-alveolar \\ 1000 | % pf & voiceless labial affricate & German Pferd & xxx & affricate & bilabial \\ 1001 | % c & voiceless palatal plosive & Hungarian tyúk `hen' & xxx & plosive & palatal \\ 1002 | % k & voiceless velar plosive & English skill & xxx & plosive & velar \\ 1003 | % g & voiced velar plosive & English go & xxx & plosive & velar \\ 1004 | % q & voiceless uvular plosive & Arabic qof & xxx & plosive & uvular \\ 1005 | % B & voiced bilabial fricative & Catalan roba `clothes' & xxx & fricative & bilabial \\ 1006 | % f & voiceless labiodental fricative & English fool, Spanish and Italian falso & xxx & fricative & labio-dental \\ 1007 | % v & voiced labiodental fricative & English voice, German Welt & xxx & fricative & labio-dental \\ 1008 | % T & voiceless dental fricative & English thing, Castilian Spanish caza & xxx & fricative & dental \\ 1009 | % D & voiced dental fricative & English this & xxx & fricative & dental\\ 1010 | % s & voiceless alveolar fricative & English see, Spanish sí & xxx & fricative & alveolar \\ 1011 | % z & voiced alveolar fricative & English zoo, German See & xxx & fricative & alveolar \\ 1012 | % S & voiceless postalveolar fricative & English she, French chemin & xxx & fricative & post-alveolar \\ 1013 | % Z & voiced postalveolar fricative & French jour, English pleasure & xxx & fricative & post-alveolar \\ 1014 | % C & voiceless palatal fricative & German Ich & xxx & fricative & palatal \\ 1015 | % x & voiceless velar fricative & Scots loch, Castilian Spanish ajo & xxx & fricative & velar \\ 1016 | % G & voiced velar fricative & Greek $\gamma\alpha\lambda\alpha$ & xxx & fricative & velar \\ 1017 | % h & voiceless glottal fricative & English ham, German Hand & xxx & fricative & glottal \\ 1018 | % m & bilabial nasal & English man & xxx & nasal & bilabial \\ 1019 | % F & labiodental nasal & Spanish infierno, Hungarian kámfor & xxx & nasal & labio-dental \\ 1020 | % n & alveolar nasal & English, Spanish and Italian no & xxx & nasal & alveolar \\ 1021 | % J & palatal nasal & Spanish año, French oignon & xxx & nasal & palatal \\ 1022 | % N & velar nasal & English ring, Italian bianco, Tagalog ngayón & xxx & nasal & velar \\ 1023 | % l & alveolar lateral approximant & English left, Spanish largo & xxx & lateral-approximant & alveolar \\ 1024 | % L & palatal lateral approximant & Italian aglio, Catalan colla, & xxx & lateral-approximant & palatal \\ 1025 | % 5 & velarized dental lateral & English meal Catalan alga & xxx & lateral-approximant & dental-velar \\ 1026 | % 4 & alveolar tap & Spanish pero, American English muddy & use & tap & alveolar \\ 1027 | % r & alveolar trill & Spanish perro & xxx & trill & alveolar \\ 1028 | % R & uvular trill & German Reich & xxx & trill & uvular \\ 1029 | % P & labiodental approximant & Dutch Waar & xxx & approximant & labio-dental \\ 1030 | % w & labial-velar approximant & English we, French oui & xxx & approximant & labio-velar \\ 1031 | % H & labial-palatal approximant & French huit & xxx & approximant & labio-palatal \\ 1032 | % j & palatal approximant & English yes, French yeux & xxx & approximant & palatal \\ 1033 | %? & glottal stop & German Verein, Danish st\o{}d & xxx & plosive & glottal \\ 1034 | % W & voiceless labial-velar fricative & & xxx & fricative & labio-velar \\ 1035 | % D:\ & lengthened voiced dental fricative & & fin & fricative & dental \\ 1036 | % T:\ & lengthened voiceless dental fricative & & fin & fricative & dental \\ 1037 | % i:\ & lengthened front closed unrounded vowel & mieten & deu & vowel & front \\ 1038 | % ii & lengthened front closed unrounded vowel in quantity II & riisu & ekk & vowel & front \\ 1039 | % ii: & lengthened front closed unrounded vowel in quantity III & & ekk & vowel & front \\ 1040 | % e: & lengthened front half closed unrounded vowel & mehr & deu & vowel & front \\ 1041 | % ee & lengthened front half closed unrounded vowel in quantity II & keere & ekk & vowel & front \\ 1042 | % ee: & lengthened front half closed unrounded vowel in quantity III & & ekk & vowel & front \\ 1043 | % E:\ & lengthened front half open unrounded vowel & M\"ar & deu & vowel & front \\ 1044 | % y: & lengthened front closed rounded vowel & Tür & deu & vowel & front \\ 1045 | % Y:\ & lengthened lax [y] & (Swiss German) & deu & vowel & front \\ 1046 | % 2: & lengthened front half closed rounded vowel & Höhle & deu & vowel & front \\ 1047 | % a: & lengthened central open vowel & Haar & deu & vowel & central \\ 1048 | % u: & lengthened back closed rounded vowel & Hut & deu & vowel & back \\ 1049 | % o: & lengthened back half closed rounded vowel & Sohle & deu & vowel & back \\ 1050 | % 3: & lengthened front half open unrounded vowel & furs & aus & vowel & front \\ 1051 | % A:\ & lengthened back open unrounded vowel & stars & aus & vowel & back \\ 1052 | % O:\ & lengthened back half open rounded vowel & cause & aus & vowel & back \\ 1053 | % P6: & lengthened central neutral unrounded vowel & & aus & vowel & central \\ 1054 | % \}: & lengthened central closed rounded vowel & pool & aus & vowel & central \\ 1055 | % Q:\ & lengthened open back rounded & (Swiss German) & aus & vowel & back \\ 1056 | % 9: & lengthened front half open rounded vowel & & nld & vowel & front \\ 1057 | % \{\{ & lengthened front open unrounded vowel in quantity II & kääru & ekk & vowel & front \\ 1058 | % \{: & lengthened front open unrounded vowel in quantity II & kääru & ekk & vowel & front \\ 1059 | % \{\{: & lengthened front open unrounded vowel in quantity III & & ekk & vowel & front \\ 1060 | % yy & lengthened front closed rounded vowel (quantitiy II) & müüri & ekk & vowel & front \\ 1061 | % yy: & lengthened front closed rounded vowel (quantitiy III) & & ekk & vowel & front \\ 1062 | % 22 & lengthened front half closed rounded vowel & nööri & ekk & vowel & front \\ 1063 | % 22: & lengthened front half closed rounded vowel in quantity III & & ekk & vowel & front \\ 1064 | % uu & lengthened back closed rounded vowel & kuuri & ekk & vowel & back \\ 1065 | % uu: & lengthened back closed rounded vowel in quantity III & & ekk & vowel & back \\ 1066 | % oo & lengthened back half closed rounded vowel & poori & ekk & vowel & back \\ 1067 | % oo: & lengthened back half closed rounded vowel in quantity III & & ekk & vowel & back \\ 1068 | % 77 & back half closed unrounded vowel in quantity II & sõõre & ekk & vowel & back \\ 1069 | % 7: & back half closed unrounded vowel in quantity II & sõõre & ekk & vowel & back \\ 1070 | % 77: & back half closed unrounded vowel in quantity III & & ekk & vowel & back \\ 1071 | % AA & lengthened back open unrounded vowel in quantity II & vaaru & ekk & vowel & back \\ 1072 | % AA:\ & lengthened back open unrounded vowel in quantity III & & ekk & vowel & back \\ 1073 | % aU & diphthong & Haus & deu & diphthong & front\textgreater{} back \\ 1074 | % aI & diphthong & Bein & deu & diphthong & front \\ 1075 | % ai & diphthong & & ita & diphthong & front \\ 1076 | % a:i & diphthong & & nld & diphthong & front \\ 1077 | % Ae & diphthong & & aus & diphthong & back\textgreater{} front \\ 1078 | % Au & diphthong & & nld & diphthong & back \\ 1079 | % OY & diphthong & heulen & deu & diphthong & back\textgreater{} front \\ 1080 | % eI & diphthong & raise & aus & diphthong & front \\ 1081 | % e@ & diphthong & stairs & aus & diphthong & front\textgreater{} central \\ 1082 | % ei & diphthong & & ita & diphthong & front \\ 1083 | % e:i & diphthong & & nld & diphthong & front \\ 1084 | % eU & diphthong & & por & diphthong & front\textgreater{} back \\ 1085 | % EI & diphthong & raise & eng & diphthong & front \\ 1086 | % Ei & diphthong & & ita & diphthong & front \\ 1087 | % \{I & diphthong & & aus & diphthong & front \\ 1088 | % \{O & diphthong & & aus & diphthong & front\textgreater{} back \\ 1089 | % I@ & diphthong & dears & aus & diphthong & front\textgreater{} central \\ 1090 | % Ii: & diphthong & accede & aus & diphthong & front \\ 1091 | % I:\ & lengthened front closed unrounded vowel & (Swiss German) & deu & vowel & front \\ 1092 | % i:@ & diphthong & memorial & nze & diphthong & central\textgreater{} front \\ 1093 | % io & diphthong & & ita & diphthong & front\textgreater{} back \\ 1094 | % iu & diphthong & & nld & diphthong & front\textgreater{} back \\ 1095 | % ja & diphthong & & ita & diphthong & front \\ 1096 | % jo & diphthong & & ita & diphthong & front\textgreater{} back \\ 1097 | % ju & diphthong & & ita & diphthong & front\textgreater{} back \\ 1098 | % oI & diphthong & & aus & diphthong & back\textgreater{} front \\ 1099 | % oi & diphthong & & ita & diphthong & back\textgreater{} front \\ 1100 | % o:i & diphthong & & nld & diphthong & back\textgreater{} front \\ 1101 | % oU & diphthong & & por & diphthong & back \\ 1102 | % oE & diphthong & & ita & diphthong & back\textgreater{} front \\ 1103 | % OI & diphthong & noise & aus & diphthong & back\textgreater{} front \\ 1104 | % Oi & diphthong & & ita & diphthong & back\textgreater{} front \\ 1105 | % ue & diphthong & & ita & diphthong & back\textgreater{} front \\ 1106 | % ui & diphthong & & nld & diphthong & back\textgreater{} front \\ 1107 | % U@ & diphthong & cures & aus & diphthong & back\textgreater{} central \\ 1108 | % U:\ & lenthened back closed rounded vowel somewhat more centralised and relaxed & (Swiss German) & deu & vowel & back \\ 1109 | % wa & diphthong & & ita & diphthong & front \\ 1110 | % we & diphthong & & ita & diphthong & front \\ 1111 | % wi & diphthong & & ita & diphthong & front \\ 1112 | % wO & diphthong & & ita & diphthong & back \\ 1113 | % yu & diphthong & & nld & diphthong & front\textgreater{} back \\ 1114 | % @U & diphthong & nose & aus & diphthong & central\textgreater{} back \\ 1115 | % @\} & diphthong & & aus & diphthong & central \\ 1116 | % @@ & geminate of schwa in quantity II & & ekk & vowel & central \\ 1117 | % @: & geminate of schwa in quantity II & & ekk & vowel & central \\ 1118 | % @@: & geminate of schwa in quantity III & & ekk & vowel & central \\ 1119 | % 9y & diphthong & & nld & diphthong & back\textgreater{} front \\ 1120 | % QI & diphthong & abide & aus & diphthong & central\textgreater{} front \\ 1121 | % U\} & diphthong & abuse & aus & diphthong & central\textgreater{} back \\ 1122 | % VU & diphtong & acetone & aus & diphthong & central\textgreater{} back \\ 1123 | % Vi & diphthong & abased & aus & diphthong & central\textgreater{} front \\ 1124 | % \{o & diphthong & accounts & aus & diphthong & front\textgreater{} back \\ 1125 | % 2i & diphthong & & ekk & diphthong & back\textgreater{} front \\ 1126 | % 2i: & diphthong & & ekk & diphthong & back\textgreater{} front \\ 1127 | % 7o: & diphthong & & ekk & diphthong & back \\ 1128 | % 7u: & diphthong & & ekk & diphthong & back \\ 1129 | % 7u & diphthong & & ekk & diphthong & back \\ 1130 | % Ai & diphthong & & ekk & diphthong & back\textgreater{} front \\ 1131 | % Ai: & diphthong & & ekk & diphthong & back\textgreater{} front \\ 1132 | % Ao: & diphthong & & ekk & diphthong & back \\ 1133 | % Au: & diphthong & & ekk & diphthong & back \\ 1134 | % Ae: & diphthong & & ekk & diphthong & back\textgreater{} front \\ 1135 | % ei: & diphthong & & ekk & diphthong & front \\ 1136 | % e:u & diphthong & & nld & diphthong & front \\ 1137 | % i\textasciitilde{} & nasalized front closed unrounded vowel & & xxx & vowel & front \\ 1138 | % e\textasciitilde{} & nasalized front half closed unrounded vowel & vin & fra & vowel & front \\ 1139 | % a\textasciitilde{} & nasalized central open vowel & vent & fra & vowel & front \\ 1140 | % o\textasciitilde{} & nasalized back half closed rounded vowel & bon & fra & vowel & back \\ 1141 | % 9\textasciitilde{} & nasalized front half open rounded vowel & brun,neuv & fra & vowel & front \\ 1142 | % E\textasciitilde{} & nasalized lengthened front half open unrounded vowel & & deu & vowel & front \\ 1143 | % O\textasciitilde{} & nasalized back half open rounded vowel & & deu & vowel & back \\ 1144 | % u\textasciitilde{} & nasalized back closed rounded vowel & & xxx & vowel & back \\ 1145 | % a:\textasciitilde{} & nasalized lengthened central open vowel & & deu & vowel & central \\ 1146 | % E:\textasciitilde{} & nasalized lengthened front half open unrounded vowel & & deu & vowel & front \\ 1147 | % o:\textasciitilde{} & nasalized lengthened back half closed rounded vowel & & deu & vowel & back \\ 1148 | % O:\textasciitilde{} & nasalized lengthened back half open rounded vowel & & nld & vowel & back \\ 1149 | % ts\_j & palatalized voiceless alveolar affricate & c'ma & pol & affricate & post-alveolar \\ 1150 | % dz\_j & palatalized voiced alveolar affricate & dz'wig & pol & affricate & alveolar \\ 1151 | % s\_j & palatalized voiceless alveolar fricative & syk & pol & fricative & alveolar \\ 1152 | % s\_js & palatalized voiceless alveolar fricative in quantity II & kassi & ekk & fricative & alveolar \\ 1153 | % s\_j:s & palatalized voiceless alveolar fricative in quantity III & & ekk & fricative & alveolar \\ 1154 | % z\_j & palatalized voiced alveolar fricative & zbir & pol & fricative & alveolar \\ 1155 | % n\_j & palatalized alveolar nasal & kon' & pol & nasal & alveolar \\ 1156 | % n\_jn & palatalized alveolar nasal in quantity II & panni & ekk & nasal & alveolar \\ 1157 | % n\_j:n & palatalized alveolar nasal in quantity III & & ekk & nasal & alveolar \\ 1158 | % l\_j & palatalized alveolar lateral approximant & pali & ekk & lateral-approximant & alveolar \\ 1159 | % l\_jl & palatalized alveolar lateral approximant in quantity II & palli & ekk & lateral-approximant & alveolar \\ 1160 | % l\_j:l & palatalized alveolar lateral approximant in quantity III & & ekk & lateral-approximant & alveolar \\ 1161 | % t\_j & palatalized voiceless alveolar plosive & padi & ekk & plosive & alveolar \\ 1162 | % t\_jt & palatalized voiceless alveolar plosive in quantity II & pati & ekk & plosive & alveolar \\ 1163 | % t\_j:t & palatalized voiceless alveolar plosive in quantity III & & ekk & plosive & alveolar \\ 1164 | % d\_j & palatalized voiced alveolar plosive & gyár & hun & plosive & alveolar \\ 1165 | % dd\_j & geminate of d' & egy & hun & plosive & alveolar \\ 1166 | % g\_j & palatalized voiced velar plosive & Gienek & pol & plosive & velar \\ 1167 | % x\_j & palatalized voiceless velar fricative & hiacynt & pol & fricative & velar \\ 1168 | % k\_j & palatalized voiceless velar plosive & kierowca & pol & plosive & velar \\ 1169 | % p\_j & palatalized voiceless bilabial plosive & piasek & pol & plosive & bilabial \\ 1170 | % xx\_j & geminate of x' & & hun & fricative & velar \\ 1171 | % p\_h & aspirated voiceless bilabial plosive & & spa & plosive & bilabial \\ 1172 | % t\_h & aspirated voiceless alveolar plosive & & spa & plosive & alveolar \\ 1173 | % k\_h & aspirated voiceless velar plosive & & spa & plosive & velar \\ 1174 | % tt & geminate of t & fatto & ita & fricative & bilabial \\ 1175 | % t: & lengthened t & että & fin & plosive & alveolar \\ 1176 | % t:t & geminate of t in quantity III & & ekk & plosive & alveolar \\ 1177 | % pp & geminate of p & & ita & plosive & bilabial \\ 1178 | % p: & lengthened p & & fin & plosive & bilabial \\ 1179 | % p:p & geminate of p in quantity III & & ekk & plosive & bilabial \\ 1180 | % kk & geminate of k & & ita & plosive & velar \\ 1181 | % k: & lengthened k & takkinsa & fin & plosive & velar \\ 1182 | % k:k & geminate of k in quantity III & & ekk & plosive & velar \\ 1183 | % dd & geminate of d & & ita & plosive & alveolar \\ 1184 | % gg & geminate of g & & ita & plosive & velar \\ 1185 | % g: & lengthened g & & fin & plosive & velar \\ 1186 | % bb & geminate of b & & ita & plosive & bilabial \\ 1187 | % b: & lengthened b & & fin & plosive & bilabial \\ 1188 | % ttS & geminate of tS & & ita & affricate & post-alveolar \\ 1189 | % tts & geminate of ts & & ita & affricate & alveolar \\ 1190 | % ddZ & geminate of dZ & & ita & affricate & post-alveolar \\ 1191 | % ddz & geminate of dz & zona & ita & affricate & alveolar \\ 1192 | % vv & geminate of v & & ita & fricative & labio-dental \\ 1193 | % v: & lengthened v & & fin & fricative & labio-dental \\ 1194 | % v:v & geminate of v in quantity III & & ekk & fricative & labio-dental \\ 1195 | % ss & geminate of s & & ita & fricative & alveolar \\ 1196 | % s: & lengthened s & & fin & fricative & alveolar \\ 1197 | % s:s & geminate of s in quantity III & & ekk & fricative & alveolar \\ 1198 | % zz & geminate of z & & hun & fricative & alveolar \\ 1199 | % SS & geminate of S & & ita & fricative & post-alveolar \\ 1200 | % S:\ & lengthened S & & fin & fricative & post-alveolar \\ 1201 | % S:S & geminate of S in quantity III & & ekk & fricative & post-alveolar \\ 1202 | % ZZ & geminate of Z & & hun & fricative & post-alveolar \\ 1203 | % xx & geminate of x & & hun & fricative & velar \\ 1204 | % rr & geminate of r & & ita & trill & alveolar \\ 1205 | % r: & lengthened r & & fin & trill & alveolar \\ 1206 | % r:r & geminate of r in quantity III & & ekk & trill & alveolar \\ 1207 | % RR & geminate of R in quantity II & & ekk & trill & uvular \\ 1208 | % nn & geminate of n & & ita & nasal & alveolar \\ 1209 | % n: & lengthened n & & fin & nasal & alveolar \\ 1210 | % n:n & geminate of n in quantity III & & ekk & nasal & alveolar \\ 1211 | % NN & geminate of N & (Swiss German) & deu & nasal & velar \\ 1212 | % N:\ & lengthened N & & fin & nasal & velar \\ 1213 | % N\_j & palatalized velar nasal & & ekk & nasal & velar \\ 1214 | % ww & geminate of w & (Swiss German) & deu & approximant & labio-velar \\ 1215 | % mm & geminate of m & & ita & nasal & bilabial \\ 1216 | % m: & lengthened m & hommasta & fin & nasal & bilabial \\ 1217 | % m:m & geminate of m in quantity III & & ekk & nasal & bilabial \\ 1218 | % FF & geminate of F & & hun & nasal & labio-dental \\ 1219 | % LL & geminate of L & & ita & lateral-approximant & palatal \\ 1220 | % ll & geminate of l & & ita & lateral-approximant & alveolar \\ 1221 | % l: & lengthened l & jolla & fin & lateral-approximant & alveolar \\ 1222 | % l:l & geminate of l in quantity III & & ekk & approximant & alveolar \\ 1223 | % JJ & geminate of J & & ita & nasal & palatal \\ 1224 | % jj & geminate of j & & ekk & approximant & palatal \\ 1225 | % j: & lengthened j & & fin & approximant & palatal \\ 1226 | % j:j & geminate of j in quantity III & & ekk & approximant & palatal \\ 1227 | % ff & geminate of f & & ita & fricative & bilabial \\ 1228 | % f: & lengthened f & & fin & fricative & bilabial \\ 1229 | % f:f & geminate of f in quantity III & & ekk & fricative & bilabial \\ 1230 | % hh & geminate of h & & ekk & fricative & glottal \\ 1231 | % h: & lengthened h & & fin & fricative & glottal \\ 1232 | % h:h & geminate of h in quantity III & & ekk & fricative & glottal \\ 1233 | % ttS\_cl & closure of ttS & & ita & affricate & post-alveolar \\ 1234 | % ttS\_rl & release of ttS & & ita & affricate & post-alveolar \\ 1235 | % tts\_cl & closure of tts & & ita & affricate & alveolar \\ 1236 | % tts\_rl & release of tts & & ita & affricate & alveolar \\ 1237 | % ddZ\_cl & closure of ddZ & & ita & affricate & post-alveolar \\ 1238 | % ddZ\_rl & release of ddZ & & ita & affricate & post-alveolar \\ 1239 | % ddz\_cl & closure of ddz & zona & ita & affricate & alveolar \\ 1240 | % ddz\_rl & release of ddz & zona & ita & affricate & alveolar \\ 1241 | % tS\_cl & closure of tS & & ita & affricate & post-alveolar \\ 1242 | % tS\_rl & release of tS & & ita & affricate & post-alveolar \\ 1243 | % ts\_cl & closure of ts & & ita & affricate & alveolar \\ 1244 | % ts\_rl & release of ts & & ita & affricate & alveolar \\ 1245 | % dZ\_cl & closure of dZ & & ita & affricate & post-alveolar \\ 1246 | % dZ\_rl & release of dZ & & ita & affricate & post-alveolar \\ 1247 | % dz\_cl & closure of dz & & ita & affricate & alveolar \\ 1248 | % dz\_rl & release of dz & & ita & affricate & alveolar \\ 1249 | % tt\_rl & release of tt & & ita & plosive & alveolar \\ 1250 | % tt\_cl & closure of tt & & ita & plosive & alveolar \\ 1251 | % pp\_cl & closure of pp & & ita & plosive & bilabial \\ 1252 | % pp\_rl & release of pp & & ita & plosive & bilabial \\ 1253 | % kk\_cl & closure of kk & & ita & plosive & velar \\ 1254 | % kk\_rl & release of kk & & ita & plosive & velar \\ 1255 | % dd\_cl & release of dd & & ita & plosive & alveolar \\ 1256 | % dd\_rl & release of dd & & ita & plosive & alveolar \\ 1257 | % gg\_cl & release of gg & & ita & plosive & velar \\ 1258 | % gg\_rl & release of gg & & ita & plosive & velar \\ 1259 | % bb\_cl & release of bb & & ita & plosive & bilabial \\ 1260 | % bb\_rl & release of bb & & ita & plosive & bilabial \\ 1261 | % t\_cl & closure of t & & ita & plosive & alveolar \\ 1262 | % t\_rl & release of t & & ita & plosive & alveolar \\ 1263 | % p\_cl & closure of p & & ita & plosive & bilabial \\ 1264 | % p\_rl & release of p & & ita & plosive & bilabial \\ 1265 | % k\_cl & closure of k & & ita & plosive & velar \\ 1266 | % k\_rl & release of k & & ita & plosive & velar \\ 1267 | % g\_cl & closure of g & & ita & plosive & velar \\ 1268 | % g\_rl & release of g & & ita & plosive & velar \\ 1269 | % d\_cl & closure of d & & ita & plosive & alveolar \\ 1270 | % d\_rl & release of d & & ita & plosive & alveolar \\ 1271 | % b\_cl & closure of b & & ita & plosive & bilabial \\ 1272 | % b\_rl & release of b & & ita & plosive & bilabial \\ 1273 | % \textless{} & recording initial silence & & xxx & silence & silence \\ 1274 | % \textgreater{} & recording trailing silence & & xxx & silence & silence \\ 1275 | % \# & inter-word silence & & xxx & silence & silence \\ 1276 | % \textless{} nib\textgreater{} & noise, non-human & & xxx & noise & noise \\ 1277 | % \textless{} p:\textgreater{} & silence interval & & xxx & silence & silence \\ 1278 | % \textless{} usb\textgreater{} & human noise, garbage & & xxx & noise & noise \\ 1279 | % p\_\textgreater{} & voiceless bilabial ejective & Georgian p\'{}erangi & xxx & ejective & bilabial\\ 1280 | % t\_\textgreater{} & voiceless alveolar ejective & Georgian zedat\'{}ani & xxx & ejective & alveolar\\ 1281 | % ts\_\textgreater{} & voiceless alveolar affricate ejective & Georgian ts'indebi & xxx & ejective & alveolar \\ 1282 | % ts\` & retroflex voiceless affricate & cyk & pol & affricate & retroflex \\ 1283 | % dz\` & retroflex voiced affricate & dzwon & pol & affricate & retroflex \\ 1284 | % 3` & retroflex front half open unrounded vowel & furs & use & vowel & front \\ 1285 | % tS\_\textgreater{} & voiceless postalveolar affricate ejective & Georgian k'uch'is & xxx & ejective & post-alveolar \\ 1286 | % c\_\textgreater{} & voiceless palatal ejective plosive & Georgian ch' & xxx & ejective & palatal \\ 1287 | % J- & voiced palatal plosive & Hungarian egy `one' & xxx & plosive & palatal \\ 1288 | % k\_\textgreater{} & voiceless velar ejective & Georgian uk'an & xxx & ejective & velar \\ 1289 | % q\_\textgreater{} & voiceless uvular ejective & Georgian saavadmq'opo & xxx & ejective & uvular\\ 1290 | % p- & voiceless bilabial fricative & Japanese fu & xxx & fricative & bilabial \\ 1291 | % s\` & retroflex voiceless fricative & szyk & pol & fricative & retroflex \\ 1292 | % z\` & retroflex voiced fricative & żyto & pol & fricative & retroflex \\ 1293 | % j- & voiced palatal fricative & Spanish ayuda & xxx & fricative & palatal \\ 1294 | % M- & velar approximant & Spanish algo & xxx & approximant & velar \\ 1295 | % X- & voiceless pharyngeal fricative & Arabic h.\^a & xxx & fricative & pharyngeal \\ 1296 | %?{-} & voiced pharyngeal fricative & Arabic \'ayn & xxx & fricative & pharyngeal \\ 1297 | % h- & voiced glottal fricative & English & use & fricative & glottal \\ 1298 | % r- & alveolar approximant & English run & xxx & approximant & alveolar \\ 1299 | % N= & syllabic velar nasal & English walking & use & nasal & velar \\ 1300 | % n= & syllabic alveolar nasal & English Boston & use & nasal & alveolar \\ 1301 | % m= & syllabic bilabial nasal & English bottom & use & nasal & bilabial \\ 1302 | % l= & syllabic lateral & English bridal & use & lateral-approximant & alveolar \\ 1303 | %\end{longtable} 1304 | %} 1305 | 1306 | \newpage 1307 | \section{Version history} 1308 | \begin{longtable}{|p{0.22\linewidth}p{0.8\linewidth}|} 1309 | \toprule 1310 | 2.0 (2016{-}12{-}28) & \tabitem{} Add orthographic word level alignment.\\ 1311 | & \tabitem{} Clean up book source code and makefile.\\ 1312 | \midrule 1313 | 1.9 (2016{-}01{-}19) & \tabitem{} Better errors in textwindow.\\ 1314 | & \tabitem{} Phonetizer file selector in settings.\\ 1315 | & \tabitem{} Added the errors to align tier.\\ 1316 | & \tabitem{} Temp files are cleaned up before alignment, therefore no 1317 | ghost annotations when the alignment has failed.\\ 1318 | \midrule 1319 | 1.8 (2015{-}11{-}19) & \tabitem{} Added support for \texttt{Sound}.\\ 1320 | & \tabitem{} Changed email address of author.\\ 1321 | & \tabitem{} Elaborated the installation instructions.\\ 1322 | & \tabitem{} Splitted up the model and phonetizer selection.\\ 1323 | & \tabitem{} Cleaned up the phone specifications.\\ 1324 | & \tabitem{} Added an universal phonetizer.\\ 1325 | & \tabitem{} Big book update with structural changes.\\ 1326 | \midrule 1327 | 1.7a (2015{-}10{-}13) & \tabitem{} Fixed critical tier creation bug.\\ 1328 | & \tabitem{} Added version to settings window.\\ 1329 | \midrule 1330 | 1.7 (2015{-}10{-}08) & \tabitem{} More robuust tier creation.\\ 1331 | & \tabitem{} Some typos fixed in the manual.\\ 1332 | & \tabitem{} Updated the bibtex snippet.\\ 1333 | & \tabitem{} Better tier alignment when the tier is empty.\\ 1334 | & \tabitem{} Corrected some spelling errors.\\ 1335 | & \tabitem{} Tidied the makefile for the book.\\ 1336 | \midrule 1337 | 1.6a (2015{-}09{-}29) & \tabitem{} Fixed the dictionary generator for English.\\ 1338 | & \tabitem{} Correctly added websites to the authors of the models.\\ 1339 | \midrule 1340 | 1.60 (2015{-}09{-}07) & \tabitem{} Fixed menu name from \texttt{force} to 1341 | \texttt{forced}.\\ 1342 | & \tabitem{} Fixed silence problem in experimental models.\\ 1343 | & \tabitem{} Added version to setup dialog.\\ 1344 | & \tabitem{} Added language for using the general sampa model without 1345 | phonetizer.\\ 1346 | & \tabitem{} Created a better error when loading rulesets or dictionaries in 1347 | non supported encodings or non existent files.\\ 1348 | & \tabitem{} Changed the licence to MIT.\\ 1349 | \midrule 1350 | 1.50 (2015{-}07{-}07) & \tabitem{} Spanish models merged in original master.\\ 1351 | & \tabitem{} Procedures in different file, thus cleaner code.\\ 1352 | & \tabitem{} Reinitialized repo.\\ 1353 | & \tabitem{} Faster book compilation.\\ 1354 | \midrule 1355 | 1.40 (2015{-}07{-}01) & \tabitem{} Added branch for own spanish models.\\ 1356 | & \tabitem{} Updated book about ruleset.\\ 1357 | & \tabitem{} Changed name in menu from \texttt{Setup\ldots} to 1358 | \texttt{Set up\dots}.\\ 1359 | \midrule 1360 | 1.39 (2015{-}04{-}01) & \tabitem{} Fixed tier alignment.\\ 1361 | \midrule 1362 | 1.3 (2015{-}03{-}25) & \tabitem{} Added option for custom python path.\\ 1363 | & \tabitem{} Greatly simplified rulesets.\\ 1364 | \midrule 1365 | 1.2 (2015{-}02{-}18) & \tabitem{} Simplified installation scripts.\\ 1366 | & \tabitem{} Fixed a unicode bug in generating dictionaries.\\ 1367 | \midrule 1368 | 1.1 (2015{-}01{-}30) & \tabitem{} Implemented better error messaging.\\ 1369 | & \tabitem{} Simplified the python code into one file.\\ 1370 | & \tabitem{} Fixed a bug that leaded to a messed up view.\\ 1371 | & \tabitem{} Fixed small phonetization errors.\\ 1372 | \midrule 1373 | 1.0 (2015{-}01{-}09) & \tabitem{} Converted the readme to a pdf.\\ 1374 | & \tabitem{} Speeded up the process by disabling pitch, intensity, 1375 | spectrum, pulses and formants while aligning, the settings do get restored 1376 | afterwards.\\ 1377 | & \tabitem{} Fixed a bug that leaded to a messed up view.\\ 1378 | \midrule 1379 | 0.9a (2014{-}12{-}02) & \tabitem{} Small bugfix in dictionary generation fixed.\\ 1380 | \midrule 1381 | 0.9 & \tabitem{} Cleaned up some parts of the readme.\\ 1382 | & \tabitem{} Added language specific information.\\ 1383 | & \tabitem{} Added english as language. Although there is no phonetizing 1384 | implemented.\\ 1385 | & \tabitem{} README.html better with light background for code blocks.\\ 1386 | & \tabitem{} Updated citing method with bibtex.\\ 1387 | \midrule 1388 | 0.8 (2014{-}10{-}31) & \tabitem{} Removed all the binary folders.\\ 1389 | & \tabitem{} Made the binary finding interactive.\\ 1390 | & \tabitem{} Made all the file chooser dialogs interactive.\\ 1391 | \midrule 1392 | 0.7 (2014{-}10{-}29) & \tabitem{} Added windows support.\\ 1393 | & \tabitem{} Cleaned up documentation.\\ 1394 | & \tabitem{} Removed binaries due htk licence.\\ 1395 | \midrule 1396 | 0.6 (2014{-}10{-}22) & \tabitem{} Refactored and cleaned up the source.\\ 1397 | \midrule 1398 | 0.5a (2014{-}09{-}08) & \tabitem{} Added comments to source code (praat).\\ 1399 | & \tabitem{} Cleaned up source.\\ 1400 | \midrule 1401 | 0.5 (2014{-}09{-}04) & \tabitem{} Fixed acronyms in spanish.\\ 1402 | & \tabitem{} Fixed cleaning with extended boundaries.\\ 1403 | & \tabitem{} Added rudimentary ruleset implementation.\\ 1404 | \midrule 1405 | 0.4 (2014{-}08{-}29) & \tabitem{} Added option for enlarging the boundaries 1406 | automatically.\\ 1407 | \midrule 1408 | 0.21 (2014{-}08{-}13) & \tabitem{} Settings split in non interactive and 1409 | interactive so that the interactive one reflects the current settings.\\ 1410 | \midrule 1411 | 0.2 (2014{-}08{-}11) & \tabitem{} Better mac compatibility.\\ 1412 | \midrule 1413 | 0.1a (2014{-}06{-}30) & \tabitem{} Tier alignment fixed.\\ 1414 | & \tabitem{} Readme for dutch.\\ 1415 | \midrule 1416 | 0.08 (2014{-}04{-}29) & \tabitem{} Cleaned up some stuff.\\ 1417 | & \tabitem{} Added dutch.\\ 1418 | & \tabitem{} Readme for spanish and sampa.\\ 1419 | \midrule 1420 | 0.07 (2014{-}04{-}28) & \tabitem{} Non interactive alignment implemented.\\ 1421 | & \tabitem{} Table of contents in readme.\\ 1422 | \midrule 1423 | 0.06 (2014{-}04{-}25) & \tabitem{} Conversion to editor scripts.\\ 1424 | \midrule 1425 | 0.05 (2014{-}04{-}03) & \tabitem{} Better readme.\\ 1426 | & \tabitem{} Functional program for linux.\\ 1427 | \midrule 1428 | 0.04 (2014{-}04{-}03) & \tabitem{} Pronunciation variants implemented.\\ 1429 | \midrule 1430 | 0.03 (2014{-}03{-}31) & \tabitem{} Aligner works in python.\\ 1431 | \midrule 1432 | 0.02 (2014{-}03{-}27) & \tabitem{} Python script around aligner started.\\ 1433 | & \tabitem{} Phonetizer skeleton done.\\ 1434 | \bottomrule 1435 | \caption{Version history} 1436 | \end{longtable} 1437 | 1438 | \bibliographystyle{ieeetr} 1439 | \bibliography{book} 1440 | 1441 | \end{document} 1442 | -------------------------------------------------------------------------------- /book/preamble.tex: -------------------------------------------------------------------------------- 1 | \documentclass[a4paper]{book} 2 | 3 | \usepackage{graphicx} 4 | \usepackage[dvipdfm]{hyperref} 5 | \usepackage[utf8]{inputenc} 6 | \usepackage{authblk} 7 | \usepackage{float} 8 | \usepackage{booktabs} 9 | \usepackage{longtable} 10 | \usepackage{multirow} 11 | \usepackage{listings} 12 | \usepackage{geometry} 13 | 14 | \author[1]{Mart Lubbers} 15 | \author[2]{Francisco Torreira} 16 | \affil[1]{\url{mart@martlubbers.net}} 17 | \affil[2]{\url{francisco.torreira@mpi.nl}} 18 | \title{Praatalign: an interactive Praat plug-in for performing phonetic forced 19 | alignment\\\large A detailed manual for version $2.0$} 20 | \date{\today} 21 | \hypersetup{% 22 | pdftitle={Praatalign detailed manual}, 23 | pdfauthor={Mart Lubbers and Francisco Torreira}, 24 | pdfsubject={Praatalign}, 25 | pdfproducer={Mart Lubbers}, 26 | pdfkeywords={praat,htk,phonetic forced alignment}, 27 | hidelinks 28 | } 29 | 30 | \lstset{% 31 | basicstyle=\footnotesize, 32 | breaklines=true, 33 | captionpos=b, 34 | frame=L, 35 | numbers=left 36 | } 37 | 38 | \newcommand{\tabitem}{~~\llap{\textbullet}~~} 39 | 40 | \graphicspath{{./img/}} 41 | -------------------------------------------------------------------------------- /cleaninterval.praat: -------------------------------------------------------------------------------- 1 | include procs.praat 2 | selection_start = Get start of selection 3 | selection_end = Get end of selection 4 | editor_info$ = TextGrid info 5 | textgrid_object$ = "TextGrid " + extractLine$(editor_info$, "Object name: ") 6 | editor_info$ = Editor info 7 | selected_tier_number = extractNumber(editor_info$, "Selected tier:") 8 | endeditor 9 | @cleanAnnotation: textgrid_object$, selected_tier_number, selection_start, selection_end 10 | -------------------------------------------------------------------------------- /generatedict.praat: -------------------------------------------------------------------------------- 1 | # Extract settings 2 | if not fileReadable("settings") 3 | exitScript("No settings file found, please run the setup first") 4 | endif 5 | settings$ = readFile$("settings") 6 | tmpfile$ = extractLine$(settings$, "OUT: ") 7 | editor_info$ = Editor info 8 | curtier = extractNumber(editor_info$, "Selected tier:") 9 | 10 | # Extract tier 11 | Extract entire selected tier 12 | endeditor 13 | textgrid_object$ = selected$("TextGrid", 1) 14 | 15 | # Create table and save 16 | Down to Table... "no" 6 "yes" "no" 17 | Save as tab-separated file... 'tmpfile$' 18 | 19 | # Clean up 20 | Remove 21 | select TextGrid 'textgrid_object$' 22 | Remove 23 | 24 | # Ask for location and write the dictionary 25 | dict_filepath$ = chooseWriteFile$("Pick the location for the dictionary", 26 | ..."missing.txt") 27 | system python generatedict.py 'dict_filepath$' 28 | pause "Dictionary successfully generated" 29 | -------------------------------------------------------------------------------- /generatedict.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import codecs 5 | import phonetizer as ph 6 | import sys 7 | import os 8 | 9 | # Load the settings 10 | with open('settings', 'r') as f: 11 | sett = {k: v.strip() for k, v in map(lambda x: x.split(': '), f)} 12 | 13 | try: 14 | os.remove('temp.status') 15 | except: 16 | pass 17 | 18 | # Load the phonetizer 19 | try: 20 | phone = ph.getphonetizer( 21 | sett['LAN'], sett['PHO'], sett['DCT'], sett['RUL']) 22 | except UnicodeError: 23 | with open('temp.status', 'w') as f: 24 | f.write('Unicode error. Check if the files are utf-8') 25 | exit() 26 | except IOError as e: 27 | if e.filename == sett['DCT']: 28 | error = 'Dictionary file couldn\'t be found\n'\ 29 | 'It is searched for in {}'.format(e.filename) 30 | elif e.filename == sett['RUL']: 31 | error = 'Ruleset file couldn\'t be found\n'\ 32 | 'It is searched for in {}'.format(e.filename) 33 | elif e.filename == sett['PHO']: 34 | error = 'Universal phonetizer file couldn\'t be found\n'\ 35 | 'It is searched for in {}'.format(e.filename) 36 | else: 37 | error = 'Some io error: ' + str(e) 38 | with open('temp.status', 'w') as f: 39 | f.write(error) 40 | exit() 41 | 42 | # Open the output file and put all missing words in the set 43 | mis = set() 44 | with codecs.open(sett['OUT'], 'r', 'utf-8') as i: 45 | for line in i: 46 | words = line.split('\t')[2] 47 | for word in words.split(): 48 | if phone.phonetizeword(word) is None: 49 | mis.add(word) 50 | 51 | # Write the set to the specified file 52 | with codecs.open(' '.join(sys.argv[1:]), 'w', 'utf-8') as o: 53 | for word in mis: 54 | o.write(u'{}\t\n'.format(word)) 55 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | if [ "$(id -u)" == "0" ]; then 4 | echo "This script should not need root rights..." 1>&2 5 | echo "Please press CRTL+C and rerun the script as a normal user" 6 | echo "If you still wish to continue type PLEASE" 7 | read line 8 | if [ "$line" -ne "PLEASE" ]; then 9 | exit 10 | fi 11 | fi 12 | case "$OSTYPE" in 13 | solaris* | *bsd* | linux*) 14 | echo "Detected LINUX/SOLARIS/BSD" 15 | dir="$HOME/.praat-dir/plugin_pralign/" 16 | ;; 17 | darwin*) 18 | echo "Detected MAC" 19 | dir="$HOME/Library/Preferences/Praat Prefs/plugin_pralign" 20 | ;; 21 | msys* | win32*) 22 | echo "You are running windows? Please run install_win.bat" 23 | exit 2 24 | ;; 25 | *) 26 | echo "Unknown operating system..." 27 | exit 2 28 | esac 29 | scriptdir="$(cd "$(dirname "$0")" && pwd)" 30 | 31 | set -x 32 | mkdir -p "$dir" 33 | rm -fr "$dir"/* || true 34 | cp -fR "$scriptdir/"*.{py,praat} "$dir" 35 | cp -fR "$scriptdir/"par.* "$dir" 36 | set +x 37 | echo "Installing complete" 38 | -------------------------------------------------------------------------------- /install_win.bat: -------------------------------------------------------------------------------- 1 | setlocal enabledelayedexpansion 2 | @ECHO OFF 3 | SET _plugindir=%USERPROFILE%\Praat\plugin_pralign 4 | RD /Q /S "%_plugindir%" 5 | MD "%_plugindir%" 6 | COPY %~dp0*.py "%_plugindir%" 7 | COPY %~dp0*.praat "%_plugindir%" 8 | MD "%_plugindir%\par.spanish" 9 | COPY par.spanish\* "%_plugindir%\par.spanish" 10 | MD "%_plugindir%\par.dutch" 11 | COPY par.dutch\* "%_plugindir%\par.dutch" 12 | echo Installing completed, please press enter to close this window... 13 | pause > nul 14 | -------------------------------------------------------------------------------- /mkrelease.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | VERSION=2.0a 3 | zip -9rv praatalign_$VERSION.zip *.{praat,py,md} {install,par,LICENCE}* 4 | tar -cvf praatalign_$VERSION.tar *.{praat,py,md} {install,par,LICENCE}* 5 | xz -ve praatalign_$VERSION.tar 6 | -------------------------------------------------------------------------------- /par.dutch/DICT: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | a:~ a: 5 | o:~ o: 6 | E:~ E: 7 | O:~ O 8 | a:i aI 9 | o:i OY 10 | e:u OY 11 | P2: P2: 12 | e: e: 13 | E~ E 14 | a: a: 15 | o: o: 16 | E: E: 17 | P9: P9 18 | O: O 19 | EI aI 20 | @U aU 21 | Ei aI 22 | P9y OY 23 | Au aU 24 | ui OY 25 | iu u: 26 | yu u: 27 | O~ O 28 | p p 29 | b b 30 | t t 31 | T s 32 | d d 33 | D v 34 | k k 35 | g g 36 | G C 37 | f f 38 | v v 39 | w v 40 | s s 41 | z z 42 | S S 43 | Z S 44 | j j 45 | x x 46 | h h 47 | m m 48 | n n 49 | N N 50 | l l 51 | r r 52 | I I 53 | i i: 54 | E E 55 | A O 56 | O O 57 | u u: 58 | Y Y 59 | y y: 60 | @ @ 61 | ? ? 62 | < 63 | > 64 | # 65 | -------------------------------------------------------------------------------- /par.dutch/HVITECONF: -------------------------------------------------------------------------------- 1 | ALLOWCXTEXP=F 2 | ALLOWXWRDEXP=F 3 | FORCEOUT=T 4 | -------------------------------------------------------------------------------- /par.dutch/MONOPHONES: -------------------------------------------------------------------------------- 1 | P2: 2 | @ 3 | C 4 | E 5 | E: 6 | I 7 | N 8 | O 9 | OY 10 | ? 11 | S 12 | U 13 | Z 14 | P6 15 | P9 16 | a 17 | a: 18 | aI 19 | aU 20 | b 21 | d 22 | e 23 | e: 24 | f 25 | g 26 | h 27 | i: 28 | j 29 | k 30 | l 31 | m 32 | n 33 | 34 | o 35 | o: 36 | p 37 | 38 | r 39 | s 40 | t 41 | u: 42 | v 43 | x 44 | y: 45 | z 46 | Y 47 | < 48 | > 49 | -------------------------------------------------------------------------------- /par.dutch/PRECONFIGNIST: -------------------------------------------------------------------------------- 1 | SUOURCEKIND = WAVEFORM 2 | TARGETKIND = MFCC_E_D_A 3 | TARGETFORMAT = HTK 4 | SOURCEFORMAT = NIST 5 | SOURCERATE = 625 6 | TARGETRATE = 100000 7 | WINDOWSIZE = 200000 8 | NUMCHANS = 26 9 | CEPLIFTER = 22 10 | NUMCEPS = 12 11 | -------------------------------------------------------------------------------- /par.spanish/DICT: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | p_h p 5 | t_h t 6 | k_h k 7 | P2: P2: 8 | aI aI 9 | aU aU 10 | OI OY 11 | eI aI 12 | eU OY 13 | @U aU 14 | rr r 15 | jj C 16 | e~ e 17 | a~ a 18 | o~ o: 19 | E~ E 20 | P9 P9 21 | P6 P6 22 | y: y: 23 | tt t 24 | pp p 25 | kk k 26 | p p 27 | b b 28 | t t 29 | T s 30 | d d 31 | D v 32 | k k 33 | g g 34 | f f 35 | B v 36 | s s 37 | z z 38 | S S 39 | w v 40 | x x 41 | h h 42 | G C 43 | v m 44 | n n 45 | m m 46 | J N 47 | N N 48 | F m 49 | l l 50 | L j 51 | j j 52 | r r 53 | i i: 54 | e e 55 | E E 56 | a a 57 | o o: 58 | u u: 59 | @ @ 60 | Y Y 61 | < < 62 | > > 63 | # 64 | -------------------------------------------------------------------------------- /par.spanish/HVITECONF: -------------------------------------------------------------------------------- 1 | ALLOWCXTEXP=F 2 | ALLOWXWRDEXP=F 3 | FORCEOUT=T 4 | -------------------------------------------------------------------------------- /par.spanish/MONOPHONES: -------------------------------------------------------------------------------- 1 | P2: 2 | @ 3 | C 4 | E 5 | E: 6 | I 7 | N 8 | O 9 | OY 10 | S 11 | U 12 | Z 13 | P6 14 | P9 15 | a 16 | a: 17 | aI 18 | aU 19 | b 20 | d 21 | e 22 | e: 23 | f 24 | g 25 | h 26 | i: 27 | j 28 | k 29 | l 30 | m 31 | n 32 | 33 | o 34 | o: 35 | p 36 | 37 | r 38 | s 39 | t 40 | u: 41 | v 42 | x 43 | y: 44 | z 45 | Y 46 | < 47 | > 48 | -------------------------------------------------------------------------------- /par.spanish/PRECONFIGNIST: -------------------------------------------------------------------------------- 1 | SOURCEKIND = WAVEFORM 2 | TARGETKIND = MFCC_E_D_A 3 | TARGETFORMAT = HTK 4 | SOURCEFORMAT = NIST 5 | SOURCERATE = 625 6 | TARGETRATE = 100000 7 | WINDOWSIZE = 200000 8 | NUMCHANS = 26 9 | CEPLIFTER = 22 10 | NUMCEPS = 12 11 | -------------------------------------------------------------------------------- /phonetizer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import codecs 4 | import re 5 | import unicodedata 6 | import itertools 7 | 8 | 9 | class tdict(dict): 10 | """Dictionary with a missing value that is the normalized version 11 | of the input 12 | """ 13 | def __missing__(self, key): 14 | char = unicodedata.normalize('NFKD', unicode(key)) 15 | return str(char.encode('ascii', 'ignore')).lower() 16 | 17 | 18 | class Phonetizer: 19 | """Skeleton class for a phonetizer for the praat align program""" 20 | reps = [(r'\v', '[aoeiu]'), (r'\c', '[^aoeui]'), ('\n', '')] 21 | 22 | def __init__(self, univ_phon=None, dictpath=None, ruleset=None): 23 | """Constructor with an optional dictionary 24 | 25 | filters -- A regular expression of things to filter out of the 26 | utterances 27 | dictpath -- Path to an optional dictonary 28 | ruleset -- Path to an optional ruleset file 29 | univ_phon -- Path to an optional universal phonetizer file 30 | """ 31 | self.r = [] 32 | self.dictionary = dict() 33 | self.univ_phon = [] 34 | if dictpath is not None: 35 | with codecs.open(dictpath, 'r', 'utf-8') as f: 36 | for l in f: 37 | if l and l[0] != '#': 38 | l = [s.strip() for s in l.split('\t')] 39 | self.dictionary[l[0]] = [var.split() for var in 40 | map(str, l[1:])] 41 | if ruleset is not None: 42 | with codecs.open(ruleset, 'r', 'utf-8') as f: 43 | for l in itertools.ifilter(lambda x: x[0] != '#', f): 44 | if not l.startswith('\t'): 45 | l = re.sub(r'([.^$*+?{}[\]\|()])', r'\\\1', l) 46 | else: 47 | for target, repl in self.reps: 48 | l = l.replace(target, repl) 49 | self.r.append(l.strip().split('\t')) 50 | if univ_phon is not None: 51 | with codecs.open(univ_phon, 'r', 'utf-8') as f: 52 | for line in f: 53 | line = [l.strip() for l in line.split('\t')] 54 | if line: 55 | self.univ_phon.append((line[0], line[1].split(' '))) 56 | 57 | def todawg(self, pron): 58 | """Converts the pronunciation variants and rules to a graph 59 | representation. 60 | 61 | pron -- Pronunciation 62 | """ 63 | # Make a translation for all multi character phones 64 | c2 = [ch for wd in pron for var in wd for ch in var if len(ch) > 1] 65 | c2 = dict(zip(c2, map(chr, range(1, len(c2)+1)))) 66 | 67 | # Create all possible combinations of pronunciation variants 68 | pron = ((' '.join(varnt + ['#']) 69 | for varnt in word) for word in pron) 70 | 71 | # Make strings of these variants 72 | pron = (' '.join(x) for x in itertools.product(*pron)) 73 | # Create combinations of all rulesets 74 | rcs = [()] 75 | for i in range(len(self.r)): 76 | rcs += itertools.combinations(self.r, i+1) 77 | all_prons = set() 78 | # For every variant 79 | for variant in pron: 80 | # For every combination of rules 81 | for combo in rcs: 82 | all_prons.add(variant) 83 | # For every rule combination 84 | for pat, target in combo: 85 | app = sorted(x.span() for x in re.finditer(pat, variant)) 86 | apporders = [()] 87 | for i in range(len(app)): 88 | apporders += itertools.permutations(app, i+1) 89 | # For every appliance order 90 | for appc in reversed(apporders): 91 | wordapp = variant 92 | for st, en in appc: 93 | try: 94 | wordapp = '{}{}{}'.format( 95 | wordapp[:st], 96 | re.sub(pat, target, wordapp[st:en]), 97 | wordapp[en:]) 98 | except: 99 | pass 100 | all_prons.add(wordapp) 101 | 102 | # Replace all the multichar phones with their appropriate byte 103 | for f, t in c2.iteritems(): 104 | all_prons = {x.replace(f, t) for x in all_prons} 105 | all_prons = {x.replace(' ', '') for x in all_prons} 106 | c2 = {v: k for k, v in c2.iteritems()} 107 | 108 | # Add all the words to the DAWG 109 | import pyDAWG 110 | pyd = pyDAWG.DAWG() 111 | for word in sorted(all_prons): 112 | pyd.add_word(word) 113 | 114 | # Create a slf with breath first search and use a translation list to 115 | # map the unnumbered nodes to their numbered variants 116 | nodenum = 0 117 | final_nodes, nodes, edges, translation = [], [], [], [] 118 | queue = [(0, pyd.q0)] 119 | visited = set() 120 | if pyd.q0.final: 121 | final_nodes.append(nodenum) 122 | else: 123 | nodes.append(nodenum) 124 | nodenum += 1 125 | # While we still have nodes to visit 126 | while queue: 127 | # If we have not visited the newest node 128 | current = queue.pop() 129 | if not current[0] in visited: 130 | # Visit the node 131 | visited.add(current[0]) 132 | # Go throught all the children 133 | for char, child in current[1].children.iteritems(): 134 | # Find translation for the child 135 | matches = [c for c in translation if c[0] == child] 136 | curnum = -1 137 | # Translate if there is a translation 138 | if matches: 139 | curnum = matches[-1][1] 140 | # Otherwise create a translation 141 | else: 142 | translation.append((child, nodenum)) 143 | curnum = nodenum 144 | nodenum += 1 145 | # If the node is final, mark is as such 146 | if child.final: 147 | final_nodes.append(curnum) 148 | else: 149 | nodes.append(curnum) 150 | # Append the edge to the list and append the child to the 151 | # queue 152 | edges.append((current[0], char, curnum)) 153 | queue.append((curnum, child)) 154 | # Convert the edges to a dictionary with for every number a set of 155 | # connections and convert the nodes to a dictionary with for every 156 | # number the appropriate character. Also convert the multichar phones 157 | # back to their original form. 158 | nodes += final_nodes 159 | nnodes = {0: '<'} 160 | nedges = {} 161 | for fr, ch, to in edges: 162 | nnodes[to] = c2[ch] if ch in c2 else ch 163 | if fr not in nedges: 164 | nedges[fr] = set() 165 | nedges[fr].add(to) 166 | # Find the last node and remember the position to connect the end of 167 | # the words to it 168 | finalnode = len(nnodes) 169 | nnodes[finalnode] = '>' 170 | for final in final_nodes: 171 | if final not in nedges: 172 | nedges[final] = set() 173 | nedges[final].add(finalnode) 174 | return nedges, nnodes 175 | 176 | def todot(self, nedges, nnodes): 177 | dot = 'digraph {rankdir=LR;' 178 | dot += ''.join('q{} [label="{}"];'.format(*x) for x in nnodes.items()) 179 | for fr, to in nedges.iteritems(): 180 | for c in to: 181 | dot += 'q{} -> q{};'.format(fr, c) 182 | return dot + '}' 183 | 184 | def toslf(self, nedges, nnodes): 185 | slf = 'N={} L={}\n'.format( 186 | len(nnodes), sum(len(to) for _, to in nedges.iteritems())) 187 | # Write all the nodes 188 | slf += ''.join( 189 | 'I={} W={}\n'.format(k, v) for k, v in nnodes.iteritems()) 190 | # Write all the edges 191 | i = 0 192 | for fr, to in nedges.iteritems(): 193 | for c in to: 194 | slf += 'J={} S={} E={}\n'.format(i, fr, c) 195 | i += 1 196 | return slf 197 | 198 | def permute(self, items, output): 199 | """Helper function that creates all possible combinations of a list 200 | 201 | items -- Possible items 202 | output -- Results list 203 | """ 204 | # Append the current combination 205 | output.append(items) 206 | # For all nodes left remove 1 and put the resulting list back in 207 | for i, item in enumerate(items): 208 | items2 = items[:] 209 | items2.remove(item) 210 | self.permute(items2, output) 211 | 212 | def applyrules(self, phon): 213 | """Apply the rules specified in the ruleset 214 | 215 | phon - phonetization of the utterance 216 | """ 217 | if phon is not None and len(phon) == 1: 218 | word = ''.join(phon[0]) 219 | for rule in self.r: 220 | mo = rule.search(word) 221 | if mo is not None: 222 | wordafter = word[:mo.end('fr')]+word[mo.start('to'):] 223 | phon.append(list(wordafter)) 224 | return phon 225 | 226 | def phonetize(self, utterance): 227 | """Phonetizes one utterance 228 | 229 | utterance -- The utterance to phonetize 230 | """ 231 | try: 232 | pron = [self.phonetizeword(unicode(word, 'utf-8')) for 233 | word in utterance.split()] 234 | except TypeError: 235 | pron = [self.phonetizeword(unicode(word)) for 236 | word in utterance.split()] 237 | pron = filter(None, pron) 238 | return pron 239 | 240 | def phonetizeword(self, word): 241 | """Returns a list of phones generated from the utterance and should 242 | return None when unable to phonetize 243 | 244 | word -- the word to phonetize 245 | """ 246 | if word in self.dictionary: 247 | return self.dictionary[word] 248 | else: 249 | return None 250 | 251 | 252 | class PhonetizerTzeltal(Phonetizer): 253 | """Phonetizer for the tzeltal language""" 254 | 255 | trans = tdict({'j': 'x', 'w': 'b', 'x': 'S', '\'': '?', 'y': 'j'}) 256 | 257 | def phonetizeword(self, word): 258 | word = re.sub('[.,\-]', '', word.lower()) 259 | if word in self.dictionary: 260 | return self.dictionary[word] 261 | phonemap = list() 262 | it = iter(enumerate(word)) 263 | for i, character in it: 264 | if character == 'c' and i+1 < len(word) and word[i+1] == 'h': 265 | if i+2 < len(word) and word[i+2] == '\'': 266 | next(it, None) 267 | phonemap.append('ts_j') 268 | next(it, None) 269 | elif character == 't' and i+1 < len(word) and word[i+1] == '\'': 270 | phonemap.append('t') 271 | next(it, None) 272 | elif character == 't' and i+1 < len(word) and word[i+1] == 'z': 273 | if i+2 < len(word) and word[i+2] == '\'': 274 | next(it, None) 275 | phonemap.append('ts_j') 276 | next(it, None) 277 | else: 278 | phonemap.append(self.trans[character]) 279 | self.dictionary[word] = [phonemap] 280 | return [phonemap] 281 | 282 | 283 | class PhonetizerSpanish(Phonetizer): 284 | """Phonetizer for the spanish language""" 285 | 286 | acronymmap = tdict({ 287 | 'B': ['b', 'e'], 'C': ['T', 'e'], 'D': ['d', 'e'], 288 | 'F': ['e', 'f', 'e'], 'G': ['x', 'e'], 'H': ['a', 't', 'S', 'e'], 289 | 'J': ['x', 'o', 't', 'a'], 'K': ['k', 'a'], 'L': ['e', 'l', 'e'], 290 | 'M': ['e', 'm', 'e'], 'N': ['e', 'n', 'e'], 'Q': ['k', 'u'], 291 | 'R': ['e', 'r', 'r', 'e'], 'S': ['e', 's', 'e'], 'T': ['t', 'e'], 292 | 'V': ['u', 'b', 'u'], 'W': ['u', 'b', 'u', 'd', 'o', 'b', 'l', 'e'], 293 | 'X': ['e', 'k', 'i', 's'], 'Y': ['i', 'g', 'r', 'i', 'e', 'g', 'a'], 294 | 'Z': ['T', 'e', 't', 'a'] 295 | }) 296 | 297 | trans = tdict({'ñ'.decode('utf-8'): 'J', 'ç'.decode('utf-8'): 'T', 298 | 'j': 'x', 'c': 'k', 'v': 'b', 'w': 'b', 'z': 'T', 'y': 'j'}) 299 | 300 | def phonetizeword(self, word): 301 | word = ''.join(ch for ch in word if 302 | unicodedata.category(ch).startswith('L') or 303 | ch in '[]<>()&') 304 | word = re.sub('[<>]', '&', re.sub('\(.*\)', '', word)) 305 | uppercases = len([ch for ch in word if 306 | unicodedata.category(ch).startswith('Lu')]) 307 | if word in self.dictionary: 308 | return self.dictionary[word] 309 | # Check sounds and foreign language. 310 | if word and '&' == word[0] or '[lang' in word: 311 | return None 312 | # Remove optional laughter and breathing and try again to look it up 313 | if '[' in word or ']' in word: 314 | return self.phonetizeword(re.sub('\[.*\]', '', word)) 315 | # Allocate the map 316 | phonemap = list() 317 | if uppercases == len(word): 318 | return [[a for b in [self.acronymmap[i] for i in word] for a in b]] 319 | else: 320 | lw = word.lower() 321 | it = iter(enumerate(lw)) 322 | for i, ch in it: 323 | if ch == 'c' and i+1 < len(lw) and\ 324 | lw[i+1] in 'eií'.decode('utf-8'): 325 | phonemap.append('T') 326 | elif ch == 'c' and i+1 < len(lw) and lw[i+1] == 'h': 327 | phonemap += ['t', 'S'] 328 | next(it, None) 329 | elif ch == 'g' and i+1 < len(lw) and\ 330 | lw[i+1] == 'ü'.decode('utf-8'): 331 | phonemap += ['g', 'u'] 332 | next(it, None) 333 | elif ch == 'g' and i+2 < len(lw) and lw[i+1] == 'u' and\ 334 | lw[i+2] in 'ei': 335 | phonemap.append('g') 336 | next(it, None) 337 | elif ch == 'g' and i+1 < len(lw) and lw[i+1] in 'ei': 338 | phonemap.append('x') 339 | elif ch == 'l' and i+1 < len(lw) and lw[i+1] == 'l': 340 | phonemap.append('jj') 341 | next(it, None) 342 | elif ch == 'q': 343 | phonemap.append('k') 344 | if i+1 < len(lw) and lw[i+1] == 'u': 345 | next(it, None) 346 | elif ch == 'r' and ((i is 0 or lw[i-1] in 'nlsm') or 347 | (i+1 < len(lw) and lw[i+1] == 'r')): 348 | if i+1 < len(lw) and lw[i+1] == 'r': 349 | next(it, None) 350 | phonemap += ['rr'] 351 | elif ch == 'y' and i+1 >= len(lw): 352 | phonemap.append('i') 353 | elif ch == 'x': 354 | phonemap += ['k', 's'] 355 | elif ch == 'h': 356 | continue 357 | else: 358 | phonemap.append(self.trans[ch]) 359 | self.dictionary[word] = [phonemap] 360 | return [phonemap] 361 | 362 | 363 | class PhonetizerDictionary(Phonetizer): 364 | """Dummy phonetizer for dictionary only phonetizers""" 365 | 366 | def phonetizeword(self, word): 367 | if word in self.dictionary: 368 | return self.dictionary[word] 369 | else: 370 | return None 371 | 372 | 373 | class PhonetizerLoopback(Phonetizer): 374 | """Dummy phonetizer that uses direct loopback to generate phonetic 375 | transcription, therefore allowing the user to use his own phonetic 376 | transcription""" 377 | 378 | def phonetize(self, utterance): 379 | """Phonetizes one utterance 380 | 381 | utterance -- The utterance to phonetize 382 | """ 383 | return [[[a]] for a in utterance.split(' ')] 384 | 385 | 386 | class PhonetizerUniversal(Phonetizer): 387 | """Universal phonetizer that uses a po dictionary""" 388 | 389 | def phonetizeword(self, word): 390 | """Returns a list of phones generated from the utterance and should 391 | return None when unable to phonetize 392 | 393 | word -- the word to phonetize 394 | """ 395 | if word in self.dictionary: 396 | return self.dictionary[word] 397 | else: 398 | i = 0 399 | phones = [] 400 | while i <= len(word): 401 | replaces = [t for t in self.univ_phon if 402 | word[i:].startswith(t[0])] 403 | if replaces: 404 | phones += replaces[0][1] 405 | i += len(replaces[0][0]) 406 | else: 407 | i += 1 408 | return None if not phones else [phones] 409 | 410 | 411 | class PhonetizerSkeleton(Phonetizer): 412 | """Skeleton to create your own phonetizer""" 413 | 414 | def phonetizeword(self, word): 415 | # Substitute all transcription specific useless markings and lowercase 416 | # word 417 | # word = re.sub('[.,\-]', '', word.lower()) 418 | # If the word already exists in the dictionary return it immediatly 419 | # if word in self.dictionary: 420 | # return self.dictionary[word] 421 | # If some condition is not met and the word is unphonetizable you 422 | # should return None 423 | # if some_condition: 424 | # return None 425 | # Do the magic phonetizing here 426 | # Add the phontization to the dictionary 427 | # self.dictionary[word] = [phonemap] 428 | # Return the map 429 | # return [phonemap] 430 | pass 431 | 432 | phonetizerdict = { 433 | 'none': PhonetizerDictionary, 434 | 'universal': PhonetizerUniversal, 435 | 'spanish': PhonetizerSpanish, 436 | 'tzeltal': PhonetizerTzeltal 437 | } 438 | 439 | 440 | def getphonetizer(lang, univ_phon=None, dictpath=None, ruleset=None): 441 | """ 442 | Gives a phonetizer by code 443 | 444 | lang - language code, has to be present in phonetizerdict 445 | dictpath - optional dictionary file 446 | ruleset - optional ruleset file 447 | univ_phon - optional universal phonetizer file 448 | """ 449 | dictpath = None if dictpath == "None" else dictpath 450 | ruleset = None if ruleset == "None" else ruleset 451 | univ_phon = None if univ_phon == "None" else univ_phon 452 | return phonetizerdict[lang](univ_phon, dictpath, ruleset) 453 | -------------------------------------------------------------------------------- /procs.praat: -------------------------------------------------------------------------------- 1 | procedure loadSettings: 2 | if not fileReadable("settings") 3 | exitScript("No settings file found, please run the setup first") 4 | endif 5 | settings$ = readFile$("settings") 6 | phonetier_name$ = extractLine$(settings$, "NEW: ") 7 | wordtier_name$ = extractLine$(settings$, "WRD: ") 8 | cantier_name$ = extractLine$(settings$, "CAN: ") 9 | llhtier_name$ = extractLine$(settings$, "LLH: ") 10 | orttier_name$ = extractLine$(settings$, "ORT: ") 11 | tmpfile$ = extractLine$(settings$, "OUT: ") 12 | pythonex$ = extractLine$(settings$, "PY2: ") 13 | boundary_margin = extractNumber(settings$, "THR: ") 14 | endproc 15 | 16 | procedure loadFileInfo: 17 | # Try longsound first 18 | longsound_info$ = "" 19 | longsound_info$ = nocheck LongSound info 20 | if longsound_info$ <> "" 21 | sound_file$ = extractLine$(longsound_info$, "File name: ") 22 | sound_object$ = "LongSound " + extractLine$(longsound_info$, "Object name: ") 23 | sound_duration = extractNumber(longsound_info$, "Duration: ") 24 | else 25 | sound_info$ = Sound info 26 | sound_object$ = "Sound " + extractLine$(sound_info$, "Object name: ") 27 | sound_duration = extractNumber(sound_info$, " Total duration: ") 28 | Select: 0, sound_duration 29 | Save selected sound as WAV file: "longsound_sound.wav" 30 | sound_file$ = "longsound_sound.wav" 31 | endif 32 | 33 | textgrid_info$ = TextGrid info 34 | textgrid_object$ = "TextGrid " + extractLine$(textgrid_info$, "Object name: ") 35 | 36 | editorinfo$ = Editor info 37 | selected_tier = extractNumber(editorinfo$, "Selected tier:") 38 | pitch_on = if extractWord$(editorinfo$, "Pitch show: ") == "yes" 39 | ... then 1 else 0 fi 40 | intensity_on = if extractWord$(editorinfo$, "Intensity show: ") == "yes" 41 | ... then 1 else 0 fi 42 | spectrum_on = if extractWord$(editorinfo$, "Spectrogram show: ") == "yes" 43 | ... then 1 else 0 fi 44 | formant_on = if extractWord$(editorinfo$, "Formant show: ") == "yes" 45 | ... then 1 else 0 fi 46 | pulses_on = if extractWord$(editorinfo$, "Pulses show: ") == "yes" 47 | ... then 1 else 0 fi 48 | endproc 49 | 50 | procedure indexOfTier: .name$ 51 | .inserted = 0 52 | if .name$ <> "" 53 | .number = -1 54 | .ntiers = Get number of tiers 55 | for .i to .ntiers 56 | .tiername$ = Get tier name: .i 57 | if .tiername$ = .name$ 58 | .number = .i 59 | endif 60 | endfor 61 | if .number = -1 62 | selected_tier = selected_tier + 1 63 | Insert interval tier: 1, .name$ 64 | .number = 1 65 | .inserted = 1 66 | endif 67 | endif 68 | endproc 69 | 70 | procedure cleanAnnotation: .obj$, .num, .start, .end 71 | editor: .obj$ 72 | .selected_tier_num = -1 73 | repeat 74 | Select next tier 75 | .editor_info$ = Editor info 76 | .selected_tier_num = extractNumber(.editor_info$, "Selected tier:") 77 | until .num = .selected_tier_num 78 | Select: .start, .end 79 | 80 | Move cursor to: .start 81 | .interval_end = Get end point of interval 82 | 83 | .to_remove_len = 0 84 | while .interval_end < .end 85 | .to_remove_len = .to_remove_len + 1 86 | Select next interval 87 | .to_remove[.to_remove_len] = Get starting point of interval 88 | .interval_end = Get end point of interval 89 | endwhile 90 | endeditor 91 | 92 | selectObject: .obj$ 93 | for .i to .to_remove_len 94 | nocheck Remove boundary at time: .selected_tier_num, .to_remove[.i] 95 | endfor 96 | 97 | .tiernum = Get interval at time: .selected_tier_num, .start-0.01 98 | nocheck Set interval text: .selected_tier_num, .tiernum, "" 99 | endproc 100 | 101 | procedure toggleGUIValues: 102 | if pitch_on == 1 103 | Show pitch 104 | endif 105 | if intensity_on == 1 106 | Show intensity 107 | endif 108 | if spectrum_on == 1 109 | Show spectrogram 110 | endif 111 | if formant_on == 1 112 | Show formants 113 | endif 114 | if pulses_on == 1 115 | Show pulses 116 | endif 117 | endproc 118 | 119 | procedure getBinary: message$, path$, default$, .toask 120 | .ex$ = "" 121 | if .toask == 1 122 | beginPause: "Instructions" 123 | comment: "Please point me to the " + default$ + " executable." 124 | comment: "When " + default$ + " is in your " + path$ + " you can cancel this." 125 | comment: "It can be found " + message$ 126 | .clicked = endPause: "Cancel", "Continue", 2, 1 127 | if .clicked = 2 128 | .ex$ = chooseReadFile$("Point me to the " + default$ + " executable") 129 | endif 130 | if .ex$ = "" 131 | .ex$ = default$ 132 | endif 133 | endif 134 | endproc 135 | 136 | procedure insertTableTextGrid: .tablefile$, .obj$, .phon$, .ort$, .wrd$, .can$, .llh$, 137 | ... .phonn, .ortn, .wrdn, .cann, .llhn 138 | nocheck Read Table from comma-separated file: .tablefile$ 139 | if extractWord$(selected$(), "") == "Table" 140 | .number_rows = Get number of rows 141 | # Put the results in the textgrid 142 | for .i to .number_rows 143 | #Extract the values 144 | selectObject: "Table praat_temp_out" 145 | .current_start$ = Get value: .i, "start" 146 | .current_start = number(.current_start$) 147 | .current_end$ = Get value: .i, "end" 148 | .current_end = number(.current_end$) 149 | .current_value$ = Get value: .i, "label" 150 | .current_type$ = Get value: .i, "type" 151 | selectObject: .obj$ 152 | 153 | if .current_type$ = "p" and .phon$ <> "" 154 | nocheck Insert boundary: .phonn, .current_start 155 | nocheck Insert boundary: .phonn, .current_end 156 | .intnum = Get interval at time: .phonn, .current_start+0.0001 157 | nocheck Set interval text: .phonn, .intnum, .current_value$ 158 | elif .current_type$ = "w" and .wrd$ <> "" 159 | nocheck Insert boundary: .wrdn, .current_start 160 | nocheck Insert boundary: .wrdn, .current_end 161 | .intnum = Get interval at time: .wrdn, .current_start+0.0001 162 | nocheck Set interval text: .wrdn, .intnum, .current_value$ 163 | elif .current_type$ = "o" and .wrd$ <> "" 164 | nocheck Insert boundary: .ortn, .current_start 165 | nocheck Insert boundary: .ortn, .current_end 166 | .intnum = Get interval at time: .ortn, .current_start+0.0001 167 | nocheck Set interval text: .ortn, .intnum, .current_value$ 168 | elif .current_type$ = "c" and .can$ <> "" 169 | nocheck Insert boundary: .cann, .current_start 170 | nocheck Insert boundary: .cann, .current_end 171 | .intnum = Get interval at time: .cann, .current_start+0.0001 172 | nocheck Set interval text: .cann, .intnum, .current_value$ 173 | elif .current_type$ = "l" and .llh$ <> "" 174 | nocheck Insert boundary: .llhn, .current_start 175 | nocheck Insert boundary: .llhn, .current_end 176 | .intnum = Get interval at time: .llhn, .current_start+0.0001 177 | nocheck Set interval text: .llhn, .intnum, .current_value$ 178 | endif 179 | endfor 180 | 181 | # Remove temporary table file 182 | selectObject: "Table praat_temp_out" 183 | Remove 184 | endif 185 | endproc 186 | -------------------------------------------------------------------------------- /pyDAWG.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | This is part of pydawg Python module. 6 | 7 | Pure python implementation. 8 | 9 | Author : Wojciech Muła, wojciech_mula@poczta.onet.pl 10 | WWW : http://0x80.pl/proj/pydawg/ 11 | License : Public domain 12 | Date : $Date$ 13 | 14 | $Id$ 15 | """ 16 | 17 | 18 | class DAWGNode: 19 | __slots__ = ["children", "final", "number"] 20 | 21 | def __init__(self, char): 22 | self.children = {} 23 | self.final = False 24 | self.number = None 25 | 26 | def get_next(self, char): 27 | try: 28 | return self.children[char] 29 | except KeyError: 30 | return None 31 | 32 | def set_next(self, char, child): 33 | self.children[char] = child 34 | 35 | def has_transition(self, char): 36 | return char in self.children 37 | 38 | def __str__(self): 39 | return "<" + "".join(self.children.keys()) + ">" 40 | 41 | 42 | def equivalence(p, q): 43 | "check if states p and q are equivalent" 44 | 45 | if p.final != q.final: 46 | return False 47 | 48 | if len(p.children) != len(q.children): 49 | return False 50 | 51 | s = set(p.children) 52 | if s != set(q.children): 53 | return False 54 | 55 | """ 56 | # exact definition of equivalence 57 | for c in s: 58 | if not equivalence(p.children[c], q.children[c]): 59 | return False 60 | """ 61 | # pratical implementation - constraints make 62 | # this much simpler and faster 63 | for c in s: 64 | if p.children[c] != q.children[c]: 65 | return False 66 | 67 | return True 68 | 69 | 70 | class DAWG: 71 | def __init__(self): 72 | self._numbers_valid = False 73 | self.register = set() 74 | self.q0 = DAWGNode(None) 75 | self.wp = '' 76 | 77 | def add_word(self, word): 78 | assert word > self.wp 79 | return self.add_word_unchecked(word) 80 | 81 | def add_word_unchecked(self, word): 82 | # 1. skip existing 83 | i = 0 84 | s = self.q0 85 | while i < len(word) and s.has_transition(word[i]): 86 | s = s.get_next(word[i]) 87 | i = i + 1 88 | 89 | assert s is not None 90 | 91 | # 2. minimize 92 | if i < len(self.wp): 93 | self._replace_or_register(s, self.wp[i:]) 94 | 95 | # 3. add suffix 96 | while i < len(word): 97 | n = DAWGNode(word[i]) 98 | s.set_next(word[i], n) 99 | assert n == s.get_next(word[i]) 100 | s = n 101 | i = i + 1 102 | 103 | s.final = True 104 | self.wp = word 105 | self._numbers_valid = False 106 | 107 | def _replace_or_register(self, state, suffix): 108 | stack = [] 109 | while suffix: 110 | letter = suffix[0] 111 | next = state.get_next(letter) 112 | stack.append((state, letter, next)) 113 | 114 | state = next 115 | suffix = suffix[1:] 116 | 117 | while stack: 118 | parent, letter, state = stack.pop() 119 | 120 | found = False 121 | for r in self.register: 122 | if equivalence(state, r): 123 | assert(parent.children[letter] == state) 124 | parent.children[letter] = r 125 | 126 | found = True 127 | break 128 | 129 | if not found: 130 | self.register.add(state) 131 | 132 | def freeze(self): 133 | self._replace_or_register(self.q0, self.wp) 134 | self._numbers_valid = False 135 | 136 | close = freeze 137 | 138 | def _num_nodes(self): 139 | def clear_aux(node): 140 | node.number = None 141 | for child in node.children.values(): 142 | clear_aux(child) 143 | 144 | def num_aux(node): 145 | if node.number is None: 146 | n = int(node.final) 147 | for child in node.children.values(): 148 | n += num_aux(child) 149 | 150 | node.number = n 151 | 152 | return node.number 153 | 154 | if not self._numbers_valid: 155 | clear_aux(self.q0) 156 | num_aux(self.q0) 157 | self._numbers_valid = True 158 | 159 | def word2index(self, word): 160 | self._num_nodes() 161 | 162 | state = self.q0 163 | index = 0 164 | for c in word: 165 | try: 166 | next = state.children[c] 167 | except KeyError: 168 | return None 169 | 170 | for C in sorted(state.children): 171 | if C < c: 172 | index += state.children[C].number 173 | else: 174 | break 175 | 176 | state = next 177 | if state.final: 178 | index = index + 1 179 | 180 | return index 181 | 182 | def index2word(self, index): 183 | self._num_nodes() 184 | 185 | state = self.q0 186 | count = index 187 | output_word = "" 188 | while True: 189 | for c in sorted(state.children): 190 | tmp = state.get_next(c) 191 | if tmp.number < count: 192 | count -= tmp.number 193 | else: 194 | output_word += c 195 | state = tmp 196 | if state.final: 197 | count -= 1 198 | 199 | break 200 | if count <= 0: 201 | break 202 | 203 | return output_word 204 | 205 | def words(self): 206 | L = [] 207 | 208 | def aux(node, word): 209 | if node.final: 210 | L.append(word) 211 | 212 | for letter, child in node.children.items(): 213 | aux(child, word + letter) 214 | 215 | aux(self.q0, '') 216 | return L 217 | 218 | def __iter__(self): 219 | return iter(self.words()) 220 | -------------------------------------------------------------------------------- /settings.praat: -------------------------------------------------------------------------------- 1 | include procs.praat 2 | 3 | pythonexecutablename$ = if windows then "python.exe" else "python" fi 4 | hcopyexecutablename$ = if windows then "HCopy.exe" else "HCopy" fi 5 | hviteexecutablename$ = if windows then "HVite.exe" else "HVite" fi 6 | soxexecutablename$ = if windows then "sox.exe" else "sox" fi 7 | # If the settings is already present extract all the entries 8 | if fileReadable("settings") 9 | settingsData$ = readFile$("settings") 10 | dictionary$ = extractLine$(settingsData$, "DCT: ") 11 | if dictionary$ = "None" 12 | dictionary$ = "" 13 | endif 14 | ruleset$ = extractLine$(settingsData$, "RUL: ") 15 | if ruleset$ = "None" 16 | ruleset$ = "" 17 | endif 18 | new$ = extractLine$(settingsData$, "NEW: ") 19 | ort$ = extractLine$(settingsData$, "ORT: ") 20 | wrd$ = extractLine$(settingsData$, "WRD: ") 21 | can$ = extractLine$(settingsData$, "CAN: ") 22 | llh$ = extractLine$(settingsData$, "LLH: ") 23 | phonetizer$ = extractLine$(settingsData$, "PHO: ") 24 | if phonetizer$ = "None" 25 | phonetizer$ = "" 26 | endif 27 | pho = 0 28 | lan$ = extractLine$(settingsData$, "LAN: ") 29 | if lan$ = "spanish" 30 | lan = 1 31 | elif lan$ = "tzeltal" 32 | lan = 2 33 | elif lan$ = "universal" 34 | lan = 3 35 | elif lan$ = "none" 36 | lan = 4 37 | else 38 | lan = 1 39 | endif 40 | model$ = extractLine$(settingsData$, "MOD: ") 41 | if model$ = "spanish" 42 | model = 1 43 | elif model$ = "dutch" 44 | model = 2 45 | else 46 | model = 1 47 | endif 48 | pho = 0 49 | log$ = extractLine$(settingsData$, "LOG: ") 50 | soxex$ = extractLine$(settingsData$, "SOX: ") 51 | hviteex$ = extractLine$(settingsData$, "HVB: ") 52 | hcopyex$ = extractLine$(settingsData$, "HCB: ") 53 | pythonex$ = extractLine$(settingsData$, "PY2: ") 54 | thr = extractNumber(settingsData$, "THR: ") 55 | # If the settings isn't present initialize everything 56 | else 57 | dictionary$ = "" 58 | ruleset$ = "" 59 | new$ = "phon" 60 | ort$ = "" 61 | wrd$ = "" 62 | can$ = "" 63 | llh$ = "" 64 | lan = 1 65 | phonetizer$ = "" 66 | pho = 0 67 | model = 1 68 | if windows 69 | log$ = "nul" 70 | else 71 | log$ = "/dev/null" 72 | endif 73 | soxex$ = soxexecutablename$ 74 | hviteex$ = hviteexecutablename$ 75 | hcopyex$ = hcopyexecutablename$ 76 | pythonex$ = pythonexecutablename$ 77 | thr = 0 78 | endif 79 | 80 | # Spawn the option window for the user 81 | beginPause: "Basic options" 82 | comment: "Praatalign version 2.0a" 83 | comment: "Name for the output tier(may already exist)" 84 | sentence: "new", new$ 85 | 86 | comment: "Name for the output tier used for orthographic word level alignment" 87 | sentence: "ort", ort$ 88 | 89 | comment: "Name for the output tier used for word level alignment" 90 | sentence: "wrd", wrd$ 91 | 92 | comment: "Name for the output tier used for canonical pronunciation" 93 | sentence: "can", can$ 94 | 95 | comment: "Name for the output tier used for log likelyhood values" 96 | sentence: "llh", llh$ 97 | 98 | comment: "Select model" 99 | optionMenu: "model", model 100 | option: "spanish" 101 | option: "dutch" 102 | 103 | comment: "Select phonetizer" 104 | optionMenu: "lan", lan 105 | option: "spanish" 106 | option: "tzeltal" 107 | option: "universal" 108 | option: "none" 109 | 110 | comment: "Select a dictionary when pressing apply" 111 | boolean: "dic", 0 112 | if dictionary$ <> "" 113 | sentence: "dictionary", dictionary$ 114 | endif 115 | 116 | comment: "Select a ruleset file when pressing apply" 117 | boolean: "rul", 0 118 | if ruleset$ <> "" 119 | sentence: "ruleset", ruleset$ 120 | endif 121 | 122 | if phonetizer$ <> "" 123 | comment: "Select a phonetizer file when pressing apply" 124 | comment: "This always happens if you use the universal phonetizer" 125 | boolean: "pho", 0 126 | sentence: "phonetizer", phonetizer$ 127 | endif 128 | 129 | comment: "Set the length of the added length to the annotations" 130 | real: "thr", thr 131 | endPause: "Apply", 1 132 | 133 | beginPause: "Advanced options" 134 | comment: "Developer/debug options" 135 | sentence: "log", log$ 136 | 137 | comment: "Select sox executable location when pressing apply" 138 | boolean: "sox", 0 139 | if soxex$ <> soxexecutablename$ 140 | sentence: "soxex", soxex$ 141 | endif 142 | 143 | comment: "Select HVite executable location when pressing apply" 144 | boolean: "hvite", 0 145 | if hviteex$ <> hviteexecutablename$ 146 | sentence: "hviteex", hviteex$ 147 | endif 148 | 149 | comment: "Select HCopy executable location when pressing apply" 150 | boolean: "hcopy", 0 151 | if hcopyex$ <> hcopyexecutablename$ 152 | sentence: "hcopyex", hcopyex$ 153 | endif 154 | 155 | comment: "Select python executable location when pressing apply" 156 | boolean: "python", 0 157 | if pythonex$ <> pythonexecutablename$ 158 | sentence: "pythonex", pythonex$ 159 | endif 160 | endPause: "Apply", 1 161 | 162 | if (phonetizer$ == "" and lan$ == "universal") or pho 163 | beginPause: "Instructions" 164 | comment: "Please point me to the universal phonetizer file." 165 | comment: "The format for such file can be found in the manual" 166 | clicked = endPause: "Cancel", "Continue", 2, 1 167 | if clicked = 2 168 | phonetizer$ = chooseReadFile$("Point me to the phonetizer file") 169 | endif 170 | if phonetizer$ = "" 171 | pause The plugin will probably malfunction since you didn't select a file. 172 | endif 173 | endif 174 | if phonetizer$ = "" 175 | phonetizer$ = "None" 176 | endif 177 | 178 | # Ask for the dictionary 179 | if dic 180 | beginPause: "Instructions" 181 | comment: "Please point me to the dictionary file." 182 | comment: "The format for such file can be found in the manual" 183 | comment: "Dictionary skeletons can be generated with Generate dictionary from tier..." 184 | clicked = endPause: "Cancel", "Continue", 2, 1 185 | if clicked = 2 186 | dictionary$ = chooseReadFile$("Point me to the dictionary file") 187 | endif 188 | endif 189 | if dictionary$ = "" 190 | dictionary$ = "None" 191 | endif 192 | 193 | # Ask for the ruleset 194 | if rul 195 | beginPause: "Instructions" 196 | comment: "Please point me to the ruleset file." 197 | comment: "The format for such file can be found in the manual" 198 | clicked = endPause: "Cancel", "Continue", 2, 1 199 | if clicked = 2 200 | ruleset$ = chooseReadFile$("Point me to the ruleset file") 201 | endif 202 | endif 203 | if ruleset$ = "" 204 | ruleset$ = "None" 205 | endif 206 | 207 | pathstring$ = if windows then "%PATH%" else "$PATH" fi 208 | messagesox$ = if windows then "C:\Program Files" else "/usr/bin" fi 209 | messagepy$ = if windows then "C:\Python27\" else "/usr/bin" fi 210 | messagehtk$ = if windows then 211 | ... "where you unzipped the file pointed out in README.html" else 212 | ... "where you downloaded or compiled the executables" fi 213 | 214 | @getBinary: messagesox$, pathstring$, soxexecutablename$, sox 215 | soxex$ = if getBinary.ex$ <> "" then getBinary.ex$ else soxex$ fi 216 | 217 | @getBinary: messagehtk$, pathstring$, hviteexecutablename$, hvite 218 | hviteex$ = if getBinary.ex$ <> "" then getBinary.ex$ else hviteex$ fi 219 | 220 | @getBinary: messagehtk$, pathstring$, hcopyexecutablename$, hcopy 221 | hcopyex$ = if getBinary.ex$ <> "" then getBinary.ex$ else hcopyex$ fi 222 | 223 | @getBinary: messagepy$, pathstring$, pythonexecutablename$, python 224 | pythonex$ = if getBinary.ex$ <> "" then getBinary.ex$ else pythonex$ fi 225 | 226 | # Delete the original settings file and write the new one 227 | deleteFile("settings") 228 | writeFileLine("settings", 229 | ..."DCT: ", dictionary$, newline$, 230 | ..."HCB: ", hcopyex$, newline$, 231 | ..."HVB: ", hviteex$, newline$, 232 | ..."LAN: ", lan$, newline$, 233 | ..."MOD: ", model$, newline$, 234 | ..."PHO: ", phonetizer$, newline$, 235 | ..."LOG: ", log$, newline$, 236 | ..."LLH: ", llh$, newline$, 237 | ..."NEW: ", new$, newline$, 238 | ..."CAN: ", can$, newline$, 239 | ..."ORT: ", ort$, newline$, 240 | ..."OUT: ", "praat_temp_out", newline$, 241 | ..."RUL: ", ruleset$, newline$, 242 | ..."SOX: ", soxex$, newline$, 243 | ..."THR: ", thr, newline$, 244 | ..."PY2: ", pythonex$, newline$, 245 | ..."WRD: ", wrd$) 246 | -------------------------------------------------------------------------------- /settings_ni.praat: -------------------------------------------------------------------------------- 1 | # The form for non interactive setup 2 | form Set the variables 3 | sentence new align 4 | sentence ort align_o 5 | sentence wrd align_w 6 | sentence can align_c 7 | sentence llh align_l 8 | sentence lan 9 | sentence model 10 | sentence pho 11 | sentence dic 12 | sentence rul 13 | real thr 14 | sentence log /dev/null 15 | sentence sox sox 16 | sentence hvb hvb 17 | sentence hcb hcb 18 | sentence py py 19 | endform 20 | 21 | # Write the settings file 22 | writeFileLine("settings", 23 | ..."DCT: ", dic$, newline$, 24 | ..."HVB: ", hvb$, newline$, 25 | ..."HCB: ", hcb$, newline$, 26 | ..."LAN: ", lan$, newline$, 27 | ..."MOD: ", model$, newline$, 28 | ..."PHO: ", pho$, newline$, 29 | ..."LOG: ", log$, newline$, 30 | ..."LLH: ", llh$, newline$, 31 | ..."NEW: ", new$, newline$, 32 | ..."CAN: ", can$, newline$, 33 | ..."ORT: ", ort$, newline$, 34 | ..."OUT: ", "praat_temp_out", newline$, 35 | ..."RUL: ", rul$, newline$, 36 | ..."SOX: ", sox$, newline$, 37 | ..."THR: ", thr, newline$, 38 | ..."PY2: ", py$, newline$, 39 | ..."WRD: ", wrd$) 40 | -------------------------------------------------------------------------------- /setup.praat: -------------------------------------------------------------------------------- 1 | # Set the write settings to UTF-8 and read settings too 2 | Text writing preferences: "UTF-8" 3 | Text reading preferences: "try UTF-8, then ISO Latin-1" 4 | 5 | # Add the menu commands within the TextGrid editor 6 | Add menu command... "TextGridEditor" "Interval" "Generate dictionary from tier..." "" 0 generatedict.praat 7 | Add menu command... "TextGridEditor" "Interval" "Clean selection" "" 0 cleaninterval.praat 8 | Add menu command... "TextGridEditor" "Interval" "Align current interval" "" 0 alignannotation.praat 9 | Add menu command... "TextGridEditor" "Interval" "Align current tier" "" 0 aligntier.praat 10 | Add menu command... "TextGridEditor" "Interval" "Set up forced alignment..." "" 0 settings.praat 11 | --------------------------------------------------------------------------------