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 CHANGED
@@ -1,8 +1,13 @@
1
- # file generated by setuptools_scm
1
+ # file generated by setuptools-scm
2
2
  # don't change, don't track in version control
3
+
4
+ __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
5
+
3
6
  TYPE_CHECKING = False
4
7
  if TYPE_CHECKING:
5
- from typing import Tuple, Union
8
+ from typing import Tuple
9
+ from typing import Union
10
+
6
11
  VERSION_TUPLE = Tuple[Union[int, str], ...]
7
12
  else:
8
13
  VERSION_TUPLE = object
@@ -12,5 +17,5 @@ __version__: str
12
17
  __version_tuple__: VERSION_TUPLE
13
18
  version_tuple: VERSION_TUPLE
14
19
 
15
- __version__ = version = '0.8.1'
16
- __version_tuple__ = version_tuple = (0, 8, 1)
20
+ __version__ = version = '0.9.0'
21
+ __version_tuple__ = version_tuple = (0, 9, 0)
anchor/command_line.py CHANGED
@@ -1,3 +1,4 @@
1
+ import os
1
2
  import sys
2
3
 
3
4
  from montreal_forced_aligner import config
anchor/main.py CHANGED
@@ -15,7 +15,7 @@ from montreal_forced_aligner.config import MfaConfiguration, get_temporary_direc
15
15
  from montreal_forced_aligner.corpus import AcousticCorpus
16
16
  from montreal_forced_aligner.data import Language, WorkflowType
17
17
  from montreal_forced_aligner.db import CorpusWorkflow
18
- from montreal_forced_aligner.diarization.speaker_diarizer import FOUND_SPEECHBRAIN
18
+ from montreal_forced_aligner.diarization.speaker_diarizer import FOUND_PYANNOTE, FOUND_SPEECHBRAIN
19
19
  from montreal_forced_aligner.exceptions import DatabaseError
20
20
  from montreal_forced_aligner.g2p.generator import PyniniValidator
