Anchor-annotator 0.8.1__py3-none-any.whl → 0.9.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- anchor/_version.py +9 -4
- anchor/command_line.py +1 -0
- anchor/main.py +113 -6
- anchor/models.py +402 -23
- anchor/plot.py +835 -104
- anchor/settings.py +6 -10
- anchor/ui_main_window.py +14 -8
- anchor/undo.py +672 -11
- anchor/widgets.py +308 -44
- anchor/workers.py +632 -351
- {Anchor_annotator-0.8.1.dist-info → anchor_annotator-0.9.0.dist-info}/METADATA +3 -2
- anchor_annotator-0.9.0.dist-info/RECORD +22 -0
- {Anchor_annotator-0.8.1.dist-info → anchor_annotator-0.9.0.dist-info}/WHEEL +1 -1
- Anchor_annotator-0.8.1.dist-info/RECORD +0 -22
- {Anchor_annotator-0.8.1.dist-info → anchor_annotator-0.9.0.dist-info/licenses}/LICENSE +0 -0
- {Anchor_annotator-0.8.1.dist-info → anchor_annotator-0.9.0.dist-info}/top_level.txt +0 -0
anchor/undo.py
CHANGED
@@ -6,8 +6,19 @@ import unicodedata
|
|
6
6
|
|
7
7
|
import pynini.lib
|
8
8
|
import sqlalchemy
|
9
|
-
from montreal_forced_aligner.data import WordType
|
10
|
-
from montreal_forced_aligner.db import
|
9
|
+
from montreal_forced_aligner.data import WordType, WorkflowType
|
10
|
+
from montreal_forced_aligner.db import (
|
11
|
+
CorpusWorkflow,
|
12
|
+
File,
|
13
|
+
Phone,
|
14
|
+
PhoneInterval,
|
15
|
+
Pronunciation,
|
16
|
+
Speaker,
|
17
|
+
Utterance,
|
18
|
+
Word,
|
19
|
+
WordInterval,
|
20
|
+
bulk_update,
|
21
|
+
)
|
11
22
|
from PySide6 import QtCore, QtGui
|
12
23
|
from sqlalchemy.orm import make_transient
|
13
24
|
|
@@ -45,6 +56,7 @@ class CorpusCommand(QtGui.QUndoCommand):
|
|
45
56
|
self.corpus_model.session.commit()
|
46
57
|
except Exception:
|
47
58
|
self.corpus_model.session.rollback()
|
59
|
+
raise
|
48
60
|
# while True:
|
49
61
|
# try:
|
50
62
|
# with self.corpus_model.session.begin_nested():
|
@@ -62,6 +74,7 @@ class CorpusCommand(QtGui.QUndoCommand):
|
|
62
74
|
self.corpus_model.session.commit()
|
63
75
|
except Exception:
|
64
76
|
self.corpus_model.session.rollback()
|
77
|
+
raise
|
65
78
|
# while True:
|
66
79
|
# try:
|
67
80
|
# with self.corpus_model.session.begin_nested():
|
@@ -218,6 +231,10 @@ class SplitUtteranceCommand(FileCommand):
|
|
218
231
|
make_transient(x)
|
219
232
|
for x in self.merged_utterance.word_intervals:
|
220
233
|
make_transient(x)
|
234
|
+
for x in self.merged_utterance.reference_phone_intervals:
|
235
|
+
make_transient(x)
|
236
|
+
for x in self.merged_utterance.reference_word_intervals:
|
237
|
+
make_transient(x)
|
221
238
|
session.add(self.merged_utterance)
|
222
239
|
for u in self.split_utterances:
|
223
240
|
session.delete(u)
|
@@ -394,21 +411,31 @@ class UpdateUtteranceTimesCommand(FileCommand):
|
|
394
411
|
)
|
395
412
|
)
|
396
413
|
|
397
|
-
def
|
398
|
-
self.utterance.begin =
|
399
|
-
self.utterance.end =
|
414
|
+
def _set_times(self, session, begin, end):
|
415
|
+
self.utterance.begin = begin
|
416
|
+
self.utterance.end = end
|
400
417
|
self.utterance.xvector = None
|
401
418
|
self.utterance.ivector = None
|
402
419
|
self.utterance.features = None
|
420
|
+
if self.utterance.phone_intervals:
|
421
|
+
self.utterance.phone_intervals[0].begin = begin
|
422
|
+
self.utterance.phone_intervals[-1].end = end
|
423
|
+
if self.utterance.word_intervals:
|
424
|
+
self.utterance.word_intervals[0].begin = begin
|
425
|
+
self.utterance.word_intervals[-1].end = end
|
426
|
+
if self.utterance.reference_phone_intervals:
|
427
|
+
self.utterance.reference_phone_intervals[0].begin = begin
|
428
|
+
self.utterance.reference_phone_intervals[-1].end = end
|
429
|
+
if self.utterance.reference_word_intervals:
|
430
|
+
self.utterance.reference_word_intervals[0].begin = begin
|
431
|
+
self.utterance.reference_word_intervals[-1].end = end
|
403
432
|
session.merge(self.utterance)
|
404
433
|
|
434
|
+
def _redo(self, session) -> None:
|
435
|
+
self._set_times(session, self.new_begin, self.new_end)
|
436
|
+
|
405
437
|
def _undo(self, session) -> None:
|
406
|
-
self.
|
407
|
-
self.utterance.end = self.old_end
|
408
|
-
self.utterance.xvector = None
|
409
|
-
self.utterance.ivector = None
|
410
|
-
self.utterance.features = None
|
411
|
-
session.merge(self.utterance)
|
438
|
+
self._set_times(session, self.old_begin, self.old_end)
|
412
439
|
|
413
440
|
def update_data(self):
|
414
441
|
super().update_data()
|
@@ -416,6 +443,640 @@ class UpdateUtteranceTimesCommand(FileCommand):
|
|
416
443
|
self.corpus_model.update_utterance_table_row(self.utterance)
|
417
444
|
|
418
445
|
|
446
|
+
class DeleteReferenceIntervalsCommand(FileCommand):
|
447
|
+
def __init__(self, utterance: Utterance, file_model: FileUtterancesModel):
|
448
|
+
super().__init__(file_model)
|
449
|
+
self.utterance = utterance
|
450
|
+
self.reference_intervals = None
|
451
|
+
self.reference_workflow = None
|
452
|
+
|
453
|
+
def _redo(self, session) -> None:
|
454
|
+
if self.reference_intervals is None:
|
455
|
+
self.reference_intervals = self.utterance.reference_phone_intervals
|
456
|
+
self.utterance.reference_phone_intervals = []
|
457
|
+
self.utterance.manual_alignments = False
|
458
|
+
session.merge(self.utterance)
|
459
|
+
|
460
|
+
def _undo(self, session) -> None:
|
461
|
+
reference_phone_intervals = []
|
462
|
+
for pi in self.reference_intervals:
|
463
|
+
make_transient(pi)
|
464
|
+
reference_phone_intervals.append(pi)
|
465
|
+
self.utterance.manual_alignments = True
|
466
|
+
self.utterance.reference_phone_intervals = sorted(
|
467
|
+
reference_phone_intervals, key=lambda x: x.begin
|
468
|
+
)
|
469
|
+
session.merge(self.utterance)
|
470
|
+
|
471
|
+
def update_data(self):
|
472
|
+
super().update_data()
|
473
|
+
self.corpus_model.changeCommandFired.emit()
|
474
|
+
self.file_model.phoneTierChanged.emit(self.utterance)
|
475
|
+
|
476
|
+
|
477
|
+
class UpdatePhoneBoundariesCommand(FileCommand):
|
478
|
+
def __init__(
|
479
|
+
self,
|
480
|
+
utterance: Utterance,
|
481
|
+
first_phone_interval: PhoneInterval,
|
482
|
+
second_phone_interval: PhoneInterval,
|
483
|
+
new_time: float,
|
484
|
+
file_model: FileUtterancesModel,
|
485
|
+
):
|
486
|
+
super().__init__(file_model)
|
487
|
+
self.utterance = utterance
|
488
|
+
self.old_manual_alignments = utterance.manual_alignments
|
489
|
+
self.first_phone_interval = first_phone_interval
|
490
|
+
self.second_phone_interval = second_phone_interval
|
491
|
+
self.first_word_interval = None
|
492
|
+
self.second_word_interval = None
|
493
|
+
self.at_word_boundary = (
|
494
|
+
self.first_phone_interval.word_interval_id
|
495
|
+
!= self.second_phone_interval.word_interval_id
|
496
|
+
)
|
497
|
+
if True or self.at_word_boundary:
|
498
|
+
if isinstance(self.first_phone_interval, PhoneInterval):
|
499
|
+
word_intervals = utterance.word_intervals
|
500
|
+
else:
|
501
|
+
word_intervals = utterance.reference_word_intervals
|
502
|
+
for wi in word_intervals:
|
503
|
+
if self.first_word_interval is not None and self.second_word_interval is not None:
|
504
|
+
break
|
505
|
+
if wi.id == first_phone_interval.word_interval_id:
|
506
|
+
self.first_word_interval = wi
|
507
|
+
elif wi.id == second_phone_interval.word_interval_id:
|
508
|
+
self.second_word_interval = wi
|
509
|
+
self.speaker_id = utterance.speaker_id
|
510
|
+
self.old_time = second_phone_interval.begin
|
511
|
+
self.new_time = new_time
|
512
|
+
self.setText(
|
513
|
+
QtCore.QCoreApplication.translate(
|
514
|
+
"UpdatePhoneBoundariesCommand", "Update phone boundaries"
|
515
|
+
)
|
516
|
+
)
|
517
|
+
|
518
|
+
def _set_time(self, session, new_time, manual_alignments):
|
519
|
+
if not self.old_manual_alignments:
|
520
|
+
self.utterance.manual_alignments = manual_alignments
|
521
|
+
session.merge(self.utterance)
|
522
|
+
self.first_phone_interval.end = new_time
|
523
|
+
self.second_phone_interval.begin = new_time
|
524
|
+
session.merge(self.utterance)
|
525
|
+
session.merge(self.first_phone_interval)
|
526
|
+
session.merge(self.second_phone_interval)
|
527
|
+
if self.at_word_boundary:
|
528
|
+
if self.first_word_interval is not None:
|
529
|
+
self.first_word_interval.end = new_time
|
530
|
+
session.merge(self.first_word_interval)
|
531
|
+
if self.second_word_interval is not None:
|
532
|
+
self.second_word_interval.begin = new_time
|
533
|
+
session.merge(self.second_word_interval)
|
534
|
+
|
535
|
+
def _redo(self, session) -> None:
|
536
|
+
self._set_time(session, self.new_time, True)
|
537
|
+
|
538
|
+
def _undo(self, session) -> None:
|
539
|
+
self._set_time(session, self.old_time, self.old_manual_alignments)
|
540
|
+
|
541
|
+
def id(self) -> int:
|
542
|
+
return 2
|
543
|
+
|
544
|
+
def mergeWith(self, other: UpdatePhoneBoundariesCommand) -> bool:
|
545
|
+
if (
|
546
|
+
other.id() != self.id()
|
547
|
+
or other.first_phone_interval.id != self.first_phone_interval.id
|
548
|
+
or other.second_phone_interval.id != self.second_phone_interval.id
|
549
|
+
):
|
550
|
+
return False
|
551
|
+
self.new_time = other.new_time
|
552
|
+
return True
|
553
|
+
|
554
|
+
def update_data(self):
|
555
|
+
super().update_data()
|
556
|
+
self.corpus_model.changeCommandFired.emit()
|
557
|
+
|
558
|
+
|
559
|
+
class DeletePhoneIntervalCommand(FileCommand):
|
560
|
+
def __init__(
|
561
|
+
self,
|
562
|
+
utterance: Utterance,
|
563
|
+
phone_interval: PhoneInterval,
|
564
|
+
previous_phone_interval: typing.Optional[PhoneInterval],
|
565
|
+
following_phone_interval: typing.Optional[PhoneInterval],
|
566
|
+
time_point: typing.Optional[float],
|
567
|
+
file_model: FileUtterancesModel,
|
568
|
+
):
|
569
|
+
super().__init__(file_model)
|
570
|
+
self.word_interval_lookup = "word_intervals"
|
571
|
+
self.phone_interval_lookup = "phone_intervals"
|
572
|
+
self.using_reference = not isinstance(phone_interval, PhoneInterval)
|
573
|
+
if self.using_reference:
|
574
|
+
self.word_interval_lookup = "reference_word_intervals"
|
575
|
+
self.phone_interval_lookup = "reference_phone_intervals"
|
576
|
+
self.utterance = utterance
|
577
|
+
self.old_manual_alignments = utterance.manual_alignments
|
578
|
+
self.phone_interval = phone_interval
|
579
|
+
self.previous_phone_interval = previous_phone_interval
|
580
|
+
self.has_previous = previous_phone_interval is not None
|
581
|
+
self.has_following = following_phone_interval is not None
|
582
|
+
self.following_phone_interval = following_phone_interval
|
583
|
+
self.new_time = time_point
|
584
|
+
self.first_word_interval = None
|
585
|
+
self.second_word_interval = None
|
586
|
+
previous_word_interval_id = (
|
587
|
+
self.previous_phone_interval.word_interval_id
|
588
|
+
if self.previous_phone_interval is not None
|
589
|
+
else None
|
590
|
+
)
|
591
|
+
word_interval_id = self.phone_interval.word_interval_id
|
592
|
+
following_word_interval_id = (
|
593
|
+
self.following_phone_interval.word_interval_id
|
594
|
+
if self.following_phone_interval is not None
|
595
|
+
else None
|
596
|
+
)
|
597
|
+
self.word_interval = None
|
598
|
+
for wi in getattr(self.utterance, self.word_interval_lookup):
|
599
|
+
if wi.id == self.phone_interval.word_interval_id:
|
600
|
+
self.word_interval = wi
|
601
|
+
break
|
602
|
+
self.single_phone_word = (
|
603
|
+
word_interval_id != previous_word_interval_id
|
604
|
+
and word_interval_id != following_word_interval_id
|
605
|
+
)
|
606
|
+
|
607
|
+
self.at_word_boundary = previous_word_interval_id != following_word_interval_id
|
608
|
+
if self.at_word_boundary:
|
609
|
+
for wi in getattr(self.utterance, self.word_interval_lookup):
|
610
|
+
if self.first_word_interval is not None and self.second_word_interval is not None:
|
611
|
+
break
|
612
|
+
if wi.id == previous_word_interval_id:
|
613
|
+
self.first_word_interval = wi
|
614
|
+
if wi.id == following_word_interval_id:
|
615
|
+
self.second_word_interval = wi
|
616
|
+
elif not self.has_previous:
|
617
|
+
for wi in getattr(self.utterance, self.word_interval_lookup):
|
618
|
+
if wi.id == following_word_interval_id:
|
619
|
+
self.second_word_interval = wi
|
620
|
+
break
|
621
|
+
elif not self.has_following:
|
622
|
+
for wi in getattr(self.utterance, self.word_interval_lookup):
|
623
|
+
if wi.id == previous_word_interval_id:
|
624
|
+
self.first_word_interval = wi
|
625
|
+
break
|
626
|
+
self.first_word_interval_end = None
|
627
|
+
if self.first_word_interval is not None:
|
628
|
+
self.first_word_interval_end = self.first_word_interval.end
|
629
|
+
self.second_word_interval_begin = None
|
630
|
+
if self.second_word_interval is not None:
|
631
|
+
self.second_word_interval_begin = self.second_word_interval.begin
|
632
|
+
self.speaker_id = utterance.speaker_id
|
633
|
+
self.setText(
|
634
|
+
QtCore.QCoreApplication.translate(
|
635
|
+
"DeletePhoneIntervalCommand", "Delete phone interval"
|
636
|
+
)
|
637
|
+
)
|
638
|
+
|
639
|
+
def _redo(self, session) -> None:
|
640
|
+
if self.has_previous and self.has_following:
|
641
|
+
new_time = (
|
642
|
+
self.new_time
|
643
|
+
if self.new_time is not None
|
644
|
+
else (self.phone_interval.begin + self.phone_interval.end) / 2
|
645
|
+
)
|
646
|
+
self.previous_phone_interval.end = new_time
|
647
|
+
self.following_phone_interval.begin = new_time
|
648
|
+
if self.at_word_boundary:
|
649
|
+
self.first_word_interval.end = new_time
|
650
|
+
self.second_word_interval.begin = new_time
|
651
|
+
session.merge(self.first_word_interval)
|
652
|
+
session.merge(self.second_word_interval)
|
653
|
+
elif self.has_following:
|
654
|
+
self.following_phone_interval.begin = self.phone_interval.begin
|
655
|
+
self.second_word_interval.begin = self.phone_interval.begin
|
656
|
+
session.merge(self.second_word_interval)
|
657
|
+
elif self.has_previous:
|
658
|
+
self.previous_phone_interval.end = self.phone_interval.end
|
659
|
+
self.first_word_interval.end = self.phone_interval.end
|
660
|
+
session.merge(self.first_word_interval)
|
661
|
+
if self.has_previous:
|
662
|
+
session.merge(self.previous_phone_interval)
|
663
|
+
if self.has_following:
|
664
|
+
session.merge(self.following_phone_interval)
|
665
|
+
|
666
|
+
phone_intervals = []
|
667
|
+
for pi in getattr(self.utterance, self.phone_interval_lookup):
|
668
|
+
if pi.id == self.phone_interval.id:
|
669
|
+
continue
|
670
|
+
session.merge(pi)
|
671
|
+
phone_intervals.append(pi)
|
672
|
+
setattr(self.utterance, self.phone_interval_lookup, phone_intervals)
|
673
|
+
if self.single_phone_word:
|
674
|
+
word_intervals = []
|
675
|
+
for wi in getattr(self.utterance, self.word_interval_lookup):
|
676
|
+
if wi.id == self.word_interval.id:
|
677
|
+
continue
|
678
|
+
session.merge(wi)
|
679
|
+
word_intervals.append(wi)
|
680
|
+
setattr(self.utterance, self.word_interval_lookup, word_intervals)
|
681
|
+
if not self.old_manual_alignments:
|
682
|
+
self.utterance.manual_alignments = True
|
683
|
+
session.merge(self.utterance)
|
684
|
+
|
685
|
+
def _undo(self, session) -> None:
|
686
|
+
if self.single_phone_word:
|
687
|
+
word_intervals = []
|
688
|
+
for wi in getattr(self.utterance, self.word_interval_lookup):
|
689
|
+
session.merge(wi)
|
690
|
+
word_intervals.append(wi)
|
691
|
+
make_transient(self.word_interval)
|
692
|
+
word_intervals.append(self.word_interval)
|
693
|
+
setattr(
|
694
|
+
self.utterance,
|
695
|
+
self.word_interval_lookup,
|
696
|
+
sorted(word_intervals, key=lambda x: x.begin),
|
697
|
+
)
|
698
|
+
phone_intervals = []
|
699
|
+
for pi in getattr(self.utterance, self.phone_interval_lookup):
|
700
|
+
session.merge(pi)
|
701
|
+
phone_intervals.append(pi)
|
702
|
+
make_transient(self.phone_interval)
|
703
|
+
phone_intervals.append(self.phone_interval)
|
704
|
+
setattr(
|
705
|
+
self.utterance,
|
706
|
+
self.phone_interval_lookup,
|
707
|
+
sorted(phone_intervals, key=lambda x: x.begin),
|
708
|
+
)
|
709
|
+
session.merge(self.utterance)
|
710
|
+
|
711
|
+
if not self.old_manual_alignments:
|
712
|
+
self.utterance.manual_alignments = self.old_manual_alignments
|
713
|
+
if self.has_previous:
|
714
|
+
self.previous_phone_interval.end = self.phone_interval.begin
|
715
|
+
session.merge(self.previous_phone_interval)
|
716
|
+
if self.has_following:
|
717
|
+
self.following_phone_interval.begin = self.phone_interval.end
|
718
|
+
session.merge(self.following_phone_interval)
|
719
|
+
if self.second_word_interval_begin is not None:
|
720
|
+
self.second_word_interval.begin = self.second_word_interval_begin
|
721
|
+
session.merge(self.second_word_interval)
|
722
|
+
if self.first_word_interval_end is not None:
|
723
|
+
self.first_word_interval.end = self.first_word_interval_end
|
724
|
+
session.merge(self.first_word_interval)
|
725
|
+
|
726
|
+
def update_data(self):
|
727
|
+
super().update_data()
|
728
|
+
self.corpus_model.changeCommandFired.emit()
|
729
|
+
self.file_model.phoneTierChanged.emit(self.utterance)
|
730
|
+
|
731
|
+
|
732
|
+
class InsertPhoneIntervalCommand(FileCommand):
|
733
|
+
def __init__(
|
734
|
+
self,
|
735
|
+
utterance: Utterance,
|
736
|
+
phone_interval: PhoneInterval,
|
737
|
+
previous_phone_interval: typing.Optional[PhoneInterval],
|
738
|
+
following_phone_interval: typing.Optional[PhoneInterval],
|
739
|
+
file_model: FileUtterancesModel,
|
740
|
+
word_interval: WordInterval = None,
|
741
|
+
):
|
742
|
+
super().__init__(file_model)
|
743
|
+
self.word_interval_lookup = "word_intervals"
|
744
|
+
self.phone_interval_lookup = "phone_intervals"
|
745
|
+
self.using_reference = not isinstance(phone_interval, PhoneInterval)
|
746
|
+
if self.using_reference:
|
747
|
+
self.word_interval_lookup = "reference_word_intervals"
|
748
|
+
self.phone_interval_lookup = "reference_phone_intervals"
|
749
|
+
self.utterance = utterance
|
750
|
+
self.old_manual_alignments = utterance.manual_alignments
|
751
|
+
self.phone_interval = phone_interval
|
752
|
+
self.previous_phone_interval = previous_phone_interval
|
753
|
+
self.has_previous = previous_phone_interval is not None
|
754
|
+
self.has_following = following_phone_interval is not None
|
755
|
+
self.following_phone_interval = following_phone_interval
|
756
|
+
self.word_interval = word_interval
|
757
|
+
self.previous_word_interval_id = (
|
758
|
+
previous_phone_interval.word_interval_id if self.has_previous else None
|
759
|
+
)
|
760
|
+
self.following_word_interval_id = (
|
761
|
+
following_phone_interval.word_interval_id if self.has_following else None
|
762
|
+
)
|
763
|
+
self.initial_word_boundary = (
|
764
|
+
self.has_previous
|
765
|
+
and self.previous_word_interval_id != self.phone_interval.word_interval_id
|
766
|
+
)
|
767
|
+
self.final_word_boundary = (
|
768
|
+
self.has_following
|
769
|
+
and self.following_word_interval_id != self.phone_interval.word_interval_id
|
770
|
+
)
|
771
|
+
|
772
|
+
self.old_time_boundary = (
|
773
|
+
self.previous_phone_interval.end
|
774
|
+
if self.has_previous
|
775
|
+
else self.following_phone_interval.begin
|
776
|
+
)
|
777
|
+
self.previous_word_interval_end = None
|
778
|
+
self.following_word_interval_begin = None
|
779
|
+
for wi in getattr(self.utterance, self.word_interval_lookup):
|
780
|
+
if (
|
781
|
+
self.previous_word_interval_id is not None
|
782
|
+
and wi.id == self.previous_word_interval_id
|
783
|
+
):
|
784
|
+
self.previous_word_interval_end = wi.end
|
785
|
+
elif (
|
786
|
+
self.following_word_interval_id is not None
|
787
|
+
and wi.id == self.following_word_interval_id
|
788
|
+
):
|
789
|
+
self.following_word_interval_begin = wi.begin
|
790
|
+
self.speaker_id = utterance.speaker_id
|
791
|
+
self.setText(
|
792
|
+
QtCore.QCoreApplication.translate(
|
793
|
+
"InsertPhoneIntervalCommand", "Insert phone interval"
|
794
|
+
)
|
795
|
+
)
|
796
|
+
|
797
|
+
def _redo(self, session) -> None:
|
798
|
+
word_intervals = []
|
799
|
+
if self.word_interval is not None:
|
800
|
+
for wi in getattr(self.utterance, self.word_interval_lookup):
|
801
|
+
session.merge(wi)
|
802
|
+
if wi.id == self.previous_word_interval_id:
|
803
|
+
wi.end = self.phone_interval.begin
|
804
|
+
if wi.id == self.following_word_interval_id:
|
805
|
+
wi.begin = self.phone_interval.end
|
806
|
+
word_intervals.append(wi)
|
807
|
+
make_transient(self.word_interval)
|
808
|
+
word_intervals.append(self.word_interval)
|
809
|
+
else:
|
810
|
+
for wi in getattr(self.utterance, self.word_interval_lookup):
|
811
|
+
session.merge(wi)
|
812
|
+
if self.initial_word_boundary:
|
813
|
+
if wi.id == self.previous_word_interval_id:
|
814
|
+
wi.end = self.phone_interval.begin
|
815
|
+
elif wi.id == self.phone_interval.word_interval_id:
|
816
|
+
wi.begin = self.phone_interval.begin
|
817
|
+
if self.final_word_boundary:
|
818
|
+
if wi.id == self.following_word_interval_id:
|
819
|
+
wi.begin = self.phone_interval.end
|
820
|
+
elif wi.id == self.phone_interval.word_interval_id:
|
821
|
+
wi.end = self.phone_interval.end
|
822
|
+
word_intervals.append(wi)
|
823
|
+
setattr(
|
824
|
+
self.utterance,
|
825
|
+
self.word_interval_lookup,
|
826
|
+
sorted(word_intervals, key=lambda x: x.begin),
|
827
|
+
)
|
828
|
+
phone_intervals = []
|
829
|
+
for pi in getattr(self.utterance, self.phone_interval_lookup):
|
830
|
+
session.merge(pi)
|
831
|
+
if self.has_previous and pi.id == self.previous_phone_interval.id:
|
832
|
+
pi.end = self.phone_interval.begin
|
833
|
+
if self.has_following and pi.id == self.following_phone_interval.id:
|
834
|
+
pi.begin = self.phone_interval.end
|
835
|
+
phone_intervals.append(pi)
|
836
|
+
make_transient(self.phone_interval)
|
837
|
+
self.phone_interval.utterance_id = self.utterance.id
|
838
|
+
phone_intervals.append(self.phone_interval)
|
839
|
+
|
840
|
+
setattr(
|
841
|
+
self.utterance,
|
842
|
+
self.phone_interval_lookup,
|
843
|
+
sorted(phone_intervals, key=lambda x: x.begin),
|
844
|
+
)
|
845
|
+
if not self.old_manual_alignments:
|
846
|
+
self.utterance.manual_alignments = True
|
847
|
+
session.merge(self.utterance)
|
848
|
+
|
849
|
+
def _undo(self, session) -> None:
|
850
|
+
phone_intervals = []
|
851
|
+
for pi in getattr(self.utterance, self.phone_interval_lookup):
|
852
|
+
if pi.id == self.phone_interval.id:
|
853
|
+
continue
|
854
|
+
session.merge(pi)
|
855
|
+
if self.has_previous and pi.id == self.previous_phone_interval.id:
|
856
|
+
pi.end = self.old_time_boundary
|
857
|
+
if self.has_following and pi.id == self.following_phone_interval.id:
|
858
|
+
pi.begin = self.old_time_boundary
|
859
|
+
phone_intervals.append(pi)
|
860
|
+
setattr(self.utterance, self.phone_interval_lookup, phone_intervals)
|
861
|
+
word_intervals = []
|
862
|
+
if self.word_interval is not None:
|
863
|
+
for wi in getattr(self.utterance, self.word_interval_lookup):
|
864
|
+
if wi.id == self.word_interval.id:
|
865
|
+
continue
|
866
|
+
session.merge(wi)
|
867
|
+
word_intervals.append(wi)
|
868
|
+
else:
|
869
|
+
for wi in getattr(self.utterance, self.word_interval_lookup):
|
870
|
+
session.merge(wi)
|
871
|
+
if self.initial_word_boundary:
|
872
|
+
if wi.id == self.previous_word_interval_id:
|
873
|
+
wi.end = self.previous_word_interval_end
|
874
|
+
elif wi.id == self.phone_interval.word_interval_id:
|
875
|
+
wi.begin = self.previous_word_interval_end
|
876
|
+
if self.final_word_boundary:
|
877
|
+
if wi.id == self.following_word_interval_id:
|
878
|
+
wi.begin = self.following_word_interval_begin
|
879
|
+
elif wi.id == self.phone_interval.word_interval_id:
|
880
|
+
wi.end = self.following_word_interval_begin
|
881
|
+
word_intervals.append(wi)
|
882
|
+
setattr(self.utterance, self.word_interval_lookup, word_intervals)
|
883
|
+
session.merge(self.utterance)
|
884
|
+
if not self.old_manual_alignments:
|
885
|
+
self.utterance.manual_alignments = self.old_manual_alignments
|
886
|
+
|
887
|
+
def update_data(self):
|
888
|
+
super().update_data()
|
889
|
+
self.corpus_model.changeCommandFired.emit()
|
890
|
+
self.file_model.phoneTierChanged.emit(self.utterance)
|
891
|
+
|
892
|
+
|
893
|
+
class UpdateWordIntervalPronunciationCommand(FileCommand):
|
894
|
+
def __init__(
|
895
|
+
self,
|
896
|
+
utterance: Utterance,
|
897
|
+
word_interval: WordInterval,
|
898
|
+
pronunciation: Pronunciation,
|
899
|
+
file_model: FileUtterancesModel,
|
900
|
+
):
|
901
|
+
super().__init__(file_model)
|
902
|
+
self.utterance = utterance
|
903
|
+
self.word_interval = word_interval
|
904
|
+
self.old_pronunciation_id = self.word_interval.pronunciation_id
|
905
|
+
self.new_pronunciation = pronunciation
|
906
|
+
self.old_phone_intervals = None
|
907
|
+
self.new_phone_intervals = None
|
908
|
+
|
909
|
+
self.setText(
|
910
|
+
QtCore.QCoreApplication.translate(
|
911
|
+
"UpdateWordIntervalPronunciationCommand", "Update pronunciation for word interval"
|
912
|
+
)
|
913
|
+
)
|
914
|
+
|
915
|
+
def _redo(self, session) -> None:
|
916
|
+
phone_intervals = []
|
917
|
+
word_intervals = []
|
918
|
+
for pi in self.utterance.phone_intervals:
|
919
|
+
if pi.word_interval_id != self.word_interval.id:
|
920
|
+
session.merge(pi)
|
921
|
+
phone_intervals.append(pi)
|
922
|
+
elif self.old_phone_intervals is None:
|
923
|
+
word_intervals.append(pi)
|
924
|
+
if self.old_phone_intervals is None:
|
925
|
+
self.old_phone_intervals = word_intervals
|
926
|
+
if self.new_phone_intervals is None:
|
927
|
+
self.new_phone_intervals = []
|
928
|
+
new_phones = self.new_pronunciation.pronunciation.split()
|
929
|
+
|
930
|
+
next_pk = session.query(sqlalchemy.func.max(PhoneInterval.id)).scalar() + 1
|
931
|
+
begin = self.word_interval.begin
|
932
|
+
phone_duration = (self.word_interval.end - self.word_interval.begin) / len(new_phones)
|
933
|
+
for p in new_phones:
|
934
|
+
end = begin + phone_duration
|
935
|
+
pi = PhoneInterval(
|
936
|
+
id=next_pk,
|
937
|
+
begin=begin,
|
938
|
+
end=end,
|
939
|
+
phone=self.corpus_model.phones[p],
|
940
|
+
word_interval=self.word_interval,
|
941
|
+
word_interval_id=self.word_interval.id,
|
942
|
+
)
|
943
|
+
self.new_phone_intervals.append(pi)
|
944
|
+
begin = end
|
945
|
+
next_pk += 1
|
946
|
+
self.new_phone_intervals[-1].end = self.word_interval.end
|
947
|
+
for pi in self.new_phone_intervals:
|
948
|
+
make_transient(pi)
|
949
|
+
phone_intervals.append(pi)
|
950
|
+
self.utterance.phone_intervals = sorted(phone_intervals, key=lambda x: x.begin)
|
951
|
+
|
952
|
+
session.merge(self.utterance)
|
953
|
+
self.word_interval.pronunciation_id = self.new_pronunciation.id
|
954
|
+
session.merge(self.word_interval)
|
955
|
+
|
956
|
+
def _undo(self, session) -> None:
|
957
|
+
phone_intervals = []
|
958
|
+
for pi in self.utterance.phone_intervals:
|
959
|
+
if pi.word_interval_id != self.word_interval.id:
|
960
|
+
session.merge(pi)
|
961
|
+
phone_intervals.append(pi)
|
962
|
+
for pi in self.old_phone_intervals:
|
963
|
+
make_transient(pi)
|
964
|
+
phone_intervals.append(pi)
|
965
|
+
|
966
|
+
self.utterance.phone_intervals = sorted(phone_intervals, key=lambda x: x.begin)
|
967
|
+
|
968
|
+
session.merge(self.utterance)
|
969
|
+
self.word_interval.pronunciation_id = self.old_pronunciation_id
|
970
|
+
session.merge(self.word_interval)
|
971
|
+
|
972
|
+
def update_data(self):
|
973
|
+
super().update_data()
|
974
|
+
self.corpus_model.changeCommandFired.emit()
|
975
|
+
self.file_model.phoneTierChanged.emit(self.utterance)
|
976
|
+
|
977
|
+
|
978
|
+
class UpdateWordIntervalWordCommand(FileCommand):
|
979
|
+
def __init__(
|
980
|
+
self,
|
981
|
+
utterance: Utterance,
|
982
|
+
word_interval: WordInterval,
|
983
|
+
word: typing.Union[Word, str],
|
984
|
+
file_model: FileUtterancesModel,
|
985
|
+
):
|
986
|
+
super().__init__(file_model)
|
987
|
+
self.utterance = utterance
|
988
|
+
self.word_interval = word_interval
|
989
|
+
self.old_word = self.word_interval.word
|
990
|
+
self.new_word = word
|
991
|
+
self.need_words_refreshed = isinstance(word, str)
|
992
|
+
|
993
|
+
self.setText(
|
994
|
+
QtCore.QCoreApplication.translate(
|
995
|
+
"UpdateWordIntervalPronunciationCommand", "Update pronunciation for word interval"
|
996
|
+
)
|
997
|
+
)
|
998
|
+
|
999
|
+
def _redo(self, session) -> None:
|
1000
|
+
session.merge(self.utterance)
|
1001
|
+
if isinstance(self.new_word, str):
|
1002
|
+
max_id, max_mapping_id = session.query(
|
1003
|
+
sqlalchemy.func.max(Word.id), sqlalchemy.func.max(Word.mapping_id)
|
1004
|
+
).first()
|
1005
|
+
dictionary_id = self.utterance.speaker.dictionary_id
|
1006
|
+
if not dictionary_id:
|
1007
|
+
dictionary_id = 1
|
1008
|
+
|
1009
|
+
self.new_word = Word(
|
1010
|
+
id=max_id + 1,
|
1011
|
+
mapping_id=max_mapping_id + 1,
|
1012
|
+
word=self.new_word,
|
1013
|
+
count=1,
|
1014
|
+
dictionary_id=dictionary_id,
|
1015
|
+
word_type=WordType.speech,
|
1016
|
+
)
|
1017
|
+
session.merge(self.new_word)
|
1018
|
+
|
1019
|
+
self.word_interval.word = self.new_word
|
1020
|
+
self.word_interval.word_id = self.new_word.id
|
1021
|
+
session.merge(self.word_interval)
|
1022
|
+
|
1023
|
+
def _undo(self, session) -> None:
|
1024
|
+
session.merge(self.utterance)
|
1025
|
+
self.word_interval.word = self.old_word
|
1026
|
+
self.word_interval.word_id = self.old_word.id
|
1027
|
+
session.merge(self.word_interval)
|
1028
|
+
|
1029
|
+
def update_data(self):
|
1030
|
+
super().update_data()
|
1031
|
+
self.corpus_model.changeCommandFired.emit()
|
1032
|
+
self.file_model.phoneTierChanged.emit(self.utterance)
|
1033
|
+
if self.need_words_refreshed:
|
1034
|
+
self.corpus_model.refresh_words()
|
1035
|
+
self.need_words_refreshed = False
|
1036
|
+
|
1037
|
+
|
1038
|
+
class UpdatePhoneIntervalCommand(FileCommand):
|
1039
|
+
def __init__(
|
1040
|
+
self,
|
1041
|
+
utterance: Utterance,
|
1042
|
+
phone_interval: PhoneInterval,
|
1043
|
+
new_phone: Phone,
|
1044
|
+
file_model: FileUtterancesModel,
|
1045
|
+
):
|
1046
|
+
super().__init__(file_model)
|
1047
|
+
self.utterance = utterance
|
1048
|
+
self.old_manual_alignments = utterance.manual_alignments
|
1049
|
+
self.phone_interval = phone_interval
|
1050
|
+
self.old_phone = self.phone_interval.phone
|
1051
|
+
self.new_phone = new_phone
|
1052
|
+
self.setText(
|
1053
|
+
QtCore.QCoreApplication.translate(
|
1054
|
+
"UpdatePhoneIntervalCommand", "Update phone interval"
|
1055
|
+
)
|
1056
|
+
)
|
1057
|
+
|
1058
|
+
def _redo(self, session) -> None:
|
1059
|
+
if not self.old_manual_alignments:
|
1060
|
+
self.utterance.manual_alignments = True
|
1061
|
+
session.merge(self.utterance)
|
1062
|
+
self.phone_interval.phone = self.new_phone
|
1063
|
+
self.phone_interval.phone_id = self.new_phone.id
|
1064
|
+
session.merge(self.phone_interval)
|
1065
|
+
|
1066
|
+
def _undo(self, session) -> None:
|
1067
|
+
if not self.old_manual_alignments:
|
1068
|
+
self.utterance.manual_alignments = self.old_manual_alignments
|
1069
|
+
session.merge(self.utterance)
|
1070
|
+
self.phone_interval.phone = self.old_phone
|
1071
|
+
self.phone_interval.phone_id = self.old_phone.id
|
1072
|
+
session.merge(self.phone_interval)
|
1073
|
+
|
1074
|
+
def update_data(self):
|
1075
|
+
super().update_data()
|
1076
|
+
self.corpus_model.changeCommandFired.emit()
|
1077
|
+
self.file_model.phoneTierChanged.emit(self.utterance)
|
1078
|
+
|
1079
|
+
|
419
1080
|
class UpdateUtteranceTextCommand(FileCommand):
|
420
1081
|
def __init__(self, utterance: Utterance, new_text: str, file_model: FileUtterancesModel):
|
421
1082
|
super().__init__(file_model)
|