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/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,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)