127 lines
3.9 KiB
C++
127 lines
3.9 KiB
C++
#include <QtTest>
|
||||
|
|
|
|||
|
|
#include <algorithm>
|
|||
|
|
#include <cmath>
|
|||
|
|
#include <vector>
|
|||
|
|
|
|||
|
|
#include "fakeasr.h"
|
|||
|
|
#include "livepipeline.h"
|
|||
|
|
|
|||
|
|
namespace {
|
|||
|
|
|
|||
|
|
constexpr double kPi = 3.14159265358979323846;
|
|||
|
|
|
|||
|
|
std::vector<float> tone(size_t samples, float amplitude)
|
|||
|
|
{
|
|||
|
|
std::vector<float> v(samples);
|
|||
|
|
for (size_t i = 0; i < samples; ++i) {
|
|||
|
|
v[i] = amplitude * (float) std::sin(2.0 * kPi * 440.0 * (double) i / 16000.0);
|
|||
|
|
}
|
|||
|
|
return v;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
size_t totalSamples(const std::vector<size_t> & v)
|
|||
|
|
{
|
|||
|
|
size_t n = 0;
|
|||
|
|
for (size_t s : v) n += s;
|
|||
|
|
return n;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
lc::LivePipelineOptions options()
|
|||
|
|
{
|
|||
|
|
lc::LivePipelineOptions o;
|
|||
|
|
o.stream.chunkSec = 0.5; // 0.5s 一块,测试里能快些触发
|
|||
|
|
return o;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
} // namespace
|
|||
|
|
|
|||
|
|
// 实时管线的回归测试(用 FakeAsr + 直接喂音频,不碰 PulseAudio)。
|
|||
|
|
class TstLivePipeline : public QObject {
|
|||
|
|
Q_OBJECT
|
|||
|
|
|
|||
|
|
private slots:
|
|||
|
|
// 静音不能送进模型。
|
|||
|
|
// 曾经把采集到的静音也喂了进去,模型对静音产生幻觉(英文语音前冒出一个"嗯。")。
|
|||
|
|
void silenceIsNotForwarded()
|
|||
|
|
{
|
|||
|
|
FakeAsr fake;
|
|||
|
|
lc::LivePipeline pipeline(fake, options());
|
|||
|
|
|
|||
|
|
const auto silence = tone(16000 * 3, 0.001f); // 3s 背景噪声
|
|||
|
|
pipeline.process(silence.data(), silence.size());
|
|||
|
|
|
|||
|
|
QCOMPARE(fake.samplesSeen.size(), size_t(0));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 不丢样本。
|
|||
|
|
// 曾经每次只处理 floor(可用/320) 帧并丢掉余数,每轮丢约 160 个样本,
|
|||
|
|
// 造成假静音和句子被截断。
|
|||
|
|
void noSamplesDroppedAcrossIrregularChunks()
|
|||
|
|
{
|
|||
|
|
FakeAsr fake;
|
|||
|
|
lc::LivePipeline pipeline(fake, options());
|
|||
|
|
|
|||
|
|
const auto speech = tone(16000 * 10, 0.1f); // 10s 语音
|
|||
|
|
const size_t sizes[] = {1000, 7, 3333, 999, 12345, 1};
|
|||
|
|
|
|||
|
|
size_t off = 0;
|
|||
|
|
int i = 0;
|
|||
|
|
while (off < speech.size()) {
|
|||
|
|
const size_t n = std::min(sizes[i++ % 6], speech.size() - off);
|
|||
|
|
pipeline.process(speech.data() + off, n);
|
|||
|
|
off += n;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const size_t forwarded = totalSamples(fake.samplesSeen);
|
|||
|
|
// 允许剩下的不足一块留在引擎里(< 0.5s),其余都必须送到
|
|||
|
|
QVERIFY2(forwarded >= 16000 * 9,
|
|||
|
|
qPrintable(QString("只送到了 %1 个样本(共 %2)")
|
|||
|
|
.arg(forwarded).arg(speech.size())));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 静音够久要收束这一段(提交文本并重置上下文)
|
|||
|
|
void silenceEndsSegment()
|
|||
|
|
{
|
|||
|
|
FakeAsr fake;
|
|||
|
|
fake.scripted = {"a b c d e f"};
|
|||
|
|
|
|||
|
|
lc::LivePipeline pipeline(fake, options());
|
|||
|
|
QString lastCommitted;
|
|||
|
|
pipeline.setCaptionCallback(
|
|||
|
|
[&lastCommitted](const std::string & committed, const std::string &,
|
|||
|
|
const std::string &) {
|
|||
|
|
lastCommitted = QString::fromStdString(committed);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const auto speech = tone(16000 * 1, 0.1f);
|
|||
|
|
pipeline.process(speech.data(), speech.size());
|
|||
|
|
const int resetsAfterSpeech = fake.resets;
|
|||
|
|
|
|||
|
|
const auto silence = tone(16000 * 2, 0.001f);
|
|||
|
|
pipeline.process(silence.data(), silence.size());
|
|||
|
|
|
|||
|
|
QVERIFY2(fake.resets > resetsAfterSpeech, "静音后没有收束这一段");
|
|||
|
|
QVERIFY(!pipeline.inSegment());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 语音之后继续有语音:不应该被切断成很多段
|
|||
|
|
void continuousSpeechStaysInOneSegment()
|
|||
|
|
{
|
|||
|
|
FakeAsr fake;
|
|||
|
|
lc::LivePipeline pipeline(fake, options());
|
|||
|
|
const int resetsAfterConstruct = fake.resets; // 构造 StreamingAsr 时会 reset 一次
|
|||
|
|
|
|||
|
|
const auto speech = tone(16000 * 4, 0.1f);
|
|||
|
|
for (size_t off = 0; off < speech.size(); off += 4000) {
|
|||
|
|
const size_t n = std::min(size_t(4000), speech.size() - off);
|
|||
|
|
pipeline.process(speech.data() + off, n);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
QVERIFY(pipeline.inSegment());
|
|||
|
|
QCOMPARE(fake.resets, resetsAfterConstruct);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
QTEST_MAIN(TstLivePipeline)
|
|||
|
|
#include "tst_livepipeline.moc"
|