├── 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 |
12 |
13 |
14 |
15 |
16 |
25 |
26 |
27 |
36 |
37 |
38 |
45 |
46 |
47 |
48 |
49 |
58 |
59 |
60 |
69 |
70 |
71 |
80 |
81 |
82 |
91 |
92 |
93 |
94 |
95 |
104 |
105 |
106 |
113 |
114 |
115 |
122 |
123 |
124 |
125 |
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 |
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 |
--------------------------------------------------------------------------------