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/_version.py +2 -2
- 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 +682 -11
- anchor/widgets.py +303 -39
- anchor/workers.py +632 -351
- {anchor_annotator-0.8.2.dist-info → anchor_annotator-0.9.1.dist-info}/METADATA +1 -1
- anchor_annotator-0.9.1.dist-info/RECORD +22 -0
- anchor_annotator-0.8.2.dist-info/RECORD +0 -22
- {anchor_annotator-0.8.2.dist-info → anchor_annotator-0.9.1.dist-info}/WHEEL +0 -0
- {anchor_annotator-0.8.2.dist-info → anchor_annotator-0.9.1.dist-info}/licenses/LICENSE +0 -0
- {anchor_annotator-0.8.2.dist-info → anchor_annotator-0.9.1.dist-info}/top_level.txt +0 -0
anchor/_version.py
CHANGED
anchor/command_line.py
CHANGED
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.
|
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.
|
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(
|
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)
|