mirror of https://gitee.com/cxasm/notepad--.git
cmake-plugin: 添加基于源代码构建的 TTS 语音合成插件示例,运行时需要指定引擎
This commit is contained in:
parent
721bfdb056
commit
e50b7d3ef0
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
# )
|
|
@ -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()
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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>
|
|
@ -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);
|
Loading…
Reference in New Issue