From 05b76c07130e1923580cd3e1784bb4c7a583a5ea Mon Sep 17 00:00:00 2001 From: Igor Zinken Date: Sun, 24 Jul 2022 13:00:04 +0200 Subject: [PATCH] Implemented Gain processor to adjust signal volume without colouring --- mwengine/CMakeLists.txt | 1 + mwengine/src/main/cpp/mwengine.i | 2 + mwengine/src/main/cpp/processors/gain.cpp | 91 +++++++++++++++++++ mwengine/src/main/cpp/processors/gain.h | 58 ++++++++++++ mwengine/src/main/cpp/tests/main.cpp | 1 + .../main/cpp/tests/processors/gain_test.cpp | 51 +++++++++++ 6 files changed, 204 insertions(+) create mode 100644 mwengine/src/main/cpp/processors/gain.cpp create mode 100644 mwengine/src/main/cpp/processors/gain.h create mode 100644 mwengine/src/main/cpp/tests/processors/gain_test.cpp diff --git a/mwengine/CMakeLists.txt b/mwengine/CMakeLists.txt index 55ba61ba..a36c24dd 100644 --- a/mwengine/CMakeLists.txt +++ b/mwengine/CMakeLists.txt @@ -117,6 +117,7 @@ set(MWENGINE_PROCESSORS ${CPP_SRC}/processors/bitcrusher.cpp ${CPP_SRC}/processors/flanger.cpp ${CPP_SRC}/processors/fm.cpp ${CPP_SRC}/processors/formantfilter.cpp + ${CPP_SRC}/processors/gain.cpp ${CPP_SRC}/processors/glitcher.cpp ${CPP_SRC}/processors/limiter.cpp ${CPP_SRC}/processors/lowpassfilter.cpp diff --git a/mwengine/src/main/cpp/mwengine.i b/mwengine/src/main/cpp/mwengine.i index 70f791f1..03a97b67 100755 --- a/mwengine/src/main/cpp/mwengine.i +++ b/mwengine/src/main/cpp/mwengine.i @@ -33,6 +33,7 @@ using namespace MWEngine; #include "processors/limiter.h" #include "processors/fm.h" #include "processors/formantfilter.h" +#include "processors/gain.h" #include "processors/glitcher.h" #include "processors/lowpassfilter.h" #include "processors/lpfhpfilter.h" @@ -115,6 +116,7 @@ typedef double SAMPLE_TYPE; %include "processors/lpfhpfilter.h" %include "processors/fm.h" %include "processors/formantfilter.h" +%include "processors/gain.h" %include "processors/glitcher.h" %include "processors/phaser.h" %include "processors/pitchshifter.h" diff --git a/mwengine/src/main/cpp/processors/gain.cpp b/mwengine/src/main/cpp/processors/gain.cpp new file mode 100644 index 00000000..c46251e5 --- /dev/null +++ b/mwengine/src/main/cpp/processors/gain.cpp @@ -0,0 +1,91 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2022 Igor Zinken - http://www.igorski.nl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "gain.h" +#include +#include + +namespace MWEngine { + +/* constructor / destructor */ + +Gain::Gain() +{ + setAmount( MAX_VOLUME ); +} + +Gain::Gain( float amount ) +{ + setAmount( amount ); +} + +Gain::~Gain() +{ + // nowt... +} + +/* public methods */ + +void Gain::process( AudioBuffer* sampleBuffer, bool isMonoSource ) +{ + // no gain specified ? do nothing + if ( _amount == MAX_VOLUME ) + return; + + int bufferSize = sampleBuffer->bufferSize; + + for ( int i = 0, l = sampleBuffer->amountOfChannels; i < l; ++i ) + { + SAMPLE_TYPE* channelBuffer = sampleBuffer->getBufferForChannel( i ); + + for ( int j = 0; j < bufferSize; ++j ) + { + channelBuffer[ j ] = capSampleSafe( channelBuffer[ j ] * _amount ); + } + + // omit unnecessary cycles by copying the mono content + if ( isMonoSource ) + { + sampleBuffer->applyMonoSource(); + break; + } + } +} + +bool Gain::isCacheable() +{ + return true; +} + +/* getters / setters */ + +float Gain::getAmount() +{ + return ( float ) _amount; +} + +void Gain::setAmount( float value ) +{ + _amount = std::max( MIN_GAIN, std::min( MAX_GAIN, ( SAMPLE_TYPE ) value )); +} + +} // E.O namespace MWEngine diff --git a/mwengine/src/main/cpp/processors/gain.h b/mwengine/src/main/cpp/processors/gain.h new file mode 100644 index 00000000..eb51f032 --- /dev/null +++ b/mwengine/src/main/cpp/processors/gain.h @@ -0,0 +1,58 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2022 Igor Zinken - https://www.igorski.nl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef __MWENGINE__GAIN_H_INCLUDED__ +#define __MWENGINE__GAIN_H_INCLUDED__ + +#include "baseprocessor.h" +#include + +namespace MWEngine { +class Gain : public BaseProcessor +{ + public: + static constexpr SAMPLE_TYPE MIN_GAIN = SILENCE; + static constexpr SAMPLE_TYPE MAX_GAIN = 20; + + Gain(); + Gain( float amount ); + ~Gain(); + + std::string getType() { + return std::string( "Gain" ); + } + + float getAmount(); + void setAmount( float value ); + +#ifndef SWIG + // internal to the engine + void process( AudioBuffer* sampleBuffer, bool isMonoSource ); + bool isCacheable(); +#endif + + private: + SAMPLE_TYPE _amount; +}; +} // E.O namespace MWEngine + +#endif diff --git a/mwengine/src/main/cpp/tests/main.cpp b/mwengine/src/main/cpp/tests/main.cpp index 401b2db2..8a4ec686 100644 --- a/mwengine/src/main/cpp/tests/main.cpp +++ b/mwengine/src/main/cpp/tests/main.cpp @@ -60,6 +60,7 @@ using namespace MWEngine; #include "processors/flanger_test.cpp" #include "processors/fm_test.cpp" #include "processors/formantfilter_test.cpp" +#include "processors/gain_test.cpp" #include "processors/glitcher_test.cpp" #include "processors/limiter_test.cpp" #include "processors/lowpassfilter_test.cpp" diff --git a/mwengine/src/main/cpp/tests/processors/gain_test.cpp b/mwengine/src/main/cpp/tests/processors/gain_test.cpp new file mode 100644 index 00000000..de7c768f --- /dev/null +++ b/mwengine/src/main/cpp/tests/processors/gain_test.cpp @@ -0,0 +1,51 @@ +#include +#include + +TEST( Gain, Constructors ) +{ + auto processor = new Gain(); + + EXPECT_EQ( MAX_VOLUME, processor->getAmount() ) + << "expected default gain to equal the maximum volume"; + + delete processor; + + processor = new Gain( 0.5f ); + + EXPECT_EQ( 0.5f, processor->getAmount() ) + << "expected default gain to equal the amount provided to the constructor"; + + delete processor; +} + +TEST( Gain, getType ) +{ + auto processor = new Gain(); + + std::string expectedType( "Gain" ); + ASSERT_TRUE( 0 == expectedType.compare( processor->getType() )); + + delete processor; +} + +TEST( Gain, setAmount ) +{ + auto processor = new Gain(); + + processor->setAmount( 2.f ); + + EXPECT_EQ( 2.f, processor->getAmount() ) + << "expected gain amount to equal the value passed to the setter"; + + processor->setAmount( Gain::MIN_GAIN - 1 ); + + EXPECT_EQ( Gain::MIN_GAIN, processor->getAmount() ) + << "expected out of range gain amount to have been capped to the minimum supported value"; + + processor->setAmount( Gain::MAX_GAIN + 1 ); + + EXPECT_EQ( Gain::MAX_GAIN, processor->getAmount() ) + << "expected out of range gain amount to have been capped to the maximum supported value"; + + delete processor; +}