def parseFile(path, fn): filename= fn.split(".")[0] #just name of file with open(path, "r") as f1: lines = f1.readlines() SAM = getSAM(lines) allSegs = getMAU(lines, SAM, filename) if allSegs is None: return segs = [] for seg in allSegs: #print("%f %f %s %s"%(seg.start, seg.end, seg.segment, seg.index)) tup = getSegInfo(seg) segs.append(tup) words = getWords(lines, allSegs, filename) maxtime = getMaxTime(allSegs) if maxtime == -1: return tg = TextGrid(maxTime = maxtime) wordtier = IntervalTier(name = 'words', maxTime = maxtime) phonetier = IntervalTier(name = 'phones', maxTime = maxtime) for interval in words: wordtier.add(*interval) for interval in segs: phonetier.add(*interval) tg.append(wordtier) tg.append(phonetier) outpath = "/Users/elias/Desktop/TextGrids/%s.TextGrid"%filename tg.write(outpath)
def from_ref_and_target(cls, ref_tg: BaseTextGridDocument, target_tg: BaseTextGridDocument): """Simply interweaves the ref and target into one tg for annotations merging""" # checking first if both TG have the same amount of tiers, and the same names ref_names = sorted(ref_tg.textgrid.getNames()) target_names = sorted(target_tg.textgrid.getNames()) if len(ref_names) != len(target_names): error_log.log_structural( "The reference and target annotator's textgrids don't have the same amount of Tiers " "(%i for the reference, %i for the target)" % (len(ref_names), len(target_names))) return if ref_names != target_names: # TODO: maybe make this more helpful error_log.log_structural("The names of some of the tiers in the reference and target textgrids don't match") return # TODO : add support for empty tiers deletion assert ref_tg.task == target_tg.task merged_tg = TextGrid(name=ref_tg.textgrid.name, minTime=ref_tg.textgrid.minTime, maxTime=ref_tg.textgrid.maxTime) for tier_name in ref_tg.textgrid.getNames(): ref_tier: IntervalTier = deepcopy(ref_tg.textgrid.getFirst(tier_name)) target_tier: IntervalTier = deepcopy(target_tg.textgrid.getFirst(tier_name)) ref_tier.name = tier_name + "-ref" target_tier.name = tier_name + "-target" merged_tg.append(ref_tier) merged_tg.append(target_tier) new_doc = cls.from_textgrid(merged_tg, ref_tg.creators + target_tg.creators, ref_tg.task) return new_doc
def ctm_to_textgrid(phone_ctm, out_directory, utt2dur, frameshift=0.01): textgrid_write_errors = {} frameshift = Decimal(str(frameshift)) if not os.path.exists(out_directory): os.makedirs(out_directory) utt2dur_mapping = generate_utt2dur(utt2dur) for i, (k, v) in enumerate(sorted(phone_ctm.items())): maxtime = Decimal(str(utt2dur_mapping[k])) try: tg = TextGrid(maxTime=maxtime) phonetier = IntervalTier(name='phones', maxTime=maxtime) for interval in v: if maxtime - interval[1] < frameshift: interval[1] = maxtime #remove B E I and stress (0,1) information from phoneme interval[2] = re.sub("\d+", "", interval[2].split('_')[0]) phonetier.add(*interval) tg.append(phonetier) outpath = os.path.join(out_directory, k + '.TextGrid') tg.write(outpath) except Exception as e: exc_type, exc_value, exc_traceback = sys.exc_info() textgrid_write_errors[k] = '\n'.join( traceback.format_exception(exc_type, exc_value, exc_traceback)) if textgrid_write_errors: error_log = os.path.join(out_directory, 'output_errors.txt') with io_open(error_log, 'w', encoding='utf-8') as f: f.write( u'The following exceptions were encountered during the ouput of the alignments to TextGrids:\n\n' ) for k, v in textgrid_write_errors.items(): f.write(u'{}:\n'.format(k)) f.write(u'{}\n\n'.format(v))
def export_segments(self, output_directory): from decimal import Decimal from textgrid import TextGrid, IntervalTier file_dict = {} for utt, segment in self.corpus.vad_segments.items(): filename, utt_begin, utt_end = segment utt_begin = Decimal(utt_begin) utt_end = Decimal(utt_end) if filename not in file_dict: file_dict[filename] = {} speaker = 'segments' text = 'speech' if speaker not in file_dict[filename]: file_dict[filename][speaker] = [] file_dict[filename][speaker].append([utt_begin, utt_end, text]) for filename, speaker_dict in file_dict.items(): try: speaker_directory = os.path.join( output_directory, self.corpus.file_directory_mapping[filename]) except KeyError: speaker_directory = output_directory os.makedirs(speaker_directory, exist_ok=True) max_time = self.corpus.get_wav_duration(filename) tg = TextGrid(maxTime=max_time) for speaker in sorted(speaker_dict.keys()): words = speaker_dict[speaker] tier = IntervalTier(name=speaker, maxTime=max_time) for w in words: if w[1] > max_time: w[1] = max_time tier.add(*w) tg.append(tier) tg.write(os.path.join(speaker_directory, filename + '.TextGrid'))
def check_times_merging(self): """Checks that paired tiers can be merged together. Outputs the partially merged textgrid as well as the merge conflicts.""" from .tasks.double import MergeResults merged_times_tg = TextGrid( name=self.textgrid.name, maxTime=self.textgrid.maxTime, minTime=self.textgrid.minTime) merge_results = MergeResults() for tier in self.checking_scheme.all_tiers_names: merged_tier_name = tier + self.TOP_GROUP_SUFFIX target_tier_name = tier + self.BOTTOM_GROUP_SUFFIX merged_tier = self.textgrid.getFirst(merged_tier_name) target_tier = self.textgrid.getFirst(target_tier_name) # in case either tier is not present, we just skip this merge if merged_tier is None or target_tier is None: continue times_merged_tier, tier_merge = self.merge_tiers(merged_tier, target_tier) merge_results.tiers_merges.append(tier_merge) times_merged_tier.name = tier merged_times_tg.append(times_merged_tier) # logging conflicts as errors that could be displayed to the # annotator (in case of merge attempt) for conflict in merge_results.to_merge_conflicts_errors(): error_log.log_merge(conflict) return merged_times_tg, merge_results
def convert_ctm_to_textgrid(ctm, textgrid): words = [] phonemes = [] with open(ctm, encoding='utf-8') as f: for l in f: tok = l.strip().split() text = tok[4] beg = float(tok[2]) dur = float(tok[3]) if tok[0][0] == '@': if besi.match(text): text = text[:-2] phonemes.append((text, beg, dur)) else: words.append((text, beg, dur)) tw = IntervalTier(name='words') tp = IntervalTier(name='phonemes') for seg in words: try: tw.add(round(seg[1], 2), round(seg[1] + seg[2], 2), seg[0]) except ValueError: print("Error in word seg: " + seg[0]) for seg in phonemes: try: tp.add(round(seg[1], 2), round(seg[1] + seg[2], 2), seg[0]) except ValueError: print("Error in phoneme seg: " + seg[0]) tg = TextGrid() tg.append(tw) tg.append(tp) tg.write(textgrid)
def reorg_noncollapsed(f): padding = 0.1 print(f) tg_path = os.path.join(noncollapsed_dir, f) tg = TextGrid() tg.read(tg_path) new_tg = TextGrid(maxTime=tg.maxTime) new_tg_path = tg_path.replace(noncollapsed_dir, data_dir) for tier in tg.tiers: new_tier = IntervalTier(name=tier.name, maxTime=tg.maxTime) for i in tier: new_mark = sub_pattern.sub(' ', i.mark).strip() if not new_mark: continue new_begin = i.minTime - padding if new_begin < 0: new_begin = 0 new_end = i.maxTime + padding if new_end > tg.maxTime: new_end = tg.maxTime try: new_tier.add(new_begin, new_end, new_mark) except ValueError: new_tier[-1].maxTime = new_end new_tier[-1].mark += ' ' + new_mark print(len(new_tier)) new_tg.append(new_tier) new_tg.write(new_tg_path)
def gen_template_tg(self, duration: float, filename: str): new_tg = TextGrid(name=filename, minTime=0.0, maxTime=duration) for tier_name in self.tiers_specs.keys(): new_tier = IntervalTier(name=tier_name, minTime=0.0, maxTime=duration) new_tg.append(new_tier) return new_tg
def createTextGrid(data, tierName = "words"): tier = IntervalTier(tierName) txtgrid = TextGrid() prevTime = 0 for (name, time, dur, words) in data: tier.add(prevTime, prevTime+dur, makeSentence(words)) prevTime += dur txtgrid.append(tier) return txtgrid
def read_tg_from_str(tg_str, round_digits=DEFAULT_TEXTGRID_PRECISION): """ Read the tiers contained in the Praat-formatted string tg_str into a TextGrid object. Times are rounded to the specified precision. Adapted from TextGrid.read() """ source = StringIO(tg_str) tg = TextGrid() file_type, short = parse_header(source) if file_type != "TextGrid": raise ValueError("The file could not be parsed as a TextGrid as it is " "lacking a proper header.") tg.minTime = parse_line(source.readline(), short, round_digits) tg.maxTime = parse_line(source.readline(), short, round_digits) source.readline() # More header junk if short: m = int(source.readline().strip()) # Will be tg.n else: m = int(source.readline().strip().split()[2]) # Will be tg.n if not short: source.readline() for i in range(m): # Loop over grids if not short: source.readline() if parse_line(source.readline(), short, round_digits) == "IntervalTier": inam = parse_line(source.readline(), short, round_digits) imin = parse_line(source.readline(), short, round_digits) imax = parse_line(source.readline(), short, round_digits) itie = IntervalTier(inam, imin, imax) itie.strict = tg.strict n = int(parse_line(source.readline(), short, round_digits)) for j in range(n): if not short: source.readline().rstrip().split() # Header junk jmin = parse_line(source.readline(), short, round_digits) jmax = parse_line(source.readline(), short, round_digits) jmrk = get_mark(source, short) if jmin < jmax: # Non-null itie.addInterval(Interval(jmin, jmax, jmrk)) tg.append(itie) else: # PointTier inam = parse_line(source.readline(), short, round_digits) imin = parse_line(source.readline(), short, round_digits) imax = parse_line(source.readline(), short, round_digits) itie = PointTier(inam) n = int(parse_line(source.readline(), short, round_digits)) for j in range(n): source.readline().rstrip() # Header junk jtim = parse_line(source.readline(), short, round_digits) jmrk = get_mark(source, short) itie.addPoint(Point(jtim, jmrk)) tg.append(itie) return tg
def ctm_to_textgrid(word_ctm, phone_ctm, out_directory, corpus, dictionary, frameshift=0.01): textgrid_write_errors = {} frameshift = Decimal(str(frameshift)) if not os.path.exists(out_directory): os.makedirs(out_directory, exist_ok=True) silences = {dictionary.optional_silence, dictionary.nonoptional_silence} for i, (filename, speaker_dict) in enumerate(sorted(word_ctm.items())): maxtime = corpus.get_wav_duration(filename) try: speaker_directory = os.path.join( out_directory, corpus.file_directory_mapping[filename]) tg = TextGrid(maxTime=maxtime) for speaker in corpus.speaker_ordering[filename]: words = speaker_dict[speaker] word_tier_name = '{} - words'.format(speaker) phone_tier_name = '{} - phones'.format(speaker) word_tier = IntervalTier(name=word_tier_name, maxTime=maxtime) phone_tier = IntervalTier(name=phone_tier_name, maxTime=maxtime) for w in words: word_tier.add(*w) for p in phone_ctm[filename][speaker]: if len(phone_tier) > 0 and phone_tier[ -1].mark in silences and p[2] in silences: phone_tier[-1].maxTime = p[1] else: if len(phone_tier) > 0 and p[2] in silences and p[ 0] < phone_tier[-1].maxTime: p = phone_tier[-1].maxTime, p[1], p[2] elif len(phone_tier) > 0 and p[2] not in silences and p[0] < phone_tier[-1].maxTime and \ phone_tier[-1].mark in silences: phone_tier[-1].maxTime = p[0] phone_tier.add(*p) tg.append(word_tier) tg.append(phone_tier) tg.write(os.path.join(speaker_directory, filename + '.TextGrid')) except Exception as e: exc_type, exc_value, exc_traceback = sys.exc_info() textgrid_write_errors[filename] = '\n'.join( traceback.format_exception(exc_type, exc_value, exc_traceback)) if textgrid_write_errors: error_log = os.path.join(out_directory, 'output_errors.txt') with open(error_log, 'w', encoding='utf8') as f: f.write( 'The following exceptions were encountered during the ouput of the alignments to TextGrids:\n\n' ) for k, v in textgrid_write_errors.items(): f.write('{}:\n'.format(k)) f.write('{}\n\n'.format(v))
def export_classification(self, output_directory): if self.cluster: self.cluster_utterances() else: self.get_classification_stats() from decimal import Decimal from textgrid import TextGrid, IntervalTier spk2utt_path = os.path.join(self.classify_directory, 'spk2utt') utt2spk_path = os.path.join(self.classify_directory, 'utt2spk') if self.corpus.segments: utt2spk = load_scp(utt2spk_path) file_dict = {} for utt, segment in self.corpus.segments.items(): filename, utt_begin, utt_end = segment.split(' ') utt_begin = Decimal(utt_begin) utt_end = Decimal(utt_end) if filename not in file_dict: file_dict[filename] = {} speaker = utt2spk[utt] text = self.corpus.text_mapping[utt] if speaker not in file_dict[filename]: file_dict[filename][speaker] = [] file_dict[filename][speaker].append([utt_begin, utt_end, text]) for filename, speaker_dict in file_dict.items(): try: speaker_directory = os.path.join( output_directory, self.corpus.file_directory_mapping[filename]) except KeyError: speaker_directory = output_directory max_time = self.corpus.get_wav_duration(filename) tg = TextGrid(maxTime=max_time) for speaker in sorted(speaker_dict.keys()): words = speaker_dict[speaker] tier = IntervalTier(name=speaker, maxTime=max_time) for w in words: if w[1] > max_time: w[1] = max_time tier.add(*w) tg.append(tier) tg.write( os.path.join(speaker_directory, filename + '.TextGrid')) else: spk2utt = load_scp(spk2utt_path) for speaker, utts in spk2utt.items(): speaker_dir = os.path.join(output_directory, speaker) os.makedirs(speaker_dir, exist_ok=True) with open(os.path.join(speaker_dir, 'utterances.txt'), 'w', encoding='utf8') as f: for u in utts: f.write('{}\n'.format(u))
def create_grid(wav_file: Path, text: str, tier_name: str, n_digits: int) -> TextGrid: assert wav_file.is_file() assert len(text) > 0 duration_s = get_wav_duration_s(wav_file) duration_s = round(duration_s, n_digits) result = TextGrid(None, 0, duration_s) tier = IntervalTier(tier_name, 0, duration_s) symbols = list(text) tier.intervals.extend(get_intervals(symbols, duration_s, n_digits)) result.append(tier) return result
def fixTiers(source, tierlist, outfile): """Takes list of TextGrids, file with new tier orders, list of output file names, returns TextGrids with new tier order""" for line, tier, out in zip(source.readlines(), tierlist, outfile.readlines()): f = line.rstrip('\n') oldtg = TextGridFromFile(f) list_from_file = eval(tier) output = out.rstrip('\n') newtg = TextGrid('newtg') for n in list_from_file: ntier = oldtg.getFirst(n) newtg.append(ntier) newtg.write(output)
def gen_merged_times(self): """Merges times""" merged_times_tg, merge_results = self.check_times_merging() new_tg = TextGrid(name=merged_times_tg.name, maxTime=merged_times_tg.maxTime, minTime=merged_times_tg.minTime) for tier_name in self.checking_scheme.all_tiers_names: merged_tier: IntervalTier = deepcopy(merged_times_tg.getFirst(tier_name)) target_tier: IntervalTier = deepcopy(self.textgrid.getFirst(tier_name + "-target")) merged_tier.name = tier_name + "-merged" new_tg.append(merged_tier) new_tg.append(target_tier) return new_tg, merge_results
def generator_textgrid(maxtime, lines, output): # Download Praat: https://www.fon.hum.uva.nl/praat/ interval = maxtime / (len(lines) + 1) margin = 0.0001 tg = TextGrid(maxTime=maxtime) linetier = IntervalTier(name="line", maxTime=maxtime) i = 0 for l in lines: s, e, w = l.split() linetier.add(minTime=float(s) + margin, maxTime=float(e), mark=w) tg.append(linetier) print("successfully generator {}".format(output)) tg.write(output)
def createNew(textgrid, tier_name, VERBOSE=False): tiers = textgrid.getList(tier_name) tier = tiers[0] new_tier = IntervalTier(tier_name+'_clean') new_txtgrid = TextGrid() if VERBOSE == True: print ("Old tier: %s" % tier) for interval in tier: if isPause(interval.mark) == True: new_tier.add(interval.minTime, interval.maxTime, '') else: new_tier.add(interval.minTime, interval.maxTime, fixString(interval.mark)) new_txtgrid.append(new_tier) if VERBOSE == True: print ("New tier: %s" % new_tier) return new_txtgrid
def export_transcriptions(self, output_directory, source=None): transcripts = self._load_transcripts(source) print(self.corpus.file_directory_mapping) if not self.corpus.segments: for utt, t in transcripts.items(): relative = self.corpus.file_directory_mapping[utt] if relative: speaker_directory = os.path.join(output_directory, relative) else: speaker_directory = output_directory os.makedirs(speaker_directory, exist_ok=True) outpath = os.path.join(speaker_directory, utt + '.lab') with open(outpath, 'w', encoding='utf8') as f: f.write(t) else: for filename in self.corpus.speaker_ordering.keys(): maxtime = self.corpus.get_wav_duration(filename) try: speaker_directory = os.path.join( output_directory, self.corpus.file_directory_mapping[filename]) except KeyError: speaker_directory = output_directory tiers = {} for speaker in self.corpus.speaker_ordering[filename]: tiers[speaker] = IntervalTier(name=speaker, maxTime=maxtime) tg = TextGrid(maxTime=maxtime) for utt_name, text in transcripts.items(): utt_filename, begin, end = self.corpus.segments[ utt_name].split(' ') if utt_filename != filename: continue speaker = self.corpus.utt_speak_mapping[utt_name] begin = float(begin) end = float(end) tiers[speaker].add(begin, end, text) for t in tiers.values(): tg.append(t) tg.write( os.path.join(speaker_directory, filename + '.TextGrid'))
def convert_ctm_to_textgrid(ctms, textgrid): for ctm in ctms: tiername = ctm.stem ret = [] with open(ctm, encoding='utf-8') as f: for l in f: tok = l.strip().split() word = tok[4] beg = float(tok[2]) dur = float(tok[3]) ret.append((word, beg, dur)) t = IntervalTier(name=tiername) for seg in ret: try: t.add(round(seg[1], 2), round(seg[1] + seg[2], 2), seg[0]) except ValueError: print("Error in seg: " + seg[0]) tg = TextGrid() tg.append(t) tg.write(textgrid)
def reorg_original(f): print(f) tg_path = os.path.join(noncollapsed_dir, f) tg = TextGrid() tg.read(tg_path) new_tg = TextGrid(maxTime=tg.maxTime) new_tg_path = tg_path.replace('_original.TextGrid', '.TextGrid') sentence_tier = tg.getFirst('Sentences') speaker_tier = tg.getFirst('Speakers') speaker_tiers = {} for i in speaker_tier: if i.mark == '': continue if ',' in i.mark: continue if i.mark == 'Tân.': continue speaker_tiers[i.mark] = IntervalTier(i.mark, maxTime=tg.maxTime) for i in sentence_tier: if not i.mark.strip(): continue duration = i.maxTime - i.minTime mid_point = i.minTime + duration / 2 speaker_int = speaker_tier.intervalContaining(mid_point) speaker = speaker_int.mark if speaker == 'Tân.': speaker = 'Tan' if speaker == '': continue if len(speaker_tiers[speaker] ) > 0 and speaker_tiers[speaker][-1].maxTime == i.minTime: speaker_tiers[speaker][-1].maxTime = i.maxTime speaker_tiers[speaker][ -1].mark = speaker_tiers[speaker][-1].mark + ' ' + i.mark else: speaker_tiers[speaker].addInterval(i) for k, v in sorted(speaker_tiers.items()): new_tg.append(v) print(speaker_tiers.keys()) new_tg.write(new_tg_path)
'sp', '{OOV}' ] and w_tier[-1].maxTime > w.minTime: w.minTime = w_tier[-1].maxTime #print(w) if w.maxTime > duration: w.maxTime = duration w_tier.addInterval(w) for p in sorted(speaker_phone_tiers[s]): if len(p_tier) and p_tier[ -1].mark == 'sil' and p_tier[-1].maxTime > p.minTime: p_tier[-1].maxTime = p.minTime if len(p_tier) and p.mark == 'sil' and p_tier[ -1].maxTime > p.minTime: p.minTime = p_tier[-1].maxTime #print(p) if p.maxTime > duration: p.maxTime = duration try: p_tier.addInterval(p) except ValueError: pass new_tg.append(w_tier) new_tg.append(p_tier) new_tg.write(out_path) except Exception as e: print(out_path) print(e) # print(tg)
label = ss[4] result.append([begin, end, label]) return result if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('words_ctm') parser.add_argument('phones_ctm') parser.add_argument('output_textgrid') args = parser.parse_args() words = read_ctm(args.words_ctm) phones = read_ctm(args.phones_ctm) max_time = phones[-1][1] tg = TextGrid(maxTime=max_time) word_tier = IntervalTier(name="words", maxTime=max_time) phone_tier = IntervalTier(name="phones", maxTime=max_time) for w in words: word_tier.add(*w) for p in phones: phone_tier.add(*p) tg.append(word_tier) tg.append(phone_tier) tg.write(args.output_textgrid)
class TextGrid(Module): ''' Manages all the widgets related to TextGrid files, including the tier name and the text content of that tier at a given frame ''' def __init__(self, app): ''' Keep a reference to the master object for binding the widgets we create ''' info(' - initializing module: TextGrid') self.app = app self.frame = Frame(self.app.BOTTOM) self.label_padx = 0 self.canvas_frame = Frame(self.app.BOTTOM) #, padx=self.label_padx) self.frame.grid(row=1, column=0, sticky='ne') self.canvas_frame.grid(row=1, column=1) self.TextGrid = None self.selectedTier = StringVar() self.tg_zoom_factor = 1.5 self.canvas_width = 800 self.canvas_height = 60 self.collapse_height = 15 self.selectedIntvlFrames = [] self.selectedItem = None self.start = 0 self.end = 0 self.current = 0 self.frame_shift = DoubleVar() self.startup() platform = util.get_platform() #bindings if platform == 'Linux': self.app.bind("<Control-n>", self.getBounds) self.app.bind("<Control-a>", self.getBounds) self.app.bind("<Control-i>", self.getBounds) self.app.bind("<Control-o>", self.getBounds) self.app.bind("<Control-f>", self.openSearch) # Command is Alt in Linux, apparently self.app.bind("<Command-Up>", self.changeTiers) self.app.bind("<Command-Down>", self.changeTiers) self.app.bind("<Command-Left>", self.changeIntervals) self.app.bind("<Command-Right>", self.changeIntervals) elif platform == 'Darwin': self.app.bind("<Command-n>", self.getBounds) self.app.bind("<Command-a>", self.getBounds) self.app.bind("<Command-i>", self.getBounds) self.app.bind("<Command-o>", self.getBounds) self.app.bind("<Command-f>", self.openSearch) self.app.bind("<Option-Up>", self.changeTiers) self.app.bind("<Option-Down>", self.changeTiers) self.app.bind("<Option-Left>", self.changeIntervals) self.app.bind("<Option-Right>", self.changeIntervals) #defaults (Command/Alt everything) else: self.app.bind("<Command-n>", self.getBounds) self.app.bind("<Command-a>", self.getBounds) self.app.bind("<Command-i>", self.getBounds) self.app.bind("<Command-o>", self.getBounds) self.app.bind("<Command-f>", self.openSearch) self.app.bind("<Command-Up>", self.changeTiers) self.app.bind("<Command-Down>", self.changeTiers) self.app.bind("<Command-Left>", self.changeIntervals) self.app.bind("<Command-Right>", self.changeIntervals) # these aren't Praat-like self.app.bind("<Shift-Left>", self.getBounds) self.app.bind("<Shift-Right>", self.getBounds) def setup(self): if LIBS_INSTALLED: self.loadOrGenerate() try: self.start = self.TextGrid.minTime self.end = self.TextGrid.maxTime self.app.frames = len( self.TextGrid.getFirst(self.frameTierName)) tiers = [] for tier in self.TextGrid.getNames(): if tier != self.frameTierName and tier != self.frameTierName + '.original': tiers.append(tier) if set(tiers) != self.tierNames: self.tierNames = set(tiers) self.TkWidgets = [] for label in self.frame.winfo_children(): label.destroy() for canvas in self.canvas_frame.winfo_children(): canvas.destroy() for tier in self.TextGrid.getNames(): if tier != self.frameTierName and tier != self.frameTierName + '.original': self.TkWidgets.append(self.makeTierWidgets(tier)) self.makeFrameWidget() self.makeTimeWidget() self.fillCanvases() self.firstFrame = int( self.TextGrid.getFirst(self.frameTierName)[0].mark) + 1 self.startFrame = self.firstFrame self.lastFrame = int( self.TextGrid.getFirst(self.frameTierName)[-1].mark) + 1 self.endFrame = self.lastFrame self.grid() except Exception as e: error(e) def startup(self): ''' ''' if LIBS_INSTALLED: self.tierNames = set() self.setup() def reset(self, event=None): ''' Try to load a TextGrid file based on information stored in the metadata ''' if LIBS_INSTALLED: self.selectedIntvlFrames = [] self.selectedItem = None self.setup() def fromFile(self, filename): if LIBS_INSTALLED: try: return TextGridFile.fromFile(filename) except (TextGridError, UnicodeDecodeError) as e: f = open(filename, 'rb') contents = util.decode_bytes(f.read()) f.close() if contents: tmp = tempfile.NamedTemporaryFile() tmp.write(contents.encode('utf-8')) tmp.seek(0) try: return TextGridFile.fromFile(tmp.name) except TextGridError as e: error(e) return None else: error( "can't load from file: unable to decode non-Unicode textgrid", filename) else: error("can't load from file: textgrid lib not installed") return None def loadOrGenerate(self): fname = self.app.Data.checkFileLevel('.TextGrid', shoulderror=False) if fname: self.TextGrid = self.fromFile(fname) else: minTime = 0. if not hasattr(self.app.Audio, 'duration'): self.app.Audio.reset() try: maxTime = self.app.Audio.duration except: warn( 'Audio has no duration attribute after calling reset(), defaulting to 1 second' ) maxTime = 1. self.TextGrid = TextGridFile(maxTime=maxTime) keys = self.app.Data.getFileLevel('all') if not ('.ult' in keys and '.txt' in keys): sentenceTier = IntervalTier("text") sentenceTier.add(minTime, maxTime, "text") self.TextGrid.append(sentenceTier) fname = self.app.Data.unrelativize( self.app.Data.getCurrentFilename() + '.TextGrid') self.app.Data.setFileLevel('.TextGrid', fname) names = self.TextGrid.getNames() for i, n in enumerate(names): if n in ALIGNMENT_TIER_NAMES: if len(self.TextGrid[i]) == 0: self.TextGrid.pop(i) break else: self.frameTierName = n return self.genFramesTier() def genFramesTier(self): debug('generating frames tier for %s' % self.app.Data.getCurrentFilename()) self.frameTierName = 'frames' times = self.app.Dicom.getFrameTimes() self.app.Data.setFileLevel("NumberOfFrames", len(times)) try: maxTime = max(self.app.Audio.duration, times[-1]) except AttributeError: maxTime = times[-1] tier = PointTier('frames', maxTime=maxTime) for f, t in enumerate(times): tier.addPoint(Point(t, str(f))) if not self.TextGrid.maxTime or maxTime > self.TextGrid.maxTime: self.TextGrid.maxTime = maxTime self.TextGrid.append(tier) keys = self.app.Data.getFileLevel('all') if '.ult' in keys and '.txt' in keys: fname = self.app.Data.unrelativize( self.app.Data.getFileLevel('.txt')) f = open(fname, 'rb') s = util.decode_bytes(f.read()) f.close() if s: line = s.splitlines()[0] sentenceTier = IntervalTier("sentence") sentenceTier.add(0, self.app.Audio.duration, line) self.TextGrid.append(sentenceTier) self.TextGrid.tiers = [self.TextGrid.tiers[-1] ] + self.TextGrid.tiers[:-1] path = self.app.Data.unrelativize( self.app.Data.getFileLevel('.TextGrid')) self.TextGrid.write(path) self.TextGrid = TextGridFile.fromFile(path) # reload to account for length changes due to frames tier being different length than audio @staticmethod def isIntervalTier(tier): if LIBS_INSTALLED: return isinstance(tier, IntervalTier) else: error("can't check if IntervalTier: textgrid lib not installed") return False def shiftFrames(self): ''' Replicate original TextGrid point tier (renamed [tiername].original) Shift points on TextGrid tier in accordance with self.frame_shift Shift value is relative to 0, i.e. inputting the same shift amount a second time will not change the shift Redisplay shifted points ''' self.app.focus() shift = self.frame_shift.get() if type(shift) == float: self.app.Data.setFileLevel('offset', shift) # diff = shift - self.app.Data.data['offset'] originalTier = self.TextGrid.getFirst(self.frameTierName + '.original') if originalTier: pass else: orig = copy.deepcopy(self.TextGrid.getFirst( self.frameTierName)) orig.name += '.original' self.TextGrid.append(orig) originalTier = self.TextGrid.getFirst(self.frameTierName + '.original') oldTier = self.TextGrid.getFirst(self.frameTierName) allPoints = oldTier[:] for point in allPoints: oldTier.removePoint(point) for point in originalTier: new_time = point.time + shift / 1000 ## NOTE: currently in ms if self.TextGrid.minTime <= new_time <= self.TextGrid.maxTime: self.TextGrid.getFirst(self.frameTierName).add( new_time, point.mark) # self.app.frames = len(self.TextGrid.getFirst(self.frameTierName)) #FIXME I feel like I shouldn't have to run the getFirst function every time, but I'm not sure when I have to go back to the original textgrid, and when I can just use a variable... self.firstFrame = int( self.TextGrid.getFirst(self.frameTierName)[0].mark) + 1 self.lastFrame = int( self.TextGrid.getFirst(self.frameTierName)[-1].mark) + 1 self.app.Data.data['offset'] = shift # self.frame_shift.set(shift) self.app.Data.write() # newTier.write(self.TextGrid.getFirst(self.frameTierName)) self.fillCanvases() self.TextGrid.write( self.app.Data.unrelativize( self.app.Data.getFileLevel('.TextGrid'))) #except ValueError: else: error('Not a float!') def makeTimeWidget(self): self.time_canvas = Canvas(self.canvas_frame, width=self.canvas_width, height=self.canvas_height / 3, highlightthickness=0) s = self.time_canvas.create_text(3, 0, anchor='nw', text=self.start) e = self.time_canvas.create_text(self.canvas_width, 0, anchor='ne', text=self.end) c = self.time_canvas.create_text(self.canvas_width / 2, 0, anchor='n', text=self.current) self.TkWidgets.append({'times': self.time_canvas}) def makeFrameWidget(self): ''' makes frame widget ''' #make regular frame stuff -- label and tier self.frames_canvas = Canvas(self.canvas_frame, width=self.canvas_width, height=self.canvas_height, background='gray', highlightthickness=0) frames_label = Canvas(self.frame, width=self.label_width, height=self.canvas_height, highlightthickness=0, background='gray') frames_label.create_text(self.label_width, 0, anchor='ne', justify='center', text='frames: ', width=self.label_width, activefill='blue') # make subframe to go on top of label canvas sbframe = Frame(frames_label) #put new widgets onto subframe offset = self.app.Data.getFileLevel('offset') if offset != None: self.frame_shift.set(offset) # for audio alignment go_btn = Button(sbframe, text='Offset', command=self.shiftFrames, takefocus=0) # minmax = len(self.app.Audio.sfile)*1000 txtbox = Spinbox(sbframe, textvariable=self.frame_shift, width=7, from_=-10000000, to=10000000) txtbox.bind('<Escape>', lambda ev: sbframe.focus()) txtbox.bind('<Return>', lambda ev: self.shiftFrames()) go_btn.grid(row=0, column=0, sticky='e') txtbox.grid(row=0, column=1, sticky='e') # put subframe on canvas window = frames_label.create_window(self.label_width * .3, self.canvas_height / 3, anchor='nw', window=sbframe) # ensure position of subframe gets updated frames_label.bind( '<Configure>', lambda e: frames_label.itemconfig(window, width=e.width)) sbframe.bind( '<Configure>', lambda e: frames_label.configure( scrollregion=frames_label.bbox("all"))) self.TkWidgets.append({ 'name': self.frameTierName, 'frames': self.frames_canvas, 'frames-label': frames_label }) self.frames_canvas.bind("<Button-1>", self.getClickedFrame) def getFrameTierName(self): ''' Handle some inconsistency in how we're naming our alignment tier ''' for name in ALIGNMENT_TIER_NAMES: if name in self.TextGrid.getNames(): return name raise NameError('Unable to find alignment tier') def getClickedFrame(self, event): ''' Jumps to clicked frame ''' item = self.my_find_closest(event.widget, event.x) self.setSelectedIntvlFrames((event.widget, item)) frame = event.widget.gettags(item)[0][5:] self.app.frame = int(frame) if not frame in self.selectedIntvlFrames: self.selectedIntvlFrames = [] self.wipeFill() self.app.framesUpdate() def makeTierWidgets(self, tier): ''' Each tier should have two canvas widgets: `canvas-label` (the tier name), and `canvas` (the intervals on the tier with their marks) ''' self.tier_pairs = {} #ends up being format {label: canvas} # self.app.Trace.frame.update() self.label_width = 300 #self.app.Trace.frame.winfo_width()+self.label_padx self.end = self.TextGrid.maxTime #float(self.TextGrid.maxTime) # self.first_frame = 1 # self.last_frame = self.TextGrid.getFirst(self.frameTierName)[-1].mark tier_obj = self.TextGrid.getFirst(tier) widgets = { 'name': tier, #'label':Label(self.frame, text=('- '+tier+':'), wraplength=200, justify='left'), 'canvas-label': Canvas(self.frame, width=self.label_width, height=self.canvas_height, highlightthickness=0), # 'text' :Label(self.frame, text='', wraplength=550, justify='left'), 'canvas': Canvas(self.canvas_frame, width=self.canvas_width, height=self.canvas_height, background='gray', highlightthickness=0) } canvas = widgets['canvas'] label = widgets['canvas-label'] #builds tier label functionality label_text = label.create_text(self.label_width, self.canvas_height / 2, anchor='e', justify='center', text='temp', width=self.label_width / 2, activefill='blue') canvas.bind("<Button-1>", self.genFrameList) label.bind("<Button-1>", self.genFrameList) label.bind("<Double-Button-1>", self.collapse) label.bind("<Button-4>", self.collapse) label.bind("<Button-5>", self.collapse) label.bind("<MouseWheel>", self.collapse) canvas.bind("<Button-4>", self.collapse) canvas.bind("<Button-5>", self.collapse) canvas.bind("<MouseWheel>", self.collapse) return widgets def changeIntervals(self, event): ''' ''' if self.selectedItem: duration = self.end - self.start # There might be a more efficient way to get the tier name: widg = self.selectedItem[0] itm = self.selectedItem[1] for el in self.TkWidgets: if widg in el.values(): tier_name = el['name'] break #finding Interval mintime and maxtime oldMinTime = None oldMaxTime = None q = 0 tags = widg.gettags(itm) while oldMinTime == None or oldMaxTime == None: if tags[q][:7] == 'minTime': oldMinTime = float(tags[q][7:]) elif tags[q][:7] == 'maxTime': oldMaxTime = float(tags[q][7:]) q += 1 tier = self.TextGrid.getFirst(tier_name) intvl_i = tier.indexContaining(oldMaxTime - ((oldMaxTime - oldMinTime) / 2)) if event.keysym == 'Left': new_intvl_i = intvl_i - 1 elif event.keysym == 'Right': new_intvl_i = intvl_i + 1 if 0 <= new_intvl_i < len(tier): #find characteristics of new adjacent interval newMinTime = tier[new_intvl_i].minTime newMaxTime = tier[new_intvl_i].maxTime itvlDuration = newMaxTime - newMinTime newCenter = newMinTime + itvlDuration / 2 #figure out new window parameters based on new interval start = newCenter - duration / 2 end = newCenter + duration / 2 if start < 0: self.start = 0 self.end = duration elif end > self.TextGrid.maxTime: self.start = self.TextGrid.maxTime - duration self.end = self.TextGrid.maxTime else: self.start = newCenter - duration / 2 self.end = newCenter + duration / 2 relDuration = self.end - self.start # select new item rel_time = newCenter - self.start x_loc = float(rel_time / relDuration * self.canvas_width) item = self.my_find_closest(widg, x_loc) self.selectedItem = (widg, item) self.setSelectedIntvlFrames(self.selectedItem) self.fillCanvases() self.genFrameList(widg=widg, x_loc=x_loc) def changeTiers(self, event): ''' ''' index = None if self.selectedItem: for i, el in enumerate(self.TkWidgets): if self.selectedItem[0] in el.values(): index = i if index != None: if event.keysym == 'Up' and 'canvas' in self.TkWidgets[index - 1]: new_widg = self.TkWidgets[index - 1]['canvas'] elif event.keysym == 'Down' and 'canvas' in self.TkWidgets[ index + 1]: new_widg = self.TkWidgets[index + 1]['canvas'] else: return new_item = new_widg.find_withtag("frame" + str(self.app.frame))[0] self.selectedItem = (new_widg, new_item) self.fillCanvases() self.update() self.app.Spectrogram.update() def getMinMaxTime(self): ''' Returns minTime and maxTime tags from selected interval If no minTime or maxTime, returns start or end time of viewed section of TextGrid ''' start = None end = None for tag in self.selectedItem[0].gettags(self.selectedItem[1]): if tag[:7] == 'minTime': start = float(tag[7:]) elif tag[:7] == 'maxTime': end = float(tag[7:]) if start == None: start = self.start if end == None: end = self.end return (start, end) def getBounds(self, event): ''' ''' # debug(event.char, event.keysym, event.keycode) # debug(self.app.frame) f = self.tg_zoom_factor a = self.end - self.start z_out = (a - (a / f)) / 2 z_in = ((f * a) - a) / 2 old_start = self.start old_end = self.end if event.keysym == 'n': if self.selectedItem: self.start, self.end = self.getMinMaxTime() # for tag in self.selectedItem[0].gettags(self.selectedItem[1]): # if tag[:7] == 'minTime': # self.start = float(tag[7:]) # elif tag[:7] == 'maxTime': # self.end = float(tag[7:]) if event.keysym == 'a': self.start = 0 self.end = self.TextGrid.maxTime if event.keysym == 'o': self.start = self.start - z_in self.end = self.end + z_in if event.keysym == 'i': self.start = self.start + z_out self.end = self.end - z_out if event.keysym == 'Left': start = self.start - a / (10 * f) end = self.end - a / (10 * f) if (start < 0): self.start = 0 self.end = a else: self.start = start self.end = end if event.keysym == 'Right': start = self.start + a / (10 * f) end = self.end + a / (10 * f) if end > self.TextGrid.maxTime: self.start = self.TextGrid.maxTime - a self.end = self.TextGrid.maxTime else: self.start = start self.end = end self.fillCanvases() def getTracedFrames(self, frames): ''' ''' frames = [frame[5:] for frame in frames] #to get rid of word "frame in tag" tracedFrames = [] for trace in self.app.Data.data['traces']: tracedFrames = tracedFrames + self.app.Data.tracesExist(trace) return set(frames).intersection(tracedFrames) def fillCanvases(self): ''' ''' if self.start < 0: self.start = 0. if self.end > self.TextGrid.maxTime: self.end = self.TextGrid.maxTime self.updateTimeLabels() if self.selectedItem: old_selected_tags = self.selectedItem[0].gettags( self.selectedItem[1]) duration = self.end - self.start self.frameTier = self.TextGrid.getFirst(self.frameTierName) for el in self.TkWidgets: if 'name' in el: tier = self.TextGrid.getFirst(el['name']) # debug(tier) if 'canvas' in el: canvas = el['canvas'] #remove previous intervals canvas.delete('all') #get starting interval i = tier.indexContaining(self.start) # not sure why, but this is sometimes None -JNW 2020-01-28 if i != None: #debug(self.TextGrid, self.current, el, tier, self.start, i) time = tier[i].maxTime frame_i = 0 while i < len(tier) and tier[i].minTime <= self.end: if self.start >= tier[i].minTime: strtime = self.start else: strtime = tier[i].minTime if self.end <= tier[i].maxTime: time = self.end length = time - strtime pixel_length = length / duration * self.canvas_width mod = length / 2 rel_time = time - self.start loc = (rel_time - mod) / duration * self.canvas_width text = canvas.create_text(loc, self.canvas_height / 2, justify='center', text=tier[i].mark, width=pixel_length, activefill='blue') minTimetag = "minTime" + str(tier[i].minTime) maxTimetag = "maxTime" + str(tier[i].maxTime) canvas.addtag_withtag(minTimetag, text) canvas.addtag_withtag(maxTimetag, text) #add containted frames to tags while frame_i < len( self.frameTier ) and self.frameTier[frame_i].time <= tier[i].maxTime: if self.frameTier[frame_i].time >= tier[i].minTime: canvas.addtag_withtag( "frame" + self.frameTier[frame_i].mark, text) if tier[i].mark != '': el['canvas-label'].addtag_all( "frame" + self.frameTier[frame_i].mark) frame_i += 1 #pass on selected-ness if self.selectedItem: if self.selectedItem[ 0] != self.app.Spectrogram.canvas: # old_selected_tags = self.selectedItem[0].gettags(self.selectedItem[1]) if minTimetag in old_selected_tags and maxTimetag in old_selected_tags and canvas == self.selectedItem[ 0]: # I'm not sure why, but when collapsing canvases this sometimes # changes which tier is selected. Adding canvas == self.selectedItem[0] # seems to fix this though. # - D.S. 2020-01-30 self.selectedItem = (canvas, text) #create line loc = rel_time / duration * self.canvas_width i += 1 if i < len(tier) and loc < self.canvas_width: canvas.create_line(loc, 0, loc, self.canvas_height, tags='line') time = tier[ i].maxTime #here so that loop doesn't run an extra time #fills labels with info about tiers w/traces self.updateTierLabels() elif 'frames' in el: frames = el['frames'] i = 0 frames.delete('all') first_frame_found = False while i < len(tier) and tier[i].time <= self.end: # debug(tier[i].time, i,'frame time and frame number (line 1076)') if tier[i].time >= self.start: # x_coord = (tier[i].time-self.start)/duration*self.canvas_width x_coord = ((tier[i].time - self.start) * self.canvas_width) / duration #determine fill if tier[i].mark in self.app.Data.getCurrentTraceTracedFrames( ): fill = 'black' else: fill = 'gray70' frame = frames.create_line(x_coord, 0, x_coord, self.canvas_height, tags="frame" + tier[i].mark, fill=fill) if first_frame_found == False and i + 1 < len(tier): self.firstFrame = int(tier[i].mark) + 1 first_frame_found = True self.frame_len = tier[i + 1].time - tier[i].time CanvasTooltip(frames, frame, text=tier[i].mark) i += 1 self.lastFrame = int(tier[i - 1].mark) self.paintCanvases() if hasattr(self.app, 'Spectrogram'): # don't try to call this during startup # because TextGrid is loaded earlier self.app.Spectrogram.reset() def updateTimeLabels(self): ''' ''' self.current = self.TextGrid.getFirst( self.frameTierName)[self.app.frame - 1].time self.TkWidgets[-1]['times'].itemconfig(1, text='{:.6f}'.format( self.start)) self.TkWidgets[-1]['times'].itemconfig(2, text='{:.6f}'.format(self.end)) self.TkWidgets[-1]['times'].itemconfig(3, text='{:.6f}'.format( self.current)) def updateTierLabels(self): ''' ''' for el in self.TkWidgets: if 'canvas' in el: current_label = el['canvas-label'].find_all()[0] nonempty_frames = el['canvas-label'].gettags(current_label) el['canvas-label'].itemconfig( current_label, text='{}:\n({}/{})'.format( el['name'], len(self.getTracedFrames(nonempty_frames)), len(nonempty_frames))) def my_find_closest(self, widg, x_loc): ''' replaces TkInter's find_closest function, which is buggy, determines whether found item is text, line, or label, and returns corresponding item ''' #could be more efficient FIXME maybe_item = None dist = 999999999999 for el in widg.find_all(): obj_x = widg.coords(el)[0] if abs(obj_x - x_loc) < dist: dist = abs(obj_x - x_loc) maybe_item = el if widg in self.tier_pairs.keys(): #on tier-label canvas #fill selected tier frames # self.setSelectedIntvlFrames(widg,item) item = maybe_item elif widg in self.tier_pairs.values( ): #on canvas with intervals/frames if isinstance(maybe_item, int): # #if item found is a boundary # if len(widg.gettags(maybe_item)) == 0 or widg.gettags(maybe_item) == ('current',): if 'line' in widg.gettags(maybe_item): #determine on which side of the line the event occurred if widg.coords(maybe_item)[0] > x_loc: item = maybe_item - 1 else: #i.e. event was on line or to the right of it item = maybe_item + 1 else: item = maybe_item # self.setSelectedIntvlFrames(widg,item) else: item = maybe_item return item def setSelectedIntvlFrames(self, tupl): ''' ''' widg, item = tupl self.selectedIntvlFrames = [] for x in widg.gettags(item): if x[:5] == 'frame': self.selectedIntvlFrames.append(x[5:]) def wipeFill(self): ''' Turns selected frame and interval back to black ''' for frame in range(1, self.app.frames + 1): if str(frame) in self.app.Data.getCurrentTraceTracedFrames(): fill = 'black' else: fill = 'gray70' self.frames_canvas.itemconfig('frame' + str(frame), fill=fill) if self.selectedItem: wdg, itm = self.selectedItem if wdg.type(itm) != 'text' and wdg.type(itm) != None: wdg, itm = self.app.Spectrogram.oldSelected wdg.itemconfig(itm, fill='black') if len(wdg.find_withtag(itm + 1)) > 0: wdg.itemconfig(itm + 1, fill='black') if len(wdg.find_withtag(itm - 1)) > 0: wdg.itemconfig(itm - 1, fill='black') #clicked tier label if wdg in self.tier_pairs.keys(): wdg.itemconfig(1, fill='black') self.tier_pairs[wdg].itemconfig('all', fill='black') self.frames_canvas.itemconfig('all', fill='black') def genFrameList(self, event=None, widg=None, x_loc=None, SI=False): ''' Upon click, reads frames within interval from the tags to the text item of that interval, and highlights text of clicked interval ''' self.wipeFill() if event: widg = event.widget x_loc = event.x if SI == False: item = self.my_find_closest(widg, x_loc) self.selectedItem = (widg, item) self.setSelectedIntvlFrames(self.selectedItem) #automatically updates frame if not str(self.app.frame) in self.selectedIntvlFrames: if self.selectedIntvlFrames: new_frame = int(self.selectedIntvlFrames[0]) else: frame = self.my_find_closest(self.frames_canvas, x_loc) framenum = self.frames_canvas.gettags(frame)[0][5:] new_frame = int(framenum) if self.firstFrame > new_frame: new_frame = self.firstFrame elif new_frame > self.lastFrame: new_frame = self.lastFrame self.app.frame = new_frame self.app.framesUpdate() else: self.paintCanvases() self.app.Spectrogram.update() def collapse(self, event): ''' collapse or uncollapse selected tier ''' widg = event.widget if event.num == 1: h = self.collapse_height if int(widg['height']) == h: h = self.canvas_height elif event.num == 4 or event.delta > 0: h = self.canvas_height else: h = self.collapse_height if int(widg['height']) == h: return elif h == self.canvas_height: mv = (self.canvas_height - self.collapse_height - 14) / 2 else: mv = (self.collapse_height + 14 - self.canvas_height) / 2 # manually shifting the text by 7 pixels is a rather ugly hack, # but it works - DS if widg in self.tier_pairs: l, c = widg, self.tier_pairs[widg] else: c = widg l = None for k in self.tier_pairs: if self.tier_pairs[k] == widg: l = k break l.configure(height=h) c.configure(height=h) l.move('all', 0, mv) self.app.event_generate('<Configure>') def paintCanvases(self): ''' ''' if self.selectedItem: wdg, itm = self.selectedItem #paint selected if wdg.type(itm) == 'text': wdg.itemconfig(itm, fill='blue') #paint boundaries of selected if itm + 1 in wdg.find_all(): wdg.itemconfig(itm + 1, fill='blue') if itm - 1 in wdg.find_all(): wdg.itemconfig(itm - 1, fill='blue') if wdg in self.tier_pairs.keys(): #if on tier-label canvas canvas = self.tier_pairs[wdg] for el in canvas.find_all(): # #make all text intervals blue # if canvas.type(canvas.find_withtag(el)) == 'text': canvas.itemconfig(el, fill='blue') #paint frames frames = wdg.gettags(itm) for frame in frames: if frame[:5] == 'frame': frame_obj = self.frames_canvas.find_withtag(frame) #detect whether frame contains any traces framenum = frame[5:] if framenum in self.app.Data.getCurrentTraceTracedFrames(): fill = 'blue' else: fill = 'dodger blue' self.frames_canvas.itemconfig(frame_obj, fill=fill) #current frame highlighted in red if self.app.frame: self.highlighted_frame = self.frames_canvas.find_withtag( 'frame' + str(self.app.frame)) self.frames_canvas.itemconfig(self.highlighted_frame, fill='red') def update(self): ''' ''' # debug(self.frames_canvas) #create list of displayed frames' tags itrobj = [] for itm in self.frames_canvas.find_all(): itrobj += list(self.frames_canvas.gettags(itm)) #if selected frame is out of view if "frame" + str(self.app.frame) not in itrobj: duration = self.end - self.start #recenter view on selected frame new_time = self.TextGrid.getFirst( self.frameTierName)[self.app.frame - 1].time self.start = new_time - (duration / 2) self.end = new_time + (duration / 2) #redraw self.fillCanvases() self.wipeFill() #if selected frame outside selected interval, select interval on same tier containing frame if self.selectedItem: if self.selectedItem[0] in self.tier_pairs.keys( ) or self.selectedItem[0] in self.tier_pairs.values(): if "frame" + str( self.app.frame) not in self.selectedItem[0].gettags( self.selectedItem[1] ): #FIXME should also detect if on label canvas widg = self.selectedItem[0] if widg in self.tier_pairs: widg = self.tier_pairs[widg] new_interval = widg.find_withtag("frame" + str(self.app.frame))[0] self.selectedItem = (self.selectedItem[0], new_interval) # repaint all frames self.paintCanvases() self.updateTimeLabels() def grid(self, event=None): ''' Wrapper for gridding all of our Tk widgets. This funciton assumes that the tiers (as specified in the actual TextGrid files) are in some sort of reasonable order, with the default label being drawn on top. ''' for t in range(len(self.TkWidgets)): tierWidgets = self.TkWidgets[t] if 'label' in tierWidgets: tierWidgets['label'].grid(row=t, column=0, sticky='w') if 'frames' in tierWidgets: tierWidgets['frames'].grid(row=t, column=2, sticky='w', pady=self.app.pady) tierWidgets['frames-label'].grid(row=t, column=0, sticky='w', pady=self.app.pady) if 'canvas' in tierWidgets: tierWidgets['canvas'].grid(row=t, column=2, sticky='w', pady=self.app.pady / 2) tierWidgets['canvas-label'].grid(row=t, column=0, sticky='w', pady=self.app.pady / 2) self.tier_pairs[ tierWidgets['canvas-label']] = tierWidgets['canvas'] if 'times' in tierWidgets: tierWidgets['times'].grid(row=t, column=2, sticky='s') def grid_remove(self): raise NotImplementedError('cannot grid_remove the TextGridModule') def openSearch(self, event=None): self.app.Search.openSearch()
if interval.mark == '': continue print(interval.mark, interval.minTime, interval.maxTime) outpath = os.path.join(temp_wav_dir, interval.mark + '.wav') extract_audio(wav_path, outpath, interval.minTime, interval.maxTime, padding = padding) rep = Mfcc(outpath, freq_lims = (80, 7800), num_coeffs = 12, win_len = 0.025, time_step = 0.01) rep.is_windowed = True duration = interval.maxTime - interval.minTime thresh = unnorm(norm(duration, min_duration, max_duration), min_thresh, max_thresh) rep.segment(threshold = thresh) print(sorted(rep._segments.keys())) padded_begin = interval.minTime - padding if padded_begin < 0: padded_begin = 0 for k in sorted(rep._segments.keys()): with open(os.path.join(temp_mfcc_dir, '{}.mfcc'.format(seg_ind)), 'wb') as fh: pickle.dump(rep[k[0],k[1]], fh) with open(os.path.join(temp_mean_dir, '{}.mean'.format(seg_ind)), 'wb') as fh: pickle.dump(rep._segments[k], fh) segs.append(str(seg_ind)) seg_ind += 1 begin = round(k[0] + padded_begin, 3) end = round(k[1] + padded_begin,3) print(begin, end) segmentation_tier.add(begin, end, '{}'.format(seg_ind)) with open(os.path.join(temp_align_dir, '{}.seg'.format(f)), 'w') as fa: fa.write(' '.join(segs)) tg.append(segmentation_tier) tg.write(textgrid_path.replace(data_dir, temp_textgrid_dir))
wordintervals.append(x) elif i == 1: for x in ti: x.maxTime += cur_dur x.minTime += cur_dur phoneintervals.append(x) cur_dur += maxtime words = IntervalTier(name='words') for i in wordintervals: words.addInterval(i) phones = IntervalTier(name='phones') for i in phoneintervals: phones.addInterval(i) tg1 = TextGrid(maxTime=cur_dur) tg1.append(words) tg1.append(phones) tg1.write(chapteroutpath1, null='') speaker_tier = IntervalTier(name=speaker) for i in range(len(groupedwavfiles)): if i == 1: speaker_tier.add(0.0, wavfiletimes[0], groupedlabtext[0]) else: speaker_tier.add(wavfiletimes[i - 2], wavfiletimes[i - 1], groupedlabtext[i - 1]) tg2 = TextGrid(maxTime=duration) tg2.append(speaker_tier) tg2.write(chapteroutpath2, null='')
def ctm_to_textgrid(word_ctm, phone_ctm, out_directory, corpus, dictionary, frameshift=0.01): textgrid_write_errors = {} frameshift = Decimal(str(frameshift)) if not os.path.exists(out_directory): os.makedirs(out_directory, exist_ok=True) if not corpus.segments: for i, (k, v) in enumerate(sorted(word_ctm.items())): maxtime = Decimal(str(corpus.get_wav_duration(k))) speaker = list(v.keys())[0] v = list(v.values())[0] try: tg = TextGrid(maxTime=maxtime) wordtier = IntervalTier(name='words', maxTime=maxtime) phonetier = IntervalTier(name='phones', maxTime=maxtime) for interval in v: if maxtime - interval[1] < frameshift: # Fix rounding issues interval[1] = maxtime wordtier.add(*interval) for interval in phone_ctm[k][speaker]: if maxtime - interval[1] < frameshift: interval[1] = maxtime phonetier.add(*interval) tg.append(wordtier) tg.append(phonetier) relative = corpus.file_directory_mapping[k] if relative: speaker_directory = os.path.join(out_directory, relative) else: speaker_directory = out_directory os.makedirs(speaker_directory, exist_ok=True) outpath = os.path.join(speaker_directory, k + '.TextGrid') tg.write(outpath) except Exception as e: exc_type, exc_value, exc_traceback = sys.exc_info() textgrid_write_errors[k] = '\n'.join(traceback.format_exception(exc_type, exc_value, exc_traceback)) else: silences = {dictionary.optional_silence, dictionary.nonoptional_silence} for i, (filename, speaker_dict) in enumerate(sorted(word_ctm.items())): maxtime = corpus.get_wav_duration(filename) try: speaker_directory = os.path.join(out_directory, corpus.file_directory_mapping[filename]) tg = TextGrid(maxTime=maxtime) for speaker in corpus.speaker_ordering[filename]: words = speaker_dict[speaker] word_tier_name = '{} - words'.format(speaker) phone_tier_name = '{} - phones'.format(speaker) word_tier = IntervalTier(name=word_tier_name, maxTime=maxtime) phone_tier = IntervalTier(name=phone_tier_name, maxTime=maxtime) for w in words: word_tier.add(*w) for p in phone_ctm[filename][speaker]: if len(phone_tier) > 0 and phone_tier[-1].mark in silences and p[2] in silences: phone_tier[-1].maxTime = p[1] else: if len(phone_tier) > 0 and p[2] in silences and p[0] < phone_tier[-1].maxTime: p = phone_tier[-1].maxTime, p[1], p[2] elif len(phone_tier) > 0 and p[2] not in silences and p[0] < phone_tier[-1].maxTime and \ phone_tier[-1].mark in silences: phone_tier[-1].maxTime = p[0] phone_tier.add(*p) tg.append(word_tier) tg.append(phone_tier) tg.write(os.path.join(speaker_directory, filename + '.TextGrid')) except Exception as e: exc_type, exc_value, exc_traceback = sys.exc_info() textgrid_write_errors[filename] = '\n'.join(traceback.format_exception(exc_type, exc_value, exc_traceback)) if textgrid_write_errors: error_log = os.path.join(out_directory, 'output_errors.txt') with open(error_log, 'w', encoding='utf8') as f: f.write('The following exceptions were encountered during the ouput of the alignments to TextGrids:\n\n') for k,v in textgrid_write_errors.items(): f.write('{}:\n'.format(k)) f.write('{}\n\n'.format(v))
def ctm_to_textgrid(word_ctm, phone_ctm, out_directory, corpus): if not os.path.exists(out_directory): os.makedirs(out_directory, exist_ok=True) if not corpus.segments: for i,(k,v) in enumerate(word_ctm.items()): maxtime = corpus.get_wav_duration(k) try: tg = TextGrid(maxTime = maxtime) wordtier = IntervalTier(name = 'words', maxTime = maxtime) phonetier = IntervalTier(name = 'phones', maxTime = maxtime) for interval in v: wordtier.add(*interval) for interval in phone_ctm[k]: phonetier.add(*interval) tg.append(wordtier) tg.append(phonetier) if corpus.speaker_directories: speaker_directory = os.path.join(out_directory, corpus.utt_speak_mapping[k]) else: speaker_directory = out_directory os.makedirs(speaker_directory, exist_ok=True) outpath = os.path.join(speaker_directory, k + '.TextGrid') tg.write(outpath) except ValueError as e: print('Could not write textgrid for {}'.format(k)) print(e) else: tgs = {} for i,(k,v) in enumerate(word_ctm.items()): rec = corpus.segments[k] rec, begin, end = rec.split(' ') maxtime = corpus.get_wav_duration(k) if rec not in tgs: tgs[rec] = TextGrid(maxTime = maxtime) tg = tgs[rec] begin = float(begin) speaker = corpus.utt_speak_mapping[k] word_tier_name = '{} - words'.format(speaker) phone_tier_name = '{} - phones'.format(speaker) wordtier = tg.getFirst(word_tier_name) if wordtier is None: wordtier = IntervalTier(name = word_tier_name, maxTime = maxtime) tg.append(wordtier) phonetier = tg.getFirst(phone_tier_name) if phonetier is None: phonetier = IntervalTier(name = phone_tier_name, maxTime = maxtime) tg.append(phonetier) for interval in v: interval = interval[0] + begin, interval[1] + begin, interval[2] wordtier.add(*interval) for interval in phone_ctm[k]: interval = interval[0] + begin, interval[1] + begin, interval[2] phonetier.add(*interval) for k,v in tgs.items(): outpath = os.path.join(out_directory, k + '.TextGrid') try: v.write(outpath) except ValueError as e: print('Could not write textgrid for {}'.format(k)) print(e)
for line in time_list: line_split = line.split() #print(line_split[0], file_id) if line_split[0] in file_id: start = float(line_split[1]) / 1000 end = float(line_split[2]) / 1000 break else: continue # Make word and speaker tiers intervalTiers = {} word_name = speaker + " - words" word_tier = IntervalTier(word_name, 0, duration) intervalTiers[word_name] = word_tier intervalTiers[word_name].add(0, duration, word) phone_name = speaker + " - phones" phone_tier = IntervalTier(phone_name, 0, duration) intervalTiers[phone_name] = phone_tier intervalTiers[phone_name].add(0, start, "h") intervalTiers[phone_name].add(start, end, vowel) intervalTiers[phone_name].add(end, duration, "d") for key, value in intervalTiers.items(): tg.append(value) export_name = file_id + ".TextGrid" tg.write(os.path.join(output_dir, export_name))
def ctm_to_textgrid(word_ctm, phone_ctm, out_directory, corpus, dictionary, frameshift=0.01): frameshift = Decimal(str(frameshift)) if not os.path.exists(out_directory): os.makedirs(out_directory, exist_ok=True) if not corpus.segments: for i, (k, v) in enumerate(sorted(word_ctm.items())): maxtime = Decimal(str(corpus.get_wav_duration(k))) speaker = list(v.keys())[0] v = list(v.values())[0] try: tg = TextGrid(maxTime=maxtime) wordtier = IntervalTier(name='words', maxTime=maxtime) phonetier = IntervalTier(name='phones', maxTime=maxtime) for interval in v: if maxtime - interval[ 1] < frameshift: # Fix rounding issues interval[1] = maxtime wordtier.add(*interval) for interval in phone_ctm[k][speaker]: if maxtime - interval[1] < frameshift: interval[1] = maxtime phonetier.add(*interval) tg.append(wordtier) tg.append(phonetier) if corpus.speaker_directories: speaker_directory = os.path.join( out_directory, corpus.utt_speak_mapping[k]) else: speaker_directory = out_directory os.makedirs(speaker_directory, exist_ok=True) outpath = os.path.join(speaker_directory, k + '.TextGrid') tg.write(outpath) except ValueError as e: print( 'There was an error writing the TextGrid for {}, please see below:' .format(k)) raise else: silences = { dictionary.optional_silence, dictionary.nonoptional_silence } for i, (filename, speaker_dict) in enumerate(sorted(word_ctm.items())): maxtime = corpus.get_wav_duration(filename) tg = TextGrid(maxTime=maxtime) for speaker, words in speaker_dict.items(): word_tier_name = '{} - words'.format(speaker) phone_tier_name = '{} - phones'.format(speaker) word_tier = IntervalTier(name=word_tier_name, maxTime=maxtime) phone_tier = IntervalTier(name=phone_tier_name, maxTime=maxtime) for w in words: word_tier.add(*w) for p in phone_ctm[filename][speaker]: if len(phone_tier) > 0 and phone_tier[ -1].mark in silences and p[2] in silences: phone_tier[-1].maxTime = p[1] else: if len(phone_tier) > 0 and p[2] in silences and p[ 0] < phone_tier[-1].maxTime: p = phone_tier[-1].maxTime, p[1], p[2] elif len(phone_tier) > 0 and p[2] not in silences and p[0] < phone_tier[-1].maxTime and \ phone_tier[-1].mark in silences: phone_tier[-1].maxTime = p[0] phone_tier.add(*p) tg.append(word_tier) tg.append(phone_tier) tg.write(os.path.join(out_directory, filename + '.TextGrid'))
for line in time_list: line_split = line.split() #print(line_split[0], file_id) if line_split[0] in file_id: start = float(line_split[1])/1000 end = float(line_split[2])/1000 break else: continue # Make word and speaker tiers intervalTiers = {} word_name = speaker + " - words" word_tier = IntervalTier(word_name, 0, duration) intervalTiers[word_name] = word_tier intervalTiers[word_name].add(0, duration, word) phone_name = speaker + " - phones" phone_tier = IntervalTier(phone_name, 0, duration) intervalTiers[phone_name] = phone_tier intervalTiers[phone_name].add(0, start, "h") intervalTiers[phone_name].add(start, end, vowel) intervalTiers[phone_name].add(end, duration, "d") for key, value in intervalTiers.items(): tg.append(value) export_name = file_id + ".TextGrid" tg.write(os.path.join(output_dir, export_name))
num_coeffs=12, win_len=0.025, time_step=0.01) rep.is_windowed = True duration = interval.maxTime - interval.minTime thresh = unnorm(norm(duration, min_duration, max_duration), min_thresh, max_thresh) rep.segment(threshold=thresh) print(sorted(rep._segments.keys())) padded_begin = interval.minTime - padding if padded_begin < 0: padded_begin = 0 for k in sorted(rep._segments.keys()): with open(os.path.join(temp_mfcc_dir, '{}.mfcc'.format(seg_ind)), 'wb') as fh: pickle.dump(rep[k[0], k[1]], fh) with open(os.path.join(temp_mean_dir, '{}.mean'.format(seg_ind)), 'wb') as fh: pickle.dump(rep._segments[k], fh) segs.append(str(seg_ind)) seg_ind += 1 begin = round(k[0] + padded_begin, 3) end = round(k[1] + padded_begin, 3) print(begin, end) segmentation_tier.add(begin, end, '{}'.format(seg_ind)) with open(os.path.join(temp_align_dir, '{}.seg'.format(f)), 'w') as fa: fa.write(' '.join(segs)) tg.append(segmentation_tier) tg.write(textgrid_path.replace(data_dir, temp_textgrid_dir))
def parse_transcript(path): file_name = os.path.splitext(os.path.basename(path))[0] tg_path = path.replace(os.path.join(orig_dir, 'txt'), output_dir).replace('.txt', '.TextGrid') tg = TextGrid() tiers = {} continuation = False prev_speaker = None with open(path, 'r', encoding='utf8') as f: for i, line in enumerate(f): line = line.strip() if i == 0: continue if not line: continue if line in ['<I>', '</I>']: continue if line.startswith('&'): continue m = re.match( r'^<\$(\w)>.*<start=?([0-9:.;l ]+) end6?=([0-9>:.;l ]*)>?[?]?\s+<#>(.+)$', line) if m is None: text = parse_text(line) try: tiers[speaker][-1].mark += ' ' + text except UnboundLocalError: continue # error else: speaker_code, start, end, text = m.groups() if speaker_code == 'Z': continue try: speaker = file_code_to_speaker[(file_name, speaker_code)] except KeyError: speaker = 'unknown_{}_{}'.format(file_name, speaker_code) if speaker not in tiers: tiers[speaker] = IntervalTier(speaker) start = parse_time(start) end = parse_time(end) text = parse_text(text) if text == "Again he's quoting": continue if not text: continue if end is None: continue if start is None: if prev_speaker != speaker: continue continuation = True tiers[speaker][-1].mark += ' ' + text if '<' in text.replace('<beep_sound>', '').replace('<unk>', ''): print(file_name, start, end, text) print(line) if continuation or (len(tiers[speaker]) > 0 and start - tiers[speaker][-1].maxTime < 0.1): tiers[speaker][-1].mark += ' ' + text if not continuation: tiers[speaker][-1].maxTime = end continuation = False else: tiers[speaker].add(start, end, text) # print(speaker) # print(start, end) # print(text) prev_speaker = speaker print(tiers.keys(), [len(x) for x in tiers.values()]) for v in tiers.values(): tg.append(v) tg.write(tg_path)
def ctm_to_textgrid(word_ctm, phone_ctm, out_directory, corpus, dictionary, frameshift=0.01): textgrid_write_errors = {} frameshift = Decimal(str(frameshift)) if not os.path.exists(out_directory): os.makedirs(out_directory, exist_ok=True) if not corpus.segments: for i, (k, v) in enumerate(sorted(word_ctm.items())): maxtime = Decimal(str(corpus.get_wav_duration(k))) speaker = list(v.keys())[0] v = list(v.values())[0] try: tg = TextGrid(maxTime=maxtime) wordtier = IntervalTier(name='words', maxTime=maxtime) phonetier = IntervalTier(name='phones', maxTime=maxtime) for interval in v: if maxtime - interval[ 1] < frameshift: # Fix rounding issues interval[1] = maxtime wordtier.add(*interval) for interval in phone_ctm[k][speaker]: if maxtime - interval[1] < frameshift: interval[1] = maxtime phonetier.add(*interval) tg.append(wordtier) tg.append(phonetier) if corpus.speaker_directories: speaker_directory = os.path.join( out_directory, corpus.utt_speak_mapping[k]) else: speaker_directory = out_directory os.makedirs(speaker_directory, exist_ok=True) outpath = os.path.join(speaker_directory, k + '.TextGrid') tg.write(outpath) except Exception as e: exc_type, exc_value, exc_traceback = sys.exc_info() textgrid_write_errors[k] = '\n'.join( traceback.format_exception(exc_type, exc_value, exc_traceback)) else: silences = { dictionary.optional_silence, dictionary.nonoptional_silence } for i, (filename, speaker_dict) in enumerate(sorted(word_ctm.items())): maxtime = corpus.get_wav_duration(filename) try: tg = TextGrid(maxTime=maxtime) for speaker, words in speaker_dict.items(): word_tier_name = '{} - words'.format(speaker) phone_tier_name = '{} - phones'.format(speaker) word_tier = IntervalTier(name=word_tier_name, maxTime=maxtime) phone_tier = IntervalTier(name=phone_tier_name, maxTime=maxtime) for w in words: word_tier.add(*w) for p in phone_ctm[filename][speaker]: if len(phone_tier) > 0 and phone_tier[ -1].mark in silences and p[2] in silences: phone_tier[-1].maxTime = p[1] else: if len(phone_tier) > 0 and p[2] in silences and p[ 0] < phone_tier[-1].maxTime: p = phone_tier[-1].maxTime, p[1], p[2] elif len(phone_tier) > 0 and p[2] not in silences and p[0] < phone_tier[-1].maxTime and \ phone_tier[-1].mark in silences: phone_tier[-1].maxTime = p[0] phone_tier.add(*p) tg.append(word_tier) tg.append(phone_tier) tg.write(os.path.join(out_directory, filename + '.TextGrid')) except Exception as e: exc_type, exc_value, exc_traceback = sys.exc_info() textgrid_write_errors[filename] = '\n'.join( traceback.format_exception(exc_type, exc_value, exc_traceback)) if textgrid_write_errors: error_log = os.path.join(out_directory, 'output_errors.txt') with open(error_log, 'w', encoding='utf8') as f: f.write( 'The following exceptions were encountered during the ouput of the alignments to TextGrids:\n\n' ) for k, v in textgrid_write_errors.items(): f.write('{}:\n'.format(k)) f.write('{}\n\n'.format(v))
elif i == 1: for x in ti: x.maxTime += cur_dur x.minTime += cur_dur phoneintervals.append(x) cur_dur += maxtime words = IntervalTier(name='words') for i in wordintervals: words.addInterval(i) phones = IntervalTier(name='phones') for i in phoneintervals: phones.addInterval(i) tg1 = TextGrid(maxTime = cur_dur) tg1.append(words) tg1.append(phones) tg1.write(chapteroutpath1, null = '') speaker_tier = IntervalTier(name=speaker) for i in range(len(groupedwavfiles)): if i == 1: speaker_tier.add(0.0, wavfiletimes[0], groupedlabtext[0]) else: speaker_tier.add(wavfiletimes[i-2], wavfiletimes[i-1], groupedlabtext[i-1]) tg2 = TextGrid(maxTime = duration) tg2.append(speaker_tier) tg2.write(chapteroutpath2, null = '')
for w in u.word: label = w._type_node['label'] if label in ['<NOISE>', '<VOCNOISE>']: continue end = w.end if end > duration: end = duration word_tier.add(w.begin, end, label) utt_word_tier.add(w.begin - begin, end - begin, label) for p in u.phone: label = p._type_node['label'] end = p.end if end > duration: end = duration try: phone_tier.add(p.begin, end, label) utt_phone_tier.add(p.begin - begin, end - begin, label) except ValueError: print(label, p.begin, p.end, duration) raise durations.append(u.duration) utt_tg.append(utt_word_tier) utt_tg.append(utt_phone_tier) utt_tg.write(utt_tg_path) tg.append(utterance_tier) tg.write(tg_path) tg.append(word_tier) tg.append(phone_tier) tg.write(full_tg_path) print(ignored, not_ignored)