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));
return !tokenToString_.empty();
return !tokenToString_.isEmpty();
}
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);
if (it != tokenToString_.end()) {
QString decoded = decodeBPE(it->second);
QString decoded = decodeBPE(it.value());
// 过滤 SenseVoice 特殊标签: <|zh|>, <|speech|>, <|NEUTRAL|> 等
if (decoded.startsWith("<|") && decoded.endsWith("|>")) {
continue;

View File

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

View File

@ -116,9 +116,9 @@ struct STTEngine::Impl {
return false;
}
}
#endif
QMutex mutex;
#endif
};
STTEngine::STTEngine(QObject* parent)
@ -139,6 +139,7 @@ bool STTEngine::loadModelSync(const QString& modelPath,
unloadModel();
}
#ifdef HAVE_ONNXRUNTIME
QString errorMsg;
bool success = impl_->loadInWorker(modelPath, device, numThreads, errorMsg);
loaded_ = success;
@ -150,6 +151,11 @@ bool STTEngine::loadModelSync(const QString& modelPath,
emit error(errorMsg);
}
return success;
#else
(void)modelPath; (void)device; (void)numThreads;
LOG_ERROR(kTag, "ONNX Runtime 未编译启用");
return false;
#endif
}
void STTEngine::loadModelAsync(const QString& modelPath,
@ -161,6 +167,7 @@ void STTEngine::loadModelAsync(const QString& modelPath,
unloadModel();
}
#ifdef HAVE_ONNXRUNTIME
LOG_INFO(kTag, QString("异步加载模型: %1").arg(modelPath));
QFuture<void> future = QtConcurrent::run([this, modelPath, device, numThreads]() {
@ -177,6 +184,10 @@ void STTEngine::loadModelAsync(const QString& modelPath,
}
}, Qt::QueuedConnection);
});
#else
(void)modelPath; (void)device; (void)numThreads;
LOG_ERROR(kTag, "ONNX Runtime 未编译启用");
#endif
}
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));
return !tokenToString_.empty();
return !tokenToString_.isEmpty();
}
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);
if (it != tokenToString_.end()) {
QString decoded = decodeBytePair(it->second);
QString decoded = decodeBytePair(it.value());
result += decoded;
} else {
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);
auto it = stringToToken_.find(ch);
if (it != stringToToken_.end()) {
tokens.push_back(it->second);
tokens.push_back(it.value());
}
}
return tokens;
@ -86,13 +86,13 @@ QString WhisperTokenizer::decodeBytePair(const QString& text) const {
}
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},
{"fr", 50265}, {"de", 50266}, {"es", 50267}, {"ru", 50268},
{"pt", 50269}, {"it", 50270}, {"auto", 50359}
};
auto it = langMap.find(langCode);
return it != langMap.end() ? it->second : 50259; // 默认英语
return it != langMap.end() ? it.value() : 50259; // 默认英语
}
bool WhisperTokenizer::isSpecialToken(int token) {

View File

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