├── .github ├── CODEOWNERS └── workflows │ └── tests.yml ├── .gitignore ├── LICENSE ├── Package.swift ├── README.md ├── Sources ├── CDevoloopAudioKit │ ├── CDelay.cpp │ ├── CombFilter.cpp │ ├── Compressor.cpp │ ├── DelayAPF.cpp │ ├── DynaRageCompressorDSP.mm │ ├── Equalisator.cpp │ ├── LPFCombFilter.cpp │ ├── MikeFilter.cpp │ ├── OnePoleLPF.cpp │ ├── RageProcessor.cpp │ ├── RhinoGuitarProcessorDSP.mm │ ├── include │ │ ├── Array.h │ │ ├── CDelay.h │ │ ├── CombFilter.h │ │ ├── Compressor.h │ │ ├── DelayAPF.h │ │ ├── DynArray.h │ │ ├── Equalisator.h │ │ ├── FFTReal │ │ │ ├── FFTReal.h │ │ │ ├── FFTRealFixLen.h │ │ │ ├── FFTRealFixLenParam.h │ │ │ ├── FFTRealPassDirect.h │ │ │ ├── FFTRealPassInverse.h │ │ │ ├── FFTRealSelect.h │ │ │ └── FFTRealUseTrigo.h │ │ ├── LPFCombFilter.h │ │ ├── MikeFilter.h │ │ ├── OnePoleLPF.h │ │ ├── OscSinCos.h │ │ ├── RageProcessor.h │ │ ├── def.h │ │ └── pluginconstants.h │ └── pluginobjects.cpp └── DevoloopAudioKit │ ├── DevoloopAudioKit.swift │ ├── DynaRageCompressor.swift │ └── RhinoGuitarProcessor.swift ├── Tests └── DevoloopAudioKitTests │ ├── GenericNodeTests.swift │ └── TestResources │ └── 12345.wav └── images └── dynarage.jpg /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # SoundpipeAudioKit Code Owners File 2 | # https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners 3 | 4 | # Primary Owners 5 | * @aure @wtholliday -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [main] 7 | pull_request: 8 | branches: [main] 9 | 10 | jobs: 11 | swift_test: 12 | name: Test 13 | uses: AudioKit/ci/.github/workflows/swift_test.yml@main 14 | with: 15 | scheme: DevoloopAudioKit 16 | platforms: iOS macOS tvOS 17 | swift-versions: 5.5 5.6 18 | 19 | # Send notification to Discord on failure. 20 | send_notification: 21 | name: Send Notification 22 | uses: AudioKit/ci/.github/workflows/send_notification.yml@main 23 | needs: [swift_test] 24 | if: ${{ failure() && github.ref == 'refs/heads/main' }} 25 | secrets: inherit 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | DerivedData/ 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | 9 | Package.resolved -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 AudioKit 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.5 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "DevoloopAudioKit", 8 | platforms: [.macOS(.v12), .iOS(.v13), .tvOS(.v13)], 9 | products: [.library(name: "DevoloopAudioKit", targets: ["DevoloopAudioKit"])], 10 | dependencies: [ 11 | .package(url: "https://github.com/AudioKit/AudioKit", from: "5.5.0"), 12 | .package(url: "https://github.com/AudioKit/AudioKitEX", from: "5.5.0"), 13 | ], 14 | targets: [ 15 | .target(name: "DevoloopAudioKit", dependencies: ["AudioKit", "AudioKitEX", "CDevoloopAudioKit"]), 16 | .target(name: "CDevoloopAudioKit", dependencies: ["AudioKit", "AudioKitEX"]), 17 | .testTarget(name: "DevoloopAudioKitTests", dependencies: ["DevoloopAudioKit"], resources: [.copy("TestResources/")]), 18 | ], 19 | cxxLanguageStandard: .cxx14 20 | ) 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | # Devoloop AudioKit (Guitar Processors) 5 | 6 | [![Build Status](https://github.com/AudioKit/DevoloopAudioKit/workflows/CI/badge.svg)](https://github.com/AudioKit/DevoloopAudioKit/actions?query=workflow%3ACI) 7 | [![License](https://img.shields.io/github/license/AudioKit/DevoloopAudioKit)](https://github.com/AudioKit/DevoloopAudioKit/blob/main/LICENSE) 8 | [![Swift Compatibility](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FAudioKit%2FDevoloopAudioKit%2Fbadge%3Ftype%3Dswift-versions&label=)](https://swiftpackageindex.com/AudioKit/DevoloopAudioKit) 9 | [![Platform Compatibility](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FAudioKit%2FDevoloopAudioKit%2Fbadge%3Ftype%3Dplatforms&label=)](https://swiftpackageindex.com/AudioKit/DevoloopAudioKit) 10 | [![Reviewed by Hound](https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg)](https://houndci.com) 11 | [![Twitter Follow](https://img.shields.io/twitter/follow/AudioKitPro.svg?style=social)](https://twitter.com/AudioKitPro) 12 | 13 |
14 | 15 | While all of AudioKit's effects and filters can be useful for processing guitar, 16 | this package contains AudioKit nodes that are distinctly guitaristic in their intent. 17 | 18 | * [DynaRage Tube Compressor](https://www.audiokit.io/DevoloopAudioKit/documentation/devoloopaudiokit/dynaragecompressor) - based on DynaRage Tube Compressor RE for Reason by Mike Gazzarusso. 19 | * [Rhino Guitar Processor](https://www.audiokit.io/DevoloopAudioKit/documentation/devoloopaudiokit/rhinoguitarprocessor) - Guitar amplifier head and cabinet simulator by Mike Gazzaruso. 20 | 21 | ## Installation 22 | 23 | Install using the Swift Package Manager. 24 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/CDelay.cpp: -------------------------------------------------------------------------------- 1 | #include "CDelay.h" 2 | #include "pluginconstants.h" 3 | 4 | CDelay::CDelay() { 5 | m_pBuffer = NULL; 6 | 7 | m_fOutputAttenuation_dB = 0; 8 | m_fDelay_ms = 0.0; 9 | 10 | m_fOutputAttenuation = 0.0; 11 | m_fDelayInSamples = 0.0; 12 | m_nSampleRate = 0; 13 | 14 | resetDelay(); 15 | } 16 | 17 | CDelay::~CDelay() { 18 | if (m_pBuffer) 19 | delete m_pBuffer; 20 | 21 | m_pBuffer = NULL; 22 | } 23 | 24 | void CDelay::init(int nDelayLength) { 25 | m_nBufferSize = nDelayLength; 26 | 27 | m_pBuffer = new float[m_nBufferSize]; 28 | 29 | // flush buffer 30 | memset(m_pBuffer, 0, (unsigned)m_nBufferSize * sizeof(float)); 31 | } 32 | 33 | void CDelay::resetDelay() { 34 | // flush buffer 35 | if (m_pBuffer) 36 | memset(m_pBuffer, 0, (unsigned)m_nBufferSize * sizeof(float)); 37 | 38 | // init read/write indices 39 | m_nWriteIndex = 0; // reset the Write index to top 40 | m_nReadIndex = 0; // reset the Write index to top 41 | 42 | cookVariables(); 43 | } 44 | 45 | void CDelay::setDelay_mSec(float fmSec) { 46 | m_fDelay_ms = fmSec; 47 | cookVariables(); 48 | } 49 | 50 | void CDelay::setOutputAttenuation_dB(float fAttendB) { 51 | m_fOutputAttenuation_dB = fAttendB; 52 | cookVariables(); 53 | } 54 | 55 | void CDelay::cookVariables() { 56 | m_fOutputAttenuation = 57 | powf((float)10.0, (float)m_fOutputAttenuation_dB / (float)20.0); 58 | 59 | m_fDelayInSamples = m_fDelay_ms * (44100.0f / 1000.0f); 60 | 61 | // subtract to make read index 62 | m_nReadIndex = m_nWriteIndex - (int)m_fDelayInSamples; 63 | 64 | // the check and wrap BACKWARDS if the index is negative 65 | if (m_nReadIndex < 0) 66 | m_nReadIndex += m_nBufferSize; // amount of wrap is Read + Length 67 | } 68 | 69 | void CDelay::writeDelayAndInc(float fDelayInput) { 70 | // write to the delay line 71 | m_pBuffer[m_nWriteIndex] = fDelayInput; // external feedback sample 72 | 73 | // incremnent the pointers and wrap if necessary 74 | m_nWriteIndex++; 75 | if (m_nWriteIndex >= m_nBufferSize) 76 | m_nWriteIndex = 0; 77 | 78 | m_nReadIndex++; 79 | if (m_nReadIndex >= m_nBufferSize) 80 | m_nReadIndex = 0; 81 | } 82 | 83 | float CDelay::readDelay() { 84 | // Read the output of the delay at m_nReadIndex 85 | float yn = m_pBuffer[m_nReadIndex]; 86 | 87 | // Read the location ONE BEHIND yn at y(n-1) 88 | int nReadIndex_1 = m_nReadIndex - 1; 89 | if (nReadIndex_1 < 0) 90 | nReadIndex_1 = m_nBufferSize - 1; // m_nBufferSize-1 is last location 91 | 92 | // get y(n-1) 93 | float yn_1 = m_pBuffer[nReadIndex_1]; 94 | 95 | // interpolate: (0, yn) and (1, yn_1) by the amount fracDelay 96 | float fFracDelay = m_fDelayInSamples - (int)m_fDelayInSamples; 97 | 98 | return dLinTerp(0, 1, yn, yn_1, fFracDelay); // interp frac between them 99 | } 100 | 101 | float CDelay::readDelayAt(float fmSec) { 102 | float fDelayInSamples = fmSec * ((float)m_nSampleRate) / 1000.0f; 103 | 104 | // subtract to make read index 105 | int nReadIndex = m_nWriteIndex - (int)fDelayInSamples; 106 | 107 | // Read the output of the delay at m_nReadIndex 108 | float yn = m_pBuffer[nReadIndex]; 109 | 110 | // Read the location ONE BEHIND yn at y(n-1) 111 | int nReadIndex_1 = nReadIndex - 1; 112 | if (nReadIndex_1 < 0) 113 | nReadIndex_1 = m_nBufferSize - 1; // m_nBufferSize-1 is last location 114 | 115 | // get y(n-1) 116 | float yn_1 = m_pBuffer[nReadIndex_1]; 117 | 118 | // interpolate: (0, yn) and (1, yn_1) by the amount fracDelay 119 | float fFracDelay = fDelayInSamples - (int)fDelayInSamples; 120 | 121 | return dLinTerp(0, 1, yn, yn_1, fFracDelay); // interp frac between them 122 | } 123 | 124 | bool CDelay::processAudio(float *pInput, float *pOutput) { 125 | // Read the Input 126 | float xn = *pInput; 127 | 128 | // read delayed output 129 | float yn = m_fDelayInSamples == 0 ? xn : readDelay(); 130 | 131 | // write to the delay line 132 | writeDelayAndInc(xn); 133 | 134 | // output attenuation 135 | *pOutput = m_fOutputAttenuation * yn; 136 | 137 | return true; // all OK 138 | } 139 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/CombFilter.cpp: -------------------------------------------------------------------------------- 1 | #include "CombFilter.h" 2 | 3 | CCombFilter::CCombFilter(void) : CDelay() { m_fComb_g = 0; } 4 | 5 | CCombFilter::~CCombFilter() {} 6 | 7 | bool CCombFilter::processAudio(float *pInput, float *pOutput) { 8 | // read the delay line to get w(n-D); call base class 9 | float yn = this->readDelay(); 10 | 11 | if (m_nReadIndex == m_nWriteIndex) 12 | yn = 0; 13 | 14 | // form fb = x(n) + m_fComb_gy(n) 15 | float fb = *pInput + m_fComb_g * yn; 16 | 17 | // write delay line 18 | this->writeDelayAndInc(fb); 19 | 20 | // write the output sample (could be combined with above line) 21 | if (m_nReadIndex == m_nWriteIndex) 22 | yn = *pInput; 23 | 24 | *pOutput = yn; 25 | 26 | // all OK 27 | return true; 28 | } 29 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/Compressor.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Compressor.cpp 3 | // SilenceDetectionEffect 4 | // 5 | // Created by Mike Gazzaruso, revision history on Github. 6 | // 7 | // 8 | 9 | #include "Compressor.h" 10 | 11 | Compressor::Compressor(float fThreshold, float fRatio, float fAttack, 12 | float fRelease, int iSampleRate) 13 | : envDetector((double)iSampleRate), compGain(0.0f) { 14 | theThreshold = fThreshold; 15 | theRatio = fRatio; 16 | 17 | envDetector.init((float)iSampleRate, fAttack, fRelease, false, 18 | DETECT_MODE_RMS, true); 19 | 20 | delayLookAhead.init(1 * iSampleRate); 21 | 22 | // set the current value 23 | delayLookAhead.setDelay_mSec(0.0f); 24 | 25 | // flush delays 26 | delayLookAhead.resetDelay(); 27 | } 28 | 29 | void Compressor::setParameters(float fThreshold, float fRatio, float fAttack, 30 | float fRelease) { 31 | this->theThreshold = fThreshold; 32 | this->theRatio = fRatio; 33 | this->envDetector.setAttackDuration(fAttack); 34 | this->envDetector.setReleaseDuration(fRelease); 35 | } 36 | 37 | float Compressor::Process(float fInputSignal, bool bLimitOn, 38 | float fSensitivity) { 39 | float fOutputSignal; 40 | 41 | float fDetector = envDetector.detect(fInputSignal * fSensitivity); 42 | 43 | float fGn = 1.0; 44 | 45 | fGn = calcCompressorGain(fDetector, theThreshold, theRatio, 1.0f, bLimitOn); 46 | this->compGain = fGn; 47 | 48 | float fLookAheadOut = 0.0f; 49 | delayLookAhead.processAudio(&fInputSignal, &fLookAheadOut); 50 | 51 | fOutputSignal = fGn * fLookAheadOut; 52 | 53 | return fOutputSignal; 54 | } 55 | 56 | float Compressor::calcCompressorGain(float fDetectorValue, float fTheThreshold, 57 | float fTheRatio, float fKneeWidth, 58 | bool bLimit) { 59 | // slope variable 60 | float CS = 1.0f - 1.0f / fTheRatio; // [Eq. 13.1] 61 | 62 | // limiting is infinite ratio thus CS->1.0 63 | if (bLimit) 64 | CS = 1; 65 | 66 | // soft-knee with detection value in range? 67 | if (fKneeWidth > 0 && fDetectorValue > (fTheThreshold - fKneeWidth / 2.0) && 68 | fDetectorValue < fTheThreshold + fKneeWidth / 2.0) { 69 | // setup for Lagrange 70 | double x[2]; 71 | double y[2]; 72 | x[0] = fTheThreshold - fKneeWidth / 2.0; 73 | x[1] = fTheThreshold + fKneeWidth / 2.0; 74 | x[1] = min(0, x[1]); // top limit is 0dBFS 75 | y[0] = 0; // CS = 0 for 1:1 ratio 76 | y[1] = CS; // current CS 77 | 78 | // interpolate & overwrite CS 79 | CS = (float)lagrpol(&x[0], &y[0], 2, fDetectorValue); 80 | } 81 | 82 | // compute gain; threshold and detection values are in dB 83 | float yG = CS * (fTheThreshold - fDetectorValue); // [Eq. 13.1] 84 | 85 | // clamp; this allows ratios of 1:1 to still operate 86 | yG = min(0, yG); 87 | 88 | // convert back to linear 89 | return powf(10.0f, yG / 20.0f); 90 | } 91 | 92 | float Compressor::getCompGain() { return this->compGain; } 93 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/DelayAPF.cpp: -------------------------------------------------------------------------------- 1 | #include "DelayAPF.h" 2 | 3 | CDelayAPF::CDelayAPF(void) : CDelay() { m_fAPF_g = 0; } 4 | 5 | CDelayAPF::~CDelayAPF() {} 6 | 7 | bool CDelayAPF::processAudio(float *pInput, float *pOutput) { 8 | // read the delay line to get w(n-D); call base class 9 | float fw_n_D = this->readDelay(); 10 | 11 | // form w(n) = x(n) + gw(n-D) 12 | float fw_n = *pInput + m_fAPF_g * fw_n_D; 13 | 14 | // form y(n) = -gw(n) + w(n-D) 15 | float fy_n = -m_fAPF_g * fw_n + fw_n_D; 16 | 17 | // write delay line 18 | this->writeDelayAndInc(fw_n); 19 | 20 | // write the output sample (could be combined with above line) 21 | *pOutput = fy_n; 22 | 23 | // all OK 24 | return true; 25 | } 26 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/DynaRageCompressorDSP.mm: -------------------------------------------------------------------------------- 1 | // Copyright AudioKit. All Rights Reserved. 2 | 3 | #include "DSPBase.h" 4 | 5 | #include "Compressor.h" 6 | #include "RageProcessor.h" 7 | #include "ParameterRamper.h" 8 | 9 | enum DynaRageCompressorParameter : AUParameterAddress { 10 | DynaRageCompressorParameterRatio, 11 | DynaRageCompressorParameterThreshold, 12 | DynaRageCompressorParameterAttackDuration, 13 | DynaRageCompressorParameterReleaseDuration, 14 | DynaRageCompressorParameterRage, 15 | DynaRageCompressorParameterRageEnabled 16 | }; 17 | 18 | class DynaRageCompressorDSP : public DSPBase { 19 | private: 20 | std::unique_ptr left_compressor; 21 | std::unique_ptr right_compressor; 22 | 23 | std::unique_ptr left_rageprocessor; 24 | std::unique_ptr right_rageprocessor; 25 | 26 | ParameterRamper ratioRamp; 27 | ParameterRamper thresholdRamp; 28 | ParameterRamper attackDurationRamp; 29 | ParameterRamper releaseDurationRamp; 30 | ParameterRamper rageRamp; 31 | 32 | bool rageIsOn = true; 33 | 34 | public: 35 | DynaRageCompressorDSP() { 36 | parameters[DynaRageCompressorParameterRatio] = &ratioRamp; 37 | parameters[DynaRageCompressorParameterThreshold] = &thresholdRamp; 38 | parameters[DynaRageCompressorParameterAttackDuration] = &attackDurationRamp; 39 | parameters[DynaRageCompressorParameterReleaseDuration] = &releaseDurationRamp; 40 | parameters[DynaRageCompressorParameterRage] = &rageRamp; 41 | } 42 | 43 | void init(int channelCount, double sampleRate) override { 44 | DSPBase::init(channelCount, sampleRate); 45 | 46 | float ratio = ratioRamp.get(); 47 | float threshold = thresholdRamp.get(); 48 | float attackDuration = attackDurationRamp.get(); 49 | float releaseDuration = releaseDurationRamp.get(); 50 | 51 | left_compressor = std::make_unique(threshold, ratio, attackDuration, releaseDuration, (int)sampleRate); 52 | right_compressor = std::make_unique(threshold, ratio, attackDuration, releaseDuration, (int)sampleRate); 53 | 54 | left_rageprocessor = std::make_unique((int)sampleRate); 55 | right_rageprocessor = std::make_unique((int)sampleRate); 56 | } 57 | 58 | void deinit() override { 59 | left_compressor.reset(); 60 | right_compressor.reset(); 61 | left_rageprocessor.reset(); 62 | right_rageprocessor.reset(); 63 | } 64 | 65 | void reset() override { 66 | float ratio = ratioRamp.get(); 67 | float threshold = thresholdRamp.get(); 68 | float attackDuration = attackDurationRamp.get(); 69 | float releaseDuration = releaseDurationRamp.get(); 70 | 71 | left_compressor = std::make_unique(threshold, ratio, attackDuration, releaseDuration, (int)sampleRate); 72 | right_compressor = std::make_unique(threshold, ratio, attackDuration, releaseDuration, (int)sampleRate); 73 | } 74 | 75 | void setParameter(AUParameterAddress address, float value, bool immediate) override { 76 | if (address == DynaRageCompressorParameterRageEnabled) { 77 | rageIsOn = value > 0.5f; 78 | } 79 | else { 80 | DSPBase::setParameter(address, value, immediate); 81 | } 82 | } 83 | 84 | float getParameter(AUParameterAddress address) override { 85 | if (address == DynaRageCompressorParameterRageEnabled) { 86 | return rageIsOn ? 1.f : 0.f; 87 | } 88 | else { 89 | return DSPBase::getParameter(address); 90 | } 91 | } 92 | 93 | void process(FrameRange range) override { 94 | for (int i : range) { 95 | 96 | float ratio = ratioRamp.getAndStep(); 97 | float threshold = thresholdRamp.getAndStep(); 98 | float attackDuration = attackDurationRamp.getAndStep(); 99 | float releaseDuration = releaseDurationRamp.getAndStep(); 100 | float rage = rageRamp.getAndStep(); 101 | 102 | left_compressor->setParameters(threshold, ratio, attackDuration, releaseDuration); 103 | right_compressor->setParameters(threshold, ratio, attackDuration, releaseDuration); 104 | 105 | float leftRageSignal = left_rageprocessor->doRage(inputSample(0, i), rage, rage); 106 | outputSample(0, i) = left_compressor->Process((bool)rageIsOn ? leftRageSignal : inputSample(0, i), false, 1); 107 | float rightRageSignal = right_rageprocessor->doRage(inputSample(1, i), rage, rage); 108 | outputSample(1, i) = right_compressor->Process((bool)rageIsOn ? rightRageSignal : inputSample(1, i), false, 1); 109 | } 110 | } 111 | }; 112 | 113 | AK_REGISTER_DSP(DynaRageCompressorDSP, "dyrc") 114 | AK_REGISTER_PARAMETER(DynaRageCompressorParameterRatio) 115 | AK_REGISTER_PARAMETER(DynaRageCompressorParameterThreshold) 116 | AK_REGISTER_PARAMETER(DynaRageCompressorParameterAttackDuration) 117 | AK_REGISTER_PARAMETER(DynaRageCompressorParameterReleaseDuration) 118 | AK_REGISTER_PARAMETER(DynaRageCompressorParameterRage) 119 | AK_REGISTER_PARAMETER(DynaRageCompressorParameterRageEnabled) 120 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/Equalisator.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Equalisator.cpp 3 | // Rhino 4 | // 5 | // Created by Mike Gazzaruso, revision history on Github. 6 | // 7 | // 8 | 9 | #include "Equalisator.h" 10 | #include 11 | 12 | Equalisator::Equalisator() { 13 | // reset filter coeffs 14 | b0a0 = b1a0 = b2a0 = a1a0 = a2a0 = 0.0f; 15 | 16 | // reset in/out history 17 | ou1 = ou2 = in1 = in2 = 0.0f; 18 | } 19 | 20 | float Equalisator::filter(float in0) { 21 | // filter 22 | float const yn = 23 | b0a0 * in0 + b1a0 * in1 + b2a0 * in2 - a1a0 * ou1 - a2a0 * ou2; 24 | 25 | // push in/out buffers 26 | in2 = in1; 27 | in1 = in0; 28 | ou2 = ou1; 29 | ou1 = yn; 30 | 31 | // return output 32 | return yn; 33 | } 34 | 35 | void Equalisator::calc_filter_coeffs(int const type, double const frequency, 36 | double const sample_rate, double const q, 37 | double const db_gain, 38 | bool q_is_bandwidth) { 39 | // temp pi 40 | double const temp_pi = 3.1415926535897932384626433832795; 41 | 42 | // temp coef vars 43 | double alpha; 44 | double a0 = 0; 45 | double a1 = 0; 46 | double a2 = 0; 47 | double b0 = 0; 48 | double b1 = 0; 49 | double b2 = 0; 50 | 51 | // peaking, lowshelf and hishelf 52 | if (type >= 6) { 53 | double const A = pow(10.0, (db_gain / 40.0)); 54 | double const omega = 2.0 * temp_pi * frequency / sample_rate; 55 | double const tsin = sin(omega); 56 | double const tcos = cos(omega); 57 | 58 | if (q_is_bandwidth) 59 | alpha = tsin * sinh(log(2.0) / 2.0 * q * omega / tsin); 60 | else 61 | alpha = tsin / (2.0 * q); 62 | 63 | double const beta = sqrt(A) / q; 64 | 65 | // peaking 66 | if (type == 6) { 67 | b0 = float(1.0 + alpha * A); 68 | b1 = float(-2.0 * tcos); 69 | b2 = float(1.0 - alpha * A); 70 | a0 = float(1.0 + alpha / A); 71 | a1 = float(-2.0 * tcos); 72 | a2 = float(1.0 - alpha / A); 73 | } 74 | 75 | // lowshelf 76 | if (type == 7) { 77 | b0 = float(A * ((A + 1.0) - (A - 1.0) * tcos + beta * tsin)); 78 | b1 = float(2.0 * A * ((A - 1.0) - (A + 1.0) * tcos)); 79 | b2 = float(A * ((A + 1.0) - (A - 1.0) * tcos - beta * tsin)); 80 | a0 = float((A + 1.0) + (A - 1.0) * tcos + beta * tsin); 81 | a1 = float(-2.0 * ((A - 1.0) + (A + 1.0) * tcos)); 82 | a2 = float((A + 1.0) + (A - 1.0) * tcos - beta * tsin); 83 | } 84 | 85 | // hishelf 86 | if (type == 8) { 87 | b0 = float(A * ((A + 1.0) + (A - 1.0) * tcos + beta * tsin)); 88 | b1 = float(-2.0 * A * ((A - 1.0) + (A + 1.0) * tcos)); 89 | b2 = float(A * ((A + 1.0) + (A - 1.0) * tcos - beta * tsin)); 90 | a0 = float((A + 1.0) - (A - 1.0) * tcos + beta * tsin); 91 | a1 = float(2.0 * ((A - 1.0) - (A + 1.0) * tcos)); 92 | a2 = float((A + 1.0) - (A - 1.0) * tcos - beta * tsin); 93 | } 94 | } else { 95 | // other filters 96 | double const omega = 2.0 * temp_pi * frequency / sample_rate; 97 | double const tsin = sin(omega); 98 | double const tcos = cos(omega); 99 | 100 | if (q_is_bandwidth) 101 | alpha = tsin * sinh(log(2.0) / 2.0 * q * omega / tsin); 102 | else 103 | alpha = tsin / (2.0 * q); 104 | 105 | // lowpass 106 | if (type == 0) { 107 | b0 = (1.0 - tcos) / 2.0; 108 | b1 = 1.0 - tcos; 109 | b2 = (1.0 - tcos) / 2.0; 110 | a0 = 1.0 + alpha; 111 | a1 = -2.0 * tcos; 112 | a2 = 1.0 - alpha; 113 | } 114 | 115 | // hipass 116 | if (type == 1) { 117 | b0 = (1.0 + tcos) / 2.0; 118 | b1 = -(1.0 + tcos); 119 | b2 = (1.0 + tcos) / 2.0; 120 | a0 = 1.0 + alpha; 121 | a1 = -2.0 * tcos; 122 | a2 = 1.0 - alpha; 123 | } 124 | 125 | // bandpass csg 126 | if (type == 2) { 127 | b0 = tsin / 2.0; 128 | b1 = 0.0; 129 | b2 = -tsin / 2; 130 | a0 = 1.0 + alpha; 131 | a1 = -2.0 * tcos; 132 | a2 = 1.0 - alpha; 133 | } 134 | 135 | // bandpass czpg 136 | if (type == 3) { 137 | b0 = alpha; 138 | b1 = 0.0; 139 | b2 = -alpha; 140 | a0 = 1.0 + alpha; 141 | a1 = -2.0 * tcos; 142 | a2 = 1.0 - alpha; 143 | } 144 | 145 | // notch 146 | if (type == 4) { 147 | b0 = 1.0; 148 | b1 = -2.0 * tcos; 149 | b2 = 1.0; 150 | a0 = 1.0 + alpha; 151 | a1 = -2.0 * tcos; 152 | a2 = 1.0 - alpha; 153 | } 154 | 155 | // allpass 156 | if (type == 5) { 157 | b0 = 1.0 - alpha; 158 | b1 = -2.0 * tcos; 159 | b2 = 1.0 + alpha; 160 | a0 = 1.0 + alpha; 161 | a1 = -2.0 * tcos; 162 | a2 = 1.0 - alpha; 163 | } 164 | } 165 | 166 | // set filter coeffs 167 | b0a0 = float(b0 / a0); 168 | b1a0 = float(b1 / a0); 169 | b2a0 = float(b2 / a0); 170 | a1a0 = float(a1 / a0); 171 | a2a0 = float(a2 / a0); 172 | } 173 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/LPFCombFilter.cpp: -------------------------------------------------------------------------------- 1 | #include "LPFCombFilter.h" 2 | 3 | CLPFCombFilter::CLPFCombFilter(void) : CDelay() { 4 | m_fComb_g = 0; 5 | m_fLPF_g = 0; 6 | m_fLPF_z1 = 0; 7 | } 8 | 9 | CLPFCombFilter::~CLPFCombFilter() {} 10 | 11 | void CLPFCombFilter::init(int nDelayLength) { 12 | m_fLPF_z1 = 0.0; 13 | 14 | CDelay::init(nDelayLength); 15 | } 16 | 17 | bool CLPFCombFilter::processAudio(float *pInput, float *pOutput) { 18 | // read the delay line to get w(n-D); call base class 19 | float yn = this->readDelay(); 20 | 21 | if (m_nReadIndex == m_nWriteIndex) 22 | yn = 0; 23 | 24 | // read 25 | float yn_LPF = yn + m_fLPF_g * m_fLPF_z1; 26 | 27 | // form fb & write 28 | m_fLPF_z1 = yn_LPF; 29 | 30 | // form fb = x(n) + m_fComb_g*yn_LPF) 31 | float fb = *pInput + m_fComb_g * yn_LPF; 32 | 33 | // write delay line 34 | this->writeDelayAndInc(fb); 35 | 36 | // write the output sample (could be combined with above line) 37 | if (m_nReadIndex == m_nWriteIndex) 38 | yn = *pInput; 39 | 40 | *pOutput = yn; 41 | 42 | // all OK 43 | return true; 44 | } 45 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/MikeFilter.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // MikeFilter.cpp 3 | // 4 | // Created by Mike Gazzaruso, revision history on Github. 5 | // 6 | // 7 | 8 | #include "MikeFilter.h" 9 | #include 10 | 11 | MikeFilter::MikeFilter() { 12 | // reset filter coeffs 13 | b0a0 = b1a0 = b2a0 = a1a0 = a2a0 = 0.0f; 14 | 15 | // reset in/out history 16 | ou1 = ou2 = in1 = in2 = 0.0f; 17 | } 18 | 19 | float MikeFilter::filter(float in0) { 20 | // filter 21 | float const yn = 22 | b0a0 * in0 + b1a0 * in1 + b2a0 * in2 - a1a0 * ou1 - a2a0 * ou2; 23 | 24 | // push in/out buffers 25 | in2 = in1; 26 | in1 = in0; 27 | ou2 = ou1; 28 | ou1 = yn; 29 | 30 | // return output 31 | return yn; 32 | } 33 | 34 | void MikeFilter::calc_filter_coeffs(double const frequency, 35 | double const sample_rate) { 36 | // temp pi 37 | double const temp_pi = 3.1415926535897932384626433832795; 38 | 39 | // temp coef vars 40 | double alpha, a0, a1, a2, b0, b1, b2; 41 | 42 | double const omega = 2.0 * temp_pi * frequency / sample_rate; 43 | double const tsin = sin(omega); 44 | double const tcos = cos(omega); 45 | 46 | alpha = tsin / (2.0 * 0.5); 47 | 48 | b0 = (1.0 - tcos) / 2.0; 49 | b1 = 1.0 - tcos; 50 | b2 = (1.0 - tcos) / 2.0; 51 | a0 = 1.0 + alpha; 52 | a1 = -2.0 * tcos; 53 | a2 = 1.0 - alpha; 54 | 55 | // set filter coeffs 56 | b0a0 = float(b0 / a0); 57 | b1a0 = float(b1 / a0); 58 | b2a0 = float(b2 / a0); 59 | a1a0 = float(a1 / a0); 60 | a2a0 = float(a2 / a0); 61 | } 62 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/OnePoleLPF.cpp: -------------------------------------------------------------------------------- 1 | #include "OnePoleLPF.h" 2 | 3 | COnePoleLPF::COnePoleLPF() { 4 | m_fLPF_g = 0; 5 | m_fLPF_z1 = 0; 6 | } 7 | 8 | COnePoleLPF::~COnePoleLPF() {} 9 | 10 | void COnePoleLPF::init() { m_fLPF_z1 = 0.0; } 11 | 12 | bool COnePoleLPF::processAudio(float *pInput, float *pOutput) { 13 | // read the delay line to get w(n-D); call base class 14 | // read 15 | float yn_LPF = *pInput * (1.0f - m_fLPF_g) + m_fLPF_g * m_fLPF_z1; 16 | // float yn_LPF = *pInput*(m_fLPF_g) + (1.0 - m_fLPF_g)*m_fLPF_z1; 17 | 18 | // this just reverses the slider 19 | // float yn_LPF = *pInput*(m_fLPF_g) + (1.0 - m_fLPF_g)*m_fLPF_z1; 20 | 21 | // form fb & write 22 | m_fLPF_z1 = yn_LPF; 23 | 24 | *pOutput = yn_LPF; 25 | 26 | // all OK 27 | return true; 28 | } 29 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/RageProcessor.cpp: -------------------------------------------------------------------------------- 1 | // Copyright AudioKit. All Rights Reserved. 2 | 3 | #include "RageProcessor.h" 4 | #include 5 | 6 | RageProcessor::RageProcessor(int iSampleRate) 7 | : iSampleRate(iSampleRate), iNumStages(2) { 8 | filterToneZ.calc_filter_coeffs(4400.0, (int)iSampleRate); 9 | } 10 | 11 | void RageProcessor::setNumStages(int theStages) { 12 | this->iNumStages = theStages; 13 | } 14 | 15 | float RageProcessor::doRage(float fCurrentSample, float fKhorne, 16 | float fNurgle) { 17 | //////// 18 | // Tube non-linear Processing 19 | ////// 20 | 21 | float f_xn = fCurrentSample; 22 | 23 | // Cascaded stages 24 | 25 | for (int s = 0; s < iNumStages; s++) { 26 | if (f_xn >= 0) 27 | f_xn = (1.0f / atanf(fKhorne)) * atanf(fKhorne * f_xn); 28 | else 29 | f_xn = (1.0f / atanf(fNurgle)) * atanf(fNurgle * f_xn); 30 | 31 | // Invert every other stage 32 | if (s % 2 == 0) 33 | f_xn *= -1.0f; 34 | } 35 | return filterToneZ.filter(f_xn); 36 | } 37 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/RhinoGuitarProcessorDSP.mm: -------------------------------------------------------------------------------- 1 | // Copyright AudioKit. All Rights Reserved. 2 | 3 | #include "DSPBase.h" 4 | 5 | #include "RageProcessor.h" 6 | #include "Equalisator.h" 7 | #include "ParameterRamper.h" 8 | #include 9 | #include 10 | 11 | enum RhinoGuitarProcessorParameter : AUParameterAddress { 12 | RhinoGuitarProcessorParameterPreGain, 13 | RhinoGuitarProcessorParameterPostGain, 14 | RhinoGuitarProcessorParameterLowGain, 15 | RhinoGuitarProcessorParameterMidGain, 16 | RhinoGuitarProcessorParameterHighGain, 17 | RhinoGuitarProcessorParameterDistortion 18 | }; 19 | 20 | class RhinoGuitarProcessorDSP : public DSPBase { 21 | private: 22 | std::unique_ptr leftRageProcessor; 23 | std::unique_ptr rightRageProcessor; 24 | Equalisator leftEqLo; 25 | Equalisator rightEqLo; 26 | Equalisator leftEqGtr; 27 | Equalisator rightEqGtr; 28 | Equalisator leftEqMi; 29 | Equalisator rightEqMi; 30 | Equalisator leftEqHi; 31 | Equalisator rightEqHi; 32 | MikeFilter mikeFilterL; 33 | MikeFilter mikeFilterR; 34 | 35 | ParameterRamper preGainRamper; 36 | ParameterRamper postGainRamper; 37 | ParameterRamper lowGainRamper; 38 | ParameterRamper midGainRamper; 39 | ParameterRamper highGainRamper; 40 | ParameterRamper distortionRamper; 41 | 42 | public: 43 | RhinoGuitarProcessorDSP() { 44 | parameters[RhinoGuitarProcessorParameterPreGain] = &preGainRamper; 45 | parameters[RhinoGuitarProcessorParameterPostGain] = &postGainRamper; 46 | parameters[RhinoGuitarProcessorParameterLowGain] = &lowGainRamper; 47 | parameters[RhinoGuitarProcessorParameterMidGain] = &midGainRamper; 48 | parameters[RhinoGuitarProcessorParameterHighGain] = &highGainRamper; 49 | parameters[RhinoGuitarProcessorParameterDistortion] = &distortionRamper; 50 | } 51 | 52 | void init(int channelCount, double sampleRate) override { 53 | DSPBase::init(channelCount, sampleRate); 54 | 55 | leftRageProcessor = std::make_unique((int)sampleRate); 56 | rightRageProcessor = std::make_unique((int)sampleRate); 57 | 58 | mikeFilterL.calc_filter_coeffs(2500.f, sampleRate); 59 | mikeFilterR.calc_filter_coeffs(2500.f, sampleRate); 60 | } 61 | 62 | void deinit() override { 63 | DSPBase::deinit(); 64 | 65 | leftRageProcessor = nullptr; 66 | rightRageProcessor = nullptr; 67 | } 68 | 69 | void process(FrameRange range) override { 70 | for (int i : range) { 71 | 72 | float preGain = preGainRamper.getAndStep(); 73 | float postGain = postGainRamper.getAndStep(); 74 | float lowGain = lowGainRamper.getAndStep(); 75 | float midGain = midGainRamper.getAndStep(); 76 | float highGain = highGainRamper.getAndStep(); 77 | float distortion = distortionRamper.getAndStep(); 78 | 79 | leftEqLo.calc_filter_coeffs(7, 120, sampleRate, 0.75, -2 * -lowGain, false); 80 | rightEqLo.calc_filter_coeffs(7, 120, sampleRate, 0.75, -2 * -lowGain, false); 81 | 82 | leftEqMi.calc_filter_coeffs(6, 2450, sampleRate, 1.7, 2.5 * midGain, true); 83 | rightEqMi.calc_filter_coeffs(6, 2450, sampleRate, 1.7, 2.5 * midGain, true); 84 | 85 | leftEqHi.calc_filter_coeffs(8, 6100, sampleRate, 1.6, -15 * -highGain, false); 86 | rightEqHi.calc_filter_coeffs(8, 6100, sampleRate, 1.6, -15 * -highGain, false); 87 | 88 | for (int channel = 0; channel < 2; ++channel) { 89 | inputSample(channel, i) *= preGain; 90 | const float r_Sig = leftRageProcessor->doRage(inputSample(channel, i), distortion * 2, distortion * 2); 91 | const float e_Sig = leftEqLo.filter(leftEqMi.filter(leftEqHi.filter(r_Sig))) * (1 / (distortion*0.8)); 92 | outputSample(channel, i) = e_Sig * postGain; 93 | } 94 | } 95 | } 96 | 97 | }; 98 | 99 | AK_REGISTER_DSP(RhinoGuitarProcessorDSP, "rhgp") 100 | AK_REGISTER_PARAMETER(RhinoGuitarProcessorParameterPreGain) 101 | AK_REGISTER_PARAMETER(RhinoGuitarProcessorParameterPostGain) 102 | AK_REGISTER_PARAMETER(RhinoGuitarProcessorParameterLowGain) 103 | AK_REGISTER_PARAMETER(RhinoGuitarProcessorParameterMidGain) 104 | AK_REGISTER_PARAMETER(RhinoGuitarProcessorParameterHighGain) 105 | AK_REGISTER_PARAMETER(RhinoGuitarProcessorParameterDistortion) 106 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/include/Array.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | Array.h 4 | By Laurent de Soras 5 | 6 | --- Legal stuff --- 7 | 8 | This program is free software. It comes without any warranty, to 9 | the extent permitted by applicable law. You can redistribute it 10 | and/or modify it under the terms of the Do What The Fuck You Want 11 | To Public License, Version 2, as published by Sam Hocevar. See 12 | http://sam.zoy.org/wtfpl/COPYING for more details. 13 | 14 | *Tab=3***********************************************************************/ 15 | 16 | #if defined(ffft_Array_CURRENT_CODEHEADER) 17 | #error Recursive inclusion of Array code header. 18 | #endif 19 | #define ffft_Array_CURRENT_CODEHEADER 20 | 21 | #if !defined(ffft_Array_CODEHEADER_INCLUDED) 22 | #define ffft_Array_CODEHEADER_INCLUDED 23 | 24 | #include 25 | 26 | namespace ffft { 27 | 28 | template Array::Array() { 29 | 30 | } 31 | 32 | template 33 | const typename Array::DataType &Array:: 34 | operator[](long pos) const { 35 | assert(pos >= 0); 36 | assert(pos < LEN); 37 | 38 | return (_data_arr[pos]); 39 | } 40 | 41 | template 42 | typename Array::DataType &Array::operator[](long pos) { 43 | assert(pos >= 0); 44 | assert(pos < LEN); 45 | 46 | return (_data_arr[pos]); 47 | } 48 | 49 | template long Array::size() { return (LEN); } 50 | 51 | 52 | 53 | 54 | 55 | } 56 | 57 | #endif 58 | 59 | #undef ffft_Array_CURRENT_CODEHEADER 60 | 61 | 62 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/include/CDelay.h: -------------------------------------------------------------------------------- 1 | /* 2 | CDelay: implements a delay line of D samples with 3 | output attenuator 4 | 5 | Can be used alone or as a base class. 6 | 7 | 8 | 9 | */ 10 | #pragma once 11 | 12 | #include 13 | 14 | class CDelay { 15 | public: 16 | // constructor/destructor 17 | CDelay(); 18 | virtual ~CDelay(); // base class MUST declare virtual destructor 19 | 20 | protected: 21 | // member variables 22 | // 23 | // delay in samples; float supports fractional delay 24 | float m_fDelayInSamples; 25 | 26 | // output attenuation value, cooked 27 | float m_fOutputAttenuation; 28 | 29 | // pointer to our circular buffer 30 | float *m_pBuffer; 31 | 32 | // read/write index values for circ buffer 33 | int m_nReadIndex; 34 | int m_nWriteIndex; 35 | 36 | // max length of buffer 37 | int m_nBufferSize; 38 | 39 | // sample rate (needed for other function) 40 | int m_nSampleRate; 41 | 42 | // delay in mSec, set by Parent Plug In 43 | float m_fDelay_ms; 44 | 45 | // Attenuation in dB, set by Parent Plug In 46 | float m_fOutputAttenuation_dB; 47 | 48 | // functions for the owner plug-in to call 49 | public: 50 | // declare buffer and zero 51 | void init(int nDelayLength); 52 | 53 | // function to cook variables 54 | void cookVariables(); 55 | 56 | // flush buffer, reset pointers to top 57 | void resetDelay(); 58 | 59 | // set functions for Parent Plug In 60 | void setDelay_mSec(float fmSec); 61 | void setSampleRate(int nFs) { m_nSampleRate = nFs; }; 62 | void setOutputAttenuation_dB(float fAttendB); 63 | 64 | // read the delay without writing or incrementing 65 | float readDelay(); 66 | 67 | // read the delay at an arbitrary time without writing or incrementing 68 | float readDelayAt(float fmSec); 69 | 70 | // write the input and icrement pointers 71 | void writeDelayAndInc(float fDelayInput); 72 | 73 | // process audio 74 | bool processAudio(float *pInput, float *pOutput); 75 | }; 76 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/include/CombFilter.h: -------------------------------------------------------------------------------- 1 | /* 2 | CCombFilter: implements a comb filter of length D with 3 | feedback gain m_fComb_g 4 | 5 | Can be used alone or as a base class. 6 | 7 | 8 | 9 | */ 10 | 11 | // Inherited Base Class functions: 12 | /* 13 | void init(int nDelayLength); 14 | void resetDelay(); 15 | void setDelay_mSec(float fmSec); 16 | void setOutputAttenuation_dB(float fAttendB); 17 | 18 | // NEED TO OVERRIDE 19 | bool processAudio(float *pInput, float *pOutput); 20 | */ 21 | #pragma once 22 | #include "CDelay.h" 23 | 24 | // derived class: CDelay does most of the work 25 | class CCombFilter : public CDelay { 26 | public: 27 | // constructor/destructor 28 | CCombFilter(); 29 | ~CCombFilter(); 30 | 31 | // members 32 | protected: 33 | float m_fComb_g; // one coefficient 34 | 35 | public: 36 | // set our g value directly 37 | void setComb_g(float fCombg) { m_fComb_g = fCombg; } 38 | 39 | // set gain using RT60 time 40 | void setComb_g_with_RTSixty(float fRT) { 41 | float fExponent = -3.0f * m_fDelayInSamples * (1.0f / m_nSampleRate); 42 | fRT /= 1000.0; // RT is in mSec! 43 | 44 | m_fComb_g = powf((float)10.0, fExponent / fRT); 45 | } 46 | 47 | // do some audio processing 48 | bool processAudio(float *pInput, float *pOutput); 49 | }; 50 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/include/Compressor.h: -------------------------------------------------------------------------------- 1 | // 2 | // Compressor.h 3 | // 4 | // Created by Mike Gazzaruso, revision history on Github. 5 | // 6 | // 7 | 8 | #pragma once 9 | 10 | #include "CDelay.h" 11 | #include "pluginconstants.h" 12 | 13 | class Compressor { 14 | 15 | public: 16 | Compressor(float fThreshold, float fRatio, float fAttack, float fRelease, 17 | int iSampleRate); 18 | float Process(float fInputSignal, bool bLimitOn, float fSensitivity); 19 | void setParameters(float fThreshold, float fRatio, float fAttack, 20 | float fRelease); 21 | float getCompGain(); 22 | 23 | // Private 24 | private: 25 | CEnvelopeDetector envDetector; 26 | CDelay delayLookAhead; 27 | float theThreshold, theRatio, theAttack, theRelease; 28 | float calcCompressorGain(float fDetectorValue, float fThreshold, float fRatio, 29 | float fKneeWidth, bool bLimit); 30 | float compGain; 31 | }; 32 | 33 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/include/DelayAPF.h: -------------------------------------------------------------------------------- 1 | /* 2 | CDelayAPF: implements a delaying APF with a single coefficient g 3 | 4 | Can be used alone or as a base class. 5 | 6 | 7 | 8 | */ 9 | 10 | // Inherited Base Class functions: 11 | /* 12 | void init(int nDelayLength); 13 | void resetDelay(); 14 | void setDelay_mSec(float fmSec); 15 | void setOutputAttenuation_dB(float fAttendB); 16 | 17 | // NEED TO OVERRIDE 18 | bool processAudio(float *pInput, float *pOutput); 19 | */ 20 | 21 | #pragma once 22 | #include "CDelay.h" 23 | 24 | // derived class of CDelay 25 | class CDelayAPF : public CDelay { 26 | public: 27 | // constructor/destructor 28 | CDelayAPF(); 29 | ~CDelayAPF(); 30 | 31 | // members 32 | protected: 33 | float m_fAPF_g; // one g coefficient 34 | 35 | public: 36 | // set our g value 37 | void setAPF_g(float fAPFg) { m_fAPF_g = fAPFg; } 38 | 39 | // overrides 40 | bool processAudio(float *pInput, float *pOutput); 41 | }; 42 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/include/DynArray.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | DynArray.h 4 | By Laurent de Soras 5 | 6 | --- Legal stuff --- 7 | 8 | This program is free software. It comes without any warranty, to 9 | the extent permitted by applicable law. You can redistribute it 10 | and/or modify it under the terms of the Do What The Fuck You Want 11 | To Public License, Version 2, as published by Sam Hocevar. See 12 | http://sam.zoy.org/wtfpl/COPYING for more details. 13 | 14 | *Tab=3***********************************************************************/ 15 | 16 | #if defined(ffft_DynArray_CURRENT_CODEHEADER) 17 | #error Recursive inclusion of DynArray code header. 18 | #endif 19 | #define ffft_DynArray_CURRENT_CODEHEADER 20 | 21 | #if !defined(ffft_DynArray_CODEHEADER_INCLUDED) 22 | #define ffft_DynArray_CODEHEADER_INCLUDED 23 | 24 | 25 | 26 | #include 27 | 28 | namespace ffft { 29 | 30 | template DynArray::DynArray() : _data_ptr(0), _len(0) { 31 | 32 | } 33 | 34 | template DynArray::DynArray(long size) : _data_ptr(0), _len(0) { 35 | assert(size >= 0); 36 | if (size > 0) { 37 | _data_ptr = new DataType[size]; 38 | _len = size; 39 | } 40 | } 41 | 42 | template DynArray::~DynArray() { 43 | delete[] _data_ptr; 44 | _data_ptr = 0; 45 | _len = 0; 46 | } 47 | 48 | template long DynArray::size() const { return (_len); } 49 | 50 | template void DynArray::resize(long size) { 51 | assert(size >= 0); 52 | if (size > 0) { 53 | DataType *old_data_ptr = _data_ptr; 54 | DataType *tmp_data_ptr = new DataType[size]; 55 | 56 | _data_ptr = tmp_data_ptr; 57 | _len = size; 58 | 59 | delete[] old_data_ptr; 60 | } 61 | } 62 | 63 | template 64 | const typename DynArray::DataType &DynArray::operator[](long pos) const { 65 | assert(pos >= 0); 66 | assert(pos < _len); 67 | 68 | return (_data_ptr[pos]); 69 | } 70 | 71 | template 72 | typename DynArray::DataType &DynArray::operator[](long pos) { 73 | assert(pos >= 0); 74 | assert(pos < _len); 75 | 76 | return (_data_ptr[pos]); 77 | } 78 | 79 | 80 | 81 | 82 | 83 | } 84 | 85 | #endif 86 | 87 | #undef ffft_DynArray_CURRENT_CODEHEADER 88 | 89 | 90 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/include/Equalisator.h: -------------------------------------------------------------------------------- 1 | // 2 | // Equalisator.h 3 | // Rhino 4 | // 5 | // Created by Mike Gazzaruso, revision history on Github. 6 | // 7 | // 8 | 9 | #pragma once 10 | 11 | class Equalisator { 12 | public: 13 | Equalisator(); 14 | void calc_filter_coeffs(int const type, double const frequency, 15 | double const sample_rate, double const q, 16 | double const db_gain, bool q_is_bandwidth); 17 | float filter(float in0); 18 | 19 | private: 20 | // filter coeffs 21 | float b0a0, b1a0, b2a0, a1a0, a2a0; 22 | 23 | // in/out history 24 | float ou1, ou2, in1, in2; 25 | }; 26 | 27 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/include/FFTReal/FFTReal.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | FFTReal.h 4 | By Laurent de Soras 5 | 6 | --- Legal stuff --- 7 | 8 | This program is free software. It comes without any warranty, to 9 | the extent permitted by applicable law. You can redistribute it 10 | and/or modify it under the terms of the Do What The Fuck You Want 11 | To Public License, Version 2, as published by Sam Hocevar. See 12 | http://sam.zoy.org/wtfpl/COPYING for more details. 13 | 14 | *Tab=3***********************************************************************/ 15 | 16 | #if defined(ffft_FFTReal_CURRENT_CODEHEADER) 17 | #error Recursive inclusion of FFTReal code header. 18 | #endif 19 | #define ffft_FFTReal_CURRENT_CODEHEADER 20 | 21 | #if !defined(ffft_FFTReal_CODEHEADER_INCLUDED) 22 | #define ffft_FFTReal_CODEHEADER_INCLUDED 23 | 24 | 25 | 26 | #include 27 | #include 28 | 29 | namespace ffft { 30 | 31 | static inline bool FFTReal_is_pow2(long x) { 32 | assert(x > 0); 33 | 34 | return ((x & -x) == x); 35 | } 36 | 37 | static inline int FFTReal_get_next_pow2(long x) { 38 | --x; 39 | 40 | int p = 0; 41 | while ((x & ~0xFFFFL) != 0) { 42 | p += 16; 43 | x >>= 16; 44 | } 45 | while ((x & ~0xFL) != 0) { 46 | p += 4; 47 | x >>= 4; 48 | } 49 | while (x > 0) { 50 | ++p; 51 | x >>= 1; 52 | } 53 | 54 | return (p); 55 | } 56 | 57 | 58 | 59 | /* 60 | ============================================================================== 61 | Name: ctor 62 | Input parameters: 63 | - length: length of the array on which we want to do a FFT. Range: power 64 | of 2 only, > 0. Throws: std::bad_alloc 65 | ============================================================================== 66 | */ 67 | 68 | template 69 | FFTReal
::FFTReal(long length) 70 | : _length(length), _nbr_bits(FFTReal_get_next_pow2(length)), _br_lut(), 71 | _trigo_lut(), _buffer(length), _trigo_osc() { 72 | assert(FFTReal_is_pow2(length)); 73 | assert(_nbr_bits <= MAX_BIT_DEPTH); 74 | 75 | init_br_lut(); 76 | init_trigo_lut(); 77 | init_trigo_osc(); 78 | } 79 | 80 | /* 81 | ============================================================================== 82 | Name: get_length 83 | Description: 84 | Returns the number of points processed by this FFT object. 85 | Returns: The number of points, power of 2, > 0. 86 | Throws: Nothing 87 | ============================================================================== 88 | */ 89 | 90 | template long FFTReal
::get_length() const { return (_length); } 91 | 92 | /* 93 | ============================================================================== 94 | Name: do_fft 95 | Description: 96 | Compute the FFT of the array. 97 | Input parameters: 98 | - x: pointer on the source array (time). 99 | Output parameters: 100 | - f: pointer on the destination array (frequencies). 101 | f [0...length(x)/2] = real values, 102 | f [length(x)/2+1...length(x)-1] = negative imaginary values of 103 | coefficients 1...length(x)/2-1. 104 | Throws: Nothing 105 | ============================================================================== 106 | */ 107 | 108 | template 109 | void FFTReal
::do_fft(DataType f[], const DataType x[]) const { 110 | assert(f != 0); 111 | assert(f != use_buffer()); 112 | assert(x != 0); 113 | assert(x != use_buffer()); 114 | assert(x != f); 115 | 116 | // General case 117 | if (_nbr_bits > 2) { 118 | compute_fft_general(f, x); 119 | } 120 | 121 | // 4-point FFT 122 | else if (_nbr_bits == 2) { 123 | f[1] = x[0] - x[2]; 124 | f[3] = x[1] - x[3]; 125 | 126 | const DataType b_0 = x[0] + x[2]; 127 | const DataType b_2 = x[1] + x[3]; 128 | 129 | f[0] = b_0 + b_2; 130 | f[2] = b_0 - b_2; 131 | } 132 | 133 | // 2-point FFT 134 | else if (_nbr_bits == 1) { 135 | f[0] = x[0] + x[1]; 136 | f[1] = x[0] - x[1]; 137 | } 138 | 139 | // 1-point FFT 140 | else { 141 | f[0] = x[0]; 142 | } 143 | } 144 | 145 | /* 146 | ============================================================================== 147 | Name: do_ifft 148 | Description: 149 | Compute the inverse FFT of the array. Note that data must be 150 | post-scaled: IFFT (FFT (x)) = x * length (x). Input parameters: 151 | - f: pointer on the source array (frequencies). 152 | f [0...length(x)/2] = real values 153 | f [length(x)/2+1...length(x)-1] = negative imaginary values of 154 | coefficients 1...length(x)/2-1. 155 | Output parameters: 156 | - x: pointer on the destination array (time). 157 | Throws: Nothing 158 | ============================================================================== 159 | */ 160 | 161 | template 162 | void FFTReal
::do_ifft(const DataType f[], DataType x[]) const { 163 | assert(f != 0); 164 | assert(f != use_buffer()); 165 | assert(x != 0); 166 | assert(x != use_buffer()); 167 | assert(x != f); 168 | 169 | // General case 170 | if (_nbr_bits > 2) { 171 | compute_ifft_general(f, x); 172 | } 173 | 174 | // 4-point IFFT 175 | else if (_nbr_bits == 2) { 176 | const DataType b_0 = f[0] + f[2]; 177 | const DataType b_2 = f[0] - f[2]; 178 | 179 | x[0] = b_0 + f[1] * 2; 180 | x[2] = b_0 - f[1] * 2; 181 | x[1] = b_2 + f[3] * 2; 182 | x[3] = b_2 - f[3] * 2; 183 | } 184 | 185 | // 2-point IFFT 186 | else if (_nbr_bits == 1) { 187 | x[0] = f[0] + f[1]; 188 | x[1] = f[0] - f[1]; 189 | } 190 | 191 | // 1-point IFFT 192 | else { 193 | x[0] = f[0]; 194 | } 195 | } 196 | 197 | /* 198 | ============================================================================== 199 | Name: rescale 200 | Description: 201 | Scale an array by divide each element by its length. This function 202 | should be called after FFT + IFFT. Input parameters: 203 | - x: pointer on array to rescale (time or frequency). 204 | Throws: Nothing 205 | ============================================================================== 206 | */ 207 | 208 | template void FFTReal
::rescale(DataType x[]) const { 209 | const DataType mul = DataType(1.0 / _length); 210 | 211 | if (_length < 4) { 212 | long i = _length - 1; 213 | do { 214 | x[i] *= mul; 215 | --i; 216 | } while (i >= 0); 217 | } 218 | 219 | else { 220 | assert((_length & 3) == 0); 221 | 222 | // Could be optimized with SIMD instruction sets (needs alignment check) 223 | long i = _length - 4; 224 | do { 225 | x[i + 0] *= mul; 226 | x[i + 1] *= mul; 227 | x[i + 2] *= mul; 228 | x[i + 3] *= mul; 229 | i -= 4; 230 | } while (i >= 0); 231 | } 232 | } 233 | 234 | /* 235 | ============================================================================== 236 | Name: use_buffer 237 | Description: 238 | Access the internal buffer, whose length is the FFT one. 239 | Buffer content will be erased at each do_fft() / do_ifft() call! 240 | This buffer cannot be used as: 241 | - source for FFT or IFFT done with this object 242 | - destination for FFT or IFFT done with this object 243 | Returns: 244 | Buffer start address 245 | Throws: Nothing 246 | ============================================================================== 247 | */ 248 | 249 | template 250 | typename FFTReal
::DataType *FFTReal
::use_buffer() const { 251 | return (&_buffer[0]); 252 | } 253 | 254 | 255 | 256 | 257 | 258 | template void FFTReal
::init_br_lut() { 259 | const long length = 1L << _nbr_bits; 260 | _br_lut.resize(length); 261 | 262 | _br_lut[0] = 0; 263 | long br_index = 0; 264 | for (long cnt = 1; cnt < length; ++cnt) { 265 | // ++br_index (bit reversed) 266 | long bit = length >> 1; 267 | while (((br_index ^= bit) & bit) == 0) { 268 | bit >>= 1; 269 | } 270 | 271 | _br_lut[cnt] = br_index; 272 | } 273 | } 274 | 275 | template void FFTReal
::init_trigo_lut() { 276 | using namespace std; 277 | 278 | if (_nbr_bits > 3) { 279 | const long total_len = (1L << (_nbr_bits - 1)) - 4; 280 | _trigo_lut.resize(total_len); 281 | 282 | for (int level = 3; level < _nbr_bits; ++level) { 283 | const long level_len = 1L << (level - 1); 284 | DataType *const level_ptr = &_trigo_lut[get_trigo_level_index(level)]; 285 | const double mul = PI / (level_len << 1); 286 | 287 | for (long i = 0; i < level_len; ++i) { 288 | level_ptr[i] = static_cast(cos(i * mul)); 289 | } 290 | } 291 | } 292 | } 293 | 294 | template void FFTReal
::init_trigo_osc() { 295 | const int nbr_osc = _nbr_bits - TRIGO_BD_LIMIT; 296 | if (nbr_osc > 0) { 297 | _trigo_osc.resize(nbr_osc); 298 | 299 | for (int osc_cnt = 0; osc_cnt < nbr_osc; ++osc_cnt) { 300 | OscType &osc = _trigo_osc[osc_cnt]; 301 | 302 | const long len = 1L << (TRIGO_BD_LIMIT + osc_cnt); 303 | const double mul = (0.5 * PI) / len; 304 | osc.set_step(mul); 305 | } 306 | } 307 | } 308 | 309 | template const long *FFTReal
::get_br_ptr() const { 310 | return (&_br_lut[0]); 311 | } 312 | 313 | template 314 | const typename FFTReal
::DataType * 315 | FFTReal
::get_trigo_ptr(int level) const { 316 | assert(level >= 3); 317 | 318 | return (&_trigo_lut[get_trigo_level_index(level)]); 319 | } 320 | 321 | template long FFTReal
::get_trigo_level_index(int level) const { 322 | assert(level >= 3); 323 | 324 | return ((1L << (level - 1)) - 4); 325 | } 326 | 327 | // Transform in several passes 328 | template 329 | void FFTReal
::compute_fft_general(DataType f[], const DataType x[]) const { 330 | assert(f != 0); 331 | assert(f != use_buffer()); 332 | assert(x != 0); 333 | assert(x != use_buffer()); 334 | assert(x != f); 335 | 336 | DataType *sf; 337 | DataType *df; 338 | 339 | if ((_nbr_bits & 1) != 0) { 340 | df = use_buffer(); 341 | sf = f; 342 | } else { 343 | df = f; 344 | sf = use_buffer(); 345 | } 346 | 347 | compute_direct_pass_1_2(df, x); 348 | compute_direct_pass_3(sf, df); 349 | 350 | for (int pass = 3; pass < _nbr_bits; ++pass) { 351 | compute_direct_pass_n(df, sf, pass); 352 | 353 | DataType *const temp_ptr = df; 354 | df = sf; 355 | sf = temp_ptr; 356 | } 357 | } 358 | 359 | template 360 | void FFTReal
::compute_direct_pass_1_2(DataType df[], 361 | const DataType x[]) const { 362 | assert(df != 0); 363 | assert(x != 0); 364 | assert(df != x); 365 | 366 | const long *const bit_rev_lut_ptr = get_br_ptr(); 367 | long coef_index = 0; 368 | do { 369 | const long rev_index_0 = bit_rev_lut_ptr[coef_index]; 370 | const long rev_index_1 = bit_rev_lut_ptr[coef_index + 1]; 371 | const long rev_index_2 = bit_rev_lut_ptr[coef_index + 2]; 372 | const long rev_index_3 = bit_rev_lut_ptr[coef_index + 3]; 373 | 374 | DataType *const df2 = df + coef_index; 375 | df2[1] = x[rev_index_0] - x[rev_index_1]; 376 | df2[3] = x[rev_index_2] - x[rev_index_3]; 377 | 378 | const DataType sf_0 = x[rev_index_0] + x[rev_index_1]; 379 | const DataType sf_2 = x[rev_index_2] + x[rev_index_3]; 380 | 381 | df2[0] = sf_0 + sf_2; 382 | df2[2] = sf_0 - sf_2; 383 | 384 | coef_index += 4; 385 | } while (coef_index < _length); 386 | } 387 | 388 | template 389 | void FFTReal
::compute_direct_pass_3(DataType df[], 390 | const DataType sf[]) const { 391 | assert(df != 0); 392 | assert(sf != 0); 393 | assert(df != sf); 394 | 395 | const DataType sqrt2_2 = DataType(SQRT2 * 0.5); 396 | long coef_index = 0; 397 | do { 398 | DataType v; 399 | 400 | df[coef_index] = sf[coef_index] + sf[coef_index + 4]; 401 | df[coef_index + 4] = sf[coef_index] - sf[coef_index + 4]; 402 | df[coef_index + 2] = sf[coef_index + 2]; 403 | df[coef_index + 6] = sf[coef_index + 6]; 404 | 405 | v = (sf[coef_index + 5] - sf[coef_index + 7]) * sqrt2_2; 406 | df[coef_index + 1] = sf[coef_index + 1] + v; 407 | df[coef_index + 3] = sf[coef_index + 1] - v; 408 | 409 | v = (sf[coef_index + 5] + sf[coef_index + 7]) * sqrt2_2; 410 | df[coef_index + 5] = v + sf[coef_index + 3]; 411 | df[coef_index + 7] = v - sf[coef_index + 3]; 412 | 413 | coef_index += 8; 414 | } while (coef_index < _length); 415 | } 416 | 417 | template 418 | void FFTReal
::compute_direct_pass_n(DataType df[], const DataType sf[], 419 | int pass) const { 420 | assert(df != 0); 421 | assert(sf != 0); 422 | assert(df != sf); 423 | assert(pass >= 3); 424 | assert(pass < _nbr_bits); 425 | 426 | if (pass <= TRIGO_BD_LIMIT) { 427 | compute_direct_pass_n_lut(df, sf, pass); 428 | } else { 429 | compute_direct_pass_n_osc(df, sf, pass); 430 | } 431 | } 432 | 433 | template 434 | void FFTReal
::compute_direct_pass_n_lut(DataType df[], const DataType sf[], 435 | int pass) const { 436 | assert(df != 0); 437 | assert(sf != 0); 438 | assert(df != sf); 439 | assert(pass >= 3); 440 | assert(pass < _nbr_bits); 441 | 442 | const long nbr_coef = 1 << pass; 443 | const long h_nbr_coef = nbr_coef >> 1; 444 | const long d_nbr_coef = nbr_coef << 1; 445 | long coef_index = 0; 446 | const DataType *const cos_ptr = get_trigo_ptr(pass); 447 | do { 448 | const DataType *const sf1r = sf + coef_index; 449 | const DataType *const sf2r = sf1r + nbr_coef; 450 | DataType *const dfr = df + coef_index; 451 | DataType *const dfi = dfr + nbr_coef; 452 | 453 | // Extreme coefficients are always real 454 | dfr[0] = sf1r[0] + sf2r[0]; 455 | dfi[0] = sf1r[0] - sf2r[0]; // dfr [nbr_coef] = 456 | dfr[h_nbr_coef] = sf1r[h_nbr_coef]; 457 | dfi[h_nbr_coef] = sf2r[h_nbr_coef]; 458 | 459 | // Others are conjugate complex numbers 460 | const DataType *const sf1i = sf1r + h_nbr_coef; 461 | const DataType *const sf2i = sf1i + nbr_coef; 462 | for (long i = 1; i < h_nbr_coef; ++i) { 463 | const DataType c = cos_ptr[i]; // cos (i*PI/nbr_coef); 464 | const DataType s = cos_ptr[h_nbr_coef - i]; // sin (i*PI/nbr_coef); 465 | DataType v; 466 | 467 | v = sf2r[i] * c - sf2i[i] * s; 468 | dfr[i] = sf1r[i] + v; 469 | dfi[-i] = sf1r[i] - v; // dfr [nbr_coef - i] = 470 | 471 | v = sf2r[i] * s + sf2i[i] * c; 472 | dfi[i] = v + sf1i[i]; 473 | dfi[nbr_coef - i] = v - sf1i[i]; 474 | } 475 | 476 | coef_index += d_nbr_coef; 477 | } while (coef_index < _length); 478 | } 479 | 480 | template 481 | void FFTReal
::compute_direct_pass_n_osc(DataType df[], const DataType sf[], 482 | int pass) const { 483 | assert(df != 0); 484 | assert(sf != 0); 485 | assert(df != sf); 486 | assert(pass > TRIGO_BD_LIMIT); 487 | assert(pass < _nbr_bits); 488 | 489 | const long nbr_coef = 1 << pass; 490 | const long h_nbr_coef = nbr_coef >> 1; 491 | const long d_nbr_coef = nbr_coef << 1; 492 | long coef_index = 0; 493 | OscType &osc = _trigo_osc[pass - (TRIGO_BD_LIMIT + 1)]; 494 | do { 495 | const DataType *const sf1r = sf + coef_index; 496 | const DataType *const sf2r = sf1r + nbr_coef; 497 | DataType *const dfr = df + coef_index; 498 | DataType *const dfi = dfr + nbr_coef; 499 | 500 | osc.clear_buffers(); 501 | 502 | // Extreme coefficients are always real 503 | dfr[0] = sf1r[0] + sf2r[0]; 504 | dfi[0] = sf1r[0] - sf2r[0]; // dfr [nbr_coef] = 505 | dfr[h_nbr_coef] = sf1r[h_nbr_coef]; 506 | dfi[h_nbr_coef] = sf2r[h_nbr_coef]; 507 | 508 | // Others are conjugate complex numbers 509 | const DataType *const sf1i = sf1r + h_nbr_coef; 510 | const DataType *const sf2i = sf1i + nbr_coef; 511 | for (long i = 1; i < h_nbr_coef; ++i) { 512 | osc.step(); 513 | const DataType c = osc.get_cos(); 514 | const DataType s = osc.get_sin(); 515 | DataType v; 516 | 517 | v = sf2r[i] * c - sf2i[i] * s; 518 | dfr[i] = sf1r[i] + v; 519 | dfi[-i] = sf1r[i] - v; // dfr [nbr_coef - i] = 520 | 521 | v = sf2r[i] * s + sf2i[i] * c; 522 | dfi[i] = v + sf1i[i]; 523 | dfi[nbr_coef - i] = v - sf1i[i]; 524 | } 525 | 526 | coef_index += d_nbr_coef; 527 | } while (coef_index < _length); 528 | } 529 | 530 | // Transform in several pass 531 | template 532 | void FFTReal
::compute_ifft_general(const DataType f[], DataType x[]) const { 533 | assert(f != 0); 534 | assert(f != use_buffer()); 535 | assert(x != 0); 536 | assert(x != use_buffer()); 537 | assert(x != f); 538 | 539 | DataType *sf = const_cast(f); 540 | DataType *df; 541 | DataType *df_temp; 542 | 543 | if (_nbr_bits & 1) { 544 | df = use_buffer(); 545 | df_temp = x; 546 | } else { 547 | df = x; 548 | df_temp = use_buffer(); 549 | } 550 | 551 | for (int pass = _nbr_bits - 1; pass >= 3; --pass) { 552 | compute_inverse_pass_n(df, sf, pass); 553 | 554 | if (pass < _nbr_bits - 1) { 555 | DataType *const temp_ptr = df; 556 | df = sf; 557 | sf = temp_ptr; 558 | } else { 559 | sf = df; 560 | df = df_temp; 561 | } 562 | } 563 | 564 | compute_inverse_pass_3(df, sf); 565 | compute_inverse_pass_1_2(x, df); 566 | } 567 | 568 | template 569 | void FFTReal
::compute_inverse_pass_n(DataType df[], const DataType sf[], 570 | int pass) const { 571 | assert(df != 0); 572 | assert(sf != 0); 573 | assert(df != sf); 574 | assert(pass >= 3); 575 | assert(pass < _nbr_bits); 576 | 577 | if (pass <= TRIGO_BD_LIMIT) { 578 | compute_inverse_pass_n_lut(df, sf, pass); 579 | } else { 580 | compute_inverse_pass_n_osc(df, sf, pass); 581 | } 582 | } 583 | 584 | template 585 | void FFTReal
::compute_inverse_pass_n_lut(DataType df[], const DataType sf[], 586 | int pass) const { 587 | assert(df != 0); 588 | assert(sf != 0); 589 | assert(df != sf); 590 | assert(pass >= 3); 591 | assert(pass < _nbr_bits); 592 | 593 | const long nbr_coef = 1 << pass; 594 | const long h_nbr_coef = nbr_coef >> 1; 595 | const long d_nbr_coef = nbr_coef << 1; 596 | long coef_index = 0; 597 | const DataType *const cos_ptr = get_trigo_ptr(pass); 598 | do { 599 | const DataType *const sfr = sf + coef_index; 600 | const DataType *const sfi = sfr + nbr_coef; 601 | DataType *const df1r = df + coef_index; 602 | DataType *const df2r = df1r + nbr_coef; 603 | 604 | // Extreme coefficients are always real 605 | df1r[0] = sfr[0] + sfi[0]; // + sfr [nbr_coef] 606 | df2r[0] = sfr[0] - sfi[0]; // - sfr [nbr_coef] 607 | df1r[h_nbr_coef] = sfr[h_nbr_coef] * 2; 608 | df2r[h_nbr_coef] = sfi[h_nbr_coef] * 2; 609 | 610 | // Others are conjugate complex numbers 611 | DataType *const df1i = df1r + h_nbr_coef; 612 | DataType *const df2i = df1i + nbr_coef; 613 | for (long i = 1; i < h_nbr_coef; ++i) { 614 | df1r[i] = sfr[i] + sfi[-i]; // + sfr [nbr_coef - i] 615 | df1i[i] = sfi[i] - sfi[nbr_coef - i]; 616 | 617 | const DataType c = cos_ptr[i]; // cos (i*PI/nbr_coef); 618 | const DataType s = cos_ptr[h_nbr_coef - i]; // sin (i*PI/nbr_coef); 619 | const DataType vr = sfr[i] - sfi[-i]; // - sfr [nbr_coef - i] 620 | const DataType vi = sfi[i] + sfi[nbr_coef - i]; 621 | 622 | df2r[i] = vr * c + vi * s; 623 | df2i[i] = vi * c - vr * s; 624 | } 625 | 626 | coef_index += d_nbr_coef; 627 | } while (coef_index < _length); 628 | } 629 | 630 | template 631 | void FFTReal
::compute_inverse_pass_n_osc(DataType df[], const DataType sf[], 632 | int pass) const { 633 | assert(df != 0); 634 | assert(sf != 0); 635 | assert(df != sf); 636 | assert(pass > TRIGO_BD_LIMIT); 637 | assert(pass < _nbr_bits); 638 | 639 | const long nbr_coef = 1 << pass; 640 | const long h_nbr_coef = nbr_coef >> 1; 641 | const long d_nbr_coef = nbr_coef << 1; 642 | long coef_index = 0; 643 | OscType &osc = _trigo_osc[pass - (TRIGO_BD_LIMIT + 1)]; 644 | do { 645 | const DataType *const sfr = sf + coef_index; 646 | const DataType *const sfi = sfr + nbr_coef; 647 | DataType *const df1r = df + coef_index; 648 | DataType *const df2r = df1r + nbr_coef; 649 | 650 | osc.clear_buffers(); 651 | 652 | // Extreme coefficients are always real 653 | df1r[0] = sfr[0] + sfi[0]; // + sfr [nbr_coef] 654 | df2r[0] = sfr[0] - sfi[0]; // - sfr [nbr_coef] 655 | df1r[h_nbr_coef] = sfr[h_nbr_coef] * 2; 656 | df2r[h_nbr_coef] = sfi[h_nbr_coef] * 2; 657 | 658 | // Others are conjugate complex numbers 659 | DataType *const df1i = df1r + h_nbr_coef; 660 | DataType *const df2i = df1i + nbr_coef; 661 | for (long i = 1; i < h_nbr_coef; ++i) { 662 | df1r[i] = sfr[i] + sfi[-i]; // + sfr [nbr_coef - i] 663 | df1i[i] = sfi[i] - sfi[nbr_coef - i]; 664 | 665 | osc.step(); 666 | const DataType c = osc.get_cos(); 667 | const DataType s = osc.get_sin(); 668 | const DataType vr = sfr[i] - sfi[-i]; // - sfr [nbr_coef - i] 669 | const DataType vi = sfi[i] + sfi[nbr_coef - i]; 670 | 671 | df2r[i] = vr * c + vi * s; 672 | df2i[i] = vi * c - vr * s; 673 | } 674 | 675 | coef_index += d_nbr_coef; 676 | } while (coef_index < _length); 677 | } 678 | 679 | template 680 | void FFTReal
::compute_inverse_pass_3(DataType df[], 681 | const DataType sf[]) const { 682 | assert(df != 0); 683 | assert(sf != 0); 684 | assert(df != sf); 685 | 686 | const DataType sqrt2_2 = DataType(SQRT2 * 0.5); 687 | long coef_index = 0; 688 | do { 689 | df[coef_index] = sf[coef_index] + sf[coef_index + 4]; 690 | df[coef_index + 4] = sf[coef_index] - sf[coef_index + 4]; 691 | df[coef_index + 2] = sf[coef_index + 2] * 2; 692 | df[coef_index + 6] = sf[coef_index + 6] * 2; 693 | 694 | df[coef_index + 1] = sf[coef_index + 1] + sf[coef_index + 3]; 695 | df[coef_index + 3] = sf[coef_index + 5] - sf[coef_index + 7]; 696 | 697 | const DataType vr = sf[coef_index + 1] - sf[coef_index + 3]; 698 | const DataType vi = sf[coef_index + 5] + sf[coef_index + 7]; 699 | 700 | df[coef_index + 5] = (vr + vi) * sqrt2_2; 701 | df[coef_index + 7] = (vi - vr) * sqrt2_2; 702 | 703 | coef_index += 8; 704 | } while (coef_index < _length); 705 | } 706 | 707 | template 708 | void FFTReal
::compute_inverse_pass_1_2(DataType x[], 709 | const DataType sf[]) const { 710 | assert(x != 0); 711 | assert(sf != 0); 712 | assert(x != sf); 713 | 714 | const long *bit_rev_lut_ptr = get_br_ptr(); 715 | const DataType *sf2 = sf; 716 | long coef_index = 0; 717 | do { 718 | { 719 | const DataType b_0 = sf2[0] + sf2[2]; 720 | const DataType b_2 = sf2[0] - sf2[2]; 721 | const DataType b_1 = sf2[1] * 2; 722 | const DataType b_3 = sf2[3] * 2; 723 | 724 | x[bit_rev_lut_ptr[0]] = b_0 + b_1; 725 | x[bit_rev_lut_ptr[1]] = b_0 - b_1; 726 | x[bit_rev_lut_ptr[2]] = b_2 + b_3; 727 | x[bit_rev_lut_ptr[3]] = b_2 - b_3; 728 | } 729 | { 730 | const DataType b_0 = sf2[4] + sf2[6]; 731 | const DataType b_2 = sf2[4] - sf2[6]; 732 | const DataType b_1 = sf2[5] * 2; 733 | const DataType b_3 = sf2[7] * 2; 734 | 735 | x[bit_rev_lut_ptr[4]] = b_0 + b_1; 736 | x[bit_rev_lut_ptr[5]] = b_0 - b_1; 737 | x[bit_rev_lut_ptr[6]] = b_2 + b_3; 738 | x[bit_rev_lut_ptr[7]] = b_2 - b_3; 739 | } 740 | 741 | sf2 += 8; 742 | coef_index += 8; 743 | bit_rev_lut_ptr += 8; 744 | } while (coef_index < _length); 745 | } 746 | 747 | } 748 | 749 | #endif 750 | 751 | #undef ffft_FFTReal_CURRENT_CODEHEADER 752 | 753 | 754 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/include/FFTReal/FFTRealFixLen.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | FFTRealFixLen.h 4 | By Laurent de Soras 5 | 6 | --- Legal stuff --- 7 | 8 | This program is free software. It comes without any warranty, to 9 | the extent permitted by applicable law. You can redistribute it 10 | and/or modify it under the terms of the Do What The Fuck You Want 11 | To Public License, Version 2, as published by Sam Hocevar. See 12 | http://sam.zoy.org/wtfpl/COPYING for more details. 13 | 14 | *Tab=3***********************************************************************/ 15 | 16 | #if defined(ffft_FFTRealFixLen_CURRENT_CODEHEADER) 17 | #error Recursive inclusion of FFTRealFixLen code header. 18 | #endif 19 | #define ffft_FFTRealFixLen_CURRENT_CODEHEADER 20 | 21 | #if !defined(ffft_FFTRealFixLen_CODEHEADER_INCLUDED) 22 | #define ffft_FFTRealFixLen_CODEHEADER_INCLUDED 23 | 24 | 25 | 26 | #include "ffft/FFTRealPassDirect.h" 27 | #include "ffft/FFTRealPassInverse.h" 28 | #include "ffft/FFTRealSelect.h" 29 | #include "ffft/def.h" 30 | 31 | #include 32 | #include 33 | 34 | namespace std {} 35 | 36 | namespace ffft { 37 | 38 | template 39 | FFTRealFixLen::FFTRealFixLen() 40 | : _buffer(FFT_LEN), _br_data(BR_ARR_SIZE), 41 | _trigo_data(TRIGO_TABLE_ARR_SIZE), _trigo_osc() { 42 | build_br_lut(); 43 | build_trigo_lut(); 44 | build_trigo_osc(); 45 | } 46 | 47 | template long FFTRealFixLen::get_length() const { 48 | return (FFT_LEN); 49 | } 50 | 51 | // General case 52 | template 53 | void FFTRealFixLen::do_fft(DataType f[], const DataType x[]) { 54 | assert(f != 0); 55 | assert(x != 0); 56 | assert(x != f); 57 | assert(FFT_LEN_L2 >= 3); 58 | 59 | // Do the transform in several passes 60 | const DataType *cos_ptr = &_trigo_data[0]; 61 | const long *br_ptr = &_br_data[0]; 62 | 63 | FFTRealPassDirect::process(FFT_LEN, f, &_buffer[0], x, 64 | cos_ptr, TRIGO_TABLE_ARR_SIZE, 65 | br_ptr, &_trigo_osc[0]); 66 | } 67 | 68 | // 4-point FFT 69 | template <> 70 | inline void FFTRealFixLen<2>::do_fft(DataType f[], const DataType x[]) { 71 | assert(f != 0); 72 | assert(x != 0); 73 | assert(x != f); 74 | 75 | f[1] = x[0] - x[2]; 76 | f[3] = x[1] - x[3]; 77 | 78 | const DataType b_0 = x[0] + x[2]; 79 | const DataType b_2 = x[1] + x[3]; 80 | 81 | f[0] = b_0 + b_2; 82 | f[2] = b_0 - b_2; 83 | } 84 | 85 | // 2-point FFT 86 | template <> 87 | inline void FFTRealFixLen<1>::do_fft(DataType f[], const DataType x[]) { 88 | assert(f != 0); 89 | assert(x != 0); 90 | assert(x != f); 91 | 92 | f[0] = x[0] + x[1]; 93 | f[1] = x[0] - x[1]; 94 | } 95 | 96 | // 1-point FFT 97 | template <> 98 | inline void FFTRealFixLen<0>::do_fft(DataType f[], const DataType x[]) { 99 | assert(f != 0); 100 | assert(x != 0); 101 | 102 | f[0] = x[0]; 103 | } 104 | 105 | // General case 106 | template 107 | void FFTRealFixLen::do_ifft(const DataType f[], DataType x[]) { 108 | assert(f != 0); 109 | assert(x != 0); 110 | assert(x != f); 111 | assert(FFT_LEN_L2 >= 3); 112 | 113 | // Do the transform in several passes 114 | DataType *s_ptr = FFTRealSelect::sel_bin(&_buffer[0], x); 115 | DataType *d_ptr = FFTRealSelect::sel_bin(x, &_buffer[0]); 116 | const DataType *cos_ptr = &_trigo_data[0]; 117 | const long *br_ptr = &_br_data[0]; 118 | 119 | FFTRealPassInverse::process(FFT_LEN, d_ptr, s_ptr, f, cos_ptr, 120 | TRIGO_TABLE_ARR_SIZE, br_ptr, 121 | &_trigo_osc[0]); 122 | } 123 | 124 | // 4-point IFFT 125 | template <> 126 | inline void FFTRealFixLen<2>::do_ifft(const DataType f[], DataType x[]) { 127 | assert(f != 0); 128 | assert(x != 0); 129 | assert(x != f); 130 | 131 | const DataType b_0 = f[0] + f[2]; 132 | const DataType b_2 = f[0] - f[2]; 133 | 134 | x[0] = b_0 + f[1] * 2; 135 | x[2] = b_0 - f[1] * 2; 136 | x[1] = b_2 + f[3] * 2; 137 | x[3] = b_2 - f[3] * 2; 138 | } 139 | 140 | // 2-point IFFT 141 | template <> 142 | inline void FFTRealFixLen<1>::do_ifft(const DataType f[], DataType x[]) { 143 | assert(f != 0); 144 | assert(x != 0); 145 | assert(x != f); 146 | 147 | x[0] = f[0] + f[1]; 148 | x[1] = f[0] - f[1]; 149 | } 150 | 151 | // 1-point IFFT 152 | template <> 153 | inline void FFTRealFixLen<0>::do_ifft(const DataType f[], DataType x[]) { 154 | assert(f != 0); 155 | assert(x != 0); 156 | assert(x != f); 157 | 158 | x[0] = f[0]; 159 | } 160 | 161 | template void FFTRealFixLen::rescale(DataType x[]) const { 162 | assert(x != 0); 163 | 164 | const DataType mul = DataType(1.0 / FFT_LEN); 165 | 166 | if (FFT_LEN < 4) { 167 | long i = FFT_LEN - 1; 168 | do { 169 | x[i] *= mul; 170 | --i; 171 | } while (i >= 0); 172 | } 173 | 174 | else { 175 | assert((FFT_LEN & 3) == 0); 176 | 177 | // Could be optimized with SIMD instruction sets (needs alignment check) 178 | long i = FFT_LEN - 4; 179 | do { 180 | x[i + 0] *= mul; 181 | x[i + 1] *= mul; 182 | x[i + 2] *= mul; 183 | x[i + 3] *= mul; 184 | i -= 4; 185 | } while (i >= 0); 186 | } 187 | } 188 | 189 | 190 | 191 | 192 | 193 | template void FFTRealFixLen::build_br_lut() { 194 | _br_data[0] = 0; 195 | for (long cnt = 1; cnt < BR_ARR_SIZE; ++cnt) { 196 | long index = cnt << 2; 197 | long br_index = 0; 198 | 199 | int bit_cnt = FFT_LEN_L2; 200 | do { 201 | br_index <<= 1; 202 | br_index += (index & 1); 203 | index >>= 1; 204 | 205 | --bit_cnt; 206 | } while (bit_cnt > 0); 207 | 208 | _br_data[cnt] = br_index; 209 | } 210 | } 211 | 212 | template void FFTRealFixLen::build_trigo_lut() { 213 | const double mul = (0.5 * PI) / TRIGO_TABLE_ARR_SIZE; 214 | for (long i = 0; i < TRIGO_TABLE_ARR_SIZE; ++i) { 215 | using namespace std; 216 | 217 | _trigo_data[i] = DataType(cos(i * mul)); 218 | } 219 | } 220 | 221 | template void FFTRealFixLen::build_trigo_osc() { 222 | for (int i = 0; i < NBR_TRIGO_OSC; ++i) { 223 | OscType &osc = _trigo_osc[i]; 224 | 225 | const long len = static_cast(TRIGO_TABLE_ARR_SIZE) << (i + 1); 226 | const double mul = (0.5 * PI) / len; 227 | osc.set_step(mul); 228 | } 229 | } 230 | 231 | } 232 | 233 | #endif 234 | 235 | #undef ffft_FFTRealFixLen_CURRENT_CODEHEADER 236 | 237 | 238 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/include/FFTReal/FFTRealFixLenParam.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | FFTRealFixLenParam.h 4 | By Laurent de Soras 5 | 6 | --- Legal stuff --- 7 | 8 | This program is free software. It comes without any warranty, to 9 | the extent permitted by applicable law. You can redistribute it 10 | and/or modify it under the terms of the Do What The Fuck You Want 11 | To Public License, Version 2, as published by Sam Hocevar. See 12 | http://sam.zoy.org/wtfpl/COPYING for more details. 13 | 14 | *Tab=3***********************************************************************/ 15 | 16 | #if !defined(ffft_FFTRealFixLenParam_HEADER_INCLUDED) 17 | #define ffft_FFTRealFixLenParam_HEADER_INCLUDED 18 | 19 | #if defined(_MSC_VER) 20 | #pragma once 21 | #pragma warning(4 : 4250) // "Inherits via dominance." 22 | #endif 23 | 24 | 25 | 26 | namespace ffft { 27 | 28 | class FFTRealFixLenParam { 29 | 30 | public: 31 | // Over this bit depth, we use direct calculation for sin/cos 32 | enum { TRIGO_BD_LIMIT = 12 }; 33 | 34 | typedef float DataType; 35 | 36 | private: 37 | FFTRealFixLenParam(); 38 | FFTRealFixLenParam(const FFTRealFixLenParam &other); 39 | FFTRealFixLenParam &operator=(const FFTRealFixLenParam &other); 40 | bool operator==(const FFTRealFixLenParam &other); 41 | bool operator!=(const FFTRealFixLenParam &other); 42 | 43 | }; 44 | 45 | } 46 | 47 | //#include "ffft/FFTRealFixLenParam.h" 48 | 49 | #endif 50 | 51 | 52 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/include/FFTReal/FFTRealPassDirect.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | FFTRealPassDirect.h 4 | By Laurent de Soras 5 | 6 | --- Legal stuff --- 7 | 8 | This program is free software. It comes without any warranty, to 9 | the extent permitted by applicable law. You can redistribute it 10 | and/or modify it under the terms of the Do What The Fuck You Want 11 | To Public License, Version 2, as published by Sam Hocevar. See 12 | http://sam.zoy.org/wtfpl/COPYING for more details. 13 | 14 | *Tab=3***********************************************************************/ 15 | 16 | #if defined(ffft_FFTRealPassDirect_CURRENT_CODEHEADER) 17 | #error Recursive inclusion of FFTRealPassDirect code header. 18 | #endif 19 | #define ffft_FFTRealPassDirect_CURRENT_CODEHEADER 20 | 21 | #if !defined(ffft_FFTRealPassDirect_CODEHEADER_INCLUDED) 22 | #define ffft_FFTRealPassDirect_CODEHEADER_INCLUDED 23 | 24 | 25 | 26 | #include "ffft/FFTRealUseTrigo.h" 27 | 28 | namespace ffft { 29 | 30 | 31 | 32 | template <> 33 | inline void 34 | FFTRealPassDirect<1>::process(long len, DataType dest_ptr[], DataType src_ptr[], 35 | const DataType x_ptr[], const DataType cos_ptr[], 36 | long cos_len, const long br_ptr[], 37 | OscType osc_list[]) { 38 | // First and second pass at once 39 | const long qlen = len >> 2; 40 | 41 | long coef_index = 0; 42 | do { 43 | // To do: unroll the loop (2x). 44 | const long ri_0 = br_ptr[coef_index >> 2]; 45 | const long ri_1 = ri_0 + 2 * qlen; // bit_rev_lut_ptr [coef_index + 1]; 46 | const long ri_2 = ri_0 + 1 * qlen; // bit_rev_lut_ptr [coef_index + 2]; 47 | const long ri_3 = ri_0 + 3 * qlen; // bit_rev_lut_ptr [coef_index + 3]; 48 | 49 | DataType *const df2 = dest_ptr + coef_index; 50 | df2[1] = x_ptr[ri_0] - x_ptr[ri_1]; 51 | df2[3] = x_ptr[ri_2] - x_ptr[ri_3]; 52 | 53 | const DataType sf_0 = x_ptr[ri_0] + x_ptr[ri_1]; 54 | const DataType sf_2 = x_ptr[ri_2] + x_ptr[ri_3]; 55 | 56 | df2[0] = sf_0 + sf_2; 57 | df2[2] = sf_0 - sf_2; 58 | 59 | coef_index += 4; 60 | } while (coef_index < len); 61 | } 62 | 63 | template <> 64 | inline void 65 | FFTRealPassDirect<2>::process(long len, DataType dest_ptr[], DataType src_ptr[], 66 | const DataType x_ptr[], const DataType cos_ptr[], 67 | long cos_len, const long br_ptr[], 68 | OscType osc_list[]) { 69 | // Executes "previous" passes first. Inverts source and destination buffers 70 | FFTRealPassDirect<1>::process(len, src_ptr, dest_ptr, x_ptr, cos_ptr, cos_len, 71 | br_ptr, osc_list); 72 | 73 | // Third pass 74 | const DataType sqrt2_2 = DataType(SQRT2 * 0.5); 75 | 76 | long coef_index = 0; 77 | do { 78 | dest_ptr[coef_index] = src_ptr[coef_index] + src_ptr[coef_index + 4]; 79 | dest_ptr[coef_index + 4] = src_ptr[coef_index] - src_ptr[coef_index + 4]; 80 | dest_ptr[coef_index + 2] = src_ptr[coef_index + 2]; 81 | dest_ptr[coef_index + 6] = src_ptr[coef_index + 6]; 82 | 83 | DataType v; 84 | 85 | v = (src_ptr[coef_index + 5] - src_ptr[coef_index + 7]) * sqrt2_2; 86 | dest_ptr[coef_index + 1] = src_ptr[coef_index + 1] + v; 87 | dest_ptr[coef_index + 3] = src_ptr[coef_index + 1] - v; 88 | 89 | v = (src_ptr[coef_index + 5] + src_ptr[coef_index + 7]) * sqrt2_2; 90 | dest_ptr[coef_index + 5] = v + src_ptr[coef_index + 3]; 91 | dest_ptr[coef_index + 7] = v - src_ptr[coef_index + 3]; 92 | 93 | coef_index += 8; 94 | } while (coef_index < len); 95 | } 96 | 97 | template 98 | void FFTRealPassDirect::process(long len, DataType dest_ptr[], 99 | DataType src_ptr[], 100 | const DataType x_ptr[], 101 | const DataType cos_ptr[], long cos_len, 102 | const long br_ptr[], OscType osc_list[]) { 103 | // Executes "previous" passes first. Inverts source and destination buffers 104 | FFTRealPassDirect::process(len, src_ptr, dest_ptr, x_ptr, cos_ptr, 105 | cos_len, br_ptr, osc_list); 106 | 107 | const long dist = 1L << (PASS - 1); 108 | const long c1_r = 0; 109 | const long c1_i = dist; 110 | const long c2_r = dist * 2; 111 | const long c2_i = dist * 3; 112 | const long cend = dist * 4; 113 | const long table_step = cos_len >> (PASS - 1); 114 | 115 | enum { TRIGO_OSC = PASS - FFTRealFixLenParam::TRIGO_BD_LIMIT }; 116 | enum { TRIGO_DIRECT = (TRIGO_OSC >= 0) ? 1 : 0 }; 117 | 118 | long coef_index = 0; 119 | do { 120 | const DataType *const sf = src_ptr + coef_index; 121 | DataType *const df = dest_ptr + coef_index; 122 | 123 | // Extreme coefficients are always real 124 | df[c1_r] = sf[c1_r] + sf[c2_r]; 125 | df[c2_r] = sf[c1_r] - sf[c2_r]; 126 | df[c1_i] = sf[c1_i]; 127 | df[c2_i] = sf[c2_i]; 128 | 129 | FFTRealUseTrigo::prepare(osc_list[TRIGO_OSC]); 130 | 131 | // Others are conjugate complex numbers 132 | for (long i = 1; i < dist; ++i) { 133 | DataType c; 134 | DataType s; 135 | FFTRealUseTrigo::iterate(osc_list[TRIGO_OSC], c, s, cos_ptr, 136 | i * table_step, 137 | (dist - i) * table_step); 138 | 139 | const DataType sf_r_i = sf[c1_r + i]; 140 | const DataType sf_i_i = sf[c1_i + i]; 141 | 142 | const DataType v1 = sf[c2_r + i] * c - sf[c2_i + i] * s; 143 | df[c1_r + i] = sf_r_i + v1; 144 | df[c2_r - i] = sf_r_i - v1; 145 | 146 | const DataType v2 = sf[c2_r + i] * s + sf[c2_i + i] * c; 147 | df[c2_r + i] = v2 + sf_i_i; 148 | df[cend - i] = v2 - sf_i_i; 149 | } 150 | 151 | coef_index += cend; 152 | } while (coef_index < len); 153 | } 154 | 155 | 156 | 157 | 158 | 159 | } 160 | 161 | #endif 162 | 163 | #undef ffft_FFTRealPassDirect_CURRENT_CODEHEADER 164 | 165 | 166 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/include/FFTReal/FFTRealPassInverse.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | FFTRealPassInverse.h 4 | By Laurent de Soras 5 | 6 | --- Legal stuff --- 7 | 8 | This program is free software. It comes without any warranty, to 9 | the extent permitted by applicable law. You can redistribute it 10 | and/or modify it under the terms of the Do What The Fuck You Want 11 | To Public License, Version 2, as published by Sam Hocevar. See 12 | http://sam.zoy.org/wtfpl/COPYING for more details. 13 | 14 | *Tab=3***********************************************************************/ 15 | 16 | #if defined(ffft_FFTRealPassInverse_CURRENT_CODEHEADER) 17 | #error Recursive inclusion of FFTRealPassInverse code header. 18 | #endif 19 | #define ffft_FFTRealPassInverse_CURRENT_CODEHEADER 20 | 21 | #if !defined(ffft_FFTRealPassInverse_CODEHEADER_INCLUDED) 22 | #define ffft_FFTRealPassInverse_CODEHEADER_INCLUDED 23 | 24 | 25 | 26 | #include "ffft/FFTRealUseTrigo.h" 27 | 28 | namespace ffft { 29 | 30 | 31 | 32 | template 33 | void FFTRealPassInverse::process(long len, DataType dest_ptr[], 34 | DataType src_ptr[], 35 | const DataType f_ptr[], 36 | const DataType cos_ptr[], long cos_len, 37 | const long br_ptr[], 38 | OscType osc_list[]) { 39 | process_internal(len, dest_ptr, f_ptr, cos_ptr, cos_len, br_ptr, osc_list); 40 | FFTRealPassInverse::process_rec(len, src_ptr, dest_ptr, cos_ptr, 41 | cos_len, br_ptr, osc_list); 42 | } 43 | 44 | template 45 | void FFTRealPassInverse::process_rec(long len, DataType dest_ptr[], 46 | DataType src_ptr[], 47 | const DataType cos_ptr[], 48 | long cos_len, const long br_ptr[], 49 | OscType osc_list[]) { 50 | process_internal(len, dest_ptr, src_ptr, cos_ptr, cos_len, br_ptr, osc_list); 51 | FFTRealPassInverse::process_rec(len, src_ptr, dest_ptr, cos_ptr, 52 | cos_len, br_ptr, osc_list); 53 | } 54 | 55 | template <> 56 | inline void FFTRealPassInverse<0>::process_rec( 57 | long len, DataType dest_ptr[], DataType src_ptr[], const DataType cos_ptr[], 58 | long cos_len, const long br_ptr[], OscType osc_list[]) { 59 | // Stops recursion 60 | } 61 | 62 | template 63 | void FFTRealPassInverse::process_internal(long len, DataType dest_ptr[], 64 | const DataType src_ptr[], 65 | const DataType cos_ptr[], 66 | long cos_len, 67 | const long br_ptr[], 68 | OscType osc_list[]) { 69 | const long dist = 1L << (PASS - 1); 70 | const long c1_r = 0; 71 | const long c1_i = dist; 72 | const long c2_r = dist * 2; 73 | const long c2_i = dist * 3; 74 | const long cend = dist * 4; 75 | const long table_step = cos_len >> (PASS - 1); 76 | 77 | enum { TRIGO_OSC = PASS - FFTRealFixLenParam::TRIGO_BD_LIMIT }; 78 | enum { TRIGO_DIRECT = (TRIGO_OSC >= 0) ? 1 : 0 }; 79 | 80 | long coef_index = 0; 81 | do { 82 | const DataType *const sf = src_ptr + coef_index; 83 | DataType *const df = dest_ptr + coef_index; 84 | 85 | // Extreme coefficients are always real 86 | df[c1_r] = sf[c1_r] + sf[c2_r]; 87 | df[c2_r] = sf[c1_r] - sf[c2_r]; 88 | df[c1_i] = sf[c1_i] * 2; 89 | df[c2_i] = sf[c2_i] * 2; 90 | 91 | FFTRealUseTrigo::prepare(osc_list[TRIGO_OSC]); 92 | 93 | // Others are conjugate complex numbers 94 | for (long i = 1; i < dist; ++i) { 95 | df[c1_r + i] = sf[c1_r + i] + sf[c2_r - i]; 96 | df[c1_i + i] = sf[c2_r + i] - sf[cend - i]; 97 | 98 | DataType c; 99 | DataType s; 100 | FFTRealUseTrigo::iterate(osc_list[TRIGO_OSC], c, s, cos_ptr, 101 | i * table_step, 102 | (dist - i) * table_step); 103 | 104 | const DataType vr = sf[c1_r + i] - sf[c2_r - i]; 105 | const DataType vi = sf[c2_r + i] + sf[cend - i]; 106 | 107 | df[c2_r + i] = vr * c + vi * s; 108 | df[c2_i + i] = vi * c - vr * s; 109 | } 110 | 111 | coef_index += cend; 112 | } while (coef_index < len); 113 | } 114 | 115 | template <> 116 | inline void FFTRealPassInverse<2>::process_internal( 117 | long len, DataType dest_ptr[], const DataType src_ptr[], 118 | const DataType cos_ptr[], long cos_len, const long br_ptr[], 119 | OscType osc_list[]) { 120 | // Antepenultimate pass 121 | const DataType sqrt2_2 = DataType(SQRT2 * 0.5); 122 | 123 | long coef_index = 0; 124 | do { 125 | dest_ptr[coef_index] = src_ptr[coef_index] + src_ptr[coef_index + 4]; 126 | dest_ptr[coef_index + 4] = src_ptr[coef_index] - src_ptr[coef_index + 4]; 127 | dest_ptr[coef_index + 2] = src_ptr[coef_index + 2] * 2; 128 | dest_ptr[coef_index + 6] = src_ptr[coef_index + 6] * 2; 129 | 130 | dest_ptr[coef_index + 1] = 131 | src_ptr[coef_index + 1] + src_ptr[coef_index + 3]; 132 | dest_ptr[coef_index + 3] = 133 | src_ptr[coef_index + 5] - src_ptr[coef_index + 7]; 134 | 135 | const DataType vr = src_ptr[coef_index + 1] - src_ptr[coef_index + 3]; 136 | const DataType vi = src_ptr[coef_index + 5] + src_ptr[coef_index + 7]; 137 | 138 | dest_ptr[coef_index + 5] = (vr + vi) * sqrt2_2; 139 | dest_ptr[coef_index + 7] = (vi - vr) * sqrt2_2; 140 | 141 | coef_index += 8; 142 | } while (coef_index < len); 143 | } 144 | 145 | template <> 146 | inline void FFTRealPassInverse<1>::process_internal( 147 | long len, DataType dest_ptr[], const DataType src_ptr[], 148 | const DataType cos_ptr[], long cos_len, const long br_ptr[], 149 | OscType osc_list[]) { 150 | // Penultimate and last pass at once 151 | const long qlen = len >> 2; 152 | 153 | long coef_index = 0; 154 | do { 155 | const long ri_0 = br_ptr[coef_index >> 2]; 156 | 157 | const DataType b_0 = src_ptr[coef_index] + src_ptr[coef_index + 2]; 158 | const DataType b_2 = src_ptr[coef_index] - src_ptr[coef_index + 2]; 159 | const DataType b_1 = src_ptr[coef_index + 1] * 2; 160 | const DataType b_3 = src_ptr[coef_index + 3] * 2; 161 | 162 | dest_ptr[ri_0] = b_0 + b_1; 163 | dest_ptr[ri_0 + 2 * qlen] = b_0 - b_1; 164 | dest_ptr[ri_0 + 1 * qlen] = b_2 + b_3; 165 | dest_ptr[ri_0 + 3 * qlen] = b_2 - b_3; 166 | 167 | coef_index += 4; 168 | } while (coef_index < len); 169 | } 170 | 171 | 172 | 173 | 174 | 175 | } 176 | 177 | #endif 178 | 179 | #undef ffft_FFTRealPassInverse_CURRENT_CODEHEADER 180 | 181 | 182 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/include/FFTReal/FFTRealSelect.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | FFTRealSelect.h 4 | By Laurent de Soras 5 | 6 | --- Legal stuff --- 7 | 8 | This program is free software. It comes without any warranty, to 9 | the extent permitted by applicable law. You can redistribute it 10 | and/or modify it under the terms of the Do What The Fuck You Want 11 | To Public License, Version 2, as published by Sam Hocevar. See 12 | http://sam.zoy.org/wtfpl/COPYING for more details. 13 | 14 | *Tab=3***********************************************************************/ 15 | 16 | #if defined(ffft_FFTRealSelect_CURRENT_CODEHEADER) 17 | #error Recursive inclusion of FFTRealSelect code header. 18 | #endif 19 | #define ffft_FFTRealSelect_CURRENT_CODEHEADER 20 | 21 | #if !defined(ffft_FFTRealSelect_CODEHEADER_INCLUDED) 22 | #define ffft_FFTRealSelect_CODEHEADER_INCLUDED 23 | 24 | namespace ffft { 25 | 26 | 27 | 28 | template float *FFTRealSelect

::sel_bin(float *e_ptr, float *o_ptr) { 29 | return (o_ptr); 30 | } 31 | 32 | template <> 33 | inline float *FFTRealSelect<0>::sel_bin(float *e_ptr, float *o_ptr) { 34 | return (e_ptr); 35 | } 36 | 37 | } 38 | 39 | #endif 40 | 41 | #undef ffft_FFTRealSelect_CURRENT_CODEHEADER 42 | 43 | 44 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/include/FFTReal/FFTRealUseTrigo.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | FFTRealUseTrigo.h 4 | By Laurent de Soras 5 | 6 | --- Legal stuff --- 7 | 8 | This program is free software. It comes without any warranty, to 9 | the extent permitted by applicable law. You can redistribute it 10 | and/or modify it under the terms of the Do What The Fuck You Want 11 | To Public License, Version 2, as published by Sam Hocevar. See 12 | http://sam.zoy.org/wtfpl/COPYING for more details. 13 | 14 | *Tab=3***********************************************************************/ 15 | 16 | #if defined(ffft_FFTRealUseTrigo_CURRENT_CODEHEADER) 17 | #error Recursive inclusion of FFTRealUseTrigo code header. 18 | #endif 19 | #define ffft_FFTRealUseTrigo_CURRENT_CODEHEADER 20 | 21 | #if !defined(ffft_FFTRealUseTrigo_CODEHEADER_INCLUDED) 22 | #define ffft_FFTRealUseTrigo_CODEHEADER_INCLUDED 23 | 24 | 25 | 26 | #include "ffft/OscSinCos.h" 27 | 28 | namespace ffft { 29 | 30 | 31 | 32 | template void FFTRealUseTrigo::prepare(OscType &osc) { 33 | osc.clear_buffers(); 34 | } 35 | 36 | template <> inline void FFTRealUseTrigo<0>::prepare(OscType &osc) { 37 | 38 | } 39 | 40 | template 41 | void FFTRealUseTrigo::iterate(OscType &osc, DataType &c, DataType &s, 42 | const DataType cos_ptr[], long index_c, 43 | long index_s) { 44 | osc.step(); 45 | c = osc.get_cos(); 46 | s = osc.get_sin(); 47 | } 48 | 49 | template <> 50 | inline void FFTRealUseTrigo<0>::iterate(OscType &osc, DataType &c, DataType &s, 51 | const DataType cos_ptr[], long index_c, 52 | long index_s) { 53 | c = cos_ptr[index_c]; 54 | s = cos_ptr[index_s]; 55 | } 56 | 57 | 58 | 59 | 60 | 61 | } 62 | 63 | #endif 64 | 65 | #undef ffft_FFTRealUseTrigo_CURRENT_CODEHEADER 66 | 67 | 68 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/include/LPFCombFilter.h: -------------------------------------------------------------------------------- 1 | /* 2 | CLPFCombFilter: implements a comb filter of length D with 3 | feedback gain m_fComb_g and LPF gain 4 | m_fLPF_g 5 | 6 | Can be used alone or as a base class. 7 | 8 | 9 | 10 | */ 11 | 12 | // Inherited Base Class functions: 13 | /* 14 | void init(int nDelayLength); 15 | void resetDelay(); 16 | void setDelay_mSec(float fmSec); 17 | void setOutputAttenuation_dB(float fAttendB); 18 | 19 | // NEED TO OVERRIDE 20 | bool processAudio(float *pInput, float *pOutput); 21 | */ 22 | #pragma once 23 | #include "CDelay.h" 24 | 25 | // derived from CDelay 26 | class CLPFCombFilter : public CDelay { 27 | public: 28 | // constructor/destructor 29 | CLPFCombFilter(); 30 | ~CLPFCombFilter(); 31 | 32 | // members 33 | protected: 34 | float m_fComb_g; // one Comb coefficient 35 | float m_fLPF_g; // one LPF coefficient 36 | float m_fLPF_z1; // one sample delay 37 | 38 | public: 39 | // set our g value directly 40 | void setComb_g(float fCombg) { m_fComb_g = fCombg; } 41 | 42 | // set our g value using RT60 43 | void setComb_g_with_RTSixty(float fRT) { 44 | float fExponent = -3.0f * m_fDelayInSamples * (1.0f / m_nSampleRate); 45 | fRT /= 1000.0; // RT is in mSec! 46 | 47 | m_fComb_g = powf((float)10.0, fExponent / fRT); 48 | } 49 | 50 | // set the LPF gain 51 | // NOTE: call setComb_g_with_RTSixty FIRST, then this 52 | void setLPF_g(float fOverAllGain) { 53 | // g2 = g*(1-g1) 54 | m_fLPF_g = (float)fOverAllGain * (1.0f - m_fComb_g); 55 | } 56 | 57 | // overrides 58 | // init 59 | void init(int nDelayLength); 60 | 61 | // process something 62 | bool processAudio(float *pInput, float *pOutput); 63 | }; 64 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/include/MikeFilter.h: -------------------------------------------------------------------------------- 1 | // 2 | // MikeFilter.h 3 | // 4 | // Created by Mike Gazzaruso, revision history on Github. 5 | // 6 | // 7 | 8 | #pragma once 9 | 10 | class MikeFilter { 11 | public: 12 | MikeFilter(); 13 | void calc_filter_coeffs(double const frequency, double const sample_rate); 14 | 15 | float filter(float in0); 16 | 17 | private: 18 | // filter coeffs 19 | float b0a0, b1a0, b2a0, a1a0, a2a0; 20 | 21 | // in/out history 22 | float ou1, ou2, in1, in2; 23 | }; 24 | 25 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/include/OnePoleLPF.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class COnePoleLPF { 4 | public: 5 | // constructor/Destructor 6 | COnePoleLPF(); 7 | ~COnePoleLPF(); 8 | 9 | // members 10 | protected: 11 | float m_fLPF_g; // one gain coefficient 12 | float m_fLPF_z1; // one delay 13 | 14 | public: 15 | // set our one and only gain coefficient 16 | void setLPF_g(float fLPFg) { m_fLPF_g = fLPFg; } 17 | 18 | // function to init 19 | void init(); 20 | 21 | // function to process audio 22 | bool processAudio(float *pInput, float *pOutput); 23 | }; 24 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/include/OscSinCos.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | OscSinCos.h 4 | By Laurent de Soras 5 | 6 | --- Legal stuff --- 7 | 8 | This program is free software. It comes without any warranty, to 9 | the extent permitted by applicable law. You can redistribute it 10 | and/or modify it under the terms of the Do What The Fuck You Want 11 | To Public License, Version 2, as published by Sam Hocevar. See 12 | http://sam.zoy.org/wtfpl/COPYING for more details. 13 | 14 | *Tab=3***********************************************************************/ 15 | 16 | #if defined(ffft_OscSinCos_CURRENT_CODEHEADER) 17 | #error Recursive inclusion of OscSinCos code header. 18 | #endif 19 | #define ffft_OscSinCos_CURRENT_CODEHEADER 20 | 21 | #if !defined(ffft_OscSinCos_CODEHEADER_INCLUDED) 22 | #define ffft_OscSinCos_CODEHEADER_INCLUDED 23 | 24 | 25 | 26 | #include 27 | 28 | namespace std {} 29 | 30 | namespace ffft { 31 | 32 | 33 | 34 | template 35 | OscSinCos::OscSinCos() 36 | : _pos_cos(1), _pos_sin(0), _step_cos(1), _step_sin(0) { 37 | 38 | } 39 | 40 | template void OscSinCos::set_step(double angle_rad) { 41 | using namespace std; 42 | 43 | _step_cos = static_cast(cos(angle_rad)); 44 | _step_sin = static_cast(sin(angle_rad)); 45 | } 46 | 47 | template 48 | typename OscSinCos::DataType OscSinCos::get_cos() const { 49 | return (_pos_cos); 50 | } 51 | 52 | template 53 | typename OscSinCos::DataType OscSinCos::get_sin() const { 54 | return (_pos_sin); 55 | } 56 | 57 | template void OscSinCos::step() { 58 | const DataType old_cos = _pos_cos; 59 | const DataType old_sin = _pos_sin; 60 | 61 | _pos_cos = old_cos * _step_cos - old_sin * _step_sin; 62 | _pos_sin = old_cos * _step_sin + old_sin * _step_cos; 63 | } 64 | 65 | template void OscSinCos::clear_buffers() { 66 | _pos_cos = static_cast(1); 67 | _pos_sin = static_cast(0); 68 | } 69 | 70 | 71 | 72 | 73 | 74 | } 75 | 76 | #endif 77 | 78 | #undef ffft_OscSinCos_CURRENT_CODEHEADER 79 | 80 | 81 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/include/RageProcessor.h: -------------------------------------------------------------------------------- 1 | // 2 | // RageProcessor.h 3 | // 4 | // Created by Mike Gazzaruso, revision history on Github. 5 | // Copyright © 2015 Mike Gazzaruso. All rights reserved. 6 | // 7 | 8 | #pragma once 9 | #include "MikeFilter.h" 10 | 11 | class RageProcessor { 12 | public: 13 | RageProcessor(int iSampleRate); 14 | float doRage(float fCurrentSample, float fKhorne, float fNurgle); 15 | MikeFilter filterToneZ; 16 | void setNumStages(int theStages); 17 | 18 | private: 19 | int iSampleRate, iNumStages; 20 | }; 21 | 22 | 23 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/include/def.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | def.h 4 | By Laurent de Soras 5 | 6 | --- Legal stuff --- 7 | 8 | This program is free software. It comes without any warranty, to 9 | the extent permitted by applicable law. You can redistribute it 10 | and/or modify it under the terms of the Do What The Fuck You Want 11 | To Public License, Version 2, as published by Sam Hocevar. See 12 | http://sam.zoy.org/wtfpl/COPYING for more details. 13 | 14 | *Tab=3***********************************************************************/ 15 | 16 | #if !defined(ffft_def_HEADER_INCLUDED) 17 | #define ffft_def_HEADER_INCLUDED 18 | 19 | #if defined(_MSC_VER) 20 | #pragma once 21 | #pragma warning(4 : 4250) // "Inherits via dominance." 22 | #endif 23 | 24 | 25 | 26 | namespace ffft { 27 | 28 | const double PI = 3.1415926535897932384626433832795; 29 | const double SQRT2 = 1.41421356237309514547462185873883; 30 | 31 | #if defined(_MSC_VER) 32 | 33 | #define ffft_FORCEINLINE __forceinline 34 | 35 | #else 36 | 37 | #define ffft_FORCEINLINE inline 38 | 39 | #endif 40 | 41 | } 42 | 43 | #endif 44 | 45 | 46 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/include/pluginconstants.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // includes for the project 4 | 5 | // this #define enables the following constants form math.h 6 | #define _MATH_DEFINES_DEFINED 7 | /* 8 | #define M_E 2.71828182845904523536 9 | #define M_LOG2E 1.44269504088896340736 10 | #define M_LOG10E 0.434294481903251827651 11 | #define M_LN2 0.693147180559945309417 12 | #define M_LN10 2.30258509299404568402 13 | #define M_PI 3.14159265358979323846 14 | #define M_PI_2 1.57079632679489661923 15 | #define M_PI_4 0.785398163397448309616 16 | #define M_1_PI 0.318309886183790671538 17 | #define M_2_PI 0.636619772367581343076 18 | #define M_2_SQRTPI 1.12837916709551257390 19 | #define M_SQRT2 1.41421356237309504880 20 | #define M_SQRT1_2 0.707106781186547524401 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | // For WIN vs MacOS 28 | // XCode requires these be defined for compatibility 29 | 30 | typedef unsigned int UINT; 31 | typedef unsigned long DWORD; 32 | typedef unsigned char UCHAR; 33 | typedef unsigned char BYTE; 34 | 35 | const UINT DETECT_MODE_PEAK = 0; 36 | const UINT DETECT_MODE_MS = 1; 37 | const UINT DETECT_MODE_RMS = 2; 38 | const UINT DETECT_MODE_NONE = 3; 39 | 40 | // More #defines for MacOS/XCode 41 | #ifndef max 42 | #define max(a, b) (((a) > (b)) ? (a) : (b)) 43 | #endif 44 | #ifndef min 45 | #define min(a, b) (((a) < (b)) ? (a) : (b)) 46 | #endif 47 | 48 | #ifndef itoa 49 | #define itoa(value, string, radix) sprintf(string, "%d", value) 50 | #endif 51 | 52 | #ifndef ltoa 53 | #define ltoa(value, string, radix) sprintf(string, "%u", value) 54 | #endif 55 | 56 | // a few more constants from student suggestions 57 | #define pi 3.1415926535897932384626433832795 58 | #define sqrt2over2 0.707106781186547524401 // same as M_SQRT1_2 59 | 60 | // constants for dealing with overflow or underflow 61 | #define FLT_EPSILON_PLUS \ 62 | 1.192092896e-07 /* smallest such that 1.0+FLT_EPSILON != 1.0 */ 63 | #define FLT_EPSILON_MINUS \ 64 | -1.192092896e-07 /* smallest such that 1.0-FLT_EPSILON != 1.0 */ 65 | #define FLT_MIN_PLUS 1.175494351e-38 /* min positive value */ 66 | #define FLT_MIN_MINUS -1.175494351e-38 /* min negative value */ 67 | 68 | // basic enums 69 | enum { intData, floatData, doubleData, UINTData, nonData }; 70 | enum { JS_ONESHOT, JS_LOOP, JS_SUSTAIN, JS_LOOP_BACKANDFORTH }; 71 | 72 | // © by Mike Gazzaruso, 2014 73 | 74 | /* 75 | Function: lagrpol() implements n-order Lagrange Interpolation 76 | 77 | Inputs: double* x Pointer to an array containing 78 | the x-coordinates of the input values double* y Pointer to an array 79 | containing the y-coordinates of the input values 80 | int n The order of the 81 | interpolator, this is also the length of the x,y input arrays double xbar 82 | The x-coorinates whose y-value we want to interpolate 83 | 84 | Returns The interpolated value y at xbar. xbar ideally is 85 | between the middle two values in the input array, but can be anywhere within 86 | the limits, which is needed for interpolating the first few or last few 87 | samples in a table with a fixed size. 88 | */ 89 | inline double lagrpol(double *x, double *y, int n, double xbar) { 90 | int i, j; 91 | double fx = 0.0; 92 | double l = 1.0; 93 | for (i = 0; i < n; i++) { 94 | l = 1.0; 95 | for (j = 0; j < n; j++) { 96 | if (j != i) 97 | l *= (xbar - x[j]) / (x[i] - x[j]); 98 | } 99 | fx += l * y[i]; 100 | } 101 | return (fx); 102 | } 103 | 104 | inline float dLinTerp(float x1, float x2, float y1, float y2, float x) { 105 | float denom = x2 - x1; 106 | if (denom == 0) 107 | return y1; // should not ever happen 108 | 109 | // calculate decimal position of x 110 | float dx = (x - x1) / (x2 - x1); 111 | 112 | // use weighted sum method of interpolating 113 | float result = dx * y2 + (1 - dx) * y1; 114 | 115 | return result; 116 | } 117 | 118 | inline bool normalizeBuffer(double *pInputBuffer, UINT uBufferSize) { 119 | double fMax = 0; 120 | 121 | for (UINT j = 0; j < uBufferSize; j++) { 122 | if ((fabs(pInputBuffer[j])) > fMax) 123 | fMax = fabs(pInputBuffer[j]); 124 | } 125 | 126 | if (fMax > 0) { 127 | for (UINT j = 0; j < uBufferSize; j++) 128 | pInputBuffer[j] = pInputBuffer[j] / fMax; 129 | } 130 | 131 | return true; 132 | } 133 | 134 | const float DIGITAL_TC = -2.0; // log(1%) 135 | const float ANALOG_TC = -0.43533393574791066201247090699309f; // (log(36.7%) 136 | const float METER_UPDATE_INTERVAL_MSEC = 15.0; 137 | const float METER_MIN_DB = -60.0; 138 | 139 | class CEnvelopeDetector { 140 | public: 141 | CEnvelopeDetector(double samplerate); 142 | ~CEnvelopeDetector(); 143 | 144 | // Call the Init Function to initialize and setup all at once; this can be 145 | // called as many times as you want 146 | void init(float samplerate, float attack_in_ms, float release_in_ms, 147 | bool bAnalogTC, UINT uDetect, bool bLogDetector); 148 | 149 | // these functions allow you to change modes and attack/release one at a time 150 | // during realtime operation 151 | void setTCModeAnalog(bool bAnalogTC); // {m_bAnalogTC = bAnalogTC;} 152 | 153 | // THEN do these after init 154 | void setAttackDuration(float attack_in_ms); 155 | void setReleaseDuration(float release_in_ms); 156 | 157 | // Use these "codes" 158 | // DETECT PEAK = 0 159 | // DETECT MS = 1 160 | // DETECT RMS = 2 161 | // 162 | void setDetectMode(UINT uDetect) { m_uDetectMode = uDetect; } 163 | 164 | void setSampleRate(float f) { m_fSampleRate = f; } 165 | 166 | void setLogDetect(bool b) { m_bLogDetector = b; } 167 | 168 | // call this to detect; it returns the peak ms or rms value at that instant 169 | float detect(float fInput); 170 | 171 | // call this from your prepareForPlay() function each time to reset the 172 | // detector 173 | void prepareForPlay(); 174 | 175 | protected: 176 | int m_nSample; 177 | float m_fAttackTime; 178 | float m_fReleaseTime; 179 | float m_fAttackTime_mSec; 180 | float m_fReleaseTime_mSec; 181 | float m_fSampleRate; 182 | float m_fEnvelope; 183 | UINT m_uDetectMode; 184 | bool m_bAnalogTC; 185 | bool m_bLogDetector; 186 | }; 187 | -------------------------------------------------------------------------------- /Sources/CDevoloopAudioKit/pluginobjects.cpp: -------------------------------------------------------------------------------- 1 | #include "pluginconstants.h" 2 | 3 | // This file contains the object implementations for the objects declared in 4 | // "pluginconstants.h" 5 | // 6 | // Note about Helper Objects: DO NOT MODIFY THESE OBJECTS. If you need to do so, 7 | // create a derived class and modify it. These objects may be updated from time 8 | // to time so they need to be left alone. 9 | 10 | // CEnvelopeDetector Implementation 11 | // ---------------------------------------------------------------- 12 | // 13 | CEnvelopeDetector::CEnvelopeDetector(double samplerate) { 14 | m_fAttackTime_mSec = 0.0; 15 | m_fReleaseTime_mSec = 0.0; 16 | m_fAttackTime = 0.0; 17 | m_fReleaseTime = 0.0; 18 | m_fSampleRate = (float)samplerate; 19 | m_fEnvelope = 0.0; 20 | m_uDetectMode = 0; 21 | m_nSample = 0; 22 | m_bAnalogTC = false; 23 | m_bLogDetector = false; 24 | } 25 | 26 | CEnvelopeDetector::~CEnvelopeDetector() {} 27 | 28 | void CEnvelopeDetector::prepareForPlay() { 29 | m_fEnvelope = 0.0; 30 | m_nSample = 0; 31 | } 32 | 33 | void CEnvelopeDetector::init(float samplerate, float attack_in_ms, 34 | float release_in_ms, bool bAnalogTC, UINT uDetect, 35 | bool bLogDetector) { 36 | m_fEnvelope = 0.0; 37 | m_fSampleRate = samplerate; 38 | m_bAnalogTC = bAnalogTC; 39 | m_fAttackTime_mSec = attack_in_ms; 40 | m_fReleaseTime_mSec = release_in_ms; 41 | m_uDetectMode = uDetect; 42 | m_bLogDetector = bLogDetector; 43 | 44 | // set themm_uDetectMode = uDetect; 45 | setAttackDuration(attack_in_ms); 46 | setReleaseDuration(release_in_ms); 47 | } 48 | 49 | void CEnvelopeDetector::setAttackDuration(float attack_in_ms) { 50 | m_fAttackTime_mSec = attack_in_ms; 51 | 52 | if (m_bAnalogTC) 53 | m_fAttackTime = expf(ANALOG_TC / (attack_in_ms * m_fSampleRate * 0.001f)); 54 | else 55 | m_fAttackTime = expf(DIGITAL_TC / (attack_in_ms * m_fSampleRate * 0.001f)); 56 | } 57 | 58 | void CEnvelopeDetector::setReleaseDuration(float release_in_ms) { 59 | m_fReleaseTime_mSec = release_in_ms; 60 | 61 | if (m_bAnalogTC) 62 | m_fReleaseTime = expf(ANALOG_TC / (release_in_ms * m_fSampleRate * 0.001f)); 63 | else 64 | m_fReleaseTime = 65 | expf(DIGITAL_TC / (release_in_ms * m_fSampleRate * 0.001f)); 66 | } 67 | 68 | void CEnvelopeDetector::setTCModeAnalog(bool bAnalogTC) { 69 | m_bAnalogTC = bAnalogTC; 70 | setAttackDuration(m_fAttackTime_mSec); 71 | setReleaseDuration(m_fReleaseTime_mSec); 72 | } 73 | 74 | float CEnvelopeDetector::detect(float fInput) { 75 | switch (m_uDetectMode) { 76 | case 0: 77 | fInput = fabsf(fInput); 78 | break; 79 | case 1: 80 | fInput = fabsf(fInput) * fabsf(fInput); 81 | break; 82 | case 2: 83 | fInput = powf((float)fabsf(fInput) * (float)fabsf(fInput), (float)0.5); 84 | break; 85 | default: 86 | fInput = (float)fabsf(fInput); 87 | break; 88 | } 89 | 90 | // float fOld = m_fEnvelope; 91 | if (fInput > m_fEnvelope) 92 | m_fEnvelope = m_fAttackTime * (m_fEnvelope - fInput) + fInput; 93 | else 94 | m_fEnvelope = m_fReleaseTime * (m_fEnvelope - fInput) + fInput; 95 | 96 | if (m_fEnvelope > 0.0 && m_fEnvelope < FLT_MIN_PLUS) 97 | m_fEnvelope = 0; 98 | if (m_fEnvelope < 0.0 && m_fEnvelope > FLT_MIN_MINUS) 99 | m_fEnvelope = 0; 100 | 101 | // bound them; can happen when using pre-detector gains of more than 1.0 102 | m_fEnvelope = min(m_fEnvelope, 1.0); 103 | m_fEnvelope = max(m_fEnvelope, 0.0); 104 | 105 | // 16-bit scaling! 106 | if (m_bLogDetector) { 107 | if (m_fEnvelope <= 0) 108 | return -96.0; // 16 bit noise floor 109 | 110 | return 20.0f * log10f(m_fEnvelope); 111 | } 112 | 113 | return m_fEnvelope; 114 | } 115 | -------------------------------------------------------------------------------- /Sources/DevoloopAudioKit/DevoloopAudioKit.swift: -------------------------------------------------------------------------------- 1 | struct DevoloopAudioKit { 2 | var text = "Hello, World!" 3 | } 4 | -------------------------------------------------------------------------------- /Sources/DevoloopAudioKit/DynaRageCompressor.swift: -------------------------------------------------------------------------------- 1 | // Copyright AudioKit. All Rights Reserved. 2 | 3 | import AudioKit 4 | import AudioKitEX 5 | import AVFoundation 6 | import CAudioKitEX 7 | 8 | /// DynaRage Tube Compressor | Based on DynaRage Tube Compressor RE for Reason 9 | /// by Devoloop Srls 10 | /// 11 | public class DynaRageCompressor: Node { 12 | let input: Node 13 | 14 | /// Connected nodes 15 | public var connections: [Node] { [input] } 16 | 17 | /// Underlying AVAudioNode 18 | public var avAudioNode = instantiate(effect: "dyrc") 19 | 20 | // MARK: - Parameters 21 | 22 | /// Specification details for ratio 23 | public static let ratioDef = NodeParameterDef( 24 | identifier: "ratio", 25 | name: "Ratio to compress with, a value > 1 will compress", 26 | address: akGetParameterAddress("DynaRageCompressorParameterRatio"), 27 | defaultValue: 1, 28 | range: 1.0 ... 20.0, 29 | unit: .generic 30 | ) 31 | 32 | /// Ratio to compress with, a value > 1 will compress 33 | @Parameter(ratioDef) public var ratio: AUValue 34 | 35 | /// Specification details for threshold 36 | public static let thresholdDef = NodeParameterDef( 37 | identifier: "threshold", 38 | name: "Threshold (in dB) 0 = max", 39 | address: akGetParameterAddress("DynaRageCompressorParameterThreshold"), 40 | defaultValue: 0.0, 41 | range: -100.0 ... 0.0, 42 | unit: .decibels 43 | ) 44 | 45 | /// Threshold (in dB) 0 = max 46 | @Parameter(thresholdDef) public var threshold: AUValue 47 | 48 | /// Specification details for attack duration 49 | public static let attackDurationDef = NodeParameterDef( 50 | identifier: "attackDuration", 51 | name: "Attack Duration", 52 | address: akGetParameterAddress("DynaRageCompressorParameterAttackDuration"), 53 | defaultValue: 0.1, 54 | range: 0.1 ... 500.0, 55 | unit: .seconds 56 | ) 57 | 58 | /// Attack dration 59 | @Parameter(attackDurationDef) public var attackDuration: AUValue 60 | 61 | /// Specification details for release duration 62 | public static let releaseDurationDef = NodeParameterDef( 63 | identifier: "releaseDuration", 64 | name: "Release Duration", 65 | address: akGetParameterAddress("DynaRageCompressorParameterReleaseDuration"), 66 | defaultValue: 0.1, 67 | range: 1.0 ... 20.0, 68 | unit: .seconds 69 | ) 70 | 71 | /// Release duration 72 | @Parameter(releaseDurationDef) public var releaseDuration: AUValue 73 | 74 | /// Specification details for rage amount 75 | public static let rageDef = NodeParameterDef( 76 | identifier: "rage", 77 | name: "Rage", 78 | address: akGetParameterAddress("DynaRageCompressorParameterRage"), 79 | defaultValue: 0.1, 80 | range: 0.1 ... 20.0, 81 | unit: .generic 82 | ) 83 | 84 | /// Rage Amount 85 | @Parameter(rageDef) public var rage: AUValue 86 | 87 | /// Specification details for range enabling 88 | public static let rageEnabledDef = NodeParameterDef( 89 | identifier: "rageEnabled", 90 | name: "Rage Enabled", 91 | address: akGetParameterAddress("DynaRageCompressorParameterRageEnabled"), 92 | defaultValue: 1.0, 93 | range: 0.0 ... 1.0, 94 | unit: .boolean 95 | ) 96 | 97 | /// Rage ON/OFF Switch 98 | @Parameter(rageEnabledDef) public var rageEnabled: Bool 99 | 100 | // MARK: - Initialization 101 | 102 | /// Initialize this compressor node 103 | /// 104 | /// - Parameters: 105 | /// - input: Input node to process 106 | /// - ratio: Ratio to compress with, a value > 1 will compress 107 | /// - threshold: Threshold (in dB) 0 = max 108 | /// - attackDuration: Attack duration in seconds 109 | /// - releaseDuration: Release duration in seconds 110 | /// 111 | public init( 112 | _ input: Node, 113 | ratio: AUValue = ratioDef.defaultValue, 114 | threshold: AUValue = thresholdDef.defaultValue, 115 | attackDuration: AUValue = attackDurationDef.defaultValue, 116 | releaseDuration: AUValue = releaseDurationDef.defaultValue, 117 | rage: AUValue = rageDef.defaultValue, 118 | rageEnabled: Bool = rageEnabledDef.defaultValue == 1.0 119 | ) { 120 | self.input = input 121 | 122 | setupParameters() 123 | 124 | self.ratio = ratio 125 | self.threshold = threshold 126 | self.attackDuration = attackDuration 127 | self.releaseDuration = releaseDuration 128 | self.rage = rage 129 | self.rageEnabled = rageEnabled 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /Sources/DevoloopAudioKit/RhinoGuitarProcessor.swift: -------------------------------------------------------------------------------- 1 | // Copyright AudioKit. All Rights Reserved. 2 | 3 | import AudioKit 4 | import AudioKitEX 5 | import AVFoundation 6 | import CAudioKitEX 7 | 8 | /// Guitar head and cab simulator. 9 | /// 10 | public class RhinoGuitarProcessor: Node { 11 | let input: Node 12 | 13 | /// Connected nodes 14 | public var connections: [Node] { [input] } 15 | 16 | /// Underlying AVAudioNode 17 | public var avAudioNode = instantiate(effect: "rhgp") 18 | 19 | // MARK: - Parameters 20 | 21 | /// Specification details for pre gain 22 | public static let preGainDef = NodeParameterDef( 23 | identifier: "preGain", 24 | name: "PreGain", 25 | address: akGetParameterAddress("RhinoGuitarProcessorParameterPreGain"), 26 | defaultValue: 5.0, 27 | range: 0.0 ... 10.0, 28 | unit: .generic 29 | ) 30 | 31 | /// Gain applied before processing. 32 | @Parameter(preGainDef) public var preGain: AUValue 33 | 34 | /// Specification details for post gain 35 | public static let postGainDef = NodeParameterDef( 36 | identifier: "postGain", 37 | name: "PostGain", 38 | address: akGetParameterAddress("RhinoGuitarProcessorParameterPostGain"), 39 | defaultValue: 0.7, 40 | range: 0.0 ... 1.0, 41 | unit: .linearGain 42 | ) 43 | 44 | /// Gain applied after processing. 45 | @Parameter(postGainDef) public var postGain: AUValue 46 | 47 | /// Specification details for low gain 48 | public static let lowGainDef = NodeParameterDef( 49 | identifier: "lowGain", 50 | name: "Low Frequency Gain", 51 | address: akGetParameterAddress("RhinoGuitarProcessorParameterLowGain"), 52 | defaultValue: 0.0, 53 | range: -1.0 ... 1.0, 54 | unit: .generic 55 | ) 56 | 57 | /// Amount of Low frequencies. 58 | @Parameter(lowGainDef) public var lowGain: AUValue 59 | 60 | /// Specification details for mid gain 61 | public static let midGainDef = NodeParameterDef( 62 | identifier: "midGain", 63 | name: "Mid Frequency Gain", 64 | address: akGetParameterAddress("RhinoGuitarProcessorParameterMidGain"), 65 | defaultValue: 0.0, 66 | range: -1.0 ... 1.0, 67 | unit: .generic 68 | ) 69 | 70 | /// Amount of Middle frequencies. 71 | @Parameter(midGainDef) public var midGain: AUValue 72 | 73 | /// Specification details for high gain 74 | public static let highGainDef = NodeParameterDef( 75 | identifier: "highGain", 76 | name: "High Frequency Gain", 77 | address: akGetParameterAddress("RhinoGuitarProcessorParameterHighGain"), 78 | defaultValue: 0.0, 79 | range: -1.0 ... 1.0, 80 | unit: .generic 81 | ) 82 | 83 | /// Amount of High frequencies. 84 | @Parameter(highGainDef) public var highGain: AUValue 85 | 86 | /// Specification details for distortion 87 | public static let distortionDef = NodeParameterDef( 88 | identifier: "distortion", 89 | name: "Distortion", 90 | address: akGetParameterAddress("RhinoGuitarProcessorParameterDistortion"), 91 | defaultValue: 1.0, 92 | range: 1.0 ... 20.0, 93 | unit: .generic 94 | ) 95 | 96 | /// Distortion Amount 97 | @Parameter(distortionDef) public var distortion: AUValue 98 | 99 | // MARK: - Initialization 100 | 101 | /// Initialize this Rhino head and cab simulator node 102 | /// 103 | /// - Parameters: 104 | /// - input: Input node to process 105 | /// - preGain: Determines the amount of gain applied to the signal before processing. 106 | /// - postGain: Gain applied after processing. 107 | /// - lowGain: Amount of Low frequencies. 108 | /// - midGain: Amount of Middle frequencies. 109 | /// - highGain: Amount of High frequencies. 110 | /// - distortion: Distortion Amount 111 | /// 112 | public init( 113 | _ input: Node, 114 | preGain: AUValue = preGainDef.defaultValue, 115 | postGain: AUValue = postGainDef.defaultValue, 116 | lowGain: AUValue = lowGainDef.defaultValue, 117 | midGain: AUValue = midGainDef.defaultValue, 118 | highGain: AUValue = highGainDef.defaultValue, 119 | distortion: AUValue = distortionDef.defaultValue 120 | ) { 121 | self.input = input 122 | 123 | setupParameters() 124 | 125 | self.preGain = preGain 126 | self.postGain = postGain 127 | self.lowGain = lowGain 128 | self.midGain = midGain 129 | self.highGain = highGain 130 | self.distortion = distortion 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /Tests/DevoloopAudioKitTests/GenericNodeTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright AudioKit. All Rights Reserved. 2 | 3 | import AudioKit 4 | import AVFoundation 5 | import DevoloopAudioKit 6 | import XCTest 7 | 8 | class GenericNodeTests: XCTestCase { 9 | func nodeParameterTest(md5: String, factory: (Node) -> Node, m1MD5: String = "", audition: Bool = false) { 10 | let url = Bundle.module.url(forResource: "12345", withExtension: "wav", subdirectory: "TestResources")! 11 | let player = AudioPlayer(url: url)! 12 | let node = factory(player) 13 | 14 | let duration = node.parameters.count + 1 15 | 16 | let engine = AudioEngine() 17 | var bigBuffer: AVAudioPCMBuffer? 18 | 19 | engine.output = node 20 | 21 | /// Do the default parameters first 22 | if bigBuffer == nil { 23 | let audio = engine.startTest(totalDuration: 1.0) 24 | player.play() 25 | player.isLooping = true 26 | audio.append(engine.render(duration: 1.0)) 27 | bigBuffer = AVAudioPCMBuffer(pcmFormat: audio.format, frameCapacity: audio.frameLength * UInt32(duration)) 28 | 29 | bigBuffer?.append(audio) 30 | } 31 | 32 | for i in 0 ..< node.parameters.count { 33 | let node = factory(player) 34 | engine.output = node 35 | 36 | let param = node.parameters[i] 37 | 38 | node.start() 39 | 40 | param.value = param.def.range.lowerBound 41 | param.ramp(to: param.def.range.upperBound, duration: 1) 42 | 43 | let audio = engine.startTest(totalDuration: 1.0) 44 | audio.append(engine.render(duration: 1.0)) 45 | 46 | bigBuffer?.append(audio) 47 | } 48 | 49 | XCTAssertFalse(bigBuffer!.isSilent) 50 | 51 | if audition { bigBuffer!.audition() } 52 | 53 | XCTAssertTrue([md5, m1MD5].contains(bigBuffer!.md5), "\(node)\nFAILEDMD5 \(bigBuffer!.md5)") 54 | } 55 | 56 | func testEffects() { 57 | nodeParameterTest(md5: "4038dc9888744626dc769da6f5da4d06", factory: { input in DynaRageCompressor(input) }) 58 | nodeParameterTest(md5: "a4d00e9a117e58eec42c01023b40a15a", factory: { input in RhinoGuitarProcessor(input) }) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Tests/DevoloopAudioKitTests/TestResources/12345.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/DevoloopAudioKit/57145b06ac528eb4cb03b0d3369c2f514648b695/Tests/DevoloopAudioKitTests/TestResources/12345.wav -------------------------------------------------------------------------------- /images/dynarage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/DevoloopAudioKit/57145b06ac528eb4cb03b0d3369c2f514648b695/images/dynarage.jpg --------------------------------------------------------------------------------