mirror of
https://github.com/overte-org/overte.git
synced 2025-08-04 05:23:33 +02:00
Merge pull request #8610 from zzmp/test/audio-jitter
add audio jitter simulator testing tool
This commit is contained in:
commit
b14f43b11e
3 changed files with 47 additions and 14 deletions
|
@ -278,12 +278,18 @@ void setupPreferences() {
|
||||||
preferences->addPreference(preference);
|
preferences->addPreference(preference);
|
||||||
}
|
}
|
||||||
#if DEV_BUILD || PR_BUILD
|
#if DEV_BUILD || PR_BUILD
|
||||||
|
{
|
||||||
|
auto getter = []()->bool { return DependencyManager::get<AudioClient>()->isSimulatingJitter(); };
|
||||||
|
auto setter = [](bool value) { return DependencyManager::get<AudioClient>()->setIsSimulatingJitter(value); };
|
||||||
|
auto preference = new CheckPreference(AUDIO, "Packet jitter simulator", getter, setter);
|
||||||
|
preferences->addPreference(preference);
|
||||||
|
}
|
||||||
{
|
{
|
||||||
auto getter = []()->float { return DependencyManager::get<AudioClient>()->getGateThreshold(); };
|
auto getter = []()->float { return DependencyManager::get<AudioClient>()->getGateThreshold(); };
|
||||||
auto setter = [](float value) { return DependencyManager::get<AudioClient>()->setGateThreshold(value); };
|
auto setter = [](float value) { return DependencyManager::get<AudioClient>()->setGateThreshold(value); };
|
||||||
auto preference = new SpinnerPreference(AUDIO, "Debug gate threshold", getter, setter);
|
auto preference = new SpinnerPreference(AUDIO, "Packet throttle threshold", getter, setter);
|
||||||
preference->setMin(1);
|
preference->setMin(1);
|
||||||
preference->setMax((float)100);
|
preference->setMax(200);
|
||||||
preference->setStep(1);
|
preference->setStep(1);
|
||||||
preferences->addPreference(preference);
|
preferences->addPreference(preference);
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,8 +56,6 @@ static const int RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES = 100;
|
||||||
static const auto DEFAULT_POSITION_GETTER = []{ return Vectors::ZERO; };
|
static const auto DEFAULT_POSITION_GETTER = []{ return Vectors::ZERO; };
|
||||||
static const auto DEFAULT_ORIENTATION_GETTER = [] { return Quaternions::IDENTITY; };
|
static const auto DEFAULT_ORIENTATION_GETTER = [] { return Quaternions::IDENTITY; };
|
||||||
|
|
||||||
static const int DEFAULT_AUDIO_OUTPUT_GATE_THRESHOLD = 1;
|
|
||||||
|
|
||||||
Setting::Handle<bool> dynamicJitterBuffers("dynamicJitterBuffers", DEFAULT_DYNAMIC_JITTER_BUFFERS);
|
Setting::Handle<bool> dynamicJitterBuffers("dynamicJitterBuffers", DEFAULT_DYNAMIC_JITTER_BUFFERS);
|
||||||
Setting::Handle<int> maxFramesOverDesired("maxFramesOverDesired", DEFAULT_MAX_FRAMES_OVER_DESIRED);
|
Setting::Handle<int> maxFramesOverDesired("maxFramesOverDesired", DEFAULT_MAX_FRAMES_OVER_DESIRED);
|
||||||
Setting::Handle<int> staticDesiredJitterBufferFrames("staticDesiredJitterBufferFrames",
|
Setting::Handle<int> staticDesiredJitterBufferFrames("staticDesiredJitterBufferFrames",
|
||||||
|
@ -102,8 +100,7 @@ private:
|
||||||
|
|
||||||
AudioClient::AudioClient() :
|
AudioClient::AudioClient() :
|
||||||
AbstractAudioInterface(),
|
AbstractAudioInterface(),
|
||||||
_gateThreshold("audioOutputGateThreshold", DEFAULT_AUDIO_OUTPUT_GATE_THRESHOLD),
|
_gate(this),
|
||||||
_gate(this, _gateThreshold.get()),
|
|
||||||
_audioInput(NULL),
|
_audioInput(NULL),
|
||||||
_desiredInputFormat(),
|
_desiredInputFormat(),
|
||||||
_inputFormat(),
|
_inputFormat(),
|
||||||
|
@ -551,31 +548,53 @@ void AudioClient::handleAudioDataPacket(QSharedPointer<ReceivedMessage> message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioClient::Gate::Gate(AudioClient* audioClient, int threshold) :
|
AudioClient::Gate::Gate(AudioClient* audioClient) :
|
||||||
_audioClient(audioClient),
|
_audioClient(audioClient) {}
|
||||||
_threshold(threshold) {}
|
|
||||||
|
void AudioClient::Gate::setIsSimulatingJitter(bool enable) {
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
flush();
|
||||||
|
_isSimulatingJitter = enable;
|
||||||
|
}
|
||||||
|
|
||||||
void AudioClient::Gate::setThreshold(int threshold) {
|
void AudioClient::Gate::setThreshold(int threshold) {
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
flush();
|
flush();
|
||||||
_threshold = std::max(threshold, 1);
|
_threshold = std::max(threshold, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioClient::Gate::insert(QSharedPointer<ReceivedMessage> message) {
|
void AudioClient::Gate::insert(QSharedPointer<ReceivedMessage> message) {
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
|
||||||
// Short-circuit for normal behavior
|
// Short-circuit for normal behavior
|
||||||
if (_threshold == 1) {
|
if (_threshold == 1 && !_isSimulatingJitter) {
|
||||||
_audioClient->_receivedAudioStream.parseData(*message);
|
_audioClient->_receivedAudioStream.parseData(*message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Throttle the current packet until the next flush
|
||||||
_queue.push(message);
|
_queue.push(message);
|
||||||
_index++;
|
_index++;
|
||||||
|
|
||||||
if (_index % _threshold == 0) {
|
// When appropriate, flush all held packets to the received audio stream
|
||||||
|
if (_isSimulatingJitter) {
|
||||||
|
// The JITTER_FLUSH_CHANCE defines the discrete probability density function of jitter (ms),
|
||||||
|
// where f(t) = pow(1 - JITTER_FLUSH_CHANCE, (t / 10) * JITTER_FLUSH_CHANCE
|
||||||
|
// for t (ms) = 10, 20, ... (because typical packet timegap is 10ms),
|
||||||
|
// because there is a JITTER_FLUSH_CHANCE of any packet instigating a flush of all held packets.
|
||||||
|
static const float JITTER_FLUSH_CHANCE = 0.6f;
|
||||||
|
// It is set at 0.6 to give a low chance of spikes (>30ms, 2.56%) so that they are obvious,
|
||||||
|
// but settled within the measured 5s window in audio network stats.
|
||||||
|
if (randFloat() < JITTER_FLUSH_CHANCE) {
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
} else if (!(_index % _threshold)) {
|
||||||
flush();
|
flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioClient::Gate::flush() {
|
void AudioClient::Gate::flush() {
|
||||||
|
// Send all held packets to the received audio stream to be (eventually) played
|
||||||
while (!_queue.empty()) {
|
while (!_queue.empty()) {
|
||||||
_audioClient->_receivedAudioStream.parseData(*_queue.front());
|
_audioClient->_receivedAudioStream.parseData(*_queue.front());
|
||||||
_queue.pop();
|
_queue.pop();
|
||||||
|
|
|
@ -132,6 +132,9 @@ public:
|
||||||
int getOutputStarveDetectionThreshold() { return _outputStarveDetectionThreshold.get(); }
|
int getOutputStarveDetectionThreshold() { return _outputStarveDetectionThreshold.get(); }
|
||||||
void setOutputStarveDetectionThreshold(int threshold) { _outputStarveDetectionThreshold.set(threshold); }
|
void setOutputStarveDetectionThreshold(int threshold) { _outputStarveDetectionThreshold.set(threshold); }
|
||||||
|
|
||||||
|
bool isSimulatingJitter() { return _gate.isSimulatingJitter(); }
|
||||||
|
void setIsSimulatingJitter(bool enable) { _gate.setIsSimulatingJitter(enable); }
|
||||||
|
|
||||||
int getGateThreshold() { return _gate.getThreshold(); }
|
int getGateThreshold() { return _gate.getThreshold(); }
|
||||||
void setGateThreshold(int threshold) { _gate.setThreshold(threshold); }
|
void setGateThreshold(int threshold) { _gate.setThreshold(threshold); }
|
||||||
|
|
||||||
|
@ -230,7 +233,10 @@ private:
|
||||||
|
|
||||||
class Gate {
|
class Gate {
|
||||||
public:
|
public:
|
||||||
Gate(AudioClient* audioClient, int threshold);
|
Gate(AudioClient* audioClient);
|
||||||
|
|
||||||
|
bool isSimulatingJitter() { return _isSimulatingJitter; }
|
||||||
|
void setIsSimulatingJitter(bool enable);
|
||||||
|
|
||||||
int getThreshold() { return _threshold; }
|
int getThreshold() { return _threshold; }
|
||||||
void setThreshold(int threshold);
|
void setThreshold(int threshold);
|
||||||
|
@ -242,11 +248,13 @@ private:
|
||||||
|
|
||||||
AudioClient* _audioClient;
|
AudioClient* _audioClient;
|
||||||
std::queue<QSharedPointer<ReceivedMessage>> _queue;
|
std::queue<QSharedPointer<ReceivedMessage>> _queue;
|
||||||
|
std::mutex _mutex;
|
||||||
|
|
||||||
int _index{ 0 };
|
int _index{ 0 };
|
||||||
int _threshold;
|
int _threshold{ 1 };
|
||||||
|
bool _isSimulatingJitter{ false };
|
||||||
};
|
};
|
||||||
|
|
||||||
Setting::Handle<int> _gateThreshold;
|
|
||||||
Gate _gate;
|
Gate _gate;
|
||||||
|
|
||||||
Mutex _injectorsMutex;
|
Mutex _injectorsMutex;
|
||||||
|
|
Loading…
Reference in a new issue