diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 8a788df831..0d114b7208 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -40,6 +40,7 @@ #include #include "Audio.h" + #include "Menu.h" #include "Util.h" #include "PositionalAudioStream.h" @@ -82,7 +83,7 @@ Audio::Audio(QObject* parent) : _noiseGateSampleCounter(0), _noiseGateOpen(false), _noiseGateEnabled(true), - _toneInjectionEnabled(false), + _audioSourceInjectEnabled(false), _noiseGateFramesToClose(0), _totalInputAudioSamples(0), _collisionSoundMagnitude(0.0f), @@ -102,6 +103,8 @@ Audio::Audio(QObject* parent) : _framesPerScope(DEFAULT_FRAMES_PER_SCOPE), _samplesPerScope(NETWORK_SAMPLES_PER_FRAME * _framesPerScope), _peqEnabled(false), + _noiseSourceEnabled(false), + _toneSourceEnabled(true), _scopeInput(0), _scopeOutputLeft(0), _scopeOutputRight(0), @@ -137,6 +140,10 @@ void Audio::reset() { _receivedAudioStream.reset(); resetStats(); _peq.reset(); + _noiseSource.reset(); + _toneSource.reset(); + _sourceGain.reset(); + _inputGain.reset(); } void Audio::resetStats() { @@ -424,14 +431,25 @@ void Audio::start() { qDebug() << "Unable to set up audio output because of a problem with output format."; } - _peq.initialize( _inputFormat.sampleRate(), _audioInput->bufferSize() ); - + _inputFrameBuffer.initialize( _inputFormat.channelCount(), _audioInput->bufferSize() * 2 ); + _peq.initialize( _inputFormat.sampleRate() ); + _inputGain.initialize(); + _sourceGain.initialize(); + _noiseSource.initialize(); + _toneSource.initialize(); + _sourceGain.setParameters(0.25f,0.0f); + _inputGain.setParameters(1.0f,0.0f); } void Audio::stop() { + _inputFrameBuffer.finalize(); _peq.finalize(); - + _inputGain.finalize(); + _sourceGain.finalize(); + _noiseSource.finalize(); + _toneSource.finalize(); + // "switch" to invalid devices in order to shut down the state switchInputToAudioDevice(QAudioDeviceInfo()); switchOutputToAudioDevice(QAudioDeviceInfo()); @@ -477,14 +495,30 @@ void Audio::handleAudioInput() { QByteArray inputByteArray = _inputDevice->readAll(); + int16_t* inputFrameData = (int16_t*)inputByteArray.data(); + const int inputFrameCount = inputByteArray.size() / sizeof(int16_t); + + _inputFrameBuffer.copyFrames(1, inputFrameCount, inputFrameData, false /*copy in*/); + + _inputGain.render(_inputFrameBuffer); // input/mic gain+mute + + // Add audio source injection if enabled + if (_audioSourceInjectEnabled && !_muted) { + + if (_toneSourceEnabled) { // sine generator + _toneSource.render(_inputFrameBuffer); + } + else if(_noiseSourceEnabled) { // pink noise generator + _noiseSource.render(_inputFrameBuffer); + } + _sourceGain.render(_inputFrameBuffer); // post gain + } if (_peqEnabled && !_muted) { - // we wish to pre-filter our captured input, prior to loopback - - int16_t* ioBuffer = (int16_t*)inputByteArray.data(); - - _peq.render(ioBuffer, ioBuffer, inputByteArray.size() / sizeof(int16_t)); + _peq.render(_inputFrameBuffer); // 3-band parametric eq } + _inputFrameBuffer.copyFrames(1, inputFrameCount, inputFrameData, true /*copy out*/); + if (Menu::getInstance()->isOptionChecked(MenuOption::EchoLocalAudio) && !_muted && _audioOutput) { // if this person wants local loopback add that to the locally injected audio @@ -522,7 +556,7 @@ void Audio::handleAudioInput() { int16_t* inputAudioSamples = new int16_t[inputSamplesRequired]; _inputRingBuffer.readSamples(inputAudioSamples, inputSamplesRequired); - + const int numNetworkBytes = _isStereoInput ? NETWORK_BUFFER_LENGTH_BYTES_STEREO : NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL; const int numNetworkSamples = _isStereoInput ? NETWORK_BUFFER_LENGTH_SAMPLES_STEREO : NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; @@ -599,20 +633,8 @@ void Audio::handleAudioInput() { _dcOffset = DC_OFFSET_AVERAGING * _dcOffset + (1.0f - DC_OFFSET_AVERAGING) * measuredDcOffset; } - // Add tone injection if enabled - const float TONE_FREQ = 220.0f / SAMPLE_RATE * TWO_PI; - const float QUARTER_VOLUME = 8192.0f; - if (_toneInjectionEnabled) { - loudness = 0.0f; - for (int i = 0; i < NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) { - networkAudioSamples[i] = QUARTER_VOLUME * sinf(TONE_FREQ * (float)(i + _proceduralEffectSample)); - loudness += fabsf(networkAudioSamples[i]); - } - } - _lastInputLoudness = fabs(loudness / NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL); - // If Noise Gate is enabled, check and turn the gate on and off - if (!_toneInjectionEnabled && _noiseGateEnabled) { + if (!_audioSourceInjectEnabled && _noiseGateEnabled) { float averageOfAllSampleFrames = 0.0f; _noiseSampleFrames[_noiseGateSampleCounter++] = _lastInputLoudness; if (_noiseGateSampleCounter == NUMBER_OF_NOISE_SAMPLE_FRAMES) { @@ -1041,8 +1063,18 @@ void Audio::processProceduralAudio(int16_t* monoInput, int numSamples) { } } -void Audio::toggleToneInjection() { - _toneInjectionEnabled = !_toneInjectionEnabled; +void Audio::toggleAudioSourceInject() { + _audioSourceInjectEnabled = !_audioSourceInjectEnabled; +} + +void Audio::selectAudioSourcePinkNoise() { + _noiseSourceEnabled = Menu::getInstance()->isOptionChecked(MenuOption::AudioSourcePinkNoise); + _toneSourceEnabled = !_noiseSourceEnabled; +} + +void Audio::selectAudioSourceSine440() { + _toneSourceEnabled = Menu::getInstance()->isOptionChecked(MenuOption::AudioSourceSine440); + _noiseSourceEnabled = !_toneSourceEnabled; } void Audio::toggleAudioSpatialProcessing() { diff --git a/interface/src/Audio.h b/interface/src/Audio.h index b5069f3b3a..f73e0331de 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -20,6 +20,12 @@ #include "Recorder.h" #include "RingBufferHistory.h" #include "MovingMinMaxAvg.h" +#include "AudioRingBuffer.h" +#include "AudioFormat.h" +#include "AudioBuffer.h" +#include "AudioSourceTone.h" +#include "AudioSourceNoise.h" +#include "AudioGain.h" #include "AudioFilter.h" #include "AudioFilterBank.h" @@ -116,7 +122,9 @@ public slots: void audioMixerKilled(); void toggleMute(); void toggleAudioNoiseReduction(); - void toggleToneInjection(); + void toggleAudioSourceInject(); + void selectAudioSourcePinkNoise(); + void selectAudioSourceSine440(); void toggleScope(); void toggleScopePause(); void toggleStats(); @@ -199,7 +207,8 @@ private: int _noiseGateSampleCounter; bool _noiseGateOpen; bool _noiseGateEnabled; - bool _toneInjectionEnabled; + bool _audioSourceInjectEnabled; + int _noiseGateFramesToClose; int _totalInputAudioSamples; @@ -282,10 +291,27 @@ private: int _framesPerScope; int _samplesPerScope; - // Multi-band parametric EQ - bool _peqEnabled; - AudioFilterPEQ3m _peq; + // Input framebuffer + AudioBufferFloat32 _inputFrameBuffer; + + // Input gain + AudioGain _inputGain; + + // Post tone/pink noise generator gain + AudioGain _sourceGain; + // Pink noise source + bool _noiseSourceEnabled; + AudioSourcePinkNoise _noiseSource; + + // Tone source + bool _toneSourceEnabled; + AudioSourceTone _toneSource; + + // Multi-band parametric EQ + bool _peqEnabled; + AudioFilterPEQ3m _peq; + QMutex _guard; QByteArray* _scopeInput; QByteArray* _scopeOutputLeft; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 11c98c738c..d7679968ae 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -545,11 +545,31 @@ Menu::Menu() : 0, this, SLOT(muteEnvironment())); - addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioToneInjection, + + addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioSourceInject, 0, false, appInstance->getAudio(), - SLOT(toggleToneInjection())); + SLOT(toggleAudioSourceInject())); + QMenu* audioSourceMenu = audioDebugMenu->addMenu("Generated Audio Source"); + { + QAction *pinkNoise = addCheckableActionToQMenuAndActionHash(audioSourceMenu, MenuOption::AudioSourcePinkNoise, + 0, + false, + appInstance->getAudio(), + SLOT(selectAudioSourcePinkNoise())); + + QAction *sine440 = addCheckableActionToQMenuAndActionHash(audioSourceMenu, MenuOption::AudioSourceSine440, + 0, + true, + appInstance->getAudio(), + SLOT(selectAudioSourceSine440())); + + QActionGroup* audioSourceGroup = new QActionGroup(audioSourceMenu); + audioSourceGroup->addAction(pinkNoise); + audioSourceGroup->addAction(sine440); + } + addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioScope, Qt::CTRL | Qt::Key_P, false, appInstance->getAudio(), diff --git a/interface/src/Menu.h b/interface/src/Menu.h index dd77c50968..3b263add36 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -347,7 +347,9 @@ namespace MenuOption { const QString AudioSpatialProcessingSlightlyRandomSurfaces = "Slightly Random Surfaces"; const QString AudioSpatialProcessingStereoSource = "Stereo Source"; const QString AudioSpatialProcessingWithDiffusions = "With Diffusions"; - const QString AudioToneInjection = "Inject Test Tone"; + const QString AudioSourceInject = "Generated Audio"; + const QString AudioSourcePinkNoise = "Pink Noise"; + const QString AudioSourceSine440 = "Sine 440hz"; const QString Avatars = "Avatars"; const QString AvatarsReceiveShadows = "Avatars Receive Shadows"; const QString Bandwidth = "Bandwidth Display"; diff --git a/libraries/audio/src/AudioBuffer.h b/libraries/audio/src/AudioBuffer.h new file mode 100644 index 0000000000..0644139f80 --- /dev/null +++ b/libraries/audio/src/AudioBuffer.h @@ -0,0 +1,174 @@ +// +// AudioBuffer.h +// hifi +// +// Created by Craig Hansen-Sturm on 8/29/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_AudioBuffer_h +#define hifi_AudioBuffer_h + +#include + +template< typename T > +class AudioFrameBuffer { + + uint16_t _channelCount; + uint16_t _frameCount; + uint16_t _frameCountMax; + + T** _frameBuffer; + + void allocateFrames() { + _frameBuffer = new T*[_channelCount]; + if (_frameBuffer) { + for (uint16_t i = 0; i < _channelCount; ++i) { + _frameBuffer[i] = new T[_frameCountMax]; + } + } + } + + void deallocateFrames() { + if (_frameBuffer) { + for (uint16_t i = 0; i < _channelCount; ++i) { + delete _frameBuffer[i]; + } + delete _frameBuffer; + } + _frameBuffer = NULL; + } + +public: + + AudioFrameBuffer() : + _channelCount(0), + _frameCount(0), + _frameCountMax(0), + _frameBuffer(NULL) { + } + + AudioFrameBuffer(const uint16_t channelCount, const uint16_t frameCount) : + _channelCount(channelCount), + _frameCount(frameCount), + _frameCountMax(frameCount), + _frameBuffer(NULL) { + allocateFrames(); + } + + ~AudioFrameBuffer() { + finalize(); + } + + void initialize(const uint16_t channelCount, const uint16_t frameCount) { + if (_frameBuffer) { + finalize(); + } + _channelCount = channelCount; + _frameCount = frameCount; + _frameCountMax = frameCount; + allocateFrames(); + } + + void finalize() { + deallocateFrames(); + _channelCount = 0; + _frameCount = 0; + } + + T**& getFrameData() { + return _frameBuffer; + } + + uint16_t getChannelCount() { + return _channelCount; + } + + uint16_t getFrameCount() { + return _frameCount; + } + + void zeroFrames() { + if (!_frameBuffer) { + return; + } + for (uint16_t i = 0; i < _channelCount; ++i) { + memset(_frameBuffer[i], 0, sizeof(T)*_frameCountMax); + } + } + + template< typename S > + void copyFrames(uint16_t channelCount, const uint16_t frameCount, S* frames, const bool copyOut = false) { + if ( !_frameBuffer || !frames) { + return; + } + assert(channelCount == _channelCount); + assert(frameCount <= _frameCountMax); + + _frameCount = frameCount; // we allow copying fewer frames than we've allocated + + if (copyOut) { + S* dst = frames; + + if(typeid(T) == typeid(S)) { // source and destination types are the same + for (int i = 0; i < _frameCount; ++i) { + for (int j = 0; j < _channelCount; ++j) { + *dst++ = _frameBuffer[j][i]; + } + } + } + else { + if(typeid(T) == typeid(float32_t) && + typeid(S) == typeid(int16_t)) { + + const int scale = (2 << ((8 * sizeof(S)) - 1)); + + for (int i = 0; i < _frameCount; ++i) { + for (int j = 0; j < _channelCount; ++j) { + *dst++ = (S)(_frameBuffer[j][i] * scale); + } + } + } + else { + assert(0); // currently unsupported conversion + } + } + } + else { // copyIn + S* src = frames; + + if(typeid(T) == typeid(S)) { // source and destination types are the same + for (int i = 0; i < _frameCount; ++i) { + for (int j = 0; j < _channelCount; ++j) { + _frameBuffer[j][i] = *src++; + } + } + } + else { + if(typeid(T) == typeid(float32_t) && + typeid(S) == typeid(int16_t)) { + + const int scale = (2 << ((8 * sizeof(S)) - 1)); + + for (int i = 0; i < _frameCount; ++i) { + for (int j = 0; j < _channelCount; ++j) { + _frameBuffer[j][i] = ((T)(*src++)) / scale; + } + } + } + else { + assert(0); // currently unsupported conversion + } + } + } + } +}; + +typedef AudioFrameBuffer< float32_t > AudioBufferFloat32; +typedef AudioFrameBuffer< int32_t > AudioBufferSInt32; + +#endif // hifi_AudioBuffer_h + diff --git a/libraries/audio/src/AudioFilter.h b/libraries/audio/src/AudioFilter.h index c2de3860db..c9458f280e 100644 --- a/libraries/audio/src/AudioFilter.h +++ b/libraries/audio/src/AudioFilter.h @@ -37,11 +37,11 @@ public: // // ctor/dtor // - AudioBiquad() - : _xm1(0.) - , _xm2(0.) - , _ym1(0.) - , _ym2(0.) { + AudioBiquad() : + _xm1(0.), + _xm2(0.), + _ym1(0.), + _ym2(0.) { setParameters(0.,0.,0.,0.,0.); } diff --git a/libraries/audio/src/AudioFilterBank.cpp b/libraries/audio/src/AudioFilterBank.cpp index a7b969540a..6599b29a01 100644 --- a/libraries/audio/src/AudioFilterBank.cpp +++ b/libraries/audio/src/AudioFilterBank.cpp @@ -9,9 +9,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include #include #include "AudioRingBuffer.h" +#include "AudioFormat.h" +#include "AudioBuffer.h" #include "AudioFilter.h" #include "AudioFilterBank.h" diff --git a/libraries/audio/src/AudioFilterBank.h b/libraries/audio/src/AudioFilterBank.h index c523736a57..b9546999d9 100644 --- a/libraries/audio/src/AudioFilterBank.h +++ b/libraries/audio/src/AudioFilterBank.h @@ -49,9 +49,9 @@ public: // // ctor/dtor // - AudioFilterBank() - : _sampleRate(0.) - , _frameCount(0) { + AudioFilterBank() : + _sampleRate(0.0f), + _frameCount(0) { for (int i = 0; i < _channelCount; ++i) { _buffer[ i ] = NULL; } @@ -64,7 +64,7 @@ public: // // public interface // - void initialize(const float sampleRate, const int frameCount) { + void initialize(const float sampleRate, const int frameCount = 0) { finalize(); for (int i = 0; i < _channelCount; ++i) { @@ -141,6 +141,16 @@ public: } } + void render(AudioBufferFloat32& frameBuffer) { + + float32_t** samples = frameBuffer.getFrameData(); + for (uint16_t j = 0; j < frameBuffer.getChannelCount(); ++j) { + for (int i = 0; i < _filterCount; ++i) { + _filters[i][j].render( samples[j], samples[j], frameBuffer.getFrameCount() ); + } + } + } + void reset() { for (int i = 0; i < _filterCount; ++i) { for (int j = 0; j < _channelCount; ++j) { diff --git a/libraries/audio/src/AudioFormat.h b/libraries/audio/src/AudioFormat.h new file mode 100644 index 0000000000..9548138f58 --- /dev/null +++ b/libraries/audio/src/AudioFormat.h @@ -0,0 +1,85 @@ +// +// AudioFormat.h +// hifi +// +// Created by Craig Hansen-Sturm on 8/28/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_AudioFormat_h +#define hifi_AudioFormat_h + +#ifndef _FLOAT32_T +#define _FLOAT32_T +typedef float float32_t; +#endif + +#ifndef _FLOAT64_T +#define _FLOAT64_T +typedef double float64_t; +#endif + +// +// Audio format structure (currently for uncompressed streams only) +// + +struct AudioFormat { + + struct Flags { + uint32_t _isFloat : 1; + uint32_t _isSigned : 1; + uint32_t _isInterleaved : 1; + uint32_t _isBigEndian : 1; + uint32_t _isPacked : 1; + uint32_t _reserved : 27; + } _flags; + + uint32_t _bytesPerFrame; + uint32_t _channelsPerFrame; + uint32_t _bitsPerChannel; + float64_t _sampleRate; + + + AudioFormat() { + memset(this, 0, sizeof(*this)); + } + ~AudioFormat() { } + + AudioFormat& operator=(const AudioFormat& fmt) { + memcpy(this, &fmt, sizeof(*this)); + return *this; + } + + bool operator==(const AudioFormat& fmt) { + return memcmp(this, &fmt, sizeof(*this)) == 0; + } + + bool operator!=(const AudioFormat& fmt) { + return memcmp(this, &fmt, sizeof(*this)) != 0; + } + + void setCanonicalFloat32(uint32_t channels) { + assert(channels > 0 && channels <= 2); + _sampleRate = SAMPLE_RATE; // todo: create audio constants header + _bitsPerChannel = sizeof(float32_t) * 8; + _channelsPerFrame = channels; + _bytesPerFrame = _channelsPerFrame * _bitsPerChannel / 8; + _flags._isFloat = true; + _flags._isInterleaved = _channelsPerFrame > 1; + } + + void setCanonicalInt16(uint32_t channels) { + assert(channels > 0 && channels <= 2); + _sampleRate = SAMPLE_RATE; // todo: create audio constants header + _bitsPerChannel = sizeof(int16_t) * 8; + _channelsPerFrame = channels; + _bytesPerFrame = _channelsPerFrame * _bitsPerChannel / 8; + _flags._isSigned = true; + _flags._isInterleaved = _channelsPerFrame > 1; + } +}; + +#endif // hifi_AudioFormat_h diff --git a/libraries/audio/src/AudioGain.h b/libraries/audio/src/AudioGain.h new file mode 100644 index 0000000000..c2cc64cba5 --- /dev/null +++ b/libraries/audio/src/AudioGain.h @@ -0,0 +1,75 @@ +// +// AudioGain.h +// hifi +// +// Created by Craig Hansen-Sturm on 9/1/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_AudioGain_h +#define hifi_AudioGain_h + +class AudioGain +{ + float32_t _gain; + bool _mute; + +public: + AudioGain() { + initialize(); + } + + ~AudioGain() { + finalize(); + } + + void initialize() { + setParameters(1.0f,0.0f); + } + + void finalize() { + } + + void reset() { + initialize(); + } + + void setParameters(const float gain, const float mute) { + _gain = std::min(std::max(gain, 0.0f), 1.0f); + _mute = mute != 0.0f; + + } + + void getParameters(float& gain, float& mute) { + gain = _gain; + mute = _mute ? 1.0f : 0.0f; + } + + void render(AudioBufferFloat32& frameBuffer) { + if (_mute) { + frameBuffer.zeroFrames(); + return; + } + + float32_t** samples = frameBuffer.getFrameData(); + for (uint16_t j = 0; j < frameBuffer.getChannelCount(); ++j) { + for (uint16_t i = 0; i < frameBuffer.getFrameCount(); i += 8) { + samples[j][i + 0] *= _gain; + samples[j][i + 1] *= _gain; + samples[j][i + 2] *= _gain; + samples[j][i + 3] *= _gain; + samples[j][i + 4] *= _gain; + samples[j][i + 5] *= _gain; + samples[j][i + 6] *= _gain; + samples[j][i + 7] *= _gain; + } + } + } +}; + +#endif // AudioGain_h + + diff --git a/libraries/audio/src/AudioSourceNoise.cpp b/libraries/audio/src/AudioSourceNoise.cpp new file mode 100644 index 0000000000..6cfdf82fd9 --- /dev/null +++ b/libraries/audio/src/AudioSourceNoise.cpp @@ -0,0 +1,21 @@ +// +// AudioSourceNoise.cpp +// hifi +// +// Created by Craig Hansen-Sturm on 8/10/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include +#include "AudioRingBuffer.h" +#include "AudioFormat.h" +#include "AudioBuffer.h" +#include "AudioSourceNoise.h" + +template<> +uint32_t AudioSourcePinkNoise::_randomSeed = 1974; // a truly random number diff --git a/libraries/audio/src/AudioSourceNoise.h b/libraries/audio/src/AudioSourceNoise.h new file mode 100644 index 0000000000..26cb21a065 --- /dev/null +++ b/libraries/audio/src/AudioSourceNoise.h @@ -0,0 +1,103 @@ +// +// AudioSourceNoise.h +// hifi +// +// Created by Craig Hansen-Sturm on 9/1/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +// Adapted from code by Phil Burk http://www.firstpr.com.au/dsp/pink-noise/ +// + +#ifndef hifi_AudioSourceNoise_h +#define hifi_AudioSourceNoise_h + +template< const uint16_t N = 30> +class AudioSourceNoise +{ + static const uint16_t _randomRows = N; + static const uint16_t _randomBits = 24; + static const uint16_t _randomShift = (sizeof(int32_t) * 8) - _randomBits; + + static uint32_t _randomSeed; + + int32_t _rows[_randomRows]; + int32_t _runningSum; // used to optimize summing of generators. + uint16_t _index; // incremented each sample. + uint16_t _indexMask; // index wrapped by ANDing with this mask. + float32_t _scale; // used to scale within range of -1.0 to +1.0 + + static uint32_t generateRandomNumber() { + _randomSeed = (_randomSeed * 196314165) + 907633515; + return _randomSeed >> _randomShift; + } + +public: + AudioSourceNoise() { + initialize(); + } + + ~AudioSourceNoise() { + finalize(); + } + + void initialize() { + memset(_rows, 0, _randomRows * sizeof(int32_t)); + + _runningSum = 0; + _index = 0; + _indexMask = (1 << _randomRows) - 1; + _scale = 1.0f / ((_randomRows + 1) * (1 << (_randomBits - 1))); + } + + void finalize() { + } + + void reset() { + initialize(); + } + + void setParameters(void) { + } + + void getParameters(void) { + } + + void render(AudioBufferFloat32& frameBuffer) { + + uint32_t randomNumber; + + float32_t** samples = frameBuffer.getFrameData(); + for (uint16_t i = 0; i < frameBuffer.getFrameCount(); ++i) { + for (uint16_t j = 0; j < frameBuffer.getChannelCount(); ++j) { + + _index = (_index + 1) & _indexMask; // increment and mask index. + if (_index != 0) { // if index is zero, don't update any random values. + + uint32_t numZeros = 0; // determine how many trailing zeros in _index + uint32_t tmp = _index; + while ((tmp & 1) == 0) { + tmp >>= 1; + numZeros++; + } + // replace the indexed _rows random value. subtract and add back to _runningSum instead + // of adding all the random values together. only one value changes each time. + _runningSum -= _rows[numZeros]; + randomNumber = generateRandomNumber(); + _runningSum += randomNumber; + _rows[numZeros] = randomNumber; + } + + // add extra white noise value and scale between -1.0 and +1.0 + samples[j][i] = (_runningSum + generateRandomNumber()) * _scale; + } + } + } +}; + +typedef AudioSourceNoise<> AudioSourcePinkNoise; + +#endif // AudioSourceNoise_h + diff --git a/libraries/audio/src/AudioSourceTone.cpp b/libraries/audio/src/AudioSourceTone.cpp new file mode 100644 index 0000000000..80a6aed48e --- /dev/null +++ b/libraries/audio/src/AudioSourceTone.cpp @@ -0,0 +1,20 @@ +// +// AudioSourceTone.cpp +// hifi +// +// Created by Craig Hansen-Sturm on 8/10/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include +#include "AudioRingBuffer.h" +#include "AudioFormat.h" +#include "AudioBuffer.h" +#include "AudioSourceTone.h" + +uint32_t AudioSourceTone::_frameOffset = 0; diff --git a/libraries/audio/src/AudioSourceTone.h b/libraries/audio/src/AudioSourceTone.h new file mode 100644 index 0000000000..5ebe1ba2a9 --- /dev/null +++ b/libraries/audio/src/AudioSourceTone.h @@ -0,0 +1,72 @@ +// +// AudioSourceTone.h +// hifi +// +// Created by Craig Hansen-Sturm on 9/1/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_AudioSourceTone_h +#define hifi_AudioSourceTone_h + +class AudioSourceTone +{ + static uint32_t _frameOffset; + float32_t _frequency; + float32_t _amplitude; + float32_t _sampleRate; + float32_t _omega; + +public: + AudioSourceTone() { + initialize(); + } + + ~AudioSourceTone() { + finalize(); + } + + void initialize() { + _frameOffset = 0; + setParameters(SAMPLE_RATE, 220.0f, 0.9f); + } + + void finalize() { + } + + void reset() { + _frameOffset = 0; + } + + void setParameters(const float32_t sampleRate, const float32_t frequency, const float32_t amplitude) { + _sampleRate = std::max(sampleRate, 1.0f); + _frequency = std::max(frequency, 1.0f); + _amplitude = std::max(amplitude, 1.0f); + _omega = _frequency / _sampleRate * TWO_PI; + } + + void getParameters(float32_t& sampleRate, float32_t& frequency, float32_t& amplitude) { + sampleRate = _sampleRate; + frequency = _frequency; + amplitude = _amplitude; + } + + void render(AudioBufferFloat32& frameBuffer) { + + // note: this is a placeholder implementation. final version will not include any transcendental ops in our render loop + + float32_t** samples = frameBuffer.getFrameData(); + for (uint16_t i = 0; i < frameBuffer.getFrameCount(); ++i) { + for (uint16_t j = 0; j < frameBuffer.getChannelCount(); ++j) { + samples[j][i] = sinf((i + _frameOffset) * _omega); + } + } + _frameOffset += frameBuffer.getFrameCount(); + } +}; + +#endif + diff --git a/libraries/audio/src/PositionalAudioStream.h b/libraries/audio/src/PositionalAudioStream.h index a7f59aebde..f5f1b9027a 100644 --- a/libraries/audio/src/PositionalAudioStream.h +++ b/libraries/audio/src/PositionalAudioStream.h @@ -16,6 +16,8 @@ #include #include "InboundAudioStream.h" +#include "AudioFormat.h" +#include "AudioBuffer.h" #include "AudioFilter.h" #include "AudioFilterBank.h"