1. 完成了对嵌入式Linux平台支持的相关代码的合并,同时对于OpenGL ES平台的shader进行了重写以适配ES平台,不过暂时还未加入到CMakeLists构建脚本当中,预计在下次提交中加入。
This commit is contained in:
@@ -1,6 +1,10 @@
|
|||||||
cmake-build-debug
|
cmake-build-debug
|
||||||
cmake-build-release
|
cmake-build-release
|
||||||
build
|
build
|
||||||
|
build_linux
|
||||||
|
build_rk3566
|
||||||
|
|
||||||
# clion
|
# clion
|
||||||
/.idea
|
/.idea
|
||||||
|
|
||||||
|
deploy.sh
|
||||||
|
|||||||
@@ -284,6 +284,12 @@ void LAppLive2DManager::LoadModelFromPath(const std::string& modelPath, const st
|
|||||||
_models.PushBack(new LAppModel()); // 这样在加载的时候都使用的models[0]这一个位置,自行实现模型选择器要注意注意
|
_models.PushBack(new LAppModel()); // 这样在加载的时候都使用的models[0]这一个位置,自行实现模型选择器要注意注意
|
||||||
_models[0]->LoadAssets(modelPathStr.GetRawString(), modelJsonName.GetRawString());
|
_models[0]->LoadAssets(modelPathStr.GetRawString(), modelJsonName.GetRawString());
|
||||||
|
|
||||||
|
if (_models[0]->GetModel() == nullptr)
|
||||||
|
{
|
||||||
|
LAppPal::PrintLogLn("[APP]Failed to load model: %s / %s", modelPath.c_str(), fileName.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 加载完后根据模型大小来重新设置当前窗口大小
|
// 加载完后根据模型大小来重新设置当前窗口大小
|
||||||
const int width = static_cast<int>(_models[0]->GetModel()->GetCanvasWidthPixel() / 15.0);
|
const int width = static_cast<int>(_models[0]->GetModel()->GetCanvasWidthPixel() / 15.0);
|
||||||
const int height = static_cast<int>(_models[0]->GetModel()->GetCanvasHeightPixel() / 15.0);
|
const int height = static_cast<int>(_models[0]->GetModel()->GetCanvasHeightPixel() / 15.0);
|
||||||
|
|||||||
@@ -168,6 +168,11 @@ void LAppModel::LoadAssets(const csmChar* dir, const csmChar* fileName)
|
|||||||
const csmString path = csmString(dir) + fileName;
|
const csmString path = csmString(dir) + fileName;
|
||||||
|
|
||||||
csmByte* buffer = CreateBuffer(path.GetRawString(), &size);
|
csmByte* buffer = CreateBuffer(path.GetRawString(), &size);
|
||||||
|
if (buffer == nullptr)
|
||||||
|
{
|
||||||
|
LAppPal::PrintLogLn("[APP]Failed to load model setting file: %s", path.GetRawString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
ICubismModelSetting* setting = new CubismModelSettingJson(buffer, size);
|
ICubismModelSetting* setting = new CubismModelSettingJson(buffer, size);
|
||||||
DeleteBuffer(buffer, path.GetRawString());
|
DeleteBuffer(buffer, path.GetRawString());
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,112 @@
|
|||||||
|
{
|
||||||
|
"version": 6,
|
||||||
|
"configurePresets": [
|
||||||
|
{
|
||||||
|
"name": "base",
|
||||||
|
"hidden": true,
|
||||||
|
"description": "所有平台的公共基础配置"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "desktop-base",
|
||||||
|
"hidden": true,
|
||||||
|
"inherits": "base",
|
||||||
|
"description": "桌面平台公共基础配置:固定 Qt 6.6.3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "embedded-base",
|
||||||
|
"hidden": true,
|
||||||
|
"inherits": "base",
|
||||||
|
"description": "嵌入式交叉编译公共基础配置,暂时只支持arm64架构",
|
||||||
|
"generator": "Unix Makefiles",
|
||||||
|
"cacheVariables": {
|
||||||
|
"TARGET_ARCH": "arm64",
|
||||||
|
"GLESv2_FOUND": "TRUE",
|
||||||
|
"EGL_FOUND": "TRUE",
|
||||||
|
"HAVE_EGL": "ON",
|
||||||
|
"HAVE_GLESv2": "ON"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "linux-native",
|
||||||
|
"inherits": "desktop-base",
|
||||||
|
"displayName": "Linux Desktop x86_64",
|
||||||
|
"description": "本地 Linux 桌面编译 (GCC + Ninja),Qt 6.6.3 固定路径",
|
||||||
|
"binaryDir": "${sourceDir}/build_linux",
|
||||||
|
"generator": "Ninja",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Debug",
|
||||||
|
"CMAKE_PREFIX_PATH": "/home/misaki/Qt6.3/6.6.3/gcc_64"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "windows-mingw",
|
||||||
|
"inherits": "desktop-base",
|
||||||
|
"displayName": "Windows MinGW x64",
|
||||||
|
"description": "Windows 桌面编译 (MinGW-w64 + Ninja),Qt 6.6.3 固定路径",
|
||||||
|
"binaryDir": "${sourceDir}/build_win_mingw",
|
||||||
|
"generator": "Ninja",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Debug",
|
||||||
|
"CMAKE_PREFIX_PATH": "C:/Qt/6.6.3/mingw_64",
|
||||||
|
"CMAKE_C_COMPILER": "C:/mingw64/bin/gcc.exe",
|
||||||
|
"CMAKE_CXX_COMPILER": "C:/mingw64/bin/g++.exe"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "windows-msvc",
|
||||||
|
"inherits": "desktop-base",
|
||||||
|
"displayName": "Windows MSVC x64",
|
||||||
|
"description": "Windows 桌面编译 (Visual Studio 2022),Qt 6.6.3 固定路径",
|
||||||
|
"binaryDir": "${sourceDir}/build_win_msvc",
|
||||||
|
"generator": "Visual Studio 17 2022",
|
||||||
|
"architecture": {
|
||||||
|
"value": "x64",
|
||||||
|
"strategy": "set"
|
||||||
|
},
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Debug",
|
||||||
|
"CMAKE_PREFIX_PATH": "C:/Qt/6.6.3/msvc2019_64"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "macos-native",
|
||||||
|
"inherits": "desktop-base",
|
||||||
|
"displayName": "macOS x86_64",
|
||||||
|
"description": "macOS 桌面编译 (Clang + Ninja),Qt 6.6.3 固定路径",
|
||||||
|
"binaryDir": "${sourceDir}/build_macos",
|
||||||
|
"generator": "Ninja",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Debug",
|
||||||
|
"CMAKE_PREFIX_PATH": "/Users/misaki/Qt/6.6.3/macos"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "rk3566-cross",
|
||||||
|
"inherits": "embedded-base",
|
||||||
|
"displayName": "RK3566 Cross Compile",
|
||||||
|
"description": "RK3566 嵌入式交叉编译 (ARM64 + Qt6.6.3 EGLFS)",
|
||||||
|
"binaryDir": "${sourceDir}/build_rk3566",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Debug",
|
||||||
|
"CMAKE_TOOLCHAIN_FILE": "/home/misaki/MisakiCodes/rk3566-sdk/toolchain/qt_toolchain.cmake",
|
||||||
|
"CMAKE_PREFIX_PATH": "/home/misaki/MisakiCodes/rk3566-sdk/3rd/Qt6.6.3",
|
||||||
|
"GLESv2_INCLUDE_DIR": "/home/misaki/MisakiCodes/rk3566-sdk/host/aarch64-buildroot-linux-gnu/sysroot/usr/include",
|
||||||
|
"GLESv2_LIBRARY": "/home/misaki/MisakiCodes/rk3566-sdk/host/aarch64-buildroot-linux-gnu/sysroot/usr/lib/libGLESv2.so",
|
||||||
|
"EGL_INCLUDE_DIR": "/home/misaki/MisakiCodes/rk3566-sdk/host/aarch64-buildroot-linux-gnu/sysroot/usr/include",
|
||||||
|
"EGL_LIBRARY": "/home/misaki/MisakiCodes/rk3566-sdk/host/aarch64-buildroot-linux-gnu/sysroot/usr/lib/libEGL.so"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"buildPresets": [
|
||||||
|
{ "name": "linux-debug", "configurePreset": "linux-native", "configuration": "Debug" },
|
||||||
|
{ "name": "linux-release", "configurePreset": "linux-native", "configuration": "Release" },
|
||||||
|
{ "name": "win-mingw-debug", "configurePreset": "windows-mingw", "configuration": "Debug" },
|
||||||
|
{ "name": "win-mingw-release", "configurePreset": "windows-mingw", "configuration": "Release" },
|
||||||
|
{ "name": "win-msvc-debug", "configurePreset": "windows-msvc", "configuration": "Debug" },
|
||||||
|
{ "name": "win-msvc-release", "configurePreset": "windows-msvc", "configuration": "Release" },
|
||||||
|
{ "name": "macos-debug", "configurePreset": "macos-native", "configuration": "Debug" },
|
||||||
|
{ "name": "macos-release", "configurePreset": "macos-native", "configuration": "Release" },
|
||||||
|
{ "name": "rk3566-debug", "configurePreset": "rk3566-cross", "configuration": "Debug" },
|
||||||
|
{ "name": "rk3566-release", "configurePreset": "rk3566-cross", "configuration": "Release" }
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
#include <QtWidgets/QApplication>
|
#include <QtWidgets/QApplication>
|
||||||
|
#if !defined(EMBEDDED_LINUX)
|
||||||
#include "ElaApplication.h"
|
#include "ElaApplication.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <QFontDatabase>
|
#include <QFontDatabase>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include "GLCore.h"
|
#include "GLCore.h"
|
||||||
@@ -12,9 +15,11 @@ int main(int argc, char *argv[])
|
|||||||
{
|
{
|
||||||
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); // 允许多个窗口使用同一个OpenGL上下文(用于实现Live2D模型的异步加载)
|
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); // 允许多个窗口使用同一个OpenGL上下文(用于实现Live2D模型的异步加载)
|
||||||
QApplication a(argc, argv);
|
QApplication a(argc, argv);
|
||||||
|
#if !defined(EMBEDDED_LINUX)
|
||||||
eApp->init();
|
eApp->init();
|
||||||
// 设置云母效果图片
|
// 设置云母效果图片
|
||||||
eApp->setMicaImagePath("Resources/Pic/Others/MicaBase.png");
|
eApp->setMicaImagePath("Resources/Pic/Others/MicaBase.png");
|
||||||
|
#endif
|
||||||
#ifdef Q_OS_UNIX
|
#ifdef Q_OS_UNIX
|
||||||
// 设置日志过滤规则,将这个特定分类的警告级别设置为“关闭”
|
// 设置日志过滤规则,将这个特定分类的警告级别设置为“关闭”
|
||||||
QLoggingCategory::setFilterRules("qt.multimedia.ffmpeg.libsymbolsresolver.warning=false");
|
QLoggingCategory::setFilterRules("qt.multimedia.ffmpeg.libsymbolsresolver.warning=false");
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#version 100
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
varying vec2 v_texCoord;
|
||||||
|
uniform sampler2D s_texture0;
|
||||||
|
uniform vec4 u_baseColor;
|
||||||
|
uniform vec4 u_multiplyColor;
|
||||||
|
uniform vec4 u_screenColor;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 texColor = texture2D(s_texture0, v_texCoord);
|
||||||
|
texColor.rgb = texColor.rgb * u_multiplyColor.rgb;
|
||||||
|
texColor.rgb = texColor.rgb + u_screenColor.rgb - (texColor.rgb * u_screenColor.rgb);
|
||||||
|
vec4 color = texColor * u_baseColor;
|
||||||
|
gl_FragColor = vec4(color.rgb * color.a, color.a);
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#version 100
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
varying vec2 v_texCoord;
|
||||||
|
varying vec4 v_clipPos;
|
||||||
|
uniform sampler2D s_texture0;
|
||||||
|
uniform sampler2D s_texture1;
|
||||||
|
uniform vec4 u_channelFlag;
|
||||||
|
uniform vec4 u_baseColor;
|
||||||
|
uniform vec4 u_multiplyColor;
|
||||||
|
uniform vec4 u_screenColor;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 texColor = texture2D(s_texture0, v_texCoord);
|
||||||
|
texColor.rgb = texColor.rgb * u_multiplyColor.rgb;
|
||||||
|
texColor.rgb = texColor.rgb + u_screenColor.rgb - (texColor.rgb * u_screenColor.rgb);
|
||||||
|
vec4 col_formask = texColor * u_baseColor;
|
||||||
|
col_formask.rgb = col_formask.rgb * col_formask.a;
|
||||||
|
vec4 clipMask = (1.0 - texture2D(s_texture1, v_clipPos.xy / v_clipPos.w)) * u_channelFlag;
|
||||||
|
float maskVal = clipMask.r + clipMask.g + clipMask.b + clipMask.a;
|
||||||
|
col_formask = col_formask * maskVal;
|
||||||
|
gl_FragColor = col_formask;
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#version 100
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
varying vec2 v_texCoord;
|
||||||
|
varying vec4 v_clipPos;
|
||||||
|
uniform sampler2D s_texture0;
|
||||||
|
uniform sampler2D s_texture1;
|
||||||
|
uniform vec4 u_channelFlag;
|
||||||
|
uniform vec4 u_baseColor;
|
||||||
|
uniform vec4 u_multiplyColor;
|
||||||
|
uniform vec4 u_screenColor;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 texColor = texture2D(s_texture0, v_texCoord);
|
||||||
|
texColor.rgb = texColor.rgb * u_multiplyColor.rgb;
|
||||||
|
texColor.rgb = texColor.rgb + u_screenColor.rgb - (texColor.rgb * u_screenColor.rgb);
|
||||||
|
vec4 col_formask = texColor * u_baseColor;
|
||||||
|
col_formask.rgb = col_formask.rgb * col_formask.a;
|
||||||
|
vec4 clipMask = (1.0 - texture2D(s_texture1, v_clipPos.xy / v_clipPos.w)) * u_channelFlag;
|
||||||
|
float maskVal = clipMask.r + clipMask.g + clipMask.b + clipMask.a;
|
||||||
|
col_formask = col_formask * (1.0 - maskVal);
|
||||||
|
gl_FragColor = col_formask;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#version 100
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
varying vec2 v_texCoord;
|
||||||
|
varying vec4 v_clipPos;
|
||||||
|
uniform sampler2D s_texture0;
|
||||||
|
uniform sampler2D s_texture1;
|
||||||
|
uniform vec4 u_channelFlag;
|
||||||
|
uniform vec4 u_baseColor;
|
||||||
|
uniform vec4 u_multiplyColor;
|
||||||
|
uniform vec4 u_screenColor;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 texColor = texture2D(s_texture0, v_texCoord);
|
||||||
|
texColor.rgb = texColor.rgb * u_multiplyColor.rgb;
|
||||||
|
texColor.rgb = (texColor.rgb + u_screenColor.rgb * texColor.a) - (texColor.rgb * u_screenColor.rgb);
|
||||||
|
vec4 col_formask = texColor * u_baseColor;
|
||||||
|
vec4 clipMask = (1.0 - texture2D(s_texture1, v_clipPos.xy / v_clipPos.w)) * u_channelFlag;
|
||||||
|
float maskVal = clipMask.r + clipMask.g + clipMask.b + clipMask.a;
|
||||||
|
col_formask = col_formask * (1.0 - maskVal);
|
||||||
|
gl_FragColor = col_formask;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#version 100
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
varying vec2 v_texCoord;
|
||||||
|
varying vec4 v_clipPos;
|
||||||
|
uniform sampler2D s_texture0;
|
||||||
|
uniform sampler2D s_texture1;
|
||||||
|
uniform vec4 u_channelFlag;
|
||||||
|
uniform vec4 u_baseColor;
|
||||||
|
uniform vec4 u_multiplyColor;
|
||||||
|
uniform vec4 u_screenColor;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 texColor = texture2D(s_texture0, v_texCoord);
|
||||||
|
texColor.rgb = texColor.rgb * u_multiplyColor.rgb;
|
||||||
|
texColor.rgb = (texColor.rgb + u_screenColor.rgb * texColor.a) - (texColor.rgb * u_screenColor.rgb);
|
||||||
|
vec4 col_formask = texColor * u_baseColor;
|
||||||
|
vec4 clipMask = (1.0 - texture2D(s_texture1, v_clipPos.xy / v_clipPos.w)) * u_channelFlag;
|
||||||
|
float maskVal = clipMask.r + clipMask.g + clipMask.b + clipMask.a;
|
||||||
|
col_formask = col_formask * maskVal;
|
||||||
|
gl_FragColor = col_formask;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#version 100
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
varying vec2 v_texCoord;
|
||||||
|
uniform sampler2D s_texture0;
|
||||||
|
uniform vec4 u_baseColor;
|
||||||
|
uniform vec4 u_multiplyColor;
|
||||||
|
uniform vec4 u_screenColor;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 texColor = texture2D(s_texture0, v_texCoord);
|
||||||
|
texColor.rgb = texColor.rgb * u_multiplyColor.rgb;
|
||||||
|
texColor.rgb = (texColor.rgb + u_screenColor.rgb * texColor.a) - (texColor.rgb * u_screenColor.rgb);
|
||||||
|
gl_FragColor = texColor * u_baseColor;
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#version 100
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
varying vec2 v_texCoord;
|
||||||
|
varying vec4 v_myPos;
|
||||||
|
uniform sampler2D s_texture0;
|
||||||
|
uniform vec4 u_channelFlag;
|
||||||
|
uniform vec4 u_baseColor;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
float isInside =
|
||||||
|
step(u_baseColor.x, v_myPos.x / v_myPos.w)
|
||||||
|
* step(u_baseColor.y, v_myPos.y / v_myPos.w)
|
||||||
|
* step(v_myPos.x / v_myPos.w, u_baseColor.z)
|
||||||
|
* step(v_myPos.y / v_myPos.w, u_baseColor.w);
|
||||||
|
gl_FragColor = u_channelFlag * texture2D(s_texture0, v_texCoord).a * isInside;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#version 100
|
||||||
|
|
||||||
|
attribute vec4 a_position;
|
||||||
|
attribute vec2 a_texCoord;
|
||||||
|
varying vec2 v_texCoord;
|
||||||
|
uniform mat4 u_matrix;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = u_matrix * a_position;
|
||||||
|
v_texCoord = a_texCoord;
|
||||||
|
v_texCoord.y = 1.0 - v_texCoord.y;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#version 100
|
||||||
|
|
||||||
|
attribute vec4 a_position;
|
||||||
|
attribute vec2 a_texCoord;
|
||||||
|
varying vec2 v_texCoord;
|
||||||
|
varying vec4 v_clipPos;
|
||||||
|
uniform mat4 u_matrix;
|
||||||
|
uniform mat4 u_clipMatrix;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = u_matrix * a_position;
|
||||||
|
v_clipPos = u_clipMatrix * a_position;
|
||||||
|
v_texCoord = a_texCoord;
|
||||||
|
v_texCoord.y = 1.0 - v_texCoord.y;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#version 100
|
||||||
|
|
||||||
|
attribute vec4 a_position;
|
||||||
|
attribute vec2 a_texCoord;
|
||||||
|
varying vec2 v_texCoord;
|
||||||
|
varying vec4 v_myPos;
|
||||||
|
uniform mat4 u_clipMatrix;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = u_clipMatrix * a_position;
|
||||||
|
v_myPos = u_clipMatrix * a_position;
|
||||||
|
v_texCoord = a_texCoord;
|
||||||
|
v_texCoord.y = 1.0 - v_texCoord.y;
|
||||||
|
}
|
||||||
@@ -24,7 +24,7 @@ class DeviceTcpServer;
|
|||||||
class DeviceWebSocketServer;
|
class DeviceWebSocketServer;
|
||||||
|
|
||||||
class AppCore final : public QObject
|
class AppCore final : public QObject
|
||||||
#ifdef Q_OS_LINUX
|
#if defined(Q_OS_LINUX) && !defined(EMBEDDED_LINUX)
|
||||||
, public QAbstractNativeEventFilter
|
, public QAbstractNativeEventFilter
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
@@ -54,14 +54,15 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
void SingleExchange();
|
void SingleExchange();
|
||||||
void tryToInit() { return; }
|
void tryToInit() { }
|
||||||
|
|
||||||
// PTT 按住说话
|
// PTT 按住说话
|
||||||
void startPttRecording();
|
void startPttRecording();
|
||||||
void stopPttRecording();
|
void stopPttRecording();
|
||||||
|
#if !defined(EMBEDDED_LINUX)
|
||||||
void setupGlobalHotkey();
|
void setupGlobalHotkey();
|
||||||
void cleanupGlobalHotkey();
|
void cleanupGlobalHotkey();
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
private:
|
private:
|
||||||
@@ -70,7 +71,7 @@ private:
|
|||||||
bool m_isPttDown = false;
|
bool m_isPttDown = false;
|
||||||
UINT m_hotkeyVKey = VK_OEM_3;
|
UINT m_hotkeyVKey = VK_OEM_3;
|
||||||
|
|
||||||
#elif defined(Q_OS_LINUX)
|
#elif defined(Q_OS_LINUX) && !defined(EMBEDDED_LINUX)
|
||||||
private:
|
private:
|
||||||
bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override;
|
bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override;
|
||||||
void onDebounceStop();
|
void onDebounceStop();
|
||||||
|
|||||||
@@ -8,8 +8,10 @@
|
|||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
#include "AudioDataHandle.h"
|
#include "AudioDataHandle.h"
|
||||||
|
#if !defined(EMBEDDED_LINUX)
|
||||||
#include "AutoAgentHandle.h"
|
#include "AutoAgentHandle.h"
|
||||||
#include "ScreenShotReqDataHandle.h"
|
#include "ScreenShotReqDataHandle.h"
|
||||||
|
#endif
|
||||||
#include "DeviceDataHandle.h"
|
#include "DeviceDataHandle.h"
|
||||||
|
|
||||||
#include "AudioInput.h"
|
#include "AudioInput.h"
|
||||||
@@ -18,7 +20,7 @@
|
|||||||
#include "DeviceTcpServer.h"
|
#include "DeviceTcpServer.h"
|
||||||
#include "DeviceWebSocketServer.h"
|
#include "DeviceWebSocketServer.h"
|
||||||
|
|
||||||
#ifdef Q_OS_LINUX
|
#if defined(Q_OS_LINUX) && !defined(EMBEDDED_LINUX)
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
#include <X11/keysym.h>
|
#include <X11/keysym.h>
|
||||||
#include <X11/XKBlib.h>
|
#include <X11/XKBlib.h>
|
||||||
@@ -88,29 +90,54 @@ AppCore::AppCore(QObject *parent) : QObject(parent)
|
|||||||
|
|
||||||
// 初始化业务解析单例
|
// 初始化业务解析单例
|
||||||
AudioDataHandle::getInstance();
|
AudioDataHandle::getInstance();
|
||||||
|
#if !defined(EMBEDDED_LINUX)
|
||||||
AutoAgentHandle::getInstance();
|
AutoAgentHandle::getInstance();
|
||||||
ScreenShotReqDataHandle::getInstance();
|
ScreenShotReqDataHandle::getInstance();
|
||||||
|
#endif
|
||||||
// 注入发送接口
|
// 注入发送接口
|
||||||
NetworkDO::getInstance()->registerSender([](const QString& type, const QJsonObject& data){
|
NetworkDO::getInstance()->registerSender([](const QString& type, const QJsonObject& data){
|
||||||
WebSocketClient::getInstance()->sendJson(type, data);
|
WebSocketClient::getInstance()->sendJson(type, data);
|
||||||
});
|
});
|
||||||
// TODO Test
|
#ifdef EMBEDDED_LINUX
|
||||||
|
// 嵌入式平台无Settings UI,需手动绑定WebSocket接收链路
|
||||||
|
connect(WebSocketClient::getInstance(), &WebSocketClient::jsonReceived,
|
||||||
|
NetworkDO::getInstance(), &NetworkDO::onDataReceived);
|
||||||
|
// 自动配置并连接服务端(优先读取环境变量 YOSUGA_SERVER_URL)
|
||||||
|
{
|
||||||
|
QString serverUrl = qEnvironmentVariable("YOSUGA_SERVER_URL", "ws://192.168.1.18:8765");
|
||||||
|
auto *client = WebSocketClient::getInstance();
|
||||||
|
client->setConfiguration(QUrl(serverUrl));
|
||||||
|
client->setAutoReconnect(true);
|
||||||
|
client->connectToServer();
|
||||||
|
qDebug() << "[AppCore] Embedded: connecting to server:" << serverUrl;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef EMBEDDED_LINUX
|
||||||
|
AudioInput::getInstance()->setAudioSettings(16000, 2);
|
||||||
|
#endif
|
||||||
AudioInput::getInstance()->setAudioPath(QDir::currentPath(), "/temp.wav");
|
AudioInput::getInstance()->setAudioPath(QDir::currentPath(), "/temp.wav");
|
||||||
// 连接必要的信号
|
// 连接必要的信号
|
||||||
connect(AudioInput::getInstance(), &AudioInput::recordingFinished_Byte,
|
connect(AudioInput::getInstance(), &AudioInput::recordingFinished_Byte,
|
||||||
this, &AppCore::onRecordingFinished_Byte);
|
this, &AppCore::onRecordingFinished_Byte);
|
||||||
|
#if !defined(EMBEDDED_LINUX)
|
||||||
setupGlobalHotkey();
|
setupGlobalHotkey();
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AppCore::~AppCore()
|
AppCore::~AppCore()
|
||||||
{
|
{
|
||||||
|
#if !defined(EMBEDDED_LINUX)
|
||||||
cleanupGlobalHotkey();
|
cleanupGlobalHotkey();
|
||||||
|
#endif
|
||||||
|
|
||||||
if (m_deviceTcpServer) m_deviceTcpServer->stop();
|
if (m_deviceTcpServer) m_deviceTcpServer->stop();
|
||||||
if (m_deviceWsServer) m_deviceWsServer->stop();
|
if (m_deviceWsServer) m_deviceWsServer->stop();
|
||||||
|
|
||||||
|
#if !defined(EMBEDDED_LINUX)
|
||||||
ScreenShotReqDataHandle::destroy();
|
ScreenShotReqDataHandle::destroy();
|
||||||
AutoAgentHandle::destroy();
|
AutoAgentHandle::destroy();
|
||||||
|
#endif
|
||||||
AudioDataHandle::destroy();
|
AudioDataHandle::destroy();
|
||||||
DeviceDataHandle::destroy();
|
DeviceDataHandle::destroy();
|
||||||
|
|
||||||
@@ -198,7 +225,7 @@ LRESULT CALLBACK AppCore::lowLevelKeyboardHook(int nCode, WPARAM wParam, LPARAM
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ===================== Linux (X11) =====================
|
// ===================== Linux (X11) =====================
|
||||||
#elif defined(Q_OS_LINUX)
|
#elif defined(Q_OS_LINUX) && !defined(EMBEDDED_LINUX)
|
||||||
void AppCore::setupGlobalHotkey() {
|
void AppCore::setupGlobalHotkey() {
|
||||||
Display *display = XOpenDisplay(nullptr);
|
Display *display = XOpenDisplay(nullptr);
|
||||||
if (!display) {
|
if (!display) {
|
||||||
|
|||||||
@@ -12,6 +12,9 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <QScopedPointer>
|
#include <QScopedPointer>
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
|
#ifdef EMBEDDED_LINUX
|
||||||
|
#include <QProcess>
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 录音模块
|
* @brief 录音模块
|
||||||
@@ -150,5 +153,8 @@ private:
|
|||||||
qreal m_smoothRms = 0.0; /// 平滑RMS值(用于防止低频杂波突然打断静音检测)
|
qreal m_smoothRms = 0.0; /// 平滑RMS值(用于防止低频杂波突然打断静音检测)
|
||||||
|
|
||||||
bool m_hasVoiceDetected = false; /// 是否已检测到人声
|
bool m_hasVoiceDetected = false; /// 是否已检测到人声
|
||||||
|
#ifdef EMBEDDED_LINUX
|
||||||
|
QProcess *m_arecordProcess = nullptr; /// 嵌入式平台使用arecord录音
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ void AudioInput::setAudioSettings(const int rate, const int channels)
|
|||||||
{
|
{
|
||||||
m_format.setSampleRate(rate);
|
m_format.setSampleRate(rate);
|
||||||
m_format.setChannelCount(channels);
|
m_format.setChannelCount(channels);
|
||||||
// 为了生成标准 WAV 且方便计算 RMS,强制设为 Int16
|
|
||||||
m_format.setSampleFormat(QAudioFormat::Int16);
|
m_format.setSampleFormat(QAudioFormat::Int16);
|
||||||
|
|
||||||
// 检查设备是否支持该格式,不支持则使用最接近的
|
// 检查设备是否支持该格式,不支持则使用最接近的
|
||||||
@@ -72,6 +71,30 @@ void AudioInput::setAudioPath(const QString &path, const QString &fileName)
|
|||||||
|
|
||||||
void AudioInput::startAudio()
|
void AudioInput::startAudio()
|
||||||
{
|
{
|
||||||
|
#ifdef EMBEDDED_LINUX
|
||||||
|
m_rawPCMData.clear();
|
||||||
|
if (!m_arecordProcess) {
|
||||||
|
m_arecordProcess = new QProcess(this);
|
||||||
|
connect(m_arecordProcess, &QProcess::readyReadStandardOutput, this, [this]() {
|
||||||
|
m_rawPCMData.append(m_arecordProcess->readAllStandardOutput());
|
||||||
|
});
|
||||||
|
} else if (m_arecordProcess->state() != QProcess::NotRunning) {
|
||||||
|
m_arecordProcess->terminate();
|
||||||
|
m_arecordProcess->waitForFinished(1000);
|
||||||
|
}
|
||||||
|
m_arecordProcess->start("arecord", {
|
||||||
|
"-D", "hw:0,0", "-f", "S16_LE",
|
||||||
|
"-r", QString::number(m_format.sampleRate()),
|
||||||
|
"-c", QString::number(m_format.channelCount()),
|
||||||
|
"-t", "raw", "-q", "-"
|
||||||
|
});
|
||||||
|
if (m_arecordProcess->waitForStarted(1000)) {
|
||||||
|
qDebug() << "Started recording via arecord: hw:0,0"
|
||||||
|
<< m_format.sampleRate() << "Hz" << m_format.channelCount() << "ch S16_LE";
|
||||||
|
} else {
|
||||||
|
qCritical() << "Failed to start arecord";
|
||||||
|
}
|
||||||
|
#else
|
||||||
// 每次开始前重新创建 QAudioSource,确保状态重置
|
// 每次开始前重新创建 QAudioSource,确保状态重置
|
||||||
if (m_audioSource) {
|
if (m_audioSource) {
|
||||||
delete m_audioSource;
|
delete m_audioSource;
|
||||||
@@ -79,8 +102,6 @@ void AudioInput::startAudio()
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_audioSource = new QAudioSource(m_currentDevice, m_format, this);
|
m_audioSource = new QAudioSource(m_currentDevice, m_format, this);
|
||||||
|
|
||||||
// 调大缓冲区以避免溢出
|
|
||||||
m_audioSource->setBufferSize(128000);
|
m_audioSource->setBufferSize(128000);
|
||||||
|
|
||||||
// start() 返回一个 QIODevice,可以从中读取数据
|
// start() 返回一个 QIODevice,可以从中读取数据
|
||||||
@@ -92,14 +113,21 @@ void AudioInput::startAudio()
|
|||||||
} else {
|
} else {
|
||||||
qCritical() << "Failed to start audio recording.";
|
qCritical() << "Failed to start audio recording.";
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioInput::stopAudio()
|
void AudioInput::stopAudio()
|
||||||
{
|
{
|
||||||
|
#ifdef EMBEDDED_LINUX
|
||||||
|
if (m_arecordProcess && m_arecordProcess->state() != QProcess::NotRunning) {
|
||||||
|
m_arecordProcess->terminate();
|
||||||
|
m_arecordProcess->waitForFinished(2000);
|
||||||
|
}
|
||||||
|
#else
|
||||||
if (m_audioSource) {
|
if (m_audioSource) {
|
||||||
m_audioSource->stop();
|
m_audioSource->stop();
|
||||||
// 注意:不要立即 delete m_audioSource,某些情况下可能导致 crash,停止即可
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// 停止所有定时器
|
// 停止所有定时器
|
||||||
m_timer->stop();
|
m_timer->stop();
|
||||||
|
|||||||
@@ -29,27 +29,24 @@ void StreamAudioWorker::start(int sampleRate, int channelCount, int bitDepth) {
|
|||||||
|
|
||||||
m_firstChunk = true;
|
m_firstChunk = true;
|
||||||
|
|
||||||
// 配置音频格式
|
|
||||||
QAudioFormat format;
|
QAudioFormat format;
|
||||||
format.setSampleRate(sampleRate);
|
format.setSampleRate(sampleRate);
|
||||||
format.setChannelCount(channelCount);
|
format.setChannelCount(channelCount);
|
||||||
|
|
||||||
if (bitDepth == 8) format.setSampleFormat(QAudioFormat::UInt8);
|
if (bitDepth == 8) format.setSampleFormat(QAudioFormat::UInt8);
|
||||||
else if (bitDepth == 16) format.setSampleFormat(QAudioFormat::Int16);
|
else if (bitDepth == 16) format.setSampleFormat(QAudioFormat::Int16);
|
||||||
else if (bitDepth == 32) format.setSampleFormat(QAudioFormat::Float); // 32位通常为Float
|
else if (bitDepth == 32) format.setSampleFormat(QAudioFormat::Float);
|
||||||
else format.setSampleFormat(QAudioFormat::Int16); // 默认回退
|
else format.setSampleFormat(QAudioFormat::Int16);
|
||||||
|
|
||||||
// 检查设备是否支持
|
|
||||||
auto device = QMediaDevices::defaultAudioOutput();
|
auto device = QMediaDevices::defaultAudioOutput();
|
||||||
if (!device.isFormatSupported(format)) {
|
if (!device.isFormatSupported(format)) {
|
||||||
qWarning() << "[Worker] Device does not support format, using preferred format.";
|
qWarning() << "[Worker] Device does not support format, using preferred format.";
|
||||||
format = device.preferredFormat();
|
format = device.preferredFormat();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建 Sink (必须在 Worker 线程中创建)
|
|
||||||
m_sink.reset(new QAudioSink(device, format));
|
m_sink.reset(new QAudioSink(device, format));
|
||||||
|
m_sink->setBufferSize(sampleRate * channelCount * (bitDepth / 8));
|
||||||
|
|
||||||
// 监听状态
|
|
||||||
connect(m_sink.data(), &QAudioSink::stateChanged, this, [this](QAudio::State state){
|
connect(m_sink.data(), &QAudioSink::stateChanged, this, [this](QAudio::State state){
|
||||||
if (state == QAudio::IdleState) {
|
if (state == QAudio::IdleState) {
|
||||||
emit playbackFinished();
|
emit playbackFinished();
|
||||||
@@ -61,32 +58,34 @@ void StreamAudioWorker::start(int sampleRate, int channelCount, int bitDepth) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 启动,获取 IO 设备
|
|
||||||
m_ioDevice = m_sink->start();
|
m_ioDevice = m_sink->start();
|
||||||
if (!m_ioDevice) {
|
if (!m_ioDevice) {
|
||||||
emit errorOccurred("Failed to start audio device");
|
emit errorOccurred("Failed to start audio device");
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "[Worker] Stream started:" << sampleRate << "Hz";
|
qDebug() << "[Worker] Stream started:" << sampleRate << "Hz, buffer:" << m_sink->bufferSize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void StreamAudioWorker::processChunk(const QByteArray& chunk) {
|
void StreamAudioWorker::processChunk(const QByteArray& chunk) {
|
||||||
if (chunk.isEmpty() || !m_ioDevice || !m_sink) return;
|
if (chunk.isEmpty() || !m_ioDevice || !m_sink) return;
|
||||||
QByteArray dataToWrite = chunk;
|
QByteArray dataToWrite = chunk;
|
||||||
// 智能处理 WAV 头
|
|
||||||
if (m_firstChunk) {
|
if (m_firstChunk) {
|
||||||
if (hasWavHeader(chunk)) {
|
if (hasWavHeader(chunk)) {
|
||||||
qDebug() << "[Worker] Detected WAV header, stripping 44 bytes.";
|
|
||||||
dataToWrite = chunk.mid(44);
|
dataToWrite = chunk.mid(44);
|
||||||
}
|
}
|
||||||
m_firstChunk = false;
|
m_firstChunk = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 写入音频设备 (QAudioSink 内部有缓冲区,这里直接 write 即可)
|
const char *ptr = dataToWrite.constData();
|
||||||
// 如果数据量过大,write 可能会阻塞,但在独立线程中这是可以接受的
|
qint64 remaining = dataToWrite.size();
|
||||||
qint64 written = m_ioDevice->write(dataToWrite);
|
while (remaining > 0) {
|
||||||
if (written != dataToWrite.size()) {
|
qint64 written = m_ioDevice->write(ptr, remaining);
|
||||||
qWarning() << "[Worker] Incomplete write:" << written << "/" << dataToWrite.size();
|
if (written > 0) {
|
||||||
|
ptr += written;
|
||||||
|
remaining -= written;
|
||||||
|
} else {
|
||||||
|
QThread::msleep(5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,10 @@
|
|||||||
|
|
||||||
#include "AutoAgentHandle.h"
|
#include "AutoAgentHandle.h"
|
||||||
#include "NetWorkDO.h"
|
#include "NetWorkDO.h"
|
||||||
|
#if defined(Q_OS_LINUX) && !defined(EMBEDDED_LINUX)
|
||||||
#include <SimpleAutoGUI.h> // 引入 AutoGUI 头文件
|
#include <SimpleAutoGUI.h> // 引入 AutoGUI 头文件
|
||||||
|
#endif
|
||||||
|
|
||||||
// 初始化静态成员
|
// 初始化静态成员
|
||||||
QScopedPointer<AutoAgentHandle> AutoAgentHandle::m_instance;
|
QScopedPointer<AutoAgentHandle> AutoAgentHandle::m_instance;
|
||||||
QMutex AutoAgentHandle::m_mutex;
|
QMutex AutoAgentHandle::m_mutex;
|
||||||
@@ -43,6 +46,7 @@ AutoAgentHandle::~AutoAgentHandle()
|
|||||||
|
|
||||||
void AutoAgentHandle::onAutoAgentPacketReceived(const AutoAgentDataObject &packet) {
|
void AutoAgentHandle::onAutoAgentPacketReceived(const AutoAgentDataObject &packet) {
|
||||||
qDebug() << "Received AutoAgent packet: " << packet.getAction();
|
qDebug() << "Received AutoAgent packet: " << packet.getAction();
|
||||||
|
#if defined(Q_OS_LINUX) && !defined(EMBEDDED_LINUX)
|
||||||
if (packet.getAction() == "click") { // 单击
|
if (packet.getAction() == "click") { // 单击
|
||||||
qDebug() << "Click: " << packet.getX1() << ", " << packet.getY1();
|
qDebug() << "Click: " << packet.getX1() << ", " << packet.getY1();
|
||||||
AutoGUI::moveToOnCurrentScreen(packet.getX1(), packet.getY1(), 0.6);
|
AutoGUI::moveToOnCurrentScreen(packet.getX1(), packet.getY1(), 0.6);
|
||||||
@@ -78,4 +82,6 @@ void AutoAgentHandle::onAutoAgentPacketReceived(const AutoAgentDataObject &packe
|
|||||||
AutoGUI::scroll(40, -1);
|
AutoGUI::scroll(40, -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user