Skip to content

Commit 8166aaf

Browse files
committed
update LFA
1 parent 13ff79b commit 8166aaf

13 files changed

Lines changed: 733 additions & 537 deletions

src/apps/LyricFA/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ find_package(SndFile CONFIG REQUIRED)
1111
find_package(cpp-pinyin CONFIG REQUIRED)
1212

1313
target_link_libraries(${PROJECT_NAME} PRIVATE
14+
AsyncTaskWindow::AsyncTaskWindow
1415
Qt${QT_VERSION_MAJOR}::Core
1516
Qt${QT_VERSION_MAJOR}::Widgets
1617
cpp-pinyin::cpp-pinyin
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
#include "LyricFAWindow.h"
2+
3+
#include <QApplication>
4+
#include <QFileDialog>
5+
#include <QLabel>
6+
#include <QLineEdit>
7+
#include <QMessageBox>
8+
#include <QStandardPaths>
9+
10+
#include "../util/AsrThread.h"
11+
#include "../util/FaTread.h"
12+
13+
namespace LyricFA {
14+
15+
LyricFAWindow::LyricFAWindow(QWidget *parent) : AsyncTaskWindow(parent) {
16+
const QString modelFolder = QDir::cleanPath(
17+
#ifdef Q_OS_MAC
18+
QApplication::applicationDirPath() + "/../Resources/model"
19+
#else
20+
QApplication::applicationDirPath() + QDir::separator() + "model"
21+
#endif
22+
);
23+
if (QDir(modelFolder).exists()) {
24+
const auto modelPath = modelFolder + QDir::separator() + "model.onnx";
25+
const auto vocabPath = modelFolder + QDir::separator() + "vocab.txt";
26+
if (!QFile(modelPath).exists() || !QFile(vocabPath).exists())
27+
QMessageBox::information(
28+
this, "Warning",
29+
"Missing model.onnx or vocab.txt, please read ReadMe.md and download the model again.");
30+
else
31+
m_asr = new Asr(modelFolder);
32+
} else {
33+
#ifdef Q_OS_MAC
34+
QMessageBox::information(
35+
this, "Warning",
36+
"Please read ReadMe.md and download asrModel to unzip to app bundle's Resources directory.");
37+
#else
38+
QMessageBox::information(this, "Warning",
39+
"Please read ReadMe.md and download asrModel to unzip to the root directory.");
40+
#endif
41+
}
42+
43+
m_mandarin = QSharedPointer<Pinyin::Pinyin>(new Pinyin::Pinyin());
44+
m_match = new MatchLyric();
45+
46+
setRunButtonText("运行 ASR");
47+
LyricFAWindow::init();
48+
}
49+
50+
LyricFAWindow::~LyricFAWindow() {
51+
delete m_asr;
52+
delete m_match;
53+
}
54+
55+
void LyricFAWindow::init() {
56+
auto *labLabel = new QLabel("Lab Out Path:", this);
57+
m_labEdit = new QLineEdit(R"(D:\python\LyricFA\test_outlab)", this);
58+
auto *btnLab = new QPushButton("Open Folder", this);
59+
auto *labLayout = new QHBoxLayout();
60+
labLayout->addWidget(m_labEdit);
61+
labLayout->addWidget(btnLab);
62+
63+
auto *jsonLabel = new QLabel("Json Out Path:", this);
64+
m_jsonEdit = new QLineEdit(R"(D:\python\LyricFA\test_outjson)", this);
65+
auto *btnJson = new QPushButton("Open Folder", this);
66+
auto *jsonLayout = new QHBoxLayout();
67+
jsonLayout->addWidget(m_jsonEdit);
68+
jsonLayout->addWidget(btnJson);
69+
70+
auto *lyricLabel = new QLabel("Raw Lyric Path:", this);
71+
m_lyricEdit = new QLineEdit(R"(D:\python\LyricFA\lyrics)", this);
72+
auto *btnLyric = new QPushButton("Lyric Folder", this);
73+
auto *lyricLayout = new QHBoxLayout();
74+
lyricLayout->addWidget(m_lyricEdit);
75+
lyricLayout->addWidget(btnLyric);
76+
77+
m_pinyinBox = new QCheckBox("ASR result saved as pinyin", this);
78+
m_matchBtn = new QPushButton("匹配歌词", this);
79+
connect(m_matchBtn, &QPushButton::clicked, this, &LyricFAWindow::slot_matchLyric);
80+
81+
m_rightPanel->addWidget(labLabel);
82+
m_rightPanel->addLayout(labLayout);
83+
m_rightPanel->addWidget(jsonLabel);
84+
m_rightPanel->addLayout(jsonLayout);
85+
m_rightPanel->addWidget(lyricLabel);
86+
m_rightPanel->addLayout(lyricLayout);
87+
m_rightPanel->addWidget(m_pinyinBox);
88+
m_rightPanel->addWidget(m_matchBtn);
89+
m_rightPanel->addStretch();
90+
91+
connect(btnLab, &QPushButton::clicked, this, &LyricFAWindow::slot_labPath);
92+
connect(btnJson, &QPushButton::clicked, this, &LyricFAWindow::slot_jsonPath);
93+
connect(btnLyric, &QPushButton::clicked, this, &LyricFAWindow::slot_lyricPath);
94+
}
95+
96+
void LyricFAWindow::runTask() {
97+
m_logOutput->clear();
98+
m_threadPool->clear();
99+
100+
const QString labOutPath = m_labEdit->text();
101+
if (!QDir(labOutPath).exists()) {
102+
QMessageBox::information(nullptr, "Warning",
103+
"Lab Out Path is empty or does not exist. Please set the output directory.");
104+
return;
105+
}
106+
107+
m_totalTasks = taskList()->count();
108+
if (m_totalTasks == 0) {
109+
QMessageBox::information(this, "Info", "No tasks to run.");
110+
return;
111+
}
112+
m_finishedTasks = 0;
113+
m_errorTasks = 0;
114+
m_errorDetails.clear();
115+
m_progressBar->setValue(0);
116+
m_progressBar->setMaximum(m_totalTasks);
117+
m_currentMode = Mode_Asr;
118+
119+
const bool toPinyin = m_pinyinBox->isChecked();
120+
121+
for (int i = 0; i < m_totalTasks; ++i) {
122+
const QListWidgetItem *item = taskList()->item(i);
123+
QString filename = item->text();
124+
const QString filePath = item->data(Qt::UserRole + 1).toString();
125+
const QString labFilePath =
126+
labOutPath + QDir::separator() + QFileInfo(filename).completeBaseName() + ".lab";
127+
128+
auto *asrThread = new AsrThread(m_asr, filename, filePath, labFilePath,
129+
QSharedPointer<Pinyin::Pinyin>(toPinyin ? m_mandarin.data() : nullptr));
130+
connect(asrThread, &AsrThread::oneFailed, this, &AsyncTaskWindow::slot_oneFailed);
131+
connect(asrThread, &AsrThread::oneFinished, this, &AsyncTaskWindow::slot_oneFinished);
132+
m_threadPool->start(asrThread);
133+
}
134+
}
135+
136+
void LyricFAWindow::onTaskFinished() {
137+
QString msg;
138+
if (m_currentMode == Mode_Asr) {
139+
msg = QString("ASR 任务完成!总数: %3, 成功: %1, 失败: %2")
140+
.arg(m_totalTasks - m_errorTasks)
141+
.arg(m_errorTasks)
142+
.arg(m_totalTasks);
143+
} else {
144+
msg = QString("歌词匹配完成!总数: %3, 成功: %1, 失败: %2")
145+
.arg(m_totalTasks - m_errorTasks)
146+
.arg(m_errorTasks)
147+
.arg(m_totalTasks);
148+
}
149+
150+
if (m_errorTasks > 0) {
151+
m_logOutput->appendPlainText("失败任务列表:");
152+
for (const QString &detail : m_errorDetails)
153+
m_logOutput->appendPlainText(" " + detail);
154+
m_errorDetails.clear();
155+
}
156+
157+
QMessageBox::information(this, QApplication::applicationName(), msg);
158+
m_totalTasks = m_finishedTasks = m_errorTasks = 0;
159+
}
160+
161+
void LyricFAWindow::slot_matchLyric() {
162+
const QString lyricFolder = m_lyricEdit->text();
163+
const QString labFolder = m_labEdit->text();
164+
const QString jsonFolder = m_jsonEdit->text();
165+
166+
if (!QDir(lyricFolder).exists() || !QDir(labFolder).exists() || !QDir(jsonFolder).exists()) {
167+
QMessageBox::information(nullptr, "Warning", "Please ensure all three paths exist and are set correctly.");
168+
return;
169+
}
170+
171+
m_logOutput->clear();
172+
m_threadPool->clear();
173+
174+
QDir().mkpath(jsonFolder);
175+
176+
const QDir labDir(labFolder);
177+
QStringList labPaths;
178+
for (const QString &file : labDir.entryList(QStringList() << "*.lab", QDir::Files)) {
179+
labPaths << labDir.absoluteFilePath(file);
180+
}
181+
182+
if (labPaths.isEmpty()) {
183+
QMessageBox::information(this, "Info", "No .lab files found in the lab folder.");
184+
return;
185+
}
186+
187+
m_match->initLyric(lyricFolder);
188+
189+
m_totalTasks = labPaths.size();
190+
m_finishedTasks = 0;
191+
m_errorTasks = 0;
192+
m_errorDetails.clear();
193+
m_progressBar->setValue(0);
194+
m_progressBar->setMaximum(m_totalTasks);
195+
m_currentMode = Mode_MatchLyric;
196+
197+
for (const QString &labPath : labPaths) {
198+
QString labName = QFileInfo(labPath).completeBaseName();
199+
const QString jsonPath = jsonFolder + QDir::separator() + labName + ".json";
200+
201+
auto *faThread = new FaTread(m_match, labName, labPath, jsonPath);
202+
connect(faThread, &FaTread::oneFailed, this, &AsyncTaskWindow::slot_oneFailed);
203+
connect(faThread, &FaTread::oneFinished, this, &AsyncTaskWindow::slot_oneFinished);
204+
m_threadPool->start(faThread);
205+
}
206+
}
207+
208+
void LyricFAWindow::slot_labPath() {
209+
const QString path = QFileDialog::getExistingDirectory(
210+
this, "Select Lab Output Directory", QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
211+
if (!path.isEmpty())
212+
m_labEdit->setText(path);
213+
}
214+
215+
void LyricFAWindow::slot_jsonPath() {
216+
const QString path = QFileDialog::getExistingDirectory(
217+
this, "Select Json Output Directory", QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
218+
if (!path.isEmpty())
219+
m_jsonEdit->setText(path);
220+
}
221+
222+
void LyricFAWindow::slot_lyricPath() {
223+
const QString path = QFileDialog::getExistingDirectory(
224+
this, "Select Raw Lyric Directory", QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
225+
if (!path.isEmpty())
226+
m_lyricEdit->setText(path);
227+
}
228+
229+
} // namespace LyricFA
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#ifndef LYRICFAWINDOW_H
2+
#define LYRICFAWINDOW_H
3+
4+
#include "AsyncTaskWindow.h"
5+
#include <QCheckBox>
6+
#include <QPushButton>
7+
#include <QSharedPointer>
8+
9+
#include "../util/Asr.h"
10+
#include "../util/MatchLyric.h"
11+
namespace LyricFA {
12+
13+
class LyricFAWindow : public AsyncTask::AsyncTaskWindow {
14+
Q_OBJECT
15+
public:
16+
explicit LyricFAWindow(QWidget *parent = nullptr);
17+
~LyricFAWindow() override;
18+
19+
protected:
20+
void init() override;
21+
void runTask() override;
22+
void onTaskFinished() override;
23+
24+
private slots:
25+
void slot_matchLyric();
26+
void slot_labPath();
27+
void slot_jsonPath();
28+
void slot_lyricPath();
29+
30+
private:
31+
QLineEdit *m_labEdit;
32+
QLineEdit *m_jsonEdit;
33+
QLineEdit *m_lyricEdit;
34+
QCheckBox *m_pinyinBox;
35+
QPushButton *m_matchBtn;
36+
37+
Asr *m_asr = nullptr;
38+
QSharedPointer<Pinyin::Pinyin> m_mandarin;
39+
MatchLyric *m_match = nullptr;
40+
41+
enum Mode { Mode_Asr, Mode_MatchLyric };
42+
Mode m_currentMode = Mode_Asr;
43+
};
44+
45+
} // namespace LyricFA
46+
47+
#endif // LYRICFAWINDOW_H

0 commit comments

Comments
 (0)