From 619db8ee934dca1dcddf5f6fe53f34d20e77fbef Mon Sep 17 00:00:00 2001 From: matsukaze Date: Tue, 27 May 2014 23:24:28 -0400 Subject: [PATCH] Job #19694 - Add option for larger number of frames to the audio scope display --- interface/src/Audio.cpp | 152 +++++++++++++++++++++++++++++++--------- interface/src/Audio.h | 27 ++++--- interface/src/Menu.cpp | 30 +++++++- interface/src/Menu.h | 4 ++ 4 files changed, 171 insertions(+), 42 deletions(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index c5d6ba23cf..50ab720450 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -97,9 +97,11 @@ Audio::Audio(int16_t initialJitterBufferSamples, QObject* parent) : _scopeEnabledPause(false), _scopeInputOffset(0), _scopeOutputOffset(0), - _scopeInput(SAMPLES_PER_SCOPE_WIDTH * sizeof(int16_t), 0), - _scopeOutputLeft(SAMPLES_PER_SCOPE_WIDTH * sizeof(int16_t), 0), - _scopeOutputRight(SAMPLES_PER_SCOPE_WIDTH * sizeof(int16_t), 0) + _framesPerScope(DEFAULT_FRAMES_PER_SCOPE), + _samplesPerScope(NETWORK_SAMPLES_PER_FRAME * _framesPerScope), + _scopeInput(0), + _scopeOutputLeft(0), + _scopeOutputRight(0) { // clear the array of locally injected samples memset(_localProceduralSamples, 0, NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL); @@ -592,7 +594,7 @@ void Audio::handleAudioInput() { unsigned int monoAudioChannel = 0; addBufferToScope(_scopeInput, _scopeInputOffset, monoAudioSamples, monoAudioChannel, numMonoAudioChannels); _scopeInputOffset += NETWORK_SAMPLES_PER_FRAME; - _scopeInputOffset %= SAMPLES_PER_SCOPE_WIDTH; + _scopeInputOffset %= _samplesPerScope; } NodeList* nodeList = NodeList::getInstance(); @@ -849,7 +851,7 @@ void Audio::processReceivedAudio(const QByteArray& audioByteArray) { samples, audioChannel, numAudioChannels); _scopeOutputOffset += NETWORK_SAMPLES_PER_FRAME; - _scopeOutputOffset %= SAMPLES_PER_SCOPE_WIDTH; + _scopeOutputOffset %= _samplesPerScope; samples += NETWORK_SAMPLES_PER_FRAME * numAudioChannels; } } @@ -1060,25 +1062,73 @@ void Audio::renderToolBox(int x, int y, bool boxed) { glDisable(GL_TEXTURE_2D); } +void Audio::toggleScope() { + _scopeEnabled = !_scopeEnabled; + if (_scopeEnabled) { + _scopeInputOffset = 0; + _scopeOutputOffset = 0; + allocateScope(); + } else { + freeScope(); + } +} + void Audio::toggleScopePause() { _scopeEnabledPause = !_scopeEnabledPause; } -void Audio::toggleScope() { - _scopeEnabled = !_scopeEnabled; - if (_scopeEnabled) { - static const int width = SAMPLES_PER_SCOPE_WIDTH; - _scopeInputOffset = 0; - _scopeOutputOffset = 0; - memset(_scopeInput.data(), 0, width * sizeof(int16_t)); - memset(_scopeOutputLeft.data(), 0, width * sizeof(int16_t)); - memset(_scopeOutputRight.data(), 0, width * sizeof(int16_t)); - _scopeEnabledPause = false; +void Audio::selectAudioScopeFiveFrames() { + if (Menu::getInstance()->isOptionChecked(MenuOption::AudioScopeFiveFrames)) { + reallocateScope(5); + } +} + +void Audio::selectAudioScopeTwentyFrames() { + if (Menu::getInstance()->isOptionChecked(MenuOption::AudioScopeTwentyFrames)) { + reallocateScope(20); + } +} + +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( - 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 float multiplier = (float)MULTIPLIER_SCOPE_HEIGHT / logf(2.0f); @@ -1089,8 +1139,9 @@ void Audio::addBufferToScope( // Temporary variable receives mapping of sample value int16_t value; + QMutexLocker lock(&_guard); // 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++) { 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 outputRightColor[4] = { 0.3f, .3f, 0.7f, 0.6f }; static const int gridRows = 2; - static const int gridCols = 5; + int gridCols = _framesPerScope; - int x = (width - SAMPLES_PER_SCOPE_WIDTH) / 2; - int y = (height - SAMPLES_PER_SCOPE_HEIGHT) / 2; - int w = SAMPLES_PER_SCOPE_WIDTH; - int h = SAMPLES_PER_SCOPE_HEIGHT; + int x = (width - SCOPE_WIDTH) / 2; + int y = (height - SCOPE_HEIGHT) / 2; + int w = SCOPE_WIDTH; + int h = SCOPE_HEIGHT; renderBackground(backgroundColor, x, y, w, h); renderGrid(gridColor, x, y, w, h, gridRows, gridCols); - renderLineStrip(inputColor, x, y, w, _scopeInputOffset, _scopeInput); - renderLineStrip(outputLeftColor, x, y, w, _scopeOutputOffset, _scopeOutputLeft); - renderLineStrip(outputRightColor, x, y, w, _scopeOutputOffset, _scopeOutputRight); + + QMutexLocker lock(&_guard); + 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) { @@ -1170,21 +1223,54 @@ void Audio::renderGrid(const float* color, int x, int y, int width, int height, 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); glBegin(GL_LINE_STRIP); int16_t sample; - int16_t* samples = ((int16_t*) byteArray.data()) + offset; - y += SAMPLES_PER_SCOPE_HEIGHT / 2; - for (int i = n - offset; --i >= 0; ) { - sample = *samples++; + int16_t* samples = ((int16_t*) byteArray->data()) + offset; + int numSamplesToAverage = _framesPerScope / DEFAULT_FRAMES_PER_SCOPE; + int count = (n - offset) / numSamplesToAverage; + 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); } - samples = (int16_t*) byteArray.data(); - for (int i = offset; --i >= 0; ) { - sample = *samples++; + + // Compute and draw the sample average across the wrap boundary + 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); } glEnd(); diff --git a/interface/src/Audio.h b/interface/src/Audio.h index 277c606d4b..79f0f84ff5 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -85,6 +85,9 @@ public slots: void toggleScope(); void toggleScopePause(); void toggleAudioSpatialProcessing(); + void selectAudioScopeFiveFrames(); + void selectAudioScopeTwentyFrames(); + void selectAudioScopeFiftyFrames(); virtual void handleAudioByteArray(const QByteArray& audioByteArray); @@ -197,28 +200,36 @@ private: int calculateNumberOfFrameSamples(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 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 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 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 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 SAMPLES_PER_SCOPE_WIDTH = FRAMES_PER_SCOPE * NETWORK_SAMPLES_PER_FRAME; + static const unsigned int DEFAULT_FRAMES_PER_SCOPE = 5; + 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 SAMPLES_PER_SCOPE_HEIGHT = 2 * 15 * MULTIPLIER_SCOPE_HEIGHT; + static const unsigned int SCOPE_HEIGHT = 2 * 15 * MULTIPLIER_SCOPE_HEIGHT; bool _scopeEnabled; bool _scopeEnabledPause; int _scopeInputOffset; int _scopeOutputOffset; - QByteArray _scopeInput; - QByteArray _scopeOutputLeft; - QByteArray _scopeOutputRight; + int _framesPerScope; + int _samplesPerScope; + QMutex _guard; + QByteArray* _scopeInput; + QByteArray* _scopeOutputLeft; + QByteArray* _scopeOutputRight; }; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 92121e719a..45b4089a11 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -417,7 +417,8 @@ Menu::Menu() : false, appInstance->getAudio(), SLOT(toggleToneInjection())); - addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioScope, Qt::CTRL | Qt::Key_P, false, + addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioScope, + Qt::CTRL | Qt::Key_P, false, appInstance->getAudio(), SLOT(toggleScope())); addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioScopePause, @@ -426,6 +427,33 @@ Menu::Menu() : appInstance->getAudio(), 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"); addCheckableActionToQMenuAndActionHash(spatialAudioMenu, MenuOption::AudioSpatialProcessing, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 70f4f62ce4..012dc1662c 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -279,6 +279,10 @@ namespace MenuOption { const QString AudioNoiseReduction = "Audio Noise Reduction"; const QString AudioScope = "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 AudioSpatialProcessing = "Audio Spatial Processing"; const QString AudioSpatialProcessingHeadOriented = "Head Oriented";