Anchor-annotator 0.8.2__py3-none-any.whl → 0.9.1__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/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 File, Pronunciation, Speaker, Utterance, Word, bulk_update
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 _redo(self, session) -> None:
398
- self.utterance.begin = self.new_begin
399
- self.utterance.end = self.new_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.utterance.begin = self.old_begin
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,650 @@ 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_word_intervals = None
452
+ self.reference_workflow = None
453
+
454
+ def _redo(self, session) -> None:
455
+ if self.reference_intervals is None:
456
+ self.reference_intervals = self.utterance.reference_phone_intervals
457
+ self.reference_word_intervals = self.utterance.reference_word_intervals
458
+ self.utterance.reference_phone_intervals = []
459
+ self.utterance.reference_word_intervals = []
460
+ self.utterance.manual_alignments = False
461
+ session.merge(self.utterance)
462
+
463
+ def _undo(self, session) -> None:
464
+ reference_phone_intervals = []
465
+ for pi in self.reference_intervals:
466
+ make_transient(pi)
467
+ reference_phone_intervals.append(pi)
468
+ reference_word_intervals = []
469
+ for wi in self.reference_word_intervals:
470
+ make_transient(wi)
471
+ reference_word_intervals.append(wi)
472
+ self.utterance.manual_alignments = True
473
+ self.utterance.reference_phone_intervals = sorted(
474
+ reference_phone_intervals, key=lambda x: x.begin
475
+ )
476
+ self.utterance.reference_word_intervals = sorted(
477
+ reference_word_intervals, key=lambda x: x.begin
478
+ )
479
+ session.merge(self.utterance)
480
+
481
+ def update_data(self):
482
+ super().update_data()
483
+ self.corpus_model.changeCommandFired.emit()
484
+ self.file_model.phoneTierChanged.emit(self.utterance)
485
+
486
+
487
+ class UpdatePhoneBoundariesCommand(FileCommand):
488
+ def __init__(
489
+ self,
490
+ utterance: Utterance,
491
+ first_phone_interval: PhoneInterval,
492
+ second_phone_interval: PhoneInterval,
493
+ new_time: float,
494
+ file_model: FileUtterancesModel,
495
+ ):
496
+ super().__init__(file_model)
497
+ self.utterance = utterance
498
+ self.old_manual_alignments = utterance.manual_alignments
499
+ self.first_phone_interval = first_phone_interval
500
+ self.second_phone_interval = second_phone_interval
501
+ self.first_word_interval = None
502
+ self.second_word_interval = None
503
+ self.at_word_boundary = (
504
+ self.first_phone_interval.word_interval_id
505
+ != self.second_phone_interval.word_interval_id
506
+ )
507
+ if True or self.at_word_boundary:
508
+ if isinstance(self.first_phone_interval, PhoneInterval):
509
+ word_intervals = utterance.word_intervals
510
+ else:
511
+ word_intervals = utterance.reference_word_intervals
512
+ for wi in word_intervals:
513
+ if self.first_word_interval is not None and self.second_word_interval is not None:
514
+ break
515
+ if wi.id == first_phone_interval.word_interval_id:
516
+ self.first_word_interval = wi
517
+ elif wi.id == second_phone_interval.word_interval_id:
518
+ self.second_word_interval = wi
519
+ self.speaker_id = utterance.speaker_id
520
+ self.old_time = second_phone_interval.begin
521
+ self.new_time = new_time
522
+ self.setText(
523
+ QtCore.QCoreApplication.translate(
524
+ "UpdatePhoneBoundariesCommand", "Update phone boundaries"
525
+ )
526
+ )
527
+
528
+ def _set_time(self, session, new_time, manual_alignments):
529
+ if not self.old_manual_alignments:
530
+ self.utterance.manual_alignments = manual_alignments
531
+ session.merge(self.utterance)
532
+ self.first_phone_interval.end = new_time
533
+ self.second_phone_interval.begin = new_time
534
+ session.merge(self.utterance)
535
+ session.merge(self.first_phone_interval)
536
+ session.merge(self.second_phone_interval)
537
+ if self.at_word_boundary:
538
+ if self.first_word_interval is not None:
539
+ self.first_word_interval.end = new_time
540
+ session.merge(self.first_word_interval)
541
+ if self.second_word_interval is not None:
542
+ self.second_word_interval.begin = new_time
543
+ session.merge(self.second_word_interval)
544
+
545
+ def _redo(self, session) -> None:
546
+ self._set_time(session, self.new_time, True)
547
+
548
+ def _undo(self, session) -> None:
549
+ self._set_time(session, self.old_time, self.old_manual_alignments)
550
+
551
+ def id(self) -> int:
552
+ return 2
553
+
554
+ def mergeWith(self, other: UpdatePhoneBoundariesCommand) -> bool:
555
+ if (
556
+ other.id() != self.id()
557
+ or other.first_phone_interval.id != self.first_phone_interval.id
558
+ or other.second_phone_interval.id != self.second_phone_interval.id
559
+ ):
560
+ return False
561
+ self.new_time = other.new_time
562
+ return True
563
+
564
+ def update_data(self):
565
+ super().update_data()
566
+ self.corpus_model.changeCommandFired.emit()
567
+
568
+
569
+ class DeletePhoneIntervalCommand(FileCommand):
570
+ def __init__(
571
+ self,
572
+ utterance: Utterance,
573
+ phone_interval: PhoneInterval,
574
+ previous_phone_interval: typing.Optional[PhoneInterval],
575
+ following_phone_interval: typing.Optional[PhoneInterval],
576
+ time_point: typing.Optional[float],
577
+ file_model: FileUtterancesModel,
578
+ ):
579
+ super().__init__(file_model)
580
+ self.word_interval_lookup = "word_intervals"
581
+ self.phone_interval_lookup = "phone_intervals"
582
+ self.using_reference = not isinstance(phone_interval, PhoneInterval)
583
+ if self.using_reference:
584
+ self.word_interval_lookup = "reference_word_intervals"
585
+ self.phone_interval_lookup = "reference_phone_intervals"
586
+ self.utterance = utterance
587
+ self.old_manual_alignments = utterance.manual_alignments
588
+ self.phone_interval = phone_interval
589
+ self.previous_phone_interval = previous_phone_interval
590
+ self.has_previous = previous_phone_interval is not None
591
+ self.has_following = following_phone_interval is not None
592
+ self.following_phone_interval = following_phone_interval
593
+ self.new_time = time_point
594
+ self.first_word_interval = None
595
+ self.second_word_interval = None
596
+ previous_word_interval_id = (
597
+ self.previous_phone_interval.word_interval_id
598
+ if self.previous_phone_interval is not None
599
+ else None
600
+ )
601
+ word_interval_id = self.phone_interval.word_interval_id
602
+ following_word_interval_id = (
603
+ self.following_phone_interval.word_interval_id
604
+ if self.following_phone_interval is not None
605
+ else None
606
+ )
607
+ self.word_interval = None
608
+ for wi in getattr(self.utterance, self.word_interval_lookup):
609
+ if wi.id == self.phone_interval.word_interval_id:
610
+ self.word_interval = wi
611
+ break
612
+ self.single_phone_word = (
613
+ word_interval_id != previous_word_interval_id
614
+ and word_interval_id != following_word_interval_id
615
+ )
616
+
617
+ self.at_word_boundary = previous_word_interval_id != following_word_interval_id
618
+ if self.at_word_boundary:
619
+ for wi in getattr(self.utterance, self.word_interval_lookup):
620
+ if self.first_word_interval is not None and self.second_word_interval is not None:
621
+ break
622
+ if wi.id == previous_word_interval_id:
623
+ self.first_word_interval = wi
624
+ if wi.id == following_word_interval_id:
625
+ self.second_word_interval = wi
626
+ elif not self.has_previous:
627
+ for wi in getattr(self.utterance, self.word_interval_lookup):
628
+ if wi.id == following_word_interval_id:
629
+ self.second_word_interval = wi
630
+ break
631
+ elif not self.has_following:
632
+ for wi in getattr(self.utterance, self.word_interval_lookup):
633
+ if wi.id == previous_word_interval_id:
634
+ self.first_word_interval = wi
635
+ break
636
+ self.first_word_interval_end = None
637
+ if self.first_word_interval is not None:
638
+ self.first_word_interval_end = self.first_word_interval.end
639
+ self.second_word_interval_begin = None
640
+ if self.second_word_interval is not None:
641
+ self.second_word_interval_begin = self.second_word_interval.begin
642
+ self.speaker_id = utterance.speaker_id
643
+ self.setText(
644
+ QtCore.QCoreApplication.translate(
645
+ "DeletePhoneIntervalCommand", "Delete phone interval"
646
+ )
647
+ )
648
+
649
+ def _redo(self, session) -> None:
650
+ if self.has_previous and self.has_following:
651
+ new_time = (
652
+ self.new_time
653
+ if self.new_time is not None
654
+ else (self.phone_interval.begin + self.phone_interval.end) / 2
655
+ )
656
+ self.previous_phone_interval.end = new_time
657
+ self.following_phone_interval.begin = new_time
658
+ if self.at_word_boundary:
659
+ self.first_word_interval.end = new_time
660
+ self.second_word_interval.begin = new_time
661
+ session.merge(self.first_word_interval)
662
+ session.merge(self.second_word_interval)
663
+ elif self.has_following:
664
+ self.following_phone_interval.begin = self.phone_interval.begin
665
+ self.second_word_interval.begin = self.phone_interval.begin
666
+ session.merge(self.second_word_interval)
667
+ elif self.has_previous:
668
+ self.previous_phone_interval.end = self.phone_interval.end
669
+ self.first_word_interval.end = self.phone_interval.end
670
+ session.merge(self.first_word_interval)
671
+ if self.has_previous:
672
+ session.merge(self.previous_phone_interval)
673
+ if self.has_following:
674
+ session.merge(self.following_phone_interval)
675
+
676
+ phone_intervals = []
677
+ for pi in getattr(self.utterance, self.phone_interval_lookup):
678
+ if pi.id == self.phone_interval.id:
679
+ continue
680
+ session.merge(pi)
681
+ phone_intervals.append(pi)
682
+ setattr(self.utterance, self.phone_interval_lookup, phone_intervals)
683
+ if self.single_phone_word:
684
+ word_intervals = []
685
+ for wi in getattr(self.utterance, self.word_interval_lookup):
686
+ if wi.id == self.word_interval.id:
687
+ continue
688
+ session.merge(wi)
689
+ word_intervals.append(wi)
690
+ setattr(self.utterance, self.word_interval_lookup, word_intervals)
691
+ if not self.old_manual_alignments:
692
+ self.utterance.manual_alignments = True
693
+ session.merge(self.utterance)
694
+
695
+ def _undo(self, session) -> None:
696
+ if self.single_phone_word:
697
+ word_intervals = []
698
+ for wi in getattr(self.utterance, self.word_interval_lookup):
699
+ session.merge(wi)
700
+ word_intervals.append(wi)
701
+ make_transient(self.word_interval)
702
+ word_intervals.append(self.word_interval)
703
+ setattr(
704
+ self.utterance,
705
+ self.word_interval_lookup,
706
+ sorted(word_intervals, key=lambda x: x.begin),
707
+ )
708
+ phone_intervals = []
709
+ for pi in getattr(self.utterance, self.phone_interval_lookup):
710
+ session.merge(pi)
711
+ phone_intervals.append(pi)
712
+ make_transient(self.phone_interval)
713
+ phone_intervals.append(self.phone_interval)
714
+ setattr(
715
+ self.utterance,
716
+ self.phone_interval_lookup,
717
+ sorted(phone_intervals, key=lambda x: x.begin),
718
+ )
719
+ session.merge(self.utterance)
720
+
721
+ if not self.old_manual_alignments:
722
+ self.utterance.manual_alignments = self.old_manual_alignments
723
+ if self.has_previous:
724
+ self.previous_phone_interval.end = self.phone_interval.begin
725
+ session.merge(self.previous_phone_interval)
726
+ if self.has_following:
727
+ self.following_phone_interval.begin = self.phone_interval.end
728
+ session.merge(self.following_phone_interval)
729
+ if self.second_word_interval_begin is not None:
730
+ self.second_word_interval.begin = self.second_word_interval_begin
731
+ session.merge(self.second_word_interval)
732
+ if self.first_word_interval_end is not None:
733
+ self.first_word_interval.end = self.first_word_interval_end
734
+ session.merge(self.first_word_interval)
735
+
736
+ def update_data(self):
737
+ super().update_data()
738
+ self.corpus_model.changeCommandFired.emit()
739
+ self.file_model.phoneTierChanged.emit(self.utterance)
740
+
741
+
742
+ class InsertPhoneIntervalCommand(FileCommand):
743
+ def __init__(
744
+ self,
745
+ utterance: Utterance,
746
+ phone_interval: PhoneInterval,
747
+ previous_phone_interval: typing.Optional[PhoneInterval],
748
+ following_phone_interval: typing.Optional[PhoneInterval],
749
+ file_model: FileUtterancesModel,
750
+ word_interval: WordInterval = None,
751
+ ):
752
+ super().__init__(file_model)
753
+ self.word_interval_lookup = "word_intervals"
754
+ self.phone_interval_lookup = "phone_intervals"
755
+ self.using_reference = not isinstance(phone_interval, PhoneInterval)
756
+ if self.using_reference:
757
+ self.word_interval_lookup = "reference_word_intervals"
758
+ self.phone_interval_lookup = "reference_phone_intervals"
759
+ self.utterance = utterance
760
+ self.old_manual_alignments = utterance.manual_alignments
761
+ self.phone_interval = phone_interval
762
+ self.previous_phone_interval = previous_phone_interval
763
+ self.has_previous = previous_phone_interval is not None
764
+ self.has_following = following_phone_interval is not None
765
+ self.following_phone_interval = following_phone_interval
766
+ self.word_interval = word_interval
767
+ self.previous_word_interval_id = (
768
+ previous_phone_interval.word_interval_id if self.has_previous else None
769
+ )
770
+ self.following_word_interval_id = (
771
+ following_phone_interval.word_interval_id if self.has_following else None
772
+ )
773
+ self.initial_word_boundary = (
774
+ self.has_previous
775
+ and self.previous_word_interval_id != self.phone_interval.word_interval_id
776
+ )
777
+ self.final_word_boundary = (
778
+ self.has_following
779
+ and self.following_word_interval_id != self.phone_interval.word_interval_id
780
+ )
781
+
782
+ self.old_time_boundary = (
783
+ self.previous_phone_interval.end
784
+ if self.has_previous
785
+ else self.following_phone_interval.begin
786
+ )
787
+ self.previous_word_interval_end = None
788
+ self.following_word_interval_begin = None
789
+ for wi in getattr(self.utterance, self.word_interval_lookup):
790
+ if (
791
+ self.previous_word_interval_id is not None
792
+ and wi.id == self.previous_word_interval_id
793
+ ):
794
+ self.previous_word_interval_end = wi.end
795
+ elif (
796
+ self.following_word_interval_id is not None
797
+ and wi.id == self.following_word_interval_id
798
+ ):
799
+ self.following_word_interval_begin = wi.begin
800
+ self.speaker_id = utterance.speaker_id
801
+ self.setText(
802
+ QtCore.QCoreApplication.translate(
803
+ "InsertPhoneIntervalCommand", "Insert phone interval"
804
+ )
805
+ )
806
+
807
+ def _redo(self, session) -> None:
808
+ word_intervals = []
809
+ if self.word_interval is not None:
810
+ for wi in getattr(self.utterance, self.word_interval_lookup):
811
+ session.merge(wi)
812
+ if wi.id == self.previous_word_interval_id:
813
+ wi.end = self.phone_interval.begin
814
+ if wi.id == self.following_word_interval_id:
815
+ wi.begin = self.phone_interval.end
816
+ word_intervals.append(wi)
817
+ make_transient(self.word_interval)
818
+ word_intervals.append(self.word_interval)
819
+ else:
820
+ for wi in getattr(self.utterance, self.word_interval_lookup):
821
+ session.merge(wi)
822
+ if self.initial_word_boundary:
823
+ if wi.id == self.previous_word_interval_id:
824
+ wi.end = self.phone_interval.begin
825
+ elif wi.id == self.phone_interval.word_interval_id:
826
+ wi.begin = self.phone_interval.begin
827
+ if self.final_word_boundary:
828
+ if wi.id == self.following_word_interval_id:
829
+ wi.begin = self.phone_interval.end
830
+ elif wi.id == self.phone_interval.word_interval_id:
831
+ wi.end = self.phone_interval.end
832
+ word_intervals.append(wi)
833
+ setattr(
834
+ self.utterance,
835
+ self.word_interval_lookup,
836
+ sorted(word_intervals, key=lambda x: x.begin),
837
+ )
838
+ phone_intervals = []
839
+ for pi in getattr(self.utterance, self.phone_interval_lookup):
840
+ session.merge(pi)
841
+ if self.has_previous and pi.id == self.previous_phone_interval.id:
842
+ pi.end = self.phone_interval.begin
843
+ if self.has_following and pi.id == self.following_phone_interval.id:
844
+ pi.begin = self.phone_interval.end
845
+ phone_intervals.append(pi)
846
+ make_transient(self.phone_interval)
847
+ self.phone_interval.utterance_id = self.utterance.id
848
+ phone_intervals.append(self.phone_interval)
849
+
850
+ setattr(
851
+ self.utterance,
852
+ self.phone_interval_lookup,
853
+ sorted(phone_intervals, key=lambda x: x.begin),
854
+ )
855
+ if not self.old_manual_alignments:
856
+ self.utterance.manual_alignments = True
857
+ session.merge(self.utterance)
858
+
859
+ def _undo(self, session) -> None:
860
+ phone_intervals = []
861
+ for pi in getattr(self.utterance, self.phone_interval_lookup):
862
+ if pi.id == self.phone_interval.id:
863
+ continue
864
+ session.merge(pi)
865
+ if self.has_previous and pi.id == self.previous_phone_interval.id:
866
+ pi.end = self.old_time_boundary
867
+ if self.has_following and pi.id == self.following_phone_interval.id:
868
+ pi.begin = self.old_time_boundary
869
+ phone_intervals.append(pi)
870
+ setattr(self.utterance, self.phone_interval_lookup, phone_intervals)
871
+ word_intervals = []
872
+ if self.word_interval is not None:
873
+ for wi in getattr(self.utterance, self.word_interval_lookup):
874
+ if wi.id == self.word_interval.id:
875
+ continue
876
+ session.merge(wi)
877
+ word_intervals.append(wi)
878
+ else:
879
+ for wi in getattr(self.utterance, self.word_interval_lookup):
880
+ session.merge(wi)
881
+ if self.initial_word_boundary:
882
+ if wi.id == self.previous_word_interval_id:
883
+ wi.end = self.previous_word_interval_end
884
+ elif wi.id == self.phone_interval.word_interval_id:
885
+ wi.begin = self.previous_word_interval_end
886
+ if self.final_word_boundary:
887
+ if wi.id == self.following_word_interval_id:
888
+ wi.begin = self.following_word_interval_begin
889
+ elif wi.id == self.phone_interval.word_interval_id:
890
+ wi.end = self.following_word_interval_begin
891
+ word_intervals.append(wi)
892
+ setattr(self.utterance, self.word_interval_lookup, word_intervals)
893
+ session.merge(self.utterance)
894
+ if not self.old_manual_alignments:
895
+ self.utterance.manual_alignments = self.old_manual_alignments
896
+
897
+ def update_data(self):
898
+ super().update_data()
899
+ self.corpus_model.changeCommandFired.emit()
900
+ self.file_model.phoneTierChanged.emit(self.utterance)
901
+
902
+
903
+ class UpdateWordIntervalPronunciationCommand(FileCommand):
904
+ def __init__(
905
+ self,
906
+ utterance: Utterance,
907
+ word_interval: WordInterval,
908
+ pronunciation: Pronunciation,
909
+ file_model: FileUtterancesModel,
910
+ ):
911
+ super().__init__(file_model)
912
+ self.utterance = utterance
913
+ self.word_interval = word_interval
914
+ self.old_pronunciation_id = self.word_interval.pronunciation_id
915
+ self.new_pronunciation = pronunciation
916
+ self.old_phone_intervals = None
917
+ self.new_phone_intervals = None
918
+
919
+ self.setText(
920
+ QtCore.QCoreApplication.translate(
921
+ "UpdateWordIntervalPronunciationCommand", "Update pronunciation for word interval"
922
+ )
923
+ )
924
+
925
+ def _redo(self, session) -> None:
926
+ phone_intervals = []
927
+ word_intervals = []
928
+ for pi in self.utterance.phone_intervals:
929
+ if pi.word_interval_id != self.word_interval.id:
930
+ session.merge(pi)
931
+ phone_intervals.append(pi)
932
+ elif self.old_phone_intervals is None:
933
+ word_intervals.append(pi)
934
+ if self.old_phone_intervals is None:
935
+ self.old_phone_intervals = word_intervals
936
+ if self.new_phone_intervals is None:
937
+ self.new_phone_intervals = []
938
+ new_phones = self.new_pronunciation.pronunciation.split()
939
+
940
+ next_pk = session.query(sqlalchemy.func.max(PhoneInterval.id)).scalar() + 1
941
+ begin = self.word_interval.begin
942
+ phone_duration = (self.word_interval.end - self.word_interval.begin) / len(new_phones)
943
+ for p in new_phones:
944
+ end = begin + phone_duration
945
+ pi = PhoneInterval(
946
+ id=next_pk,
947
+ begin=begin,
948
+ end=end,
949
+ phone=self.corpus_model.phones[p],
950
+ word_interval=self.word_interval,
951
+ word_interval_id=self.word_interval.id,
952
+ )
953
+ self.new_phone_intervals.append(pi)
954
+ begin = end
955
+ next_pk += 1
956
+ self.new_phone_intervals[-1].end = self.word_interval.end
957
+ for pi in self.new_phone_intervals:
958
+ make_transient(pi)
959
+ phone_intervals.append(pi)
960
+ self.utterance.phone_intervals = sorted(phone_intervals, key=lambda x: x.begin)
961
+
962
+ session.merge(self.utterance)
963
+ self.word_interval.pronunciation_id = self.new_pronunciation.id
964
+ session.merge(self.word_interval)
965
+
966
+ def _undo(self, session) -> None:
967
+ phone_intervals = []
968
+ for pi in self.utterance.phone_intervals:
969
+ if pi.word_interval_id != self.word_interval.id:
970
+ session.merge(pi)
971
+ phone_intervals.append(pi)
972
+ for pi in self.old_phone_intervals:
973
+ make_transient(pi)
974
+ phone_intervals.append(pi)
975
+
976
+ self.utterance.phone_intervals = sorted(phone_intervals, key=lambda x: x.begin)
977
+
978
+ session.merge(self.utterance)
979
+ self.word_interval.pronunciation_id = self.old_pronunciation_id
980
+ session.merge(self.word_interval)
981
+
982
+ def update_data(self):
983
+ super().update_data()
984
+ self.corpus_model.changeCommandFired.emit()
985
+ self.file_model.phoneTierChanged.emit(self.utterance)
986
+
987
+
988
+ class UpdateWordIntervalWordCommand(FileCommand):
989
+ def __init__(
990
+ self,
991
+ utterance: Utterance,
992
+ word_interval: WordInterval,
993
+ word: typing.Union[Word, str],
994
+ file_model: FileUtterancesModel,
995
+ ):
996
+ super().__init__(file_model)
997
+ self.utterance = utterance
998
+ self.word_interval = word_interval
999
+ self.old_word = self.word_interval.word
1000
+ self.new_word = word
1001
+ self.need_words_refreshed = isinstance(word, str)
1002
+
1003
+ self.setText(
1004
+ QtCore.QCoreApplication.translate(
1005
+ "UpdateWordIntervalPronunciationCommand", "Update pronunciation for word interval"
1006
+ )
1007
+ )
1008
+
1009
+ def _redo(self, session) -> None:
1010
+ session.merge(self.utterance)
1011
+ if isinstance(self.new_word, str):
1012
+ max_id, max_mapping_id = session.query(
1013
+ sqlalchemy.func.max(Word.id), sqlalchemy.func.max(Word.mapping_id)
1014
+ ).first()
1015
+ dictionary_id = self.utterance.speaker.dictionary_id
1016
+ if not dictionary_id:
1017
+ dictionary_id = 1
1018
+
1019
+ self.new_word = Word(
1020
+ id=max_id + 1,
1021
+ mapping_id=max_mapping_id + 1,
1022
+ word=self.new_word,
1023
+ count=1,
1024
+ dictionary_id=dictionary_id,
1025
+ word_type=WordType.speech,
1026
+ )
1027
+ session.merge(self.new_word)
1028
+
1029
+ self.word_interval.word = self.new_word
1030
+ self.word_interval.word_id = self.new_word.id
1031
+ session.merge(self.word_interval)
1032
+
1033
+ def _undo(self, session) -> None:
1034
+ session.merge(self.utterance)
1035
+ self.word_interval.word = self.old_word
1036
+ self.word_interval.word_id = self.old_word.id
1037
+ session.merge(self.word_interval)
1038
+
1039
+ def update_data(self):
1040
+ super().update_data()
1041
+ self.corpus_model.changeCommandFired.emit()
1042
+ self.file_model.phoneTierChanged.emit(self.utterance)
1043
+ if self.need_words_refreshed:
1044
+ self.corpus_model.refresh_words()
1045
+ self.need_words_refreshed = False
1046
+
1047
+
1048
+ class UpdatePhoneIntervalCommand(FileCommand):
1049
+ def __init__(
1050
+ self,
1051
+ utterance: Utterance,
1052
+ phone_interval: PhoneInterval,
1053
+ new_phone: Phone,
1054
+ file_model: FileUtterancesModel,
1055
+ ):
1056
+ super().__init__(file_model)
1057
+ self.utterance = utterance
1058
+ self.old_manual_alignments = utterance.manual_alignments
1059
+ self.phone_interval = phone_interval
1060
+ self.old_phone = self.phone_interval.phone
1061
+ self.new_phone = new_phone
1062
+ self.setText(
1063
+ QtCore.QCoreApplication.translate(
1064
+ "UpdatePhoneIntervalCommand", "Update phone interval"
1065
+ )
1066
+ )
1067
+
1068
+ def _redo(self, session) -> None:
1069
+ if not self.old_manual_alignments:
1070
+ self.utterance.manual_alignments = True
1071
+ session.merge(self.utterance)
1072
+ self.phone_interval.phone = self.new_phone
1073
+ self.phone_interval.phone_id = self.new_phone.id
1074
+ session.merge(self.phone_interval)
1075
+
1076
+ def _undo(self, session) -> None:
1077
+ if not self.old_manual_alignments:
1078
+ self.utterance.manual_alignments = self.old_manual_alignments
1079
+ session.merge(self.utterance)
1080
+ self.phone_interval.phone = self.old_phone
1081
+ self.phone_interval.phone_id = self.old_phone.id
1082
+ session.merge(self.phone_interval)
1083
+
1084
+ def update_data(self):
1085
+ super().update_data()
1086
+ self.corpus_model.changeCommandFired.emit()
1087
+ self.file_model.phoneTierChanged.emit(self.utterance)
1088
+
1089
+
419
1090
  class UpdateUtteranceTextCommand(FileCommand):
420
1091
  def __init__(self, utterance: Utterance, new_text: str, file_model: FileUtterancesModel):
421
1092
  super().__init__(file_model)