1. 优化了与服务端数据传输时的架构设计
2. 解决了一些杂项内容
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
cmake-build-debug
|
cmake-build-debug
|
||||||
|
cmake-build-release
|
||||||
build
|
build
|
||||||
|
|
||||||
# clion
|
# clion
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.30)
|
cmake_minimum_required(VERSION 3.30)
|
||||||
project(Yosuga VERSION 1.0)
|
project(Yosuga VERSION 1.0)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20) # C++20
|
||||||
set(CMAKE_AUTOMOC ON)
|
set(CMAKE_AUTOMOC ON)
|
||||||
set(CMAKE_AUTORCC ON)
|
set(CMAKE_AUTORCC ON)
|
||||||
set(CMAKE_AUTOUIC ON)
|
set(CMAKE_AUTOUIC ON)
|
||||||
|
|||||||
@@ -4,8 +4,24 @@
|
|||||||

|

|
||||||

|

|
||||||
|
|
||||||
|
欢迎访问本项目。
|
||||||
|
|
||||||
|
首先向你介绍一下Yosuga这个项目:
|
||||||
|
|
||||||
|
本项目的作者是Misakiotoha(みさきおとは[見崎音羽])。[call me "Misaki" でいいよ]
|
||||||
|
|
||||||
|
之所以叫Yosuga,这个词来源日语当中的单词"縁"的发音,其意思是"缘分,关系"。
|
||||||
|
|
||||||
|
本项目分为三个部分:
|
||||||
|
1. Yosuga:这是项目的前端部分,是Yosuga与用户交互的一层,采用C++20 + Qt6.6.3编写,使用到的核心外部库为Live2D For C++ SDK。
|
||||||
|
2. Yosuga_server:这是项目的后端部分,是Yosuga的核心,采用python3.11编写,使用到的外部库较多,负责联系项目的各个部分。
|
||||||
|
3. Yosuga_embedded:这是项目的拓展部分,使得Yosuga对嵌入式设备拥有几乎完全的自定义控制能力,采用C语言编写,只使用到了cJSON库,平台无关,增强了Yosuga与外界的交互能力。
|
||||||
|
|
||||||
|
_**本项目为Yosuga.**_
|
||||||
|
|
||||||
本项目使用CMake构建,基于C++Qt6.6.3以及Live2D官方SDK(CubismSdkForNative-5-r.4.1)实现Live2D桌面宠物
|
本项目使用CMake构建,基于C++Qt6.6.3以及Live2D官方SDK(CubismSdkForNative-5-r.4.1)实现Live2D桌面宠物
|
||||||
(本项目由Yosuga[Qt5] 发展更新而来,项目架构与代码都有所不同,最显著的特点是本项目支持多平台)
|
(本项目由Yosuga[Qt5] 发展更新而来,项目架构与代码都有所不同,最显著的特点是本项目支持多平台)
|
||||||
|
|
||||||
环境为:
|
环境为:
|
||||||
|
|
||||||
- cmake version 3.5
|
- cmake version 3.5
|
||||||
|
|||||||
@@ -7,10 +7,7 @@
|
|||||||
* 虽然变得方便了,但也带来了危险,如果你肆意通过中介指针去调用GLCore的成员函数
|
* 虽然变得方便了,但也带来了危险,如果你肆意通过中介指针去调用GLCore的成员函数
|
||||||
* 可能会导致渲染问题等
|
* 可能会导致渲染问题等
|
||||||
*/
|
*/
|
||||||
|
#pragma once
|
||||||
#ifndef YOSUGA_APPCONTEXT_H
|
|
||||||
#define YOSUGA_APPCONTEXT_H
|
|
||||||
|
|
||||||
#include "GLCore.h"
|
#include "GLCore.h"
|
||||||
|
|
||||||
class AppContext {
|
class AppContext {
|
||||||
@@ -24,5 +21,3 @@ public:
|
|||||||
private:
|
private:
|
||||||
static inline GLCore* s_glCore = nullptr; // C++17 内联静态成员
|
static inline GLCore* s_glCore = nullptr; // C++17 内联静态成员
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //YOSUGA_APPCONTEXT_H
|
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ GLCore::GLCore(const int width, const int height, QWidget *parent)
|
|||||||
this->setWindowFlag(Qt::Tool); // 隐藏应用程序图标
|
this->setWindowFlag(Qt::Tool); // 隐藏应用程序图标
|
||||||
this->setAttribute(Qt::WA_TranslucentBackground); // 设置窗口背景透明
|
this->setAttribute(Qt::WA_TranslucentBackground); // 设置窗口背景透明
|
||||||
|
|
||||||
|
|
||||||
// 帧率控制初始化
|
// 帧率控制初始化
|
||||||
frameTimer = new QTimer(this);
|
frameTimer = new QTimer(this);
|
||||||
connect(frameTimer, &QTimer::timeout, [&]() {
|
connect(frameTimer, &QTimer::timeout, [&]() {
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
//
|
||||||
|
// Created by misaki on 2026/1/13.
|
||||||
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据传输对象 (DTO) 定义
|
||||||
|
* AudioDataTransferObject
|
||||||
|
* 与Yosuga_server对等
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <QObject>
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonValue>
|
||||||
|
#include "DataTransferObjectBase.h"
|
||||||
|
// 前向声明,减少依赖
|
||||||
|
class QJsonObject;
|
||||||
|
class AudioDataTransferObject final : public DataTransferObjectBase{
|
||||||
|
public:
|
||||||
|
// 构造函数(带默认值)
|
||||||
|
explicit AudioDataTransferObject(const QString& owner = "client",
|
||||||
|
bool isStream = false,
|
||||||
|
bool isStart = false,
|
||||||
|
bool isEnd = false,
|
||||||
|
int sequence = 0,
|
||||||
|
const QByteArray& data = {},
|
||||||
|
int sampleRate = 16000,
|
||||||
|
int channelCount = 1,
|
||||||
|
int bitDepth = 16,
|
||||||
|
double duration = 0.0,
|
||||||
|
const QString& text = "");
|
||||||
|
// 静态工厂方法
|
||||||
|
static AudioDataTransferObject fromJson(const QJsonObject& json);
|
||||||
|
|
||||||
|
[[nodiscard]] QString type() const override { return "audio_data"; }
|
||||||
|
|
||||||
|
// 序列化
|
||||||
|
[[nodiscard]] QJsonObject toJson() const override; // 通过多态即可统一调用方式
|
||||||
|
|
||||||
|
// 链式调用设置
|
||||||
|
AudioDataTransferObject& setData(const QString& key, const QJsonValue& value) override;
|
||||||
|
|
||||||
|
[[nodiscard]] QString owner() const { return m_owner; }
|
||||||
|
[[nodiscard]] bool isStream() const { return m_isStream; }
|
||||||
|
[[nodiscard]] bool isStart() const { return m_isStart; }
|
||||||
|
[[nodiscard]] bool isEnd() const { return m_isEnd; }
|
||||||
|
[[nodiscard]] int sequence() const { return m_sequence; }
|
||||||
|
[[nodiscard]] QByteArray audioData() const { return m_data; }
|
||||||
|
[[nodiscard]] int sampleRate() const { return m_sampleRate; }
|
||||||
|
[[nodiscard]] int channelCount() const { return m_channelCount; }
|
||||||
|
[[nodiscard]] int bitDepth() const { return m_bitDepth; }
|
||||||
|
[[nodiscard]] double duration() const { return m_duration; }
|
||||||
|
[[nodiscard]] QString text() const { return m_text; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_owner; /// 音频数据的拥有者(server or client)
|
||||||
|
bool m_isStream; /// 音频数据是否为流式数据
|
||||||
|
bool m_isStart; /// 音频数据是否开始(流式时有效)
|
||||||
|
bool m_isEnd; /// 音频数据是否结束(流式时有效)
|
||||||
|
int m_sequence; /// 音频数据块序列号(流式时有效)
|
||||||
|
QByteArray m_data; /// 音频数据,流式时为分块数据,base64编码
|
||||||
|
int m_sampleRate; /// 音频采样率
|
||||||
|
int m_channelCount; /// 音频通道数
|
||||||
|
int m_bitDepth; /// 音频采样位数
|
||||||
|
double m_duration; /// 音频时长
|
||||||
|
QString m_text; /// 音频对应的文本
|
||||||
|
};
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
//
|
||||||
|
// Created by misaki on 2026/1/13.
|
||||||
|
//
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class DataTransferObjectBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~DataTransferObjectBase() = default;
|
||||||
|
|
||||||
|
// 获取类型,用于区分不同的DTO子类对象
|
||||||
|
[[nodiscard]] virtual QString type() const = 0;
|
||||||
|
|
||||||
|
// 序列化
|
||||||
|
[[nodiscard]] virtual QJsonObject toJson() const = 0;
|
||||||
|
|
||||||
|
// 链式调用设置
|
||||||
|
virtual DataTransferObjectBase& setData(const QString& key, const QJsonValue& value) = 0;
|
||||||
|
};
|
||||||
+4
-36
@@ -25,34 +25,8 @@
|
|||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
/**
|
#include "DataTransferObjectBase.h"
|
||||||
* 数据传输对象 (DTO) 定义
|
#include "AudioDataTransferObject.h"
|
||||||
*/
|
|
||||||
// 音频文本捆绑数据结构
|
|
||||||
struct AudioDataPacket {
|
|
||||||
QString text; // 文本内容
|
|
||||||
QByteArray audioData; // 音频原始数据 (二进制)
|
|
||||||
int sampleRate; // 采样率
|
|
||||||
int channels; // 通道数
|
|
||||||
qint64 duration; // 时长 (ms)
|
|
||||||
|
|
||||||
AudioDataPacket() : sampleRate(16000), channels(1), duration(0) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 控制指令数据结构 (预留)
|
|
||||||
enum class ControlType {
|
|
||||||
Click,
|
|
||||||
Input,
|
|
||||||
Scroll
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ControlDataPacket {
|
|
||||||
ControlType action;
|
|
||||||
int x;
|
|
||||||
int y;
|
|
||||||
QString extraData;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NetworkDO
|
* NetworkDO
|
||||||
*/
|
*/
|
||||||
@@ -75,13 +49,11 @@ public:
|
|||||||
void registerSender(SenderFunc sender);
|
void registerSender(SenderFunc sender);
|
||||||
|
|
||||||
// 业务发送函数
|
// 业务发送函数
|
||||||
void sendAudioPacket(const AudioDataPacket& packet);
|
void sendPacket(const DataTransferObjectBase& packet);
|
||||||
void sendControlPacket(const ControlDataPacket& packet);
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
// 业务接收信号
|
// 业务接收信号
|
||||||
void audioPacketReceived(const AudioDataPacket& packet); // 音频数据准备完成信号
|
void audioPacketReceived(const AudioDataTransferObject& packet); // 音频数据准备完成信号
|
||||||
void controlPacketReceived(const ControlDataPacket& packet); // 控制数据准备完成信号
|
|
||||||
void errorOccurred(const QString& errorMsg); // 错误信号
|
void errorOccurred(const QString& errorMsg); // 错误信号
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
@@ -92,10 +64,6 @@ public:
|
|||||||
private:
|
private:
|
||||||
// 构造/析构函数私有化
|
// 构造/析构函数私有化
|
||||||
explicit NetworkDO(QObject *parent = nullptr);
|
explicit NetworkDO(QObject *parent = nullptr);
|
||||||
|
|
||||||
// 内部处理逻辑
|
|
||||||
void handleAudioMessage(const QJsonObject& data);
|
|
||||||
|
|
||||||
static QScopedPointer<NetworkDO> m_instance;
|
static QScopedPointer<NetworkDO> m_instance;
|
||||||
static QMutex m_mutex;
|
static QMutex m_mutex;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
//
|
||||||
|
// Created by misaki on 2026/1/13.
|
||||||
|
//
|
||||||
|
#include "AudioDataTransferObject.h"
|
||||||
|
#include <QJsonValue>
|
||||||
|
// 构造函数实现(初始化列表)
|
||||||
|
AudioDataTransferObject::AudioDataTransferObject(const QString& owner,
|
||||||
|
bool isStream,
|
||||||
|
bool isStart,
|
||||||
|
bool isEnd,
|
||||||
|
int sequence,
|
||||||
|
const QByteArray& data,
|
||||||
|
int sampleRate,
|
||||||
|
int channelCount,
|
||||||
|
int bitDepth,
|
||||||
|
double duration,
|
||||||
|
const QString& text)
|
||||||
|
: m_owner(owner)
|
||||||
|
, m_isStream(isStream)
|
||||||
|
, m_isStart(isStart)
|
||||||
|
, m_isEnd(isEnd)
|
||||||
|
, m_sequence(sequence)
|
||||||
|
, m_data(data)
|
||||||
|
, m_sampleRate(sampleRate)
|
||||||
|
, m_channelCount(channelCount)
|
||||||
|
, m_bitDepth(bitDepth)
|
||||||
|
, m_duration(duration)
|
||||||
|
, m_text(text) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// 静态工厂方法:从 JSON 反序列化
|
||||||
|
AudioDataTransferObject AudioDataTransferObject::fromJson(const QJsonObject& json) {
|
||||||
|
// 逐个字段读取,不存在则用默认值
|
||||||
|
QString owner = json.value("Owner").toString("server");
|
||||||
|
bool isStream = json.value("isStream").toBool(false);
|
||||||
|
bool isStart = json.value("isStart").toBool(false);
|
||||||
|
bool isEnd = json.value("isEnd").toBool(false);
|
||||||
|
int sequence = json.value("sequence").toInt(0);
|
||||||
|
// 处理 base64 编码的 data 字段
|
||||||
|
QByteArray data;
|
||||||
|
if (json.contains("data")) {
|
||||||
|
const QString base64Str = json.value("data").toString();
|
||||||
|
data = QByteArray::fromBase64(base64Str.toUtf8());
|
||||||
|
}
|
||||||
|
int sampleRate = json.value("sampleRate").toInt(16000);
|
||||||
|
int channelCount = json.value("channelCount").toInt(1);
|
||||||
|
int bitDepth = json.value("bitDepth").toInt(16);
|
||||||
|
double duration = json.value("duration").toDouble(0.0);
|
||||||
|
QString text = json.value("text").toString();
|
||||||
|
|
||||||
|
// 调用构造函数创建对象
|
||||||
|
return AudioDataTransferObject(owner, isStream, isStart, isEnd,
|
||||||
|
sequence, data, sampleRate, channelCount,
|
||||||
|
bitDepth, duration, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 序列化为 JSON
|
||||||
|
QJsonObject AudioDataTransferObject::toJson() const {
|
||||||
|
QJsonObject json;
|
||||||
|
json["Owner"] = m_owner;
|
||||||
|
json["isStream"] = m_isStream;
|
||||||
|
json["isStart"] = m_isStart;
|
||||||
|
json["isEnd"] = m_isEnd;
|
||||||
|
json["sequence"] = m_sequence;
|
||||||
|
// data 字段 base64 编码
|
||||||
|
json["data"] = QString(m_data.toBase64());
|
||||||
|
json["sampleRate"] = m_sampleRate;
|
||||||
|
json["channelCount"] = m_channelCount;
|
||||||
|
json["bitDepth"] = m_bitDepth;
|
||||||
|
json["duration"] = m_duration;
|
||||||
|
json["text"] = m_text;
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 链式设置
|
||||||
|
AudioDataTransferObject& AudioDataTransferObject::setData(const QString& key,
|
||||||
|
const QJsonValue& value) {
|
||||||
|
if (key == "Owner") {
|
||||||
|
m_owner = value.toString();
|
||||||
|
} else if (key == "isStream") {
|
||||||
|
m_isStream = value.toBool();
|
||||||
|
} else if (key == "isStart") {
|
||||||
|
m_isStart = value.toBool();
|
||||||
|
} else if (key == "isEnd") {
|
||||||
|
m_isEnd = value.toBool();
|
||||||
|
} else if (key == "sequence") {
|
||||||
|
m_sequence = value.toInt();
|
||||||
|
} else if (key == "data") {
|
||||||
|
// 这里要求已是 base64 字符串
|
||||||
|
m_data = QByteArray::fromBase64(value.toString().toUtf8());
|
||||||
|
} else if (key == "sampleRate") {
|
||||||
|
m_sampleRate = value.toInt();
|
||||||
|
} else if (key == "channelCount") {
|
||||||
|
m_channelCount = value.toInt();
|
||||||
|
} else if (key == "bitDepth") {
|
||||||
|
m_bitDepth = value.toInt();
|
||||||
|
} else if (key == "duration") {
|
||||||
|
m_duration = value.toDouble();
|
||||||
|
} else if (key == "text") {
|
||||||
|
m_text = value.toString();
|
||||||
|
}
|
||||||
|
// 如果 key 不存在,默认忽略
|
||||||
|
return *this; // 返回自身引用,支持链式调用
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
//
|
||||||
|
// Created by misaki on 2026/1/13.
|
||||||
|
//
|
||||||
|
#include "DataTransferObjectBase.h"
|
||||||
+12
-52
@@ -48,71 +48,31 @@ void NetworkDO::registerSender(SenderFunc sender)
|
|||||||
m_sender = std::move(sender);
|
m_sender = std::move(sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkDO::sendAudioPacket(const AudioDataPacket& packet)
|
void NetworkDO::sendPacket(const DataTransferObjectBase &packet)
|
||||||
{
|
{
|
||||||
// 检查发送器是否已注入
|
// 检查发送器是否已注入
|
||||||
if (!m_sender) {
|
if (!m_sender) {
|
||||||
emit errorOccurred("Sender not registered! Call registerSender() first.");
|
emit errorOccurred("Sender not registered! Call registerSender() first.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 依赖注入 + 多态实现完美解耦
|
||||||
// 封装数据 (DTO -> JSON)
|
m_sender(packet.type(), packet.toJson());
|
||||||
QJsonObject dataObj;
|
|
||||||
dataObj["text"] = packet.text;
|
|
||||||
// 音频转 Base64 字符串传输
|
|
||||||
dataObj["audio"] = QString::fromLatin1(packet.audioData.toBase64());
|
|
||||||
dataObj["sampleRate"] = packet.sampleRate;
|
|
||||||
dataObj["channels"] = packet.channels;
|
|
||||||
dataObj["duration"] = packet.duration;
|
|
||||||
|
|
||||||
// 调用底层发送 (解耦)
|
|
||||||
// "textAudio" 是与后端约定的协议类型
|
|
||||||
m_sender("textAudio", dataObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkDO::sendControlPacket(const ControlDataPacket& packet)
|
|
||||||
{
|
|
||||||
if (!m_sender) return;
|
|
||||||
|
|
||||||
QJsonObject dataObj;
|
|
||||||
dataObj["action"] = static_cast<int>(packet.action);
|
|
||||||
dataObj["x"] = packet.x;
|
|
||||||
dataObj["y"] = packet.y;
|
|
||||||
|
|
||||||
m_sender("control", dataObj);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 接受并没有完全解耦
|
||||||
void NetworkDO::onDataReceived(const QString& type, const QJsonObject& data)
|
void NetworkDO::onDataReceived(const QString& type, const QJsonObject& data)
|
||||||
{
|
{
|
||||||
// 根据类型分发
|
// 根据类型分发数据包
|
||||||
if (type == "textAudio") {
|
// 为什么分发做在这里,而不是统一数据再去分发,如果不在这里做分发通知,分开发信号,而使用统一的信号
|
||||||
handleAudioMessage(data);
|
// 如果有多个观察者,让观察者自动识别数据包,这会导致信号广播,容易引起性能问题(因为这里依赖的是Qt的信号与槽机制)
|
||||||
|
// TODO: 在此处使用工厂模式,根据type内容快速创建对应的对象
|
||||||
|
if (type == "audio_data") {
|
||||||
|
emit audioPacketReceived(AudioDataTransferObject::fromJson(data)); // 构造并发送音频对象
|
||||||
}
|
}
|
||||||
else if (type == "control") {
|
else if (type == "control_data") {
|
||||||
// handleControlMessage(data);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
qWarning() << "[NetworkDO] Received unknown type:" << type;
|
qWarning() << "[NetworkDO] Received unknown type:" << type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkDO::handleAudioMessage(const QJsonObject& data)
|
|
||||||
{
|
|
||||||
AudioDataPacket packet;
|
|
||||||
|
|
||||||
// 解析基础字段 (JSON -> DTO)
|
|
||||||
packet.text = data.value("text").toString();
|
|
||||||
packet.sampleRate = data.value("sampleRate").toInt(16000);
|
|
||||||
packet.channels = data.value("channels").toInt(1);
|
|
||||||
// 注意类型转换,确保 long long 精度
|
|
||||||
packet.duration = static_cast<qint64>(data.value("duration").toDouble());
|
|
||||||
|
|
||||||
// 解析音频 (Base64 -> Binary)
|
|
||||||
QString base64Audio = data.value("audio").toString();
|
|
||||||
if (!base64Audio.isEmpty()) {
|
|
||||||
packet.audioData = QByteArray::fromBase64(base64Audio.toLatin1());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 通知上层业务
|
|
||||||
emit audioPacketReceived(packet);
|
|
||||||
}
|
|
||||||
@@ -117,13 +117,13 @@ void NetWorkPage::initWebSocketClient() {
|
|||||||
client->connectToServer(); // 连接
|
client->connectToServer(); // 连接
|
||||||
// 使用定时器延迟检查连接状态
|
// 使用定时器延迟检查连接状态
|
||||||
QTimer::singleShot(1000, this, [this, client]() {
|
QTimer::singleShot(1000, this, [this, client]() {
|
||||||
if (client->isConnected()) {
|
if (!client->isConnected()) {
|
||||||
ElaMessageBar::success(ElaMessageBarType::TopRight, "连通测试",
|
|
||||||
"连通测试成功", 800.0, this);
|
|
||||||
} else {
|
|
||||||
ElaMessageBar::warning(ElaMessageBarType::TopLeft, "连通测试",
|
ElaMessageBar::warning(ElaMessageBarType::TopLeft, "连通测试",
|
||||||
"无法连接到服务器,请检查地址和服务器状态", 1500.0, this);
|
"无法连接到服务器,请检查地址和服务器状态", 1500.0, this);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
ElaMessageBar::success(ElaMessageBarType::TopRight, "连通测试",
|
||||||
|
"连通测试成功", 800.0, this);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
connect(connectPushButton, &ElaPushButton::clicked, this, [this, client]() {
|
connect(connectPushButton, &ElaPushButton::clicked, this, [this, client]() {
|
||||||
@@ -146,28 +146,29 @@ void NetWorkPage::initWebSocketClient() {
|
|||||||
"正在连接服务器...", 800.0, this);
|
"正在连接服务器...", 800.0, this);
|
||||||
});
|
});
|
||||||
connect(disconnectPushButton, &ElaPushButton::clicked, this, [this, client]() {
|
connect(disconnectPushButton, &ElaPushButton::clicked, this, [this, client]() {
|
||||||
if (client->isConnected()) {
|
if (!client->isConnected()) {
|
||||||
|
ElaMessageBar::information(ElaMessageBarType::BottomRight, "断开连接",
|
||||||
|
"当前未连接", 800.0, this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
client->disconnectFromServer();
|
client->disconnectFromServer();
|
||||||
ElaMessageBar::success(ElaMessageBarType::TopRight, "断开连接",
|
ElaMessageBar::success(ElaMessageBarType::TopRight, "断开连接",
|
||||||
"已断开连接", 800.0, this);
|
"已断开连接", 800.0, this);
|
||||||
} else {
|
});
|
||||||
|
connect(sendTestPushButton, &ElaPushButton::clicked, this, [this, netDO, client]() {
|
||||||
|
if (!client->isConnected()) {
|
||||||
ElaMessageBar::information(ElaMessageBarType::BottomRight, "断开连接",
|
ElaMessageBar::information(ElaMessageBarType::BottomRight, "断开连接",
|
||||||
"当前未连接", 800.0, this);
|
"当前未连接", 800.0, this);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
connect(sendTestPushButton, &ElaPushButton::clicked, this, [this, netDO]() {
|
|
||||||
// 创建数据包
|
// 创建数据包
|
||||||
AudioDataPacket packet;
|
AudioDataTransferObject packet;
|
||||||
packet.text = "Hello, World!";
|
packet.setData("Owner", "client")
|
||||||
// 填入元数据
|
.setData("isStream", true)
|
||||||
packet.sampleRate = 16000;
|
.setData("sequence", 42)
|
||||||
packet.channels = 1;
|
.setData("text", "Hello World")
|
||||||
packet.duration = 2500; // 2.5秒
|
.setData("data", "SGVsbG8gV29ybGQ="); // 填入音频数据 (Hello World的base64)
|
||||||
// 填入音频数据 (模拟从文件或录音设备读取的二进制数据)
|
netDO->sendPacket(packet);
|
||||||
QByteArray rawAudioData;
|
|
||||||
rawAudioData.append("...这里是真实的PCM或WAV二进制数据...");
|
|
||||||
packet.audioData = rawAudioData;
|
|
||||||
netDO->sendAudioPacket(packet);
|
|
||||||
ElaMessageBar::success(ElaMessageBarType::TopRight, "发送测试",
|
ElaMessageBar::success(ElaMessageBarType::TopRight, "发送测试",
|
||||||
"已成功发送数据包", 1000.0, this);
|
"已成功发送数据包", 1000.0, this);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user