21
21
  from montreal_forced_aligner.models import (
@@ -33,6 +33,7 @@ import anchor.db
33
33
  from anchor import workers
34
34
  from anchor.models import (
35
35
  AcousticModelTableModel,
36
+ AlignmentAnalysisModel,
36
37
  CorpusModel,
37
38
  CorpusSelectionModel,
38
39
  CorpusTableModel,
@@ -52,7 +53,7 @@ from anchor.ui_corpus_manager import Ui_CorpusManagerDialog
52
53
  from anchor.ui_error_dialog import Ui_ErrorDialog
53
54
  from anchor.ui_main_window import Ui_MainWindow
54
55
  from anchor.ui_preferences import Ui_PreferencesDialog
55
- from anchor.widgets import MediaPlayer, ProgressWidget
56
+ from anchor.widgets import MediaPlayer, ProgressWidget, ScrollableMenuStyle
56
57
 
57
58
 
58
59
  class MainWindow(QtWidgets.QMainWindow):
@@ -104,6 +105,7 @@ class MainWindow(QtWidgets.QMainWindow):
104
105
  self.status_indicator = ProgressWidget()
105
106
  self.status_indicator.setFixedWidth(self.ui.statusbar.height())
106
107
  self.ui.statusbar.addPermanentWidget(self.status_indicator, 0)
108
+
107
109
  self.settings = AnchorSettings()
108
110
  self.settings.themeUpdated.connect(self.refresh_style_sheets)
109
111
  self.style_hints = QtGui.QGuiApplication.styleHints()
@@ -122,6 +124,7 @@ class MainWindow(QtWidgets.QMainWindow):
122
124
  self.tabifyDockWidget(self.ui.utteranceDockWidget, self.ui.languageModelDockWidget)
123
125
  self.tabifyDockWidget(self.ui.utteranceDockWidget, self.ui.speakerDockWidget)
124
126
  self.tabifyDockWidget(self.ui.utteranceDockWidget, self.ui.diarizationDockWidget)
127
+ self.tabifyDockWidget(self.ui.utteranceDockWidget, self.ui.alignmentAnalysisDockWidget)
125
128
  self.media_player = MediaPlayer(self)
126
129
  self.media_player.playbackStateChanged.connect(self.handleAudioState)
127
130
  self.media_player.audioReady.connect(self.file_loaded)
@@ -143,6 +146,8 @@ class MainWindow(QtWidgets.QMainWindow):
143
146
  "Counting speaker results": None,
144
147
  "Counting diarization results": None,
145
148
  "Diarizing utterances": None,
149
+ "Counting alignment analysis results": None,
150
+ "Analyzing alignments": None,
146
151
  "Recalculating speaker ivectors": None,
147
152
  "Finding duplicates": None,
148
153
  "Querying utterances": None,
@@ -421,17 +426,20 @@ class MainWindow(QtWidgets.QMainWindow):
421
426
  elif function == "Counting utterance results":
422
427
  worker = workers.QueryUtterancesWorker(self.corpus_model.session, **extra_args[0])
423
428
  worker.signals.result.connect(finished_function)
429
+ elif function in {"Analyzing alignments", "Counting alignment analysis results"}:
430
+ worker = workers.AlignmentAnalysisWorker(self.corpus_model.session, **extra_args[0])
431
+ worker.signals.result.connect(finished_function)
424
432
  elif function == "Diarizing utterances":
425
433
  worker = workers.SpeakerDiarizationWorker(self.corpus_model.session, **extra_args[0])
426
434
  worker.signals.result.connect(finished_function)
427
435
  elif function == "Diarizing speakers":
428
- worker = workers.SpeakerUtterancesWorker(self.corpus_model.session, **extra_args[0])
436
+ worker = workers.SpeakerComparisonWorker(self.corpus_model.session, **extra_args[0])
429
437
  worker.signals.result.connect(finished_function)
430
438
  elif function == "Counting utterance diarization results":
431
439
  worker = workers.SpeakerDiarizationWorker(self.corpus_model.session, **extra_args[0])
432
440
  worker.signals.result.connect(finished_function)
433
441
  elif function == "Counting speaker diarization results":
434
- worker = workers.SpeakerUtterancesWorker(self.corpus_model.session, **extra_args[0])
442
+ worker = workers.SpeakerComparisonWorker(self.corpus_model.session, **extra_args[0])
435
443
  worker.signals.result.connect(finished_function)
436
444
  elif function == "Merging speakers":
437
445
  self.set_application_state("loading")
@@ -532,12 +540,14 @@ class MainWindow(QtWidgets.QMainWindow):
532
540
  self.file_utterances_model = FileUtterancesModel(self)
533
541
  self.speaker_model = SpeakerModel(self)
534
542
  self.diarization_model = DiarizationModel(self)
543
+ self.alignment_analysis_model = AlignmentAnalysisModel(self)
535
544
 
536
545
  self.file_utterances_model.set_corpus_model(self.corpus_model)
537
546
 
538
547
  self.corpus_model.databaseSynced.connect(self.handle_changes_synced)
539
548
  self.corpus_model.runFunction.connect(self.execute_runnable)
540
549
  self.diarization_model.runFunction.connect(self.execute_runnable)
550
+ self.alignment_analysis_model.runFunction.connect(self.execute_runnable)
541
551
  self.corpus_model.lockCorpus.connect(self.anchor_lock_corpus)
542
552
  self.corpus_model.statusUpdate.connect(self.update_status_message)
543
553
  self.corpus_model.unlockCorpus.connect(self.anchor_unlock_corpus)
@@ -551,6 +561,7 @@ class MainWindow(QtWidgets.QMainWindow):
551
561
  self.corpus_model.set_dictionary_model(self.dictionary_model)
552
562
  self.speaker_model.set_corpus_model(self.corpus_model)
553
563
  self.diarization_model.set_corpus_model(self.corpus_model)
564
+ self.alignment_analysis_model.set_corpus_model(self.corpus_model)
554
565
  self.oov_model.set_corpus_model(self.corpus_model)
555
566
  self.selection_model = CorpusSelectionModel(self.corpus_model)
556
567
  self.file_selection_model = FileSelectionModel(self.file_utterances_model)
@@ -575,6 +586,9 @@ class MainWindow(QtWidgets.QMainWindow):
575
586
  self.ui.languageModelWidget.set_models(self.corpus_model)
576
587
  self.ui.dictionaryWidget.set_models(self.dictionary_model)
577
588
  self.ui.diarizationWidget.set_models(self.diarization_model, self.file_selection_model)
589
+ self.ui.alignmentAnalysisWidget.set_models(
590
+ self.alignment_analysis_model, self.file_selection_model
591
+ )
578
592
  self.ui.oovWidget.set_models(self.oov_model)
579
593
  self.file_selection_model.currentUtteranceChanged.connect(self.change_utterance)
580
594
  self.file_selection_model.currentUtteranceChanged.connect(
@@ -665,7 +679,7 @@ class MainWindow(QtWidgets.QMainWindow):
665
679
  else:
666
680
  if (
667
681
  not self.corpus_model.corpus.has_alignments()
668
- or not self.corpus_model.corpus.has_alignments(WorkflowType.reference)
682
+ or not self.corpus_model.corpus.has_alignments()
669
683
  ):
670
684
  self.ui.evaluateAlignmentsAct.setEnabled(False)
671
685
  # if self.corpus_model.corpus.alignment_done:
@@ -777,6 +791,10 @@ class MainWindow(QtWidgets.QMainWindow):
777
791
  self.settings.setValue(
778
792
  AnchorSettings.DIARIZATION_VISIBLE, self.ui.diarizationDockWidget.isVisible()
779
793
  )
794
+ self.settings.setValue(
795
+ AnchorSettings.ALIGNMENT_ANALYSIS_VISIBLE,
796
+ self.ui.alignmentAnalysisDockWidget.isVisible(),
797
+ )
780
798
  self.settings.setValue(AnchorSettings.GEOMETRY, self.saveGeometry())
781
799
  self.settings.setValue(AnchorSettings.WINDOW_STATE, self.saveState())
782
800
 
@@ -925,6 +943,7 @@ class MainWindow(QtWidgets.QMainWindow):
925
943
  self.ui.menuWindow.addAction(self.ui.alignmentDockWidget.toggleViewAction())
926
944
  self.ui.menuWindow.addAction(self.ui.transcriptionDockWidget.toggleViewAction())
927
945
  self.ui.menuWindow.addAction(self.ui.diarizationDockWidget.toggleViewAction())
946
+ self.ui.menuWindow.addAction(self.ui.alignmentAnalysisDockWidget.toggleViewAction())
928
947
 
929
948
  self.merge_all_action = QtGui.QAction(self)
930
949
  self.merge_all_action.setObjectName("merge_all_action")
@@ -1063,6 +1082,41 @@ class MainWindow(QtWidgets.QMainWindow):
1063
1082
  f"Found {duplicate_count} duplicate files, see {duplicate_path}."
1064
1083
  )
1065
1084
 
1085
+ def refresh_local_dictionaries(self):
1086
+ for a in self.ui.mfaDictionaryMenu.actions():
1087
+ self.dictionary_action_group.removeAction(a)
1088
+ self.ui.mfaDictionaryMenu.clear()
1089
+ with sqlalchemy.orm.Session(self.db_engine) as session:
1090
+ current_corpus = (
1091
+ session.query(anchor.db.AnchorCorpus)
1092
+ .options(
1093
+ sqlalchemy.orm.joinedload(anchor.db.AnchorCorpus.acoustic_model),
1094
+ sqlalchemy.orm.joinedload(anchor.db.AnchorCorpus.language_model),
1095
+ sqlalchemy.orm.joinedload(anchor.db.AnchorCorpus.dictionary),
1096
+ sqlalchemy.orm.joinedload(anchor.db.AnchorCorpus.ivector_extractor),
1097
+ sqlalchemy.orm.joinedload(anchor.db.AnchorCorpus.g2p_model),
1098
+ sqlalchemy.orm.joinedload(anchor.db.AnchorCorpus.sad_model),
1099
+ )
1100
+ .filter(anchor.db.AnchorCorpus.current == True) # noqa
1101
+ .first()
1102
+ )
1103
+ for m in (
1104
+ session.query(anchor.db.Dictionary)
1105
+ .filter_by(available_locally=True)
1106
+ .order_by(anchor.db.Dictionary.last_used.desc())
1107
+ ):
1108
+ a = QtGui.QAction(text=f"{m.path} [{m.name}]", parent=self)
1109
+ a.setData(m.id)
1110
+ if (
1111
+ current_corpus is not None
1112
+ and current_corpus.dictionary is not None
1113
+ and current_corpus.dictionary == m
1114
+ ):
1115
+ a.setChecked(True)
1116
+ a.triggered.connect(self.change_dictionary)
1117
+ self.dictionary_action_group.addAction(a)
1118
+ self.ui.mfaDictionaryMenu.addAction(a)
1119
+
1066
1120
  def refresh_model_actions(self):
1067
1121
  self.ui.menuLanguage.clear()
1068
1122
  for lang in sorted(Language, key=lambda x: x.display_name):
@@ -1230,6 +1284,24 @@ class MainWindow(QtWidgets.QMainWindow):
1230
1284
  a.triggered.connect(self.change_ivector_extractor)
1231
1285
  self.ui.ivectorExtractorMenu.addAction(a)
1232
1286
  self.ivector_action_group.addAction(a)
1287
+ if FOUND_PYANNOTE:
1288
+ m = (
1289
+ session.query(anchor.db.IvectorExtractor)
1290
+ .filter(anchor.db.IvectorExtractor.path == "pyannote")
1291
+ .first()
1292
+ )
1293
+ if m is None:
1294
+ m = anchor.db.IvectorExtractor(
1295
+ name="pyannote", path="pyannote", available_locally=True
1296
+ )
1297
+ session.add(m)
1298
+ session.flush()
1299
+ session.commit()
1300
+ a = QtGui.QAction(text="pyannote", parent=self)
1301
+ a.setData(m.id)
1302
+ a.triggered.connect(self.change_ivector_extractor)
1303
+ self.ui.ivectorExtractorMenu.addAction(a)
1304
+ self.ivector_action_group.addAction(a)
1233
1305
  for m_name, found in [("speechbrain", FOUND_SPEECHBRAIN), ("whisper", FOUND_WHISPERX)]:
1234
1306
  if not found:
1235
1307
  continue
@@ -1356,7 +1428,7 @@ class MainWindow(QtWidgets.QMainWindow):
1356
1428
  self.set_application_state("loading")
1357
1429
  self.ui.loadingScreen.setCorpusName(f"Loading {c.path}...")
1358
1430
  dictionary_path = None
1359
- if c.dictionary is not None:
1431
+ if c.dictionary is not None and c.dictionary.path != ".":
1360
1432
  dictionary_path = c.dictionary.path
1361
1433
  self.corpus_worker.set_params(c.path, dictionary_path)
1362
1434
  self.corpus_worker.start()
@@ -1406,6 +1478,8 @@ class MainWindow(QtWidgets.QMainWindow):
1406
1478
  self.corpus_model.has_per_speaker_transcribed_alignments = True
1407
1479
 
1408
1480
  def finalize_load_corpus(self, corpus: AcousticCorpus):
1481
+ from montreal_forced_aligner.db import Dictionary
1482
+
1409
1483
  if corpus is None:
1410
1484
  self.set_application_state("unloaded")
1411
1485
  self.corpus = corpus
@@ -1414,6 +1488,7 @@ class MainWindow(QtWidgets.QMainWindow):
1414
1488
  plda_path = self.corpus_model.corpus.output_directory.joinpath(
1415
1489
  "speaker_diarization"
1416
1490
  ).joinpath("plda")
1491
+ self.corpus_model.corpus.create_new_current_workflow(WorkflowType.reference)
1417
1492
  if plda_path.exists():
1418
1493
  self.corpus_model.plda = read_kaldi_object(Plda, plda_path)
1419
1494
  self.corpus_model.runFunction.emit(
@@ -1430,6 +1505,33 @@ class MainWindow(QtWidgets.QMainWindow):
1430
1505
  c = session.query(anchor.db.AnchorCorpus).filter_by(current=True).first()
1431
1506
  if c.custom_mapping_path:
1432
1507
  self.dictionary_model.set_custom_mapping(c.custom_mapping_path)
1508
+ dictionaries = self.corpus_model.session.query(Dictionary).all()
1509
+ refresh_dictionaries = c.dictionary_id is None and dictionaries
1510
+ if refresh_dictionaries:
1511
+ for d in dictionaries:
1512
+ dictionary_path = d.path
1513
+ if not os.path.exists(dictionary_path):
1514
+ continue
1515
+ self.settings.setValue(
1516
+ AnchorSettings.DEFAULT_DICTIONARY_DIRECTORY,
1517
+ os.path.dirname(dictionary_path),
1518
+ )
1519
+ d = (
1520
+ session.query(anchor.db.Dictionary)
1521
+ .filter_by(path=dictionary_path)
1522
+ .first()
1523
+ )
1524
+ if not d:
1525
+ d_name = os.path.splitext(os.path.basename(dictionary_path))[0]
1526
+ d = anchor.db.Dictionary(
1527
+ name=d_name, path=dictionary_path, available_locally=True
1528
+ )
1529
+ session.add(d)
1530
+ c.dictionary = d
1531
+ session.commit()
1532
+ if refresh_dictionaries:
1533
+ self.refresh_local_dictionaries()
1534
+ self.check_actions()
1433
1535
 
1434
1536
  def finalize_reload_corpus(self):
1435
1537
  self.set_application_state("loaded")
@@ -1709,6 +1811,7 @@ class MainWindow(QtWidgets.QMainWindow):
1709
1811
  self.ui.alignmentDockWidget.setVisible(False)
1710
1812
  self.ui.languageModelDockWidget.setVisible(False)
1711
1813
  self.ui.diarizationDockWidget.setVisible(False)
1814
+ self.ui.alignmentAnalysisDockWidget.setVisible(False)
1712
1815
  self.ui.toolBar.setVisible(False)
1713
1816
 
1714
1817
  self.ui.utteranceDetailWidget.setVisible(False)
@@ -1760,6 +1863,9 @@ class MainWindow(QtWidgets.QMainWindow):
1760
1863
  self.ui.diarizationDockWidget.setVisible(
1761
1864
  self.settings.value(AnchorSettings.DIARIZATION_VISIBLE)
1762
1865
  )
1866
+ self.ui.alignmentAnalysisDockWidget.setVisible(
1867
+ self.settings.value(AnchorSettings.ALIGNMENT_ANALYSIS_VISIBLE)
1868
+ )
1763
1869
  self.ui.toolBar.setVisible(True)
1764
1870
 
1765
1871
  self.ui.utteranceDetailWidget.setVisible(True)
@@ -1795,6 +1901,7 @@ class MainWindow(QtWidgets.QMainWindow):
1795
1901
  self.ui.speakerDockWidget.setVisible(False)
1796
1902
  self.ui.utteranceDetailWidget.setVisible(False)
1797
1903
  self.ui.diarizationDockWidget.setVisible(False)
1904
+ self.ui.alignmentAnalysisDockWidget.setVisible(False)
1798
1905
 
1799
1906
  self.ui.changeTemporaryDirectoryAct.setEnabled(True)
1800
1907
  self.ui.openPreferencesAct.setEnabled(True)