一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
168 行
5.2 KiB
C++
168 行
5.2 KiB
C++
#include "findLongestSilence_test.hpp"
|
|
#include "findLongestSilence.hpp"
|
|
#include "androidUtil.h"
|
|
#include "testing.hpp"
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
#include <sstream>
|
|
#include <cmath>
|
|
#include <random>
|
|
|
|
static void testTones();
|
|
static void testToneWithPause();
|
|
static void testSilence();
|
|
static void testNoise();
|
|
|
|
struct GeneratedAudio {
|
|
std::vector<float> data;
|
|
int sampleRate;
|
|
int sampleCount;
|
|
};
|
|
|
|
using AudioGenerator = std::function<const float(float)>;
|
|
static GeneratedAudio makeAudio(const AudioGenerator& generator, int sampleRate, float duration);
|
|
static void expectNoSilence(const GeneratedAudio& audio, const std::string& testLabel);
|
|
static void expectSilenceBetween(const GeneratedAudio& audio, float startTimeSeconds, float stopTimeSeconds, const std::string& testLabel);
|
|
|
|
|
|
void findLongestSilence_test() {
|
|
testTones();
|
|
testToneWithPause();
|
|
testSilence();
|
|
testNoise();
|
|
}
|
|
|
|
|
|
static void testTones() {
|
|
for (int frequency = 440; frequency < 1600; frequency += 300) {
|
|
std::stringstream messageBuilder;
|
|
messageBuilder << "Should not find silence in tone with frequency " << frequency << " HZ.";
|
|
|
|
auto audioTone = makeAudio([frequency](float t) {
|
|
// Also set the amplitude to 0.2f (to more closely match mic input).
|
|
return std::sin(t * static_cast<float>(frequency)) * 0.2f;
|
|
}, 15000, 10.0f);
|
|
|
|
expectNoSilence(audioTone, messageBuilder.str());
|
|
}
|
|
|
|
auto lowFrequencyTone = makeAudio([](float t) {
|
|
return std::sin(t * 8) * 0.3f;
|
|
}, 15000, 10.0f);
|
|
expectSilenceBetween(lowFrequencyTone, 0.0f, 10.0f, "Should find silence in a very low-frequency tone");
|
|
}
|
|
|
|
static void testToneWithPause() {
|
|
auto audioToneWithPause = makeAudio([](float t) {
|
|
if (t < 5.0f || t > 6.0f) {
|
|
return std::sin(t * 880);
|
|
} else {
|
|
return 0.0f;
|
|
}
|
|
}, 15000, 11.0f);
|
|
expectSilenceBetween(audioToneWithPause, 5.0f, 6.0f, "Should find silence when completely silent in a region");
|
|
|
|
auto audioToneWithTwoPauses = makeAudio([](float t) {
|
|
if (t < 1.0f || (t > 8.0f && t < 10.0f)) {
|
|
return 0.0f;
|
|
} else {
|
|
return std::sin(t * 880);
|
|
}
|
|
}, 15000, 20.0f);
|
|
expectSilenceBetween(audioToneWithPause, 5.0f, 6.0f, "Should find silence when completely silent in a region");
|
|
}
|
|
|
|
static void testSilence() {
|
|
auto silence = makeAudio([](float t) {
|
|
return 0.0f;
|
|
}, 16000, 10.0f);
|
|
expectSilenceBetween(silence, 0.0f, 10.0f, "Should find silence in a completely silent signal");
|
|
}
|
|
|
|
static void testNoise() {
|
|
std::minstd_rand randomness {2};
|
|
std::uniform_real_distribution noiseGenerator {-1.0, 1.0};
|
|
auto quietNoise = makeAudio([&](float t) {
|
|
return noiseGenerator(randomness) * 0.02f;
|
|
}, 16000, 5.0f);
|
|
expectSilenceBetween(quietNoise, 0.0f, 5.0f, "Should find silence in a tone with low-amplitude noise");
|
|
}
|
|
|
|
|
|
static GeneratedAudio makeAudio(const AudioGenerator& generator, int sampleRate, float duration) {
|
|
std::vector<float> result { };
|
|
|
|
int numSamples = static_cast<int>(static_cast<float>(sampleRate) * duration);
|
|
for (int i = 0; i < numSamples; i++) {
|
|
float time = static_cast<float>(i) / static_cast<float>(sampleRate);
|
|
result.push_back(generator(time));
|
|
}
|
|
|
|
return {
|
|
.data=result,
|
|
.sampleRate=sampleRate,
|
|
.sampleCount=numSamples,
|
|
};
|
|
}
|
|
|
|
|
|
static float samplesToSeconds(int samples, int sampleRate) {
|
|
return static_cast<float>(samples) / static_cast<float>(sampleRate);
|
|
}
|
|
|
|
static void expectNoSilence(const GeneratedAudio& audio, const std::string& testLabel) {
|
|
auto silence = findLongestSilence(
|
|
audio.data,
|
|
LongestSilenceOptions {
|
|
.sampleRate = audio.sampleRate,
|
|
.minSilenceLengthSeconds = 0.02f,
|
|
.maximumSilenceStartSamples = audio.sampleCount,
|
|
.returnFirstMatch = false,
|
|
}
|
|
);
|
|
if (silence.isValid) {
|
|
std::stringstream errorBuilder;
|
|
float startSeconds = samplesToSeconds(silence.start, audio.sampleRate);
|
|
float stopSeconds = samplesToSeconds(silence.end, audio.sampleRate);
|
|
errorBuilder << "Error: Found silence between " << startSeconds << "s and " << stopSeconds << "s";
|
|
errorBuilder << ": " << testLabel;
|
|
fail(errorBuilder.str());
|
|
}
|
|
|
|
logTestPass(testLabel);
|
|
}
|
|
|
|
static void expectSilenceBetween(const GeneratedAudio& audio, float startTimeSeconds, float stopTimeSeconds, const std::string& testLabel) {
|
|
auto silenceResult = findLongestSilence(
|
|
audio.data,
|
|
LongestSilenceOptions {
|
|
.sampleRate = audio.sampleRate,
|
|
.minSilenceLengthSeconds = 0.02f,
|
|
.maximumSilenceStartSamples = audio.sampleCount,
|
|
.returnFirstMatch = false,
|
|
}
|
|
);
|
|
|
|
if (!silenceResult.isValid) {
|
|
fail("Error: No silence found: " + testLabel);
|
|
}
|
|
|
|
auto checkEndpoint = [&] (int actualValueSamples, float expectedValueSeconds, const std::string& description) {
|
|
float actualValueSeconds = samplesToSeconds(actualValueSamples, audio.sampleRate);
|
|
float tolerance = 0.1f; // 100ms
|
|
if (std::abs(expectedValueSeconds - actualValueSeconds) > tolerance) {
|
|
std::stringstream messageBuilder;
|
|
messageBuilder << "Error: Silence " << description << " mismatch: ";
|
|
messageBuilder << "got " << actualValueSeconds << "s expected " << expectedValueSeconds << "s. ";
|
|
messageBuilder << testLabel;
|
|
fail(messageBuilder.str());
|
|
}
|
|
};
|
|
|
|
checkEndpoint(silenceResult.start, startTimeSeconds, "start time");
|
|
checkEndpoint(silenceResult.end, stopTimeSeconds, "stop time");
|
|
|
|
logTestPass(testLabel);
|
|
}
|