├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE.txt ├── README.md ├── include ├── BlobParameter.h ├── BooleanParameter.h ├── ConcurrentParameterSet.h ├── DataParameter.h ├── DecibelParameter.h ├── Event.h ├── EventDispatcher.h ├── FloatParameter.h ├── FrequencyParameter.h ├── IntegerParameter.h ├── Parameter.h ├── ParameterSet.h ├── PluginParameters.h ├── StringParameter.h └── VoidParameter.h └── test ├── CMakeLists.txt ├── MultithreadedTest.cpp ├── PluginParametersTest.cpp └── TestRunner.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Temporary files 2 | *.swp 3 | *~ 4 | *.o 5 | *.so 6 | *.obj 7 | 8 | # Build products 9 | test/pluginparameterstest 10 | pluginparameterstest.exe 11 | 12 | # Test output 13 | test/out/* 14 | Debug/* 15 | test/Debug/* 16 | Release/* 17 | test/Release/* 18 | 19 | # Stuff from various IDE's 20 | *.build 21 | test/*.build 22 | .idea/* 23 | *.xcodeproj 24 | *.perspectivev3 25 | *.pbxuser 26 | *.user 27 | *.sdf 28 | *.opensdf 29 | *.suo 30 | nbproject/* 31 | *.log 32 | *.tlog 33 | *.lastbuildstate 34 | *.sln 35 | *.vcxproj 36 | *.filters 37 | *.pdb 38 | *.ilk 39 | *.cache 40 | 41 | # Vim helpers 42 | tags 43 | cscope.out 44 | 45 | # CMake 46 | CMakeCache.txt 47 | CMakeFiles 48 | CMakeScripts 49 | Makefile 50 | Makefile.in 51 | *.cmake 52 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "include/readerwriterqueue"] 2 | path = include/readerwriterqueue 3 | url = https://github.com/teragonaudio/readerwriterqueue.git 4 | [submodule "include/tinythread"] 5 | path = include/tinythread 6 | url = https://github.com/teragonaudio/tinythreadpp.git 7 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | project(PluginParameters) 3 | 4 | set(CMAKE_INCLUDE_CURRENT_DIR TRUE) 5 | include_directories(${CMAKE_SOURCE_DIR}/include) 6 | include_directories(${CMAKE_SOURCE_DIR}/include/tinythread) 7 | include_directories(${CMAKE_SOURCE_DIR}/include/readerwriterqueue) 8 | 9 | if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") 10 | add_definitions(-DLINUX=1) 11 | set(CMAKE_C_FLAGS "-fmessage-length=0 -m32 -pipe -Wno-trigraphs -std=c99 -O3 -Wmissing-field-initializers -Wall -Wreturn-type -Wunused-variable -pedantic -Wshadow -Wsign-compare -D__cdecl=\"\" -D_POSIX_C_SOURCE=200809L") 12 | set(CMAKE_CXX_FLAGS "-fmessage-length=0 -m32 -pipe -Wno-trigraphs -std=c++11 -O3 -Wmissing-field-initializers -Wall -Wreturn-type -Wunused-variable -pedantic -Wshadow -Wsign-compare -D__cdecl=\"\"") 13 | set(CMAKE_EXE_LINKER_FLAGS "-m32") 14 | elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 15 | set(CMAKE_C_COMPILER "clang") 16 | set(CMAKE_CXX_COMPILER "clang++") 17 | add_definitions(-DMACOSX=1) 18 | set(CMAKE_C_FLAGS "-arch i386 -fmessage-length=0 -pipe -Wno-trigraphs -fpascal-strings -std=c99 -Wmissing-field-initializers -Wall -Wmissing-prototypes -Wreturn-type -Wunused-variable -pedantic -Wshadow -Wsign-compare -Wnewline-eof -Wshorten-64-to-32 -fasm-blocks -mmacosx-version-min=10.7") 19 | set(CMAKE_CXX_FLAGS "-arch i386 -fmessage-length=0 -pipe -Wno-trigraphs -fpascal-strings -stdlib=libc++ -std=c++11 -Wmissing-field-initializers -Wall -Wmissing-prototypes -Wreturn-type -Wunused-variable -pedantic -Wshadow -Wsign-compare -Wnewline-eof -Wshorten-64-to-32 -fasm-blocks -mmacosx-version-min=10.7") 20 | set(CMAKE_EXE_LINKER_FLAGS "-arch i386 -framework Carbon -framework CoreFoundation") 21 | elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows") 22 | add_definitions(/DWIN32=1) 23 | set(CMAKE_C_FLAGS "") 24 | # /EHsc is needed 25 | set(CMAKE_CXX_FLAGS "/EHsc") 26 | endif() 27 | 28 | if("${UNIX}") 29 | if(CMAKE_BUILD_TYPE MATCHES "Debug") 30 | add_definitions("-O0") 31 | add_definitions("-g") 32 | add_definitions("-DDEBUG=1") 33 | else() 34 | add_definitions("-O2") 35 | endif() 36 | endif("${UNIX}") 37 | 38 | add_subdirectory(test) 39 | 40 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Teragon Audio. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | - Redistributions of source code must retain the above copyright notice, 7 | this list of conditions and the following disclaimer. 8 | - Redistributions in binary form must reproduce the above copyright notice, 9 | this list of conditions and the following disclaimer in the documentation 10 | and/or other materials provided with the distribution. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 13 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 16 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 18 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 19 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 20 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 21 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 22 | POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PluginParameters 2 | ================ 3 | 4 | PluginParameters is a simple library used to keep parameter information for an 5 | audio plugin, such as a VST or AudioUnit. The need for this arises from the 6 | fact that most plugins have a number of parameters they need to maintain, and 7 | this information needs to be sent to various parts of the plugin's processing 8 | code. The VST or AudioUnit side of things also needs to query this information 9 | in a more structured manner, and this can lead to plugin code getting very 10 | messy with huge `switch` blocks to get/set the correct value. 11 | 12 | 13 | Features 14 | -------- 15 | 16 | * Provides a parameter database which supports the following data types: 17 | - BlobParameter 18 | - BooleanParameter 19 | - DecibelParameter 20 | - FloatParameter 21 | - FrequencyParameter 22 | - IntegerParameter 23 | - StringParameter 24 | * For all parameter types, you can get the raw value (ie, the thing you 25 | probably need in your code to do useful stuff), or a scaled value between 26 | 0.0 - 1.0. This makes PluginParameters ideal for use in VST plugins, which 27 | need a 0.0 - 1.0 representation of all data. 28 | * The decibel parameter is represented internally as a linear value, which is 29 | more convenient for most audio processing work. Additionally, it is 30 | logarithmic, which is useful when fitting it to a knob or fader control. 31 | * The frequency parameter uses logarithmic scaling for its value, so it is 32 | easier to select higher frequencies. This is particularly convenient for 33 | filters where one MIDI step would otherwise mean a difference of several 34 | hundred hertz. 35 | * All parameters provide a minimum, maximum, and default value. 36 | * All parameter types also have a function to get the displayable value as a 37 | string. 38 | * Parameters have string unit suffixes for pretty-printing the value. For 39 | example, DecibelParameter automatically adds "dB" to the printed value, and 40 | FrequencyParameter adds "Hz" or "kHz", depending on the frequency. 41 | * Parameters can have an unlimited number of observers, which will receive a 42 | callback when the parameter value is changed. Observers can be added or 43 | removed to a parameter at any time. 44 | * Lookup of parameters can be done either by index or by name, with either the 45 | `get()` method or the square brackets (ie, `parameters["frequency"]`). 46 | * Small! Just a few hundred lines of code, and only one CPP file to add to your 47 | project (tinythread.cpp, which is from a third-party library). Simply include 48 | `PluginParameters.h` and you've got the whole library at your fingertips. 49 | * Threadsafe! PluginParameters can be built to run in a multi-threaded 50 | environment ideal for audio plugins, with one high-priority audio thread and 51 | multiple low-priority threads for background tasks or GUI. 52 | * The multi-threaded implementation is based on lock-free queues, and is 53 | completely mutex-free for high performance in realtime audio applications. 54 | 55 | 56 | Usage (Single-Threaded) 57 | ----------------------- 58 | 59 | *Note*: As of PluginParameters version 3.0, the multi-threaded implementation is 60 | the default. To use the single-threaded implementation, you will need to define 61 | the following preprocessor definition in your project: 62 | 63 | ```c++ 64 | #define PLUGINPARAMETERS_MULTITHREADED 0 65 | #include "PluginParameters.h" 66 | ``` 67 | 68 | Assuming that you've pointed your source to include `PluginParameters.h` and 69 | everything compiles fine, your next step is to make a `ParameterSet` and add 70 | some parameters to it. This should probably be done in your plugin's 71 | constructor: 72 | 73 | ```c++ 74 | MyPlugin::MyPlugin() { 75 | // Assumes that there is a ParameterSet member named "parameters" in 76 | // the header file. 77 | this->parameters.add(new BooleanParameter("Awesomeness", true)); 78 | this->parameters.add(new FrequencyParameter("Frequency", 20.0, 20000.0, 10000.0); 79 | this->parameters.add(new DecibelParameter("Gain", -60.0, 3.0, 0.0)); 80 | } 81 | ``` 82 | 83 | For a VST plugin, a ton of boilerplate code regarding parameter values can be 84 | eliminated and replaced with the following: 85 | 86 | ```c++ 87 | float MyPlugin::getParameter(VstInt32 index) { 88 | return this->parameters[index]->getScaledValue(); 89 | } 90 | 91 | void MyPlugin::setParameter(VstInt32 index, float value) { 92 | this->parameters[index]->setScaledValue(value); 93 | } 94 | 95 | void MyPlugin::getParameterName (VstInt32 index, char* label) { 96 | vst_strncpy(label, this->parameters[index]->getName().c_str(), kVstMaxParamStrLen); 97 | } 98 | 99 | void MyPlugin::getParameterDisplay(VstInt32 index, char* text) { 100 | vst_strncpy(text, this->parameters[index]->getDisplayText().c_str(), kVstMaxParamStrLen); 101 | } 102 | ``` 103 | 104 | When you actually want to use the parameter data in your code, it might look 105 | something like this: 106 | 107 | ```c++ 108 | void MyPlugin::processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames) { 109 | if(this->parameters["Awesomeness"]->getValue()) { 110 | for(int i = 0; i < sampleFrames; i++) { 111 | *outputs[0][i] = *inputs[0][i] * this->parameters["Gain"]->getValue(); 112 | *outputs[1][i] = *inputs[1][i] * this->parameters["Gain"]->getValue(); 113 | } 114 | } 115 | } 116 | ``` 117 | 118 | Note: The above example code may look a bit different than your actual 119 | implementation. It's just to give you a general idea as to how the library 120 | should be used. For real-world examples of PluginParameters, check out the 121 | source code for any of the [Teragon Audio plugins][4]. 122 | 123 | 124 | Usage (Multi-threaded) 125 | ---------------------- 126 | 127 | If you intend on using PluginParameters in combination with a plugin GUI, then 128 | you will need to use the multi-threaded features of PluginParameters. Attempting 129 | to use the single-threaded version in a multi-threaded environment may result in 130 | priority inversion (and thus, occaisional stuttering on the audio thread), or 131 | race conditions caused by multiple objects attempting to get/set parameter 132 | values. 133 | 134 | The multi-threaded mode must be enabled at compile-time like so: 135 | 136 | ```c++ 137 | #define PLUGINPARAMETERS_MULTITHREADED 1 138 | #include "PluginParameters.h" 139 | ``` 140 | 141 | *Note*: As of PluginParameters version 3.0, the multi-threaded implementation is 142 | the default; it is not necessary to force this definition anymore. Simply 143 | including `PluginParameters.h` is enough. 144 | 145 | Some dependencies of the multi-threaded implementation require C++11 support, 146 | so you may need to enable this for your compiler as necessary. Likewise, you 147 | will need to add `tinythread.cpp` to the list of files compiled by your 148 | project. 149 | 150 | When a `ConcurrentParameterSet` (which should be used instead of the standard 151 | `PluginParameterSet`) is created by your plugin, it will create a new 152 | low-priority background thread for asynchronous parameter events. This thread 153 | will be automatically shut down and destroyed when the `ConcurrentParameterSet` 154 | is destroyed. 155 | 156 | In multi-threaded mode, you may not directly modify parameter values. Instead, 157 | you must schedule changes via an event dispatcher. Therefore a full 158 | implementation would look something like this: 159 | 160 | In VST plugin's audio processing code: 161 | 162 | ```c++ 163 | MyPlugin::MyPlugin() { 164 | // Assumes that there is a ConcurrentParameterSet member named 165 | // "parameters" in the header file. 166 | this->parameters.add(new BooleanParameter("Awesomeness", true)); 167 | this->parameters.add(new FrequencyParameter("Frequency", 20.0, 20000.0, 10000.0); 168 | this->parameters.add(new DecibelParameter("Gain", -60.0, 3.0, 0.0)); 169 | } 170 | 171 | void MyPlugin::process(float** inputs, float** outputs, long blocksize) { 172 | this->parameters.processRealtimeEvents(); 173 | // Other processing code follows... 174 | } 175 | 176 | float MyPlugin::getParameter(VstInt32 index) { 177 | return this->parameters[index]->getScaledValue(); 178 | } 179 | 180 | void MyPlugin::setParameter(VstInt32 index, float value) { 181 | this->parameters.setScaled(index, value); 182 | } 183 | 184 | void MyPlugin::getParameterName (VstInt32 index, char* label) { 185 | vst_strncpy(label, this->parameters[index]->getName().c_str(), kVstMaxParamStrLen); 186 | } 187 | 188 | void MyPlugin::getParameterDisplay(VstInt32 index, char* text) { 189 | vst_strncpy(text, this->parameters[index]->getDisplayText().c_str(), kVstMaxParamStrLen); 190 | } 191 | 192 | void MyPlugin::suspend() { 193 | this->parameters.pause(); 194 | } 195 | 196 | void MyPlugin::resume() { 197 | this->parameters.resume(); 198 | } 199 | ``` 200 | 201 | In plugin's GUI code: 202 | 203 | ```c++ 204 | // This code will be highly implementation-specific, but basically let's 205 | // assume that some GUI code is ready to set a parameter value after user 206 | // interaction. However in this example we'll assume that the GUI also wants 207 | // to be notified about updates to the parameter, so it therefore would 208 | // implement PluginParameterObserver. 209 | MyGui::onValueUpdated(int index, float newValue) { 210 | // We must pass "this" as the last argument so we don't notify ourselves 211 | // when sending parameter changes to the audio thread. 212 | this->parameters.set(index, newValue, this); 213 | } 214 | 215 | bool MyGui::isRealtimePriority() const { 216 | return false; 217 | } 218 | 219 | void MyGui::onParameterUpdated(const PluginParameter* parameter) { 220 | // Update widget to reflect parameter's new value 221 | } 222 | ``` 223 | 224 | You can also have parameter observers on the realtime thread. This can be 225 | useful to recalculate cached values based on parameter data (like filter 226 | coefficients, for example). 227 | 228 | Note that `ConcurrentParameterSet` *cannot* fully guarantee that the 229 | asynchronous event thread will be ready to process events after the parameter 230 | set itself is finished being constructed. In other words, never do this: 231 | 232 | ```c++ 233 | // Assumes a member of type ThreadsafePluginParameterSet*, and a 234 | // PluginParameterObserver* named observer. 235 | void MyPlugin::initialize() { 236 | parameters = new ThreadsafePluginParameterSet(); 237 | Parameter *p = new BooleanParameter("test"); 238 | p->addObserver(observer); 239 | parameters.add(p); 240 | parameters.set("test", true); // BAD! Here we cannot guarantee that the 241 | // observer will be called. The easy fix is to simply call set() elsewhere 242 | // after this constructor exits. 243 | 244 | // Also never do this: 245 | parameters = new ConcurrentParameterSet(); 246 | delete parameters; 247 | } 248 | ``` 249 | 250 | The reason that this code is bad is because the low-priority event thread may 251 | not be fully started when the call to `set()` has been made, and therefore the 252 | parameter change would not be correctly scheduled on that thread. The only way 253 | to guarantee this behavior would be to have a mutex within both `set()` and 254 | `processRealtimeEvents()`, which has performance consequences for the audio 255 | thread. 256 | 257 | That said, as long as you don't attempt to schedule parameter changes or destroy 258 | the parameter set immediately after construction, `ConcurrentParameterSet` 259 | should be safe and reliable to use. Also it should be noted that all `Parameter` 260 | subclasses provide a constructor which allows you to set a default value, so it 261 | shouldn't be necessary to schedule parameter changes immediately after 262 | constructing the parameter set. 263 | 264 | 265 | Testing 266 | ------- 267 | 268 | PluginParameters comes with a small test suite which is used to develop the 269 | library as well as fix bugs. If you think you've found a bug in 270 | PluginParameters, then please build the test suites and run it before reporting 271 | a bug for your platform. The test suites are built using CMake, and generate two 272 | executables, `pluginparametertest` and `multithreadedtest`. 273 | 274 | PluginParameters is built with [CMake][1] and should compile cleanly out of 275 | the box. Building on unix platforms (including Mac OSX) is simply a matter of 276 | running `cmake . ; make`. On Windows, one can run `cmake.exe -G "Visual Studio 277 | 12"` to generate a Visual Studio project file which can build the project. 278 | 279 | 280 | License 281 | ------- 282 | 283 | PluginParameters is licensed under the BSD licnese. See the file `LICENSE.txt` 284 | provided with the source code for more details. If built in multi-threaded mode, 285 | then code from [TinyThread++][2] and [readerwriterqueue][3] is used. Please see 286 | the respective license files for each of these libraries, which can be found in 287 | the `include` directory. 288 | 289 | Finally, a big thanks to the authors of TinyThread++ and readerwriterqueue for 290 | making this library possible. Writing multi-threaded code is hard! 291 | 292 | 293 | [1]: http://www.cmake.org 294 | [2]: http://tinythreadpp.bitsnbites.eu 295 | [3]: https://github.com/cameron314/readerwriterqueue 296 | [4]: https://github.com/teragonaudio 297 | -------------------------------------------------------------------------------- /include/BlobParameter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Teragon Audio. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * - Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * - Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 17 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | * POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #ifndef __PluginParameters_BlobParameter_h__ 27 | #define __PluginParameters_BlobParameter_h__ 28 | 29 | #include 30 | #include 31 | #include "DataParameter.h" 32 | 33 | namespace teragon { 34 | 35 | class BlobParameter : public DataParameter { 36 | public: 37 | BlobParameter(const ParameterString &inName, void *inData = NULL, size_t inDataSize = 0) : 38 | DataParameter(inName), 39 | data(NULL), dataSize(inDataSize) { 40 | if(inData != NULL && inDataSize > 0) { 41 | data = malloc(inDataSize); 42 | memcpy(data, inData, inDataSize); 43 | } 44 | } 45 | 46 | virtual ~BlobParameter() { 47 | if(data != NULL) { 48 | free(data); 49 | } 50 | } 51 | 52 | virtual const ParameterString getDisplayText() const { 53 | return (data != NULL && dataSize > 0) ? "(Data)" : "(Null)"; 54 | } 55 | 56 | virtual void *getData() const { 57 | return data; 58 | } 59 | 60 | virtual size_t getDataSize() const { 61 | return dataSize; 62 | } 63 | 64 | 65 | #if PLUGINPARAMETERS_MULTITHREADED 66 | protected: 67 | #endif 68 | 69 | virtual void setValue(const void *inData, const size_t inDataSize) { 70 | if(inData == NULL || inDataSize == 0) { 71 | return; 72 | } 73 | if(data != NULL) { 74 | free(data); 75 | } 76 | 77 | data = malloc(inDataSize); 78 | dataSize = inDataSize; 79 | memcpy(data, inData, inDataSize); 80 | 81 | notifyObservers(); 82 | } 83 | 84 | private: 85 | void *data; 86 | size_t dataSize; 87 | }; 88 | 89 | } // namespace teragon 90 | 91 | #endif // __PluginParameters_BlobParameter_h__ 92 | -------------------------------------------------------------------------------- /include/BooleanParameter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Teragon Audio. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * - Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * - Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 17 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | * POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #ifndef __PluginParameters_BooleanParameter_h__ 27 | #define __PluginParameters_BooleanParameter_h__ 28 | 29 | #include "Parameter.h" 30 | 31 | namespace teragon { 32 | 33 | class BooleanParameter : public Parameter { 34 | public: 35 | BooleanParameter(const ParameterString &inName, bool inDefaultValue = false) : 36 | Parameter(inName), value(inDefaultValue) {} 37 | 38 | virtual ~BooleanParameter() {} 39 | 40 | virtual const ParameterString getDisplayText() const { 41 | return value ? "Enabled" : "Disabled"; 42 | } 43 | 44 | virtual const ParameterValue getScaledValue() const { 45 | return getValue(); 46 | } 47 | 48 | virtual const ParameterValue getValue() const { 49 | return value ? 1.0 : 0.0; 50 | } 51 | 52 | #if PLUGINPARAMETERS_MULTITHREADED 53 | protected: 54 | #endif 55 | 56 | virtual void setScaledValue(const ParameterValue inValue) { 57 | setValue(inValue); 58 | } 59 | 60 | virtual void setValue(const ParameterValue inValue) { 61 | value = inValue > 0.5 ? true : false; 62 | Parameter::setValue(inValue); 63 | } 64 | 65 | private: 66 | bool value; 67 | }; 68 | 69 | } // namespace teragon 70 | 71 | #endif // __PluginParameters_BooleanParameter_h__ 72 | -------------------------------------------------------------------------------- /include/ConcurrentParameterSet.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Teragon Audio. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * - Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * - Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 17 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | * POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #ifndef __PluginParameters_ConcurrentParameterSet_h__ 27 | #define __PluginParameters_ConcurrentParameterSet_h__ 28 | 29 | #include "ParameterSet.h" 30 | #include "Parameter.h" 31 | #include "EventDispatcher.h" 32 | 33 | namespace teragon { 34 | 35 | #if PLUGINPARAMETERS_MULTITHREADED 36 | static void asyncDispatcherCallback(void *arg) { 37 | EventDispatcher *dispatcher = reinterpret_cast(arg); 38 | dispatcher->start(); 39 | 40 | while(!dispatcher->isKilled()) { 41 | // WARNING: Deadlock can occur here 42 | // If this thread is interrupted between these two lines, and the main thread 43 | // exits (ie, the ConcurrentParameterSet is destroyed directly after creation), 44 | // then the corresponding notify() call thrown by kill() will not be received. 45 | // To avoid this problem, you should not destroy a ConcurrentParameterSet right 46 | // after creating it. 47 | dispatcher->wait(); 48 | // This thread can be notified both in case of an event callback or when the 49 | // thread should join and exit. In the second case, we should not attempt to 50 | // run process(), as bad things may happen. 51 | if(!dispatcher->isKilled()) { 52 | dispatcher->process(); 53 | } 54 | } 55 | } 56 | #endif // PLUGINPARAMETERS_MULTITHREADED 57 | 58 | class ConcurrentParameterSet : public ParameterSet, public EventScheduler { 59 | #if PLUGINPARAMETERS_MULTITHREADED 60 | public: 61 | /** 62 | * Create a new parameter set which can be used by multiple threads. This 63 | * assumes that there is one high-priority thread which is executed from a 64 | * runloop, and one or more low-priority threads for background tasks or GUI. 65 | * 66 | * Simply using this class in place of ParameterSet does not guarantee 67 | * thread-safe code. See the top-level README for information and examples 68 | * regarding correct usage of this class. 69 | */ 70 | explicit ConcurrentParameterSet() : ParameterSet(), EventScheduler(), 71 | asyncDispatcher(this, false), realtimeDispatcher(this, true), 72 | asyncDispatcherThread(asyncDispatcherCallback, &asyncDispatcher), 73 | realtimeEventLoopPaused(false) { 74 | asyncDispatcherThread.set_name("PluginParametersAsyncDispatcher"); 75 | asyncDispatcherThread.set_low_priority(); 76 | 77 | // Wait for the async dispatcher thread to be fully started. 78 | while(!asyncDispatcher.isStarted()) { 79 | sleep(10); 80 | } 81 | } 82 | 83 | virtual ~ConcurrentParameterSet() { 84 | asyncDispatcher.kill(); 85 | asyncDispatcherThread.join(); 86 | } 87 | 88 | /** 89 | * Process events on the realtime dispatcher. This method should be called 90 | * in the plugin's process() function. 91 | */ 92 | virtual void processRealtimeEvents() { 93 | realtimeDispatcher.process(); 94 | } 95 | 96 | /** 97 | * Set a parameter's value. When PLUGINPARAMETERS_MULTITHREADED is set, 98 | * then this method must be used rather than Parameter::set(). The actual 99 | * operation will be dispatched to the main thread and executed there, and 100 | * any asynchronous observers will be notified afterwards. This means that 101 | * there can a some small delay before other asynchronous observers receive 102 | * their notifications. Realtime observers, however, will be notified during 103 | * the next call to processRealtimeEvents(). 104 | * 105 | * @param name Parameter name. No error checking is done here, you must ensure 106 | * that the name is valid. Otherwise, this call will fail silently. 107 | * @param value New value 108 | * @param sender Sending object (can be NULL). If non-NULL, then this object 109 | * will *not* receive notifications on the observer callback, 110 | * since presumably this object is pushing state to other 111 | * observers. 112 | */ 113 | virtual void set(const ParameterString &name, const ParameterValue value, 114 | ParameterObserver *sender = NULL) { 115 | Parameter *parameter = get(name); 116 | if(parameter != NULL) { 117 | set(parameter, value, sender); 118 | } 119 | } 120 | 121 | /** 122 | * Set a parameter's value. When PLUGINPARAMETERS_MULTITHREADED is set, 123 | * then this method must be used rather than Parameter::set(). The actual 124 | * operation will be dispatched to the main thread and executed there, and 125 | * any asynchronous observers will be notified afterwards. This means that 126 | * there can a some small delay before other asynchronous observers receive 127 | * their notifications. Realtime observers, however, will be notified during 128 | * the next call to processRealtimeEvents(). 129 | * 130 | * @param index Parameter index. No error checking is done here, you must 131 | * ensure that the index is valid. 132 | * @param value New value 133 | * @param sender Sending object (can be NULL). If non-NULL, then this object 134 | * will *not* receive notifications on the observer callback, 135 | * since presumably this object is pushing state to other 136 | * observers. 137 | */ 138 | virtual void set(const size_t index, const ParameterValue value, 139 | ParameterObserver *sender = NULL) { 140 | return set(parameterList.at(index), value, sender); 141 | } 142 | 143 | /** 144 | * Set a parameter's value. When PLUGINPARAMETERS_MULTITHREADED is set, 145 | * then this method must be used rather than Parameter::set(). The actual 146 | * operation will be dispatched to the main thread and executed there, and 147 | * any asynchronous observers will be notified afterwards. This means that 148 | * there can a some small delay before other asynchronous observers receive 149 | * their notifications. Realtime observers, however, will be notified during 150 | * the next call to processRealtimeEvents(). 151 | * 152 | * @param parameter Parameter 153 | * @param value New value 154 | * @param sender Sending object (can be NULL). If non-NULL, then this object 155 | * will *not* receive notifications on the observer callback, 156 | * since presumably this object is pushing state to other 157 | * observers. 158 | */ 159 | virtual void set(Parameter *parameter, const ParameterValue value, 160 | ParameterObserver *sender = NULL) { 161 | scheduleEvent(new Event(parameter, value, true, sender)); 162 | } 163 | 164 | /** 165 | * Set a parameter's value. When PLUGINPARAMETERS_MULTITHREADED is set, 166 | * then this method must be used rather than Parameter::set(). The actual 167 | * operation will be dispatched to the main thread and executed there, and 168 | * any asynchronous observers will be notified afterwards. This means that 169 | * there can a some small delay before other asynchronous observers receive 170 | * their notifications. Realtime observers, however, will be notified during 171 | * the next call to processRealtimeEvents(). 172 | * 173 | * @param name Parameter name. No error checking is done here, you must ensure 174 | * that the name is valid. Otherwise, this call will fail silently. 175 | * @param value New value 176 | * @param sender Sending object (can be NULL). If non-NULL, then this object 177 | * will *not* receive notifications on the observer callback, 178 | * since presumably this object is pushing state to other 179 | * observers. 180 | */ 181 | virtual void setScaled(const ParameterString &name, const ParameterValue value, 182 | ParameterObserver *sender = NULL) { 183 | Parameter *parameter = get(name); 184 | if(parameter != NULL) { 185 | setScaled(parameter, value, sender); 186 | } 187 | } 188 | 189 | /** 190 | * Set a parameter's value. When PLUGINPARAMETERS_MULTITHREADED is set, 191 | * then this method must be used rather than Parameter::set(). The actual 192 | * operation will be dispatched to the main thread and executed there, and 193 | * any asynchronous observers will be notified afterwards. This means that 194 | * there can a some small delay before other asynchronous observers receive 195 | * their notifications. Realtime observers, however, will be notified during 196 | * the next call to processRealtimeEvents(). 197 | * 198 | * @param index Parameter index. No error checking is done here, you must 199 | * ensure that the index is valid. 200 | * @param value New value 201 | * @param sender Sending object (can be NULL). If non-NULL, then this object 202 | * will *not* receive notifications on the observer callback, 203 | * since presumably this object is pushing state to other 204 | * observers. 205 | */ 206 | virtual void setScaled(const size_t index, const ParameterValue value, 207 | ParameterObserver *sender = NULL) { 208 | return setScaled(parameterList.at(index), value, sender); 209 | } 210 | 211 | /** 212 | * Set a parameter's value. When PLUGINPARAMETERS_MULTITHREADED is set, 213 | * then this method must be used rather than Parameter::set(). The actual 214 | * operation will be dispatched to the main thread and executed there, and 215 | * any asynchronous observers will be notified afterwards. This means that 216 | * there can a some small delay before other asynchronous observers receive 217 | * their notifications. Realtime observers, however, will be notified during 218 | * the next call to processRealtimeEvents(). 219 | * 220 | * @param parameter Parameter 221 | * @param value New value 222 | * @param sender Sending object (can be NULL). If non-NULL, then this object 223 | * will *not* receive notifications on the observer callback, 224 | * since presumably this object is pushing state to other 225 | * observers. 226 | */ 227 | virtual void setScaled(Parameter *parameter, const ParameterValue value, 228 | ParameterObserver *sender = NULL) { 229 | scheduleEvent(new ScaledEvent(parameter, value, true, sender)); 230 | } 231 | 232 | /** 233 | * Set a parameter's value. When PLUGINPARAMETERS_MULTITHREADED is set, 234 | * then this method must be used rather than Parameter::set(). The actual 235 | * operation will be dispatched to the main thread and executed there, and 236 | * any asynchronous observers will be notified afterwards. This means that 237 | * there can a some small delay before other asynchronous observers receive 238 | * their notifications. Realtime observers, however, will be notified during 239 | * the next call to processRealtimeEvents(). 240 | * 241 | * @param name Parameter name. No error checking is done here, you must ensure 242 | * that the name is valid. Otherwise, this call will fail silently. 243 | * @param value New data value 244 | * @param dataSize Data size, in bytes 245 | * @param sender Sending object (can be NULL). If non-NULL, then this object 246 | * will *not* receive notifications on the observer callback, 247 | * since presumably this object is pushing state to other 248 | * observers. 249 | */ 250 | virtual void setData(const ParameterString &name, const void *data, 251 | const size_t dataSize, ParameterObserver *sender = NULL) { 252 | Parameter *parameter = get(name); 253 | if(parameter != NULL) { 254 | setData(parameter, data, dataSize, sender); 255 | } 256 | } 257 | 258 | /** 259 | * Set a parameter's value. When PLUGINPARAMETERS_MULTITHREADED is set, 260 | * then this method must be used rather than Parameter::set(). The actual 261 | * operation will be dispatched to the main thread and executed there, and 262 | * any asynchronous observers will be notified afterwards. This means that 263 | * there can a some small delay before other asynchronous observers receive 264 | * their notifications. Realtime observers, however, will be notified during 265 | * the next call to processRealtimeEvents(). 266 | * 267 | * @param index Parameter index. No error checking is done here, you must 268 | * ensure that the index is valid. 269 | * @param value New data value 270 | * @param dataSize Data size, in bytes 271 | * @param sender Sending object (can be NULL). If non-NULL, then this object 272 | * will *not* receive notifications on the observer callback, 273 | * since presumably this object is pushing state to other 274 | * observers. 275 | */ 276 | virtual void setData(const size_t index, const void *data, 277 | const size_t dataSize, ParameterObserver *sender = NULL) { 278 | return setData(parameterList.at(index), data, dataSize, sender); 279 | } 280 | 281 | /** 282 | * Set a parameter's value. When PLUGINPARAMETERS_MULTITHREADED is set, 283 | * then this method must be used rather than Parameter::set(). The actual 284 | * operation will be dispatched to the main thread and executed there, and 285 | * any asynchronous observers will be notified afterwards. This means that 286 | * there can a some small delay before other asynchronous observers receive 287 | * their notifications. Realtime observers, however, will be notified during 288 | * the next call to processRealtimeEvents(). 289 | * 290 | * @param parameter Parameter 291 | * @param value New data value 292 | * @param dataSize Data size, in bytes 293 | * @param sender Sending object (can be NULL). If non-NULL, then this object 294 | * will *not* receive notifications on the observer callback, 295 | * since presumably this object is pushing state to other 296 | * observers. 297 | */ 298 | virtual void setData(Parameter *parameter, const void *data, 299 | const size_t dataSize, ParameterObserver *sender = NULL) { 300 | DataParameter *dataParameter = dynamic_cast(parameter); 301 | if(dataParameter != NULL) { 302 | scheduleEvent(new DataEvent(dataParameter, data, dataSize, true, sender)); 303 | } 304 | } 305 | 306 | /** 307 | * Pause normal processing of realtime events. When this method is called, 308 | * then events will be executed on both the realtime and asynchronous 309 | * threads immediately after scheduling them. This is necessary so that the 310 | * GUI still looks and behaves normally when even when audio processing 311 | * is stopped. 312 | * 313 | * This method should be called by the plugin when the transport changes 314 | * from playback to paused/stopped. Leaving a ConcurrentParameterSet paused 315 | * while playback is active may result in priority inversion, be careful! 316 | */ 317 | virtual void pause() { 318 | realtimeEventLoopPaused = true; 319 | } 320 | 321 | /** 322 | * Resume normal processing of realtime events. This method should be 323 | * called by the plugin when the transport changes from pause/stopped 324 | * to playing. 325 | */ 326 | virtual void resume() { 327 | realtimeEventLoopPaused = false; 328 | } 329 | 330 | static void sleep(const unsigned long milliseconds) { 331 | #if WIN32 332 | Sleep(milliseconds); 333 | #else 334 | usleep(((useconds_t)milliseconds * 1000)); 335 | #endif 336 | } 337 | 338 | protected: 339 | virtual void scheduleEvent(Event *event) { 340 | if(!asyncDispatcher.isStarted()) { 341 | return; 342 | } 343 | else if(asyncDispatcher.isKilled()) { 344 | return; 345 | } 346 | 347 | if(event->isRealtime) { 348 | realtimeDispatcher.add(event); 349 | } 350 | else { 351 | asyncDispatcher.add(event); 352 | asyncDispatcher.notify(); 353 | } 354 | 355 | if(realtimeEventLoopPaused) { 356 | processRealtimeEvents(); 357 | } 358 | } 359 | 360 | private: 361 | EventDispatcher asyncDispatcher; 362 | EventDispatcher realtimeDispatcher; 363 | EventDispatcherThread asyncDispatcherThread; 364 | bool realtimeEventLoopPaused; 365 | 366 | #endif // PLUGINPARAMETERS_MULTITHREADED 367 | }; 368 | 369 | } // namespace teragon 370 | 371 | #endif // __PluginParameters_ConcurrentParameterSet_h__ 372 | -------------------------------------------------------------------------------- /include/DataParameter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Teragon Audio. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * - Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * - Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 17 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | * POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #ifndef __PluginParameters_DataParameter_h__ 27 | #define __PluginParameters_DataParameter_h__ 28 | 29 | #include "Parameter.h" 30 | 31 | namespace teragon { 32 | 33 | /** 34 | * This class is intended for non-calculation data holders, such as strings or blobs. 35 | */ 36 | class DataParameter : public Parameter { 37 | public: 38 | DataParameter(const ParameterString &inName) : Parameter(inName, 0.0, 1.0, 0.0) {} 39 | virtual ~DataParameter() {} 40 | 41 | virtual const ParameterValue getScaledValue() const { 42 | return 0.0; 43 | } 44 | 45 | virtual void setScaledValue(const ParameterValue value) {} 46 | 47 | virtual void setValue(const ParameterValue inValue) {} 48 | 49 | #if PLUGINPARAMETERS_MULTITHREADED 50 | friend class Event; 51 | friend class DataEvent; 52 | 53 | protected: 54 | #endif 55 | 56 | virtual void setValue(const void *inData, const size_t inDataSize) = 0; 57 | }; 58 | 59 | } // namespace teragon 60 | 61 | #endif // __PluginParameters_DataParameter_h__ 62 | -------------------------------------------------------------------------------- /include/DecibelParameter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Teragon Audio. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * - Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * - Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 17 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | * POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #ifndef __PluginParameters_DecibelParameter_h__ 27 | #define __PluginParameters_DecibelParameter_h__ 28 | 29 | #include 30 | #include 31 | #include "FloatParameter.h" 32 | 33 | namespace teragon { 34 | 35 | class DecibelParameter : public FloatParameter { 36 | public: 37 | /** 38 | * Create a new parameter for a volume in decibels. Note that this parameter 39 | * represents its value internally as a *linear* value. If you actually need 40 | * this value in decibels, see the convertLinearToDecibels() method. 41 | */ 42 | DecibelParameter(const ParameterString &inName, 43 | ParameterValue inMinValue, 44 | ParameterValue inMaxValue, 45 | ParameterValue inDefaultValue) : 46 | FloatParameter(inName, convertDecibelsToLinear(inMinValue), 47 | convertDecibelsToLinear(inMaxValue), 48 | convertDecibelsToLinear(inDefaultValue)) { 49 | logMinValue = log(getMinValue()); 50 | range = log(getMaxValue()) - log(getMinValue()); 51 | } 52 | 53 | virtual ~DecibelParameter() {} 54 | 55 | virtual const ParameterString getDisplayText() const { 56 | std::stringstream numberFormatter; 57 | numberFormatter.precision(getDisplayPrecision()); 58 | numberFormatter << std::fixed << convertLinearToDecibels(getValue()); 59 | return numberFormatter.str() + " dB"; 60 | } 61 | 62 | static const ParameterValue convertDecibelsToLinear(const ParameterValue decibels) { 63 | return pow(10.0, decibels / 20.0); 64 | } 65 | 66 | static const ParameterValue convertLinearToDecibels(const ParameterValue linear) { 67 | return 20.0 * log10(linear); 68 | } 69 | 70 | virtual const ParameterValue getScaledValue() const { 71 | return (log(getValue()) - logMinValue) / range; 72 | } 73 | 74 | virtual void setScaledValue(const ParameterValue inValue) { 75 | setValue(exp(inValue * range + logMinValue)); 76 | } 77 | 78 | private: 79 | double logMinValue; 80 | double range; 81 | }; 82 | 83 | } // namespace teragon 84 | 85 | #endif // __PluginParameters_DecibelParameter_h__ 86 | -------------------------------------------------------------------------------- /include/Event.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Teragon Audio. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * - Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * - Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 17 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | * POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #ifndef __PluginParameters_Event_h__ 27 | #define __PluginParameters_Event_h__ 28 | 29 | #include 30 | #include "Parameter.h" 31 | #include "StringParameter.h" 32 | 33 | namespace teragon { 34 | 35 | class Event { 36 | public: 37 | Event(Parameter *p, const ParameterValue v, 38 | bool realtime = false, const ParameterObserver *s = NULL) : 39 | parameter(p), value(v), isRealtime(realtime), sender(s) {} 40 | 41 | virtual ~Event() {} 42 | 43 | virtual void apply() { 44 | parameter->setValue(value); 45 | } 46 | 47 | Parameter *parameter; 48 | const ParameterValue value; 49 | bool isRealtime; 50 | const ParameterObserver *sender; 51 | 52 | private: 53 | // Disallow assignment operator 54 | Event &operator = (const Event &) { 55 | return *this; 56 | } 57 | }; 58 | 59 | class ScaledEvent : public Event { 60 | public: 61 | ScaledEvent(Parameter *p, const ParameterValue v, 62 | bool realtime = false, const ParameterObserver *s = NULL) : 63 | Event(p, v, realtime, s) {} 64 | 65 | virtual ~ScaledEvent() {} 66 | 67 | virtual void apply() { 68 | parameter->setScaledValue(value); 69 | } 70 | }; 71 | 72 | class DataEvent : public Event { 73 | public: 74 | DataEvent(DataParameter *p, const void *inData, const size_t inDataSize, 75 | bool realtime = false, const ParameterObserver *s = NULL) : 76 | Event(dynamic_cast(p), 0, realtime, s), 77 | dataParameter(p), dataValue(NULL), dataSize(inDataSize) { 78 | if(inDataSize > 0 && inData != NULL) { 79 | dataValue = malloc(inDataSize); 80 | memcpy(dataValue, inData, inDataSize); 81 | } 82 | } 83 | 84 | virtual ~DataEvent() { 85 | if(dataValue != NULL) { 86 | free(dataValue); 87 | } 88 | } 89 | 90 | virtual void apply() { 91 | dataParameter->setValue(dataValue, dataSize); 92 | } 93 | 94 | DataParameter *dataParameter; 95 | void *dataValue; 96 | const size_t dataSize; 97 | }; 98 | 99 | } // namespace teragon 100 | 101 | #endif // __PluginParameters_Event_h__ 102 | -------------------------------------------------------------------------------- /include/EventDispatcher.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Teragon Audio. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * - Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * - Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 17 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | * POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | 27 | #ifndef __PluginParameters_EventDispatcher_h__ 28 | #define __PluginParameters_EventDispatcher_h__ 29 | 30 | #if PLUGINPARAMETERS_MULTITHREADED 31 | #include "readerwriterqueue/readerwriterqueue.h" 32 | #include "tinythread/source/tinythread.h" 33 | #endif 34 | 35 | #include "Event.h" 36 | #include "Parameter.h" 37 | 38 | namespace teragon { 39 | 40 | #if PLUGINPARAMETERS_MULTITHREADED 41 | typedef tthread::thread EventDispatcherThread; 42 | typedef tthread::lock_guard EventDispatcherLockGuard; 43 | typedef tthread::mutex EventDispatcherMutex; 44 | typedef tthread::condition_variable EventDispatcherConditionVariable; 45 | #endif 46 | 47 | class EventScheduler { 48 | public: 49 | EventScheduler() {} 50 | virtual ~EventScheduler() {} 51 | 52 | virtual void scheduleEvent(Event *event) = 0; 53 | }; 54 | 55 | class EventDispatcher { 56 | #if PLUGINPARAMETERS_MULTITHREADED 57 | public: 58 | EventDispatcher(EventScheduler *s, bool realtime) : 59 | eventQueue(), scheduler(s), isRealtime(realtime), started(false), killed(false) {} 60 | 61 | virtual ~EventDispatcher() {} 62 | 63 | void add(Event *event) { 64 | eventQueue.enqueue(event); 65 | } 66 | 67 | void process() { 68 | Event *event = NULL; 69 | while(eventQueue.try_dequeue(event)) { 70 | if(event != NULL) { 71 | // Only execute parameter changes on the realtime thread 72 | if(isRealtime) { 73 | event->apply(); 74 | } 75 | 76 | // Notify all observers of the same type 77 | for(size_t i = 0; i < event->parameter->getNumObservers(); ++i) { 78 | ParameterObserver *observer = event->parameter->getObserver(i); 79 | if(observer != NULL && 80 | observer->isRealtimePriority() == isRealtime && 81 | observer != event->sender) { 82 | observer->onParameterUpdated(event->parameter); 83 | } 84 | } 85 | 86 | if(isRealtime) { 87 | // Re-dispatch the event to the async thread 88 | event->isRealtime = false; 89 | scheduler->scheduleEvent(event); 90 | } 91 | else { 92 | // If this is the async thread, then all observers know about the 93 | // parameter change and this event can be deleted. 94 | delete event; 95 | } 96 | } 97 | event = NULL; 98 | } 99 | } 100 | 101 | volatile bool isStarted() const { 102 | return started; 103 | } 104 | 105 | void start() { 106 | started = true; 107 | } 108 | 109 | volatile bool isKilled() const { 110 | return killed; 111 | } 112 | 113 | void kill() { 114 | killed = true; 115 | notify(); 116 | } 117 | 118 | void notify() { 119 | waitLock.notify_all(); 120 | } 121 | 122 | void wait() { 123 | waitLock.wait(mutex); 124 | } 125 | 126 | private: 127 | tthread::condition_variable waitLock; 128 | EventDispatcherMutex mutex; 129 | moodycamel::ReaderWriterQueue eventQueue; 130 | 131 | EventScheduler *scheduler; 132 | const bool isRealtime; 133 | volatile bool started; 134 | volatile bool killed; 135 | 136 | #endif // PLUGINPARAMETERS_MULTITHREADED 137 | }; 138 | 139 | } // namespace teragon 140 | 141 | #endif // __PluginParameters_EventDispatcher_h__ -------------------------------------------------------------------------------- /include/FloatParameter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Teragon Audio. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * - Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * - Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 17 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | * POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #ifndef __PluginParameters_FloatParameter_h__ 27 | #define __PluginParameters_FloatParameter_h__ 28 | 29 | #include 30 | #include "Parameter.h" 31 | 32 | namespace teragon { 33 | 34 | class FloatParameter : public Parameter { 35 | public: 36 | FloatParameter(const ParameterString &inName, 37 | ParameterValue inMinValue, 38 | ParameterValue inMaxValue, 39 | ParameterValue inDefaultValue) : 40 | Parameter(inName, inMinValue, inMaxValue, inDefaultValue) { 41 | range = inMaxValue - inMinValue; 42 | } 43 | 44 | virtual ~FloatParameter() {} 45 | 46 | virtual const ParameterString getDisplayText() const { 47 | std::stringstream numberFormatter; 48 | numberFormatter.precision(getDisplayPrecision()); 49 | numberFormatter << std::fixed << getValue(); 50 | std::string result = numberFormatter.str(); 51 | if(getUnit().length() > 0) { 52 | result.append(" ").append(getUnit()); 53 | } 54 | return result; 55 | } 56 | 57 | virtual const ParameterValue getScaledValue() const { 58 | return (getValue() - getMinValue()) / range; 59 | } 60 | 61 | #if PLUGINPARAMETERS_MULTITHREADED 62 | protected: 63 | #endif 64 | 65 | virtual void setScaledValue(const ParameterValue inValue) { 66 | setValue(inValue * range + getMinValue()); 67 | } 68 | 69 | private: 70 | ParameterValue range; 71 | }; 72 | 73 | } // namespace teragon 74 | 75 | #endif // __PluginParameters_FloatParameter_h__ 76 | -------------------------------------------------------------------------------- /include/FrequencyParameter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Teragon Audio. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * - Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * - Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 17 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | * POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #ifndef __PluginParameters_FrequencyParameter_h__ 27 | #define __PluginParameters_FrequencyParameter_h__ 28 | 29 | #include 30 | #include 31 | #include "Parameter.h" 32 | 33 | namespace teragon { 34 | 35 | class FrequencyParameter : public Parameter { 36 | public: 37 | FrequencyParameter(const ParameterString &inName, 38 | ParameterValue inMinValue, 39 | ParameterValue inMaxValue, 40 | ParameterValue inDefaultValue) : 41 | Parameter(inName, inMinValue, inMaxValue, inDefaultValue) { 42 | logMinValue = log(inMinValue); 43 | range = log(inMaxValue) - log(inMinValue); 44 | } 45 | 46 | virtual ~FrequencyParameter() {} 47 | 48 | static ParameterString getFormattedFrequency(const ParameterValue frequency, 49 | const unsigned int displayPrecision) { 50 | if(frequency >= 1000.0) { 51 | std::stringstream numberFormatter; 52 | numberFormatter.precision(displayPrecision); 53 | numberFormatter << std::fixed << frequency / 1000.0; 54 | return numberFormatter.str() + " kHz"; 55 | } 56 | else { 57 | std::stringstream numberFormatter; 58 | numberFormatter.precision(displayPrecision); 59 | numberFormatter << std::fixed << frequency; 60 | return numberFormatter.str() + " Hz"; 61 | } 62 | } 63 | 64 | virtual const ParameterString getDisplayText() const { 65 | return getFormattedFrequency(getValue(), getDisplayPrecision()); 66 | } 67 | 68 | virtual const ParameterValue getScaledValue() const { 69 | return (log(getValue()) - logMinValue) / range; 70 | } 71 | 72 | #if PLUGINPARAMETERS_MULTITHREADED 73 | protected: 74 | #endif 75 | 76 | virtual void setScaledValue(const ParameterValue inValue) { 77 | setValue(exp(inValue * range + logMinValue)); 78 | } 79 | 80 | private: 81 | double logMinValue; 82 | double range; 83 | }; 84 | 85 | } // namespace teragon 86 | 87 | #endif // __PluginParameters_FrequencyParameter_h__ 88 | -------------------------------------------------------------------------------- /include/IntegerParameter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Teragon Audio. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * - Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * - Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 17 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | * POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #ifndef __PluginParameters_IntegerParameter_h__ 27 | #define __PluginParameters_IntegerParameter_h__ 28 | 29 | #include 30 | #include "FloatParameter.h" 31 | 32 | namespace teragon { 33 | 34 | class IntegerParameter : public FloatParameter { 35 | public: 36 | IntegerParameter(const ParameterString &inName, 37 | int inMinValue, 38 | int inMaxValue, 39 | int inDefaultValue) : 40 | FloatParameter(inName, (ParameterValue)inMinValue, 41 | (ParameterValue)inMaxValue, 42 | (ParameterValue)inDefaultValue) {} 43 | 44 | virtual ~IntegerParameter() {} 45 | 46 | virtual const ParameterString getDisplayText() const { 47 | std::stringstream numberFormatter; 48 | numberFormatter << (int)getValue(); 49 | std::string result = numberFormatter.str(); 50 | if(getUnit().length() > 0) { 51 | result.append(" ").append(getUnit()); 52 | } 53 | return result; 54 | } 55 | }; 56 | 57 | } // namespace teragon 58 | 59 | #endif // __PluginParameters_IntegerParameter_h__ 60 | -------------------------------------------------------------------------------- /include/Parameter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Teragon Audio. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * - Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * - Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 17 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | * POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #ifndef __PluginParameters_Parameter_h__ 27 | #define __PluginParameters_Parameter_h__ 28 | 29 | #include 30 | #include 31 | 32 | namespace teragon { 33 | 34 | typedef std::string ParameterString; 35 | typedef double ParameterValue; 36 | 37 | static const unsigned int kDefaultDisplayPrecision = 2; 38 | 39 | class Parameter; 40 | 41 | class ParameterObserver { 42 | public: 43 | ParameterObserver() {} 44 | virtual ~ParameterObserver() {} 45 | 46 | #if PLUGINPARAMETERS_MULTITHREADED 47 | virtual bool isRealtimePriority() const = 0; 48 | #endif 49 | 50 | /** 51 | * Method to be called when a parameter's value has been updated. 52 | */ 53 | virtual void onParameterUpdated(const Parameter *parameter) = 0; 54 | }; 55 | 56 | typedef std::vector ParameterObserverMap; 57 | 58 | class Parameter { 59 | public: 60 | /** 61 | * Create a new parameter with defaults for the minimum and maximum values. 62 | * 63 | * @param inName The parameter name 64 | */ 65 | Parameter(const ParameterString &inName) : 66 | name(inName), unit(""), minValue(0.0), maxValue(1.0), defaultValue(0.0), value(0.0), 67 | precision(kDefaultDisplayPrecision), description("") {} 68 | 69 | /** 70 | * Create a new floating point parameter. This is probably the most common 71 | * parameter type used in a plugin. 72 | * 73 | * @param inName The parameter name 74 | * @param inMinValue The parameter's minimum value 75 | * @param inMaxValue The parameter's maximum value 76 | * @param inDefaultValue The parameter's initial default value 77 | * 78 | * Note that there is *no* checking done for the parameter ranges, so you 79 | * need to make sure that they are sane. If the range was otherwise invalid, 80 | * an exception would need to be thrown, and that would therefore impose the 81 | * requirement of exception handling on the plugin developer. 82 | * 83 | * Therefore this class has been intentionally kept rather minimal, and 84 | * operates under the assumption that you are not insane and choose sensible 85 | * values for the minimum, maximum, and default values. 86 | */ 87 | Parameter(const ParameterString &inName, 88 | ParameterValue inMinValue, 89 | ParameterValue inMaxValue, 90 | ParameterValue inDefaultValue) : 91 | name(inName), unit(""), minValue(inMinValue), maxValue(inMaxValue), defaultValue(inDefaultValue), 92 | value(inDefaultValue), precision(kDefaultDisplayPrecision), description("") {} 93 | 94 | virtual ~Parameter() {} 95 | 96 | /** 97 | * @return The parameter's display name 98 | */ 99 | const ParameterString &getName() const { 100 | return name; 101 | } 102 | 103 | /** 104 | * Get the parameter's name for serialization operations. All characters which 105 | * are not in the A-Z, a-z, 0-9 range are simply removed. 106 | * 107 | * @return A the parameter's name, safe for serialization operations 108 | */ 109 | 110 | const ParameterString getSafeName() const { 111 | return makeSafeName(name); 112 | } 113 | 114 | /** 115 | * Get the serialized version of a string 116 | * 117 | * @param string The string to convert 118 | * @return A string safe for serialization operations 119 | */ 120 | static const ParameterString makeSafeName(const ParameterString &string) { 121 | ParameterString result; 122 | for(size_t i = 0; i < string.length(); ++i) { 123 | if(((string[i] >= 'a' && string[i] <= 'z') || 124 | (string[i] >= '0' && string[i] <= '9') || 125 | (string[i] >= 'A' && string[i] <= 'Z'))) { 126 | result += string[i]; 127 | } 128 | } 129 | return result; 130 | } 131 | 132 | /** 133 | * @return The display text for the parameter 134 | */ 135 | virtual const ParameterString getDisplayText() const = 0; 136 | 137 | /** 138 | * @return The parameter's value, scaled in the range {0.0 - 1.0} 139 | */ 140 | virtual const ParameterValue getScaledValue() const = 0; 141 | 142 | /** 143 | * Get the parameter's interval value, which will be between the minimum 144 | * and maximum values set in the constructor. 145 | */ 146 | virtual const ParameterValue getValue() const { 147 | return value; 148 | } 149 | 150 | #if PLUGINPARAMETERS_MULTITHREADED 151 | friend class Event; 152 | friend class ScaledEvent; 153 | 154 | // The multi-threaded version shouldn't allow parameters to have their value 155 | // be directly set in this manner. Instead, all parameter setting must be 156 | // done by the owning parameter set, as it can dispatch the operation on 157 | // the correct thread. Allowing this function to remain public might tempt 158 | // misuse when caching parameters to directly get/set their value, as this 159 | // would not notify all observers on both threads (not to mention other 160 | // potential concurrency problems). 161 | protected: 162 | #endif 163 | 164 | /** 165 | * Set the parameter's interval value directly. 166 | * 167 | * @param value Value, which must be between the minimum and maximum values 168 | */ 169 | virtual void setValue(const ParameterValue inValue) { 170 | // TODO: Possible ABA problem here 171 | if(value != inValue) { 172 | value = inValue; 173 | notifyObservers(); 174 | } 175 | } 176 | 177 | /** 178 | * Set the parameter's value, scaled in the range {0.0 - 1.0} 179 | * 180 | * @param value The parameter value, must be between {0.0 - 1.0} 181 | */ 182 | virtual void setScaledValue(const ParameterValue value) = 0; 183 | 184 | #if PLUGINPARAMETERS_MULTITHREADED 185 | public: 186 | #endif 187 | 188 | /** 189 | * @return Get the parameter's minimum value 190 | */ 191 | virtual const ParameterValue getMinValue() const { 192 | return minValue; 193 | } 194 | 195 | /** 196 | * @return Get the parameter's maximum value 197 | */ 198 | virtual const ParameterValue getMaxValue() const { 199 | return maxValue; 200 | } 201 | 202 | /** 203 | * @return Get the parameter's initial default value. Useful for resetting parameters. 204 | */ 205 | virtual const ParameterValue getDefaultValue() const { 206 | return defaultValue; 207 | } 208 | 209 | /** 210 | * Get the number of decimal places for displaying floating-point parameter values. 211 | */ 212 | virtual const unsigned int getDisplayPrecision() const { 213 | return precision; 214 | } 215 | 216 | /** 217 | * Number of floating point digits to be displayed, for parameters which support 218 | * display precision. 219 | * 220 | * @param inPrecision Number of decimal digits to display 221 | */ 222 | virtual void setDisplayPrecision(unsigned int inPrecision) { 223 | this->precision = inPrecision; 224 | } 225 | 226 | /** 227 | * Get the unit string for the parameter. This is generally used by getDisplayText(), 228 | * and is by default an empty string. 229 | */ 230 | virtual const ParameterString &getUnit() const { 231 | return unit; 232 | } 233 | 234 | /** 235 | * Set a display unit (such as "ms" for milliseconds) to be shown in the 236 | * parameter's getDisplayText() string. 237 | * 238 | * @param inUnit Unit string to display 239 | */ 240 | virtual void setUnit(const ParameterString &inUnit) { 241 | this->unit = inUnit; 242 | } 243 | 244 | /** 245 | * Get the parameter description, which is a string that describes what the function of 246 | * the parameter is. This can be used to provide user-facing help for parameters. 247 | */ 248 | const ParameterString &getDescription() const { 249 | return description; 250 | } 251 | 252 | /** 253 | * Set the parameter's description. 254 | * 255 | * @param description Parameter description 256 | */ 257 | void setDescription(const ParameterString &inDescription) { 258 | this->description = inDescription; 259 | } 260 | 261 | /** 262 | * Add an observer to be alerted any time this parameter is set to a new value. 263 | * 264 | * @param observer Pointer to observing instance 265 | */ 266 | virtual void addObserver(ParameterObserver *observer) { 267 | observers.push_back(observer); 268 | } 269 | 270 | /** 271 | * Get a given observer for the parameter. 272 | * 273 | * @param index Observer to get. If the index is invalid, NULL will be returned. 274 | * @return The ParameterObserver, or NULL if the index is out of range. 275 | */ 276 | virtual ParameterObserver *getObserver(const size_t index) const { 277 | return index < observers.size() ? observers.at(index) : NULL; 278 | } 279 | 280 | /** 281 | * Get the total number of observers for the parameter. Useful for iterating over 282 | * the list of observers. 283 | */ 284 | virtual const size_t getNumObservers() const { 285 | return observers.size(); 286 | } 287 | 288 | /** 289 | * Remove an observer from the list of observers for this parameter. If you do not call 290 | * this method before your observer goes out of scope, future calls to this parameter's 291 | * setValue() method may result in segfaults. 292 | * 293 | * @param observer Instance to remove 294 | */ 295 | virtual void removeObserver(ParameterObserver *observer) { 296 | ParameterObserverMap::iterator iterator = observers.begin(); 297 | while(iterator != observers.end() && observers.size() > 0) { 298 | if(*iterator == observer) { 299 | iterator = observers.erase(iterator); 300 | } 301 | else { 302 | ++iterator; 303 | } 304 | } 305 | } 306 | 307 | protected: 308 | /** 309 | * Notify all observers that a parameter has been updated. If PluginParameters 310 | * is built with PLUGINPARAMETERS_MULTITHREADED=1, this method has no effect, since it 311 | * will be called synchronously, and observer updates are instead executed 312 | * from the EventDispatcher in that case. 313 | */ 314 | virtual void notifyObservers() const { 315 | #if !PLUGINPARAMETERS_MULTITHREADED 316 | for(ParameterObserverMap::const_iterator iterator = observers.begin(); iterator != observers.end(); ++iterator) { 317 | (*iterator)->onParameterUpdated(this); 318 | } 319 | #endif 320 | } 321 | 322 | private: 323 | // Disallow assignment operator. It doesn't really make sense to try 324 | // to assign one parameter to another, and if this is allowed then we 325 | // must drop the const several fields. 326 | Parameter &operator = (const Parameter &) { 327 | return *this; 328 | } 329 | 330 | private: 331 | const ParameterString name; 332 | ParameterString unit; 333 | const ParameterValue minValue; 334 | const ParameterValue maxValue; 335 | const ParameterValue defaultValue; 336 | ParameterValue value; 337 | unsigned int precision; 338 | ParameterString description; 339 | 340 | ParameterObserverMap observers; 341 | }; 342 | 343 | } // namespace teragon 344 | 345 | #endif // __PluginParameters_Parameter_h__ 346 | -------------------------------------------------------------------------------- /include/ParameterSet.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Teragon Audio. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * - Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * - Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 17 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | * POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #ifndef __PluginParameters_PluginParameterSet_h__ 27 | #define __PluginParameters_PluginParameterSet_h__ 28 | 29 | #include 30 | #include 31 | #include "Parameter.h" 32 | 33 | namespace teragon { 34 | 35 | class ParameterSet { 36 | #if PLUGINPARAMETERS_MULTITHREADED 37 | // For the multi-threaded implementation, forbid direct instantiation of 38 | // a ParameterSet, forcing the caller to use ConcurrentParameterSet instead. 39 | protected: 40 | #else 41 | public: 42 | #endif 43 | explicit ParameterSet() {} 44 | 45 | #if PLUGINPARAMETERS_MULTITHREADED 46 | public: 47 | #endif 48 | virtual ~ParameterSet() { 49 | // Delete all parameters added to the set 50 | for(size_t i = 0; i < size(); i++) { 51 | delete parameterList.at(i); 52 | } 53 | } 54 | 55 | /** 56 | * Add a parameter to the set. Note that this class does *not* free the 57 | * parameter's memory upon destruction, if this is important to you then 58 | * you must call the clear() method yourself before destroying this set 59 | * instance. 60 | * 61 | * @param parameter Pointer to parameter instance 62 | * @return parameter which was added if successful, NULL otherwise. Note that 63 | * adding a parameter to a set twice is considered failing behavior. 64 | */ 65 | virtual Parameter *add(Parameter *parameter) { 66 | if(parameter == NULL || get(parameter->getName()) != NULL) { 67 | return NULL; 68 | } 69 | parameterMap.insert(std::make_pair(parameter->getSafeName(), parameter)); 70 | parameterList.push_back(parameter); 71 | return parameter; 72 | } 73 | 74 | /** 75 | * @return Number of parameters in the set 76 | */ 77 | virtual const size_t size() const { 78 | return parameterList.size(); 79 | } 80 | 81 | 82 | virtual void clear() { 83 | for(ParameterList::iterator iterator = parameterList.begin(); iterator != parameterList.end(); ++iterator) { 84 | delete *iterator; 85 | } 86 | parameterList.clear(); 87 | parameterMap.clear(); 88 | } 89 | 90 | /** 91 | * Lookup a parameter by index, for example: parameterSet[2] 92 | * 93 | * @param i Parameter index, must be less than the set's size or undefined 94 | * behavior will occur 95 | * @return Reference to parameter 96 | */ 97 | virtual Parameter *operator [](const int i) const { 98 | return get(i); 99 | } 100 | 101 | /** 102 | * Lookup a parameter by index 103 | * 104 | * @param i Parameter index, must be less than the set's size or undefined 105 | * behavior will occur 106 | * @return Reference to parameter 107 | */ 108 | virtual Parameter *get(const int index) const { 109 | return parameterList.at(index); 110 | } 111 | 112 | /** 113 | * Lookup a parameter by name, for example: parameterSet["foo"] 114 | * 115 | * @param name The parameter's name 116 | * @return Reference to parameter, or NULL if not found 117 | */ 118 | virtual Parameter *operator [](const ParameterString &name) const { 119 | return get(name); 120 | } 121 | 122 | /** 123 | * Lookup a parameter by name 124 | * 125 | * @param name The parameter's name 126 | * @return Reference to parameter, or NULL if not found 127 | */ 128 | virtual Parameter *get(const ParameterString &name) const { 129 | ParameterMap::const_iterator iterator = parameterMap.find(Parameter::makeSafeName(name)); 130 | return (iterator != parameterMap.end()) ? iterator->second : NULL; 131 | } 132 | 133 | protected: 134 | typedef std::map ParameterMap; 135 | typedef std::vector ParameterList; 136 | 137 | ParameterMap parameterMap; 138 | ParameterList parameterList; 139 | }; 140 | 141 | } // namespace teragon 142 | 143 | #endif // __PluginParameters_PluginParameterSet_h__ 144 | -------------------------------------------------------------------------------- /include/PluginParameters.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Teragon Audio. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * - Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * - Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 17 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | * POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #ifndef __PluginParameters_PluginParameters_h__ 27 | #define __PluginParameters_PluginParameters_h__ 28 | 29 | #ifndef PLUGINPARAMETERS_MULTITHREADED 30 | #define PLUGINPARAMETERS_MULTITHREADED 1 31 | #endif 32 | 33 | #include "BlobParameter.h" 34 | #include "BooleanParameter.h" 35 | #include "DecibelParameter.h" 36 | #include "FloatParameter.h" 37 | #include "FrequencyParameter.h" 38 | #include "IntegerParameter.h" 39 | #include "StringParameter.h" 40 | #include "ParameterSet.h" 41 | #include "VoidParameter.h" 42 | 43 | #if PLUGINPARAMETERS_MULTITHREADED 44 | #include "EventDispatcher.h" 45 | #include "ConcurrentParameterSet.h" 46 | #endif 47 | 48 | #endif // __PluginParameters_PluginParameters_h__ 49 | -------------------------------------------------------------------------------- /include/StringParameter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Teragon Audio. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * - Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * - Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 17 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | * POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #ifndef __PluginParameters_StringParameter_h__ 27 | #define __PluginParameters_StringParameter_h__ 28 | 29 | #include "DataParameter.h" 30 | 31 | namespace teragon { 32 | 33 | class StringParameter : public DataParameter { 34 | public: 35 | StringParameter(const ParameterString &inName, 36 | ParameterString inDefaultValue = "") : 37 | DataParameter(inName), stringValue(inDefaultValue) {} 38 | 39 | virtual ~StringParameter() {} 40 | 41 | virtual const ParameterString getDisplayText() const { 42 | return stringValue; 43 | } 44 | 45 | #if PLUGINPARAMETERS_MULTITHREADED 46 | protected: 47 | #endif 48 | 49 | virtual void setValue(const void *inData, const size_t inDataSize) { 50 | stringValue.assign((const char *)inData, inDataSize); 51 | notifyObservers(); 52 | } 53 | 54 | private: 55 | ParameterString stringValue; 56 | }; 57 | 58 | } // namespace teragon 59 | 60 | #endif // __PluginParameters_StringParameter_h__ 61 | -------------------------------------------------------------------------------- /include/VoidParameter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Teragon Audio. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * - Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * - Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 17 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | * POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #ifndef __PluginParameters_VoidParameter_h__ 27 | #define __PluginParameters_VoidParameter_h__ 28 | 29 | namespace teragon { 30 | 31 | /** 32 | * Parameter which does not hold a real value. This parameter's value is 33 | * always 0, regardless of whether setValue() is called. 34 | * This parameter type is mostly useful to send events between observers. 35 | */ 36 | class VoidParameter : public Parameter { 37 | public: 38 | VoidParameter(const ParameterString &inName) : Parameter(inName) {} 39 | 40 | virtual ~VoidParameter() {} 41 | 42 | virtual const ParameterString getDisplayText() const { 43 | return "Triggered"; 44 | } 45 | 46 | virtual const ParameterValue getDisplayValue() const { 47 | return getValue(); 48 | } 49 | 50 | virtual void setDisplayValue(const ParameterValue inValue) { 51 | setValue(inValue); 52 | } 53 | 54 | virtual const ParameterValue getScaledValue() const { 55 | return getValue(); 56 | } 57 | 58 | virtual const ParameterValue getValue() const { 59 | return 0.0; 60 | } 61 | 62 | #if PLUGINPARAMETERS_MULTITHREADED 63 | protected: 64 | #endif 65 | 66 | virtual void setScaledValue(const ParameterValue inValue) { 67 | setValue(inValue); 68 | } 69 | 70 | virtual void setValue(const ParameterValue = 0.0) { 71 | notifyObservers(); 72 | } 73 | }; 74 | 75 | } // namespace teragon 76 | 77 | #endif // __PluginParameters_VoidParameter_h__ 78 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB PluginParameters_SOURCES ${CMAKE_SOURCE_DIR}/include/*.h) 2 | set(TinyThread_SOURCES ${CMAKE_SOURCE_DIR}/include/tinythread/source/tinythread.cpp) 3 | add_executable(pluginparameterstest PluginParametersTest.cpp ${PluginParameters_SOURCES} ${TinyThread_SOURCES}) 4 | add_executable(multithreadedtest MultithreadedTest.cpp ${PluginParameters_SOURCES} ${TinyThread_SOURCES}) 5 | if("${UNIX}") 6 | target_link_libraries(multithreadedtest pthread) 7 | endif("${UNIX}") 8 | -------------------------------------------------------------------------------- /test/MultithreadedTest.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Teragon Audio. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * - Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * - Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 17 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | * POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #include 27 | 28 | // Force multi-threaded build 29 | #define PLUGINPARAMETERS_MULTITHREADED 1 30 | #include "PluginParameters.h" 31 | #include "TestRunner.h" 32 | 33 | // Simulate a realtime audio system by sleeping a bit after processing events. 34 | // Here we assume 11ms sleep per block, which is approximately the amount of 35 | // time needed to process 512 samples at 44100Hz sample rate. 36 | // Several blocks may be processed before async changes are received, but here 37 | // we only want to ensure that the event was routed from async->realtime. 38 | #define SLEEP_TIME_PER_BLOCK_MS 11 39 | #define TEST_NUM_BLOCKS_TO_PROCESS 10 40 | 41 | namespace teragon { 42 | 43 | //////////////////////////////////////////////////////////////////////////////// 44 | // Observers 45 | //////////////////////////////////////////////////////////////////////////////// 46 | 47 | class TestCounterObserver : public ParameterObserver { 48 | public: 49 | TestCounterObserver(bool isRealtime = true) : ParameterObserver(), 50 | realtime(isRealtime), count(0) {} 51 | 52 | virtual ~TestCounterObserver() {} 53 | 54 | bool isRealtimePriority() const { 55 | return realtime; 56 | } 57 | 58 | virtual void onParameterUpdated(const Parameter *parameter) { 59 | count++; 60 | } 61 | 62 | const bool realtime; 63 | int count; 64 | }; 65 | 66 | class TestCacheValueObserver : public TestCounterObserver { 67 | public: 68 | TestCacheValueObserver(bool isRealtime = true) : TestCounterObserver(isRealtime), value(0) {} 69 | 70 | void onParameterUpdated(const Parameter *parameter) { 71 | TestCounterObserver::onParameterUpdated(parameter); 72 | value = parameter->getValue(); 73 | } 74 | 75 | ParameterValue value; 76 | }; 77 | 78 | //////////////////////////////////////////////////////////////////////////////// 79 | // Tests 80 | //////////////////////////////////////////////////////////////////////////////// 81 | 82 | class _Tests { 83 | public: 84 | static bool testCreateConcurrentParameterSet() { 85 | ConcurrentParameterSet *s = new ConcurrentParameterSet(); 86 | ASSERT_SIZE_EQUALS((size_t)0, s->size()); 87 | ConcurrentParameterSet::sleep(SLEEP_TIME_PER_BLOCK_MS); 88 | delete s; 89 | return true; 90 | } 91 | 92 | static bool testCreateManyConcurrentParameterSets() { 93 | // Attempt to expose bugs caused by fast-exiting threads 94 | printf("\nCreating sets"); 95 | for(int i = 0; i < 20; i++) { 96 | printf("."); 97 | fflush(stdout); 98 | ConcurrentParameterSet *s = new ConcurrentParameterSet(); 99 | ASSERT_SIZE_EQUALS((size_t)0, s->size()); 100 | // Sleep a bit to avoid a rare (but still ever-present) deadlock problem which 101 | // can occur by destroying a ConcurrentParameterSet before the asynchronous event 102 | // thread has been fully started. 103 | ConcurrentParameterSet::sleep(SLEEP_TIME_PER_BLOCK_MS); 104 | delete s; 105 | } 106 | return true; 107 | } 108 | 109 | static bool testThreadsafeSetParameterRealtime() { 110 | ConcurrentParameterSet s; 111 | Parameter *p = s.add(new BooleanParameter("test")); 112 | ASSERT_NOT_NULL(p); 113 | ASSERT_FALSE(p->getValue()); 114 | s.set(p, true); 115 | s.processRealtimeEvents(); 116 | ASSERT(p->getValue()); 117 | return true; 118 | } 119 | 120 | static bool testThreadsafeSetParameterAsync() { 121 | ConcurrentParameterSet s; 122 | Parameter *p = s.add(new BooleanParameter("test")); 123 | ASSERT_NOT_NULL(p); 124 | ASSERT_FALSE(p->getValue()); 125 | s.set(p, true); 126 | while(!p->getValue()) { 127 | s.processRealtimeEvents(); 128 | ConcurrentParameterSet::sleep(SLEEP_TIME_PER_BLOCK_MS); 129 | } 130 | ASSERT(p->getValue()); 131 | return true; 132 | } 133 | 134 | static bool testThreadsafeSetParameterWithNameAsync() { 135 | ConcurrentParameterSet s; 136 | Parameter *p = s.add(new BooleanParameter("test")); 137 | ASSERT_NOT_NULL(p); 138 | ASSERT_FALSE(p->getValue()); 139 | s.set("test", true); 140 | while(!p->getValue()) { 141 | s.processRealtimeEvents(); 142 | ConcurrentParameterSet::sleep(SLEEP_TIME_PER_BLOCK_MS); 143 | } 144 | ASSERT(p->getValue()); 145 | return true; 146 | } 147 | 148 | static bool testThreadsafeSetParameterWithIndexAsync() { 149 | ConcurrentParameterSet s; 150 | Parameter *p = s.add(new BooleanParameter("test")); 151 | ASSERT_NOT_NULL(p); 152 | ASSERT_FALSE(p->getValue()); 153 | s.set((size_t)0, true); 154 | while(!p->getValue()) { 155 | s.processRealtimeEvents(); 156 | ConcurrentParameterSet::sleep(SLEEP_TIME_PER_BLOCK_MS); 157 | } 158 | ASSERT(p->getValue()); 159 | return true; 160 | } 161 | 162 | static bool testThreadsafeSetParameterWithInvalidNameAsync() { 163 | ConcurrentParameterSet s; 164 | Parameter *p = s.add(new BooleanParameter("test")); 165 | ASSERT_NOT_NULL(p); 166 | ASSERT_FALSE(p->getValue()); 167 | s.set("invalid", true); 168 | int retries = TEST_NUM_BLOCKS_TO_PROCESS; 169 | while(!p->getValue() && retries-- > 0) { 170 | s.processRealtimeEvents(); 171 | ConcurrentParameterSet::sleep(SLEEP_TIME_PER_BLOCK_MS); 172 | } 173 | // Should silently fail (PluginParameters does not throw, and set returns void). 174 | ASSERT_FALSE(p->getValue()); 175 | return true; 176 | } 177 | 178 | static bool testThreadsafeSetDataParameterAsync() { 179 | ConcurrentParameterSet s; 180 | StringParameter *p = new StringParameter("test"); 181 | s.add(p); 182 | ASSERT_NOT_NULL(p); 183 | ASSERT_STRING("", p->getDisplayText()); 184 | const char *data = "hello"; 185 | s.setData(p, data, strlen(data)); 186 | while(p->getDisplayText() == "") { 187 | s.processRealtimeEvents(); 188 | ConcurrentParameterSet::sleep(SLEEP_TIME_PER_BLOCK_MS); 189 | } 190 | ASSERT_STRING("hello", p->getDisplayText()); 191 | return true; 192 | } 193 | 194 | static bool testThreadsafeSetDataParameterWithNameAsync() { 195 | ConcurrentParameterSet s; 196 | StringParameter *p = new StringParameter("test"); 197 | s.add(p); 198 | ASSERT_NOT_NULL(p); 199 | ASSERT_STRING("", p->getDisplayText()); 200 | const char *data = "hello"; 201 | s.setData("test", data, strlen(data)); 202 | while(p->getDisplayText() == "") { 203 | s.processRealtimeEvents(); 204 | ConcurrentParameterSet::sleep(SLEEP_TIME_PER_BLOCK_MS); 205 | } 206 | ASSERT_STRING("hello", p->getDisplayText()); 207 | return true; 208 | } 209 | 210 | static bool testThreadsafeSetDataParameterWithIndexAsync() { 211 | ConcurrentParameterSet s; 212 | StringParameter *p = new StringParameter("test"); 213 | s.add(p); 214 | ASSERT_NOT_NULL(p); 215 | ASSERT_STRING("", p->getDisplayText()); 216 | const char *data = "hello"; 217 | s.setData((size_t)0, data, strlen(data)); 218 | while(p->getDisplayText() == "") { 219 | s.processRealtimeEvents(); 220 | ConcurrentParameterSet::sleep(SLEEP_TIME_PER_BLOCK_MS); 221 | } 222 | ASSERT_STRING("hello", p->getDisplayText()); 223 | return true; 224 | } 225 | 226 | static bool testThreadsafeSetDataParameterWithInvalidNameAsync() { 227 | ConcurrentParameterSet s; 228 | StringParameter *p = new StringParameter("test"); 229 | s.add(p); 230 | ASSERT_NOT_NULL(p); 231 | ASSERT_STRING("", p->getDisplayText()); 232 | const char *data = "hello"; 233 | s.setData("invalid", data, strlen(data)); 234 | int retries = TEST_NUM_BLOCKS_TO_PROCESS; 235 | while(p->getDisplayText() == "" && retries-- > 0) { 236 | s.processRealtimeEvents(); 237 | ConcurrentParameterSet::sleep(SLEEP_TIME_PER_BLOCK_MS); 238 | } 239 | // Should silently fail (PluginParameters does not throw, and set returns void). 240 | ASSERT_STRING("", p->getDisplayText()); 241 | return true; 242 | } 243 | 244 | static bool testThreadsafeSetDataParameterAsyncForceFree() { 245 | ConcurrentParameterSet s; 246 | StringParameter *p = new StringParameter("test"); 247 | s.add(p); 248 | ASSERT_NOT_NULL(p); 249 | ASSERT_STRING("", p->getDisplayText()); 250 | char *data = new char[6]; 251 | memset(data, 0, 6); 252 | memcpy(data, "hello", 5); 253 | s.setData(p, data, strlen(data)); 254 | free(data); 255 | while(p->getDisplayText() == "") { 256 | s.processRealtimeEvents(); 257 | ConcurrentParameterSet::sleep(SLEEP_TIME_PER_BLOCK_MS); 258 | } 259 | ASSERT_STRING("hello", p->getDisplayText()); 260 | return true; 261 | } 262 | 263 | static bool testThreadsafeSetParameterBothThreadsFromAsync() { 264 | ConcurrentParameterSet s; 265 | TestCacheValueObserver realtimeObserver(true); 266 | TestCacheValueObserver asyncObserver(false); 267 | Parameter *p = s.add(new BooleanParameter("test")); 268 | ASSERT_NOT_NULL(p); 269 | p->addObserver(&realtimeObserver); 270 | p->addObserver(&asyncObserver); 271 | ASSERT_FALSE(p->getValue()); 272 | s.set(p, true); 273 | for(int i = 0; i < TEST_NUM_BLOCKS_TO_PROCESS; i++) { 274 | s.processRealtimeEvents(); 275 | ConcurrentParameterSet::sleep(SLEEP_TIME_PER_BLOCK_MS); 276 | } 277 | ASSERT(p->getValue()); 278 | ASSERT_INT_EQUALS(1, realtimeObserver.count); 279 | ASSERT_INT_EQUALS(1, (int)realtimeObserver.value); 280 | ASSERT_INT_EQUALS(1, asyncObserver.count); 281 | ASSERT_INT_EQUALS(1, (int)asyncObserver.value); 282 | return true; 283 | } 284 | 285 | static bool testThreadsafeSetParameterBothThreadsFromRealtime() { 286 | ConcurrentParameterSet s; 287 | TestCounterObserver realtimeObserver(true); 288 | TestCounterObserver asyncObserver(false); 289 | Parameter *p = s.add(new BooleanParameter("test")); 290 | ASSERT_NOT_NULL(p); 291 | p->addObserver(&realtimeObserver); 292 | p->addObserver(&asyncObserver); 293 | ASSERT_FALSE(p->getValue()); 294 | s.set(p, true); 295 | for(int i = 0; i < TEST_NUM_BLOCKS_TO_PROCESS; i++) { 296 | s.processRealtimeEvents(); 297 | ConcurrentParameterSet::sleep(SLEEP_TIME_PER_BLOCK_MS); 298 | } 299 | ASSERT(p->getValue()); 300 | ASSERT_INT_EQUALS(1, realtimeObserver.count); 301 | ASSERT_INT_EQUALS(1, asyncObserver.count); 302 | return true; 303 | } 304 | 305 | static bool testThreadsafeSetParameterWithSender() { 306 | ConcurrentParameterSet s; 307 | TestCounterObserver realtimeObserver(true); 308 | TestCounterObserver asyncObserver(false); 309 | Parameter *p = s.add(new BooleanParameter("test")); 310 | ASSERT_NOT_NULL(p); 311 | p->addObserver(&realtimeObserver); 312 | p->addObserver(&asyncObserver); 313 | ASSERT_FALSE(p->getValue()); 314 | s.set(p, true, &asyncObserver); 315 | for(int i = 0; i < TEST_NUM_BLOCKS_TO_PROCESS; i++) { 316 | s.processRealtimeEvents(); 317 | ConcurrentParameterSet::sleep(SLEEP_TIME_PER_BLOCK_MS); 318 | } 319 | ASSERT(p->getValue()); 320 | ASSERT_INT_EQUALS(1, realtimeObserver.count); 321 | // The sender should NOT be called for its own events 322 | ASSERT_INT_EQUALS(0, asyncObserver.count); 323 | return true; 324 | } 325 | }; 326 | 327 | } // namespace teragon 328 | 329 | using namespace teragon; 330 | 331 | //////////////////////////////////////////////////////////////////////////////// 332 | // Run test suite 333 | //////////////////////////////////////////////////////////////////////////////// 334 | 335 | int main(int argc, char *argv[]) { 336 | gNumFailedTests = 0; 337 | const int numIterations = 100; 338 | 339 | // Run the tests several times, which increases the probability of exposing 340 | // race conditions or other multithreaded bugs. Note that even by doing this, 341 | // we cannot guarantee with 100% certainty that race conditions do not exist. 342 | // Gotta love concurrent programming. :) 343 | for(int i = 0; i < numIterations && gNumFailedTests == 0; i++) { 344 | printf("Running tests, iteration %d/%d:\n", i, numIterations); 345 | ADD_TEST(_Tests::testCreateConcurrentParameterSet()); 346 | ADD_TEST(_Tests::testCreateManyConcurrentParameterSets()); 347 | ADD_TEST(_Tests::testThreadsafeSetParameterAsync()); 348 | ADD_TEST(_Tests::testThreadsafeSetParameterWithNameAsync()); 349 | ADD_TEST(_Tests::testThreadsafeSetParameterWithIndexAsync()); 350 | ADD_TEST(_Tests::testThreadsafeSetParameterWithInvalidNameAsync()); 351 | ADD_TEST(_Tests::testThreadsafeSetDataParameterAsync()); 352 | ADD_TEST(_Tests::testThreadsafeSetDataParameterWithNameAsync()); 353 | ADD_TEST(_Tests::testThreadsafeSetDataParameterWithIndexAsync()); 354 | ADD_TEST(_Tests::testThreadsafeSetDataParameterWithInvalidNameAsync()); 355 | ADD_TEST(_Tests::testThreadsafeSetDataParameterAsyncForceFree()); 356 | ADD_TEST(_Tests::testThreadsafeSetParameterRealtime()); 357 | ADD_TEST(_Tests::testThreadsafeSetParameterBothThreadsFromAsync()); 358 | ADD_TEST(_Tests::testThreadsafeSetParameterBothThreadsFromRealtime()); 359 | ADD_TEST(_Tests::testThreadsafeSetParameterWithSender()); 360 | } 361 | 362 | if(gNumFailedTests > 0) { 363 | printf("\nFAILED %d tests\n", gNumFailedTests); 364 | } 365 | else { 366 | printf("\nAll tests passed\n"); 367 | } 368 | 369 | return gNumFailedTests; 370 | } 371 | -------------------------------------------------------------------------------- /test/PluginParametersTest.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Teragon Audio. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * - Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * - Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 17 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | * POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #include 27 | 28 | // Disable multi-threaded build, otherwise Parameter::setValue() is not directly 29 | // accessible without using a ConcurrentParameterSet. This symbol should be 30 | // defined before including PluginParameters.h. 31 | #define PLUGINPARAMETERS_MULTITHREADED 0 32 | #include "PluginParameters.h" 33 | #include "TestRunner.h" 34 | 35 | namespace teragon { 36 | 37 | //////////////////////////////////////////////////////////////////////////////// 38 | // Observers 39 | //////////////////////////////////////////////////////////////////////////////// 40 | 41 | class TestObserver : public ParameterObserver { 42 | public: 43 | TestObserver() : ParameterObserver(), notified(false) {} 44 | 45 | void onParameterUpdated(const Parameter *parameter) { 46 | notified = true; 47 | } 48 | 49 | bool notified; 50 | }; 51 | 52 | class TestCounterObserver : public ParameterObserver { 53 | public: 54 | TestCounterObserver(bool isRealtime = true) : ParameterObserver(), 55 | realtime(isRealtime), count(0) {} 56 | 57 | virtual ~TestCounterObserver() {} 58 | 59 | virtual void onParameterUpdated(const Parameter *parameter) { 60 | count++; 61 | } 62 | 63 | const bool realtime; 64 | int count; 65 | }; 66 | 67 | class BooleanParameterObserver : public ParameterObserver { 68 | public: 69 | BooleanParameterObserver() : ParameterObserver(), value(false) {} 70 | 71 | virtual ~BooleanParameterObserver() {} 72 | 73 | void onParameterUpdated(const Parameter *parameter) { 74 | value = parameter->getValue() > 0.5; 75 | } 76 | 77 | bool value; 78 | }; 79 | 80 | class StringParameterObserver : public ParameterObserver { 81 | public: 82 | StringParameterObserver() : ParameterObserver(), value("") {} 83 | 84 | virtual ~StringParameterObserver() {} 85 | 86 | void onParameterUpdated(const Parameter *parameter) { 87 | value = parameter->getDisplayText(); 88 | } 89 | 90 | ParameterString value; 91 | }; 92 | 93 | //////////////////////////////////////////////////////////////////////////////// 94 | // Tests 95 | //////////////////////////////////////////////////////////////////////////////// 96 | 97 | class _Tests { 98 | public: 99 | static bool testCreateBlobParameter() { 100 | BlobParameter p("test"); 101 | ASSERT_FALSE(p.getValue()); 102 | ASSERT_IS_NULL(p.getData()); 103 | ASSERT_SIZE_EQUALS((size_t)0, p.getDataSize()); 104 | ASSERT_STRING("test", p.getName()); 105 | ASSERT_STRING("(Null)", p.getDisplayText()); 106 | ASSERT_STRING("test", p.getSafeName()); 107 | return true; 108 | } 109 | 110 | static bool testSetBlobParameter() { 111 | BlobParameter p("test"); 112 | ASSERT_IS_NULL(p.getData()); 113 | ASSERT_SIZE_EQUALS((size_t)0, p.getDataSize()); 114 | // Make some test data, with lots of null chars inside of it 115 | unsigned char testData[100]; 116 | for(size_t i = 0; i < sizeof(testData); ++i) { 117 | testData[i] = (unsigned char)(i % 10); 118 | } 119 | 120 | p.setValue(testData, sizeof(testData)); 121 | 122 | ASSERT_NOT_NULL(p.getData()); 123 | ASSERT_SIZE_EQUALS(100ul, p.getDataSize()); 124 | ASSERT_STRING("(Data)", p.getDisplayText()); 125 | unsigned char* result = (unsigned char*)p.getData(); 126 | for(size_t i = 0; i < p.getDataSize(); ++i) { 127 | ASSERT_INT_EQUALS(result[i], (int)(i % 10)); 128 | } 129 | return true; 130 | } 131 | 132 | static bool testCreateBoolParameter() { 133 | BooleanParameter p("test"); 134 | ASSERT_FALSE(p.getValue()); 135 | ASSERT_EQUALS(0.0, p.getScaledValue()); 136 | ASSERT_STRING("test", p.getName()); 137 | ASSERT_STRING("Disabled", p.getDisplayText()); 138 | ASSERT_STRING("test", p.getSafeName()); 139 | return true; 140 | } 141 | 142 | static bool testSetBoolParameter() { 143 | BooleanParameter p("test"); 144 | ASSERT_FALSE(p.getValue()); 145 | p.setValue(1.0); 146 | ASSERT(p.getValue()); 147 | return true; 148 | } 149 | 150 | static bool testSetBoolParameterWithListener() { 151 | BooleanParameter *p = new BooleanParameter("test"); 152 | BooleanParameterObserver l; 153 | p->addObserver(&l); 154 | p->setValue(true); 155 | ASSERT(l.value); 156 | delete p; 157 | return true; 158 | } 159 | 160 | static bool testCreateDecibelParameter() { 161 | DecibelParameter p("test", -60.0, 3.0, 0.0); 162 | ASSERT_EQUALS(0.952381, p.getScaledValue()); 163 | ASSERT_EQUALS(1.0, p.getValue()); 164 | ASSERT_STRING("0.00 dB", p.getDisplayText()); 165 | return true; 166 | } 167 | 168 | static bool testSetDecibelParameter() { 169 | DecibelParameter p("test", -60.0, 3.0, -10.0); 170 | ASSERT_STRING("-10.00 dB", p.getDisplayText()); 171 | p.setValue(1.0); 172 | ASSERT_EQUALS(0.952381, p.getScaledValue()); 173 | ASSERT_EQUALS(1.0, p.getValue()); 174 | ASSERT_STRING("0.00 dB", p.getDisplayText()); 175 | p.setScaledValue(0.5); 176 | ASSERT_EQUALS(0.5, p.getScaledValue()); 177 | ASSERT_EQUALS(0.037584, p.getValue()); 178 | ASSERT_STRING("-28.50 dB", p.getDisplayText()); 179 | return true; 180 | } 181 | 182 | static bool testCreateFloatParameter() { 183 | FloatParameter p("test", 0.0, 50.0, 25.0); 184 | ASSERT_EQUALS(25.0, p.getValue()); 185 | ASSERT_EQUALS(0.5, p.getScaledValue()); 186 | ASSERT_STRING("25.00", p.getDisplayText()); 187 | return true; 188 | } 189 | 190 | static bool testSetFloatParameter() { 191 | FloatParameter p("test", 0.0, 60.0, 0.0); 192 | p.setValue(30.0); 193 | ASSERT_EQUALS(30.0, p.getValue()); 194 | ASSERT_EQUALS(0.5, p.getScaledValue()); 195 | p.setScaledValue(0.25); 196 | ASSERT_EQUALS(15.0, p.getValue()); 197 | ASSERT_EQUALS(0.25, p.getScaledValue()); 198 | return true; 199 | } 200 | 201 | static bool testCreateFrequencyParameter() { 202 | FrequencyParameter p("test", 20.0, 20000.0, 10000.0); 203 | ASSERT_EQUALS(10000.0, p.getValue()); 204 | ASSERT_EQUALS(0.899657, p.getScaledValue()); 205 | ASSERT_STRING("10.00 kHz", p.getDisplayText()); 206 | return true; 207 | } 208 | 209 | static bool testSetFrequencyParameter() { 210 | FrequencyParameter p("test", 20.0, 20000.0, 10000.0); 211 | p.setValue(666.0); 212 | ASSERT_EQUALS(666.0, p.getValue()); 213 | ASSERT_EQUALS(0.507481, p.getScaledValue()); 214 | p.setScaledValue(0.75); 215 | ASSERT_EQUALS(3556.559, p.getValue()); 216 | ASSERT_EQUALS(0.75, p.getScaledValue()); 217 | return true; 218 | } 219 | 220 | static bool testCreateIntegerParameter() { 221 | IntegerParameter p("test", 0, 60, 15); 222 | ASSERT_EQUALS(15.0, p.getValue()); 223 | ASSERT_EQUALS(0.25, p.getScaledValue()); 224 | ASSERT_STRING("15", p.getDisplayText()); 225 | return true; 226 | } 227 | 228 | static bool testSetIntegerParameter() { 229 | IntegerParameter p("test", 0, 60, 15); 230 | p.setValue(30); 231 | ASSERT_EQUALS(30.0, p.getValue()); 232 | ASSERT_EQUALS(0.5, p.getScaledValue()); 233 | p.setScaledValue(0.75); 234 | ASSERT_EQUALS(45.0, p.getValue()); 235 | ASSERT_EQUALS(0.75, p.getScaledValue()); 236 | return true; 237 | } 238 | 239 | static bool testCreateStringParameter() { 240 | StringParameter p("test", "whatever"); 241 | ASSERT_EQUALS(0.0, p.getValue()); 242 | ASSERT_STRING("whatever", p.getDisplayText()); 243 | return true; 244 | } 245 | 246 | static bool testSetStringParameter() { 247 | StringParameter p("test", "whatever"); 248 | ASSERT_EQUALS(0.0, p.getValue()); 249 | ASSERT_STRING("whatever", p.getDisplayText()); 250 | p.setValue("something", 9); 251 | ASSERT_STRING("something", p.getDisplayText()); 252 | return true; 253 | } 254 | 255 | static bool testSetStringParameterWithListener() { 256 | StringParameter *p = new StringParameter("test", "whatever"); 257 | StringParameterObserver l; 258 | p->addObserver(&l); 259 | p->setValue("something", 9); 260 | ASSERT_STRING("something", l.value); 261 | delete p; 262 | return true; 263 | } 264 | 265 | static bool testCreateVoidParameter() { 266 | VoidParameter *p = new VoidParameter("test"); 267 | ASSERT_EQUALS(0.0, p->getValue()); 268 | TestCounterObserver l; 269 | p->addObserver(&l); 270 | p->setValue(); 271 | ASSERT_INT_EQUALS(1, l.count); 272 | delete p; 273 | return true; 274 | } 275 | 276 | static bool testCreateParameterWithBadName() { 277 | // NOTE: This test will succeed, I'd rather not throw from the constructor! 278 | // So use your head and don't make any weird parameter names. Just know 279 | // that the library isn't going to protect you from yourself. :) 280 | BooleanParameter p(""); 281 | return true; 282 | } 283 | 284 | static bool testCreateParameterWithBadRange() { 285 | // NOTE: This test will also succeed, PluginParameters doesn't check for bad ranges 286 | // Just be aware of this behavior rather than trying to abuse the library. 287 | IntegerParameter p("bad", 100, 0, 300); 288 | return true; 289 | } 290 | 291 | static bool testCreateParameterSet() { 292 | ParameterSet s; 293 | // Really just a basic sanity check 294 | ASSERT_SIZE_EQUALS((size_t)0, s.size()); 295 | return true; 296 | } 297 | 298 | static bool testAddParameterToSet() { 299 | ParameterSet s; 300 | Parameter *_p1; 301 | Parameter *_p2; 302 | 303 | _p1 = s.add(new BooleanParameter("Parameter 1")); 304 | ASSERT_NOT_NULL(_p1); 305 | _p2 = s.add(new BooleanParameter("Parameter 2")); 306 | ASSERT_NOT_NULL(_p2); 307 | ASSERT_SIZE_EQUALS(2ul, s.size()); 308 | ASSERT_STRING("Parameter 1", s.get(0)->getName()); 309 | ASSERT_STRING("Parameter 1", _p1->getName()); 310 | ASSERT_STRING("Parameter 2", s.get(1)->getName()); 311 | ASSERT_STRING("Parameter 2", _p2->getName()); 312 | return true; 313 | } 314 | 315 | static bool testAddNullParameterToSet() { 316 | ParameterSet s; 317 | ASSERT_IS_NULL(s.add(NULL)); 318 | ASSERT_SIZE_EQUALS((size_t)0, s.size()); 319 | return true; 320 | } 321 | 322 | static bool testAddDuplicateParameterToSet() { 323 | BooleanParameter *p = new BooleanParameter("test"); 324 | ParameterSet s; 325 | ASSERT_NOT_NULL(s.add(p)); 326 | ASSERT_IS_NULL(s.add(p)); 327 | ASSERT_SIZE_EQUALS(1ul, s.size()); 328 | return true; 329 | } 330 | 331 | static bool testAddDuplicateSafeNameParameterToSet() { 332 | ParameterSet s; 333 | ASSERT_NOT_NULL(s.add(new BooleanParameter("Parameter1"))); 334 | ASSERT_IS_NULL(s.add(new BooleanParameter("Parameter 1"))); 335 | ASSERT_SIZE_EQUALS(1ul, s.size()); 336 | return true; 337 | } 338 | 339 | static bool testClearParameterSet() { 340 | ParameterSet s; 341 | ASSERT_NOT_NULL(s.add(new BooleanParameter("Parameter1"))); 342 | ASSERT_NOT_NULL(s.add(new BooleanParameter("Parameter2"))); 343 | ASSERT_SIZE_EQUALS(2ul, s.size()); 344 | s.clear(); 345 | ASSERT_SIZE_EQUALS((size_t)0, s.size()); 346 | return true; 347 | } 348 | 349 | static bool testGetParameterByName() { 350 | ParameterSet s; 351 | ASSERT_NOT_NULL(s.add(new BooleanParameter("Parameter 1"))); 352 | ASSERT_NOT_NULL(s.add(new BooleanParameter("Parameter 2"))); 353 | ASSERT_SIZE_EQUALS(2ul, s.size()); 354 | Parameter *pe = s.get("Parameter 2"); 355 | ASSERT_NOT_NULL(pe); 356 | ASSERT_STRING("Parameter 2", pe->getName()); 357 | return true; 358 | } 359 | 360 | static bool testGetParameterByIndex() { 361 | ParameterSet s; 362 | ASSERT_NOT_NULL(s.add(new BooleanParameter("Parameter 1"))); 363 | ASSERT_NOT_NULL(s.add(new BooleanParameter("Parameter 2"))); 364 | ASSERT_SIZE_EQUALS(2ul, s.size()); 365 | ASSERT_STRING("Parameter 2", s.get(1)->getName()); 366 | return true; 367 | } 368 | 369 | static bool testGetParameterByNameOperator() { 370 | ParameterSet s; 371 | ASSERT_NOT_NULL(s.add(new BooleanParameter("Parameter 1"))); 372 | ASSERT_NOT_NULL(s.add(new BooleanParameter("Parameter 2"))); 373 | ASSERT_SIZE_EQUALS(2ul, s.size()); 374 | ASSERT_STRING("Parameter 2", s["Parameter 2"]->getName()); 375 | return true; 376 | } 377 | 378 | static bool testGetParameterByIndexOperator() { 379 | ParameterSet s; 380 | ASSERT_NOT_NULL(s.add(new BooleanParameter("Parameter 1"))); 381 | ASSERT_NOT_NULL(s.add(new BooleanParameter("Parameter 2"))); 382 | ASSERT_SIZE_EQUALS(2ul, s.size()); 383 | ASSERT_STRING("Parameter 2", s[1]->getName()); 384 | return true; 385 | } 386 | 387 | static bool testGetSafeName() { 388 | BooleanParameter p("hi there"); 389 | ASSERT_STRING("hithere", p.getSafeName()); 390 | return true; 391 | } 392 | 393 | static bool testAddObserver() { 394 | BooleanParameter *p = new BooleanParameter("test"); 395 | TestObserver t; 396 | p->addObserver(&t); 397 | p->setValue(1.0); 398 | ASSERT(t.notified); 399 | delete p; 400 | return true; 401 | } 402 | 403 | static bool testRemoveObserver() { 404 | BooleanParameter p("test"); 405 | TestObserver t; 406 | p.addObserver(&t); 407 | p.removeObserver(&t); 408 | p.setValue(1.0); 409 | ASSERT_FALSE(t.notified); 410 | return true; 411 | } 412 | 413 | static bool testShouldNotNotifyForSameValue() { 414 | BooleanParameter p("test", false); 415 | TestCounterObserver l; 416 | p.addObserver(&l); 417 | p.setValue(p.getValue()); 418 | ASSERT_INT_EQUALS(0, l.count); 419 | return true; 420 | } 421 | 422 | static bool testGetMinValue() { 423 | FloatParameter p("test", 0.123, 1.23, 1.00); 424 | ASSERT_EQUALS(0.123, p.getMinValue()); 425 | return true; 426 | } 427 | 428 | static bool testGetMaxValue() { 429 | FloatParameter p("test", 0.123, 1.23, 1.00); 430 | ASSERT_EQUALS(1.23, p.getMaxValue()); 431 | return true; 432 | } 433 | 434 | static bool testGetDefaultValue() { 435 | FloatParameter p("test", 0.123, 1.23, 1.00); 436 | ASSERT_EQUALS(1.0, p.getDefaultValue()); 437 | return true; 438 | } 439 | 440 | static bool testSetParameterUnit() { 441 | FloatParameter p("test", 0.0, 1.0, 0.0); 442 | ASSERT_STRING("0.00", p.getDisplayText()); 443 | p.setUnit("foo"); 444 | ASSERT_STRING("0.00 foo", p.getDisplayText()); 445 | return true; 446 | } 447 | 448 | static bool testSetPrecision() { 449 | FloatParameter p("test", 0.0, 1.0, 0.123456); 450 | ASSERT_STRING("0.12", p.getDisplayText()); 451 | p.setDisplayPrecision(3); 452 | ASSERT_STRING("0.123", p.getDisplayText()); 453 | // Rounding! 454 | p.setDisplayPrecision(5); 455 | ASSERT_STRING("0.12346", p.getDisplayText()); 456 | return true; 457 | } 458 | 459 | static bool testSetParameterDescription() { 460 | BooleanParameter p("test"); 461 | ASSERT_STRING("", p.getDescription()); 462 | p.setDescription("hello, world!"); 463 | ASSERT_STRING("hello, world!", p.getDescription()); 464 | return true; 465 | } 466 | }; 467 | 468 | } // namespace teragon 469 | 470 | using namespace teragon; 471 | 472 | //////////////////////////////////////////////////////////////////////////////// 473 | // Run test suite 474 | //////////////////////////////////////////////////////////////////////////////// 475 | 476 | int main(int argc, char *argv[]) { 477 | gNumFailedTests = 0; 478 | 479 | ADD_TEST(_Tests::testCreateBlobParameter()); 480 | ADD_TEST(_Tests::testSetBlobParameter()); 481 | 482 | ADD_TEST(_Tests::testCreateBoolParameter()); 483 | ADD_TEST(_Tests::testSetBoolParameter()); 484 | ADD_TEST(_Tests::testSetBoolParameterWithListener()); 485 | 486 | ADD_TEST(_Tests::testCreateDecibelParameter()); 487 | ADD_TEST(_Tests::testSetDecibelParameter()); 488 | 489 | ADD_TEST(_Tests::testCreateFloatParameter()); 490 | ADD_TEST(_Tests::testSetFloatParameter()); 491 | 492 | ADD_TEST(_Tests::testCreateFrequencyParameter()); 493 | ADD_TEST(_Tests::testSetFrequencyParameter()); 494 | 495 | ADD_TEST(_Tests::testCreateIntegerParameter()); 496 | ADD_TEST(_Tests::testSetIntegerParameter()); 497 | 498 | ADD_TEST(_Tests::testCreateStringParameter()); 499 | ADD_TEST(_Tests::testSetStringParameter()); 500 | ADD_TEST(_Tests::testSetStringParameterWithListener()); 501 | 502 | ADD_TEST(_Tests::testCreateVoidParameter()); 503 | 504 | ADD_TEST(_Tests::testCreateParameterWithBadName()); 505 | ADD_TEST(_Tests::testCreateParameterWithBadRange()); 506 | 507 | ADD_TEST(_Tests::testCreateParameterSet()); 508 | ADD_TEST(_Tests::testAddParameterToSet()); 509 | ADD_TEST(_Tests::testAddNullParameterToSet()); 510 | ADD_TEST(_Tests::testAddDuplicateParameterToSet()); 511 | ADD_TEST(_Tests::testAddDuplicateSafeNameParameterToSet()); 512 | 513 | ADD_TEST(_Tests::testClearParameterSet()); 514 | ADD_TEST(_Tests::testGetParameterByName()); 515 | ADD_TEST(_Tests::testGetParameterByIndex()); 516 | ADD_TEST(_Tests::testGetParameterByNameOperator()); 517 | ADD_TEST(_Tests::testGetParameterByIndexOperator()); 518 | 519 | ADD_TEST(_Tests::testGetSafeName()); 520 | ADD_TEST(_Tests::testAddObserver()); 521 | ADD_TEST(_Tests::testRemoveObserver()); 522 | ADD_TEST(_Tests::testShouldNotNotifyForSameValue()); 523 | 524 | ADD_TEST(_Tests::testGetMinValue()); 525 | ADD_TEST(_Tests::testGetMaxValue()); 526 | ADD_TEST(_Tests::testGetDefaultValue()); 527 | ADD_TEST(_Tests::testSetParameterUnit()); 528 | ADD_TEST(_Tests::testSetPrecision()); 529 | ADD_TEST(_Tests::testSetParameterDescription()); 530 | 531 | if(gNumFailedTests > 0) { 532 | printf("\nFAILED %d tests\n", gNumFailedTests); 533 | } 534 | else { 535 | printf("\nAll tests passed\n"); 536 | } 537 | 538 | return gNumFailedTests; 539 | } 540 | -------------------------------------------------------------------------------- /test/TestRunner.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Teragon Audio. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * - Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * - Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 17 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | * POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #ifndef __PluginParameters_TestRunner_h__ 27 | #define __PluginParameters_TestRunner_h__ 28 | 29 | #include 30 | #include 31 | 32 | // From http://stackoverflow.com/a/240370/14302 33 | #define STRINGIFY(x) #x 34 | #define TOSTRING(x) STRINGIFY(x) 35 | 36 | //////////////////////////////////////////////////////////////////////////////// 37 | // Test suite macros 38 | //////////////////////////////////////////////////////////////////////////////// 39 | 40 | static int gNumFailedTests = 0; 41 | 42 | #define ADD_TEST(func) { \ 43 | printf("Running %s: ", TOSTRING(func)); \ 44 | fflush(stdout); \ 45 | if(func) printf("success\n"); \ 46 | else { printf("*** FAIL ***\n"); gNumFailedTests++; } \ 47 | } 48 | 49 | #define ASSERT(result) { \ 50 | if(!result) { printf("%s was false. ", TOSTRING(result)); return false; } \ 51 | } 52 | 53 | #define ASSERT_FALSE(result) { \ 54 | if(result) { printf("%s was true. ", TOSTRING(result)); return false; } \ 55 | } 56 | 57 | #define ASSERT_IS_NULL(result) { \ 58 | if(result != NULL) { printf("%s was not null. ", TOSTRING(result)); return false; } \ 59 | } 60 | 61 | #define ASSERT_NOT_NULL(result) { \ 62 | if(result == NULL) { printf("%s was null. ", TOSTRING(result)); return false; } \ 63 | } 64 | 65 | #define ASSERT_EQUALS(expected, result) { \ 66 | if(fabs(fabs(result) - fabs(expected)) > 0.001) { \ 67 | printf("Expected %f, got %f. ", expected, result); \ 68 | return false; \ 69 | } \ 70 | } 71 | 72 | #define ASSERT_INT_EQUALS(expected, result) { \ 73 | if(result != expected) { \ 74 | printf("Expected %d, got %d. ", expected, result); \ 75 | return false; \ 76 | } \ 77 | } 78 | 79 | #define ASSERT_SIZE_EQUALS(expected, result) { \ 80 | if(result != expected) { \ 81 | printf("Expected %lu, got %lu. ", expected, result); \ 82 | return false; \ 83 | } \ 84 | } 85 | 86 | #define ASSERT_STRING(expected, result) { \ 87 | std::string e(expected); \ 88 | if(e != result) { \ 89 | printf("Expected '%s', got '%s'. ", expected, result.c_str()); \ 90 | return false; \ 91 | } \ 92 | } 93 | 94 | #endif // __PluginParameters_TestRunner_h__ 95 | --------------------------------------------------------------------------------