fix: 修复 Windows Qt5.12 MinGW 编译错误

- std::unordered_map 替换为 QHash(旧版 GCC 7.3.0 不支持 QString 的 std::hash)
- 涉及文件: whisper_tokenizer.h/cpp, sense_voice_tokenizer.h/cpp
- STTEngine::Impl 的 loadInWorker 和 mutex 移出 HAVE_ONNXRUNTIME 条件编译块
- loadModelSync/loadModelAsync 添加 HAVE_ONNXRUNTIME 保护

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Alvin Young 2026-05-13 17:36:48 +08:00
parent 50b6a5daea
commit a56f273c45
5 changed files with 28 additions and 19 deletions

View File

@ -41,7 +41,7 @@ bool SenseVoiceTokenizer::load(const QString& tokensPath) {
} }
LOG_INFO(kTag, QString("词表已加载: %1 个词条 (%2)").arg(lineCount).arg(tokensPath)); LOG_INFO(kTag, QString("词表已加载: %1 个词条 (%2)").arg(lineCount).arg(tokensPath));
return !tokenToString_.empty(); return !tokenToString_.isEmpty();
} }
QString SenseVoiceTokenizer::decode(const std::vector<int>& tokens) const { QString SenseVoiceTokenizer::decode(const std::vector<int>& tokens) const {
@ -56,7 +56,7 @@ QString SenseVoiceTokenizer::decode(const std::vector<int>& tokens) const {
auto it = tokenToString_.find(token); auto it = tokenToString_.find(token);
if (it != tokenToString_.end()) { if (it != tokenToString_.end()) {
QString decoded = decodeBPE(it->second); QString decoded = decodeBPE(it.value());
// 过滤 SenseVoice 特殊标签: <|zh|>, <|speech|>, <|NEUTRAL|> 等 // 过滤 SenseVoice 特殊标签: <|zh|>, <|speech|>, <|NEUTRAL|> 等
if (decoded.startsWith("<|") && decoded.endsWith("|>")) { if (decoded.startsWith("<|") && decoded.endsWith("|>")) {
continue; continue;

View File

@ -1,9 +1,8 @@
#pragma once #pragma once
#include <QString> #include <QString>
#include <QStringList> #include <QHash>
#include <vector> #include <vector>
#include <unordered_map>
namespace impress { namespace impress {
@ -24,10 +23,10 @@ public:
QString decode(const std::vector<int>& tokens) const; QString decode(const std::vector<int>& tokens) const;
/** @brief 是否已加载 */ /** @brief 是否已加载 */
bool isLoaded() const { return !tokenToString_.empty(); } bool isLoaded() const { return !tokenToString_.isEmpty(); }
/** @brief 词表大小 */ /** @brief 词表大小 */
int vocabSize() const { return static_cast<int>(tokenToString_.size()); } int vocabSize() const { return tokenToString_.size(); }
// 特殊 token // 特殊 token
static constexpr int kTokenBlank = 0; // CTC blank / <unk> static constexpr int kTokenBlank = 0; // CTC blank / <unk>
@ -35,7 +34,7 @@ public:
static constexpr int kTokenEOS = 2; // </s> static constexpr int kTokenEOS = 2; // </s>
private: private:
std::unordered_map<int, QString> tokenToString_; QHash<int, QString> tokenToString_;
QString decodeBPE(const QString& token) const; QString decodeBPE(const QString& token) const;
}; };

View File

@ -116,9 +116,9 @@ struct STTEngine::Impl {
return false; return false;
} }
} }
#endif
QMutex mutex; QMutex mutex;
#endif
}; };
STTEngine::STTEngine(QObject* parent) STTEngine::STTEngine(QObject* parent)
@ -139,6 +139,7 @@ bool STTEngine::loadModelSync(const QString& modelPath,
unloadModel(); unloadModel();
} }
#ifdef HAVE_ONNXRUNTIME
QString errorMsg; QString errorMsg;
bool success = impl_->loadInWorker(modelPath, device, numThreads, errorMsg); bool success = impl_->loadInWorker(modelPath, device, numThreads, errorMsg);
loaded_ = success; loaded_ = success;
@ -150,6 +151,11 @@ bool STTEngine::loadModelSync(const QString& modelPath,
emit error(errorMsg); emit error(errorMsg);
} }
return success; return success;
#else
(void)modelPath; (void)device; (void)numThreads;
LOG_ERROR(kTag, "ONNX Runtime 未编译启用");
return false;
#endif
} }
void STTEngine::loadModelAsync(const QString& modelPath, void STTEngine::loadModelAsync(const QString& modelPath,
@ -161,6 +167,7 @@ void STTEngine::loadModelAsync(const QString& modelPath,
unloadModel(); unloadModel();
} }
#ifdef HAVE_ONNXRUNTIME
LOG_INFO(kTag, QString("异步加载模型: %1").arg(modelPath)); LOG_INFO(kTag, QString("异步加载模型: %1").arg(modelPath));
QFuture<void> future = QtConcurrent::run([this, modelPath, device, numThreads]() { QFuture<void> future = QtConcurrent::run([this, modelPath, device, numThreads]() {
@ -177,6 +184,10 @@ void STTEngine::loadModelAsync(const QString& modelPath,
} }
}, Qt::QueuedConnection); }, Qt::QueuedConnection);
}); });
#else
(void)modelPath; (void)device; (void)numThreads;
LOG_ERROR(kTag, "ONNX Runtime 未编译启用");
#endif
} }
void STTEngine::unloadModel() { void STTEngine::unloadModel() {

View File

@ -45,7 +45,7 @@ bool WhisperTokenizer::loadVocabulary(const QString& vocabPath) {
} }
LOG_INFO(kTag, QString("词表已加载: %1 个词条 (文件: %2)").arg(lineCount).arg(vocabPath)); LOG_INFO(kTag, QString("词表已加载: %1 个词条 (文件: %2)").arg(lineCount).arg(vocabPath));
return !tokenToString_.empty(); return !tokenToString_.isEmpty();
} }
QString WhisperTokenizer::decode(const std::vector<int>& tokens) const { QString WhisperTokenizer::decode(const std::vector<int>& tokens) const {
@ -55,7 +55,7 @@ QString WhisperTokenizer::decode(const std::vector<int>& tokens) const {
auto it = tokenToString_.find(token); auto it = tokenToString_.find(token);
if (it != tokenToString_.end()) { if (it != tokenToString_.end()) {
QString decoded = decodeBytePair(it->second); QString decoded = decodeBytePair(it.value());
result += decoded; result += decoded;
} else { } else {
result += QString("<|token:%1|>").arg(token); result += QString("<|token:%1|>").arg(token);
@ -71,7 +71,7 @@ std::vector<int> WhisperTokenizer::encode(const QString& text) const {
QString ch = text.mid(i, 1); QString ch = text.mid(i, 1);
auto it = stringToToken_.find(ch); auto it = stringToToken_.find(ch);
if (it != stringToToken_.end()) { if (it != stringToToken_.end()) {
tokens.push_back(it->second); tokens.push_back(it.value());
} }
} }
return tokens; return tokens;
@ -86,13 +86,13 @@ QString WhisperTokenizer::decodeBytePair(const QString& text) const {
} }
int WhisperTokenizer::languageTokenId(const QString& langCode) { int WhisperTokenizer::languageTokenId(const QString& langCode) {
static const std::unordered_map<QString, int> langMap = { static const QHash<QString, int> langMap = {
{"zh", 50260}, {"en", 50259}, {"ja", 50261}, {"ko", 50262}, {"zh", 50260}, {"en", 50259}, {"ja", 50261}, {"ko", 50262},
{"fr", 50265}, {"de", 50266}, {"es", 50267}, {"ru", 50268}, {"fr", 50265}, {"de", 50266}, {"es", 50267}, {"ru", 50268},
{"pt", 50269}, {"it", 50270}, {"auto", 50359} {"pt", 50269}, {"it", 50270}, {"auto", 50359}
}; };
auto it = langMap.find(langCode); auto it = langMap.find(langCode);
return it != langMap.end() ? it->second : 50259; // 默认英语 return it != langMap.end() ? it.value() : 50259; // 默认英语
} }
bool WhisperTokenizer::isSpecialToken(int token) { bool WhisperTokenizer::isSpecialToken(int token) {

View File

@ -1,9 +1,8 @@
#pragma once #pragma once
#include <QString> #include <QString>
#include <QStringList> #include <QHash>
#include <vector> #include <vector>
#include <unordered_map>
#include <optional> #include <optional>
namespace impress { namespace impress {
@ -28,10 +27,10 @@ public:
std::vector<int> encode(const QString& text) const; std::vector<int> encode(const QString& text) const;
/** @brief 是否已加载词表 */ /** @brief 是否已加载词表 */
bool isLoaded() const { return !tokenToString_.empty(); } bool isLoaded() const { return !tokenToString_.isEmpty(); }
/** @brief 词表大小 */ /** @brief 词表大小 */
int vocabSize() const { return static_cast<int>(tokenToString_.size()); } int vocabSize() const { return tokenToString_.size(); }
// Whisper 特殊 token // Whisper 特殊 token
static constexpr int kTokenEndOfText = 50257; static constexpr int kTokenEndOfText = 50257;
@ -49,8 +48,8 @@ public:
static bool isSpecialToken(int token); static bool isSpecialToken(int token);
private: private:
std::unordered_map<int, QString> tokenToString_; QHash<int, QString> tokenToString_;
std::unordered_map<QString, int> stringToToken_; QHash<QString, int> stringToToken_;
QString decodeBytePair(const QString& text) const; QString decodeBytePair(const QString& text) const;
}; };