├── mac ├── PkgInfo ├── audio-unit │ ├── resource │ │ ├── loop.wav │ │ ├── plugin.icns │ │ ├── Info.plist │ │ ├── Info.appex.plist │ │ └── Main.storyboard │ ├── plugin.entitlements │ ├── src │ │ ├── AppDelegate.h │ │ ├── ViewController.h │ │ ├── AppDelegate.m │ │ └── ViewController.m │ ├── CMakeLists.txt │ └── audiounitconfig.h └── Info.plist ├── resource ├── version.png ├── background.png ├── design │ └── plugin.psd ├── plugin.rc └── plugin.uidesc ├── .gitignore ├── setup.bat ├── LICENSE ├── setup.sh ├── src ├── version.h ├── vstentry_vst2.cpp ├── waveshaper.h ├── lfo.cpp ├── bitcrusher.h ├── snd.h ├── paramids.h ├── pluginprocess.cpp ├── audiobuffer.h ├── waveshaper.cpp ├── limiter.h ├── util.h ├── bitcrusher.cpp ├── lfo.h ├── limiter.cpp ├── limiter.tcc ├── global.h ├── vstentry.cpp ├── calc.h ├── pluginprocess.h ├── ui │ ├── controller.h │ ├── uimessagecontroller.h │ └── controller.cpp ├── vst.h ├── pluginprocess.tcc ├── audiobuffer.cpp ├── formantfilter.h ├── formantfilter.cpp └── vst.cpp ├── README.md └── CMakeLists.txt /mac/PkgInfo: -------------------------------------------------------------------------------- 1 | APPLTrsf -------------------------------------------------------------------------------- /resource/version.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igorski/transformant/HEAD/resource/version.png -------------------------------------------------------------------------------- /resource/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igorski/transformant/HEAD/resource/background.png -------------------------------------------------------------------------------- /resource/design/plugin.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igorski/transformant/HEAD/resource/design/plugin.psd -------------------------------------------------------------------------------- /mac/audio-unit/resource/loop.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igorski/transformant/HEAD/mac/audio-unit/resource/loop.wav -------------------------------------------------------------------------------- /mac/audio-unit/resource/plugin.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igorski/transformant/HEAD/mac/audio-unit/resource/plugin.icns -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | vst3sdk 3 | Makefile 4 | CMakeCache.txt 5 | CMakeFiles 6 | 7 | .DS_Store 8 | Thumbs.db 9 | 10 | .vs 11 | .idea 12 | *.swp 13 | *.bak 14 | -------------------------------------------------------------------------------- /mac/audio-unit/plugin.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /setup.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | cls 3 | 4 | echo "Retrieving Steinberg VST3 SDK..." 5 | git clone --recursive https://github.com/steinbergmedia/vst3sdk.git --branch v3.7.11_build_10 6 | 7 | cd vst3sdk 8 | rmdir /Q /S build 9 | mkdir build 10 | cd build 11 | 12 | cmake.exe -G"Visual Studio 16 2019" .. 13 | cmake --build . --config Release 14 | 15 | cd .. 16 | -------------------------------------------------------------------------------- /mac/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSHumanReadableCopyright 6 | ${MACOSX_BUNDLE_COPYRIGHT} 7 | CFBundleDevelopmentRegion 8 | English 9 | CFBundleExecutable 10 | ${MACOSX_BUNDLE_BUNDLE_NAME} 11 | CFBundleIconFile 12 | 13 | CFBundleIdentifier 14 | ${MACOSX_BUNDLE_GUI_IDENTIFIER} 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundlePackageType 18 | BNDL 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${MACOSX_BUNDLE_BUNDLE_VERSION} 23 | CFBundleShortVersionString 24 | ${MACOSX_BUNDLE_SHORT_VERSION_STRING} 25 | CSResourcesFileMapped 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Igor Zinken 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 | -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | clear 3 | 4 | echo "Retrieving Steinberg VST3 SDK..." 5 | git clone --recursive https://github.com/steinbergmedia/vst3sdk.git --branch v3.7.11_build_10 6 | 7 | echo "--------------------------------" 8 | echo "Setting up Steinberg VST3 SDK..." 9 | 10 | cd vst3sdk 11 | rm -rf build 12 | mkdir build 13 | cd build 14 | 15 | echo "---------------------" 16 | 17 | # Parse arguments 18 | 19 | while [[ "$#" -gt 0 ]]; do 20 | case $1 in 21 | --platform) platform="$2"; shift ;; 22 | --coresdk) coresdk="$2"; shift ;; 23 | *) echo "Unknown parameter passed: $1"; exit 1 ;; 24 | esac 25 | shift 26 | done 27 | 28 | if [ "$platform" == "mac" ]; then 29 | echo "Building for macOS..." 30 | if [ "$coresdk" ]; then 31 | echo "Building with CoreAudio support. Library specified to be at $coresdk" 32 | FLAGS="-DSMTG_COREAUDIO_SDK_PATH=$coresdk" 33 | fi 34 | cmake -GXcode -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" ${FLAGS} .. 35 | else 36 | echo "Building for Linux..." 37 | cmake -DCMAKE_BUILD_TYPE=Release .. 38 | fi 39 | 40 | cmake --build . --config Release -------------------------------------------------------------------------------- /src/version.h: -------------------------------------------------------------------------------- 1 | #ifndef __VERSION_HEADER__ 2 | #define __VERSION_HEADER__ 3 | 4 | #define INT_TO_STR(x) #x 5 | 6 | #define MAJOR_VERSION_INT PLUGIN_MAJOR_VERSION 7 | #define MAJOR_VERSION_STR INT_TO_STR(MAJOR_VERSION_INT) 8 | 9 | #define SUB_VERSION_INT PLUGIN_MINOR_VERSION 10 | #define SUB_VERSION_STR INT_TO_STR(SUB_VERSION_INT) 11 | 12 | #define RELEASE_NUMBER_INT PLUGIN_RELEASE_NUMBER 13 | #define RELEASE_NUMBER_STR INT_TO_STR(RELEASE_NUMBER_INT) 14 | 15 | #define BUILD_NUMBER_INT PLUGIN_BUILD_NUMBER 16 | #define BUILD_NUMBER_STR INT_TO_STR(PLUGIN_BUILD_NUMBER) 17 | 18 | // Version with build number (example "1.0.3.342") 19 | #define FULL_VERSION_STR MAJOR_VERSION_STR "." SUB_VERSION_STR "." RELEASE_NUMBER_STR "." BUILD_NUMBER_STR 20 | 21 | // Version without build number (example "1.0.3") 22 | #define VERSION_STR MAJOR_VERSION_STR "." SUB_VERSION_STR "." RELEASE_NUMBER_STR 23 | 24 | #define stringOriginalFilename "transformant.vst3" 25 | #define stringFileDescription "Transformant plugin" 26 | #define stringCompanyName "igorski.nl\0" 27 | #define stringLegalCopyright #PLUGIN_COPYRIGHT 28 | #define stringLegalTrademarks "VST is a trademark of Steinberg Media Technologies GmbH" 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /mac/audio-unit/resource/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | plugin 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0.4 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | NSHumanReadableCopyright 28 | Copyright © 2019-2024 igorski.nl 29 | NSMainStoryboardFile 30 | Main 31 | NSPrincipalClass 32 | NSApplication 33 | SupportedNumChannels 34 | kSupportedNumChannels 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/vstentry_vst2.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020 Igor Zinken - https://www.igorski.nl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | #include "public.sdk/source/vst/vst2wrapper/vst2wrapper.h" 24 | #include "global.h" 25 | 26 | //------------------------------------------------------------------------ 27 | ::AudioEffect* createEffectInstance (audioMasterCallback audioMaster) 28 | { 29 | return Steinberg::Vst::Vst2Wrapper::create( GetPluginFactory (), Igorski::VST::ProcessorUID, Igorski::VST::ID, audioMaster ); 30 | } 31 | -------------------------------------------------------------------------------- /resource/plugin.rc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../src/version.h" 3 | 4 | #define APSTUDIO_READONLY_SYMBOLS 5 | 6 | plugin.uidesc DATA "plugin.uidesc" 7 | background.png PNG "background.png" 8 | version.png PNG "version.png" 9 | 10 | ///////////////////////////////////////////////////////////////////////////// 11 | // Version 12 | ///////////////////////////////////////////////////////////////////////////// 13 | VS_VERSION_INFO VERSIONINFO 14 | FILEVERSION MAJOR_VERSION_INT,SUB_VERSION_INT,RELEASE_NUMBER_INT,BUILD_NUMBER_INT 15 | PRODUCTVERSION MAJOR_VERSION_INT,SUB_VERSION_INT,RELEASE_NUMBER_INT,BUILD_NUMBER_INT 16 | FILEFLAGSMASK 0x3fL 17 | #ifdef _DEBUG 18 | FILEFLAGS 0x1L 19 | #else 20 | FILEFLAGS 0x0L 21 | #endif 22 | FILEOS 0x40004L 23 | FILETYPE 0x1L 24 | FILESUBTYPE 0x0L 25 | BEGIN 26 | BLOCK "StringFileInfo" 27 | BEGIN 28 | BLOCK "040004e4" 29 | BEGIN 30 | VALUE "FileVersion", FULL_VERSION_STR"\0" 31 | VALUE "ProductVersion", FULL_VERSION_STR"\0" 32 | VALUE "OriginalFilename", "transformant.vst3\0" 33 | VALUE "FileDescription", stringFileDescription"\0" 34 | VALUE "InternalName", stringFileDescription"\0" 35 | VALUE "ProductName", stringFileDescription"\0" 36 | VALUE "CompanyName", stringCompanyName"\0" 37 | VALUE "LegalCopyright", stringLegalCopyright"\0" 38 | VALUE "LegalTrademarks", stringLegalTrademarks"\0" 39 | //VALUE "PrivateBuild", " \0" 40 | //VALUE "SpecialBuild", " \0" 41 | //VALUE "Comments", " \0" 42 | END 43 | END 44 | BLOCK "VarFileInfo" 45 | BEGIN 46 | VALUE "Translation", 0x400, 1252 47 | END 48 | END 49 | -------------------------------------------------------------------------------- /src/waveshaper.h: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2013-2020 Igor Zinken - https://www.igorski.nl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | #ifndef __WAVESHAPER_H_INCLUDED__ 24 | #define __WAVESHAPER_H_INCLUDED__ 25 | 26 | namespace Igorski { 27 | class WaveShaper 28 | { 29 | public: 30 | WaveShaper( float amount, float level ); 31 | 32 | float getAmount(); 33 | void setAmount( float value ); // range between -1 and +1 34 | float getLevel(); 35 | void setLevel( float value ); 36 | void process( double* inBuffer, int bufferSize ); 37 | 38 | private: 39 | float _amount; 40 | float _multiplier; 41 | float _level; 42 | }; 43 | } 44 | 45 | #endif -------------------------------------------------------------------------------- /src/lfo.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2013-2020 Igor Zinken - https://www.igorski.nl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | #include "lfo.h" 24 | 25 | namespace Igorski { 26 | 27 | LFO::LFO( float sampleRate ) { 28 | _rate = VST::MIN_LFO_RATE(); 29 | _accumulator = 0.f; 30 | _sampleRate = sampleRate; 31 | } 32 | 33 | LFO::~LFO() { 34 | 35 | } 36 | 37 | /* public methods */ 38 | 39 | float LFO::getRate() 40 | { 41 | return _rate; 42 | } 43 | 44 | void LFO::setRate( float value ) 45 | { 46 | _rate = value; 47 | } 48 | 49 | void LFO::setAccumulator( float value ) 50 | { 51 | _accumulator = value; 52 | } 53 | 54 | float LFO::getAccumulator() 55 | { 56 | return _accumulator; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/bitcrusher.h: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2013-2020 Igor Zinken - https://www.igorski.nl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | #ifndef __BITCRUSHER_H_INCLUDED__ 24 | #define __BITCRUSHER_H_INCLUDED__ 25 | 26 | #include "lfo.h" 27 | 28 | namespace Igorski { 29 | class BitCrusher { 30 | 31 | public: 32 | BitCrusher( float amount, float inputMix, float outputMix ); 33 | ~BitCrusher(); 34 | 35 | void process( double* inBuffer, int bufferSize ); 36 | 37 | void setAmount( float value ); // range between -1 to +1 38 | void setInputMix( float value ); 39 | void setOutputMix( float value ); 40 | 41 | private: 42 | int _bits; // we scale the amount to integers in the 1-16 range 43 | float _amount; 44 | float _inputMix; 45 | float _outputMix; 46 | 47 | void calcBits(); 48 | }; 49 | } 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/snd.h: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2022 Igor Zinken - https://www.igorski.nl 5 | * 6 | * Adaptation of source provided in the JUCE library: 7 | * Copyright (c) 2020 - Raw Material Software Limited 8 | * 9 | * JUCE is an open source library subject to commercial or open-source 10 | * licensing. 11 | * 12 | * The code included in this file is provided under the terms of the ISC license 13 | * http://www.isc.org/downloads/software-support-policy/isc-license. Permission 14 | * To use, copy, modify, and/or distribute this software for any purpose with or 15 | * without fee is hereby granted provided that the above copyright notice and 16 | * this permission notice appear in all copies. 17 | * 18 | * JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER 19 | * EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE 20 | * DISCLAIMED. 21 | */ 22 | #ifndef __SND_H_INCLUDED__ 23 | #define __SND_H_INCLUDED__ 24 | 25 | #ifdef __SSE__ 26 | #define USE_SSE_INTRINSICS 27 | #endif 28 | #ifdef __SSE2__ 29 | #define USE_SSE_INTRINSICS 30 | #endif 31 | 32 | #ifdef USE_SSE_INTRINSICS 33 | #include 34 | #endif 35 | 36 | //============================================================================== 37 | /** 38 | Helper class providing an RAII-based mechanism for temporarily disabling 39 | denormals on your CPU. 40 | */ 41 | class ScopedNoDenormals 42 | { 43 | public: 44 | inline ScopedNoDenormals() noexcept 45 | { 46 | #ifdef USE_SSE_INTRINSICS 47 | mxcsr = _mm_getcsr(); 48 | _mm_setcsr (mxcsr | 0x8040); // add the DAZ and FZ bits 49 | #endif 50 | } 51 | 52 | 53 | inline ~ScopedNoDenormals() noexcept 54 | { 55 | #ifdef USE_SSE_INTRINSICS 56 | _mm_setcsr (mxcsr); 57 | #endif 58 | } 59 | 60 | private: 61 | #ifdef USE_SSE_INTRINSICS 62 | unsigned int mxcsr; 63 | #endif 64 | }; 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /src/paramids.h: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020 Igor Zinken - https://www.igorski.nl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | #ifndef __PARAMIDS_HEADER__ 24 | #define __PARAMIDS_HEADER__ 25 | 26 | enum 27 | { 28 | // ids for all visual controls 29 | 30 | kVowelLId = 1, // formant vowel L 31 | kVowelRId, // formant vowel R 32 | kVowelSyncId, // formant vowel sync 33 | kLFOVowelLId, // formant vowel L LFO rate 34 | kLFOVowelLDepthId, // formant vowel L LFO depth 35 | kLFOVowelRId, // formant vowel R LFO rate 36 | kLFOVowelRDepthId, // formant vowel R LFO depth 37 | kDistortionTypeId, // distortion type 38 | kDriveId, // distortion drive amount 39 | kDistortionChainId, // distortion pre/pos formant mix 40 | kVuPPMId // for the Vu value return to host 41 | }; 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /mac/audio-unit/resource/Info.appex.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | AUv3WrapperExtension 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | XPC! 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | NSExtension 24 | 25 | NSExtensionAttributes 26 | 27 | NSExtensionServiceRoleType 28 | NSExtensionServiceRoleTypeEditor 29 | AudioComponents 30 | 31 | 32 | description 33 | kAUcomponentDescription 34 | manufacturer 35 | kAUcomponentManufacturer1 36 | name 37 | kAUcomponentName 38 | sandboxSafe 39 | 40 | subtype 41 | kAUcomponentSubType1 42 | tags 43 | 44 | kAUcomponentTag 45 | 46 | type 47 | kAUcomponentType1 48 | version 49 | kAUcomponentVersion 50 | 51 | 52 | 53 | NSExtensionPointIdentifier 54 | com.apple.AudioUnit-UI 55 | NSExtensionPrincipalClass 56 | AUv3WrapperViewController 57 | 58 | SupportedNumChannels 59 | kSupportedNumChannels 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/pluginprocess.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020 Igor Zinken - https://www.igorski.nl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | #include "pluginprocess.h" 24 | #include "calc.h" 25 | #include 26 | 27 | namespace Igorski { 28 | 29 | PluginProcess::PluginProcess( int amountOfChannels, float sampleRate ) { 30 | _amountOfChannels = amountOfChannels; 31 | _sampleRate = sampleRate; 32 | 33 | bitCrusher = new BitCrusher( 8, 1.f, .5f ); 34 | waveShaper = new WaveShaper( 0.f, 1.f ); 35 | limiter = new Limiter( 10.f, 500.f, .95f ); 36 | formantFilterL = new FormantFilter( 0.f, _sampleRate ); 37 | formantFilterR = new FormantFilter( 0.f, _sampleRate ); 38 | 39 | // will be lazily created in the process function 40 | _mixBuffer = nullptr; 41 | } 42 | 43 | PluginProcess::~PluginProcess() { 44 | delete _mixBuffer; 45 | delete bitCrusher; 46 | delete waveShaper; 47 | delete limiter; 48 | delete formantFilterL; 49 | delete formantFilterR; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/audiobuffer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2013-2020 Igor Zinken - https://www.igorski.nl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | #ifndef __AUDIOBUFFER_H_INCLUDED__ 24 | #define __AUDIOBUFFER_H_INCLUDED__ 25 | 26 | #include "global.h" 27 | #include 28 | 29 | /** 30 | * An AudioBuffer represents multiple channels of audio 31 | * each of equal buffer length. 32 | * AudioBuffer has convenience methods for cloning, silencing and mixing 33 | */ 34 | class AudioBuffer 35 | { 36 | public: 37 | AudioBuffer( int aAmountOfChannels, int aBufferSize ); 38 | ~AudioBuffer(); 39 | 40 | int amountOfChannels; 41 | int bufferSize; 42 | bool loopeable; 43 | 44 | double* getBufferForChannel( int aChannelNum ); 45 | int mergeBuffers( AudioBuffer* aBuffer, int aReadOffset, int aWriteOffset, float aMixVolume ); 46 | void silenceBuffers(); 47 | void adjustBufferVolumes( float volume ); 48 | bool isSilent(); 49 | AudioBuffer* clone(); 50 | 51 | protected: 52 | std::vector* _buffers; 53 | }; 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /src/waveshaper.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2013-2020 Igor Zinken - https://www.igorski.nl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | #include "waveshaper.h" 24 | #include 25 | 26 | namespace Igorski { 27 | 28 | // constructor 29 | 30 | WaveShaper::WaveShaper( float amount, float level ) 31 | { 32 | setAmount( amount ); 33 | setLevel ( level ); 34 | } 35 | 36 | /* public methods */ 37 | 38 | void WaveShaper::process( double* inBuffer, int bufferSize ) 39 | { 40 | for ( int j = 0; j < bufferSize; ++j ) 41 | { 42 | double input = inBuffer[ j ]; 43 | inBuffer[ j ] = (( 1.0 + _multiplier ) * input / ( 1.0 + _multiplier * std::abs( input ))) * _level; 44 | } 45 | } 46 | 47 | /* getters / setters */ 48 | 49 | float WaveShaper::getAmount() 50 | { 51 | return _amount; 52 | } 53 | 54 | void WaveShaper::setAmount( float value ) 55 | { 56 | _amount = value; 57 | _multiplier = 2.0f * _amount / ( 1.0f - fmin(0.99999f, _amount)); 58 | } 59 | 60 | float WaveShaper::getLevel() 61 | { 62 | return _level; 63 | } 64 | 65 | void WaveShaper::setLevel( float value ) 66 | { 67 | _level = value; 68 | } 69 | 70 | } // E.O namespace Igorski -------------------------------------------------------------------------------- /mac/audio-unit/src/AppDelegate.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // Project : VST SDK 3 | // 4 | // Category : Helpers 5 | // Filename : 6 | // Created by : Steinberg, 07/2017. 7 | // Description : VST 3 AUv3Wrapper 8 | // 9 | //----------------------------------------------------------------------------- 10 | // LICENSE 11 | // (c) 2022, Steinberg Media Technologies GmbH, All Rights Reserved 12 | //----------------------------------------------------------------------------- 13 | // Redistribution and use in source and binary forms, with or without modification, 14 | // are permitted provided that the following conditions are met: 15 | // 16 | // * Redistributions of source code must retain the above copyright notice, 17 | // this list of conditions and the following disclaimer. 18 | // * Redistributions in binary form must reproduce the above copyright notice, 19 | // this list of conditions and the following disclaimer in the documentation 20 | // and/or other materials provided with the distribution. 21 | // * Neither the name of the Steinberg Media Technologies nor the names of its 22 | // contributors may be used to endorse or promote products derived from this 23 | // software without specific prior written permission. 24 | // 25 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 26 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 27 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 28 | // IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 29 | // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 30 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 32 | // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 33 | // OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | // OF THE POSSIBILITY OF SUCH DAMAGE. 35 | //----------------------------------------------------------------------------- 36 | 37 | #import 38 | 39 | @interface AppDelegate : NSObject 40 | @end 41 | -------------------------------------------------------------------------------- /mac/audio-unit/src/ViewController.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // Project : VST SDK 3 | // 4 | // Category : Helpers 5 | // Filename : 6 | // Created by : Steinberg, 07/2017. 7 | // Description : VST 3 AUv3Wrapper 8 | // 9 | //----------------------------------------------------------------------------- 10 | // LICENSE 11 | // (c) 2022, Steinberg Media Technologies GmbH, All Rights Reserved 12 | //----------------------------------------------------------------------------- 13 | // Redistribution and use in source and binary forms, with or without modification, 14 | // are permitted provided that the following conditions are met: 15 | // 16 | // * Redistributions of source code must retain the above copyright notice, 17 | // this list of conditions and the following disclaimer. 18 | // * Redistributions in binary form must reproduce the above copyright notice, 19 | // this list of conditions and the following disclaimer in the documentation 20 | // and/or other materials provided with the distribution. 21 | // * Neither the name of the Steinberg Media Technologies nor the names of its 22 | // contributors may be used to endorse or promote products derived from this 23 | // software without specific prior written permission. 24 | // 25 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 26 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 27 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 28 | // IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 29 | // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 30 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 32 | // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 33 | // OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | // OF THE POSSIBILITY OF SUCH DAMAGE. 35 | //----------------------------------------------------------------------------- 36 | 37 | #import 38 | 39 | @interface ViewController : NSViewController 40 | 41 | @end 42 | 43 | 44 | -------------------------------------------------------------------------------- /mac/audio-unit/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | include(SMTG_AddVST3AuV3) 3 | 4 | function(create_audio_unit vst3_target) 5 | set(audio_unit_src_dir ${CMAKE_CURRENT_SOURCE_DIR}/mac/audio-unit) 6 | 7 | set(au_app_sources 8 | ${audio_unit_src_dir}/src/ViewController.m 9 | ${audio_unit_src_dir}/src/ViewController.h 10 | ${audio_unit_src_dir}/src/AppDelegate.m 11 | ${audio_unit_src_dir}/src/AppDelegate.h 12 | ${audio_unit_src_dir}/audiounitconfig.h 13 | ) 14 | 15 | set(au_app_ui_resources 16 | ${audio_unit_src_dir}/resource/Main.storyboard 17 | ${audio_unit_src_dir}/resource/plugin.icns 18 | ${audio_unit_src_dir}/resource/loop.wav 19 | ) 20 | 21 | # these redefine values set in SMTG_AddVST3AuV3.cmake --- 22 | set(public_sdk_SOURCE_DIR ${VST3_SDK_ROOT}/public.sdk) 23 | set(auv3wrappermacos_sources 24 | ${VST3_SDK_ROOT}/public.sdk/source/vst/auv3wrapper/AUv3WrappermacOS/main.mm 25 | ) 26 | set(auv3wrappermacosextension_sources 27 | ${VST3_SDK_ROOT}/public.sdk/source/vst/auv3wrapper/Shared/AUv3WrapperFactory.mm 28 | ${VSTSDK_PLUGIN_SOURCE} 29 | ) 30 | # --- E.O. SMTG_*.cmake overrides 31 | 32 | set(au_target ${target}_auv3) 33 | 34 | smtg_add_auv3_app( 35 | ${au_target} 36 | "macOS" 37 | "Transformant AUV3" 38 | "nl.igorski.vst.${target}" 39 | "${audio_unit_src_dir}/audiounitconfig.h" 40 | "${audio_unit_src_dir}/plugin.entitlements" 41 | "${au_app_sources}" 42 | "${au_app_ui_resources}" 43 | "${audio_unit_src_dir}/resource/Info.plist" 44 | "${audio_unit_src_dir}/resource/Info.appex.plist" 45 | ${vst3_target} 46 | ) 47 | 48 | #exposes auv3wrappermacos 49 | target_link_directories(${au_target}_appextension_macos PRIVATE "${VST3_SDK_ROOT}/build/lib") 50 | target_link_directories(${au_target} PRIVATE "${VST3_SDK_ROOT}/build/lib") 51 | 52 | target_link_libraries(${au_target} 53 | PUBLIC 54 | base 55 | pluginterfaces 56 | sdk 57 | sdk_common 58 | sdk_hosting 59 | ) 60 | 61 | target_link_libraries(${au_target}_appextension_macos 62 | PUBLIC 63 | base 64 | pluginterfaces 65 | sdk 66 | sdk_hosting 67 | ) 68 | endfunction() -------------------------------------------------------------------------------- /src/limiter.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Ported from mdaLimiterProcessor.h 3 | * Created by Arne Scheffler on 6/14/08. 4 | * 5 | * mda VST Plug-ins 6 | * 7 | * Copyright (c) 2008 Paul Kellett 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 10 | * this software and associated documentation files (the "Software"), to deal in 11 | * the Software without restriction, including without limitation the rights to 12 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 13 | * the Software, and to permit persons to whom the Software is furnished to do so, 14 | * subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in all 17 | * copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 21 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 22 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 23 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 24 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | #ifndef __LIMITER_H_INCLUDED__ 27 | #define __LIMITER_H_INCLUDED__ 28 | 29 | #include "audiobuffer.h" 30 | #include 31 | 32 | class Limiter 33 | { 34 | public: 35 | Limiter(); 36 | Limiter( float attackMs, float releaseMs, float thresholdDb ); 37 | ~Limiter(); 38 | 39 | template 40 | void process( SampleType** outputBuffer, int bufferSize, int numOutChannels ); 41 | 42 | void setAttack( float attackMs ); 43 | void setRelease( float releaseMs ); 44 | void setThreshold( float thresholdDb ); 45 | 46 | float getLinearGR(); 47 | 48 | protected: 49 | void init( float attackMs, float releaseMs, float thresholdDb ); 50 | void recalculate(); 51 | 52 | float pTresh; // in dB, -20 - 20 53 | float pTrim; 54 | float pAttack; // in microseconds 55 | float pRelease; // in ms 56 | float pKnee; 57 | 58 | float thresh, gain, att, rel, trim; 59 | }; 60 | 61 | #include "limiter.tcc" 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /mac/audio-unit/src/AppDelegate.m: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // Project : VST SDK 3 | // 4 | // Category : Helpers 5 | // Filename : 6 | // Created by : Steinberg, 07/2017. 7 | // Description : VST 3 AUv3Wrapper 8 | // 9 | //----------------------------------------------------------------------------- 10 | // LICENSE 11 | // (c) 2022, Steinberg Media Technologies GmbH, All Rights Reserved 12 | //----------------------------------------------------------------------------- 13 | // Redistribution and use in source and binary forms, with or without modification, 14 | // are permitted provided that the following conditions are met: 15 | // 16 | // * Redistributions of source code must retain the above copyright notice, 17 | // this list of conditions and the following disclaimer. 18 | // * Redistributions in binary form must reproduce the above copyright notice, 19 | // this list of conditions and the following disclaimer in the documentation 20 | // and/or other materials provided with the distribution. 21 | // * Neither the name of the Steinberg Media Technologies nor the names of its 22 | // contributors may be used to endorse or promote products derived from this 23 | // software without specific prior written permission. 24 | // 25 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 26 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 27 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 28 | // IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 29 | // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 30 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 32 | // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 33 | // OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | // OF THE POSSIBILITY OF SUCH DAMAGE. 35 | //----------------------------------------------------------------------------- 36 | 37 | #import "AppDelegate.h" 38 | 39 | @implementation AppDelegate 40 | 41 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 42 | } 43 | 44 | - (void)applicationWillTerminate:(NSNotification *)aNotification { 45 | } 46 | 47 | - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender 48 | { 49 | return YES; 50 | } 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2018 Igor Zinken - https://www.igorski.nl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | #ifndef __UTIL_HEADER__ 24 | #define __UTIL_HEADER__ 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | namespace Igorski { 31 | namespace Util { 32 | 33 | /** 34 | * Convenience method to log a message to a file 35 | * multiple messages can be written to the same file (are 36 | * separated by a new line) 37 | * 38 | * This should be used for debugging purposes only 39 | */ 40 | void log( const char* message, const char* filename ) 41 | { 42 | std::ofstream out; 43 | 44 | char buff[20]; 45 | struct tm *sTm; 46 | 47 | time_t now = time( 0 ); 48 | sTm = gmtime( &now ); 49 | 50 | strftime( buff, sizeof( buff ), "%Y-%m-%d %H:%M:%S", sTm ); 51 | 52 | out.open( filename, std::ios_base::app ); 53 | out << buff << " " << message << "\n"; 54 | 55 | out.close(); 56 | } 57 | 58 | void log( std::string message, const char* filename ) 59 | { 60 | log( message.c_str(), filename ); 61 | } 62 | 63 | void log( float value, const char* filename ) 64 | { 65 | log( std::to_string( value ), filename ); 66 | } 67 | 68 | void log( int value, const char* filename ) 69 | { 70 | log( std::to_string( value ), filename ); 71 | } 72 | 73 | } 74 | } 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /mac/audio-unit/audiounitconfig.h: -------------------------------------------------------------------------------- 1 | // The specific variant of the Audio Unit app extension. 2 | // The four possible types and their values are: 3 | // Effect (aufx), Generator (augn), Instrument (aumu), and Music Effect (aufm) 4 | #define kAUcomponentType 'aufx' 5 | #define kAUcomponentType1 aufx 6 | 7 | // A subtype code (unique ID) for the audio unit, such as gav3. 8 | // This value must be exactly 4 alphanumeric characters 9 | #define kAUcomponentSubType 'frmt' 10 | #define kAUcomponentSubType1 frmt 11 | 12 | // A manufacturer code for the audio unit, such as Aaud. 13 | // This value must be exactly 4 alphanumeric characters 14 | #define kAUcomponentManufacturer 'IGOR' 15 | #define kAUcomponentManufacturer1 IGOR 16 | 17 | // A product name for the audio unit 18 | #define kAUcomponentDescription AUv3WrapperExtension 19 | 20 | // The full name of the audio unit. 21 | // This is derived from the manufacturer and description key values 22 | #define kAUcomponentName Igorski: Transformant 23 | 24 | // Displayed Tags 25 | #define kAUcomponentTag Effects 26 | 27 | // A version number for the Audio Unit app extension (decimal value of hexadecimal representation with zeros between subversions) 28 | // Hexadecimal indexes representing: [0] = main version, [1] = 0 = dot, [2] = sub version, [3] = 0 = dot, [4] = sub-sub version, 29 | // e.g. 1.0.0 == 0x10000 == 65536, 1.2.3 = 0x10203 = 66051 30 | // needs to correspond with semver version in Info.plist 31 | #define kAUcomponentVersion 65540 32 | 33 | // Supported number of channels of your audio unit. 34 | // Integer indexes representing: [0] = input count, [1] = output count, [2] = 2nd input count, 35 | // [3]=2nd output count, etc. 36 | // e.g. 1122 == config1: [mono input, mono output], config2: [stereo input, stereo output] 37 | // see channelCapabilities for discussion 38 | #define kSupportedNumChannels 1122 39 | 40 | // The preview audio file name. 41 | // To add your own custom audio file (for standalone effects), add an audio file to the project (AUv3WrappermacOS and AUv3WrapperiOS targets) and 42 | // enter the file name here 43 | #define kAudioFileName "loop" 44 | 45 | // The preview audio file format. 46 | // To add your own custom audio file (for standalone effects), add an audio file to the project (AUv3WrappermacOS and AUv3WrapperiOS targets) and 47 | // enter the file format here 48 | #define kAudioFileFormat "wav" 49 | 50 | // componentFlags (leave at 0) 51 | #define kAUcomponentFlags 0 52 | 53 | // componentFlagsMask (leave at 0) 54 | #define kAUcomponentFlagsMask 0 55 | 56 | // class name for the application delegate 57 | #define kAUapplicationDelegateClassName AppDelegate 58 | -------------------------------------------------------------------------------- /src/bitcrusher.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2013-2020 Igor Zinken - https://www.igorski.nl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | #include "bitcrusher.h" 24 | #include "global.h" 25 | #include "calc.h" 26 | #include 27 | #include 28 | 29 | namespace Igorski { 30 | 31 | /* constructor */ 32 | 33 | BitCrusher::BitCrusher( float amount, float inputMix, float outputMix ) 34 | { 35 | setAmount ( amount ); 36 | setInputMix ( inputMix ); 37 | setOutputMix( outputMix ); 38 | } 39 | 40 | BitCrusher::~BitCrusher() 41 | { 42 | 43 | } 44 | 45 | /* public methods */ 46 | 47 | void BitCrusher::process( double* inBuffer, int bufferSize ) 48 | { 49 | // sound should not be crushed ? do nothing 50 | if ( _bits == 16 ) { 51 | return; 52 | } 53 | 54 | int bitsPlusOne = _bits + 1; 55 | 56 | for ( int i = 0; i < bufferSize; ++i ) 57 | { 58 | short input = ( short ) (( inBuffer[ i ] * _inputMix ) * SHRT_MAX ); 59 | short prevent_offset = ( short )( -1 >> bitsPlusOne ); 60 | input &= ( -1 << ( 16 - _bits )); 61 | inBuffer[ i ] = (( input + prevent_offset ) * _outputMix ) / SHRT_MAX; 62 | } 63 | } 64 | 65 | /* setters */ 66 | 67 | void BitCrusher::setAmount( float value ) 68 | { 69 | // invert the range 0 == max bits (no distortion), 1 == min bits (severely distorted) 70 | _amount = abs(value - 1.f); 71 | 72 | calcBits(); 73 | } 74 | 75 | void BitCrusher::setInputMix( float value ) 76 | { 77 | _inputMix = Calc::cap( value ); 78 | } 79 | 80 | void BitCrusher::setOutputMix( float value ) 81 | { 82 | _outputMix = Calc::cap( value ); 83 | } 84 | 85 | /* private methods */ 86 | 87 | void BitCrusher::calcBits() 88 | { 89 | // scale float to 1 - 16 bit range 90 | _bits = ( int ) floor( Calc::scale( _amount, 1, 15 )) + 1; 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/lfo.h: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2013-2020 Igor Zinken - https://www.igorski.nl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | #ifndef __LFO_H_INCLUDED__ 24 | #define __LFO_H_INCLUDED__ 25 | 26 | #include "global.h" 27 | 28 | namespace Igorski { 29 | class LFO { 30 | 31 | public: 32 | LFO( float sampleRate ); 33 | ~LFO(); 34 | 35 | float getRate(); 36 | void setRate( float value ); 37 | 38 | // accumulators are used to retrieve a sample from the wave table 39 | // in other words: track the progress of the oscillator against its range 40 | 41 | float getAccumulator(); 42 | void setAccumulator( float offset ); 43 | 44 | /** 45 | * retrieve a value from the wave table for the current 46 | * accumulator position, this method also increments 47 | * the accumulator and keeps it within bounds 48 | */ 49 | inline float peek() 50 | { 51 | // the wave table offset to read from 52 | float SR_OVER_LENGTH = _sampleRate / ( float ) TABLE_SIZE; 53 | int readOffset = ( _accumulator == 0.f ) ? 0 : ( int ) ( _accumulator / SR_OVER_LENGTH ); 54 | 55 | // increment the accumulators read offset 56 | _accumulator += _rate; 57 | 58 | // keep the accumulator within the bounds of the sample frequency 59 | if ( _accumulator > _sampleRate ) 60 | _accumulator -= _sampleRate; 61 | 62 | // return the sample present at the calculated offset within the table 63 | return VST::TABLE[ readOffset ]; 64 | } 65 | 66 | private: 67 | 68 | // see Igorski::VST::LFO_TABLE; 69 | static const int TABLE_SIZE = 128; 70 | 71 | // used internally 72 | 73 | float _rate; 74 | float _accumulator; // is read offset in wave table buffer 75 | float _sampleRate; 76 | }; 77 | } 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /src/limiter.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Ported from mdaLimiterProcessor.cpp 3 | * Created by Arne Scheffler on 6/14/08. 4 | * 5 | * mda VST Plug-ins 6 | * 7 | * Copyright (c) 2008 Paul Kellett 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 10 | * this software and associated documentation files (the "Software"), to deal in 11 | * the Software without restriction, including without limitation the rights to 12 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 13 | * the Software, and to permit persons to whom the Software is furnished to do so, 14 | * subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in all 17 | * copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 21 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 22 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 23 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 24 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | #include "limiter.h" 27 | #include "global.h" 28 | #include 29 | 30 | // constructors / destructor 31 | 32 | Limiter::Limiter() 33 | { 34 | init( 0.15, 0.50, 0.60 ); 35 | } 36 | 37 | Limiter::Limiter( float attackMs, float releaseMs, float thresholdDb ) 38 | { 39 | init( attackMs, releaseMs, thresholdDb ); 40 | } 41 | 42 | Limiter::~Limiter() 43 | { 44 | // nowt... 45 | } 46 | 47 | /* public methods */ 48 | 49 | void Limiter::setAttack( float attackMs ) 50 | { 51 | pAttack = ( float ) attackMs; 52 | recalculate(); 53 | } 54 | 55 | void Limiter::setRelease( float releaseMs ) 56 | { 57 | pRelease = ( float ) releaseMs; 58 | recalculate(); 59 | } 60 | 61 | void Limiter::setThreshold( float thresholdDb ) 62 | { 63 | pTresh = ( float ) thresholdDb; 64 | recalculate(); 65 | } 66 | 67 | float Limiter::getLinearGR() 68 | { 69 | return gain > 1.f ? 1.f / gain : 1.f; 70 | } 71 | 72 | /* protected methods */ 73 | 74 | void Limiter::init( float attackMs, float releaseMs, float thresholdDb ) 75 | { 76 | pAttack = ( float ) attackMs; 77 | pRelease = ( float ) releaseMs; 78 | pTresh = ( float ) thresholdDb; 79 | pTrim = ( float ) 0.60; 80 | pKnee = ( float ) 0.40; 81 | 82 | gain = 1.f; 83 | 84 | recalculate(); 85 | } 86 | 87 | void Limiter::recalculate() 88 | { 89 | if ( pKnee > 0.5 ) { 90 | // soft knee 91 | thresh = ( float ) pow( 10.0, 1.f - ( 2.0 * pTresh )); 92 | } 93 | else { 94 | // hard knee 95 | thresh = ( float ) pow( 10.0, ( 2.0 * pTresh ) - 2.0 ); 96 | } 97 | trim = ( float )( pow( 10.0, ( 2.0 * pTrim) - 1.f )); 98 | att = ( float ) pow( 10.0, -2.0 * pAttack ); 99 | rel = ( float ) pow( 10.0, -2.0 - ( 3.0 * pRelease )); 100 | } 101 | -------------------------------------------------------------------------------- /src/limiter.tcc: -------------------------------------------------------------------------------- 1 | /** 2 | * Ported from mdaLimiterProcessor.cpp 3 | * Created by Arne Scheffler on 6/14/08. 4 | * 5 | * mda VST Plug-ins 6 | * 7 | * Copyright (c) 2008 Paul Kellett 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 10 | * this software and associated documentation files (the "Software"), to deal in 11 | * the Software without restriction, including without limitation the rights to 12 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 13 | * the Software, and to permit persons to whom the Software is furnished to do so, 14 | * subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in all 17 | * copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 21 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 22 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 23 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 24 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | template 27 | void Limiter::process( SampleType** outputBuffer, int bufferSize, int numOutChannels ) 28 | { 29 | // if ( gain > 0.9999f && outputBuffer->isSilent ) 30 | // { 31 | // // don't process if input is silent 32 | // return; 33 | // } 34 | 35 | SampleType g, at, re, tr, th, lev, ol, or_; 36 | 37 | th = thresh; 38 | g = gain; 39 | at = att; 40 | re = rel; 41 | tr = trim; 42 | 43 | bool hasRight = ( numOutChannels > 1 ); 44 | 45 | SampleType* leftBuffer = outputBuffer[ 0 ]; 46 | SampleType* rightBuffer = hasRight ? outputBuffer[ 1 ] : 0; 47 | 48 | if ( pKnee > 0.5 ) 49 | { 50 | // soft knee 51 | 52 | for ( int i = 0; i < bufferSize; ++i ) { 53 | 54 | ol = leftBuffer[ i ]; 55 | or_ = hasRight ? rightBuffer[ i ] : 0; 56 | 57 | lev = ( SampleType ) ( 1.f / ( 1.f + th * fabs( ol + or_ ))); 58 | 59 | if ( g > lev ) { 60 | g = g - at * ( g - lev ); 61 | } 62 | else { 63 | g = g + re * ( lev - g ); 64 | } 65 | 66 | leftBuffer[ i ] = ( ol * tr * g ); 67 | 68 | if ( hasRight ) 69 | rightBuffer[ i ] = ( or_ * tr * g ); 70 | } 71 | } 72 | else 73 | { 74 | for ( int i = 0; i < bufferSize; ++i ) { 75 | 76 | ol = leftBuffer[ i ]; 77 | or_ = hasRight ? rightBuffer[ i ] : 0; 78 | 79 | lev = ( SampleType ) ( 0.5 * g * fabs( ol + or_ )); 80 | 81 | if ( lev > th ) { 82 | g = g - ( at * ( lev - th )); 83 | } 84 | else { 85 | // below threshold 86 | g = g + ( SampleType )( re * ( 1.f - g )); 87 | } 88 | 89 | leftBuffer[ i ] = ( ol * tr * g ); 90 | 91 | if ( hasRight ) 92 | rightBuffer[ i ] = ( or_ * tr * g ); 93 | } 94 | } 95 | gain = g; 96 | } 97 | -------------------------------------------------------------------------------- /src/global.h: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020 Igor Zinken - https://www.igorski.nl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | #ifndef __GLOBAL_HEADER__ 24 | #define __GLOBAL_HEADER__ 25 | 26 | #include "pluginterfaces/base/fplatform.h" 27 | #include "pluginterfaces/base/funknown.h" 28 | 29 | using namespace Steinberg; 30 | 31 | namespace Igorski { 32 | namespace VST { 33 | 34 | static const int ID = 9715187; 35 | static const char* NAME = "Transformant"; 36 | static const char* VENDOR = "igorski.nl"; 37 | 38 | static const FUID ProcessorUID( 0x9B87BC9B, 0x0D974BF4, 0x812B59EA, 0xAE2F10A2 ); 39 | static const FUID ControllerUID( 0x73A7B7C0, 0x1AD743C1, 0xBFBFD9F4, 0x5F5A04E1 ); 40 | 41 | static const float PI = 3.141592653589793f; 42 | static const float TWO_PI = PI * 2.f; 43 | 44 | // maximum and minimum rate of oscillation in Hz 45 | // also see plugin.uidesc to update the controls to match 46 | 47 | static const float MAX_LFO_RATE() { return 10.f; } 48 | static const float MIN_LFO_RATE() { return .1f; } 49 | 50 | // sine waveform used for the oscillator 51 | static const float TABLE[ 128 ] = { 0, 0.0490677, 0.0980171, 0.14673, 0.19509, 0.24298, 0.290285, 0.33689, 0.382683, 0.427555, 0.471397, 0.514103, 0.55557, 0.595699, 0.634393, 0.671559, 0.707107, 0.740951, 0.77301, 0.803208, 0.83147, 0.857729, 0.881921, 0.903989, 0.92388, 0.941544, 0.95694, 0.970031, 0.980785, 0.989177, 0.995185, 0.998795, 1, 0.998795, 0.995185, 0.989177, 0.980785, 0.970031, 0.95694, 0.941544, 0.92388, 0.903989, 0.881921, 0.857729, 0.83147, 0.803208, 0.77301, 0.740951, 0.707107, 0.671559, 0.634393, 0.595699, 0.55557, 0.514103, 0.471397, 0.427555, 0.382683, 0.33689, 0.290285, 0.24298, 0.19509, 0.14673, 0.0980171, 0.0490677, 1.22465e-16, -0.0490677, -0.0980171, -0.14673, -0.19509, -0.24298, -0.290285, -0.33689, -0.382683, -0.427555, -0.471397, -0.514103, -0.55557, -0.595699, -0.634393, -0.671559, -0.707107, -0.740951, -0.77301, -0.803208, -0.83147, -0.857729, -0.881921, -0.903989, -0.92388, -0.941544, -0.95694, -0.970031, -0.980785, -0.989177, -0.995185, -0.998795, -1, -0.998795, -0.995185, -0.989177, -0.980785, -0.970031, -0.95694, -0.941544, -0.92388, -0.903989, -0.881921, -0.857729, -0.83147, -0.803208, -0.77301, -0.740951, -0.707107, -0.671559, -0.634393, -0.595699, -0.55557, -0.514103, -0.471397, -0.427555, -0.382683, -0.33689, -0.290285, -0.24298, -0.19509, -0.14673, -0.0980171, -0.0490677 }; 52 | } 53 | } 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /src/vstentry.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2018-2020 Igor Zinken - https://www.igorski.nl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | #include "vst.h" 24 | #include "ui/controller.h" 25 | #include "global.h" 26 | #include "version.h" 27 | 28 | #include "public.sdk/source/main/pluginfactory.h" 29 | 30 | #if TARGET_OS_IPHONE 31 | #include "public.sdk/source/vst/vstguieditor.h" 32 | extern void* moduleHandle; 33 | #endif 34 | 35 | using namespace Steinberg::Vst; 36 | using namespace Igorski; 37 | 38 | #if TARGET_OS_IPHONE 39 | #include "public.sdk/source/vst/vstguieditor.h" 40 | extern void* moduleHandle; 41 | #endif 42 | 43 | //------------------------------------------------------------------------ 44 | // VST Plug-in Entry 45 | //------------------------------------------------------------------------ 46 | // Windows: do not forget to include a .def file in your project to export 47 | // GetPluginFactory function! 48 | //------------------------------------------------------------------------ 49 | 50 | BEGIN_FACTORY_DEF( "igorski.nl", 51 | "https://www.igorski.nl", 52 | "mailto:info@igorski.nl") 53 | 54 | //---First Plug-in included in this factory------- 55 | // its kVstAudioEffectClass component 56 | DEF_CLASS2( INLINE_UID_FROM_FUID( Igorski::VST::ProcessorUID ), 57 | PClassInfo::kManyInstances, // cardinality 58 | kVstAudioEffectClass, // the component category (do not change this) 59 | Igorski::VST::NAME, // plug-in name 60 | Vst::kDistributable, // means that component and controller could be distributed on different computers 61 | "Fx", // Subcategory for this Plug-in 62 | FULL_VERSION_STR, // Plug-in version 63 | kVstVersionString, // the VST 3 SDK version (do not change this) 64 | Transformant::createInstance ) // function pointer called when this component should be instantiated 65 | 66 | // its kVstComponentControllerClass component 67 | DEF_CLASS2( INLINE_UID_FROM_FUID( Igorski::VST::ControllerUID ), 68 | PClassInfo::kManyInstances, // cardinality 69 | kVstComponentControllerClass, // the Controller category (do not change this) 70 | Igorski::VST::NAME, // controller name (could be the same as component name) 71 | 0, "", // neither of these are used here 72 | FULL_VERSION_STR, // Plug-in version 73 | kVstVersionString, // the VST 3 SDK version (do not change this) 74 | PluginController::createInstance ) // function pointer called when this component should be instantiated 75 | 76 | END_FACTORY 77 | -------------------------------------------------------------------------------- /src/calc.h: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2018-2020 Igor Zinken - https://www.igorski.nl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | #ifndef __CALC_HEADER__ 24 | #define __CALC_HEADER__ 25 | 26 | #include 27 | #include 28 | #include "global.h" 29 | 30 | #define undenormaliseFloat(sample) ((((*(uint32 *)&(sample))&0x7f800000)==0)&&((sample)!=0.f)) 31 | #define undenormaliseDouble(sample) ((((((uint32 *)&(sample))[1])&0x7fe00000)==0)&&((sample)!=0.)) 32 | 33 | /** 34 | * convenience utilities to process values 35 | * common to the VST plugin context 36 | */ 37 | namespace Igorski { 38 | namespace Calc { 39 | 40 | /** 41 | * convert given value in seconds to the appropriate 42 | * value in samples (for the current sampling rate) 43 | */ 44 | inline int secondsToBuffer( float seconds, float sampleRate ) 45 | { 46 | return ( int )( seconds * sampleRate ); 47 | } 48 | 49 | /** 50 | * convert given value in milliseconds to the appropriate 51 | * value in samples (for the current sampling rate) 52 | */ 53 | inline int millisecondsToBuffer( float milliseconds, float sampleRate ) 54 | { 55 | return secondsToBuffer( milliseconds / 1000.f, sampleRate ); 56 | } 57 | 58 | // convenience method to ensure given value is within the 0.f - +1.f range 59 | 60 | inline float cap( float value ) 61 | { 62 | return std::min( 1.f, std::max( 0.f, value )); 63 | } 64 | 65 | // convenience method to ensure a sample is in the valid -1.f - +1.f range 66 | 67 | inline float capSample( float value ) 68 | { 69 | return std::min( 1.f, std::max( -1.f, value )); 70 | } 71 | 72 | // convenience method to round given number value to the nearest 73 | // multiple of valueToRoundTo 74 | // e.g. roundTo( 236.32, 10 ) == 240 and roundTo( 236.32, 5 ) == 235 75 | 76 | inline float roundTo( float value, float valueToRoundTo ) 77 | { 78 | float resto = fmod( value, valueToRoundTo ); 79 | 80 | if ( resto <= ( valueToRoundTo / 2 )) 81 | return value - resto; 82 | 83 | return value + valueToRoundTo - resto; 84 | } 85 | 86 | // convenience method to scale given value and its expected maxValue against 87 | // an arbitrary range (defined by maxCompareValue in relation to maxValue) 88 | 89 | inline float scale( float value, float maxValue, float maxCompareValue ) 90 | { 91 | float ratio = maxCompareValue / maxValue; 92 | return ( float ) ( std::min( maxValue, value ) * ratio ); 93 | } 94 | 95 | // cast a floating point value to a boolean true/false 96 | 97 | inline bool toBool( float value ) 98 | { 99 | return value >= .5; 100 | } 101 | } 102 | } 103 | 104 | #endif 105 | -------------------------------------------------------------------------------- /src/pluginprocess.h: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020 Igor Zinken - https://www.igorski.nl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | #ifndef __PLUGIN_PROCESS__H_INCLUDED__ 24 | #define __PLUGIN_PROCESS__H_INCLUDED__ 25 | 26 | #include "global.h" 27 | #include "audiobuffer.h" 28 | #include "bitcrusher.h" 29 | #include "waveshaper.h" 30 | #include "formantfilter.h" 31 | #include "limiter.h" 32 | #include "snd.h" 33 | #include 34 | 35 | using namespace Steinberg; 36 | 37 | namespace Igorski { 38 | class PluginProcess { 39 | 40 | public: 41 | PluginProcess( int amountOfChannels, float sampleRate ); 42 | ~PluginProcess(); 43 | 44 | // apply effect to incoming sampleBuffer contents 45 | 46 | template 47 | void process( SampleType** inBuffer, SampleType** outBuffer, int numInChannels, int numOutChannels, 48 | int bufferSize, uint32 sampleFramesSize 49 | ); 50 | 51 | // for a speed improvement we don't actually iterate over all channels, but assume 52 | // that if the first channel is empty, all are. 53 | 54 | inline bool isBufferSilent( float** buffer, int numChannels, int bufferSize ) { 55 | float* channelBuffer = buffer[ 0 ]; 56 | for ( int32 i = 0; i < bufferSize; ++i ) { 57 | if ( channelBuffer[ i ] != 0.f ) { 58 | return false; 59 | } 60 | } 61 | return true; 62 | }; 63 | 64 | inline bool isBufferSilent( double** buffer, int numChannels, int bufferSize ) { 65 | double* channelBuffer = buffer[ 0 ]; 66 | for ( int32 i = 0; i < bufferSize; ++i ) { 67 | if ( channelBuffer[ i ] != 0.0 ) { 68 | return false; 69 | } 70 | } 71 | return true; 72 | }; 73 | 74 | BitCrusher* bitCrusher; 75 | WaveShaper* waveShaper; 76 | Limiter* limiter; 77 | FormantFilter* formantFilterL; 78 | FormantFilter* formantFilterR; 79 | 80 | // whether effects are applied onto the input delay signal or onto 81 | // the delayed signal itself (false = on input, true = on delay) 82 | 83 | bool distortionPostMix = false; 84 | bool distortionTypeCrusher = false; 85 | 86 | inline bool hasLFO() { 87 | return formantFilterL->hasLFO || formantFilterR->hasLFO; 88 | } 89 | 90 | private: 91 | AudioBuffer* _mixBuffer; // buffer used for the sample process mixing 92 | 93 | int _amountOfChannels; 94 | float _sampleRate; 95 | 96 | // ensures the pre- and post mix buffers match the appropriate amount of channels 97 | // and buffer size. this also clones the contents of given in buffer into the pre-mix buffer 98 | // the buffers are pooled so this can be called upon each process cycle without allocation overhead 99 | 100 | template 101 | void prepareMixBuffers( SampleType** inBuffer, int numInChannels, int bufferSize ); 102 | 103 | }; 104 | } 105 | 106 | #include "pluginprocess.tcc" 107 | 108 | #endif 109 | -------------------------------------------------------------------------------- /src/ui/controller.h: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020-2023 Igor Zinken - https://www.igorski.nl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | #ifndef __CONTROLLER_HEADER__ 24 | #define __CONTROLLER_HEADER__ 25 | 26 | #include "vstgui/plugin-bindings/vst3editor.h" 27 | #include "public.sdk/source/vst/vsteditcontroller.h" 28 | 29 | #include 30 | 31 | namespace Steinberg { 32 | namespace Vst { 33 | 34 | using namespace VSTGUI; 35 | 36 | template 37 | class PluginUIMessageController; 38 | 39 | class PluginController : public EditControllerEx1, public IMidiMapping, public VSTGUI::VST3EditorDelegate 40 | { 41 | public: 42 | typedef PluginUIMessageController UIMessageController; 43 | //--- --------------------------------------------------------------------- 44 | // create function required for Plug-in factory, 45 | // it will be called to create new instances of this controller 46 | //--- --------------------------------------------------------------------- 47 | static FUnknown* createInstance( void* /*context*/ ) 48 | { 49 | return ( IEditController* ) new PluginController; 50 | } 51 | 52 | //---from IPluginBase-------- 53 | tresult PLUGIN_API initialize( FUnknown* context ) SMTG_OVERRIDE; 54 | tresult PLUGIN_API terminate() SMTG_OVERRIDE; 55 | 56 | //---from EditController----- 57 | tresult PLUGIN_API setComponentState( IBStream* state ) SMTG_OVERRIDE; 58 | IPlugView* PLUGIN_API createView( const char* name ) SMTG_OVERRIDE; 59 | tresult PLUGIN_API setState( IBStream* state ) SMTG_OVERRIDE; 60 | tresult PLUGIN_API getState( IBStream* state ) SMTG_OVERRIDE; 61 | tresult PLUGIN_API setParamNormalized( ParamID tag, ParamValue value) SMTG_OVERRIDE; 62 | tresult PLUGIN_API getParamStringByValue( ParamID tag, ParamValue valueNormalized, 63 | String128 string ) SMTG_OVERRIDE; 64 | tresult PLUGIN_API getParamValueByString( ParamID tag, TChar* string, 65 | ParamValue& valueNormalized ) SMTG_OVERRIDE; 66 | 67 | //---from ComponentBase----- 68 | tresult receiveText( const char* text ) SMTG_OVERRIDE; 69 | 70 | //---from IMidiMapping----------------- 71 | tresult PLUGIN_API getMidiControllerAssignment (int32 busIndex, int16 channel, 72 | CtrlNumber midiControllerNumber, 73 | ParamID& tag) SMTG_OVERRIDE; 74 | 75 | //---from VST3EditorDelegate----------- 76 | IController* createSubController( UTF8StringPtr name, const IUIDescription* description, 77 | VST3Editor* editor ) SMTG_OVERRIDE; 78 | 79 | DELEGATE_REFCOUNT ( EditController ) 80 | tresult PLUGIN_API queryInterface( const char* iid, void** obj ) SMTG_OVERRIDE; 81 | 82 | //---Internal functions------- 83 | void addUIMessageController( UIMessageController* controller ); 84 | void removeUIMessageController( UIMessageController* controller ); 85 | 86 | void setDefaultMessageText( String128 text ); 87 | TChar* getDefaultMessageText(); 88 | 89 | private: 90 | typedef std::vector UIMessageControllerList; 91 | UIMessageControllerList uiMessageControllers; 92 | 93 | String128 defaultMessageText; 94 | }; 95 | 96 | //------------------------------------------------------------------------ 97 | } // namespace Vst 98 | } // namespace Steinberg 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /src/vst.h: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2018-2020 Igor Zinken - https://www.igorski.nl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | #ifndef __VST_HEADER__ 24 | #define __VST_HEADER__ 25 | 26 | #include "public.sdk/source/vst/vstaudioeffect.h" 27 | #include "pluginprocess.h" 28 | #include "global.h" 29 | 30 | using namespace Steinberg::Vst; 31 | 32 | namespace Igorski { 33 | 34 | class Transformant : public AudioEffect 35 | { 36 | public: 37 | Transformant (); 38 | virtual ~Transformant(); // do not forget virtual here 39 | 40 | //--- --------------------------------------------------------------------- 41 | // create function required for Plug-in factory, 42 | // it will be called to create new instances of this Plug-in 43 | //--- --------------------------------------------------------------------- 44 | static FUnknown* createInstance( void* /*context*/ ) { return ( IAudioProcessor* ) new Transformant; } 45 | 46 | //--- --------------------------------------------------------------------- 47 | // AudioEffect overrides: 48 | //--- --------------------------------------------------------------------- 49 | /** Called at first after constructor */ 50 | tresult PLUGIN_API initialize( FUnknown* context ) SMTG_OVERRIDE; 51 | 52 | /** Called at the end before destructor */ 53 | tresult PLUGIN_API terminate() SMTG_OVERRIDE; 54 | 55 | /** Switch the Plug-in on/off */ 56 | tresult PLUGIN_API setActive( TBool state ) SMTG_OVERRIDE; 57 | 58 | /** Here we go...the process call */ 59 | tresult PLUGIN_API process( ProcessData& data ) SMTG_OVERRIDE; 60 | 61 | /** Test of a communication channel between controller and component */ 62 | tresult receiveText( const char* text ) SMTG_OVERRIDE; 63 | 64 | /** For persistence */ 65 | tresult PLUGIN_API setState( IBStream* state ) SMTG_OVERRIDE; 66 | tresult PLUGIN_API getState( IBStream* state ) SMTG_OVERRIDE; 67 | 68 | /** Will be called before any process call */ 69 | tresult PLUGIN_API setupProcessing( ProcessSetup& newSetup ) SMTG_OVERRIDE; 70 | 71 | /** Bus arrangement managing */ 72 | tresult PLUGIN_API setBusArrangements( SpeakerArrangement* inputs, int32 numIns, 73 | SpeakerArrangement* outputs, 74 | int32 numOuts ) SMTG_OVERRIDE; 75 | 76 | /** Asks if a given sample size is supported see \ref SymbolicSampleSizes. */ 77 | tresult PLUGIN_API canProcessSampleSize( int32 symbolicSampleSize ) SMTG_OVERRIDE; 78 | 79 | /** We want to receive message. */ 80 | tresult PLUGIN_API notify( IMessage* message ) SMTG_OVERRIDE; 81 | 82 | //------------------------------------------------------------------------ 83 | protected: 84 | //============================================================================== 85 | 86 | // our model values, these are all 0 - 1 range 87 | // (normalized) RangeParameter values 88 | 89 | float fVowelL; 90 | float fVowelR; 91 | float fVowelSync; 92 | float fLFOVowelL; 93 | float fLFOVowelR; 94 | float fLFOVowelLDepth; 95 | float fLFOVowelRDepth; 96 | float fDistortionType; 97 | float fDrive; 98 | float fDistortionChain; 99 | 100 | float outputGainOld; // for visualizing output gain in DAW 101 | 102 | int32 currentProcessMode; 103 | 104 | Igorski::PluginProcess* pluginProcess; 105 | 106 | // synchronize the processors model with UI led changes 107 | 108 | void syncModel(); 109 | }; 110 | 111 | } 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /src/pluginprocess.tcc: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020 Igor Zinken - https://www.igorski.nl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | namespace Igorski 24 | { 25 | template 26 | void PluginProcess::process( SampleType** inBuffer, SampleType** outBuffer, int numInChannels, int numOutChannels, 27 | int bufferSize, uint32 sampleFramesSize ) { 28 | 29 | ScopedNoDenormals noDenormals; 30 | 31 | // prepare the mix buffers and clone the incoming buffer contents into the pre-mix buffer 32 | 33 | prepareMixBuffers( inBuffer, numInChannels, bufferSize ); 34 | 35 | for ( int32 c = 0; c < numInChannels; ++c ) 36 | { 37 | SampleType* channelInBuffer = inBuffer[ c ]; 38 | SampleType* channelOutBuffer = outBuffer[ c ]; 39 | auto channelMixBuffer = _mixBuffer->getBufferForChannel( c ); 40 | 41 | // pre formant filter bit crusher processing 42 | 43 | if ( !distortionPostMix ) { 44 | if ( distortionTypeCrusher ) { 45 | bitCrusher->process( channelMixBuffer, bufferSize ); 46 | } else { 47 | waveShaper->process( channelMixBuffer, bufferSize ); 48 | } 49 | } 50 | 51 | // formant filter 52 | 53 | if ( c % 2 == 0 ) { 54 | formantFilterL->process( channelMixBuffer, bufferSize ); 55 | } else { 56 | formantFilterR->process( channelMixBuffer, bufferSize ); 57 | } 58 | 59 | // post formant filter bit crusher processing 60 | 61 | if ( distortionPostMix ) { 62 | if ( distortionTypeCrusher ) { 63 | bitCrusher->process( channelMixBuffer, bufferSize ); 64 | } else { 65 | waveShaper->process( channelMixBuffer, bufferSize ); 66 | } 67 | } 68 | 69 | // write the effected mix buffers into the output buffer 70 | // note here we convert the double values to whatever SampleType is 71 | 72 | for ( size_t i = 0; i < bufferSize; ++i ) { 73 | 74 | // before writing to the out buffer we take a snapshot of the current in sample 75 | // value as VST2 in Ableton Live supplies the same buffer for in and out! 76 | // in case we want to offer a wet/dry balance 77 | //inSample = channelInBuffer[ i ]; 78 | 79 | // wet mix (e.g. the effected signal) 80 | channelOutBuffer[ i ] = ( SampleType ) channelMixBuffer[ i ]; 81 | } 82 | } 83 | // limit the output signal as it can get quite hot 84 | limiter->process( outBuffer, bufferSize, numOutChannels ); 85 | } 86 | 87 | template 88 | void PluginProcess::prepareMixBuffers( SampleType** inBuffer, int numInChannels, int bufferSize ) 89 | { 90 | // if the pre mix buffer wasn't created yet or the buffer size has changed 91 | // delete existing buffer and create new one to match properties 92 | 93 | if ( _mixBuffer == nullptr || _mixBuffer->bufferSize != bufferSize ) { 94 | delete _mixBuffer; 95 | _mixBuffer = new AudioBuffer( numInChannels, bufferSize ); 96 | } 97 | 98 | // clone the in buffer contents 99 | // note the clone is always cast to double as it is 100 | // used for internal processing (see PluginProcess::process) 101 | 102 | for ( int c = 0; c < numInChannels; ++c ) { 103 | 104 | SampleType* inChannelBuffer = ( SampleType* ) inBuffer[ c ]; 105 | auto channelMixBuffer = ( double* ) _mixBuffer->getBufferForChannel( c ); 106 | 107 | for ( int i = 0; i < bufferSize; ++i ) { 108 | // clone into the pre mix buffer for pre-processing 109 | channelMixBuffer[ i ] = ( double ) inChannelBuffer[ i ]; 110 | } 111 | } 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /src/audiobuffer.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2013-2020 Igor Zinken - https://www.igorski.nl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | #include "audiobuffer.h" 24 | #include 25 | #include 26 | 27 | AudioBuffer::AudioBuffer( int aAmountOfChannels, int aBufferSize ) 28 | { 29 | loopeable = false; 30 | amountOfChannels = aAmountOfChannels; 31 | bufferSize = aBufferSize; 32 | 33 | // create silent buffers for each channel 34 | 35 | _buffers = new std::vector( amountOfChannels ); 36 | 37 | // fill buffers with silence 38 | 39 | for ( int i = 0; i < amountOfChannels; ++i ) { 40 | _buffers->at( i ) = new double[ aBufferSize ]; 41 | memset( _buffers->at( i ), 0, aBufferSize * sizeof( double )); // zero bits should equal 0.f 42 | } 43 | } 44 | 45 | AudioBuffer::~AudioBuffer() 46 | { 47 | while ( !_buffers->empty()) { 48 | delete[] _buffers->back(), _buffers->pop_back(); 49 | } 50 | delete _buffers; 51 | } 52 | 53 | /* public methods */ 54 | 55 | double* AudioBuffer::getBufferForChannel( int aChannelNum ) 56 | { 57 | return _buffers->at( aChannelNum ); 58 | } 59 | 60 | int AudioBuffer::mergeBuffers( AudioBuffer* aBuffer, int aReadOffset, int aWriteOffset, float aMixVolume ) 61 | { 62 | if ( aBuffer == 0 || aWriteOffset >= bufferSize ) 63 | return 0; 64 | 65 | int sourceLength = aBuffer->bufferSize; 66 | int maxSourceChannel = aBuffer->amountOfChannels - 1; 67 | int writeLength = bufferSize; 68 | int writtenSamples = 0; 69 | 70 | // keep writes within the bounds of this buffer 71 | 72 | if (( aWriteOffset + writeLength ) >= bufferSize ) 73 | writeLength = bufferSize - aWriteOffset; 74 | 75 | int maxWriteOffset = aWriteOffset + writeLength; 76 | int c; 77 | 78 | for ( c = 0; c < amountOfChannels; ++c ) 79 | { 80 | if ( c > maxSourceChannel ) 81 | break; 82 | 83 | auto srcBuffer = aBuffer->getBufferForChannel( c ); 84 | auto targetBuffer = getBufferForChannel( c ); 85 | 86 | for ( int i = aWriteOffset, r = aReadOffset; i < maxWriteOffset; ++i, ++r ) 87 | { 88 | if ( r >= sourceLength ) 89 | { 90 | if ( aBuffer->loopeable ) 91 | r = 0; 92 | else 93 | break; 94 | } 95 | targetBuffer[ i ] += ( srcBuffer[ r ] * aMixVolume ); 96 | ++writtenSamples; 97 | } 98 | } 99 | // return the amount of samples written (per buffer) 100 | return ( c == 0 ) ? writtenSamples : writtenSamples / c; 101 | } 102 | 103 | /** 104 | * fills the buffers with silence 105 | * clearing their previous contents 106 | */ 107 | void AudioBuffer::silenceBuffers() 108 | { 109 | // use mem set to quickly erase existing buffer contents, zero bits should equal 0.f 110 | for ( int i = 0; i < amountOfChannels; ++i ) 111 | memset( getBufferForChannel( i ), 0, bufferSize * sizeof( double )); 112 | } 113 | 114 | void AudioBuffer::adjustBufferVolumes( float amp ) 115 | { 116 | for ( int i = 0; i < amountOfChannels; ++i ) 117 | { 118 | auto buffer = getBufferForChannel( i ); 119 | 120 | for ( int j = 0; j < bufferSize; ++j ) 121 | buffer[ j ] *= amp; 122 | } 123 | } 124 | 125 | bool AudioBuffer::isSilent() 126 | { 127 | for ( int i = 0; i < amountOfChannels; ++i ) 128 | { 129 | auto buffer = getBufferForChannel( i ); 130 | for ( int j = 0; j < bufferSize; ++j ) 131 | { 132 | if ( buffer[ j ] != 0.f ) 133 | return false; 134 | } 135 | } 136 | return true; 137 | } 138 | 139 | AudioBuffer* AudioBuffer::clone() 140 | { 141 | AudioBuffer* output = new AudioBuffer( amountOfChannels, bufferSize ); 142 | 143 | for ( int i = 0; i < amountOfChannels; ++i ) 144 | { 145 | auto sourceBuffer = getBufferForChannel( i ); 146 | auto targetBuffer = output->getBufferForChannel( i ); 147 | 148 | memcpy( targetBuffer, sourceBuffer, bufferSize * sizeof( double )); 149 | } 150 | return output; 151 | } 152 | -------------------------------------------------------------------------------- /src/ui/uimessagecontroller.h: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020-2023 Igor Zinken - https://www.igorski.nl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | #ifndef __MESSAGE_CONTROLLER_HEADER__ 24 | #define __MESSAGE_CONTROLLER_HEADER__ 25 | 26 | #include "vstgui/lib/iviewlistener.h" 27 | #include "vstgui/uidescription/icontroller.h" 28 | #include "public.sdk/source/vst/utility/stringconvert.h" 29 | 30 | //------------------------------------------------------------------------ 31 | namespace Steinberg { 32 | namespace Vst { 33 | 34 | //------------------------------------------------------------------------ 35 | // PluginUIMessageController 36 | //------------------------------------------------------------------------ 37 | template 38 | class PluginUIMessageController : public VSTGUI::IController, public VSTGUI::ViewListenerAdapter 39 | { 40 | public: 41 | enum Tags 42 | { 43 | kSendMessageTag = 1000 // see plugin.uidesc tags 44 | }; 45 | 46 | PluginUIMessageController( ControllerType* pluginController ) : pluginController( pluginController ), textEdit( nullptr ) 47 | { 48 | } 49 | 50 | ~PluginUIMessageController() 51 | { 52 | viewWillDelete( textEdit ); 53 | pluginController->removeUIMessageController( this ); 54 | } 55 | 56 | void setMessageText( String128 msgText ) 57 | { 58 | if ( !textEdit ) 59 | return; 60 | 61 | textEdit->setText( VST3::StringConvert::convert( msgText )); 62 | } 63 | 64 | private: 65 | typedef VSTGUI::CControl CControl; 66 | typedef VSTGUI::CView CView; 67 | typedef VSTGUI::CTextEdit CTextEdit; 68 | typedef VSTGUI::UTF8String UTF8String; 69 | 70 | //--- from IControlListener ---------------------- 71 | void valueChanged( CControl* /*pControl*/ ) override {} 72 | void controlBeginEdit( CControl* /*pControl*/ ) override {} 73 | void controlEndEdit( CControl* pControl ) override 74 | { 75 | if ( pControl->getTag () == kSendMessageTag ) 76 | { 77 | if ( pControl->getValueNormalized () > 0.5f ) 78 | { 79 | pluginController->sendTextMessage( textEdit->getText ().data() ); 80 | pControl->setValue( 0.f ); 81 | pControl->invalid(); 82 | 83 | //---send a binary message 84 | if ( IPtr message = owned( pluginController->allocateMessage())) 85 | { 86 | message->setMessageID ("BinaryMessage"); 87 | uint32 size = 100; 88 | char8 data[100]; 89 | memset( data, 0, size * sizeof( char )); 90 | // fill my data with dummy stuff 91 | for ( uint32 i = 0; i < size; i++ ) 92 | data[ i ] = i; 93 | message->getAttributes ()->setBinary( "MyData", data, size ); 94 | pluginController->sendMessage( message ); 95 | } 96 | } 97 | } 98 | } 99 | //--- from IControlListener ---------------------- 100 | //--- is called when a view is created ----- 101 | CView* verifyView ( CView* view, const UIAttributes& /*attributes*/, 102 | const IUIDescription* /*description*/ ) override 103 | { 104 | if ( CTextEdit* te = dynamic_cast( view )) 105 | { 106 | // this allows us to keep a pointer of the text edit view 107 | textEdit = te; 108 | 109 | // add this as listener in order to get viewWillDelete and viewLostFocus calls 110 | textEdit->registerViewListener (this); 111 | 112 | // set its contents 113 | textEdit->setText( VST3::StringConvert::convert( pluginController->getDefaultMessageText())); 114 | } 115 | return view; 116 | } 117 | //--- from IViewListenerAdapter ---------------------- 118 | //--- is called when a view will be deleted: the editor is closed ----- 119 | void viewWillDelete (CView* view) override 120 | { 121 | if (dynamic_cast (view) == textEdit) 122 | { 123 | textEdit->unregisterViewListener (this); 124 | textEdit = nullptr; 125 | } 126 | } 127 | //--- is called when the view is unfocused ----------------- 128 | void viewLostFocus (CView* view) override 129 | { 130 | if (dynamic_cast (view) == textEdit) 131 | { 132 | // save the last content of the text edit view 133 | const auto& text = textEdit->getText(); 134 | auto utf16Text = VST3::StringConvert::convert( text.getString()); 135 | pluginController->setDefaultMessageText( utf16Text.data()); 136 | } 137 | } 138 | ControllerType* pluginController; 139 | CTextEdit* textEdit; 140 | }; 141 | 142 | //------------------------------------------------------------------------ 143 | } // Vst 144 | } // Igorski 145 | 146 | #endif 147 | -------------------------------------------------------------------------------- /mac/audio-unit/src/ViewController.m: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // Project : VST SDK 3 | // 4 | // Category : Helpers 5 | // Filename : 6 | // Created by : Steinberg, 07/2017. 7 | // Description : VST 3 AUv3Wrapper 8 | // 9 | //----------------------------------------------------------------------------- 10 | // LICENSE 11 | // (c) 2022, Steinberg Media Technologies GmbH, All Rights Reserved 12 | //----------------------------------------------------------------------------- 13 | // Redistribution and use in source and binary forms, with or without modification, 14 | // are permitted provided that the following conditions are met: 15 | // 16 | // * Redistributions of source code must retain the above copyright notice, 17 | // this list of conditions and the following disclaimer. 18 | // * Redistributions in binary form must reproduce the above copyright notice, 19 | // this list of conditions and the following disclaimer in the documentation 20 | // and/or other materials provided with the distribution. 21 | // * Neither the name of the Steinberg Media Technologies nor the names of its 22 | // contributors may be used to endorse or promote products derived from this 23 | // software without specific prior written permission. 24 | // 25 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 26 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 27 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 28 | // IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 29 | // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 30 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 32 | // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 33 | // OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | // OF THE POSSIBILITY OF SUCH DAMAGE. 35 | //----------------------------------------------------------------------------- 36 | #import "ViewController.h" 37 | #import 38 | #import "public.sdk/source/vst/auv3wrapper/Shared/AUv3AudioEngine.h" 39 | #import "public.sdk/source/vst/auv3wrapper/Shared/AUv3Wrapper.h" 40 | 41 | @class AUv3WrapperViewController; 42 | 43 | @interface ViewController () 44 | { 45 | // Button for playback 46 | IBOutlet NSButton* playButton; 47 | 48 | AUv3AudioEngine* audioEngine; 49 | 50 | // Container for the custom view. 51 | AUv3WrapperViewController* auV3ViewController; 52 | } 53 | 54 | @property IBOutlet NSView *containerView; 55 | -(IBAction)togglePlay:(id)sender; 56 | -(void)handleMenuSelection:(id)sender; 57 | 58 | @end 59 | 60 | @implementation ViewController 61 | //------------------------------------------------------------------------ 62 | - (void)viewDidLoad 63 | { 64 | [super viewDidLoad]; 65 | 66 | // Do any additional setup after loading the view. 67 | [self embedPlugInView]; 68 | 69 | AudioComponentDescription desc; 70 | 71 | desc.componentType = kAUcomponentType; 72 | desc.componentSubType = kAUcomponentSubType; 73 | desc.componentManufacturer = kAUcomponentManufacturer; 74 | desc.componentFlags = kAUcomponentFlags; 75 | desc.componentFlagsMask = kAUcomponentFlagsMask; 76 | 77 | if (desc.componentType == 'aufx' || desc.componentType == 'aumf') 78 | [self addFileMenuEntry]; 79 | 80 | [AUAudioUnit registerSubclass: AUv3Wrapper.class asComponentDescription:desc name:@"Local AUv3" version: UINT32_MAX]; 81 | 82 | audioEngine = [[AUv3AudioEngine alloc] initWithComponentType:desc.componentType]; 83 | 84 | [audioEngine loadAudioUnitWithComponentDescription:desc completion:^{ 85 | auV3ViewController.audioUnit = (AUv3Wrapper*)audioEngine.currentAudioUnit; 86 | 87 | NSString* fileName = @kAudioFileName; 88 | NSString* fileFormat = @kAudioFileFormat; 89 | NSURL* fileURL = [[NSBundle mainBundle] URLForResource:fileName withExtension:fileFormat]; 90 | NSError* error = [audioEngine loadAudioFile:fileURL]; 91 | if (error) 92 | { 93 | NSLog (@"Error setting up audio or midi file: %@", [error description]); 94 | } 95 | }]; 96 | } 97 | 98 | //------------------------------------------------------------------------ 99 | - (void)embedPlugInView 100 | { 101 | NSURL *builtInPlugInURL = [[NSBundle mainBundle] builtInPlugInsURL]; 102 | NSURL *pluginURL = [builtInPlugInURL URLByAppendingPathComponent: @"vst3plugin.appex"]; 103 | NSBundle *appExtensionBundle = [NSBundle bundleWithURL: pluginURL]; 104 | 105 | auV3ViewController = [[AUv3WrapperViewController alloc] initWithNibName: @"AUv3WrapperViewController" bundle: appExtensionBundle]; 106 | 107 | // Present the view controller's view. 108 | NSView *view = auV3ViewController.view; 109 | view.frame = _containerView.bounds; 110 | 111 | [_containerView addSubview: view]; 112 | 113 | view.translatesAutoresizingMaskIntoConstraints = NO; 114 | 115 | NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat: @"H:|-[view]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(view)]; 116 | [_containerView addConstraints: constraints]; 117 | 118 | constraints = [NSLayoutConstraint constraintsWithVisualFormat: @"V:|-[view]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(view)]; 119 | [_containerView addConstraints: constraints]; 120 | } 121 | 122 | //------------------------------------------------------------------------ 123 | -(void)addFileMenuEntry 124 | { 125 | NSApplication *app = [NSApplication sharedApplication]; 126 | NSMenu *fileMenu = [[app.mainMenu itemWithTag:123] submenu]; 127 | 128 | NSMenuItem *openFileItem = [[NSMenuItem alloc] initWithTitle:@"Load file..." 129 | action:@selector(handleMenuSelection:) 130 | keyEquivalent:@"O"]; 131 | [fileMenu insertItem:openFileItem atIndex:0]; 132 | } 133 | 134 | //------------------------------------------------------------------------ 135 | -(void)handleMenuSelection:(NSMenuItem *)sender 136 | { 137 | // create the open dialog 138 | NSOpenPanel* openPanel = [NSOpenPanel openPanel]; 139 | openPanel.title = @"Choose an audio file"; 140 | openPanel.showsResizeIndicator = YES; 141 | openPanel.canChooseFiles = YES; 142 | openPanel.allowsMultipleSelection = NO; 143 | openPanel.canChooseDirectories = NO; 144 | openPanel.canCreateDirectories = YES; 145 | openPanel.allowedFileTypes = @[@"aac", @"aif", @"aiff", @"caf", @"m4a", @"mp3", @"wav"]; 146 | 147 | if ( [openPanel runModal] == NSModalResponseOK ) 148 | { 149 | NSArray* urls = [openPanel URLs]; 150 | 151 | // Loop through all the files and process them. 152 | for(int i = 0; i < [urls count]; i++ ) 153 | { 154 | NSError* error = [audioEngine loadAudioFile:[urls objectAtIndex:i]]; 155 | 156 | if (error != nil) 157 | { 158 | NSAlert *alert = [[NSAlert alloc] init]; 159 | [alert setMessageText:@"Error loading file"]; 160 | [alert setInformativeText:@"Something went wrong loading the audio file. Please make sure to select the correct format and try again."]; 161 | [alert addButtonWithTitle:@"Ok"]; 162 | [alert runModal]; 163 | } 164 | } 165 | } 166 | } 167 | 168 | //------------------------------------------------------------------------ 169 | -(IBAction)togglePlay:(id)sender 170 | { 171 | BOOL isPlaying = [audioEngine startStop]; 172 | 173 | [playButton setTitle: isPlaying ? @"Stop" : @"Play"]; 174 | } 175 | 176 | #pragma mark 177 | //------------------------------------------------------------------------ 178 | - (void)windowWillClose:(NSNotification *)notification 179 | { 180 | // Main applicaiton window closing, we're done 181 | auV3ViewController = nil; 182 | } 183 | @end 184 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TRANSFORMANT 2 | 3 | Transformant is a VST/AU plug-in which provides a stereo formant filter effect, driven by oscillators and obliterated to gravel through bit crushing or wave shaping. 4 | 5 | You can hear it being the leading effect on Drosophelia's [Detractor](https://songwhip.com/drosophelia/detractor). 6 | 7 | ## Build instructions 8 | 9 | The project uses [CMake](https://cmake.org) to generate the Makefiles and has been built and tested on macOS, Windows 10 and Linux (Ubuntu). 10 | 11 | ### Environment setup 12 | 13 | Apart from requiring _CMake_ and a C(++) compiler such as _Clang_ or _MSVC_, the only other dependency is the [VST SDK from Steinberg](https://www.steinberg.net/en/company/developers.html) (the projects latest update requires SDK version 3.7.11). 14 | 15 | #### Setting up the easy way : installing a local version of the Steinberg SDK 16 | 17 | You can instantly retrieve and build the SDK using the following commands. 18 | 19 | ##### Installation on Unix: 20 | 21 | ``` 22 | sh setup.sh --platform PLATFORM 23 | ``` 24 | 25 | Where optional flag _--platform_ can be either `mac` or `linux` (defaults to linux). 26 | 27 | Linux users might be interested in the [required packages](https://steinbergmedia.github.io/vst3_dev_portal/pages/Getting+Started/How+to+setup+my+system.html#for-linux). 28 | 29 | ##### Installation on Windows: 30 | 31 | ``` 32 | setup.bat 33 | ``` 34 | 35 | This will create a (Git ignored) subfolder in this repository folder with a prebuilt Steinberg SDK. 36 | 37 | #### Setting up the flexible way : pointing towards an external SDK build / supporting VST2 38 | 39 | In case you wish to use a different SDK version (for instance to reuse an existing build elsewhere on your computer or to 40 | target VST2 builds), you can invoke all build scripts by providing the `VST3_SDK_ROOT` environment variable, like so: 41 | 42 | ``` 43 | VST3_SDK_ROOT=/path/to/prebuilt/VST3_SDK sh build.sh 44 | ``` 45 | 46 | After downloading the Steinberg SDK you must generate a release build of its sources. To do this, execute the following commands from the root of the Steinberg SDK folder: 47 | 48 | ``` 49 | cd vst3sdk 50 | mkdir build 51 | cd build 52 | cmake -DCMAKE_BUILD_TYPE=Release .. 53 | cmake --build . --config Release 54 | ``` 55 | 56 | The result being that `{VST3_SDK_ROOT}/build/lib/Release/` will contain the Steinberg VST libraries required to build the plugin. 57 | 58 | In case you intend to build VST2 versions as well, keep in mind that as of SDK 3.6.11, Steinberg no longer packages the required `/pluginterfaces/vst2.x`-folder inside the vst3sdk folder. If you wish to build a VST2 plugin, copying the folder from an older SDK version _could_ work (verified 3.6.9. `vst2.x` folders to work with SDK 3.7.11), though be aware that you _need a license to target VST2_. You can view [Steinbergs rationale on this decision here](https://helpcenter.steinberg.de/hc/en-us/articles/4409561018258-VST-2-Discontinued). 59 | 60 | To prepare for building VST2 versions of the plugin, run the following from the root of the Steinberg SDK folder (run the _.bat_ version instead of the _.sh_ version on Windows) prior to building the library: 61 | 62 | ``` 63 | ./copy_vst2_to_vst3_sdk.sh 64 | ``` 65 | 66 | And if you are running Linux, you can easily resolve all dependencies by first running the following from the root of the Steinberg SDK folder: 67 | 68 | ``` 69 | ./tools/setup_linux_packages_for_vst3sdk.sh 70 | ``` 71 | 72 | ### Building the plugin 73 | 74 | See the provided shell scripts. The build output will be stored in `./build/VST3/transformant.vst3` as well as symbolically linked to your systems VST-plugin folder (on Unix). 75 | 76 | #### Compiling on Unix systems: 77 | 78 | ``` 79 | sh build.sh --type TYPE 80 | ``` 81 | 82 | Where optional flag _--type_ can be either `vst3`, `vst2` or `au` (defaults to vst3)*. 83 | 84 | #### Compiling on Windows: 85 | 86 | Assuming the Visual Studio Build Tools have been installed: 87 | 88 | ``` 89 | build.bat 90 | ``` 91 | 92 | Where you can optionally append `vst2` to the command to build a VST2* plugin. 93 | 94 | _*As mentioned in the "setup" section, VST2 builds are not supported out-of-the-box._ 95 | 96 | ## On compatibility 97 | 98 | ### Compiling for both 32-bit and 64-bit architectures 99 | 100 | Depending on your host software having 32-bit or 64-bit support (with the latter targeting either Intel or ARM), you can choose to compile for a wider range of architectures. To do so, updating the build shell scripts/batch files to contain the following: 101 | 102 | **macOS:** 103 | 104 | ``` 105 | cmake -"DCMAKE_OSX_ARCHITECTURES=x86_64;arm64;i1386" .. 106 | ``` 107 | 108 | Which will allow you to compile a single, "fat" binary that supports all architectures (Intel, ARM and legacy 32-bit Intel). Note 109 | that by default compilation is for 64-bit architecture for both Intel and ARM CPU's, _you can likely ignore this section_. 110 | 111 | **Windows:** 112 | 113 | ``` 114 | cmake.exe -G "Visual Studio 16 2019" -A Win64 -S .. -B "build64" 115 | cmake.exe --build build64 --config Release 116 | 117 | cmake.exe -G "Visual Studio 16 2019" -A Win32 -S .. -B "build32" 118 | cmake.exe --build build32 --config Release 119 | ``` 120 | 121 | Which is a little more cumbersome as you compile separate binaries for the separate architectures. 122 | 123 | Note that the above also needs to be done when building the Steinberg SDK (which for the Windows build implies that a separate build is created for each architecture). 124 | 125 | While both macOS and Windows have been fully 64-bit for the past versions, building for 32-bit provides the best backward 126 | compatibility for older OS versions. Musicians are known to keep working systems at the cost of not 127 | running an up to date system... _still, you can likely ignore this section_. 128 | 129 | ### Build as Audio Unit (macOS only) 130 | 131 | For this you will need a little extra preparation while building Steinberg SDK as you will need the 132 | "[CoreAudio SDK](https://developer.apple.com/library/archive/samplecode/CoreAudioUtilityClasses/Introduction/Intro.html)" and XCode. Execute the following instructions to build the SDK with Audio Unit support, 133 | providing the appropriate path to the actual installation location of the CoreAudio SDK: 134 | 135 | ``` 136 | sh setup.sh --platform mac --coresdk /path/to/CoreAudioUtilityClasses/CoreAudio 137 | ``` 138 | 139 | After which you can run the build script like so: 140 | 141 | ``` 142 | sh build.sh --type au 143 | ``` 144 | 145 | The Audio Unit component will be located in `./build/bin/Release/Transformant AUv3.app` 146 | 147 | You can validate the Audio Unit using Apple's _auval_ utility, by running `auval -v aufx frmt IGOR` on the command line (reflecting the values defined in `audiounitconfig.h`). Note that there is the curious behaviour that you might need to reboot before the plugin shows up, though you can force a flush of the Audio Unit cache at runtime by running `killall -9 AudioComponentRegistrar`. 148 | 149 | In case of errors you can look for instances of [kAudioUnitErr](https://www.osstatus.com/search/results?platform=all&framework=all&search=kaudiouniterr) 150 | 151 | ### Running the plugin 152 | 153 | You can copy the build output into your system VST(3) folder and run it directly in a VST host / DAW of your choice. 154 | 155 | When debugging, you can also choose to run the plugin against Steinbergs validator and editor host utilities: 156 | 157 | ``` 158 | {VST3_SDK_ROOT}/build/bin/validator build/VST3/transformant.vst3 159 | {VST3_SDK_ROOT}/build/bin/editorhost build/VST3/transformant.vst3 160 | ``` 161 | 162 | ### Signing the plugin on macOS 163 | 164 | You will need to have your code signing set up appropriately. Assuming you have set up your Apple Developer account, you can find your signing identity like so: 165 | 166 | ``` 167 | security find-identity -p codesigning -v 168 | ``` 169 | 170 | From which you can take your name and team id and pass them to the build script like so: 171 | 172 | ``` 173 | sh build.sh --team_id TEAM_ID --identity "YOUR_NAME" 174 | ``` -------------------------------------------------------------------------------- /src/formantfilter.h: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020 Igor Zinken - https://www.igorski.nl 5 | * 6 | * Adapted from public source code by Paul Sernine, based on work by Thierry Rochebois 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | * this software and associated documentation files (the "Software"), to deal in 10 | * the Software without restriction, including without limitation the rights to 11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 12 | * the Software, and to permit persons to whom the Software is furnished to do so, 13 | * subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 20 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 21 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 22 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | #ifndef __FORMANTFILTER_H_INCLUDED__ 26 | #define __FORMANTFILTER_H_INCLUDED__ 27 | 28 | #include "lfo.h" 29 | #include "calc.h" 30 | #include 31 | 32 | namespace Igorski { 33 | class FormantFilter 34 | { 35 | static const int VOWEL_AMOUNT = 4; 36 | static const int COEFF_AMOUNT = 9; 37 | static const int FORMANT_TABLE_SIZE = (256+1); // The last entry of the table equals the first (to avoid a modulo) 38 | static const int MAX_FORMANT_WIDTH = 64; 39 | static constexpr double ATTENUATOR = 0.0005; 40 | 41 | // hard coded values for dynamics processing, in -1 to +1 range 42 | 43 | static constexpr double DYNAMICS_THRESHOLD = 0.10; 44 | static constexpr double DYNAMICS_RATIO = 0.50; 45 | static constexpr double DYNAMICS_LEVEL = 0.65; 46 | static constexpr double DYNAMICS_ATTACK = 0.18; 47 | static constexpr double DYNAMICS_RELEASE = 0.55; 48 | static constexpr double DYNAMICS_LIMITER_DYNAMICS_THRESHOLD = 0.99; 49 | static constexpr double DYNAMICS_GATE_DYNAMICS_THRESHOLD = 0.02; 50 | static constexpr double DYNAMICS_GATE_DYNAMICS_ATTACK = 0.10; 51 | static constexpr double DYNAMICS_GATE_DECAY = 0.50; 52 | static constexpr double DYNAMICS_MIX = 1.00; 53 | 54 | // whether to apply the formant synthesis to the signal 55 | // otherwise the input is applied to the carrier directly 56 | 57 | static const bool APPLY_SYNTHESIS_SIGNAL = false; 58 | 59 | public: 60 | FormantFilter( float aVowel, float sampleRate ); 61 | ~FormantFilter(); 62 | 63 | void setVowel( float aVowel ); 64 | float getVowel(); 65 | void setLFO( float LFORatePercentage, float LFODepth ); 66 | void process( double* inBuffer, int bufferSize ); 67 | 68 | LFO* lfo; 69 | bool hasLFO; 70 | 71 | private: 72 | 73 | float _sampleRate; 74 | float _halfSampleRateFrac; 75 | double _vowel; 76 | double _tempVowel; 77 | int _coeffOffset; 78 | float _lfoDepth; 79 | double _lfoRange; 80 | double _lfoMax; 81 | double _lfoMin; 82 | 83 | void cacheLFO(); 84 | inline void cacheCoeffOffset() 85 | { 86 | _coeffOffset = ( int ) Calc::scale( _tempVowel, 1.f, ( float ) COEFF_AMOUNT - 1 ); 87 | } 88 | 89 | // vowel definitions 90 | 91 | struct Formant { 92 | double value; 93 | double coeffs[ COEFF_AMOUNT ]; 94 | }; 95 | 96 | double FORMANT_WIDTH_SCALE[ VOWEL_AMOUNT ] = { 100, 120, 150, 300 }; 97 | 98 | Formant A_COEFFICIENTS[ VOWEL_AMOUNT ] = { 99 | { 0.0, { 1.0, 0.5, 1.0, 1.0, 0.7, 1.0, 1.0, 0.3, 1.0 } }, 100 | { 0.0, { 2.0, 0.5, 0.7, 0.7,0.35, 0.3, 0.5, 1.0, 0.7 } }, 101 | { 0.0, { 0.3,0.15, 0.2, 0.4, 0.1, 0.3, 0.7, 0.2, 0.2 } }, 102 | { 0.0, { 0.2, 0.1, 0.2, 0.3, 0.1, 0.1, 0.3, 0.2, 0.3 } } 103 | }; 104 | 105 | Formant F_COEFFICIENTS[ VOWEL_AMOUNT ] = { 106 | { 100.0, { 730, 200, 400, 250, 190, 350, 550, 550, 450 } }, 107 | { 100.0, { 1090, 2100, 900, 1700, 800, 1900, 1600, 850, 1100 } }, 108 | { 100.0, { 2440, 3100, 2300, 2100, 2000, 2500, 2250, 1900, 1500 } }, 109 | { 100.0, { 3400, 4700, 3000, 3300, 3400, 3700, 3200, 3000, 3000 } } 110 | }; 111 | 112 | // the below are used for the formant synthesis 113 | 114 | double FORMANT_TABLE[ FORMANT_TABLE_SIZE * MAX_FORMANT_WIDTH ]; 115 | double _phase = 0.0; 116 | 117 | double generateFormant( double phase, const double width ); 118 | double getFormant( double phase, double width ); 119 | double getCarrier( const double position, const double phase ); 120 | 121 | // Fast approximation of cos( pi * x ) for x in -1 to +1 range 122 | 123 | inline double fast_cos( const double x ) 124 | { 125 | double x2 = x * x; 126 | return 1 + x2 * ( -4 + 2 * x2 ); 127 | } 128 | 129 | // dynamics processing (compression and limiting to keep vowel level constant) 130 | 131 | inline double compress( double sample ) 132 | { 133 | double a, b, i, j, g, out; 134 | double e = _dEnv, 135 | e2 = _dEnv2, 136 | ge = _dGainEnv, 137 | re = ( 1.f - _dRelease ), 138 | lth = _dLimThreshold; 139 | 140 | if ( _fullDynamicsProcessing ) { 141 | 142 | // apply compression, gating and limiting 143 | 144 | if ( lth == 0.f ) { 145 | lth = 1000.f; 146 | } 147 | a = sample; 148 | i = ( a < 0.f ) ? -a : a; 149 | 150 | e = ( i > e ) ? e + _dAttack * ( i - e ) : e * re; 151 | e2 = ( i > e ) ? i : e2 * re; // ir; 152 | 153 | g = ( e > _dThreshold ) ? _dTrim / ( 1.f + _dRatio * (( e / _dThreshold ) - 1.f )) : _dTrim; 154 | 155 | if ( g < 0.f ) { 156 | g = 0.f; 157 | } 158 | if ( g * e2 > lth ) { 159 | g = lth / e2; // limiting 160 | } 161 | ge = ( e > _dExpThreshold ) ? ge + _dGateAttack - _dGateAttack * ge : ge * _dExpRatio; // gating 162 | out = a * ( g * ge + _dDry ); 163 | } 164 | else { 165 | // compression only 166 | a = sample; 167 | i = ( a < 0.f ) ? -a : a; 168 | 169 | e = ( i > e ) ? e + _dAttack * ( i - e ) : e * re; // envelope 170 | g = ( e > _dThreshold ) ? _dTrim / ( 1.f + _dRatio * (( e / _dThreshold ) - 1.f )) : _dTrim; // gain 171 | 172 | out = a * ( g + _dDry ); // VCA 173 | } 174 | 175 | // catch denormals 176 | 177 | _dEnv = ( e < 1.0e-10 ) ? 0.0 : e; 178 | _dEnv2 = ( e2 < 1.0e-10 ) ? 0.0 : e2; 179 | _dGainEnv = ( ge < 1.0e-10 ) ? 0.0 : ge; 180 | 181 | return out; 182 | } 183 | 184 | void cacheDynamicsProcessing(); 185 | 186 | double _dThreshold; 187 | double _dRatio; 188 | double _dAttack; 189 | double _dRelease; 190 | double _dTrim; 191 | double _dLimThreshold; 192 | double _dExpThreshold; 193 | double _dExpRatio; 194 | double _dDry; 195 | double _dEnv; 196 | double _dEnv2; 197 | double _dGainEnv; 198 | double _dGateAttack; 199 | bool _fullDynamicsProcessing; 200 | 201 | }; 202 | } 203 | 204 | #endif 205 | -------------------------------------------------------------------------------- /src/formantfilter.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020 Igor Zinken - https://www.igorski.nl 5 | * 6 | * Adapted from public source code by Paul Sernine, based on work by Thierry Rochebois 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | * this software and associated documentation files (the "Software"), to deal in 10 | * the Software without restriction, including without limitation the rights to 11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 12 | * the Software and to permit persons to whom the Software is furnished to do so, 13 | * subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 20 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 21 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 22 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | #include "formantfilter.h" 26 | #include 27 | 28 | namespace Igorski { 29 | 30 | /* constructor / destructor */ 31 | 32 | FormantFilter::FormantFilter( float aVowel, float sampleRate ) 33 | { 34 | double coeff = 2.0 / ( FORMANT_TABLE_SIZE - 1 ); 35 | 36 | for ( size_t i = 0; i < MAX_FORMANT_WIDTH; i++ ) 37 | { 38 | for ( size_t j = 0; j < FORMANT_TABLE_SIZE; j++ ) { 39 | FORMANT_TABLE[ j + i * FORMANT_TABLE_SIZE ] = generateFormant( -1 + j * coeff, double( i )); 40 | } 41 | } 42 | 43 | _sampleRate = sampleRate; 44 | _halfSampleRateFrac = 1.f / ( _sampleRate * 0.5f ); 45 | 46 | setVowel( aVowel ); 47 | cacheDynamicsProcessing(); 48 | 49 | // note: LFO is always "on" as its used by the formant synthesis 50 | // when we want the audible oscillation of vowels to stop, the LFO 51 | // depth is merely at 0 52 | 53 | lfo = new LFO( _sampleRate ); 54 | setLFO( 0.f, 0.f ); 55 | } 56 | 57 | FormantFilter::~FormantFilter() 58 | { 59 | delete lfo; 60 | } 61 | 62 | /* public methods */ 63 | 64 | float FormantFilter::getVowel() 65 | { 66 | return ( float ) _vowel; 67 | } 68 | 69 | void FormantFilter::setVowel( float aVowel ) 70 | { 71 | _vowel = ( double ) aVowel; 72 | 73 | double tempRatio = _tempVowel / std::max( 0.000000001, _vowel ); 74 | 75 | // in case FormantFilter is attached to oscillator, keep relative offset 76 | // of currently moving vowel in place 77 | _tempVowel = ( hasLFO ) ? _vowel * tempRatio : _vowel; 78 | 79 | cacheCoeffOffset(); 80 | cacheLFO(); 81 | } 82 | 83 | void FormantFilter::setLFO( float LFORatePercentage, float LFODepth ) 84 | { 85 | bool isLFOenabled = LFORatePercentage > 0.f; 86 | bool wasChanged = hasLFO != isLFOenabled || _lfoDepth != LFODepth; 87 | 88 | hasLFO = isLFOenabled; 89 | 90 | lfo->setRate( 91 | VST::MIN_LFO_RATE() + ( 92 | LFORatePercentage * ( VST::MAX_LFO_RATE() - VST::MIN_LFO_RATE() ) 93 | ) 94 | ); 95 | 96 | if ( wasChanged ) { 97 | _lfoDepth = LFODepth; 98 | cacheLFO(); 99 | } 100 | } 101 | 102 | void FormantFilter::process( double* inBuffer, int bufferSize ) 103 | { 104 | float lfoValue; 105 | double in, out, fp, ufp, phaseAcc, formant, carrier; 106 | 107 | for ( size_t i = 0; i < bufferSize; ++i ) 108 | { 109 | in = inBuffer[ i ]; 110 | out = 0.0; 111 | 112 | // sweep the LFO 113 | 114 | lfoValue = lfo->peek() * .5f + .5f; // make waveform unipolar 115 | _tempVowel = std::min( _lfoMax, _lfoMin + _lfoRange * lfoValue ); // relative to LFO depth 116 | 117 | cacheCoeffOffset(); // ensure the appropriate coeff is used for the new _tempVowel value 118 | 119 | // calculate the phase for the formant synthesis and carrier 120 | 121 | fp = 12 * powf( 2.0, 4 - 4 * _tempVowel ); // sweep 122 | // fp *= ( 1.0 + 0.01 * sinf( tmp * 0.0015 )); // optional vibrato (sinf value determines speed) 123 | ufp = 1.0 / fp; 124 | 125 | phaseAcc = fp * _halfSampleRateFrac; 126 | _phase += phaseAcc; 127 | _phase -= 2 * ( _phase > 1 ); 128 | 129 | // calculate the coefficients 130 | 131 | for ( size_t j = 0; j < VOWEL_AMOUNT; ++j ) 132 | { 133 | auto a = &A_COEFFICIENTS[ j ]; 134 | auto f = &F_COEFFICIENTS[ j ]; 135 | 136 | a->value += ATTENUATOR * ( a->coeffs[ _coeffOffset ] - a->value ); 137 | f->value += ATTENUATOR * ( f->coeffs[ _coeffOffset ] - f->value ); 138 | 139 | // apply formant onto the input signal 140 | 141 | double formant = APPLY_SYNTHESIS_SIGNAL ? getFormant( _phase, FORMANT_WIDTH_SCALE[ j ] * ufp ) : 1.0; 142 | double carrier = getCarrier( f->value * ufp, _phase ); 143 | 144 | // the fp/fn coefficients stand for a -3dB/oct spectral envelope 145 | out += a->value * ( fp / f->value ) * in * formant * carrier; 146 | } 147 | 148 | // catch denormals 149 | 150 | undenormaliseDouble( out ); 151 | 152 | // compress signal and write to output 153 | 154 | inBuffer[ i ] = compress( out ); 155 | } 156 | } 157 | 158 | /* private methods */ 159 | 160 | void FormantFilter::cacheLFO() 161 | { 162 | // when LFO is "off" we mock a depth of 0. In reality we keep 163 | // the LFO moving to feed the carrier signal. The LFO won't 164 | // change the active vowel coefficient in this mode. 165 | 166 | _lfoRange = _vowel * ( hasLFO ? _lfoDepth : 0 ); 167 | _lfoMax = std::min( 1., _vowel + _lfoRange / 2. ); 168 | _lfoMin = std::max( 0., _vowel - _lfoRange / 2. ); 169 | } 170 | 171 | double FormantFilter::generateFormant( double phase, const double width ) 172 | { 173 | int hmax = int( 10 * width ) > FORMANT_TABLE_SIZE / 2 ? FORMANT_TABLE_SIZE / 2 : int( 10 * width ); 174 | double jupe = 0.15f; 175 | 176 | double a = 0.5f; 177 | double phi = 0.0f; 178 | double hann, gaussian, harmonic; 179 | 180 | for ( size_t h = 1; h < hmax; h++ ) { 181 | phi += VST::PI * phase; 182 | hann = 0.5f + 0.5f * fast_cos( h * ( 1.0 / hmax )); 183 | gaussian = 0.85f * exp( -h * h / ( width * width )); 184 | harmonic = cosf( phi ); 185 | a += hann * ( gaussian + jupe ) * harmonic; 186 | } 187 | return a; 188 | } 189 | 190 | double FormantFilter::getFormant( double phase, double width ) 191 | { 192 | width = ( width < 0 ) ? 0 : width > MAX_FORMANT_WIDTH - 2 ? MAX_FORMANT_WIDTH - 2 : width; 193 | double P = ( FORMANT_TABLE_SIZE - 1 ) * ( phase + 1 ) * 0.5f; // normalize phase 194 | 195 | // calculate the integer and fractional parts of the phase and width 196 | 197 | int phaseI = ( int ) P; 198 | double phaseF = P - phaseI; 199 | 200 | int widthI = ( int ) width; 201 | double widthF = width - widthI; 202 | 203 | int i00 = phaseI + FORMANT_TABLE_SIZE * widthI; 204 | int i10 = i00 + FORMANT_TABLE_SIZE; 205 | 206 | // bilinear interpolation of formant values 207 | return ( 1 - widthF ) * 208 | ( FORMANT_TABLE[ i00 ] + phaseF * ( FORMANT_TABLE[ i00 + 1 ] - FORMANT_TABLE[ i00 ])) + 209 | widthF * ( FORMANT_TABLE[ i10 ] + phaseF * ( FORMANT_TABLE[ i10 + 1 ] - FORMANT_TABLE[ i10 ])); 210 | } 211 | 212 | double FormantFilter::getCarrier( const double position, const double phase ) 213 | { 214 | double harmI = floor( position ); // integer and 215 | double harmF = position - harmI; // fractional part of harmonic number 216 | 217 | // keep within -1 to +1 range 218 | double phi1 = fmodf( phase * harmI + 1 + 1000, 2.0 ) - 1.0; 219 | double phi2 = fmodf( phase * ( harmI + 1 ) + 1 + 1000, 2.0 ) - 1.0; 220 | 221 | // calculate the two carriers 222 | double carrier1 = fast_cos( phi1 ); 223 | double carrier2 = fast_cos( phi2 ); 224 | 225 | // return interpolation between the two carriers 226 | return carrier1 + harmF * ( carrier2 - carrier1 ); 227 | } 228 | 229 | void FormantFilter::cacheDynamicsProcessing() 230 | { 231 | _fullDynamicsProcessing = false; 232 | 233 | _dThreshold = pow( 10.0, ( 2.0 * DYNAMICS_THRESHOLD - 2.0 )); 234 | _dRatio = 2.5 * DYNAMICS_RATIO - 0.5; 235 | 236 | if ( _dRatio > 1.0 ) { 237 | _dRatio = 1.f + 16.f * ( _dRatio - 1.f ) * ( _dRatio - 1.f ); 238 | _fullDynamicsProcessing = true; 239 | } 240 | if ( _dRatio < 0.0 ) { 241 | _dRatio = 0.6f * _dRatio; 242 | _fullDynamicsProcessing = true; 243 | } 244 | _dTrim = pow( 10.0,( 2.0 * DYNAMICS_LEVEL )); 245 | _dAttack = pow( 10.0,( -0.002 - 2.0 * DYNAMICS_ATTACK )); 246 | _dRelease = pow( 10.0,( -2.0 - 3.0 * DYNAMICS_RELEASE )); 247 | 248 | // limiter 249 | 250 | if ( DYNAMICS_LIMITER_DYNAMICS_THRESHOLD > 0.98 ) { 251 | _dLimThreshold = 0.f; 252 | } 253 | else { 254 | _dLimThreshold = 0.99 * pow( 10.0, int( 30.0 * DYNAMICS_LIMITER_DYNAMICS_THRESHOLD - 20.0 ) / 20.f ); 255 | _fullDynamicsProcessing = true; 256 | } 257 | 258 | // expander 259 | 260 | if ( DYNAMICS_GATE_DYNAMICS_THRESHOLD < 0.02 ) { 261 | _dExpThreshold = 0.f; 262 | } 263 | else { 264 | _dExpThreshold = pow( 10.f, ( 3.0 * DYNAMICS_GATE_DYNAMICS_THRESHOLD - 3.0 )); 265 | _fullDynamicsProcessing = true; 266 | } 267 | _dExpRatio = 1.0 - pow( 10.f, ( -2.0 - 3.3 * DYNAMICS_GATE_DECAY )); 268 | _dGateAttack = pow( 10.0, (-0.002 - 3.0 * DYNAMICS_GATE_DYNAMICS_ATTACK )); 269 | 270 | if ( _dRatio < 0.0f && _dThreshold < 0.1f ) { 271 | _dRatio *= _dThreshold * 15.f; 272 | } 273 | _dDry = 1.0f - DYNAMICS_MIX; 274 | _dTrim *= DYNAMICS_MIX; 275 | } 276 | 277 | } 278 | -------------------------------------------------------------------------------- /resource/plugin.uidesc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 126 | 127 | 128 | 129 | 130 | 131 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ######################################### 2 | # CMake Project for TRANSFORMANT Plugin # 3 | ######################################### 4 | 5 | cmake_minimum_required(VERSION 3.19) 6 | set(CMAKE_COLOR_MAKEFILE ON) 7 | 8 | # uncomment to build as VST2.4 instead of VST3.0 (provides wider DAW compatibility), not supported on Linux 9 | #set(SMTG_CREATE_VST2_VERSION "Use VST2" ON) 10 | 11 | project(Transformant) 12 | set(PROJECT_VERSION 1) 13 | set(target transformant) 14 | set(copyright "igorski.nl 2020-2024") 15 | set(major_version 1) 16 | set(minor_version 0) 17 | set(release_number 4) 18 | set(build_number 1) # TODO supply through CLI (build number is not included in public facing version strings) 19 | set(version_string "${major_version}.${minor_version}.${release_number}") # also see audiounitconfig.h#kAUcomponentVersion and related Info.plist 20 | set(SMTG_CREATE_MODULE_INFO false) 21 | 22 | ##################### 23 | # Compiler settings # 24 | ##################### 25 | 26 | set(CMAKE_CXX_STANDARD 17) 27 | add_definitions(-DNDEBUG) 28 | add_compile_definitions(PLUGIN_COPYRIGHT=${copyright}) 29 | add_compile_definitions(PLUGIN_MAJOR_VERSION=${major_version}) 30 | add_compile_definitions(PLUGIN_MINOR_VERSION=${minor_version}) 31 | add_compile_definitions(PLUGIN_RELEASE_NUMBER=${release_number}) 32 | add_compile_definitions(PLUGIN_BUILD_NUMBER=${build_number}) 33 | 34 | if(MSVC) 35 | add_definitions(/D _CRT_SECURE_NO_WARNINGS) 36 | endif() 37 | 38 | if(UNIX) 39 | if(APPLE) 40 | if (XCODE) 41 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++17") 42 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") 43 | else() 44 | set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) 45 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -stdlib=libc++") 46 | link_libraries(c++) 47 | endif() 48 | # support Yosemite and up 49 | set(CMAKE_OSX_SYSROOT macosx10.13) 50 | set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13") 51 | else() 52 | set(LINUX true) 53 | add_definitions( -D__cdecl= ) 54 | set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) 55 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -Wno-multichar") 56 | link_libraries(stdc++fs pthread dl pango-1.0 pangocairo-1.0) 57 | endif() 58 | else() 59 | ## spotted to not be set by default on VS CLI. Here we assume any non-Unix 60 | ## OS MUST be Windows. Then again that's the only other intended target for this project... 61 | set(WIN true) 62 | endif() 63 | 64 | ############ 65 | # Includes # 66 | ############ 67 | 68 | list(APPEND CMAKE_MODULE_PATH "${VST3_SDK_ROOT}/cmake/modules") 69 | 70 | # include(SMTG_AAXSupport) 71 | include(SMTG_AddVST3Library) 72 | include(SMTG_AddVST3Options) 73 | include(SMTG_Bundle) 74 | include(SMTG_CoreAudioSupport) 75 | include(SMTG_ExportedSymbols) 76 | include(SMTG_Global) 77 | include(SMTG_PlatformIOS) 78 | include(SMTG_PlatformToolset) 79 | include(SMTG_PrefixHeader) 80 | include(SMTG_UniversalBinary) 81 | include(SMTG_VstGuiSupport) 82 | 83 | ######################### 84 | # Steinberg VST sources # 85 | ######################### 86 | 87 | set(VSTSDK_PLUGIN_SOURCE 88 | ${VST3_SDK_ROOT}/public.sdk/source/common/commoniids.cpp 89 | ${VST3_SDK_ROOT}/public.sdk/source/vst/vstaudioeffect.cpp 90 | ${VST3_SDK_ROOT}/public.sdk/source/vst/vstaudioprocessoralgo.h 91 | ${VST3_SDK_ROOT}/public.sdk/source/vst/vsteditcontroller.h 92 | ${VST3_SDK_ROOT}/pluginterfaces/base/ibstream.h 93 | ${VST3_SDK_ROOT}/pluginterfaces/base/ustring.h 94 | ${VST3_SDK_ROOT}/pluginterfaces/vst/ivstevents.h 95 | ${VST3_SDK_ROOT}/pluginterfaces/vst/ivstparameterchanges.h 96 | ${VST3_SDK_ROOT}/pluginterfaces/vst/vstpresetkeys.h 97 | ) 98 | 99 | set(vst2_sources 100 | ${VST3_SDK_ROOT}/public.sdk/source/vst/vst2wrapper/vst2wrapper.sdk.cpp 101 | src/vstentry_vst2.cpp 102 | ) 103 | if(APPLE) 104 | set(vst2_sources 105 | ${vst2_sources} 106 | ${VST3_SDK_ROOT}/public.sdk/source/common/threadchecker_mac.mm 107 | ) 108 | elseif(WIN) 109 | set(vst2_sources 110 | ${vst2_sources} 111 | ${VST3_SDK_ROOT}/public.sdk/source/common/threadchecker_win32.cpp 112 | ) 113 | endif() 114 | 115 | ########################## 116 | # Plugin project sources # 117 | ########################## 118 | 119 | set(vst_sources 120 | src/global.h 121 | src/audiobuffer.h 122 | src/audiobuffer.cpp 123 | src/bitcrusher.h 124 | src/bitcrusher.cpp 125 | src/formantfilter.h 126 | src/formantfilter.cpp 127 | src/lfo.h 128 | src/lfo.cpp 129 | src/limiter.h 130 | src/limiter.cpp 131 | src/paramids.h 132 | src/pluginprocess.h 133 | src/pluginprocess.cpp 134 | src/vst.h 135 | src/vst.cpp 136 | src/vstentry.cpp 137 | src/version.h 138 | src/waveshaper.h 139 | src/waveshaper.cpp 140 | src/ui/controller.h 141 | src/ui/controller.cpp 142 | src/ui/uimessagecontroller.h 143 | ${VSTSDK_PLUGIN_SOURCE} 144 | ) 145 | 146 | # add the VST2 source files when compiling a VST2 (not supported on Linux) 147 | if(SMTG_CREATE_VST2_VERSION) 148 | if(APPLE OR WIN) 149 | set(vst_sources ${vst_sources} ${vst2_sources}) 150 | endif() 151 | endif() 152 | 153 | set(vst_resources 154 | "resource/background.png" 155 | "resource/version.png" 156 | ) 157 | set(vst_ui_descr "resource/plugin.uidesc") 158 | 159 | ####### 160 | # VST # 161 | ####### 162 | 163 | smtg_add_vst3plugin(${target} ${vst_sources}) 164 | smtg_target_configure_version_file(${target}) 165 | 166 | ## include Steinberg libraries 167 | 168 | set(steinberg_libs "base" "pluginterfaces" "sdk" "vstgui" "vstgui_support" "vstgui_uidescription") 169 | include_directories(${VST3_SDK_ROOT}) 170 | foreach(lib IN ITEMS ${steinberg_libs}) 171 | if(UNIX) 172 | target_link_libraries(${target} PRIVATE ${VST3_SDK_ROOT}/build/lib/Release/lib${lib}.a) 173 | elseif(WIN) 174 | target_link_libraries(${target} PRIVATE ${VST3_SDK_ROOT}/build/lib/Release/${lib}.lib) 175 | endif() 176 | endforeach(lib) 177 | 178 | ## Include Steinberg VSTGUI 179 | 180 | target_include_directories(${target} PUBLIC ${VST3_SDK_ROOT}/vstgui4) 181 | target_sources(${target} PRIVATE 182 | ${VST3_SDK_ROOT}/vstgui4/vstgui/vstgui_uidescription.cpp 183 | ${VST3_SDK_ROOT}/vstgui4/vstgui/plugin-bindings/vst3editor.cpp 184 | ${VST3_SDK_ROOT}/public.sdk/source/vst/vstguieditor.cpp 185 | ) 186 | 187 | ## include macOS specific libraries 188 | 189 | IF (APPLE) 190 | target_sources (${target} PRIVATE 191 | ${VST3_SDK_ROOT}/public.sdk/source/main/macmain.cpp 192 | ) 193 | if(XCODE) 194 | target_link_libraries(${target} PRIVATE "-framework Cocoa" "-framework OpenGL" "-framework Accelerate" "-framework QuartzCore" "-framework Carbon") 195 | else() 196 | find_library(COREFOUNDATION_FRAMEWORK CoreFoundation) 197 | find_library(COCOA_FRAMEWORK Cocoa) 198 | find_library(OPENGL_FRAMEWORK OpenGL) 199 | find_library(ACCELERATE_FRAMEWORK Accelerate) 200 | find_library(QUARTZCORE_FRAMEWORK QuartzCore) 201 | find_library(CARBON_FRAMEWORK Carbon) 202 | find_library(EXPAT Expat) 203 | target_link_libraries(${target} PRIVATE ${COREFOUNDATION_FRAMEWORK} ${COCOA_FRAMEWORK} ${OPENGL_FRAMEWORK} ${ACCELERATE_FRAMEWORK} ${QUARTZCORE_FRAMEWORK} ${CARBON_FRAMEWORK} ${EXPAT}) 204 | endif() 205 | set_target_properties(${target} PROPERTIES 206 | BUNDLE true 207 | BUNDLE_EXTENSION "vst3" 208 | XCODE_ATTRIBUTE_WRAPPER_EXTENSION "vst3" 209 | MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/mac/Info.plist" 210 | MACOSX_BUNDLE_BUNDLE_NAME "${target}" 211 | MACOSX_BUNDLE_GUI_IDENTIFIER "nl.igorski.vst.${target}" 212 | MACOSX_BUNDLE_ICON_FILE "" 213 | MACOSX_BUNDLE_SHORT_VERSION_STRING "${version_string}" 214 | MACOSX_BUNDLE_COPYRIGHT "${copyright}" 215 | ) 216 | endif() 217 | 218 | ## include Linux specific libraries 219 | 220 | if (LINUX) 221 | target_sources (${target} PRIVATE 222 | ${VST3_SDK_ROOT}/public.sdk/source/main/linuxmain.cpp 223 | ) 224 | set(VSTGUI_LTO_COMPILER_FLAGS "-O3 -flto") 225 | set(VSTGUI_LTO_LINKER_FLAGS "") 226 | find_package(X11 REQUIRED) 227 | find_package(Freetype REQUIRED) 228 | find_package(PkgConfig REQUIRED) 229 | pkg_check_modules(LIBXCB REQUIRED xcb) 230 | pkg_check_modules(LIBXCB_UTIL REQUIRED xcb-util) 231 | pkg_check_modules(LIBXCB_CURSOR REQUIRED xcb-cursor) 232 | pkg_check_modules(LIBXCB_KEYSYMS REQUIRED xcb-keysyms) 233 | pkg_check_modules(LIBXCB_XKB REQUIRED xcb-xkb) 234 | pkg_check_modules(LIBXKB_COMMON REQUIRED xkbcommon) 235 | pkg_check_modules(LIBXKB_COMMON_X11 REQUIRED xkbcommon-x11) 236 | set(LINUX_LIBRARIES 237 | ${X11_LIBRARIES} 238 | ${FREETYPE_LIBRARIES} 239 | ${LIBXCB_LIBRARIES} 240 | ${LIBXCB_UTIL_LIBRARIES} 241 | ${LIBXCB_CURSOR_LIBRARIES} 242 | ${LIBXCB_KEYSYMS_LIBRARIES} 243 | ${LIBXCB_XKB_LIBRARIES} 244 | ${LIBXKB_COMMON_LIBRARIES} 245 | ${LIBXKB_COMMON_X11_LIBRARIES} 246 | cairo 247 | fontconfig 248 | dl 249 | ) 250 | target_link_libraries(${target} PRIVATE ${LINUX_LIBRARIES}) 251 | endif() 252 | 253 | ## Include Windows specific libraries 254 | 255 | if(WIN) 256 | target_sources(${target} PRIVATE 257 | ${VST3_SDK_ROOT}/public.sdk/source/main/dllmain.cpp 258 | # ${VST3_SDK_ROOT}/vstgui4/vstgui/vstgui_win32.cpp 259 | ) 260 | endif() 261 | 262 | ## Add the resource files to the bundle 263 | 264 | smtg_target_add_plugin_resources(${target} 265 | RESOURCES ${vst_ui_descr} ${vst_resources} 266 | ) 267 | 268 | if(APPLE) 269 | ############## 270 | # Audio Unit # 271 | ############## 272 | if (XCODE AND SMTG_CREATE_AU_VERSION) 273 | message(STATUS "SMTG_CREATE_AU_VERSION is set. An Audio Unit version of the plug-in will be created.") 274 | target_compile_definitions(${target} PRIVATE BUILD_AUDIO_UNIT) 275 | smtg_target_codesign(${target} ${SMTG_IOS_DEVELOPMENT_TEAM} ${SMTG_CODE_SIGN_IDENTITY_MAC}) 276 | add_subdirectory(mac/audio-unit) 277 | create_audio_unit(${target}) 278 | else() 279 | smtg_target_set_bundle(${target} INFOPLIST "${CMAKE_CURRENT_SOURCE_DIR}/mac/Info.plist" PREPROCESS) 280 | # adding PkgInfo at root level makes plugin appear as a file instead of folder 281 | smtg_target_add_plugin_resources(${target} RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/mac/PkgInfo" OUTPUT_SUBDIRECTORY "../") 282 | endif() 283 | smtg_target_set_bundle(${target} 284 | BUNDLE_IDENTIFIER "nl.igorski.${target}" 285 | COMPANY_NAME "igorski.nl" 286 | ) 287 | elseif(WIN) 288 | target_sources(${target} PRIVATE resource/plugin.rc) 289 | endif() 290 | 291 | if (SMTG_CREATE_VST2_VERSION) 292 | message(STATUS "SMTG_CREATE_VST2_VERSION is set. A VST 2 version of the plug-in will be created.") 293 | if(XCODE) 294 | # fix missing VSTPluginMain symbol when also building VST 2 version 295 | set_target_properties(${target} PROPERTIES XCODE_ATTRIBUTE_EXPORTED_SYMBOLS_FILE "") 296 | endif() 297 | if (WIN) 298 | add_definitions(-D_CRT_SECURE_NO_WARNINGS) 299 | endif() 300 | endif() 301 | 302 | ###################### 303 | # Installation paths # 304 | ###################### 305 | 306 | if(APPLE) 307 | install(TARGETS ${target} 308 | DESTINATION "$ENV{HOME}/Library/Audio/Plug-Ins/VST" 309 | ) 310 | elseif(WIN32) 311 | install(TARGETS ${target} 312 | DESTINATION "C:/Program Files (x86)/Common Files/VST3/" 313 | ) 314 | elseif(WIN) 315 | install(TARGETS ${target} 316 | DESTINATION "C:/Program Files/Common Files/VST3/" 317 | ) 318 | elseif(LINUX) 319 | install(TARGETS ${target} 320 | DESTINATION "/usr/local/lib/vst3/" 321 | ) 322 | endif() -------------------------------------------------------------------------------- /mac/audio-unit/resource/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /src/ui/controller.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020-2024 Igor Zinken - https://www.igorski.nl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | #include "../global.h" 24 | #include "controller.h" 25 | #include "uimessagecontroller.h" 26 | #include "../paramids.h" 27 | 28 | #include "pluginterfaces/base/ibstream.h" 29 | #include "pluginterfaces/base/ustring.h" 30 | #include "pluginterfaces/vst/ivstmidicontrollers.h" 31 | 32 | #include "base/source/fstring.h" 33 | 34 | #include "vstgui/uidescription/delegationcontroller.h" 35 | 36 | #include 37 | #include 38 | 39 | namespace Steinberg { 40 | namespace Vst { 41 | 42 | //------------------------------------------------------------------------ 43 | // Controller Implementation 44 | //------------------------------------------------------------------------ 45 | tresult PLUGIN_API PluginController::initialize( FUnknown* context ) 46 | { 47 | tresult result = EditControllerEx1::initialize( context ); 48 | 49 | if ( result != kResultOk ) 50 | return result; 51 | 52 | //--- Create Units------------- 53 | UnitInfo unitInfo; 54 | Unit* unit; 55 | 56 | // create root only if you want to use the programListId 57 | /* unitInfo.id = kRootUnitId; // always for Root Unit 58 | unitInfo.parentUnitId = kNoParentUnitId; // always for Root Unit 59 | Steinberg::UString (unitInfo.name, USTRINGSIZE (unitInfo.name)).assign (USTRING ("Root")); 60 | unitInfo.programListId = kNoProgramListId; 61 | 62 | unit = new Unit (unitInfo); 63 | addUnitInfo (unit);*/ 64 | 65 | // create a unit1 66 | unitInfo.id = 1; 67 | unitInfo.parentUnitId = kRootUnitId; // attached to the root unit 68 | 69 | Steinberg::UString( unitInfo.name, USTRINGSIZE( unitInfo.name )).assign( USTRING( "Transformant" )); 70 | 71 | unitInfo.programListId = kNoProgramListId; 72 | 73 | unit = new Unit( unitInfo ); 74 | addUnit( unit ); 75 | int32 unitId = 1; 76 | 77 | // Formant filter controls 78 | 79 | parameters.addParameter( new RangeParameter( 80 | USTRING( "Vowel L" ), kVowelLId, USTRING( "0 - 1" ), 81 | 0.f, 1.f, 0.f, 82 | 0, ParameterInfo::kCanAutomate, unitId 83 | )); 84 | 85 | parameters.addParameter( new RangeParameter( 86 | USTRING( "Vowel R" ), kVowelRId, USTRING( "0 - 1" ), 87 | 0.f, 1.f, 0.f, 88 | 0, ParameterInfo::kCanAutomate, unitId 89 | )); 90 | 91 | parameters.addParameter( 92 | USTRING( "Vowel Sync" ), 0, 1, 1, ParameterInfo::kCanAutomate, kVowelSyncId, unitId 93 | ); 94 | 95 | // LFO controls 96 | 97 | parameters.addParameter( new RangeParameter( 98 | USTRING( "Vowel L LFO rate" ), kLFOVowelLId, USTRING( "Hz" ), 99 | Igorski::VST::MIN_LFO_RATE(), Igorski::VST::MAX_LFO_RATE(), Igorski::VST::MIN_LFO_RATE(), 100 | 0, ParameterInfo::kCanAutomate, unitId 101 | )); 102 | 103 | parameters.addParameter( new RangeParameter( 104 | USTRING( "Vowel L LFO depth" ), kLFOVowelLDepthId, USTRING( "%" ), 105 | 0.f, 1.f, 0.5f, 106 | 0, ParameterInfo::kCanAutomate, unitId 107 | )); 108 | 109 | parameters.addParameter( new RangeParameter( 110 | USTRING( "Vowel R LFO rate" ), kLFOVowelRId, USTRING( "Hz" ), 111 | Igorski::VST::MIN_LFO_RATE(), Igorski::VST::MAX_LFO_RATE(), Igorski::VST::MIN_LFO_RATE(), 112 | 0, ParameterInfo::kCanAutomate, unitId 113 | )); 114 | 115 | parameters.addParameter( new RangeParameter( 116 | USTRING( "Vowel R LFO depth" ), kLFOVowelRDepthId, USTRING( "%" ), 117 | 0.f, 1.f, 0.5f, 118 | 0, ParameterInfo::kCanAutomate, unitId 119 | )); 120 | 121 | // distortion controls 122 | 123 | parameters.addParameter( 124 | USTRING( "Distortion Type" ), 0, 1, 0, ParameterInfo::kCanAutomate, kDistortionTypeId, unitId 125 | ); 126 | 127 | parameters.addParameter( new RangeParameter( 128 | USTRING( "Drive" ), kDriveId, USTRING( "0 - 1" ), 129 | 0.f, 1.f, 0.f, 130 | 0, ParameterInfo::kCanAutomate, unitId 131 | )); 132 | 133 | parameters.addParameter( 134 | USTRING( "Distortion pre/post" ), 0, 1, 0, ParameterInfo::kCanAutomate, kDistortionChainId, unitId 135 | ); 136 | 137 | // initialization 138 | 139 | String str( "TRANSFORMANT" ); 140 | str.copyTo16( defaultMessageText, 0, 127 ); 141 | 142 | return result; 143 | } 144 | 145 | //------------------------------------------------------------------------ 146 | tresult PLUGIN_API PluginController::terminate() 147 | { 148 | return EditControllerEx1::terminate (); 149 | } 150 | 151 | //------------------------------------------------------------------------ 152 | tresult PLUGIN_API PluginController::setComponentState( IBStream* state ) 153 | { 154 | // we receive the current state of the component (processor part) 155 | if ( state ) 156 | { 157 | float savedVowelL = 1.f; 158 | if ( state->read( &savedVowelL, sizeof( float )) != kResultOk ) 159 | return kResultFalse; 160 | 161 | float savedVowelR = 1.f; 162 | if ( state->read( &savedVowelR, sizeof( float )) != kResultOk ) 163 | return kResultFalse; 164 | 165 | float savedVowelSync = 1.f; 166 | if ( state->read( &savedVowelSync, sizeof( float )) != kResultOk ) 167 | return kResultFalse; 168 | 169 | float savedLFOVowelL = Igorski::VST::MIN_LFO_RATE(); 170 | if ( state->read( &savedLFOVowelL, sizeof( float )) != kResultOk ) 171 | return kResultFalse; 172 | 173 | float savedLFOVowelR = Igorski::VST::MIN_LFO_RATE(); 174 | if ( state->read( &savedLFOVowelR, sizeof( float )) != kResultOk ) 175 | return kResultFalse; 176 | 177 | float savedLFOVowelLDepth = 1.f; 178 | if ( state->read( &savedLFOVowelLDepth, sizeof( float )) != kResultOk ) 179 | return kResultFalse; 180 | 181 | float savedLFOVowelRDepth = 1.f; 182 | if ( state->read( &savedLFOVowelRDepth, sizeof( float )) != kResultOk ) 183 | return kResultFalse; 184 | 185 | float savedDistortionType = 1.f; 186 | if ( state->read( &savedDistortionType, sizeof( float )) != kResultOk ) 187 | return kResultFalse; 188 | 189 | float savedDrive = 1.f; 190 | if ( state->read( &savedDrive, sizeof( float )) != kResultOk ) 191 | return kResultFalse; 192 | 193 | float savedDistortionChain = 0.f; 194 | if ( state->read( &savedDistortionChain, sizeof( float )) != kResultOk ) 195 | return kResultFalse; 196 | 197 | #if BYTEORDER == kBigEndian 198 | SWAP32( savedVowelL ) 199 | SWAP32( savedVowelR ) 200 | SWAP32( savedVowelSync ) 201 | SWAP32( savedLFOVowelL ) 202 | SWAP32( savedLFOVowelR ) 203 | SWAP32( savedLFOVowelLDepth ) 204 | SWAP32( savedLFOVowelRDepth ) 205 | SWAP32( savedDistortionType ) 206 | SWAP32( savedDrive ) 207 | SWAP32( savedDistortionChain ) 208 | #endif 209 | 210 | setParamNormalized( kVowelLId, savedVowelL ); 211 | setParamNormalized( kVowelRId, savedVowelR ); 212 | setParamNormalized( kVowelSyncId, savedVowelSync ); 213 | setParamNormalized( kLFOVowelLId, savedLFOVowelL ); 214 | setParamNormalized( kLFOVowelRId, savedLFOVowelR ); 215 | setParamNormalized( kLFOVowelLDepthId, savedLFOVowelLDepth ); 216 | setParamNormalized( kLFOVowelRDepthId, savedLFOVowelRDepth ); 217 | setParamNormalized( kDistortionTypeId, savedDistortionType ); 218 | setParamNormalized( kDriveId, savedDrive ); 219 | setParamNormalized( kDistortionChainId, savedDistortionChain ); 220 | 221 | state->seek( sizeof ( float ), IBStream::kIBSeekCur ); 222 | } 223 | return kResultOk; 224 | } 225 | 226 | //------------------------------------------------------------------------ 227 | IPlugView* PLUGIN_API PluginController::createView( const char* name ) 228 | { 229 | // create the visual editor 230 | if ( name && strcmp( name, "editor" ) == 0 ) 231 | { 232 | VST3Editor* view = new VST3Editor( this, "view", "plugin.uidesc" ); 233 | return view; 234 | } 235 | return 0; 236 | } 237 | 238 | //------------------------------------------------------------------------ 239 | IController* PluginController::createSubController( UTF8StringPtr name, 240 | const IUIDescription* /*description*/, 241 | VST3Editor* /*editor*/ ) 242 | { 243 | if ( UTF8StringView( name ) == "MessageController" ) 244 | { 245 | UIMessageController* controller = new UIMessageController( this ); 246 | addUIMessageController( controller ); 247 | return controller; 248 | } 249 | return nullptr; 250 | } 251 | 252 | //------------------------------------------------------------------------ 253 | tresult PLUGIN_API PluginController::setState( IBStream* state ) 254 | { 255 | tresult result = kResultFalse; 256 | 257 | int8 byteOrder; 258 | if (( result = state->read( &byteOrder, sizeof( int8 ))) != kResultTrue ) 259 | return result; 260 | 261 | if (( result = state->read( defaultMessageText, 128 * sizeof( TChar ))) != kResultTrue ) 262 | return result; 263 | 264 | // if the byteorder doesn't match, byte swap the text array ... 265 | if ( byteOrder != BYTEORDER ) 266 | { 267 | for ( int32 i = 0; i < 128; i++ ) 268 | SWAP_16( defaultMessageText[ i ]) 269 | } 270 | 271 | // update our editors 272 | for ( UIMessageControllerList::iterator it = uiMessageControllers.begin (), end = uiMessageControllers.end (); it != end; ++it ) 273 | ( *it )->setMessageText( defaultMessageText ); 274 | 275 | return result; 276 | } 277 | 278 | //------------------------------------------------------------------------ 279 | tresult PLUGIN_API PluginController::getState( IBStream* state ) 280 | { 281 | // here we can save UI settings for example 282 | 283 | // as we save a Unicode string, we must know the byteorder when setState is called 284 | int8 byteOrder = BYTEORDER; 285 | if ( state->write( &byteOrder, sizeof( int8 )) == kResultTrue ) 286 | { 287 | return state->write( defaultMessageText, 128 * sizeof( TChar )); 288 | } 289 | return kResultFalse; 290 | } 291 | 292 | //------------------------------------------------------------------------ 293 | tresult PluginController::receiveText( const char* text ) 294 | { 295 | // received from Component 296 | if ( text ) 297 | { 298 | fprintf( stderr, "[Controller] received: " ); 299 | fprintf( stderr, "%s", text ); 300 | fprintf( stderr, "\n" ); 301 | } 302 | return kResultOk; 303 | } 304 | 305 | //------------------------------------------------------------------------ 306 | tresult PLUGIN_API PluginController::setParamNormalized( ParamID tag, ParamValue value ) 307 | { 308 | // called from host to update our parameters state 309 | tresult result = EditControllerEx1::setParamNormalized( tag, value ); 310 | return result; 311 | } 312 | 313 | //------------------------------------------------------------------------ 314 | tresult PLUGIN_API PluginController::getParamStringByValue( ParamID tag, ParamValue valueNormalized, String128 string ) 315 | { 316 | switch ( tag ) 317 | { 318 | // these controls are floating point values in 0 - 1 range, we can 319 | // simply read the normalized value which is in the same range 320 | 321 | case kVowelLId: 322 | case kVowelRId: 323 | case kVowelSyncId: 324 | case kLFOVowelLDepthId: 325 | case kLFOVowelRDepthId: 326 | case kDistortionTypeId: 327 | case kDriveId: 328 | case kDistortionChainId: 329 | { 330 | char text[32]; 331 | 332 | switch ( tag ) { 333 | default: 334 | sprintf( text, "%.2f", ( float ) valueNormalized ); 335 | break; 336 | 337 | case kVowelSyncId: 338 | sprintf( text, "%s", ( valueNormalized == 0 ) ? "Off": "On" ); 339 | break; 340 | 341 | case kDistortionTypeId: 342 | sprintf( text, "%s", ( valueNormalized == 0 ) ? "Waveshaper": "Bitcrusher" ); 343 | break; 344 | 345 | case kDistortionChainId: 346 | sprintf( text, "%s", ( valueNormalized == 0 ) ? "Pre-formant mix" : "Post-formant mix" ); 347 | break; 348 | } 349 | Steinberg::UString( string, 128 ).fromAscii( text ); 350 | 351 | return kResultTrue; 352 | } 353 | 354 | // vowel LFO setting is also floating point but in a custom range 355 | // request the plain value from the normalized value 356 | 357 | case kLFOVowelLId: 358 | case kLFOVowelRId: 359 | { 360 | char text[32]; 361 | if (valueNormalized == 0 ) 362 | sprintf( text, "%s", "Off" ); 363 | else 364 | sprintf( text, "%.2f", normalizedParamToPlain( tag, valueNormalized )); 365 | Steinberg::UString( string, 128 ).fromAscii( text ); 366 | 367 | return kResultTrue; 368 | } 369 | 370 | // everything else 371 | default: 372 | return EditControllerEx1::getParamStringByValue( tag, valueNormalized, string ); 373 | } 374 | } 375 | 376 | //------------------------------------------------------------------------ 377 | tresult PLUGIN_API PluginController::getParamValueByString( ParamID tag, TChar* string, ParamValue& valueNormalized ) 378 | { 379 | /* example, but better to use a custom Parameter as seen in RangeParameter 380 | switch (tag) 381 | { 382 | case kAttackId: 383 | { 384 | Steinberg::UString wrapper ((TChar*)string, -1); // don't know buffer size here! 385 | double tmp = 0.0; 386 | if (wrapper.scanFloat (tmp)) 387 | { 388 | valueNormalized = expf (logf (10.f) * (float)tmp / 20.f); 389 | return kResultTrue; 390 | } 391 | return kResultFalse; 392 | } 393 | }*/ 394 | return EditControllerEx1::getParamValueByString( tag, string, valueNormalized ); 395 | } 396 | 397 | //------------------------------------------------------------------------ 398 | void PluginController::addUIMessageController( UIMessageController* controller ) 399 | { 400 | uiMessageControllers.push_back( controller ); 401 | } 402 | 403 | //------------------------------------------------------------------------ 404 | void PluginController::removeUIMessageController( UIMessageController* controller ) 405 | { 406 | UIMessageControllerList::const_iterator it = std::find( 407 | uiMessageControllers.begin(), uiMessageControllers.end (), controller 408 | ); 409 | if ( it != uiMessageControllers.end()) 410 | uiMessageControllers.erase( it ); 411 | } 412 | 413 | //------------------------------------------------------------------------ 414 | void PluginController::setDefaultMessageText( String128 text ) 415 | { 416 | String tmp( text ); 417 | tmp.copyTo16( defaultMessageText, 0, 127 ); 418 | } 419 | 420 | //------------------------------------------------------------------------ 421 | TChar* PluginController::getDefaultMessageText() 422 | { 423 | return defaultMessageText; 424 | } 425 | 426 | //------------------------------------------------------------------------ 427 | tresult PLUGIN_API PluginController::queryInterface( const char* iid, void** obj ) 428 | { 429 | QUERY_INTERFACE( iid, obj, IMidiMapping::iid, IMidiMapping ); 430 | return EditControllerEx1::queryInterface( iid, obj ); 431 | } 432 | 433 | //------------------------------------------------------------------------ 434 | tresult PLUGIN_API PluginController::getMidiControllerAssignment( int32 busIndex, int16 /*midiChannel*/, 435 | CtrlNumber midiControllerNumber, ParamID& tag ) 436 | { 437 | // we support for the Gain parameter all MIDI Channel but only first bus (there is only one!) 438 | /* 439 | if ( busIndex == 0 && midiControllerNumber == kCtrlVolume ) 440 | { 441 | tag = kDelayTimeId; 442 | return kResultTrue; 443 | } 444 | */ 445 | return kResultFalse; 446 | } 447 | 448 | //------------------------------------------------------------------------ 449 | } // namespace Vst 450 | } // namespace Steinberg 451 | -------------------------------------------------------------------------------- /src/vst.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2018-2023 Igor Zinken - https://www.igorski.nl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | #include "global.h" 24 | #include "vst.h" 25 | #include "paramids.h" 26 | #include "calc.h" 27 | 28 | #include "public.sdk/source/vst/vstaudioprocessoralgo.h" 29 | 30 | #include "pluginterfaces/base/ibstream.h" 31 | #include "pluginterfaces/base/ustring.h" 32 | #include "pluginterfaces/vst/ivstevents.h" 33 | #include "pluginterfaces/vst/ivstparameterchanges.h" 34 | #include "pluginterfaces/vst/vstpresetkeys.h" 35 | 36 | #include 37 | 38 | namespace Igorski { 39 | 40 | //------------------------------------------------------------------------ 41 | // Transformant Implementation 42 | //------------------------------------------------------------------------ 43 | Transformant::Transformant() 44 | : fVowelL( 0.f ) 45 | , fVowelR( 0.f ) 46 | , fVowelSync( 1.f ) 47 | , fLFOVowelL( 0.f ) 48 | , fLFOVowelR( 0.f ) 49 | , fLFOVowelLDepth( 0.5f ) 50 | , fLFOVowelRDepth( 0.5f ) 51 | , fDistortionType( 0.f ) 52 | , fDrive( 0.f ) 53 | , fDistortionChain( 0.f ) 54 | , pluginProcess( nullptr ) 55 | // , outputGainOld( 0.f ) 56 | , currentProcessMode( -1 ) // -1 means not initialized 57 | { 58 | // register its editor class (the same as used in vstentry.cpp) 59 | setControllerClass( VST::ControllerUID ); 60 | 61 | // should be created on setupProcessing, this however doesn't fire for Audio Unit using auval? 62 | pluginProcess = new PluginProcess( 2, 44100.f ); 63 | } 64 | 65 | //------------------------------------------------------------------------ 66 | Transformant::~Transformant() 67 | { 68 | // free all allocated resources 69 | delete pluginProcess; 70 | } 71 | 72 | //------------------------------------------------------------------------ 73 | tresult PLUGIN_API Transformant::initialize( FUnknown* context ) 74 | { 75 | //---always initialize the parent------- 76 | tresult result = AudioEffect::initialize( context ); 77 | // if everything Ok, continue 78 | if ( result != kResultOk ) 79 | return result; 80 | 81 | //---create Audio In/Out buses------ 82 | addAudioInput ( STR16( "Stereo In" ), SpeakerArr::kStereo ); 83 | addAudioOutput( STR16( "Stereo Out" ), SpeakerArr::kStereo ); 84 | 85 | //---create Event In/Out buses (1 bus with only 1 channel)------ 86 | addEventInput( STR16( "Event In" ), 1 ); 87 | 88 | return kResultOk; 89 | } 90 | 91 | //------------------------------------------------------------------------ 92 | tresult PLUGIN_API Transformant::terminate() 93 | { 94 | // nothing to do here yet...except calling our parent terminate 95 | return AudioEffect::terminate(); 96 | } 97 | 98 | //------------------------------------------------------------------------ 99 | tresult PLUGIN_API Transformant::setActive (TBool state) 100 | { 101 | if (state) 102 | sendTextMessage( "Transformant::setActive (true)" ); 103 | else 104 | sendTextMessage( "Transformant::setActive (false)" ); 105 | 106 | // reset output level meter 107 | // outputGainOld = 0.f; 108 | 109 | // call our parent setActive 110 | return AudioEffect::setActive( state ); 111 | } 112 | 113 | //------------------------------------------------------------------------ 114 | tresult PLUGIN_API Transformant::process( ProcessData& data ) 115 | { 116 | // In this example there are 4 steps: 117 | // 1) Read inputs parameters coming from host (in order to adapt our model values) 118 | // 2) Read inputs events coming from host (note on/off events) 119 | // 3) Apply the effect using the input buffer into the output buffer 120 | 121 | //---1) Read input parameter changes----------- 122 | IParameterChanges* paramChanges = data.inputParameterChanges; 123 | if ( paramChanges ) 124 | { 125 | int32 numParamsChanged = paramChanges->getParameterCount(); 126 | // for each parameter which are some changes in this audio block: 127 | for ( int32 i = 0; i < numParamsChanged; i++ ) 128 | { 129 | IParamValueQueue* paramQueue = paramChanges->getParameterData( i ); 130 | if ( paramQueue ) 131 | { 132 | ParamValue value; 133 | int32 sampleOffset; 134 | int32 numPoints = paramQueue->getPointCount(); 135 | 136 | if ( paramQueue->getPoint( numPoints - 1, sampleOffset, value ) != kResultTrue ) { 137 | continue; 138 | } 139 | 140 | switch ( paramQueue->getParameterId()) 141 | { 142 | case kVowelLId: 143 | fVowelL = ( float ) value; 144 | break; 145 | 146 | case kVowelRId: 147 | fVowelR = ( float ) value; 148 | break; 149 | 150 | case kVowelSyncId: 151 | fVowelSync = ( float ) value; 152 | break; 153 | 154 | case kLFOVowelLId: 155 | fLFOVowelL = ( float ) value; 156 | break; 157 | 158 | case kLFOVowelRId: 159 | fLFOVowelR = ( float ) value; 160 | break; 161 | 162 | case kLFOVowelLDepthId: 163 | fLFOVowelLDepth = ( float ) value; 164 | break; 165 | 166 | case kLFOVowelRDepthId: 167 | fLFOVowelRDepth = ( float ) value; 168 | break; 169 | 170 | case kDistortionTypeId: 171 | fDistortionType = ( float ) value; 172 | break; 173 | 174 | case kDriveId: 175 | fDrive = ( float ) value; 176 | break; 177 | 178 | case kDistortionChainId: 179 | fDistortionChain = ( float ) value; 180 | break; 181 | } 182 | syncModel(); 183 | } 184 | } 185 | } 186 | 187 | //---2) Read input events------------- 188 | // IEventList* eventList = data.inputEvents; 189 | 190 | //------------------------------------- 191 | //---3) Process Audio--------------------- 192 | //------------------------------------- 193 | 194 | if ( data.numInputs == 0 || data.numOutputs == 0 ) 195 | { 196 | // nothing to do 197 | return kResultOk; 198 | } 199 | 200 | int32 numInChannels = data.inputs[ 0 ].numChannels; 201 | int32 numOutChannels = data.outputs[ 0 ].numChannels; 202 | 203 | // --- get audio buffers---------------- 204 | uint32 sampleFramesSize = getSampleFramesSizeInBytes( processSetup, data.numSamples ); 205 | void** in = getChannelBuffersPointer( processSetup, data.inputs [ 0 ] ); 206 | void** out = getChannelBuffersPointer( processSetup, data.outputs[ 0 ] ); 207 | 208 | // process the incoming sound! 209 | 210 | bool isDoublePrecision = data.symbolicSampleSize == kSample64; 211 | bool isSilentInput = data.inputs[ 0 ].silenceFlags != 0; 212 | bool isSilentOutput = false; 213 | 214 | if ( isDoublePrecision ) { 215 | // 64-bit samples, e.g. Reaper64 216 | pluginProcess->process( 217 | ( double** ) in, ( double** ) out, numInChannels, numOutChannels, 218 | data.numSamples, sampleFramesSize 219 | ); 220 | if ( isSilentInput ) { 221 | isSilentOutput = pluginProcess->isBufferSilent(( double** ) out, numOutChannels, data.numSamples ); 222 | } 223 | } 224 | else { 225 | // 32-bit samples, e.g. Ableton Live, Bitwig Studio... (oddly enough also when 64-bit?) 226 | pluginProcess->process( 227 | ( float** ) in, ( float** ) out, numInChannels, numOutChannels, 228 | data.numSamples, sampleFramesSize 229 | ); 230 | if ( isSilentInput ) { 231 | isSilentOutput = pluginProcess->isBufferSilent(( float** ) out, numOutChannels, data.numSamples ); 232 | } 233 | } 234 | 235 | // output flags 236 | 237 | data.outputs[ 0 ].silenceFlags = isSilentOutput ? (( uint64 ) 1 << numOutChannels ) - 1 : 0; 238 | 239 | // float outputGain = pluginProcess->limiter->getLinearGR(); 240 | //---4) Write output parameter changes----------- 241 | // IParameterChanges* outParamChanges = data.outputParameterChanges; 242 | // // a new value of VuMeter will be sent to the host 243 | // // (the host will send it back in sync to our controller for updating our editor) 244 | // if ( !isDoublePrecision && outParamChanges && outputGainOld != outputGain ) { 245 | // int32 index = 0; 246 | // IParamValueQueue* paramQueue = outParamChanges->addParameterData( kVuPPMId, index ); 247 | // if ( paramQueue ) 248 | // paramQueue->addPoint( 0, outputGain, index ); 249 | // } 250 | // outputGainOld = outputGain; 251 | 252 | return kResultOk; 253 | } 254 | 255 | //------------------------------------------------------------------------ 256 | tresult Transformant::receiveText( const char* text ) 257 | { 258 | // received from Controller 259 | fprintf( stderr, "[Transformant] received: " ); 260 | fprintf( stderr, "%s", text ); 261 | fprintf( stderr, "\n" ); 262 | 263 | return kResultOk; 264 | } 265 | 266 | //------------------------------------------------------------------------ 267 | tresult PLUGIN_API Transformant::setState( IBStream* state ) 268 | { 269 | // called when we load a preset, the model has to be reloaded 270 | 271 | float savedVowelL = 0.f; 272 | if ( state->read( &savedVowelL, sizeof ( float )) != kResultOk ) 273 | return kResultFalse; 274 | 275 | float savedVowelR = 0.f; 276 | if ( state->read( &savedVowelR, sizeof ( float )) != kResultOk ) 277 | return kResultFalse; 278 | 279 | float savedVowelSync = 0.f; 280 | if ( state->read( &savedVowelSync, sizeof ( float )) != kResultOk ) 281 | return kResultFalse; 282 | 283 | float savedLFOVowelL = 0.f; 284 | if ( state->read( &savedLFOVowelL, sizeof ( float )) != kResultOk ) 285 | return kResultFalse; 286 | 287 | float savedLFOVowelR = 0.f; 288 | if ( state->read( &savedLFOVowelR, sizeof ( float )) != kResultOk ) 289 | return kResultFalse; 290 | 291 | float savedLFOVowelLDepth = 0.f; 292 | if ( state->read( &savedLFOVowelLDepth, sizeof ( float )) != kResultOk ) 293 | return kResultFalse; 294 | 295 | float savedLFOVowelRDepth = 0.f; 296 | if ( state->read( &savedLFOVowelRDepth, sizeof ( float )) != kResultOk ) 297 | return kResultFalse; 298 | 299 | float savedDistortionType = 0.f; 300 | if ( state->read( &savedDistortionType, sizeof ( float )) != kResultOk ) 301 | return kResultFalse; 302 | 303 | float savedDrive = 0.f; 304 | if ( state->read( &savedDrive, sizeof ( float )) != kResultOk ) 305 | return kResultFalse; 306 | 307 | float savedDistortionChain = 0.f; 308 | if ( state->read( &savedDistortionChain, sizeof ( float )) != kResultOk ) 309 | return kResultFalse; 310 | 311 | #if BYTEORDER == kBigEndian 312 | SWAP32( savedVowelL ) 313 | SWAP32( savedVowelR ) 314 | SWAP32( savedVowelSync ) 315 | SWAP32( savedLFOVowelL ) 316 | SWAP32( savedLFOVowelR ) 317 | SWAP32( savedLFOVowelLDepth ) 318 | SWAP32( savedLFOVowelRDepth ) 319 | SWAP32( savedDistortionType ) 320 | SWAP32( savedDrive ) 321 | SWAP32( savedDistortionChain ) 322 | #endif 323 | 324 | fVowelL = savedVowelL; 325 | fVowelR = savedVowelR; 326 | fVowelSync = savedVowelSync; 327 | fLFOVowelL = savedLFOVowelL; 328 | fLFOVowelR = savedLFOVowelR; 329 | fLFOVowelLDepth = savedLFOVowelLDepth; 330 | fLFOVowelRDepth = savedLFOVowelRDepth; 331 | fDistortionType = savedDistortionType; 332 | fDrive = savedDrive; 333 | fDistortionChain = savedDistortionChain; 334 | 335 | syncModel(); 336 | 337 | // Example of using the IStreamAttributes interface 338 | FUnknownPtr stream (state); 339 | if ( stream ) 340 | { 341 | IAttributeList* list = stream->getAttributes (); 342 | if ( list ) 343 | { 344 | // get the current type (project/Default..) of this state 345 | String128 string = {0}; 346 | if ( list->getString( PresetAttributes::kStateType, string, 128 * sizeof( TChar )) == kResultTrue ) 347 | { 348 | UString128 tmp( string ); 349 | char ascii[128]; 350 | tmp.toAscii( ascii, 128 ); 351 | if ( !strncmp( ascii, StateType::kProject, strlen( StateType::kProject ))) 352 | { 353 | // we are in project loading context... 354 | } 355 | } 356 | 357 | // get the full file path of this state 358 | TChar fullPath[1024]; 359 | memset( fullPath, 0, 1024 * sizeof( TChar )); 360 | if ( list->getString( PresetAttributes::kFilePathStringType, 361 | fullPath, 1024 * sizeof( TChar )) == kResultTrue ) 362 | { 363 | // here we have the full path ... 364 | } 365 | } 366 | } 367 | return kResultOk; 368 | } 369 | 370 | //------------------------------------------------------------------------ 371 | tresult PLUGIN_API Transformant::getState( IBStream* state ) 372 | { 373 | // here we need to save the model 374 | 375 | float toSaveVowelL = fVowelL; 376 | float toSaveVowelR = fVowelR; 377 | float toSaveVowelSync = fVowelSync; 378 | float toSaveLFOVowelL = fLFOVowelL; 379 | float toSaveLFOVowelR = fLFOVowelR; 380 | float toSaveLFOVowelLDepth = fLFOVowelLDepth; 381 | float toSaveLFOVowelRDepth = fLFOVowelRDepth; 382 | float toSaveDistortionType = fDistortionType; 383 | float toSaveDrive = fDrive; 384 | float toSaveDistortionChain = fDistortionChain; 385 | 386 | #if BYTEORDER == kBigEndian 387 | SWAP32( toSaveVowelL ); 388 | SWAP32( toSaveVowelR ); 389 | SWAP32( toSaveVowelSync ); 390 | SWAP32( toSaveLFOVowelL ); 391 | SWAP32( toSaveLFOVowelR ); 392 | SWAP32( toSaveLFOVowelLDepth ); 393 | SWAP32( toSaveLFOVowelRDepth ); 394 | SWAP32( toSaveDistortionType ); 395 | SWAP32( toSaveDrive ); 396 | SWAP32( toSaveDriveDepth ); 397 | #endif 398 | 399 | state->write( &toSaveVowelL , sizeof( float )); 400 | state->write( &toSaveVowelR , sizeof( float )); 401 | state->write( &toSaveVowelSync , sizeof( float )); 402 | state->write( &toSaveLFOVowelL , sizeof( float )); 403 | state->write( &toSaveLFOVowelR , sizeof( float )); 404 | state->write( &toSaveLFOVowelLDepth , sizeof( float )); 405 | state->write( &toSaveLFOVowelRDepth , sizeof( float )); 406 | state->write( &toSaveDistortionType , sizeof( float )); 407 | state->write( &toSaveDrive , sizeof( float )); 408 | state->write( &toSaveDistortionChain, sizeof( float )); 409 | 410 | return kResultOk; 411 | } 412 | 413 | //------------------------------------------------------------------------ 414 | tresult PLUGIN_API Transformant::setupProcessing( ProcessSetup& newSetup ) 415 | { 416 | // called before the process call, always in a disabled state (not active) 417 | 418 | // here we keep a trace of the processing mode (offline,...) for example. 419 | currentProcessMode = newSetup.processMode; 420 | 421 | // spotted to fire multiple times... 422 | 423 | if ( pluginProcess != nullptr ) 424 | delete pluginProcess; 425 | 426 | pluginProcess = new PluginProcess( 2, newSetup.sampleRate ); 427 | 428 | syncModel(); 429 | 430 | return AudioEffect::setupProcessing( newSetup ); 431 | } 432 | 433 | //------------------------------------------------------------------------ 434 | tresult PLUGIN_API Transformant::setBusArrangements( SpeakerArrangement* inputs, int32 numIns, SpeakerArrangement* outputs, int32 numOuts ) 435 | { 436 | bool isMonoInOut = SpeakerArr::getChannelCount( inputs[ 0 ]) == 1 && SpeakerArr::getChannelCount( outputs[ 0 ]) == 1; 437 | bool isStereoInOut = SpeakerArr::getChannelCount( inputs[ 0 ]) == 2 && SpeakerArr::getChannelCount( outputs[ 0 ]) == 2; 438 | #ifdef BUILD_AUDIO_UNIT 439 | if ( !isMonoInOut && !isStereoInOut ) { 440 | return AudioEffect::setBusArrangements( inputs, numIns, outputs, numOuts ); // solves auval 4099 error 441 | } 442 | #endif 443 | if ( numIns == 1 && numOuts == 1 ) 444 | { 445 | if ( isMonoInOut ) 446 | { 447 | AudioBus* bus = FCast( audioInputs.at( 0 )); 448 | if ( bus ) 449 | { 450 | // check if we are Mono => Mono, if not we need to recreate the buses 451 | if ( bus->getArrangement() != inputs[0]) 452 | { 453 | removeAudioBusses(); 454 | addAudioInput ( STR16( "Mono In" ), inputs [ 0 ] ); 455 | addAudioOutput( STR16( "Mono Out" ), outputs[ 0 ] ); 456 | } 457 | return kResultOk; 458 | } 459 | } 460 | // the host wants something else than Mono => Mono, in this case we are always Stereo => Stereo 461 | else 462 | { 463 | AudioBus* bus = FCast( audioInputs.at( 0 )); 464 | if ( bus ) 465 | { 466 | // the host wants 2->2 (could be LsRs -> LsRs) 467 | if ( isStereoInOut ) 468 | { 469 | removeAudioBusses(); 470 | addAudioInput ( STR16( "Stereo In"), inputs [ 0 ] ); 471 | addAudioOutput ( STR16( "Stereo Out"), outputs[ 0 ]); 472 | 473 | return kResultTrue; 474 | } 475 | // the host want something different than 1->1 or 2->2 : in this case we want stereo 476 | else if ( bus->getArrangement() != SpeakerArr::kStereo ) 477 | { 478 | removeAudioBusses(); 479 | addAudioInput ( STR16( "Stereo In"), SpeakerArr::kStereo ); 480 | addAudioOutput( STR16( "Stereo Out"), SpeakerArr::kStereo ); 481 | 482 | return kResultFalse; 483 | } 484 | } 485 | } 486 | } 487 | return kResultFalse; 488 | } 489 | 490 | //------------------------------------------------------------------------ 491 | tresult PLUGIN_API Transformant::canProcessSampleSize( int32 symbolicSampleSize ) 492 | { 493 | if ( symbolicSampleSize == kSample32 ) 494 | return kResultTrue; 495 | 496 | // we support double processing 497 | if ( symbolicSampleSize == kSample64 ) 498 | return kResultTrue; 499 | 500 | return kResultFalse; 501 | } 502 | 503 | //------------------------------------------------------------------------ 504 | tresult PLUGIN_API Transformant::notify( IMessage* message ) 505 | { 506 | if ( !message ) 507 | return kInvalidArgument; 508 | 509 | if ( !strcmp( message->getMessageID(), "BinaryMessage" )) 510 | { 511 | const void* data; 512 | uint32 size; 513 | if ( message->getAttributes ()->getBinary( "MyData", data, size ) == kResultOk ) 514 | { 515 | // we are in UI thread 516 | // size should be 100 517 | if ( size == 100 && ((char*)data)[1] == 1 ) // yeah... 518 | { 519 | fprintf( stderr, "[Transformant] received the binary message!\n" ); 520 | } 521 | return kResultOk; 522 | } 523 | } 524 | 525 | return AudioEffect::notify( message ); 526 | } 527 | 528 | void Transformant::syncModel() 529 | { 530 | pluginProcess->distortionPostMix = Calc::toBool( fDistortionChain ); 531 | pluginProcess->distortionTypeCrusher = Calc::toBool( fDistortionType ); 532 | pluginProcess->bitCrusher->setAmount( fDrive ); 533 | pluginProcess->waveShaper->setAmount( fDrive ); 534 | 535 | pluginProcess->formantFilterL->setVowel( fVowelL ); 536 | pluginProcess->formantFilterL->setLFO( fLFOVowelL, fLFOVowelLDepth ); 537 | 538 | // when vowel sync is on, both channels have the same vowel and LFO settings 539 | 540 | if ( Calc::toBool( fVowelSync )) { 541 | pluginProcess->formantFilterR->setVowel( fVowelL ); 542 | pluginProcess->formantFilterR->setLFO( fLFOVowelL, fLFOVowelLDepth ); 543 | } else { 544 | pluginProcess->formantFilterR->setVowel( fVowelR ); 545 | pluginProcess->formantFilterR->setLFO( fLFOVowelR, fLFOVowelRDepth ); 546 | } 547 | } 548 | 549 | } 550 | --------------------------------------------------------------------------------