cmake-plugin: 添加基于源代码构建的 TTS 语音合成插件示例,运行时需要指定引擎

This commit is contained in:
zinface 2023-03-31 16:35:19 +08:00
parent 721bfdb056
commit e50b7d3ef0
6 changed files with 527 additions and 0 deletions

View File

@ -30,4 +30,8 @@ if(USE_NOTEPAD_PLUGIN)
# Linux lxqt-qterminal
add_subdirectory(linux-terminal-plugin)
endif(UNIX AND NOT APPLE)
# TTS
add_subdirectory(TTS-plugin)
endif(USE_NOTEPAD_PLUGIN)

View File

@ -0,0 +1,91 @@
# TTS-plugin/CMakeLists.txt
#
# 使
#
# TTS-plugin
# TTS-plugin -> your plugin name
set(LOCAL_PLUGIN_NAME "TTS-plugin")
# TTS-plugin
# TTS-plugin
# 1. TTS-plugin
# 2. TTS-plugin
if(TRUE)
# TTS-plugin
spark_file_glob(LocalSources
./*.h ./*.cpp ./*.ui
)
spark_add_library(${LOCAL_PLUGIN_NAME} SHARED ${LocalSources})
target_include_directories(${LOCAL_PLUGIN_NAME} PRIVATE
${PROJECT_SOURCE_DIR}/src
${PROJECT_SOURCE_DIR}/src/cceditor
${PROJECT_SOURCE_DIR}/src/qscint/src
${PROJECT_SOURCE_DIR}/src/qscint/src/Qsci
${PROJECT_SOURCE_DIR}/src/qscint/scintilla/src
${PROJECT_SOURCE_DIR}/src/qscint/scintilla/include
${PROJECT_SOURCE_DIR}/src/qscint/scintilla/lexlib
${PROJECT_SOURCE_DIR}/src/qscint/scintilla/boostregex
)
# target_link_libraries(${LOCAL_PLUGIN_NAME} QSci)
target_link_QSci(${LOCAL_PLUGIN_NAME})
if(USE_QT6)
# target_link_qt6_Core5Compat(${LOCAL_PLUGIN_NAME}) # : Qt6 使 Core5Compat Qt5
# target_link_qt6_PrintSupport(${LOCAL_PLUGIN_NAME})
# target_link_qt6_XmlPatterns(${LOCAL_PLUGIN_NAME}) # Bug
else()
# target_link_qt5_PrintSupport(${LOCAL_PLUGIN_NAME})
# target_link_qt5_XmlPatterns(${LOCAL_PLUGIN_NAME})
endif(USE_QT6)
# Notepad-- plugin
set_target_properties(${LOCAL_PLUGIN_NAME}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY $<TARGET_FILE_DIR:${PROJECT_NAME}>/plugin
LIBRARY_OUTPUT_DIRECTORY $<TARGET_FILE_DIR:${PROJECT_NAME}>/plugin
ARCHIVE_OUTPUT_DIRECTORY $<TARGET_FILE_DIR:${PROJECT_NAME}>/plugin)
# bin/plugin 每个位置或许都不一样(特别是 Linux)
# install(TARGETS ${LOCAL_PLUGIN_NAME} DESTINATION bin/plugin)
endif(TRUE)
# ----------------- TTS-plugin ----------------- #
if(WIN32 AND NOTEPAD_BUILD_BY_SHARED)
# Windows QScintilla
target_compile_definitions(${LOCAL_PLUGIN_NAME}
PRIVATE
NOTEPAD_PLUGIN_MANAGER
QSCINTILLA_DLL # Windows 使 QSci Q_DECL_IMPORT
# QSCINTILLA_EXPORT Q_DECL_IMPORT
)
else()
# Windows QScintilla
target_compile_definitions(${LOCAL_PLUGIN_NAME}
PRIVATE
NOTEPAD_PLUGIN_MANAGER
# QSCINTILLA_DLL # Windows 使 QSci Q_DECL_IMPORT
# QSCINTILLA_EXPORT Q_DECL_IMPORT
)
endif(WIN32 AND NOTEPAD_BUILD_BY_SHARED)
if(UNIX)
# Unix/Linux
target_compile_definitions(${LOCAL_PLUGIN_NAME}
PRIVATE
NOTEPAD_PLUGIN_MANAGER
)
endif(UNIX)
# ----------------- TTS-plugin ----------------- #
# include(ExternalProject)
# ExternalProject_Add(
# GIT_REPOSITORY
# https://gitee.com/mirrors/ekho.git
# )

View File

@ -0,0 +1,258 @@
#include "ndd_plugin_implement.h"
#include "ui_ndd_plugin_implement.h"
#include <qsciscintilla.h>
#include <QDebug>
#include <QProcess>
#include <QDir>
#include <QDockWidget>
#include <QFile>
#include <QFileInfo>
#include <QThread>
NddPluginImplement::NddPluginImplement(QWidget *parent, QsciScintilla *pEdit) : QMainWindow(parent)
, currentWidget(parent)
, currentEdit(pEdit)
, ttsSettingWidget(new Ui::NddPluginImplement)
{
ttsSettingWidget->setupUi(this);
}
NddPluginImplement::~NddPluginImplement()
{
}
void NddPluginImplement::setMenuActions(QMenu *menu)
{
connect(menu->addAction(u8"TTS Settings"), &QAction::triggered, this, [=](){
this->setParent(nullptr);
this->show();
});
connect(menu->addAction(u8"TTS Settings(Dock)"), &QAction::triggered, this, [=](){
auto mainWindow = dynamic_cast<QMainWindow*>(currentWidget);
if(!mainWindow) {
qDebug() << "None";
return;
}
QDockWidget *m_dockWidget = new QDockWidget(mainWindow);
m_dockWidget->setMinimumSize(100, 200);
mainWindow->addDockWidget(Qt::RightDockWidgetArea, m_dockWidget);
if (!m_dockWidget->isVisible())
m_dockWidget->show();
m_dockWidget->setWidget(this);
});
//
connect(menu->addAction(u8"TTS Start"), &QAction::triggered, this, [&](){
// 获取当前编辑器,并直接打印编辑器内容
QsciScintilla *edit = getCurrentEditFunc();
QStringList arguments = QStringList() << "-v" << "zh" << edit->selectedText();;
speakeSpeck(arguments);
});
connect(menu->addAction(u8"TTS Stop"), &QAction::triggered, this, [&](){
if (process->state() != QProcess::NotRunning) {
process->kill();
}
});
connect(menu->addAction(u8"阅读选中内容 eSpeak (未指定)"), &QAction::triggered, this, [&](){
QStringList arguments = QStringList() << getCurrentEditFunc()->selectedText();
speakeSpeck(arguments);
});
connect(menu->addAction(u8"阅读选中内容 eSpeak (普通话)"), &QAction::triggered, this, [&](){
QStringList arguments = QStringList() << "-v" << "zh" << getCurrentEditFunc()->selectedText();
speakeSpeck(arguments);
});
connect(menu->addAction(u8"阅读选中内容 Ekho (未指定)"), &QAction::triggered, this, [&](){
QStringList arguments = QStringList() << getCurrentEditFunc()->selectedText();
speakeSpeck(arguments);
});
connect(menu->addAction(u8"阅读选中内容 Ekho (普通话)"), &QAction::triggered, this, [&](){
QStringList arguments = QStringList() << "-v" << "Mandarin" << getCurrentEditFunc()->selectedText();
speakEkho(arguments);
});
connect(menu->addAction(u8"阅读选中内容 Ekho (广东话-粤语)"), &QAction::triggered, this, [&](){
QStringList arguments = QStringList() << "-v" << "Cantonese" << getCurrentEditFunc()->selectedText();
speakEkho(arguments);
});
connect(menu->addAction(u8"阅读选中内容 Ekho (台山话)"), &QAction::triggered, this, [&](){
QStringList arguments = QStringList() << "-v" << "Toisanese" << getCurrentEditFunc()->selectedText();
speakEkho(arguments);
});
connect(menu->addAction(u8"阅读选中内容 Ekho (客家话)"), &QAction::triggered, this, [&](){
QStringList arguments = QStringList() << "-v" << "Hakka" << getCurrentEditFunc()->selectedText();
speakEkho(arguments);
});
connect(menu->addAction(u8"阅读选中内容 Ekho (藏语)"), &QAction::triggered, this, [&](){
QStringList arguments = QStringList() << "-v" << "Tibetan" << getCurrentEditFunc()->selectedText();
speakEkho(arguments);
});
connect(menu->addAction(u8"阅读选中内容 Ekho (南京话)"), &QAction::triggered, this, [&](){
QStringList arguments = QStringList() << "-v" << "Ngangien" << getCurrentEditFunc()->selectedText();
speakEkho(arguments);
});
connect(menu->addAction(u8"阅读选中内容 Ekho (韩语)"), &QAction::triggered, this, [&](){
QStringList arguments = QStringList() << "-v" << "Hangul" << getCurrentEditFunc()->selectedText();
speakEkho(arguments);
});
connect(menu->addAction(u8"监听变化"), &QAction::triggered, this, [&](){
// QThread *thread = new QThread;
currentEdit = getCurrentEditFunc();
// 光标移动声
connect(currentEdit, &QsciScintilla::selectionChanged, this, [=](){
speakeSpeckEditrMonitor("k", 450);
});
// 输入声 - change event
connect(currentEdit, &QsciScintilla::textChanged, this, [=](){
QStringList arguments = QStringList() << "-v" << "zh";
speakeSpeckEditrMonitor(u8"", arguments, 450);
});
});
// Cursor
connect(menu->addAction(u8"光标行号变化监听"), &QAction::triggered, this, [&](){
currentEdit = getCurrentEditFunc();
connect(currentEdit, &QsciScintilla::cursorPosChange, this, [=](int x, int y){
QStringList arguments = QStringList() << "-v" << "zh";
speakEkhoEditrMonitor(QString(u8"现在是第 %1").arg(x+1), arguments, 175);
});
});
}
// ...
void NddPluginImplement::speakeSpeckEditrMonitor(const QString &text) {
QStringList arguments = QStringList() << "-v" << "en" << "-s" << "450" << text;
speakeSpeck(arguments);
}
void NddPluginImplement::speakeSpeckEditrMonitor(const QString &text, int speed) {
QStringList arguments = QStringList() << "-v" << "en" << "-s" << QString::number(speed) << text;
speakeSpeck(arguments);
}
void NddPluginImplement::speakeSpeckEditrMonitor(const QString &text, QStringList &arguments, int speed)
{
arguments << "-s" << QString::number(speed) << text;
speakeSpeck(arguments);
}
void NddPluginImplement::speakEkhoEditrMonitor(const QString &text)
{
QStringList arguments = QStringList() << text;
speakEkho(arguments);
}
void NddPluginImplement::speakEkhoEditrMonitor(const QString &text, int speed)
{
QStringList arguments = QStringList() << "-s" << QString::number(speed) << text;
speakeSpeck(arguments);
}
void NddPluginImplement::speakEkhoEditrMonitor(const QString &text, QStringList &arguments, int speed)
{
arguments << "-s" << QString::number(speed) << text;
speakeSpeck(arguments);
}
void NddPluginImplement::speakeSpeck(const QString &text) {
QStringList arguments = QStringList() << "-v" << "zh" << "-s" << "600";
arguments << "";
speakeSpeck(arguments);
}
void NddPluginImplement::speakeSpeck(const QStringList &arguments)
{
if (process->state() == QProcess::Running) {
return;
}
QString eSpeakEngine = ttsSettingWidget->espeakEngine->text();
if (!QFile::exists(eSpeakEngine)) {
return;
}
QDir eSpeakEngineDir = QFileInfo(eSpeakEngine).absoluteDir();
if (process->state() == QProcess::NotRunning) {
process->start(eSpeakEngine, arguments);
if (!process->waitForStarted(1000)) {
qDebug() << "Failed to start espeak process";
return;
}
}
}
void NddPluginImplement::speakEkho(const QString &text) {
QStringList arguments = QStringList() << getCurrentEditFunc()->text();
speakEkho(arguments);
}
void NddPluginImplement::speakEkho(const QStringList &arguments)
{
if (process->state() == QProcess::Running) {
process->kill();
}
QString ekhoEngine = ttsSettingWidget->ekhoEngine->text();
if (!QFile::exists(ekhoEngine)) {
return;
}
QDir ekhoEngineDir = QFileInfo(ekhoEngine).absoluteDir();
process->setWorkingDirectory(ekhoEngineDir.absolutePath());
if (process->state() == QProcess::NotRunning) {
process->start(ekhoEngine, arguments);
if (!process->waitForStarted(1000)) {
qDebug() << "Failed to start espeak process";
return;
}
}
}
void NddPluginImplement::on_pushButton_clicked()
{
}

View File

@ -0,0 +1,57 @@
#ifndef NDD_PLUGIN_IMPLEMENT_H
#define NDD_PLUGIN_IMPLEMENT_H
#include <QMainWindow>
#include <QMenu>
#include <QProcess>
class QsciScintilla;
namespace Ui {
class NddPluginImplement;
};
class NddPluginImplement : public QMainWindow
{
Q_OBJECT
public:
explicit NddPluginImplement(QWidget *parent = nullptr, QsciScintilla *pEdit = nullptr);
~NddPluginImplement();
void setMenuActions(QMenu *menu);
void setCurrentEditFunc(std::function<QsciScintilla* ()> func) {
getCurrentEditFunc = func;
}
private slots:
void onReadyRead();
void speakeSpeck(const QString &text);
void speakeSpeck(const QStringList &arguments);
void speakEkho(const QString &text);
void speakEkho(const QStringList &arguments);
void speakeSpeckEditrMonitor(const QString &text);
void speakeSpeckEditrMonitor(const QString &text, int speed);
void speakeSpeckEditrMonitor(const QString &text, QStringList &arguments, int speed);
void speakEkhoEditrMonitor(const QString &text);
void speakEkhoEditrMonitor(const QString &text, int speed);
void speakEkhoEditrMonitor(const QString &text, QStringList &arguments, int speed);
void on_pushButton_clicked();
private:
// 目前看来需要准备一个完整内部状态
QWidget *currentWidget;
QsciScintilla *currentEdit;
std::function<QsciScintilla* ()> getCurrentEditFunc;
QProcess *process = new QProcess;
private:
Ui::NddPluginImplement *ttsSettingWidget;
};
#endif // NDD_PLUGIN_IMPLEMENT_H

View File

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NddPluginImplement</class>
<widget class="QMainWindow" name="NddPluginImplement">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>571</width>
<height>370</height>
</rect>
</property>
<property name="windowTitle">
<string>TTS Settings Panel</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>TTS Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>TTS Engine(eSpeak):</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="espeakEngine">
<property name="text">
<string>/usr/bin/espeak</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_2">
<property name="text">
<string>Open</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>TTS Engine(Ekho):</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="ekhoEngine">
<property name="text">
<string>/usr/bin/ekho</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>Open</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>226</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>571</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,11 @@
#include <qobject.h>
#include <qstring.h>
#include <include/pluginGl.h>
#include <functional>
#include <qsciscintilla.h>
#include "ndd_plugin_implement.h"
NOTEPAD_PLUGIN_METADATA_IDENTIFY_V1(u8"TTS 语音合成", "0.1", "zinface", u8"二级菜单高级插件", "");
NOTEPAD_PLUGIN_METADATA_IMPLEMENT_V1(NddPluginImplement, false);