mirror of
https://github.com/overte-org/overte.git
synced 2025-08-08 18:16:45 +02:00
Job #19694 - Add option for larger number of frames to the audio scope display
This commit is contained in:
parent
c52592311f
commit
619db8ee93
4 changed files with 171 additions and 42 deletions
|
@ -97,9 +97,11 @@ Audio::Audio(int16_t initialJitterBufferSamples, QObject* parent) :
|
||||||
_scopeEnabledPause(false),
|
_scopeEnabledPause(false),
|
||||||
_scopeInputOffset(0),
|
_scopeInputOffset(0),
|
||||||
_scopeOutputOffset(0),
|
_scopeOutputOffset(0),
|
||||||
_scopeInput(SAMPLES_PER_SCOPE_WIDTH * sizeof(int16_t), 0),
|
_framesPerScope(DEFAULT_FRAMES_PER_SCOPE),
|
||||||
_scopeOutputLeft(SAMPLES_PER_SCOPE_WIDTH * sizeof(int16_t), 0),
|
_samplesPerScope(NETWORK_SAMPLES_PER_FRAME * _framesPerScope),
|
||||||
_scopeOutputRight(SAMPLES_PER_SCOPE_WIDTH * sizeof(int16_t), 0)
|
_scopeInput(0),
|
||||||
|
_scopeOutputLeft(0),
|
||||||
|
_scopeOutputRight(0)
|
||||||
{
|
{
|
||||||
// clear the array of locally injected samples
|
// clear the array of locally injected samples
|
||||||
memset(_localProceduralSamples, 0, NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL);
|
memset(_localProceduralSamples, 0, NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL);
|
||||||
|
@ -592,7 +594,7 @@ void Audio::handleAudioInput() {
|
||||||
unsigned int monoAudioChannel = 0;
|
unsigned int monoAudioChannel = 0;
|
||||||
addBufferToScope(_scopeInput, _scopeInputOffset, monoAudioSamples, monoAudioChannel, numMonoAudioChannels);
|
addBufferToScope(_scopeInput, _scopeInputOffset, monoAudioSamples, monoAudioChannel, numMonoAudioChannels);
|
||||||
_scopeInputOffset += NETWORK_SAMPLES_PER_FRAME;
|
_scopeInputOffset += NETWORK_SAMPLES_PER_FRAME;
|
||||||
_scopeInputOffset %= SAMPLES_PER_SCOPE_WIDTH;
|
_scopeInputOffset %= _samplesPerScope;
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeList* nodeList = NodeList::getInstance();
|
NodeList* nodeList = NodeList::getInstance();
|
||||||
|
@ -849,7 +851,7 @@ void Audio::processReceivedAudio(const QByteArray& audioByteArray) {
|
||||||
samples, audioChannel, numAudioChannels);
|
samples, audioChannel, numAudioChannels);
|
||||||
|
|
||||||
_scopeOutputOffset += NETWORK_SAMPLES_PER_FRAME;
|
_scopeOutputOffset += NETWORK_SAMPLES_PER_FRAME;
|
||||||
_scopeOutputOffset %= SAMPLES_PER_SCOPE_WIDTH;
|
_scopeOutputOffset %= _samplesPerScope;
|
||||||
samples += NETWORK_SAMPLES_PER_FRAME * numAudioChannels;
|
samples += NETWORK_SAMPLES_PER_FRAME * numAudioChannels;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1060,25 +1062,73 @@ void Audio::renderToolBox(int x, int y, bool boxed) {
|
||||||
glDisable(GL_TEXTURE_2D);
|
glDisable(GL_TEXTURE_2D);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Audio::toggleScope() {
|
||||||
|
_scopeEnabled = !_scopeEnabled;
|
||||||
|
if (_scopeEnabled) {
|
||||||
|
_scopeInputOffset = 0;
|
||||||
|
_scopeOutputOffset = 0;
|
||||||
|
allocateScope();
|
||||||
|
} else {
|
||||||
|
freeScope();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Audio::toggleScopePause() {
|
void Audio::toggleScopePause() {
|
||||||
_scopeEnabledPause = !_scopeEnabledPause;
|
_scopeEnabledPause = !_scopeEnabledPause;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Audio::toggleScope() {
|
void Audio::selectAudioScopeFiveFrames() {
|
||||||
_scopeEnabled = !_scopeEnabled;
|
if (Menu::getInstance()->isOptionChecked(MenuOption::AudioScopeFiveFrames)) {
|
||||||
if (_scopeEnabled) {
|
reallocateScope(5);
|
||||||
static const int width = SAMPLES_PER_SCOPE_WIDTH;
|
}
|
||||||
_scopeInputOffset = 0;
|
}
|
||||||
_scopeOutputOffset = 0;
|
|
||||||
memset(_scopeInput.data(), 0, width * sizeof(int16_t));
|
void Audio::selectAudioScopeTwentyFrames() {
|
||||||
memset(_scopeOutputLeft.data(), 0, width * sizeof(int16_t));
|
if (Menu::getInstance()->isOptionChecked(MenuOption::AudioScopeTwentyFrames)) {
|
||||||
memset(_scopeOutputRight.data(), 0, width * sizeof(int16_t));
|
reallocateScope(20);
|
||||||
_scopeEnabledPause = false;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Audio::selectAudioScopeFiftyFrames() {
|
||||||
|
if (Menu::getInstance()->isOptionChecked(MenuOption::AudioScopeFiftyFrames)) {
|
||||||
|
reallocateScope(50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Audio::allocateScope() {
|
||||||
|
int num = _samplesPerScope * sizeof(int16_t);
|
||||||
|
_scopeInput = new QByteArray(num, 0);
|
||||||
|
_scopeOutputLeft = new QByteArray(num, 0);
|
||||||
|
_scopeOutputRight = new QByteArray(num, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Audio::reallocateScope(int frames) {
|
||||||
|
if (_framesPerScope != frames) {
|
||||||
|
_framesPerScope = frames;
|
||||||
|
_samplesPerScope = NETWORK_SAMPLES_PER_FRAME * _framesPerScope;
|
||||||
|
QMutexLocker lock(&_guard);
|
||||||
|
freeScope();
|
||||||
|
allocateScope();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Audio::freeScope() {
|
||||||
|
if (_scopeInput) {
|
||||||
|
delete _scopeInput;
|
||||||
|
_scopeInput = 0;
|
||||||
|
}
|
||||||
|
if (_scopeOutputLeft) {
|
||||||
|
delete _scopeOutputLeft;
|
||||||
|
_scopeOutputLeft = 0;
|
||||||
|
}
|
||||||
|
if (_scopeOutputRight) {
|
||||||
|
delete _scopeOutputRight;
|
||||||
|
_scopeOutputRight = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Audio::addBufferToScope(
|
void Audio::addBufferToScope(
|
||||||
QByteArray& byteArray, unsigned int frameOffset, const int16_t* source, unsigned int sourceChannel, unsigned int sourceNumberOfChannels) {
|
QByteArray* byteArray, unsigned int frameOffset, const int16_t* source, unsigned int sourceChannel, unsigned int sourceNumberOfChannels) {
|
||||||
|
|
||||||
// Constant multiplier to map sample value to vertical size of scope
|
// Constant multiplier to map sample value to vertical size of scope
|
||||||
float multiplier = (float)MULTIPLIER_SCOPE_HEIGHT / logf(2.0f);
|
float multiplier = (float)MULTIPLIER_SCOPE_HEIGHT / logf(2.0f);
|
||||||
|
@ -1089,8 +1139,9 @@ void Audio::addBufferToScope(
|
||||||
// Temporary variable receives mapping of sample value
|
// Temporary variable receives mapping of sample value
|
||||||
int16_t value;
|
int16_t value;
|
||||||
|
|
||||||
|
QMutexLocker lock(&_guard);
|
||||||
// Short int pointer to mapped samples in byte array
|
// Short int pointer to mapped samples in byte array
|
||||||
int16_t* destination = (int16_t*) byteArray.data();
|
int16_t* destination = (int16_t*) byteArray->data();
|
||||||
|
|
||||||
for (unsigned int i = 0; i < NETWORK_SAMPLES_PER_FRAME; i++) {
|
for (unsigned int i = 0; i < NETWORK_SAMPLES_PER_FRAME; i++) {
|
||||||
sample = (float)source[i * sourceNumberOfChannels + sourceChannel];
|
sample = (float)source[i * sourceNumberOfChannels + sourceChannel];
|
||||||
|
@ -1116,18 +1167,20 @@ void Audio::renderScope(int width, int height) {
|
||||||
static const float outputLeftColor[4] = { 0.7f, .3f, 0.3f, 0.6f };
|
static const float outputLeftColor[4] = { 0.7f, .3f, 0.3f, 0.6f };
|
||||||
static const float outputRightColor[4] = { 0.3f, .3f, 0.7f, 0.6f };
|
static const float outputRightColor[4] = { 0.3f, .3f, 0.7f, 0.6f };
|
||||||
static const int gridRows = 2;
|
static const int gridRows = 2;
|
||||||
static const int gridCols = 5;
|
int gridCols = _framesPerScope;
|
||||||
|
|
||||||
int x = (width - SAMPLES_PER_SCOPE_WIDTH) / 2;
|
int x = (width - SCOPE_WIDTH) / 2;
|
||||||
int y = (height - SAMPLES_PER_SCOPE_HEIGHT) / 2;
|
int y = (height - SCOPE_HEIGHT) / 2;
|
||||||
int w = SAMPLES_PER_SCOPE_WIDTH;
|
int w = SCOPE_WIDTH;
|
||||||
int h = SAMPLES_PER_SCOPE_HEIGHT;
|
int h = SCOPE_HEIGHT;
|
||||||
|
|
||||||
renderBackground(backgroundColor, x, y, w, h);
|
renderBackground(backgroundColor, x, y, w, h);
|
||||||
renderGrid(gridColor, x, y, w, h, gridRows, gridCols);
|
renderGrid(gridColor, x, y, w, h, gridRows, gridCols);
|
||||||
renderLineStrip(inputColor, x, y, w, _scopeInputOffset, _scopeInput);
|
|
||||||
renderLineStrip(outputLeftColor, x, y, w, _scopeOutputOffset, _scopeOutputLeft);
|
QMutexLocker lock(&_guard);
|
||||||
renderLineStrip(outputRightColor, x, y, w, _scopeOutputOffset, _scopeOutputRight);
|
renderLineStrip(inputColor, x, y, _samplesPerScope, _scopeInputOffset, _scopeInput);
|
||||||
|
renderLineStrip(outputLeftColor, x, y, _samplesPerScope, _scopeOutputOffset, _scopeOutputLeft);
|
||||||
|
renderLineStrip(outputRightColor, x, y, _samplesPerScope, _scopeOutputOffset, _scopeOutputRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Audio::renderBackground(const float* color, int x, int y, int width, int height) {
|
void Audio::renderBackground(const float* color, int x, int y, int width, int height) {
|
||||||
|
@ -1170,21 +1223,54 @@ void Audio::renderGrid(const float* color, int x, int y, int width, int height,
|
||||||
glColor4f(1, 1, 1, 1);
|
glColor4f(1, 1, 1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Audio::renderLineStrip(const float* color, int x, int y, int n, int offset, const QByteArray& byteArray) {
|
void Audio::renderLineStrip(const float* color, int x, int y, int n, int offset, const QByteArray* byteArray) {
|
||||||
|
|
||||||
glColor4fv(color);
|
glColor4fv(color);
|
||||||
glBegin(GL_LINE_STRIP);
|
glBegin(GL_LINE_STRIP);
|
||||||
|
|
||||||
int16_t sample;
|
int16_t sample;
|
||||||
int16_t* samples = ((int16_t*) byteArray.data()) + offset;
|
int16_t* samples = ((int16_t*) byteArray->data()) + offset;
|
||||||
y += SAMPLES_PER_SCOPE_HEIGHT / 2;
|
int numSamplesToAverage = _framesPerScope / DEFAULT_FRAMES_PER_SCOPE;
|
||||||
for (int i = n - offset; --i >= 0; ) {
|
int count = (n - offset) / numSamplesToAverage;
|
||||||
sample = *samples++;
|
int remainder = (n - offset) % numSamplesToAverage;
|
||||||
|
y += SCOPE_HEIGHT / 2;
|
||||||
|
|
||||||
|
// Compute and draw the sample averages from the offset position
|
||||||
|
for (int i = count; --i >= 0; ) {
|
||||||
|
sample = 0;
|
||||||
|
for (int j = numSamplesToAverage; --j >= 0; ) {
|
||||||
|
sample += *samples++;
|
||||||
|
}
|
||||||
|
sample /= numSamplesToAverage;
|
||||||
glVertex2i(x++, y - sample);
|
glVertex2i(x++, y - sample);
|
||||||
}
|
}
|
||||||
samples = (int16_t*) byteArray.data();
|
|
||||||
for (int i = offset; --i >= 0; ) {
|
// Compute and draw the sample average across the wrap boundary
|
||||||
sample = *samples++;
|
if (remainder != 0) {
|
||||||
|
sample = 0;
|
||||||
|
for (int j = remainder; --j >= 0; ) {
|
||||||
|
sample += *samples++;
|
||||||
|
}
|
||||||
|
|
||||||
|
samples = (int16_t*) byteArray->data();
|
||||||
|
|
||||||
|
for (int j = numSamplesToAverage - remainder; --j >= 0; ) {
|
||||||
|
sample += *samples++;
|
||||||
|
}
|
||||||
|
sample /= numSamplesToAverage;
|
||||||
|
glVertex2i(x++, y - sample);
|
||||||
|
} else {
|
||||||
|
samples = (int16_t*) byteArray->data();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute and draw the sample average from the beginning to the offset
|
||||||
|
count = (offset - remainder) / numSamplesToAverage;
|
||||||
|
for (int i = count; --i >= 0; ) {
|
||||||
|
sample = 0;
|
||||||
|
for (int j = numSamplesToAverage; --j >= 0; ) {
|
||||||
|
sample += *samples++;
|
||||||
|
}
|
||||||
|
sample /= numSamplesToAverage;
|
||||||
glVertex2i(x++, y - sample);
|
glVertex2i(x++, y - sample);
|
||||||
}
|
}
|
||||||
glEnd();
|
glEnd();
|
||||||
|
|
|
@ -85,6 +85,9 @@ public slots:
|
||||||
void toggleScope();
|
void toggleScope();
|
||||||
void toggleScopePause();
|
void toggleScopePause();
|
||||||
void toggleAudioSpatialProcessing();
|
void toggleAudioSpatialProcessing();
|
||||||
|
void selectAudioScopeFiveFrames();
|
||||||
|
void selectAudioScopeTwentyFrames();
|
||||||
|
void selectAudioScopeFiftyFrames();
|
||||||
|
|
||||||
virtual void handleAudioByteArray(const QByteArray& audioByteArray);
|
virtual void handleAudioByteArray(const QByteArray& audioByteArray);
|
||||||
|
|
||||||
|
@ -197,28 +200,36 @@ private:
|
||||||
int calculateNumberOfFrameSamples(int numBytes);
|
int calculateNumberOfFrameSamples(int numBytes);
|
||||||
float calculateDeviceToNetworkInputRatio(int numBytes);
|
float calculateDeviceToNetworkInputRatio(int numBytes);
|
||||||
|
|
||||||
|
// Audio scope methods for allocation/deallocation
|
||||||
|
void allocateScope();
|
||||||
|
void freeScope();
|
||||||
|
void reallocateScope(int frames);
|
||||||
|
|
||||||
// Audio scope methods for data acquisition
|
// Audio scope methods for data acquisition
|
||||||
void addBufferToScope(
|
void addBufferToScope(
|
||||||
QByteArray& byteArray, unsigned int frameOffset, const int16_t* source, unsigned int sourceChannel, unsigned int sourceNumberOfChannels);
|
QByteArray* byteArray, unsigned int frameOffset, const int16_t* source, unsigned int sourceChannel, unsigned int sourceNumberOfChannels);
|
||||||
|
|
||||||
// Audio scope methods for rendering
|
// Audio scope methods for rendering
|
||||||
void renderBackground(const float* color, int x, int y, int width, int height);
|
void renderBackground(const float* color, int x, int y, int width, int height);
|
||||||
void renderGrid(const float* color, int x, int y, int width, int height, int rows, int cols);
|
void renderGrid(const float* color, int x, int y, int width, int height, int rows, int cols);
|
||||||
void renderLineStrip(const float* color, int x, int y, int n, int offset, const QByteArray& byteArray);
|
void renderLineStrip(const float* color, int x, int y, int n, int offset, const QByteArray* byteArray);
|
||||||
|
|
||||||
// Audio scope data
|
// Audio scope data
|
||||||
static const unsigned int NETWORK_SAMPLES_PER_FRAME = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL;
|
static const unsigned int NETWORK_SAMPLES_PER_FRAME = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL;
|
||||||
static const unsigned int FRAMES_PER_SCOPE = 5;
|
static const unsigned int DEFAULT_FRAMES_PER_SCOPE = 5;
|
||||||
static const unsigned int SAMPLES_PER_SCOPE_WIDTH = FRAMES_PER_SCOPE * NETWORK_SAMPLES_PER_FRAME;
|
static const unsigned int SCOPE_WIDTH = NETWORK_SAMPLES_PER_FRAME * DEFAULT_FRAMES_PER_SCOPE;
|
||||||
static const unsigned int MULTIPLIER_SCOPE_HEIGHT = 20;
|
static const unsigned int MULTIPLIER_SCOPE_HEIGHT = 20;
|
||||||
static const unsigned int SAMPLES_PER_SCOPE_HEIGHT = 2 * 15 * MULTIPLIER_SCOPE_HEIGHT;
|
static const unsigned int SCOPE_HEIGHT = 2 * 15 * MULTIPLIER_SCOPE_HEIGHT;
|
||||||
bool _scopeEnabled;
|
bool _scopeEnabled;
|
||||||
bool _scopeEnabledPause;
|
bool _scopeEnabledPause;
|
||||||
int _scopeInputOffset;
|
int _scopeInputOffset;
|
||||||
int _scopeOutputOffset;
|
int _scopeOutputOffset;
|
||||||
QByteArray _scopeInput;
|
int _framesPerScope;
|
||||||
QByteArray _scopeOutputLeft;
|
int _samplesPerScope;
|
||||||
QByteArray _scopeOutputRight;
|
QMutex _guard;
|
||||||
|
QByteArray* _scopeInput;
|
||||||
|
QByteArray* _scopeOutputLeft;
|
||||||
|
QByteArray* _scopeOutputRight;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -417,7 +417,8 @@ Menu::Menu() :
|
||||||
false,
|
false,
|
||||||
appInstance->getAudio(),
|
appInstance->getAudio(),
|
||||||
SLOT(toggleToneInjection()));
|
SLOT(toggleToneInjection()));
|
||||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioScope, Qt::CTRL | Qt::Key_P, false,
|
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioScope,
|
||||||
|
Qt::CTRL | Qt::Key_P, false,
|
||||||
appInstance->getAudio(),
|
appInstance->getAudio(),
|
||||||
SLOT(toggleScope()));
|
SLOT(toggleScope()));
|
||||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioScopePause,
|
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioScopePause,
|
||||||
|
@ -426,6 +427,33 @@ Menu::Menu() :
|
||||||
appInstance->getAudio(),
|
appInstance->getAudio(),
|
||||||
SLOT(toggleScopePause()));
|
SLOT(toggleScopePause()));
|
||||||
|
|
||||||
|
QMenu* audioScopeMenu = audioDebugMenu->addMenu("Audio Scope Options");
|
||||||
|
addDisabledActionAndSeparator(audioScopeMenu, "Display Frames");
|
||||||
|
{
|
||||||
|
QAction *fiveFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeFiveFrames,
|
||||||
|
0,
|
||||||
|
true,
|
||||||
|
appInstance->getAudio(),
|
||||||
|
SLOT(selectAudioScopeFiveFrames()));
|
||||||
|
|
||||||
|
QAction *twentyFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeTwentyFrames,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
appInstance->getAudio(),
|
||||||
|
SLOT(selectAudioScopeTwentyFrames()));
|
||||||
|
|
||||||
|
QAction *fiftyFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeFiftyFrames,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
appInstance->getAudio(),
|
||||||
|
SLOT(selectAudioScopeFiftyFrames()));
|
||||||
|
|
||||||
|
QActionGroup* audioScopeFramesGroup = new QActionGroup(audioScopeMenu);
|
||||||
|
audioScopeFramesGroup->addAction(fiveFrames);
|
||||||
|
audioScopeFramesGroup->addAction(twentyFrames);
|
||||||
|
audioScopeFramesGroup->addAction(fiftyFrames);
|
||||||
|
}
|
||||||
|
|
||||||
QMenu* spatialAudioMenu = audioDebugMenu->addMenu("Spatial Audio");
|
QMenu* spatialAudioMenu = audioDebugMenu->addMenu("Spatial Audio");
|
||||||
|
|
||||||
addCheckableActionToQMenuAndActionHash(spatialAudioMenu, MenuOption::AudioSpatialProcessing,
|
addCheckableActionToQMenuAndActionHash(spatialAudioMenu, MenuOption::AudioSpatialProcessing,
|
||||||
|
|
|
@ -279,6 +279,10 @@ namespace MenuOption {
|
||||||
const QString AudioNoiseReduction = "Audio Noise Reduction";
|
const QString AudioNoiseReduction = "Audio Noise Reduction";
|
||||||
const QString AudioScope = "Audio Scope";
|
const QString AudioScope = "Audio Scope";
|
||||||
const QString AudioScopePause = "Pause Audio Scope";
|
const QString AudioScopePause = "Pause Audio Scope";
|
||||||
|
const QString AudioScopeFrames = "Display Frames";
|
||||||
|
const QString AudioScopeFiveFrames = "Five";
|
||||||
|
const QString AudioScopeTwentyFrames = "Twenty";
|
||||||
|
const QString AudioScopeFiftyFrames = "Fifty";
|
||||||
const QString AudioToneInjection = "Inject Test Tone";
|
const QString AudioToneInjection = "Inject Test Tone";
|
||||||
const QString AudioSpatialProcessing = "Audio Spatial Processing";
|
const QString AudioSpatialProcessing = "Audio Spatial Processing";
|
||||||
const QString AudioSpatialProcessingHeadOriented = "Head Oriented";
|
const QString AudioSpatialProcessingHeadOriented = "Head Oriented";
|
||||||
|
|
Loading…
Reference in a new issue